diff --git a/.cargo/config.toml b/.cargo/config.toml index f113e9114acef51eaae6dd96666cc49781c8d41a..1b8ffe1a1c827fcf5fcf65b6c24c0439e2f3dc8c 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -8,3 +8,4 @@ rustdocflags = [ # Needed for musl builds so user doesn't have to install musl-tools. CC_x86_64_unknown_linux_musl = { value = ".cargo/musl-gcc", force = true, relative = true } CXX_x86_64_unknown_linux_musl = { value = ".cargo/musl-g++", force = true, relative = true } +CARGO_WORKSPACE_ROOT_DIR = { value = "", relative = true } diff --git a/.config/lychee.toml b/.config/lychee.toml index b7bb6f0ce4953bcdba91685d8161d9046765cdcb..b1f08de33340999d52114ed8c6b5fcd6604662fa 100644 --- a/.config/lychee.toml +++ b/.config/lychee.toml @@ -18,7 +18,10 @@ accept = [ "429", ] -exclude_path = ["./target"] +exclude_path = [ + "./prdoc", + "./target", +] exclude = [ # Place holders (no need to fix these): diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 4fc5b97caae0735058337c8b23e4ca7471761d24..d13add97d419dca273fb1f0604f4da7ecee6cd5b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -64,7 +64,8 @@ /substrate/primitives/merkle-mountain-range/ @acatangiu # Contracts -/substrate/frame/contracts/ @athei @paritytech/docs-audit +/substrate/frame/contracts/ @paritytech/smart-contracts @paritytech/docs-audit +/substrate/frame/revive/ @paritytech/smart-contracts @paritytech/docs-audit # NPoS and election /substrate/frame/election-provider-multi-phase/ @paritytech/staking-core @paritytech/docs-audit diff --git a/.github/actions/build-push-image/action.yml b/.github/actions/build-push-image/action.yml new file mode 100644 index 0000000000000000000000000000000000000000..fead9cfe336933dbde9a936e49b44fe1cf1396ed --- /dev/null +++ b/.github/actions/build-push-image/action.yml @@ -0,0 +1,47 @@ +name: 'build and push image' +inputs: + dockerfile: + description: "dockerfile to build" + required: true + image-name: + description: "" + required: true +outputs: + branch: + description: 'Branch name for the PR' + value: ${{ steps.branch.outputs.branch }} + + +runs: + using: "composite" + steps: + + # gcloud + # https://github.com/paritytech/ci_cd/wiki/GitHub:-Push-Docker-image-to-GCP-Registry + - name: "Set up Cloud SDK" + uses: "google-github-actions/setup-gcloud@v2" + - name: "gcloud info" + shell: bash + run: "gcloud info" + - name: "Auth in gcloud registry" + shell: bash + run: "gcloud auth configure-docker europe-docker.pkg.dev --quiet" + + - name: build + shell: bash + env: + ZOMBIENET_IMAGE: "docker.io/paritytech/zombienet:v1.3.105" + run: | + export BRANCH_NAME=${{ github.head_ref || github.ref_name }} + export DOCKER_IMAGES_VERSION=${BRANCH_NAME/\//-} + if [[ ${{ github.event_name }} == "merge_group" ]]; then export DOCKER_IMAGES_VERSION="${GITHUB_SHA::8}"; fi + docker build \ + --build-arg VCS_REF="${GITHUB_SHA}" \ + --build-arg BUILD_DATE="$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \ + --build-arg IMAGE_NAME="${{ inputs.image-name }}" \ + --build-arg ZOMBIENET_IMAGE="${ZOMBIENET_IMAGE}" \ + -t "${{ inputs.image-name }}:$DOCKER_IMAGES_VERSION" \ + -f ${{ inputs.dockerfile }} \ + . + docker push "${{ inputs.image-name }}:$DOCKER_IMAGES_VERSION" + diff --git a/.github/actions/set-up-gh/action.yml b/.github/actions/set-up-gh/action.yml index fc16ce0b26334283b83266e381c1811738be87c9..4dc3af4a19f231270abf1d260f8f29ab1c44df82 100644 --- a/.github/actions/set-up-gh/action.yml +++ b/.github/actions/set-up-gh/action.yml @@ -1,5 +1,5 @@ -name: 'install gh' -description: 'Install the gh cli in a debian based distro and switches to the PR branch.' +name: "install gh" +description: "Install the gh cli in a debian based distro and switches to the PR branch." inputs: pr-number: description: "Number of the PR" @@ -9,28 +9,20 @@ inputs: required: true outputs: branch: - description: 'Branch name for the PR' + description: "Branch name for the PR" value: ${{ steps.branch.outputs.branch }} runs: using: "composite" steps: - - name: Instal gh cli - shell: bash - # Here it would get the script from previous step - run: | - (type -p wget >/dev/null || (apt update && apt-get install wget -y)) - mkdir -p -m 755 /etc/apt/keyrings - wget -qO- https://cli.github.com/packages/githubcli-archive-keyring.gpg | tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/null - chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg - echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null - apt update - apt install gh -y - git config --global --add safe.directory '*' - - run: gh pr checkout ${{ inputs.pr-number }} - shell: bash - env: - GITHUB_TOKEN: ${{ inputs.GH_TOKEN }} - - name: Export branch name - shell: bash - run: echo "branch=$(git rev-parse --abbrev-ref HEAD)" >> "$GITHUB_OUTPUT" - id: branch + - name: Set up git + shell: bash + # Here it would get the script from previous step + run: git config --global --add safe.directory '*' + - run: gh pr checkout ${{ inputs.pr-number }} + shell: bash + env: + GITHUB_TOKEN: ${{ inputs.GH_TOKEN }} + - name: Export branch name + shell: bash + run: echo "branch=$(git rev-parse --abbrev-ref HEAD)" >> "$GITHUB_OUTPUT" + id: branch diff --git a/.github/actions/set-up-mac/README.md b/.github/actions/set-up-mac/README.md deleted file mode 100644 index 0bbc7112bd1d40e24c91d426df014a877166e3b0..0000000000000000000000000000000000000000 --- a/.github/actions/set-up-mac/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# How to use - -```yml - set-image: - runs-on: macos-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - id: set_image - run: cat .github/env >> $GITHUB_OUTPUT - - name: Install dependencies - uses: ./.github/actions/set-up-mac - with: - IMAGE: ${{ steps.set-image.outputs.IMAGE }} -``` diff --git a/.github/actions/set-up-mac/action.yml b/.github/actions/set-up-mac/action.yml deleted file mode 100644 index a3b026679402c2dc3b086cf572041b1f4bcc61e6..0000000000000000000000000000000000000000 --- a/.github/actions/set-up-mac/action.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: "Set up rust on mac" -description: "Install the required tools for Mac runners" -inputs: - IMAGE: - description: "Rust docker image" - required: true -runs: - using: "composite" - steps: - - name: Install with Hombrew - shell: bash - run: brew install protobuf rustup openssl pkg-config zlib xz zstd llvm jq curl gcc make cmake - - name: Set version - shell: bash - run: | - VERSION=$(echo $IMAGE | sed -E 's/.*:bullseye-([^-]+)-.*/\1/') - echo $VERSION - echo "VERSION=$VERSION" >> $GITHUB_ENV - NIGHTLY=$(echo $IMAGE | sed -E 's/.*([0-9]{4}-[0-9]{2}-[0-9]{2}).*/\1/') - echo $NIGHTLY - echo "NIGHTLY=$NIGHTLY" >> $GITHUB_ENV - env: - IMAGE: ${{ inputs.IMAGE }} - - - name: Install rustup - shell: bash - run: | - rustup-init -y - rustup install $VERSION - rustup default $VERSION - rustup toolchain install "nightly-${NIGHTLY}" - - - name: MacOS Deps - shell: bash - run: | - rustup target add wasm32-unknown-unknown --toolchain $VERSION - rustup component add rust-src rustfmt clippy --toolchain $VERSION - - - name: Check Rust - shell: bash - run: | - rustup show - rustup +nightly show diff --git a/.github/codecov.yml b/.github/codecov.yml index ceceb9e63654ce83c942571dea79cae29f15d851..b237c9fe6b044492349aa2f94c860f5e940d0589 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -6,4 +6,10 @@ coverage: project: default: target: 1.0 - threshold: 2.0 \ No newline at end of file + threshold: 2.0 + +comment: + behavior: new + +fixes: + - "/__w/polkadot-sdk/polkadot-sdk/::" \ No newline at end of file diff --git a/.github/env b/.github/env index 2e4d5b48100dfb3e976270efd155843e907687a8..bb61e1f4cd99f7a3f585bb04d8961ee6fb0ffb2f 100644 --- a/.github/env +++ b/.github/env @@ -1 +1 @@ -IMAGE="docker.io/paritytech/ci-unified:bullseye-1.77.0-2024-04-10-v202407161507" +IMAGE="docker.io/paritytech/ci-unified:bullseye-1.81.0-2024-09-11-v202409111034" diff --git a/.github/review-bot.yml b/.github/review-bot.yml index adbc480c6ba1a69e8cdd112af4be378849d26315..c2080142f506a6b594bf96521f383404d7337c9d 100644 --- a/.github/review-bot.yml +++ b/.github/review-bot.yml @@ -29,7 +29,7 @@ rules: # excluding files from 'Runtime files' and 'CI files' rules exclude: - ^cumulus/parachains/common/src/[^/]+\.rs$ - - ^substrate/frame/(?!.*(nfts/.*|uniques/.*|babe/.*|grandpa/.*|beefy|merkle-mountain-range/.*|contracts/.*|election|nomination-pools/.*|staking/.*|aura/.*)) + - ^substrate/frame/(?!.*(nfts/.*|uniques/.*|babe/.*|grandpa/.*|beefy|merkle-mountain-range/.*|contracts/.*|revive/.*|election|nomination-pools/.*|staking/.*|aura/.*)) - ^\.gitlab-ci\.yml - ^docker/.* - ^\.github/.* @@ -56,7 +56,7 @@ rules: - name: FRAME coders substrate condition: include: - - ^substrate/frame/(?!.*(nfts/.*|uniques/.*|babe/.*|grandpa/.*|beefy|merkle-mountain-range/.*|contracts/.*|election|nomination-pools/.*|staking/.*|aura/.*)) + - ^substrate/frame/(?!.*(nfts/.*|uniques/.*|babe/.*|grandpa/.*|beefy|merkle-mountain-range/.*|contracts/.*|revive/.*|election|nomination-pools/.*|staking/.*|aura/.*)) type: "and" reviewers: - minApprovals: 2 @@ -66,6 +66,17 @@ rules: teams: - frame-coders + # Smart Contracts + - name: Smart Contracts + type: basic + condition: + include: + - ^substrate/frame/contracts/.* + - ^substrate/frame/revive/.* + minApprovals: 2 + teams: + - smart-contracts + # Protection of THIS file - name: Review Bot countAuthor: true diff --git a/.gitlab/check-each-crate.py b/.github/scripts/check-each-crate.py similarity index 81% rename from .gitlab/check-each-crate.py rename to .github/scripts/check-each-crate.py index 9b654f8071ac7237fe9c7c943540e8e020cebd6e..7a53e812ddfc73ad59943b5fc62a4b8757f29524 100755 --- a/.gitlab/check-each-crate.py +++ b/.github/scripts/check-each-crate.py @@ -9,6 +9,7 @@ # # - `target_group`: Integer starting from 1, the group this script should execute. # - `groups_total`: Integer starting from 1, total number of groups. +# - `disable_forklift`: Boolean, whether to disable forklift or not. import subprocess, sys @@ -31,6 +32,9 @@ crates.sort() target_group = int(sys.argv[1]) - 1 groups_total = int(sys.argv[2]) +disable_forklift = bool(sys.argv[3] if len(sys.argv) > 3 else False) + +print(f"Target group: {target_group}, Total groups: {groups_total}, Disable forklift: {disable_forklift}", file=sys.stderr) if len(crates) == 0: print("No crates detected!", file=sys.stderr) @@ -55,7 +59,11 @@ for i in range(0, crates_per_group + overflow_crates): print(f"Checking {crates[crate][0]}", file=sys.stderr) - res = subprocess.run(["forklift", "cargo", "check", "--locked"], cwd = crates[crate][1]) + cmd = ["cargo", "check", "--locked"] + + cmd.insert(0, 'forklift') if not disable_forklift else None + + res = subprocess.run(cmd, cwd = crates[crate][1]) if res.returncode != 0: sys.exit(1) diff --git a/.github/scripts/cmd/cmd.py b/.github/scripts/cmd/cmd.py index 63bd6a2795aacfb019d582a06c37957ce565fd8b..01f36ea375c5bf8bc6a7039e184de39b5013faab 100755 --- a/.github/scripts/cmd/cmd.py +++ b/.github/scripts/cmd/cmd.py @@ -5,6 +5,7 @@ import sys import json import argparse import _help +import importlib.util _HelpAction = _help._HelpAction @@ -67,130 +68,172 @@ parser_ui = subparsers.add_parser('update-ui', help='Updates UI tests') for arg, config in common_args.items(): parser_ui.add_argument(arg, **config) +""" +PRDOC +""" +# Import generate-prdoc.py dynamically +spec = importlib.util.spec_from_file_location("generate_prdoc", ".github/scripts/generate-prdoc.py") +generate_prdoc = importlib.util.module_from_spec(spec) +spec.loader.exec_module(generate_prdoc) + +parser_prdoc = subparsers.add_parser('prdoc', help='Generates PR documentation') +generate_prdoc.setup_parser(parser_prdoc, pr_required=False) + +def main(): + global args, unknown, runtimesMatrix + args, unknown = parser.parse_known_args() + + print(f'args: {args}') + + if args.command == 'bench': + runtime_pallets_map = {} + failed_benchmarks = {} + successful_benchmarks = {} + + profile = "release" + + print(f'Provided runtimes: {args.runtime}') + # convert to mapped dict + runtimesMatrix = list(filter(lambda x: x['name'] in args.runtime, runtimesMatrix)) + runtimesMatrix = {x['name']: x for x in runtimesMatrix} + print(f'Filtered out runtimes: {runtimesMatrix}') + + # loop over remaining runtimes to collect available pallets + for runtime in runtimesMatrix.values(): + os.system(f"forklift cargo build -p {runtime['package']} --profile {profile} --features={runtime['bench_features']}") + print(f'-- listing pallets for benchmark for {runtime["name"]}') + wasm_file = f"target/{profile}/wbuild/{runtime['package']}/{runtime['package'].replace('-', '_')}.wasm" + output = os.popen( + f"frame-omni-bencher v1 benchmark pallet --no-csv-header --no-storage-info --no-min-squares --no-median-slopes --all --list --runtime={wasm_file} {runtime['bench_flags']}").read() + raw_pallets = output.strip().split('\n') + + all_pallets = set() + for pallet in raw_pallets: + if pallet: + all_pallets.add(pallet.split(',')[0].strip()) + + pallets = list(all_pallets) + print(f'Pallets in {runtime["name"]}: {pallets}') + runtime_pallets_map[runtime['name']] = pallets + + print(f'\n') + + # filter out only the specified pallets from collected runtimes/pallets + if args.pallet: + print(f'Pallets: {args.pallet}') + new_pallets_map = {} + # keep only specified pallets if they exist in the runtime + for runtime in runtime_pallets_map: + if set(args.pallet).issubset(set(runtime_pallets_map[runtime])): + new_pallets_map[runtime] = args.pallet + + runtime_pallets_map = new_pallets_map + + print(f'Filtered out runtimes & pallets: {runtime_pallets_map}\n') + + if not runtime_pallets_map: + if args.pallet and not args.runtime: + print(f"No pallets {args.pallet} found in any runtime") + elif args.runtime and not args.pallet: + print(f"{args.runtime} runtime does not have any pallets") + elif args.runtime and args.pallet: + print(f"No pallets {args.pallet} found in {args.runtime}") + else: + print('No runtimes found') + sys.exit(1) -args, unknown = parser.parse_known_args() - -print(f'args: {args}') - -if args.command == 'bench': - runtime_pallets_map = {} - failed_benchmarks = {} - successful_benchmarks = {} - - profile = "release" - - print(f'Provided runtimes: {args.runtime}') - # convert to mapped dict - runtimesMatrix = list(filter(lambda x: x['name'] in args.runtime, runtimesMatrix)) - runtimesMatrix = {x['name']: x for x in runtimesMatrix} - print(f'Filtered out runtimes: {runtimesMatrix}') - - # loop over remaining runtimes to collect available pallets - for runtime in runtimesMatrix.values(): - os.system(f"forklift cargo build -p {runtime['package']} --profile {profile} --features runtime-benchmarks") - print(f'-- listing pallets for benchmark for {runtime["name"]}') - wasm_file = f"target/{profile}/wbuild/{runtime['package']}/{runtime['package'].replace('-', '_')}.wasm" - output = os.popen( - f"frame-omni-bencher v1 benchmark pallet --no-csv-header --no-storage-info --no-min-squares --no-median-slopes --all --list --runtime={wasm_file}").read() - raw_pallets = output.strip().split('\n') - - all_pallets = set() - for pallet in raw_pallets: - if pallet: - all_pallets.add(pallet.split(',')[0].strip()) - - pallets = list(all_pallets) - print(f'Pallets in {runtime}: {pallets}') - runtime_pallets_map[runtime['name']] = pallets - - # filter out only the specified pallets from collected runtimes/pallets - if args.pallet: - print(f'Pallet: {args.pallet}') - new_pallets_map = {} - # keep only specified pallets if they exist in the runtime for runtime in runtime_pallets_map: - if set(args.pallet).issubset(set(runtime_pallets_map[runtime])): - new_pallets_map[runtime] = args.pallet - - runtime_pallets_map = new_pallets_map - - print(f'Filtered out runtimes & pallets: {runtime_pallets_map}') - - if not runtime_pallets_map: - if args.pallet and not args.runtime: - print(f"No pallets {args.pallet} found in any runtime") - elif args.runtime and not args.pallet: - print(f"{args.runtime} runtime does not have any pallets") - elif args.runtime and args.pallet: - print(f"No pallets {args.pallet} found in {args.runtime}") - else: - print('No runtimes found') - sys.exit(1) - - header_path = os.path.abspath('./substrate/HEADER-APACHE2') - - for runtime in runtime_pallets_map: - for pallet in runtime_pallets_map[runtime]: - config = runtimesMatrix[runtime] - print(f'-- config: {config}') - if runtime == 'dev': - # to support sub-modules (https://github.com/paritytech/command-bot/issues/275) - search_manifest_path = f"cargo metadata --locked --format-version 1 --no-deps | jq -r '.packages[] | select(.name == \"{pallet.replace('_', '-')}\") | .manifest_path'" - print(f'-- running: {search_manifest_path}') - manifest_path = os.popen(search_manifest_path).read() - if not manifest_path: - print(f'-- pallet {pallet} not found in dev runtime') - exit(1) - package_dir = os.path.dirname(manifest_path) - print(f'-- package_dir: {package_dir}') - print(f'-- manifest_path: {manifest_path}') - output_path = os.path.join(package_dir, "src", "weights.rs") - else: - default_path = f"./{config['path']}/src/weights" - xcm_path = f"./{config['path']}/src/weights/xcm" - output_path = default_path if not pallet.startswith("pallet_xcm_benchmarks") else xcm_path - print(f'-- benchmarking {pallet} in {runtime} into {output_path}') - cmd = f"frame-omni-bencher v1 benchmark pallet --extrinsic=* --runtime=target/{profile}/wbuild/{config['package']}/{config['package'].replace('-', '_')}.wasm --pallet={pallet} --header={header_path} --output={output_path} --wasm-execution=compiled --steps=50 --repeat=20 --heap-pages=4096 --no-storage-info --no-min-squares --no-median-slopes" - print(f'-- Running: {cmd}') - status = os.system(cmd) - if status != 0 and not args.continue_on_fail: - print(f'Failed to benchmark {pallet} in {runtime}') - sys.exit(1) - - # Otherwise collect failed benchmarks and print them at the end - # push failed pallets to failed_benchmarks - if status != 0: - failed_benchmarks[f'{runtime}'] = failed_benchmarks.get(f'{runtime}', []) + [pallet] - else: - successful_benchmarks[f'{runtime}'] = successful_benchmarks.get(f'{runtime}', []) + [pallet] - - if failed_benchmarks: - print('โŒ Failed benchmarks of runtimes/pallets:') - for runtime, pallets in failed_benchmarks.items(): - print(f'-- {runtime}: {pallets}') - - if successful_benchmarks: - print('โœ… Successful benchmarks of runtimes/pallets:') - for runtime, pallets in successful_benchmarks.items(): - print(f'-- {runtime}: {pallets}') - -elif args.command == 'fmt': - command = f"cargo +nightly fmt" - print(f'Formatting with `{command}`') - nightly_status = os.system(f'{command}') - taplo_status = os.system('taplo format --config .config/taplo.toml') - - if (nightly_status != 0 or taplo_status != 0) and not args.continue_on_fail: - print('โŒ Failed to format code') - sys.exit(1) - -elif args.command == 'update-ui': - command = 'sh ./scripts/update-ui-tests.sh' - print(f'Updating ui with `{command}`') - status = os.system(f'{command}') - - if status != 0 and not args.continue_on_fail: - print('โŒ Failed to format code') - sys.exit(1) - -print('๐Ÿš€ Done') + for pallet in runtime_pallets_map[runtime]: + config = runtimesMatrix[runtime] + header_path = os.path.abspath(config['header']) + template = None + + print(f'-- config: {config}') + if runtime == 'dev': + # to support sub-modules (https://github.com/paritytech/command-bot/issues/275) + search_manifest_path = f"cargo metadata --locked --format-version 1 --no-deps | jq -r '.packages[] | select(.name == \"{pallet.replace('_', '-')}\") | .manifest_path'" + print(f'-- running: {search_manifest_path}') + manifest_path = os.popen(search_manifest_path).read() + if not manifest_path: + print(f'-- pallet {pallet} not found in dev runtime') + exit(1) + package_dir = os.path.dirname(manifest_path) + print(f'-- package_dir: {package_dir}') + print(f'-- manifest_path: {manifest_path}') + output_path = os.path.join(package_dir, "src", "weights.rs") + template = config['template'] + else: + default_path = f"./{config['path']}/src/weights" + xcm_path = f"./{config['path']}/src/weights/xcm" + output_path = default_path + if pallet.startswith("pallet_xcm_benchmarks"): + template = config['template'] + output_path = xcm_path + + print(f'-- benchmarking {pallet} in {runtime} into {output_path}') + cmd = f"frame-omni-bencher v1 benchmark pallet " \ + f"--extrinsic=* " \ + f"--runtime=target/{profile}/wbuild/{config['package']}/{config['package'].replace('-', '_')}.wasm " \ + f"--pallet={pallet} " \ + f"--header={header_path} " \ + f"--output={output_path} " \ + f"--wasm-execution=compiled " \ + f"--steps=50 " \ + f"--repeat=20 " \ + f"--heap-pages=4096 " \ + f"{f'--template={template} ' if template else ''}" \ + f"--no-storage-info --no-min-squares --no-median-slopes " \ + f"{config['bench_flags']}" + print(f'-- Running: {cmd} \n') + status = os.system(cmd) + if status != 0 and not args.continue_on_fail: + print(f'Failed to benchmark {pallet} in {runtime}') + sys.exit(1) + + # Otherwise collect failed benchmarks and print them at the end + # push failed pallets to failed_benchmarks + if status != 0: + failed_benchmarks[f'{runtime}'] = failed_benchmarks.get(f'{runtime}', []) + [pallet] + else: + successful_benchmarks[f'{runtime}'] = successful_benchmarks.get(f'{runtime}', []) + [pallet] + + if failed_benchmarks: + print('โŒ Failed benchmarks of runtimes/pallets:') + for runtime, pallets in failed_benchmarks.items(): + print(f'-- {runtime}: {pallets}') + + if successful_benchmarks: + print('โœ… Successful benchmarks of runtimes/pallets:') + for runtime, pallets in successful_benchmarks.items(): + print(f'-- {runtime}: {pallets}') + + elif args.command == 'fmt': + command = f"cargo +nightly fmt" + print(f'Formatting with `{command}`') + nightly_status = os.system(f'{command}') + taplo_status = os.system('taplo format --config .config/taplo.toml') + + if (nightly_status != 0 or taplo_status != 0) and not args.continue_on_fail: + print('โŒ Failed to format code') + sys.exit(1) + + elif args.command == 'update-ui': + command = 'sh ./scripts/update-ui-tests.sh' + print(f'Updating ui with `{command}`') + status = os.system(f'{command}') + + if status != 0 and not args.continue_on_fail: + print('โŒ Failed to format code') + sys.exit(1) + + elif args.command == 'prdoc': + # Call the main function from ./github/scripts/generate-prdoc.py module + exit_code = generate_prdoc.main(args) + if exit_code != 0 and not args.continue_on_fail: + print('โŒ Failed to generate prdoc') + sys.exit(exit_code) + + print('๐Ÿš€ Done') + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/.github/scripts/cmd/test_cmd.py b/.github/scripts/cmd/test_cmd.py new file mode 100644 index 0000000000000000000000000000000000000000..0316c7ff1bb48a793c6227a6c1a36aa24b8c762d --- /dev/null +++ b/.github/scripts/cmd/test_cmd.py @@ -0,0 +1,432 @@ +import unittest +from unittest.mock import patch, mock_open, MagicMock, call +import json +import sys +import os +import argparse + +# Mock data for runtimes-matrix.json +mock_runtimes_matrix = [ + { + "name": "dev", + "package": "kitchensink-runtime", + "path": "substrate/frame", + "header": "substrate/HEADER-APACHE2", + "template": "substrate/.maintain/frame-weight-template.hbs", + "bench_features": "runtime-benchmarks,riscv", + "bench_flags": "--flag1 --flag2" + }, + { + "name": "westend", + "package": "westend-runtime", + "path": "polkadot/runtime/westend", + "header": "polkadot/file_header.txt", + "template": "polkadot/xcm/pallet-xcm-benchmarks/template.hbs", + "bench_features": "runtime-benchmarks", + "bench_flags": "--flag3 --flag4" + }, + { + "name": "rococo", + "package": "rococo-runtime", + "path": "polkadot/runtime/rococo", + "header": "polkadot/file_header.txt", + "template": "polkadot/xcm/pallet-xcm-benchmarks/template.hbs", + "bench_features": "runtime-benchmarks", + "bench_flags": "" + }, + { + "name": "asset-hub-westend", + "package": "asset-hub-westend-runtime", + "path": "cumulus/parachains/runtimes/assets/asset-hub-westend", + "header": "cumulus/file_header.txt", + "template": "cumulus/templates/xcm-bench-template.hbs", + "bench_features": "runtime-benchmarks", + "bench_flags": "--flag7 --flag8" + } +] + +def get_mock_bench_output(runtime, pallets, output_path, header, bench_flags, template = None): + return f"frame-omni-bencher v1 benchmark pallet --extrinsic=* " \ + f"--runtime=target/release/wbuild/{runtime}-runtime/{runtime.replace('-', '_')}_runtime.wasm " \ + f"--pallet={pallets} --header={header} " \ + f"--output={output_path} " \ + f"--wasm-execution=compiled " \ + f"--steps=50 --repeat=20 --heap-pages=4096 " \ + f"{f'--template={template} ' if template else ''}" \ + f"--no-storage-info --no-min-squares --no-median-slopes " \ + f"{bench_flags}" + +class TestCmd(unittest.TestCase): + + def setUp(self): + self.patcher1 = patch('builtins.open', new_callable=mock_open, read_data=json.dumps(mock_runtimes_matrix)) + self.patcher2 = patch('json.load', return_value=mock_runtimes_matrix) + self.patcher3 = patch('argparse.ArgumentParser.parse_known_args') + self.patcher4 = patch('os.system', return_value=0) + self.patcher5 = patch('os.popen') + self.patcher6 = patch('importlib.util.spec_from_file_location', return_value=MagicMock()) + self.patcher7 = patch('importlib.util.module_from_spec', return_value=MagicMock()) + self.patcher8 = patch('cmd.generate_prdoc.main', return_value=0) + + self.mock_open = self.patcher1.start() + self.mock_json_load = self.patcher2.start() + self.mock_parse_args = self.patcher3.start() + self.mock_system = self.patcher4.start() + self.mock_popen = self.patcher5.start() + self.mock_spec_from_file_location = self.patcher6.start() + self.mock_module_from_spec = self.patcher7.start() + self.mock_generate_prdoc_main = self.patcher8.start() + + # Ensure that cmd.py uses the mock_runtimes_matrix + import cmd + cmd.runtimesMatrix = mock_runtimes_matrix + + def tearDown(self): + self.patcher1.stop() + self.patcher2.stop() + self.patcher3.stop() + self.patcher4.stop() + self.patcher5.stop() + self.patcher6.stop() + self.patcher7.stop() + self.patcher8.stop() + + def test_bench_command_normal_execution_all_runtimes(self): + self.mock_parse_args.return_value = (argparse.Namespace( + command='bench', + runtime=list(map(lambda x: x['name'], mock_runtimes_matrix)), + pallet=['pallet_balances'], + continue_on_fail=False, + quiet=False, + clean=False, + image=None + ), []) + + self.mock_popen.return_value.read.side_effect = [ + "pallet_balances\npallet_staking\npallet_something\n", # Output for dev runtime + "pallet_balances\npallet_staking\npallet_something\n", # Output for westend runtime + "pallet_staking\npallet_something\n", # Output for rococo runtime - no pallet here + "pallet_balances\npallet_staking\npallet_something\n", # Output for asset-hub-westend runtime + "./substrate/frame/balances/Cargo.toml\n", # Mock manifest path for dev -> pallet_balances + ] + + with patch('sys.exit') as mock_exit: + import cmd + cmd.main() + mock_exit.assert_not_called() + + expected_calls = [ + # Build calls + call("forklift cargo build -p kitchensink-runtime --profile release --features=runtime-benchmarks,riscv"), + call("forklift cargo build -p westend-runtime --profile release --features=runtime-benchmarks"), + call("forklift cargo build -p rococo-runtime --profile release --features=runtime-benchmarks"), + call("forklift cargo build -p asset-hub-westend-runtime --profile release --features=runtime-benchmarks"), + + call(get_mock_bench_output( + runtime='kitchensink', + pallets='pallet_balances', + output_path='./substrate/frame/balances/src/weights.rs', + header=os.path.abspath('substrate/HEADER-APACHE2'), + bench_flags='--flag1 --flag2', + template="substrate/.maintain/frame-weight-template.hbs" + )), + call(get_mock_bench_output( + runtime='westend', + pallets='pallet_balances', + output_path='./polkadot/runtime/westend/src/weights', + header=os.path.abspath('polkadot/file_header.txt'), + bench_flags='--flag3 --flag4' + )), + # skips rococo benchmark + call(get_mock_bench_output( + runtime='asset-hub-westend', + pallets='pallet_balances', + output_path='./cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights', + header=os.path.abspath('cumulus/file_header.txt'), + bench_flags='--flag7 --flag8' + )), + ] + self.mock_system.assert_has_calls(expected_calls, any_order=True) + + def test_bench_command_normal_execution(self): + self.mock_parse_args.return_value = (argparse.Namespace( + command='bench', + runtime=['westend'], + pallet=['pallet_balances', 'pallet_staking'], + continue_on_fail=False, + quiet=False, + clean=False, + image=None + ), []) + header_path = os.path.abspath('polkadot/file_header.txt') + self.mock_popen.return_value.read.side_effect = [ + "pallet_balances\npallet_staking\npallet_something\n", # Output for westend runtime + ] + + with patch('sys.exit') as mock_exit: + import cmd + cmd.main() + mock_exit.assert_not_called() + + expected_calls = [ + # Build calls + call("forklift cargo build -p westend-runtime --profile release --features=runtime-benchmarks"), + + # Westend runtime calls + call(get_mock_bench_output( + runtime='westend', + pallets='pallet_balances', + output_path='./polkadot/runtime/westend/src/weights', + header=header_path, + bench_flags='--flag3 --flag4' + )), + call(get_mock_bench_output( + runtime='westend', + pallets='pallet_staking', + output_path='./polkadot/runtime/westend/src/weights', + header=header_path, + bench_flags='--flag3 --flag4' + )), + ] + self.mock_system.assert_has_calls(expected_calls, any_order=True) + + + def test_bench_command_normal_execution_xcm(self): + self.mock_parse_args.return_value = (argparse.Namespace( + command='bench', + runtime=['westend'], + pallet=['pallet_xcm_benchmarks::generic'], + continue_on_fail=False, + quiet=False, + clean=False, + image=None + ), []) + header_path = os.path.abspath('polkadot/file_header.txt') + self.mock_popen.return_value.read.side_effect = [ + "pallet_balances\npallet_staking\npallet_something\npallet_xcm_benchmarks::generic\n", # Output for westend runtime + ] + + with patch('sys.exit') as mock_exit: + import cmd + cmd.main() + mock_exit.assert_not_called() + + expected_calls = [ + # Build calls + call("forklift cargo build -p westend-runtime --profile release --features=runtime-benchmarks"), + + # Westend runtime calls + call(get_mock_bench_output( + runtime='westend', + pallets='pallet_xcm_benchmarks::generic', + output_path='./polkadot/runtime/westend/src/weights/xcm', + header=header_path, + bench_flags='--flag3 --flag4', + template="polkadot/xcm/pallet-xcm-benchmarks/template.hbs" + )), + ] + self.mock_system.assert_has_calls(expected_calls, any_order=True) + + def test_bench_command_two_runtimes_two_pallets(self): + self.mock_parse_args.return_value = (argparse.Namespace( + command='bench', + runtime=['westend', 'rococo'], + pallet=['pallet_balances', 'pallet_staking'], + continue_on_fail=False, + quiet=False, + clean=False, + image=None + ), []) + self.mock_popen.return_value.read.side_effect = [ + "pallet_staking\npallet_balances\n", # Output for westend runtime + "pallet_staking\npallet_balances\n", # Output for rococo runtime + ] + + with patch('sys.exit') as mock_exit: + import cmd + cmd.main() + mock_exit.assert_not_called() + header_path = os.path.abspath('polkadot/file_header.txt') + + expected_calls = [ + # Build calls + call("forklift cargo build -p westend-runtime --profile release --features=runtime-benchmarks"), + call("forklift cargo build -p rococo-runtime --profile release --features=runtime-benchmarks"), + # Westend runtime calls + call(get_mock_bench_output( + runtime='westend', + pallets='pallet_staking', + output_path='./polkadot/runtime/westend/src/weights', + header=header_path, + bench_flags='--flag3 --flag4' + )), + call(get_mock_bench_output( + runtime='westend', + pallets='pallet_balances', + output_path='./polkadot/runtime/westend/src/weights', + header=header_path, + bench_flags='--flag3 --flag4' + )), + # Rococo runtime calls + call(get_mock_bench_output( + runtime='rococo', + pallets='pallet_staking', + output_path='./polkadot/runtime/rococo/src/weights', + header=header_path, + bench_flags='' + )), + call(get_mock_bench_output( + runtime='rococo', + pallets='pallet_balances', + output_path='./polkadot/runtime/rococo/src/weights', + header=header_path, + bench_flags='' + )), + ] + self.mock_system.assert_has_calls(expected_calls, any_order=True) + + def test_bench_command_one_dev_runtime(self): + self.mock_parse_args.return_value = (argparse.Namespace( + command='bench', + runtime=['dev'], + pallet=['pallet_balances'], + continue_on_fail=False, + quiet=False, + clean=False, + image=None + ), []) + manifest_dir = "substrate/frame/kitchensink" + self.mock_popen.return_value.read.side_effect = [ + "pallet_balances\npallet_something", # Output for dev runtime + manifest_dir + "/Cargo.toml" # Output for manifest path in dev runtime + ] + header_path = os.path.abspath('substrate/HEADER-APACHE2') + + with patch('sys.exit') as mock_exit: + import cmd + cmd.main() + mock_exit.assert_not_called() + + expected_calls = [ + # Build calls + call("forklift cargo build -p kitchensink-runtime --profile release --features=runtime-benchmarks,riscv"), + # Westend runtime calls + call(get_mock_bench_output( + runtime='kitchensink', + pallets='pallet_balances', + output_path=manifest_dir + "/src/weights.rs", + header=header_path, + bench_flags='--flag1 --flag2', + template="substrate/.maintain/frame-weight-template.hbs" + )), + ] + self.mock_system.assert_has_calls(expected_calls, any_order=True) + + def test_bench_command_one_cumulus_runtime(self): + self.mock_parse_args.return_value = (argparse.Namespace( + command='bench', + runtime=['asset-hub-westend'], + pallet=['pallet_assets'], + continue_on_fail=False, + quiet=False, + clean=False, + image=None + ), []) + self.mock_popen.return_value.read.side_effect = [ + "pallet_assets\n", # Output for asset-hub-westend runtime + ] + header_path = os.path.abspath('cumulus/file_header.txt') + + with patch('sys.exit') as mock_exit: + import cmd + cmd.main() + mock_exit.assert_not_called() + + expected_calls = [ + # Build calls + call("forklift cargo build -p asset-hub-westend-runtime --profile release --features=runtime-benchmarks"), + # Asset-hub-westend runtime calls + call(get_mock_bench_output( + runtime='asset-hub-westend', + pallets='pallet_assets', + output_path='./cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights', + header=header_path, + bench_flags='--flag7 --flag8' + )), + ] + + self.mock_system.assert_has_calls(expected_calls, any_order=True) + + def test_bench_command_one_cumulus_runtime_xcm(self): + self.mock_parse_args.return_value = (argparse.Namespace( + command='bench', + runtime=['asset-hub-westend'], + pallet=['pallet_xcm_benchmarks::generic', 'pallet_assets'], + continue_on_fail=False, + quiet=False, + clean=False, + image=None + ), []) + self.mock_popen.return_value.read.side_effect = [ + "pallet_assets\npallet_xcm_benchmarks::generic\n", # Output for asset-hub-westend runtime + ] + header_path = os.path.abspath('cumulus/file_header.txt') + + with patch('sys.exit') as mock_exit: + import cmd + cmd.main() + mock_exit.assert_not_called() + + expected_calls = [ + # Build calls + call("forklift cargo build -p asset-hub-westend-runtime --profile release --features=runtime-benchmarks"), + # Asset-hub-westend runtime calls + call(get_mock_bench_output( + runtime='asset-hub-westend', + pallets='pallet_xcm_benchmarks::generic', + output_path='./cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm', + header=header_path, + bench_flags='--flag7 --flag8', + template="cumulus/templates/xcm-bench-template.hbs" + )), + call(get_mock_bench_output( + runtime='asset-hub-westend', + pallets='pallet_assets', + output_path='./cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights', + header=header_path, + bench_flags='--flag7 --flag8' + )), + ] + + self.mock_system.assert_has_calls(expected_calls, any_order=True) + + @patch('argparse.ArgumentParser.parse_known_args', return_value=(argparse.Namespace(command='fmt', continue_on_fail=False), [])) + @patch('os.system', return_value=0) + def test_fmt_command(self, mock_system, mock_parse_args): + with patch('sys.exit') as mock_exit: + import cmd + cmd.main() + mock_exit.assert_not_called() + mock_system.assert_any_call('cargo +nightly fmt') + mock_system.assert_any_call('taplo format --config .config/taplo.toml') + + @patch('argparse.ArgumentParser.parse_known_args', return_value=(argparse.Namespace(command='update-ui', continue_on_fail=False), [])) + @patch('os.system', return_value=0) + def test_update_ui_command(self, mock_system, mock_parse_args): + with patch('sys.exit') as mock_exit: + import cmd + cmd.main() + mock_exit.assert_not_called() + mock_system.assert_called_with('sh ./scripts/update-ui-tests.sh') + + @patch('argparse.ArgumentParser.parse_known_args', return_value=(argparse.Namespace(command='prdoc', continue_on_fail=False), [])) + @patch('os.system', return_value=0) + def test_prdoc_command(self, mock_system, mock_parse_args): + with patch('sys.exit') as mock_exit: + import cmd + cmd.main() + mock_exit.assert_not_called() + self.mock_generate_prdoc_main.assert_called_with(mock_parse_args.return_value[0]) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/.github/scripts/common/lib.sh b/.github/scripts/common/lib.sh index bfb3120ad9bba1d647ef8719d99ae885cee2dfb7..d2a4baf12fa739bb21f9978255919ef6ec552ad5 100755 --- a/.github/scripts/common/lib.sh +++ b/.github/scripts/common/lib.sh @@ -242,6 +242,7 @@ fetch_release_artifacts() { # - GITHUB_TOKEN # - REPO in the form paritytech/polkadot fetch_release_artifacts_from_s3() { + BINARY=$1 echo "Version : $VERSION" echo "Repo : $REPO" echo "Binary : $BINARY" @@ -299,23 +300,23 @@ function check_sha256() { } # Import GPG keys of the release team members -# This is done in parallel as it can take a while sometimes function import_gpg_keys() { - GPG_KEYSERVER=${GPG_KEYSERVER:-"keyserver.ubuntu.com"} + GPG_KEYSERVER=${GPG_KEYSERVER:-"hkps://keyserver.ubuntu.com"} SEC="9D4B2B6EB8F97156D19669A9FF0812D491B96798" EGOR="E6FC4D4782EB0FA64A4903CCDB7D3555DD3932D3" MORGAN="2E92A9D8B15D7891363D1AE8AF9E6C43F7F8C4CF" + PARITY_RELEASES="90BD75EBBB8E95CB3DA6078F94A4029AB4B35DAE" - echo "Importing GPG keys from $GPG_KEYSERVER in parallel" - for key in $SEC $EGOR $MORGAN; do + echo "Importing GPG keys from $GPG_KEYSERVER" + for key in $SEC $EGOR $MORGAN $PARITY_RELEASES; do ( echo "Importing GPG key $key" gpg --no-tty --quiet --keyserver $GPG_KEYSERVER --recv-keys $key echo -e "5\ny\n" | gpg --no-tty --command-fd 0 --expert --edit-key $key trust; - ) & + ) done wait - gpg -k $SEC + gpg -k } # Check the GPG signature for a given binary @@ -461,7 +462,7 @@ function get_polkadot_node_version_from_code() { validate_stable_tag() { tag="$1" - pattern='^stable[0-9]+(-[0-9]+)?$' + pattern="^stable[0-9]{4}(-[0-9]+)?(-rc[0-9]+)?$" if [[ $tag =~ $pattern ]]; then echo $tag diff --git a/.github/scripts/deny-git-deps.py b/.github/scripts/deny-git-deps.py index 622fc64c488123a153b438af8516f3fa65133776..bd4fcf1f92372bb689e458678b9e5b32f377768c 100644 --- a/.github/scripts/deny-git-deps.py +++ b/.github/scripts/deny-git-deps.py @@ -15,6 +15,7 @@ KNOWN_BAD_GIT_DEPS = { 'simple-mermaid': ['xcm-docs'], # Fix in 'bandersnatch_vrfs': ['sp-core'], + 'subwasmlib': ['polkadot-zombienet-sdk-tests'], } root = sys.argv[1] if len(sys.argv) > 1 else os.getcwd() @@ -24,7 +25,7 @@ errors = [] def check_dep(dep, used_by): if dep.location != DependencyLocation.GIT: return - + if used_by in KNOWN_BAD_GIT_DEPS.get(dep.name, []): print(f'๐Ÿคจ Ignoring git dependency {dep.name} in {used_by}') else: diff --git a/.github/scripts/generate-prdoc.py b/.github/scripts/generate-prdoc.py index ba7def20fcb99bb48830a0a75d789a1ca8c20397..edcdb82cd22ea4e0aeb25114defd2cdbcc2b15b7 100644 --- a/.github/scripts/generate-prdoc.py +++ b/.github/scripts/generate-prdoc.py @@ -7,7 +7,7 @@ It downloads and parses the patch from the GitHub API to opulate the prdoc with This will delete any prdoc that already exists for the PR if `--force` is passed. Usage: - python generate-prdoc.py --pr 1234 --audience "TODO" --bump "TODO" + python generate-prdoc.py --pr 1234 --audience node_dev --bump patch """ import argparse @@ -48,9 +48,8 @@ def create_prdoc(pr, audience, title, description, patch, bump, force): else: print(f"No preexisting PrDoc for PR {pr}") - prdoc = { "doc": [{}], "crates": [] } + prdoc = { "title": title, "doc": [{}], "crates": [] } - prdoc["title"] = title prdoc["doc"][0]["audience"] = audience prdoc["doc"][0]["description"] = description @@ -58,20 +57,24 @@ def create_prdoc(pr, audience, title, description, patch, bump, force): modified_paths = [] for diff in whatthepatch.parse_patch(patch): - modified_paths.append(diff.header.new_path) + new_path = diff.header.new_path + # Sometimes this lib returns `/dev/null` as the new path... + if not new_path.startswith("/dev"): + modified_paths.append(new_path) modified_crates = {} for p in modified_paths: # Go up until we find a Cargo.toml p = os.path.join(workspace.path, p) while not os.path.exists(os.path.join(p, "Cargo.toml")): + if p == '/': + exit(1) p = os.path.dirname(p) with open(os.path.join(p, "Cargo.toml")) as f: manifest = toml.load(f) if not "package" in manifest: - print(f"File was not in any crate: {p}") continue crate_name = manifest["package"]["name"] @@ -80,8 +83,6 @@ def create_prdoc(pr, audience, title, description, patch, bump, force): else: print(f"Skipping unpublished crate: {crate_name}") - print(f"Modified crates: {modified_crates.keys()}") - for crate_name in modified_crates.keys(): entry = { "name": crate_name } @@ -95,19 +96,47 @@ def create_prdoc(pr, audience, title, description, patch, bump, force): # write the parsed PR documentation back to the file with open(path, "w") as f: - yaml.dump(prdoc, f) + yaml.dump(prdoc, f, sort_keys=False) print(f"PrDoc for PR {pr} written to {path}") -def parse_args(): - parser = argparse.ArgumentParser() - parser.add_argument("--pr", type=int, required=True) - parser.add_argument("--audience", type=str, default="TODO") - parser.add_argument("--bump", type=str, default="TODO") - parser.add_argument("--force", type=str) - return parser.parse_args() +# Make the `description` a multiline string instead of escaping \r\n. +def setup_yaml(): + def yaml_multiline_string_presenter(dumper, data): + if len(data.splitlines()) > 1: + data = '\n'.join([line.rstrip() for line in data.strip().splitlines()]) + return dumper.represent_scalar('tag:yaml.org,2002:str', data, style='|') + return dumper.represent_scalar('tag:yaml.org,2002:str', data) + + yaml.add_representer(str, yaml_multiline_string_presenter) + +# parse_args is also used by cmd/cmd.py +# if pr_required is False, then --pr is optional, as it can be derived from the PR comment body +def setup_parser(parser=None, pr_required=True): + allowed_audiences = ["runtime_dev", "runtime_user", "node_dev", "node_operator"] + if parser is None: + parser = argparse.ArgumentParser() + parser.add_argument("--pr", type=int, required=pr_required, help="The PR number to generate the PrDoc for.") + parser.add_argument("--audience", type=str, nargs='*', choices=allowed_audiences, default=["todo"], help="The audience of whom the changes may concern. Example: --audience runtime_dev node_dev") + parser.add_argument("--bump", type=str, default="major", choices=["patch", "minor", "major", "silent", "ignore", "no_change"], help="A default bump level for all crates. Example: --bump patch") + parser.add_argument("--force", action="store_true", help="Whether to overwrite any existing PrDoc.") + return parser + +def snake_to_title(s): + return ' '.join(word.capitalize() for word in s.split('_')) + +def main(args): + print(f"Args: {args}, force: {args.force}") + setup_yaml() + try: + # Convert snake_case audience arguments to title case + mapped_audiences = [snake_to_title(a) for a in args.audience] + from_pr_number(args.pr, mapped_audiences, args.bump, args.force) + return 0 + except Exception as e: + print(f"Error generating prdoc: {e}") + return 1 if __name__ == "__main__": - args = parse_args() - force = True if args.force.lower() == "true" else False - print(f"Args: {args}, force: {force}") - from_pr_number(args.pr, args.audience, args.bump, force) + parser = setup_parser() + args = parser.parse_args() + main(args) diff --git a/.github/scripts/generate-prdoc.requirements.txt b/.github/scripts/generate-prdoc.requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..c17aceff63a0fcd095ba7f66fd9dc39ac3eba2c4 --- /dev/null +++ b/.github/scripts/generate-prdoc.requirements.txt @@ -0,0 +1,6 @@ +requests +cargo-workspace +PyGithub +whatthepatch +pyyaml +toml \ No newline at end of file diff --git a/.github/scripts/release/build-deb.sh b/.github/scripts/release/build-deb.sh new file mode 100755 index 0000000000000000000000000000000000000000..6cb833f98a4e48e70f297cc2be82994917f78123 --- /dev/null +++ b/.github/scripts/release/build-deb.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +set -e + +PRODUCT=$1 +VERSION=$2 +PROFILE=${PROFILE:-production} + +cargo install --version 2.7.0 cargo-deb --locked -q +echo "Using cargo-deb v$(cargo-deb --version)" +echo "Building a Debian package for '$PRODUCT' in '$PROFILE' profile" + +# we need to start the custom version with a didgit as requires it cargo-deb +cargo deb --profile $PROFILE --no-strip --no-build -p $PRODUCT --deb-version 1-$VERSION + +deb=target/debian/$PRODUCT_*_amd64.deb + +cp $deb target/production/ diff --git a/.github/scripts/release/build-linux-release.sh b/.github/scripts/release/build-linux-release.sh new file mode 100755 index 0000000000000000000000000000000000000000..a6bd658d292a6b9dedf32077185521c1cff0c066 --- /dev/null +++ b/.github/scripts/release/build-linux-release.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +# This is used to build our binaries: +# - polkadot +# - polkadot-parachain +# set -e + +BIN=$1 +PACKAGE=${2:-$BIN} + +PROFILE=${PROFILE:-production} +ARTIFACTS=/artifacts/$BIN +VERSION=$(git tag -l --contains HEAD | grep -E "^v.*") + +echo "Artifacts will be copied into $ARTIFACTS" +mkdir -p "$ARTIFACTS" + +git log --pretty=oneline -n 1 +time cargo build --profile $PROFILE --locked --verbose --bin $BIN --package $PACKAGE + +echo "Artifact target: $ARTIFACTS" + +cp ./target/$PROFILE/$BIN "$ARTIFACTS" +pushd "$ARTIFACTS" > /dev/nul +sha256sum "$BIN" | tee "$BIN.sha256" + +EXTRATAG="$($ARTIFACTS/$BIN --version | + sed -n -r 's/^'$BIN' ([0-9.]+.*-[0-9a-f]{7,13})-.*$/\1/p')" + +EXTRATAG="${VERSION}-${EXTRATAG}-$(cut -c 1-8 $ARTIFACTS/$BIN.sha256)" + +echo "$BIN version = ${VERSION} (EXTRATAG = ${EXTRATAG})" +echo -n ${VERSION} > "$ARTIFACTS/VERSION" +echo -n ${EXTRATAG} > "$ARTIFACTS/EXTRATAG" diff --git a/.github/scripts/release/release_lib.sh b/.github/scripts/release/release_lib.sh new file mode 100644 index 0000000000000000000000000000000000000000..f5032073b6173057e78994c539e527aeffc80892 --- /dev/null +++ b/.github/scripts/release/release_lib.sh @@ -0,0 +1,139 @@ +#!/usr/bin/env bash + +# Set the new version by replacing the value of the constant given as patetrn +# in the file. +# +# input: pattern, version, file +#output: none +set_version() { + pattern=$1 + version=$2 + file=$3 + + sed -i "s/$pattern/\1\"${version}\"/g" $file + return 0 +} + +# Commit changes to git with specific message. +# "|| true" does not let script to fail with exit code 1, +# in case there is nothing to commit. +# +# input: MESSAGE (any message which should be used for the commit) +# output: none +commit_with_message() { + MESSAGE=$1 + git commit -a -m "$MESSAGE" || true +} + +# Retun list of the runtimes filterd +# input: none +# output: list of filtered runtimes +get_filtered_runtimes_list() { + grep_filters=("runtime.*" "test|template|starters|substrate") + + git grep spec_version: | grep .rs: | grep -e "${grep_filters[0]}" | grep "lib.rs" | grep -vE "${grep_filters[1]}" | cut -d: -f1 +} + +# Sets provided spec version +# input: version +set_spec_versions() { + NEW_VERSION=$1 + runtimes_list=(${@:2}) + + printf "Setting spec_version to $NEW_VERSION\n" + + for f in ${runtimes_list[@]}; do + printf " processing $f" + sed -ri "s/spec_version: [0-9]+_[0-9]+_[0-9]+,/spec_version: $NEW_VERSION,/" $f + done + + commit_with_message "Bump spec_version to $NEW_VERSION" + + git_show_log 'spec_version' +} + +# Displays formated results of the git log command +# for the given pattern which needs to be found in logs +# input: pattern, count (optional, default is 10) +git_show_log() { + PATTERN="$1" + COUNT=${2:-10} + git log --pretty=format:"%h %ad | %s%d [%an]" --graph --date=iso-strict | \ + head -n $COUNT | grep -iE "$PATTERN" --color=always -z +} + +# Get a spec_version number from the crate version +# +# ## inputs +# - v1.12.0 or 1.12.0 +# +# ## output: +# 1_012_000 or 1_012_001 if SUFFIX is set +function get_spec_version() { + INPUT=$1 + SUFFIX=${SUFFIX:-000} #this variable makes it possible to set a specific ruuntime version like 93826 it can be intialised as sestem variable + [[ $INPUT =~ .*([0-9]+\.[0-9]+\.[0-9]{1,2}).* ]] + VERSION="${BASH_REMATCH[1]}" + MATCH="${BASH_REMATCH[0]}" + if [ -z $MATCH ]; then + return 1 + else + SPEC_VERSION="$(sed -e "s/\./_0/g" -e "s/_[^_]*\$/_$SUFFIX/" <<< $VERSION)" + echo "$SPEC_VERSION" + return 0 + fi +} + +# Reorganize the prdoc files for the release +# +# input: VERSION (e.g. v1.0.0) +# output: none +reorder_prdocs() { + VERSION="$1" + + printf "[+] โ„น๏ธ Reordering prdocs:" + + VERSION=$(sed -E 's/^v([0-9]+\.[0-9]+\.[0-9]+).*$/\1/' <<< "$VERSION") #getting reed of the 'v' prefix + mkdir -p "prdoc/$VERSION" + mv prdoc/pr_*.prdoc prdoc/$VERSION + git add -A + commit_with_message "Reordering prdocs for the release $VERSION" +} + +# Bump the binary version of the polkadot-parachain binary with the +# new bumped version and commit changes. +# +# input: version e.g. 1.16.0 +set_polkadot_parachain_binary_version() { + bumped_version="$1" + cargo_toml_file="$2" + + set_version "\(^version = \)\".*\"" $bumped_version $cargo_toml_file + + cargo update --workspace --offline # we need this to update Cargo.loc with the new versions as well + + MESSAGE="Bump versions in: ${cargo_toml_file}" + commit_with_message "$MESSAGE" + git_show_log "$MESSAGE" +} + + +upload_s3_release() { + alias aws='podman run --rm -it docker.io/paritytech/awscli -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY -e AWS_BUCKET aws' + + product=$1 + version=$2 + + echo "Working on product: $product " + echo "Working on version: $version " + + echo "Current content, should be empty on new uploads:" + aws s3 ls "s3://releases.parity.io/polkadot/${version}/" --recursive --human-readable --summarize || true + echo "Content to be uploaded:" + artifacts="artifacts/$product/" + ls "$artifacts" + aws s3 sync --acl public-read "$artifacts" "s3://releases.parity.io/polkadot/${version}/" + echo "Uploaded files:" + aws s3 ls "s3://releases.parity.io/polkadot/${version}/" --recursive --human-readable --summarize + echo "โœ… The release should be at https://releases.parity.io/polkadot/${version}" +} diff --git a/.github/workflows/build-misc.yml b/.github/workflows/build-misc.yml new file mode 100644 index 0000000000000000000000000000000000000000..2a8e81b978788ba1aed2846eecc42593629f03ae --- /dev/null +++ b/.github/workflows/build-misc.yml @@ -0,0 +1,84 @@ +name: Build Misc + +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + merge_group: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + + preflight: + uses: ./.github/workflows/reusable-preflight.yml + + build-runtimes-polkavm: + timeout-minutes: 20 + needs: [preflight] + runs-on: ${{ needs.preflight.outputs.RUNNER }} + container: + image: ${{ needs.preflight.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Check Rust + run: | + rustup show + rustup +nightly show + + - name: Build + env: + SUBSTRATE_RUNTIME_TARGET: riscv + run: | + forklift cargo check -p minimal-template-runtime + forklift cargo check -p westend-runtime + forklift cargo check -p rococo-runtime + forklift cargo check -p polkadot-test-runtime + + build-subkey: + timeout-minutes: 20 + needs: [preflight] + runs-on: ${{ needs.preflight.outputs.RUNNER }} + container: + image: ${{ needs.preflight.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Check Rust + run: | + rustup show + rustup +nightly show + + - name: Build + env: + SKIP_WASM_BUILD: 1 + run: | + cd ./substrate/bin/utils/subkey + forklift cargo build --locked --release + + confirm-required-build-misc-jobs-passed: + runs-on: ubuntu-latest + name: All build misc jobs passed + # If any new job gets added, be sure to add it to this array + needs: [build-runtimes-polkavm, build-subkey] + if: always() && !cancelled() + steps: + - run: | + tee resultfile <<< '${{ toJSON(needs) }}' + FAILURES=$(cat resultfile | grep '"result": "failure"' | wc -l) + if [ $FAILURES -gt 0 ]; then + echo "### At least one required job failed โŒ" >> $GITHUB_STEP_SUMMARY + exit 1 + else + echo '### Good job! All the required jobs passed ๐Ÿš€' >> $GITHUB_STEP_SUMMARY + fi diff --git a/.github/workflows/build-publish-images.yml b/.github/workflows/build-publish-images.yml new file mode 100644 index 0000000000000000000000000000000000000000..874b5d37469cde33f2b92f8cec0a4d61c57b7ffb --- /dev/null +++ b/.github/workflows/build-publish-images.yml @@ -0,0 +1,494 @@ +# GHA for build-* +name: Build and push images + +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened, ready_for_review, labeled] + merge_group: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true +env: + COMMIT_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + +jobs: + # + # + # + preflight: + ## TODO: remove when ready + if: contains(github.event.label.name, 'GHA-migration') || contains(github.event.pull_request.labels.*.name, 'GHA-migration') + uses: ./.github/workflows/reusable-preflight.yml + + ### Build ######################## + + # + # + # + build-linux-stable: + needs: [preflight] + runs-on: ${{ needs.preflight.outputs.RUNNER }} + timeout-minutes: 60 + container: + image: ${{ needs.preflight.outputs.IMAGE }} + env: + RUST_TOOLCHAIN: stable + # Enable debug assertions since we are running optimized builds for testing + # but still want to have debug assertions. + RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: build + run: | + forklift cargo build --locked --profile testnet --features pyroscope,fast-runtime --bin polkadot --bin polkadot-prepare-worker --bin polkadot-execute-worker + ROCOCO_EPOCH_DURATION=10 ./polkadot/scripts/build-only-wasm.sh rococo-runtime $(pwd)/runtimes/rococo-runtime-10/ + ROCOCO_EPOCH_DURATION=100 ./polkadot/scripts/build-only-wasm.sh rococo-runtime $(pwd)/runtimes/rococo-runtime-100/ + ROCOCO_EPOCH_DURATION=600 ./polkadot/scripts/build-only-wasm.sh rococo-runtime $(pwd)/runtimes/rococo-runtime-600/ + pwd + ls -alR runtimes + - name: pack artifacts + run: | + mkdir -p ./artifacts + VERSION="${{ needs.preflight.outputs.SOURCE_REF_NAME }}" # will be tag or branch name + mv ./target/testnet/polkadot ./artifacts/. + mv ./target/testnet/polkadot-prepare-worker ./artifacts/. + mv ./target/testnet/polkadot-execute-worker ./artifacts/. + mv ./runtimes/ ./artifacts/. + cd artifacts/ + sha256sum polkadot | tee polkadot.sha256 + shasum -c polkadot.sha256 + cd ../ + EXTRATAG="${{ needs.preflight.outputs.SOURCE_REF_NAME }}-${COMMIT_SHA}" + echo "Polkadot version = ${VERSION} (EXTRATAG = ${EXTRATAG})" + echo -n ${VERSION} > ./artifacts/VERSION + echo -n ${EXTRATAG} > ./artifacts/EXTRATAG + echo -n ${GITHUB_RUN_ID} > ./artifacts/BUILD_LINUX_JOB_ID + RELEASE_VERSION=$(./artifacts/polkadot -V | awk '{print $2}'| awk -F "-" '{print $1}') + echo -n "v${RELEASE_VERSION}" > ./artifacts/BUILD_RELEASE_VERSION + cp -r docker/* ./artifacts + + - name: tar + run: tar -cvf artifacts.tar artifacts + + - name: upload artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + path: artifacts.tar + retention-days: 1 + + # + # + # + build-linux-stable-cumulus: + needs: [preflight] + runs-on: ${{ needs.preflight.outputs.RUNNER }} + timeout-minutes: 60 + container: + image: ${{ needs.preflight.outputs.IMAGE }} + env: + RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: build + run: | + echo "___Building a binary, please refrain from using it in production since it goes with the debug assertions.___" + forklift cargo build --release --locked -p polkadot-parachain-bin --bin polkadot-parachain + echo "___Packing the artifacts___" + mkdir -p ./artifacts + mv ./target/release/polkadot-parachain ./artifacts/. + echo "___The VERSION is either a tag name or the curent branch if triggered not by a tag___" + echo ${{ needs.preflight.outputs.SOURCE_REF_NAME }} | tee ./artifacts/VERSION + + - name: tar + run: tar -cvf artifacts.tar artifacts + + - name: upload artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + path: artifacts.tar + retention-days: 1 + + # + # + # + build-test-parachain: + needs: [preflight] + runs-on: ${{ needs.preflight.outputs.RUNNER }} + timeout-minutes: 60 + container: + image: ${{ needs.preflight.outputs.IMAGE }} + env: + RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: build + run: | + echo "___Building a binary, please refrain from using it in production since it goes with the debug assertions.___" + forklift cargo build --release --locked -p cumulus-test-service --bin test-parachain + - name: pack artifacts + run: | + echo "___Packing the artifacts___" + mkdir -p ./artifacts + mv ./target/release/test-parachain ./artifacts/. + mkdir -p ./artifacts/zombienet + mv ./target/release/wbuild/cumulus-test-runtime/wasm_binary_spec_version_incremented.rs.compact.compressed.wasm ./artifacts/zombienet/. + + - name: tar + run: tar -cvf artifacts.tar artifacts + + - name: upload artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + path: artifacts.tar + retention-days: 1 + + # + # + # + build-test-collators: + needs: [preflight] + runs-on: ${{ needs.preflight.outputs.RUNNER }} + timeout-minutes: 60 + container: + image: ${{ needs.preflight.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: build + run: | + forklift cargo build --locked --profile testnet -p test-parachain-adder-collator + forklift cargo build --locked --profile testnet -p test-parachain-undying-collator + - name: pack artifacts + run: | + mkdir -p ./artifacts + mv ./target/testnet/adder-collator ./artifacts/. + mv ./target/testnet/undying-collator ./artifacts/. + echo -n "${{ needs.preflight.outputs.SOURCE_REF_NAME }}" > ./artifacts/VERSION + echo -n "${{ needs.preflight.outputs.SOURCE_REF_NAME }}-${COMMIT_SHA}" > ./artifacts/EXTRATAG + echo "adder-collator version = $(cat ./artifacts/VERSION) (EXTRATAG = $(cat ./artifacts/EXTRATAG))" + echo "undying-collator version = $(cat ./artifacts/VERSION) (EXTRATAG = $(cat ./artifacts/EXTRATAG))" + cp -r ./docker/* ./artifacts + + - name: tar + run: tar -cvf artifacts.tar artifacts + + - name: upload artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + path: artifacts.tar + retention-days: 1 + + # + # + # + build-malus: + needs: [preflight] + runs-on: ${{ needs.preflight.outputs.RUNNER }} + timeout-minutes: 60 + container: + image: ${{ needs.preflight.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: build + run: | + forklift cargo build --locked --profile testnet -p polkadot-test-malus --bin malus --bin polkadot-prepare-worker --bin polkadot-execute-worker + - name: pack artifacts + run: | + mkdir -p ./artifacts + mv ./target/testnet/malus ./artifacts/. + mv ./target/testnet/polkadot-execute-worker ./artifacts/. + mv ./target/testnet/polkadot-prepare-worker ./artifacts/. + echo -n "${{ needs.preflight.outputs.SOURCE_REF_NAME }}" > ./artifacts/VERSION + echo -n "${{ needs.preflight.outputs.SOURCE_REF_NAME }}-${COMMIT_SHA}" > ./artifacts/EXTRATAG + echo "polkadot-test-malus = $(cat ./artifacts/VERSION) (EXTRATAG = $(cat ./artifacts/EXTRATAG))" + cp -r ./docker/* ./artifacts + + - name: tar + run: tar -cvf artifacts.tar artifacts + + - name: upload artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + path: artifacts.tar + retention-days: 1 + + # + # + # + build-linux-substrate: + needs: [preflight] + runs-on: ${{ needs.preflight.outputs.RUNNER }} + timeout-minutes: 60 + container: + image: ${{ needs.preflight.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + # tldr: we need to checkout the branch HEAD explicitly because of our dynamic versioning approach while building the substrate binary + # see https://github.com/paritytech/ci_cd/issues/682#issuecomment-1340953589 + ref: ${{ github.head_ref || github.ref_name }} + - name: build + run: | + mkdir -p ./artifacts/substrate/ + WASM_BUILD_NO_COLOR=1 forklift cargo build --locked --release -p staging-node-cli + ls -la target/release/ + - name: pack artifacts + run: | + mv target/release/substrate-node ./artifacts/substrate/substrate + echo -n "Substrate version = " + if [[ "${{ github.ref }}" == "refs/tags/"* ]]; then + echo "${{ github.ref_name }}" | tee ./artifacts/substrate/VERSION; + else + ./artifacts/substrate/substrate --version | + cut -d ' ' -f 2 | tee ./artifacts/substrate/VERSION; + fi + sha256sum ./artifacts/substrate/substrate | tee ./artifacts/substrate/substrate.sha256 + cp -r ./docker/dockerfiles/substrate_injected.Dockerfile ./artifacts/substrate/ + + - name: tar + run: tar -cvf artifacts.tar artifacts + + - name: upload artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + path: artifacts.tar + retention-days: 1 + + # + # + # + prepare-bridges-zombienet-artifacts: + needs: [preflight] + runs-on: ${{ needs.preflight.outputs.RUNNER }} + timeout-minutes: 60 + container: + image: ${{ needs.preflight.outputs.IMAGE }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: build + run: | + forklift cargo build --locked --profile testnet -p polkadot-test-malus --bin malus --bin polkadot-prepare-worker --bin polkadot-execute-worker + - name: pack artifacts + run: | + mkdir -p ./artifacts/bridges-polkadot-sdk/bridges + cp -r bridges/testing ./artifacts/bridges-polkadot-sdk/bridges/testing + + - name: tar + run: tar -cvf artifacts.tar artifacts + + - name: upload artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ github.job }}-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + path: artifacts.tar + retention-days: 1 + + ### Publish ######################## + + # + # + # + build-push-image-test-parachain: + needs: [preflight, build-test-parachain] + runs-on: ${{ needs.preflight.outputs.RUNNER_DEFAULT }} + timeout-minutes: 60 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4.1.8 + with: + name: build-test-parachain-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + + - name: tar + run: tar -xvf artifacts.tar + + - name: build and push image + uses: ./.github/actions/build-push-image + with: + image-name: "europe-docker.pkg.dev/parity-ci-2024/temp-images/test-parachain" + dockerfile: "docker/dockerfiles/test-parachain_injected.Dockerfile" + + # + # + # + build-push-image-polkadot-debug: + needs: [preflight, build-linux-stable] + runs-on: ${{ needs.preflight.outputs.RUNNER_DEFAULT }} + timeout-minutes: 60 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4.1.8 + with: + name: build-linux-stable-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + + - name: tar + run: tar -xvf artifacts.tar + + - name: build and push image + uses: ./.github/actions/build-push-image + with: + image-name: "europe-docker.pkg.dev/parity-ci-2024/temp-images/polkadot-debug" + dockerfile: "docker/dockerfiles/polkadot/polkadot_injected_debug.Dockerfile" + + # + # + # + build-push-image-colander: + needs: [preflight, build-test-collators] + runs-on: ${{ needs.preflight.outputs.RUNNER_DEFAULT }} + timeout-minutes: 60 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4.1.8 + with: + name: build-test-collators-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + + - name: tar + run: tar -xvf artifacts.tar + + - name: build and push image + uses: ./.github/actions/build-push-image + with: + image-name: "europe-docker.pkg.dev/parity-ci-2024/temp-images/colander" + dockerfile: "docker/dockerfiles/collator_injected.Dockerfile" + + # + # + # + build-push-image-malus: + needs: [preflight, build-malus] + runs-on: ${{ needs.preflight.outputs.RUNNER_DEFAULT }} + timeout-minutes: 60 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4.1.8 + with: + name: build-malus-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + + - name: tar + run: tar -xvf artifacts.tar + + - name: build and push image + uses: ./.github/actions/build-push-image + with: + image-name: "europe-docker.pkg.dev/parity-ci-2024/temp-images/malus" + dockerfile: "docker/dockerfiles/malus_injected.Dockerfile" + + # + # + # + build-push-image-substrate-pr: + needs: [preflight, build-linux-substrate] + runs-on: ${{ needs.preflight.outputs.RUNNER_DEFAULT }} + timeout-minutes: 60 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4.1.8 + with: + name: build-linux-substrate-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + + - name: tar + run: tar -xvf artifacts.tar + + - name: build and push image + uses: ./.github/actions/build-push-image + with: + image-name: "europe-docker.pkg.dev/parity-ci-2024/temp-images/substrate" + dockerfile: "docker/dockerfiles/substrate_injected.Dockerfile" + + # + # + # + # unlike other images, bridges+zombienet image is based on Zombienet image that pulls required binaries + # from other fresh images (polkadot and cumulus) + build-push-image-bridges-zombienet-tests: + needs: + [ + preflight, + build-linux-stable, + build-linux-stable-cumulus, + prepare-bridges-zombienet-artifacts, + ] + runs-on: ${{ needs.preflight.outputs.RUNNER_DEFAULT }} + timeout-minutes: 60 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4.1.8 + with: + name: build-linux-stable-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + - name: tar + run: | + tar -xvf artifacts.tar + rm artifacts.tar + + - uses: actions/download-artifact@v4.1.8 + with: + name: build-linux-stable-cumulus-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + - name: tar + run: | + tar -xvf artifacts.tar + rm artifacts.tar + + - uses: actions/download-artifact@v4.1.8 + with: + name: prepare-bridges-zombienet-artifacts-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + - name: tar + run: | + tar -xvf artifacts.tar + rm artifacts.tar + + - name: build and push image + uses: ./.github/actions/build-push-image + with: + image-name: "europe-docker.pkg.dev/parity-ci-2024/temp-images/bridges-zombienet-tests" + dockerfile: "docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile" + + # + # + # + build-push-image-polkadot-parachain-debug: + needs: [preflight, build-linux-stable-cumulus] + runs-on: ${{ needs.preflight.outputs.RUNNER_DEFAULT }} + timeout-minutes: 60 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4.1.8 + with: + name: build-linux-stable-cumulus-${{ needs.preflight.outputs.SOURCE_REF_NAME }} + + - name: tar + run: tar -xvf artifacts.tar + + - name: build and push image + uses: ./.github/actions/build-push-image + with: + image-name: "europe-docker.pkg.dev/parity-ci-2024/temp-images/polkadot-parachain-debug" + dockerfile: "docker/dockerfiles/polkadot-parachain/polkadot-parachain-debug_unsigned_injected.Dockerfile" diff --git a/.github/workflows/check-cargo-check-runtimes.yml b/.github/workflows/check-cargo-check-runtimes.yml index ebcf6c5fc9bd30b16d4cdfaa3d2fd8f35b9eb3e1..b49a2cbbfd3e1c309a2991b35fdffd1b090ef3a8 100644 --- a/.github/workflows/check-cargo-check-runtimes.yml +++ b/.github/workflows/check-cargo-check-runtimes.yml @@ -1,33 +1,27 @@ name: Check Cargo Check Runtimes +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + on: pull_request: - types: [ opened, synchronize, reopened, ready_for_review, labeled ] - + types: [opened, synchronize, reopened, ready_for_review] + paths: + - "cumulus/parachains/runtimes/*" # Jobs in this workflow depend on each other, only for limiting peak amount of spawned workers jobs: - # GitHub Actions allows using 'env' in a container context. - # However, env variables don't work for forks: https://github.com/orgs/community/discussions/44322 - # This workaround sets the container image for each job using 'set-image' job output. - set-image: - if: contains(github.event.label.name, 'GHA-migration') || contains(github.event.pull_request.labels.*.name, 'GHA-migration') - runs-on: ubuntu-latest - timeout-minutes: 20 - outputs: - IMAGE: ${{ steps.set_image.outputs.IMAGE }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - id: set_image - run: cat .github/env >> $GITHUB_OUTPUT + preflight: + uses: ./.github/workflows/reusable-preflight.yml + check-runtime-assets: - runs-on: arc-runners-polkadot-sdk-beefy - needs: [set-image] + runs-on: ${{ needs.preflight.outputs.RUNNER }} + needs: [preflight] timeout-minutes: 20 container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - name: Checkout uses: actions/checkout@v4 @@ -37,11 +31,11 @@ jobs: root: cumulus/parachains/runtimes/assets check-runtime-collectives: - runs-on: arc-runners-polkadot-sdk-beefy - needs: [check-runtime-assets, set-image] + runs-on: ${{ needs.preflight.outputs.RUNNER }} + needs: [check-runtime-assets, preflight] timeout-minutes: 20 container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - name: Checkout uses: actions/checkout@v4 @@ -51,10 +45,10 @@ jobs: root: cumulus/parachains/runtimes/collectives check-runtime-coretime: - runs-on: arc-runners-polkadot-sdk-beefy + runs-on: ${{ needs.preflight.outputs.RUNNER }} container: - image: ${{ needs.set-image.outputs.IMAGE }} - needs: [check-runtime-assets, set-image] + image: ${{ needs.preflight.outputs.IMAGE }} + needs: [check-runtime-assets, preflight] timeout-minutes: 20 steps: - name: Checkout @@ -65,10 +59,10 @@ jobs: root: cumulus/parachains/runtimes/coretime check-runtime-bridge-hubs: - runs-on: arc-runners-polkadot-sdk-beefy + runs-on: ${{ needs.preflight.outputs.RUNNER }} container: - image: ${{ needs.set-image.outputs.IMAGE }} - needs: [set-image] + image: ${{ needs.preflight.outputs.IMAGE }} + needs: [preflight] timeout-minutes: 20 steps: - name: Checkout @@ -79,10 +73,10 @@ jobs: root: cumulus/parachains/runtimes/bridge-hubs check-runtime-contracts: - runs-on: arc-runners-polkadot-sdk-beefy + runs-on: ${{ needs.preflight.outputs.RUNNER }} container: - image: ${{ needs.set-image.outputs.IMAGE }} - needs: [check-runtime-collectives, set-image] + image: ${{ needs.preflight.outputs.IMAGE }} + needs: [check-runtime-collectives, preflight] timeout-minutes: 20 steps: - name: Checkout @@ -92,25 +86,11 @@ jobs: with: root: cumulus/parachains/runtimes/contracts - check-runtime-starters: - runs-on: arc-runners-polkadot-sdk-beefy - container: - image: ${{ needs.set-image.outputs.IMAGE }} - needs: [check-runtime-assets, set-image] - timeout-minutes: 20 - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Run cargo check - uses: ./.github/actions/cargo-check-runtimes - with: - root: cumulus/parachains/runtimes/starters - check-runtime-testing: - runs-on: arc-runners-polkadot-sdk-beefy + runs-on: ${{ needs.preflight.outputs.RUNNER }} container: - image: ${{ needs.set-image.outputs.IMAGE }} - needs: [check-runtime-starters, set-image] + image: ${{ needs.preflight.outputs.IMAGE }} + needs: [preflight] timeout-minutes: 20 steps: - name: Checkout @@ -132,5 +112,14 @@ jobs: - check-runtime-contracts - check-runtime-starters - check-runtime-testing + if: always() && !cancelled() steps: - - run: echo '### Good job! All the tests passed ๐Ÿš€' >> $GITHUB_STEP_SUMMARY + - run: | + tee resultfile <<< '${{ toJSON(needs) }}' + FAILURES=$(cat resultfile | grep '"result": "failure"' | wc -l) + if [ $FAILURES -gt 0 ]; then + echo "### At least one required job failed โŒ" >> $GITHUB_STEP_SUMMARY + exit 1 + else + echo '### Good job! All the required jobs passed ๐Ÿš€' >> $GITHUB_STEP_SUMMARY + fi diff --git a/.github/workflows/check-features.yml b/.github/workflows/check-features.yml index d34b3d52c5332b61d9a90dc03de938f154de5c7e..d8e2f72c0ffd83852bb6bd015b7256934c23519c 100644 --- a/.github/workflows/check-features.yml +++ b/.github/workflows/check-features.yml @@ -4,6 +4,10 @@ on: pull_request: types: [opened, synchronize, reopened, ready_for_review] +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: check-features: runs-on: ubuntu-latest diff --git a/.github/workflows/check-frame-omni-bencher.yml b/.github/workflows/check-frame-omni-bencher.yml index e9db2d9129797b1707a273122c5edf19542f9381..924a8b7f712fee78b5b114afa69013e40fe6faef 100644 --- a/.github/workflows/check-frame-omni-bencher.yml +++ b/.github/workflows/check-frame-omni-bencher.yml @@ -5,7 +5,7 @@ on: branches: - master pull_request: - types: [ opened, synchronize, reopened, ready_for_review, labeled ] + types: [opened, synchronize, reopened, ready_for_review, labeled] merge_group: concurrency: @@ -16,55 +16,65 @@ env: ARTIFACTS_NAME: frame-omni-bencher-artifacts jobs: - changes: - # TODO: remove once migration is complete or this workflow is fully stable - if: contains(github.event.label.name, 'GHA-migration') - permissions: - pull-requests: read - uses: ./.github/workflows/reusable-check-changed-files.yml + preflight: + uses: ./.github/workflows/reusable-preflight.yml - set-image: - # GitHub Actions allows using 'env' in a container context. - # However, env variables don't work for forks: https://github.com/orgs/community/discussions/44322 - # This workaround sets the container image for each job using 'set-image' job output. - runs-on: ubuntu-latest - needs: changes - if: ${{ needs.changes.outputs.rust }} - outputs: - IMAGE: ${{ steps.set_image.outputs.IMAGE }} + quick-benchmarks-omni: + runs-on: ${{ needs.preflight.outputs.RUNNER_BENCHMARK }} + needs: [preflight] + if: ${{ needs.preflight.outputs.changes_rust }} + env: + RUSTFLAGS: "-C debug-assertions" + RUST_BACKTRACE: "full" + WASM_BUILD_NO_COLOR: 1 + WASM_BUILD_RUSTFLAGS: "-C debug-assertions" + RUST_LOG: "frame_omni_bencher=info,polkadot_sdk_frame=info" + timeout-minutes: 30 + container: + image: ${{ needs.preflight.outputs.IMAGE }} steps: - name: Checkout uses: actions/checkout@v4 - - id: set_image - run: cat .github/env >> $GITHUB_OUTPUT + - name: script + run: | + forklift cargo build --locked --quiet --release -p asset-hub-westend-runtime --features runtime-benchmarks + forklift cargo run --locked --release -p frame-omni-bencher --quiet -- v1 benchmark pallet --runtime target/release/wbuild/asset-hub-westend-runtime/asset_hub_westend_runtime.compact.compressed.wasm --all --steps 2 --repeat 1 --quiet + + runtime-matrix: + runs-on: ubuntu-latest + needs: [preflight] + if: ${{ needs.preflight.outputs.changes_rust }} + timeout-minutes: 30 + outputs: + runtime: ${{ steps.runtime.outputs.runtime }} + container: + image: ${{ needs.preflight.outputs.IMAGE }} + name: Extract runtimes from matrix + steps: + - uses: actions/checkout@v4 + - id: runtime + run: | + RUNTIMES=$(jq '[.[] | select(.package != null)]' .github/workflows/runtimes-matrix.json) + + RUNTIMES=$(echo $RUNTIMES | jq -c .) + echo "runtime=$RUNTIMES" + echo "runtime=$RUNTIMES" >> $GITHUB_OUTPUT run-frame-omni-bencher: - runs-on: arc-runners-polkadot-sdk-beefy - needs: [ set-image, changes ] # , build-frame-omni-bencher ] - if: ${{ needs.changes.outputs.rust }} + runs-on: ${{ needs.preflight.outputs.RUNNER_BENCHMARK }} + needs: [preflight, runtime-matrix] + if: ${{ needs.preflight.outputs.changes_rust }} timeout-minutes: 30 strategy: fail-fast: false # keep running other workflows even if one fails, to see the logs of all possible failures matrix: - runtime: - [ - westend-runtime, - rococo-runtime, - asset-hub-rococo-runtime, - asset-hub-westend-runtime, - bridge-hub-rococo-runtime, - bridge-hub-westend-runtime, - collectives-westend-runtime, - coretime-rococo-runtime, - coretime-westend-runtime, - people-rococo-runtime, - people-westend-runtime, - glutton-westend-runtime, - ] + runtime: ${{ fromJSON(needs.runtime-matrix.outputs.runtime) }} container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} env: - PACKAGE_NAME: ${{ matrix.runtime }} + PACKAGE_NAME: ${{ matrix.runtime.package }} + FLAGS: ${{ matrix.runtime.bench_flags }} + RUST_LOG: "frame_omni_bencher=info,polkadot_sdk_frame=info" steps: - name: Checkout uses: actions/checkout@v4 @@ -73,13 +83,25 @@ jobs: run: | RUNTIME_BLOB_NAME=$(echo $PACKAGE_NAME | sed 's/-/_/g').compact.compressed.wasm RUNTIME_BLOB_PATH=./target/release/wbuild/$PACKAGE_NAME/$RUNTIME_BLOB_NAME - forklift cargo build --release --locked -p $PACKAGE_NAME -p frame-omni-bencher --features runtime-benchmarks + forklift cargo build --release --locked -p $PACKAGE_NAME -p frame-omni-bencher --features=${{ matrix.runtime.bench_features }} --quiet echo "Running short benchmarking for PACKAGE_NAME=$PACKAGE_NAME and RUNTIME_BLOB_PATH=$RUNTIME_BLOB_PATH" ls -lrt $RUNTIME_BLOB_PATH - ./target/release/frame-omni-bencher v1 benchmark pallet --runtime $RUNTIME_BLOB_PATH --all --steps 2 --repeat 1 + + cmd="./target/release/frame-omni-bencher v1 benchmark pallet --runtime $RUNTIME_BLOB_PATH --all --steps 2 --repeat 1 $FLAGS" + echo "Running command: $cmd" + eval "$cmd" confirm-frame-omni-benchers-passed: runs-on: ubuntu-latest name: All benchmarks passed - needs: run-frame-omni-bencher + needs: [quick-benchmarks-omni, run-frame-omni-bencher] + if: always() && !cancelled() steps: - - run: echo '### Good job! All the benchmarks passed ๐Ÿš€' >> $GITHUB_STEP_SUMMARY + - run: | + tee resultfile <<< '${{ toJSON(needs) }}' + FAILURES=$(cat resultfile | grep '"result": "failure"' | wc -l) + if [ $FAILURES -gt 0 ]; then + echo "### At least one required job failed โŒ" >> $GITHUB_STEP_SUMMARY + exit 1 + else + echo '### Good job! All the required jobs passed ๐Ÿš€' >> $GITHUB_STEP_SUMMARY + fi diff --git a/.github/workflows/check-getting-started.yml b/.github/workflows/check-getting-started.yml new file mode 100644 index 0000000000000000000000000000000000000000..0661fa144348de687c0ddf1266be593664adc5e4 --- /dev/null +++ b/.github/workflows/check-getting-started.yml @@ -0,0 +1,296 @@ +name: Check the getting-started.sh script + +# This workflow aims to make sure that the `getting-started.sh` script +# is functional and allows to build the templates +# on different operating systems. +# +# There are two jobs inside. +# One for systems that can run in a docker container, and one for macOS. +# +# Each job consists of: +# 1. Some necessary prerequisites for the workflow itself. +# 2. A first pass of the script, which will install dependencies and clone a template. +# 3. A second pass of the script, to make sure the behaviour is as expected. +# 4. Building the template - making sure it's buildable and runnable. +# +# The script is interacted with using the `expect` tool, which is available on all relevant systems. +# The steps are not re-used between macOS and other systems, +# because they are very similar but a little different. +# Additionally, macOS does NOT start from scratch here - for example, we have homebrew already installed. +# +# There are many combinations of systems, shells and templates. +# We test a selected handful of combinations here. + +on: + pull_request: + paths: + - ".github/workflows/check-getting-started.yml" + - "scripts/getting-started.sh" + schedule: + - cron: "0 5 * * *" + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + check-getting-started: + strategy: + fail-fast: true + matrix: + include: + - name: ubuntu + container: ubuntu + template: minimal + shell: bash + - name: debian + container: debian + template: parachain + shell: sh + - name: arch + container: archlinux + template: solochain + shell: sh + - name: fedora + container: fedora + template: parachain + shell: sh + - name: opensuse + container: opensuse/tumbleweed + template: solochain + shell: sh + runs-on: parity-large + container: ${{ matrix.container }}:latest + steps: + # A minimal amount of prerequisites required before we can run the actual getting-started script, + # which will install the rest of requirements. + - name: Install ubuntu/debian prerequisites + run: apt update && apt install -y expect sudo git + if: contains(matrix.name, 'ubuntu') || contains(matrix.name, 'debian') + - name: Install arch prerequisites + run: pacman -Syu --needed --noconfirm expect sudo git + if: contains(matrix.name, 'arch') + - name: Install fedora prerequisites + run: dnf --assumeyes install expect sudo git + if: contains(matrix.name, 'fedora') + - name: Install opensuse prerequisites + run: zypper install --no-confirm expect sudo git + if: contains(matrix.name, 'opensuse') + + - name: Checkout + uses: actions/checkout@v4 + + - name: Set additional expect flags if necessary + run: | + # Add a debug flag to expect, if github is re-run with debug logging enabled. + [ "${{ runner.debug }}" = "1" ] && EXPECT_FLAGS="-d" || EXPECT_FLAGS="" + echo "EXPECT_FLAGS=${EXPECT_FLAGS}" >> $GITHUB_ENV + + - name: Check the first run of the script + run: | + expect $EXPECT_FLAGS -c ' + set timeout 240 + + spawn ${{ matrix.shell }} scripts/getting-started.sh + + expect_after { + timeout { puts stderr "Timed out on an expect"; exit 1 } + eof { puts stderr "EOF received on an expect"; exit 1 } + } + + expect -nocase "Detected ${{ matrix.name }}" + + expect "Rust is not installed. Install it?" { + send "y\r" + expect "Proceed with standard installation (default - just press enter)" { + send "\r" + expect "Rust is installed now" + } + } + + expect "Setup the Rust environment" { + send "y\r" + } + + expect "start with one of the templates" { + send "y\r" + } + + expect -re "(.)\\) ${{ matrix.template }} template" { + send "$expect_out(1,string)\r" + } + + expect "compile the node?" { + send "n\r" + } + + expect eof + ' + timeout-minutes: 15 + + - name: Check the second run of the script + run: | + expect $EXPECT_FLAGS -c ' + set timeout 120 + + spawn ${{ matrix.shell }} scripts/getting-started.sh + + expect_after { + timeout { puts stderr "Timed out on an expect"; exit 1 } + eof { puts stderr "EOF received on an expect"; exit 1 } + } + + expect "Rust already installed" {} + + expect "Setup the Rust environment" { + send "n\r" + } + + expect "start with one of the templates" { + send "y\r" + } + + expect -re "(.)\\) ${{ matrix.template }} template" { + send "$expect_out(1,string)\r" + expect "directory already exists" {} + } + + expect "compile the node?" { + send "n\r" + } + + expect eof + ' + timeout-minutes: 15 + + - name: Compile the node outside of the script + run: | + . "$HOME/.cargo/env" + cd ${{ matrix.template }}-template + cargo build --release + timeout-minutes: 120 + + - name: Check that the binary is executable + run: | + . "$HOME/.cargo/env" + cd ${{ matrix.template }}-template + cargo run --release -- --help + timeout-minutes: 5 + + check-getting-started-macos: + strategy: + fail-fast: true + matrix: + include: + - template: parachain + shell: sh + - template: solochain + shell: bash + runs-on: macos-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set additional expect flags if necessary + run: | + # Add a debug flag to expect, if github is re-run with debug logging enabled. + [ "${{ runner.debug }}" = "1" ] && EXPECT_FLAGS="-d" || EXPECT_FLAGS="" + echo "EXPECT_FLAGS=${EXPECT_FLAGS}" >> $GITHUB_ENV + + - name: Check the first run of the script + run: | + expect $EXPECT_FLAGS -c ' + set timeout 120 + + spawn ${{ matrix.shell }} scripts/getting-started.sh + + expect_after { + timeout { puts stderr "Timed out on an expect"; exit 1 } + eof { puts stderr "EOF received on an expect"; exit 1 } + } + + expect -nocase "Detected macOS" + + expect "Homebrew already installed" + + expect "Install cmake" { + send "y\r" + } + + expect "Rust already installed" {} + + expect "Setup the Rust environment" { + send "y\r" + } + + expect "start with one of the templates" { + send "y\r" + } + + expect -re "(.)\\) ${{ matrix.template }} template" { + send "$expect_out(1,string)\r" + } + + expect "compile the node?" { + send "n\r" + } + + expect eof + ' + timeout-minutes: 15 + + - name: Check the second run of the script + run: | + expect $EXPECT_FLAGS -c ' + set timeout 120 + + spawn ${{ matrix.shell }} scripts/getting-started.sh + + expect_after { + timeout { puts stderr "Timed out on an expect"; exit 1 } + eof { puts stderr "EOF received on an expect"; exit 1 } + } + + expect "Homebrew already installed" + + expect "Install cmake" { + send "y\r" + } + + expect "Rust already installed" {} + + expect "Setup the Rust environment" { + send "n\r" + } + + expect "start with one of the templates" { + send "y\r" + } + + expect -re "(.)\\) ${{ matrix.template }} template" { + send "$expect_out(1,string)\r" + expect "directory already exists" {} + } + + expect "compile the node?" { + send "n\r" + } + + expect eof + ' + timeout-minutes: 15 + + - name: Compile the node outside of the script + run: | + . "$HOME/.cargo/env" + cd ${{ matrix.template }}-template + cargo build --release + timeout-minutes: 120 + + - name: Check that the binary is executable + run: | + . "$HOME/.cargo/env" + cd ${{ matrix.template }}-template + cargo run --release -- --help + timeout-minutes: 5 diff --git a/.github/workflows/check-labels.yml b/.github/workflows/check-labels.yml index 1d1a8770058d33ba5e449c6a2e8b307e0ff02eb7..6ec35840608c81a3b1ffb70dbf8684cfce3a489b 100644 --- a/.github/workflows/check-labels.yml +++ b/.github/workflows/check-labels.yml @@ -1,5 +1,9 @@ name: Check labels +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + on: pull_request: types: [labeled, opened, synchronize, unlabeled] diff --git a/.github/workflows/check-licenses.yml b/.github/workflows/check-licenses.yml index a74986048976ecc0974285da3f14a9f8ee2b683d..8bd87118201ac87f34ae8c948970b5fce609741c 100644 --- a/.github/workflows/check-licenses.yml +++ b/.github/workflows/check-licenses.yml @@ -4,6 +4,10 @@ on: pull_request: merge_group: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + permissions: packages: read @@ -16,8 +20,8 @@ jobs: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - name: Checkout sources - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: actions/setup-node@v4.0.3 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 + - uses: actions/setup-node@v4.0.4 with: node-version: "18.x" registry-url: "https://npm.pkg.github.com" @@ -35,7 +39,6 @@ jobs: shopt -s globstar npx @paritytech/license-scanner scan \ --ensure-licenses ${{ env.LICENSES }} \ - --exclude ./cumulus/parachain-template \ -- ./cumulus/**/*.rs - name: Check the licenses in Substrate @@ -44,3 +47,38 @@ jobs: npx @paritytech/license-scanner scan \ --ensure-licenses ${{ env.LICENSES }} \ -- ./substrate/**/*.rs + + check-product-references: + runs-on: ubuntu-latest + timeout-minutes: 10 + env: + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - name: Checkout sources + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 + - uses: actions/setup-node@v4.0.4 + with: + node-version: "18.x" + registry-url: "https://npm.pkg.github.com" + scope: "@paritytech" + + - name: Check the product references in Polkadot + run: | + shopt -s globstar + npx @paritytech/license-scanner scan \ + --ensure-product 'Polkadot' \ + -- ./polkadot/**/*.rs + + - name: Check the product references in Cumulus + run: | + shopt -s globstar + npx @paritytech/license-scanner scan \ + --ensure-product 'Cumulus' \ + -- ./cumulus/**/*.rs + + - name: Check the product references in Substrate + run: | + shopt -s globstar + npx @paritytech/license-scanner scan \ + --ensure-product 'Substrate' \ + -- ./substrate/**/*.rs diff --git a/.github/workflows/check-links.yml b/.github/workflows/check-links.yml index d10f34e6faef4012b6348b20400f5c304f317df4..0ebd33d417af740780f4e91b9d5388a6b5ca6f0e 100644 --- a/.github/workflows/check-links.yml +++ b/.github/workflows/check-links.yml @@ -10,6 +10,10 @@ on: types: [opened, synchronize, reopened, ready_for_review] merge_group: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + permissions: packages: read @@ -25,7 +29,7 @@ jobs: # This should restore from the most recent one: restore-keys: cache-lychee- - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.0 (22. Sep 2023) + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.0 (22. Sep 2023) - name: Lychee link checker uses: lycheeverse/lychee-action@2b973e86fc7b1f6b36a93795fe2c9c6ae1118621 # for v1.9.1 (10. Jan 2024) diff --git a/.github/workflows/check-prdoc.yml b/.github/workflows/check-prdoc.yml index 69311c41dd6f11800b7a0c23e9f7ab8416e80178..fc2824770225e6f9e78a68e51edfb6bcceae6e25 100644 --- a/.github/workflows/check-prdoc.yml +++ b/.github/workflows/check-prdoc.yml @@ -1,12 +1,16 @@ name: Check PRdoc +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + on: pull_request: types: [labeled, opened, synchronize, unlabeled] merge_group: env: - IMAGE: docker.io/paritytech/prdoc:v0.0.8 + IMAGE: docker.io/paritytech/prdoc:v0.1.1 API_BASE: https://api.github.com/repos REPO: ${{ github.repository }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -20,7 +24,7 @@ jobs: if: github.event.pull_request.number != '' steps: - name: Checkout repo - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc #v4.1.7 # we cannot show the version in this step (ie before checking out the repo) # due to https://github.com/paritytech/prdoc/issues/15 - name: Check if PRdoc is required diff --git a/.github/workflows/check-runtime-migration.yml b/.github/workflows/check-runtime-migration.yml index b93a07bf61f1152e05df493fa9dc6d8b3f675a68..39a1c4d03486f0da20712f270775a39c66a571e7 100644 --- a/.github/workflows/check-runtime-migration.yml +++ b/.github/workflows/check-runtime-migration.yml @@ -17,38 +17,19 @@ concurrency: cancel-in-progress: true jobs: - set-image: - # GitHub Actions allows using 'env' in a container context. - # However, env variables don't work for forks: https://github.com/orgs/community/discussions/44322 - # This workaround sets the container image for each job using 'set-image' job output. - runs-on: ubuntu-latest - outputs: - IMAGE: ${{ steps.set_image.outputs.IMAGE }} - RUNNER: ${{ steps.set_runner.outputs.RUNNER }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - id: set_image - run: cat .github/env >> $GITHUB_OUTPUT - # By default we use spot machines that can be terminated at any time. - # Merge queues use persistent runners to avoid kicking off from queue when the runner is terminated. - - id: set_runner - run: | - # Run merge queues on persistent runners - if [[ $GITHUB_REF_NAME == *"gh-readonly-queue"* ]]; then - echo "RUNNER=arc-runners-polkadot-sdk-beefy-persistent" >> $GITHUB_OUTPUT - else - echo "RUNNER=arc-runners-polkadot-sdk-beefy" >> $GITHUB_OUTPUT - fi + preflight: + uses: ./.github/workflows/reusable-preflight.yml + # More info can be found here: https://github.com/paritytech/polkadot/pull/5865 check-runtime-migration: - runs-on: ${{ needs.set-image.outputs.RUNNER }} + runs-on: ${{ needs.preflight.outputs.RUNNER }} + if: ${{ needs.preflight.outputs.changes_rust }} # We need to set this to rather long to allow the snapshot to be created, but the average time # should be much lower. timeout-minutes: 60 - needs: [set-image] + needs: [preflight] container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} strategy: fail-fast: false matrix: @@ -69,54 +50,59 @@ jobs: package: westend-runtime wasm: westend_runtime.compact.compressed.wasm uri: "wss://try-runtime-westend.polkadot.io:443" - subcommand_extra_args: "--no-weight-warnings" + subcommand_extra_args: "--no-weight-warnings --blocktime 6000" command_extra_args: "" - network: rococo package: rococo-runtime wasm: rococo_runtime.compact.compressed.wasm uri: "wss://try-runtime-rococo.polkadot.io:443" - subcommand_extra_args: "--no-weight-warnings" + subcommand_extra_args: "--no-weight-warnings --blocktime 6000" command_extra_args: "" - network: asset-hub-westend package: asset-hub-westend-runtime wasm: asset_hub_westend_runtime.compact.compressed.wasm uri: "wss://westend-asset-hub-rpc.polkadot.io:443" - subcommand_extra_args: "" + subcommand_extra_args: " --blocktime 6000" command_extra_args: "" - network: "asset-hub-rococo" package: "asset-hub-rococo-runtime" wasm: "asset_hub_rococo_runtime.compact.compressed.wasm" uri: "wss://rococo-asset-hub-rpc.polkadot.io:443" - subcommand_extra_args: "" + subcommand_extra_args: " --blocktime 6000" command_extra_args: "" - network: "bridge-hub-westend" package: "bridge-hub-westend-runtime" wasm: "bridge_hub_westend_runtime.compact.compressed.wasm" uri: "wss://westend-bridge-hub-rpc.polkadot.io:443" + subcommand_extra_args: " --blocktime 6000" - network: "bridge-hub-rococo" package: "bridge-hub-rococo-runtime" wasm: "bridge_hub_rococo_runtime.compact.compressed.wasm" uri: "wss://rococo-bridge-hub-rpc.polkadot.io:443" + subcommand_extra_args: " --blocktime 6000" - network: "contracts-rococo" package: "contracts-rococo-runtime" wasm: "contracts_rococo_runtime.compact.compressed.wasm" uri: "wss://rococo-contracts-rpc.polkadot.io:443" + subcommand_extra_args: " --blocktime 6000" - network: "collectives-westend" package: "collectives-westend-runtime" wasm: "collectives_westend_runtime.compact.compressed.wasm" uri: "wss://westend-collectives-rpc.polkadot.io:443" command_extra_args: "--disable-spec-name-check" + subcommand_extra_args: " --blocktime 6000" - network: "coretime-rococo" package: "coretime-rococo-runtime" wasm: "coretime_rococo_runtime.compact.compressed.wasm" uri: "wss://rococo-coretime-rpc.polkadot.io:443" + subcommand_extra_args: " --blocktime 6000" steps: - name: Checkout uses: actions/checkout@v4 - name: Download CLI run: | - curl -sL https://github.com/paritytech/try-runtime-cli/releases/download/v0.7.0/try-runtime-x86_64-unknown-linux-musl -o try-runtime + curl -sL https://github.com/paritytech/try-runtime-cli/releases/download/v0.8.0/try-runtime-x86_64-unknown-linux-musl -o try-runtime chmod +x ./try-runtime echo "Using try-runtime-cli version:" ./try-runtime --version @@ -162,5 +148,14 @@ jobs: name: All runtime migrations passed # If any new job gets added, be sure to add it to this array needs: [check-runtime-migration] + if: always() && !cancelled() steps: - - run: echo '### Good job! All the checks passed ๐Ÿš€' >> $GITHUB_STEP_SUMMARY + - run: | + tee resultfile <<< '${{ toJSON(needs) }}' + FAILURES=$(cat resultfile | grep '"result": "failure"' | wc -l) + if [ $FAILURES -gt 0 ]; then + echo "### At least one required job failed โŒ" >> $GITHUB_STEP_SUMMARY + exit 1 + else + echo '### Good job! All the required jobs passed ๐Ÿš€' >> $GITHUB_STEP_SUMMARY + fi diff --git a/.github/workflows/check-semver.yml b/.github/workflows/check-semver.yml index 15eb32f4062cfaa0a18125117c8015f0e57f05bb..811ec4d5558f09956b38230a3ba1934bd33a496e 100644 --- a/.github/workflows/check-semver.yml +++ b/.github/workflows/check-semver.yml @@ -13,12 +13,15 @@ env: TOOLCHAIN: nightly-2024-06-01 jobs: + preflight: + uses: ./.github/workflows/reusable-preflight.yml check-semver: runs-on: ubuntu-latest + needs: [preflight] container: - image: docker.io/paritytech/ci-unified:bullseye-1.77.0-2024-04-10-v20240408 + image: ${{ needs.preflight.outputs.IMAGE }} steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 with: fetch-depth: 2 @@ -36,10 +39,6 @@ jobs: run: | echo "This is a backport into stable." - wget -q https://github.com/cli/cli/releases/download/v2.51.0/gh_2.51.0_linux_amd64.tar.gz -O gh.tar.gz && \ - tar -xzf gh.tar.gz && mv gh_2.51.0_linux_amd64/bin/gh /usr/local/bin/gh && rm gh.tar.gz - chmod +x /usr/local/bin/gh - cat > msg.txt <> $GITHUB_OUTPUT + + preflight: + uses: ./.github/workflows/reusable-preflight.yml + fmt: runs-on: ubuntu-latest timeout-minutes: 20 - needs: [set-image] + needs: [preflight] container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: Cargo fmt run: cargo +nightly fmt --all -- --check check-dependency-rules: runs-on: ubuntu-latest timeout-minutes: 20 steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: check dependency rules run: | cd substrate/ @@ -50,11 +41,11 @@ jobs: check-rust-feature-propagation: runs-on: ubuntu-latest timeout-minutes: 20 - needs: [set-image] + needs: [preflight] container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: fetch deps run: | # Pull all dependencies eagerly: @@ -66,21 +57,21 @@ jobs: test-rust-features: runs-on: ubuntu-latest timeout-minutes: 20 - needs: [set-image] + needs: [preflight] container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: run rust features run: bash .gitlab/rust-features.sh . check-toml-format: runs-on: ubuntu-latest timeout-minutes: 20 - needs: [set-image] + needs: [preflight] container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: check toml format run: | taplo format --check --config .config/taplo.toml @@ -89,7 +80,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 20 steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.0 (22. Sep 2023) + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.0 (22. Sep 2023) - name: install python deps run: | sudo apt-get update && sudo apt-get install -y python3-pip python3 @@ -109,9 +100,9 @@ jobs: timeout-minutes: 20 steps: - name: Checkout sources - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: Setup Node.js - uses: actions/setup-node@v4.0.3 + uses: actions/setup-node@v4.0.4 with: node-version: "18.x" registry-url: "https://npm.pkg.github.com" @@ -130,11 +121,11 @@ jobs: check-umbrella: runs-on: ubuntu-latest timeout-minutes: 20 - needs: [set-image] + needs: [preflight] container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.0 (22. Sep 2023) + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.0 (22. Sep 2023) - name: install python deps run: pip3 install "cargo-workspace>=1.2.4" toml - name: check umbrella correctness @@ -181,3 +172,29 @@ jobs: env: ASSERT_REGEX: "FAIL-CI" GIT_DEPTH: 1 + + confirm-required-checks-quick-jobs-passed: + runs-on: ubuntu-latest + name: All quick checks passed + # If any new job gets added, be sure to add it to this array + needs: + - fmt + - check-dependency-rules + - check-rust-feature-propagation + - test-rust-features + - check-toml-format + - check-workspace + - check-markdown + - check-umbrella + - check-fail-ci + if: always() && !cancelled() + steps: + - run: | + tee resultfile <<< '${{ toJSON(needs) }}' + FAILURES=$(cat resultfile | grep '"result": "failure"' | wc -l) + if [ $FAILURES -gt 0 ]; then + echo "### At least one required job failed โŒ" >> $GITHUB_STEP_SUMMARY + exit 1 + else + echo '### Good job! All the required jobs passed ๐Ÿš€' >> $GITHUB_STEP_SUMMARY + fi diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index ad9d0d1a959d6911fcafeb51b55f08a8dc200fc8..0793c31dbb87c01f95abd93f624453b03b9128dd 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -15,78 +15,53 @@ concurrency: permissions: {} jobs: - # temporary disabled because currently doesn't work in merge queue - # changes: - # permissions: - # pull-requests: read - # uses: ./.github/workflows/reusable-check-changed-files.yml - set-image: - # GitHub Actions allows using 'env' in a container context. - # However, env variables don't work for forks: https://github.com/orgs/community/discussions/44322 - # This workaround sets the container image for each job using 'set-image' job output. - runs-on: ubuntu-latest - outputs: - IMAGE: ${{ steps.set_image.outputs.IMAGE }} - RUNNER: ${{ steps.set_runner.outputs.RUNNER }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - id: set_image - run: cat .github/env >> $GITHUB_OUTPUT - # By default we use spot machines that can be terminated at any time. - # Merge queues use persistent runners to avoid kicking off from queue when the runner is terminated. - - id: set_runner - run: | - # Run merge queues on persistent runners - if [[ $GITHUB_REF_NAME == *"gh-readonly-queue"* ]]; then - echo "RUNNER=arc-runners-polkadot-sdk-beefy-persistent" >> $GITHUB_OUTPUT - else - echo "RUNNER=arc-runners-polkadot-sdk-beefy" >> $GITHUB_OUTPUT - fi + preflight: + uses: ./.github/workflows/reusable-preflight.yml + cargo-clippy: - runs-on: ${{ needs.set-image.outputs.RUNNER }} - needs: [set-image] - # if: ${{ needs.changes.outputs.rust }} + runs-on: ${{ needs.preflight.outputs.RUNNER }} + needs: [preflight] + if: ${{ needs.preflight.outputs.changes_rust }} timeout-minutes: 40 container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} env: RUSTFLAGS: "-D warnings" SKIP_WASM_BUILD: 1 steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: script run: | - forklift cargo clippy --all-targets --locked --workspace - forklift cargo clippy --all-targets --all-features --locked --workspace + forklift cargo clippy --all-targets --locked --workspace --quiet + forklift cargo clippy --all-targets --all-features --locked --workspace --quiet check-try-runtime: - runs-on: ${{ needs.set-image.outputs.RUNNER }} - needs: [set-image] - # if: ${{ needs.changes.outputs.rust }} + runs-on: ${{ needs.preflight.outputs.RUNNER }} + needs: [preflight] + if: ${{ needs.preflight.outputs.changes_rust }} timeout-minutes: 40 container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: script run: | - forklift cargo check --locked --all --features try-runtime + forklift cargo check --locked --all --features try-runtime --quiet # this is taken from cumulus # Check that parachain-template will compile with `try-runtime` feature flag. forklift cargo check --locked -p parachain-template-node --features try-runtime # add after https://github.com/paritytech/substrate/pull/14502 is merged # experimental code may rely on try-runtime and vice-versa - forklift cargo check --locked --all --features try-runtime,experimental + forklift cargo check --locked --all --features try-runtime,experimental --quiet # check-core-crypto-features works fast without forklift check-core-crypto-features: - runs-on: ${{ needs.set-image.outputs.RUNNER }} - needs: [set-image] - # if: ${{ needs.changes.outputs.rust }} + runs-on: ${{ needs.preflight.outputs.RUNNER }} + needs: [preflight] + if: ${{ needs.preflight.outputs.changes_rust }} timeout-minutes: 30 container: - image: ${{ needs.set-image.outputs.IMAGE }} + image: ${{ needs.preflight.outputs.IMAGE }} steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@6d193bf28034eafb982f37bd894289fe649468fc # v4.1.7 - name: script run: | cd substrate/primitives/core @@ -105,5 +80,14 @@ jobs: name: All checks passed # If any new job gets added, be sure to add it to this array needs: [cargo-clippy, check-try-runtime, check-core-crypto-features] + if: always() && !cancelled() steps: - - run: echo '### Good job! All the checks passed ๐Ÿš€' >> $GITHUB_STEP_SUMMARY + - run: | + tee resultfile <<< '${{ toJSON(needs) }}' + FAILURES=$(cat resultfile | grep '"result": "failure"' | wc -l) + if [ $FAILURES -gt 0 ]; then + echo "### At least one required job failed โŒ" >> $GITHUB_STEP_SUMMARY + exit 1 + else + echo '### Good job! All the required jobs passed ๐Ÿš€' >> $GITHUB_STEP_SUMMARY + fi diff --git a/.github/workflows/cmd-tests.yml b/.github/workflows/cmd-tests.yml new file mode 100644 index 0000000000000000000000000000000000000000..af73c6a5b2d32a19e7b64de4935f71988fbf8a1f --- /dev/null +++ b/.github/workflows/cmd-tests.yml @@ -0,0 +1,18 @@ +name: Command Bot Tests + +on: + pull_request: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + test-cmd-bot: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: python3 .github/scripts/cmd/test_cmd.py diff --git a/.github/workflows/cmd.yml b/.github/workflows/cmd.yml index dac46cf435a670942e7d54eff0cb6e8b1170e8a8..cbe39bc4e3efba5e1593e2164e5a86cb10079c29 100644 --- a/.github/workflows/cmd.yml +++ b/.github/workflows/cmd.yml @@ -2,7 +2,7 @@ name: Command on: issue_comment: # listen for comments on issues - types: [ created ] + types: [created] permissions: # allow the action to comment on the PR contents: write @@ -35,14 +35,14 @@ jobs: try { const org = '${{ github.event.repository.owner.login }}'; const username = '${{ github.event.comment.user.login }}'; - + const membership = await github.rest.orgs.checkMembershipForUser({ org: org, username: username }); - + console.log(membership, membership.status, membership.status === 204); - + if (membership.status === 204) { return 'true'; } else { @@ -52,7 +52,7 @@ jobs: } catch (error) { console.log(error) } - + return 'false'; reject-non-members: @@ -140,7 +140,7 @@ jobs: } }) help: - needs: [ clean, is-org-member ] + needs: [clean, is-org-member] if: ${{ startsWith(github.event.comment.body, '/cmd') && contains(github.event.comment.body, '--help') && needs.is-org-member.outputs.member == 'true' }} runs-on: ubuntu-latest steps: @@ -152,13 +152,14 @@ jobs: id: get-pr-comment with: text: ${{ github.event.comment.body }} - regex: '^(\/cmd )([-\/\s\w.=:]+)$' # see explanation in docs/contributor/commands-readme.md#examples + regex: "^(\\/cmd )([-\\/\\s\\w.=:]+)$" # see explanation in docs/contributor/commands-readme.md#examples - name: Save output of help id: help env: CMD: ${{ steps.get-pr-comment.outputs.group2 }} # to avoid "" around the command run: | + python3 -m pip install -r .github/scripts/generate-prdoc.requirements.txt echo 'help<> $GITHUB_OUTPUT python3 .github/scripts/cmd/cmd.py $CMD >> $GITHUB_OUTPUT echo 'EOF' >> $GITHUB_OUTPUT @@ -173,11 +174,11 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, body: `
Command help: - + \`\`\` ${{ steps.help.outputs.help }} \`\`\` - +
` }) @@ -208,7 +209,7 @@ jobs: }) set-image: - needs: [ clean, is-org-member ] + needs: [clean, is-org-member] if: ${{ startsWith(github.event.comment.body, '/cmd') && !contains(github.event.comment.body, '--help') && needs.is-org-member.outputs.member == 'true' }} runs-on: ubuntu-latest outputs: @@ -222,26 +223,67 @@ jobs: run: | BODY=$(echo "${{ github.event.comment.body }}" | xargs) IMAGE_OVERRIDE=$(echo $BODY | grep -oe 'docker.io/paritytech/ci-unified:.*\s' | xargs) - + cat .github/env >> $GITHUB_OUTPUT - + if [ -n "$IMAGE_OVERRIDE" ]; then echo "IMAGE=$IMAGE_OVERRIDE" >> $GITHUB_OUTPUT fi - + if [[ $BODY == "/cmd bench"* ]]; then - echo "RUNNER=arc-runners-polkadot-sdk-benchmark" >> $GITHUB_OUTPUT + echo "RUNNER=parity-weights" >> $GITHUB_OUTPUT elif [[ $BODY == "/cmd update-ui"* ]]; then - echo "RUNNER=arc-runners-polkadot-sdk-beefy" >> $GITHUB_OUTPUT + echo "RUNNER=parity-large" >> $GITHUB_OUTPUT else echo "RUNNER=ubuntu-latest" >> $GITHUB_OUTPUT fi + # Get PR branch name, because the issue_comment event does not contain the PR branch name + get-pr-branch: + needs: [set-image] + runs-on: ubuntu-latest + outputs: + pr-branch: ${{ steps.get-pr.outputs.pr_branch }} + repo: ${{ steps.get-pr.outputs.repo }} + steps: + - name: Check if the issue is a PR + id: check-pr + run: | + if [ -n "${{ github.event.issue.pull_request.url }}" ]; then + echo "This is a pull request comment" + else + echo "This is not a pull request comment" + exit 1 + fi + + - name: Get PR Branch Name and Repo + if: steps.check-pr.outcome == 'success' + id: get-pr + uses: actions/github-script@v7 + with: + script: | + const pr = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number, + }); + const prBranch = pr.data.head.ref; + const repo = pr.data.head.repo.full_name; + console.log(prBranch, repo) + core.setOutput('pr_branch', prBranch); + core.setOutput('repo', repo); + + - name: Use PR Branch Name and Repo + run: | + echo "The PR branch is ${{ steps.get-pr.outputs.pr_branch }}" + echo "The repository is ${{ steps.get-pr.outputs.repo }}" + cmd: - needs: [ set-image ] + needs: [set-image, get-pr-branch] env: - JOB_NAME: 'cmd' + JOB_NAME: "cmd" runs-on: ${{ needs.set-image.outputs.RUNNER }} + timeout-minutes: 1800 # 30 hours as it could take a long time to run all the runtimes/pallets container: image: ${{ needs.set-image.outputs.IMAGE }} steps: @@ -250,7 +292,18 @@ jobs: id: get-pr-comment with: text: ${{ github.event.comment.body }} - regex: '^(\/cmd )([-\/\s\w.=:]+)$' # see explanation in docs/contributor/commands-readme.md#examples + regex: "^(\\/cmd )([-\\/\\s\\w.=:]+)$" # see explanation in docs/contributor/commands-readme.md#examples + + # In order to run prdoc without specifying the PR number, we need to add the PR number as an argument automatically + - name: Prepare PR Number argument + id: pr-arg + run: | + CMD="${{ steps.get-pr-comment.outputs.group2 }}" + if echo "$CMD" | grep -q "prdoc" && ! echo "$CMD" | grep -qE "\-\-pr[[:space:]=][0-9]+"; then + echo "arg=--pr ${{ github.event.issue.number }}" >> $GITHUB_OUTPUT + else + echo "arg=" >> $GITHUB_OUTPUT + fi - name: Build workflow link if: ${{ !contains(github.event.comment.body, '--quiet') }} @@ -261,26 +314,26 @@ jobs: -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ -H "Accept: application/vnd.github.v3+json" \ https://api.github.com/repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/jobs | jq '.jobs[] | select(.name | contains("${{ env.JOB_NAME }}")) | .html_url') - + runLink=$(curl -s \ -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ -H "Accept: application/vnd.github.v3+json" \ https://api.github.com/repos/${{ github.repository }}/actions/runs/${{ github.run_id }} | jq '.html_url') - + echo "job_url=${jobLink}" echo "run_url=${runLink}" echo "job_url=$jobLink" >> $GITHUB_OUTPUT echo "run_url=$runLink" >> $GITHUB_OUTPUT - - name: Comment PR (Start) - if: ${{ !contains(github.event.comment.body, '--quiet') }} + # No need to comment on prdoc start or if --quiet + if: ${{ !contains(github.event.comment.body, '--quiet') && !contains(github.event.comment.body, 'prdoc') }} uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | let job_url = ${{ steps.build-link.outputs.job_url }} - + github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, @@ -291,23 +344,28 @@ jobs: - name: Checkout uses: actions/checkout@v4 with: - ref: ${{ github.head_ref }} + repository: ${{ needs.get-pr-branch.outputs.repo }} + ref: ${{ needs.get-pr-branch.outputs.pr-branch }} - name: Install dependencies for bench if: startsWith(steps.get-pr-comment.outputs.group2, 'bench') - run: cargo install subweight frame-omni-bencher --locked + run: | + cargo install subweight --locked + cargo install --path substrate/utils/frame/omni-bencher --locked - name: Run cmd id: cmd env: CMD: ${{ steps.get-pr-comment.outputs.group2 }} # to avoid "" around the command + PR_ARG: ${{ steps.pr-arg.outputs.arg }} run: | - echo "Running command: '$CMD' on '${{ needs.set-image.outputs.RUNNER }}' runner, container: '${{ needs.set-image.outputs.IMAGE }}'" + echo "Running command: '$CMD $PR_ARG' on '${{ needs.set-image.outputs.RUNNER }}' runner, container: '${{ needs.set-image.outputs.IMAGE }}'" echo "RUST_NIGHTLY_VERSION: $RUST_NIGHTLY_VERSION" # Fixes "detected dubious ownership" error in the ci git config --global --add safe.directory '*' git remote -v - python3 .github/scripts/cmd/cmd.py $CMD + python3 -m pip install -r .github/scripts/generate-prdoc.requirements.txt + python3 .github/scripts/cmd/cmd.py $CMD $PR_ARG git status git diff @@ -316,12 +374,14 @@ jobs: if [ -n "$(git status --porcelain)" ]; then git config --local user.email "action@github.com" git config --local user.name "GitHub Action" - - git pull origin ${{ github.head_ref }} + git add . git restore --staged Cargo.lock # ignore changes in Cargo.lock git commit -m "Update from ${{ github.actor }} running command '${{ steps.get-pr-comment.outputs.group2 }}'" || true - git push origin ${{ github.head_ref }} + + git pull --rebase origin ${{ needs.get-pr-branch.outputs.pr-branch }} + + git push origin ${{ needs.get-pr-branch.outputs.pr-branch }} else echo "Nothing to commit"; fi @@ -339,8 +399,8 @@ jobs: --no-color \ --change added changed \ --ignore-errors \ - refs/remotes/origin/master ${{ github.ref }}) - + refs/remotes/origin/master refs/heads/${{ needs.get-pr-branch.outputs.pr-branch }}) + # Save the multiline result to the output { echo "result<> $GITHUB_OUTPUT - name: Comment PR (End) - if: ${{ !failure() && !contains(github.event.comment.body, '--quiet') }} + # No need to comment on prdoc success or --quiet + if: ${{ !failure() && !contains(github.event.comment.body, '--quiet') && !contains(github.event.comment.body, 'prdoc') }} uses: actions/github-script@v7 env: - SUBWEIGHT: '${{ steps.subweight.outputs.result }}' + SUBWEIGHT: "${{ steps.subweight.outputs.result }}" with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | let runUrl = ${{ steps.build-link.outputs.run_url }} let subweight = process.env.SUBWEIGHT; - + let subweightCollapsed = subweight ? `
\n\nSubweight results:\n\n${subweight}\n\n
` : ''; - + github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, diff --git a/.github/workflows/command-backport.yml b/.github/workflows/command-backport.yml index 72da40f198394a0d651976721338bbc256d5855e..7f1d59bc0f6c02320852878e905f572b6efde3a1 100644 --- a/.github/workflows/command-backport.yml +++ b/.github/workflows/command-backport.yml @@ -9,6 +9,8 @@ on: permissions: contents: write # so it can comment pull-requests: write # so it can create pull requests + issues: write + actions: write # It may have to backport changes to the CI as well. jobs: backport: @@ -27,15 +29,24 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Generate token + id: generate_token + uses: tibdex/github-app-token@v2.1.0 + with: + app_id: ${{ secrets.CMD_BOT_APP_ID }} + private_key: ${{ secrets.CMD_BOT_APP_KEY }} + - name: Create backport pull requests uses: korthout/backport-action@v3 id: backport with: - target_branches: stable2407 + target_branches: stable2407 stable2409 merge_commits: skip - github_token: ${{ secrets.GITHUB_TOKEN }} + github_token: ${{ steps.generate_token.outputs.token }} pull_description: | - Backport #${pull_number} into `${target_branch}` (cc @${pull_author}). + Backport #${pull_number} into `${target_branch}` from ${pull_author}. + + See the [documentation](https://github.com/paritytech/polkadot-sdk/blob/master/docs/BACKPORT.md) on how to use this bot. +![Current Stable Release](https://raw.githubusercontent.com/paritytech/release-registry/main/badges/polkadot-sdk-latest.svg)  ![Next Stable Release](https://raw.githubusercontent.com/paritytech/release-registry/main/badges/polkadot-sdk-next.svg) -### ๐Ÿค  Nightly +The Polkadot SDK is released every three months as a `stableYYMMDD` release. They are supported for +one year with patches. See the next upcoming versions in the [Release +Registry](https://github.com/paritytech/release-registry/). -`nightly` releases are released every night from the `master` branch, potentially with breaking -changes. They have pre-release version numbers in the format `major.0.0-nightlyYYMMDD`. +You can use [`psvm`](https://github.com/paritytech/psvm) to update all dependencies to a specific +version without needing to manually select the correct version for each crate. ## ๐Ÿ› ๏ธ Tooling diff --git a/bridges/bin/runtime-common/Cargo.toml b/bridges/bin/runtime-common/Cargo.toml index 36f27b6aa0358fcb8027bbfe6e571bc1a50962e6..b8835d55f0da2ad9a88a94f10539f4312950600a 100644 --- a/bridges/bin/runtime-common/Cargo.toml +++ b/bridges/bin/runtime-common/Cargo.toml @@ -25,7 +25,6 @@ bp-polkadot-core = { workspace = true } bp-relayers = { workspace = true } bp-runtime = { workspace = true } bp-xcm-bridge-hub = { workspace = true } -bp-xcm-bridge-hub-router = { workspace = true } pallet-bridge-grandpa = { workspace = true } pallet-bridge-messages = { workspace = true } pallet-bridge-parachains = { workspace = true } @@ -43,12 +42,15 @@ sp-trie = { optional = true, workspace = true } # Polkadot dependencies xcm = { workspace = true } -xcm-builder = { workspace = true } [dev-dependencies] bp-test-utils = { workspace = true } pallet-balances = { workspace = true } -pallet-bridge-messages = { features = ["std", "test-helpers"], workspace = true } +pallet-bridge-messages = { features = [ + "std", + "test-helpers", +], workspace = true } +sp-core = { workspace = true } [features] default = ["std"] @@ -60,7 +62,6 @@ std = [ "bp-relayers/std", "bp-runtime/std", "bp-test-utils/std", - "bp-xcm-bridge-hub-router/std", "bp-xcm-bridge-hub/std", "codec/std", "frame-support/std", @@ -74,12 +75,12 @@ std = [ "pallet-transaction-payment/std", "pallet-utility/std", "scale-info/std", + "sp-core/std", "sp-io/std", "sp-runtime/std", "sp-std/std", "sp-trie/std", "tuplex/std", - "xcm-builder/std", "xcm/std", ] runtime-benchmarks = [ @@ -95,10 +96,6 @@ runtime-benchmarks = [ "pallet-utility/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "sp-trie", - "xcm-builder/runtime-benchmarks", ] integrity-test = ["static_assertions"] -test-helpers = [ - "bp-runtime/test-helpers", - "sp-trie", -] +test-helpers = ["bp-runtime/test-helpers", "sp-trie"] diff --git a/bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs b/bridges/bin/runtime-common/src/extensions.rs similarity index 76% rename from bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs rename to bridges/bin/runtime-common/src/extensions.rs index df75092af6e8b89fb76003cf0bfff508214ea825..dced502394717411654d270b335d57b94feda31a 100644 --- a/bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs +++ b/bridges/bin/runtime-common/src/extensions.rs @@ -18,23 +18,20 @@ //! obsolete (duplicated) data or do not pass some additional pallet-specific //! checks. -use crate::{ - extensions::refund_relayer_extension::RefundableParachainId, - messages_call_ext::MessagesCallSubType, -}; +use bp_parachains::SubmitParachainHeadsInfo; use bp_relayers::ExplicitOrAccountParams; use bp_runtime::Parachain; use pallet_bridge_grandpa::{ BridgedBlockNumber, CallSubType as GrandpaCallSubType, SubmitFinalityProofHelper, }; -use pallet_bridge_parachains::{ - CallSubType as ParachainsCallSubtype, SubmitParachainHeadsHelper, SubmitParachainHeadsInfo, -}; +use pallet_bridge_messages::CallSubType as MessagesCallSubType; +use pallet_bridge_parachains::{CallSubType as ParachainsCallSubtype, SubmitParachainHeadsHelper}; use pallet_bridge_relayers::Pallet as RelayersPallet; use sp_runtime::{ - traits::{Get, PhantomData, UniqueSaturatedInto}, + traits::{Get, UniqueSaturatedInto}, transaction_validity::{TransactionPriority, TransactionValidity, ValidTransactionBuilder}, }; +use sp_std::marker::PhantomData; // Re-export to avoid include tuplex dependency everywhere. #[doc(hidden)] @@ -126,17 +123,27 @@ where /// `(BundledHeaderNumber - 1 - BestKnownHeaderNumber) * Priority::get()`. /// The boost is only applied if submitter has active registration in the relayers /// pallet. -pub struct CheckAndBoostBridgeParachainsTransactions( - PhantomData<(T, RefPara, Priority, SlashAccount)>, -); - -impl, SlashAccount: Get> - BridgeRuntimeFilterCall - for CheckAndBoostBridgeParachainsTransactions +pub struct CheckAndBoostBridgeParachainsTransactions< + T, + ParachainsInstance, + Para, + Priority, + SlashAccount, +>(PhantomData<(T, ParachainsInstance, Para, Priority, SlashAccount)>); + +impl< + T, + ParachainsInstance, + Para, + Priority: Get, + SlashAccount: Get, + > BridgeRuntimeFilterCall + for CheckAndBoostBridgeParachainsTransactions where - T: pallet_bridge_relayers::Config + pallet_bridge_parachains::Config, - RefPara: RefundableParachainId, - T::RuntimeCall: ParachainsCallSubtype, + T: pallet_bridge_relayers::Config + pallet_bridge_parachains::Config, + ParachainsInstance: 'static, + Para: Parachain, + T::RuntimeCall: ParachainsCallSubtype, { // bridged header number, bundled in transaction type ToPostDispatch = Option; @@ -145,10 +152,10 @@ where who: &T::AccountId, call: &T::RuntimeCall, ) -> (Self::ToPostDispatch, TransactionValidity) { - match ParachainsCallSubtype::::check_obsolete_submit_parachain_heads( + match ParachainsCallSubtype::::check_obsolete_submit_parachain_heads( call, ) { - Ok(Some(our_tx)) if our_tx.base.para_id.0 == RefPara::BridgedChain::PARACHAIN_ID => { + Ok(Some(our_tx)) if our_tx.base.para_id.0 == Para::PARACHAIN_ID => { let to_post_dispatch = Some(our_tx.base); let total_priority_boost = compute_priority_boost::(&who, our_tx.improved_by); @@ -167,7 +174,7 @@ where let Some(update) = maybe_update else { return }; // we are only interested in failed or unneeded transactions let has_failed = has_failed || - !SubmitParachainHeadsHelper::::was_successful(&update); + !SubmitParachainHeadsHelper::::was_successful(&update); if !has_failed { return @@ -275,7 +282,7 @@ macro_rules! generate_bridge_reject_obsolete_headers_and_messages { type Pre = ( $account_id, ( $( - <$filter_call as $crate::extensions::check_obsolete_extension::BridgeRuntimeFilterCall< + <$filter_call as $crate::extensions::BridgeRuntimeFilterCall< $account_id, $call, >>::ToPostDispatch, @@ -302,7 +309,7 @@ macro_rules! generate_bridge_reject_obsolete_headers_and_messages { $( let (from_validate, call_filter_validity) = < $filter_call as - $crate::extensions::check_obsolete_extension::BridgeRuntimeFilterCall< + $crate::extensions::BridgeRuntimeFilterCall< Self::AccountId, $call, >>::validate(&who, call); @@ -319,12 +326,13 @@ macro_rules! generate_bridge_reject_obsolete_headers_and_messages { info: &sp_runtime::traits::DispatchInfoOf, len: usize, ) -> Result { - use $crate::extensions::check_obsolete_extension::__private::tuplex::PushBack; + use $crate::extensions::__private::tuplex::PushBack; + let to_post_dispatch = (); $( let (from_validate, call_filter_validity) = < $filter_call as - $crate::extensions::check_obsolete_extension::BridgeRuntimeFilterCall< + $crate::extensions::BridgeRuntimeFilterCall< $account_id, $call, >>::validate(&relayer, call); @@ -342,14 +350,15 @@ macro_rules! generate_bridge_reject_obsolete_headers_and_messages { len: usize, result: &sp_runtime::DispatchResult, ) -> Result<(), sp_runtime::transaction_validity::TransactionValidityError> { - use $crate::extensions::check_obsolete_extension::__private::tuplex::PopFront; + use $crate::extensions::__private::tuplex::PopFront; + let Some((relayer, to_post_dispatch)) = to_post_dispatch else { return Ok(()) }; let has_failed = result.is_err(); $( let (item, to_post_dispatch) = to_post_dispatch.pop_front(); < $filter_call as - $crate::extensions::check_obsolete_extension::BridgeRuntimeFilterCall< + $crate::extensions::BridgeRuntimeFilterCall< $account_id, $call, >>::post_dispatch(&relayer, has_failed, item); @@ -363,25 +372,36 @@ macro_rules! generate_bridge_reject_obsolete_headers_and_messages { #[cfg(test)] mod tests { use super::*; - use crate::{ - extensions::refund_relayer_extension::{ - tests::{ - initialize_environment, relayer_account_at_this_chain, - submit_parachain_head_call_ex, submit_relay_header_call_ex, - }, - RefundableParachain, - }, - mock::*, - }; - use bp_polkadot_core::parachains::ParaId; + use crate::mock::*; + use bp_header_chain::StoredHeaderDataBuilder; + use bp_messages::{InboundLaneData, MessageNonce, OutboundLaneData}; + use bp_parachains::{BestParaHeadHash, ParaInfo}; + use bp_polkadot_core::parachains::{ParaHeadsProof, ParaId}; + use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; use bp_runtime::HeaderId; - use frame_support::{assert_err, assert_ok}; + use bp_test_utils::{make_default_justification, test_keyring, TEST_GRANDPA_SET_ID}; + use frame_support::{assert_err, assert_ok, traits::fungible::Mutate}; + use pallet_bridge_grandpa::{Call as GrandpaCall, StoredAuthoritySet}; + use pallet_bridge_parachains::Call as ParachainsCall; use sp_runtime::{ - traits::{ConstU64, SignedExtension}, + traits::{parameter_types, ConstU64, Header as _, SignedExtension}, transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}, DispatchError, }; + parameter_types! { + pub MsgProofsRewardsAccount: RewardsAccountParams = RewardsAccountParams::new( + test_lane_id(), + TEST_BRIDGED_CHAIN_ID, + RewardsAccountOwner::ThisChain, + ); + pub MsgDeliveryProofsRewardsAccount: RewardsAccountParams = RewardsAccountParams::new( + test_lane_id(), + TEST_BRIDGED_CHAIN_ID, + RewardsAccountOwner::BridgedChain, + ); + } + pub struct MockCall { data: u32, } @@ -455,6 +475,99 @@ mod tests { } } + fn initial_balance_of_relayer_account_at_this_chain() -> ThisChainBalance { + let test_stake: ThisChainBalance = TestStake::get(); + ExistentialDeposit::get().saturating_add(test_stake * 100) + } + + // in tests, the following accounts are equal (because of how `into_sub_account_truncating` + // works) + + fn delivery_rewards_account() -> ThisChainAccountId { + TestPaymentProcedure::rewards_account(MsgProofsRewardsAccount::get()) + } + + fn confirmation_rewards_account() -> ThisChainAccountId { + TestPaymentProcedure::rewards_account(MsgDeliveryProofsRewardsAccount::get()) + } + + fn relayer_account_at_this_chain() -> ThisChainAccountId { + 0 + } + + fn initialize_environment( + best_relay_header_number: BridgedChainBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, + best_message: MessageNonce, + ) { + let authorities = test_keyring().into_iter().map(|(a, w)| (a.into(), w)).collect(); + let best_relay_header = HeaderId(best_relay_header_number, BridgedChainHash::default()); + pallet_bridge_grandpa::CurrentAuthoritySet::::put( + StoredAuthoritySet::try_new(authorities, TEST_GRANDPA_SET_ID).unwrap(), + ); + pallet_bridge_grandpa::BestFinalized::::put(best_relay_header); + pallet_bridge_grandpa::ImportedHeaders::::insert( + best_relay_header.hash(), + bp_test_utils::test_header::(0).build(), + ); + + let para_id = ParaId(BridgedUnderlyingParachain::PARACHAIN_ID); + let para_info = ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: parachain_head_at_relay_header_number, + head_hash: [parachain_head_at_relay_header_number as u8; 32].into(), + }, + next_imported_hash_position: 0, + }; + pallet_bridge_parachains::ParasInfo::::insert(para_id, para_info); + + let lane_id = test_lane_id(); + let in_lane_data = + InboundLaneData { last_confirmed_nonce: best_message, ..Default::default() }; + pallet_bridge_messages::InboundLanes::::insert(lane_id, in_lane_data); + + let out_lane_data = + OutboundLaneData { latest_received_nonce: best_message, ..Default::default() }; + pallet_bridge_messages::OutboundLanes::::insert(lane_id, out_lane_data); + + Balances::mint_into(&delivery_rewards_account(), ExistentialDeposit::get()).unwrap(); + Balances::mint_into(&confirmation_rewards_account(), ExistentialDeposit::get()).unwrap(); + Balances::mint_into( + &relayer_account_at_this_chain(), + initial_balance_of_relayer_account_at_this_chain(), + ) + .unwrap(); + } + + fn submit_relay_header_call(relay_header_number: BridgedChainBlockNumber) -> RuntimeCall { + let relay_header = BridgedChainHeader::new( + relay_header_number, + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ); + let relay_justification = make_default_justification(&relay_header); + + RuntimeCall::BridgeGrandpa(GrandpaCall::submit_finality_proof { + finality_target: Box::new(relay_header), + justification: relay_justification, + }) + } + + fn submit_parachain_head_call( + parachain_head_at_relay_header_number: BridgedChainBlockNumber, + ) -> RuntimeCall { + RuntimeCall::BridgeParachains(ParachainsCall::submit_parachain_heads { + at_relay_block: (parachain_head_at_relay_header_number, BridgedChainHash::default()), + parachains: vec![( + ParaId(BridgedUnderlyingParachain::PARACHAIN_ID), + [parachain_head_at_relay_header_number as u8; 32].into(), + )], + parachain_heads_proof: ParaHeadsProof { storage_proof: Default::default() }, + }) + } + #[test] fn test_generated_obsolete_extension() { generate_bridge_reject_obsolete_headers_and_messages!( @@ -546,7 +659,7 @@ mod tests { let priority_boost = BridgeGrandpaWrapper::validate( &relayer_account_at_this_chain(), - &submit_relay_header_call_ex(200), + &submit_relay_header_call(200), ) .1 .unwrap() @@ -564,7 +677,7 @@ mod tests { let priority_boost = BridgeGrandpaWrapper::validate( &relayer_account_at_this_chain(), - &submit_relay_header_call_ex(200), + &submit_relay_header_call(200), ) .1 .unwrap() @@ -601,7 +714,8 @@ mod tests { type BridgeParachainsWrapper = CheckAndBoostBridgeParachainsTransactions< TestRuntime, - RefundableParachain<(), BridgedUnderlyingParachain>, + (), + BridgedUnderlyingParachain, ConstU64<1_000>, SlashDestination, >; @@ -613,7 +727,7 @@ mod tests { let priority_boost = BridgeParachainsWrapper::validate( &relayer_account_at_this_chain(), - &submit_parachain_head_call_ex(200), + &submit_parachain_head_call(200), ) .1 .unwrap() @@ -631,7 +745,7 @@ mod tests { let priority_boost = BridgeParachainsWrapper::validate( &relayer_account_at_this_chain(), - &submit_parachain_head_call_ex(200), + &submit_parachain_head_call(200), ) .1 .unwrap() diff --git a/bridges/bin/runtime-common/src/extensions/mod.rs b/bridges/bin/runtime-common/src/extensions/mod.rs deleted file mode 100644 index 3f1b506aaae3ef66fe6f44379258356a2074464c..0000000000000000000000000000000000000000 --- a/bridges/bin/runtime-common/src/extensions/mod.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Bridge-specific transaction extensions. - -pub mod check_obsolete_extension; -pub mod priority_calculator; -pub mod refund_relayer_extension; diff --git a/bridges/bin/runtime-common/src/integrity.rs b/bridges/bin/runtime-common/src/integrity.rs index a0a9367dd1400309d0f0be967665a4543ad982cb..2ff6c4c9165aade8899bc4d8fea47da43f59ec8b 100644 --- a/bridges/bin/runtime-common/src/integrity.rs +++ b/bridges/bin/runtime-common/src/integrity.rs @@ -68,21 +68,21 @@ macro_rules! assert_bridge_messages_pallet_types( bridged_chain: $bridged:path, ) => { { - use $crate::messages_xcm_extension::XcmAsPlainPayload; + use $crate::integrity::__private::bp_xcm_bridge_hub::XcmAsPlainPayload; use $crate::integrity::__private::static_assertions::assert_type_eq_all; use bp_messages::ChainWithMessages; use bp_runtime::Chain; - use pallet_bridge_messages::Config as MessagesConfig; + use pallet_bridge_messages::Config as BridgeMessagesConfig; // if one of asserts fail, then either bridge isn't configured properly (or alternatively - non-standard // configuration is used), or something has broke existing configuration (meaning that all bridged chains // and relays will stop functioning) - assert_type_eq_all!(<$r as MessagesConfig<$i>>::ThisChain, $this); - assert_type_eq_all!(<$r as MessagesConfig<$i>>::BridgedChain, $bridged); + assert_type_eq_all!(<$r as BridgeMessagesConfig<$i>>::ThisChain, $this); + assert_type_eq_all!(<$r as BridgeMessagesConfig<$i>>::BridgedChain, $bridged); - assert_type_eq_all!(<$r as MessagesConfig<$i>>::OutboundPayload, XcmAsPlainPayload); - assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundPayload, XcmAsPlainPayload); + assert_type_eq_all!(<$r as BridgeMessagesConfig<$i>>::OutboundPayload, XcmAsPlainPayload); + assert_type_eq_all!(<$r as BridgeMessagesConfig<$i>>::InboundPayload, XcmAsPlainPayload); } } ); @@ -174,12 +174,6 @@ where R: pallet_bridge_messages::Config, MI: 'static, { - assert!( - !R::ActiveOutboundLanes::get().is_empty(), - "ActiveOutboundLanes ({:?}) must not be empty", - R::ActiveOutboundLanes::get(), - ); - assert!( pallet_bridge_messages::BridgedChainOf::::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX <= pallet_bridge_messages::BridgedChainOf::::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, diff --git a/bridges/bin/runtime-common/src/lib.rs b/bridges/bin/runtime-common/src/lib.rs index b65bb6041d5610ce2bdfb63f923f3f24b21dcd7f..ac8b013086b1bd63da2ed2e14d40002341be1758 100644 --- a/bridges/bin/runtime-common/src/lib.rs +++ b/bridges/bin/runtime-common/src/lib.rs @@ -20,16 +20,11 @@ #![cfg_attr(not(feature = "std"), no_std)] pub mod extensions; - pub mod messages_api; pub mod messages_benchmarking; -pub mod messages_call_ext; -pub mod messages_xcm_extension; pub mod parachains_benchmarking; mod mock; #[cfg(feature = "integrity-test")] pub mod integrity; - -const LOG_TARGET_BRIDGE_DISPATCH: &str = "runtime::bridge-dispatch"; diff --git a/bridges/bin/runtime-common/src/messages_api.rs b/bridges/bin/runtime-common/src/messages_api.rs index 7fbdeb366124778b36c77725be8ca8778020be1b..c8522d4d1f276aa945682de04edbe032a20e8a93 100644 --- a/bridges/bin/runtime-common/src/messages_api.rs +++ b/bridges/bin/runtime-common/src/messages_api.rs @@ -16,14 +16,12 @@ //! Helpers for implementing various message-related runtime API methods. -use bp_messages::{ - InboundMessageDetails, LaneId, MessageNonce, MessagePayload, OutboundMessageDetails, -}; +use bp_messages::{InboundMessageDetails, MessageNonce, MessagePayload, OutboundMessageDetails}; use sp_std::vec::Vec; /// Implementation of the `To*OutboundLaneApi::message_details`. pub fn outbound_message_details( - lane: LaneId, + lane: Runtime::LaneId, begin: MessageNonce, end: MessageNonce, ) -> Vec @@ -48,7 +46,7 @@ where /// Implementation of the `To*InboundLaneApi::message_details`. pub fn inbound_message_details( - lane: LaneId, + lane: Runtime::LaneId, messages: Vec<(MessagePayload, OutboundMessageDetails)>, ) -> Vec where diff --git a/bridges/bin/runtime-common/src/messages_benchmarking.rs b/bridges/bin/runtime-common/src/messages_benchmarking.rs index 1880e65547fe6d5e0af71e6ec7c6e0e214f20866..acbdbcda8deafcf64430b1cabe0ca9eaf6eddfb1 100644 --- a/bridges/bin/runtime-common/src/messages_benchmarking.rs +++ b/bridges/bin/runtime-common/src/messages_benchmarking.rs @@ -33,15 +33,15 @@ use pallet_bridge_messages::{ encode_all_messages, encode_lane_data, prepare_message_delivery_storage_proof, prepare_messages_storage_proof, }, - BridgedChainOf, ThisChainOf, + BridgedChainOf, LaneIdOf, ThisChainOf, }; use sp_runtime::traits::{Header, Zero}; use sp_std::prelude::*; use xcm::latest::prelude::*; /// Prepare inbound bridge message according to given message proof parameters. -fn prepare_inbound_message( - params: &MessageProofParams, +fn prepare_inbound_message( + params: &MessageProofParams, successful_dispatch_message_generator: impl Fn(usize) -> MessagePayload, ) -> MessagePayload { let expected_size = params.proof_params.db_size.unwrap_or(0) as usize; @@ -71,9 +71,9 @@ fn prepare_inbound_message( /// uses GRANDPA finality. For parachains, please use the `prepare_message_proof_from_parachain` /// function. pub fn prepare_message_proof_from_grandpa_chain( - params: MessageProofParams, + params: MessageProofParams>, message_generator: impl Fn(usize) -> MessagePayload, -) -> (FromBridgedChainMessagesProof>>, Weight) +) -> (FromBridgedChainMessagesProof>, LaneIdOf>, Weight) where R: pallet_bridge_grandpa::Config> + pallet_bridge_messages::Config< @@ -84,18 +84,21 @@ where MI: 'static, { // prepare storage proof - let (state_root, storage_proof) = - prepare_messages_storage_proof::, ThisChainOf>( - params.lane, - params.message_nonces.clone(), - params.outbound_lane_data.clone(), - params.proof_params, - |_| prepare_inbound_message(¶ms, &message_generator), - encode_all_messages, - encode_lane_data, - false, - false, - ); + let (state_root, storage_proof) = prepare_messages_storage_proof::< + BridgedChainOf, + ThisChainOf, + LaneIdOf, + >( + params.lane, + params.message_nonces.clone(), + params.outbound_lane_data.clone(), + params.proof_params, + |_| prepare_inbound_message(¶ms, &message_generator), + encode_all_messages, + encode_lane_data, + false, + false, + ); // update runtime storage let (_, bridged_header_hash) = insert_header_to_grandpa_pallet::(state_root); @@ -121,9 +124,9 @@ where /// uses parachain finality. For GRANDPA chains, please use the /// `prepare_message_proof_from_grandpa_chain` function. pub fn prepare_message_proof_from_parachain( - params: MessageProofParams, + params: MessageProofParams>, message_generator: impl Fn(usize) -> MessagePayload, -) -> (FromBridgedChainMessagesProof>>, Weight) +) -> (FromBridgedChainMessagesProof>, LaneIdOf>, Weight) where R: pallet_bridge_parachains::Config + pallet_bridge_messages::Config, PI: 'static, @@ -131,18 +134,21 @@ where BridgedChainOf: Chain + Parachain, { // prepare storage proof - let (state_root, storage_proof) = - prepare_messages_storage_proof::, ThisChainOf>( - params.lane, - params.message_nonces.clone(), - params.outbound_lane_data.clone(), - params.proof_params, - |_| prepare_inbound_message(¶ms, &message_generator), - encode_all_messages, - encode_lane_data, - false, - false, - ); + let (state_root, storage_proof) = prepare_messages_storage_proof::< + BridgedChainOf, + ThisChainOf, + LaneIdOf, + >( + params.lane, + params.message_nonces.clone(), + params.outbound_lane_data.clone(), + params.proof_params, + |_| prepare_inbound_message(¶ms, &message_generator), + encode_all_messages, + encode_lane_data, + false, + false, + ); // update runtime storage let (_, bridged_header_hash) = @@ -166,8 +172,8 @@ where /// uses GRANDPA finality. For parachains, please use the /// `prepare_message_delivery_proof_from_parachain` function. pub fn prepare_message_delivery_proof_from_grandpa_chain( - params: MessageDeliveryProofParams>>, -) -> FromBridgedChainMessagesDeliveryProof>> + params: MessageDeliveryProofParams>, LaneIdOf>, +) -> FromBridgedChainMessagesDeliveryProof>, LaneIdOf> where R: pallet_bridge_grandpa::Config> + pallet_bridge_messages::Config< @@ -182,6 +188,7 @@ where let (state_root, storage_proof) = prepare_message_delivery_storage_proof::< BridgedChainOf, ThisChainOf, + LaneIdOf, >(params.lane, params.inbound_lane_data, params.proof_params); // update runtime storage @@ -200,8 +207,8 @@ where /// uses parachain finality. For GRANDPA chains, please use the /// `prepare_message_delivery_proof_from_grandpa_chain` function. pub fn prepare_message_delivery_proof_from_parachain( - params: MessageDeliveryProofParams>>, -) -> FromBridgedChainMessagesDeliveryProof>> + params: MessageDeliveryProofParams>, LaneIdOf>, +) -> FromBridgedChainMessagesDeliveryProof>, LaneIdOf> where R: pallet_bridge_parachains::Config + pallet_bridge_messages::Config, PI: 'static, @@ -213,6 +220,7 @@ where let (state_root, storage_proof) = prepare_message_delivery_storage_proof::< BridgedChainOf, ThisChainOf, + LaneIdOf, >(params.lane, params.inbound_lane_data, params.proof_params); // update runtime storage diff --git a/bridges/bin/runtime-common/src/messages_xcm_extension.rs b/bridges/bin/runtime-common/src/messages_xcm_extension.rs deleted file mode 100644 index 46ed4da0d85481fcc7223740084945924f9c710f..0000000000000000000000000000000000000000 --- a/bridges/bin/runtime-common/src/messages_xcm_extension.rs +++ /dev/null @@ -1,502 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Module provides utilities for easier XCM handling, e.g: -//! `XcmExecutor` -> `MessageSender` -> `OutboundMessageQueue` -//! | -//! `Relayer` -//! | -//! `XcmRouter` <- `MessageDispatch` <- `InboundMessageQueue` - -use bp_messages::{ - source_chain::OnMessagesDelivered, - target_chain::{DispatchMessage, MessageDispatch}, - LaneId, MessageNonce, -}; -use bp_runtime::messages::MessageDispatchResult; -pub use bp_xcm_bridge_hub::XcmAsPlainPayload; -use bp_xcm_bridge_hub_router::XcmChannelStatusProvider; -use codec::{Decode, Encode}; -use frame_support::{traits::Get, weights::Weight, CloneNoBound, EqNoBound, PartialEqNoBound}; -use pallet_bridge_messages::{ - Config as MessagesConfig, OutboundLanesCongestedSignals, WeightInfoExt as MessagesPalletWeights, -}; -use scale_info::TypeInfo; -use sp_runtime::SaturatedConversion; -use sp_std::{fmt::Debug, marker::PhantomData}; -use xcm::prelude::*; -use xcm_builder::{DispatchBlob, DispatchBlobError}; - -/// Message dispatch result type for single message. -#[derive(CloneNoBound, EqNoBound, PartialEqNoBound, Encode, Decode, Debug, TypeInfo)] -pub enum XcmBlobMessageDispatchResult { - /// We've been unable to decode message payload. - InvalidPayload, - /// Message has been dispatched. - Dispatched, - /// Message has **NOT** been dispatched because of given error. - NotDispatched(#[codec(skip)] Option), -} - -/// [`XcmBlobMessageDispatch`] is responsible for dispatching received messages -/// -/// It needs to be used at the target bridge hub. -pub struct XcmBlobMessageDispatch { - _marker: sp_std::marker::PhantomData<(DispatchBlob, Weights, Channel)>, -} - -impl< - BlobDispatcher: DispatchBlob, - Weights: MessagesPalletWeights, - Channel: XcmChannelStatusProvider, - > MessageDispatch for XcmBlobMessageDispatch -{ - type DispatchPayload = XcmAsPlainPayload; - type DispatchLevelResult = XcmBlobMessageDispatchResult; - - fn is_active() -> bool { - !Channel::is_congested() - } - - fn dispatch_weight(message: &mut DispatchMessage) -> Weight { - match message.data.payload { - Ok(ref payload) => { - let payload_size = payload.encoded_size().saturated_into(); - Weights::message_dispatch_weight(payload_size) - }, - Err(_) => Weight::zero(), - } - } - - fn dispatch( - message: DispatchMessage, - ) -> MessageDispatchResult { - let payload = match message.data.payload { - Ok(payload) => payload, - Err(e) => { - log::error!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "[XcmBlobMessageDispatch] payload error: {:?} - message_nonce: {:?}", - e, - message.key.nonce - ); - return MessageDispatchResult { - unspent_weight: Weight::zero(), - dispatch_level_result: XcmBlobMessageDispatchResult::InvalidPayload, - } - }, - }; - let dispatch_level_result = match BlobDispatcher::dispatch_blob(payload) { - Ok(_) => { - log::debug!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob was ok - message_nonce: {:?}", - message.key.nonce - ); - XcmBlobMessageDispatchResult::Dispatched - }, - Err(e) => { - log::error!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob failed, error: {:?} - message_nonce: {:?}", - e, message.key.nonce - ); - XcmBlobMessageDispatchResult::NotDispatched(Some(e)) - }, - }; - MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result } - } -} - -/// A pair of sending chain location and message lane, used by this chain to send messages -/// over the bridge. -#[cfg_attr(feature = "std", derive(Debug, Eq, PartialEq))] -pub struct SenderAndLane { - /// Sending chain relative location. - pub location: Location, - /// Message lane, used by the sending chain. - pub lane: LaneId, -} - -impl SenderAndLane { - /// Create new object using provided location and lane. - pub fn new(location: Location, lane: LaneId) -> Self { - SenderAndLane { location, lane } - } -} - -/// [`XcmBlobHauler`] is responsible for sending messages to the bridge "point-to-point link" from -/// one side, where on the other it can be dispatched by [`XcmBlobMessageDispatch`]. -pub trait XcmBlobHauler { - /// Runtime that has messages pallet deployed. - type Runtime: MessagesConfig; - /// Instance of the messages pallet that is used to send messages. - type MessagesInstance: 'static; - - /// Actual XCM message sender (`HRMP` or `UMP`) to the source chain - /// location (`Self::SenderAndLane::get().location`). - type ToSourceChainSender: SendXcm; - /// An XCM message that is sent to the sending chain when the bridge queue becomes congested. - type CongestedMessage: Get>>; - /// An XCM message that is sent to the sending chain when the bridge queue becomes not - /// congested. - type UncongestedMessage: Get>>; - - /// Returns `true` if we want to handle congestion. - fn supports_congestion_detection() -> bool { - Self::CongestedMessage::get().is_some() || Self::UncongestedMessage::get().is_some() - } -} - -/// XCM bridge adapter which connects [`XcmBlobHauler`] with [`pallet_bridge_messages`] and -/// makes sure that XCM blob is sent to the outbound lane to be relayed. -/// -/// It needs to be used at the source bridge hub. -pub struct XcmBlobHaulerAdapter( - sp_std::marker::PhantomData<(XcmBlobHauler, Lanes)>, -); - -impl< - H: XcmBlobHauler, - Lanes: Get>, - > OnMessagesDelivered for XcmBlobHaulerAdapter -{ - fn on_messages_delivered(lane: LaneId, enqueued_messages: MessageNonce) { - if let Some(sender_and_lane) = - Lanes::get().iter().find(|link| link.0.lane == lane).map(|link| &link.0) - { - // notify XCM queue manager about updated lane state - LocalXcmQueueManager::::on_bridge_messages_delivered( - sender_and_lane, - enqueued_messages, - ); - } - } -} - -/// Manager of local XCM queues (and indirectly - underlying transport channels) that -/// controls the queue state. -/// -/// It needs to be used at the source bridge hub. -pub struct LocalXcmQueueManager(PhantomData); - -/// Maximal number of messages in the outbound bridge queue. Once we reach this limit, we -/// send a "congestion" XCM message to the sending chain. -const OUTBOUND_LANE_CONGESTED_THRESHOLD: MessageNonce = 8_192; - -/// After we have sent "congestion" XCM message to the sending chain, we wait until number -/// of messages in the outbound bridge queue drops to this count, before sending `uncongestion` -/// XCM message. -const OUTBOUND_LANE_UNCONGESTED_THRESHOLD: MessageNonce = 1_024; - -impl LocalXcmQueueManager { - /// Must be called whenever we push a message to the bridge lane. - pub fn on_bridge_message_enqueued( - sender_and_lane: &SenderAndLane, - enqueued_messages: MessageNonce, - ) { - // skip if we dont want to handle congestion - if !H::supports_congestion_detection() { - return - } - - // if we have already sent the congestion signal, we don't want to do anything - if Self::is_congested_signal_sent(sender_and_lane.lane) { - return - } - - // if the bridge queue is not congested, we don't want to do anything - let is_congested = enqueued_messages > OUTBOUND_LANE_CONGESTED_THRESHOLD; - if !is_congested { - return - } - - log::info!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "Sending 'congested' XCM message to {:?} to avoid overloading lane {:?}: there are\ - {} messages queued at the bridge queue", - sender_and_lane.location, - sender_and_lane.lane, - enqueued_messages, - ); - - if let Err(e) = Self::send_congested_signal(sender_and_lane) { - log::info!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "Failed to send the 'congested' XCM message to {:?}: {:?}", - sender_and_lane.location, - e, - ); - } - } - - /// Must be called whenever we receive a message delivery confirmation. - pub fn on_bridge_messages_delivered( - sender_and_lane: &SenderAndLane, - enqueued_messages: MessageNonce, - ) { - // skip if we don't want to handle congestion - if !H::supports_congestion_detection() { - return - } - - // if we have not sent the congestion signal before, we don't want to do anything - if !Self::is_congested_signal_sent(sender_and_lane.lane) { - return - } - - // if the bridge queue is still congested, we don't want to do anything - let is_congested = enqueued_messages > OUTBOUND_LANE_UNCONGESTED_THRESHOLD; - if is_congested { - return - } - - log::info!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "Sending 'uncongested' XCM message to {:?}. Lane {:?}: there are\ - {} messages queued at the bridge queue", - sender_and_lane.location, - sender_and_lane.lane, - enqueued_messages, - ); - - if let Err(e) = Self::send_uncongested_signal(sender_and_lane) { - log::info!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "Failed to send the 'uncongested' XCM message to {:?}: {:?}", - sender_and_lane.location, - e, - ); - } - } - - /// Returns true if we have sent "congested" signal to the `sending_chain_location`. - fn is_congested_signal_sent(lane: LaneId) -> bool { - OutboundLanesCongestedSignals::::get(lane) - } - - /// Send congested signal to the `sending_chain_location`. - fn send_congested_signal(sender_and_lane: &SenderAndLane) -> Result<(), SendError> { - if let Some(msg) = H::CongestedMessage::get() { - send_xcm::(sender_and_lane.location.clone(), msg)?; - OutboundLanesCongestedSignals::::insert( - sender_and_lane.lane, - true, - ); - } - Ok(()) - } - - /// Send `uncongested` signal to the `sending_chain_location`. - fn send_uncongested_signal(sender_and_lane: &SenderAndLane) -> Result<(), SendError> { - if let Some(msg) = H::UncongestedMessage::get() { - send_xcm::(sender_and_lane.location.clone(), msg)?; - OutboundLanesCongestedSignals::::remove( - sender_and_lane.lane, - ); - } - Ok(()) - } -} - -/// Adapter for the implementation of `GetVersion`, which attempts to find the minimal -/// configured XCM version between the destination `dest` and the bridge hub location provided as -/// `Get`. -pub struct XcmVersionOfDestAndRemoteBridge( - sp_std::marker::PhantomData<(Version, RemoteBridge)>, -); -impl> GetVersion - for XcmVersionOfDestAndRemoteBridge -{ - fn get_version_for(dest: &Location) -> Option { - let dest_version = Version::get_version_for(dest); - let bridge_hub_version = Version::get_version_for(&RemoteBridge::get()); - - match (dest_version, bridge_hub_version) { - (Some(dv), Some(bhv)) => Some(sp_std::cmp::min(dv, bhv)), - (Some(dv), None) => Some(dv), - (None, Some(bhv)) => Some(bhv), - (None, None) => None, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::mock::*; - - use bp_messages::OutboundLaneData; - use frame_support::parameter_types; - use pallet_bridge_messages::OutboundLanes; - - parameter_types! { - pub TestSenderAndLane: SenderAndLane = SenderAndLane { - location: Location::new(1, [Parachain(1000)]), - lane: TEST_LANE_ID, - }; - pub TestLanes: sp_std::vec::Vec<(SenderAndLane, (NetworkId, InteriorLocation))> = sp_std::vec![ - (TestSenderAndLane::get(), (NetworkId::ByGenesis([0; 32]), InteriorLocation::Here)) - ]; - pub DummyXcmMessage: Xcm<()> = Xcm::new(); - } - - struct DummySendXcm; - - impl DummySendXcm { - fn messages_sent() -> u32 { - frame_support::storage::unhashed::get(b"DummySendXcm").unwrap_or(0) - } - } - - impl SendXcm for DummySendXcm { - type Ticket = (); - - fn validate( - _destination: &mut Option, - _message: &mut Option>, - ) -> SendResult { - Ok(((), Default::default())) - } - - fn deliver(_ticket: Self::Ticket) -> Result { - let messages_sent: u32 = Self::messages_sent(); - frame_support::storage::unhashed::put(b"DummySendXcm", &(messages_sent + 1)); - Ok(XcmHash::default()) - } - } - - struct TestBlobHauler; - - impl XcmBlobHauler for TestBlobHauler { - type Runtime = TestRuntime; - type MessagesInstance = (); - - type ToSourceChainSender = DummySendXcm; - type CongestedMessage = DummyXcmMessage; - type UncongestedMessage = DummyXcmMessage; - } - - type TestBlobHaulerAdapter = XcmBlobHaulerAdapter; - - fn fill_up_lane_to_congestion() -> MessageNonce { - let latest_generated_nonce = OUTBOUND_LANE_CONGESTED_THRESHOLD; - OutboundLanes::::insert( - TEST_LANE_ID, - OutboundLaneData { - oldest_unpruned_nonce: 0, - latest_received_nonce: 0, - latest_generated_nonce, - }, - ); - latest_generated_nonce - } - - #[test] - fn congested_signal_is_not_sent_twice() { - run_test(|| { - let enqueued = fill_up_lane_to_congestion(); - - // next sent message leads to congested signal - LocalXcmQueueManager::::on_bridge_message_enqueued( - &TestSenderAndLane::get(), - enqueued + 1, - ); - assert_eq!(DummySendXcm::messages_sent(), 1); - - // next sent message => we don't sent another congested signal - LocalXcmQueueManager::::on_bridge_message_enqueued( - &TestSenderAndLane::get(), - enqueued, - ); - assert_eq!(DummySendXcm::messages_sent(), 1); - }); - } - - #[test] - fn congested_signal_is_not_sent_when_outbound_lane_is_not_congested() { - run_test(|| { - LocalXcmQueueManager::::on_bridge_message_enqueued( - &TestSenderAndLane::get(), - 1, - ); - assert_eq!(DummySendXcm::messages_sent(), 0); - }); - } - - #[test] - fn congested_signal_is_sent_when_outbound_lane_is_congested() { - run_test(|| { - let enqueued = fill_up_lane_to_congestion(); - - // next sent message leads to congested signal - LocalXcmQueueManager::::on_bridge_message_enqueued( - &TestSenderAndLane::get(), - enqueued + 1, - ); - assert_eq!(DummySendXcm::messages_sent(), 1); - assert!(LocalXcmQueueManager::::is_congested_signal_sent(TEST_LANE_ID)); - }); - } - - #[test] - fn uncongested_signal_is_not_sent_when_messages_are_delivered_at_other_lane() { - run_test(|| { - LocalXcmQueueManager::::send_congested_signal(&TestSenderAndLane::get()).unwrap(); - assert_eq!(DummySendXcm::messages_sent(), 1); - - // when we receive a delivery report for other lane, we don't send an uncongested signal - TestBlobHaulerAdapter::on_messages_delivered(LaneId([42, 42, 42, 42]), 0); - assert_eq!(DummySendXcm::messages_sent(), 1); - }); - } - - #[test] - fn uncongested_signal_is_not_sent_when_we_havent_send_congested_signal_before() { - run_test(|| { - TestBlobHaulerAdapter::on_messages_delivered(TEST_LANE_ID, 0); - assert_eq!(DummySendXcm::messages_sent(), 0); - }); - } - - #[test] - fn uncongested_signal_is_not_sent_if_outbound_lane_is_still_congested() { - run_test(|| { - LocalXcmQueueManager::::send_congested_signal(&TestSenderAndLane::get()).unwrap(); - assert_eq!(DummySendXcm::messages_sent(), 1); - - TestBlobHaulerAdapter::on_messages_delivered( - TEST_LANE_ID, - OUTBOUND_LANE_UNCONGESTED_THRESHOLD + 1, - ); - assert_eq!(DummySendXcm::messages_sent(), 1); - }); - } - - #[test] - fn uncongested_signal_is_sent_if_outbound_lane_is_uncongested() { - run_test(|| { - LocalXcmQueueManager::::send_congested_signal(&TestSenderAndLane::get()).unwrap(); - assert_eq!(DummySendXcm::messages_sent(), 1); - - TestBlobHaulerAdapter::on_messages_delivered( - TEST_LANE_ID, - OUTBOUND_LANE_UNCONGESTED_THRESHOLD, - ); - assert_eq!(DummySendXcm::messages_sent(), 2); - }); - } -} diff --git a/bridges/bin/runtime-common/src/mock.rs b/bridges/bin/runtime-common/src/mock.rs index 2f248a7162a6cbdcc09d2980a922b1a065127e40..1d4043fc4b61b257ca559b3454eeed63b882ca62 100644 --- a/bridges/bin/runtime-common/src/mock.rs +++ b/bridges/bin/runtime-common/src/mock.rs @@ -18,16 +18,15 @@ #![cfg(test)] -use crate::messages_xcm_extension::XcmAsPlainPayload; - use bp_header_chain::ChainWithGrandpa; use bp_messages::{ target_chain::{DispatchMessage, MessageDispatch}, - ChainWithMessages, LaneId, MessageNonce, + ChainWithMessages, HashedLaneId, LaneIdType, MessageNonce, }; use bp_parachains::SingleParaStoredHeaderDataBuilder; use bp_relayers::PayRewardFromAccount; use bp_runtime::{messages::MessageDispatchResult, Chain, ChainId, Parachain}; +use codec::Encode; use frame_support::{ derive_impl, parameter_types, weights::{ConstantMultiplier, IdentityFee, RuntimeDbWeight, Weight}, @@ -71,7 +70,7 @@ pub type BridgedChainHeader = sp_runtime::generic::Header; /// Rewards payment procedure. -pub type TestPaymentProcedure = PayRewardFromAccount; +pub type TestPaymentProcedure = PayRewardFromAccount; /// Stake that we are using in tests. pub type TestStake = ConstU64<5_000>; /// Stake and slash mechanism to use in tests. @@ -84,8 +83,13 @@ pub type TestStakeAndSlash = pallet_bridge_relayers::StakeAndSlashNamed< ConstU32<8>, >; -/// Message lane used in tests. -pub const TEST_LANE_ID: LaneId = LaneId([0, 0, 0, 0]); +/// Lane identifier type used for tests. +pub type TestLaneIdType = HashedLaneId; +/// Lane that we're using in tests. +pub fn test_lane_id() -> TestLaneIdType { + TestLaneIdType::try_new(1, 2).unwrap() +} + /// Bridged chain id used in tests. pub const TEST_BRIDGED_CHAIN_ID: ChainId = *b"brdg"; /// Maximal extrinsic size at the `BridgedChain`. @@ -111,7 +115,6 @@ crate::generate_bridge_reject_obsolete_headers_and_messages! { } parameter_types! { - pub const ActiveOutboundLanes: &'static [LaneId] = &[TEST_LANE_ID]; pub const BridgedParasPalletName: &'static str = "Paras"; pub const ExistentialDeposit: ThisChainBalance = 500; pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { read: 1, write: 2 }; @@ -185,13 +188,12 @@ impl pallet_bridge_parachains::Config for TestRuntime { impl pallet_bridge_messages::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; type WeightInfo = pallet_bridge_messages::weights::BridgeWeight; - type ActiveOutboundLanes = ActiveOutboundLanes; - - type OutboundPayload = XcmAsPlainPayload; + type OutboundPayload = Vec; type InboundPayload = Vec; - type DeliveryPayments = (); + type LaneId = TestLaneIdType; + type DeliveryPayments = (); type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< TestRuntime, (), @@ -212,31 +214,36 @@ impl pallet_bridge_relayers::Config for TestRuntime { type PaymentProcedure = TestPaymentProcedure; type StakeAndSlash = TestStakeAndSlash; type WeightInfo = (); + type LaneId = TestLaneIdType; } /// Dummy message dispatcher. pub struct DummyMessageDispatch; impl DummyMessageDispatch { - pub fn deactivate() { - frame_support::storage::unhashed::put(&b"inactive"[..], &false); + pub fn deactivate(lane: TestLaneIdType) { + frame_support::storage::unhashed::put(&(b"inactive", lane).encode()[..], &false); } } impl MessageDispatch for DummyMessageDispatch { type DispatchPayload = Vec; type DispatchLevelResult = (); + type LaneId = TestLaneIdType; - fn is_active() -> bool { - frame_support::storage::unhashed::take::(&b"inactive"[..]) != Some(false) + fn is_active(lane: Self::LaneId) -> bool { + frame_support::storage::unhashed::take::(&(b"inactive", lane).encode()[..]) != + Some(false) } - fn dispatch_weight(_message: &mut DispatchMessage) -> Weight { + fn dispatch_weight( + _message: &mut DispatchMessage, + ) -> Weight { Weight::zero() } fn dispatch( - _: DispatchMessage, + _: DispatchMessage, ) -> MessageDispatchResult { MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result: () } } diff --git a/bridges/bin/runtime-common/src/parachains_benchmarking.rs b/bridges/bin/runtime-common/src/parachains_benchmarking.rs index bcbd779b44dea5fbef7781335cfa1d359ab8c1f1..e48a5664e31a62da3de91aaf368b6ad8ed2f0840 100644 --- a/bridges/bin/runtime-common/src/parachains_benchmarking.rs +++ b/bridges/bin/runtime-common/src/parachains_benchmarking.rs @@ -20,12 +20,13 @@ use crate::messages_benchmarking::insert_header_to_grandpa_pallet; -use bp_parachains::parachain_head_storage_key_at_source; +use bp_parachains::{ + parachain_head_storage_key_at_source, RelayBlockHash, RelayBlockHasher, RelayBlockNumber, +}; use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}; use bp_runtime::{grow_storage_value, record_all_trie_keys, Chain, UnverifiedStorageProofParams}; use codec::Encode; use frame_support::traits::Get; -use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; use sp_std::prelude::*; use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut}; diff --git a/bridges/chains/chain-bridge-hub-kusama/src/lib.rs b/bridges/chains/chain-bridge-hub-kusama/src/lib.rs index c990e8a12f367cafbd35b0693b323a6ec5fb5e96..485fb3d31f2001a0303c8aa7165c50bab7acbbbf 100644 --- a/bridges/chains/chain-bridge-hub-kusama/src/lib.rs +++ b/bridges/chains/chain-bridge-hub-kusama/src/lib.rs @@ -93,4 +93,4 @@ pub const WITH_BRIDGE_HUB_KUSAMA_MESSAGES_PALLET_NAME: &str = "BridgeKusamaMessa pub const WITH_BRIDGE_HUB_KUSAMA_RELAYERS_PALLET_NAME: &str = "BridgeRelayers"; decl_bridge_finality_runtime_apis!(bridge_hub_kusama); -decl_bridge_messages_runtime_apis!(bridge_hub_kusama); +decl_bridge_messages_runtime_apis!(bridge_hub_kusama, LegacyLaneId); diff --git a/bridges/chains/chain-bridge-hub-polkadot/src/lib.rs b/bridges/chains/chain-bridge-hub-polkadot/src/lib.rs index 7379b8863b1de5c1a1482db90077e958f0a33366..7a1793b4da4a46680fe5b54c3b4c311635a47786 100644 --- a/bridges/chains/chain-bridge-hub-polkadot/src/lib.rs +++ b/bridges/chains/chain-bridge-hub-polkadot/src/lib.rs @@ -85,4 +85,4 @@ pub const WITH_BRIDGE_HUB_POLKADOT_MESSAGES_PALLET_NAME: &str = "BridgePolkadotM pub const WITH_BRIDGE_HUB_POLKADOT_RELAYERS_PALLET_NAME: &str = "BridgeRelayers"; decl_bridge_finality_runtime_apis!(bridge_hub_polkadot); -decl_bridge_messages_runtime_apis!(bridge_hub_polkadot); +decl_bridge_messages_runtime_apis!(bridge_hub_polkadot, LegacyLaneId); diff --git a/bridges/chains/chain-bridge-hub-rococo/Cargo.toml b/bridges/chains/chain-bridge-hub-rococo/Cargo.toml index 66848ba0e2634b3b4e525f27013b04768204c749..23fbd9a2742f7ccf137bad53372e6e80d62a65c0 100644 --- a/bridges/chains/chain-bridge-hub-rococo/Cargo.toml +++ b/bridges/chains/chain-bridge-hub-rococo/Cargo.toml @@ -14,14 +14,15 @@ exclude-from-umbrella = true workspace = true [dependencies] -# Bridge Dependencies +codec = { features = ["derive"], workspace = true } +# Bridge Dependencies bp-bridge-hub-cumulus = { workspace = true } bp-runtime = { workspace = true } bp-messages = { workspace = true } +bp-xcm-bridge-hub = { workspace = true } # Substrate Based Dependencies - frame-support = { workspace = true } sp-api = { workspace = true } sp-runtime = { workspace = true } @@ -33,6 +34,8 @@ std = [ "bp-bridge-hub-cumulus/std", "bp-messages/std", "bp-runtime/std", + "bp-xcm-bridge-hub/std", + "codec/std", "frame-support/std", "sp-api/std", "sp-runtime/std", diff --git a/bridges/chains/chain-bridge-hub-rococo/src/lib.rs b/bridges/chains/chain-bridge-hub-rococo/src/lib.rs index 73af997b9950ef640040e44cbba0b93b6a7a56a3..538bc44019f5226f56b2f07b8cca47451b93e678 100644 --- a/bridges/chains/chain-bridge-hub-rococo/src/lib.rs +++ b/bridges/chains/chain-bridge-hub-rococo/src/lib.rs @@ -25,6 +25,7 @@ use bp_messages::*; use bp_runtime::{ decl_bridge_finality_runtime_apis, decl_bridge_messages_runtime_apis, Chain, ChainId, Parachain, }; +use codec::{Decode, Encode}; use frame_support::{ dispatch::DispatchClass, sp_runtime::{MultiAddress, MultiSigner, RuntimeDebug, StateVersion}, @@ -98,19 +99,27 @@ pub const WITH_BRIDGE_ROCOCO_TO_WESTEND_MESSAGES_PALLET_INDEX: u8 = 51; pub const WITH_BRIDGE_ROCOCO_TO_BULLETIN_MESSAGES_PALLET_INDEX: u8 = 61; decl_bridge_finality_runtime_apis!(bridge_hub_rococo); -decl_bridge_messages_runtime_apis!(bridge_hub_rococo); +decl_bridge_messages_runtime_apis!(bridge_hub_rococo, LegacyLaneId); frame_support::parameter_types! { /// The XCM fee that is paid for executing XCM program (with `ExportMessage` instruction) at the Rococo /// BridgeHub. /// (initially was calculated by test `BridgeHubRococo::can_calculate_weight_for_paid_export_message_with_reserve_transfer` + `33%`) - pub const BridgeHubRococoBaseXcmFeeInRocs: u128 = 59_034_266; + pub const BridgeHubRococoBaseXcmFeeInRocs: u128 = 57_145_832; /// Transaction fee that is paid at the Rococo BridgeHub for delivering single inbound message. /// (initially was calculated by test `BridgeHubRococo::can_calculate_fee_for_standalone_message_delivery_transaction` + `33%`) - pub const BridgeHubRococoBaseDeliveryFeeInRocs: u128 = 314_037_860; + pub const BridgeHubRococoBaseDeliveryFeeInRocs: u128 = 297_644_174; /// Transaction fee that is paid at the Rococo BridgeHub for delivering single outbound message confirmation. /// (initially was calculated by test `BridgeHubRococo::can_calculate_fee_for_standalone_message_confirmation_transaction` + `33%`) - pub const BridgeHubRococoBaseConfirmationFeeInRocs: u128 = 57_414_813; + pub const BridgeHubRococoBaseConfirmationFeeInRocs: u128 = 56_740_432; +} + +/// Wrapper over `BridgeHubRococo`'s `RuntimeCall` that can be used without a runtime. +#[derive(Decode, Encode)] +pub enum RuntimeCall { + /// Points to the `pallet_xcm_bridge_hub` pallet instance for `BridgeHubWestend`. + #[codec(index = 52)] + XcmOverBridgeHubWestend(bp_xcm_bridge_hub::XcmBridgeHubCall), } diff --git a/bridges/chains/chain-bridge-hub-westend/Cargo.toml b/bridges/chains/chain-bridge-hub-westend/Cargo.toml index 24a196c1d70d128c49ddb788a699fe6e22e6c359..61357e6aa6c848c32614524cf8a755511bad81d3 100644 --- a/bridges/chains/chain-bridge-hub-westend/Cargo.toml +++ b/bridges/chains/chain-bridge-hub-westend/Cargo.toml @@ -14,15 +14,15 @@ exclude-from-umbrella = true workspace = true [dependencies] +codec = { features = ["derive"], workspace = true } # Bridge Dependencies - bp-bridge-hub-cumulus = { workspace = true } bp-runtime = { workspace = true } bp-messages = { workspace = true } +bp-xcm-bridge-hub = { workspace = true } # Substrate Based Dependencies - frame-support = { workspace = true } sp-api = { workspace = true } sp-runtime = { workspace = true } @@ -34,6 +34,8 @@ std = [ "bp-bridge-hub-cumulus/std", "bp-messages/std", "bp-runtime/std", + "bp-xcm-bridge-hub/std", + "codec/std", "frame-support/std", "sp-api/std", "sp-runtime/std", diff --git a/bridges/chains/chain-bridge-hub-westend/src/lib.rs b/bridges/chains/chain-bridge-hub-westend/src/lib.rs index 17ff2c858a1d3eeae329cb972d95adc32952ede4..7a213fdb28c82df5212a86669b480fe371cf8837 100644 --- a/bridges/chains/chain-bridge-hub-westend/src/lib.rs +++ b/bridges/chains/chain-bridge-hub-westend/src/lib.rs @@ -24,6 +24,7 @@ use bp_messages::*; use bp_runtime::{ decl_bridge_finality_runtime_apis, decl_bridge_messages_runtime_apis, Chain, ChainId, Parachain, }; +use codec::{Decode, Encode}; use frame_support::dispatch::DispatchClass; use sp_runtime::{RuntimeDebug, StateVersion}; @@ -87,19 +88,27 @@ pub const WITH_BRIDGE_HUB_WESTEND_RELAYERS_PALLET_NAME: &str = "BridgeRelayers"; pub const WITH_BRIDGE_WESTEND_TO_ROCOCO_MESSAGES_PALLET_INDEX: u8 = 44; decl_bridge_finality_runtime_apis!(bridge_hub_westend); -decl_bridge_messages_runtime_apis!(bridge_hub_westend); +decl_bridge_messages_runtime_apis!(bridge_hub_westend, LegacyLaneId); frame_support::parameter_types! { /// The XCM fee that is paid for executing XCM program (with `ExportMessage` instruction) at the Westend /// BridgeHub. /// (initially was calculated by test `BridgeHubWestend::can_calculate_weight_for_paid_export_message_with_reserve_transfer` + `33%`) - pub const BridgeHubWestendBaseXcmFeeInWnds: u128 = 17_756_830_000; + pub const BridgeHubWestendBaseXcmFeeInWnds: u128 = 18_191_740_000; /// Transaction fee that is paid at the Westend BridgeHub for delivering single inbound message. /// (initially was calculated by test `BridgeHubWestend::can_calculate_fee_for_standalone_message_delivery_transaction` + `33%`) - pub const BridgeHubWestendBaseDeliveryFeeInWnds: u128 = 94_211_536_452; + pub const BridgeHubWestendBaseDeliveryFeeInWnds: u128 = 89_293_427_116; /// Transaction fee that is paid at the Westend BridgeHub for delivering single outbound message confirmation. /// (initially was calculated by test `BridgeHubWestend::can_calculate_fee_for_standalone_message_confirmation_transaction` + `33%`) - pub const BridgeHubWestendBaseConfirmationFeeInWnds: u128 = 17_224_486_452; + pub const BridgeHubWestendBaseConfirmationFeeInWnds: u128 = 17_022_177_116; +} + +/// Wrapper over `BridgeHubWestend`'s `RuntimeCall` that can be used without a runtime. +#[derive(Decode, Encode)] +pub enum RuntimeCall { + /// Points to the `pallet_xcm_bridge_hub` pallet instance for `BridgeHubRococo`. + #[codec(index = 45)] + XcmOverBridgeHubRococo(bp_xcm_bridge_hub::XcmBridgeHubCall), } diff --git a/bridges/chains/chain-polkadot-bulletin/src/lib.rs b/bridges/chains/chain-polkadot-bulletin/src/lib.rs index 88980a9575016bd5c5e1428329454e8131a2075d..d009369197213da84aca80b9263f82ae4407ca0f 100644 --- a/bridges/chains/chain-polkadot-bulletin/src/lib.rs +++ b/bridges/chains/chain-polkadot-bulletin/src/lib.rs @@ -228,4 +228,4 @@ impl ChainWithMessages for PolkadotBulletin { } decl_bridge_finality_runtime_apis!(polkadot_bulletin, grandpa); -decl_bridge_messages_runtime_apis!(polkadot_bulletin); +decl_bridge_messages_runtime_apis!(polkadot_bulletin, bp_messages::HashedLaneId); diff --git a/bridges/docs/polkadot-kusama-bridge-overview.md b/bridges/docs/polkadot-kusama-bridge-overview.md index 08036f0b0722b869786ae3d0abfc6ae7ea7c2c18..b1812e4caf126995566f7531d43cdf4f307a4c5f 100644 --- a/bridges/docs/polkadot-kusama-bridge-overview.md +++ b/bridges/docs/polkadot-kusama-bridge-overview.md @@ -25,8 +25,9 @@ You won't be able to directly use bridge hub transactions to send XCM messages o use other parachains transactions, which will use HRMP to deliver messages to the Bridge Hub. The Bridge Hub will just queue these messages in its outbound lane, which is dedicated to deliver messages between two parachains. -Our first planned bridge will connect the Polkadot and Kusama Asset Hubs. A bridge between those two parachains would -allow Asset Hub Polkadot accounts to hold wrapped KSM tokens and Asset Hub Kusama accounts to hold wrapped DOT tokens. +Our first planned bridge will connect the Polkadot and Kusama Asset Hubs. A bridge between those two +parachains would allow Asset Hub Polkadot accounts to hold wrapped KSM tokens and Asset Hub Kusama +accounts to hold wrapped DOT tokens. For that bridge (pair of parachains under different consensus systems) we'll be using the lane 00000000. Later, when other parachains will join the bridge, they will be using other lanes for their messages. @@ -92,13 +93,14 @@ Obviously, there should be someone who is paying relayer rewards. We want bridge can't use fees for rewards. Instead, the parachains using the bridge, use sovereign accounts on both sides of the bridge to cover relayer rewards. -Bridged Parachains will have sovereign accounts at bridge hubs. For example, the Kusama Asset Hub will have an account -at the Polkadot Bridge Hub. The Polkadot Asset Hub will have an account at the Kusama Bridge Hub. The sovereign accounts -are used as a source of funds when the relayer is calling the `pallet_bridge_relayers::claim_rewards`. +Bridged Parachains will have sovereign accounts at bridge hubs. For example, the Kusama Asset Hub will +have an account at the Polkadot Bridge Hub. The Polkadot Asset Hub will have an account at the Kusama +Bridge Hub. The sovereign accounts are used as a source of funds when the relayer is calling the +`pallet_bridge_relayers::claim_rewards`. -Since messages lane is only used by the pair of parachains, there's no collision between different bridges. E.g. Kusama -Asset Hub will only reward relayers that are delivering messages from Kusama Asset Hub. The Kusama Asset Hub sovereign -account is not used to cover rewards of bridging with some other Polkadot Parachain. +Since messages lane is only used by the pair of parachains, there's no collision between different bridges. E.g. +Kusama Asset Hub will only reward relayers that are delivering messages from Kusama Asset Hub. +The Kusama Asset Hub sovereign account is not used to cover rewards of bridging with some other Polkadot Parachain. ### Multiple Relayers and Rewards diff --git a/bridges/modules/grandpa/src/call_ext.rs b/bridges/modules/grandpa/src/call_ext.rs index f08eb4c5d1ab5ae231afc388dacb0699d58fbc46..d964901ba4bc6681246b1b9d30104e652a282fa1 100644 --- a/bridges/modules/grandpa/src/call_ext.rs +++ b/bridges/modules/grandpa/src/call_ext.rs @@ -18,7 +18,10 @@ use crate::{ weights::WeightInfo, BestFinalized, BridgedBlockNumber, BridgedHeader, Config, CurrentAuthoritySet, Error, FreeHeadersRemaining, Pallet, }; -use bp_header_chain::{justification::GrandpaJustification, submit_finality_proof_limits_extras}; +use bp_header_chain::{ + justification::GrandpaJustification, submit_finality_proof_limits_extras, + SubmitFinalityProofInfo, +}; use bp_runtime::{BlockNumberOf, Chain, OwnedBridgeModule}; use frame_support::{ dispatch::CallableCallFor, @@ -31,37 +34,11 @@ use sp_runtime::{ transaction_validity::{InvalidTransaction, TransactionValidityError}, RuntimeDebug, SaturatedConversion, }; - -/// Info about a `SubmitParachainHeads` call which tries to update a single parachain. -#[derive(Copy, Clone, PartialEq, RuntimeDebug)] -pub struct SubmitFinalityProofInfo { - /// Number of the finality target. - pub block_number: N, - /// An identifier of the validators set that has signed the submitted justification. - /// It might be `None` if deprecated version of the `submit_finality_proof` is used. - pub current_set_id: Option, - /// If `true`, then the call proves new **mandatory** header. - pub is_mandatory: bool, - /// If `true`, then the call must be free (assuming that everything else is valid) to - /// be treated as valid. - pub is_free_execution_expected: bool, - /// Extra weight that we assume is included in the call. - /// - /// We have some assumptions about headers and justifications of the bridged chain. - /// We know that if our assumptions are correct, then the call must not have the - /// weight above some limit. The fee paid for weight above that limit, is never refunded. - pub extra_weight: Weight, - /// Extra size (in bytes) that we assume are included in the call. - /// - /// We have some assumptions about headers and justifications of the bridged chain. - /// We know that if our assumptions are correct, then the call must not have the - /// weight above some limit. The fee paid for bytes above that limit, is never refunded. - pub extra_size: u32, -} +use sp_std::fmt::Debug; /// Verified `SubmitFinalityProofInfo`. #[derive(Copy, Clone, PartialEq, RuntimeDebug)] -pub struct VerifiedSubmitFinalityProofInfo { +pub struct VerifiedSubmitFinalityProofInfo { /// Base call information. pub base: SubmitFinalityProofInfo, /// A difference between bundled bridged header and best bridged header known to us @@ -69,13 +46,6 @@ pub struct VerifiedSubmitFinalityProofInfo { pub improved_by: N, } -impl SubmitFinalityProofInfo { - /// Returns `true` if call size/weight is below our estimations for regular calls. - pub fn fits_limits(&self) -> bool { - self.extra_weight.is_zero() && self.extra_size.is_zero() - } -} - /// Helper struct that provides methods for working with the `SubmitFinalityProof` call. pub struct SubmitFinalityProofHelper, I: 'static> { _phantom_data: sp_std::marker::PhantomData<(T, I)>, @@ -336,9 +306,9 @@ mod tests { TestRuntime, }, BestFinalized, Config, CurrentAuthoritySet, FreeHeadersRemaining, PalletOperatingMode, - StoredAuthoritySet, SubmitFinalityProofInfo, WeightInfo, + StoredAuthoritySet, WeightInfo, }; - use bp_header_chain::ChainWithGrandpa; + use bp_header_chain::{ChainWithGrandpa, SubmitFinalityProofInfo}; use bp_runtime::{BasicOperatingMode, HeaderId}; use bp_test_utils::{ make_default_justification, make_justification_for_header, JustificationGeneratorParams, diff --git a/bridges/modules/grandpa/src/lib.rs b/bridges/modules/grandpa/src/lib.rs index c62951b74656b052d4858dec2af1393e41553029..22a15ec4062f723223ce80cd9b18bd2a11914327 100644 --- a/bridges/modules/grandpa/src/lib.rs +++ b/bridges/modules/grandpa/src/lib.rs @@ -117,7 +117,7 @@ pub mod pallet { /// /// However, if the bridged chain gets compromised, its validators may generate as many /// "free" headers as they want. And they may fill the whole block (at this chain) for - /// free. This constants limits number of calls that we may refund in a single block. + /// free. This constant limits number of calls that we may refund in a single block. /// All calls above this limit are accepted, but are not refunded. #[pallet::constant] type MaxFreeHeadersPerBlock: Get; @@ -728,15 +728,13 @@ pub mod pallet { init_params; let authority_set_length = authority_list.len(); let authority_set = StoredAuthoritySet::::try_new(authority_list, set_id) - .map_err(|e| { + .inspect_err(|_| { log::error!( target: LOG_TARGET, "Failed to initialize bridge. Number of authorities in the set {} is larger than the configured value {}", authority_set_length, T::BridgedChain::MAX_AUTHORITIES_COUNT, ); - - e })?; let initial_hash = header.hash(); diff --git a/bridges/modules/messages/Cargo.toml b/bridges/modules/messages/Cargo.toml index 33f524030d264e4ed292f8f67273e838e15fc3a9..9df318587e3895ee8d040a586f4d70770fc64f0f 100644 --- a/bridges/modules/messages/Cargo.toml +++ b/bridges/modules/messages/Cargo.toml @@ -73,7 +73,4 @@ try-runtime = [ "pallet-bridge-grandpa/try-runtime", "sp-runtime/try-runtime", ] -test-helpers = [ - "bp-runtime/test-helpers", - "sp-trie", -] +test-helpers = ["bp-runtime/test-helpers", "sp-trie"] diff --git a/bridges/modules/messages/README.md b/bridges/modules/messages/README.md index 80fd92eb0e5a7d975ba45619838007a12f5f5553..a78c8680249851b86fe09c42d66d36914ca8dd80 100644 --- a/bridges/modules/messages/README.md +++ b/bridges/modules/messages/README.md @@ -28,9 +28,10 @@ Single message lane may be seen as a transport channel for single application (o time the module itself never dictates any lane or message rules. In the end, it is the runtime developer who defines what message lane and message mean for this runtime. -In our [Kusama<>Polkadot bridge](../../docs/polkadot-kusama-bridge-overview.md) we are using lane as a channel of -communication between two parachains of different relay chains. For example, lane `[0, 0, 0, 0]` is used for Polkadot <> -Kusama Asset Hub communications. Other lanes may be used to bridge other parachains. +In our [Kusama<>Polkadot bridge](../../docs/polkadot-kusama-bridge-overview.md) we are using lane +as a channel of communication between two parachains of different relay chains. For example, lane +`[0, 0, 0, 0]` is used for Polkadot <> Kusama Asset Hub communications. Other lanes may be used to +bridge other parachains. ## Message Workflow @@ -142,10 +143,9 @@ and will simply reject all transactions, related to inbound messages. ### What about other Constants in the Messages Module Configuration Trait? -Two settings that are used to check messages in the `send_message()` function. The -`pallet_bridge_messages::Config::ActiveOutboundLanes` is an array of all message lanes, that -may be used to send messages. All messages sent using other lanes are rejected. All messages that have -size above `pallet_bridge_messages::Config::MaximalOutboundPayloadSize` will also be rejected. +`pallet_bridge_messages::Config::MaximalOutboundPayloadSize` constant defines the maximal size +of outbound message that may be sent. If the message size is above this limit, the message is +rejected. To be able to reward the relayer for delivering messages, we store a map of message nonces range => identifier of the relayer that has delivered this range at the target chain runtime storage. If a diff --git a/bridges/modules/messages/src/benchmarking.rs b/bridges/modules/messages/src/benchmarking.rs index d38aaf32dc94bd157de0d3e910b729a7970c1684..355fb08ab28ae4eb063ee8bc1a826b5cf8a2654b 100644 --- a/bridges/modules/messages/src/benchmarking.rs +++ b/bridges/modules/messages/src/benchmarking.rs @@ -19,14 +19,14 @@ #![cfg(feature = "runtime-benchmarks")] use crate::{ - inbound_lane::InboundLaneStorage, outbound_lane, weights_ext::EXPECTED_DEFAULT_MESSAGE_LENGTH, - BridgedChainOf, Call, OutboundLanes, RuntimeInboundLaneStorage, + active_outbound_lane, weights_ext::EXPECTED_DEFAULT_MESSAGE_LENGTH, BridgedChainOf, Call, + InboundLanes, OutboundLanes, }; use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, target_chain::FromBridgedChainMessagesProof, ChainWithMessages, DeliveredMessages, - InboundLaneData, LaneId, MessageNonce, OutboundLaneData, UnrewardedRelayer, + InboundLaneData, LaneState, MessageNonce, OutboundLaneData, UnrewardedRelayer, UnrewardedRelayersState, }; use bp_runtime::{AccountIdOf, HashOf, UnverifiedStorageProofParams}; @@ -44,7 +44,7 @@ pub struct Pallet, I: 'static = ()>(crate::Pallet); /// Benchmark-specific message proof parameters. #[derive(Debug)] -pub struct MessageProofParams { +pub struct MessageProofParams { /// Id of the lane. pub lane: LaneId, /// Range of messages to include in the proof. @@ -62,7 +62,7 @@ pub struct MessageProofParams { /// Benchmark-specific message delivery proof parameters. #[derive(Debug)] -pub struct MessageDeliveryProofParams { +pub struct MessageDeliveryProofParams { /// Id of the lane. pub lane: LaneId, /// The proof needs to include this inbound lane data. @@ -74,10 +74,8 @@ pub struct MessageDeliveryProofParams { /// Trait that must be implemented by runtime. pub trait Config: crate::Config { /// Lane id to use in benchmarks. - /// - /// By default, lane 00000000 is used. - fn bench_lane_id() -> LaneId { - LaneId([0, 0, 0, 0]) + fn bench_lane_id() -> Self::LaneId { + Self::LaneId::default() } /// Return id of relayer account at the bridged chain. @@ -96,12 +94,12 @@ pub trait Config: crate::Config { /// Prepare messages proof to receive by the module. fn prepare_message_proof( - params: MessageProofParams, - ) -> (FromBridgedChainMessagesProof>>, Weight); + params: MessageProofParams, + ) -> (FromBridgedChainMessagesProof>, Self::LaneId>, Weight); /// Prepare messages delivery proof to receive by the module. fn prepare_message_delivery_proof( - params: MessageDeliveryProofParams, - ) -> FromBridgedChainMessagesDeliveryProof>>; + params: MessageDeliveryProofParams, + ) -> FromBridgedChainMessagesDeliveryProof>, Self::LaneId>; /// Returns true if message has been successfully dispatched or not. fn is_message_successfully_dispatched(_nonce: MessageNonce) -> bool { @@ -113,22 +111,32 @@ pub trait Config: crate::Config { } fn send_regular_message, I: 'static>() { - let mut outbound_lane = outbound_lane::(T::bench_lane_id()); + OutboundLanes::::insert( + T::bench_lane_id(), + OutboundLaneData { + state: LaneState::Opened, + latest_generated_nonce: 1, + ..Default::default() + }, + ); + + let mut outbound_lane = active_outbound_lane::(T::bench_lane_id()).unwrap(); outbound_lane.send_message(BoundedVec::try_from(vec![]).expect("We craft valid messages")); } fn receive_messages, I: 'static>(nonce: MessageNonce) { - let mut inbound_lane_storage = - RuntimeInboundLaneStorage::::from_lane_id(T::bench_lane_id()); - inbound_lane_storage.set_data(InboundLaneData { - relayers: vec![UnrewardedRelayer { - relayer: T::bridged_relayer_id(), - messages: DeliveredMessages::new(nonce), - }] - .into_iter() - .collect(), - last_confirmed_nonce: 0, - }); + InboundLanes::::insert( + T::bench_lane_id(), + InboundLaneData { + state: LaneState::Opened, + relayers: vec![UnrewardedRelayer { + relayer: T::bridged_relayer_id(), + messages: DeliveredMessages::new(nonce), + }] + .into(), + last_confirmed_nonce: 0, + }, + ); } struct ReceiveMessagesProofSetup, I: 'static> { @@ -173,8 +181,8 @@ impl, I: 'static> ReceiveMessagesProofSetup { fn check_last_nonce(&self) { assert_eq!( - crate::InboundLanes::::get(&T::bench_lane_id()).last_delivered_nonce(), - self.last_nonce(), + crate::InboundLanes::::get(&T::bench_lane_id()).map(|d| d.last_delivered_nonce()), + Some(self.last_nonce()), ); } } @@ -277,6 +285,7 @@ mod benchmarks { lane: T::bench_lane_id(), message_nonces: setup.nonces(), outbound_lane_data: Some(OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: setup.last_nonce(), latest_received_nonce: ReceiveMessagesProofSetup::::LATEST_RECEIVED_NONCE, latest_generated_nonce: setup.last_nonce(), @@ -356,6 +365,7 @@ mod benchmarks { let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams { lane: T::bench_lane_id(), inbound_lane_data: InboundLaneData { + state: LaneState::Opened, relayers: vec![UnrewardedRelayer { relayer: relayer_id.clone(), messages: DeliveredMessages::new(1), @@ -374,7 +384,10 @@ mod benchmarks { relayers_state, ); - assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 1); + assert_eq!( + OutboundLanes::::get(T::bench_lane_id()).map(|s| s.latest_received_nonce), + Some(1) + ); assert!(T::is_relayer_rewarded(&relayer_id)); } @@ -404,6 +417,7 @@ mod benchmarks { let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams { lane: T::bench_lane_id(), inbound_lane_data: InboundLaneData { + state: LaneState::Opened, relayers: vec![UnrewardedRelayer { relayer: relayer_id.clone(), messages: delivered_messages, @@ -422,7 +436,10 @@ mod benchmarks { relayers_state, ); - assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 2); + assert_eq!( + OutboundLanes::::get(T::bench_lane_id()).map(|s| s.latest_received_nonce), + Some(2) + ); assert!(T::is_relayer_rewarded(&relayer_id)); } @@ -451,6 +468,7 @@ mod benchmarks { let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams { lane: T::bench_lane_id(), inbound_lane_data: InboundLaneData { + state: LaneState::Opened, relayers: vec![ UnrewardedRelayer { relayer: relayer1_id.clone(), @@ -475,7 +493,10 @@ mod benchmarks { relayers_state, ); - assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 2); + assert_eq!( + OutboundLanes::::get(T::bench_lane_id()).map(|s| s.latest_received_nonce), + Some(2) + ); assert!(T::is_relayer_rewarded(&relayer1_id)); assert!(T::is_relayer_rewarded(&relayer2_id)); } diff --git a/bridges/bin/runtime-common/src/messages_call_ext.rs b/bridges/modules/messages/src/call_ext.rs similarity index 56% rename from bridges/bin/runtime-common/src/messages_call_ext.rs rename to bridges/modules/messages/src/call_ext.rs index a9ee1969ae0ca462f36098f03b4454e1399af129..9e5f5f8d1129ed249b485f6c7662890deb1e8a4c 100644 --- a/bridges/bin/runtime-common/src/messages_call_ext.rs +++ b/bridges/modules/messages/src/call_ext.rs @@ -16,121 +16,18 @@ //! Helpers for easier manipulation of call processing with signed extensions. +use crate::{BridgedChainOf, Config, InboundLanes, OutboundLanes, Pallet, LOG_TARGET}; + use bp_messages::{ - target_chain::MessageDispatch, ChainWithMessages, InboundLaneData, LaneId, MessageNonce, + target_chain::MessageDispatch, BaseMessagesProofInfo, ChainWithMessages, InboundLaneData, + MessageNonce, MessagesCallInfo, ReceiveMessagesDeliveryProofInfo, ReceiveMessagesProofInfo, + UnrewardedRelayerOccupation, }; use bp_runtime::{AccountIdOf, OwnedBridgeModule}; use frame_support::{dispatch::CallableCallFor, traits::IsSubType}; -use pallet_bridge_messages::{BridgedChainOf, Config, Pallet}; -use sp_runtime::{transaction_validity::TransactionValidity, RuntimeDebug}; -use sp_std::ops::RangeInclusive; - -/// Generic info about a messages delivery/confirmation proof. -#[derive(PartialEq, RuntimeDebug)] -pub struct BaseMessagesProofInfo { - /// Message lane, used by the call. - pub lane_id: LaneId, - /// Nonces of messages, included in the call. - /// - /// For delivery transaction, it is nonces of bundled messages. For confirmation - /// transaction, it is nonces that are to be confirmed during the call. - pub bundled_range: RangeInclusive, - /// Nonce of the best message, stored by this chain before the call is dispatched. - /// - /// For delivery transaction, it is the nonce of best delivered message before the call. - /// For confirmation transaction, it is the nonce of best confirmed message before the call. - pub best_stored_nonce: MessageNonce, -} - -impl BaseMessagesProofInfo { - /// Returns true if `bundled_range` continues the `0..=best_stored_nonce` range. - fn appends_to_stored_nonce(&self) -> bool { - Some(*self.bundled_range.start()) == self.best_stored_nonce.checked_add(1) - } -} - -/// Occupation state of the unrewarded relayers vector. -#[derive(PartialEq, RuntimeDebug)] -#[cfg_attr(test, derive(Default))] -pub struct UnrewardedRelayerOccupation { - /// The number of remaining unoccupied entries for new relayers. - pub free_relayer_slots: MessageNonce, - /// The number of messages that we are ready to accept. - pub free_message_slots: MessageNonce, -} - -/// Info about a `ReceiveMessagesProof` call which tries to update a single lane. -#[derive(PartialEq, RuntimeDebug)] -pub struct ReceiveMessagesProofInfo { - /// Base messages proof info - pub base: BaseMessagesProofInfo, - /// State of unrewarded relayers vector. - pub unrewarded_relayers: UnrewardedRelayerOccupation, -} - -impl ReceiveMessagesProofInfo { - /// Returns true if: - /// - /// - either inbound lane is ready to accept bundled messages; - /// - /// - or there are no bundled messages, but the inbound lane is blocked by too many unconfirmed - /// messages and/or unrewarded relayers. - fn is_obsolete(&self, is_dispatcher_active: bool) -> bool { - // if dispatcher is inactive, we don't accept any delivery transactions - if !is_dispatcher_active { - return true - } - - // transactions with zero bundled nonces are not allowed, unless they're message - // delivery transactions, which brings reward confirmations required to unblock - // the lane - if self.base.bundled_range.is_empty() { - let empty_transactions_allowed = - // we allow empty transactions when we can't accept delivery from new relayers - self.unrewarded_relayers.free_relayer_slots == 0 || - // or if we can't accept new messages at all - self.unrewarded_relayers.free_message_slots == 0; - - return !empty_transactions_allowed - } +use sp_runtime::transaction_validity::TransactionValidity; - // otherwise we require bundled messages to continue stored range - !self.base.appends_to_stored_nonce() - } -} - -/// Info about a `ReceiveMessagesDeliveryProof` call which tries to update a single lane. -#[derive(PartialEq, RuntimeDebug)] -pub struct ReceiveMessagesDeliveryProofInfo(pub BaseMessagesProofInfo); - -impl ReceiveMessagesDeliveryProofInfo { - /// Returns true if outbound lane is ready to accept confirmations of bundled messages. - fn is_obsolete(&self) -> bool { - self.0.bundled_range.is_empty() || !self.0.appends_to_stored_nonce() - } -} - -/// Info about a `ReceiveMessagesProof` or a `ReceiveMessagesDeliveryProof` call -/// which tries to update a single lane. -#[derive(PartialEq, RuntimeDebug)] -pub enum CallInfo { - /// Messages delivery call info. - ReceiveMessagesProof(ReceiveMessagesProofInfo), - /// Messages delivery confirmation call info. - ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo), -} - -impl CallInfo { - /// Returns range of messages, bundled with the call. - pub fn bundled_messages(&self) -> RangeInclusive { - match *self { - Self::ReceiveMessagesProof(ref info) => info.base.bundled_range.clone(), - Self::ReceiveMessagesDeliveryProof(ref info) => info.0.bundled_range.clone(), - } - } -} - -/// Helper struct that provides methods for working with a call supported by `CallInfo`. +/// Helper struct that provides methods for working with a call supported by `MessagesCallInfo`. pub struct CallHelper, I: 'static> { _phantom_data: sp_std::marker::PhantomData<(T, I)>, } @@ -142,11 +39,13 @@ impl, I: 'static> CallHelper { /// /// - call is `receive_messages_delivery_proof` and all messages confirmations have been /// received. - pub fn was_successful(info: &CallInfo) -> bool { + pub fn was_successful(info: &MessagesCallInfo) -> bool { match info { - CallInfo::ReceiveMessagesProof(info) => { - let inbound_lane_data = - pallet_bridge_messages::InboundLanes::::get(info.base.lane_id); + MessagesCallInfo::ReceiveMessagesProof(info) => { + let inbound_lane_data = match InboundLanes::::get(info.base.lane_id) { + Some(inbound_lane_data) => inbound_lane_data, + None => return false, + }; if info.base.bundled_range.is_empty() { let post_occupation = unrewarded_relayers_occupation::(&inbound_lane_data); @@ -160,9 +59,11 @@ impl, I: 'static> CallHelper { inbound_lane_data.last_delivered_nonce() == *info.base.bundled_range.end() }, - CallInfo::ReceiveMessagesDeliveryProof(info) => { - let outbound_lane_data = - pallet_bridge_messages::OutboundLanes::::get(info.0.lane_id); + MessagesCallInfo::ReceiveMessagesDeliveryProof(info) => { + let outbound_lane_data = match OutboundLanes::::get(info.0.lane_id) { + Some(outbound_lane_data) => outbound_lane_data, + None => return false, + }; outbound_lane_data.latest_received_nonce == *info.0.bundled_range.end() }, } @@ -170,23 +71,25 @@ impl, I: 'static> CallHelper { } /// Trait representing a call that is a sub type of `pallet_bridge_messages::Call`. -pub trait MessagesCallSubType, I: 'static>: +pub trait CallSubType, I: 'static>: IsSubType, T>> { /// Create a new instance of `ReceiveMessagesProofInfo` from a `ReceiveMessagesProof` call. - fn receive_messages_proof_info(&self) -> Option; + fn receive_messages_proof_info(&self) -> Option>; /// Create a new instance of `ReceiveMessagesDeliveryProofInfo` from /// a `ReceiveMessagesDeliveryProof` call. - fn receive_messages_delivery_proof_info(&self) -> Option; + fn receive_messages_delivery_proof_info( + &self, + ) -> Option>; - /// Create a new instance of `CallInfo` from a `ReceiveMessagesProof` + /// Create a new instance of `MessagesCallInfo` from a `ReceiveMessagesProof` /// or a `ReceiveMessagesDeliveryProof` call. - fn call_info(&self) -> Option; + fn call_info(&self) -> Option>; - /// Create a new instance of `CallInfo` from a `ReceiveMessagesProof` + /// Create a new instance of `MessagesCallInfo` from a `ReceiveMessagesProof` /// or a `ReceiveMessagesDeliveryProof` call, if the call is for the provided lane. - fn call_info_for(&self, lane_id: LaneId) -> Option; + fn call_info_for(&self, lane_id: T::LaneId) -> Option>; /// Ensures that a `ReceiveMessagesProof` or a `ReceiveMessagesDeliveryProof` call: /// @@ -211,15 +114,13 @@ impl< Call: IsSubType, T>>, T: frame_system::Config + Config, I: 'static, - > MessagesCallSubType for T::RuntimeCall + > CallSubType for T::RuntimeCall { - fn receive_messages_proof_info(&self) -> Option { - if let Some(pallet_bridge_messages::Call::::receive_messages_proof { - ref proof, - .. - }) = self.is_sub_type() + fn receive_messages_proof_info(&self) -> Option> { + if let Some(crate::Call::::receive_messages_proof { ref proof, .. }) = + self.is_sub_type() { - let inbound_lane_data = pallet_bridge_messages::InboundLanes::::get(proof.lane); + let inbound_lane_data = InboundLanes::::get(proof.lane)?; return Some(ReceiveMessagesProofInfo { base: BaseMessagesProofInfo { @@ -236,14 +137,16 @@ impl< None } - fn receive_messages_delivery_proof_info(&self) -> Option { - if let Some(pallet_bridge_messages::Call::::receive_messages_delivery_proof { + fn receive_messages_delivery_proof_info( + &self, + ) -> Option> { + if let Some(crate::Call::::receive_messages_delivery_proof { ref proof, ref relayers_state, .. }) = self.is_sub_type() { - let outbound_lane_data = pallet_bridge_messages::OutboundLanes::::get(proof.lane); + let outbound_lane_data = OutboundLanes::::get(proof.lane)?; return Some(ReceiveMessagesDeliveryProofInfo(BaseMessagesProofInfo { lane_id: proof.lane, @@ -260,23 +163,23 @@ impl< None } - fn call_info(&self) -> Option { + fn call_info(&self) -> Option> { if let Some(info) = self.receive_messages_proof_info() { - return Some(CallInfo::ReceiveMessagesProof(info)) + return Some(MessagesCallInfo::ReceiveMessagesProof(info)) } if let Some(info) = self.receive_messages_delivery_proof_info() { - return Some(CallInfo::ReceiveMessagesDeliveryProof(info)) + return Some(MessagesCallInfo::ReceiveMessagesDeliveryProof(info)) } None } - fn call_info_for(&self, lane_id: LaneId) -> Option { + fn call_info_for(&self, lane_id: T::LaneId) -> Option> { self.call_info().filter(|info| { let actual_lane_id = match info { - CallInfo::ReceiveMessagesProof(info) => info.base.lane_id, - CallInfo::ReceiveMessagesDeliveryProof(info) => info.0.lane_id, + MessagesCallInfo::ReceiveMessagesProof(info) => info.base.lane_id, + MessagesCallInfo::ReceiveMessagesDeliveryProof(info) => info.0.lane_id, }; actual_lane_id == lane_id }) @@ -287,29 +190,30 @@ impl< match self.call_info() { Some(proof_info) if is_pallet_halted => { log::trace!( - target: pallet_bridge_messages::LOG_TARGET, + target: LOG_TARGET, "Rejecting messages transaction on halted pallet: {:?}", proof_info ); return sp_runtime::transaction_validity::InvalidTransaction::Call.into() }, - Some(CallInfo::ReceiveMessagesProof(proof_info)) - if proof_info.is_obsolete(T::MessageDispatch::is_active()) => + Some(MessagesCallInfo::ReceiveMessagesProof(proof_info)) + if proof_info + .is_obsolete(T::MessageDispatch::is_active(proof_info.base.lane_id)) => { log::trace!( - target: pallet_bridge_messages::LOG_TARGET, + target: LOG_TARGET, "Rejecting obsolete messages delivery transaction: {:?}", proof_info ); return sp_runtime::transaction_validity::InvalidTransaction::Stale.into() }, - Some(CallInfo::ReceiveMessagesDeliveryProof(proof_info)) + Some(MessagesCallInfo::ReceiveMessagesDeliveryProof(proof_info)) if proof_info.is_obsolete() => { log::trace!( - target: pallet_bridge_messages::LOG_TARGET, + target: LOG_TARGET, "Rejecting obsolete messages confirmation transaction: {:?}", proof_info, ); @@ -343,52 +247,45 @@ fn unrewarded_relayers_occupation, I: 'static>( #[cfg(test)] mod tests { use super::*; - use crate::{ - messages_call_ext::MessagesCallSubType, - mock::{BridgedUnderlyingChain, DummyMessageDispatch, TestRuntime, ThisChainRuntimeCall}, - }; + use crate::tests::mock::*; use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, - target_chain::FromBridgedChainMessagesProof, DeliveredMessages, UnrewardedRelayer, - UnrewardedRelayersState, + target_chain::FromBridgedChainMessagesProof, DeliveredMessages, InboundLaneData, LaneState, + OutboundLaneData, UnrewardedRelayer, UnrewardedRelayersState, }; use sp_std::ops::RangeInclusive; fn fill_unrewarded_relayers() { - let mut inbound_lane_state = - pallet_bridge_messages::InboundLanes::::get(LaneId([0, 0, 0, 0])); - for n in 0..BridgedUnderlyingChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX { + let mut inbound_lane_state = InboundLanes::::get(test_lane_id()).unwrap(); + for n in 0..BridgedChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX { inbound_lane_state.relayers.push_back(UnrewardedRelayer { relayer: Default::default(), messages: DeliveredMessages { begin: n + 1, end: n + 1 }, }); } - pallet_bridge_messages::InboundLanes::::insert( - LaneId([0, 0, 0, 0]), - inbound_lane_state, - ); + InboundLanes::::insert(test_lane_id(), inbound_lane_state); } fn fill_unrewarded_messages() { - let mut inbound_lane_state = - pallet_bridge_messages::InboundLanes::::get(LaneId([0, 0, 0, 0])); + let mut inbound_lane_state = InboundLanes::::get(test_lane_id()).unwrap(); inbound_lane_state.relayers.push_back(UnrewardedRelayer { relayer: Default::default(), messages: DeliveredMessages { begin: 1, - end: BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + end: BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, }, }); - pallet_bridge_messages::InboundLanes::::insert( - LaneId([0, 0, 0, 0]), - inbound_lane_state, - ); + InboundLanes::::insert(test_lane_id(), inbound_lane_state); } fn deliver_message_10() { - pallet_bridge_messages::InboundLanes::::insert( - LaneId([0, 0, 0, 0]), - bp_messages::InboundLaneData { relayers: Default::default(), last_confirmed_nonce: 10 }, + InboundLanes::::insert( + test_lane_id(), + bp_messages::InboundLaneData { + state: LaneState::Opened, + relayers: Default::default(), + last_confirmed_nonce: 10, + }, ); } @@ -396,28 +293,33 @@ mod tests { nonces_start: bp_messages::MessageNonce, nonces_end: bp_messages::MessageNonce, ) -> bool { - ThisChainRuntimeCall::BridgeMessages( - pallet_bridge_messages::Call::::receive_messages_proof { - relayer_id_at_bridged_chain: 42, - messages_count: nonces_end.checked_sub(nonces_start).map(|x| x + 1).unwrap_or(0) - as u32, - dispatch_weight: frame_support::weights::Weight::zero(), - proof: Box::new(FromBridgedChainMessagesProof { - bridged_header_hash: Default::default(), - storage_proof: Default::default(), - lane: LaneId([0, 0, 0, 0]), - nonces_start, - nonces_end, - }), - }, - ) + RuntimeCall::Messages(crate::Call::::receive_messages_proof { + relayer_id_at_bridged_chain: 42, + messages_count: nonces_end.checked_sub(nonces_start).map(|x| x + 1).unwrap_or(0) as u32, + dispatch_weight: frame_support::weights::Weight::zero(), + proof: Box::new(FromBridgedChainMessagesProof { + bridged_header_hash: Default::default(), + storage_proof: Default::default(), + lane: test_lane_id(), + nonces_start, + nonces_end, + }), + }) .check_obsolete_call() .is_ok() } + fn run_test(test: impl Fn() -> T) -> T { + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + InboundLanes::::insert(test_lane_id(), InboundLaneData::opened()); + OutboundLanes::::insert(test_lane_id(), OutboundLaneData::opened()); + test() + }) + } + #[test] fn extension_rejects_obsolete_messages() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best delivered is message#10 and we're trying to deliver messages 8..=9 // => tx is rejected deliver_message_10(); @@ -427,7 +329,7 @@ mod tests { #[test] fn extension_rejects_same_message() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best delivered is message#10 and we're trying to import messages 10..=10 // => tx is rejected deliver_message_10(); @@ -437,7 +339,7 @@ mod tests { #[test] fn extension_rejects_call_with_some_obsolete_messages() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best delivered is message#10 and we're trying to deliver messages // 10..=15 => tx is rejected deliver_message_10(); @@ -447,7 +349,7 @@ mod tests { #[test] fn extension_rejects_call_with_future_messages() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best delivered is message#10 and we're trying to deliver messages // 13..=15 => tx is rejected deliver_message_10(); @@ -457,12 +359,12 @@ mod tests { #[test] fn extension_reject_call_when_dispatcher_is_inactive() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best delivered is message#10 and we're trying to deliver message 11..=15 // => tx is accepted, but we have inactive dispatcher, so... deliver_message_10(); - DummyMessageDispatch::deactivate(); + TestMessageDispatch::deactivate(test_lane_id()); assert!(!validate_message_delivery(11, 15)); }); } @@ -470,7 +372,7 @@ mod tests { #[test] fn extension_rejects_empty_delivery_with_rewards_confirmations_if_there_are_free_relayer_and_message_slots( ) { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { deliver_message_10(); assert!(!validate_message_delivery(10, 9)); }); @@ -479,7 +381,7 @@ mod tests { #[test] fn extension_accepts_empty_delivery_with_rewards_confirmations_if_there_are_no_free_relayer_slots( ) { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { deliver_message_10(); fill_unrewarded_relayers(); assert!(validate_message_delivery(10, 9)); @@ -489,18 +391,18 @@ mod tests { #[test] fn extension_accepts_empty_delivery_with_rewards_confirmations_if_there_are_no_free_message_slots( ) { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { fill_unrewarded_messages(); assert!(validate_message_delivery( - BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, - BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX - 1 + BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX - 1 )); }); } #[test] fn extension_accepts_new_messages() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best delivered is message#10 and we're trying to deliver message 11..=15 // => tx is accepted deliver_message_10(); @@ -509,9 +411,10 @@ mod tests { } fn confirm_message_10() { - pallet_bridge_messages::OutboundLanes::::insert( - LaneId([0, 0, 0, 0]), + OutboundLanes::::insert( + test_lane_id(), bp_messages::OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: 0, latest_received_nonce: 10, latest_generated_nonce: 10, @@ -520,26 +423,21 @@ mod tests { } fn validate_message_confirmation(last_delivered_nonce: bp_messages::MessageNonce) -> bool { - ThisChainRuntimeCall::BridgeMessages( - pallet_bridge_messages::Call::::receive_messages_delivery_proof { - proof: FromBridgedChainMessagesDeliveryProof { - bridged_header_hash: Default::default(), - storage_proof: Default::default(), - lane: LaneId([0, 0, 0, 0]), - }, - relayers_state: UnrewardedRelayersState { - last_delivered_nonce, - ..Default::default() - }, + RuntimeCall::Messages(crate::Call::::receive_messages_delivery_proof { + proof: FromBridgedChainMessagesDeliveryProof { + bridged_header_hash: Default::default(), + storage_proof: Default::default(), + lane: test_lane_id(), }, - ) + relayers_state: UnrewardedRelayersState { last_delivered_nonce, ..Default::default() }, + }) .check_obsolete_call() .is_ok() } #[test] fn extension_rejects_obsolete_confirmations() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best confirmed is message#10 and we're trying to confirm message#5 => tx // is rejected confirm_message_10(); @@ -549,7 +447,7 @@ mod tests { #[test] fn extension_rejects_same_confirmation() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best confirmed is message#10 and we're trying to confirm message#10 => // tx is rejected confirm_message_10(); @@ -559,7 +457,7 @@ mod tests { #[test] fn extension_rejects_empty_confirmation_even_if_there_are_no_free_unrewarded_entries() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { confirm_message_10(); fill_unrewarded_relayers(); assert!(!validate_message_confirmation(10)); @@ -568,7 +466,7 @@ mod tests { #[test] fn extension_accepts_new_confirmation() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { // when current best confirmed is message#10 and we're trying to confirm message#15 => // tx is accepted confirm_message_10(); @@ -580,10 +478,10 @@ mod tests { bundled_range: RangeInclusive, is_empty: bool, ) -> bool { - CallHelper::::was_successful(&CallInfo::ReceiveMessagesProof( + CallHelper::::was_successful(&MessagesCallInfo::ReceiveMessagesProof( ReceiveMessagesProofInfo { base: BaseMessagesProofInfo { - lane_id: LaneId([0, 0, 0, 0]), + lane_id: test_lane_id(), bundled_range, best_stored_nonce: 0, // doesn't matter for `was_successful` }, @@ -592,7 +490,7 @@ mod tests { free_message_slots: if is_empty { 0 } else { - BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX + BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX }, }, }, @@ -602,7 +500,7 @@ mod tests { #[test] #[allow(clippy::reversed_empty_ranges)] fn was_successful_returns_false_for_failed_reward_confirmation_transaction() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { fill_unrewarded_messages(); assert!(!was_message_delivery_successful(10..=9, true)); }); @@ -611,14 +509,14 @@ mod tests { #[test] #[allow(clippy::reversed_empty_ranges)] fn was_successful_returns_true_for_successful_reward_confirmation_transaction() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { assert!(was_message_delivery_successful(10..=9, true)); }); } #[test] fn was_successful_returns_false_for_failed_delivery() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { deliver_message_10(); assert!(!was_message_delivery_successful(10..=12, false)); }); @@ -626,7 +524,7 @@ mod tests { #[test] fn was_successful_returns_false_for_partially_successful_delivery() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { deliver_message_10(); assert!(!was_message_delivery_successful(9..=12, false)); }); @@ -634,25 +532,27 @@ mod tests { #[test] fn was_successful_returns_true_for_successful_delivery() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { deliver_message_10(); assert!(was_message_delivery_successful(9..=10, false)); }); } fn was_message_confirmation_successful(bundled_range: RangeInclusive) -> bool { - CallHelper::::was_successful(&CallInfo::ReceiveMessagesDeliveryProof( - ReceiveMessagesDeliveryProofInfo(BaseMessagesProofInfo { - lane_id: LaneId([0, 0, 0, 0]), - bundled_range, - best_stored_nonce: 0, // doesn't matter for `was_successful` - }), - )) + CallHelper::::was_successful( + &MessagesCallInfo::ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo( + BaseMessagesProofInfo { + lane_id: test_lane_id(), + bundled_range, + best_stored_nonce: 0, // doesn't matter for `was_successful` + }, + )), + ) } #[test] fn was_successful_returns_false_for_failed_confirmation() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { confirm_message_10(); assert!(!was_message_confirmation_successful(10..=12)); }); @@ -660,7 +560,7 @@ mod tests { #[test] fn was_successful_returns_false_for_partially_successful_confirmation() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { confirm_message_10(); assert!(!was_message_confirmation_successful(9..=12)); }); @@ -668,7 +568,7 @@ mod tests { #[test] fn was_successful_returns_true_for_successful_confirmation() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { + run_test(|| { confirm_message_10(); assert!(was_message_confirmation_successful(9..=10)); }); diff --git a/bridges/modules/messages/src/inbound_lane.rs b/bridges/modules/messages/src/inbound_lane.rs index 7ef4599a93c4823a9be7fa918b35c309cea6611a..91f1159f8f9152e0ba7025762eeafb06999b5acb 100644 --- a/bridges/modules/messages/src/inbound_lane.rs +++ b/bridges/modules/messages/src/inbound_lane.rs @@ -20,7 +20,7 @@ use crate::{BridgedChainOf, Config}; use bp_messages::{ target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, - ChainWithMessages, DeliveredMessages, InboundLaneData, LaneId, MessageKey, MessageNonce, + ChainWithMessages, DeliveredMessages, InboundLaneData, LaneState, MessageKey, MessageNonce, OutboundLaneData, ReceptionResult, UnrewardedRelayer, }; use bp_runtime::AccountIdOf; @@ -33,17 +33,21 @@ use sp_std::prelude::PartialEq; pub trait InboundLaneStorage { /// Id of relayer on source chain. type Relayer: Clone + PartialEq; + /// Lane identifier type. + type LaneId: Encode; /// Lane id. - fn id(&self) -> LaneId; + fn id(&self) -> Self::LaneId; /// Return maximal number of unrewarded relayer entries in inbound lane. fn max_unrewarded_relayer_entries(&self) -> MessageNonce; /// Return maximal number of unconfirmed messages in inbound lane. fn max_unconfirmed_messages(&self) -> MessageNonce; /// Get lane data from the storage. - fn get_or_init_data(&mut self) -> InboundLaneData; + fn data(&self) -> InboundLaneData; /// Update lane data in the storage. fn set_data(&mut self, data: InboundLaneData); + /// Purge lane data from the storage. + fn purge(self); } /// Inbound lane data wrapper that implements `MaxEncodedLen`. @@ -120,9 +124,21 @@ impl InboundLane { InboundLane { storage } } - /// Returns `mut` storage reference. - pub fn storage_mut(&mut self) -> &mut S { - &mut self.storage + /// Get lane state. + pub fn state(&self) -> LaneState { + self.storage.data().state + } + + /// Returns storage reference. + pub fn storage(&self) -> &S { + &self.storage + } + + /// Set lane state. + pub fn set_state(&mut self, state: LaneState) { + let mut data = self.storage.data(); + data.state = state; + self.storage.set_data(data); } /// Receive state of the corresponding outbound lane. @@ -130,7 +146,7 @@ impl InboundLane { &mut self, outbound_lane_data: OutboundLaneData, ) -> Option { - let mut data = self.storage.get_or_init_data(); + let mut data = self.storage.data(); let last_delivered_nonce = data.last_delivered_nonce(); if outbound_lane_data.latest_received_nonce > last_delivered_nonce { @@ -167,13 +183,13 @@ impl InboundLane { } /// Receive new message. - pub fn receive_message( + pub fn receive_message>( &mut self, relayer_at_bridged_chain: &S::Relayer, nonce: MessageNonce, message_data: DispatchMessageData, ) -> ReceptionResult { - let mut data = self.storage.get_or_init_data(); + let mut data = self.storage.data(); if Some(nonce) != data.last_delivered_nonce().checked_add(1) { return ReceptionResult::InvalidNonce } @@ -211,20 +227,17 @@ impl InboundLane { ReceptionResult::Dispatched(dispatch_result) } + + /// Purge lane state from the storage. + pub fn purge(self) { + self.storage.purge() + } } #[cfg(test)] mod tests { use super::*; - use crate::{ - inbound_lane, - tests::mock::{ - dispatch_result, inbound_message_data, inbound_unrewarded_relayers_state, run_test, - unrewarded_relayer, BridgedChain, TestMessageDispatch, TestRuntime, REGULAR_PAYLOAD, - TEST_LANE_ID, TEST_RELAYER_A, TEST_RELAYER_B, TEST_RELAYER_C, - }, - RuntimeInboundLaneStorage, - }; + use crate::{active_inbound_lane, lanes_manager::RuntimeInboundLaneStorage, tests::mock::*}; use bp_messages::UnrewardedRelayersState; fn receive_regular_message( @@ -244,7 +257,7 @@ mod tests { #[test] fn receive_status_update_ignores_status_from_the_future() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); receive_regular_message(&mut lane, 1); assert_eq!( lane.receive_state_update(OutboundLaneData { @@ -254,14 +267,14 @@ mod tests { None, ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 0); + assert_eq!(lane.storage.data().last_confirmed_nonce, 0); }); } #[test] fn receive_status_update_ignores_obsolete_status() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); receive_regular_message(&mut lane, 1); receive_regular_message(&mut lane, 2); receive_regular_message(&mut lane, 3); @@ -272,7 +285,7 @@ mod tests { }), Some(3), ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 3); + assert_eq!(lane.storage.data().last_confirmed_nonce, 3); assert_eq!( lane.receive_state_update(OutboundLaneData { @@ -281,20 +294,20 @@ mod tests { }), None, ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 3); + assert_eq!(lane.storage.data().last_confirmed_nonce, 3); }); } #[test] fn receive_status_update_works() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); receive_regular_message(&mut lane, 1); receive_regular_message(&mut lane, 2); receive_regular_message(&mut lane, 3); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 0); + assert_eq!(lane.storage.data().last_confirmed_nonce, 0); assert_eq!( - lane.storage.get_or_init_data().relayers, + lane.storage.data().relayers, vec![unrewarded_relayer(1, 3, TEST_RELAYER_A)] ); @@ -305,9 +318,9 @@ mod tests { }), Some(2), ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 2); + assert_eq!(lane.storage.data().last_confirmed_nonce, 2); assert_eq!( - lane.storage.get_or_init_data().relayers, + lane.storage.data().relayers, vec![unrewarded_relayer(3, 3, TEST_RELAYER_A)] ); @@ -318,16 +331,16 @@ mod tests { }), Some(3), ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 3); - assert_eq!(lane.storage.get_or_init_data().relayers, vec![]); + assert_eq!(lane.storage.data().last_confirmed_nonce, 3); + assert_eq!(lane.storage.data().relayers, vec![]); }); } #[test] fn receive_status_update_works_with_batches_from_relayers() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); - let mut seed_storage_data = lane.storage.get_or_init_data(); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); + let mut seed_storage_data = lane.storage.data(); // Prepare data seed_storage_data.last_confirmed_nonce = 0; seed_storage_data.relayers.push_back(unrewarded_relayer(1, 1, TEST_RELAYER_A)); @@ -343,9 +356,9 @@ mod tests { }), Some(3), ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 3); + assert_eq!(lane.storage.data().last_confirmed_nonce, 3); assert_eq!( - lane.storage.get_or_init_data().relayers, + lane.storage.data().relayers, vec![ unrewarded_relayer(4, 4, TEST_RELAYER_B), unrewarded_relayer(5, 5, TEST_RELAYER_C) @@ -357,7 +370,7 @@ mod tests { #[test] fn fails_to_receive_message_with_incorrect_nonce() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); assert_eq!( lane.receive_message::( &TEST_RELAYER_A, @@ -366,14 +379,14 @@ mod tests { ), ReceptionResult::InvalidNonce ); - assert_eq!(lane.storage.get_or_init_data().last_delivered_nonce(), 0); + assert_eq!(lane.storage.data().last_delivered_nonce(), 0); }); } #[test] fn fails_to_receive_messages_above_unrewarded_relayer_entries_limit_per_lane() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); let max_nonce = BridgedChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; for current_nonce in 1..max_nonce + 1 { assert_eq!( @@ -409,7 +422,7 @@ mod tests { #[test] fn fails_to_receive_messages_above_unconfirmed_messages_limit_per_lane() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); let max_nonce = BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; for current_nonce in 1..=max_nonce { assert_eq!( @@ -445,7 +458,7 @@ mod tests { #[test] fn correctly_receives_following_messages_from_two_relayers_alternately() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); assert_eq!( lane.receive_message::( &TEST_RELAYER_A, @@ -471,7 +484,7 @@ mod tests { ReceptionResult::Dispatched(dispatch_result(0)) ); assert_eq!( - lane.storage.get_or_init_data().relayers, + lane.storage.data().relayers, vec![ unrewarded_relayer(1, 1, TEST_RELAYER_A), unrewarded_relayer(2, 2, TEST_RELAYER_B), @@ -484,7 +497,7 @@ mod tests { #[test] fn rejects_same_message_from_two_different_relayers() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); assert_eq!( lane.receive_message::( &TEST_RELAYER_A, @@ -507,16 +520,16 @@ mod tests { #[test] fn correct_message_is_processed_instantly() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); receive_regular_message(&mut lane, 1); - assert_eq!(lane.storage.get_or_init_data().last_delivered_nonce(), 1); + assert_eq!(lane.storage.data().last_delivered_nonce(), 1); }); } #[test] fn unspent_weight_is_returned_by_receive_message() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); let mut payload = REGULAR_PAYLOAD; *payload.dispatch_result.unspent_weight.ref_time_mut() = 1; assert_eq!( @@ -533,7 +546,7 @@ mod tests { #[test] fn first_message_is_confirmed_correctly() { run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); + let mut lane = active_inbound_lane::(test_lane_id()).unwrap(); receive_regular_message(&mut lane, 1); receive_regular_message(&mut lane, 2); assert_eq!( @@ -544,7 +557,7 @@ mod tests { Some(1), ); assert_eq!( - inbound_unrewarded_relayers_state(TEST_LANE_ID), + inbound_unrewarded_relayers_state(test_lane_id()), UnrewardedRelayersState { unrewarded_relayer_entries: 1, messages_in_oldest_entry: 1, diff --git a/bridges/modules/messages/src/lanes_manager.rs b/bridges/modules/messages/src/lanes_manager.rs new file mode 100644 index 0000000000000000000000000000000000000000..27cab48535d7bc354d31a7f11ac616edf7e879d1 --- /dev/null +++ b/bridges/modules/messages/src/lanes_manager.rs @@ -0,0 +1,285 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +use crate::{ + BridgedChainOf, Config, InboundLane, InboundLaneStorage, InboundLanes, OutboundLane, + OutboundLaneStorage, OutboundLanes, OutboundMessages, StoredInboundLaneData, + StoredMessagePayload, +}; + +use bp_messages::{ + target_chain::MessageDispatch, ChainWithMessages, InboundLaneData, LaneState, MessageKey, + MessageNonce, OutboundLaneData, +}; +use bp_runtime::AccountIdOf; +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::{ensure, sp_runtime::RuntimeDebug, PalletError}; +use scale_info::TypeInfo; +use sp_std::marker::PhantomData; + +/// Lanes manager errors. +#[derive(Encode, Decode, RuntimeDebug, PartialEq, Eq, PalletError, TypeInfo)] +pub enum LanesManagerError { + /// Inbound lane already exists. + InboundLaneAlreadyExists, + /// Outbound lane already exists. + OutboundLaneAlreadyExists, + /// No inbound lane with given id. + UnknownInboundLane, + /// No outbound lane with given id. + UnknownOutboundLane, + /// Inbound lane with given id is closed. + ClosedInboundLane, + /// Outbound lane with given id is closed. + ClosedOutboundLane, + /// Message dispatcher is inactive at given inbound lane. This is logical equivalent + /// of the [`Self::ClosedInboundLane`] variant. + LaneDispatcherInactive, +} + +/// Message lanes manager. +pub struct LanesManager(PhantomData<(T, I)>); + +impl, I: 'static> Default for LanesManager { + fn default() -> Self { + Self::new() + } +} + +impl, I: 'static> LanesManager { + /// Create new lanes manager. + pub fn new() -> Self { + Self(PhantomData) + } + + /// Create new inbound lane in `Opened` state. + pub fn create_inbound_lane( + &self, + lane_id: T::LaneId, + ) -> Result>, LanesManagerError> { + InboundLanes::::try_mutate(lane_id, |lane| match lane { + Some(_) => Err(LanesManagerError::InboundLaneAlreadyExists), + None => { + *lane = Some(StoredInboundLaneData(InboundLaneData { + state: LaneState::Opened, + ..Default::default() + })); + Ok(()) + }, + })?; + + self.active_inbound_lane(lane_id) + } + + /// Create new outbound lane in `Opened` state. + pub fn create_outbound_lane( + &self, + lane_id: T::LaneId, + ) -> Result>, LanesManagerError> { + OutboundLanes::::try_mutate(lane_id, |lane| match lane { + Some(_) => Err(LanesManagerError::OutboundLaneAlreadyExists), + None => { + *lane = Some(OutboundLaneData { state: LaneState::Opened, ..Default::default() }); + Ok(()) + }, + })?; + + self.active_outbound_lane(lane_id) + } + + /// Get existing inbound lane, checking that it is in usable state. + pub fn active_inbound_lane( + &self, + lane_id: T::LaneId, + ) -> Result>, LanesManagerError> { + Ok(InboundLane::new(RuntimeInboundLaneStorage::from_lane_id(lane_id, true)?)) + } + + /// Get existing outbound lane, checking that it is in usable state. + pub fn active_outbound_lane( + &self, + lane_id: T::LaneId, + ) -> Result>, LanesManagerError> { + Ok(OutboundLane::new(RuntimeOutboundLaneStorage::from_lane_id(lane_id, true)?)) + } + + /// Get existing inbound lane without any additional state checks. + pub fn any_state_inbound_lane( + &self, + lane_id: T::LaneId, + ) -> Result>, LanesManagerError> { + Ok(InboundLane::new(RuntimeInboundLaneStorage::from_lane_id(lane_id, false)?)) + } + + /// Get existing outbound lane without any additional state checks. + pub fn any_state_outbound_lane( + &self, + lane_id: T::LaneId, + ) -> Result>, LanesManagerError> { + Ok(OutboundLane::new(RuntimeOutboundLaneStorage::from_lane_id(lane_id, false)?)) + } +} + +/// Runtime inbound lane storage. +pub struct RuntimeInboundLaneStorage, I: 'static = ()> { + pub(crate) lane_id: T::LaneId, + pub(crate) cached_data: InboundLaneData>>, +} + +impl, I: 'static> RuntimeInboundLaneStorage { + /// Creates new runtime inbound lane storage for given **existing** lane. + fn from_lane_id( + lane_id: T::LaneId, + check_active: bool, + ) -> Result, LanesManagerError> { + let cached_data = + InboundLanes::::get(lane_id).ok_or(LanesManagerError::UnknownInboundLane)?; + + if check_active { + // check that the lane is not explicitly closed + ensure!(cached_data.state.is_active(), LanesManagerError::ClosedInboundLane); + // apart from the explicit closure, the lane may be unable to receive any messages. + // Right now we do an additional check here, but it may be done later (e.g. by + // explicitly closing the lane and reopening it from + // `pallet-xcm-bridge-hub::on-initialize`) + // + // The fact that we only check it here, means that the `MessageDispatch` may switch + // to inactive state during some message dispatch in the middle of message delivery + // transaction. But we treat result of `MessageDispatch::is_active()` as a hint, so + // we know that it won't drop messages - just it experiences problems with processing. + // This would allow us to check that in our signed extensions, and invalidate + // transaction early, thus avoiding losing honest relayers funds. This problem should + // gone with relayers coordination protocol. + // + // There's a limit on number of messages in the message delivery transaction, so even + // if we dispatch (enqueue) some additional messages, we'll know the maximal queue + // length; + ensure!( + T::MessageDispatch::is_active(lane_id), + LanesManagerError::LaneDispatcherInactive + ); + } + + Ok(RuntimeInboundLaneStorage { lane_id, cached_data: cached_data.into() }) + } + + /// Returns number of bytes that may be subtracted from the PoV component of + /// `receive_messages_proof` call, because the actual inbound lane state is smaller than the + /// maximal configured. + /// + /// Maximal inbound lane state set size is configured by the + /// `MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX` constant from the pallet configuration. The PoV + /// of the call includes the maximal size of inbound lane state. If the actual size is smaller, + /// we may subtract extra bytes from this component. + pub fn extra_proof_size_bytes(&self) -> u64 { + let max_encoded_len = StoredInboundLaneData::::max_encoded_len(); + let relayers_count = self.data().relayers.len(); + let actual_encoded_len = + InboundLaneData::>>::encoded_size_hint(relayers_count) + .unwrap_or(usize::MAX); + max_encoded_len.saturating_sub(actual_encoded_len) as _ + } +} + +impl, I: 'static> InboundLaneStorage for RuntimeInboundLaneStorage { + type Relayer = AccountIdOf>; + type LaneId = T::LaneId; + + fn id(&self) -> Self::LaneId { + self.lane_id + } + + fn max_unrewarded_relayer_entries(&self) -> MessageNonce { + BridgedChainOf::::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX + } + + fn max_unconfirmed_messages(&self) -> MessageNonce { + BridgedChainOf::::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX + } + + fn data(&self) -> InboundLaneData>> { + self.cached_data.clone() + } + + fn set_data(&mut self, data: InboundLaneData>>) { + self.cached_data = data.clone(); + InboundLanes::::insert(self.lane_id, StoredInboundLaneData::(data)) + } + + fn purge(self) { + InboundLanes::::remove(self.lane_id) + } +} + +/// Runtime outbound lane storage. +#[derive(Debug, PartialEq, Eq)] +pub struct RuntimeOutboundLaneStorage, I: 'static> { + pub(crate) lane_id: T::LaneId, + pub(crate) cached_data: OutboundLaneData, + pub(crate) _phantom: PhantomData<(T, I)>, +} + +impl, I: 'static> RuntimeOutboundLaneStorage { + /// Creates new runtime outbound lane storage for given **existing** lane. + fn from_lane_id(lane_id: T::LaneId, check_active: bool) -> Result { + let cached_data = + OutboundLanes::::get(lane_id).ok_or(LanesManagerError::UnknownOutboundLane)?; + ensure!( + !check_active || cached_data.state.is_active(), + LanesManagerError::ClosedOutboundLane + ); + Ok(Self { lane_id, cached_data, _phantom: PhantomData }) + } +} + +impl, I: 'static> OutboundLaneStorage for RuntimeOutboundLaneStorage { + type StoredMessagePayload = StoredMessagePayload; + type LaneId = T::LaneId; + + fn id(&self) -> Self::LaneId { + self.lane_id + } + + fn data(&self) -> OutboundLaneData { + self.cached_data.clone() + } + + fn set_data(&mut self, data: OutboundLaneData) { + self.cached_data = data.clone(); + OutboundLanes::::insert(self.lane_id, data) + } + + #[cfg(test)] + fn message(&self, nonce: &MessageNonce) -> Option { + OutboundMessages::::get(MessageKey { lane_id: self.lane_id, nonce: *nonce }) + .map(Into::into) + } + + fn save_message(&mut self, nonce: MessageNonce, message_payload: Self::StoredMessagePayload) { + OutboundMessages::::insert( + MessageKey { lane_id: self.lane_id, nonce }, + message_payload, + ); + } + + fn remove_message(&mut self, nonce: &MessageNonce) { + OutboundMessages::::remove(MessageKey { lane_id: self.lane_id, nonce: *nonce }); + } + + fn purge(self) { + OutboundLanes::::remove(self.lane_id) + } +} diff --git a/bridges/modules/messages/src/lib.rs b/bridges/modules/messages/src/lib.rs index c36313a147644aa61a8efb900dcc35bbf630ceae..af14257db99c1cf0882f4f1a5a94329b194d1977 100644 --- a/bridges/modules/messages/src/lib.rs +++ b/bridges/modules/messages/src/lib.rs @@ -36,8 +36,13 @@ #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] -pub use inbound_lane::StoredInboundLaneData; -pub use outbound_lane::StoredMessagePayload; +pub use inbound_lane::{InboundLane, InboundLaneStorage, StoredInboundLaneData}; +pub use lanes_manager::{ + LanesManager, LanesManagerError, RuntimeInboundLaneStorage, RuntimeOutboundLaneStorage, +}; +pub use outbound_lane::{ + OutboundLane, OutboundLaneStorage, ReceptionConfirmationError, StoredMessagePayload, +}; pub use weights::WeightInfo; pub use weights_ext::{ ensure_able_to_receive_confirmation, ensure_able_to_receive_message, @@ -45,11 +50,6 @@ pub use weights_ext::{ EXPECTED_DEFAULT_MESSAGE_LENGTH, EXTRA_STORAGE_PROOF_SIZE, }; -use crate::{ - inbound_lane::{InboundLane, InboundLaneStorage}, - outbound_lane::{OutboundLane, OutboundLaneStorage, ReceptionConfirmationError}, -}; - use bp_header_chain::HeaderChain; use bp_messages::{ source_chain::{ @@ -60,19 +60,21 @@ use bp_messages::{ DeliveryPayments, DispatchMessage, FromBridgedChainMessagesProof, MessageDispatch, ProvedLaneMessages, ProvedMessages, }, - ChainWithMessages, DeliveredMessages, InboundLaneData, InboundMessageDetails, LaneId, - MessageKey, MessageNonce, MessagePayload, MessagesOperatingMode, OutboundLaneData, - OutboundMessageDetails, UnrewardedRelayersState, VerificationError, + ChainWithMessages, DeliveredMessages, InboundLaneData, InboundMessageDetails, MessageKey, + MessageNonce, MessagePayload, MessagesOperatingMode, OutboundLaneData, OutboundMessageDetails, + UnrewardedRelayersState, VerificationError, }; use bp_runtime::{ AccountIdOf, BasicOperatingMode, HashOf, OwnedBridgeModule, PreComputedSize, RangeInclusiveExt, Size, }; -use codec::{Decode, Encode, MaxEncodedLen}; +use codec::{Decode, Encode}; use frame_support::{dispatch::PostDispatchInfo, ensure, fail, traits::Get, DefaultNoBound}; use sp_std::{marker::PhantomData, prelude::*}; +mod call_ext; mod inbound_lane; +mod lanes_manager; mod outbound_lane; mod proofs; mod tests; @@ -82,7 +84,9 @@ pub mod weights; #[cfg(feature = "runtime-benchmarks")] pub mod benchmarking; +pub mod migration; +pub use call_ext::*; pub use pallet::*; #[cfg(feature = "test-helpers")] pub use tests::*; @@ -93,7 +97,7 @@ pub const LOG_TARGET: &str = "runtime::bridge-messages"; #[frame_support::pallet] pub mod pallet { use super::*; - use bp_messages::{ReceivedMessages, ReceptionResult}; + use bp_messages::{LaneIdType, ReceivedMessages, ReceptionResult}; use bp_runtime::RangeInclusiveExt; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; @@ -115,24 +119,29 @@ pub mod pallet { /// Bridged chain headers provider. type BridgedHeaderChain: HeaderChain; - /// Get all active outbound lanes that the message pallet is serving. - type ActiveOutboundLanes: Get<&'static [LaneId]>; - /// Payload type of outbound messages. This payload is dispatched on the bridged chain. type OutboundPayload: Parameter + Size; /// Payload type of inbound messages. This payload is dispatched on this chain. type InboundPayload: Decode; + /// Lane identifier type. + type LaneId: LaneIdType; /// Handler for relayer payments that happen during message delivery transaction. type DeliveryPayments: DeliveryPayments; /// Handler for relayer payments that happen during message delivery confirmation /// transaction. - type DeliveryConfirmationPayments: DeliveryConfirmationPayments; + type DeliveryConfirmationPayments: DeliveryConfirmationPayments< + Self::AccountId, + Self::LaneId, + >; /// Delivery confirmation callback. - type OnMessagesDelivered: OnMessagesDelivered; + type OnMessagesDelivered: OnMessagesDelivered; /// Message dispatch handler. - type MessageDispatch: MessageDispatch; + type MessageDispatch: MessageDispatch< + DispatchPayload = Self::InboundPayload, + LaneId = Self::LaneId, + >; } /// Shortcut to this chain type for Config. @@ -141,8 +150,11 @@ pub mod pallet { pub type BridgedChainOf = >::BridgedChain; /// Shortcut to bridged header chain type for Config. pub type BridgedHeaderChainOf = >::BridgedHeaderChain; + /// Shortcut to lane identifier type for Config. + pub type LaneIdOf = >::LaneId; #[pallet::pallet] + #[pallet::storage_version(migration::STORAGE_VERSION)] pub struct Pallet(PhantomData<(T, I)>); impl, I: 'static> OwnedBridgeModule for Pallet { @@ -201,7 +213,7 @@ pub mod pallet { pub fn receive_messages_proof( origin: OriginFor, relayer_id_at_bridged_chain: AccountIdOf>, - proof: Box>>>, + proof: Box>, T::LaneId>>, messages_count: u32, dispatch_weight: Weight, ) -> DispatchResultWithPostInfo { @@ -215,9 +227,6 @@ pub mod pallet { Error::::TooManyMessagesInTheProof ); - // if message dispatcher is currently inactive, we won't accept any messages - ensure!(T::MessageDispatch::is_active(), Error::::MessageDispatchInactive); - // why do we need to know the weight of this (`receive_messages_proof`) call? Because // we may want to return some funds for not-dispatching (or partially dispatching) some // messages to the call origin (relayer). And this is done by returning actual weight @@ -236,92 +245,89 @@ pub mod pallet { let mut actual_weight = declared_weight; // verify messages proof && convert proof into messages - let messages = verify_and_decode_messages_proof::(*proof, messages_count) - .map_err(|err| { - log::trace!(target: LOG_TARGET, "Rejecting invalid messages proof: {:?}", err,); + let (lane_id, lane_data) = + verify_and_decode_messages_proof::(*proof, messages_count).map_err( + |err| { + log::trace!(target: LOG_TARGET, "Rejecting invalid messages proof: {:?}", err,); - Error::::InvalidMessagesProof - })?; + Error::::InvalidMessagesProof + }, + )?; // dispatch messages and (optionally) update lane(s) state(s) let mut total_messages = 0; let mut valid_messages = 0; - let mut messages_received_status = Vec::with_capacity(messages.len()); let mut dispatch_weight_left = dispatch_weight; - for (lane_id, lane_data) in messages { - let mut lane = inbound_lane::(lane_id); - - // subtract extra storage proof bytes from the actual PoV size - there may be - // less unrewarded relayers than the maximal configured value - let lane_extra_proof_size_bytes = lane.storage_mut().extra_proof_size_bytes(); - actual_weight = actual_weight.set_proof_size( - actual_weight.proof_size().saturating_sub(lane_extra_proof_size_bytes), - ); + let mut lane = active_inbound_lane::(lane_id)?; - if let Some(lane_state) = lane_data.lane_state { - let updated_latest_confirmed_nonce = lane.receive_state_update(lane_state); - if let Some(updated_latest_confirmed_nonce) = updated_latest_confirmed_nonce { - log::trace!( - target: LOG_TARGET, - "Received lane {:?} state update: latest_confirmed_nonce={}. Unrewarded relayers: {:?}", - lane_id, - updated_latest_confirmed_nonce, - UnrewardedRelayersState::from(&lane.storage_mut().get_or_init_data()), - ); - } - } + // subtract extra storage proof bytes from the actual PoV size - there may be + // less unrewarded relayers than the maximal configured value + let lane_extra_proof_size_bytes = lane.storage().extra_proof_size_bytes(); + actual_weight = actual_weight.set_proof_size( + actual_weight.proof_size().saturating_sub(lane_extra_proof_size_bytes), + ); - let mut lane_messages_received_status = - ReceivedMessages::new(lane_id, Vec::with_capacity(lane_data.messages.len())); - for mut message in lane_data.messages { - debug_assert_eq!(message.key.lane_id, lane_id); - total_messages += 1; - - // ensure that relayer has declared enough weight for dispatching next message - // on this lane. We can't dispatch lane messages out-of-order, so if declared - // weight is not enough, let's move to next lane - let message_dispatch_weight = T::MessageDispatch::dispatch_weight(&mut message); - if message_dispatch_weight.any_gt(dispatch_weight_left) { - log::trace!( - target: LOG_TARGET, - "Cannot dispatch any more messages on lane {:?}. Weight: declared={}, left={}", - lane_id, - message_dispatch_weight, - dispatch_weight_left, - ); - - fail!(Error::::InsufficientDispatchWeight); - } + if let Some(lane_state) = lane_data.lane_state { + let updated_latest_confirmed_nonce = lane.receive_state_update(lane_state); + if let Some(updated_latest_confirmed_nonce) = updated_latest_confirmed_nonce { + log::trace!( + target: LOG_TARGET, + "Received lane {:?} state update: latest_confirmed_nonce={}. Unrewarded relayers: {:?}", + lane_id, + updated_latest_confirmed_nonce, + UnrewardedRelayersState::from(&lane.storage().data()), + ); + } + } - let receival_result = lane.receive_message::( - &relayer_id_at_bridged_chain, - message.key.nonce, - message.data, + let mut messages_received_status = + ReceivedMessages::new(lane_id, Vec::with_capacity(lane_data.messages.len())); + for mut message in lane_data.messages { + debug_assert_eq!(message.key.lane_id, lane_id); + total_messages += 1; + + // ensure that relayer has declared enough weight for dispatching next message + // on this lane. We can't dispatch lane messages out-of-order, so if declared + // weight is not enough, let's move to next lane + let message_dispatch_weight = T::MessageDispatch::dispatch_weight(&mut message); + if message_dispatch_weight.any_gt(dispatch_weight_left) { + log::trace!( + target: LOG_TARGET, + "Cannot dispatch any more messages on lane {:?}. Weight: declared={}, left={}", + lane_id, + message_dispatch_weight, + dispatch_weight_left, ); - // note that we're returning unspent weight to relayer even if message has been - // rejected by the lane. This allows relayers to submit spam transactions with - // e.g. the same set of already delivered messages over and over again, without - // losing funds for messages dispatch. But keep in mind that relayer pays base - // delivery transaction cost anyway. And base cost covers everything except - // dispatch, so we have a balance here. - let unspent_weight = match &receival_result { - ReceptionResult::Dispatched(dispatch_result) => { - valid_messages += 1; - dispatch_result.unspent_weight - }, - ReceptionResult::InvalidNonce | - ReceptionResult::TooManyUnrewardedRelayers | - ReceptionResult::TooManyUnconfirmedMessages => message_dispatch_weight, - }; - lane_messages_received_status.push(message.key.nonce, receival_result); - - let unspent_weight = unspent_weight.min(message_dispatch_weight); - dispatch_weight_left -= message_dispatch_weight - unspent_weight; - actual_weight = actual_weight.saturating_sub(unspent_weight); + fail!(Error::::InsufficientDispatchWeight); } - messages_received_status.push(lane_messages_received_status); + let receival_result = lane.receive_message::( + &relayer_id_at_bridged_chain, + message.key.nonce, + message.data, + ); + + // note that we're returning unspent weight to relayer even if message has been + // rejected by the lane. This allows relayers to submit spam transactions with + // e.g. the same set of already delivered messages over and over again, without + // losing funds for messages dispatch. But keep in mind that relayer pays base + // delivery transaction cost anyway. And base cost covers everything except + // dispatch, so we have a balance here. + let unspent_weight = match &receival_result { + ReceptionResult::Dispatched(dispatch_result) => { + valid_messages += 1; + dispatch_result.unspent_weight + }, + ReceptionResult::InvalidNonce | + ReceptionResult::TooManyUnrewardedRelayers | + ReceptionResult::TooManyUnconfirmedMessages => message_dispatch_weight, + }; + messages_received_status.push(message.key.nonce, receival_result); + + let unspent_weight = unspent_weight.min(message_dispatch_weight); + dispatch_weight_left -= message_dispatch_weight - unspent_weight; + actual_weight = actual_weight.saturating_sub(unspent_weight); } // let's now deal with relayer payments @@ -354,7 +360,7 @@ pub mod pallet { ))] pub fn receive_messages_delivery_proof( origin: OriginFor, - proof: FromBridgedChainMessagesDeliveryProof>>, + proof: FromBridgedChainMessagesDeliveryProof>, T::LaneId>, mut relayers_state: UnrewardedRelayersState, ) -> DispatchResultWithPostInfo { Self::ensure_not_halted().map_err(Error::::BridgeModule)?; @@ -377,7 +383,7 @@ pub mod pallet { ); // mark messages as delivered - let mut lane = outbound_lane::(lane_id); + let mut lane = any_state_outbound_lane::(lane_id)?; let last_delivered_nonce = lane_data.last_delivered_nonce(); let confirmed_messages = lane .confirm_delivery( @@ -391,7 +397,7 @@ pub mod pallet { // emit 'delivered' event let received_range = confirmed_messages.begin..=confirmed_messages.end; Self::deposit_event(Event::MessagesDelivered { - lane_id, + lane_id: lane_id.into(), messages: confirmed_messages, }); @@ -445,19 +451,22 @@ pub mod pallet { /// Message has been accepted and is waiting to be delivered. MessageAccepted { /// Lane, which has accepted the message. - lane_id: LaneId, + lane_id: T::LaneId, /// Nonce of accepted message. nonce: MessageNonce, }, /// Messages have been received from the bridged chain. MessagesReceived( /// Result of received messages dispatch. - Vec::DispatchLevelResult>>, + ReceivedMessages< + ::DispatchLevelResult, + T::LaneId, + >, ), /// Messages in the inclusive range have been delivered to the bridged chain. MessagesDelivered { /// Lane for which the delivery has been confirmed. - lane_id: LaneId, + lane_id: T::LaneId, /// Delivered messages. messages: DeliveredMessages, }, @@ -468,14 +477,10 @@ pub mod pallet { pub enum Error { /// Pallet is not in Normal operating mode. NotOperatingNormally, - /// The outbound lane is inactive. - InactiveOutboundLane, - /// The inbound message dispatcher is inactive. - MessageDispatchInactive, + /// Error that is reported by the lanes manager. + LanesManager(LanesManagerError), /// Message has been treated as invalid by the pallet logic. MessageRejectedByPallet(VerificationError), - /// Submitter has failed to pay fee for delivering and dispatching messages. - FailedToWithdrawMessageFee, /// The transaction brings too many messages. TooManyMessagesInTheProof, /// Invalid messages has been submitted. @@ -488,8 +493,6 @@ pub mod pallet { /// The cumulative dispatch weight, passed by relayer is not enough to cover dispatch /// of all bundled messages. InsufficientDispatchWeight, - /// The message someone is trying to work with (i.e. increase fee) is not yet sent. - MessageIsNotYetSent, /// Error confirming messages receival. ReceptionConfirmation(ReceptionConfirmationError), /// Error generated by the `OwnedBridgeModule` trait. @@ -514,45 +517,27 @@ pub mod pallet { pub type PalletOperatingMode, I: 'static = ()> = StorageValue<_, MessagesOperatingMode, ValueQuery>; + // TODO: https://github.com/paritytech/parity-bridges-common/pull/2213: let's limit number of + // possible opened lanes && use it to constraint maps below + /// Map of lane id => inbound lane data. #[pallet::storage] pub type InboundLanes, I: 'static = ()> = - StorageMap<_, Blake2_128Concat, LaneId, StoredInboundLaneData, ValueQuery>; + StorageMap<_, Blake2_128Concat, T::LaneId, StoredInboundLaneData, OptionQuery>; /// Map of lane id => outbound lane data. #[pallet::storage] pub type OutboundLanes, I: 'static = ()> = StorageMap< Hasher = Blake2_128Concat, - Key = LaneId, + Key = T::LaneId, Value = OutboundLaneData, - QueryKind = ValueQuery, - OnEmpty = GetDefault, - MaxValues = MaybeOutboundLanesCount, - >; - - /// Map of lane id => is congested signal sent. It is managed by the - /// `bridge_runtime_common::LocalXcmQueueManager`. - /// - /// **bridges-v1**: this map is a temporary hack and will be dropped in the `v2`. We can emulate - /// a storage map using `sp_io::unhashed` storage functions, but then benchmarks are not - /// accounting its `proof_size`, so it is missing from the final weights. So we need to make it - /// a map inside some pallet. We could use a simply value instead of map here, because - /// in `v1` we'll only have a single lane. But in the case of adding another lane before `v2`, - /// it'll be easier to deal with the isolated storage map instead. - #[pallet::storage] - pub type OutboundLanesCongestedSignals, I: 'static = ()> = StorageMap< - Hasher = Blake2_128Concat, - Key = LaneId, - Value = bool, - QueryKind = ValueQuery, - OnEmpty = GetDefault, - MaxValues = MaybeOutboundLanesCount, + QueryKind = OptionQuery, >; /// All queued outbound messages. #[pallet::storage] pub type OutboundMessages, I: 'static = ()> = - StorageMap<_, Blake2_128Concat, MessageKey, StoredMessagePayload>; + StorageMap<_, Blake2_128Concat, MessageKey, StoredMessagePayload>; #[pallet::genesis_config] #[derive(DefaultNoBound)] @@ -561,8 +546,11 @@ pub mod pallet { pub operating_mode: MessagesOperatingMode, /// Initial pallet owner. pub owner: Option, + /// Opened lanes. + pub opened_lanes: Vec, /// Dummy marker. - pub phantom: sp_std::marker::PhantomData, + #[serde(skip)] + pub _phantom: sp_std::marker::PhantomData, } #[pallet::genesis_build] @@ -572,6 +560,11 @@ pub mod pallet { if let Some(ref owner) = self.owner { PalletOwner::::put(owner); } + + for lane_id in &self.opened_lanes { + InboundLanes::::insert(lane_id, InboundLaneData::opened()); + OutboundLanes::::insert(lane_id, OutboundLaneData::opened()); + } } } @@ -585,13 +578,16 @@ pub mod pallet { impl, I: 'static> Pallet { /// Get stored data of the outbound message with given nonce. - pub fn outbound_message_data(lane: LaneId, nonce: MessageNonce) -> Option { + pub fn outbound_message_data( + lane: T::LaneId, + nonce: MessageNonce, + ) -> Option { OutboundMessages::::get(MessageKey { lane_id: lane, nonce }).map(Into::into) } /// Prepare data, related to given inbound message. pub fn inbound_message_data( - lane: LaneId, + lane: T::LaneId, payload: MessagePayload, outbound_details: OutboundMessageDetails, ) -> InboundMessageDetails { @@ -605,15 +601,15 @@ pub mod pallet { } /// Return outbound lane data. - pub fn outbound_lane_data(lane: LaneId) -> OutboundLaneData { + pub fn outbound_lane_data(lane: T::LaneId) -> Option { OutboundLanes::::get(lane) } /// Return inbound lane data. pub fn inbound_lane_data( - lane: LaneId, - ) -> InboundLaneData>> { - InboundLanes::::get(lane).0 + lane: T::LaneId, + ) -> Option>>> { + InboundLanes::::get(lane).map(|lane| lane.0) } } @@ -668,26 +664,18 @@ pub mod pallet { Ok(()) } } - - /// Get-parameter that returns number of active outbound lanes that the pallet maintains. - pub struct MaybeOutboundLanesCount(PhantomData<(T, I)>); - - impl, I: 'static> Get> for MaybeOutboundLanesCount { - fn get() -> Option { - Some(T::ActiveOutboundLanes::get().len() as u32) - } - } } /// Structure, containing a validated message payload and all the info required /// to send it on the bridge. #[derive(Debug, PartialEq, Eq)] pub struct SendMessageArgs, I: 'static> { - lane_id: LaneId, + lane_id: T::LaneId, + lane: OutboundLane>, payload: StoredMessagePayload, } -impl bp_messages::source_chain::MessagesBridge for Pallet +impl bp_messages::source_chain::MessagesBridge for Pallet where T: Config, I: 'static, @@ -696,16 +684,18 @@ where type SendMessageArgs = SendMessageArgs; fn validate_message( - lane: LaneId, + lane_id: T::LaneId, message: &T::OutboundPayload, ) -> Result, Self::Error> { + // we can't accept any messages if the pallet is halted ensure_normal_operating_mode::()?; - // let's check if outbound lane is active - ensure!(T::ActiveOutboundLanes::get().contains(&lane), Error::::InactiveOutboundLane); + // check lane + let lane = active_outbound_lane::(lane_id)?; Ok(SendMessageArgs { - lane_id: lane, + lane_id, + lane, payload: StoredMessagePayload::::try_from(message.encode()).map_err(|_| { Error::::MessageRejectedByPallet(VerificationError::MessageTooLarge) })?, @@ -714,7 +704,7 @@ where fn send_message(args: SendMessageArgs) -> SendMessageArtifacts { // save message in outbound storage and emit event - let mut lane = outbound_lane::(args.lane_id); + let mut lane = args.lane; let message_len = args.payload.len(); let nonce = lane.send_message(args.payload); @@ -729,7 +719,10 @@ where message_len, ); - Pallet::::deposit_event(Event::MessageAccepted { lane_id: args.lane_id, nonce }); + Pallet::::deposit_event(Event::MessageAccepted { + lane_id: args.lane_id.into(), + nonce, + }); SendMessageArtifacts { nonce, enqueued_messages } } @@ -746,144 +739,51 @@ fn ensure_normal_operating_mode, I: 'static>() -> Result<(), Error< Err(Error::::NotOperatingNormally) } -/// Creates new inbound lane object, backed by runtime storage. -fn inbound_lane, I: 'static>( - lane_id: LaneId, -) -> InboundLane> { - InboundLane::new(RuntimeInboundLaneStorage::from_lane_id(lane_id)) +/// Creates new inbound lane object, backed by runtime storage. Lane must be active. +fn active_inbound_lane, I: 'static>( + lane_id: T::LaneId, +) -> Result>, Error> { + LanesManager::::new() + .active_inbound_lane(lane_id) + .map_err(Error::LanesManager) } -/// Creates new outbound lane object, backed by runtime storage. -fn outbound_lane, I: 'static>( - lane_id: LaneId, -) -> OutboundLane> { - OutboundLane::new(RuntimeOutboundLaneStorage { lane_id, _phantom: Default::default() }) -} - -/// Runtime inbound lane storage. -struct RuntimeInboundLaneStorage, I: 'static = ()> { - lane_id: LaneId, - cached_data: Option>>>, - _phantom: PhantomData, -} - -impl, I: 'static> RuntimeInboundLaneStorage { - /// Creates new runtime inbound lane storage. - fn from_lane_id(lane_id: LaneId) -> RuntimeInboundLaneStorage { - RuntimeInboundLaneStorage { lane_id, cached_data: None, _phantom: Default::default() } - } +/// Creates new outbound lane object, backed by runtime storage. Lane must be active. +fn active_outbound_lane, I: 'static>( + lane_id: T::LaneId, +) -> Result>, Error> { + LanesManager::::new() + .active_outbound_lane(lane_id) + .map_err(Error::LanesManager) } -impl, I: 'static> RuntimeInboundLaneStorage { - /// Returns number of bytes that may be subtracted from the PoV component of - /// `receive_messages_proof` call, because the actual inbound lane state is smaller than the - /// maximal configured. - /// - /// Maximal inbound lane state set size is configured by the - /// `MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX` constant from the pallet configuration. The PoV - /// of the call includes the maximal size of inbound lane state. If the actual size is smaller, - /// we may subtract extra bytes from this component. - pub fn extra_proof_size_bytes(&mut self) -> u64 { - let max_encoded_len = StoredInboundLaneData::::max_encoded_len(); - let relayers_count = self.get_or_init_data().relayers.len(); - let actual_encoded_len = - InboundLaneData::>>::encoded_size_hint(relayers_count) - .unwrap_or(usize::MAX); - max_encoded_len.saturating_sub(actual_encoded_len) as _ - } -} - -impl, I: 'static> InboundLaneStorage for RuntimeInboundLaneStorage { - type Relayer = AccountIdOf>; - - fn id(&self) -> LaneId { - self.lane_id - } - - fn max_unrewarded_relayer_entries(&self) -> MessageNonce { - BridgedChainOf::::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX - } - - fn max_unconfirmed_messages(&self) -> MessageNonce { - BridgedChainOf::::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX - } - - fn get_or_init_data(&mut self) -> InboundLaneData>> { - match self.cached_data { - Some(ref data) => data.clone(), - None => { - let data: InboundLaneData>> = - InboundLanes::::get(self.lane_id).into(); - self.cached_data = Some(data.clone()); - data - }, - } - } - - fn set_data(&mut self, data: InboundLaneData>>) { - self.cached_data = Some(data.clone()); - InboundLanes::::insert(self.lane_id, StoredInboundLaneData::(data)) - } -} - -/// Runtime outbound lane storage. -struct RuntimeOutboundLaneStorage { - lane_id: LaneId, - _phantom: PhantomData<(T, I)>, -} - -impl, I: 'static> OutboundLaneStorage for RuntimeOutboundLaneStorage { - type StoredMessagePayload = StoredMessagePayload; - - fn id(&self) -> LaneId { - self.lane_id - } - - fn data(&self) -> OutboundLaneData { - OutboundLanes::::get(self.lane_id) - } - - fn set_data(&mut self, data: OutboundLaneData) { - OutboundLanes::::insert(self.lane_id, data) - } - - #[cfg(test)] - fn message(&self, nonce: &MessageNonce) -> Option { - OutboundMessages::::get(MessageKey { lane_id: self.lane_id, nonce: *nonce }) - } - - fn save_message(&mut self, nonce: MessageNonce, message_payload: Self::StoredMessagePayload) { - OutboundMessages::::insert( - MessageKey { lane_id: self.lane_id, nonce }, - message_payload, - ); - } - - fn remove_message(&mut self, nonce: &MessageNonce) { - OutboundMessages::::remove(MessageKey { lane_id: self.lane_id, nonce: *nonce }); - } +/// Creates new outbound lane object, backed by runtime storage. +fn any_state_outbound_lane, I: 'static>( + lane_id: T::LaneId, +) -> Result>, Error> { + LanesManager::::new() + .any_state_outbound_lane(lane_id) + .map_err(Error::LanesManager) } /// Verify messages proof and return proved messages with decoded payload. fn verify_and_decode_messages_proof, I: 'static>( - proof: FromBridgedChainMessagesProof>>, + proof: FromBridgedChainMessagesProof>, T::LaneId>, messages_count: u32, -) -> Result>, VerificationError> { +) -> Result< + ProvedMessages>, + VerificationError, +> { // `receive_messages_proof` weight formula and `MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX` // check guarantees that the `message_count` is sane and Vec may be allocated. // (tx with too many messages will either be rejected from the pool, or will fail earlier) - proofs::verify_messages_proof::(proof, messages_count).map(|messages_by_lane| { - messages_by_lane - .into_iter() - .map(|(lane, lane_data)| { - ( - lane, - ProvedLaneMessages { - lane_state: lane_data.lane_state, - messages: lane_data.messages.into_iter().map(Into::into).collect(), - }, - ) - }) - .collect() + proofs::verify_messages_proof::(proof, messages_count).map(|(lane, lane_data)| { + ( + lane, + ProvedLaneMessages { + lane_state: lane_data.lane_state, + messages: lane_data.messages.into_iter().map(Into::into).collect(), + }, + ) }) } diff --git a/bridges/modules/messages/src/migration.rs b/bridges/modules/messages/src/migration.rs new file mode 100644 index 0000000000000000000000000000000000000000..dc9a8119079ef11d5d06736b49363f7194c8b763 --- /dev/null +++ b/bridges/modules/messages/src/migration.rs @@ -0,0 +1,146 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! A module that is responsible for migration of storage. + +use crate::{Config, Pallet}; +use frame_support::{ + traits::{Get, StorageVersion}, + weights::Weight, +}; + +/// The in-code storage version. +pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); + +/// This module contains data structures that are valid for the initial state of `0`. +/// (used with v1 migration). +pub mod v0 { + use super::Config; + use crate::BridgedChainOf; + use bp_messages::{MessageNonce, UnrewardedRelayer}; + use bp_runtime::AccountIdOf; + use codec::{Decode, Encode}; + use sp_std::collections::vec_deque::VecDeque; + + #[derive(Encode, Decode, Clone, PartialEq, Eq)] + pub(crate) struct StoredInboundLaneData, I: 'static>( + pub(crate) InboundLaneData>>, + ); + #[derive(Encode, Decode, Clone, PartialEq, Eq)] + pub(crate) struct InboundLaneData { + pub(crate) relayers: VecDeque>, + pub(crate) last_confirmed_nonce: MessageNonce, + } + #[derive(Encode, Decode, Clone, PartialEq, Eq)] + pub(crate) struct OutboundLaneData { + pub(crate) oldest_unpruned_nonce: MessageNonce, + pub(crate) latest_received_nonce: MessageNonce, + pub(crate) latest_generated_nonce: MessageNonce, + } +} + +/// This migration to `1` updates the metadata of `InboundLanes` and `OutboundLanes` to the new +/// structures. +pub mod v1 { + use super::*; + use crate::{ + InboundLaneData, InboundLanes, OutboundLaneData, OutboundLanes, StoredInboundLaneData, + }; + use bp_messages::LaneState; + use frame_support::traits::UncheckedOnRuntimeUpgrade; + use sp_std::marker::PhantomData; + + /// Migrates the pallet storage to v1. + pub struct UncheckedMigrationV0ToV1(PhantomData<(T, I)>); + + impl, I: 'static> UncheckedOnRuntimeUpgrade for UncheckedMigrationV0ToV1 { + fn on_runtime_upgrade() -> Weight { + let mut weight = T::DbWeight::get().reads(1); + + // `InboundLanes` - add state to the old structs + let translate_inbound = + |pre: v0::StoredInboundLaneData| -> Option> { + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + Some(v1::StoredInboundLaneData(v1::InboundLaneData { + state: LaneState::Opened, + relayers: pre.0.relayers, + last_confirmed_nonce: pre.0.last_confirmed_nonce, + })) + }; + InboundLanes::::translate_values(translate_inbound); + + // `OutboundLanes` - add state to the old structs + let translate_outbound = |pre: v0::OutboundLaneData| -> Option { + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + Some(v1::OutboundLaneData { + state: LaneState::Opened, + oldest_unpruned_nonce: pre.oldest_unpruned_nonce, + latest_received_nonce: pre.latest_received_nonce, + latest_generated_nonce: pre.latest_generated_nonce, + }) + }; + OutboundLanes::::translate_values(translate_outbound); + + weight + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::DispatchError> { + use codec::Encode; + + let number_of_inbound_to_migrate = InboundLanes::::iter_keys().count(); + let number_of_outbound_to_migrate = OutboundLanes::::iter_keys().count(); + Ok((number_of_inbound_to_migrate as u32, number_of_outbound_to_migrate as u32).encode()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: sp_std::vec::Vec) -> Result<(), sp_runtime::DispatchError> { + use codec::Decode; + const LOG_TARGET: &str = "runtime::bridge-messages-migration"; + + let (number_of_inbound_to_migrate, number_of_outbound_to_migrate): (u32, u32) = + Decode::decode(&mut &state[..]).unwrap(); + let number_of_inbound = InboundLanes::::iter_keys().count(); + let number_of_outbound = OutboundLanes::::iter_keys().count(); + + log::info!(target: LOG_TARGET, "post-upgrade expects '{number_of_inbound_to_migrate}' inbound lanes to have been migrated."); + log::info!(target: LOG_TARGET, "post-upgrade expects '{number_of_outbound_to_migrate}' outbound lanes to have been migrated."); + + frame_support::ensure!( + number_of_inbound_to_migrate as usize == number_of_inbound, + "must migrate all `InboundLanes`." + ); + frame_support::ensure!( + number_of_outbound_to_migrate as usize == number_of_outbound, + "must migrate all `OutboundLanes`." + ); + + log::info!(target: LOG_TARGET, "migrated all."); + Ok(()) + } + } + + /// [`UncheckedMigrationV0ToV1`] wrapped in a + /// [`VersionedMigration`](frame_support::migrations::VersionedMigration), ensuring the + /// migration is only performed when on-chain version is 0. + pub type MigrationToV1 = frame_support::migrations::VersionedMigration< + 0, + 1, + UncheckedMigrationV0ToV1, + Pallet, + ::DbWeight, + >; +} diff --git a/bridges/modules/messages/src/outbound_lane.rs b/bridges/modules/messages/src/outbound_lane.rs index 788a13e82b1b51f02a171288f60c839b3bcd7d3a..c72713e7455a648622e9432a5c0c7bbd29b118b7 100644 --- a/bridges/modules/messages/src/outbound_lane.rs +++ b/bridges/modules/messages/src/outbound_lane.rs @@ -19,20 +19,24 @@ use crate::{Config, LOG_TARGET}; use bp_messages::{ - ChainWithMessages, DeliveredMessages, LaneId, MessageNonce, OutboundLaneData, UnrewardedRelayer, + ChainWithMessages, DeliveredMessages, LaneState, MessageNonce, OutboundLaneData, + UnrewardedRelayer, }; use codec::{Decode, Encode}; use frame_support::{traits::Get, BoundedVec, PalletError}; use scale_info::TypeInfo; use sp_runtime::RuntimeDebug; -use sp_std::{collections::vec_deque::VecDeque, marker::PhantomData}; +use sp_std::{collections::vec_deque::VecDeque, marker::PhantomData, ops::RangeInclusive}; /// Outbound lane storage. pub trait OutboundLaneStorage { + /// Stored message payload type. type StoredMessagePayload; + /// Lane identifier type. + type LaneId: Encode; /// Lane id. - fn id(&self) -> LaneId; + fn id(&self) -> Self::LaneId; /// Get lane data from the storage. fn data(&self) -> OutboundLaneData; /// Update lane data in the storage. @@ -44,6 +48,8 @@ pub trait OutboundLaneStorage { fn save_message(&mut self, nonce: MessageNonce, message_payload: Self::StoredMessagePayload); /// Remove outbound message from the storage. fn remove_message(&mut self, nonce: &MessageNonce); + /// Purge lane data from the storage. + fn purge(self); } /// Limit for the `StoredMessagePayload` vector. @@ -75,6 +81,7 @@ pub enum ReceptionConfirmationError { } /// Outbound messages lane. +#[derive(Debug, PartialEq, Eq)] pub struct OutboundLane { storage: S, } @@ -90,6 +97,24 @@ impl OutboundLane { self.storage.data() } + /// Get lane state. + pub fn state(&self) -> LaneState { + self.storage.data().state + } + + /// Set lane state. + pub fn set_state(&mut self, state: LaneState) { + let mut data = self.storage.data(); + data.state = state; + self.storage.set_data(data); + } + + /// Return nonces of all currently queued messages. + pub fn queued_messages(&self) -> RangeInclusive { + let data = self.storage.data(); + data.oldest_unpruned_nonce..=data.latest_generated_nonce + } + /// Send message over lane. /// /// Returns new message nonce. @@ -150,6 +175,19 @@ impl OutboundLane { Ok(Some(confirmed_messages)) } + + /// Remove message from the storage. Doesn't perform any checks. + pub fn remove_oldest_unpruned_message(&mut self) { + let mut data = self.storage.data(); + self.storage.remove_message(&data.oldest_unpruned_nonce); + data.oldest_unpruned_nonce += 1; + self.storage.set_data(data); + } + + /// Purge lane state from the storage. + pub fn purge(self) { + self.storage.purge() + } } /// Verifies unrewarded relayers vec. @@ -187,10 +225,10 @@ fn ensure_unrewarded_relayers_are_correct( mod tests { use super::*; use crate::{ - outbound_lane, + active_outbound_lane, tests::mock::{ - outbound_message_data, run_test, unrewarded_relayer, TestRelayer, TestRuntime, - REGULAR_PAYLOAD, TEST_LANE_ID, + outbound_message_data, run_test, test_lane_id, unrewarded_relayer, TestRelayer, + TestRuntime, REGULAR_PAYLOAD, }, }; use sp_std::ops::RangeInclusive; @@ -212,7 +250,7 @@ mod tests { relayers: &VecDeque>, ) -> Result, ReceptionConfirmationError> { run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); + let mut lane = active_outbound_lane::(test_lane_id()).unwrap(); lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); @@ -228,7 +266,7 @@ mod tests { #[test] fn send_message_works() { run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); + let mut lane = active_outbound_lane::(test_lane_id()).unwrap(); assert_eq!(lane.storage.data().latest_generated_nonce, 0); assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 1); assert!(lane.storage.message(&1).is_some()); @@ -239,7 +277,7 @@ mod tests { #[test] fn confirm_delivery_works() { run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); + let mut lane = active_outbound_lane::(test_lane_id()).unwrap(); assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 1); assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 2); assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 3); @@ -259,7 +297,7 @@ mod tests { #[test] fn confirm_partial_delivery_works() { run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); + let mut lane = active_outbound_lane::(test_lane_id()).unwrap(); assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 1); assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 2); assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 3); @@ -288,7 +326,7 @@ mod tests { #[test] fn confirm_delivery_rejects_nonce_lesser_than_latest_received() { run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); + let mut lane = active_outbound_lane::(test_lane_id()).unwrap(); lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); @@ -368,7 +406,7 @@ mod tests { #[test] fn confirm_delivery_detects_when_more_than_expected_messages_are_confirmed() { run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); + let mut lane = active_outbound_lane::(test_lane_id()).unwrap(); lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); diff --git a/bridges/modules/messages/src/proofs.rs b/bridges/modules/messages/src/proofs.rs index a3318833fa611a6515469172dcc24f114bbbf4ad..dcd642341d7763d52bba1f1023b91ec241aa3c24 100644 --- a/bridges/modules/messages/src/proofs.rs +++ b/bridges/modules/messages/src/proofs.rs @@ -22,7 +22,7 @@ use bp_header_chain::{HeaderChain, HeaderChainError}; use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, target_chain::{FromBridgedChainMessagesProof, ProvedLaneMessages, ProvedMessages}, - ChainWithMessages, InboundLaneData, LaneId, Message, MessageKey, MessageNonce, MessagePayload, + ChainWithMessages, InboundLaneData, Message, MessageKey, MessageNonce, MessagePayload, OutboundLaneData, VerificationError, }; use bp_runtime::{ @@ -32,8 +32,8 @@ use codec::Decode; use sp_std::vec::Vec; /// 'Parsed' message delivery proof - inbound lane id and its state. -pub(crate) type ParsedMessagesDeliveryProofFromBridgedChain = - (LaneId, InboundLaneData<::AccountId>); +pub(crate) type ParsedMessagesDeliveryProofFromBridgedChain = + (>::LaneId, InboundLaneData<::AccountId>); /// Verify proof of Bridged -> This chain messages. /// @@ -44,9 +44,9 @@ pub(crate) type ParsedMessagesDeliveryProofFromBridgedChain = /// outside of this function. This function only verifies that the proof declares exactly /// `messages_count` messages. pub fn verify_messages_proof, I: 'static>( - proof: FromBridgedChainMessagesProof>>, + proof: FromBridgedChainMessagesProof>, T::LaneId>, messages_count: u32, -) -> Result, VerificationError> { +) -> Result>, VerificationError> { let FromBridgedChainMessagesProof { bridged_header_hash, storage_proof, @@ -98,17 +98,13 @@ pub fn verify_messages_proof, I: 'static>( // Check that the storage proof doesn't have any untouched keys. parser.ensure_no_unused_keys().map_err(VerificationError::StorageProof)?; - // We only support single lane messages in this generated_schema - let mut proved_messages = ProvedMessages::new(); - proved_messages.insert(lane, proved_lane_messages); - - Ok(proved_messages) + Ok((lane, proved_lane_messages)) } /// Verify proof of This -> Bridged chain messages delivery. pub fn verify_messages_delivery_proof, I: 'static>( - proof: FromBridgedChainMessagesDeliveryProof>>, -) -> Result, VerificationError> { + proof: FromBridgedChainMessagesDeliveryProof>, T::LaneId>, +) -> Result, VerificationError> { let FromBridgedChainMessagesDeliveryProof { bridged_header_hash, storage_proof, lane } = proof; let mut parser: MessagesStorageProofAdapter = MessagesStorageProofAdapter::try_new_with_verified_storage_proof( @@ -147,7 +143,7 @@ trait StorageProofAdapter, I: 'static> { fn read_and_decode_outbound_lane_data( &mut self, - lane_id: &LaneId, + lane_id: &T::LaneId, ) -> Result, StorageProofError> { let storage_outbound_lane_data_key = bp_messages::storage_keys::outbound_lane_data_key( T::ThisChain::WITH_CHAIN_MESSAGES_PALLET_NAME, @@ -158,7 +154,7 @@ trait StorageProofAdapter, I: 'static> { fn read_and_decode_message_payload( &mut self, - message_key: &MessageKey, + message_key: &MessageKey, ) -> Result { let storage_message_key = bp_messages::storage_keys::message_key( T::ThisChain::WITH_CHAIN_MESSAGES_PALLET_NAME, @@ -220,7 +216,8 @@ mod tests { mock::*, }; - use bp_header_chain::StoredHeaderDataBuilder; + use bp_header_chain::{HeaderChainError, StoredHeaderDataBuilder}; + use bp_messages::LaneState; use bp_runtime::{HeaderId, StorageProofError}; use codec::Encode; use sp_runtime::traits::Header; @@ -232,19 +229,20 @@ mod tests { encode_outbound_lane_data: impl Fn(&OutboundLaneData) -> Vec, add_duplicate_key: bool, add_unused_key: bool, - test: impl Fn(FromBridgedChainMessagesProof) -> R, + test: impl Fn(FromBridgedChainMessagesProof) -> R, ) -> R { - let (state_root, storage_proof) = prepare_messages_storage_proof::( - TEST_LANE_ID, - 1..=nonces_end, - outbound_lane_data, - bp_runtime::UnverifiedStorageProofParams::default(), - generate_dummy_message, - encode_message, - encode_outbound_lane_data, - add_duplicate_key, - add_unused_key, - ); + let (state_root, storage_proof) = + prepare_messages_storage_proof::( + test_lane_id(), + 1..=nonces_end, + outbound_lane_data, + bp_runtime::UnverifiedStorageProofParams::default(), + generate_dummy_message, + encode_message, + encode_outbound_lane_data, + add_duplicate_key, + add_unused_key, + ); sp_io::TestExternalities::new(Default::default()).execute_with(move || { let bridged_header = BridgedChainHeader::new( @@ -267,7 +265,7 @@ mod tests { test(FromBridgedChainMessagesProof { bridged_header_hash, storage_proof, - lane: TEST_LANE_ID, + lane: test_lane_id(), nonces_start: 1, nonces_end, }) @@ -440,6 +438,7 @@ mod tests { using_messages_proof( 10, Some(OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: 1, latest_received_nonce: 1, latest_generated_nonce: 1, @@ -480,6 +479,7 @@ mod tests { using_messages_proof( 0, Some(OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: 1, latest_received_nonce: 1, latest_generated_nonce: 1, @@ -490,19 +490,18 @@ mod tests { false, |proof| verify_messages_proof::(proof, 0), ), - Ok(vec![( - TEST_LANE_ID, + Ok(( + test_lane_id(), ProvedLaneMessages { lane_state: Some(OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: 1, latest_received_nonce: 1, latest_generated_nonce: 1, }), messages: Vec::new(), }, - )] - .into_iter() - .collect()), + )), ); } @@ -512,6 +511,7 @@ mod tests { using_messages_proof( 1, Some(OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: 1, latest_received_nonce: 1, latest_generated_nonce: 1, @@ -522,22 +522,21 @@ mod tests { false, |proof| verify_messages_proof::(proof, 1), ), - Ok(vec![( - TEST_LANE_ID, + Ok(( + test_lane_id(), ProvedLaneMessages { lane_state: Some(OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: 1, latest_received_nonce: 1, latest_generated_nonce: 1, }), messages: vec![Message { - key: MessageKey { lane_id: TEST_LANE_ID, nonce: 1 }, + key: MessageKey { lane_id: test_lane_id(), nonce: 1 }, payload: vec![42], }], }, - )] - .into_iter() - .collect()), + )) ); } diff --git a/bridges/modules/messages/src/tests/messages_generation.rs b/bridges/modules/messages/src/tests/messages_generation.rs index 6c4867fa6de39fa97b35cbd1f270bdeff4c76dff..00b1d3eefe43b176b31bf4b13e426a6bcb5319e3 100644 --- a/bridges/modules/messages/src/tests/messages_generation.rs +++ b/bridges/modules/messages/src/tests/messages_generation.rs @@ -17,8 +17,8 @@ //! Helpers for generating message storage proofs, that are used by tests and by benchmarks. use bp_messages::{ - storage_keys, ChainWithMessages, InboundLaneData, LaneId, MessageKey, MessageNonce, - MessagePayload, OutboundLaneData, + storage_keys, ChainWithMessages, InboundLaneData, MessageKey, MessageNonce, MessagePayload, + OutboundLaneData, }; use bp_runtime::{ grow_storage_value, record_all_trie_keys, AccountIdOf, Chain, HashOf, HasherOf, @@ -47,7 +47,11 @@ pub fn encode_lane_data(d: &OutboundLaneData) -> Vec { /// /// Returns state trie root and nodes with prepared messages. #[allow(clippy::too_many_arguments)] -pub fn prepare_messages_storage_proof( +pub fn prepare_messages_storage_proof< + BridgedChain: Chain, + ThisChain: ChainWithMessages, + LaneId: Encode + Copy, +>( lane: LaneId, message_nonces: RangeInclusive, outbound_lane_data: Option, @@ -132,7 +136,11 @@ where /// Prepare storage proof of given messages delivery. /// /// Returns state trie root and nodes with prepared messages. -pub fn prepare_message_delivery_storage_proof( +pub fn prepare_message_delivery_storage_proof< + BridgedChain: Chain, + ThisChain: ChainWithMessages, + LaneId: Encode, +>( lane: LaneId, inbound_lane_data: InboundLaneData>, proof_params: UnverifiedStorageProofParams, diff --git a/bridges/modules/messages/src/tests/mock.rs b/bridges/modules/messages/src/tests/mock.rs index 99da019dc0847d0787c30ada8e614270e83ddfdb..2935ebd69610f6138c18c22d2c548e2f670487a2 100644 --- a/bridges/modules/messages/src/tests/mock.rs +++ b/bridges/modules/messages/src/tests/mock.rs @@ -35,15 +35,16 @@ use bp_messages::{ DeliveryPayments, DispatchMessage, DispatchMessageData, FromBridgedChainMessagesProof, MessageDispatch, }, - ChainWithMessages, DeliveredMessages, InboundLaneData, LaneId, Message, MessageKey, - MessageNonce, OutboundLaneData, UnrewardedRelayer, UnrewardedRelayersState, + ChainWithMessages, DeliveredMessages, HashedLaneId, InboundLaneData, LaneIdType, LaneState, + Message, MessageKey, MessageNonce, OutboundLaneData, UnrewardedRelayer, + UnrewardedRelayersState, }; use bp_runtime::{ messages::MessageDispatchResult, Chain, ChainId, Size, UnverifiedStorageProofParams, }; use codec::{Decode, Encode}; use frame_support::{ - derive_impl, parameter_types, + derive_impl, weights::{constants::RocksDbWeight, Weight}, }; use scale_info::TypeInfo; @@ -183,12 +184,6 @@ impl pallet_bridge_grandpa::Config for TestRuntime { type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; } -parameter_types! { - pub const MaxMessagesToPruneAtOnce: u64 = 10; - pub const TestBridgedChainId: bp_runtime::ChainId = *b"test"; - pub const ActiveOutboundLanes: &'static [LaneId] = &[TEST_LANE_ID, TEST_LANE_ID_2]; -} - /// weights of messages pallet calls we use in tests. pub type TestWeightInfo = (); @@ -200,13 +195,11 @@ impl Config for TestRuntime { type BridgedChain = BridgedChain; type BridgedHeaderChain = BridgedChainGrandpa; - type ActiveOutboundLanes = ActiveOutboundLanes; - type OutboundPayload = TestPayload; - type InboundPayload = TestPayload; - type DeliveryPayments = TestDeliveryPayments; + type LaneId = TestLaneIdType; + type DeliveryPayments = TestDeliveryPayments; type DeliveryConfirmationPayments = TestDeliveryConfirmationPayments; type OnMessagesDelivered = TestOnMessagesDelivered; @@ -215,13 +208,13 @@ impl Config for TestRuntime { #[cfg(feature = "runtime-benchmarks")] impl crate::benchmarking::Config<()> for TestRuntime { - fn bench_lane_id() -> LaneId { - TEST_LANE_ID + fn bench_lane_id() -> Self::LaneId { + test_lane_id() } fn prepare_message_proof( - params: crate::benchmarking::MessageProofParams, - ) -> (FromBridgedChainMessagesProof, Weight) { + params: crate::benchmarking::MessageProofParams, + ) -> (FromBridgedChainMessagesProof, Weight) { use bp_runtime::RangeInclusiveExt; let dispatch_weight = @@ -236,8 +229,8 @@ impl crate::benchmarking::Config<()> for TestRuntime { } fn prepare_message_delivery_proof( - params: crate::benchmarking::MessageDeliveryProofParams, - ) -> FromBridgedChainMessagesDeliveryProof { + params: crate::benchmarking::MessageDeliveryProofParams, + ) -> FromBridgedChainMessagesDeliveryProof { // in mock run we only care about benchmarks correctness, not the benchmark results // => ignore size related arguments prepare_messages_delivery_proof(params.lane, params.inbound_lane_data) @@ -266,14 +259,22 @@ pub const TEST_RELAYER_B: AccountId = 101; /// Account id of additional test relayer - C. pub const TEST_RELAYER_C: AccountId = 102; +/// Lane identifier type used for tests. +pub type TestLaneIdType = HashedLaneId; /// Lane that we're using in tests. -pub const TEST_LANE_ID: LaneId = LaneId([0, 0, 0, 1]); +pub fn test_lane_id() -> TestLaneIdType { + TestLaneIdType::try_new(1, 2).unwrap() +} -/// Secondary lane that we're using in tests. -pub const TEST_LANE_ID_2: LaneId = LaneId([0, 0, 0, 2]); +/// Lane that is completely unknown to our runtime. +pub fn unknown_lane_id() -> TestLaneIdType { + TestLaneIdType::try_new(1, 3).unwrap() +} -/// Inactive outbound lane. -pub const TEST_LANE_ID_3: LaneId = LaneId([0, 0, 0, 3]); +/// Lane that is registered, but it is closed. +pub fn closed_lane_id() -> TestLaneIdType { + TestLaneIdType::try_new(1, 4).unwrap() +} /// Regular message payload. pub const REGULAR_PAYLOAD: TestPayload = message_payload(0, 50); @@ -318,11 +319,11 @@ impl TestDeliveryConfirmationPayments { } } -impl DeliveryConfirmationPayments for TestDeliveryConfirmationPayments { +impl DeliveryConfirmationPayments for TestDeliveryConfirmationPayments { type Error = &'static str; fn pay_reward( - _lane_id: LaneId, + _lane_id: TestLaneIdType, messages_relayers: VecDeque>, _confirmation_relayer: &AccountId, received_range: &RangeInclusive, @@ -343,22 +344,33 @@ impl DeliveryConfirmationPayments for TestDeliveryConfirmationPayment pub struct TestMessageDispatch; impl TestMessageDispatch { - pub fn deactivate() { - frame_support::storage::unhashed::put(b"TestMessageDispatch.IsCongested", &true) + pub fn deactivate(lane: TestLaneIdType) { + // "enqueue" enough (to deactivate dispatcher) messages at dispatcher + let latest_received_nonce = BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX + 1; + for _ in 1..=latest_received_nonce { + Self::emulate_enqueued_message(lane); + } + } + + pub fn emulate_enqueued_message(lane: TestLaneIdType) { + let key = (b"dispatched", lane).encode(); + let dispatched = frame_support::storage::unhashed::get_or_default::(&key[..]); + frame_support::storage::unhashed::put(&key[..], &(dispatched + 1)); } } impl MessageDispatch for TestMessageDispatch { type DispatchPayload = TestPayload; type DispatchLevelResult = TestDispatchLevelResult; + type LaneId = TestLaneIdType; - fn is_active() -> bool { - !frame_support::storage::unhashed::get_or_default::( - b"TestMessageDispatch.IsCongested", - ) + fn is_active(lane: Self::LaneId) -> bool { + frame_support::storage::unhashed::get_or_default::( + &(b"dispatched", lane).encode()[..], + ) <= BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX } - fn dispatch_weight(message: &mut DispatchMessage) -> Weight { + fn dispatch_weight(message: &mut DispatchMessage) -> Weight { match message.data.payload.as_ref() { Ok(payload) => payload.declared_weight, Err(_) => Weight::zero(), @@ -366,10 +378,13 @@ impl MessageDispatch for TestMessageDispatch { } fn dispatch( - message: DispatchMessage, + message: DispatchMessage, ) -> MessageDispatchResult { match message.data.payload.as_ref() { - Ok(payload) => payload.dispatch_result.clone(), + Ok(payload) => { + Self::emulate_enqueued_message(message.key.lane_id); + payload.dispatch_result.clone() + }, Err(_) => dispatch_result(0), } } @@ -379,13 +394,13 @@ impl MessageDispatch for TestMessageDispatch { pub struct TestOnMessagesDelivered; impl TestOnMessagesDelivered { - pub fn call_arguments() -> Option<(LaneId, MessageNonce)> { + pub fn call_arguments() -> Option<(TestLaneIdType, MessageNonce)> { frame_support::storage::unhashed::get(b"TestOnMessagesDelivered.OnMessagesDelivered") } } -impl OnMessagesDelivered for TestOnMessagesDelivered { - fn on_messages_delivered(lane: LaneId, enqueued_messages: MessageNonce) { +impl OnMessagesDelivered for TestOnMessagesDelivered { + fn on_messages_delivered(lane: TestLaneIdType, enqueued_messages: MessageNonce) { frame_support::storage::unhashed::put( b"TestOnMessagesDelivered.OnMessagesDelivered", &(lane, enqueued_messages), @@ -394,8 +409,8 @@ impl OnMessagesDelivered for TestOnMessagesDelivered { } /// Return test lane message with given nonce and payload. -pub fn message(nonce: MessageNonce, payload: TestPayload) -> Message { - Message { key: MessageKey { lane_id: TEST_LANE_ID, nonce }, payload: payload.encode() } +pub fn message(nonce: MessageNonce, payload: TestPayload) -> Message { + Message { key: MessageKey { lane_id: test_lane_id(), nonce }, payload: payload.encode() } } /// Return valid outbound message data, constructed from given payload. @@ -438,8 +453,8 @@ pub fn unrewarded_relayer( } /// Returns unrewarded relayers state at given lane. -pub fn inbound_unrewarded_relayers_state(lane: bp_messages::LaneId) -> UnrewardedRelayersState { - let inbound_lane_data = crate::InboundLanes::::get(lane).0; +pub fn inbound_unrewarded_relayers_state(lane: TestLaneIdType) -> UnrewardedRelayersState { + let inbound_lane_data = crate::InboundLanes::::get(lane).unwrap().0; UnrewardedRelayersState::from(&inbound_lane_data) } @@ -454,7 +469,19 @@ pub fn new_test_ext() -> sp_io::TestExternalities { /// Run pallet test. pub fn run_test(test: impl FnOnce() -> T) -> T { - new_test_ext().execute_with(test) + new_test_ext().execute_with(|| { + crate::InboundLanes::::insert(test_lane_id(), InboundLaneData::opened()); + crate::OutboundLanes::::insert(test_lane_id(), OutboundLaneData::opened()); + crate::InboundLanes::::insert( + closed_lane_id(), + InboundLaneData { state: LaneState::Closed, ..Default::default() }, + ); + crate::OutboundLanes::::insert( + closed_lane_id(), + OutboundLaneData { state: LaneState::Closed, ..Default::default() }, + ); + test() + }) } /// Prepare valid storage proof for given messages and insert appropriate header to the @@ -463,24 +490,25 @@ pub fn run_test(test: impl FnOnce() -> T) -> T { /// Since this function changes the runtime storage, you can't "inline" it in the /// `asset_noop` macro calls. pub fn prepare_messages_proof( - messages: Vec, + messages: Vec>, outbound_lane_data: Option, -) -> Box> { +) -> Box> { // first - let's generate storage proof let lane = messages.first().unwrap().key.lane_id; let nonces_start = messages.first().unwrap().key.nonce; let nonces_end = messages.last().unwrap().key.nonce; - let (storage_root, storage_proof) = prepare_messages_storage_proof::( - TEST_LANE_ID, - nonces_start..=nonces_end, - outbound_lane_data, - UnverifiedStorageProofParams::default(), - |nonce| messages[(nonce - nonces_start) as usize].payload.clone(), - encode_all_messages, - encode_lane_data, - false, - false, - ); + let (storage_root, storage_proof) = + prepare_messages_storage_proof::( + lane, + nonces_start..=nonces_end, + outbound_lane_data, + UnverifiedStorageProofParams::default(), + |nonce| messages[(nonce - nonces_start) as usize].payload.clone(), + encode_all_messages, + encode_lane_data, + false, + false, + ); // let's now insert bridged chain header into the storage let bridged_header_hash = Default::default(); @@ -489,7 +517,7 @@ pub fn prepare_messages_proof( StoredHeaderData { number: 0, state_root: storage_root }, ); - Box::new(FromBridgedChainMessagesProof:: { + Box::new(FromBridgedChainMessagesProof:: { bridged_header_hash, storage_proof, lane, @@ -504,12 +532,12 @@ pub fn prepare_messages_proof( /// Since this function changes the runtime storage, you can't "inline" it in the /// `asset_noop` macro calls. pub fn prepare_messages_delivery_proof( - lane: LaneId, + lane: TestLaneIdType, inbound_lane_data: InboundLaneData, -) -> FromBridgedChainMessagesDeliveryProof { +) -> FromBridgedChainMessagesDeliveryProof { // first - let's generate storage proof let (storage_root, storage_proof) = - prepare_message_delivery_storage_proof::( + prepare_message_delivery_storage_proof::( lane, inbound_lane_data, UnverifiedStorageProofParams::default(), @@ -522,7 +550,7 @@ pub fn prepare_messages_delivery_proof( StoredHeaderData { number: 0, state_root: storage_root }, ); - FromBridgedChainMessagesDeliveryProof:: { + FromBridgedChainMessagesDeliveryProof:: { bridged_header_hash, storage_proof, lane, diff --git a/bridges/modules/messages/src/tests/pallet_tests.rs b/bridges/modules/messages/src/tests/pallet_tests.rs index f7a288d649a936b8931ea3ac629b0757927debba..9df103a7cf6f1b6173dd264733d298d4a68ab7df 100644 --- a/bridges/modules/messages/src/tests/pallet_tests.rs +++ b/bridges/modules/messages/src/tests/pallet_tests.rs @@ -17,20 +17,20 @@ //! Pallet-level tests. use crate::{ - outbound_lane, + active_outbound_lane, + lanes_manager::RuntimeInboundLaneStorage, outbound_lane::ReceptionConfirmationError, - tests::mock::{self, RuntimeEvent as TestEvent, *}, + tests::mock::{RuntimeEvent as TestEvent, *}, weights_ext::WeightInfoExt, - Call, Config, Error, Event, InboundLanes, MaybeOutboundLanesCount, OutboundLanes, - OutboundMessages, Pallet, PalletOperatingMode, PalletOwner, RuntimeInboundLaneStorage, - StoredInboundLaneData, + Call, Config, Error, Event, InboundLanes, LanesManagerError, OutboundLanes, OutboundMessages, + Pallet, PalletOperatingMode, PalletOwner, StoredInboundLaneData, }; use bp_messages::{ source_chain::{FromBridgedChainMessagesDeliveryProof, MessagesBridge}, - target_chain::FromBridgedChainMessagesProof, + target_chain::{FromBridgedChainMessagesProof, MessageDispatch}, BridgeMessagesCall, ChainWithMessages, DeliveredMessages, InboundLaneData, - InboundMessageDetails, LaneId, MessageKey, MessageNonce, MessagesOperatingMode, + InboundMessageDetails, LaneIdType, LaneState, MessageKey, MessageNonce, MessagesOperatingMode, OutboundLaneData, OutboundMessageDetails, UnrewardedRelayer, UnrewardedRelayersState, VerificationError, }; @@ -44,7 +44,6 @@ use frame_support::{ weights::Weight, }; use frame_system::{EventRecord, Pallet as System, Phase}; -use sp_core::Get; use sp_runtime::{BoundedVec, DispatchError}; fn get_ready_for_events() { @@ -52,10 +51,10 @@ fn get_ready_for_events() { System::::reset_events(); } -fn send_regular_message(lane_id: LaneId) { +fn send_regular_message(lane_id: TestLaneIdType) { get_ready_for_events(); - let outbound_lane = outbound_lane::(lane_id); + let outbound_lane = active_outbound_lane::(lane_id).unwrap(); let message_nonce = outbound_lane.data().latest_generated_nonce + 1; let prev_enqueued_messages = outbound_lane.data().queued_messages().saturating_len(); let valid_message = Pallet::::validate_message(lane_id, ®ULAR_PAYLOAD) @@ -68,7 +67,10 @@ fn send_regular_message(lane_id: LaneId) { System::::events(), vec![EventRecord { phase: Phase::Initialization, - event: TestEvent::Messages(Event::MessageAccepted { lane_id, nonce: message_nonce }), + event: TestEvent::Messages(Event::MessageAccepted { + lane_id: lane_id.into(), + nonce: message_nonce + }), topics: vec![], }], ); @@ -81,8 +83,9 @@ fn receive_messages_delivery_proof() { assert_ok!(Pallet::::receive_messages_delivery_proof( RuntimeOrigin::signed(1), prepare_messages_delivery_proof( - TEST_LANE_ID, + test_lane_id(), InboundLaneData { + state: LaneState::Opened, last_confirmed_nonce: 1, relayers: vec![UnrewardedRelayer { relayer: 0, @@ -105,7 +108,7 @@ fn receive_messages_delivery_proof() { vec![EventRecord { phase: Phase::Initialization, event: TestEvent::Messages(Event::MessagesDelivered { - lane_id: TEST_LANE_ID, + lane_id: test_lane_id().into(), messages: DeliveredMessages::new(1), }), topics: vec![], @@ -117,14 +120,14 @@ fn receive_messages_delivery_proof() { fn pallet_rejects_transactions_if_halted() { run_test(|| { // send message first to be able to check that delivery_proof fails later - send_regular_message(TEST_LANE_ID); + send_regular_message(test_lane_id()); PalletOperatingMode::::put(MessagesOperatingMode::Basic( BasicOperatingMode::Halted, )); assert_noop!( - Pallet::::validate_message(TEST_LANE_ID, ®ULAR_PAYLOAD), + Pallet::::validate_message(test_lane_id(), ®ULAR_PAYLOAD), Error::::NotOperatingNormally, ); @@ -141,8 +144,9 @@ fn pallet_rejects_transactions_if_halted() { ); let delivery_proof = prepare_messages_delivery_proof( - TEST_LANE_ID, + test_lane_id(), InboundLaneData { + state: LaneState::Opened, last_confirmed_nonce: 1, relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)].into(), }, @@ -167,7 +171,7 @@ fn pallet_rejects_transactions_if_halted() { #[test] fn receive_messages_fails_if_dispatcher_is_inactive() { run_test(|| { - TestMessageDispatch::deactivate(); + TestMessageDispatch::deactivate(test_lane_id()); let proof = prepare_messages_proof(vec![message(1, REGULAR_PAYLOAD)], None); assert_noop!( Pallet::::receive_messages_proof( @@ -177,7 +181,7 @@ fn receive_messages_fails_if_dispatcher_is_inactive() { 1, REGULAR_PAYLOAD.declared_weight, ), - Error::::MessageDispatchInactive, + Error::::LanesManager(LanesManagerError::LaneDispatcherInactive), ); }); } @@ -186,14 +190,14 @@ fn receive_messages_fails_if_dispatcher_is_inactive() { fn pallet_rejects_new_messages_in_rejecting_outbound_messages_operating_mode() { run_test(|| { // send message first to be able to check that delivery_proof fails later - send_regular_message(TEST_LANE_ID); + send_regular_message(test_lane_id()); PalletOperatingMode::::put( MessagesOperatingMode::RejectingOutboundMessages, ); assert_noop!( - Pallet::::validate_message(TEST_LANE_ID, ®ULAR_PAYLOAD), + Pallet::::validate_message(test_lane_id(), ®ULAR_PAYLOAD), Error::::NotOperatingNormally, ); @@ -208,8 +212,9 @@ fn pallet_rejects_new_messages_in_rejecting_outbound_messages_operating_mode() { assert_ok!(Pallet::::receive_messages_delivery_proof( RuntimeOrigin::signed(1), prepare_messages_delivery_proof( - TEST_LANE_ID, + test_lane_id(), InboundLaneData { + state: LaneState::Opened, last_confirmed_nonce: 1, relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)].into(), }, @@ -228,7 +233,7 @@ fn pallet_rejects_new_messages_in_rejecting_outbound_messages_operating_mode() { #[test] fn send_message_works() { run_test(|| { - send_regular_message(TEST_LANE_ID); + send_regular_message(test_lane_id()); }); } @@ -243,7 +248,7 @@ fn send_message_rejects_too_large_message() { .extra .extend_from_slice(&vec![0u8; max_outbound_payload_size as usize]); assert_noop!( - Pallet::::validate_message(TEST_LANE_ID, &message_payload.clone(),), + Pallet::::validate_message(test_lane_id(), &message_payload.clone(),), Error::::MessageRejectedByPallet(VerificationError::MessageTooLarge), ); @@ -254,7 +259,7 @@ fn send_message_rejects_too_large_message() { assert_eq!(message_payload.encoded_size() as u32, max_outbound_payload_size); let valid_message = - Pallet::::validate_message(TEST_LANE_ID, &message_payload) + Pallet::::validate_message(test_lane_id(), &message_payload) .expect("validate_message has failed"); Pallet::::send_message(valid_message); }) @@ -271,7 +276,13 @@ fn receive_messages_proof_works() { REGULAR_PAYLOAD.declared_weight, )); - assert_eq!(InboundLanes::::get(TEST_LANE_ID).0.last_delivered_nonce(), 1); + assert_eq!( + InboundLanes::::get(test_lane_id()) + .unwrap() + .0 + .last_delivered_nonce(), + 1 + ); assert!(TestDeliveryPayments::is_reward_paid(1)); }); @@ -282,8 +293,9 @@ fn receive_messages_proof_updates_confirmed_message_nonce() { run_test(|| { // say we have received 10 messages && last confirmed message is 8 InboundLanes::::insert( - TEST_LANE_ID, + test_lane_id(), InboundLaneData { + state: LaneState::Opened, last_confirmed_nonce: 8, relayers: vec![ unrewarded_relayer(9, 9, TEST_RELAYER_A), @@ -293,7 +305,7 @@ fn receive_messages_proof_updates_confirmed_message_nonce() { }, ); assert_eq!( - inbound_unrewarded_relayers_state(TEST_LANE_ID), + inbound_unrewarded_relayers_state(test_lane_id()), UnrewardedRelayersState { unrewarded_relayer_entries: 2, messages_in_oldest_entry: 1, @@ -315,8 +327,9 @@ fn receive_messages_proof_updates_confirmed_message_nonce() { )); assert_eq!( - InboundLanes::::get(TEST_LANE_ID).0, + InboundLanes::::get(test_lane_id()).unwrap().0, InboundLaneData { + state: LaneState::Opened, last_confirmed_nonce: 9, relayers: vec![ unrewarded_relayer(10, 10, TEST_RELAYER_B), @@ -326,7 +339,7 @@ fn receive_messages_proof_updates_confirmed_message_nonce() { }, ); assert_eq!( - inbound_unrewarded_relayers_state(TEST_LANE_ID), + inbound_unrewarded_relayers_state(test_lane_id()), UnrewardedRelayersState { unrewarded_relayer_entries: 2, messages_in_oldest_entry: 1, @@ -337,6 +350,86 @@ fn receive_messages_proof_updates_confirmed_message_nonce() { }); } +#[test] +fn receive_messages_proof_fails_when_dispatcher_is_inactive() { + run_test(|| { + // "enqueue" enough (to deactivate dispatcher) messages at dispatcher + let latest_received_nonce = BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX + 1; + for _ in 1..=latest_received_nonce { + TestMessageDispatch::emulate_enqueued_message(test_lane_id()); + } + assert!(!TestMessageDispatch::is_active(test_lane_id())); + InboundLanes::::insert( + test_lane_id(), + InboundLaneData { + state: LaneState::Opened, + last_confirmed_nonce: latest_received_nonce, + relayers: vec![].into(), + }, + ); + + // try to delvier next message - it should fail because dispatcher is in "suspended" state + // at the beginning of the call + let messages_proof = + prepare_messages_proof(vec![message(latest_received_nonce + 1, REGULAR_PAYLOAD)], None); + assert_noop!( + Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + messages_proof, + 1, + REGULAR_PAYLOAD.declared_weight, + ), + Error::::LanesManager(LanesManagerError::LaneDispatcherInactive) + ); + assert!(!TestMessageDispatch::is_active(test_lane_id())); + }); +} + +#[test] +fn receive_messages_succeeds_when_dispatcher_becomes_inactive_in_the_middle_of_transaction() { + run_test(|| { + // "enqueue" enough (to deactivate dispatcher) messages at dispatcher + let latest_received_nonce = BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX / 2; + for _ in 1..=latest_received_nonce { + TestMessageDispatch::emulate_enqueued_message(test_lane_id()); + } + assert!(TestMessageDispatch::is_active(test_lane_id())); + InboundLanes::::insert( + test_lane_id(), + InboundLaneData { + state: LaneState::Opened, + last_confirmed_nonce: latest_received_nonce, + relayers: vec![].into(), + }, + ); + + // try to delvier next `BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX` messages + // - it will lead to dispatcher deactivation, but the transaction shall not fail and all + // messages must be delivered + let messages_begin = latest_received_nonce + 1; + let messages_end = + messages_begin + BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + let messages_range = messages_begin..messages_end; + let messages_count = BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + assert_ok!(Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + prepare_messages_proof( + messages_range.map(|nonce| message(nonce, REGULAR_PAYLOAD)).collect(), + None, + ), + messages_count as _, + REGULAR_PAYLOAD.declared_weight * messages_count, + ),); + assert_eq!( + inbound_unrewarded_relayers_state(test_lane_id()).last_delivered_nonce, + messages_end - 1, + ); + assert!(!TestMessageDispatch::is_active(test_lane_id())); + }); +} + #[test] fn receive_messages_proof_does_not_accept_message_if_dispatch_weight_is_not_enough() { run_test(|| { @@ -354,7 +447,10 @@ fn receive_messages_proof_does_not_accept_message_if_dispatch_weight_is_not_enou ), Error::::InsufficientDispatchWeight ); - assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 0); + assert_eq!( + InboundLanes::::get(test_lane_id()).unwrap().last_delivered_nonce(), + 0 + ); }); } @@ -397,26 +493,64 @@ fn receive_messages_proof_rejects_proof_with_too_many_messages() { #[test] fn receive_messages_delivery_proof_works() { run_test(|| { - assert_eq!(OutboundLanes::::get(TEST_LANE_ID).latest_received_nonce, 0); - assert_eq!(OutboundLanes::::get(TEST_LANE_ID).oldest_unpruned_nonce, 1); + assert_eq!( + OutboundLanes::::get(test_lane_id()) + .unwrap() + .latest_received_nonce, + 0, + ); + assert_eq!( + OutboundLanes::::get(test_lane_id()) + .unwrap() + .oldest_unpruned_nonce, + 1, + ); + + send_regular_message(test_lane_id()); + receive_messages_delivery_proof(); + + assert_eq!( + OutboundLanes::::get(test_lane_id()) + .unwrap() + .latest_received_nonce, + 1, + ); + assert_eq!( + OutboundLanes::::get(test_lane_id()) + .unwrap() + .oldest_unpruned_nonce, + 2, + ); + }); +} - send_regular_message(TEST_LANE_ID); +#[test] +fn receive_messages_delivery_proof_works_on_closed_outbound_lanes() { + run_test(|| { + send_regular_message(test_lane_id()); + active_outbound_lane::(test_lane_id()) + .unwrap() + .set_state(LaneState::Closed); receive_messages_delivery_proof(); - assert_eq!(OutboundLanes::::get(TEST_LANE_ID).latest_received_nonce, 1); - assert_eq!(OutboundLanes::::get(TEST_LANE_ID).oldest_unpruned_nonce, 2); + assert_eq!( + OutboundLanes::::get(test_lane_id()) + .unwrap() + .latest_received_nonce, + 1, + ); }); } #[test] fn receive_messages_delivery_proof_rewards_relayers() { run_test(|| { - send_regular_message(TEST_LANE_ID); - send_regular_message(TEST_LANE_ID); + send_regular_message(test_lane_id()); + send_regular_message(test_lane_id()); // this reports delivery of message 1 => reward is paid to TEST_RELAYER_A let single_message_delivery_proof = prepare_messages_delivery_proof( - TEST_LANE_ID, + test_lane_id(), InboundLaneData { relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)].into(), ..Default::default() @@ -452,7 +586,7 @@ fn receive_messages_delivery_proof_rewards_relayers() { // this reports delivery of both message 1 and message 2 => reward is paid only to // TEST_RELAYER_B let two_messages_delivery_proof = prepare_messages_delivery_proof( - TEST_LANE_ID, + test_lane_id(), InboundLaneData { relayers: vec![ unrewarded_relayer(1, 1, TEST_RELAYER_A), @@ -490,15 +624,15 @@ fn receive_messages_delivery_proof_rewards_relayers() { ); assert!(!TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_A, 1)); assert!(TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_B, 1)); - assert_eq!(TestOnMessagesDelivered::call_arguments(), Some((TEST_LANE_ID, 0))); + assert_eq!(TestOnMessagesDelivered::call_arguments(), Some((test_lane_id(), 0))); }); } #[test] fn receive_messages_delivery_proof_rejects_invalid_proof() { run_test(|| { - let mut proof = prepare_messages_delivery_proof(TEST_LANE_ID, Default::default()); - proof.lane = bp_messages::LaneId([42, 42, 42, 42]); + let mut proof = prepare_messages_delivery_proof(test_lane_id(), Default::default()); + proof.lane = TestLaneIdType::try_new(42, 84).unwrap(); assert_noop!( Pallet::::receive_messages_delivery_proof( @@ -516,7 +650,7 @@ fn receive_messages_delivery_proof_rejects_proof_if_declared_relayers_state_is_i run_test(|| { // when number of relayers entries is invalid let proof = prepare_messages_delivery_proof( - TEST_LANE_ID, + test_lane_id(), InboundLaneData { relayers: vec![ unrewarded_relayer(1, 1, TEST_RELAYER_A), @@ -542,7 +676,7 @@ fn receive_messages_delivery_proof_rejects_proof_if_declared_relayers_state_is_i // when number of messages is invalid let proof = prepare_messages_delivery_proof( - TEST_LANE_ID, + test_lane_id(), InboundLaneData { relayers: vec![ unrewarded_relayer(1, 1, TEST_RELAYER_A), @@ -568,7 +702,7 @@ fn receive_messages_delivery_proof_rejects_proof_if_declared_relayers_state_is_i // when last delivered nonce is invalid let proof = prepare_messages_delivery_proof( - TEST_LANE_ID, + test_lane_id(), InboundLaneData { relayers: vec![ unrewarded_relayer(1, 1, TEST_RELAYER_A), @@ -609,7 +743,10 @@ fn receive_messages_accepts_single_message_with_invalid_payload() { * improperly encoded) */ ),); - assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 1,); + assert_eq!( + InboundLanes::::get(test_lane_id()).unwrap().last_delivered_nonce(), + 1, + ); }); } @@ -630,7 +767,10 @@ fn receive_messages_accepts_batch_with_message_with_invalid_payload() { REGULAR_PAYLOAD.declared_weight + REGULAR_PAYLOAD.declared_weight, ),); - assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 3,); + assert_eq!( + InboundLanes::::get(test_lane_id()).unwrap().last_delivered_nonce(), + 3, + ); }); } @@ -653,7 +793,10 @@ fn actual_dispatch_weight_does_not_overflow() { ), Error::::InsufficientDispatchWeight ); - assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 0); + assert_eq!( + InboundLanes::::get(test_lane_id()).unwrap().last_delivered_nonce(), + 0 + ); }); } @@ -730,8 +873,9 @@ fn proof_size_refund_from_receive_messages_proof_works() { REGULAR_PAYLOAD.declared_weight, ); InboundLanes::::insert( - TEST_LANE_ID, + test_lane_id(), StoredInboundLaneData(InboundLaneData { + state: LaneState::Opened, relayers: vec![ UnrewardedRelayer { relayer: 42, @@ -758,8 +902,9 @@ fn proof_size_refund_from_receive_messages_proof_works() { // if count of unrewarded relayer entries is less than maximal, then some `proof_size` // must be refunded InboundLanes::::insert( - TEST_LANE_ID, + test_lane_id(), StoredInboundLaneData(InboundLaneData { + state: LaneState::Opened, relayers: vec![ UnrewardedRelayer { relayer: 42, @@ -795,7 +940,7 @@ fn receive_messages_delivery_proof_rejects_proof_if_trying_to_confirm_more_messa { run_test(|| { // send message first to be able to check that delivery_proof fails later - send_regular_message(TEST_LANE_ID); + send_regular_message(test_lane_id()); // 1) InboundLaneData declares that the `last_confirmed_nonce` is 1; // 2) InboundLaneData has no entries => `InboundLaneData::last_delivered_nonce()` returns @@ -804,8 +949,12 @@ fn receive_messages_delivery_proof_rejects_proof_if_trying_to_confirm_more_messa // 4) so the number of declared messages (see `UnrewardedRelayersState`) is `0` and numer of // actually confirmed messages is `1`. let proof = prepare_messages_delivery_proof( - TEST_LANE_ID, - InboundLaneData { last_confirmed_nonce: 1, relayers: Default::default() }, + test_lane_id(), + InboundLaneData { + state: LaneState::Opened, + last_confirmed_nonce: 1, + relayers: Default::default(), + }, ); assert_noop!( Pallet::::receive_messages_delivery_proof( @@ -829,20 +978,20 @@ fn storage_keys_computed_properly() { assert_eq!( OutboundMessages::::storage_map_final_key(MessageKey { - lane_id: TEST_LANE_ID, + lane_id: test_lane_id(), nonce: 42 }), - bp_messages::storage_keys::message_key("Messages", &TEST_LANE_ID, 42).0, + bp_messages::storage_keys::message_key("Messages", &test_lane_id(), 42).0, ); assert_eq!( - OutboundLanes::::storage_map_final_key(TEST_LANE_ID), - bp_messages::storage_keys::outbound_lane_data_key("Messages", &TEST_LANE_ID).0, + OutboundLanes::::storage_map_final_key(test_lane_id()), + bp_messages::storage_keys::outbound_lane_data_key("Messages", &test_lane_id()).0, ); assert_eq!( - InboundLanes::::storage_map_final_key(TEST_LANE_ID), - bp_messages::storage_keys::inbound_lane_data_key("Messages", &TEST_LANE_ID).0, + InboundLanes::::storage_map_final_key(test_lane_id()), + bp_messages::storage_keys::inbound_lane_data_key("Messages", &test_lane_id()).0, ); } @@ -851,7 +1000,7 @@ fn inbound_message_details_works() { run_test(|| { assert_eq!( Pallet::::inbound_message_data( - TEST_LANE_ID, + test_lane_id(), REGULAR_PAYLOAD.encode(), OutboundMessageDetails { nonce: 0, dispatch_weight: Weight::zero(), size: 0 }, ), @@ -860,24 +1009,15 @@ fn inbound_message_details_works() { }); } -#[test] -fn outbound_message_from_unconfigured_lane_is_rejected() { - run_test(|| { - assert_noop!( - Pallet::::validate_message(TEST_LANE_ID_3, ®ULAR_PAYLOAD,), - Error::::InactiveOutboundLane, - ); - }); -} - #[test] fn test_bridge_messages_call_is_correctly_defined() { run_test(|| { let account_id = 1; let message_proof = prepare_messages_proof(vec![message(1, REGULAR_PAYLOAD)], None); let message_delivery_proof = prepare_messages_delivery_proof( - TEST_LANE_ID, + test_lane_id(), InboundLaneData { + state: LaneState::Opened, last_confirmed_nonce: 1, relayers: vec![UnrewardedRelayer { relayer: 0, @@ -901,8 +1041,8 @@ fn test_bridge_messages_call_is_correctly_defined() { }; let indirect_receive_messages_proof_call = BridgeMessagesCall::< AccountId, - FromBridgedChainMessagesProof, - FromBridgedChainMessagesDeliveryProof, + FromBridgedChainMessagesProof, + FromBridgedChainMessagesDeliveryProof, >::receive_messages_proof { relayer_id_at_bridged_chain: account_id, proof: *message_proof, @@ -921,8 +1061,8 @@ fn test_bridge_messages_call_is_correctly_defined() { }; let indirect_receive_messages_delivery_proof_call = BridgeMessagesCall::< AccountId, - FromBridgedChainMessagesProof, - FromBridgedChainMessagesDeliveryProof, + FromBridgedChainMessagesProof, + FromBridgedChainMessagesDeliveryProof, >::receive_messages_delivery_proof { proof: message_delivery_proof, relayers_state: unrewarded_relayer_state, @@ -947,12 +1087,12 @@ fn inbound_storage_extra_proof_size_bytes_works() { fn storage(relayer_entries: usize) -> RuntimeInboundLaneStorage { RuntimeInboundLaneStorage { - lane_id: Default::default(), - cached_data: Some(InboundLaneData { + lane_id: TestLaneIdType::try_new(1, 2).unwrap(), + cached_data: InboundLaneData { + state: LaneState::Opened, relayers: vec![relayer_entry(); relayer_entries].into(), last_confirmed_nonce: 0, - }), - _phantom: Default::default(), + }, } } @@ -977,22 +1117,99 @@ fn inbound_storage_extra_proof_size_bytes_works() { } #[test] -fn maybe_outbound_lanes_count_returns_correct_value() { - assert_eq!( - MaybeOutboundLanesCount::::get(), - Some(mock::ActiveOutboundLanes::get().len() as u32) - ); +fn send_messages_fails_if_outbound_lane_is_not_opened() { + run_test(|| { + assert_noop!( + Pallet::::validate_message(unknown_lane_id(), ®ULAR_PAYLOAD), + Error::::LanesManager(LanesManagerError::UnknownOutboundLane), + ); + + assert_noop!( + Pallet::::validate_message(closed_lane_id(), ®ULAR_PAYLOAD), + Error::::LanesManager(LanesManagerError::ClosedOutboundLane), + ); + }); +} + +#[test] +fn receive_messages_proof_fails_if_inbound_lane_is_not_opened() { + run_test(|| { + let mut message = message(1, REGULAR_PAYLOAD); + message.key.lane_id = unknown_lane_id(); + let proof = prepare_messages_proof(vec![message.clone()], None); + + assert_noop!( + Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + proof, + 1, + REGULAR_PAYLOAD.declared_weight, + ), + Error::::LanesManager(LanesManagerError::UnknownInboundLane), + ); + + message.key.lane_id = closed_lane_id(); + let proof = prepare_messages_proof(vec![message], None); + + assert_noop!( + Pallet::::receive_messages_proof( + RuntimeOrigin::signed(1), + TEST_RELAYER_A, + proof, + 1, + REGULAR_PAYLOAD.declared_weight, + ), + Error::::LanesManager(LanesManagerError::ClosedInboundLane), + ); + }); +} + +#[test] +fn receive_messages_delivery_proof_fails_if_outbound_lane_is_unknown() { + run_test(|| { + let make_proof = |lane: TestLaneIdType| { + prepare_messages_delivery_proof( + lane, + InboundLaneData { + state: LaneState::Opened, + last_confirmed_nonce: 1, + relayers: vec![UnrewardedRelayer { + relayer: 0, + messages: DeliveredMessages::new(1), + }] + .into(), + }, + ) + }; + + let proof = make_proof(unknown_lane_id()); + assert_noop!( + Pallet::::receive_messages_delivery_proof( + RuntimeOrigin::signed(1), + proof, + UnrewardedRelayersState { + unrewarded_relayer_entries: 1, + messages_in_oldest_entry: 1, + total_messages: 1, + last_delivered_nonce: 1, + }, + ), + Error::::LanesManager(LanesManagerError::UnknownOutboundLane), + ); + }); } #[test] fn do_try_state_for_outbound_lanes_works() { run_test(|| { - let lane_id = TEST_LANE_ID; + let lane_id = test_lane_id(); // setup delivered nonce 1 OutboundLanes::::insert( lane_id, OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: 2, latest_received_nonce: 1, latest_generated_nonce: 0, diff --git a/bridges/modules/parachains/src/call_ext.rs b/bridges/modules/parachains/src/call_ext.rs index 0f77eaf2c5a93d372cab8af0857f10fa40ca920f..b67da03a6315cc240722ca525940cbeab9e1b893 100644 --- a/bridges/modules/parachains/src/call_ext.rs +++ b/bridges/modules/parachains/src/call_ext.rs @@ -14,10 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -use crate::{Config, GrandpaPalletOf, Pallet, RelayBlockHash, RelayBlockNumber}; +use crate::{Config, GrandpaPalletOf, Pallet, RelayBlockNumber}; use bp_header_chain::HeaderChain; -use bp_parachains::BestParaHeadHash; -use bp_polkadot_core::parachains::{ParaHash, ParaId}; +use bp_parachains::{BestParaHeadHash, SubmitParachainHeadsInfo}; use bp_runtime::{HeaderId, OwnedBridgeModule}; use frame_support::{ dispatch::CallableCallFor, @@ -30,21 +29,6 @@ use sp_runtime::{ RuntimeDebug, }; -/// Info about a `SubmitParachainHeads` call which tries to update a single parachain. -#[derive(PartialEq, RuntimeDebug)] -pub struct SubmitParachainHeadsInfo { - /// Number and hash of the finalized relay block that has been used to prove parachain - /// finality. - pub at_relay_block: HeaderId, - /// Parachain identifier. - pub para_id: ParaId, - /// Hash of the bundled parachain head. - pub para_head_hash: ParaHash, - /// If `true`, then the call must be free (assuming that everything else is valid) to - /// be treated as valid. - pub is_free_execution_expected: bool, -} - /// Verified `SubmitParachainHeadsInfo`. #[derive(PartialEq, RuntimeDebug)] pub struct VerifiedSubmitParachainHeadsInfo { diff --git a/bridges/modules/parachains/src/lib.rs b/bridges/modules/parachains/src/lib.rs index e2c30ce9aecc1eb3b39fc588cc6386481f82fa7f..bbf6a6600d56fd0497c30ca24ee2e6b2efd43982 100644 --- a/bridges/modules/parachains/src/lib.rs +++ b/bridges/modules/parachains/src/lib.rs @@ -28,7 +28,10 @@ pub use weights::WeightInfo; pub use weights_ext::WeightInfoExt; use bp_header_chain::{HeaderChain, HeaderChainError}; -use bp_parachains::{ParaInfo, ParaStoredHeaderData}; +use bp_parachains::{ + ParaInfo, ParaStoredHeaderData, RelayBlockHash, RelayBlockHasher, RelayBlockNumber, + SubmitParachainHeadsInfo, +}; use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; use bp_runtime::{Chain, HashOf, HeaderId, HeaderIdOf, Parachain}; use frame_support::{dispatch::PostDispatchInfo, DefaultNoBound}; @@ -61,13 +64,6 @@ mod proofs; /// The target that will be used when publishing logs related to this pallet. pub const LOG_TARGET: &str = "runtime::bridge-parachains"; -/// Block hash of the bridged relay chain. -pub type RelayBlockHash = bp_polkadot_core::Hash; -/// Block number of the bridged relay chain. -pub type RelayBlockNumber = bp_polkadot_core::BlockNumber; -/// Hasher of the bridged relay chain. -pub type RelayBlockHasher = bp_polkadot_core::Hasher; - /// Artifacts of the parachains head update. struct UpdateParachainHeadArtifacts { /// New best head of the parachain. @@ -739,7 +735,8 @@ pub mod pallet { /// Initial pallet owner. pub owner: Option, /// Dummy marker. - pub phantom: sp_std::marker::PhantomData, + #[serde(skip)] + pub _phantom: sp_std::marker::PhantomData, } #[pallet::genesis_build] diff --git a/bridges/modules/relayers/Cargo.toml b/bridges/modules/relayers/Cargo.toml index 27a28546afb482851040cbe16bd40071e86a70cb..0bf889bcca0ef674b68b0f10dbbf01eb9a7bc046 100644 --- a/bridges/modules/relayers/Cargo.toml +++ b/bridges/modules/relayers/Cargo.toml @@ -16,41 +16,58 @@ log = { workspace = true } scale-info = { features = ["derive"], workspace = true } # Bridge dependencies - +bp-header-chain = { workspace = true } bp-messages = { workspace = true } bp-relayers = { workspace = true } bp-runtime = { workspace = true } +pallet-bridge-grandpa = { workspace = true } pallet-bridge-messages = { workspace = true } +pallet-bridge-parachains = { workspace = true } # Substrate Dependencies - frame-benchmarking = { optional = true, workspace = true } frame-support = { workspace = true } frame-system = { workspace = true } +pallet-transaction-payment = { workspace = true } sp-arithmetic = { workspace = true } sp-runtime = { workspace = true } sp-std = { workspace = true } [dev-dependencies] -bp-runtime = { workspace = true, default-features = true } +bp-runtime = { workspace = true } pallet-balances = { workspace = true, default-features = true } -sp-io = { workspace = true, default-features = true } -sp-runtime = { workspace = true, default-features = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +bp-parachains = { workspace = true } +bp-polkadot-core = { workspace = true } +bp-test-utils = { workspace = true } +pallet-utility = { workspace = true } +sp-core = { workspace = true } [features] default = ["std"] std = [ + "bp-header-chain/std", "bp-messages/std", + "bp-parachains/std", + "bp-polkadot-core/std", "bp-relayers/std", "bp-runtime/std", + "bp-test-utils/std", "codec/std", "frame-benchmarking/std", "frame-support/std", "frame-system/std", "log/std", + "pallet-bridge-grandpa/std", "pallet-bridge-messages/std", + "pallet-bridge-parachains/std", + "pallet-transaction-payment/std", + "pallet-utility/std", "scale-info/std", "sp-arithmetic/std", + "sp-core/std", + "sp-io/std", "sp-runtime/std", "sp-std/std", ] @@ -59,13 +76,21 @@ runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "pallet-balances/runtime-benchmarks", + "pallet-bridge-grandpa/runtime-benchmarks", "pallet-bridge-messages/runtime-benchmarks", + "pallet-bridge-parachains/runtime-benchmarks", + "pallet-utility/runtime-benchmarks", "sp-runtime/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", "pallet-balances/try-runtime", + "pallet-bridge-grandpa/try-runtime", "pallet-bridge-messages/try-runtime", + "pallet-bridge-parachains/try-runtime", + "pallet-transaction-payment/try-runtime", + "pallet-utility/try-runtime", "sp-runtime/try-runtime", ] +integrity-test = [] diff --git a/bridges/modules/relayers/src/benchmarking.rs b/bridges/modules/relayers/src/benchmarking.rs index ca312d44edfddd286eae1715655d538b6b00f070..8fe3fc11d6ae67c151119e585a3048149a81ea36 100644 --- a/bridges/modules/relayers/src/benchmarking.rs +++ b/bridges/modules/relayers/src/benchmarking.rs @@ -20,9 +20,8 @@ use crate::*; -use bp_messages::LaneId; use bp_relayers::RewardsAccountOwner; -use frame_benchmarking::{benchmarks, whitelisted_caller}; +use frame_benchmarking::{benchmarks_instance_pallet, whitelisted_caller}; use frame_system::RawOrigin; use sp_runtime::traits::One; @@ -30,27 +29,34 @@ use sp_runtime::traits::One; const REWARD_AMOUNT: u32 = u32::MAX; /// Pallet we're benchmarking here. -pub struct Pallet(crate::Pallet); +pub struct Pallet, I: 'static = ()>(crate::Pallet); /// Trait that must be implemented by runtime. -pub trait Config: crate::Config { +pub trait Config: crate::Config { + /// Lane id to use in benchmarks. + fn bench_lane_id() -> Self::LaneId { + Self::LaneId::default() + } /// Prepare environment for paying given reward for serving given lane. - fn prepare_rewards_account(account_params: RewardsAccountParams, reward: Self::Reward); + fn prepare_rewards_account( + account_params: RewardsAccountParams, + reward: Self::Reward, + ); /// Give enough balance to given account. fn deposit_account(account: Self::AccountId, balance: Self::Reward); } -benchmarks! { +benchmarks_instance_pallet! { // Benchmark `claim_rewards` call. claim_rewards { - let lane = LaneId([0, 0, 0, 0]); + let lane = T::bench_lane_id(); let account_params = RewardsAccountParams::new(lane, *b"test", RewardsAccountOwner::ThisChain); let relayer: T::AccountId = whitelisted_caller(); let reward = T::Reward::from(REWARD_AMOUNT); T::prepare_rewards_account(account_params, reward); - RelayerRewards::::insert(&relayer, account_params, reward); + RelayerRewards::::insert(&relayer, account_params, reward); }: _(RawOrigin::Signed(relayer), account_params) verify { // we can't check anything here, because `PaymentProcedure` is responsible for @@ -62,30 +68,30 @@ benchmarks! { register { let relayer: T::AccountId = whitelisted_caller(); let valid_till = frame_system::Pallet::::block_number() - .saturating_add(crate::Pallet::::required_registration_lease()) + .saturating_add(crate::Pallet::::required_registration_lease()) .saturating_add(One::one()) .saturating_add(One::one()); - T::deposit_account(relayer.clone(), crate::Pallet::::required_stake()); + T::deposit_account(relayer.clone(), crate::Pallet::::required_stake()); }: _(RawOrigin::Signed(relayer.clone()), valid_till) verify { - assert!(crate::Pallet::::is_registration_active(&relayer)); + assert!(crate::Pallet::::is_registration_active(&relayer)); } // Benchmark `deregister` call. deregister { let relayer: T::AccountId = whitelisted_caller(); let valid_till = frame_system::Pallet::::block_number() - .saturating_add(crate::Pallet::::required_registration_lease()) + .saturating_add(crate::Pallet::::required_registration_lease()) .saturating_add(One::one()) .saturating_add(One::one()); - T::deposit_account(relayer.clone(), crate::Pallet::::required_stake()); - crate::Pallet::::register(RawOrigin::Signed(relayer.clone()).into(), valid_till).unwrap(); + T::deposit_account(relayer.clone(), crate::Pallet::::required_stake()); + crate::Pallet::::register(RawOrigin::Signed(relayer.clone()).into(), valid_till).unwrap(); frame_system::Pallet::::set_block_number(valid_till.saturating_add(One::one())); }: _(RawOrigin::Signed(relayer.clone())) verify { - assert!(!crate::Pallet::::is_registration_active(&relayer)); + assert!(!crate::Pallet::::is_registration_active(&relayer)); } // Benchmark `slash_and_deregister` method of the pallet. We are adding this weight to @@ -95,36 +101,36 @@ benchmarks! { // prepare and register relayer account let relayer: T::AccountId = whitelisted_caller(); let valid_till = frame_system::Pallet::::block_number() - .saturating_add(crate::Pallet::::required_registration_lease()) + .saturating_add(crate::Pallet::::required_registration_lease()) .saturating_add(One::one()) .saturating_add(One::one()); - T::deposit_account(relayer.clone(), crate::Pallet::::required_stake()); - crate::Pallet::::register(RawOrigin::Signed(relayer.clone()).into(), valid_till).unwrap(); + T::deposit_account(relayer.clone(), crate::Pallet::::required_stake()); + crate::Pallet::::register(RawOrigin::Signed(relayer.clone()).into(), valid_till).unwrap(); // create slash destination account - let lane = LaneId([0, 0, 0, 0]); + let lane = T::bench_lane_id(); let slash_destination = RewardsAccountParams::new(lane, *b"test", RewardsAccountOwner::ThisChain); T::prepare_rewards_account(slash_destination, Zero::zero()); }: { - crate::Pallet::::slash_and_deregister(&relayer, slash_destination.into()) + crate::Pallet::::slash_and_deregister(&relayer, slash_destination.into()) } verify { - assert!(!crate::Pallet::::is_registration_active(&relayer)); + assert!(!crate::Pallet::::is_registration_active(&relayer)); } // Benchmark `register_relayer_reward` method of the pallet. We are adding this weight to // the weight of message delivery call if `RefundBridgedParachainMessages` signed extension // is deployed at runtime level. register_relayer_reward { - let lane = LaneId([0, 0, 0, 0]); + let lane = T::bench_lane_id(); let relayer: T::AccountId = whitelisted_caller(); let account_params = RewardsAccountParams::new(lane, *b"test", RewardsAccountOwner::ThisChain); }: { - crate::Pallet::::register_relayer_reward(account_params, &relayer, One::one()); + crate::Pallet::::register_relayer_reward(account_params, &relayer, One::one()); } verify { - assert_eq!(RelayerRewards::::get(relayer, &account_params), Some(One::one())); + assert_eq!(RelayerRewards::::get(relayer, &account_params), Some(One::one())); } impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::TestRuntime) diff --git a/bridges/modules/relayers/src/extension/grandpa_adapter.rs b/bridges/modules/relayers/src/extension/grandpa_adapter.rs new file mode 100644 index 0000000000000000000000000000000000000000..2a8a6e78ef9c732b868f1293f313b35a044ad81a --- /dev/null +++ b/bridges/modules/relayers/src/extension/grandpa_adapter.rs @@ -0,0 +1,182 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Adapter that allows using `pallet-bridge-relayers` as a signed extension in the +//! bridge with remote GRANDPA chain. + +use crate::{ + extension::verify_messages_call_succeeded, Config as BridgeRelayersConfig, LOG_TARGET, +}; + +use bp_relayers::{BatchCallUnpacker, ExtensionCallData, ExtensionCallInfo, ExtensionConfig}; +use bp_runtime::{Chain, StaticStrProvider}; +use frame_support::dispatch::{DispatchInfo, PostDispatchInfo}; +use frame_system::Config as SystemConfig; +use pallet_bridge_grandpa::{ + CallSubType as BridgeGrandpaCallSubtype, Config as BridgeGrandpaConfig, + SubmitFinalityProofHelper, +}; +use pallet_bridge_messages::{ + CallSubType as BridgeMessagesCallSubType, Config as BridgeMessagesConfig, LaneIdOf, +}; +use sp_runtime::{ + traits::{Dispatchable, Get}, + transaction_validity::{TransactionPriority, TransactionValidityError}, + Saturating, +}; +use sp_std::marker::PhantomData; + +/// Adapter to be used in signed extension configuration, when bridging with remote +/// chains that are using GRANDPA finality. +pub struct WithGrandpaChainExtensionConfig< + // signed extension identifier + IdProvider, + // runtime that implements `BridgeMessagesConfig`, which + // uses `BridgeGrandpaConfig` to receive messages and + // confirmations from the remote chain. + Runtime, + // batch call unpacker + BatchCallUnpacker, + // instance of the `pallet-bridge-grandpa`, tracked by this extension + BridgeGrandpaPalletInstance, + // instance of BridgedChain `pallet-bridge-messages`, tracked by this extension + BridgeMessagesPalletInstance, + // instance of `pallet-bridge-relayers`, tracked by this extension + BridgeRelayersPalletInstance, + // message delivery transaction priority boost for every additional message + PriorityBoostPerMessage, +>( + PhantomData<( + IdProvider, + Runtime, + BatchCallUnpacker, + BridgeGrandpaPalletInstance, + BridgeMessagesPalletInstance, + BridgeRelayersPalletInstance, + PriorityBoostPerMessage, + )>, +); + +impl ExtensionConfig + for WithGrandpaChainExtensionConfig +where + ID: StaticStrProvider, + R: BridgeRelayersConfig + + BridgeMessagesConfig> + + BridgeGrandpaConfig, + BCU: BatchCallUnpacker, + GI: 'static, + MI: 'static, + RI: 'static, + P: Get, + R::RuntimeCall: Dispatchable + + BridgeGrandpaCallSubtype + + BridgeMessagesCallSubType, +{ + type IdProvider = ID; + type Runtime = R; + type BridgeMessagesPalletInstance = MI; + type BridgeRelayersPalletInstance = RI; + type PriorityBoostPerMessage = P; + type RemoteGrandpaChainBlockNumber = pallet_bridge_grandpa::BridgedBlockNumber; + type LaneId = LaneIdOf; + + fn parse_and_check_for_obsolete_call( + call: &R::RuntimeCall, + ) -> Result< + Option>, + TransactionValidityError, + > { + let calls = BCU::unpack(call, 2); + let total_calls = calls.len(); + let mut calls = calls.into_iter().map(Self::check_obsolete_parsed_call).rev(); + + let msgs_call = calls.next().transpose()?.and_then(|c| c.call_info()); + let relay_finality_call = + calls.next().transpose()?.and_then(|c| c.submit_finality_proof_info()); + + Ok(match (total_calls, relay_finality_call, msgs_call) { + (2, Some(relay_finality_call), Some(msgs_call)) => + Some(ExtensionCallInfo::RelayFinalityAndMsgs(relay_finality_call, msgs_call)), + (1, None, Some(msgs_call)) => Some(ExtensionCallInfo::Msgs(msgs_call)), + _ => None, + }) + } + + fn check_obsolete_parsed_call( + call: &R::RuntimeCall, + ) -> Result<&R::RuntimeCall, TransactionValidityError> { + call.check_obsolete_submit_finality_proof()?; + call.check_obsolete_call()?; + Ok(call) + } + + fn check_call_result( + call_info: &ExtensionCallInfo, + call_data: &mut ExtensionCallData, + relayer: &R::AccountId, + ) -> bool { + verify_submit_finality_proof_succeeded::(call_info, call_data, relayer) && + verify_messages_call_succeeded::(call_info, call_data, relayer) + } +} + +/// If the batch call contains the GRANDPA chain state update call, verify that it +/// has been successful. +/// +/// Only returns false when GRANDPA chain state update call has failed. +pub(crate) fn verify_submit_finality_proof_succeeded( + call_info: &ExtensionCallInfo, + call_data: &mut ExtensionCallData, + relayer: &::AccountId, +) -> bool +where + C: ExtensionConfig, + GI: 'static, + C::Runtime: BridgeGrandpaConfig, + >::BridgedChain: + Chain, +{ + let Some(finality_proof_info) = call_info.submit_finality_proof_info() else { return true }; + + if !SubmitFinalityProofHelper::::was_successful( + finality_proof_info.block_number, + ) { + // we only refund relayer if all calls have updated chain state + log::trace!( + target: LOG_TARGET, + "{}.{:?}: relayer {:?} has submitted invalid GRANDPA chain finality proof", + C::IdProvider::STR, + call_info.messages_call_info().lane_id(), + relayer, + ); + return false + } + + // there's a conflict between how bridge GRANDPA pallet works and a `utility.batchAll` + // transaction. If relay chain header is mandatory, the GRANDPA pallet returns + // `Pays::No`, because such transaction is mandatory for operating the bridge. But + // `utility.batchAll` transaction always requires payment. But in both cases we'll + // refund relayer - either explicitly here, or using `Pays::No` if he's choosing + // to submit dedicated transaction. + + // submitter has means to include extra weight/bytes in the `submit_finality_proof` + // call, so let's subtract extra weight/size to avoid refunding for this extra stuff + call_data.extra_weight.saturating_accrue(finality_proof_info.extra_weight); + call_data.extra_size.saturating_accrue(finality_proof_info.extra_size); + + true +} diff --git a/bridges/modules/relayers/src/extension/messages_adapter.rs b/bridges/modules/relayers/src/extension/messages_adapter.rs new file mode 100644 index 0000000000000000000000000000000000000000..e8c2088b7f2d3e6a08bfd45550bf0723132824d6 --- /dev/null +++ b/bridges/modules/relayers/src/extension/messages_adapter.rs @@ -0,0 +1,99 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Adapter that allows using `pallet-bridge-relayers` as a signed extension in the +//! bridge with any remote chain. This adapter does not refund any finality transactions. + +use crate::{extension::verify_messages_call_succeeded, Config as BridgeRelayersConfig}; + +use bp_relayers::{ExtensionCallData, ExtensionCallInfo, ExtensionConfig}; +use bp_runtime::StaticStrProvider; +use frame_support::dispatch::{DispatchInfo, PostDispatchInfo}; +use pallet_bridge_messages::{ + CallSubType as BridgeMessagesCallSubType, Config as BridgeMessagesConfig, LaneIdOf, +}; +use sp_runtime::{ + traits::{Dispatchable, Get}, + transaction_validity::{TransactionPriority, TransactionValidityError}, +}; +use sp_std::marker::PhantomData; + +/// Transaction extension that refunds a relayer for standalone messages delivery and confirmation +/// transactions. Finality transactions are not refunded. +pub struct WithMessagesExtensionConfig< + IdProvider, + Runtime, + BridgeMessagesPalletInstance, + BridgeRelayersPalletInstance, + PriorityBoostPerMessage, +>( + PhantomData<( + // signed extension identifier + IdProvider, + // runtime with `pallet-bridge-messages` pallet deployed + Runtime, + // instance of BridgedChain `pallet-bridge-messages`, tracked by this extension + BridgeMessagesPalletInstance, + // instance of `pallet-bridge-relayers`, tracked by this extension + BridgeRelayersPalletInstance, + // message delivery transaction priority boost for every additional message + PriorityBoostPerMessage, + )>, +); + +impl ExtensionConfig for WithMessagesExtensionConfig +where + ID: StaticStrProvider, + R: BridgeRelayersConfig + BridgeMessagesConfig, + MI: 'static, + RI: 'static, + P: Get, + R::RuntimeCall: Dispatchable + + BridgeMessagesCallSubType, +{ + type IdProvider = ID; + type Runtime = R; + type BridgeMessagesPalletInstance = MI; + type BridgeRelayersPalletInstance = RI; + type PriorityBoostPerMessage = P; + type RemoteGrandpaChainBlockNumber = (); + type LaneId = LaneIdOf; + + fn parse_and_check_for_obsolete_call( + call: &R::RuntimeCall, + ) -> Result< + Option>, + TransactionValidityError, + > { + let call = Self::check_obsolete_parsed_call(call)?; + Ok(call.call_info().map(ExtensionCallInfo::Msgs)) + } + + fn check_obsolete_parsed_call( + call: &R::RuntimeCall, + ) -> Result<&R::RuntimeCall, TransactionValidityError> { + call.check_obsolete_call()?; + Ok(call) + } + + fn check_call_result( + call_info: &ExtensionCallInfo, + call_data: &mut ExtensionCallData, + relayer: &R::AccountId, + ) -> bool { + verify_messages_call_succeeded::(call_info, call_data, relayer) + } +} diff --git a/bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs b/bridges/modules/relayers/src/extension/mod.rs similarity index 60% rename from bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs rename to bridges/modules/relayers/src/extension/mod.rs index 6ba3506377d0e602bf9ee706b13c248efd6afaca..9a248eb8e7983c09934396e7ccae824dd83b296c 100644 --- a/bridges/bin/runtime-common/src/extensions/refund_relayer_extension.rs +++ b/bridges/modules/relayers/src/extension/mod.rs @@ -14,292 +14,187 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -//! Signed extension that refunds relayer if he has delivered some new messages. -//! It also refunds transaction cost if the transaction is an `utility.batchAll()` -//! with calls that are: delivering new message and all necessary underlying headers -//! (parachain or relay chain). - -use crate::messages_call_ext::{ - CallHelper as MessagesCallHelper, CallInfo as MessagesCallInfo, MessagesCallSubType, +//! Signed extension, built around `pallet-bridge-relayers`. It is able to: +//! +//! - refund the cost of successful message delivery and confirmation transactions to the submitter +//! by registering corresponding reward in the pallet; +//! +//! - bump priority of messages delivery and confirmation transactions, signed by the registered +//! relayers. + +use crate::{Config as RelayersConfig, Pallet as RelayersPallet, WeightInfoExt, LOG_TARGET}; + +use bp_messages::{ChainWithMessages, MessageNonce}; +use bp_relayers::{ + ExplicitOrAccountParams, ExtensionCallData, ExtensionCallInfo, ExtensionConfig, + RewardsAccountOwner, RewardsAccountParams, }; -use bp_messages::{ChainWithMessages, LaneId, MessageNonce}; -use bp_relayers::{ExplicitOrAccountParams, RewardsAccountOwner, RewardsAccountParams}; -use bp_runtime::{Chain, Parachain, RangeInclusiveExt, StaticStrProvider}; -use codec::{Codec, Decode, Encode}; +use bp_runtime::{Chain, RangeInclusiveExt, StaticStrProvider}; +use codec::{Decode, Encode}; use frame_support::{ - dispatch::{CallableCallFor, DispatchInfo, PostDispatchInfo}, - traits::IsSubType, - weights::Weight, + dispatch::{DispatchInfo, PostDispatchInfo}, CloneNoBound, DefaultNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound, }; -use pallet_bridge_grandpa::{ - CallSubType as GrandpaCallSubType, SubmitFinalityProofHelper, SubmitFinalityProofInfo, -}; -use pallet_bridge_messages::Config as MessagesConfig; -use pallet_bridge_parachains::{ - BoundedBridgeGrandpaConfig, CallSubType as ParachainsCallSubType, Config as ParachainsConfig, - RelayBlockNumber, SubmitParachainHeadsHelper, SubmitParachainHeadsInfo, +use frame_system::Config as SystemConfig; +use pallet_bridge_messages::{ + CallHelper as MessagesCallHelper, Config as BridgeMessagesConfig, LaneIdOf, }; -use pallet_bridge_relayers::{ - Config as RelayersConfig, Pallet as RelayersPallet, WeightInfoExt as _, +use pallet_transaction_payment::{ + Config as TransactionPaymentConfig, OnChargeTransaction, Pallet as TransactionPaymentPallet, }; -use pallet_transaction_payment::{Config as TransactionPaymentConfig, OnChargeTransaction}; -use pallet_utility::{Call as UtilityCall, Config as UtilityConfig, Pallet as UtilityPallet}; use scale_info::TypeInfo; use sp_runtime::{ - traits::{DispatchInfoOf, Dispatchable, Get, PostDispatchInfoOf, SignedExtension, Zero}, + traits::{DispatchInfoOf, Dispatchable, PostDispatchInfoOf, SignedExtension, Zero}, transaction_validity::{ - TransactionPriority, TransactionValidity, TransactionValidityError, ValidTransactionBuilder, + TransactionValidity, TransactionValidityError, ValidTransactionBuilder, }, - DispatchResult, FixedPointOperand, RuntimeDebug, + DispatchResult, RuntimeDebug, }; -use sp_std::{marker::PhantomData, vec, vec::Vec}; - -type AccountIdOf = ::AccountId; -// without this typedef rustfmt fails with internal err -type BalanceOf = - <::OnChargeTransaction as OnChargeTransaction>::Balance; -type CallOf = ::RuntimeCall; - -/// Trait identifying a bridged parachain. A relayer might be refunded for delivering messages -/// coming from this parachain. -pub trait RefundableParachainId { - /// The instance of the bridge parachains pallet. - type Instance: 'static; - /// The parachain Id. - type BridgedChain: Parachain; -} - -/// Implementation of `RefundableParachainId` for `trait Parachain`. -pub struct RefundableParachain(PhantomData<(Instance, Para)>); - -impl RefundableParachainId for RefundableParachain -where - Instance: 'static, - Para: Parachain, -{ - type Instance = Instance; - type BridgedChain = Para; -} - -/// Trait identifying a bridged messages lane. A relayer might be refunded for delivering messages -/// coming from this lane. -pub trait RefundableMessagesLaneId { - /// The instance of the bridge messages pallet. - type Instance: 'static; - /// The messages lane id. - type Id: Get; -} - -/// Default implementation of `RefundableMessagesLaneId`. -pub struct RefundableMessagesLane(PhantomData<(Instance, Id)>); - -impl RefundableMessagesLaneId for RefundableMessagesLane -where - Instance: 'static, - Id: Get, -{ - type Instance = Instance; - type Id = Id; -} - -/// Refund calculator. -pub trait RefundCalculator { - /// The underlying integer type in which the refund is calculated. - type Balance; - - /// Compute refund for given transaction. - fn compute_refund( - info: &DispatchInfo, - post_info: &PostDispatchInfo, - len: usize, - tip: Self::Balance, - ) -> Self::Balance; -} +use sp_std::{fmt::Debug, marker::PhantomData}; -/// `RefundCalculator` implementation which refunds the actual transaction fee. -pub struct ActualFeeRefund(PhantomData); +pub use grandpa_adapter::WithGrandpaChainExtensionConfig; +pub use messages_adapter::WithMessagesExtensionConfig; +pub use parachain_adapter::WithParachainExtensionConfig; +pub use priority::*; -impl RefundCalculator for ActualFeeRefund -where - R: TransactionPaymentConfig, - CallOf: Dispatchable, - BalanceOf: FixedPointOperand, -{ - type Balance = BalanceOf; - - fn compute_refund( - info: &DispatchInfo, - post_info: &PostDispatchInfo, - len: usize, - tip: BalanceOf, - ) -> BalanceOf { - pallet_transaction_payment::Pallet::::compute_actual_fee(len as _, info, post_info, tip) - } -} +mod grandpa_adapter; +mod messages_adapter; +mod parachain_adapter; +mod priority; /// Data that is crafted in `pre_dispatch` method and used at `post_dispatch`. #[cfg_attr(test, derive(Debug, PartialEq))] -pub struct PreDispatchData { +pub struct PreDispatchData< + AccountId, + RemoteGrandpaChainBlockNumber: Debug, + LaneId: Clone + Copy + Debug, +> { /// Transaction submitter (relayer) account. relayer: AccountId, /// Type of the call. - call_info: CallInfo, + call_info: ExtensionCallInfo, } -/// Type of the call that the extension recognizes. -#[derive(RuntimeDebugNoBound, PartialEq)] -pub enum CallInfo { - /// Relay chain finality + parachain finality + message delivery/confirmation calls. - AllFinalityAndMsgs( - SubmitFinalityProofInfo, - SubmitParachainHeadsInfo, - MessagesCallInfo, - ), - /// Relay chain finality + message delivery/confirmation calls. - RelayFinalityAndMsgs(SubmitFinalityProofInfo, MessagesCallInfo), - /// Parachain finality + message delivery/confirmation calls. - /// - /// This variant is used only when bridging with parachain. - ParachainFinalityAndMsgs(SubmitParachainHeadsInfo, MessagesCallInfo), - /// Standalone message delivery/confirmation call. - Msgs(MessagesCallInfo), -} - -impl CallInfo { - /// Returns true if call is a message delivery call (with optional finality calls). - fn is_receive_messages_proof_call(&self) -> bool { - match self.messages_call_info() { - MessagesCallInfo::ReceiveMessagesProof(_) => true, - MessagesCallInfo::ReceiveMessagesDeliveryProof(_) => false, - } - } - - /// Returns the pre-dispatch `finality_target` sent to the `SubmitFinalityProof` call. - fn submit_finality_proof_info(&self) -> Option> { - match *self { - Self::AllFinalityAndMsgs(info, _, _) => Some(info), - Self::RelayFinalityAndMsgs(info, _) => Some(info), - _ => None, - } - } - +impl + PreDispatchData +{ /// Returns mutable reference to pre-dispatch `finality_target` sent to the /// `SubmitFinalityProof` call. #[cfg(test)] - fn submit_finality_proof_info_mut( + pub fn submit_finality_proof_info_mut( &mut self, - ) -> Option<&mut SubmitFinalityProofInfo> { - match *self { - Self::AllFinalityAndMsgs(ref mut info, _, _) => Some(info), - Self::RelayFinalityAndMsgs(ref mut info, _) => Some(info), + ) -> Option<&mut bp_header_chain::SubmitFinalityProofInfo> { + match self.call_info { + ExtensionCallInfo::AllFinalityAndMsgs(ref mut info, _, _) => Some(info), + ExtensionCallInfo::RelayFinalityAndMsgs(ref mut info, _) => Some(info), _ => None, } } - - /// Returns the pre-dispatch `SubmitParachainHeadsInfo`. - fn submit_parachain_heads_info(&self) -> Option<&SubmitParachainHeadsInfo> { - match self { - Self::AllFinalityAndMsgs(_, info, _) => Some(info), - Self::ParachainFinalityAndMsgs(info, _) => Some(info), - _ => None, - } - } - - /// Returns the pre-dispatch `ReceiveMessagesProofInfo`. - fn messages_call_info(&self) -> &MessagesCallInfo { - match self { - Self::AllFinalityAndMsgs(_, _, info) => info, - Self::RelayFinalityAndMsgs(_, info) => info, - Self::ParachainFinalityAndMsgs(_, info) => info, - Self::Msgs(info) => info, - } - } } /// The actions on relayer account that need to be performed because of his actions. #[derive(RuntimeDebug, PartialEq)] -pub enum RelayerAccountAction { +pub enum RelayerAccountAction { /// Do nothing with relayer account. None, /// Reward the relayer. - Reward(AccountId, RewardsAccountParams, Reward), + Reward(AccountId, RewardsAccountParams, Reward), /// Slash the relayer. - Slash(AccountId, RewardsAccountParams), + Slash(AccountId, RewardsAccountParams), } -/// Everything common among our refund signed extensions. -pub trait RefundSignedExtension: - 'static + Clone + Codec + sp_std::fmt::Debug + Default + Eq + PartialEq + Send + Sync + TypeInfo +/// A signed extension, built around `pallet-bridge-relayers`. +/// +/// It may be incorporated into runtime to refund relayers for submitting correct +/// message delivery and confirmation transactions, optionally batched with required +/// finality proofs. +#[derive( + DefaultNoBound, + CloneNoBound, + Decode, + Encode, + EqNoBound, + PartialEqNoBound, + RuntimeDebugNoBound, + TypeInfo, +)] +#[scale_info(skip_type_params(Runtime, Config, LaneId))] +pub struct BridgeRelayersSignedExtension( + PhantomData<(Runtime, Config, LaneId)>, +); + +impl BridgeRelayersSignedExtension +where + Self: 'static + Send + Sync, + R: RelayersConfig + + BridgeMessagesConfig + + TransactionPaymentConfig, + C: ExtensionConfig, + R::RuntimeCall: Dispatchable, + ::OnChargeTransaction: + OnChargeTransaction, + LaneId: Clone + Copy + Decode + Encode + Debug + TypeInfo, { - /// This chain runtime. - type Runtime: MessagesConfig<::Instance> - + RelayersConfig; - /// Messages pallet and lane reference. - type Msgs: RefundableMessagesLaneId; - /// Refund amount calculator. - type Refund: RefundCalculator::Reward>; - /// Priority boost calculator. - type Priority: Get; - /// Signed extension unique identifier. - type Id: StaticStrProvider; - - /// Unpack batch runtime call. - fn expand_call(call: &CallOf) -> Vec<&CallOf>; - - /// Given runtime call, check if it has supported format. Additionally, check if any of - /// (optionally batched) calls are obsolete and we shall reject the transaction. - fn parse_and_check_for_obsolete_call( - call: &CallOf, - ) -> Result, TransactionValidityError>; - - /// Check if parsed call is already obsolete. - fn check_obsolete_parsed_call( - call: &CallOf, - ) -> Result<&CallOf, TransactionValidityError>; - - /// Called from post-dispatch and shall perform additional checks (apart from messages - /// transaction success) of given call result. - fn additional_call_result_check( - relayer: &AccountIdOf, - call_info: &CallInfo, - extra_weight: &mut Weight, - extra_size: &mut u32, - ) -> bool; + /// Returns number of bundled messages `Some(_)`, if the given call info is a: + /// + /// - message delivery transaction; + /// + /// - with reasonable bundled messages that may be accepted by the messages pallet. + /// + /// This function is used to check whether the transaction priority should be + /// virtually boosted. The relayer registration (we only boost priority for registered + /// relayer transactions) must be checked outside. + fn bundled_messages_for_priority_boost( + call_info: Option<&ExtensionCallInfo>, + ) -> Option { + // we only boost priority of message delivery transactions + let parsed_call = match call_info { + Some(parsed_call) if parsed_call.is_receive_messages_proof_call() => parsed_call, + _ => return None, + }; + + // compute total number of messages in transaction + let bundled_messages = parsed_call.messages_call_info().bundled_messages().saturating_len(); + + // a quick check to avoid invalid high-priority transactions + let max_unconfirmed_messages_in_confirmation_tx = >::BridgedChain + ::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + if bundled_messages > max_unconfirmed_messages_in_confirmation_tx { + return None + } + + Some(bundled_messages) + } /// Given post-dispatch information, analyze the outcome of relayer call and return /// actions that need to be performed on relayer account. fn analyze_call_result( - pre: Option>>>, + pre: Option< + Option>, + >, info: &DispatchInfo, post_info: &PostDispatchInfo, len: usize, result: &DispatchResult, - ) -> RelayerAccountAction, ::Reward> - { - let mut extra_weight = Weight::zero(); - let mut extra_size = 0; - + ) -> RelayerAccountAction { // We don't refund anything for transactions that we don't support. let (relayer, call_info) = match pre { Some(Some(pre)) => (pre.relayer, pre.call_info), _ => return RelayerAccountAction::None, }; - // now we know that the relayer either needs to be rewarded, or slashed + // now we know that the call is supported and we may need to reward or slash relayer // => let's prepare the correspondent account that pays reward/receives slashed amount - let reward_account_params = - RewardsAccountParams::new( - ::Id::get(), - ::Instance, - >>::BridgedChain::ID, - if call_info.is_receive_messages_proof_call() { - RewardsAccountOwner::ThisChain - } else { - RewardsAccountOwner::BridgedChain - }, - ); + let lane_id = call_info.messages_call_info().lane_id(); + let reward_account_params = RewardsAccountParams::new( + lane_id, + >::BridgedChain::ID, + if call_info.is_receive_messages_proof_call() { + RewardsAccountOwner::ThisChain + } else { + RewardsAccountOwner::BridgedChain + }, + ); // prepare return value for the case if the call has failed or it has not caused // expected side effects (e.g. not all messages have been accepted) @@ -322,37 +217,19 @@ pub trait RefundSignedExtension: // We don't refund anything if the transaction has failed. if let Err(e) = result { log::trace!( - target: "runtime::bridge", - "{} via {:?}: relayer {:?} has submitted invalid messages transaction: {:?}", - Self::Id::STR, - ::Id::get(), + target: LOG_TARGET, + "{}.{:?}: relayer {:?} has submitted invalid messages transaction: {:?}", + Self::IDENTIFIER, + lane_id, relayer, e, ); return slash_relayer_if_delivery_result } - // Check if the `ReceiveMessagesProof` call delivered at least some of the messages that - // it contained. If this happens, we consider the transaction "helpful" and refund it. - let msgs_call_info = call_info.messages_call_info(); - if !MessagesCallHelper::::Instance>::was_successful(msgs_call_info) { - log::trace!( - target: "runtime::bridge", - "{} via {:?}: relayer {:?} has submitted invalid messages call", - Self::Id::STR, - ::Id::get(), - relayer, - ); - return slash_relayer_if_delivery_result - } - - // do additional checks - if !Self::additional_call_result_check( - &relayer, - &call_info, - &mut extra_weight, - &mut extra_size, - ) { + // check whether the call has succeeded + let mut call_data = ExtensionCallData::default(); + if !C::check_call_result(&call_info, &mut call_data, &relayer) { return slash_relayer_if_delivery_result } @@ -364,81 +241,56 @@ pub trait RefundSignedExtension: let tip = Zero::zero(); // decrease post-dispatch weight/size using extra weight/size that we know now - let post_info_len = len.saturating_sub(extra_size as usize); - let mut post_info_weight = - post_info.actual_weight.unwrap_or(info.weight).saturating_sub(extra_weight); + let post_info_len = len.saturating_sub(call_data.extra_size as usize); + let mut post_info_weight = post_info + .actual_weight + .unwrap_or(info.weight) + .saturating_sub(call_data.extra_weight); // let's also replace the weight of slashing relayer with the weight of rewarding relayer if call_info.is_receive_messages_proof_call() { post_info_weight = post_info_weight.saturating_sub( - ::WeightInfo::extra_weight_of_successful_receive_messages_proof_call(), + ::WeightInfo::extra_weight_of_successful_receive_messages_proof_call(), ); } // compute the relayer refund let mut post_info = *post_info; post_info.actual_weight = Some(post_info_weight); - let refund = Self::Refund::compute_refund(info, &post_info, post_info_len, tip); + let refund = Self::compute_refund(info, &post_info, post_info_len, tip); // we can finally reward relayer RelayerAccountAction::Reward(relayer, reward_account_params, refund) } - /// Returns number of bundled messages `Some(_)`, if the given call info is a: - /// - /// - message delivery transaction; - /// - /// - with reasonable bundled messages that may be accepted by the messages pallet. - /// - /// This function is used to check whether the transaction priority should be - /// virtually boosted. The relayer registration (we only boost priority for registered - /// relayer transactions) must be checked outside. - fn bundled_messages_for_priority_boost(call_info: Option<&CallInfo>) -> Option { - // we only boost priority of message delivery transactions - let parsed_call = match call_info { - Some(parsed_call) if parsed_call.is_receive_messages_proof_call() => parsed_call, - _ => return None, - }; - - // compute total number of messages in transaction - let bundled_messages = parsed_call.messages_call_info().bundled_messages().saturating_len(); - - // a quick check to avoid invalid high-priority transactions - let max_unconfirmed_messages_in_confirmation_tx = ::Instance, - >>::BridgedChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; - if bundled_messages > max_unconfirmed_messages_in_confirmation_tx { - return None - } - - Some(bundled_messages) + /// Compute refund for the successful relayer transaction + fn compute_refund( + info: &DispatchInfo, + post_info: &PostDispatchInfo, + len: usize, + tip: R::Reward, + ) -> R::Reward { + TransactionPaymentPallet::::compute_actual_fee(len as _, info, post_info, tip) } } -/// Adapter that allow implementing `sp_runtime::traits::SignedExtension` for any -/// `RefundSignedExtension`. -#[derive( - DefaultNoBound, - CloneNoBound, - Decode, - Encode, - EqNoBound, - PartialEqNoBound, - RuntimeDebugNoBound, - TypeInfo, -)] -pub struct RefundSignedExtensionAdapter(T); - -impl SignedExtension for RefundSignedExtensionAdapter +impl SignedExtension for BridgeRelayersSignedExtension where - CallOf: Dispatchable - + MessagesCallSubType::Instance>, + Self: 'static + Send + Sync, + R: RelayersConfig + + BridgeMessagesConfig + + TransactionPaymentConfig, + C: ExtensionConfig, + R::RuntimeCall: Dispatchable, + ::OnChargeTransaction: + OnChargeTransaction, + LaneId: Clone + Copy + Decode + Encode + Debug + TypeInfo, { - const IDENTIFIER: &'static str = T::Id::STR; - type AccountId = AccountIdOf; - type Call = CallOf; + const IDENTIFIER: &'static str = C::IdProvider::STR; + type AccountId = R::AccountId; + type Call = R::RuntimeCall; type AdditionalSigned = (); - type Pre = Option>>; + type Pre = Option>; fn additional_signed(&self) -> Result<(), TransactionValidityError> { Ok(()) @@ -456,33 +308,33 @@ where // we're not calling `validate` from `pre_dispatch` directly because of performance // reasons, so if you're adding some code that may fail here, please check if it needs // to be added to the `pre_dispatch` as well - let parsed_call = T::parse_and_check_for_obsolete_call(call)?; + let parsed_call = C::parse_and_check_for_obsolete_call(call)?; // the following code just plays with transaction priority and never returns an error // we only boost priority of presumably correct message delivery transactions - let bundled_messages = match T::bundled_messages_for_priority_boost(parsed_call.as_ref()) { + let bundled_messages = match Self::bundled_messages_for_priority_boost(parsed_call.as_ref()) + { Some(bundled_messages) => bundled_messages, None => return Ok(Default::default()), }; // we only boost priority if relayer has staked required balance - if !RelayersPallet::::is_registration_active(who) { + if !RelayersPallet::::is_registration_active(who) { return Ok(Default::default()) } // compute priority boost - let priority_boost = crate::extensions::priority_calculator::compute_priority_boost::< - T::Priority, - >(bundled_messages); + let priority_boost = + priority::compute_priority_boost::(bundled_messages); let valid_transaction = ValidTransactionBuilder::default().priority(priority_boost); log::trace!( - target: "runtime::bridge", - "{} via {:?} has boosted priority of message delivery transaction \ + target: LOG_TARGET, + "{}.{:?}: has boosted priority of message delivery transaction \ of relayer {:?}: {} messages -> {} priority", Self::IDENTIFIER, - ::Id::get(), + parsed_call.as_ref().map(|p| p.messages_call_info().lane_id()), who, bundled_messages, priority_boost, @@ -499,14 +351,14 @@ where _len: usize, ) -> Result { // this is a relevant piece of `validate` that we need here (in `pre_dispatch`) - let parsed_call = T::parse_and_check_for_obsolete_call(call)?; + let parsed_call = C::parse_and_check_for_obsolete_call(call)?; Ok(parsed_call.map(|call_info| { log::trace!( - target: "runtime::bridge", - "{} via {:?} parsed bridge transaction in pre-dispatch: {:?}", + target: LOG_TARGET, + "{}.{:?}: parsed bridge transaction in pre-dispatch: {:?}", Self::IDENTIFIER, - ::Id::get(), + call_info.messages_call_info().lane_id(), call_info, ); PreDispatchData { relayer: who.clone(), call_info } @@ -520,28 +372,28 @@ where len: usize, result: &DispatchResult, ) -> Result<(), TransactionValidityError> { - let call_result = T::analyze_call_result(pre, info, post_info, len, result); + let lane_id = pre + .as_ref() + .and_then(|p| p.as_ref()) + .map(|p| p.call_info.messages_call_info().lane_id()); + let call_result = Self::analyze_call_result(pre, info, post_info, len, result); match call_result { RelayerAccountAction::None => (), RelayerAccountAction::Reward(relayer, reward_account, reward) => { - RelayersPallet::::register_relayer_reward( - reward_account, - &relayer, - reward, - ); + RelayersPallet::::register_relayer_reward(reward_account, &relayer, reward); log::trace!( - target: "runtime::bridge", - "{} via {:?} has registered reward: {:?} for {:?}", + target: LOG_TARGET, + "{}.{:?}: has registered reward: {:?} for {:?}", Self::IDENTIFIER, - ::Id::get(), + lane_id, reward, relayer, ); }, RelayerAccountAction::Slash(relayer, slash_account) => - RelayersPallet::::slash_and_deregister( + RelayersPallet::::slash_and_deregister( &relayer, ExplicitOrAccountParams::Params(slash_account), ), @@ -551,416 +403,65 @@ where } } -/// Signed extension that refunds a relayer for new messages coming from a parachain. -/// -/// Also refunds relayer for successful finality delivery if it comes in batch (`utility.batchAll`) -/// with message delivery transaction. Batch may deliver either both relay chain header and -/// parachain head, or just parachain head. Corresponding headers must be used in messages -/// proof verification. -/// -/// Extension does not refund transaction tip due to security reasons. -#[derive( - DefaultNoBound, - CloneNoBound, - Decode, - Encode, - EqNoBound, - PartialEqNoBound, - RuntimeDebugNoBound, - TypeInfo, -)] -#[scale_info(skip_type_params(Runtime, Para, Msgs, Refund, Priority, Id))] -pub struct RefundBridgedParachainMessages( - PhantomData<( - // runtime with `frame-utility`, `pallet-bridge-grandpa`, `pallet-bridge-parachains`, - // `pallet-bridge-messages` and `pallet-bridge-relayers` pallets deployed - Runtime, - // implementation of `RefundableParachainId` trait, which specifies the instance of - // the used `pallet-bridge-parachains` pallet and the bridged parachain id - Para, - // implementation of `RefundableMessagesLaneId` trait, which specifies the instance of - // the used `pallet-bridge-messages` pallet and the lane within this pallet - Msgs, - // implementation of the `RefundCalculator` trait, that is used to compute refund that - // we give to relayer for his transaction - Refund, - // getter for per-message `TransactionPriority` boost that we give to message - // delivery transactions - Priority, - // the runtime-unique identifier of this signed extension - Id, - )>, -); - -impl RefundSignedExtension - for RefundBridgedParachainMessages -where - Self: 'static + Send + Sync, - RefundBridgedGrandpaMessages< - Runtime, - Runtime::BridgesGrandpaPalletInstance, - Msgs, - Refund, - Priority, - Id, - >: 'static + Send + Sync, - Runtime: UtilityConfig> - + BoundedBridgeGrandpaConfig - + ParachainsConfig - + MessagesConfig - + RelayersConfig, - Para: RefundableParachainId, - Msgs: RefundableMessagesLaneId, - Refund: RefundCalculator, - Priority: Get, - Id: StaticStrProvider, - CallOf: Dispatchable - + IsSubType, Runtime>> - + GrandpaCallSubType - + ParachainsCallSubType - + MessagesCallSubType, -{ - type Runtime = Runtime; - type Msgs = Msgs; - type Refund = Refund; - type Priority = Priority; - type Id = Id; - - fn expand_call(call: &CallOf) -> Vec<&CallOf> { - match call.is_sub_type() { - Some(UtilityCall::::batch_all { ref calls }) if calls.len() <= 3 => - calls.iter().collect(), - Some(_) => vec![], - None => vec![call], - } - } - - fn parse_and_check_for_obsolete_call( - call: &CallOf, - ) -> Result, TransactionValidityError> { - let calls = Self::expand_call(call); - let total_calls = calls.len(); - let mut calls = calls.into_iter().map(Self::check_obsolete_parsed_call).rev(); - - let msgs_call = calls.next().transpose()?.and_then(|c| c.call_info_for(Msgs::Id::get())); - let para_finality_call = calls - .next() - .transpose()? - .and_then(|c| c.submit_parachain_heads_info_for(Para::BridgedChain::PARACHAIN_ID)); - let relay_finality_call = - calls.next().transpose()?.and_then(|c| c.submit_finality_proof_info()); - - Ok(match (total_calls, relay_finality_call, para_finality_call, msgs_call) { - (3, Some(relay_finality_call), Some(para_finality_call), Some(msgs_call)) => Some( - CallInfo::AllFinalityAndMsgs(relay_finality_call, para_finality_call, msgs_call), - ), - (2, None, Some(para_finality_call), Some(msgs_call)) => - Some(CallInfo::ParachainFinalityAndMsgs(para_finality_call, msgs_call)), - (1, None, None, Some(msgs_call)) => Some(CallInfo::Msgs(msgs_call)), - _ => None, - }) - } - - fn check_obsolete_parsed_call( - call: &CallOf, - ) -> Result<&CallOf, TransactionValidityError> { - call.check_obsolete_submit_finality_proof()?; - call.check_obsolete_submit_parachain_heads()?; - call.check_obsolete_call()?; - Ok(call) - } - - fn additional_call_result_check( - relayer: &Runtime::AccountId, - call_info: &CallInfo, - extra_weight: &mut Weight, - extra_size: &mut u32, - ) -> bool { - // check if relay chain state has been updated - let is_grandpa_call_successful = - RefundBridgedGrandpaMessages::< - Runtime, - Runtime::BridgesGrandpaPalletInstance, - Msgs, - Refund, - Priority, - Id, - >::additional_call_result_check(relayer, call_info, extra_weight, extra_size); - if !is_grandpa_call_successful { - return false - } - - // check if parachain state has been updated - if let Some(para_proof_info) = call_info.submit_parachain_heads_info() { - if !SubmitParachainHeadsHelper::::was_successful( - para_proof_info, - ) { - // we only refund relayer if all calls have updated chain state - log::trace!( - target: "runtime::bridge", - "{} from parachain {} via {:?}: relayer {:?} has submitted invalid parachain finality proof", - Id::STR, - Para::BridgedChain::PARACHAIN_ID, - Msgs::Id::get(), - relayer, - ); - return false - } - } - - true - } -} - -/// Signed extension that refunds a relayer for new messages coming from a standalone (GRANDPA) -/// chain. -/// -/// Also refunds relayer for successful finality delivery if it comes in batch (`utility.batchAll`) -/// with message delivery transaction. Batch may deliver either both relay chain header and -/// parachain head, or just parachain head. Corresponding headers must be used in messages -/// proof verification. -/// -/// Extension does not refund transaction tip due to security reasons. -#[derive( - DefaultNoBound, - CloneNoBound, - Decode, - Encode, - EqNoBound, - PartialEqNoBound, - RuntimeDebugNoBound, - TypeInfo, -)] -#[scale_info(skip_type_params(Runtime, GrandpaInstance, Msgs, Refund, Priority, Id))] -pub struct RefundBridgedGrandpaMessages( - PhantomData<( - // runtime with `frame-utility`, `pallet-bridge-grandpa`, - // `pallet-bridge-messages` and `pallet-bridge-relayers` pallets deployed - Runtime, - // bridge GRANDPA pallet instance, used to track bridged chain state - GrandpaInstance, - // implementation of `RefundableMessagesLaneId` trait, which specifies the instance of - // the used `pallet-bridge-messages` pallet and the lane within this pallet - Msgs, - // implementation of the `RefundCalculator` trait, that is used to compute refund that - // we give to relayer for his transaction - Refund, - // getter for per-message `TransactionPriority` boost that we give to message - // delivery transactions - Priority, - // the runtime-unique identifier of this signed extension - Id, - )>, -); - -impl RefundSignedExtension - for RefundBridgedGrandpaMessages -where - Self: 'static + Send + Sync, - Runtime: UtilityConfig> - + BoundedBridgeGrandpaConfig - + MessagesConfig - + RelayersConfig, - GrandpaInstance: 'static, - Msgs: RefundableMessagesLaneId, - Refund: RefundCalculator, - Priority: Get, - Id: StaticStrProvider, - CallOf: Dispatchable - + IsSubType, Runtime>> - + GrandpaCallSubType - + MessagesCallSubType, -{ - type Runtime = Runtime; - type Msgs = Msgs; - type Refund = Refund; - type Priority = Priority; - type Id = Id; - - fn expand_call(call: &CallOf) -> Vec<&CallOf> { - match call.is_sub_type() { - Some(UtilityCall::::batch_all { ref calls }) if calls.len() <= 2 => - calls.iter().collect(), - Some(_) => vec![], - None => vec![call], - } - } - - fn parse_and_check_for_obsolete_call( - call: &CallOf, - ) -> Result, TransactionValidityError> { - let calls = Self::expand_call(call); - let total_calls = calls.len(); - let mut calls = calls.into_iter().map(Self::check_obsolete_parsed_call).rev(); - - let msgs_call = calls.next().transpose()?.and_then(|c| c.call_info_for(Msgs::Id::get())); - let relay_finality_call = - calls.next().transpose()?.and_then(|c| c.submit_finality_proof_info()); - - Ok(match (total_calls, relay_finality_call, msgs_call) { - (2, Some(relay_finality_call), Some(msgs_call)) => - Some(CallInfo::RelayFinalityAndMsgs(relay_finality_call, msgs_call)), - (1, None, Some(msgs_call)) => Some(CallInfo::Msgs(msgs_call)), - _ => None, - }) - } - - fn check_obsolete_parsed_call( - call: &CallOf, - ) -> Result<&CallOf, TransactionValidityError> { - call.check_obsolete_submit_finality_proof()?; - call.check_obsolete_call()?; - Ok(call) - } - - fn additional_call_result_check( - relayer: &Runtime::AccountId, - call_info: &CallInfo, - extra_weight: &mut Weight, - extra_size: &mut u32, - ) -> bool { - // check if relay chain state has been updated - if let Some(finality_proof_info) = call_info.submit_finality_proof_info() { - if !SubmitFinalityProofHelper::::was_successful( - finality_proof_info.block_number, - ) { - // we only refund relayer if all calls have updated chain state - log::trace!( - target: "runtime::bridge", - "{} via {:?}: relayer {:?} has submitted invalid relay chain finality proof", - Self::Id::STR, - ::Id::get(), - relayer, - ); - return false - } - - // there's a conflict between how bridge GRANDPA pallet works and a `utility.batchAll` - // transaction. If relay chain header is mandatory, the GRANDPA pallet returns - // `Pays::No`, because such transaction is mandatory for operating the bridge. But - // `utility.batchAll` transaction always requires payment. But in both cases we'll - // refund relayer - either explicitly here, or using `Pays::No` if he's choosing - // to submit dedicated transaction. - - // submitter has means to include extra weight/bytes in the `submit_finality_proof` - // call, so let's subtract extra weight/size to avoid refunding for this extra stuff - *extra_weight = (*extra_weight).saturating_add(finality_proof_info.extra_weight); - *extra_size = (*extra_size).saturating_add(finality_proof_info.extra_size); - } - - true - } -} - -/// Transaction extension that refunds a relayer for standalone messages delivery and confirmation -/// transactions. Finality transactions are not refunded. -#[derive( - DefaultNoBound, - CloneNoBound, - Decode, - Encode, - EqNoBound, - PartialEqNoBound, - RuntimeDebugNoBound, - TypeInfo, -)] -#[scale_info(skip_type_params(Runtime, GrandpaInstance, Msgs, Refund, Priority, Id))] -pub struct RefundBridgedMessages( - PhantomData<( - // runtime with `pallet-bridge-messages` and `pallet-bridge-relayers` pallets deployed - Runtime, - // implementation of `RefundableMessagesLaneId` trait, which specifies the instance of - // the used `pallet-bridge-messages` pallet and the lane within this pallet - Msgs, - // implementation of the `RefundCalculator` trait, that is used to compute refund that - // we give to relayer for his transaction - Refund, - // getter for per-message `TransactionPriority` boost that we give to message - // delivery transactions - Priority, - // the runtime-unique identifier of this signed extension - Id, - )>, -); - -impl RefundSignedExtension - for RefundBridgedMessages +/// Verify that the messages pallet call, supported by extension has succeeded. +pub(crate) fn verify_messages_call_succeeded( + call_info: &ExtensionCallInfo< + C::RemoteGrandpaChainBlockNumber, + LaneIdOf, + >, + _call_data: &mut ExtensionCallData, + relayer: &::AccountId, +) -> bool where - Self: 'static + Send + Sync, - Runtime: MessagesConfig + RelayersConfig, - Msgs: RefundableMessagesLaneId, - Refund: RefundCalculator, - Priority: Get, - Id: StaticStrProvider, - CallOf: Dispatchable - + MessagesCallSubType, + C: ExtensionConfig, + C::Runtime: BridgeMessagesConfig, { - type Runtime = Runtime; - type Msgs = Msgs; - type Refund = Refund; - type Priority = Priority; - type Id = Id; - - fn expand_call(call: &CallOf) -> Vec<&CallOf> { - vec![call] - } + let messages_call = call_info.messages_call_info(); - fn parse_and_check_for_obsolete_call( - call: &CallOf, - ) -> Result, TransactionValidityError> { - let call = Self::check_obsolete_parsed_call(call)?; - Ok(call.call_info_for(Msgs::Id::get()).map(CallInfo::Msgs)) - } - - fn check_obsolete_parsed_call( - call: &CallOf, - ) -> Result<&CallOf, TransactionValidityError> { - call.check_obsolete_call()?; - Ok(call) + if !MessagesCallHelper::::was_successful( + messages_call, + ) { + log::trace!( + target: LOG_TARGET, + "{}.{:?}: relayer {:?} has submitted invalid messages call", + C::IdProvider::STR, + call_info.messages_call_info().lane_id(), + relayer, + ); + return false } - fn additional_call_result_check( - _relayer: &Runtime::AccountId, - _call_info: &CallInfo, - _extra_weight: &mut Weight, - _extra_size: &mut u32, - ) -> bool { - // everything is checked by the `RefundTransactionExtension` - true - } + true } #[cfg(test)] -pub(crate) mod tests { +mod tests { use super::*; - use crate::{ - messages_call_ext::{ - BaseMessagesProofInfo, ReceiveMessagesDeliveryProofInfo, ReceiveMessagesProofInfo, - UnrewardedRelayerOccupation, - }, - mock::*, - }; - use bp_header_chain::StoredHeaderDataBuilder; + use crate::mock::*; + + use bp_header_chain::{StoredHeaderDataBuilder, SubmitFinalityProofInfo}; use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, - target_chain::FromBridgedChainMessagesProof, DeliveredMessages, InboundLaneData, - MessageNonce, MessagesOperatingMode, OutboundLaneData, UnrewardedRelayer, - UnrewardedRelayersState, + target_chain::FromBridgedChainMessagesProof, BaseMessagesProofInfo, DeliveredMessages, + InboundLaneData, MessageNonce, MessagesCallInfo, MessagesOperatingMode, OutboundLaneData, + ReceiveMessagesDeliveryProofInfo, ReceiveMessagesProofInfo, UnrewardedRelayer, + UnrewardedRelayerOccupation, UnrewardedRelayersState, }; - use bp_parachains::{BestParaHeadHash, ParaInfo}; + use bp_parachains::{BestParaHeadHash, ParaInfo, SubmitParachainHeadsInfo}; use bp_polkadot_core::parachains::{ParaHeadsProof, ParaId}; - use bp_runtime::{BasicOperatingMode, HeaderId}; + use bp_relayers::RuntimeWithUtilityPallet; + use bp_runtime::{BasicOperatingMode, HeaderId, Parachain}; use bp_test_utils::{make_default_justification, test_keyring, TEST_GRANDPA_SET_ID}; use frame_support::{ + __private::sp_tracing, assert_storage_noop, parameter_types, traits::{fungible::Mutate, ReservableCurrency}, weights::Weight, }; use pallet_bridge_grandpa::{Call as GrandpaCall, Pallet as GrandpaPallet, StoredAuthoritySet}; use pallet_bridge_messages::{Call as MessagesCall, Pallet as MessagesPallet}; - use pallet_bridge_parachains::{ - Call as ParachainsCall, Pallet as ParachainsPallet, RelayBlockHash, - }; + use pallet_bridge_parachains::{Call as ParachainsCall, Pallet as ParachainsPallet}; + use pallet_utility::Call as UtilityCall; use sp_runtime::{ traits::{ConstU64, Header as HeaderT}, transaction_validity::{InvalidTransaction, ValidTransaction}, @@ -968,50 +469,57 @@ pub(crate) mod tests { }; parameter_types! { - pub TestLaneId: LaneId = TEST_LANE_ID; - pub MsgProofsRewardsAccount: RewardsAccountParams = RewardsAccountParams::new( - TEST_LANE_ID, + TestParachain: u32 = BridgedUnderlyingParachain::PARACHAIN_ID; + pub MsgProofsRewardsAccount: RewardsAccountParams = RewardsAccountParams::new( + test_lane_id(), TEST_BRIDGED_CHAIN_ID, RewardsAccountOwner::ThisChain, ); - pub MsgDeliveryProofsRewardsAccount: RewardsAccountParams = RewardsAccountParams::new( - TEST_LANE_ID, + pub MsgDeliveryProofsRewardsAccount: RewardsAccountParams = RewardsAccountParams::new( + test_lane_id(), TEST_BRIDGED_CHAIN_ID, RewardsAccountOwner::BridgedChain, ); } + bp_runtime::generate_static_str_provider!(TestGrandpaExtension); bp_runtime::generate_static_str_provider!(TestExtension); + bp_runtime::generate_static_str_provider!(TestMessagesExtension); - type TestMessagesExtensionProvider = RefundBridgedMessages< + type TestGrandpaExtensionConfig = grandpa_adapter::WithGrandpaChainExtensionConfig< + StrTestGrandpaExtension, TestRuntime, - RefundableMessagesLane<(), TestLaneId>, - ActualFeeRefund, + RuntimeWithUtilityPallet, + (), + (), + (), ConstU64<1>, - StrTestExtension, >; - type TestMessagesExtension = RefundSignedExtensionAdapter; - type TestGrandpaExtensionProvider = RefundBridgedGrandpaMessages< + type TestGrandpaExtension = + BridgeRelayersSignedExtension; + type TestExtensionConfig = parachain_adapter::WithParachainExtensionConfig< + StrTestExtension, TestRuntime, + RuntimeWithUtilityPallet, + (), + (), (), - RefundableMessagesLane<(), TestLaneId>, - ActualFeeRefund, ConstU64<1>, - StrTestExtension, >; - type TestGrandpaExtension = RefundSignedExtensionAdapter; - type TestExtensionProvider = RefundBridgedParachainMessages< + type TestExtension = + BridgeRelayersSignedExtension; + type TestMessagesExtensionConfig = messages_adapter::WithMessagesExtensionConfig< + StrTestMessagesExtension, TestRuntime, - RefundableParachain<(), BridgedUnderlyingParachain>, - RefundableMessagesLane<(), TestLaneId>, - ActualFeeRefund, + (), + (), ConstU64<1>, - StrTestExtension, >; - type TestExtension = RefundSignedExtensionAdapter; + type TestMessagesExtension = + BridgeRelayersSignedExtension; fn initial_balance_of_relayer_account_at_this_chain() -> ThisChainBalance { - let test_stake: ThisChainBalance = TestStake::get(); + let test_stake: ThisChainBalance = Stake::get(); ExistentialDeposit::get().saturating_add(test_stake * 100) } @@ -1026,7 +534,7 @@ pub(crate) mod tests { TestPaymentProcedure::rewards_account(MsgDeliveryProofsRewardsAccount::get()) } - pub fn relayer_account_at_this_chain() -> ThisChainAccountId { + fn relayer_account_at_this_chain() -> ThisChainAccountId { 0 } @@ -1034,13 +542,13 @@ pub(crate) mod tests { 0 } - pub fn initialize_environment( - best_relay_header_number: RelayBlockNumber, - parachain_head_at_relay_header_number: RelayBlockNumber, + fn initialize_environment( + best_relay_header_number: BridgedChainBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) { let authorities = test_keyring().into_iter().map(|(a, w)| (a.into(), w)).collect(); - let best_relay_header = HeaderId(best_relay_header_number, RelayBlockHash::default()); + let best_relay_header = HeaderId(best_relay_header_number, BridgedChainHash::default()); pallet_bridge_grandpa::CurrentAuthoritySet::::put( StoredAuthoritySet::try_new(authorities, TEST_GRANDPA_SET_ID).unwrap(), ); @@ -1050,7 +558,7 @@ pub(crate) mod tests { bp_test_utils::test_header::(0).build(), ); - let para_id = ParaId(BridgedUnderlyingParachain::PARACHAIN_ID); + let para_id = ParaId(TestParachain::get()); let para_info = ParaInfo { best_head_hash: BestParaHeadHash { at_relay_block_number: parachain_head_at_relay_header_number, @@ -1060,7 +568,7 @@ pub(crate) mod tests { }; pallet_bridge_parachains::ParasInfo::::insert(para_id, para_info); - let lane_id = TestLaneId::get(); + let lane_id = test_lane_id(); let in_lane_data = InboundLaneData { last_confirmed_nonce: best_message, ..Default::default() }; pallet_bridge_messages::InboundLanes::::insert(lane_id, in_lane_data); @@ -1078,7 +586,7 @@ pub(crate) mod tests { .unwrap(); } - fn submit_relay_header_call(relay_header_number: RelayBlockNumber) -> RuntimeCall { + fn submit_relay_header_call(relay_header_number: BridgedChainBlockNumber) -> RuntimeCall { let relay_header = BridgedChainHeader::new( relay_header_number, Default::default(), @@ -1094,7 +602,7 @@ pub(crate) mod tests { }) } - pub fn submit_relay_header_call_ex(relay_header_number: RelayBlockNumber) -> RuntimeCall { + fn submit_relay_header_call_ex(relay_header_number: BridgedChainBlockNumber) -> RuntimeCall { let relay_header = BridgedChainHeader::new( relay_header_number, Default::default(), @@ -1113,12 +621,12 @@ pub(crate) mod tests { } fn submit_parachain_head_call( - parachain_head_at_relay_header_number: RelayBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, ) -> RuntimeCall { RuntimeCall::BridgeParachains(ParachainsCall::submit_parachain_heads { - at_relay_block: (parachain_head_at_relay_header_number, RelayBlockHash::default()), + at_relay_block: (parachain_head_at_relay_header_number, BridgedChainHash::default()), parachains: vec![( - ParaId(BridgedUnderlyingParachain::PARACHAIN_ID), + ParaId(TestParachain::get()), [parachain_head_at_relay_header_number as u8; 32].into(), )], parachain_heads_proof: ParaHeadsProof { storage_proof: Default::default() }, @@ -1126,12 +634,12 @@ pub(crate) mod tests { } pub fn submit_parachain_head_call_ex( - parachain_head_at_relay_header_number: RelayBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, ) -> RuntimeCall { RuntimeCall::BridgeParachains(ParachainsCall::submit_parachain_heads_ex { - at_relay_block: (parachain_head_at_relay_header_number, RelayBlockHash::default()), + at_relay_block: (parachain_head_at_relay_header_number, BridgedChainHash::default()), parachains: vec![( - ParaId(BridgedUnderlyingParachain::PARACHAIN_ID), + ParaId(TestParachain::get()), [parachain_head_at_relay_header_number as u8; 32].into(), )], parachain_heads_proof: ParaHeadsProof { storage_proof: Default::default() }, @@ -1145,10 +653,11 @@ pub(crate) mod tests { proof: Box::new(FromBridgedChainMessagesProof { bridged_header_hash: Default::default(), storage_proof: Default::default(), - lane: TestLaneId::get(), + lane: test_lane_id(), nonces_start: pallet_bridge_messages::InboundLanes::::get( - TEST_LANE_ID, + test_lane_id(), ) + .unwrap() .last_delivered_nonce() + 1, nonces_end: best_message, @@ -1163,7 +672,7 @@ pub(crate) mod tests { proof: FromBridgedChainMessagesDeliveryProof { bridged_header_hash: Default::default(), storage_proof: Default::default(), - lane: TestLaneId::get(), + lane: test_lane_id(), }, relayers_state: UnrewardedRelayersState { last_delivered_nonce: best_message, @@ -1173,7 +682,7 @@ pub(crate) mod tests { } fn parachain_finality_and_delivery_batch_call( - parachain_head_at_relay_header_number: RelayBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1185,7 +694,7 @@ pub(crate) mod tests { } fn parachain_finality_and_confirmation_batch_call( - parachain_head_at_relay_header_number: RelayBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1197,7 +706,7 @@ pub(crate) mod tests { } fn relay_finality_and_delivery_batch_call( - relay_header_number: RelayBlockNumber, + relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1209,7 +718,7 @@ pub(crate) mod tests { } fn relay_finality_and_delivery_batch_call_ex( - relay_header_number: RelayBlockNumber, + relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1221,7 +730,7 @@ pub(crate) mod tests { } fn relay_finality_and_confirmation_batch_call( - relay_header_number: RelayBlockNumber, + relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1233,7 +742,7 @@ pub(crate) mod tests { } fn relay_finality_and_confirmation_batch_call_ex( - relay_header_number: RelayBlockNumber, + relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1245,8 +754,8 @@ pub(crate) mod tests { } fn all_finality_and_delivery_batch_call( - relay_header_number: RelayBlockNumber, - parachain_head_at_relay_header_number: RelayBlockNumber, + relay_header_number: BridgedChainBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1259,8 +768,8 @@ pub(crate) mod tests { } fn all_finality_and_delivery_batch_call_ex( - relay_header_number: RelayBlockNumber, - parachain_head_at_relay_header_number: RelayBlockNumber, + relay_header_number: BridgedChainBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1273,8 +782,8 @@ pub(crate) mod tests { } fn all_finality_and_confirmation_batch_call( - relay_header_number: RelayBlockNumber, - parachain_head_at_relay_header_number: RelayBlockNumber, + relay_header_number: BridgedChainBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1287,8 +796,8 @@ pub(crate) mod tests { } fn all_finality_and_confirmation_batch_call_ex( - relay_header_number: RelayBlockNumber, - parachain_head_at_relay_header_number: RelayBlockNumber, + relay_header_number: BridgedChainBlockNumber, + parachain_head_at_relay_header_number: BridgedChainBlockNumber, best_message: MessageNonce, ) -> RuntimeCall { RuntimeCall::Utility(UtilityCall::batch_all { @@ -1300,10 +809,11 @@ pub(crate) mod tests { }) } - fn all_finality_pre_dispatch_data() -> PreDispatchData { + fn all_finality_pre_dispatch_data( + ) -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::AllFinalityAndMsgs( + call_info: ExtensionCallInfo::AllFinalityAndMsgs( SubmitFinalityProofInfo { block_number: 200, current_set_id: None, @@ -1314,38 +824,40 @@ pub(crate) mod tests { }, SubmitParachainHeadsInfo { at_relay_block: HeaderId(200, [0u8; 32].into()), - para_id: ParaId(BridgedUnderlyingParachain::PARACHAIN_ID), + para_id: ParaId(TestParachain::get()), para_head_hash: [200u8; 32].into(), is_free_execution_expected: false, }, MessagesCallInfo::ReceiveMessagesProof(ReceiveMessagesProofInfo { base: BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, + lane_id: test_lane_id(), bundled_range: 101..=200, best_stored_nonce: 100, }, unrewarded_relayers: UnrewardedRelayerOccupation { free_relayer_slots: - BridgedUnderlyingChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + BridgedUnderlyingParachain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, free_message_slots: - BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + BridgedUnderlyingParachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, }, }), ), } } - fn all_finality_pre_dispatch_data_ex() -> PreDispatchData { + #[cfg(test)] + fn all_finality_pre_dispatch_data_ex( + ) -> PreDispatchData { let mut data = all_finality_pre_dispatch_data(); - data.call_info.submit_finality_proof_info_mut().unwrap().current_set_id = - Some(TEST_GRANDPA_SET_ID); + data.submit_finality_proof_info_mut().unwrap().current_set_id = Some(TEST_GRANDPA_SET_ID); data } - fn all_finality_confirmation_pre_dispatch_data() -> PreDispatchData { + fn all_finality_confirmation_pre_dispatch_data( + ) -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::AllFinalityAndMsgs( + call_info: ExtensionCallInfo::AllFinalityAndMsgs( SubmitFinalityProofInfo { block_number: 200, current_set_id: None, @@ -1356,13 +868,13 @@ pub(crate) mod tests { }, SubmitParachainHeadsInfo { at_relay_block: HeaderId(200, [0u8; 32].into()), - para_id: ParaId(BridgedUnderlyingParachain::PARACHAIN_ID), + para_id: ParaId(TestParachain::get()), para_head_hash: [200u8; 32].into(), is_free_execution_expected: false, }, MessagesCallInfo::ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo( BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, + lane_id: test_lane_id(), bundled_range: 101..=200, best_stored_nonce: 100, }, @@ -1371,17 +883,18 @@ pub(crate) mod tests { } } - fn all_finality_confirmation_pre_dispatch_data_ex() -> PreDispatchData { + fn all_finality_confirmation_pre_dispatch_data_ex( + ) -> PreDispatchData { let mut data = all_finality_confirmation_pre_dispatch_data(); - data.call_info.submit_finality_proof_info_mut().unwrap().current_set_id = - Some(TEST_GRANDPA_SET_ID); + data.submit_finality_proof_info_mut().unwrap().current_set_id = Some(TEST_GRANDPA_SET_ID); data } - fn relay_finality_pre_dispatch_data() -> PreDispatchData { + fn relay_finality_pre_dispatch_data( + ) -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::RelayFinalityAndMsgs( + call_info: ExtensionCallInfo::RelayFinalityAndMsgs( SubmitFinalityProofInfo { block_number: 200, current_set_id: None, @@ -1392,32 +905,33 @@ pub(crate) mod tests { }, MessagesCallInfo::ReceiveMessagesProof(ReceiveMessagesProofInfo { base: BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, + lane_id: test_lane_id(), bundled_range: 101..=200, best_stored_nonce: 100, }, unrewarded_relayers: UnrewardedRelayerOccupation { free_relayer_slots: - BridgedUnderlyingChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + BridgedUnderlyingParachain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, free_message_slots: - BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + BridgedUnderlyingParachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, }, }), ), } } - fn relay_finality_pre_dispatch_data_ex() -> PreDispatchData { + fn relay_finality_pre_dispatch_data_ex( + ) -> PreDispatchData { let mut data = relay_finality_pre_dispatch_data(); - data.call_info.submit_finality_proof_info_mut().unwrap().current_set_id = - Some(TEST_GRANDPA_SET_ID); + data.submit_finality_proof_info_mut().unwrap().current_set_id = Some(TEST_GRANDPA_SET_ID); data } - fn relay_finality_confirmation_pre_dispatch_data() -> PreDispatchData { + fn relay_finality_confirmation_pre_dispatch_data( + ) -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::RelayFinalityAndMsgs( + call_info: ExtensionCallInfo::RelayFinalityAndMsgs( SubmitFinalityProofInfo { block_number: 200, current_set_id: None, @@ -1428,7 +942,7 @@ pub(crate) mod tests { }, MessagesCallInfo::ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo( BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, + lane_id: test_lane_id(), bundled_range: 101..=200, best_stored_nonce: 100, }, @@ -1437,53 +951,55 @@ pub(crate) mod tests { } } - fn relay_finality_confirmation_pre_dispatch_data_ex() -> PreDispatchData { + fn relay_finality_confirmation_pre_dispatch_data_ex( + ) -> PreDispatchData { let mut data = relay_finality_confirmation_pre_dispatch_data(); - data.call_info.submit_finality_proof_info_mut().unwrap().current_set_id = - Some(TEST_GRANDPA_SET_ID); + data.submit_finality_proof_info_mut().unwrap().current_set_id = Some(TEST_GRANDPA_SET_ID); data } - fn parachain_finality_pre_dispatch_data() -> PreDispatchData { + fn parachain_finality_pre_dispatch_data( + ) -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::ParachainFinalityAndMsgs( + call_info: ExtensionCallInfo::ParachainFinalityAndMsgs( SubmitParachainHeadsInfo { at_relay_block: HeaderId(200, [0u8; 32].into()), - para_id: ParaId(BridgedUnderlyingParachain::PARACHAIN_ID), + para_id: ParaId(TestParachain::get()), para_head_hash: [200u8; 32].into(), is_free_execution_expected: false, }, MessagesCallInfo::ReceiveMessagesProof(ReceiveMessagesProofInfo { base: BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, + lane_id: test_lane_id(), bundled_range: 101..=200, best_stored_nonce: 100, }, unrewarded_relayers: UnrewardedRelayerOccupation { free_relayer_slots: - BridgedUnderlyingChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + BridgedUnderlyingParachain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, free_message_slots: - BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + BridgedUnderlyingParachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, }, }), ), } } - fn parachain_finality_confirmation_pre_dispatch_data() -> PreDispatchData { + fn parachain_finality_confirmation_pre_dispatch_data( + ) -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::ParachainFinalityAndMsgs( + call_info: ExtensionCallInfo::ParachainFinalityAndMsgs( SubmitParachainHeadsInfo { at_relay_block: HeaderId(200, [0u8; 32].into()), - para_id: ParaId(BridgedUnderlyingParachain::PARACHAIN_ID), + para_id: ParaId(TestParachain::get()), para_head_hash: [200u8; 32].into(), is_free_execution_expected: false, }, MessagesCallInfo::ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo( BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, + lane_id: test_lane_id(), bundled_range: 101..=200, best_stored_nonce: 100, }, @@ -1492,33 +1008,35 @@ pub(crate) mod tests { } } - fn delivery_pre_dispatch_data() -> PreDispatchData { + fn delivery_pre_dispatch_data( + ) -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::Msgs(MessagesCallInfo::ReceiveMessagesProof( + call_info: ExtensionCallInfo::Msgs(MessagesCallInfo::ReceiveMessagesProof( ReceiveMessagesProofInfo { base: BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, + lane_id: test_lane_id(), bundled_range: 101..=200, best_stored_nonce: 100, }, unrewarded_relayers: UnrewardedRelayerOccupation { free_relayer_slots: - BridgedUnderlyingChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + BridgedUnderlyingParachain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, free_message_slots: - BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + BridgedUnderlyingParachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, }, }, )), } } - fn confirmation_pre_dispatch_data() -> PreDispatchData { + fn confirmation_pre_dispatch_data( + ) -> PreDispatchData { PreDispatchData { relayer: relayer_account_at_this_chain(), - call_info: CallInfo::Msgs(MessagesCallInfo::ReceiveMessagesDeliveryProof( + call_info: ExtensionCallInfo::Msgs(MessagesCallInfo::ReceiveMessagesDeliveryProof( ReceiveMessagesDeliveryProofInfo(BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, + lane_id: test_lane_id(), bundled_range: 101..=200, best_stored_nonce: 100, }), @@ -1527,14 +1045,18 @@ pub(crate) mod tests { } fn set_bundled_range_end( - mut pre_dispatch_data: PreDispatchData, + mut pre_dispatch_data: PreDispatchData< + ThisChainAccountId, + BridgedChainBlockNumber, + TestLaneIdType, + >, end: MessageNonce, - ) -> PreDispatchData { + ) -> PreDispatchData { let msg_info = match pre_dispatch_data.call_info { - CallInfo::AllFinalityAndMsgs(_, _, ref mut info) => info, - CallInfo::RelayFinalityAndMsgs(_, ref mut info) => info, - CallInfo::ParachainFinalityAndMsgs(_, ref mut info) => info, - CallInfo::Msgs(ref mut info) => info, + ExtensionCallInfo::AllFinalityAndMsgs(_, _, ref mut info) => info, + ExtensionCallInfo::RelayFinalityAndMsgs(_, ref mut info) => info, + ExtensionCallInfo::ParachainFinalityAndMsgs(_, ref mut info) => info, + ExtensionCallInfo::Msgs(ref mut info) => info, }; if let MessagesCallInfo::ReceiveMessagesProof(ref mut msg_info) = msg_info { @@ -1545,20 +1067,17 @@ pub(crate) mod tests { } fn run_validate(call: RuntimeCall) -> TransactionValidity { - let extension: TestExtension = - RefundSignedExtensionAdapter(RefundBridgedParachainMessages(PhantomData)); + let extension: TestExtension = BridgeRelayersSignedExtension(PhantomData); extension.validate(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) } fn run_grandpa_validate(call: RuntimeCall) -> TransactionValidity { - let extension: TestGrandpaExtension = - RefundSignedExtensionAdapter(RefundBridgedGrandpaMessages(PhantomData)); + let extension: TestGrandpaExtension = BridgeRelayersSignedExtension(PhantomData); extension.validate(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) } fn run_messages_validate(call: RuntimeCall) -> TransactionValidity { - let extension: TestMessagesExtension = - RefundSignedExtensionAdapter(RefundBridgedMessages(PhantomData)); + let extension: TestMessagesExtension = BridgeRelayersSignedExtension(PhantomData); extension.validate(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) } @@ -1571,25 +1090,32 @@ pub(crate) mod tests { fn run_pre_dispatch( call: RuntimeCall, - ) -> Result>, TransactionValidityError> { - let extension: TestExtension = - RefundSignedExtensionAdapter(RefundBridgedParachainMessages(PhantomData)); + ) -> Result< + Option>, + TransactionValidityError, + > { + sp_tracing::try_init_simple(); + let extension: TestExtension = BridgeRelayersSignedExtension(PhantomData); extension.pre_dispatch(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) } fn run_grandpa_pre_dispatch( call: RuntimeCall, - ) -> Result>, TransactionValidityError> { - let extension: TestGrandpaExtension = - RefundSignedExtensionAdapter(RefundBridgedGrandpaMessages(PhantomData)); + ) -> Result< + Option>, + TransactionValidityError, + > { + let extension: TestGrandpaExtension = BridgeRelayersSignedExtension(PhantomData); extension.pre_dispatch(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) } fn run_messages_pre_dispatch( call: RuntimeCall, - ) -> Result>, TransactionValidityError> { - let extension: TestMessagesExtension = - RefundSignedExtensionAdapter(RefundBridgedMessages(PhantomData)); + ) -> Result< + Option>, + TransactionValidityError, + > { + let extension: TestMessagesExtension = BridgeRelayersSignedExtension(PhantomData); extension.pre_dispatch(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) } @@ -1609,7 +1135,9 @@ pub(crate) mod tests { } fn run_post_dispatch( - pre_dispatch_data: Option>, + pre_dispatch_data: Option< + PreDispatchData, + >, dispatch_result: DispatchResult, ) { let post_dispatch_result = TestExtension::post_dispatch( @@ -1651,46 +1179,28 @@ pub(crate) mod tests { Balances::set_balance(&relayer_account_at_this_chain(), ExistentialDeposit::get()); // message delivery is failing - let fns = [run_validate, run_grandpa_validate, run_messages_validate]; - for f in fns { - assert_eq!(f(message_delivery_call(200)), Ok(Default::default()),); - assert_eq!( - f(parachain_finality_and_delivery_batch_call(200, 200)), - Ok(Default::default()), - ); - assert_eq!( - f(all_finality_and_delivery_batch_call(200, 200, 200)), - Ok(Default::default()), - ); - assert_eq!( - f(all_finality_and_delivery_batch_call_ex(200, 200, 200)), - Ok(Default::default()), - ); - } - - // message confirmation validation is passing + assert_eq!(run_validate(message_delivery_call(200)), Ok(Default::default()),); assert_eq!( - ignore_priority(run_validate(message_confirmation_call(200))), + run_validate(parachain_finality_and_delivery_batch_call(200, 200)), Ok(Default::default()), ); assert_eq!( - ignore_priority(run_messages_validate(message_confirmation_call(200))), + run_validate(all_finality_and_delivery_batch_call(200, 200, 200)), Ok(Default::default()), ); + // message confirmation validation is passing assert_eq!( - ignore_priority(run_validate(parachain_finality_and_confirmation_batch_call( - 200, 200 - ))), + ignore_priority(run_validate(message_confirmation_call(200))), Ok(Default::default()), ); assert_eq!( - ignore_priority(run_validate(all_finality_and_confirmation_batch_call( - 200, 200, 200 + ignore_priority(run_validate(parachain_finality_and_confirmation_batch_call( + 200, 200 ))), Ok(Default::default()), ); assert_eq!( - ignore_priority(run_validate(all_finality_and_confirmation_batch_call_ex( + ignore_priority(run_validate(all_finality_and_confirmation_batch_call( 200, 200, 200 ))), Ok(Default::default()), @@ -1706,28 +1216,25 @@ pub(crate) mod tests { BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000) .unwrap(); - let fns = [run_validate, run_grandpa_validate, run_messages_validate]; - for f in fns { - let priority_of_100_messages_delivery = - f(message_delivery_call(200)).unwrap().priority; - let priority_of_200_messages_delivery = - f(message_delivery_call(300)).unwrap().priority; - assert!( - priority_of_200_messages_delivery > priority_of_100_messages_delivery, - "Invalid priorities: {} for 200 messages vs {} for 100 messages", - priority_of_200_messages_delivery, - priority_of_100_messages_delivery, - ); + let priority_of_100_messages_delivery = + run_validate(message_delivery_call(200)).unwrap().priority; + let priority_of_200_messages_delivery = + run_validate(message_delivery_call(300)).unwrap().priority; + assert!( + priority_of_200_messages_delivery > priority_of_100_messages_delivery, + "Invalid priorities: {} for 200 messages vs {} for 100 messages", + priority_of_200_messages_delivery, + priority_of_100_messages_delivery, + ); - let priority_of_100_messages_confirmation = - f(message_confirmation_call(200)).unwrap().priority; - let priority_of_200_messages_confirmation = - f(message_confirmation_call(300)).unwrap().priority; - assert_eq!( - priority_of_100_messages_confirmation, - priority_of_200_messages_confirmation - ); - } + let priority_of_100_messages_confirmation = + run_validate(message_confirmation_call(200)).unwrap().priority; + let priority_of_200_messages_confirmation = + run_validate(message_confirmation_call(300)).unwrap().priority; + assert_eq!( + priority_of_100_messages_confirmation, + priority_of_200_messages_confirmation + ); }); } @@ -1739,26 +1246,23 @@ pub(crate) mod tests { BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000) .unwrap(); - let fns = [run_validate, run_grandpa_validate, run_messages_validate]; - for f in fns { - let priority_of_max_messages_delivery = f(message_delivery_call( - 100 + BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, - )) - .unwrap() - .priority; - let priority_of_more_than_max_messages_delivery = f(message_delivery_call( - 100 + BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX + 1, - )) - .unwrap() - .priority; + let priority_of_max_messages_delivery = run_validate(message_delivery_call( + 100 + BridgedUnderlyingParachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + )) + .unwrap() + .priority; + let priority_of_more_than_max_messages_delivery = run_validate(message_delivery_call( + 100 + BridgedUnderlyingParachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX + 1, + )) + .unwrap() + .priority; - assert!( - priority_of_max_messages_delivery > priority_of_more_than_max_messages_delivery, - "Invalid priorities: {} for MAX messages vs {} for MAX+1 messages", - priority_of_max_messages_delivery, - priority_of_more_than_max_messages_delivery, - ); - } + assert!( + priority_of_max_messages_delivery > priority_of_more_than_max_messages_delivery, + "Invalid priorities: {} for MAX messages vs {} for MAX+1 messages", + priority_of_max_messages_delivery, + priority_of_more_than_max_messages_delivery, + ); }); } @@ -1775,15 +1279,10 @@ pub(crate) mod tests { ignore_priority(run_validate(message_confirmation_call(200))), Ok(ValidTransaction::default()), ); - assert_eq!( ignore_priority(run_messages_validate(message_delivery_call(200))), Ok(ValidTransaction::default()), ); - assert_eq!( - ignore_priority(run_messages_validate(message_confirmation_call(200))), - Ok(ValidTransaction::default()), - ); assert_eq!( ignore_priority(run_validate(parachain_finality_and_delivery_batch_call(200, 200))), @@ -1800,24 +1299,12 @@ pub(crate) mod tests { ignore_priority(run_validate(all_finality_and_delivery_batch_call(200, 200, 200))), Ok(ValidTransaction::default()), ); - assert_eq!( - ignore_priority(run_validate(all_finality_and_delivery_batch_call_ex( - 200, 200, 200 - ))), - Ok(ValidTransaction::default()), - ); assert_eq!( ignore_priority(run_validate(all_finality_and_confirmation_batch_call( 200, 200, 200 ))), Ok(ValidTransaction::default()), ); - assert_eq!( - ignore_priority(run_validate(all_finality_and_confirmation_batch_call_ex( - 200, 200, 200 - ))), - Ok(ValidTransaction::default()), - ); }); } @@ -2103,13 +1590,10 @@ pub(crate) mod tests { let call = RuntimeCall::Utility(UtilityCall::batch_all { calls: vec![ RuntimeCall::BridgeParachains(ParachainsCall::submit_parachain_heads { - at_relay_block: (100, RelayBlockHash::default()), + at_relay_block: (100, BridgedChainHash::default()), parachains: vec![ - (ParaId(BridgedUnderlyingParachain::PARACHAIN_ID), [1u8; 32].into()), - ( - ParaId(BridgedUnderlyingParachain::PARACHAIN_ID + 1), - [1u8; 32].into(), - ), + (ParaId(TestParachain::get()), [1u8; 32].into()), + (ParaId(TestParachain::get() + 1), [1u8; 32].into()), ], parachain_heads_proof: ParaHeadsProof { storage_proof: Default::default() }, }), @@ -2250,7 +1734,7 @@ pub(crate) mod tests { // now repeat the same with size+weight refund: we expect smaller reward let mut pre_dispatch_data = all_finality_pre_dispatch_data(); match pre_dispatch_data.call_info { - CallInfo::AllFinalityAndMsgs(ref mut info, ..) => { + ExtensionCallInfo::AllFinalityAndMsgs(ref mut info, ..) => { info.extra_weight.set_ref_time( frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND, ); @@ -2356,7 +1840,7 @@ pub(crate) mod tests { let delivery_rewards_account_balance = Balances::free_balance(delivery_rewards_account()); - let test_stake: ThisChainBalance = TestStake::get(); + let test_stake: ThisChainBalance = Stake::get(); Balances::set_balance( &relayer_account_at_this_chain(), ExistentialDeposit::get() + test_stake * 10, @@ -2426,10 +1910,14 @@ pub(crate) mod tests { } fn run_analyze_call_result( - pre_dispatch_data: PreDispatchData, + pre_dispatch_data: PreDispatchData< + ThisChainAccountId, + BridgedChainBlockNumber, + TestLaneIdType, + >, dispatch_result: DispatchResult, - ) -> RelayerAccountAction { - TestExtensionProvider::analyze_call_result( + ) -> RelayerAccountAction { + TestExtension::analyze_call_result( Some(Some(pre_dispatch_data)), &dispatch_info(), &post_dispatch_info(), @@ -2494,41 +1982,29 @@ pub(crate) mod tests { } #[test] - fn messages_ext_only_parses_standalone_transactions() { + fn grandpa_ext_only_parses_valid_batches() { run_test(|| { initialize_environment(100, 100, 100); // relay + parachain + message delivery calls batch is ignored assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( + TestGrandpaExtensionConfig::parse_and_check_for_obsolete_call( &all_finality_and_delivery_batch_call(200, 200, 200) ), Ok(None), ); - assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( - &all_finality_and_delivery_batch_call_ex(200, 200, 200) - ), - Ok(None), - ); // relay + parachain + message confirmation calls batch is ignored assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( + TestGrandpaExtensionConfig::parse_and_check_for_obsolete_call( &all_finality_and_confirmation_batch_call(200, 200, 200) ), Ok(None), ); - assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( - &all_finality_and_confirmation_batch_call_ex(200, 200, 200) - ), - Ok(None), - ); // parachain + message delivery call batch is ignored assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( + TestGrandpaExtensionConfig::parse_and_check_for_obsolete_call( ¶chain_finality_and_delivery_batch_call(200, 200) ), Ok(None), @@ -2536,43 +2012,31 @@ pub(crate) mod tests { // parachain + message confirmation call batch is ignored assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( + TestGrandpaExtensionConfig::parse_and_check_for_obsolete_call( ¶chain_finality_and_confirmation_batch_call(200, 200) ), Ok(None), ); - // relay + message delivery call batch is ignored + // relay + message delivery call batch is accepted assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( + TestGrandpaExtensionConfig::parse_and_check_for_obsolete_call( &relay_finality_and_delivery_batch_call(200, 200) ), - Ok(None), - ); - assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( - &relay_finality_and_delivery_batch_call_ex(200, 200) - ), - Ok(None), + Ok(Some(relay_finality_pre_dispatch_data().call_info)), ); - // relay + message confirmation call batch is ignored + // relay + message confirmation call batch is accepted assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( + TestGrandpaExtensionConfig::parse_and_check_for_obsolete_call( &relay_finality_and_confirmation_batch_call(200, 200) ), - Ok(None), - ); - assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( - &relay_finality_and_confirmation_batch_call_ex(200, 200) - ), - Ok(None), + Ok(Some(relay_finality_confirmation_pre_dispatch_data().call_info)), ); // message delivery call batch is accepted assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( + TestGrandpaExtensionConfig::parse_and_check_for_obsolete_call( &message_delivery_call(200) ), Ok(Some(delivery_pre_dispatch_data().call_info)), @@ -2580,7 +2044,7 @@ pub(crate) mod tests { // message confirmation call batch is accepted assert_eq!( - TestMessagesExtensionProvider::parse_and_check_for_obsolete_call( + TestGrandpaExtensionConfig::parse_and_check_for_obsolete_call( &message_confirmation_call(200) ), Ok(Some(confirmation_pre_dispatch_data().call_info)), @@ -2589,66 +2053,19 @@ pub(crate) mod tests { } #[test] - fn messages_ext_rejects_calls_with_obsolete_messages() { - run_test(|| { - initialize_environment(100, 100, 100); - - assert_eq!( - run_messages_pre_dispatch(message_delivery_call(100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_messages_pre_dispatch(message_confirmation_call(100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - - assert_eq!( - run_messages_validate(message_delivery_call(100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_messages_validate(message_confirmation_call(100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - }); - } - - #[test] - fn messages_ext_accepts_calls_with_new_messages() { - run_test(|| { - initialize_environment(100, 100, 100); - - assert_eq!( - run_messages_pre_dispatch(message_delivery_call(200)), - Ok(Some(delivery_pre_dispatch_data())), - ); - assert_eq!( - run_messages_pre_dispatch(message_confirmation_call(200)), - Ok(Some(confirmation_pre_dispatch_data())), - ); - - assert_eq!(run_messages_validate(message_delivery_call(200)), Ok(Default::default()),); - assert_eq!( - run_messages_validate(message_confirmation_call(200)), - Ok(Default::default()), - ); - }); - } - - #[test] - fn grandpa_ext_only_parses_valid_batches() { + fn messages_ext_only_parses_standalone_transactions() { run_test(|| { initialize_environment(100, 100, 100); // relay + parachain + message delivery calls batch is ignored assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( &all_finality_and_delivery_batch_call(200, 200, 200) ), Ok(None), ); assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( &all_finality_and_delivery_batch_call_ex(200, 200, 200) ), Ok(None), @@ -2656,13 +2073,13 @@ pub(crate) mod tests { // relay + parachain + message confirmation calls batch is ignored assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( &all_finality_and_confirmation_batch_call(200, 200, 200) ), Ok(None), ); assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( &all_finality_and_confirmation_batch_call_ex(200, 200, 200) ), Ok(None), @@ -2670,7 +2087,7 @@ pub(crate) mod tests { // parachain + message delivery call batch is ignored assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( ¶chain_finality_and_delivery_batch_call(200, 200) ), Ok(None), @@ -2678,43 +2095,43 @@ pub(crate) mod tests { // parachain + message confirmation call batch is ignored assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( ¶chain_finality_and_confirmation_batch_call(200, 200) ), Ok(None), ); - // relay + message delivery call batch is accepted + // relay + message delivery call batch is ignored assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( &relay_finality_and_delivery_batch_call(200, 200) ), - Ok(Some(relay_finality_pre_dispatch_data().call_info)), + Ok(None), ); assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( &relay_finality_and_delivery_batch_call_ex(200, 200) ), - Ok(Some(relay_finality_pre_dispatch_data_ex().call_info)), + Ok(None), ); - // relay + message confirmation call batch is accepted + // relay + message confirmation call batch is ignored assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( &relay_finality_and_confirmation_batch_call(200, 200) ), - Ok(Some(relay_finality_confirmation_pre_dispatch_data().call_info)), + Ok(None), ); assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( &relay_finality_and_confirmation_batch_call_ex(200, 200) ), - Ok(Some(relay_finality_confirmation_pre_dispatch_data_ex().call_info)), + Ok(None), ); // message delivery call batch is accepted assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( &message_delivery_call(200) ), Ok(Some(delivery_pre_dispatch_data().call_info)), @@ -2722,7 +2139,7 @@ pub(crate) mod tests { // message confirmation call batch is accepted assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( + TestMessagesExtensionConfig::parse_and_check_for_obsolete_call( &message_confirmation_call(200) ), Ok(Some(confirmation_pre_dispatch_data().call_info)), @@ -2730,6 +2147,53 @@ pub(crate) mod tests { }); } + #[test] + fn messages_ext_rejects_calls_with_obsolete_messages() { + run_test(|| { + initialize_environment(100, 100, 100); + + assert_eq!( + run_messages_pre_dispatch(message_delivery_call(100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + assert_eq!( + run_messages_pre_dispatch(message_confirmation_call(100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + + assert_eq!( + run_messages_validate(message_delivery_call(100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + assert_eq!( + run_messages_validate(message_confirmation_call(100)), + Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), + ); + }); + } + + #[test] + fn messages_ext_accepts_calls_with_new_messages() { + run_test(|| { + initialize_environment(100, 100, 100); + + assert_eq!( + run_messages_pre_dispatch(message_delivery_call(200)), + Ok(Some(delivery_pre_dispatch_data())), + ); + assert_eq!( + run_messages_pre_dispatch(message_confirmation_call(200)), + Ok(Some(confirmation_pre_dispatch_data())), + ); + + assert_eq!(run_messages_validate(message_delivery_call(200)), Ok(Default::default()),); + assert_eq!( + run_messages_validate(message_confirmation_call(200)), + Ok(Default::default()), + ); + }); + } + #[test] fn grandpa_ext_rejects_batch_with_obsolete_relay_chain_header() { run_test(|| { @@ -2874,7 +2338,7 @@ pub(crate) mod tests { fn does_not_panic_on_boosting_priority_of_empty_message_delivery_transaction() { run_test(|| { let best_delivered_message = - BridgedUnderlyingChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + BridgedUnderlyingParachain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; initialize_environment(100, 100, best_delivered_message); // register relayer so it gets priority boost @@ -2882,7 +2346,7 @@ pub(crate) mod tests { .unwrap(); // allow empty message delivery transactions - let lane_id = TestLaneId::get(); + let lane_id = test_lane_id(); let in_lane_data = InboundLaneData { last_confirmed_nonce: 0, relayers: vec![UnrewardedRelayer { @@ -2890,6 +2354,7 @@ pub(crate) mod tests { messages: DeliveredMessages { begin: 1, end: best_delivered_message }, }] .into(), + ..Default::default() }; pallet_bridge_messages::InboundLanes::::insert(lane_id, in_lane_data); diff --git a/bridges/modules/relayers/src/extension/parachain_adapter.rs b/bridges/modules/relayers/src/extension/parachain_adapter.rs new file mode 100644 index 0000000000000000000000000000000000000000..69cf766dd674de6d68d20531dec27f7b2424e419 --- /dev/null +++ b/bridges/modules/relayers/src/extension/parachain_adapter.rs @@ -0,0 +1,188 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Adapter that allows using `pallet-bridge-relayers` as a signed extension in the +//! bridge with remote parachain. + +use crate::{ + extension::{ + grandpa_adapter::verify_submit_finality_proof_succeeded, verify_messages_call_succeeded, + }, + Config as BridgeRelayersConfig, LOG_TARGET, +}; + +use bp_relayers::{BatchCallUnpacker, ExtensionCallData, ExtensionCallInfo, ExtensionConfig}; +use bp_runtime::{Parachain, StaticStrProvider}; +use frame_support::dispatch::{DispatchInfo, PostDispatchInfo}; +use frame_system::Config as SystemConfig; +use pallet_bridge_grandpa::{ + CallSubType as BridgeGrandpaCallSubtype, Config as BridgeGrandpaConfig, +}; +use pallet_bridge_messages::{ + CallSubType as BridgeMessagesCallSubType, Config as BridgeMessagesConfig, LaneIdOf, +}; +use pallet_bridge_parachains::{ + CallSubType as BridgeParachainsCallSubtype, Config as BridgeParachainsConfig, + SubmitParachainHeadsHelper, +}; +use sp_runtime::{ + traits::{Dispatchable, Get}, + transaction_validity::{TransactionPriority, TransactionValidityError}, +}; +use sp_std::marker::PhantomData; + +/// Adapter to be used in signed extension configuration, when bridging with remote parachains. +pub struct WithParachainExtensionConfig< + // signed extension identifier + IdProvider, + // runtime that implements `BridgeMessagesConfig`, which + // uses `BridgeParachainsConfig` to receive messages and + // confirmations from the remote chain. + Runtime, + // batch call unpacker + BatchCallUnpacker, + // instance of the `pallet-bridge-parachains`, tracked by this extension + BridgeParachainsPalletInstance, + // instance of BridgedChain `pallet-bridge-messages`, tracked by this extension + BridgeMessagesPalletInstance, + // instance of `pallet-bridge-relayers`, tracked by this extension + BridgeRelayersPalletInstance, + // message delivery transaction priority boost for every additional message + PriorityBoostPerMessage, +>( + PhantomData<( + IdProvider, + Runtime, + BatchCallUnpacker, + BridgeParachainsPalletInstance, + BridgeMessagesPalletInstance, + BridgeRelayersPalletInstance, + PriorityBoostPerMessage, + )>, +); + +impl ExtensionConfig + for WithParachainExtensionConfig +where + ID: StaticStrProvider, + R: BridgeRelayersConfig + + BridgeMessagesConfig + + BridgeParachainsConfig + + BridgeGrandpaConfig, + BCU: BatchCallUnpacker, + PI: 'static, + MI: 'static, + RI: 'static, + P: Get, + R::RuntimeCall: Dispatchable + + BridgeGrandpaCallSubtype + + BridgeParachainsCallSubtype + + BridgeMessagesCallSubType, + >::BridgedChain: Parachain, +{ + type IdProvider = ID; + type Runtime = R; + type BridgeMessagesPalletInstance = MI; + type BridgeRelayersPalletInstance = RI; + type PriorityBoostPerMessage = P; + type RemoteGrandpaChainBlockNumber = + pallet_bridge_grandpa::BridgedBlockNumber; + type LaneId = LaneIdOf; + + fn parse_and_check_for_obsolete_call( + call: &R::RuntimeCall, + ) -> Result< + Option>, + TransactionValidityError, + > { + let calls = BCU::unpack(call, 3); + let total_calls = calls.len(); + let mut calls = calls.into_iter().map(Self::check_obsolete_parsed_call).rev(); + + let msgs_call = calls.next().transpose()?.and_then(|c| c.call_info()); + let para_finality_call = calls.next().transpose()?.and_then(|c| { + let r = c.submit_parachain_heads_info_for( + >::BridgedChain::PARACHAIN_ID, + ); + r + }); + let relay_finality_call = + calls.next().transpose()?.and_then(|c| c.submit_finality_proof_info()); + Ok(match (total_calls, relay_finality_call, para_finality_call, msgs_call) { + (3, Some(relay_finality_call), Some(para_finality_call), Some(msgs_call)) => + Some(ExtensionCallInfo::AllFinalityAndMsgs( + relay_finality_call, + para_finality_call, + msgs_call, + )), + (2, None, Some(para_finality_call), Some(msgs_call)) => + Some(ExtensionCallInfo::ParachainFinalityAndMsgs(para_finality_call, msgs_call)), + (1, None, None, Some(msgs_call)) => Some(ExtensionCallInfo::Msgs(msgs_call)), + _ => None, + }) + } + + fn check_obsolete_parsed_call( + call: &R::RuntimeCall, + ) -> Result<&R::RuntimeCall, TransactionValidityError> { + call.check_obsolete_submit_finality_proof()?; + call.check_obsolete_submit_parachain_heads()?; + call.check_obsolete_call()?; + Ok(call) + } + + fn check_call_result( + call_info: &ExtensionCallInfo, + call_data: &mut ExtensionCallData, + relayer: &R::AccountId, + ) -> bool { + verify_submit_finality_proof_succeeded::( + call_info, call_data, relayer, + ) && verify_submit_parachain_head_succeeded::(call_info, call_data, relayer) && + verify_messages_call_succeeded::(call_info, call_data, relayer) + } +} + +/// If the batch call contains the parachain state update call, verify that it +/// has been successful. +/// +/// Only returns false when parachain state update call has failed. +pub(crate) fn verify_submit_parachain_head_succeeded( + call_info: &ExtensionCallInfo, + _call_data: &mut ExtensionCallData, + relayer: &::AccountId, +) -> bool +where + C: ExtensionConfig, + PI: 'static, + C::Runtime: BridgeParachainsConfig, +{ + let Some(para_proof_info) = call_info.submit_parachain_heads_info() else { return true }; + + if !SubmitParachainHeadsHelper::::was_successful(para_proof_info) { + // we only refund relayer if all calls have updated chain state + log::trace!( + target: LOG_TARGET, + "{}.{:?}: relayer {:?} has submitted invalid parachain finality proof", + C::IdProvider::STR, + call_info.messages_call_info().lane_id(), + relayer, + ); + return false + } + + true +} diff --git a/bridges/bin/runtime-common/src/extensions/priority_calculator.rs b/bridges/modules/relayers/src/extension/priority.rs similarity index 96% rename from bridges/bin/runtime-common/src/extensions/priority_calculator.rs rename to bridges/modules/relayers/src/extension/priority.rs index 9f559dc13b64d3912f0d1679c21fa682034bdb8e..da188eaf5bdd4a270368c8dad867e77e591ed12c 100644 --- a/bridges/bin/runtime-common/src/extensions/priority_calculator.rs +++ b/bridges/modules/relayers/src/extension/priority.rs @@ -50,7 +50,6 @@ mod integrity_tests {} #[cfg(feature = "integrity-test")] mod integrity_tests { use super::{compute_priority_boost, ItemCount}; - use crate::extensions::refund_relayer_extension::RefundableParachainId; use bp_messages::MessageNonce; use bp_runtime::PreComputedSize; @@ -239,12 +238,18 @@ mod integrity_tests { /// almost the same priority if we'll add `tip_boost_per_header` tip to the `TX1`. We want /// to be sure that if we add plain `PriorityBoostPerHeader` priority to `TX1`, the priority /// will be close to `TX2` as well. - pub fn ensure_priority_boost_is_sane( + pub fn ensure_priority_boost_is_sane< + Runtime, + ParachainsInstance, + Para, + PriorityBoostPerHeader, + >( tip_boost_per_header: BalanceOf, ) where Runtime: pallet_transaction_payment::Config - + pallet_bridge_parachains::Config, - RefundableParachain: RefundableParachainId, + + pallet_bridge_parachains::Config, + ParachainsInstance: 'static, + Para: Parachain, PriorityBoostPerHeader: Get, Runtime::RuntimeCall: Dispatchable, BalanceOf: Send + Sync + FixedPointOperand, @@ -263,7 +268,8 @@ mod integrity_tests { |_n_headers, tip| { estimate_parachain_header_submit_transaction_priority::< Runtime, - RefundableParachain, + ParachainsInstance, + Para, >(tip) }, ); @@ -271,13 +277,18 @@ mod integrity_tests { /// Estimate parachain header delivery transaction priority. #[cfg(feature = "integrity-test")] - fn estimate_parachain_header_submit_transaction_priority( + fn estimate_parachain_header_submit_transaction_priority< + Runtime, + ParachainsInstance, + Para, + >( tip: BalanceOf, ) -> TransactionPriority where Runtime: pallet_transaction_payment::Config - + pallet_bridge_parachains::Config, - RefundableParachain: RefundableParachainId, + + pallet_bridge_parachains::Config, + ParachainsInstance: 'static, + Para: Parachain, Runtime::RuntimeCall: Dispatchable, BalanceOf: Send + Sync + FixedPointOperand, { @@ -287,14 +298,14 @@ mod integrity_tests { let base_tx_size = 512; // let's say we are relaying largest parachain headers and proof takes some more bytes let tx_call_size = >::WeightInfo::expected_extra_storage_proof_size() - .saturating_add(RefundableParachain::BridgedChain::MAX_HEADER_SIZE); + .saturating_add(Para::MAX_HEADER_SIZE); // finally we are able to estimate transaction size and weight let transaction_size = base_tx_size.saturating_add(tx_call_size); let transaction_weight = >::WeightInfo::submit_parachain_heads_weight( Runtime::DbWeight::get(), &PreComputedSize(transaction_size as _), diff --git a/bridges/modules/relayers/src/lib.rs b/bridges/modules/relayers/src/lib.rs index 2c86ec01f5b915bc3b3b1e65c15adae7c45e1819..f06c2e16ac248513b449b98038049995a1d61302 100644 --- a/bridges/modules/relayers/src/lib.rs +++ b/bridges/modules/relayers/src/lib.rs @@ -36,13 +36,14 @@ pub use stake_adapter::StakeAndSlashNamed; pub use weights::WeightInfo; pub use weights_ext::WeightInfoExt; -pub mod benchmarking; - mod mock; mod payment_adapter; mod stake_adapter; mod weights_ext; +pub mod benchmarking; +pub mod extension; +pub mod migration; pub mod weights; /// The target that will be used when publishing logs related to this pallet. @@ -51,46 +52,58 @@ pub const LOG_TARGET: &str = "runtime::bridge-relayers"; #[frame_support::pallet] pub mod pallet { use super::*; + use bp_messages::LaneIdType; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; /// `RelayerRewardsKeyProvider` for given configuration. - type RelayerRewardsKeyProviderOf = - RelayerRewardsKeyProvider<::AccountId, ::Reward>; + type RelayerRewardsKeyProviderOf = RelayerRewardsKeyProvider< + ::AccountId, + >::Reward, + >::LaneId, + >; #[pallet::config] - pub trait Config: frame_system::Config { + pub trait Config: frame_system::Config { /// The overarching event type. - type RuntimeEvent: From> + IsType<::RuntimeEvent>; + type RuntimeEvent: From> + + IsType<::RuntimeEvent>; /// Type of relayer reward. type Reward: AtLeast32BitUnsigned + Copy + Member + Parameter + MaxEncodedLen; /// Pay rewards scheme. - type PaymentProcedure: PaymentProcedure; + type PaymentProcedure: PaymentProcedure< + Self::AccountId, + Self::Reward, + LaneId = Self::LaneId, + >; /// Stake and slash scheme. type StakeAndSlash: StakeAndSlash, Self::Reward>; /// Pallet call weights. type WeightInfo: WeightInfoExt; + /// Lane identifier type. + type LaneId: LaneIdType + Send + Sync; } #[pallet::pallet] - pub struct Pallet(PhantomData); + #[pallet::storage_version(migration::STORAGE_VERSION)] + pub struct Pallet(PhantomData<(T, I)>); #[pallet::call] - impl Pallet { + impl, I: 'static> Pallet { /// Claim accumulated rewards. #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::claim_rewards())] pub fn claim_rewards( origin: OriginFor, - rewards_account_params: RewardsAccountParams, + rewards_account_params: RewardsAccountParams, ) -> DispatchResult { let relayer = ensure_signed(origin)?; - RelayerRewards::::try_mutate_exists( + RelayerRewards::::try_mutate_exists( &relayer, rewards_account_params, |maybe_reward| -> DispatchResult { - let reward = maybe_reward.take().ok_or(Error::::NoRewardForRelayer)?; + let reward = maybe_reward.take().ok_or(Error::::NoRewardForRelayer)?; T::PaymentProcedure::pay_reward(&relayer, rewards_account_params, reward) .map_err(|e| { log::trace!( @@ -100,10 +113,10 @@ pub mod pallet { relayer, e, ); - Error::::FailedToPayReward + Error::::FailedToPayReward })?; - Self::deposit_event(Event::::RewardPaid { + Self::deposit_event(Event::::RewardPaid { relayer: relayer.clone(), rewards_account_params, reward, @@ -125,53 +138,57 @@ pub mod pallet { // than the `RequiredRegistrationLease` let lease = valid_till.saturating_sub(frame_system::Pallet::::block_number()); ensure!( - lease > Pallet::::required_registration_lease(), - Error::::InvalidRegistrationLease + lease > Self::required_registration_lease(), + Error::::InvalidRegistrationLease ); - RegisteredRelayers::::try_mutate(&relayer, |maybe_registration| -> DispatchResult { - let mut registration = maybe_registration - .unwrap_or_else(|| Registration { valid_till, stake: Zero::zero() }); + RegisteredRelayers::::try_mutate( + &relayer, + |maybe_registration| -> DispatchResult { + let mut registration = maybe_registration + .unwrap_or_else(|| Registration { valid_till, stake: Zero::zero() }); + + // new `valid_till` must be larger (or equal) than the old one + ensure!( + valid_till >= registration.valid_till, + Error::::CannotReduceRegistrationLease, + ); + registration.valid_till = valid_till; + + // regarding stake, there are three options: + // - if relayer stake is larger than required stake, we may do unreserve + // - if relayer stake equals to required stake, we do nothing + // - if relayer stake is smaller than required stake, we do additional reserve + let required_stake = Self::required_stake(); + if let Some(to_unreserve) = registration.stake.checked_sub(&required_stake) { + Self::do_unreserve(&relayer, to_unreserve)?; + } else if let Some(to_reserve) = required_stake.checked_sub(®istration.stake) + { + T::StakeAndSlash::reserve(&relayer, to_reserve).map_err(|e| { + log::trace!( + target: LOG_TARGET, + "Failed to reserve {:?} on relayer {:?} account: {:?}", + to_reserve, + relayer, + e, + ); - // new `valid_till` must be larger (or equal) than the old one - ensure!( - valid_till >= registration.valid_till, - Error::::CannotReduceRegistrationLease, - ); - registration.valid_till = valid_till; - - // regarding stake, there are three options: - // - if relayer stake is larger than required stake, we may do unreserve - // - if relayer stake equals to required stake, we do nothing - // - if relayer stake is smaller than required stake, we do additional reserve - let required_stake = Pallet::::required_stake(); - if let Some(to_unreserve) = registration.stake.checked_sub(&required_stake) { - Self::do_unreserve(&relayer, to_unreserve)?; - } else if let Some(to_reserve) = required_stake.checked_sub(®istration.stake) { - T::StakeAndSlash::reserve(&relayer, to_reserve).map_err(|e| { - log::trace!( - target: LOG_TARGET, - "Failed to reserve {:?} on relayer {:?} account: {:?}", - to_reserve, - relayer, - e, - ); - - Error::::FailedToReserve - })?; - } - registration.stake = required_stake; - - log::trace!(target: LOG_TARGET, "Successfully registered relayer: {:?}", relayer); - Self::deposit_event(Event::::RegistrationUpdated { - relayer: relayer.clone(), - registration, - }); - - *maybe_registration = Some(registration); - - Ok(()) - }) + Error::::FailedToReserve + })?; + } + registration.stake = required_stake; + + log::trace!(target: LOG_TARGET, "Successfully registered relayer: {:?}", relayer); + Self::deposit_event(Event::::RegistrationUpdated { + relayer: relayer.clone(), + registration, + }); + + *maybe_registration = Some(registration); + + Ok(()) + }, + ) } /// `Deregister` relayer. @@ -183,34 +200,37 @@ pub mod pallet { pub fn deregister(origin: OriginFor) -> DispatchResult { let relayer = ensure_signed(origin)?; - RegisteredRelayers::::try_mutate(&relayer, |maybe_registration| -> DispatchResult { - let registration = match maybe_registration.take() { - Some(registration) => registration, - None => fail!(Error::::NotRegistered), - }; - - // we can't deregister until `valid_till + 1` - ensure!( - registration.valid_till < frame_system::Pallet::::block_number(), - Error::::RegistrationIsStillActive, - ); + RegisteredRelayers::::try_mutate( + &relayer, + |maybe_registration| -> DispatchResult { + let registration = match maybe_registration.take() { + Some(registration) => registration, + None => fail!(Error::::NotRegistered), + }; + + // we can't deregister until `valid_till + 1` + ensure!( + registration.valid_till < frame_system::Pallet::::block_number(), + Error::::RegistrationIsStillActive, + ); - // if stake is non-zero, we should do unreserve - if !registration.stake.is_zero() { - Self::do_unreserve(&relayer, registration.stake)?; - } + // if stake is non-zero, we should do unreserve + if !registration.stake.is_zero() { + Self::do_unreserve(&relayer, registration.stake)?; + } - log::trace!(target: LOG_TARGET, "Successfully deregistered relayer: {:?}", relayer); - Self::deposit_event(Event::::Deregistered { relayer: relayer.clone() }); + log::trace!(target: LOG_TARGET, "Successfully deregistered relayer: {:?}", relayer); + Self::deposit_event(Event::::Deregistered { relayer: relayer.clone() }); - *maybe_registration = None; + *maybe_registration = None; - Ok(()) - }) + Ok(()) + }, + ) } } - impl Pallet { + impl, I: 'static> Pallet { /// Returns true if given relayer registration is active at current block. /// /// This call respects both `RequiredStake` and `RequiredRegistrationLease`, meaning that @@ -243,9 +263,9 @@ pub mod pallet { /// It may fail inside, but error is swallowed and we only log it. pub fn slash_and_deregister( relayer: &T::AccountId, - slash_destination: ExplicitOrAccountParams, + slash_destination: ExplicitOrAccountParams, ) { - let registration = match RegisteredRelayers::::take(relayer) { + let registration = match RegisteredRelayers::::take(relayer) { Some(registration) => registration, None => { log::trace!( @@ -304,7 +324,7 @@ pub mod pallet { /// Register reward for given relayer. pub fn register_relayer_reward( - rewards_account_params: RewardsAccountParams, + rewards_account_params: RewardsAccountParams, relayer: &T::AccountId, reward: T::Reward, ) { @@ -312,7 +332,7 @@ pub mod pallet { return } - RelayerRewards::::mutate( + RelayerRewards::::mutate( relayer, rewards_account_params, |old_reward: &mut Option| { @@ -327,7 +347,7 @@ pub mod pallet { new_reward, ); - Self::deposit_event(Event::::RewardRegistered { + Self::deposit_event(Event::::RewardRegistered { relayer: relayer.clone(), rewards_account_params, reward, @@ -366,7 +386,7 @@ pub mod pallet { relayer, ); - fail!(Error::::FailedToUnreserve) + fail!(Error::::FailedToUnreserve) } Ok(()) @@ -375,13 +395,13 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event { + pub enum Event, I: 'static = ()> { /// Relayer reward has been registered and may be claimed later. RewardRegistered { /// Relayer account that can claim reward. relayer: T::AccountId, /// Relayer can claim reward from this account. - rewards_account_params: RewardsAccountParams, + rewards_account_params: RewardsAccountParams, /// Reward amount. reward: T::Reward, }, @@ -390,7 +410,7 @@ pub mod pallet { /// Relayer account that has been rewarded. relayer: T::AccountId, /// Relayer has received reward from this account. - rewards_account_params: RewardsAccountParams, + rewards_account_params: RewardsAccountParams, /// Reward amount. reward: T::Reward, }, @@ -416,7 +436,7 @@ pub mod pallet { } #[pallet::error] - pub enum Error { + pub enum Error { /// No reward can be claimed by given relayer. NoRewardForRelayer, /// Reward payment procedure has failed. @@ -439,13 +459,13 @@ pub mod pallet { /// Map of the relayer => accumulated reward. #[pallet::storage] #[pallet::getter(fn relayer_reward)] - pub type RelayerRewards = StorageDoubleMap< + pub type RelayerRewards, I: 'static = ()> = StorageDoubleMap< _, - as StorageDoubleMapKeyProvider>::Hasher1, - as StorageDoubleMapKeyProvider>::Key1, - as StorageDoubleMapKeyProvider>::Hasher2, - as StorageDoubleMapKeyProvider>::Key2, - as StorageDoubleMapKeyProvider>::Value, + as StorageDoubleMapKeyProvider>::Hasher1, + as StorageDoubleMapKeyProvider>::Key1, + as StorageDoubleMapKeyProvider>::Hasher2, + as StorageDoubleMapKeyProvider>::Key2, + as StorageDoubleMapKeyProvider>::Value, OptionQuery, >; @@ -457,7 +477,7 @@ pub mod pallet { /// relayer is present. #[pallet::storage] #[pallet::getter(fn registered_relayer)] - pub type RegisteredRelayers = StorageMap< + pub type RegisteredRelayers, I: 'static = ()> = StorageMap< _, Blake2_128Concat, T::AccountId, @@ -469,10 +489,10 @@ pub mod pallet { #[cfg(test)] mod tests { use super::*; + use bp_messages::LaneIdType; use mock::{RuntimeEvent as TestEvent, *}; use crate::Event::{RewardPaid, RewardRegistered}; - use bp_messages::LaneId; use bp_relayers::RewardsAccountOwner; use frame_support::{ assert_noop, assert_ok, @@ -492,7 +512,7 @@ mod tests { get_ready_for_events(); Pallet::::register_relayer_reward( - TEST_REWARDS_ACCOUNT_PARAMS, + test_reward_account_param(), ®ULAR_RELAYER, 100, ); @@ -502,9 +522,9 @@ mod tests { System::::events().last(), Some(&EventRecord { phase: Phase::Initialization, - event: TestEvent::Relayers(RewardRegistered { + event: TestEvent::BridgeRelayers(RewardRegistered { relayer: REGULAR_RELAYER, - rewards_account_params: TEST_REWARDS_ACCOUNT_PARAMS, + rewards_account_params: test_reward_account_param(), reward: 100 }), topics: vec![], @@ -519,7 +539,7 @@ mod tests { assert_noop!( Pallet::::claim_rewards( RuntimeOrigin::root(), - TEST_REWARDS_ACCOUNT_PARAMS + test_reward_account_param() ), DispatchError::BadOrigin, ); @@ -532,7 +552,7 @@ mod tests { assert_noop!( Pallet::::claim_rewards( RuntimeOrigin::signed(REGULAR_RELAYER), - TEST_REWARDS_ACCOUNT_PARAMS + test_reward_account_param() ), Error::::NoRewardForRelayer, ); @@ -544,13 +564,13 @@ mod tests { run_test(|| { RelayerRewards::::insert( FAILING_RELAYER, - TEST_REWARDS_ACCOUNT_PARAMS, + test_reward_account_param(), 100, ); assert_noop!( Pallet::::claim_rewards( RuntimeOrigin::signed(FAILING_RELAYER), - TEST_REWARDS_ACCOUNT_PARAMS + test_reward_account_param() ), Error::::FailedToPayReward, ); @@ -564,15 +584,15 @@ mod tests { RelayerRewards::::insert( REGULAR_RELAYER, - TEST_REWARDS_ACCOUNT_PARAMS, + test_reward_account_param(), 100, ); assert_ok!(Pallet::::claim_rewards( RuntimeOrigin::signed(REGULAR_RELAYER), - TEST_REWARDS_ACCOUNT_PARAMS + test_reward_account_param() )); assert_eq!( - RelayerRewards::::get(REGULAR_RELAYER, TEST_REWARDS_ACCOUNT_PARAMS), + RelayerRewards::::get(REGULAR_RELAYER, test_reward_account_param()), None ); @@ -581,9 +601,9 @@ mod tests { System::::events().last(), Some(&EventRecord { phase: Phase::Initialization, - event: TestEvent::Relayers(RewardPaid { + event: TestEvent::BridgeRelayers(RewardPaid { relayer: REGULAR_RELAYER, - rewards_account_params: TEST_REWARDS_ACCOUNT_PARAMS, + rewards_account_params: test_reward_account_param(), reward: 100 }), topics: vec![], @@ -595,16 +615,17 @@ mod tests { #[test] fn pay_reward_from_account_actually_pays_reward() { type Balances = pallet_balances::Pallet; - type PayLaneRewardFromAccount = bp_relayers::PayRewardFromAccount; + type PayLaneRewardFromAccount = + bp_relayers::PayRewardFromAccount; run_test(|| { let in_lane_0 = RewardsAccountParams::new( - LaneId([0, 0, 0, 0]), + TestLaneIdType::try_new(1, 2).unwrap(), *b"test", RewardsAccountOwner::ThisChain, ); let out_lane_1 = RewardsAccountParams::new( - LaneId([0, 0, 0, 1]), + TestLaneIdType::try_new(1, 3).unwrap(), *b"test", RewardsAccountOwner::BridgedChain, ); @@ -676,7 +697,7 @@ mod tests { System::::events().last(), Some(&EventRecord { phase: Phase::Initialization, - event: TestEvent::Relayers(Event::RegistrationUpdated { + event: TestEvent::BridgeRelayers(Event::RegistrationUpdated { relayer: REGISTER_RELAYER, registration: Registration { valid_till: 150, stake: Stake::get() }, }), @@ -744,7 +765,7 @@ mod tests { System::::events().last(), Some(&EventRecord { phase: Phase::Initialization, - event: TestEvent::Relayers(Event::RegistrationUpdated { + event: TestEvent::BridgeRelayers(Event::RegistrationUpdated { relayer: REGISTER_RELAYER, registration: Registration { valid_till: 150, stake: Stake::get() } }), @@ -808,7 +829,7 @@ mod tests { System::::events().last(), Some(&EventRecord { phase: Phase::Initialization, - event: TestEvent::Relayers(Event::RegistrationUpdated { + event: TestEvent::BridgeRelayers(Event::RegistrationUpdated { relayer: REGISTER_RELAYER, registration: Registration { valid_till: 150, stake: Stake::get() } }), @@ -870,7 +891,9 @@ mod tests { System::::events().last(), Some(&EventRecord { phase: Phase::Initialization, - event: TestEvent::Relayers(Event::Deregistered { relayer: REGISTER_RELAYER }), + event: TestEvent::BridgeRelayers(Event::Deregistered { + relayer: REGISTER_RELAYER + }), topics: vec![], }), ); diff --git a/bridges/modules/relayers/src/migration.rs b/bridges/modules/relayers/src/migration.rs new file mode 100644 index 0000000000000000000000000000000000000000..8bf473b300c2a15eb983fb3e4c6f4c4c5dad0105 --- /dev/null +++ b/bridges/modules/relayers/src/migration.rs @@ -0,0 +1,243 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! A module that is responsible for migration of storage. + +use frame_support::{ + traits::{Get, StorageVersion}, + weights::Weight, +}; + +/// The in-code storage version. +pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); + +/// This module contains data structures that are valid for the initial state of `0`. +/// (used with v1 migration). +pub mod v0 { + use crate::{Config, Pallet}; + use bp_relayers::RewardsAccountOwner; + use bp_runtime::{ChainId, StorageDoubleMapKeyProvider}; + use codec::{Codec, Decode, Encode, EncodeLike, MaxEncodedLen}; + use frame_support::{pallet_prelude::OptionQuery, Blake2_128Concat, Identity}; + use scale_info::TypeInfo; + use sp_runtime::traits::AccountIdConversion; + use sp_std::marker::PhantomData; + + /// Structure used to identify the account that pays a reward to the relayer. + #[derive(Copy, Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen)] + pub struct RewardsAccountParams { + /// lane_id + pub lane_id: LaneId, + /// bridged_chain_id + pub bridged_chain_id: ChainId, + /// owner + pub owner: RewardsAccountOwner, + } + + impl RewardsAccountParams { + /// Create a new instance of `RewardsAccountParams`. + pub const fn new( + lane_id: LaneId, + bridged_chain_id: ChainId, + owner: RewardsAccountOwner, + ) -> Self { + Self { lane_id, bridged_chain_id, owner } + } + } + + impl sp_runtime::TypeId for RewardsAccountParams { + const TYPE_ID: [u8; 4] = *b"brap"; + } + + pub(crate) struct RelayerRewardsKeyProvider( + PhantomData<(AccountId, Reward, LaneId)>, + ); + + impl StorageDoubleMapKeyProvider + for RelayerRewardsKeyProvider + where + AccountId: 'static + Codec + EncodeLike + Send + Sync, + Reward: 'static + Codec + EncodeLike + Send + Sync, + LaneId: Codec + EncodeLike + Send + Sync, + { + const MAP_NAME: &'static str = "RelayerRewards"; + + type Hasher1 = Blake2_128Concat; + type Key1 = AccountId; + type Hasher2 = Identity; + type Key2 = RewardsAccountParams; + type Value = Reward; + } + + pub(crate) type RelayerRewardsKeyProviderOf = RelayerRewardsKeyProvider< + ::AccountId, + >::Reward, + >::LaneId, + >; + + #[frame_support::storage_alias] + pub(crate) type RelayerRewards, I: 'static> = StorageDoubleMap< + Pallet, + as StorageDoubleMapKeyProvider>::Hasher1, + as StorageDoubleMapKeyProvider>::Key1, + as StorageDoubleMapKeyProvider>::Hasher2, + as StorageDoubleMapKeyProvider>::Key2, + as StorageDoubleMapKeyProvider>::Value, + OptionQuery, + >; + + /// Reward account generator for `v0`. + pub struct PayRewardFromAccount(PhantomData<(Account, LaneId)>); + impl PayRewardFromAccount + where + Account: Decode + Encode, + LaneId: Decode + Encode, + { + /// Return account that pays rewards based on the provided parameters. + pub fn rewards_account(params: RewardsAccountParams) -> Account { + params.into_sub_account_truncating(b"rewards-account") + } + } +} + +/// This migration updates `RelayerRewards` where `RewardsAccountParams` was used as the key with +/// `lane_id` as the first attribute, which affects `into_sub_account_truncating`. We are migrating +/// this key to use the new `RewardsAccountParams` where `lane_id` is the last attribute. +pub mod v1 { + use super::*; + use crate::{Config, Pallet}; + use bp_relayers::RewardsAccountParams; + use frame_support::traits::UncheckedOnRuntimeUpgrade; + use sp_std::marker::PhantomData; + + #[cfg(feature = "try-runtime")] + use crate::RelayerRewards; + + /// Migrates the pallet storage to v1. + pub struct UncheckedMigrationV0ToV1(PhantomData<(T, I)>); + + #[cfg(feature = "try-runtime")] + const LOG_TARGET: &str = "runtime::bridge-relayers-migration"; + + impl, I: 'static> UncheckedOnRuntimeUpgrade for UncheckedMigrationV0ToV1 { + fn on_runtime_upgrade() -> Weight { + let mut weight = T::DbWeight::get().reads(1); + + // list all rewards (we cannot do this as one step because of `drain` limitation) + let mut rewards_to_migrate = + sp_std::vec::Vec::with_capacity(v0::RelayerRewards::::iter().count()); + for (key1, key2, reward) in v0::RelayerRewards::::drain() { + rewards_to_migrate.push((key1, key2, reward)); + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + } + + // re-register rewards with new format of `RewardsAccountParams`. + for (key1, key2, reward) in rewards_to_migrate { + // expand old key + let v0::RewardsAccountParams { owner, lane_id, bridged_chain_id } = key2; + + // re-register reward + Pallet::::register_relayer_reward( + v1::RewardsAccountParams::new(lane_id, bridged_chain_id, owner), + &key1, + reward, + ); + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + } + + weight + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::DispatchError> { + use codec::Encode; + use frame_support::BoundedBTreeMap; + use sp_runtime::traits::ConstU32; + + // collect actual rewards + let mut rewards: BoundedBTreeMap< + (T::AccountId, T::LaneId), + T::Reward, + ConstU32<{ u32::MAX }>, + > = BoundedBTreeMap::new(); + for (key1, key2, reward) in v0::RelayerRewards::::iter() { + log::info!(target: LOG_TARGET, "Reward to migrate: {key1:?}::{key2:?} - {reward:?}"); + rewards = rewards + .try_mutate(|inner| { + inner + .entry((key1.clone(), key2.lane_id)) + .and_modify(|value| *value += reward) + .or_insert(reward); + }) + .unwrap(); + } + log::info!(target: LOG_TARGET, "Found total rewards to migrate: {rewards:?}"); + + Ok(rewards.encode()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: sp_std::vec::Vec) -> Result<(), sp_runtime::DispatchError> { + use codec::Decode; + use frame_support::BoundedBTreeMap; + use sp_runtime::traits::ConstU32; + + let rewards_before: BoundedBTreeMap< + (T::AccountId, T::LaneId), + T::Reward, + ConstU32<{ u32::MAX }>, + > = Decode::decode(&mut &state[..]).unwrap(); + + // collect migrated rewards + let mut rewards_after: BoundedBTreeMap< + (T::AccountId, T::LaneId), + T::Reward, + ConstU32<{ u32::MAX }>, + > = BoundedBTreeMap::new(); + for (key1, key2, reward) in v1::RelayerRewards::::iter() { + log::info!(target: LOG_TARGET, "Migrated rewards: {key1:?}::{key2:?} - {reward:?}"); + rewards_after = rewards_after + .try_mutate(|inner| { + inner + .entry((key1.clone(), *key2.lane_id())) + .and_modify(|value| *value += reward) + .or_insert(reward); + }) + .unwrap(); + } + log::info!(target: LOG_TARGET, "Found total migrated rewards: {rewards_after:?}"); + + frame_support::ensure!( + rewards_before == rewards_after, + "The rewards were not migrated correctly!." + ); + + log::info!(target: LOG_TARGET, "migrated all."); + Ok(()) + } + } + + /// [`UncheckedMigrationV0ToV1`] wrapped in a + /// [`VersionedMigration`](frame_support::migrations::VersionedMigration), ensuring the + /// migration is only performed when on-chain version is 0. + pub type MigrationToV1 = frame_support::migrations::VersionedMigration< + 0, + 1, + UncheckedMigrationV0ToV1, + Pallet, + ::DbWeight, + >; +} diff --git a/bridges/modules/relayers/src/mock.rs b/bridges/modules/relayers/src/mock.rs index 3124787896c3e1ee20014fc21b87ccbc19e6a2c2..d186e968e648264a81e465ff01c4bf1d6082b048 100644 --- a/bridges/modules/relayers/src/mock.rs +++ b/bridges/modules/relayers/src/mock.rs @@ -18,51 +18,191 @@ use crate as pallet_bridge_relayers; -use bp_messages::LaneId; +use bp_header_chain::ChainWithGrandpa; +use bp_messages::{ + target_chain::{DispatchMessage, MessageDispatch}, + ChainWithMessages, HashedLaneId, LaneIdType, MessageNonce, +}; +use bp_parachains::SingleParaStoredHeaderDataBuilder; use bp_relayers::{ PayRewardFromAccount, PaymentProcedure, RewardsAccountOwner, RewardsAccountParams, }; +use bp_runtime::{messages::MessageDispatchResult, Chain, ChainId, Parachain}; +use codec::Encode; use frame_support::{ - derive_impl, parameter_types, traits::fungible::Mutate, weights::RuntimeDbWeight, + derive_impl, parameter_types, + traits::fungible::Mutate, + weights::{ConstantMultiplier, IdentityFee, RuntimeDbWeight, Weight}, +}; +use pallet_transaction_payment::Multiplier; +use sp_core::{ConstU64, ConstU8, H256}; +use sp_runtime::{ + traits::{BlakeTwo256, ConstU32}, + BuildStorage, FixedPointNumber, Perquintill, StateVersion, }; -use sp_runtime::BuildStorage; -pub type AccountId = u64; -pub type Balance = u64; -pub type BlockNumber = u64; +/// Account identifier at `ThisChain`. +pub type ThisChainAccountId = u64; +/// Balance at `ThisChain`. +pub type ThisChainBalance = u64; +/// Block number at `ThisChain`. +pub type ThisChainBlockNumber = u32; +/// Hash at `ThisChain`. +pub type ThisChainHash = H256; +/// Hasher at `ThisChain`. +pub type ThisChainHasher = BlakeTwo256; +/// Header of `ThisChain`. +pub type ThisChainHeader = sp_runtime::generic::Header; +/// Block of `ThisChain`. +pub type ThisChainBlock = frame_system::mocking::MockBlockU32; + +/// Account identifier at the `BridgedChain`. +pub type BridgedChainAccountId = u128; +/// Balance at the `BridgedChain`. +pub type BridgedChainBalance = u128; +/// Block number at the `BridgedChain`. +pub type BridgedChainBlockNumber = u32; +/// Hash at the `BridgedChain`. +pub type BridgedChainHash = H256; +/// Hasher at the `BridgedChain`. +pub type BridgedChainHasher = BlakeTwo256; +/// Header of the `BridgedChain`. +pub type BridgedChainHeader = + sp_runtime::generic::Header; + +/// Bridged chain id used in tests. +pub const TEST_BRIDGED_CHAIN_ID: ChainId = *b"brdg"; +/// Maximal extrinsic size at the `BridgedChain`. +pub const BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE: u32 = 1024; + +/// Lane identifier type used for tests. +pub type TestLaneIdType = HashedLaneId; +/// Lane that we're using in tests. +pub fn test_lane_id() -> TestLaneIdType { + TestLaneIdType::try_new(1, 2).unwrap() +} + +/// Underlying chain of `ThisChain`. +pub struct ThisUnderlyingChain; + +impl Chain for ThisUnderlyingChain { + const ID: ChainId = *b"tuch"; + + type BlockNumber = ThisChainBlockNumber; + type Hash = ThisChainHash; + type Hasher = ThisChainHasher; + type Header = ThisChainHeader; + type AccountId = ThisChainAccountId; + type Balance = ThisChainBalance; + type Nonce = u32; + type Signature = sp_runtime::MultiSignature; + + const STATE_VERSION: StateVersion = StateVersion::V1; + + fn max_extrinsic_size() -> u32 { + BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE + } + + fn max_extrinsic_weight() -> Weight { + Weight::zero() + } +} + +impl ChainWithMessages for ThisUnderlyingChain { + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = ""; + + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 16; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 1000; +} + +/// Underlying chain of `BridgedChain`. +pub struct BridgedUnderlyingParachain; + +impl Chain for BridgedUnderlyingParachain { + const ID: ChainId = TEST_BRIDGED_CHAIN_ID; + + type BlockNumber = BridgedChainBlockNumber; + type Hash = BridgedChainHash; + type Hasher = BridgedChainHasher; + type Header = BridgedChainHeader; + type AccountId = BridgedChainAccountId; + type Balance = BridgedChainBalance; + type Nonce = u32; + type Signature = sp_runtime::MultiSignature; + + const STATE_VERSION: StateVersion = StateVersion::V1; + + fn max_extrinsic_size() -> u32 { + BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE + } + fn max_extrinsic_weight() -> Weight { + Weight::zero() + } +} + +impl ChainWithGrandpa for BridgedUnderlyingParachain { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = ""; + const MAX_AUTHORITIES_COUNT: u32 = 16; + const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = 8; + const MAX_MANDATORY_HEADER_SIZE: u32 = 256; + const AVERAGE_HEADER_SIZE: u32 = 64; +} + +impl ChainWithMessages for BridgedUnderlyingParachain { + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = ""; + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 16; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 1000; +} + +impl Parachain for BridgedUnderlyingParachain { + const PARACHAIN_ID: u32 = 42; + const MAX_HEADER_SIZE: u32 = 1_024; +} pub type TestStakeAndSlash = pallet_bridge_relayers::StakeAndSlashNamed< - AccountId, - BlockNumber, + ThisChainAccountId, + ThisChainBlockNumber, Balances, ReserveId, Stake, Lease, >; -type Block = frame_system::mocking::MockBlock; - frame_support::construct_runtime! { pub enum TestRuntime { System: frame_system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Event}, - Relayers: pallet_bridge_relayers::{Pallet, Call, Event}, + Utility: pallet_utility, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, + BridgeRelayers: pallet_bridge_relayers::{Pallet, Call, Storage, Event}, + BridgeGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage, Event}, + BridgeParachains: pallet_bridge_parachains::{Pallet, Call, Storage, Event}, + BridgeMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event, Config}, } } parameter_types! { + pub const BridgedParasPalletName: &'static str = "Paras"; pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { read: 1, write: 2 }; - pub const ExistentialDeposit: Balance = 1; + pub const ExistentialDeposit: ThisChainBalance = 1; pub const ReserveId: [u8; 8] = *b"brdgrlrs"; - pub const Stake: Balance = 1_000; - pub const Lease: BlockNumber = 8; + pub const Stake: ThisChainBalance = 1_000; + pub const Lease: ThisChainBlockNumber = 8; + pub const TargetBlockFullness: Perquintill = Perquintill::from_percent(25); + pub const TransactionBaseFee: ThisChainBalance = 0; + pub const TransactionByteFee: ThisChainBalance = 1; + pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(3, 100_000); + pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1, 1_000_000u128); + pub MaximumMultiplier: Multiplier = sp_runtime::traits::Bounded::max_value(); } #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for TestRuntime { - type Block = Block; - type AccountData = pallet_balances::AccountData; + type Block = ThisChainBlock; + // TODO: remove when https://github.com/paritytech/polkadot-sdk/pull/4543 merged + type BlockHashCount = ConstU32<10>; + type AccountData = pallet_balances::AccountData; type DbWeight = DbWeight; } @@ -72,21 +212,91 @@ impl pallet_balances::Config for TestRuntime { type AccountStore = System; } +impl pallet_utility::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type PalletsOrigin = OriginCaller; + type WeightInfo = (); +} + +#[derive_impl(pallet_transaction_payment::config_preludes::TestDefaultConfig)] +impl pallet_transaction_payment::Config for TestRuntime { + type OnChargeTransaction = pallet_transaction_payment::FungibleAdapter; + type OperationalFeeMultiplier = ConstU8<5>; + type WeightToFee = IdentityFee; + type LengthToFee = ConstantMultiplier; + type FeeMultiplierUpdate = pallet_transaction_payment::TargetedFeeAdjustment< + TestRuntime, + TargetBlockFullness, + AdjustmentVariable, + MinimumMultiplier, + MaximumMultiplier, + >; + type RuntimeEvent = RuntimeEvent; +} + +impl pallet_bridge_grandpa::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type BridgedChain = BridgedUnderlyingParachain; + type MaxFreeHeadersPerBlock = ConstU32<4>; + type FreeHeadersInterval = ConstU32<1_024>; + type HeadersToKeep = ConstU32<8>; + type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; +} + +impl pallet_bridge_parachains::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type BridgesGrandpaPalletInstance = (); + type ParasPalletName = BridgedParasPalletName; + type ParaStoredHeaderDataBuilder = + SingleParaStoredHeaderDataBuilder; + type HeadsToKeep = ConstU32<8>; + type MaxParaHeadDataSize = ConstU32<1024>; + type WeightInfo = pallet_bridge_parachains::weights::BridgeWeight; +} + +impl pallet_bridge_messages::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_bridge_messages::weights::BridgeWeight; + + type OutboundPayload = Vec; + type InboundPayload = Vec; + type LaneId = TestLaneIdType; + + type DeliveryPayments = (); + type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< + TestRuntime, + (), + ConstU64<100_000>, + >; + type OnMessagesDelivered = (); + + type MessageDispatch = DummyMessageDispatch; + type ThisChain = ThisUnderlyingChain; + type BridgedChain = BridgedUnderlyingParachain; + type BridgedHeaderChain = BridgeGrandpa; +} + impl pallet_bridge_relayers::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; - type Reward = Balance; + type Reward = ThisChainBalance; type PaymentProcedure = TestPaymentProcedure; type StakeAndSlash = TestStakeAndSlash; type WeightInfo = (); + type LaneId = TestLaneIdType; } #[cfg(feature = "runtime-benchmarks")] impl pallet_bridge_relayers::benchmarking::Config for TestRuntime { - fn prepare_rewards_account(account_params: RewardsAccountParams, reward: Balance) { - let rewards_account = - bp_relayers::PayRewardFromAccount::::rewards_account( - account_params, - ); + fn prepare_rewards_account( + account_params: RewardsAccountParams, + reward: Self::Reward, + ) { + let rewards_account = bp_relayers::PayRewardFromAccount::< + Balances, + ThisChainAccountId, + Self::LaneId, + >::rewards_account(account_params); Self::deposit_account(rewards_account, reward); } @@ -95,35 +305,32 @@ impl pallet_bridge_relayers::benchmarking::Config for TestRuntime { } } -/// Message lane that we're using in tests. -pub const TEST_REWARDS_ACCOUNT_PARAMS: RewardsAccountParams = - RewardsAccountParams::new(LaneId([0, 0, 0, 0]), *b"test", RewardsAccountOwner::ThisChain); - /// Regular relayer that may receive rewards. -pub const REGULAR_RELAYER: AccountId = 1; +pub const REGULAR_RELAYER: ThisChainAccountId = 1; /// Relayer that can't receive rewards. -pub const FAILING_RELAYER: AccountId = 2; +pub const FAILING_RELAYER: ThisChainAccountId = 2; /// Relayer that is able to register. -pub const REGISTER_RELAYER: AccountId = 42; +pub const REGISTER_RELAYER: ThisChainAccountId = 42; /// Payment procedure that rejects payments to the `FAILING_RELAYER`. pub struct TestPaymentProcedure; impl TestPaymentProcedure { - pub fn rewards_account(params: RewardsAccountParams) -> AccountId { - PayRewardFromAccount::<(), AccountId>::rewards_account(params) + pub fn rewards_account(params: RewardsAccountParams) -> ThisChainAccountId { + PayRewardFromAccount::<(), ThisChainAccountId, TestLaneIdType>::rewards_account(params) } } -impl PaymentProcedure for TestPaymentProcedure { +impl PaymentProcedure for TestPaymentProcedure { type Error = (); + type LaneId = TestLaneIdType; fn pay_reward( - relayer: &AccountId, - _lane_id: RewardsAccountParams, - _reward: Balance, + relayer: &ThisChainAccountId, + _lane_id: RewardsAccountParams, + _reward: ThisChainBalance, ) -> Result<(), Self::Error> { match *relayer { FAILING_RELAYER => Err(()), @@ -132,6 +339,47 @@ impl PaymentProcedure for TestPaymentProcedure { } } +/// Dummy message dispatcher. +pub struct DummyMessageDispatch; + +impl DummyMessageDispatch { + pub fn deactivate(lane: TestLaneIdType) { + frame_support::storage::unhashed::put(&(b"inactive", lane).encode()[..], &false); + } +} + +impl MessageDispatch for DummyMessageDispatch { + type DispatchPayload = Vec; + type DispatchLevelResult = (); + type LaneId = TestLaneIdType; + + fn is_active(lane: Self::LaneId) -> bool { + frame_support::storage::unhashed::take::(&(b"inactive", lane).encode()[..]) != + Some(false) + } + + fn dispatch_weight( + _message: &mut DispatchMessage, + ) -> Weight { + Weight::zero() + } + + fn dispatch( + _: DispatchMessage, + ) -> MessageDispatchResult { + MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result: () } + } +} + +/// Reward account params that we are using in tests. +pub fn test_reward_account_param() -> RewardsAccountParams { + RewardsAccountParams::new( + TestLaneIdType::try_new(1, 2).unwrap(), + *b"test", + RewardsAccountOwner::ThisChain, + ) +} + /// Return test externalities to use in tests. pub fn new_test_ext() -> sp_io::TestExternalities { let t = frame_system::GenesisConfig::::default().build_storage().unwrap(); diff --git a/bridges/modules/relayers/src/payment_adapter.rs b/bridges/modules/relayers/src/payment_adapter.rs index f75c409aca4f3ef85fab748e4dc41af12b545562..5383cba5ecbddcf565aaabcb6852d970c6fcdcb8 100644 --- a/bridges/modules/relayers/src/payment_adapter.rs +++ b/bridges/modules/relayers/src/payment_adapter.rs @@ -20,11 +20,12 @@ use crate::{Config, Pallet}; use bp_messages::{ source_chain::{DeliveryConfirmationPayments, RelayersRewards}, - LaneId, MessageNonce, + MessageNonce, }; use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; use bp_runtime::Chain; use frame_support::{sp_runtime::SaturatedConversion, traits::Get}; +use pallet_bridge_messages::LaneIdOf; use sp_arithmetic::traits::{Saturating, Zero}; use sp_std::{collections::vec_deque::VecDeque, marker::PhantomData, ops::RangeInclusive}; @@ -34,17 +35,17 @@ pub struct DeliveryConfirmationPaymentsAdapter( PhantomData<(T, MI, DeliveryReward)>, ); -impl DeliveryConfirmationPayments +impl DeliveryConfirmationPayments> for DeliveryConfirmationPaymentsAdapter where - T: Config + pallet_bridge_messages::Config, + T: Config + pallet_bridge_messages::Config::LaneId>, MI: 'static, DeliveryReward: Get, { type Error = &'static str; fn pay_reward( - lane_id: LaneId, + lane_id: LaneIdOf, messages_relayers: VecDeque>, confirmation_relayer: &T::AccountId, received_range: &RangeInclusive, @@ -72,7 +73,7 @@ where fn register_relayers_rewards( confirmation_relayer: &T::AccountId, relayers_rewards: RelayersRewards, - lane_id: RewardsAccountParams, + lane_id: RewardsAccountParams, delivery_fee: T::Reward, ) { // reward every relayer except `confirmation_relayer` @@ -103,11 +104,11 @@ mod tests { use super::*; use crate::{mock::*, RelayerRewards}; - const RELAYER_1: AccountId = 1; - const RELAYER_2: AccountId = 2; - const RELAYER_3: AccountId = 3; + const RELAYER_1: ThisChainAccountId = 1; + const RELAYER_2: ThisChainAccountId = 2; + const RELAYER_3: ThisChainAccountId = 3; - fn relayers_rewards() -> RelayersRewards { + fn relayers_rewards() -> RelayersRewards { vec![(RELAYER_1, 2), (RELAYER_2, 3)].into_iter().collect() } @@ -117,16 +118,16 @@ mod tests { register_relayers_rewards::( &RELAYER_2, relayers_rewards(), - TEST_REWARDS_ACCOUNT_PARAMS, + test_reward_account_param(), 50, ); assert_eq!( - RelayerRewards::::get(RELAYER_1, TEST_REWARDS_ACCOUNT_PARAMS), + RelayerRewards::::get(RELAYER_1, test_reward_account_param()), Some(100) ); assert_eq!( - RelayerRewards::::get(RELAYER_2, TEST_REWARDS_ACCOUNT_PARAMS), + RelayerRewards::::get(RELAYER_2, test_reward_account_param()), Some(150) ); }); @@ -138,20 +139,20 @@ mod tests { register_relayers_rewards::( &RELAYER_3, relayers_rewards(), - TEST_REWARDS_ACCOUNT_PARAMS, + test_reward_account_param(), 50, ); assert_eq!( - RelayerRewards::::get(RELAYER_1, TEST_REWARDS_ACCOUNT_PARAMS), + RelayerRewards::::get(RELAYER_1, test_reward_account_param()), Some(100) ); assert_eq!( - RelayerRewards::::get(RELAYER_2, TEST_REWARDS_ACCOUNT_PARAMS), + RelayerRewards::::get(RELAYER_2, test_reward_account_param()), Some(150) ); assert_eq!( - RelayerRewards::::get(RELAYER_3, TEST_REWARDS_ACCOUNT_PARAMS), + RelayerRewards::::get(RELAYER_3, test_reward_account_param()), None ); }); diff --git a/bridges/modules/relayers/src/stake_adapter.rs b/bridges/modules/relayers/src/stake_adapter.rs index 7ba90d91dfd94e49bf0ff6ee8fcc06f80e287c41..1792f0be8316acfc9b32508198654c035cc51289 100644 --- a/bridges/modules/relayers/src/stake_adapter.rs +++ b/bridges/modules/relayers/src/stake_adapter.rs @@ -18,7 +18,7 @@ //! mechanism of the relayers pallet. use bp_relayers::{ExplicitOrAccountParams, PayRewardFromAccount, StakeAndSlash}; -use codec::Codec; +use codec::{Codec, Decode, Encode}; use frame_support::traits::{tokens::BalanceStatus, NamedReservableCurrency}; use sp_runtime::{traits::Get, DispatchError, DispatchResult}; use sp_std::{fmt::Debug, marker::PhantomData}; @@ -53,15 +53,15 @@ where Currency::unreserve_named(&ReserveId::get(), relayer, amount) } - fn repatriate_reserved( + fn repatriate_reserved( relayer: &AccountId, - beneficiary: ExplicitOrAccountParams, + beneficiary: ExplicitOrAccountParams, amount: Currency::Balance, ) -> Result { let beneficiary_account = match beneficiary { ExplicitOrAccountParams::Explicit(account) => account, ExplicitOrAccountParams::Params(params) => - PayRewardFromAccount::<(), AccountId>::rewards_account(params), + PayRewardFromAccount::<(), AccountId, LaneId>::rewards_account(params), }; Currency::repatriate_reserved_named( &ReserveId::get(), @@ -80,7 +80,7 @@ mod tests { use frame_support::traits::fungible::Mutate; - fn test_stake() -> Balance { + fn test_stake() -> ThisChainBalance { Stake::get() } @@ -130,7 +130,7 @@ mod tests { #[test] fn repatriate_reserved_works() { run_test(|| { - let beneficiary = TEST_REWARDS_ACCOUNT_PARAMS; + let beneficiary = test_reward_account_param(); let beneficiary_account = TestPaymentProcedure::rewards_account(beneficiary); let mut expected_balance = ExistentialDeposit::get(); @@ -186,7 +186,7 @@ mod tests { #[test] fn repatriate_reserved_doesnt_work_when_beneficiary_account_is_missing() { run_test(|| { - let beneficiary = TEST_REWARDS_ACCOUNT_PARAMS; + let beneficiary = test_reward_account_param(); let beneficiary_account = TestPaymentProcedure::rewards_account(beneficiary); Balances::mint_into(&3, test_stake() * 2).unwrap(); diff --git a/bridges/modules/xcm-bridge-hub-router/Cargo.toml b/bridges/modules/xcm-bridge-hub-router/Cargo.toml index ec7c3b5628327f6cbb3d5b3920dba59521c6c209..55824f6a7fe7bf89a872fbef503a574c02c7f9ae 100644 --- a/bridges/modules/xcm-bridge-hub-router/Cargo.toml +++ b/bridges/modules/xcm-bridge-hub-router/Cargo.toml @@ -16,11 +16,9 @@ log = { workspace = true } scale-info = { features = ["bit-vec", "derive", "serde"], workspace = true } # Bridge dependencies - bp-xcm-bridge-hub-router = { workspace = true } # Substrate Dependencies - frame-benchmarking = { optional = true, workspace = true } frame-support = { workspace = true } frame-system = { workspace = true } @@ -29,7 +27,6 @@ sp-runtime = { workspace = true } sp-std = { workspace = true } # Polkadot Dependencies - xcm = { workspace = true } xcm-builder = { workspace = true } diff --git a/bridges/modules/xcm-bridge-hub-router/src/benchmarking.rs b/bridges/modules/xcm-bridge-hub-router/src/benchmarking.rs index c4f9f534c1a479cd7dc4ba545353b9d92c45d2c8..3c4a10f82e7dff12ae7d8f7b300bc5d66839073c 100644 --- a/bridges/modules/xcm-bridge-hub-router/src/benchmarking.rs +++ b/bridges/modules/xcm-bridge-hub-router/src/benchmarking.rs @@ -18,11 +18,9 @@ #![cfg(feature = "runtime-benchmarks")] -use crate::{Bridge, Call}; - -use bp_xcm_bridge_hub_router::{BridgeState, MINIMAL_DELIVERY_FEE_FACTOR}; +use crate::{DeliveryFeeFactor, MINIMAL_DELIVERY_FEE_FACTOR}; use frame_benchmarking::{benchmarks_instance_pallet, BenchmarkError}; -use frame_support::traits::{EnsureOrigin, Get, Hooks, UnfilteredDispatchable}; +use frame_support::traits::{Get, Hooks}; use sp_runtime::traits::Zero; use xcm::prelude::*; @@ -47,49 +45,16 @@ pub trait Config: crate::Config { benchmarks_instance_pallet! { on_initialize_when_non_congested { - Bridge::::put(BridgeState { - is_congested: false, - delivery_fee_factor: MINIMAL_DELIVERY_FEE_FACTOR + MINIMAL_DELIVERY_FEE_FACTOR, - }); + DeliveryFeeFactor::::put(MINIMAL_DELIVERY_FEE_FACTOR + MINIMAL_DELIVERY_FEE_FACTOR); }: { crate::Pallet::::on_initialize(Zero::zero()) } on_initialize_when_congested { - Bridge::::put(BridgeState { - is_congested: false, - delivery_fee_factor: MINIMAL_DELIVERY_FEE_FACTOR + MINIMAL_DELIVERY_FEE_FACTOR, - }); - + DeliveryFeeFactor::::put(MINIMAL_DELIVERY_FEE_FACTOR + MINIMAL_DELIVERY_FEE_FACTOR); let _ = T::ensure_bridged_target_destination()?; T::make_congested(); }: { crate::Pallet::::on_initialize(Zero::zero()) } - - report_bridge_status { - Bridge::::put(BridgeState::default()); - - let origin: T::RuntimeOrigin = T::BridgeHubOrigin::try_successful_origin().expect("expected valid BridgeHubOrigin"); - let bridge_id = Default::default(); - let is_congested = true; - - let call = Call::::report_bridge_status { bridge_id, is_congested }; - }: { call.dispatch_bypass_filter(origin)? } - verify { - assert!(Bridge::::get().is_congested); - } - - send_message { - let dest = T::ensure_bridged_target_destination()?; - let xcm = sp_std::vec![].into(); - - // make local queue congested, because it means additional db write - T::make_congested(); - }: { - send_xcm::>(dest, xcm).expect("message is sent") - } - verify { - assert!(Bridge::::get().delivery_fee_factor > MINIMAL_DELIVERY_FEE_FACTOR); - } } diff --git a/bridges/modules/xcm-bridge-hub-router/src/lib.rs b/bridges/modules/xcm-bridge-hub-router/src/lib.rs index 607394603466e6cb60d20dbefa4aa47580b54c42..fe8f5a2efdfb8ad2f166941a841def915bc103b0 100644 --- a/bridges/modules/xcm-bridge-hub-router/src/lib.rs +++ b/bridges/modules/xcm-bridge-hub-router/src/lib.rs @@ -30,12 +30,9 @@ #![cfg_attr(not(feature = "std"), no_std)] -use bp_xcm_bridge_hub_router::{ - BridgeState, XcmChannelStatusProvider, MINIMAL_DELIVERY_FEE_FACTOR, -}; +pub use bp_xcm_bridge_hub_router::XcmChannelStatusProvider; use codec::Encode; use frame_support::traits::Get; -use sp_core::H256; use sp_runtime::{FixedPointNumber, FixedU128, Saturating}; use sp_std::vec::Vec; use xcm::prelude::*; @@ -49,6 +46,9 @@ pub mod weights; mod mock; +/// Minimal delivery fee factor. +pub const MINIMAL_DELIVERY_FEE_FACTOR: FixedU128 = FixedU128::from_u32(1); + /// The factor that is used to increase current message fee factor when bridge experiencing /// some lags. const EXPONENTIAL_FEE_BASE: FixedU128 = FixedU128::from_rational(105, 100); // 1.05 @@ -77,11 +77,16 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { + /// The overarching event type. + type RuntimeEvent: From> + + IsType<::RuntimeEvent>; /// Benchmarks results from runtime we're plugged into. type WeightInfo: WeightInfo; /// Universal location of this runtime. type UniversalLocation: Get; + /// Relative location of the supported sibling bridge hub. + type SiblingBridgeHubLocation: Get; /// The bridged network that this config is for if specified. /// Also used for filtering `Bridges` by `BridgedNetworkId`. /// If not specified, allows all networks pass through. @@ -93,13 +98,10 @@ pub mod pallet { /// Checks the XCM version for the destination. type DestinationVersion: GetVersion; - /// Origin of the sibling bridge hub that is allowed to report bridge status. - type BridgeHubOrigin: EnsureOrigin; /// Actual message sender (`HRMP` or `DMP`) to the sibling bridge hub location. - type ToBridgeHubSender: SendXcm + InspectMessageQueues; - /// Underlying channel with the sibling bridge hub. It must match the channel, used - /// by the `Self::ToBridgeHubSender`. - type WithBridgeHubChannel: XcmChannelStatusProvider; + type ToBridgeHubSender: SendXcm; + /// Local XCM channel manager. + type LocalXcmChannelManager: XcmChannelStatusProvider; /// Additional fee that is paid for every byte of the outbound message. type ByteFee: Get; @@ -113,118 +115,118 @@ pub mod pallet { #[pallet::hooks] impl, I: 'static> Hooks> for Pallet { fn on_initialize(_n: BlockNumberFor) -> Weight { - // TODO: make sure that `WithBridgeHubChannel::is_congested` returns true if either - // of XCM channels (outbound/inbound) is suspended. Because if outbound is suspended - // that is definitely congestion. If inbound is suspended, then we are not able to - // receive the "report_bridge_status" signal (that maybe sent by the bridge hub). - - // if the channel with sibling/child bridge hub is suspended, we don't change - // anything - if T::WithBridgeHubChannel::is_congested() { + // if XCM channel is still congested, we don't change anything + if T::LocalXcmChannelManager::is_congested(&T::SiblingBridgeHubLocation::get()) { return T::WeightInfo::on_initialize_when_congested() } - // if bridge has reported congestion, we don't change anything - let mut bridge = Self::bridge(); - if bridge.is_congested { + // if we can't decrease the delivery fee factor anymore, we don't change anything + let mut delivery_fee_factor = Self::delivery_fee_factor(); + if delivery_fee_factor == MINIMAL_DELIVERY_FEE_FACTOR { return T::WeightInfo::on_initialize_when_congested() } - // if fee factor is already minimal, we don't change anything - if bridge.delivery_fee_factor == MINIMAL_DELIVERY_FEE_FACTOR { - return T::WeightInfo::on_initialize_when_congested() - } - - let previous_factor = bridge.delivery_fee_factor; - bridge.delivery_fee_factor = - MINIMAL_DELIVERY_FEE_FACTOR.max(bridge.delivery_fee_factor / EXPONENTIAL_FEE_BASE); + let previous_factor = delivery_fee_factor; + delivery_fee_factor = + MINIMAL_DELIVERY_FEE_FACTOR.max(delivery_fee_factor / EXPONENTIAL_FEE_BASE); log::info!( target: LOG_TARGET, - "Bridge queue is uncongested. Decreased fee factor from {} to {}", + "Bridge channel is uncongested. Decreased fee factor from {} to {}", previous_factor, - bridge.delivery_fee_factor, + delivery_fee_factor, ); + Self::deposit_event(Event::DeliveryFeeFactorDecreased { + new_value: delivery_fee_factor, + }); + + DeliveryFeeFactor::::put(delivery_fee_factor); - Bridge::::put(bridge); T::WeightInfo::on_initialize_when_non_congested() } } - #[pallet::call] - impl, I: 'static> Pallet { - /// Notification about congested bridge queue. - #[pallet::call_index(0)] - #[pallet::weight(T::WeightInfo::report_bridge_status())] - pub fn report_bridge_status( - origin: OriginFor, - // this argument is not currently used, but to ease future migration, we'll keep it - // here - bridge_id: H256, - is_congested: bool, - ) -> DispatchResult { - let _ = T::BridgeHubOrigin::ensure_origin(origin)?; - - log::info!( - target: LOG_TARGET, - "Received bridge status from {:?}: congested = {}", - bridge_id, - is_congested, - ); - - Bridge::::mutate(|bridge| { - bridge.is_congested = is_congested; - }); - Ok(()) - } + /// Initialization value for the delivery fee factor. + #[pallet::type_value] + pub fn InitialFactor() -> FixedU128 { + MINIMAL_DELIVERY_FEE_FACTOR } - /// Bridge that we are using. + /// The number to multiply the base delivery fee by. + /// + /// This factor is shared by all bridges, served by this pallet. For example, if this + /// chain (`Config::UniversalLocation`) opens two bridges ( + /// `X2(GlobalConsensus(Config::BridgedNetworkId::get()), Parachain(1000))` and + /// `X2(GlobalConsensus(Config::BridgedNetworkId::get()), Parachain(2000))`), then they + /// both will be sharing the same fee factor. This is because both bridges are sharing + /// the same local XCM channel with the child/sibling bridge hub, which we are using + /// to detect congestion: /// - /// **bridges-v1** assumptions: all outbound messages through this router are using single lane - /// and to single remote consensus. If there is some other remote consensus that uses the same - /// bridge hub, the separate pallet instance shall be used, In `v2` we'll have all required - /// primitives (lane-id aka bridge-id, derived from XCM locations) to support multiple bridges - /// by the same pallet instance. + /// ```nocompile + /// ThisChain --- Local XCM channel --> Sibling Bridge Hub ------ + /// | | + /// | | + /// | | + /// Lane1 Lane2 + /// | | + /// | | + /// | | + /// \ / | + /// Parachain1 <-- Local XCM channel --- Remote Bridge Hub <------ + /// | + /// | + /// Parachain1 <-- Local XCM channel --------- + /// ``` + /// + /// If at least one of other channels is congested, the local XCM channel with sibling + /// bridge hub eventually becomes congested too. And we have no means to detect - which + /// bridge exactly causes the congestion. So the best solution here is not to make + /// any differences between all bridges, started by this chain. #[pallet::storage] - #[pallet::getter(fn bridge)] - pub type Bridge, I: 'static = ()> = StorageValue<_, BridgeState, ValueQuery>; + #[pallet::getter(fn delivery_fee_factor)] + pub type DeliveryFeeFactor, I: 'static = ()> = + StorageValue<_, FixedU128, ValueQuery, InitialFactor>; impl, I: 'static> Pallet { /// Called when new message is sent (queued to local outbound XCM queue) over the bridge. pub(crate) fn on_message_sent_to_bridge(message_size: u32) { - log::trace!( - target: LOG_TARGET, - "on_message_sent_to_bridge - message_size: {message_size:?}", - ); - let _ = Bridge::::try_mutate(|bridge| { - let is_channel_with_bridge_hub_congested = T::WithBridgeHubChannel::is_congested(); - let is_bridge_congested = bridge.is_congested; - - // if outbound queue is not congested AND bridge has not reported congestion, do - // nothing - if !is_channel_with_bridge_hub_congested && !is_bridge_congested { - return Err(()) - } - - // ok - we need to increase the fee factor, let's do that - let message_size_factor = FixedU128::from_u32(message_size.saturating_div(1024)) - .saturating_mul(MESSAGE_SIZE_FEE_BASE); - let total_factor = EXPONENTIAL_FEE_BASE.saturating_add(message_size_factor); - let previous_factor = bridge.delivery_fee_factor; - bridge.delivery_fee_factor = - bridge.delivery_fee_factor.saturating_mul(total_factor); + // if outbound channel is not congested, do nothing + if !T::LocalXcmChannelManager::is_congested(&T::SiblingBridgeHubLocation::get()) { + return + } + // ok - we need to increase the fee factor, let's do that + let message_size_factor = FixedU128::from_u32(message_size.saturating_div(1024)) + .saturating_mul(MESSAGE_SIZE_FEE_BASE); + let total_factor = EXPONENTIAL_FEE_BASE.saturating_add(message_size_factor); + DeliveryFeeFactor::::mutate(|f| { + let previous_factor = *f; + *f = f.saturating_mul(total_factor); log::info!( target: LOG_TARGET, "Bridge channel is congested. Increased fee factor from {} to {}", previous_factor, - bridge.delivery_fee_factor, + f, ); - - Ok(()) + Self::deposit_event(Event::DeliveryFeeFactorIncreased { new_value: *f }); + *f }); } } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event, I: 'static = ()> { + /// Delivery fee factor has been decreased. + DeliveryFeeFactorDecreased { + /// New value of the `DeliveryFeeFactor`. + new_value: FixedU128, + }, + /// Delivery fee factor has been increased. + DeliveryFeeFactorIncreased { + /// New value of the `DeliveryFeeFactor`. + new_value: FixedU128, + }, + } } /// We'll be using `SovereignPaidRemoteExporter` to send remote messages over the sibling/child @@ -259,17 +261,25 @@ impl, I: 'static> ExporterFor for Pallet { } // ensure that the message is sent to the expected bridged network and location. - let Some((bridge_hub_location, maybe_payment)) = - T::Bridges::exporter_for(network, remote_location, message) - else { - log::trace!( - target: LOG_TARGET, - "Router with bridged_network_id {:?} does not support bridging to network {:?} and remote_location {:?}!", - T::BridgedNetworkId::get(), - network, - remote_location, - ); - return None + let (bridge_hub_location, maybe_payment) = match T::Bridges::exporter_for( + network, + remote_location, + message, + ) { + Some((bridge_hub_location, maybe_payment)) + if bridge_hub_location.eq(&T::SiblingBridgeHubLocation::get()) => + (bridge_hub_location, maybe_payment), + _ => { + log::trace!( + target: LOG_TARGET, + "Router configured with bridged_network_id {:?} and sibling_bridge_hub_location: {:?} does not support bridging to network {:?} and remote_location {:?}!", + T::BridgedNetworkId::get(), + T::SiblingBridgeHubLocation::get(), + network, + remote_location, + ); + return None + }, }; // take `base_fee` from `T::Brides`, but it has to be the same `T::FeeAsset` @@ -279,8 +289,8 @@ impl, I: 'static> ExporterFor for Pallet { invalid_asset => { log::error!( target: LOG_TARGET, - "Router with bridged_network_id {:?} is configured for `T::FeeAsset` {:?} which is not \ - compatible with {:?} for bridge_hub_location: {:?} for bridging to {:?}/{:?}!", + "Router with bridged_network_id {:?} is configured for `T::FeeAsset` {:?} \ + which is not compatible with {:?} for bridge_hub_location: {:?} for bridging to {:?}/{:?}!", T::BridgedNetworkId::get(), T::FeeAsset::get(), invalid_asset, @@ -300,18 +310,18 @@ impl, I: 'static> ExporterFor for Pallet { let message_size = message.encoded_size(); let message_fee = (message_size as u128).saturating_mul(T::ByteFee::get()); let fee_sum = base_fee.saturating_add(message_fee); - let fee_factor = Self::bridge().delivery_fee_factor; - let fee = fee_factor.saturating_mul_int(fee_sum); + let fee_factor = Self::delivery_fee_factor(); + let fee = fee_factor.saturating_mul_int(fee_sum); let fee = if fee > 0 { Some((T::FeeAsset::get(), fee).into()) } else { None }; log::info!( target: LOG_TARGET, - "Validate send message to {:?} ({} bytes) over bridge. Computed bridge fee {:?} using fee factor {}", + "Going to send message to {:?} ({} bytes) over bridge. Computed bridge fee {:?} using fee factor {}", (network, remote_location), message_size, fee, - fee_factor + fee_factor, ); Some((bridge_hub_location, fee)) @@ -398,8 +408,12 @@ impl, I: 'static> SendXcm for Pallet { } impl, I: 'static> InspectMessageQueues for Pallet { + fn clear_messages() {} + + /// This router needs to implement `InspectMessageQueues` but doesn't have to + /// return any messages, since it just reuses the `XcmpQueue` router. fn get_messages() -> Vec<(VersionedLocation, Vec>)> { - ViaBridgeHubExporter::::get_messages() + Vec::new() } } @@ -410,66 +424,57 @@ mod tests { use mock::*; use frame_support::traits::Hooks; + use frame_system::{EventRecord, Phase}; use sp_runtime::traits::One; - fn congested_bridge(delivery_fee_factor: FixedU128) -> BridgeState { - BridgeState { is_congested: true, delivery_fee_factor } - } - - fn uncongested_bridge(delivery_fee_factor: FixedU128) -> BridgeState { - BridgeState { is_congested: false, delivery_fee_factor } - } - #[test] fn initial_fee_factor_is_one() { run_test(|| { - assert_eq!( - Bridge::::get(), - uncongested_bridge(MINIMAL_DELIVERY_FEE_FACTOR), - ); + assert_eq!(DeliveryFeeFactor::::get(), MINIMAL_DELIVERY_FEE_FACTOR); }) } #[test] fn fee_factor_is_not_decreased_from_on_initialize_when_xcm_channel_is_congested() { run_test(|| { - Bridge::::put(uncongested_bridge(FixedU128::from_rational(125, 100))); - TestWithBridgeHubChannel::make_congested(); + DeliveryFeeFactor::::put(FixedU128::from_rational(125, 100)); + TestLocalXcmChannelManager::make_congested(&SiblingBridgeHubLocation::get()); - // it should not decrease, because xcm channel is congested - let old_bridge = XcmBridgeHubRouter::bridge(); + // it should not decrease, because queue is congested + let old_delivery_fee_factor = XcmBridgeHubRouter::delivery_fee_factor(); XcmBridgeHubRouter::on_initialize(One::one()); - assert_eq!(XcmBridgeHubRouter::bridge(), old_bridge); - }) - } + assert_eq!(XcmBridgeHubRouter::delivery_fee_factor(), old_delivery_fee_factor); - #[test] - fn fee_factor_is_not_decreased_from_on_initialize_when_bridge_has_reported_congestion() { - run_test(|| { - Bridge::::put(congested_bridge(FixedU128::from_rational(125, 100))); - - // it should not decrease, because bridge congested - let old_bridge = XcmBridgeHubRouter::bridge(); - XcmBridgeHubRouter::on_initialize(One::one()); - assert_eq!(XcmBridgeHubRouter::bridge(), old_bridge); + assert_eq!(System::events(), vec![]); }) } #[test] fn fee_factor_is_decreased_from_on_initialize_when_xcm_channel_is_uncongested() { run_test(|| { - Bridge::::put(uncongested_bridge(FixedU128::from_rational(125, 100))); + let initial_fee_factor = FixedU128::from_rational(125, 100); + DeliveryFeeFactor::::put(initial_fee_factor); - // it should eventually decreased to one - while XcmBridgeHubRouter::bridge().delivery_fee_factor > MINIMAL_DELIVERY_FEE_FACTOR { + // it shold eventually decreased to one + while XcmBridgeHubRouter::delivery_fee_factor() > MINIMAL_DELIVERY_FEE_FACTOR { XcmBridgeHubRouter::on_initialize(One::one()); } // verify that it doesn't decreases anymore XcmBridgeHubRouter::on_initialize(One::one()); + assert_eq!(XcmBridgeHubRouter::delivery_fee_factor(), MINIMAL_DELIVERY_FEE_FACTOR); + + // check emitted event + let first_system_event = System::events().first().cloned(); assert_eq!( - XcmBridgeHubRouter::bridge(), - uncongested_bridge(MINIMAL_DELIVERY_FEE_FACTOR) + first_system_event, + Some(EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::XcmBridgeHubRouter(Event::DeliveryFeeFactorDecreased { + new_value: initial_fee_factor / EXPONENTIAL_FEE_BASE, + }), + topics: vec![], + }) ); }) } @@ -577,7 +582,7 @@ mod tests { // but when factor is larger than one, it increases the fee, so it becomes: // `(BASE_FEE + BYTE_FEE * msg_size) * F + HRMP_FEE` let factor = FixedU128::from_rational(125, 100); - Bridge::::put(uncongested_bridge(factor)); + DeliveryFeeFactor::::put(factor); let expected_fee = (FixedU128::saturating_from_integer(BASE_FEE + BYTE_FEE * (msg_size as u128)) * factor) @@ -591,45 +596,31 @@ mod tests { } #[test] - fn sent_message_doesnt_increase_factor_if_xcm_channel_is_uncongested() { + fn sent_message_doesnt_increase_factor_if_queue_is_uncongested() { run_test(|| { - let old_bridge = XcmBridgeHubRouter::bridge(); - assert_ok!(send_xcm::( - Location::new(2, [GlobalConsensus(BridgedNetworkId::get()), Parachain(1000)]), - vec![ClearOrigin].into(), - ) - .map(drop)); + let old_delivery_fee_factor = XcmBridgeHubRouter::delivery_fee_factor(); + assert_eq!( + send_xcm::( + Location::new(2, [GlobalConsensus(BridgedNetworkId::get()), Parachain(1000)]), + vec![ClearOrigin].into(), + ) + .map(drop), + Ok(()), + ); assert!(TestToBridgeHubSender::is_message_sent()); - assert_eq!(old_bridge, XcmBridgeHubRouter::bridge()); - }); - } - - #[test] - fn sent_message_increases_factor_if_xcm_channel_is_congested() { - run_test(|| { - TestWithBridgeHubChannel::make_congested(); - - let old_bridge = XcmBridgeHubRouter::bridge(); - assert_ok!(send_xcm::( - Location::new(2, [GlobalConsensus(BridgedNetworkId::get()), Parachain(1000)]), - vec![ClearOrigin].into(), - ) - .map(drop)); + assert_eq!(old_delivery_fee_factor, XcmBridgeHubRouter::delivery_fee_factor()); - assert!(TestToBridgeHubSender::is_message_sent()); - assert!( - old_bridge.delivery_fee_factor < XcmBridgeHubRouter::bridge().delivery_fee_factor - ); + assert_eq!(System::events(), vec![]); }); } #[test] - fn sent_message_increases_factor_if_bridge_has_reported_congestion() { + fn sent_message_increases_factor_if_xcm_channel_is_congested() { run_test(|| { - Bridge::::put(congested_bridge(MINIMAL_DELIVERY_FEE_FACTOR)); + TestLocalXcmChannelManager::make_congested(&SiblingBridgeHubLocation::get()); - let old_bridge = XcmBridgeHubRouter::bridge(); + let old_delivery_fee_factor = XcmBridgeHubRouter::delivery_fee_factor(); assert_ok!(send_xcm::( Location::new(2, [GlobalConsensus(BridgedNetworkId::get()), Parachain(1000)]), vec![ClearOrigin].into(), @@ -637,41 +628,31 @@ mod tests { .map(drop)); assert!(TestToBridgeHubSender::is_message_sent()); - assert!( - old_bridge.delivery_fee_factor < XcmBridgeHubRouter::bridge().delivery_fee_factor - ); + assert!(old_delivery_fee_factor < XcmBridgeHubRouter::delivery_fee_factor()); + + // check emitted event + let first_system_event = System::events().first().cloned(); + assert!(matches!( + first_system_event, + Some(EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::XcmBridgeHubRouter( + Event::DeliveryFeeFactorIncreased { .. } + ), + .. + }) + )); }); } #[test] - fn get_messages_works() { + fn get_messages_does_not_return_anything() { run_test(|| { assert_ok!(send_xcm::( (Parent, Parent, GlobalConsensus(BridgedNetworkId::get()), Parachain(1000)).into(), vec![ClearOrigin].into() )); - assert_eq!( - XcmBridgeHubRouter::get_messages(), - vec![( - VersionedLocation::V4((Parent, Parachain(1002)).into()), - vec![VersionedXcm::V4( - Xcm::builder() - .withdraw_asset((Parent, 1_002_000)) - .buy_execution((Parent, 1_002_000), Unlimited) - .set_appendix( - Xcm::builder_unsafe() - .deposit_asset(AllCounted(1), (Parent, Parachain(1000))) - .build() - ) - .export_message( - Kusama, - Parachain(1000), - Xcm::builder_unsafe().clear_origin().build() - ) - .build() - )], - ),], - ); + assert_eq!(XcmBridgeHubRouter::get_messages(), vec![]); }); } } diff --git a/bridges/modules/xcm-bridge-hub-router/src/mock.rs b/bridges/modules/xcm-bridge-hub-router/src/mock.rs index 3e2c1bb369cb72439e53e492cf638a404caa99a3..bb265e1925a20fbfb7b4f6ea480b2145ca75cc4f 100644 --- a/bridges/modules/xcm-bridge-hub-router/src/mock.rs +++ b/bridges/modules/xcm-bridge-hub-router/src/mock.rs @@ -24,13 +24,11 @@ use frame_support::{ construct_runtime, derive_impl, parameter_types, traits::{Contains, Equals}, }; -use frame_system::EnsureRoot; use sp_runtime::{traits::ConstU128, BuildStorage}; use sp_std::cell::RefCell; use xcm::prelude::*; use xcm_builder::{InspectMessageQueues, NetworkExportTable, NetworkExportTableItem}; -pub type AccountId = u64; type Block = frame_system::mocking::MockBlock; /// HRMP fee. @@ -44,7 +42,7 @@ construct_runtime! { pub enum TestRuntime { System: frame_system::{Pallet, Call, Config, Storage, Event}, - XcmBridgeHubRouter: pallet_xcm_bridge_hub_router::{Pallet, Storage}, + XcmBridgeHubRouter: pallet_xcm_bridge_hub_router::{Pallet, Storage, Event}, } } @@ -72,17 +70,18 @@ impl frame_system::Config for TestRuntime { } impl pallet_xcm_bridge_hub_router::Config<()> for TestRuntime { + type RuntimeEvent = RuntimeEvent; type WeightInfo = (); type UniversalLocation = UniversalLocation; + type SiblingBridgeHubLocation = SiblingBridgeHubLocation; type BridgedNetworkId = BridgedNetworkId; type Bridges = NetworkExportTable; type DestinationVersion = LatestOrNoneForLocationVersionChecker>; - type BridgeHubOrigin = EnsureRoot; type ToBridgeHubSender = TestToBridgeHubSender; - type WithBridgeHubChannel = TestWithBridgeHubChannel; + type LocalXcmChannelManager = TestLocalXcmChannelManager; type ByteFee = ConstU128; type FeeAsset = BridgeFeeAsset; @@ -131,6 +130,10 @@ impl SendXcm for TestToBridgeHubSender { } impl InspectMessageQueues for TestToBridgeHubSender { + fn clear_messages() { + SENT_XCM.with(|q| q.borrow_mut().clear()); + } + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { SENT_XCM.with(|q| { (*q.borrow()) @@ -147,17 +150,22 @@ impl InspectMessageQueues for TestToBridgeHubSender { } } -pub struct TestWithBridgeHubChannel; +pub struct TestLocalXcmChannelManager; -impl TestWithBridgeHubChannel { - pub fn make_congested() { - frame_support::storage::unhashed::put(b"TestWithBridgeHubChannel.Congested", &true); +impl TestLocalXcmChannelManager { + pub fn make_congested(with: &Location) { + frame_support::storage::unhashed::put( + &(b"TestLocalXcmChannelManager.Congested", with).encode()[..], + &true, + ); } } -impl XcmChannelStatusProvider for TestWithBridgeHubChannel { - fn is_congested() -> bool { - frame_support::storage::unhashed::get_or_default(b"TestWithBridgeHubChannel.Congested") +impl XcmChannelStatusProvider for TestLocalXcmChannelManager { + fn is_congested(with: &Location) -> bool { + frame_support::storage::unhashed::get_or_default( + &(b"TestLocalXcmChannelManager.Congested", with).encode()[..], + ) } } @@ -169,7 +177,12 @@ pub fn new_test_ext() -> sp_io::TestExternalities { /// Run pallet test. pub fn run_test(test: impl FnOnce() -> T) -> T { - new_test_ext().execute_with(test) + new_test_ext().execute_with(|| { + System::set_block_number(1); + System::reset_events(); + + test() + }) } pub(crate) fn fake_message_hash(message: &Xcm) -> XcmHash { diff --git a/bridges/modules/xcm-bridge-hub-router/src/weights.rs b/bridges/modules/xcm-bridge-hub-router/src/weights.rs index b0c8fc6252cd5e6eaa968cce06636a308e1c7e05..d9a0426fecaf8de6858b785222a50d0e4291f0af 100644 --- a/bridges/modules/xcm-bridge-hub-router/src/weights.rs +++ b/bridges/modules/xcm-bridge-hub-router/src/weights.rs @@ -52,8 +52,6 @@ use sp_std::marker::PhantomData; pub trait WeightInfo { fn on_initialize_when_non_congested() -> Weight; fn on_initialize_when_congested() -> Weight; - fn report_bridge_status() -> Weight; - fn send_message() -> Weight; } /// Weights for `pallet_xcm_bridge_hub_router` that are generated using one of the Bridge testnets. @@ -61,30 +59,20 @@ pub trait WeightInfo { /// Those weights are test only and must never be used in production. pub struct BridgeWeight(PhantomData); impl WeightInfo for BridgeWeight { - /// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1) /// - /// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: - /// 512, mode: `MaxEncodedLen`) + /// Storage: `XcmBridgeHubRouter::DeliveryFeeFactor` (r:1 w:1) /// - /// Storage: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` - /// (r:1 w:0) - /// - /// Proof: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` (r:1 - /// w:0) + /// Proof: `XcmBridgeHubRouter::DeliveryFeeFactor` (`max_values`: Some(1), `max_size`: Some(16), + /// added: 511, mode: `MaxEncodedLen`) fn on_initialize_when_non_congested() -> Weight { // Proof Size summary in bytes: - // Measured: `53` - // Estimated: `3518` - // Minimum execution time: 11_934 nanoseconds. - Weight::from_parts(12_201_000, 3518) + // Measured: `52` + // Estimated: `3517` + // Minimum execution time: 11_141 nanoseconds. + Weight::from_parts(11_339_000, 3517) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1) - /// - /// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: - /// 512, mode: `MaxEncodedLen`) - /// /// Storage: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` /// (r:1 w:0) /// @@ -92,117 +80,44 @@ impl WeightInfo for BridgeWeight { /// w:0) fn on_initialize_when_congested() -> Weight { // Proof Size summary in bytes: - // Measured: `94` - // Estimated: `3559` - // Minimum execution time: 9_010 nanoseconds. - Weight::from_parts(9_594_000, 3559) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1) - /// - /// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: - /// 512, mode: `MaxEncodedLen`) - fn report_bridge_status() -> Weight { - // Proof Size summary in bytes: - // Measured: `53` - // Estimated: `1502` - // Minimum execution time: 10_427 nanoseconds. - Weight::from_parts(10_682_000, 1502) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1) - /// - /// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: - /// 512, mode: `MaxEncodedLen`) - /// - /// Storage: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` - /// (r:1 w:0) - /// - /// Proof: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` (r:1 - /// w:0) - fn send_message() -> Weight { - // Proof Size summary in bytes: - // Measured: `52` - // Estimated: `3517` - // Minimum execution time: 19_709 nanoseconds. - Weight::from_parts(20_110_000, 3517) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) + // Measured: `82` + // Estimated: `3547` + // Minimum execution time: 4_239 nanoseconds. + Weight::from_parts(4_383_000, 3547).saturating_add(T::DbWeight::get().reads(1_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - /// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1) - /// - /// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: - /// 512, mode: `MaxEncodedLen`) - /// /// Storage: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` /// (r:1 w:0) /// /// Proof: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` (r:1 /// w:0) - fn on_initialize_when_non_congested() -> Weight { - // Proof Size summary in bytes: - // Measured: `53` - // Estimated: `3518` - // Minimum execution time: 11_934 nanoseconds. - Weight::from_parts(12_201_000, 3518) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1) /// - /// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: - /// 512, mode: `MaxEncodedLen`) + /// Storage: `XcmBridgeHubRouter::DeliveryFeeFactor` (r:1 w:1) /// - /// Storage: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` - /// (r:1 w:0) - /// - /// Proof: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` (r:1 - /// w:0) - fn on_initialize_when_congested() -> Weight { + /// Proof: `XcmBridgeHubRouter::DeliveryFeeFactor` (`max_values`: Some(1), `max_size`: Some(16), + /// added: 511, mode: `MaxEncodedLen`) + fn on_initialize_when_non_congested() -> Weight { // Proof Size summary in bytes: - // Measured: `94` - // Estimated: `3559` - // Minimum execution time: 9_010 nanoseconds. - Weight::from_parts(9_594_000, 3559) + // Measured: `52` + // Estimated: `3517` + // Minimum execution time: 11_141 nanoseconds. + Weight::from_parts(11_339_000, 3517) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1) - /// - /// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: - /// 512, mode: `MaxEncodedLen`) - fn report_bridge_status() -> Weight { - // Proof Size summary in bytes: - // Measured: `53` - // Estimated: `1502` - // Minimum execution time: 10_427 nanoseconds. - Weight::from_parts(10_682_000, 1502) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1) - /// - /// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: - /// 512, mode: `MaxEncodedLen`) - /// /// Storage: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` /// (r:1 w:0) /// /// Proof: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` (r:1 /// w:0) - fn send_message() -> Weight { + fn on_initialize_when_congested() -> Weight { // Proof Size summary in bytes: - // Measured: `52` - // Estimated: `3517` - // Minimum execution time: 19_709 nanoseconds. - Weight::from_parts(20_110_000, 3517) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) + // Measured: `82` + // Estimated: `3547` + // Minimum execution time: 4_239 nanoseconds. + Weight::from_parts(4_383_000, 3547).saturating_add(RocksDbWeight::get().reads(1_u64)) } } diff --git a/bridges/modules/xcm-bridge-hub/Cargo.toml b/bridges/modules/xcm-bridge-hub/Cargo.toml index 8fbf61a0a5354e808ad4a5c74094c907eb29470d..fe58b910a94ef99547bed8f2956a6084aea43e2d 100644 --- a/bridges/modules/xcm-bridge-hub/Cargo.toml +++ b/bridges/modules/xcm-bridge-hub/Cargo.toml @@ -20,7 +20,6 @@ bp-messages = { workspace = true } bp-runtime = { workspace = true } bp-xcm-bridge-hub = { workspace = true } pallet-bridge-messages = { workspace = true } -bridge-runtime-common = { workspace = true } # Substrate Dependencies frame-support = { workspace = true } @@ -35,27 +34,31 @@ xcm-builder = { workspace = true } xcm-executor = { workspace = true } [dev-dependencies] -bp-header-chain = { workspace = true, default-features = true } -pallet-balances = { workspace = true, default-features = true } -sp-io = { workspace = true, default-features = true } -bp-runtime = { features = ["test-helpers"], workspace = true } +pallet-balances = { workspace = true } +sp-io = { workspace = true } +bp-runtime = { workspace = true } +bp-header-chain = { workspace = true } pallet-xcm-bridge-hub-router = { workspace = true } +polkadot-parachain-primitives = { workspace = true } [features] default = ["std"] std = [ + "bp-header-chain/std", "bp-messages/std", "bp-runtime/std", "bp-xcm-bridge-hub/std", - "bridge-runtime-common/std", "codec/std", "frame-support/std", "frame-system/std", "log/std", + "pallet-balances/std", "pallet-bridge-messages/std", "pallet-xcm-bridge-hub-router/std", + "polkadot-parachain-primitives/std", "scale-info/std", "sp-core/std", + "sp-io/std", "sp-runtime/std", "sp-std/std", "xcm-builder/std", @@ -63,12 +66,12 @@ std = [ "xcm/std", ] runtime-benchmarks = [ - "bridge-runtime-common/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "pallet-bridge-messages/runtime-benchmarks", "pallet-xcm-bridge-hub-router/runtime-benchmarks", + "polkadot-parachain-primitives/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", diff --git a/bridges/modules/xcm-bridge-hub/src/dispatcher.rs b/bridges/modules/xcm-bridge-hub/src/dispatcher.rs new file mode 100644 index 0000000000000000000000000000000000000000..dd855c7069aadf1fc95840c462530cd7bb7bcb00 --- /dev/null +++ b/bridges/modules/xcm-bridge-hub/src/dispatcher.rs @@ -0,0 +1,267 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! The code that allows to use the pallet (`pallet-xcm-bridge-hub`) as inbound +//! bridge messages dispatcher. Internally, it just forwards inbound blob to the +//! XCM-level blob dispatcher, which pushes message to some other queue (e.g. +//! to HRMP queue with the sibling target chain). +//! +//! This code is executed at the target bridge hub. + +use crate::{Config, Pallet, LOG_TARGET}; + +use bp_messages::target_chain::{DispatchMessage, MessageDispatch}; +use bp_runtime::messages::MessageDispatchResult; +use bp_xcm_bridge_hub::{LocalXcmChannelManager, XcmAsPlainPayload}; +use codec::{Decode, Encode}; +use frame_support::{weights::Weight, CloneNoBound, EqNoBound, PartialEqNoBound}; +use pallet_bridge_messages::{Config as BridgeMessagesConfig, WeightInfoExt}; +use scale_info::TypeInfo; +use sp_runtime::SaturatedConversion; +use xcm::prelude::*; +use xcm_builder::{DispatchBlob, DispatchBlobError}; + +/// Message dispatch result type for single message. +#[derive(CloneNoBound, EqNoBound, PartialEqNoBound, Encode, Decode, Debug, TypeInfo)] +pub enum XcmBlobMessageDispatchResult { + /// We've been unable to decode message payload. + InvalidPayload, + /// Message has been dispatched. + Dispatched, + /// Message has **NOT** been dispatched because of given error. + NotDispatched(#[codec(skip)] Option), +} + +/// An easy way to access associated messages pallet weights. +type MessagesPalletWeights = + >::BridgeMessagesPalletInstance>>::WeightInfo; + +impl, I: 'static> MessageDispatch for Pallet +where + T: BridgeMessagesConfig, +{ + type DispatchPayload = XcmAsPlainPayload; + type DispatchLevelResult = XcmBlobMessageDispatchResult; + type LaneId = T::LaneId; + + fn is_active(lane: Self::LaneId) -> bool { + Pallet::::bridge_by_lane_id(&lane) + .and_then(|(_, bridge)| bridge.bridge_origin_relative_location.try_as().cloned().ok()) + .map(|recipient: Location| !T::LocalXcmChannelManager::is_congested(&recipient)) + .unwrap_or(false) + } + + fn dispatch_weight( + message: &mut DispatchMessage, + ) -> Weight { + match message.data.payload { + Ok(ref payload) => { + let payload_size = payload.encoded_size().saturated_into(); + MessagesPalletWeights::::message_dispatch_weight(payload_size) + }, + Err(_) => Weight::zero(), + } + } + + fn dispatch( + message: DispatchMessage, + ) -> MessageDispatchResult { + let payload = match message.data.payload { + Ok(payload) => payload, + Err(e) => { + log::error!( + target: LOG_TARGET, + "dispatch - payload error: {e:?} for lane_id: {:?} and message_nonce: {:?}", + message.key.lane_id, + message.key.nonce + ); + return MessageDispatchResult { + unspent_weight: Weight::zero(), + dispatch_level_result: XcmBlobMessageDispatchResult::InvalidPayload, + } + }, + }; + let dispatch_level_result = match T::BlobDispatcher::dispatch_blob(payload) { + Ok(_) => { + log::debug!( + target: LOG_TARGET, + "dispatch - `DispatchBlob::dispatch_blob` was ok for lane_id: {:?} and message_nonce: {:?}", + message.key.lane_id, + message.key.nonce + ); + XcmBlobMessageDispatchResult::Dispatched + }, + Err(e) => { + log::error!( + target: LOG_TARGET, + "dispatch - `DispatchBlob::dispatch_blob` failed with error: {e:?} for lane_id: {:?} and message_nonce: {:?}", + message.key.lane_id, + message.key.nonce + ); + XcmBlobMessageDispatchResult::NotDispatched(Some(e)) + }, + }; + MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{mock::*, Bridges, LaneToBridge, LanesManagerOf}; + + use bp_messages::{target_chain::DispatchMessageData, LaneIdType, MessageKey}; + use bp_xcm_bridge_hub::{Bridge, BridgeLocations, BridgeState}; + use frame_support::assert_ok; + use pallet_bridge_messages::InboundLaneStorage; + use xcm_executor::traits::ConvertLocation; + + fn bridge() -> (Box, TestLaneIdType) { + let origin = OpenBridgeOrigin::sibling_parachain_origin(); + let with = bridged_asset_hub_universal_location(); + let locations = + XcmOverBridge::bridge_locations_from_origin(origin, Box::new(with.into())).unwrap(); + let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap(); + (locations, lane_id) + } + + fn run_test_with_opened_bridge(test: impl FnOnce()) { + run_test(|| { + let (bridge, lane_id) = bridge(); + + if !Bridges::::contains_key(bridge.bridge_id()) { + // insert bridge + Bridges::::insert( + bridge.bridge_id(), + Bridge { + bridge_origin_relative_location: Box::new( + bridge.bridge_origin_relative_location().clone().into(), + ), + bridge_origin_universal_location: Box::new( + bridge.bridge_origin_universal_location().clone().into(), + ), + bridge_destination_universal_location: Box::new( + bridge.bridge_destination_universal_location().clone().into(), + ), + state: BridgeState::Opened, + bridge_owner_account: LocationToAccountId::convert_location( + bridge.bridge_origin_relative_location(), + ) + .expect("valid accountId"), + deposit: 0, + lane_id, + }, + ); + LaneToBridge::::insert(lane_id, bridge.bridge_id()); + + // create lanes + let lanes_manager = LanesManagerOf::::new(); + if lanes_manager.create_inbound_lane(lane_id).is_ok() { + assert_eq!( + 0, + lanes_manager + .active_inbound_lane(lane_id) + .unwrap() + .storage() + .data() + .last_confirmed_nonce + ); + } + if lanes_manager.create_outbound_lane(lane_id).is_ok() { + assert!(lanes_manager + .active_outbound_lane(lane_id) + .unwrap() + .queued_messages() + .is_empty()); + } + } + assert_ok!(XcmOverBridge::do_try_state()); + + test(); + }); + } + + fn invalid_message() -> DispatchMessage, TestLaneIdType> { + DispatchMessage { + key: MessageKey { lane_id: TestLaneIdType::try_new(1, 2).unwrap(), nonce: 1 }, + data: DispatchMessageData { payload: Err(codec::Error::from("test")) }, + } + } + + fn valid_message() -> DispatchMessage, TestLaneIdType> { + DispatchMessage { + key: MessageKey { lane_id: TestLaneIdType::try_new(1, 2).unwrap(), nonce: 1 }, + data: DispatchMessageData { payload: Ok(vec![42]) }, + } + } + + #[test] + fn dispatcher_is_inactive_when_channel_with_target_chain_is_congested() { + run_test_with_opened_bridge(|| { + TestLocalXcmChannelManager::make_congested(); + assert!(!XcmOverBridge::is_active(bridge().1)); + }); + } + + #[test] + fn dispatcher_is_active_when_channel_with_target_chain_is_not_congested() { + run_test_with_opened_bridge(|| { + assert!(XcmOverBridge::is_active(bridge().1)); + }); + } + + #[test] + fn dispatch_weight_is_zero_if_we_have_failed_to_decode_message() { + run_test(|| { + assert_eq!(XcmOverBridge::dispatch_weight(&mut invalid_message()), Weight::zero()); + }); + } + + #[test] + fn dispatch_weight_is_non_zero_if_we_have_decoded_message() { + run_test(|| { + assert_ne!(XcmOverBridge::dispatch_weight(&mut valid_message()), Weight::zero()); + }); + } + + #[test] + fn message_is_not_dispatched_when_we_have_failed_to_decode_message() { + run_test(|| { + assert_eq!( + XcmOverBridge::dispatch(invalid_message()), + MessageDispatchResult { + unspent_weight: Weight::zero(), + dispatch_level_result: XcmBlobMessageDispatchResult::InvalidPayload, + }, + ); + assert!(!TestBlobDispatcher::is_dispatched()); + }); + } + + #[test] + fn message_is_dispatched_when_we_have_decoded_message() { + run_test(|| { + assert_eq!( + XcmOverBridge::dispatch(valid_message()), + MessageDispatchResult { + unspent_weight: Weight::zero(), + dispatch_level_result: XcmBlobMessageDispatchResult::Dispatched, + }, + ); + assert!(TestBlobDispatcher::is_dispatched()); + }); + } +} diff --git a/bridges/modules/xcm-bridge-hub/src/exporter.rs b/bridges/modules/xcm-bridge-hub/src/exporter.rs index 5bca3ad8e2777c2e9243867fa491922592482c9e..5afb9f36bc9414bde920eec3d3e84bc7487f711d 100644 --- a/bridges/modules/xcm-bridge-hub/src/exporter.rs +++ b/bridges/modules/xcm-bridge-hub/src/exporter.rs @@ -22,14 +22,29 @@ use crate::{Config, Pallet, LOG_TARGET}; -use bp_messages::source_chain::MessagesBridge; -use bp_xcm_bridge_hub::XcmAsPlainPayload; -use bridge_runtime_common::messages_xcm_extension::{LocalXcmQueueManager, SenderAndLane}; -use pallet_bridge_messages::{Config as BridgeMessagesConfig, Pallet as BridgeMessagesPallet}; +use crate::{BridgeOf, Bridges}; + +use bp_messages::{ + source_chain::{MessagesBridge, OnMessagesDelivered}, + MessageNonce, +}; +use bp_xcm_bridge_hub::{BridgeId, BridgeState, LocalXcmChannelManager, XcmAsPlainPayload}; +use frame_support::{ensure, traits::Get}; +use pallet_bridge_messages::{ + Config as BridgeMessagesConfig, Error, Pallet as BridgeMessagesPallet, +}; use xcm::prelude::*; use xcm_builder::{HaulBlob, HaulBlobError, HaulBlobExporter}; use xcm_executor::traits::ExportXcm; +/// Maximal number of messages in the outbound bridge queue. Once we reach this limit, we +/// suspend a bridge. +const OUTBOUND_LANE_CONGESTED_THRESHOLD: MessageNonce = 8_192; + +/// After we have suspended the bridge, we wait until number of messages in the outbound bridge +/// queue drops to this count, before sending resuming the bridge. +const OUTBOUND_LANE_UNCONGESTED_THRESHOLD: MessageNonce = 1_024; + /// An easy way to access `HaulBlobExporter`. pub type PalletAsHaulBlobExporter = HaulBlobExporter< DummyHaulBlob, @@ -45,8 +60,9 @@ where T: BridgeMessagesConfig, { type Ticket = ( - SenderAndLane, - as MessagesBridge>::SendMessageArgs, + BridgeId, + BridgeOf, + as MessagesBridge>::SendMessageArgs, XcmHash, ); @@ -57,12 +73,72 @@ where destination: &mut Option, message: &mut Option>, ) -> Result<(Self::Ticket, Assets), SendError> { - // Find supported lane_id. - let sender_and_lane = Self::lane_for( - universal_source.as_ref().ok_or(SendError::MissingArgument)?, - (&network, destination.as_ref().ok_or(SendError::MissingArgument)?), + log::trace!( + target: LOG_TARGET, + "Validate for network: {network:?}, channel: {channel:?}, universal_source: {universal_source:?}, destination: {destination:?}" + ); + + // `HaulBlobExporter` may consume the `universal_source` and `destination` arguments, so + // let's save them before + let bridge_origin_universal_location = + universal_source.clone().take().ok_or(SendError::MissingArgument)?; + // Note: watch out this is `ExportMessage::destination`, which is relative to the `network`, + // which means it does not contain `GlobalConsensus`, We need to find `BridgeId` with + // `Self::bridge_locations` which requires **universal** location for destination. + let bridge_destination_universal_location = { + let dest = destination.clone().take().ok_or(SendError::MissingArgument)?; + match dest.global_consensus() { + Ok(dest_network) => { + log::trace!( + target: LOG_TARGET, + "Destination: {dest:?} is already universal, checking dest_network: {dest_network:?} and network: {network:?} if matches: {:?}", + dest_network == network + ); + ensure!(dest_network == network, SendError::NotApplicable); + // ok, `dest` looks like a universal location, so let's use it + dest + }, + Err(_) => { + // `dest` is not a universal location, so we need to prepend it with + // `GlobalConsensus`. + dest.pushed_front_with(GlobalConsensus(network)).map_err(|error_data| { + log::error!( + target: LOG_TARGET, + "Destination: {:?} is not a universal and prepending with {:?} failed!", + error_data.0, + error_data.1, + ); + SendError::NotApplicable + })? + }, + } + }; + + // prepare the origin relative location + let bridge_origin_relative_location = + bridge_origin_universal_location.relative_to(&T::UniversalLocation::get()); + + // then we are able to compute the `BridgeId` and find `LaneId` used to send messages + let locations = Self::bridge_locations( + bridge_origin_relative_location, + bridge_destination_universal_location.into(), ) - .ok_or(SendError::NotApplicable)?; + .map_err(|e| { + log::error!( + target: LOG_TARGET, + "Validate `bridge_locations` with error: {e:?}", + ); + SendError::NotApplicable + })?; + let bridge = Self::bridge(locations.bridge_id()).ok_or_else(|| { + log::error!( + target: LOG_TARGET, + "No opened bridge for requested bridge_origin_relative_location: {:?} and bridge_destination_universal_location: {:?}", + locations.bridge_origin_relative_location(), + locations.bridge_destination_universal_location(), + ); + SendError::NotApplicable + })?; // check if we are able to route the message. We use existing `HaulBlobExporter` for that. // It will make all required changes and will encode message properly, so that the @@ -75,43 +151,197 @@ where message, )?; - let bridge_message = MessagesPallet::::validate_message(sender_and_lane.lane, &blob) + let bridge_message = MessagesPallet::::validate_message(bridge.lane_id, &blob) .map_err(|e| { - log::debug!( + match e { + Error::LanesManager(ref ei) => + log::error!(target: LOG_TARGET, "LanesManager: {ei:?}"), + Error::MessageRejectedByPallet(ref ei) => + log::error!(target: LOG_TARGET, "MessageRejectedByPallet: {ei:?}"), + Error::ReceptionConfirmation(ref ei) => + log::error!(target: LOG_TARGET, "ReceptionConfirmation: {ei:?}"), + _ => (), + }; + + log::error!( target: LOG_TARGET, - "XCM message {:?} cannot be exported because of bridge error {:?} on bridge {:?}", + "XCM message {:?} cannot be exported because of bridge error: {:?} on bridge {:?} and laneId: {:?}", id, e, - sender_and_lane.lane, + locations, + bridge.lane_id, ); SendError::Transport("BridgeValidateError") })?; - Ok(((sender_and_lane, bridge_message, id), price)) + Ok(((*locations.bridge_id(), bridge, bridge_message, id), price)) } - fn deliver((sender_and_lane, bridge_message, id): Self::Ticket) -> Result { - let lane_id = sender_and_lane.lane; + fn deliver( + (bridge_id, bridge, bridge_message, id): Self::Ticket, + ) -> Result { let artifacts = MessagesPallet::::send_message(bridge_message); log::info!( target: LOG_TARGET, - "XCM message {:?} has been enqueued at bridge {:?} with nonce {}", + "XCM message {:?} has been enqueued at bridge {:?} and lane_id: {:?} with nonce {}", id, - lane_id, + bridge_id, + bridge.lane_id, artifacts.nonce, ); - // notify XCM queue manager about updated lane state - LocalXcmQueueManager::::on_bridge_message_enqueued( - &sender_and_lane, - artifacts.enqueued_messages, - ); + // maybe we need switch to congested state + Self::on_bridge_message_enqueued(bridge_id, bridge, artifacts.enqueued_messages); Ok(id) } } +impl, I: 'static> OnMessagesDelivered for Pallet { + fn on_messages_delivered(lane_id: T::LaneId, enqueued_messages: MessageNonce) { + Self::on_bridge_messages_delivered(lane_id, enqueued_messages); + } +} + +impl, I: 'static> Pallet { + /// Called when new message is pushed onto outbound bridge queue. + fn on_bridge_message_enqueued( + bridge_id: BridgeId, + bridge: BridgeOf, + enqueued_messages: MessageNonce, + ) { + // if the bridge queue is not congested, we don't want to do anything + let is_congested = enqueued_messages > OUTBOUND_LANE_CONGESTED_THRESHOLD; + if !is_congested { + return + } + + // TODO: https://github.com/paritytech/parity-bridges-common/issues/2006 we either need fishermens + // to watch this rule violation (suspended, but keep sending new messages), or we need a + // hard limit for that like other XCM queues have + + // check if the lane is already suspended. If it is, do nothing. We still accept new + // messages to the suspended bridge, hoping that it'll be actually resumed soon + if bridge.state == BridgeState::Suspended { + return + } + + // else - suspend the bridge + let bridge_origin_relative_location = match bridge.bridge_origin_relative_location.try_as() + { + Ok(bridge_origin_relative_location) => bridge_origin_relative_location, + Err(_) => { + log::debug!( + target: LOG_TARGET, + "Failed to convert the bridge {:?} origin location {:?}", + bridge_id, + bridge.bridge_origin_relative_location, + ); + + return + }, + }; + let suspend_result = + T::LocalXcmChannelManager::suspend_bridge(bridge_origin_relative_location, bridge_id); + match suspend_result { + Ok(_) => { + log::debug!( + target: LOG_TARGET, + "Suspended the bridge {:?}, originated by the {:?}", + bridge_id, + bridge.bridge_origin_relative_location, + ); + }, + Err(e) => { + log::debug!( + target: LOG_TARGET, + "Failed to suspended the bridge {:?}, originated by the {:?}: {:?}", + bridge_id, + bridge.bridge_origin_relative_location, + e, + ); + + return + }, + } + + // and remember that we have suspended the bridge + Bridges::::mutate_extant(bridge_id, |bridge| { + bridge.state = BridgeState::Suspended; + }); + } + + /// Must be called whenever we receive a message delivery confirmation. + fn on_bridge_messages_delivered(lane_id: T::LaneId, enqueued_messages: MessageNonce) { + // if the bridge queue is still congested, we don't want to do anything + let is_congested = enqueued_messages > OUTBOUND_LANE_UNCONGESTED_THRESHOLD; + if is_congested { + return + } + + // if we have not suspended the bridge before (or it is closed), we don't want to do + // anything + let (bridge_id, bridge) = match Self::bridge_by_lane_id(&lane_id) { + Some(bridge) if bridge.1.state == BridgeState::Suspended => bridge, + _ => { + // if there is no bridge or it has been closed, then we don't need to send resume + // signal to the local origin - it has closed bridge itself, so it should have + // alrady pruned everything else + return + }, + }; + + // else - resume the bridge + let bridge_origin_relative_location = (*bridge.bridge_origin_relative_location).try_into(); + let bridge_origin_relative_location = match bridge_origin_relative_location { + Ok(bridge_origin_relative_location) => bridge_origin_relative_location, + Err(e) => { + log::debug!( + target: LOG_TARGET, + "Failed to convert the bridge {:?} location for lane_id: {:?}, error {:?}", + bridge_id, + lane_id, + e, + ); + + return + }, + }; + + let resume_result = + T::LocalXcmChannelManager::resume_bridge(&bridge_origin_relative_location, bridge_id); + match resume_result { + Ok(_) => { + log::debug!( + target: LOG_TARGET, + "Resumed the bridge {:?} and lane_id: {:?}, originated by the {:?}", + bridge_id, + lane_id, + bridge_origin_relative_location, + ); + }, + Err(e) => { + log::debug!( + target: LOG_TARGET, + "Failed to resume the bridge {:?} and lane_id: {:?}, originated by the {:?}: {:?}", + bridge_id, + lane_id, + bridge_origin_relative_location, + e, + ); + + return + }, + } + + // and forget that we have previously suspended the bridge + Bridges::::mutate_extant(bridge_id, |bridge| { + bridge.state = BridgeState::Opened; + }); + } +} + /// Dummy implementation of the `HaulBlob` trait that is never called. /// /// We are using `HaulBlobExporter`, which requires `HaulBlob` implementation. It assumes that @@ -130,30 +360,203 @@ impl HaulBlob for DummyHaulBlob { #[cfg(test)] mod tests { use super::*; - use crate::mock::*; + use crate::{mock::*, Bridges, LaneToBridge, LanesManagerOf}; + use bp_runtime::RangeInclusiveExt; + use bp_xcm_bridge_hub::{Bridge, BridgeLocations, BridgeState}; use frame_support::assert_ok; - use xcm_executor::traits::export_xcm; + use pallet_bridge_messages::InboundLaneStorage; + use xcm_builder::{NetworkExportTable, UnpaidRemoteExporter}; + use xcm_executor::traits::{export_xcm, ConvertLocation}; fn universal_source() -> InteriorLocation { - [GlobalConsensus(RelayNetwork::get()), Parachain(SIBLING_ASSET_HUB_ID)].into() + SiblingUniversalLocation::get() } fn bridged_relative_destination() -> InteriorLocation { BridgedRelativeDestination::get() } + fn bridged_universal_destination() -> InteriorLocation { + BridgedUniversalDestination::get() + } + + fn open_lane() -> (BridgeLocations, TestLaneIdType) { + // open expected outbound lane + let origin = OpenBridgeOrigin::sibling_parachain_origin(); + let with = bridged_asset_hub_universal_location(); + let locations = + XcmOverBridge::bridge_locations_from_origin(origin, Box::new(with.into())).unwrap(); + let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap(); + + if !Bridges::::contains_key(locations.bridge_id()) { + // insert bridge + Bridges::::insert( + locations.bridge_id(), + Bridge { + bridge_origin_relative_location: Box::new(SiblingLocation::get().into()), + bridge_origin_universal_location: Box::new( + locations.bridge_origin_universal_location().clone().into(), + ), + bridge_destination_universal_location: Box::new( + locations.bridge_destination_universal_location().clone().into(), + ), + state: BridgeState::Opened, + bridge_owner_account: LocationToAccountId::convert_location( + locations.bridge_origin_relative_location(), + ) + .expect("valid accountId"), + deposit: 0, + lane_id, + }, + ); + LaneToBridge::::insert(lane_id, locations.bridge_id()); + + // create lanes + let lanes_manager = LanesManagerOf::::new(); + if lanes_manager.create_inbound_lane(lane_id).is_ok() { + assert_eq!( + 0, + lanes_manager + .active_inbound_lane(lane_id) + .unwrap() + .storage() + .data() + .last_confirmed_nonce + ); + } + if lanes_manager.create_outbound_lane(lane_id).is_ok() { + assert!(lanes_manager + .active_outbound_lane(lane_id) + .unwrap() + .queued_messages() + .is_empty()); + } + } + assert_ok!(XcmOverBridge::do_try_state()); + + (*locations, lane_id) + } + + fn open_lane_and_send_regular_message() -> (BridgeId, TestLaneIdType) { + let (locations, lane_id) = open_lane(); + + // now let's try to enqueue message using our `ExportXcm` implementation + export_xcm::( + BridgedRelayNetwork::get(), + 0, + locations.bridge_origin_universal_location().clone(), + locations.bridge_destination_universal_location().clone(), + vec![Instruction::ClearOrigin].into(), + ) + .unwrap(); + + (*locations.bridge_id(), lane_id) + } + #[test] - fn export_works() { + fn exporter_works() { run_test(|| { - assert_ok!(export_xcm::( - BridgedRelayNetwork::get(), - 0, - universal_source(), - bridged_relative_destination(), - vec![Instruction::ClearOrigin].into(), - )); - }) + let (_, lane_id) = open_lane_and_send_regular_message(); + + // double check that the message has been pushed to the expected lane + // (it should already been checked during `send_message` call) + assert!(!LanesManagerOf::::new() + .active_outbound_lane(lane_id) + .unwrap() + .queued_messages() + .is_empty()); + }); + } + + #[test] + fn exporter_does_not_suspend_the_bridge_if_outbound_bridge_queue_is_not_congested() { + run_test(|| { + let (bridge_id, _) = open_lane_and_send_regular_message(); + assert!(!TestLocalXcmChannelManager::is_bridge_suspened()); + assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Opened); + }); + } + + #[test] + fn exporter_does_not_suspend_the_bridge_if_it_is_already_suspended() { + run_test(|| { + let (bridge_id, _) = open_lane_and_send_regular_message(); + Bridges::::mutate_extant(bridge_id, |bridge| { + bridge.state = BridgeState::Suspended; + }); + for _ in 1..OUTBOUND_LANE_CONGESTED_THRESHOLD { + open_lane_and_send_regular_message(); + } + + open_lane_and_send_regular_message(); + assert!(!TestLocalXcmChannelManager::is_bridge_suspened()); + }); + } + + #[test] + fn exporter_suspends_the_bridge_if_outbound_bridge_queue_is_congested() { + run_test(|| { + let (bridge_id, _) = open_lane_and_send_regular_message(); + for _ in 1..OUTBOUND_LANE_CONGESTED_THRESHOLD { + open_lane_and_send_regular_message(); + } + + assert!(!TestLocalXcmChannelManager::is_bridge_suspened()); + assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Opened); + + open_lane_and_send_regular_message(); + assert!(TestLocalXcmChannelManager::is_bridge_suspened()); + assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Suspended); + }); + } + + #[test] + fn bridge_is_not_resumed_if_outbound_bridge_queue_is_still_congested() { + run_test(|| { + let (bridge_id, lane_id) = open_lane_and_send_regular_message(); + Bridges::::mutate_extant(bridge_id, |bridge| { + bridge.state = BridgeState::Suspended; + }); + XcmOverBridge::on_bridge_messages_delivered( + lane_id, + OUTBOUND_LANE_UNCONGESTED_THRESHOLD + 1, + ); + + assert!(!TestLocalXcmChannelManager::is_bridge_resumed()); + assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Suspended); + }); + } + + #[test] + fn bridge_is_not_resumed_if_it_was_not_suspended_before() { + run_test(|| { + let (bridge_id, lane_id) = open_lane_and_send_regular_message(); + XcmOverBridge::on_bridge_messages_delivered( + lane_id, + OUTBOUND_LANE_UNCONGESTED_THRESHOLD, + ); + + assert!(!TestLocalXcmChannelManager::is_bridge_resumed()); + assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Opened); + }); + } + + #[test] + fn bridge_is_resumed_when_enough_messages_are_delivered() { + run_test(|| { + let (bridge_id, lane_id) = open_lane_and_send_regular_message(); + Bridges::::mutate_extant(bridge_id, |bridge| { + bridge.state = BridgeState::Suspended; + }); + XcmOverBridge::on_bridge_messages_delivered( + lane_id, + OUTBOUND_LANE_UNCONGESTED_THRESHOLD, + ); + + assert!(TestLocalXcmChannelManager::is_bridge_resumed()); + assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Opened); + }); } #[test] @@ -186,23 +589,56 @@ mod tests { #[test] fn exporter_computes_correct_lane_id() { run_test(|| { - let expected_lane_id = TEST_LANE_ID; + assert_ne!(bridged_universal_destination(), bridged_relative_destination()); - assert_eq!( - XcmOverBridge::validate( - BridgedRelayNetwork::get(), - 0, - &mut Some(universal_source()), - &mut Some(bridged_relative_destination()), - &mut Some(Vec::new().into()), - ) - .unwrap() - .0 - .0 - .lane, - expected_lane_id, - ); - }) + let locations = BridgeLocations::bridge_locations( + UniversalLocation::get(), + SiblingLocation::get(), + bridged_universal_destination(), + BridgedRelayNetwork::get(), + ) + .unwrap(); + let expected_bridge_id = locations.bridge_id(); + let expected_lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap(); + + if LanesManagerOf::::new() + .create_outbound_lane(expected_lane_id) + .is_ok() + { + Bridges::::insert( + expected_bridge_id, + Bridge { + bridge_origin_relative_location: Box::new( + locations.bridge_origin_relative_location().clone().into(), + ), + bridge_origin_universal_location: Box::new( + locations.bridge_origin_universal_location().clone().into(), + ), + bridge_destination_universal_location: Box::new( + locations.bridge_destination_universal_location().clone().into(), + ), + state: BridgeState::Opened, + bridge_owner_account: [0u8; 32].into(), + deposit: 0, + lane_id: expected_lane_id, + }, + ); + } + + let ticket = XcmOverBridge::validate( + BridgedRelayNetwork::get(), + 0, + &mut Some(universal_source()), + // Note: The `ExportMessage` expects relative `InteriorLocation` in the + // `BridgedRelayNetwork`. + &mut Some(bridged_relative_destination()), + &mut Some(Vec::new().into()), + ) + .unwrap() + .0; + assert_eq!(&ticket.0, expected_bridge_id); + assert_eq!(ticket.1.lane_id, expected_lane_id); + }); } #[test] @@ -210,31 +646,138 @@ mod tests { run_test(|| { // valid routable destination let dest = Location::new(2, BridgedUniversalDestination::get()); - let expected_lane_id = TEST_LANE_ID; + + // open bridge + let (_, expected_lane_id) = open_lane(); // check before - no messages assert_eq!( pallet_bridge_messages::Pallet::::outbound_lane_data( expected_lane_id ) + .unwrap() .queued_messages() .saturating_len(), 0 ); + // send `ExportMessage(message)` by `UnpaidRemoteExporter`. + TestExportXcmWithXcmOverBridge::set_origin_for_execute(SiblingLocation::get()); + assert_ok!(send_xcm::< + UnpaidRemoteExporter< + NetworkExportTable, + TestExportXcmWithXcmOverBridge, + UniversalLocation, + >, + >(dest.clone(), Xcm::<()>::default())); + // send `ExportMessage(message)` by `pallet_xcm_bridge_hub_router`. TestExportXcmWithXcmOverBridge::set_origin_for_execute(SiblingLocation::get()); - assert_ok!(send_xcm::(dest, Xcm::<()>::default())); + assert_ok!(send_xcm::(dest.clone(), Xcm::<()>::default())); // check after - a message ready to be relayed assert_eq!( pallet_bridge_messages::Pallet::::outbound_lane_data( expected_lane_id ) + .unwrap() .queued_messages() .saturating_len(), - 1 + 2 ); }) } + + #[test] + fn validate_works() { + run_test(|| { + let xcm: Xcm<()> = vec![ClearOrigin].into(); + + // check that router does not consume when `NotApplicable` + let mut xcm_wrapper = Some(xcm.clone()); + let mut universal_source_wrapper = Some(universal_source()); + + // wrong `NetworkId` + let mut dest_wrapper = Some(bridged_relative_destination()); + assert_eq!( + XcmOverBridge::validate( + NetworkId::ByGenesis([0; 32]), + 0, + &mut universal_source_wrapper, + &mut dest_wrapper, + &mut xcm_wrapper, + ), + Err(SendError::NotApplicable), + ); + // dest and xcm is NOT consumed and untouched + assert_eq!(&Some(xcm.clone()), &xcm_wrapper); + assert_eq!(&Some(universal_source()), &universal_source_wrapper); + assert_eq!(&Some(bridged_relative_destination()), &dest_wrapper); + + // dest starts with wrong `NetworkId` + let mut invalid_dest_wrapper = Some( + [GlobalConsensus(NetworkId::ByGenesis([0; 32])), Parachain(BRIDGED_ASSET_HUB_ID)] + .into(), + ); + assert_eq!( + XcmOverBridge::validate( + BridgedRelayNetwork::get(), + 0, + &mut Some(universal_source()), + &mut invalid_dest_wrapper, + &mut xcm_wrapper, + ), + Err(SendError::NotApplicable), + ); + // dest and xcm is NOT consumed and untouched + assert_eq!(&Some(xcm.clone()), &xcm_wrapper); + assert_eq!(&Some(universal_source()), &universal_source_wrapper); + assert_eq!( + &Some( + [ + GlobalConsensus(NetworkId::ByGenesis([0; 32]),), + Parachain(BRIDGED_ASSET_HUB_ID) + ] + .into() + ), + &invalid_dest_wrapper + ); + + // no opened lane for dest + let mut dest_without_lane_wrapper = + Some([GlobalConsensus(BridgedRelayNetwork::get()), Parachain(5679)].into()); + assert_eq!( + XcmOverBridge::validate( + BridgedRelayNetwork::get(), + 0, + &mut Some(universal_source()), + &mut dest_without_lane_wrapper, + &mut xcm_wrapper, + ), + Err(SendError::NotApplicable), + ); + // dest and xcm is NOT consumed and untouched + assert_eq!(&Some(xcm.clone()), &xcm_wrapper); + assert_eq!(&Some(universal_source()), &universal_source_wrapper); + assert_eq!( + &Some([GlobalConsensus(BridgedRelayNetwork::get(),), Parachain(5679)].into()), + &dest_without_lane_wrapper + ); + + // ok + let _ = open_lane(); + let mut dest_wrapper = Some(bridged_relative_destination()); + assert_ok!(XcmOverBridge::validate( + BridgedRelayNetwork::get(), + 0, + &mut Some(universal_source()), + &mut dest_wrapper, + &mut xcm_wrapper, + )); + // dest and xcm IS consumed + assert_eq!(None, xcm_wrapper); + assert_eq!(&Some(universal_source()), &universal_source_wrapper); + assert_eq!(None, dest_wrapper); + }); + } } diff --git a/bridges/modules/xcm-bridge-hub/src/lib.rs b/bridges/modules/xcm-bridge-hub/src/lib.rs index 60b988497fc59e94cbfe1a6e30cd6f3039d8c331..1b2536598a202892052d395284d88c682448c801 100644 --- a/bridges/modules/xcm-bridge-hub/src/lib.rs +++ b/bridges/modules/xcm-bridge-hub/src/lib.rs @@ -14,19 +14,156 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . -//! Module that adds XCM support to bridge pallets. +//! Module that adds XCM support to bridge pallets. The pallet allows to dynamically +//! open and close bridges between local (to this pallet location) and remote XCM +//! destinations. +//! +//! The `pallet_xcm_bridge_hub` pallet is used to manage (open, close) bridges between chains from +//! different consensuses. The new extrinsics `fn open_bridge` and `fn close_bridge` are introduced. +//! Other chains can manage channels with different bridged global consensuses. +//! +//! # Concept of `lane` and `LaneId` +//! +//! There is another `pallet_bridge_messages` pallet that handles inbound/outbound lanes for +//! messages. Each lane is a unique connection between two chains from different consensuses and is +//! identified by `LaneId`. `LaneId` is generated once when a new bridge is requested by `fn +//! open_bridge`. It is generated by `BridgeLocations::calculate_lane_id` based on the following +//! parameters: +//! - Source `bridge_origin_universal_location` (latest XCM) +//! - Destination `bridge_destination_universal_location` (latest XCM) +//! - XCM version (both sides of the bridge must use the same parameters to generate the same +//! `LaneId`) +//! - `bridge_origin_universal_location`, `bridge_destination_universal_location` is converted to +//! the `Versioned*` structs +//! +//! `LaneId` is expected to never change because: +//! - We need the same `LaneId` on both sides of the bridge, as `LaneId` is part of the message key +//! proofs. +//! - Runtime upgrades are entirely asynchronous. +//! - We already have a running production Polkadot/Kusama bridge that uses `LaneId([0, 0, 0, 0])`. +//! +//! `LaneId` is backward compatible, meaning it can be encoded/decoded from the older format `[u8; +//! 4]` used for static lanes, as well as the new format `H256` generated by +//! `BridgeLocations::calculate_lane_id`. +//! +//! # Concept of `bridge` and `BridgeId` +//! +//! The `pallet_xcm_bridge_hub` pallet needs to store some metadata about opened bridges. The bridge +//! (or bridge metadata) is stored under the `BridgeId` key. +//! +//! `BridgeId` is generated from `bridge_origin_relative_location` and +//! `bridge_origin_universal_location` using the `latest` XCM structs. `BridgeId` is not transferred +//! over the bridge; it is only important for local consensus. It essentially serves as an index/key +//! to bridge metadata. All the XCM infrastructure around `XcmExecutor`, `SendXcm`, `ExportXcm` use +//! the `latest` XCM, so `BridgeId` must remain compatible with the `latest` XCM. For example, we +//! have an `ExportXcm` implementation in `exporter.rs` that handles the `ExportMessage` instruction +//! with `universal_source` and `destination` (latest XCM), so we need to create `BridgeId` and the +//! corresponding `LaneId`. +//! +//! # Migrations and State +//! +//! This pallet implements `try_state`, ensuring compatibility and checking everything so we know if +//! any migration is needed. `do_try_state` checks for `BridgeId` compatibility, which is +//! recalculated on runtime upgrade. Upgrading to a new XCM version should not break anything, +//! except removing older XCM versions. In such cases, we need to add migration for `BridgeId` and +//! stored `Versioned*` structs and update `LaneToBridge` mapping, but this won't affect `LaneId` +//! over the bridge. +//! +//! # How to Open a Bridge? +//! +//! The `pallet_xcm_bridge_hub` pallet has the extrinsic `fn open_bridge` and an important +//! configuration `pallet_xcm_bridge_hub::Config::OpenBridgeOrigin`, which translates the call's +//! origin to the XCM `Location` and converts it to the `bridge_origin_universal_location`. With the +//! current setup, this origin/location is expected to be either the relay chain or a sibling +//! parachain as one side of the bridge. Another parameter is +//! `bridge_destination_universal_location`, which is the other side of the bridge from a different +//! global consensus. +//! +//! Every bridge between two XCM locations has a dedicated lane in associated +//! messages pallet. Assuming that this pallet is deployed at the bridge hub +//! parachain and there's a similar pallet at the bridged network, the dynamic +//! bridge lifetime is as follows: +//! +//! 1) the sibling parachain opens a XCMP channel with this bridge hub; +//! +//! 2) the sibling parachain funds its sovereign parachain account at this bridge hub. It shall hold +//! enough funds to pay for the bridge (see `BridgeDeposit`); +//! +//! 3) the sibling parachain opens the bridge by sending XCM `Transact` instruction with the +//! `open_bridge` call. The `BridgeDeposit` amount is reserved on the sovereign account of +//! sibling parachain; +//! +//! 4) at the other side of the bridge, the same thing (1, 2, 3) happens. Parachains that need to +//! connect over the bridge need to coordinate the moment when they start sending messages over +//! the bridge. Otherwise they may lose messages and/or bundled assets; +//! +//! 5) when either side wants to close the bridge, it sends the XCM `Transact` with the +//! `close_bridge` call. The bridge is closed immediately if there are no queued messages. +//! Otherwise, the owner must repeat the `close_bridge` call to prune all queued messages first. +//! +//! The pallet doesn't provide any mechanism for graceful closure, because it always involves +//! some contract between two connected chains and the bridge hub knows nothing about that. It +//! is the task for the connected chains to make sure that all required actions are completed +//! before the closure. In the end, the bridge hub can't even guarantee that all messages that +//! are delivered to the destination, are processed in the way their sender expects. So if we +//! can't guarantee that, we shall not care about more complex procedures and leave it to the +//! participating parties. +//! +//! # Example +//! +//! Example of opening a bridge between some random parachains from Polkadot and Kusama: +//! +//! 0. Let's have: +//! - BridgeHubPolkadot with `UniversalLocation` = `[GlobalConsensus(Polkadot), Parachain(1002)]` +//! - BridgeHubKusama with `UniversalLocation` = `[GlobalConsensus(Kusama), Parachain(1002)]` +//! 1. The Polkadot local sibling parachain `Location::new(1, Parachain(1234))` must send some DOTs +//! to its sovereign account on BridgeHubPolkadot to cover `BridgeDeposit`, fees for `Transact`, +//! and the existential deposit. +//! 2. Send a call to the BridgeHubPolkadot from the local sibling parachain: `Location::new(1, +//! Parachain(1234))` ``` xcm::Transact( origin_kind: OriginKind::Xcm, +//! XcmOverBridgeHubKusama::open_bridge( VersionedInteriorLocation::V4([GlobalConsensus(Kusama), +//! Parachain(4567)].into()), ); ) ``` +//! 3. Check the stored bridge metadata and generated `LaneId`. +//! 4. The Kusama local sibling parachain `Location::new(1, Parachain(4567))` must send some KSMs to +//! its sovereign account +//! on BridgeHubKusama to cover `BridgeDeposit`, fees for `Transact`, and the existential deposit. +//! 5. Send a call to the BridgeHubKusama from the local sibling parachain: `Location::new(1, +//! Parachain(4567))` ``` xcm::Transact( origin_kind: OriginKind::Xcm, +//! XcmOverBridgeHubKusama::open_bridge( +//! VersionedInteriorLocation::V4([GlobalConsensus(Polkadot), Parachain(1234)].into()), ); ) ``` +//! 6. Check the stored bridge metadata and generated `LaneId`. +//! 7. Both `LaneId`s from steps 3 and 6 must be the same (see above _Concept of `lane` and +//! `LaneId`_). +//! 8. Run the bridge messages relayer for `LaneId`. +//! 9. Send messages from both sides. +//! +//! The opening bridge holds the configured `BridgeDeposit` from the origin's sovereign account, but +//! this deposit is returned when the bridge is closed with `fn close_bridge`. #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] -use bridge_runtime_common::messages_xcm_extension::XcmBlobHauler; -use pallet_bridge_messages::Config as BridgeMessagesConfig; +use bp_messages::{LaneState, MessageNonce}; +use bp_runtime::{AccountIdOf, BalanceOf, RangeInclusiveExt}; +pub use bp_xcm_bridge_hub::{Bridge, BridgeId, BridgeState}; +use bp_xcm_bridge_hub::{BridgeLocations, BridgeLocationsError, LocalXcmChannelManager}; +use frame_support::{traits::fungible::MutateHold, DefaultNoBound}; +use frame_system::Config as SystemConfig; +use pallet_bridge_messages::{Config as BridgeMessagesConfig, LanesManagerError}; +use sp_runtime::traits::Zero; +use sp_std::{boxed::Box, vec::Vec}; use xcm::prelude::*; +use xcm_builder::DispatchBlob; +use xcm_executor::traits::ConvertLocation; +pub use bp_xcm_bridge_hub::XcmAsPlainPayload; +pub use dispatcher::XcmBlobMessageDispatchResult; pub use exporter::PalletAsHaulBlobExporter; pub use pallet::*; +mod dispatcher; mod exporter; +pub mod migration; mod mock; /// The target that will be used when publishing logs related to this pallet. @@ -35,15 +172,29 @@ pub const LOG_TARGET: &str = "runtime::bridge-xcm"; #[frame_support::pallet] pub mod pallet { use super::*; - use bridge_runtime_common::messages_xcm_extension::SenderAndLane; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::BlockNumberFor; + use frame_support::{ + pallet_prelude::*, + traits::{tokens::Precision, Contains}, + }; + use frame_system::pallet_prelude::{BlockNumberFor, *}; + + /// The reason for this pallet placing a hold on funds. + #[pallet::composite_enum] + pub enum HoldReason { + /// The funds are held as a deposit for opened bridge. + #[codec(index = 0)] + BridgeDeposit, + } #[pallet::config] #[pallet::disable_frame_system_supertrait_check] pub trait Config: BridgeMessagesConfig { + /// The overarching event type. + type RuntimeEvent: From> + + IsType<::RuntimeEvent>; + /// Runtime's universal location. type UniversalLocation: Get; // TODO: https://github.com/paritytech/parity-bridges-common/issues/1666 remove `ChainId` and @@ -56,63 +207,1495 @@ pub mod pallet { /// `BridgedNetworkId` consensus. type BridgeMessagesPalletInstance: 'static; - /// Price of single message export to the bridged consensus (`Self::BridgedNetworkId`). + /// Price of single message export to the bridged consensus (`Self::BridgedNetwork`). type MessageExportPrice: Get; /// Checks the XCM version for the destination. type DestinationVersion: GetVersion; - /// Get point-to-point links with bridged consensus (`Self::BridgedNetworkId`). - /// (this will be replaced with dynamic on-chain bridges - `Bridges V2`) - type Lanes: Get>; - /// Support for point-to-point links - /// (this will be replaced with dynamic on-chain bridges - `Bridges V2`) - type LanesSupport: XcmBlobHauler; + /// The origin that is allowed to call privileged operations on the pallet, e.g. open/close + /// bridge for locations. + type ForceOrigin: EnsureOrigin<::RuntimeOrigin>; + /// A set of XCM locations within local consensus system that are allowed to open + /// bridges with remote destinations. + type OpenBridgeOrigin: EnsureOrigin< + ::RuntimeOrigin, + Success = Location, + >; + /// A converter between a location and a sovereign account. + type BridgeOriginAccountIdConverter: ConvertLocation>>; + + /// Amount of this chain native tokens that is reserved on the sibling parachain account + /// when bridge open request is registered. + #[pallet::constant] + type BridgeDeposit: Get>>; + /// Currency used to pay for bridge registration. + type Currency: MutateHold< + AccountIdOf>, + Balance = BalanceOf>, + Reason = Self::RuntimeHoldReason, + >; + /// The overarching runtime hold reason. + type RuntimeHoldReason: From>; + /// Do not hold `Self::BridgeDeposit` for the location of `Self::OpenBridgeOrigin`. + /// For example, it is possible to make an exception for a system parachain or relay. + type AllowWithoutBridgeDeposit: Contains; + + /// Local XCM channel manager. + type LocalXcmChannelManager: LocalXcmChannelManager; + /// XCM-level dispatcher for inbound bridge messages. + type BlobDispatcher: DispatchBlob; } + /// An alias for the bridge metadata. + pub type BridgeOf = Bridge, LaneIdOf>; + /// An alias for this chain. + pub type ThisChainOf = + pallet_bridge_messages::ThisChainOf>::BridgeMessagesPalletInstance>; + /// An alias for lane identifier type. + pub type LaneIdOf = + >::BridgeMessagesPalletInstance>>::LaneId; + /// An alias for the associated lanes manager. + pub type LanesManagerOf = + pallet_bridge_messages::LanesManager>::BridgeMessagesPalletInstance>; + #[pallet::pallet] + #[pallet::storage_version(migration::STORAGE_VERSION)] pub struct Pallet(PhantomData<(T, I)>); #[pallet::hooks] impl, I: 'static> Hooks> for Pallet { fn integrity_test() { assert!( - Self::bridged_network_id().is_some(), + Self::bridged_network_id().is_ok(), "Configured `T::BridgedNetwork`: {:?} does not contain `GlobalConsensus` junction with `NetworkId`", T::BridgedNetwork::get() ) } + + #[cfg(feature = "try-runtime")] + fn try_state(_n: BlockNumberFor) -> Result<(), sp_runtime::TryRuntimeError> { + Self::do_try_state() + } + } + + #[pallet::call] + impl, I: 'static> Pallet { + /// Open a bridge between two locations. + /// + /// The caller must be within the `T::OpenBridgeOrigin` filter (presumably: a sibling + /// parachain or a parent relay chain). The `bridge_destination_universal_location` must be + /// a destination within the consensus of the `T::BridgedNetwork` network. + /// + /// The `BridgeDeposit` amount is reserved on the caller account. This deposit + /// is unreserved after bridge is closed. + /// + /// The states after this call: bridge is `Opened`, outbound lane is `Opened`, inbound lane + /// is `Opened`. + #[pallet::call_index(0)] + #[pallet::weight(Weight::zero())] // TODO:(bridges-v2) - https://github.com/paritytech/parity-bridges-common/issues/3046 - add benchmarks impl + pub fn open_bridge( + origin: OriginFor, + bridge_destination_universal_location: Box, + ) -> DispatchResult { + // check and compute required bridge locations and laneId + let xcm_version = bridge_destination_universal_location.identify_version(); + let locations = + Self::bridge_locations_from_origin(origin, bridge_destination_universal_location)?; + let lane_id = locations.calculate_lane_id(xcm_version).map_err(|e| { + log::trace!( + target: LOG_TARGET, + "calculate_lane_id error: {e:?}", + ); + Error::::BridgeLocations(e) + })?; + + Self::do_open_bridge(locations, lane_id, true) + } + + /// Try to close the bridge. + /// + /// Can only be called by the "owner" of this side of the bridge, meaning that the + /// inbound XCM channel with the local origin chain is working. + /// + /// Closed bridge is a bridge without any traces in the runtime storage. So this method + /// first tries to prune all queued messages at the outbound lane. When there are no + /// outbound messages left, outbound and inbound lanes are purged. After that, funds + /// are returned back to the owner of this side of the bridge. + /// + /// The number of messages that we may prune in a single call is limited by the + /// `may_prune_messages` argument. If there are more messages in the queue, the method + /// prunes exactly `may_prune_messages` and exits early. The caller may call it again + /// until outbound queue is depleted and get his funds back. + /// + /// The states after this call: everything is either `Closed`, or purged from the + /// runtime storage. + #[pallet::call_index(1)] + #[pallet::weight(Weight::zero())] // TODO:(bridges-v2) - https://github.com/paritytech/parity-bridges-common/issues/3046 - add benchmarks impl + pub fn close_bridge( + origin: OriginFor, + bridge_destination_universal_location: Box, + may_prune_messages: MessageNonce, + ) -> DispatchResult { + // compute required bridge locations + let locations = + Self::bridge_locations_from_origin(origin, bridge_destination_universal_location)?; + + // TODO: https://github.com/paritytech/parity-bridges-common/issues/1760 - may do refund here, if + // bridge/lanes are already closed + for messages that are not pruned + + // update bridge metadata - this also guarantees that the bridge is in the proper state + let bridge = + Bridges::::try_mutate_exists(locations.bridge_id(), |bridge| match bridge { + Some(bridge) => { + bridge.state = BridgeState::Closed; + Ok(bridge.clone()) + }, + None => Err(Error::::UnknownBridge), + })?; + + // close inbound and outbound lanes + let lanes_manager = LanesManagerOf::::new(); + let mut inbound_lane = lanes_manager + .any_state_inbound_lane(bridge.lane_id) + .map_err(Error::::LanesManager)?; + let mut outbound_lane = lanes_manager + .any_state_outbound_lane(bridge.lane_id) + .map_err(Error::::LanesManager)?; + + // now prune queued messages + let mut pruned_messages = 0; + for _ in outbound_lane.queued_messages() { + if pruned_messages == may_prune_messages { + break + } + + outbound_lane.remove_oldest_unpruned_message(); + pruned_messages += 1; + } + + // if there are outbound messages in the queue, just update states and early exit + if !outbound_lane.queued_messages().is_empty() { + // update lanes state. Under normal circumstances, following calls shall never fail + inbound_lane.set_state(LaneState::Closed); + outbound_lane.set_state(LaneState::Closed); + + // write something to log + let enqueued_messages = outbound_lane.queued_messages().saturating_len(); + log::trace!( + target: LOG_TARGET, + "Bridge {:?} between {:?} and {:?} is closing lane_id: {:?}. {} messages remaining", + locations.bridge_id(), + locations.bridge_origin_universal_location(), + locations.bridge_destination_universal_location(), + bridge.lane_id, + enqueued_messages, + ); + + // deposit the `ClosingBridge` event + Self::deposit_event(Event::::ClosingBridge { + bridge_id: *locations.bridge_id(), + lane_id: bridge.lane_id.into(), + pruned_messages, + enqueued_messages, + }); + + return Ok(()) + } + + // else we have pruned all messages, so lanes and the bridge itself may gone + inbound_lane.purge(); + outbound_lane.purge(); + Bridges::::remove(locations.bridge_id()); + LaneToBridge::::remove(bridge.lane_id); + + // return deposit + let released_deposit = T::Currency::release( + &HoldReason::BridgeDeposit.into(), + &bridge.bridge_owner_account, + bridge.deposit, + Precision::BestEffort, + ) + .inspect_err(|e| { + // we can't do anything here - looks like funds have been (partially) unreserved + // before by someone else. Let's not fail, though - it'll be worse for the caller + log::error!( + target: LOG_TARGET, + "Failed to unreserve during the bridge {:?} closure with error: {e:?}", + locations.bridge_id(), + ); + }) + .ok() + .unwrap_or(BalanceOf::>::zero()); + + // write something to log + log::trace!( + target: LOG_TARGET, + "Bridge {:?} between {:?} and {:?} has closed lane_id: {:?}, the bridge deposit {released_deposit:?} was returned", + locations.bridge_id(), + bridge.lane_id, + locations.bridge_origin_universal_location(), + locations.bridge_destination_universal_location(), + ); + + // deposit the `BridgePruned` event + Self::deposit_event(Event::::BridgePruned { + bridge_id: *locations.bridge_id(), + lane_id: bridge.lane_id.into(), + bridge_deposit: released_deposit, + pruned_messages, + }); + + Ok(()) + } } impl, I: 'static> Pallet { - /// Returns dedicated/configured lane identifier. - pub(crate) fn lane_for( - source: &InteriorLocation, - dest: (&NetworkId, &InteriorLocation), - ) -> Option { - let source = source.clone().relative_to(&T::UniversalLocation::get()); - - // Check that we have configured a point-to-point lane for 'source' and `dest`. - T::Lanes::get() - .into_iter() - .find_map(|(lane_source, (lane_dest_network, lane_dest))| { - if lane_source.location == source && - &lane_dest_network == dest.0 && - Self::bridged_network_id().as_ref() == Some(dest.0) && - &lane_dest == dest.1 - { - Some(lane_source) - } else { - None - } - }) + /// Open bridge for lane. + pub fn do_open_bridge( + locations: Box, + lane_id: T::LaneId, + create_lanes: bool, + ) -> Result<(), DispatchError> { + // reserve balance on the origin's sovereign account (if needed) + let bridge_owner_account = T::BridgeOriginAccountIdConverter::convert_location( + locations.bridge_origin_relative_location(), + ) + .ok_or(Error::::InvalidBridgeOriginAccount)?; + let deposit = if T::AllowWithoutBridgeDeposit::contains( + locations.bridge_origin_relative_location(), + ) { + BalanceOf::>::zero() + } else { + let deposit = T::BridgeDeposit::get(); + T::Currency::hold( + &HoldReason::BridgeDeposit.into(), + &bridge_owner_account, + deposit, + ) + .map_err(|e| { + log::error!( + target: LOG_TARGET, + "Failed to hold bridge deposit: {deposit:?} \ + from bridge_owner_account: {bridge_owner_account:?} derived from \ + bridge_origin_relative_location: {:?} with error: {e:?}", + locations.bridge_origin_relative_location(), + ); + Error::::FailedToReserveBridgeDeposit + })?; + deposit + }; + + // save bridge metadata + Bridges::::try_mutate(locations.bridge_id(), |bridge| match bridge { + Some(_) => Err(Error::::BridgeAlreadyExists), + None => { + *bridge = Some(BridgeOf:: { + bridge_origin_relative_location: Box::new( + locations.bridge_origin_relative_location().clone().into(), + ), + bridge_origin_universal_location: Box::new( + locations.bridge_origin_universal_location().clone().into(), + ), + bridge_destination_universal_location: Box::new( + locations.bridge_destination_universal_location().clone().into(), + ), + state: BridgeState::Opened, + bridge_owner_account, + deposit, + lane_id, + }); + Ok(()) + }, + })?; + // save lane to bridge mapping + LaneToBridge::::try_mutate(lane_id, |bridge| match bridge { + Some(_) => Err(Error::::BridgeAlreadyExists), + None => { + *bridge = Some(*locations.bridge_id()); + Ok(()) + }, + })?; + + if create_lanes { + // create new lanes. Under normal circumstances, following calls shall never fail + let lanes_manager = LanesManagerOf::::new(); + lanes_manager + .create_inbound_lane(lane_id) + .map_err(Error::::LanesManager)?; + lanes_manager + .create_outbound_lane(lane_id) + .map_err(Error::::LanesManager)?; + } + + // write something to log + log::trace!( + target: LOG_TARGET, + "Bridge {:?} between {:?} and {:?} has been opened using lane_id: {lane_id:?}", + locations.bridge_id(), + locations.bridge_origin_universal_location(), + locations.bridge_destination_universal_location(), + ); + + // deposit `BridgeOpened` event + Self::deposit_event(Event::::BridgeOpened { + bridge_id: *locations.bridge_id(), + bridge_deposit: deposit, + local_endpoint: Box::new(locations.bridge_origin_universal_location().clone()), + remote_endpoint: Box::new( + locations.bridge_destination_universal_location().clone(), + ), + lane_id: lane_id.into(), + }); + + Ok(()) + } + } + + impl, I: 'static> Pallet { + /// Return bridge endpoint locations and dedicated lane identifier. This method converts + /// runtime `origin` argument to relative `Location` using the `T::OpenBridgeOrigin` + /// converter. + pub fn bridge_locations_from_origin( + origin: OriginFor, + bridge_destination_universal_location: Box, + ) -> Result, sp_runtime::DispatchError> { + Self::bridge_locations( + T::OpenBridgeOrigin::ensure_origin(origin)?, + (*bridge_destination_universal_location) + .try_into() + .map_err(|_| Error::::UnsupportedXcmVersion)?, + ) + } + + /// Return bridge endpoint locations and dedicated **bridge** identifier (`BridgeId`). + pub fn bridge_locations( + bridge_origin_relative_location: Location, + bridge_destination_universal_location: InteriorLocation, + ) -> Result, sp_runtime::DispatchError> { + BridgeLocations::bridge_locations( + T::UniversalLocation::get(), + bridge_origin_relative_location, + bridge_destination_universal_location, + Self::bridged_network_id()?, + ) + .map_err(|e| { + log::trace!( + target: LOG_TARGET, + "bridge_locations error: {e:?}", + ); + Error::::BridgeLocations(e).into() + }) + } + + /// Return bridge metadata by bridge_id + pub fn bridge(bridge_id: &BridgeId) -> Option> { + Bridges::::get(bridge_id) + } + + /// Return bridge metadata by lane_id + pub fn bridge_by_lane_id(lane_id: &T::LaneId) -> Option<(BridgeId, BridgeOf)> { + LaneToBridge::::get(lane_id) + .and_then(|bridge_id| Self::bridge(&bridge_id).map(|bridge| (bridge_id, bridge))) } + } + impl, I: 'static> Pallet { /// Returns some `NetworkId` if contains `GlobalConsensus` junction. - fn bridged_network_id() -> Option { + fn bridged_network_id() -> Result { match T::BridgedNetwork::get().take_first_interior() { - Some(GlobalConsensus(network)) => Some(network), - _ => None, + Some(GlobalConsensus(network)) => Ok(network), + _ => Err(Error::::BridgeLocations( + BridgeLocationsError::InvalidBridgeDestination, + ) + .into()), + } + } + } + + #[cfg(any(test, feature = "try-runtime", feature = "std"))] + impl, I: 'static> Pallet { + /// Ensure the correctness of the state of this pallet. + pub fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> { + use sp_std::collections::btree_set::BTreeSet; + + let mut lanes = BTreeSet::new(); + + // check all known bridge configurations + for (bridge_id, bridge) in Bridges::::iter() { + lanes.insert(Self::do_try_state_for_bridge(bridge_id, bridge)?); + } + ensure!( + lanes.len() == Bridges::::iter().count(), + "Invalid `Bridges` configuration, probably two bridges handle the same laneId!" + ); + ensure!( + lanes.len() == LaneToBridge::::iter().count(), + "Invalid `LaneToBridge` configuration, probably missing or not removed laneId!" + ); + + // check connected `pallet_bridge_messages` state. + Self::do_try_state_for_messages() + } + + /// Ensure the correctness of the state of the bridge. + pub fn do_try_state_for_bridge( + bridge_id: BridgeId, + bridge: BridgeOf, + ) -> Result { + log::info!(target: LOG_TARGET, "Checking `do_try_state_for_bridge` for bridge_id: {bridge_id:?} and bridge: {bridge:?}"); + + // check `BridgeId` points to the same `LaneId` and vice versa. + ensure!( + Some(bridge_id) == LaneToBridge::::get(bridge.lane_id), + "Found `LaneToBridge` inconsistency for bridge_id - missing mapping!" + ); + + // check `pallet_bridge_messages` state for that `LaneId`. + let lanes_manager = LanesManagerOf::::new(); + ensure!( + lanes_manager.any_state_inbound_lane(bridge.lane_id).is_ok(), + "Inbound lane not found!", + ); + ensure!( + lanes_manager.any_state_outbound_lane(bridge.lane_id).is_ok(), + "Outbound lane not found!", + ); + + // check that `locations` are convertible to the `latest` XCM. + let bridge_origin_relative_location_as_latest: &Location = + bridge.bridge_origin_relative_location.try_as().map_err(|_| { + "`bridge.bridge_origin_relative_location` cannot be converted to the `latest` XCM, needs migration!" + })?; + let bridge_origin_universal_location_as_latest: &InteriorLocation = bridge.bridge_origin_universal_location + .try_as() + .map_err(|_| "`bridge.bridge_origin_universal_location` cannot be converted to the `latest` XCM, needs migration!")?; + let bridge_destination_universal_location_as_latest: &InteriorLocation = bridge.bridge_destination_universal_location + .try_as() + .map_err(|_| "`bridge.bridge_destination_universal_location` cannot be converted to the `latest` XCM, needs migration!")?; + + // check `BridgeId` does not change + ensure!( + bridge_id == BridgeId::new(bridge_origin_universal_location_as_latest, bridge_destination_universal_location_as_latest), + "`bridge_id` is different than calculated from `bridge_origin_universal_location_as_latest` and `bridge_destination_universal_location_as_latest`, needs migration!" + ); + + // check bridge account owner + ensure!( + T::BridgeOriginAccountIdConverter::convert_location(bridge_origin_relative_location_as_latest) == Some(bridge.bridge_owner_account), + "`bridge.bridge_owner_account` is different than calculated from `bridge.bridge_origin_relative_location`, needs migration!" + ); + + Ok(bridge.lane_id) + } + + /// Ensure the correctness of the state of the connected `pallet_bridge_messages` instance. + pub fn do_try_state_for_messages() -> Result<(), sp_runtime::TryRuntimeError> { + // check that all `InboundLanes` laneIds have mapping to some bridge. + for lane_id in pallet_bridge_messages::InboundLanes::::iter_keys() { + log::info!(target: LOG_TARGET, "Checking `do_try_state_for_messages` for `InboundLanes`'s lane_id: {lane_id:?}..."); + ensure!( + LaneToBridge::::get(lane_id).is_some(), + "Found `LaneToBridge` inconsistency for `InboundLanes`'s lane_id - missing mapping!" + ); + } + + // check that all `OutboundLanes` laneIds have mapping to some bridge. + for lane_id in pallet_bridge_messages::OutboundLanes::::iter_keys() { + log::info!(target: LOG_TARGET, "Checking `do_try_state_for_messages` for `OutboundLanes`'s lane_id: {lane_id:?}..."); + ensure!( + LaneToBridge::::get(lane_id).is_some(), + "Found `LaneToBridge` inconsistency for `OutboundLanes`'s lane_id - missing mapping!" + ); } + + Ok(()) } } + + /// All registered bridges. + #[pallet::storage] + pub type Bridges, I: 'static = ()> = + StorageMap<_, Identity, BridgeId, BridgeOf>; + /// All registered `lane_id` and `bridge_id` mappings. + #[pallet::storage] + pub type LaneToBridge, I: 'static = ()> = + StorageMap<_, Identity, T::LaneId, BridgeId>; + + #[pallet::genesis_config] + #[derive(DefaultNoBound)] + pub struct GenesisConfig, I: 'static = ()> { + /// Opened bridges. + /// + /// Keep in mind that we are **NOT** reserving any amount for the bridges opened at + /// genesis. We are **NOT** opening lanes, used by this bridge. It all must be done using + /// other pallets genesis configuration or some other means. + pub opened_bridges: Vec<(Location, InteriorLocation, Option)>, + /// Dummy marker. + #[serde(skip)] + pub _phantom: sp_std::marker::PhantomData<(T, I)>, + } + + #[pallet::genesis_build] + impl, I: 'static> BuildGenesisConfig for GenesisConfig + where + T: frame_system::Config>>, + { + fn build(&self) { + for ( + bridge_origin_relative_location, + bridge_destination_universal_location, + maybe_lane_id, + ) in &self.opened_bridges + { + let locations = Pallet::::bridge_locations( + bridge_origin_relative_location.clone(), + bridge_destination_universal_location.clone().into(), + ) + .expect("Invalid genesis configuration"); + + let lane_id = match maybe_lane_id { + Some(lane_id) => *lane_id, + None => + locations.calculate_lane_id(xcm::latest::VERSION).expect("Valid locations"), + }; + + Pallet::::do_open_bridge(locations, lane_id, true) + .expect("Valid opened bridge!"); + } + } + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event, I: 'static = ()> { + /// The bridge between two locations has been opened. + BridgeOpened { + /// Bridge identifier. + bridge_id: BridgeId, + /// Amount of deposit held. + bridge_deposit: BalanceOf>, + + /// Universal location of local bridge endpoint. + local_endpoint: Box, + /// Universal location of remote bridge endpoint. + remote_endpoint: Box, + /// Lane identifier. + lane_id: T::LaneId, + }, + /// Bridge is going to be closed, but not yet fully pruned from the runtime storage. + ClosingBridge { + /// Bridge identifier. + bridge_id: BridgeId, + /// Lane identifier. + lane_id: T::LaneId, + /// Number of pruned messages during the close call. + pruned_messages: MessageNonce, + /// Number of enqueued messages that need to be pruned in follow up calls. + enqueued_messages: MessageNonce, + }, + /// Bridge has been closed and pruned from the runtime storage. It now may be reopened + /// again by any participant. + BridgePruned { + /// Bridge identifier. + bridge_id: BridgeId, + /// Lane identifier. + lane_id: T::LaneId, + /// Amount of deposit released. + bridge_deposit: BalanceOf>, + /// Number of pruned messages during the close call. + pruned_messages: MessageNonce, + }, + } + + #[pallet::error] + pub enum Error { + /// Bridge locations error. + BridgeLocations(BridgeLocationsError), + /// Invalid local bridge origin account. + InvalidBridgeOriginAccount, + /// The bridge is already registered in this pallet. + BridgeAlreadyExists, + /// The local origin already owns a maximal number of bridges. + TooManyBridgesForLocalOrigin, + /// Trying to close already closed bridge. + BridgeAlreadyClosed, + /// Lanes manager error. + LanesManager(LanesManagerError), + /// Trying to access unknown bridge. + UnknownBridge, + /// The bridge origin can't pay the required amount for opening the bridge. + FailedToReserveBridgeDeposit, + /// The version of XCM location argument is unsupported. + UnsupportedXcmVersion, + } +} + +#[cfg(test)] +mod tests { + use super::*; + use bp_messages::LaneIdType; + use mock::*; + + use frame_support::{assert_err, assert_noop, assert_ok, traits::fungible::Mutate, BoundedVec}; + use frame_system::{EventRecord, Phase}; + use sp_runtime::TryRuntimeError; + + fn fund_origin_sovereign_account(locations: &BridgeLocations, balance: Balance) -> AccountId { + let bridge_owner_account = + LocationToAccountId::convert_location(locations.bridge_origin_relative_location()) + .unwrap(); + assert_ok!(Balances::mint_into(&bridge_owner_account, balance)); + bridge_owner_account + } + + fn mock_open_bridge_from_with( + origin: RuntimeOrigin, + deposit: Balance, + with: InteriorLocation, + ) -> (BridgeOf, BridgeLocations) { + let locations = + XcmOverBridge::bridge_locations_from_origin(origin, Box::new(with.into())).unwrap(); + let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap(); + let bridge_owner_account = + fund_origin_sovereign_account(&locations, deposit + ExistentialDeposit::get()); + Balances::hold(&HoldReason::BridgeDeposit.into(), &bridge_owner_account, deposit).unwrap(); + + let bridge = Bridge { + bridge_origin_relative_location: Box::new( + locations.bridge_origin_relative_location().clone().into(), + ), + bridge_origin_universal_location: Box::new( + locations.bridge_origin_universal_location().clone().into(), + ), + bridge_destination_universal_location: Box::new( + locations.bridge_destination_universal_location().clone().into(), + ), + state: BridgeState::Opened, + bridge_owner_account, + deposit, + lane_id, + }; + Bridges::::insert(locations.bridge_id(), bridge.clone()); + LaneToBridge::::insert(bridge.lane_id, locations.bridge_id()); + + let lanes_manager = LanesManagerOf::::new(); + lanes_manager.create_inbound_lane(bridge.lane_id).unwrap(); + lanes_manager.create_outbound_lane(bridge.lane_id).unwrap(); + + assert_ok!(XcmOverBridge::do_try_state()); + + (bridge, *locations) + } + + fn mock_open_bridge_from( + origin: RuntimeOrigin, + deposit: Balance, + ) -> (BridgeOf, BridgeLocations) { + mock_open_bridge_from_with(origin, deposit, bridged_asset_hub_universal_location()) + } + + fn enqueue_message(lane: TestLaneIdType) { + let lanes_manager = LanesManagerOf::::new(); + lanes_manager + .active_outbound_lane(lane) + .unwrap() + .send_message(BoundedVec::try_from(vec![42]).expect("We craft valid messages")); + } + + #[test] + fn open_bridge_fails_if_origin_is_not_allowed() { + run_test(|| { + assert_noop!( + XcmOverBridge::open_bridge( + OpenBridgeOrigin::disallowed_origin(), + Box::new(bridged_asset_hub_universal_location().into()), + ), + sp_runtime::DispatchError::BadOrigin, + ); + }) + } + + #[test] + fn open_bridge_fails_if_origin_is_not_relative() { + run_test(|| { + assert_noop!( + XcmOverBridge::open_bridge( + OpenBridgeOrigin::parent_relay_chain_universal_origin(), + Box::new(bridged_asset_hub_universal_location().into()), + ), + Error::::BridgeLocations( + BridgeLocationsError::InvalidBridgeOrigin + ), + ); + + assert_noop!( + XcmOverBridge::open_bridge( + OpenBridgeOrigin::sibling_parachain_universal_origin(), + Box::new(bridged_asset_hub_universal_location().into()), + ), + Error::::BridgeLocations( + BridgeLocationsError::InvalidBridgeOrigin + ), + ); + }) + } + + #[test] + fn open_bridge_fails_if_destination_is_not_remote() { + run_test(|| { + assert_noop!( + XcmOverBridge::open_bridge( + OpenBridgeOrigin::parent_relay_chain_origin(), + Box::new( + [GlobalConsensus(RelayNetwork::get()), Parachain(BRIDGED_ASSET_HUB_ID)] + .into() + ), + ), + Error::::BridgeLocations(BridgeLocationsError::DestinationIsLocal), + ); + }); + } + + #[test] + fn open_bridge_fails_if_outside_of_bridged_consensus() { + run_test(|| { + assert_noop!( + XcmOverBridge::open_bridge( + OpenBridgeOrigin::parent_relay_chain_origin(), + Box::new( + [ + GlobalConsensus(NonBridgedRelayNetwork::get()), + Parachain(BRIDGED_ASSET_HUB_ID) + ] + .into() + ), + ), + Error::::BridgeLocations( + BridgeLocationsError::UnreachableDestination + ), + ); + }); + } + + #[test] + fn open_bridge_fails_if_origin_has_no_sovereign_account() { + run_test(|| { + assert_noop!( + XcmOverBridge::open_bridge( + OpenBridgeOrigin::origin_without_sovereign_account(), + Box::new(bridged_asset_hub_universal_location().into()), + ), + Error::::InvalidBridgeOriginAccount, + ); + }); + } + + #[test] + fn open_bridge_fails_if_origin_sovereign_account_has_no_enough_funds() { + run_test(|| { + assert_noop!( + XcmOverBridge::open_bridge( + OpenBridgeOrigin::sibling_parachain_origin(), + Box::new(bridged_asset_hub_universal_location().into()), + ), + Error::::FailedToReserveBridgeDeposit, + ); + }); + } + + #[test] + fn open_bridge_fails_if_it_already_exists() { + run_test(|| { + let origin = OpenBridgeOrigin::parent_relay_chain_origin(); + let locations = XcmOverBridge::bridge_locations_from_origin( + origin.clone(), + Box::new(bridged_asset_hub_universal_location().into()), + ) + .unwrap(); + let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap(); + fund_origin_sovereign_account( + &locations, + BridgeDeposit::get() + ExistentialDeposit::get(), + ); + + Bridges::::insert( + locations.bridge_id(), + Bridge { + bridge_origin_relative_location: Box::new( + locations.bridge_origin_relative_location().clone().into(), + ), + bridge_origin_universal_location: Box::new( + locations.bridge_origin_universal_location().clone().into(), + ), + bridge_destination_universal_location: Box::new( + locations.bridge_destination_universal_location().clone().into(), + ), + state: BridgeState::Opened, + bridge_owner_account: [0u8; 32].into(), + deposit: 0, + lane_id, + }, + ); + + assert_noop!( + XcmOverBridge::open_bridge( + origin, + Box::new(bridged_asset_hub_universal_location().into()), + ), + Error::::BridgeAlreadyExists, + ); + }) + } + + #[test] + fn open_bridge_fails_if_its_lanes_already_exists() { + run_test(|| { + let origin = OpenBridgeOrigin::parent_relay_chain_origin(); + let locations = XcmOverBridge::bridge_locations_from_origin( + origin.clone(), + Box::new(bridged_asset_hub_universal_location().into()), + ) + .unwrap(); + let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap(); + fund_origin_sovereign_account( + &locations, + BridgeDeposit::get() + ExistentialDeposit::get(), + ); + + let lanes_manager = LanesManagerOf::::new(); + + lanes_manager.create_inbound_lane(lane_id).unwrap(); + assert_noop!( + XcmOverBridge::open_bridge( + origin.clone(), + Box::new(bridged_asset_hub_universal_location().into()), + ), + Error::::LanesManager(LanesManagerError::InboundLaneAlreadyExists), + ); + + lanes_manager.active_inbound_lane(lane_id).unwrap().purge(); + lanes_manager.create_outbound_lane(lane_id).unwrap(); + assert_noop!( + XcmOverBridge::open_bridge( + origin, + Box::new(bridged_asset_hub_universal_location().into()), + ), + Error::::LanesManager( + LanesManagerError::OutboundLaneAlreadyExists + ), + ); + }) + } + + #[test] + fn open_bridge_works() { + run_test(|| { + // in our test runtime, we expect that bridge may be opened by parent relay chain + // and any sibling parachain + let origins = [ + (OpenBridgeOrigin::parent_relay_chain_origin(), 0), + (OpenBridgeOrigin::sibling_parachain_origin(), BridgeDeposit::get()), + ]; + + // check that every origin may open the bridge + let lanes_manager = LanesManagerOf::::new(); + let existential_deposit = ExistentialDeposit::get(); + for (origin, expected_deposit) in origins { + // reset events + System::set_block_number(1); + System::reset_events(); + + // compute all other locations + let xcm_version = xcm::latest::VERSION; + let locations = XcmOverBridge::bridge_locations_from_origin( + origin.clone(), + Box::new( + VersionedInteriorLocation::from(bridged_asset_hub_universal_location()) + .into_version(xcm_version) + .expect("valid conversion"), + ), + ) + .unwrap(); + let lane_id = locations.calculate_lane_id(xcm_version).unwrap(); + + // ensure that there's no bridge and lanes in the storage + assert_eq!(Bridges::::get(locations.bridge_id()), None); + assert_eq!( + lanes_manager.active_inbound_lane(lane_id).map(drop), + Err(LanesManagerError::UnknownInboundLane) + ); + assert_eq!( + lanes_manager.active_outbound_lane(lane_id).map(drop), + Err(LanesManagerError::UnknownOutboundLane) + ); + assert_eq!(LaneToBridge::::get(lane_id), None); + + // give enough funds to the sovereign account of the bridge origin + let bridge_owner_account = fund_origin_sovereign_account( + &locations, + expected_deposit + existential_deposit, + ); + assert_eq!( + Balances::free_balance(&bridge_owner_account), + expected_deposit + existential_deposit + ); + assert_eq!(Balances::reserved_balance(&bridge_owner_account), 0); + + // now open the bridge + assert_ok!(XcmOverBridge::open_bridge( + origin, + Box::new(locations.bridge_destination_universal_location().clone().into()), + )); + + // ensure that everything has been set up in the runtime storage + assert_eq!( + Bridges::::get(locations.bridge_id()), + Some(Bridge { + bridge_origin_relative_location: Box::new( + locations.bridge_origin_relative_location().clone().into() + ), + bridge_origin_universal_location: Box::new( + locations.bridge_origin_universal_location().clone().into(), + ), + bridge_destination_universal_location: Box::new( + locations.bridge_destination_universal_location().clone().into(), + ), + state: BridgeState::Opened, + bridge_owner_account: bridge_owner_account.clone(), + deposit: expected_deposit, + lane_id + }), + ); + assert_eq!( + lanes_manager.active_inbound_lane(lane_id).map(|l| l.state()), + Ok(LaneState::Opened) + ); + assert_eq!( + lanes_manager.active_outbound_lane(lane_id).map(|l| l.state()), + Ok(LaneState::Opened) + ); + assert_eq!( + LaneToBridge::::get(lane_id), + Some(*locations.bridge_id()) + ); + assert_eq!(Balances::free_balance(&bridge_owner_account), existential_deposit); + assert_eq!(Balances::reserved_balance(&bridge_owner_account), expected_deposit); + + // ensure that the proper event is deposited + assert_eq!( + System::events().last(), + Some(&EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::XcmOverBridge(Event::BridgeOpened { + bridge_id: *locations.bridge_id(), + bridge_deposit: expected_deposit, + local_endpoint: Box::new( + locations.bridge_origin_universal_location().clone() + ), + remote_endpoint: Box::new( + locations.bridge_destination_universal_location().clone() + ), + lane_id: lane_id.into() + }), + topics: vec![], + }), + ); + + // check state + assert_ok!(XcmOverBridge::do_try_state()); + } + }); + } + + #[test] + fn close_bridge_fails_if_origin_is_not_allowed() { + run_test(|| { + assert_noop!( + XcmOverBridge::close_bridge( + OpenBridgeOrigin::disallowed_origin(), + Box::new(bridged_asset_hub_universal_location().into()), + 0, + ), + sp_runtime::DispatchError::BadOrigin, + ); + }) + } + + #[test] + fn close_bridge_fails_if_origin_is_not_relative() { + run_test(|| { + assert_noop!( + XcmOverBridge::close_bridge( + OpenBridgeOrigin::parent_relay_chain_universal_origin(), + Box::new(bridged_asset_hub_universal_location().into()), + 0, + ), + Error::::BridgeLocations( + BridgeLocationsError::InvalidBridgeOrigin + ), + ); + + assert_noop!( + XcmOverBridge::close_bridge( + OpenBridgeOrigin::sibling_parachain_universal_origin(), + Box::new(bridged_asset_hub_universal_location().into()), + 0, + ), + Error::::BridgeLocations( + BridgeLocationsError::InvalidBridgeOrigin + ), + ); + }) + } + + #[test] + fn close_bridge_fails_if_its_lanes_are_unknown() { + run_test(|| { + let origin = OpenBridgeOrigin::parent_relay_chain_origin(); + let (bridge, locations) = mock_open_bridge_from(origin.clone(), 0); + + let lanes_manager = LanesManagerOf::::new(); + lanes_manager.any_state_inbound_lane(bridge.lane_id).unwrap().purge(); + assert_noop!( + XcmOverBridge::close_bridge( + origin.clone(), + Box::new(locations.bridge_destination_universal_location().clone().into()), + 0, + ), + Error::::LanesManager(LanesManagerError::UnknownInboundLane), + ); + lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().purge(); + + let (_, locations) = mock_open_bridge_from(origin.clone(), 0); + lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().purge(); + assert_noop!( + XcmOverBridge::close_bridge( + origin, + Box::new(locations.bridge_destination_universal_location().clone().into()), + 0, + ), + Error::::LanesManager(LanesManagerError::UnknownOutboundLane), + ); + }); + } + + #[test] + fn close_bridge_works() { + run_test(|| { + let origin = OpenBridgeOrigin::parent_relay_chain_origin(); + let expected_deposit = BridgeDeposit::get(); + let (bridge, locations) = mock_open_bridge_from(origin.clone(), expected_deposit); + System::set_block_number(1); + + // remember owner balances + let free_balance = Balances::free_balance(&bridge.bridge_owner_account); + let reserved_balance = Balances::reserved_balance(&bridge.bridge_owner_account); + + // enqueue some messages + for _ in 0..32 { + enqueue_message(bridge.lane_id); + } + + // now call the `close_bridge`, which will only partially prune messages + assert_ok!(XcmOverBridge::close_bridge( + origin.clone(), + Box::new(locations.bridge_destination_universal_location().clone().into()), + 16, + ),); + + // as a result, the bridge and lanes are switched to the `Closed` state, some messages + // are pruned, but funds are not unreserved + let lanes_manager = LanesManagerOf::::new(); + assert_eq!( + Bridges::::get(locations.bridge_id()).map(|b| b.state), + Some(BridgeState::Closed) + ); + assert_eq!( + lanes_manager.any_state_inbound_lane(bridge.lane_id).unwrap().state(), + LaneState::Closed + ); + assert_eq!( + lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().state(), + LaneState::Closed + ); + assert_eq!( + lanes_manager + .any_state_outbound_lane(bridge.lane_id) + .unwrap() + .queued_messages() + .checked_len(), + Some(16) + ); + assert_eq!( + LaneToBridge::::get(bridge.lane_id), + Some(*locations.bridge_id()) + ); + assert_eq!(Balances::free_balance(&bridge.bridge_owner_account), free_balance); + assert_eq!(Balances::reserved_balance(&bridge.bridge_owner_account), reserved_balance); + assert_eq!( + System::events().last(), + Some(&EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::XcmOverBridge(Event::ClosingBridge { + bridge_id: *locations.bridge_id(), + lane_id: bridge.lane_id.into(), + pruned_messages: 16, + enqueued_messages: 16, + }), + topics: vec![], + }), + ); + + // now call the `close_bridge` again, which will only partially prune messages + assert_ok!(XcmOverBridge::close_bridge( + origin.clone(), + Box::new(locations.bridge_destination_universal_location().clone().into()), + 8, + ),); + + // nothing is changed (apart from the pruned messages) + assert_eq!( + Bridges::::get(locations.bridge_id()).map(|b| b.state), + Some(BridgeState::Closed) + ); + assert_eq!( + lanes_manager.any_state_inbound_lane(bridge.lane_id).unwrap().state(), + LaneState::Closed + ); + assert_eq!( + lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().state(), + LaneState::Closed + ); + assert_eq!( + lanes_manager + .any_state_outbound_lane(bridge.lane_id) + .unwrap() + .queued_messages() + .checked_len(), + Some(8) + ); + assert_eq!( + LaneToBridge::::get(bridge.lane_id), + Some(*locations.bridge_id()) + ); + assert_eq!(Balances::free_balance(&bridge.bridge_owner_account), free_balance); + assert_eq!(Balances::reserved_balance(&bridge.bridge_owner_account), reserved_balance); + assert_eq!( + System::events().last(), + Some(&EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::XcmOverBridge(Event::ClosingBridge { + bridge_id: *locations.bridge_id(), + lane_id: bridge.lane_id.into(), + pruned_messages: 8, + enqueued_messages: 8, + }), + topics: vec![], + }), + ); + + // now call the `close_bridge` again that will prune all remaining messages and the + // bridge + assert_ok!(XcmOverBridge::close_bridge( + origin, + Box::new(locations.bridge_destination_universal_location().clone().into()), + 9, + ),); + + // there's no traces of bridge in the runtime storage and funds are unreserved + assert_eq!( + Bridges::::get(locations.bridge_id()).map(|b| b.state), + None + ); + assert_eq!( + lanes_manager.any_state_inbound_lane(bridge.lane_id).map(drop), + Err(LanesManagerError::UnknownInboundLane) + ); + assert_eq!( + lanes_manager.any_state_outbound_lane(bridge.lane_id).map(drop), + Err(LanesManagerError::UnknownOutboundLane) + ); + assert_eq!(LaneToBridge::::get(bridge.lane_id), None); + assert_eq!( + Balances::free_balance(&bridge.bridge_owner_account), + free_balance + reserved_balance + ); + assert_eq!(Balances::reserved_balance(&bridge.bridge_owner_account), 0); + assert_eq!( + System::events().last(), + Some(&EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::XcmOverBridge(Event::BridgePruned { + bridge_id: *locations.bridge_id(), + lane_id: bridge.lane_id.into(), + bridge_deposit: expected_deposit, + pruned_messages: 8, + }), + topics: vec![], + }), + ); + }); + } + + #[test] + fn do_try_state_works() { + let bridge_origin_relative_location = SiblingLocation::get(); + let bridge_origin_universal_location = SiblingUniversalLocation::get(); + let bridge_destination_universal_location = BridgedUniversalDestination::get(); + let bridge_owner_account = + LocationToAccountId::convert_location(&bridge_origin_relative_location) + .expect("valid accountId"); + let bridge_owner_account_mismatch = + LocationToAccountId::convert_location(&Location::parent()).expect("valid accountId"); + let bridge_id = BridgeId::new( + &bridge_origin_universal_location, + &bridge_destination_universal_location, + ); + let bridge_id_mismatch = BridgeId::new(&InteriorLocation::Here, &InteriorLocation::Here); + let lane_id = TestLaneIdType::try_new(1, 2).unwrap(); + let lane_id_mismatch = TestLaneIdType::try_new(3, 4).unwrap(); + + let test_bridge_state = + |id, + bridge, + (lane_id, bridge_id), + (inbound_lane_id, outbound_lane_id), + expected_error: Option| { + Bridges::::insert(id, bridge); + LaneToBridge::::insert(lane_id, bridge_id); + + let lanes_manager = LanesManagerOf::::new(); + lanes_manager.create_inbound_lane(inbound_lane_id).unwrap(); + lanes_manager.create_outbound_lane(outbound_lane_id).unwrap(); + + let result = XcmOverBridge::do_try_state(); + if let Some(e) = expected_error { + assert_err!(result, e); + } else { + assert_ok!(result); + } + }; + let cleanup = |bridge_id, lane_ids| { + Bridges::::remove(bridge_id); + for lane_id in lane_ids { + LaneToBridge::::remove(lane_id); + let lanes_manager = LanesManagerOf::::new(); + if let Ok(lane) = lanes_manager.any_state_inbound_lane(lane_id) { + lane.purge(); + } + if let Ok(lane) = lanes_manager.any_state_outbound_lane(lane_id) { + lane.purge(); + } + } + assert_ok!(XcmOverBridge::do_try_state()); + }; + + run_test(|| { + // ok state + test_bridge_state( + bridge_id, + Bridge { + bridge_origin_relative_location: Box::new(VersionedLocation::from( + bridge_origin_relative_location.clone(), + )), + bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from( + bridge_origin_universal_location.clone(), + )), + bridge_destination_universal_location: Box::new( + VersionedInteriorLocation::from( + bridge_destination_universal_location.clone(), + ), + ), + state: BridgeState::Opened, + bridge_owner_account: bridge_owner_account.clone(), + deposit: Zero::zero(), + lane_id, + }, + (lane_id, bridge_id), + (lane_id, lane_id), + None, + ); + cleanup(bridge_id, vec![lane_id]); + + // error - missing `LaneToBridge` mapping + test_bridge_state( + bridge_id, + Bridge { + bridge_origin_relative_location: Box::new(VersionedLocation::from( + bridge_origin_relative_location.clone(), + )), + bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from( + bridge_origin_universal_location.clone(), + )), + bridge_destination_universal_location: Box::new( + VersionedInteriorLocation::from( + bridge_destination_universal_location.clone(), + ), + ), + state: BridgeState::Opened, + bridge_owner_account: bridge_owner_account.clone(), + deposit: Zero::zero(), + lane_id, + }, + (lane_id, bridge_id_mismatch), + (lane_id, lane_id), + Some(TryRuntimeError::Other( + "Found `LaneToBridge` inconsistency for bridge_id - missing mapping!", + )), + ); + cleanup(bridge_id, vec![lane_id]); + + // error bridge owner account cannot be calculated + test_bridge_state( + bridge_id, + Bridge { + bridge_origin_relative_location: Box::new(VersionedLocation::from( + bridge_origin_relative_location.clone(), + )), + bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from( + bridge_origin_universal_location.clone(), + )), + bridge_destination_universal_location: Box::new(VersionedInteriorLocation::from( + bridge_destination_universal_location.clone(), + )), + state: BridgeState::Opened, + bridge_owner_account: bridge_owner_account_mismatch.clone(), + deposit: Zero::zero(), + lane_id, + }, + (lane_id, bridge_id), + (lane_id, lane_id), + Some(TryRuntimeError::Other("`bridge.bridge_owner_account` is different than calculated from `bridge.bridge_origin_relative_location`, needs migration!")), + ); + cleanup(bridge_id, vec![lane_id]); + + // error when (bridge_origin_universal_location + bridge_destination_universal_location) + // produces different `BridgeId` + test_bridge_state( + bridge_id_mismatch, + Bridge { + bridge_origin_relative_location: Box::new(VersionedLocation::from( + bridge_origin_relative_location.clone(), + )), + bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from( + bridge_origin_universal_location.clone(), + )), + bridge_destination_universal_location: Box::new(VersionedInteriorLocation::from( + bridge_destination_universal_location.clone(), + )), + state: BridgeState::Opened, + bridge_owner_account: bridge_owner_account_mismatch.clone(), + deposit: Zero::zero(), + lane_id, + }, + (lane_id, bridge_id_mismatch), + (lane_id, lane_id), + Some(TryRuntimeError::Other("`bridge_id` is different than calculated from `bridge_origin_universal_location_as_latest` and `bridge_destination_universal_location_as_latest`, needs migration!")), + ); + cleanup(bridge_id_mismatch, vec![lane_id]); + + // missing inbound lane for a bridge + test_bridge_state( + bridge_id, + Bridge { + bridge_origin_relative_location: Box::new(VersionedLocation::from( + bridge_origin_relative_location.clone(), + )), + bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from( + bridge_origin_universal_location.clone(), + )), + bridge_destination_universal_location: Box::new( + VersionedInteriorLocation::from( + bridge_destination_universal_location.clone(), + ), + ), + state: BridgeState::Opened, + bridge_owner_account: bridge_owner_account.clone(), + deposit: Zero::zero(), + lane_id, + }, + (lane_id, bridge_id), + (lane_id_mismatch, lane_id), + Some(TryRuntimeError::Other("Inbound lane not found!")), + ); + cleanup(bridge_id, vec![lane_id, lane_id_mismatch]); + + // missing outbound lane for a bridge + test_bridge_state( + bridge_id, + Bridge { + bridge_origin_relative_location: Box::new(VersionedLocation::from( + bridge_origin_relative_location.clone(), + )), + bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from( + bridge_origin_universal_location.clone(), + )), + bridge_destination_universal_location: Box::new( + VersionedInteriorLocation::from( + bridge_destination_universal_location.clone(), + ), + ), + state: BridgeState::Opened, + bridge_owner_account: bridge_owner_account.clone(), + deposit: Zero::zero(), + lane_id, + }, + (lane_id, bridge_id), + (lane_id, lane_id_mismatch), + Some(TryRuntimeError::Other("Outbound lane not found!")), + ); + cleanup(bridge_id, vec![lane_id, lane_id_mismatch]); + + // missing bridge for inbound lane + let lanes_manager = LanesManagerOf::::new(); + assert!(lanes_manager.create_inbound_lane(lane_id).is_ok()); + assert_err!(XcmOverBridge::do_try_state(), TryRuntimeError::Other("Found `LaneToBridge` inconsistency for `InboundLanes`'s lane_id - missing mapping!")); + cleanup(bridge_id, vec![lane_id]); + + // missing bridge for outbound lane + let lanes_manager = LanesManagerOf::::new(); + assert!(lanes_manager.create_outbound_lane(lane_id).is_ok()); + assert_err!(XcmOverBridge::do_try_state(), TryRuntimeError::Other("Found `LaneToBridge` inconsistency for `OutboundLanes`'s lane_id - missing mapping!")); + cleanup(bridge_id, vec![lane_id]); + }); + } + + #[test] + fn ensure_encoding_compatibility() { + use codec::Encode; + + let bridge_destination_universal_location = BridgedUniversalDestination::get(); + let may_prune_messages = 13; + + assert_eq!( + bp_xcm_bridge_hub::XcmBridgeHubCall::open_bridge { + bridge_destination_universal_location: Box::new( + bridge_destination_universal_location.clone().into() + ) + } + .encode(), + Call::::open_bridge { + bridge_destination_universal_location: Box::new( + bridge_destination_universal_location.clone().into() + ) + } + .encode() + ); + assert_eq!( + bp_xcm_bridge_hub::XcmBridgeHubCall::close_bridge { + bridge_destination_universal_location: Box::new( + bridge_destination_universal_location.clone().into() + ), + may_prune_messages, + } + .encode(), + Call::::close_bridge { + bridge_destination_universal_location: Box::new( + bridge_destination_universal_location.clone().into() + ), + may_prune_messages, + } + .encode() + ); + } } diff --git a/bridges/modules/xcm-bridge-hub/src/migration.rs b/bridges/modules/xcm-bridge-hub/src/migration.rs new file mode 100644 index 0000000000000000000000000000000000000000..ffd5233a917b1c8d7c838313a2c5ae095be27593 --- /dev/null +++ b/bridges/modules/xcm-bridge-hub/src/migration.rs @@ -0,0 +1,145 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! A module that is responsible for migration of storage. + +use crate::{Config, Pallet, LOG_TARGET}; +use frame_support::{ + traits::{Get, OnRuntimeUpgrade, StorageVersion}, + weights::Weight, +}; +use xcm::prelude::{InteriorLocation, Location}; + +/// The in-code storage version. +pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); + +/// This migration does not modify storage but can be used to open a bridge and link it to the +/// specified LaneId. This is useful when we want to open a bridge and use a custom LaneId instead +/// of the pre-calculated one provided by the `fn open_bridge extrinsic`. +/// Or perhaps if you want to ensure that your runtime (e.g., for testing) always has an open +/// bridge. +pub struct OpenBridgeForLane< + T, + I, + Lane, + CreateLane, + SourceRelativeLocation, + BridgedUniversalLocation, +>( + core::marker::PhantomData<( + T, + I, + Lane, + CreateLane, + SourceRelativeLocation, + BridgedUniversalLocation, + )>, +); +impl< + T: Config, + I: 'static, + Lane: Get, + CreateLane: Get, + SourceRelativeLocation: Get, + BridgedUniversalLocation: Get, + > OnRuntimeUpgrade + for OpenBridgeForLane +{ + fn on_runtime_upgrade() -> Weight { + let bridge_origin_relative_location = SourceRelativeLocation::get(); + let bridge_destination_universal_location = BridgedUniversalLocation::get(); + let lane_id = Lane::get(); + let create_lane = CreateLane::get(); + log::info!( + target: LOG_TARGET, + "OpenBridgeForLane - going to open bridge with lane_id: {lane_id:?} (create_lane: {create_lane:?}) \ + between bridge_origin_relative_location: {bridge_origin_relative_location:?} and \ + bridge_destination_universal_location: {bridge_destination_universal_location:?}", + ); + + let locations = match Pallet::::bridge_locations( + bridge_origin_relative_location.clone(), + bridge_destination_universal_location.clone(), + ) { + Ok(locations) => locations, + Err(e) => { + log::error!( + target: LOG_TARGET, + "OpenBridgeForLane - on_runtime_upgrade failed to construct bridge_locations with error: {e:?}" + ); + return T::DbWeight::get().reads(0) + }, + }; + + // check if already exists + if let Some((bridge_id, bridge)) = Pallet::::bridge_by_lane_id(&lane_id) { + log::info!( + target: LOG_TARGET, + "OpenBridgeForLane - bridge: {bridge:?} with bridge_id: {bridge_id:?} already exist for lane_id: {lane_id:?}!" + ); + if &bridge_id != locations.bridge_id() { + log::warn!( + target: LOG_TARGET, + "OpenBridgeForLane - check you parameters, because a different bridge: {bridge:?} \ + with bridge_id: {bridge_id:?} exist for lane_id: {lane_id:?} for requested \ + bridge_origin_relative_location: {bridge_origin_relative_location:?} and \ + bridge_destination_universal_location: {bridge_destination_universal_location:?} !", + ); + } + + return T::DbWeight::get().reads(2) + } + + if let Err(e) = Pallet::::do_open_bridge(locations, lane_id, create_lane) { + log::error!(target: LOG_TARGET, "OpenBridgeForLane - do_open_bridge failed with error: {e:?}"); + T::DbWeight::get().reads(6) + } else { + log::info!(target: LOG_TARGET, "OpenBridgeForLane - do_open_bridge passed!"); + T::DbWeight::get().reads_writes(6, 4) + } + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_state: sp_std::vec::Vec) -> Result<(), sp_runtime::DispatchError> { + let bridge_origin_relative_location = SourceRelativeLocation::get(); + let bridge_destination_universal_location = BridgedUniversalLocation::get(); + let lane_id = Lane::get(); + + // check that requested bridge is stored + let Ok(locations) = Pallet::::bridge_locations( + bridge_origin_relative_location.clone(), + bridge_destination_universal_location.clone(), + ) else { + return Err(sp_runtime::DispatchError::Other("Invalid locations!")) + }; + let Some((bridge_id, _)) = Pallet::::bridge_by_lane_id(&lane_id) else { + return Err(sp_runtime::DispatchError::Other("Missing bridge!")) + }; + frame_support::ensure!( + locations.bridge_id() == &bridge_id, + "Bridge is not stored correctly!" + ); + + log::info!( + target: LOG_TARGET, + "OpenBridgeForLane - post_upgrade found opened bridge with lane_id: {lane_id:?} \ + between bridge_origin_relative_location: {bridge_origin_relative_location:?} and \ + bridge_destination_universal_location: {bridge_destination_universal_location:?}", + ); + + Ok(()) + } +} diff --git a/bridges/modules/xcm-bridge-hub/src/mock.rs b/bridges/modules/xcm-bridge-hub/src/mock.rs index 0cca32ba9e5f55a80baa4168f483ea33ba69ce58..6511b9fc5b04e6da2052853c49df716ed3b13ac9 100644 --- a/bridges/modules/xcm-bridge-hub/src/mock.rs +++ b/bridges/modules/xcm-bridge-hub/src/mock.rs @@ -20,16 +20,17 @@ use crate as pallet_xcm_bridge_hub; use bp_messages::{ target_chain::{DispatchMessage, MessageDispatch}, - ChainWithMessages, LaneId, MessageNonce, + ChainWithMessages, HashedLaneId, MessageNonce, }; use bp_runtime::{messages::MessageDispatchResult, Chain, ChainId, HashOf}; -use bridge_runtime_common::messages_xcm_extension::{SenderAndLane, XcmBlobHauler}; +use bp_xcm_bridge_hub::{BridgeId, LocalXcmChannelManager}; use codec::Encode; use frame_support::{ assert_ok, derive_impl, parameter_types, - traits::{Everything, NeverEnsureOrigin}, + traits::{EnsureOrigin, Equals, Everything, OriginTrait}, weights::RuntimeDbWeight, }; +use polkadot_parachain_primitives::primitives::Sibling; use sp_core::H256; use sp_runtime::{ testing::Header as SubstrateHeader, @@ -39,27 +40,29 @@ use sp_runtime::{ use sp_std::cell::RefCell; use xcm::prelude::*; use xcm_builder::{ - AllowUnpaidExecutionFrom, FixedWeightBounds, InspectMessageQueues, NetworkExportTable, - NetworkExportTableItem, + AllowUnpaidExecutionFrom, DispatchBlob, DispatchBlobError, FixedWeightBounds, + InspectMessageQueues, NetworkExportTable, NetworkExportTableItem, ParentIsPreset, + SiblingParachainConvertsVia, }; use xcm_executor::XcmExecutor; pub type AccountId = AccountId32; pub type Balance = u64; - type Block = frame_system::mocking::MockBlock; +/// Lane identifier type used for tests. +pub type TestLaneIdType = HashedLaneId; + pub const SIBLING_ASSET_HUB_ID: u32 = 2001; pub const THIS_BRIDGE_HUB_ID: u32 = 2002; pub const BRIDGED_ASSET_HUB_ID: u32 = 1001; -pub const TEST_LANE_ID: LaneId = LaneId([0, 0, 0, 1]); frame_support::construct_runtime! { pub enum TestRuntime { System: frame_system::{Pallet, Call, Config, Storage, Event}, Balances: pallet_balances::{Pallet, Event}, Messages: pallet_bridge_messages::{Pallet, Call, Event}, - XcmOverBridge: pallet_xcm_bridge_hub::{Pallet}, + XcmOverBridge: pallet_xcm_bridge_hub::{Pallet, Call, HoldReason, Event}, XcmOverBridgeRouter: pallet_xcm_bridge_hub_router, } } @@ -82,25 +85,23 @@ impl pallet_balances::Config for TestRuntime { type AccountStore = System; } -parameter_types! { - pub const ActiveOutboundLanes: &'static [LaneId] = &[TEST_LANE_ID]; -} - impl pallet_bridge_messages::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; type WeightInfo = TestMessagesWeights; - type ActiveOutboundLanes = ActiveOutboundLanes; + type ThisChain = ThisUnderlyingChain; + type BridgedChain = BridgedUnderlyingChain; + type BridgedHeaderChain = BridgedHeaderChain; + type OutboundPayload = Vec; type InboundPayload = Vec; + type LaneId = TestLaneIdType; + type DeliveryPayments = (); type DeliveryConfirmationPayments = (); type OnMessagesDelivered = (); - type MessageDispatch = TestMessageDispatch; - type ThisChain = ThisUnderlyingChain; - type BridgedChain = BridgedUnderlyingChain; - type BridgedHeaderChain = BridgedHeaderChain; + type MessageDispatch = TestMessageDispatch; } pub struct TestMessagesWeights; @@ -127,8 +128,8 @@ impl pallet_bridge_messages::WeightInfo for TestMessagesWeights { fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { Weight::zero() } - fn receive_single_n_bytes_message_proof_with_dispatch(_: u32) -> Weight { - Weight::zero() + fn receive_single_n_bytes_message_proof_with_dispatch(_n: u32) -> Weight { + Weight::from_parts(1, 0) } } @@ -153,15 +154,15 @@ parameter_types! { Parachain(THIS_BRIDGE_HUB_ID), ].into(); pub SiblingLocation: Location = Location::new(1, [Parachain(SIBLING_ASSET_HUB_ID)]); + pub SiblingUniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get()), Parachain(SIBLING_ASSET_HUB_ID)].into(); - pub const BridgedRelayNetwork: NetworkId = NetworkId::Polkadot; + pub const BridgedRelayNetwork: NetworkId = NetworkId::ByGenesis([1; 32]); pub BridgedRelayNetworkLocation: Location = (Parent, GlobalConsensus(BridgedRelayNetwork::get())).into(); pub BridgedRelativeDestination: InteriorLocation = [Parachain(BRIDGED_ASSET_HUB_ID)].into(); pub BridgedUniversalDestination: InteriorLocation = [GlobalConsensus(BridgedRelayNetwork::get()), Parachain(BRIDGED_ASSET_HUB_ID)].into(); pub const NonBridgedRelayNetwork: NetworkId = NetworkId::Rococo; pub const BridgeDeposit: Balance = 100_000; - pub const Penalty: Balance = 1_000; // configuration for pallet_xcm_bridge_hub_router pub BridgeHubLocation: Location = Here.into(); @@ -178,7 +179,14 @@ parameter_types! { pub UnitWeightCost: Weight = Weight::from_parts(10, 10); } +/// **Universal** `InteriorLocation` of bridged asset hub. +pub fn bridged_asset_hub_universal_location() -> InteriorLocation { + BridgedUniversalDestination::get() +} + impl pallet_xcm_bridge_hub::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type UniversalLocation = UniversalLocation; type BridgedNetwork = BridgedRelayNetworkLocation; type BridgeMessagesPalletInstance = (); @@ -186,25 +194,35 @@ impl pallet_xcm_bridge_hub::Config for TestRuntime { type MessageExportPrice = (); type DestinationVersion = AlwaysLatest; - type Lanes = TestLanes; - type LanesSupport = TestXcmBlobHauler; + type ForceOrigin = frame_system::EnsureNever<()>; + type OpenBridgeOrigin = OpenBridgeOrigin; + type BridgeOriginAccountIdConverter = LocationToAccountId; + + type BridgeDeposit = BridgeDeposit; + type Currency = Balances; + type RuntimeHoldReason = RuntimeHoldReason; + type AllowWithoutBridgeDeposit = Equals; + + type LocalXcmChannelManager = TestLocalXcmChannelManager; + + type BlobDispatcher = TestBlobDispatcher; } impl pallet_xcm_bridge_hub_router::Config<()> for TestRuntime { + type RuntimeEvent = RuntimeEvent; type WeightInfo = (); type UniversalLocation = UniversalLocation; + type SiblingBridgeHubLocation = BridgeHubLocation; type BridgedNetworkId = BridgedRelayNetwork; type Bridges = NetworkExportTable; type DestinationVersion = AlwaysLatest; - type BridgeHubOrigin = NeverEnsureOrigin; type ToBridgeHubSender = TestExportXcmWithXcmOverBridge; + type LocalXcmChannelManager = TestLocalXcmChannelManager; type ByteFee = ConstU128<0>; type FeeAsset = BridgeFeeAsset; - - type WithBridgeHubChannel = (); } pub struct XcmConfig; @@ -281,6 +299,10 @@ impl SendXcm for TestExportXcmWithXcmOverBridge { } } impl InspectMessageQueues for TestExportXcmWithXcmOverBridge { + fn clear_messages() { + todo!() + } + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { todo!() } @@ -291,29 +313,149 @@ impl TestExportXcmWithXcmOverBridge { } } +/// Type for specifying how a `Location` can be converted into an `AccountId`. This is used +/// when determining ownership of accounts for asset transacting and when attempting to use XCM +/// `Transact` in order to determine the dispatch Origin. +pub type LocationToAccountId = ( + // The parent (Relay-chain) origin converts to the parent `AccountId`. + ParentIsPreset, + // Sibling parachain origins convert to AccountId via the `ParaId::into`. + SiblingParachainConvertsVia, +); + parameter_types! { - pub TestSenderAndLane: SenderAndLane = SenderAndLane { - location: SiblingLocation::get(), - lane: TEST_LANE_ID, - }; - pub TestLanes: sp_std::vec::Vec<(SenderAndLane, (NetworkId, InteriorLocation))> = sp_std::vec![ - (TestSenderAndLane::get(), (BridgedRelayNetwork::get(), BridgedRelativeDestination::get())) - ]; + pub ParentRelayChainLocation: Location = Location { parents: 1, interior: Here }; } +pub struct OpenBridgeOrigin; + +impl OpenBridgeOrigin { + pub fn parent_relay_chain_origin() -> RuntimeOrigin { + RuntimeOrigin::signed([0u8; 32].into()) + } + + pub fn parent_relay_chain_universal_origin() -> RuntimeOrigin { + RuntimeOrigin::signed([1u8; 32].into()) + } -pub struct TestXcmBlobHauler; -impl XcmBlobHauler for TestXcmBlobHauler { - type Runtime = TestRuntime; - type MessagesInstance = (); - type ToSourceChainSender = (); - type CongestedMessage = (); - type UncongestedMessage = (); + pub fn sibling_parachain_origin() -> RuntimeOrigin { + let mut account = [0u8; 32]; + account[..4].copy_from_slice(&SIBLING_ASSET_HUB_ID.encode()[..4]); + RuntimeOrigin::signed(account.into()) + } + + pub fn sibling_parachain_universal_origin() -> RuntimeOrigin { + RuntimeOrigin::signed([2u8; 32].into()) + } + + pub fn origin_without_sovereign_account() -> RuntimeOrigin { + RuntimeOrigin::signed([3u8; 32].into()) + } + + pub fn disallowed_origin() -> RuntimeOrigin { + RuntimeOrigin::signed([42u8; 32].into()) + } +} + +impl EnsureOrigin for OpenBridgeOrigin { + type Success = Location; + + fn try_origin(o: RuntimeOrigin) -> Result { + let signer = o.clone().into_signer(); + if signer == Self::parent_relay_chain_origin().into_signer() { + return Ok(ParentRelayChainLocation::get()) + } else if signer == Self::parent_relay_chain_universal_origin().into_signer() { + return Ok(Location { + parents: 2, + interior: GlobalConsensus(RelayNetwork::get()).into(), + }) + } else if signer == Self::sibling_parachain_universal_origin().into_signer() { + return Ok(Location { + parents: 2, + interior: [GlobalConsensus(RelayNetwork::get()), Parachain(SIBLING_ASSET_HUB_ID)] + .into(), + }) + } else if signer == Self::origin_without_sovereign_account().into_signer() { + return Ok(Location { + parents: 1, + interior: [Parachain(SIBLING_ASSET_HUB_ID), OnlyChild].into(), + }) + } + + let mut sibling_account = [0u8; 32]; + sibling_account[..4].copy_from_slice(&SIBLING_ASSET_HUB_ID.encode()[..4]); + if signer == Some(sibling_account.into()) { + return Ok(Location { parents: 1, interior: Parachain(SIBLING_ASSET_HUB_ID).into() }) + } + + Err(o) + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin() -> Result { + Ok(Self::parent_relay_chain_origin()) + } +} + +pub struct TestLocalXcmChannelManager; + +impl TestLocalXcmChannelManager { + pub fn make_congested() { + frame_support::storage::unhashed::put(b"TestLocalXcmChannelManager.Congested", &true); + } + + pub fn is_bridge_suspened() -> bool { + frame_support::storage::unhashed::get_or_default(b"TestLocalXcmChannelManager.Suspended") + } + + pub fn is_bridge_resumed() -> bool { + frame_support::storage::unhashed::get_or_default(b"TestLocalXcmChannelManager.Resumed") + } +} + +impl LocalXcmChannelManager for TestLocalXcmChannelManager { + type Error = (); + + fn is_congested(_with: &Location) -> bool { + frame_support::storage::unhashed::get_or_default(b"TestLocalXcmChannelManager.Congested") + } + + fn suspend_bridge(_local_origin: &Location, _bridge: BridgeId) -> Result<(), Self::Error> { + frame_support::storage::unhashed::put(b"TestLocalXcmChannelManager.Suspended", &true); + Ok(()) + } + + fn resume_bridge(_local_origin: &Location, _bridge: BridgeId) -> Result<(), Self::Error> { + frame_support::storage::unhashed::put(b"TestLocalXcmChannelManager.Resumed", &true); + Ok(()) + } +} + +impl pallet_xcm_bridge_hub_router::XcmChannelStatusProvider for TestLocalXcmChannelManager { + fn is_congested(with: &Location) -> bool { + ::is_congested(with) + } +} + +pub struct TestBlobDispatcher; + +impl TestBlobDispatcher { + pub fn is_dispatched() -> bool { + frame_support::storage::unhashed::get_or_default(b"TestBlobDispatcher.Dispatched") + } +} + +impl DispatchBlob for TestBlobDispatcher { + fn dispatch_blob(_blob: Vec) -> Result<(), DispatchBlobError> { + frame_support::storage::unhashed::put(b"TestBlobDispatcher.Dispatched", &true); + Ok(()) + } } pub struct ThisUnderlyingChain; impl Chain for ThisUnderlyingChain { const ID: ChainId = *b"tuch"; + type BlockNumber = u64; type Hash = H256; type Hasher = BlakeTwo256; @@ -335,16 +477,15 @@ impl Chain for ThisUnderlyingChain { } impl ChainWithMessages for ThisUnderlyingChain { - const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = ""; - + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = "WithThisChainBridgeMessages"; const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 16; - const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 1000; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 128; } -pub struct BridgedUnderlyingChain; pub type BridgedHeaderHash = H256; pub type BridgedChainHeader = SubstrateHeader; +pub struct BridgedUnderlyingChain; impl Chain for BridgedUnderlyingChain { const ID: ChainId = *b"bgdc"; type BlockNumber = u64; @@ -368,16 +509,25 @@ impl Chain for BridgedUnderlyingChain { } impl ChainWithMessages for BridgedUnderlyingChain { - const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = ""; + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = "WithBridgedChainBridgeMessages"; const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 16; - const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 1000; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 128; +} + +pub struct BridgedHeaderChain; +impl bp_header_chain::HeaderChain for BridgedHeaderChain { + fn finalized_header_state_root( + _hash: HashOf, + ) -> Option> { + unreachable!() + } } /// Test message dispatcher. pub struct TestMessageDispatch; impl TestMessageDispatch { - pub fn deactivate(lane: LaneId) { + pub fn deactivate(lane: TestLaneIdType) { frame_support::storage::unhashed::put(&(b"inactive", lane).encode()[..], &false); } } @@ -385,31 +535,26 @@ impl TestMessageDispatch { impl MessageDispatch for TestMessageDispatch { type DispatchPayload = Vec; type DispatchLevelResult = (); + type LaneId = TestLaneIdType; - fn is_active() -> bool { - frame_support::storage::unhashed::take::(&(b"inactive").encode()[..]) != Some(false) + fn is_active(lane: Self::LaneId) -> bool { + frame_support::storage::unhashed::take::(&(b"inactive", lane).encode()[..]) != + Some(false) } - fn dispatch_weight(_message: &mut DispatchMessage) -> Weight { + fn dispatch_weight( + _message: &mut DispatchMessage, + ) -> Weight { Weight::zero() } fn dispatch( - _: DispatchMessage, + _: DispatchMessage, ) -> MessageDispatchResult { MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result: () } } } -pub struct BridgedHeaderChain; -impl bp_header_chain::HeaderChain for BridgedHeaderChain { - fn finalized_header_state_root( - _hash: HashOf, - ) -> Option> { - unreachable!() - } -} - /// Run pallet test. pub fn run_test(test: impl FnOnce() -> T) -> T { sp_io::TestExternalities::new( diff --git a/bridges/primitives/header-chain/src/call_info.rs b/bridges/primitives/header-chain/src/call_info.rs new file mode 100644 index 0000000000000000000000000000000000000000..acf7447adabc764b5205f5ad03c3f0a2609d9784 --- /dev/null +++ b/bridges/primitives/header-chain/src/call_info.rs @@ -0,0 +1,94 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Defines structures related to calls of the `pallet-bridge-grandpa` pallet. + +use crate::{justification, InitializationData}; + +use bp_runtime::HeaderOf; +use codec::{Decode, Encode}; +use frame_support::{weights::Weight, RuntimeDebugNoBound}; +use scale_info::TypeInfo; +use sp_consensus_grandpa::SetId; +use sp_runtime::traits::{Header as HeaderT, Zero}; +use sp_std::{boxed::Box, fmt::Debug}; + +/// A minimized version of `pallet-bridge-grandpa::Call` that can be used without a runtime. +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum BridgeGrandpaCall { + /// `pallet-bridge-grandpa::Call::submit_finality_proof` + #[codec(index = 0)] + submit_finality_proof { + /// The header that we are going to finalize. + finality_target: Box
, + /// Finality justification for the `finality_target`. + justification: justification::GrandpaJustification
, + }, + /// `pallet-bridge-grandpa::Call::initialize` + #[codec(index = 1)] + initialize { + /// All data, required to initialize the pallet. + init_data: InitializationData
, + }, + /// `pallet-bridge-grandpa::Call::submit_finality_proof_ex` + #[codec(index = 4)] + submit_finality_proof_ex { + /// The header that we are going to finalize. + finality_target: Box
, + /// Finality justification for the `finality_target`. + justification: justification::GrandpaJustification
, + /// An identifier of the validators set, that have signed the justification. + current_set_id: SetId, + }, +} + +/// The `BridgeGrandpaCall` for a pallet that bridges with given `C`; +pub type BridgeGrandpaCallOf = BridgeGrandpaCall>; + +/// A digest information on the `BridgeGrandpaCall::submit_finality_proof` call. +#[derive(Copy, Clone, PartialEq, RuntimeDebugNoBound)] +pub struct SubmitFinalityProofInfo { + /// Number of the finality target. + pub block_number: N, + /// An identifier of the validators set that has signed the submitted justification. + /// It might be `None` if deprecated version of the `submit_finality_proof` is used. + pub current_set_id: Option, + /// If `true`, then the call proves new **mandatory** header. + pub is_mandatory: bool, + /// If `true`, then the call must be free (assuming that everything else is valid) to + /// be treated as valid. + pub is_free_execution_expected: bool, + /// Extra weight that we assume is included in the call. + /// + /// We have some assumptions about headers and justifications of the bridged chain. + /// We know that if our assumptions are correct, then the call must not have the + /// weight above some limit. The fee paid for weight above that limit, is never refunded. + pub extra_weight: Weight, + /// Extra size (in bytes) that we assume are included in the call. + /// + /// We have some assumptions about headers and justifications of the bridged chain. + /// We know that if our assumptions are correct, then the call must not have the + /// weight above some limit. The fee paid for bytes above that limit, is never refunded. + pub extra_size: u32, +} + +impl SubmitFinalityProofInfo { + /// Returns `true` if call size/weight is below our estimations for regular calls. + pub fn fits_limits(&self) -> bool { + self.extra_weight.is_zero() && self.extra_size.is_zero() + } +} diff --git a/bridges/primitives/header-chain/src/justification/mod.rs b/bridges/primitives/header-chain/src/justification/mod.rs index d7c2cbf429e2b01efe4a9ea2481e66e2857d0044..87f53dac64638846f1a9c447aba4268e74cfa0f7 100644 --- a/bridges/primitives/header-chain/src/justification/mod.rs +++ b/bridges/primitives/header-chain/src/justification/mod.rs @@ -32,7 +32,6 @@ pub use verification::{ use bp_runtime::{BlockNumberOf, Chain, HashOf, HeaderId}; use codec::{Decode, Encode, MaxEncodedLen}; -use frame_support::RuntimeDebugNoBound; use scale_info::TypeInfo; use sp_consensus_grandpa::{AuthorityId, AuthoritySignature}; use sp_runtime::{traits::Header as HeaderT, RuntimeDebug, SaturatedConversion}; @@ -43,7 +42,8 @@ use sp_std::prelude::*; /// /// This particular proof is used to prove that headers on a bridged chain /// (so not our chain) have been finalized correctly. -#[derive(Encode, Decode, Clone, PartialEq, Eq, TypeInfo, RuntimeDebugNoBound)] +#[derive(Encode, Decode, Clone, PartialEq, Eq, TypeInfo)] +#[cfg_attr(feature = "std", derive(Debug))] pub struct GrandpaJustification { /// The round (voting period) this justification is valid for. pub round: u64, @@ -54,6 +54,17 @@ pub struct GrandpaJustification { pub votes_ancestries: Vec
, } +// A proper Debug impl for no-std is not possible for the `GrandpaJustification` since the `Commit` +// type only implements Debug that for `std` here: +// https://github.com/paritytech/finality-grandpa/blob/8c45a664c05657f0c71057158d3ba555ba7d20de/src/lib.rs#L275 +// so we do a manual impl. +#[cfg(not(feature = "std"))] +impl core::fmt::Debug for GrandpaJustification { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, "GrandpaJustification {{ round: {:?}, commit: , votes_ancestries: {:?} }}", self.round, self.votes_ancestries) + } +} + impl GrandpaJustification { /// Returns reasonable size of justification using constants from the provided chain. /// diff --git a/bridges/primitives/header-chain/src/lib.rs b/bridges/primitives/header-chain/src/lib.rs index 26295dee1801a127f455ed3288bd5232cb60bc10..48326bf5c19d5a5cfcccab0c69ab2949978418e6 100644 --- a/bridges/primitives/header-chain/src/lib.rs +++ b/bridges/primitives/header-chain/src/lib.rs @@ -38,6 +38,10 @@ use sp_consensus_grandpa::{ use sp_runtime::{traits::Header as HeaderT, Digest, RuntimeDebug, SaturatedConversion}; use sp_std::{boxed::Box, vec::Vec}; +pub use call_info::{BridgeGrandpaCall, BridgeGrandpaCallOf, SubmitFinalityProofInfo}; + +mod call_info; + pub mod justification; pub mod storage_keys; @@ -228,39 +232,6 @@ pub trait FindEquivocations Result, Self::Error>; } -/// A minimized version of `pallet-bridge-grandpa::Call` that can be used without a runtime. -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -#[allow(non_camel_case_types)] -pub enum BridgeGrandpaCall { - /// `pallet-bridge-grandpa::Call::submit_finality_proof` - #[codec(index = 0)] - submit_finality_proof { - /// The header that we are going to finalize. - finality_target: Box
, - /// Finality justification for the `finality_target`. - justification: justification::GrandpaJustification
, - }, - /// `pallet-bridge-grandpa::Call::initialize` - #[codec(index = 1)] - initialize { - /// All data, required to initialize the pallet. - init_data: InitializationData
, - }, - /// `pallet-bridge-grandpa::Call::submit_finality_proof_ex` - #[codec(index = 4)] - submit_finality_proof_ex { - /// The header that we are going to finalize. - finality_target: Box
, - /// Finality justification for the `finality_target`. - justification: justification::GrandpaJustification
, - /// An identifier of the validators set, that have signed the justification. - current_set_id: SetId, - }, -} - -/// The `BridgeGrandpaCall` used by a chain. -pub type BridgeGrandpaCallOf = BridgeGrandpaCall>; - /// Substrate-based chain that is using direct GRANDPA finality. /// /// Keep in mind that parachains are relying on relay chain GRANDPA, so they should not implement diff --git a/bridges/primitives/messages/Cargo.toml b/bridges/primitives/messages/Cargo.toml index 4a9037342bcea66d813cdb79969c4ff3172f0bab..87c8cbe881803522888fd73497e80d9fd2cb2e08 100644 --- a/bridges/primitives/messages/Cargo.toml +++ b/bridges/primitives/messages/Cargo.toml @@ -16,19 +16,19 @@ scale-info = { features = ["bit-vec", "derive"], workspace = true } serde = { features = ["alloc", "derive"], workspace = true } # Bridge dependencies - bp-runtime = { workspace = true } bp-header-chain = { workspace = true } # Substrate Dependencies - frame-support = { workspace = true } sp-core = { workspace = true } sp-std = { workspace = true } +sp-io = { workspace = true } [dev-dependencies] hex = { workspace = true, default-features = true } hex-literal = { workspace = true, default-features = true } +bp-runtime = { workspace = true } [features] default = ["std"] @@ -40,5 +40,6 @@ std = [ "scale-info/std", "serde/std", "sp-core/std", + "sp-io/std", "sp-std/std", ] diff --git a/bridges/primitives/messages/src/call_info.rs b/bridges/primitives/messages/src/call_info.rs new file mode 100644 index 0000000000000000000000000000000000000000..dfd076f029b471349c3fcc02880aa76db8b53d2c --- /dev/null +++ b/bridges/primitives/messages/src/call_info.rs @@ -0,0 +1,164 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Defines structures related to calls of the `pallet-bridge-messages` pallet. + +use crate::{MessageNonce, UnrewardedRelayersState}; + +use codec::{Decode, Encode}; +use frame_support::weights::Weight; +use scale_info::TypeInfo; +use sp_core::RuntimeDebug; +use sp_std::ops::RangeInclusive; + +/// A minimized version of `pallet-bridge-messages::Call` that can be used without a runtime. +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum BridgeMessagesCall { + /// `pallet-bridge-messages::Call::receive_messages_proof` + #[codec(index = 2)] + receive_messages_proof { + /// Account id of relayer at the **bridged** chain. + relayer_id_at_bridged_chain: AccountId, + /// Messages proof. + proof: MessagesProof, + /// A number of messages in the proof. + messages_count: u32, + /// Total dispatch weight of messages in the proof. + dispatch_weight: Weight, + }, + /// `pallet-bridge-messages::Call::receive_messages_delivery_proof` + #[codec(index = 3)] + receive_messages_delivery_proof { + /// Messages delivery proof. + proof: MessagesDeliveryProof, + /// "Digest" of unrewarded relayers state at the bridged chain. + relayers_state: UnrewardedRelayersState, + }, +} + +/// Generic info about a messages delivery/confirmation proof. +#[derive(PartialEq, RuntimeDebug)] +pub struct BaseMessagesProofInfo { + /// Message lane, used by the call. + pub lane_id: LaneId, + /// Nonces of messages, included in the call. + /// + /// For delivery transaction, it is nonces of bundled messages. For confirmation + /// transaction, it is nonces that are to be confirmed during the call. + pub bundled_range: RangeInclusive, + /// Nonce of the best message, stored by this chain before the call is dispatched. + /// + /// For delivery transaction, it is the nonce of best delivered message before the call. + /// For confirmation transaction, it is the nonce of best confirmed message before the call. + pub best_stored_nonce: MessageNonce, +} + +impl BaseMessagesProofInfo { + /// Returns true if `bundled_range` continues the `0..=best_stored_nonce` range. + pub fn appends_to_stored_nonce(&self) -> bool { + Some(*self.bundled_range.start()) == self.best_stored_nonce.checked_add(1) + } +} + +/// Occupation state of the unrewarded relayers vector. +#[derive(PartialEq, RuntimeDebug)] +#[cfg_attr(test, derive(Default))] +pub struct UnrewardedRelayerOccupation { + /// The number of remaining unoccupied entries for new relayers. + pub free_relayer_slots: MessageNonce, + /// The number of messages that we are ready to accept. + pub free_message_slots: MessageNonce, +} + +/// Info about a `ReceiveMessagesProof` call which tries to update a single lane. +#[derive(PartialEq, RuntimeDebug)] +pub struct ReceiveMessagesProofInfo { + /// Base messages proof info + pub base: BaseMessagesProofInfo, + /// State of unrewarded relayers vector. + pub unrewarded_relayers: UnrewardedRelayerOccupation, +} + +impl ReceiveMessagesProofInfo { + /// Returns true if: + /// + /// - either inbound lane is ready to accept bundled messages; + /// + /// - or there are no bundled messages, but the inbound lane is blocked by too many unconfirmed + /// messages and/or unrewarded relayers. + pub fn is_obsolete(&self, is_dispatcher_active: bool) -> bool { + // if dispatcher is inactive, we don't accept any delivery transactions + if !is_dispatcher_active { + return true + } + + // transactions with zero bundled nonces are not allowed, unless they're message + // delivery transactions, which brings reward confirmations required to unblock + // the lane + if self.base.bundled_range.is_empty() { + let empty_transactions_allowed = + // we allow empty transactions when we can't accept delivery from new relayers + self.unrewarded_relayers.free_relayer_slots == 0 || + // or if we can't accept new messages at all + self.unrewarded_relayers.free_message_slots == 0; + + return !empty_transactions_allowed + } + + // otherwise we require bundled messages to continue stored range + !self.base.appends_to_stored_nonce() + } +} + +/// Info about a `ReceiveMessagesDeliveryProof` call which tries to update a single lane. +#[derive(PartialEq, RuntimeDebug)] +pub struct ReceiveMessagesDeliveryProofInfo(pub BaseMessagesProofInfo); + +impl ReceiveMessagesDeliveryProofInfo { + /// Returns true if outbound lane is ready to accept confirmations of bundled messages. + pub fn is_obsolete(&self) -> bool { + self.0.bundled_range.is_empty() || !self.0.appends_to_stored_nonce() + } +} + +/// Info about a `ReceiveMessagesProof` or a `ReceiveMessagesDeliveryProof` call +/// which tries to update a single lane. +#[derive(PartialEq, RuntimeDebug)] +pub enum MessagesCallInfo { + /// Messages delivery call info. + ReceiveMessagesProof(ReceiveMessagesProofInfo), + /// Messages delivery confirmation call info. + ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo), +} + +impl MessagesCallInfo { + /// Returns lane, used by the call. + pub fn lane_id(&self) -> LaneId { + match *self { + Self::ReceiveMessagesProof(ref info) => info.base.lane_id, + Self::ReceiveMessagesDeliveryProof(ref info) => info.0.lane_id, + } + } + + /// Returns range of messages, bundled with the call. + pub fn bundled_messages(&self) -> RangeInclusive { + match *self { + Self::ReceiveMessagesProof(ref info) => info.base.bundled_range.clone(), + Self::ReceiveMessagesDeliveryProof(ref info) => info.0.bundled_range.clone(), + } + } +} diff --git a/bridges/primitives/messages/src/lane.rs b/bridges/primitives/messages/src/lane.rs new file mode 100644 index 0000000000000000000000000000000000000000..0f14ce93e11425ff8d2dd69165159ed203e72f03 --- /dev/null +++ b/bridges/primitives/messages/src/lane.rs @@ -0,0 +1,334 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Primitives of messages module, that represents lane id. + +use codec::{Codec, Decode, Encode, EncodeLike, MaxEncodedLen}; +use scale_info::TypeInfo; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use sp_core::{RuntimeDebug, TypeId, H256}; +use sp_io::hashing::blake2_256; +use sp_std::fmt::Debug; + +/// Trait representing a generic `LaneId` type. +pub trait LaneIdType: + Clone + + Copy + + Codec + + EncodeLike + + Debug + + Default + + PartialEq + + Eq + + Ord + + TypeInfo + + MaxEncodedLen + + Serialize + + DeserializeOwned +{ + /// Creates a new `LaneId` type (if supported). + fn try_new(endpoint1: E, endpoint2: E) -> Result; +} + +/// Bridge lane identifier (legacy). +/// +/// Note: For backwards compatibility reasons, we also handle the older format `[u8; 4]`. +#[derive( + Clone, + Copy, + Decode, + Default, + Encode, + Eq, + Ord, + PartialOrd, + PartialEq, + TypeInfo, + MaxEncodedLen, + Serialize, + Deserialize, +)] +pub struct LegacyLaneId(pub [u8; 4]); + +impl LaneIdType for LegacyLaneId { + /// Create lane identifier from two locations. + fn try_new(_endpoint1: T, _endpoint2: T) -> Result { + // we don't support this for `LegacyLaneId`, because it was hard-coded before + Err(()) + } +} + +#[cfg(feature = "std")] +impl TryFrom> for LegacyLaneId { + type Error = (); + + fn try_from(value: Vec) -> Result { + if value.len() == 4 { + return <[u8; 4]>::try_from(value).map(Self).map_err(|_| ()); + } + Err(()) + } +} + +impl core::fmt::Debug for LegacyLaneId { + fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { + self.0.fmt(fmt) + } +} + +impl AsRef<[u8]> for LegacyLaneId { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl TypeId for LegacyLaneId { + const TYPE_ID: [u8; 4] = *b"blan"; +} + +/// Bridge lane identifier. +/// +/// Lane connects two endpoints at both sides of the bridge. We assume that every endpoint +/// has its own unique identifier. We want lane identifiers to be **the same on the both sides +/// of the bridge** (and naturally unique across global consensus if endpoints have unique +/// identifiers). So lane id is the hash (`blake2_256`) of **ordered** encoded locations +/// concatenation (separated by some binary data). I.e.: +/// +/// ```nocompile +/// let endpoint1 = X2(GlobalConsensus(NetworkId::Rococo), Parachain(42)); +/// let endpoint2 = X2(GlobalConsensus(NetworkId::Wococo), Parachain(777)); +/// +/// let final_lane_key = if endpoint1 < endpoint2 { +/// (endpoint1, VALUES_SEPARATOR, endpoint2) +/// } else { +/// (endpoint2, VALUES_SEPARATOR, endpoint1) +/// }.using_encoded(blake2_256); +/// ``` +#[derive( + Clone, + Copy, + Decode, + Default, + Encode, + Eq, + Ord, + PartialOrd, + PartialEq, + TypeInfo, + MaxEncodedLen, + Serialize, + Deserialize, +)] +pub struct HashedLaneId(H256); + +impl HashedLaneId { + /// Create lane identifier from given hash. + /// + /// There's no `From` implementation for the `LaneId`, because using this conversion + /// in a wrong way (i.e. computing hash of endpoints manually) may lead to issues. So we + /// want the call to be explicit. + #[cfg(feature = "std")] + pub const fn from_inner(inner: H256) -> Self { + Self(inner) + } + + /// Access the inner lane representation. + pub fn inner(&self) -> &H256 { + &self.0 + } +} + +impl core::fmt::Display for HashedLaneId { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + core::fmt::Display::fmt(&self.0, f) + } +} + +impl core::fmt::Debug for HashedLaneId { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + core::fmt::Debug::fmt(&self.0, f) + } +} + +impl TypeId for HashedLaneId { + const TYPE_ID: [u8; 4] = *b"hlan"; +} + +impl LaneIdType for HashedLaneId { + /// Create lane identifier from two locations. + fn try_new(endpoint1: T, endpoint2: T) -> Result { + const VALUES_SEPARATOR: [u8; 31] = *b"bridges-lane-id-value-separator"; + + Ok(Self( + if endpoint1 < endpoint2 { + (endpoint1, VALUES_SEPARATOR, endpoint2) + } else { + (endpoint2, VALUES_SEPARATOR, endpoint1) + } + .using_encoded(blake2_256) + .into(), + )) + } +} + +#[cfg(feature = "std")] +impl TryFrom> for HashedLaneId { + type Error = (); + + fn try_from(value: Vec) -> Result { + if value.len() == 32 { + return <[u8; 32]>::try_from(value).map(|v| Self(H256::from(v))).map_err(|_| ()); + } + Err(()) + } +} + +/// Lane state. +#[derive(Clone, Copy, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen, RuntimeDebug)] +pub enum LaneState { + /// Lane is opened and messages may be sent/received over it. + Opened, + /// Lane is closed and all attempts to send/receive messages to/from this lane + /// will fail. + /// + /// Keep in mind that the lane has two ends and the state of the same lane at + /// its ends may be different. Those who are controlling/serving the lane + /// and/or sending messages over the lane, have to coordinate their actions on + /// both ends to make sure that lane is operating smoothly on both ends. + Closed, +} + +impl LaneState { + /// Returns true if lane state allows sending/receiving messages. + pub fn is_active(&self) -> bool { + matches!(*self, LaneState::Opened) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::MessageNonce; + + #[test] + fn lane_id_debug_format_matches_inner_hash_format() { + assert_eq!( + format!("{:?}", HashedLaneId(H256::from([1u8; 32]))), + format!("{:?}", H256::from([1u8; 32])), + ); + assert_eq!(format!("{:?}", LegacyLaneId([0, 0, 0, 1])), format!("{:?}", [0, 0, 0, 1]),); + } + + #[test] + fn hashed_encode_decode_works() { + // simple encode/decode - new format + let lane_id = HashedLaneId(H256::from([1u8; 32])); + let encoded_lane_id = lane_id.encode(); + let decoded_lane_id = HashedLaneId::decode(&mut &encoded_lane_id[..]).expect("decodable"); + assert_eq!(lane_id, decoded_lane_id); + assert_eq!( + "0101010101010101010101010101010101010101010101010101010101010101", + hex::encode(encoded_lane_id) + ); + } + + #[test] + fn legacy_encode_decode_works() { + // simple encode/decode - old format + let lane_id = LegacyLaneId([0, 0, 0, 1]); + let encoded_lane_id = lane_id.encode(); + let decoded_lane_id = LegacyLaneId::decode(&mut &encoded_lane_id[..]).expect("decodable"); + assert_eq!(lane_id, decoded_lane_id); + assert_eq!("00000001", hex::encode(encoded_lane_id)); + + // decode sample + let bytes = vec![0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]; + let (lane, nonce_start, nonce_end): (LegacyLaneId, MessageNonce, MessageNonce) = + Decode::decode(&mut &bytes[..]).unwrap(); + assert_eq!(lane, LegacyLaneId([0, 0, 0, 2])); + assert_eq!(nonce_start, 1); + assert_eq!(nonce_end, 1); + + // run encode/decode for `LaneId` with different positions + let expected_lane = LegacyLaneId([0, 0, 0, 1]); + let expected_nonce_start = 1088_u64; + let expected_nonce_end = 9185_u64; + + // decode: LaneId,Nonce,Nonce + let bytes = (expected_lane, expected_nonce_start, expected_nonce_end).encode(); + let (lane, nonce_start, nonce_end): (LegacyLaneId, MessageNonce, MessageNonce) = + Decode::decode(&mut &bytes[..]).unwrap(); + assert_eq!(lane, expected_lane); + assert_eq!(nonce_start, expected_nonce_start); + assert_eq!(nonce_end, expected_nonce_end); + + // decode: Nonce,LaneId,Nonce + let bytes = (expected_nonce_start, expected_lane, expected_nonce_end).encode(); + let (nonce_start, lane, nonce_end): (MessageNonce, LegacyLaneId, MessageNonce) = + Decode::decode(&mut &bytes[..]).unwrap(); + assert_eq!(lane, expected_lane); + assert_eq!(nonce_start, expected_nonce_start); + assert_eq!(nonce_end, expected_nonce_end); + + // decode: Nonce,Nonce,LaneId + let bytes = (expected_nonce_start, expected_nonce_end, expected_lane).encode(); + let (nonce_start, nonce_end, lane): (MessageNonce, MessageNonce, LegacyLaneId) = + Decode::decode(&mut &bytes[..]).unwrap(); + assert_eq!(lane, expected_lane); + assert_eq!(nonce_start, expected_nonce_start); + assert_eq!(nonce_end, expected_nonce_end); + } + + #[test] + fn hashed_lane_id_is_generated_using_ordered_endpoints() { + assert_eq!(HashedLaneId::try_new(1, 2).unwrap(), HashedLaneId::try_new(2, 1).unwrap()); + } + + #[test] + fn hashed_lane_id_is_different_for_different_endpoints() { + assert_ne!(HashedLaneId::try_new(1, 2).unwrap(), HashedLaneId::try_new(1, 3).unwrap()); + } + + #[test] + fn hashed_lane_id_is_different_even_if_arguments_has_partial_matching_encoding() { + /// Some artificial type that generates the same encoding for different values + /// concatenations. I.e. the encoding for `(Either::Two(1, 2), Either::Two(3, 4))` + /// is the same as encoding of `(Either::Three(1, 2, 3), Either::One(4))`. + /// In practice, this type is not useful, because you can't do a proper decoding. + /// But still there may be some collisions even in proper types. + #[derive(Eq, Ord, PartialEq, PartialOrd)] + enum Either { + Three(u64, u64, u64), + Two(u64, u64), + One(u64), + } + + impl codec::Encode for Either { + fn encode(&self) -> Vec { + match *self { + Self::One(a) => a.encode(), + Self::Two(a, b) => (a, b).encode(), + Self::Three(a, b, c) => (a, b, c).encode(), + } + } + } + + assert_ne!( + HashedLaneId::try_new(Either::Two(1, 2), Either::Two(3, 4)).unwrap(), + HashedLaneId::try_new(Either::Three(1, 2, 3), Either::One(4)).unwrap(), + ); + } +} diff --git a/bridges/primitives/messages/src/lib.rs b/bridges/primitives/messages/src/lib.rs index 9984f8ac3222780ea2fd6af56dd896beb264b2d5..2776b806cc16bf30fb09c06d904c05f6bcea21d0 100644 --- a/bridges/primitives/messages/src/lib.rs +++ b/bridges/primitives/messages/src/lib.rs @@ -31,9 +31,17 @@ pub use frame_support::weights::Weight; use scale_info::TypeInfo; use serde::{Deserialize, Serialize}; use source_chain::RelayersRewards; -use sp_core::{RuntimeDebug, TypeId}; +use sp_core::RuntimeDebug; use sp_std::{collections::vec_deque::VecDeque, ops::RangeInclusive, prelude::*}; +pub use call_info::{ + BaseMessagesProofInfo, BridgeMessagesCall, MessagesCallInfo, ReceiveMessagesDeliveryProofInfo, + ReceiveMessagesProofInfo, UnrewardedRelayerOccupation, +}; +pub use lane::{HashedLaneId, LaneIdType, LaneState, LegacyLaneId}; + +mod call_info; +mod lane; pub mod source_chain; pub mod storage_keys; pub mod target_chain; @@ -165,52 +173,15 @@ impl OperatingMode for MessagesOperatingMode { } } -/// Lane id which implements `TypeId`. -#[derive( - Clone, - Copy, - Decode, - Default, - Encode, - Eq, - Ord, - PartialOrd, - PartialEq, - TypeInfo, - MaxEncodedLen, - Serialize, - Deserialize, -)] -pub struct LaneId(pub [u8; 4]); - -impl core::fmt::Debug for LaneId { - fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { - self.0.fmt(fmt) - } -} - -impl AsRef<[u8]> for LaneId { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -impl TypeId for LaneId { - const TYPE_ID: [u8; 4] = *b"blan"; -} - /// Message nonce. Valid messages will never have 0 nonce. pub type MessageNonce = u64; -/// Message id as a tuple. -pub type BridgeMessageId = (LaneId, MessageNonce); - /// Opaque message payload. We only decode this payload when it is dispatched. pub type MessagePayload = Vec; /// Message key (unique message identifier) as it is stored in the storage. #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] -pub struct MessageKey { +pub struct MessageKey { /// ID of the message lane. pub lane_id: LaneId, /// Message nonce. @@ -219,9 +190,9 @@ pub struct MessageKey { /// Message as it is stored in the storage. #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] -pub struct Message { +pub struct Message { /// Message key. - pub key: MessageKey, + pub key: MessageKey, /// Message payload. pub payload: MessagePayload, } @@ -257,15 +228,29 @@ pub struct InboundLaneData { /// This value is updated indirectly when an `OutboundLane` state of the source /// chain is received alongside with new messages delivery. pub last_confirmed_nonce: MessageNonce, + + /// Inbound lane state. + /// + /// If state is `Closed`, then all attempts to deliver messages to this end will fail. + pub state: LaneState, } impl Default for InboundLaneData { fn default() -> Self { - InboundLaneData { relayers: VecDeque::new(), last_confirmed_nonce: 0 } + InboundLaneData { + state: LaneState::Closed, + relayers: VecDeque::new(), + last_confirmed_nonce: 0, + } } } impl InboundLaneData { + /// Returns default inbound lane data with opened state. + pub fn opened() -> Self { + InboundLaneData { state: LaneState::Opened, ..Default::default() } + } + /// Returns approximate size of the struct, given a number of entries in the `relayers` set and /// size of each entry. /// @@ -351,21 +336,21 @@ pub struct UnrewardedRelayer { } /// Received messages with their dispatch result. -#[derive(Clone, Default, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] -pub struct ReceivedMessages { +#[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] +pub struct ReceivedMessages { /// Id of the lane which is receiving messages. pub lane: LaneId, /// Result of messages which we tried to dispatch pub receive_results: Vec<(MessageNonce, ReceptionResult)>, } -impl ReceivedMessages { +impl ReceivedMessages { /// Creates new `ReceivedMessages` structure from given results. pub fn new( lane: LaneId, receive_results: Vec<(MessageNonce, ReceptionResult)>, ) -> Self { - ReceivedMessages { lane, receive_results } + ReceivedMessages { lane: lane.into(), receive_results } } /// Push `result` of the `message` delivery onto `receive_results` vector. @@ -471,11 +456,23 @@ pub struct OutboundLaneData { pub latest_received_nonce: MessageNonce, /// Nonce of the latest message, generated by us. pub latest_generated_nonce: MessageNonce, + /// Lane state. + /// + /// If state is `Closed`, then all attempts to send messages at this end will fail. + pub state: LaneState, +} + +impl OutboundLaneData { + /// Returns default outbound lane data with opened state. + pub fn opened() -> Self { + OutboundLaneData { state: LaneState::Opened, ..Default::default() } + } } impl Default for OutboundLaneData { fn default() -> Self { OutboundLaneData { + state: LaneState::Closed, // it is 1 because we're pruning everything in [oldest_unpruned_nonce; // latest_received_nonce] oldest_unpruned_nonce: 1, @@ -514,32 +511,6 @@ where relayers_rewards } -/// A minimized version of `pallet-bridge-messages::Call` that can be used without a runtime. -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -#[allow(non_camel_case_types)] -pub enum BridgeMessagesCall { - /// `pallet-bridge-messages::Call::receive_messages_proof` - #[codec(index = 2)] - receive_messages_proof { - /// Account id of relayer at the **bridged** chain. - relayer_id_at_bridged_chain: AccountId, - /// Messages proof. - proof: MessagesProof, - /// A number of messages in the proof. - messages_count: u32, - /// Total dispatch weight of messages in the proof. - dispatch_weight: Weight, - }, - /// `pallet-bridge-messages::Call::receive_messages_delivery_proof` - #[codec(index = 3)] - receive_messages_delivery_proof { - /// Messages delivery proof. - proof: MessagesDeliveryProof, - /// "Digest" of unrewarded relayers state at the bridged chain. - relayers_state: UnrewardedRelayersState, - }, -} - /// Error that happens during message verification. #[derive(Encode, Decode, RuntimeDebug, PartialEq, Eq, PalletError, TypeInfo)] pub enum VerificationError { @@ -569,9 +540,16 @@ pub enum VerificationError { mod tests { use super::*; + #[test] + fn lane_is_closed_by_default() { + assert_eq!(InboundLaneData::<()>::default().state, LaneState::Closed); + assert_eq!(OutboundLaneData::default().state, LaneState::Closed); + } + #[test] fn total_unrewarded_messages_does_not_overflow() { let lane_data = InboundLaneData { + state: LaneState::Opened, relayers: vec![ UnrewardedRelayer { relayer: 1, messages: DeliveredMessages::new(0) }, UnrewardedRelayer { @@ -599,6 +577,7 @@ mod tests { for (relayer_entries, messages_count) in test_cases { let expected_size = InboundLaneData::::encoded_size_hint(relayer_entries as _); let actual_size = InboundLaneData { + state: LaneState::Opened, relayers: (1u8..=relayer_entries) .map(|i| UnrewardedRelayer { relayer: i, @@ -626,9 +605,4 @@ mod tests { assert!(delivered_messages.contains_message(150)); assert!(!delivered_messages.contains_message(151)); } - - #[test] - fn lane_id_debug_format_matches_inner_array_format() { - assert_eq!(format!("{:?}", LaneId([0, 0, 0, 0])), format!("{:?}", [0, 0, 0, 0]),); - } } diff --git a/bridges/primitives/messages/src/source_chain.rs b/bridges/primitives/messages/src/source_chain.rs index 64f015bdb822eb25ff42a3fdb52bf6405a5424bf..1d4a513035c744bf4044156858bcdf9d756e94fa 100644 --- a/bridges/primitives/messages/src/source_chain.rs +++ b/bridges/primitives/messages/src/source_chain.rs @@ -16,7 +16,7 @@ //! Primitives of messages module, that are used on the source chain. -use crate::{LaneId, MessageNonce, UnrewardedRelayer}; +use crate::{MessageNonce, UnrewardedRelayer}; use bp_runtime::{raw_storage_proof_size, RawStorageProof, Size}; use codec::{Decode, Encode}; @@ -39,7 +39,7 @@ use sp_std::{ /// /// - lane id. #[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug, TypeInfo)] -pub struct FromBridgedChainMessagesDeliveryProof { +pub struct FromBridgedChainMessagesDeliveryProof { /// Hash of the bridge header the proof is for. pub bridged_header_hash: BridgedHeaderHash, /// Storage trie proof generated for [`Self::bridged_header_hash`]. @@ -48,7 +48,9 @@ pub struct FromBridgedChainMessagesDeliveryProof { pub lane: LaneId, } -impl Size for FromBridgedChainMessagesDeliveryProof { +impl Size + for FromBridgedChainMessagesDeliveryProof +{ fn size(&self) -> u32 { use frame_support::sp_runtime::SaturatedConversion; raw_storage_proof_size(&self.storage_proof).saturated_into() @@ -60,7 +62,7 @@ pub type RelayersRewards = BTreeMap; /// Manages payments that are happening at the source chain during delivery confirmation /// transaction. -pub trait DeliveryConfirmationPayments { +pub trait DeliveryConfirmationPayments { /// Error type. type Error: Debug + Into<&'static str>; @@ -78,7 +80,7 @@ pub trait DeliveryConfirmationPayments { ) -> MessageNonce; } -impl DeliveryConfirmationPayments for () { +impl DeliveryConfirmationPayments for () { type Error = &'static str; fn pay_reward( @@ -94,14 +96,14 @@ impl DeliveryConfirmationPayments for () { /// Callback that is called at the source chain (bridge hub) when we get delivery confirmation /// for new messages. -pub trait OnMessagesDelivered { +pub trait OnMessagesDelivered { /// New messages delivery has been confirmed. /// /// The only argument of the function is the number of yet undelivered messages fn on_messages_delivered(lane: LaneId, enqueued_messages: MessageNonce); } -impl OnMessagesDelivered for () { +impl OnMessagesDelivered for () { fn on_messages_delivered(_lane: LaneId, _enqueued_messages: MessageNonce) {} } @@ -115,7 +117,7 @@ pub struct SendMessageArtifacts { } /// Messages bridge API to be used from other pallets. -pub trait MessagesBridge { +pub trait MessagesBridge { /// Error type. type Error: Debug; @@ -141,7 +143,7 @@ pub trait MessagesBridge { /// where outbound messages are forbidden. pub struct ForbidOutboundMessages; -impl DeliveryConfirmationPayments for ForbidOutboundMessages { +impl DeliveryConfirmationPayments for ForbidOutboundMessages { type Error = &'static str; fn pay_reward( diff --git a/bridges/primitives/messages/src/storage_keys.rs b/bridges/primitives/messages/src/storage_keys.rs index 8eedf8fcc7ac98ae300ca0485a0827afd3cd1bb5..fb3371cb830c4b3b94a2f60c1f0939329fa815c4 100644 --- a/bridges/primitives/messages/src/storage_keys.rs +++ b/bridges/primitives/messages/src/storage_keys.rs @@ -25,7 +25,7 @@ pub const OUTBOUND_LANES_MAP_NAME: &str = "OutboundLanes"; /// Name of the `InboundLanes` storage map. pub const INBOUND_LANES_MAP_NAME: &str = "InboundLanes"; -use crate::{LaneId, MessageKey, MessageNonce}; +use crate::{MessageKey, MessageNonce}; use codec::Encode; use frame_support::Blake2_128Concat; @@ -43,16 +43,20 @@ pub fn operating_mode_key(pallet_prefix: &str) -> StorageKey { } /// Storage key of the outbound message in the runtime storage. -pub fn message_key(pallet_prefix: &str, lane: &LaneId, nonce: MessageNonce) -> StorageKey { +pub fn message_key( + pallet_prefix: &str, + lane: LaneId, + nonce: MessageNonce, +) -> StorageKey { bp_runtime::storage_map_final_key::( pallet_prefix, OUTBOUND_MESSAGES_MAP_NAME, - &MessageKey { lane_id: *lane, nonce }.encode(), + &MessageKey { lane_id: lane, nonce }.encode(), ) } /// Storage key of the outbound message lane state in the runtime storage. -pub fn outbound_lane_data_key(pallet_prefix: &str, lane: &LaneId) -> StorageKey { +pub fn outbound_lane_data_key(pallet_prefix: &str, lane: &LaneId) -> StorageKey { bp_runtime::storage_map_final_key::( pallet_prefix, OUTBOUND_LANES_MAP_NAME, @@ -61,7 +65,7 @@ pub fn outbound_lane_data_key(pallet_prefix: &str, lane: &LaneId) -> StorageKey } /// Storage key of the inbound message lane state in the runtime storage. -pub fn inbound_lane_data_key(pallet_prefix: &str, lane: &LaneId) -> StorageKey { +pub fn inbound_lane_data_key(pallet_prefix: &str, lane: &LaneId) -> StorageKey { bp_runtime::storage_map_final_key::( pallet_prefix, INBOUND_LANES_MAP_NAME, @@ -72,6 +76,10 @@ pub fn inbound_lane_data_key(pallet_prefix: &str, lane: &LaneId) -> StorageKey { #[cfg(test)] mod tests { use super::*; + use crate::{ + lane::{HashedLaneId, LegacyLaneId}, + LaneIdType, + }; use hex_literal::hex; #[test] @@ -91,7 +99,17 @@ mod tests { fn storage_message_key_computed_properly() { // If this test fails, then something has been changed in module storage that is breaking // all previously crafted messages proofs. - let storage_key = message_key("BridgeMessages", &LaneId(*b"test"), 42).0; + let storage_key = + message_key("BridgeMessages", &HashedLaneId::try_new(1, 2).unwrap(), 42).0; + assert_eq!( + storage_key, + hex!("dd16c784ebd3390a9bc0357c7511ed018a395e6242c6813b196ca31ed0547ea70e9bdb8f50c68d12f06eabb57759ee5eb1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dcf87f9793be208e5ea02a00000000000000").to_vec(), + "Unexpected storage key: {}", + hex::encode(&storage_key), + ); + + // check backwards compatibility + let storage_key = message_key("BridgeMessages", &LegacyLaneId(*b"test"), 42).0; assert_eq!( storage_key, hex!("dd16c784ebd3390a9bc0357c7511ed018a395e6242c6813b196ca31ed0547ea79446af0e09063bd4a7874aef8a997cec746573742a00000000000000").to_vec(), @@ -104,7 +122,17 @@ mod tests { fn outbound_lane_data_key_computed_properly() { // If this test fails, then something has been changed in module storage that is breaking // all previously crafted outbound lane state proofs. - let storage_key = outbound_lane_data_key("BridgeMessages", &LaneId(*b"test")).0; + let storage_key = + outbound_lane_data_key("BridgeMessages", &HashedLaneId::try_new(1, 2).unwrap()).0; + assert_eq!( + storage_key, + hex!("dd16c784ebd3390a9bc0357c7511ed0196c246acb9b55077390e3ca723a0ca1fd3bef8b00df8ca7b01813b5e2741950db1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dcf87f9793be208e5ea0").to_vec(), + "Unexpected storage key: {}", + hex::encode(&storage_key), + ); + + // check backwards compatibility + let storage_key = outbound_lane_data_key("BridgeMessages", &LegacyLaneId(*b"test")).0; assert_eq!( storage_key, hex!("dd16c784ebd3390a9bc0357c7511ed0196c246acb9b55077390e3ca723a0ca1f44a8995dd50b6657a037a7839304535b74657374").to_vec(), @@ -117,7 +145,17 @@ mod tests { fn inbound_lane_data_key_computed_properly() { // If this test fails, then something has been changed in module storage that is breaking // all previously crafted inbound lane state proofs. - let storage_key = inbound_lane_data_key("BridgeMessages", &LaneId(*b"test")).0; + let storage_key = + inbound_lane_data_key("BridgeMessages", &HashedLaneId::try_new(1, 2).unwrap()).0; + assert_eq!( + storage_key, + hex!("dd16c784ebd3390a9bc0357c7511ed01e5f83cf83f2127eb47afdc35d6e43fabd3bef8b00df8ca7b01813b5e2741950db1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dcf87f9793be208e5ea0").to_vec(), + "Unexpected storage key: {}", + hex::encode(&storage_key), + ); + + // check backwards compatibility + let storage_key = inbound_lane_data_key("BridgeMessages", &LegacyLaneId(*b"test")).0; assert_eq!( storage_key, hex!("dd16c784ebd3390a9bc0357c7511ed01e5f83cf83f2127eb47afdc35d6e43fab44a8995dd50b6657a037a7839304535b74657374").to_vec(), diff --git a/bridges/primitives/messages/src/target_chain.rs b/bridges/primitives/messages/src/target_chain.rs index 74fecb9d9f0d89420d2ca54a4356e23c9130e614..cf07a400933a965f5097d4654f6e405a1d11b33b 100644 --- a/bridges/primitives/messages/src/target_chain.rs +++ b/bridges/primitives/messages/src/target_chain.rs @@ -16,14 +16,14 @@ //! Primitives of messages module, that are used on the target chain. -use crate::{LaneId, Message, MessageKey, MessageNonce, MessagePayload, OutboundLaneData}; +use crate::{Message, MessageKey, MessageNonce, MessagePayload, OutboundLaneData}; use bp_runtime::{messages::MessageDispatchResult, raw_storage_proof_size, RawStorageProof, Size}; use codec::{Decode, Encode, Error as CodecError}; use frame_support::weights::Weight; use scale_info::TypeInfo; use sp_core::RuntimeDebug; -use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, marker::PhantomData, prelude::*}; +use sp_std::{fmt::Debug, marker::PhantomData, prelude::*}; /// Messages proof from bridged chain. /// @@ -38,20 +38,20 @@ use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, marker::PhantomData, /// /// - nonces (inclusive range) of messages which are included in this proof. #[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug, TypeInfo)] -pub struct FromBridgedChainMessagesProof { +pub struct FromBridgedChainMessagesProof { /// Hash of the finalized bridged header the proof is for. pub bridged_header_hash: BridgedHeaderHash, /// A storage trie proof of messages being delivered. pub storage_proof: RawStorageProof, /// Messages in this proof are sent over this lane. - pub lane: LaneId, + pub lane: Lane, /// Nonce of the first message being delivered. pub nonces_start: MessageNonce, /// Nonce of the last message being delivered. pub nonces_end: MessageNonce, } -impl Size for FromBridgedChainMessagesProof { +impl Size for FromBridgedChainMessagesProof { fn size(&self) -> u32 { use frame_support::sp_runtime::SaturatedConversion; raw_storage_proof_size(&self.storage_proof).saturated_into() @@ -59,7 +59,7 @@ impl Size for FromBridgedChainMessagesProof = BTreeMap>; +pub type ProvedMessages = (LaneId, ProvedLaneMessages); /// Proved messages from single lane of the source chain. #[derive(RuntimeDebug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo)] @@ -79,9 +79,9 @@ pub struct DispatchMessageData { /// Message with decoded dispatch payload. #[derive(RuntimeDebug)] -pub struct DispatchMessage { +pub struct DispatchMessage { /// Message key. - pub key: MessageKey, + pub key: MessageKey, /// Message data with decoded dispatch payload. pub data: DispatchMessageData, } @@ -96,6 +96,9 @@ pub trait MessageDispatch { /// Fine-grained result of single message dispatch (for better diagnostic purposes) type DispatchLevelResult: Clone + sp_std::fmt::Debug + Eq; + /// Lane identifier type. + type LaneId: Encode; + /// Returns `true` if dispatcher is ready to accept additional messages. The `false` should /// be treated as a hint by both dispatcher and its consumers - i.e. dispatcher shall not /// simply drop messages if it returns `false`. The consumer may still call the `dispatch` @@ -103,21 +106,23 @@ pub trait MessageDispatch { /// /// We check it in the messages delivery transaction prologue. So if it becomes `false` /// after some portion of messages is already dispatched, it doesn't fail the whole transaction. - fn is_active() -> bool; + fn is_active(lane: Self::LaneId) -> bool; /// Estimate dispatch weight. /// /// This function must return correct upper bound of dispatch weight. The return value /// of this function is expected to match return value of the corresponding /// `FromInboundLaneApi::message_details().dispatch_weight` call. - fn dispatch_weight(message: &mut DispatchMessage) -> Weight; + fn dispatch_weight( + message: &mut DispatchMessage, + ) -> Weight; /// Called when inbound message is received. /// /// It is up to the implementers of this trait to determine whether the message /// is invalid (i.e. improperly encoded, has too large weight, ...) or not. fn dispatch( - message: DispatchMessage, + message: DispatchMessage, ) -> MessageDispatchResult; } @@ -146,8 +151,10 @@ impl Default for ProvedLaneMessages { } } -impl From for DispatchMessage { - fn from(message: Message) -> Self { +impl From> + for DispatchMessage +{ + fn from(message: Message) -> Self { DispatchMessage { key: message.key, data: message.payload.into() } } } @@ -173,22 +180,27 @@ impl DeliveryPayments for () { /// Structure that may be used in place of `MessageDispatch` on chains, /// where inbound messages are forbidden. -pub struct ForbidInboundMessages(PhantomData); +pub struct ForbidInboundMessages(PhantomData<(DispatchPayload, LaneId)>); -impl MessageDispatch for ForbidInboundMessages { +impl MessageDispatch + for ForbidInboundMessages +{ type DispatchPayload = DispatchPayload; type DispatchLevelResult = (); + type LaneId = LaneId; - fn is_active() -> bool { + fn is_active(_: LaneId) -> bool { false } - fn dispatch_weight(_message: &mut DispatchMessage) -> Weight { + fn dispatch_weight( + _message: &mut DispatchMessage, + ) -> Weight { Weight::MAX } fn dispatch( - _: DispatchMessage, + _: DispatchMessage, ) -> MessageDispatchResult { MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result: () } } diff --git a/bridges/primitives/parachains/src/call_info.rs b/bridges/primitives/parachains/src/call_info.rs new file mode 100644 index 0000000000000000000000000000000000000000..fd7cd45a72c81baacfad95f864c461f92136b805 --- /dev/null +++ b/bridges/primitives/parachains/src/call_info.rs @@ -0,0 +1,59 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Defines structures related to calls of the `pallet-bridge-parachains` pallet. + +use crate::{ParaHash, ParaId, RelayBlockHash, RelayBlockNumber}; + +use bp_polkadot_core::parachains::ParaHeadsProof; +use bp_runtime::HeaderId; +use codec::{Decode, Encode}; +use scale_info::TypeInfo; +use sp_runtime::RuntimeDebug; +use sp_std::vec::Vec; + +/// A minimized version of `pallet-bridge-parachains::Call` that can be used without a runtime. +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum BridgeParachainCall { + /// `pallet-bridge-parachains::Call::submit_parachain_heads` + #[codec(index = 0)] + submit_parachain_heads { + /// Relay chain block, for which we have submitted the `parachain_heads_proof`. + at_relay_block: (RelayBlockNumber, RelayBlockHash), + /// Parachain identifiers and their head hashes. + parachains: Vec<(ParaId, ParaHash)>, + /// Parachain heads proof. + parachain_heads_proof: ParaHeadsProof, + }, +} + +/// Info about a `SubmitParachainHeads` call which tries to update a single parachain. +/// +/// The pallet supports updating multiple parachain heads at once, +#[derive(PartialEq, RuntimeDebug)] +pub struct SubmitParachainHeadsInfo { + /// Number and hash of the finalized relay block that has been used to prove parachain + /// finality. + pub at_relay_block: HeaderId, + /// Parachain identifier. + pub para_id: ParaId, + /// Hash of the bundled parachain head. + pub para_head_hash: ParaHash, + /// If `true`, then the call must be free (assuming that everything else is valid) to + /// be treated as valid. + pub is_free_execution_expected: bool, +} diff --git a/bridges/primitives/parachains/src/lib.rs b/bridges/primitives/parachains/src/lib.rs index 142c6e9b08923fdd2934fb7f3b9c2d12788fc8b9..ec3bf9ca3a0af6d2b159fc354e6792519e0c6321 100644 --- a/bridges/primitives/parachains/src/lib.rs +++ b/bridges/primitives/parachains/src/lib.rs @@ -20,11 +20,9 @@ #![cfg_attr(not(feature = "std"), no_std)] pub use bp_header_chain::StoredHeaderData; +pub use call_info::{BridgeParachainCall, SubmitParachainHeadsInfo}; -use bp_polkadot_core::{ - parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}, - BlockNumber as RelayBlockNumber, Hash as RelayBlockHash, -}; +use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaId}; use bp_runtime::{ BlockNumberOf, Chain, HashOf, HeaderOf, Parachain, StorageDoubleMapKeyProvider, StorageMapKeyProvider, @@ -36,6 +34,15 @@ use sp_core::storage::StorageKey; use sp_runtime::{traits::Header as HeaderT, RuntimeDebug}; use sp_std::{marker::PhantomData, prelude::*}; +/// Block hash of the bridged relay chain. +pub type RelayBlockHash = bp_polkadot_core::Hash; +/// Block number of the bridged relay chain. +pub type RelayBlockNumber = bp_polkadot_core::BlockNumber; +/// Hasher of the bridged relay chain. +pub type RelayBlockHasher = bp_polkadot_core::Hasher; + +mod call_info; + /// Best known parachain head hash. #[derive(Clone, Decode, Encode, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] pub struct BestParaHeadHash { @@ -185,19 +192,3 @@ impl ParaStoredHeaderDataBuilder for C { None } } - -/// A minimized version of `pallet-bridge-parachains::Call` that can be used without a runtime. -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -#[allow(non_camel_case_types)] -pub enum BridgeParachainCall { - /// `pallet-bridge-parachains::Call::submit_parachain_heads` - #[codec(index = 0)] - submit_parachain_heads { - /// Relay chain block, for which we have submitted the `parachain_heads_proof`. - at_relay_block: (RelayBlockNumber, RelayBlockHash), - /// Parachain identifiers and their head hashes. - parachains: Vec<(ParaId, ParaHash)>, - /// Parachain heads proof. - parachain_heads_proof: ParaHeadsProof, - }, -} diff --git a/bridges/primitives/polkadot-core/Cargo.toml b/bridges/primitives/polkadot-core/Cargo.toml index acae2f431bf20aa8babf57e586cd4bb726fe2ec8..295fb281e9bbc5bf7b76d6c66353c6768eb33c12 100644 --- a/bridges/primitives/polkadot-core/Cargo.toml +++ b/bridges/primitives/polkadot-core/Cargo.toml @@ -12,9 +12,10 @@ workspace = true [dependencies] codec = { features = ["derive"], workspace = true } -parity-util-mem = { optional = true, workspace = true } scale-info = { features = ["derive"], workspace = true } -serde = { optional = true, features = ["derive"], workspace = true, default-features = true } +serde = { optional = true, features = [ + "derive", +], workspace = true, default-features = true } # Bridge Dependencies @@ -40,7 +41,6 @@ std = [ "codec/std", "frame-support/std", "frame-system/std", - "parity-util-mem", "scale-info/std", "serde", "sp-core/std", diff --git a/bridges/primitives/polkadot-core/src/parachains.rs b/bridges/primitives/polkadot-core/src/parachains.rs index d54ee108386edf50865c500eb1062c24b01c0c3e..a8b1cf6eebf49dedf40bcb94135d4e6717dc14d0 100644 --- a/bridges/primitives/polkadot-core/src/parachains.rs +++ b/bridges/primitives/polkadot-core/src/parachains.rs @@ -32,9 +32,6 @@ use sp_std::vec::Vec; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; -#[cfg(feature = "std")] -use parity_util_mem::MallocSizeOf; - /// Parachain id. /// /// This is an equivalent of the `polkadot_parachain_primitives::Id`, which is a compact-encoded @@ -71,7 +68,7 @@ impl From for ParaId { #[derive( PartialEq, Eq, Clone, PartialOrd, Ord, Encode, Decode, RuntimeDebug, TypeInfo, Default, )] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Hash, MallocSizeOf))] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Hash))] pub struct ParaHead(pub Vec); impl ParaHead { diff --git a/bridges/primitives/relayers/Cargo.toml b/bridges/primitives/relayers/Cargo.toml index 3448e8a4096339966023d0f5ddd0e158380ab12a..34be38bed4ac67fec9f0b5a1e1c1a8b0ca70e6d5 100644 --- a/bridges/primitives/relayers/Cargo.toml +++ b/bridges/primitives/relayers/Cargo.toml @@ -15,13 +15,15 @@ codec = { features = ["bit-vec", "derive"], workspace = true } scale-info = { features = ["bit-vec", "derive"], workspace = true } # Bridge Dependencies - +bp-header-chain = { workspace = true } bp-messages = { workspace = true } +bp-parachains = { workspace = true } bp-runtime = { workspace = true } # Substrate Dependencies - +frame-system = { workspace = true } frame-support = { workspace = true } +pallet-utility = { workspace = true } sp-runtime = { workspace = true } sp-std = { workspace = true } @@ -32,10 +34,14 @@ hex-literal = { workspace = true, default-features = true } [features] default = ["std"] std = [ + "bp-header-chain/std", "bp-messages/std", + "bp-parachains/std", "bp-runtime/std", "codec/std", "frame-support/std", + "frame-system/std", + "pallet-utility/std", "scale-info/std", "sp-runtime/std", "sp-std/std", diff --git a/bridges/primitives/relayers/src/extension.rs b/bridges/primitives/relayers/src/extension.rs new file mode 100644 index 0000000000000000000000000000000000000000..a9fa4df27ead317d6a34babe36dd289b3db568b0 --- /dev/null +++ b/bridges/primitives/relayers/src/extension.rs @@ -0,0 +1,197 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! All runtime calls, supported by `pallet-bridge-relayers` when it acts as a signed +//! extension. + +use bp_header_chain::SubmitFinalityProofInfo; +use bp_messages::MessagesCallInfo; +use bp_parachains::SubmitParachainHeadsInfo; +use bp_runtime::StaticStrProvider; +use codec::{Decode, Encode}; +use frame_support::{ + dispatch::CallableCallFor, traits::IsSubType, weights::Weight, RuntimeDebugNoBound, +}; +use frame_system::Config as SystemConfig; +use pallet_utility::{Call as UtilityCall, Pallet as UtilityPallet}; +use sp_runtime::{ + traits::Get, + transaction_validity::{TransactionPriority, TransactionValidityError}, + RuntimeDebug, +}; +use sp_std::{fmt::Debug, marker::PhantomData, vec, vec::Vec}; + +/// Type of the call that the signed extension recognizes. +#[derive(PartialEq, RuntimeDebugNoBound)] +pub enum ExtensionCallInfo { + /// Relay chain finality + parachain finality + message delivery/confirmation calls. + AllFinalityAndMsgs( + SubmitFinalityProofInfo, + SubmitParachainHeadsInfo, + MessagesCallInfo, + ), + /// Relay chain finality + message delivery/confirmation calls. + RelayFinalityAndMsgs( + SubmitFinalityProofInfo, + MessagesCallInfo, + ), + /// Parachain finality + message delivery/confirmation calls. + /// + /// This variant is used only when bridging with parachain. + ParachainFinalityAndMsgs(SubmitParachainHeadsInfo, MessagesCallInfo), + /// Standalone message delivery/confirmation call. + Msgs(MessagesCallInfo), +} + +impl + ExtensionCallInfo +{ + /// Returns true if call is a message delivery call (with optional finality calls). + pub fn is_receive_messages_proof_call(&self) -> bool { + match self.messages_call_info() { + MessagesCallInfo::ReceiveMessagesProof(_) => true, + MessagesCallInfo::ReceiveMessagesDeliveryProof(_) => false, + } + } + + /// Returns the pre-dispatch `finality_target` sent to the `SubmitFinalityProof` call. + pub fn submit_finality_proof_info( + &self, + ) -> Option> { + match *self { + Self::AllFinalityAndMsgs(info, _, _) => Some(info), + Self::RelayFinalityAndMsgs(info, _) => Some(info), + _ => None, + } + } + + /// Returns the pre-dispatch `SubmitParachainHeadsInfo`. + pub fn submit_parachain_heads_info(&self) -> Option<&SubmitParachainHeadsInfo> { + match self { + Self::AllFinalityAndMsgs(_, info, _) => Some(info), + Self::ParachainFinalityAndMsgs(info, _) => Some(info), + _ => None, + } + } + + /// Returns the pre-dispatch `ReceiveMessagesProofInfo`. + pub fn messages_call_info(&self) -> &MessagesCallInfo { + match self { + Self::AllFinalityAndMsgs(_, _, info) => info, + Self::RelayFinalityAndMsgs(_, info) => info, + Self::ParachainFinalityAndMsgs(_, info) => info, + Self::Msgs(info) => info, + } + } +} + +/// Extra post-dispatch data, associated with the supported runtime call. +#[derive(Default, RuntimeDebug)] +pub struct ExtensionCallData { + /// Extra weight, consumed by the call. We have some assumptions about normal weight + /// that may be consumed by expected calls. If the actual weight is larger than that, + /// we do not refund relayer for this extra weight. + pub extra_weight: Weight, + /// Extra size, consumed by the call. We have some assumptions about normal size + /// of the encoded call. If the actual size is larger than that, we do not refund relayer + /// for this extra size. + pub extra_size: u32, +} + +/// Signed extension configuration. +/// +/// The single `pallet-bridge-relayers` instance may be shared by multiple messages +/// pallet instances, bridging with different remote networks. We expect every instance +/// of the messages pallet to add a separate signed extension to runtime. So it must +/// have a separate configuration. +pub trait ExtensionConfig { + /// Unique identifier of the signed extension that will use this configuration. + type IdProvider: StaticStrProvider; + /// Runtime that optionally supports batched calls. We assume that batched call + /// succeeds if and only if all of its nested calls succeed. + type Runtime: frame_system::Config; + /// Relayers pallet instance. + type BridgeRelayersPalletInstance: 'static; + /// Messages pallet instance. + type BridgeMessagesPalletInstance: 'static; + /// Additional priority that is added to base message delivery transaction priority + /// for every additional bundled message. + type PriorityBoostPerMessage: Get; + /// Block number for the remote **GRANDPA chain**. Mind that this chain is not + /// necessarily the chain that we are bridging with. If we are bridging with + /// parachain, it must be its parent relay chain. If we are bridging with the + /// GRANDPA chain, it must be it. + type RemoteGrandpaChainBlockNumber: Clone + Copy + Debug; + /// Lane identifier type. + type LaneId: Clone + Copy + Decode + Encode + Debug; + + /// Given runtime call, check if it is supported by the signed extension. Additionally, + /// check if call (or any of batched calls) are obsolete. + fn parse_and_check_for_obsolete_call( + call: &::RuntimeCall, + ) -> Result< + Option>, + TransactionValidityError, + >; + + /// Check if runtime call is already obsolete. + fn check_obsolete_parsed_call( + call: &::RuntimeCall, + ) -> Result<&::RuntimeCall, TransactionValidityError>; + + /// Given runtime call info, check that this call has been successful and has updated + /// runtime storage accordingly. + fn check_call_result( + call_info: &ExtensionCallInfo, + call_data: &mut ExtensionCallData, + relayer: &::AccountId, + ) -> bool; +} + +/// Something that can unpack batch calls (all-or-nothing flavor) of given size. +pub trait BatchCallUnpacker { + /// Unpack batch call with no more than `max_packed_calls` calls. + fn unpack(call: &Runtime::RuntimeCall, max_packed_calls: u32) -> Vec<&Runtime::RuntimeCall>; +} + +/// An `BatchCallUnpacker` adapter for runtimes with utility pallet. +pub struct RuntimeWithUtilityPallet(PhantomData); + +impl BatchCallUnpacker for RuntimeWithUtilityPallet +where + Runtime: pallet_utility::Config::RuntimeCall>, + ::RuntimeCall: + IsSubType, Runtime>>, +{ + fn unpack( + call: &::RuntimeCall, + max_packed_calls: u32, + ) -> Vec<&::RuntimeCall> { + match call.is_sub_type() { + Some(UtilityCall::::batch_all { ref calls }) + if calls.len() <= max_packed_calls as usize => + calls.iter().collect(), + Some(_) => vec![], + None => vec![call], + } + } +} + +impl BatchCallUnpacker for () { + fn unpack(call: &Runtime::RuntimeCall, _max_packed_calls: u32) -> Vec<&Runtime::RuntimeCall> { + vec![call] + } +} diff --git a/bridges/primitives/relayers/src/lib.rs b/bridges/primitives/relayers/src/lib.rs index 436f33db4008013572592939b63c0e02e92b8e99..faa4cb17762940ff0dc6d3fbf322dbf99bb92e49 100644 --- a/bridges/primitives/relayers/src/lib.rs +++ b/bridges/primitives/relayers/src/lib.rs @@ -19,9 +19,12 @@ #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] +pub use extension::{ + BatchCallUnpacker, ExtensionCallData, ExtensionCallInfo, ExtensionConfig, + RuntimeWithUtilityPallet, +}; pub use registration::{ExplicitOrAccountParams, Registration, StakeAndSlash}; -use bp_messages::LaneId; use bp_runtime::{ChainId, StorageDoubleMapKeyProvider}; use frame_support::{traits::tokens::Preservation, Blake2_128Concat, Identity}; use scale_info::TypeInfo; @@ -32,6 +35,7 @@ use sp_runtime::{ }; use sp_std::{fmt::Debug, marker::PhantomData}; +mod extension; mod registration; /// The owner of the sovereign account that should pay the rewards. @@ -56,13 +60,16 @@ pub enum RewardsAccountOwner { /// of the sovereign accounts will pay rewards for different operations. So we need multiple /// parameters to identify the account that pays a reward to the relayer. #[derive(Copy, Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen)] -pub struct RewardsAccountParams { - lane_id: LaneId, - bridged_chain_id: ChainId, +pub struct RewardsAccountParams { + // **IMPORTANT NOTE**: the order of fields here matters - we are using + // `into_account_truncating` and lane id is already `32` byte, so if other fields are encoded + // after it, they're simply dropped. So lane id shall be the last field. owner: RewardsAccountOwner, + bridged_chain_id: ChainId, + lane_id: LaneId, } -impl RewardsAccountParams { +impl RewardsAccountParams { /// Create a new instance of `RewardsAccountParams`. pub const fn new( lane_id: LaneId, @@ -71,9 +78,14 @@ impl RewardsAccountParams { ) -> Self { Self { lane_id, bridged_chain_id, owner } } + + /// Getter for `lane_id`. + pub const fn lane_id(&self) -> &LaneId { + &self.lane_id + } } -impl TypeId for RewardsAccountParams { +impl TypeId for RewardsAccountParams { const TYPE_ID: [u8; 4] = *b"brap"; } @@ -81,47 +93,58 @@ impl TypeId for RewardsAccountParams { pub trait PaymentProcedure { /// Error that may be returned by the procedure. type Error: Debug; + /// Lane identifier type. + type LaneId: Decode + Encode; /// Pay reward to the relayer from the account with provided params. fn pay_reward( relayer: &Relayer, - rewards_account_params: RewardsAccountParams, + rewards_account_params: RewardsAccountParams, reward: Reward, ) -> Result<(), Self::Error>; } impl PaymentProcedure for () { type Error = &'static str; + type LaneId = (); - fn pay_reward(_: &Relayer, _: RewardsAccountParams, _: Reward) -> Result<(), Self::Error> { + fn pay_reward( + _: &Relayer, + _: RewardsAccountParams, + _: Reward, + ) -> Result<(), Self::Error> { Ok(()) } } /// Reward payment procedure that does `balances::transfer` call from the account, derived from /// given params. -pub struct PayRewardFromAccount(PhantomData<(T, Relayer)>); +pub struct PayRewardFromAccount(PhantomData<(T, Relayer, LaneId)>); -impl PayRewardFromAccount +impl PayRewardFromAccount where Relayer: Decode + Encode, + LaneId: Decode + Encode, { /// Return account that pays rewards based on the provided parameters. - pub fn rewards_account(params: RewardsAccountParams) -> Relayer { + pub fn rewards_account(params: RewardsAccountParams) -> Relayer { params.into_sub_account_truncating(b"rewards-account") } } -impl PaymentProcedure for PayRewardFromAccount +impl PaymentProcedure + for PayRewardFromAccount where T: frame_support::traits::fungible::Mutate, Relayer: Decode + Encode + Eq, + LaneId: Decode + Encode, { type Error = sp_runtime::DispatchError; + type LaneId = LaneId; fn pay_reward( relayer: &Relayer, - rewards_account_params: RewardsAccountParams, + rewards_account_params: RewardsAccountParams, reward: T::Balance, ) -> Result<(), Self::Error> { T::transfer( @@ -134,49 +157,57 @@ where } } -/// Can be use to access the runtime storage key within the `RelayerRewards` map of the relayers +/// Can be used to access the runtime storage key within the `RelayerRewards` map of the relayers /// pallet. -pub struct RelayerRewardsKeyProvider(PhantomData<(AccountId, Reward)>); +pub struct RelayerRewardsKeyProvider( + PhantomData<(AccountId, Reward, LaneId)>, +); -impl StorageDoubleMapKeyProvider for RelayerRewardsKeyProvider +impl StorageDoubleMapKeyProvider + for RelayerRewardsKeyProvider where AccountId: 'static + Codec + EncodeLike + Send + Sync, Reward: 'static + Codec + EncodeLike + Send + Sync, + LaneId: Codec + EncodeLike + Send + Sync, { const MAP_NAME: &'static str = "RelayerRewards"; type Hasher1 = Blake2_128Concat; type Key1 = AccountId; type Hasher2 = Identity; - type Key2 = RewardsAccountParams; + type Key2 = RewardsAccountParams; type Value = Reward; } #[cfg(test)] mod tests { use super::*; - use bp_messages::LaneId; - use sp_runtime::testing::H256; + use bp_messages::{HashedLaneId, LaneIdType, LegacyLaneId}; + use sp_runtime::{app_crypto::Ss58Codec, testing::H256}; #[test] fn different_lanes_are_using_different_accounts() { assert_eq!( - PayRewardFromAccount::<(), H256>::rewards_account(RewardsAccountParams::new( - LaneId([0, 0, 0, 0]), - *b"test", - RewardsAccountOwner::ThisChain - )), - hex_literal::hex!("62726170000000007465737400726577617264732d6163636f756e7400000000") + PayRewardFromAccount::<(), H256, HashedLaneId>::rewards_account( + RewardsAccountParams::new( + HashedLaneId::try_new(1, 2).unwrap(), + *b"test", + RewardsAccountOwner::ThisChain + ) + ), + hex_literal::hex!("627261700074657374b1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dc") .into(), ); assert_eq!( - PayRewardFromAccount::<(), H256>::rewards_account(RewardsAccountParams::new( - LaneId([0, 0, 0, 1]), - *b"test", - RewardsAccountOwner::ThisChain - )), - hex_literal::hex!("62726170000000017465737400726577617264732d6163636f756e7400000000") + PayRewardFromAccount::<(), H256, HashedLaneId>::rewards_account( + RewardsAccountParams::new( + HashedLaneId::try_new(1, 3).unwrap(), + *b"test", + RewardsAccountOwner::ThisChain + ) + ), + hex_literal::hex!("627261700074657374a43e8951aa302c133beb5f85821a21645f07b487270ef3") .into(), ); } @@ -184,23 +215,101 @@ mod tests { #[test] fn different_directions_are_using_different_accounts() { assert_eq!( - PayRewardFromAccount::<(), H256>::rewards_account(RewardsAccountParams::new( - LaneId([0, 0, 0, 0]), - *b"test", - RewardsAccountOwner::ThisChain - )), - hex_literal::hex!("62726170000000007465737400726577617264732d6163636f756e7400000000") + PayRewardFromAccount::<(), H256, HashedLaneId>::rewards_account( + RewardsAccountParams::new( + HashedLaneId::try_new(1, 2).unwrap(), + *b"test", + RewardsAccountOwner::ThisChain + ) + ), + hex_literal::hex!("627261700074657374b1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dc") .into(), ); assert_eq!( - PayRewardFromAccount::<(), H256>::rewards_account(RewardsAccountParams::new( - LaneId([0, 0, 0, 0]), - *b"test", - RewardsAccountOwner::BridgedChain - )), - hex_literal::hex!("62726170000000007465737401726577617264732d6163636f756e7400000000") + PayRewardFromAccount::<(), H256, HashedLaneId>::rewards_account( + RewardsAccountParams::new( + HashedLaneId::try_new(1, 2).unwrap(), + *b"test", + RewardsAccountOwner::BridgedChain + ) + ), + hex_literal::hex!("627261700174657374b1d3dccd8b3c3a012afe265f3e3c4432129b8aee50c9dc") .into(), ); } + + #[test] + fn pay_reward_from_account_for_legacy_lane_id_works() { + let test_data = vec![ + // Note: these accounts are used for integration tests within + // `bridges_rococo_westend.sh` + ( + LegacyLaneId([0, 0, 0, 1]), + b"bhks", + RewardsAccountOwner::ThisChain, + (0_u16, "13E5fui97x6KTwNnSjaEKZ8s7kJNot5F3aUsy3jUtuoMyUec"), + ), + ( + LegacyLaneId([0, 0, 0, 1]), + b"bhks", + RewardsAccountOwner::BridgedChain, + (0_u16, "13E5fui9Ka9Vz4JbGN3xWjmwDNxnxF1N9Hhhbeu3VCqLChuj"), + ), + ( + LegacyLaneId([0, 0, 0, 1]), + b"bhpd", + RewardsAccountOwner::ThisChain, + (2_u16, "EoQBtnwtXqnSnr9cgBEJpKU7NjeC9EnR4D1VjgcvHz9ZYmS"), + ), + ( + LegacyLaneId([0, 0, 0, 1]), + b"bhpd", + RewardsAccountOwner::BridgedChain, + (2_u16, "EoQBtnx69txxumxSJexVzxYD1Q4LWAuWmRq8LrBWb27nhYN"), + ), + // Note: these accounts are used for integration tests within + // `bridges_polkadot_kusama.sh` from fellows. + ( + LegacyLaneId([0, 0, 0, 2]), + b"bhwd", + RewardsAccountOwner::ThisChain, + (4_u16, "SNihsskf7bFhnHH9HJFMjWD3FJ96ESdAQTFZUAtXudRQbaH"), + ), + ( + LegacyLaneId([0, 0, 0, 2]), + b"bhwd", + RewardsAccountOwner::BridgedChain, + (4_u16, "SNihsskrjeSDuD5xumyYv9H8sxZEbNkG7g5C5LT8CfPdaSE"), + ), + ( + LegacyLaneId([0, 0, 0, 2]), + b"bhro", + RewardsAccountOwner::ThisChain, + (4_u16, "SNihsskf7bF2vWogkC6uFoiqPhd3dUX6TGzYZ1ocJdo3xHp"), + ), + ( + LegacyLaneId([0, 0, 0, 2]), + b"bhro", + RewardsAccountOwner::BridgedChain, + (4_u16, "SNihsskrjeRZ3ScWNfq6SSnw2N3BzQeCAVpBABNCbfmHENB"), + ), + ]; + + for (lane_id, bridged_chain_id, owner, (expected_ss58, expected_account)) in test_data { + assert_eq!( + expected_account, + sp_runtime::AccountId32::new(PayRewardFromAccount::< + [u8; 32], + [u8; 32], + LegacyLaneId, + >::rewards_account(RewardsAccountParams::new( + lane_id, + *bridged_chain_id, + owner + ))) + .to_ss58check_with_version(expected_ss58.into()) + ); + } + } } diff --git a/bridges/primitives/relayers/src/registration.rs b/bridges/primitives/relayers/src/registration.rs index 9d9b7e4812201390c6831ff020c12c8cc995c17a..d74ef18cf706bdee134dc1157a150a3132b36a2e 100644 --- a/bridges/primitives/relayers/src/registration.rs +++ b/bridges/primitives/relayers/src/registration.rs @@ -48,15 +48,17 @@ use sp_runtime::{ /// Either explicit account reference or `RewardsAccountParams`. #[derive(Clone, Debug)] -pub enum ExplicitOrAccountParams { +pub enum ExplicitOrAccountParams { /// Explicit account reference. Explicit(AccountId), /// Account, referenced using `RewardsAccountParams`. - Params(RewardsAccountParams), + Params(RewardsAccountParams), } -impl From for ExplicitOrAccountParams { - fn from(params: RewardsAccountParams) -> Self { +impl From> + for ExplicitOrAccountParams +{ + fn from(params: RewardsAccountParams) -> Self { ExplicitOrAccountParams::Params(params) } } @@ -103,9 +105,9 @@ pub trait StakeAndSlash { /// `beneficiary`. /// /// Returns `Ok(_)` with non-zero balance if we have failed to repatriate some portion of stake. - fn repatriate_reserved( + fn repatriate_reserved( relayer: &AccountId, - beneficiary: ExplicitOrAccountParams, + beneficiary: ExplicitOrAccountParams, amount: Balance, ) -> Result; } @@ -126,9 +128,9 @@ where Zero::zero() } - fn repatriate_reserved( + fn repatriate_reserved( _relayer: &AccountId, - _beneficiary: ExplicitOrAccountParams, + _beneficiary: ExplicitOrAccountParams, _amount: Balance, ) -> Result { Ok(Zero::zero()) diff --git a/bridges/primitives/runtime/Cargo.toml b/bridges/primitives/runtime/Cargo.toml index 117409b37b9457f93194585b12aabd0de00d5c7f..7528f2e5d6caa853a1f2e5695c74e795141bd773 100644 --- a/bridges/primitives/runtime/Cargo.toml +++ b/bridges/primitives/runtime/Cargo.toml @@ -20,7 +20,6 @@ scale-info = { features = ["derive"], workspace = true } serde = { features = ["alloc", "derive"], workspace = true } # Substrate Dependencies - frame-support = { workspace = true } frame-system = { workspace = true } sp-core = { workspace = true } diff --git a/bridges/primitives/runtime/src/chain.rs b/bridges/primitives/runtime/src/chain.rs index 0db4eac79a7500bf295756efaef514f5a4429e6c..eba3bcadfead084878e456ebe8d69cc69d015da0 100644 --- a/bridges/primitives/runtime/src/chain.rs +++ b/bridges/primitives/runtime/src/chain.rs @@ -365,17 +365,23 @@ macro_rules! decl_bridge_finality_runtime_apis { }; } +// Re-export to avoid include tuplex dependency everywhere. +#[doc(hidden)] +pub mod __private { + pub use codec; +} + /// Convenience macro that declares bridge messages runtime apis and related constants for a chain. /// This includes: /// - chain-specific bridge runtime APIs: -/// - `ToOutboundLaneApi` -/// - `FromInboundLaneApi` +/// - `ToOutboundLaneApi` +/// - `FromInboundLaneApi` /// - constants that are stringified names of runtime API methods: /// - `FROM__MESSAGE_DETAILS_METHOD`, /// The name of the chain has to be specified in snake case (e.g. `bridge_hub_polkadot`). #[macro_export] macro_rules! decl_bridge_messages_runtime_apis { - ($chain: ident) => { + ($chain: ident, $lane_id_type:ty) => { bp_runtime::paste::item! { mod [<$chain _messages_api>] { use super::*; @@ -400,7 +406,7 @@ macro_rules! decl_bridge_messages_runtime_apis { /// If some (or all) messages are missing from the storage, they'll also will /// be missing from the resulting vector. The vector is ordered by the nonce. fn message_details( - lane: bp_messages::LaneId, + lane: $lane_id_type, begin: bp_messages::MessageNonce, end: bp_messages::MessageNonce, ) -> sp_std::vec::Vec; @@ -416,7 +422,7 @@ macro_rules! decl_bridge_messages_runtime_apis { pub trait [] { /// Return details of given inbound messages. fn message_details( - lane: bp_messages::LaneId, + lane: $lane_id_type, messages: sp_std::vec::Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>, ) -> sp_std::vec::Vec; } @@ -433,8 +439,8 @@ macro_rules! decl_bridge_messages_runtime_apis { /// The name of the chain has to be specified in snake case (e.g. `bridge_hub_polkadot`). #[macro_export] macro_rules! decl_bridge_runtime_apis { - ($chain: ident $(, $consensus: ident)?) => { + ($chain: ident $(, $consensus: ident, $lane_id_type:ident)?) => { bp_runtime::decl_bridge_finality_runtime_apis!($chain $(, $consensus)?); - bp_runtime::decl_bridge_messages_runtime_apis!($chain); + bp_runtime::decl_bridge_messages_runtime_apis!($chain, $lane_id_type); }; } diff --git a/bridges/primitives/runtime/src/lib.rs b/bridges/primitives/runtime/src/lib.rs index 8f5040ad9a1bee5efad995b4e62d5e883dfb0c9f..90eb72922beaa3bc490c879eacb43a7db521f9a0 100644 --- a/bridges/primitives/runtime/src/lib.rs +++ b/bridges/primitives/runtime/src/lib.rs @@ -36,7 +36,7 @@ use sp_std::{fmt::Debug, ops::RangeInclusive, vec, vec::Vec}; pub use chain::{ AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, Chain, EncodedOrDecodedCall, HashOf, HasherOf, HeaderOf, NonceOf, Parachain, ParachainIdOf, SignatureOf, TransactionEraOf, - UnderlyingChainOf, UnderlyingChainProvider, + UnderlyingChainOf, UnderlyingChainProvider, __private, }; pub use frame_support::storage::storage_prefix as storage_value_final_key; use num_traits::{CheckedAdd, CheckedSub, One, SaturatingAdd, Zero}; @@ -272,7 +272,7 @@ pub trait StorageMapKeyProvider { } } -/// Can be use to access the runtime storage key of a `StorageDoubleMap`. +/// Can be used to access the runtime storage key of a `StorageDoubleMap`. pub trait StorageDoubleMapKeyProvider { /// The name of the variable that holds the `StorageDoubleMap`. const MAP_NAME: &'static str; diff --git a/bridges/primitives/runtime/src/storage_proof.rs b/bridges/primitives/runtime/src/storage_proof.rs index 7bfa0d6fde01186f1fe09e66dd3ba1accf286ce5..8bd9001f2b6c137935f8757e125d8dd5ac4ff8d6 100644 --- a/bridges/primitives/runtime/src/storage_proof.rs +++ b/bridges/primitives/runtime/src/storage_proof.rs @@ -18,7 +18,7 @@ use frame_support::PalletError; use sp_core::RuntimeDebug; -use sp_std::{default::Default, vec::Vec}; +use sp_std::vec::Vec; use sp_trie::{ accessed_nodes_tracker::AccessedNodesTracker, read_trie_value, LayoutV1, MemoryDB, StorageProof, }; @@ -280,7 +280,7 @@ where /// Return valid storage proof and state root. /// -/// NOTE: This should only be used for **testing**. +/// Note: This should only be used for **testing**. #[cfg(feature = "std")] pub fn craft_valid_storage_proof() -> (sp_core::H256, RawStorageProof) { use sp_state_machine::{backend::Backend, prove_read, InMemoryBackend}; diff --git a/bridges/primitives/xcm-bridge-hub-router/Cargo.toml b/bridges/primitives/xcm-bridge-hub-router/Cargo.toml index c3cf3356184be676ffae0c212fc20455395d6d09..ba0c51152bd2fa6fd6e03b942cd4ce1fb93b8fd9 100644 --- a/bridges/primitives/xcm-bridge-hub-router/Cargo.toml +++ b/bridges/primitives/xcm-bridge-hub-router/Cargo.toml @@ -18,6 +18,15 @@ scale-info = { features = ["bit-vec", "derive"], workspace = true } sp-runtime = { workspace = true } sp-core = { workspace = true } +# Polkadot Dependencies +xcm = { workspace = true } + [features] default = ["std"] -std = ["codec/std", "scale-info/std", "sp-core/std", "sp-runtime/std"] +std = [ + "codec/std", + "scale-info/std", + "sp-core/std", + "sp-runtime/std", + "xcm/std", +] diff --git a/bridges/primitives/xcm-bridge-hub-router/src/lib.rs b/bridges/primitives/xcm-bridge-hub-router/src/lib.rs index dbedb7a52c7fee85e35c7fadc67d11d8cfa434dc..89123b51ef2f9b18473fcb19aab04458cde50978 100644 --- a/bridges/primitives/xcm-bridge-hub-router/src/lib.rs +++ b/bridges/primitives/xcm-bridge-hub-router/src/lib.rs @@ -22,6 +22,7 @@ use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_core::H256; use sp_runtime::{FixedU128, RuntimeDebug}; +use xcm::latest::prelude::Location; /// Minimal delivery fee factor. pub const MINIMAL_DELIVERY_FEE_FACTOR: FixedU128 = FixedU128::from_u32(1); @@ -32,11 +33,11 @@ pub const MINIMAL_DELIVERY_FEE_FACTOR: FixedU128 = FixedU128::from_u32(1); /// of the bridge queues. pub trait XcmChannelStatusProvider { /// Returns true if the channel is currently congested. - fn is_congested() -> bool; + fn is_congested(with: &Location) -> bool; } impl XcmChannelStatusProvider for () { - fn is_congested() -> bool { + fn is_congested(_with: &Location) -> bool { false } } diff --git a/bridges/primitives/xcm-bridge-hub/Cargo.toml b/bridges/primitives/xcm-bridge-hub/Cargo.toml index 932e9ade019741dbc6a99fcea317aaee539ed9c9..79201a8756f9a61f1f5bc93aa2f631e9ad7fa441 100644 --- a/bridges/primitives/xcm-bridge-hub/Cargo.toml +++ b/bridges/primitives/xcm-bridge-hub/Cargo.toml @@ -11,10 +11,34 @@ repository.workspace = true workspace = true [dependencies] +codec = { features = ["derive"], workspace = true } +scale-info = { features = ["derive"], workspace = true } +serde = { features = ["alloc", "derive"], workspace = true } + +# Bridge Dependencies +bp-messages = { workspace = true } +bp-runtime = { workspace = true } # Substrate Dependencies sp-std = { workspace = true } +sp-io = { workspace = true } +sp-core = { workspace = true } +frame-support = { workspace = true } + +# Polkadot Dependencies +xcm = { workspace = true } [features] default = ["std"] -std = ["sp-std/std"] +std = [ + "bp-messages/std", + "bp-runtime/std", + "codec/std", + "frame-support/std", + "scale-info/std", + "serde/std", + "sp-core/std", + "sp-io/std", + "sp-std/std", + "xcm/std", +] diff --git a/bridges/primitives/xcm-bridge-hub/src/call_info.rs b/bridges/primitives/xcm-bridge-hub/src/call_info.rs new file mode 100644 index 0000000000000000000000000000000000000000..fd4fc67822fe0c390deae6481e5f4cb84f9b0708 --- /dev/null +++ b/bridges/primitives/xcm-bridge-hub/src/call_info.rs @@ -0,0 +1,43 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Defines structures related to calls of the `pallet-xcm-bridge-hub` pallet. + +use bp_messages::MessageNonce; +use codec::{Decode, Encode}; +use scale_info::TypeInfo; +use sp_std::boxed::Box; +use xcm::prelude::VersionedInteriorLocation; + +/// A minimized version of `pallet_xcm_bridge_hub::Call` that can be used without a runtime. +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum XcmBridgeHubCall { + /// `pallet_xcm_bridge_hub::Call::open_bridge` + #[codec(index = 0)] + open_bridge { + /// Universal `InteriorLocation` from the bridged consensus. + bridge_destination_universal_location: Box, + }, + /// `pallet_xcm_bridge_hub::Call::close_bridge` + #[codec(index = 1)] + close_bridge { + /// Universal `InteriorLocation` from the bridged consensus. + bridge_destination_universal_location: Box, + /// The number of messages that we may prune in a single call. + may_prune_messages: MessageNonce, + }, +} diff --git a/bridges/primitives/xcm-bridge-hub/src/lib.rs b/bridges/primitives/xcm-bridge-hub/src/lib.rs index 9745011c902d2c3949b81886c872f438678a11b8..061e7a27506329029da1e62f7ddd56f516b5dd80 100644 --- a/bridges/primitives/xcm-bridge-hub/src/lib.rs +++ b/bridges/primitives/xcm-bridge-hub/src/lib.rs @@ -19,6 +19,688 @@ #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] +use bp_messages::LaneIdType; +use bp_runtime::{AccountIdOf, BalanceOf, Chain}; +pub use call_info::XcmBridgeHubCall; +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::{ + ensure, sp_runtime::RuntimeDebug, CloneNoBound, PalletError, PartialEqNoBound, + RuntimeDebugNoBound, +}; +use scale_info::TypeInfo; +use serde::{Deserialize, Serialize}; +use sp_core::H256; +use sp_io::hashing::blake2_256; +use sp_std::boxed::Box; +use xcm::{ + latest::prelude::*, prelude::XcmVersion, IntoVersion, VersionedInteriorLocation, + VersionedLocation, +}; + +mod call_info; + /// Encoded XCM blob. We expect the bridge messages pallet to use this blob type for both inbound /// and outbound payloads. pub type XcmAsPlainPayload = sp_std::vec::Vec; + +/// Bridge identifier - used **only** for communicating with sibling/parent chains in the same +/// consensus. +/// +/// For example, `SendXcm` implementations (which use the `latest` XCM) can use it to identify a +/// bridge and the corresponding `LaneId` that is used for over-consensus communication between +/// bridge hubs. +/// +/// This identifier is constructed from the `latest` XCM, so it is expected to ensure migration to +/// the `latest` XCM version. This could change the `BridgeId`, but it will not affect the `LaneId`. +/// In other words, `LaneId` will never change, while `BridgeId` could change with (every) XCM +/// upgrade. +#[derive( + Clone, + Copy, + Decode, + Encode, + Eq, + Ord, + PartialOrd, + PartialEq, + TypeInfo, + MaxEncodedLen, + Serialize, + Deserialize, +)] +pub struct BridgeId(H256); + +impl BridgeId { + /// Create bridge identifier from two universal locations. + /// + /// Note: The `BridgeId` is constructed from `latest` XCM, so if stored, you need to ensure + /// compatibility with newer XCM versions. + pub fn new( + universal_source: &InteriorLocation, + universal_destination: &InteriorLocation, + ) -> Self { + const VALUES_SEPARATOR: [u8; 33] = *b"bridges-bridge-id-value-separator"; + + BridgeId( + (universal_source, VALUES_SEPARATOR, universal_destination) + .using_encoded(blake2_256) + .into(), + ) + } +} + +impl core::fmt::Debug for BridgeId { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + core::fmt::Debug::fmt(&self.0, f) + } +} + +/// Local XCM channel manager. +pub trait LocalXcmChannelManager { + /// Error that may be returned when suspending/resuming the bridge. + type Error: sp_std::fmt::Debug; + + /// Returns true if the channel with given location is currently congested. + /// + /// The `with` is guaranteed to be in the same consensus. However, it may point to something + /// below the chain level - like the contract or pallet instance, for example. + fn is_congested(with: &Location) -> bool; + + /// Suspend the bridge, opened by given origin. + /// + /// The `local_origin` is guaranteed to be in the same consensus. However, it may point to + /// something below the chain level - like the contract or pallet instance, for example. + fn suspend_bridge(local_origin: &Location, bridge: BridgeId) -> Result<(), Self::Error>; + + /// Resume the previously suspended bridge, opened by given origin. + /// + /// The `local_origin` is guaranteed to be in the same consensus. However, it may point to + /// something below the chain level - like the contract or pallet instance, for example. + fn resume_bridge(local_origin: &Location, bridge: BridgeId) -> Result<(), Self::Error>; +} + +impl LocalXcmChannelManager for () { + type Error = (); + + fn is_congested(_with: &Location) -> bool { + false + } + + fn suspend_bridge(_local_origin: &Location, _bridge: BridgeId) -> Result<(), Self::Error> { + Ok(()) + } + + fn resume_bridge(_local_origin: &Location, _bridge: BridgeId) -> Result<(), Self::Error> { + Ok(()) + } +} + +/// Bridge state. +#[derive(Clone, Copy, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen, RuntimeDebug)] +pub enum BridgeState { + /// Bridge is opened. Associated lanes are also opened. + Opened, + /// Bridge is suspended. Associated lanes are opened. + /// + /// We keep accepting messages to the bridge. The only difference with the `Opened` state + /// is that we have sent the "Suspended" message/signal to the local bridge origin. + Suspended, + /// Bridge is closed. Associated lanes are also closed. + /// After all outbound messages will be pruned, the bridge will vanish without any traces. + Closed, +} + +/// Bridge metadata. +#[derive( + CloneNoBound, Decode, Encode, Eq, PartialEqNoBound, TypeInfo, MaxEncodedLen, RuntimeDebugNoBound, +)] +#[scale_info(skip_type_params(ThisChain, LaneId))] +pub struct Bridge { + /// Relative location of the bridge origin chain. This is expected to be **convertible** to the + /// `latest` XCM, so the check and migration needs to be ensured. + pub bridge_origin_relative_location: Box, + + /// See [`BridgeLocations::bridge_origin_universal_location`]. + /// Stored for `BridgeId` sanity check. + pub bridge_origin_universal_location: Box, + /// See [`BridgeLocations::bridge_destination_universal_location`]. + /// Stored for `BridgeId` sanity check. + pub bridge_destination_universal_location: Box, + + /// Current bridge state. + pub state: BridgeState, + /// Account with the reserved funds. Derived from `self.bridge_origin_relative_location`. + pub bridge_owner_account: AccountIdOf, + /// Reserved amount on the sovereign account of the sibling bridge origin. + pub deposit: BalanceOf, + + /// Mapping to the unique `LaneId`. + pub lane_id: LaneId, +} + +/// Locations of bridge endpoints at both sides of the bridge. +#[derive(Clone, RuntimeDebug, PartialEq, Eq)] +pub struct BridgeLocations { + /// Relative (to this bridge hub) location of this side of the bridge. + bridge_origin_relative_location: Location, + /// Universal (unique) location of this side of the bridge. + bridge_origin_universal_location: InteriorLocation, + /// Universal (unique) location of other side of the bridge. + bridge_destination_universal_location: InteriorLocation, + /// An identifier of the dedicated bridge message lane. + bridge_id: BridgeId, +} + +/// Errors that may happen when we check bridge locations. +#[derive(Encode, Decode, RuntimeDebug, PartialEq, Eq, PalletError, TypeInfo)] +pub enum BridgeLocationsError { + /// Origin or destination locations are not universal. + NonUniversalLocation, + /// Bridge origin location is not supported. + InvalidBridgeOrigin, + /// Bridge destination is not supported (in general). + InvalidBridgeDestination, + /// Destination location is within the same global consensus. + DestinationIsLocal, + /// Destination network is not the network we are bridged with. + UnreachableDestination, + /// Destination location is unsupported. We only support bridges with relay + /// chain or its parachains. + UnsupportedDestinationLocation, + /// The version of XCM location argument is unsupported. + UnsupportedXcmVersion, + /// The `LaneIdType` generator is not supported. + UnsupportedLaneIdType, +} + +impl BridgeLocations { + /// Given XCM locations, generate lane id and universal locations of bridge endpoints. + /// + /// The `here_universal_location` is the universal location of the bridge hub runtime. + /// + /// The `bridge_origin_relative_location` is the relative (to the `here_universal_location`) + /// location of the bridge endpoint at this side of the bridge. It may be the parent relay + /// chain or the sibling parachain. All junctions below parachain level are dropped. + /// + /// The `bridge_destination_universal_location` is the universal location of the bridge + /// destination. It may be the parent relay or the sibling parachain of the **bridged** + /// bridge hub. All junctions below parachain level are dropped. + /// + /// Why we drop all junctions between parachain level - that's because the lane is a bridge + /// between two chains. All routing under this level happens when the message is delivered + /// to the bridge destination. So at bridge level we don't care about low level junctions. + /// + /// Returns error if `bridge_origin_relative_location` is outside of `here_universal_location` + /// local consensus OR if `bridge_destination_universal_location` is not a universal location. + pub fn bridge_locations( + here_universal_location: InteriorLocation, + bridge_origin_relative_location: Location, + bridge_destination_universal_location: InteriorLocation, + expected_remote_network: NetworkId, + ) -> Result, BridgeLocationsError> { + fn strip_low_level_junctions( + location: InteriorLocation, + ) -> Result { + let mut junctions = location.into_iter(); + + let global_consensus = junctions + .next() + .filter(|junction| matches!(junction, GlobalConsensus(_))) + .ok_or(BridgeLocationsError::NonUniversalLocation)?; + + // we only expect `Parachain` junction here. There are other junctions that + // may need to be supported (like `GeneralKey` and `OnlyChild`), but now we + // only support bridges with relay and parachans + // + // if there's something other than parachain, let's strip it + let maybe_parachain = + junctions.next().filter(|junction| matches!(junction, Parachain(_))); + Ok(match maybe_parachain { + Some(parachain) => [global_consensus, parachain].into(), + None => [global_consensus].into(), + }) + } + + // ensure that the `here_universal_location` and `bridge_destination_universal_location` + // are universal locations within different consensus systems + let local_network = here_universal_location + .global_consensus() + .map_err(|_| BridgeLocationsError::NonUniversalLocation)?; + let remote_network = bridge_destination_universal_location + .global_consensus() + .map_err(|_| BridgeLocationsError::NonUniversalLocation)?; + ensure!(local_network != remote_network, BridgeLocationsError::DestinationIsLocal); + ensure!( + remote_network == expected_remote_network, + BridgeLocationsError::UnreachableDestination + ); + + // get universal location of endpoint, located at this side of the bridge + let bridge_origin_universal_location = here_universal_location + .within_global(bridge_origin_relative_location.clone()) + .map_err(|_| BridgeLocationsError::InvalidBridgeOrigin)?; + // strip low-level junctions within universal locations + let bridge_origin_universal_location = + strip_low_level_junctions(bridge_origin_universal_location)?; + let bridge_destination_universal_location = + strip_low_level_junctions(bridge_destination_universal_location)?; + + // we know that the `bridge_destination_universal_location` starts from the + // `GlobalConsensus` and we know that the `bridge_origin_universal_location` + // is also within the `GlobalConsensus`. So we know that the lane id will be + // the same on both ends of the bridge + let bridge_id = BridgeId::new( + &bridge_origin_universal_location, + &bridge_destination_universal_location, + ); + + Ok(Box::new(BridgeLocations { + bridge_origin_relative_location, + bridge_origin_universal_location, + bridge_destination_universal_location, + bridge_id, + })) + } + + /// Getter for `bridge_origin_relative_location` + pub fn bridge_origin_relative_location(&self) -> &Location { + &self.bridge_origin_relative_location + } + + /// Getter for `bridge_origin_universal_location` + pub fn bridge_origin_universal_location(&self) -> &InteriorLocation { + &self.bridge_origin_universal_location + } + + /// Getter for `bridge_destination_universal_location` + pub fn bridge_destination_universal_location(&self) -> &InteriorLocation { + &self.bridge_destination_universal_location + } + + /// Getter for `bridge_id` + pub fn bridge_id(&self) -> &BridgeId { + &self.bridge_id + } + + /// Generates the exact same `LaneId` on the both bridge hubs. + /// + /// Note: Use this **only** when opening a new bridge. + pub fn calculate_lane_id( + &self, + xcm_version: XcmVersion, + ) -> Result { + // a tricky helper struct that adds required `Ord` support for + // `VersionedInteriorLocation` + #[derive(Eq, PartialEq, Ord, PartialOrd)] + struct EncodedVersionedInteriorLocation(sp_std::vec::Vec); + impl Encode for EncodedVersionedInteriorLocation { + fn encode(&self) -> sp_std::vec::Vec { + self.0.clone() + } + } + + let universal_location1 = + VersionedInteriorLocation::from(self.bridge_origin_universal_location.clone()) + .into_version(xcm_version) + .map_err(|_| BridgeLocationsError::UnsupportedXcmVersion); + let universal_location2 = + VersionedInteriorLocation::from(self.bridge_destination_universal_location.clone()) + .into_version(xcm_version) + .map_err(|_| BridgeLocationsError::UnsupportedXcmVersion); + + LaneId::try_new( + EncodedVersionedInteriorLocation(universal_location1.encode()), + EncodedVersionedInteriorLocation(universal_location2.encode()), + ) + .map_err(|_| BridgeLocationsError::UnsupportedLaneIdType) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const LOCAL_NETWORK: NetworkId = Kusama; + const REMOTE_NETWORK: NetworkId = Polkadot; + const UNREACHABLE_NETWORK: NetworkId = Rococo; + const SIBLING_PARACHAIN: u32 = 1000; + const LOCAL_BRIDGE_HUB: u32 = 1001; + const REMOTE_PARACHAIN: u32 = 2000; + + struct SuccessfulTest { + here_universal_location: InteriorLocation, + bridge_origin_relative_location: Location, + + bridge_origin_universal_location: InteriorLocation, + bridge_destination_universal_location: InteriorLocation, + + expected_remote_network: NetworkId, + } + + fn run_successful_test(test: SuccessfulTest) -> BridgeLocations { + let locations = BridgeLocations::bridge_locations( + test.here_universal_location, + test.bridge_origin_relative_location.clone(), + test.bridge_destination_universal_location.clone(), + test.expected_remote_network, + ); + assert_eq!( + locations, + Ok(Box::new(BridgeLocations { + bridge_origin_relative_location: test.bridge_origin_relative_location, + bridge_origin_universal_location: test.bridge_origin_universal_location.clone(), + bridge_destination_universal_location: test + .bridge_destination_universal_location + .clone(), + bridge_id: BridgeId::new( + &test.bridge_origin_universal_location, + &test.bridge_destination_universal_location, + ), + })), + ); + + *locations.unwrap() + } + + // successful tests that with various origins and destinations + + #[test] + fn at_relay_from_local_relay_to_remote_relay_works() { + run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_origin_relative_location: Here.into(), + + bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(), + + expected_remote_network: REMOTE_NETWORK, + }); + } + + #[test] + fn at_relay_from_sibling_parachain_to_remote_relay_works() { + run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_origin_relative_location: [Parachain(SIBLING_PARACHAIN)].into(), + + bridge_origin_universal_location: [ + GlobalConsensus(LOCAL_NETWORK), + Parachain(SIBLING_PARACHAIN), + ] + .into(), + bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(), + + expected_remote_network: REMOTE_NETWORK, + }); + } + + #[test] + fn at_relay_from_local_relay_to_remote_parachain_works() { + run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_origin_relative_location: Here.into(), + + bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_destination_universal_location: [ + GlobalConsensus(REMOTE_NETWORK), + Parachain(REMOTE_PARACHAIN), + ] + .into(), + + expected_remote_network: REMOTE_NETWORK, + }); + } + + #[test] + fn at_relay_from_sibling_parachain_to_remote_parachain_works() { + run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_origin_relative_location: [Parachain(SIBLING_PARACHAIN)].into(), + + bridge_origin_universal_location: [ + GlobalConsensus(LOCAL_NETWORK), + Parachain(SIBLING_PARACHAIN), + ] + .into(), + bridge_destination_universal_location: [ + GlobalConsensus(REMOTE_NETWORK), + Parachain(REMOTE_PARACHAIN), + ] + .into(), + + expected_remote_network: REMOTE_NETWORK, + }); + } + + #[test] + fn at_bridge_hub_from_local_relay_to_remote_relay_works() { + run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK), Parachain(LOCAL_BRIDGE_HUB)] + .into(), + bridge_origin_relative_location: Parent.into(), + + bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(), + + expected_remote_network: REMOTE_NETWORK, + }); + } + + #[test] + fn at_bridge_hub_from_sibling_parachain_to_remote_relay_works() { + run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK), Parachain(LOCAL_BRIDGE_HUB)] + .into(), + bridge_origin_relative_location: ParentThen([Parachain(SIBLING_PARACHAIN)].into()) + .into(), + + bridge_origin_universal_location: [ + GlobalConsensus(LOCAL_NETWORK), + Parachain(SIBLING_PARACHAIN), + ] + .into(), + bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(), + + expected_remote_network: REMOTE_NETWORK, + }); + } + + #[test] + fn at_bridge_hub_from_local_relay_to_remote_parachain_works() { + run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK), Parachain(LOCAL_BRIDGE_HUB)] + .into(), + bridge_origin_relative_location: Parent.into(), + + bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_destination_universal_location: [ + GlobalConsensus(REMOTE_NETWORK), + Parachain(REMOTE_PARACHAIN), + ] + .into(), + + expected_remote_network: REMOTE_NETWORK, + }); + } + + #[test] + fn at_bridge_hub_from_sibling_parachain_to_remote_parachain_works() { + run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK), Parachain(LOCAL_BRIDGE_HUB)] + .into(), + bridge_origin_relative_location: ParentThen([Parachain(SIBLING_PARACHAIN)].into()) + .into(), + + bridge_origin_universal_location: [ + GlobalConsensus(LOCAL_NETWORK), + Parachain(SIBLING_PARACHAIN), + ] + .into(), + bridge_destination_universal_location: [ + GlobalConsensus(REMOTE_NETWORK), + Parachain(REMOTE_PARACHAIN), + ] + .into(), + + expected_remote_network: REMOTE_NETWORK, + }); + } + + // successful tests that show that we are ignoring low-level junctions of bridge origins + + #[test] + fn low_level_junctions_at_bridge_origin_are_stripped() { + let locations1 = run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_origin_relative_location: Here.into(), + + bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(), + + expected_remote_network: REMOTE_NETWORK, + }); + let locations2 = run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_origin_relative_location: [PalletInstance(0)].into(), + + bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(), + + expected_remote_network: REMOTE_NETWORK, + }); + + assert_eq!(locations1.bridge_id, locations2.bridge_id); + } + + #[test] + fn low_level_junctions_at_bridge_destination_are_stripped() { + let locations1 = run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_origin_relative_location: Here.into(), + + bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(), + + expected_remote_network: REMOTE_NETWORK, + }); + let locations2 = run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_origin_relative_location: Here.into(), + + bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(), + + expected_remote_network: REMOTE_NETWORK, + }); + + assert_eq!(locations1.bridge_id, locations2.bridge_id); + } + + #[test] + fn calculate_lane_id_works() { + type TestLaneId = bp_messages::HashedLaneId; + + let from_local_to_remote = run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK), Parachain(LOCAL_BRIDGE_HUB)] + .into(), + bridge_origin_relative_location: ParentThen([Parachain(SIBLING_PARACHAIN)].into()) + .into(), + + bridge_origin_universal_location: [ + GlobalConsensus(LOCAL_NETWORK), + Parachain(SIBLING_PARACHAIN), + ] + .into(), + bridge_destination_universal_location: [ + GlobalConsensus(REMOTE_NETWORK), + Parachain(REMOTE_PARACHAIN), + ] + .into(), + + expected_remote_network: REMOTE_NETWORK, + }); + + let from_remote_to_local = run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(REMOTE_NETWORK), Parachain(LOCAL_BRIDGE_HUB)] + .into(), + bridge_origin_relative_location: ParentThen([Parachain(REMOTE_PARACHAIN)].into()) + .into(), + + bridge_origin_universal_location: [ + GlobalConsensus(REMOTE_NETWORK), + Parachain(REMOTE_PARACHAIN), + ] + .into(), + bridge_destination_universal_location: [ + GlobalConsensus(LOCAL_NETWORK), + Parachain(SIBLING_PARACHAIN), + ] + .into(), + + expected_remote_network: LOCAL_NETWORK, + }); + + assert_ne!( + from_local_to_remote.calculate_lane_id::(xcm::latest::VERSION), + from_remote_to_local.calculate_lane_id::(xcm::latest::VERSION - 1), + ); + assert_eq!( + from_local_to_remote.calculate_lane_id::(xcm::latest::VERSION), + from_remote_to_local.calculate_lane_id::(xcm::latest::VERSION), + ); + } + + // negative tests + + #[test] + fn bridge_locations_fails_when_here_is_not_universal_location() { + assert_eq!( + BridgeLocations::bridge_locations( + [Parachain(1000)].into(), + Here.into(), + [GlobalConsensus(REMOTE_NETWORK)].into(), + REMOTE_NETWORK, + ), + Err(BridgeLocationsError::NonUniversalLocation), + ); + } + + #[test] + fn bridge_locations_fails_when_computed_destination_is_not_universal_location() { + assert_eq!( + BridgeLocations::bridge_locations( + [GlobalConsensus(LOCAL_NETWORK)].into(), + Here.into(), + [OnlyChild].into(), + REMOTE_NETWORK, + ), + Err(BridgeLocationsError::NonUniversalLocation), + ); + } + + #[test] + fn bridge_locations_fails_when_computed_destination_is_local() { + assert_eq!( + BridgeLocations::bridge_locations( + [GlobalConsensus(LOCAL_NETWORK)].into(), + Here.into(), + [GlobalConsensus(LOCAL_NETWORK), OnlyChild].into(), + REMOTE_NETWORK, + ), + Err(BridgeLocationsError::DestinationIsLocal), + ); + } + + #[test] + fn bridge_locations_fails_when_computed_destination_is_unreachable() { + assert_eq!( + BridgeLocations::bridge_locations( + [GlobalConsensus(LOCAL_NETWORK)].into(), + Here.into(), + [GlobalConsensus(UNREACHABLE_NETWORK)].into(), + REMOTE_NETWORK, + ), + Err(BridgeLocationsError::UnreachableDestination), + ); + } +} diff --git a/bridges/relays/client-substrate/Cargo.toml b/bridges/relays/client-substrate/Cargo.toml index 969cd73d6194fcf42e03f54ae14029cfdf73d877..6065c23773e3f6808ee0a56f07f0ef0325378fce 100644 --- a/bridges/relays/client-substrate/Cargo.toml +++ b/bridges/relays/client-substrate/Cargo.toml @@ -20,8 +20,12 @@ log = { workspace = true } num-traits = { workspace = true, default-features = true } rand = { workspace = true, default-features = true } serde_json = { workspace = true } -scale-info = { features = ["derive"], workspace = true, default-features = true } -tokio = { features = ["rt-multi-thread"], workspace = true, default-features = true } +scale-info = { features = [ + "derive", +], workspace = true, default-features = true } +tokio = { features = [ + "rt-multi-thread", +], workspace = true, default-features = true } thiserror = { workspace = true } quick_cache = { workspace = true } diff --git a/bridges/relays/client-substrate/src/chain.rs b/bridges/relays/client-substrate/src/chain.rs index 227e9c31c5bfc6a93df88bb117f29bdb1b7c61d3..9856f0d0237ea63997ffc24baf0f1f099df9ef6b 100644 --- a/bridges/relays/client-substrate/src/chain.rs +++ b/bridges/relays/client-substrate/src/chain.rs @@ -113,9 +113,6 @@ impl Parachain for T where T: UnderlyingChainProvider + Chain + ParachainBase /// Substrate-based chain with messaging support from minimal relay-client point of view. pub trait ChainWithMessages: Chain + ChainWithMessagesBase { - // TODO (https://github.com/paritytech/parity-bridges-common/issues/1692): check all the names - // after the issue is fixed - all names must be changed - /// Name of the bridge relayers pallet (used in `construct_runtime` macro call) that is deployed /// at some other chain to bridge with this `ChainWithMessages`. /// diff --git a/bridges/relays/lib-substrate-relay/src/cli/bridge.rs b/bridges/relays/lib-substrate-relay/src/cli/bridge.rs index 5631285b3c544de0e3caf85a6b74b7ee31601c56..9467813f86cc94555d8dfb003d4c675cf025b5f5 100644 --- a/bridges/relays/lib-substrate-relay/src/cli/bridge.rs +++ b/bridges/relays/lib-substrate-relay/src/cli/bridge.rs @@ -22,7 +22,7 @@ use crate::{ messages::{MessagesRelayLimits, SubstrateMessageLane}, parachains::SubstrateParachainsPipeline, }; -use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; +use bp_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; use relay_substrate_client::{ Chain, ChainWithRuntimeVersion, ChainWithTransactions, Parachain, RelayChain, }; @@ -108,3 +108,7 @@ pub trait MessagesCliBridge: CliBridgeBase { None } } + +/// An alias for lane identifier type. +pub type MessagesLaneIdOf = + <::MessagesLane as SubstrateMessageLane>::LaneId; diff --git a/bridges/relays/lib-substrate-relay/src/cli/mod.rs b/bridges/relays/lib-substrate-relay/src/cli/mod.rs index ddb3e416dc32628b5a85f27f322d0568cbee1c10..d7aa38f1f2bafc23b878847e182379b3a0f402c4 100644 --- a/bridges/relays/lib-substrate-relay/src/cli/mod.rs +++ b/bridges/relays/lib-substrate-relay/src/cli/mod.rs @@ -16,12 +16,10 @@ //! Deal with CLI args of substrate-to-substrate relay. -use codec::{Decode, Encode}; use rbtag::BuildInfo; +use sp_runtime::traits::TryConvert; +use std::str::FromStr; use structopt::StructOpt; -use strum::{EnumString, VariantNames}; - -use bp_messages::LaneId; pub mod bridge; pub mod chain_schema; @@ -42,45 +40,19 @@ pub type DefaultClient = relay_substrate_client::RpcWithCachingClient; /// Lane id. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct HexLaneId(pub [u8; 4]); +pub struct HexLaneId(Vec); -impl From for LaneId { - fn from(lane_id: HexLaneId) -> LaneId { - LaneId(lane_id.0) +impl>> TryConvert for HexLaneId { + fn try_convert(lane_id: HexLaneId) -> Result { + T::try_from(lane_id.0.clone()).map_err(|_| lane_id) } } -impl std::str::FromStr for HexLaneId { +impl FromStr for HexLaneId { type Err = hex::FromHexError; fn from_str(s: &str) -> Result { - let mut lane_id = [0u8; 4]; - hex::decode_to_slice(s, &mut lane_id)?; - Ok(HexLaneId(lane_id)) - } -} - -/// Nicer formatting for raw bytes vectors. -#[derive(Default, Encode, Decode, PartialEq, Eq)] -pub struct HexBytes(pub Vec); - -impl std::str::FromStr for HexBytes { - type Err = hex::FromHexError; - - fn from_str(s: &str) -> Result { - Ok(Self(hex::decode(s)?)) - } -} - -impl std::fmt::Debug for HexBytes { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(fmt, "0x{self}") - } -} - -impl std::fmt::Display for HexBytes { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(fmt, "{}", hex::encode(&self.0)) + hex::decode(s).map(Self) } } @@ -166,31 +138,39 @@ where } } -#[doc = "Runtime version params."] -#[derive(StructOpt, Debug, PartialEq, Eq, Clone, Copy, EnumString, VariantNames)] -pub enum RuntimeVersionType { - /// Auto query version from chain - Auto, - /// Custom `spec_version` and `transaction_version` - Custom, - /// Read version from bundle dependencies directly. - Bundle, -} - #[cfg(test)] mod tests { use super::*; + use bp_messages::{HashedLaneId, LegacyLaneId}; + use sp_core::H256; #[test] - fn hex_bytes_display_matches_from_str_for_clap() { - // given - let hex = HexBytes(vec![1, 2, 3, 4]); - let display = format!("{hex}"); - - // when - let hex2: HexBytes = display.parse().unwrap(); - - // then - assert_eq!(hex.0, hex2.0); + fn hex_lane_id_from_str_works() { + // hash variant + assert!(HexLaneId::from_str( + "101010101010101010101010101010101010101010101010101010101010101" + ) + .is_err()); + assert!(HexLaneId::from_str( + "00101010101010101010101010101010101010101010101010101010101010101" + ) + .is_err()); + assert_eq!( + HexLaneId::try_convert( + HexLaneId::from_str( + "0101010101010101010101010101010101010101010101010101010101010101" + ) + .unwrap() + ), + Ok(HashedLaneId::from_inner(H256::from([1u8; 32]))) + ); + + // array variant + assert!(HexLaneId::from_str("0000001").is_err()); + assert!(HexLaneId::from_str("000000001").is_err()); + assert_eq!( + HexLaneId::try_convert(HexLaneId::from_str("00000001").unwrap()), + Ok(LegacyLaneId([0, 0, 0, 1])) + ); } } diff --git a/bridges/relays/lib-substrate-relay/src/cli/relay_headers.rs b/bridges/relays/lib-substrate-relay/src/cli/relay_headers.rs index ea92a0c9acce15a7f5000d0dd7e0253bbafc7844..308b041c46f790c637106aa08db08b50f9746d97 100644 --- a/bridges/relays/lib-substrate-relay/src/cli/relay_headers.rs +++ b/bridges/relays/lib-substrate-relay/src/cli/relay_headers.rs @@ -96,6 +96,7 @@ pub trait HeadersRelayer: RelayToRelayHeadersCliBridge { signer: target_sign, mortality: target_transactions_mortality, }; + Self::Finality::start_relay_guards(&target_client, target_client.can_start_version_guard()) .await?; diff --git a/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/mod.rs b/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/mod.rs index 338dda3c63309acbefd2616d052ae5dc4bf1d1e0..bb6c689a76eb02bdbf6def9d3ac5d4742817ab06 100644 --- a/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/mod.rs +++ b/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/mod.rs @@ -31,18 +31,20 @@ pub mod relay_to_relay; pub mod relay_to_parachain; use async_trait::async_trait; -use std::{marker::PhantomData, sync::Arc}; +use std::{fmt::Debug, marker::PhantomData, sync::Arc}; use structopt::StructOpt; use futures::{FutureExt, TryFutureExt}; use crate::{ - cli::{bridge::MessagesCliBridge, DefaultClient, HexLaneId, PrometheusParams}, + cli::{ + bridge::{MessagesCliBridge, MessagesLaneIdOf}, + DefaultClient, HexLaneId, PrometheusParams, + }, messages::{MessagesRelayLimits, MessagesRelayParams}, on_demand::OnDemandRelay, HeadersToRelay, TaggedAccount, TransactionParams, }; -use bp_messages::LaneId; use bp_runtime::BalanceOf; use relay_substrate_client::{ AccountIdOf, AccountKeyPairOf, Chain, ChainWithBalances, ChainWithMessages, @@ -50,12 +52,13 @@ use relay_substrate_client::{ }; use relay_utils::metrics::MetricsParams; use sp_core::Pair; +use sp_runtime::traits::TryConvert; /// Parameters that have the same names across all bridges. #[derive(Debug, PartialEq, StructOpt)] pub struct HeadersAndMessagesSharedParams { /// Hex-encoded lane identifiers that should be served by the complex relay. - #[structopt(long, default_value = "00000000")] + #[structopt(long)] pub lane: Vec, /// If passed, only mandatory headers (headers that are changing the GRANDPA authorities set) /// are relayed. @@ -163,7 +166,7 @@ where &self, source_to_target_headers_relay: Arc>, target_to_source_headers_relay: Arc>, - lane_id: LaneId, + lane_id: MessagesLaneIdOf, maybe_limits: Option, ) -> MessagesRelayParams, DefaultClient> { MessagesRelayParams { @@ -287,36 +290,57 @@ where self.mut_base().start_on_demand_headers_relayers().await?; // add balance-related metrics - let lanes = self + let lanes_l2r: Vec> = self + .base() + .common() + .shared + .lane + .iter() + .cloned() + .map(HexLaneId::try_convert) + .collect::, HexLaneId>>() + .map_err(|e| { + anyhow::format_err!("Conversion failed for L2R lanes with error: {:?}!", e) + })?; + let lanes_r2l: Vec> = self .base() .common() .shared .lane .iter() .cloned() - .map(Into::into) - .collect::>(); + .map(HexLaneId::try_convert) + .collect::, HexLaneId>>() + .map_err(|e| { + anyhow::format_err!("Conversion failed for R2L lanes with error: {:?}!", e) + })?; { let common = self.mut_base().mut_common(); - crate::messages::metrics::add_relay_balances_metrics::<_, Self::Right>( - common.left.client.clone(), - &common.metrics_params, - &common.left.accounts, - &lanes, + crate::messages::metrics::add_relay_balances_metrics::< + _, + Self::Right, + MessagesLaneIdOf, + >( + common.left.client.clone(), &common.metrics_params, &common.left.accounts, &lanes_l2r ) .await?; - crate::messages::metrics::add_relay_balances_metrics::<_, Self::Left>( + crate::messages::metrics::add_relay_balances_metrics::< + _, + Self::Left, + MessagesLaneIdOf, + >( common.right.client.clone(), &common.metrics_params, &common.right.accounts, - &lanes, + &lanes_r2l, ) .await?; } // Need 2x capacity since we consider both directions for each lane - let mut message_relays = Vec::with_capacity(lanes.len() * 2); - for lane in lanes { + let mut message_relays = + Vec::with_capacity(lanes_l2r.len().saturating_add(lanes_r2l.len())); + for lane in lanes_l2r { let left_to_right_messages = crate::messages::run::<::MessagesLane, _, _>( self.left_to_right().messages_relay_params( @@ -329,7 +353,8 @@ where .map_err(|e| anyhow::format_err!("{}", e)) .boxed(); message_relays.push(left_to_right_messages); - + } + for lane in lanes_r2l { let right_to_left_messages = crate::messages::run::<::MessagesLane, _, _>( self.right_to_left().messages_relay_params( @@ -422,7 +447,7 @@ mod tests { "--polkadot-port", "9944", "--lane", - "00000000", + "0000000000000000000000000000000000000000000000000000000000000000", "--prometheus-host", "0.0.0.0", ]); @@ -432,7 +457,7 @@ mod tests { res, BridgeHubKusamaBridgeHubPolkadotHeadersAndMessages { shared: HeadersAndMessagesSharedParams { - lane: vec![HexLaneId([0x00, 0x00, 0x00, 0x00])], + lane: vec![HexLaneId(vec![0x00u8; 32])], only_mandatory_headers: false, only_free_headers: false, prometheus_params: PrometheusParams { diff --git a/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/parachain_to_parachain.rs b/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/parachain_to_parachain.rs index 8104be7af807a67bbc001e70e24565c81b6beb17..e8b797f84fa51d39c42ae761839fd9335f23bc84 100644 --- a/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/parachain_to_parachain.rs +++ b/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/parachain_to_parachain.rs @@ -30,8 +30,8 @@ use crate::{ headers::OnDemandHeadersRelay, parachains::OnDemandParachainsRelay, OnDemandRelay, }, }; +use bp_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; use bp_polkadot_core::parachains::ParaHash; -use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; use relay_substrate_client::{ AccountIdOf, AccountKeyPairOf, Chain, ChainWithRuntimeVersion, ChainWithTransactions, Client, Parachain, diff --git a/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/relay_to_parachain.rs b/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/relay_to_parachain.rs index 6c078973fedc08a724e51808f2cb47f3a64ca1a1..f9884ee197b4002305f0b56e315837b3ce4f3709 100644 --- a/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/relay_to_parachain.rs +++ b/bridges/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/relay_to_parachain.rs @@ -33,8 +33,8 @@ use crate::{ headers::OnDemandHeadersRelay, parachains::OnDemandParachainsRelay, OnDemandRelay, }, }; +use bp_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; use bp_polkadot_core::parachains::ParaHash; -use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; use relay_substrate_client::{ AccountIdOf, AccountKeyPairOf, Chain, ChainWithRuntimeVersion, ChainWithTransactions, Client, Parachain, diff --git a/bridges/relays/lib-substrate-relay/src/cli/relay_messages.rs b/bridges/relays/lib-substrate-relay/src/cli/relay_messages.rs index 68bbe71ae599c901bdf7a9b6e55cf93c020adbb6..71d3adc078e2bfcee32a82a5cb8544e6a0e3a88d 100644 --- a/bridges/relays/lib-substrate-relay/src/cli/relay_messages.rs +++ b/bridges/relays/lib-substrate-relay/src/cli/relay_messages.rs @@ -33,12 +33,13 @@ use relay_substrate_client::{ ChainWithTransactions, Client, }; use relay_utils::UniqueSaturatedInto; +use sp_runtime::traits::TryConvert; /// Messages relaying params. #[derive(StructOpt)] pub struct RelayMessagesParams { - /// Hex-encoded lane id that should be served by the relay. Defaults to `00000000`. - #[structopt(long, default_value = "00000000")] + /// Hex-encoded lane id that should be served by the relay. + #[structopt(long)] lane: HexLaneId, #[structopt(flatten)] source: SourceConnectionParams, @@ -59,8 +60,8 @@ pub struct RelayMessagesRangeParams { /// This header must be previously proved to the target chain. #[structopt(long)] at_source_block: u128, - /// Hex-encoded lane id that should be served by the relay. Defaults to `00000000`. - #[structopt(long, default_value = "00000000")] + /// Hex-encoded lane id that should be served by the relay. + #[structopt(long)] lane: HexLaneId, /// Nonce (inclusive) of the first message to relay. #[structopt(long)] @@ -88,8 +89,8 @@ pub struct RelayMessagesDeliveryConfirmationParams { /// delivery proof. This header must be previously proved to the source chain. #[structopt(long)] at_target_block: u128, - /// Hex-encoded lane id that should be served by the relay. Defaults to `00000000`. - #[structopt(long, default_value = "00000000")] + /// Hex-encoded lane id that should be served by the relay. + #[structopt(long)] lane: HexLaneId, #[structopt(flatten)] source: SourceConnectionParams, @@ -116,6 +117,11 @@ where let target_client = data.target.into_client::().await?; let target_sign = data.target_sign.to_keypair::()?; let target_transactions_mortality = data.target_sign.transactions_mortality()?; + let lane_id = HexLaneId::try_convert(data.lane).map_err(|invalid_lane_id| { + anyhow::format_err!("Invalid laneId: {:?}!", invalid_lane_id) + })?; + + Self::start_relay_guards(&target_client, target_client.can_start_version_guard()).await?; crate::messages::run::(MessagesRelayParams { source_client, @@ -130,7 +136,7 @@ where }, source_to_target_headers_relay: None, target_to_source_headers_relay: None, - lane_id: data.lane.into(), + lane_id, limits: Self::maybe_messages_limits(), metrics_params: data.prometheus_params.into_metrics_params()?, }) @@ -146,6 +152,9 @@ where let source_transactions_mortality = data.source_sign.transactions_mortality()?; let target_sign = data.target_sign.to_keypair::()?; let target_transactions_mortality = data.target_sign.transactions_mortality()?; + let lane_id = HexLaneId::try_convert(data.lane).map_err(|invalid_lane_id| { + anyhow::format_err!("Invalid laneId: {:?}!", invalid_lane_id) + })?; let at_source_block = source_client .header_by_number(data.at_source_block.unique_saturated_into()) @@ -167,7 +176,7 @@ where TransactionParams { signer: source_sign, mortality: source_transactions_mortality }, TransactionParams { signer: target_sign, mortality: target_transactions_mortality }, at_source_block, - data.lane.into(), + lane_id, data.messages_start..=data.messages_end, data.outbound_state_proof_required, ) @@ -182,6 +191,9 @@ where let target_client = data.target.into_client::().await?; let source_sign = data.source_sign.to_keypair::()?; let source_transactions_mortality = data.source_sign.transactions_mortality()?; + let lane_id = HexLaneId::try_convert(data.lane).map_err(|invalid_lane_id| { + anyhow::format_err!("Invalid laneId: {:?}!", invalid_lane_id) + })?; let at_target_block = target_client .header_by_number(data.at_target_block.unique_saturated_into()) @@ -202,8 +214,22 @@ where target_client, TransactionParams { signer: source_sign, mortality: source_transactions_mortality }, at_target_block, - data.lane.into(), + lane_id, ) .await } + + /// Add relay guards if required. + async fn start_relay_guards( + target_client: &impl Client, + enable_version_guard: bool, + ) -> relay_substrate_client::Result<()> { + if enable_version_guard { + relay_substrate_client::guard::abort_on_spec_version_change( + target_client.clone(), + target_client.simple_runtime_version().await?.spec_version, + ); + } + Ok(()) + } } diff --git a/bridges/relays/lib-substrate-relay/src/cli/relay_parachains.rs b/bridges/relays/lib-substrate-relay/src/cli/relay_parachains.rs index 77cd395ff7225e9a0d088cc35cf0f86ab78caf46..83285b69f70157ab5a05831af006535b08a7cf3d 100644 --- a/bridges/relays/lib-substrate-relay/src/cli/relay_parachains.rs +++ b/bridges/relays/lib-substrate-relay/src/cli/relay_parachains.rs @@ -32,6 +32,7 @@ use crate::{ chain_schema::*, DefaultClient, PrometheusParams, }, + finality::SubstrateFinalitySyncPipeline, parachains::{source::ParachainsSource, target::ParachainsTarget, ParachainsPipelineAdapter}, TransactionParams, }; @@ -104,6 +105,12 @@ where data.prometheus_params.into_metrics_params()?; GlobalMetrics::new()?.register_and_spawn(&metrics_params.registry)?; + Self::RelayFinality::start_relay_guards( + target_client.target_client(), + target_client.target_client().can_start_version_guard(), + ) + .await?; + parachains_relay::parachains_loop::run( source_client, target_client, diff --git a/bridges/relays/lib-substrate-relay/src/messages/metrics.rs b/bridges/relays/lib-substrate-relay/src/messages/metrics.rs index 8845f43dcb62aa778b50354141381290fc0b897d..efe429701c41b6796433db1f6f4cef5d42ebc331 100644 --- a/bridges/relays/lib-substrate-relay/src/messages/metrics.rs +++ b/bridges/relays/lib-substrate-relay/src/messages/metrics.rs @@ -18,11 +18,11 @@ use crate::TaggedAccount; -use bp_messages::LaneId; use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; use bp_runtime::StorageDoubleMapKeyProvider; -use codec::Decode; +use codec::{Decode, EncodeLike}; use frame_system::AccountInfo; +use messages_relay::Labeled; use pallet_balances::AccountData; use relay_substrate_client::{ metrics::{FloatStorageValue, FloatStorageValueMetric}, @@ -35,7 +35,7 @@ use sp_runtime::{FixedPointNumber, FixedU128}; use std::{fmt::Debug, marker::PhantomData}; /// Add relay accounts balance metrics. -pub async fn add_relay_balances_metrics( +pub async fn add_relay_balances_metrics( client: impl Client, metrics: &MetricsParams, relay_accounts: &Vec>>, @@ -43,6 +43,7 @@ pub async fn add_relay_balances_metrics anyhow::Result<()> where BalanceOf: Into + std::fmt::Debug, + LaneId: Clone + Copy + Decode + EncodeLike + Send + Sync + Labeled, { if relay_accounts.is_empty() { return Ok(()) @@ -52,9 +53,8 @@ where let token_decimals = client .token_decimals() .await? - .map(|token_decimals| { + .inspect(|token_decimals| { log::info!(target: "bridge", "Read `tokenDecimals` for {}: {}", C::NAME, token_decimals); - token_decimals }) .unwrap_or_else(|| { // turns out it is normal not to have this property - e.g. when polkadot binary is @@ -86,25 +86,25 @@ where FloatStorageValueMetric::new( AccountBalance:: { token_decimals, _phantom: Default::default() }, client.clone(), - bp_relayers::RelayerRewardsKeyProvider::, BalanceOf>::final_key( + bp_relayers::RelayerRewardsKeyProvider::, BalanceOf, LaneId>::final_key( relayers_pallet_name, account.id(), &RewardsAccountParams::new(*lane, BC::ID, RewardsAccountOwner::ThisChain), ), - format!("at_{}_relay_{}_reward_for_msgs_from_{}_on_lane_{}", C::NAME, account.tag(), BC::NAME, hex::encode(lane.as_ref())), - format!("Reward of the {} relay account at {} for delivering messages from {} on lane {:?}", account.tag(), C::NAME, BC::NAME, lane), + format!("at_{}_relay_{}_reward_for_msgs_from_{}_on_lane_{}", C::NAME, account.tag(), BC::NAME, lane.label()), + format!("Reward of the {} relay account at {} for delivering messages from {} on lane {:?}", account.tag(), C::NAME, BC::NAME, lane.label()), )?.register_and_spawn(&metrics.registry)?; FloatStorageValueMetric::new( AccountBalance:: { token_decimals, _phantom: Default::default() }, client.clone(), - bp_relayers::RelayerRewardsKeyProvider::, BalanceOf>::final_key( + bp_relayers::RelayerRewardsKeyProvider::, BalanceOf, LaneId>::final_key( relayers_pallet_name, account.id(), &RewardsAccountParams::new(*lane, BC::ID, RewardsAccountOwner::BridgedChain), ), - format!("at_{}_relay_{}_reward_for_msgs_to_{}_on_lane_{}", C::NAME, account.tag(), BC::NAME, hex::encode(lane.as_ref())), - format!("Reward of the {} relay account at {} for delivering messages confirmations from {} on lane {:?}", account.tag(), C::NAME, BC::NAME, lane), + format!("at_{}_relay_{}_reward_for_msgs_to_{}_on_lane_{}", C::NAME, account.tag(), BC::NAME, lane.label()), + format!("Reward of the {} relay account at {} for delivering messages confirmations from {} on lane {:?}", account.tag(), C::NAME, BC::NAME, lane.label()), )?.register_and_spawn(&metrics.registry)?; } } diff --git a/bridges/relays/lib-substrate-relay/src/messages/mod.rs b/bridges/relays/lib-substrate-relay/src/messages/mod.rs index e52b7020666941c9a8d9937655496acf451c5379..f7031648bc35d3b0dc249ae25888409eb368d0d9 100644 --- a/bridges/relays/lib-substrate-relay/src/messages/mod.rs +++ b/bridges/relays/lib-substrate-relay/src/messages/mod.rs @@ -27,19 +27,17 @@ use crate::{ use async_std::sync::Arc; use bp_messages::{ - target_chain::FromBridgedChainMessagesProof, ChainWithMessages as _, LaneId, MessageNonce, + target_chain::FromBridgedChainMessagesProof, ChainWithMessages as _, MessageNonce, }; -use bp_runtime::{ - AccountIdOf, Chain as _, EncodedOrDecodedCall, HeaderIdOf, TransactionEra, WeightExtraOps, -}; -use codec::Encode; +use bp_runtime::{AccountIdOf, EncodedOrDecodedCall, HeaderIdOf, TransactionEra, WeightExtraOps}; +use codec::{Codec, Encode, EncodeLike}; use frame_support::{dispatch::GetDispatchInfo, weights::Weight}; -use messages_relay::{message_lane::MessageLane, message_lane_loop::BatchTransaction}; +use messages_relay::{message_lane::MessageLane, message_lane_loop::BatchTransaction, Labeled}; use pallet_bridge_messages::{Call as BridgeMessagesCall, Config as BridgeMessagesConfig}; use relay_substrate_client::{ transaction_stall_timeout, AccountKeyPairOf, BalanceOf, BlockNumberOf, CallOf, Chain, - ChainWithMessages, ChainWithTransactions, Client, Error as SubstrateError, HashOf, SignParam, - UnsignedTransaction, + ChainBase, ChainWithMessages, ChainWithTransactions, Client, Error as SubstrateError, HashOf, + SignParam, UnsignedTransaction, }; use relay_utils::{ metrics::{GlobalMetrics, MetricsParams, StandaloneMetric}, @@ -60,6 +58,18 @@ pub trait SubstrateMessageLane: 'static + Clone + Debug + Send + Sync { /// Messages from the `SourceChain` are dispatched on this chain. type TargetChain: ChainWithMessages + ChainWithTransactions; + /// Lane identifier type. + type LaneId: Clone + + Copy + + Debug + + Codec + + EncodeLike + + Send + + Sync + + Labeled + + TryFrom> + + Default; + /// How receive messages proof call is built? type ReceiveMessagesProofCallBuilder: ReceiveMessagesProofCallBuilder; /// How receive messages delivery proof call is built? @@ -81,8 +91,10 @@ impl MessageLane for MessageLaneAdapter

{ const SOURCE_NAME: &'static str = P::SourceChain::NAME; const TARGET_NAME: &'static str = P::TargetChain::NAME; - type MessagesProof = SubstrateMessagesProof; - type MessagesReceivingProof = SubstrateMessagesDeliveryProof; + type LaneId = P::LaneId; + + type MessagesProof = SubstrateMessagesProof; + type MessagesReceivingProof = SubstrateMessagesDeliveryProof; type SourceChainBalance = BalanceOf; type SourceHeaderNumber = BlockNumberOf; @@ -109,7 +121,7 @@ pub struct MessagesRelayParams pub target_to_source_headers_relay: Option>>, /// Identifier of lane that needs to be served. - pub lane_id: LaneId, + pub lane_id: P::LaneId, /// Messages relay limits. If not provided, the relay tries to determine it automatically, /// using `TransactionPayment` pallet runtime API. pub limits: Option, @@ -293,7 +305,7 @@ pub async fn relay_messages_range( source_transaction_params: TransactionParams>, target_transaction_params: TransactionParams>, at_source_block: HeaderIdOf, - lane_id: LaneId, + lane_id: P::LaneId, range: RangeInclusive, outbound_state_proof_required: bool, ) -> anyhow::Result<()> @@ -335,7 +347,7 @@ pub async fn relay_messages_delivery_confirmation( target_client: impl Client, source_transaction_params: TransactionParams>, at_target_block: HeaderIdOf, - lane_id: LaneId, + lane_id: P::LaneId, ) -> anyhow::Result<()> where AccountIdOf: From< as Pair>::Public>, @@ -372,7 +384,7 @@ pub trait ReceiveMessagesProofCallBuilder { /// messages module at the target chain. fn build_receive_messages_proof_call( relayer_id_at_source: AccountIdOf, - proof: SubstrateMessagesProof, + proof: SubstrateMessagesProof, messages_count: u32, dispatch_weight: Weight, trace_call: bool, @@ -388,7 +400,7 @@ pub struct DirectReceiveMessagesProofCallBuilder { impl ReceiveMessagesProofCallBuilder

for DirectReceiveMessagesProofCallBuilder where P: SubstrateMessageLane, - R: BridgeMessagesConfig, + R: BridgeMessagesConfig, I: 'static, R::BridgedChain: bp_runtime::Chain, Hash = HashOf>, @@ -396,7 +408,7 @@ where { fn build_receive_messages_proof_call( relayer_id_at_source: AccountIdOf, - proof: SubstrateMessagesProof, + proof: SubstrateMessagesProof, messages_count: u32, dispatch_weight: Weight, trace_call: bool, @@ -444,7 +456,8 @@ macro_rules! generate_receive_message_proof_call_builder { <$pipeline as $crate::messages::SubstrateMessageLane>::SourceChain >, proof: $crate::messages::source::SubstrateMessagesProof< - <$pipeline as $crate::messages::SubstrateMessageLane>::SourceChain + <$pipeline as $crate::messages::SubstrateMessageLane>::SourceChain, + <$pipeline as $crate::messages::SubstrateMessageLane>::LaneId >, messages_count: u32, dispatch_weight: bp_messages::Weight, @@ -470,7 +483,7 @@ pub trait ReceiveMessagesDeliveryProofCallBuilder { /// Given messages delivery proof, build call of `receive_messages_delivery_proof` function of /// bridge messages module at the source chain. fn build_receive_messages_delivery_proof_call( - proof: SubstrateMessagesDeliveryProof, + proof: SubstrateMessagesDeliveryProof, trace_call: bool, ) -> CallOf; } @@ -485,13 +498,13 @@ impl ReceiveMessagesDeliveryProofCallBuilder

for DirectReceiveMessagesDeliveryProofCallBuilder where P: SubstrateMessageLane, - R: BridgeMessagesConfig, + R: BridgeMessagesConfig, I: 'static, R::BridgedChain: bp_runtime::Chain>, CallOf: From> + GetDispatchInfo, { fn build_receive_messages_delivery_proof_call( - proof: SubstrateMessagesDeliveryProof, + proof: SubstrateMessagesDeliveryProof, trace_call: bool, ) -> CallOf { let call: CallOf = @@ -533,7 +546,8 @@ macro_rules! generate_receive_message_delivery_proof_call_builder { { fn build_receive_messages_delivery_proof_call( proof: $crate::messages::target::SubstrateMessagesDeliveryProof< - <$pipeline as $crate::messages::SubstrateMessageLane>::TargetChain + <$pipeline as $crate::messages::SubstrateMessageLane>::TargetChain, + <$pipeline as $crate::messages::SubstrateMessageLane>::LaneId >, _trace_call: bool, ) -> relay_substrate_client::CallOf< @@ -644,7 +658,7 @@ where FromBridgedChainMessagesProof { bridged_header_hash: Default::default(), storage_proof: Default::default(), - lane: Default::default(), + lane: P::LaneId::default(), nonces_start: 1, nonces_end: messages as u64, }, @@ -674,7 +688,7 @@ where mod tests { use super::*; use bp_messages::{ - source_chain::FromBridgedChainMessagesDeliveryProof, UnrewardedRelayersState, + source_chain::FromBridgedChainMessagesDeliveryProof, LaneIdType, UnrewardedRelayersState, }; use relay_substrate_client::calls::{UtilityCall as MockUtilityCall, UtilityCall}; @@ -687,8 +701,8 @@ mod tests { } pub type CodegenBridgeMessagesCall = bp_messages::BridgeMessagesCall< u64, - Box>, - FromBridgedChainMessagesDeliveryProof, + Box>, + FromBridgedChainMessagesDeliveryProof, >; impl From> for RuntimeCall { @@ -706,7 +720,7 @@ mod tests { let receive_messages_proof = FromBridgedChainMessagesProof { bridged_header_hash: Default::default(), storage_proof: Default::default(), - lane: LaneId([0, 0, 0, 0]), + lane: mock::TestLaneIdType::try_new(1, 2).unwrap(), nonces_start: 0, nonces_end: 0, }; @@ -761,7 +775,7 @@ mod tests { let receive_messages_delivery_proof = FromBridgedChainMessagesDeliveryProof { bridged_header_hash: Default::default(), storage_proof: Default::default(), - lane: LaneId([0, 0, 0, 0]), + lane: mock::TestLaneIdType::try_new(1, 2).unwrap(), }; let relayers_state = UnrewardedRelayersState { unrewarded_relayer_entries: 0, @@ -808,7 +822,7 @@ mod tests { // mock runtime with `pallet_bridge_messages` mod mock { use super::super::*; - use bp_messages::target_chain::ForbidInboundMessages; + use bp_messages::{target_chain::ForbidInboundMessages, HashedLaneId}; use bp_runtime::ChainId; use frame_support::derive_impl; use sp_core::H256; @@ -819,6 +833,9 @@ mod tests { type Block = frame_system::mocking::MockBlock; pub type SignedBlock = generic::SignedBlock; + /// Lane identifier type used for tests. + pub type TestLaneIdType = HashedLaneId; + frame_support::construct_runtime! { pub enum TestRuntime { @@ -838,13 +855,13 @@ mod tests { type ThisChain = ThisUnderlyingChain; type BridgedChain = BridgedUnderlyingChain; type BridgedHeaderChain = BridgedHeaderChain; - type ActiveOutboundLanes = (); type OutboundPayload = Vec; type InboundPayload = Vec; + type LaneId = TestLaneIdType; type DeliveryPayments = (); type DeliveryConfirmationPayments = (); type OnMessagesDelivered = (); - type MessageDispatch = ForbidInboundMessages>; + type MessageDispatch = ForbidInboundMessages, Self::LaneId>; } pub struct ThisUnderlyingChain; @@ -1006,6 +1023,7 @@ mod tests { impl SubstrateMessageLane for ThisChainToBridgedChainMessageLane { type SourceChain = ThisChain; type TargetChain = BridgedChain; + type LaneId = mock::TestLaneIdType; type ReceiveMessagesProofCallBuilder = ThisChainToBridgedChainMessageLaneReceiveMessagesProofCallBuilder; type ReceiveMessagesDeliveryProofCallBuilder = diff --git a/bridges/relays/lib-substrate-relay/src/messages/source.rs b/bridges/relays/lib-substrate-relay/src/messages/source.rs index b75fc86d5eee20d247de3cbc26f324ed46e9ad2d..3e60ed7abd097c602586842e82f56bc10d82dd6c 100644 --- a/bridges/relays/lib-substrate-relay/src/messages/source.rs +++ b/bridges/relays/lib-substrate-relay/src/messages/source.rs @@ -34,11 +34,11 @@ use async_trait::async_trait; use bp_messages::{ storage_keys::{operating_mode_key, outbound_lane_data_key}, target_chain::FromBridgedChainMessagesProof, - ChainWithMessages as _, InboundMessageDetails, LaneId, MessageNonce, MessagePayload, - MessagesOperatingMode, OutboundLaneData, OutboundMessageDetails, + ChainWithMessages as _, InboundMessageDetails, MessageNonce, MessagePayload, + MessagesOperatingMode, OutboundMessageDetails, }; use bp_runtime::{BasicOperatingMode, HeaderIdProvider, RangeInclusiveExt}; -use codec::Encode; +use codec::{Decode, Encode}; use frame_support::weights::Weight; use messages_relay::{ message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf}, @@ -60,14 +60,26 @@ use std::ops::RangeInclusive; /// Intermediate message proof returned by the source Substrate node. Includes everything /// required to submit to the target node: cumulative dispatch weight of bundled messages and /// the proof itself. -pub type SubstrateMessagesProof = (Weight, FromBridgedChainMessagesProof>); +pub type SubstrateMessagesProof = (Weight, FromBridgedChainMessagesProof, L>); type MessagesToRefine<'a> = Vec<(MessagePayload, &'a mut OutboundMessageDetails)>; +/// Outbound lane data - for backwards compatibility with `bp_messages::OutboundLaneData` which has +/// additional `lane_state` attribute. +/// +/// TODO: remove - https://github.com/paritytech/polkadot-sdk/issues/5923 +#[derive(Decode)] +struct LegacyOutboundLaneData { + #[allow(unused)] + oldest_unpruned_nonce: MessageNonce, + latest_received_nonce: MessageNonce, + latest_generated_nonce: MessageNonce, +} + /// Substrate client as Substrate messages source. pub struct SubstrateMessagesSource { source_client: SourceClnt, target_client: TargetClnt, - lane_id: LaneId, + lane_id: P::LaneId, transaction_params: TransactionParams>, target_to_source_headers_relay: Option>>, } @@ -79,7 +91,7 @@ impl, TargetClnt> pub fn new( source_client: SourceClnt, target_client: TargetClnt, - lane_id: LaneId, + lane_id: P::LaneId, transaction_params: TransactionParams>, target_to_source_headers_relay: Option< Arc>, @@ -98,7 +110,7 @@ impl, TargetClnt> async fn outbound_lane_data( &self, id: SourceHeaderIdOf>, - ) -> Result, SubstrateError> { + ) -> Result, SubstrateError> { self.source_client .storage_value( id.hash(), @@ -256,8 +268,11 @@ where } let best_target_header_hash = self.target_client.best_header_hash().await?; - for mut msgs_to_refine_batch in - split_msgs_to_refine::(self.lane_id, msgs_to_refine)? + for mut msgs_to_refine_batch in split_msgs_to_refine::< + P::SourceChain, + P::TargetChain, + P::LaneId, + >(self.lane_id, msgs_to_refine)? { let in_msgs_details = self .target_client @@ -542,7 +557,7 @@ fn validate_out_msgs_details( Ok(()) } -fn split_msgs_to_refine( +fn split_msgs_to_refine( lane_id: LaneId, msgs_to_refine: MessagesToRefine, ) -> Result, SubstrateError> { @@ -578,8 +593,12 @@ fn split_msgs_to_refine( #[cfg(test)] mod tests { use super::*; + use bp_messages::{HashedLaneId, LaneIdType}; use relay_substrate_client::test_chain::TestChain; + /// Lane identifier type used for tests. + type TestLaneIdType = HashedLaneId; + fn message_details_from_rpc( nonces: RangeInclusive, ) -> Vec { @@ -660,8 +679,10 @@ mod tests { msgs_to_refine.push((payload, out_msg_details)); } - let maybe_batches = - split_msgs_to_refine::(Default::default(), msgs_to_refine); + let maybe_batches = split_msgs_to_refine::( + TestLaneIdType::try_new(1, 2).unwrap(), + msgs_to_refine, + ); match expected_batches { Ok(expected_batches) => { let batches = maybe_batches.unwrap(); @@ -734,4 +755,38 @@ mod tests { Ok(vec![2, 4, 3]), ); } + + #[test] + fn outbound_lane_data_wrapper_is_compatible() { + let bytes_without_state = + vec![1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0]; + let bytes_with_state = { + // add state byte `bp_messages::LaneState::Opened` + let mut b = bytes_without_state.clone(); + b.push(0); + b + }; + + let full = bp_messages::OutboundLaneData { + oldest_unpruned_nonce: 1, + latest_received_nonce: 2, + latest_generated_nonce: 3, + state: bp_messages::LaneState::Opened, + }; + assert_eq!(full.encode(), bytes_with_state); + assert_ne!(full.encode(), bytes_without_state); + + // decode from `bytes_with_state` + let decoded: LegacyOutboundLaneData = Decode::decode(&mut &bytes_with_state[..]).unwrap(); + assert_eq!(full.oldest_unpruned_nonce, decoded.oldest_unpruned_nonce); + assert_eq!(full.latest_received_nonce, decoded.latest_received_nonce); + assert_eq!(full.latest_generated_nonce, decoded.latest_generated_nonce); + + // decode from `bytes_without_state` + let decoded: LegacyOutboundLaneData = + Decode::decode(&mut &bytes_without_state[..]).unwrap(); + assert_eq!(full.oldest_unpruned_nonce, decoded.oldest_unpruned_nonce); + assert_eq!(full.latest_received_nonce, decoded.latest_received_nonce); + assert_eq!(full.latest_generated_nonce, decoded.latest_generated_nonce); + } } diff --git a/bridges/relays/lib-substrate-relay/src/messages/target.rs b/bridges/relays/lib-substrate-relay/src/messages/target.rs index a6bf169cffb67ae149d9d4c8c5a2348dc18f5b39..214819a1c426aa87ccafdce6b39f905d0d6cfcd7 100644 --- a/bridges/relays/lib-substrate-relay/src/messages/target.rs +++ b/bridges/relays/lib-substrate-relay/src/messages/target.rs @@ -36,8 +36,9 @@ use async_std::sync::Arc; use async_trait::async_trait; use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, storage_keys::inbound_lane_data_key, - ChainWithMessages as _, InboundLaneData, LaneId, MessageNonce, UnrewardedRelayersState, + ChainWithMessages as _, LaneState, MessageNonce, UnrewardedRelayer, UnrewardedRelayersState, }; +use codec::Decode; use messages_relay::{ message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf}, message_lane_loop::{NoncesSubmitArtifacts, TargetClient, TargetClientState}, @@ -48,17 +49,57 @@ use relay_substrate_client::{ }; use relay_utils::relay_loop::Client as RelayClient; use sp_core::Pair; -use std::{convert::TryFrom, ops::RangeInclusive}; +use std::{collections::VecDeque, convert::TryFrom, ops::RangeInclusive}; /// Message receiving proof returned by the target Substrate node. -pub type SubstrateMessagesDeliveryProof = - (UnrewardedRelayersState, FromBridgedChainMessagesDeliveryProof>); +pub type SubstrateMessagesDeliveryProof = + (UnrewardedRelayersState, FromBridgedChainMessagesDeliveryProof, L>); + +/// Inbound lane data - for backwards compatibility with `bp_messages::InboundLaneData` which has +/// additional `lane_state` attribute. +/// +/// TODO: remove - https://github.com/paritytech/polkadot-sdk/issues/5923 +#[derive(Decode)] +struct LegacyInboundLaneData { + relayers: VecDeque>, + last_confirmed_nonce: MessageNonce, +} +impl Default for LegacyInboundLaneData { + fn default() -> Self { + let full = bp_messages::InboundLaneData::default(); + Self { relayers: full.relayers, last_confirmed_nonce: full.last_confirmed_nonce } + } +} + +impl LegacyInboundLaneData { + pub fn last_delivered_nonce(self) -> MessageNonce { + bp_messages::InboundLaneData { + relayers: self.relayers, + last_confirmed_nonce: self.last_confirmed_nonce, + // we don't care about the state here + state: LaneState::Opened, + } + .last_delivered_nonce() + } +} + +impl From> for UnrewardedRelayersState { + fn from(value: LegacyInboundLaneData) -> Self { + (&bp_messages::InboundLaneData { + relayers: value.relayers, + last_confirmed_nonce: value.last_confirmed_nonce, + // we don't care about the state here + state: LaneState::Opened, + }) + .into() + } +} /// Substrate client as Substrate messages target. pub struct SubstrateMessagesTarget { target_client: TargetClnt, source_client: SourceClnt, - lane_id: LaneId, + lane_id: P::LaneId, relayer_id_at_source: AccountIdOf, transaction_params: Option>>, source_to_target_headers_relay: Option>>, @@ -73,7 +114,7 @@ where pub fn new( target_client: TargetClnt, source_client: SourceClnt, - lane_id: LaneId, + lane_id: P::LaneId, relayer_id_at_source: AccountIdOf, transaction_params: Option>>, source_to_target_headers_relay: Option< @@ -94,7 +135,7 @@ where async fn inbound_lane_data( &self, id: TargetHeaderIdOf>, - ) -> Result>>, SubstrateError> { + ) -> Result>>, SubstrateError> { self.target_client .storage_value( id.hash(), @@ -217,8 +258,8 @@ where ) -> Result<(TargetHeaderIdOf>, UnrewardedRelayersState), SubstrateError> { let inbound_lane_data = - self.inbound_lane_data(id).await?.unwrap_or(InboundLaneData::default()); - Ok((id, (&inbound_lane_data).into())) + self.inbound_lane_data(id).await?.unwrap_or(LegacyInboundLaneData::default()); + Ok((id, inbound_lane_data.into())) } async fn prove_messages_receiving( @@ -308,7 +349,7 @@ where fn make_messages_delivery_call( relayer_id_at_source: AccountIdOf, nonces: RangeInclusive, - proof: SubstrateMessagesProof, + proof: SubstrateMessagesProof, trace_call: bool, ) -> CallOf { let messages_count = nonces.end() - nonces.start() + 1; @@ -321,3 +362,49 @@ fn make_messages_delivery_call( trace_call, ) } + +#[cfg(test)] +mod tests { + use super::*; + use bp_messages::{DeliveredMessages, UnrewardedRelayer}; + use codec::Encode; + + #[test] + fn inbound_lane_data_wrapper_is_compatible() { + let bytes_without_state = + vec![4, 0, 2, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0]; + let bytes_with_state = { + // add state byte `bp_messages::LaneState::Opened` + let mut b = bytes_without_state.clone(); + b.push(0); + b + }; + + let full = bp_messages::InboundLaneData:: { + relayers: vec![UnrewardedRelayer { + relayer: Default::default(), + messages: DeliveredMessages { begin: 2, end: 5 }, + }] + .into_iter() + .collect(), + last_confirmed_nonce: 6, + state: bp_messages::LaneState::Opened, + }; + assert_eq!(full.encode(), bytes_with_state); + assert_ne!(full.encode(), bytes_without_state); + + // decode from `bytes_with_state` + let decoded: LegacyInboundLaneData = + Decode::decode(&mut &bytes_with_state[..]).unwrap(); + assert_eq!(full.relayers, decoded.relayers); + assert_eq!(full.last_confirmed_nonce, decoded.last_confirmed_nonce); + assert_eq!(full.last_delivered_nonce(), decoded.last_delivered_nonce()); + + // decode from `bytes_without_state` + let decoded: LegacyInboundLaneData = + Decode::decode(&mut &bytes_without_state[..]).unwrap(); + assert_eq!(full.relayers, decoded.relayers); + assert_eq!(full.last_confirmed_nonce, decoded.last_confirmed_nonce); + assert_eq!(full.last_delivered_nonce(), decoded.last_delivered_nonce()); + } +} diff --git a/bridges/relays/lib-substrate-relay/src/on_demand/parachains.rs b/bridges/relays/lib-substrate-relay/src/on_demand/parachains.rs index 4579222a2c681c49e076f67d2eacaeb1dc8b9fca..96eba0af988c11c1dd48b708ae4dc1d43882c3f5 100644 --- a/bridges/relays/lib-substrate-relay/src/on_demand/parachains.rs +++ b/bridges/relays/lib-substrate-relay/src/on_demand/parachains.rs @@ -31,11 +31,11 @@ use async_std::{ sync::{Arc, Mutex}, }; use async_trait::async_trait; +use bp_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; use bp_polkadot_core::parachains::{ParaHash, ParaId}; use bp_runtime::HeaderIdProvider; use futures::{select, FutureExt}; use num_traits::Zero; -use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; use parachains_relay::parachains_loop::{AvailableHeader, SourceClient, TargetClient}; use relay_substrate_client::{ is_ancient_block, AccountIdOf, AccountKeyPairOf, BlockNumberOf, CallOf, Chain, Client, @@ -664,7 +664,8 @@ impl<'a, P: SubstrateParachainsPipeline, SourceRelayClnt, TargetClnt> for ( &'a OnDemandParachainsRelay, &'a ParachainsSource, - ) where + ) +where SourceRelayClnt: Client, TargetClnt: Client, { diff --git a/bridges/relays/lib-substrate-relay/src/parachains/mod.rs b/bridges/relays/lib-substrate-relay/src/parachains/mod.rs index 8b128bb770dd7a05d28ad46d4561f4d859b1deb6..08d8e5e2a4f55e7463a21c6bad289a000be50f08 100644 --- a/bridges/relays/lib-substrate-relay/src/parachains/mod.rs +++ b/bridges/relays/lib-substrate-relay/src/parachains/mod.rs @@ -18,11 +18,9 @@ //! parachain finality proofs synchronization pipelines. use async_trait::async_trait; +use bp_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; -use pallet_bridge_parachains::{ - Call as BridgeParachainsCall, Config as BridgeParachainsConfig, RelayBlockHash, - RelayBlockHasher, RelayBlockNumber, -}; +use pallet_bridge_parachains::{Call as BridgeParachainsCall, Config as BridgeParachainsConfig}; use parachains_relay::ParachainsPipeline; use relay_substrate_client::{ CallOf, Chain, ChainWithTransactions, HeaderIdOf, Parachain, RelayChain, diff --git a/bridges/relays/messages/Cargo.toml b/bridges/relays/messages/Cargo.toml index c7a132bb3bae7ebc34728de8c94c41fb39c89751..f9df73507c753564fb85a88ba7382b00b4a98e57 100644 --- a/bridges/relays/messages/Cargo.toml +++ b/bridges/relays/messages/Cargo.toml @@ -26,3 +26,6 @@ finality-relay = { workspace = true } relay-utils = { workspace = true } sp-arithmetic = { workspace = true, default-features = true } + +[dev-dependencies] +sp-core = { workspace = true } diff --git a/bridges/relays/messages/src/lib.rs b/bridges/relays/messages/src/lib.rs index 78a3237ba4fe03851412b86e08867fcba07e8451..f5e09f4d4684fbea2aea838abb0dbb1c57d4327e 100644 --- a/bridges/relays/messages/src/lib.rs +++ b/bridges/relays/messages/src/lib.rs @@ -38,3 +38,4 @@ mod message_race_strategy; pub use message_race_delivery::relay_messages_range; pub use message_race_receiving::relay_messages_delivery_confirmation; +pub use metrics::Labeled; diff --git a/bridges/relays/messages/src/message_lane.rs b/bridges/relays/messages/src/message_lane.rs index 5c9728ad93abd5aa1ea9b2fc77b2a6f9968539f6..84c1e57ba4eb8ef5ec5bff12a9a338c7ab4f2cbe 100644 --- a/bridges/relays/messages/src/message_lane.rs +++ b/bridges/relays/messages/src/message_lane.rs @@ -19,6 +19,7 @@ //! 1) relay new messages from source to target node; //! 2) relay proof-of-delivery from target to source node. +use crate::metrics::Labeled; use num_traits::{SaturatingAdd, Zero}; use relay_utils::{BlockNumberBase, HeaderId}; use sp_arithmetic::traits::AtLeast32BitUnsigned; @@ -31,6 +32,9 @@ pub trait MessageLane: 'static + Clone + Send + Sync { /// Name of the messages target. const TARGET_NAME: &'static str; + /// Lane identifier type. + type LaneId: Clone + Send + Sync + Labeled; + /// Messages proof. type MessagesProof: Clone + Debug + Send + Sync; /// Messages receiving proof. diff --git a/bridges/relays/messages/src/message_lane_loop.rs b/bridges/relays/messages/src/message_lane_loop.rs index b681d86d2ae8fadb6b0d5b5277de5a1285533d36..36de637f04c437450e9054dd5088c3da5eb329cd 100644 --- a/bridges/relays/messages/src/message_lane_loop.rs +++ b/bridges/relays/messages/src/message_lane_loop.rs @@ -29,7 +29,7 @@ use std::{collections::BTreeMap, fmt::Debug, future::Future, ops::RangeInclusive use async_trait::async_trait; use futures::{channel::mpsc::unbounded, future::FutureExt, stream::StreamExt}; -use bp_messages::{LaneId, MessageNonce, UnrewardedRelayersState, Weight}; +use bp_messages::{MessageNonce, UnrewardedRelayersState, Weight}; use relay_utils::{ interval, metrics::MetricsParams, process_future_result, relay_loop::Client as RelayClient, retry_backoff, FailedClient, TransactionTracker, @@ -39,12 +39,12 @@ use crate::{ message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf}, message_race_delivery::run as run_message_delivery_race, message_race_receiving::run as run_message_receiving_race, - metrics::MessageLaneLoopMetrics, + metrics::{Labeled, MessageLaneLoopMetrics}, }; /// Message lane loop configuration params. #[derive(Debug, Clone)] -pub struct Params { +pub struct Params { /// Id of lane this loop is servicing. pub lane: LaneId, /// Interval at which we ask target node about its updates. @@ -275,13 +275,13 @@ pub struct ClientsState { /// Return prefix that will be used by default to expose Prometheus metrics of the finality proofs /// sync loop. -pub fn metrics_prefix(lane: &LaneId) -> String { - format!("{}_to_{}_MessageLane_{}", P::SOURCE_NAME, P::TARGET_NAME, hex::encode(lane)) +pub fn metrics_prefix(lane: &P::LaneId) -> String { + format!("{}_to_{}_MessageLane_{}", P::SOURCE_NAME, P::TARGET_NAME, lane.label()) } /// Run message lane service loop. pub async fn run( - params: Params, + params: Params, source_client: impl SourceClient

, target_client: impl TargetClient

, metrics_params: MetricsParams, @@ -309,7 +309,7 @@ pub async fn run( /// Run one-way message delivery loop until connection with target or source node is lost, or exit /// signal is received. async fn run_until_connection_lost, TC: TargetClient

>( - params: Params, + params: Params, source_client: SC, target_client: TC, metrics_msg: Option, @@ -471,9 +471,9 @@ async fn run_until_connection_lost, TC: Targ pub(crate) mod tests { use std::sync::Arc; + use bp_messages::{HashedLaneId, LaneIdType, LegacyLaneId}; use futures::stream::StreamExt; use parking_lot::Mutex; - use relay_utils::{HeaderId, MaybeConnectionError, TrackedTransactionStatus}; use super::*; @@ -504,6 +504,9 @@ pub(crate) mod tests { } } + /// Lane identifier type used for tests. + pub type TestLaneIdType = HashedLaneId; + #[derive(Clone)] pub struct TestMessageLane; @@ -520,6 +523,8 @@ pub(crate) mod tests { type TargetHeaderNumber = TestTargetHeaderNumber; type TargetHeaderHash = TestTargetHeaderHash; + + type LaneId = TestLaneIdType; } #[derive(Clone, Debug)] @@ -957,7 +962,7 @@ pub(crate) mod tests { }; let _ = run( Params { - lane: LaneId([0, 0, 0, 0]), + lane: TestLaneIdType::try_new(1, 2).unwrap(), source_tick: Duration::from_millis(100), target_tick: Duration::from_millis(100), reconnect_delay: Duration::from_millis(0), @@ -1274,4 +1279,36 @@ pub(crate) mod tests { assert!(!result.target_to_source_header_requirements.is_empty()); assert!(!result.source_to_target_header_requirements.is_empty()); } + + #[test] + fn metrics_prefix_is_valid() { + assert!(MessageLaneLoopMetrics::new(Some(&metrics_prefix::( + &HashedLaneId::try_new(1, 2).unwrap() + ))) + .is_ok()); + + // with LegacyLaneId + #[derive(Clone)] + pub struct LegacyTestMessageLane; + impl MessageLane for LegacyTestMessageLane { + const SOURCE_NAME: &'static str = "LegacyTestSource"; + const TARGET_NAME: &'static str = "LegacyTestTarget"; + + type MessagesProof = TestMessagesProof; + type MessagesReceivingProof = TestMessagesReceivingProof; + + type SourceChainBalance = TestSourceChainBalance; + type SourceHeaderNumber = TestSourceHeaderNumber; + type SourceHeaderHash = TestSourceHeaderHash; + + type TargetHeaderNumber = TestTargetHeaderNumber; + type TargetHeaderHash = TestTargetHeaderHash; + + type LaneId = LegacyLaneId; + } + assert!(MessageLaneLoopMetrics::new(Some(&metrics_prefix::( + &LegacyLaneId([0, 0, 0, 1]) + ))) + .is_ok()); + } } diff --git a/bridges/relays/messages/src/message_race_delivery.rs b/bridges/relays/messages/src/message_race_delivery.rs index cbb89baabcc5ab38f164189066e225ad1e8f9240..b09533a4ddc1ddd0a06d6572124843a49f476164 100644 --- a/bridges/relays/messages/src/message_race_delivery.rs +++ b/bridges/relays/messages/src/message_race_delivery.rs @@ -59,9 +59,7 @@ pub async fn run( _phantom: Default::default(), }, target_state_updates, - MessageDeliveryStrategy:: { - lane_source_client: source_client, - lane_target_client: target_client, + MessageDeliveryStrategy::

{ max_unrewarded_relayer_entries_at_target: params .max_unrewarded_relayer_entries_at_target, max_unconfirmed_nonces_at_target: params.max_unconfirmed_nonces_at_target, @@ -71,7 +69,6 @@ pub async fn run( latest_confirmed_nonces_at_source: VecDeque::new(), target_nonces: None, strategy: BasicStrategy::new(), - metrics_msg, }, ) .await @@ -300,11 +297,7 @@ struct DeliveryRaceTargetNoncesData { } /// Messages delivery strategy. -struct MessageDeliveryStrategy { - /// The client that is connected to the message lane source node. - lane_source_client: SC, - /// The client that is connected to the message lane target node. - lane_target_client: TC, +struct MessageDeliveryStrategy { /// Maximal unrewarded relayer entries at target client. max_unrewarded_relayer_entries_at_target: MessageNonce, /// Maximal unconfirmed nonces at target client. @@ -322,8 +315,6 @@ struct MessageDeliveryStrategy { target_nonces: Option>, /// Basic delivery strategy. strategy: MessageDeliveryStrategyBase

, - /// Message lane metrics. - metrics_msg: Option, } type MessageDeliveryStrategyBase

= BasicStrategy< @@ -335,7 +326,7 @@ type MessageDeliveryStrategyBase

= BasicStrategy<

::MessagesProof, >; -impl std::fmt::Debug for MessageDeliveryStrategy { +impl std::fmt::Debug for MessageDeliveryStrategy

{ fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { fmt.debug_struct("MessageDeliveryStrategy") .field( @@ -353,11 +344,9 @@ impl std::fmt::Debug for MessageDeliveryStrategy MessageDeliveryStrategy +impl MessageDeliveryStrategy

where P: MessageLane, - SC: MessageLaneSourceClient

, - TC: MessageLaneTargetClient

, { /// Returns true if some race action can be selected (with `select_race_action`) at given /// `best_finalized_source_header_id_at_best_target` source header at target. @@ -465,23 +454,18 @@ where let max_nonces = std::cmp::min(max_nonces, self.max_messages_in_single_batch); let max_messages_weight_in_single_batch = self.max_messages_weight_in_single_batch; let max_messages_size_in_single_batch = self.max_messages_size_in_single_batch; - let lane_source_client = self.lane_source_client.clone(); - let lane_target_client = self.lane_target_client.clone(); // select nonces from nonces, available for delivery let selected_nonces = match self.strategy.available_source_queue_indices(race_state) { Some(available_source_queue_indices) => { let source_queue = self.strategy.source_queue(); - let reference = RelayMessagesBatchReference { + let reference = RelayMessagesBatchReference::

{ max_messages_in_this_batch: max_nonces, max_messages_weight_in_single_batch, max_messages_size_in_single_batch, - lane_source_client: lane_source_client.clone(), - lane_target_client: lane_target_client.clone(), best_target_nonce, nonces_queue: source_queue.clone(), nonces_queue_range: available_source_queue_indices, - metrics: self.metrics_msg.clone(), }; MessageRaceLimits::decide(reference).await @@ -534,12 +518,10 @@ where } #[async_trait] -impl RaceStrategy, TargetHeaderIdOf

, P::MessagesProof> - for MessageDeliveryStrategy +impl

RaceStrategy, TargetHeaderIdOf

, P::MessagesProof> + for MessageDeliveryStrategy

where P: MessageLane, - SC: MessageLaneSourceClient

, - TC: MessageLaneTargetClient

, { type SourceNoncesRange = MessageDetailsMap; type ProofParameters = MessageProofParameters; @@ -707,8 +689,7 @@ mod tests { message_lane_loop::{ tests::{ header_id, TestMessageLane, TestMessagesBatchTransaction, TestMessagesProof, - TestSourceChainBalance, TestSourceClient, TestSourceHeaderId, TestTargetClient, - TestTargetHeaderId, + TestSourceChainBalance, TestSourceHeaderId, TestTargetHeaderId, }, MessageDetails, }, @@ -726,8 +707,7 @@ mod tests { TestMessagesProof, TestMessagesBatchTransaction, >; - type TestStrategy = - MessageDeliveryStrategy; + type TestStrategy = MessageDeliveryStrategy; fn source_nonces( new_nonces: RangeInclusive, @@ -770,9 +750,6 @@ mod tests { max_messages_weight_in_single_batch: Weight::from_parts(4, 0), max_messages_size_in_single_batch: 4, latest_confirmed_nonces_at_source: vec![(header_id(1), 19)].into_iter().collect(), - lane_source_client: TestSourceClient::default(), - lane_target_client: TestTargetClient::default(), - metrics_msg: None, target_nonces: Some(TargetClientNonces { latest_nonce: 19, nonces_data: DeliveryRaceTargetNoncesData { @@ -1167,9 +1144,6 @@ mod tests { max_messages_weight_in_single_batch: Weight::from_parts(4, 0), max_messages_size_in_single_batch: 4, latest_confirmed_nonces_at_source: VecDeque::new(), - lane_source_client: TestSourceClient::default(), - lane_target_client: TestTargetClient::default(), - metrics_msg: None, target_nonces: None, strategy: BasicStrategy::new(), }; diff --git a/bridges/relays/messages/src/message_race_limits.rs b/bridges/relays/messages/src/message_race_limits.rs index 873bb6aad04253b833c51cea89f2124167e077d1..8fcd1f911f68267545a8173fd41e76c4876f770b 100644 --- a/bridges/relays/messages/src/message_race_limits.rs +++ b/bridges/relays/messages/src/message_race_limits.rs @@ -23,33 +23,16 @@ use bp_messages::{MessageNonce, Weight}; use crate::{ message_lane::MessageLane, - message_lane_loop::{ - MessageDetails, MessageDetailsMap, SourceClient as MessageLaneSourceClient, - TargetClient as MessageLaneTargetClient, - }, + message_lane_loop::{MessageDetails, MessageDetailsMap}, message_race_loop::NoncesRange, message_race_strategy::SourceRangesQueue, - metrics::MessageLaneLoopMetrics, }; /// Reference data for participating in relay -pub struct RelayReference< - P: MessageLane, - SourceClient: MessageLaneSourceClient

, - TargetClient: MessageLaneTargetClient

, -> { - /// The client that is connected to the message lane source node. - pub lane_source_client: SourceClient, - /// The client that is connected to the message lane target node. - pub lane_target_client: TargetClient, - /// Metrics reference. - pub metrics: Option, +pub struct RelayReference { /// Messages size summary pub selected_size: u32, - /// Hard check begin nonce - pub hard_selected_begin_nonce: MessageNonce, - /// Index by all ready nonces pub index: usize, /// Current nonce @@ -59,23 +42,13 @@ pub struct RelayReference< } /// Relay reference data -pub struct RelayMessagesBatchReference< - P: MessageLane, - SourceClient: MessageLaneSourceClient

, - TargetClient: MessageLaneTargetClient

, -> { +pub struct RelayMessagesBatchReference { /// Maximal number of relayed messages in single delivery transaction. pub max_messages_in_this_batch: MessageNonce, /// Maximal cumulative dispatch weight of relayed messages in single delivery transaction. pub max_messages_weight_in_single_batch: Weight, /// Maximal cumulative size of relayed messages in single delivery transaction. pub max_messages_size_in_single_batch: u32, - /// The client that is connected to the message lane source node. - pub lane_source_client: SourceClient, - /// The client that is connected to the message lane target node. - pub lane_target_client: TargetClient, - /// Metrics reference. - pub metrics: Option, /// Best available nonce at the **best** target block. We do not want to deliver nonces /// less than this nonce, even though the block may be retracted. pub best_target_nonce: MessageNonce, @@ -94,12 +67,8 @@ pub struct RelayMessagesBatchReference< pub struct MessageRaceLimits; impl MessageRaceLimits { - pub async fn decide< - P: MessageLane, - SourceClient: MessageLaneSourceClient

, - TargetClient: MessageLaneTargetClient

, - >( - reference: RelayMessagesBatchReference, + pub async fn decide( + reference: RelayMessagesBatchReference

, ) -> Option> { let mut hard_selected_count = 0; @@ -112,15 +81,9 @@ impl MessageRaceLimits { ); // relay reference - let mut relay_reference = RelayReference { - lane_source_client: reference.lane_source_client.clone(), - lane_target_client: reference.lane_target_client.clone(), - metrics: reference.metrics.clone(), - + let mut relay_reference = RelayReference::

{ selected_size: 0, - hard_selected_begin_nonce, - index: 0, nonce: 0, details: MessageDetails { diff --git a/bridges/relays/messages/src/message_race_loop.rs b/bridges/relays/messages/src/message_race_loop.rs index 31341a9a0c0cd9fce4ba67e14bc43838f4770f3f..ea6a2371dc9037624465f4abfa2d45d1c6b7316a 100644 --- a/bridges/relays/messages/src/message_race_loop.rs +++ b/bridges/relays/messages/src/message_race_loop.rs @@ -225,15 +225,9 @@ pub trait RaceState: Clone + Send + Sync { /// client (at the `best_finalized_source_header_id_at_best_target`). fn set_best_finalized_source_header_id_at_best_target(&mut self, id: SourceHeaderId); - /// Best finalized source header id at the source client. - fn best_finalized_source_header_id_at_source(&self) -> Option; /// Best finalized source header id at the best block on the target /// client (at the `best_finalized_source_header_id_at_best_target`). fn best_finalized_source_header_id_at_best_target(&self) -> Option; - /// The best header id at the target client. - fn best_target_header_id(&self) -> Option; - /// Best finalized header id at the target client. - fn best_finalized_target_header_id(&self) -> Option; /// Returns `true` if we have selected nonces to submit to the target node. fn nonces_to_submit(&self) -> Option>; @@ -296,22 +290,10 @@ where self.best_finalized_source_header_id_at_best_target = Some(id); } - fn best_finalized_source_header_id_at_source(&self) -> Option { - self.best_finalized_source_header_id_at_source.clone() - } - fn best_finalized_source_header_id_at_best_target(&self) -> Option { self.best_finalized_source_header_id_at_best_target.clone() } - fn best_target_header_id(&self) -> Option { - self.best_target_header_id.clone() - } - - fn best_finalized_target_header_id(&self) -> Option { - self.best_finalized_target_header_id.clone() - } - fn nonces_to_submit(&self) -> Option> { self.nonces_to_submit.clone().map(|(_, nonces, _)| nonces) } diff --git a/bridges/relays/messages/src/message_race_strategy.rs b/bridges/relays/messages/src/message_race_strategy.rs index 3a532331d79dc83f680ca7b5e21e471e60335b84..1303fcfedebd7f7ec1b1acc893854ee61b32b9ec 100644 --- a/bridges/relays/messages/src/message_race_strategy.rs +++ b/bridges/relays/messages/src/message_race_strategy.rs @@ -67,7 +67,8 @@ impl< TargetHeaderHash, SourceNoncesRange, Proof, - > where + > +where SourceHeaderHash: Clone, SourceHeaderNumber: Clone + Ord, SourceNoncesRange: NoncesRange, @@ -189,7 +190,8 @@ impl< TargetHeaderHash, SourceNoncesRange, Proof, - > where + > +where SourceHeaderHash: Clone + Debug + Send + Sync, SourceHeaderNumber: Clone + Ord + Debug + Send + Sync, SourceNoncesRange: NoncesRange + Debug + Send + Sync, diff --git a/bridges/relays/messages/src/metrics.rs b/bridges/relays/messages/src/metrics.rs index 69d80d178de809211b1874965adcf02fd76a66b8..2ca10e56d74aa603794f471308ba4dfb489309df 100644 --- a/bridges/relays/messages/src/metrics.rs +++ b/bridges/relays/messages/src/metrics.rs @@ -21,7 +21,7 @@ use crate::{ message_lane_loop::{SourceClientState, TargetClientState}, }; -use bp_messages::MessageNonce; +use bp_messages::{HashedLaneId, LegacyLaneId, MessageNonce}; use finality_relay::SyncLoopMetrics; use relay_utils::metrics::{ metric_name, register, GaugeVec, Metric, Opts, PrometheusError, Registry, U64, @@ -146,3 +146,32 @@ impl Metric for MessageLaneLoopMetrics { Ok(()) } } + +/// Provides a label for metrics. +pub trait Labeled { + /// Returns a label. + fn label(&self) -> String; +} + +/// `Labeled` implementation for `LegacyLaneId`. +impl Labeled for LegacyLaneId { + fn label(&self) -> String { + hex::encode(self.0) + } +} + +/// `Labeled` implementation for `HashedLaneId`. +impl Labeled for HashedLaneId { + fn label(&self) -> String { + format!("{:?}", self.inner()) + } +} + +#[test] +fn lane_to_label_works() { + assert_eq!( + "0x0101010101010101010101010101010101010101010101010101010101010101", + HashedLaneId::from_inner(sp_core::H256::from([1u8; 32])).label(), + ); + assert_eq!("00000001", LegacyLaneId([0, 0, 0, 1]).label()); +} diff --git a/bridges/relays/parachains/src/parachains_loop.rs b/bridges/relays/parachains/src/parachains_loop.rs index 0fd1d72c7075bc29632280615f203e0c0028359b..dfe6b230ceda1689434fce3a3fde3b01ff0e24a5 100644 --- a/bridges/relays/parachains/src/parachains_loop.rs +++ b/bridges/relays/parachains/src/parachains_loop.rs @@ -177,6 +177,14 @@ pub async fn run( where P::SourceRelayChain: Chain, { + log::info!( + target: "bridge", + "Starting {} -> {} finality proof relay: relaying (only_free_headers: {:?}) headers", + P::SourceParachain::NAME, + P::TargetChain::NAME, + only_free_headers, + ); + let exit_signal = exit_signal.shared(); relay_utils::relay_loop(source_client, target_client) .with_metrics(metrics_params) @@ -496,7 +504,7 @@ where }, (AvailableHeader::Missing, Some(_)) => { // parachain/parathread has been offboarded removed from the system. It needs to - // be propageted to the target client + // be propagated to the target client true }, (AvailableHeader::Missing, None) => { diff --git a/bridges/snowbridge/docs/v2.md b/bridges/snowbridge/docs/v2.md new file mode 100644 index 0000000000000000000000000000000000000000..8ec440c47cecb61bb8a6cc61a52f539f04302f71 --- /dev/null +++ b/bridges/snowbridge/docs/v2.md @@ -0,0 +1,356 @@ +# Snowbridge V2 + +This design lowers fees, improves UX, improves relayer decentralization and allows "transacting" over the bridge, making +it a general-purpose bridge rather than just a token bridge. + +We're grateful to Adrian Catangiu, Francisco Aguirre, and others from the Parity XCM/Bridges team for their help and +collaboration on this design. + +## Summary + +- Unordered messaging +- All messages routed through AH +- Off-chain fee estimation +- Pโ†’E Fee Asset: WETH +- Eโ†’P Fee Asset: ETH +- Relayer rewards for both directions paid out on AH in WETH + +## Polkadotโ†’Ethereum + +Given source parachain $S$, with native token $S^{'}$ and the initial xcm $x_0$ to be executed on $S$. + +### Step 1: User agent constructs initial XCM + +The user agent constructs an initial XCM message $x_0$ that will be executed on S. + +The fee amounts in this message should be high enough to enable dry-running, after which they will be lowered. + +### Step 2: User agent estimates fees + +- Given source parachain $S$, with native token $S^{'}$ and the initial xcm $x_0$ to be executed on $S$. +- The native currency $P^{'}$ (DOT) of the Polkadot relay chain, and $E^{'}$ (ETH) of Ethereum. +- Suppose that the user agent chooses relayer reward $r$ in $E^{'}$. +- Suppose that the exchange rates are $K_{P^{'}/S^{'}}$ and $K_{E^{'}/S^{'}}$. The user agent chooses a multiplier to + $\beta$ to cover volatility in these rates. + +Apply the following sequence operations: + +1. Dry-run $x_0$ on $S$ to receive xcm $x_1$ and cost $a$ in $S^{'}$ +2. Dry-run $x_1$ on AH to receive xcm $x_2$ and cost $b$ in $P^{'}$ (DOT) +3. Dry-run $x_2$ on BH to receive command $m$ and cost $c$ in $P^{'}$ (DOT) +4. Dry-run $m$ on Ethereum to receive cost $d$ in $E^{'}$ (ETH) + +The final cost to the user in $S^{'}$ is given by + +$$ +\beta \left(a + \frac{b + c}{K_{P^{'}/S^{'}}} + \frac{d + r}{K_{E^{'}/S^{'}}}\right) +$$ + +The user agent should perform a final update to xcm $x_0$, substituting the calculated fee amounts. + +### Step 3: User agent initiates bridging operation + +The user agent calls `pallet_xcm::execute` with the initial xcm $x_0$ + +```text +WithdrawAsset (KLT, 100) +PayFees (KLT, 20) +InitiateAssetsTransfer asset=(KLT, 60) remoteFee=(KLT, 20) dest=AH + ExchangeAsset give=(KLT, 20) want=(WETH, 1) + InitiateAssetsTransfer asset=(KLT, 40) remoteFee=(WETH, 1) dest=Ethereum + DepositAsset (KLT, 40) beneficiary=Bob +``` + +### Step 4: AH executes message x1 + +The message $x_1$ is application-specific: + +```text +ReserveAssetDeposited (KLT, 80) +PayFees (KLT, 20) +SetAssetClaimer Kilt/Alice +AliasOrigin Kilt/Alice +ExchangeAsset give=(KLT, 20) want=(WETH, 1) +InitiateAssetsTransfer asset=(KLT, 60) remoteFee=(WETH, 1) dest=Ethereum + DepositAsset (KLT, 60) beneficiary=Bob +``` + +or + +```text +*ReserveAssetDeposited (KLT, 80) +*PayFees (KLT, 20) +*SetAssetClaimer Kilt/Alice +*AliasOrigin Kilt/Alice +ExchangeAsset give=(KLT, 20) want=(WETH, 1) +InitiateAssetsTransfer asset=(KLT, 60) remoteFee=(WETH, 1) dest=Ethereum + DepositAsset (KLT, 60) beneficiary=Bob + Transact Bob.hello() +``` + +Note that the `SetAssetClaimer` instruction is placed before `AliasOrigin` in case AH fails to interpret the latter +instruction. + +In all cases, $x_1$ should contain the necessary instructions to: + +1. Pay fees for local execution using `PaysFees` +2. Obtain WETH for remote delivery fees. + +The XCM bridge-router on AH will charge a small fee to prevent spamming BH with bridge messages. This is necessary since +the `ExportMessage` instruction in message $x_2$ will have no execution fee on BH. For a similar reason, we should also +impose a minimum relayer reward of at least the existential deposit 0.1 DOT, which acts as a deposit to stop spamming +messages with 0 rewards. + +### Step 5: BH executes message x2 + +Message $x_2$ is parsed by the `SnowbridgeMessageExporter` in block $n$ with the following effects: + +- A bridge command $m$ is committed to binary merkle tree $M_n$. + - The transferred asset is parsed from `ReserveAssetDeposited` , `WithdrawAsset` or `TeleportedAssetReceived` + instructions for the local, destination and teleport asset transfer types respectively. + - The original origin is preserved through the `AliasOrigin` instruction. This will allow us to resolve agents for the + case of `Transact`. + - The message exporter must be able to support multiple assets and reserve types in the same message and potentially + multiple `Transacts`. + - The Message Exporter must be able to support multiple Deposited Assets. + - The Message Exporter must be able to parse `SetAssetClaimer` and allow the provided location to claim the assets on + BH in case of errors. +- Given relayer reward $r$ in WETH, set storage $P(\mathrm{hash}(m)) = r$. This is parsed from the `WithdrawAsset` and + `PayFees` instruction within `ExportMessage`. + +Note that WETH on AH & BH is a wrapped derivative of the +[WETH](https://etherscan.io/token/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2) ERC20 contract on Ethereum, which is +itself a wrapper over ETH, the native currency of Ethereum. For the purposes of this document you can consider them all +to be of equivalent value. + +```text +!WithdrawAsset(DOT, 10) +!PayFees (DOT, 10) +!ExportMessage dest=Ethereum + *ReserveAssetDeposited (KLT, 60) + *WithdrawAsset (WETH, 1) + *PayFees (WETH, 1) + *SetAssetClaimer Kilt/Alice + *AliasOrigin Kilt/Alice + DepositAsset (KLT, 60) beneficiary=Bob +``` + +or + +```text +!WithdrawAsset(DOT, 10) +!PayFees (DOT, 10) +!ExportMessage dest=Ethereum + *ReserveAssetDeposited (KLT, 80) + *PayFees (KLT, 20) + *SetAssetClaimer Kilt/Alice + *AliasOrigin Kilt/Alice + DepositAsset (KLT, 60) beneficiary=Bob + Transact Bob.hello() +``` + +### Step 6: Relayer relays message to Gateway + +1. A relayer _Charlie_ inspects storage $P$ to look for new messages to relay. Suppose it finds $\mathrm{hash}(m)$ + giving reward $r$. +2. The relayer queries $m$ from $M$ and constructs the necessary proofs. +3. The relayer dry-runs m on Ethereum to decide whether the message is profitable to deliver. +4. The relayer finally delivers the message together with a relayer-controlled address $u$ on AH where the relayer can + claim their reward after proof of delivery. + +### Step 7: Relayer delivers proof of delivery to BH + +The proof of delivery is essentially a merkle proof for the `InboundMessageAccepted` event log. + +When BH processes the proof of delivery: + +1. The command $m$ is removed from storage items $M$ and $P$. +2. The relayer reward is tracked in storage $R$, where $R(u)$ is the accumulated rewards that can be claimed by account + $u$. + +## Ethereumโ†’Polkadot + +### Step 1: Submit send on Gateway + +The interface that the Gateway will use to initiate transfers will be similar to the interface from +`transfer_assets_using_type_and_then` extrinsic that we currently use to initiate transfers from the Polkadot to +Ethereum direction. + +1. It must allow multiple assets to be transferred and specify the transfer type: Local, Destination or Teleport asset + transfer types. It is the job of the User Agent/UX layer to fill in this information correctly. +2. It must allow specifying a destination which is `Address32`, `Address20` or a custom scale-encoded XCM payload that + is executed on the destination. This is how we will support `Transact` , the User Agent/UX layer can build a + scale-encoded payload with an encoded transact call. +3. The same interface is used for both PNA (Polkadot Assets) and ERC20 tokens. Internally we will still look up whether + the token is registered as a PNA or ERC20 for the purpose of minting/locking burning/unlocking logic. The asset + transfer type chosen by the UX layer will inform the XCM that is built from the message on BH. + +```solidity +enum Kind { + Index, + Address32, + Address20, + XCMPayload, +} + +struct Beneficiary { + Kind kind; + bytes data; +} + +enum AssetTransferType { + ReserveDeposit, ReserveWithdraw, Teleport +} + +struct Token { + AssetTransferType type; + address token; + uint128 amount; +} + +function send( + ParaID destinationChain, + Beneficiary calldata beneficiary, + Token[] tokens, + uint128 reward +) external payable; +``` + +Message enqueued $m_0$: + +```solidity +send( + 3022, // KILT Para Id + Address32(0x0000....), + [(ReserveWithdraw, KLT, 100)], + 10, // WETH +) +``` + +```solidity +send { value: 3 }( // Send 3 Eth for fees and reward + 3022, // KILT Para Id + XCMPayload( + DepositAsset (KLT, 100) dest=Bob + Transact Bob.hello() + ), + [(ReserveWithdraw, KLT, 100)], + 1, // 1 ETH of 3 needs to be for the reward, the rest is for fees +) +``` + +The User Agent/UX layer will need to estimate the fee required to be passed into the `send` method. This may be an issue +as we cannot Dry-Run something on Polkadot that has not even been submitted on Ethereum yet. We may need to make RPC API +to DryRun and get back the xcm that would be submitted to asset hub. + +### Step 2: Relayer relays message to Bridge Hub + +On-chain exchange rate is eliminated. Users pay remote delivery costs in ETH, and this amount is sent with the message +as WETH. The delivery fee can be claimed by the relayer on BH. + +The user agent applies a similar dry-running process as with +[Step 2: User agent estimates fees](https://www.notion.so/Step-2-User-agent-estimates-fees-113296aaabef8159bcd0e6dd2e64c3d0?pvs=21). + +The message is converted from $m_0$ to $x_0$ during message submission on BH. Dry-running submission will return $x_0$ +to the relayer so that it can verify it is profitable. + +### Step 3: AH receives $x_0$ from BH + +Submitting the message $m_0$ will cause the following XCM, $x_0$, to be built on BH and dispatched to AH. + +```text +WithdrawAsset (KLT, 100) +ReserveAssetDeposited(WETH, 2) +PayFees (WETH, 1) +SetAssetClaimer Kilt/Bob // derived from beneficiary on final destination +AliasOrigin Ethereum/Alice // derived from msg.sender +InitiateAssetsTransfer asset=(KLT, 100) remoteFee=(WETH, 1) dest=KLT + DepositAsset (KLT, 100) beneficiary=Bob +``` + +```text +WithdrawAsset (KLT, 100) +ReserveAssetDeposited(WETH, 2) +PayFees (WETH, 1) +SetAssetClaimer Kilt/Bob // derived from beneficiary on final destination +AliasOrigin Ethereum/Alice // derived from msg.sender +InitiateAssetsTransfer asset=(KLT, 100) remoteFee=(WETH, 1) dest=KLT + DepositAsset (KLT, 100) beneficiary=Bob + Transact Bob.hello() +``` + +### Step 4: KILT Receives XCM from AH + +The following XCM $x_1$ is received from AH on KILT. + +```text +*WithdrawAsset (KLT, 100) +*ReserveAssetDeposited (WETH, 1) +*PayFees (WETH, 1) +*SetAssetClaimer Ethereum/Alice +*AliasOrigin Ethereum/Alice // origin preserved from AH +SetAssetClaimer Bob +DepositAsset (KLT, 100) beneficiary=Bob +``` + +```text +*WithdrawAsset (KLT, 100) +*ReserveAssetDeposited (WETH, 1) +*PayFees (WETH, 1) +*SetAssetClaimer Ethereum/Alice +*AliasOrigin Ethereum/Alice // origin preserved from AH +SetAssetClaimer Bob +DepositAsset (KLT, 100) beneficiary=Bob +Transact Bob.hello() // executes with the origin from AH +``` + +## Relayer Rewards + +The tracking and disbursement of relayer rewards for both directions has been unified. Rewards are accumulated on BH in +WETH and must be manually claimed. As part of the claims flow, an XCM instruction is sent to AH to mint the WETH into +the deposit account chosen by the relayer. + +To claim, call following extrinsic, where $o$ is rewards account (origin), and $w$ is account on AH where the WETH will +be minted. + +$$ +\mathrm{claim}(o,w) +$$ + +For tax accounting purposes it might be desirable that $o \neq w$. + +## Top-Up + +Top-up of the relayer reward is viable to implement for either direction as extrinsics on Bridge Hub and Ethereum +respectively. + +## Origin Preservation + +Origins for transact will be preserved by use of the `AliasOrigin` instruction. This instruction will have the following +rules that parachain runtimes will need to allow: + +1. `AliasOrigin` can behave like `DescendOrigin`. This is safe because it respects the hierarchy of multi-locations and + does not allow jumping up. Meaning no escalation of privileges. + 1. Example location `Ethereum` can alias into `Ethereum/Alice` because we are descending in origin and this + essentially is how the `DescendOrigin` instruction works. +2. `AliasOrigin` must allow AH to alias into bridged locations such as + `{ parents: 2, interior: GlobalConsensus(Ethereum) }` and all of its internal locations so that AH can act as a proxy + for the bridge on parachains. + +`AliasOrigin` will be inserted by every `InitiateAssetTransfer` instruction on the source parachain, populated with the +contents of the origin register, essentially forwarding the origin of the source to the destination. + +RFCS: + +[https://github.com/polkadot-fellows/RFCs/pull/122](https://github.com/polkadot-fellows/RFCs/pull/122) + +[https://github.com/polkadot-fellows/RFCs/blob/main/text/0100-xcm-multi-type-asset-transfer.md](https://github.com/polkadot-fellows/RFCs/blob/main/text/0100-xcm-multi-type-asset-transfer.md) + +## Parachain Requirements + +1. Pallet-xcm.execute enabled. +2. XCM payment and dry run apis implemented. +3. Must accept WETH needed for fees. Though in future user agents can inject `ExchangeAsset` instructions to obtain + WETH. +4. Trust AH as a reserve for bridged assets. +5. Origin Preservation rules configured which allow asset hub to impersonate bridged addresses. diff --git a/bridges/snowbridge/pallets/ethereum-client/src/lib.rs b/bridges/snowbridge/pallets/ethereum-client/src/lib.rs index 6894977c21f4a2a78b1c7f67eb0b36a791936b92..311b54b97dee92559f21c195a4f24bd71bacbd8f 100644 --- a/bridges/snowbridge/pallets/ethereum-client/src/lib.rs +++ b/bridges/snowbridge/pallets/ethereum-client/src/lib.rs @@ -33,7 +33,10 @@ mod tests; mod benchmarking; use frame_support::{ - dispatch::DispatchResult, pallet_prelude::OptionQuery, traits::Get, transactional, + dispatch::{DispatchResult, PostDispatchInfo}, + pallet_prelude::OptionQuery, + traits::Get, + transactional, }; use frame_system::ensure_signed; use snowbridge_beacon_primitives::{ @@ -82,6 +85,9 @@ pub mod pallet { type RuntimeEvent: From> + IsType<::RuntimeEvent>; #[pallet::constant] type ForkVersions: Get; + /// Minimum gap between finalized headers for an update to be free. + #[pallet::constant] + type FreeHeadersInterval: Get; type WeightInfo: WeightInfo; } @@ -173,6 +179,10 @@ pub mod pallet { #[pallet::storage] pub type NextSyncCommittee = StorageValue<_, SyncCommitteePrepared, ValueQuery>; + /// The last period where the next sync committee was updated for free. + #[pallet::storage] + pub type LatestSyncCommitteeUpdatePeriod = StorageValue<_, u64, ValueQuery>; + /// The current operating mode of the pallet. #[pallet::storage] #[pallet::getter(fn operating_mode)] @@ -204,11 +214,10 @@ pub mod pallet { #[transactional] /// Submits a new finalized beacon header update. The update may contain the next /// sync committee. - pub fn submit(origin: OriginFor, update: Box) -> DispatchResult { + pub fn submit(origin: OriginFor, update: Box) -> DispatchResultWithPostInfo { ensure_signed(origin)?; ensure!(!Self::operating_mode().is_halted(), Error::::Halted); - Self::process_update(&update)?; - Ok(()) + Self::process_update(&update) } /// Halt or resume all pallet operations. May only be called by root. @@ -280,10 +289,9 @@ pub mod pallet { Ok(()) } - pub(crate) fn process_update(update: &Update) -> DispatchResult { + pub(crate) fn process_update(update: &Update) -> DispatchResultWithPostInfo { Self::verify_update(update)?; - Self::apply_update(update)?; - Ok(()) + Self::apply_update(update) } /// References and strictly follows @@ -432,11 +440,19 @@ pub mod pallet { /// Reference and strictly follows DispatchResult { + /// SyncCommitteePrepared type. Stores the provided finalized header. Updates are free + /// if the certain conditions specified in `check_refundable` are met. + fn apply_update(update: &Update) -> DispatchResultWithPostInfo { let latest_finalized_state = FinalizedBeaconState::::get(LatestFinalizedBlockRoot::::get()) .ok_or(Error::::NotBootstrapped)?; + + let pays_fee = Self::check_refundable(update, latest_finalized_state.slot); + let actual_weight = match update.next_sync_committee_update { + None => T::WeightInfo::submit(), + Some(_) => T::WeightInfo::submit_with_sync_committee(), + }; + if let Some(next_sync_committee_update) = &update.next_sync_committee_update { let store_period = compute_period(latest_finalized_state.slot); let update_finalized_period = compute_period(update.finalized_header.slot); @@ -460,6 +476,7 @@ pub mod pallet { "๐Ÿ’ซ SyncCommitteeUpdated at period {}.", update_finalized_period ); + >::set(update_finalized_period); Self::deposit_event(Event::SyncCommitteeUpdated { period: update_finalized_period, }); @@ -469,7 +486,7 @@ pub mod pallet { Self::store_finalized_header(update.finalized_header, update.block_roots_root)?; } - Ok(()) + Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee }) } /// Computes the signing root for a given beacon header and domain. The hash tree root @@ -634,11 +651,38 @@ pub mod pallet { config::SLOTS_PER_EPOCH as u64, )); let domain_type = config::DOMAIN_SYNC_COMMITTEE.to_vec(); - // Domains are used for for seeds, for signatures, and for selecting aggregators. + // Domains are used for seeds, for signatures, and for selecting aggregators. let domain = Self::compute_domain(domain_type, fork_version, validators_root)?; // Hash tree root of SigningData - object root + domain let signing_root = Self::compute_signing_root(header, domain)?; Ok(signing_root) } + + /// Updates are free if the update is successful and the interval between the latest + /// finalized header in storage and the newly imported header is large enough. All + /// successful sync committee updates are free. + pub(super) fn check_refundable(update: &Update, latest_slot: u64) -> Pays { + // If the sync committee was successfully updated, the update may be free. + let update_period = compute_period(update.finalized_header.slot); + let latest_free_update_period = LatestSyncCommitteeUpdatePeriod::::get(); + // If the next sync committee is not known and this update sets it, the update is free. + // If the sync committee update is in a period that we have not received an update for, + // the update is free. + let refundable = + !>::exists() || update_period > latest_free_update_period; + if update.next_sync_committee_update.is_some() && refundable { + return Pays::No; + } + + // If the latest finalized header is larger than the minimum slot interval, the header + // import transaction is free. + if update.finalized_header.slot >= + latest_slot.saturating_add(T::FreeHeadersInterval::get() as u64) + { + return Pays::No; + } + + Pays::Yes + } } } diff --git a/bridges/snowbridge/pallets/ethereum-client/src/mock.rs b/bridges/snowbridge/pallets/ethereum-client/src/mock.rs index 96298d4fa8962641d6ec8bab0ef67b751ab749cc..7dbabdee8234f023d40d4c3cd80ce831da2f3b26 100644 --- a/bridges/snowbridge/pallets/ethereum-client/src/mock.rs +++ b/bridges/snowbridge/pallets/ethereum-client/src/mock.rs @@ -10,6 +10,7 @@ use sp_std::default::Default; use std::{fs::File, path::PathBuf}; type Block = frame_system::mocking::MockBlock; +use frame_support::traits::ConstU32; use sp_runtime::BuildStorage; fn load_fixture(basename: String) -> Result @@ -58,6 +59,33 @@ pub fn load_next_finalized_header_update_fixture() -> snowbridge_beacon_primitiv load_fixture("next-finalized-header-update.json".to_string()).unwrap() } +pub fn load_sync_committee_update_period_0() -> Box< + snowbridge_beacon_primitives::Update< + { config::SYNC_COMMITTEE_SIZE }, + { config::SYNC_COMMITTEE_BITS_SIZE }, + >, +> { + Box::new(load_fixture("sync-committee-update-period-0.json".to_string()).unwrap()) +} + +pub fn load_sync_committee_update_period_0_older_fixture() -> Box< + snowbridge_beacon_primitives::Update< + { config::SYNC_COMMITTEE_SIZE }, + { config::SYNC_COMMITTEE_BITS_SIZE }, + >, +> { + Box::new(load_fixture("sync-committee-update-period-0-older.json".to_string()).unwrap()) +} + +pub fn load_sync_committee_update_period_0_newer_fixture() -> Box< + snowbridge_beacon_primitives::Update< + { config::SYNC_COMMITTEE_SIZE }, + { config::SYNC_COMMITTEE_BITS_SIZE }, + >, +> { + Box::new(load_fixture("sync-committee-update-period-0-newer.json".to_string()).unwrap()) +} + pub fn get_message_verification_payload() -> (Log, Proof) { let inbound_fixture = snowbridge_pallet_ethereum_client_fixtures::make_inbound_fixture(); (inbound_fixture.message.event_log, inbound_fixture.message.proof) @@ -108,9 +136,12 @@ parameter_types! { }; } +pub const FREE_SLOTS_INTERVAL: u32 = config::SLOTS_PER_EPOCH as u32; + impl ethereum_beacon_client::Config for Test { type RuntimeEvent = RuntimeEvent; type ForkVersions = ChainForkVersions; + type FreeHeadersInterval = ConstU32; type WeightInfo = (); } diff --git a/bridges/snowbridge/pallets/ethereum-client/src/tests.rs b/bridges/snowbridge/pallets/ethereum-client/src/tests.rs index c16743b75ea40b793947240773d159da908dab01..de298ee711d062f9f545ffd7cd8e9a5cfafa495e 100644 --- a/bridges/snowbridge/pallets/ethereum-client/src/tests.rs +++ b/bridges/snowbridge/pallets/ethereum-client/src/tests.rs @@ -1,21 +1,19 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2023 Snowfork +pub use crate::mock::*; use crate::{ - functions::compute_period, sync_committee_sum, verify_merkle_branch, BeaconHeader, - CompactBeaconState, Error, FinalizedBeaconState, LatestFinalizedBlockRoot, NextSyncCommittee, - SyncCommitteePrepared, -}; - -use crate::mock::{ - get_message_verification_payload, load_checkpoint_update_fixture, - load_finalized_header_update_fixture, load_next_finalized_header_update_fixture, - load_next_sync_committee_update_fixture, load_sync_committee_update_fixture, + config::{EPOCHS_PER_SYNC_COMMITTEE_PERIOD, SLOTS_PER_EPOCH, SLOTS_PER_HISTORICAL_ROOT}, + functions::compute_period, + mock::{ + get_message_verification_payload, load_checkpoint_update_fixture, + load_finalized_header_update_fixture, load_next_finalized_header_update_fixture, + load_next_sync_committee_update_fixture, load_sync_committee_update_fixture, + }, + sync_committee_sum, verify_merkle_branch, BeaconHeader, CompactBeaconState, Error, + FinalizedBeaconState, LatestFinalizedBlockRoot, LatestSyncCommitteeUpdatePeriod, + NextSyncCommittee, SyncCommitteePrepared, }; - -pub use crate::mock::*; - -use crate::config::{EPOCHS_PER_SYNC_COMMITTEE_PERIOD, SLOTS_PER_EPOCH, SLOTS_PER_HISTORICAL_ROOT}; -use frame_support::{assert_err, assert_noop, assert_ok}; +use frame_support::{assert_err, assert_noop, assert_ok, pallet_prelude::Pays}; use hex_literal::hex; use snowbridge_beacon_primitives::{ types::deneb, Fork, ForkVersions, NextSyncCommitteeUpdate, VersionedExecutionPayloadHeader, @@ -129,6 +127,39 @@ pub fn compute_domain_bls() { }); } +#[test] +pub fn may_refund_call_fee() { + let finalized_update = Box::new(load_next_finalized_header_update_fixture()); + let sync_committee_update = Box::new(load_sync_committee_update_fixture()); + new_tester().execute_with(|| { + let free_headers_interval: u64 = crate::mock::FREE_SLOTS_INTERVAL as u64; + // Not free, smaller than the allowed free header interval + assert_eq!( + EthereumBeaconClient::check_refundable( + &finalized_update.clone(), + finalized_update.finalized_header.slot + free_headers_interval + ), + Pays::Yes + ); + // Is free, larger than the minimum interval + assert_eq!( + EthereumBeaconClient::check_refundable( + &finalized_update, + finalized_update.finalized_header.slot - (free_headers_interval + 2) + ), + Pays::No + ); + // Is free, valid sync committee update + assert_eq!( + EthereumBeaconClient::check_refundable( + &sync_committee_update, + finalized_update.finalized_header.slot + ), + Pays::No + ); + }); +} + #[test] pub fn verify_merkle_branch_for_finalized_root() { new_tester().execute_with(|| { @@ -340,7 +371,9 @@ fn submit_update_in_current_period() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone())); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone()); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, Pays::No); let block_root: H256 = update.finalized_header.hash_tree_root().unwrap(); assert!(>::contains_key(block_root)); }); @@ -357,7 +390,9 @@ fn submit_update_with_sync_committee_in_current_period() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); assert!(!>::exists()); - assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update)); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, Pays::No); assert!(>::exists()); }); } @@ -374,20 +409,21 @@ fn reject_submit_update_in_next_period() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - assert_ok!(EthereumBeaconClient::submit( - RuntimeOrigin::signed(1), - sync_committee_update.clone() - )); + let result = + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), sync_committee_update.clone()); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, Pays::No); + // check an update in the next period is rejected - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone()), - Error::::SyncCommitteeUpdateRequired - ); + let second_result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone()); + assert_err!(second_result, Error::::SyncCommitteeUpdateRequired); + assert_eq!(second_result.unwrap_err().post_info.pays_fee, Pays::Yes); + // submit update with next sync committee - assert_ok!(EthereumBeaconClient::submit( - RuntimeOrigin::signed(1), - next_sync_committee_update - )); + let third_result = + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), next_sync_committee_update); + assert_ok!(third_result); + assert_eq!(third_result.unwrap().pays_fee, Pays::No); // check same header in the next period can now be submitted successfully assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone())); let block_root: H256 = update.finalized_header.clone().hash_tree_root().unwrap(); @@ -407,10 +443,9 @@ fn submit_update_with_invalid_header_proof() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); assert!(!>::exists()); - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update), - Error::::InvalidHeaderMerkleProof - ); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update); + assert_err!(result, Error::::InvalidHeaderMerkleProof); + assert_eq!(result.unwrap_err().post_info.pays_fee, Pays::Yes); }); } @@ -426,10 +461,9 @@ fn submit_update_with_invalid_block_roots_proof() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); assert!(!>::exists()); - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update), - Error::::InvalidBlockRootsRootMerkleProof - ); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update); + assert_err!(result, Error::::InvalidBlockRootsRootMerkleProof); + assert_eq!(result.unwrap_err().post_info.pays_fee, Pays::Yes); }); } @@ -447,10 +481,9 @@ fn submit_update_with_invalid_next_sync_committee_proof() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); assert!(!>::exists()); - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update), - Error::::InvalidSyncCommitteeMerkleProof - ); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update); + assert_err!(result, Error::::InvalidSyncCommitteeMerkleProof); + assert_eq!(result.unwrap_err().post_info.pays_fee, Pays::Yes); }); } @@ -464,14 +497,14 @@ fn submit_update_with_skipped_period() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - assert_ok!(EthereumBeaconClient::submit( - RuntimeOrigin::signed(1), - sync_committee_update.clone() - )); - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update), - Error::::SkippedSyncCommitteePeriod - ); + let result = + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), sync_committee_update.clone()); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, Pays::No); + + let second_result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update); + assert_err!(second_result, Error::::SkippedSyncCommitteePeriod); + assert_eq!(second_result.unwrap_err().post_info.pays_fee, Pays::Yes); }); } @@ -487,9 +520,16 @@ fn submit_update_with_sync_committee_in_next_period() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); assert!(!>::exists()); - assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone())); + + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone()); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, Pays::No); assert!(>::exists()); - assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), next_update.clone())); + + let second_result = + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), next_update.clone()); + assert_ok!(second_result); + assert_eq!(second_result.unwrap().pays_fee, Pays::No); let last_finalized_state = FinalizedBeaconState::::get(LatestFinalizedBlockRoot::::get()).unwrap(); let last_synced_period = compute_period(last_finalized_state.slot); @@ -505,13 +545,12 @@ fn submit_update_with_sync_committee_invalid_signature_slot() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - // makes a invalid update with signature_slot should be more than attested_slot + // makes an invalid update with signature_slot should be more than attested_slot update.signature_slot = update.attested_header.slot; - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update), - Error::::InvalidUpdateSlot - ); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update); + assert_err!(result, Error::::InvalidUpdateSlot); + assert_eq!(result.unwrap_err().post_info.pays_fee, Pays::Yes); }); } @@ -525,10 +564,9 @@ fn submit_update_with_skipped_sync_committee_period() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), finalized_update), - Error::::SkippedSyncCommitteePeriod - ); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), finalized_update); + assert_err!(result, Error::::SkippedSyncCommitteePeriod); + assert_eq!(result.unwrap_err().post_info.pays_fee, Pays::Yes); }); } @@ -546,10 +584,9 @@ fn submit_irrelevant_update() { update.attested_header.slot = checkpoint.header.slot; update.signature_slot = checkpoint.header.slot + 1; - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update), - Error::::IrrelevantUpdate - ); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update); + assert_err!(result, Error::::IrrelevantUpdate); + assert_eq!(result.unwrap_err().post_info.pays_fee, Pays::Yes); }); } @@ -558,10 +595,9 @@ fn submit_update_with_missing_bootstrap() { let update = Box::new(load_next_finalized_header_update_fixture()); new_tester().execute_with(|| { - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update), - Error::::NotBootstrapped - ); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update); + assert_err!(result, Error::::NotBootstrapped); + assert_eq!(result.unwrap_err().post_info.pays_fee, Pays::Yes); }); } @@ -574,7 +610,9 @@ fn submit_update_with_invalid_sync_committee_update() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update)); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, Pays::No); // makes update with invalid next_sync_committee >::mutate(>::get(), |x| { @@ -586,10 +624,9 @@ fn submit_update_with_invalid_sync_committee_update() { let next_sync_committee = NextSyncCommitteeUpdate::default(); next_update.next_sync_committee_update = Some(next_sync_committee); - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), next_update), - Error::::InvalidSyncCommitteeUpdate - ); + let second_result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), next_update); + assert_err!(second_result, Error::::InvalidSyncCommitteeUpdate); + assert_eq!(second_result.unwrap_err().post_info.pays_fee, Pays::Yes); }); } @@ -612,12 +649,15 @@ fn submit_finalized_header_update_with_too_large_gap() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone())); + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone()); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, Pays::No); assert!(>::exists()); - assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), next_update.clone()), - Error::::InvalidFinalizedHeaderGap - ); + + let second_result = + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), next_update.clone()); + assert_err!(second_result, Error::::InvalidFinalizedHeaderGap); + assert_eq!(second_result.unwrap_err().post_info.pays_fee, Pays::Yes); }); } @@ -637,14 +677,89 @@ fn submit_finalized_header_update_with_gap_at_limit() { new_tester().execute_with(|| { assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); - assert_ok!(EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone())); + + let result = EthereumBeaconClient::submit(RuntimeOrigin::signed(1), update.clone()); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, Pays::No); assert!(>::exists()); + + let second_result = + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), next_update.clone()); assert_err!( - EthereumBeaconClient::submit(RuntimeOrigin::signed(1), next_update.clone()), + second_result, // The test should pass the InvalidFinalizedHeaderGap check, and will fail at the // next check, the merkle proof, because we changed the next_update slots. Error::::InvalidHeaderMerkleProof ); + assert_eq!(second_result.unwrap_err().post_info.pays_fee, Pays::Yes); + }); +} + +#[test] +fn duplicate_sync_committee_updates_are_not_free() { + let checkpoint = Box::new(load_checkpoint_update_fixture()); + let sync_committee_update = Box::new(load_sync_committee_update_fixture()); + + new_tester().execute_with(|| { + assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); + let result = + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), sync_committee_update.clone()); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, Pays::No); + + // Check that if the same update is submitted, the update is not free. + let second_result = + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), sync_committee_update); + assert_ok!(second_result); + assert_eq!(second_result.unwrap().pays_fee, Pays::Yes); + }); +} + +#[test] +fn sync_committee_update_for_sync_committee_already_imported_are_not_free() { + let checkpoint = Box::new(load_checkpoint_update_fixture()); + let sync_committee_update = Box::new(load_sync_committee_update_fixture()); // slot 129 + let second_sync_committee_update = load_sync_committee_update_period_0(); // slot 128 + let third_sync_committee_update = load_sync_committee_update_period_0_newer_fixture(); // slot 224 + let fourth_sync_committee_update = load_sync_committee_update_period_0_older_fixture(); // slot 96 + let fith_sync_committee_update = Box::new(load_next_sync_committee_update_fixture()); // slot 8259 + + new_tester().execute_with(|| { + assert_ok!(EthereumBeaconClient::process_checkpoint_update(&checkpoint)); + assert_eq!(>::get(), 0); + + // Check that setting the next sync committee for period 0 is free (it is not set yet). + let result = + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), sync_committee_update.clone()); + assert_ok!(result); + assert_eq!(result.unwrap().pays_fee, Pays::No); + assert_eq!(>::get(), 0); + + // Check that setting the next sync committee for period 0 again is not free. + let second_result = + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), second_sync_committee_update); + assert_eq!(second_result.unwrap().pays_fee, Pays::Yes); + assert_eq!(>::get(), 0); + + // Check that setting an update with a sync committee that has already been set, but with a + // newer finalized header, is free. + let third_result = + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), third_sync_committee_update); + assert_eq!(third_result.unwrap().pays_fee, Pays::No); + assert_eq!(>::get(), 0); + + // Check that setting the next sync committee for period 0 again with an earlier slot is not + // free. + let fourth_result = + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), fourth_sync_committee_update); + assert_err!(fourth_result, Error::::IrrelevantUpdate); + assert_eq!(fourth_result.unwrap_err().post_info.pays_fee, Pays::Yes); + + // Check that setting the next sync committee for period 1 is free. + let fith_result = + EthereumBeaconClient::submit(RuntimeOrigin::signed(1), fith_sync_committee_update); + assert_eq!(fith_result.unwrap().pays_fee, Pays::No); + assert_eq!(>::get(), 1); }); } diff --git a/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/initial-checkpoint.json b/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/initial-checkpoint.json index a62d646617e49ee11c1d68e401069c5bdf4368be..34e65d20b885874e35d4499bafd906e22a2ecd1e 100755 --- a/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/initial-checkpoint.json +++ b/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/initial-checkpoint.json @@ -1,10 +1,10 @@ { "header": { - "slot": 864, + "slot": 64, "proposer_index": 4, - "parent_root": "0x614e7672f991ac268cd841055973f55e1e42228831a211adef207bb7329be614", - "state_root": "0x5fa8dfca3d760e4242ab46d529144627aa85348a19173b6e081172c701197a4a", - "body_root": "0x0f34c083b1803666bb1ac5e73fa71582731a2cf37d279ff0a3b0cad5a2ff371e" + "parent_root": "0x88e5b7e0dd468b334caf9281e0665184d2d712d7ffe632123ea07631b714920c", + "state_root": "0x82771f834d4d896f4969abdaf45f28f49a7437ecfca7bf2f7db7bfac5ca7224f", + "body_root": "0x8b36f34ceba40a29c9c6fa6266564c7df30ea75fecf1a85e6ec1cb4aabf4dc68" }, "current_sync_committee": { "pubkeys": [ @@ -525,18 +525,18 @@ }, "current_sync_committee_branch": [ "0x3ade38d498a062b50880a9409e1ca3a7fd4315d91eeb3bb83e56ac6bfe8d6a59", - "0xa9e90f89e7f90fd5d79a6bbcaf40ba5cfc05ab1b561ac51c84867c32248d5b1e", - "0xbd1a76b03e02402bb24a627de1980a80ab17691980271f597b844b89b497ef75", - "0x07bbcd27c7cad089023db046eda17e8209842b7d97add8b873519e84fe6480e7", - "0x94c11eeee4cb6192bf40810f23486d8c75dfbc2b6f28d988d6f74435ede243b0" + "0x058baa5628d6156e55ab99da54244be4a071978528f2eb3b19a4f4d7ab36f870", + "0x5f89984c1068b616e99589e161d2bb73b92c68b3422ef309ace434894b4503ae", + "0x4f1c230cf2bbe39502171956421fbe4f1c0a71a9691944019047b84584b371d5", + "0xbf8d5f6021db16e9b50e639e5c489eb8dc06449bf4ed17045cb949cb89a58a04" ], "validators_root": "0x270d43e74ce340de4bca2b1936beca0f4f5408d9e78aec4850920baf659d5b69", - "block_roots_root": "0xb9aab9c388c4e4fcd899b71f62c498fc73406e38e8eb14aa440e9affa06f2a10", + "block_roots_root": "0x2c453665ba6fc024116daf5246126e36085c61257cfbcce69d0bdcf89c766dc0", "block_roots_branch": [ - "0x733422bd810895dab74cbbe07c69dd440cbb51f573181ad4dddac30fcdd0f41f", - "0x9b9eca73ab01d14549c325ba1b4610bb20bf1f8ec2dbd649f9d8cc7f3cea75fa", - "0xbcc666ad0ad9f9725cbd682bc95589d35b1b53b2a615f1e6e8dd5e086336becf", - "0x3069b547a08f703a1715016e926cbd64e71f93f64fb68d98d8c8f1ab745c46e5", - "0xc2de7e1097239404e17b263cfa0473533cc41e903cb03440d633bc5c27314cb4" + "0xbd04f51e43f63b0be48034920e8f5976111b7717225abccedbc6bcb327b95d00", + "0x758319a3bad11ee10fde1036551d982583c0392f284de5cc429b67fbd74c25d5", + "0xb42179d040c2bec20fa0a2750baf225b8097b5c9e4e22af9250cc773f4259427", + "0x5340ad5877c72dca689ca04bc8fedb78d67a4801d99887937edd8ccd29f87e82", + "0x9f03be8e70f74fc6b51e6ed03c96aabb544b5c50e5cdb8c0ab5001d1249d55f0" ] -} \ No newline at end of file +} diff --git a/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/sync-committee-update-period-0-newer.json b/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/sync-committee-update-period-0-newer.json new file mode 100755 index 0000000000000000000000000000000000000000..7139589acbcebd14171c4a7d405a1864fea1bfff --- /dev/null +++ b/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/sync-committee-update-period-0-newer.json @@ -0,0 +1,565 @@ +{ + "attested_header": { + "slot": 224, + "proposer_index": 0, + "parent_root": "0xecfba5f579f43f474039f6f9abce51eb5607f6295aa45e1c353fa20245ab4efb", + "state_root": "0x10b21ccac4df114a9c30eaaff57f064b692e957a52eb43a8264702da76ba81f7", + "body_root": "0x6bd1768f675673b4ae32a197f569f7d279568fd5f60d32bd6ea11ecff559fc35" + }, + "sync_aggregate": { + "sync_committee_bits": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000", + "sync_committee_signature": "0xb8f4800cb32edf6d05e9cace783d663719f7750f0438b8481c89895809c5430005df25b73393133c9df595e5998d6a540449d8840f8bd16474608bb0b9daa349b76429d8d7e314f2fb6e628c4f68c5469bc8c698bb232a767a4b080b8909aa53" + }, + "signature_slot": 225, + "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", + "0xaad994f17223061c45fb5ec4930b2da08512e221ca6857bde8929eda92dc115c", + "0x61145312b89c006c2d1406285a9f2f826679d20b00239f65f76d40e28abe3bca", + "0x37977cb0ebd513f5123ede3a57b228f31eb98ecaad7757cf8e405fee8224982e", + "0x8c24e3a8ddb0bad93d5dcd240f566c5d08bc381a58b94e337bed63f75104fe45" + ] + }, + "finalized_header": { + "slot": 160, + "proposer_index": 0, + "parent_root": "0x6b536af592b64a337ae033b9646c4a10fd3369be72fcdaf53ae37797df8ec581", + "state_root": "0x1ed5990e4a1188a49ee64cdeb0ee9e480f29ce4d8020a0c5407471771a76ef2d", + "body_root": "0x73fb27d7521c84855007a824231d3b2b1650cd9ee34d914625f692c36b8112ef" + }, + "finality_branch": [ + "0x0500000000000000000000000000000000000000000000000000000000000000", + "0x10c726fac935bf9657cc7476d3cfa7bedec5983dcfb59e8a7df6d0a619e108d7", + "0x98e9116c6bb7f20de18800dc63e73e689d06d6a47d35b5e2b32cf093d475840d", + "0x61145312b89c006c2d1406285a9f2f826679d20b00239f65f76d40e28abe3bca", + "0x37977cb0ebd513f5123ede3a57b228f31eb98ecaad7757cf8e405fee8224982e", + "0x8c24e3a8ddb0bad93d5dcd240f566c5d08bc381a58b94e337bed63f75104fe45" + ], + "block_roots_root": "0xa626dafac4b71585a5b18d18198d7e7c0a09c43b0fb3f2e68e04304d3be94b91", + "block_roots_branch": [ + "0x1a4ced7954adc2f360994137f07d1ae456b008d5ff81f40f252da770a0cd70c9", + "0xa6d595807cef4f868a03813aceb42f07fadf37f93d5b30a3603f55c1eab0081d", + "0x50f2310554199f26d4a326c940dd6e014db55bb8f18bf3642fed22e58ddb5dd6", + "0xd8a7fed47a6e1934c5a5750a44aa70de9898c42e877fc87f0acb0e1b9d236091", + "0xad421833151ec4b8fd8269f16b4b41f15e7e0b82d561553ed5a50e5d6c5f2190" + ], + "execution_header": null, + "execution_branch": null +} \ No newline at end of file diff --git a/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/sync-committee-update-period-0-older.json b/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/sync-committee-update-period-0-older.json new file mode 100755 index 0000000000000000000000000000000000000000..b0eff7cac1b092e23efacd53ff2b384e2c6f20c5 --- /dev/null +++ b/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/sync-committee-update-period-0-older.json @@ -0,0 +1,565 @@ +{ + "attested_header": { + "slot": 96, + "proposer_index": 5, + "parent_root": "0x711c0cbebb834c0cd47d74732d78bc9f4794be2d7805176a4613ebaa9546569e", + "state_root": "0xe5ee40ae4ce991c927de404f3aea3209a55f29b54ee96d146c1e9fb733e14018", + "body_root": "0x57953c9bb22c5231b07078e6a3d82bd85ccdf48f55b4bb410c20af4cf4c3b03e" + }, + "sync_aggregate": { + "sync_committee_bits": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sync_committee_signature": "0xa8a01929a4018d7f5cf3d0511b68ae6af1e32320a263d282ff85bf56860154bd70cd9b0b0f4aa7a956d0375b9b4ba6700c723fcaaeb577acd9a0a88baf0bb418e39f97b17b1edcaeb95fa086d4c5d410addc9f29c0b6c6c14775216cdcb828db" + }, + "signature_slot": 97, + "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", + "0x48118ce24b62eda9ed2d37108f94efe223e6a385d84bcec6b2a53584271ea001", + "0xd72abb2443691ce25174da082c4c60880775d67f83802afd73cc2bf0edd06f73", + "0x0de609b4a50cd2729a8f9d9b6a505b008555dc121b18fb99c148be86ae08a53e", + "0xfb86aae7b54b08642d51132227e409e5247fa9ddb24287deab442ebf5dd9146c" + ] + }, + "finalized_header": { + "slot": 64, + "proposer_index": 4, + "parent_root": "0x60e496771388130ba1dc1d5d447bd43b4a5026a5d17d20f34d5352c0a97e5585", + "state_root": "0x7007a070c06dbd1c6de2f6fb1288f6569a13a00a1ed7505a8b1ede38827dd39c", + "body_root": "0xbccefd80ea680aa944837ec75d660651f369f72724f125e871b787c3dab18ea4" + }, + "finality_branch": [ + "0x0200000000000000000000000000000000000000000000000000000000000000", + "0x10c726fac935bf9657cc7476d3cfa7bedec5983dcfb59e8a7df6d0a619e108d7", + "0x98e9116c6bb7f20de18800dc63e73e689d06d6a47d35b5e2b32cf093d475840d", + "0xd72abb2443691ce25174da082c4c60880775d67f83802afd73cc2bf0edd06f73", + "0x0de609b4a50cd2729a8f9d9b6a505b008555dc121b18fb99c148be86ae08a53e", + "0xfb86aae7b54b08642d51132227e409e5247fa9ddb24287deab442ebf5dd9146c" + ], + "block_roots_root": "0xf70c00c84139e631f8d4a69120f5837e5d14db26aee6aa29f5a6a100b53f820b", + "block_roots_branch": [ + "0x3c2f0c8588c1501bcd371de7103ad74ae93fe72b4703a1bd00fd77acefd90c76", + "0x8ac33e1bd9a7fa543236bf6f385b6082bb6e68ec344d0bc03e620dd908df4b07", + "0x56e652a369b875c2f28e96d341ed76ca453e2f5a0ee2ca571a9ae19d92e842df", + "0x5340ad5877c72dca689ca04bc8fedb78d67a4801d99887937edd8ccd29f87e82", + "0x91eee53bd353a3e021e2c382d9502503b7f9f1198b042ff36e8abdc74fd920dc" + ], + "execution_header": null, + "execution_branch": null +} \ No newline at end of file diff --git a/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/sync-committee-update-period-0.json b/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/sync-committee-update-period-0.json new file mode 100644 index 0000000000000000000000000000000000000000..916deb7513c8e141152a498cf3832068cb5190aa --- /dev/null +++ b/bridges/snowbridge/pallets/ethereum-client/tests/fixtures/sync-committee-update-period-0.json @@ -0,0 +1,565 @@ +{ + "attested_header": { + "slot": 128, + "proposer_index": 1, + "parent_root": "0x2161b169bc9dda1785a8c087e6455d9648d8df8c6d5f98f75d29c1c1c9e13ceb", + "state_root": "0x044bb5ec8eabc0ba7a74646cb92e4c6bd96f5d2974e0e191d3fd05de4eb1acea", + "body_root": "0x2b52b7dbe94cd1c024431064486880f2093480498f2b8a704fec9edc34f68eb8" + }, + "sync_aggregate": { + "sync_committee_bits": "0x00000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "sync_committee_signature": "0x95ceea859d98d209441120821af32fa7ceb6080cf62db7a00a0f578ac83a4a1c619104474e715d1688732e8fe5b19f2417a4f6ba957b3cd2b8c817c8d8c42fc822062385269858feb955cd010744d8357dffef00535cf2e7a1017e58b22c4423" + }, + "signature_slot": 129, + "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", + "0x028330a337168f77730425239a3abdfe336671cf5047fd03ea84eb668a0bad9e", + "0xe2b84cae247ad985d1d089df0f668f7f29ba1db750e5f32159e002dcda2d3f5f", + "0xecf54973b62af22f2620c37c14138021e5ea274f80815a52b3ed6c6234e039da", + "0x63a9c666a4d51dbfceda9b1c9dac57019fce464fd5733e6a6598dde49cc4ea23" + ] + }, + "finalized_header": { + "slot": 64, + "proposer_index": 4, + "parent_root": "0x88e5b7e0dd468b334caf9281e0665184d2d712d7ffe632123ea07631b714920c", + "state_root": "0x82771f834d4d896f4969abdaf45f28f49a7437ecfca7bf2f7db7bfac5ca7224f", + "body_root": "0x8b36f34ceba40a29c9c6fa6266564c7df30ea75fecf1a85e6ec1cb4aabf4dc68" + }, + "finality_branch": [ + "0x0200000000000000000000000000000000000000000000000000000000000000", + "0x10c726fac935bf9657cc7476d3cfa7bedec5983dcfb59e8a7df6d0a619e108d7", + "0x98e9116c6bb7f20de18800dc63e73e689d06d6a47d35b5e2b32cf093d475840d", + "0xe2b84cae247ad985d1d089df0f668f7f29ba1db750e5f32159e002dcda2d3f5f", + "0xecf54973b62af22f2620c37c14138021e5ea274f80815a52b3ed6c6234e039da", + "0x63a9c666a4d51dbfceda9b1c9dac57019fce464fd5733e6a6598dde49cc4ea23" + ], + "block_roots_root": "0x2c453665ba6fc024116daf5246126e36085c61257cfbcce69d0bdcf89c766dc0", + "block_roots_branch": [ + "0xbd04f51e43f63b0be48034920e8f5976111b7717225abccedbc6bcb327b95d00", + "0x758319a3bad11ee10fde1036551d982583c0392f284de5cc429b67fbd74c25d5", + "0xb42179d040c2bec20fa0a2750baf225b8097b5c9e4e22af9250cc773f4259427", + "0x5340ad5877c72dca689ca04bc8fedb78d67a4801d99887937edd8ccd29f87e82", + "0x9f03be8e70f74fc6b51e6ed03c96aabb544b5c50e5cdb8c0ab5001d1249d55f0" + ], + "execution_header": null, + "execution_branch": null +} \ No newline at end of file diff --git a/bridges/snowbridge/pallets/inbound-queue/src/lib.rs b/bridges/snowbridge/pallets/inbound-queue/src/lib.rs index 4a1486204eb08a43846bafaacfa05c465d8dc5fd..423b92b9fae04dceca93820f0a2f11a866498576 100644 --- a/bridges/snowbridge/pallets/inbound-queue/src/lib.rs +++ b/bridges/snowbridge/pallets/inbound-queue/src/lib.rs @@ -48,12 +48,11 @@ use frame_support::{ }; use frame_system::ensure_signed; use scale_info::TypeInfo; -use sp_core::{H160, H256}; +use sp_core::H160; use sp_runtime::traits::Zero; use sp_std::vec; use xcm::prelude::{ - send_xcm, Instruction::SetTopic, Junction::*, Location, SendError as XcmpSendError, SendXcm, - Xcm, XcmContext, XcmHash, + send_xcm, Junction::*, Location, SendError as XcmpSendError, SendXcm, Xcm, XcmContext, XcmHash, }; use xcm_executor::traits::TransactAsset; @@ -62,9 +61,8 @@ use snowbridge_core::{ sibling_sovereign_account, BasicOperatingMode, Channel, ChannelId, ParaId, PricingParameters, StaticLookup, }; -use snowbridge_router_primitives::{ - inbound, - inbound::{ConvertMessage, ConvertMessageError}, +use snowbridge_router_primitives::inbound::{ + ConvertMessage, ConvertMessageError, VersionedMessage, }; use sp_runtime::{traits::Saturating, SaturatedConversion, TokenError}; @@ -86,6 +84,7 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; + use sp_core::H256; #[pallet::pallet] pub struct Pallet(_); @@ -276,12 +275,12 @@ pub mod pallet { T::Token::transfer(&sovereign_account, &who, amount, Preservation::Preserve)?; } + // Decode payload into `VersionedMessage` + let message = VersionedMessage::decode_all(&mut envelope.payload.as_ref()) + .map_err(|_| Error::::InvalidPayload)?; + // Decode message into XCM - let (xcm, fee) = - match inbound::VersionedMessage::decode_all(&mut envelope.payload.as_ref()) { - Ok(message) => Self::do_convert(envelope.message_id, message)?, - Err(_) => return Err(Error::::InvalidPayload.into()), - }; + let (xcm, fee) = Self::do_convert(envelope.message_id, message.clone())?; log::info!( target: LOG_TARGET, @@ -323,12 +322,10 @@ pub mod pallet { impl Pallet { pub fn do_convert( message_id: H256, - message: inbound::VersionedMessage, + message: VersionedMessage, ) -> Result<(Xcm<()>, BalanceOf), Error> { - let (mut xcm, fee) = - T::MessageConverter::convert(message).map_err(|e| Error::::ConvertMessage(e))?; - // Append the message id as an XCM topic - xcm.inner_mut().extend(vec![SetTopic(message_id.into())]); + let (xcm, fee) = T::MessageConverter::convert(message_id, message) + .map_err(|e| Error::::ConvertMessage(e))?; Ok((xcm, fee)) } diff --git a/bridges/snowbridge/pallets/inbound-queue/src/mock.rs b/bridges/snowbridge/pallets/inbound-queue/src/mock.rs index a031676c6076ad417a9d0fd1f060e9387bb99eea..3e67d5ab738b1a1a492667ceb955c5508292dac3 100644 --- a/bridges/snowbridge/pallets/inbound-queue/src/mock.rs +++ b/bridges/snowbridge/pallets/inbound-queue/src/mock.rs @@ -10,12 +10,12 @@ use snowbridge_beacon_primitives::{ use snowbridge_core::{ gwei, inbound::{Log, Proof, VerificationError}, - meth, Channel, ChannelId, PricingParameters, Rewards, StaticLookup, + meth, Channel, ChannelId, PricingParameters, Rewards, StaticLookup, TokenId, }; use snowbridge_router_primitives::inbound::MessageToXcm; use sp_core::{H160, H256}; use sp_runtime::{ - traits::{IdentifyAccount, IdentityLookup, Verify}, + traits::{IdentifyAccount, IdentityLookup, MaybeEquivalence, Verify}, BuildStorage, FixedU128, MultiSignature, }; use sp_std::{convert::From, default::Default}; @@ -88,6 +88,7 @@ parameter_types! { impl snowbridge_pallet_ethereum_client::Config for Test { type RuntimeEvent = RuntimeEvent; type ForkVersions = ChainForkVersions; + type FreeHeadersInterval = ConstU32<32>; type WeightInfo = (); } @@ -111,6 +112,9 @@ parameter_types! { pub const SendTokenExecutionFee: u128 = 1_000_000_000; pub const InitialFund: u128 = 1_000_000_000_000; pub const InboundQueuePalletInstance: u8 = 80; + pub UniversalLocation: InteriorLocation = + [GlobalConsensus(Westend), Parachain(1002)].into(); + pub AssetHubFromEthereum: Location = Location::new(1,[GlobalConsensus(Westend),Parachain(1000)]); } #[cfg(feature = "runtime-benchmarks")] @@ -204,6 +208,16 @@ impl TransactAsset for SuccessfulTransactor { } } +pub struct MockTokenIdConvert; +impl MaybeEquivalence for MockTokenIdConvert { + fn convert(_id: &TokenId) -> Option { + Some(Location::parent()) + } + fn convert_back(_loc: &Location) -> Option { + None + } +} + impl inbound_queue::Config for Test { type RuntimeEvent = RuntimeEvent; type Verifier = MockVerifier; @@ -217,6 +231,9 @@ impl inbound_queue::Config for Test { InboundQueuePalletInstance, AccountId, Balance, + MockTokenIdConvert, + UniversalLocation, + AssetHubFromEthereum, >; type PricingParameters = Parameters; type ChannelLookup = MockChannelLookup; diff --git a/bridges/snowbridge/pallets/inbound-queue/src/test.rs b/bridges/snowbridge/pallets/inbound-queue/src/test.rs index bd993c968df7373910b438a2e090ef2896bb41dd..41c38460aabf1c796d743f0ee9de135d3c9e9ef0 100644 --- a/bridges/snowbridge/pallets/inbound-queue/src/test.rs +++ b/bridges/snowbridge/pallets/inbound-queue/src/test.rs @@ -40,8 +40,8 @@ fn test_submit_happy_path() { .into(), nonce: 1, message_id: [ - 57, 61, 232, 3, 66, 61, 25, 190, 234, 188, 193, 174, 13, 186, 1, 64, 237, 94, 73, - 83, 14, 18, 209, 213, 78, 121, 43, 108, 251, 245, 107, 67, + 255, 125, 48, 71, 174, 185, 100, 26, 159, 43, 108, 6, 116, 218, 55, 155, 223, 143, + 141, 22, 124, 110, 241, 18, 122, 217, 130, 29, 139, 76, 97, 201, ], fee_burned: 110000000000, } diff --git a/bridges/snowbridge/pallets/outbound-queue/merkle-tree/src/lib.rs b/bridges/snowbridge/pallets/outbound-queue/merkle-tree/src/lib.rs index d5c89b9c0987f22573aed765ce87ca0ec54c5bf1..eeeaa6e68cf94faf4739fc4c3cdc9bfb99b466ef 100644 --- a/bridges/snowbridge/pallets/outbound-queue/merkle-tree/src/lib.rs +++ b/bridges/snowbridge/pallets/outbound-queue/merkle-tree/src/lib.rs @@ -182,12 +182,6 @@ where let root = merkelize::(hashes.into_iter(), &mut collect_proof); let leaf = leaf.expect("Requested `leaf_index` is greater than number of leaves."); - #[cfg(feature = "debug")] - log::debug!( - "[merkle_proof] Proof: {:?}", - collect_proof.proof.iter().map(hex::encode).collect::>() - ); - MerkleProof { root, proof: collect_proof.proof, number_of_leaves, leaf_index, leaf } } @@ -274,8 +268,6 @@ where V: Visitor, I: Iterator, { - #[cfg(feature = "debug")] - log::debug!("[merkelize_row]"); next.clear(); let hash_len = ::LENGTH; @@ -286,9 +278,6 @@ where let b = iter.next(); visitor.visit(index, &a, &b); - #[cfg(feature = "debug")] - log::debug!(" {:?}\n {:?}", a.as_ref().map(hex::encode), b.as_ref().map(hex::encode)); - index += 2; match (a, b) { (Some(a), Some(b)) => { @@ -309,14 +298,7 @@ where // Last item = root. (Some(a), None) => return Ok(a), // Finish up, no more items. - _ => { - #[cfg(feature = "debug")] - log::debug!( - "[merkelize_row] Next: {:?}", - next.iter().map(hex::encode).collect::>() - ); - return Err(next) - }, + _ => return Err(next), } } } diff --git a/bridges/snowbridge/pallets/outbound-queue/src/mock.rs b/bridges/snowbridge/pallets/outbound-queue/src/mock.rs index d65a96e2702dfbc69e52a913964de230bcdeadd0..0b34893333e4b33be3be949a032521ac3f240a8f 100644 --- a/bridges/snowbridge/pallets/outbound-queue/src/mock.rs +++ b/bridges/snowbridge/pallets/outbound-queue/src/mock.rs @@ -164,13 +164,11 @@ pub fn mock_message(sibling_para_id: u32) -> Message { Message { id: None, channel_id: ParaId::from(sibling_para_id).into(), - command: Command::AgentExecute { + command: Command::TransferNativeToken { agent_id: Default::default(), - command: AgentExecuteCommand::TransferToken { - token: Default::default(), - recipient: Default::default(), - amount: 0, - }, + token: Default::default(), + recipient: Default::default(), + amount: 0, }, } } diff --git a/bridges/snowbridge/pallets/system/src/benchmarking.rs b/bridges/snowbridge/pallets/system/src/benchmarking.rs index ef908ad6a3f9dee2f269333a79b8d4afa5e6b7ca..20798b7c349389815b96eecc3d3f9887337f5fce 100644 --- a/bridges/snowbridge/pallets/system/src/benchmarking.rs +++ b/bridges/snowbridge/pallets/system/src/benchmarking.rs @@ -159,6 +159,29 @@ mod benchmarks { Ok(()) } + #[benchmark] + fn register_token() -> Result<(), BenchmarkError> { + let caller: T::AccountId = whitelisted_caller(); + + let amount: BalanceOf = + (10_000_000_000_000_u128).saturated_into::().saturated_into(); + + T::Token::mint_into(&caller, amount)?; + + let relay_token_asset_id: Location = Location::parent(); + let asset = Box::new(VersionedLocation::V4(relay_token_asset_id)); + let asset_metadata = AssetMetadata { + name: "wnd".as_bytes().to_vec().try_into().unwrap(), + symbol: "wnd".as_bytes().to_vec().try_into().unwrap(), + decimals: 12, + }; + + #[extrinsic_call] + _(RawOrigin::Root, asset, asset_metadata); + + Ok(()) + } + impl_benchmark_test_suite!( SnowbridgeControl, crate::mock::new_test_ext(true), diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 39c73e3630e7b425e46252943a44d10bfe1cebc7..1e8a788b7a5a8979e66f9e0003a1fc169f6c398d 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -35,8 +35,14 @@ //! //! Typically, Polkadot governance will use the `force_transfer_native_from_agent` and //! `force_update_channel` and extrinsics to manage agents and channels for system parachains. +//! +//! ## Polkadot-native tokens on Ethereum +//! +//! Tokens deposited on AssetHub pallet can be bridged to Ethereum as wrapped ERC20 tokens. As a +//! prerequisite, the token should be registered first. +//! +//! * [`Call::register_token`]: Register a token location as a wrapped ERC20 contract on Ethereum. #![cfg_attr(not(feature = "std"), no_std)] - #[cfg(test)] mod mock; @@ -63,13 +69,16 @@ use frame_system::pallet_prelude::*; use snowbridge_core::{ meth, outbound::{Command, Initializer, Message, OperatingMode, SendError, SendMessage}, - sibling_sovereign_account, AgentId, Channel, ChannelId, ParaId, - PricingParameters as PricingParametersRecord, PRIMARY_GOVERNANCE_CHANNEL, + sibling_sovereign_account, AgentId, AssetMetadata, Channel, ChannelId, ParaId, + PricingParameters as PricingParametersRecord, TokenId, TokenIdOf, PRIMARY_GOVERNANCE_CHANNEL, SECONDARY_GOVERNANCE_CHANNEL, }; use sp_core::{RuntimeDebug, H160, H256}; use sp_io::hashing::blake2_256; -use sp_runtime::{traits::BadOrigin, DispatchError, SaturatedConversion}; +use sp_runtime::{ + traits::{BadOrigin, MaybeEquivalence}, + DispatchError, SaturatedConversion, +}; use sp_std::prelude::*; use xcm::prelude::*; use xcm_executor::traits::ConvertLocation; @@ -99,7 +108,7 @@ where } /// Hash the location to produce an agent id -fn agent_id_of(location: &Location) -> Result { +pub fn agent_id_of(location: &Location) -> Result { T::AgentIdOf::convert_location(location).ok_or(Error::::LocationConversionFailed.into()) } @@ -127,6 +136,7 @@ where #[frame_support::pallet] pub mod pallet { + use frame_support::dispatch::PostDispatchInfo; use snowbridge_core::StaticLookup; use sp_core::U256; @@ -164,6 +174,12 @@ pub mod pallet { type WeightInfo: WeightInfo; + /// This chain's Universal Location. + type UniversalLocation: Get; + + // The bridges configured Ethereum location + type EthereumLocation: Get; + #[cfg(feature = "runtime-benchmarks")] type Helper: BenchmarkHelper; } @@ -211,6 +227,13 @@ pub mod pallet { PricingParametersChanged { params: PricingParametersOf, }, + /// Register Polkadot-native token as a wrapped ERC20 token on Ethereum + RegisterToken { + /// Location of Polkadot-native token + location: VersionedLocation, + /// ID of Polkadot-native token on Ethereum + foreign_token_id: H256, + }, } #[pallet::error] @@ -243,6 +266,16 @@ pub mod pallet { pub type PricingParameters = StorageValue<_, PricingParametersOf, ValueQuery, T::DefaultPricingParameters>; + /// Lookup table for foreign token ID to native location relative to ethereum + #[pallet::storage] + pub type ForeignToNativeId = + StorageMap<_, Blake2_128Concat, TokenId, xcm::v4::Location, OptionQuery>; + + /// Lookup table for native location relative to ethereum to foreign token ID + #[pallet::storage] + pub type NativeToForeignId = + StorageMap<_, Blake2_128Concat, xcm::v4::Location, TokenId, OptionQuery>; + #[pallet::genesis_config] #[derive(frame_support::DefaultNoBound)] pub struct GenesisConfig { @@ -574,6 +607,34 @@ pub mod pallet { }); Ok(()) } + + /// Registers a Polkadot-native token as a wrapped ERC20 token on Ethereum. + /// Privileged. Can only be called by root. + /// + /// Fee required: No + /// + /// - `origin`: Must be root + /// - `location`: Location of the asset (relative to this chain) + /// - `metadata`: Metadata to include in the instantiated ERC20 contract on Ethereum + #[pallet::call_index(10)] + #[pallet::weight(T::WeightInfo::register_token())] + pub fn register_token( + origin: OriginFor, + location: Box, + metadata: AssetMetadata, + ) -> DispatchResultWithPostInfo { + ensure_root(origin)?; + + let location: Location = + (*location).try_into().map_err(|_| Error::::UnsupportedLocationVersion)?; + + Self::do_register_token(&location, metadata, PaysFee::::No)?; + + Ok(PostDispatchInfo { + actual_weight: Some(T::WeightInfo::register_token()), + pays_fee: Pays::No, + }) + } } impl Pallet { @@ -663,6 +724,42 @@ pub mod pallet { let secondary_exists = Channels::::contains_key(SECONDARY_GOVERNANCE_CHANNEL); primary_exists && secondary_exists } + + pub(crate) fn do_register_token( + location: &Location, + metadata: AssetMetadata, + pays_fee: PaysFee, + ) -> Result<(), DispatchError> { + let ethereum_location = T::EthereumLocation::get(); + // reanchor to Ethereum context + let location = location + .clone() + .reanchored(ðereum_location, &T::UniversalLocation::get()) + .map_err(|_| Error::::LocationConversionFailed)?; + + let token_id = TokenIdOf::convert_location(&location) + .ok_or(Error::::LocationConversionFailed)?; + + if !ForeignToNativeId::::contains_key(token_id) { + NativeToForeignId::::insert(location.clone(), token_id); + ForeignToNativeId::::insert(token_id, location.clone()); + } + + let command = Command::RegisterForeignToken { + token_id, + name: metadata.name.into_inner(), + symbol: metadata.symbol.into_inner(), + decimals: metadata.decimals, + }; + Self::send(SECONDARY_GOVERNANCE_CHANNEL, command, pays_fee)?; + + Self::deposit_event(Event::::RegisterToken { + location: location.clone().into(), + foreign_token_id: token_id, + }); + + Ok(()) + } } impl StaticLookup for Pallet { @@ -684,4 +781,13 @@ pub mod pallet { PricingParameters::::get() } } + + impl MaybeEquivalence for Pallet { + fn convert(foreign_id: &TokenId) -> Option { + ForeignToNativeId::::get(foreign_id) + } + fn convert_back(location: &Location) -> Option { + NativeToForeignId::::get(location) + } + } } diff --git a/bridges/snowbridge/pallets/system/src/mock.rs b/bridges/snowbridge/pallets/system/src/mock.rs index 98bd3da9ab27c974a1a85abbb208064e41f89991..47b089866a53fda4a689efb5e37b8e66f5df433d 100644 --- a/bridges/snowbridge/pallets/system/src/mock.rs +++ b/bridges/snowbridge/pallets/system/src/mock.rs @@ -166,10 +166,12 @@ impl snowbridge_pallet_outbound_queue::Config for Test { parameter_types! { pub const SS58Prefix: u8 = 42; pub const AnyNetwork: Option = None; - pub const RelayNetwork: Option = Some(NetworkId::Kusama); + pub const RelayNetwork: Option = Some(NetworkId::Polkadot); pub const RelayLocation: Location = Location::parent(); pub UniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(1013)].into(); + pub EthereumNetwork: NetworkId = NetworkId::Ethereum { chain_id: 11155111 }; + pub EthereumDestination: Location = Location::new(2,[GlobalConsensus(EthereumNetwork::get())]); } pub const DOT: u128 = 10_000_000_000; @@ -177,8 +179,8 @@ pub const DOT: u128 = 10_000_000_000; parameter_types! { pub TreasuryAccount: AccountId = PalletId(*b"py/trsry").into_account_truncating(); pub Fee: u64 = 1000; - pub const RococoNetwork: NetworkId = NetworkId::Rococo; pub const InitialFunding: u128 = 1_000_000_000_000; + pub BridgeHubParaId: ParaId = ParaId::new(1002); pub AssetHubParaId: ParaId = ParaId::new(1000); pub TestParaId: u32 = 2000; pub Parameters: PricingParameters = PricingParameters { @@ -188,7 +190,6 @@ parameter_types! { multiplier: FixedU128::from_rational(4, 3) }; pub const InboundDeliveryCost: u128 = 1_000_000_000; - } #[cfg(feature = "runtime-benchmarks")] @@ -208,6 +209,8 @@ impl crate::Config for Test { type DefaultPricingParameters = Parameters; type WeightInfo = (); type InboundDeliveryCost = InboundDeliveryCost; + type UniversalLocation = UniversalLocation; + type EthereumLocation = EthereumDestination; #[cfg(feature = "runtime-benchmarks")] type Helper = (); } diff --git a/bridges/snowbridge/pallets/system/src/tests.rs b/bridges/snowbridge/pallets/system/src/tests.rs index 09f24195a30ad8ee13f4587c54102bc24eed4134..d0286e04abdfca3375d1d71446f62408e4e94464 100644 --- a/bridges/snowbridge/pallets/system/src/tests.rs +++ b/bridges/snowbridge/pallets/system/src/tests.rs @@ -248,7 +248,7 @@ fn create_channel() { let _ = Balances::mint_into(&sovereign_account, 10000); assert_ok!(EthereumSystem::create_agent(origin.clone())); - assert_ok!(EthereumSystem::create_channel(origin, OperatingMode::Normal)); + assert_ok!(EthereumSystem::create_channel(origin, OperatingMode::Normal,)); }); } @@ -264,10 +264,10 @@ fn create_channel_fail_already_exists() { let _ = Balances::mint_into(&sovereign_account, 10000); assert_ok!(EthereumSystem::create_agent(origin.clone())); - assert_ok!(EthereumSystem::create_channel(origin.clone(), OperatingMode::Normal)); + assert_ok!(EthereumSystem::create_channel(origin.clone(), OperatingMode::Normal,)); assert_noop!( - EthereumSystem::create_channel(origin, OperatingMode::Normal), + EthereumSystem::create_channel(origin, OperatingMode::Normal,), Error::::ChannelAlreadyCreated ); }); @@ -334,10 +334,10 @@ fn update_channel() { // First create the channel let _ = Balances::mint_into(&sovereign_account, 10000); assert_ok!(EthereumSystem::create_agent(origin.clone())); - assert_ok!(EthereumSystem::create_channel(origin.clone(), OperatingMode::Normal)); + assert_ok!(EthereumSystem::create_channel(origin.clone(), OperatingMode::Normal,)); // Now try to update it - assert_ok!(EthereumSystem::update_channel(origin, OperatingMode::Normal)); + assert_ok!(EthereumSystem::update_channel(origin, OperatingMode::Normal,)); System::assert_last_event(RuntimeEvent::EthereumSystem(crate::Event::UpdateChannel { channel_id: ParaId::from(2000).into(), @@ -383,12 +383,12 @@ fn update_channel_bad_origin() { // Signed origin not allowed assert_noop!( - EthereumSystem::update_channel(RuntimeOrigin::signed([14; 32].into()), mode), + EthereumSystem::update_channel(RuntimeOrigin::signed([14; 32].into()), mode,), BadOrigin ); // None origin not allowed - assert_noop!(EthereumSystem::update_channel(RuntimeOrigin::none(), mode), BadOrigin); + assert_noop!(EthereumSystem::update_channel(RuntimeOrigin::none(), mode,), BadOrigin); }); } @@ -400,7 +400,7 @@ fn update_channel_fails_not_exist() { // Now try to update it assert_noop!( - EthereumSystem::update_channel(origin, OperatingMode::Normal), + EthereumSystem::update_channel(origin, OperatingMode::Normal,), Error::::NoChannel ); }); @@ -419,7 +419,7 @@ fn force_update_channel() { // First create the channel let _ = Balances::mint_into(&sovereign_account, 10000); assert_ok!(EthereumSystem::create_agent(origin.clone())); - assert_ok!(EthereumSystem::create_channel(origin.clone(), OperatingMode::Normal)); + assert_ok!(EthereumSystem::create_channel(origin.clone(), OperatingMode::Normal,)); // Now try to force update it let force_origin = RuntimeOrigin::root(); @@ -463,7 +463,7 @@ fn transfer_native_from_agent() { // First create the agent and channel assert_ok!(EthereumSystem::create_agent(origin.clone())); - assert_ok!(EthereumSystem::create_channel(origin, OperatingMode::Normal)); + assert_ok!(EthereumSystem::create_channel(origin, OperatingMode::Normal,)); let origin = make_xcm_origin(origin_location.clone()); assert_ok!(EthereumSystem::transfer_native_from_agent(origin, recipient, amount),); @@ -584,7 +584,7 @@ fn charge_fee_for_transfer_native_from_agent() { // create_agent & create_channel first assert_ok!(EthereumSystem::create_agent(origin.clone())); - assert_ok!(EthereumSystem::create_channel(origin.clone(), OperatingMode::Normal)); + assert_ok!(EthereumSystem::create_channel(origin.clone(), OperatingMode::Normal,)); // assert sovereign_balance decreased by only the base_fee let sovereign_balance_before = Balances::balance(&sovereign_account); @@ -631,3 +631,116 @@ fn no_genesis_build_is_uninitialized() { assert!(!EthereumSystem::is_initialized(), "Ethereum initialized."); }); } + +#[test] +fn register_token_with_signed_yields_bad_origin() { + new_test_ext(true).execute_with(|| { + let origin = RuntimeOrigin::signed([14; 32].into()); + let location = Location::new(1, [Parachain(2000)]); + let versioned_location: Box = Box::new(location.clone().into()); + assert_noop!( + EthereumSystem::register_token(origin, versioned_location, Default::default()), + BadOrigin + ); + }); +} + +pub struct RegisterTokenTestCase { + /// Input: Location of Polkadot-native token relative to BH + pub native: Location, + /// Output: Reanchored, canonicalized location + pub reanchored: Location, + /// Output: Stable hash of reanchored location + pub foreign: TokenId, +} + +#[test] +fn register_all_tokens_succeeds() { + let test_cases = vec![ + // DOT + RegisterTokenTestCase { + native: Location::parent(), + reanchored: Location::new(1, GlobalConsensus(Polkadot)), + foreign: hex!("4e241583d94b5d48a27a22064cd49b2ed6f5231d2d950e432f9b7c2e0ade52b2") + .into(), + }, + // GLMR (Some Polkadot parachain currency) + RegisterTokenTestCase { + native: Location::new(1, [Parachain(2004)]), + reanchored: Location::new(1, [GlobalConsensus(Polkadot), Parachain(2004)]), + foreign: hex!("34c08fc90409b6924f0e8eabb7c2aaa0c749e23e31adad9f6d217b577737fafb") + .into(), + }, + // USDT + RegisterTokenTestCase { + native: Location::new(1, [Parachain(1000), PalletInstance(50), GeneralIndex(1984)]), + reanchored: Location::new( + 1, + [ + GlobalConsensus(Polkadot), + Parachain(1000), + PalletInstance(50), + GeneralIndex(1984), + ], + ), + foreign: hex!("14b0579be12d7d7f9971f1d4b41f0e88384b9b74799b0150d4aa6cd01afb4444") + .into(), + }, + // KSM + RegisterTokenTestCase { + native: Location::new(2, [GlobalConsensus(Kusama)]), + reanchored: Location::new(1, [GlobalConsensus(Kusama)]), + foreign: hex!("03b6054d0c576dd8391e34e1609cf398f68050c23009d19ce93c000922bcd852") + .into(), + }, + // KAR (Some Kusama parachain currency) + RegisterTokenTestCase { + native: Location::new(2, [GlobalConsensus(Kusama), Parachain(2000)]), + reanchored: Location::new(1, [GlobalConsensus(Kusama), Parachain(2000)]), + foreign: hex!("d3e39ad6ea4cee68c9741181e94098823b2ea34a467577d0875c036f0fce5be0") + .into(), + }, + ]; + for tc in test_cases.iter() { + new_test_ext(true).execute_with(|| { + let origin = RuntimeOrigin::root(); + let versioned_location: VersionedLocation = tc.native.clone().into(); + + assert_ok!(EthereumSystem::register_token( + origin, + Box::new(versioned_location), + Default::default() + )); + + assert_eq!(NativeToForeignId::::get(tc.reanchored.clone()), Some(tc.foreign)); + assert_eq!(ForeignToNativeId::::get(tc.foreign), Some(tc.reanchored.clone())); + + System::assert_last_event(RuntimeEvent::EthereumSystem(Event::::RegisterToken { + location: tc.reanchored.clone().into(), + foreign_token_id: tc.foreign, + })); + }); + } +} + +#[test] +fn register_ethereum_native_token_fails() { + new_test_ext(true).execute_with(|| { + let origin = RuntimeOrigin::root(); + let location = Location::new( + 2, + [ + GlobalConsensus(Ethereum { chain_id: 11155111 }), + AccountKey20 { + network: None, + key: hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d"), + }, + ], + ); + let versioned_location: Box = Box::new(location.clone().into()); + assert_noop!( + EthereumSystem::register_token(origin, versioned_location, Default::default()), + Error::::LocationConversionFailed + ); + }); +} diff --git a/bridges/snowbridge/pallets/system/src/weights.rs b/bridges/snowbridge/pallets/system/src/weights.rs index 6e532a0d8a8c19339cc39574aeb09668468f34e9..3513097f8b554ac40a1c3780fb7da59c65dcfd20 100644 --- a/bridges/snowbridge/pallets/system/src/weights.rs +++ b/bridges/snowbridge/pallets/system/src/weights.rs @@ -42,6 +42,7 @@ pub trait WeightInfo { fn force_transfer_native_from_agent() -> Weight; fn set_token_transfer_fees() -> Weight; fn set_pricing_parameters() -> Weight; + fn register_token() -> Weight; } // For backwards compatibility and tests. @@ -246,4 +247,14 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } + + fn register_token() -> Weight { + // Proof Size summary in bytes: + // Measured: `256` + // Estimated: `6044` + // Minimum execution time: 45_000_000 picoseconds. + Weight::from_parts(45_000_000, 6044) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } } diff --git a/bridges/snowbridge/primitives/core/Cargo.toml b/bridges/snowbridge/primitives/core/Cargo.toml index f9bee1ff4959ae56f73b50d4c91c2ea2e63bb0a6..fa37c795b2d1e0871109cb393d6ef546e282cc99 100644 --- a/bridges/snowbridge/primitives/core/Cargo.toml +++ b/bridges/snowbridge/primitives/core/Cargo.toml @@ -35,6 +35,7 @@ ethabi = { workspace = true } [dev-dependencies] hex = { workspace = true, default-features = true } +xcm-executor = { workspace = true, default-features = true } [features] default = ["std"] @@ -62,4 +63,5 @@ runtime-benchmarks = [ "polkadot-parachain-primitives/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", + "xcm-executor/runtime-benchmarks", ] diff --git a/bridges/snowbridge/primitives/core/src/lib.rs b/bridges/snowbridge/primitives/core/src/lib.rs index ed1af4225d24bb9ae8d08f7adda2925e7d5029d3..7ad129a52542ed32f96e4533e7cf962039d7224d 100644 --- a/bridges/snowbridge/primitives/core/src/lib.rs +++ b/bridges/snowbridge/primitives/core/src/lib.rs @@ -9,11 +9,13 @@ mod tests; pub mod inbound; +pub mod location; pub mod operating_mode; pub mod outbound; pub mod pricing; pub mod ringbuffer; +pub use location::{AgentId, AgentIdOf, TokenId, TokenIdOf}; pub use polkadot_parachain_primitives::primitives::{ Id as ParaId, IsSystem, Sibling as SiblingParaId, }; @@ -21,18 +23,16 @@ pub use ringbuffer::{RingBufferMap, RingBufferMapImpl}; pub use sp_core::U256; use codec::{Decode, Encode, MaxEncodedLen}; -use frame_support::traits::Contains; +use frame_support::{traits::Contains, BoundedVec}; use hex_literal::hex; use scale_info::TypeInfo; -use sp_core::H256; +use sp_core::{ConstU32, H256}; use sp_io::hashing::keccak_256; use sp_runtime::{traits::AccountIdConversion, RuntimeDebug}; use sp_std::prelude::*; use xcm::prelude::{Junction::Parachain, Location}; -use xcm_builder::{DescribeAllTerminal, DescribeFamily, DescribeLocation, HashedDescription}; /// The ID of an agent contract -pub type AgentId = H256; pub use operating_mode::BasicOperatingMode; pub use pricing::{PricingParameters, Rewards}; @@ -151,16 +151,24 @@ pub const PRIMARY_GOVERNANCE_CHANNEL: ChannelId = pub const SECONDARY_GOVERNANCE_CHANNEL: ChannelId = ChannelId::new(hex!("0000000000000000000000000000000000000000000000000000000000000002")); -pub struct DescribeHere; -impl DescribeLocation for DescribeHere { - fn describe_location(l: &Location) -> Option> { - match l.unpack() { - (0, []) => Some(Vec::::new().encode()), - _ => None, +/// Metadata to include in the instantiated ERC20 token contract +#[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)] +pub struct AssetMetadata { + pub name: BoundedVec>, + pub symbol: BoundedVec>, + pub decimals: u8, +} + +#[cfg(any(test, feature = "std", feature = "runtime-benchmarks"))] +impl Default for AssetMetadata { + fn default() -> Self { + AssetMetadata { + name: BoundedVec::truncate_from(vec![]), + symbol: BoundedVec::truncate_from(vec![]), + decimals: 0, } } } -/// Creates an AgentId from a Location. An AgentId is a unique mapping to a Agent contract on -/// Ethereum which acts as the sovereign account for the Location. -pub type AgentIdOf = HashedDescription)>; +/// Maximum length of a string field in ERC20 token metada +const METADATA_FIELD_MAX_LEN: u32 = 32; diff --git a/bridges/snowbridge/primitives/core/src/location.rs b/bridges/snowbridge/primitives/core/src/location.rs new file mode 100644 index 0000000000000000000000000000000000000000..aad1c9ece05c3f7097a08ee243bad0ccec60010d --- /dev/null +++ b/bridges/snowbridge/primitives/core/src/location.rs @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +//! # Location +//! +//! Location helpers for dealing with Tokens and Agents + +pub use polkadot_parachain_primitives::primitives::{ + Id as ParaId, IsSystem, Sibling as SiblingParaId, +}; +pub use sp_core::U256; + +use codec::Encode; +use sp_core::H256; +use sp_std::prelude::*; +use xcm::prelude::{ + AccountId32, AccountKey20, GeneralIndex, GeneralKey, GlobalConsensus, Location, PalletInstance, +}; +use xcm_builder::{ + DescribeAllTerminal, DescribeFamily, DescribeLocation, DescribeTerminus, HashedDescription, +}; + +pub type AgentId = H256; + +/// Creates an AgentId from a Location. An AgentId is a unique mapping to a Agent contract on +/// Ethereum which acts as the sovereign account for the Location. +#[allow(deprecated)] +pub type AgentIdOf = + HashedDescription)>; + +pub type TokenId = H256; + +/// Convert a token location (relative to Ethereum) to a stable ID that can be used on the Ethereum +/// side +pub type TokenIdOf = HashedDescription< + TokenId, + DescribeGlobalPrefix<(DescribeTerminus, DescribeFamily)>, +>; + +/// This looks like DescribeTerminus that was added to xcm-builder. However this does an extra +/// `encode` to the Vector producing a different output to DescribeTerminus. `DescribeHere` +/// should NOT be used for new code. This is left here for backwards compatibility of channels and +/// agents. +#[deprecated(note = "Use DescribeTerminus from xcm-builder instead.")] +pub struct DescribeHere; +#[allow(deprecated)] +impl DescribeLocation for DescribeHere { + fn describe_location(l: &Location) -> Option> { + match l.unpack() { + (0, []) => Some(Vec::::new().encode()), + _ => None, + } + } +} +pub struct DescribeGlobalPrefix(sp_std::marker::PhantomData); +impl DescribeLocation for DescribeGlobalPrefix { + fn describe_location(l: &Location) -> Option> { + match (l.parent_count(), l.first_interior()) { + (1, Some(GlobalConsensus(network))) => { + let mut tail = l.clone().split_first_interior().0; + tail.dec_parent(); + let interior = Suffix::describe_location(&tail)?; + Some((b"GlobalConsensus", network, interior).encode()) + }, + _ => None, + } + } +} + +pub struct DescribeTokenTerminal; +impl DescribeLocation for DescribeTokenTerminal { + fn describe_location(l: &Location) -> Option> { + match l.unpack().1 { + [] => Some(Vec::::new().encode()), + [GeneralIndex(index)] => Some((b"GeneralIndex", *index).encode()), + [GeneralKey { data, .. }] => Some((b"GeneralKey", *data).encode()), + [AccountKey20 { key, .. }] => Some((b"AccountKey20", *key).encode()), + [AccountId32 { id, .. }] => Some((b"AccountId32", *id).encode()), + + // Pallet + [PalletInstance(instance)] => Some((b"PalletInstance", *instance).encode()), + [PalletInstance(instance), GeneralIndex(index)] => + Some((b"PalletInstance", *instance, b"GeneralIndex", *index).encode()), + [PalletInstance(instance), GeneralKey { data, .. }] => + Some((b"PalletInstance", *instance, b"GeneralKey", *data).encode()), + + [PalletInstance(instance), AccountKey20 { key, .. }] => + Some((b"PalletInstance", *instance, b"AccountKey20", *key).encode()), + [PalletInstance(instance), AccountId32 { id, .. }] => + Some((b"PalletInstance", *instance, b"AccountId32", *id).encode()), + + // Reject all other locations + _ => None, + } + } +} + +#[cfg(test)] +mod tests { + use crate::TokenIdOf; + use xcm::prelude::{ + GeneralIndex, GeneralKey, GlobalConsensus, Junction::*, Location, NetworkId::*, + PalletInstance, Parachain, + }; + use xcm_executor::traits::ConvertLocation; + + #[test] + fn test_token_of_id() { + let token_locations = [ + // Relay Chain cases + // Relay Chain relative to Ethereum + Location::new(1, [GlobalConsensus(Westend)]), + // Parachain cases + // Parachain relative to Ethereum + Location::new(1, [GlobalConsensus(Westend), Parachain(2000)]), + // Parachain general index + Location::new(1, [GlobalConsensus(Westend), Parachain(2000), GeneralIndex(1)]), + // Parachain general key + Location::new( + 1, + [ + GlobalConsensus(Westend), + Parachain(2000), + GeneralKey { length: 32, data: [0; 32] }, + ], + ), + // Parachain account key 20 + Location::new( + 1, + [ + GlobalConsensus(Westend), + Parachain(2000), + AccountKey20 { network: None, key: [0; 20] }, + ], + ), + // Parachain account id 32 + Location::new( + 1, + [ + GlobalConsensus(Westend), + Parachain(2000), + AccountId32 { network: None, id: [0; 32] }, + ], + ), + // Parchain Pallet instance cases + // Parachain pallet instance + Location::new(1, [GlobalConsensus(Westend), Parachain(2000), PalletInstance(8)]), + // Parachain Pallet general index + Location::new( + 1, + [GlobalConsensus(Westend), Parachain(2000), PalletInstance(8), GeneralIndex(1)], + ), + // Parachain Pallet general key + Location::new( + 1, + [ + GlobalConsensus(Westend), + Parachain(2000), + PalletInstance(8), + GeneralKey { length: 32, data: [0; 32] }, + ], + ), + // Parachain Pallet account key 20 + Location::new( + 1, + [ + GlobalConsensus(Westend), + Parachain(2000), + PalletInstance(8), + AccountKey20 { network: None, key: [0; 20] }, + ], + ), + // Parachain Pallet account id 32 + Location::new( + 1, + [ + GlobalConsensus(Westend), + Parachain(2000), + PalletInstance(8), + AccountId32 { network: None, id: [0; 32] }, + ], + ), + ]; + + for token in token_locations { + assert!( + TokenIdOf::convert_location(&token).is_some(), + "Valid token = {token:?} yeilds no TokenId." + ); + } + + let non_token_locations = [ + // Relative location for a token should fail. + Location::new(1, []), + // Relative location for a token should fail. + Location::new(1, [Parachain(1000)]), + ]; + + for token in non_token_locations { + assert!( + TokenIdOf::convert_location(&token).is_none(), + "Invalid token = {token:?} yeilds a TokenId." + ); + } + } +} diff --git a/bridges/snowbridge/primitives/core/src/outbound.rs b/bridges/snowbridge/primitives/core/src/outbound.rs index 0ba0fdb61089e9bd77f162a87bcf7f78842bae5c..77770761822a8fe1a88d39f304dac9124ba9be1a 100644 --- a/bridges/snowbridge/primitives/core/src/outbound.rs +++ b/bridges/snowbridge/primitives/core/src/outbound.rs @@ -139,6 +139,37 @@ mod v1 { // Fee multiplier multiplier: UD60x18, }, + /// Transfer ERC20 tokens + TransferNativeToken { + /// ID of the agent + agent_id: H256, + /// Address of the ERC20 token + token: H160, + /// The recipient of the tokens + recipient: H160, + /// The amount of tokens to transfer + amount: u128, + }, + /// Register foreign token from Polkadot + RegisterForeignToken { + /// ID for the token + token_id: H256, + /// Name of the token + name: Vec, + /// Short symbol for the token + symbol: Vec, + /// Number of decimal places + decimals: u8, + }, + /// Mint foreign token from Polkadot + MintForeignToken { + /// ID for the token + token_id: H256, + /// The recipient of the newly minted tokens + recipient: H160, + /// The amount of tokens to mint + amount: u128, + }, } impl Command { @@ -154,6 +185,9 @@ mod v1 { Command::TransferNativeFromAgent { .. } => 6, Command::SetTokenTransferFees { .. } => 7, Command::SetPricingParameters { .. } => 8, + Command::TransferNativeToken { .. } => 9, + Command::RegisterForeignToken { .. } => 10, + Command::MintForeignToken { .. } => 11, } } @@ -211,6 +245,26 @@ mod v1 { Token::Uint(U256::from(*delivery_cost)), Token::Uint(multiplier.clone().into_inner()), ])]), + Command::TransferNativeToken { agent_id, token, recipient, amount } => + ethabi::encode(&[Token::Tuple(vec![ + Token::FixedBytes(agent_id.as_bytes().to_owned()), + Token::Address(*token), + Token::Address(*recipient), + Token::Uint(U256::from(*amount)), + ])]), + Command::RegisterForeignToken { token_id, name, symbol, decimals } => + ethabi::encode(&[Token::Tuple(vec![ + Token::FixedBytes(token_id.as_bytes().to_owned()), + Token::String(name.to_owned()), + Token::String(symbol.to_owned()), + Token::Uint(U256::from(*decimals)), + ])]), + Command::MintForeignToken { token_id, recipient, amount } => + ethabi::encode(&[Token::Tuple(vec![ + Token::FixedBytes(token_id.as_bytes().to_owned()), + Token::Address(*recipient), + Token::Uint(U256::from(*amount)), + ])]), } } } @@ -403,6 +457,9 @@ impl GasMeter for ConstantGasMeter { }, Command::SetTokenTransferFees { .. } => 60_000, Command::SetPricingParameters { .. } => 60_000, + Command::TransferNativeToken { .. } => 100_000, + Command::RegisterForeignToken { .. } => 1_200_000, + Command::MintForeignToken { .. } => 100_000, } } } diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index 54e47a7a8b6af0a0dce1d54d52e3e0799e6f6e84..fbfc52d01c8373019054723fca8f05d90f33012d 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -9,9 +9,10 @@ use codec::{Decode, Encode}; use core::marker::PhantomData; use frame_support::{traits::tokens::Balance as BalanceT, weights::Weight, PalletError}; use scale_info::TypeInfo; -use sp_core::{Get, RuntimeDebug, H160}; +use snowbridge_core::TokenId; +use sp_core::{Get, RuntimeDebug, H160, H256}; use sp_io::hashing::blake2_256; -use sp_runtime::MultiAddress; +use sp_runtime::{traits::MaybeEquivalence, MultiAddress}; use sp_std::prelude::*; use xcm::prelude::{Junction::AccountKey20, *}; use xcm_executor::traits::ConvertLocation; @@ -45,7 +46,7 @@ pub enum Command { /// XCM execution fee on AssetHub fee: u128, }, - /// Send a token to AssetHub or another parachain + /// Send Ethereum token to AssetHub or another parachain SendToken { /// The address of the ERC20 token to be bridged over to AssetHub token: H160, @@ -56,6 +57,17 @@ pub enum Command { /// XCM execution fee on AssetHub fee: u128, }, + /// Send Polkadot token back to the original parachain + SendNativeToken { + /// The Id of the token + token_id: TokenId, + /// The destination for the transfer + destination: Destination, + /// Amount to transfer + amount: u128, + /// XCM execution fee on AssetHub + fee: u128, + }, } /// Destination for bridged tokens @@ -89,10 +101,16 @@ pub struct MessageToXcm< InboundQueuePalletInstance, AccountId, Balance, + ConvertAssetId, + EthereumUniversalLocation, + GlobalAssetHubLocation, > where CreateAssetCall: Get, CreateAssetDeposit: Get, Balance: BalanceT, + ConvertAssetId: MaybeEquivalence, + EthereumUniversalLocation: Get, + GlobalAssetHubLocation: Get, { _phantom: PhantomData<( CreateAssetCall, @@ -100,6 +118,9 @@ pub struct MessageToXcm< InboundQueuePalletInstance, AccountId, Balance, + ConvertAssetId, + EthereumUniversalLocation, + GlobalAssetHubLocation, )>, } @@ -108,6 +129,11 @@ pub struct MessageToXcm< pub enum ConvertMessageError { /// The message version is not supported for conversion. UnsupportedVersion, + InvalidDestination, + InvalidToken, + /// The fee asset is not supported for conversion. + UnsupportedFeeAsset, + CannotReanchor, } /// convert the inbound message to xcm which will be forwarded to the destination chain @@ -115,51 +141,109 @@ pub trait ConvertMessage { type Balance: BalanceT + From; type AccountId; /// Converts a versioned message into an XCM message and an optional topicID - fn convert(message: VersionedMessage) -> Result<(Xcm<()>, Self::Balance), ConvertMessageError>; + fn convert( + message_id: H256, + message: VersionedMessage, + ) -> Result<(Xcm<()>, Self::Balance), ConvertMessageError>; } pub type CallIndex = [u8; 2]; -impl - ConvertMessage +impl< + CreateAssetCall, + CreateAssetDeposit, + InboundQueuePalletInstance, + AccountId, + Balance, + ConvertAssetId, + EthereumUniversalLocation, + GlobalAssetHubLocation, + > ConvertMessage for MessageToXcm< CreateAssetCall, CreateAssetDeposit, InboundQueuePalletInstance, AccountId, Balance, - > where + ConvertAssetId, + EthereumUniversalLocation, + GlobalAssetHubLocation, + > +where CreateAssetCall: Get, CreateAssetDeposit: Get, InboundQueuePalletInstance: Get, Balance: BalanceT + From, AccountId: Into<[u8; 32]>, + ConvertAssetId: MaybeEquivalence, + EthereumUniversalLocation: Get, + GlobalAssetHubLocation: Get, { type Balance = Balance; type AccountId = AccountId; - fn convert(message: VersionedMessage) -> Result<(Xcm<()>, Self::Balance), ConvertMessageError> { + fn convert( + message_id: H256, + message: VersionedMessage, + ) -> Result<(Xcm<()>, Self::Balance), ConvertMessageError> { use Command::*; use VersionedMessage::*; match message { V1(MessageV1 { chain_id, command: RegisterToken { token, fee } }) => - Ok(Self::convert_register_token(chain_id, token, fee)), + Ok(Self::convert_register_token(message_id, chain_id, token, fee)), V1(MessageV1 { chain_id, command: SendToken { token, destination, amount, fee } }) => - Ok(Self::convert_send_token(chain_id, token, destination, amount, fee)), + Ok(Self::convert_send_token(message_id, chain_id, token, destination, amount, fee)), + V1(MessageV1 { + chain_id, + command: SendNativeToken { token_id, destination, amount, fee }, + }) => Self::convert_send_native_token( + message_id, + chain_id, + token_id, + destination, + amount, + fee, + ), } } } -impl - MessageToXcm +impl< + CreateAssetCall, + CreateAssetDeposit, + InboundQueuePalletInstance, + AccountId, + Balance, + ConvertAssetId, + EthereumUniversalLocation, + GlobalAssetHubLocation, + > + MessageToXcm< + CreateAssetCall, + CreateAssetDeposit, + InboundQueuePalletInstance, + AccountId, + Balance, + ConvertAssetId, + EthereumUniversalLocation, + GlobalAssetHubLocation, + > where CreateAssetCall: Get, CreateAssetDeposit: Get, InboundQueuePalletInstance: Get, Balance: BalanceT + From, AccountId: Into<[u8; 32]>, + ConvertAssetId: MaybeEquivalence, + EthereumUniversalLocation: Get, + GlobalAssetHubLocation: Get, { - fn convert_register_token(chain_id: u64, token: H160, fee: u128) -> (Xcm<()>, Balance) { + fn convert_register_token( + message_id: H256, + chain_id: u64, + token: H160, + fee: u128, + ) -> (Xcm<()>, Balance) { let network = Ethereum { chain_id }; let xcm_fee: Asset = (Location::parent(), fee).into(); let deposit: Asset = (Location::parent(), CreateAssetDeposit::get()).into(); @@ -167,7 +251,7 @@ where let total_amount = fee + CreateAssetDeposit::get(); let total: Asset = (Location::parent(), total_amount).into(); - let bridge_location: Location = (Parent, Parent, GlobalConsensus(network)).into(); + let bridge_location = Location::new(2, GlobalConsensus(network)); let owner = GlobalConsensusEthereumConvertsFor::<[u8; 32]>::from_chain_id(&chain_id); let asset_id = Self::convert_token_address(network, token); @@ -180,8 +264,15 @@ where // Pay for execution. BuyExecution { fees: xcm_fee, weight_limit: Unlimited }, // Fund the snowbridge sovereign with the required deposit for creation. - DepositAsset { assets: Definite(deposit.into()), beneficiary: bridge_location }, - // Only our inbound-queue pallet is allowed to invoke `UniversalOrigin` + DepositAsset { assets: Definite(deposit.into()), beneficiary: bridge_location.clone() }, + // This `SetAppendix` ensures that `xcm_fee` not spent by `Transact` will be + // deposited to snowbridge sovereign, instead of being trapped, regardless of + // `Transact` success or not. + SetAppendix(Xcm(vec![ + RefundSurplus, + DepositAsset { assets: AllCounted(1).into(), beneficiary: bridge_location }, + ])), + // Only our inbound-queue pallet is allowed to invoke `UniversalOrigin`. DescendOrigin(PalletInstance(inbound_queue_pallet_index).into()), // Change origin to the bridge. UniversalOrigin(GlobalConsensus(network)), @@ -198,10 +289,10 @@ where .encode() .into(), }, - RefundSurplus, - // Clear the origin so that remaining assets in holding - // are claimable by the physical origin (BridgeHub) - ClearOrigin, + // Forward message id to Asset Hub + SetTopic(message_id.into()), + // Once the program ends here, appendix program will run, which will deposit any + // leftover fee to snowbridge sovereign. ] .into(); @@ -209,6 +300,7 @@ where } fn convert_send_token( + message_id: H256, chain_id: u64, token: H160, destination: Destination, @@ -255,17 +347,26 @@ where match dest_para_id { Some(dest_para_id) => { let dest_para_fee_asset: Asset = (Location::parent(), dest_para_fee).into(); + let bridge_location = Location::new(2, GlobalConsensus(network)); instructions.extend(vec![ + // After program finishes deposit any leftover assets to the snowbridge + // sovereign. + SetAppendix(Xcm(vec![DepositAsset { + assets: Wild(AllCounted(2)), + beneficiary: bridge_location, + }])), // Perform a deposit reserve to send to destination chain. DepositReserveAsset { - assets: Definite(vec![dest_para_fee_asset.clone(), asset.clone()].into()), + assets: Definite(vec![dest_para_fee_asset.clone(), asset].into()), dest: Location::new(1, [Parachain(dest_para_id)]), xcm: vec![ // Buy execution on target. BuyExecution { fees: dest_para_fee_asset, weight_limit: Unlimited }, - // Deposit asset to beneficiary. - DepositAsset { assets: Definite(asset.into()), beneficiary }, + // Deposit assets to beneficiary. + DepositAsset { assets: Wild(AllCounted(2)), beneficiary }, + // Forward message id to destination parachain. + SetTopic(message_id.into()), ] .into(), }, @@ -281,6 +382,11 @@ where }, } + // Forward message id to Asset Hub. + instructions.push(SetTopic(message_id.into())); + + // The `instructions` to forward to AssetHub, and the `total_fees` to locally burn (since + // they are teleported within `instructions`). (instructions.into(), total_fees.into()) } @@ -291,6 +397,59 @@ where [GlobalConsensus(network), AccountKey20 { network: None, key: token.into() }], ) } + + /// Constructs an XCM message destined for AssetHub that withdraws assets from the sovereign + /// account of the Gateway contract and either deposits those assets into a recipient account or + /// forwards the assets to another parachain. + fn convert_send_native_token( + message_id: H256, + chain_id: u64, + token_id: TokenId, + destination: Destination, + amount: u128, + asset_hub_fee: u128, + ) -> Result<(Xcm<()>, Balance), ConvertMessageError> { + let network = Ethereum { chain_id }; + let asset_hub_fee_asset: Asset = (Location::parent(), asset_hub_fee).into(); + + let beneficiary = match destination { + // Final destination is a 32-byte account on AssetHub + Destination::AccountId32 { id } => + Ok(Location::new(0, [AccountId32 { network: None, id }])), + _ => Err(ConvertMessageError::InvalidDestination), + }?; + + let total_fee_asset: Asset = (Location::parent(), asset_hub_fee).into(); + + let asset_loc = + ConvertAssetId::convert(&token_id).ok_or(ConvertMessageError::InvalidToken)?; + + let mut reanchored_asset_loc = asset_loc.clone(); + reanchored_asset_loc + .reanchor(&GlobalAssetHubLocation::get(), &EthereumUniversalLocation::get()) + .map_err(|_| ConvertMessageError::CannotReanchor)?; + + let asset: Asset = (reanchored_asset_loc, amount).into(); + + let inbound_queue_pallet_index = InboundQueuePalletInstance::get(); + + let instructions = vec![ + ReceiveTeleportedAsset(total_fee_asset.clone().into()), + BuyExecution { fees: asset_hub_fee_asset, weight_limit: Unlimited }, + DescendOrigin(PalletInstance(inbound_queue_pallet_index).into()), + UniversalOrigin(GlobalConsensus(network)), + WithdrawAsset(asset.clone().into()), + // Deposit both asset and fees to beneficiary so the fees will not get + // trapped. Another benefit is when fees left more than ED on AssetHub could be + // used to create the beneficiary account in case it does not exist. + DepositAsset { assets: Wild(AllCounted(2)), beneficiary }, + SetTopic(message_id.into()), + ]; + + // `total_fees` to burn on this chain when sending `instructions` to run on AH (which also + // teleport fees) + Ok((instructions.into(), asset_hub_fee.into())) + } } pub struct GlobalConsensusEthereumConvertsFor(PhantomData); diff --git a/bridges/snowbridge/primitives/router/src/inbound/tests.rs b/bridges/snowbridge/primitives/router/src/inbound/tests.rs index 75670b05c1004d65f16e20848c05ca9b28c38452..e0e90e516be166b0dadd06b158c035e1f12e9d30 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/tests.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/tests.rs @@ -1,6 +1,6 @@ use super::GlobalConsensusEthereumConvertsFor; use crate::inbound::CallIndex; -use frame_support::parameter_types; +use frame_support::{assert_ok, parameter_types}; use hex_literal::hex; use xcm::prelude::*; use xcm_executor::traits::ConvertLocation; @@ -38,3 +38,32 @@ fn test_contract_location_with_incorrect_location_fails_convert() { None, ); } + +#[test] +fn test_reanchor_all_assets() { + let ethereum_context: InteriorLocation = [GlobalConsensus(Ethereum { chain_id: 1 })].into(); + let ethereum = Location::new(2, ethereum_context.clone()); + let ah_context: InteriorLocation = [GlobalConsensus(Polkadot), Parachain(1000)].into(); + let global_ah = Location::new(1, ah_context.clone()); + let assets = vec![ + // DOT + Location::new(1, []), + // GLMR (Some Polkadot parachain currency) + Location::new(1, [Parachain(2004)]), + // AH asset + Location::new(0, [PalletInstance(50), GeneralIndex(42)]), + // KSM + Location::new(2, [GlobalConsensus(Kusama)]), + // KAR (Some Kusama parachain currency) + Location::new(2, [GlobalConsensus(Kusama), Parachain(2000)]), + ]; + for asset in assets.iter() { + // reanchor logic in pallet_xcm on AH + let mut reanchored_asset = asset.clone(); + assert_ok!(reanchored_asset.reanchor(ðereum, &ah_context)); + // reanchor back to original location in context of Ethereum + let mut reanchored_asset_with_ethereum_context = reanchored_asset.clone(); + assert_ok!(reanchored_asset_with_ethereum_context.reanchor(&global_ah, ðereum_context)); + assert_eq!(reanchored_asset_with_ethereum_context, asset.clone()); + } +} diff --git a/bridges/snowbridge/primitives/router/src/outbound/mod.rs b/bridges/snowbridge/primitives/router/src/outbound/mod.rs index ddc36ce8cb61b370e862de1784a4f6b9fc6f16b0..efc1ef56f30479f2f238ebccbdae0303bc99e07c 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/mod.rs @@ -12,9 +12,10 @@ use codec::{Decode, Encode}; use frame_support::{ensure, traits::Get}; use snowbridge_core::{ outbound::{AgentExecuteCommand, Command, Message, SendMessage}, - ChannelId, ParaId, + AgentId, ChannelId, ParaId, TokenId, TokenIdOf, }; use sp_core::{H160, H256}; +use sp_runtime::traits::MaybeEquivalence; use sp_std::{iter::Peekable, marker::PhantomData, prelude::*}; use xcm::prelude::*; use xcm_executor::traits::{ConvertLocation, ExportXcm}; @@ -24,15 +25,32 @@ pub struct EthereumBlobExporter< EthereumNetwork, OutboundQueue, AgentHashedDescription, ->(PhantomData<(UniversalLocation, EthereumNetwork, OutboundQueue, AgentHashedDescription)>); - -impl ExportXcm - for EthereumBlobExporter + ConvertAssetId, +>( + PhantomData<( + UniversalLocation, + EthereumNetwork, + OutboundQueue, + AgentHashedDescription, + ConvertAssetId, + )>, +); + +impl + ExportXcm + for EthereumBlobExporter< + UniversalLocation, + EthereumNetwork, + OutboundQueue, + AgentHashedDescription, + ConvertAssetId, + > where UniversalLocation: Get, EthereumNetwork: Get, OutboundQueue: SendMessage, AgentHashedDescription: ConvertLocation, + ConvertAssetId: MaybeEquivalence, { type Ticket = (Vec, XcmHash); @@ -51,13 +69,15 @@ where return Err(SendError::NotApplicable) } - let dest = destination.take().ok_or(SendError::MissingArgument)?; + // Cloning destination to avoid modifying the value so subsequent exporters can use it. + let dest = destination.clone().take().ok_or(SendError::MissingArgument)?; if dest != Here { log::trace!(target: "xcm::ethereum_blob_exporter", "skipped due to unmatched remote destination {dest:?}."); return Err(SendError::NotApplicable) } - let (local_net, local_sub) = universal_source + // Cloning universal_source to avoid modifying the value so subsequent exporters can use it. + let (local_net, local_sub) = universal_source.clone() .take() .ok_or_else(|| { log::error!(target: "xcm::ethereum_blob_exporter", "universal source not provided."); @@ -66,7 +86,7 @@ where .split_global() .map_err(|()| { log::error!(target: "xcm::ethereum_blob_exporter", "could not get global consensus from universal source '{universal_source:?}'."); - SendError::Unroutable + SendError::NotApplicable })?; if Ok(local_net) != universal_location.global_consensus() { @@ -78,7 +98,17 @@ where [Parachain(para_id)] => *para_id, _ => { log::error!(target: "xcm::ethereum_blob_exporter", "could not get parachain id from universal source '{local_sub:?}'."); - return Err(SendError::MissingArgument) + return Err(SendError::NotApplicable) + }, + }; + + let source_location = Location::new(1, local_sub.clone()); + + let agent_id = match AgentHashedDescription::convert_location(&source_location) { + Some(id) => id, + None => { + log::error!(target: "xcm::ethereum_blob_exporter", "unroutable due to not being able to create agent id. '{source_location:?}'"); + return Err(SendError::NotApplicable) }, }; @@ -87,28 +117,16 @@ where SendError::MissingArgument })?; - let mut converter = XcmConverter::new(&message, &expected_network); - let (agent_execute_command, message_id) = converter.convert().map_err(|err|{ + let mut converter = + XcmConverter::::new(&message, expected_network, agent_id); + let (command, message_id) = converter.convert().map_err(|err|{ log::error!(target: "xcm::ethereum_blob_exporter", "unroutable due to pattern matching error '{err:?}'."); SendError::Unroutable })?; - let source_location = Location::new(1, local_sub.clone()); - let agent_id = match AgentHashedDescription::convert_location(&source_location) { - Some(id) => id, - None => { - log::error!(target: "xcm::ethereum_blob_exporter", "unroutable due to not being able to create agent id. '{source_location:?}'"); - return Err(SendError::Unroutable) - }, - }; - let channel_id: ChannelId = ParaId::from(para_id).into(); - let outbound_message = Message { - id: Some(message_id.into()), - channel_id, - command: Command::AgentExecute { agent_id, command: agent_execute_command }, - }; + let outbound_message = Message { id: Some(message_id.into()), channel_id, command }; // validate the message let (ticket, fee) = OutboundQueue::validate(&outbound_message).map_err(|err| { @@ -154,6 +172,9 @@ enum XcmConverterError { AssetResolutionFailed, InvalidFeeAsset, SetTopicExpected, + ReserveAssetDepositedExpected, + InvalidAsset, + UnexpectedInstruction, } macro_rules! match_expression { @@ -165,18 +186,33 @@ macro_rules! match_expression { }; } -struct XcmConverter<'a, Call> { +struct XcmConverter<'a, ConvertAssetId, Call> { iter: Peekable>>, - ethereum_network: &'a NetworkId, + ethereum_network: NetworkId, + agent_id: AgentId, + _marker: PhantomData, } -impl<'a, Call> XcmConverter<'a, Call> { - fn new(message: &'a Xcm, ethereum_network: &'a NetworkId) -> Self { - Self { iter: message.inner().iter().peekable(), ethereum_network } +impl<'a, ConvertAssetId, Call> XcmConverter<'a, ConvertAssetId, Call> +where + ConvertAssetId: MaybeEquivalence, +{ + fn new(message: &'a Xcm, ethereum_network: NetworkId, agent_id: AgentId) -> Self { + Self { + iter: message.inner().iter().peekable(), + ethereum_network, + agent_id, + _marker: Default::default(), + } } - fn convert(&mut self) -> Result<(AgentExecuteCommand, [u8; 32]), XcmConverterError> { - // Get withdraw/deposit and make native tokens create message. - let result = self.native_tokens_unlock_message()?; + fn convert(&mut self) -> Result<(Command, [u8; 32]), XcmConverterError> { + let result = match self.peek() { + Ok(ReserveAssetDeposited { .. }) => self.send_native_tokens_message(), + // Get withdraw/deposit and make native tokens create message. + Ok(WithdrawAsset { .. }) => self.send_tokens_message(), + Err(e) => Err(e), + _ => return Err(XcmConverterError::UnexpectedInstruction), + }?; // All xcm instructions must be consumed before exit. if self.next().is_ok() { @@ -186,9 +222,7 @@ impl<'a, Call> XcmConverter<'a, Call> { Ok(result) } - fn native_tokens_unlock_message( - &mut self, - ) -> Result<(AgentExecuteCommand, [u8; 32]), XcmConverterError> { + fn send_tokens_message(&mut self) -> Result<(Command, [u8; 32]), XcmConverterError> { use XcmConverterError::*; // Get the reserve assets from WithdrawAsset. @@ -262,7 +296,13 @@ impl<'a, Call> XcmConverter<'a, Call> { // Check if there is a SetTopic and skip over it if found. let topic_id = match_expression!(self.next()?, SetTopic(id), id).ok_or(SetTopicExpected)?; - Ok((AgentExecuteCommand::TransferToken { token, recipient, amount }, *topic_id)) + Ok(( + Command::AgentExecute { + agent_id: self.agent_id, + command: AgentExecuteCommand::TransferToken { token, recipient, amount }, + }, + *topic_id, + )) } fn next(&mut self) -> Result<&'a Instruction, XcmConverterError> { @@ -275,9 +315,95 @@ impl<'a, Call> XcmConverter<'a, Call> { fn network_matches(&self, network: &Option) -> bool { if let Some(network) = network { - network == self.ethereum_network + *network == self.ethereum_network } else { true } } + + /// Convert the xcm for Polkadot-native token from AH into the Command + /// To match transfers of Polkadot-native tokens, we expect an input of the form: + /// # ReserveAssetDeposited + /// # ClearOrigin + /// # BuyExecution + /// # DepositAsset + /// # SetTopic + fn send_native_tokens_message(&mut self) -> Result<(Command, [u8; 32]), XcmConverterError> { + use XcmConverterError::*; + + // Get the reserve assets. + let reserve_assets = + match_expression!(self.next()?, ReserveAssetDeposited(reserve_assets), reserve_assets) + .ok_or(ReserveAssetDepositedExpected)?; + + // Check if clear origin exists and skip over it. + if match_expression!(self.peek(), Ok(ClearOrigin), ()).is_some() { + let _ = self.next(); + } + + // Get the fee asset item from BuyExecution or continue parsing. + let fee_asset = match_expression!(self.peek(), Ok(BuyExecution { fees, .. }), fees); + if fee_asset.is_some() { + let _ = self.next(); + } + + let (deposit_assets, beneficiary) = match_expression!( + self.next()?, + DepositAsset { assets, beneficiary }, + (assets, beneficiary) + ) + .ok_or(DepositAssetExpected)?; + + // assert that the beneficiary is AccountKey20. + let recipient = match_expression!( + beneficiary.unpack(), + (0, [AccountKey20 { network, key }]) + if self.network_matches(network), + H160(*key) + ) + .ok_or(BeneficiaryResolutionFailed)?; + + // Make sure there are reserved assets. + if reserve_assets.len() == 0 { + return Err(NoReserveAssets) + } + + // Check the the deposit asset filter matches what was reserved. + if reserve_assets.inner().iter().any(|asset| !deposit_assets.matches(asset)) { + return Err(FilterDoesNotConsumeAllAssets) + } + + // We only support a single asset at a time. + ensure!(reserve_assets.len() == 1, TooManyAssets); + let reserve_asset = reserve_assets.get(0).ok_or(AssetResolutionFailed)?; + + // If there was a fee specified verify it. + if let Some(fee_asset) = fee_asset { + // The fee asset must be the same as the reserve asset. + if fee_asset.id != reserve_asset.id || fee_asset.fun > reserve_asset.fun { + return Err(InvalidFeeAsset) + } + } + + let (asset_id, amount) = match reserve_asset { + Asset { id: AssetId(inner_location), fun: Fungible(amount) } => + Some((inner_location.clone(), *amount)), + _ => None, + } + .ok_or(AssetResolutionFailed)?; + + // transfer amount must be greater than 0. + ensure!(amount > 0, ZeroAssetTransfer); + + let token_id = TokenIdOf::convert_location(&asset_id).ok_or(InvalidAsset)?; + + let expected_asset_id = ConvertAssetId::convert(&token_id).ok_or(InvalidAsset)?; + + ensure!(asset_id == expected_asset_id, InvalidAsset); + + // Check if there is a SetTopic and skip over it if found. + let topic_id = match_expression!(self.next()?, SetTopic(id), id).ok_or(SetTopicExpected)?; + + Ok((Command::MintForeignToken { token_id, recipient, amount }, *topic_id)) + } } diff --git a/bridges/snowbridge/primitives/router/src/outbound/tests.rs b/bridges/snowbridge/primitives/router/src/outbound/tests.rs index 111243bb45a7ee8e9e06438e1d4eaffb16573d7c..8bd3fa24df5bff9290b4a7a87afaf90f1947d2f8 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/tests.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/tests.rs @@ -4,7 +4,8 @@ use snowbridge_core::{ outbound::{Fee, SendError, SendMessageFeeProvider}, AgentIdOf, }; -use xcm::v3::prelude::SendError as XcmSendError; +use sp_std::default::Default; +use xcm::prelude::SendError as XcmSendError; use super::*; @@ -57,6 +58,16 @@ impl SendMessageFeeProvider for MockErrOutboundQueue { } } +pub struct MockTokenIdConvert; +impl MaybeEquivalence for MockTokenIdConvert { + fn convert(_id: &TokenId) -> Option { + Some(Location::new(1, [GlobalConsensus(Westend)])) + } + fn convert_back(_loc: &Location) -> Option { + None + } +} + #[test] fn exporter_validate_with_unknown_network_yields_not_applicable() { let network = Ethereum { chain_id: 1337 }; @@ -65,14 +76,14 @@ fn exporter_validate_with_unknown_network_yields_not_applicable() { let mut destination: Option = None; let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::NotApplicable)); } @@ -84,14 +95,14 @@ fn exporter_validate_with_invalid_destination_yields_missing_argument() { let mut destination: Option = None; let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::MissingArgument)); } @@ -106,14 +117,14 @@ fn exporter_validate_with_x8_destination_yields_not_applicable() { ); let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::NotApplicable)); } @@ -125,34 +136,34 @@ fn exporter_validate_without_universal_source_yields_missing_argument() { let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::MissingArgument)); } #[test] -fn exporter_validate_without_global_universal_location_yields_unroutable() { +fn exporter_validate_without_global_universal_location_yields_not_applicable() { let network = BridgedNetwork::get(); let channel: u32 = 0; let mut universal_source: Option = Here.into(); let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); - assert_eq!(result, Err(XcmSendError::Unroutable)); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); + assert_eq!(result, Err(XcmSendError::NotApplicable)); } #[test] @@ -163,14 +174,14 @@ fn exporter_validate_without_global_bridge_location_yields_not_applicable() { let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::NotApplicable)); } @@ -183,38 +194,38 @@ fn exporter_validate_with_remote_universal_source_yields_not_applicable() { let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::NotApplicable)); } #[test] -fn exporter_validate_without_para_id_in_source_yields_missing_argument() { +fn exporter_validate_without_para_id_in_source_yields_not_applicable() { let network = BridgedNetwork::get(); let channel: u32 = 0; let mut universal_source: Option = Some(GlobalConsensus(Polkadot).into()); let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); - assert_eq!(result, Err(XcmSendError::MissingArgument)); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); + assert_eq!(result, Err(XcmSendError::NotApplicable)); } #[test] -fn exporter_validate_complex_para_id_in_source_yields_missing_argument() { +fn exporter_validate_complex_para_id_in_source_yields_not_applicable() { let network = BridgedNetwork::get(); let channel: u32 = 0; let mut universal_source: Option = @@ -222,15 +233,15 @@ fn exporter_validate_complex_para_id_in_source_yields_missing_argument() { let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); - assert_eq!(result, Err(XcmSendError::MissingArgument)); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); + assert_eq!(result, Err(XcmSendError::NotApplicable)); } #[test] @@ -242,14 +253,14 @@ fn exporter_validate_without_xcm_message_yields_missing_argument() { let mut destination: Option = Here.into(); let mut message: Option> = None; - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::MissingArgument)); } @@ -289,14 +300,14 @@ fn exporter_validate_with_max_target_fee_yields_unroutable() { .into(), ); - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::Unroutable)); } @@ -316,14 +327,14 @@ fn exporter_validate_with_unparsable_xcm_yields_unroutable() { let mut message: Option> = Some(vec![WithdrawAsset(fees), BuyExecution { fees: fee, weight_limit: Unlimited }].into()); - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert_eq!(result, Err(XcmSendError::Unroutable)); } @@ -362,14 +373,14 @@ fn exporter_validate_xcm_success_case_1() { .into(), ); - let result = EthereumBlobExporter::< - UniversalLocation, - BridgedNetwork, - MockOkOutboundQueue, - AgentIdOf, - >::validate( - network, channel, &mut universal_source, &mut destination, &mut message - ); + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate(network, channel, &mut universal_source, &mut destination, &mut message); assert!(result.is_ok()); } @@ -381,6 +392,7 @@ fn exporter_deliver_with_submit_failure_yields_unroutable() { BridgedNetwork, MockErrOutboundQueue, AgentIdOf, + MockTokenIdConvert, >::deliver((hex!("deadbeef").to_vec(), XcmHash::default())); assert_eq!(result, Err(XcmSendError::Transport("other transport error"))) } @@ -410,11 +422,15 @@ fn xcm_converter_convert_success() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); - let expected_payload = AgentExecuteCommand::TransferToken { - token: token_address.into(), - recipient: beneficiary_address.into(), - amount: 1000, + let mut converter = + XcmConverter::::new(&message, network, Default::default()); + let expected_payload = Command::AgentExecute { + agent_id: Default::default(), + command: AgentExecuteCommand::TransferToken { + token: token_address.into(), + recipient: beneficiary_address.into(), + amount: 1000, + }, }; let result = converter.convert(); assert_eq!(result, Ok((expected_payload, [0; 32]))); @@ -443,11 +459,15 @@ fn xcm_converter_convert_without_buy_execution_yields_success() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); - let expected_payload = AgentExecuteCommand::TransferToken { - token: token_address.into(), - recipient: beneficiary_address.into(), - amount: 1000, + let mut converter = + XcmConverter::::new(&message, network, Default::default()); + let expected_payload = Command::AgentExecute { + agent_id: Default::default(), + command: AgentExecuteCommand::TransferToken { + token: token_address.into(), + recipient: beneficiary_address.into(), + amount: 1000, + }, }; let result = converter.convert(); assert_eq!(result, Ok((expected_payload, [0; 32]))); @@ -478,11 +498,15 @@ fn xcm_converter_convert_with_wildcard_all_asset_filter_succeeds() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); - let expected_payload = AgentExecuteCommand::TransferToken { - token: token_address.into(), - recipient: beneficiary_address.into(), - amount: 1000, + let mut converter = + XcmConverter::::new(&message, network, Default::default()); + let expected_payload = Command::AgentExecute { + agent_id: Default::default(), + command: AgentExecuteCommand::TransferToken { + token: token_address.into(), + recipient: beneficiary_address.into(), + amount: 1000, + }, }; let result = converter.convert(); assert_eq!(result, Ok((expected_payload, [0; 32]))); @@ -513,11 +537,15 @@ fn xcm_converter_convert_with_fees_less_than_reserve_yields_success() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); - let expected_payload = AgentExecuteCommand::TransferToken { - token: token_address.into(), - recipient: beneficiary_address.into(), - amount: 1000, + let mut converter = + XcmConverter::::new(&message, network, Default::default()); + let expected_payload = Command::AgentExecute { + agent_id: Default::default(), + command: AgentExecuteCommand::TransferToken { + token: token_address.into(), + recipient: beneficiary_address.into(), + amount: 1000, + }, }; let result = converter.convert(); assert_eq!(result, Ok((expected_payload, [0; 32]))); @@ -547,7 +575,8 @@ fn xcm_converter_convert_without_set_topic_yields_set_topic_expected() { ClearTopic, ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::SetTopicExpected)); } @@ -564,7 +593,8 @@ fn xcm_converter_convert_with_partial_message_yields_unexpected_end_of_xcm() { .into(); let message: Xcm<()> = vec![WithdrawAsset(assets)].into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::UnexpectedEndOfXcm)); } @@ -595,7 +625,8 @@ fn xcm_converter_with_different_fee_asset_fails() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::InvalidFeeAsset)); } @@ -625,7 +656,8 @@ fn xcm_converter_with_fees_greater_than_reserve_fails() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::InvalidFeeAsset)); } @@ -636,7 +668,8 @@ fn xcm_converter_convert_with_empty_xcm_yields_unexpected_end_of_xcm() { let message: Xcm<()> = vec![].into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::UnexpectedEndOfXcm)); @@ -668,7 +701,8 @@ fn xcm_converter_convert_with_extra_instructions_yields_end_of_xcm_message_expec ClearError, ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::EndOfXcmMessageExpected)); @@ -698,10 +732,11 @@ fn xcm_converter_convert_without_withdraw_asset_yields_withdraw_expected() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); - assert_eq!(result.err(), Some(XcmConverterError::WithdrawAssetExpected)); + assert_eq!(result.err(), Some(XcmConverterError::UnexpectedInstruction)); } #[test] @@ -723,7 +758,8 @@ fn xcm_converter_convert_without_withdraw_asset_yields_deposit_expected() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::DepositAssetExpected)); @@ -756,7 +792,8 @@ fn xcm_converter_convert_without_assets_yields_no_reserve_assets() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::NoReserveAssets)); @@ -794,7 +831,8 @@ fn xcm_converter_convert_with_two_assets_yields_too_many_assets() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::TooManyAssets)); @@ -825,7 +863,8 @@ fn xcm_converter_convert_without_consuming_filter_yields_filter_does_not_consume SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::FilterDoesNotConsumeAllAssets)); @@ -856,7 +895,8 @@ fn xcm_converter_convert_with_zero_amount_asset_yields_zero_asset_transfer() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::ZeroAssetTransfer)); @@ -886,7 +926,8 @@ fn xcm_converter_convert_non_ethereum_asset_yields_asset_resolution_failed() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::AssetResolutionFailed)); @@ -919,7 +960,8 @@ fn xcm_converter_convert_non_ethereum_chain_asset_yields_asset_resolution_failed SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::AssetResolutionFailed)); @@ -952,7 +994,8 @@ fn xcm_converter_convert_non_ethereum_chain_yields_asset_resolution_failed() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::AssetResolutionFailed)); @@ -989,7 +1032,8 @@ fn xcm_converter_convert_with_non_ethereum_beneficiary_yields_beneficiary_resolu SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::BeneficiaryResolutionFailed)); @@ -1025,7 +1069,8 @@ fn xcm_converter_convert_with_non_ethereum_chain_beneficiary_yields_beneficiary_ SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::BeneficiaryResolutionFailed)); @@ -1056,3 +1101,169 @@ fn test_describe_here() { hex!("03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").into() ) } + +#[test] +fn xcm_converter_transfer_native_token_success() { + let network = BridgedNetwork::get(); + + let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); + + let amount = 1000000; + let asset_location = Location::new(1, [GlobalConsensus(Westend)]); + let token_id = TokenIdOf::convert_location(&asset_location).unwrap(); + + let assets: Assets = vec![Asset { id: AssetId(asset_location), fun: Fungible(amount) }].into(); + let filter: AssetFilter = assets.clone().into(); + + let message: Xcm<()> = vec![ + ReserveAssetDeposited(assets.clone()), + ClearOrigin, + BuyExecution { fees: assets.get(0).unwrap().clone(), weight_limit: Unlimited }, + DepositAsset { + assets: filter, + beneficiary: AccountKey20 { network: None, key: beneficiary_address }.into(), + }, + SetTopic([0; 32]), + ] + .into(); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); + let expected_payload = + Command::MintForeignToken { recipient: beneficiary_address.into(), amount, token_id }; + let result = converter.convert(); + assert_eq!(result, Ok((expected_payload, [0; 32]))); +} + +#[test] +fn xcm_converter_transfer_native_token_with_invalid_location_will_fail() { + let network = BridgedNetwork::get(); + + let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); + + let amount = 1000000; + // Invalid asset location from a different consensus + let asset_location = Location { parents: 2, interior: [GlobalConsensus(Rococo)].into() }; + + let assets: Assets = vec![Asset { id: AssetId(asset_location), fun: Fungible(amount) }].into(); + let filter: AssetFilter = assets.clone().into(); + + let message: Xcm<()> = vec![ + ReserveAssetDeposited(assets.clone()), + ClearOrigin, + BuyExecution { fees: assets.get(0).unwrap().clone(), weight_limit: Unlimited }, + DepositAsset { + assets: filter, + beneficiary: AccountKey20 { network: None, key: beneficiary_address }.into(), + }, + SetTopic([0; 32]), + ] + .into(); + let mut converter = + XcmConverter::::new(&message, network, Default::default()); + let result = converter.convert(); + assert_eq!(result.err(), Some(XcmConverterError::InvalidAsset)); +} + +#[test] +fn exporter_validate_with_invalid_dest_does_not_alter_destination() { + let network = BridgedNetwork::get(); + let destination: InteriorLocation = Parachain(1000).into(); + + let universal_source: InteriorLocation = [GlobalConsensus(Polkadot), Parachain(1000)].into(); + + let token_address: [u8; 20] = hex!("1000000000000000000000000000000000000000"); + let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); + + let channel: u32 = 0; + let assets: Assets = vec![Asset { + id: AssetId([AccountKey20 { network: None, key: token_address }].into()), + fun: Fungible(1000), + }] + .into(); + let fee = assets.clone().get(0).unwrap().clone(); + let filter: AssetFilter = assets.clone().into(); + let msg: Xcm<()> = vec![ + WithdrawAsset(assets.clone()), + ClearOrigin, + BuyExecution { fees: fee, weight_limit: Unlimited }, + DepositAsset { + assets: filter, + beneficiary: AccountKey20 { network: None, key: beneficiary_address }.into(), + }, + SetTopic([0; 32]), + ] + .into(); + let mut msg_wrapper: Option> = Some(msg.clone()); + let mut dest_wrapper = Some(destination.clone()); + let mut universal_source_wrapper = Some(universal_source.clone()); + + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate( + network, channel, &mut universal_source_wrapper, &mut dest_wrapper, &mut msg_wrapper + ); + + assert_eq!(result, Err(XcmSendError::NotApplicable)); + + // ensure mutable variables are not changed + assert_eq!(Some(destination), dest_wrapper); + assert_eq!(Some(msg), msg_wrapper); + assert_eq!(Some(universal_source), universal_source_wrapper); +} + +#[test] +fn exporter_validate_with_invalid_universal_source_does_not_alter_universal_source() { + let network = BridgedNetwork::get(); + let destination: InteriorLocation = Here.into(); + + let universal_source: InteriorLocation = [GlobalConsensus(Westend), Parachain(1000)].into(); + + let token_address: [u8; 20] = hex!("1000000000000000000000000000000000000000"); + let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); + + let channel: u32 = 0; + let assets: Assets = vec![Asset { + id: AssetId([AccountKey20 { network: None, key: token_address }].into()), + fun: Fungible(1000), + }] + .into(); + let fee = assets.clone().get(0).unwrap().clone(); + let filter: AssetFilter = assets.clone().into(); + let msg: Xcm<()> = vec![ + WithdrawAsset(assets.clone()), + ClearOrigin, + BuyExecution { fees: fee, weight_limit: Unlimited }, + DepositAsset { + assets: filter, + beneficiary: AccountKey20 { network: None, key: beneficiary_address }.into(), + }, + SetTopic([0; 32]), + ] + .into(); + let mut msg_wrapper: Option> = Some(msg.clone()); + let mut dest_wrapper = Some(destination.clone()); + let mut universal_source_wrapper = Some(universal_source.clone()); + + let result = + EthereumBlobExporter::< + UniversalLocation, + BridgedNetwork, + MockOkOutboundQueue, + AgentIdOf, + MockTokenIdConvert, + >::validate( + network, channel, &mut universal_source_wrapper, &mut dest_wrapper, &mut msg_wrapper + ); + + assert_eq!(result, Err(XcmSendError::NotApplicable)); + + // ensure mutable variables are not changed + assert_eq!(Some(destination), dest_wrapper); + assert_eq!(Some(msg), msg_wrapper); + assert_eq!(Some(universal_source), universal_source_wrapper); +} diff --git a/bridges/snowbridge/runtime/runtime-common/src/lib.rs b/bridges/snowbridge/runtime/runtime-common/src/lib.rs index aae45520ff4bd84545cef260dc363c4fd6053ea3..0b1a74b232a0c0ca909f53674efb3a130ea1d6a6 100644 --- a/bridges/snowbridge/runtime/runtime-common/src/lib.rs +++ b/bridges/snowbridge/runtime/runtime-common/src/lib.rs @@ -50,7 +50,8 @@ impl where + > +where Balance: BaseArithmetic + Unsigned + Copy + From + Into + Debug, AccountId: Clone + FullCodec, FeeAssetLocation: Get, diff --git a/bridges/snowbridge/runtime/test-common/src/lib.rs b/bridges/snowbridge/runtime/test-common/src/lib.rs index 8f36313e360f1a41d85b54b9796e62a45f42e9b4..b157ad4356bdf5769119dd52b0fe027d72fab2e1 100644 --- a/bridges/snowbridge/runtime/test-common/src/lib.rs +++ b/bridges/snowbridge/runtime/test-common/src/lib.rs @@ -12,7 +12,7 @@ use parachains_runtimes_test_utils::{ }; use snowbridge_core::{ChannelId, ParaId}; use snowbridge_pallet_ethereum_client_fixtures::*; -use sp_core::{H160, U256}; +use sp_core::{Get, H160, U256}; use sp_keyring::AccountKeyring::*; use sp_runtime::{traits::Header, AccountId32, DigestItem, SaturatedConversion, Saturating}; use xcm::{ @@ -466,23 +466,37 @@ pub fn ethereum_extrinsic( let initial_checkpoint = make_checkpoint(); let update = make_finalized_header_update(); let sync_committee_update = make_sync_committee_update(); + let mut invalid_update = make_finalized_header_update(); + let mut invalid_sync_committee_update = make_sync_committee_update(); + invalid_update.finalized_header.slot = 4354; + invalid_sync_committee_update.finalized_header.slot = 4354; let alice = Alice; let alice_account = alice.to_account_id(); >::mint_into( - &alice_account.into(), + &alice_account.clone().into(), 10_000_000_000_000_u128.saturated_into::>(), ) .unwrap(); + let balance_before = + >::free_balance(&alice_account.clone().into()); assert_ok!(>::force_checkpoint( RuntimeHelper::::root_origin(), - initial_checkpoint, + initial_checkpoint.clone(), )); + let balance_after_checkpoint = + >::free_balance(&alice_account.clone().into()); let update_call: ::RuntimeCall = snowbridge_pallet_ethereum_client::Call::::submit { - update: Box::new(*update), + update: Box::new(*update.clone()), + } + .into(); + + let invalid_update_call: ::RuntimeCall = + snowbridge_pallet_ethereum_client::Call::::submit { + update: Box::new(*invalid_update), } .into(); @@ -492,12 +506,63 @@ pub fn ethereum_extrinsic( } .into(); + let invalid_update_sync_committee_call: ::RuntimeCall = + snowbridge_pallet_ethereum_client::Call::::submit { + update: Box::new(*invalid_sync_committee_update), + } + .into(); + + // Finalized header update let update_outcome = construct_and_apply_extrinsic(alice, update_call.into()); assert_ok!(update_outcome); + let balance_after_update = + >::free_balance(&alice_account.clone().into()); + + // Invalid finalized header update + let invalid_update_outcome = + construct_and_apply_extrinsic(alice, invalid_update_call.into()); + assert_err!( + invalid_update_outcome, + snowbridge_pallet_ethereum_client::Error::::InvalidUpdateSlot + ); + let balance_after_invalid_update = + >::free_balance(&alice_account.clone().into()); + // Sync committee update let sync_committee_outcome = construct_and_apply_extrinsic(alice, update_sync_committee_call.into()); assert_ok!(sync_committee_outcome); + let balance_after_sync_com_update = + >::free_balance(&alice_account.clone().into()); + + // Invalid sync committee update + let invalid_sync_committee_outcome = + construct_and_apply_extrinsic(alice, invalid_update_sync_committee_call.into()); + assert_err!( + invalid_sync_committee_outcome, + snowbridge_pallet_ethereum_client::Error::::InvalidUpdateSlot + ); + let balance_after_invalid_sync_com_update = + >::free_balance(&alice_account.clone().into()); + + // Assert paid operations are charged and free operations are free + // Checkpoint is a free operation + assert!(balance_before == balance_after_checkpoint); + let gap = + ::FreeHeadersInterval::get(); + // Large enough header gap is free + if update.finalized_header.slot >= initial_checkpoint.header.slot + gap as u64 { + assert!(balance_after_checkpoint == balance_after_update); + } else { + // Otherwise paid + assert!(balance_after_checkpoint > balance_after_update); + } + // An invalid update is paid + assert!(balance_after_update > balance_after_invalid_update); + // A successful sync committee update is free + assert!(balance_after_invalid_update == balance_after_sync_com_update); + // An invalid sync committee update is paid + assert!(balance_after_sync_com_update > balance_after_invalid_sync_com_update); }); } diff --git a/bridges/testing/README.md b/bridges/testing/README.md index 158dfd73b1ad8d6b67891b661bc215c290017cd0..89a07c421e3e279b04ef3813d35a9db72cdc3576 100644 --- a/bridges/testing/README.md +++ b/bridges/testing/README.md @@ -22,7 +22,7 @@ Prerequisites for running the tests locally: - copy the `substrate-relay` binary, built in the previous step, to `~/local_bridge_testing/bin/substrate-relay`; After that, any test can be run using the `run-test.sh` command. -Example: `./run-new-test.sh 0001-asset-transfer` +Example: `./run-test.sh 0001-asset-transfer` Hopefully, it'll show the "All tests have completed successfully" message in the end. Otherwise, it'll print paths to zombienet diff --git a/bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh b/bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh index 54633449134b4ce85ff124d034cd6f58061409a1..e7848fe7163c7419d01e76b1cd5c60f271b6ae98 100755 --- a/bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh +++ b/bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh @@ -53,66 +53,66 @@ ASSET_HUB_ROCOCO_SOVEREIGN_ACCOUNT_AT_BRIDGE_HUB_ROCOCO="5Eg2fntNprdN3FgH4sfEaaZ # Expected sovereign accounts for rewards on BridgeHubs. # # Generated by: -# #[test] -# fn generate_sovereign_accounts_for_rewards() { -# use bp_messages::LaneId; -# use bp_relayers::{PayRewardFromAccount, RewardsAccountOwner, RewardsAccountParams}; -# use sp_core::crypto::Ss58Codec; +##[test] +#fn generate_sovereign_accounts_for_rewards() { +# use bp_messages::LegacyLaneId; +# use bp_relayers::{PayRewardFromAccount, RewardsAccountOwner, RewardsAccountParams}; +# use sp_core::crypto::Ss58Codec; # -# // SS58=42 -# println!( -# "ON_BRIDGE_HUB_ROCOCO_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhwd_ThisChain=\"{}\"", -# frame_support::sp_runtime::AccountId32::new( -# PayRewardFromAccount::<[u8; 32], [u8; 32]>::rewards_account(RewardsAccountParams::new( -# LaneId([0, 0, 0, 2]), -# *b"bhwd", -# RewardsAccountOwner::ThisChain -# )) -# ) -# .to_ss58check_with_version(42_u16.into()) -# ); -# // SS58=42 -# println!( -# "ON_BRIDGE_HUB_ROCOCO_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhwd_BridgedChain=\"{}\"", -# frame_support::sp_runtime::AccountId32::new( -# PayRewardFromAccount::<[u8; 32], [u8; 32]>::rewards_account(RewardsAccountParams::new( -# LaneId([0, 0, 0, 2]), -# *b"bhwd", -# RewardsAccountOwner::BridgedChain -# )) -# ) -# .to_ss58check_with_version(42_u16.into()) -# ); +# // SS58=42 +# println!( +# "ON_BRIDGE_HUB_ROCOCO_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhwd_ThisChain=\"{}\"", +# frame_support::sp_runtime::AccountId32::new( +# PayRewardFromAccount::<[u8; 32], [u8; 32], LegacyLaneId>::rewards_account(RewardsAccountParams::new( +# LegacyLaneId([0, 0, 0, 2]), +# *b"bhwd", +# RewardsAccountOwner::ThisChain +# )) +# ) +# .to_ss58check_with_version(42_u16.into()) +# ); +# // SS58=42 +# println!( +# "ON_BRIDGE_HUB_ROCOCO_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhwd_BridgedChain=\"{}\"", +# frame_support::sp_runtime::AccountId32::new( +# PayRewardFromAccount::<[u8; 32], [u8; 32], LegacyLaneId>::rewards_account(RewardsAccountParams::new( +# LegacyLaneId([0, 0, 0, 2]), +# *b"bhwd", +# RewardsAccountOwner::BridgedChain +# )) +# ) +# .to_ss58check_with_version(42_u16.into()) +# ); # -# // SS58=42 -# println!( -# "ON_BRIDGE_HUB_WESTEND_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhro_ThisChain=\"{}\"", -# frame_support::sp_runtime::AccountId32::new( -# PayRewardFromAccount::<[u8; 32], [u8; 32]>::rewards_account(RewardsAccountParams::new( -# LaneId([0, 0, 0, 2]), -# *b"bhro", -# RewardsAccountOwner::ThisChain -# )) -# ) -# .to_ss58check_with_version(42_u16.into()) -# ); -# // SS58=42 -# println!( -# "ON_BRIDGE_HUB_WESTEND_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhro_BridgedChain=\"{}\"", -# frame_support::sp_runtime::AccountId32::new( -# PayRewardFromAccount::<[u8; 32], [u8; 32]>::rewards_account(RewardsAccountParams::new( -# LaneId([0, 0, 0, 2]), -# *b"bhro", -# RewardsAccountOwner::BridgedChain -# )) -# ) -# .to_ss58check_with_version(42_u16.into()) -# ); -# } -ON_BRIDGE_HUB_ROCOCO_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhwd_ThisChain="5EHnXaT5BhiSGP5hbdsoVGtzi2sQVgpDNToTxLYeQvKoMPEm" -ON_BRIDGE_HUB_ROCOCO_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhwd_BridgedChain="5EHnXaT5BhiSGP5hbdt5EJSapXYbxEv678jyWHEUskCXcjqo" -ON_BRIDGE_HUB_WESTEND_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhro_ThisChain="5EHnXaT5BhiSGP5h9Rg8sgUJqoLym3iEaWUiboT8S9AT5xFh" -ON_BRIDGE_HUB_WESTEND_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhro_BridgedChain="5EHnXaT5BhiSGP5h9RgQci1txJ2BDbp7KBRE9k8xty3BMUSi" +# // SS58=42 +# println!( +# "ON_BRIDGE_HUB_WESTEND_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhro_ThisChain=\"{}\"", +# frame_support::sp_runtime::AccountId32::new( +# PayRewardFromAccount::<[u8; 32], [u8; 32], LegacyLaneId>::rewards_account(RewardsAccountParams::new( +# LegacyLaneId([0, 0, 0, 2]), +# *b"bhro", +# RewardsAccountOwner::ThisChain +# )) +# ) +# .to_ss58check_with_version(42_u16.into()) +# ); +# // SS58=42 +# println!( +# "ON_BRIDGE_HUB_WESTEND_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhro_BridgedChain=\"{}\"", +# frame_support::sp_runtime::AccountId32::new( +# PayRewardFromAccount::<[u8; 32], [u8; 32], LegacyLaneId>::rewards_account(RewardsAccountParams::new( +# LegacyLaneId([0, 0, 0, 2]), +# *b"bhro", +# RewardsAccountOwner::BridgedChain +# )) +# ) +# .to_ss58check_with_version(42_u16.into()) +# ); +#} +ON_BRIDGE_HUB_ROCOCO_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhwd_ThisChain="5EHnXaT5GApse1euZWj9hycMbgjKBCNQL9WEwScL8QDx6mhK" +ON_BRIDGE_HUB_ROCOCO_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhwd_BridgedChain="5EHnXaT5Tnt4A8aiP9CsuAFRhKPjKZJXRrj4a3mtihFvKpTi" +ON_BRIDGE_HUB_WESTEND_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhro_ThisChain="5EHnXaT5GApry9tS6yd1FVusPq8o8bQJGCKyvXTFCoEKk5Z9" +ON_BRIDGE_HUB_WESTEND_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhro_BridgedChain="5EHnXaT5Tnt3VGpEvc6jSgYwVToDGxLRMuYoZ8coo6GHyWbR" LANE_ID="00000002" XCM_VERSION=3 diff --git a/bridges/testing/framework/utils/generate_hex_encoded_call/package-lock.json b/bridges/testing/framework/utils/generate_hex_encoded_call/package-lock.json deleted file mode 100644 index ca3abcc528cfaabc19cebf47426ef62f4ea5a8a6..0000000000000000000000000000000000000000 --- a/bridges/testing/framework/utils/generate_hex_encoded_call/package-lock.json +++ /dev/null @@ -1,759 +0,0 @@ -{ - "name": "y", - "version": "y", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "y", - "version": "y", - "license": "MIT", - "dependencies": { - "@polkadot/api": "^10.11", - "@polkadot/util": "^12.6" - } - }, - "node_modules/@noble/curves": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.3.0.tgz", - "integrity": "sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==", - "dependencies": { - "@noble/hashes": "1.3.3" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@noble/hashes": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz", - "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==", - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@polkadot/api": { - "version": "10.11.2", - "resolved": "https://registry.npmjs.org/@polkadot/api/-/api-10.11.2.tgz", - "integrity": "sha512-AorCZxCWCoTtdbl4DPUZh+ACe/pbLIS1BkdQY0AFJuZllm0x/yWzjgampcPd5jQAA/O3iKShRBkZqj6Mk9yG/A==", - "dependencies": { - "@polkadot/api-augment": "10.11.2", - "@polkadot/api-base": "10.11.2", - "@polkadot/api-derive": "10.11.2", - "@polkadot/keyring": "^12.6.2", - "@polkadot/rpc-augment": "10.11.2", - "@polkadot/rpc-core": "10.11.2", - "@polkadot/rpc-provider": "10.11.2", - "@polkadot/types": "10.11.2", - "@polkadot/types-augment": "10.11.2", - "@polkadot/types-codec": "10.11.2", - "@polkadot/types-create": "10.11.2", - "@polkadot/types-known": "10.11.2", - "@polkadot/util": "^12.6.2", - "@polkadot/util-crypto": "^12.6.2", - "eventemitter3": "^5.0.1", - "rxjs": "^7.8.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/api-augment": { - "version": "10.11.2", - "resolved": "https://registry.npmjs.org/@polkadot/api-augment/-/api-augment-10.11.2.tgz", - "integrity": "sha512-PTpnqpezc75qBqUtgrc0GYB8h9UHjfbHSRZamAbecIVAJ2/zc6CqtnldeaBlIu1IKTgBzi3FFtTyYu+ZGbNT2Q==", - "dependencies": { - "@polkadot/api-base": "10.11.2", - "@polkadot/rpc-augment": "10.11.2", - "@polkadot/types": "10.11.2", - "@polkadot/types-augment": "10.11.2", - "@polkadot/types-codec": "10.11.2", - "@polkadot/util": "^12.6.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/api-base": { - "version": "10.11.2", - "resolved": "https://registry.npmjs.org/@polkadot/api-base/-/api-base-10.11.2.tgz", - "integrity": "sha512-4LIjaUfO9nOzilxo7XqzYKCNMtmUypdk8oHPdrRnSjKEsnK7vDsNi+979z2KXNXd2KFSCFHENmI523fYnMnReg==", - "dependencies": { - "@polkadot/rpc-core": "10.11.2", - "@polkadot/types": "10.11.2", - "@polkadot/util": "^12.6.2", - "rxjs": "^7.8.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/api-derive": { - "version": "10.11.2", - "resolved": "https://registry.npmjs.org/@polkadot/api-derive/-/api-derive-10.11.2.tgz", - "integrity": "sha512-m3BQbPionkd1iSlknddxnL2hDtolPIsT+aRyrtn4zgMRPoLjHFmTmovvg8RaUyYofJtZeYrnjMw0mdxiSXx7eA==", - "dependencies": { - "@polkadot/api": "10.11.2", - "@polkadot/api-augment": "10.11.2", - "@polkadot/api-base": "10.11.2", - "@polkadot/rpc-core": "10.11.2", - "@polkadot/types": "10.11.2", - "@polkadot/types-codec": "10.11.2", - "@polkadot/util": "^12.6.2", - "@polkadot/util-crypto": "^12.6.2", - "rxjs": "^7.8.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/keyring": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/@polkadot/keyring/-/keyring-12.6.2.tgz", - "integrity": "sha512-O3Q7GVmRYm8q7HuB3S0+Yf/q/EB2egKRRU3fv9b3B7V+A52tKzA+vIwEmNVaD1g5FKW9oB97rmpggs0zaKFqHw==", - "dependencies": { - "@polkadot/util": "12.6.2", - "@polkadot/util-crypto": "12.6.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "12.6.2", - "@polkadot/util-crypto": "12.6.2" - } - }, - "node_modules/@polkadot/networks": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-12.6.2.tgz", - "integrity": "sha512-1oWtZm1IvPWqvMrldVH6NI2gBoCndl5GEwx7lAuQWGr7eNL+6Bdc5K3Z9T0MzFvDGoi2/CBqjX9dRKo39pDC/w==", - "dependencies": { - "@polkadot/util": "12.6.2", - "@substrate/ss58-registry": "^1.44.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/rpc-augment": { - "version": "10.11.2", - "resolved": "https://registry.npmjs.org/@polkadot/rpc-augment/-/rpc-augment-10.11.2.tgz", - "integrity": "sha512-9AhT0WW81/8jYbRcAC6PRmuxXqNhJje8OYiulBQHbG1DTCcjAfz+6VQBke9BwTStzPq7d526+yyBKD17O3zlAA==", - "dependencies": { - "@polkadot/rpc-core": "10.11.2", - "@polkadot/types": "10.11.2", - "@polkadot/types-codec": "10.11.2", - "@polkadot/util": "^12.6.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/rpc-core": { - "version": "10.11.2", - "resolved": "https://registry.npmjs.org/@polkadot/rpc-core/-/rpc-core-10.11.2.tgz", - "integrity": "sha512-Ot0CFLWx8sZhLZog20WDuniPA01Bk2StNDsdAQgcFKPwZw6ShPaZQCHuKLQK6I6DodOrem9FXX7c1hvoKJP5Ww==", - "dependencies": { - "@polkadot/rpc-augment": "10.11.2", - "@polkadot/rpc-provider": "10.11.2", - "@polkadot/types": "10.11.2", - "@polkadot/util": "^12.6.2", - "rxjs": "^7.8.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/rpc-provider": { - "version": "10.11.2", - "resolved": "https://registry.npmjs.org/@polkadot/rpc-provider/-/rpc-provider-10.11.2.tgz", - "integrity": "sha512-he5jWMpDJp7e+vUzTZDzpkB7ps3H8psRally+/ZvZZScPvFEjfczT7I1WWY9h58s8+ImeVP/lkXjL9h/gUOt3Q==", - "dependencies": { - "@polkadot/keyring": "^12.6.2", - "@polkadot/types": "10.11.2", - "@polkadot/types-support": "10.11.2", - "@polkadot/util": "^12.6.2", - "@polkadot/util-crypto": "^12.6.2", - "@polkadot/x-fetch": "^12.6.2", - "@polkadot/x-global": "^12.6.2", - "@polkadot/x-ws": "^12.6.2", - "eventemitter3": "^5.0.1", - "mock-socket": "^9.3.1", - "nock": "^13.4.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@substrate/connect": "0.7.35" - } - }, - "node_modules/@polkadot/types": { - "version": "10.11.2", - "resolved": "https://registry.npmjs.org/@polkadot/types/-/types-10.11.2.tgz", - "integrity": "sha512-d52j3xXni+C8GdYZVTSfu8ROAnzXFMlyRvXtor0PudUc8UQHOaC4+mYAkTBGA2gKdmL8MHSfRSbhcxHhsikY6Q==", - "dependencies": { - "@polkadot/keyring": "^12.6.2", - "@polkadot/types-augment": "10.11.2", - "@polkadot/types-codec": "10.11.2", - "@polkadot/types-create": "10.11.2", - "@polkadot/util": "^12.6.2", - "@polkadot/util-crypto": "^12.6.2", - "rxjs": "^7.8.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/types-augment": { - "version": "10.11.2", - "resolved": "https://registry.npmjs.org/@polkadot/types-augment/-/types-augment-10.11.2.tgz", - "integrity": "sha512-8eB8ew04wZiE5GnmFvEFW1euJWmF62SGxb1O+8wL3zoUtB9Xgo1vB6w6xbTrd+HLV6jNSeXXnbbF1BEUvi9cNg==", - "dependencies": { - "@polkadot/types": "10.11.2", - "@polkadot/types-codec": "10.11.2", - "@polkadot/util": "^12.6.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/types-codec": { - "version": "10.11.2", - "resolved": "https://registry.npmjs.org/@polkadot/types-codec/-/types-codec-10.11.2.tgz", - "integrity": "sha512-3xjOQL+LOOMzYqlgP9ROL0FQnzU8lGflgYewzau7AsDlFziSEtb49a9BpYo6zil4koC+QB8zQ9OHGFumG08T8w==", - "dependencies": { - "@polkadot/util": "^12.6.2", - "@polkadot/x-bigint": "^12.6.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/types-create": { - "version": "10.11.2", - "resolved": "https://registry.npmjs.org/@polkadot/types-create/-/types-create-10.11.2.tgz", - "integrity": "sha512-SJt23NxYvefRxVZZm6mT9ed1pR6FDoIGQ3xUpbjhTLfU2wuhpKjekMVorYQ6z/gK2JLMu2kV92Ardsz+6GX5XQ==", - "dependencies": { - "@polkadot/types-codec": "10.11.2", - "@polkadot/util": "^12.6.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/types-known": { - "version": "10.11.2", - "resolved": "https://registry.npmjs.org/@polkadot/types-known/-/types-known-10.11.2.tgz", - "integrity": "sha512-kbEIX7NUQFxpDB0FFGNyXX/odY7jbp56RGD+Z4A731fW2xh/DgAQrI994xTzuh0c0EqPE26oQm3kATSpseqo9w==", - "dependencies": { - "@polkadot/networks": "^12.6.2", - "@polkadot/types": "10.11.2", - "@polkadot/types-codec": "10.11.2", - "@polkadot/types-create": "10.11.2", - "@polkadot/util": "^12.6.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/types-support": { - "version": "10.11.2", - "resolved": "https://registry.npmjs.org/@polkadot/types-support/-/types-support-10.11.2.tgz", - "integrity": "sha512-X11hoykFYv/3efg4coZy2hUOUc97JhjQMJLzDhHniFwGLlYU8MeLnPdCVGkXx0xDDjTo4/ptS1XpZ5HYcg+gRw==", - "dependencies": { - "@polkadot/util": "^12.6.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/util": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-12.6.2.tgz", - "integrity": "sha512-l8TubR7CLEY47240uki0TQzFvtnxFIO7uI/0GoWzpYD/O62EIAMRsuY01N4DuwgKq2ZWD59WhzsLYmA5K6ksdw==", - "dependencies": { - "@polkadot/x-bigint": "12.6.2", - "@polkadot/x-global": "12.6.2", - "@polkadot/x-textdecoder": "12.6.2", - "@polkadot/x-textencoder": "12.6.2", - "@types/bn.js": "^5.1.5", - "bn.js": "^5.2.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/util-crypto": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-12.6.2.tgz", - "integrity": "sha512-FEWI/dJ7wDMNN1WOzZAjQoIcCP/3vz3wvAp5QQm+lOrzOLj0iDmaIGIcBkz8HVm3ErfSe/uKP0KS4jgV/ib+Mg==", - "dependencies": { - "@noble/curves": "^1.3.0", - "@noble/hashes": "^1.3.3", - "@polkadot/networks": "12.6.2", - "@polkadot/util": "12.6.2", - "@polkadot/wasm-crypto": "^7.3.2", - "@polkadot/wasm-util": "^7.3.2", - "@polkadot/x-bigint": "12.6.2", - "@polkadot/x-randomvalues": "12.6.2", - "@scure/base": "^1.1.5", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "12.6.2" - } - }, - "node_modules/@polkadot/wasm-bridge": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-bridge/-/wasm-bridge-7.3.2.tgz", - "integrity": "sha512-AJEXChcf/nKXd5Q/YLEV5dXQMle3UNT7jcXYmIffZAo/KI394a+/24PaISyQjoNC0fkzS1Q8T5pnGGHmXiVz2g==", - "dependencies": { - "@polkadot/wasm-util": "7.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "*", - "@polkadot/x-randomvalues": "*" - } - }, - "node_modules/@polkadot/wasm-crypto": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto/-/wasm-crypto-7.3.2.tgz", - "integrity": "sha512-+neIDLSJ6jjVXsjyZ5oLSv16oIpwp+PxFqTUaZdZDoA2EyFRQB8pP7+qLsMNk+WJuhuJ4qXil/7XiOnZYZ+wxw==", - "dependencies": { - "@polkadot/wasm-bridge": "7.3.2", - "@polkadot/wasm-crypto-asmjs": "7.3.2", - "@polkadot/wasm-crypto-init": "7.3.2", - "@polkadot/wasm-crypto-wasm": "7.3.2", - "@polkadot/wasm-util": "7.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "*", - "@polkadot/x-randomvalues": "*" - } - }, - "node_modules/@polkadot/wasm-crypto-asmjs": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-7.3.2.tgz", - "integrity": "sha512-QP5eiUqUFur/2UoF2KKKYJcesc71fXhQFLT3D4ZjG28Mfk2ZPI0QNRUfpcxVQmIUpV5USHg4geCBNuCYsMm20Q==", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "*" - } - }, - "node_modules/@polkadot/wasm-crypto-init": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-init/-/wasm-crypto-init-7.3.2.tgz", - "integrity": "sha512-FPq73zGmvZtnuJaFV44brze3Lkrki3b4PebxCy9Fplw8nTmisKo9Xxtfew08r0njyYh+uiJRAxPCXadkC9sc8g==", - "dependencies": { - "@polkadot/wasm-bridge": "7.3.2", - "@polkadot/wasm-crypto-asmjs": "7.3.2", - "@polkadot/wasm-crypto-wasm": "7.3.2", - "@polkadot/wasm-util": "7.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "*", - "@polkadot/x-randomvalues": "*" - } - }, - "node_modules/@polkadot/wasm-crypto-wasm": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-7.3.2.tgz", - "integrity": "sha512-15wd0EMv9IXs5Abp1ZKpKKAVyZPhATIAHfKsyoWCEFDLSOA0/K0QGOxzrAlsrdUkiKZOq7uzSIgIDgW8okx2Mw==", - "dependencies": { - "@polkadot/wasm-util": "7.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "*" - } - }, - "node_modules/@polkadot/wasm-util": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-util/-/wasm-util-7.3.2.tgz", - "integrity": "sha512-bmD+Dxo1lTZyZNxbyPE380wd82QsX+43mgCm40boyKrRppXEyQmWT98v/Poc7chLuskYb6X8IQ6lvvK2bGR4Tg==", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "*" - } - }, - "node_modules/@polkadot/x-bigint": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-12.6.2.tgz", - "integrity": "sha512-HSIk60uFPX4GOFZSnIF7VYJz7WZA7tpFJsne7SzxOooRwMTWEtw3fUpFy5cYYOeLh17/kHH1Y7SVcuxzVLc74Q==", - "dependencies": { - "@polkadot/x-global": "12.6.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/x-fetch": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-fetch/-/x-fetch-12.6.2.tgz", - "integrity": "sha512-8wM/Z9JJPWN1pzSpU7XxTI1ldj/AfC8hKioBlUahZ8gUiJaOF7K9XEFCrCDLis/A1BoOu7Ne6WMx/vsJJIbDWw==", - "dependencies": { - "@polkadot/x-global": "12.6.2", - "node-fetch": "^3.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/x-global": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-12.6.2.tgz", - "integrity": "sha512-a8d6m+PW98jmsYDtAWp88qS4dl8DyqUBsd0S+WgyfSMtpEXu6v9nXDgPZgwF5xdDvXhm+P0ZfVkVTnIGrScb5g==", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/x-randomvalues": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-12.6.2.tgz", - "integrity": "sha512-Vr8uG7rH2IcNJwtyf5ebdODMcr0XjoCpUbI91Zv6AlKVYOGKZlKLYJHIwpTaKKB+7KPWyQrk4Mlym/rS7v9feg==", - "dependencies": { - "@polkadot/x-global": "12.6.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "12.6.2", - "@polkadot/wasm-util": "*" - } - }, - "node_modules/@polkadot/x-textdecoder": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-12.6.2.tgz", - "integrity": "sha512-M1Bir7tYvNappfpFWXOJcnxUhBUFWkUFIdJSyH0zs5LmFtFdbKAeiDXxSp2Swp5ddOZdZgPac294/o2TnQKN1w==", - "dependencies": { - "@polkadot/x-global": "12.6.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/x-textencoder": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-12.6.2.tgz", - "integrity": "sha512-4N+3UVCpI489tUJ6cv3uf0PjOHvgGp9Dl+SZRLgFGt9mvxnvpW/7+XBADRMtlG4xi5gaRK7bgl5bmY6OMDsNdw==", - "dependencies": { - "@polkadot/x-global": "12.6.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/x-ws": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-ws/-/x-ws-12.6.2.tgz", - "integrity": "sha512-cGZWo7K5eRRQCRl2LrcyCYsrc3lRbTlixZh3AzgU8uX4wASVGRlNWi/Hf4TtHNe1ExCDmxabJzdIsABIfrr7xw==", - "dependencies": { - "@polkadot/x-global": "12.6.2", - "tslib": "^2.6.2", - "ws": "^8.15.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@scure/base": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.5.tgz", - "integrity": "sha512-Brj9FiG2W1MRQSTB212YVPRrcbjkv48FoZi/u4l/zds/ieRrqsh7aUf6CLwkAq61oKXr/ZlTzlY66gLIj3TFTQ==", - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@substrate/connect": { - "version": "0.7.35", - "resolved": "https://registry.npmjs.org/@substrate/connect/-/connect-0.7.35.tgz", - "integrity": "sha512-Io8vkalbwaye+7yXfG1Nj52tOOoJln2bMlc7Q9Yy3vEWqZEVkgKmcPVzbwV0CWL3QD+KMPDA2Dnw/X7EdwgoLw==", - "hasInstallScript": true, - "optional": true, - "dependencies": { - "@substrate/connect-extension-protocol": "^1.0.1", - "smoldot": "2.0.7" - } - }, - "node_modules/@substrate/connect-extension-protocol": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@substrate/connect-extension-protocol/-/connect-extension-protocol-1.0.1.tgz", - "integrity": "sha512-161JhCC1csjH3GE5mPLEd7HbWtwNSPJBg3p1Ksz9SFlTzj/bgEwudiRN2y5i0MoLGCIJRYKyKGMxVnd29PzNjg==", - "optional": true - }, - "node_modules/@substrate/ss58-registry": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/@substrate/ss58-registry/-/ss58-registry-1.44.0.tgz", - "integrity": "sha512-7lQ/7mMCzVNSEfDS4BCqnRnKCFKpcOaPrxMeGTXHX1YQzM/m2BBHjbK2C3dJvjv7GYxMiaTq/HdWQj1xS6ss+A==" - }, - "node_modules/@types/bn.js": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.5.tgz", - "integrity": "sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/node": { - "version": "20.10.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.5.tgz", - "integrity": "sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw==", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/bn.js": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", - "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" - }, - "node_modules/data-uri-to-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", - "engines": { - "node": ">= 12" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" - }, - "node_modules/fetch-blob": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "paypal", - "url": "https://paypal.me/jimmywarting" - } - ], - "dependencies": { - "node-domexception": "^1.0.0", - "web-streams-polyfill": "^3.0.3" - }, - "engines": { - "node": "^12.20 || >= 14.13" - } - }, - "node_modules/formdata-polyfill": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", - "dependencies": { - "fetch-blob": "^3.1.2" - }, - "engines": { - "node": ">=12.20.0" - } - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" - }, - "node_modules/mock-socket": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/mock-socket/-/mock-socket-9.3.1.tgz", - "integrity": "sha512-qxBgB7Qa2sEQgHFjj0dSigq7fX4k6Saisd5Nelwp2q8mlbAFh5dHV9JTTlF8viYJLSSWgMCZFUom8PJcMNBoJw==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/nock": { - "version": "13.4.0", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.4.0.tgz", - "integrity": "sha512-W8NVHjO/LCTNA64yxAPHV/K47LpGYcVzgKd3Q0n6owhwvD0Dgoterc25R4rnZbckJEb6Loxz1f5QMuJpJnbSyQ==", - "dependencies": { - "debug": "^4.1.0", - "json-stringify-safe": "^5.0.1", - "propagate": "^2.0.0" - }, - "engines": { - "node": ">= 10.13" - } - }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", - "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", - "dependencies": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-fetch" - } - }, - "node_modules/propagate": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", - "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/smoldot": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/smoldot/-/smoldot-2.0.7.tgz", - "integrity": "sha512-VAOBqEen6vises36/zgrmAT1GWk2qE3X8AGnO7lmQFdskbKx8EovnwS22rtPAG+Y1Rk23/S22kDJUdPANyPkBA==", - "optional": true, - "dependencies": { - "ws": "^8.8.1" - } - }, - "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, - "node_modules/web-streams-polyfill": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", - "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - } - } -} diff --git a/bridges/testing/framework/utils/generate_hex_encoded_call/package.json b/bridges/testing/framework/utils/generate_hex_encoded_call/package.json index ecf0a2483db100e688e06da4233f036c2383b3a6..d3406c97c61ac20516dc207c12fb5f168f2a6aee 100644 --- a/bridges/testing/framework/utils/generate_hex_encoded_call/package.json +++ b/bridges/testing/framework/utils/generate_hex_encoded_call/package.json @@ -5,7 +5,7 @@ "main": "index.js", "license": "MIT", "dependencies": { - "@polkadot/api": "^10.11", - "@polkadot/util": "^12.6" + "@polkadot/api": "^14.0", + "@polkadot/util": "^13.1" } } diff --git a/cumulus/client/cli/src/lib.rs b/cumulus/client/cli/src/lib.rs index 564d7b58c94dc8d093031753ab69251f9b905ec5..b08ad75c430d773bac3e16c746363b2d6cdae27e 100644 --- a/cumulus/client/cli/src/lib.rs +++ b/cumulus/client/cli/src/lib.rs @@ -96,7 +96,7 @@ impl PurgeChainCmd { Some('y') | Some('Y') => {}, _ => { println!("Aborted"); - return Ok(()) + return Ok(()); }, } } @@ -432,19 +432,19 @@ impl sc_cli::CliConfiguration for NormalizedRunCmd { } fn rpc_max_request_size(&self) -> sc_cli::Result { - Ok(self.base.rpc_max_request_size) + self.base.rpc_max_request_size() } fn rpc_max_response_size(&self) -> sc_cli::Result { - Ok(self.base.rpc_max_response_size) + self.base.rpc_max_response_size() } fn rpc_max_subscriptions_per_connection(&self) -> sc_cli::Result { - Ok(self.base.rpc_max_subscriptions_per_connection) + 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) + Ok(self.base.rpc_params.rpc_message_buffer_capacity_per_connection) } fn rpc_batch_config(&self) -> sc_cli::Result { diff --git a/cumulus/client/collator/src/lib.rs b/cumulus/client/collator/src/lib.rs index 47da0f6d96f2f41e527515aef021c53e54dea5d9..91ff913f263d572a747d966e8d3e3f0154db9f26 100644 --- a/cumulus/client/collator/src/lib.rs +++ b/cumulus/client/collator/src/lib.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Cumulus. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/cumulus/client/consensus/aura/Cargo.toml b/cumulus/client/consensus/aura/Cargo.toml index 47e2d8572c3f8e589997ca9066e63c6896e721f9..0bb2de6bb9b8f47baa76b6c288533cb24acb4ea3 100644 --- a/cumulus/client/consensus/aura/Cargo.toml +++ b/cumulus/client/consensus/aura/Cargo.toml @@ -53,6 +53,7 @@ cumulus-client-collator = { workspace = true, default-features = true } polkadot-primitives = { workspace = true, default-features = true } polkadot-node-primitives = { workspace = true, default-features = true } polkadot-node-subsystem = { workspace = true, default-features = true } +polkadot-node-subsystem-util = { workspace = true, default-features = true } polkadot-overseer = { workspace = true, default-features = true } [features] diff --git a/cumulus/client/consensus/aura/src/collators/lookahead.rs b/cumulus/client/consensus/aura/src/collators/lookahead.rs index 0be1e0a23ca5bbd70c00c86443a5ebf4ab90c833..8ac43fbd116e5f14f9c872408cb95bf7604987cd 100644 --- a/cumulus/client/consensus/aura/src/collators/lookahead.rs +++ b/cumulus/client/consensus/aura/src/collators/lookahead.rs @@ -36,7 +36,9 @@ use cumulus_client_collator::service::ServiceInterface as CollatorServiceInterfa use cumulus_client_consensus_common::{self as consensus_common, ParachainBlockImportMarker}; use cumulus_client_consensus_proposer::ProposerInterface; use cumulus_primitives_aura::AuraUnincludedSegmentApi; -use cumulus_primitives_core::{CollectCollationInfo, PersistedValidationData}; +use cumulus_primitives_core::{ + ClaimQueueOffset, CollectCollationInfo, PersistedValidationData, DEFAULT_CLAIM_QUEUE_OFFSET, +}; use cumulus_relay_chain_interface::RelayChainInterface; use polkadot_node_primitives::{PoV, SubmitCollationParams}; @@ -260,6 +262,7 @@ where relay_parent, params.para_id, &mut params.relay_client, + ClaimQueueOffset(DEFAULT_CLAIM_QUEUE_OFFSET), ) .await .get(0) diff --git a/cumulus/client/consensus/aura/src/collators/mod.rs b/cumulus/client/consensus/aura/src/collators/mod.rs index 7d430ecdc727ab2c44b84bec509938a22da4c3f8..89070607fbaba9e919a9dd3e924ddb84f64261de 100644 --- a/cumulus/client/consensus/aura/src/collators/mod.rs +++ b/cumulus/client/consensus/aura/src/collators/mod.rs @@ -26,11 +26,12 @@ use cumulus_client_consensus_common::{ self as consensus_common, load_abridged_host_configuration, ParentSearchParams, }; use cumulus_primitives_aura::{AuraUnincludedSegmentApi, Slot}; -use cumulus_primitives_core::{relay_chain::Hash as ParaHash, BlockT}; +use cumulus_primitives_core::{relay_chain::Hash as ParaHash, BlockT, ClaimQueueOffset}; use cumulus_relay_chain_interface::RelayChainInterface; +use polkadot_node_subsystem_util::runtime::ClaimQueueSnapshot; use polkadot_primitives::{ - AsyncBackingParams, CoreIndex, CoreState, Hash as RelayHash, Id as ParaId, - OccupiedCoreAssumption, ValidationCodeHash, + AsyncBackingParams, CoreIndex, Hash as RelayHash, Id as ParaId, OccupiedCoreAssumption, + ValidationCodeHash, }; use sc_consensus_aura::{standalone as aura_internal, AuraApi}; use sp_api::ProvideRuntimeApi; @@ -126,50 +127,33 @@ async fn async_backing_params( } } -// Return all the cores assigned to the para at the provided relay parent. +// Return all the cores assigned to the para at the provided relay parent, using the claim queue +// offset. +// Will return an empty vec if the provided offset is higher than the claim queue length (which +// corresponds to the scheduling_lookahead on the relay chain). async fn cores_scheduled_for_para( relay_parent: RelayHash, para_id: ParaId, relay_client: &impl RelayChainInterface, + claim_queue_offset: ClaimQueueOffset, ) -> Vec { - // Get `AvailabilityCores` from runtime - let cores = match relay_client.availability_cores(relay_parent).await { - Ok(cores) => cores, + // Get `ClaimQueue` from runtime + let claim_queue: ClaimQueueSnapshot = match relay_client.claim_queue(relay_parent).await { + Ok(claim_queue) => claim_queue.into(), Err(error) => { tracing::error!( target: crate::LOG_TARGET, ?error, ?relay_parent, - "Failed to query availability cores runtime API", + "Failed to query claim queue runtime API", ); return Vec::new() }, }; - let max_candidate_depth = async_backing_params(relay_parent, relay_client) - .await - .map(|c| c.max_candidate_depth) - .unwrap_or(0); - - cores - .iter() - .enumerate() - .filter_map(|(index, core)| { - let core_para_id = match core { - CoreState::Scheduled(scheduled_core) => Some(scheduled_core.para_id), - CoreState::Occupied(occupied_core) if max_candidate_depth > 0 => occupied_core - .next_up_on_available - .as_ref() - .map(|scheduled_core| scheduled_core.para_id), - CoreState::Free | CoreState::Occupied(_) => None, - }; - - if core_para_id == Some(para_id) { - Some(CoreIndex(index as u32)) - } else { - None - } - }) + claim_queue + .iter_claims_at_depth(claim_queue_offset.0 as usize) + .filter_map(|(core_index, core_para_id)| (core_para_id == para_id).then_some(core_index)) .collect() } diff --git a/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs b/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs index b70cfe3841b78e439c2a936a37222da89ea9c5de..e75b52aeebd34c93b26217425e0aad39a7673682 100644 --- a/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs +++ b/cumulus/client/consensus/aura/src/collators/slot_based/block_builder_task.rs @@ -20,10 +20,13 @@ use cumulus_client_collator::service::ServiceInterface as CollatorServiceInterfa use cumulus_client_consensus_common::{self as consensus_common, ParachainBlockImportMarker}; use cumulus_client_consensus_proposer::ProposerInterface; use cumulus_primitives_aura::AuraUnincludedSegmentApi; -use cumulus_primitives_core::{CollectCollationInfo, PersistedValidationData}; +use cumulus_primitives_core::{ + GetCoreSelectorApi, PersistedValidationData, DEFAULT_CLAIM_QUEUE_OFFSET, +}; use cumulus_relay_chain_interface::RelayChainInterface; use polkadot_primitives::{ + vstaging::{ClaimQueueOffset, CoreSelector}, BlockId, CoreIndex, Hash as RelayHash, Header as RelayHeader, Id as ParaId, OccupiedCoreAssumption, }; @@ -31,16 +34,16 @@ use polkadot_primitives::{ use futures::prelude::*; use sc_client_api::{backend::AuxStore, BlockBackend, BlockOf, UsageProvider}; use sc_consensus::BlockImport; -use sp_api::ProvideRuntimeApi; +use sp_api::{ApiExt, ProvideRuntimeApi}; use sp_application_crypto::AppPublic; use sp_blockchain::HeaderBackend; -use sp_consensus_aura::{AuraApi, Slot, SlotDuration}; -use sp_core::crypto::Pair; +use sp_consensus_aura::{AuraApi, Slot}; +use sp_core::{crypto::Pair, U256}; use sp_inherents::CreateInherentDataProviders; use sp_keystore::KeystorePtr; -use sp_runtime::traits::{Block as BlockT, Header as HeaderT, Member}; +use sp_runtime::traits::{Block as BlockT, Header as HeaderT, Member, One}; use sp_timestamp::Timestamp; -use std::{sync::Arc, time::Duration}; +use std::{collections::BTreeSet, sync::Arc, time::Duration}; use super::CollatorMessage; use crate::{ @@ -87,8 +90,6 @@ pub struct BuilderTaskParams< pub authoring_duration: Duration, /// Channel to send built blocks to the collation task. pub collator_sender: sc_utils::mpsc::TracingUnboundedSender>, - /// Slot duration of the relay chain - pub relay_chain_slot_duration: Duration, /// Drift every slot by this duration. /// This is a time quantity that is subtracted from the actual timestamp when computing /// the time left to enter a new slot. In practice, this *left-shifts* the clock time with the @@ -102,7 +103,6 @@ pub struct BuilderTaskParams< struct SlotInfo { pub timestamp: Timestamp, pub slot: Slot, - pub slot_duration: SlotDuration, } #[derive(Debug)] @@ -153,11 +153,7 @@ where let time_until_next_slot = time_until_next_slot(slot_duration.as_duration(), self.drift); tokio::time::sleep(time_until_next_slot).await; let timestamp = sp_timestamp::Timestamp::current(); - Ok(SlotInfo { - slot: Slot::from_timestamp(timestamp, slot_duration), - timestamp, - slot_duration, - }) + Ok(SlotInfo { slot: Slot::from_timestamp(timestamp, slot_duration), timestamp }) } } @@ -177,7 +173,7 @@ where + Sync + 'static, Client::Api: - AuraApi + CollectCollationInfo + AuraUnincludedSegmentApi, + AuraApi + GetCoreSelectorApi + AuraUnincludedSegmentApi, Backend: sc_client_api::Backend + 'static, RelayClient: RelayChainInterface + Clone + 'static, CIDP: CreateInherentDataProviders + 'static, @@ -205,7 +201,6 @@ where code_hash_provider, authoring_duration, para_backend, - relay_chain_slot_duration, slot_drift, } = params; @@ -233,18 +228,42 @@ where return; }; - let Some(expected_cores) = - expected_core_count(relay_chain_slot_duration, para_slot.slot_duration) + let Ok(relay_parent) = relay_client.best_block_hash().await else { + tracing::warn!(target: crate::LOG_TARGET, "Unable to fetch latest relay chain block hash."); + continue + }; + + let Some((included_block, parent)) = + crate::collators::find_parent(relay_parent, para_id, &*para_backend, &relay_client) + .await else { - return + continue }; + let parent_hash = parent.hash; + + // Retrieve the core selector. + let (core_selector, claim_queue_offset) = + match core_selector(&*para_client, &parent).await { + Ok(core_selector) => core_selector, + Err(err) => { + tracing::trace!( + target: crate::LOG_TARGET, + "Unable to retrieve the core selector from the runtime API: {}", + err + ); + continue + }, + }; + let Ok(RelayChainData { relay_parent_header, max_pov_size, - relay_parent_hash: relay_parent, scheduled_cores, - }) = relay_chain_fetcher.get_relay_chain_data().await + claimed_cores, + }) = relay_chain_fetcher + .get_mut_relay_chain_data(relay_parent, claim_queue_offset) + .await else { continue; }; @@ -252,23 +271,32 @@ where if scheduled_cores.is_empty() { tracing::debug!(target: LOG_TARGET, "Parachain not scheduled, skipping slot."); continue; + } else { + tracing::debug!( + target: LOG_TARGET, + ?relay_parent, + "Parachain is scheduled on cores: {:?}", + scheduled_cores + ); } - let core_index_in_scheduled: u64 = *para_slot.slot % expected_cores; - let Some(core_index) = scheduled_cores.get(core_index_in_scheduled as usize) else { - tracing::debug!(target: LOG_TARGET, core_index_in_scheduled, core_len = scheduled_cores.len(), "Para is scheduled, but not enough cores available."); + let core_selector = core_selector.0 as usize % scheduled_cores.len(); + let Some(core_index) = scheduled_cores.get(core_selector) else { + // This cannot really happen, as we modulo the core selector with the + // scheduled_cores length and we check that the scheduled_cores is not empty. continue; }; - let Some((included_block, parent)) = - crate::collators::find_parent(relay_parent, para_id, &*para_backend, &relay_client) - .await - else { + if !claimed_cores.insert(*core_index) { + tracing::debug!( + target: LOG_TARGET, + "Core {:?} was already claimed at this relay chain slot", + core_index + ); continue - }; + } let parent_header = parent.header; - let parent_hash = parent.hash; // We mainly call this to inform users at genesis if there is a mismatch with the // on-chain data. @@ -315,7 +343,7 @@ where parent_head: parent_header.encode().into(), relay_parent_number: *relay_parent_header.number(), relay_parent_storage_root: *relay_parent_header.state_root(), - max_pov_size, + max_pov_size: *max_pov_size, }; let (parachain_inherent_data, other_inherent_data) = match collator @@ -394,34 +422,17 @@ where } } -/// Calculate the expected core count based on the slot duration of the relay and parachain. -/// -/// If `slot_duration` is smaller than `relay_chain_slot_duration` that means that we produce more -/// than one parachain block per relay chain block. In order to get these backed, we need multiple -/// cores. This method calculates how many cores we should expect to have scheduled under the -/// assumption that we have a fixed number of cores assigned to our parachain. -fn expected_core_count( - relay_chain_slot_duration: Duration, - slot_duration: SlotDuration, -) -> Option { - let slot_duration_millis = slot_duration.as_millis(); - u64::try_from(relay_chain_slot_duration.as_millis()) - .map_err(|e| tracing::error!("Unable to calculate expected parachain core count: {e}")) - .map(|relay_slot_duration| (relay_slot_duration / slot_duration_millis).max(1)) - .ok() -} - /// Contains relay chain data necessary for parachain block building. #[derive(Clone)] struct RelayChainData { /// Current relay chain parent header. pub relay_parent_header: RelayHeader, - /// The cores this para is scheduled on in the context of the relay parent. + /// The cores on which the para is scheduled at the configured claim queue offset. pub scheduled_cores: Vec, /// Maximum configured PoV size on the relay chain. pub max_pov_size: u32, - /// Current relay chain parent header. - pub relay_parent_hash: RelayHash, + /// The claimed cores at a relay parent. + pub claimed_cores: BTreeSet, } /// Simple helper to fetch relay chain data and cache it based on the current relay chain best block @@ -443,30 +454,39 @@ where /// Fetch required [`RelayChainData`] from the relay chain. /// If this data has been fetched in the past for the incoming hash, it will reuse /// cached data. - pub async fn get_relay_chain_data(&mut self) -> Result { - let Ok(relay_parent) = self.relay_client.best_block_hash().await else { - tracing::warn!(target: crate::LOG_TARGET, "Unable to fetch latest relay chain block hash."); - return Err(()) - }; - + pub async fn get_mut_relay_chain_data( + &mut self, + relay_parent: RelayHash, + claim_queue_offset: ClaimQueueOffset, + ) -> Result<&mut RelayChainData, ()> { match &self.last_data { - Some((last_seen_hash, data)) if *last_seen_hash == relay_parent => { + Some((last_seen_hash, _)) if *last_seen_hash == relay_parent => { tracing::trace!(target: crate::LOG_TARGET, %relay_parent, "Using cached data for relay parent."); - Ok(data.clone()) + Ok(&mut self.last_data.as_mut().expect("last_data is Some").1) }, _ => { tracing::trace!(target: crate::LOG_TARGET, %relay_parent, "Relay chain best block changed, fetching new data from relay chain."); - let data = self.update_for_relay_parent(relay_parent).await?; - self.last_data = Some((relay_parent, data.clone())); - Ok(data) + let data = self.update_for_relay_parent(relay_parent, claim_queue_offset).await?; + self.last_data = Some((relay_parent, data)); + Ok(&mut self.last_data.as_mut().expect("last_data was just set above").1) }, } } /// Fetch fresh data from the relay chain for the given relay parent hash. - async fn update_for_relay_parent(&self, relay_parent: RelayHash) -> Result { - let scheduled_cores = - cores_scheduled_for_para(relay_parent, self.para_id, &self.relay_client).await; + async fn update_for_relay_parent( + &self, + relay_parent: RelayHash, + claim_queue_offset: ClaimQueueOffset, + ) -> Result { + let scheduled_cores = cores_scheduled_for_para( + relay_parent, + self.para_id, + &self.relay_client, + claim_queue_offset, + ) + .await; + let Ok(Some(relay_parent_header)) = self.relay_client.header(BlockId::Hash(relay_parent)).await else { @@ -488,10 +508,32 @@ where }; Ok(RelayChainData { - relay_parent_hash: relay_parent, relay_parent_header, scheduled_cores, max_pov_size, + claimed_cores: BTreeSet::new(), }) } } + +async fn core_selector( + para_client: &Client, + parent: &consensus_common::PotentialParent, +) -> Result<(CoreSelector, ClaimQueueOffset), sp_api::ApiError> +where + Client: ProvideRuntimeApi + Send + Sync, + Client::Api: GetCoreSelectorApi, +{ + let block_hash = parent.hash; + let runtime_api = para_client.runtime_api(); + + if runtime_api.has_api::>(block_hash)? { + Ok(runtime_api.core_selector(block_hash)?) + } else { + let next_block_number: U256 = (*parent.header.number() + One::one()).into(); + + // If the runtime API does not support the core selector API, fallback to some default + // values. + Ok((CoreSelector(next_block_number.byte(0)), ClaimQueueOffset(DEFAULT_CLAIM_QUEUE_OFFSET))) + } +} diff --git a/cumulus/client/consensus/aura/src/collators/slot_based/mod.rs b/cumulus/client/consensus/aura/src/collators/slot_based/mod.rs index 0fe49d58d25be672d8541b486c0aa8f22d825c30..7453d3c89d08c4923237463f583b45f0f024cbb5 100644 --- a/cumulus/client/consensus/aura/src/collators/slot_based/mod.rs +++ b/cumulus/client/consensus/aura/src/collators/slot_based/mod.rs @@ -34,7 +34,7 @@ use cumulus_client_collator::service::ServiceInterface as CollatorServiceInterfa use cumulus_client_consensus_common::{self as consensus_common, ParachainBlockImportMarker}; use cumulus_client_consensus_proposer::ProposerInterface; use cumulus_primitives_aura::AuraUnincludedSegmentApi; -use cumulus_primitives_core::CollectCollationInfo; +use cumulus_primitives_core::GetCoreSelectorApi; use cumulus_relay_chain_interface::RelayChainInterface; use polkadot_primitives::{ CollatorPair, CoreIndex, Hash as RelayHash, Id as ParaId, ValidationCodeHash, @@ -82,8 +82,6 @@ pub struct Params { pub collator_key: CollatorPair, /// The para's ID. pub para_id: ParaId, - /// The length of slots in the relay chain. - pub relay_chain_slot_duration: Duration, /// The underlying block proposer this should call into. pub proposer: Proposer, /// The generic collator service used to plug into this consensus engine. @@ -113,7 +111,7 @@ where + Sync + 'static, Client::Api: - AuraApi + CollectCollationInfo + AuraUnincludedSegmentApi, + AuraApi + GetCoreSelectorApi + AuraUnincludedSegmentApi, Backend: sc_client_api::Backend + 'static, RClient: RelayChainInterface + Clone + 'static, CIDP: CreateInherentDataProviders + 'static, @@ -151,7 +149,6 @@ where collator_service: params.collator_service, authoring_duration: params.authoring_duration, collator_sender: tx, - relay_chain_slot_duration: params.relay_chain_slot_duration, slot_drift: params.slot_drift, }; diff --git a/cumulus/client/consensus/common/src/tests.rs b/cumulus/client/consensus/common/src/tests.rs index 06f90330d4745525cb20f6c3950b7d011a71f486..94e2304011be0bfbf003685de32ad5fd5fc8f424 100644 --- a/cumulus/client/consensus/common/src/tests.rs +++ b/cumulus/client/consensus/common/src/tests.rs @@ -24,7 +24,7 @@ use cumulus_primitives_core::{ CumulusDigestItem, InboundDownwardMessage, InboundHrmpMessage, }; use cumulus_relay_chain_interface::{ - CommittedCandidateReceipt, OccupiedCoreAssumption, OverseerHandle, PHeader, ParaId, + CommittedCandidateReceipt, CoreIndex, OccupiedCoreAssumption, OverseerHandle, PHeader, ParaId, RelayChainInterface, RelayChainResult, SessionIndex, StorageValue, ValidatorId, }; use cumulus_test_client::{ @@ -41,7 +41,7 @@ use sp_blockchain::Backend as BlockchainBackend; use sp_consensus::{BlockOrigin, BlockStatus}; use sp_version::RuntimeVersion; use std::{ - collections::{BTreeMap, HashMap}, + collections::{BTreeMap, HashMap, VecDeque}, pin::Pin, sync::{Arc, Mutex}, time::Duration, @@ -268,6 +268,22 @@ impl RelayChainInterface for Relaychain { async fn version(&self, _: PHash) -> RelayChainResult { unimplemented!("Not needed for test") } + + async fn claim_queue( + &self, + _: PHash, + ) -> RelayChainResult>> { + unimplemented!("Not needed for test"); + } + + async fn call_runtime_api( + &self, + _method_name: &'static str, + _hash: PHash, + _payload: &[u8], + ) -> RelayChainResult> { + unimplemented!("Not needed for test") + } } fn sproof_with_best_parent(client: &Client) -> RelayStateSproofBuilder { diff --git a/cumulus/client/network/src/lib.rs b/cumulus/client/network/src/lib.rs index dab15bba590a0f46720d45bdaddc92f594fb8a9f..01ad15bed4daec41ec6c66874739d4f0420bc84c 100644 --- a/cumulus/client/network/src/lib.rs +++ b/cumulus/client/network/src/lib.rs @@ -1,18 +1,18 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Cumulus. -// Polkadot is free software: you can redistribute it and/or modify +// 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. -// Polkadot is distributed in the hope that it will be useful, +// 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 Polkadot. If not, see . +// along with Cumulus. If not, see . //! Parachain specific networking //! diff --git a/cumulus/client/network/src/tests.rs b/cumulus/client/network/src/tests.rs index cde73c4c51806b7dcd9f01f5d9bd180a618c1dd9..4b347364521009a5426100e4945da84bceee40c1 100644 --- a/cumulus/client/network/src/tests.rs +++ b/cumulus/client/network/src/tests.rs @@ -1,22 +1,22 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Cumulus. -// Polkadot is free software: you can redistribute it and/or modify +// 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. -// Polkadot is distributed in the hope that it will be useful, +// 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 Polkadot. If not, see . +// along with Cumulus. If not, see . use super::*; use async_trait::async_trait; -use cumulus_primitives_core::relay_chain::BlockId; +use cumulus_primitives_core::relay_chain::{BlockId, CoreIndex}; use cumulus_relay_chain_inprocess_interface::{check_block_in_chain, BlockCheckStatus}; use cumulus_relay_chain_interface::{ OverseerHandle, PHeader, ParaId, RelayChainError, RelayChainResult, @@ -45,7 +45,11 @@ use sp_keystore::{testing::MemoryKeystore, Keystore, KeystorePtr}; use sp_runtime::RuntimeAppPublic; use sp_state_machine::StorageValue; use sp_version::RuntimeVersion; -use std::{borrow::Cow, collections::BTreeMap, time::Duration}; +use std::{ + borrow::Cow, + collections::{BTreeMap, VecDeque}, + time::Duration, +}; fn check_error(error: crate::BoxedError, check_error: impl Fn(&BlockAnnounceError) -> bool) { let error = *error @@ -323,9 +327,25 @@ impl RelayChainInterface for DummyRelayChainInterface { impl_version: 0, apis: Cow::Owned(apis), transaction_version: 5, - state_version: 1, + system_version: 1, }) } + + async fn claim_queue( + &self, + _: PHash, + ) -> RelayChainResult>> { + unimplemented!("Not needed for test"); + } + + async fn call_runtime_api( + &self, + _method_name: &'static str, + _hash: PHash, + _payload: &[u8], + ) -> RelayChainResult> { + unimplemented!("Not needed for test") + } } fn make_validator_and_api() -> ( diff --git a/cumulus/client/parachain-inherent/src/lib.rs b/cumulus/client/parachain-inherent/src/lib.rs index 051eb6764c8ce97a8305a07bdddb8f06f61fe4bc..0bb436a876b4ff9f80f97a0c3bfbbdbfab8f2796 100644 --- a/cumulus/client/parachain-inherent/src/lib.rs +++ b/cumulus/client/parachain-inherent/src/lib.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Cumulus. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/cumulus/client/parachain-inherent/src/mock.rs b/cumulus/client/parachain-inherent/src/mock.rs index dfe4a66c3dc19665d413fed47434d803d6ed63af..a3f881e6ef9dbd4ae4798975c2332f98ee44e207 100644 --- a/cumulus/client/parachain-inherent/src/mock.rs +++ b/cumulus/client/parachain-inherent/src/mock.rs @@ -6,7 +6,7 @@ // 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, +// 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. diff --git a/cumulus/client/pov-recovery/src/active_candidate_recovery.rs b/cumulus/client/pov-recovery/src/active_candidate_recovery.rs index 50de98909ea471ea3de39a9edd85f4f9d71a8b3b..9badc69fe816becaa5d2df3bdbbc50fed68b6ddb 100644 --- a/cumulus/client/pov-recovery/src/active_candidate_recovery.rs +++ b/cumulus/client/pov-recovery/src/active_candidate_recovery.rs @@ -1,18 +1,18 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Cumulus. -// Polkadot is free software: you can redistribute it and/or modify +// 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. -// Polkadot is distributed in the hope that it will be useful, +// 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 Polkadot. If not, see . +// along with Cumulus. If not, see . use sp_runtime::traits::Block as BlockT; diff --git a/cumulus/client/pov-recovery/src/lib.rs b/cumulus/client/pov-recovery/src/lib.rs index 6ace18155e871d40baa2662c8c13b605b5768bbe..043cba12d1937807ded90c755377f8c99b976433 100644 --- a/cumulus/client/pov-recovery/src/lib.rs +++ b/cumulus/client/pov-recovery/src/lib.rs @@ -6,7 +6,7 @@ // 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, +// 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. diff --git a/cumulus/client/pov-recovery/src/tests.rs b/cumulus/client/pov-recovery/src/tests.rs index 6f274ed18b6bc7871607cae6c5183d6d861e5117..94dec32485ccb7478771a7d8e909056d7312dfee 100644 --- a/cumulus/client/pov-recovery/src/tests.rs +++ b/cumulus/client/pov-recovery/src/tests.rs @@ -1,24 +1,24 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Cumulus. -// Polkadot is free software: you can redistribute it and/or modify +// 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. -// Polkadot is distributed in the hope that it will be useful, +// 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 Polkadot. If not, see . +// along with Cumulus. If not, see . use super::*; use assert_matches::assert_matches; use codec::{Decode, Encode}; use cumulus_primitives_core::relay_chain::{ - BlockId, CandidateCommitments, CandidateDescriptor, CoreState, + BlockId, CandidateCommitments, CandidateDescriptor, CoreIndex, CoreState, }; use cumulus_relay_chain_interface::{ InboundDownwardMessage, InboundHrmpMessage, OccupiedCoreAssumption, PHash, PHeader, @@ -43,7 +43,7 @@ use sp_runtime::{generic::SignedBlock, Justifications}; use sp_version::RuntimeVersion; use std::{ borrow::Cow, - collections::BTreeMap, + collections::{BTreeMap, VecDeque}, ops::Range, sync::{Arc, Mutex}, }; @@ -329,7 +329,7 @@ impl RelayChainInterface for Relaychain { impl_version: 0, apis: Cow::Owned(apis), transaction_version: 5, - state_version: 1, + system_version: 1, }) } @@ -487,6 +487,22 @@ impl RelayChainInterface for Relaychain { ) -> RelayChainResult>>> { unimplemented!("Not needed for test"); } + + async fn claim_queue( + &self, + _: PHash, + ) -> RelayChainResult>> { + unimplemented!("Not needed for test"); + } + + async fn call_runtime_api( + &self, + _method_name: &'static str, + _hash: PHash, + _payload: &[u8], + ) -> RelayChainResult> { + unimplemented!("Not needed for test") + } } fn make_candidate_chain(candidate_number_range: Range) -> Vec { diff --git a/cumulus/client/relay-chain-inprocess-interface/src/lib.rs b/cumulus/client/relay-chain-inprocess-interface/src/lib.rs index c796dc5f7c382fb18b5b8f5c9b2d5a69eb43cb6b..3a204b0f383aa22f94b7f3f7c86e748af555e64a 100644 --- a/cumulus/client/relay-chain-inprocess-interface/src/lib.rs +++ b/cumulus/client/relay-chain-inprocess-interface/src/lib.rs @@ -14,14 +14,19 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use std::{collections::btree_map::BTreeMap, pin::Pin, sync::Arc, time::Duration}; +use std::{ + collections::{BTreeMap, VecDeque}, + pin::Pin, + sync::Arc, + time::Duration, +}; use async_trait::async_trait; use cumulus_primitives_core::{ relay_chain::{ runtime_api::ParachainHost, Block as PBlock, BlockId, BlockNumber, - CommittedCandidateReceipt, CoreState, Hash as PHash, Header as PHeader, InboundHrmpMessage, - OccupiedCoreAssumption, SessionIndex, ValidationCodeHash, ValidatorId, + CommittedCandidateReceipt, CoreIndex, CoreState, Hash as PHash, Header as PHeader, + InboundHrmpMessage, OccupiedCoreAssumption, SessionIndex, ValidationCodeHash, ValidatorId, }, InboundDownwardMessage, ParaId, PersistedValidationData, }; @@ -36,7 +41,7 @@ use sc_client_api::{ StorageProof, }; use sc_telemetry::TelemetryWorkerHandle; -use sp_api::ProvideRuntimeApi; +use sp_api::{CallApiAt, CallApiAtParams, CallContext, ProvideRuntimeApi}; use sp_consensus::SyncOracle; use sp_core::Pair; use sp_state_machine::{Backend as StateBackend, StorageValue}; @@ -137,7 +142,11 @@ impl RelayChainInterface for RelayChainInProcessInterface { hash: PHash, para_id: ParaId, ) -> RelayChainResult> { - Ok(self.full_client.runtime_api().candidate_pending_availability(hash, para_id)?) + Ok(self + .full_client + .runtime_api() + .candidate_pending_availability(hash, para_id)? + .map(|receipt| receipt.into())) } async fn session_index_for_child(&self, hash: PHash) -> RelayChainResult { @@ -176,6 +185,23 @@ impl RelayChainInterface for RelayChainInProcessInterface { Ok(self.backend.blockchain().info().finalized_hash) } + async fn call_runtime_api( + &self, + method_name: &'static str, + hash: PHash, + payload: &[u8], + ) -> RelayChainResult> { + Ok(self.full_client.call_api_at(CallApiAtParams { + at: hash, + function: method_name, + arguments: payload.to_vec(), + overlayed_changes: &Default::default(), + call_context: CallContext::Offchain, + recorder: &None, + extensions: &Default::default(), + })?) + } + async fn is_major_syncing(&self) -> RelayChainResult { Ok(self.sync_oracle.is_major_syncing()) } @@ -260,7 +286,13 @@ impl RelayChainInterface for RelayChainInProcessInterface { &self, relay_parent: PHash, ) -> RelayChainResult>> { - Ok(self.full_client.runtime_api().availability_cores(relay_parent)?) + Ok(self + .full_client + .runtime_api() + .availability_cores(relay_parent)? + .into_iter() + .map(|core_state| core_state.into()) + .collect::>()) } async fn candidates_pending_availability( @@ -268,7 +300,20 @@ impl RelayChainInterface for RelayChainInProcessInterface { hash: PHash, para_id: ParaId, ) -> RelayChainResult> { - Ok(self.full_client.runtime_api().candidates_pending_availability(hash, para_id)?) + Ok(self + .full_client + .runtime_api() + .candidates_pending_availability(hash, para_id)? + .into_iter() + .map(|receipt| receipt.into()) + .collect::>()) + } + + async fn claim_queue( + &self, + hash: PHash, + ) -> RelayChainResult>> { + Ok(self.full_client.runtime_api().claim_queue(hash)?) } } @@ -318,7 +363,6 @@ fn build_polkadot_full_node( // Disable BEEFY. It should not be required by the internal relay chain node. enable_beefy: false, force_authoring_backoff: false, - jaeger_agent: None, telemetry_worker_handle, // Cumulus doesn't spawn PVF workers, so we can disable version checks. @@ -334,6 +378,7 @@ fn build_polkadot_full_node( execute_workers_max_num: None, prepare_workers_hard_max_num: None, prepare_workers_soft_max_num: None, + enable_approval_voting_parallel: false, }, )?; diff --git a/cumulus/client/relay-chain-interface/src/lib.rs b/cumulus/client/relay-chain-interface/src/lib.rs index d02035e84e92f45c4da74f91912ee57abaa083ee..2eed71c4d7d6788a43b1c66c31145d75e6ae82b0 100644 --- a/cumulus/client/relay-chain-interface/src/lib.rs +++ b/cumulus/client/relay-chain-interface/src/lib.rs @@ -14,7 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use std::{collections::BTreeMap, pin::Pin, sync::Arc}; +use std::{ + collections::{BTreeMap, VecDeque}, + pin::Pin, + sync::Arc, +}; use futures::Stream; use polkadot_overseer::prometheus::PrometheusError; @@ -22,15 +26,16 @@ use sc_client_api::StorageProof; use sp_version::RuntimeVersion; use async_trait::async_trait; -use codec::Error as CodecError; +use codec::{Decode, Encode, Error as CodecError}; use jsonrpsee_core::ClientError as JsonRpcError; use sp_api::ApiError; -use cumulus_primitives_core::relay_chain::BlockId; +use cumulus_primitives_core::relay_chain::{BlockId, Hash as RelayHash}; pub use cumulus_primitives_core::{ relay_chain::{ - BlockNumber, CommittedCandidateReceipt, CoreState, Hash as PHash, Header as PHeader, - InboundHrmpMessage, OccupiedCoreAssumption, SessionIndex, ValidationCodeHash, ValidatorId, + BlockNumber, CommittedCandidateReceipt, CoreIndex, CoreState, Hash as PHash, + Header as PHeader, InboundHrmpMessage, OccupiedCoreAssumption, SessionIndex, + ValidationCodeHash, ValidatorId, }, InboundDownwardMessage, ParaId, PersistedValidationData, }; @@ -117,6 +122,14 @@ pub trait RelayChainInterface: Send + Sync { /// Get the hash of the finalized block. async fn finalized_block_hash(&self) -> RelayChainResult; + /// Call an arbitrary runtime api. The input and output are SCALE-encoded. + async fn call_runtime_api( + &self, + method_name: &'static str, + hash: RelayHash, + payload: &[u8], + ) -> RelayChainResult>; + /// Returns the whole contents of the downward message queue for the parachain we are collating /// for. /// @@ -225,6 +238,12 @@ pub trait RelayChainInterface: Send + Sync { &self, relay_parent: PHash, ) -> RelayChainResult>>; + + /// Fetch the claim queue. + async fn claim_queue( + &self, + relay_parent: PHash, + ) -> RelayChainResult>>; } #[async_trait] @@ -296,6 +315,15 @@ where (**self).finalized_block_hash().await } + async fn call_runtime_api( + &self, + method_name: &'static str, + hash: RelayHash, + payload: &[u8], + ) -> RelayChainResult> { + (**self).call_runtime_api(method_name, hash, payload).await + } + async fn is_major_syncing(&self) -> RelayChainResult { (**self).is_major_syncing().await } @@ -363,4 +391,27 @@ where async fn version(&self, relay_parent: PHash) -> RelayChainResult { (**self).version(relay_parent).await } + + async fn claim_queue( + &self, + relay_parent: PHash, + ) -> RelayChainResult>> { + (**self).claim_queue(relay_parent).await + } +} + +/// Helper function to call an arbitrary runtime API using a `RelayChainInterface` client. +/// Unlike the trait method, this function can be generic, so it handles the encoding of input and +/// output params. +pub async fn call_runtime_api( + client: &(impl RelayChainInterface + ?Sized), + method_name: &'static str, + hash: RelayHash, + payload: impl Encode, +) -> RelayChainResult +where + R: Decode, +{ + let res = client.call_runtime_api(method_name, hash, &payload.encode()).await?; + Decode::decode(&mut &*res).map_err(Into::into) } diff --git a/cumulus/client/relay-chain-minimal-node/src/blockchain_rpc_client.rs b/cumulus/client/relay-chain-minimal-node/src/blockchain_rpc_client.rs index 06f19941165a26374555064efcd797e1b9ebde34..7d6b5bfe3ec730cc827e104c21377175543fab26 100644 --- a/cumulus/client/relay-chain-minimal-node/src/blockchain_rpc_client.rs +++ b/cumulus/client/relay-chain-minimal-node/src/blockchain_rpc_client.rs @@ -19,6 +19,7 @@ use std::{ pin::Pin, }; +use cumulus_primitives_core::{InboundDownwardMessage, ParaId, PersistedValidationData}; use cumulus_relay_chain_interface::{RelayChainError, RelayChainResult}; use cumulus_relay_chain_rpc_interface::RelayChainRpcClient; use futures::{Stream, StreamExt}; @@ -132,7 +133,7 @@ impl RuntimeApiSubsystemClient for BlockChainRpcClient { ) -> Result< ( Vec>, - polkadot_primitives::GroupRotationInfo, + polkadot_primitives::GroupRotationInfo, ), sp_api::ApiError, > { @@ -142,27 +143,16 @@ impl RuntimeApiSubsystemClient for BlockChainRpcClient { async fn availability_cores( &self, at: Hash, - ) -> Result< - Vec>, - sp_api::ApiError, - > { + ) -> Result>, sp_api::ApiError> { Ok(self.rpc_client.parachain_host_availability_cores(at).await?) } async fn persisted_validation_data( &self, at: Hash, - para_id: cumulus_primitives_core::ParaId, + para_id: ParaId, assumption: polkadot_primitives::OccupiedCoreAssumption, - ) -> Result< - Option< - cumulus_primitives_core::PersistedValidationData< - Hash, - polkadot_core_primitives::BlockNumber, - >, - >, - sp_api::ApiError, - > { + ) -> Result>, sp_api::ApiError> { Ok(self .rpc_client .parachain_host_persisted_validation_data(at, para_id, assumption) @@ -172,14 +162,11 @@ impl RuntimeApiSubsystemClient for BlockChainRpcClient { async fn assumed_validation_data( &self, at: Hash, - para_id: cumulus_primitives_core::ParaId, + para_id: ParaId, expected_persisted_validation_data_hash: Hash, ) -> Result< Option<( - cumulus_primitives_core::PersistedValidationData< - Hash, - polkadot_core_primitives::BlockNumber, - >, + PersistedValidationData, polkadot_primitives::ValidationCodeHash, )>, sp_api::ApiError, @@ -197,7 +184,7 @@ impl RuntimeApiSubsystemClient for BlockChainRpcClient { async fn check_validation_outputs( &self, at: Hash, - para_id: cumulus_primitives_core::ParaId, + para_id: ParaId, outputs: polkadot_primitives::CandidateCommitments, ) -> Result { Ok(self @@ -216,7 +203,7 @@ impl RuntimeApiSubsystemClient for BlockChainRpcClient { async fn validation_code( &self, at: Hash, - para_id: cumulus_primitives_core::ParaId, + para_id: ParaId, assumption: polkadot_primitives::OccupiedCoreAssumption, ) -> Result, sp_api::ApiError> { Ok(self.rpc_client.parachain_host_validation_code(at, para_id, assumption).await?) @@ -225,7 +212,7 @@ impl RuntimeApiSubsystemClient for BlockChainRpcClient { async fn candidate_pending_availability( &self, at: Hash, - para_id: cumulus_primitives_core::ParaId, + para_id: ParaId, ) -> Result>, sp_api::ApiError> { Ok(self .rpc_client @@ -243,24 +230,19 @@ impl RuntimeApiSubsystemClient for BlockChainRpcClient { async fn dmq_contents( &self, at: Hash, - recipient: cumulus_primitives_core::ParaId, - ) -> Result< - Vec>, - sp_api::ApiError, - > { + recipient: ParaId, + ) -> Result>, sp_api::ApiError> { Ok(self.rpc_client.parachain_host_dmq_contents(recipient, at).await?) } async fn inbound_hrmp_channels_contents( &self, at: Hash, - recipient: cumulus_primitives_core::ParaId, + recipient: ParaId, ) -> Result< std::collections::BTreeMap< - cumulus_primitives_core::ParaId, - Vec< - polkadot_core_primitives::InboundHrmpMessage, - >, + ParaId, + Vec>, >, sp_api::ApiError, > { @@ -329,7 +311,7 @@ impl RuntimeApiSubsystemClient for BlockChainRpcClient { async fn validation_code_hash( &self, at: Hash, - para_id: cumulus_primitives_core::ParaId, + para_id: ParaId, assumption: polkadot_primitives::OccupiedCoreAssumption, ) -> Result, sp_api::ApiError> { Ok(self @@ -424,7 +406,7 @@ impl RuntimeApiSubsystemClient for BlockChainRpcClient { async fn para_backing_state( &self, at: Hash, - para_id: cumulus_primitives_core::ParaId, + para_id: ParaId, ) -> Result, ApiError> { Ok(self.rpc_client.parachain_host_para_backing_state(at, para_id).await?) } @@ -448,14 +430,14 @@ impl RuntimeApiSubsystemClient for BlockChainRpcClient { async fn claim_queue( &self, at: Hash, - ) -> Result>, ApiError> { + ) -> Result>, ApiError> { Ok(self.rpc_client.parachain_host_claim_queue(at).await?) } async fn candidates_pending_availability( &self, at: Hash, - para_id: cumulus_primitives_core::ParaId, + para_id: ParaId, ) -> Result>, sp_api::ApiError> { Ok(self .rpc_client 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 f01ef8b05ecba77cf6458fdfe80e966b3a67e6a3..5acc30537080cb872934de8058282dec65fd3202 100644 --- a/cumulus/client/relay-chain-minimal-node/src/collator_overseer.rs +++ b/cumulus/client/relay-chain-minimal-node/src/collator_overseer.rs @@ -1,18 +1,18 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Cumulus. -// Polkadot is free software: you can redistribute it and/or modify +// 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. -// Polkadot is distributed in the hope that it will be useful, +// 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 Polkadot. If not, see . +// along with Cumulus. If not, see . use futures::{select, StreamExt}; use std::sync::Arc; diff --git a/cumulus/client/relay-chain-minimal-node/src/lib.rs b/cumulus/client/relay-chain-minimal-node/src/lib.rs index 732a242e72957214ce8161d5f785d20b8c2484b3..a3d858ea40c92fa6ffebdb54cd52b05905380721 100644 --- a/cumulus/client/relay-chain-minimal-node/src/lib.rs +++ b/cumulus/client/relay-chain-minimal-node/src/lib.rs @@ -1,18 +1,18 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Cumulus. -// Polkadot is free software: you can redistribute it and/or modify +// 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. -// Polkadot is distributed in the hope that it will be useful, +// 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 Polkadot. If not, see . +// along with Cumulus. If not, see . use collator_overseer::NewMinimalNode; @@ -96,19 +96,20 @@ async fn build_interface( client: RelayChainRpcClient, ) -> RelayChainResult<(Arc<(dyn RelayChainInterface + 'static)>, Option)> { let collator_pair = CollatorPair::generate().0; + let blockchain_rpc_client = Arc::new(BlockChainRpcClient::new(client.clone())); let collator_node = match polkadot_config.network.network_backend { sc_network::config::NetworkBackendType::Libp2p => new_minimal_relay_chain::>( polkadot_config, collator_pair.clone(), - Arc::new(BlockChainRpcClient::new(client.clone())), + blockchain_rpc_client, ) .await?, sc_network::config::NetworkBackendType::Litep2p => new_minimal_relay_chain::( polkadot_config, collator_pair.clone(), - Arc::new(BlockChainRpcClient::new(client.clone())), + blockchain_rpc_client, ) .await?, }; @@ -120,17 +121,19 @@ async fn build_interface( } pub async fn build_minimal_relay_chain_node_with_rpc( - polkadot_config: Configuration, + relay_chain_config: Configuration, + parachain_prometheus_registry: Option<&Registry>, task_manager: &mut TaskManager, relay_chain_url: Vec, ) -> RelayChainResult<(Arc<(dyn RelayChainInterface + 'static)>, Option)> { let client = cumulus_relay_chain_rpc_interface::create_client_and_start_worker( relay_chain_url, task_manager, + parachain_prometheus_registry, ) .await?; - build_interface(polkadot_config, task_manager, client).await + build_interface(relay_chain_config, task_manager, client).await } pub async fn build_minimal_relay_chain_node_light_client( @@ -175,7 +178,7 @@ async fn new_minimal_relay_chain, ) -> Result { - let role = config.role.clone(); + let role = config.role; let mut net_config = sc_network::config::FullNetworkConfiguration::<_, _, Network>::new( &config.network, config.prometheus_config.as_ref().map(|cfg| cfg.registry.clone()), diff --git a/cumulus/client/relay-chain-minimal-node/src/network.rs b/cumulus/client/relay-chain-minimal-node/src/network.rs index 025ac7a81a21c6a23622cac37882994309c54e85..afe83a2a12f91dcaa2ee61230ee79a0203175748 100644 --- a/cumulus/client/relay-chain-minimal-node/src/network.rs +++ b/cumulus/client/relay-chain-minimal-node/src/network.rs @@ -65,7 +65,7 @@ pub(crate) fn build_collator_network>( spawn_handle.spawn("peer-store", Some("networking"), peer_store.run()); let network_params = sc_network::config::Params:: { - role: config.role.clone(), + role: config.role, executor: { let spawn_handle = Clone::clone(&spawn_handle); Box::new(move |fut| { diff --git a/cumulus/client/relay-chain-rpc-interface/Cargo.toml b/cumulus/client/relay-chain-rpc-interface/Cargo.toml index 6c0730a56a264b9805b092df203671778719bc49..fb4cb4ceed4ec9a6ec796fa7b00c4df353e90ad6 100644 --- a/cumulus/client/relay-chain-rpc-interface/Cargo.toml +++ b/cumulus/client/relay-chain-rpc-interface/Cargo.toml @@ -9,6 +9,9 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [lints] workspace = true +[dev-dependencies] +portpicker = "0.1.1" + [dependencies] polkadot-overseer = { workspace = true, default-features = true } @@ -26,6 +29,7 @@ sp-version = { workspace = true, default-features = true } sc-client-api = { workspace = true, default-features = true } sc-rpc-api = { workspace = true, default-features = true } sc-service = { workspace = true, default-features = true } +prometheus-endpoint = { workspace = true, default-features = true } tokio = { features = ["sync"], workspace = true, default-features = true } tokio-util = { features = ["compat"], workspace = true } @@ -46,3 +50,4 @@ either = { workspace = true, default-features = true } thiserror = { workspace = true } rand = { workspace = true, default-features = true } pin-project = { workspace = true } +prometheus = { workspace = true } diff --git a/cumulus/client/relay-chain-rpc-interface/src/lib.rs b/cumulus/client/relay-chain-rpc-interface/src/lib.rs index e32ec6a41a4bf00db5e5db07235bf43bbad1dbf6..f53cdeffea94ba638f8abe8f5a9cd4add97e6563 100644 --- a/cumulus/client/relay-chain-rpc-interface/src/lib.rs +++ b/cumulus/client/relay-chain-rpc-interface/src/lib.rs @@ -39,6 +39,7 @@ use cumulus_primitives_core::relay_chain::BlockId; pub use url::Url; mod light_client_worker; +mod metrics; mod reconnecting_ws_client; mod rpc_client; mod tokio_platform; @@ -87,12 +88,13 @@ impl RelayChainInterface for RelayChainRpcInterface { async fn header(&self, block_id: BlockId) -> RelayChainResult> { let hash = match block_id { BlockId::Hash(hash) => hash, - BlockId::Number(num) => + BlockId::Number(num) => { if let Some(hash) = self.rpc_client.chain_get_block_hash(Some(num)).await? { hash } else { return Ok(None) - }, + } + }, }; let header = self.rpc_client.chain_get_header(Some(hash)).await?; @@ -163,6 +165,18 @@ impl RelayChainInterface for RelayChainRpcInterface { self.rpc_client.chain_get_finalized_head().await } + async fn call_runtime_api( + &self, + method_name: &'static str, + hash: RelayHash, + payload: &[u8], + ) -> RelayChainResult> { + self.rpc_client + .call_remote_runtime_function_encoded(method_name, hash, payload) + .await + .map(|bytes| bytes.to_vec()) + } + async fn is_major_syncing(&self) -> RelayChainResult { self.rpc_client.system_health().await.map(|h| h.is_syncing) } @@ -258,4 +272,13 @@ impl RelayChainInterface for RelayChainRpcInterface { ) -> RelayChainResult>> { self.rpc_client.parachain_host_availability_cores(relay_parent).await } + + async fn claim_queue( + &self, + relay_parent: RelayHash, + ) -> RelayChainResult< + BTreeMap>, + > { + self.rpc_client.parachain_host_claim_queue(relay_parent).await + } } diff --git a/cumulus/client/relay-chain-rpc-interface/src/metrics.rs b/cumulus/client/relay-chain-rpc-interface/src/metrics.rs new file mode 100644 index 0000000000000000000000000000000000000000..4d09464d237c69faffa7f168dd9647c82adba285 --- /dev/null +++ b/cumulus/client/relay-chain-rpc-interface/src/metrics.rs @@ -0,0 +1,49 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +use prometheus::{Error as PrometheusError, HistogramTimer, Registry}; +use prometheus_endpoint::{HistogramOpts, HistogramVec, Opts}; + +/// Gathers metrics about the blockchain RPC client. +#[derive(Clone)] +pub(crate) struct RelaychainRpcMetrics { + rpc_request: HistogramVec, +} + +impl RelaychainRpcMetrics { + pub(crate) fn register(registry: &Registry) -> Result { + Ok(Self { + rpc_request: prometheus_endpoint::register( + HistogramVec::new( + HistogramOpts { + common_opts: Opts::new( + "relay_chain_rpc_interface", + "Tracks stats about cumulus relay chain RPC interface", + ), + buckets: prometheus::exponential_buckets(0.001, 4.0, 9) + .expect("function parameters are constant and always valid; qed"), + }, + &["method"], + )?, + registry, + )?, + }) + } + + pub(crate) fn start_request_timer(&self, method: &str) -> HistogramTimer { + self.rpc_request.with_label_values(&[method]).start_timer() + } +} 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 48d35dd3a55eee935fb27f2fbc60302e9ed3e76a..dc0e9d697b466faf1f7838a055d3732bcdd4d9d7 100644 --- a/cumulus/client/relay-chain-rpc-interface/src/reconnecting_ws_client.rs +++ b/cumulus/client/relay-chain-rpc-interface/src/reconnecting_ws_client.rs @@ -34,7 +34,7 @@ use jsonrpsee::{ use sc_rpc_api::chain::ChainApiClient; use schnellru::{ByLength, LruMap}; use sp_runtime::generic::SignedBlock; -use std::sync::Arc; +use std::{sync::Arc, time::Duration}; use tokio::sync::mpsc::{ channel as tokio_channel, Receiver as TokioReceiver, Sender as TokioSender, }; @@ -43,6 +43,9 @@ use url::Url; use crate::rpc_client::{distribute_header, RpcDispatcherMessage}; const LOG_TARGET: &str = "reconnecting-websocket-client"; +const DEFAULT_EXTERNAL_RPC_CONN_RETRIES: usize = 5; +const DEFAULT_SLEEP_TIME_MS_BETWEEN_RETRIES: u64 = 1000; +const DEFAULT_SLEEP_EXP_BACKOFF_BETWEEN_RETRIES: i32 = 2; /// Worker that should be used in combination with [`RelayChainRpcClient`]. /// @@ -93,16 +96,45 @@ struct RelayChainSubscriptions { best_subscription: Subscription, } -/// Try to find a new RPC server to connect to. +/// Try to find a new RPC server to connect to. Uses a naive retry +/// logic that does an exponential backoff in between iterations +/// through all URLs from the list. It uses a constant to tell how +/// many iterations of connection attempts to all URLs we allow. We +/// return early when a connection is made. async fn connect_next_available_rpc_server( urls: &Vec, starting_position: usize, ) -> Result<(usize, Arc), ()> { tracing::debug!(target: LOG_TARGET, starting_position, "Connecting to RPC server."); - for (counter, url) in urls.iter().cycle().skip(starting_position).take(urls.len()).enumerate() { + + let mut prev_iteration: u32 = 0; + for (counter, url) in urls + .iter() + .cycle() + .skip(starting_position) + .take(urls.len() * DEFAULT_EXTERNAL_RPC_CONN_RETRIES) + .enumerate() + { + // If we reached the end of the urls list, backoff before retrying + // connections to the entire list once more. + let Ok(current_iteration) = (counter / urls.len()).try_into() else { + tracing::error!(target: LOG_TARGET, "Too many connection attempts to the RPC servers, aborting..."); + break; + }; + if current_iteration > prev_iteration { + // Safe conversion given we convert positive i32s which are lower than u64::MAX. + tokio::time::sleep(Duration::from_millis( + DEFAULT_SLEEP_TIME_MS_BETWEEN_RETRIES * + DEFAULT_SLEEP_EXP_BACKOFF_BETWEEN_RETRIES.pow(prev_iteration) as u64, + )) + .await; + prev_iteration = current_iteration; + } + let index = (starting_position + counter) % urls.len(); tracing::info!( target: LOG_TARGET, + attempt = current_iteration, index, url, "Trying to connect to next external relaychain node.", @@ -112,6 +144,8 @@ async fn connect_next_available_rpc_server( Err(err) => tracing::debug!(target: LOG_TARGET, url, ?err, "Unable to connect."), }; } + + tracing::error!(target: LOG_TARGET, "Retrying to connect to any external relaychain node failed."); Err(()) } @@ -431,9 +465,14 @@ impl ReconnectingWebsocketWorker { #[cfg(test)] mod test { - use super::url_to_string_with_port; + use std::time::Duration; + + use super::{url_to_string_with_port, ClientManager}; + use jsonrpsee::Methods; use url::Url; + const SERVER_STARTUP_DELAY_SECONDS: u64 = 10; + #[test] fn url_to_string_works() { let url = Url::parse("wss://something/path").unwrap(); @@ -460,4 +499,29 @@ mod test { url_to_string_with_port(url) ); } + + #[tokio::test] + // Testing the retry logic at full means increasing CI with half a minute according + // to the current logic, so lets test it best effort. + async fn client_manager_retry_logic() { + let port = portpicker::pick_unused_port().unwrap(); + let server = jsonrpsee::server::Server::builder() + .build(format!("0.0.0.0:{}", port)) + .await + .unwrap(); + + // Start the server. + let server = tokio::spawn(async { + tokio::time::sleep(Duration::from_secs(SERVER_STARTUP_DELAY_SECONDS)).await; + server.start(Methods::default()) + }); + + // Start the client. Not exitting right away with an error means it + // is handling gracefully received connections refused while the server + // is starting. + let res = ClientManager::new(vec![format!("ws://127.0.0.1:{}", port)]).await; + assert!(res.is_ok()); + + server.await.unwrap(); + } } diff --git a/cumulus/client/relay-chain-rpc-interface/src/rpc_client.rs b/cumulus/client/relay-chain-rpc-interface/src/rpc_client.rs index c7eaa45958b0b002a00ec8210d5044bd56aa0569..d8e5abaddc6b4364c286b53036be8c9e9446a31e 100644 --- a/cumulus/client/relay-chain-rpc-interface/src/rpc_client.rs +++ b/cumulus/client/relay-chain-rpc-interface/src/rpc_client.rs @@ -22,7 +22,8 @@ use jsonrpsee::{ core::{params::ArrayParams, ClientError as JsonRpseeError}, rpc_params, }; -use serde::de::DeserializeOwned; +use prometheus::Registry; +use serde::{de::DeserializeOwned, Serialize}; use serde_json::Value as JsonValue; use std::collections::{btree_map::BTreeMap, VecDeque}; use tokio::sync::mpsc::Sender as TokioSender; @@ -52,6 +53,7 @@ use sp_version::RuntimeVersion; use crate::{ light_client_worker::{build_smoldot_client, LightClientRpcWorker}, + metrics::RelaychainRpcMetrics, reconnecting_ws_client::ReconnectingWebsocketWorker, }; pub use url::Url; @@ -87,6 +89,7 @@ pub enum RpcDispatcherMessage { pub async fn create_client_and_start_worker( urls: Vec, task_manager: &mut TaskManager, + prometheus_registry: Option<&Registry>, ) -> RelayChainResult { let (worker, sender) = ReconnectingWebsocketWorker::new(urls).await; @@ -94,7 +97,7 @@ pub async fn create_client_and_start_worker( .spawn_essential_handle() .spawn("relay-chain-rpc-worker", None, worker.run()); - let client = RelayChainRpcClient::new(sender); + let client = RelayChainRpcClient::new(sender, prometheus_registry); Ok(client) } @@ -113,16 +116,21 @@ pub async fn create_client_and_start_light_client_worker( .spawn_essential_handle() .spawn("relay-light-client-worker", None, worker.run()); - let client = RelayChainRpcClient::new(sender); + // We'll not setup prometheus exporter metrics for the light client worker. + let client = RelayChainRpcClient::new(sender, None); Ok(client) } +#[derive(Serialize)] +struct PayloadToHex<'a>(#[serde(with = "sp_core::bytes")] &'a [u8]); + /// Client that maps RPC methods and deserializes results #[derive(Clone)] pub struct RelayChainRpcClient { /// Sender to send messages to the worker. worker_channel: TokioSender, + metrics: Option, } impl RelayChainRpcClient { @@ -130,8 +138,44 @@ impl RelayChainRpcClient { /// /// This client expects a channel connected to a worker that processes /// requests sent via this channel. - pub(crate) fn new(worker_channel: TokioSender) -> Self { - RelayChainRpcClient { worker_channel } + pub(crate) fn new( + worker_channel: TokioSender, + prometheus_registry: Option<&Registry>, + ) -> Self { + RelayChainRpcClient { + worker_channel, + metrics: prometheus_registry + .and_then(|inner| RelaychainRpcMetrics::register(inner).map_err(|err| { + tracing::warn!(target: LOG_TARGET, error = %err, "Unable to instantiate the RPC client metrics, continuing w/o metrics setup."); + }).ok()), + } + } + + /// Same as `call_remote_runtime_function` but work on encoded data + pub async fn call_remote_runtime_function_encoded( + &self, + method_name: &str, + hash: RelayHash, + payload: &[u8], + ) -> RelayChainResult { + let payload = PayloadToHex(payload); + + let params = rpc_params! { + method_name, + payload, + hash + }; + + self.request_tracing::("state_call", params, |err| { + tracing::trace!( + target: LOG_TARGET, + %method_name, + %hash, + error = %err, + "Error during call to 'state_call'.", + ); + }) + .await } /// Call a call to `state_call` rpc method. @@ -143,21 +187,8 @@ impl RelayChainRpcClient { ) -> RelayChainResult { let payload_bytes = payload.map_or(sp_core::Bytes(Vec::new()), |v| sp_core::Bytes(v.encode())); - let params = rpc_params! { - method_name, - payload_bytes, - hash - }; let res = self - .request_tracing::("state_call", params, |err| { - tracing::trace!( - target: LOG_TARGET, - %method_name, - %hash, - error = %err, - "Error during call to 'state_call'.", - ); - }) + .call_remote_runtime_function_encoded(method_name, hash, &payload_bytes) .await?; Decode::decode(&mut &*res.0).map_err(Into::into) } @@ -190,6 +221,8 @@ impl RelayChainRpcClient { R: DeserializeOwned + std::fmt::Debug, OR: Fn(&RelayChainError), { + let _timer = self.metrics.as_ref().map(|inner| inner.start_request_timer(method)); + let (tx, rx) = futures::channel::oneshot::channel(); let message = RpcDispatcherMessage::Request(method.into(), params, tx); diff --git a/cumulus/client/service/src/lib.rs b/cumulus/client/service/src/lib.rs index 7f656aabca7ac78a9bc4c35137ec56ed535fe362..92dc64371f34a9f147e7c5c5c2d48ff3ff4a547c 100644 --- a/cumulus/client/service/src/lib.rs +++ b/cumulus/client/service/src/lib.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Cumulus. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. @@ -40,7 +40,10 @@ use sc_consensus::{ use sc_network::{config::SyncMode, service::traits::NetworkService, NetworkBackend}; use sc_network_sync::SyncingService; use sc_network_transactions::TransactionsHandlerController; -use sc_service::{Configuration, NetworkStarter, SpawnTaskHandle, TaskManager, WarpSyncConfig}; +use sc_service::{ + build_polkadot_syncing_strategy, Configuration, NetworkStarter, SpawnTaskHandle, TaskManager, + WarpSyncConfig, +}; use sc_telemetry::{log, TelemetryWorkerHandle}; use sc_utils::mpsc::TracingUnboundedSender; use sp_api::ProvideRuntimeApi; @@ -370,6 +373,7 @@ pub async fn build_relay_chain_interface( cumulus_client_cli::RelayChainMode::ExternalRpc(rpc_target_urls) => build_minimal_relay_chain_node_with_rpc( relay_chain_config, + parachain_config.prometheus_registry(), task_manager, rpc_target_urls, ) @@ -425,7 +429,7 @@ pub struct BuildNetworkParams< pub async fn build_network<'a, Block, Client, RCInterface, IQ, Network>( BuildNetworkParams { parachain_config, - net_config, + mut net_config, client, transaction_pool, para_id, @@ -462,7 +466,7 @@ where IQ: ImportQueue + 'static, Network: NetworkBackend::Hash>, { - let warp_sync_params = match parachain_config.network.sync_mode { + let warp_sync_config = match parachain_config.network.sync_mode { SyncMode::Warp => { log::debug!(target: LOG_TARGET_SYNC, "waiting for announce block..."); @@ -493,9 +497,19 @@ where }, }; let metrics = Network::register_notification_metrics( - parachain_config.prometheus_config.as_ref().map(|cfg| &cfg.registry), + parachain_config.prometheus_config.as_ref().map(|config| &config.registry), ); + let syncing_strategy = build_polkadot_syncing_strategy( + parachain_config.protocol_id(), + parachain_config.chain_spec.fork_id(), + &mut net_config, + warp_sync_config, + client.clone(), + &spawn_handle, + parachain_config.prometheus_config.as_ref().map(|config| &config.registry), + )?; + sc_service::build_network(sc_service::BuildNetworkParams { config: parachain_config, net_config, @@ -504,7 +518,7 @@ where spawn_handle, import_queue, block_announce_validator_builder: Some(Box::new(move |_| block_announce_validator)), - warp_sync_config: warp_sync_params, + syncing_strategy, block_relay: None, metrics, }) diff --git a/cumulus/pallets/collator-selection/src/migration.rs b/cumulus/pallets/collator-selection/src/migration.rs index c52016948069a65c641fa014a390f1a6a85d3cb3..34f9142970820353bea352a21aab75f99b7827b9 100644 --- a/cumulus/pallets/collator-selection/src/migration.rs +++ b/cumulus/pallets/collator-selection/src/migration.rs @@ -1,18 +1,18 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Cumulus. -// Polkadot is free software: you can redistribute it and/or modify +// 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. -// Polkadot is distributed in the hope that it will be useful, +// 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 Polkadot. If not, see . +// along with Cumulus. If not, see . //! A module that is responsible for migration of storage for Collator Selection. diff --git a/cumulus/pallets/collator-selection/src/weights.rs b/cumulus/pallets/collator-selection/src/weights.rs index 12e6b755e9769cb4234bc15016c0a3b47650054f..0ac4a085754ab6384375767304ba9c87f418e439 100644 --- a/cumulus/pallets/collator-selection/src/weights.rs +++ b/cumulus/pallets/collator-selection/src/weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/pallets/dmp-queue/src/migration.rs b/cumulus/pallets/dmp-queue/src/migration.rs index b1945e8eb37b85e9c39cbafeba4b6d7ec15502c0..1b83fea710a3f344071c8c88e8561dee942fe578 100644 --- a/cumulus/pallets/dmp-queue/src/migration.rs +++ b/cumulus/pallets/dmp-queue/src/migration.rs @@ -1,18 +1,18 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Cumulus. -// Polkadot is free software: you can redistribute it and/or modify +// 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. -// Polkadot is distributed in the hope that it will be useful, +// 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 Polkadot. If not, see . +// along with Cumulus. If not, see . //! Migrates the storage from the previously deleted DMP pallet. diff --git a/cumulus/pallets/dmp-queue/src/mock.rs b/cumulus/pallets/dmp-queue/src/mock.rs index ed72ce678e3efa416df78b95f18c0115c91e2e1f..a46a6ba6c8bae03cd078d807bb60b8f1e1812902 100644 --- a/cumulus/pallets/dmp-queue/src/mock.rs +++ b/cumulus/pallets/dmp-queue/src/mock.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/pallets/dmp-queue/src/tests.rs b/cumulus/pallets/dmp-queue/src/tests.rs index a157d0584f2550bb9cacac730a0e942e3c91503e..70d542ea2ed2abe9f09573205560dc96bf5c3011 100644 --- a/cumulus/pallets/dmp-queue/src/tests.rs +++ b/cumulus/pallets/dmp-queue/src/tests.rs @@ -1,12 +1,12 @@ // Copyright Parity Technologies (UK) Ltd. // This file is part of Cumulus. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/cumulus/pallets/parachain-system/Cargo.toml b/cumulus/pallets/parachain-system/Cargo.toml index 30a232f01b3e5ca82eab239626e2255a683972ee..3cb0394c4b95431ca32a6e4e743a0598a54b8e37 100644 --- a/cumulus/pallets/parachain-system/Cargo.toml +++ b/cumulus/pallets/parachain-system/Cargo.toml @@ -49,7 +49,6 @@ cumulus-primitives-proof-size-hostfunction = { workspace = true } [dev-dependencies] assert_matches = { workspace = true } hex-literal = { workspace = true, default-features = true } -lazy_static = { workspace = true } trie-standardmap = { workspace = true } rand = { workspace = true, default-features = true } futures = { workspace = true } @@ -122,3 +121,5 @@ try-runtime = [ "polkadot-runtime-parachains/try-runtime", "sp-runtime/try-runtime", ] + +experimental-ump-signals = [] diff --git a/cumulus/pallets/parachain-system/src/benchmarking.rs b/cumulus/pallets/parachain-system/src/benchmarking.rs index 5cde8eb5b78857618d7e6a44f777fe7f19305929..8f97d12a48091ab1296d5adcd5d85eabc59027b9 100644 --- a/cumulus/pallets/parachain-system/src/benchmarking.rs +++ b/cumulus/pallets/parachain-system/src/benchmarking.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/pallets/parachain-system/src/lib.rs b/cumulus/pallets/parachain-system/src/lib.rs index bf136dc0644c4c124c9b7466da908af64892ed0d..98989a852b8dc5ad806564187ad93fe2d290dc83 100644 --- a/cumulus/pallets/parachain-system/src/lib.rs +++ b/cumulus/pallets/parachain-system/src/lib.rs @@ -31,12 +31,16 @@ extern crate alloc; use alloc::{collections::btree_map::BTreeMap, vec, vec::Vec}; use codec::{Decode, Encode}; -use core::cmp; +use core::{cmp, marker::PhantomData}; use cumulus_primitives_core::{ - relay_chain, AbridgedHostConfiguration, ChannelInfo, ChannelStatus, CollationInfo, - GetChannelInfo, InboundDownwardMessage, InboundHrmpMessage, ListChannelInfos, MessageSendError, + relay_chain::{ + self, + vstaging::{ClaimQueueOffset, CoreSelector}, + }, + AbridgedHostConfiguration, ChannelInfo, ChannelStatus, CollationInfo, GetChannelInfo, + InboundDownwardMessage, InboundHrmpMessage, ListChannelInfos, MessageSendError, OutboundHrmpMessage, ParaId, PersistedValidationData, UpwardMessage, UpwardMessageSender, - XcmpMessageHandler, XcmpMessageSource, + XcmpMessageHandler, XcmpMessageSource, DEFAULT_CLAIM_QUEUE_OFFSET, }; use cumulus_primitives_parachain_inherent::{MessageQueueChain, ParachainInherentData}; use frame_support::{ @@ -51,8 +55,9 @@ use frame_system::{ensure_none, ensure_root, pallet_prelude::HeaderFor}; use polkadot_parachain_primitives::primitives::RelayChainBlockNumber; use polkadot_runtime_parachains::FeeTracker; use scale_info::TypeInfo; +use sp_core::U256; use sp_runtime::{ - traits::{Block as BlockT, BlockNumberProvider, Hash}, + traits::{Block as BlockT, BlockNumberProvider, Hash, One}, BoundedSlice, FixedU128, RuntimeDebug, Saturating, }; use xcm::{latest::XcmHash, VersionedLocation, VersionedXcm}; @@ -186,6 +191,48 @@ pub mod ump_constants { pub const MESSAGE_SIZE_FEE_BASE: FixedU128 = FixedU128::from_rational(1, 1000); // 0.001 } +/// Trait for selecting the next core to build the candidate for. +pub trait SelectCore { + /// Core selector information for the current block. + fn selected_core() -> (CoreSelector, ClaimQueueOffset); + /// Core selector information for the next block. + fn select_next_core() -> (CoreSelector, ClaimQueueOffset); +} + +/// The default core selection policy. +pub struct DefaultCoreSelector(PhantomData); + +impl SelectCore for DefaultCoreSelector { + fn selected_core() -> (CoreSelector, ClaimQueueOffset) { + let core_selector: U256 = frame_system::Pallet::::block_number().into(); + + (CoreSelector(core_selector.byte(0)), ClaimQueueOffset(DEFAULT_CLAIM_QUEUE_OFFSET)) + } + + fn select_next_core() -> (CoreSelector, ClaimQueueOffset) { + let core_selector: U256 = (frame_system::Pallet::::block_number() + One::one()).into(); + + (CoreSelector(core_selector.byte(0)), ClaimQueueOffset(DEFAULT_CLAIM_QUEUE_OFFSET)) + } +} + +/// Core selection policy that builds on claim queue offset 1. +pub struct LookaheadCoreSelector(PhantomData); + +impl SelectCore for LookaheadCoreSelector { + fn selected_core() -> (CoreSelector, ClaimQueueOffset) { + let core_selector: U256 = frame_system::Pallet::::block_number().into(); + + (CoreSelector(core_selector.byte(0)), ClaimQueueOffset(1)) + } + + fn select_next_core() -> (CoreSelector, ClaimQueueOffset) { + let core_selector: U256 = (frame_system::Pallet::::block_number() + One::one()).into(); + + (CoreSelector(core_selector.byte(0)), ClaimQueueOffset(1)) + } +} + #[frame_support::pallet] pub mod pallet { use super::*; @@ -246,6 +293,9 @@ pub mod pallet { /// that collators aren't expected to have node versions that supply the included block /// in the relay-chain state proof. type ConsensusHook: ConsensusHook; + + /// Select core. + type SelectCore: SelectCore; } #[pallet::hooks] @@ -341,6 +391,11 @@ pub mod pallet { UpwardMessages::::put(&up[..num as usize]); *up = up.split_off(num as usize); + // Send the core selector UMP signal. This is experimental until relay chain + // validators are upgraded to handle ump signals. + #[cfg(feature = "experimental-ump-signals")] + Self::send_ump_signal(); + // If the total size of the pending messages is less than the threshold, // we decrease the fee factor, since the queue is less congested. // This makes delivery of new messages cheaper. @@ -366,7 +421,8 @@ pub mod pallet { let maximum_channels = host_config .hrmp_max_message_num_per_candidate - .min(>::take()) as usize; + .min(>::take()) + as usize; // Note: this internally calls the `GetChannelInfo` implementation for this // pallet, which draws on the `RelevantMessagingState`. That in turn has @@ -1372,6 +1428,11 @@ impl Pallet { } } + /// Returns the core selector for the next block. + pub fn core_selector() -> (CoreSelector, ClaimQueueOffset) { + T::SelectCore::select_next_core() + } + /// Set a custom head data that should be returned as result of `validate_block`. /// /// This will overwrite the head data that is returned as result of `validate_block` while @@ -1388,6 +1449,20 @@ impl Pallet { CustomValidationHeadData::::put(head_data); } + /// Send the ump signals + #[cfg(feature = "experimental-ump-signals")] + fn send_ump_signal() { + use cumulus_primitives_core::relay_chain::vstaging::{UMPSignal, UMP_SEPARATOR}; + + UpwardMessages::::mutate(|up| { + up.push(UMP_SEPARATOR); + + // Send the core selector signal. + let core_selector = T::SelectCore::selected_core(); + up.push(UMPSignal::SelectCore(core_selector.0, core_selector.1).encode()); + }); + } + /// Open HRMP channel for using it in benchmarks or tests. /// /// The caller assumes that the pallet will accept regular outbound message to the sibling @@ -1540,6 +1615,10 @@ impl UpwardMessageSender for Pallet { } impl InspectMessageQueues for Pallet { + fn clear_messages() { + PendingUpwardMessages::::kill(); + } + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { use xcm::prelude::*; @@ -1548,7 +1627,11 @@ impl InspectMessageQueues for Pallet { .map(|encoded_message| VersionedXcm::<()>::decode(&mut &encoded_message[..]).unwrap()) .collect(); - vec![(VersionedLocation::V4(Parent.into()), messages)] + if messages.is_empty() { + vec![] + } else { + vec![(VersionedLocation::from(Location::parent()), messages)] + } } } diff --git a/cumulus/pallets/parachain-system/src/mock.rs b/cumulus/pallets/parachain-system/src/mock.rs index b4d118aadf04dc4dcda50cec41362de65820e4db..1f5e4f4dbcf3c07a0e620f2a638916cd2928591b 100644 --- a/cumulus/pallets/parachain-system/src/mock.rs +++ b/cumulus/pallets/parachain-system/src/mock.rs @@ -64,7 +64,7 @@ parameter_types! { impl_version: 1, apis: sp_version::create_apis_vec!([]), transaction_version: 1, - state_version: 1, + system_version: 1, }; pub const ParachainId: ParaId = ParaId::new(200); pub const ReservedXcmpWeight: Weight = Weight::zero(); @@ -94,6 +94,7 @@ impl Config for Test { type CheckAssociatedRelayNumber = AnyRelayNumber; type ConsensusHook = TestConsensusHook; type WeightInfo = (); + type SelectCore = DefaultCoreSelector; } std::thread_local! { diff --git a/cumulus/pallets/parachain-system/src/tests.rs b/cumulus/pallets/parachain-system/src/tests.rs index 548231966e420f2c8a00642042657257e20f085d..23223627ebca2cf7bc197459bff9391edc7d4fd3 100755 --- a/cumulus/pallets/parachain-system/src/tests.rs +++ b/cumulus/pallets/parachain-system/src/tests.rs @@ -754,12 +754,8 @@ fn message_queue_chain() { #[test] #[cfg(not(feature = "runtime-benchmarks"))] fn receive_dmp() { - lazy_static::lazy_static! { - static ref MSG: InboundDownwardMessage = InboundDownwardMessage { - sent_at: 1, - msg: b"down".to_vec(), - }; - } + static MSG: std::sync::LazyLock = + std::sync::LazyLock::new(|| InboundDownwardMessage { sent_at: 1, msg: b"down".to_vec() }); BlockTests::new() .with_relay_sproof_builder(|_, relay_block_num, sproof| match relay_block_num { @@ -771,14 +767,14 @@ fn receive_dmp() { }) .with_inherent_data(|_, relay_block_num, data| match relay_block_num { 1 => { - data.downward_messages.push(MSG.clone()); + data.downward_messages.push((*MSG).clone()); }, _ => unreachable!(), }) .add(1, || { HANDLED_DMP_MESSAGES.with(|m| { let mut m = m.borrow_mut(); - assert_eq!(&*m, &[(MSG.msg.clone())]); + assert_eq!(&*m, &[MSG.msg.clone()]); m.clear(); }); }); diff --git a/cumulus/pallets/parachain-system/src/validate_block/implementation.rs b/cumulus/pallets/parachain-system/src/validate_block/implementation.rs index 42311ca9d8340d038df126175df096777cf989a9..c4c8440e5187dde760b686aae877403cdd00d579 100644 --- a/cumulus/pallets/parachain-system/src/validate_block/implementation.rs +++ b/cumulus/pallets/parachain-system/src/validate_block/implementation.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Substrate. +// This file is part of Cumulus. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/cumulus/pallets/parachain-system/src/validate_block/mod.rs b/cumulus/pallets/parachain-system/src/validate_block/mod.rs index 3a00d4d352a69830ff225221d2fb63450c96b9e9..2d210f4bac2ba85bceab2867ce03676c14ec3c17 100644 --- a/cumulus/pallets/parachain-system/src/validate_block/mod.rs +++ b/cumulus/pallets/parachain-system/src/validate_block/mod.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Substrate. +// This file is part of Cumulus. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/cumulus/pallets/parachain-system/src/validate_block/tests.rs b/cumulus/pallets/parachain-system/src/validate_block/tests.rs index a44d17507810099004113ce3ca30739c8ee3ddce..871ce5c1710e8dd6888e58cf075d972b006d2a64 100644 --- a/cumulus/pallets/parachain-system/src/validate_block/tests.rs +++ b/cumulus/pallets/parachain-system/src/validate_block/tests.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Substrate. +// This file is part of Cumulus. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/cumulus/pallets/parachain-system/src/validate_block/trie_cache.rs b/cumulus/pallets/parachain-system/src/validate_block/trie_cache.rs index 5999b3ce87f9d285412ea8bccdf1258ae1f66733..035541fb17b1e8fdac208c06ec337294db33b19e 100644 --- a/cumulus/pallets/parachain-system/src/validate_block/trie_cache.rs +++ b/cumulus/pallets/parachain-system/src/validate_block/trie_cache.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 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 19801340719526ac6775a9a92fc0b3d879f84afb..4a478d047f1bb1142f324c1a9adb9c729d808e7d 100644 --- a/cumulus/pallets/parachain-system/src/validate_block/trie_recorder.rs +++ b/cumulus/pallets/parachain-system/src/validate_block/trie_recorder.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Substrate. +// This file is part of Cumulus. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/cumulus/pallets/session-benchmarking/src/lib.rs b/cumulus/pallets/session-benchmarking/src/lib.rs index f5bfef006169035797d17957beb574200fd153ab..7d3a9ff70e7e57c23928c925bb975c43b96651c9 100644 --- a/cumulus/pallets/session-benchmarking/src/lib.rs +++ b/cumulus/pallets/session-benchmarking/src/lib.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/pallets/xcmp-queue/src/bridging.rs b/cumulus/pallets/xcmp-queue/src/bridging.rs index eff4a37b0cef7033c15f2629e54fb7763a35a975..8ed11505a27a991cf2f6e2ff2097bf886fca1ddb 100644 --- a/cumulus/pallets/xcmp-queue/src/bridging.rs +++ b/cumulus/pallets/xcmp-queue/src/bridging.rs @@ -15,48 +15,52 @@ use crate::{pallet, OutboundState}; use cumulus_primitives_core::ParaId; -use frame_support::pallet_prelude::Get; +use xcm::latest::prelude::*; /// Adapter implementation for `bp_xcm_bridge_hub_router::XcmChannelStatusProvider` which checks -/// both `OutboundXcmpStatus` and `InboundXcmpStatus` for defined `ParaId` if any of those is +/// both `OutboundXcmpStatus` and `InboundXcmpStatus` for defined `Location` if any of those is /// suspended. -pub struct InAndOutXcmpChannelStatusProvider( - core::marker::PhantomData<(SiblingBridgeHubParaId, Runtime)>, -); -impl, Runtime: crate::Config> - bp_xcm_bridge_hub_router::XcmChannelStatusProvider - for InAndOutXcmpChannelStatusProvider +pub struct InAndOutXcmpChannelStatusProvider(core::marker::PhantomData); +impl bp_xcm_bridge_hub_router::XcmChannelStatusProvider + for InAndOutXcmpChannelStatusProvider { - fn is_congested() -> bool { + fn is_congested(with: &Location) -> bool { + // handle congestion only for a sibling parachain locations. + let sibling_para_id: ParaId = match with.unpack() { + (_, [Parachain(para_id)]) => (*para_id).into(), + _ => return false, + }; + // if the inbound channel with recipient is suspended, it means that we are unable to - // receive congestion reports from the bridge hub. So we assume the bridge pipeline is - // congested too - if pallet::Pallet::::is_inbound_channel_suspended(SiblingBridgeHubParaId::get()) { + // receive congestion reports from the `with` location. So we assume the pipeline is + // congested too. + if pallet::Pallet::::is_inbound_channel_suspended(sibling_para_id) { return true } // if the outbound channel with recipient is suspended, it means that one of further - // bridge queues (e.g. bridge queue between two bridge hubs) is overloaded, so we shall + // queues (e.g. bridge queue between two bridge hubs) is overloaded, so we shall // take larger fee for our outbound messages - OutXcmpChannelStatusProvider::::is_congested() + OutXcmpChannelStatusProvider::::is_congested(with) } } /// Adapter implementation for `bp_xcm_bridge_hub_router::XcmChannelStatusProvider` which checks /// only `OutboundXcmpStatus` for defined `SiblingParaId` if is suspended. -pub struct OutXcmpChannelStatusProvider( - core::marker::PhantomData<(SiblingBridgeHubParaId, Runtime)>, -); -impl, Runtime: crate::Config> - bp_xcm_bridge_hub_router::XcmChannelStatusProvider - for OutXcmpChannelStatusProvider +pub struct OutXcmpChannelStatusProvider(core::marker::PhantomData); +impl bp_xcm_bridge_hub_router::XcmChannelStatusProvider + for OutXcmpChannelStatusProvider { - fn is_congested() -> bool { - let sibling_bridge_hub_id: ParaId = SiblingBridgeHubParaId::get(); + fn is_congested(with: &Location) -> bool { + // handle congestion only for a sibling parachain locations. + let sibling_para_id: ParaId = match with.unpack() { + (_, [Parachain(para_id)]) => (*para_id).into(), + _ => return false, + }; // let's find the channel's state with the sibling parachain, let Some((outbound_state, queued_pages)) = - pallet::Pallet::::outbound_channel_state(sibling_bridge_hub_id) + pallet::Pallet::::outbound_channel_state(sibling_para_id) else { return false }; diff --git a/cumulus/pallets/xcmp-queue/src/lib.rs b/cumulus/pallets/xcmp-queue/src/lib.rs index 8c4446a925d4da281d11f09c9e3e3e3b4feb6697..6bb7395f6553b2984ee2e36590dc42f6eb88785f 100644 --- a/cumulus/pallets/xcmp-queue/src/lib.rs +++ b/cumulus/pallets/xcmp-queue/src/lib.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Cumulus. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. @@ -1008,6 +1008,11 @@ impl SendXcm for Pallet { } impl InspectMessageQueues for Pallet { + fn clear_messages() { + // Best effort. + let _ = OutboundXcmpMessages::::clear(u32::MAX, None); + } + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { use xcm::prelude::*; diff --git a/cumulus/pallets/xcmp-queue/src/migration.rs b/cumulus/pallets/xcmp-queue/src/migration.rs index d0657aaea9fd0d1daab6003b9721434c8ea769e1..6e41c9d9a8789a742a1abc78d0364bed7916ed92 100644 --- a/cumulus/pallets/xcmp-queue/src/migration.rs +++ b/cumulus/pallets/xcmp-queue/src/migration.rs @@ -1,18 +1,18 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Cumulus. -// Polkadot is free software: you can redistribute it and/or modify +// 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. -// Polkadot is distributed in the hope that it will be useful, +// 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 Polkadot. If not, see . +// along with Cumulus. If not, see . //! A module that is responsible for migration of storage. diff --git a/cumulus/pallets/xcmp-queue/src/migration/v5.rs b/cumulus/pallets/xcmp-queue/src/migration/v5.rs index 818365f36f605ea3ddceceeb8654cf11ca690e44..0bf3303bd7d00c7518aa66164071a146d2548f1b 100644 --- a/cumulus/pallets/xcmp-queue/src/migration/v5.rs +++ b/cumulus/pallets/xcmp-queue/src/migration/v5.rs @@ -1,18 +1,18 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Cumulus. -// Polkadot is free software: you can redistribute it and/or modify +// 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. -// Polkadot is distributed in the hope that it will be useful, +// 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 Polkadot. If not, see . +// along with Cumulus. If not, see . //! Migrates the storage to version 5. diff --git a/cumulus/pallets/xcmp-queue/src/mock.rs b/cumulus/pallets/xcmp-queue/src/mock.rs index 348939de1f14613d3c1bb54b1d63d9c7bbf4cec6..3964ecf28cac62e06e6c75916dc24c6f34aa4c20 100644 --- a/cumulus/pallets/xcmp-queue/src/mock.rs +++ b/cumulus/pallets/xcmp-queue/src/mock.rs @@ -20,7 +20,7 @@ use cumulus_pallet_parachain_system::AnyRelayNumber; use cumulus_primitives_core::{ChannelInfo, IsSystem, ParaId}; use frame_support::{ derive_impl, parameter_types, - traits::{ConstU32, Everything, Nothing, OriginTrait}, + traits::{ConstU32, Everything, OriginTrait}, BoundedSlice, }; use frame_system::EnsureRoot; @@ -30,10 +30,6 @@ use sp_runtime::{ BuildStorage, }; use xcm::prelude::*; -use xcm_builder::{ - FixedWeightBounds, FrameTransactionalProcessor, FungibleAdapter, IsConcrete, NativeAsset, - ParentIsPreset, -}; use xcm_executor::traits::ConvertOrigin; type Block = frame_system::mocking::MockBlock; @@ -108,6 +104,7 @@ impl cumulus_pallet_parachain_system::Config for Test { type ReservedXcmpWeight = (); type CheckAssociatedRelayNumber = AnyRelayNumber; type ConsensusHook = cumulus_pallet_parachain_system::consensus_hook::ExpectParentIncluded; + type SelectCore = cumulus_pallet_parachain_system::DefaultCoreSelector; } parameter_types! { @@ -118,61 +115,6 @@ parameter_types! { pub const MaxAssetsIntoHolding: u32 = 64; } -/// Means for transacting assets on this chain. -pub type LocalAssetTransactor = FungibleAdapter< - // Use this currency: - Balances, - // Use this currency when it is a fungible asset matching the given location or name: - IsConcrete, - // Do a simple punn to convert an AccountId32 Location into a native chain 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. - (), ->; - -pub type LocationToAccountId = (ParentIsPreset,); - -pub struct XcmConfig; -impl xcm_executor::Config for XcmConfig { - type RuntimeCall = RuntimeCall; - type XcmSender = XcmRouter; - // How to withdraw and deposit an asset. - type AssetTransactor = LocalAssetTransactor; - type OriginConverter = (); - type IsReserve = NativeAsset; - type IsTeleporter = NativeAsset; - type UniversalLocation = UniversalLocation; - type Barrier = (); - type Weigher = FixedWeightBounds; - type Trader = (); - type ResponseHandler = (); - type AssetTrap = (); - type AssetClaims = (); - type SubscriptionService = (); - type PalletInstancesInfo = AllPalletsWithSystem; - type MaxAssetsIntoHolding = MaxAssetsIntoHolding; - type AssetLocker = (); - type AssetExchanger = (); - type FeeManager = (); - type MessageExporter = (); - type UniversalAliases = Nothing; - type CallDispatcher = RuntimeCall; - type SafeCallFilter = Everything; - type Aliasers = Nothing; - type TransactionalProcessor = FrameTransactionalProcessor; - type HrmpNewChannelOpenRequestHandler = (); - type HrmpChannelAcceptedHandler = (); - type HrmpChannelClosingHandler = (); - type XcmRecorder = (); -} - -pub type XcmRouter = ( - // XCMP to communicate with the sibling chains. - XcmpQueue, -); - pub struct SystemParachainAsSuperuser(PhantomData); impl ConvertOrigin for SystemParachainAsSuperuser diff --git a/cumulus/parachains/chain-specs/coretime-polkadot.json b/cumulus/parachains/chain-specs/coretime-polkadot.json index 806231db764671eeeddc50219962108856acd492..e4f947d2afc953eeb6c4607c8b17ae8ecfc9e01b 100644 --- a/cumulus/parachains/chain-specs/coretime-polkadot.json +++ b/cumulus/parachains/chain-specs/coretime-polkadot.json @@ -10,7 +10,9 @@ "/dns4/coretime-polkadot.boot.stake.plus/tcp/30332/wss/p2p/12D3KooWFJ2yBTKFKYwgKUjfY3F7XfaxHV8hY6fbJu5oMkpP7wZ9", "/dns4/coretime-polkadot.boot.stake.plus/tcp/31332/wss/p2p/12D3KooWCy5pToLafcQzPHn5kadxAftmF6Eh8ZJGPXhSeXSUDfjv", "/dns/coretime-polkadot-boot-ng.dwellir.com/tcp/443/wss/p2p/12D3KooWGpmytHjdthrkKgkXDZyKm9ABtJ2PtGk9NStJDG4pChy9", - "/dns/coretime-polkadot-boot-ng.dwellir.com/tcp/30361/p2p/12D3KooWGpmytHjdthrkKgkXDZyKm9ABtJ2PtGk9NStJDG4pChy9" + "/dns/coretime-polkadot-boot-ng.dwellir.com/tcp/30361/p2p/12D3KooWGpmytHjdthrkKgkXDZyKm9ABtJ2PtGk9NStJDG4pChy9", + "/dns/coretime-polkadot-bootnode.radiumblock.com/tcp/30333/p2p/12D3KooWFsQphSqvqjVyKcEdR1D7LPcXHqjmy6ASuJrTr5isk9JU", + "/dns/coretime-polkadot-bootnode.radiumblock.com/tcp/30336/wss/p2p/12D3KooWFsQphSqvqjVyKcEdR1D7LPcXHqjmy6ASuJrTr5isk9JU" ], "telemetryEndpoints": null, "protocolId": null, @@ -91,4 +93,4 @@ "childrenDefault": {} } } -} \ No newline at end of file +} diff --git a/cumulus/parachains/chain-specs/people-polkadot.json b/cumulus/parachains/chain-specs/people-polkadot.json index 6e30829eab49a10434a0191852267623c3cfc199..083c0fbf44a4ac6272dca80b80982532c976334b 100644 --- a/cumulus/parachains/chain-specs/people-polkadot.json +++ b/cumulus/parachains/chain-specs/people-polkadot.json @@ -6,7 +6,9 @@ "/dns/polkadot-people-connect-0.polkadot.io/tcp/30334/p2p/12D3KooWP7BoJ7nAF9QnsreN8Eft1yHNUhvhxFiQyKFEUePi9mu3", "/dns/polkadot-people-connect-1.polkadot.io/tcp/30334/p2p/12D3KooWSSfWY3fTGJvGkuNUNBSNVCdLLNJnwkZSNQt7GCRYXu4o", "/dns/polkadot-people-connect-0.polkadot.io/tcp/443/wss/p2p/12D3KooWP7BoJ7nAF9QnsreN8Eft1yHNUhvhxFiQyKFEUePi9mu3", - "/dns/polkadot-people-connect-1.polkadot.io/tcp/443/wss/p2p/12D3KooWSSfWY3fTGJvGkuNUNBSNVCdLLNJnwkZSNQt7GCRYXu4o" + "/dns/polkadot-people-connect-1.polkadot.io/tcp/443/wss/p2p/12D3KooWSSfWY3fTGJvGkuNUNBSNVCdLLNJnwkZSNQt7GCRYXu4o", + "/dns/people-polkadot-boot-ng.dwellir.com/tcp/443/wss/p2p/12D3KooWKMYu1L28TkDf1ooMW8D8PHcztLnjV3bausH9eiVTRUYN", + "/dns/people-polkadot-boot-ng.dwellir.com/tcp/30346/p2p/12D3KooWKMYu1L28TkDf1ooMW8D8PHcztLnjV3bausH9eiVTRUYN" ], "telemetryEndpoints": null, "protocolId": null, diff --git a/cumulus/parachains/chain-specs/shell-head-data b/cumulus/parachains/chain-specs/shell-head-data deleted file mode 100644 index 032a8c73e9398efe90b8e16dc2e96af0288a2041..0000000000000000000000000000000000000000 --- a/cumulus/parachains/chain-specs/shell-head-data +++ /dev/null @@ -1 +0,0 @@ -0x000000000000000000000000000000000000000000000000000000000000000000c1ef26b567de07159e4ecd415fbbb0340c56a09c4d72c82516d0f3bc2b782c8003170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c11131400 \ No newline at end of file diff --git a/cumulus/parachains/chain-specs/shell.json b/cumulus/parachains/chain-specs/shell.json deleted file mode 100644 index a02734316d32586bf1293f15eb1caabdf958ac41..0000000000000000000000000000000000000000 --- a/cumulus/parachains/chain-specs/shell.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "name": "Shell", - "id": "shell", - "chainType": "Live", - "bootNodes": [ - "/ip4/34.65.116.156/tcp/30334/p2p/12D3KooWMdwvej593sntpXcxpUaFcsjc1EpCr5CL1JMoKmEhgj1N", - "/ip4/34.65.105.127/tcp/30334/p2p/12D3KooWRywSWa2sQpcRuLhSeNSEs6bepLGgcdxFg8P7jtXRuiYf", - "/ip4/34.65.142.204/tcp/30334/p2p/12D3KooWDGnPd5PzgvcbSwXsCBN3kb1dWbu58sy6R7h4fJGnZtq5", - "/ip4/34.65.32.100/tcp/30334/p2p/12D3KooWSzHX7A3t6BwUQrq8R9ZVWLrfyYgkYLfpKMcRs14oFSgc" - ], - "telemetryEndpoints": null, - "protocolId": null, - "properties": null, - "relay_chain": "polkadot", - "para_id": 1000, - "consensusEngine": null, - "codeSubstitutes": {}, - "genesis": { - "raw": { - "top": { - "0x0d715f2646c8f85767b5d2764bb278264e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x0d715f2646c8f85767b5d2764bb2782604a74d81251e398fd8a0a4d55023bb3f": "0xe8030000", - "0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01", - "0x45323df7cc47150b3930e2666b0aa3134e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545", - "0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746b4def25cfda6ef3a00000000": "0x4545454545454545454545454545454545454545454545454545454545454545", - "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x08147368656c6c", - "0x79e2fe5d327165001f8232643023ed8b4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x3a636f6465": "0x52bc537646db8e0528b52ffd0058449e04ae95c504114c108066940e98db59ddf6af6efb57b7dde0b64469d187d03f0c9208338b0a9eb6f31f3d0dab728101fedf5603adc13d96d400acfcf610cbaa56a582dd86ca95fddffd6def4db6dc52ca94520a3b115310ff106bf4ed568cbaeacc358aede3df1a119009a9ebcf35e2d7a3228081c842f12de71abdbaa558cb26ed8b1e4708e99b4ff5c2375fd2f34d0ec57cfbd42f3fd5d97f5aa37e47cd7ed70809d6d09020c290c54f265f38c2ab0be9118597d725020bb23c10d6c13fb7f4884c8fa6e0f2cf298c88f5f3032ea882fcf98122e8e0495e70f043fae5cb1e607e1299610c252b7e1255b1c232c34fa21498e115066390603046942db480072de0c1b39330226be59f572cd8f2e5cb6b10269b007d9542fc7cf21222f843fae59b5318103d628114fe79931f283d51e9f9a75d5c15bce0350e599606d1a329c8f0cd3736e7f89cb121edeb96bb140b32b45fa273c6829cfff4c6a947537879760a23c23bf8e70f13c2fe56aaf2ec747295679dbdc5c9bfde04beba1e4db182576c487ff9f2ea456f9f1762e0c5dff89df20e8b6fb74194bf31b4cf6e71746e291684a5f3c586ac37395579a61ec5008cc780708bfa2ecea26fde116788c342c80199fb2c9bcc686ffec3968b43fae5cb97f5c688dcf8f56db1c98c2f80d87876587c47227e1bcf6ef1a5584721f676c680b09344d62b9ba61e51a1e589f85b4ef626fbed7de33bde788efa1b89ac572f72cb62581ca47e14d3de2e8daa8777b15d2e3e89a209e751c43c5174b9f82453acf90f6b9cea384248af7228e69b0ce53c117f93549cb7ab17bee72d97077f9746adc3bbf8a4270a232df8f2e5cb1720e7967bbadceec10ced97a702c5819cb3bae506f37c352a18ff62f39fb65ae4576fb1df0a5a40a6fec34f54cb154ca86fc2df8125fb46640ac6ab7754309ebfaf8efa86b7dc2587b79a9bb6b6ef06cc04418bfc1d950eaf1810ffc31cf78bd6c91675372bd6be15c380acbd415920f692b6d8715d310664eb21b082648d4d99a54264cd8378e5e2082578d7311959c20b07443d31deede0d3a4bc72e7a62ae2086946ed3be3875ec57d1d3e4128974076a2f7eb166cff61783dfb7ad9b7bb95eb12a2f7eccf15233274837de3642095cf7daee4bc74f8e45cb10428cb49b9bea85f5191e14501234268408dc378e709e0d3341d41013ecdd20f9fa66631824ffcce7ba878e744b04f03e9174db37ccdcf9ec7a1bc9ddba558107406c25e79c380ecb353d16a18037eb8abc818907576c682d8dde2d8bc00f6f5f5c586f4108c9243d639874f943bb7a8a85fe293f3acc67d894f7a7402162874e796553915a1577152fe288771163d152957cc882227505464a37f51685d5f31210cc52f9fb8bdbcfe50e41666b184406fb7ba1d6c87edf6a98eb323e51052de9473a7ffc3941482eeacc52cab2f224e5e6e294651de0dd961905b1b71dad7db39e2b4b3536e2d96809f57dd31295f5f8c083be50d849d72c5822877d0895ee5d65eaef225edcf61f4f29e5771ee3f77feded07e79e85647a2f7ed2f4e76de73bfa154ddd1fec38a0171de4fcea27f4ec5d92e049d72ab31204b2e5f42f4def22543eac5c8e88966be72a2eb496e5d975fd77579f94a95a7fd8a73bfafe8dc524cc872f9ca2d521c32c0aff240ef38f9a13b270571de3eb45fbe72c51450f49554ddc11810e750b6ff34f55cb4dad917c87e7bcb480bad686f1378d1dc6ab1a41863ba32d97a49bb302ada858025a472e894abf3450a42f2ca7f9814f515aa52b1c5b88709a99c1449bece0ee3dc87be1810e815ccc961d3c6c626dd1fde5db762f62dc58274653236e5b0491b164492b3e849fed316293aafaad8647fee3b27c526fbce2dd665caa92a3aab97bb6186fa7180f4438772bf8a2b8528af9c14f59dab8ffd175d79d41b9f7a5ad0107ce74328afe415a36c9a39afdebcbdc426e8ea2420fb904d0a23ce73edc2c8e883788b8a9372e896a5582567d1574e8a739fe430ce206bf4437bf4aa3c2487fed3a4b85e45f61f86d70e352acee64f0eed3b59f4f645611555898296216c59c31e62500620c52ce5129bd63336ad13914ddfebd7b7c7afaf17c127f7ebce27c5d245caafebb0cf022d11beae3a2b85f4a8d93b2220136a07e2e7af5bee52f5317bde1529a07a67b9e6b3e574f2edb33242cff04e5269ee1a9bbb280121bc80e50b907ac7f9c4f77c9ea4a88aa77c3a79e7aa3f5485987a2445099e689f924a348c1fea2f5ffe492a4fa83b29c2c20b509346f3897772b6cfead97fba23106fedd5a2ba6216d02b44015220be9d6c11376bc1b2f42aea1ad813285ef43376656f47cf0eaf8c08eb275b571680ba5a961220d3a328bc703eb1b39bf8c4cc5164f1dc40cccc4c58eb1b3d094b5f7566f6ecd6aecfeb797723ffd13779185f904f731faafa589f3dd97e076909db635ebec94c8cd5d7c67eacc9bbb87c930b467d2c59a31f32c0afb32b46a4192d262433faf58b4ffa6cf4ab9abdfe50d4cb70b0f4954f939d7d7d36b75aecc9d89bab2b46845da84965534bf9c345bc10b0940059f31e3e4dd8834f3333f169f6bc73e744f0a9bdf3129f144b1c8ede790efb309073e553ff2ce21dbbf31a3eb53b777ef169ddb9ea3829a4474e2c2540e67c9acdc4a7e9dfdeaec327fe3e7a62e5db2ff661a01dcf3f73be5d779358ce5567f67cbbe5e2beb5978bfad6628d4dce357feed8029a53fec396c5eaeae284ef5ce374578bd01563398b9e7565544ee7cd7f9a237417f5b7c50c3a773596d59762413a6c6a5ff7b891a38bd3dfb9d5578b952b06e52c7ae83fdc98aecc84269693a85fd9e49ce4253e356fcf69bb72ae937cafec9d677cda8dca266593e58b05d96f6cb2e4b49ce41bf5ad48d4cf6e2deb368c8bfa5c7ae7b07da67020e59dd9deb92acfcc79e7ed3f0d63e5a498c509ff3963aa32179bd61b9b9eab2f1654499c21944387d7b3532f2a16e42464137b566a51df61fb342323232323a07693fa689d6fef79fda1a85ae785a53e991e4141c6eb11145d78bc1e4191c55bba23d353a64750a8e0f5f5d91b1340760445153fdbb3c37508b57d947766f6eb33fb095f9dbdf1c9b9ab433ee9ab5b8d0535394446b98b945b7b51fec38c1951f4945b8b09794ec9f9fcb9c558d090987ffec32deebba8cf5850736b2f2a3e574cd9346b7e2dc59c9c4ebc732a4ef894bf389f2b9b28ffe917f5a938db8bfa8a3949d4cfa675d5e1b7ccc5a26193a9ebb3944fed13beba6ed46f7c6257b73aea73dcb7b8e37ee3d3babac557c7758b47d65ee38d91356f675f2c88afc5580e91ad5b0d0b5a5facc9c62676cb5d8bad2bd652d9c4fec3d65e1bf71563807e47fd22defd81a500c8ac18d599fecea7c6a676754b77648b05ad2b16645fd9d4fec31b954d137e7b0032f6762be393b249bdbd864febedcaa7fd097f66df6eed95f149bf5db1f51ffe618e3843e0b3c367893384bd397b9344fb2b954db3bdba6242d6594ef6f51fe6dd24584a808cbd5df9344bdfde5ee2937a7b0e9fd6db1bfb2c507bc627e5c2065cbe5df9a43f6bbe5d755a0af9eb0f45bb412c1522d323232e2ff3ca05942baf3296ea8b2293af58d410c553af58d250c6efab8da5a64c9db5ae7c9aea2b954deaea45bb332c3d329af24a59ca93a9b3742753f7b400e40553484f0e2f0e0f0caf0c4f0a2f0c4f0c8f0c8f0bef8bc7c5cbc2d3c20be361e169795b7867785f7860bc313c2abc2a3c2f1e973785f78497e5e9e04d7956bc123c285e94d782c782b782a7829782878207e59de0b9e0e1e0dde055f16cf06af068f0a4bc193c19bc183c2a1e0cde0b1c196e0e4e0e2e0eae8b0ba36de0d4e0b83838bca3ae82c570657066e829382f4d856687ae8223833b432ba3e9a1e5c119c1a5c0cdc0c9c061e170e09ce09ae098e08ee092e096e0b47057381ab81c3818b81fb42f4e076e05ae8a1b82abc201c149712570460e8ab60747859be25ce0b0b820b83b70154f8b77e525c19dc041816fd070683d683c7839705f7062682374111a8bb6d244e821b410d6047c85f6c25f3412d80aec054b81a7c050e02a70172be5c161cbb057582fb60a4b857542103adc97a5410420c001258a68200318b840050e70c4c7007a00bd2d1e1326400197078787d784352a7242822746208210380182261f2062b264a8092694782089d044f2a83c1f382dab048706c7056705e785ab4283d25cb059dc145c96b68246458bb25e78667026682960373430da96e685b78366a5a1a0c160a5d05ed088d058e0be7065383db83d74165a0c0d466fa1a1d05a68366c150d0637468761dde0a27052b827ac065e037771547058705758315c1d96097bb45dec11d60adb83a6413b01a381c3d81b6c171606cb86b685a6a55da159a179c158ec969dc24661b95828ec1316cb6ab149d8256c1b56080b84dd01a761b56c95cdc152591f2c0ff68ad5c16a61bfb0536c0956066b83adc1a260a378c010053c23210e28c2009cc5dad76d68a8b88f30416203459a40e2c812458a58db4a283952a40849f748920d2041a4e4031c284294c4912548945082a448911b07d332502638a0044991248a30c10125429430818408892449983800511247922461e20041740c8890587244c9910ec4a6429984e088501126458e28c14412479428c224044786286102091326471e70a35fa02040d2c42f3922b48489091089ad023d42848412489220d1a4c8910f1cf9400320a0c4910e7400090804d129502642491c592289964299102939b2e4c8124a90781b2941c2011cce832681848912244b889c38522404499a680289224b8e289104c907808492602209500edf413d5004092224944082e8c80264d80e2240920409244448942099c17250a1249848b2812247883e90c411278ecc301c74091307b8645a0c8a8b2b5bbc16597a6464f45bd4378fa7c053d826966aaaaa3a76765cb75de675ccddda39d78dd9355e755ba3b9c66b6d6b40d7dac2d71cc2ca44ea6632c176bb37cc6d9d73cbed9cd3e61cb3e55ceb1acc4d5114e51ac31aae063b08bbc1aa2184eb1aa9c11a4e9d53e8b4e1ba57c335e6edd61a1031ba95bb6e3b07371cadadd3f961d9ad5bc7bc8dd7ed2e4376ce396eae313be79a73ee6a55b5cbcddd5c63d7ad21ec6ecdb5065d7bf2b9768dd755ce6d7330ec5c734ea606e59c73d0ed6e3bc7aebb39e75a43ede61a352ad78ed9f1cdcd8dbb71cdb9beb9b971cceea6dbb91bbee11b66e76e9ccc0db36ce8fa4976ea5e2bb783ad9dd3d6ae356ce7dc3676ce717371bb3599d69c6539c78e1d376edc1c376e8d5d73ccce3576ceb9d6989949ce39b7aead6bcd39e77897dd4d63c76e97ddf23a6eccdc1caf73cd35e798991db773bcccbb8e1b6407e1766b8eb9b5b6636eae612355b09fc31a356ab4d620bb6dfddaed6ebba1baabbb3674b7edeebed696a276176aab6c5ccc3eac73eb761536754cb5d66ad4a84151356a3c2dbc2f8088b9c9a9219404121b489284091d383568d8a07183d5204a0209264a90584254840889073cc084a946c2e03039d20125482c91845472a4892690c8f1001222251f5822613840f4011f8e2c5184480992254d14498209120fe4501eb0c1478e2ce14091254c10219104134992209143c4c907acc7100128e204132020b2d1012548966082c4125e83680912444c80186209931f6e7488234c981c59d284079848a2c892234d9848e2c892224830512289234502207412c0104d247184898fcc12491021b181224b3041a20409074a1c40b2e44893220308800004308412478a3041d444089624f1038f2448847a6c80a0314309248e2c3185e088501122278e28d951d301206834004c88982071e2888e191dc209241c38b2a4899e98231d40620913444c8a289104c9124914214af20124387044891d9924479a1c4952c409243c7023eb0013498a744009920e748089243731479838cc12248e2449a2881347945082a4881224482c91040738c0441122278ec8264a8e34a60128a28409244c961c695284880926961451e2c892234a2c61a2c84900431419e2881012493650e40892254445889c38a2a38992232da31c82254c78e088075e1325477a0793c2eac489132745ce4911171539235ae4c44951512b6a468ad6c916151515751134a24545da46548d3871e2c4899366c449d13a71c2469cac13274eda48d1aa1175e2a4a88da813276c449dac91a275b2469c6cd11a71b24e8ab8883252b445ce48d116ad112d7a46b4a8888da81615151535235ab46ca4688b8a8ada48104cc5aaea7a8009121a2e45e634339ec9f88c9ca20424858c1457907cc6657cc6336fee3d7c82debc08f6d929ba3375bef9d53e0aa493882a5d58f1e29b2bef952fbef9ae20db572c5628e3e74ef9e633517ba75d2666517b27266aefc044ed1d2c7aefb493a25232199b2e277947a5642e925436919c47d6d625a36cbaa4b289e44057acf00987e4ed243de281976f6f5ff846464ff2c582487288acbd622d81ae586193be0e9175f7c238a18aaab6e942599bab363961cb99bd02b19c259aecc915eb2670a02bd88b78972c91edef12b8ed0ab2f61acedb97f0b3e9c7f34951ec3fbc649f88d7c7fa6c0ff5d43e9bb76fce42cd1b9bb8c9d9228c2ac8f67581843423a3c684ac91d1b733260452de24cc1a0cd715043090b187261481065528e37a40141518210e586c610578b0bcb0f8d5b57d8a7090072b6ab0050da290c31740a49fd9b6c0d22332c502041e3cf4cb9f966f57f551a90fe80ae37e17cf4e61014216df764ac6ed98cf9e87f149448ccf1e5cc4537ef934bde573c7b74f366a30dede660ed658e972dcf6e8671f7d674f92d743b71c143e350fe23fe3d0954f323edbc326e78cbc641e06f79b0cc53cc9b32c368771ba2851aaa892c591175bc2f0e2ca8042c514215c795d5858dc7750f814e3ed0f1be29eb0a93d46ce1a36b5c34807854ded30ee3b2c6353fb5a914d67f4ed333b220a36027afec358d427a910f3f2750a23e2e42f38841dcac97f79c3a03b287cbabcbd61500e61bfe42461436228df8611b1e4d03ec97fd8e2b7ca0de6c973e74f8a54a038d073cb455ebe5c5ca0179fb01190091ce80ae816d52e8a9a0f4212e9a22297e061ec2f5f3e8aa7e47402c543b7ae0b4621e85fbe2afa4939143e5144d867d153ff54e4775810033c94430678e81616448fbe32fa6a8fbe92437bf4d01d047250f8b4df4ec57d20c5b8819c917be2a03c2daf3f145d4eb888dbf2c10e7616efb6206bce25e7cdfb8e7b78d79939333232820248d95b182c054016447f9d075b5673238a18b07fa9dc1664edeaebbd3a4bded4f737eef76046147d8f572f4283560e91ed2bf6ee90f92f63a1bd7c734bf9f49e663c32cad5f43c328ab9c1a8f2501e44253ea73c73cb6141991c227b71bf31238afe492a501c28bac557e6835b3f6cf910f7ad6eddba45f5f1fa297f3b05e5696ef1e543d4b76c6cbce4945b950d6563e3038e5b7d598d01b9e1cf2dcb5d5cc31b46a41b23527373035e54d47f35eec309b27c3b8e5b7aaa22458164ca0514284f73956e56298a4746e38a05cd70561fcd5b4f9437d7fdd1595688e6357c9666f6975faeead3557aa7f9e53458107f1ade55da67b37496fdd15eac10cd1be635e42cddc89939afa9223b4bfbb4969b1a8ee398b796f6f9c19bb717fbe3798de3b496e835e4cc7eb69698b7f19bd6f2cd7f8894eed838155b4d361ea3eaceb4791baf89aa3cf3c6dbb8f2dbb417aa42d8f866d91fea433f0bac483a6ad2130da77c879e6828e53834dc2687460dcc88a2a7e18a01f1a7e17bd5d0701b0f2af90f6771daf0cc67e29cd9b711f7df73abc6860d973ed8b081831951f4365cd288356e131b9b6afcd988739f86cfc080d4d4c0d4c849c333b7f11fb6665c8a01a10183d1701bb7a1c165f029c79bb7119f70bc793f81f1cde28596175b0a9b9acbd855b0a9b98dd8534a578c4fa29bc081a2bc02c73dc729b76ce0e0386644d1e3780c1694e35676311684bf24935c8f33b49f7993f6cf8762fe49d9643f73c580646ef90f635ea3864f37de7cbfe053c99bef163ed978f305834f34bcd9f0192c888d1b6eddf0c582e00cedbb37693e780dc719dacff126ed87d6cbe3f850cce348559e89e3ec1846a4c67d904df65dcee7377e6144da716493fd1c399f673724ce101b8ee338522873cb6d48a11b27daaff11fd6b76af0ea4c0d07f3de87a89f133b0a9b9ae3c4a6824dadcac7bcb85eb0a9798db85ad8d4bc26ee176c6a7e13770b9b9a97e282c1a6e63671b9b0a9398db861c8c4b92fe3331890fdcda23ccd174a666253731a9f1175d8d4dca2713d9f37994f191fed9d1a9fd553aeea2388be8c53beeab3597aa739e535b0a0cdd2bc464e98c999cdcd42237d4f90355f9f9e05f531b45e9648fbcbc227f6e6eb059fb4a80f1dd2fe669aeccff84fcf447d99a89f45fd98381d26ce0b8bb3f4575c427d732b12edf773929c30fbe7959cd937dd91d6cdb5182587c87a5eb13541f6e4542ebfbebe59dae76708517847ff670852d8a4efe4843f37cb376f2f3e341a4bb05ef440e985da597d299f3849e927f4e73c9394fe4518350d1974e781d23bf6e7a3404df65736e981d23b29d4bc65c62db0f607490aee6e5e2696ee50f5f181f642eceda7e7dac5abef936c7d12f1eada9c04cac1166cd25f37f1e949202bba00cd1dafdd0e4b1d67d6bc01da855111d890072ee82086190035a9bdc3fc904fd5b367ed5354042098a08a315821c615b000554364fe3f9cb1152c981a9f30f399c5f82c659ef934bd9315f421a664939ba732e3d33974c83ea9190e877e7a0625a94461e333b2448a91b3f4995b7ed5c8093f6bd2bf84737d1a6e398f4c888a931ea02b287f5890f3e70ff249a8f98dafcfeb5fa41c260e399f45ef241320927291469c4e8e9e862f4644c69f62ce3952b1f1928b4c68b84c9c94d3f017677b1ab28af388876ed9c42b9e53f126d6786464347cbf3c948d05514c434a2a2467cc490fd015cead261d9845af847aa27ee745cf5810e743eb7084b4770ebfbdbb89fa36518160d4a321548133e4b9f3a742648ac54a95cf79c56245056f597ab93877975079d0c31ebefcf321f8a404503c93a26f4259ed827c1252179dab3cc65d9cfb316e597ac150eeec43e3ecce3e4e139d774c33333e342ee3ca3e364e79f6bc042939339bec394d54de7972967c262aef587c4dd374199fa599653ea18ff20ebb1372aeaf6c82693fdf979f31febe3c0dff61551f33e08ce734e27497aa8f49f9d07ce2631cbaaa0f279bdc7c8dcf9821fd43d09908cdf018179ae1ae49ff124e0e419fcf9bf42f41e3c921e8cf29777136e76f86bbd85e311a52d534673835430ed5482a53d0b814334e334323a728014941f219390504920273292e9722d31d76928cb4d82f9742752793133ecb79c4cf243d2b14e36b14239dc3384cc4e215b3de61b722293e41b2220747a0410c9c508529404e744b153d48f145185cd8c31540da3b45275593f3f96a3c3b64cf9dcff6ee9f53fedc0971fee4ec7744a8a1fdd7ee0d69b2ff248e10f8cf9350dfa49f7a429a3b39d70917b5132e6a4e5a1145cdd4c052b6948be8eeee6e539bd844013665a66faa995a6bdddddd5a6b3a408aa2288a821445398c8d2493003665ee280a42485110420829a7288a82d0298a720821a4ece05a6bad396fada9baa8950c11179b76db16f639e79cdb1fea412c8879d9313be7f8cdc1b5d65a6b6d9d7bad35fde65ec362e75c6bcef16e6b8dd739e75cb342fdbdf7de736d9923d8047d964c3b66cf37e79cfbf39ca63a5eebd452befeb005ec530bb1a0ddd57d8fa27c5755576a53942ff5de5b2aa32245edab1e5161801042081bb47864d53e5f5f29b4cefe7caec41142712577b009565e55105655554158555555418710c2ca21845e555505a5901e315362f4781763f1c8201674b1c9f90f13f16e07ef38f6c59ebf0765729abb258d59dd733def4cbde3dedb5d666ea6f6294dc8904fcf9d5f7c627ff18d9131b311454fe5400759bc8ed7231d404942c6ef783dca81174a613fac94cc12cf352d41afaaaa32b38c12cccb41bc5275b7bb7bd779d5555579950ddddddd3adf39bcd3aebeae657a2c69df5a6bae8fbabbdb7fb835190cb0a972483de7ddcde1d5aa0823851951f4b839bbc316b0df606bddad3933b7ee6eb1a9a5a0f0a15c7b2a1a79ef39f7babb1f15f89c73ce0ded33297ae8ea8da21646551ee72f3e1904346f4751d4a3de93cebd072184ef3de7ce39e764b0bbbbeb8a0595d8b4cc4e0602eccbbcdecdcccccdccbccbdc5a63e65edfdde57d3070ddddfd7cc21277c3f51f6ed72053e1999939a69d5b5395e95957bfd8d45aebf6b0a0d69a6badb564ee76ceb9eef6d6ecccccaddb3a397477773f570a0ba2562ad6dce291ad2b1694c3a65e7f6e5558d0934364ee614614bdf36f6c65a97573d75e74aeb5e65a6badbbb5f75e6badbdbbbbc9b9fdd4e0dab4b5d69a3f574cce22271573ee5c9b8c116c823e6129874fedcdc53746d6ceeeda150beafdf6acc61bc3396fcd2d8805393944b6eb199b9e53f1614614fd3e77cff9f3862d60dfbde7100b7aef518fffb96bcd397f8fa27eda397fae796badb926857a281384414a18a090d1e58bec150b1ca6f058c090e5f548075dfc34fdf3d22b1630e0a0e754b97a738b47065db1202aa41a6f8c0c6748f3ca2b295439e5cd6725718438a7dc3925a940712092845e4199c3a6b7165f3c32ca17830e1dcad9a042cab5653236656ca22e3651bea4413e153de50d836ca21c480b2e5002991ee5608bd7793dca81942f3a41d7b754171bb20fe5a49ef849c42f81b41003480b2e0d2590165cd8a44fb9bed5fc940369b18592405a6cf1e7c23ea5de79ae0fa405186ff1c880b4e8c2267dcb7a62b6a7dce29155aed86c5fb1f26916bdb2a9a288f8a15311488b30d8a40fa4851841405a6ce153d1eb33566a1fd83bcf5fef4c38e1cf920c90165fb0e9016901069bf4f52939f781b4d8c226fd1f7e4eb6a8319502ab5dd9312b06beaab78b47d65c63412d87c83aae2fe6a4e5ae2613a02f053e35e167665f229b5956c32722fee65c7a6e11c8fa507ff9f2e54bfbf5f62b71866c6b3977bf608002c0bd7261c5150664d61415b4155b7cceeb911538782be65931aa3ee67b72160da9bee37b3e295f399dbcb28985b4535878f996ef8967d72b44b1dae77cd68bef3bce7eaf95e815a27c2b21c093a36f7944952608e1052e9cf816c20b5cc07cc72a809c93461ac6a5578842a252020d03c888004f8ea0105ee0e25131c2318508591a15231f56d0032aa6f0cff526348c37a231062ca25883911190ca27d49d28c1410ebe0045194646404d1acdf695b38987804c19c6e0833d3c31320252ff612368bed909176d534fbaf9b27b94f376caf973ceedeb911555386fcd294f6b662eb4b706d547731ce7fbfc38cef5c88a19b46edfabf4ed4b7943efe7edf039f5dafb79777b7bcf0a295f62d3f3e69e8b8d4db0357f91041eac0a648ce1a00c2359052ef5c13b074b8fde7973eae3458843793f3f0e25f508075bc240049b1c0ec8f81e36395f4cd7fb154b1cb4fc621580a5f6516f9ff621b9f31eeac39507badb815e45c83bd0333e4d1bd83e25de81ee8e70c0e52711efdc467dec3b0b460c7abf1ee1400c5f6213f4a1fdf254d0507b551e48458735e551ffe117f79d7051e3de828c67c741958f6129d4de9c6bfbee73f64d40a64737e8f2edfa8c29c049c516d0ceedec3ffc6263d373c8fe1423d242fd82ec7b72b63fb728df37db295f6f5188a2b021cda90540a7e462434e3c29011446bfe43993a27f726882200f7af8f24c544b152a50f0953329fa4a3e392977fed33f0ce374e21b3604cae9c437a7dca2bce10c69fefc7973c5829e14723ef4c4cf0ef26473c680905e95673e778d0535576c08f5cddbd779bfbd1edda00caf6c52e569c5828aa87551ccb090646fdac1a7f6ed597b0dfb2cd0c3eb023034b05e8f6ec0822fe558aa71557595b074652e36b51acf02366ffe9aff6b4e80cc790f982dc1a7e745f0a9fd730a50f1cf31c03e0de8f5967fdac5bf1edffc87ab88f2cf73f8c4ffde7bcf55e74921f5c262505105142d4a24d32328471f9800b25972ae71bf5404309e3dc7b4e3d95d2cf5eea164e54a0e163678964550810e1664b0c1e2c90c2b70a08184192cac80315b09c2f3cdeb51118680b1c0722fc0b8dcaae2147a5e393bb7f89cddead6bcf2f6966e8528af8050ee60b5556b18665d72c220528cfa9890cab7f295b372c68248be9148e524392ba1e71c2b6fcd15a384dab5cdab284479d5a817a53698155dfbc0b0d87964ea1c557754cec89e5ca1eb4e767db0b89039027ef8d4ed10d0f2eded3dda67bfc856cef59e687271c73ab9a3874f2b658f49dfd43d507d8ab698220d5970010d46356003507ba63e5a06165b68c128082cd07205a8bda43e0640820eb410010f5860d102a8ddd587004673d88210aea8c1152f00b52b972960180cac766acdfca3adaeeeaaebca6555f51d9c80e6680c4380af475dc0c310b43c7b73f629d47c9e4037810341794513d29c65737df66efbde62931837825553bbd6045bc073aa3df1bc2a44fb8bed8b09e96fcc72497322f8cfd9c98bab3c6db5d5d630cf9f1cea6fcad3cdc9163d6e340c81cb6b6b150d82b0071504e10441a042082f00828b128c6106170c360b2860d8320413f4e00939a0d802042880812bf010c6d11562c0706bad41387fb882cbf55e8f9f1e45f4681fd3eb21a762e1cdeb5f768516601a34998a60d346f1de7befc99f1e6cda71bd62394215debd1e75218b1896eacc4cab0a8785e33d28701ca9421dc2404109a2d803171c62f8a97200051bae84e10628ac808b34e8cce08a296f6a96ea8a2990e480450a585460e906818b6a39a27d7eb6cc4c0fceec3f09683ef508016c5aa720c0a61b5052401ec12647c92198a3fee85121eef0fccc4db6cb1c9ff0464ffb5c3d50b51c018177e5d711c0a7f7627005964a08563016a0600858b0000428d0000c5cf8e10a1424014608650801ca5b4265056a6a168aeab1bab002e747b0699d73cfd7df83c20b25700db64fcf3273114515387bc50203287ef6b87665e818d0200b1da6008615f2600617482148e148085440404f8f9ad7a320d0e1ad572c52b04212a05073bbf31572fedc79db38df66915555054655f56c504ea8bb1fb16cf1623122cf574ea176f6f75cb11672de74e562ed8ac122b8ad858ba3d25a12625e8f80808246835e7af4f3c40545b8d2e50a25ccc04a1662152c7e4086e756a06d88a20b3dc8a18a28d4000baf22841f9ce19900075214010c528401873c74e1061743308ae2e57a3d42c3916b81c9d43e3d660f10a0a8b8c74f8f22d8b43e86a558f0f2b033343658ed597c4abcb3aecf67fbd51ae413cb8b4d0b79675b93bd5a7ab7dbdb77db2d5cc9668fdf1eed636a3e7f1003bbab8f7d37f5389fa6dfc1a6f51ebdb3ee28b5c2afcf1ffef9f52fea8f05a810ebef0760f8f7cf618d9c49741e09997e8f22ae963d3d8af841001618b8e00764d8d6b28f3384081674cd39e728caa773ce51ce398a72ce39aa41e79c7bce3de79e73cfbdd6f36a3ab57eddddedffde7bafbb5d7753cbdd989919a6b17639eb91a80ac28a223dcb5d0d6b188ed9ec523e75b70c0c7659a40ac6c0dc288ffa4f666687de4ead79f94737af6b4ebb9d7cfed34cc4dfaebb1dfb730a0b72ddcdce75777773ce756bedc4efb52fd5efbdf74edcaebb9b4fddddd4a57cea6e78299f5ebbeee69ecc170956555555d5d47992e7b08954c32692bbf2901cb2c939e79c73ce39e79c73ce39e7a073ceb9e69c738ec4a4162dab9ddc7b569c24276141fdde73ce39e7d8bdf71eeb2c75ad956c3d8715c29494c7c9536b7eafdf6386eff57befbdf7169e5ab3bf06dbe9f10f2a4ea2f6edee313333e4062f3629e5cc0dfa74ceb9766aed5a7bcd39e7dc73ceeddbaabbf9458a9d7bcebd76ee39f7d8b9e7dc6b90b44678c1523eb173563bb5e6e5f6e95c73ae39d79c6baeb7ef60299f7e9447bd881efdbadf7bfd5ebff7babbbb75de7bafdf7bfddeebd7ef3d2fd55cddafbbfb7577bfeeeed70dd94475bfee7eddefbd7eafdfebd7ef75f7d3d12162079bd4df7befb5a9bbfb7577eb58edd49a977f74bfeed6cddddddded6c5a66cea1dc724c62be2ebe2ebeae6e6d45a2fee7453ce5a7d6555531735744b089f21d6ca27c8b90ad9bd844b9b389726bb97372de7befbde7a512c9adb78e5b7763b78f99e173148cf355e579937ff092c5829c0f6692e38a99d939276bdeb6536b66fec1566bffdebbd844bdf71e33cb0bbeeea13c94bbe7449e0bb17337b7c7ddeccbed7193667bfdc3cdbedcdea31cd5989979c93e8c334911df41edeccccc8f99297d563bb5e6e51fce9dbb7e7beadef6c712b2e9f9be6efeb675fbf75cf7b5f75a77abb540acb01a9248241289e4ed44fd1042087777174228736ad8a49ead7abbd8a4edd49a7ff9072f376f3e77775b846cd2ddddd6e25cafd1eb44d6673bb5860b9db7652f35a165b5536b7e22e61fbc2fb46427917b2cc8676bae35d7d65dabf3e68238d984ac11dc1963145144114a7492e87cf74622d601ba62bdf9b60e37af143a608723306829030cda00061b3847a8f2ebd96afbf5a32348f1e11a05c4a00439b822062d9e1065073e58313c32a6e46241975bd6fe906a1ef19687e12d57cb31229a87b75c31215a86b7dc927b45211a06cdc3be10c5f2eb17112dc3af2b197ee5d07eb9e2be458aaa3cd0190baa547758569cebd0ab5d2cc87228c4f2950e89587225ce7a23495513f4954210b63ff659202eb2b6e1608390ad3fea417fada749a1c00333d8c1080d4798011072bc7d1a1033a41ef37a17cf5e536a1f2579d0e20e5a56a0451aa20031cef30ba0f0cceca28eff20ab5967c5f2822d7e7b5c2b839f482a00f2832a55d80296268c9105a8c637cfccc085511768bc726174f4d72b174630b0c1b71b28b02e74810a1a282a2f40b1a000010575b002961fd4200f35b8e9021cba80075e2abe7df392638912836fbe834fdb93063885e6072150c219ae2c01095788918312952750e9820d6aa81ce10c57a8410f5820031b2a50a8d0402606574a7087279801087140821168f8c00c7388998119b210421954d8028a2c9630834a16585a90061caf473ee0c1cf783d72039426342aba10842e1061e6f588ca171b90a1044be0010f64b085156a5ae0ae63dae1dd022dc30e34788309a8c0820dd440e3032d5ba05a0a5aaba6b0b3f495152ddfbc864fd637d752e9f5e80b3b5471810b82116568030fb650210b6080c30c0bbc7837b78bcd4239fb8b42943b0a5eae810186bb944fab65bf78bd9ae7137aee9cb121429e94d6bee7f0b53b2fdef973fe84d04e6df194432e52208477fedc92b96466646472689c9cd7bbf6cf55cee7559c101ba2efc56042a0bfc626ca5f9438247faeaf184902e1573659fc3fedae924e4f1109808091244e3c89524595cd824ffccdad9a8b54526f17364451b0c5f702da9d850979ee324861422035e3523e09c18b7d66b3663e6757afe29016820d6977de3021cf59ad79b539984b0b5fb2f8f6a237e2c13be7137314627a42dd771321cdc88885584984046d9d97504e048114bdb79cfccd511f52500ef553be00271bb50dc5d21d6dfa573cb276fb16eeca5939d562f31b3b7e1d12f1ebcfe9403e55555579f3169b3f742e399f59d55ec9092525fdf5f9c9099d9cd977ce6f739f06c4aeede1361a32f51baf473d88c1cbbc1ef5a07514625eb1c0800e5a7e7e7e10d0d323e707010930c2c6eb1111eaf0fc8ae50427d0f2034b75b6c8bca77da00ab688810fa280431d9a00862b3261384323c1a02a4214d994f7de7bed3d3047566ed01e0520c0a63da27d9cfb387904470134739eb17c61e5990b28c8f0cc7e049b588f544b0dfbfc24804debb326e7f7c9fad4a35f3f020214c0009fd8e6154b0bf4f034af474590c24ff85c5a572eb01e1b1c1a62c8b0cadb6c522456105d5f5d1a53585eb6290237378d2aba7523bda0b9e69e0adc738f7900e55d7bbd1eeda08c2f51d7c6b5c197d804b5065f6293ba4ae5610a1b5a786d5457aca1f2b0d71063e33eec6824d3a31d80f1f37a6e95fad80116bec4a655eab9d6bc104695479d17638105958643d52acb6a4a390d0a194f88c31eae70832b7c90830ccb196e20a1ccd894d5c0a6595653cac96a625e8f8a10c6b7d7232364f13373aa4fa0a4e79768497a1e5271ce91a83f480e69cfdeb60b4628e3f5c808620cf17a6404257c4ffb2c15bc5cde6d15ef7c960adf283fc87adac79b9c44ed5a34b9963aeeeddc5d15f7099b9c37783184f2c457d51a8d215ba36fd7b7c80f634d1025332ed9dc28efb4385fa37de2bc3d6ff03be6b0c995b864b3e7ddbe73237c6a3d32c295774ec4a7e6ce9dd81ffbce15feece9a959b4f8d42f1c68aa185c5c150bc88aac7776dd73e79ba4f4943b1ff6b9e3d54d5c4431e5655eb9888205afd80c2cd79834402bcb74a05ff8829730aad8c216cc4087981e64d1832e2ccbb22ce87a8382222d52e0458b0aa020450caa00d3833af8800595e556154d6caa7cfaebb069bd7d48beae59fb90a4ffac7101d0ccf9a6f30198d28323c0e1054448421d809a433e61df8e7c20e59bcb2108dfe44cd263da8dc1741f055aa762a9864deb2e664f5c874fcdb7a3b3699da30eefac2f1599005e8f765083a7f17ab483273ffd29275bb48e7282a5eae3c6f9f461961a8d29d323e6f99cf949fa1f7085609f90f269fdb856087618e5619f7d826d7fa80ad1827876929cfc3df687ae107346e7d92b39f9678f6787d1464dec1d7fe89aa68d67d3c4f10cd7a2a13cececd7fea85921d8d97fece8c127555393f3e6d987675f39d93fe3d393ecbc5f93eb1416d45ce325ed67f62c85f4a8c330e59d5bdaa31480b9f3ea33bede340567f65af3546c11b2a97915493103c2cfb2467966e84e73226ef094707cf399bd0fdff6c7b64bfbf2092d9fd9df9837de5179fec3a6f6a140ef3887979c30b34cbfe59d37394b1605da2704bae31c0a2e53bc73eb8277ce57799c73dcdf176453b5bcf3a964c0d4288ffe117c9a3320c0a729f44d4deb55bcd4b40e2354d33a159b9ad654935d40e0b76739f4c4b76f526aefcc357a824dce9dd8294eb668fbd9c0521d664bf747bb74f707437129e563adef3216443d48c549f13aa76207b20ed2ae26b662e31de78a5527ca1dc99d239124113f49e20ca9bcbd6513fef5ca2d8ed38de0dbb992aba6e78b0969bfbed890fecaad6d4f2c600951f6ff7c5588e78c09e95fb778a8bf94f195739cfccf37364c081b7d2597544e045f9587bdb1a02a36e1afdc525e6d18d2dc7e921cea378b1e4a29b2e6dc60e1905c7ffa93dc22c979c4535eb5cf06fdc5381fc9daa08a096c72f3cf553e49658ae65238d79a3b75eb4d96a47032eb1d752964a97794a57a916b5058aa33f7e89b6f955728304a2645bb402f4a8672f9d6b3581011385e0346cb9b7812d193240946cb497109e816c9ad38042184de91249d1c11710ea18b1347cefdcbadbd7060531fba2dc8a64c993b2588bafe15f1cd6bc40b7a8d77846ef1c87eb8fda76bdcd2abb1a092c358f292370c488d9766cce4b8b558d08dd7f069f3307be8aab309a27fe2d7a8779a433923f0d1e711df3e21f0ddae7fc91dfb3bdacfcb872c9f4fdcf0d97eb9894f42d04d7db90dbfdc86bbbb0d97433e481d28b2e6347cee94a17de83f8c139707cad3dc822d2fbf66e340bf2c877e794718673b8eff74431bfec36b64e3cd6bc869f3738d6c6cdcc9095b4f279ee4caa71cbf71cb2dc823533639a22779130ad027b975c493e4d06cd2bf04c99d5bae7fb9c721929324c999143d49326927872cbfbc864377de4344fb6ded458aee8a05b90d771127462a32eeed4c8abe25931a32c90dd9e4e67d701b52d53471e4900f92ca1498c3600e037ba7498149189702f3cca5889159ef349771293219e3d6e53ba57d64a491de691e23715cdb07465aaeed83c91b572ca78d348e9cb0869cd0923323c99939394b50b9cc9d7213f4ee90dd780f9b9a43572cc88df7e8374676e333bcdde29bbee19b19d978077a2936e555d4b7894b1c8d38fdafeb29ca6be24c72bdf39962c6a5909991c160fb4c11e352c0c4c0c064ed3345e652c46431313ee3cd4bed33054daeeca3bcb30dca37e6008a4b031628c87e795dc8a037b7164b80bee5442cf9ee90e10ca1bca24205df3cc87e796a4a4bf9e6aaaaaaea00b85db87cf3dddd5d0750fcac7c6bcecccc0e784d86976fdedded000744f2a32bad39a03907e59b3be7807e5a648539808148be259b4d8a031688e4d675591891ca29b72e388b9e9254482dca37a7248e90ca290f52c92dca53f43f1ce4798f808c6f0ee59b4cd2a03c03e5600bddb68a60a9cebca63c5f6ce2dedde71d1b0b6a5833fad6647c9bdda5d45cbe796f691fe74dce240d8ad562d6846c2f3671a97d9a94de61af9c54b2e42c5da576c4ce580318c849cb4befb03729ed43b925a753959c196c529eb7fc6cd9fa4fb7e6f2134a79bef8a4a441f98cb941e90665c7c8ae298f0d96eacc1c5e9ed2f26df190597ced95dea1dcd5220684fd391ff189ab406df1cda924e889b35047ead3c39b5359d46700de9cbaa23e4ede669b1284cf66a43eae4b1ed4d43e1b8c6fde5fd09323834d51d487086fdea4a88f00bc7973519f1e6fde5dd42700debcbda88f8f376f32d4878737ef2dea43086fb3870ed71ef5519faa79a63e9595de691d6fbeeac387376f7acae1cda19e4cdefcd21310dee44c426979caf90a9bf8e80e195ff9de42f2119bb2f0a93ae22ae8a97a429545cbb7bb2eed7371e91d32f874796193cb839eae39a82908e9bacc67f4edd594f6c9a4f48e95f6c9744c791c95956f6fbe978f48798e18849b220e0722e2f0f73653f83484d481c39beb70abafea884fd995ea097aca96a0a6f6ec28cbb75f5cda87664bef78e1134d976b0e7aa269839afae2322f32be3d93d23e3651b22aed639349c9aac49ab75a6bef5190a2288a8215849044b22ecbb2ae0bbb1666b5616c74c1306ce990c15c4b876ce244a14396b90573e9449cb7666af094de6997c252d8ca0c8c0a57a9f2ed5676e9c41bc934302a0c85a1d4d4d4cc94281d4d7a99b974a2cd5b3468dcf0133662a3196f8a520ce9a506169443ca2e65b0a97dc920e3db651741320b26e4c923e569b7c18e90577a0773c56c70b16e2e22e20ecdf388528bcc226564e44e5c2001c4f58251d92e5dbe77a202e950ad41b1c11ac040398ec8a82a9256241269a6479447f28a0f3ed8b821e546141b64d8f062a38b0d2e36b6d8c4687303c78ae344f101c4d5b2597a470e9ba5b2b1457de0709c28edb37395b89472302abb65b7d490b1bab0b03d3abaae9bec7bc41c56aae4e454507e30da02b3050cb657aec4c4d4e091e1604136bc688ece348c8d2e6ccab978643958504e17363565230c31d8d4578ed85c27c6bc4f944757ac54519eae5103e78651d699cd17365bd864a305270a0e15ea05c70798b789d2353659fce0ca3e3659f04efb0f3e8098a347cc718a39b225645766f8cc8e8c24193e0188d24a9529cad34eb385060db7b14146ef74a9e4ca3e36b8788b385268b0204ae2445921380db655aae0b0d172e335385170a2e0d011378b9a1a6cad58c9c180f0db01e7ca1171b84fdcf153d4e13a11fb00447924af482bca23233373038a8d30d8d46e8a36b8d06034d8c48972f4ed345bdac7464bef34cd966f0110c1c3468b966fa7a103475c1eb0a99d060d6ca774f10103c22fbbd8e1c6f5a64885c7858839ac284ff3c4c47843d813b784515923231c3678e24ae99d761b581025312a1ba5ddf2e1d289969487f9214aefb4036113a57dac097f905d62f540c4244b74216ac2cb134eb45b372e9d083f4754c248bbf5c3453d10d18804b49b22048ec81131c0a6f61c18052c9d6b88b8847fc889d10fe1168f6c8737c7e13a18111cde3c88f8c6c870b8b24987eff020fc87ad20220ed7dfe13ab0a01d72880c875b7bedc0a10333a2e871b862427048226463930ed9d3f3a38351e9d1a39f4dbcda0d4607e58d721d38ae46e1b872441dee03107358c951a577da858839a6288f4f8fb7d3b4a19fe4ed345d48de964f1a2f928cde69ef8936c2e09d0e6ad2061738643b6e60a631b2f51c56d4c78e89134576699f1d6f3f7accdd06171b5c5c474747103a31e775e8b074ae1c5187bb080037c5201c8848b9107187ebc4f6345ebe71a2e8d854691f9e56bd8ea4b233e52b0740fc014aefb4f344e9a577da71a234d9800a6847ca2e75c0b103874e10140e2c48070e1e59109e030b4ad2a0bc0ecf8105e9c0e1b341790aca377f4e1ca1bd337b4b15df5c75e890e9cc27a47cf3f9c4946fb3bb7c108e23ce240dca531e440e2c884927f69b22951d0762c76da2b48f0d2e6d33c586077c0ac2db6daaf04987b7db08814f948e4d946f7731eb9d76c5a8c0de9143b6414429069bda7544e945d2814ded36b874ebc81c1890f673d8d18151593964ebb384e3d2891c4410392e9db83b760071e9447d53c4a1824ded40441c296cca2171a2c421e3e236c2601f2906f360414dca2e6c6a5f2fd93a006e4a363466d4d000312393c598726070b84e24bdce741c519547a7c91f6ca67cbb8c536ef9f618674f21bb11a7c9469c37a67cbb0f71ca247c7b4e9c8d05274e538d387b0a373851bebd1467fb6993c5b7dbc4e934e26c2c33e2f49a386d8e666926ce1e99382516df9ed9e0327b0a3f6597d983c5097fdae0f2d08ab3942307df5ec569fa29bb643f6597676fd810ca790b3ef1153e7af0156b549c2b466901baa2492594966f6cd21c5e5e7fe875c21803c4e0c2ca936f3a7c7b0e174428e3db5b3bbb2bee9578e58b8276b5582158751a0b0d9a003eb7440b28df9e917174842e3fe333de6ec5d4382ba6c65ae8d4c2d0f8c388b477e6104ee8336e913022ce639cc280143d8cf1197d9a3829b77c264eca17e88a3551a8aadc79e53171167de5ec307156de228e906eb784780d9f28af714b7fc6691cc61a9f89599c459f79bbc5d7e5397cc27c557966c633a1b74c4e8c88738811810ee33f0d331865e28cf1e659148a719c21ce63dc798cc41902e395c3c81c2a50a23000906a00485814ba64c6a628547915219ba6734ad6909c6c51b7934356b9624778a128b9ce7276762b6aefb4e6c0903d578a924df8294751158c19253fc0ef7ca8bf7cf9407b27a9c651f080ce0b7573cb5db4dcd4c9e43ce233b72c57d6f92c4acbf7e9bce5304e9d871042084f98b367723a1a2a9227804f938822f814e399b32340c63367272293892698a8ca83456593a9e62dcb92ae9d14276cca71b64e0a2ab0e72dabb59e1e397bbe61707d609c2f3fa21d3b4c7c71c3c86096917c9abec55943e49a5b1b86cc397faebafb7c679d72ef5c0d203dd0f342ad5c5e6b65587037b625d2539eddb2009045797fe522075decc0e87b5eb9d001169e8b2358e141187f5dce050f54f0a6572e7610c6efe08bb7de0aa2959c456ff9f3cb2d1a7f951331e9c9c0640b2f1a2bf6803272421839b33d2ab9b4dc91e4029c53168dcb49dec4d81fce49b379a9e1b33d97b79aad85a2654a162947519e7df6780b4aefb0571811ebafca2b2c880ebfbcc427f59c4adb1b9a972b96b0204be640997066b3347d9aa03cfb1ef98e9b1c08cfe1385c479b188d0e3f79fc6a6fd86101804810009093bdfc453967c1cb7bccd0c8c06441f8e49194573e8750975b8ead00c06d08e1377adc03e07108973cfc076d624c1e7f3e9b9727b98e38f775788505b920a987f390b36708394d301e00395dc67be42cd1b8107266330e8029048f04008505b95cc8e2f18711b9fef21ead3d43e32a63398ce5992585900b009ed76e0af564b5473b3ec3e43480709c1c7e83c36d74bce42e363176449207116bb80e1deea87d968c1d39a149ce0c08394b39e4741c7ba4b347cfbe64b40f5bf941ce9e929c3d524e9329cae9ee72966ec8996536e49261cdc809218d9c59562a6510be9265e3d4e27846c36166b8cc8cd340bf7c562539e4b2fcc959916c6e7068cc98612bed4323674f8f8c9c26138c9cee999ca5d24a2b94b4f27c2521e22d9f4988983d4f5ae74a52be3d2a8730cb4aa54ace2444fce599acb1dede703989e4ed0dfb439f042f08af2bbab7665ced0d106659a95455b3a7874c424412229210f130323dcf5d7a877d96dc347bda726a616424110a86bb64dce57117ee7271171277e9f9e6854756c36d70dce291cdb8459344e7699cb1201ab72a27c5ca2d1e590d9b48a5cae291412ce8826c22491229cef875e333ae58d00d4663c969c4bf7ae2abd2cfb81589dcdf346e6dd5c1585e72c5822c1e99cd0cff611b0c08f4f1c14998bb981acf5cd927c7615cf934777ce53ec41ebdc39e136ba2f6ce53c7a2f60ee55654e59191b3e62bc78995d788ca26183975bef29b5899323973bef299ca69c4525436cd883672b68fc299a99ca527a7ffecf1a69f3d7a2eef2ace4684904bb642d9deb042382fe5687b43f3524283e99d5823aba43e9efa703f00e5619f37ad0b51eb6632d854521f9cf36ed5a053521f69c8980c3e4db642073ecd243dcdc5e002343e5595b3b3158bba4aea837ae630b2997d4507cb9994e5cc9aefa13cec4590e2acc948ce979c4bfce5a458c5997d55795579e556b45e18da9c9dbd58995724777ec524a5af2aafb9c0c82c974ddab3922cff6117db67318997672759243a3cfba2219bdc85bbb48f11edf2ec93bd3cec71c5154366f9bcde923571b6b79e932ce7973fe7964fe89625dd252d399df409a56539cfdc9272ee15277fe6d69b44add810e759e6165f4a919c82a9a8607e79e31315280e84f9252749ce85c188586fc56041649c44aaf4adaaf28dc227cb2b6f2ca822c50c0bb2646393564e13493e1361fcc54a52804d587c52e32670a018b72ce8975b15153f6351b03fa05f42ce2d223372f25b4e434e7eb751feba1c460b3abc3087ce2d877eb962432eafe45ca2c681fe9c647925a710cb2b2296574e013e91fcf285b23f2abf6455cd20390d16e4cabc0623723de9cd908f5443f218d27b3e83e43448cfedd15b5574cef9bbf65088f5d97317170a4d842e13f567228c5b7dcdca5d7549214bca3c49013639c7e293183781036155e5167fe69388a79c0ac6575ee3d92123eacfa410c92fb77c566eade58a2de0aafc49925712e7b97358b99b0ac643f9c6c82651ffe5ce05001d8b8d0500e5246a9808c64fe7375810cb852e09bd0646e47acb929349d1570e298ad27fff6608de724b9719668ee02df7965744dc5bee2dbeb47d9a57be9810ca2daf2c273de5517e12f15b5ef496cdd5245d86f961eb923812688eb9039a63b2e8ab38617b925b579c953ba7a0ff702c7a52d42d5f11f5537251b042506051b03ff4dd4679d73beb1d6b7a0a0d27dc9c2c74b2456c01e5412d9285599765d5bc057d96deb262e21b23836e798c6f9cfe3172882c26b6878958bce26591620539874dae897bc6825afb8dea9e870cf00516c12c87b145ca97bc44e373c73f9f3daf3e337ceadcf8cc7975cdaa29ddc84c6aefd0c4374646e3303030b1d1449352aedbf364d407e522f697f5a4ea2d775111bef52e2a526f511715dfbbb7aa7ecbbaa8c86f5d177551aa3cf3e6d961dee22b531fed3be7619cc66bf844e3fd68e410d9a389ed611a0b42f95053dd5fc3a71c36f1e7f0a9c6dbb319398b7ec661e2d479187fafe61f8dcfd2bf9c7ffeefc1b88d63f18d91d1f873cca988c921321a8cc65d79287f11d2c838c482c8c8c89c3364ed242c68c6170302e334fca76762c96184e83c0dffe11a3ed9784b253a4fc36d6c3c475299e10f9b512375d894a90ffdf63d4356c3de9ea90f8a1dc65f9cd9bff634f0697c5e4fe334723eb79c8a337bca2d99b83ff31c73c582c44498388b1ec61703f29ce43f0d132fd9a43d09632cc892436424d6b74892a87d55c326f67fb149d84eb668a691aa7074042831f85695512abe4b398bb9d89447a505323dca828c7f3dca220effe48a751c99d7a32cb800654646464640ecbb07cb6a6f27eff8c3a649397421ea63312094439e45c00f9bb8c1e5236c71be864c1f3ae5950329fa1edc2ecbdaab41c711d2daf76be530f22b26a4728bba5cb4aab8a4b2aa0bba4572cb2debb965412771f94a0eddb0e2e4b7e29bfb503677a90f26fb9437365dad8528cfbab5ef044a7ab4243d7eb9c716babb9b77db17dddf60f7362fb4d6f44b16d7dc77bb5f6f0668dd2d4bfbcf92146b28e695676760e9bebecae6263e95d8739c8ae71c366dc6a667bfda39a425a874aa96e973450c3ac790a1119a49002883150030301c128a8603124d54541f14000f93a65c56210a644994a3380a520a19c3082084000018112198216d00c95b065e65fd3cc27551dc8222c2552c37d1aac814fe97141a819b4296c99a44d0c5570b689f0544a4bd23e18e8d1de0f2f00bdff97c3598eb29760dc5af787fd2b9959a729a1acd1aa6e45a94c3c7897b6f483a0f1010f2d64052fc3a4ada9e639b0da1e617bb9b42bf2eb277d1acd271d20b9556c18b948106eda9a18de22bcb0a15b194f06e10a61de741ad0f151da3b048c1c4b43df4099d71e353bb50854bb6cf3c9dcf7e3dd9dbba2e8e9e73fd634e960f2d54b9d128afcfa2206ae5474fe07b6aaa7dc04ede8eadd8a4fb2dfc22bcc7b6218ab771148e8007c92b763917a6835dde339e1a1609dfc4fefedf14b48fcae013c9bb39db61c14a2fceb3ee6cc2edb17482c4934700f2c4866012a29df3dc655ac9d9b371d56678f6fc5cda5524777c83b7f612634608a35ca2a35908e8fc83ca2d01143801a4d3707e66d119998ef43a1312ee7eaf2fba9167aec609dba9dd5b69ade8d889b1a839e9fc82ee392bf60daa51b89da1e95f00ca90a99f8f4508c8ce0e3af75b18eb669a12f06f03f340ca157ad6b61c9d5f6e5723f2377c8ca82b82698eb0bc647d64524177f027205d4762662f046bde317b77e2c8de792c9c6a469dc80f3c5d50139cc255d437e030ecddcbed9035a3addc125b3650ab2e3127aee0be17362517e2322e430d31cea3ec7fb2cab225bfd434b3746227fe9a3e9b94bb6ca7fdfc697e48d7387074b893b61d2aff82fe7c222705bc7ab362635b97278c83fb29043f44a20edae8718372c329b64f74c8712c1ffba292ddc8debc6a72bd8a1d698905bdd374fc6109efa95dcc98428ada3c5b048cc4803df212ae7fb7e2a93c2e12c65092f4a2d0113a06942f67f231246d795d697878791937e83ddaaceb8ed7c5a764eec98b6fa08a81eaa2aa744fb1865f170681fc61ebb6170689cf6f6f80a529d33d0aeaf7e85ae3f07582c975ec1f2796b593b32579ddc1a57e0cf17cb8b2ba56c660b380f9e7e40decbe7aa63df7b02ce0ddcdf998ee8a076a0bd8db5ddf8fb4cdae74573cac0d542da0509eab91e79a852d465c5276c92f23d3460189e68a863753bd0a73d377d53c552f4059583f133b429434d918a0a0ac5a71c269f1d43bbad42ed93ed7e2a4b3397227c9eb937a2110a748771a822cb5d68d3430e271100e8760a0163db5e10783e00d16663a580ef36761447b7b8bd40611fe14a60fefba6b509d07f98b45cf40b246e5f967b3fd3e61d94cdc46c54d22eef565e27eb21108d8cccda5bc934a2419020efc5ffb40d463a89f09d03b9060ecd01b890d3503c279f1e0ec57dd45d8d3727c45a1475e9492a04678c1a5c7fba5f3c3c79bdb2be78e775ac81bb5299db2f8aad78770bf84080e0d4aded3f782dff3558977ffee8778f6be59e4294b1c6432aff8ddfe8538d7972502f7faf038f20423fe92fbd57f254184595bcd88267cfbf783295a5eb73efd4f2071f87ee7a778299c8a5e030295f31a7b446c182d44b43ba612551e4409543513253198f101176dc37213d22e54f5b7088b84924c0e4364f1fc2c4a1426fe562f31d12f9c7eb48f2eed985b879d096163cc379a3776f723f23470bb1ea7a6b7e7c0c897d1f1eaa1806239a600e00f716493fba261013804acfb6bf72eae4d19f63a8b9d7db623e93af8f9505208207081e2bda73650eedfa9208f9595dd773cf09a5114e44ff43e785eebaf893f1195500c4bb8b2071213617f0f1939ff25d742fcb45e0ea2c246821a9039513c854bb410ae2c2e743cdc2bc4e30f21bb287d6b7722bbe98cbf2ac0a344b0cbd4e955321e0875b00e752c4b6004988b6af3b9e5638161b9965e2d939410ef8e4c6da6326deb54dd8a4882de02961ddbf50d020cc0a0d2dabd2155e43cb6d29033751f1c9ac910250413f763be3221926e8c201d13451e81c79de799e9ede865bb874f062cbbcb3ae51221105a7ed6b379ba2cd2962cdfb7e1bb0c622d8e8cc88ada46fa45db7d59165720ca69f97b99c791e92ab4508c1ce288290fa1e20367841ff2ed32b8e73c94531fe7eef51dd680000716e1d5d8e881c742e112f10b24bba22972bc0005a0ac761714af9f24629c5a6e3feca756901ddbad75f8a42eae95dae7fa4c61e6eae4e89209e75cc028abcb396d6c14b75d1069d1c70443fc2065093e1679e0422fa20f88b8641992a139f4c956587c48ac870cdfd9699b6606ebe52762757d222a2b0b526101fa775e9ff5ffc7673a13837ae31461a5e4ae140460a6c4c7235097ba548b8f43e77d49c1c5641cf8b9f888adecb8bfb01ea0a033a7d43d3ce4b67754c5bc90094cacf3af48cd2fdbfbaff6c77313502ff851819791bfa70c2776341098c06d3ebd61649ef8d0bf317734afaf83a21163073428ba0c2dc3e6e888e81201972d544e0288dc4ef4ae3d2e78b00604f60b6c014a38628c3b958647318ba032976c4ad903af44a2ddf153fad3967005d58e846a4091ec827d20527e1e36dd76f57ee4c8dcdde9cf8bbdebc8e78e870124da16d1819f6ac93c01a58e456dfde329c5311a1af599a1abbbef95024f36a3e3aa600df54f67364b7c7f457c143450ba0e448af44558f3059e0df615a595d11f115be2b240670fe442a47251fc81ef497e3d478518a30ca0be474fc1ec000e6044205a2312b69c8c011875f496a8e316cd95c513dd9a11db7619904c666dd14f8ac6430fc82c896d95169f408364e923758985fb4692e6a50865371d35deb7798acd8a4110c96979ca6a1fc0cef3f1429345fad6c8fee0a30eac32c72b7968b8ffdc8ea684b5120020e05cbb77e8e28c3c4fa8582d11594d7f60ba1fe7fa2606334069c82843c748909e34e6a5dcd36f5219053ef6227d229ef220809aa18ff7cdd05d7f442cd688e8f46c0dd5404639b7a2b5dcefa5a5c044f68a48d35a4688e51b7681125b3055f971aefe47c648f5669c2925f6397ce1d51ab2645e2e0dbb87717947535e30dcc852264e1bf4cffc90a1a88740685b008b7d0268b577aa56e8ece9ac84962a11c4621e0310f14420da3bdfbe2e2ded15bfdb33c4f69f382b4ef740dcab7c03531a83518c56cac97d60cdc519870c729234911528c866e2929b33af148a66e38ec44943dee2609b72655b35315f2cb43625aa59eb9c70eb7c8cf617c9684e51c933622f87c2312c695881800c2211b9ffb4888b2ce340c401df4c087b47aa738a33092ddd0f4f1f4080c634bed35295050684a98dafc419048af85c6ee267ef94cf640aa02a54a641555ec9b7d245ed862ce6931000e8c7750a2520403887711d6a798b42be5e5e86181de50520a24c1e14c8233d1d4676b1fbd5de487d756e615dc7797253ac84d5a621958600a7166902ec23c657053d3dd189fddb7bf59181392c5c9b02f685e1784c8e5dc120027bddd10d3684462706aae145e52798b45f7e7bce1afd65c3ccbe9cd16a14d78611a3a60617e570819186b17c87308c63bb6df3f38cf3f0fb5bf1740c1a8c23b3ca5a3fda3dad681c8459d00310f58276fe0cf3e8501c32b87bc8af436d8c56005daf7897a5a868853dd41c06fe59b76082af400681235a6179a516607fa3aa6ab15074af0c444bdc846de40e746fe7673115a3e49441aed21ace0eee69aa4536aab617054c3f41af2468db3f4eedfdb972fe40222f65c0511d84e4983e7f793c6effeaa93ea06bd613232671ac02f409ef430e9192962a5b9359531609b4b384aa846eec0f662574624df5968013ae8835aab275586f64ac1de2f12e0457a1e76f851de4b015bdfe559d787501785218b923550d87a09f191e2b4f0ad588d80150510b9f6de8bf57cc108ba1588bd5cb9e81a803a1ad1e0b7fd34773b40978fc6e90653a0ea039ea01a2b53710334d1febde6d5765490b36000a5f4731e55a74d03cf3a967469260fc08ed094e51fdb1d9b667be2fb28108b3f04498e27e4f5f672a6ee52c0e8d11420e372aa6907418c18d55b984dec68ea6aeaba7bd73328ab26e9168b6a31e93d7216a01d209fc9d5fcfc051086eac9dbcd8879bc876b724a39be2b08f27ad117f7d2053267a919fc3acdc70002c15590ffb60a2487b0e5a3bd0c12efd5d66a2bc450c03ce000aaf97d2da5f9c0a2ab897a1e029b40ad09ef8290f7c4c1f594d3481a8f04133c49bea27d9aff27261cc3bee8c73217e55d22af784772b28d180ed6bc50d76779be2460b21e8fb10c215f2207ec98e94d07683a5f07396de7962cb9cc3bd40ad8949e9c2d24f1bb9bdd47eac0c4430963300413fa5cb1c18ba4d74b69e5d745de62c0afa8052cb2e50053e0173bcf1397d289c4e7627891144ab5226d35c1f2c02bc2583d77bbb368d11acbb6c2607f8396be26c9bdaa1ff8e0fca481ca0c956f2b97c224f66c9d93a7438a63a80c9e9c694310eb134c3f12eb6197080dda9e6b6ad46b6d06bed7c515fc6107eaf98b59f5a2e80518c2e04cf1248a48b1ae2d16a090562b54e2d8792cb217a3994608e68f9a78b08dc32b0f7d8ae284460667f965a5ff929cfdc9c5e0b3bccdcde0a1fe53c200c5e36099523e4e469c6580e751dd83cb1b2562cf8e609fc22a377d8c293c1bb4ad68efdd634c12254343f635131eafcfc2910e1df08181697b2e9cb2ce11ff651f68a67a89d5afb941516da95854959d2aa08890b91bbfed95c73f5f15b5d2fa2be4d1fcbd9f0193420d196f75141673c311ff6a28d599693b5618e73f6906046f2d04192eb46a045e8410b45494fd992b5397acf5df0f5f3db052a86762845184f604412402924e32ecaa18aaa80e0939d70be740c51672381539c9d33825759df62df1af3a25ac66dd4fafd9aa3ecdfa786a72cd0e47e662969d16b0aa1565d08b81834b7868fa037514cc3d6d4e637922e36cea616e632dd964c622cdf913a7e38cbd823146e5649caa83f96818ddd0097dfb1627125ff0aeb618462c218e2d5e53a912e0c00b157a69d97e01136431f96e2f9b9802a809404ea9e63c73a76acbef12f9ffbde0b3be1085427b4e6456a3ad3feba1c7173bc841f5c40dc0ac0f109160de47726a5b82a7ef9b0f21e6244e08404c450a6d6a88924852574889d41ee98d84b17150d222abf629c99b95e83e72482f09a3ee4a1856e9b9a3b439e0175d4e1dc9dfdb843ee0932e4a1607ecb5ea63380731946e1019d98485f0686e9c194a87972e5fa6f63392dff881624b96969e886c65c5758dbe4dd9a9d854b88d0d31c9a482b322f0bbde7655ed1dccfc59e1dc09f79880951a1ddc463bc0c0e07f2a2f78621c1e83b2a0c8d7ec916927267f652b82cc97d4a097910c89f020ca1ad881cc4c1601d52d63874b36efce62d87a55c238a61ebdffb6d25adac0329ef29688ecd6272283834db18e51fe93f054cd28d298db18e577da81524da2141d0cc9b9e28b250e6b7f2a1be7f410cc28f71f41e64d31782ddebeb2d4a3f695474aa1be179d9c4971574702b334aa00f1d4541d79a87e3fe9e7b3f358f0de1f347264226f9405ba8fe9af2c090c7479aa736cb74621bdf65618fb656774c38b047eb8c783d6fa9d359d74da4fd82383e6b6c7d7316469bd79786341b0db05a863a7af0cf299c600940cf010bc39c201e778b73ec8634f28c478470d9512d9c6d47e0a2e3f9ec306cc3d402397bb3d3479ffafd720adc480529777b07933396cc5d7ab10065efedafd71932603990fc284112916a92dc7d32745a477c17faa0e7de3db592d92c8439ab364e5cd4309fcd34d00d88b8b89d9d0293092a057addb393014f73e529724e5039485b370ca8baad5cb02629974c0adbd11e06b0258f84816bc6d4d9739aaf13d42365da7264cf14d407c2fe01da8f598cdbf5cacddc99110814174f514b70c9e3fdcf92e841d97c95b8d9a01da7bcf61c4a83fd91969177b099488847579ed9315056cd824fe5ba453437f5afcf6a967bfff076c3a4bb2ca150b5401b9c15eabe193ce7156842280a28e13378ce048a0971f55e7139b93a0acc0918dd28dfa4a84c06a2a51a0005ad9eb0cde939c37987c1050c9e0956de52403d37a4e92143abe92950f6d44e4ea3355b386230a549dd59d23f86daf9d6839d78c30aac71ce5e62524c81cedf922e9c76c5e2fe91546189694da853ec5440cbc870204203ebf520834b1841b574d878c48052217c6c90cf2c380a82cb03bb8f81b65c626e0b0b06028bdee617b22e1138401cb4d0e47d918276180eff010b0ff1959c813db1710b5328f5c419eb61f176d54b4ec01f6777cb8dbddf96f7c598e3704fb368b0ebb202c8438f0a9750060df0285a261e348da01d2f1d6f6c9c59af9bcabc1aa4338efe7a06ab64d5543dc20ba1fcff0e4e38663401e49bd4953dd61f9fa8851cf7e30a15a0223caf425312306165c06eb5efac732425de3ee9c51d567565ddf87b2b711d2f97c5eb542f86ed2b450ab189fb8b988749db7f89a2c2603bee732b80c338ceb36cd7fae73a61e1d407a04150cd62bd19d03f30371b91a6fd16ce71340890196b10abd3763bd1901b7743f8f59bfded775b1c7b499e7b1ee3a49e0c6dd495b58e868ac7f3b004be129d8920ad9c701a4a1144d78ff83816b0780ae73324c2ef8a7b3ec9cce297407bed7d268664e14240cc68f4d2af290066440b8abc62148f390175d54401987a48b24c1f30565ea180bcb8bf581de92ecf280c321eb32eb7beba3b5afb827b9df4415682b2fe31146f790b0bd02609c44ab8954bb8e38e8b6cc3a55630c6242ecee2bbc193e3b0d1f7c42707f769c37c060daeb71510c666a09f8b06eda9cac030d862b4c818b262df0cca22dc4a63542c7ed937106a09b54259c88d49a55c6a39c3d34a2296f71014a7c07b571b35c1f4f9bbe428f2ab8fbea54379fcaa6625677ba5dd5db57421b5c14a9e3992b35b8110c44a442caab50d0a2bceb7220af96eafe1e5a4137cdc51271ca34605e10d29ddf134f8088dac5cf58bd852a2d4bb896f8f012231b298602d3125a4f999f13a7519a2fc013e9a53031c0f30f570e0b3377a4bf5f043e232f3de763652e6d173f0a4720c917a617c63a785ca3617a23fd13ee19190d61152f6d584c19900333098b9b6a4e56ba0e4248fd97555dd7292565f2aa10ae56275a2c3eaaebc2e463e639fec58471c2a7786046f5ad9f6eb4f30d41ebc58f04c6c51c35fe4aaf4467d5a9f8759db3d949614e4f358ac0ba591a0184ab5ea7afebbeb72cd8ea872d392b0fe93a8086994b964a8082c50b7396c38425739eaf46940cb3fdce7a20b97f79a8966c9e29dca688c7c9b1f3ae1fdba70b29ba40e7069f2f742fc4929ae3723f607f4ad464182d0b8b43955e1976f11c9a0f9cf8a7e2eaa88cb17c51c2a80e82ca594e89074ca6a62b156db132738ab2b010f728c07fbbf3dac830670b7ec3dba647032fae1d3940e9eef7ea48086e33e02d6b6ad9824d47ffcf70ff09d332b9294aaa260f4102dcff9c3c6944edf937788abfd71cb69f619966bf191db8b80961755007a685d995f1469564934dc7f50657548834d00705bd0210a1ea937bd3524443b7cf00b791c6b8371911ca86205a51bc0117ebb4cb0089845ca0976337aa85e6e84df82ad3d2ccb44d290446fea2a3b619be4067a24ed5198642955080472e7049bea65a1f5ea6b725e319afeec4caf683f65dcf0781433b68cfdb7964e541f9c39f41a3b04cbe3671fa037c0f3d30c485dd45b056306e155b82324e189f3ccd85a2c741e1425dc5e8918b0e70ac6063db82ac7e202e84636166ee5b750d5f3ec43c496a7098b94032e4d9cdc127dc9252328f20617b3ba095de6a0359153b835e63fe830af1cbb8b520e1e55253cf70ca9f465980718e4aeeb094f1ff06bbf62119ef80feb24b66ec735cd3d5bc16f6c0c453805af24f9b6a53903fc9da9b34f5ec1d51ca03482b7f07b23aff95a99ab46619b50f30e6d06f7590f119e32d7bc6e6b6e4570c391f43adab8189ff73b3ca291a251c2a66b297a64ab577462243ea5bfefb60d5b45f98bb9ca476504b3676157e40edb8174c9153426714dfdef2777795c8fcaa0d9dffde27910993b7307a5fe1d626a28a519dd70ef2f36d21d940a0780e272dd5387606ebca7cdcc4e8ef73ea60dd99fdf72ba0b87eac0dd4c3d1a508898855be3f6a1a4beec30a068dc492eb3cd1b844c6dc4e14509a99e980b18b686f318567097c22ecac9907dd88695864545e852c7f83065fa276107c4a82902b98e0e163bb23062a094c4573e8b756c097ba22e94aadfabab907b908cd81699c07ab628d25a4b9dab8cc5da4ba41d10c10552f0f0897cbdeddeacd1fcdd74d24134199c45e6f51e21adac07c5046501c9bc0b582c31c6760013a72c65c2087fd7da7c40d3ec5741fa1b493fa8b47993d0525816b1192f28cd7023bc9496182bce43f639856a326f359b704ba992dc2906febb9849f8d5490454db87f1ca8cd83ab7ce0d3953349a7ef2d1bc46ad970d5644166e6dd075394b370ccfb8734c7c351e55f14cec14ee5d32fb7811aec50ab32ded47b5b4a75c2689ce8e9311b444466d2b6e0d12539d922035afd61b585035390daa4ec55ffa5321e243dd6f2d8d5494ca2d2dc0d77040203ac867967e2a44788ab7232119890d07103b2ab6c1194d5e7c3b503bd8ed91491fc803b01b5d05861eea051f96514b449a8630f1dd7ed1b07539e5cbd1cea27319a6f0cea5348c3d94e3a10ae69f42e84f16bf922fdd2e88fa0e3642dc039af193b0dfe5089cda711dad2cc4c543fa07aa7f2de933bff9d3f42f8c8f7ffa24ea4b335f5eb52045a46ae36237d097d13ad68acb7259f796551ac7c2518396210765bce2e563d73eb338ae053c33620d78629f61e9de6f85dc0ede045bb7b23c2e9ec8fc46cd37ebd4aa1c853f33389764d6aa47dc6a4dd6fdfb1691b2ada1dc19c655fb1e71cddd16007727eef77de19610908e0fe3246088b497a1029f469160318a45236358f769f65164fc9f3ae08b84d790f70364095618eb57fab490904c2aa01dd6ff81f5837680c6cf730a55861e284f5f518ad6e85712c95042489ef787cb1bf34d2923d3af601f2e09ee579bba0cdd929333b5bf8665a76b6cc39570eba6a74f92243b36558d38eed95bee1c14de21d43aa9fdbfbd32bc0a4fb80bba4b07e04835eea4e08156e315a20ebca38ba5b6f949b514848d019e3cbf54aec2a9bdb9843d5e10d4a93046c5d1a6dcd6000e14c44bc6089ab2d606c6c30169778bec65ad2c44eadc30e8193f76546ac966dfb7f5af83812b5cc53d5e5e97eb7eac16654ceb069bfbb56a1c15f3b2f5cee784c554a2024e23aa93bead1c59d6df34405d5489aad2b1477b38bec8f8ca9b43ca9087ba033166c30a6944fa667e83f72ca3c55f40193a5c63ba4b055fcc3b09573e951d42a73be8406e80d80efd97c37121308997201c156b31611e6952518e9c4d9ebb3f4d774a70f06ac31a2fc25404150f55ec895bffffd387d92866d8bd21af67b2b509efadf4963905dea68a27b37e508b6c365e6946330aa27b9192ddfeceaa21118bae8b474c5142b447b469bce34085e56ae2187d3ec67844c72bbd130e27b4821492dc84bc58f921d0c3bf65e5194f1aa7a559c6e8283b0bcd897f9861e5274f628b3d2cf6efcaba27ac44c487f969baf5d8674a0c921d532028a1e4d7abe8e2c54131335de02580646bfa72fd33ee3808d3c88cc8ff74ae744096815cded3856e4c4f183098e9b9d6b86d34cdeba517d37a2abcee5a961216126eb2b7e20b11c0508b9bb98531576bc6a3e1d19ef2a5d476ffca7568759b0c2dd313456b42da93ee8ff3f3b4d3ea4eeaf27b7eb25bf6e09d2a676e1a42f860ab63ec2e4179418987261e6006bf8a6847243d3d7c2271aabb0f4ab2e6695ee9e5b1799d6dca4c1d071437a784b842179c637004e2aaf80428aa4a6246aea02e0b6c8aa2a9e68bc9b311ba2331a9d6bb0442f257a2386f3aee7dd6e2e46f952142c9db8d82d50390c2b1e50e4ed32a1ca48b643ba21c71242943452829158403ae1615a679985a74fc5ce68be220f47499cfa58c109a1421c2acf9984e85909df51e847b05979f39f0b93043205469278df41a4ac75bb55cc0da143f94d590d62170d9295eba13b54379931cb6cda2a6297c76e246f40eb9be01eecc145d4465dbc720d75d2a2b3b62e2919afa76c41a9629467a08e324f53a5b2a7981dddc362a52d0e73a125812e66711652834ade530da041462dc9443f9b5c341e2dda10d54420c942bdb88863b0d12258e9a8042bfcc7a41155e84c792bb6116477497a3eaba966d492ff704e80f609768bd5835bec28f2fab00b013805ca6166a9eac6a2b6d770ae1f21fba62cf2c548aaa4cf9f11d4c0797364c4e50e0c3983866a2a89bb8a711662020f6ea0b5960dd1c42df07255046d2bab3f2965cba940ac2c3ec5ff323ffd292251542e3ca9270028740a293125a67c584bac8613272fc895edc8e7511d3a1eef141680184aea00fcb3f4ca86fce79ba57060c234a9cf2c21da3bc6f98cbc469c542e18bb3b543ea7388ec1108c207f6c4902f360702057d6055f059c505c88890ca63fa04ef46919698a2f4514ae537472ae108231143b98995c9084848ab8c6b4314fedee23605383496b64b57add89f18e2ed4f59d524b05abdf7c2d96068175c91d120a29a0e7eafd71a60e18a2df3c783a314a68b58fb53197354e1bf68cae8831003c1bc32c24d8f872ddf01064adc490c3d6102850a0eb15a7a0147cc37ae856ec2a36b3853595b668d0c4caf6f88af85c02cf230e8ced70a90aea08ed2d992db6105a8be15b083e6676a99ff8dc0a83fbdc002e69933e4b49fc09f9a3fb83f312e61dcd127e47a266dbf506a9879b24089efc5a6236d64fbf56229186e812700c0ec4d10d48b9bb3d814a363211090909c73ff8427131a67ef226269883cab2cdac8726926255bed597cd4f17ad1b8ace0e870b4f4dd19f3167f9d324c3dc4d6e7d3f9a84236688edd07044cf06583661969e81c9bb7050f9e5a702a087c118b5d4e573df4f4e804f0e9ec1705e88274397f8e41eb9d065b1a0e5dc3c783e15fbedae7620bd3d0cf3ca3503c0e54a73dff838f5811b8c0a4b633410ae4530a41be3598c2ddf7343357d3e22fd5f8016ec71cac219730a32e41107d1fbfc2738fbd52e5c2d203e7784b623ac5f5669dc6b65e6359abb1add5d8d66a6c6b35b6b51adb5a8d6dadc6b656635babb1add5d8d65ae38ab8551a44a5d6d3017dd0d2c2ff004c4f7306bc7e716a8f969700283228cc228ab3d647cd7abce536dcc458b0554c90bc7b744c48119cc1244f07ef39913f8c29d4c6f0b627cd617f402b12b2d768ced8a5e2ccf43aba650ff831ad0ce4e9f1d6f69f6c91301083e16b9dbe81c7d41920d0f48f244dace56112fb67ef8c17e8a8ffca16ff47ec89afdbe7dbbc0611849270ca0c065de20005e16b0f461d8b2dc65d98c32ad78dfe982ec607dbe7042a290a6f42a70abef7eb0c244d080b84c864571fea23a5afcd7b70381e731d34190144fa05cd179b10289f950850ce900732b616adb10aab63525cdb5acb9ee281a3e8b1a7d6b9749ccabc059f77201f352a19b7598ec747096ac20bf73ce5a1527256d0c6f8826d8508580f98348976756437d414911e4df3689ca63f555deb130f77433f66e9f778e13bc7ceab0429d6d26db87411b1e6f30de307d1ddcded3ad15d68df205668f06b734e7efdc0cd710f787212c6b4ab7ecf7ae8f18f0dcbf3a051152a9900bdccb39209e9f4f6b0f2e9d8e4222433b085009f3f449d81a360fbd35dced8f5e26bb1ec286de953f6811e13a46147943b490a22d1a906cd624ef85c09def565cfd86f08f3e0b7fbe0c743e64bee3b5f555ba62173faa90d3981e92bd6627cfd4ef65f4cd676ffa21dff84a2e4b451b8f1fd6b05ebcef5b95724cdfac9ee9a841724af94f4119b0055fa71e27d03cadb6e6496cca3ea1c9b08e06e2e3b38ef0dbd7b5abb4a69c8f98ee271777b7bac2ea9454aed8517cf39bfd37839218466069f3eccf2934d150d33aa4aa6e3ccd624dd40e778d3216a0ec7510a3c08448426424e6ed7a6e95b3b76dcf89dcb2612a82263697666574925c2e68f2d6240d8de5217f9228ae77dff6502d9efe8f1a4b1a5a13405f4a058eca097e3fc480e123f6e4639708c88974b75c33ff99661cd86f234b6c8585385f688d8a7d2b1436e0053622ce715069cdfcbfaaf22f23344c91e20d0d5c90163d325532901a48ca10e2ae2cf76c98362f519f8e502185ca36019ed4bb8b1193fffc1d398c51a3b9123cf1b1124aca1ed38a51734809141e7c7b6c4fbbc41fa41867107dc04a4a7b3fe4cd0c27d8870ac0b862d813d77287fea071e82721bb235f49855076c73aed4ebcac06927621bd866bcecf81c14622d00128bf44001e70d25b442bd4680def4136df25c7bb498cf46e723abdd4aa06c5e5f3618dd95b4733b0a96cc0388f28f2e645c3a23a4277cdac8b01f253a080177760c23ee2a7b8d337c9462cd8457b637c6ea7c683ddd47487bef47018e81165faf472d3a393887b3de67ed0103dec253fc24e5daf8f3343dab3a96684612a9e3c3cdb620ced7d8595ddf28d565fb27cfe71086835e70b8b98dbcd9ff022de443f345ea2dc9d358ae6ddc2772b7fd4d9e2f0b24df170538d0fb89bb45e09c92b8e5abffeff0bf22fe147f7e1d67adfdd7f356f1bcd2f88206f60b80173099fd3ef9ca3aa535f4d8c8051cd622e355aaa59b148683e64e4d1f56c57bce7689cbbf7565cb275c67271527074f27b1b4e52d6cd48d773a71021e5d877ad322d261e0f37c2f1328beaf0bd4dff7b6b44f4da8e3f3097f88848279a5d64b6e4dcf22fa1a91d0b347a7db7d8574503d3ddd17cba3a633c1cc857b902eb2baa1aab26efd6b9098b9caf6007a5f71b6a5d4b0f1075f65d2ec1da4644cad4937723127a0cd4bfb356a7f83e0f5ed1664e3a75f62cbe8e5f78f339c0f551a56b0f8bb1885970052f8411d3fc35daa23c15cab16ea7e893a3648e76e225796fc2003aa14354940fb94711cefe1379ae383e2392242bf288710be4fce2571a5f4138d88a5145b7248e7fe9b357bcf0baf739cd0b9fed4b9a5f6be387aa03a633ad3310e8ef957c45e1213df8874f26b5b64a9fa57700b2c95802b0cdf64bc3df6f1d1db74c26c51ed1ab8125ef0b17f6ce3aa42709b2b75b102b1589913df2204cf230aba77393b18b68cc421132ac29c44412bff8247db5c91a3e73303bbd62aa1469098931726437a7c34df4d6b74d31ad46ccd365e334dbd465a9b934cf1e22da9aa69f424d7ae9e4d7dbc6863f4265decfa1368109479de0881dd0bc024cbb5fea7113eb071b38af3a3e199be7aa0101b5f3a93cc08980dd99e34829df2204823cf42535854637ba67ab976aeebaa7fa1dbd3a906e6f4f7978ee56def1b0786a2b66fffae818a63a501e193ddf3bb11b8fcacfa26b832f972054c3552ddd618d8981a2fdccd9a5d2281b88abbebb3d1844f860883052838778d02729ba3fcc3756c4d6fdc58a33bd5e6611e293483333950a63614442b7faefc989aa5da3efa8f83c9e20a28070d2fe20f7cffb4c834437c0751b9f72f3e58fac8da379dc8b80546e9fbea5e53dcbb172aa4d11e1871408191587b76b01752a68406ebc783402fba05060cd363cf2c03490c8b7766191beca700669bd981b3cf3f220afa9f4a8e2477536de347e14acffa0ca7fd8147d8a9ebc48dc16b6176ab5cf819dac48f20eed3825fbc49fe0e33675c39ba7774e65beb945da4e89d7079482a422c436dead65030103748bfe8a151ff289293d209310a5e8b9fd1cf458bb9621a46b4fe4924e39207bb73e5b37e7a84b34a883447274e92b24df7d5072a4bed8449f0ef1cb565e3c3f1d25c299588859730a66277278929a999ab070f55c6fda4be8ab97b168b3ef0cc2b9610111f85d2cbe7db22935983f60d9dcf2a1be2eaf565f6ab8ef9a58b092a0568239c36b0773e070d5ebefce22da569f66ba73164a342c75c43ebc6b3c81f200ed014387d57285e53d7ba143be9c1071daa5068523c9c6338e96f43cbc2a2391e514759d1e0ed9070abd4d128a5d0ca4c2865f69490005d85fb0c07274c79c165f3e6be1d2607c2ecdb332cc463dc264c4c457872d3aa23e67571fbe4f6bbae9fc5b3e87a66674b22a273f6e824ba5003fc73d860ba1a5685cc0a90802bf72335a1f665dc5c6caa3841b8686f71f07b318a63e62ce44dad9931315b199c1d9b4036b18e09a585bfbb893f37749ba3757556404b6dd9592c4c610d8504548ea4c62f4aec5314e18500e1328b1bf44ff8b080f11867daf6783d01113d05d0051e1a00c3ee106abc3de5dafe6b7be8874e3230ec33652189615200ad969b74d622544093b4397e9818560af21f2af3771bb9b54a514d777815982ac8febcfe542288ac17674241c7412e29038fe5fbfa3a44a9ee05dc1a67ad5af6b7b919a99446adf0f399dc49972b1dac3ad5cbc7641d8c1684e9ff80d1dc023e693a1772327de496d43580e28c76ca01da6752cf82c461410071ba19ab8849f7bbf96ff064431f881e28fd7674eea742896fb3e058fbb0c8eec882ed6950fc640760e103e66c93b301051c7cb39a2a24770be34332d5d7a777e6b309409fc7de7a95f992d840fda36e88e663fb981dee270c329f4ccd6e48d4be5106e587f3dcf252881b7eca0840542aace17efd4123358ae32e10f19328865248cdb29216b4d48a241deb697fc5d662c0cc770444af16f551c9f9fdb9f51c986a5c652aecf58f1b99b1b4f04dbf1b49ff5ca66b5c746442e9a7ec09c79b56bf674030b8861e0289b0398976ae7788b9fd27249e3f32250fd381cb2bea9284849bed6a444bcebf82da66b91649cb1aa5124b49401882f09d8f4d594a0bdcb5933f261fde9ef109947be8c3c90e11b5e3b08961df377a11b5cca56913884de2846e709b79a79420a01f081d1d3eaa7af0d52aacd8c62b1487eca7d817e213f02b1b508dcf9d7a9b6c058f4891f9bdfa17afae3fac3449f51c4fac3ec352e1d01593aee61080a6c34cbf80bd00423faa90f18b8b4900065eb016ca78066dddc38aa000567cabe8368846ae763aeba90c440f9aa339eb5d7e339e5043c84f4ba923d92439afdda28528f8694203cdef868a298a0235be4fac4d455b5ee5ffa6182a133523cb5e873072a84fd67646a10def4ff420740753387cd20c5399a0b407e18b8512986be4cbe2bd538d4d16fe8e156e1c28a2123c62b1f56ed57927e724946535cb425203dd1ec5a948dc0d74cd660f2e9453175974812dc1595c32e1832eb110415898b5dc17da183e62a7befe549e14d1ba7a0c54980e1c9fe6243e5ce0d8e5b4c885eb7b4a9be6f41a3efe27a21397ca8ed6cb8bfc07ddbd23f2d99ffe00757a47e1c486f132f30a140ff5bffd14eaeec74a26274185a89ff2f34a6d1c0bfe2076f0ef7fdc87385f9cddb606bbf8a33988e3d7e7e6449142c9e104f125d2b4fa62c2e567693f13c38e635eaa44e7cf841d86b3c877b76a3ee53bf542bd5d15b6c3b2b68cd58febf1eb92c8514308179e9a5ce872a05564fcf4ab0ec46650164bdeb90c8cc3c1bee82c4eeef82e04ef94d1582b1bb3156ed1863aaeb17dc65533ee90974bcf29b354dde408fedf06856de401a421db5d62efdac88a4a47bc97bd80570e96b4e99065d4e04a776464b85a175c58734486102f1cc5e73c709a1b94062bc3b7f3eba2ebfff30a096e7838c985f90904589f83e32fb2a810a92d43d4daea3e87d6fd8f8ffc6c331f4de087274111feb5490ace1f97f9e2ed6f77f70f2ffa7f8b0fdf0fb79cab72361bb5a37c3aee7f83747485a11e0db7e5eaab44f77403a439e3b600cac894124cb7b411a133b8b26cc5e9824b66e1aa7a694136a99c9738c317a514090bfca9343cc96c02c54d2ac6cdd813ae73d1722a5dd497e6b870b7a7bb4b53ba9723d9631f7a8c4d14a6a39bfa808beb5bdddcd5a618013ec3caf2052ff706b4ce17611e649b315c6da9edeae7c6cb65a8fe2f2b2e564010d3e8d2bb700cc26bc7e98ee0435fc113d62b859e9a3f09de1b8abb3ad3483c6e6c8adcb210f4825e4a1012df401a4cb8c48dc156fb5f0504534b309f40bf498384f5e876017c85d95a8f882d874e2808ddb180650a4e19f231ee79b8981c9511300fbeddebef5c4cf59ca48d8fb206a01d74d5edde221c815c10452de7a8057b2d2853eae9daa8394c0298a264945524d62221ec6aa8fdce526ff7099410f36f945651d6cfc9df932d71b8c86dd239cfc6a39e770913b76c857aa23a08e6f706161e88816dfc5c096cf43bef94cab155400d6ce587b3a51bf8ef8c4103267d6e8d9cddca95d260550e955d9a6c342dd0b8704b84edce4d8b40bf93d20769df94656b45d011b730129f6f1b9cc8eef55e4e48427a1536515a04592a5c0303960f4577d898ab324c0a9f1d351cfeab0f4c4439008831d02ba4246735fe2e0aa739e0d2a572604261f4aa159137d8a3308d9683fea759ea11eb1b70921e69250ff45fa3e96af3df2b878beff7c970b0f095cc5f952480170e27650631b1022a4f4392ffbeaa94ef01a1134a8f4e789a22c35fffa6fabc7f9dea3baf6bed127507ab9d28978bf70e5d513be3f47cbfce1b269f5f45ee3eff945e1b703fa0bfe4d1c6ffb57763f6d452dfcf05ee9784a97fa1c632d5f27e60cfedc48202f421582bf38a8607c821b82ef38c8606e821b82ef38c8606e821b82ef38c1ef412200fc17ad976f67936f09758249ef41e336354f3b5433d89b682f27f2af694073d7dfea9c860630053aa584e7ea0cfc8548081efbf58238c4a6599a0676998f7a6830c8ca04c73877457ee4d30be7d6794350c517eaa25d11908d6148f3a93df7f3268c998cc337c879e14b9add235252d0c0eed8c03d326822792007a895ef60a5943dfe70602572cf070deb5a262e84c55720c7c47735bb2ff671259cdb974cc3d012c08edf400c4330cc30f26653ae1d7411cfce82b3fc1cf617e2460c792c6fae29a1defe33cae4e9865f0cce2ea6fc897250d9869855645ec50c79d814451a24ccba5752221c16ff5315e0a3670f9e5d19608065dc59f81544d77b6108a9ab9fcf391142c8a6595c452d91fd978c355d2c229fffc1ed19c34cf817e86eec6aee7edf9de30fbb822cb0def7a2b0d18f5e025b177f2afb72473347a954c5140f9c2d38fa8caca1d55f8d81999dd2f792a51b6ed20f7a4301daf2f95d8499212b93757364d6e354ac317b26f54b612b48d9243d91cfe25995a21203db99910d3a87678e79ded2182e57093d1c71cbac035acde264ef78467417412160e11e89c9f0f64baf372c4e1989be64cec9b9f7a292fde2193943e456502238a4877fa273b468265956acc5d4a941711b438bb77942aac9885e2b3fea066e1586fb62439b7e2c8b62b3357479728915f47c94063a26b441a90b41a26e5141baba5949c7c2c59517aee95a8a275f262dc808552bf079e98f0f085da752e25ede478d9d4d7e82e047c8400490f354f6de122147ce18339467834d34a453b8d109964b51a2a625bfc79cd29fc462a0d269f5afe556a9f75e33c4b09dcf1f51a1f44cd8716eb95086ad1b1263533fcd130422e7c5b70882497bb782778dc6d99f7d533ff4309cb9473a03b303a733b6c9909bd929325da0346cbe38bb475fd4a93b888ab65673943fda2ff81cbd0a9f76c60ba6fec6809886466fcbb737a2e9762bfba19a86fe690c67ecc1634d583a26722920059a9fa30fb99705984b65e19b76fc3590d98b0ccd22a7c0e60db92cd8221bd243eb998777ddc63e6be1ec0033d56e3fc1fe102c9b0804509dd6506c5c2900632ee379dfe538d6abfabe11e44b89093402811b0c2a93fc1293d9ca691e8359daa560417a7cb3da7d37855b4f1c56368f190dbd195a31019be75d87384428b1b3dd73c39e02b39918e059affa544bf030a19fcc159b9aec36f0bc5a21395fadb54f1852b61fa933f8311f4ac872e1bacc6a3a886ec21c493f6788c13ba801083222d85e3aecaa2a092c22c365994b83ea1a2d701027fedc93626a506f56f3ab87301dfd4c2dc64a54d7fb6133507db69895ee0dff8925e644ff10cf38e8781d2a0a91f12b994b5f41994a2db0787728b3d0256556f9d008133609f03c7235905184e85a5b3c4f257a3afca41ec9e1a748bb2911429ee3ca714a79ea174b50c98fc167ef0d7c2416bf1c1390b47d753a9327df6ac8a95ea8306f968b5dc5d5edeff0e95e24b64b89314173b8ddf8628554aa1ab9f8198eb5659475db6d59031fab8b6c8902a4fc9343c4d8411cfa0d71c7927eac46217053f322f9e9a5dd75b96023d518bb5e673b7942f503ed94258f8c6b6ce9ca7c2440ac3777663d949580302203326792e5ad3fe6fc8c7ab612f1feced835f084d31bcd517c4a655dcdac3e0084b1fcf651fc5a64f5254179079216ceb14d64cbc82edb5ba14716fb551c0bddde2311ebad461164737237a303578648e3caff54b13c7b9fffc21de5082132bcdb55e78c108ca3b3b384771797425c8391d1abeec24a0c04e77c398a31f478e37cae4e31b243acaa179a3fea6db9ef3c7080307efd9fdc04d781c14610257327b74ba223233237be2e9ff2fe4d24e47924e18f3215bedbe5e6099d0f8255886369f92f359acc0e8c185b22854dc423d9b9685ee643c30dc61bd6d681086e97a09e0e02607e95cdde5fc2a39d6e6d2fc4add9b0c81925663bf11122b1d640b2d71bd5ae1e2857a18e93aa4fcc66aeb63b2ebfe50bd9bb5bb1316d1920b4ff4a3f583d891cd704520e8caf22916757f45ffbe3b2eeb755d18092a69528b38a024428dd9060e20dee0d6ab4e3119234c93061494e8a9e8b7110967d74b0bbd4cb208d6f430e589df8782190836a2771c823439d56e09650d4bbe7504b41c227c0332021b5ff445fa8bdba12f8372a5340569624b9a5b62064a1f268f06651547cc32490c48023dc36dff703ac8854d9ccb4a80a8aaf302e70097e267fbcaf8c8c62e099669c4741f7a8d56bd6c90f33f284454ad7a87aeaf055c765abf9a97d52f5cf66963c11ba1e40a18a6e5e29e2b54f1803d0a4161ca43a93cd5be7bf7d1290af54f1440a2b43c11f0eab5e85ea80fb969e5f89db0faf01f74db8d286a5ca3dae65d1901ac0a36450975dc639b49d7c89224fc187bfc22a485edc512d346dde5bd53599b79afa632f5f0bd90bf987684021e8eba99dc8de4e51fa1d82a5e474794e66bf32fd0d1f07a4f8a3012eaa53a03eabed51081fb41a8323e2971588ffc382535b93f5bcf3f827dfee5b785743afe32f6bd5448072aa2579a79680efd34aeadeedacc90865f382ecd393caf50d8c13f0af58737fcbaa6fc941280b280fc397a68c69f27be67dbc8c63f6c111307c63b6181250d89ecaecc1744437b4741aecd3d2d4a231cff71417a92dccf9d084351683cb5d3a1edcb49fa0bf66bdc998e1f0c4bfd01f8749b631b720040d8831e03650fa28f81b1d6d5785d2368b37bee619e52030c6ef0394fca86201e7b50422df196e85f0217096753aae8f5372c14da7ccf447892dd2b1382050b462bb344fa014a67e2f4cac275cbc475bde0024f8830be0b45713488cc4b79d463ba1735f3540e7d63fec5f78dfca7d8aa72d93a47bf83a3b0980aad19a187963e390ec4e05098d6058a73cd3bdb284ea66b671103155321441652575d5af8627a95cb9b6685748632843bf6ec9053bff60865abd5d0f400bddfe3b91f046cd6f385d717c08b89f8028338078f783755da5e610feef1554486f9b2dc972e3e03f85bd0eecbef52c7cd9fdedbddf6fb27762eea552f1ecac088958fbf2593e4da5d12cc11962067839987405ee2cd651045a7ff08b020b0e5953dcec10627b38160b1c490c8e80919ae7eeb925cbdd22926596f007359104de868c4dab8cf8cea13134c0a0c43908c386fc790ec3f95f3773d64f7b501070e7b726f5f742692da2b3c4aee51a60bb8ca6d5779cb7af11f7f7e06c82955d88c5bc5fdb08219f0322e92b50bc4661a953933063ed611265258222e15c75524b23397d8f87b5ae443244ef3c94b7bea9e8e6dbb25d61f0f74a75edcda65aee5eb6c9049bc581c28523c0139f9c998b0c8f825234e4dc3849025809452dc7214fe03006450218d84c7bcba6ea2035d53c46b24e09cc592cc9c189248aee46af958a52441696ef0d142f2f3829f4666612bd95d99cfbe60d67f919eb0a7582ba9679019cbf6f2c20a9d7bf1cc29ec92efaa1e05cc23c86e503792325aa11d9bd54fa66b204901905ce521ca9f65c405653788648619d83f11dcae42d91cc3ac1f501fdd80b6eded079316a26b8aa099e2b3639102371478087aa50a222901a25b67346a6c6951c1967c2c81652b6f1ffa5af80c761233ddb66d747fb0c5ce0d6b1a20e55a525867358f2f4a01de5b5b49de5a9199d8d264cf6321c726520d09d5a732153d01b28c5e3230c045d86c29643807463fc74c55afafd3dd5ab92d9bd12ef059a39b4073a7a8ecd573d9145b63d063bf17835894da6eecb753189fb1ebed5d2b302bdecfab84b3b3a99a0536ca04e76a82140fc7eefe1f97d17046350735d75db8bbedac8d5e69a230198232a57a4c62d04eaa7dcd1d4fa47e480ea17e10ff502aff4df0d8814e62755143a6816a75fe5347a14a5df8620e65a1a444685a8f3d705e21957eb623b4b13b42547dd495e00b3beeaae51bd2e7bf80d476cbba8fc929e5fb104c1745a2ab9181e21378e737129062dac5abdda63992b2e7cf4d842c1353fb39729b9d2c273a5a41884b66fad18ec49f78464a7a62bd503dbfd34af07e42160c6fc034bdf1a75eb5e49ca7e9902b21cd23612cf8b3d590ffc4db3b07210e962e267a6adce230aded3c977119efce5bd356c0231b8d3831b24f1f7175b4d0cd24b92c9723ed47b46360d573442d035b8be82eb1f471efb5b9a406602b17d0b029a30fce50b2ba25bd05d0cd750ace9ae2a572020d8f501a7104d887721eb703ae99f155c95f63399c1d3b0220484723cf76010ef707d6392b06ca529017715f96bd01447db7b2bb4a97ca1e5c7aee0004c2863c00c48e0a23163ec5e393d9d38153ff38b735bf875d9d904222c47da20982d6fcd858e2e18b4edd82bb85e0fdd003d7836486dd3af8ade1fd55ed6e900fb5c4d303110a84f03a3d42b5ca0a8e7b98319d59e5b15778b07909fbe1500eb2873a9a74558051a5bb8df25f230cc9bd240083a9a2955c05c71ac0b4b2a04cd45b8cbc0545c094def6d80108152282a9ee4c3f5344093447bc6677902e7805281b5c6f031c3bc032dd27a5190de4d253b6302fbdb222a377671deaf997277bc6c9470ed7e037efb7920d517b40390195add7259c32a446db9c2e0cf322a9f5790bfa96b80ee282ee34a42db7772420ff29f8806bef1a1f5c77e62c561a6373f8fed949dbc3e1d81da3ac5d9c981ef8f5b57f342e1325f9faefa8c608f03629ffb22783b8a30c99a47cd7e6fb357c51eb4904fcc7ba9cd4c3bd9ca535f7770637e06192031b350fb0b493f9acf4ff65049a930e7117282bc89ecce8c59c7ef2010e3e964adcae55a9d3be8de83b6fbc2c6997845d77b48cfb97fc0355b53a0392e0b298f7ad354c1f900ba76d1e7757eeb929e1e701bdaa436d1aabd7f53d68ab2516c067f955f83a605e33dac3ebc8d82c56bc26c02ad0a70e4513414007174c7867a727079a4da5827010308f2e3cb2d90b1d732e178003f9008ec529d32dad8e38b068e5f2cb65e49451eb997bbbca2c7b956dbd6bd785adc2c531d666e11a326d053833098210950477ec079b2ba037bc42b97625369ee54c5bbe289beb9c17dc0cb824824331c61cbcb310f786abb8b84ce3bea14f7f590fb40f0ea069f1fcddf6a86721c5b169e0da9570bb60c8def577d7d0534ef33ac23f93c679f0d1d05b8c20deb3e0fe97d2aa2cbbb7ace0f8c53e5ac520a2c80f6868b433862298536e59af61d2d4833ccd2f98655efc55e5d1338c8422b9f765629d3147b7f02fcf5095cb4716dfa5365280ebebd8a3fd80f0917b959a612c9d0d95ca811a7b75c9826765e80515d263827b857e7b5ddaa4c780abe59f027426793e6682ef8195af528935b464e0dfb8bfdeb7323aeaa84ed5ad689d61d31855960d34b2929dc8c5363eb5b01aeca89809b19390d5724a6c9c537fd8115ed82cde14c01f37a182dcbb328dc5145219a037c1a6244b517b8188bc12ddc0a6392bb9d6822a6356dd069ff1e960a205455bc9c73dd6bf57997807410456c6fdaacd1e872b384e25b6628ebbdf01a4fba287ee08afb134de7e00f9c06abe3595fa398df17a0a178d4a1ad3ca5d1bb6f84c10734bdcff08f8f7f6aef79e8c8d10e99d74caebe2ddc6939e6c60e8a958723c14e9a32f99de88276ac6e87a8df35dcfcc6c6ce3524bab8602fc714733c093f1a8f0dbc53dd6cc5e73b7c45e049d71e362f947c0f4cb176a72b821f8f19ef910f277665f954270cc10f72a3bd6ae7b37fe9e5cb009cc49d683b9abb4cbea6fc0f62f64a2e3dc90b252ddcdfc9c29d32bbc83e75560f463aaac7538fc1191e0a8067dc90b9343960e5378fb95deae0e63c5901181f06845b8f7169a021671d36b45d25d3b86407ede780af34bbba4de722ed666b39db04bc6067c10f2ca1fe41d4213d6bce1605286b22f05681ad522d11de54dbaf2399dca346a80682651cdcb4cc12c7ecc69ea21b4654368082b5ced2885881819e8fbc00843e89701161d0798dcae303deb6bf66efc9d95e35c27a7e978e2b1b418efb9cd142cbdca6d883c16a41aef3115f30cf87d5ad4c3420f9ec7e119eea0bd24b186b280969ea5e0990cb77baf2a68331a17ae01d7240c0d2fd546bd60448bbb1fd5d430107f87e42d4cf6e622c5044886e5ed6d11de9cc3e9732e9a6efe31ac00e9470830b4eb4d29b3abcba5e07b46a4659cf0d239e58de4ccae4b8f1ca27a5301325be43b4dbe006808dcb0c06f34348e46e5e215548e9e2ddc472c0947c72439418563463ea894682474dc66a4d80bea3291e3b528b70d60563d2c182fabbaca0a0c96aa5804523bb739a9ebf116c203cbeffce0667f9d987ee5fcf5226d75dfdd3da207f5024fbdd26e5e14e4d2cd92f3036bfeb5c593f446d3383463f4fce5c945be5fbdf726dbcd34d228a0784ed5c80740ebd0d3f71743426148269553f8a4fee349602c4ed8cf029ea9f0f6bc5f43d995861ceac159d3ac80fa6e6d40380e9ba730bc2ce9e4df929db5da00cbe8b4ca517d1366376828742f7f1aaa9956a4ca025096a629e004147ed4b1ec6c5a0496558ccb8c107d166c9eaea7ed093aff19baef24d6261d603df13d4e18d5e1deb5df99f0e7edb2ac58563b852bc2039982fa8af99211600d519385617edab89b4e8c6df1eeae030cb0d6463ffef23a7e4810c37e91b57a67cbaa1b8c9a54d1650b02b66cb3af159a2be67a833058d9d2309ee546dd2df7038cbc86582ed2e42c0cb31324a51959b6930103e7f8e795d8ceeccadf08b46087d6db56dd2a8cce632010d182bd05e2a4533b97e7cce9a48844a687d043a7d5df4eb901232ed481c9870765393e12f7e3ef92fad5feb282ddf9cbe9e977adbd788f0980e02fc3f5cf6f54f5aa5dec6d4c7cafdc6da7210ecde5cb6858655d77ef0dde0a09c20346a61e713632b193acc3d8edac4247b875eaf0563aad96d855788722d0be13cabd7cde927bfc9ed2992da658abc3a1537bc4b991ddb7710a7f36a385ac69cfaac701d188f5e418390c99980ac1f29a372d7bb0a41fccb65cbacfcc8b3fa27baee451541019e0abc281b85deed9b03ad372ab4fe361b166490ac2b06b0e148468273dc9675ff8d4f8206ac6ce6521ff637d3c30d478ac3d6528da7214ebfb662b7fcc6b13637942147f7cd414f9f52145d771c31ab24138aa5ef521472e5da3f184a45867cdd6e200dd0f10608a0fe47991fed2ba9800acbf7514e411b67280562ee8b9a0d1cadbf65912f3f41ee0094ccf8a38edf75fd6bf5d4133c150b044fde160835c97f233440e3053a80d74986239dba19a3ad727265553e73d0daa191495c29de6015ec6ab8ccc7cb86574214762c80b5be9f0a3e6c48eb1113997d1b4d0dafafef00a5137a7bfa50803057727ec37b35e65cdcab581919b0bed77d09673bb0bb2b5bb24c978678eddade4eb844d50474c772e773a4684e0812519437339b55d320fe7b4c280addb94cb03fe8f60db9a191a9b911df7378f5d26b9fc01ad478e211aa3b06bc2811f36a082b491c04d732cada6ea1ef9b8e226fe96c3283dc335352e0e909b92350c021858bb3023c266cdff9a0a1b6a7f0359780dea2359b5098e150370090b59acf6fc80cbf6d8c273a509c369e2cca9587ef0f34308ee9bd1310150e55c5770d8ffdf5ec17305257ddebe396ef4f22dd1399bd3d28e6ef9eb96d91fffb15f330bd860095b030f361d0863768248116622872e186e7dcc98727e11d04f289e5aac2ec0023f11a812659877bd25441ca9de8fc8315f0d8419488be480b776916b21f1c17d20aaa31245dad0bd86104de080f9ffec8ac5c3c52ab224348f56da163eda4aa8b3b5a0a16b0190a788978fea424c9908f26068d07b8ec257affc0333cb3b41d3ae4bb87db5f3a9d226aa3f4cc163181cb13f90da5bc14fa18c5aef9bb74aa38bcc266f9ab75ec02d35bc36b49efa9b3a34335848b265b293c7fd0a483f1a2063d9cf5dbb900c9f8afd933f43bcea1452c51195c34262e07aeb790451e63c2f718a02847a968a40a18bbb9b73506c2b098d7b79737185afb5c9528ba8dc50e40e1961a5db340078f09352db5b9d9b752c9bdbb5073bda7ab9437ce9205f2290ef489cee3034622311959fb4feef9bc9279f12f1f6fcaa99d43e55f438eedf298968af6e5bdea9dd9312bdc8ac028e01883e762867e6d1ed0d1adc4f6570408e4e4cac8824b817f2f4d57623d0d87830e336203aa3820ad95194039d635cc8cde6eaa0f8f2a399355b055a926f7300f718c641af6a6274a579424379226dcc8462086f204d2803b30f22a5755b272fad395c10cd282f5ec0ed3cd7ec5cd58b615accf297756098bc134b89555cb71075235b49a3a1dc8fec72cbde65512d571f5e518e028903c9968492fc32393cc9a5ac7858665f6a8a292849189cd99677a39ee34c753dddd46262810465b727f904de44dcd8739f06499793c0f03c529ccf64333471493d2b0919491929209dd8db4b1e195dd83c14bf9b62aabedd4e128874bce750652978078557ff4933c0e0dabd3e846a132d33a0bed2e490c80024910085233eb2bf39e11c25b24e9e8a1f71381c88079eee65f278aafa72c4ce5aa71722fcc60d94540bc434c1ae9b6b3ae329cd2cf34c1996679e1dfa2cf447b5000fa22ae205262d428975101d366fb12c183aef869b160294a716d31d64dfa123f3605557f4cf018db77cacdb416f0a35c0d90d50a9ee207644d9cfd3122aa5bbcdcc5496021f2db2ddaab213fabfcd81b890f77a384e406eea2495f96f7c7f2b668ad0da5510d55c370b13c22fb68748718e6426b68e930528e1e1780c2f7e90baaa326ef504989317e1a1ff6093bfb204c876c944b9eaa68b217c32e4d9e2b275fca96315165f2cfdbaae42a8d8decb9410bcfebdc3b2ac54bcdfc888d21d85f49fb713d978ac813b389e466e60defece9130dc55647bf7498e6bf5e7305424f96aeffc4a2407d6fd0197b8be1cc602ed26d7897808a5a54cbcfe18e49142c331b70cf3db00ea5976383f8206aa110a3d0435a141e22540bbd32ec614b72165d8fe6e99e5908abc6f51414e4180fa377001153a60db8db31c94a77f3dc52872c09bddb730c0097bd4555d7b6bb43af464c7c1fc654a7ad2294ca0f7b3a6ae94576d88bac0f26c18b822f96ba0018002abc60c299561ecd83044c0b008e8d0f1de6cf1b661c4d0d73bc22a4d47233a77ecb935ff99ad64122d09636bc226ae0c87db9238308cbb83d599be7f989e63ff88fdb190521e546fcb9f934579a79f61e18e199af0c4cd0c7bb81922940735431d447981c5712327c716a09d6ccde76d6525f12c7208fc0776b9a4abbae25c671d39a0a42406962aaed383bf1f433462b9a0642c02d1a1cc74aa805a5f7e3912ce3caaa7c0195393864986af451fbce9cc70e6d0df21570f848f09fc06800fbec686fdd7c559fcefa896bb9cbbc2a04591f4cd5bde17ccf7319867f42aba9ac27f3a5e7fc20ea046ddf00d22538712851d7008d72d81316ad0b739974c3d963ca8e8299b48610bbd65dfe0f5f391d0ad003bd005209c80a0aff97017d2d42a209beab8caafe6f89728755d6b9b86d89e817988eb3de6c7e8eb9edd9b5c8bf8a7ef785816c7fac944d02e609a4ab0a907f4616f34681456f5b9c80e7c1d9b1c49b6f49904147a0f4981af89b9b1fc535f6b7ca165428896e9e82fc7deca99b8897cd793791b6b4f8db363d8c938520748f7a83f2a9d697e9588c27180a02cd1c43e2fe2c3ab565cc3e870eebf754ff6800b15d3abbbda36cff9ea910df27c778241dfb7e811a42626815225fa6d1068ae21cb2e7d0dcd2e29b96bcc46e371a852c75b1c2ffcfd30a000716028822357ecaa66d4a9160f53a564976a335561c6b7ff54291e5b3d2aa43c1b2976c358c4840a0a2816d2cdc3e24a793be592325ddb26471de0ac3a3fb582c95f5254cb372f0910cecc487e75f663350f9143498c166f19dab59ddb23ea61a134b9377153cd76a9a7bd39b2d6a58fbef16786eb191e5238e7d6d4f3d7ccd6c414114213ba363f59150906d509fc3d179b9cde2236d65782fc4b7d95fae3741a32369008271c7c835ea0e6b6d427ef1ca2cc6ade97e9c03447d4af29253d6a59c7c01c27495ce11cce358b150a7c43688ea0d37b490f1e72368b5569f79dbf09dfa002a7b7731270455b69e06e8861c06d743fc6091ce6dfa2fc0e975528984cc6e462d1d705fedd390b07d1c88449751f00e5fa9d3efae8cd0e78a87bf11aa2327010101e27b3e668e49ebfbbdc25dbc0eb25e9432c330b3d4859f65cfbd35cf859261c93d3b0a0b1bdd409e94873b6b72071e31a9e940e20e03eaab1187015fb1f01778dbd0722c0f7fdec67202625d771d97cf7df55b66ee5aa20b009530b40b8e777196e2b4ff6062d8e02f5fe1f8906dd00bbe41c99b737ab50eb28e81b3bd469098d4634ad68b5b17030d156f7226ab027a6686115cad97046e77e7514fac4442c9046108822a01b97e0813b8f50522e8f31bf10ee2d5265810e1df5d429432a0c0fd061ace752c70deabc28cd28131b0892901b03edaf5cbba4d28d38d2df110b02e8bf9aa16eef6580186c8f59db72366f387d01ba8da46b7a05fad4017db6c45910cd83e2477fc046a644e4aadec170e6e2c0a3603de5a8a82f24014d89edb1b56da418d97f97b39acb0c6993f1368a01125b1afc6520dc273dc7de5cb66b45f992c99fc6dc8f09855ef42f22ea8fdcb1ce15ea9f47135654b14cb6e9eab9a733163761516184f06ce6f03e24b2083c36cd1efd8334f14ac27efc542265b6e01216e5338bb5cb7f2e1b1fc05056985fce8d5a2b31a55671019c282c49990de758be38de7180b3948f0a5d089adcdd06e3321999a3130d9d64274dc2dce4f5181cb4a2ad0e3e92a4ea19ef82bd512c0d89a84a41a8353443540c75cb9faecd2133cf03edf5bfee36644257713a033b8ba8bf3176cb89ac2472367eb9f3ac4b2603594416bc15ac4ffb283a0a93c87370fe9eb639f708ec8d670cdf521aab75513bdadf321e4a6d0789bb301ee84df465ca206c7d3f1463d95f780cc7a61c620f7eac52aa8424a890145a62cc90110adb91ad8f3ac6d4107f79d946f430f40040609b5ff12ab35bf7e4b068abd0aab09ab52b0d6cc0a17c8e3c246f8ffa2fbec99f4ac8d1e9905c11609d6609cd0d65d44bd63fff3182d7df073704e9b427528b96fea64978ff4926e825817b7f66006f2b70e34a4aad5ca2f07e3deac062cc7b9aedf8575d6e0c5b4e74011aff618ebad67437e616b5624b48808917aecf71f4cd9f6816afcb4cc79a83b262534729433c055abaf6aa7fcc92ce49249b1c247b1ada53e87b04349c4bc11ccc271a617b6cdafbb917abdc215652601dd157133e26489d8872f73a8b1f3a6688fd4c593645e7f897632219b71aa20db141a6626cebadf72eb46c40009a1f07b01b79de9f31f7f2f9b6b3c46c7ceb786cae4d02275671609c1a0cc0526d3d62117e34b6f50f182a972fdc026e1a7e209e65209d1c2df484e4130e26ad84b533376c80ab141d10c2532028f6da76beecd8c6553e3907648a2f720ffc3f9868a50f08f4990d5c0cb6d66cb68dec1f511039f24417ea8b76dce9bd3790b290939291108db295a6198595622d7301d72bbc856e74f619268e25d64c0c4b6c1a2d4c5c00c67141406ab099cb2b069775cccfc2bf7190453b11181010e7cb23f0510c7f36bef7d20504ae67cda30c6a1fc50f412cbe002435403063d0aa0fc07465d25c35880ac53e9f401e415089784a89479c9cd899fa93cdc9f67bd0b7d7f7990bb783fcee782530c283da29cc0f8addcc14bd5fdaccc461c459ed399f822eb47059b3b4373faca5f499f5653702f422038a0b179c55201e555107b96918e506631dda351d8a1d4313f549c619d6e66db8e2803095881966df83ac54d2121ccc4314c34c52b4b927ded58821bdae757f12d38ce7facff5f97a4b68602a5f9f6c5d4bf66eb2f7de52a624659e09a009f7095f4f3e4940341210c988092605792420d28f46990d1fcae48f461939c25f2e4f2372e4d15b7cd63990f675dcdaf0a16c6d09be8fc2f64f987c1326fd08d7fc237f7f7e424e406ddef2f4e32f399c8a9c8048e5e927c9c9f479dded4afd1b914f1a512a432a47a348023a4fb8ec2f2201653f4f3f2d1a76a363699676e293e98a938ea837fa2be111d65abbfb48ae4235ba4ca9cc3b50a6f4e94806f27424f7deaeebbea15cdff32e19c893fcbe4fb43c29d08572fd4b0651f8e52ad691fe38ca6859e140daf2298a444aa156a47146cd214992a552c934ca6c984619a594526a69d759dad1d7716affd2b7a578329d4ae4e9743a9d50469e4fae65d7efb2dee86ae9090b783e9e0f1642ca727a40b93f5cb5f9fda9367f3cd63bf4c763e0d777208f7d5f6be9939b7a3f3c5e8fbf9a7e08e2baf36117f29e196973e4f67ab2975d080abaf3610ff7a0feea771c59bffbba506717ba47c8faa70be57ecf6fdcf62542ca7286b3dc1f52279289b0198716d7ec46daeca0363b09f2f420a06bad87cb9d3d5c9bfdf7887716fbd59f033950c38c74ccfbe1e13c5ca53dea3e9c591384b4da5a5b03ddf6bba6a96fb17bb5b56fbf4ed77c6bc7210c472018a64e4a23d00405e5ae5a759d8a451257dd7822ad5d71c178a586e6e5826752c4dcab62c5f7a980b958567c2d30a6c994f382058b1c1a33f20b16326aece0793cf8f0cf030060b4f0e15dc4d07979e93ceffb40101489c23014ad5d71c1786586e6e5826b52c4dcab62c5f7a980b958567c2d30a6c9240a43511cc7713422914824e924d99592d6aeb860bc3243f372c1352962ee1d4723128924c952c964329d4e7e3a75a5a75abbddddda7b6f672d89244b2593c9743aa150a892122f29e94a4bdc45e28834a14c4652e994422a9d5aa5537962697111c30fcd51367af325da90e0bf4659687252fa1046f471f430618c6843a63e6694a17c0cea4319199791e94a65dc5142d5285bbdaafb945fad54441f57af32ce60b158ad96b75a5d69cbda950f554699cbabb0441b123f6b94ad7cd8126dc8996f8d329a6fbd3e7cd147977ffc21166dc89ac7a32cc5e3980fcbd2cbb22b2deffd108fb2158f4bd186fcbe5c116d48d8afacb0883eae7896efc396961617177771e94a5d4ca616d186ccf9d06594bd7817976843b278d728cbf9d0146d48196f8eb2196f8e336ace1fbe441f5ffc8bc5872fa20d49e35f46598d7fd9e14318188781e94a6160441b32f661cc28bbf91819995116fb7046b4210bf033a3ec003f33cea899001fd2883ede3ccdce8735a20d9980af19650af81a213e4c91c253a4e84a53b45aafd3df4e91d89f0d74f3671b11c09f1d44803f5ba8007f369203fcd94342fcd94912f0a7f328e04fc7c1803f1d0b40fcd950e4f70100fd4343c61852a99a19148a245326a218c49f4e0bc09f5e1bc09f6e33c09f7e43c09f8e5bc09f9ef3d90e0316a080042040880318a000041880000270130b02886f3d007c68c9a0c15299a959a598a444950ad1ddad1d653c7c78451bd287bf9787d6879ee77d2f2f2b44ea316fa779f0f067db7a891efeec1bce873ffb472e488f901fba067d83cc22e745963163071a355efeecda1856ab540a8522c906ea20a121e7f11cfdd3fa01c77817aff330de876ff13dfc3f0f2cfdc24276532346636707173362c8809175705abcf89c568a0a25459e745de779a3acf5e127da902efe1b65305ab4fe65e745a32c86ce87a1e7e920528f753b475a7f56a1ffb322c1f9b30eb5f8b3795cfcd93860fcd93974fe6c2576feec598c3f5b87eccfa68d21f6672d43fe56c0feac41ffab540a85baf9b36d2cfeec5bce9f8d7bf167e7f29fddb3c39fedd3b517d90e3176b20e8c172e5ae4e0b0f8d64dec7a1f6cc58949090a0582a048b421557c188a36e48a0f4315de87e3388e463e1a75a523930926528fdd1d1f2afeacb81f367fd65ccf8a3fab101f223f4574a83fa841c878c5e53543135393c2f4ab552a2534d43c9da3674dab419e0e18e65bb0f9ae5ff12c6f833fac82e53361994fd1aa798969a1819971bdcc92c5e55bf150a7944914c5711c65de8723d1866cf9d12873fd689c51338bf7995e9e1c65304f9a1f964a5e2a75a5a57b633b34090f8ef2cf9a83e5cf3a6bf9b3ea70fd5969e69f75477ea93c64feacb6d69f340e993e07febb150b67455768fd89f9b302d59b492606e6e565ba5c5a58564afc79df2ac3dbb156a6d58a442291e428537d58126dc8d5974a26d328ebde34ce389d4e2894a3505d29cada1b917acc77589290e967da4385a4fc497d68110a448dd0207a44050c47259313540ae579c81ca9d9d3cc5ab50d5d157cc2232631e9572f7e0aeeb00ab38c2cf89527fdc9fd289812c96d2acdb13ca990dc7f82694f6e5279d220b9bf44736279deacc8fde1fdbfb9b961c10247b421c7c71965a4c71967e48836e4fd9c51d67d4e0bd186347d8b5176fa161fba70e12e5c74a52ebe110d9a33b0ca45ac5888d463bd43a2449425533353f2cf1b90e6b0549af6a71415d3667f09a6fe429567cd29c684699ba592c4a4119f26689a2c35e74569fa61a88c4bee2f8d32c0ec258b104724c954eadf346b4e93fe474d2ad3afd3b708593fc411a9871f088eba074547d41bdfca02508494eaa84ef325da6f997ebbcd69ee3b327d771d38ffe13e7c0664a448112024434240468a24f9a9b5480321f1a14cdf8580ac914cdf16714fd279d775a5ddcf8f1caebb7978db7ed85ca66f716dbbbd44a67f9b474f10cf3daf2bf5ba2684e4c8112121244782dc8f1cb1428de3768e4cff368f508724d3ef8ef4cc6b1d99bed74a7cfe7d5de9174469f58113e2d3e3fec3878f8b13527d7a703faa8f9faf48a6ff11011d04bb52b0de8c30c2086b87ee1d67d46c048f8e4ae3a93832fdcf885aab3cea0e918b445da96888d2daed3bd8aedbc1ebc90b00caf43ffa936980427668930247327d5104420fc3ae34fc469952b14d4a76bb8bd65e7a3d4f2459c07146cda251261a67d42cda9035a38c7e38cea839a64d17c5ae54fc46df37a22b7ac26a8d3069ecfbf3f313e5b563949cca27d645c726b924d386b9bf6c495139ec44c7a71c7aa263530e3fd1712987a0e898cca148744ccaa138caa1283a1e73388a8ec51c8e44c7610e49a263510e4ba207e6b0243afe7268121d7b393c898ebb1ca244776c7368223af6dc3944111dd71ca6a854a3ee65ee9d2adb98f7e7575f2489ea184bbffadbeccf5da61fda91bfcb5cafb9ddcd21a41e63c2631d7698090d74be42e460020337790833dfd8e93ea4638def3150a3c85e761847f7493081819b2cba96a6f0eead3ef6706a4c80cac3c3c3f36d80d6f8726d4c00dbbdcd57879b6b48ef082c35d04cd8f17aff7920fb57cfd6c323109e9d897bbf882fdb977559a9fba8bbc480e7dbb6b4d66e210c6d48ef3acfeb3c120d37efe0968c5b5ce0161f2a13cbef7f7a42f8fea79ef05d7c814b1858c7662b69fef2f14d6ce39bd8c637b18d6f621b3366e9e8d76cd602447152bcbf4a952b691e9379d68c357b1a6fc6c5a4797394d1982fd33467fe35e322bee099bff8e66330cdc360ef75308b878171fe05cef91a0c7b1738c59bd8e69335b3794c953be1694173ec75f8b366c59fa6cd9f7fa6485b6e51f690f9c9e0e7e0720807972cf00a8e36fd6f7089abb5c923e77fc2e3311dcab346f42bcad3b429cf57519ea9577992a237cbf393294bdacb7cfb295e268d37e3a27973e6cd5136f32fd33469fe4563be8835d87beb3906f790791813208fb166264226481a46f3fe26430d9b314d806870b9630697351e33b334471936af4106bf9bb85b5a4cd1c4d5c585467b8926a63906ab82c060558f6b94bd94aadc4c959be9e858b9c345a399af97d79b5894cd97695df82c73647f177c7a135a54b9d35c51e5583e780cd9ff4b1a8b35f326ac5439d68c3594fd51587fc29ad594b066b80ba4895693cf9276d2a17c96ac59492b875aa690fd45342a94cf92766bd67544bdd12509f020b4214b40da6603896b65c17f5efa68edbd6437ca2aaa7390b5768a56efb6b66dbf97dee779e148732192d64a2b4d9cad5cc082eb93f38fbffa76de078623592a9528c5194f14a7e4a3ffe10df8eb02fe52831069ec51794213b193381129c511d99f8c86548fb65441ba50f651c69889a8e26c04ee292b115c80103f117800900d0ab4d91fb45211ed4780207c5458967ea5e3cdcd0ea85127e7439bfda791469bfd210bfcf0c50ec7fee77f5224abfbdd0541d0a47bac69b3df3bbfd69a749faf6994d94e825c8df6070932c6feb4194471c2ef9ac7bc6fd1fed81f7087adb5dc6faa34fb638b74ece361fa6a5fedffafbd5df8a7ff78b3af96e28c0d22ff50045a4ab1285618e4ef2b8dd2f4d3ebd77ed816e997570fbfbfdafaa9cbb27c1449964a2b2b2ba5d87d3ab1b08c321b2ca3acfb303c6badb57aa5d42badf7adff0d59467186839c15c140f996f6e7abfde4feaff6f1e898a8a35ffdfd25a3cc4b7126542249e3a87b9227d395ebad17d8a046cc5ca8042e842428ec14926eebfe5524a4a55ebfbbbb6b5b5bdbb6079d362a6dfac9ba5940f6eb38499190970cf4bb170009d999500984b2a5df6712220159b37d8b06d38806a11108095921fa0909017981053a28cb690a656fd166d8891bb8e5da392ab6e1f978af03eb8186cb6c794b6d2a7fda19cb81fce5f8eddb1a8d95f7df9ea8bc7a89adc058babf6953a72b756834ccfbe981c23c283c20d1c72acf4f0528fb874a7892ecef792bfce5b09320bf9f868d823c28286c54066afa8f3ca0ec2f12f29889472424123a43259a3a6d1fb9bb60fb2becf9b62cd1af165bc95049928e611eb8d6eef65a6bdda248d2ededeebd2eb8fb15bed9855982804a25534fcd5a5b92a45f5e32b4f24fa9b4d84e5f326455682bf8f52bdc65212d3c43285409d0cfe39b898909ed18ae3d7d208aa3f2feaad4abd04657e585cd51067e1219e6e97ea81b8378a15a27a1427bc1ddbb6095c7b8f5257e79f9e21fe231f1a0a0bc890785a4611efd5229714d049dca77abd7aab5eaba3100bdc33234643b1731ed60096201c22c3f54c67f94f2fe23f1cf91500e77f10bfd90bf702db4e3afe0161b6eb97da0080441151e2ab6363b09f2fbb08695fa48a5a6b243a5eb56ab6ea5a2b24385870aad4d4f823c712db7d81a89161b4b10c5618d1ed73cd622fd89f293fd698badc546c14f74c12adfd2fa55910cfe69da91fd59b04aad4dff15ac42f3d7593212cafe261e8fb5d8fce524c9c433cba23f4b8630c6f8316eb12db6ecad12d758d60873d589700dd7ecedbcaf52f01ba9fc6a655f65d5a5e0d3b3aa3c59b08073a8d05c483e4d3b700eb3bca16408fb9c2c5d20c2925a6cb866f35932847b700dffa8f050a1994299aef811f7068a679280d2ef1704cd813cc95a6b14e499ea7677d2869329fab743f0b64691a3ce81a4afe3165bcfd22ccd56cf88a5b5a5e5fadded58087db27b90d3fa0541d9ba7b3779d61cf4a980743f55d943317c20fb7bd80ba232fe4d23eb576f77f7200f0a7d3c0c37ffbe0fa742efbc5028f750ee8f27e8cbe142413e5e907f2228d62848d18b74684f8f1b7fe15a240ac1eec52ff526f509c25f5e0e020a9ff0eb56d484a138cafc7b90628f40f8df0ef4624c531cbd91da9a1794bbbd207fb51734226f088adf17966238bc4fe277bc9209f7b73f1a6bf8d7d2b6e97faff5a7b97bd13b17f9b52551047ee2a87bf1362a83cf32fdd03a415227ac135f0b854e24fb4fa6f4a3209bccf4ab5190e7976df77d5f58cb3002c1d0e4a4340253282857adee55b1c86e754f24f15b71c1786586e6e5826b52c458152b4c261530d35b618271b17cadddee6eedbdf7e545a775efb5a2ab56f7aa5864b7ba2792f8adb860bc3243f372c13529626cadddee6eedbdd792ae5addab6291ddea9e48e2576bb7bb5b7befb527afb5dbddadbdf7765f8995b997ec582c56cb962693e9b5b4b4b858989d980b173b31173645cbbbd889bdb4745ac480d12283c68d0b062c40010940801007304001623ba2dcf202001f86c47668b8880143864e8bd64bcb10cf941a4f241352ecae150d796969d1f9cbc34b8cf7bc9b6f7969e9b48801e3c6e4992c30aef16f478a2792fd44435a620f23460b1d1f5adfc3cb4b8b28df0c89edb8880143a745eba5e5a5d5d2f2f2d26ab1e1f82d2f2d9367b2c0b8c66f91ed106327ebc078e1a2f522caa309b66288671a4f2452ecae150d318da3f9a10ad338dad1ff68f24c1618d7df8e144f24fb89868cad77c1b0982b3c6c63328da2fc435a2f2d302e93c5338d433cd37822916277ad6888c91b4793c91b6d091c4ddeed48f144b25fbd8d3231302f2fd3e5d2e29944d95ed66a88671a4f2452ecae150db9d692dd7dd3e974427d6f6f478a27d2271a62bd279d4472d5e1147caf15e56f88671a4f2452ecae150db12e40d1101a443444344474573642c083f2bcee3d922453a9f77c28452281b47f92a8d3ea10b387247f7f5a1d5f0e9e1cdf97c3023e44a67cc88744da3c6e4770e27363691d02f2bcb96931be8ecefdd3854efef4a194d5dbb739680c25f5f6579886bf9cf42798fa8bfc8ba9bf4cce551ea94c2754c9db4f5158c9d2b2f7dd87fe1dbe0101e9b9d3f098fd4ccaf323c9f2244925edf11f478bf3d8f77d246971d93fb4f5a2b0c99f30d959c827048c823c4940619d037992805498f428f87e0a9fbc09935efe6cdf47927ea6f2f9d9b2bf58824940fe2a3f40d69070cde4934827a4b7a3b72f52f9e5911d6a9ece844923dc05c9221210aabd3100e0cec9f497bf37f6787ff977b7b74aa5dadd411449ba48241292cf4f4891ec1f7a2ec1a71f7f759ee78d32f265265190a748cbfe61389280e24002058e1b1aa26968c1124da0c066e40c32a03d4b4c8127090fa4c083c48efff5d1a67fc59ec395a8c235f728bb2dfcd852b39f8eb7244286cd183022dfeff198e616a63954115517776ff706de5849a8c10d2f108a4112206cb8b102153ce10362e0f1a9dd088002361c99810db8700338bc40c115d6607b01146b00b2e3cfc27e93b449f398db7b73dcbd619a5368769cc7e84e3e5d0a34f764f75c769cc7465bff7932da3c56faced2ec249fa30d833fa032fe2a180442a5f15f61d00795f14fc1a010a80cd8447d8150e8b1845a76d008b4ec3f845aa6b9277ab20e99e69e08cae08de274f6077f4071fabb0455b7766b3cfa05e80309d2f2945eab409ee06dc847c3c0200dfb91fd2cdd465b2efb68b3f9749ce31a56ffc6ec0c28e8810e8204e10c5fd8f12fd13c269a81086e40c30bdec0630d3b3764e005320051a24711e600851dff92cd631298c10daac084190fa670841dff8afdd6a6a3e0a766fd145e51b3fe094e51b3be094651b37e09ea642a757759527f75833ec4fe4c73363092cfeed27f1c65d40cde4e4076f3384fbe79ac16e997ff2a9fb5e7de8ee42a92ec7fde9ece01d9c3fbc6017982b70ce2c01f0dab44bcb74fe4727f9829901d3e007df40bbc350ff0f6dddb924c81ec10baa51d2778bad276d9a24dff1b12b9d3848f71b4e589eaf25ce52692eade40daaf3de38cead3a6bfe8c70f70fca1e6aeb936e94e133f1ad6b5dc69a2e787b7d3c40f1f9e8f86f90071d97f342e17a4cd5a6b2da2779ac8ed34d183f3d18de0cd63f7e62fbfdd5b76dc08d71bae3f702e48f6b7d5078fc72a8f7ef9d79eef23c954ea7ebfcc7cd69ed40cfaf2c734a6e32f3f7f00d4f447a1b020daf49f41bb903d0103a0b0234134ac02f406e4597f641f17c8fdf5fc72111677b3652f42ccb5da1097b84011ddb9276ed926d31c4e0eb94719ada51d27686dd2fca34c81c82006e38cce18a851d45aebd766a53254a659daa43f394c89b4b4e3444f9b345bf0d6b0ced64ab5af28de87698ee803b8e62cf6fc612668ae0848e43acaa899da80fc9ac76e8d96661eb3387a7ea55997b534cbbeca14480c78e4f3d6b21351bf773c4c776cc82bb3f65299c6f5b2d03b3a6fe412d5117ec33ca26fd85bc510e4db742090a60e2405a203473e6991ec3537399064ff160ba859d1b057ce7238e2df4e615e7863dd9f285f69fc4b2ae37fbac91018f4755222d9ff522864ffa64bc8fe25c5794165fcfdbfe6b4a834fefe37a4b3a484ec37b90545abc5ca028ba582051595d5cf6a95728594149515542a942aa0a0a488a45227434e4e4ca860625232859212940f0a7592c2e9648a82c9548242a9440a2149d21348a4911346a3b109e328f68862c88430142d4124029500825f90eff392e0791d12baee02b9373b95a91606c571f7ecff82e274bba038f5a605c569d4584b92dad28e13b52aca426843d6fe50f44598aa278b69664a6bfcc6ce4966233bb51d7f4a9e27248a63ff7efd4b71baafdf511cefeb7b14f6c1688b19c410704003a84c7f871980174065fa2d56009599007da16a8e10140d09a03299f69912c26395c7a33f2620eda75068ecc671299fa92c7a0450997e4a653a01350d540a64c85d5f27f542eeffba467e9d794c085a79fa3dfa93ba95a794291018ecc84d03929429902ed8209fb566abdbd17944f692de325db921aa1e0c42ba436dd83200c13f7510fdc9e2540163c5cc0c8c8ccbc59222f79f2eff2a2fb9ff6ca1aca4504c327dda245349ee3f455f18fe6949d4ec17e103d0573f8807f099e47e57c9fdde5591fb2d8bdc7f001a2488f2d4219fdff7654b85b0b6db4b639e8751fd320294533205624409998e407cb987f73a4d7acc2b3bccfdb57af544b6f3dced519caebf6b8ad314a7fba6b7ef105ed051587b6fd70d8116aa4e2655989212ae4ea655c80a5b412e60010b5820caa10515d6d593440209245ce00217b460051d453fedae21206bf9d5ddda7bf18cecce31071fc29fe7894694ca74421ee441ede2cc5e8b047b6dcee63e9bcd5ea76ec8c067cbe5c22ef4c22f1485611886a552e914a2e850ae240cca3eca083311359f9fedb3a5a0a65042d5c9a40a5352c2d5c9b40a59614b059694741948d56873350e399bebe917d99c1c6c10db637336c762b15af7b3f9ab6d2df15ba25f6dbdd4ffcf4291240963fc62a92cef97a3ec7eb5ef1e7eb6137cb625be5bee3fd9927cb610fcba0fc5592d214318865eabd75e0159abbb7bad5e6badee5eabbb7bad5e6badeeb5babbd7eab5d6eaeeb57aadb5ba7bad5e6badeeb5babbd7eab5d6eaee5eabd75aabbb532f29f5b2b082147433f2fbeec6fbbeeffb3c4f241289a2288e2808c11169348aa108e441586b6ddbfd99a029a59492f6deaefb3c59addf2792e2388eb25a5638905da24895564a92a59235555a294ac90d2eb5fed166684dd0b91a3dcc80c964e41d1a3bb8787b010a7bf1f68ba031186fdf080ad3791a4481e80ff549414f9a9b69f3754363df6c4561353533c64b0ccc2d08381fa02fc0c4a0efc763a4dccc63a41af353648af22469ca9f3c33cbaf9a1dede730bb1c837bdc87c16f22d4b0ce6ebef74f61601c303e89df815132616f0d236941a0303206359c8fc74e38a0869d7abe4061a727e07cb2ffc92728fb93721e33b10d79cc04a8a6c634ff532992b4e53269289b18954350247378cc24477ef161d7759e088a429148248ae38836ea1847dacb34cd978e1db3d188245624248da48344a2bd4cd37ce9d831a391aadce9435234714a0e51a24a88244332318a0c69626282ca69f13538e6030b980e2b6c54bc7c6891c32206d361858d2a97ca2a5e5ec9289b2931f1490e5326a7c6712ac931b8dc517b294b5a2b81c248647808050ba18c41e0201d34d2d391354b116558e69477c0250f1e72a1502e195c7764bc64cc9789d2222412894c353016c2411897ac1e568e6563d55834d68c9523c5c5256525a596c223650915904a084b8b2ac7c2c2c2c2c2d2d203c8ca41715eafca794c8666becc1b95495d892fa59c28a6d4c02d403f54e65f84bbe15aad56ab4566f365ae6834951d2aa38c5563d1583a582cdacb34cd978e1db3176cdfc4a44c0363a120a01f2ae32a2a2c96100b092b09ebc6dad13ab166ad56abd56a9d4e709ce46853884795cba98274acdc4153698d720f3566f050e248e2776a7c143b3cbc3f19039296fa93acdd6c34763006faf1c1a1a0bc2ae808ab0666cd5a079b0e5b0a102925252525258564b1aa27882af73ae159fd79c2a3caa972af991b8b467bc1afa781657ec698773016c2411888ca380a55c262b570b489e3c1d2118359339313928989898989c9094995abe13c76c2539281cb9c0c97b81d7059e3e1a19933e68b55d2b09863708f9b77815b80a88cdf984cefe5509904070fd1272412894422d1975f33f365defccac4a14f2657785670ac28b1449b3bda2c098d4aa552a9f42759faf31bd156c00cc38a61bdb0b068e22fbff8b7dccb301ff32fcf3202615ff5e27fac61dffcd0c42ffe6594bd7881c71ad6f4f0c53162c468797d0c4ccd97895de20b9e09afa2e6d090e62eafc035bfba6fee5eb8bf6d5e21aaa839ae4f5198f8f55714d6f2f54b688ec5551aff935cbf44612a5f5f4461abafef1496f2f547d5ab60d7f2f6cd35e5afc8aeaba3d2f85fdadd01da2a8dffebbb52559ad5db3fbeaaa62c8118bf755c5acdb13e7747cdb1446c35c7febc74749766cb1dd96b39becb75b8be069f3772cdd35156f33a0d13ff8c69f9b306e6d65e2a7fb6acfe64a5fc79529a95b9b280b74ae37fa6bcfe44e59a052a53cbd3beeb5fd8fe541a2ba4d278cdb13e95a6e6582295c6bf2b59fe966797dfe5d95d0ff7bb9f71bffb1bf9733debbbc6d7dfc816d3fcc245741e4b1595c6f52aace33538e65d18e643985751736886f9184cf36894c1942a48d791e908f33a6d858cde9f8ebe545a21d93f65854123545309141482c12c983ee0fb5b22adf7b73fe683cfe293bdf5654924fb9f3772d3baf7faae3feb9bafd7f17a7620d7b734d7ab749596d666cdaca73b5898e633cc277803916012e1e5b3342bcda084830c83697ec134575caeda4c15813c2d4d6646a61c6d97661ff2240d7b2733050283388caaa7630d54567dc5174065150d62e66bd31cd227d787f17703795e2017e1790898af6fcb93f5c3ccdb9877f92eefad1ce202e510ddbbbefb767d370261fe6dfbf744657ba280a06d8e33babfa53dbb17fdfd0ebfd47285dd727d980f6910f6657299e6ed873dbcbceb2bbef769f77a177efdc51dbee60b9f36b28b8e40982e1bdd831f7a1d7ef9b08e00a899c64bd9b581bca50dd4f8a1d78d3cc4bcf9e12776d83e1d015073cc79df96fde3d3b146ccdb72889837cb2164deda409db7b481ca31a595e98146a6a4acb208cf33ef52da9ab208cf30df305f7f9829cf9a61fe3c22d78bbbb187b37b1b81ecfa6efcc17f388bf00c539e1ec8e30ffd43995de5d95f847fe31a564b2fc27fdc7c5cdcccc71a2319343f53e3c9333faa21caf7ed08c40d6fac517fe675bce28bedd8039847a78d6c69cb3c6d188cf9f2b45f32e5f9654bbe9016fcd1df7288d1cf7c8f3dd0fcccd7b1c6e8671edf6f7cda18e1ceb47f87b83ff3615847205cfff2757cbdc5dd3786294f1b2f3ca37bfb263e6bee2e9e317fe675fa05bb3a373e6da0f2ccb75c4fdb345db72bcf23f2cc8bccf2b481a2f9469e2947ef34dfc2e70e36b2e8efadaadeaaac066aa6f97e9aaff8b491eb3711c82ecfaff814229fa2b781ca345fc71f6aa6793af680ca34afd31f7444c8c8053b776083e08d06d3fc38c8c81121236d7a5f22d93f6c7c6d68c0e6141e6a9729347297293cdcc8f56f0c61cb5b5e9f219044d81b0d3e6ba6f9c64df37da599f7687b824ca9613f053472442805047f8ab479dec834a84c833bd75156a2e1c813bcf5d4d202a85c5ff4358ba8e8bd2b7b78df8da81d26ca941af553ae91234229d72703360ff193fd16c9fe37f28d4c4719886b1c797d1a4669be3fb25f21d945233e6fe4f1eb281bbf45a28a691611e127532299e6888023e78680245b9aebbd875abfb1fd1f3a1b2db22dcffa1aa8d93ef9b7f1ca5bbcf295b6b95271bfc5b8e2ce1bd99efd4574be40dcafb8ffe2eaa33651696a79cb14b0a761aed2e6dd3eb529c1db57965fcf408a7ef4d1304ad21093d8731e7e0c4a2086b4a6c046a4789bfede6ff4e54ff32739f3672ad7b45ab9c6c55ddcc5e5431b5ca2d14aa98f4a331b4c77da0952c5b3de59df7a3acaba0992754dd0c73541dc689a7fefb709c8fb3a6e9adfc3e6e968dabc10c896e7f641b6ccb77fde9b6dd3e2c05bc3ccf79507711eb3b9dfd9069774f4cbcbd2ec4d5c834660620a94bbb7c1a5d9697133f36de70d13532ad3a0cd062ed1b01f1ddb2142deefb7b497f2559ea95c67d0b9d71290321f6369e58b346d8a3f41a0afb73cbfcf7a79a6b28f444ce687f93adef69857fa85197d0a4c7764301196756b274824fbb7307892e0ed3a718240b23f0c3e8bb02a708a79031de02007cf4e8dbffc3b08e4599aa1640a04062108df53847f6b18e9a3612e26680e68d301857539a8391667830abb34a039b716030ab35f73eeadd2c080e2f8203bce6d0d766ad9dfdaec2dfb9b1f8636e4bd40202fee06de720d0383340cf6bae1a5a1f73b4178178ef9f44b8a433efd158a739f9afe5df7c44f2b53205bc0e57bf31808fec0dd480a040aa0fc7382371f10c9750f480a040a1b981bc205fd00c9024eacb866d0567328a5790268cb4e50d33ffb5fd0084a1092fd2dae8248a8559008190814076c82cadc6a0ee8a3d2f807018b90cf5b6b0d69b5585460b154a6a0a2b2f259ad52a49092a28a824a8502051494949054eae4092727264e30312969424909aa07853a31e174322dc1642a29a15422839024290924d20809a3d108641cc59c288647084391114422b00820f8fdf83e8f089ed70da1ebee13f9e6fae0adad7582e2b87b1314a747a38c6e21509c7a47cfa1c5a5ecc234f70349267197616010400fd31d4a712a18845e41f69f51976b24baf0f571834065fc497c81503613e409deba27df8569ae086cc824f632fd4c40927fff2ccd280eccfbdf1fd0fbc405925f1e66b4790c04427d39d884f10848c8feaf21647fb3ec1a09ba2c0eb374a428e6b0a49fa0e0ede69a40c12964ff5e823cc1db1361bf6badb5d65a6bad96beea83b852ae28b9fe794d4872bc4f6da9be509b34370fc873b479c9965d066580910fbbef7affb0249ac61f689b2718a477409ee0adeb488c5d6267129de2bc695ca24d1ff2242d2cae55c55e0e6dc85bbb3cccf2439648c78abbea639796667734ec77b860e8fb48f2f6e0cda2a44e4c4a5027d39ff5415ce9cfdb0e2e04e901716d7a8bf166d301798e361f6d8eb7d136dec0db78b3efb5fe686b73bcd93f479b2d47077dfc35dac69b0c9000bf7b7bb3e6267bdffd798364ffb691aed2c97b33dddedb4d7dfffb9d7f3baefb9f6a4bf3cfcff527992d88036ffdfd5d09fab0d5559e64aeb6164101710dbb2578c3200ebc95448040faf4b4d9309a55aff2a76321fbe950f8bb908e81b7d389647fcf35ecd6dafcd1b06fbd0fc52e8f0e627d80c4dacc90fd291872b534d742f61616cb63794ccbe3433ee4495c2eeb7299ac9667adacbc4e7b92b63cdd3dbc5f8d2b1fda90e563233279ded4c09c4fbf2ce3aaa1f9fa5dcdb15fcbd3468b5cdf055f1bbeb51e64fdf3d6f2e5d1a683b7174c79a63ca63c3fe63bf056436331d844a5f19fc1a00c067fb419539e264c79fe4bd95d9a75832920cdeffeec59108ad3a36d08c97a57597ee3223a5f1ef65d7e94b9b8609b57de872db1e29b55f8862cb18eb21e47b4d861a26b0bcb4adbaf6f6b12902881853694c1e70a6978a0129c4009212620811076c6254a8c316db3ef8c3cc1dbd9b3db78bc8db636471b3dc1db509e5ea8b4a95c8b82a064a866460000008317002028100c07846192648920f914001472be5e5e4e180874184541104629640c2186000008010020232334630200e2be49081a17149bf9b977d42c95346061a08395c868b7440a6acfdb077563f534a416d5e6e3dcfc5ef89780f9c847d177a6059e8b5ccf2c71060e63b1c126e55572426b893563aca535238e53666e03047f93e23b42e3eaad378327da4c87f73d769f54aa7042671e122422dc9e6c8ffe8ae82a9857a0909edfbab23ef8a9b66f29464be1231780e7f459819e3d3a0be2ef2c7928753cf1f7938f0c4e527875511feebdb86a6d69433e058636dfd35f905b91af79bf44adb6d44dc154193bb6ba74a476b701fe8241c0ec8bb9d59945b500062f1488f6b00d1ee2a4c9a3346c8a488ec589e7b5e5fa53e3d4b94c84802393214ea9937eae22620c322eb6c8658ba8bd9f4318fb23ffe25a67279896da2776b6bb02d73e13770adfbbecc1c887a64ce462fde5e2c6cd7c4a48ca05a28e414b37c67e56d3a31ece39bda6f7440c4ec787cf3b44a6eef184f4ffc772e971dd7ee45839544ee623ddc7624711bd2114a3b871895f9f28d9caec7940586d87af3858990a8e4fd64dba61c75781112205b666dd2758669101050481183cf6db1b53afaa70094ecab594c2041fade5a7487b540a9014ce33ca2679d1c84f00379388aa7b77bda2b2c2d9d4dcbaa1e7d0dba10c13039002b984dcfed0d5ef4dd3f2156b798b7dc50860e408f2be260af78cc48063a909015273cf1757f7c82ccc36288e4d476b1d7761740922bf4c662f8aaf8e66e7cfda00843c7980ddfa20c24c292c5bfb44a879b2a7e2210998655ecdd2c826031b7510fcc885718948454d51c40d75cfde34f06b2b9e1625ba56e1abe47e1c31a82cb348319dff0da691eb14a2e6c1f9418c33e14cdf7b1928e556e4f1293957955d443dd5015f2cc25e792e4a5a9de1cc1490208ad23bd956702af8e676656ef55c69e12a8b2616390bad83cf09a1a8b92f4e61d1586fd09ebfd2e5d94220f7ffbe03705cdd8d83c4070fb01e65371abbebc22ebeb22f4e82f1164f2e1c70e3d1ac2bb4b1580e39e3719bb6da0487e3d2bceb147a89227388b434a90eddb3772407311ea508c16cb592e47e61a55a1e540c79a32d52bd0a40127b2898eab5243185d35e46c03d7bc8161327c7763377631cbee06b812dc4cd946c09768554622ec2d9918d256daf6dd260171c390a431c00a08d5486eb5edaac8e096f813d1bcd9c21f3b3ed347254ad2d07c8fe3d8cc9135e76655ece18590c0299b9833f8b378e7b41629c22c221606f8784058fcaf993ff82c8e13e289710acc0fa8a9cfd156201b29085b115e414198160ea6e2c0ed2748177edf774827436364f252e41583486c0409bc65350ae87be201fb614729127a9b9d2e7373b964a8e159c10549cb915590da2a570b1d76bc5eb7049ac3e989d4d917a61c9000cd9429529c9b948885e413506974758a7973082e2725c39ca3499e4a30879d2848040cbd79694fd7121b1fbfa8d7f8d72363fb90817f948dac04b5ac39e522fbec7aa748a9421deee95cdc3907743be6d8b5882a48f886109f8b6a9ce4910d3be87e3a43506b31044c0fa7c15371d15c2e4420c10cca274c102cf447949102b14534096397fa2f7f713770c00e2eee911c482dc4ba2abcdb3b6b2a5e2a65a603fc1c2e76bd41bc7b24830cc61c916dfb678e253a69a47377a26cf4a798456c530c55c9708e0058e195d54443f34e95d73e542b173036efa52264f7b2a21303d1e92d338008d71643f8b286d428def02f20c15fd02f196c6f14f8bd689b17a851fbe06d9074549b34c9badfd32697b3911ff10ba74b2d67c006dff5de3039707c65182391498caa0bcaf32a75ad4e80668bfeaa1881dfbb51fb986b403839adf832cc7339362fc632aed5b19ba0eade76fdc1e03bd9198a770ecaf0a47184e9d51c4119185a93094736e31c14cb5205e2b87f4392809c45d0ed7a29484a5341716dada8379116ded46ca52dee31a005357c9a9bbec387913a04e3549517abd8037257fb46f808dc4bb08e4db2f479c2e401926cbd9d577feb0a00f77e925d84ab917e8244c4863ae579c8c0cf4801b22227dd6a9193793d2fd15487843e8686481e41b946e94ea918eaa36ee003efda94a139aa0a606396e4888c148c04c4810b5f2c8769dbfb6f70e9f89b26861d2d482b8c8882ca9be8aa118c93b983afba4a3bd9d20b8d851ffc0adade76f991b8242d666094329e7ee727f7cb1c21909afd9932c1dadb02176a92396dc30661c6f3c8923b8cb38ef6e5233a7d07bdd1562391e53108c7fc78f4c4d25c1bf480cac4131ab4058da74425721943c2e3084b48403f6b6da0bcf55282520eb3c0d4e810c0a3024c2cfb54769a5ec0cb5814291840d12a2cc095b1f2696026a41d0fcc8ff783ec31a5f24783307dea8baa0a1df0bf217e6f1080371cd20cf8dd8734692c0736d9bfdbb7873e6fd8c6633eb777b2cdde0c10f6a9e53af6e42df59881c592543fde89eb12f78bcab78c3d8139e3bc7eecdb1607760500d7c8aa91369a7c9f66ab8a11c012ba15d51f54a035d97089e85dbe447a063b42efd60aec43d4ad0f8ea6d9dcb52491721dbdc6a3fbe4e2f1e1f3c0bfa3a60c1a4cdbc32d2edc431d63e2e4b495aa53876f86170c0fb4b0dd35f0083f5eecbf0bf4571f402e644738f5648a804e5ea01fe18743f81d89fcd48d1272e0a3f68d9a22aaf2e61bc53edba86b008374f72b611a3b0ce6ea514602dbafdbfcfc61191cc340be65ef02914ee61cb9ff6e7b398481a455de3f3ec89dacb1c43c255fc19a184177b76e0796ad01583ae1684a2c072c9057b0f194d569d7892b478299613eb4ce33679752361452e0ecc3408c924923a7470b8d0375ad67c06496fa52a8b5e130d8bf7c6323d56afe1b69c99908c40bb09647ffafc98be5ce572f3815c2022f81ee92550c999829d85760ecb23309d0ee9ec2d2893a3e368ba80115d4d94deb397b49a39913c5baf298b1d59bf917d9765e0150454b1402a6aa87d8a6c9b4fe16c2aa335403fe43255cc408ad38bec8e5c04f43609b5802370f40a3baea19908a7debec67d6b022c71bef38a313ef20954c54f869e05e57b01f51356534f5a5d3681bd528abad2bb1966560f6e777f5a5ba5d12db28f2a05ffe5ff5505bff7b96648b1800c407d544a4ae3577ba0e802fd9dbda122ee4774701190a7035030600e8b485d4ee3e2b17c4e81ce731bcf9031b7e79b01e333cc4e1c02f31e0ea20f13e864d0cfb67e0e69c54451bc81b8a4579c014b177f456bc8d9d84e7919b1f5d5bf2dc9c3f31b96116e2f00bc92b6fb3285247ffd604e5db99ef92b6f6d1bf0d2f19a38d4e6b373a084218b16174489b366ae5e0b6a25dfe980ed9546f128c5fac5b4397a060f8bd84dab765a166d6fe47e1f3a5f48fb2d26fd2796d4bd84b35045d926e6b41fb5fda7067e12975e1dc1e3f564df9c6539ba1aa6193b5acbd949e4e3c7b9a0ebe2c748dacea6e2199edb057e6d5762529fa70eab42ae930d39115fcbc63455cbcaf9b3058f637e5a1dee02786736e4a076691b72aa3d0af7d7163c87c19544a5494522222e6d954b9aea00d8ba669cdad3db2ca5ac2655b17d88db2e5e4bbc64ad34f3b945b8b519172be698b99a2adcb34000f46a2d948cc2644e137d004c84bb3f0a8d5121249ee72ccd809f9298a187f8fc3bdd36872ec4c359a6c9eafb0ffde1c70fa1f75e9683ece70eb53a18601ba6d32a936ece38a9530c9a3634f7a449df5e8335880996f7b762d4e5858339029ad26cb7235e3eb7c6ed97270188c4c6abcda10a301f3f1244af05c6f41fbaff9c1e6cc2f1b415edf0b6ecef703b5cd28b945f7d02d7f2c6e9b9eca596e35098b7392d6f36ce3b03819c2002b36ecbc8660ecdf69e8be7e8204c71c575dacf26446716a3f6451d37c2a42dbdb92888d10e7c49a676274769968d32b2e18842888895f4a65ea5d5db06f48ead5bfe56f1aad94ab6e9cd6b075c272578a250d69d4b00a4440329f620d64dab6d31a4407744198970c08ec0224bac204ea8efc8d55f91ca6df5a88b47869b827279ba71f76cce10dcb02600d81bd9c35969e18d70bbe9eaa96c06944fea8b5a8d71b684166dcffde5d95faceba683958dcf520e0f89c06ada0c3b07ab8279d03a453b21ba9b87174312dc4c8ae2edb0917cbfcc7d3ebcf093be5f62d541cacd917b48e404b30e01de613a99c10a8ab5b13524f42b2c44d8ac10a06e6e47d88da355cc6a74e89474ee9a167b430e05a84cd854c2c118fea367f6772124ef4aee8f67fb7d8d16aad29ea93e73769084162c59ed09c561d3d3c9c5cf32361b92df24a81c61e08251971c3d349470fc8cdc3173fc08dca165e240c0076ab3376523bc58b83324180f91a1959659866c8467a09da36677790a2b217c5f0ee06f1589ad96776d058b49e22af13a8114ddec9d59a67730e604284127c8ce7a6122c7f86a1030cb4aef0244395961427c533bece3556435fc89275e18ab93108d63cc975b97b583aae0cda1a930fd34808c1ccdf2a344991b9338144d626045adf174a0191d7da065316476053577bfc8322ed2cbb568cea93e8b696934adf7ae59df4cffaed4cc524b2a05579a3009e320858ab9ba4a981b62a8f5254215605f83226851da8a2e45e64852dd41105d6ffff3ed31db3617c660033b8f84e8ba28196dbae7bc689f17d8fe857b2970d49870da54e8e401d254ce4d3bb0884fb7f9d976eb19d694c24f0a1ca314e6683eb15a53fd08aed397814ff896244ac0d0c2cfc9bac889f0d50147e2c957cc9bc37bee50426a1b6a1786b8958895f978a557ee66d3708b2ecbf9a3b8903ec25536bfdc2d9f64a82f477b86cf54bd23556fbce4bde900658012a63d184009cb9cdfd07cf1a770ef0980fb6263950842f3e92bf9cc08220923489ca9510151cd36b4b8c4d6d93a1a5b6c17a0da3b4a553cc88f1440eee21310027eb7a47c0e5336471d9f295680d25ea0e0f663f540a1eaf99cfb19bbf62a3b556e75e050cb1e777b413f48da4a8f7b578c50beb0a898d0c3cb0103299ce01c238b11411a7d06e8c1f25d56ce6f3bebd3f5e2fd4204e93587acbf7ba251cbefade6534794b5c627ac9182069b9ba57f7fb7f3797b8973ba429c155e72301ba3361245cf6f1f9f33b131a3ff1c5c2c31b611fc483a7612bf995ef1d0f17d96319c849e7ee95ad9a4613b4486037dd7a0c4e506dd0ec9162d30f7d013d0cb6b14134788ed32204a68cb4000af6ad6caf81adacac68a3fc00c9ef874332a2ddc669a2ee5b2ed35d82ed74b0013d223c875a5dd6544e9b4e0150cd8c2df23ec36a1e9563fa415e0c88698801f0deb6e20e756dc8b43c929f14fb45ed5e8d4a7cd307e151e2c4969927e80a153607b8937b39d3f147b0123886b2f52360288893bba91ef50e4e9dac770c3f927d700248dbf92d2eff4aa5585085b6ee6a55d2186f8d77675de92aab92b9e29df4f88d1fd9964506829eeb8090ac0d02eeab520be43941c65466a9c553e5d76e1b08d4db446d671154387c5dcd6e53647dfc0e6fe0df337920dcadfb0eb66245ab9a8a8bff32398029a718fa856af5414cc7d8434fab2318caa6ba709c25a9b6b11fb4dfa8edd864e9832a11561f0cbc60a6caa386f0844313ef4aeb31557fb62bbae093a929fc2d25cfc5623614fc465e80a7b9bcd3f1ddfb56aa4a7855588930a36b860f0825aa93b35215e50ce86d749674972393629b1e1463927a09f36907fc24bdac0baff43ebb5d200d44ac46c1c92085b26cdfb7e5da5212bcb81a5343e8003b8b8c234932bcc1f385a5c747410f7f30ad17d748bf96554ad97afc129df5f1564751cb96586f296a218dcad6a38b1886e7905d4134b4ef2a787b8daf5867660ff0fae41dbdd245320eba0e014805efa09b925d0a6ab579ed9294fa7e7d575282e154b9b81e6d93a8a6429fb540655393a4db8a881d33e8abdb65019778703071fbba9e627d1081fcbcb1a1a32969f09257b5ed9dac589da4c8cb5f7fef93e50bf77043092e4799ac172a0749c26b1354f0c31ea3ef5f690eb256deb1b6a537b17f7d2c14b23432ece0d6b9ff96f16a44005a9b3a1a20c44daa33690f8979f5e32aafedd98ad69c5425f3ab4a761ef52beab386470ccbbe66013812136fb09ff20700f26c1c131de8a385343f5137b18c27323d109ec3c8deacc163684b247bd1662caad3000f6a88efa0361e95c57a0f03b6f60a7916b0a0a0603985f71706fb49a4e8dd355927661ea4096a5337fa365e94e0d4f4a35eb2e49a6a4cd0964c359fcf9ee6b646f454ef417ecf390c81f588ce1f522197cfc8b63660feaff30ca842a0ff5a1b492e85173a7e2e7e661977f15b8d6107f060cd1add887b65cf75c69ef7b8fad10140179bfd35ce9e198f025665b0ab8ea9514289b45a2b12d7f4988e04ae263107d676eba5294603bec57b0543d616cab6ce74d2978a48b6dad13ef2d54ccc82ff179ffcd5c2352f5e9430e4927450f416eb0d8363a5eb2a9462bf01c1ba697e4b0edcc9a91c4b06d4a0af68794374c4d85c9e99bf9e6fa34e0622fd70463649952db89ec69a4769a31aee8b3c3be3b9029e8756dad4a49524158fa1862df5e10cff544108bcbd7f138ed4f45e037c61605716bb6f0364a0471d3ed3088532141b3d71dbbc69d22ea1815496998d9ac9c154d4595a489eb86914d8fc5ffd6c2be03cf4a6c6b9ecb78db9b4745d181b5f10ef35b7040ba9c275635e7912e2793fbe63ccf512bdbb305334a73c2dd4ccf7ddb5492a563956b39779ab1a86d2e815a6433ac8b5087f9a4fed8fd34f5535ebd5c817791747af8c778ae7d3b7c62535b236ae666c07e8248bc4d631b8d957d60a74e5e4ea5fc55b35aabfc6a82f45a45b767fbacc572e12f99a59282c9f66a794e7b100376becdf71ab206c82817c9250688af8dd2651a6156f4e5194111669366ac0d8add23c4223316c7318055b0d18f5e89de6d2eceb712aba0b8580bd34f8a42b41ae4e6de7e75565eec95cffef465fc399e868cb5db1447d435d826970783f08d7823ba8e3c9992d99acaf200122132187bd00a154e0466e4ba05cf4f780233d17016147cfef24258a1139f5b041223b35cfc0bb14394919f3f1c34bd10aea658eacc84e295d15e97292a05ea1f9e254399fdd45a0525b859e723f62546e8d81f3fd86adcbaa796ae819bfe8b39ac4e703a4e38f1867975eeba042841a8bc85564924494d2911e7d1b9f9341216dc1950a86c12090e88e2e859a8fff2fef49f28615b5473e12574c845c973b3acb9dda8f6619a5ad0d354327176686949eb11eee1f75e60e223eccec9b488fc1662f5bacea3b42fa2ddf55253b7bfa0efea77129dbf82ac6ef7313c9bc53c7a7f22fa3bd7747cbf467ab2d711f4b7b636265b9975edc7b43c779bbde587244f41d3b1c3f4e2ccad95f8783c8dd58363ad5bd0c091c2ef673201fec71f88101714adb6a418b7063e13918c84d8b0b87d12ad24682b3d5e820fa25c5a36a4dc1cb9e62d1ebdd2cd3bf3d34529b57cb4ca5ba2e94d5aef319c8975c099fc55773364d62ca51e5fa38330f1e8d3c110046cf4fd30f9418ed0fd03531d0e8cc6681dccaa9702a3907c82497482bd6c2dae3646da78088f379828042b02082b0b16bc3405f9395ddda995dd55fe6662eac66c0dbcec86ad8a3ad41cdaf9a63ab7965b65814645bd73fce6e73647138b70469771a4b739419d4c0cf4466e2bdc6855ff159a928a8ebeae6e2261b6154010c773b7472f1390cbda018fea425213941c2cc38088ffc2add409791cb25788a59d594fa14828509b97beb5fe6b09e1e596aa6abbe5904b5bcd7d3ac584f2aea7c929db96339f6892aacbe2d2e104ff4f7d404e7fb57444e464d95f47f31f0944f87c374d7b1ed817154e7fd2169c3af1448f3eaaad074fb60f59f45eb307c2bf8406dec41b50586e7c5b190c639aed21c423d2d1c9d2f72311aadbc7b14efd7640ad61749b52ae6300555eb8ed66aae199acc87dc78c9f08985a2b1b873cfb94c6190a561bdf61c756fb59b6096755fe8b20dd06742c66bbb49b6cf4c7e667644f63937b2855bc4dae91edbadcbab1cf07836cb75a6ca370208f8d3c975fa20b1101e85bece5ccb0907bdbad7d269bb171bf82344f5a5aebee838d7cdf363e075f3b33f33382e03aabfc7118d6f15ead3a816209d3ea0276085173c32dbb3cd3ce1025c800166cbb75dfc386fcdac17350fc4c3de7681ab4854b24f7704537dfced30227da9a46929c79df33870115a75756b3aa384708cfe5d08b2848cb120ec537df24186cb4b9fc1d6203354e4e4034993cd26056ca242066ef9759965c40599bd0034a1d5811a658e146622be991795f7c05ae88858b218fb2e7f70387920e7669ec2d0d3e14a5082e52e526ebbbf8b37f7e45324bbb2f3e7ee1ab4758a96e347128766183494bf79af8dca7944014a9d9d3332b10002597843df86fe8e5c6f7ec0b6dbda14fef20027bbfa782ddd495b3e0c8aa46848735d0d1eefff9cf7d3d7bedd9ebca5e3a3195f1be405713bb3f56951867eab8a9cd7c33b095a139d2c46f681bf7379b2a4c9c76dc237b3dd39be1b5ef4ff8915c155c66fdc01854ce91655390dee14eb5763f8544af6bfcc813b0793c036efed7d4caccfc8dbb7b072ce1487bccb176e87927edd3d62073d06038b4e451c9aeca2d383aea58780b9e3f0164ac85960e16307d764c42c569fb721eea9ec76c1425ccb62f3fa4643a4911cd88c6962f08e6f17ad3efec79d8f78945ed0e74eee0f37292d50261756ee861e05374078b2b7c1e76ce8de785570332c3c07dbba8ad25fa0f9199b00bd7f75e4cc0ea331edf378b0bba51d3ea63b517cac1ea7b957f0f3a45f4afd339927f0537e9a87ec4fa2afffd37bdd04cd3ea5b1f85336238f4e32bae04a62fc10d5277cfd5a4058a5e7f2864d960fa2adaff986bf1b9c5ec019733169c3ed20faea60cfb9c6418b14733bf61075922103ebc83eb3d21186fba397e5bc52bacbbdfdfafa9c81245b5ec122011b7af3aa0c14ce30cbc06cf33af03e0b0148c7f97dece0eb02cb12ae13a3563c6d1c21a07f3488633e60a1ea2c40787a5ec01a0e810f54102060cbd2b2692e5a3b6b8e86a8296f15a2ac7222752acf05cff260f0a7fa79edaf0d5698a3c93660307fdebf6a2da2f94b39059bfb57e96632f53d229fa2ed3a432d6124b404c28f95e5489c17d1cb69d92162c677b03f33ca17d0273ced5b6cca3448d1fd6dd63d36eeaaa6d684ee60bfb1ae3f666a6d3aea7dea7e4bea4cecc36ccf8998324fe9142eb4b4f9207dc3c0bbba6fc2ef672d2e92bea1dc145d59a6aa882b092a1a49daf04c6dbf53b367d35b8953b4c6daef04fedd96050ed95cc136e552da1ceaa06caa06b40dd0af792290a2de49ee90da173a09714f41e4d6af7b294c63905e1734caa66a1990dcf2ba21a15986752fb038268aed1d29b124e04255ed4fee85f8c115188f7d7da4b894c4f53cae4ebbd26cbf3377dafa25d86c6e2bdd8d9adfe8065f0a9442c8145b83532224b522cf5d3c31107bc925184c68fb159daa06d531d0e83e4bb77eb23c0579cb565bf7db03d913c63cb5590bab849209e7405a716d40d8358e99e91bf845896874cbb70bff190f32bd1caaed03a230b8dcba8dcec7f39614c0ab5b11e1ff1d70e2cff8b3e2989f98d8608e3c08acb98f27fb3590b60b930b1af49a53c62b65ed13be8509e52c8fca11252518dbe8a1c4ec4a3b4e33b53c5dcd85dcf3c4d0a4adbdce7d558f755cccca9b3b74d11c8a338c39a0c16599ae443a02661fb6a128fe89aba3d109f95561796d191f1e820afff658a000b1900c914c926a5aa9326d57c8d7df94cb72ef5b1cd19a05f49da4ae898522edc6d0ba6740ab526fc1e8ee298dddd48114d918d52c1b06a24eedd8ca5174fa598d21de647dfeb3573af4e2a86c9a206d74935ea836255ef6e4cbd6647c1986d1594b0ea00b2a7f1f3b7bd9b3ab92e6e9791dfbd234c4b31a4dd51bdb74d564cea2da7739019416cf25a677ed3c005701db800a6b5d6cdff9b5ed12689674aa1c1a73194deb2d85fc05cd50fd5205bb171f0a85c0b4c43b436283061fe4d6051dd27b3ed139c6615c02ae28275e69aa0e1b5cc2da8c744f12b34f817071a537d31ae872cd3b77aafe5ccdf00b51995c80b2c28a22e393ae84bf57cae478de7fe78aafd95a022caf795ec90b33dd1ab224a717ca6d24d0cc2c9fc8aa87dfc0a8f7b5026ee3a5ed8607278e3868d9b6eda07dec3f6a8ca773daff7fabf29770d610d3d22507aeefad63323ee0078e7c170951aae3de94d5058afe7423359c89f5836ddd0683745bd8d8e5cb7b046ade014f913c4e9ebfe1c7bce4674f0fb257fe2daeab7ba6098247eb015a0b22c92384328d2a1f50778feb88f1fa3fed60f21cf586bcf25bffb870a5292a8b286f8829e46ac793c56a8a898a64ac19fbb5752d2f5df8ed330e8d54aaad46b034d335ac4d111dd32dc3bfdb1d86ff38ce69ecdf32bd7b3e5711772f03ecb783ad9c7a5c32da9f2236daa30e47d8a364f5d2274ef9c15851fe1e9c4b7690469316e4c348c45d2abf22d44f0ee37e9b3bfc694932a5def450e00796df2bd101547e22c7f8f70192b94eb1e13055222e66c72dfd65848753df32551390bcfaf39732687ad00780f3c798e02023afceba776fd6255d5e7e2050f5584476e0124f05609771d4491ce9472a9dfcbbedcd3c9a212aca66fd34726ef451621819d7cb527dabf81c52417259ec9ff478e99c98e65801a84bd79e15b6564a0798593770675a595d77bb54cf5bec1746027b2bb0dfb029eb4eea4c1c1e4b9ad9f6210e35b1df3b5c5c79d75522a2d1b226cdc4cdddd314d4a3e287851e561dd8e75ead054100cda02ab716340b40b21469bb9c09bc1f723378c2f91ceb23f89a317b7f9eeec593bf9eb461ac8c0fbf3621193b2382545234aac3e09527a4f91ba84e41ceb4950c47c84f154a97188109b47a6bef945311d9dc5ffd71c33f8f3d6276d74a633d1259f30c1213a32e552b283fdc4902f0afb62c25ed04d5d105d329ba9fa66e50aebef68bdd953983bb54aed0278ba8c81cea2c2cd0580c7a8a83b5860d2bbf96d21d74cde3f73e205a65ed888e968e4e1d52039b7389cad095eb8573e04fe572add6f525dfd5859f4afaeeb34daf9238fafa374a52ac5ffb142f503de3cbf98eb200f0c62567b2743146439c2208b8ad16521939c5345b8944c39230f45f0cea13b1b97d2c216318310561daa76839137b6b37f272ea54cd0c4c7729dc9bc9ea7d2e74fbf974f41f9402b768ba9345284534e29408248a7f499017c50ae89ff39554d56d41b140fc11dc7e505ccee89d500bd36ce090cc89ae03d8864114dd9ad295b51f55d2c38375864a7109b1294981a908b2280a2acfd15e6a12a0fc7eb4789e977dce23cc636661314eacbe1e0a26a3812836025c23adaed11693f3dc9def0adcf235baa6d39e788fb2d0b72d60a4b452c446c405ca66a08585e7f75cb62fa4a4f62648fc8d9498a7a194aceb0d3837f0150b004b5d8d8b8fc8c0da428f56c52f4bc4a6e9126113c4b4a905284d95d8d4e960947c3bd681aca8a9b590acd0c8a3119b79337b5ef1d635ab0ffac9e691ce665be3b95b2a17872401bd474f45dc04c83d4f918849565cc75174264fafa37c6877dafb263802661ac447fd44b182a615c677ef144afbacfea7d30439f3391d62052a265043503a486f0a93063375e8a937cc08b6485eefc813b177e52c63d69e1bc2b85f613b9a4cb91d121a0012793374dc4ab3e179b9cd92fe553c33df00bc98b24c7299bade13c29af8af93082823ffbe1ff06b9725defaa51bfb6c2b0e67931b492bd7b441a8cd0a42412cb561f26c9e8724452c4c92cd9037997a9cac245a45436a43c47a48ffce21ca807674157336ae38834b8488415c9b02a3db290f4e04a15e94902cc28ca8fe5915e3aaf8d893191263fd253387c53003e3322250a8e48d9122b7a4b9e1f8c485132dfdd14f2247ef22eba0ccc54f932652dbb4d3965b8910ffc2c6b616a33136ce958dbe672180d973a3105e64015e3458c3effe124442f39e8b013c1c19a3675d26e3517d4a2fda4db200a6182da5864173209e5819720b213924450713363e3820e09001d82e597d6d179b22b5006f1008e9765c4cd2b9823a4216929ca8969847e140a50e78e32e49d60eecc006292fe156a3982709413ef3c38e916f521b14ba50715e059325a71122b66fda4a6c54b44431759b34004bcd104760f3747be6aa1e776a3e126a18199c46150e00d44d0717cfeddcccb345ad9b39697577ff11227089db9f517ce9f3c99918681b370603474870dc0b18dc90b1fcd8fc970a727015acc75cbc06f70b05cdcd71762c4e54d02e540ca99fb12137c6e8155e3bd54993ea221ef16f5a3f026b1290b11de7bf7fb243c1282836e5f002671276015292522d9f94df38a87d0d23df81da13f8a05ea79c0fb5effcd8c57e9d8d8f784b8b20d63adf0292c6a8827672cc8bfe0ab50d5edeababa45dc5a34de6a0250663c0ec4bcfb4387ba6591a431ea67222c84bdab148a3b5698f1caeeaa16bb81ba0ece9e786ead99439450ef00b7e9131eeb524183c0043e30c17363fafa822ae4c5447849371083774f311825a4cacfcf3c0ade5b6fd0847a4d31356b43b201e6d5cf442394730ce8ef7babc448a89ac345ada3639030ef881e5373ba0a5c3f3a2b5e8026d93e27c28f8f6a1df81726432104c3a71a5fc9c47691e867e6b48a94047b8573742f9a1a50b589d955f88cb4435c5c7607a7acea67a225aa12b329851b4140eeabc04642a0a2357e247c14a533a602e0698600de401dc63f80a9f768b01cd592192e0e930566d4eb67d889a1241d6353d304198a3ae12f31c8098bc80f8d385000dd1eea205998a5c7f637e3cdef5aa423f3ac57e3522230281d2948af566519f6ca7a50a39959335150c9216fb4a4286cf3014440ac1281f8761e299c4eb8c63d7ea08406857ee8c394cfa894640f093811c0be7d5c7ea12b042eaff1bc9a82531253eb09d00265e21034bf1cee3c64c6b2db98a80d78dd5021e96d138bcea0bdf7d15c47069f614247ba84641808ca4a2444dd49011244630797a6459128298e82e0c064be9326915e44cae319b380216dfe6d2dacc6e9e47367b74740a3798454682518faf64050b1ab63e1491b715bbc9963b4c795116d1348d9c0008aa59a1947cdc9a072b678fade3ff663e093ac707f2120a26932f2accaa1ec2764dbb1636dc87bc59c919f01a2bae4a221ac1ef6d6a0bdd009d392f383495aaa5d48620d2cb38248ca0c2cc422df635af3908224fb9f1878b0854c34e4b2b2786d24874e3953c7d2df1cd45f8111eb746871285b432b4aef3bd3984487bc110d7c8528ea84f7b124284c202ad17ba5ca8317dbeb821c55b0f8aa0d673b0db42b022a24ba5fb346fc5de49ad66e0e43d4b4b33bffb2a50d7e4f6254dd99f221ea9548658916dc5214b203f5a51d29e59759b34e24a5557548945942ee0e90371d3d7941be8715ad7de1c85648d2f70435573e6008e3a1ee05d2711b9229150200a3fd374b9ea3e2948f13e57c24ccea1b81ec4abc650aad1c92118923edddae77a327ba687f91fe03fd072de85e6b0d1c89cd3f794dce0f60d77adee284bd5719565d69500afc75a7938455e3121e1a60f1c6bdaca315e13cebd15aa6dad8868f56bee008b4be96a4d6b72659ad3d4ede242c3b1ab16efd8dd87a9aebd682bdc2de6e1d3e1297aa6e3c9e560961400c32fc8832fa1ebdcd5a17fa9f5cbf1b81d06143052f7f580aa6ce19ce38c23e78cb2058f80c9435935e834c8fafdb0a2cc847e18c0a298e715fbe4d15d2f0e301f9dc7add2dd8b9dab2ad1f2126bedd5da72301d9c7735bd9a87315655df2e83c592ee38e9b7c4c9447df32e39e0fcd417c93773caf01b771bdcf527c6834903360e71f60a7b53c8b660b8e71f6e724837950620ba1aaa9d5e5a05c72255c0fe32ef04a37abbd5dc43cb440fff253d62cc8765c065307a72078abcad8648be2432eeea3b82ee3f5a16a9910e6a4a7596dc9915b1318b50042bd5fae4527e9fecf19e837f9a3f0053eae611be02f04b843af99572c974685168d155f15b0e3ae55eb9fe9158b95ab714e0ea915bd2f3643189b510bb455cb72c816b9104b2839e4eebfd22967d9e192e424bf57446d219fd1057e9c3e8509aa6e438ce20d1ccc2b0a7a000ab147238767ca2fa20cee3c893987228ae6a76f4a082c471cd82c05552fc5af655ee9c4ee4b418ae8de2048a8a4e963defdcb1bca78bcc84163b1d0a702dd9634adf44d564df6153aff172f5b103c8e0035145c71be007ab61858ead72be458be238220563e672eb57fe7e5865958f56b8f7360a745807e551aa96511c319a71ce2ec559afba56f6eeae03a90b2625f6912614db9a234de0b01758181ed045dd2e2edc9c615c549e1eeafe4ba1b58dc41d6d85f88ad53a8041cd920a51426d1819b3ff6f7b179325a83cb089b03f3fef58c7a5d0dfb8d905475cf7caae640c9cf811dc704d1a357c3710375705f191dd652dd84cf01d1852576af21d8766e66fbdaf548d1612d18b84b23f738bdb6d2e775194a7d1cab13f81c4ffcee7bbaaaba5297caa7738a4f47ead369666c4e1ae3a71344e73cb72bc1238d28def03f4c1787247f5a0b62cfadf49f5ebc805ad25526ec46ee39b14c20f20fd43fc35613355a95227acf6522573657101c6123893c034df5ca09e7f5706abf2a8d07c3c9a07f4d52c00815e94f0b86839a04fb973bf63e27f891dbba28fa85940a118448dfb83a2328b448deb65e80c8c23e9159f352e528457bc311a48d8679958a22ed76e57add54a13311f6e6640e3628b8e655ad5f773e7990d100e62655ced290949cd5c8b9df2ca91fc024b43989a612677197aa109e0eb2e11c86b7e10d88206742c39ce5ad5b1f4687e493eef280b2fa6c790306407608db23843d89ecb67803d5f3f2f5ec7fb0c91ba8449b790309d6ec8c73295560d44cdc3bfa365e2eaa71be768ff997c34cbb967c55e26b158d8b85f28a19c7935e40bedb2de7e075e507d0c2d6436316c5b5721adb03a1c68bf218141c94453940c3bf167e22bdb47a0671595049806072b2daffc646301202fb6141fbc694d8c665214b4b564b05de72b929265fab2f51d7f434f08209ab920dba5faf51bd2a2c2b117c28a0aa40516ad6696db65bb5d46a4c5660885ed276ee79b54a067fa8b8d5981d1547144a79dc0ec29375a17563e9a6da8f3a98ab56aaf422c6fbf4757ba2c43ae5521270fb43d1d0e4c010e7ce0de6f1c15880ec527acc8b6b502bae83ffb8a9c6ad361e36606e1eabf8ef8a42e76a304d8c0305e9c6e83e51881f1a4c0135bee5b111a6e00007bc5e561c5fe8eb79d28e1478e9d48ea68c10d569e361e1242d6ab8878169a4636ea13b9ed40819c3628174eda31468dbd471c74552cdb66a2235bf5adbf44ff3e46b1b7c5b8d35ebb3426212519d8fe3564771b972fb1aec77b07765c6bd7d8e07da24293fa456a4e91c01907b72523572eea1c6eab7cf30c6a0b4b83e88073d99306ae05b32b747345c36532c7f5baa878a65ebb20aedbb8666007144263601cefceb17252ec9af8a3f8097cddfccd374c8c1d3c30da0ceaec22e9842d3a8fa3c3da28e8cc23cad28c3523b6f224b6dd1b020af213f8b9eef149ff90faa6d8662a79991bf1ccc88df2446fb35357ec83768fdca50263382b42f46ef612e64558c32a576256b9b1b4a356288ec4bf89ecc87b8c1c3dd26790fd6e92cce696c8010f589e650b91447518a59b95324569d2d0f55d096151d091b57d8bc11d5b7507e7032d93e59b88ce5547be379dd26769977deff0895c92fca2f7b2bf8b410032482ea90aed3c7f45059e7623e02f08a3282abe6b6779fd832d7d1d88ba6ad449a54cf162a538e66f456717ffd48bd6b466e5077f0a5ec1e496a5b24bb83e2c14a9204d250b4bb4363623f992a29ba15c7fb145c25f978c300d196e228c22431df80166923b8984fbe4d8a4e97f13e05b5493e42534ed2521c659e24fdac40606d2ca8139e384e800a251f0c677337331c3e667c2594c4322be10e5a29fbd847fa06d0fbcf203dc4327fb1874809bdbe3bd556cc610c3e8a07aa0ad67e332da610bf880ffe41bca914efd31bc2d8748f8d7241037562df237983813ab1f4fabe9b7a693d3d2184d1f92d941774239addfbc949094727c5750eef086793c2ffaa49e92d79208c0ea52bc70e9d93f447e50688f180fb28fb43f17933b9e3a07b28fca1545f75f592b0fc9430887a32958c91c4b8c741aab5bbc29758b073bfa5d6bc3ca240bc58ef91bc51604eacef49de5860a26ec33d92bfb23af629edc16736a55de97403383a5016cb2cc21d1f7ad156f98a715895d0fb5e415fe70f01ceb87ea83a5cbb47b598427c119ff7256e2c705fac35009fd414e6f3d3d9cc7c2e8bd495e8458d9f5669e028e1618dcecd8ec1b8eae7d75bab0473001f5223d0287ad44866135089353ef080fb28fb43a95e71f5e218cf9530c22a1e58da2f6a4dc69420d47a5de16b0cecbcdf52695e8f48205e6c6fc538a929cee7d3d9ccfc5c16292bf14bfbdaaea7bc5246ae24478468b7e60040492ae9c321e724fca7b4ea8a5c2c8d6e02bf3beb54dc21c010194c95a1da62a0c504e297f1898ff845b53c3d9fcd4ca73369a4ee890a1c1ff1a46329c5c4d2fb6063b1d3263dc7d2a688d7fc6e1a13a2478fca6c02801817126b9469e94e49bfd196343e8889828966a0863b422de36107bfbdaa7939821b5157e9696958e0e7b7766180a650ca6f69a951c455aaebca0b97cccd1e655b05a186bfa8bd3c5ab203f8719e2aac267ef1563945610a83302b4a4b4bf2711f282813900fc61647bfad1ac043e52c17acd2f2632163a34170dabed607383ceb022b788ad022ba645535ba689ca6ea11995b446944af896093ff2f37112797a192e6613c4074dde509d1a392888e7018bf9d53086d44bfa824fae158897ee80f647766389970aeead6db64900498d4c93ed674ca8dbf429acd7d9ab2e83462ab6f76447de1a2eafbf9c73a82471723500464ca9f9c7d10288f1403cda757228660a06b41e6a160b3a94904dd3cc31e06d609b1ce417b91f9048bcfc39b6b8a9b16ab2e268459630032f38b958498fb4e348d150ae15825b02dc677a4c5710e5ef1c6c92bca899748f889de6d957734897af16ee33f986d1e8fb8907b22ea635ca7fbaae171055a8d478aa1bfc6fb73a73a41f2256021a432850758c5729c02049ad73e28ad6565c893f35583911116a8c0d4d55c84188cbc913aaa67654d88cdc2451f47e08bc130c21cc0074822d75280baf7307a4adc161838b27f9b496820edb6f661629c1c01b1aa2ecb209b56df5469a2a656d5cb3ac858f176cae6652c818e57aa3bce95d43df4c0b14d6f29b9ae69e8bb94ea48935bc6ec63995a245342ea7ed7d5d7b2e2619db03d622f638c0ba960d6e0640620477931d8997e4d848f2484771dc89478b849e7c8d7b7eb0f47f08a07a6addb5079a15a6f0ab851f73e3d66d74590a34ad4960ff1250a0c5dc653142f6edb8d8354c095c3c9caa9192baeec9582b7dcccd84dfdca42520f528f8f4c3c469f4734e6595dd95580131ed82346b94ae1b6456da9bd2f8a130051bfdb6232c847c0016f0b0edd626b5589658ace0a10b023e514479d256c7307b73ee0750f23c8f24a5d45e31dee9242b15e211c693ed3e5deffc8219ab007dc062dc9fa2fcf193b326b884991e476f25ac292d02b1c936a5d0f029e283192d2feec251cdf427eae5495c0017ba1c6b53ecd730b7a48a74e3c1a8e5629ecff18fd79f355018530a8016a01c2fe95b0c1a2378a994b948a0ad2af31a169fa8f3d07105f17e009651f2e2efc0712dc1011fab5a756439836c55b5c3e752dad6375a45b7c89e5c15c748491a117dc54f2e38e474c1bf5fab93d5baac3607bc698e9253ece78d6062580437b56fdd6fc6cbf19e75ecf6651a7011a29544aa283257743f4f3aa10c0acb7155455a370439be3f4480d0521d152636c910e2beb9512266f67611266f408755b91cb1c2292fdd30937eebb6849fd20adb5ab19a56b64d123db0c204429059a898d7e607c18302e842093163524d1b86677ba256fa89feb0a6f5f6bee15d6389611d512337debf26460b7669372e2b7eaabb1d4220eb5f3e12c402836f36ed52f51737048c59a005717ba16caba573620d0732c5c68e437972ee2ed3e2fd142b171d81a2528c06c4cfc180990a0de30e15b4a903abc2353591d85c47e9a1db7d09593865e44d9b72dde8b1f4fd5922e0847cac3dd9d70f77ea1f3e4da8e69ad7f04140b4b6c84a85ac716a7e50959e04917f5fba6590e3dad8366cb40565193a0f2a545e02c2dc27eb51abb9e6212feb9cae31389e78009988d995a7dce6a440e5f314d84c41ced9463324463ffbe4d400a93984b7582a68f115b1cafb00324bbfc1cc4ae509c90ff1e6da10c4ec72b25cc2d9531f70936fdd7f641b8a7a667ca0b0129deab856d177edd3b478e907062c71c8497ae8897266d868c07e1a51102ddc4c18cf8b0dd55b72afab5ce1b27ba3b6667faba3cd392b2e337217fabef61386d6916fe89f93f647f0e05fd676822fa2b100dd4e1d7c29a3ca3a62bd784cd7591ec03c8cd9f7c71cf9349ca97cf2ea329891374c8f591ae26692564f403a50f20363cc17cccfce9e3d89196b4cd5fdc7b149da6c3977da8ca79cf01a67f8fa2e576792f1a2a6f67ed1c0ff7c5f8fe296c9bbe58d5d410b786d070bee67bbc562a4d0f089e54fb558af24f7837674028feef51f9e56b7780a104a37909a1710dc0f958dd820fddf50bc1d1a10c60ea3cdc95102b6c6f9b03cb0f90caaeb90ed27ec5ae1fc18db5bf6544432ccf581b8df35ff7508e262148bc3904b676a580989f56327f801f2d26d8acb5420d859df4e2d751f54f38aa8afe5b2c406430026962572fc37e7ffb0b43668b78fd581c1804d3f0d21080a2ee6fff88ab2f3a789f83f17d8255aac4deb56191ba9af475ebb37e8eaac675272a4a1e831d4c3e524fd41481b43087087e887d442655e5b13aba8c31b1800ff8b0f620db41bc37fa0fcd14989d985b768f5d280efeafa0adb9d3184790f98dacfa8e137b7f21f25494c825e05794eaeec4eb2fda0a43d6f15a6a51d4dd89d65fb05586a8c36da9a2acb313adbf5a9d25be4c463fe24005c2c3ab9aa9a4505b1562940ca740f485a4e54228781f69f659a6426c2924467fcb34b37d70598893c8e08b42f83adf5bdbcd5329e7c3b0662096b789ad12d67e68a60cfd418116b8476650a1d459c4f95de13e327b37adf067ea549bdff1dc8856d73315867764b04d223dccfb3c6c2349a77aad176b287f5c7de8b4a903fa60e3eedb64eb4b8cf1e7f61d30dbd0e7e4e1ee53ac00c27109fac9afd36b2901ac61c61a9870dd20d3bdc1a5de0670d86d3a4d0994f934fecba7d9c184444242c8997ebfb5bf3728525f4797b656bdfa42c3b4f64da95b2925b78fd29564e6e6d98bc142bcda6e9acdb42d87afc22eb2e625c30aada1163487ee3b698aec2986d1cc74c7aba6e3b9f5ffc05c1520be40f8855d77e1d889828b871861e360d2d7f5b11524830b85e99502c758195f19f42d74ed5ae6a5cc2585af3031a8bf35c9e6b8d136be42c5a036ac678a41e9bf35727b0a32a4858fa549c80bbb42c3e08030e9d239a29dc8b27a6019db32c64318eb6d50d319d9667f8a5bab3e679a9b9fcbe013c3ec867ae35d8c122663e4e87927127b189bb8a94cf5150fdac54d9fbac01f305ed7cc43bfa610fc6fed78d0c67f820a2e5ec4261607933e40896a2dd32868925503aa25c45af1c6507a66396de23460e78819a5c5a68acca196853205fba5c1e8d24ce832b851606fc510526e4c6ab109c6eddc1073ddf599427cd8b5b9459c996ff806de3fc6d6172460ee01b2c91856c5e076b7b00daa45db1b902587cf9330a4d3209171ad9c7727d8ef99694af61483916438c3b5261d0059ee9e44a0239d44d7809f126a9831fa3acabcc25dbd65987d4952c6c936eb1e453ea65b17fbc6785834ca459a502672877b7f90fa17ab9e151aab4e46a73b6d43937223accf32f5b586c18d666f19838aa4da0e9bb4df7d135866668f05228efa45605e0ce44efc39862c0bf958b1633687f75089b5508941bd10310db7c43bdf657968522eb9eca6e5f6fe0ee206c36e8f1c925e322ba5cb9bac74861391ab91d5e4df0f37b6213ae19d2a14af9f46b6fd3d24cd3e14535db35799bee0297d1259a9ddfc54f5e4799d64b4bc88079f4a594aadf74ad507374b32380dff106c8adeffa3e935ffe19ba567403ea3256662be2077071a13c77dd486a58ea11af6777e567cc1df2a2e60de05012e9a19f44cd0b76d06f3cf79fecd0d6b3207e2deb6c623e2adf154371f13fdc561590b08558832c95753ad9e59b9bc2711d8cc24f85cdf467656a8e82af3fbef323c4f959c67670f867d6eddd60190a5f7e5bb12314a2497fc5acecac8b7257eb7ecbeedfb67618385cda6a09fadfa8eb33dcf88bc724aa311cd79fbacafedc90a48186262f05504db556cfa726cde5c4ec5c02b8bf3068236e3266513d15a947678292fe715d74ee56c401b166d5536f5d7693221fd77b3bc7675c4437f187481fa8f07e782169d5881fef7da70b20a7790bcd9a09427623977625638a8de65f28676aa617b3c387f7a88238e41e17f379857f2c775aaf7b78523a5f75785be04d6a60fddf36e907f7934fe5fbcf23f7226a063ed144da1d18882b041fc052ec014ce650f2c2fad57f741a8d3d0082522bc8e7b7ef0c2a4112fa69308db53fe223c4325a32199dd3f563a59014c8fa23a6622aadba61485e8a3c58dba39d319a5e00e16be5efe1b1968252ba57f27ba219496fb6cda7966da88c31e0e645079f6c6029d74c51ed9a8c4c9dd6ec8ce39b77ab45b7f4439811d57ee4504788778cf1ebc38eec1ae9cd90669d15f5ff59d2b3fa9a9c4ad70421f950d2a5388095723737733146ec0c1afdc139ab36f8f3c9d09ca55bb95146524622bc2c840ce8d10037e093cd20e65dd6041a88eed4d49c7a505ff23d1596edfb50072f120d507e33f7a3632e45f2a6bdd1dac9a8ad43cdbf0c995c665a0e148dc8506327088a6ae7943862c337893e939296bac38c43836842277b11e8c11885c41c3035d4e8628c8d1d91b651af2865b29e4aabd0366064d2a86792a16b49a284d2a26823c86c30b3613c8391ae07d15d8f56f7eafcd430dd280b1c0a53eae51102d2b216c41c6191cce1229905e38c62fff714d841675fb1c1ad185b8696b5754c486cf1ee61b2b439593145e1ece03d10b6ae6d9229f66345eb6deb88b655a60dd1da8c69d3bf5a170c8813331614832649e65dc57b28f9d4a4cea3f1a2e09e468816b6abba66b842b2f69ca73d9b7e5d9986af12a5b5562f34f15f98ef5e2d706fbfbbe947eff53f87c1aa88f550b5ba94837ac487cc3229749535facf3a6e0529a2f2f7dcabdd0f7d7d68fd86c07993ffc5638fd8bbb396c61ee6f01cf5da24dc8975b70aced81da6e02305c6201b2c570c54a1206553c7fdb2c209b56c87ded65f53929e38ad4a1b8a8a46e0c2afcf7e9932c6300f453b82f58c53d50d87d9c5c7dddcb98251282f0dc08b27880d886e6dfbd26d60295a05e623c06057247d9c7c2050eff60416db8cd599e65fd2ed1312904a2fb22d493350ac68b4bd8fb3432276b58499217be25f99c50f5fdf0014d87236caa4b2f10794960dfbe4f7875b49b96dbd47b3d8eeddd603a9012c6dc325c2b32497346383d46fbab0db3be79084664ec8c5fd9a96d90d4a3b544f8c239415e737380796e561ede23ebd0b3ecbe4f83d23025e9255ad80ed905d3bdc0748e57e6228ae822c9afa7d280b6688474c36f4d00be98f7bdb6104c8e60309d9863ebe521a6469a4f4d305d938ad71b9995d55c4344ab047ae827c156209c59a5034d38b21d9e4879183f4703cb71aaced55f0ea86852c569589a983e76de5c05ae071411fc581b8151810020ea80cb4f9c2f30ddfb9269998391fb8f9029b06c2bdc803615dfdf24217b6f42f69652ca9452ec0b890b4c0c56ba4b2155e9d6eea4905255e27594424a25f560199963585bc14fe4d4920dd90df4259b9ed1c8ccb29b274a2dc678038d3c79218aad24bd11f4a352ce39b108b3ccb8ab60543033345e171667dc55302a9819ca1c7d68bace677c8697969b17a234d660da88fe028dae45ae1f656e23628c6d448c31d296e8b2f1bc9012060c09e3459cd327f7c1c03873df75e4437f524bb0458386d0128da804a1873092883e09f483417d604b4648bb0b77c109e1c29ba33d3bd07713d2ee8213220726443bed59576c45403f1a441046d6ccdb82fa32f0718706517b3da9a51687918484ff388987cedcc21798174f2db51713fda341a34a7b1f8ca4870e6910b5277f8b824af05a120d25e834966860814e638b6eb948a23145e9a1d33882adb8037dca0823a93d2bedd928b5b75d596a0f4b7b353c741859606cd15e0d3af0a5c22c156e0123890651aa49522a2825f430fa0c09a5aca490dacbb81d164c910a82addda972e2be541413f7a5801e3a562312e8478348a9535252477f80dd894feaee6cd2da0c893f1cd65e8c5f1701fd2e25a5f632c95246b902201391d1cc7f97522492c1d20aa2d8f9aec8348ce20f527b5f049af98f0691ece20f6c01c12cd0e18a7627337d896a30f4d16526c743fff44be9bad2de16b1e0e7a1634f5ef840bfb0b4b7452a107a488388c9c36b09fb79c84b54aca007ff61400f4f3e34d822163cf90f83022fa59da1a1ba13c594ba13c55177fb3488681cd190d25ecd43a76184e33f1a531efe4783cac39d2128e052e7b07503681b827be26c85215b9aaf868f14e8f59ec1dfb5b97ca1b3842e6bdc8de7450a19267ac59ec123fa6547cf47d12746ee88be0cf1c8fc74c7bec382291a89243320f732b4361c59ca4021238484e8b33bfad95f82da23625b2f4370c5fec2c44b9417277eb02bf60f7bf94126c3c3c3870ea3b1600b4616aef0d981ce575dc80bfba4dd404f534a79f229fdc45836ae7772e8d87a2687aead27bd75396402b1f48a1d2ec12954668af684a09019a2d93c992a84578c1ad42636e1e570e92f5e1b1a434d5885b3ca30d15d2a4acc07ab4c50772c13f43ce7758492118a3e7c297d900c13b14e30d0ebe8bfe873da361e663efaf480b2cb3c69ef7b9142e6e74507db824bf045a6c9b30cd0f391ebe8937102c3b02b667f9142420ac3103d0097602bfaecaa03db8abd8a20d8dd21f3d0b3ebe833de86725e11339d36146f28c8eddaf0d27714f465e8886f8f3e3e5b7c198a3e6bd31e87e82723f4dd3242b1c64b1239f8f9f95982620bb4b3d05930921cbbbbbb7178e8a851edf08eacbbab87ec7bc48d822cc6a2b2e84b7035ae4d03e8161d09fda7adb6929399428790ad0128f40fdba21858f918638cd1972642c0e83fcc8901e8164141e83fec8b3260f2d0b16e8c2540f41d9390016b1013f90830d9756d3c2fa042b4873991ee80a8827ebb84c49f08dc2524ed61ee268facddc18f325d11e6b7480923f4125f92fa4c8eb9d72f7aa8af7f497739997fa7d3d2e599a39cb53b2eee5a723992eefaba5cc992f6ae2597a33cbbea1254458204b67827fafc40d21ecadb952c696f1fc5e5a0d7a5f9a98528f5c3e5323d608e72146744663ffa7e63573b916d347f50d2831f49cb205eacee5a47778dd35d7b0c0eb49d7ddafbf60849bb926626dfbe80f67a8984b4b7fa76960e255b7c23692aa80f1faec811e9aedb757668cf1f7b24be3dc0e2bff88be574077e7da9c316925db5b320b74bdfac47319522bb816ec718b6bbbb2d97837a051cfb2074083dc69ce99fdc5fe8132a59b8bc32c2e616835a644c613ed50ccdc7adaa66aa2f33333333b30fde493a8957b2f5807d735f1198eda09b2add6ebb8ddbc1c89099bdd99999378e44cd7d3cbedd442184ce70b77b45551f23c7b831cab5f954df3054c3e2c6085732332f33f3aef73233af942c659432420eeb0e639392c94dcee3b1e81f005efc5bc1f857df857f319e39a6509be6e2170c86bd50ce79fcf4cca5f27b731fe627df7a4cd7695a5315436d5a85e9aee7338f1b4f264d7eaa5f8ce7f1d2759a023cb0dd08b9863da821b8a7637cc283070f19f6b5f2d19726a359f7ee96caef1a0d595fc00a69cf875de78d3cba5b9cee7659edadafeb68cf7f9dc296ef6a7d5500b457dce03fdfd642b6341f1f3d7bb6b1253a6cf113e8514aa11f3fa9827efc2463eea0f6bee8fc84a170100775c7de0b51973f811cd4defe74b90411bc753831b94acfbe321f5b40a5f7e9b708ca0b8ab4c7cf4ea4bd8b93e1013ee698f773c6cb8e55f8d90dd489ee9885baeb983ef39467266287e1295188dafb78484a7bab67e7284ef46750c378bbab392e44edda5c3c278ea174c7bfdbc5f193ee9887da63e729daf39f51380aff3ce49e2841036e01f51fd8e2284b608b9fec8a9d370b940efcc7519ebd5740e76fd11550fee3279cf1c908e99c7535ac3582383e7cd8a1bdafc3e1d1d5746b77bcbd4ffafadc7e488f1c8dfbd27407a5841e69fc6968e051e65cd7bbf656b8487ebdb72a899867d8a5237adcddd91febbf23daf41a65d679e859e9753a5b2e8a0837b6b713422bd9d2ecba5475075da5a3d3d1d5a866bab53bbb1df4a306b292e3976807426f126a2f08f620946031ae847ec4627c1b5c6046ddc1294d24e586d33997faac45a267d3694d12638c5c92c935c7f8dd80e2a17ffc3dc543a129f0e889a49c601e7b8e3737aed3ed83dd7e8b5200069f953b2e2d15cf0ea53c7bc492f3393c7363a0ed37ea9cdd5fcf6ca7de75cab443270fa53cf558597c335dfec8c137d3f7757a4e9e1b4e9d3929d41d1b15d14f0ac92196526e77ed4c9d52cfba727733bbac2cbe89cd39f8a62995e1b8cf35740a27bffa25e281d243273d1f3bdf2de6e09b9e1eeb0def2a69fda217fbb9bd65e79d6750a3f58b2f87baeba49472480e5529d41decbace85da6b28dae3a12c6e3ef4fc119aef7c86db0845e9ce891aa7dcc7e3a9778c5d51afd8eaa903e0556b438554aaaef3d56ad5e1c0d175ae0357d506ef680d35b84d0d3fa0d46da8a1a686531cd08d4bb1381b2ee6b748892b32a8edd603e535ded51aac86c92aa09f4968b5720601fdb0efdce4385a80600d1cc7c92ac3721bbc06cf3e25f2e51443b589e86712dac170541c6e53bf9eb771de786c3c83da456fa0c11163f49ef7286fc8daf3ac44e2701cae036d2a0dc7516df8ac3c365a8066784dfd60be73564dc2cf6e4395e921f31a3cf31abc7f403b8fdd75ae03bb7aa2513fd3c97be3b9f1cd092841362a8e1a87ce36be9cdd405b805a9cb43c69016a0182595a9cb40f36aec15183f7a83ec3331cb55b7af474f06fa7e4e0df1e6d1178ac94a522c73f1d3e871c1cfc6b3d7f1e73f16beac75fe3d706247bc6b61e315e83c359dd0e9db487c3db4f1b4fb38b1ae32f6a7418150757d51c8f5179b80f5507ffa1e610abc4a468c5f4c48e006ccb9d43fdc175a83e388f1ac373aaca71a8303cd6171e535db8e71d9fac7c3720379cbdc7d7b17fc67bd5ddcdc999996e8e4a34492c9ef176ee87e9332bd3c6c3dc8dbaba2263c9a315cda3521cc3da5c59e21cf913c7d0577ee969f02cbbe12cce991c079ffcf7c3a5b3eb740eceb9e19225036486fbba7f42272f5b80288bc5dd902c7696ef36d312a5bd1d1c7a8b13edf138f41616aca7c3a1b728c15e8b436f21c17a322d23f0e150864b1597afffc5775f7c77c7d70f63856fa0c3fce06ef80ddf6d86eb41fa9461f9caa34b26515e47003fda99c539ec928b2cbf1e7ce51f74f2f1c7b8e48c489fc9c137d1a59301ece0485fdd7002f4c029e9bfe13fe40d1c3c6e40b46f5f02709f8e5f79eb07f7c55f49c7c1a3c7e82e369ec8e59055eecf6755fe39a7ac73b27e643fc3e5e09b15c731896fa212df40e7b0b036d07df8b0f8e686eb409dee51a3f3503de7a939f80e5507d75179784bcd71998a83c7c4d4d5e958857322161a5c06f7754ce29c98856fa05be19cb88566050bbb4372606d943847fa60e11cc9e49d062c0f3d6ec139b209dfc42c9c237f04b0c543971cd81d72046be3c33912886fa0eb386779d83b02b0b556ab981e80efd8f100e0f82683b581de5af95c750cd7c3e472f04dfbca75ba39191d7ec3a34b279c13a70c40063b389209b36cc2392d4023802e8178873f9300b8cecf6f2e80260f3d07e74cbf21bf71fcd3f1b175a8aa1c78d496edd873ea693b761c2adc8edd088bc36188735cece0be4605e0da1bf667dfb8af9fa7e3702daf6e77319e83737a3a0ed137ec4597acc9ebba1c73e9d9bcae1963ac49909c1c6e3f9c4e3fa29f78a04b4511462f81440f423e12c915d98d9d74d7279f279f6e04154618bdd4f6070d5fa4bbc63c3332fde45aec0ecddb33edda7e4c3ff9c749529f99b61fa69f4cd9a995ae98aa745277b005f4eb25252ced2d71cefe8fe8adc5ee4892fac632dd08731ff79269fb21fa0c720203fccc3113cd7123eaebf9205aa4bde9ddb94749a4bb1957c908fa6491452e66a13d04f7707797d06bd88cd7fcf63a0c4cec5667e9553b46a90a96e0db3f1a1ebacde620d99beec2b77f370f7da5f64bdf5b742b739a99afb39c3a4bc6359593c9cab7cf5e7d7de5dbafc6f2ad115be92cf247fa481fc98408f4bb808ca4cfb75f417d057dfb0524bdbfdd076c5ddca79ad80e8ccfebf425f45b6388d8337b3e21db9ae9597b3f81f69123ddcde93b5dab524ad98325731cc50a2a69ba6b24ca0e91b043ae484572a4bb8944baac48ba9b7ed5223dbfd949eb5396695a912348dadbdf3826f140d27154634433a77f304bf41d6c1581abe918f5eec3f9afe328ec4477d38b401c9c13f7b1585de74e957e7a3b817eaa9faa19a7694fe38d775b2847618f32af29af753789a6489aa72bb0a58f7261643d36c68e1d320d5e3d5b1b280d315e9963780dd1716c57fd53d17f3982abe82fbe5b0f780479896fa2c378e53e1575eea3321c106c492198e8bcc439311e39ee53552934c3fd9ae13cc3e70c9799318397766f66f80514e36ba159cf9dbdabe16d5d407015d95f361eaf130c5486cf8091214386c3c858da1dfbd13988caf01929193ec3bf0b88860d97aeaa9fcad9b39818671b598b0d1b36fcc60b1b364e366eb8c4c4d018ee9be1b286b7c8909142c9f430c36b788d199894f6388f8e19b5573d3a36c5058d19329ca8bbe8372a94d25d741b151a75176d547e1975468d2eb556e760d4ea43cf57df8d87631a2c1d03f1d993a49e3954ca751ac63de682d21e0d8f7e05b537c3a35f4cb427c3a3672e367cb71e366a7856c3db5134d06c95abc1c9f460c3ab571bdc8b86d56032270dafe1996ae3a95143db7ce8f919329c999341532b0dde5cf524a9afd5dd511b10e834d048df3620315e390cf073dcc72ee33a9079647c8d1e323823d2536e44c695f0c7b84ed7a8fb198cb6ec5c8543154ed15d7c51194be52cbc45fbd0a8cda47f64d46ec24154c663b4547997e830eac70fc337293cf50f1e5d406b133da65e49ac4d7478062ac341a4eea2c7a0d25df4cc45a31b5f401093e13eac0352c5e0e0d1c216adbb9367fec1a30b05ec4fe206043a035d4eae27bb637b80aec200bfca75a0aaeec3f817f57317f5d360eaa77aa9415c2ef5eb961a447c5a83e8ff2ea08f9eaa4ad8e4d351dc876ddc47b56e6beccec4612ad5ca89cd1822f6348451946d573682424cc5391a0de847658d7169a00978e959724d779c9a471e83d13ee558dd8d0709ca3f1c338982b20f93dda6779dd8c4362ddb6a938871dc598c312e9c291e4aba63ccbb6258479f28d7e986289467b389dca6a32684ce4cdad3aec8db278e7da8877a08ae4e9ef22c5591e88066aeb991ccfbe4df2e29e98efd34f4f1d033a4e2e2fce2ec9cc45658e90a43c13886d25d507b5af7c6b3411490cc99090eea8fa178de6feca5a2dc65bbbce572acfbfaf98bfb8e3876b53631c9db8f8be39fee7c9874473995cabdeb582c25ed510006497b29675fb2dde223b8624ff96e3ca9ab09ea1795f9d18e39061d7a73323ffa53fe0df1d1f77b5e6e3c7c9403faf15ef16126edb17393f632d79c81d8c97f0ce5850c692f955d3df0c6836558e657c56493c77cc35a0261327212eb06fa26fa4ceac0d921e09b20ed49ed080e8ff6bab53bd2e4ec4030d273956737355003bd5f550762df91c58a008d977bbbaa3b9ec1407ccc9de3ff7a624d7c55cf609d65ab61dd7d31623e1b68ce0fbb26e69777c5fcaadd846f1ac3b80e01df3417659889177d98b24328e887edced26f8d14a4e1888c999959b294bd35cca6642921171b5298a18f1b7bec58ddf0070c9dd923735d044c09bbdaff52bf4515bbdabfb80f9b3ba0307ebf25f0103e8c6ec201748bca80e52ff79726c9dcddddddeef3da1dba93be03136874e84184b4b7444cc0f2d287c0160d647bcb377753a1df26119135e849a1d432530fffcb296ced8ec67d58c67d34760b73291bf3df5c7e54db4c5262f425852be9df4ba01f355d30dd4917a2043a840bd249a02d2f236450db76c822d85dbab20914b2942c36126823d11ed1142f3df6a7cb65c9cc2b99a3cca09432c628f9591c5c01ffd1dc68ba2e533c395cc5ed4efa894342807d9d3e6d1e6374194d3146e7c83531b923a617715d73fa5504e645cccb8b98ae791198c98bd038155c71a73561d764f5c02294171532ffe830b286c8308ceeaeef73cd47e949380cc478ccbf75c96b9a2e7d9a4c26d334714930bf38ef4e5241a34fff58917be6964b72f9c726973c97b34b13c75c128c6b02fae5aef56abd3b18eca3b16244318b6c69be6d231a44c802daeb791953be017808808734948685383cf4e53efaf178184f29475df3d3e74928a5d42dd0f394b3c0c97df3ad5ffcd63588f2263047ed7216d8b6fb342ec9724d48ee08e945cc29a5cfcb8b90d38b909e79111747e12a3ae645649ccac461dc856193935ce4a2cbd8d3720a45cf16bb8840bf751f3abe437bd021c6c5e8ed52b029cffed1168f5db1efcc64e59962a0d059ce103af43d8223c83a08343acde0357f5e57162f87575f575f17b770f29452cebf2a4b72ee71071f7419f7e27677777777b70ad063dc6cc6685a6d0ec61f8edf9a4dc6efe59de698ef7a29ff60fcbe7841c37a598e36633d94afcb6c0ee528c45f9c11cd2f00fc0ddf4e311adbb5a36c7c7b66a343abe16bbcd81d33d666866fda615e6a0fa9e6b0f6685cfb073dd3b6fa1adc78807ed2c0ab98db0e73b8fde08c44139c1242e9d8c623e5e4201769523c99c8b6864eb971e1eece5d4e721ff6a97e9d8b90653c99cae3f04b0513501e734d8b928b3e3720d3650ece397974250d399a18e3c9cc28094d3386644efe70392a6a2ccec11cf2f8eb939f8ecb772f2edb80643fb91c5b0f93cfe99b033ab92acfe4a8cfa69d3ccb5cd6e8316aca5705c35f60cbe29c0bca199f315c623e51a8188c9a1b4a439d50280ee5a84d3bcd702c65b81e58621e89ccb88b2f7c71f1c30f7609647250ca78845b8f8972d3068488cfe03e958c249fe13eec73c8f04f0737e35b0f7623d365fcda8068cf73d32627833b796a26b20c8410c3aeeb42cdadc77493bbca690b8c5f95c537d26365d7813115c6a829095baee23eacc561701ffd7ec17d2a1d0e665d38caa17cfaeea665dc75692d73a397a3fc53d2580f284e93d15cb571449cc44e4e2186f22ff6c0dcc889db9c3720da63ce3ef044b9140adbe0665ae5bf2094ff50ab2b77db330e3a63ec13fb823e3113d783a469190c030bd9d6409887f174e2647e9c5c7a8f13d7f358fde2c78f39d4e99ec794f443d7813c70bd8d7ac6d9bccd9a39a093269ad79cf28a17fbe408f91be2796de2152f6e7d72230142c82c70e219ebd6c2c87d47601ec3b893cbbaadc9d6b8efc84b8f1b0f98679ef50624fa6e3f26ccb87559bfeed74f0b5b266f8871d742c9450a53c05d2f6cb7f62ed6bbcb7337c9fae6d667b60b355eeec8cb49f6dd80ec10dc7335dc010c0c0c0da6b560bad5d8c57d1885d978aeab5eaea3bd7d4c076d6fb7130f1dddf1c293b377acf6a64b29fdbaaeabcacb3923996b2ee3835f3abfe46408d0f397f7fc6544e33aef8e9b50124d7e399fb0bf2a0f7d3d17ac1feb4fdc77447b2c064f4e7e55291df34b5e13ab0c05df5c7e9297639cd6dd0c37e3974be9d952e89315d7b5d6c1c4adc7ee2e8e39aeeb8e610deb825817b2a5f96eec5a51b272e58238485159aef9494d491d3d4119832d17eebb0355402eaa4c7417be3b596402fd6850a134886010292ab122e07217dc06da2fe7faa5525184904140bbca55dc065c78fbeeb818e2824b4581591a66f1694fc50eb3b4f7e2e2d03da5e4225348a92add25fdb4c74e83284583a85b34887a053d955aa25ce94f10f5490505c1968c101742b8e05241b0674709b63675e5a5a6020fd89c206b855f6fc07a2f7c9d019b23846fda15b01e0c5f4fc02259f1bb4b5800dff432606d9aae107e9d67737474d89c1df8a6bdb5393ef8a63d0005c8e103ebd77fbd165fdfb19e8bef0d5dcd77e363bcf0a0ed9f4b0eabae97e5e8584ff375189b83135381e8551389319cf322ed55eee32ff2cdbc74986d2181ab760cd0c7fc832fa5f3e6602e3dae57c3a563bbad1897b516e9ae590a5afddba41ed5751a09cdc7cf79967535fe35a34b1bd323176dc4e9fb36bc65da98a9e9d7f663c2559d6ec3864b1b97bc84e8ae1ff14d1b1b55c375da460d3eebcaaf1aff46ddaf816d978cf2aa107b6931c1d0cd643299dc466de2076727c661b61f2618979376dab0d4e9743ac54bfa69fb113dbad878e67cd97ce8f911b919a952fd88df440cc74442d81d97070102a15fcaeeb8bc9d89ae996d3c9189606b77ba32a141ecbc460d22099fe16db812ec6f00c115d0b767b5091abca3c1676de247b513e3372a06b0b7c141b004dbe2172c0bc1176c8b73b042b0c54a3007db622dac126c75935e69e1eb26366aaf60bb761a6a37d1a4bda1b487797b07752bc69b86cfd0f00ffe8c6f7c19dd6486ef077f86c754c62a4b29837cf776cee22e73a1369e8962d911a216354d281a9f5f83a606dc782eaeee57a28fa3f0ed33ead751be5d46fd66ead723f87699181810ed518086489118b737e9a7183f6d313128401383a361300451c178e142c810062c800ad249e7c5a585a676f051001fa84831aa355d77ed2c2b1e0adfce42d0f808cc846f3f55d1f1b11270b8083aec0e0e82f3e01c96c237ed548cf0ed385246871532510bfdd650c91a51645f862b8dc3fed27c100cf46ba1f62e8750c02129edad771bf594264ae29ff63e5f6aef639fa7f2edcca4bd24b682859b6ceb47d8e84d5b87187ae8f563a3b7d2a37fd786c9337739ac47d8e8b375acbbde24e7249f9662f4ed0bc8965983d1613d42c3351196af8fbe89daa85b4a7ba155332df7d975ec4ab6ed7537f769df9ec420f6f07a23307ed2fb88b6717723dce852eec29d2bc3037f8d0eb8ddf6ee569aeee4f6765cef0e6641d7b9af206401c56f51164cbc8edf222b4b9fb5c6557e7cf8fdd99ca84897662b903131c277a24bf7d138b4c71aa471686f2d286cfd148ee06a9168924ac997ba296461f9c0d527fdc0d5fe51d07fd823b5fbf8f9761fda8bdf4740df8df33b4350706b1042395149f80cdf4b547e96f5949d288edadb22259fdfdf191a6a4f06fad050378c22cb6ea01896a5da05740ab39278c4632b8fac212f8c6bc3ad7d3b54a269b4c8b34a93d7941162cdd86226d4d7f3dca85d2bbf2e95325316f4970a25984085124880bf5428310536475411596b81b2af769918a90a159389c2682c1d35271310bb36d2a4e34ab635598c7ecd0823fc758cbb62fcfadcd5baf4862da5e4608c305e41f47337cf397dc619218c10eebc164e6e5d5e975c08a7dc7558674378c9d074d7edb09579c38c73d8a62dd0e841ba5bd6472fc2e6ce8912c208619c52ce29a544a2b0e717a317e1d53997c7eb82179c10c229328b1e84107e47b48f1ee79cf39a978c1e43e90e23cb295931ca18655ca29925969288c0162b8816625c9185c3d281d3852974ba2025898d4811204a004407812d1347c4081a44ba63161124304135a18e0fa21d7c3ce1ca13b0c80ec820916893a4b47490214742862c216df1a8892061063126ebf820dac187164270b5314e2c8810218104aa8819bdcfd15581e88e8b10012288109d2844458020d21de4928aaec0c27f7bb48435f5c117ae2d90a6c0d2329f405484267d88929011830c39123244670b9f1ac44022862491d10c32841b421406a35e6d0c025b3a473050fa683d9323b8da9712b682e460e9940109c220425433344d187d4bb49581c57a12e30e57f4c08b0c6694d825d432a18ecbe4814d0a2c80c212260bb6744cd84c91ea6ac59d8610c2092f245eb24841162db8585de39b817c7c7c8eb0e20583cec26129584ba48535fa198950419325a4d0c2480b2b5a4d9881167ea0129eaf204443575a1ce3f649af36088b42855443bdd2b90251aff6bfae0ae9aea5044a4b5720e267dc0166cefc0b210b5cedac4384b08830ccdb1bbf4447b8f2f1b7e80a2efcb744291ebca884bae0630513c430052a3bde7f34044a9012855191157a708422e090038b455aa445aab9a205a95fc77a194acb7604ac03318ca0d4041a2184f00a1dbf91c6142a38c1083bb08172608918d4221cc18a294df0a04b64c145cc8e2270600a76042ac609385418258231c1910992d0811644b002021d3b6ab052a5000c1b634430a25311b5ec8bd1176c86d0822190214b01086218e530da418e4a093250205d188a0116249f273ce10621dda251085128c1a19c00c5432e18019524e0c00b52c01c104208e10d14fa90cc317a8adc977143ba630fd21d6326eea318b713040e09c24f0e41008215425081ca937c7c7c8c803285960583101328f0200c467881040000820fbe3d621aedf69976a7e970b0babb7bc9c7c7c703b1bb7708b24ed041135ca044132e5cf140b3babbbb83c4f676e0313cb4e2a1634b7897874e61aac8e221141e1249217ae834ed75354020c1e3c0c1a0d8203379910a1b1401e3f14a180106a7bde8588f07b2e042134135c8220c2eb889310c384ba400b52e8aaad081b17c7c7c3a0065c905bff40ce8e7cffe03304821dac11381a8888900688df41903ac17633b76b846413e9887bf4390ab7aaf98e17ff4b9efc85241e3e89020422011df1eb12abef341169e8a1fd0e07b088fc19e0871ca107977bbaf296377ec8edd32767b9b9b646c4cc69e8f7d4df6281365227314a9a5e929d0e93ea0d191b5f2db9d7c018d1eb3ce414c7cf46c6e3ccbe5a0d8624cd02efac7fa701e932ea0df1af9681f82606ec25ca731e938bf2c8cf38ddedde2f0610f570a74592806215b9a4f0225e9d11ff4c9b02a454095fcb779f469ef72e991497b2d2e3dfeb43856fb33de30d7e91ab78a711af491d8e6b465337193d3811bc699362028c75adc949ad395f4d69ff2594da914c5360cc3b0944f0efb817acae4d9a6b54c6fc13e2c3efdb09f1cb699b61e97cfcdb50dc35c6bd2dec6834d6ce3d92276aa2617cf1ce5d8e6da0fe7d08dd380a663aed3da073807e5f3744a6d546bf213a5e44ab94e9feab6f7c2511ebbe5c251a8cd3f94a7b81e18757293b7b8b86c8e71df8fcd31c79c3720db74ed876bf14f6bf22d7e99b61ed331ce08e629c7361717d3a6daaeadc706c3e7a632b9b8565fea346d304c70e369e172506d8acfb02a4f959f52ff53cac4611ffbbc8bd3aafdbca89abba82f0e535bfcba745ab6cf4b4739b6f974f1e913634a29b7396a73978ceb0185b90bf7f16f322e7ef281a7eb29caaf16ed29ca53fec56ff1de7a503782e2a8cf7f4aaec7fce2b4971a7b854a51eeb340cf639eb5b4b4eccf77a9fc1ba5f2a963d4457ea6699a928b3ae69f92eb5b1cc34e59fe833e3e2f27c61dd1de08758d7acabf23dab76c1c10cda9eb744bfdf86993ff4e593582f293cf16dfeaa7e43747d5afc7e7514eb1fa69ef63dfc55b5a6a8f961f2d2e2d3e7d7a0b877587d1cd759aa638545df25b3da2bdc6691fc856fb4056f7596bf2270e9bbcd207255f34493a4a3a4200dd222c4c7eb7f82df2c2cf67a9ad3dbfb025c4f4e5240e92a3ed2dc5b63b3ea27a1e897dc4648f1c41169ffe591b761c448e916a5bc718638cb1bb61470ca354a5d2ba8b44147a8f1fd8c1ec0661777707f923ac6f181ff00455aa0bc68fdd41e660e2e6bb71310bcd228f8f351f3f9a192dc6184d31c6e833c62c0ad3b3d581622ee3f683865775377db539e79c2847b567f2e91426bb816addcd6972ac9e5c4a082184ac87ce30dd4d0ab02618e87784f5987f41fe927e5d521a61c7341853d56885e90ec21a533e5317b73f24e6d96e3f9c20c3cf8877275d09bf115577d267ba8bcbb0a344c17427dde498ebc06bfaa92a4971310b16bd5b3ee06a7e2b85878e9f3534188afb607ebae326dd4df7318d3e1c1c56d7b9ab54d8bc3823763f32b5f644c6447326999f7c693ed6eaee6475774eb57d765841417e9d63d48eb03e9a6aa8163e4b4bbf455ca8f2df2249ed547747abbc9355a1871ca5bb1883dae30df408ebd763ac4d9c3ceb248ac5c40cdb7862c4e21468dc456a290ce1a4f6302bacc46a6f17b684e09d85ab6b6ede5d6ca627c7ba8b11a6bb250257d0672db280ee361efa2337c40aca50baa582ab2747d03f9ea2928233050b0a66e2a1671c050a5b43bc6399540f9da10f81ad85dc878383711f8b75715fd77915da1eb5ee966598fbe83031c675087d79e96a9a946537d0ee76b3200f594758cfde6c8422945e7a11487ff2e83e31ee8e934b8fecb14bc7580bbebb63d7e6dbfe63a417fd4cc47a26f0d91d50890d9600ca6fdb8f25daa22ee27fa1f29e8273ba4ad6538573506580abf22558025b2aef7e92fdb75976a569b0a76824bebd4530451b21e50c4659a66871c43958f8f61fa22642ca188cbc30e5af1c4dc109d011df0ed31cf8f617327c3b56db5d5aaac03954f827ec0e964223710e57e19b2c9aca0f013c64a4f6bef81ff48f91802cd1438e91ba83eed24297e819a9c5751a426fa1d9a96e8ef5e69b4787cb3793b9203339392d5cc90216aea064852a6461850a585cf153904214a69cb0caee90de534e08e5e6bd01d97cf3f5b8fdd836dfd7fcb4691bb6b9dc7e6c9c2637fe2f3c6fc10adf3ec920062c5dd0c2b767e1db4fb00adf6e25080ca83e725ca5bb7625d8430e89cb7ce399f084049be54ad4e5ffd75c1f7afe02aa57a2fae93d21350dd13d2e60cd15d9d6b052129ca8aec02850a87168cff60c55c8c2ca3a14ea2e8a5b591ba1f696fbf8a1509b817e900a58ac4d3b5f21b4d00921869c0c0ffccb71d2f37719e807a3c028df5fa09fea7bc9da3494ef28ab8235899b52987875205b9a2a80200944d0c2ceb600e09f3f2bc70d8d0f0307aed8af0a6d5c5ca74df5eb79939665d0e79c734e2927849a63365cb39149e91042083597366c481b6a70b84e673515870e1e46bf990d2a48bb1d779ab39ebca69e2e79418735f5633df40cbaac9867529315fbdd605c7a19f4279f9784326e816f2edf7771f1abc625b8f1d86058a7cb2a671dd29de61906793b7d004208f44cf49963957ff806fa694877fc01be818ef5bc062b03b16eeabe1337dcc77fe3ec524a29e5119cbf719667acca51a640f9e7c609c79ea9ac0d3b637153f965ed4142690e8e02382f5937dd2c168be5bdf1b024ce0e0d647e489f3e8bfc6e3c60cfce58501d3870380ecf669558c472c266957eaaf10adf4cc7b02df00d0e3f71587738707032d37908c4b2aa551b75a6faa99ae212d7b805be7171592f7fb9352eb563355ba13e7647dc7c87218f39ac1fc5b213125cc59333ccc30b7aacdbc57d9a9278a4fbd40e3caad0940711ffc349f1684f6220fe8ccf30e5bcf548a55cab6c85ee0e24606bb178e24a0f8060042da8d060877d093bfb28218267fe699f3e74774178b94e5f2c1fbabbfc729dee2e974716e9729cee2e4ea59ac0263de48ca8bcc533556d7118357eb65b8f165739ac2a7ff1c2857f5bc5bff0df9c207bc3fee285d7f40b7fe13cdac3fc85ebec0e1afec267daa3c15fb8b757e32f9ca63d1bfc8577edddf88b0fe65fb8d62d00f80b7fe1b1bd197fe12f1c0900b8208f5519ce325cc6318715ae8dbbb384dac3fc844924b015717026c7819ab376b98b8b67dcc2d5053bccfd546d5c5697310eab3bc651846a4063fce32831ec04cd43974236f508eb63228490668706319ed950f96fb819ce36d4cd68a8fc3595ad501aafe19cd9a8373cc88bf34b0dbe42face717844ece49341ee5b241e2f6d749285f39295b90d36cef4c62fb7c17d588de3e03e9c2ce8b7416cf887791ff6330a5ce5a8fb59bcf11c959fb5473795add00f07002c67c769ef268703a02e5cb132d769cc86ba6bc34fa2401b38326ee17784c79f3c930eeb6a898f0da8f415a23dc85180c79f3c73e92bcfe229d650b7bbd5767b8a92831c4e7790861a5ca5d57da843d606fa8c2aa342192a3138558551f75f54eab0b6bba8479f5b79f6971a44bceaa772a9df6ef1ec1a0f7daa96fac106fa549d40f728cec8e6d06166b809455881154da860a28a13769808cfdd6fbedb0e210159189145e8055798c2187636c736252a0425458108289880c9ce66f4db1546bfa17edbba09d013ef20d87a29c20559c080084b2cf164a7bda3b4b742c0d1e40826b2e00112507061a7bd87dadb20c0560f122520a20b2be882154e6065a7bd8dda5b201c51416aef2aed9dbebd93ac746b77bcabb3ec0ef8ddde8d04ae386897da79687708f11659b0b4b77d3b2f29213941a90fed4376c70299f2032954014516a519ecb4141a466520a2045f78820992a8e21b094f041511851e7cc109293bedfcc340ed75ea79202188095c98c1123d88c24ed1bfa0c11542f085282c2185043b3d424a4f8a8092823338418a1d015c01657e3b8e01d693507e3a8864cd4b9a3ff58afdc35e524fbaf41da4079159102b8f6141dabb1a0b9a292dc1d656a1083f36288114aa90e2043bd0394b7b3a3cf4f6d9c2436f26ed9d1e7aff7413e8eda4bd27ddda231f1f4682dc69a887090b9288020faa982245163bd0d9a8bd1c84debb1e3a4731711fc642dee5d08710815ea43dd3434782f4d095b4877ae84b5a08e8cce447034250f0820b9e30918520ecc0a02a3c7466a23dec7dc0168e773d98410665d042951c3401cb4ece43d73142f6d055b0a5000e80810959b8e20a3bb8c2ce0eed2110ae78291dd328085e1e210856bcf41959248417bc749af666badfcda9e1352e07ede721d842b2434020facc637bf5b3acb5609964a474b484f4ccacf41c04b6d80c7f3a9dfcc44327ac484a0c1e63c263d8aec763c841e3e3c0160f0de19ce1af1afeba98a8bd26f2f1f94f569e423ac6f114444b3e3eff498e89b8a145b2545445117e4e5fa524a42323a2a167178ad241fda481faa77d9e3dcb92529223f1c7d200462d244ba44b57d21d33115c2d41d21d2f92eda09742e85ed2ba676608ee8126215dc036094cbe3d52181a4d768c2a557739987fd7453195aa3be6a80a862955c166ac178766a5d47437e74af9b9009a0cce7c4d77d3997637b34c42c87dec7c84c7438f55353908c3338d3323ab94996418618cdf111adf78920840127976583f7f0855bfdd7155221f762f1b425e8992f2080eb3d6de10420877f019233347e60821d4b0f63a46d831c61865c718e31563bc628cf18a31c618638c13cb628ccccc713233333373ebf85d9b0c42f60f4218047f33841042082184ac1be8c02c7a117633d7ef08646666ae818c03da1ebd08679c587bd3b1083f9c871cbb0eccb2e871c723381b8ff4f6628c9143f1d6ae7d4a1e72dd0e7b0cf4a3365478764fef7a84e6255322e8fe3eb4029511a2f9e61b2784e63d3b9b84b9acbf454bc420d502de2c29208272c406862bcebe26d19684bfbdb1cb532d804850fecd8288382e111de18a0c82c212ebc4542f75d2134f5c444f18dd608b474f386118d4040be051134cd88b2c1f78a7679f2ad01b28e6d90d5add3cd639bda5b65ba65a3a8289c9649a31a949d1114d6c684b473c0123e9670bd36493c78728ffdaa7cf125315a24d9c10ecd35e735f73ecb34b7c9aeb2ce378e8cfdce4eca43d262d2484d1bfc0d6ee303925114581025d228a429499e6ce10145f90bf9220f9cbd4a6ba9faa1bb7dbae990810458864396af10e27f949884f22c49b4c3a5897c7b879139afb5f1a2744af7aab9aeb40ad9eea92eed837b6ec481125768c1846378beafa2dfac9827f6c6cd378688c8356d06fb3b0a6eab728055462bb4ce478e88fd397b4079f5d88f6b6e809280c45b75e82b60807505e303f79ee2998080747ffb113cf71369122489c73da03f0df6239da9be917b7417c0457e07cf620220efbd79cef0eec798797b52bf67ef66dde690bf04fcfa6fccbb72809e837ce6cd67e664e42420408de81a523eb20ed7d46dab9203eab9076f8a3856c3da0ebd8b759368bb396b05c7e8b94e0c17f2c557b3df34b2effe06f0ea6d1765577ed1f8559025b44dae457cdb2b53155acbe8447681e2683353be896fccab7d3d4f4d1d3b05277cdcccccccccc6ca5bbfe38c9a79dbdd5fee3a4779d5e9a9593bab3c24a7ce5dbfb522204509ee6b708892c3fe33334ddb1131a7ed2ddddddddddaed3d0fb63a0232494befb2d4282e8b1a058d027947cf0bbf140ee63711f4ba77f7ee6b7080458f8205537e1a22642bdf4404f988967ef25d8a270658506a1847e43c10a5ad424e967fc163521fa6c21156893a567ef2adc4a7d656a340f9110fa1bbf4548fc7cc61b0f4f6f3d9adbb591d1bf468258c02ba8f47ec2043778ee2a9f7b3427bf79e8d123c66ff8104218639c729b62a0edd8d6a39b5b0f467dd0d79b8333959d6c3cd397da9b1c3bec004da23a0d3940d72111949ddfbf911aa997189584ff1aa997c3b428bdbf40bb9b070e42081ba90cf46b24a3effee8b0cae85b5547d86869c9ac3b051aa377c3daa3934cdf1e0df7e1113642d2dd6e57e94e055b9cc4aed851e02c78e618f0ee7007d14e6aa4eea013e8d7f3dc59e8421f1741c8b214d323c4a52b3ac71863e4d8d9888de0111f5b6996c25660942d601418858dd8888d36c258942d4d9499498b1088ee3a4697524a29a59452ca2334f506baad1877e1df2ac178a9a0a8d42c76a5494c992212001000005314002028140c0805a3b16040ac6aea1e14000d92ac5672521b88398e52c820630c01000000000010001098218100208371ff2fc93ac7f3f252315e71ac01cca79290e62a604498accf2dfbc80d742185055b94cfacc7ce1912aeb01ce0638b540ddc368d8739004ffe9ff0d7a44965093e8a9654ead7512d79cae817950721717a39d74e16cbbbc08e3b2583ebad0ef1daf19f853be30a24759d125b692ced0d0dffbf1f2ce565e3829caee38324e3e6c6c8cdfb26e968bcb41df399a82a37876a5252c0e005e9b4b3fae368c80620886bb8eeb100266bfab90fdb519e662c2a2856466f6e30a7b6886cdc4840ebf0abeb307fe2110401cb57b72fff849f137bdef823518e582543f7833bee29eceed08c1db37073fb995e4aa578b7a763cffc045bc1295a97c1144108a42366f560344d8d9c4405c1fa12a52082e437240059a5800bd6da1d3e50ce9c9b243ae3ffdd1ddd75b740d9c9b35e8b75a26a4f4fda238c0ba3705e93b47d59f85d7f607e1fdcfc07f683ffc07ef41f9a0ff6b1ffe03e721fbafeb83bf99098d3860ac8c60e9d5c3af186d823a507e6393ca8b85ba0eee6edcda894ffc062286a64afc4ff869ccf83d91a082a768c3500950799e6efd7e060320d6bce1a646f579b3de1d62dbb84f3da1235087f993b8150269983ac9c6fd9461158b2dbc4aeec490ca4ac248fa184be9864155c38f3c494253e45dcde8d033a5feae7e9ef0982d1b2e7f404fba55c5fb6a36d473d171e22761a069928470998074df3c4f4feccdc6c95ce951a9471afee219b32b92ababb9b93792f113ec8b6922e41a0cbe8e3dddcb6889344b6a648f3d308f5ac8e193f45f0730007dbf4225200c7c75cf801b013e5a43fa706dc1cfa5be9523b61fbaf2b9c1810e16f6d6b7b554388b0170611086cea34944d646fde50c6a4efbbb4b660930151ce61613a6530ce52079a2c0df913c654f0e663c9d91bb6dac56e417f4b821e4ec8055c16a379b889b77467137b60accc46ec5faed1fdf6141a38d601754faec01daf80a7bcf3df2718ceab20475217d7b60267dfadc4fa438749750ca672fdd586d3ca510a60d905cb3c5388cec77a5b388ace77c9daf892f15f6d76d62007029ee2112b246ea82bec213c4a007a9089bfaacc16077bdaee682823fda97477135575841051a435618dc67b224a37f70546ae25b9c21b21df10f796568892f050eac1a906fa6b61209cb9e9b9d29843ec69d1faa63f65d242f19a16befe62974c07bd22dc60f6fde6d001cf2fd4d245304d0fb53eb243fb4f18cedb62a2c3c2511186cc96f2027806651bfc0041470ea95735870eaf15e1f0c621860ea536263b6ba7485070051b6f065ecbeb2c17d472220428c79cd5a2350f95343277a078d87494ca88b6a8fe98153a50cb728f6b8b67e531e251793b3f9e6fb3d504164989726b840fb4c0139e0c68b01682011241b8b86913e548defeae5f3af5253c26ba5c0579205e89e66d129feb101c1acfc6e0ce0155ecfec1c00f228da819d51c0e14fd77c1984110464afd6e4d86febbb837b13c42b78e3f7143b72bafaa2ee6d05b3dca948426c999a5b2ffc15e17df1fc293a3389b9c9a99f1bc0e305faaef12a4058e78c66543944b3b811bd86ca1c7bf843f83fe78ad5d08d69723f75276da2fb771a0af1cde7d3fa3249fc210ebb66720fabc17a3948a96270cfab22601bb9872499b2c8751cc518af520b98206449461a17309f47e10174b6acc0473baa703730763c4d4109eeaa2ed926c760817bdb4f87a911c81d81b11cf51a355e01bfb3899e24fc83df97cba57de979f0c7a3a72251bdefab0868172330c1b776a3180604f33c2c1b7b1e88a62ba46be8df952779884548b991141c01267f1e05a38734824fdd8c7cb6291d62a333d037c1e33d7a6935886865453d697390309317aba68fe2d0a5c35d07481d636ad9f0071dbee5e79dde3143c944beba2ee0d623cb165f83a5d526969f84b9e6b6624e9d702c242eb668a103dea0c72f36aebbb7c92ed4ccdae5e7dab9010fcdee22717312ef68634a1036dc508b3e73d4e0850b35647c67c38059d99330620d68b3595c2ef0fb0fc4c057cedc631602a4cb8d6cf0e316bb3e664af2b60382383660bae0afb910b0806a2db243c567d78dc83a773d8881180268d5ed547744d9dbfff12637da1a895d00181687ca86ca29bb004f454f8811082d75eaa5db2e95593c832299ca895bdc975f3be995a242974c01e315f1b3aa199554e168dac697b6e47213a5de549ac743741b3f1559594b917b0611df478127e48c39f0d202f82e87b0836fbaa403e44d77d10d25a9fb668e5ec978e1bae16e63649d2b512ec06c5f4a9aa0903489f68c283f68370a613147913f56ff8caf5e21c8a3880381f4b4e3efdea9540eb5f9ee3911c644955ca3aff420ec694805b411387258454ae0edc9461a9ec90caf17020308ed3f67477e297ab687e2aba46e87ce3bd3ed4aa67163c8e580c44618211f401ea63e9468558deb4d127ad8bcb5d062f437eeb8b5b14e5f8d59ecc94c090ca9f792d031e4f54f021424450c7f922f145de02d52c5e65f309ecb2687dfe7aaa551191cc95f81247d339732428747f5cb35d0750745258283648ebc76190b51511d03f080bf0a1042be73f56b9a87e043cb8a0127937b677ddc72710421840b8c80766a09e6f2ad059b30c78e5504d67d692ab204045397ed6b8cfc8ebe05b0a045b639573ad19e5aa5005ec206656b348b72a86dcf6b23ec14408ed74bf3a366ee50b82f8724698571140a9b4669286d000f08e6e844e0453950d176039592ddd1850710a372ef04aaa92a3cff407feb8e5e9a8f9b576dc0e8552e01c0c5781d227039685c777a0989d1b58ad5f3d693dd8f37f353d0c9b679a4b11715904b30254765c02d836fb7bf3806cecd432c58be3c17be7b1ea9c3906da2163dc480476429ef0394009a0557fb2d03680a4bcba0bd67a921cd681d5de6c35e40103c312ae20d0deaba44dcf4cda28fb2ec4cc254119bf12a66fe38e93b4c7e58882a4920ed63e24347c5905a25367f419512845e6a368c2372f61a798b58ea4d0030b5f19c1d5474b0874525c53bdb5989c684d3e87a300303a4d61a9d7027020c78289b91b5120b895c67964fd1b6c91207cffc64c276097bea458f8f6b78e02af1a9499367d7c6c34d3c22df09af560914abdabc72cc70f3ab4857c88cb0fae356a05bdffc93e7106c628c45e0ed8f995594f7c2ad02402961a4d6b1be37c19ab31b80aa08880ebfd7edea9a2f3d912705bcc4c5e650712d32b0745ce8376f819df356750f161b6a5d4ac5dbd88a7bc24ab7d2fdad60da6aef19a7aec0492a9d64838229be3ee77dd5dd68ddbc7d8e9d294955847456a3a61239b0ec15218797c1377d9184aeefca31b43b525ca0d388a69990501a429e97c534092891b6646445613e84e2b153d82890fabe4e429739b7c82c347bb49a4b6d24fae3666e843d6a6a2c31585ef5ca20694d7e444233f450ffafe71e4ea15ea0d79f0fad9f6cf6b2e4fe8600904cad223225e62588289b3722895994e76d57f58f0673d961570229c99d1794adee7eb35c111c7ba5de778e7f6869206cf08b3a4b56913526c0ca48eaa23e3cfa5581468dc29184965a85bde7df49592ea06c5042bcc2ae484e90f925302e965d0ff3e340f72d4041cb3a7f115edee3797bc33fcaa43b0f1948ad4397f5ca99bc85feede58f515024c62833509d6d9ac0948688449b73953c7316a678b5a8cbdfb927e2160a01e3cd4cbb2c06493c96b73c8258583dc7724afd90809f9304731a91973ac987866eb51ac9936e23b346bd9858387230f3c26336ea335cfe276a99486d8d5a0846f423ae149a43e04f1b3e1f6bbabe32f1d57e3874ca046c10b71f6da33e224761e440b85d831c65ef60c09677b4705fd2dbec273e350c5f18c274d00ca9ce8142ac7839f507c18efe1ba415d99ff888492691b870ea8f06b735d287d3d007539d825ed8fcfee6a990dd308a5cb3e95a50b8d9257242334ef5dad3880bc58f76d34cf8e0b5d3634cc6f5c016a4bbbdd3d3ed92ded10e045f5fb1306199ac4976fab3d6e253bb5bebf43235e283733d86dbc9acc6d3a0988287154781dd2932e2ee1270d1a28c9340dd5415ac132dc600ed237ff4521555fee34d337815e718bc93737846ff64d6662b03023f9aafec3659262dea610abd5f28047f76f20a254430de20872a9e7d4ac44b93f63d30062d101f2e50a62a17da01f67ed3ff5200856fb064d148a3027ff915a5cfbfd408abf495c050fd8becfd49faa86ddeb08483b2eeb80e08f72b4087c651d09fbaa7c03a8f70fddc867e4c8e7769b4a9edf3b129c560b493095facc28ae9b87220e429965497dc6bbb37f9861761fdc438d3df896ba62f0b7bbcd5871741fd4deaa25a5951774535a71c13dfc85d415111189e432c3cdc05e5aec41bb62c56a963730615776380f6455563ecca96dc60317892612aac68a3a4dd3f010a853dff920605e26f2883d95860588db52229f14236453323e050ca6f73dd2e7bdabd9aeb09f8796f52f8d6bfc15e04f8c2615e1e00427253e3e1d220c4427751c6a3ca333a8ebccb13cd8b32c33b73a683a28230d2ba83577fe95daf6f484273a8ca613bf60b090c4cd0f4fd144fb1c327164bce200b91780070aa3664848917142bcb1f193928eee96224fb0cd123015f49b25c50cc5cbb3165622e1ffc4b04a4d0f994f44c522c74b8c52305a99df21d2c4f24544a89d69a9d66e6d58b5d1ed94566abe11c6f0e38e9e6e7022c253ade8ebb8a64f6b4cf0508c91e9421e70253d204c168122e37abc8b25c44474117d7dd6cd81f63d307809bd56673cfd3fe49d90b527ed85c352862fa91648687185dd1712c5f4829240bd16ce3ed72febf0fa17c08b4cd926d2f135d90012033d4a7502f5e59c16e3889556f0db2bf894d1ad8ea0ea62169c2d23219e46a6e2ce167789d8c4718166bea6d78ee08bd878a603965514441f0098a862f2ecd910d680fa3d233c385fbf6e9eddf8057b70e9bfcc3e50744060821498830c32d56cf540b3e3febfe2e7790ba3b8fdef31c600efe32f9293889b26ce3b0537074d89f956c501f14c6df2e2a3a3063d1037ac33bddf34fe6464c0256561fee02a6814355f2818b6eebd1f59780641816a157a112ab880318ed4509171df33e70db8bfc1a5531e1e492e691247503ab4051178456599186e60dd7e05e61c92b64d8b917358756ddfc6c77638651118ea24f2f77457015290eb8a751fad7f971116c6d9e80daae2a3337be303ccaf87f2150e8945648d0a5360ed572031998097b66fbb02f29ce8112490c9a111b7e8d342a99adbe46f81e9e6a84b84e0a53b4d18487b68321cae18b23747cfa4813d8487a89b057ca623e95e39cbd0692ad440e6f8b3fda31539732494511f3a2e69d23661f5bdbab46feaf199a7a890bb2ed06ba37ef640552eb179ac0bad08536fd01845ec2184e1537d02df40632b642d485fda7d6395a0dde6e214064f5099ef1a4bbc737ab8c099eb978101d729699578065e3e9577015603a485584787603dd8357b36447233abeee6e7c8446671521ac2d6bcfb6f93aaf5c3e1f60da4cb021f0dc0d4be53d8c3a020f8b2537aed1d734a7099bed9c4e4133143c04794d5cec514815915b928ffe66b4c3122121607d1d8c22561bd604b54511b88b24ae501ab6009cc4761767e1bcd7d1aea9a84d50c2009888f363a4bbe92b109c12e53719a0ac0201fa93164ab57383469567d9cc0460ad5d5a0922c158e1784d0c8681371fa24591f45ee11bf6ac34f139e2e93fc8fe36c263699c7535b8b4ea52649a195c3b4c70e0598f4ed5ba82e7ce080889252955c3fdeb44c7f6e75ba5810e130d22c35013ab54450a412f4c81209eb0fdcbc87810d0401ebe97abce7839155c74839b95db9d7727723c9f53c4f0009f34f3cf0cdd48cba5524a6bece72b3000549b5196f0e79f293d25438fe899aae6077e4c9977ff1475390f23ceeaff6e71f38651fb796ea3f324a860d1c51f097291112c4b0ca39006b54e1fd7c131f298f3fdb2c583e42844521bfbf10068da56fdf7187f70d1310fb0eba68d1f3f732f340e6a2492c6de93500425a2e8b4d3f218f987c020fe8314ad3cf0f56a0c762e7947cfc0370413d231ab42ca9e25125bd2e53119d08e7ff190d59ea8176b50a1b88d4d23381023f42ff8cedb83f24f094a2ba6d429b2d13b56e0e9f35e100080189a24e3911aa71247038236e8fd2fd7d4a823bc0ca9c749589a4e73070573943c9c964cee691bf4e650ba4a0e9435adcb164e702de00fc1f9b2da19e18f8b684b600faaf3c00b6636840f223e9ba0929186ab1f2e5df02c8f9fef6c58a910be0b260e0a4b8123f6e964527000c2fc7a7f849b37e5b0d3cdc9dd2dc39c6a132004da5b5004a32aefa49576f88be1214c454fb6145594e12ef546b5f9f332399fdd786d253be7dd1560598001e5b0d59bc14de524d3a70a824d9a61bd77ef7a74bf00f6ce48f735a09c21683dbbe1f6320638f31c1a7ee88b39cd21c766e0d260001b9293bf7527f9c8a7dd18b24b103a1f82f13cdc1bac749b79bd86c800bd5ad5406332529fa23b04fce613e131da3bf5b244f4610c948b40f42d87860a941dd4120c89f32394a76fed97add37049928f8663c2ab8a4499c23cfc2a38739a67398317ac67fe4e4d31dca9fb8b33a34b829a7ffd1dad9d14b26b38248cdfc8ca1cf6d6f9a6f84b3ae90014dc5014351ae69472a05fab684cb3d97d1537cfdc46f508a73185957ad1e7498a44a4183a59939536880b3ad5562099669e790bcbbef4483d8cafca900db6fde0373105fc869e3bf6945392538c547883a1d163e01ba4fa111efcfcdd3cdbc58bdf264d7804d8422466356a883942ac18f529d62110f0dd96795ee10423d8b1fe03e8820714cb9a348b5d87600b85aa9ee578faf94fca3e497c9db58d1c6d6a797d196c840cd2ed67a869ce49052a07f6ee236b2290723ad2314929d2f2890fc5ad0b1dcfc934b358cdf93fdf4216a39a02ad83322a2416cbb2a700ce45d71ec842b3407434a636b7e3c180f5dbc311f5bae996cd2af8d43995d2d42f586ba202a52a7ddfebee520a1b0bf2cc7ebd164a599eb6385083e0dcc40ee16276cc3e3e85d5c7291b5d6b41ffb09ef632efb8a958700c6531da06f5eaa77ef86b013caa3a8c4a790f52508d688231c07d2c54ad947b8195f2945fbce0ec7d84c13d754ac909854d5684ab4c4a67f03acaa46d4ed44d66e020b613721e23eb6e091260d77ffb2a2f09256626b27e86066429b764d8b0d5de72ef40b08269b3cbf93cd9ecd84dfb187fdf5e60f7847e47c77e99a369facbeef368f1e0ffe25f8b9ebd15d57174fb48fde55f5e24c9df5112301a0279de61a5a0a6adb9ebb2a031276ee36d73387e1b782502ac358455239182c1bd199a08f3a6e63d256a0c5e85b6501adee961af1408530584d25e4588b9e0bb320b9b99429bc39d944cd71f853cb34a25b0b0d2a0d0d36f0200c11a25d762571c89a41ab69983af23d17dd0e3c2769d9251842c52098bb4e27e39ca29e1e32f9629c2b5345da0d3f2658bf6881efe30d6c372d532247195119679462407e71a012e345d5d344cf33bfded5f96c83f4e6fb443793a762547c9547e5670aa13c30bbf2c285fcb31c972123e0dea503fb63d11cfd95db8d4759bfdc5e13bde05f0a359e72df53bbe07589484cb0a76e5e88af9121d96821d9c2d0b9f48456d9395f5509e9dce8477dd98fa23c3a97c4ccce057ca8d3257e6bd1ca2253496a512f740cebd6b79d0f43dfc8c20c04b3fdbb54a82a2b574abf0e1728698bf9765893c2e57580a635c7ac738b74ed4ab9741b1aee89835953b963fb5bcdca1ea397400d4c9561e9ef1709b552bd2bcc61e61c90a8afbb000905535932e2f1c431a88fb47596b28f2b77e2e5002599894e9ebfcc57ffd90cd66fd0046c89c54cdb4d5cba9455af5f75af6b4bd9fd40b2e611570b20e016fc696e79794257eca8490544cf69691fe7a91cd0c5766b58e41b09c7aac2efa6e04d4d8d507c345977a41f85139109db972cbc019c7884124dd8fac30dde3d4b04f20aa5f7dfe96d8bf1a689ca3038a268853efef2aa42a16c7144ea5bf52515d417ae7c25306ef4fb5331f2c0e6786a20235f502928c52cd137f3942705938149d778b9fe1ef332bac476bc79c30eaeb4798cd4c6cbeaed99412203aa680a3c32cb1aedaf21a80fc7576aefeb6364574851432b96f5c781dbcc075480827ff236b59b5d112b0e0aab609d5a3f56f33e5f37f44551020c098f46059a1f12600b62ac0969f0ceee551a1fa422fd024bbd73842b44df498103b3d3f6bd413f05db6a707843f3d529782515d35a89bb6371b14009222aa28955180287ca638378df71b40292c7c7975e47e863b607019b7bef6d8f8201fa340864a551783707c326a42b08013a6623738fe81b0df7154725ac842226a8a702b4c5b473b0f3b581653d3140f84a2e905085013943ec910704fa73dc50b856b84c7b7da5bf363aaa67c5a53ea99519696a064ce4868e62927d222606a982162a6db051ea2a0dda2006a98a36cca178225fddd61ac8a386b0cd27281b5e8ab53753ede6ba1fd7c226035bc4486438ab70c7e2f7db34f68155a0e9f72f5ee998a11a1e212280829b2a875b1ac695f5891184d66528ebe23c1f1bf7ca2331d5a6d7e852d394f59d30453821cd2779e19b27adb3913afe4d3eb49ac39a150322860e1302cd48188dab687824337b179d408ca5c62ec109a1164a67dafd702cf74c0fdc5b5a4d45951a7ea7e04712a721eef5a807240394117d85258c296019a1ab601395ef2dd4158c6dcab0272b0a75755fd548962c395656c0a0c768be20ec7b06d3ff04e8196355a46ce5b9876d78d0e1903629ae905e017f5750ef3329c4c91a676469850df96d0067a8b52186e6926384507b6431e31743357dbcbd2de476f00fc8e0280a8faba245c22708b12585643b3bfc4d831f14fc67d2ffe8d5f531471019b2594d79b86b51f720e1e3249fc628234f8784c3f27271cb23faa5b3de11ba8e4de4269d28c22b429c99761cdcf12c5af5b212e0bf07765128082545c84484d485f18df1a884f8667ff17088d171e8d146611ec288bf6935a48a68071ccb702f97f8c14c2b479b6cb966a8188f74101d106e5c46a4a58b90400519d5eccfee18a2724464ff0fe1cf64a46a4a6f036d4207aef125877ee0b5f77283454737cfb374bdf3778f2d6ec075ee5eaa30d3a6b532379fd9c87f1b15a354e8e747b4f32097abca7c804123c116efb877432a9142271a7321486986933f4ae98a4ca4182422d028dd950e6960f1d83545963b056a55c0ec94ed8f7ef9c3450133b87e4d62076ae85afecaa7eaf0466633fbe4300eba9d457eb2cc6a248caeb21d70267f0428646c4df35f6dcdec2dd57188f3dd2ab5828d9748689cab26cbd25ba5b68a8ccd2f9992714bc43c5e74cdae9a5876dc6896f5b2e3bf05a9c50e46039ff6ff74b7da68e8c8b1cf74cd30b2d086c1cf2a12277f78b951969b7b2d44c4cb90c95677941ad416a45a34eda491b9d1bcc3db7a7eda3f241161e6fe54e1c1182ba83e8b5d3ffbfa7300a8178e35e6ecec1a836115371eba3849a4159283812bcc19bc83983925a711bc0c74c24b9fcfdcc56ecea62ac767388edc0096db85603ff8dfd0e0f8eaa43bc07606ad438d571026e21412558b60f4c3cb8b39d35860c9897f302aeae8e845e74e0431b2de4a8396e02f61bb9686269cc18bbc2379de944abfab7a50ad2afa3711950a04d3a58fd253da6e29e02defc1595e2b4e56c31dd0738e64f7079e78a6f07a62f4c3ae8259568d06859d12e826ba13dd0cfa9f6bcdc269d61db438156a585ad373809d3d0ef207ac2e72a8fd28e711f83060b2ad177583e2878dbdcb00f4ade57bac4ac288268a5690173a4dda46f4fe1521dc9c70f6f6becd5ba191d4553b6f6d1b3cf32cb13be7908dcd21a99d833690c01d723791ae77ff9d5ea3fc9b3510b5bc8333a5fd854c7a6cf0b2ec5c3c9b18263ba59ee4e59d31b90548e67692efb8c75086259b30572fe1e736e2fce05c10213f9871a0ea9de9e6664df3b5e3329502dc416e08958c58f87bd150088ed60db305153cd0863f662b9c4e1f1bdeceec780fd6781b13205b2f54337c760861558ed1ba58486d643cf77b51b5dd1369482a245e22cee8c52152996b3da9fea36d898d494a6a75f58dc1b655c3e84c0493c565f4824ee666121796a1e5b7e48e0d6ffe57160cbb53d9059f3c7f9e7df29bf898588e0bc2035c4f044ce99230674f139c3f974e72c86db78811082d657724828228d5d4364b6c4ccbdd28d7d7f218b61a15063d00e41c3694df61c6a5493ebd99648cc30b871c1b6ef7af763be22b3464a3b2915c5f1590cb93ef82ba33e7df3d0c1f6d6caad4db069abc4ba2d8e75767b6b6b389dd9e9acc7bee2ce8f8252076c122125472355480e466c6478046b907e48396d52ff409bc2ecf23b0810da212fe0603d890416804e745cc9ac727d8082c3fe22f9a16a74132b4dbcd20f3cf64d7ac058dd1cdb205a71363370c7777beab14c67a76c72bf1f9c4a94ef2525120b951e09585c12b7aa617642475a4a0a125f02347e44196abedee1b796d6e25133fb387bc211331c3341f1b215b2cfd17f17aa840801eec7a81dabf5a24a5202b7ef11e5321a0470ae8160af25a2f839179ce58042b9bda0872b8f1120d4e2c525cc19c3e101542a23b55c5171ec27b11de4201a8b147c5caff5435e0e26e7c91aba3e467bf52fb84a40da31644b83464640200a87a8b56e7e7c66d2529bb091048c4156c47968028fe845d4a1f9ed85faf352a0708c557e478f3d25624f26ccf33a03122611b95733416ca80431e39a3c31686c14a9be92177d83d479fee0274c89158afebd197abf7b227bd9ae1344041a0986bae79a5c4794821a0a3fd24e396a6df105c474a8d80c5a380c215a602a0b581aa4ab563e8df2560a6cab580c917a651214ba661a3119835c6327b9a841a6226b14da0a1f46d0fe27992d4814fd3ec6d9abfa7585b130084876c6befc177fcdcac5837ee12faa080e02be856dcf0809ca5c4e8d4fe48e3d4f0009931e1f9f419215552e2e3738c51315282b2bcb26f0e44ed0cad2ab671022f39e3c3ac3490fb578a85d3eba7c3b8b5b29047bdcc95f7f3f5709714e9abb739fc23e987c16712d884e487d4b86055f79e34939bcb25cfd71ae8741ca29ac4b341251af4e95dc059d6d5719c019c92a5f69351ddda19d55e819971be757891d7ed5c51ea8e99d8f1d42064d535f68f35222e50de90a5d87cecb60ae584319c9562c0af4f8ecd10e3a70b4a5e4f117ea4ac27b9a0e8c66aebecf507850a4512d435a89eb84aea0f7f1c6341a3c2a01a11f285ab96894d806b50b1c9b0d054f257da2d5fe063c6636c496001ca07a6f962042d452a180e7e30a97402bade149994e162d66ddd89b92aa8436445585f7f0237de7d320662a65c55516330093f450b85013df5200a211ebbf2f317f094790bb394eba2cae64d0029a702242a6a6f4757384978647548416d10d720298dabea33af40d0c608416b358c054b86ea56b447a4f914ff987b2a631c037fb188320d8232b3c82a11856a85b9e0d0afb02e5b4293de50c6b4b48492be1d1867090d1552019368fb07d0ac29a1bab3e5d28c969450a74ffbe328a1a84292db50773f055d1b93d0deb696844e8fbced29092daca1adaeee7e056565241477e15e3eb32812ba0a198d446b250a7a60604c245456c841ba74db5350b20509ed8d0a48e8e88d7be74768c4723b911b55b86859e8b9e5bd2d5dbb13977bd4d75c743deb11ba5e1c1ea10b4d2f37233ed38125b4b93dd211ca9e63afe5b0952374b73b3842df4f12d539dd085db1f6aa052a5e6d6d50deaaffd8e9b9321621a7757b761ccfc5cf5b798174c751f287534ddedfc120e9ce34ae1724d214872cbbdca072454a6bdc877797e41ea04cb3b99cb42b7dbf874274edba3354c85439a95ef28677c93f53b1522ff6ac76d96b5ebe6b67ae5ee7b61bc8ca623e954d6c37c36bbd97236140156a139420c994de88636dcabd57bb2f57e4e0a01ff1f96039a4a3bddf33a774af7fbbfb4328bcdb69b724fb3e4f1031c8f63f3b8fdf15dc5f3ec1e275ac5149f928b84733cbb5fd2d6069fc78f11d1a26cdee3420351f5a55897768924236945d990d497b1642e80fb56a123ac3e0fad002847eb81ab2f0d88d7bc64cab8c411c01734aacc51acffc7e414604ab40be7dbc09778683234f2269896a9268a74b364608b4cac65e1e2103aa26695e822c2fb7ce91e056ea7e77635c34ef87636c361544a72fbc402e6e35372faf6601a29483fd143e31121d105f3b7514d69e58201a0ea0c7daeb6a759205c5116b7efed5031ffd2f5a6c2e7248b7286bde509c93fbfac50a6a12724e9ab265f661cde0639c916f8cad718cf54619e27046c0e50b636dc70818055e541a3959be128b11c858f32cf81e7506aaa46c18ba0c23209fe88ac476ed5266909c5c01887a4cdb7848d245f5cd999c2a71e6023d919faa3c2302b4477edaf172c73e605dbc56dd6137743c3e7449d9f6f37c4b94791b5580008d94ab168f5ba03cd4ab1809c42ac1c431c7fa8726f62caa2eef6ab518d910a113edbff55645c79b3a290c97d3a33518c9575a6ee9ffd4fcb0e4461b640c7c9af0ccdee03198ee9404f58bd8feacbb433af489059c833768b760d8da7c510b64e43c43b9c05a83965e1876d6f06cca0d428721f91ba5c05d0e25727c53e45a1efae588580d6a62808eec7a09c42e39e0d1720c621792e6e791496e4b698cfc12fa6d1ec05555a91705d0c70edafed69161c5b18bd423b4204be7efbcc660313d2f2923deac76b0270d54e1d5c639159f4b0dce7f65049f751ca48199e7facdef8eaa12d97dd5e37776d7b61c45555644e86a363daab0a3b242c2d42d555afcd0b708c2c450e2c6eb5b73c953b13cd4dcb6d6c625083bdff58c0b3816ddefe023bb13e2471ef8d592a4547ef161428a98fd4f9b8e0421175262f63a891e4515f5b2c30ed426ec511b628b9f67b613cfd18fbc07fe489eb1bd984fcbee3e827f9ead1324e981e47c78aaef5b12d1521bfc82a72bd053b2b3817102bc6b352e3811c857cfef8d699d0abce53f4d942e00b92fbe2607a8179f3c5e96ca9a95bff45461641187054b33486f99b1a2bd028c2382e9f6bcaa73514fac563cec8d76f9dbcd55f1b3d9cfdd40211bd393808653714748d7ea0cf62544540ef810c3356fe337968bcc691861cc87b6ae78edc74bc05fbb5da37dd70cdf5e47d7192b7fbd7784d752fe85d32c6c035ac28943959dd942ffad0d949aff8c0846f38d45642012f81f8152e4b7111b9f95b3035a1f7ec91c3f20e457d4787105c2079cd583d083b97e7cb7d1a1034322c70ebafa61d05d9d9c7273b8d2a1c0dc0432893c1987b0d37698ae4434073267b887b34cdaf8f94e43ecdc62ffcfa0c96023a1920f74d1e12defce9c8ce8dfe17d61ff1cfe535df28df0c76c33491aed7dd21d3bc893376667c38e91a91b975995dfffa37eae69dc1439c9db2d8f6924c8c0b12b9fdb55d2fd6ebd93cfd93cc04bef9174643d799b2f2a983892b9331e1ab47da28768004aa50f3475f8c34f282d6c1630e15b85c33229a37b512ce24c8a3a8e062caac3d1a948f20caeba89a6d9b63aa7dfa78061af8d6ecaed9729e7c9c849c19818c5af6b57649f5fa6682b13c0412486823703259b47d1aabef558507293f6bea334172221cb6d3e2bdd4c121c16b58ea107bcc672b0ad0db7959ad5ac03d8c4e5dcc5f5c9958d8efb40e87fa4c2980415233a5570afed5be23ac1398271643e0cf4fcfbb2d858ce728dc9e8ee535a8dbe9660543692ed7e9c44048485833a84d507cbb24dc21b135276d8b288d21678dfcc22222f0507a28e5539e3e374d226bcb87a659bf559ec6a1cf7ee98497b41a0d5154e86c840cd54579027accb0bb68d70887fdc38ab4710288de6c8d1038235101daa0c6d2037d62ef8a6bd79594e5ba16df796597c89736b2772822a37b8370da61d5d6eda9e520087ccec9eb7f2564a759387795efb6eadf606fc14ea3879d962756e664a11c0c83bed60f2c90dd3f9b4a17b517debed14d8fab6aaaafa417fddac7738ed09ba51c7159e00096822e6d518de3b4342319a8b1704479a4893bb2298f2e659dce0764ceaa84c88578998f2ad3365823d2b6daa9bcd1fb09d465b2956876b60931da9581e3919799a0cfc0daa0535fa7e50accb2972d60273d5e49c257a5d2a20c5370c99213b5a051719cedd194bb945d9e1df41c5562b08cc076a02e7317038ac1a195376df483bd56c2d846ef69c54fa051577bf8a937b1348d938bc959a9066ca2eec663d23108e54839d080c6f1f44d8fd79c9df92e218512b15500c621b0337bad62d661e379e07ea42ffc3886976ad3691b16c516caaec2b35d6dd2e4d0aa8ceacb796cb0f7a784b244e074fb3e638ad110ad39ef9aad73ab065fc26acc9e7b6f08556f75ccab843a47ae007806005c9462cb1a39d1b302f2655e8494d7e6de77b9d2968c2b42aa4da1ed6a20cf999a3b31274e671fd3322bbb3360239495c62fea4d3591c286641c17546345100b360ee5b630b3df11ce552fa777a33272ab8635bdee5f3720aaee5832ce0021656cdfc295c0dd8ffdbdca877147ccf3e7b1bcdac2c2c0b94665b3815b3aec9d8a93622f3e8858bdcac3793a864eaf05f8fc92521f823524c0872f7e963eb053b4597bc9bcb714e2b6830f91caaf7b2049f8cc51b24b96749c226a6a9e0fc734043db674aad4c83225ee57691ac97ec69c24c59d838d2479ec136c0bb6120771a59e0a04af5c2efdbe8c071466416ae2a1929ef18e6d0558a288b1294ea90183d3498dc56e0140ff91543439fc868e6e1248e46decb63c4f39b586fab4b218158c8d868013b67b0142242d0d74147fb10748b1c95ce001d58338433b7907435fe2578d2a0320a1482eedab7d62bf594e231bb2058e44055d06bee78b8c97e2e6a775a00dd25eae193c3f48e3b7049ead8727dc23ecb1d0ce12b657b56424b6ad71d246c01236aaa7694636e765379b261d918aa89a9b6b8ccf44fa29b889e2c2d7238df51a1675df84334242acba0b5a49de1a74ef81518c37a0e05e22979a08c465305b6e850701fdda840206e5c161af9fdd451291114213c4227ad4c7409b22f862571424ad67f5b6fd2e76e7dbb190883e1917e132a1119a99b5c60285382c34d7ca6ed1d7eed9f96704180110c199240b04293dd6ecf6306ed5c6e785e9befe0ef80be17f4ffcd8e1a9450777fd694cd1a7e70f14c54e96da2b6cf5c19cf11a84c1f789444fb8c23ab96b58709ed908e00f0c1c71ac91ffe5fb2bf5af5502a6b48fafb62314314b0ff4b76668d54d4ba356ebc93d6c33ee7e37aa5bf0c2e2805ee4cc53586d059c32fd66cc7e6ad62009657e85ee34f515a3098888d50952fe66f9cca949405eea3ddbd74d1880ed979d2deff11e6f7e1e0c89e3fd4ccbdd7d7033ea881d08375c8428c68418fe721e5c709a8e0ca7d2ee833c5bce236771b03dfe292d3018fa71e44049e43127ab0423eb4cdd6e2e02e5b98ebec56344b18c1401d6d503f671b8fe35394d0cf2970594e1a0e51ac5210ea11b87d178e77958d92462c7abbac0365964ed84a1e259e11211200520a80a410077c9a82fc0d053410557304212bef298d9d26a6c26c959ec60709ca09e0b97aa9ff82177a248176653c0d3614a8920624a9b93e68244f1e5007275b68c240e0b9a81f5deaa4a3343d39ed7a5c873502a5bcb2616b8f2dc593f5c96aa567ce079b81cc0e94a888e680c94d1640c8f4622ba0bd23da6a63b0e0779c10fb292eebd965fd7c7b3909435111d95a89b56cfe3bc364e7247b09c13b64d59a7edf8d6037bed26449427ebb70618524b8018f1ab5c92cc8c3bc1ba673506ecf2b6da122f5d56c5b9db80ec7481a128ccda0b3a489c1a08967128493c7f7b80eeb9ecb556be79e91a581862b38820968896f1830e73751233130c54493e150312efd40b5e5ec49dbad661d75045725709e8eaa91f114a310d324fe215dd8bb90e118cf6c6fcef970cb0902526d62f43524037c580f0bdeff2364286593c737c1193190645e514a39bbcdaef6e1a4b5d7508a42ee6c650382a3d75d5198fa57ca6438eac7cb9774c62cfd97029a84f0109bda17834eba62521e78c0e81141235478e7f9c8f4d7260b520b41c771f8185ad74e61c6777e506cb13aaf119d09f6cf1aa7308975791d320edb0990e9e9471b81703e57a8df4f4da1a88bfd530a03824b539f0a6debb4a5ca10781076dd73badc1aff31297bb04848bfcde4c015d04359d06dbdf1053042b7c3c0e8970e2c0eff86ea8dcb2bb67b8cc5531dfceda526d6e1b50842a0698e4c43acd8ab794df658d9550eb5438f64a1f4d89ac747167bb88d09f50e58a44c7ecf9a028522781b9b77fc666375da1f214513de7aca12284c752f4cb3b83b2d7a9827fb76ef76bac041b7f465997da424c813fa806df4de7803089be0f70f245a29e30a7a053457cff9bafb96511376794fdba0f90e9444a9a69ef34a70d3f4686e190001edf29a8a3616332e0f751e727e808986d38ae090ab5d4a1d89aa501ca5345b6d1dbfd28c13e8de57d3d2867b3e7a3a122fa916a142dcf2ca6d7fae033a66012b93abba2b14558e3a6af80af11d0af34c226bdfceae3bb90249edbea84fa4c21a7acd0c636963d26148df51e780bcaeae93ba42a631c7e21ccc2400aa545560ad474ebb26b8d0be7d44821a7c60cd5b3fdc8ccfe853842becf251e62bdbceb4f387de35225bf9ea574ef15fe675da958636e05d39381f7a720ca9ad405111fb337f135e64ce20cadf302463489802d927b954012b9e2f9a41457d4c4e60327daacd1f7233147b7bd2d245a3da81a8fdacec4548a4fea61c31f11ba030f199b6b82dc01bccb521079778cc63d5d1884288c064fec84f7596bd3296e9284a428ad7afe54724c516ca29d6e5e563e5b46fe99f57b2ea68840454f6be28ef43031098228d4517c568360ebf8e2be24bf12a2267f38dae377ac7ebd9b25391af46ed58d21398610f1e8360eb61fcc9c6368118c5ec6d82da7c32bd26fd2a296985933ddd08c15494ad2389aab51572dc91b5edb0d4544c83c253fc604cbaaf6223174e8c54abea6ad72d883c4907ee11136b41760f157620eaa9548600802821adf88c4c8632e207d293a0a418231d5a493e4098af707634ff82ee06ee5b44e0a6824492eef940b721f9a2bbbae517ec592f3696e80013dfdc35402e044ec6f95e0c2d196cda8cf1ccf4fa1ddc5c9fbbb8ce67c3d2859181cc6e81e4c64af9e0fdeb85c1c1779713a09151e6430dc1d29ba9f4b26e800b1edb8b3a18fe46a4ebed6d7f36d2fc4472872ea5de128dbdf8f77732ec603ac40b1843bce4d4de7843465e9a07d44bfd293930dd43558171a1ffb2673880c757572888f8ea2ec23c951b44805e5c3b3b0f8e3288a68c9787421d17e446b9e61f187a8be8ecfc7aa23d0cdadcc230e15ca9a26a70f14ef4b1469c60b7a13023c92f01452911a3a97c17e61b4df5ef4c0980d5abd8b8398eea5cb08238bce7b72be43a673fc5b2617e8b5327f05f1dae85a7ea1f7d0ec6826655ddc56230f550da93a018b163ee31d8ce8534a99478bc98348a66cb833d9b6eb5ec37115c585dfa86c8db801f48b1a46790dc01dde3f37f1e4ba062d58f72127c50ae229427e295a6fd8614ad4f7ccb526c403b73541eb9c4b201c72bab23ad9aa3134a132916491647ac3f1087764d2af00ceac4239667ca62a8483d1b7d1072707378370e1ff46a17aa71b740362c03024868c9e554b9f9d9f695d4a0a65dc1733baeec6b2f5d42305d6b1dddd296bfcb5a5ae7a1479626196f1b2b7948dced62e40a3664136e1d2b6b7cf0bc5e3c31f5ca0fd28ddba703a2109a1752ebae92c3d9ed6214e40782fc125cbf80aa4a158c9551508d4c99569d668c8d7b583c91c2e80e9b8523b71caf234719fe8ced941ae932750a140bee88d35e0d76a2d822952d370c7b503d95999071355c8ea238e1c87b71680a4c728666825b8a7829229af00d431b11cee8c4e67d532d74b8b4a647a09e25fc87dc53acdfec98f959e67bad77cddd35b9e2c02f68c80b9e1dce0224483ed5aaf863b8e0a1e6afc1585e5ec2109ebd86c2e0aa7e2a9785f09a97ab7991bb63c7d5d9c5dcc6966b0326946bbef3793e8175040bc4dcdfd08e7d287c184b8a091316558784ff126fe118fa12978105dbadd24a98c7f46a6c29caf5b0992e184f971a386773ba188dc77ccb2ec0bd99c8503a176d8535b6934b65fae3c1da73c5f6f087b76532beae45970de61532aefac91868783d5eac28e4e37822bf1e7dfb718db5144fba4a7f7340c35be09f9a466f735d545562fc2f58cd3bd8c7984eb31a0eea9af6f8f3b8d3918cbe30d0dd1ed046c11e1e6f05153b86ab6cab39d2de3f3a11bd7950ed2c55d87545eacd43a9cc787b5315f7eb7c7dbc2e38a2c7a9007a025ffb0c9e1a4511296e7431028ae8f5fa6d7d56093a295c33d2797fab2c3ce7b79a52b081597ac1f376c5af084f1125608eddddb06de53f2c5943f107f60b96101b776fd9cf7951dff8dc4e0ae482eb6aaa7e995d36c262916e2106710e84425fd44ba3e6dc40c3d695675086dddc02bf6015b817b91ef5f3ce9085cf57bcfec6ccabf04f6875e0978bb87b89c8740bf31284c1678d41ed985a27ed8bd056a6369e90b74dbcd331a0f9b485e5699d63aeb051beace7f99cb716f973462c4678f1c4827ecafe890b5084799386a44760ad037b041a863c52982be70bb4efb3a4d045b40e13b5f8d7d38c1b4237b7c8096e44549d79ceeeb259ca93d6ba4984219c0594dba8edcf8e0cc9177f2d638c1e5cece4a85a5b05af4091d982975937e201dd7119a59d03893107213c646ee662ef19baaeb645a7918da950b72ec968ed9063c84a6285c75bd6dd741c437fa8d83a633ee690b3249b0e08365d72c5ef5cde257368c991f7a0e7a62a5ad74c835bab17f1987f53589675d07ad771b2681970b4e82202143821e64fb42fc473dea581dc00f3907a065e871b63b36893429bd261ba1ecd946a5c6fd526cb309285c5519b928798b92c1f073a795799be1031bb0ae3b6ac7c26a61d1c2ab2c7a816a1200f6cae1cfa3601db92732421c28fa0b59da4168a06d3925308885707af2e405a0d5101e98b1451d26cdcfce8d8ae6c7cc47f84426af864282a4b9d34f140b6f6841f9cfc8b96eebf073a64d63019cc2a95f25a29c10597eacd0db3b5124697bb0fdc17e32db65be5a0686ac5c53b3f128db50f6773091094d7d2c46cbc812efc977908f94f4f592e3b612552b5f17a3c48363239a4b5481df1c19852816099d6614a3bc566c35303c040e44abf83a53dce99b18a006a9d85711b240de8a8867e371ccba8ee2fb7cc4232f0046ab09a825791328a28b28479f6e249fba1300afaef3c1896e0b55012da5cd955d50ecbfc691893932c6c1cb11c17500839d6f2de8d440de41f05baddf2b23332e97c8a5bb8e401030e4cbb46160347a7987fa399743f09ea1e6ed009174e6ce4e230f9d84fe9e37669a8ab7cc2c51533eaad960b74a05ed1d7f7d015eac711a0085af0859872c524775db8b5ab01a9b0b98ce89d732be0c151a71d0b4cd0c8237160507e316f3ac92db457267a05c84b6103953a595ead162a50cefc1a87321c18777e0e1dd16fd9091df01ff58e7d724fb89311c3176902ccf0ec7f3592abdb2183692523c494e483cc9921a4addc1afbd8cfe459af415f7cd5fd6136985c04732bbe81c41d9488dfba1fa3e8b93976bf397d975732a5d4ce846ddb29f423a39abbce4a8fad1c0009dda2a45b60855d9df0f95650670cf2d69b2d8a61f983de7588d6e0589d29eafed6ff59e25746084589dd3fa5102c2130a0440499dfbd844bb6920909742c038a0d303665756c98a4e01739d45103e30b0189299e1e48bc2c33053a1aeb2d86848e13f2a541821c097ef91373c827966f870658d4034c67fe532249801f3d65217d7475d3572984f31cee826989228eef5b22c5845491849b6660f7c92e2ed4ebe0c9c9fd7b95bd0bda38eb0ba8250d6a79b9b2ca0b37b1bb53a9e9d404155e1b8303e74ed547d6a02e1c48d6ce8dfebca0a8270c418864e081187e10ec25d0b97ef9976b8c8a4f53bc3a4b846d853b5938d109bd8a4cfb215941cdb9b31595362788d5edbc366af009cd4df28662ba1cba000b3a43d841b40356a8ac581c7e197dd436811f99206112ec7409465a7b5861a594b38193f3d24305368ee0652d263aa33902f28ed7e6ff48b70bcec2d602ea9028c28eb05a34f420a130744ab4bcbc89c6b8cca5bc0b64f8f05d08c80854d64810c9d9cee36cc9bc2b10f0604cb022c15d063686e8722450fdf8735833e706a96845b6d5b48e0b0c684f70d5e39b97cf1fd0c81e08ffddc46f595aac8ac4b8ee1d010be7a4791234a422f9f2e3df424ec4c46891ce9c0f2f116883f4ca1ab02a63cd68e3d7adc22457ecfca918fe5eb25ca3a38548e83b638524c08d0f22c91086b542012a9ff50dcd107cb8190c5e515968764a849529f84f3e550dab7a8365d7e63955af5671941e99e29a01f2b6a66eff0b157724779b27c7bd05252e9452000e3fc6dfe14cf44f4caa6408001a6f3920a8d90c411a4f6a081432f852a7b35f54cac322f991d2411a46ca98daf415b10612f45880a7747a8900721c43c9172162ba6f04b711bf589bb1ebd13a792f42244718c5349740e9efd2021122f1b9ef2389e9c0971e2299fafb3afe3f52be8f9a85561c3671de78cbb03b3248d9e8c56e03dfe4b8e430d6d2f99f95e50fda1dc69f08c6e820914101ae48cf3a05440803f98725d74c56492f88ecd90e07e409aa94cf67229bbcf9803737b7410fb61b0f6c91a94ec1bc6e1998431056ef4f40185170d30f810692ecc156a1451dd9ef252a8b6426aa195c18fe9e237a5eef663aad210e1656afe1ea25f19b0350b1e7e488583659344f2f38756d48773818dea120998a5f3d0fa15954061a500f278ef43c84e5b28771f145f4120b882bb1124128a2bc3de6557898ee670d47a2803995021f33d984195f8ed40e4e2471030674eaafc3453713af5cfeeb1d475b85b00c5d8d7763aa5908272f19f3626a54380cabc25b943d999a18b6f0585d3b0769f64a0f8dbfea14b49a0c25b43d74b0c4d8652f690e2a91039bec5798ac8a54e22d6c2bafa03fed4a7cf649adb6c9697ea00ed19f9df0d2e4161aea097a3f9663a2a43e445cd12665d26e4576b0348935e99fe3e9ccb15a4ab083ea86f8a90cba53bd66b229b8ae93d6bbcd2d2ccf086af932498a33a4a281af1b725d9874d6d70fe38e315c5ec59545913e4f83cc6c2c21869b6803bc2f8e9503ce97d167bf8fb9e7a1f0f4f08b232191d8dc0542370bb953d838f0b3230f63c32eccfc82b995bde6a343b46c731d7479232c986c1c60d834c34d7e87c1ff860d6e994f4ce1ddeb0fec497c1d017740280c542093e6d27d0d673a8eb1a40ddb0d64e0fead811a3777a8f75e087d881f94bd28a278617decc4c410e275e81a7f06fd8272ff955d9ade9d281c24fa82062c0fa09863e5bfdcdceb16fae0768142e6fef3976ead0f0cd6c0beca1fbb41dfaef4aae14eb86a25a0fb9d2f1d70c09bf167fd069215c36efe345c3f60299d726dee5cd90df37f42657c69a2c6db2cb639bbc027be6bef959f5d708574ba312710614ad6ee47f6aa2b89791e8ff134607f016deb88ad3b100c49a065f6062e6ef9cacfc28097c3feac7cfe0592b642815de705bb178c88f54567aaa3cb76b6c106a3a1ff04b4cca2a9bafc3f8181172dcc72fed7f822760c920290d1a06e15282edd559f0af037d4aae632db830bd45a0cc130aa13d5a4af7ccef69a880e5ed6febfc5ce314b900eb074aa9770df3355ec9c9519f1810ed445d686a1387bf355a53ab8ef27b10ec13e45b82334af3e41aefdb0181218920227e95e7034f833b083195bd78636a9575b23998e80444fb526a0568a57bb20afcf4d584c02028cbc1d760851def0167fbcbae31e718052b1df96d6c7a01b927d10dc217c398a738b43595e8317d1b295cd507299800b3f73236f1c6540820fea994acfa1e142466197f632a8ed7a166bb1a288a13d52b8c097460a35dd8d3543a1162fe38c224dfe04a4732553a72a7cb31d4cdda6c24e5a9c09373c97fa4294b87edd8424969e1299e3ddd2be3eb10739dd5c9f1c7646131a62096a7f2cfbcc45d32ccd8aa435966590abcea105985331de19cf260875123e48e036c0d558e5170aed318f13b70a28ca55655e1fdb85c777957b2b6e75f10d442525dafe839a46ae12299c0cfc4694c4883199322bae0d752d8f20cdcbad6cb8b00053c333964c71a69dd5f4989ed422c6b512b6ee2be78a0936b507c1f2b05806adf76481b6e4856396c9994b1f307ecf631d5e2d4c7b3fa4adcd4810efd789eef1e70b562472abdc5fc77690eee9e8d1ea4e0766f4e56e007640da223fa6f295e04087c16f4fb9cf89e0b2d446bbcd7d2485812ed5c1db21e3dfec3f0f747c09d6f163f41fce474172fa2359fe23de4d5c72af14d57f826d5e16a2b23439d6f983336f2b076b07251cdd082953dbd97032a440167c4ffc539606fab913904098a19ffa024b560982ee9f8a3c93f743f857692207a4f733909fc07277e64a11c000cc029d6e4185beec04964c202920f7099bdc2ad82b76daea8d1029c90ea46a9c61fc096b202747f1576f1dcb0fccc1aa9b1a33d7112807c26b26dcaf57eb47786ca0b0064cd9f9a0e833125ff97fafe6a5c23a2c565276580f0910430dc2e21123d74dbde11545ceba84594af207f92d2a1810785cc3880ad3be08be90cc1821aa3177cc1d0e0733d078cbf3a5cfbce975f1fc899688535fb132401c264e81ad0c38349234f4c89b81cbca97f2faceb6149576fc662b70253ab4da8a6319a50faf74123b1a8dd2faca311355ea75e5d5006bd0182fbb7724ca94b8b8c9e8d0a05442862c8f7566cef706af56613e4f22195783f41aa329683448f6ef72f0b66c3d44408bb319c5e4db8e85683ae99b1a217e8e0140fc353b3da378410cf7af89a052763bd84d70863fe49335ff7021cb823f02fa4fed52c2022e37d42721ef60fa78a10411fc5d0a7bff3e6d8ef432380a2570f0bfcc3befad8c9ffc8655f18e08bcc0bf6d371de3084c287b4213e8b9cc81f3e6436f8fc039018a654057a68e3e34efc1aeef476e4454d6b53af27f1c0c7bd0914ae62eb68c4f487e148842a62d91d728ddb8f6b140b28555d67dc73a05d370215d293dfe47523d00e81d06957e52ad79f10b0200012145b47f506fea40f3d8c5f33c80e81931f2a7c269ef8ce03677a3e17e17f4f03e879d421e47aa64f0004433ae9e8efcb4ae29f8448f77f30c98a3261ca838b92ab01a36ba3765fcb1b964a7bb8b1cc62e85cbe0d61d64c5a27053d30f3378173e933244aa570f1cd9197480ff0fb2ac3a083cbfb6d55b853750f64e82adfa3db7a2c00277b6b59c782571bf26ea655f2a12c602f7bbee9d43726d7b30d00d4ef717873b2a93a8dc66d2a0f86226c99eada8ab6600fdc21b03fdfb2af0ea5d330a023e19803a0c934e273184ef7668cf980f294faf24af6555abe7d9029cb17493968617e7e981eb4e8aa9192c7902f0c478fef9f31a59a38777fe93037c5146186210b12abf6b8a7c13b8ef567b63bcf47221dbf1cf3855dda4be09f2ef18ef43634d002b5e1468194cabd9a4b6e0ebefe5d8923eebcf9c0ee17504382b2ff125e9dacce5a5feee9c9449a01cf9eaf9c561f91b760a61265a0061ffda06fea7f8713e3f2e62fabe212fd0262cab3f57deeedddfa9e1f6fb6e3444f8dd1ccb4c20c2d9d0ff53434371213b7e2e803cc96b20cb15207feaa7c60d97766fd93d49f000525b2f8e97826e2132e9a75f259229f7a37c34438bf12af7084dcbdd65461eec374a56b3ebc7e3203904f2386c01bf365f3f5b6f80ce8ca51450e399f91fbdc68705e28dae31363c8a81e29857a268a1f967e0baf5d3a18f417b0c5a265d2d2b44c7ae327bc6212c119cb85376a51099d3e77252115b807b9bff3c7ea4568400f71a0bb367cbf6aec3758c7bb4987a0d9264042ce08b0970bf92b622b1e264c09c0ee6a0fa30a377871a025c78392bf5f223432c5d1a67d32ebea96e66b7dbceace4802ca6dddc639f48904ffcd243ff07f001145555174a7fa6321fefeea1d20d53313d1fed47b475420cd49215959b7d82d1c3abe1ce20addf1290ab28571d49b4d29ee1f4083be76459486f7ad6b8ee2a77c128e7864c5a471413f50d066f6e7887070ea48c5eb08d099c1db4a3b46f008ae5a4f90d817d961984ef9f21e4fb0fb30d534ca11e57ec16dfca1fa3af94be9f705862442217200e26569296a6c4c9eb0d71776024cdba6ffdc5cfe039e15584a3b8fbada53782a31e926fd9c72aeeaee0ee575ff51f80148a5421f81ef6ef85e884d6ac93757bd348079d05d9744e1739324366ef9fbb53e2d19f5ebf26bbdad2e1687ce33958f46d87ff441467453b0b2a6b76856a85eea90a848e470f29cd26cda35f128eeabdf1108f6e3fd243a698b5ccc20e21e1369421ea1ecf4b9da392f0440c9cfb5bebe9938bd3b7ac4609710bd1b9c2a68722fcc09d95e58085420b84afcefc51048b4f1d509db83789df5b0239eaa427492d501b7fe1d267b9d13c2a1e114441b6909629cbdf9de2cfd8307610c52a7897c6f40593105db0306f8a62141454c0c23e3663c39127ab9f9ef5441c871096d853a31416b226ea5f72b034c86e9004fd14898286e14df57f46ee8b4dd25276aa9f8ac3b97f0f0d771be6352057b289dfc3a35b31d9dc89b7059af3b1b4db1d915206d7bc120dd313f07aa2fad46b0d652f4f4d276350e9bdc6a7b1668c4aeacd50a3f74132eb511665d3cf2dc53efa7f9b584f977df84eb57ede4a676bf79c323b35b5f9312a8d530fa932137d4dad5b6102b87462c656c3a6abb402f0d624d39d47f54a9f2ca43e364a757f270bd8979e9b8bef5aa999073488d113ba466918b8f980f117f48aef8edfadb2fbbb1095f5d1829fd486e200562d1f1aa0d5cfb120ad6952251ffab3f14c7ede4c0703adc85bf48129a8cef4835124688af1f6cce730c38a0418b16f30efa7fff581125dc188b89c2c9c4f7a15062548b90b8510aae7361c320c2aae17e33d22f544bcfd4139f06c182963ec421f5c34f282f9cd32601a0610af2eba62825f2df77d1b2d8ef779c14d494920ebc1e2e43b6d179fddcfd52acf0e7f613cb1b0ec620f6a18ec2afef53b6dc78d1dcd835bb22c50b9d8a1b7cb8b87484e5bcad3692011cb7096003daefd6288529614fee0c72af470cbada7b99979f50c6b38832a132c205e3fc6e671bfd6234c71234cd4a178d7c3ced167683722dc2cb734b5b68d01959ec35fd6c086527321189d887e7660444a1a957ad35bf7a29a83cda16b2150f1adf979c7b2f62a9263bc8912c899e5fcef41f6e02bc16a5f8fe507de1a3b5e1baf2fd9234ed9d49891341f6c3b0b01a01f5f453d994c4629572f89f8da8300be01e7964c322e1d43e86e7838cc3b08fa088786b98e278fd36d68a932e002c454a459884bcf9538e39e2a645a1e2ef8fa1325bb460015c5c830914ade9ad1a4af3222f9e4dd0aa2a0ed10281b246392cd2408ee7b066b221f9ad6b1ff67d1206b0d5db70b2dd39c87d61d962a3a170a591c59bae753c24e87c147faeacfa5dd5c5d9c6ec52f8976dc71c55cfd7e9f5c093fb92db0ddf44304cfd56bf3258e6a329225d19c1d91d0814768063a97eac770fe37d60a80859765962e73cc0551807cc947157fc52867d494b167e80ae6ea84ffe320c9c617ef4583791f1f746d6b0413fe8961ea7aa19746434ffb52eea8a27cc2f49f57e7d044beb83f8fbf7c0c6d8d70db5ec16d9fcf9716ad5317dab2e60f1855450c9f601871abe81b330e40ddb2606a456792d91bb3ee850eb36e1e3cd102bc1caeafdab95be1c466c5f52b64d252067eb7230b61959d29c6c9ca8416458fef1e95eec9357ab936f4acc073f02edeb05b0e2ecac65d1e3ee68ecbfd6b6bb0dc95c3eb53b3469f4292bb594de36eb062079eeef1cfc5c669b24bd5ebf1f71be6f1ab4c5e2ebb299117c460ad3199b2eceadf024e2b8eb4688c696fccd8d8bde0eefe6d0572ea6d0dd856bd655491ffc1a5e1dd5fc21b88af9c911f973d1f6e327caf2338953972838d5debb62c0d84dbea1acbbd57b1961388feab2c8581d0f4e711dd59aa0ab0b2bdaaec4a48fb37d34b44dd9d23c6d5aaab0d10ca3698d397ce18b053122e0e2549ac6fc6048ff0f7c57dd773b965313d2d8415a039cabb401f22da46bf80086a1aaac3dbef99f500bf40c296e4602f4b4ca2bbb9fb8b6bdc934c7a5e763949e19ee8e0e606f1ba66267182f6813920d7b2efc6160e68ff95b71133a2417bcaf7d26ec3a1313aa5566d7512038a7202046160c042732706c834a8027c97f31840e26b096fb329390fe30defebc9d688b7b56d12bf1392370e41abe274bda4e3dd6b8d596fd5cf9ab804ba429d184d6a2df4e20978a59634e6622c23efb1262635046ddeca1146cf0046fee29fb531800a8703ee505cecb0a8b7300492c2f1645125b7dd437dc841e584a84a306ec8b0648f777565731888269f30be29f3f6f40f0306283e8c38fcd0b0df3521374bc8f1ae714f915bb3a264c27ca531ccb27c4f9974e97c7153a75cc76b5c533f248119f0c936f7cdfc5bb716f25465689e33753bb41a724672630a3f34221770bd0e43d91bf2984e8b6592e196e985cda261530306d25808975b095fc761434e064e43a24c07ac3bafcce9800637d79068102ef869ab2a5c6385039746df0cc254f865c281fc2b18e9ce282c502d82c24bd27af47e8e6a8ba06fbd32c8451c72bc9f18e4d25ee5fdce3bcd362fe2e20eef677498cd78a87cb8cf20b43af3a322590dbff5770d41fc3eb33e917443a639c999b9dc8f7c21a72be8c29d642f7ad8654b19aa1b9af90d217c83246106aea75669cebf3bc32e0b56e0d6f9f53f2f2004ef19f53a42e13ae0d2b214e68fefde72fff8d1992766f4591f12c9829dadebc12e53c8a7dfa9fb151c17a7d704dcdc50b7453bfff4b7b1ba75d781b3f8ed14889f2e455ad3fbf0e5a318ac132d19735be1ef4d7232ef0aef0c540a967f02a8160ab01965f88d439d851cdfed1b2185d10c515ede81a5bd539910069cdf012db4170e7684568d25618923b70ee4db38a00562bf3373889674ed91a812d0729745547db4a953848022bbe7fbaabb0ed75a6e38d605081ed0aa60cf21ad8bf50ead25eb3a5ad470c68545253d2ce7894cd4ed8c2204f2d67ebea9d6e3c5f2bae72c4516c4a3259dca9579e685920ef64a3aa342e0ff00aa5523d3286ea31f50f6ed169947890a3e2b00c0d4ea5658513e2f4a972faa9dcdfa9e53a34dff194cd40c53fbd35efd4afcbba7898ffefdf1809009b32f95f8ee71ff6d3c3b1ac141a32a7e2d44b4e135028a127c4508721b7b72430f6df6c3f782651a12e8959fc493dc282a6deb2bc1588fa5a5ee451270323442af3ea7a7a69bb37f7e97fcfcbf61a9ba7b8887193af22f0a5763d4a454c12c21a516dcb11c0ebe24c07860f04194005778b3a57f610f73f2e1e809b79d16e5e9cd0b381f4973c8fe4b091766cdc50d88fd69deb6f8e06d7d2a9e0da43c2076ed295bbf395348946f45f186f5a9c1152d58b49d836e4646a2da920e5f761c64c4e332926203ad608e0a6338d0e41d1cf28b36128e2c12b260044169b2184d51718f4e9759858114ae00e0b74eb4547a68c09e5efcae8f92f100f44ef337a288084b1e1d870d3a50f51a832fca15dfa0c3671067bc6c1c5b833c12df1e016c810823b567becec469f0b4686b13bcd350b87432b056a7e03354c7bf21905a670caa4b6e7cb5bdeb30a3315208642df66d2e35ad33a1ed8b2da5b55855d359138c90df2a04bc2b5a4f5ed770faf381571b5d3cfe2cb3249ec4902cad3c6afdee3c1dc42254e27ae8197542c522572968f10b0de9425262627d3db01314ed712d00269a88401134254ad879bfe46a67a40b7e22efd38e2d45ab2032c2a39ebf1b69f7d137304a6e0f1a8faf9c19f5e4cf6f01f83ca5ba828c5f9303a16346859e07030a60972ba9fbab42512b7b9d985a13777839302b3861c91ecec4482c7bd20c74f3450c45635a20f1ad21c590ea378501333ce504164bf7a1a5a23bdd15655f39efa147e62ab05cbfac89a4733f6971bf4f4614b0bac41903eec4c09811191102810f045c286ebc42af132da406bf343ea07f3dd2e7ac0ee732ceeb53cb4542fd8d30096b1daff2f804ecf35a8b4243650ab3b289c7bf3f44951cbf89e88cccba5c300de287100af90ef48b875cfc7787ae73bd312a147b23354d2171d0b5617fad8091ab2e03fe18db81fcf115ad55f181bb15afec19e9375eee39638129c7e8d99e68ea0d29be920a5167fb0ba1e55f89468088565bb9e472179a680440d653dab8a802504daf53e1640fc072c0eab7a80ca7ab9c803b29c087cc253f0d4db110cda794e4d23cab100e52a90ad48907fc09fcc1efcee8b36b1148ee13044da4bb8606efab0ff141d3e9e98cf8a5217d15014a0cb648098ba18b7415ac8f478212815903c0ee221370cb0a610dba88bf8a27b230efc657494b4120781a08d4fe79166e316ddc3777a0f0563430eb78b8f706797302d26c0fc5c44887542812043bbf0cf3ebd6454d62d79b3f96cd0c4c93ce550c1eebc980075a2df669129677f58a2237f17db574934372c76106667dbd430e91a85e4eb3a772ecc210a9fc05d0b6672d8cd85382a28ff437a8811f22dba975a7d5862c474fe52f40456923dbadca57412698bd1d1ab93ec3bec4241fbd5c904cb310515893ae00edbfca1b06692bbc68fab07d9c5327ef66ee174fda49450e87e2a744b9d3bd56d1fe969789f80fe98ba29615d251fb29193847a65a95aa8f8f3f4045a5ca37de0c68ba57e81dea95a2c882a04be73f904fc50104090c0481248a110721a06a06387ddc37a157fb3b750347db60ffc775985dd9474a0f3cc8eb2e4f2291faca7ccc32784561a7ac5fad3b6b37cd38fa27655fb9e6cd557d14e46b658dc86bd97f08676d445e5026f836d36eda9c1174e3f4a32da511b75125bb8ae5c91e257f83f89aa8001d180ac0069c4edaa4dcdff8c165c314f2f68b3069e3fc21301ce74d3edfb01f0eb75fe27f6e0e3bb3f40adfd7c75e67c5e3a1c327e1a321933991801d142d3fe8a0d9d7691337210c421fdf3e85f893406a83d0c623ffe81cc7928ee543e7544f16bd0fef95b4668994817614458d059e94ae23f8d3376ec7ad0b8e8a8ed7bbb46911a941d7d43fd2ad06149798576b431a95e4dccd2c717f407343a57e93881cca40455a37a4cae093e7a800de1acc1f1b37020ead0def95f17132d15d60e6157541aa4c1a5d508943a1a673d4feb7360a2062e56744e875f258e1dda0450819f0ed25c145b60924cba90f7a5e3a4b28cee58e8bcac936618aae5ebee4527879fe95b2b0b22eff59dbae97aae1223878037fb5d825aab5e7705550a9f647a3e41416f986aa7197ccc2f837ed7e60953d3f602019ddabc1d196facc9c580571cdd2c0205e9cbe6c1e7c4299554aa20b3797a3614b4aa12d19657b7847de99bbeedf5405fcacdf1aed9290978450cc04f8ff30cdfd111c217495f7082aabc41ef4c2a1f170da820e8a2f2ee013413fe98c81f912b35ab5f84a1e3e901efc746d42e6a8b204dd09027f7bcbec1800f4117984e98bd4707b519606e01c7681538387ffb2666c4eae4658845f021712bb79969274a0f97d1c982353807231e1bdd4c9c7e8ab7387a5412cf1c5c8423cdb28e890af78802cd178522c798d2bde8698ffb6ef426c79174013ad269487a06fee2dc0ba92fa83121a97248b1b216a80133cd5679f779fd1b68f0315c63f8222cfdecac176c24f829d2f287ce93f9db4a464bf66f73fe021e82594eb3e401d28fb1b16334382434f7187b7b74c280219ddd1fee2b889b4381716440755da169088b3a44ec62198504b0608dacabe31fbe8192eb1bda4f20cdf164c965c34269eabbce991c099ce1096a32cf90f7db050c726f68688d2a7f7f14615ad842079dd48601843545e2281c986b1a79e056865c7e15b2635f0fbf20c882aa5972c25c08d1b4fb9aef39767771d34a408842592e02f2b368d3b7888c7d11988ee8c51dafc04f9443a81167e7a01d686f19d841fe1ae52809b8391c426f960ce947a897214389d93e93a3777510355a7f8559c96a7b1a7cc26f9e76d67ada3c1db8f90f9dcb15727e4341812b74a32e5fd6b78a9fb294a9ab29d2af034c96d303fd34255771945bb0fc7eeb238dd4ede69d2d0f50ae73a6885d0b41318394350ce9ba33afd660ff76df0fe06bd3fe2a6f7bb65f9f67b8ed259cfcd03c337e03e0aee1d085139a33c436a402caa23a685db81f743ee932178f2c45fe2877238468f639d1885e9fd3c93dd66c2d7214cd72c3dc7f66f56a29a471ae4ec35b1940a5f827af286adeae98d0ceb0429722faf94534418eb2e44d6356fdb592d29b1a6a86ed1de06713701d129ade08c238996ff75f9c569014283927a479fda624f2379aea1bc4bcd9683dbf9e42e2560cfb8fd22102ba030beeb8bc1948fd54a2b74752c9bc770502b6ed4db8f6e0fbc6543eed5bc6f3de4c035773ed860db1053106be551f2852dc514a3d1670f5a0ddf2e1eee7d3590ce6b62ebebf5888293253a95d0636f2eb28c354224a00a85599187d4ee06bb314ae573b0c3a12639abbe04c2404e8e566919e213bd18a26e15c9bc966960bfc8da7a32e374f2e1dd35f78650d570e678c9bb65d818f34e4f1dbdf2b816dd08a51535324e611a8554d2250258e4b750c75e11d530e6443fec00526984a9759c56b2a9f3089fb3ce8f2612bb5d388731b404930b0fc64a9db1f8f65c07057634c71f4eb70bdb1250c4fde6fee0ebf083e2590041128bcf98caa480311f73fe9ec3e20a7220ae1d2d5d2aa921cdb5d33806eeb10f18b837a71247310c2ea7f9bee8fb1bcc3dcb90e5758292482a45f87f6c572386c6e3701fb824b840260178dcf12376040c086687a8c9af0a39f8f4b1ccdf3a74d279436020ee031e483e0d9674e517f53d382776044fa98140a04435c65660a92d7d3b58c2553f90e84b9ea609a6093f60e0a9a891f014ab704cc581c85099a248e6677ca638af0d60e5f2d9da5cfe6dfc918c055e64760d78d85ff102ae4f54bd2fa3b67dcfa499931c7946455758d9a29006a88b139e915dc51bc7e16060a37b7b0347f4a89526933cc28d944cf826deba6c0ab24cf87a74b501b7cf6dd4e780036b2b6c548c0f5098e52e9a036a7928b2631b240caa58dd34b89d8688ef091a6b0585b388cd1326464f09809434c216d1f628e2f21c2e0e10f78b8403bf890acfde8f0a1b38755ac5e926e708dfef160ba87983f010a654ed667d11ef6463e1ec921a2ae204df4a792da6752073b6716eb215d40f5a1b19e68353fc00e4db74c3ad9ac8a38f158690305a196b75ecc2339f27b074ca64a7328d68010bbc75d9c385a474ff36b8dd9ac325fe6d387515fb2dbcd76d576165e1db47a3ef75cc4cd3398f20da57a0132edf9be9a22a74451d4649959dec7be671d219099b0566501f997aec122eb3d22c34581c141cc818b53a696db650394a2785afdc725ec73d379111ebab0c1a292265d3599e5d4c87fd2d571a0cb563b5760882f20b048f6e684059a379d13b31e6b65890113748327897d3c1fa07348e0c3841f99b44d64462f496bd6eb4bd9159b3ae7a318df2a06d96f80dc9302cebc0678d141020e3c5a0caccf61a9bb2af58a4def44f01b89f0d619d0a43ee455f4bb9636ba2e1f6fa2b6dcb4953fa5222fc0ea08dd36ea9a5ccff18dcd48e7ee2a0a8577afd9d012ad1cba1a5c0c26b1b546d411af7b22befd517523b06e4a173eaa0a4675d1675a884f9ab87d4402f7abf22218ee42e5ce040b5599ab7bf52ee1d74cdf439280026e80e3ec1240eef68d527ce6ccf6c97ceb8e796ef745e092996702bd74a27830c3aa0d2ca1e9df0d97e0be68f1f9b42397a56bb75551c1eda75c6c03e40c55b4aa3f30115bb1ced70b5403110dfa02ef1d1c579b1c11db2aeb2bc94af949a824b6215ad260bf48eb0c9fae460c8e85ca0b4bbe927e8e84b879c6d91943201834d405786e6818d00030dd30267b014ba3a4d592d3c0ea842c32a6484612aea1ad2ab6bf8ed226583a65164edea9f7950c596637bd5850ccee4becbad08d627740787d48233b06c6df21772225900192f21d7f525deb4a3c43b13a19a41e70682632e4d6db6396febc25c8dad39ae2c165889b7d60beea3c038f7fa974390d970a36771132f1bece686dcb47537f047d6c39d4d513ed0815103f4449df05f0d26798ba336077fb1179dc91e0d93c02f4762c319f8838be321fe8472ae6a1f5c9ef619dba1825f72cc6008c808d60b37c7b13e80ae5e74dfd190fe0a68db24dcc7a3e1fdb553c9e5b0af587cc67f608b34a508617f753ff339af40dfc819834099584fb0029cb35b6228dd52b2313f2d83acbb12335b583cfb16dc85a7ca0d07e4aa47f4c8217f5a3bd41e5c41d163f2439579406c9c7c1b3fcd41b4d9198c515c09392210b782ea26d01f4dafdf8c016162057e6154a6467f2eb3fe90075bbe98399fb7d0fb56b593dbc947dda443a30f02171522cf7d921e50207a95a146452b0f4c5c13911e9e8bc496bd60f47589d7af8cf2ee196ebf128eaf16b05a9525d2d8edbeb33ac0d352d18b94835a9a62d727153f8ae1ca733fb8e40f6016aeea7488212f39054a3b284d5ea0b3cb900af1718068da06d4da01612f53c5125d6f7b386a1c5b48c778f7656b3556645ee3e959775ed656bbffaeeafb39d59baec1501118a0670ce6d819948d02fd3275a1093e613754c6ce71ffa03e98e8e49c0d0c667678b52bca684425f0e1ba66244a1bf730fc3fdab78a6d4e179992609a7458c5a19961d3a264ba35d01cc04e7a52c29b59527eb458d3a446b69e07505657c1f13facf7c34a933e50e18279cf7d7a7837492dab88e4bcb7d28389b6582cae7b5e0e041cb929c0ec7f0685706af98dc4a0cbf33a7983555f06712ccc37e4be7dcfb4a9ad240b522eafc38852c303456a02b40a2f91856976ee5845f81b38d7e2cbc1cfa512016fea9daaffdacf6896fcda6cbebe5e173494df7c77576118ea0b0ab65460c1f2378195fb56911e65c37cd8bf59b857bd488262fb4b5a893c993cf4de64ee96e9eb6a0be70fb4eccb545a0b244bf1a699f48b54b9c82b004aa48612bf2eac713f6400ccda45a6ba43bcc7a6e9556962f15671862548d26d5cb385f82eb7433be076c06fc80025dc040f12853494941735613d314d165228f61a34014717cff7aec125a3f255f2bb08301da5274102e4a7a569384e8031f0324dc3927e329e0440c48a1056033c0f4cbc77fd481c0eb57ef215aa85a376cf34e9121c84d80f15a449fe26b8f13d2acaa5cd11e5d42e3495188f72c39de16009f476f0042527f9996eaff2136a3146b1c9472be40d4c3790efdb8eeded3a810377c70507ccd3451db0ff6c5675728c8751e1e621d76372e5c259c7310eef41cf6423d89a93ef5327bcbf8660e7d762a55e7dbc2467cf2629098fc5f723bb3004eb8e034bdef45448ed694fe58b9ff933047b20059982615741a5eaa4a991f8ec0128bfcde32d6a7c37d28365991f815fb4c45c918ca1518cbea30dac226bd272baa75f36a1d2238163ba5c8e6b18ae82e42b8d520bcda77f8e27a0bf06e683973beac56cf7317c31ac305095491d2252084eb89549fb015cf4ea33b15ff247ccb8cf0a8fc54640290f94fba867e66e2eb3367f070b99cc432ade3fe18e00351a2b2527377acdc9424d897812d3387d3180f1276bb9856cbb1e2c14df875a862cc3e7eb5f84ac1aa40559f7a21292f90d5f3df01b737a8789b97963b4b2475faf124d8edbac2f465863ae1f59f7d57da65a9b2d8ad397343a1f05c3e7d78e193e920e8688d57341f5b16d97b71d200a95c87e5e2017799e64e492d53321c3381ab01738c4f3fd067cbfc406206d2e07c59ea18e1e22f0ca1fb8d7cf8391eebde8551150fd3ca58d6f768a2ca08047ff3c87b57b03aac1dfeeabcc36559928fa58bb8b2e9fae422f53a028640bca6af4ea8e27adbb23fd52813d86da478652e7e704cc141e66c37522ec64707a4fe82cf1d94ae712c42d6aa6291907fa02cd26a287359d0352fc7d6193bb3c04ff483ff8ad89755f205938d52ca43f55bb3e6f8959b91789149afceaa116a774b4ccb013c6d7e31f951ed705d6ea87324f2ca991ba1bc9cd889e6736dd725f7ba60b191ca0d15c2c95bb1b97a0bfb614b7b7d4de369aae742f65b34436490a7cb24330c585d0d7096e3e192a7a242ea1182bf99aba952bb7c1b99715961d550586310fdb1929d709a167aedb3fe69fc2c285298bff0e642c2b5546fd6186aa34df5e4b48154050b1e4a7ddbfff762a7421f6739dde08a4cba8574606077eb7e00e311a2094fbb9f2abf63b018864092a6594b595075ff4eee357eea837e38a8d72a3fabac7182a2eef2a48a86a0580db935605c44b57232aa2aa80892ad7a4097467b33c2ed31cd404dd03867f63ef6491990322196c0b2d0badc990f236de84071ed8e75c60ec78bb016a572f1645853a82a6e403dbab4ab0e3a6acd6c2e1186de1dc4aba0086c7a9291e0c9cc52d37216a3feabd26e6192600aa74b2ef783c25cbfa619bf741b9e768f0a546afe7c9554b73a574308fc69d6e58305746fea774d7e85a86be24e48d428353f8d7b725d6ad576ce3c800cf11ab6c5da4ee2852f5510bdfd3bdc7024a6ec7b239c9487a76f4b677fc6b143ef2c24a39a7aff87375a050449b2955bd3f28f1ead218580c63d559510986012f2402a23ac99cfbb82dfcea84ad0a8bcaf76e56718e345a4bb8317d258bf88d53c3dc682440e40dd57d6c05ccc3ee3763ca581c7e3279f5946bb9c59a0acd94f7f5ee8887aa218915704f5ba4c6c30cf2c4ba76adeb4cdb81adfe1d2948d988f8c99aafaaf0d2b3609d349af759239636be83753e3b560a518b54f70d29133de5eaeedf6c55e0c041176de8b803e633401fb9210bc530cb1d0c48f68ba874141bbb6c3ec5eda20effbbae614615ca2df7b7fd089394fd21990feb414b9fca47891b1e6bac36f94412d08e226ac8102b907b2dc9f06590fa16650272dd19bf8462763c55445e1a3f4b568f911609f5b204da77da36c3fb0f6b74eedeecf6c9e1560d01b1109e72b0b15421ce266cb4b494a88b7923c2882fc6e86bae55402fae2281326c2edab9ec192f6db9020211bd4e497b9b940331a8c84afb70bdf0396a496e6bad75e22bc07f7ad3f51944986c04f1941569dfd03f001e34b09c8224ff4d4b46e9c1cc7d93e9181394ca519d67b925ddfe8845d54fa298c0bf45a84d6a6d0cf3b74650420360a939693ba1d9afab123a812156058e4fd0d215752d6b73b9766ce650422e2feacce56c488eb88e637f93a9d60d0b8bf10d08474d0d6c287e09acffb9f2e1c27fcdeab72d979c1f84440251818daa09e9f43ae16077128dfba5e6aa14cd55e4b25fb41ef40208e523ea43d6f024a7adb0986ea9f17cb35f58c291bc9752d41b5d1f2a2dd16e4f77c49a3aa2bc8a8be126856b1dd4c27e2c3248dd8706a220e0b060a4099d2d9a4c3c8b1422f10ca7a49cb289602d30a0ab55c4552925bc9e3c920fe4b55109b25a4f7850b964a97c662b4001ac00bec815e08211c9b9887bf1779a653f950fb36b857b610d0030e809eab35c91889c7ede961d6cc76564a02ed8021700eb47940d4097361ef412c5a2e562e563e351993ed9fcf941e38fcd3aa7184318fe293c3cca26a83b0bb1c286dd1fdcce124bd587897081ba762afd91312265fc3b174f45974c58940c58fa68a46656e27ead1d90e708b20fce15d027f173fab9ffdff117bd473041cd90958aec82326e997efaf1b96112986d7f9e61c70c2256efe3b22cab419caf841a69357589d69c601599348354bf4675cf0a85ff7f43d9997312e29e45a0659bb37fb299e479fcdec3fd1a8520043dd94e8223197a03960baeeb6635472417c914315e039802e185a1de02441c05be8fc4c10e56105086b63918d6c7c60eeb6363877547db62a3bc4d3cddc06f798cd1277b3ab8db727723befffe3a7af11d025ce47ac3629c399bc8b2ae54efaa67792934a7cf97975a45e156f05242aebad99aedaeedeede52a62403400ee50dfa0eb623487c70c004ce661fc9e94467b3a7a521f961a2019ca5d5240ca10752b6f1b36f2dede7055080b2b5b8e2a88a0d095865ab2e2385c97d2489eb081503741248f0029ccd7e8d016cf6ed8924885a0c94584114487076cb119870b6fb5dab76083252b2776d2f12c6aa935e778219e248206a5d42aeb508b5cacafa87e19582c2518b04f39b08ef94328269d55753535252566dbb565c726aa16ebfa00695e65230a8b39e41eda8ae7597ec525dbc2ca1a4b25b79be5db255149cf0b2cb35084cc85e6d8b47ed692694547b9bbe842f5b09909e4992f742d0bdd049d3c3210e4cd4a79829d00bf9ec73154320d065e071f48aff1f4ecd30147a1af1fcfe3b871cbd0ff403d35f04a293bff799f7e99d5dcf3f8ed6c47751bcf9c7d1cbf3f3133e8851472f1a7edf87f2841ee879df1982de091ea8ca2e7ac3d0f3c865ac3c26a8f359ea8139307795634a0294f27dd54959b9ec6fc5ca94ecd67aaef6a45dfbebaf27eae80f4ff7f9e34d76c94cd1fc6351479dc7fefca03f55308ba83d3d3dfe3f9e437de6b4b675ae4e30d3f0aa88a51ad4be547d8aa83dfea3eb6c8d01c237df3c3d29213054186af6ac6a8f7b5d7c96a66e25e39ef2f8a9c7c551ef62b9641c08d62fdf26a75e944735e5681747fd5dd8abca99f56b76995e94a555294b33b2f97b53565bb2a75e14e99e7328eff11c06ccaba55a7320b287578e7aeeda931a74b34f5927661a5ebdc59e97634a3253d41261d6fece39a61b5e894896e6b92ab3342fca0bafbc290be6d0d03a810ad95d88ecee6e65998490c14fd11c545a3865657edfed0fb2db2264b74bc8855396165eb9cc3ffcc2aba9f02abb877382998239d00b78055ad51e0f99f214aa18448b4baa98735af8fe602e2abb37038f0b0fb27b37c8fe22113f1095783a88a27a3508a550833c1984575df3b4b48f7f109c6c41caeed920fba72210fd5e94cffab4ef7db5d6ec79298aa6e1edeab6e59985eb4f5938168e7554bfe65b6bad157bd9b272b9aef83e19253254fe25eddc76083395712b97e17f75e57ee528ea6d873059df99757aae663b8429da38ead65a6badb5d6e644d173ee61e7aa78f1daf47dff0782f65a2539f762656bb55fbfd3adbc50b9955ff917f7d204b732612ed5141eaf9e617be39d5ddb6d470f57cfd6dc5c8dd05d4cff7a866958b1648bc562c9f6eb2effb09eb5b61b08e8e70745ade779e72e5f1bf9c7bd9ee779a1cf52986c6152dcb6d6faf09993d65a4b86b5dafa83dc1e8f23cff7ec289b47e557083aaaa3dcfbc20f64b158e5df07c151243560ee706fd1ddc1511f4bd2b57ce148d280acac86d5ac32f52bfaea2c4132ebef65e1a29b6f47f52eca843d0dc82d6f297e4c817768b5583031d6a7f8919582c52ac73216fbf2c732f6acf2168b855e79de5ac2945e8a1bb252e0fbb1ee591343ab759261c882b1ee0b9b564c4fc127e21d82f82cc81d4911b7c0243fad5564df56ebbfd6dff2d66a3968037a0ac6f1ac5df30e3e13df1f88cfc6f70f5274f378d6c4c0e2e1e6fb290847ffce1d58600271d4adeb50f45f46c68176f0962aad9904deb0d65ab97855e94df2b86467ba48de4daa954b05c6bf5fc3fab1da214c2773f8acf5fe361081ad91f597c53ad3f14df25938bd99f5faf1b4ac4b03ccdf9ff9d7f8459e4d183cbec693848df89ae585fd785ef361a3dff11ba96be48f974566d61973c933ed568a928631c34a18547c0b4c6a2eaff27673a4f871acaf37f1ebadccbf84f97bc9677db3ba2aa76452d7609e84d5d04073a69e3a8c4c43d809037b180ca7373fcda7187f987fcd87f914e692cf3ac9b306e65f3ffe8fa5fd78cf1407eb2f0dacbff7c9f3ded7082b61a8f97bfeb80ffb57cdebc686ac21c99b33bdf9f5296cceb495f2abe695c299f278b19761602e0cec759aa6f9437626e655905f8a37ef3d491679599fe2ef99c2c633edd1922a70cd5eded3b434f1cbf3c7d2c47328f63267579fd15c4f9e30f26160cef4e60b033b7f90cf7a983385b9e4c37c2d6760dd7fc1dcb3e6f5e68f1fbb6fb2605e050d0be661e39b250cacff0173a6ed2a692033eb73388c8a33c5195364f37d18eb7e6c1cbfc78f95b771ac39bf706792c5f2f2c55e663d2bbf7065e148f1ac146fbe706af4434e3e4ec93cdaf246de3753c018c1a468a56cfe906dbd89c177e123d749e57e7535979c83353eeb5f258efb25f9e38f23ebba2e7971dc9165455c43bdd1394705727f481a0463a2188fa878ef9d451103a96788a454ffc54f6d7dfb6957a9a723d9d1cb5bf5af4121aeb536a4b36d2847fd53fb0d55a5ad4079dfb58add4121e5ca825c7725d8e5d14b01542d397c3bc4d9440b91edf3705a23b9cc3f74db4df6c9b6ba267e4a664b25dbf76aada9549f7ba6e109a4a3b27786675d62d6b4abd0200ff972ea48559490905ee0623d00380d3399cf2f0a6698f67b5f772580ca60f86d94eb90fdf07df7bf6757b1ed50aeab52e5892c561cc9955cca9521ccb487aaf8771524abc4547ac1964c360966fd29aaaa1c9f07b4f37ec63bbbd6baadf556f7bc7aad3675aff551d38618a8b2c42e572c4b34f1e48a85045e72356b1daa4e348ca08a656ccdc82fc2a94af5428d5cbff6a7b45cffa7d664689f4ae3616540e98a6c1fd75a6fa933f5a6ce5e6f65603e7d65cbca293ea66b6d05d6ae1a42b54fbffd01fae1b9112307ec6367ea399833750f10b54fd770e84f71a69ea36bb0f36582a20d4409c0a142b1a2bc71efbdf7de7befbdf7de7befbdf7de7befbdf71e3552bd2fdb1257df388bad3d53f0ec5d4389ee32475db35d6b2b00e85ae7daa76bfdd43edd357b3e91dbb3076a4fd720f76fc9fd2edc2ba8687f0b97cd82dc4fc62037142fc84d78a006b50a6a4f77adafb44fbf96ec442e6707b91f24bb84212707470738383739b8b9b199b2b1a9c1414d0dcd0d6868666c30332303858c8c8a1aa8501143839898d813b1188c140c4c8a19a4480193010cf68ac1eb656a314d170c5caed60b5aadd20565494691e4d8827164b180c5129d10c5304b18824d80e0c7c4f77958b2777b97ef5d410db2d6aaa0063954a7e046670b22f9cb56829da37dc5d1de9d0de5684d0cac472244667dfdc202331c712afef82979bff7c00fafbd81697f7cef26dcba9c297e15dfbe1432db6fffb106fc9a3fbfb93a855f43f1acf1d6133f3eb3b4f07f2aee0809d7501d7545cb4f0cdc910ccadac04cfb29b7cebbba4ad515ea04a369696d822aebc712ba20f73fd1a609557bda6c322dd0e50252505566698dbb27685b4c196b0d13f6f66f98150b0670d905738bf957fbb2dfd93f8bb68a54b803af22206486596b613058ad7e85d5b7150613dffe285a11068359d1dab6229daef88eedbf88d317fe9e608a5f21401e29791b15fb495ce54d14ada5023b7fd887518100e9e4ddeb97b42571ddf7b395305f2fe2f465f6ecbaa335d63bcb74346c3fc78a4d47c5eff2268a40c86c675e2634557c8c9731583c3a76bd88fd2d4e710ce2fb59c6e0ef3a7fd411a79d79f09c1ec9459e9dbeaf58141dd7c440925fb3ad89a1fce174bffc9a819019763a7d678d7fb5912eeb1a7f5b06aa7466da566d556764ce34cca6795aa98839ad62478861f2e8be92b7795f82a75c77b9ee9620421e9db4f2726515fa94dcde14923ec5ca9b7c967ace93b06a2fad14ef250ce9689ff60866eaba9691de6c45d1753e4bc5777dcb78fd10bfbe23591a10b5a5d0b326d499aba2fdfdb6df6f642e04ae020d14526426d4a016d93efd2b868882e8ba3e537fd7a7feb0af46d987dcd4f535387272fd9c5cdf75f5ac6697aeb33de7b3b43f57996971bb07ccc6595ad8542db655387a476a6eb09ef53506b83f787fbfdaea7b6f64f3701d5f2cccba72a6378bb58441c435372a16a72c597c2c4ebbdc968553a3cc12d2b96271ba922fae3140f883ad9644e0dfb3c6867ba446fc6af3ce1ada76bfdabe37b219d9c273c8ed10cc3434b19d32d3de49595a5865fd4d5565fd69ef9aaa2f4095d3f085c433b216c74afdcffe17c7f03e81eaf9837f8080f04ffdf407c867f5043a7f6238ea9dff07a80254dc16d1bacfe5a63099303a52bedbfdd65a6badb5d65a8ff4f0a77319fef6c84ccd244198ff7d498030edf7e7d4ace9afd97ee71ad767377d2f18477777777777f7cfbbd6bab5d6dddddd1d7477ef7eadb55aefb6351cab8ca3b7d7570c3fadf56d3dc1cfbbd67b14433bb692f9171b798b2d18ab4efa59d599f54145a7bc5c3da9ec6f7de03d51a1ec0080b0533ebb67cd06cc296ba96a90e5627f506b0d557dfcbf494667de5cf61f1b84f1533395f128f4a632d9a372d4dffb9a6bb75cc9fe1789c9699d25e76fb9f8ac6221c114bfdfbbab94fdb39e47f9acc6cc308e7a8d41762d8e84f93d599a9dfa7496e651764ac6f3a87c9686cefab186b67955accc9f704d0d6d13bfc600e2b39e758aa7d764651e1797f9933036323a3c39f5a8bcaf38edd359993fcaf33a3236264c986d11ccf48bb25566fae9b27ffa4565ffefe79e5fe7fa1e95a535d40f8afecbc898a6134c7fc1e92ea83dfea3b51ef81787f7867f311032b75785cec5bb7c5e3cca31604ef994a350b527c465ebc44c7fb2bbd75b1acaa12c959dba8159f37df57c763f1b475d06a97f5ac9a360507b5c507dfc1b7b5413b33fb553afe3a84dfaac5e5270aa2968c9154b125c320bb93221821364223508f62e92304cf2c5f2c67ab184c1338bacdf4bd49a8c0c54086acdc64604df3eee81984fdb04b9760a72fd76a20699ad447f20571d0d1c516b3aa8940ad41aca0325d7ef226a8da77dfcbbc94f2bf505728d926b3b69a41e22d76f216a2d46fbf87712206a0da86b135a0ab5fb41183fad564cc8b542a14621d74f6b1572fdea968c7baa0766cb744dc8a66b43dfb51774ba0680b7c9791cb46b32b44fd772b48fdbd03e31806e501b9e9a991b36db17ea1a8fa1aef9689faeedd03e40dac71f0bfd2ee74c1de794a16b417274ada87d88b4cf54fbf8df68b191aa8182e69cda32e4c6e2d135bfe2a36bbedba16b9e0548d73caaaf507996aa28ff205d732d455d73a9f6f1af39145df3a9f699ea9a9ba0faf86bb1723741d6c11813b8dc39a61ddd8e149fd1e09eacccea2a776aab526b3592586769a22ef7c398d50c5be38ff6c7a7d6393ed92fcff129f799da44d51efb2cdcb5c7be88435cfb234b18c26c86d97fa8ac88a48dc0f00789b1eaa42c72f5bd233ac64aea8252a014b8059c82e1267b3338c2f3ce18ab18ab8cb303a56a8f8353de8db102b7e47e0f9c02a51cc5d9ddf7ee89b3c3d981542c7249c6b1dd8158873165dababb5f6b43bf37f43c128ab7fa43981163d501a39e6afdf41b2f6e5b8853e42f392547242b6b6a75695965ffdbaacad1a2ca39ea9e57ab55eeaf1888b5ca5fe8c1f2775585cb9e1552f63ccf7bd2627ade077edff7ca61f88121088252366a973387dc11c95199e75d8fb457a699478f0cb12b731975d9471dabab6dc13047a5f226e6b287b57a5ef5c27a8e4a8efac8947d4472d4736d6511b3acacec7b82699f720a466507df62990b3ed1c027f029bb8f4839b553585f5c863febca5177e5aeb2bf4b8acfc2cff98cc9753eebf06944cafe212e338864af72385d0e53b02987e76dda92533b25bb7775aea6fe5234611c5d892b49cd5c6bedae5dabe7f5bd2018dafccbc0c8c0d8985d6bedaa83a26ff3363a32b5d65abbcdd39574aded4a5c49bf109cdce7cdd55875521955b2d7b6019f683bda2b26ceae0685ae29ae27476ff2f53e65b1846d72be670f55ed418119669c5ded71d793cf5250ca7a3d545372ff536e57932b0339f72b032c8b25f08994c552f6b4076797bd21dd4375effdd4d574032783382730db9ec04c5d4d6d6393bdd3d5547bfcc6d5943d5f57151955ac8c55878afd5a5343f9ac272a4fd61355fb35e147adca4636239b8bf74cfb087c163e62fde7c51452af72b5955fe25a5d84a7a3df596d602a93bd8c0146fede8666fdf5d9f72c6cde90882cb4e0077ed6ba6d6b6d2d5be3c983b58da07fe18b57c4b5fa781fe28cc3e67b5e2adf175d6ba9f050ad3d85d8fc799f57bdefbbc0585b307f2c8d75374d776bed597f4e30d61bfd2007bc64f0cbbf6389c791f5ac1f9f7502c0d121fc82a3b70593cbafaeaf8ca11f7cafc431fef8d765dffc71f4c6d11b0243cdb5af8796bca607f3c98036e18b3a2c74e4217fca18ad0c43cd408ede2f7265186a16ba5230d32ac5c3f5f7738c3a6a50cd8d311d42400d62fdbd48b2f823eb6f79135f9e287e8d2c72804b6e3d090507b2e4f249337b67da1f900cc2f0250dad73c88e3f46ef4cc7b3ff7b16f6ca770c04a6a63c6f944ffebdf7de3b8af8873ff92edc250ef24b277fc8c3ef49d6dfa83d60bb7ec4a9f7adeff286f7ac16c6acf24beffcca5a9e178b583860257b4f852db9e61abef7e1fff0eeec5a6fb7a33dc1d0b3a31d4f1b86ad4fc9af4a660d8ed7d7afa75126dffef8c2a6896fb57e2d6fd8ecc243baf68c5f347ec5e90ff0bd2f5dbe6cc9153c6b70b857debcd1bdbe2cf9ba515f2f5c83a3feeb5f675ae37dcd691be5b17ee98dcef5eb8dceafd739be896f636b2ccb1f9d7461f259cec29e2babf4ec8aa1a3f0ec9f90ce9f90f6568a24830fa6a11024d9fb11a7ac07bfc43fc81be4f835b2f7e378fe08fa7b21f63faf0d1dbd61bf55ccd25873787f53249072e7ce588eb83aa22a7f34787fcfd34397f9ebd825f89beefed77a97f67d7d19cfd90f1f0982c3e17036afbc796fed7f58c9dba8f4bb131b6869c193f1bec04c7f7e4cf0de5de760ac3aee2ca89ee7e0e749c06180eb42762d6c1ffb5f48f39dfede8fee7fcf5bc45875b298f6ac325d23a9989ee95d26a018258e9d6c86f776770ca0f000978ce6ca840798727d7da3ab5f03a6fdfac3fb2224ccebb3b4060971dd0eeb0cdf835edf8ec27d7b4bb206d59cdab77fbdefb4e50cf62f0f6de47543b51266bde0f7fd1f5dbdd04b32ebd75df6bc33fdbea845af6b4fdb7ccf113c41e5d167c8150b0932837f9f46fe3ca4d10fefc9ecffc3c553bd4f9e2ae38d91af9873f456efd7d78ced3bd3ae62cf6a7b9775392698e4930cde6738434eeeb375fe6dbffb610d7e83dff6c57426bfaa8c2473a57595f97712665bb517a7e5a890ee4dc2d1dbe46456dbb5cbb264959fba2e27c5674e811af4fdfdfedafbde2a67e8cf3f6f91adabddb7764d29c9912c827de74ed8b7877797bf33c443c2a2ce20e8ead3afec5c1f86a6e9fa54e6b2de7596c63adbcacaee87a169cac8b455edb9bf04d3fcb4ada4d420fbb7d614f5d54de5a1fd21ef299fddefcb1fb2430e56189c02bfce21dee7c2433aa7aeebe2b3dbe47f3b177ad93a9fe96030d8d9ba10d7dc2f93657ada3926476feb1cbd2126bf96ee6ad7e5fb7edfa5987fbf99746368ad7562fd0ffb45edf8c787a31f14e2cae1b3ca19fa41219dad6d11e40b759d5a4f6b9dd5654f32df9ba90cd9495c187c49bfb2be50e42b9e66edb16dfbcb80197ed6eb4bfacc3a954753c5c411520eca9589245a720d1ad25f6b8f91fbf5bc7b3d9d7ced5b9edae33b21ae967cc938b932b1644bd6e57e0f29e5b919fcb16b8f4ef6f0839f38b3c94532b9f545379750d97edab99f40f79e303b7f0bd7dad3faf24710ebe4108fffe11ae29074146ca5fdf7c9af89813584879b6bedb95f4f7b7a390378ced09ff7e10cde8367dfff7ef47bff7dfae1cf3b3d2690bc274cfbcde368fda2ce4db6064c21aedcb5a73ba4c1e82bc81230c265ff7bbfefbbdff7a537fce1d5dcf0dedf28a743d96b4fadc151237b8dece7f5ea5eabcfbeefda75ec28f2c5291220f2f57ca3c8e5ed5aef9a1b7ed6e0a891ebd7c85164f7bc5cc9f6ef8fae4ba373d67286fbf60c53a35cd3eac5bcfde51ea101a6cc42ae5890a8ca9ffff9c3cb9b7defeda7457efeb8ef3378270c45edb3dbb66fd8b7a1a3357c7ba6feee7e1e61034f7945ae583670944757ff58a56c54becb6371884978c9250ddf8b6775d4f14ffd528d8e46fb0133d5c9fe0133bd1948fdf2e368f7155aebd3ba0417cda7f507b93e510d6a1a7ce4886da8d386f005e3e8fdbe37bef70f1c657f99f78097fcf913c5ef3d3725dff7803956db01d36ec0748f32bfdfb538cdf5dd54a8473914b9dfb17d32fd6b7632492f9d5c3fe49603a6f9a9d54149f159cd14a841f77f8867daa96d6a9533f8dfdb227de7b3761709c3ec2cae2b666d4851f80e55d28024cbbcffe827898b3a93e387c91feff7664903cedf69af385aedcb6ff5566ae4d4b23a4b6b9d61689aba1b367bd406ccb4f30f982feacc4ac24c6b55fd527c9a93af8ce956d66ac01c913053abcbfdb6c9673fc467d52015df6f295083b259e2109f058a1f7b15b833ac9c417cd6d7f2c6436798b3a16278264bd4e95135c83f2b7fd8ebab2d533c0f611662ab80cbb60659f361d8ca2acd49c5189cde5d367f6c25ccd47f74dff9cc65644e5c73bfcab01cdb8dceef1d2a75281fb2ed954798a943595dd71c88dc6fa538945ff1dd78a636f380c3461145fe9c5cff83fca2bef887fff7fee387e50cfe1fc94367f2c71147b5bebff23254617badfb43be031016ee9d8da3a5d559b917bfea5a73699f171eb338daac1c32385a557cda22fd3c8eb171647df9fd360d75badcbf83a5ed542d2d725aab9c781ad9df6cff58fbc344595aedd03c53a32ad9ff154512d5a03ab34d66da5057cc6e2ffd25f7d72cad4f4d13d49a0f14a4d54bed92562edd5369457d8bd364705937972ee2a739d95f45a50d595905eb8c74555aeb63efac4f3dfb6983a3f563b8561fd69936eb434bc3a1cafc0190e24c4dd86b4459b8d61eff9b5ea02a54d12a5465fe16bf6056595869323ebad6d5c7ff47067dfb2597279719fc3f9b0173fc1f427e08d59d5194dfa83a290ff81587601a05f811e59d1c99247f87ef47f2472ce4fbc4f7efc92f6dc87ea640aa54fe51b391bf1016ebfb4ef24cef0fbb4f7c2f59effdd86978a65dc1cff57aa78edde5bac28dcacbc2152db4370592c51fbfaf8e4fdeece58f977cf28867f13b6b0d3fd957cb0018e5afc471bfcfa1103a2a031e260dc6d145422106416281620624962186f014b9eea0e0e2d6dddd1d0032bc100a01e0060016b887d6cdf75df85f6efc5a6128610e0179a92272a4bf680720404247beeffb3e0ff4f011978138daf869f8b1fe03df718eb6c0043fad57471c6d1e3c7c8447e349e57bb1c4f1b1f0f704f3c3f1f9103fd657e2f84ed2070f1fdfc9c347f6fffe45d1fd4e1e6ef601633ada95ca06990c009fa532a8a511e9ef9f5c8180720ebaa05ae5a8cafe36d42de3dfdcfaaa430639828bca22b2897238da720a67399c5c3f5a6b6320bd2dfcd1c3dfdb1f229fb5ad9888c85133dfeebe16fc91e8ceae6d2227b2e19637f159f8263e91cffa5b6ca26b0391e7202272d4df06475d14c5d3c6d12a95233cc3f7208e7acd7896678d28be9f452edb2561be5566e10ba3334d234c900ccd1c9616c4b4d50ae77ada99322ec3df3f60a6285a81a26cff0892f7a4d2df65ade78ffe8aa3811c2d71f40b7de5cdda6beb2964bd556b8f053a532021182f77f4fdb9bd1f149d92fd796274bf3d7f78258ebe8f7e7fd73ec9d444511e98db81f06d124cffd0bd2f4cc5bf677534fc9cf02714ad98be288ae00f58cfeaa8d4a3c290064cd99f8cd2b205c6e6b4484171c5770ee5a8dba3d0caaa6aadf5b157397a7adda1cac6ff7e1cb145b238478f1c758be391eddb7c3bd73ed3cff6742b15985539f52b2fd95dc9bd7494a3956671321930c55cb130b140e851b60833eda8a86b6371563644484791cb2f0a73ebd39b4332b75923bf929fdadc7d8e2676fd88bdfc3dc1747dae1f9b2c5dae9afbde3cba0d3f3b62cfaef1c91f451739822e5c5f6f3e89871499997cd78fac6ae612570f475cb3896b45abc94398cbb356d2faa442540b93fb4cede32f37ebed159fb14e08e86428f1b613f80dded7526bf5428ff29e8ad222d504d3e22cceed91cfdae690b49167634bc6b18ea3f5eabddec5a9ad46b7ad134892a471b4b3755baffd2e295b752bd505e723049dcd9e546a7074b6b02134059e7324c779268c86d5d5955515d52e4a6a6a4a2a6ad7f454050aaaca5353e31aa973ad6b5de71aa971343c134655e5995f84afacaaa862343c332646c3f3ce20b8c70dad0bebf1b1f7743c4a84e6fea9f1931b4808904c910c8d333583e06a651df33dba86228b3461649ef452e7623411448a18f1e1fd0120430ea21e382082f408da4a93a0972801d4bb09defb04ef286825fa4aa74005bd6b2ccd4437f19ea59d786741b7a0a356d02e787f010c3a062d839e414bf5134d83f71a341436682d37681cf454eba077d05b9a07dd83f64153f50fde81d03978ef203497f7170211aa8a6084237469242441093d04ab253413bc38e109ef0f85ab2848610a5f9a40852a44c1023cf463db00b707bea1c00d821f3b073813d1c4c9d2932298a044516292d4363f8299daa820b83b70d4f446b82bf048705be06fc0b7ff010bfd11dc48ef036e23de08ee23be086e249e08ee255f84ddd683892857b93211a52aa7f608b932c1f4258722ab6bade43d09d0d3fec6ecfd757c26e3fd514bbb7d0c18ef0f3454837cde3fd7a016dedfd6b59ef77fc1673cef0f009fb1f02b64d97b10efcf43881ee21510d4c3fb17a94132ef6fa46b30ef7fc467e27b03f1deb8f76fa37724efdfba1fde8fbc1351837c78ff16410d32f2fead44d78a1079fff6ef2b435ffb59d0fbb7d48cf717fe5d55835cbc7f17a106fdbc7f1ba16be87b0b16bff3fe7de5389fe9bcb707dedf57bc9fefef3a4bf3d15483f0fbfb1135e8dfdf91e85acefb7b123ec3f99bf7f7293eb3f91d5ff3fe7ec56734afe3eb8ca511d520cf410d6a275df32d4da5464311f2fe4a489656d44aba0f33bd20346237c420097d1b815401a7172c20f4d788a6a3f7ca01b4cd855e62496c9fa83d34681f206c69d8dec076866d95ed6263600b035b1bbe3ef822d51eff16f05d527bfc7bf04d6a1f7f1e7c59c077057c33be327c2780af027ac05706460402df281cbe5a0e80af11be48f02500be3fe07b84081f8c14c1570aede34f04df2f2fece16ad80bca616fc68b225c60af0aea41b9a703f674b09703f6569c3ef063af083837363b6a68f0a7037f4833444d9c2c3d61aa01258a1025fc4531c19f1647a592f09783dae31f047f3d30d8c29f0eb23f6c4bf637a9b2bfc725fbd3b0fabc64ff1bbe2b107794fd08b8a97c4751052a7c998214a27005852738c10b11f0d75485bf238a803f248c80bf24bae06f0a12f0572509f8835202feae0c017fbb25e04f0b13f02715dada0a3bd9be9bc084255829210948e872042314a18a083ac05ed50eb057842dd833020fb0d7a507d8b3f201f6bc5061efea07f8c301017f4741c05f8e0bfe9884803f1d68eb2108814b1080f0032a1ff480075b76a0831860af8a0cb0278219604f0929ec5979027b5034c0de951a606fa7057b5237c0de140eb0b7650a7b549fad73c0227b0a0737b0011435a0c113523390410c76f85e61c1370a4ce02b8526f0fd92057b3827b077d402ece5a2b0c7b402ece95c80bda617602f091860efc9b3b51692ec2bb843f00d8317b820aa052c70224b134c60d9f508f0dd59c1170b09f06562097cb39400df28287cb598005fa913e03b85027cb728812f550af0ed82afd5b5f50a5480bb03df29c07df47d0577d3370a7057e0fb04b82df06d027cfb86c242df25c08df4bd046e23be4980fb886f2bb891f81e01ee254308f13df004de0877108f047795bf012be07fc03de58fe016c1fb80fb036f0477922f825bc913c1f783e00b656dadc40bafe46d2efcc80a5b4e36fe08b38e9c88003b09f9ae829d6a7c0b819d88be91b0535fe01b0a76daf1fd043bf9f80e017622c03708b0930edf54b0d301be9bf41421df41d4f81e82e8852ef01d45c737d38eef251fdf1fc8e1db033a7c77001fb1905c867f9220dd816f269df4ade4be0b1849c6e6c2b712e6c1cc9d743ae9f84e827930f33d9d72f81b3e4ae2c280947f011f25e9286c543a895712da5c38935410c9d278b0a1c35ea4fb74a32c8d86f7bf551e92a50dbdbff76469395e87f71adeff43b2b4fcfedfd3176569b6f7ffaaaaf835458b3efd4981847c5aa314f06995aaf1699d4a6b555aadd27a953652dab9b475fd947695b4a13a2a6da9b4a7d2ae6ac0a76db5c3a77de5804f1d29fdd4733f3e759d0d9ffa138f4fbd0a54013ef528037cea52387cea5308f8d4ab12f0a95b2de053bf62c0a71609029fda5c043eb5ba219fda27097c6aab58281bb607c09eb1d780bdc973e0a6c24d00dc34e0c64d20073ce463870e1eb0dd0d89000418b0800420008701f0b0e147ea801d1a0023e6f1398bc9b8c17181a88602843411459deb2270b5bd0483b92f70bf8d6823928c48fa2f9f4bb097bb02b89d92c04efd747363c0cc48a1ad9fbea5e021f78520c1e596f2635b00fb133e2a7a256f2b3aa974d2277d33f9766aa7a4a41ff276c29ddb02b87333f9924e0bc8386a81bb010e6ce0c73662031c680e6c60031c0037709a46f4126cf3e8916d01ec54f49d047e298d81ef0a9c321ac047543ae9938051441185ad35f02c9ccfbaa933e0b3cec0fb3712b86bcffdbe000eadec7e6300572b3b02d7f6499760ef246ce270b8241ce270b89c81a61f59e5ad9bce176667a033f0689881f3c7d19b541d4dc232381c2e8f1fd94b96603137063095d67d5f001f51690abceec79682ad14ec52704bc135c3e09bdb02b82b808dc09e11f81a81ad11d8735300878eeabe93be29f0a3abbc3505ce1766eb743fe44d01dcb98dc09d3b09873218e5d69da6a3d7085cb3a346e02f8f5d857e090e97e09ac7916495f7fc9c30697c7f10dc447c0381bbc9370eb793ef813bf746b8971e09ee277f03ee22fe07dc4c7f043794f701779437825be98be066f2447027bd104e3bd358d248df3d2c692542bc1de2c74bdab2917af84e12c47b473fde6f6c09e928987cd651dedfea7cd650de9be9fd6d93a57511ffa4972cad73efede4fded159f7593f7b73b4b6b22dedf3ee95a2bbdbf5daa41cde4fd6dae0675d2fbbb5359da8f77171026eed3301b7d6a6693458baf96268047df719696f6aea92c4dc80f9f9a3906494c4c48153bf26998633b3a2bbe5a1ad1ff7cb5341defe25f9cf89f88a5f5b0f1433ebc7056e6463e3573112a2b732242395f2ded85c7b9b1a9a1f96a693e7ec6574bcbe183be5a9a0e5f7b3f82e9df4bb08d42c256cb10d84e29c1768b10d8521d61cb2509b6554160dbc551ff70e7df40ec32d8647146670a4259997f88e44cc31fced404a58e9c2948e5c39986a09595f99b46ced42c72a62191333585ce74b481bf1b6ef82180193e1c6578d106f68204618561c862d9c0f70007f0f43d8600fc88c306b60a50000f215296e680bff1230c2f02c006f61e3d5e68dd7678175e1ca3c2f1478fb481db76b15351d1db28d034411b258a55749ac004de46594b6bc00f7d8ae61fbf5a9acd06066d173b11e07b20c083394b0b9baccc3fdc41599a03797f6b65690f787f1b6569367c4eca04a16ab0513968f8f14129ff10c91fa4b234b1c9caac2c4ddc85612b447aa29232a340ab506c39d560e5b0c9d220f0fee193a50de0fdc32a964680f70fa12ccd86f70f77961681f70fa32cad00ef1f4a599a01fe86f70fa92c2dc8fb87559646f4fea195a5e9d8f1fe22ced2841ce01d87f71775968680f7179b2c6dc8fb8b4f9696807705bcf3787f71676912787f31cad216f0fea294a531e0fdc5294bebf1fe2295a515bdbf5865693ede5fb4b2b41cde5fbcb2341d2cadc6fbb3902c6d02efcfca591a0fefcfd2591a0f7802b8084b00f318825b086eaa20d89b22801d02d8e21e80ed0e08b64d3fb053a5d81d8077c08db332ff06e88073c0d5ca82de07ee8119b0001eb85ad9ec158013801180ab95c5781c70b532187f00bc03ebc044b85ad9cfdf800d800b806dc0d5ca7a9e0078005f03ce8169c002c033601970b532161f030e00c681ab95ad78006018fe06ae56f67fc32fe06a6538ef02b6e18c87b00d5c6d3570b532bf2a626230297efcc8506ccd7c2d854e1a58e887ac10ee5cc34138ad52698d4aabd50ccbc031300cfc02a70e957a151f9cba540f4ead2e5ba4d43ea5b60a8a5be0d4ad52bfcafe3b5807a79e4b5d8771da52694f21754e87a63f3338b5b9d4a762ba4a5aaf70da4dd977d97143caa6ec4fb2422a113724cca94835c4cc290bd73935ca4278e41faca6b3ae40d2fef945e246620c92194642e877a582b1eaa433566dc99085ed16500a940262bd22986e7e0d9b1ac706ad05a168a547b2f8a08841a9ef03cb1b370b21f3180542e1a8b3a66c06bbeb90bb725135883616cc95834d617107a59eba5a07c10f6ca546593c419b3fcf76ea7e6aa7f297d3fb3320d35875d214bbecb5fb034201d37bef882b230b938e7adeed8d278b75a640ac551e5f4c323bb39ef59d59b8b377dff03e3da2cb1fe935a298d6641f6168e50e3f05a3004621a9cc7d8e55b5670933754d8d55aea9d18e55d96f7c279962d721105419f6749f72bdb98b34465959d76aa966349f5a2e339fda2a9acca7d6aa8666aeafc7ba31cad2605351611ea370786b8bd6de2fe6297baef9abcbe67b12339db11a806853f1636d2b1e2e1df32ffb3438f1664f2751fcab834d591ab8bb53760adc02ee2c0db6b3b2fe7037c23098613438940ae5825a59197a5583db664eb4caca5a8693e5723f2cddc9c56431531e2aace342ca4a972b314df32a836f963710a9ab105ebd78a87257c14c3158c67e144918fe720c76b2d1c243857532df99c23b4c8e4a45c9743b13ea46ea46ea068a9b291e2a4bb3a1c1f150d520da6c6cb02ecb90eb331609c31e599665d982e19b6968aa3ce5fe1432fdd5c2683e854df150c9a4b8acdf2c6fb11f5fafd7bfcadb4bd6248b61994ec54b850a0c53a142a686323232310fc3b68a95dc3f2363698e5cd6270dced126cc7427e77d4c18133396640c77861d5181c59ee689a689664a956a652a687056d66f9a3c545b727facbcc17e8c89897d4c798bc5c4bcb5ef504cd8c76020a0d48f0f6058a69349913531f1e30397e9f27e7c5083ea8d544c59836813dfe29a9d9c28d3c53cc974352a6a6a66ac66ae7ce6fd1d674818e64c79b3a2a9a46dc1306b9ea6bc01a1a9cafd8a796badb53136168bb131fb43d6d6fcab86878ba330dc39863bd740e57e9a9f2bb97f06ef78bae4fe1436c543856f74b91fb6b3b498a7348cc176b9657ec8776ea472bf0cdec95959bf0abca3b3b29d1d54ee8f79daa992fb63313b4ffd164b544ac343857524d6ede476260d8ee68806c967df954cca74b95fa69335f9ccfb8e3d787bc5c4bcc5a91b61dad8c7589e2a479b87cb8d06577bfa6398876a0566ba93cbfd290f55eedf69f2594a134b37365fff851a1a1383f9cb3d4cd3849922699af7bd997c0e0e76bae1c771bcf61302c66814c5680869314663acac91d5327f88a041adc79ec4942b3bcfa2e5c606db2795498f196bc5d6c076676539a83d3a689f2d5f968460a7b23217ae9345cd9960425e9190108d1a356cd818ca34683499f136ca1b7e1b43e54de887d033c75ca2597ac1c286a6c50e8ef7433e03058a8b15333f3a37345058eb821e4589c938bab531211913438e24ab6576f91939a7e7494cb9e23bcfe25a7caaf66c71d4a9b87871f48bc54579d23e96c98abd62774d309ab36951b32206b33adb9469d0807d8df2d64c5ebc8df286f33686ca5b33fd502e6f2b3edbc4eef7f2d672713a8162eee09998a4efa49bb751fea242d7bdbc5d1b1a3aef95b79cf79a94372265ad3d313f7ae00541eb0d8262abc3c63607c3dda5bd388af3232526137b128efa14bfe2bb2caec5a77c4b52142ffec5e22c0d6cad143bc5516b25075b1c162d273b4b37b79fd8e8e46255b5f4cfc9a1f92f6f373c8fcb5b67791c74f36779dbf973c50a1daba3e35d7574c496ff8f98ec13b5e40a1df2c67082080bf22bdbc9c330618845a2e8b8f8d719a205462e9e49ab856bc58a1649a2645c2b5a42f4c028c160cf82c50bd20532232c600fc36c25325688c5fee7e715fb16626f832d690364c95bdef05f5a0f3f8c30dc4db6488f6c164946a1d1427913fa167cca5b3b69f13fe50dc9ffb8286f42ef8289c5bf286fff2f2e0c0b0386778501a385f216fb5abbddddda7bcbdbcedf56faf216e5c50b18e5ad25f431cadbfd18505a2fa3bc7512932634b115f33060c4286ffde45f4679bb2f6336a3d1802c1090770502125bfecd44e9dba3bc8fd696b7d68cd25ffc5e79bb792fc9dabfe347822ede2b6fadf7beefc98fe1c3307f1e2c6f3a0f36938fb920818072727e4650508e9050cccb5841e805c4cfcfab84d24203f133fa8047806a139fe2ebdb27b5167e7dcb546738ffe4535b65f6c3a7d6ca8cf6a9859af5f0a9bd92ebceea72f8d4365911d811e4146161c287d9129a9b9d209e1a0b484e94488c2230c4d6955d47b59696eaa34f7b6ac70a974c27f9d48f1cc99584e8616104caaff82e8b12193ee55b9cca53afb2722f7ee55f2cce1e11e0538bf4b1769d822b8d82af2768a82fc11224b0f22350d104ee3cb6c8b2f4bee57299a6399ae4eb35c260b0f1befdf7ae0f6384050b7286cc080b25325668a209fce51c9a68a2096c730e393c69a2091ce61c72c821c9063714283fa62061a5f7291a0626168b758c8c8969152a54acb02b5678d715dd72ad5861cbdb8c8c6b450bf15e79eb8101411004892002c49e9b8826e2c79004979ac035cf70c18f32a48ad27b99d8cc0c0d0d0d0d5953debcafc9b1216d6c7c88ddecd0d0dcf004edd0b080a476a2670c22280cb148ebb8f8d719a289918b3fc2b566e076c133cd44fd7843da94dedfc070707272726039e43f0c632cb332997795c17668686e788276685840523bd1330611148658640677d4b713274e949cb4e0c793c4a5f7e70a72850ea9a3d3d22177765a2c58b4782c0f8f77e569ddecd0d0dcf004edb080a4d6021f3e7cf8683171e2bd6721cbf9162b34138f9637f3512cdf5164e5cd7b5947e1215bb35f61858ee2637d7cbcab4fcb091d3a74e8b8597e64816c756e2759b030b1c33905d8f6820c38601e3e8010e92bb848a3001b39d1184560887d02dc228072e1a3b54081b4a3044bac70c91cb9121309acb03042ba6005194b2350f1837d8b537989dd8b7fb1473687ed52ede90fb17d522d9395629bacac1f08db29d68a3d00b63b560e26798258d041c4091da316f451540b2173a816a010c022e57e1babeba741cd6666cc08aad584847068d0701dd9d9a12d39b2336ba107a1574f7da51ac4d7771dd45a0fb1a5ce6e983ef5ab2f3c9f5a1c1048543b3ef5aa284c21850a9e9f999c153f3230173045aeea175c1f21e5a204c97e20baaf831a81b1e4838fe8e46c6124e3533f42120369e7c511af0275a587590b5dc41150f8b1c07224957c4dd242283935113fe69038a5f739ff18e31193e739ae58b1a284a15bb02db4e05d5b3082be8fe8c36d513bcf16329a189d319090385152c25fde81ab4d494909dbbc63079312f6bc6387120ef38e1d3b9294686c7e1462c2e4471d7245e9bd4eefecb06051decc67918205d9a2bc79df02072551d4d55166b0a8910535fa0f63c908fa3ea20f37278acee19cd013274d96f85146a2a5f7b2d80a2bb0c0020b3116481e9e584f4f4f0c1b2386778d11fb59310353c4450cb3a8a9c15166b0a8f921c88913274e9ae05ee2db891327509cdc2512fcd802d9537adf820fe9f343c23055fc4f7933ff27c50fe9a2bc79ef02e705f9e2058c666934ef4a8b32339323f3330353c44593e0efbd4a51acfcf88284517aff4206cec398a948f1777661982cfcacbc35ee6737c8060579d7a09a6f14dc5674e8d0a1a35b77043fd2ee089c6e78154182040912244851519020e137835f90018711601e3e80b0406ce488a3b83e0a92fda0548e8fc2f04184422a398d642071266faa3d7e84103b47684c7ac09ec5512d3e1504f61c0c815d07d5a7ff06fb16a7722e8e7af12f16678f8eb0c86a9227e70746c70c2b443a4648e0d342875e22c613f62533a9dc2f8cc4429016e5cde66b424236356ae8d47456b8476ab2253e2cbd90f9d4a13e9b506de1eb3b12b5d6f3f53d893aabf928333ef52c1e65e453d792ba943f117dea55b03411a4837021166972f33b46301648829a266705104c7e1a47a48573b68842fab4b5bc7af86109ebd47438b202922b796e49fdd4753c2c1c013d6d2623c657183e5f7f1472ea227eec21794aef7b5a68c1c7a7bc99efe343fefc8c2e5cb818ef0c3b6386779d41e4a7cf16383827d213570b9c1e7e78b58484f09789848484b0cd44442029843d13e13124857098898888949cccfcd84b2f4817a5f72f1a068c183162740c52868c9ecd66425648c8bb0a3592a058ce8a6ff7f29603041374050d8ec84f2f2d2d2d2d619b75d061097bd6418725a6255cb3d013253fd2c859e93d2d06043463c68cd80c32282856abd56236ac0d1bded5468dddfc8d116ce79b200962d16ab55a4f702bf956ab95e44721b2567a2f4483460d1286d941c06043e5adc80fc170e75827f9ee308fb116e2471b24ac730b01d688f9f710d8e986ef208a8a8a8a8a8a8204297a6f14c5239c81b0adbe20030e42b008a2cd68725630b514223262e0b4407afdc004c3a7cb91157c60f2a616b01fd1831d094fc2a7f815df7916d7e2683f0f0b362e8adcec20d9817692d3d54a7ad0d1e1ca6cb8ae9fa84a6e98fe2e424a2c7a51ab3511a241a3468d26366c0cd9a121ef3a6464068b7b83bf6b6e7e7081ff1e71bd68263a45ea43f9b4ab7205e2ebb7116aad715deaac9dcc567cea4a443ef59c33b9ceaae8d3bef225cea44910d90ae70b9db1c9a97d508b2fb8b491723e7cda4c492c8cccd0917695b6d25069ef5a4bda544648be917af8ba44e6614428b87347c14e3afc0844d24aef8166cc080a2a6fe6070591b5f2e67d4d48a8bca12f54c2706bd81a35bc6b8d582dc76629a8c5e30809f990b483236464068b0b050afe7211142850b0cd454550b0e7a2222838cc4545450f05d7ccf4e4c7662244c668d41a35c818ae79b441c66cacf0e39039241b2263998c35c964ecf5a38d8ca13632d6f9da2c2dd5726c96825a3c1326f8cbe3259b09f69830c136e7900313ec39871c98e03043799e1f4bb255debc6fb95ce58d86f9660c77e9fdebf5b0f2863e2c76b3b79b77bdc5787ec78e1d3ba230618105d90a4444444444444182906423e1dc03b6d51764a8611c58c03c7c007194c81219211a1c1f581899010546acc16d25871f5eb85c4655ede922d49e3642f5e92e8e7af122d88f5cc9d1fe1fec4c584482594da23339413b767874dcb8801de9ab7e22fd3abc8882d084849668d4a861c306bae2669bb377cd2cde263643d3a4c97b799b9901c3344d5eec9c17c7c5a71d65ab5fd3ae92ebebebb712b5e6fafa6da5cefac9d759cea70dc5cfa73da5f3696f51fab4a9a072eddd1335583af2c30dba2205cabb9aa546d5b4681b167f3543a303cf809d2f76faa916e483912f42e46be756c24e3e7e64d1f871ac24399634ca1f7e6c99477ec8c716e9fde8ea251826fa26d9e38df976c2c4e4a445d730b1789bd88349f8cb413a097b49f826619b8304e924ec39081ec31c24489067f251a06418ecc7b22c5b240cb397be55de68fc0fef2a6fe6bb4a183a1fa1f16679f3de5c42ff55de56fcab611606f3aeb0b015f39daf976fb659074e921305a843c78f2179719895bee64ec23523f9b17db41723f9580c1c45f1c80f3d76ecd8b163c78e1d3b76bc37ba5cac56587b3a866bf6016723d8c624d40289cd2b1521828d549f2333b871337033bdc0bd833b892ab5a7ff855b04b54789ead3561ced2b8ebac0ad25073714bdc5076e2a2bebaf056156fb902b9cd4a861a103c388fa29574771a27ea6d01d33ca1b0d1a4c356cd8181acad9666d36ef6a7bb20405ca936fb2f417ca8f9e57de6cde83cd9c9fb6ad5e05b1f8fa466a6de7eb1fa9b38ef2356d5d4bc19f7653ae9d443f35ae919a026d01a61f174e706ad29aa52e4935f92b946323244667c5a32dbefe18d3f1638d1a356c9030cc661a2261982e7e7ecc24d38f36128689f3b6f256e324e6633d62d88bb5622c56ab936ec047549a89d28f637f356ad4a831921564b5fc7f5cc41011c5c4e0cfbb644c0c11d147c6101111bd37c2c010168b415b605bf57941068c83f3b0c14088d49e7e16b848ede9dfc146aacf11267d74e2666a291877d30dee2474566056fb908ed6f49328a91c9731d3391dfd8443542454de626758a386d2db286f34707ea884a1f30d8dcfe5cdfbace4e46de5ad9fbc2dca8fd705eb820bded585b015fb232a0de595bc8d8ad0a775aa52317d5ab934e9a56aa544e3e6d3c741531e27fd2405aa55aafea00621d7209baf6fabb59aafff429d35938fd1ccc07ac0603d7a9030ecb9071ec1c64e357e0c49180e738f1e3d7e64892c562be66fc047545a497f8333c670070112d2516433c6f3138346cb3c26a97563c81806f3079231ec85640cdf3c8a640cdb9c2858640c7b51d18f2319c361a6819d84bc37cad49194f1af19866bb6a9c1b6eaf382a332380ecd847d2c6120334a988c8279fa09be01bf95f56828b83ada4d6ae07682ab0b298ab164cc25633e320624631cf310e6d88f2019239231ac3ab25a4361ee249c6b4f7f0bdbf00b3cdc0706e2a8679556a96ca36a0c1eaa3dfd9f3efae3f5b8e15399984f59c8b506d55a33f9fa649d75d2d7e7a931aaacd662624fe3eb8fa363307ff9065c6da3bd79f46ec04972a2f86ec037fc088e20e922ab25c495633ecc8d452698958449fc56d6dfc23c3546457b3ca01b686059f5e98fe1fe2e63ceb0f6b48ead5fc32398eeefed9a9eaa404155796ab2388b64735667753667912c8eaacaeaeacaaa8aca84ddd60ee65f84a7a4a2764d4f55a0a0aa3c799367c2ae7fcc914cd71561d705acf7a8d48a986b733dc91822110000000400c314002030180c88c4a2d180385265c90714000d95be6064248fa320c721840c4184100018000000000000040620d0026dfc17e3ba7e998cfc5bb0d9206f2d1ba68892286bf7cc1e0f614896aa12fab3d175ec582dcc987aa0750e3f671defc7a92b7340428402c25885844c40c90597db4fa43e2030021bc5e7c63ab4556b81a001f222936bab07729ae185997b723d55614a1a76b84076e9c9b0aaf5672361ec58597606512e2dae8cf5c99bed5a730ae3613ce4b54adbf64382e158722d6dbdf01d9fdcc9fa5d580ab102ff839a04a55c87215d1ccf6a6582fc756b8cabdec363840e0d03d142ef6109d3f565f650b0b410226b9073d31379ae4ce4ee3ff148d6280d36d2555c57bb2e0589f469bcdae362818337058c5a38e942df8d071a0141ebf545e5ed3a94efa877418a952655b3c4cecba71a51c27850d0718d2f87b3ef6817175728a3c99065e00a77c02a10e6687c5ba66ba241bc9ddcf43930b1538a54b4622e7aa6c1141daf4bdc5005e21256b6fb3fba25dace6890b55b3ffb564e59ecb257d1d61327f5d7ac13635d6eda2cf476559b359f501f71d2c4fc7d34917d2eccfafd01adb67ebf6aa06d73c5c058c4d76759653eac00d5b2e75f47fa14e68a67693a4704bdf1491f238dbc0e8131368b1c9e46c737515c24919d5373ef04434373590b63f018e3f2d67fb042689ab0e20f89b7514ca97a6eca852c86001f677200c163a98c2ca6db272f7b354666d70760a1dde29da401496ee6f6dd9479c23e84083e2a31f7c833b3f108802b0350602ae7486ae8cb902b17e0ea0509de43c2504e33f2ab1dc923075ff83792dc4412df10ac557fec578517dacab7de705671303db21b000b2a391ca313f1b8aea0a97053e885d8229241a1a12df298114200a615f4834cd0d8b78e1b29dcc9e8787bfd52250efa6a387d1814ed1a68b64070531158ac484c1f89bc6d3f84b96cecf666c4878604147f8de23de33114dfc6c5247201a0df453f7771b3f25de8d18a17e9b1dd92668aeca6614199c029ad0373d48d5225f6eddf1fbd3e51ec4f5b4913815af1c5ca3eaca559a1184cbf79b4433cf7cce57ff7b570f434f94f7e3fe37fb59e44ec4715cfaed9ad925de2c500e137682fc06a4241588629a48f4aefb4e8660f67487a1d1e423737487be8e77a76230acb8ddc869556948e54a99326538981d0a7fa6add4b237c7fbd465244750b4b5c72ca8151eafa3b6cdd2fcf69c7330814e48b4834aa9ed4d2b978ea9299e1ca93bec0247d92fc123a84a12b78888f1779e8f1e1d08856879649874f0c952de8c5b7c8b4fa8d58455b89d9f733ca4dafd614ea87e95a59c2be22e1f5828c455818e606f0855a5d57e6a2af2e8e6182c3f959ce3725e698621d78375a38dfaa06f72c9b748acb4b27272619dc02d395463139f8b0f94e04293e3d4cee0533aaa78552cda212fc56db37dcd8034b58b969ec9f724e6346f7fa9dbef6fbdca34dbeb3aed38aa597295c04d88f5bb4b47be3b0812b9c511b61f76a9311770037f83e7bd36387386d9516c5eeb72899868575e52bccfe8d75715fe8ac726162d97c1d7f92ce70a7e1eb6558bdec8d1fae24540546e4c7f6e067d43085fe4b75855455757a117639c04bc88f4227bfa1e70db766d494af01226440a2abd6882cf69fdeba386d593be5236080168f957fe9033838577b413c2c47438e357714817d67da468b797e2b53060ea04c0df7ee1965fdbc8aedc458606f48f62085c963e4783e5f7c448079086891373e7e9ab1afa9ac9553bc3b98520596a02b81213b5a153810a7a6b214663afe24f093009c19bba67a87bba3756de98e5e560fc8365d9f153dfa3f47a162ea704874846e7492b814a6985dfefd20a668553d3f0e0ede4b1711987896d6196f1018535e7dab46d0f22709300724db564f8b764b4d4598d0d6f8bc9f588aaa0a36a68fb3a038c498a0408d475f50878467b8bfc34f0533a7eb82e00ba3d9d58987beae7f11a745d23dbc37258965dfa5e4a5e84ebaf088d36dc6e13c13d23d1f6ae24d3d95ac61635a706479db20ac927c9a5a8c839be7690faadf52764e8a49df9ec8cf063a6c1626a99401b329c8ce7c10ac35d01ba374097e62cd4e2931c58a9970b073e0cea728fe0c908595238850afcbca9761bb3b01354ea8065ebd35ded8aedbbe6a9cf14a7b5c0bc493b651874d36c437e046a07d040de109f9b58cfd6d2307ef651b02a3a766bf8a2a2f6c6b19c79a41fee1f7a8946fa85e1e51d95746818e3d8d6d4ead40cf6ceccf07ea0099f762f6183cc134d0b3fa939193aaf0c2926cce2efd67a09dbde147ca35e92b63d0dabca85654e3f96c641dc465decc2a1335a49006b5cf81113762371dd5d89f318bfc9f7d49d2043e7e28b4cba3e65cf9a1b9dc646fb4ed74168d7c07528311e3efa85fcc64de3058d0c4672e35deca185e0b9c5908c4219da675cafa2f6d00185649ec2c8f2fdb845d2bfcafd03e75100a654ea2ebb4a2b82e19faf1e16211e8b244ba749149a0f06114aaa4fc8d6240132c11c932f57d750400a84f3d53103232080ea8af68e42dad862bf2b8dad723e3100e81d905e4b3702da400276001795e1df198b8824b497bee5aa9ba6cc1766e311c83541a6c5ecb9ef9ded1e4d792ebf53d398d193388b0718cfe73acfa95c336cb562dafa478094b5768549dec00635fb727af479d4212a88bf9356ece548b5d829e2cb40ad643874f120abdf89f89b5dcec302d46a90f9c3a3d6510929e3a84e3fad1f9f342b818d546c8bcdaece0a6b1372f7604c3a0572d2f6cf3abf33f8c022afc9af3f41413f5b51eb08fb11d60beb995cb92ef47c954d4cdad2afcec74280ac2d9fd0aac5a2d5779e65d2f7b927e715537765f4382a89278a559c9f427b2e983435e988aeaa9940e6e97bfaabd54ec22cd91d74af633a3df3d30d50423a6ff99ab5a5d0e584ee1482dc9a15c86a972a70de09b78c162012cd78896e2d43d9369864db5bb0ce3784a1910d3702dd1ce34430dba709fe6519479564079383394a34c005c86ed4ef9a980fc9b85c789c29050b53bac24095ca8a65368e651359b6f2ce05ff406aace9391e59cc27c3b8cf99dcdf7038a85bc4a04f08e41d2e2a26c87190d8476eec16d7e4cfd1fb70b19329d1b1f616387ee3f2c0a74c252fcdd95c6a9aa10f7f7ca45db1754f3cfc9019f01dc96e644ffd4c70503e5cffb9a515c816b37aa4d0a90d8722c8aed08b024ee32f1dc0fe34bbae851f30b225e3ee24627ec7cd33640fa2c1936504f45eff8b9c5b9c6644072dd864c7957410e54930a6f8756aa5d255e0aa16323a7a102e519ca90880c0beef58336107f1b6bbfdd58311e742a161cf737e0e58bbd4155518039dd1d17d1d484d8d2275b031363c61e85e25a00507ae7c1606c022a77a9f3b9f46cf9310b7aded221092e79d7fcaae768b31ced8da35d385af86cb4ef564da42142ed18c0a60462f6dcea4e50985e06b182676eb3d9a8274b1b1f2a998ea9c178e6139cf1207630e08e82b5dca2988c4dc604513445b0442b58380d2b7563eb931d441508e2a157c75ac677d165141b5e7e84e89c0794ab557b2b9a46758a8243aae3fd8096b1198e5c72c0309884cd07b120593918de152e22f95e1c20e02fe8616f2842cd1ac532241a462be92b2c51157215efc731c5edaedf0efa9712ca20587af92e3c52bc31ddbdafe50b980a36594186c9af91619126d5e7e2c0d464341cbb68e3e0e8cebc1f870eeb5f00370e863a30b05f54b2480ad787e027488df40b73c72fe2582dc6fea7a02cc25860a5bfd574cb1de57dac7065137c048617c7810725e001007829c14fecfeb8008f2214c011b96289b2601f36dd0267ab9abfbc485b2084309e503daa724a60677cb638d04c3c18b8ea65f0a752b1d4cbc321080d93d94d0d8039ad0014d788ecc217a5c2ef6f864aebe438a06018c4ede89b266a492068fad6d1c9cd494169167f6d283cd8484f4c8a75b251f910deba48908640db65e321fe7cdcd8f1b42982501d92d0f8bbafc1d0d37f4c4c157be663eb98d931824022ee67e0020b144a8cafd19c074f07da47bddbca76d20d4d598561cddc08d5c2709a718631430713dbc36d336c4ed67bbbd91a335cc0e336d30c690882f1918c8c78c24e229661fea7f14ed624885b2b65af18868818b8ea452d69b3b0bdeaf5bec9b70f992cf62802b915c13c3d3082d9f08ee7f23ae3b6a03267e03927bed2d2677307d417dd12b58474d20df88c9035764394967b17b57447d5b3be302600a6f91f027557df1cc0909f19a60d5ff205cd62f0da572635f67fee960c7a8713f7ede103e5d6e2085c728b887cb731a105264be8b07d75c5234e3377c5fe6f2a5cdcc95ecafba411db611105b41aad951e93621f095a24520788e936326bd35d19fbdacf3b28317d4339c3d0410a2478771e10d099e99351baa2c6677e0338b3008b211df4e7ac9c24928334dec0ab353a9a3a3721ffbbc0e021b55b9cd0bbd81f7cf1d6471b1c22ca89d0c1de020b3b4f8161f77846e20753c3803a606258c2a43f0e9c1fc74ec474e3a40a17d1457b148b63661b7977bbc6578f1fafe848432429db922653930af8a0c4981a2acdc28c7f5b40cd25a4ddc582ed3168d20dd134df25ce3b9ef33b85158beb3ee7beb31fb9637084a875846b68340d2dcb876ca24a810563c7dc236457301e5bb89fbf296c17fd9c034b3113c71309f41db0c74f4150781732a078ccd11b041b1b0fcf8f6aebfcc426e1e43a3e7117a318e12909079d30bfe99d6a1a424274159e873867d2de3f393958bbce1e7ba598df1bbf67403465ba499ac02e20e73d7bb9e3d8802b6096efc8dc9550b9960769c0109bc9dd9140d256ae18a8a79e0dc931e0926eb1ca50a093185a5fa9adea00ca22748b95f9d28a10f8cfc8f604c1a6895969085195aa35e62683f173876374955635aa5b8b298b93945f824a053eb8f92d7b12c6a4736758a0f3cbd60d160930298a00c37b135d825105f884938bef1449fd2a0a6b238422324a19f0f49f4cbe2b7a844e32d53c48b4eddc2465a7ca8c24d6b7b4178317701c814bcade2d73c4d4877e9958801ec59721b1aba03e08e53dd652a717af65769efb722a0e3ca2d7b6b7ac2d9132942169a109a35995556f9b930b4a817cab4c2b006e43eb0abbbbab9874596cb78c104ddf9600d1e6b14fb27015199ed452d05419cc91529fbb48f0f719c76c5574f952c5ed7b319112a97d56d24c2db1e02c4abba7d4015a8593d9bfc1f860e431f8f914b33cc600fc8295e3a543bcce9b12adaa4e1e3791b7401e7986c6a60cec89ea4247d96c8ea604b50f2939ea2c4590379cce4ffda28fa87c9c82f12e513d49c6573f7452978c1ebf148d18c300f983708783bcb4c54657eb205651c9a5189190c2096c66a57928f533bca7efc92794d6ba875460eee3eb4a9cf785d3772aec60c1467ae601b7a439e62aa5ec9e15bc6b16a3317d8568d48072c7ed2a868a23c7234fa82769b1a9608516c56879dd1b2bc771fd74ff2d25a2eab9fe092eae6d1066b4340c3c522a764202707052e8467d426a4c3269f346aa41b64d62a939f414f0771ced54601db5ff4c0c89954463913f6a831834baef0ef84861fc27a08d11d85f69ee0d95a01999516975be59c9384ee20f66d2cf5ccde270001f3962c4bc775582306b8ab42b8dc636928f2f56f80da34d7809cf10a34815ebdf3950b61790d3c63cd880ec1215e07d5c07f5149a2a87841ef795dce5aa61dac947607b108e976cb15281c79a414e4e090102cee0f47e8e9f569149cfadbe7a5f7588232534e0c97f275c4cd2656cc1a99ad855a72f65f3ecbb64c6ccf4e15539c3af583281025326442acc99f9b29d9189bc7989fdcd26f65f24f8d515b6ac934d9eb11e84d9ba8df033cca89cd02866d3284b2910466b732d9c7e1c80467fd53295c9301ba611f819bc65fcc7668a874f4e0b80c27ab8e1db5abc2de03e564e7318bf272cbc9a65b33581428183044846ef5273d92af7666852c63e94535b305f4ab8901ebafa41507aa83144e1c05b89d97a0bbb8fcc27e5df43f355a865d30e46bc8bb088fe32cfe3e035bd4e82598c63b33eee5d60c9ded42774c0a2d6b67624b67d6fb2ffed653e637c297c78b3f2c295d2102e686b2c1347e587f6cc6c88d4590f0bdc58228448de9560d2297168c11bafed83727ed328f2ea481d234f83515e8f3cbcd035517441fbc276256a60e3e884390ad67d142092b17eac448f2e3d1dc51939b3401b3b94ca8f1df362986ab15466c152c24bb89b6eb25342ce955fd401a19c8bd379b5ad75c90bbb5c8bbec7fbbafa156b64a04be251f5f94e97e4c2d001ea06f9d4d296069621afbcf7d9b6f6aff8f6cbf7b971e1f64d58a9883022955126bd2a0dee5b98691bd82565af9a59b4afdca8351aabf45d225af64a8503a9293ea10303eab2b5ecb86471953d1b6d8729924b48354bafb767db8c65566536e5bfb34f9b119d046781d03d72ac8fca6b32ee3821a0d96f54e4aefbe860380fd8b6f0a1d8927b2399c4b8126012e79622304f82c283d5d703fb547b9f5bae49507c2a65b96bc51aecb87c6ab18b96aa9486fd352cca9dc5cbc3ede9770bb219326ad72fd9aeb79773051f658d275b54f3e7df703df9926de50675662a4496ab3aae0047a5cb99a3f924078f0ace49ef5dfc4ca555c637c72edaabd7f4f33442716524e19533bfc764393fa53218ae9f8b1b56c29f8bd419abef825984a61f545ae980e5ecdeea2ddc35f85a88b02bf2fb6d7e4728a49a7bba0157e8ff7e74ccb9eaba07a51be3052b6972b7d9b5df42ec3b0cec4a6109f84d1c4dd30dad16c00882ef575051f69e488b19e67f281472961d79b22e0dd0021a0b91c5b414d422f64fe04ae5c64fc1a75aea31012d7845ae7cce577cc31738029327df28c13e61790719e24c00ebe26cf650e385a2b04c555503e1366d5ea9f89a639d727866e1ca55872c05f83737700b86c7870f01d094692e4d6dd1355d59f155f2ea0e08cfcde61f015b0bcefb02ece6340a53a4cf2897797d71a048f6c128e53248c06234c39db8e8e749fc431299b12b8da4fd1364f86c42471f640516d3aab634fa0e0636c5a3762a92967f4dd1adc39cf708e747cfb25f7db894d2b12435090a26299dcbd33ae984835095e9a56e0d5b94c88f93c498d3f5c0e9100eed93b04d2a4f5a15cd5002a509791ae98bbb46a6b143a68eb9291ae209cd61e34e28de5d10f440a870fb48df98583bf221bd209cb8b0a8cb382591835570838646b4a39d0116132a85d6d5d598d24d718ab9f141cefeef0d2c27bf34f7afab550926fb53e89be3412b1fd8a8cfe50427ab9b04a829d98a661adcd8a18b27f84284cee32af1d852f197f41d349295b15ccfff43c64fb6c74c35c78cfe19d5b041b0f33d2d5fa2402babc10a114f21a9b1b7c80b1196b3a48f62a6e18198ca3cf90006ee701e1fb90a91951beb6dedb7d3f48f77720d48db95c543a0b2b0deee906f58d29b9ce80a447950ef9584e6b71f606a1a6176f70d2abab0f7e419f1ecaddfdf6783351449f2cb9e7d07cb195c836f79c11f1e40d6bffb5276abe9123d329800e19f7570ede026477e2d8b740a3fcc99c37615b3d37c345f47b3c6a875ed78dea90237f3988495f90b38e97cfbf66d4f7eae6b92f6baa2390299af4659cd8de5d5e874a39b1402751488d640a3038a79d693c9c4487e6b4ebc7a8539b2d01f67b89fcce82af1d9560dfd73a17b8040812e8085ff8d16c0610c0bce9ceac9326f24f913db2a6db13fedf9dcd4389c9e12ba9432befea82cdde810a49acee269456ba9002865d3a0db76cf04490abfeaecc1871803ffdd6c1a089e57af4ce646f06c749ae6d5289b59633c1b9dd6e88d8aa08e4aa219717430504b27e40249f3916e4d3477116e42723882bc638349f706c7e166b149aa04be6e8942528f8399d46f85b7ec615442c6eef9a6fa603a77fc3f92d758adc306352270927fe80217de19b012aabd9e0b9265d4d6e23648dcde23444a5db364530599386c1891ca3708e073150ef868db984203495c83b8bbbccb0d25cffa745a946086ed9e9424312daebfab6b4b36a1b4051cc850d70df0431d00cf2804293a887b0dfe320634642042094bc17b33bc457059d7436436e496425652b1d8800404d3a6610728c8ce4658863af90216776e05e2a22f485c75903582201d486f00310dc11f8894531d015807007bc9ff282968f610713dee7de751707828ef0e276287a233749fe8d019625db01c0288e3be5c70540854861871c3716d48181bb75a63e5d4c0461a9d647c3be71973d599e10503307e320a660c6d89e1046188198cabf862cf5ee0d04547e6420b5bb8d68121240b162e5ba69c9e36b2bd79450d26e652514535223c1576b97fa778e96e2144b1a8dc163402a84b7d242465d5501921d65d1694623099b50754a0a8ef32453647eacdb33a211849bd9586f1079774848ea4128b5ddea559ef07814dd7a333d285b555542e6f14ed91a0b102ca9e356b8c9681cd74c7fb9fdfb8a0c9667183595914ea1e1d2c91b5e6c53d5dc14e19fbc8e0cb422fca617c032c82cc52e43590753300149bc9bf6744ce4c9e920ee92478296d3c87a575af49d1e6209352688d0f4cd43243f19e786a5ff4ea0edb3c3a6365f9b51fd592c2525471caef80607dfe1c06c2614c45a2c398c04eed7ce82aa162e8d3f7739eceb379ea1e749eeb6d9ed0b35cc69a0d098a68dcf9cd8707ff6b8a3a33a3344f19cd939bbc6c6b6eab68bf5938d2dd34e5eb12bae685dcc06b6e802d93b46316ebcb22ae6565b96c5b0a59b590c518b24e43163964850f590591a522b22a91f5f4843faf3f801ae480cdc57735823d7430438364090e18180df6959e153adefa5dadfe6414424565a9bfe757dee29156e3072494d7303a5d60250cc415545b2cc45fc263432a6b0a58b2027a0ec0421b2063222d49ba7156b7ecd8cdf315f01878d562d47bf6da7656cda752fa487ca98fd559b5e960ecd22e6bcc7eb6c92df0272b8d20185be5631781dc086c0632238657cd5f4b7133caafc931f4037743c02b823cb4ebbbed94cf8c22e98ebede457fc703784ac373e2e249f89168ac023f7771dd318fbd626acfb2eca5d6127eb8a4ead35ad29c8face77939e8923c2361ec570c575095490c8efb6a4756b3fa7abb446daf73b4fd07d0fa06d41d0fa17737a7b85011d2c8cf6b41454d0fe90595642fbbe5b0976f34190894f335ba9fbe0781aea2bc8b4df7f8d31e9330b2f8b1fab2fcb64ddf772dd2fcdbfcddba414b0eaf42ad486ae7da3131c0e327a12c996e2c3919970de3cacb97832ef75d1f7780f5a4148947660db6758d98eb8273b97258cc402e321b0772582c59a299176961dd0b12be47b69322456c7f8c64828a56171c1371a69c71ecaa6706b01f337b73c62bcf0abebb441e12d99c92c75209caae92f75b0ab2fd13ce4ef6df131cd6a9710d0756abc58147ed0b345cd4bafeb1e97d869bdcd2fc8711ad09384a0d187889ec96d4e84a3e37625bc0fd7297d50a0c65d2e59ec5cb115b9fbcdf3bd7d6946a60fa8b14b9d4141a4ad8adc23e433f9a2abe75766d377b17a13f396045d0ab0ac63fa5f602e3c7e0f5f2208faba7f0ab2b34c00a327e259df8a38ab945332e19768c9592c3b784fb7bc6721a2192eb794d55741a9659ad20810b3b633a1d210939b9edd3e0548af85f68269584864e175dd65a072bea88700ae4a8a03e847d43331301a03184b46b83c00f2fcf77b1fb3efdf05128a6e291a24f4e7ba0300fce61387399e69af1bea3819d45d1d9e8983d6ab9466253f666f0c64da09f726381ae1347dab8b635cec5343e4a987d4fd3430e937a987c405fe6380ea1c91ec574c91dfdf59b0ea82bf73f55a0386e62013b9a93411ca5552048bae857951f51f513344a4539ac725a51a3716e4c44c3f98f65fdbc8041eee22ebae1358b2e5e9a9adfb51f1e9b2e997a3f34eade35c9ae01ceb59ab79f39ec33d84f1a76f0684af020d7bb4ecc0256501ff5cc59989a7ef0bc313210f494e0f3a4d2193cebe7af7bcbdbb072cbd0ef91b8b2af4aa915d2c0335d904e4515d3aa9105ceb4e3dfcde4784935dcaa2ea30fe69831d96182d3b1dc93f6c9a0a22ce513d13380e403156427803de79cacd53ad1d305d829e42c0fca1149468a12604ca9bdfd4ae8649ff29e78e6676dfc213d2403c24b559fbb0e22db7e0b1a03c1f32e90b385cb5ae19e5feb0bfe9ac855ca5eaa210210945af7ed370828d9617e5041babaa520576af81c18b5fe4cd3e6ecc985ca82bf4944744a0d0efd693be24fe3ea8cf164af7ad0e419427c9e1b8a4ded9082c90c2fda8923578d099edc94c2452f973828746b1ee15ea63a2b379ae5fba134fb8063f1f55486c854ff21de7b2b4ad3ab48e6b2a07434d61fc1db16e4b64ab99518e8030b52637a21757b07c57daf93b4e2371cced9870c57aeda7abe2bc91d2e8f560dddc51e51f9de2d09dd76b22a042a5d66c0e48cc78414e9c9a28c5cef920a6050696de90a1c4b4f5009114be764797e3b27d3c179494d68f3564fb24cbc1d5ff2e2e1238698126810ec55a7e84418108999124fd5a6f81986327dda0d576219d4a92da5641446ab90b8d06aee5d780d8d274a273c76bf1b9cadabd82312903e1f5f9ee37a07ce4b10ba758bb1defb71d3e6b5c054fc6f20b2302d71785178031c175c3c632bd259d2d771c14cb02de9fa2ec829c9c42d280e1600256a3e7c663b7b9162cff7ae98e71f1fe0499b4abb998079d7668e3974b23e063503f68d7c7f3e924dbe7404a9866988564d572e03d3c27a5904d8b27831b04479828479d7190b6f4dfe9365812c5ffcfd83d0a261bd6ead1d9127e871d8442fbcea1087ebbf5e35d53e9735465b4ca72b8ac0aef6816fe4c061be96b68639f8306585e96d16534b03356c6137cbe914c270b5b6fffc8b5416e416903739e71c2c50af2f26e991807c3e9add68f3de7daa21698bca21001f39485f5de76a2fe52ed31490276945bad451e6cf252868e777f8942eb1002977ef3e0fe13fe01f873e0ed060ab7ede5414be1855e146d425decb059c988a2cb326f03edbe9cb8225d0bb4dfbd341e80bed0bd4408c4f41151255278b2d0bd50ba42c192a7b1835e9b5f26c1b88459d092c92293e705d1d12be17783111f40964801ee8f876d4af1ec8cc6b0c607b1719df130334f4eb63e2e7133106e81946ff89a5f6559c838455a310c13a8642a78a991544e43733006b58fa078648766c63078c698c891e319600b6905ac92135c39e8c1e2e264827aa55cb27c861a4ba111f9a79fd1f477c5cd225e0c08b79dc187d6ef89149ad7f583f0ba97bbadb03d4c486566d2128b63296c7aa4ebb8877b4bab41b33d104995a20f2c437123edfc838a4cba5fc820ef2525b97ae277c689b39042f168ef18de4230611431434bdc9376273b5bf88d81b719a97578d525b13ee40592827c2a76e329ffa5c4f59042cd496e7562fbc4c3d29cd2f303d98dc5c9bdf4e1a71a71122bfe338a71b7e22046bce2ff3172fc6897fb7d91cd1cc3514b7519dddd03d72983b309615df4ad0e409b9e067bf13c6996253607cf8c7e925424842256ce26dce9a44ffbde997578db380a6354d5f04403d3571a24c824af958897acf269e79004e1fbccf528228b5e6e1007c7aa76183af237fbad06e9f09fd81575ea055ec7a7344846c536dd69a50c9f5622409fcf59bd22e79fb0f1ca1c73ccd20eda55a985f0c220894d045659711196af4fbdd8644c00692c529f885030ab654a7daf29a4c3d3bdc53de31e9d2be29a43f829e86c98b505aa701d178adc073ec6e73fbc31acc88004888f31ebbbbf1aa61dfd24d9a4ba75ff44c72a4245f4b2220ba75a4fb8a670d54914e65383d8c4aca7167d23d411e106b808ea4f9bbddfa1b4bb661b3c806226fb4a461ff147a86897e2e6b9883cdf1319947c52f7eb8fbb8d0b78c987a0a1fd32d2c36cc4f9ad7361c848c225c490abab15568d5dc0a2186b13ff0d2c22017cf37c08480d976078f4bc478162ccb168150a312f99c5e2952aff99d5c38dcc2cbbaca2fff79ba73955bfe45c25fb494cee4dd00dc110e6cb9cee9ed47115ded5074f34a26fd6c4408be714ab2e7368882eaf8924551a4d17fa50c34847b32419842fea9a16dd31b42efebe57c9d762b6058a56a5381e4c9884c07b146574d12cb6e68783aa27cf755b1b2d503625a692feebc4039304d5e376ed3e3f1440d42e126c25257c5ca38d7342fa65751c48468d8fba9dbe53e262cddc9e4ea24a31ebf7f70535365325bfcf66d3447a32b60236c35e644feef3c110a36612f0d257d89a1c16b02ca3b4c8bcb1c84a6a21c00b7b1db214d892e96f077d5f8149653bee3a2ba0cfc93a0c75a8c9f27335ee3ed4438231d4ff439305a853d1428037087834aa6a3f1a6daf6a4bd6ac6229adfa9b85019c03e29149dd97135ace7aa23e0725b6271c30c61d5a5f56116baa0d54b6140915d851e54a1815f43933bcc6e94df2a44ed0f8b188dc0d7bf319588adc68a13200410f4796dbf2f38fb38250d92046d35d3b3d5bd6818f345f07fa983d6140aadfad08b9f0966aa2ffdff094749651fd87215e2017b2fc57a14d9d30d73602f5806e46e6c00d578ace76579295e38cb6862f86ddf96fb45af93a173ccebde8af3c1a57f4262870fc4dd84000db5e2838a9ccda8f172a164dae66ff709a308c4ef57c758434ac39639a09610acbf55c8187dde7c13cf7a075b6709205d414e6275e95109652a994ac28966349385aa67b28cee6844f50f59cd299cd47668fa9059921b360f0682264eab1918078dbc2bda64dfac285762ab2f73e458e332f54eb36c64be1ade53f6e01958a962aee7410f40e6061a467033dce577dc1d403f1dbf4c3e6aeb3808f71322b7f470e8d9e45f7ae6309317107006e6c9303c879679006243cfbd82b8a6b7a5c1a92ea7baa2f4a72962520daab48bb0e13c52962c64c4c57469d55881eecd340a89162a8d59675a0e7b8d2fb9231fae5abb76488df937824e444d384f085963254b9ab82075e29d371ce05b3cd2241ad91045a9f8ce869b0c2231dcee45b9ad765166d137472f6aa9468fd9257693bf053acee07d7a42fb5b8be2f7a1474b2e210c5422572c8b08d6e14b8554173ed1d398490826c75aa1af58819b71794f2d21d4696dc04a7c80e4b8ea5668a5f72ae0abe65febb08d16c496ae652d41a0b8f9562101873b7a13fabdd5162d1f329b773d92f7b6a05555e5573ee7b85310a61c49fb43ee86d5a8e5ff39e07cd734e91b48d463c6f4a3c0dc8e62b325baeb6ca6fb60749a046ab48ec7f8c0759e57abc27e5f8a96bd86443e0529808a5edb5e2cef6014276428647a9550d3f8fc735bb985a52a6a53ade1b178dc4220126981660a129f620dbe1061945e63a73c79162525c0527dd45bb14950059ded2a8412e832ccf8ae68cee58b94222ce387e2cef112fe3e59a3128e4847cfdb114aa7e6bfec6080a911a95358f654f3269ea5da080ed13a459239270f2332461e0fb007883da1cf44f3a9c09c3302f304db8fb62d75540ce64608faada2a899ce690a3728397952d6271c5ba46662f2cb7ea028a486e2d4c2f71d17205fa75163b84837b2061fdc2f2608b861ec8cdd022d6129d91a570e78a0c7f86eaf199d738fe9bbad54bf7a2764a5e75f047a5baf6223ce03648b283ae55150b5230ab3af3698cc7bbed1c039aa87874f867516d06614ae36c72fb599f7bcde502b49989e4d62d208cdbec1c5c8946c6fe43d05246adebf8aace1ddfdfa8d58b379470b55d95a4c1b49b53a4fbfbd4c47cd8fc8a3c0ffb1ed2e5aacaf29ebcf3645278a42dfa568eec58e32c4c1f49b9ada464cab89988d3332842f736ead60074cd962f939c68d31c07c02bd7ba405a0fccd5b780cafad880efb1239654c8ad70a6788726814d5c713b7712e2a532008153e287c12211fe42da9a592030c15643241e62314b0acbade5b5e25afa119461b7dab121f8061f4d12c6fbc0565cb4dbaf55d75f15104414a5cad9b4f7ce8f81877c720075a966d252cf29c66e263500c20484ac826a80fab96badd5d8a6151815c704165d80411a9b4ae2448dcea7207a913b2f57494ce1c335390ac71d5e569093e72f27724fbe6062d395ecca1edb833ab36bb3ff5820f603e46c8709b696a38ad40a998dda145ab4700538c00ef0024d414ea7a6abe9c3dd30af0a01e8c8eae19d0694617acb5b122ff8e750c79ac2ac9a25da8a1766ab2c32a94989203e42b4830cda11126790e3323dfcc67ce0245344b27e31c20ccb35f7d35cf3b076c4b476efd44ee5f15df58eb5ea5c8c4777e94e23990506e567444de878576b72c9936b9313012f7942acfdd49a803fb8946ec4967420aa207c3e83317eb701b08e15dae713404f031d2c4dc076dd067bb0f23c15225a63ab7eef18137072d6a0023fa3061cea2355d29436c413cde6c22945aefb34ba8dbac456b7ce53143a629a8508c28a4429dbc45cd5e6d05916876367e553dafadcf1885367e01f7291f12cdfc3968e7d6b58c140ba03ce1b6dccc432698707d6182345aef5c5adac45f0d7ea3a9aa3f506d6a024e00e718c530b86da62e9434ef35f5bf9f8e83cc86e5e0329f506b81661196478e22f1df765666edb9200edd4fd2acd6033c5fab8769361e6e1a7904c1569cf14cdc629e1831278e50b76a3308b7f7b0688563124bc82cc9d36832aee202a4797c7d3526810de0da63617df35f4b57d33375ed21f988e8f7252621e3e8e6a3e852e41734f27eefd8c6c02f5765748655d3e5f23228057835c8c406450651fcd92afb425bba61fe04209a554206567f9546ff6b4b3ecb14cf2360a4480f3b0401914fb11300b90e0b58f6ff177f07da18b5c16394447a30e800a60e0ba156384dd97b7c66b5b44e72ef63528d9f65fcb76ca07444067084868aad3394c14e4345940ba8319e89878c18e02b5390bc6aa81687a0c45e039700d7243bc25343ab52ef7827535ded4d8276c03cf39d5a8605d16c30f844e30001f24f2126d21fc907e844d251d3b3a3986c3049419f1baa35975a42e3f810bd48ba14fc52f419ef571104bfa182b7d6f11622a4fe463397295cc574a77d12aa773dbb4f8329a6df13bfc36692a6b7fab7e7365fafbf79f4b5fc8b47eba5295f112b99809ee3c7b64feaddb86c3e8b35ac3f56668833467629d54d037dcb268696544b68d293e7709a9b067618bb220a3ee6de4a3e4028d131aa3e66693c8d04317e92a884b4c70bde512248aa87ac631c3c0988596c720528e4074d0f2b0f64470a3779c99f4f403709f739b15dc25ef2d7bcef74d98e024f3b3256875b4892644f08748889a714e8939cec067801a8e070132f046d4f046536b68953cd4819c2edc476e697b452b77577a17aea43206fd69067f516abf690416c9e75d1324778df8f2c75a0251fa008ab6fd8affe9d9d3dbd03890cef33dd43489e42b672b34a152a2a568ab6883b276e09bdbd05cb303af17cdbde2a6366647fd492f17850ad3d80c27afcd3c7f5a834ec83e0318d6fa3fc978962ca4955fefa1ffe7f1144e42276a77e5bf88e1026ed590ff37a686cf1b86e77275bbe64dfcf79a40fe383d623054b2c7fa248c235028292b619f8b28828a6f207da75faf950375853f9b32d3a3808c728fe4bb33b81c36c5391cebd16e560903ff10696339b5efd0450bfad90e3345c192046f120f356495c291fe267c74d7f7d52b33872e3ec3b7696f2a5287fa64872e21f45ccce23836efcc9ff031ae6732153499096dbaf975710e6061c77897345c5cb2620ea0398750d2130860f5c60819d52a09b1f78be8b7630c868b7367121bc52bd2686bc0ca49f03e0344d9da361aada36caccca43bca38e1db8488e5846728ccbc4f9c415fcfe6374f01b596fcd403f64466a54e8a5a52a765b819f87c2b55b3e86dd80097d49ff93ffe0fab1ee2fc7bd4b50328c26814780fb992b78ea4d6ae8b2c864100058c584c37fc2cd049afd85b28a0e64a9ad26e08167880194785c7289292888b6edad055ce91b4b2eda59eb858f7152345fcfbeb84e34ce0dc173ab75b9548718e1a1a0dddbe95592ca74247da243d0f933c30f90f0878dc21be3114ed50d302f625a9af2bb6e14d62212f124f67b5e891256b011d287b50d84a153695cec013c6b7554f30737b63a807d4923ae71c4c4090094d854a34ef6a1ec35e3eff1448406a4c5d28ec2205e53b0107384caf82ba396d8e852aa828a4c4afe2b0336d6adf5a4d1dcc2a28c88b198482c8ceabad8fe89260c64926faa33bc87c9020e900369590e374ba5bce58494673e9a5a2c32953eb2d4e58d705d5095bea59f539c4b75ea99f757c53da259a1a8a9d0411f83ce0020b517ab7b61c34e74d56aaabfbf754d29e1e1f201144ac2b5b345c3e4ed4f39d2a47b125d492fd6d0573c6e42e50d0500c0bb00a0c7533972a6a43e5011598ec0fb3120f155f4efd82388018325ed6ee5f450a5dd9cfb414b4e9a8104c6ec4e81c3f9e111eb6a6f9830cf94aad7a846cfe895d450627aa921860d342e35bc7f1fbefa2f40ff3387666b40f35cdc4cb3c16cd381bc75c12a3ef34ca3680b37fae4a869d07a6d5339fcb39ac1dc0d17e2b03ff383216700c2ff38f5483556f179ef9bdbe6fb610189406aff8a6ad4c880a3b13269822386313c23e08e55491c2f3aece16f82252ef52ddb5b35e148466d2b8ba78389024796d88170ed5f3328680a4cf6f99436ce626ce194fbeba12032c3e5c884c2db2358198cb12247a4dd7fb67d7a6b53fcc26f082510f1cb7bb9fcc7138e9c1df465eb7be388d29b50aea3f0891e6f99bae38a563e9312b551700289df6258bd538046c264c38e17697721607ee72032ee8e54c1a9ceaa2fadfce35407d4702d620ad7037041658c1992ebd86fe0d2443940a205f701cfc44b7ffafd1e22e7fad99e7b4e741632c663b083febd26d76c72ff757fc58e1b5e063e5763b8de034c233e6ddd353b720babeb1f16132e6ca02c81e21cd87785f1a68622bf67e75e3c1eb0cb6f1280c8332315c4189871ac43b00186acabf51ce912829384aca0c822b6104d7b7f566e1ccc19d1380ce1656fd11828695910191a04a85495d87591638b66047e987db4c7c217a1aff97ab17bc06d6ad08992e9b426e2a08df1f82089380e6ecf76c307c9ccdcaea8c7f2565a783b97ef3500962b877de0eb054d3f40b84934f9c6d7ccc1e2ac0fc4295b36386eb1273e6c5f543b170ec4894ee2f887c2359c0bfbfb8df68284693ec0a712ccca09178b13129eb024d9190d0cf47ed66867744345da20331f8746668b3d6df4be1b09f95e02516b1915cc1a7921d91bdc47969eec49100bfcc36aacd4e3f6d42e5b39a7f10e6816aaa82842bdbd85cc2e5d0c20764443910d3560f529b9b0871099377ef10754979b93c97041a1d6066a178a4d69e6add81f6e8e915d7d0c3a19bcbd5672945b1a0366304862732c0262db59d51038365cb1fd086542888c4972ecc8759472000428470b16ea3508b20f27921a855437a2d2f44461944186202bdeda8de39b948c6a808989ba43d3b15a04c9ab4926f13e95443d45614c51d5e7e3b024bf332c7c875ec3490067c171a3784ede565a2fdf46269a0b3c3ed2ac130afe0d825fa6ca0e6708b6edaf4c2fdedc3d5376e28f82a8a877ccd5e309aaab4379d73541f08f2030a4da0ca5698f64d814e0f1dca502c605f49c28722a579f15565026cb408d7b46612bf5399c65425a4da80be4f7a63bfe62a45a4b0c3031fc2cc419a8889db5f24fe85ea816ff87113621098003253e00444679b1db8579cc9240c89c5cd461a0e2bb9ff1c77a9f20345c06de8c0fdb4baa86fda894231dc9f12fb8e102394125ca99ff233c51b76d202ca99885be0496581f289ff0c255e3e8526f47abf6c7a87d2fab06f09008a31cb0a27411ee16d221a6ddf12ce1543fc81193770bcc0ac0411ed0f89331901eeee63da64532328b412f580b30f848ece912a65365a4ff4aa7ca0595b749084bc952da547f806350817b5026eeae0006e6a9e903ca1bcb1e2f6d91c6f905ad03b80d922e2413d067a7bb900c2be2f0d440ec6d5b53eb0cf49e3cc0eaa3ec23922e500c30e976e30e05c0fffb32fb734704e5a6b8090a9528f5ef9b0e2d0800ff1b55899cd82fa8a84091ec81384329148696cc99bb6cf5575af993d0e59270fbc10661d9de54ab96bbd559bfef1bbd1f63390fc8d29891893346278bc4d74acdca88469fb975670282cc53ae972bba622eee5bdcdd2f423b26970f02cbcc7e6d61507cbae14d3c1aaee71e6321014037c4a41349d72384d0ccc8ab846d44b5a3eeddaedd02dbe81462a24d936205d86789fd4c561c897f55ec45c6948cb9c7a5e0f540ad2d46136f53497912b2e0a626693c527a9b15ff6bf496c8f1001835c3e273ce94d0dc4710f9213714d1fd23c6f39ce042355557d0af770468a1e7e31c6df820980811560ebb100e37e5abf27cf7520d81b2b433b2860f2923238b38397c19663baf539ebaaf5b2df1cb95e4418d7a53a3bd17a926fc31b39d244794fc0d449d0956ab96950bbfcb7a7c5a5ddf80cbdee1f0f71841e8677c92df0ce03cd8cc05c46e216326d397edcdc21bef998ce5f19893e883a08851dbd95aa6d87dffaef7da4056fbf1115a39aad913ed44ea77c42b1e903e11424b4f9b93bafa8c059d379c68e907e775a44ecfa7060aed8ed3733462ba9498c9b079be7e9b0178a4447f70185547a4f9e93861a883cb1c263314899e4b61848c86e078a0d2bc06a29d832db1ec3c9d1426a9cbcba29d6ab04df23e161584d39817e792a0f4205f066e60910b18eb8c29ee3d4e9b0af1d95c218bb792813a478622705f4abcf0c49e189e7b8704ab3c120c7b8034c495d4ab1bf5eca07fb5c70866008d80c63b6bbdba1343f6f4fd2d811c927637180796456c80247c25b982b085b094ca8caa4ad62b0fce47e50349305765ebc2d682ef718b205553fbd3c34293423678114ac0b9b0a60cab22f51d5746aedace914c33be85ebce46a5fac4825885f3880b3b2af683539bff2c4b37ebc9368558fd099c59e09d1954ddb7f5dc44751e2e5c3c8f736d138eaf88549c414dbe4e626ec6d2487cda72409bb264067414d97b3ad71228b2ea84a5860ea595f66fbf13ab28d8e40156b16afb8b3dc1dfe877f16818ad1f8a18895537503aa8eb3848ddb33e35c807db4c10b6cc4927e1f0ea140b0c52a6412f8c146a5a7850ae6e0076f98fa8aa5031beca975f92a08bfdf799a4cd4bf922d0c026523c83b33659ea6475425f21ca5ddd3278633e057e64810b71e1cbad074484ef89554b0e5e556c39da21fa9eb561a24a42c76192cbde08a5bd5ebd02eab89e18fcfcbf45e90eb379af373f772610f7b49c6dc200715ffd5b23eef607aadb0fc0a60cdfc31bfcee251ea7ec904190c7f8492644d4e3806593eb8e20ae43f657dc381f41ee002a2f2572a5aca99d58d3c57896ff1e82a581a4ec5951ccdbd35439f9c19796824be0ac0c91bee12ed96c16f15b7c5bd6197570ee62963e3bcdcc69a978067236f5f8205f6ef73cb8e489b01f000e9c8b42eff11680de68ceeb83dc3b84e931e55cd221056f3c3a160ab456ab01eb9e7198c2c157d18f07e00b88dc8e425b2697ea45e3746c78dfe02e181664e48ae460297e87a26d17f3fb5ec1a47b8c184f519ee6da3fa34319bdeb9972dd3f4243d6b61ce0eed72d1394c422dce64bc299df90662e3c24d4df135b52c34aa41dffb17c43502f4e1fac803dbc3d8a1984631a06f6a274cb3814b98d0c8a7ff377401733592e86048b8a869e6a5c914d3f716c40d0d2721e8b892ced8d422d202162e6081421634a014214b6b758443054ac029b489b61a911acd2e84f6ef41dd88253a33d3c1b3f781589db0cc431d7ec37760d35261f07df1d4393be05e0ac3d53fc11e25ae2d9c08c3d292204052925172201563533fcfa215f604f80418f63da92b7951614d201d3ff3b04b7b6a0150eb13d76419478aa002013a83f1101c7cd2c839900744a5532e85cac6be3265174eaf4121f3a2710ce52d12ee48be1f62903a3f7fbce3ff92709ef5bfced4f915c56fc9d41a72d81233e977751a8f45ca03d7a91df28b82527e2cea5bbf682daf78e27698aaea1da710a1e3bd7148a151a50b81ef0d2ee3c39623650d291e6ce14ea64f49c8442e197174df9a70c34e92c02b5f15008aa4d22a2126349050368edb65206bbf15b3ac711bcf0455df0362e92750a1a4d5355156267e025804ba4f10c6d3a5d87a213a4befb169b4e610c8016ca9c912a8aa803c16b6a43678cf4367d4728775edb6efc91daf240911f93c6a0da6dc8f292551f181431048828dc4eb86c6c456ee1779593224caa4e7ed9d700a396673d55502857dee06027704acee6e32a8a7951d58d042003fc86504899f2c31d0542731995ee9a09f1920484acc0b312d000a8188d601d37e1e43785964616107d43200f57fc68f0ee82c8d31f8ef28f39791ff8362d09626923e036e1a74eac10bad62ed2f53abca6d32f54e45b86da4fe58d54b21d84c9826df5b5357b02e8c752af25d29c9ae7441e495d6290cd6c684a53aab28961728712f66b308e85a14101553483d4b2a8511d7271896905e7318c5298b1c20296bc761e14eecc76e8021d511c4d36043fd5bc373027ab8e8126d823cd47395d4a8060cb51fcc15bff717574691b4378cb219029a37e628625ecb1ff5be7644dfa192204343ccf7ceda091040d87bc232418a9c3809a8538bba9d5f6c5fef3fa5a0fc631841b15d6055cb804e5a9ed93925f74b680479a1cea6ae58da1d95f93ca14dd7af161afb9f986bdc04c6ab67d1472fa238e4b470b84598b93af5aaa2c7125e17f6a472c654f1ae1815854e28da98bafa9b8914de4e80c1fe821ffb83011ae3230941f421dfb05f8d851e6aad667bb06feb45cc0c47d5e4a838f27e5868368c7f3708a8b9d57b25a372539f3da43d5b11f8e71ed3920583cb84cc99a605c21d24831f62740afb4830145ec445996ab9eb1ddc7b361a63c7a07508c2d32ae870ce5c3da3baa5176c10b512a0c691bf395dd35ca3644fa0145f88e6530e03264de976b45ec95591639ac2c67fa46d582daeea4bbd71acf0ad93e6f8ef9c2bf4432f5620dac7dcc1000aaa5f409abf4b5635b69367a02bb977e817774273e549c3211666306281b64f1d88ad1999d5ba026fb60842761c7992fc85b4d4526ed19c3015f4f3cc1671e8ea54cb3d41b5e1b6aa93499ec227d16668c0829b8278065b180bba0904cb41137dad440a20a98a8cb3e1aef5f29337e972440e7a94d58b12c8b7ce0a5325d776adb9e4278916de933d67bfcef2ffc45a05ec758367f67e3cee28b092609e5d75dcd8f4b18f981d8ca061ba86696e786799e4e2f8490bbe5c241103f44f433651803851c6f8c0a70fd7f540011c77fae446d3b2ee52cb660a3193f04585570e2afa664132eaa804f8783a07e051018dc6708c60baaa0640a4034f196d4bfa6bbebc27b50e99c106ec24ef05c4815dc38209ce364d0a8808e61b1bfc4eb8911820a298ecfc47b40b5394c7302f1744034166482ce7201bf7b48f3fcea0ca8c0c50b9a2922d064392b0ec9309fc3d8d4dedfc620abe4f9eff4694b1c75a61895e7131fc32f5df3824bb9d0fb21ba6cfc4f03ba3127e508b441b70431b36db5205be68576f48618fd131b0ddb860f2fa244c12cde62fe46449d6917e21ff24110d203cc5e8e8a2a8a19c66299618947d63d75181e2cc257302ff3c0ae5e76baa94c2893e0b960189c01ad74ed273b7a27e64f4b9039e33fd0fe27072088da9c071057304a4fe77fc6ccebbe01c7dbe18bfda058e9874a94b0d5dc090e786c5c4927f9e95f0673772a15137b4ce5e2459870bf0672692ddf46ddb63a204e31d07648b8bc0f7aa1e33ab5ef79ac442e08409d893c58e6a5cbaa2847c85d02adc44c91098f409d3d30c126a1c1930aa691b50b0ac70b823fba749ff8fa6342efcfc5dcadaacb9c7abc1f4c7d83b7025b36090fe6670ef363d03b9943bd61b812d455316f6336b1b3379a2dc998378c2cf22397b7ab545fb924b2980dbe28b4a0ccb409ea5d9098d8ec4559dfb64a273c30eb6f172c7e51b36af6f3acca33238d1d1cf925b04a4386290df1940aa9f5e6f515d2889620c1799ff7dba19ee4e9bac35f2eade63d5d48127560b34257faaa251cc1ab6bf94d75c16ce72ef621de23bed81fc5a7cb87251edb843ddcfac2395dbcacbd0272a39993e151ff3c7d1c6fa05c03a2ca8c318c1edf205e5a5803bf4250806df48906692dd2c5e7aed27c262c67d6cae3230597d9c34024ba5cd95697345f33537e3aecf2491cd7001bd3c005810eb24c1f69d6e40a9087f8ac313536408e453be3c9019b7c92c71173559ce68c00d0d7f1c17a2b7dbbde3aea4869d2df33400edbea4cba0fa524bd08d8d4c51368d1d5a27da90645eeb877259d698e38431f780a5a03bb9e20732cbe03ada648d2bb8a6e19784c7591e05658e4da41273fc42f0be553d8bcb40c2430efa9c3c06c19488f013251604ad3778a9911012ddc66da37adb11fc2d74ba38bcb0d0084f1f023a12340bf38b088a68a85dac4c55795eb0f9ff4a9a00d0604c38380bd3c124ccc52fcf3e7070138575b2d0c1fae53f0d0418b9a3df1c701032288ce173c371496dd821c14b450d40590908e04e36800a6df3f55eef445128071a10504403b9798aa9fe218bf30b103a3ceb3630b676eaf7481f190d944e0184d7a19ac700adb67c426749834c0d44c020fdc7406faeb8078748691ef0ea1e9ddb75f8f9803619a1ca2a60bb515f997bfb90708d4e506026cf21431ac97bc86e0a1a65a768dc83d5fab9503f1386aa8e61ea00e6a707b102f083b401648469fdbd19cbbe6ce12c100623745acc54274d56621285f2a9b3db9456aaff738f6d0fe75aafe4bee1918b4afce61d9f307b40dacf0841f19538f8450b81dfcf9d9797ff7380172bfcd7254b97cf3c9d6a02e675a9a0b4995d97b847dcc468b785817469e5323633190ea64c52625f033d447cb32c91dd56011ba2c9f4a663874e16a4a4ad436c28fae819e8549c7654a079fc3ca995a1514428c2bfb78391d76b33c1e9228c5a7ba9c62cbb4e8c0b444b4d96ca188f4b8aba736a8ebbf09d45fd625bb6faf0d873eeee060bb6184dcae77e9595cf42c6c4876ccd967f5b2393f490330c90eb598c66f93716054094ffc5e34f4f51c18016de499742c395aec2d0023010d742a10b07fd1eeecd891eb66d29a0bbe5f28786abce83094180eedb624a8fce915bd1be809d0ebd8215e16bbea0199bc790e8f28625a544277767a22dca4a16cc3fe3d346417d92e40d2aca4ddadbf1ebeb9e60ec645a095de486a36dddfa66483535c42d3a6e784d358a58c9fa32eba2abb11e1b81189a4fa092a033435951c17cb577f26ac03a1900b14cbcca24fcf075f1317c0d4c8d0b112fa017d3c4ebf7a5c20743edb1e1771751ac75d69e36ad6de5cd74d087c04be71d3e4d214abf2e65bb7f56e36b047d09cb0723b8ef0a7f504e6340c49f5e1e0e512ad561ff2b68efc8d7b456cb190945ce739f3f9fe07837626401018e6da29b9807e7054eacf2b5d669bb25e5382fa10cb28226d0331985239fef9621534d521c3fe54f080cdb186790ffaf1e070cc95fd29c5a2512cd1fc06d570328925f27f5142b25ed4aa8cb5c1ebddfdff965a4f44bbca4fda705284028fbabc04e338729817d82a83b2f90b03eaa3fe2be57621a0a100c182b7647f7f7d2086d9f6c2fe8bd59ed396ea963e299e9d49792a9f73a18688b639a62adffd9728d778a197e2aed79a769227d7633a742871f784b48bbfe87bc8f29c339b654606999df3db3bacc3cc67c2bb25310a99d1c2a81c4fb3c8d934a4289781f0e76896acfde1c0461929b1a19ff760e253065cf74d00c9921e7ce4577109676fe25a64c2c9f1151239156b722a0233db3b6fb9f3554553ca86ae8fab787521436abdb474ca3bdfb921588e29b4280c969329833d889bc276f0f45aca3130c249c5cab77ce26891f2b6c1598155dcc5d1bb0bad861dc86ed535f281d88a1fb4e170e2e8ac328e7869d515d6d52d0ffdf4409a8dcd167dc0108ee9d4fda88cd51e817e1a7838b0104c70214e83f8169cd02a1e12c6a42956759ec47ebec762c1af4df42efd4756d8659799d6967a952eb246de1906c28f80b750fa44c38f2358a56edc331816ad4e3afdebf51199855c884c2a32637805628473c793f366edfef064e4c3d808409c025a7b11f443eea834bc22ba2350d1b9f9c7c6d2f3d02b26ef30098a154a40dc4cabb2cddfe5d6a1534085e0da3db8ae44bcfcabdc0fda745e5047f5d11e8b71dc1b64fae3aff5f40bf8d56ba6d854d149c43e4ea7c611ab754c664ead9fc1f31802923499f7a06edd8aa6aa480936562664c556df646e45d5f4ad0f034ff16df7904fbe8f082f161c4d800b804b365bb0bb62e719a2bb06965e857d08be3404140cf4b76ba093ddc50d1e3ccf32a1016519164e8000760b79fd0bba8dc4ca2d20c15026b04af4c3e6d16fe2107a78cb451d19f0341d4ff35006762d4eb8ba2151acacf43f66655e16f04de8643b596c13a23b2792bc636687afe7e115c6e92ca3a82be25fe7286929f90ff173ef959cd65d0700470c0f49718428e2a5fcf23a0cf5580ec28b19f708c7a46dcb7e624ee49f1108b9a2177b744468c447d92b787506a759ddf4f49d482af2340862d65fae4e75fd511d5e6fed89e4b5cde4f4289a07f9996fd90b883caeab2f90b31f04fa0117c5148c80ce22c360e8af213fbb25cd309e61e1f3a4f8c5c6f89585a06f81885f1acdf858a8d4660741853cc59b201a07935942aebd336bb7e1b4eace4914635abab0a83e61b38a6e1e275c4f155d973e6283d1df03cc95e3630fbaf96d9c28cfd5529eba442675fdfd417b50c4d0cdc970d934914c487fceecf79e6ee465eac33305267b89e4419eb641c7880f804284da67146fd3996e5deca09ebc6449c52b3cf847ba1777573953367a2b7ba4050ebd02b6c3a2fcd4dbec749cf542e43baece4c5a075a393c4d5a27fc9cca1d0b5baeb2d3d67d3596e446cc64aa7fe7539d3378f64a75bcc9e8e111a1cf94293ea97b9d9ca8c161ceac8435e019b3939a1cb1c72f747c99ca61caf18d28edc06f0c93eb74d7636f4b42518ea821b12c882e129c96d55d4b2efc5dd363d2478675772dbaf94306ff6f6e1667fc57ef25d4c036a06e43b033b1bdcec073de9ba52ca6c9835a9c006ca3665b43d23414f24dd4216a9831810c7d17332470eaab8fd4973190933a48f0a5586863ad9db8ead4bc4ca7b3194612653f3acdb75e30999696cd3da132f83970265222edefb2f233359ea2917053c29d06f9eea7ea8c7223f29328b2e2d2fe29f1b401223fb573013c1a48b837498f790d1910955fde648d661cb1c56760762334f4d87b1891b913eee4a27f62061a5b3a9280babaaec7dd554ba640d2e5d46009e85df4fd1592e60d4b51987dcd8bf14e6f3a42e8a116ec95b9389c9642500a380c484b3f5b2f906ddcdf43d5629e9bc6e86700f415b2411fbaf8ebf409cf2356a109f79eefd105dcb1acb21f752a46e260eaa6ed42964cd34c8f05b3686034379ac958d38a376ddd96f41373fc5126f6f3d0269d7f4fb5e3201945722fe23b4fc587e7db33cfff9f73cb68d5912374ce86c12ce3c0f8700f8a880d132b42b804440f3a11a129bf16815f951923d0f7567d519be8473527fc16fa5b4ff41df7159c5804c4ef7df74d00f403665197aa7e8290b6a587c883f057bbfa37bdcb0722bc44d796d5bda4f0a6f33849ef4a6faf580a4a0fd2bbcde75102f7891a1f09a61fb112f3a86211447e8a3bbde353b41a8bdd84438c0055f273f9c3d1974034601083ab7f6369c1e795a6dc0887dfad16f891d9a46d831500925731881ddb058f76dc7e2241d26731a348cb6a3e28e582592c35247000c01afc140584b2f839426bafdbc0c46e05796dc2d48698025b4d6c11aa706400db06768d9366ff8307428e45c40ada0dcfa3a6e487ae4c60801afc2bf161267bcd327c57860499020e9e4a3e963788c50f36fe50d058a87c34a00ed5e6556969293742ccbff40aae3605ae82ae600b5c53b5252573ffaa641a4d62a315c8ea02a49863d8afff288bb800cba9fff792d5df716285f03942b9c5b1df916651555ec34c15fb727136abc603213aeaf63a5b5aedb5953f526a3a49cebfff0813b8d172ad7d4e31e16ee9bcfe3652d82bf57b0afd075b8ebbee5f6f94e5f880ec7a27e08f802993d60b3695aee70fcf639a4d6a6177f9f0b0fee68f65965367c9e336f98ca4c97097385bcabd7c93d9ed9e1f0ad96c54cdb55a52e3549fe82ee69d4c88b51b0bb236f3fd5fdaef1bdbe007be280081183df237f8c044d3692df706b931cd647e4295c3e122849960e2efb50ca6183028a8ab48741be3f7f2aa60085dc3496069dd3f97dfcdbbdff9107a9c795b1cc34c4948e3423ca48f4d430fe4c39ad451972b16571b86875b2190264fddae84427aaf9c297c02e19ef2e505060193bfe0c94676ad3e262b31106a3150bcfcd24291afd9ba2274525e4aeb81a26843b7f1838f0a844b086f94053b6882f82a18eb3845cfbde27f2234f2a572bad7922f6893368744f05fedc2d65841bb9ca0f6f99cfbe0347ae8b2ddd97a2a4b508d82c929ca06c28b3f275ad8e1f99c85f79201803310bd863cd521cd701708231e17742ea24f30c10245b4b5254df8406edbb5d810351fdf84229e91c3872081d1824626c85890b781641d57f172e4149a2d0e001a23e4866b03e2640180fc2a695cfdcfb2db3c85ae63d68dc92c54797f06e18a437fd12bb08c598796061bc6f171bcff279808be54de43be1bb585ad3c6a67e681ad919390df075cef1918e87cf348578b29562a674eda21bbb362856dbfa7f6256301a862237ffdbacde0d95a3ae33673a6de215e4f8915e49de02fdf88f6155469a2728cf9957586074550cf002ced50d6e4a189ec1aea447096bd0563d7845c005151f2b466fd949c44eb90302724b99c6759670775ba7cdd3ec15e1f8217a692535b42151ffd0538246ad11baa9eb65ecc97b52fe1b862ccb07070dfb2767aacce74070cdc38c97edd58026bab6601e58164c73fd37e5f6a5002645a2dfb0851a5ad6728a50c4678ccd7409d26621b64215f5aa38641449009101bb1b7adb96bec496e1f8fa510e853b442844964473c3ef1514e84c1521739c9fa67c0d384b2d7fd1457fd02d56606f58079de2e6c57cb6fe23148f656e5fb0daca51387d17452f10424ce8f5b6a6e4e25076d80b837db50220f67738312eaec9bc77be471874af78423f6c64b284ba3e172f21d2161426b0e5b1e38ea244718d2e048b5c181410535e7a2a8b4010fc92a9fd2203506cac73c8a1b7792020d7205cb3ad08c116ae64ed11e1fc95884a91ec7af580e90d22de45703d5e5f9dd794566dcc136abea204497eb77f8a0b84aa6dcc3d380236a0fc9a0281d222639d0c5f2412feaef7ae49417330629a6a5438333ae3270d56d5144cdaee3037b246f6d919bbbb66862d4fae151cbb84a0820daa6a07d11965c0a568ec6fb4de53ac11c88b99a8a4404242a10484a12f63843160d7904f3836367ccc583e4d1a92acd5b3412846eb3c0910221365b6e1a88390206e59661e18f5edbaeb820f4e4b95c684d42f418d37adb6504eaae98961f77520fca10bf2598fd69ae1c82d0e735eaecc24f33464a95638d751d4711ce36d3787f6ae925e7e3cae7c268c6e434bfe48b222403059682b92f79f36d83360abf6039516d2055c7a15a993e084e255955a05f83185ee295e92281639a63c9a9fbb7c0dccf732a34ee78ec9d24000c7dd0f5ab1a02d79c324195443468f54755547c2493c232a8357976d3f0cc4af4fadd69282b2c6fb5aad67d9920e09424bbc66c5efde181e80cbfd27394eb3a88db93aead9b5c4a89b03d4be124e78546ac4809776f09441721580516c99cff3f7bac84f0e8bead3ff0363869a291d56fd71fdb10231340ccefc15842620d826ec805e62e8d6a5fc977175cabb78fb60a74ddd27f595b3627d77e4687da60b4ad0e342cd70984bbaf1d50547788627b9e6f6b8596818cdf20a2df70a177b8106bb30d136bf16ea4059cbf6473a687bf41421ef292b4e643526a426c0bc984ee6407c57fa58abfa46757821f08a5e9e09e081336ef99d9d69662bc26e54c2082cc21e1672f2924e412a0e3d6b733c1d3e4e70d7a40e46b72a7f284497a330c002478cae2a2ed27a2e2b2d04263c061cb280b7a78737f521429fe408be040775097e3493eb2e106d14ccbde8da700d0d85ea9138e39a0af42773264a403c2cfbf7b09318446adc83b03b616935956ca1c6bd5e7044ed07102eb29ca27022deff239ca518480477ce40b65c55417443a263c1d15d293f5d1b3edc1229c92389fc71b3a48db4a869e8364e03d1eb7c354470cf005c642cbed53491ac322c670fdce1f497da8211a06d4a1c82d9a863910f8011a951800d4664c7c279f497ebb572ae75709dd14df06c43b2a548034c92626ec5772182cf4c80d60816446a9d0814cd7973cb50e486c07f98b281491001135a00d021b5faf5742888daa9aeccfc6af9933f5e8838277ad53228e11c593018c3b08c5443902a87a62c02320c94504d6420d51f27366e456fed3fe947bbd46bb72f826db9b33dd873046546905e6ea66a6ce2da9a00fc25510faf2fa6fae4892ab985f83cd0a46add4547de02c4c6632b4fa7ee597f45b58502ceff68f0d7cce04a5d7936f7b3fbc6ad13428d60de3c41d51861f145beb3d7a8156ffa718959115388c5bc8e93bab6847cc6ad346328c1a9097ecf8db6aec8f78a5ec190cb5cde3c11fd7e06aaa9d7a16adc686ab62c211e288eed8949890922602fab0753ccf90f3ba2ca93a59ac9400e2d66d5676c284d798fe796a30b6e0fc3bb6f55acca958c78a2dd5801c65e26f881d22719cd347bcf509fdc138031c8a8a2c54f1a45823934505878c6f659a2f5cbd31665729b9ef03206448679dcdcb7d9fdecb24a840299fd1dc13f6ae6aea20c2d17c7ae3044192b1462a1ea062f09b81cec2344835a495654bf3216854655df87c2ff3e71f9b8b2986b06d4761f17ccc20bc6f851bf31a8b031440fa10526601c8da84eac80af1a839caec0941fdecf24c42bd0c1cc726abfcab42e58380e844c3554a1bfc5892dbf3efada3304d4502e828e65fbd0fe9cf6f7b3a66065420bc5166593a664adc72ab0cead1eead7a1e03b4d266ae6d62926ec06427173c3593c081980a566401ef092ef8f7019c3f4dc1b077b082b15c2f18a704782d5415560ade48a62c2fdd51b0314516b0152794feec6f6fda89b963a19da63a637c9d567baade7a23cc3dbd04ef04c8d71ab7238cc5c172978b948b05ecbdc74a57b9818cd9bf29ac0187e009dd0dbbc8d5a85653fab37fdf515931d4b97d4f3d1b1260569871825e08c612e2763942141ba4cbb5f40f6e9f30900035129028a7777f9ff8266bc0b57f00b5a31936fe1d0238833cacf169f0a7ee99a0e4aa8506a234ddc5003ad1ea89bd1ef6edcc544416ffcbc81c3a72e3901242b290d3f8bd58edeb293326cc5f866e887bb17a22deabec2e3a2eaa74c4285728488746ef4fa3efc87b4c7ee5d640f5f407ba896a1a41d72491bbffb5f3d0bde2fafd9d2c2801648f5c616161b444e971890e44b160b134578189bd0855cb3849f1391b2e23a22918994ea5302cd30e9d9c58c203f7d78796b26cf911e577d236476b36f9819522a83cb33ef40cfabd103f4b8852fc525e179032fe60e853b01005258dc240459731c96fb94c4b4e6a40ac95284cd0801ab03160e1aecda3fb744bc23e20575de3efbbc831b374ab77411f089f698950c41bb18d499d10038da82c30e27f6e9155c8f4f2cf073dc9829fa4b6f7c91371d052fbf7ad7af899bd9e011f8c195cc7888d0456cd102225fa7a84ccf7a61af256a3fff23caf49584b852f38d43be7df1437b788590dbab6b7215940c1bc3285fe9ea250dc92f113843c37c29848a6d808b170368bbdd30ac805c5c139cdbe2cc566e58a69941816bc85c399248a29567ff816055a7b079188866b876eb82087898043968662172dfc8fc2d927c0c341dd4f1d556a427efa3f9beff0c409e3d8dc059e1571e408e5d8faba91be22e7f03977a2e145f79730f207611c1ce7535ee1c12b6985cbceabbb6160ffc0fce719e6597cab9a06ae41d1060a606ef513996ef18d574909f0573e1829c658e55f4108357097d1150439ee503ec7e9ffb7e9f6a351ac3ff90d2fd405977e0611ce706753c44bfcc0806bda7a0927772d068a3f47d6a7b45f49c54cf019f2f0d2b7b0d4954eef1be400690b1727f5709c5a674233715bb07a821703b232bd40735b0278080462405e6157b09b223f200bd12f29baa961e860d511b4a6f1558876d6bf1d640c5f09adeca19f25d74043e51d5dbe8fc3c432f5152438bea59997234bf1f4b2ef634c61b8b46f915487f375a0a4328f5fdd3c784003a715d7e8957c0b7c20528b4bdf08e806eba51202d47d5fc893c89c018d076f55383148ce936804610485b772db88b9c117185bbede264dc95fdb0f424610762c748657e9eba5d0aa5144d0cf790d9bb8f3fde03f1c15768e0469057d05f9d183529f1d40175f3f7bc2e4cbb934c123ae4934995a3447884cd28fb62079809df3ad5e41319ad8939f7ef3131520f2fc26a79b394d42dfa9cbce4ac758300e30a88103c284fa71f0683580ee9df05459364827f66d88619a58f59cca59355b9884a07ae6a3a08c9695b8bd52cd447d0bf0c11d10e698b565fee08199f5830991efafebed4b3684f382cdc2e673638be9b41b5d31934d9531cf4de62eb5d81571b5e15ea0136e078395d9ff207bd785cad54fcad54e1110a3b6f1e10f3b158f897f2632e136b4451e6271602b8d7b40fe12159b4b11abd89f727a1543d1587aa7af822e82e79b451bfa2cc3d58c2b29f984b1480a1b5ea2f37b9cd16131a589cd7f734320c0ccbcee23ebca7a7545f88615eb5a5f1c2a16ecd93ab69903c7d5e419d16a6c6040b1687df645df4914e03f08a64611d2198c6d213c5553e9911776ebc183605b86fcccbba06810ecdc7312893716358c733ad3d8c29a52bced5f9c7b84d17efe610776d49dc7f93ae05f3997235f7f8e8199a86ed5e4d83bb3114baa9f5f5ef775d36a96b8774c6c47c73f02ee9a3a8175951daf2982aacbe520a382ce976a25d35b987a8c13b23aede55cbf039571f96abc797c99ba4098d0db4f16b3bf16df256652116088ece3eb3d370b15e3ecdcaa7a233f843ede2b678b43ac646997f588c3fd0ee05a6311be3b56fc40265be22b9907f50180c48a9b4ae425c20031ac90774babe30443b95e54ee8d0ecd1966cd3296eba0513ae38d95e0eb7901abdbffd050c4f39a5e040d0982d4726a344dc163f6d52668d5239e48e5563703de72c59873bedce5a452f3898bca27041b4c5091f1bcfea65622cf70f978b0d7068d2005cbbabefce420d96c388074d905c986cf04a638a71c9e1d5ca550aadf852470dedca8b0700d07e70384e472f9e3b022c1250d02b62883c453ca15f1135f99dc4a9b59a313f7bcff14229f4de222263f6f6f75d945768ebce7f7700d5b9fd7974d54c0e129b9d55ac4710418450d468d2064486d4b29eb151b452ab2d0307ed427dc512fa68e8bf2fb425dd4beefb1b6ad0263137c8a0285964d69d429b1f2903648826be0bbe09fd0c1e6a277b384a4aa805cb2c202895c0d96b91969fca69710781d29dbae9052c1172e8e26f88bc4a8460186306dbaafda0f6dda11a1bfa2dee8d24bb13d30ca9d03c1762c9b32b7c40ea898d41223bb43a7fc083c499571a579b9aa97c0948e299322323c9d605e4eefd44f07f5d65eb889a5bb6fc742df62c6334d56a5de6c8b2c67d7fd2b699b1a444b51080bab93f6814d755985a57094412bc6a545cd8683b119177941d161bba84ee3bf3326c2a42beecd8669dd2126533673e098deab96394eca6b8f984c45cc6ba3515f26d9cce6ef1c2490056dd4083ab68341d790048ad1c97102b985dacbc95195f1ebc93bd388bbcff4408b6696f51064cbe9e82a23332dbf8eb9c4a27aee4ec724d87a25545fe877f5504a0237dbd9298aec721241e6d486d228c4690822622bb39d95dc934b4cb6f4de05eb00b9d2088d82aea561c8ee0479c6dab7ea953c7fa9a9a190a04a2ee7b3bc8b41e232092ab21a39b67d5e2ddac59001cb196085f6005deeb7074938d48f40d3bafaef8eff2a46be875bd89e820eeb0eb25dc5459029ca15fad641002c967bde0afd5202686c50423bd239f98194ba69a1de34ae9eab042566cdef38f8be8058685a8b2c3457e6b15672c4b90c537615d4f27817aae374e5c245dbb6de6bbc6e61b03bfbabf30ac6a0236c4b76739d1ea709e2a62b2883ddc26aa7b0797e0dc026b96f8d4e14892dec95de27a0edb93239d18ab4ee7100e028a503a97fc0228df24148c827a8beee9708338d4635e746310981d8f2f563f82c92a9e208d42b0c77d4cdea8b31cb1091f533fd224979db31bc186e566d4f1847b44d6d42263ef4bfcf91737c0c3f54ee5dcc6aa456548b5f39b817120013b9dedf08a588a00a3d6320317e8685d54866fa6f63e2e73c6aa46b4ed45c79e3e9da9c5bb789987357f0dc62e8686e62852be734888ecff0be60fd2956b1c45a6c633196d8c656ac6289cd58c5526cb18ca5d8c52eb6b18bad5862195bb18a8dfffdaffd53ddfefcf77f41fbf15d8b57f311b78e48ccd40cf62a3754a136f9e06a37b45db9eabf08d7e624f46f983c0240aaccd36c318285ea2c3dd1c2518d9b1cfe447cb9c7cadd18ac47a1b21e9cf7b424f6e6a5db6d89aa67953348a3352367f32f60eaecad1451860a85e05985730c3b3c260f1e6cde05a3e38e50a76d5569529c1ba035de710f5068ee9c89f65e0d8d0612a7bf7afd907fe2f8e7edf16197dfa35e26b32e896bbb435580e438be3b0d598abd15366c175dd9cffc62124822bde21592a9a663fa96395143c8ade2edb8954489134bcc081f0fa196354829eae48969ef639191528c6a41d0ebb80744ad306aab507d2e3dd3b5cc111b42ee6b6f458d248ab85862b65a1f00b59480570d83a276d7cc412ff96aa3d0b5926fde0476736dd1454ec6cb4aabb4516d0b6be6a4fe0fa8bf9e0de580c902fbc47d96f4cabbb90f9ce45a0b9fd374c09ab58d42637e2f1d0f1bc061c85b6f7d7d307bd36a19075015dc32601f0d45dc2e1b8a0111c16f8dd57dddc6119aebd39be42b30d09311a4e52d7e5acbef475e1e40b4bc27c2c0c4b2535ca40e41951f373660c011893fc8299920e45c5faf0879a0b78b7c195600e548a98a810aa416237088a1c1b2754597961b97f9acddf1ea6f35d13fffff7303b6ce2b7c6a55623383c6807202bd5335e465862ade10b67238a28d8b4f0af112388c85c40f90319b71e7ce76e2d4e027c3afc455512817c992329a45402a95941e0742f4b1197a6767607f5fc30c368cca1f57685597fac89ff345818d6bff33f70b3ffb7b5e71eafe7a9b127cf7fe8fb820c46ccf44b999897e4383adb7ac0117890ead20416f623163ca1ade2d481266306e6b4830b9e6f7376a14f74199f578eb4e327b8c3fa478be46f97383baca4645fb80b9e382cf29df570037c747ca6f0e0a02d711f31db0ace08d359bad41c5bcdd7b36af24c27123deb7afcc9c716b97b119cc1ac084efd428b0c1571352b088632c64eb2a4d0956d8f178f97054415132c7c62eccb7a961258d584d5451ff31698a8bd551c3cb9eea95afb0c77f582a803bbbce581b42fa8429482e821c17778aa29757a82adf64ff579e4a758e928d81a20f68c8c19515290c73f440fc263895a090ba2c0ca24aee182f05e52641d39a40132c8cf09a3527cb817aabcbf88d506078d98cbc96eb82f411adebcaf81f271d52f7b6f542529e453b76819ef0f8c80e0940e0802b3e861d28dd39656e8bee68ce5d8c82676ebb8cb8c1e4b29e1a43ec5ed80cda90f53f838ca3e98d167c686e17225090fee818f4eb9731137af1e9ef789710845d68a24487049c94611f0c3af72dc16280cd24fcd5dbde0268ea2d4d0e4c10a5f3662cbb05a13085285c308279207caeea59b4d6fd5fee98c3e74d1cde4e416c32994b628095f088f0a2aabb2498b065c53d9bbed54db13d2887b9c306efcb996243d19d50d7d2fc7f896774d15154fee0e7adb1158fe260292f60194fa88410e0dad7963621f22b13bfd076778724101962246e9095c1b13a9b9de51f011714b66194473ede2e5c1f8f975981db21285fd6912a665dfa7b727d515550fd0f019058b0b4200ace7b2c0f0ae225d722058967fb4110a0661c63bd71f0bbf1374b8b4b100532d0e1166f2d4f33dea00556754ad71f6a0ce4baf4c901a14e174cf002e99c1ba364d5a7e5ac5c5eee23f6e657a979e4510f1c8158fc806e8a99e53e37cc9d7592bff835869d389a268ed98f9aeaae19e5b57a78b5c0db7867152df3f84363834b0a1def7f30a1cf1f2abc11a2425a8d0f14acc08bf794ff74c9fbdf4be7be2e38c8636254d929d910050cd546f05f767aa1d5883b504b4082dd3414d9f694c992d381bf643bae02733e3725471ed4656c775abd2cc76b1e24d92d1900f0a16316b401a110d0f438fb786ee071f9cf0db523f3124a3bcf834b02a4d29d51085f36deb854e63f5aa93a112383ce0387a81641039a58a810b91db338123b82f430e572bf2b1e89f4b2c4b1b6494329d24b28027e38e612cc79b6e81dc42de0b3763ebb801c3accf8c88ab7a96e9e858b5aecaff6ad8611a416a06c1bfda61b6ef5b7589515476a17433304942a2a37ebd0b284602598bb2633bf8d303c06fceb2fb0b8585a03bc7b88628462c1619cc326b170dcc76dfb347c28833ec329c5401bca53f99578e6e115418319f2f689013998dd9a4ea78f752193cd0b4cdb26a1b4eab14b45b517f2498ad83fa184d4cfbf1ed18b372139233b07b821e4d1eeb3d202e9bfde177ca63e1683dabb27263dd58fa79b69abef3c8c13484378410d10966bc5b98c98cfccac6b948a6150bb749fe3cda2ea40255cf207dd020af7f8fbe16f1a05cc5a89e50a000df584a9a610c214b35cac52dff972104821306e62bc4172c2d56c21112dac4ff55efc525ea140f27c12381af7f134b2005803f428b0c32bba55f39e6d6ccf055242fa5632379bf47eaf5b075c5e57784e1fbd3fd7d898d551ac220cb85d4d8d3b48d612757b6b2346a5a7216e25a46e14019ca01e37238e3a31459b271502153a9416cbe387fd8826a4adfbd738dd3c2c356a2947656325df3a349f984ed20c72a4340d44b66a57a2b20d93c3320472f9a45431e921098a958a288e4e04016d10dcf6dcd8582040bbce8d87a757569b061d54f7ff6131ab81e15599f8a23471f09fbabd67c42e9ece680c41b59971619eab318f06ac67e6f091b62c897dfb934f550d080b71ac3c89b84e137739b9fc388942f7ed39732de7c0a47ba9e3cc6dd418191a3f79aa890433fa4d070ab4bfd4d5bd1b49b1c6ce63a6e850e0e8fd62238173b03416b439e05b1711d35e7a3b31449921b023ee833702378470e362e01b71ffb5c2d47829360640458c08cd49e1389476b7326dfe84cd78db427727b76e7f3fbdc7282e0f6cea615cff136f98921f49fd43157f07c2cb4e182fed0c4e4b554f47e5d10ef43430d18726f767114e5ab3569efb1fe9374e165d2978b9a01427f0247d293253dfefcd9384de45c9bc4e6ea568cc9c5cd358c7234fea4a96de607cd393b87912b66c42540eb0f93126b042c0fbf62fc08d6b3079e9c4bcb74874a0783f68c6114bad9cab60761564c3b3545c5ab91f704370237463f006506edc01bb49fe9968bafeed45110f33bb4b23286e8f9cba60fa95ed6d0ba1a9236d433b3c3154a167bd38b36be82a10ed1168a71fcf2771696b91776ec2319a095a3a96e5e40950e34b49abb55bba76fd5957e3570270e97166d76de329b005ce41247a5d7091878a25900c06306f2e033425c35c03ef96fc52b68d28cb84f66976d2553bce7110f0b114f6b1d2aad7f4c4c3efb15e91207f92c4bd434ceee8d87bc846c48b77ad4b5a462e984bbc408c586c28ea437287376929a1efa51ce7d3d6c743f1a15529e8b7da9448e4d5819358892700cde802517bafc064917723f13ad291b39f3de0dd47dba4034e12dd170c2c1114b7097ed2b7ef2368d4d336f7aa4aeb9ae3f0f03ad1b812484a871dcbb27b534e5960e8f953c9ef78f00ade1ce0476d1972f53b13b76016a6980029fdacd5ec4a66542ae4ac57456ed7751e6e5083d2a8dc851039e2a461e3752c78cad50ae335932c70cd6127d9f03e5d926a9bb73437c29ad7b16eecb4b0ef4b101c19775ce6a858f06964d51c026c3924b9715fc53e41cdd7992f9373d30dc437ca1e0a0789412c579bb4fb5607e4e7bf6912f75f59fd5102aeb626dc0bf3f005cc7eec71e728c7de93b4b459c242e1b8e9285852a8162f69304fbe91837867e8434eb7bea66b7a89e6ce39ba03f9ab700a89ac100e95b11630485652f4be58efd7df8e820fc04bcb2dc259f872d7fa58ab2c30371e99471442ec303b3f1ee74bdd2cfe996f28b5697fef2ef77b86ba230b0c424becf4cd155055603736bea845336ca73e38d27881e3ca2c3133c9a6fc5c41d3d10fc7e275c708a59ee5a565fc2600d8648724bd33b969e8a053ac7117fbfd295128d0ae4b4b15f6e203499930feb9b572b37c926abef594e66ea80c41120639f090af4da637d210267a40f0eb63e28d21a1a9b395f5d5f8d140b5764fcfd007304f20d655fb7f64040c2b3704f0177e91d22ee060c6d9601b0ad6ab0a8f7e54df95ca07e55cad2bc07a55b924f87413887672bc1b198c2060151e5ef2bcb9a830e47f0bbadaa1e40ac3884be6eaafe3b8af24c7e96b90b98810c242433b5ee17b6b85f9e3ac62af78de77575f2ee4bdd0a4d26e56901db84952ac3c1011d2ec0ece2ab73265e46b4e0c5edc1f7a0497957134336e6736ec6ab424971a15c9c93189f94ba7be568d8cfda8112a5505538364fba101278e7b034c03369550f7d0cb9e45113cf9566ecb3fde71223f38255bfdf641b62f36750b1adf054e985e6591b0f8c162210e241456ac23c8894fccc43dddcf2ccf6909642cdfda3663beab060cd3ca351a536e783d5a2433f66527c0d8c479a06c4516caf2396660d1d1ec4fdabe5afc6c6ba72f0bfdbedbb76ecef25e96c4ea2309ab7a834b1a477c3bf71ef15b4951e122db1dc4e675dd0392f3d724a239a39d039d5a382c8cadc92c7445aff78f8b67b889b755d8b179157d4df078e41b769c47190ad4db82fafb171c109ed71cbcdef48dc6994fffb8d3239f03f4440234d0035a07e39d5b30561d710e69a0fd5a4fe380229cc0977768965e18852881eff4a000dbabe43ed8e5fa30ca403691c27d3082ffad6ae7bd92a3f9740de623c7e903a915155485f4172fd1de7a18a08f9bb16fffdbcfda77e993d0d2922da59449ca900aba098509369bcd368e2d961071884389b2b269e2c771400234445484080f0a45a6522ad56a457b924ce95a45a2521959ad860005d1fe8e18fb8454467425b4da84955b10908f102150b75464c88d112f802455aa95ae957ec56209f1afe4bbbd23f9200e9188994a133e4384040d7184109428f8a708cd49d779df078261a83dd13d5d13752dbca1d6b5590e47ba16532a954a25140a85e281daa13aa16837cc3412a96432b59c74edff8442919e58fa92ae89ba860aad2667aa18d24875025b30d82c8634529daa378ba9411ad1a0b972ce61d775b318d2c80b3b4d7beffb401004c330d45acf62682f8e46e3389248a55289b424e99592356ad4a8417bd3e98442a1c8542a45f2f0f0f0f0f0dcb8c1c383e3c5eb7baa12217cd624052c961d1e6270cc38120017b0175290518fa017560831c411423ffc0060082bd5cdfed83835861620b6d8ac879be9430568c860d80193830e128fe746a9643a9d50a877adaa1093e984423d49ce4a93ae8d2f2e249952a95eab55e9224b2c564bd76a904bd74aef027abd60309f9f9fd2c6e7e75d2bb717675d6bf9ec8dcfc2327ef9e2e234fc63b9f3e232a3060d9144228de3972f2ea4902c85a4c9743aa1308a2453a971fcb2468d1a35be30d456d35e1c8d46e3cececece38eae8e8e8e8e8e8e8e8e8e8e8340902f48a312a67b60afcd0c4edc74f9092204260fa28c120793c0cd1d19283c5adc78605478c0eeb53d4b47654ec9801abd1a2070d1e590e60ec756dfc374ddacb55b2788cf1a2b96c25cc715de78d5efd3e10a4bd5cdf799fae8dff8561a8add65ea9a6d162c48811c3478f9d9d9d1d1eccb3338eb8495dc2c8ea35fb09d29143089a128b0b0e529123416e85454b0a54000493c5e879b1b1669830746ec468c4ecf0782e5da3bdeba56b28182ca66bb48ffd934662252023b0d66853d616d9cf5dbd4bbc4750b4590c2ef2628d48828c4ca149d022588bc57c7109758df6a10eb528eada0c357ab17cca250bf8181a490b5d890721c410479486b45a86407f7ccad5b3984558318060361c6c66bc784ca6130a4592295d6bf9d47d213e3e3647ec2d1224fefcc0708831a96be593b194aebd4ad764af2a7f1563e95acbb35ab6d5f24a5b2d4fbc3f968ae09f170c09901157cb87b512853cec65b3d96c369bcd66b3d96c3756b2bc982d4248a248092b41acd50f12232f73c1e1486988e7744291642aa552adec6ae595ae5648622e1b14ca88a952ad740df62b9627e220244882ca179298cb46bca531fff80809f548879dfec46ee323402c7305a30931c411422e4fc29fb8d91f5e64a4f5414362a805984cf84fba5642a14812a774adf5a9abb22a9557aa527922f9a3909185a40aa1b956370624a260364738b2092b3f5a4e13f24250a278125ee44a5881d9945e2312d70f50a9643a9d502852d7723cc922e589fea69a0b195a9d5ae9d8d1e3038542a150281a8d86a2d160baf67a584cd758af6b29fecbb2ac7953d7666fb278621502d3355a2bb65ab578bceb9a8a776b752df6f6de0b335d2e988f520bd305c3489121438ce89015c9c1a2055596a6aebd7a9e85a545a66bff321a9792a96be2832c61d5e2bf30c6586cd8a26be2bb84578bdfc35b965998f50dc74cc868bec060e6f83e8c5e60e39364356529ab297900b55a4027242d569e0e1815058ad5bba06bb114ba5657f0296254e81acbab589d1e000058e189e3bb9022aca50b005684174511305c588459c3f4a0b977765f60c18ed9fd17c6159e8e971ad37c0980113e6acc9e2324c9a3a5857c1e2de457c008cbf57ab15404915caf143ce028a14a65bd28195d5bbdcccc0b5debf9172e18306874ad7c9a1a5b53e395d6d4c8e81af994d6eaae6be3bbb5bad67a0bc38505c991a07aa4452ca8e5c95558cb4aa36bb4a7a9d13554ca448b11c3c91d3a7cf488751c51344ceed0e1a3c7c58a01da779ef77dba86fffbc2300c533695f24a539de824b943878f1e71dcd9d9d9d9d161a869af457134d235fca351d7683f9248249665b1bc5256273a49eed0510a499af625d2643a85275dc37f2251ba467b14499224f9b2af9757faea4427c954486adaa754a16ab5d235fc2b96aed19ed56ab59c245ba14bd3be0573e91aeac1d84bd7f0bf60652ce624f9e3a74c376e504ca3a168281acdbbbc0a3a891fe732234a0f1d2a4b3031dbf1820e2f0c015d21c4103e3bc2672e042588911e802451a4849527644a7bbbc0cbb533040f56e8c70bd80d341610236605b612461130b21a2c5841e325071f2e6020f2834f0658a4a0142ef0d4e81aed6b62e81aed63a81e06187c2463663b789c3c2f955034ac22c798198d1a30ece09183a3c293a63d8a2453614ad7f0a752a14ad768af5aad56a52d4bafb41c4732665662852b4d7b56abe572e91a7ed7eb5561305827fa8f64cc0c0618608081f631f2bf2c4bd23449161616f2c5bebc78a52f9de82339923468d0a0d103ed652d2dba867ad02574d135fc2e23ed5fc6d96c446151a0f04a5174a293e3f8128e65dd22499215488c1816a817e0e1b9e1b41b4eab4c9c87a7f4e3c5908fd1e34263ab4b5426583c080fc1103e907abcb0228922259eb037fbc312b145ac111ba4732aed103a329043070f9f01519508d102c4476ca787db0c93c6a8023560d87173006a81c4bb81b241b980c206470c0beb0504c41ad1ca1750cd8c6cc556ab160f1f97d86a07cd0bcc74b9603e5a98ae1e24183f3a84783c0c69c9c1e288add50685c205161c312410eb558e6835b3552b36c38587cfcb0e9a9d1f17cca4d1c2078c1ea41ab79e870952811f5e7512b224071b1b940b2840161616f0b6d89616afb4a5139df50202628d68e50ba86646b662ab558b878f4b6cb583e6458810214284542142901829f260fd6109495948822d362d360fba84644896147c09c917142f2139b3f91b27713c0813923021c9f2375efd69ac1710d08856be806a664f83c64f908a07c3d006e502398e248e27e95acc93487fbac97bef55f1477e682968658a183162c4700145b94447e706be8143493d00b471c45a7f626d220426485999a0566e3f7cf613a42404311e2aa24c392561fa80d18344c41619528447470b8b1c52d0535615d45a44959284cfea508c1435b6d60c971d3b345a946a18918939461c3a3784368a060484f2817ba068403132987fcbc686a6f517860b4bac050e529167d1e2736b0542d17af8c0b018196964c8cae69a955e2f650c56cba4195960b8f8a886c70e1d397c3a0ffc803e1044813e1e0c71d823a401c5c83ad1c9212b234686cc4aae959197f245d61b376ad083201886a1aea17ca0c43ad235d98f604e2229f5902449f63c59da6dcbb61ee59205bc0ff79d1ddfd971dfd9f19d1d14cd69a34f245d098fd283d8517a088c1cf12a9108c1a645c302c3e50707a948d08d1645582b545356262a2cc86f436c2e7f8931c31c7562228b1818342d5ac0d0411ac1a0699103478dcf8b65968a1c295d36fe23fe8dbd58eed6deab6b18639cc39ccd52e91a6f6eb6aec1d0411ac16891e363d0351c1f43edeb8d891845c3a3592aeb0c1ce29c69cf755d576badf5d65ac7711c59c4e8e8e8e8e8e8e8e8d0747494f83451adf8ed87ff04cd8670c44811f3e6d282e467085bc48c20a88a10d41542402d18a41c4ea56413573766613c3636b418316080e1e6c6e7e7c7577eefbdb5ea1a0d15b3f7ea1a0d63f26180a1c4f15bd758fcc6d1b596c719bd00748de60510838d2106af3486184c1909868bec6f4d8b97a0d5583262039491a068b0183e32249b362107aa8201d5873eb14feac4f677ea84ed536e28da5e89d411100bdba77daa927cbdf72a61041bdc1489224535a0538e588c31f6809c5822882a2528c1ed08911220c8d6fbf138175fe7d9eb7eb0d96f66a0423178266beac49c21121b5802576bad496bf8b141a58a119614e140131dec8033655022efa80ccf9f5983cf7e5071d9bc89e17a8c8b0686a0811324424d0002c7bff31fa5b0fd4f2aff771fb0b0fd53b84c18a3daf4890f43d8e1a64f7cd0c13e63beaa770052c71d0f22b68a0fb618638cb13b768c31c6d81d3bc6eed6b1c518638cab63c7f83a768c31c6d831c6d85ac716638c31c68e31c618e3305b6b33768cdd3c6d9410e088b69b3ad41eae96a40052d9e95274cb6a4723d895ed573030e233c56434e5de6b8ea660a2f11c35d9dec467fed72cc240fd6a8ea3281cd10046443122a3a1d1900cae1d1bc5886c277bc865a28ca6d4d114aac2ad0bc69190cb9ca326234a435d8a3000d2b7f65e0ddc6bafb5d65a6b6db5d6560bbafbb5b6bbeec3786f775dffaa93eae4ea303e724dd96cf6ae77c954d65a6badb51d47c41195600052d96912d9f8c16d65f4284db73137446758ecbf85bdb5e74966f5b1b73f5219d5a7feb435a2137405a5c96f4f6ecac6e7f7c4c60f826fdaa858f9ba6261f0abca32895a59544ea12af0672da324b230b01bf954bfae9465cb3c7910710d8cab6f95366c15aa6b18597fe3e595ca52529c5c9636f593e9643a9d9ebae9cfdfa7d936fd39db27255138aea35cd775a4fd3abbfd728d5de7d1cef33c97909326a4a7de3ac74dfa5c8e9b95ff4a0c66b59229574b568fdf55be7e546ae244a8f4ad122bc9f8af2b2ea3cae5682b01fb010b4241fa59f185f1f5848d9bcf706d7dce39e7565ead5a7995ffc6e65a9e2df3f584cfb009fbe133fcbae233cc4a924b71b394af220b833f56fe2082a564b7cc92e5c4c2e08795ac261606ff972c26168695c4c2e0d71555f9aae233fcacf2d584070097e68b8acfb079dbf8cf17958ddfbcfdf018411e87f5f8cd202e533e7ef347f5e5e36f95af0f9f95443f7e16133bf38f9fd5c4cec41e3fcb899d813dd6e29faf2b3b56b21e56c6de2c619f4bbd5949f6ebcfd2958d5d0f869dd8fab25cc2facf1fcb0fcbff2acfcb646797799e76fe5649a98afcac72a5da1d558173aa3c452c6cfc647976a8f284fdd8f84f2410364aa93cad124a8cca9335b4f18be579b5f8c439db272bc9c67f92409cb689f37565e3ef48206c12fb149f184068762611eba3f6a032e904748cbbfc61fdcce960a41b2788237430428023da3ad83ff9cce6603c5ddb5c72ffec5c76e69a8e016b59d0b750401946df14090d684aef8cd5348066908b73ed9f3408dbfe0e31db9ab6d15e72ffeca1f5b66f4975cd366e6b5207024865df077e1f86dabcb16179d623b699fffb340ddf7b593f8f87bef1ebfc69ad31d65a9767ccce3e7e1e7e6d7a06f0c76058f5f75a3bb1e78beddfe8bfa7d7a33bf4bcb0e685a3696762480fcae0dfbd87a8ff8cc0a7c7e19fccd3f1c7616c7636c2d3c73c8732bbd09fea934980d0f43eb3d5c5d8991b521886efd5226a8df79fa9cfd367fd234fcbf0bd67eae03dfe2ed47fc391fa64a2bed3e7f0fb48ee2d2a3c654dc5dddd1d67d31bdfcb5a84fe52697f548e3cf12f68be9d11cd189279bdf00bbffb2f7b5cf8fdf975dee7cfd37f6367688cad469c315b7fde995f65dcefbc1f99670cf8d508ef39b31a5157e0d746d41a9a73588da835dabbef995e28e36af37b2e3469e0fefb1bef6e2ebd4ebeac9ffa8c98627be6e9bd6886e6bd7150eb97c15f7fcf0ebdf20cb7a74beab3d003cdf3f6d8088d30c2c9aeb7f3517b3e8be2c1bd97de5affdedb7d750fc711e4bc191be1e6b87ae2ba1f18739d97e33da76b9e876d675d2602a1bdb56a1bee4bdfda5b041d220252d9291669414aad52150e41ab65e8b62db4ddd3fcf84f4ba5b33055beac5dfd985acb8c6dcde50f226cf818fccece58dfdf013c1b4a99b26d12bbbea552697dcfb4543abaef8de386325158d6edf5da2b9e38c4221fb7c75a6e0a9728bc43184f8a67f23bcecfdd6bedb5f839734990c7c9358ee2e41c8c679773309e315b7606b98f1f8cc158adbd41762d3da6eb3ad90db2a93e80e35c251626d3603c3bee9e34f4126120223f8cfeff83cb67d5962a2746d5d9b32b47b46be9e76c3bade50d55e13118fd4fea1d115b04a432fb9456776b816a756befc5b8ba6c369bcd66b3d96c369bedb65842c42020940861e3430307244998d846ec34870e1e4028c1e106aa26a9b656bf97e56eefb5c1d8b51262edd5b5928b4280eebd38678eeb3a9b15c699e3bacef36c56b534c6ccc81b244992240f0fc9c343beb804190982b584b4c87e7cca95cbc26c9c4ac386cfa6d42025d1e6fe8b07e3cc715de779fe72d96c369bcd66b3d96c98765bac227088222589a21c3a2a0f25364742c3b689784b63d10d140d87055c6b2fc6ae9ccb97ae912d9ad16cc17061391254a4c891161f5484454b0ce3cc715de77926cbc7aac97271ce1cd7759de77d1f17763e7a8020582af9e8b1b3b3b3b3b3b3b3b3b3b3b38323870eba82c76af65222848e1c362d1a1697ba4465a236d12268e35498ccd5636399238c2337441634627678eec539af38ce65833127c4c7133f67aeeb3ceffbc0950dc7759ef77d20b8b27114ad868af142d67b3ee3388ee3582a8d331d5c9ba880223112234aab45f6b3b2c9a1a3aee0f162e9043929c1441cee2771ced3b5f1bd1f100c2add2316d35e63a9542a956ea954725ab964010f629bcd66cb369b67b37558837a091f21413f41ac5511488c380e31806c576ce2c64f10c6610138eb1af9c367e477ba86face836a0dbc2068bd82a027562a567091ac6b2e7c8e71bae67a8ee63b5d7bf9ce2f0b6663c3da91a28505b3d101c3a5d57af560d1e2316a7124c8081050500e23386258681c87a4d3b5eae3adbeefc5ea3aeffb40300c3b9717182d4030d4bad6f35ad4b5d58b235d2b7ff41ac191c51ce99af82321a3ae3949d74a4f2a0119b9e348d2b56ae44b362653cc656fe6b80f85225329956ab5727981d1a2da62cc7079d981d162341ac791542a955c5e4aa6d309852249329552a966cc9831c366b3d974747474747474747474747470e40062bd826a664d52a082d80c1e2f343b954a5da232416b1337fff113a48487214574882d2c6e3d36346c1fa3e5a26207ac46a9070d233cb2219dd3094592a9944ac5623a8b49c3348c693c761859cd5e65a55297a84cb4685c7e70908a04899f434785c5c8765c3d2fac19230c9d2337622c68f0a05229d56af5458ec45c956651367f692389542ae9daf82593c9743a9d6aadb5d65a4ba57b4b2c62489224491e1e928787bc41ea94a8d4252a13b4fa8f1f212e2d487e4c212851586a11ad18319bcfaa9c01d341a154ba46f3291244fe90234046ae8febf55324a8c512b2b2b9e2bb5e369bcd66b3052a9579eed8f785874b8f161fab97511a555d99678fca7461db27cb1305cc39db76547afb3588a95472d23b6954a39837d4547ccbb3ef5b1cdd585e3a134723f3e45187c4b71f8ae6c95d53078ed35cf8a1cee1b8a73b6c317221c7bdd6399c19765687eeefdb325761ecde7bad733adbbdae795ef737ae754e679ecc1a6584818de23242dbbee8c4cac83db60f9242af994e1707e3f9db76dbffdbf4090b7cb6bf537dc220f9b13c9d519ceebdbf717faa1fa499ebbc0f3c7b76564155787d3729f51995f9ec6925853ebba68c08f4517b3e164780325b6b752f6a42e6325df8a3c538ee1befbc4d49bf6ba514097869feb36ce1805e76f4fa7bebe2b206eea9c9833e67b66ee7326eb6ae79808a73bad9d1da955cb653c6f3778c9da16f676eac5721ecd609a11328b1a990098028da54e8043d6c9b6fdbfab0eb531bbae75e06e7bd0ddf7b2fc37baa6b3bd86c003fbf8cfce187660ff073a83fcbc826f82d19b74a65d70f6b15d0ac4416c6ecf1bde7850f8644c0245f13271e93cfa4da86960ddc7bef993dbae7cc967dee7b70284e4bc6f7de531caa6d08a0389f29c3336be8be2583470f8e0f1caa6b685953c614aa757874a659872c51c55376c5c165cb86fcf8f1d0ae9ff3672ab9e80ee1245809918d99e024bb16d919fcf552d9952bef140b63037eee39b3c7c5a1383519556ed1ae3cf2df3e9c6c5ac0c78a202071c3b9e5bf445470d1ae393f369b9c77a8c40f566d03fe6c9e77080115e70e5d220bd30d39e98a441af4e006446e55a80a591e48e901945b151272f259c98215a2dc70ec532243496aadb7c9ae6f89d8993b5422d28012379c4a71aad00db2ab1d222aa252855e39edcdde76b54ec0aa65b0db4d4a55dc5b9e54a80441d8f60eedf30aedfa6e85ba9a1ca84008a7680a511dcab74d597c783dd7c51e2efdbff5f1d7d70177d5a494721ccdb4a3b4a34f29a5945a4be9e590784225cab625edc106eb121fd8b6acd9a037c1876d4b181bb44b0861db12c5066f0fdb962e1bc425d8601edab64c6d9063e2085d28836dcbdf20ed382fd3af0947006b13b4409f3421836d4bfa6483f489136edb96b15db92d68e1b6ebf77875a09f2bfbec4a604545538c97de6b1d7b59ade5329ed54a6797d27bff6b356bbd17bb9dd18caddfca899f09c001d43d1a290796467a6fa330dcf5bdbf25c6f86baeb17fc1de85efa754b6aec2997aa3528674a1aa46ffe98aa570f1c246e8a450e8108d42b3aa119f7e0ba5493d7d1714b4267cfa2aa8cce9e9b3a035a6a7ff02a5293d7d4c6bea4f5d41696a2db1a9aeeccdf79a3c32c6713757562894d50adcd79f4ae3debf56b1f54aadb902a5b1ef4c0cd99c6981b0fdab942849d8fe2a236cff5511285dc10ac2fdcfb39f1debd7f1dc60b15ca08ba03611a168fd18635df2dc0d134130aca5ef7ab333dd77666d62529f552526ecc66efdab3cc7fdfad7ab3c3b9ebb5f5fabc0cc4a85e5fd67aecb3c282bab50eb1ff67ab9fc0bf6b207613218acf5fad8b75ebf322ff559eb5dfeea9cd7c71e8cb55ea797cbebf558e7bccc187ce61f33afcf5a3742b19618ec5f6cc95ead92aec9cc16634bf6fad6cb6c11e0fff5af370ba067187ab5fef52cafdd7a11e055d6c0d7d7075d6585f26ab56e77637fff9a27385485eebf5c0ffbd777a5eb49e62d731546d7ebff573ac7f5b007612fd7d3fdaf75edcd16a3eb5dae67e91c97c900f3faec65520d7bd7eb5d1f2b299dc14cea33d7b35eae56885f158acbb8fe05ea1ae8fa1bf7aecb6c11a0d562e91b5abae6b1ca555969405588382051e5c9240452ad45e14bd3f0f9f5bd0f7e5e16bade1de53bfad1e3bf2fea9cd16393455d3a677447e068e48dfea57346a60d7c5ffa011816c3a4aeb142ea32985a7a415c7e956571cba6a55fd4aeb7339e655c49b9d5e55c57f7c24da8aa5d1fa66bd57cc0b65f4b1b2a91db5c59a1388c3f8baed9bf2e83bd6f08871b63fcbd7d500806e397f1839ff19b740e7ed0bce2bbcec19884453cc26f750ed6d95efc259d733f9b6109fe059fa46b2038c232ecb7ac42da001d149789806a0b0de1cfdeee762a1c2222a0dad767d484e959f883eb6ecedd4fb38bf96fdce66df12916ea9ab5df9ff2eb71bca7d7601996b656986bea9c6e5bdff5fe48e7dcaff64d9d7347ba66ad66c9e675164d7159b74cd360936842100fdbbeac25ff4ad76ea557ccefb74d597a603ebc075b008800f498187c37bbbb7750c8dfe59c04103be3eb3246d6450d56f277392fd9d9f318da0319d90ad5d0849cf3931e92b033be1e83a5de75b72a37c40cc137dff080221a96c80e11141c29820980348107406801be37742cdf05f2bd297c565f50220349688009459001117e30028f9ba5b8e112a1c839e39c67f909b79d77f6cf5e7743510a235682c880107c60420a47888293a53c61c21362ccd90846bacf19c5f12c84b5a68903022a332754c10954f639a3f2e37852b98ab0c54d9f2061689fe357eb4beebd17e72f2d724901092b1be0c110309002cef5818f11b8bdc3122a1a68c2480f4131c0b93db89fc265ee08440c7a38b281162401089cfb2f5c86f260dfbf49c2f6f6fd19eecb70196a837d9ff82c61df6780cb605ab46ffcc6679b3ef1316297367de223c4ad02d2a732dfb5d65aebec854d7d17b556ea5efd07a4389da5303ef3c725c5012badd4134f1cdb86a3dbd774285e073cc5dbbfdc5b4eb5b9e7cc93fb6c826557524a29ad6faf59e9ef6ac6d4188ca78bedfe2f5cb850d1991b0ae3af87ec930a51228b0593e9e433bfd7f4b7b421dbda548e3e231fb427f7eca23ed1997bfad3ee52cda9a321d55aeb9818105495a62f9578a74ca6af6422ffea1cd3df07afe9e926ffd335d26c319a4853677aac734c17a7ea06cf09eccb999e3399678e7d9fce469f75cea5ee22e96f5c55b6de4477ea6e3b9a54c5c8f45e324f1fa6b25215a3cfba467acb954aefaea95c827f55e22825b003cfb877a82b5426cf4ff98308d3a8249fa7dba99baeb404f52bd4abf0a74a0a749bacd1190550190aa03335288cd72435e84cf50085f1231cc6033ba84138c27bf7af443e834fbbbba7e7fdbec7250e0be37f2a797ce65f8908dfbf3229fd2d4b6f2acf9e6dfa1b2e53f23fef735f32a938eb2e99e7fdd1877a00f7497f632f1856ecd78634cc9e38fea62e53c79a58d2adc7b36260fb94ed8ec4955d77996cdf6cb13602e8f4de4ade50530cd4ce802a00ed011da25162464f3f060d9511ff058c176cea8ab3c5ece5bd9775295a33ab2bfc4d9b3e4db1473b738385f1f1861f8c55463663b833a4b0498b48d4c96457fba445dce392bb01bfc90e00ff35c55817b5a68617d8c6265bae441a4762c6ba0c65fce9b29afd19e03230166aa05290b2fdc3b2fad0993f583a805261d32704fa472fcff0c177ed2f3e587e1f926c6705b090852d6cd24ba067934c098ca68dee870578d8239302abedbdf8a249e9ecfc5e9b9f69f3381353cae2a439a83a16c6fe356558189b851f543840ae8e9d01325a18fb173f90fb40a805422d8c9dc1e66861e85b53a5da66aec298f2e13d6e4123409a62bcd6da5bca36577eefd1139f6135bbc7b5e7cccf75657dfab9bcb9bcd5f260a4760620959df8b6731ea5cc461a259c510da55432b55a7bafd7a5b8efba94cc90fd52df79cf21718129df73a4ef1efcf085d13ff07b610c4df6ecc2111c6f38f7e630f999b8509222898cc8a6f98650f2bf7e6c09f3b7e89c96cde33312e7852555954478b5f14f95ca34690f94e6f230125f4977fa4c845bcbc4fa56c284c8724d9a34b14fb8b5e2b609ab842d421d7298fcf608368999948fa282628bd8d956bf2b75ce7b38da99daa40e5d69325ec157c03a3474495788fb71222076fe2286d8f9f1a8635df59c798e2a13ecdc79f7dd575a639974e3a8bc3f5cc6771e4db944f7bc42f705e775c1e90db1f3bfcef1ee2dbf65a25fa51a4f2f754ec3f7e02c9c75f7461dba1d2bd695489d506833a6ea908b14b70ecd3851765b64239e9609780aefcdc2e49fdd1b91798e9c79761d95cd6083481153ea134aa038959f2652ec950bc449d7fcde5c06a6338eabcfaf5387ecccca5cfda962fd39b64a2d935a9699f5009a44d9f9af949d1f659edd4d62e72f99f7f6e55785a52db9f2bc3727df895c2e52c8f22ba94a529c9689bb7ce68685392d1317fbb44cce173e8385c98f7acbe4de6ecc6067ee0f87c94f324f953653e6fde1526f1bedd47bf7169ae73de2649e578834cf0e6596cc8f04de3e30053eec95e25054d310be779df7c478eaa73b9b5d107d6fe2481c8961e86a857ffa93a94389645e5387afd4af4d17369c57de0dfe78c267b91cfd0f2244d3c8eabacfddddc3d27b554971f068bb14f7dcc7bdc7c46172378eaab7964993f1ed9fdde6522d9dd39927f71d0df6532605eafeccd3ddfb30c5bd529c534ae37527ebde7c961f9ca53e2c8cfe671ddad95edbd9b78db67defee7c8fd8f90a5d283b135998ae1b47f354a93ef37c2792cd6083481153ea134aa038959f2652ec950b044b68af6ce7442e63fffb1dae4ae7d8931bfff43eb333968985c94f0245d3bdaaf22b3b9f3121994c2c1326df9ffea069fcefc1aeac6527c4a75a86512783fed37b894496fa759421229719cd7ac52476ce43bd92aa4f7859afd42b1ec493e40761c254f93d57826fed06bd14f5522051e952a86099d4fc9649ce7f79a03395c2789fbbcfffab1589e4fde96d4fc978c6ecd3866cdf280ddd19fffde13275e7fcf4bc4eba4ffdd93d49e77466d7791e67b62870b7f7b6d1f6debd52d3e03df7208ad02bedfec0c7a972fc6efcc6cee4a9db336f178228c2eefef059fe50f563abbbfbd99867fe3bdcfca2ce7193027577a635bdb23e217265a3d8f4099128fb6a19c6373d984bd483a854d7710f7adddb46bbeb9e7c92a6e17bcea440dd29f3f41f7fd43490be336fec8d932d029c4acf537518df64f2d42a84cb7c9fbf5492c0cfa379e347a5a8cb330036dba9133b7f3c2f4e17fb3a8c7f5885cbfe9e9358a7b76231e35bc91300c48f8f3e0eb884fbafa438b4b44ac6b312551ed04a44ab10b6ad48b8dbda3fe9942b65e75b56a22ae50aa367fa77235732c061569ea9e333fba44a64b3691f9705f0192572a24d59eebd40beb7dfdbef2df7cebd73efb79d2b2dad92d0a710055a4369ec675bc53a31ba14280d05fcb385c1ce6faf8c9688ed0668dfd339f63fee7d0a74742ca87aa0af9d65a8bb56f72ab6caa5b2f39f96c8ceef55f267af82daa87a5e2add3eb1d0ce7f4bb762ad30e2b73557b967aeb2f3e94976fe3327e1c09c3d0f2cb7af70e7b8efc107b9f2f3429b5d41f3e386fc2ee9de3bd356613c6d959df3955a85fbd33ab1abb80c67664d03f75ec565f25b2bf60aa5c9f1e74cce926e3608a52afcb9db0583409fb8369c3ef14000698a5b7fa89c58822261f440c491f875e1030143101cf5030975288ef48fa3d723f307150e10110811dc8f5efcd1eb3f3f53296e8bce14369bfee8c5b2b330f875893f8c79efb9c725e54821f72f8cb8f3bc0e73dce3e7e8131228d9987bd7350e575dcb165bbbc24fa9b7f1578aef6fcae283ebc9ce21c107305224800c6da7f6b501dcfa637c73ec3bdeb5744fe124a8d57b9ebaeb5b21c6fb76a62a57e689b767118cdd7fdd0e5de6ac94863bc1e77400294da53ec3949edeaefb9ad73671f13ecb18e37f407d3097b65a6bad6f4b6c9ef6beadd5bfbef52360cc4c48d535d9370a4ef6a9fa0019b065dbe29f31fffd4a3b8a84d1be0dd9be3238f378cef70601a45fcb4e8c5fa5dc33d89992c2dc87b1ef2df7f954c5bd4cc65b3150975c128c6fed8f96885b01043d6bafb5d71619eb5b4ae9bdf75e77ea36de9dbabb67a7945e9fe57a6badb4d6ecb5bea58eadbdd65edbe18ec36567619c526bdd1495b8d984e109c6be67cdbe2cf60d800a001785abaeb827ccbe2cfbded3dcf70cf7ad91d515f76c6d95cdbee5d8f9922e3cd59a99aad6a48871f162a41abd76e927853eab3535d015312e5ea0cc31c528469be20bb31c7d598eff25e96365e961a5e95fe5698602c8a8218a0d5485ff9ec17b7fea7d03e8ccbf55e65067feac120274e6bf52953614e07bf03bf04f477d2e51a83ff377cfda28fdf55328f31c407e3287ad51a4793a0075aa50e6f98053793ad9d87a1c6f94ca30e421955d8763ebaff52f8fad5f34290d776baa04cac4941d6e046c76e6b8ecd96d6caef7bc7cd41e6bb590da7d7f3f2f5f4628a106fef9b273569a94c67bfa2c5f771d0c2fb6dcadef3ffb52764dd9ccba39f399fd6abecfeccc65a8def4090b8eec5057954ae53395162fae55db086b9fbbe38a77104723120e8670f082ea84ceec5b1fc1f87606436b5c6fdf054ad37afb29280deb536f7f05a521df9ededa774169566f3f00b446f5f66728cdf8f661d09ad1dbafa134fac5b74fa335e1db8781d2806f7fd31c3a536f14c6be1d4d1f8407a31090e06ab1f0f64fe5ed4fa24edff62fadf4f65789a6512924b7afb6c39caced418c67bdd59bbbf59bbdbb76a87a82cac467f6eb09a84c114c5ca6a68082a6d0465813bf7b5015de1eaa05a80a1eaaad37aac2be13f1329fc96f4ba2c8815f6fbb96ac4d350d768fef3d690107762d479b6afaa488c806f1eedeb5ae894bea0f87b11f82548fccb3678f4c417c86f42ad3971e34953548bdcd6034d91f7fe3b8d299f8a452e630f6b5ceb95b8563df540e5207eab5ae05a646a8d40a5f27319488460400000000e316000020100a05c341a1280da51c6e1f14801170926c6246a58b9324c8611842c81063000000803100223030435a01d964a9cef42787c22f5b13691c0500ddb7af0f08af2c48a760e163c1923fe9364e380cc641c6fbcfe55aee5f6270e340701eecb9ffe1e230a81a4cf2b528bcc4f5e1e03e7a3d3a524865c95bdb3cc1fe1591cf7642972703fba463e9108bcd60734da82346d1e1473556d16019cc144283df5f2222df7dc9c056dde1b11c111e98a7093db154f7ddbb469b6ae1e8ac85099406a3b5a28230a53ca0e709bf68e7c92587227bb206cf925b376291f27171799694d7864aaeed4cd1146bb9078bbe6fa73c958072fe48680befbd225efb947a9ad22d4f24301124ffe935bdb3e43458eb38729e0b8a1b12ae18a23663229b2ef5dd51b90e8285c4dddf41d27a4e9285eef202c4ddc7a78027e925436798359f3781ab63eecaf6a99af75e71e8f28d1e0b2bb74be2c4eb0434dd2805a3574ac1ee10d7241b09942ed47c17e26b0790b37786e1d7463ee3f835689bdf4ddfd67c74aac3e52eb5c4868616619c0072a09067d624308b74e1781694984f6bc10a73dbb101ad7098f9ed59225c70a98a64670831348ee0f844a15e8b5765814a7028914382704fa27c9d3241c408333526684120cecf12629f3ef7a82964fbc732bf2a4752912799206f8c60a7e1e08c89f0c26f99929dacb0e362928e3f784931d5b2d64f94fbbd20120cbd6f10460323383769ec5efa67772798ff89c96be920f1d5773c9db689d94f1a874ce666525304717ea0cbc86371e220f4649e2bd0292bf964cbb9b24d0be1707560558683061502f1fe8f8fe9646a131cf72371b3e9bbfbc419f3e9b7eaa0f4c5eead13506ba171a6e8697a6d864c3c31da250c32a4e5e7cf6cf8c5106f9a3c8bc4136c34e06a88aab5a69b1e89cdbabb70f8b8992f2f390fcc4c73adf6dfbcef00bf33ff31be00bfa18056c8a37973584675170ce96e78c7a93d01c3d8fd29a70241a72aefef766e4dae34e8e588af10f32ceaf643bd7fc181c0bc3de7133cca3718fc0c7ef43163f4a8a1768ee918b716a527cd7d3d9e86802e058fc81084d6148d0da5c58a2c821aa5850e1edf1fe5d9b416eb1df046973cda39de45c7e632540f94a19a0c079837043b38985586601715d2a0815a650e3ddf3180f4d93005e1e1090775acc92e699698e5713400846a8fd5392216e53720d8764442eb35bb8c1325e2f7aba2ef804ada3fcb17b4a1a249c782c02112b36f3822107952d015b21b4b30867bca0267229678d0248f9942dda436aa3e2b694a26bf794f15b4c82a155081a0a1603d6094a43cf79183e8ceeea8ad0a189d4971eca2ae95f798a831646a207a2e41d349c9db3e18636afb3cba029a26dcc54f5dddb37467d61c46c233425d67135e84ad9a81658adf3fc43b4769b660906dde11c3235a189ea8d023664e411a2e74cb9a4b488583d32576ee0756156ea3f3c068098ac4c9ee6d08608bcd1ab176c830eafa11b1ea000bafb322bc0e59de5ff014ee60bf5b31ac4d49955a82e10833215bda869639724ff3e128b44d760ad3f35768adbf45323f46fe11c0a4013e9ff04f547dac004cab559d599732db7cbd01deb85c28e31b305bf0657ff3c3e82a8a9500bc50a584be5482f59a4bc13ac07ef48990b92bf84c0afa3b0086b6b1c88b48a6dfd91a3440f9a7197646924833463a7deecfc3247ea1aa204a4a7bf7219c6147d70407ecf513bf27de7efb149f86deaa0a6e5c0701fb21e549cfdbc2b3a059db46fb4753df5c0c62d17022b04fe02c528126bab6add93b74e3de0f2890f0a5e9025cdec73a8db438a8b26a29948728072b08323ef760c71d50627d285ffe1a697ce7a4c724a39bbbdcf7ce302e4406929a68250a4e5ddc0387c4bb6a5415da595d5f1306d0d4b47bddec71cfdf4ea3038473fa8912364afa58e398762b3de2d5564c44e8852906edca22d9a9888fa49340a56446c23f48658e8d4fb5996eab30dab12a5da92a420155d611180b1cac69e52d6cea01d8fbcf95e49b9eac70d354304ee4ca67c9af8d3b430525c1615c9acacf503ce30b09463b6eeb12fbbcada250531f5dba85f99f6f239881b21b7e3f857a727371fa616be045804eea1beef0ac128380eb302cee9681962d9d03a46dfd8a1b4f9d086b3d8ea0196e5a952f38fb245efa787e52f43e9bd971c526fffe044838a08a55446eaf2be5ba7707a39c9d98e89477caab9367a0b8278f57e65640eb19f46f0cdb67434bb24acdef7d2a7bd79d3ceccf1777f941cf3f1be05ab86c3fecc096a6db07373b7970277ca05c3ad2a65e35c50aa4d7267495d9f8a5a4b1a4bcbbc880f23a45a2f04114ea27e7be6f00196b3ead4754606c98f53673f9d7675b98734a679f67429cc580ee3353868dfad9858520e9d5e0e8d44e410245a0e9b1a020003d922ad93342bc51fd1fd61868a1a0cd3459f5b9cb4661712fbfd357d358b2f5e7e51c46a03a3374cab3992e8ff31bb2330c62a7d07295c3d99576d3cf6659110da618273449a7d14bc3b9a8e74b725ae1474418a914c5491c1037e3b1395d2316dbf037376616e1555748cad8827d0d57ee426954f41af947f4a5424b92431dc5ed548787d5198690162b7502581d06998bfc374b15c534a39ae9a753e4775cf4683977fa9890de50b2c6e128dec7a291025591bd645bc1dad28f74fa7d59e8aaac4e7ecdf30564c23608926f808188a0bf20ba942b5b62052c9a7bfb8017466b75edd4ee92755d7c377b2df3584cbc15be51dd5a0b4fbed0bc4427ff3ba40629faf1ec13a57b86f7378159f8685636dc013dc0b43ba0e7d6a52e4b3596c91d2dfb1257506a59a4a6d6b9f9347bd3a6e23183d4ed8e28359a7f91ad99f7de7cc1da95771838ec354a6cf1ed1e295d72c73dc17149f2d3979957cdf7196a01fece4f1f0e1c18f4ddb254b1cb1cc7b244a003ab66308087c5b3d717b8fb5e02d8405a66400a0d4e66305ffe5c0fb85f4ac55b3ec3a1945030c6386a5d21cd88b0d501368da89406a213ee532c4a0819fbd39cb99cacb7ed7a8e74c9785c5947f6fe4ac93b76f94978e248b1bd33c7e008b3f9dd10998dae01201161540532aee8c31de74645958b9e15518992ef341585ebf1a5e532b85a22c9c936e90962b5d5fc529c2d2e2421e46f15ca6284439841a492b53503487b1952622189e876e944bcfeb694838ed1596a942fe4ef14423b9f69b232e7f17d0c2e656afa185721b71658faf81b241b54ee9b577017834330bbb65c843f2fcf577d8f368a9ad4fb0a314d41d99a73943bf3c18211a62804baf23c4dba7f0017ed0793471c28c1ef20c6a8d09687b2faccd4e3fc12b4aa0e0e9c59ef03ff930f148cef8586c7447d918372a04db3d1294d231c7137cfb285af52c975dfa9f7b1667fb026fa2ab491fbba61ed42667f37b823fae2eaca4193f62ef331db685ee392893c8102edaa2f1bf8a8d05dafc805ff8dc4d443b41448be19b45d7d3164351b83f0e3baf9b84e123b1c635e0d178098c6068d3a2bb2dfac9366733cdaaf3f680d001b48171799e023d9496dafe912d8892c2539a4d7e4e75969fb4579c01dd0ec0c6d59fe73a2b725992d799d70aafa7427dd8143c15aa3f4bf6756342d3444a0c9e78de027090419ab6102d0cb15d644916205378d4f7b21628b5fcfcfcd7d2720cbd14361018e35aa9b2078148b029c489cdf4b917ad0d0c2e503935c0468bd76e51ff0fb20751cc0853989f862d2a51ff360c8421cb143e01026c301a02597469541d4e62e4ea8a0b568e24e3c87eed8b755e8db2f9ed3de1bf6499d38520760178c33fcb1b92ece6ccd7fc7d43eb73f8269885dcbc962509e92c5fde53999bb0cf35347f080772db1cfdaf4c253910f12dd06004ee8c81d2cde1c125da7c8325a299bb847694cdece449b6d17c7b27f964af73b7999bacead3c23efdd2a1e98396c358fcaea78d31baad6f93d0dea7acd0a6479d04dd3dd84141c223b527d2f4f7e56a0129ef2f018e5f3e1c3510ad0ff791fd3bb20fc876f399d7bf327fe843d73f80626970559afc437913d3baf09eda026de4f94cae7789718ae3638ece85429e95ed90a0e3df38640dd55e4aa49c899a3ea30efa57d404267e32217c0dc1ff7dbd76e8aa3bfbe77bc4986c4e650d06cc8dc0c2c9987742e6a57cf947e5d453ba338156f5c4a5a11a1a15dca3f7a0d0715f6f9332ea225be84c5dd9a0f3fbc90ad9ff0dcfbb2483b10a42152cd47670b93d056bc9439ed4cb2380d320d44d041c81d9ac885c70dac87d80dc1aa1bb33ae682debd8473c792dc70ba189ee83e58a129ae7c6912ba66c7b2696b08982f667de38a13a9e8e30466d33e74a6185a86ff54a6e693e3eaa015e19450c8d0355b2517c7fffc30f31bbf9668df2a61a2488b1ea69ce70e490d959c6b83ef3a5126a6d27050e3e1cc64c229b6d931efcff62c8fcdc4823db1817858656ecaf942af59825980f9f76571856c03832cf81ad74ae273ab4b218a22af0add381e862ae2ec3e11a2a0fd8ce395238f1355db6b4368cb38de2cf621c2fd17495b9c6b0b9d7f0c311f3e1f88a2721f8527e9ed7e13368c6ddb1bb9f5003d4f93e83b8fa36cf99f71ab03a70c642d2ef3539e272010ae4dd15f33e1ee9081d3a5c5d69ec605ae254549c2e6848b64900867aee2fd0e59602dc806c08f0b06f5a0c346c096bb7c63e8e4a6e8094a04e22d8718ac9c5d9e18dccb4c931cb62e1fbc147752c6b9b9969965c1f65c25fd9a98dc2687c21bdfaab2c3354252f230fc7ade5cfff94dd4ea076d2c72a83d0490a6c68a6e1afa66325eb83a6415c94c3b4b26ad9babb3612c916b85d4332cd31fd251293422735742db1f209c987205130a3fd2493827f7e3fe7c692d622ad2ec3bf1e5f1e2f3ec931d6e6584c0d3df8524325ea33acb92d24a1f3802c9517faf937fd3fbdb4fbb2dc986042729fb22b6b5b530916b3e0706cbb21837cd9a49a517dab8d27f25cb647b6122badf616e223ae96a9df9a667234e5d9848eb76e2007b92a201dc1102435d6369d2741d78d6e2c44087f6239fdc3780a61584f360ba45fb53d9ed27f4c3c954f484d1ea9db63e57a5ef3b1032f8c9d96f2286bc1325802fc305dba2e91ddf0f061d51b824d7418160a1b9d03a5c07c4f22d2f78b0c05364a8099a3ad90ea198b904ea4f376f88a39565d99e9af48d0cf7aec991a4b15d7a836616a83fd58207f797545b39c00280f913bc63dbe02dd794e2f77ea041f6717b51174d14c618c7173e9ddeaf6037fdd0856b11d3962cd6a71bb4b4b762df56c1864e9f5685d1c2763f77f535466bd11a15b5b922b5ac0c4ab8f81946e807113c765337572349260b349cbd9a4383a2bb02188107828565140a39f0e4328393b02087e5cf344c3ed9e3784784ebae0db6f6a92fca0e04700b153e219d373ce00b190ded04f91afa5689895cd57b5487b11168c9a5ed0bf1df28b04f561beb6a4680b4e99b7464f2c136cd745ef886a6e0a148f60a09726f08970b01d2d2cd292796b2f83d43db337a910df51f719e7c886c2841d6a6ac16d0bf804c929142f94c786999cf031de8a5e08f60b16631db2c3b54dd4aec38a980facbf78536004f04fbbb0aa2ebd8b826be699f69aed999ab7143cf25d77689d738a9dc788156a2e736bdca1840b772a59ef1a1f4ed82b0427137902e6370ed00d0ca1eae1416388fa150ebbe0cb09915348ebdf04568256e670ad3a4a1c4f45623abc1ec47497e6caf440667371fb2e141f3e4a0e0c2082b083708fb4b6613c0698305c53b530af62f3f0666d2383677d7912c2e188d6c50372521e16e7fd3ac4dca7d2949016e7fdce1e730d87474f42edc4424e13451ebb1c088dbdbdec265ca11c78cc74031ae0082c598533a191c003457e6ccca26ad81e15c0a9fd46d69c1e58aaf0186620ae857c3f05524c717b9a4a086c36d4edc1586f86fad7ac66c1c2cd10d3ec867258913be810522b7d08292b0a3c90edbc9c8ba6881a4302cb668e6b7008394f3a7a80ae8aa63d9e6b8b507cba72fed356910ac5998b323cc0d8fcc1bdf67530cee03948f36fd8968fdd4078f3a8a0b44390814de06cd9418d7d1947a0808cc94cfbec082193a1311119fd131b73049a668898c79a6e2ca0cb9818b7d6194769871af00c60375f6d592e9b7834105999f18a315859d891ef87b413c6f5c1fa6e6460a3f16c7c4181467700fa676d430eaac2f60d8eac0cbadf1c8bb684176d1005d725043ea914c5fdef14abadc8d4e4c6a68f2883d944a3b5e454b50749c3103f9644c5fd74b14e74a3568fe940e504cc9eb9b0c33a38f06e1917fca9e9d092c5da750d18c7f656cc3150868f842e6b23812f0a1fba327658c7d942a9674a97710987962f12443fcc9334fbb49e06002275d66ad9c7b6d772da957bfcd2d8d47a713f5b5910fd7b1e106a1a093004d0f087c79fb675d3318f7391022d738f506ed6d44fe1d11eb1ffad4da404811dfc51d57d35bb12b29db27d07367c4e4915cca3fddd9cea35bc284eddbfaecd7604dbd3a1a432f0d97840fbbaf9baf82e10cb6f08890e5d7557f49f972775526713542827e802591d809c175268a01dd217fe8f2b6ccb6dde466686d3338f93ff102cb31af4bf2a21d4e61be23bd9945d3fc407ea543b6396f3e216bc880afef3271a1e5b850b6324219964a4710e0f5bac9fd75ea82798362b84b52e609de1b057e98b042be6ab850a1fd50fad85387a89c6f4461e68ac49cb666c12dd912e6e19c4fbf0e815d610bcfbcfd2846ebaab6732ee169a256bd5be7a34b9d10e1a58eba69f64d9b159767f20f879aa258f26eca0e2b0cfae001fae8473853545d7814d3d102e0a6f4ae85f4ce545c35520d167f1ac47cec8fa38b7d80d3ff7a5caa2bb0b2380616b51a50d63348a913c2c2827a926a797bc22d1b54d428773a63d21119577a41464dd6ee1e3f940729c3cde6cd6bf78a58e94caacba25180e96c2cb64b3302e1ed45501e417724a9d52fba17c12150082a061c199bb76ce7d9c48884cd975f9415219e850495d6189651f5c931a8bb1b301b15060054187c2a3257fd4cb4c5daed89660f775695449ad572a1dd89d5932a58c01516b4acd3cd9dd311fbfab191afc8a325765607a3cedd50c724f41a401753e5c553caf31d61254ab40d4f44fad201b4f3c82adb7fc4351031e5c604bfa12170d66e660ec1f72a1482e28ef805725683e00740eba8332119f00981ef7934418aa0fc5e5937024a993cb3862807570481f422c5cd17b7fb3523c75e786f4b896e6bdc07160bcc50ad97795d86d9547a8156b206fdab2f4fec9e981df2738088017a840f8f01dec2100715f7d92bc0b39aa98b2bf1bd7383b4a78ca0d7f7d747fe45b18b5e9786bba45bb17ede9b974eb270eb8da06abeede102a404bd3330fcf660eb843d5bc7158a2de8c20f1db852c3855929dfe7395e3447cb98547aee4349ed53c045760ad0c334b9c994ed45e4ce836c65244abad523833d83bc6f642e1c0f49d3bd27e53a40a390a21294f863158a7971e70d48e6429bd775a4daf5c34e733441ce7115af3e34fe48f959bed923264c38dc02f2de8baf46e5a64986bd3b81a87ebb349638bb5c19acc4cb2dbc5e9d1ddd69d853efa97c8b13a4f0811360998f39abf29371710684402f5a9941f0b7f181804693f1e5c7df3dddcc6d75b0ed505e394326c78900158049029131a7bb93c6011b044b4f2b18efdcaec70542ab9401703bec341dc8b75db18c17fbdd4e3a361a39aee31ed39ac50ea90c0987332e48340bc842b007ad330b26c913dc4a46709e63fce31c04472dc98090e4a7a9bface87cce10455794ae8dcb1237201bfd5c28f01e1cc50e229ed8795ab7a6dc12bc8f75444e7090814e84d1918b30de41d389ecc07685db1918f193862cfa816b94470f2b20547296f16e4e5f1b90facc20f7fcbdbba284dfd67e30aac9f5d8c96eb2ae2af22f1514e6edb6615687e5a5d0f96b7c3c682ca619e66e58693ae2f4a4ce429d1ad0246981e1a75357d69ca8851c6572c0ab1f10a22625fe051ae5e4d6f1975d7632f39eccdf813235ddba91a18b9ce3aeb1218e745b3a23e480aa5af248fb82fac1553415c0aa9cf989396069063a654949aa6be9a2e74b4fa8319703537a962494809811a7c0fd0613853ebb445204e764b98026cf914943b1925373c3776c374737af4ebb68595abd69530b294c459c9b899f5bc2097c3b88f7c85bf9448bbb0c4264c15afdd008d90798e558af7c05688b7630fe68133f49146c3d16592e14ba796eef145a760dc6ceb26c46556505294dbf4205168ee8cc39ba33d6a95fdce7820044738ef6d7b4b65d1f90ebeb9e6481de63ae8f1bbb2ce23802ad40998606515b94d4b6bc80f02e9d1027d4c1a95274bcbf126d4bd1cd8bfeb238a4078bbb0e27b7e3723757ce9bca825ed92a97e46692859851d32beae0fc20c529ec0a8a7817c4a425e85e8aeb3f6641b71b46c5c6413c003737c553e59a93a85bc05a70b98bdb430a0bc39d292ac29b3aa75edcd904a203a30cc2c96f5d09b164f21457c4cc080dd0ef3ba42130107784f9200c87a0bf8f482868bd6346de6b9a6383f1935b24ee386e5621acb9eca73ae60c62fffd324cbe300e02fa652c04122fb3151704c5f4ee10808651e749ef244a3a2f79eaa3907eca72040adb0a0a22409397197b3913e3208beb87ce2ef165323ae6237acc4ec38d6a1f57f86f1a206dfa65e484c580d1531f4bd6561928ed2a4b62b26a2b0348c6ce66db3b14564cfdd6ad318a87cc0c58bfdabe8c0e366030c9c99969fa65c24324c55ea143f91a4e1fdd0c42de450c12c628ecb68b63d591f033cb6b8a91f99c726d87d2469ed2885ff1668eae18a6a40302fa04e8a3b4426dc3a6ee731c4af197416e5434a7ce72d5d1c792bb473e59bf6c1fe7fedaa5c408e704c5b713bb91d69ad15964468addf61941e76bb019b3586556fb2adb802a841ea82cfdacde9de718c6d8854601df55fb60705c866af4fa0c9964b11ade228ddf10c255a3445d4c221cfeaefca6f5d9d324f534c68438587a4160c95d7c55a952f01032f4b3ce4d73ec30983772fbcdb84f0e51924b24d6b3bd517090cb0b48a8605bbf153d68121597dfe8e20fa2e219f77831bd318651056b29f9c959b6778dc98b8c608082a5a4c6511db69ec8e53887b14995c104535a188bf8bee7bc85bd1747c4a7264fdaf1950fa4858286019e6fd2c7d0c7509fdea02c321f45e94be203b8c7c59f77abc4062d09428bfcced5dcdfed8d4a084bef5870a18f018dc4998f8694d5411ac5be294bb6b6821037895a2cc99c55a83a0befc7c8622c6a209cca6cd92dbae8327018021274a6ab972e5cd308b019f9810e72e37a396ec7a9388b6899e4de3d0402373183aa9465b955713d6b29c05b7e13812f9d9337fa4bd98fcb3a4fb4d650ef1c80b86f8c9da8e71f1df0991bd0fb58faeed02b49a68254bb84f76ec067ddc345ebfc938a88250262e86b351850a650d0f744510593b5f2918d965c91fa4104401f15068903078e0ad090164d7f410bce46d9454590c6f5e29631177908ece7ef88db6f0768fd82161da0e378afdb7f19d909e85599782730a5576292b7baf9ad32429c76be8beec90918a4dbaba91dc8d6de5e62f65c0699a9485b94008a706d0776b5bc00282d77a6caf9d7e769d6687311506ef100b03689b2eb9268268094663924043d3e99092ac2b22817f6c7d66ba3d0af07ba4a7bac2f3e7542a1679569c13be7dd2f5e0bc178d4321dcde1d32656b25be85da7d392e33e182e93f32d4e01475632aed582bfadc6630eaf415dcce37e6f54455bd995e82b9562a796702aff536659a2d65361548f6f5185f98a24e341b1375061c530c7d9fca62c9b7624e0321601950b50c30c2971af6ef286f716ad5aeb573794f713f38cbb7259815abb887ecd9293d62b50e8705713cd37497dd74d84115a865778bc61f5266042153be31b8cf15f31ca856b393471a5512e3a40a21c5209665a65e67bb8910b93cd021bf5dbec6f79a52dda151de1e319e82879587ddea9052000cc999ae99dc2c993d0ed0d8855f028dd282829127cacd8be24aa2c1ce67c190df6d41de7ce76269d19b74e0d846f49c26d0b53e2f02794fb4bea492a7a9c29c7c844ebb647a7c80b938c7728be37871d9b295d7c0b229224819ba866d7db3b568230c28ba911651588347671dfd0cdbb1afd88fa9f208a9076b4ac303d4c8f8477f24eac4841733bd07af9506eca8abfc7c99a7ae70e1f27cc53a368f3b54672cd692488179365427db23a22dcc6b10b9bbee6fea44ad6deabc62876334479c0ae48d99a960ade877ef44e8493bdcf9c5745fa395e0681a25ad731c3cb1bec65b8ac208a91c26af773344b91ae0980c34117c8aaa9a22ecd8c5976f692832ab7f896ca80980f23f95ceb919a417d429862123c196967225941e5631e573c53639ead533007900afc73cd535c9af08c316da0fe51b6e1aebb478300a1841d4d514b5f7ad6448819cd4dafd046b657e3f2dab1ebbf2a4ea17f0befdfa363d08c0c9868752726f0448a6535d5cb965420a14e461db1522befa980f099e17facfc43f57c0672de496f5d76290c8803c72896c3176986457bd7e2ce5821606518c0a38d21af81dbfb40fb9a1f864dc5df2ef847d57a8629a916c1a1666c2ee0849a63bedae2a92d1e5477cc4bcfd0cbc685aeba5628c81108a2baf8c3c0eba7d072691327534922c6a02ea621ac423be686413173e2bc4b3180371e142d5d4a4b8c618561d9cfdb9156255a0da33527a1c0c9401ab0a3a63f23c7f574acd3716b4fed3733509a6d6c2491d023b5dcd9c1f0b1646d5e790464787bf4be6dae20113c8d7ee2fd19855cd61976c2eb27bcae9be42145ec3383234fb6c3c762080f12f8c908d0b5281dd6d0381e4100dab0127d82cf5b1ea8796562b350af8e9716bff86ef1129486469ba2685c671fcc381a31b9665e588e79b2fe0fa9035035e4e40fbd05dc800cf996fcbb11d933ccf7a44bf795004a9d6e957b8e58a205ba83c1abfe3e46d248f01fd175a15410b8f00212bab1f57617ab80460e414d97107a8aa04bbbe988b6acf8e5f231bb2338fddc12cf52102360749903c076e7742837a34ba36b8109540cd6e46ae27b27dcb3a8c15181e7e159967eb013c58caddfe5569c44bec58ff0e02ad3474e7891956e4c7e30d61c9d13e21b5b51f7bb489ff15dc66d5df340dd0b7786346969aeb8a19fab6140c98c6829cde27f979d10c7740391969eaa7dc54d30c4f074216a8fed9691ae133a4dc400f8fd6050d51d2e30a532be33ca5eba3848e62374f98a4383c3cb8be0d4d0274adcfc1b73f7a9304516d746be8ef664864a7e2f40031900dc168a38156b680360242700bb95cae94b01a0fa0510954b80c0a070c538340c4480208e140518037c93a24a0623c76fa5b4436221a22d111c6eea8504cc109bb0c9f2858b51fad22111a3ad6e894204990045ebea43e86629366724152628641bfa9f41461595ce35d6db12102be5aa3e07248ebc6a955d36a40f23d0050a42225f9cf39b611bac354c13addaf6c3b4f078f4ab159b516267a9be49505664e480b1174fb1a673a079e5c13319d72d2c6a2dd81d685b60c8fd54634da93247eb07e5dc63ca8e69edc0f8fb306db11b9ea07ec87f34071f8f25748d4b5c8b9ee31a82fc5f660c9f0bbeac6a57af018c723e7f0f9ce9a023c4dc5d7af2b90ef6a750525f55f7b6b420f159664eb90d7cb337d4fce6452d0e438049ccd839a8b927142cc1ebfc4b18d577e1758bdf903e01b7cf6b1aa1ffccb6254de65608e3962a3290ff60ad5df7b98c9936df20683a55833037d0faa3146f1489cd2996830fa195b9b69d74b9c6c45e281b32148a6f94d3224775d6205e79b7916b6a1235b567f234c9030a610a20ec2f661b7c8904d7343582768b8b0ffb53a265672e9089f9fdf8a708f50fc30dfec143ec0f5336dce479461044b8f0f4e5b8aafd06b4c7f8ebaf77f1345e838415b94de0b273150d3ebd635590ebe7a289efdd9d865228c29ed2e85dd0cfb148a650623fa293341b9bd1f8cda844aa969aa48e94d986a49cf1f936c18552492b5358cc9e88d69b6ad336bf66214e284ac884bf8e7879c64b2201e3dd8e376e20ca651b0260ef98843531b6c4ba0a4abce08115fcea63fb4c7cf951c4cbe3739514f997bb3ae92463333448ef301caa8780f3f5c5147d851088d431b35ed92f43dae73eb0061b3cb2f207f5d7c1076b881f95d3c055703f41bd0f630619cde2095a1e6f989aa7c2244000dc9dd209b5e8e885219feb27dcc2a900ed1c1c9a3f65b2ae3299595653b9ce822a0c9af19a458270a534003705e9643758aa0048a3e6239f44e6e4d12a1f03ea21e3c58aeaeaffeb03407cdb2b004ee6f273826cf8e1693c40ff7feec1968c0c2dd90b61c082f17b436567842e5a0414d73bb78c62aa3f849d885870102ee8b862813e6a0174ecb5aa7d150254758db38c67b3c8ce3a96c845c3077e3b28fb50d1426fa3897c6f09828187f588c6ed09dc10c613810c642e02023b0ff050d3ff1e206638b106d1484f04fd0175ff40957036544e33a9c2012392aa2f3ac29bd42782265a08af66fa5611b71d4ebdf4e005aacf016c24198738c99a5c3f781521e796870e467ad0136f1f33fd4299ef101939022b5ea62f595bd58a6ae93731b6f0bc2dcc0cb5c55fb2c4c131b830b6bba1a085decfe78a03e9e5e2feb33eb3970adfa659a5a8d07881b6cc6f250f8c54a258e13158e945067df27249b220deac2dbfe29dceef0e1eb5d7e3baf120d1db540c070826f71311a16ed100badfbf13a1d9c2fd4142b7fa66e2e09f1024b19566a0e17a66e8a6f68641d0802fcb451f25388332e2a87a3dedb35aa0818a05f808af2ece500911d166ced27ca994cc3cf47a32df31081056d5eb96ca3e077d83d6e7408d8e184ca076f5b826ec4697f21bf9d9b8eb5807fb430b453f5f3742611a94c9bcee11f1b821882b45af0606522999e3bf714b379332537bbefe46a98a8371f3634fea7a61836bf7ab050b7d5fb6568c5bd5bc84d7a1a708c6a7c31bfa2e4bf199138ef80112521fa265bd445121d3dc62ad9b88af72df1604360332101d457973550dfb92f4d4acacee41a88d7990fa947e09cd6dc26d448844977c230c73622a59cc6d660db5fe5e3a3beb9e12b0db0a6914bb5b788bd6425d564bf95fb12f7c3d2d404018b7f686b29f679815341a058683199f1a9bd55d7500b8b94d8c8faff4a9832331bf4f989839d37fedf34f0f1c885cd08b20b30358906bf8bf0addf07a05572228ef6d5321fe17b8bd45119f76fce6fbc8208f04c37fda22c88c6dd8bd74ca4055b78f0f84922c0e40180c5fecf61d8c1d92b46f3b250cf28870b9f0a4e4f447f1c8abdcc6adfa4aae76df26047257aa60d7987257badc87e751e543e34d3da0b6abaa579635a2494a78741dacea5f2b192f2e34d6f5c5360c9d4c211e6cc05ccc7e3f4891aedab198cd7ae1831411844b9cf7b22225ef712f3805cca9463f03a70c00f0053b0a6b40ac95db0b2fb801e4b66e5dbd1c697a4f43de25655856e3907414d2dac8ba71c1fe08581ead1cf33a0d4606cfc29f75b7300916123d2cdc103ab9bee6b8a1ff0f177451d11023e5aec46537feb75d7831f25e3f2bc3078fc6d1c70fd73bf89363f6c22d4ef2b4c4701a378ec919a3c8d70cee3d6e71523e94290fc3fc537cd1cb5c366e1cdad9cf1e953f5da601754f602f95cfd0e34a85dccbe99b2c4b1af7a99c47ddf5d87d6fd427cda8dd43385fcc0b02237b477b7d35c84cdc2b48918baae5011cb381b1b7c874defc9dcc5e6a1f3ae4f03f785e0eb134e94e3459728dd435fba0bea6ba7722f039e4d5ab16950364951d61da1f91d76af65145363eb93f37155a6a4d554c7f128b04d2e4979816870fec3283e342e3b6f8229f1134e788b907520836adb838b8bf3c59467bb1fadd761cf2fef0a2452afb138acb8068a4de0bcb980b84f49d27af28b11def275c16bdc05b60a0ddeabab306fc354e617d5e81ce80e178c4e69132f7e0b54d56e80e443bf9504b6e393b99915d171f5868636515738dbc823b3a0bae008dd7fb6847930c89ef04c9f18c61dfc6a6bb3f1485b92d3358f74e78a11e00f6c5089f2ff97f9ff6c0f9714000ad85cfb73b50846b77474877dea4d00125c23b8f13f82b79b8773e11132061e0aec14c33fecee72471421103062f7e4828d7269e238cc0f521046abd73dcb57ace5244292b252115811274802579e710e84f8a406dd249681aa77837f83c82d667bdd190e061601a19b66181d8a2e716f2453aae021e5499e71ce7220d6072a801bbbbadc9c648d9680ae3d7e5d89690ec3c33c0009e32474628832c2fdf8eb2c76c8b189d578a40eb9c61d8fcf3aa2524593a1acf9e0d59da1796ec567ae236e3b0afcec71a176d4538f3aba37865a56ccd02bed66ee76abb02c5f71c8958f0bb25786d5b84964a0169a065873d658d410f07b5d324d59e432e52ad7cdf23d5d2a2f2f146b108ced1b21fb0ba71e38b9c314a1c57051ae18d5614b0e4e2ab4458e74851a32cf75daf637fe8ea0bd33139b82c6c141caa423e5d04acd7fe369244adb246d771ad5d93cfd0e65e90cf8bc854de524622f918aa10e1ff818a70741bfc0542b8525b403b7b790680a2bb4545a5d6295517575f8c016914c79a428c776663220643928873a7502290e0f40471b60d2547b10ea8a7e081aece5ed3bfb53108124ecde6e7aecb1526448a36e116f4d040161ae3666b1b3ee82bbd1f6821332bf46a11c051db72c6c0e1128323b1ecdde01300ef58145b360f0d873bda069ddffc0bbce4cbc0bff6b267044920308583d527c36d034b91494307c187d86c91925c92a6a000ca92fcaa14d50deac86b72f7dc2e4356e006d0aa06b547f6d344d2a36534830ec764d19117e212bfaf396fe88f9dddaf30f1c22852966947fba363a581ab4c5f52904a06da57dc92417811afc641de7e014c010e579628e6463d14405a83b56d494464885fe5726df145d008a80106fdf499cab42f256f2f70aab01a97a2e044307a4f0dbf23d07ff60ad29bfaebc804c0651077bbd1532a08e4aeaeea22157925d9794074f15c245030825a034191d022bc6a2002ac54d794deb4f6bcc1fa492cbe460343c032febdf070ff28b46134c16bd0f713c6b45730185906adbef13310bd2ef6519e9a5d5e7b56b8cf4f0cbe18769e3250a62594be039297509b9398e2a01e1822729bf05efd02ba829a33225fd89ad4f040b7ae5f92badbd2c7eed7213fa0633d43bb27e8726ff1a93599f1102bc1347301c1009d00a56788fbd73689367da5859a8a8c1ac0dd96f8f5ceaad89468702a67dff410409268150ea9d81855855fbd01cc2822b59eb1c7d8735410b65d299525c570ab56c11246faa84670ac4aa4631b33bf07721cd03e3d2cadbfcd877201a30266ac3124dc019c3e8e7ff5c566c28e4830de6a9b45313587f4a0455c1c33eeca2a0aef7ce3534f7033925cc82e66626838a53e84d05fc86b1dac1741c0338e089664c357e8539a9145284b48ba53b83eaa6428d031e065ae64867445f63208bc0a6eff9418b9c511ff9f4e9a32820ed871867e5619c8d94cc107a7a5671b88c81f9942c85e145e4a8b110701b9cb25841628dd154b1106076d89b0b032be95ccab12a1550106c9e10df08c3103f717678075764f3a234333cf7ec5cbc4aa611256276dae3a63bfaa23b445c0a856d8240dd84e5972a3907b80ffe6254bda820f19b194f73aeb60f042f26e86ca626ef8920f81ac1a17185aac448341268c692bee8c4b15ed8f1a5a5acb547c219945e6afb6f02e83e0ef05f7f5f2f0202800e4287219689c66523d4cfc1872070110cdc976008c94b3476c5995ccd9a5afec1322b04dee56ac03b7f01782901e6ae3d4fe1ead6330db934eeecdff2e81c202d21611fef0e682d822fa3aa84b08a44550af70a3a9eb69ec61c6b042f5dbf1f59895800116a052cb1536d44f05c29b0bccf7046189e8dfad60fa49e8371488380649d28d730ac4c54d2d7fd6ee182ae691c4ecd2f2ea729f1c303205ae1c010638ec63ce8bb45905a477273732eafd44c10c8a72aee55fa1bcca0ddb2c21b9bd57c98e86c81c4698641ec32d58d8d089aca6ad2ffa244373d339e7a3071f29d753d90f5d2a82fb2f621cc2dfa46479a46b3720dd238b5e0305e264d4cdbaa65f7df13d26cdacf957f2872b6a7a43d60b06c29ea6753a0a9a957c227d63c4608c20dd8ecedfe20ca42cc2e224303b360ebb768e9d6667fc81ade3041dbbbd0103fa92bd6af20c59dc41e812a0909b140ed6447ab0e05a2908e82b7e1fc9eb60d37407c3a962774fe976e22033b2e3f9e012398bb9e9c72164367b8e0bd0f4dbd8faa31c124edef13e3d181821d8f863fb04a76158f044489096704985aca12587fb310286d390d96fab15837289d91a8b81096796457558480919dba05caab20f839241803e144c10b3826fc4cc84dc238616c9cd86588134c30cd500d575fb9f84fc501d0e2ca371215deaeca75b44a0500f0479d5d56b0ceb20040976d6f0d8ed0075c9c45609f7f4eaa8a80f5de5207853921a83582855e8fd3e8864e3bf21293c0050e982709a20ed32a729227b0c18d7572f30ff510a910128fe993cbf8dc1d86d0427b2b274e1cd902b8b6a9054698fa040f6c127cbbea131944a94502203588ed01b5431e0ff78213b8239f334e93110463daed1bea0d4fd88f702bfe8ed1888888703a07479014fe88042414247fb72a13f1d2ce64c9bb250399344b536710c1548e2b0eddfc809dd8119415864bc3f1b409d34dac0931971ebc3fdeff717682afe05fe1a2403b89a055bb561e7dc164177328d27d86c11921963b3e03fc5910cf29b76b8cab120783f14b737c3fe6445cd310c7de2b0be712d2c3626114565a7d0839055cc9d0697022c745f32706a8079b745279607dedcfb090653218e2f16b3f7a70f820a5129ebe7f52f2291e4703cfae064778cb13be36f5090d7d76e2348b17dc5b9bac25d6b00fbbaffde04ff1dfe270ea6ede8a6a7b799d1765c081eb3559e693fe36c2297d6875f198a56b1e3bafa33cefd192c4a91b0d85762c7a51793e25d8924c80f328c42edfbc40e1a15b0a35e4181f042c4a4231682f8e88a5aae28ef196bea40c46182a65877b456de0878950b94adb7844a5b9bd27f0a4e81700e6b1e872b650139b8b8dc0af58cc300388060f252fddcab97dda109a86d7001697f6a3b6612ba5c2e204bd1ba7c2693c0af921e2ad285b3b311fb5f44c29e356d109309341e705569b111ae13025f2fdee98770e37c37592b4d70d6511fbe7d2c446b281c6cecb1d83166f39d31b39682a9c7ca114ffc5c26be3c7d4c82a4075b38babc07be963e8ea1d06c20187839808105777a1783cf32470fd528148a417b255d5e10e4c2b5f25fd12254a9684d9f1375749ee0de5a4061b905931ae269e4b2db1bce13dc47fa008b13cdd63b21e7f1c2abe7e8d0bcb10f89feacf46340a5cc4e9b162223de007c31f2a70147b0773f256046cc66e97fbbb86da512f414f4d31dc20296e5bd2c064cd5d28719ad08b87d8eaa5f99cf132d465aebb916fab3e098001f70100acc7340453421f405f4534f174b9630e1b5476c6c8ecb6c6de7676a64b686276dbedd08394f81cf6dcdeb84d850b61c191723a40530e78a8cbf9d27328cc7acfc4586952ee0ad7c9adf867fd46cd03bd236030893e57138c73a88a8299b2edc05420845245f78888f25095752197d820c306cb42b0087ad55fc646bc318e949870901b8017f47f49510cf782603bba52a09a8a948614e086d723e9b1bd6e2649bae15ba66a29ad7173f19d1477f3ba8531dce0754c95009fa806f377921053c24a0f612598f1da56e2d3e51c3a0857582a40136ad4570e3a617f51a93a365d9a908c9f545eb589b584d0d832d69556f10081ec1745cc9daee2da686e15d0dfb04f736d453c32a3c9ac2cf6258a79a84984be5bfc0abb2f1548297bdded19957dfe207907042e75d51e00096bf5629547258884a91ecd7f7275fec6c7bdab976fd416c1bbc808350d3a693caec0b3f865c41aba1437239f4eb3b1c0c0ec109bad46337630415239b5b932001f6474a141500ce0e9e67282e6761dba9a0b6ccdf0af3ccb937fc801e639c3bc46537308364e7f1e5b667343970ced291c1057e51e9fbbc0005390bce1661d3afcf972e4a473bd184a1cb900201884a2b11613801e6764b1e073231f94a6c9fbd44cb1576583153c1d5a58c67f9e15b8944eb304fc2cdcc2df30150c8e08ba23845553f4f6cc807b0daa35671f53040e17d09dae4aa1881409fc51fe58de2001f18a7393a9bd642b2501a4d0aad71aa0f96c4cbcef7af34e402abb1d00764b3c90064644d820eef22b72251a7eb89d4da2eb0ca97c5e4bd954cc0ad5e8b3bc2a961f60e087095b1947f5517a7dc331347207c5edf228474c3695cbd65042b0a46da07061a58466301196693919603cb535505e74c49c01f58749ad53b61f63401884be77d606b940c01490074cc093cc59e237546fe8715eaa69288f0f7ec27a7768fa3aa634077e1bcebf64102818dd675bf4722e095a8a906bccf1026c2c56476698ceba92525e2c076fc0ba86d0f59daee86dd880a2010d576f5f61fd19bec6f0d7cc65d8fbbc3cc2eaf4af6a1f39884e5a0af0b9567c459f24c61bbf92bba2b9ac31483938f8b81f4dabd4927dc6804f84ce7121811362992283192253892200be22998a648c0d9405623c2d5442c515cc6dfe7484e66d9bbe9f34b8490c6b32ac222143deed0d1a2e9bf814a3aae783eff399e6712c11af8d907182642bb6c9a80805393447c765559ed7f7f126a7c1b595d02647f1f2c5c4e05cbebf0dec13d1c10e0157901abfe3aec99b3bc42f1752ffb1456b983488abbc725130a08cf11f5d21af47111f750a163d52d216ae8665ccc7c767d80ee80fd295cc41df2c37f983cc4dfefc423d3f502bc1ed7e10ff0cc481d71170f9a18bc73a1b5ed102274ec2364e8c34c5b2f84f878ef821281e6c9546a9109db2878bde5128c4b157c6d32f553417429051224c73159131e152e5ba65f7c1e06bd074094cc704111a6916968402402b1d1ae358c9c6011cdf2d4520f03ce99b866a482f563ab7b54a2bb677f7f390233ecff8785db148e9cd20641220dafb7c52456e4e3789433447c736a53824402a6b25bb1d7d215abebce6f77245bb0b0edc399eb7d19043d98b0522ceb71a27b846b0b0a8782f71d3b4dabb74028e2764dbc81cf3d0522c5dd7233feec3293114672a350d1a15d2c8133cc47392599a0c36e792f914bd424e45a5a3f938b8d7a81fc2f6fd18b2a8b75b57b10b0f890c46924e0942ba121853b5ec155a26c2fc30727766b9c25a0c4c7200d334b3e043bd7b4553ff89488f0ade705cfa8d5e0ab0c136ac34fe4739a2d686170bf9ab6b9bae6a0c9d3a7df3c5340a59184d66dbec882306a1352837a3c3315c63628f56551c8bab2015a5956fbb74a608ea36edba74270bd9a864a47e83aa8d9e4a03e7166abfa718a17b7a61a8c741e3378d962b42e4ce052eaceea7a5fcac2d8d8d7241fa30e861f85afe42f07d791d8e487dcff22392fc8ec05cf7e52427bbf240b7bf8682f5bfbe611ba65dd57e456dbd07d8af2bd5ca5dc977ca95a28e62004b55c095825451b742adbbe5609a2e00c608f78a9f839b9225b1cac0e752d0ab748ed9ba47a3116e01056e9fb75cf48c24576a7156cfa29fd65ecae94e08742bbbc611e262d1345fdc1fc635125183978250c56c05b3951d281e871d0db8a55683c184bd5a0c4f923e4f6a328eca6e5430e1a6a87a265f8e92ab2af9ed7ad76c868ffbd0a0e53d00f56b2b0afaf9da62bb586974408ffc4f44eadc6e5c33fffb861e038cdb71a6b61382d22341a920c700337c69ed5981407e3dd90250b0f9d9c12e404c9792a6bbb29d887a8c54ee13256198b670baef0fe9538cc326245b42a1d563a0298f61d5b3ccb7a5a805dc12400e562a0f62e1d757444e890337bea3b9333b823af1e4f5d3f90cce296245b40b4d22af28bf0f867ecc74ff45b8fded05b234a70e9c2ae092214e2da79f4b49244761c96ec67ae8ec224580ce275d8cb5d1c3ae64099f01fb6b548f6cf00990b4c1c09a373073c06bd94aa255163a546d3ae1fdd7c66476250c99e4b722b17b9b0f3d1df70cd71bed9a4973483d716eed4e0c0448b1e02877d5a7a2712a3be4063409cf4c0ae73ad85d406c7b17a5c19cde4f8beafd8f47d2ecd342f5ca169c94a677473a1194b346ff3b3d9322869727becb528a95c9a93ab8b36528d07640b593bacf66dcd1666e6cac8804b9b04bf542efcfcc6d24897db371ae74f36926d9de5136234d6e32534f3f193b85d39aee26dadecdbc83ee29d769f442442211de1ccb5486ccf47935fe78f64718649efd64eda3e26d5830241e7c04a9586e6f73f362f80bee42f0803303c141ba63a6ce965c993561db695a1e682cdda72dfd3b2bc58e068fe57aef48414551c024dabbc611e2254044d7fc8f92369ddfc8aaa260088270f1432c4e8e5ac458a5b400c840e16b06c6787950ab2975970f29fb98ca0ec8156e7756f9e5c32616d1188c4ea836fd59d34f2882c32b5ee209a56f296ac67cfd83cc0e4fea999d47b1b21ebc7584e00cc15ae195214bc2e7d337e3a9990d0b32b43d3057876bb81b632c471eb8ea2d688f3635c00055664942a49f16863018785a68d7974f843ff67d5fe7f18a896549163742bbbc45baa5854982e18b2a6dc8a82503afb9e397c85103d4e37e6c4201120e6bf89bbda902487d405ea74b481df8dbf2777abe4a38407094468508e5fdd49b6ef10efc95e02501585197a2cd4c4f4f722a113c23d4d08cd7cb78b49a2b91e002ffdaf9f3c1729242fa6269725a5c94bdc2a3031462502244dda5506c0914ffd8cc0d2c9f1d448807d28257cbab1edbd93ce6b4f7a1db2e2b630b28f2ee14fd21e53d89587073c9e49ba2a4868f11ddef054e1b38e23c3c97d93bad23b3c4be6ac14339057061e4e67e599e7627cb892bbfd10189fd6a1f44232cb03fe6eec19e622550d1fb3ef77607c15c446caa1bddd12b0e115dbf5cd9f0fc69830efb6dbf4c2a48c8239bb60ae2ef4d27cace65298433aa81e64b615f727268e656d4a91fa6ee18b66acadfade271937c43d5f4df925c1aeba18d6eb14f82e38036c2cd22a031f0d102e52c053b09bfdc77da58ef4ad23728120a9a4895006403a5c73ddc103482cdebd700aa2f55ea92ba34c22f73cf09468347fc5b5505741eeb44ac062eba7665f791a4bb8088b69444185d648188a08ddf3a004b67e415393e2ff07de4f3f1d20a78e61cb80907b40d32cc6afc9cdd56b8bae65385f2e4409e053313960795151edf8b98baac1691b7f2056b60e8dbb7d28e5ac544a787d0574c2ceb5ec0281647220a640efc4aaf1473c68e78da17b0c136e5e81d2f61528fc05607c4d2effd7ea591d6415b3c83390c4efd9084da16947c2672f494a5a1729e03206965a358f9fabe00088376c99dfb1c78592c43983520f5c36cce714433680a639c6e050eb96433d1731a4047344e055155b27b058bb5370ee49f0c4d2bb6f162b70a47e147d4237794989f1791ff940ab3b9daf7f7789acde31fc20b4eb9f5ab216ee8dbed1cec6859d38c4e4d1d0256078524be302884b01c7b907d72ee222f09fd237d54e2cdfe3a9dbc0da6595674bb2301045ef324f60f4c4663d7154eb101258615ccc9d29b927380337e9a05752a9b3f5df893798e4567fb2e85848a2f0863fbfc75ba1df09a09c1c7925495109ec360756b26dc1b1c811a60f2923e21c30280a145176332386165e49709622ae8b0ccbbca36ca0a1a6a3b0a692885a7e2679cae4e39699c4926af0d8c27ccd69ec44ab9e81a17a51636f2b041a62fea15c988fd3a9b3e5ace342a9376b221b3e6906fd567a8a3f7163241c4d5188beddd7aff489304aa5936f25f1291586347d79c50d412febc03c31d7d8224949b8a5c0eb8a2fcc12a42f1c7a90d0460efd84df606e4605dc569ae305d7c6bc5d8fd5adfb343303a020b267c56d8f5bebe57e316f8abd3b69add1a9268e5a8ffa24916062c5a9fd10e6b26a4cd74814e390d284a71bbcb44f1239ce3aa5c315633fb46aa2482cb8e4a32424896c456e7809568acd6d4bf0cc6ef89b6566c636d8c8fdaa89bdfd2d09ec24910df8a85ab624a2f28c89dd84013739815e902846a5d1e1c3287483e545889cca22b947297a5dae3ef218b1e7fa7e95487df79ca0c49d1410391429b0162b81d2992ee6af6c46a4db1328814d64974412479d5228feea4a69d0f5f1acf18914a4ca5562e9993f4ef8e1dc332ad7cf503e7060b250400bbdf916ba2076eee36b543272955ce07ba96ce0887c84be0b0c7dbc3b66512ee8de9ed4c79c00bf8ef772d81299df68716fd21a1c38557bbce80e9a114b831b1d14bd66d2a8325cd6f18dd52adf1614a6f6ff5f28cd3b007312d3a8ca8c3e8d23c6496006e6aebc06477112690448980d447a9f003102f8c746c0823d7d136f49a0d86cbcceddf092d6e461449142c98036a1b98053e4dd237919e5ce0fa5ab06cef68acc802cb0d7711d5b1a582131fd38ed580e98965f9fee90401015a38d61ad7c702484225e2779375b5c49a28a8e9b19128fe023407f740e52c1f3bd67abebac165a181a3d2072666f15970bd5daf93383c375fea1864e3d14348f07f7f3e020d71ddb74a86f51870e313382ff7d48018298f42307974df04ce845bb1c0caf618433de5143c517a1585a8f52af3e9c90c9b1026ff7daa4a6a43fb42f89502de936f6d519faa553181bf237f4c8af81d5a98c2c72d8c8ac6211d59f675c109f2f6e7ee1feb45615747bc0b8c89c895678048b79c2a7fc1bb858ea085336feeb17884ab3a7434998bc99e9206b48acd8fb0714d15a1e0be6802369f616860ef4417308d88ff6d2397b733b07bcd0b69df0f8ba69ad0fa9466c11ab3edc23e9926156a2d1ac94b673313e739f3e1a68c278b4a4c2557a3186683883103a8844ce01ad5515358aaa25f13ef37c1a64d892e8650f0fc819af8bb1a7244e2f6ef4492fbe1468b055a8febecb59038a9f5ffee08b6b68e9fc94f4e61ed8f08ddec2c317bb9666e8fd8269edf5c1162b40a41e54ca01c28df58de65fb667e5abc0677cb3be337eb698eeb18b130409bfed1ecac8e6624205d863bd60a8572fa21d3d8142b1b690f7fcf66caac43ed4acfacbbce9edfdc0c9ed7de8acc6199374573b098373c0165ff58587d2ee3205acff31f0d04be4bb3188008e277c318911c8f50b6dcadb01bee9363e8568821608f060d6589935a52a8dcc980ae5b297db25d5d881c0d16ed227f3eed7b4fe8589bf1f03d1a02c89f30a6f46a0c5cfdff494ece88ed75e1d00f7f13495d7d25eaa60838435508837a9019406e6e2e0b5c0ce9291540946437828edd795220dbe189d023707f5be3f8bc74d059d2fc2e4609f3850af5a05400c94e05bdd991b96574d8c3312ee0da39345d323b657d18fa37cd073d4eabf813813842025c00b6e8fcb523402a76e8f85e90029203cae631ce20bc6b8b8c0c839d8392990c4e8a8d8124efb885229c674b3ae2fdb5e55c09d41e2fc57ab482d51958ea6bb00fd0806bd43dc573cf5119cb90179fbd161f3fa3b63f49b4be3935aa79cbb75dc539d5af3e2af9f7aa6e2caa3cd7ebf73b36ff0329ef6b1ef709aa4f0c6bc80fab16161920bbcb47317bbf9ca447086e3029dc04d6af619745e0e9b5352ea7b45d4635602f38433c02d22b102b511e34acb110ba50c8407aad88147a8a74b4563c3fd8009593e73800fb2ca621bbb007ec598768c08ff0018e207467d7c75767764d6de783b54622530a288213d5f246c05cce42ade08430c83da7f663dd8cef4537bc1de491e7769c317a2840b0215ea11e49868b8fa85c5968596290069abe93d765d2bd0df35f3ac54420cc566093c3f8604ff560c6617c49e260dde750b23068685e24f38d4a625a0854e00a4f8f4c372099bd8cf4053a3139641ee2f4e0da829f582a0342e3d2f4c44daa549335739f041529d98da68b39157b9f28374d8024f7782df9709bd611251d87aeb994486e32299745568dcac4f71b8fa52dd3f7bdbd699310402a60389a98e87f8408825d1afe8d328db9e427b0a8cac7012264354c0417b6f79bde79fc245d064782bd945badf6a55c5bc28b4a7f423e07213fdb82f922dbd739bc76d3274dfe9e01070620b85de0ab1caab50adf8878a65c14fc8477ad21b521dc96428ee1189cd957341dd71da6fdb5cd8e2ccc6adc6d4b938de3ea8d5ab9b4a68650bbc0c39af4c4672eb3fb7bd6504ec855dd2bd70a56ee173b9ac60013a401c9df8aa9f1d5cdf6dcc68d01b5363e219f6b270d23ad3e6fa5c0fd92f2a5ced1b5761c02bb5119d718a700362b57087f951eca2a4dbfe6fdd400de0f35a8b00d07097a49af69c9d274cd430407ce43633c9198f0356f43549b8a1b1ef7ec8bf59825db09da1e2543c812467e1a052cf9385827980512f74baf8e99b26c25dd187ca65bee1457841a1ce666efae8174c3ec8260b51cfdac0869700355dbc6b2340f4e3297217d60727af370fa53749fad8e6be5f1d2ef46021285075fa2850ae7aba1c58361297bb00224d5ceed734dfb55c5024939b6e151de9d58449babd151eacc749ed4ce8d72ce6ee4ac744981e7cd7d4d5190ddda336a7d4003b91deda494d709c05e966ab61bab20cd94240c65aecd9ea83fcd5c8848410c2ecd3846d8aa74c0d6607255760d469c617dc5a7f1a56cf012fd5ba6236286a5f25015240a7dae59e2631e8b8e45695749b7fc439f5ce9e506dfb7fdc0f8b641a24ffa04ad588aa06341ac803d581998c7e9f043140ee45c49a5fea13837acf628272dfa2e3725b22dc68447b6f3417dda8b6c18c0c841e9d51d2ed9902cf8791d86f0b02d36562901d8f2bd94de5e8dc42a1519ed79faed024f37d5b594a99e88475e8e696f331a8bb11b3b08ca3c9c83bea8e9317c6bbca50ac8af5895695ca0aeed4c0e46251fbb42c4c2b443859ab7651c485b6b5c0fcd2522013d6c67a71aa2f694cf8181210080d17ba2d09350b247f1542733da1340e71307e3e65455d919be39b1c0c89a706abe4c9071d4264260b0741ecb171f50ecb4c7af719725c814f3b659540dfcc81635ce002a881953e73ae820a3f619735acd5563317c9ca2c9cfdc6f1186cbc0f3a0049da9a48251ec4a0a0f184a94f284c4dc81775ff80b7393d55a9c9718048906ee3cbfbe4afa98d04dacf34c1f6ae8178a1dcf4dc506302a47db215f41c6882fae30ffcd4895c1fc29eea0ab4f1e408ee524238fb89fee159a4b4689c93bcc996bb84101e2ae384357ba12ea0a385ee8d732cba454e2e2a228fac4f00885ddfc8ec6b41b71751f54aad450c9d3b579053fc5e2ca63e9e218670d03ae611b95ad2e0ce99d121e838e8da19b4661891e1dae187acb65e9e736a933a69a2007d99d0800d4f8d80c99bc0104f82cdeb74e5d87266f338620375ea0eaece3e8f0c947bb03c242812390ce5dba87233a5616c6ce4a4511b521ba01a94df9d09b4797cc31618fd31a6f580b459abbf2b85a9cea7df23f83c7b7c1022906e3f1c732c55892a1c05913dd8a3f978479199306af9909094559ceccbbc9b478b803da226014acdb0f7916cea6a3ac8cc245521a55b43dbe88869635a32d3e888c2ed31e57e7ca58674f04a0f0c84b2b44b2ed303fcbc786f5a87ef621f54fa3ce627b43f9ba507118ed287fadd4a8c178157eda2bceaa382ed7d0fa34540f59398f6e5383839dadf0b48894c20ed87b7ee5860042244329e4c808ce5e415e5544f36e6519634ea9cb989404966d58ff9abdc6c1bcb192efbf8644daf695f67126e35bf7044338a521e6043e3ccefb4a532e0edebb2afe324fc5b881435e8aba566e074a63a80405365498bdafa7d65224dc872369d3303ed1990b663c7c121d982954b72d01eb2f0ed1758c33e128de63fd98ad21ef4d3de721718ed38b7da63c2213c504585a86d26f4d9078d8e3a0c2955720104ea0cda5e0b2e17509313d95e4b6c0178e7b1099521d60e2a0ca77dbc45267d0505c18ad7bf06fffa50b9719fb06ec1570abd83112420b56d8369932564bec6ea213653f0b3fffd3653a67384096945d6ce04b20f045d231b2137740d1b446251bb59a77ed0f6db23c5810e716901eae9d66d09517edcafd61d6bab50649d469bad4a12eb7ca782c47f4e17d91c357682b01613cadd94d3be5b12ceb853f5cf90bb4388244f5b4e27ffdd5b8ab7e1846c42b03a65b95ea8dac8af66cc8a5e03ab523fa8f9ef7a3bfff729685e6d01f2e1a8938b46671fcffa88036e3d8ce186f7dfd5f6ad653c5090b1c6b05cdec584a9ff3e2dd854986c3dfa8f96d815bd065294ca7fd73ac85bd28abb277f003c4a674fc09c0df03827755d81ca4bbc36fe40b34385e09a147cbd88f4591018c6f3a05581a978dd576e92cfa163c07ff09c03403d0ab6df6edc01d1a99a77b8e9f5e7ae2c3fa022035e545ed8b272cb1c61417dafadbbb6c90aabdb0b71b04216ad1af0fc25e295400c5128d3319be35145e22de31f8d1af06eafc8416f8dc7bdabff8cc27709b3a82c164eebdaece5ed7697860f9a208b4df0d2d4878de9ba80a7fb4d19d42a24a8da5efa11bf4b9ea2c87743c06333f05402ff0d3c46d35a893683e0f42338cacc3794820018585e55eefbe0da44c26e327a0a231a7d3b0202a6e3b6c0023da06519d3f460356ea1201e45bfb04b0155db5a9a6ddf9fed77c31b111b4cbcfbae3de8815786ed0b5a5dd1e5137469562f46e83c69a7b2fe48c652c50a8d989c7324f9692319cf31701336e86e9cb1c2dae7d49838f4bf155a563ce1995b22499f63ba1095b9d11abcb05581a963158c5cdf7ecbcaaa967a478933931ca640100faad509c258d287a80015961659c9fd6998a256c7cafee841ee0d07d71a118b583f4e5a72b9c741fc7569a51bf22b2b5629364ae0c30be3761ce0d19de88af904654cacd8016fc23a1f263e5563f39444302dd93f3215f707c437b886850b14afdf5907783a5c6463719a77ed6bd262e766b5b962f2655c336de6e2ce619299a26f38c01f3db9181d50f6050f50513c029d1803300345b732c5ad8933137106551307f8e38ff544ebae485ab5a1a1e1c635e88171a98ad4dceaa58aac42a4fadddccbcda132f5775cc50df07813fa27ad3057f58ea360b8c46c208a29a76b21db7d448084c3a4aa49dbb0c7afb078995f832786c5e3e93eb6756a677bc0bb48004accb887d4e4521f91a2cea4262bdcf731c3b013298b6c48a59352cb85034ac57b6b0a2bf9dea9f1fd28be2f3e75268973ab27ce54e71ab90a6d55d5f7f8593144207e089462213fc7e70394e3f210b5891ac145862a9ca86bf89ee4c5b4d5c05eb2a4ef39ff4b8bf28e9ab13809735d454458590fb96df64cf8fe493b912f6cda05964a9deb7c8b6c52bced696e03c1b0df14ebc2d38253d3c9c6daf02e0288449316efd99a91538205bbe77665c2ecf18cf9d5b6edf725d0d2bfd90efebf84b4e16a39cf1e859914ae7d8b374e7d4914a4e81ded7a81a76d8e3d47102327254cd5488ac3d1301a68942abf739db645ad2812f483cdefae6ae1733191503c180b6dc0a00f317302d93c90ce7a44b2e7050aaa8b8d7e3787e2135856fae1df7c2a1c04b205c35545ffa145678d6500bae8b26e50ff566066bd7323a6c38576ecfd22c8117ed612e8583a8a888378156b21b9e309309673b83b053152f554a5afd7d396b7dfd53469fa65d7def613c99911c7eb9a9feaf0b71e183dc4f27b40a788ef14226ad9dd8f7306e63e1156bc19b0de370bf66a2e4b42102a00213f0f21312a73b012b16f9fbf0343045600009f07d637f37ac9152125777daf4ce9f9ea26d322e97e155c5cb994bfa74188b51434122b6028e1aa76af94060008c6c9f6f739b30554340d38b95b5b7910c0ecaecb9f9145d94ccecd6d3b6d7d7db348811e2d3b73d5da3de087f7efef63347c99d4ff02593fafe4529743c9ee9d3b42f39a95dd531fb4991b542a2f8f87086ab16e1593547f0ed2e6ac2b38aaf415138e44deef0bd45370f8587375bfdf4ead5317317a3ba70f96e2a559aa823f2333ba2f4166632ed139a8f44de20a2f9817baa712dfbbffbc8e91ef0764a32be47979cff998f324053f7bc5b521d52e3315e561314ebe8d9b181af69d25a6e761d164c43c0bbdacc8b5c569748f168c4f46f30379c273e9d57de3a510483e119156ef605daa251f13ceca2ee56fffbb5e582ed5dad167a9fb794b13ad98860fc245a460c30d84165fe65754089754675d27f2b2206f1be348c398b0773e96b4db8a8cd841e163d0d6f34cb21fc8de95a320c12b43006f94cc088407d525d3f90913d93b3f610ba758fc05ba2bf73f61776833d65d7ca4310648c5864024a8c81415c3979552b27769f6beb1f7ff58165fa53bf0fb3ee5f212d4a5baff604c6ab46ada9d329c30adddb3b6fb4c688613c436e7f886dd3f45cf4426708379e34dbc185ff6eece357820ef570ddea1bb44a670235224c4e102529558c66ed3af19214bb09eca62098510bcda95323126dc870ba02b6c270f8f32b4c204d5bb00151328c3eaa2d853a511ce35f6ab68d73cf6b922a7d2849690d4f2d14b899f33737ce9b7dde3874cc95827c0c93e1cfa5ec2b52f14c275998f6810b903db66ba750986d9a9397114d41795f2cb7c2f0f29d29aa850ac051c737bd706049d4a37983293eeca7483fdf38aee9d323736dc0061dc2cc90c4fe943afb8c7a6c48af1ab174469dacd3e2393255ce016dd96617dad1cbf192ac3a4bad9f5130e76bab322100a21d2d43e3ad0d872d3c8acde3194edbf22bbaba2607e22001e9c24f16834379a8adda23fd14a5c89d2c4a759f3ae21b8738ee441f0631498a2f65214f70a1a485039a5f470bfe8e9d2e6677f0b014faa2f7a8df5294830de7c6b15cc96f9180ff508553b6964f6441966e33c95f36ffbd733d09fd1852b9d8b350a716d583d263af396f178a5e7812bea04fd20811acc53b9fc3c3b406be7b2c0f3089289a16f0155de5cc9de76b42914bda52b1f59bc5fd6758dfcfefc3ae4f93daec8f413bd23562d81e5c148f4e336c6d5f0c5531c2af5909dee8c638e040801048a2b276524549d727392c059331dc7980194140523d61cc04774b6288b06ceb38320a62f1398e30292e58bcbb64a6e6332c3f225111418fde9e026587a093905dcece4471844aeb97fcc3337f496139d9518628c362b1ad792f7e5b874bed837ec1c11b7097d0587b2977bb675da3b51ee19eb30f51be963ae2d10390b0d1d7892b7a360dcaa5fcf5dc7d9e90d801c0ab4f121a8fb0dc1e223d312e4f705f2e5c1008a74623840ad6b009476e7fd13438749b1104628c715371f6a32238945b11d42bc3a11304b15c9ced4cb07d9ee49ce98495a9f9081ed122374f19ba0285eabb18c8121c8123a0bf40166e3c5e7236e456eef743a4492b5b9e93f481e2aaf87295c3580f621ecc0d4bc42fd9ea42ba62d1477044ac177ba5a305c15271d994da5d7ebf57abd3e778fad63cd49eee2ea527711d793542aee8b7a21250b13bfdc5f3ed970c0023080c381011054cfdc0d73794c1f56832d58c4df8d642b002813e6dc07a2bfadad19d9468834b237d95bcabd03800bc00b440b2dbad0b7cd062e763eb0ca068634dc41dfbe1d5996691fb77db1ab3da66338fb68ae3679f4856a0f6177f942b42b7571648d2eb6f0674ae559d1659c3bd93563bc9af14e95cb55a97ba9fef1e20d27913b6f830d5cec12362719261176756c4e264c3870e78f3bb387530d3d486247768c58112b92e4cecfce07be31c62d79e7fbc793b3639186f6f9afb3dfb61bb80da4b2433b0a0eef6af7ee16232b0343151f4457fb022e056787495c1abbf1061a0fc714b6a8128dc8f0584295188b745c2ae5e489425c0cafe4913b9d846cdf981b6df7a1f4582ba7529a4d7df4f19057023417e471bd49e9a37b42421a69442a71a34f725f8ce1915e12c3eb2e647b6409d3c3db0937fa646cba9d70a9361d75ea982ee9664aa9ee46694b85a1e4a704490fd7c07aba0898ecd98a80692c7786e8150d617a30d513c830d6a49e4c86084be7a7c0c8aa148c663202c3a8139d70e9b716dce8935d412310f4eea76fde923642e970949cbfb5e06caac2c0c04c18cf48eac867b8973c76374ace2693c9e96447a7d3e9a59bac0918f2b597ba1ca99f4660c8a6d4519cfa442f919e9230182b61301206137de44eafe8777cba1ba04edb4274c99b91d6136968a7a0873749dd44de58100ce5908cca215cfacd05a7f5d52a9f87e83208aad73eb241f6f4cc908d301f41c2d4157ac87530dcd62e984e35e5a7ea46cf3d71a94daf44f45e8fecb91972e941305403199c3ebcc36097f6a5e7222351e8f31c37efe893dcc7895a76a2777743bbfd06721ddf548a879457047a9cc3452c402f7bb8d59d5e22e11677ca973b1475eda7f7ce0dd44fe76f3b7d25a7d34b523f629a1c743b3a0fd11d8d6e3aaacb516f021e216f0a0c4fd77eea72943e3add7b0988f2c3e8a8a34a1f813bb4d227bae90b250d2e25bdf4c921aca2a28744a74fe9d08cd26f37b08cf5061ae2eb8956216ff1b33de4ad60db700c0b64d98c3f46f4b0577bbf7f40209b65d916923ddc4329f7f49c6edc44a25068d467f042eee1420f7721ba28a5d48ee136ed1b5cfafef1f8f666ddb90e071ff46dc4953a7688a79cdc213ef5e1e11e4672016fbe251d72e96d26a7055db316a40769599665da41df32cdc60c8c399d905a7b2ea53e34ebe15606d22e4776fecc7ab815df48da27d2c8b2ac87529ad58470360ea685e88155f330ac9a9a5741976010ed02810488ab1957cd741220aee64150827216c1aaf91e1630c00ff338620d1502441af43d1ecc3ccb67e7559e70e57f1e7b6214ae7c98c39d991121224bde66aa842daa84ec73e704ef7c584d9c72a39d37b66b029735a48e6c999f5e9c40bb640d15d93253150c539ec737aab21a556c8095bcb24c4525b6b2f3559125b6e8393b334f7a9f9bc447e5cea9e283bc6ca50fabd0e0f5a9ce953a7332b34fa60629c5e59e95b91f5458a54a39213b0753183c516140e472d65990c7c6cd3b41dac6795782a15471ab8a3e41dd0f2aa108e44095950f853e1e9d4516b79ebf1e0810c410444cc0474d133752f0116ef127af8a5bdd0f2aa1e915f443052ee8077e0555ddfdc00781e11155d69038bc7b3979c85b63943558891b6badf5dbccaae5326b41b5ce7a813009256efdc671f5db516fb3a0b973de3b6bb5d6823c64ecd6575046c5adaba85a03c316559eb225144ac8054ae0090d755c9a4ad11a73297dbc26df441f0f1f51ec982f39caa5c8e40b2b10bb26242127d4e40beb4d9e7de1bc26e0129789627e3b293131e9ce1d9844d439f19e455689b2ecf42652113763138b42d99309f5fa1218ea30fd7493975cf4279f350143eff4454e996a8966e0c62cf23c91492a95ddc42b79e8a5ee7d47cac424756a9212a5401e25a0900c94fec3740b46537ca12f812a93aa575d02dfab265dc725fded7a5fd52ed34b25d3f652c874ee3f4aeffee374d07f445e4d30b47d1726ade861dad58161cc258d4e1fd3a36f87e781d92938bb2fd52b3ef7fdd840a097d124284dcd4e5bd8625cc5099cf6b3ff989f608c979026cacc2fcac88e63e4217e2271c308445fe6b7ab2fff8377e72afe877dff4736e77f343841af57f23f625cd5f77f54302c2203bd7bf948ccb79c6014fc06b71879d2c8497b2848d24ffbaca735a452e6d8217f822dfa30abc0c0cfadcf7c983a7aae7d36adf5bc54eafd68c4f464643ff05cb8c543ca68257844d06ec75537e7c5967ccf998cced975d62ec7bc7df6c95919bcd76e66de773fa85c09ce0e87fde411cf1d0e06e70e0af690778299123a979ec814a3da3449d4665e0d594edb403b6c2fb8ace3108b88e5914696e4954226eeb4a1b4921d523a98bc386141e126b7b21636f477f07674ec3dd58bc5b2d281484faf28bf7a455f3c7edbe1ab219f131dcedf54ba19e6de6ee026b74c3a7cabd55567f9964337c302c2f0562c2b1dbed5599ac562b14c67b1582c16e92c168bc5b267b1582cd69bc562b158a1b3580360098025c3e2ce625d560c2b00aced2c16005836583db0b4b3583558345833583b9cc58261b9582d960e67b162b064b07860b15c582f2c18ac1767b19ca5c2ca81d5c26281acb35cb0582c560aeb849562b1582816ab9ec5621d876e86350001c8dc980000c0460f3568cc8071b578901103c68b4b4b0e2ae05da49ca450d26463f3448944faf89a4816bc011c795ec8850d2e4020100804028140201197d222856f28b49da09c741ab703aa04b5e9c07707d00b5289546badb5d65aabc6e28d3cbe2f6ce53a2eabd96c0d748d71e070b9171f5ffe84743b7cb2c74a5bd33161a26157e3d9d7cbc76b878fcfb2fa22abf815e4d2bf5ef42cd0b715795fb00eef1f4f08e8da8d982fbe79a70dbb367be3f5c9fb8d05bbac0f0fc8a5fdeef70ebb36cbe3f5dcb6825ba8f75b0a7681ec14fdee770dbb403648bf81301276f1cc434e72b110409f7bb865dfe71c76b1cc43d6b9317d26c22def7d7e825d1c00003c64296c9c6db815baab7b78c8b01a34fa120b69845d3de3a13c2291dcedae8ef58a5ec6786831c1ae0e6513304209c50eef170f252c94af1bc61fd77cc5232ae00dc0ae69430401b8c50f63cc458a8973cd58af5a2cef6eb66917e835038676003602b0b9f41c6b97169301437b633120d55ed6a65df6d52bfa0080a10500f3d8609e1ec0d0d678d178d9196068616c5ca0cda535565faf7665b11618a678e8988c8ec500c3148ce679691e9757cb4b65d3aeec156b178dbd7a459fca010c539e0a187a1968933a18a63c1760e8c5ec0a0ced0427cf6bf26c381bce8693c3898d0db754a7df9ee0d6ead4e6aa423e38a47258c981efc9c777db5edb6b7b6daf4baf8252c14185878787e7d2df60026f001fc3a68f2feae3bb8162b158ecd283a47f7f0c835eed02d9f4eaf502bd4036201b900dc8e6d27fd6850d2ef8923ebe5aac5d9aa6699aa6699aa6dd4b69f16a97a6699aa6699aa66936844e504ef87a1f5f1b6b97b5d65a6badb5d6059732493d86edab5dd65a1bfbb2b12f6b636dac8db5b9f42d365409ea721fdf1a6b57adb5d65a6bad356507d3a9beda556badb5d65a6b4579412abdf8f8eef0f1cd62edca7878623c319e180f0f0fcfa53fc9bc51f66a576693bdb257f6ca6c329bcc26b3b9f4261c128598c6da457978623c319e180f0f0fcfa54fb170aeeef4d52e6ad32b7a6b5fd4e665633d1bf2f14429fa92ce077943ac2f94e4e5cd871bce862305b7f8b4ab696b3daf53ac8f2fcbc7b7d2eb40dff4db0a33f3d7b3b9d2d08b2fde15bd8a7e853e0722971e87292ebdca477f03cea507a1b8f4df1397fe1fbd0bfa16402e7dca073bf9e84dbec8b4a427f2ca54ca217da31ebc4fd4a970df52d84a1bafcf46d0906fb8e15cfaed05273b2dd268f046a446f00dbb9a904f64d1675f280c9145df59847cae8eb4018918c33ba1be498e06e7f2e3e5c090e5c6ee3cec4324baabb8b583bb8a55717bf7d1b66ddb48bbed7a0d5e7c7de43ef088be0fe8383046969041dcbbc730c8c39e7b77fec2d9cc85baf321f592773713232b44e28e9ea1dce4278fdd8d94a37c33f9427a4dbe49ce44883b7aca02eec8e4281fea275fe9b1cb81c345994cbe50476ac46374fb928f87cc221ee12696d1171ed1f701a36f277d3c460f69ee68863790877d3aee8cabbeb0883b437ba89b81016178037836806ccb6c501b361ec302f862aceae1960d1b32b3f119df667c95879edaf88d7a8455f3363e1bd3363e79eb974111e3d60ac80282a12700f08879eb8ccfd71e1b9fbc333ee3965bf633ce714b7ec6bb6e6606fd324a6fd40aa2e05879fdca69ee56ab95d33ec668eecacc0ac5693a84e914dccabec9092b72c3fe99b076754eafe65bc0d05acf638161f760c1ad97ecb95f23e156c83bddb3154186ac21e3f36d84d8ba9fef25c856eb329f6f1e6ed9f87c1fe1160f311e6a498835625c216bc0f87cb914628d97cf18e6e17ed90ca26abef5653890aa79992feb4154cdc380f16c07b2653e86e9990d62cbbc8c4f0691359f41215be66dbc80191551356f781684d832eb73f84b3d0e2ff52a8f9d0f2dee4b4d41b901e524b2eac197faefa5fe2ff536bc4456bd8b97c8aa6ff1524da5941229b2ea515eea4f5e22ab1e06189a3c918928940a75251d175995db501be804d24c9a8dacfad15fea4b350365f725b2ea43edbe6c73069146bf3c9341a4c19155fff22c8a484346567d7906c57ca101d218a650744ebb7848afe6ade5213187def012bc8e0d09a912bc151836099809deb652c3ad1618cadbfa8c9482618ff9f24d5810aff57086f43437664458f5f2ac4837f372baf22c8755b3f518deea17afeb5e3773bf795ca7c00be7cdbd8f612905ab70b6e664778387532134687c520a5ecdd74fd257a4e1430ee129c8e08512677ed6ccdacd64200caf6635a748eb6450704bc6e7b39b4823c6adbd2f8f6c93c896f997cbf8a88f189fbcf406c6e73b89cc5329cf6b992477d25764cde3d0f8329c5ecda984279f3d9c37d4751918df0b0f5f6ba6f518be1f8f18f2eacbe9bb9b99416f839e071c6ef1f018206c9cdedb50e601b8054300803c8ab83beab35f7639609c8228f21e8aac2b0fed4591a00f34f2050587fcca6fc8afbcac7c059cf230be70e52e19f436e879c87e5bcf7e9071dae5a020cacbe9576e9f7d282f97cf603cb4282fa00f34f7e5f22bafdf5e62bc7c28385e0ee3375e0ee3343776315e0ee330c0094e56ad3c2c62e530049057e632c0ac0c5e057b645d0164fe22655e2f41994b7087cc5fc01d77857e61057bcc2b737f683dfb05630cd8a3af8d07200400d8a32f0faf3c9c0261030ce5e5e1f43c7c61129766b754de0211ca67618fbe3c8061052e7dbd54d6f7e8d8c530c5c99e81618fa63c85470f05abe88c9a8b83822fadbfbc7c863d2de2d6cf2e078d0543dbe1a07f794cc39cda6f868decaaf11ebef2c82e1a87f11ebec82b1860686fadaf41e38bbc5a0143db832f8cc3bc7e77ed98f19773f743eb2f971d8e197f59f9a4fdc222aefd8c2fa4b933be7157009abbd2e5c8b2d7577065c66318c6d783efcb37c9cdf876d8bf7c83f1fac9226e16ef4a0ef9fad8adc8d7d7c30057ced9efe533bef817fbf1cd5e6e3f1402c49b5d5e9a8b82a3de9ee6d6c92a5bbf32030c93b82f77dd65e37d01677cc6d77aa8e3ba0ef385455cd7e90db7281424ef200f7484bcadd6e9d728f06a4c8743dcd0f51ef3b63e5badc7b0eb9b30ca7d31876645589593ad7cf16e95a32b9fbc5c3f5a69fd6c952b3060c4589141c10c8787674432222b60d6fae4e5e13362de309391ddb48bdef46a3e06185aef86de641308b78e9813c6ad97cfcf9a974f6ec1f8cbedcbb76ee6a5c11ef43c621659d0dc8d7e1148dd1df42b8ff1455b3ffb6c63fa08a4ee0ef995d3dc182fdfac05b91c6e51709ed4cdd8d39bca7535a4b9dfcb17729782afd9ed512f8c182fdf6a566be56a6e4839986ca9b53e7639380dc8968a423fefcb638028f4f6561820ca0b788d98940814ec3abd018bdc799a43843fc5a91477ced7bdc96e3228c6e0c59a5073c3ca934521b6549f672b48de426c719f671f4ec2ad179f2e9f6728441a3a7c9ea9106bb07c9eb3206bb03ecf5e88357678cb43aec2f52618ee7c953a95c7e5137d209f2dc4d8c28a5fb87c964f0b97afc31723ebc5c785214a95ea0a97a58ebcb11f078455960392651fa701d942296c050c8988476ef6ba72f9b892add04fdb6115a59a911bd6ccd6ecd3ac902df40ad942c109a24829e67c0e5f9819e1cee3f0855911eebcca176644b8f3377ca10de017baf8beb0459862c317a2b8f8c293165f6892f28525285f883a997c6129f585a4922f1ca1be5074c3ec260cdd30bb09bb5276436f42cefbc22aea902a716384bc61767373e7411336a45d51a6553f27ce9d1ae88a32f393045e18633d7b6a3bda4eadf5f6d3aea0af9f0c42c12646e085f1d5a7695aed80116847d838904d192a95cb80e9c1863bef9952591462075620043164600640f0c9a289580c0912ca05976c0925842009cc0a4ad80c2183112748812a01052708e902fd2185076c40050f8450061cac200a5700218786035988e2044d6080091c74000a5db8f3a65457c5058c20796ee812abb42ef1bbb8c880e921ce0d5897ed8a20e091e0721817192e9e8b06d39c10842b58e0b2691f8ce67217fae30a988b8b0c95ca5d5ceef426bd3307044dd85c64f00a0445a09a13364dca6c5bec14a0dd824880ae3d9c004e10f620fbcd726b9f7d4c4b22aa0e03319f813b28c8de64e9c34718421840f053869f199ce109718a40480d3860831c5cc08923c8cc1d2740e1049c3bc1f0ac9a9d1026ca8d547801e7ee6089ee8e9fb4fb737677cbee29a482b657933ed415e86e12e732b0d160a7edcf4d7ed1d6befcfaf4eb31af04e337250d656982adc4c697a22bc6887265e49e19f0e463c7200cef57b2121408e2810d28a1020e402104324e5009e76481b1aa1bf6eea1cdacc3c1b2846ce967f3eb6f14859e05765986dc18deb28fc5876c99afcdf2eaadfb9b8f5d0e962564cb7c463f997ab10f07a4b87d5a05078888e697b18c91a572b34f7bd33e48abdf8e186918c1437463644d56f51b0ce933edc6a6b9f4ebe8b5ce1655421aadcb900e813e80c56db9918a0fc02e0874eedb69e3708970d65acbfd24b1938148180f176a3764188c63200c63552739c2ad20220d1ecb2261122644af4a9f975c0dee7c284350a37164cb7ca831ac5d4d8457f39c6d10d6b1791034b1458e51ce50487381d031f0f85d87a36f9804e9313cd0b7185ebfdac776853bcfe035cf3cccf399371336a72994300924d288371657f3de4629dd6c9f4eb625b7dd0588eddcc6711f3d9db8184779933020dc327d5ed6441adde707ee7c573184d840881d93356064cbfcdb09777612ecb50f857b9f7bd7ddf4a170dfb87e710de3b8fb8834b8cff78fd8af7ea251d022680e7099e9f4edc4712527eea8a3ced56ea6a4e4b69b2981e171df46dc68b4c168d65efbdac6932d6f38b3cf6f57b8f3dd0d03c29d313c49845530edf5bbe70f2fec57fb2042911891d89c73cef97abd5eafd7ebe523c681b618c78dd3384ca48030f57127161c1b993f38ec0cdcf9067267fbe857cc5acfb3b190a738833755319f4efddcf9ba2a0abb1348177ad64d24d2883d6fade7354fbfe699b04d784fa4a0b8f32f96e2ce47c930564d9ee6c0d05acfe30ddc79585c4d582c06db352d8687c3e5582504abe61710591304ba06d248248ddbb46bdbd370374dfba66ddcc6511269e3362ab558c79a48e7441af69308777e563184e88201426c81a60e15729c709370fb49405efbd087b27dfb8ca0da77e4c66d0191c6f679064421b8a57d52018b1d32e878e1cec6c28c1189ac3983105747a820859d26e8cc23e418d19d471197f430f4d2b7d27679e70fdb45177ddbba19ef5c37e3791f0c6fa3020450dced5991fb1babc8000caef62d08088050f1012457e5462a3e9073371bb8ede3db7d3de6ed6e6c17954aa1ee874df4a9dc92e8d35edab68deb6e6cdbe66da51157da385276ee5a97637b76dbe1c8be6ddb6328019adb43725fe4c943b46936940144f669077de1e8ce6ffbb62ce360da15040f56a48a10715d9eb7d385a504e38946d17277f48952e27acc9bdde08efa66e26278a7d067e8a77740a85ced7207753f843eeda84fe56a0f15c1113a0786f286420f9de6c61b3b1cdc4360282f77837be81bf769dfe44471a14f3bf79d3e23e80d674ce56a0f7d2a772be14e33477b08e4ae06ce18ab26169ef6fe2462dadeaf5d0eeddbb30ec7764dfbd6751b18ee28bd87ec8f44129dfbec3c8ce66960e880ab812103aef621aef6d9f9b0dd82e102ae06060feb902b5f779ae7c5abc6403c896c51e5fcc59e4e78f4354cc8266ce4ab57d2c666ce9aee51d9e67b3cc71859e30ad2def1b31c358ecda17c62323733e3d0257874541515bab36e06c416235c36e9b0e9e679811352f0041f34510407324d85db8d84db3a82d0d11144cae8862a190c893a625ccc08829850ab43089d20384687471be20b28a0b566319036b2f5b203ba7ea0012ad0820d82208405361a90e12978c1090bea6055bb622a38022666a5976308101b0001621bd05c605c64c02c21b32350025433900056871041e804b15d3e0ed1ae0dd4f101e698042440d0021a6430851b1f1996c2e5181666906922108388c707f3100e1822e601ac6a1ec9cccc0cba910a11f4dc30c64c5ea029e2f6a5d43e3fcbb16a1e31edbc735e66d0d258c94aa8e088e8a02fe46ed74e4e19782051bc5d157da0e66c94380085de9eb45c8fc0cd03b3779763bb22ee6e884e7feb6714de7c28034feb92c8b9a0ed0b5d6e26a770b9436e0c4b911d0743b825bfc52b4f67e48cc2934364f6812daa1851dd2a822157e5462a44c0c4f571e38d777b9daff576ce09ea7c6023acdab80dc7166dec268efaed5b886b1778fdd03c833e0f024320eab7b95590e596acb49ba931d630e23dacd6e1a89fa07db5df36d0853002f4f9edeb21eff6fa85aabb7d7b0dbcc87539be3bc1faac5e64b2c9cebed0861e8808d5e5be498e3b7db63d86edf6f8596b254b5943b97ddccd6ca0dbd86d6048f31869f4fc7c0c831ed3a2cfb20a3c3945603843b6db3edae198afa079fbda0131b58c1e2143b2e3413cdf8b9ff173143f636b07054320e28f90f4936e1dcf8f2cf0e4ce7b055ee85de69697c2314f4d1c69c8196b6c9695861a0c1bb7b871c648a3bca5dddd2de2297673ce1f7e3bb41404c35473e3e4ccfbe85fa0d7f092e1efc89d1a15584a267faef4e9559665974658958172e7c88d9734e3d8531c18a6bccac5ce314929a594524e125d9c94b7b1181301a910c1cee57c489906b7b96df472475e959049a4baf4d2081014943ba03c92d32ee97311981302c314b871905ec910183e95f23822ac92e720670e62328a9a8bc31057f25926931de472ededdd408e4d18782107b93248160a692e115c65232627f9417429e84d19ee8ebeb318656298c49d32fcf14ba669647348a0220b486ea4220b46ee0e37529105286e0caff490e3b4da76638deea147ba117563490bfbd0450dfaf032d2a85c64f54b5f175bfaa44f1455dd423f2addbe67ba7d11eaf64327b7dfa1dc3ed7209055a1fd58becbb7341e593adc061b5ed8e0c2c5ca458b16aa1629292b29282839a09c9ce0706262a262924add902a29014b50a80f753afd6432d9602a955c9448a416a4d12865e479289e4874220a854c425d97ea38ae84db36d406029d409a66d2ac2dd95a4935cb46d96df9d0a3f37db9234bfe5da471236bab1f5f2ae3ec908c2cee9090c9a0e7b1280949231b0484910ff286bcfe4972ecc831efc0e6ca679f3db3652b5895edf06ded11721e6bb7910959c3aa4cc2260dbcceb2aa23e3e1d1e16917bf3cc97365f69a9d5f200da4df2cb3057db71fbfb9ca36abf65ac66cb3f3676d8c2fb6a94e4424974d29cf3e19f6b8dab36f36e1d96b1f11dccd62d8727340b2d70930125f83d207abd8f4137d8ec0f3a392da4f273cd0466b95e037b3ee5a6b6d597b03f5476f069ecac5cede096fb361153f71e2c99a770dad198e9452d33e419a94de174a292e7f03753353c6899efea8977919cd28a89bd1a4949a3581a76907d96ef91a7a7f5224afe8b309b90c04aa19383f29a574cacb83aef56bf64c6add8c3dc7ad08a4bc21773bc7ec92a71214d29d76f6f292e646273c9b559cacca197923a1b89cbaf2e686d246029913c8d4be5aade4805c6e8acbc1a89493d3806c999f1c631a5097d0404545650b5316894b446a1345d09bfc662b6516d1944a71cde51803e10ec816f9f8188e5c03929d8339e79c733eb5b594da4e36a976856c61fecfb412480d902ba700128223b7c68d548440c8b5a067d18705e2311163928e3b37ce9c768181893f118bf9782927fa28eaa27bdf184582d5b4b1d45e294b29b95e72a5af9ce8a3b75fe8b10bc22d0bceeeee1afbb06bac8b6d74f9fa2485160c2dcbbbf89ae7eb23add3afa2b37cdbe08028110ae8496e06e714cc53caab5dd3344dd4b5cf547253edb39b01452055c3adeef35a6bd3d95cb690d263090788e66e956b1678dee7356e893e3f3baf6de6e5ab5dec8357f3405ea5cf12d8437a48f7cd23760e61d50ada87893ba98e4ffb50b07d1af4d9668dad49cd29d81579e24c22bd89be7837edd376421aadcf22d1b5f717ca2ba27dd1b5afef7d14c5bbb67341b01bc39b48f3ba35ed0ad9a25df4900abc18afc4e2c535a003b9edd3493694129444496951b40485f4d59fbed417ef76eae4fba33216e9217d3ae1a1d807c37e58758272aef9c1695b71ea99d61e42bfb627c927c952a27c210749dd50fe904ebb99fec9576ff2123e9160eaa80d55ab949251cc2c254b59b56e8634849a2ea5949e6efa66fadac7c63b9d1ec324898222bb197a4eb9641f2de46b5cf4100b9e3e795b7ca659039a803525e0ac6196cc2cb7669a8e6b77b2f254f2575976d27633a47712fa698a9c62327db2ab4da673dd8c692b7d2be120d1dce98417ca9f185e6791451659c8b015330c726c3f7eb18f3bb1e016ffa787b46b32f1c3169c38ed8a6c2d8873e76b9fbb2747faba1a29dfa9cb6720fc1cfcfa2de3f8e3aee676b16bcf5f0744b6b4a45dd701d922e94c81471fd39476355d4d07640b3dfd4862f18a52dee05cf9275ef24762f1f18b6b50e035abe8f9934d5cf9e624bf47c879f42382bb3dcf40ee7c0c9361e7b2dc480519a0b8db16b9ede3807407f5bbef03bd3ff9f36295c4e263907db06a327165cce6782f1e9876b9b8a854e1530dd3300dc3383d2f7ffa0c3afd30e66ce17a5e779df27b00fdc683524a2548bf9983df343b0d4160968572668267d5104344567fcba6083cfe42f9baf33453b9fda9b3c9ac20954af5bd4f5a2b6fbf0778df78785e0eee208f7362235789e4329d4f0003e88834b4ce2be9ae7d03083d745b724397605872c3c7f04697dd8c90d23dd2ed65496adb8bb8db91d8b66b3779bca98731c6637b4873b76fdb25407337500226a9a3dc5a7b2d25a5c527a4e429d6821240018598c00c98fee3743a9d4ea8ff309dfec3f493ff4095fcc709ea26a976951cf5d09e1e7aa687a91230461418638cabb8d2c1a40f04e5e3a5ef7bf61ee8035d520a8a9de80bc5585482cc3e7909d0dce89d48706a8fe149d02635edf29a7c949a263f65539a11e588cc39238bb7fd07680365df3260ffa3da5abd766540fb0fab59d0539a06da40fdeda0a8e222e3c6ed8b6d7bd5abe67e0c8f8e26377337c770129586f2f4e96cd2f948e93b9bb31fd39f9335b04595b0fadc5905bb05183446fa2140951529e5e3cae5ca57246863b77e5adbb1a56aad1e3f4ac556f7f894d87af158638d97c750acb1f295c3004fb1867c098c472a9fd81e10a6aea5965218af1f11a94b634c677bf17112f6e12d44d5fcea632b48d5bceae32844d5fc4b156ecbfc8ad7322f190cb7653efba01059f3bc235be63b055ef61901e32e7ff98cc8cef295cf087bd6e56704fd0e30be1eded5e1d9d7c3bb2db75f0faf877745f2865bd3da5a6b05ebb329563ebd977a0b862b60368514ac9aa31730b45e064e29389be28688d4a5bf3181276d9e9037373d4be085d9cdcd6efa2b60682fc130757b08abe614deb39b6fa7210fa98fe14991277a8ca2b981a82a42a4413390ac918113265b26e81604bd821b38351059f3a4d349e90cc5198f02a895c2a8b5c62a915a79aa4e8dddf9da236fed6b37632dad3ad58abaf3b2f28275d8b88dfbcb4a17eafea2bb0e2ca1959addcaccce388c0f6600b39b71dddb9a715b36543030ad19365af7ba5c36fe72eab2e1d2542e970d57066385c677b3b2a95c9fe1b2e172b95c2e1797cbc5356386eb8bd77ed2e5fa6cb87c61ccd5cfc56377a3aa868c70b815573fab35c81af1d29217af3bc291a2e4a65d164499b7e17fbf2404a24c10e500f6a127e0a00d60f3f40af410187b65c1d8530a1e88eecc2a78f5a09bd0bb998b8d88deb92ec0d51a64cbf6ec0b5543ee567758255f8db04aa248c1aa1e8223bb2893c7a3f4dbe426153c52776ecb28cc4597badd0461f3f5baf5d937a7e055245e77728fdda4d73e9a3bbf1edae5409625640bad4758257b5086b4cb471429ef61ef491f450a6e19c1a387bd28efa3e0b478c95168106bb4700152d363bb4447d118fb27ae40eff9baa0d4d748a22a05ec9ea8025df4f9689ff802fa059278c988be71cad73d28df11f69a807d24aa40dfdae4eb2391057a8bbec205bdab105b40df66ca27c4fee41302b332f629b07b7a05bae8037da35cca97fafcb28b92fabcc94fbe23ec4d7d4be9664c40185eca24f5183ed21980e6a6c046c22ad0b72cd650f344d69482077a58cae99d5e818e6283db4785253b252b6edfb453b24758d53c36e4a460579489239cdb1f350fd7c311f1b89cdbf7b8d8ed7b9d0ece1eb13d968743c2957278d56faf095ec8ddb064a764676a9a565fca291581e2e6f64b3f261fecd27c4a56702b5601c591dba59e862207372cf970355030e186252c6e3ff46e95ad6805992152703ddc32dd7e094fbb42ef97ecb44b54b2532ac2ab3e85b52b047220cc54429982558d12bb8d32e4a2e0941829e129d99951f0686eac3b573ee47ab80505cf88a923087679b364a7eb6626d758bb5082b00f3d04228192800ad8871e65ac1016ac3193a9c4d59cae497c7c72a23d9dba99931156b5941ca897253cacea921d9d76d9cbe7d857c5e2ca5a89a85fc58255d2fa28e5b4ab64a70e69174a10a18b2e0291a83abd920f8175880cca043d56f553bdea9bc894724a764a39253bf69ec8933e95cc2478f22659f604af450bef66377a97d905e74293b25659b5739fa1af3e37bbf64160c2ee04430afc5c2226ac040a567509ceedc7929bc8eac74e47894daffa455855d26155db1b96724a39d6f2f0aa1f41282090e5b1478cdcbe56a335c12e938d45c2ad58850d8adcf674b0b87d3f6334c0c20d35d8edae268a58450d3270438d89db329af474b815469d1d6e8514e659c1ad5885db3c37f48cd0cbadb0942333440a588996d2b89e5ef535302cb9094b76726e9feb6957f374b419d7733b2cd9b9fdcd062e66549b73ced9c49d555622260c012c63c1dee955f6fa6507d06462b55a8336ae0b89bc51462a9950273969eaa44589898b981242c191440c9a2e89492b3bd1c4b68928f78514ab58caebc4aaa851c38dbd8adcc4c61de4468e31ee581eafc70746f582619b1bc619127d62d12727fae8dcf4dcf8dcfcdcf0eb866191a757cc13e3c9913c3a926747f2d897074bd53c88ca2688bc71f151f59c27b5c331c70546e57255ea1eeb34ac5f6532192a33655a8665a48c9599577861d75c7918b6893e3d371247f2fc04995778a18545db399125dfed481a5931b2228885bdb18a0e24b952015eaca20347ea8d5574200a973ba7bbb18a0e20410c928a1dd8e5a9c98721091caba467f24c9ec94696fc1ccd7f7615bc21e24a874bdc818d97314c45eb63a3fc43aae1c64b32dc78e985e8b29f12494b232e979a541a37e299ea58cf6baae3d3d67a3e6dade7b510ed319c9dea509deda34ae8db76718d9c351b4806afe8b9a65329aa53a343333993f598f06454a33abdca3a1b59f3b1035d5845df3e3aed334fdf4b8834e8291362ab4f4f9d105d2e5447d660a160b8f431522d5c4aa97069769776b50f8bac613d7b416065b9f45bd7773d8595dc4aebeb4d462ea912552cb195bdbb7edd33cbe2d53e1fda97b88c0646917da3dd4c8330bcacb36c569cd929b14516af8a1f412e7863153ec6703791a453ca7863f217b38c6783f48bf194360babbc209000e7c62edc48c5088270a9cd1663b482eb61dac870b95125bab8c6a65df3876bda357334303c67330a84bcf3a75d5c5cc99b2c07fea8b05c098644b88c40ae51656078b03ef4d0b986998834425f78aa930b558ee356b7dcc621e5b9b886573327b6f0fb918b791d433f127a288653f61b88a38fa18f6fd794e0e51384e74a14dc480509621787e86a5d0ed10db1b45cd685645665a699b81812792352890b854221b0f361de39b9d5a10e8788f3ba6f235257b2a693e5341ca252974374fb9b893bb1e4100a0ebe6105faf2dd4a9853b32ff74530d47143225c6eb79c21141c7dfb7d1b0c93b8a1cf6e15162fbb68443acfdb2cf0e20abc906b98a0d9697067eea1d06be51eaa620350dc50096caec7fdb8202658c562f0e6c3f904b7f8f3865b3beab4e156ff87c801c13df40e477de8b186b699d3ae2eaeea4fb6be3ea75d51c67a33876b16743ef0ed8772782ea1f7c6719d715cfdfafdeea16f47fd81c351450f891efa26e2b22f74d217afe8312ce248a1d081085df4d8cd84bc4ff4edc81ee21e4a819b889b44f0660bbc11d74d8a2c49a47edf0d87d91c0a2b704d39b4884d2496913443d32564d96355d8e2865c5363ef248266d2916c01826dd247ee7c30b18a9952f9801bf689487137b717cee66969d3669af59412b8116f8337e26530c34008423064c810969e9b2f08a974c2a1c6343a25f9f9f141224f4c2614ca8432fd8405c94c32e7c7d2639a730373ce1a9b5f109e464673741acd9e1e243f2693e90709d81a7a4c23941e23a6d3a9449aa69e76a1f4a04c9422a38f765dd7733a15199548a72153c472e69c467a4cdf4627272258d9b06ab50a3253a9d1d7f734648a584e483262ea993da64fde911d9548a71e2986c4727a78aefc098542753323934904debc89450b76c4a205162dccef74ea399d8a24f9f139f9c989cf952793098532a14e7efa42224e925cc982648ee43dc588e42431d2d36342954828530fb766cf9ca6d7d32bcae4c3e2c32ac982c4f4b1f44c21f4142991e69c736e45e69c52957134b41bedcdcae0cd20d332cf8d37b44d42d264b66506b247f6c81e8ee3faa77fba7f9e100155b7eaca37ab98e0e2e242241623326113f60466666e261c416f77cfb76c1fe47904404706073ddc3ae2cae0e008b7e28d323838d2c3e3f87941941ff8603c83f28a2dee000ca74872ca11218cecf01840a7c89471ce504873f94f99f3292a29943eabb7d7586a51d342214d6ce9c5243391e7a434cb6aadd65a2e06253830989430ace2579a5bc170d65a9570e9d72d322232c22d7909a60060ce24dc100097250f072e4b50c4135941f0425528e229c1995e88514a5999abac999473071eadb55a6be93cd7787d0acba4949990fa189e31c6ec339395cedab5e76378b466598682a1609a06d3609aa651a69f10db26f0423eed22917e7e42efca6cd22c9bb47bce466100054301a981a13030511898b09a4d5a61192c9b9f10edf4138282a180b0cb724bca6a727272424249494931d5f909a9a7b567f79cb046c17a4e1da06074523e0a56b3af6928a7a0618b71da94df90b7654e8c524ae6988517b64e901aa4045ebde46aadd6da181eea15513e2e007a6d9bf668a794b2a3c64b789366f3614a7e548741378657716ed83d5feff48a1f4a21f6d92724d4cdf494a49ac8e24f1c7821ead573eb5ba706e950af209f2623937821a9e6b443aaa1326463b35be7a7c9cfd6db4b785dfd26a5340103ea85f28182a15e2660300143a4116fb5a94158d504abf8219b76d983766e2e7fa3a857eb5c9e968235480d721d4e7ef3d24f88a61d4093b1ef90b443aaf94841423635a8972642e1be2844bbfd6097d1c0d013211b5290cb9f6c72727242fa244a4a4a8a697e51c8d34fc87c366f3f1f51d4b770e16226a354047227b23864c3aad013a19b2a6cb81582762ed730c1a420a426b8151231831cb413694494fc502f948f2ff404aff8a86ea6c1900d10bc306473f9a49a5973f9a817a35e11646ba699f8740b18c8eca1aec6ee9deedb688c517bddb0078362a7317ddce2a9c62b2cc0d50fab98be792cc7b326d6844df7e58df69e40affadd0db68b6f64957ddb3737375a070427d5d5c4a87d5d79fb51cbb61ae16195dc892ce983553056b59474b34c33de6c65cb9f76e617abecf99c51695f69a8b1d58f1156b14acef31238ede2beddbe77c29e6dc50d7ba79bb9412f7cd41c9dcb604844efdcba5a4305bd78dd17383786575880563fab35c8160ea863a804ecd70a931a65aa66040000001315000030140a878362b158305145b9fc14000d8da65272529c474914831432c818030c0004000098010081999248002e3302ed9ebd577decbb15e557dd765d8334e0e23d9cfb577cffc74ec4038fecd5bc35b0a119d0c930f272a3a1ae0558ee0cc100b274c5eadd9c2cf8d029451d1fb675b716600b8e69f4b75d0bd43f911abee1da2143d85186648d5dd10fb59df3ed902d147c06747aa9d1fb85fa822388a0f613d086886f137874304ce61c93e75878a9e435b2c4a0268ef24a27e3d9ef1d6333ba599823b0595d1bad0fb265fd64d14e395dce27807770617ef15dd9755d37abf41bee59875acb0cd824d71b99b76e296b41b06210155196441e8367d01327210806aca417765dac381090bd700717002141f4e8925dda174e7d3cacf0997c1d8c71a8f2dc131216d2dd3757f9e18efdae45b4386bb5593e47679ebefda5aa6095abd63c2fe2162b5ad9c6dfd2b11ab819b242206a59d241ac6986c2729a963e9f1956e81914faffc6c28cfef7db1afd67bbfe4199c52f38435fb20aa8b45480eaf1e708b9428864d6add5f457d0e0bfe8b034cfc9f875e16f33d8648960695b19a74ac6ec49061cbbfdc6d9aee2c5e27c247caea0275414ab0c5b782e5d309cfd73312864f532103fd6ee7a4b5e9313c1fa18e7fd6406da3aa86fc3db444c63513c8ec38534392be690663145ac8b948992546e5c826cb52f63231709594dd4ce411237198a65b2eba253801e660d072257c75bdc74f09ce9338612d9715430d04b44ec11eb3d857b73960ce27e6ac4b668ccaa8b01568e92123e6262123f65bf9333bb0b65f0991098371494356308e6fb31f0a4049872e680d66418dc590a136f06b6e09a0a1237e39934cca2aab107469950bfd0fb62cc0c8b83f9360a609b79d5f5c892632ef63ae9a4d6b91e823b978309499f5d1a5686d2ef3e2887c45bec6efb2a2eef32daba0c7079a7cf0b60dbbde876a64f34f43f68868d33a881ee6a2e7339163a01c0d640bf985ab66ccfb45ae8da65570e59a5d468260165e996ee2b38429f3afdd7a12c738a88b1f4c56bad16b5917e932139f5fc1b02b4383a9fa3b3ab336b9294ac8b3a298181720cbff68e80f825ff870bc665be8d22fdc824059e8e1cfc61d23c67b7e17319f3702e7fec287e36fd8914d09d824a2f4e80e4bbfcde40c9877b23bb619c8b57a0e6b05f1c12be4383522c2107e9a49442903e371d2369d97984cb0449d76555e8cca3b2ac6cdcd95371cbda2b89ab872b06abc07b83a7bad16065fbb71e46f4d30558f36b29d3b0d26e967dc96234930df2283bd5f35a72ef569be5fc0883155e7918d55b009f5ae93e9cd079684f99b47eb985c4006b135ee4648632f3890b870e11ae960a70c79f32999ad2bd0e0b55d6813a60ea742d6f808a6ba741acd49e7eae1bd6f4c103f2d4e285f281678ce89d834ff2199f82c280d2286aabd9d25118870572f3e04d9a36a0547751fa260a3e0f307a19a32623463fbb96c73da88f15dcbe0e8748c720ace0a813d2b16a7acf39f62b22a6ac09d53ce7557cc1d5b31aa7b402dc8465824e5dcf053cf44cf7253299ab061c3c247785b9712bc0d79457260a4ef24c6fe8be797f57def745b0ed6c80e0fb7b42742a4168992876fe1aa2ab1991dac4eea23532916a58a0996849acac768b469e98f765342bd10810618d20b2d96da8944bec79d4b2b0e46caac9d89d932ea4d2f275f60d89cb519c8437ba4720f4ed59504fd25b523e4edb31f63a7a82a4543b8b3eef05ca583487b95878712d3f4da1d88b93e42d54f9af46d6f27e72b113cf9a9b774b518dd0a5450f3bf9edaef17cb7a8bb66c65780af482ccabfbf81d76c87fb2fd3aa51ecb069e5e9ee5923ec52d0189c3b98a63f218dd7c5c7329933c2288c6818df195ab473e732d1d7fe1a1a85e43e25d01f53b4b1021cf8f88817254c6cbac4dbac77584755ddecd60a7b0efef4588365ccf0e067ee0a80463bba5590ffba9da832509f2020ca3c78bd81320902cadc084f4fc5427633354a5dc4f934e2a740ee782ef88e8d66ece1e6d4a63ad00721c3d13500917c7f207ef43435274f33a278fc010e12768e47a52b7154a4b94107b50d92c2e1dea11e0dc11260a209b81cce254f492febed7dbec2212446fe3f22e871a12c46b47d83dea917bef7bda9e72580563eb5f3c4d80e2cb5f5ff1c53eb0d41a43aecdfb93af44f7e36fd72bf612a70ed1fc92e44520e436ccfb65975675cc1f14e28087b6f9f5b26769cb8e6a722c3a06ea18e1e7ec38abe30711b352da7235e23d10c4364f8c5f9bc2bd61f4f52f3a75819c673d8f77b176d325f3a4815abb26dc9bc5448dac62822a8a3d74791efe857a53403cdd3cae98b9cf26baf4a4bcb6059a6e4c871678a5abb07c8a26400c03f2493687b4b20df12e4e3364cf5107274b1dacd2c76765f8b8e8123ad02532404e50166eccd88b6485e2eb9ece1e1989a584cb7c43711e08ea619c1d102a5a5b6561908c884cd7f2b786b258273ce834c34103f4430429a21913cc5ee6ff448ee6b16e51a657aa3500ebf76a55b8e6978f15f141ac857225223171ff6383e08a47b2820c38da23b1b1f0fdbf2ca5c7cd1c055c61fb2e5bb78e233c313412819be9755a071a4d44e6eee334f95f720261aaebd5a6c8ee6dea5834a515dfaf7463d7fbbac8729d1448e522dc1d3582b361f89830a23600b537d91e6a755f8be798e54dbc95a7e84acbfa0939d71909b559d1fbbaeaf05f645070fe0d10b34a1a8ff5d70783b0cf2abda80994588d1dd76edd93933b600c4d3d8336d4742fae7e2369872bcb6e3fa2b88b2a73608727a8debb48d732f587abea9ecb5fbfc2cdd56b1d3362405e78b34690c2eade68c32f68ec88839b7fa71ce28838a1ea142c0c065c6e173b026598a7d24310368f50ec9f43f49844564724e226a47c87e11b14e34566521d6388326052d4c9201b6d91de1c51f8e9d54fca6bc5907f8096f2f7bf34864db1d39cc95cf6b154d645f0be1d91db2d9dbe328d542e1bb1b33c58444d6eeb789fab2e3a285e1ae9663968b1a791fd8ad1b6dbbe9ba105880f6f9b87a254612e51d6b41a0551b40a49ae8a9e87783418fad8d6e5131bb578b2113070cf15d7262b2a8133adb85e840c7bbe8777cfcb2bd0713e2df0d8761dcef7e5901e02d36eec894485a2dae60519e4594689ee33ecf842d2103de151bf02f639826fd3978396c0e9d01bedd9c27a7bc4c25b8ba9d4ddf8608302e80b7296d63768beb71009de8d3a15afa21566d06a8929ef1c5093097667eb6ef2fc97130a1c9c6be18ef22525518629f87f1f1874a29cde7c5a6d254f2476cd2838640810a1d2284b4cf86b164dacc4346d4434c2327b1c16c7607695a13a6edd3cf33274aaaa542d9d85c4bda9bfe00d443ea15863b10030c3cb6c738dfff79ef18f4b1600469f8f8879e62a60c381e399f17b51d311eef1ba81efd213166c06c8fe2704d827ef594a36396bfa3ee2116cac554c4ba488ea4f607baa2142b3ca2384a4d0ef88d26ceac3b9a3d8c7712805fe3adf18e82ba5820e236f7f903eb392520649cd6b480a3028dd2e912a0b44cb3ec33c5c6f3273402c492b3a0b515b452b28a4b5c59fc116c17b90d0a0ec82a448ed5bcf5f4ded9f5224993435bc63152af1b8dd74279f4c175a82ef0d803fc5845e25ee0b549a098ca06ea52875f5c481fd5656daacb4d6d9f2551effcd3e51bd12703ed5002a27a48e0d3748c00bb8624a8189911449ceddb054d02f795b8f5df09ee1b070e321c73db961d61980a2c72023e4080fcf5af51d5cf757c3e788836d921785dbdd59b8c1b4671cf07335aeb72069fc03ad6f60cf9225957842627e442809751c8e0b978529897b3763d707c65f460b9d61d1cb911c24ec114a47d1cc4eec6fb81db4f530abd631a9733536887cbec0f2ecf4b10220ae71de4f900339677aa49ef3aec47620c2e57c1a08ca997907ee99d645e3af36dfbfd31bb4aa06bf15dda0d76f29defdf4fcbe1ad2a917c90443ca4d93c7a2f517ef6f745f9ee9532d40cc76e5bd9114ed209682fcfbdb46b6954fe0eb9f62d69f76726209377e681c619b2b60eb94700590de0c5a38a408007a366f5ff88e5c249bb7af4c16b6348204f6d735ebaaef04ddec718bd3dd6d1fe3524a055b39c7be3eee550ee09aacffb4dcb2f8d90d4a6b6f7df60ff505a6d73adcb22417ce490fc7f0dc4a3d70585ffa438e8a17364246c303818c4bf3dc330d0a000c80bf36d94853bc3b6283fdae380048566ca35b4e55802a95cd906ca9588da56da31418008d6fa32abb626495117f81ed13012347558f1ae270c7f1eeab679334c3b9117315a3b288c40ede70214b15e6c77b580dd9766d36d3950af564bb9c9224b3c66ed7d211b97d3bbf16ed832148729f3ee4cf1a63153f21712605d5530eae2f53b18a96bc67dba2fcd570eb7054a65d872aa4031208aeb135282600c7f6daa950801456a7d01d8d07a9e5ff606b9d7053d22f3dbcb6d31b62f773a5882b0464f3070c8c529d3642165f17e629995259e9b094992f26f9a83da40940da25fae73c738f67eb21fa4c2e870800169c8883e3b5c0442266470cecbd397409883beb04eceda7b92220f370335d8deabb9473442989d3bd784b0b737d2b887c90746fc0dbc0db29d295ba81a9c9a568ddf7acb38e206c58c6b8530a5c001acbb1270c7903f0828961e6b40fabcee8fb05dd94eec40a843620e4652f6635e77d0601a396f4c89997583ac5fe71b1ef4ad4b52f3c271bc9b400a1b5df594db22d978967dbb2cb1872c369653f71255fbd429451ec3e8ca1efbe30a572ee68717591bf3f0097ab070c171f3ee11e6857c654601526667da0488b106476aefe63e5ab23cf9adb8f82eb44ce973784416fbdf3a62cddb258059b43d0435528a0bf841dad7a9118508a04fedafec935ab78e4a2cb29d57dcb4dc4a0ab133ac9002dd5a621bd63531ab07db64c2e689568fe8d1ab1b6700973379fb9968f337a57bb02e120fc16b5d59ed33917e72cadffc80a5320c80193c9909b1be42491afc06a825ad0880407fc1d8db9b6d11f6be3678b2283f3a4db0d00ccb66b2626b1fc5a7d837b4bc921ca5904b63ae4ef482317f8398afc2c4619a62f57bc5602c3eaa3290139f7c83c10814c453bfa48d4534bf258e44b12158661c3d3794561ebdfb223cdf1ef311ff86a9f3637fa2c1b74398661c815583e1a642cb340d00ee8bff18ac2dd499daa1d688e9cff0a5463e9d6d28f32075be51aff268c69181c146cd423fc71324376ac97374f4c40932ec811fbb9db8633379a758c44a671c6ece3f59b957b1fff1b79f885128b1c6ba9a71e8c85e1c8ee0b7592a342e9b6dccbd74f9822b13c805d53e0741bbb1d67f4420e96fcd38d8cfd49f804dc1c80dca97bfe0468d39be6e771a2f403ce8b677929bc6e395d185661c3e5eec21f5919d619a71e4995e245402986ccaf212311144664e6d51692d3a18eaffbf16359154e19568a7cf0defbbfa21e0ab2cde56a83597413a542d14f974330eebced71a72b6cea14d763144dba519473805bcac2336bb416364446da00cc76cd0f8695cce4772b274634e9878654561d5d409e3699f1ff8b8dd1c3e8601cd38f2d608ddc03cf97e00c4adf01e1f2e6b0687e38fda2376fe48b6837dffae8987ece72733008687b41402cdaf4dddfe452fdfa9d80f7732aba49ab5779a71f4e6b8ab4235a83e259d549b3620ed09f9c44205eea6c75006b0e731a65b822a431f658c6ed494b50bc5746bc6119f47a400237ec41cebeaaaa446d4f440b83de30885d67732b617f7b9f26d129a2033e0fdb1ebb66d65aa83a6b5eb72c631097d3ddb587a2adc9da59551e961bd99b3c1f5301516089e6bf2e5d4520371b332b48f39aa6e21dc19d19fea18edae7271f86f6d2878b257a2352046b7280f03b48cad4af38315ec142557748945fc52ad3434772fe2136cb32036abd87625bf107d45bcc95a19f96269b2aa3351a2ad2e22d5ef64aefc8f92f703de1e5219a6455786cf1d688700a5c169f0bf25549cde4e3a9d241f870962eb6c86fe4f6f03ceab5fdbc467a5872970d23414bdc4b03bc175c14915b2497c4c8b98415604f5ea0447514c86cb73c75a4f724093436627f4252bfbcb366d88eca72955b2b2a6fe98bc6b7715877b634b36ab6977a458b955de284ee22f1f0ba40cefa48887f073107bdf2c5152692402e711cb7195ab18bbe38eea8a906b89213f8961304e32ebe80af21fa52c2000676561f61d63022ee31fed75dcb4c6ca877392e392ccfaf8afd474c5661cf6f397b037d200773460cb6d09884499ae486c95fad26a0fc7f2514a09720e0b5156c348adfc90799f2eb86efd4b919ddfb5be12f875e0c00fa10009becc94cebc818a56b75d18e09ae4b9c0427cc49c71460560d1d82a6ec779ae86a6e6105522f13a4ae8d3afa8d0c2c4f70a4f1fce61b9b0d4399d4244edd5da44d18b872dda3ee46f0882c7fe5d190e57cdadec3b216c6c2754e89c5bf932e88ed2c878e8b0ce25e98a154dde6791aaa0c96184a43aa4da2b835db485f1fa4f7f6c29bc5c9e24e70405ac120f44e9fd26a91ad114f6e0e481136b471c683e1db7f1d6c38f08e1dd729de455239042b10e34607fc560a8f03146d227af7229ee3fd4bc5ad80d951dd84e748e29e8400ad43f09e3fe5c9a6ffb402de3d1641be69abc9e28680cb2d2c2b0e324f9a7ed579ec935fe3cc4076a714aabd7c79623176f37b53c8641f57fb11b2f6eaaf04013e7cd68867e09c13bd9bb28b0b60f351703cbeee3ed810b7e7ba161a448695779464ee68960936cb2ca59e8b6c8fc643a03356d42d82aa91f0693b561485ac0f58835540d17c6ee0f1af6c58d35d586face8ea0b162acfe091dee4c46080fc5563f26b0db9736cef9bcf9a72f415eea56b739b7a054281d7b268c9733a9cfa148df2828ba3979946a178a446f109ee2c06a290fdbf1a12038f937022f94e3d7c3341d3d4f2aa8d50ded7630821ba6b4503c692c4779d60cee4660b6f3ebbbeb7ecc4f60476768f43a252650fa8c1730396b8f651e39e86b4e1e3c728cc08e90ddbbf0e090724b6d5e5b1169f371197e6705fa3aa5f37a5b46420e38d018c8acc74d7c7f9a453c53cba4b825ea4d604fc4d1951579de74c0a1bbe9dd64c8202611f8ca9b6c2345b363137c1d88e3ef67136941a3309ac9f7efd7912cf130eebffae2092e3c2e1d2ed87fef4e7425d36081e8b0a5889dbc3c2f3a22d1188d307adbbd0e288bbeec422126f48c3159e3a015919b3e0198e4c618fc889e89c896b0a2a552c4d29b81da6d1846ba02fc9ac1b5abd96f1dda97f12ef1bed92d1e61c475a3d611f9b945f459cf6051a4c3962f70af4d525a44a3106e96ae58b28780d0ab0570882472d7847f465692c34c165793e819688383f182e7d4f94eeca3fce276f6edc3c042b8e45a3ab5ed7a4dee2e01763012d13a1f4eb904808569327e1db50743821af3bc32b22c2afc2829dbcc1b67b8c574956511cf4efe0d519d4fd84cb56cd7b2c2648d845814494559cc1268d64bd0aa13b007b6622dd26415a26ccd11f17917bfaaaec38ad3f160876cb6a6b385b380b8be0bafd51a954a5411ef5632a5830bcbb60a6c82a479ce8595bf6f00bed385e895bd07d238c68c958c537f0f4d6865bc606e85c7a46f35b9b1212a0f03328361d144bd85bc148103e71a72dafe2a70562e4a0d59d924e3bf3837197f333cd5317f0ce5154ee03e6488669b5518bb140bc4d4bf9972f0af14776bce86b1d1b73954de37e62b8003a0aa4e5628bce886dccae018b19ef4f504696d156822d4778f72165b9cac3788ea211d2d048650a07947e94af77d621b416a82de9bc4b8138364c3ad866712cd461c827f819a93f0c472da152a761b58590e1307e83e696fe15791018420be5622d9ab4c31411ba9461ee527f28839f5068835eb08f74eff2214209a4b281381962bae835c776dc52fe88a00914e2297f7284215e0f01e9af60a5df1a9fbaa1ccf9494c2663db31bc57d66a932037a466ac7b10e073e66111655296f50f03c5abe701f127f4b07f1a9dbdd4fc861b023de0ff6be66a25b03467c16d0f258fff2dabdb90ce87252b43e75f928bb6e472af96f1e07cc7dc18003273b928bd270ba31b41a8dba814d764b157303cc8a60fc355392e5c5de170177d7af4847a0ad5da9daa9d42d5a00e30a70aa89441c30e22531b67ecfc5fb2bae5dbaee4256af265cfa76594e538c7d2c8327c7c4f67d347fc5813037c81c30b288ebfe2d3ac68cf1de49673f3c17539070fb36cef801b6cc2d44aa7f45771ac15d9131ff3adbbc71132cf7142d207610e5cdb86e7db35394d757a6903bec303a7adec186d701a87ac08e0696904b61c6e32a4e05cbd5c4f5707107b5278e0e89ea8ac99daede145549d01f3f841b12f467b32880380d27080be1ea9d8373c2b36115595df383a637f48b9a09c7eb65d1f578ddbd69c685c38b8e3e40a85c0bba1efd3cca764191e1d84c0447976e43f11d0dbe61141d240cb051c36d12552259d6a10db39ad995bef9ee14046e70901b0a860d4a3131b0d8a0d1140f09097aa540da3bf9aa9990a8fc420a6cfb64d6330b3a010c114aa638cdb669858cb4a3140352876843b3d8ec59ad8b5207193e09b711f1e42a56a9793a9680ae9954572ded55662a815109b6b858f31454628da666c2dc289ae9e781d468ec30d9780fd6e28326a4ac1ef2d22d783f5d311d70a5e16caa28f0239e73ce2cbf1e4bd523cf9d2529ae58f27f96f3608c8c5ab2adb80ed1c426e9a2a4950390df790aa1a2365230fd60002a05fb333dda508f0dce5d792af142436dd7fce0c97e35bcd59228bb5fe7eaa235b49a6144489ad98aa0e74e9cd76fea1b0162edc06e96d7aed8424c48285bc54e0640854d5d7d8fd6ae76190c50f81270000361aa8f248fc8a1ee5e9bd7b9c9d984ccebbe61003635d967d5355f872e01b0e0e0c03f715fb59cc38af5093221abf5bb7a0138c2b6815548ea35f4f701af4706d41bfe02a48d6f3439295a8ede7f01fabd5471abbd70e55c3f7fd49ead75503ca77e35a2bb419f675d0ffde93c5b4be1beb8bdc9185e9283285e09e451e8d894ea192128cba5fcaf07eb5e2f402500570d2c2abeed44a7f70a24eaf5fb895732cc93addd30e9e9ee746e389f88e4798c73918f1017f8c3df34ceec50b873f50a98829a17d46ce205dfbeef8e5d79ced203a91e75fffc057fc5e873991a2a6342b557f8af8eec5831c4854c2fb608c99646863bf952c5feace752952c405e2f4fec2bfd6a2b838afb5d6450ec3978bc1b38d38b9fb8e6d7bd2f7390ce7c54c4e543ab548f2e7ac4204b013467c88631aac7c9f04352254177c792169c34a51220914bf11824bf09f41e2c6fbdce380a2754c0c5db7f801d7209a2919b0f9253d0ed40cc71ca84d8e53491b793949396e8932a464e48a7350ec8378cd710b408037f82e41c0d3f1d01055528a9e446d60abb11c6df8f617e3d91fe09b847d9e812c4789bc885f67d6662ee5eff09095a8c2aa307411e00172c4c7a8051234963d76bc3b9fbf2ac3e7cf7b197a5a3e6d35292a87cc34553194b7d6343ed4ac8c7128c5bdde4ac987c07d1055530ab0ef3c47acbd1a598993bcd9076a5eb1ea46d996fd06235a003c8efa44090061b1a56284d5f9856522a824698d028592c40286035d93222b2315842eedf83c64dfaa419ea7d3f88839779fc5a6a66fa89f870a79a0552a9ccba0bd0af9b9254f78c3fd3cb5e70b424ed7c299583b9b28217a232aa078bf68d5b5371e6918fc3dfbcfd227f666518abc0e9384cfef8c95952ae79cdc8478fb23f09f77346945dc7c4ba0b69d3a6c301e6c637a33cd56e310c403702240717a5cb0fc01117eb4e8b19ea71a58f8023918501a375db8d55db5944fa9c82fbbf1f44c0aa411c81d86347b62e92d0737cf646e67b12d3396a3bc4b924ff1af4826b2bb559f9da9c4c1afc1a99387a92aca2077d38d49232d13d9f2f13c91805ee2d389c72c676877b4831d2b640813525b2d7e01b4a82e7bdb31b28ecb102fa6db0fcfae2dbe5c1136d16d7523d966695217248a34beca6803638d6aa387d15771aa0cbfef1adfe2973af6e63739ee1d238c0e5e3a43ba4818e626cf3828661d440cc9d8dee2b644469ab092158b90438cb1e626cc9ddbb504d83787e6d13c978a2103762f14c0afbc9519198d04591e65c2376e480c54f712ecee46314173dde3c7e758b222b0f097a8e220b3f62feeca5874dabd799e96089651e0c72775b6011745664f1572d4328697197ed04b2bc3472391d1bae0abb1ace14178f4ce4859def01704a3dd9e0603888a2ab08e2b2f728c970b793eb4bd7e370debd3f306008f287a360ae2182735529b464566cec6077f241b957fd8f99406a02a093ae9e9843f50795a3dcecc234096b3d27da1c1362c30dcdc109179323761e8acd63fe28fc563f7da4a41de6650dab33b68d275d2b4135a2800bf013e803113dc9b4917549301cb9219bc662d4a192b3243d8739bc758a68adfc09efa4f34f0a0187cf8da87d7e3ca5b440131a25cce2f6e4453bd070a890fa00d3a06a16eb31bac39370ad74a02ebb2346492317f3671c0d3e63ae529715c32ef78b4f5f844780f179a1ed7555d2c64e68cb917ff50d0b2dbaa5d19b6b7d8adc402597eb75600518710039038b5931931b763aab718c8f28e21ae1362cc9f8b820774cc12f8483194b04afb51fc8fa539cc2397f067c517567808559282e5f0f70741a58cc8b76ced0319d70ae500b15297f7d7b520f0985f4dc85d9fdaf5ad8e65b0744c507eaa2c01f327f36cd418c10947ca52217ca75a0f4603d84ab8ede2c2b8a590d9128a63cfcf0f055073de928c98a750ebac670d8279892b14ea1849e3202515bb04780eecc7c44fb462bae69e80d6dcf308c119b941f4b497af51773fb108a97b03c4d9df490b6188d00977400ae22ae506fa22b1248e04841f290f6d39b6805067ec2a10020b3824f8129285ee8f1f6630aa2369bd48845900bb4ed529977633bcb8830c9194217fb024c3228f89571543ae80567bb85d7f2f14d7f21d281398d0aaa8af297ebdf7cff385e9a2817301907cc4757b1947c98ae570cf099560c72792f01b04ee7064be2e185656f822f16cfcb620ed857c64fe18fe8adc6bdb7273ad2bbe0c7cf320c073afd5aefefc89359e30b0d78fb853d514a6dfd3fd6fa832ab6f5d79fca3d54a4d36b4d16ab46f44980d42a4512fc2d86131bdb0aed452c61c7b0cd2575b158a65f3a1c3be85b237b8aeda9d8ef0b73235ee68ba43e89c8aa7d5cb0f967123d7e62c7329a46c3349df91123cd6c8c0151c50a0436e104b52f95697a6e7c3054f2431c574f8e9fa0cdf5aab4f94ccaa532a345f6b04fc63e883569920a9f0c1b95b211d9b64bb1c15f82c4a1002c4c2bee69596153dcd452d897f58f31d04db8eec9db5ab8578be8a4b35cb57470a4eca89fad8df3203b11ea0f30e0aca9600d54cdad1b11620497b20b4d5d5668e17b6171418de5d32cf12bd1ab65c88bab223ccbc06ce570542f3d356e41f7cd9e9e78a5b71f660365b2b7d84870e89ec6fb6c9de18525e12f94254064450df0cf837ff9fc6556fe4014c09a075dba1a43edb0ea9f4fabf56ba1358b9ebc301d2ba792220dd55bd0bcbb8b9759f44694710bd3097ff106b6d11c4adccf4e613d05dbb8c10f6ab02b94d83e83bf3a0c0dfa9f28d25f3aa4d7422bc648f99c6ba5be9134958c77fb2f97acddffc66cc99765147eeaf9ea5d9cee3f460b9a4b02d75cf76234acb905c478c08232b3deff7c7bf25df6ed4b508e4657d9bf18ff86a61afe076f9b6ac0fffb7a5a4fae9328c0c91e0152aab47fa147690cf6d933d923df5a015457b176b2b2f2fbde115eb87856649846512ca0594187cc06c0abc5e331174c89e9daa6a07afefb5999cedcfeffd0c0714dd02f5964464326e5d54fafc5f44e7b45c3327ebd51ef84a27d5b5d46fbc4686f23303bf940df2a4479b4eb1b96b3f1453baf391310f7ce27df9c278065259b34ffff453c821281ad939f0875ce296d36d308f1821982ffdf2d1c0a9124d339cd0373cdab1e1ab39822150b29f9ef3be2d99d5a39673b1c9abce0cb064715d0b011ce90ad8aaf3652cbdc6bfca2744087b93b1762fae0730f6289d4bfe64698deae84007c67c15d60a89829b7316a43cd79a9a147a802d1a108a5df8ccc7a576276cc4200cb12b6fde43cc43da4620b957362912cf6fcaf9962701759d48c55beeb13e1941f11b22853d25675d0759cfecd74043bcc32da78c879bd133ebac523666a9d9adabf6e6f0a99af3637621c5e3a36642756f980c3b9ca7c3884e9c86eeae042031cab63c880695c95d6ff72d6f00c8c629da27f7f20508837c04c51b1ecf708b2dcbf536f4d7c05b031072fabde9cc47d11d49c2277a9bebec0967efa72c8e9a49e7aee74c11693ba81491665118ddd4958084c89df49cd1c92d8c2bc0a933df5f2592e6a3a3c5097963b3d2b1d4524153cef46e0dd1142b45fa70798d1cabc660dae377f5be74130476b73b224e4a460742e985f14f5b52294d0ff2fe3a495075f74d904bee48562e65b7981c384f1247dda918ef777e3be6b45e5ab61f6e1b249b0659fd8bcb0c813c0d40e9a86d9820817185ea09bb55832f1db9af5531031f7f2492333e1a823b19e7762c701a17170a1e0217fa7360ee1a511fbaf8dee88ae53eb49d9c8574807ffaf3f1c95704042286df338f089706841da6fd50f4e8e6f2e3c42cca26b98fa3c5d273aff3871600c21a36e4497d92b23a04850ae55d7abf5a4f42664d36c7cfa6b9eff4786a1fe44eb4e25fb8cb552c920fb6483b491b80d987956703880c80463932038487c7e5e0c216f7ec44f7c30b45353f4b4d68be5ecb9274229269b85f66e96f7740c4f6c529ba129de3d662e7aab5ba2e4803115726527b13db78e1b9679aad883b4f848c9a1db77a23860ba33b01189726fd793ebda07afb428882e1f06c788a4f9db59c815ae1c7ffe4c28f9a2441919465f5501ba17d88c34162cffcbc8d578ce3dcd902074e672d41ee0cff969826e2ae0cc4b91b8aa852a8c4afd9a3a2822f99c43c6d6f75d2dfb160190ccd0ea6de18373da16e109ae7147c6d6c6428619c263f3e99125861bfb76cc33cd31d0de7bf5795e8bb8a71313935b094348617ec4ad5657bb78b3756e42081bb8657f22c30e4f5f51495b2c89923d1acd044a3ef97b8aeed1e92d890f637205516584ecae84234c2be2860a159011d175ae6adbd10bd30f2110d10d49b4e6dbb9b3129b8b79930c8033b8369994e667900393295f4411b5142ceda6a7e761f701baf47f6a3080b4416bfbf0226a884db4748d82a4a6175dc082731ac8d76b9dd5ba054a9c150254b460440f99e515bff489a2cf9e0a3a7779c6bb0ec882d3ccbc75ad492a855e4e58f3cdbcd4bd219aeeb1ead49f46301013a0563e4a35b6f8ac4e5d1600baea1db625ac2f928f7d13dddd4f6dca4134dc924a2306030932c62087d0e5c6f1505e4fd2bd9bb59bb01732814030aad9a1c494e6298e1b1c2402af2a5cb99af4a98869b4a23b025022b489d1a85a8834814e749229d830a61a4840b09a461b8054ee000c89522d25f5e4d562b0d4c852d9ed77b316ad853803c4e63248e0778f4a03a13d3e45c5e51e3565ae1c904a357e5b8f4044914bb3753f637c2a0845dced065b470a899a6d5138333b285fb1379b5859cc26a73700b5613ff7cffb12c04e206df8f7fa36e06b7e54643332dbad7724fb56172c00068693ce26b857195d2328a3c15d0f6942fdb5ae7b4900f6fbe457aabc0d574678e3be16f238bd691070952f6a301ba7b416765f52a7b7ade7a5de8593e4a071204a60fb8b8dccac986fbbe68cc874188964330f7f13e707e0f05781cf50423e3f6728d0e1b03be8d56443a3fb5f89aeebeb9dc1b271904224af8d39fc2f6ba542b89de9c8fefd97f0ddc0ac27be498deb68af940e6ee13897eeb469b577457f72bfa9d026d94fb2d4aabf576d883bb85535c97e593e7306178b37eb676ce82f45fa78417534025a7fe84f5bcadffa48b9a6c75c5b8c5df2fd8b3960678e35c8474a65f647f34afe9616e112025c92034951506ecfbfc3db88fcf381ae4a076195a17d54a5554d3ba56a118d009e5a021ee959b3a9ebd4b239a74c590c15afec000fa7f25f5e091754a2f6a925828f409cb51c5a0e1b5e9808d559252380ac7b95a0c3f3e004ffab5f5740bf5c3ea7a6e2f79514a225cc170d95f6154b37247fdd63fbe3c38fb116fb81029c798362fb21a3714b75b34c4c2037f45a635ff831c512b70a98ed2e4277c61945c9364a12cff01b4626ba4e0bc2af77e571b05726065414a8b6876d84cf191ee1c35c5d571f9949818d583a5781874103cec56e64c268a670b55841a873a24bd8ef2987e7527fbeb8aa35cd5ebf1399caada5ef15928a06c16ec7703abf85fa45c42554d36fd98533fa80e13582d8a1bc793d8a2be34c347138f682842ee069e26ca9489be0e3ef19d29fdfa598c5416eabbdd5e0571963069b001acccfc1d2728367c4235928663814318a10f70c2167ba43744d553b0b7bf073fe29aced95b3bf2c4222e5ee2e4dc14272c05e7b59a59ac9b73a90a8e839793ed3b13d88086d09b68eeacc35fe45770e1dd0858dad3200458b2ae084b839d5e66eab58c32d333b8b0453a08462c33a372ed965221dc9478d8d5c4cfc865282889dbd86480ada62108924e95d6a281ae30cf5831d7ce560029f22b6c3ba0a51b1e872fb2662ab25798409bf5b48f04c98037cb87246171224af2e8a37160b540c07e8d5c926ff113a60eb673f0ca9ab6b7219ff281a9534fe1f9e8fc1380353ff45ad7619a8bf4745760c3364152bdda821fa97297dd82806052d1d504fc9574692aa25b2e4e160b48e65ccc9573a4478ba742d8ddb8007435dd581492286fb85a13eed4a08d96aa0d40b82837088f8c456eaf025b910d2733b14abd5a0575c5813ae56b1e000c4a0c87f3ab681792c788621d08586bb49436395d046b066e86f9ed2e113b82cf30bc44561e01b44ba89ff779c802b201ef778d749ab42aec97a1c74af81df7f2654e32eea6694dc2ac39f0bbd1087531e9a75851a7afb162d7bdbd80343cc2814d10c45b04051bb1692aec32d8841147066eab0dadbb848f86e9dae9adf2794e9fffa0c0051143b55cc373dc5bd63fab5a59ce8264891a79ccb9091a0945fc83f59832ee345107329ac43a92bd5a5fa216f57be5e936a28bd006375785ddc75d313f23c3ac4e9980746b6b8d8eab9850f1ca46306dd5aba1a8ea79de6f334b21b6e4d95332958a318e3daab2425cb0bf7e00caf9c8b39fd77da59d9cdf1099127e067bcd87dbdab4cb0774f3f1ce992ad79451cc4f36f94e21b4e00d4541db802d47f2242c6b929c5df512b3fe77fdc455014ed90a0d2ddead6fb8f2922f9db39c12e43749031bfa9561fee61b3723b997a23de9800711fc7168068954d641cfb319e7c2c95662693bebc8cfa67fad26ff58e43fa975fbf2f0d110a9975877e139c3fea875eaf37bfabe9434740444888ed0efb5155818fa3a9a2632c6250f106db1d7a90cd0dadfa9b8f1d2234c1fd94db97bf78b8c93c3198c078d9d92729b09559b1c124769f2909ae4751b031b9865d7195124edd607ce14c620b54c3718f64d18f90f28ec363eeb3e94612b5857fdea838fef91188adc54fea9bd6bd16075b62662670a490a88f3eed222a09775bd9ce501d2ba41fada5db657ec4b85410335286b120051a2d05bf9c1e18243eead860c413257997840da5bdf8deddd3905ae6e9ff3230d0ce0f184483bfb3866b02b389039e07c83856eeaf097efe96f1055cc53434c9e8d78fc37bb6d3536fa4b03032e9696af41def658c05786c18282abab17058421c0e9f800adf4fba34a467afa55e1df70cff12132043a31115d75acd0b94a53afa4871dca75efd738630c4974a3724785c91e17aa0e14837f732dfb849cb0d6141c131c252a6e31eabea3db1ebc5a54a65a2798df059c4c54d3abceaf5c9a0bb116e1fdd40c2d57600a2f4d7ef1b7477ccbe4d86ecf3a5253e151b8f709a303e303467badd16ac4a18854c514673746d15e44590f4682f4d8b3df92062f84e909529b4481f4e1e637c8a7e06b05288feaf715c3a0d189ef92efb4fa6484a6ca48f7c818c4de2e0c6a2e072255e916c3c483127201d3b1619d4ca8c05911949f54337ad357f5d665a0def6ac73228606ee03c22a4c215567df632e4c1adf1f34f68c917158fb514ee57c52f76cd1d3e90528da3eedc630c380058bb1fa5fac644597ff5000a204b97ce2890be2fba3702be6d43449ddc21cce6d19a0408e9f38090e9e6c6c9503d0c97b549712d2f35638991de5f7938b7c9a1999b6f824a0eadb686f4bb7035ed124f389967d3764d6982098f0d28c87a702f14354d03aa69db9a3c048fb8eead5b3ffbcd21620e610dad556c1e86be88298105df471c938e9edc00444cde3b3b8720779d93f95408903be91023b3ef83c6592277a17059996206eb2695b675ae741830eaa1f78ecdfb9d9661f8039b2ae823178329b1b9412ea6ba4f7b5bb0075e28590575b1b5a31ca5a96e5b4fb360c113f679b3bb7df8d46465233b4c98e20672695b141fdd3f0acf6af4a71fe5c790137bf64c9ef3251fca61b2319cdce3c346846af39ad73f1d267945fc4c695aba5b8d91c5061d37ffcefb75115990176447687c19be2b550cb444653c610f4ad0cfeeb774d2bfe7a981a21b176784200546cd919d6e88c89effe6f3bcb65b2e461898201dedc881af29cb1e24877dd174885339b7132d6756e2d9c9b987eecad04c79e98dd8af42063b682e00bf42be8707c6654e914fd4757951da9a2bfa0fe73dd4f9eaae128acb6318e522c1c20eb413dfba0bcb46d0743ec4df030f51f9c4ae0fd3cc13c20eb35fc547897a686f594ea684a51759e1e5d0c4bf7463c28c6c75e295c339c10a3f55aab41540c77086822a728dd1254179f95d4ea72c458da71c5ffd99b74ad94c60ea1ebc5f70e6978566e17bb1e9316a75ffbf097b678a4a9e1b8ade66b44b7010138bb5920bd604b6568cfadd6ec8602f92e6b65ef72b88606688bd06dfd4be002a8a132af0b98504bb33e68429bacd260675f48fe1497b218941adbc285f39bead824d15f3db35653b9103194843e939ad60ef12ec18627c49d31f571fba984a05ba50fe8c25013b0ec702391bf3f0831ee13d21333ca82bbc42925aa32c9e366addc4bf07c6e40510e293bd8d3f79e43e68cab852accdae9c1f49ebd825186ea267cad88997d8002f4da2730b44dda7d9d76c652f1c6b6e20aea846b4bb09d04bbda2b3fa452c6071fd1b38f41346436175618d124ba5d50807241077bf5f06f1c16b7f9dfb4f2e103a86e1ca1d81b86c1cd503f25f5339ffb53492b0c6f3e3c29aba46f4a378d786e90ee2ee7c1cacbc1eae718f460aade1b1b1f6ab0b1e981ab0eb4cd0cc14a17a19ddf6600e8efc69a398178286c6aabf2040112a6226632a5947555e71db21059b83837e7f5b97da2a77f3bdf70470be4ce4ffab0482d8c394aee88b590f97a47d3cab03ef4087b579463e380347ed972466ed7d2118934cef58a431434754e128ca286c70ddecb7374507ac64cf7ee876fbdaeaedbb2ce4cf801e56457bff28b258c009dcdda0b87d0ed68317e02cb4fe945bf69b6dee6e76aa1104b24a097656efcbea7ebbbd42ad8e5df60acae4734a9281ceb6941844e2b7345a382cbc603c1a62cec99608e7b02c6b6e140214c14ab06b2097e99b8b5eadcbc93749c359b99a33b52f6e01fa5f5e76d1409f7135c070d10c4596bded43c2c0615f720346730adc01a903e5f7ddf4b3c0a7d26427a7b9256a995600709ce55f84dc45224f53062ef3a0a5b285890d237116fecc5782982040829b5e70927c26d192772af26507aadba66a1c263e2c2fbe7a099562cfb9b07cae0595b92d7d3d0eaaf2fe9924ca39d68ca762f991d0a5f717960c1d9f924e891751becd6244dc792a7be9e4558bf061001d981c40f82e246074ba7feb44b4ae33dd4e17b59175317d5c001a2bec8952eacfe735b318d07a79a47fe523d17c33b046e4b16fd0d2122c8619549d1a064183d6cbf983041f8bccf298171392aaaf99a2c27c07ace33002f8ff2b7b650bca8b025b214b972f638dc8a3926cef14d6ee698db033aca18c80903a33301dd9c134844971a0a41aa6442984161d8564c408286db8acdc1b441408bacc5c22960a11e687534f6537a4d25851d02542092145d2ac10bf597f2b774b67e246cf35ab1dfcba6f0d67f0d69cab68dc2eb5f050c6a097f1d8a0c9c98b02cd2d6c1481dd985be99d4623de43cf592b4d83c447dfcac0c62f610a6e72360554de5c8e7f6f26c840a4071a229d4222e1c34238797b04738065ae7a06b5c607eeaca55c571ca94d2edb4b6129b8b536f3224d04d0a8c94158bc03d173f62cce4f222cf2d25a49a6291e18f24e9a395c28e0d0e1e1dde9c82a886c6ccb57dc65b6b87a250cca9e96a52849c260d42f16dd1298cf1ae93cfd32120a30909a78ad52d40e8c922b312f6a7444a50ea2c2b25a9abc6a9ac409aa5bd251f919c5ac23dc1e9ee10779f59fa35146fe3635d3cacb816201f9177585c58664f7a0d86270b3d697a272024a5ca2b0047fe0335f3267a7a82baf01910c97a3bb147d6c86a97a5c8c4325bcfd2ca5187cacd9d79daf20b925367d1c47708207ba4a9362e8a0ca1a72d310484feb88eae896de41d347e34160496fb621c0204bd2160de853d47650391e15ff796f48391e2dcfe4693c4f516eb30c2ffe6d10cc54390caf452e54c1579dbd62eb8e0201d8ed43abc2146fa38c1d00d4a1aef500985b9418a005f114c136225ca0b2fc0af7d6c3da0ba23efcbd19a296e7d18a20faacedc8e6888754949febcae025d0f88e52deaa24241d42650d5749c136273b50cba80f59172022921be09551e095242c56898a20175b3e9a1b4f29d42aa070b20f676e797758694f5e53625d1dd2c00ff6c0b3943939a40c62b95016777e13cb1b4a3646e9dd80460550906081550340de5da36673467660ddf92da0a3d8ac5c20ccc436b0f631a0110b391a29170bd099a9d62dd0efb02cc68e0aa2dd2d393cf94774c1d135d7c139adaf3bb55a7dd4f1a4b408294f01ec5d3d6186d0158a178302526b2ffa40b609f5cd8d722b1596c7fce958231a56bfe98dfb89c0028bb78009b2cf0dc1e0dfffe6eaf765a73a6fad71b58e74e130956d755975457d010ae9e07643494bc978efdf38011592c93d3a151f17fbfeb1b3c73218a4fcb7708fcda77d6508fee0d0fd27c29ddbc186f2da9a06e2c25ea7b4698f19d4d2d6d74fbf8ad610b4300296e150792f5dd312e5c8cc41cd0cc72911d6f753ab84df7ba30229395509f72a6da0a07312667a961422c05730f6b320b96cd7dad5ab6aebe5eacc8bf07a6cc510ac1cd426db52e548c2389eb6604ecb4c2f459580e109d462be06f3b2a3d0a30c1d4147ca938427175b0e2ee23042ede0c02526ba1eae9643437b79a8acc4d4dc39c33e3432758c03657a046805ecc89c56a441bf566d08f66382f6f6e5422d54af56577195e932faea9d2683d4d59d85b3264a40594559f524ae304137cf16a89181186224af5215388cfd20d5cc4be970255371a31770a95c4931e4a804caf56aa867bdbfe18bee9eaecbedf7c04d07923564582a2c9ce7fada7d39563a073547f850dc2c4aebdadf2ec5f1d8e1ca653407c27aaff7807743d5dfb01c2cff23f101e66a8933a867d2200932d8d0f5461cea1624771b42144f5074522476eca02fe15e1a3a245846f8a1c2f15030eca53d6e1f8a834c69234640941d0751b0a20666d5a353d5698eb59a16ac88b2ef173386dc88524e8c19678644e9a08c19008092f89f318f39ea0d5435664c67a234187ef81dcbf930c84c1c73e40845617d8e1978386de3f914d8c2a7eab5c75f8b43dfc44d613574fbee170e59c44b2cf2d3086717855d1ae3d272098110d027663532cbfffb6d218f6d94012aaa49a2b790188b14416970c40715e681adf0b7eb771800f2b26d035c7c5f38ef23a99afeb1c852bda22b7bb6a3d12dc6175c26954b323a5d68200eb3ac64302bd36083b46b3206137fb19083745ddead774fd22e11e474f6f0bba0b04cef881683d4369e6729b70d1ce05eb52357b0201e010f6cc5ea5a15e621243b154636098a81d4630b6498ab54938c87e3602f4d109ef293d2473c0a2cb4b366d2deebfb7f9981517446bdaebc5a6c70d910c6b13a63592c26a068575d1fc2a98b4e9ed8ca34026cd937de8ec99efe4b3fb552dce3e6ba52607b126e66e18f51ecb2fe02850aa36b31dd9a1395093ed2f43065e99a697264ca7a2387766b51381720bcc1d6cb54c1cb84b0bd93f8fd6058f8398eac419f52ca4e3371d845f25c3c208e0ac619162d23ee8c519f8caa3c42c0aac332d3f2d5da87a0b18179511a6511a74a601fbfc9d1f9f695bbd828a28d94f00e2330035acb94d26f64fc4eb825070dfb8c1216d8a5f3b6350ae0796fb430e80cdab600840a640f94ac3bb46da733dfc0ec4879ed7049f99db8acce9bfdb03dc5f0a1c7ea6eb59c2544debf9ecbf78eb8ab9464d353fd991bc304fccda028cafa5e937f0a0e8df4d1ceac493b83f8a484a9d283ee13f6a65df0f02b70686ad03f464963d0b56d9119c3beea720c326b2ad50b86e2c828ca55492366df369ca826c47c2b5650f19f421d2f0f2b4d91e8cb7d4506deb12d61223e5906a449ccb5d4e3266fdb55f022cd99fc3d987f50e78cce58b51729f5130b05944da63dec208e219106bd63cfcb9da82428baae272b3ee29ab740e2a9ca2f0c16799453baf2b62d824c8a4ec961d8086f3962ae40799e90b76d0b152baf1996e8da4284a13edecb6ecf607780eca07b5b0262c71e3a402a4955283cf2c4cdb7de106fdca2b248a09cd2ac8fc696a0fc549f7b28403079dbb281cff88db9d6348f2db5757a4d9c3dd2f3410d49ee748961d06d5b61900b713b518da3cb44a54511e8ccf6215b9adff34b82acaa92ab70234d96387ab8b70095b4f21456dadc64bb0c00085a2b24e46ce17ab1de3c9a0e50501a52658c737f08c58d94fb861ca42f23abe2dfd59ba36200978cd135ced5e051b169a78b7f92bebc1c49d6b2e030b169e15b501f263aa7f5d5ee5a2369e95b00e6c8fdb218009c6a71ae6ce65c2505e1985fc8d243e2fd7b18539e230902fd78289889918c70d3e3d1430995bcd0abb5620d715758885e3f2950a9f82d1c0c518bdb3691a8d95d5b192b376ef908a59ae4e6afcd52cd84711791b77ab55e96e521a66b7f6f688a3be0daedb4cf204acd8ea5c8077707561fb6021856b76306fd85a7e1d31229d9e3c5fd9ae5f0c3e5b3af32f5069db8a3af4108e52d1792a56dda65c6cfc539ea87441fa4f0a0920cc97e6524a0cccb81daa5b14fb34ce5668c1ac84b2556078be8e9d55f431f60d9b96974d72647afeac0db65ca97b6e0fb794797654f3560e135fbff54072267b21c57f27d311b4e790e888d0e7b56ad294bdc21501e08e2880359f05f620554813f89855407deeb01228bc44c279e5d6dd179597c96470f7df6c30932d3689132a0791dbc49f3eac7c4d65bcc80a1b0f3e19f594a4ffd3e2bf1600c6539223e33856329b4970335e2e4747086716ba46376762e984dfb4c9d0677f6ca1527a7426c818969010b81b575ed4adceb8a9c64d03e8e8ea56d67aa66ee0681da3b37e7398f551f00c122e610d9b7041a427b7ea3385b71c0f2c3b44242bd46e6ad84fc656b823d9ff849c8e9998287bb6d5858fed93ef50acabede20a7c77ef2e5f5bec70f79c9707aa45d81740135b815ad2703b043291b3f40e3bdd3b664216e230fcb30948abafa1a46164c649665bfddbdb29bf817b87b572a797b4249e22a29d1ed528d6c935c334baf0406535287c9aac9bd3e330b6369e10b49c3980ae9e33fd7016bf87205c60f99ba1d2528db8005fecbe733bfffd4a54f5343050345c784abab321abe5791e610cefdc5f512ee907fe1744523095202eb132ec6ed5976f839672ae8d4e8c44148a904409104d91812205fa5efb0213d37da532a9839653b98b8e9a8b9528ca64d524c8f04dfe645045e3fb79d2d7e532dc8da9f13914a98af93c55e89bc757aa421eccd02fb42c2bba308aec3645508871b9814c2b5a34ff6f8473e3783168a146f6b2037e3f8bbb438cca98e9992955dd35e74bf89c4c4ac1f7fa5980f5bda640f51ee227084451a708dbf3b35f8b73626fad0da56589149b568a0b9fae15e0e672a08bb0341af252d0794bf14f19c77fe86785ed996161dcbdf088c499644386259c4a9d97a7fb2d8a00f947f7f2d937e69c8c9f971a9189d6922e5289dc2f453d6163bedeec3816dc32a1db3a9428bbd99f90140a2dfa715a9a57a6f0f70d365185c601fa15fc21f1494556891e9c0f4d11bb6ef296868262232d16dd2c7150f3ac0ecf30beca6baaae8340d40ceb11beaa49fd36e3021a45cadde4fd565042efe2e48fc8a3c0539f0cd17f024532c86fedd4d5ad8a909deaf3b2c2674a73e73a3540bbc0a2135493917596d701212dbd43664303e8125c15837ec5883f6367c7dcdb7ca8dfb59d58b15b020cb7644abdbf6132f55e882fc1ef47ad02f05ccae2a747d915737cea6a2dbaadef8a2b1d14bb2e6b10b10dc9f1daeb19e8bae0f20fd205521f2c82148440e350207216dde6330bc94b0a9f32ea5f29dfb6c393dd170c86497f1f8321667f8f34bc1b7839b581bc82a140e5cdfff350edc1af77f4b57bb085f1adaba3c6f0a4db2397c8ad5f4a3c15b841480a046b8f0a8669b8372ef389b1297e71e6c92374f0d55178d6f0686ba889ce2f454b309af46ef8d0b032f529e9bdc9053299612727b06dfd4d13f1e345fb1b5fb11d500de0295b2d0d8eb56a4ed70c529286314aed2d27882023d66abad1c4e0657092f54eb2c2fc10daa913669257808d71de3102a0e620c03505b76842988ccfc67e9a84e4137d34c23209751d17756a05a17fd4c4015c8b61a80388b01e4d70a9bbb40149eda9b56c7d0419e830fa394e5333731081a72fa0071183b0a1921e0c9451b70e52220ce1578b2cbe379d4008956423d1edb2f88bb284fdbd010dc959c6c5bf525ba2ca95462a9fe958f8d662627075eb80316bd50c30b8d4b974fb9235261572935c472a9f2c3e1ca9451b8d4c7343e380aad3cc05a09c27728462f0e50056028700683461d50802feb87289dcf49e5719e0f446e3465a011116aad2b6e4bb044cc56cb5841adae2b180044f76525d449cf6bc5442bd6d9871252510b088c790bc9692390a24588c03db06979556e0f72048225d4294956b025477899bc1a953bec653f5edca47cba4542109a8348303431dbe5f2330eede4e3ad2710bae318e9d1d3234238645c4243c91c497b38326c9cd0085f9e7fb16550798771ae2162040c5b131136e8033e48e8145a57c4313f669e9819266a5423f783805461ec6739b2629837a0d25ec54f75c7d4032968cb00af6bcf0274c18ab5c5bbdd761ebfcbcedb197dbe7641cde6194ae58428161b88c305999045f98eb48d3ef4585b5a505b58d0cab5f50bc2ffd29fe877da74b4f636f2196d0120cf640877dd5fa3d8bb95002c90556970145c4a18910b4e08558f9854c20c570fb9a46a7a4fb3ae835919999448926292986a66c3b3b5201a4540758d758efa37b62a3f4915c0c23f198f40c1b914c3525f3ad3298d1e1d272338ff16569bcf0bb8b6b3ddd9adc19b577809920f17049fe1649d44982cd6ff5e738b16a21327415c3b5637aeb4e957dab7712a8dc2b4146362e6581756ba53d4e537bf2f724d8864b1c8029aa8c2dab43639d2262e084a50c98f08f15042ab76599fba8b89d99df9a1a999d5f5966c5ba64f57ecc2771eb5206771a86d78f274434be63624f4b2b92303dc5e47a4bc9c25407d0b85ec203dc0ba815d85a564794594b1cb467c0c11b0dc4b04c5cdd61fe60638edbe4d70ff45d7a0aeeb2b45c528a2e7b306bdfe78ab5140922f2b1ccd344c8d994a22a4c623dbead08c0d8b25deff1385e1cbdf862c8f753103ed32d0a400a0356b4a13ddc3a0e648059bd7d0dc02b5da5bd2540ae1e3636492aacbde2263de4dd778bcfa6ba9a1c4fcae910c17f54be33b41d52e5cbffbc80df01c63bbf05c5eece503c859ce0e01418261245d8f6cbc742635f5e0d2400a6a58361dbaaeb817f59f187b90797efdad29995fde39d9196e67d080a21d5412e889a8f47559c128dacece4f003702f27cd8c984b1efde3d0ed3adbe5eb66c17cdcce77676591aa9fa0bdb54830b13756d3becf749ad20293c0d243853857523c8a96f03a7d3a631eb5acb4544e978558eb55cf10a3aa0a9415562e5145bed1041ad7cc00c183ecfe0c5ce396d3e96b877a4f502bf3b41469b5f9658f6ae47d139afe384af8fd42e6b4ed79815e13d2ec4c6dff02f79efd835ced007b08213961aa974996d19f911f10fa765aa5c56f52022769e6a31e35f62a75e2b569bc93f52a3590262ec06e8d4795840dc638bf8e7c974b26f2050b3e48da089fd0b0fce41730646c9e5a452c37de82e6961ae1a181d15b1796472c80d9e778b8847621f55bc241da6c18282aaa2135045683c8d67bab4153034902765eff75ac3bed9e2fe9a62e2d1b00086cd7f2ddf29974d667087251c89c86425979672ca16b74b74f8c5c95485a4d526c926e2c1dda6245e4ddc461070a9d5fd1ad666e4d882698fe7d37d641f9b760091ad6f4f24bc9463bdd187e860877c73061fded036bd49f8d95620229e7a9fa119919ae1004935cb0dc5349ba12ec61351878ecf08efba3b840017fea813bce1b644fdf061f27a351c544775d5611df213e5bdd36e3aabc0286da9507a8cb576f21e684ed7ba860e7702559521bfa9612cb9bc5915d63dc0bc4fa81b12d4d08a3af0013866ecdda2e0c4a867a5d3f70c2e369983ce5209bb12a9b51b1cd5ae43c18eb24855db5b5f00b262d0fbc7c2fbea378c5d205b785a95e8e20c0a88d367e6988f270de1ed5de10ea1d0c73b1edf2dbbc8e3499caa55495deb4835e84b11c4686f13c04c75010a6aef38029bf58efe4b870a6602f9c2d383f149c8693f797ed4692d37b59a41000075c70879775e0f7da90ba6e2995d21923e5f5ed7b547e464a836c81102fc6fe7afda0baae5a67543faa53e9b23db43c6792601e884191666e8c01f330c0e7a44a0965281f46c3589a8cab922a1c801f9193af851cc410c926d6f9bca4c6ae3217023a94627aecc6a2592a1b60d6fe9d91b3dd119911d9597dd5ec85dcd5da63ee8fc349d275b6b49ea319cd31ede314cb5cd6b5129dbd8553aa3cf04a0af03720aea56a95b51a6330c1f40e6fa4c1f92dc00bf14608fa28c3ad030e919f01144731b70fadf8520bcbf1e857795dd789953793e3c6bebbb0926fe0d3efb223838bead63f1417cab514b56e3a1f49fae21b81576c1dfab3ef0fb5f8234aba6197495da03a4f9e2d35edcf7319884d95658800414d7425ced812bed5e686a5243a2acc0a62afcca20806609e927d6cd11d6cc52aabebe343d2337403fff5bedd95934443d1dedd79e0db008d90135b0744e6d740eeeeb990b28f6d7de3cabfbbc655fe5412cb76f35bca29c5efa3eb0af269fcc3fb9ebcc79d5d5d4a787d577a33128d4d9d0afcc810cfbb401ed2affb718195dc97257d673e4db10614de3e9aa6d8f0e17d3d93fc7dafc369e78f046301c4976fd07b1b036bc67bbae7f72a4dce6567be049ab648baa2986da477e1718ff9a96df29dc231e6f857e40638eb65c7e72f811633cddc7d9ab5b1fedf9c2453dd637c18ead88f704bacf7bb123c2da040ca4258c40581530e462c22a11edbdb6857e41d431e7f2336ee26a7177442a19c6108d382db730950dff38bfddce27dc0ea4b9ff944de31b66a33a3ae2af4e648f373bbb6a78f66441b56a19e98a7800e2b2f1b2f16a69113dd33e136f0b61dbae88bf2d8841f715cfd0b73618a6d6e05a0e1ab9898d16c2aea5110b23a5b6034c6bf7ca049eb0b06695962beea3f192c55ead9b1cb569db89ae208498cc6644d73f51c214e4ca3faee6428fbbb7d6c0a13873d109c1049cde6324dc4e376f89ff7e56f433e97cf9e8ab9cd71e318b4b5c34706348a40e5ba800401c88116e88f71c9b26e75d63a44d63eaa159d20dab3c398dcf23db48f7964f287f1a8783c877bea0ef4cd0313ffcfb03579b7bc1dafd7a3a17e5a55df6b90940ca1443ee1ea35eb4e4accfc07eb98a4d9fc02523df742f8ad3fe8caf5a4bec2505ae183a23488181736a83db66de23152ce74e637b71f22624678764b761afe1ec76744f43723ddacca9924bea5f9eab3e53dea7f62415f240bd59117b2c5e2e2d5f53d6c9465f408f5d986a2b435dfa389eaa72c09df3d25990c8953a437f28a6011391296634158fc5a33a10571f1faccf8e6c349c236568261a729f7756868dfa6ecff210a0688df618f93146ad9bb8087d2a95c5a08d3471b4639777f544db145f0fbd34a505b151553ba434b65a11c428426db704a3413f180bc801c5ffd587ab57f37a7f25122bf3f1b08b3f66f65c053544834b25c623b88443a616f708d98d8b72015e6317fd2b5321192f8269d47d9279e51fe21d222fac114fb0fe70fe15947a5eb0582770c038ec2ec345a2f2a463d3bad027438ed7fea691990b6dee5ffec11ae15b36c2382dba55d4a55c78e2b719738e9d39d5450b5f53034edb96ec374a9fa810cd0b68c0366f17d81fc9066aefeb7b04708e32b4b48199956285085f70e0fcf5c3395b5182561a7736e5a4d17cda2cc240d27c318a3dfc414acc5e21cbe9627003d2c7a4405521366f11cca3a1185fa508d2bd71e860296926aa41a68af50ddf2b6aca89225c03462b985ad472981c52ac96de38b51e0cb89ee2ff1d064dfe070bcaaf62581290c15b1c9f05363a05e74171892cfc0b19fed8e8b0ad382313ece7ddf467f9e2a4fd44bf9c007647612ef610ccbde8aca832504226ac6cf794421d79fd93160187352583377d946849d2a8809827b4669616b4870140afdbda3aac212569b5c7e526a7e57e7d358df0896f8ed89d3ef53d6cdbd64abf054c8026992556eab4460c71b182e539c2438b51c09b73bc68966a97dc1d68cbf1cd6480d51b9b06f256cdaa2f88377fbaf8e9cc42ce37c6515670426d5cdbabeca7dfa188382d777094bbcb4c911f715e0ba37477c424959270587e48875833603d1f62bc98cd57cc85384766341fc6513bf39ed940280ff9a66b9163574a6c87e3b95a4852e663bb65bdd1ca16036c5bec121c6ba1c009730f4a06bbf0e97a1203fc1300860252e41a40604039f65739220d80c1c92e2a598e46cc8abf219ed172eeb4a8286ef4a34b2998bba67047b317566892f3e800e97823a361a69f48999351dfc7e59794e4b0fa9068a33945ed97503edfb4a1e961d451fa551338c7c71eba9ff530067646b8fa4f0b4cd0a98b1fde7d6730e06e3e84317c34205ce3f6d50cf26992e2da42524168dd98088122b9130074009d8c70ce5ea053d69b53c3b5fbd0f08655dab06df4bcd422b7f0877eefd534b15ca577d0b11312cef6d64675509bfe43ce72ca37fde0ae2577d3b2c9a97172fcbaaf0b6b81741004a63e2e8b05fea33396f6fc91f11546d02df1e9b85f6ad48480c7f9c19b0802ece9d2cfccdf8157ccc0ca6be995f901ebed92185e657f17d25dbca4f1aec2f12181ed121977d561305e129a1a0b89bcc4543d893adb874511a4ce454eebfa0ee542fb59956aa60859a6951e274d4bf9dfa61ae1d6c2d13ce38ce8c7dbc9207c58b8323187e1e51348b4837c1e0599f75287cb3f417cd0e81547685b0a53ae3a4a133e7c152f9ee303e3f8a2675d867a646767d2cd4121590718f8f0364f8435a4ae924aed396dd2447b1ab356e2856531dc4daf232d3ada15c9f18f3d9dbf8df8187157f0fee5f004979f056033c34f54ff3572a8c592cef6bb2e74b7cfd19a9525b81932cc63e57906941e3ed876c71e1922e700a306a6612946c72d1a961400896cc99e88d409926bc4b14f88b986799d84611fc368068fcf96fa4822116bf3fcafc8024382ba6c4f3f23671fa4854cb24c5c6b0beeaca3592306104b8f33ea60b0c24d681b2cacd0cb40e75ce2c8a3d27e81ae79dfa7ceeb0e018319a5e9ecc75b193e0d8b9f3ffc895650cf7f476bd0d1ca369569fd4e5a8ff5c034308de8489b0717518b53dfe91083ddb1fc90c99c5ca9f39db0e09d8f785d790a99e7d712314bed60e8ce7a2a6a6472741b61b2383f7abee214eb0be6656e75e6053355791a7410cdf9b4a39de2c5e54a7a6e4d2baa740f0144d4e4b184e1b31237ecaf819d2a33e79fe5f4c804a2037f51345e733c27bdd768540bf2a75df52c0569c2adee029c370acbb98594b5b064cd06e550a5bef6409603ad272c7abd29bb7463b86fe76becfe05ea39a66db18be14b91fdffdd0a70a52ab09b15e3e3116b22f9329ef38c085e0a2fbdb8b3dd183bc78cb1c74e293350c1944373ae1492ba1ce378b581cb330c9aa458801cf551373d5ef9c76d7312b8e71e18bdc188fb3ae91743dab2241e5b00986bea03adaff001303a529550ee18ba72db63c9038c2db5fe01c09e76d5d4ad6567629af3782d2e0cec6632c6446877f80ca1345c3c28a949f54d52263451f36082506482c160c9dad92227509ec984490f3225f7993ac0af08c480b8197788e0a16c6db2f817e8044b62ba1c3a63beb1fa2f8e3379acbc5c9760582664b9dc6e849872f2f53076f461e8188071612c6ecc3078d382ef52ca2017b1efc6b00361b12ea9345449c7c4e857e7e30223d0833dcd75d4a5d02311cb12bc62b7bafb1963b0cfd2b5ad40f3250ae0ab435b008a6f1681cf89dbb2b4a333b57cc9a6c73cf42c847272218340e996aab089252c14b1745a49f3bd12b2008b5ac22bfe6a88803981658395e0f3e3c3b3b5ed2ac132d97f2cc0faedcab670d339f2683c46d8580d8d6248d11eeb61f63d0669b12b8e65e6b40f586a01c702df925e03247f31260a55c4b19c08ca181a259ac3679b1eafa44bccfc41504aec279617dd7bb478c6b1444cd7f2b2e902c557150b146c4811ccf6bbcbb18bfabd008df6b218c3aa123451a649d764e38cbb9eeec60972748652395a3d89bd9b3dbf77eeeae3cd3f2e750be0b6bf856c269c60b4462083d1a231c9f1e976a9db64a262974abcb04b0947ec4262017b008204288fff542c99f50717ae0d29607139ba6a96116bfc952671f4ccbb8cbb2a53659cabad65ec849f3da0f733a0402002c4bfd17db1e48f6bcbf5b86c28b43d57c5c216a106b5475878a17218c4a7e020600586350cc81b9c5d9ba315e1e06b10bc672b022e84144ba1ef8735b1edcfb77e828cac260864373502d949526cb710bc27e65d92a5a0d8bc61d52a45df50cacf7e2bd0affd6e11afdd28ad7229f14b39eb23246209c960b579840c9ebf9a6d1e0305c8356bda44c7b08b5e5690be98c05a02c40444f94b42b1a700009f7651bb8e009a7a01eca607fe81ae92f4b84fba8960fdf328f95b9d7101df1f8eda1b3d1dbc6486a0252a26de3c352d37309f3c9d495abf046998eda35522882d2937ea59f04d1b9339eefa00beb5c3d149fc945f73d18d3b72fa914bc9b7ac1401fd6195e67ef0707350dbf8a8e98678fe6bb4370c306cc1833db7f85c8a0845296d12e5abca7f98bc684f53551f65eb0fe3a825aefa142d01c66eff49dc43ea8dbd1971640bc71bcdcd00f03e2de7d9501337d88d0b0f67fd380e5b8a1d84824cc792cc946508babfaf1f9aebd22cd57a5fb7a56d7d10b45499a309515bf6f94e858dcf5a4e08c755a73f11247af666c1790cad53db202e5ea94a230b721dc4366c5ed7e5c4071e44a5908711b603c49d2d8a7150bf7b10a47dd921f5decae06970731936c1575798dd4b29d4f85e48e7ae0a6eef2b3ace5fc0de11832979511b8df77eb3fd883cb1ba85be8a286aaae054cb992719b3fcd4d54005b527c6fdd66309d5d7cead4ceba9a4a8f69ec6ba9a8de3ecd2ada19e5be8f952b23ecf94dc4373e7e79b9dbe2ee260ce8411c784bf67452da3528ba19a4eaa2e70214b19bbabb9e07957e398696a803988ce3fadc5021b5c94c9ebd0b8818841a90b4715729735a2384db4c0c4d1e9d386af5caf34908049dc106b4d7c299fe2d8b02ea10ef39c25e76aeeab575190d31646775630e66a5a2120309075a79536afc6528f129d5d9689f20a0874b5649f96bcb7e66a0c1c9e2db7aaa60a01ca17950790fc2329a95bd7cbcdd590a96184d6d90db5b8cdd55c54fa809307d92d5ab7359ffb401c9488179ad43b15743531b4a0a902d528f78d844af6d978925ac342b17799e68f0f8c288a37afa41edda1cf9429067f66382c4f79137de65a5c27edf1c637dcadce0bd222286eff2e67cec58b75772334a2ef99774537ea7551f9cb5cc852d3ffc49acae0320c7937b74adcba6986ee6ece571188312d75decd0e38690bf72f7f8eecc01638594e2eb57ffbe86c56a091529149ffc495f26e9c7ccdf80adcc2fe5fdd93bc1b6da950423ffbfdf86e664001711838bc54fe2d85bebd13a1c1b86420d4e8e7a76e03dcef3cc2e0a4049fecedb47d0a47177637c9a04c4a3389403af9e40af87e7d87dfd40def9997ace56f35979b4673168057ef1875cbb35f0ca4ea66372392d6a75c2800fd35a58ed7dfa568f8b5ffc31822f211aece049b011bdf6e2230c45238282a4c2a5bc400c3591b1779edb19d25cef553c74fd22f6f8b89529a890f2e64dafe326b375154f34051bed27087651153335aa6996a89b2fde30d17f3bc996e70fd24fdcb217b428e81a1e20d2a7759d01a94a22ba4d417dd3dcb24ece626f00935e84add505391606121a8b26b5a8754917c9822b346cc157d24e7177f67f8d1da162475b34e9f548bde50e9cbd1918fd00ea36454edf72f30f15a76e8d6dbaed3e1e800ef8b090b48a43d88c6c2f9ab69344730d74ec1c66342f6797075f1bf2b712486108b306c561fc4f05550ec3e9340881438554e61809e9bcf9338e6ac74138592415eec7793baee10553a68e457bce35ef2811a1624abed75d0c113b85037ae306fa2d4033b6f2d014478de91c12402384ceab8da3a631a4ad9006b5866171c68c7a4067c1858e6196102d5bf641855a3f45f91262c0574f020750715b6e15f31f223693a3757397aabc24b3a8accea14538a11a6a2f05fe6bb240bfb32a714448b9511cd5823cbeb44ebf2dbc0fcc313614b6fefdd421a8f1d39a623f8a27b040f987a6c47f96076c401988c6235eac50945a57ada9c620db19499bb4857746365c58f8331ad5f6d2e5d777e09a04b76c4358eb2d6fcde630f8f4a6b845b9fb03e32397f6280079e8e0c14689998be5de70abb91118a8df2f519670c3356c84d5a93b9da1c183dcec07db2a31dbf24a6d017479358e07c811f538de75616a3f4d58bd3696ae37f83d2e8c55157f33e6c456b5f903d9cf00f73ecfe54275a8c56500c360bb58a51fc17d1d77589b672a0a83001c7051f00cb68762a8107e0708671b2007e413e06b8ca75a82aa99f0e6ad1e99dd56008a26b2a799ba40fe3a85fdb2e58d1fd0b2dda2c83580447e2466fd02703d55cf68875ee16083d9e4d4485b735b29a870d49c56917a1cd6d45afe1b14268725a216bf3ad9128fc2c28951cfbe8dae128cb46ed424abdc63e593e91090602441d6d44e04edf8add207e63caf43a7a4cba6e54e4f6ca04467c9530ee5f9c29b6d4e8e58d96da12c61680585997e2d8eed83ab47f617f05efaa2ef650e9c6e87e3a8fdeb8a2b183e0ed3a1f5d205844e4a4dc4b124d04532546f3eb8f9579a412e95e02632af3c90a4e4e28721c849a6269a9a1f3a0073db399da4cc422d6a65f263e2bdd5946e3b4f49349ac45d87a8a78cf7dc740ac9c75c78b61c965c30a2bda91e73d9798b21b325df89acaf7e4500725277aefde4358829d4a82eb7b3f11df728a03cfbfb0b88c56c32dd1c2e4651903bbb3a47f0b4e10be031578de13d881b5d8811f98c0052a90400d64a022e6c00e3cf0811358600512488a6520811ee8c0061e8126eeec29626b426d69b43bc43efa3c6a2ad64dded18b62a8b02dc8de4dd46723a4e2266fcc92e12b688c4a1b05f89b3e2f96b642daab46a24df0fe89e0c86852c05f9b2bfa2636acdb2f5395642f61e9f3dab0c0c67142fdff2b4bed0bb40bf17767e9f3cebc8c715cc2b27dc00babe682b78579983e5b04bbc2dcdb74a29fe8fe69a0486b54c8f9b084bb076c587be08bd167a9979a57fcd1d3f4d99b221fc6e1b6a694c5ce35174e4ddbed02ebbdd71fa9c22f03f6c790df02352880dd2968526283fa28ba5b41929305ec4bca1046e1a480e96ade69c17404509c7f5785819e2c2f49045e8fdd1992999f5980796df8704412216c97285fa9e06668dbe7ed0091ee335a14948b7a5e396f16ab8ede34e05cf36c89167b80afc6acd02c2664b10a6fe49e25687748339c34b008cd3b3da5905087b8f153f08c071d829ea34e54f9929c1d4f0f7328fd36a3766fea63270f2758ae44ae7ca344e22f2fea3fcad6e9aedfc80e01f1f4daa05cef247717bfa6d6a48dc013019f4bac0dc06f082dc754a2112723eff81c2e9b0c023e4011b3d67794133fda76ecb7a522c23313af16e8301cf6ef92f6a27a9c283f5ae98524132c3f60435de5634cbca69c956ea33da46852e0108a7d41ca144cd4e0841144efcd93cf6da341bf50f32ff5af1e693f6bad6230d6c12f4560e0ac63d9bcd7b779a442860c3a518d1a40f1332690f34220bc308f337c2e08a4331e818809890dd9e0a781cadade7cb9cbdfcd97ecdf4cf8ec49ad7f49f2017237509b4cf06e044336c7746406432af9d5a15127f20c0bad0e587cf60aabf71230531fd5954dd36b2560e3169a001f0142bd78f4c2c48e6ceab1851822dc4a9e9cac84df1ec3e057e20971e70b8bf4a2d73e8c87730034d1feeb45d6f8d635ed9480e9d769b392addcbb8bfd7212adb196aff5c213d2771ec1fd46f0c15f6430e37651dfbc754ea7d5e66a3096eee202f1d94e2f4d5759c79553ced9d8c9878b30a1dd0feee0fe2c0846de1512a94d1caf97103c8f0d8d515f37f15d486040a3b95d2949296f38ebf8ba27a53eea7ff0ade981cd6e5d1c1605a236b3163d8d473eda968cc6994a08f8d979dd6d3dabd24191b43c8409d2528fa2a30d8fc00decd65ffe9e35a880163a8a5db846756f56fd87636b12a9ce2c015d563c7ab6dd315c84253c287f48dc5b621a0627c904fac5d5e159985ec966f247108eb49725c37a1b6e11bfd10203de7beaf6868e5f31d081681b2a031bd5522015bbb480db17331ecc663991b85d4055687f412e8312293666c27d5462a1bd2ecce1b66e75c23d64fa42db58d0c6393db855a338d7f3d69523917c1063388170f7e0556cc7020e4a4b16b33619d7768f7d4de913d92f0988cc6e21b62f87b3adee92de736fa46c62fba5c246d27e41bee33aaec4db322eac1ed632c527b0571228929d8507bbbed63c024c380c0b109c51f3edca62f22d02fae1eec054d8ea02ae0d96e0d9b557113870247a651ffafb270322d52ab8246aef6c332f353dca3ac513bd4f0a2c725d15ec1d1273a907db645c68bd2d3ac54fb8574f58a88b0a352745296ad7f74e82843a8d5a1125ff34ed0f3431b897802a6a7f610e4a39f099cf5564a8f1aaf3d0aa299bf07d3fb9f058e524f575f4949cf80998adc7edbdb6180e3f896384b2e61b0edf881ac4d8871371b8face84f4d496e03cf0d80182a19e7ad24393d1dbbc14f758525c6df2a4940e33fa759be4c593f69c537feaccbc91bd90917921d910af0e09840f2402640cd1796ab6150cd9ac2061152b02c40349c0b9e45f12f4d005b94feaee80c5623d1616123836eff6c0dc4f0a88b17bf09149f20e2e42bc55846af837003db608da415523245b5aa4a0501f941d56471a7d7e8fe3e0abe264e87b18bfe49f64056d7de1bc37cd287889207107dcb54a81802d8a42c88437b81e20cdf6b56ab9c90fb272b04a84b30d5703c64401324cc19a184577f4377c4ac1f81144ebd285165ec621d19e805f5a33aabca220645f9e9b7b9825d343f9d2fb9db37d9c815f118aea6d8d3048bbde73ceadce552994073ca30c1effec1c8212231310faa7faa2d274c709c54dbbc53eeef6248a5863d23747b4d9b2da291943838a8ec7de2d5e21a56297b9302d56762d2a2873a49a8b22ad3742831bd2a90bc4c8d52a4634275003692de7ce04f5cfdd0d1e7b3a57a15837e5b842ab55bc3cabd0b26f7da0f790a231bde6e68c3d788c4e14873b4b8bc910a3c5b262bd0bcae2a96cece0c49cc1661d9321a9b5b232380309801a50db60e1ec1c637cb9a5a5d2a30f8e87e1ca7fd0cc128a9b2b8ed68bd435ac013cd11c315963ca92985cf384b18130eac6cbcb274c8285487455cf99c929d5e7cf129201f44d04da9bd3b65720dcd5a803d2e58089d1a5e1e268c0a1e00b970f0602c680c084af7be5cc68b818bea29325461881c6370a72451e0b327be5e7681dead969b879d68c71006b1878422d42a9043f9e6f646b96ec267b37d97bef2d939429b50bad0b110b935237a2088ee328a51f781f6534f061ada38c114568e0882d4240c2c0877352eaee45be721ce7ee55aca30c06b8518688213050e43b2c32a0450b880fe7a4d47d94b940101cc799388eab15880b04018415ef09f1df283369ae3055600a29a4a800153ff83085141698a9a2c4715cd78d3215a0e2bd51e687f7c61d13fb508129a4b0c04c153b3c4184c80e505080872788f4d0812854baae23f2de28b30314ff8d3214f86fdc31310f3b3c41a487f78c321d788fcaa78c46294d4c0008c7719ef789042847294d4c0048ca7b469995f7e8f0e128e3c490177d0efa7dea8bb5c6d1377cc88b46855c1455ca264ce845a39485b8e81bfe212f42aed78409c5904c985013d88409e1782f1624180786fd4349264cc849cd8409c9944c98508ecd920913d2719204d76025d806eb2ca9787a611ba3a82a74436f68558807fd32614231ff024c98509326384c98108e3f02264cc8897f8e091392c91a30614239390e983021084c98b19d40113911e188706cce789580fd2f13b07bad6c1c13bb290bd84334d318d8df2446ab2a8ca2bc55d5058df237ad20e0f3d9741385681346c4aa348a7241ab2a151a75695fdcf0336ef8d00d55d51845790542a33cf464ba78a8365d3c0465ba78c8365d3cb4335d3c749b2e1e8a325d5cc4ea625455caca2474e8c98411d13ca94d18514bac41993022232114db8411b93cb69d09233ac2eddc268ce8f5a2fea12813468464ba44c1fe22d68411c1a60b155a05c241a3fcbdcbb21db3621ba7d8c623dbf806db3122db981669325dfc4d338ac241ab4c4e68947f935246cb24d04c185192d68411d5189949686a5a4a8c607f916b8a8e4cd16bc2886ea60b92099b36ae25476e5e4c906018f6efa255622d0ba623d8bfa4e35f7a425573049304ef5f8a4255b304ef6fa2a1aaf97a7f93ab719c9e039f4bb4524d89cf25db129f4b37ccc46713cbd412a36144494a4ec0fea524fe252554355def5f62e25f6ae25f724255d38bf75caa29d904f95cba11f2b914c31bf85cc229c992f85c9a61ff120b1a4654f3b9e402ec4f82e24fdaf12fd150d534f2fe2523543543f0fea5231af84cb2916e457c2eb18cf0cf2557e985c4e7120cfb93c668189192e9e2e1675216b03fc9060d23b2212da1aa89c5fb939850d52cf2fe24275435b3787f520e55cdd6fb9374a86a6af1fea328fe241a7fd211aa9a1e787f1212aa9a57bc3f2909554d9a173f93708081cfa41b526c88cf241c223e936419f84c22d130a906c467128bd40ae233c97581cfa497109f493052cd67920df61f91d130a22550a86ae6fd474fa8aa8af71fe9501515ef3f72425553bcffa8095549f1fea31f348cf73e2203f6d16d64b3c0e751ed87cf239a0f9f472359053e8f704437d3c57fa445c388988c96505507de7fa484aaa278ff5112aa82e2fd4747a8ea89f71f19a12a227f82cf2320e06982cf2310601fc546373d7c1ed950e0f3a88687cf23d8e8b5c3e7910bfb8ba0505513ef2fdaa1aa21ef2f8a42554ebcbf08090d238a4d17ff1b1a87e85da4834534209f45b5097c16d9b08b6e79c41ab59a4c987642ab1abba809f6ef254effe3863b43cc822849c725c1fea21a4f8d922b5282fd4536a20df6172d1997607fd18d888928364f704537d30457c40414c542373cf0de3f14f30f35f10fe17431ae89c749281373c619cbfb8774264cb70b8d0f9d88b2318765869d46488786cddf18d89fc5669005a0189e2eb00b218756843bc86117b21803ec1fe2e264c59a97159194c08a474860452323b061942336dcf1c2864f4460439d2e6c98e3b261132e6cc8640b1b2a09810d9318b1211210d8d0c8076c48a385f5ecb4ac074a16d6f3a488f5e460613d4e68ac87c915d6b3c403d6a3c40aeb41c2b29e2333d6538505a35061c11d8af29fc2825258b00316744251fe5158b00945f94361c1272c48c4824928cadf090b0eb1a0118af26fc2823414e59f623f9bfd6a36c0b03f132fecbf84bf12620bfba74416f6e7c00dfb2761c3fea81af6df000dfb0b9961ff2035c4b03f1237d89f061bec7f440df6370286fd8bb0fe00085dd85f033664617f1b37ec9f01226ad87f081af69f6186fd3120c3fe211cec7f63d85f881bec7f018f0df60fc25383fdad0786fd81f0bcb0bfc9e3c2fe204f0bfb5b8085fd7fb037ecef832d6bd8bf0234ec5f7a19f6ef810231eccfc30df6afb1430d76127c617f1d5cd87f02b6290a086bc5eefc9851cac49a28f6471bfd0425ad8f71582dc5140ca50ef63e8b702a45559a0964634538502873a5d16a13aaaa4f3ccbd5288ab37957acb5a88a62e75af56d201b07dd6037cd3ea29969e65e87c18f03b96eb6ba0be5171fe5020777185cdcd8803b0c2e8c6091105a84d338c0f72c6e2756176e63d8a4501303bb090998162945b26f060e4371ee943b400c5ed3ab6ff89b66d5c5a5fdc3f9ac08c756fe5079f92ae6fd4d0c787f53cd570d787fd3cd5732ef7f7af94ac7fb9f6e7cb5e3fd4f335f39e0fd4f375ff178ff9497af1ef0fe2937beeaf1fe29335f41e0fd536ebecae1fd67bc7c1581f79f71e32b1fef3f63e6ab1fef3fe3e62b09b08c37a36a7c7ef9cf38949f1bc77eeed9fddcb5193eb7cdc6e7be01e0f36cd1f079ba6af83c5fa8cfb326f579dad8f079dedcf079e2b47c9eb21b9fe72c009f67cde5f3b4bd7c9e37da1ac067ea22c067fa2ac0675a6380cfd446f599deac3e531c1c3e53d9013ed3198ecfb406f399da10f099de12f0d95bee5a80d7e0cf6e83b3697683a704acffb0eec3d208589a83a510b0b4879db407d8c9c34e98a7e63bc0eeb042786abe0e2bd300cb00fb796a7e8c759c43011601b61dc6e2c0c1ae700c600b3007e07ab12ed3766376830dd65328eb291a006063069ba72cc8b6a74aeba91a767eec0ff1e6efe411e18870ee874d26c02ec2e91b5c28f34703ffa3e15cdf2b3dcff33ccffb7e52af21873f5f7cfef3d4ac60ccfc901629e7f718a55833cd44382bb7960ffdba9507a3a9f99e0d9e9fbf1decfff97200b261f91ea769664d3393cea9753a627a62da31cd4eac93918f66823de6388efb195cab949527b126fbbeb23c9dbccf626d8296dc30a1aaaec66423c2c9094331ec6c6051549db5ceb0cb91f94a45094df92f9931a135a961577162c39e039a8174a2809f4db352a44566e0a0cc221cafce9c3af2f06a9d5e37bdd7c1a09c19080a0541c15e2f48465134c61f6473f2986634e5eff99a83d2f3dd7cd0b3ddac5e2a4ae7a56e5d7df032012d714255a0076463aac120284fb08374b083726a0c24a3aa89414eb0839a809850d5acb0538db15c58856da1ac0f7e16e13436cd7c65b269a1ec5ee484a2bc1cec8966221d116de6b6967d2aad3ea93578f2a9b54aab4fa6070739a026d8bfa505624251a0d80dc49aa7e25d1d147b624d844283fdc7d79884ae42e2ce0d76469ab135ba4624e20dfb7bb356588dc1a82a7fb4eae20e205cebfbca926bb58c6bb58c6be18085fa34afac5afed06187ece1870e3dc379453822277436ef27d2c1fe4346031ddc61d0c0092600ee3068f0c2205aad730ce5473355286516e198eacf7c4351fef5826c280ab46466d2111d61f64a84d32222b4276b1c1e5c4549c0fe2209c35ccb57624dacf9caa423d6c49a1fb211088d51624351ad9a85328bb5ef2bcbd3e91f857a79e15a3860816be99835908d0c68c29eb436e7cc09896ca3ef89a886fd7b02a58e219ba9ea8765f53f5f953f415f7f445735f6116ba5cc63a5cc57261d9a721314ec9f4b1aaeb5b2544219fa9a5386f50a22af866716cd30946a85b2be13baea188f86a708c757209bcbc1446faa61f9211fa6e16f4a726a1cd3f3296f6241c378de69a238fa4ed5ff6a95e62bb775aa569aed66b94e6b94d75a00f573cc629dd637ea9fba6f789c80ab8b81ebc42cf68bcfd3f85ca759161bfeb41d3e855f7f7c1a96c887c30f678415023338bcb3be5167ad429962b33b0167114e0dc4c47483fd2bf8d59bde6c31827305bdfae0acb785356fa5a8ae53e18c70b0efeb93327b4ff07c8f8b950ae5f79f07d9709f45389f6d9fcf89d37ade136faa8c50074fd17b6eae2decf9708e3233ea14cacf7f9f454aa8ca94c45443551ee945382e9251557d277de65a35ec7f7e26a67a3a7cffb95380e1fd149fd7a1c67fffdd29beaf716762eacde0735f69e5ad5228bb0a06349f9f54ac9136f064a4460d1afb77652f1560e840be7d2a3e57c5fff3e5974f2f15e455f1ab2281f23fcf03bdf9e3c1dab737c5935203153aa1284e36431ccf5f7441136c7187e10218d601acb3798a4bf180dfced5b8f9f33c7af3e3eef04fac1656f9a3c885bec18d469f43d2763ad1fca9e54e7f0805c19b8780787cfae308be5310bc2b3477a54551dc0a8bd55da1cc24174634a31655a9f837ce33231655f5adf194d6185fac4172a171f4ad5028c1cf1e7df1479b2b4e196d0b8d27a54619e2f88ac6b7d3685f7d5e953572d752f263afcfecf734ac6763a2e29067f3c908e67edaef4bf639b1d2c85365e6dd3c9bb7f3b576bed6db1145cdd4ed438e1fd27f04535152b75419930a25f99fcfa71a7f625195ff0c13159f46d286a0bbf81e2b8e4fed388ae328de518ba258239a57d542391ad18c5e2e2594ddd79c920b55e31c7bd4cfe11d2fad1cdda98fb6854551748a23088a20288e9e07c5d1333e0d69cc13abc68735c21a618db0c6e7c7a1e7a37efe288a7b9b67f08f589e8daafc47d3b365cfc199b4f9c4a2317e3eb142908ee253f1a9e7474be9e8e2f81ea7e3f8e2ad308ae2fee97f7d5114f7a3ad4828ea9ffbba85924342553654d52f6acb158ae2be76a1cc27ec6df314d72316fdd0e62bcf16da6ca10d73a0f7c438cdc3af61f30caef17394a9a12201fbe4fb60c9eb437b0a14dd669f552950fae183352ce9434b6de3d2ce18cf56c3dcdf546c372c8a2a3a14a542a328eeb959df001d0aa5533a6b7c8fd4adf8a39dc1b486f8393cb4a21755682adcabe85015131f5e79ee55668d83c69f5e458c86a1f1dec8447e68acccf06c9c67ab3885c6ca0c1596cfe743ca426365060d4abe0af929e487c5a63cb51d5e4979fa335e25e5c3d02833e30ea04c999142bb14fafdc96615276456a5a8d293e168cc674f145569651e9d5854756251159d22f879f233495a958f0a0d73ef2c8ae2beb39599b4b130f7a48daa3ccf3db943de9ee4c6f1562994e3e7112bfb6cd207c3ef71871882cefdec1bf4b3a7a728eec471dc8aabacc267c01ac5091e8669d3d45665f50dbfd94051147def8c406b30fd1518a6bf32f3557d95bf5dd77d2b2fbad219a1ccdf0b16fee8f31d7eb1588daf4ee2778aad08e1e8cacba472aaf1152dc5530da6a7d82886eb4e34a71645951d19e5f7271a3aee381919d15014c5e2cf710787c5b123a32c31e8434fc71d158b2390ee14e8bb53a1f771876371dcd18d027d372af475dc41b138cafca0c1028b538be529daed38b1aee8d637e8cf390299312b1ffa0c3b45fd950f0950aebcc0d0a1beca9d42e5ebafd82966fcca97af98e8765ab1eda994951f2bdfa304664cfd1cd474f3d5ca53b1fb3a4f47ea5e9ea2624c64f3557dfaa29baf549e9e6e2e3169ba4e9f2cddec35f1c53de9e6cf23be5e626ce57277fc149b4f2d4c3fe43490cd27d6c995dd76bfd1576a6c7416c3f4bf97afca17a6af622bcbd6eaf5ec375fd118fa3dae5055c959256a0b971f12a05c41425760c3fe5d90f1e7c78e4a3d5660f47b6ebc392587d273e3e9c79b4ddfdd95d773b7b682743f821ff218bf7b3afaf83c7755c8e71eecac0af9b9fbd0aa909d25df2b7de393fcc71ee28ffff97c37fae0ae4af83f5a95f04995f0eae89e7cf2bbff3cd97139fc10f9dce880893d2f5e21a00f3d6e7c80e73b4b7e8f1198d8d33d79bb3094dd83ef79f206f1fce70621fdf89de756d27bfe2329a5f4dd8f27d3f7dd57fa3ef2c70fb9b147067fcc000e9f1b73f01c32f8e48f971b73a098fb6ea682b474e4e13dc8f1c1f0e66eec56602b30ee2b6757602b2f2625f7796505c98fcf8f48fffc38faf060f1473bfaefeb46991189f4fc68c1f1bb5107f921387e9e9024c99b85783f8ea3fff018ddcfe770d2175acf01c4e28fecf83d8e20f8642786d2f3a1276d90b187f318fff3244992211547db7824f3e79b98c1e48be3f8a0ed96508e9f4950142df9e1371e795ef4a1ed51c713369cbdf75c21b5853f9f471feb2fb2b9891af63ee4fdca6b05097d2f9dc8fcc41ebb0fc307cb0f39eb192ff89c25491de37f7eb63017fe98673028fe98cb17a6df63975fbc413a1f65beef6c27f3dd1e13bbcb24c155bf0f233d19a17fe82e777577379dc9cc39298c524add9dbbdd6aed461997e99837ca90350ce5e3e0f48d7e2884dc16aa11bc5ae777dd247b728d638240a795b7e1a067e3544f9ea29eafac0d4af1ee653a0de90b9ed531393aad8874fe290cdd72b862c761edb8ef3e048d32ddc44e7698f6f7e96e7e556861d1ca289515a5b2be2e0f01db5b582d45ba96d6c7bac20cad0c3def71de79baae03bbd0d3751df87d077e1d48eb0a66778d3ec9f9814cd614e52a342494140965778f324b843db095010fa93f311d7b9ce88794523c6372d5dd41b0fba07c4f444ee4c4285dc59d8f669a7d34118ef82117be58ebf875941945ae082d62abdcd7ff5aaca34cdd2973d5c1f2e0871c128af2bc2337283fcf05c1a762cdbb5f2827f72c3687cf09a92d2cbe694615f075d7343f2ffc289f1bbdfa39ee3a65e0a4ba64c0a414c96a9c8d0cac80b3c8f661cda00b9c4551c0593f9a6946fb6854d579df07065317ad42dd20c4c8111c1081114f2bd54ab6d22df5e2473c20441327353644505183050ae050f4407381fc340cd1840e009820c061baf8cc0f5c5c11c4936e62249c19ad8acfa41a02dc07272610050fcf6a616088059263ba7812403c211bcd76a8d9906880164e0a994a3f72fd08b632b2b9f93c8ad1388920305dfc3da107765c1547163094a30e8ab9568da238282a3c90fffd77a950a94ffef7e4774cb53e46391a116b229428d84716f69146140376110cd8451b767107bb6c86703f795b6663c240a06d4c2f4c5d34ea88ce939d521423acc9d537fc8318f1368ce067165e08f9b9859123c4704212254c9a3811e251ae9f5a04792244f05464f13564e02db0050b5c006587c6c891d6059e060f14f14d84e075d082082ac6c8c212264e72747e781b43fc2c02c56ba0d5c3574163031c8ca2d01c41926402d38a209af82157e8303322634486264e749e4089620a1f3a5005059c30722489922593c80e4f0ce161f4032d80000251941d1c271425eb1b9e68860a90ff58ce763948480207725c677392c1565840c019256641059c0b80b358133b30c4450ba602cea5cecc869a00837080a4f404cf11dc1294e9c5cd259b909b4bb914651ab9d9944d3447dc6cca2623aed36729344c292b5101fb772e29c1d3757329979604b9b9743345704b4d6616b7e4240337977229676e71736966c4cda518344c08496906d8bf3369074f0fdc4cba61274529e2e6126b86e0968c4c2d6e2ee5d211226e2ebdb097905071730926c42575a1614230d217b07f6712130bdc4ccaa42643dc4cc281e26652ce6c5d924e0f97f464d2dc0bdc4cb2617fd20e1a269464ba380fac689850cd15d8bf3389665a7133c9481037935c4d5c129279c52525d1e166520d769292999b49363fdc4cbac13e7232c5cd2319f6518e0f378f661db8a32755dc11140adc3cb2611fed3871f3e836819b49426898909251170d13b2191161e44514378f704677c484871bc5e7d19221178acf23254fdc3caad9e18e9010b9443e8f8e607f91180d1392f9ea8617e12cc249b959a4b372b38806e4aadc263e8b7666dc2cba0df95c009c4538a29b472c273e8f68bc9b47ad79f3c885a226c02624426ce94909ca11b6c4016ba23105a9e199c51626234c2f5c9202154a4a4a42d852939293521136252d8828e1607f2a4aa519ad1483d20ca0b0a41e2c294ae902b674c49690dc4a2cec3f44132517f6d7a1344150aa2175e10b3396f483259172483a4158d293188924c3feb3086986fd354022d5b07f15d646da010f48344e58929109581212120f96d422b9b0fffc00e985fd31408261ff212492cd0dc90ad215b19193510e1076f4640465b4435123d96886fd276b44c3fe3b8c6ad85f8ad1e846c48e8e8c908c94d8d1123b624251a3d7685483fd574636d83f65348ae18c84d02efea32edac59f08232f46345644c38a764439322f5846ae71c4c2fea2e886fdabc826aa617fae03672231dac5454e70c281111c39c201122ca1c4088e30f12a41129f5d5c7091bb1012c4c5c506bc1001122e2eb8c85d0809925d5c6cc00b1120a1810f67ddddddddddddddddddddddddddddddddddddddddddddddddfd7d20e8f17842310c4531013608dbc64e39719e6977f7180ead66bbb15a2e5a637313c391cd6c3756cbf5a2b0971a8af6b3930c67ced80deb6b95aed3eb61a89a971bcaf6b513ad9ce1f0827af76fc678ab5b36a3d56caed70b06aba9b1b1b99975db58edeaee8e691c7c45fb68adbc793138e080030e3828e1ab9acfa6bc39d92e46095fd96e94d5f21a6ed6d4d4d4d4d4f8cd172b714eb2ef268dbe7c3569b4499b34d6d72ae78bbe5e99be64beb2bdd450b49f9d3e2a6bf9eae50585fa3f9dca92cacaef54fe09f52f287fee7e51afc7260e3961db58957ea69ec27c95c3cf20fef3d5e8e7e7124f2456def37d172a69bc4a8a893483adc172430d002040035e0031ef7203e600dfbe7ac0afbe7de580370003f00212f0edab1f9ec7b7af2af03b64be7db5c303f9f61506fec747e0db57403c04be7d6581f7a1071ebe7d45c4ebf0edab215e889f3be58ba941d4b80a3f7f90ef0f83d8cc55a733c68bf97c956a01748ad2cf264c3f041bfc7c513f880571b65f18f18f7ac101899b8f30c27e9ed240068818c20a712f60db53415c202ce0c3adc0a5c0dde1667ac313c8fd717dd8f654042e042e8fb963cadc06d8f614036e8c6d4fe19b696b013753177dc1dc595b5dd52dc0cd3326809b27ce94ddb837dc3c61a959c305c0cdf366869b27cbdeb7d3e52996a97267d8932ddd40b2f15a149edfcd2f38e019533bf6f9a43f8c5e6829922a6376b7f7f7e8c3bb1bf064b8bb4182389de9e6dc226c1bee343afef059fdbe9e312da781ef19dd4685d7e7c32ad2cf771d37ca749f595b96eb4ed5977bad05e03fda274e6b94e3ea2fde6e5477dfe01cb4fd85e7f78bef7ac889a3ad1fda0e835d18caeab14b54c1c2f5c350ac9c57f73ba73f0a01fd14f51d535f54c51de145bf7b9f5c9d5e9d3315c5fdf1cacacc867b06cec719e40f856a09a30651563386be2000f7a9002d53f4ef45ccd9c0fade3dafebca78e19c9211860428bfbe4998dc788d2b498283a793260eabcdd9c273d21d3ce7113c678dde541cee8632594275f09cd4e6e62cce455994264a123c67f79c945277afb5c66ae59ae0e87433cf6b32a1e03939239d91241eec88913995e0d9c2b37b4e4aa9bbd75abb2ec7cbf96a60ceec9e9352eaeeb556cf5b1965e68373dad831714c669263a51fdc2f36f5e9dc87de99001d846d238b688d6b1340ffbeeffb6c14d501283d1c7c5f91d45775813507251315675c391989e3837206c7aaf000bf53f23318b4dfe7d928aa7e2e8aaa9fd716764b71ad60286ddf5796a7d33f0af5f2e2361287744255df4da2b6489983efa30c48bfa04fe90c0eddaaf0f0f7debbd9dd6d14555d14555f64e3293094d9ab929a04d78a04d79d6a04d74a43a3e0fa6eabb5abdf775de6819e67f31539f3929c7d2d5c3f5287aa9aace1fa9f58ca3c551f7c2594600b2b4a94cf6b618d350a652667df27361543dbe743db27b4e5289f7b7908e8f9cfba7fdef71ecfdd7bb7dff779ceb785d25ffcc670e694f343b79ff7d9d4cd3fcf73cfbd0f3defc1ef3ccf3feffb402a94a10dd71975a7ac367246ce3239a336b481e579f781a3587150924fe68cd42169b5a5d532eeb7dea09c0fbedbeffbeedd7eef59f0f3ce1ffc3ea73b74d531e48cd4b1248da2ea572d94ee0aa05fc959a597f442e933116da5060128615fcf9622e473a38f1077640972a112ec3e5dd8759fb0fbb8f243e58365e339ca900d7e2ef8a1af7bb27c92f4bec4654b911616f92d4548b2bac85b8fd4d715caea1f7263e0f96317f07cf1c359b1470a787e38c618ad1283ba6d4e5cab358e4ef41daf950f02bd93ef1f7e481059426006939f197ec2e7843d9e9b5d0c9c4b4c5b8a746d61b5d01469695da16c69c1421878ae0581baf153419f4f98be675ad0cb5419a2f8e5180afd0702813e047d17ba79625088413dee008126c8bacd7728cafd6685b2fa93f663eb13caf1f389a2706ca041e1d8c04a95315dba5baa0c1b58a95baa8c4fb4426903abb242ef7410d20f4916ebe6c66b6a6c369fce798dc1512ccb8fc78296564b913a85d29fbec784aafaabb70457ef06576f7a57acefb9d96960fb8abef75e0dc3f7783eb4550aa5e7f39c1ff7a0c7f58c794d18fac4b0fb1353ad1e08fef7e1e8c37b0ebc4328aef50ea1f8f36ea6b8f415ac5fb220c7954acf3df8a5cf41c71a7e2aca84f13ee45032dc71dfe7bd67dce1792fd57d2a8a0dac0a7ef79ef53c67c1272bb68106e7541913467c8f96483e463753cc7d36b0b0572a5daef4b9f43d9a3e072d79e28b37cca6db837bcea6ca982ede55111ffc8e246bad9f8a3261b8f76eb68165030b77eff1984a13d78e24b9074d5d773a716f02fff3b837bde7b161f23fb62bc8bd09741b1c7296c3a2f87d5fc562e9b3a928d3c57bcf83b67e7e64c71759f1bdf2a7e5c4fa9ec779e2f59c8e957bd2cafc3ff49fafe8876ec5e4e721b5bb333a3194427c86c9db7d57bbfbdc8b1e1b0c7bf7dc0fa9f87bf26f17865288109f850f12c99b5138f5837c57ac32cfc8d5eec5300c9f1b65428ff510b4b3c3a09dd8ab5c579fe33a87eebfebd92c84ebb84bfbc4aaa033a387a610aaf0f09fd55f341aada13573ce09e4a3d128a59402f1dcdd1d4867abb502e1388e0352bb0e88378e0784764c2881cc18cef37dc1ddcf53f3fd7ef5fd298f7afd9601f7b8e34312f69c30b7a9dd5d83f2e449ed5683f2844669adbfdeba9f7bd2ddee4a646cba9f8bf577fd5d77f7dfa2ececdc6e51766cb5f6735dbfd7efed74b72dd684099358ce4c166bc284f644e786e39874314fd6fd1f93eebea9b5eb5adff7ea07fbc1eeee9e94bad7ea79b26e998ca7bbbbfb44a97baddfd78d3ac984dddd08a8a161fa61c8f33ceebf6fa1f95a8c78461ff5a73f4769feaaf538cff3ae87600e5dd79e6fa16969dda837bf3beef8fc375b582d455a5a54e5fde77dacaf6b61b5d04c98aee5bdcf415b8a4c98fa3404f9286fa62d2dec2055f9e1ed7e76efffdebd2af5b9ff7accc1fb79330fefa7f7f37bff169aeb39686e69e10f1c77847ede205fe8f3bce7a1efbeefe3429cdfe05a6778e777b6850664c72fadf8a4adfff9b4b0c8f08629fdf97def7d3fb91ede735785cef73c04f4781fa44eefbf9eefdefcd0e3f9962213a6b1a7c39e372787bd9f2d4526ccb3bcf73cf0fbbcf99e07cfbb03dacab2eee9c8d0af42a1fc9e35dfbb3904c4dccde1610b8baa8ab4b430ad8a39e339a80a21fd9052b29bcedb747e772a12f0be7b201d43838577bb8b05e7a9193cc71d93be97b57e0ee8db9151569c5d0c176307a9c283be7b5df7d941afcfaecffa467f7f0eea2f8871ef2f588dcd4d0ca7e59ac16a2dd70b5663f382fa53f97da7f24fa87f41e1d0f216adc6865372b68be1626c3506a78cc129399c922bb917adc676335938e587cb22aefea982678349e7fc15ac600546bcbaab20ca576f5584c241019f4842f03eb8c350010f3e1177182aa801e8c11d860a5ee0093d1e10c9b461a8a0083e91ba3086a2f0919125498691828a49b8c348410cca1109480c230521c0a7695340239a818511480c524aac74aa318d28388948bc4811e10e03090b9f1a9cf111c7b042c330a4392845d1399150247e83e9bbdbd4ea4930fdea48382698fa8b46a9d76a1467dd6a4e83290dd377772532b40917eb6ca8122f07d3f79c604a6b6c75e766e4bbd62dca0e7d5124b65813264c62393359ac0913da139d1b8e8b7939b219a3ff31a1b4eb683c9ad6f7bd666bd2783c3309a693d53d27a5d4dd6bad3219279f1319f804a4c93c4ec2ee3929a5ee5e6bfd3e907a50989e64c61d1307194cc130fcff1dffe1531b3e1d75844eaa3ca12a4e4547854655eee44f2c5f59f264ed89e5294eac5183734a92fe537c9ed4c1926f6fc7d09bc7174110fc7c2a51379e14f153b75419356ce36e94116bd41f01abee543e91517aa7fa6586923dce897bc1fef279d7b8ae6147f9eac506c7d9a028cf7a40103cc2fb107c8f5dc2f33cef2e01de5cbbafa38fefabf75fc7bd0d8ae2ba23a32471bdfd83284b708f3c483c5d4cc2b651863297d84b7f0a8421014aaf370441320c5b9cfdf9397ffc6fb193193f74b1ea701cd6faf5dd0699eff3aaf478fafe65e4e014dc3f98d9e0d07dfcd8aee3e76355788cf9f3e3cfe0f063f38ec64ad0f74f823e6d4fc7c4e3eb98e3d531f1e77347dc8956c671776719306773155680d3e6991eb30c98ebc4d07e9ed73d787bba7cdeede952ce8cb3f341d87d48eea8efbdf793fb9e651865be6eb6fb274f7c1dfcbe2a442aee5f82c47ddff333d807e5be2f91893b4f5f951e354877874cccdd1ac467c0494b10d6f0343414f9626a7cdbc8600d35ec8c0929c9e6ffa1b1309ca5cff3467f49a4eebd27ae230f920d92455154f72512a9c4228ac052c771f9be534afa928d8f5883ed981ab66368b01d43fafc82bb27bdf75d131ba4f4349ee5fdbb1f348e15ae867e9df1b57e57bfab1d8d6721b17c37e36bfd41921cdc48c0fd83241ee0c492e57e857b1a9f83b2882b760912db2f59168e23558ee3dec6a5aed107fdfae18d1a345c5c5092d87b961a33c6c71d15352305754ba4008835589a1943c32df231347c0d5be41443c305a37e6843a4b092c23e257dc3bf7de520507f1dc8c2fe2c9f3b508b568905d2802dd2d863ac79e2d17f3b2aefffdd6c54451a7bf48c1c667c7d128fee6be9bbfe649e5094f9aa63be43f1ff45b237dbc0ddbb0bcafadeadbf0489eba7a47cb5a44b5d97c8c4dcb78c3a0230030bcb8a7d50d9750e5fb1d45b50e62e86bb265415fb41e3c8f4ed07a197c6e7ae8b81687ca5f1245b9fc536a6f1214a64b14fb2a51cead3f8ff94a72faae26ebecf8d2c57a5c70c7ffffe0c3f471db5e6da5861795b02c08fa30f1acf72894cbc7273fd7f318703c0c655e9717f869fe1de20ff4326a6f13968a52a7b33a92aa12a1a370809fc461ea41a160c1da0fffba52752496f5f8548c54b4c1b8df78c3ee8932e91894b171c7db07cdf5c7fe573b8b5d445517f557a8082acfc9089591e7483703f64be88356cc754aa72fc958c324655346eee5779961ba43b1caa5ab94f515d8f193cfa9e28aafb7abb0f5dc43beee07ec8c4f455ec0c5b52548ae94fb78e3c4c5f5fa42ad3155d700500ae2ca42e368e3aed82126365dd51766217f3d5b47de9e613e9e6b27cf07378632e6441367f60ccf74f56defc91275c759460e75ee6ab0f8c4ef907e1d4834f86fdcbc17c82c1aeea74318a1a42e95ffd3fcf1f664255dfd79befc67c08c0fd75a57f8404e038ae56b0ab62ceac703744a141abc31d060d587896180c1b184fc0c8c11977186014019fc0889960091df5b9d73175d4eb65a9a356991d7447f783068b7e2c66df18712b2b366c9c4e75dce112e8beaf2cebb883ca646fecc828e953aa83eaa02139b137eaf840efc4ee6ecfb747ab615a5bb5956beb73792c8da945ca6cc3e5b7b65aefd1bc5afd72ace7c3aeebba9b6738ee3f9b5132ec75dca92c5d98ba2b07a55f5ae7d5ae725eed3a6e7ea247bb3084fe6d23936878ee94d97b956ee8e2628b101801c107b4686551040b9a2b3c60056ba60a2aa690a20351dcbc26bc219e131e11ef098f892594e040121b10122423718411384af93373d17087c185007087e1aa01fe88d19f59e3287dc845ab455a11b2c8b875c13606ae49410788b9f8fc6df1b90cc1e79311107c46e1f981cf2f78865a7cfe5a9fcb2c3e9f8a7c7e2c68c42b3e7f3897d873c29ec71e14f6bc604fcc53f4a928a5785c51507c7ec14f7c7ec1443ea3b0e78437c46bc263c2bb2df1f953e273c981cfa7243eff063ea3847c7ec141320a89cf9f4f461451fbc0fe2b1b012e0f7ef8122717afb9c56b86e0358dbcc49f20b819353f30b5b8a16cb66e289b59dc50368bdc5036b19834e4bce28aade9812bb6a615576c4dd6155b33555071c5d814578c4971c55807ae188be28a31289eb8f985c8cd28276efe21379f9ab8b96462893bda2871471b0edcd126893bda6ce08e36423eb0f183dcfc72330a899bff889b4f46dc5c16317e60e307367e60e338ceb14787f30c2ee2c397af423098eb05e3e205dbe2050bc10b66e4050381cc57a1ef03377f65793afda38a84323cb1085bb1ef2bcbd3e97fb2c4d68cd8c225153797a7296e3ebd14373f0a15c5cd2fe28dfe68e3abd1cdcb0bea899b514fe4e63f3971f3a91c7273d98478fb98b8f92b97b8b93c2971f3e93970f3a392b819b5819b5f701e6d30fdb1467a1172f30b2ac8cda8cf37ff09899b4fe5113797468cb5af3cfd07161313827d3eb00fec03fbc03eb0cfe717fa5e4c68f67d65793afd8fde452e9274912ed245ba4817f95584f37d65598a70ca2f9f1bb1e8835820168805628158a0cf2fdde886f4a4273de949ef8d6cf44935bef2bcbca050ffa753597afe72797a77c19954d371d58bb85f70fffacc3e345fb93cfd4fcd572f4fff73f355059e3ef9f2950f4f9fbc2167e4cd57403cfdf255de94b3f2067af96a88a70fbaf115114f1f34f355069e3ee8e62b0d3c3da170689c96f50cd536500d794e1aca09803c657346dac8d4455f0ff84c6b32cd9fd90c54f6b999da807ca6372b7e669fdd26fb8d06ac67c03a11960e6129062c15c2d20bd8590bc24e20ecacf154bf05ec6c79aaff077b84a7fa7db00ef0547f052cca53fd3d58c7f1543f0578681a407ed8f6940f1b81fe1c7a8547efb0ed291d2f63db5375468e05d8f6949700db9e3ac1d8f6140e7b805e5995017a00b65f6c7bcac572d643137d006efe6edc5c8abee5e693e86ff0d06cb81925fad4cd2e387b68289bc918492b5925aca48160a01888166261fa352c0dcb6257ac8a9d6153ecc99a6cc992ec674661a2cfac6f387f66d306e7cf2c86e97f6ca633d0d5389dcda0c9b2654a03bdd76786a3c2a30c78debcc42c0376fa144fbff3279eb73fecfee88c329f9dcf8d7e6814f5c475288a92ac5186a4a128fa8942516e5dc635d7d5ae733a3bcfe733eb1bf49bc878454a1e9485737d951edecf9f3708763bbfff28aaddaaf4b841bc39e79c73ceee7ec1d90fb7af42b35968460bcd6aa1992d34bb85661e2b34f35aae0ee6ea6a5c9d8debc61573e1b86a9fade46e27d6b730ed5c229ceec5e26c5837ac180b872563cd58b49bdb0dc7bae15a379ceb867bdd70b09b1a9ba7621f4e293bcd9e86aae151b561ee3373358efa2f37289b7fbd3e33d4cba34e5f7e66deabb6bc182e3413b946acd1cdc8e6bd3eb34abaa18ad54caa8a39e35c114c4079aa3eca5178deea2a7e758481020ed76f5982ebf79001ba2ad1ea1780aa3a8c254870fd5a5f6aebe01ae3b4a4aa5c4f2f85155c6de05abd8bf95f111fd4b9cf41fd43ef3329f3ccb0db915094085697886ea80d07931125712414e5600c014a514c46549950948b48197652e699d9ca0fcd57e4cb53fedf5796a7d33f0af5a17d688dc3c320265f950b4eca7cc5bd77cc6765b3fc617a979865f84a97358ea63028f34824f3ce08b4bb69777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777707a916a8442c17564d102a44230000000001e315002030180c078462a1300e0359b21d14000f76a24a724ea10c8431a694318a18220220000000000000081200eaa783925cc8b116f54a700b934c688af5a58bc9c0854604435c07f68e83e428c0c16090d859095a1b4aa5eda5f0d935ce861c41baabe47d5d9aefc6d139f4020b82b710044fdc28e99c361fde3ad45d154e19ee463940a847658eedd754064271d47fbde75857528fd37d04ca6e6c81658a1f8a36d6e8574954e3ebe759ab4765fa5efa21ed6732cc5c614788e9c70578851d4ff61174cdb2817e3cc9c5fd484395403d8d45cfc4d5ad32df846a2ba10b936909915dd34a519647f7f75f29504114c3e2c90c63034e3eac9ef59e2174936d0f2dc650f71715cef910082803aa56fb1f6a976674b381f39604dff6788b8b2a56460c31fdcc6339baf7b5b8f642a96ebcc1430736bd56eb320468d18086f390d0bfd0f5708661b0223c9ef45af9eb137d38518bb0b9c1fcbac76b1845738327196ed2b917258b36dd570fe30cd007e22904a3b765923bc2b4219bfaa4dbc02aa930baf0b6134c682850026f31307b04d549dc4e1fa467710d73fc5bbe0294deb7cbaa13cb348d6d262ff165c5570f374407130b58c65698fb192fc56f9b59e0a0a6cd73284a2a397b5a8af913010d8f107598cee9ccdff03560b73578b02a44720f626599ca248840e69c6820f88d8ca0b3f3e8a53a3d3d86adc12d790f29ce0e2a453168920fee556ae45fa912415f3affd85641be258c7ec14751c023505acc994b1ab88d6208710c4bb732ef0b1f8f8ade30cf9ba9c42fa0983886637c93338d0373a5a5311570e55d2deb4df4701c92d6df732ebb06b848d09fcebdaa89d9477fd1ae5745b95ddb50707e8374d3168765d5644ad39ea26300b91a3d3d1681320df03c65980e2a415a49b35bc1e484eacc8c2f9085fc522c09c79754cecdac5e8d1889fc58f4ba5d14e568cfc6b5a2ae1875fd0da966d4c3f2a5e2aef8404b378abe8927a56db9c2daac7b9f477b433767e975d248393fc9ba7fa4c5fb9cac968a3b1bde4da134eb29beeb2a947a9e92270904ac66671977ada18708e064433c44458cabde84c2449c2f72614b362f1955481006671213e7745690782dea77844608ee0b223c956c4eeee0d57d04956679895da6748e20815cf10319ca38e1998fa4749f4ec6486f1b18ab95b0a1cc217e6223c50842c8fce95b006edb84cb4a801055357613edaa642ed14be4ccfdb7d6098b6a94c99eaf003e13a5186ddab8c387bdd28b1d4c7cfc1bab430383be007a64a1bf08abcdb73874dcfe7c435964a8c3c468187167868365d50a2f03dfefc5511b6fb093573b77cf87bbbd9cfd847312dde97cb1e5492fed7b60a52d44889c8af0408301c3a2dab28011a65c2e87b2217996a4e160d622553df19129c7c279b7a6a2c990c3d8f3b3a635b95c74588b7b3ea11820dc12c593e2019e0bdab5a6484c26938567727c2f1fe0843ae0423595e9b697df10a6ce46ca0cbdc05f86c28246e61999f7cd41cf8a218a0c75b060a711d7dfc247baef8802ba1cf9fae4c225493abc9892a3dca33243029f254ea05506ca068917397acbeefffd2bfa4f87032cee319613d0be3470aa183431509a1ad004ee78d223c80b4441dbfe3e05fe68a6820aebe3805118e269e6c71da25896f9c2778b642a570a765b005ae3709d52cfd5a7bb140a2ecb15a381a8143c45b443cc355d3f022935f984ce452cb0bc098744a91a47f62747cdf88c9dd67521d50dc539ab04457dca0d7dea6750e1077270b51137d37fe50c5e687179fc86c138ccd49bf90583f7a6598869534a49bd1856782d7d93dd6713f2f3e9f240644db4c76e684b34859d7b1fa418235c4e2d3fa068de93d6b5401fd84e1bef6e271a3a719973eaaf8cd91bddf109256b308fc015319c581d874f35ab47c3c44dcf04f849746545b968583aa4526eb233f2266ba29d075775fd2084151f79fc91b9657c17839c784384e9013fe3afd384bd098e364fa75c5c43d4e7eda6f8fd425eed8e2762ea5624383817d5972996cbea05bf7cc00fbf219eebde7e9c89d5f3836d0d28d33e1b6b4a0f8867cbef1a21cef483d3272fc08125408fc173c86a5e3179b57b56c918bcf8443aecd345bdae0a2c4b1d6ab24de388ca1e314a8fb6f876771dcfaa98544710641e817483638c77e2ca34587441ccfe064461d6481b8036dcff5eed8829229114d329d8f40b50bd03b26e4da738f7eb2afce72b00007cb0297917c509e945db4c52451e38878579b69a1ad0187ef76aacc8c548ead1212c7098046198fb6961592755ca4faa018dd3a1a2c1704d2e6aaafde000f45cc9c5413907e4bd29ed778484cece3d41b8ed845d341f24e6939f41acf48cb6f36bab296262e5dc298cc03214446f53366a80d8a5e0969a05da876e2041a70274082e894fdd09039c90ea6311ed135d399336a04948261402421b30df2721096864830d52986d5b9e2183b8267b840d60e5a30ae0b5400431966009083810cf0d5763005a95ad80325bbb5ea34e567154def40d1956590767ef41b1beb0d9a894dd23e278e70debe7a8a02baebab91a2890e12349d8c35795343cc57de41ef31079a4cc94c0e845c160cf3d016f748a03ed204413d6074bd4018297d51ad56d7a061e462de21ed884d8ef492d64ee3fdfa6733581876a077118c560de57dd0dfb105e92f4c12abe8addf45bee96ee8baf2428bd78832d608f3f2838390c0849569f6b74ee96e40d2452edd4c423e23f961ba6e9a29c98c257a2f0e1ee588b077b47510aa1a362d1a14bd4142c04887032c12a08859a15ffaa6cee5dd35d9a3325d00a1b25b0a2d180c3ffc89a0b286a5556efcff643344be66fb4d3a9b240f67940ea5b522b7c1370c15fd195ca2df85a61831211808f0b7025689458753583956273f4f3fd8c28f60068ee1ab81c0003877b4ec977feb44bfc5f7c451dfb060f1ffab34ef70fda776aa74f06acd45bf9b9bb276635ead6f25f92e1f79aba445ff75e41cc8cba003b8077f64905aa1561d7428fdf03c1a1ee0a62561da016061a8787d5d653e7b01cf04a1fd1fb549c3c84d84d4ff8a715618e200b28c13f727f73edea6dbab461061cb3001a56128797c620a71b4c36b90bb5e8d2bb181bdd13afdf1ca5e66f87d817008e98107c57ee587e84c359f7abb171636076b8c25631c8d907fa6d25a192a85c10825fadcf36f05bea5dd9c4d0cf2439f0a426528117a24ab57007301f4e49281f101bf97cc3349c3968edcbaa01f7c96c5be052b9f48ff59f7f68516b981cb135e29b52ca803eb16f6e30b4e7feba9e4ce8c0d4b19586a8b81ffdbaaf8f175f31a5a2dfeb53030dfb70cc479da02ca634e35330066911374c7427b02c9799de12dae203d61a4a20fae26efb69f577c66a7423e1a0f13c9bdcb4c7633b302d20eceedcbae1b0522b3edb7ac0617c2a13a37cdaa9eac446cd1a3712caa7239544f1eee9d23cf42996027322035970ec22b32821575117c2e8449030090ab1aa61a954c0b42cbe500b1816b4246e20a32205afbd40b8cdecd10de1606012438be4fe2f6d2545932466b2cb8b93fb406c5144584224604dc0a4f9dab9257985bc8b9f25ad1eacfd737e3b604f379528de2a7c7e7c3ffd48cef6b78fdcd1eee62379b32406220896d46044b6821d1f0b434a3af31f669fa32efd614ee8984872dc6a058cc3a53195613847a04cbefe899d6540b116a8cbe5979e82e509d6a9db56a09b2aa60f4d330e676365dab51cbc1e82e04f50105cbc627f60e5fdee81a897fc0e5cbe420e54b11f2fbc42c31f169b125c56ccf59cbe2efd8323586a8fea4bd93cf3daa45c6a3d24545fc4f1bd64ff3cee92f1913897a48ce3b7a46b206a49dda1ce92e8bdb4fa1243404b53899f9df77ca80c162bac62788415c0504871eb029b1d642dffa4f892b9be4eb061642a832dab10a672adae7c24952985c5daed43ad397742d82bc6b13871f1a8da35e358c7d8a1b071c8cf30708712f3e2e7adba40acdfd09f30959b78bc1ca737b9066ddd9888126ffe0304925a805c9a7ce04b703397c237b6355c0047cb4552dc888fe0e31bd57dacaf8f21b2443e85799fea4045d809dc921805ae3ed04a6f5663a7a86d6ecdc6507eb01707b6d3f6ab61c1638a9db5568521adaa73d67735878cfcbe2c9ca3d140f1ad81dc4a913b37f82a93e361ac5afa96542c3e88337fd7cf49a5ea1acb7d4d8babfef792cb880afd3aa4477eeb1a2bd6ab5341b4a6ce598f7026658c00a8efe9dc4e8275e9ea784698210ef173893e5d5c6f8fea7931b4e8584b9ef41575458067076120dcaf636d15c648b21a604e66f4c67ee022fc69c0c82c3527500c47793856d7703474df967076f8ed24ba9154d45dfd414f1752fd400b4478106d04e75cb468bee32a2ac9ed1b408c2cdec5487cef4b7ac814c13e1985e5a44194114dad2fb003b9e6c8da39f5186f2d730d4157fa1f92d41785f226a23441ae43b5a3833269b6e33255981c45ccedd3af73c4ffa87e50903827da4f7d8967eafd789bb82be1efdf13c3be78ff75121cc1cab68ef712f91ebed8dcc58406ba4cdf88a74b6137194ce9391194715e5050669aa6c68c58d152fbb911e897c18b423da60ee8dc40e9c4676cb4a0d687dc5b4621f98714e320df5572a94869e2f188854a931969ac34d3946ac4a2a71a6108285563439504ab6ac4aa63d528c36bd578b898935523cd1c920259d3ab710660c9007dcd1a4b0bec19ceb935463d264a396567d7b8deaff1e08ffe89991c85fdf4bcf76f4a710dcad9c75d88cdcbad87534228f6a1d3dce3948e452c0cbb89ed9e620e1a4197b1fcc7291be9724ac5c13fb647f8c6cc9abd7ac4979408b26c659e77af53be723a4634544f699898874b6720a12fb06e93de58c270255612a3d3a871c396918ef1321175442cd0e44017e555b09c1a4bc86fd1ec629e0e13ee9cbd601db149c61801344d55ac5bc9156524a0258c22c854c4a1b2aab84245cd2de9a54a84a1fa992b124538dc0ae15d44dcadcb18d37146bcb8613e31db9a0848d24c5d1d8c00c7713ccbb876124d1deaaac51d859cad98ee96710a72ad04a01777b47eab8b11016aaf4cbd38682e1ece984d2d060071bcf53cef31a5b28a2c2638ab223053744eaefca4c895e1499380791ebb7bd154c4b9d464513004ae338154fde448b67c0755cb67ae7d1eeacf8228db452f5582eefe0aba6aa1384c4e7f650999c8a2eb622f27e3e14f7c66cbf1118d6e8b8e8f9df82374c8c75ab13d53137a8f4c02b5e46a18d2af6c822b39b3ed0e388d9b010709d25b55e130552149d2013b2334af614a947e45990ac417584283e0e20c10d1b27c3f13df47e4461ea1a71fd45875d6a9131821355d3279c498321fbe3b8fdbd5dae6511ec7aaa37224bddfcb913aa8361f984a0920bcc6675c6a608e0dbfd656192b4ec150baf301ebce1872e050c4e597d61647fff8d66a8b0f908786e37bf4cdf716d6db718592df2fd80d42a9e28b6df0443c42227aa6c4f1853d128ee863feddec4992c1650cdeebd02683382f8d678a13beb5ec48427c23ecfe8a4d341a12559d6d181b86fd28fe6b674aee5bc1a13c132f652df1d3c9f4426f64fef970c5934ce5b0f68a0be9a110f52639463e823679905a749d71e62efce31cc386559120f226cfe62a4099eab7cb1bfad13a4ade2134537736f8034c0a0e20360a693630375815fe24425af1cac7bf4708825456e784cd3a1e5522ecf25a9639d304b11b44bb61b4fcda6148d24e9ace44720ab482ad90160764f959d9c6238a332c21d8c197e9a0a1ef183260badb4c7fe86768c6466a325aa14734b51af0df8419e35b55826d40741c44c55d5582d80b9ab61611569540bcdd8e837dbaaa746274f3b8a7feaab2166751038759558e648064ae934e2f569606e4fb87130dbe19ff71232010263a2792aa5407277da883d3e1cd1a3da1288f0bb77bbeb27aa18396730b567ec8e133836192b5b80a57c327370c7c9e1f1c03e9a91ae13e2bde7ab9462f15f00eb2d61c877b0f9e7ec108eaccb786cbd6979e089427f2561d976337ba4f4ad5868bcb95f000c0919664e8f50aed6fb5752f465a76e1ccb2068efb2a127c7c1c1c9acac109edce24fdc427ec0db328787bae14622e2611e76387d693370409525e7dce42d4f610b209258600b3ae3ebc9d5c358bac8d98605934a5600532e833ea5c5360a0817b537724d8bee104c606c31b6d2a012b99702f7852c5d010e01d3040dca86daa03336f7dd48461cd63686acd438535ba3d3c52dd481d9e35ba73074a73f7f97ac327d9936205abd9b2a915de79d1f1310ba565bbb23503d4e14903d10fddeb74ff05db00b0a789355d5b425e099bc07bccb9f3267ca3e33446947bde0a52c3222143761b169d507ad97117438a697e7b7b8a4b9c4293552fa2dfc73d0351777b71c1f4cb116b56d3a1a92ee27b6afdf83cc132ce1721006c2dd58c1997f139b82aa9fa002b2cdea7b47046620c3ebe2aa91ac80ad677fa633dcba274d2e7d0812b7e80d59e765ccbbe2a4fb45c14f00a1f78bdd71ce9b2d68a733d970254d107aceff4c77a9645e9a4cfa10357fc00ab3dedb8967d559e68b928e0153eb8fecf01999dac3fec74d7ca260e4d44bc2e9f0c2e090c6de614f0b15a2b20da4bb563c6454c0eae4aaa3ec00aaff79a235dd65a71aee752802afa80f59dfe58cfb2289df43974e08a1f60b5a71dd7b2afca132d1705bcc2075eef3547baacb5e25ccfa50055f481fa5f3ed87c62f964d1bbd437733811627a3972507968b8315181c6d55861f13ea585331263f0f15549d54056b0bed31feb5916a5933e870e5cf103acf6b4e35af65579a2e5a28057f8c0ebbde64897b5569cebb914a08a3e50ffcb079b4f2c9f2c7a97fa660e27424c2f470e2a0f0d37262ad0b81a2b2cdea7b47046620c3ebe2aa91ac80ad677fa633dcba274d2e7d0812b7e80d59e765ccbbe2a4fb45c14f00a1f5cffe780cc4ed61f76ba6b65138726225e974f0697048636730af858ad1510eda5da31e32226075725551f6085d77bcd912e6bad38d7732940157da0fe970f369f583e59f42ef5cd1c4e84985e8e1c541e1a6e4c54a071355658bc4f69e18cc4187c7c5552359015ea7ff960f389e59345ef52dfcce14488e9e5c841e5a1e1c644051a5763e53f7f4605967a86b8db7f2d54ae1aca3a13848cc9ff5e26fc5653bcb0e836471934246725aec2c9d9f723ff5250c1d86de758eb959792f53213676e93440ef915cfcbb2f88d8beed4678cc661939b4232f4d774d503ad4fe1e7a79aecd6746791102751c283ee864739d7c689b526cfb892ab55b05ca8fabbc9c30e36138ae7a2a08911a3d3e008b212ef203850ec0ca84f121a358be45560a90870ced6a7b329b8d5f3130f306db5d7f4b45c27f694a48b05ebf41062e80264ec463b441a9d623f872f4048374583cebcdbbe8000b28a4e688bc3816eec3e593db64dbf0782f692b078738c355f5b70778f71ba5d7d0aa5944309fd0254d32faba07d1ac581166c46dda2a180ea39aeac5e34aa729dec4805ce3063d926d5607bdcdbfe7a51bb8f1f6997ebcaf8811240b194b887dbb8070d1770301ae921876a93cfce4e36cbc10f2cc74342fa18077cb6d4efc3c06689752a25aa23630722c1d141fb800a9f0af7d393a12149e730ebcae9f5fd5aaf22b22709a7a8adfd6da6a8b8b8b30dec8bdf7d1ddbabdff7341fba89e2655de630975225780f38405e2dd92983b01e962a255e7c710159ed6cccd4c054b11a410f7cdfb09e5f3e5d76ac85db5a97531f558aec9e2811cf1db85bbf4779e8bb8c16c47b29df7c84522f24d2bd27732b11f46bd91006bc7e95778dd1dcaf2faf3169fc75820893d15f9f63300bf737aaaed1c95f7e2d362e4c2582a70d453fa2ca6ea323cacc60dd80875f36e623e0702bea35250afbacd3da95ce47f59437b9ddb1c0b04824dde93996c6b96c21966523a465ef67b90f5e3f40be8d62a75e7891c438009e9bc302bfe023638824eb81293b09a6742107383653d947bd848dce29fe9aaa51c19923207c205b92c1a15709e5e31cfc400208f048a94c295c29f9bd033b68e653bbcf6edbd2ae956c1f4a701fcadd2769a6d00e52a89e5491c229a0f2013850b66b04865f7555458145bcfad4542451930b76908f5ec81e3a7ab9b8cfbb6f811bb0a5a0c325aa8c6ff431d8e0991c433e0fbd4093588b32bce42339bc4cbd847829206a2a6a2edb478c1dd8cfe20b697d93992c6860aa199dcbb93599888c5bd7c9c30cc51319a1090d684307a18d0df7289b6fd667653a720460b81f48e2aa663d074210555e8da05f112f8df61af7f19aad42eb8d417cb685656b53e1d3519107defcaa4a6a24a1577e2437bfad92cebe1bec3a16182029c402899a0ffa8a084454c79001cd6ced891844100bd174aed2297a1150b51e43d927fe6d64626017288f7783e545a88062534af4591385acd8a69b48ff0651f1e57f7467045a696130b8ccf60018281f351a7326442de4b72251439750d301006285b2a477344325b6b81f6fc0a9d47b031f50d2e096af0055464d6061aefe61272e8b14c60fc8b6210e04581f44073ffd30501988eeb76b1c93bf2a5a18fa6c0f8fe2c7e169848fde7d6493d2e2d1495bde93673f740f4418f5ae332778312000dfe9916fa9ee13f5d3937b9727f3ce6006559f05678f5cf4ec94e7b96ae659a3f2f854f564c1f4ac177a1e359136a183a8cd3ca92ee598500cef80cdce8229e06ca1c989dbc2197fcef170fe4014984fd54ced2b673342ee170c376ad37a1983554f10ef3c8973e8e54b0d9faa891f4b3de4fec02f3e79c5c3c2370d8d240cdaa0918eec0d8cfc70c3211ac131aae2b018a12aba716a93dda791dfe016445ec0a0d23c384ffed6a58bc5408c3ee2de7278031dae6ec1d2d982a207a4fcc9f36e19777e105b8acdf6fa0a975f53a41277e23fb7a3c7d67192aa01bdb037c5eaed2049b633e7b4f347a8852665a6d7e2fdc98abcad01f05094ea9fea0fb8d442d9bb1e8236b5b00b7bd2281a6debcc80cb6b5a826640e5b4afc10ca83a5f3c3b530be7f9c543d2a98585f36267137b6271d3843a011a75f42c91a73d4b9d05e5c7c62d96f51a556797b0f39f5740d36bd5483f010d35acf3cbac13ff41211ad53abbdc5be7f8739ded4b0e9ae3072e0d8cc76d08dd20b093700b3b736e40e5de46dd59840d195e260fb5ba6672a3903fdb571599cca9d788871873a9ab3c85f8d28c8c78a93675ffbfbff312ebfc19519e7959e7079d45ec32baf1c14c24e64a7b19a3b49a8cfde6aa21c75c136719837c1f2a7b6188785ec8b506ca1d49dffae9e68637a3e7bf2bae36da2c57d143aa6c365fe2dbed4f458f256a106921099c898963eff3b8470ce65f2b3d300e77fa6e4a5439449e0e68b8a1ddad5608b434a387cc340539614077810572b7e5ddae6c903888fb27c790de26f13ef440afe61a66cf6aff59443825510db93ec21cc20e8b8a321c4557f6cbb0fda207a3f047a42f77983537f5745e15c1e89dcfac80da89acbf14c4fd1ddfa0747893108cae8eed6af22fb3032ed860c3e5e383a7d2b490fc7ba89e245d3d2d88834bed679c20b58779b0fd0cf02b286886f4e04ff895b37adc0db049c7f16fa16f8ac8fb7f8c8db8bca582adb3aff1e4e6d741e5121c2e337199043492e3e6b4c2a8a1bedac120a3f64e636b09a74645ddcf1834dc4d7db71c00b5ca509816c2fa2caa1166ad252daf6912101dc2365614b1612bb98f4d0ab48d06b77b0b5d082ca5e3f1c508e78d003bc370d821652df4eda49505fef8a662d7870eda1a04f3674345a521b0a2d1483f533275973d2a454f0c392ceac28e31f635f717d3ecb31db734da55f59f9e4202a1da31720edfcbb2e416a15b8b307e3f5b3381a48a11439966480ef01956d50e05bf485cabbae61aa80df0ff292b0f2e0484d1f4ce9a483acba580588b6a46716acfea886f79e4035dc7405aa5f82fa53c266f04d339f1625a8812c4c4124bf81ca2eae2d8ad89b0a3e307f8334e593f50c62089cc854ccb100f74a921899b91411140e3191033d3c9766766dbe7750cea74d3310c8d95ddfccabc02c7c5fff278f1c5e5c082e34aff787f22c850e320b2ed98598e69d11f856366e164be2f168d4121b2a8f607ccf7a479825c7134bf6a9546482c89e0c3cfc42021b973da107900304abf08006335de82bea2f618e8d832a85cfdd513e60da859416e158ef58a789030a3cbe40f97857c84577499c5682a185cf00de6a4ff8d4c60b930b765ce1f967c4fa3e337e540f4913033f2eabf0e1fe6ac88edd68ada5bac5932609d21ec04d2ce755f5f5c49568a334d5b32ebd0cd30a83c0569206d4741b794130fbc9f8a6bc00db7c80755138b433acf8a6c5f53f70195479c5531ab6df990ba65a587c9b86fd0700ea1d32064b2464407e18dd604a6e1419ce1aad74cf47afc9799c5a3401f97f16bb08a7fc00db7fa062d69eb1d329927966952b69073a443b8cadbe75460a8b2e1d68f2e6add4ce7bb0b237a68281fd1766646b13c44d88939093c74d48c624a86b4825d53b0eaa4501a40e6da837c5d012e5e1d38029d9583d6f382bb7713af87ad039c4e01626da84eceb635007cced026127586da1d00dc6ac5c732eab7fbec0e48d53e3d99417e8237a06f32dc44946181cd400e6f15e3c40a706d1f41861cfe16833479a8e63cf35fcec37f37d37cb92a0ad446cdc35bb54eaf9fd879f78abc01c4d2941621bc87372c146870a0efd32df604b5d5447512f123406085af571163208af18d6006c3b0f3a9c88c2144cd75dc9d649426dd20f3f9606d67e118ab6387c125ef8bde37df0d12484dd1409793ab428b1536c73167eb6ab45fdb6096a8731a69e53cd3a5a8072796ac59a438a5820c112370464edf98256a6cbbb0d2fd369be8e252b017e3719e57a7d4d04b6ad3a55d57fd652106449a60a03b34a2f21e1c9f89daad270f9a22e85f927f5a6e8beb65d452ee6a4974de927b6f7bf8d45b4a3d32e136e2592978ea0c3203fca58bdde3330584306fb0402c9aad3be9d68ec9f05acb9e6feb71fac984f73daa7c1ffe0c41257100d77e10510883d8724db57a29d2606392a332cf8895b43ef540d5f19e0c288109e1e377bf561dd67f2e0474a4bbf42c024001c76db8f1b7e153b20eedc67f0e3986503f37fe0c5195e7e1445bac60a7e31ce62187675677d33e3eb1b6a4a216d8c75921ad58c15237e54ba522670cef8ae2f490afbcf204a2634a843e6ad43c3accfcdcdea683427000c2f02ee2e71d42a3298dab2357aa1697b0f222872fdc61499e611702180a0e6f5bda8c3ada403a69c8e3fc8f70fc1a313f5a01f8d35ee89b21ea7f7a4844e414d2d2f9b3dd84d1450c0dec45ecd16a788e9d2a8d0e7b0774fb18d2b30b3aa70e14103705b89e20c812a09a770afcc76922952ccf6ac14826d06fc32fb2258a616aaab895885a9a587ef1d71d22c37ae257377cabd77f5f5db095d1a006a96e5030757d8ba51612bf077aa1c127be0222d0492e9eac9d54202c974ad2ed4da5557a7888bed54aa4a60abfa21b3575fbc5898dd79ad8cf6ebc808777e251e01a4979cc13883ca0c50658bd9d9428e2958eafe5bc8c54e7bac5eb4847ccb84ea2f11aee30a8da0663edfc8892b3ab528a529d92a4402eac340d7d9d057bd34c1fa16f54e0d38f94c8d625c96305d89d5e423de05e78fc4ef11875dbed05ae8cf20dac68210c343c69710781d2fb33bf67228e5e26202f83b07a9a6bde1c122084844a08df22efb6e083b1090e1c01355d6c8521dbb0e0fe603825f7f621c17ed2f560937aef022224ee6228a4947d981f49e13060b04ad163c6831724174a68e57c0ed744b26b4ec7e67b8c58d7e80130b1f083a90e8bfa00d75cc09b3c1424273b2fa59d9f05cce062a87452780115611681d01845aa6898bc41adbe11c7d157631e37d9ae16e3adf20339c79c7528f28b22318dcfc0b8d18197637daf0855410cc037b4c1002519eaa8e8155f5c0aaab387a7346a76e0dde92cc052a8cab8042d3768db45afac78697e958fe1583f182a309408e98736d8c03416992623014715962891755f31b35a387cc40c175a1160eec6c340e410fd9435ed9e84a28758203ccb0060a9472358e8b53144d38d773fbf55b4c2aa9b45bdd7512d7de9f9e0fc98dbd35562518b850bbd0bb4219bff832056ad0f90a3b9fc2c7b093c4d3becef0f4264a540a371554cea7f790a3b28bbdb6302a177b8c2181e09c5a842c3bd504e7eaf3f08f41722cc947de28ade01608625d2ff1080ac2e115e200b159bb7b2c80dd037d5ae2c37e4188843703cf38da5c28bd2439e81d10f94c81344b86854a74c908c769cb53ae28bf23ff0184214450d6bad4718ce37257440b172f0133e4a2db3629f7ccde186195186f32ea47e19c75db9a6d1b99c5dd467ae4ed3474106c29c00d48f3da62e46a06508b10fc479b442077adf68e33597b3b2baef3e00da08a03b9f3def11dfc02c070c18f0d61804da56cefe6e394e409163edf73907fdda1c21ff9c7e5cfb6ec07644b0d71d04633b112c975d1f42b3fcedf9ed453a75acbdf9a828db57fd284c2640eeb976ccb12cc3028bd8f3ab25751a3dbaba2f9fb26405977ae30d2f7e5c3bbba26de88c28dff31657b9ecff047a7036687ea04efab82de6c10999ad65030f7568c63d511700805a8ceb5441ce94c92183ec14e4226b0c34acd7dcbcac7070f9ca2ee5e4018423f41ddbba1d7046674fec79cc94d470f459c35d77609622130e6c6f101274244409b211fc6cac2bb0778440c6998b13736a21d1f0f905d82e6b6e036ef9fe742e8053fc2f15b6620e7ea8a6dabc9e340ee3e9dd88fc4381ad854758abdd480c58866fdd88758c2b698daf5111ef67cc59ea6816034257ecf81d2009248e0d81eb34c3ccf6ca40ec89d7bde6123f7f9b83e832a1e919a0cb0c523942ef9d096e16d7d9714e7f08dead8e5853e631124c023a5e99da36ace6de2901aa49b94cb5a01deceed19a6d6d76a4549b96bf1217227ee5063fd1748839f5e9e61036b916775da0617d4f762a000a1825ce2e938affcedfe9774001d0ff1b4e60e5b98d2073b37a38bbbf67617d0df21d1f17b1d63c730c07d5679c82677e76794e675fc674a7331add765874ea32474c1a16d1c09382bd848e93eab9eb5a01836721c82811a037f330e5cc11e838453d5a32a0831d17b8a94dde27210a025db458201ce24f832675b1fbf71bd84787f7c846e3f62212b04e5b3894a8c92f5dab241186f51e3b958b4f309421e35bc287e937a4b8a93d89db9673386069d6a23017661bb99987ddea1c6cc8eafb58177ab3dcd3902bcfeec81703c8af6b5042884ef0bcdd9183658dabf912d07421a6f200ac34c8cc16b539025addde5aa02e7bae349a79b01ddf69bc8ad528b3f953ac6de0808fe946100ec6b658f555765c4d3f0f36dc35f20123abc7ebf5b461180e30bd21c65f58c9378c1a3429f78ec1d54a356907aa6d61f7a0d2638200f4257ddb799c96cfed6913456e95201421dff067d2077dfdf4417beaef34ce6781073411bba1684f465d9ec43ecb69b2629cdf04442393f93e6b05d170399e63f34f3ffda703cd39302b563395ce40285b11ac7704ced03be053e44e808e9cdb07560e81a45d055a54c60a2953f237aa157b685e67936d7a44003b8d75eb178c2c3678d943c19e19009ea2e46d81745603d58758257c2b7c997ff9d82d9d9003ca274a45a2c4041198f7d22c6fee1878a2865e5e3751afc640e1da112bf284b59b3456a21d51c7c677ff43a6c5b428a4b546b4933886c4e8efc284af5e4a8eb7abc4dd22876940a49d94b5659e98ffc7a302fa0ab6d7fbae2ba1d589ba4da7bfb28e087afe06f4ca9cb18ee8f67becce9bfafae3325ffa99846e62c33638d34f18ad0f1d44fa4384655b859bc2ffa62390033dbe2301b025c187340fabba37a00dca5b7808f1b09f8eb99d6d536955efcac370f2b235078c8e78f6af7b2e92fe36b978223a5688c2f055cc9f6769a0e08ba895e4a9031890f31633d9e54b58b754d1f7627617f02d052a7e9749a9682ecb54da79268147d001fce4cadb76ae2e6160969840083daf30b1efa28bc9a40d8aef64bf23ba7dde063ff8aca1275310e735fbbde6a3a01c0a7273803fa5093c74772d117d75cb1c03dbb010e5939a2c945716fe455634bbcfb0bee6ed39ba01652b607eb936022452e209f3d0568bb22d12494cbbfc1b5cc4ca2b1a39162c608dca8532d0d4078771f5c1bd4aa1463bd46e56b3058acb13dec4e3be22a3226b64d770ade759c324fdfa4a1231fb5b7e232072573da28100667644dc39366f4ee995d7fa96c30c595a648bf2b0157dde760efe0fa01e7120422815249c36169ac6f8932629ccc343508cd26bfc31b4520f98998639133fcd4c3a4b747acdd92f90e0c8e0a052f6904b1d92f29163693032070e174e008855ffd16c5e6e9c1e6e72e33a24ddb608ab0d60d555a81e9f6019d099efed1a99fad9479d3b6de81d7fe0488e442e1bead09054505b9a114bcfdddc5b8f25ba11c8fa63754863c5d22cb7f7eac11dc17438151779b3385f2fe012eaaf403d497a1aaa83f4c65bfb7f3749b0e357b45c4b9e25de22ea6730b1d1b428c0fcb3adc63f6df19095ad270981c7b3cbfd5c7502fc05767e99fcaf0aaa9d90b262fd2028d95cf0008020c86bbde4aa900979f29d18514e5dac38197a46c469ce593b45397d40be60facb9d2a34897f4fbfe6c89d4294750c5fd6addf1a0ea20a77ec75713498d418c3675588900e454ae7865f79b1f1b3c791182ef84b3157611cbbff667162982c993cd5514e590d328713bb1d85ce960794064395ccc314ed5acf1facc3c69ad24a24afd3487e26b5a31144ca08382ba55e5ac9a15e5fd1af627116bdf3d60c8758b194d22e85b6cbc0daa3b1606a1236b1944bc348e892e79268ee3208a5781f60bd1268ada18583aca033ae87713f64d86437fbda16ac02edebba3b4b9b4ef32f8b6341e2d4f6d17d49dd24857a14bcfe1d85701e3138e86e71b1777d61a2d659f49f23f36356e839ddf1ccf86118a8c221298a408480caa521c635540259aef9050177b9033f0a9f30d20f0a34a260b4be45fea55f62fb8312e4f28b0a2fd7517f099caa33f52aadfb6fe291960af597b8b999a8e0e1168b7b12b57a477abd02b8b23bb762a6067b8a6cc57b6a4b0da3583569a0e28f45e17bc8997ba7dcd5335c7e4a739f4945ad094872a0ddc6b4265e21ec44f0949a12625f051f8044a8cd40972bb0a32cf1e420dc620d056530790195cade34d7b8a86f6e6982b2493018c5d1c213513e1a56ce1d4b1859779d0eeec03f9190661e0cb298133d623b796f17f47ec8d58cad2ce05ee7e17b676ff3666a760d1666481543479ae3a68258c130b75110392a0d43c27100f3b9a232416449d7a11be4316cfc52a636b88b05891e645b877aa9f653165537eb6619452e18d54b35309bd0b5c8ae155565e85261b6169386a3ddb934247fa647c48154f02358008903b64f04534912f6f950d763fb606cb1c50e40f5e8dd5885b94af898daa666378da148fa24d28a85e6a462ec92902f7a3ae13096e58d74a528d35ff5f4a9fba7c57fa7e3c0b4441d9d1e5f4b7fe4acb82220cde64d288a8a219bb847d15dc7d0df7c0e1d408d1a50e9b2a6469e5379ac5897ee98999d4c7ff2d98b60717bec349c5737b7e926beeb029bd597fb43ba959b6da5da3f9ebee30d873917d2576d4bc1a6fc3f92c4e2c275a272654b642b616a107d118d48af2c774f85e7fa42a0eefbf2c97b86b38b20795fe87511f16ecf98e697a2e48b405feacab52960f33854c5b34abcf4159f7a89ca35c98de9f92449fa6db94dd6237c3b011604b80a47fccc0cb4bb639f37c126be8ba281ed0c408367af805977fa0018a88d78826996d9b94e0db692ae4081e62f938d4237b5eb888c4f8bc2591593c864d3b31163416e3481faa6d2a300488b4f56df6979ba90f16b456ff2572f4235935ab4fa39ff2f5303afd49bc32789b4d1e5799c299d3d90b9ab40b1c38b3b9d4592a2860912c328c8e514ffc0e5698228ccd5983427d87e4519f7fcb27d64143f3877c3e24fbfc6acf0169a35b7bbe5cd192e2a91d68bbf27749157c7751369a9f3f8b1ffa2c37f3de52e45aef92ae2266540987700e3dc6a7dfcc8b2211b1c1a085b0cfbfedfe80e1caf4756419b982eee91af58dd5327bae7eb8fa690cde97b4f1c58e971b04ab9c885baf7212285a0b4aef200d9aac35d19d4c2a23105305d8aa9ac95ddf23e7ef20e9d86a1eac727292257db7752ba071a430ec77d0c3fccb03008f20a6b0cbd81f8ed8a703f0ee5c24ac4f6d2d5b4edf52b378b11cb30efd134e30c6575db02ddbaa762d546ddee956a9997ab9a744b3f94ff72a01177fbe3ede6db85c7070ce00c9e69d2f877670571090a5dee02f16d7ad0bcbaa60e875c2622036321c9a48f81e27cf7a3f61b5ad160126a26490d1d5feca1cb1ea915e19f824b45dcac3fdfb19e433aa2768550115eb3b3cc4643c295b7092e559abd50a2f717fb17e62da5551ebe760615edf430d6fb25139371da090f478bd600f4e32c7fe1d6302ab44ff2f31e4749aee41f7f6d87f5d6274be83fd319bc045bf38c349ced0db6ce66c6e8f2ba7a0e0bba78346f9208d6284c01ffdee4bd0ad9ac6f55063ad9064fb92e1b39c2479bf16d8d2e9d4edbd2f363c5142e40fcbbdff9dee57807059d355e60f63802727f0883ee09c433dc2bd3b1b27e3d12855963f6df7e7a1fc5cacb68cbcb9101989d7041bf138496bf86235c7b41bdb094d3f5e7fa0570a69d2c59b04f34c39e335b5f9c886b6117934c7beaa4f766f17db01cf00031213c0685e1c3aefee35424004a73250180ab5eed2893c9f8af800b710ca6e63775e03cf5e944eb2088e2d1545122e7fe7c3f6ffeaef039b3885a1e83fa1a8e0e5e27e835730f3bf2948c9518ba5df08432548c70fc9545369964c85ec84d9e061bb6e4d1d607d20d5dc0bcae2260ab9aa01c9f86e03e145912be8b2d8b18646dab1eed1a831276ae34ef317eaceb03cb7cf2c9c71ff341f8c0df5a20e0bf1cdce7f1f1139f7ead07a7b22abcc0951ec5ecfaa0f50995a90801e168071f8c219d5b8965f718ea5a7d71f2b83028ed446dc9bdf980fcf686a98a7da488f2cb635b0d89efe0f8f105d217bc72246822fba4b601315e2d2bab45de92f9ceb65201b1a69c5ab34088daa1e60364e55972f54e5daa7c006ddc732994202cf1a841ccaad27b7165d61062f26abaf2e3479eec7ee334a25c526a5946e7936eeba03f0c42692be04b29d9fc20df07ee775af981ea0b3739b9753add7e091801959e1200791ff82eed2e700d4f0151123448c5a71f04766ac431b55031725ce618269cd0a3ef83373f2d56c87e960ebd1e006191f8fbedf30430a26685663c766f9070c65f2d06f859751fb8396de5c47d1a58465eac2305dbe5d2a55424d530b7331d9e5960aca1cc5d3f8c03ad2e947e825ea9da3f937018a8587d667e00b5cd7c8a740618ff4f8e8c43584c111a986e1575d3858cd8b3090451f9fbf26069f10e1337a63d6b0102c03ad1da936c6e1b3621cba016a0cae63630117930a8349ba1ae1fb30ca610bcb959f60ef28f5f6e761a17b2fd0bc3d5d9a30d474f747e850fdf361ade5f74a6dae058007666585443eaf858c99d33d85488b53635506df929b40210679357967bfa39f3ac8594a5a77d561b2e0f347ffd8b7ea896913b7c1d5379d820f3fa3216683c611ffcadc001b9f5f7c161fad169d2c693970e559c87cd6e2b72c4aea58c03ca22b6c7b91d6b1a47654902adeabcd5cf6ac76b4c560c58d017f5d821b7abe21f78db8a705dd126e2e2e50f43be3cb4824284c13592c6b20d09064a28a132644732c0c37a59d6ca80f5e5668c2029e208fb043c81d4cf40b9297549f76f8730a37f24bf272ff0700a7e17d16ef2ae55983483cca59d66b0f255a1ed0ed4186acb21bf7041eb2db085561c42132801f093d705d4c4d2367f7286025761d2a304e499b703a7c599bd29bc2669d04cc2187e7c595037d015834cd41dacda32b55d8448b2c5979595f43930aab7200cae19f9d97cb5a89509866488da1f17b8d65b85699f943aa7f47660c946df04173367e12c5847f8cca58ba78427281fc2780a5e1fad2dc2bead183c834ce5f3cb9f40063d0cfae7727245646ee36eb347c77c9b7d65acfa9028d46e7790c4e0b565260d669d80f58a96ce0b77b22049e8ddfecb0fcd67ab3faf8aef979467e10904e706fa4ff3d6ce59415d15ea994a555fe44807f3f0b01f92c118348705da1f6daeef86c1bdf25d317d0f0a08fa5e217a9217a76080f1f8e9bf78d6aa64575c237baeea12c491bf9644ad833005a5727eccf57f419f609a93fffb99fe451fdc4d6a7cd81dfe468fd0678b06bcd4250aecdc92ee1762904522ce222ae850841f21b40bf5f0e14468466ba2783e1fc5b2c8fb548b23eeb31d2ad96b92f452bed4f818e8cb406eaecaa8c98c88d1bbc534e5f4b76dcd2352faf819f6f96df30140491a971de2a23467bb6fc3b77b590b2de2e468992c39fb5315548bd159c5d7310e9f49da122aaeeb2f7982755ec09b44b90ff4767a0c674455cddb467a8a898a3f460eb8baa826ba834004d0636105d2ae594435a2bb5084e061ae6ab5a397893b94ab0abccc3260d2d2e41cadff8c6743732c6b35e76ed67d54fbe37f256af1bd7b64e6d19d6df187c3b063839e4ae698d98d8ffba03477483461d287348656c2df11c93936aed88d819e0145291aae2df0e6f989ff2287742c0068817ad67d8e901f068931b3a052849b426aaf04653a548aadd81e2dede22afb3824500043680653a0651dcd9b812a2442e6a1fdfd000c70d5b84aa8fd66538709ae38f17026d6f41e31cb1423627a7a1e73f4f137dea6d813d72d40dcb7f47d2e5cc666f45a107e52c458ec308636689a397a873856664c0ddce8d207533c19b97f85b95d98e69258691a48b2a2ac8d37a02020b03863a80d365fb3faf59f5e8aee6bd733a1b5103cd55744c6f7627a1d74278ecbe05413cd47ad006f2842a9ba906e17af17b532d03726611b797006633132d1da86fe46091a8e2969301ec8de8d43e3f3337d2e9a65b812a8467d30a999a0945adeb35069b4f067a5b4f364e1f937b5d20bf4a49d91cd937e7b97e82b52acd0da959ba7a81a00ab520bdb9566a86cb0f5b9039589d9bd68cf44f4ccd8a2dd917ef76b530acfbc53586267c888ddbc6f0b6b1ef07387f0810afda43ba06b9ef4ded20643b9a242ee1426b7219a9983259315ca2c24dcf0e7c15a48c71cde13d0d4bb1380ca13074647d56b9e86af7dd2b8a6bb520108d3905814374e765c57ca3eac32bad383b5454106cbcef614b02c4ca4824a2d12b1c2c905760ed9b4d20120c472ae99e742eb9ec1ba4a8e8bea3653f784b9b39564158dfff163a6326de2b24ecc74cbf9b14841df10cde47815f8b3cc09f0bd23f238cfc1c205f5cf617608ba790f9b783cb0079c43b238e89e4598ffad3b64227f7404805edc9aada58bba2f641e60540e714063521fc7d78cc3dbef193db6c1017952db1e70116abc01641954fff0d697b8600a12b1571c9d8290d64754c648fe50e0be41a7bfc7a4f691a9c1ec55824ed610b6f3bce1c3827e20bef117c8edc0e3fa81a7674d22a3d9946d8fde412f8fa9476ea15afe70ed65ae0d3a7e18061f7ac1754178ca12cc6a8b8cde65c7791678a87ea80a635257c4fbf0ce2e5be48ffe43d827af2b184a734e3154d0cd91c32fa8547138b1a0b1f0a2b1144523c10e75a989f418742c86d8b7caeccbc02c94f411cfbd2e563e84caab71cb50570ed673bfc4ce4fbba72b492ba63bbc82c0fe8460ee24ab14379ae927437d3e5b410cb01dd1ed590bd836f8502dd48c990618274aa39586331cf1c457d7a00f0d59a0780b5a652628ec3f787110e603862ccc30d828750a1637c720c1404f24a44faeff7d079429bdf214c802f527f14ac0d36ebdf1bed1c9ae78b4aedbac82f73c4a10a530168b24670801bafe2cf770d78ac316d8a05c2f6c84a213376e3fd8a9efb1de313a8101910a4412e1e7576d630954928f1d4209854f40a8477d65bd4ac730291488acb127504b44fc4fbfc1a7654acefc6a4f2066e66cfd3b1684512a0dba98e8fc99d73c357e5bbf6863b3ac156807a9aff99b34463c392e282a01bcec42d0344c9b0551b8c1edb538b97e83d26001165da7d8d141e6d3494ff6d009d50725dd54133c6c25058a2b611d576692ee2b63c0771e9f0400c944d7459a061858a5ed477c59a1b12b8d4e6d2378c711c0ab6f538aaea99dfc682a849f27b553b5ca139547a359b9c17878056c1acb62e6d09a72a9c7e83c73ddac93f72d7718d83cd441dc43c92379fd8fec9f369db74d20d0c8c17831a0fdb4163e1ab237c0d1c2eb4c259ad24e8740c1870b5ac20d1df7342b104bb36e99184eff5c787348a704884a03d3b789114922a5eb15c13b042d194df13440aee0e5017c9ee8f7a33f05d41da6f81a2dc3fb9faabba0d13b30983b12be0b6a78b89fb2eb31cb6317e443829c16c1ba3c26d7936ec84de4878b9b0e8f27daf15037170e838c624e8a3f836f9e06b76c44b8151159c5d50dc06e323e741d0665b544ec1e763fe8c17866b5f89086415f760630c666340b8b1a92c41a813177f96fa602f0c540e667bd745c476018c4cf56daa9a360908816d8fa54b10416436c3ac5123ef6455ddd0da899427173e26d9dc5609ac944d1421384f568ad67be10ef6d07340fe644d00236c24c68a69b01263ef32e63aa3046c09fce533dde600c037eca4346aeee56065475660ac02c2ecf46518b3cbdf41b1ad27e27bb4129cb1cf5a052d3e61c7a7d668455f90d2a483cc95c644ea1f55d5fa75009da88275776a402fddd46eb3da3611e8cd0faa1d89d3d254d97e9485119ec603ad5539cbf0c817dde61e60d1aeedb4990a878e885b105c74d261b986b7c82377c26b2ed5b2207bc3876c431a742fb0a0c72c832af04644c53c6bdf2c90b04dc7ed58b22e79c6ebd25a5039905d5ca2fe4fcda193b2a93b48813e70c50b116fe6099d8a444e77371aa6309db97a2a2bb55fb050379d3d5289bc63fc020d2080fd8af56a36b057b8bb09a28e6b3cde1470df4f392cbdc8afe63fb2c948f80e3b7f7f435cf0f6596fc706dd89288ac714e0b7ef75eef9642f1ac96d4032aac6a9d6a3f9f0eac5bd220d37b0691fb3be586cfdc1020d762f47d9a718b291e0345353c93d4da9b51a822567ca88c3e33c0acde75d9b546cf4d67515eb4f2b0b1654f77ae96abeac24bff92cb9f6b6747f13623dbab70a9a659bbed322a39c9cdce93446f1ab49b7537bf23b5b5b5613e0bad31507f3b3168b442c607fb0dbe7cf5d3710be9bc6a12c054e4c49c01a3145a85fd7af4bfb6dc633b019abe881f2ab0411569363a68749b8e3acfba475b85f003b88c1cb5b6e044dd29dd034f15f75cfc8b04638e620bd66f72423700892040c656f2246185d4c5d04638d3b017309df39c670a47ca8941c8e3339f19cf43abe160bacbf8d173ec4e9554bc3ec145fa4b4871660a478c55e926c9096a993bb4a660c414a621b3d8565758e925d2d0ab574baedf7756f117bd6492089cd058601f9f2346769f1e123ec3880a9ed562d39cf36212ab53ddd478dd5e4be0aa7a4af6da9a1e90c9760f7976c2346e3569a1c0aa17711b66400b6a4208bad751693b87c3b7929ec0c058433f4b4a91780c7377239214f5a07cbb5642c1a792491780d97162af43d5927506771e6013680d8153fb4c93e7adad0fe3a79b807641e3694dd000e29078734ffe50a6bb06bff25cdeb64290c420b5b76a6ae72110a4caeabde753a2efc3ad32173c0bf60b84a935b5def787aed08d2f4ec75bcaec4b1025860d1c7367ee08271daf49764a62f12f6686eb01881b94e83a22f5826957f62582ed14e4d19c007cef8e312186b5d1bd966f59c8a65a56adffc526d1a06028d28c5dbe291ca0a9de084725e5f262ee3dd615b4b0a797a5d813750a325e7abc571cfa84e72d7e96ec198aa28ee9efc3d01383c7cc7aee117b4394eed94bc3d1f57abd2381e2987ac0189f0baa0e23a631846839bdaddb697e4ae5e79dff770c49a9636223fa8dbe8b2703bd730604af7a437520781fc9ecf3f2c77890ac9d3709fa12b99858d5785e38092ba965122d8e071a4750b2bc2e253b08c902304780f8040acfd01a2afe0f4000a4eebc4da7e838a158f255021a29f855a738f5b43ca7ece0342e8a4259bd2431a2265bb46970e16f6b72e37cb4534b6e13c8c136cd54631b8666075bb7a6b7e7f834800eb380c6dd030198a6a442ed974697271fc06011d1468d098b93c0f2327f6a030569367a83f9824e292a7bb3e3d5baa53bc408c3b70583464dc8023704b4dd3650be1b9201008cbfc50f94d958d5d2641c0ee6a1eb30dbd035f69add83b64025aecd8158051a3c5c3b1163e1b40b89333ee14c27ad4c10e8db4e1eb5844e5a644ac6daee1a876e627d7163a6e941b59ff6260a429bf01cdff912f1f9a1b56ce89cad8905746e3eb73aaa33fabdba582e49cff51e1c690c6203c246b02ada9baa0d58f2fdb367fa710b982ac0d146af92dc85a367e241ada40cf593c99c989b285de88b810a44b0a0e45fc59da34ec0a808a2634e618a29e715f5345539d55edeaf7db4c754693d29462201b9e3950858d7fc4394840ff04fe63a75def7fa1f464817b223e0e67a1bd483cb9006649ae4581b509c06e378072d98bc9c3b5a7c01071878a01048706085320b1e9ee6836137bee51d018105a7da87384407619a52c806a66e5618bc0097560c0dfb7adbe1091ee4eddbc6b61d2ce06650540c299f4d46f667d5f83098b9c022505a57059421ced88a485fc03a7826823dc5300a7a78bcf37d069478384472cac0c02cd67c38f87dc160078091913e08f5abbc7fa55b648a3b2643530cdedcb9d05da5ec6d365d5e0483a044065bf1810b5ef89ba25dc6971ec41e343e23bed627e3292b1199b46b3f98c9516923c338804e5a525e828b684924ead05b030a2e26fb5600ff486068056c654b3b0f70d8b6bff66c0ee32f7503342aa0a5cd2b7f5fcc81c4a3f46a9e2dd40495709ddab81478a5f001206b28cfacb49195be0f4140025e0a7b6375d4ba245f9a9a80c53f97d1070e50b376a1413c4add6c649c4e0bbaa4aca39ec930e482ff1ea36a0757cc6c20673bb352944a1a27696a6da2726e94134f36720473092cb6f0324cce185b324ac229de4d2ab14a70a13739a1c890bdf6ba1c7e35b5229449330722e35c299366d43af6d40e566f9a7fea1cc9892128dff49806755e06d93a26f1a988ea5cb7167933d0c7e96a116a47bb21af17399d5c51b5cbb4418b60ca89d776af3249d20df60f38f43af381c12a30e323636a2429359aee60a16161983e76a22cbb5825d1687cbe64add3f817cbfd01e1504b4ac3c79d8aba682bb63acfb4b5f4cb627b74a7c89bb7c07e38f8c61ae2db674f710f3d1a1356e2a3aa4089398e86611cef0117821cbd38488a308da53193173de6c601d38b891b825ed244fef6c6b1f3a1dfd86e4c00109ba0cc9d3482813188f657da66e85f45549d4188fb919e86f89fee77eba431220ae6e9360a28471aea75dd9da2892bddff49636f9481347afcc12d69a15fe20c5e95e6b017512705181f1451098a77c07ea85877b85fa6fb4e06af6af660ff859ebeea6280d147613d21bdbe91f1d34d2c6b06f2bd469451d60522ec448f18d4bd8ab700e621488398163960255bfe5f561c101d1556697962111b482298ea895f9d7be0fb84fe5fa2b3709195ec99db1482cb3caaf94dc4f5881dda33134a70400fda97e3cd5a453559cb3f207c50b91162bd3903ef4a333cdeaa752801f347f090d4434c1b97a75fae63074ea4b1e5cd93e599138855c899aa6ceac918662bb584d8cf06211cec2b927b2918c2827ce6653ecc6d81b5f49ee1c753858c2c8e382810a58504603d08c4b88b78c23ce5ecb98d740a5ca5180a480224d1baf97b2bfeebeff0b2adce55446955ce0b1b9073e84753da60556901ce6cd6ffbca803ad585a42c3532c1b494a2c1f8b6adac0627e6eb1b7823f98106486da9cb96608bf9f2709eb88cd38d1210a955006ca2c3d83bbd833a85ccf5f0221f2f9afbf8c907044f213da16122190059c4a7ec8df7d26898047895154b125ac7eb430ceb58bcff24b58eb1431bf000305720c323e6f97887966a04556fe73b5e3b8d05ceead3dc8c712bc387083c96754210fd50a2820d686d62a4df6d29d1de784ac040d84bfe48cce80f0c3466b01fa177dc7b8efaebe4e569bebf780cfcaa66f254c15f23e265d630c76b5cae029de0b9c963a8e01d2800924837b989b32a5245bf31e2ca92de55894c4982e30d5188082baad63235a7cf573642c78476bfaa08d3703d7a17251b123b4dd10595e20f09b5bde5429791bd49ef044e7f1b28a3badfa987ff5e94eb2be9ad6b783a7ebabde0477a5169561c5930e1b375616d122eb3be7188c93232cb9faf4158e59be2122552b05a547fd1e9fbbee673ce39ebe979a635038ee22fad68e3e8003d247d0d217349bbe45e0c828d04b9fe392be99da14d0d773ecf3a55e7dc6591fe8d953eb23bd456b5d01a5f8177f7bf80dfa330d773cf13ec9f8f9fec9c31d66a3e7963fbe7717f4f8093d290ebdd744cfa9456ff44b6ac7fc64952bd096efbedecfb74d53a7f79025bfd0d577fae6e35a7cd2de7b8fb5e707b2c6c49f53d037989fa7b3978f225f9f6f1c743d279e37d43075e14f84defbec6d7d6c3b9fc41bdf0bfa9e57d495c4d82f1f7bd04c65cc35f231dd5b276051ef9bd84dd2f33c57c0f133f28d128225e91b8b9cc8a7fbaecbf8293e1cf2123ec93400002024b59e4d071f319a9ea6cb4cc59b200e2bfa2345177eb845f058747b106d275c4a34ff2714a4ac79da48f00bf1cb78bb56909ca2e6b6b57c9362a19f013fdf840feadbe3d3410993cb0429405f1f7b312c9ab256c1a2ac3d5d380a296122f450b7ebbe10f8500860bf7db4bfb50bbff0922899484cbd414fd35806b3e13416e5ae57ce4c04dc54d0830791e0e049335a3a9e4146de2cc13187a03b983ed4e8b79cc1a131026d0ebd6a8fe551ff694e680c4153fd17a83c58bd3f2e60c96a1563ce86bae7d23f1eb74dfd7605b0146faaf3adf8bc689773e167018e3398726209e210f70038cfe426977a503bf4edc9dff8a5e638cc737da1fb9ea583c5d3f80ffd61177094d2d99d3bd4443b6b70779477b081971bf194913725f3940819dd02127a93a9c721f63adcfb037cd08be1137d575aabae3f91dbd937f2fcaf05f29db61899d47e2dfdfdec3f5802cc2b03aa4414c59a3c827a6bf5aa056fc51509613df4e4c2196b34b41c45db71f2f745ac5c998f8a7deb5ceb2eba2c1eee9ead180048df1542f8df08c3e2ed535cccbe20455c167c44f19ca9a4f37af397abed1e63a43468d0150571995d0ec208f46dadd92cc023487bb1b4cecf216d3660ba8e7391bc236d9825e8e07c25d2c41a1f10393c26cd0969303738e4b54e89101750354eb982e09251756353550eaca2e63eb2c1c69b5506108effe1b062e51a30d68d276d08c3b806ea66e3a11bb94b060f80aab2b06a3c319f79483606766ea936aa11ac4837ee9b349bc19129bb8f0de9d7c6cfba62189f6a6c4b8ba8fe28c6165b34d0c456fea1e8a3c7d60c7647b34f0e808ab67240031fd81c8725c125982191eee72f178703ca31cc8911be7de3e05e7e00cac3eb6e837337a4787947ab810d56afe8f1af14a3647a517b7c6e3341495011b637c6cd91c1da47214517e6a70ac3af547512d1a31af805c950ab60c663b5681721df8447f5054baaa65b841228544a42ae41ea382c98de95ac6ac8d89b6aaf40b86cda5f041b1522232d3048b645c1372dc878b8e2a14185f7b48c49ecc59cf9454cf64b410663a59911f056b402d17d2f36d4a82e723bbcbac5806d62c792952482e32cbe4e8385871fdfc8ccd452bab7d8556e81df77ae34f0cf84fc7b553ea4c54b61a55705c2e2fd3d868bac900d97a502278c8ac301330e4fc8b589097e0a9d96237afe58b27ac6d3dc32017471b058ad0c1929146d1f68126c3124d40cd3b8ce89fe4a826b6dfa671e669d491f33bc1e6bb4e13481d67a8793e1f54a50e2ca0dae1356fd446236792d4ca131af19ad30fc62ee3e8d7bc6f410ee1cc26a4c1d73cedd9d608fd189a082c8645026244c998da88ba496269c159ecaad3880aa5e9cacdcc83a5e7c7109904645422e81bf47f03b8f6bc510ebddd9c8d1a905b4d864258d44617bf4598851534395f3654e9f2abff9055c6bf2bc8f6a098d459d030fdaf25e95fe6ef12b62554a2766433508378154a1f159802414d4801a6ab48123a88b1b9f0adbd9508c282835264371303e86514c1c3445b9a241272e7d2322695ac9759a6980962127045b3626ca59d4b890930f4ff15be4e1fcac590be4240e757d6893b284d73065a13776fd288843a0198dfddc34091ff4ed733d6d01b89db5d44ca61a81c06b7788bd745e195d02c2d35d5183bc4a4e715d2e9bf5c1888935e22eced9f7f9e176f01349771adda6be0acbf68435f760be957521fcec2ee9bd3703e366e03f21a3072a96d033a81eeee238f0546b742d3dd7be9d95ed0db79216a9864ea59c3b3076aede77e30a8b3e71a316d0d8e5d996d93773def1f23868cfc88286c723aaab41e9920c94888008229a576abd018a00feeee37853e4a3700c1fd21171f7c2d227977ee01a0cde8d9866b77b3399f5bcb758e6fc620bcb9ef35644c66764a2fcb9a80a1e06d2de29fecb081dcb9815dd0f4a59901b9df463103bef6866398c31604f727d843b19575178d8dfdf2d38d6aa7be7d881fd617132d687f975e1121b357cd9452465a6d2c93eb18b57421e0b14513baf8466420c9b38a85bf424dfa4e582ddc1dcc3d053eb54792d1c635d708147e4b50710b5eff6e0b28d4cd18a71adb0b386a73fbc5f9d4273f5d3d1e56503a0d60a7adbd003d9eecf0871a45776aa00f745e9aba05c3ee95bec6946c3f605630f8e5042de83e0be9c7e538d22dd184baf71da5264e08e553491a9489ea7d7f23dea95b1275d5c4418a01f923ea2a89fc6b52568a580f295fda99857cce4ee5caefa5753ca1710ebd4ae06cd2095c16b2bceafa17ca04313710f2f433cc1bf1059b8a52c0f5b1b4881038da545012e7ea469f066bbe465994bf525f88b37335669ea782b1e05a9b92fdbe4080e710aa226a91df45ba69020db5096169b32f4c469d4c0f12654f5be93ed7c7a6c96e585e2b897855b20e093b28bf4a079bdbb9e66e91ac4ae811300c9a0f3e521efe42a338c8703cd76982034eb189acbc62361af7405c3f61e36fc07962b3b3833d3046ed77a4582b8a51a848bc6572e68cfdb9309955e7f42de508692872c4926b8e69a7187ab71062b89079cc7e74b0bc0e8bde1e30deeb70da141db6f31772488ead129a706424ffbc4e68f8c2d36b5e8b9ae2b31077735fa67540e405e08b46433d7d451b1436b107400600b958c280f64df11bfdda356c9a3bf6147706657468fb8beaf06904ec9951431a4b9af90b89a20f53a903ef547f01d89e420f1ca7e7c2235329c75a4190167afa4c7a8e39069d2983bd6be9eb223643337ae0aac186aa77f5e7cd717a98e994c4e269850ec9ed070f1df74ec7ffd53fa254be061afa26903cfcd5fb1d2bda3886ffe7f03cc5dbf58a067313a5d843e01056a640f8547d67e8f092247991eadaa7dfeb64a12d5d06a42708e5c7f87e3a6cd6c33980c3a8bfd03814fd2ccac3c2e0fc4e3c04d7ec7dca8c88be153b5eb0b4e559a212bf49e206538423eef4ae41f0f6b3182c0a58c2c1b7de41fb9da84b17f25732a34df14b805a242ca0917983c1e364dec1274dd4f2cd6e132a0ebb5972e9530e490a6be7d3deb16ef07f04df5bbaf79c0908fcb9664392540bea87d7e73d3c8ccc2e1cd577ab68eb7f293cd8c302968b50444b1ff0ee97fd8845e58b184fb4721f6ef46b121d2db06ebdb4bbec5447a8682fdb22f6df2094001889313d9fa7e4090f2338db0efdff06f510b9d291408878882c6f57431987cc5a931430e6f24942e1a60fa6f9dc2ba0c1c40a8f15ec56724a6ef955dfb9252077a19661e41380c6bb4ef435094b8e9314862a2a251525ad82e6002c48544d01828ba32496a3edf791b7b225e6ae16746df1a98e596d607c720ed1e1c78732b43e10aad657965cc8c502edc3b4ce932f093af16db9a588a8c97342848f27adbf6492ae6b4919ab617c22191071ad022f4abdfd361e9b843cd95207c166539f68831e33f202c343d86ff0e3930615ba522b73d89ed29b4c6ae20764221f519419a54b453c4d1276500e94d0117c5997739d01ade73b6f1b6225c82481d94a0880bee5d2a9118d14295abea10d0a7ae4a760e48b74c1b314083a29522748258d7dfdc350eed5fd96b70f6fee62de6ecf8ca6b69c879b65a52bf11a0b4e92389c612c0ac25fa16e53081f622af84f2942208ad1edaf6799b7e4fd73e255fdef41264261dfc6f0cd1484e7e5999756208bd905ff3ba8f55fbba1571b45021ff178e44ee6439ed601feb37cba3f4d7bf22b3dd25037fd8fa26484004d3aba2883c6ad00b82060119e815c61342a04fa15a43cf3c748dfa3aa587f175105c75c20403291c8e1e75f05eb291a38d0de6921c93af5f0542e6e2c6fd09e71b7a86ac8920d5d4e2eae19dba90c7b0c466f34dbfe3f12841e104bc9ea159626a0b5ffca717bf0cca474d536868c8d7cb22725ab186900ac40589691cbcc22dc96e1f8154220d3dcb186a244b14934d49b10885773a9b3ebd7396a05f5c2bca700bb5649e7a3b3b97a0cb5cd77b5b0311cdac2a20346975a8c4add1dba2ea0b7a6b8a40b3f5120f5ea159eb04786efde4d65118fc086f4a5457bd0d0576f25dc047cebd70e51cf0eae4e96ce38c2bb0c931f7f460cd9642e57275e77e4366368627677ab19c30fe671a019049f705f93f50d77f322113c9c3112336167dcbc89ebbe9d1262a8de74ee34250a586e1dcd53d29742ce3e6b014b4372a953731f3168505354bd01e21e65a25356ba495de44e0507a784a8fa07b23524ae69a9da5c254f584a37db1f62b015dce38dd851f78fbb84d0c3d574eb981c740bd5147b64ccd06e1058ed8bdbbdde03fa58700a6b0068e8b41e5e873a9eda7ab3079dcec000ee19baf98ae52d19c9d412315a77531518d5a9c6978e36b6128788860671838717d7a8b91b0b3b1ee563b26f88518acaa9cbd1e71ec55563bf7744d46d7c302c78278b736998d93c25c93f55a2e0ea32f280b82a59a2b9cc161c65a507822012fffb6c3203072ba727081e54f1bbb6be3ad2e3e7ad5c4a5f3f0cdf5e103a10a74e187e86381c8a344ad6d8dc357c8c60de518e2150dd8610d19dca38461bca289d84b5e01b8443e77ba2a966f26f7c0aedfbc1e2a60c84dde040db9cd9af27c76c9c46615e912d6f097cdd4ae016031578668a388b3c4c808c9d8737a03c6a5fcb3bcecc886baf314be36dc049bd37d85c5f94e75f340b4621094f490e04ca55de39be99944767df5d262ed0899f3a137717ae986dba5bbeb2fe115d2f1b614e1d852ec915de1af4a731f5c0e37ec7a7987a2718798278df804356d85c0064ba127ef49b4347c34913ef0268d7cd8a7ce9ad7f98eb2b1235ec8959b9b0b6db6c69a79a1980c5607f824da17510b1d5a639242c597a4ea6149761b89d622a61c766412d806785f4eb27c8ca408317019decaba9111f1bd493231f6552a04b1acb774b3e5bb375f9342cdb50013817bcb3261fe7a09f9b6bc67b81de49238f5dbf7f65aa6b16d43539ed8353390d7a08d82df154e703ae40a48f222f64fcaabe8d85b80e0233efdedf2a3ab50dfdf2ff88f6415965e8bc941d7a332238f46f0640a681015b17b0debca49ff114c6561dd9279a6b0bf9295a40e24ddc6c8b91bc6c5fab96347318e6fc0159b9b0725da829e24d37c0f19398be8344838d06635fe9962fd5a38e713636bb204c0fc628addf3b84f5459478e513fd3f8205ed439a288526c95bf850cc166d1039688d10a3ed14fe0923e85192dddcd6fc131c31c058c10f4c3bcbdbc27651bd3f570da1faffa4399d4a501114744376100c2f5723784d44d46e2edacb9f0bbda283d64858050fcba7323b776896b118b20dc07ad610491d29410a421540dac6f2227ad232ee132a2f3ccfada0d25d0eb3cbef4c6ef7b68b357c632164bab0e584c89ebdb7570d5e223ed6e1531da2ac84f9c415539a487d0062e97abf7e44d8874675d327616e55954edc4e96eb2ae3ae1b0d37453dbf0c868d5d5c1b08b5e685a9a00467ad53bb94d2bf05159a4527ee922fde35553c74ae277d11dac9f9f2adaf18316231cd394db5c34c236adfffddbe67ad53f93e2953da2679f45d8dd2ef6aed170559b48a650d1ae683295f298dad7657a87ae49f7a420531d2d65664de6248c2cfc93bee46ca884e1c2e18d299f1c679729d328dbe2e554bcbaa48f4c01c7d79adb1636986604421450b0984397a4c252f57f1cab05ff38cd5cb8e4eae7cfc919c5dc74013f4b6b32227d91b708f81ec5427b6df1bf45375247e233e95d35e56b56388a771adc9ee2057a2998f69b6768e8b6c7cbe848c09d0a32a450737de423a1e298e05ccd1ebc9ac3114a2d708e48a21066d737012ac4312c9d5948f375cc996f67ba0ff9c7163b6296d45a938a38944f5675c4528feae9bb66734c41ebf336770c121c0529a410a08d03c8bb05b5f1841570ac56f3d15960ce7e18b82736417348c741af45bf00117d3f61d934f5a0b2ba065bf70bcf701a2379f5b4ab0911d9f622ae615f36bb85889aa6a9579df08670cf32642260276765b1efa3161e49e80b4d7c72619f9d65063a7a29b7ad9af18826c49d9bdd108401006b19f8fc1df20be2be1de6b1d481b68c8eebd7dc00b9740d7b110c1b9c282c1a4113d223991bcf9f2d1630c6141681f8d79e056e7b328a546c8da54477772879f705597c20857d6ef094996a20b873fa837fb15a80081ff11c7063f58f2032429498cfa1dcf664d006084170dad90eaaaaf1d908bb51b94935d87e3f4c1a0770988459fee53331d441e77430a19f7528591c5eb0a4ae39d7b37dbd87abfa402a4463c1f6dff8f3b0fbcaecd1ea153eff8f1727db6227fc374f68fbf2889de93844a1c54ae09420c6e64e453ffc80149c3d99ba98013fd8890d521b60861ddf5c8d7ef7f12191925bfaa5aa5cb7bbf145c1593349dd26c95fc5ce2c78771570a2ba9c05260a3f8db0dccf32220a47061231d6312a19457c5dc552b31090e156db4b987c6b0ec59cd6f2aa09f8be72edf59ee4ee87d481f092d7a20d9b06f309f98574ad1aab78a080b0b86e4b88a95a0b0de65b32d80c1565ba415960b1dfeb6c8a9ef01cce47ad7d503d69f56938a81b4577a1080646d5bf05a95f6fe80b9631117dba983a2099829bc7d3e8bebaf41e533ce76f465d327a8674302577a6c6c665114d8032c2cf0aa687b19911c8c3aa3bf29928a3c16fc80896ea15a9d188410c9834ba7d86a75621a0d13da8a2c84a30b1a881244e3fb249f0ee5460637617c018b4cd482198fe3f20c5f9d2ed059c03537ff118d02e9c1f6053361a506ce348a775eb55f4b8fff79112b4c9763a58fba07645fd942f4e31f4c50cd235f08fd05cbd6b25191f569272a121ceb70dd0f1822178fcf5500edc80a320dba4ca885e5df37c51a400b05951804fe61117c22119d1c3656e01d45b62a36c1397c55db796f8ae37fda39c18dddf9a37d411755cf0013b6c8ab0fe8e784f00ccf8620d4a1974d4567d3bccf54bd6687c344583d1ad7cb541b222959e3f7023c509e1bb64f1a688544156f5e3181828cd44661f07d472c39a049962dc240402a0270ba4eab9e39c89ded66dd4afd090a4a5feb4c5c3fccf460c86b773c194880e598c0dd6c73f0f81a6776faa6b0204c1fad554401369b70dec0a9e24036865e51e5ac3eb3d9d5b24558e5e46511dffd218aecab5dcc0cead763d5fd65e430cf1a37d4f4ee26de9d4eb723e60ed28ccf549b8a5f3187b9f3ea5710c2abc5679667a291bfb36aa52bc526d127ff641d767b3915476cdb0554d12ec1d2ba792c287ee53e2515c099d4f699e0755293a3baa339f2c67779ce26371ac91d58cbd110f42d79965f92b7fd5497add91e601e344e956604a3f4788b2453fabd97c05f488e0d50676e6b10be7f8e75c650df159300a31a4dadcf20f1204ae00867c25d4de1ae5e5b788413bc5963f09d8f7380971390e212f5d6c81a8da9bb15ba037dd4366606852c3b9622d573389adc304a6b85b60e91e565ad38f0c359a100a5a584393d3d57b9b4e2d2c940e8ac478412278797997e9709d3ab3928b0c906b9e69cb9262080853e1c8d0a856f9d3d571f9e48908e8a930a579a8c52314eb82e8d5e1e7662a4aefccb7c447328226aff9f4ca67e594ccbe4b38e0a6fc9c24459067b3cd839081e538b305723f4ebd37e38e73e571c9400bf9d1c73ae9101ed95a6c112ce330ad16e4a1933e510784684b31f86da0c98e335f1faea06964f9fdc8f109f14e89be56763c14b2833892d15bc97a44355adadcf0a624b73c69ff2a523d00b4caed5a22b198c3020d338ff7b5b079d227c074c7e3557ce0467b692aabd9112a6e4d2b2c8890f0e697838d629a6190a0f24dc74f4bf1d7437a2f10a6ed711437a79a763e4cdd3cba2fa8eb28acc2527b9c78ff9acb08e1072418ca9e1dfffda99bec6e0bdbdc8fd016edeefde8db653ebc30178dc5f8ceade90e1c091c5cb92e059bf22664775275c1ad81fe75e70f675fa528e1cb067f872bb0952add0ccaf385395583885c71139cbfb4b730280913baf92d706aa28067ff7b4cd3c3ea2e75dc42a6c9b6d1d0ea02cebb16361a8dbcfe9ebd73f901fccf040adf33c25520d65d491be1f5d7aef10f6581056dc186a4128d9a7d5b4f7b4ec5464bdaa95c7815ba2deb154e9093946a6cb502e0127fe986cc6b4fdfe86ad548fe595966f2aec2c47caf6ba7ae44f6b746ba0959da422b7b73cb8b0b4f0bc70c51272a4949a3f80aa5392037142ae42b08f7e9f455f395e94f3f03b74e534aede5cbea9ca44f29955452ddb9c964a25a13ea934a0d884a299d3399dea4692217e91395a2ce91fa92a252fc222464914751c75dd1b5a9fce5abce8776fc28df399977a8e2ab28857c954df11596e2ed50d70bdeeab600ee50282c80ae023fc15b99e08ebb095e0f77b9145f017593d4e49534a3d7e40f835b5e7de68fafb0186fc78fc078abfbf1e51770077201aeb4dff12bbc95e9e34b25b81395e00e6985d7235ae1688c236c9d546a798648537e92906a6053a6d1d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d05c147551173d72a74773a373a373a373a373a373a373a373a373a373a373a373a373a373a373a373a373a373a373a373a373a373a373a373a373a373a373a373a374a3d38fa60876fc79c4d18e6fca520b8f8b8ff3eff89afc62c79762fcc09d077774328cad24bb6085d74366e1e144b823bf4028db0b3bbe520d096a6a6c3061c3460c4bc410430da01a356894a0a1a191040d1a3390983143f5a352cd1c3133233302191919229021232606660c189854185229941828d4f665db4e60389d4c61984c180c8cb5254dbb5fb837fb22cbac17acad5e6ac5ba8061940b94ce2dec29a5d4c2eb1163ccc2ebe1ee58783de00fcda4a1b0a6599ba2b845b118dc9a3a7299d1dc1575a04ff4894ace196173201f2fc2d649156c7e84f847a28efd9814234fd4b15a2ac59baa958a52b04d146cf2670c1a944a5ce4c78f01b7fc6b704b7a7c4c89fe8cfe9e4352d7b68a3d2aed08dd9deae71c7d59e534c146b54a6fcec1230fa7089b4f39b4df0d26328c884c643442c5cb9c73ce095d62d0e7933edde7f439e79c73ce391f36b1515ab7397d7ead6ffe9c73ce39e974edb9730edda5ec629b0082920bb647eff3d594af6916da9e20cee3f0e12cf6d820d6da5ab57dfc36de12ca9759dea8e96bff3e0c1fcb5deec916f0f6b50fb34cbfff61bff2448bf1c48a65b899647006e13e1b9d1b31fc036318d5608c50ca5c717c35cbb28ce21fda6b4fb59b7f9278d67d1a0e23845aca85af4415618c576bf1a27b47d967b895d58b4d7cc36739becd1e730d3c9c938f651b9ccbd962838f278455014cb6bfa6f5c0209e69d9bf8771363dcc9c73d16b3817a3fc9eb73dfb37e9ce664b1d31635821841056582b849826fe83b807e21eed637e5bd352ee6b5fab9e51bc1ccdb778490f290503f9ee65c32124e741fc5d4ab813777c97af879c7bbe4f72f7e8da11734d97a579bae0ae33686eac38eb4fa32d40793feacf2db8a413aa23e3a37432e79c73ce396974f4a7cbb9a893645fe7633848f655573d839c8b3fe977ee7306f96a4e2717e31686decd4f923d0d0a728e3e9d4f27d5158a2dfbeda78b6edbd3ed793ccb343fb7dc3548fd4d7710ee5b357c1cdddbae7f210e12f7f6d78588637cd9f5a510718c3196d40d1f0e7d19b59a39e0bbfe7d55bf8743b574754ca493a0f9dd7caa839cd349e667b2cb5eba32e992aea8135df567746199b6afa43a483a19a2076e89cdbac4270b368899629cc35edb1896510cc35e9391b32c7bcd4a91f1769341b187c12d4ccfb0599ba5c890218320b4a3cb5752647cfce8240661693fa32028ed4eba60340c7b0ceba62ba599708f49462661300e736358889cbbd68905992fe79c271c44f6c8c7fe426d4693d8d70cfb194de64edb99e65c965d95265df283e25f2969a412d5c6249e317b48a5135fd160ffd2a5a71396c985fd1582e6ac39975dfa90e24a1fdb8ad2554f3d37ea279e534fd4cf2c1f02c11d6b53735a2d3578714fccd77f30ff64208437704bd60cb7647ecb18421a70060d0d1a1a8f42e94e8986d210b53d7e0a21fe8fc91dea339cc26f69ead7392bc401b76dc35abea59153afa21f1323374c43d4c36d460a8592337407231e82a2b4456d955bea65ea6bea9b54fc0eadc5a9b9c1c86c0fb747e5b7336e6d305a3a072337cc8c8621965ad65e6a52d77933ede11e49a5ee2c1eb2bd9c91f5537276be639ea66a4c0cfd6e0926a19438d88f791e4ffdbd9af64f0392dbb6750fb97b56dc23f3f5797c255f9352bed6a46a1fce6931f5a89cfaf91ad0ac5f67501635e35133348fa3ea867a38e6791c5567fcf6d27554d6feb432460b99d2a23ef534c87c3964be7c0d08eec89732f53215f7d498c7f090d4a4e9999f64d31d9c7888b6e7d3a464d4d4a4916ad12bf3b4d65afb31292da10b05f3aa2ce367327d1919d4f378955e5f563ddf7ea7444b6921566b34d9a35ebecc43ab53ffba29234bbfb131557e8a090db7689665b8e7dc2feb9610e6b34c2fc19660dd126ccf87401bf551befc1a03f331f36386c07c7d18547dd4a772c764a3b0257bfa6c2b65f6b40707c9b63a6b8ceee446bdcd44ec09a33526e00e148f0f5fa15ebb5a131ee734ad6df8e7c46ad69cb0698fafd4b29c42054694d2f56ea2ccd9325f48efda78cc9c1a5daae89a2ee99252da504929a518a55677f94d8cd23a25955352576af274812526212521c5f9cfa6db7694ce1961f38774d27f72734ed237e1fb8473fa43a9e39194aa0d618416b832ec16bb45165534bf41dae4436afdd57f1a53482ba41042e86e02ec51b0bddcc1a4188c12db0264c9371f7dd8c35e7d533fe9121f7938d1e30ceedbf7f46f3a5e00ecf1c71e5230369a20fed8fb3ffaf3ffe54efee03e1d5b0da2ec6754032739fb1905e195d4d5b92dd59de53aae46a8bb2dc69bdf5cd4812cf99bca68cbbf795b065080a77a46cf39c7fed5a84324c61a6bacbee95f22b56e9b148c8b3a44de8dfc5ab74d059bd834df6e0459b5cd572fc9716bab4e00d714534a299d26786e04641263fdc293c902f9dd128f790f5b5447fa9aafb443edd108c9a4d8cfe085b875a396d1b7ffe07361693f232e28a97c25c5fefc1a56a394d22e530cf764126b23542c2ad83fd5f60ce33085cc0d85cc5d2b74f78983c0f777c71aacfbd137e979297d2f348918d319fe841a9dd5085e4f1066b8a51c723fd34b7cc359ab5ee23bfbab93643f9554596bfd59ef09eeebb089740d0fb9bb5edc837dad4d5c433db3cc5e7b1008ee6415bafc99803bdafb12dfa721f1a43b9f5aabaf4413e20f7507278df6f26fad0f87d48740fa4278718f3fd49954a26d976f714ffd24a75a5dce2b5906a7869fd13765fc36dfc7f283b0d6aaeb6b42340de14f4bed4bdc937d120c6a3b63e241261cb7aea66519ccb409b1ecb444fb9be1eeb97fdf2918fefe57d76a99804cc0269e99c0d313b0e8653d6fcf3c1cfa4c98b1e1669e082ccbb22ccb52daa1b0bda22b4945457c459376f6f47ddca01aaaf11994eb834b881edfd967dfc1a19be5f9c4b66477f595683b7b228a803e0f710bfb0c671955c439d3677907e74c99bec139d3d32fea5f99650f87ae5d82dde7ae7eced9c7aef45596b1af36cb32ccd62ccbb2ecdea7f1e11cfdb783af34aaede01cf5c1571d9cb2e9532ddbf43cf46956ed4f274cf4b753d69ca3ef4ed84c0fa1c0285067534c0586a6bb0dbb1a53bd1b8ae56014c32cc51e8f8d15e5d4ba512eeaf0d83cfa188659ecafc56cc69aa6d98c5fcb40d8bf42586badceb256ebfdccda5bdf7e56df39ccc639cce3c322cd4d45d0c2cb3eed67a48514f643dd379b6c8fdefb3477ec3a1632638cee4ff3e37d7c04ecf83227799a26c80176fc03d00489638c01bb58c10bf2134cf7e91e638cdbf423b6a95f8331cea9242ec528837694414c26ac4fc0f7b21d6acfc697a094c86b463f024412124a2916e9a45a32a7af094310c2128e5064832934c19253903a478a8d6a1dd9c33f127532cdc51bf8524a2939a83b586d964a29a5941e4192740409162ed8c1c676c759b727a0c2e61b892f7227f1b36d5697a32848403ac215479002162e70c211998474040976440809291aa194124897a02e50a691d01196b14ae4e17225462564132578c0842248f1841596175c6820e7c532f7c3c205af24242245549d0d8dfd8ca4bcc05d4a29a59452ca5ab15ab35a6bcdaa75ac62f48b2dbecfe884bb75395be6d427e516a896524a29a59452ca9b65d9fdec667fb35aed575be5bc72aaae812df8c09c9c1a6a1134f41f8bca7ce9d2397f88df399963bf3d709187e3cf13fd7efc1a2f0ad531f17851f7c2e8afa5aea84c83c1a121b86dd74375a883ae5a9fc7a193d7033ad9554322a8c0d8b63054051d38147390fe4815638c9ccd9678036db2acb5fedba8bf40dcbf2ec4bdf7def75c75275128ecfd5e0cf5f71d156f4445faf2a7e0623fa329b2b813fb1951b9b2b140140af5b766215028140a8542bd670ce5140fb9ef28f928aae516690d6dd35d7cecefcc3cde7bfd33edc6d872ed6356a2daa76ce26143967d7e807440b1cd2b9bbbfbc04421a194521281014516c12d63d0e9ef918f79ec31bd3dd657e6f835dbc7f6310c8bf263263f6679a7c4b2af4f62768b2897b6e097735724114b94bc2487e2dc8ce2dc44d59a55999f6cf279a2120d26651972e1dcac1a6a716e6e714e4a1da340716e4e29667c4d8faf68fa2e067de4c2579a1740106d10e68338323f466b1f8a412798dcc5a46250cc8e4151071ee9930ebaf3bba8657814af8db162d895b9ca192516bfca39a38e49cec52e5b221751c020e7e013288bf6bbc124069a6670e73f9b0ebae615e8a3026373e8d01d3a74e8d0a143870e1d3a747777ce9df38a61980442fe9d42482925a63bf922fd29657cf933baf4e85086218ad2be27cfc1f67607a3ec30aaec67b474b479ec67b4e4d23cf131bb65c6a983a7729a126d0f83d3ca9d556c99dd02c21a5f8b134aecaf948b69ef2fd5aeb47fb394fa595ee2bb65f5bc346287d8dc58c6a6a4d3dd3fc97cfaf2e98c5309095fef11ab8f7d7c0c9352ffbdf79594faf06d20d51cf760da088d45853e1ceae00b56d9bee77b213177f015e57c52cad7f32e7c79cf2b352aaf4cddc1211ecec13fe299587c3fb3aa53edf778e27b3fa1f69e7c53bef752bef7b4ac99c68687afe043087fbeb60ebe12ed3a7e33c77f593e75fab26cd74f3bc27f7e620cfce29cffec60f3efe097ad677ca3200055d91e05c21215271eafb3e1c7bbf3700ec20dbd86af608435ee496ef82edddd1d4a97d21d42287586359c83bf6916346cb1c98fb3d3584ac50c903e470451ff4d8a416c3169c3fde73810eeb3e920fe976113bf992f359ce115bf990f4b4f1051624fb1d410c8b9b9e7852fb3d882903ad62022d4f095c5d671187f12cfc24f034619a56029a594d18b3da34c815bacc52dfb6ac52494724278058b73b3de373d8cf0c63799200563332d21c2b9f9f7067183983e4c88357cf9cd4bda7ce0b179f9eafe7ca9e00bce28b621e4869a73536cf2a304e33e4bab8cf56ada57ed4ac17fa7fc7aa5604cfdc88fe27b9628edb52a6925d56ab6d23eb5d6d2b7bae21eedb5a9c9c71acd566bce254569a1f64fa355b6fb4ab46d67c5aacd6ece2aab3558b336e77d4db36fa5977da52a26f9cd95f551ec049693de373dcd597d556327d809b883fdac28d40935e70943611886a9362d443ead3e843abdcc9a66fbec524a5ffec34de08efca92d7675d8b5ed84f98a87d818b97442e92b3f9bd8d3743536fd4ef6cc4f82d1ec7e6ae9d2a7a1bbed74b3ada8d65a6b3d690ff7d8c74d2c769299f0902b93b9fdaefa86799b3becda198cc5d94b6dc7a478735ffe53615ae2501f26264d9dcd37c9b8ef43f7effd19baabf76bc64de00efa5996cf136772ea65b2951193bb989b63fc5beb49bb73ce39757dd4cfd34fd4fd53bd9ae6f4f4ad133f7a17f530df79936d2fea377b3f096ade87d1a86edbd809b873b5b72653f62ca75386bb5a5ba73de5b7277602eed098a469dade979262aff92afbfa9bebe0af5a894ab5af490b997f2dc54ec01d54e31cbbc41bcd6698d5b223155b1793ee777e849db8f9ed13765d6b3f72b9274a35286352f4e2345d46402659cc23b836d3a34bf920c4fc3d3dc3864dd95e739dd7a54674f7d7e3de6a83c18ac7bdddc5bd1f8ecf82294ea49452c297923ea42dbea4704e0c4ef81e841305db94d3f6bc3da558d5cc0f90553d227c3527e5d980706ddae317824e1e1f40324a4d312ae7eea55f6ba514088ad9cc3e86518a515b316bbf52aa65866573764ca08b4227b38bd9f789983fe79c4e6cf73bd59c526c3490a0166a31bba453c36fe6cfa943e5dc9cd813617f5afbd5d2aa594cbf492955ed4955ef66523ae9a4934e3a298d94f2504aa976efd5ee6bd7dacc62b4528cce0ce225262c09a20b1390901029e203d2fbf8fff7c10384e71521212111e10182540489c812228240ea52a7431f2810420861ad7fb36c24be7a7fc4395884c7c70d72d6faded6f3fb6b709dfaf373301a36ccd28c52dd699a474ead5b86599e38710bcbe1f18a64cd3ccecd4702c4112fc2067ffea447dcec9fcd556131c6a83bcb45c543e0cbaac559314d4a29a3c681be76b166b5482079d281544b8cbe945b68b6affe7dab255a2e30ede3f3b8dd02eec0c7709471d3e46f76cbc6a4bc8ffda6837a08a5122112fb88699afbf5678c117bab05eec8c7f4b45bf6cc700f562106534db5664094527ab56a733c3d6adaa8bd70a83d84f1d6aae9635f35ac8560afd578b1ac69eecb9f9d92ed3b08b427fe4ef6c44f527754b2e9533665e93755774aa2ee34c0c46e61b7803b6fcf1de3dcd8c6ec167047e879b193c49fcf0b08e445148a8f4525dafc2a14759e16dafe53db2de08e18e9da8479729fcdfb220f4b1122ce41f964f3413ed9a02bdeb87625dcd86f99b3c7ba2addab2beadc20a2ced59ae260c3fe8873fe98460214751e104a7ce55dd5b69223d1fdfae0abfa6e8790bb833e49e477980c4273dd622651111f6a8c1ab502c2ba6463efc5a864acf61d2fe06d1c35edb5d7aece34938d69bb057c760bbb654b2617c2a48de53887e59eb7913c1ceca7945be10f5096af07dc32d8bab71489f05597b3b13a77ce0e40f8ca1ffb0b9d388761391bb3af445b698f7d0d3c7c05e14dc53887fa9b55cea1fe694dab7f65b5f5de0a0476b3ecabf6cfb92b9db3fa6aedbec6133b2635b3b57e9635205bebbd99aece699aa629c239ec1f74f90aeb8ec9c61767e8720e834ebc48e52e66a39e082c4e273614928b9a526cd8d3b05f1f22412d1babd57a94adcb893a9adf608f61183663b055ec8bb88fbdcdf48bed7ef6f6af5f591ffb1d7ca858c52a56b18ad55a91fe48f58a361e58ad58cdd918177588c88937d8d75ab3b7c9f23b574d18e37fa6c71988ecefe3373dfc4308ac336b6f9669d95ffbce551be7eab3c1741298a441d31086768c71cd94614a712bc37fbf8b663299b437695988394daf6521e69cd344dfe7fd89f57bbe54c3b98333ade94afe844dab5d955ce45391cf63fb34d11cc3d3bf1b548a4adbb67a398f7b13f42ebf6d7ca4d886f62eda959c8b153f7d932b79175fbdd8fd8eef3e3fce4588143f9bc982c595a494524a15c4d34c8d07e171a6a8e344783726ed479d1f757ed4f991ecb25d3b101e4efc2e5a61c797514a698a2dd84ea8e7712fce41dc33f32eb13cbdf84d97a2d7030bf4f9712ece2e148a8bfaecf854487bfa148aafe8d390d2459d380df2d5f3958dd7037f8c2a8f7e95eedbc8110bce7047a52faa6d8bdb7f297a38b18bec2245b09994dcf3e9b7dc45a33804cc1b4c4ade7719241cbaf86ae6695ebe631c54efb38be169fcd4dd0c2da76b1ad3d3985e6a9ac6a47f30d9a6976fbaa6d7bec7f4dad55ed3524bd367b9d3fef432dbf057f9cfd4d86cbca6b9efaffd7c9a69ee9b9ed25cfd83c9be6f7a0dd304b99f7dcffd0c679f691fc436691f638ae16d68fac5667a7f98bf18c50a88e1afe91f5e80490789e14d3a490c7f75921a5a0d21dbd3afa11f8dea250dd7dd8c999733efaa99f7199f6efaa9b9e9e9cf197fe95afa945d7ce53a521a9562971fdb5f7db7bff765e84be93388c7bccf2c334dba63b283d8707683b554ca7457bb9897f94f4964846c4164fe9a5e4646a3549bf493c97292f9f76ff6329b7e4ecdf4d264cab499efcbccbff3be8c39697ae64b2d657467c4be7ffad3cb681999bcbd77447610fbf451c957910b8dd4a7f412d9e397c62836e970fbe9976ca84ce99c74ce39279d94ce49a78f73d1579b5e22f746a16c3441361ae579fc9ab27429a594746e196e29e5944a01357f44103f7c3671909aaf7f6bce7a86ad26d7d4d4e8229508520eb9e68f640ac5dbe15f24db4c75c83402993a71ce7f0b639f82a2e95d47e7bd53d029a8550364f30faf4386c017c93e7ccd70e7c0fd35e5774e7746c039332cabe1446fa018c811865bb5e61b906b3e876c73113be4f7b89c6fad21006eddbb613e6a30cc535424d76f40ae9f43aed5e70745320fdf80ccc3e790b17de98fdff85bdce2e12f0f0fe3780ae4ab0c54a3e9c8f0cf573d7ccdd7e88e87bf99874c7d76b0e5efe84f4dcde7a8791cafe1961255abea9a9aafb5561c5f9395a876c691a98f7339ea5f1a362339bee6251e52f339f49c92e9175b8d91e79ec72f97e7949a7fdbc81b00b78ce8193423f80626bbe61f0e52c3e939a5e6fdb31e72e4f81ddcca9143e6d59093935b389ec7e94fe5e1fd29900e6ef1c0439629193e258336428543b571681b78f0784076c0670ed03f327c910c770332dc39647a376033963db06bdee32340fdec6a597e3ae41ddee6077c91fcb603f2033e874c7703f2db5783cf4df963083ccba65680986b73f1bfc43d64683534ac5215c90df81c72ec38b06bfcddd4c73848dd38fe62ad48cebe0139fb1cb24f215f25a97fdfdf01f5ef49bbf77ebdd9adb93536e0d6bd33a6981a13deb622397f0372fe1c32dcf903feb8ba71fcddf21b011f0e8ebf9863f049537e1e9fe22b1cda3f47edd6e839655f06649b27926ffc90ac3f8514752e5210dcb15b3c553fe847dc4f7dec16b8b32df974db45aa760b03fe4626f23a0ff9cf710fd1d28fc3c7e18e3c823b4e415c8e6d29fb5e40a6581e4e0fae781c0f76f070aebc9c23b2f270727083c70d4df1706c4003c8cd40e8e14889c1e360f0828703e5897350b0e0e1ac00058f737282876382269a3c1c122481c4c3f1c721d39f87e3cf68dfaff96adf5777f5ffbbfada3bfe51bfe68dd8f9a97f7e1b874c45f070fc6fc874040fc75f48a6473c1c7f05e4209902c9b42753261e8eff8f4c49f0e8130fc7df47a6ae87e39f804c4bf070fc59999a0072fe3bd31464aa8287e38f804c831e8eff01325dc1e3fc0d9069140fc7df864c5bf070fc773275c1c3f1ef916901328df270fc79642a45a6327838feab4ca53cce9f0099d6e0e1f8d790e99487e34f43a6541e8eff0c99da0072fe03c8948a87e39f93290e1e8eff8e4cab3c1c7f01649a83c7f9e364aa8387e3af23d300646ac5c3f10740a65768d1c3f18f0e45fdcfef5fb3cef68ac7bd278fe08e7da1bec39ceeaa57f170fc73e80cf78d6cad789c3f0eb9eb4ef60e93edf0af32ddcedeb58c87533f06194960626ac42849c1c880b9002a1593c2c086a24165e0b4bd3ecc1684697b7d1a2721b069330d517f06d6c0d54cda92ece2bb019b691907eac7d80e60dbeb775925826eafaffa885b17f3c09edbab8f12f403536e4f6282403e9cfad4c7919045c4ff09813f9cfa11b7f25f8f336a38f2db36d977fd24f5332bde154fe747864fe25932682af5ebbdf11207c9f28de7815b37f40c5bbd5171903af111e7fc7164b86d7c77355f3fe7ce889da4e6b3b7563c8b05b45abcd5bfbf45721dfac43f5314502720e77f2353251ee76fabf0b72fed3d2503e63bdf329def99d4cbf807f332313ff3bbe66faef92974a37beae3ab9a1bbabb5fbfbbafbd7e9ef839043a7746ecec2f161dca374df7795cc857af3b37023e9c8a15c0a4476e21de18fbfe14aaf1bf7a0a39e7738a7331e48eaa60fbd7c89d0c4dee6068e40eb53bead39db62a777677d4a7c376675f9d49c6ee2cf5e952bba33e7405fbeecebea80bb6ff491ed128b6bf467d8af0d5d518804744409f9bba1fae522c3c6251c1003cda738a5e128473fe5388e2a1ed43819c73da643bd5b2755348f3394500464026358c804cb00c2aa105153ef701fedb703a70147cb8b7d94e7b0b32f633ea828baddacfa80b2cbbfb1cb9056d06edc1580388981f734f23cc79e7a532fb80875493668b6022cef963f2f5a895be1ed8d4f9c178a3d90d1888dd4c9447bcf1c739e766afc7ec31ffe168788896f65693aa5de3b5ef6ca884a931b63f955143dda37b4aaa1d35102e65175fee6bb32cd3b497da4b2d88a6bd7635184dd33a24c7659a868768c5d7b07c55fb4ef5362ecca9c14d7ffe8c963232c6c4a86de8fcc7180fd1b28ffda6d5ef6c74e4c8bb53f0ed20813bba9881c7f94d1463fbc3f7c5f6b7c0eb818147027794cc903942f545296ad7ae96df1c426239eee71c0e627f6a1a2fda9fb1999561add666ac4ebfc249e73369f2dd9396f5c8e075194ab857d6e03936b478481a6dfe75701cc5626aa85455d36ab5f415452e3008e17cfaf4319b19b6f9f2f51022a49b1bbe4f29e5cb996d767c8923f220077b9148ccc209768c5f63a49452ec0926501475010a2e40780212134b5aa862473969d2a51c12a5f4664c3051a2891a308184164869810fbea08521cd39872149dd016043ce0777265541459744a43d62038a592de6943a2a1d818d4db1a022842c36a5540a1ddc112ca2f8b1cee5703ae491ee060cb438e79cdc4c3202912866949c0e8877c79db2c8911b112d8c2a362713929438429655c1920748910f204b43511675811da1414623911d908aec4085d76cc2842c62e4d1c205573a105db88e441d9e986404a74e440bd7098331c60806a478f3624624ea64fa48133f588209b250820a2e5040c2174c88555a2d7cb62805a478f36a13095089e20ad70cae80420abc2085a2298f965257482a229ac14912d901a9c80e611092569060669452ca2900cd9973448e9528c410e59c7336a1896c8293263cd9549b48d4e199483e34a2e40152e40348185c78c2f03203110b4e710a4853d8128f9032a0295a00a9608c2f584102172fb0069b6f624828a594984bc99313813807a116b6a0c10c9e90042f8696ba904455f02be7a494d26a55d5db8cfd8cba88b2b1fd8cbad8418c4a74493aa28405cf0d9aa0a6dd2ea59454488a54508a54e812a9b014957ca212500e9d3973c8c7c78704515c5a7378e4d4c0a3c80947db1fdbb29faf30cb83a772ca29e9113928728215720a69180f90221f40ba787551d4c55117485d6ce922a90ba52eba48201a5d4ce9626816494c56e9e3e3e3032465952d6324911d908aec3086cb6f5e5085b209a986fec515e56c1fe3c86fde2e6a82062dc860c9670a345081185c088318638c805e3133798014f900f28a92b0e414c67d0b4988c2f62d6664d19296231e242cd4c803a4c8079057d405a1a44d331239df51d2911b78c134eb0ef5683fa31134d9fed83bda741e51338224be90133b82ca39e774f9cd0baa50fc468a2146e5a812541ca18fbc228c1ea162896c3e9b3d146fde9612cb487608d2e2a58b137ee04305cbbb2041832e76b03d22215284278b251c5cd9520b9f85160a60042a4350c11550b08417ac2c3cf1aaebd4c8022a4f3201c21ecc408b000a2daa28a51116453d50c14e6d7162b3ce395b00858c1d73ce2925168e307757c37e4c5e3e68820a0a9e70820628c0c29c73ce39a751d07624b8fb4d96229ce004252041d214aceeb73b141f1f1f26585c76c76dcff1953f0f1f44dcdda5bc32853d5fceaf3b9862cfd77c8563cfdf76a0e585f62cc27e463c00a3007b7e4e0d3caea062cfb78107893ddfc77c20be7a46455bf62c7a485264ab2b2f016c510021be2b643145280a4488d203a12bf4780167648516dbb7b8e2823de39f54412b269de8620443480802129660032bc85985146c80824b065513cf39e79c33d299d158851635e2111446bb7ab20a25b088305892c0092975f022c200a04891032d98600a1d78c1624b022868d8667635fc146b22af5edd6dd9d48109648802697b61b11d298aa40bbf0a57a862092f78c214545421562cd6fa82a26767b3e79c44629e1461cfae86f77e03bf561e73ce39e79c930b02d87c3567f8fc81972658d1831148a14404168fa8f3f106fe1076a752da5d0d7bc29c2b6c229d92ba45619bf3a12ee21c7cb90311aa8bec6eca18895e3d2a8d0358ba4fc7a6bb259bbe126dc76a5ff66866adeeaaabb61f2a98aed963fa65d97b26f52cc2e61fa53fa92d944c5c9a22b2084d13691e0043c07a2f9f094be6a87f3ca31c4c810ff7649a1fcf2807429b27c6dc3111021ae520ca36c299b84321c8a7617befd5a3bb76af51cbecb3caf59f58e0f693324af9f1e6fd5339f71cbeefa48c52460c42973427de3c191f7c3146ddc5f83531fe9bf1a0bbbb3f19a573d1dd9d4228dfebd3524a39253dd5ba6defa17e917a7ead8a37ef7d8c0d9af6fbf7dac381104238e3e15c8750c3086b8c5bbc795f1f8cd5e18b31d65021e3bbba6da88edb36d3507f378d1f8919a899c7afa2f1a6d3cc8ceab599bf19896cc6cc8c4945636606e3b755db76828989e1f6844961a8192afa3130a7146a464686fe8c0c19999919999f9179192d840c2d6589984702c6b4f98dbf96d2d8514a72f0cfc7269dcfb1d41329a59452fe7bac1a619393ce79edc4a00a52ea524a50d1f645680a2e4210570187a884a1c5cbd2121884f6330a03cb33dacf280c2fa84cd9cf88ca93dd411fec6734450b1195735ee405765a696db4d6ad85f6d96ab35c881d00e859d83f1a6b36e10ef9dd69c713c0c3f1a779c2bc03618f02f0d05911a0061a6618c0a473ee98908b37fedc8ed763be9c120a206e0ce7f5b0d8d5687c28658c98aea19a11623632ec789d0156db5f86d743000f47c70c991ea86ce0f62300c7c9b7715a8e6a276c8b3a37efc6ff710f870518d6f17ad0f7e7b8a8f358a91a6fb697c2302447bd8009b59ffdcc9d06e10d4c6ad63029f15fd4d5396f725f016f6b1088dee82c853be2c307ff419b0db7efbe9bd1a12c176f68aafe01b7f6f7511fb74fbd09eb1753fa4594ae3a52cfbae5aee6943b9589e3e6cba284db561af9dfe77109775e8dffa0eef286f24ddd416ecffa381615ffd72532d1661686f064d7ec67240431f6a56143f20f572da68c86c0c5c6d9cf6808538a8e20b47fbfa22324b1b40493ee16a8f9683fb78bf6b2ef977dbbbca218e0dfd6b8658067bda61ce0dfceb8758067117d41c0bffdb8858067bd9af0fcdb35b8c5f32c222ff6bf6d03b7f6b388b8b0feed18708bf5acd79304fcdb35702b01cf22e2c2c7bf4d835b3e9ef572d2fab769e056eb59af9f1ffff60cdcfaf12c222f3dffb60ab77a9e45e404c8bf3d835b409e457425c8bf2d835b419e45a405877f5b066ee1f02ca22937fcdb31b875c3b3ac7811f26fc3e096906759f972e4df4ee1d69167113589c0bf8dc2ad083c8b084b0efff6865b393c8be86702fff609b726f02ca2273afcdb26dcd2e15944518afcdb18b78a3c8b28cbd1d670cbf42ca22aafc7dbfbe216f61e83ad9b5eb6df207656445797c82edb93f69d5c38578b2a963a54abd42b75a85641fd388702da969cdbbe54a972e54ad1d111cb8a46f3ffb60d9aec655fb6675efcc63f55b46d5c2755a4f215cd7c243f9f66fe63fdf05d8d27536fa3fe90535a720a2968bb5f4d4ba4b4e4145294ededbbf1b75b2cd2cedc055080817fb175292424d48b65e5ca95a3a3a2a32bb01c6d4b4c6a56c5574c5856aacc2a555cc7a4d4a5ae6c8fba432dd5ba6d2ad53fc7e56c4ba7201b248394f88587221eb24bd00675d565a3d42ab54b477de2ebb565fb979396edeeae72081dfbe29cabaa4fd537ab1aab3f2dc05e2dc0ae602f6ca8cad66151b6ff9531b8e22b0c8a7355f08b89dd820539e7d81325da954fb6fa8f6ed94eb56ca7596ee0a7a3eda72cdb4f2f796fcc322ccef9e9e59c9faec04c774c5eadbab2ac24f98a6525c9b9b75d7b1b4afb193961cbaed9cfc809af6d97a20eaee2378eaf6cffec9d2054a445cb26c07e455aa29873fedbd9ac794e9db3bf8187c4af36dec6c7dc69da86f6b8957d54025f813b70e32ddbdf46eef0d0632edbdf06fe01371e7262fb1a5cf38adff863c97c6616201d996bcb606359494ae2c2b2c28565c58b2cb3ac70b1626359c934cb4a87aa75085c76cc6659498a01c7c0b2b2c5575b585694923cfb7a6fcdb2af590d9c428a3a78480b1734f00d6f578d7af9aaa33e577cd55d245491af58565058e0cee9ca69e9f578d5a702015117145f0d615f7c85e485e86b2862425928169713ec09e6055bda4e3fe3a19e9aa39db5ec1ef28ff6feb489af8cbc3f75f9ca46b5ed1b396efc9554b6fa459c9b4f818f8122cd3fea82d8f16b7e1bdfc92c7b1b997eb1656fe30220b391334db1f84a6a0eb76a06e2dcc4e22b0e485a14f547dca3f55cc2c6c3db18c9314138389d4373b8956551cf873371641e725703cdb2fd7be8a893a965c6adaa65d10b8aafa06bbf6d80cc7a4189a28af212f2157c62bf7d80cc7a09bda6ccbca6102df90a3ab1df464066112d117d9121faf202f2156c62bfcd93592fa0571319af2649be824df6db3bb38892bc88f18248c9579004fb6d5666112911718121e2f20af2156462bf9d80cc7a05bd9ea45e4f88b6f80a2eb1dff69159445b88b8401171e1f21504da6fb732ebe572b23979f9f80a2ab1dffe91592f9fd7cfe9f5d3c5573089fd764f661175f162f2e2f2154462bf0d24b3885c4eb09397afe0cf7e3b486611bdae6857907c058fd86fe3905944485aae1622215fc111ecb76fc82c2221a22919d1942ebe8222d86f0bc92c2b5dbc582f56967c057df6db4732cbca92952fd5ca17205fbd31f6db11c82c22a0265893225fbd30ecb773c82ca2222c148b8faf9e18fbed09641691cfcffc09f2d5fbb2dfd621b388829ec827507cf5c0b0df2e92594450a2c42847be7a61ecb74d99457494c5b33c9cb7594443be7a60ecb7b1cc221a72ee6d165195cd22aaf270ded69062d1162d695b244d491b823bb64aa751b1351c106dcd8aed36bed3aed8aef5e065b15d0bc28def3422ecfb9d5665fbfd4eb372916ef8875b373c8f2f31fd8d3cb378383d78f38a69c59b448ff39779e2e071738a598387336df070fce794ac44db42a6d0a481b5b2fd6f0ecce19e1e7ec880efe3f19b23076e09818031c61863e3f03f70fcd4d595f1f0b2ba6a09de2ae6c891e323b6b12dc51bdf96ec1623f6c9db90279785a2d1b7312bd55d7d1bdab664f3aa6db3d996b42971906ae32d1423168a916c836c907d62a118d1a188d56cd8a06102d9d71c22166d501f237f5d5b23a822e76aadb5fe0070ab62eeb31c81af99fbeb5a5621fed1c3672319c9db205f21797ffbc4dfdb6e703702b93a97730fcfe5b791a2d6378e7cc643750d017398800e457c77d46708595d38e414967b030381fbc6135b477da84fd49942422d23df4da19b3917b30c17612cb88a7331872f61ab7ffdb7e75c87fa0000b7aefb4b9dfdd43e5167a9235fdd18554be6be7fe4efcda9a354d1952d5594c2e2f4bdce342e724e95f10b6371cef11105639bd99ac6559cf39738c8ccc65730e8a68ec9ceb412b6fabe659bd9cfe84b0f7645b2e1a8865d4f453adea606d5b7c1ad7a0ac243f5a7b4f11d8ffd5a773c78e47812b6ee221d6df1553785b6165f3d23171737b4a219af3a87b64fa1fa3870ab4e256cd4c78b7c7544a7b0b80cdccf291eaffbacb2fd65c0b3ba5c2ea834a71811049576adff3a98b4352554d176f2e4bfbe10b8ec2d4730fb1909a1ca164294fd8c84e0a4c67e465996f6a949d5ae986f7602824fc756bb2baafe8b7f6a8243d427a0939397945a6c102f811bbf9c7329b624f197c05dbf4369b175a88caf64fc7a0e619c263ce49cff17fe9f38479f083981b29d36e999fb3997fd603bbd3e05398771c041eaffdfcfaf230a8b738e7a0951515e4cd9f26739e7bf3b342459fb877b7a189a43790a492cdbfd6e0add9f539c73a5dde1a11a6c1dea35877c75df8f701f967d1e0fda9d12a8b4af10704bb15ace603b01e92d5bcd539fd7c3f7f61f63ccb9d11dc6384df5b56c1cdfd9a5ab05094a0e0a7c770afa1bdfe121eaf3704430c61633d35f249f0349e321c8c5eef0d0104e81dcf9ceb9d3b6e96beeea36bd91dccd9f2e67f720e44d7f03a06919c93f2ce4791cf5f2d5159417be7a1bc9d10ff6070720459d256478241e20c3031eb025ea2c51814742860ac8f0830c6fdf97b0c02351010bd8afc00ff607fb3fbcfd0abcbfa5f9e33af6e537fe2eb91cc92f3674579b6cdd6df3f564fb0ff52b505f8607d47740adb5565963fd5875e0568daf03b762cd02805bd9ef2df2e5abeccaf6a2f8b3757ee49fc557f11d89fafc68d9f090af1eaee2abdac2433f4280deceb2afb5d65a6bad357e26e5b561e3ff73f6364cb908e79c081b5577febe74cf8b5724dfbd285821b8c37f70f80e2271c377300921df4125827c079b00f90e36d1f31d74a2f51d3c818fefa093047c0751b0bf832ce0f90e428180efe013037c075fb02b84c1ce77300605f80e0af1f80ece40e73b4803027c07a7a8e13b3844c377f00603f80e5ac9f90e12edf80ebe70be833bd0f11de44100be8357dc7c07b1f458c077d00700f80e1609e03ba88319be8354acbe8335e8f11d8c7280ef6014acef600a7e7c079f782b057c07816e7cf7bce861f33c7cf7b8e81e12b8da3d1f7406e80880e3c2b173e4bb1bbbda40db2970470e456aec9a4de0abee9410e19c2fb1d5efde12dc12757c8b91bf5a08eed82124dabfbaa20ecae57255978b02d54581e7a1baa6c01d5bc5067e1475220fdf430f6f4477dbb6437027bee00e7f237feddd6a320ed48b43bd36f714d09dceee5055e04eaa08eeb0f27aa4b07838a9f8823b3608eef0ef41c7231eb46fbb14752e927da5b0783d6c15469ed39d4e0edde53c0edd7159775fa33bd5eeec52b54b7669bb8d8f5517e19c7f96d5bfb5d6fab3abb6f1595662e3e1967e9b7f10b7ae3c924030a23b9d1c4e77393970e8eeb3ee543535fbaa1921a9f643b60f81acc3fb908bfc9d4096c04b20bc1e5b180fc775727238eedf54ebd52143e0efcc5ff3100f793a29ebc30ecef9cb5d244b0152b17ef86dc97f0bc35f872c99f038ff1cb20cc2934738f29504de5f66719d1fde5f6a711dfb7efa777a9be90f123892290ce00e281ee738be26e3f89c3b26ef39e0cba1bb9ad7fee21f35cfed9cbb10ec9abf9ec3b6f9874aac78a8344c582a831a32342323000000011315002028100c86c442a170384d1459f10114800f92ac546e529aa74910430821430c0102300000000200813430003bbdc2bf263da8a4f0944ef9db508f2d0a90b7a8edf14e0f181c42f4eeab7bb0d81bb00f311372a7cfcb71c1e63f3bec715be85995dbae38faba3a5a1f1010b0c429d8249db05d3dc1ba891390b5c2bf68057c9b201021e4f5b05d981314256a193e4821ab1470ceb4dc6be3b0ab56dafb7f40b9231db0ddac6e2837ab9baa9be54677633f37cab50c7dc28701f887801ee1cf6ef3cd25bd3a2600f706fb0317f04369d9a2a003f8155d692c5b876c848436700cea6e37295897554fbb730ab5015804725db67320b2a1ad73eed44ff9b23772e1564812ff648b1cb9107883a80a23604e240c3228af08aad907bd1a0defadd77828ac92223f76415b3f3bece3c11c1bb0657bda4ec86c76d84aefc608afff5bfa4864265acc3809c65e68a71742f4ec110e845ef5ef6c5e91aeed8c776a5edbec3f878c6b4def036b7f7cbad6f6ff677a10124a38773bd7235d3af7edb539e1f4b930e7e95ca3b2ab03dfb5fefb69061c9e7636b82dcddcc2e465772f0e8fde2b9103583505e073bdf3d46d3f53f73b484ce839f5c9e68a9e2906b55be32cc7b0bd873a54cdfd5791da4691cf61418c4377a6ffb137c13f3c3f5c6609f43fe461ff256f8083e3d7a524da26936a924c62434dd4cc6dd15ab3b6d31a0bde950af9c08654c2fd7cac444ec36110ff8368778369dfbd3df9334b2e97bb974744487a74a0f7028c0a65a51ff405e7d3ea6f29f3d33a5b8f0ff4639801e648dd22f75192e74b5440000642b134bfc9bb83878202170ac0f0ad11d9bc1a3616e0e8a785098e5e8666f61858d3c887da1a121af8be94efaf1278b880d7c4fceb8b70f2cf071ef3721ad44cd5346a2f44e3957361d03a006e3be836b291836a91bdbd69dfc649e8322355b5c057614a759278520c1f2039cf4e4f27187e75f1ee10e6f6e0ee1cb24b0344f473f0556449dbf96f23a9b3702301991256727e4f082e7358a7f2842ccecde1830c76169db4e50bbfce42739fde4858313bf031cba16a76758375aa36305df0e63c1f9b73d9b49b349c4148abef7eaf13747a7d8bc89bd3716dad7c9683032b90d2255426b63a4252aac31971d5003220bd64e7342149886776f3bdda45e9f4b1b5ba42224c182d8bd4af6c5e011eee6a2b2f31f166771a8f0bda80926cdd533a22fe869d95ab028ccc332cd3338a2cd7ef10a3aa53d1fadf172c76749f7d07fe623874cb7f2e28cea638f4b8f3a7637884f00fe52435d33689984b124ccc8396ec9a0c9cf6552fc627499e81c42627f672399e22c8b00ba1d72aada7a64e312cd900160359fa74d704130e0fe051fd6f1d1b3357e9b55202a9a7f73e0ddda122e0323f3ee401e128556f7a6cd89cd56fe3e6bdbe1a983b7b6cc6fcdb6f53e6f09e9b9b077c68d25ce26743e6239f1a37a7f9d5c0bce86333e6567f9b323ffbdcdc1cef4393e6893f1b32d7fcd4b8f9eaaf06e6bc1f9b316ffedb94b9f7e7e6e6ef1f9a3407004eaabd0ee692b908c825a3df98dfa7b67479a4dee1212749aa75680d0fe07988ebc848702833247543e52220cbd8bd173604253ae8f58dcecc389b061e32d72a4e595a4a8403fbeddbc3a1a569bcd55ef030392dafc3e05a942c3418ab35f1a144366514a7e6c07f67040b2eb5deecafd7f9e2f3a3ae13de28cce742f011c4a887a9b510239871510c37a761336762acd94da37c8d7aa4e6f9b7a0b77b758c62e00b4997f04de44639963d56cc133bea67d6056961c4494450b8de5ce5fa6f22c8f7fcc3b6a2dc2422d05d0231412b02d64f4761c2461586ae5fdb89da451e62d3a36274ad077f7891e5288816fdddf033937c6e67c7757830414c091bff0b8a72b9553a6cded2ea71fd63442bf226374f2a864badef11bd5eba19874749c78090dcbb355ffdf7850016b5b83a35755198c1d1d4849ca206c1cb3ab60f67766c1e87fec0b5f1a08d6cc2c5e48d7c94eb1c18aa0b165cd3a6d541756413d201ac569b9c5a6663fadf0357bbd8d4102159f62070abec258c97da55b01f77d31722b84fd8f4e2c2588b85c0f4567a352b72e1e02bf90530b02a972ffceab64fa8536e9aeea2a753c89ac1df576e3432062fc7437a6a34023712120868fe2090287a49f64d3f0708ca70f1c7289667a5876df86e5ae683e6131ab678653f2341db8a23cb26195391c69e86692af5cdba75463e6e12decf6faf8dbc08c76018db2cfc2c06be6e90a269a18d18fea0bec998a2514b3afaa4eefc5c86928c9a9c60e0c53b2e47f12f4a8f2c4ac8572c2e86b36885fa9825f79e8a315f9585587419fde7efad03f8fe23740c9db80c24deeac228d5469ac33456f1df0ad03265cc872455db094c31000cffd7ff76f3342a156cd1c1d132d54fb8165bb619d7c38a842629cbd275c1788824de3d5cb6f03aeb09907d6e4cffe197aa024997a487a3670172e16c8e871bac742025181ec49e15996b10a4b8580d7575242d3f884cbf61588df7f673c5680dd36ccfa1301ccddad086c02bcc1d16ac6c159ad4ef4212423950290e29c89fa3c27aaeff10adb88e33491357d6feafc4559ab0ea73afdcd377d06a020b72a3f6b2dfd567c239cb484f7aa41d8c5f5f09b4ab111a22bf6ce94c5277e136c302acba6e28a6805b53a362a53ed6710a3333a8ea44c75464e959c7299eaec05c248e1395591098861ec03525250c10ffe67ebc2954b3423786898df7b590b7009ce00682998a04cfa6adfcb5a418c77e16b5d6c0f6e902eb8b999e0a1a15759dd6d6edf8414a6fab32606e0b7f9797b27e4f93ef64794dffdaa00749cb5c5ba21d905457aafe9dcb7d65df21c05954add4276fd8861f0af7dc235345245bc0f3006370b89501b4ea31bd318d6aed1429985fdaaef02722aba434320d37bf6cd337f03b45310e6cd17718f259ce60afb0aff3f91541881eef8f4d3ab24cfddc86ca69243b77c6a566bee266e19d4e7e1423e09551ec7a115122ab587b02839a24c5f099e2bd93bbea309affd02c30747cc6953a89737f219d6eb4620b26c3c2ed6d83055ce13f0e7c2fd9b48922d250a65d7cbba9c0cd15a9c1aea1e931c17d9a6a0d198c20ec50b1401ed3a6d4f92b0b1c6e0db8399c59362ab07fcede430c5b5091c5bb0ce89a678ac1302c9b6cb44138cf7cd99903ca94b64d6afde7dd8492535523c9adab42bde0fa1e62a47064f80e2827a002323bcc470750330ce1a610c03b4690a51451fa26210519fd53c08021a0d906121ee3135c3dcd74e9d56ddc4fcb34bb4bcadff40f6f3d32a7476d860ff43c8fc46d32840ab89ef8fe9d35e8600541883f7a55a4e2aa06cbee145a3080f173e18a1150591490f9e216cbdcba22de66a96c0151eba83b197a22599b97582c1d49a925948058a3dd1e6933451ecef080fe5dad21d299b02909abfb4b09efbd3924e180ebf409408899384ccf3ce83d54c0976125fb64a966f5769366efefd399e571668f7a31f466fff988cdfa71c8cddaf2198ff99b04663bef9447790ff1b8f3a47854b1a0c7f9ffba66219fe1515e4dadd97b4fe091e7497bd4fcda1ef7cec9638e25f568c75f5e5e62aa10c03f8d8d0a74d493d22023f721a29e9bd84545059d681cf7c33f056db59d4e7399a3b3baaaa1c58870169945f5683e705ea0715d0a226debfe781d31991399a4e60a4252fefa8046ee5ac24a3352013b154d119b5be8ac6af60a4d26ea8a2813e62210d088f7947d59b687a49d263767fcb09db4810babf18f1bc9e9fbb5bcb482480b623610692c90e66f373820b3ab68a147deaf8ec6f9c169a07c7c60e0239777fcb0ea6a836372d1d01aa6d260d9a3ae347d3dd343335cbaa64bb3c389a2a8d2dcb1aec781aafc5d38e6380e8e0bb047bcd7520584ee6e7adaf245141b322963c7268cfa1092638cf341f1c18318eba1f57e3cffaa255d638570e5d618c6ea7fd0f53505105c1140f54b7365159a164cd9c970a53bb5bb6b3e17b142d79b54f2a8f9a5a828fae2ed59ab3a44895bc0c379094acf27fc85b1235d24c94d3454cf7b7c672c8486d6a5af18a0a5e984d6ef7da93ff49dfd349e2ffec34a642668cd3eb7deca62e318fb019ef54d5ef70112f0d574b831e0e86bbc0ec51e8ed24d21e261ce64af1171d2014df672a1943ec11bc25f318f8ac2328de8debc1ef243e24da23d6238fdda1c0b99bf6b2cdb4c79ccb03e8c636d291cd1eeb183035dc4f243340f69417013f8a779d741b2bc866e0960516938a24f6906722ad83b24050d52f5805045f29d993820168db13cd4ab14647bc36cba17b0926dc86c1136957f5641088699ba210aac1a532564bd48b2a85eb5097dc66a3bab0826cea0d81ad917a13b29612a63498cb911c467e2a1ca92787aa4f4f42714397e6d9c0588ce5170011c078bbbfb6ae689d495a26dc9321479543ebd25eb4f54d4482d2531ec0712a8abb76298d460c32b6164435820f8ada15f0010dabf6e204151dbca90735ea40e8bdc0917ce1cc2b787e107aa51d9aa39412693121b1a402b87fc929b4f5ea6c2d3ec448d49501689a97f484071c65a5e7dc01f0fd546b1c266b1f6d4d98ff1c241450255a8aada08548123afcf1cc00f2b55805b1f699d40a1bcb31b5168e5ae490b8c54cc302397ac5a09fc3e9dd401a694bea829407025c6d0daab8343bb7e8e91ccf64177edc1e2d0b937fb1e6a6fe5f41d95adadf6a16ff2a40e80ef2118abb89b56d087ea0a51e7e3c6695cba8b5f895058f32584a5f188d566b471197d30bc3c237efdc6917284bc8af970e8ea99481783e415162ef71f79c93fe2bb69d60f6f268fd841553bdc7b432aa9317141490cc12c4d3c03a9571ed50db528cee52e58be1812fb6ec0db835f78bf8edc5437f4dc07d893e4fab5ae3797a9c50795198e0b39c170b43ca685cb869c06f30c644e78151a8b99ae5516adf8a7d7e83d8880c7aa8a60fee4296ac279e2d70396afe25686f239298f1b391317f88f61db7bf5491f26f6b47bdb6ef2fd9aff89a8f377a8a599add1684e705432d3988c8ad9a432b0f380ae7480130ed3dbc5b96d5dbdbf61d1e121515cd3169d46c54d1fe7dd446905c2b7668b9a0b8faf592402f3318bb81bd1ed0d183c6e86db08c83c68536c4b9ad1d548253098520e1153a3ec20e4bc59709cc6854f908ecb338c5791b2e8206cdb6490e3c3b06449fa59fe7efa9b6c691a5135c0c37bdd86d2a0504d285eb9750853c3e9c4176b764076e4aaa063d803c355f8658398d4c5c2fb77415cf5ba1913e418cdc81ae41ed6906eec4ec1b070b2426981b11a174488efb637702a2c0d180a5d6bb81dfd631de90a4af364a837844d1c5706360a4c03b823db02c5df14f214216fd671ad15570741554cb62569925fc1f4c523a8a14f52bab558e10d4a8ac22d25de43ce1a1dec68490095dc3c2875f7e557bcfd783f8a8583370f76dc83bd795e7887605ce7685630c48f4f6e553ca2d0003a42e8c1809c178812ac04bf6d17701e301b6f79fc46ce69ccce5bf169d86a2d97b0cca02f259e52efb54eb0c9809c4f31e84e294e0e9a3026ea88839c96c6b1dd9db0111a6c1016a5b2a11dfd0ae118fda5a40c31ac86389a332d2e84565966ad5a03f01597ba0017cbf723e35d77eac690be25c052edd34d69a474e0cae1e46897b4a2ede78e3f62c31d10959050831ece719c82af5677a871198347bacc53343e0bc61d8f3bd8c87521a769ffd69eed79735f42923e705a62b0be9a27f14c1c449d4c0bc222bf8493b8b0862244b4963d9bef7b709b76c0cf5a2cf9f7ef9bef9fd929c426062f95d38e135e38a07684c1ca837faab76c56be0acf9c718de00c05781bc99212e2bbe2e1adb209c2fefd298866928f2d1067a6109bca07022ed1ca06bbb12464b9648fc6963e124278648e07dfd888a62747111c114159e8a25582c287da442c2fb46aec192ef7faa2049e77ad0e25dd5c36e6d03950f4408ef727b8a2d2ff81f049c362d18bb1c64c4957927b4ffbc605af8d05f6a87813baf35642155403311667a032ce149134f6d4f37efa149128bfaadbd4f7b395df56fb558f852be5fe1caf7d4c38cc2d1da8c60afaa439971d3bb5c207bd73b774def5d15b69d4ed5e5dd949fba750101dd41f1342ba21d6b845103fa7c1527609066c58e6d1816dded1797b53257a0f0b3ca1f3e08fa161cdd546fd437827a0c5ead3cea69ce7e5fc84a22332355cfe90ca17eb41f942d368a31c20bd45bfd0ac70764bfcea18ecf805f424332f22678b2eb9654d1f039075c67bf9f46afcc50d92c8ae97079aa291a413e820ed971516d6c4cedb80c34844ad2e66b53750a3691a18d3b48136cd971ac3b2e1fa469c9c26346d585bb3b59aada161ad8c6a6eaa476d98d1de8c868db5c4a45106efde45e2585733e2d62808479b1943f5bc4d0c3df3a74540b2ec119d33c099720e58bcb2dba109e80a46f721857699abfc137f832715f98722c8fb125244c1c965c99216ae6580366b13f919d0e0cecd3ac0e5ee15a8b51d4895e210e58319bc93c54c4926cfec773608c320a85504818b84be397dd64300b04a111fe0f1eaa8315bdd1c00abc9ca1038db07803299e6018410851ce4a1d846acbe8cd2551fe4eef018777b272d19cd4c381c5d230134517f0d3c2e700b7f2e2431fa1ce4e041361fa8a6fe645a910084b2706866a466a8e5e93b671f8781331a90310e840ee893ef3d829b603f9cae580ffa131ab5cde4749744485925645e89e69690f2e70bba28ab4f4876fd553125ecb272d3dc335882e6f1be8cf2d5770bc5053dcad832fb233d45a1e001ee960584850143e9318e8ef033689f44a8ace5328d35879b2acb67aeb708a920e99a7bf3d042a5b32304c665d1739a8eeba96f0c8a5250204f0efd07970f8e411c6bdf09d70495b5fcc5900a3babac455dcae3e224a94f64600992193b86f7b5bc4e19d1cd0b3f3c9334a27c98f086cd3f87a3217a576adef1f2ed27590e071d99c5c8034ffc9241b5091cdc77eabce8f99defe29cacb497b6e926687862c2c4607b622c34a6459b5f23eeb242f61a235b5e1352c42b4aca2bfbb67cd01f8e1d084338bab2563300fa3a9a02117b50be545d7e82da223705756f645f9a04205a87bcf26e4fae0cc0d75eda2e77766a682f5c491206f55b1d29d16051cab198ce1b46a42cd7a04a2f6c8e06b10913b326e1a2ce465e8c54ead43fd02bb25684463811b3d4fae051a809341081aa368f52b7f9582c7b2e2c7d25efe6e6d94adf7454c289f6db0ddcb4b7d3fc3bd1216a16f270a16401e6144feb287bb3c8349ea24196885c953138fa42eec9fdfccb15e63d90da5b034fb63a3208f042f2538e2fff03bbc93397662dedc228dbc6ceca466f5796a6221a0920d8d28b9750c7ad3e7911778041d4aa343b8293efeb8e964534b8bbb999e6c500344bc007f0d2e629e873a43e6546efe180dfed1f98afefe30c70b97450d4590adbd436be8fb097ddccbec0aaf76e6dbcae878b01f4c0e0efcb620b3cb0e361a73e7eb65d7789349fd7044513d6f7982b448f473c99a96c1928362219f0f31a9b2915f83b051d94aec7611273e7cfe53e41ea5f4edf415b17a4bedba9e22eb8d67500631f25e27ec5f21a3b73beea26478fe5cee2abe719f2942191ad433051b78e1065393c3d8b0854aea9deda8267c205fda7b71fdf9a1ff0c7d239196938405146973f367ddbfd89c237c6364eec0de1a70fdc24eed48a9f5ec904c1ae06bb57e76bb717bb813b047c114b9e02799cb749c003ea1288cdf4c486d6b7a039d0f0d7b3e46ad7f3ba3a2bac4156347ae0d3c88e89eae5ae4bf85a5668e94912a33e0908c9d9380a74fab95a9ddfe6d48b729d5fbd43add9a56671823deedc01b9f49bca0e7b006bff7e3ef39085ffc92d889f607234187fc7551301f225fdd8cd698c567c48dc7052a35298bd440b3c4d4a5b7dbe996525d7187a9e4742d348ea71f9c29aa532a0cecdd54786d6b61ad950d8d61b320b2234e961202cf3114bef60d5762ceca506938e1c484d283d2765d8c1c1f9af37bfc1a705d08295d238985ca80d3bccdf2617d1154cfd4271f00080057e6b153745fe5a7412881f2e31d35fb45efd39b99e4911174b87668f908bd544be5ce3c43c3c8491b67ace00384cf830e0b529741bf0a4027b0ab46f428e054c92e11e001b4ea804404f539831902405e201c0b8844710dc01438b004636611d4005514824808e14381118d209e52890100c424cdd82487d08f30178c19b024a071cb72a34fef62ce6be35c05601273d94bd69b961bd5b87e2ea3fe7eeafc43e390ec068d6c3a870e94c2c7ca68d277f758731e6b6cb57000c8cbd047c01f1be581c187b5b7a52af5635d5b97a1f442f5f391e87dcfa03cfe8902d9de50c5948fdf02a2164782494a608d37236225cb440344ee430e3e54d01426372d49b9bb65726247463e370ade1caa09090d42f946b8bb399e20a65048f84f96658df2ae8a0ea2086a65d05f2fcf9078d80ee9fba4798d6c6d7fcbeab915735520f13cf2d6dbd1c033e7a295328ac300190e9e84ea051600a98b4ce6a65be524732166ddb1314f208bd5f9f6a7d006c0472e1ccf679a4517e800991828fe5b35ed68473b2da4c96af2cab404264759930264b4007ab983ccae3acc2cd01cff02d21d448e79929675397ee37eb2f39b1db796f5cbe08d637dadf216fd96068f60e3de03c720e4e2f6062ddb594c9790009ffe0bc7ed45765c900c613b31f0576fbcc90b0ce83b6fb3d17269123d9d3ffb470cd53f91af8aaa10b2f23860c9245aa9fcffe659c0c3d8c0537fd055d1bad01e526a9abd3bf7b401c80b871456f9a74eec74d4d7065ec751a43fb6d24c36734be41e8d0f53167764cb1560302b0881fe80e01c70924e601060c606b914e9894532c4a3e18b757aedcb68249573dc629655d83f9163b961973b623a530e118360dafa18e04c26f3061058391e101f1655d2b725161a0ba1df17860bdaa2c1aa8eaeefcee6cfdb17e565081f7e372cd09d76220094a0144d7fcba5c7d50e335ebcda748e535afe4d4e5cd7acd4b4c299252cde94acab1a4efab6c3d2ff611eb3c276fd43085ee4f508458ee107287d44ac0b027c5220a8bbf7506f8e97eff22318df01ce8a20e4e41e40a35928e29ee6c4e987d4fbf0ca88ddf1c14397b22f35019f8d05b106ae2534b51c9e806d003d8a5b46dcd9ecdfd55511b4415c018cae9a72a9e7c262287453621c4b8d5a39db7bd187d4689a87d386782aff9c5c89434fe8c7650f0697c9fbde1ab4b415fc221cb8c6aba5122e87109a69c216bbe36473d75545121f30f1a87c35a606d32ab06c35ab1f8a95a1e12eb8f0060c8daba066438c23434b2736be98484e05d165833fd5332a061b919b2a463320cd090ae931a67045a266181543051a8c515f55ca1038ca2c9a5d4b9adb88177c82fd8a2b67dacadaf317dbaa9958320c97c54625f604d391bed1c5efd1959082c857cdd44845c24838ac934a8c55883cab6006855aa802a8eaf53d24bccd256823f79ea51b07e5a7febc20338d6f70c1242ef90ab820f00e788a73d2e2f2e208cce0e2f4cc2b981d756d7b973feffc9c4d83484d9e2ff7f9e41e280e8279980b17d545e9ed9632387da35dc09ed98bc84d432d06a525329e5c3259a503bff26663eedff7789b74c8bd499666e960cf8b9c3bef906f8be2d49dd67406e645cd090ce4f7df7270fb1d27451fa30934edc42a5982e92a977310d923b2a2275811b14f3b74088855ad34797b8e1ed60f4cf99fc58f08966431ec66a12444ab03cba6bbdf3a1616bae3f7975b0300c3afff6fcb8ea7a13ac457493cbfc25ada9e32e982e1a6cf76da627972722514f83e817900435dae3d72f0663346b8092411396cdb8ad5de5b35ea120dc26225a0f6380bde52a38f989a385fda7fc51e5ce41838418216ca61534c145efc09cc914db67080df951602d299b203f23edaa1393a6ff19b3f95a3d815c4cdb5e12314f0c7b09106c5628a65f358eee9d96c36367b087be8f5afca30cc6a22970ab120575ee0a7a4ae1f5ee4c6e8a7d248262d3c073d3033285c7c7222d28cb0cde4c95f5db05f47d78c7ca98184c57c4ee38dcccf2974f8c401930c595ed605fabe148ab80a091f705a59d008311a68817f7fcb459f95cab1addd7a66f6cb9208e8804a4d9f647aa425d8ae34d349d0d7163a50f6d0034a80bacb6e3e297d2c76b67014296f9b611af19d33a5f7ac8b9071e59f24a6380d231ee724addf42918256c435e158408f0ad16f65af1c18a23af345141dfbb101eb248ef7be32887def68029b296f922bd8f931572e3134023f61ed74d7e3f88e47a3c78fe32d7b558bdd4e572fc48744320b3167565e5a052149bfc88d45a2548b5b3330f4481cfdcf39b08057a3330927b9c31eadeae3afea070944d1a98216dafcb10cb62330d17b60f1e2efe4ecc34bf40f54302d4fa9d1835cfc9157ec4b8ccdfc3f5c53378d3a9f3121fa098d0964cf51099ff941633a722e2e85e7cc1b32e287ef56e61099faf9f76af095478c6cb2e79b30edd3f1c8cfd215a8c6a029929676539da26511254ac1437087ae780b8037daaf49a5d89efe1c27010e95e20b2b039713502646cb3b7af3470dc553cf16c2110ff22754f74cdf6ce0497ce69123b09e44de0ed16358cb7a21836171041c028f87be00cfdb4595dd519795d03c20da7cda46f52d7596d49b702be94c7a2f588ae2b64ffa83ea6ea5b20c7680a5a730ff9302f5ab7ff67f9320265b1545a079d87c2c55b6075df09b4d8057c5681c659cba1c2e095da5980dc9a796a3ad5e31aa20b6c03819b1580e42e64a7981916c06ab03564b00b2bb7a144f4fa48accddb2d5e7a243a14be950c8f501c9452523f7c0542ad5b745d7d5fe421095e970a3171858b95fe60f5f1455530f64f144206952b18306c9a684485fec05d654a7d00c812181757d3b9e149ede3e3bb882f94de0ebe65d687667443d16bd1597bc4b2b3c5d868115f44b96993c17732fc40ff3d153029454b282701d726ccdc1291fcdd8f780b67a32edee3fddd333b55cd12632696b16e544e5cdaa35f3f62a7a2424b01cec9ab44877472458568cc950cf6803d8c4ff9bfd1f91b56ef78258325e56f2b4feecfed251e6806e13ae297ec29d846980502f4d2fc1575af51d711cfe18fd9f0600a00ab20a566eaffe73e4a632c67295fadbecda3f88630b0f1bf05c8a4d4e432235f8088397b3c4a70c1e68b0053a6aac49d0ca0cca0667c806c1563d906ab359d1c73fb00e17cae3beb594490345015ed3a716690167de40b51b6d0bc5424f0f359054fb99c085200df09281a48e85436a1887d7cacd5cad86ca884c1dbde5d3b2dfc18b79409c47fa064d452430d98fd2b8a410b450afeecd2416440ae8b37ef7fe7c15115e15492dfc732c8c3b634a077e8964cf2a2e8fd041418f1e0f455d5d6dcbb4faf9c404fdce38fc9c04b005682c8718433db8ac3746078043e38a6cdc87c4d0eed863f3e83c4bd5c80f71f11ccb031760a1e51f1e26dd72117082532602015ee03906991902068f8e20ac22dffe2a4dc379f427b517ecf64b441c12f170b107d4e764e192b55c50751de7d08693ea8ff5a5ba5fde86a900073a364c59f6e1ad4601df3969148b778992a2eeba4e16fa534af4c1408ad2557a01cd361056f387fbc737e796e6395c55e4a32fe015394d2b1c8e37c62990a53180efd96ce5787e48107fcaa059d66bed6259f6adba40791b40f1b3f747fcd249f0e3f0a200db5a3d4c8c04ebba1bc104694d2b423210692d271625a956116df84ee3705105d25ebf579a1bf7e65371696e9fe48ef51940ab04260b98faec806eb5e13ca18b14a15cf428c42a509e13923b0f91af07a1f45f6440c99c1535c2bf2ef405f0b3610ad8008c55b3d626c115826e84529c88829a4e39c5f5a35426762ffc7e6aa96597c7f0ff9561507138fe5f9e09dc34594860021f0573228de54d93479887fab4a1878421bc3043aa2656257c602c3a2d76e668002c433bf47b3bfff559ee59618fce5994f9bff3dcb2298ab2254d6d6eec3090fe0be78b4d1ca3a89dd61c1bd577586de639da21e487bcf03ba7912ee3d9e840a2532d5f23132ecd9d551d7246edfc90889be915813cc841818f69638c14910d7896b945206808a481587dd08358b2113aed8967214f4794584fec412547aa5dcca04f4dea2073783a70f74e0eeb333324e1120c00a711005ea1621811fba200411c97d288bb18c75bf21c1a8dc93bd0979837845020c5903de7452a59f8179e66a46b3d42c70ed784d14c239ca8fd129ca6326d5a046b69d31cb056e846058870ff8954f7be279a85e0c6d5172f0ecc96737e32bfb83a2f79af3dcdb57bd988f2e32739ae2d04b210d530a24fd1701d03fd58962ddbec80e05dda843323f09b8995a07a49115e7ed34861df6e30fe0f6c1926797a38932a1d07f9ff2f83750594de88b5a0125ddfb38fbbd1b0146156b1f6b89efe2f53295888290456e06c42eb26b9a7560079128db79138d7cea7d12530dcd836da4a1e8e6ad416b26e8af1a45448798fd787fc24c5973428407de66a82745e049b40c391b0b67a853b0eb3b19359e1051e42aa8bf47d1889c2f983fcc2cc6a6b490fba577073517e3c2036c5ceb83c3ca0afd80b1492377973e256ce08d3dbb49373be673283a03af25518fdea09e9678c1d9a7a6bac0202de0213d7b30958f575341c2f66f133047eaa49a413f327dab6f176663bb9ce1205876ae541a429613d99894e08296f9bd3235f7387264c15f549e28cde630dac9649948d0d95280fb3a043f8d25a336fb351d6d492994d4b89ce46a8aedad30a437e16608d92c5c176eb4de9095fcbf4b29e053c2853c575f73463983eaac3870509b23164fb3ddc871195e157eeaffd8381036a8726a9b1b208368bfccf0dc809a28039cb2fd73dd9d4bd30b7aae769c3e4d9356892d9ac6944224f82802ccc4bb851fd6000df4f52d286fdc1affabf6e26d1bae36b67cd7689fe1d14c51dee26e6682a89a40d5516cc0c0c68b171881bfcf656bda325839a2589465d4d39b866dc4926f341f69e2aa494af9f0597b09bbf92491f92302161d1a54c9428ba0c2ad3e1982d06b87acd00e7afbd45c4407f14aeecb8ba9df46be7fea5b36ee42345c9e8c140ee5c7e77d1114721cc08cdd49a2d64a129dd816270bbe3ed244716433f4b1b89b41e2a37be71ceb57346f3bc79e97539fc8484066f4ad1a6d45951d4583ec0437a44a8dd8b3f4ce7ed4cbe2ef150d68a712c8f6c391669de52856e4c34e5a6b780f855de8778296ecc7ec524874343de43b1d1ffa6369eb56456c45ceab701b2246a8b13353651f3d17dd09f0976763a412512c38e5ad86683f05d2a913be0b2d53aa3b18d09c8161ec2ac9a8b41157a4fb8b905ae1410c61749140a9610c2523cdead96810c6f20ac2f43494536feff6fd5e0fd95cd488aa9acf95677fc7d6a64fa8a5f43e7ed891f157ee0ad7ec9031e6fe58b215f8741f92636242e717c6d44eda8b19b27b420f4c659892c222bdd1ea44a197813f57117396e37196f4d1fcf3d892f7630f2cabfea6c807a8caf0c47a888e71c9677e81c239aea5224fa147f350a56b83a5fc2fb51e10e5c3c1944f8ebf5669c448391130de8c27260315b57cf0a9ad393c58e2b48fc79a98213d0d63eaa7e960b9f76c2e7831debbe525872ffbeb6a86ed33889c568d2480da30768c3fabf89f3a8e92eff43f0a1ee6c6ffec023108e2a0c87e5a08f2bf19ba9071158c5507865ea3d5235f61bb0d0d6a3a154c7e7f2934d19ca1a8ce5226bd10bc683b49f4f3ba259064056317abc448153fb688728fd49c1dc15050a668f2f069e0c302508f59a32a8bf80e4aa2fa1cc9435243c789cc67f48f7b748da8e4ffcaf6a9a44a62e28f2a0baa0d90614f3a281c123d8252dfbcb49140f18b0505d9fc0dab6be41eda901f29ed4d686724333b18639bf0d4e53228ab4cdf7c8bc87d739d2ad3f4622a223c76d6d15b336c9d2b081b3c4f4e6e9112d90e31fc88a25ae27e3888d143287f6d4c133b240635cf890f8f9532864d89ed28942c14115a8129f284146f75f1e8b8be2b3fc93345f40b1d1988dd1b8141e7048d7e18a179cd492a4514f632c831b71b5cdc5d2c296c08e3edc4bd84bec9c46ca631c77735045e9affb18f6573cfc25aaf1d50822dffff4155adb9908befee8466e7ce31f3bad059eef0b93c72b9e158f06a87b77e168b2a67ef910fb84e5b6b17c7eeb690b873af404114a2a2be9f46e8f8e1048425c746f78fefc731c1ab83cd6d7a91fb08cebb99c9a56ae65739f31f057be9f46cce33d465c8d65f478b99604e176b6aa2a09b1537fc73a924ee431cf7d8392dc43ed6ce01a50ba7ff4fe62b53be902e03ce94f0b0e20988cb3c19d00af70bfa220601dc23d3bd6faec1de96ab650f948850246f3a8a3f75810dd8fca430b89150fa43e40aa42a8b21aac22258be0bbdc95e8f78b686e2f22f1d551e667cf2addfa3be4984208f5eceb8ffaf52c5297deae7f811f4c22b197693421a51e0e2e8356c9ad14f2b93d7b019cdc97a4d8725c600914448eac91911eb45c9eb44c352ae10d1e09bec9a80f476476dbb45110411f44644636f154f6eec5eb1629ab7d38e0b0faafe90ccf1583fb74c1172eb03d76546d99d7f9b4dfd1b81ba571050775d56ebb638056d9e2d0c5716c127e0ddef8018d47378e3c7762863ef7f87f030558f375657dea42808145802ee36c5bc16d1d97e32c4d4da1c970e9c2bbb66df29f5dea224b17b98ca9eb4703b51db5a0a8859985edde10d9ff757272825be42cc0a8109ae62c2c801a004c7db08bbad20b84195068d6c4191ba846dc1b9a7603e0c2c4b37c49d0fd1ffbde116c8a191bfb42595fc776946c8434bd0623078c6fc214f99ef9ea6ed35a5cb2e693f074136431dd27a8478c86eb3e50088e0abb26896ca5907f4b1430f8c02126ab4ab5a9b8104608f4e051702c98a216f14a207ba881de28c8540b88fac4d55fe1093dbb2a82c135f117f6875601b16692cd7d14382f926b8c8d1be349f8742e7462c78470cd062897ae19ab3ff359f9166305c51f99eeafec212adc67aec7837c15f4bea665f8f0afd5088b8bd3424b2b611da2bd47badf46609df07818b4d0934e8e951790715d7a601fa4ae010800dbbc1d00388f0861732e8c682349b0c0f49e65b407e21b0a08e9c980b70d32b47ffa798461895bc25f500ac3e4d5fb51d741e58b1f9a167bd5b9945a3ddc09dc42c67353fcb54d1319db3aa3df8c33d6eff9746dd3eb57c776d973ee434d337b9686121737d61ae79f760fe395e8e0c1f4b2bffc23fe3f628aeaba0f0abf686c981aab278e60c908d99a56376604defae8499dcb9743ec28bdacf0067557d11081b838261ef177d3cce8f8952f5aceb93ee5dec05a9b72b0570740849f14206f0f6592decae6798ef6ec2981794ead1a47d797951e8bba032fbe5e5fbc0c48a78764417561a9d163abf62d10749a9f8c0e5fe97974c43a3e22fd9b183d069094736543af9707fde8c3f3c7b70e27886391ce6a50402115338b572028b6780dafbd73c757da226163d25e60fcf33dbed867e44c71837bdcd48fa8a048a412f542044b1a57cae3f193ca693860d8c5a99ba178a2f9701aa638706080c7cdda6d30034eec565b8c9cc0a2956a98c6cde78d379d830ec1de07b796e29bd05fb3ef194f30fcf95f4a014b4648ac43335a442574d14d28bcd5cfade8b47f3a04528e3df87fc40eed310c05c72ba1084fa11f8fb00d328b4a598d2e1f308cc157ce34e372dd4a4ab4daa51fbd758a694c0b456faa04b2dfa8fb62e21578df94b8f5d7c17ccf69a7b1e78d5b855aa31ced252b4d8499dc987cb814677c6a2e1d5b0144835d35d0342391b2c9fc5675e0539b3624e8b70f0985155a0e926c89262b2a0dd9de344bbb12de55f4c2d831f8ca57ba3d7aafb0de8760ad4c2fa1dd68920c5cbb781de8f50588987d9ed8b9a5a31effdb2c7c48a326a925fca91fe34e81306ca7840bd5ed530b2d3da00b7fa47842a40cca56172c4b75e9b846e3e68b28ff9bc93b5823ed0878aedb3b3da7ace3011100281f4833bacd9e843274d8c61106431cd91683e8fbd096b0c8dee8ae9d41530e0c3af3ea547e57ce92dfc137d802ea04c069f1c7f71befb8d50398628df008960b26a1a648986d613f5575b3956c5976a3356b09810a4c6e98a1cd103d5f73bf68d0db59cf20ce28757b325444c563096dfe339d3c639d752b700bc14db2309410451bc6bb7cb7e49a24295c90a4f4942d1361d8495008e1e6c6a82cbd267a1b17a6d8884f0b3caf3d567dedff845404e6341615c74c9fd3b5d3e00f4f755dd580a402494d6241a86fd491401b118df523afcc24b24cadfc49654c1eb0cd113b1730869b3c3d72755c9449ffaaed01bcb8c0255944f86113b9ac94dc00141556b265b584924ce444eaec4baa44c4f52adff27555cda04a242ea2c3bdcf1dff2d3c2b05d4a9595ba1b2834a1315e7c06ef39013708bf2c36ef52dd5d3ac2c4ee9defa4336dfc5f1d637d300009a91ce147c742580f634c27fd45af1dcc4bec61aac8736ad200ddb153d459eb993179a5762271424842978cc607b322c03894225825b5c87dbb4de99be1c20b1f5bb5174483e64180c1c8e100a345857e695434ed0697c127aed7632bf64781a4f42efdbfef0fee4dc4bb6aea5329818334c38edfb5cdd376dedc4b68787d39b085c29912b50d5442552a62cb92041d97c954670b009fc6506502ae4988727d4fda70302c2f52611dc5874e682b6a216f0b2428a871057df407640aa4cb329d0ac730880b9908b7b4aceaa5f92edc6d362b0d4aa72f7ca1731c177aa3080f93881a6835cb1a1be1ba0c9750e2bfc10c57d91e46406eca76b4de8b2e06e076058f06c02f30c46b0af71d01560bb03729fef4c465afe1677f13172efc1b04eff5272a84e2b08d92e1a9f88179191b2842fea8b0d5b776481aa73cb297fbdc4664afede8631dababb6329cec279fcb415a1cea4b146159c24a3bfd1448a4dd15e7dd9df0614cccb864cfa7c68e9a1333e2467f9e4446b03500edbb57404a0eecd08f1247a8452c697775eb99af91c4c799861c490c2401671896b96230ec50762a6ed9d311146d9f6b5678b7ef88fb463d1299e8ad3e5c814405600265d4d10c244c1716db39659eb5f105f1a6376d9eafdb48a02d3f3c4d6e45bb207ae473c55d77313548aa64eb97a49bb58159f5fe1686bc7ad8007c93a5035804f2b05d7ea447865845c7c73f6996ee168f7958ef301bf4867a275c95d8d4c37e7f69986ce677822eb54894886f12ff924968ce47cbbcf7b25228c6ac7a30c68eaf14c9f7faf4458d6544c7564ae0909c13e0382ea518979c67dc96a73a64ef07aa354f7cb53aa5583732c54389cd7ee0dcd894b565ca96db018139a9ceb312b0d7e66775de1f9003c38b4c68210f6c3483849466794713b3c3c46be9001ddbec50c74541c8839029b8aae8912564278165c0fe124b87577cce30079f8e3f1f73d2c22b72fa98cb0baf48e27a1ea176ed20599414c4d12a3dc3e1c21d584527a4d2477c5e0b21a94d102877761815a21f829aa012f652454e14278246f284bf5ee96ccc780a986dbbd1bd150f9c564503ce8aef91b05e60e16d926fd59e0589ff5bfd2d42f9dfeaaefbd2c75f109271d1068f71c57c08f675d4d56837530ceb57f5e87294f951c481424359620220e867f95f407b2f63d28dc14dae1a5d4cc34d0f113da080207531035290ec9ef040bba745d148ba49962f854749a55e45c9788aab6fead516736ad8025a5fc63e92e4d8c69edda99e098a9785bd0ec8d7f21c024e29063abec8c5345c73a127f8cee6c020f4e5e48a2a1a0c6fd2d8b7ebfbc17e5d2e3846213f862d357b5d063cd20ac64f11fa119e2be4ecdbbb573ef290cae652d6538430ace4be6c901973c571c75d3fba6e0f7599e04da256b534dd821b4a0ac31ecf1b2177888bce6f74c796911eb660313b1bc6c741008acd0c088535da93bdc8c51a0a390a26c9c65ac70e5a7afc89b388fec086a419e9c08bbd1a5ef627f87af9ac6587a1988653fd6a2e4df44ad7fe1c44bdda46300ad05f29145283270a4504b87083d15ff538a804bc0fb3b18fc46b5d55c8c4d965a8ed80ea75980865979ce4c69e0020e78050c149ab007140e5c186525a30231dab690c92ff9c5af2141cf5811f87fd81addcc09fa12852fd50f3686df41d1c161394b00b87586a67e220c2b0b3ee2dc82f31e0ec0b0d1b31a7e6523edf4a69eeb54ca07c6d91f1ad384d18b0803169a1212643237a0a01db65fc0cea36a2b4f56192db3d1b557c56e85cc3f8fd63010ea5979358093a0842388f2f80b9da40b8f70f473ee20f44a899e13925f358303e603e6978b8073c6a5263ff649a8ec2c06fde3f988e5982168bdf7178be974766b516d312c4cbc5c3fa153c9d1b941d2c10be3fed8130ec820e785323d05e31cc68a0a5ddbe30a85a830e831e1f26a0133d6483300ffacdcbb79ab2849de617fd0f9c6029fcafa12e57bfe420010560ef8861906530e589548c69127b7322fce3bcdd899da7d1e47b7d3d0cf093db181c9c1096760976ccface6dbd19e57780666a5bd7f51364b731c5b08cc0c20c2f85f44b2118ff6dc6bb498b35b2a24c354df54bb2228430b2f43dd09675311392ab753368ef1c5932296cb7c4f7b45a5b2776012b9b52fa943b0ea6d7abe09f478cf3207b567c106a988e357705229a7ee1714373ca9f1666dca0e8bf86d70a5bc68168418d7aa561738750ca38357ed2e9c7c5b14badea0cef976453fbda87c491958a7bf9b332bfeec1bf65da27124079daadceba65fafda170a0ab00f12ca8af929c2cd95ce502a32ecfb7c0a6b7bed49c7fd0ab1b8cac78861694fd513354b6592defbcddadbbfd8cdee8e4184fd73cf9ef19f230c6e48899b91688f731dccc91ce5905971ba0779107da53723a7130fa3c984623ea27b3e7164f4409918dd16a3ec41bfb32211967b01b800634280d01aa92ed734d8f277c2031532567bf623642866da29cfafd38015e2bfb7fc6507fc6ba80182fa7af191b7ce470dc392a50219cb91ada4828180ad2d34741f83a8d1687d3b522fd53edfc122f035c689058691b8b68c28e3e2feb960cc90b5fdad2cf0063454c98056fb15f762d40ea5dd922b14222d574d4e0b87ec437e01010b8921a26bc3483357c81239004ad834996ab6e6072de1172d13b3a314f8483f731105c623213a5c561b2e2036cbedf334ed7b22f221d702a727b40c27b04162623be80905e7cc9a4d651bcc7947ad49b11b2b945ebc3c366ec451411304206741d38732f108a3d4f6c90369bc1b60122b88f935b1d74e5c7a6c501f7dc74e4030bee3e1d05af36e55dc58920df7354a602fab0d5c382c221070dfdae50918a263294749bcdd39b831e9d814e143667cdcade992ff6e6f8b5561c27448881b27fa8ffc333e1c5e62762fe753ce73f26a2d793a11c905a0a86741a82238117d246ae47f8f42bf65f3111c40222a8b89962af4259d12b6a90656c9cd752a1047a95f7614428fe5ffac7f61f567570c5462279f2180895c0c1608962c328c871488d8fcafccb37038aed4500a23396fe6cef0d42c25acfd2f2831820bb2f28367c86da23f582fdf99e80bdf05443e3cd08c54eaca4285aad8e413c69d8216bc4866384ddc42a6ab965885a083d88d21283cde1294dcae04f45487e52b9c1aea9d7cf24e8b0c5a108f09050e6e59d1410d6e7b4168ea0f7a2853c891a5c7d431b50193ad1b08ba2f69d9ac9fa57b7ddf5641de14606824d2d133d47a641d2ffda4fbb265139e28733899a45631446c5050eee5b2e8a7df90628f4334652126d874b6ed888e926d6174718dacca2a47bd9a766efc17302746d16914733c7b529a06609a1e2511a1c73cd79ca22bd62f8651d5a3f66a506accde7e704b8bf771c41e1eb04c061f364189c4b873f4e6a186c660b858102566c91e83f409ddaac4637e5e314499d009188b7b41be1394e00c7f7b3b18157e321e6525dc3d08adf567363a932577de6aad58ac56361e4c412e5214abedb7132ca68c86a5fb59e942a24b413305f8c0f6c4a0f52eb387d8fb1796a1da74240aecfba0322c880cdf72f1c39cd22c770e4cbc0b363c3c1157263f027249c500f1eb004d598da55862781a4f8122d70ddc53df28ac61da37b17d52f2b274b86d4f2ab15de37b32db7a2d97ba0d21968d9c7c0efa2315f79ce7d14a6b904446bb3ca92c827cbc4218e9099fec778799c147f6ae37083f74c5a7ce9a87885be5acf5077e3f093e6f8c24c0fe087e34af71572dad2ab3085465397cb6f0291a61f97ec23c9703b2c22d27d4b99d6c9e68f0f8740cf3a0abb434875d1111d5c7fbf86911789f053d49f3bca44652964800507de02473ce59b716257a8c059f3da51415652668b879f482db041c737ee41aaef84c3118faebcd7f2af42adb9947d13a9f568255bba055c9b7920fc636508ce79a9b98de6a05de4a6a506fb3d8b620d1002b981df8b07aafa22a099a2f84211a159374a871ca9728348ded20f3b30a49f5583d4208c42c1a9a0ccc3584b001602e2e3c47308dc1a882112e3b2cf6ae3843bcc06d6c9ab2417067ba1baafb4bb3fa623b3281504526718c7861813e1ead418eed80839790296900505adba06043d95efab3ca0a04a7e37eb7fc99c45070505a8017dac560730443f961a8d18f1de924ddab207da8a05328f7c38a45e3969e44db0c4314e0b9ce3ee404ac54b44ee800473f4b82b065f68966f982088bc9cabb83ca7803aba08e38682b20bf497c81224024f812719be280061cd0b4d15cc1c74da8362959fc19e7d01fe12fb02196c6d072622ba9d838f115397e0163f7ff490723bdd74591f12513a39e9995217def063d14a2b8ac78955c0e27c078a4a4e09414d39ed9096751032ea0341139d1302e1c0f1c55c3cea4bc1e2d0711d2c8a218305be3239d29472981f05faeafad98459f55cf4f2ac700db31cc5dd6cd6a0dd3b2650bea8222fad010786e4a8e2bfec7c595bbedfbed7561598294e2dfe452adfa908884c8b13cb5ebfdae5df7c0faab7ad5bacec19b54e84967f2a09d490cb4892aeedbac1603d9beb3b30bbe46c5728e26968fbd36be8531a91164c8edf66d29a97c267f1c241eafd97f1fb5105ad589e4fed285963dc09a45857ed6beb27392b040d1680ca457e0790fa0e0891481df039a147c10946272d7e48ffe9cef35530a2ccc969b5106d90d9d6abb0690b129cb756f8b91b3fdbaf9c2da6add27c41b20668813cd288da71e1b9335e638ac34ce12cba045d40280e6872df8a8f7046ad982c7d54375f3184ce14ddeb07029144e719917ce1266838053c6ee66a1814de84310b64ead62a96be17a0e7a10af48b2133012d920f36d652eb2faae56e715f1f0ab5c3b2d482371fbea69c19b9993444754d2594ca29934670408c81bb27115f478e81f8724eb999067937b5915555485650ed5ed7ab2576e97e138ee7f164411cfb50fbc2fd9480edbc6fc1175a2c5b7eb78a6d98a768332ab1da19e0041170097b2d9082195386c13893cdc9149614e29d3b7d9cd11c583d1669b0460d594fd919bd425616b11d283671b195ec69a603401df79db7732c04eb8599fed9dc5b62ec2d5c006b1f1f2d83dadfe4a7323b40093b9246717752cb7e65e90f62243747fe2987e856b7daeab152fc039fb6f26564833e6abd83a76a60ebeccf0d0bd6261e5a20139efd21ffdab2408860de85f0916287044d259a53f5e18efd01a2fb25d5078a44c0245000204e33e195703b8701e7bcfee2071e242ffc5fef1b18a64b6846569ac04a4347ec80fc18b374b720e3efd919dd78c2e8fcaf7e3182b4d8a7ab3167fdfa38af4d8b65bad2f82a8a60f8e9da2553f9e67e23572bcc447c072998d2118fa4787f5d65376b777dd27f698787bd990926284850ff3c5dd7657d186d4e4d3b2e6691da462a84a6a2c696c07d7e98f95629937534d08a6cf0652ad72ba6b64158e18549e183c274916d3c9dbb488b8ef5dc44b982ee0457b640f598c4065cef04ecd8cff4f9c048155231c75625a605029e381fd70b720abaf3748428bddec56d56d69e6d8906d32f95473717b571d0efbbbc53a63cffcc2e787f52d5b7b9d9b72ccc46766ca8dfa0cba132132def3b12c7132809df225714d2b5397be90865b2187157faf65fc07d9e7e7b402de36b258c51aa819da95529186bcacbc452977d3739e71f8370e65732031ee68ef6d6e6d74616df4e36a08a23ff30ac03536c2c60124438bf34a2826387723ae592f7f568f24777967fb9af202da9925a89fb58006e3db283dbe8fe38c201b9b421da8b3c9e9055c98bb2b4e9df5aaff21b4632e1dfb036558fb4a2b4669a3bf56cf60bb5c77b3aa117a4a15a518d13239e2206d09a4b53840dffe00091520df8d7e75dd39295d95882b21c454b4e209bd42b162830e8bf7f96548321abd195d698f371f1ff3ede47c121524228726f1c78a2a6e6b62316f5f400d6ca85433d2264441645cba220c317a7e9f6bdad69e29860da2d9ce801582d9371538fb0754989501bd20bd5510cdce6261cd1bf951f03fffaa11a541825e982f3d419bfc6affec3d64c9622bd93dab1e80028f7d21903a0ba14deca99079fcfee047be1b454fac45ab618ed190391cd69ba0f06d16964281e838874d30d674a05028418f28f6d4358e0ad2b3e187691c83ff119bcad2168130ac179160ffb4fafdd42a85ece6e0ed3836f8284c9fcc7badf5cfa08b6f8dd8c7de427e01671dcb9dfdfeb88efc756fad02b753f485d27fd0f10750384742e490811a0df075d8ddfb9f89b91db84317fa6a9f2f3311b441bb90d033862b2b0c4c2d4225b3a8e506f02d9a6809916169f40bdb1a2d3797a945a2e3035531ebaf0a4e5a22ff80ccda01c75bc80cb51a443ccd8118cba8f907997d1b6838c4381102cb7e118d85eb88742ba2d4a0f95500c28f3126532fe8d83e774f707a7ba15c4d572421e531e1ba9a48e30aa58f2df2481f4681d4e7a91a8060259610d72f3f492b93d817aa1931d83c134cdbc2b7cc210b0d8c59b39a1189c11abbf2fca25d2e4c9d7d5dc0c592970536423940d38adcedefe4dfb82eac5dfddbfccb4b87a9324ac25c5e1ee053662b7b8ff77860767065449710091edc4d29ddab46b14d2a6adc2941b3cfbf00da915e2fd80302fbe13439097f9c1df4fee84031a859817fb5bed1c0c059784b4dbbad6a6999763b10d92c422db5985b15f7371fc32185a012f832608ba05c8148322a76ecb4a58b305f8ecd5d1b754ae39015f3113d2217d24655704c9ab250d70d087e61283ac9556943440e8b9124e5528f6b1a95f9c8561254ad127131b99de066efef0d6e158e2b43eb83ed34efd80a36722e349d7851c3b4770ca23e7ecbd013ac9009c7846824896987a21aad29f745b60b826f636d467fd9e0775f4eaa3958b8ec6cdb55f3d1578625982c49e87ae04ac8a0de6bab2d6675200e91757c2bb587819e55c2d33ccfd11ea3e2f1ca580c0fe9156e49430c32c981fc9701e50e40f282de6968b85a51e2ad833d8a66dd101c8cc7d4f161d4e9b811329462e4c6ae3a2557d76fb94aaf870ff7dc450c59341956d140c72e1f0f39fac1a0561c5a5f5edfbe8dea88096c18f88fa291ab005ef4b2216aa29458a68f41baade82ee2b2a82dfef8b9e49f62a5649a069f27db03f547b51df5624b2d3bf161ac536a895251b01c5bc9c8ad48eaf58060d61158f9ace219d50c2ca508a2d24355a03a956a402a454aa0e531e032aab8939b72b92547547e51f27b532f8ea8a0520746e50db58a8112f8e2ac5e1459eed3ae78a16e36e939db1bc772f1b8ffe340df329d2dc963979daa1385b7f8eaf73eb4272ccda1860647b6a72e954feef0220216ed1ccce8e9bc69457002a777af9f22ad7bda26631d7eea35819c02ed20440d77cf196e08df6a3f2300efe61f79ed558f064d3f0b9321395a27e82b79f57306f9eb39d0af7c76e422602881804397bf92d2d44a7bda1f4ba809271d44bb70ae909de3994fdbe8b545444d99c14749e5487d8b050bb470e6075685859a74b31649786abc1ed2b6d4a391a6530c41bd959f0b28b98c60d631296d7e781992ea2e1dec56dd8effa14451979a531086b8a4c5fee084ab3d362512bf73c006bf11999c04a32c8f4eeca2ec226beaa6a6836a4d1df9d08f6bb0e674b95da4a1440f3d638a3455745610df21123aec08109e92333edf3a5ffdbd8d4b8ba265880616b8cdfb851dad088077c993e09c6a95cb2ac3ae40b705b141d088436924f5573beb879f80216e1141f172d22d4c1e4ea62a490644357b1d64773ec29d9ef171c8cbb37b23f265057b15220aa045525ebb81e9f744b2cb244bea8edfbf125fdc825232d730473366749b317f2387dc0274f1db006575d6b99f6c8aacb091e4fc25e3b773f9440751a52909a9949ae55ba77b6d4d42c9d0981bc22ed5e9b2ffa44230025ae146493177d735f1d22b9957ee5bb1f403b06e97018836f367281df1b48f3035563564c26d9b557e0547e2095fa815435778fd84c691f8f11d20b0e5081b8fd003146b9c759ce04f997e6b777e3d398be554d060d6aa4b37131692530c0de72f01002fc0154eb6c36e215a2b9ba11d78a68df73fcde1f7da2bb35c9318e8888abf3679812dfada81adf2a1b0acf9b7d6e473ade6bf448e515c282d6422ca9c4110020fce98f03311db4bd3e4148377e279ab0726ec545387e8ce818f0851ee904b87919cce6ae9b8772003e695dffeb83346f239bf4032c4a0ca4b165972ccee7073375c269b1f3892ba1a688052879b2972feb050b168257f6d7312f7430bf385664a798c70028f8e8d94ed3f18f30a09b56d608a366511e23401846c0aff18fe00902be637f440d0f704f10bc1f0c65df0ec7b99d0d12bef4a071e141b4f108f2f4d4b6206d81ae9a765b4c82f284132dc5abcf73ea1952007996a811fb5b4b6457023abde199091af2064132e320431942fd952c3056498d64fa05f51f22bf7316e9386603e4ca7219c2994f7b9845f076c62216b2c50604c9eb1e17c87927dc2893cd0b949164b79d9d61fd0be48d688196eee8b37907d00266c83d60b634e77b260b5fbefc8a0cb8442b1e118049424cb87af41f1c72045d7d07a20520ec027b11866771f705a6730e1e43751b74a880cf76bd4296339d0100ab47eea986981ff52e25b2eaa703764946fbd503f59667f74caefe5df9b697c35ee53c31a5007f6ff02b369c32f376cd79eb8638643e1505d9f516f0f08a10dc31d2bf0406f717a40658f9d85f8a74821239cc5c94368380cd22104fe216c1986c8cfc41e16692d7d038fb162a10492272c2a6d49dc8c2b8f0b2502dde31f63f0d5fb6166c86b0fc4b2208aad8fedd0e63532cdd22c64062926946040ba2d08a7492471c78a65a31fc0f14133c208f19214117ddcbcf057058942fbcbc4161e03e7322d4dfccb0aaf22b5978e5d272c24fa137a7254519f0edb52c4dd60e87b3a46608262dd904a925f1756d375addd971cb4f2a1c6a4a7c12daa2e88ee9fa0143f685d8ef0250dd1d607e87875c94360078bd7101c3b4a12c9278927a74848eda0ab9372818e7204fc10fdb7cc71c325af0cc343ecd59f3bd1a13152fb7277fbba9fe19c2f73f1b933e903102905149a4bc2a788436c2b70507fde4d05d1bc7fe754b9d37cd5d281030333aa96fb66d55aecb0cbdd3f7da1b21a365b6fb9d3bd64176dd31f02a3a4a206cf4007fa9c9679ba1a8568fb555e0a33e34b50f79b10d7be6cc7410c664199cd95bae9a45e56578c940c1b1dbd09f80caf048ffb6356e715935457c5ae1aeafcc326566f9de4cc490fe7de954d81056481e01d599199c571d3150e9c8126713dae268046af531b95619d89193a60b2d8ab5805b2aec567736760ddca112cbaa16e2d4759e4cffa8f665bf477d53d1964f6fcce8530092a21b9d772f013dc3aa6a0206f39f7baaaa9cd2b65956de2521a6a8be45c43d0db52ecddb49922b660b5604a1cd354cd91b836d4d65f58089b317bb44d02a9f14205b08b76188f868136d1b23a506fe4f2056489b403560d70052ada25d43b9563294bcc4abeb1a60c9abc093ade24904871266a0ec06e49b03c1319d7fb77a0815e7dfcd18a41f980105dd181baacd302bfdda9c7f0300084e40a23b1da504a871aeb02f73cd97ac9c7d95204101360c8ff53e2a8764c9cadbd090ad1adeb896f3ef8fc7129e5a2317068deb551efa5d2bf42217b55880c45009260c53f781af5b7f8e64a60eecf0db05c6892c8170089045591fc704d5f16f04dbfe4cd96a197352be4330b0be58e54652717f30a42eb86d5bac1742816a00473cce686b577db435f7585cfb5d466ba0477a75193da25fb4f50339ffeeebbdb9689192bdcaa6ddfcc73edaf9775ae78bb5ee6312d8bf95f90ecebfd5e69ee8ac9cb46e0eae81e67f958d9a1081136dca708f48c3cd561a0c5ccb01841747c4bcc58bad051054c5b03ffb01493d39ce3b6e2238ff16b5998ac60d1e461f2c37d41562b52b55fe72feed90c5519f284f863096b14ed4fdabc4275db3d389d30607a634a8cc1944cdd6e1fa911ea51f9ee97b54f3c7bab1dad92986554e8ab00c5b715c14253ca2b1af9ec9f910befe8163d522ef354f7dfb3bdf13025f5be44cd3f2e2843c53ad033c809f382f06dcc04c1e3395008e3d2593b9f16f7febb43bb7be8ee6cc7db664a6d6c288dbdfb79513b868d7866dfcb85aaf81d4d376065360a214f6cada5885b0f90e547f36350c20d8970680177930925522389cad097cd5c3b3eea7c73310712723889a4330fb7c17c069f39e600e0a26a0bb9a314821660aac6318bf907d6a3462e03bfad9540431962327bc34e4e7e36b017cdcf07f1663a89cb790a4fac4249f7ee4b8ce74b428c75dce83b65d8b3aecf96b50942ac0ca0c10fb1f83a8d25647a44b3e87870c9e97a16fbb4a4b7f55cd8cd493d952328be1f32f03ea1952b6a3270cd9ab0262904f02850e1c48daa14856e448205107e4f0b4036d3ec66ddce3cfa6d3cf339f980322210a43db702445fb8a4ac600b815f80fc11d951c7f50c218afce8573221f27062d3bc48cd9df9a76cdc2afaea9e7869831841bc3cc6fbdd93299e98a6729151657b83e6f62875e571d98a4b3df092d01dc80b64777cd0c8e119bc30487617fbbbc519250aae3e74ecd785a87605ff383ee08314218cd8e8d3c8619d6d0b25b08b0f7f78ac4108f59d452879e949a8290794dbe5390d15a8204d0f58bd450f30348b0914b78c71a4fd2ffa14c489a2f84bd92882625b66fa822540ea2c46964c857bca71101327f6b287461e0e523ab4e3298c52ab66a655f3283b16d5d613edc00adac5c64c54939f1304cda1257093071d2c378cedf910dd05260d4788cb129442005c27dad3526a7e3b39a7d2c6c78fd6eefe48d61d7232d0faef1ce7faf1d81b46c6b9ab63a7f5cb4c798e772c49dd557a1d224485286fdf2839b5eb50fbb5f8b60d090917e63b4d451924606885fba6c736bf2ca686fa384c427a0013286beb4c47c21ca6c525d1ae9107b7599cbd77b8ac45daf7ebc8ff9d7512cdec520cc3e38072d253ca9496dd868c95c1de7bcb2fef3bd765c239b84b42ef207bc75b62cd67781b7406e107bc6a6ac3650db599ea0838a17458810cd818b5a64137fa27afc622f06b188492cb114ab58c42c76318b5d6cc4109b18c428a662885d8cc514733188254662f9a0faa036bc2e2ec1cef97f3134cb59c6fe9dc2ff40e18a8246adb35705cfe3edf903856743f7c4439de7b9f9c449c56bb079eaace771dc727253f038da9c3f56793ee84e3a54799c9a4f9fd5bc0c5aa78e5a1ec6eda777051e63cdd94385e7e3fee4838a5730ebd47503df390ecb382ec1f962b2aeca84bde8f8aa162e8e5a87cd1eea8d975f3a26db7e6d87041da72815c40b3e1a71f838b31e40e51f49555ac5a164e4a22d9cf621658987b8f4650154c30ba4333cdd4ab8986c6c3d9bd20e9399329afeded505233b75e80f480ddd09ed2d8f9bc172fef3a7f894eb6a7f5fdfc96b1e5175fa3be79f7a85ab6a6ef9eecb275dd5ab68bbfe8bf1566fe9c89f015fdd25d1b040758a5a70e9c7deaedb79c0d002d2734360ce82418f1e7e457f6d7ae12502bbe999d564a520863d10c33a1d80194902e2629c72533f21c4c74818744a1190f9bdecda54bb784950b7d1c2a9a9045672314686e6143ac8850df6d895117ab103e5d57411292316b153d4b2fc9ec906870b2e4089ab15c1dc839562ca68de93ca1a5728985497d21586cb19edf551d354ec1293db0e5a32e73173801c9c28988209caa57f27576eb651c4c2f4e010282096fe0a63708f69a3f4a7b692a9fc3aa204faa4e051ad85dafbc563bfde7a11a4e7070756c638773a9ba8ae13036b61f6002f6c551e28b67703dc033867b0804f22d60be3af4c42635009c2f59d48de9b6e68692b0b838c2ce52c70d7cf4f16c695b3f576732876d8ab0c90dfed4f09dd79b3d28b5208d67c614cf83d46fb6ebb84a599c8431b181e06ad6ceb7f8e0ffec11f463e48276a618854f5342a8eee21e42ea568630a56e384003dcd006ab40f957585528357f434526e81355c1f78f8d3613f0d2c452121ae9ebed33d41f35cab4081531eb337faad9e569ed7ab6cca69c6e6690a28437fed56e51ae2f5e1a3fff5b49e119cab8197d476906169743df3d2c85037105316e55220531474bfb0d27a9a7e74535895f30a3605708e12a45a5c608a99441e198193c3d448a3d713e972d1aa98375167abe99b2c33a65c65f16fb62a188b9f6e79a80ee63197f93593b22eee50b3e97c092de008e2960ac6da655558a3194982bf53920174ca53615a3cdcad745310854554c4d9b729f6a5b78f4c79ff2c435211a0310f16c06b2af31d1f2ed06f5d76b1a54f215cdb4dc7c91d2873d690ca928dd865627c307cc4384d50d41c0a8e16421644f13dc163dc23f8843718361a6d411be31d8f781428c637dcd9a99865634f07be508abdd2f51ed85d69a688be5d85167f01c7a6a76b431e611ea646261e5cd05eb6730a85f09ebae16c7e03b6727850265e150a6abfc092a1023301c3c03d2caccfbfd58e6f3c7c00459af06ec694a2cdc843b597293b5de503fa919671dc3af8cb944daffaf94cd6b896840fb92dec6a598c375a7596b4bbea60ed9f79b5d8f2e76d775c31ad22dbef348572da344f76e969c85757b71dfaceb615e56ddb122f1cd9f82a6c54a278c00799b91e83e10425d5f19e7b1e159732d72ae72917e66d8a0ee66c0ada6b6b7e922375ed4d5fb66e82ba1501816ad6ad96cd723d708800c19600861f4d1ebd7e35ac8e6a89b992b4081eec28c668723e148c387735caad5bb76b8251c7d5e2b568ab44871f61f0359094efefe76e1ee50d17978ac0687aca595c73926dc39ce7abeb8341d974d45b6e21ff4478b6c2afa2c2f046e363138627a889314dcc8bd3c0634bbec5c15a1c6b83fa62d559c06f3113700b7253d96429bcf3f1584aab5be9cf596603636a91fbc1caec3bcffbf636aa8b4efacd7a68629e33de05bcbc484b660a31de6ba371423fec87eb16091c9a817103e71fce01707274bc1c5d83ecca0f66ee1ebc4aa35addee3895c058464194845d1dafcb0c459e41c327331471cb2c2c701c6d08d8cef4dc8912d717bc29b5a93d22009a203520fc2cd30c781fa7cc55ca2a30b39e7694e681f32c603ed50e932ad5b66920a485ac438cc369e6558ff914efcdc090fd8594214b9f3e652fd15254421c558efeb45568059fad1a16a815642a04a3e02aabf601c2d552d4091db70fa17d5c2b34a09e653bb759eb4ef19ab34cc34f446fa1bcbb5e5b0af34526d03525308493f37343f74a41b32ccdf406758e8290620f949966fb7f3aa909d4166858f50355b0dc034f20f74ae00b2772d7a4ecbaf294ca3a843ef432a899c17d766e379d1f8826684bfb993782166505e5fb2178a5888b0806974aefa33fb816cdfee28e99502a095fc6203b905bea6e79dd83d27197236d2842282f458d5a99763ba828a9d1a18bbc9a3c6c1fc29e93866e16015c2f66af753f47e9f8282b7405d177708b9b5f35cdb38783239668b79916104545e7e94a0516b12320451022ec1a6d734bfa0d2e295734d5693429d8ae316ac7f487e65b4096feee83a6a385f35dfb44cab51bae73d82db26a01d4bebe5ae283f5ba9898e52b83944c8eccccf1cd80c57471e690642d59a32780d5386ecb222697d6ac932786472acdb66aca4534386ab997080e23898a3c27e5dc566da879a0a9360d1b3c9fb8759ee801c5e4f15c5b50dc628cb791187e6288894b987ef2f101f54a9c915576209afca34f10ea5ae183525dc0146b193e031e4490acff408fcc3651e7117c3eef2ff3aac8f2dc8c7be5c02bc68f2634f143603893d9d5ddc869e89b76add8067f9762fb7fd8553bb56433851755cc75788acda8dda23ba7e6f1c69a60b6edb932cff09cdf411f45bd718c3ad034ec7a7dd0ba0c09e7d1b15a65e5e0e12af7bca2f0533cb10c00663bd80887eed81f7a0d1ffb6f8da899d65d62779932fa20535a20a688d20071b2a241f642a0709a77a875d6f0cd161bc583413de01325bca7ae0407f4006f5b57ae037fcc6ad0ceee78041b3ead096342074f0c36292cf74875dd0487164e4aef8e7a30e4fb1b311b8ab5433e05c7814f59f7140286fc13b63b2e6e5c71720b3c278d67fa0d59d2667b7ae2678bb70d651b7b0e79b5ae61b3703c27341885c7ca07e0a726f66c6263e4fd504a1b1b04bba7b8a9765fd4cb1b9edac491fe75d3cb13f287273c612d62037ece9f92a94d6efb73d6a74d8b878c36192342b40220ee292dd88d3da049616162429a45871a66e22186f2547f54d7374c3a39829889c0e84911a6e634787e94f92d7086319197fe8d021cbd8dc54829be543948cbe2b8aacec34202c541e2854a4b6a30074bde60b1313e65362908f729efba3dc3d92c714ccfb2a2131f1a9039a6d93946a4b04c24e7a8c5dade7cac4503c965d02476816d559c31373000786ad66e8695f53fb483607b91caaf4b169c6df68d6d8ba3b039132b5188479ef17c16d0deea0acb4a7f961546d801fe6295e069a96e34b9bebe6fa91266ada1fa212ead33b1001f5013249bb947a429f68e1213c3e1c85029207415b75db32bb1842fdd768eac568322a691edc48c728763b9fc7a17c5f71f48d9c669c648284e6df56aa47bd84b712297e0342ebdd31d07484b5e716474a71a064fb838bd8dd5391d30e1a537c1445246f74d4b92eedc2990b8c40ea10a857777a08469b9c8fd8b256a4d6da9827d13a11bbcd49565658e08558db8811474c0c086bd1e8a25c100f51c1ce9dc0898285ca6478d56e734a8023063239f4a30645ae0aba737fa5186d9cdb2580cc54bc20058d276dd099e59c904ffceb2dd13f8c7682b62f9e4aeacb1d434b22762707683a50d46715b63035284f337c0bcdca6b13cd4b943d24cfaab738de42092c089519287fea91fb40d8223b7116a4a0a2080bdd7dbb2f4eedede146444ac266ea2df7aadd47c7fa3ad9b9832fdc966710417ad6028bb24ceca791ff22ef5e71c57f98a2aa8a20a6b8ae88881f85f489eafabefa7c901f5910e958711cef1c78742a288e9814b8e87f0a46e45ed94616c4a0e14b5f1857818b959ba3aa0ad396a551c9e86bd385413de9a165989cde21bc5263cffa6f2db55f99d18ab9f78ad23455727ea3ffe282753397ffbcb681e103d1cdcfd5b0a6e9cbaa1bb35a14084869ab427dde546b793552df0d217a45c6e885b9cd344b972910370c6130b7bdbfab1ee51af35e68f54207c27dbe106f527d660db7abdcf05c653dcf5754f0ceda6c2f6a530684de50fb8fdb27058db71b3495b7fa061a22da7c68d02c004ce048e861253a0537156899625d67685cee75f2856bba9f8540c62e01c3ec1f5f927527d639330bd65485abb652a14aed4c6d019e4497172183ce7196056f4f98f7ea348cca50766cef58da64849e0cc30249c5192a8ce86df5641e5b1bbfb5ae7d60f5962c1f5d08a38f9e519895633c129fdb9313a46c01b7b018a2eb0461d5d0ea377cc922d56ca7698016c92f2ab43a74cc6cf49cb8efa4ef94746ca4cf543f49ed0522498177aac8e9a43e330b2fd2a4ba5fc201f576e6388e055f20c722a7659dcc29f5052795aee158b618ddf62ab74d0c5cd1320013f4d4ee186ef20852a0d6056111cff4c4eb9a8238ca97092cd2e65fb265335d1001e91ec5b456665a93fa852205be7a472f0932e199794bf2d0249a6ea49a14aac1df147ff14713d02373231c21bb9d2bf3a48a9d3942c81884bccfe2057a338c869b917adf4d4af062665a182966453cac1a89daba2e2eec89771621df36daa7e2167eca18fbda0d98cbb51e6bf0721e353fe3dfd194cd2c538c1ae3d39f22c089e96423cc5ce829a7c63d3ba68371cec95333f25d44d0dbc0e9cd950c7a140090c04599c2faf5e139df5f699a035b86e2f19840fbfc8dc6e139fa072574bca5c8291615e1b515670880bbf72a8e1a36a97772a6a090a8608bc520df35f6c7f76cc8576de4d452d8b528be9589a82769df80584939883c4dced76abf6f8e932f0c0448b65eb0207d1603b09e36129f2e10be0e41466b75a4b8328a6eea632670f7a35aedf3e300957e8da8b410c98fde0d1dc43e3aa08269b33320e694b4f25eca1b8024d6278fc6cf4aca0b53d07302c3f87d0a0a1d268585b41fcf2dfe033d19c3fd25ab9170a784739de3eff5bcbd4818bce63116a98e636f5bc6e2aae8bedcde5bce35c0fcbfd535591f0102964b91d79206005587ea61ca1d5cff8f534946364d7735171aa0bd9fcc951457791bdee3c80eb4a30bd1ebdbc12ee32c0dc2a37d23ecc21f94276e219b22a634d571eab448a59078dfa105e0ad08aad2aff3bd94fba4cbf8cd4ed81057ef11609048312e090fd9402579ef57494cc29091d810129afa4a302cc855325ce83f940dac4ae195d849d00df031cd4459885e3ce48b8250e18c441b3366639939f6901358c4ce274165e9403512220be273320ad468c087c91b406114219f00a1d1d5257e4444304aa0b8fb2708c43c0133134880d9604a017e83b4400ced98bb4449fb5ad1b80fa8138814cde6c07e67c834e1f228c4e846b514c59990b59edb4cc6a48c1e7aeeff1a5cb1962fc47f8d23e58b4222dd74b1bb346a5c5e7fac800b9ae5172cb754e331e704b7ea4d134a8be13c5332742ce79f47f21e15583e70cdb268cc0bf29b4a3220acf9ccf6d3712430a34b54f1968d5bd386e5a75964b9945cd3cfb89ef4b3e743e669bc07a61965eb3f8261043877dcde449358519d90e0733bccc8cde9098ebe03e6e0b1a41bb028047b3e9868e4c0b448b2db4763be205d89f95e2bf9650c5fc17c825ac12c319b537e83fa3805363d9b47c619518ca75d91256cdc4afd8cb316da1208b35b61dbf6f89eac053a833bee37bde98f6533eadcbc8351d893e11c85e32781ee26ed5fe6d823fb87a21274bf199f1520208209374185402d5182b2a81a9a271588e2eb8173e2ef03f96db2610503511db9757f6c07a5b0ca3862ff5202f6484eef6f4c716a85e839b02b11775964babf2fe5e8bae8cb38c91bfb487f8ca6bf34d63b1090f02cd98cdee190fe884df4fde817ad8c68cb069ed3c708e09b337b884b4c2c9157e24be6c3e502e7172c7a4ba8fb302f36bd11d8244650e1bef94d7be548dbd59ccc55eb2f6d40b2ea3390b9d33d8d9184dba8e9b0a5050555964e88d304010c6a35d2aa2e6f01a94558c75592b907fc6f82c9c04a1aa71d2bb6e4c8066db22ace1df8d14304a3509809a5efd5466fc0664106f1e911d6204eecfe18a44b7f6a1fc0014b6af3f01ed6e36e8dfde4a52189262090828c08e2c953994d9bacd5d8c134f944903af592fb94cd6540b0e3dc040c6e675125156c3215f6074dd730eab6b1ea9e238b328a2f28d336675f95f8bc0586f8315b32bcd8806ac66af669dec8c8727577a93af0c4f482055913c7f2bf4ac85763482a11dd59255a67fe0b8fcb9a0e451922396c21305adae3a8ca08d9de20e75b5563ed1146e78ccc7f902c541ceb382d045faf7c301da460333b8ccd077976cf59aed8ae75f8d8b8c0230ae6233708f6d03a0d2e95ca77d8614018f9eefa54f39bab0c73b979a08579ca1a04921b61e566773b43f70894ef91cdd27000f7ca1078ac3490222ca4f265ebaafed9d05c1d8bd406cc37159477c4ccec90e76cc6891c9e004e3af655f4a2a2328528098549e043d168c46b743ece8e94ec7797cd32bb61855a9f9261240dbafde331117037c88e4923b4f101c4393b11c5183e75ae23c9985cab1c320023c14e837e6ec5ba907931f91c528a1ceaf1f15e0450a9f1887f3e6f05161d97306e1e3fd77a012ecd025c77c23f9918563f1765007b3deb4a3808462b458406f1b182a2f168b30a8e4d0985219a927c97ae89fe29dc9f9f145b6c51539c815c98d557cde01d5b1a55fd41664fa9ef4f347d570343053045842c028bb3c2257bd0d888fc099109d3f8173c0dbf0c2a63f54a3a08861960603d22db0e9d547f9656b66ad7002cc40ebbeb4a897aabc17711074ac74b6c2a39cc19cfabe474785d0db0befd8b6301ddbb8b225e10b851cb170558361223f95cb1a06a001e0710e734b2d33618ad3fb50a00e323859f3b3f3d39acb2fa69e9fa20498601c41d3eccd79a9184285144321db3c472655c1b16430ac7f3f477348310d40592707ab5362ca77bfc8e8e0240938261dea5b51e1936be86d15ec546b6741bb02a7f2c02f84e42211f99a11b2fb24e6afa348e00bbba1a5c67644442b83437a08dc0cea55732e86409e9d0a00ecb8e63d5a6b41ae8b4c4a387cc069eb459065fe3a0ddc27ce177f30c35c58893855037ac46e81e69f64d65036aea0589f112d5df7309a736091e436c8c436ab7d78ccea01147bef111e8f711d5108776c32cff83587533b619c3ea38bcc5be221950b5e364c970fbfaa1aa774d6b710b8567de204d00b6db2c250ba75cdc61efa2c9851ffd3a83f2c4e99b751b9f5582b2f18ca3f4ccacdf93cf8bafc03f47365becaa738534c1fd2409ba614250c600f67e9c5fcde4a71f5198e273641945d96afdf4efc8cbcaefe62c8237931987bde611ac01c8cdb89c603cbff4728e37c528984041095560aceb56883624745101e72840ddb48c2b500800878a89a7007ea2b0c4056c8d8a16d41d4383b7200d78abfb175b1ce373163dc2fd49b86c7a38f9725000122b3534b6177d489e4476c3594abf088d5ebe3f1820cb135e60dc106dc381270bf225e6d26271a324cfc02a6b9a385c719cd0751f4b68a70a37ff43d06f2d53d0f8204676f433073e5351102c1ae50a5064f9d747d0b2414a0abca0eaeaa96e475925a0b22bc3e12036032166af1591d510be94456b311640681c48e2bb4aa16584da403397f658ba6a188e5bde202a3040c78123b96b433d88622d5dbb4050d8a7afd91b71a1ecca1c5b507006c4c585116c5f0f915ee5ac0b33da68140657e665a15b13d80358b5a323d09abd59546502421456b8672739de6554fda8d9ba3c8bbfd2992ffb5080ed37d74f82ca8a83479ac913f03656736567d73648a62a09dd8fb20c97549567ec652c4d978419246431e4c862c56f89374b47092584b4933b9e3a37653df8356118214e45d727911859a1666709a0a866768a919c6b7f9d14be9f1fcd4c165521956dd5c7aee11115d513bbb2d68425aceb6594a777fafe735b5706232990087a2f13ac3bccab356c287908da53faab829b2f5e48cc0cab976191012bc55cced1efe80d6ff07f7b56df8510237193c6c17c4a4fcad4f2b624332f8a2dde1e45ffed36d9a3243c9c2ca38828763d9c41e9b3763cbe522722bcda31dc4ca155f37875e13a1cb33aeb442521640f9cee068cf57ac507710664cebf527c3e5a36ebb929cbeecfd1c5871a78e56f2bad4869a4968da52a25c5819b4f46da82124888b7c03b6f8e40f5c696ca8b9c4bee281f43e2e789927277198d4239f5457316a0c69afeaa165aaff562622ec08de2a3d7362aadffafbef4b5056defba6b9ec64dc45b9859c150e5a20eefcd5765a235a15fdb25b1a25610c9e0a819857cb43a8de43fae5a0cb4dbea90d153f6817e336ae04b8980cc3a49fb30794a9261ea0018bc5996e0ed0c2d076ed98522898729c89e6c7e6bb63671ad847ff5e67206908093cd1f6d81a944563609aa2a5d7ddb55652ec2024d58e6a18ed0cc847cb950b434cdd1f552c03b26a106a061c358e241606c9111a2fb54f605a3899b05ebcc01970a8b659ce10e19edaca1d904122570ceddcdfd80ce805751e9c40e314106dae6dcfc99ba2e509998d5a3b3474c4d95a049db4f2003decc7d80469e2625e7a59e7c1211f4a1b9c7facb2655825ec6a5ce29e25d84dc3fd8560efc93e256e0118352eea96ddc250112353794ff03d72eb79f96753f073ad48e4291e620389cdd3a1b00d116027b8142218d2184e3703346e641e3afa9e52034d035467e5cd36bdd511bc663fba7422ad34a922622db258e1b0ff213bbebbdea14252699a3e1bd4d9f0cd080860c75f430fb523e83cb0913f9408868a4292878fb39a25ff75b2a1e1220d422abd9a64c3732fe8af434a560b295c6d35212641c66b93b7abdecde36fe941dd080287300522ba6f1b75be209bf5c9ee5153370ad5206b9bdc73324b2f05dfda2c3d1cd3e15099ba148184c4a69c1aeb0f3e2bb9e4d17daebccf99e6300a0197a6acbd561df94dad042ec887f9811623bf70b00fd4436794989e1e79a4d413f215fe447090efc2851fc48fd06f279a8701ed45c3b57e5fcce66fd6d95479503ab60d5bb67f28db5037abc3d0973f9345425d11ccaf14e2c27d42265f8c40b57f9ca88d4f4f500071117daa5d92172e6df97cb52b68a58e0af66689a4298baa1eebe79b714809edc426bf93cfad18deaf5c79dd20c3d2506bb01952cc9f6e85cb93c1233e0db9069872b8adb609f4c8b0d211416436677a789b9c58650cabe94075d8f77cd998964fb15cd002fd8cfd9d25a003951aae13f41cb69d533cb0a048ed8b9bacaaa2ff941af186a61d741b356bc738898229ac56c6543c4640cdb4c069ae31a2457c750e8f9a6f280fd0a1f176b20f2e8e3f399648c66f1457b7f7f634b2b26dfc931ed80f53061d9a50dfb3f2f4aa2a5e720d1786b7805ef5b467f5c16ecab2aad4bd77c82e25d7586999633dddefd1cffd01736e88473c5b6f1099574538d5a7738d6b1cc304539dc008a6ad76e246a951bd86a88d0d32bd6c1c5310a29212f7e11ca1391ab441ae5469bbac713a5065862067fe697b15764acfdd19fb4389548aefc457f27032c6984834cfa2e9d706f6258d474d725a20f22a864f49cdbad164112d7dd79af522e6197a8d1a9e0a4816ecedf400405a656a8d6fc6932ec1f88329d51afa66ac24aef5cf9e61340f4706d8204b2846db1b73d1ea0e03b7e4a5ae3479a0aa03d7d0adc3cb2bfe78148c2f324e5310fc32167f10512a4e3e6f4ed4432362957838a9f14d74cc98b4a1681f98c276618492c310f2291e2b458f159fab3f3833ae869fbe91b39985403f86ab1d9220ab6b85caa6f2650f49c6735f560bab920dcfd2646d83475eb3e077221100c84d6790786f6a21228ed03f12cd977cfc3f9248b53a155a3886954bb0aa03d34d92e26bd235194bba6280e3a7f3484cc2841e27a2542942021569acd84d7a5d418fae89fe68f88256014c5e049e4cc4770e56105887dc1c7a86ca1fe539f5efb0121e47b58385cb3f3cbfbc4abbdbc4e91c0820ff610f2902ff188dac89eaea2d7a31c3ce29b909c204735c736872cedd879806a2bf27e9a3a41ae7caed4f90f5f6b6d1922d6592295d0ef00d000fa74725f1bebbfe8f85512cd15bb74d3a78e4e72d2751b76f37297233781ce4657d6f59de775e182eb13fc37f477c2dab93e0b7a305bdefee5f7192d377acd3cfec376b8602c324dedff1bff7581e26b16f2249fba8f0075ffbf7efa902f72f0ff6f9cfebdcba141454f183145d9288420bc25808ac2e98ebc4ca5bf8bb071e45145c740085194cb1842dec037cc7b29af45107f6ebf66f78a4490be67625f0b77fad13201450d084329d17d24dac8bc394873faa0755a83f791e089e7e428cc14fd18c28eca14014ca031fe58148502fc4432d2e1ec41310fd6bbcc036a66cb9de87ed191849de9ae2ad2ab766694bd2161a5c7ce78452ba1e06c118321e63ce0be9364e178b0d168ac4a00782f75eae0be74413de72bc459ccdee3de2fa8e0dfc4d7aa382b8963c538335c3648defa371e369dcb0a81bacef67cc907283068dafc19aa1aa90345ec85dd1e8505cf8e385a71a0f3e88ef57c3060fe47d0cde8bb778d8c3d7f3700dbcc5310d63d01b3906884a388f8b0f97b0f098fee0ff014110630ff585d7fb401b2fc43d1b2c0f75f21e3cb16622794118e4fd7e066b8622ef8c19bf5aad1ea4c13ad5b0610cb8061e3f95d7248d197f5fc85db17a868c8b0545c2f0241734637b4b54a5a490e065994e56098376c9036d10a00983d756b138a607b1c92a79cbf4971504fec1f731c617775e7ab052448e860dc0279640865cdd08435a2ac87e62094e20417b599e145f22ddc33ff74b3d07a35ba562d1a0dec50a3481a7cbea93c913bde518e3d388576f31b8fa136b85df84c9ac6ed0a8f23e342c1632f8c6f79fc0bfe1cf0dd722c58c6f61b9122c6fc2bbb83be1355010c1774ebf833d5cf256d8cd7025ec346e8d1b061c67b8c6384391e06a9cc120c171460bad9c58a1920963102f19bb292326d32d21ce5ccb9de28af11d1b6098301472675248d6c9b5f4abfd8ab7b0f811ed4b7d040e7fc07055e36bfc0c04b90888a6074de3cd491be3eaa4962aef43e39f80f8f14ae271869fb622418c4156dbc0644ea71bfe981e34996c8c356ad4a8710abb26444ce6fb6eb8c449d383a7f4e7f4a2e9c45a79ab3b9417b6b7ac89c6af4edfe1cf8a06ebb4024d2cf0572cd6699c391612371eb328e32d7fce03a23f6ef7fff0512ede65b0660ee6c54a64cd60a852a811f458337caf8731e856c0932b9d4c7e59bee4562c0e9e4921bdcfebde831126e9765490b734e3da6041a342d66c45862afb1d4723a2ac901b4a114de112eeada36c87342fc4395688e242311cbdd329fc3089ea4f8fa3882bf1455fe29c1b43d538b3e17fa1111c7e7fbb92155f12c39f9063b55da17e75798eac64fc8b079f267c57b23f54a15f21bf0f5f883fa7f299f558335f3a9d1efbe9345a7b3a7d873158558ee7348f7af18f5c9ea15be3bf1a658ce1db7186247cfbe15bee5d4994f128f11b9fd83f85e16a062afc1926c54e8629cad72b6fe44c2817ee45a552a9543427d4683fe7f2ac628c2f4657121fb3faf2b8f89f7d472e8f8b71e88eae0a9f267c8b8a8146f5a85035da40fdf7e20f5d1ed1fe67432334279a8fa27a57b9ca94a21967aa14d145944aa552b9950f253acf7dab7af0ed9852fde933dd500018654319a490347f7af13d8ce1f422929338b6b7c47116fef7586419119f7b4ea4119f7bee454f153eea55a990b622c1d1ad589cfba05bb13fac2f911fe6f065d9548c2e7f3aa1def4f83fdfa1a16cf79c29346113eaf1698667a8204e412805a1aa8242a1be300c8308c3d106113ef760db204294b7eeaafb8088faf041cf5a93352da19482a812849213246895ba0f8856e97613f8ecc7323da89aa1c8f0f4f7041a39d95314f29e4623a7ef1e87484edf8d34f2bee7423cb4627156de8792f259079345ecc40f95c4f08a182eb9d28baef42b95a74295a85279ca5722195a09ad8456422b5916c025540a9742a5f04ab8e44aab952bb9d20a7f28d261ac3e55eafbbed4f721abb10883fc5cfc54295f8556422bd7b30097702905830b969488c595520ffeccb960b93ca17fc8c2aa7126c220ddeb54aa7186223d0586ac952bb9d26ae54aab556825b4125ae14236005c8a2c2f5e844ae1951763b8145a79f1a1d28bf0ca8b70c995fe852bfdca85f833172f6e7c2e30cd2ab4125a09ad64112d07c5182c478c5c3ed4873a5914136044181d105797c78201b302b35a8181419829c45c316629540aad34c1a50b9726ba7069e570e0c6e3b069e58456c22b1d13e2eac6e3b069e57040fcdeae6e3c0e9b564e0b0c1818ac6ed8845642a515182ce4ccc1d40003c60a0a5402b37acbe534ae422b63504a60f71ca7e25e65ef673a810fa2dea4528dd704be09a50adf640a416f168657157e2a35863615a6b00af5a9ef94831fb250415c4f59fc032d61b478d182454b155207d95f3030c50cdbb7a3f78ebaa329895f93c9e4f5e9823f0b8d3c71d3173efebeef1b69524fc36aaf498d334ca65cf5a22a157ee0f899c69994bedcdf15378ab88a3263c816c8fe72812ee4970b6c217790fd25892cb290dd7b4dde9e617275f2897d6e6c6fecf0c7fb0e61d4387dbb674abadd77e098a57bf7995dba3ce2771fbe88c9f03b150b4586e3ec43b1da6bc0ef587e1aef9b460f8dcca43cb9e3ccae9ead72c719f6c627f6bbe7ba118f33fbf76639e18eca170c4041e620fb0b06b07cd99205698720fb8b963124a8fa6c38e859eb7d0af5296b7a7b72c1bd1127c317e252c84fbdea51ac192651a97761df45ea512e1e358ae30ca544a63a511c5dfc283e0d0deac5ee2dcd37ce30e9b950e1efd48be3ec234fefbd2a35626f7d286fd9d338ebde64c3ff1b47acb2dc04ac921dfb65936ca031ea6451e3d580f8c4f4dc8752429c89e2fd8fb4e2898b6dc27671eb84170b85ef74e34c8ad799469bc55b374b12bec36df19d6f039cbdcdd9b1fb2dde6940b4e44ccc80f8bd1561ac6ee0f8560c2039b3e4ecf40503559e8beff4170c24d91055c5e2701c9e39179be5d6dc4705617fd87126c59ec67166ab7069824853a598de66f9ba9305b1384211a92f471071042d261509f00ec031dd320215607704223aae6b1de4708404a0301450b7c3410e474001e500840fc886304140617490c3182f0c50d089ec278ec0c584839b83c4beff6a25055b5b44989c1c2e0ee26c725adee2c2e82007ccd958cbc37738dcba453220da6fd900057155f41067dfd730b0ddc79e01dc217ede7220c4bf2ccb91357a88b316992ce40c07e92b8bc3ff06aa4c0b6263900571e56ec700882e04c42136eeba0e002277646415b1e48b34e00a62631552b861439cb24453799fcf7bb80ec7715cc8755444fc2871ca1298fb11c45aee83a23d027e8700f1247a3f6509ee1fc7c7c4d44d11bf14c02296f46c702cdb4830892f899f6beba316fbd8da2040cf365680d8897d6b6c14b1a41d3fef3b2a62f7383c0ee3cf77ec8ebfe5461b782c6239b202d8768b7b2bc57770d3143b2ed17d0dfc6077c31f8c2dee7620efe3b01bbd91e0182e471a2009f7de38dc73b83b4037a6ddaf1fd9b8ebbc6eb47dc3f570b5d817e23709f08adc73dc95622d0da08b5396f01568bf71b0a0b83efe0d83b42b6fd9f71dd40c063913510ee821ce5aee45024d90fe36cee33eceb224c7ba7185ec2d9ca0010b18e1074838210b9f46d1a0bc855b025a2c793f00e215685045ca152666300311dc200b1848800807885fc0bc6004099618c10d764046050d1022d5046922fb091e60e165b62a028b2b6e2d16537600851d28c13ac10b05aa9c21bd64e981972c4ff092250b2f4c2c91ed858924d205d95e38300412544d5962092f36916cd68ac953f358416c7a7ba3bb65e8233dda6041fb20d864e31dcbbd65d9707ace5b4650e40c0441d0f41ecbf4f5e4bc0d0e6f591bbcd516c738ebb7c15bfee0cfec0379cb06dbffc48e3604cd7a7c2b44ffcf07d22d105140fdacb5403da19540fad3ec0f1816c777bc6f2390ce83dfdf89cc774cef7d04b2fbcffeb0572c8ec502c3feb047581cbbf4a1505848d702de407510c8212cd5d02d7f2f07a4fb80f4b77d65d6589058ec8f1b90fe4fde22d12f1aec113d701cb02c07e43b76d6ffbd0d8bf47bdfe4294c77eb1b57dee281ec1e6f04d500d928c4d9eab3f460a538a7fa8057c57f4e0e8f1e20fda2099c80a0213441053f88514111587cf0103ffc60058a05281fa4c8117b7278e49868b200020158088c5bf821073f0081470f902f409c804c91fd05082ea00ac06e8a58437c61c9c6477cc1b264bbb1247d818019945b0b013210c0e28a2f4528f1050b1457cc172c4d903314f9054b07c822b28034563364747ff78b41cedcb13b86f1c2058da84a8528f064fabc8ec3d76d7bbb778d1a29f1ca294ce75d8771bbf707c6abd25e157cddb69fc2d8ae518ebd65410fd5a1b5a82efcf94edf07c35bee71237e93f7733ab9f5c66e0e25aebafbbb176b0182aaeeeb6fadb5d671163c60212d0f59c83b629f0282e0ec9a21bbad7bf7cd45d93362f7597fda35c39272ad7f60a971dfdc8fc2a514fb893059c8991786a379614818e04fecc7b41579efca052f5e5861c60a32e267bb8d08f3a99ef8128132d8881260115c7cef12e6d2dee26e6085186bc6082d6eed1723923e23b22461bb3fa5cf0a1558b7d6afbb1528c020b06203e48ac3d88a2c5cc771aa212ccb8a29e4eaf3bc2ab2a882cc521562aa60c167fa3e0fcca7f4a14a603a994c556ce0049e4e3809339d14e20cb29fa842081005824f5441a58a1f4817a958022a44a170d28a0a2cc2541896a1420ca602cc47050952aa548a0a2da41df35171844a54a9a850faa6d8429c4209342e68684c51a03e21b810e3e2850b174f4c11c5135338f102c68b17282ea829b27c532c4da18027a4280303150346a8833d63b92bc5186c032c0514e4bd2c299a90620bf984145962c88811438aa54bf613522861b29f90a2cac3c842c60c19327a00d6a09302d360053350ab1933ae9c9cc8512289276870e5091a287d67c0158dd54a461068d4a04123c614356cd4a8f1c40ca0c04fcc0004e4ca5b369e988105da468d1ba9ee9b1e6f4a90039b455b6bad756bad75d40dfbdccb2ccddd31c61c57a5ebaa785e95cffbbecf851420b6edde472cc900118bedd72df60e984c9fc9143e74f4e8e0d1b1a3a3870e1e3a76e8d0a14347478e0e1f3a3d3a3c3a3b3a3d7478e8ecd0d1a1a3a393a3e323a72787276727a7470e8f9c1d393a72747272725e35d71bd1bf6a66d9fa2f3576de50a22f427fbda1dd2d01b775f7b7e840bd9a349bfa436b39e4e5cc68d14aa0bb63b4e8abee2839769a4f2ba1a0b59c7677d8cdc5609f882a23ba5b468b3601ddeda2457bc50baf9aad3f33ed817c26fa18ed86cad4af8165286aea8aa1359cb5569453148643c4a3a8ec2182e9e8e84104db9179720f0f5151118f1e3c3d652682e5e410f9c821e22182ede881035bd55b8aa3a6aeb556d4fed3ddab16edd7dd4774b7edee4ed134cd7f961fcb677a5b73fa276aaee57ae65aa6d96ae6af6699ae6f749aeb5ad652b4e8cbee36a2bb23d0dd45e0eec6d2dd2fbaad93ee8ea145fb6aea8265284247658a034b551c184aa395384869991299b4eee6ba7decee1b2db60d862954800004464c33bc2851238216f000848313041545c1230828da4f8bf5839e302f8e3ed713b01041094d0149987ac69a09e88479412308d7b5a94470c030ab82000b7c527808b880ed252989eceb5dc480e07df08edda4946b0500f6dbccbced370aba65b5146b77c0eff29c9e872955de87e381871efceff7601f7f0fa6ffbe471ea8bc0f37f2d08369acf23eb687eebfd11b7be8ec732310aafe1e3a7b5a1615fcf69d35a50a154bc57e97c7abe02955f0b8840e305059420718c8275596d00106f2be0eed85bc36303105132b20574800bbbbfbbb876a87683187fba639af4b8e24dbf5e5c1d884393cf36ab6e06ab4e830c6230c480f437a60bc2eb7c6df1bd30d419c7962bc2becfb7b633c31165bdc1f0ae442360038cc753981dfb0ab31137e8d16618e29a12a47922af5dfe5a1f9145e57ac7a1a560aecb8bf1e82355bd46c51b3c5f594e3284e35666ab4c89154a3458e29399230f6662d30aaeec3a713f8370cd99d5e9c9d5423ed8a49a5c213eaf4e0699c996ab6a8d9c23361ee0858146bccd46891638a58b3458d19f16bb410734c115de448c264f72e6868c6998bd98a69dec5c360f5a54989355bd46c51b3c51da372ae0ad838665e994b845bc68ea622e014caa6503e109f98bc3046106797cce5f1c278612e192f8ce83b5e9830be13fe27c67752578cf17ae09161a1fefbaf468bd064aa3153b3c5e841e12d2fdeba9e170f0a0f4c118870cd24d56cf179607c202611e192f1728950e666e13b384cca91f47d5e182f8c37c525738970b5a8d1a2c60c1994e9c4b2a66fb4974ccd165e991e5c8a774131297bbd334d71378520f737e5e3cc7e8a158e36546ca8d850b9770c78bbeefb3ecfce56530762d0f47d3654441c021a94cd140fdb50b1a13205db28619b2a268fe63dbc9a52e2987afc36546ca8d850c14945808d63f679c16fa78ca7206094c9a23ed30fc427a72f8b10c4a50f2f99be6f8befa094e0f235e13b5d58534e6f9374e2c6efcab7f461f996be2b1f16b00b02676f735b6ca8d8289d3e2cdd0f3a32a2f7f65b32fd4ef7a74f02bc5b6ca8581c703c9db27c59be249678ac4f1236542c4e6733c526092fd95099a952ac1d6da87c427c4964876d926ca6609b2a1f4e6ac26a3a9dbed56432996ca8d850b1a1b2721f7cde37abc64c8d169e97238974af7377d1431f67a7377dc784fb9e377e5eeac12b86fc5e35a35d31e0a3c071b662f29bad98bc3f6b81b962c8ef671fc62d30f78a21ed7f63cd16355bd46c51b385c72161003344c055c81683935ed0e83ae0ad2630011733a894f1945ec8c066f01783e338cee380e0beeb1101460e522f5a10c3450952344830811291b8c04985c411a61412546484488881d2810b308cea24810cc43049e088f09380102acf24841a35a0f14263cbca8810c8863041406174f085010a6a01a00320bee2b31f4d5bd18832e490b8ba41d32f4b7ee21723c6bc3bc0d660051552b861a3068dd50c193160bc704123aa52210a3c99bc0ef8132f5cd03801bc17f78d2a15a2c093e9f33a0e5fb7a5d85fae5cc1c2438421dd41f7eefb1bcdba2498624d692f3313093e262fd9e037cb933a04711452a9d403a1e2418a4fea410e95624d113b6c8eeb514beac30e0adf4b205329d40bf1be82c42fe44e600c697a1bfe74d3a0bc65329964908202b2db66c6fd7dfb9c89d55780dfac4ba6c2243288e0830f5e54c81adb0824c8a14256278104af37c6b6ab919a1d91601624f8b14ee09836ead7ee34cefa4395c13ac0407edd87e9da91a8c763c81e755881d40106d276a7b1dbe6bbd79bf9df915c6116cabf23f4370eb00572d623cd5b345bbfd21c524bf7521a09418fffc3df8ddc3885ca8d0ff7555ef8e0a7f90e0c72462369def2cf21673080a67044e2ac89c043f7ab5b23c5a7977084082620022e6392927c38d6142aeee37d95203ef8395695203e3c2c11849f071bc4f0b04490f7dcc8c3111e9608e21e8f3cb46e8d83692624a0055a8a60030dec8006dd12c4f691e20018980009249e10e30125f8b44ff73e9895843760c5023f54c0043e53a8dcf8e0aff2828615ce9cf1c12d90fe1dc66315ec839f1ba75001e283bf0af6e1b8e7cab87b46b4bd230c6fd95fad6e8d090b1089aadb5afca63782df649fc3d74d636804bf8f7e71636febdf6fc4bf2daa7bee83bae7bc6e1cdbdabee99742ded008c630380ec481e3c68d3b8e3682bae79e490776ddc9ddc81dbbf0c7472fbca311246ecf9026772377ecf7372285f442f74015eaa24ea6cf3b8b4d29bff79404aab807551cd775defb2df39121e3cf5df1864630fe3c6b2dd979dc7b5cf78d69f4c6ee1bfffb419f8f08f8a49078768d74a3167f6ed482c59c61390eb9116585b5d65afbf9181fe3eededd72b9cb5dee729dcf2d53067398c31ce773bf4b170e77491d777d2e37bacf7df04371acce0ad3832a1b48bae7de92524810e4462d77fc8c78df3dc7a47dce9003b03e78bc62ae985be323c55e1c4eb16d9d68c2baf524bc23fcfa8d805204a614914567040ee322c690abceebba22ae3839c176c3e0fb41114edc1be0c009452c7d4554c16206633142378d63f9016969fc06244b8ab53f58c4b8b558c06051a202b88a7577777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777f75ba5cab52f5ce1b37befbdf7de7befbdf7de7befbd66fc0a50065605579cc0f75e514410388caf74e1d2711ea7006bbbbf5c99805b7b05027edd1b70650157a870185ff181882b88d8e20b1144f0ae7c5340c19de0968008100c810825dc5a228ec0442c5d772292b2b0d676eb60cfe00e1bd0832938cc35008a06785935a009aee3b80624b16ac095ceebbc064cb1597c196208a4e3f03ecf1b220c14a6ef1ba2892f437021610c9165882ba924c0ae055f8698b21485cf2900368e998d3224eafb41140b9c71af9924f28efd2896fbcc307379ee278aa771c637ce9eac9105bc662e8f67e6d698fefb44d1cc35b90fd8be8322bf779fee970d995c1ff03d33974755e6d698deb4f2cc90261338ce00736b4c2fc45565aea97b6beaba6bfa1765b090a6bf5088b3196648d3cf5e94214da01633cccc402dba088a6c6f99c68e09e2375a33b31961487f0e8fa8cf4699ee09707697987c3e7d6bc4781632968b99f6a29a7d23cb5333a400b07e859b9aedc0c20a2aa47003a7660668e1868d1a34565c6ac643ab4bcd0e50e381393e1772a4ec4bb1160701368ebbd38373e342cd8e8fdcf1fac89748be828cdf9727008fe3fbf2b4f02cb49ef63208c0cb91314fbec9bb5d21051bef961cf2f3d6cbe5a9f1762503c6db3197c7c8db8f54f2ad442467f8c819be67887d373c01be2f0ff7f6c55b9af0fff45d1e1cdee6f04e0a507ea2b85afdb76cef18a05b9ebfa188abf56d52cb26e590616ca3fd3ed1d6bcd0c68fbc49fe3e3a175176938c8adc27b3b4c0f0b2cbe3638e28a25bad160200809fad324b8b901fba3c37e96eb93549748b02dd5aba5db86496162437cc6dc18501eb7db652814990911aff347e26cef8d9ea22e166d1adccd272c3cb2ecf1d43467c178f8fc04574ebc5a7ac4f6669c1e1878ee06e75abb512c5ccd242f4472e8f5d528055b2e1b64c6083c92c2d3c7fe4f2f8787f9b3f90af727cf78e67d12d7724742bcce5f131d68c9309b32456b935378c3573c558337ec770b9fed7ccd225936549e4b2e4586e0dce2c13e9632e0f8f1eefef652e0f8f8f1f552e0f90f7bf4b97e785cb038390f7bf5b2e97cb43e4f21479ffebe5f2184112c39277a0f7bf652e0f93f7bf662e4fd03b0def8fabe0a5cb63c3e5b9e1fdfb5b5581c56044acefd6b4981c461c0830da50c30c6393118895448618048084d5b7e6c86884c5650b0cacbe3541c6998b01c27a8d33f7d233ce2e969db107eb265d2f37cbdd72b9dc2e2c8c01180130b2585ec5971c0b8dd50c19b706c638b362fad6d08c335b4535ceec12cef2a287ebd77d006395b62c7042db6e3130b06ead0bfcba930173f1bd2bc01cc666a0e03a8efbe2755d7b692b4e18019a42fb85f65b077be68e77498c672163a6bd5851e2b3b2a5bf140c25b07b62c56dc799896c7b258823c856ca42690af20a91841264ffcc92412891de0b3185b48f7b40deb74a5090de5b283a78a4fd3b33fdf4ed15fcefbb4fc2b1302a4ce2a1442801281a94694425cd2ca1902baa84768b6519acc98a2a218d4e53b6a2a0bb4fd0dd9f09badb89ee2e0109ba7b04dddda5bb45d0dd21e86e1074771337c89a8dd272fd175ef8f406f33335ad27a8906aba6ad4e7a146e86dd4160b3cbc4d9a6367565b48f989101291900c96965915424acbd3247299babd0737d46c0d45289adafeff4ff361bfaaa8cc36e90fe4180a7bd91de59fe667d8893e12ed443f96d73266bb7d26fa55bdd5be5c4b2234ac598d9d5915426337359677305354b6de4aa115569eb9bb05d0edaf8e651dfe47b99631743d67a7f92a5279442b6b655adace5bd1f3ecd8a163aa486a27a0bb73b4582bd3ac474a3394fca7f9eb99d59cda3e133dbabe1acbf9fc2fd73226b466554d5d679ad7b5b4e1a4abdadd2c4e08c7a36f0cba7b498b36568db4428b35bab4dac4e83455d54cd2dd1e40b296361ca435c795e3d2b9418b4ef326d34a988ab343470f2cfb208af9f0a1630706cb3a373514e622bac956d308295d55d328a7dd46dd1dd4620d547777a0bb311635cd6691b99e3966feaade98bcba9eb96694d597ada5ed6934f2f5dae9a1e34877dfebaf661bd22a44e386283937158d52d6846cb55466a4d6f25ac6ccd8590ad56e6794b2b69e371486043bf34d879c16c5d2f2c876138a52d682c8d4f2489a896a37254f6028cd07a9a4e598d02d86c25e2f357599b652a8a69647383d884a221f3b3a383d59479979e41e311e1d453d3b7278dcac68ac7c42436d3cba6768714696eee652ae65ecd79bed346139860e39451d86c0447773a0bb973873e6cc99ee6eb4e8449263656a649b61554b19ccd53c91ec60d2cad32cf28344b3d9c828652d96a1a459cd6910a134dba2943598a99e66ba46c94f60288d4828ab25d10a2b6d3574c8eb05cb31141644c78e1b90340345296b41809ca8b9da96d0ca155d85a29435a3d34cd726af97904d4d33906a3520c6b0e91596d31485fd8a9e9fd3f2d5a34fbfc8fc585e61d91625bf6afe6759d0ff18124a51a1a197ade6d1c7b24c354fb3e87dd4d4a5ae66cda475b71276951999b9062222b966aee78d09528aae65ec2c93d0ca34db96d0ca5a8a2a51d5923644452a8150256a2680db5e5354a63e8aae32b594fd2bab698692d33f91a8a94ba696321c9c9da21d1e3b787ae0e8e828737876c078c476ece0d19393796ef25aa6ab4a745371d4f5863e8165190c67358d70720d95dda0466909c391a9a5ecf55a53d466bba942b956a645228c0e7477accc69ce45422a526953d59206cbab991e050962aba54780d8ca2439a7413eb4723dd11dcc1495c54c5a0925c88924d784624d5eaf6ca6ab904dcd6bb9c6d2f2e83c428918a9b5b5145acfacae674eb3ec34d50cc55ccd9a99c6ca2c1382e95013d2a116abd1b1e3f6363a76dc5a79075315cab77cd341289683d4c4b251cc4c89c8b7892129d312a805049669506142e66996b221b9bb37d0dd1ac83114968914a1680ab49e5925227f6cb5f448941c3b91a86a4e8d4e24b65a7a24ef6036c9b77c1b800cb69636d5248254d2329358998fc07448a2438d88915974a2435694d6443593ac28caa4bbb7c840cbe759ca86d86ae9115528afe5aa0a2195b49cbe5eb5bcaa48aad069963258999636a135d368a54d48bdd16eb51b6c4565aaad3c9be4b55c9b189da651a9aa39090278d025002a04c850046b29d4e1000934d1c44d12560f58c9808a0c10e981064141bc9083aebbb560e04cb72b96d7d5acd550d8139ec04aa28f651a0d4d3f13fd59c27e2d652bac54cf325dffcc36f45125dd7dd34da445b48a6bcdaab99ab4f24cf31a253fed445313caf9d3dd17103a8d4c5a29947730d5f586229532747712a7b9aa39f632d85ad6321afb9a56ceb0f486aa2eb428fa1071b40add9f8b8f86aa4c77e3607fe043e7837c9ade60397238d5d89c5aad03fc01badb02dd5d81eea6c0c9a6bbb374f704ba1b89eec6dd584e06f8b77903bc9aba5219ac966b387d438f9abad434c768e68d0867ad1575ef6075b70ed5d7ddffff6fd3420baf2b4421c6e4208583feb5fc587e0233cfd2b6de92a4689afe1ca1698afafc6b68e85f279ba1a17fad4599f67f1aaa59654666fabf16655a6be85fb14c33cba234ab6ad9ddb7c5d402502da6a4a46add4da3c55450f70b2fa8e60fbdd1699e5976f435ff02906fbd00e44fd32813a9a9ab5ccb9850770ddd31747797491a6aafe6356eaf391aebb5ca72326cca7e3a304fdf2b5805cfbf8fc3d7daf6b603c030d8f67e70ed6dadbd7ec45acfbab5b7dbc1ad5bcf6530b1b6b35ebb0d5dd7bbae757a8bddf192c5d6fac92ec1ada9398bfd666b676ead7f33b80db7de61b743706b3bb703b033acbdfea580bb5ecacab036c6776d16dbb9b57e85ace3938987a36c90cf7238ee8f3bd6b93daa1dcb052cc21bb75fc0e2dbc29cdbdb9c032c16b6bd45516cdb165bb746b09fe7b0d639f76bed0e8b633f8b7277ef86d816dcb1db9ceb02697c2cd8d0450d8780e59c86c5f6da7620ff93c5ed66d73f6485b8cd4a816dcebe56e0d776167b36b4178bfbf7bab6e75a77fc8e32b52c67ad978361a0c9b17eea3eb7d65acfda9775bfc1edacc9f18562473fba37d604bafd3ceb425c054fe1a670fbb655d9cedd8658bcd869b8bbed2c2eb2261c16e4dc7236c563adb5d6860ba0034d6e893e94e77600f6b3f6bac5f6884dc17df80d07dd73ce5eebd641f7b0bddcf561af6d599345596b7f5815fc743dfb61ebd6beaced64380c8bdd72d8fa70d05e57c1deeb8e1d5b1feeb6869facc97ed8adb52f9b829fbc6bf187adb53c6f5547d705ce5e6bb278654da61788830d152c00b075efdccb568c91031eb1d6ad5b6b6fda6b5b777790ee66d2ddad16432e33a0c09699c1d51fb403326182288005119a943bc819c00ec63410cb06bd640733c84b584e48400905d0254aca5919015040052514296fe4f860ad707a7690da004f19e2d8a1c0c9ee94360a3dc69c41a247962dfa660c1454b019b3c56d863080ba902527870b6f730415c63c4a852c335c74961739f41037443f06a2fb76c7cebc96af8a279f934daba92bd388caf534531b4e8ea130f3bc75534e017841528d175e93e7d13079dcbd3150413e2819bf5677fa7a300a06ca23006abde10d3f31404420a94bb967c236610a3d1c483ad1808a619b940fcec3063703b61d902f492fc840840608c7272c438d15498d00cbbd6c3e38ae716fa8487201080131543e08b8467c4840120d530d281811482a32e3fcd41313928688103dd30db92758014a3e7c98e6eaace043132a60ca51c594cf0c04520e7242683c1f38868c03971f5e70ad5391130b438e73783e7e1801058cfc300306e04c7122082892c05b911140843a5456044934767c38793ebad397ba3c488cdc144e453c1f7ca4d0e43b62e3a7c8c97404d52406c44b92886d764e9e8f176177027fc6026020e9f41d5181680149dc1539db0da03b713fba1c240ce0cdd4c277a43b79345243542a6025a619306ed090e36b2961c2820fa7cfe5ed0877f02001f250d8abc1bdee8e09855b78219ec93b611c2e06dc23c472362e8b939163fa60d70796a4ef24c3c4e3c3529205797c2d8c03e3e8349094238589702e702a9870e01b2a9d9bc20f492e1c7b8bee0dbe9c01c60210819e304062820361bc70614203490421e50051d42745193654002737d840c30c4d806410c09157cf8e160b0100c0e315a800862001186bd0f0b2c400065a3030801f185e29c440811d144e9060892d5a92b00005b2486086560b464480e886231841086d0003cca2203f4ede962196784c351093812d2f1f3b336080a7cfbb614304217092634b94080049911f24ac2820012ec6f43001131cd080941a68e0d959c1c03308a3041a82bcb063f2ba0920810403962809f2a3059485823fc19de0667099378293f11d7063ac0ddc0aafc244056a0a9f813be14feecce2f852e06e84347c86c7b030685e702e9cc68a38e5207772d3f739b6d7daaebb6b84aa6be39e20090b918465e872b4a288351f682e90c471288f051bf88029a08367a092d84017441296c124e374630544928900f886ab02b876a8afc8d7d39d6e4f8e17247d5d926038f1c1c4738598724323a07490c49d49e25035aa4842ad371f4832ad52eefde070b82b5562803420f003564a52b1c4bf373438df8ae6cc0f9c0b5f8f07e4aa2e906fbd3758061858dda95322e9825d0d6c03ea5c8b51d8e6548473c1234057a33b7533140c140cd30c308647801b851fba1a97cb0f1c98a400cc70ed850290027ea1541d0db6f7e60ec140610ccfbd1a3c91eb3c9afbe262a46c075e0ea3eebd7ebdfb5dd3e906bede0c9e0b15d089c6eb3c7b3dcc03e360fbd1b86392c0568f0d24a594f081a6c3363e223f601dfc300a7d9fd9020835103383302d0073336a72668b21967e7c607881073d8f60049c1644d5e7e170c312256764f084971518908305024222a0032ba260010a44c084161c170b42380fb8ad0c50d32520a1cc15544ca1021048e9a1004e6eb06189122345565061868c273851022638d0c3018452b8819e2ba8523ac8810eb530e9cc164d6082131c66c8a0b182052b7802052208410718604501420520400d4b94208942062b888109b01cb9810da290010b54808213602089052000082494b1820a149cc0090f4c49ca31243f3461191ff440072d50410948d04406861022c76a40f25343134621831648c08821a6e458010480a4c8cf8f572ac4ed831e44210317aca0042468a2031968809529390800c90b3f5e3c3c746a5cd0e03ee3831ee4c0052d58810a4a40820e64a001435811620a162010cac1090c382e2da020430caa140e52b8b1a4b4001b6ac8a980100b408ab2449fba3b002d9a98d0a633770b1c050e0afe846f0358039e01d76275b8cecdb12ef0c66dae0bf765ac6053f01b5e8d4bc357dc0c2bc3f4c2858b2b625598124318281ae08d138de9fb3aef761dc75ddb6aeaaaddceb45c553cd4dd30ba3b77b78d1671daed7275b7b78881f4d017a15ff36a5e91885aff3966aaa7f9aaf9abf93f90d2322d65b02f7f45cf57cd5f9188ba16f14d777f2de20074370b2dde33a110c4f007dd9d6a3114734310e113ba1b6cf166d139b85c2e1c5eb61a9d99f645e8679849533f458b6267fe6c749669778f2d5e5a778b2d5e27dd3e7cf4f8e0f1b1e3a3870f1e3e76f8d0e143c7478e0f1f3d3d3d3c3d3b3d3d7a78f4ece8d1d1a3d393d3e383a78787876787a7070f0f9e1d3c3a78747872787cecf4ecf0ecececf4d8e1b1b36347c78ece4ece8e8f1e3d3d787aecf4e8d183478f1d3d74f4d0e991d3c3078f1e1e3c3c7678f4e0c183c70e1e3a78e8f0c8e1e16347cf0e9e1d3b3b7aece0b163c70e1d3b7476e4ec085084d6ac0e7dec348b8674929cde686659d443ba817cfb682ee55ac616d0dd515adc01869abad628b99da39965d14f774fd147b0a0133a0b3575c5cefc0456129da60c56d66e622d66ecca4c328d66c6709495e4183aa449ac05311b1a89884654ae6769a62594cf273a24d6403d3d9a349141759d66293bd72765edd1f5d31c4bd13fcd4789ca3f42cf27259377c1b24dfd18d29a661b9ada7eece9a1aaa36af6e869d2e46379cdb1168cbceb2c612a5ab3aaa9794b6dbfaa3946435b6fcbf949ed93bcab76ab9951cadae77fa92cfcbb64b2a19ccad6574fb35c6b5a9f555a59e45d433a76dc86866e695011fa43b9569eb06c5387deb5aa28ed33d17f363232d3f57c743d51d8d0907a9be5a32f323fabb71296897ea8c6e6f50a6a0d7db621a939cd6bce456a09fbd3aca9e659ca60b9062bd33543c9b49b556664d25e4569307355cdd3aca579cd311566da4820195d6119ca9735396dfd9749deb5aa26ed878204011952cb9c7e263ad30c259faf9ab17c7b24da6ac24c5a2daf66cc95e658996536aefcaea1a14722fa35377957cd6a3eba7e14b505d556d65aff2e35cbd4525626b1596f365a59f3a7a9d2ca150d7a17112445a789a6f9861355926ba51258b6353991a0416a2c6725b06c6b02438386103951259c111c110408d3dd5c64a5aade5025ffaa69957f03126eb0a2bba5e897adccb2d7d010941f1a5af2ae9a1cabccc8ace568bd4a2bcf57735ad26039f6cf802e47c812012cd4d4457403ed06176ec0d1dd4666edd1f311700416ea0d01a6348d661609d9545568bda15915a2d1cc2742b2d53c12d2e1d2d171e5dc9c47684a24534b19cc869e239456946628b0d256c2fe656a79c46de03ef015d0c095559406fb286bbead67999646ff3614adc17472a9b17c669306fb1355cb9ca66811d3c9a5aa2551f9242d6b32d899893e458bcea2a795262cdb4c2a66908f90d09cd6100601b51edd82b628eb24a7dbfed8155ee86e2674f3402cfaba3b8c151d86030d02a220fa891428badb033278208abcd6a8eb99a1e0f059166432994e331499b51874d42693e9e432994ca71a9cac9644d928966947e8f904e7cc382a0c8de140c93117ce4bb5adabac95838ac3d7d055bd0d31994ca7ee465b6c12a665d0dd316815e7475b1b17daa21a0aaa90d169a69d9582bba67566da0b403e66d652452d32e9d2dd308012cb3afccd4933cb1f27fa44b55b0afbf2d52c536fb6cf31f41442d7d3148a9d19a6e658bee9906342b00c05473d51f3bbe9ee02b43880245ac7e7785b09138ad1d07fe185af69d94a98ab8896d51d1d57cecd8aa2a90d0b3d65002b3575e98082034bd51a2c8a90cdbf6cfe2594665aec2c65423638ff8ae5d8792bca298e7aa3d9d2dbbf5633965398d0690ad56051804220224043806a808e808c809c00b174ec98010d8a759b20cba0d268a43a24cd4064b99644981c6525ab5acab0aaa50c6a0db65a7a44a6964732cd66a6416850cc564b8fa8ab5aca00b321c7d0214d62269113fd5962ab1161a2a62eb32d0e0cf0412da27fc432d123d188caf556ab99aba99a45660cadfda09566ba7e917943cd22f46de8ba9a51728c76438f64e0a2e6954664aa2a6acbb2352d3f434189cacfb215553f96735afe8a24c750253e44b29906a5b0d37cbd886ab77408adbce99067a095b9494e8156146592cd34a86d0e6ba3ad79a2e66ac64e149653dbc750d99a6ba979d321a756a6a62e5836775c3a74b8726e886237d94cd71208c0a6bb5fd0dd2ee8ee160489659bcd4c57f55553cb23200e4377a72df2c0a57b6845d1342d6d7fa4be8484942c41d284c90f0d423f4648264b829a0809bd7e62290acb4ccab5248216c9d4f2080d317035cdc5d0c10d1a7f20964ddb0d0643bf482896d55be325d6331f7daea14ddea512ddd447cf97c1ce32f5e99b54d3425a6db52ca49a2ea4fc4488658dec4a658aee06d3dd2c1852724407caeb55a2a69aba884c1a14d309661ad4841545d347822b462408a2112090d20ce5737a5bcbd3c86cd25601302320b03818b1e9760195e91ae5e7f67ac1cc583e6790a9e591273094e653ae2591a05f4bdb2d4dd121ff62b28409939f7ca3a131f495d792662b6b3838b6b2f67afdd0f809f26376ff2b769a4fdee65f2f989aba70d4d52cd39b1a2c0a4e0d16e5444d557dbd5254a8068b22b4a619ca89c288c0881ce9ced1a163c751c92486244a8e9da5b9d26eb59cc662a4b9d26e497270f236eb0d7d9a1687bcd0dd3a2d0e4175b7cb767b1acd2c7a1acd245bf4e9d2a8b996a9aada441f1bbabbc98f1112480888fc39f2e8fa3a3d7278b8746c6596bdda427e241a512dab2a49946b20a5209d2093201b4006b59a8d4a9b102f48e8fa423620c4869d1ead578790202942d7dc44888d90140c4280214c0771c5b2517ab3bdd16966a2d49661674e73514e1f4df30e65921661c8e96ea3d3cc34537664abe57525bbfba7c52063da15a44bafe8191464a8cf5b1a3363485a0cf2f5dba4689a661c9cbceb85ac963985e5d8114aa3bdc0c3bb86209de51b9d261afbf586be70c30b1c005a0482459116811c612b737a9222909e3e6fe9a3ab79a49aff67699496b42742579aa996b21b10d68f30dd2e9a6aa24a7e54e96e178d762b7ad5cce95ac3bb90d20ce555138946b379d57c576c488662aa67b645c9ffaa0945e8f582a966edf5424d219b3af42a8d56c25a3f54d3a4b564e85f436b5685ce1286b49a51326da8c9d090a126efc20f89a130578b3f96747793165f5abc88f07ac1eb04af9af862bdbeee1c9cfc4abba9397d9b58566f42383871a9b78fe57cf4aa8f29a20f1960514e14a6965f0ea0c59e2edd64cf5017a1b17cde705aec69b52bd75058cd34531b9ada6ca5cd0acb4545f973cc54bf7c951c1afa2c0b72ade859b422f96c5b616594fcb1fc0485729a2e918709dd7da4451e0ef0ecc063437703b5c8f391218204c87a66283f3938f998f9383871251962c3efc4768874770e3d2ea3134d8f3e9f68ec8bccf48622fd13989922a9657ea434d3f02e9586c2d0154d6d4f2b6ba9aa66552d6d484436eab98379a2a90c76223579976aae678e9dd99663a759caf28a2a71a525ed0633d7b4b47df927fa6a8a16d9b901a687da63051e60bacd2722fad5ace58f9d657e5226e151c4c3070fb13ba7b01c9ce0e084147778819945262cdbd45ab923891d43edcaaba9a22bed5616e5d8cd899e652ce79809336f4dba1b86167788dd9d8313b4e8347170f22e241aed56a4aa1906bba91fcb6ba6d96a28ecff24451d62babbd1a213490c493e4da3f347bda967564f7408508e8effd173c3c1fc1cdfc3b3c38564e86f90f293cf271ac3d0908a949f0c7d113a842406243a3a9d21e84821ea3820851675a20441ca3120e527aea1219d95cebb00f4399ec78e1e578e4b3d51f3666808293ff92274086868a806c845bbc16825ec9168792dd31b0c563e9a8f3e8a59245bd31c3b6f7f7b74fd5f5194c9e747aae173e478fd042d51922387ebcf0cc53442ca4fbe546b793569b4f2693433869c21e444912382eede6931c7871c1dddada4459c1ee064e96e15293f21baa94839861c436132b594b9866818426143345a09337f5c47f98495e91a253fba7e91eef669d1a5831f2dba92e8206f5383d93004488a16a1d1ca21b472c8eb757abd56d82d5dd5138509a93976a3c14e53e8ccb1982913326da5904d7dbd8a6859cdc1894c2d654236a76964a6280e4ede15cb39fd9b13fd73897843a6656a79e4f50a8a218191af57500e4efe7170e24a730d75a2bb85b478c383ebcc9908478b3747ba71803c00bec9db64330d1af22f35af665ac3dffcbf0f91bf5141f0e57b7474b878fef5592d7dfea646a7b5e451f56d9000913f141b7a35ab33fc6bc808a509ada68ada726ce8d1f569b412f63a7fe3aa697d5e332dc73e453faf667af4d9a8d4f99ba11a9dd692a1bf0902e401e0ca66babe9a8d8ef2d9e4c6d5dd352dde70364ee8ee558b363be8ee1e2dda2861e3830d926883b6d0a2cd916e21351b9da50c16240866dad0554dd534d76eea89c282d4f28a6455b39149c408a5ad678692a1a036a12865ed2cd79248accc4756144d856a592d6540592d65b6203d3720b04cc3eb05cb505eafd8aa0ad9d4722d63eb794387146526ea7a6613661615dd5434c34ef45c4177aba0bba1e8ee1474f797ee7ea2bbbde41485adb7253ab06267f91956d26c397d744d83d4d4956165519ad7d3bcc1517bba9b01624d171b9ac6fe44ffcc4fca9506b4e8348568341426b4c2328d86c26a6cdd693eb3510d500d8f4ef393143daab92194c59a6e55884cab620b06ddad26babb552db68e28d73256aeb9d6526aaddd2d27dd276aaa69861233a1a0a9ed4b9fb639b22851c2b472badb6bb1154337101433c3321335964d9acd46a6e8daa4882b9f686abecdaf513291d35ccf1b5a2bd51c33d334434187d87096fff9682817c5ce52b69e37f43ccb4fd1a2b5546fafde644767567fcd48bfa2a7ed693473553fa7e5cf507e4ef393b4aca5792d8f1e28099323efca37a45899535af9e8a3e71799bf6655c9513e8bd0b4cc34d867a2582e42ff5d46a7594361a7f92ad1c7b299a625ec5533cd6b51f9b10ca5543f7696b11c2b511a119a8f3e5666d9bf2b964fb35c89d057cb23d554f3998d6a685af4e5a32aad7cd5a4113d5259534dd9fa43273af4a7f92e23577e9b22f4330c4d6d4ff43639458b722a839539d75e353f46cb66d1aba62ce85d69b6bd5a7e7a3b4b289996d3b5a4e534d750f58bd08fa1b0cfb2b53c8d4cf5f3d1cb60a6ece8d7723d735ada56b5a4c1805464aee8f9e8fa6b566b78976aae59fd1c86cab52412cbab6aaa69061ac24135bf7ca313a5bd5afeaa9a5132f9ae1c86d42c839539cdb2155d8f5c67b6d94a59961dfdf96b799ab11445614fa399479f66d99a8dd0f56367492b73fae56728a52c87a11c4387189d66eccc404338fc9965ebdbe41bad84f2e5aba5ec57f38b90bc0b8966b3951f3bcb5fcdacfe99f3d16a5353b4e88ff2995374551f5d3fd7f0b17306d5e8346db6929666284b80907c627935894c199a0219993e01a23ea8e40b57763d4b32a5d0cc0888000000003315002028140e0a458301711cc489e4f5011480097c9a4c6e42174cc482284671100621641421800042000c0001a931880023ebc8a4f917736295e480c3834f10d4848a3dbe44f02cce07f03d76708415f8b2620584da6780e7a4f0e72a51f9d7b84188c268546307c5d0f082027da03e65853aa8329da77b1497e9f22b58f1b2845c5d9eb4d91d0c22a5575109c9c686815d226e668247fe8671c97b579a917bda400fdb4d1b4c3bce7b79835ef30a34e953bb2d832e43281028a78b9f2fdb7332ed61a0cd3c949ff53069c35c685e9d45cce5eaa6dcaadab3202c7ac5f1b436dc114fda13352b78410281b1c7ceba56e2e07e39847fa2b19d8f62f106e4d1c97e74a3414e8c5792adcbc9770e22d4bff0543aa0b8c2b30ba1e0786d5f56da03f1c609187a15cec8632f5ec8cb5ddb66fac8764c31500f8adaddc8b0ac2963c0979539a7d2a631c8cf00b5f25990282debd94f19292f94a92b253353c164eef03288dfb5e475e6ef973440579a5b43b6a0c989e8faa7cc4ff012a2d5b4672c8697042be5a81b5f1f583088eb9db70088a4e1793fc4cf8b0a940eaac2deada6b0b63eed129bb1bd81f7aaacd32f4e2672bf853e9910a1f5e4e44863a5c2f37fa908a302a5ecb2dae420b7acc59e9f30a2dac25a7e9b396e0298168747718ff7f03848adb164f2bbeb01610951a1647df4a5290a1c722c6b90532a25fd8e8868460c0228c9e21440af44d1d350586e9dd1799e2bc69e3de2837c5f8ab19e50ff08727db319132038f9fbd512c93db2a099538d8301efb0044244732259fbe53d656803b09cca243ef7fe54f81d7198d849b839e20af443ca940bde86103c6ab9e989db1349b418d0304579a1f27df42ec78fa90b45a3f02fc4203fe669bd5044a90c3218f8c0849fe8f1b02dbf97cd60b4e183915ff51ffe5dcc2e0d5d653beda23b1ceba39d9ece60903c1386e367133c6fc1804e9335058914964b041564eed8118f65b96e94f98f6d7d05c15bd9c596498426a87111998a5ad2cf73d236f3f7b1503d0001ce611e9a2d0ef44ae010913037235d455d1e3a3dad8e15e557bd2edc79ba4ca4ba050c8f79b3b4b1ab85c360f9b5c1a04cd428b5eccd10ee79675977ea61194d52b299cda55c5eb80c49734255a3cd2c894803f81b62ec7702e57d54046091feae5af06a6fcd9b36c1b1d1ee08ac126b2fcc66ea40a0e3814c7d473023be4ff195174679b5a42282320cf95915a8b2a6ede9641a55ef4ad45cbd91d43ae0d924ecd54c310aed3586b17f76765db96c9d8452e992472848539fbbb2f7b91ea3cd7c0ec82fdf1cb0a56bc2c07a3c25f83ce01562598cf8d43871b92aec3fc17f6f7677193b264744e1695e7c8030e6b7cf78b71dd3132a98c1cb71064326dafb6492b35ee5f288503020e2f9f57e2599bf0e7df3d6dff59c55e488e6cdd350e749669c9d7bf98eb0da20707bd766a3fe1017f0fb30a4be46eb693280a1243d98dbd18d7c5341632fe7ade366f40067f75ffb9c96eddf3dc5268a9160675c787b92c6e462de98ab993cc5462364cd6d2b3a23ac3741a711914151520348db9e6a880ef719f2a11928e4b6d3744327bcc1522034a56a80d6d3c5efc018c1cc2cc2c72e1d67f62dde78fbeb5f3b75660136899a96d16c47ad1f60ec994576cfe486ae81c32be3b408b1a0b437c64077c29d7fcaa4f0594c2a6fdbaab23d760216fbc73d4b03a70dc05cdf79f0545890c74678e7c36e8e989c97259212affc9d1d4efb264628c3d6c8e4f7c9350270614028c7fa2d6d9289e758848109305b76c6a9967ac55b003d4f922aede5e008ce296bf2c6f52b482e28d9c78fcd2087aac33ff8e5f3cb65db9268fbf2c472f3ad74f725eb6008e9eece1dc048ac1162ff228cc05ddaa4ddbab7f9da1430c3dc10f98f4f668185ade23b507093b0505c767188b8192de3713ff3c1f91413b2d6273d7b0f72574b7d36b744c6785f229964d857ea391f509821c55af026f496faf583d73c447a6c9030e9a3c55c64a683d387971ee6e1d6105aa36f770f27fee7d97fd34fb972aeb096f1c7a6ff35b3e189d34a506e8f47a17dd1ad66138fe3bb1bb8c7e40982b0068d727c06ac112d2cd6a338e19343f525c573b07b7c6d7b32814e8102ff82d751f08b02a52bd2b17ae7bde45e1cd77ea4a5754a5b442ddc7bcc2bd1cfb193f0fbed41aed1c9790af5288b53aef36a17b837d646883cbf110e43c7b39b3963bea3a5d12fdcdcef14a8cd39cb0c8555917e30702093fd19f864abadeba080703c0aab008d36b1a823868b891463a3f25a52e31687e1bd5a5a1487918518b743095ff6ad8a545008370c972e4064278a0fb083c32b2bd8a42a7e7119fa534058a3c8fa7ba999eb50a893a490f5d4b10ea4e94c027ed466e34e66d28ece57d1a2035739d206e1aaa1b37f3cb3e84119cb039588a635d795240164e9fddae3318d78b27f90b5a9149034edcc8480785fc34a0e3b8d09456961eb20c23414a25234dee133c4990ec78ada8779bf7d21fde590b48af7fddfbc31abb94679029a87bd4967f0315098fdf60167930fd6b5b5710a4806684879d70894ad8192debfd0269be3c606c6f97f689e359f10fddad1b391437e1d9cb681fa52e8a0acfd63411142b3364067b63b786eaacd10e9cca6d5f37e229ae79e2e8af5e83a76f4d670f253c9b88f52d2b390c8dca3f759f0adbfeab33db221a4b4d11945a8732e78f70860d22848d7afb92a42d233159ac9f3283c2e0ee78d7f779361c2d6d40063f171dab81bd6b94fff2182ae21b1a38d787a960de8fee426f38b3475d5faee528d873eef395d8419c41ce2ac138bf960a682c2bcfed19ccda40256c6992cc677192cdb76df223607773398fbcdc3ab1ac289ba0e7494c165a13739992ccea553f7d58fe8be44533d17eec37dd96d9f4cf490486567c29b03fd1c290cde96bfe45f0291b07cb2755d9336c39946aff671a67694f50921761c151a8166721bdf8fc34c850183a24e00af223e0acc9434a505b115211d0cb3df2d489ef3f147f016a7bfcb19c6d973b6c3cd335f931c9499e70ad2d380aa2578786ae98e719446cc5162729d8bc6a9b4132407f926868fb395557ca773537306aa2a86c48480768639c2d086d5622b2e5f26464d1992e6a6e5a4aca12e012a5ba889d53e461d0e5f86fe8693e3d2ab629100398c503c6efccf98bf7d8ff2c335748643e8db39aeab9eb4838e3675e55224b00ede60b9f63d08264fe38bd5357f6a8d727f60d8f046bbb24075ea302c9b6a345262dc7bef92ff89cfcef0d9d1afe5fb81bcc920fef34a7e9f29e15a16ffbee175b5bb823ca346d953e76667a276b021336c635631a3b31814f0b69c66919907f6d13e383a911a451ff33b2ad662175ec0037780961bd69609cfc0aaf627a8ed5343037006a6a2cad7b6595fcc6b9d0fbbde03fe5d50b548865b8144d4b7465981fe973ad2d661b9e87b9891bf45256af1ac5f6060ae4afa4d01db75d27a0c1fad49bc270a03a0f52d79c84332502ac150840b5259d06b7b43694e4972b16743ea3a3a551eabbe64155f75e324240cec434e0928cf7c03cf9f07acba32330949cdff426c7d681abfd6191b300de39578ee7ec880660223d5624523a7c0618b942d40d89d5227efd251f771e23619f79c74be5fe9d1a7dbbb8f1de2c8bac5e181099f533ae753e8fa69e703d26ede1ab3d88b8153ef204959826a2c65746156b74f96de4a229f9ad0571dfd05b2929fc327f02d8f90cd4a1b1322a95abc923d621c63580ae964d6c57a03044dc1c7829cc041fbb9b5d221379b4c693e3841118f184b253e770e9314f6de2288fb9bf188dbfe0a76141bd99f3f4b286d1e98c47b16ff117f5db3a8a9c3edb61ecc7701b67d1afd05233640ff7d64e7e0c171d93184215c41232bb10617991b4d085c7df7f2b81e5f78d747db783cb96c3cf7c56000601b41c3d9dc6b01e05307d8e5f468168d5896061d17d3636df256003fc5666b11dddf62d3c520b78cb9db0725312c373f084082f34b7325369f3ed026f3c088d18db3f13d6dab320435c88b032736a22ebfa4da3fe240ae2470c883ddfc09053305d0c3e074de804c67f09a0ce53599e0685851276ae3e22aad0a80c037d9e2d4e651c91e330cc2c9761db6de9cc4983cbde0f71bcbc89b312dc7091833f05361dd03345d85d623a1405eb796b9bc68f87a3af23a0e6c6e52c05b4950231102c7b43b9f393f8e2332142a051e8132684b80c52f81e2fec9c8f2d39bdc70f858ad175033709d736115c50a33d6440becb009574da32cb3038afeabb088dfa4e93cc661839554169288ca930bdaf6e43dd29f7ad5cebb8cbcf52e83439cf60a222284ad977adf9dd21c3539492a600e929b2a792a213d629ac9d8dae626badfa5d18a6e1c04a4ad7ef27c2d9119a5c803ccf40838dca47456843943ec8e655014eb8406bcdc4f79f4d722a1682fd98905808d16c01cbcf376c68725357ed1be9c1ae630146ff10f625aa976ecb5d67b9b3ab25a4332c1a4ad57c247ed5be980840d8e2878a809b10fe294f3e60bc459ce9a8471e405a453acb7e7a10ed69fa7db5ade8ce2a712c0db87fc9919b75dd5afe8d6aaac9314e3635f2efaf606e83af39feecfefd42ccf0bae9f028a7f5586613bbc9095c52ca8a9a16f43728ee4d402c005d260295ebf0ed6c89ee350b03ebed63537e2abf27753e9a97ae125d9ac2aad801ef4e17f8de3c3ede27baf2b2e7d8ba86439a5ade1cb69877f278bba35d33c31f1dbe679b7caaffd7739d37b94786a66dba3b3b16a71745ef54bdd93f517484876238a1c62a16cf317dcf14653f1812800c86f2cf2ebedd1d6f3fd7cf4787840719340fbc925ae8a78ef2b38f710f1074fb6da85295e5b168c2bdf253df8737458ad14b4e092752ba2642bfc479f0841264a5b60b1de6737037ffd5f14d3b87cd0dd58131c8ed6978bf08d806b47570ec8311cbbb39f2a77af8e70b01b99f79f8e7dfd074e5264a8ab42d6f8ef75b01aac17fced3db902a9a9dfee3b0411ebc189dd5f5e8f935c58aeef4f87fbdb43c6c3a70c44db4b0ec5b046ff68e0fdb12c66c5bd64ad629516c45647a38c7d4c048ebcc20bd4b0f664cbb886c5defb56e93b6ffbc22dd26fbc91bb711dd34289ea9f663e5e4db65736fe52151b0faea2587a5944c517d966fac48912dc48c1aa21edf5f33a2be21ca375b0a70de06362e210a97b7af071b436daaff2b4fac97b2385b5d123addaf81c707470800338c8e2b91c9f25955ebc80344c7fc069b1d6099c1914f6e0dacb3d0c14cc5e88e11553585d25340a84f50317384b08679e6ba3c199712d74ab63470cbfd179406afd3a929b4110b6eb64775f4d43a44d25b2d706ec5698bfadcc946b77d8f4cce05657cab944af0b0091a1a1e43df054403d67d907a35e10ef2466539aee5f1978dbceaad8ee871b3659b862cb4d4b749a3b7058b1f105429ab4873d2d08cc3fdf95568017d8b3d6207b6e10bbe841df9f04deb20cc67ca0549dd5a293628335dbdbb01a935eda5fba219c2ff9db5e910152079642e4ec320bdf06816a413990aeb538a647c5e7962ab4a9d2247435c9e8cdaabd56ab8dcb100b84075ec8dfafa943fc89bb85eef1cc1ae33bc1759c4676bb9dfd3f65dd267ee52419725c5256186d8f31cafb261d8f530c15fef6f5f8279fd312faef504f89df7e8c3e3ffe5e46a7836845a675b9c66787fb788a2118203a06e4158307f281d39df1f09f06a1040fd837c704026810f0fc4ea6f935ba2d3a3dd058c64b019a055f32ad40b1c741906280d2086f319722e9287aa9c2a1af9ec9005811b52f323e1b7c7329601945f5ddb7d16d79264ddcf3e042ba5fb3d4e0f78d9dd6fb98cce4490f2c225aa51055f05a20567500fa86020c6a9a1b2054cbbe1fe595dacbd73998592780405cb6194f9139d1e373877ae4a6f85aafb87dfef427a9ccfa14bd4774b2e1ef4d0ba685df58e5d7059a9083f7b3b2d61ff1cad13fb432191b280aaf813a94f593f03a5616b9b67eb552d14de366589506cdae7895f081614dabb5649d7703563a24911da4ff3be88ef39497c3840311332ed596134acac5c6eecc271bce8fb81ec31cc8fd4decdbe93e9b6e8367e1cb4ea7b137f4ad1730f37ccb17d34c6ba58a4bfda5b410dcba06659941e2da6b9a0b58fd1b3154f8ed5c50e6544e11a4cfcd3f52ff3c34448c2f5fc12dde6bae13a6aff49ddf4a5b02ef3d80a4914da1ebdecf05eced1a36d801923f5fc03ad2b7dfe8798dbbe1892c2f6b2ce6ac12565800f23166266a34b28dcb2445f33e423b4e5dc72d33decb451b7aa77a238c9953f665b955952699edf8f742754ce5372f905929cd0d3e667eb0045ec63c18cc97ce953b2bc53c10da228676deed336d37131ab160da800637b29c45499bdd02620a7c149e17e65b2c5e150ac0a0d31fc2f296801c4281b79268505cc980038e32a36e798439d1a16463fde11d55f86a58555a22eda54487c67e0b529e00d6aee013e709987c574dfc84fee2dd5c8d7c016ff079d08ceeb34c7c162f72f4cf810f576aff437bebb5cdf7612f6b0eafc862864a537cdd85e195607e91ee084f9243b0396f24d21e2bfa5bf55fd32c804af4d2ac9c8efecefd14d48c37d436d09937fe44f7ff8b71f8cf44fe2610639a09360634f5f7194cef7edfe43e63d9417b5e8e340332ac8606e0f13d37b642a768bbb3216c5e1022c20e4342e84114357af70d09a637a0d6707e247ce891e0b29bea2152942ed6a5c1bbd9ef031842e34f597666062039f082fff5f2ef876abbf8c210e12c68dc31ff9442b1c31e760f1ee58ea336f4070519f297fd17627adf6953e97dd35b7fb252df1fa86b53ed7cccc16b5620ca6c2514ef61193ffab06fd41659590fe9e43423994e6e9e17fcb6bfcdf1074dd1f6376e7eaf3faa20c14f16f4b86c4fb418eed02d2d9f4ae63def65bb2cc1d9da7e86c8f2cc7488791dfdc86ebcb6a797f8e82fb68c6ea0f92191af3f87099e49ab8b1828051fd8c92d4550f6ea4c7b9ff6a0dcf2beef4cae9ee5513e76a9372ac0ee17aadc0f687b5d40ef0f8b6ad867282ab0ef04eb05b9a82a08c6eaa5aaab9e5cb2589978012ade419fbabf88c316e9d393ee3b9dfd1189c09a45fa4e40dc1fd787bf6459853989c9551edc9dfd064efcb4f637780ed0cb11b7c3c309679dd6c56aca2cc7dbb9c2342c6036c34dc89c57c3f101f8d85f9d6d59cf0f66a30be53af0d677fd4f69f89b2dd2ad299527ca25bffe9cf43f8e77de17bf93b1459bd9305c1d55f7ff0704f087ff32eed358856119e8719f760036f7fc4851233b6b3d8dc1359dd5d1184ecade4a92904978a3a43523f27515a5ce66bea03c7cdf1b600f41916ff8ba4ce63d6879165367b415cf5c097e38664c0be38f46933a063daff05b0db8cfc265bad8742612f50ed608fe52249ccb8fc4f6a2f0b17ece81e8758cba9398eda40c0ca35a1a179ff32cfd7a10b8389a7c2b5166239cceb93e82070ccf97d596d7579a20a62d4253e8c16e32499f8a38f8633583f690c64b81016a9781f6fc80a07844ed897fe72449cce45ec09b7c5c8eba63d66a2045e24ee662980463751a31dc745c954545342ea31ba987451f70709300b79063d4d3e46835e77298be462a1081cf5468cd040204e3df669038cae8fc6741d994e34b85a7bfcd11280dfa46085c74eb2d624e32cd09fcbcc0d0e9fbd45e964af7c6a02b56c671293c48a5a82ceea17e14600d6ae368823941ed9fe0285fe88d808eafd9d1bab0fef601aa76774043dde19a70ad96f52585db3617bc9e4e1bbc00b1a0cdb8f2ba780ae73b80615d3f46c90131ddd3a306dfadc334e56386ffe8ada1b7cb41af9502578bcc2812bc9188c77635755d840f354cb965345fc3a28b6fc0b9ae048d405171ef363dd0c1fc39b02ac129360d1653b80a685d80a8d2320cbc6513481ee499d40af7042c497753a25b900932b194b1d685f6982a4c2796fce0849a6f2fda0e92689db0c68de313e725409732391a4a0046a8249cb42068f410f4b96b9c261972474549d415e8dd39837e9e82bfd9dad509bfaa4ffc079207abefa4fbd44d2e26447ec261e34504a8b55c4ea7a467680a4788fb70544b3a3ae8e1eaba04e54d34676476065548d7dad0fd190e1ad42e32d6c94135d3bf98f8f816f536a30316042cbfdc76aa8108190369a52b8ccce111771670869d057d8976d1699c3fe0342a38d7418063ec59d9099f6e4521832cda1b94c7c086da557c22804bf07bb301719dc571811c99970abc56bdbea7a34cd8b18326e03d00974fc48813eb40e203bbbe72dd05f4ac27a9538e608d326adca51ad3cd1aec1d997a74b3b7e9f73c0a067deea051d9316684e3e191cf8c2ac8cb4f596e832ba6831715ab40b09d5511304078918db121e1330805b3d95190a3e66bbcb4a34db6244868b5bf3c308538ad2b300796132f9b64fcfb3f4c00ff13f8f4caf8e8c8207a6c6e1536ae879695f17c061eb6bbba0be947fe075e4d67763213640174ce645b72a434f4f3e0c6c1ed3d937b3592beefe7695db9115fe928372fc8be17a587433db8b5de59d6ea7fd80a24e04f48b0e780ae629bbc4a3ec0f0b79bfc8f3639cb7e3bebfdc31348870fee7f0c0ef573b680e80b647c6dddd165850b7b34e07ff6a4e00b36c338e7c3492f4784041937d983ff97cbc3a08bf760c00d7ae37f41d9c8d6bef0161acf7e2a8af1b2afe7ca92b140a5ae5a57d531fa656feef083190788ff5975780d2312503e11ba792d921b0accaceab9b7e7537d522ba063f18e7ab543174b755c2d6fb70a40de667ba6118918221758f0e06dbb9ecff078edf2a5d7005449d877fb553e087c22e1504f3896bcdb22ba8630889c3892c0346c536ad226b3e60217341107b7ceedfae870ff8500b047b60ed60f868b6d27227b83b68a3d7055d4507e79e4b1997becba2e6c17e50dd697b99dcab7c42d9c5f4c39646e714081b77b26a43f3502da129b6b0f59bb3a0b6508e0f9d50ce9010268e2b24201e4521cd71c152b4322b9300c3c05518bbe4d7c93534c385294011c00ae7f8cc68432d510f0edf4aa121f24a3cc5091af801280c1c536e928ef3e3e4c4fb22b3be74996cfac519e7debb9fa2b54453cf86a62d85bb6fb541134ca8161b5e0129135033a0dd9c5288fd50f5d331b0c20dc093f73c29335bc7043a45cafd136e5b4cccedfe6c8355963e36bb8a6333ce035a1ce2fd57a05e5a332c3e9a36f8c3f5c019fd84226147b864b3556ccdfc4bd520fa483bd57c395e8f91e8ca56d1fc45c4217da54ec92539956446894ba89677bb0445e9c9861268a71da8bb09eebb0fcecbeb2b909461010e71f9a48874b28268c7a1c7343c38579621bebdd160eef3f135ed82ab42183f9318f319fde41e09b0eb1604e5bcae2b0ff6996415098271db9db4f58e15890e15e0bc16e08d9575cb046d2f5b545740cfc543a60380630f842f3ded9ba57184a54afce5f9bcfdb3081a7ceac8e8853d31a886148816ed2d3503b5aa12d11520d17e62594c19bb3805c7ed7b0e488639441466e61885202d987cb9dca55529d738f77289f093fdab7bc958e7c9da35a38ad40157f77c11b999b27e75331e86d21eaaa165355f4642335b2b921f98e7a94d5a4cd3fad544a522840ddaa6b8487a4d6f051a7d833fd1a02c1dc5d33e5b100d3395a0ee6ccb49af4d16ee6f17e8e704bb17d828b3d812caaf5d0e61a5f105fab1a9d22e5b0d1f77a21b1f15427ef6513d118adc17f92bf215cc05a5fee8f4fca48a6152c40c948e59462bebc010503dff0294c37407be705ea82f75174ca2ef23139c78c2a5b9fe5e4025143f3b1bc6784672628733f229b2900586045d055681be74ad1364688621c912a25d3468c42ae54ebd32ea8f57f10a1d92384790e5461386e24cf4bbdbc854936291c78a94918a2a98d4e7495d359ce3997f9bc4dedc51b0cf471680c8c06227b2a69ef6e3075e7d1eadac7e7c1228cd73f82d75c78783ced002948a6d321774badede47b7715e9f5d7c574e4f00eaaec934b54447cf60b0d5937281200c10f93e0ba34c62d89c99fac6a2f72d50f5ac1c4735f1f7ae3dcdc0b9ccaac4c0d6d559b145043da2dbb7601f9a7c81c919a14a0c92ee35cf0eacd07df77e37cfd1a78c312f88ef9f95bc10f2681d688fe7f57e0c126505ab2dfaf830e4681ed997fbf0756d805b646f9f88dc00f8340ea18bffd3dfd895faaf8fe0b5030919e4d5b1438494fcf260a46d27b6a090ae9582c6578b5580e0581ded572325f77b10657f3b8a27ca74b9d360b5a5b93c77ff34da8f240af061f462c35b0c32f135316b33f172cec77846dd10f1343b407c6b96ce49bf871eaca36fe6fab71c2c217d21cdd0634d5ef4bf1d4cbe89a9f33f457d1b647289eaf65af0f57522fdbf6aac23b6fbfe5edbb6fbff5e6776f7bf3def7df7aefbd37dffce66deff2bbfef3dad1cf128198febc61a33feb5feff6d3a3e2b7f8bdfe79999166517888ec6c8163e1f39d38783b1451e29ad229d75567018bacb1cc0ad6ac65c92ad6596289d82d9d5c1cacd138c062803a091fd1b508846b8b0ef6107e832045fa54d64be2347509838660e5de50adcf4262c744ebfd4987c5d7dab1e690291facfcc5e72d09b129584e89bf807aabd84270c0ab7a188ca64b3f211bc27a20880ca0e61ed380ab1c8020c977cbf1b33e4a744a33e73c890cd689dee9f5e9a34462005144c96a574c3ca9c6ad67591e257aab62e8a591a7dc1cf1b17589fb5b30230945a24986d5d8e1c3a186ed600867e68567a9352478635f959cd8ca2683ab4d9d7994cf4c0ede6f9c511c75e6b18912dd5abe9bc28a5bf8254a34896dcf19c26e4c51a21fd5f45e29621c66aae0b985e80a18faa844db4fb73ad4933ce2c4e88e53cace2ace9e94660666941d4ae4e0651a11930f1e1e84245c60019b3b710f10a8b6dd7fdfb1fc62b993d7a5e2170658c0400e447009e8086e6ab8f35c1b184b4b51327bd345ad2be9c47c5ceab2044a33ec9d75239fbe084e7969b85377124d104ec810947831710e7cd073f695d284c7263abcd0f2111d3919cf1922925ab6045dd1416e3c9ae84ba671e27a81e9dc39523c3528dec0ba5ab84c3a0a204570bb3cdcb87fc373ca835a288556c80ca7cb0becfbc5e00b505f9d4f04c32be0e614d863a5354a2f4de17f5f6275c373477792d6b58b19483fa1b1a80efa7534c722eb08cb696769bdf28e46f183799fbcd3a4e2dacc4c0431a3ad1f6218dc2c92e40f6c6170adeb0b6461a0089a17d66e5426be8092c2e0c896601a3825f04cfe80c16d0d40ef7eb484010d95360de4f69c8504c683c3819ca75bb2667815e0f2491883893a0210ffaca92287a98c1654df6f3317694f98777d65178e330c08c46393d02018b9e156dbae3ce3535f0931a6358cac87d839ce79c8535b6f01e706675944450299ee94343bc17880d05087fd3a5c649be4be8f5a777f1d500cc6ea45287f67d530be24996e3e54b45d4ff0a6b260474a483d7dacb2cbf3749530a024b45d0aac7660ecd7644d17001c123e675481a98588a691b3f2386834280f59da60c233071e9194adda5d23efd03c718536c42162a1cfa160382b505f14427a383ee66b4368edd27a62d5c915f981fd4fceccaba288b5c86fbcea310bfe7025b69f610b2453824d9ef1c28d1d030356eeb3ded8bbde5f405fe3898f5ee42c9b2c1488279b3d4f53a97c2e765aa5393bd305c4f7745aca31a26a4a36d64255f06e64c72e05cb9f3ea287e7c8d445a3634562871f46e7724392689f81e0dc9a8400a9bb125123ca20072b36b558444e5eb2219e9f433cf43d6caf40f19ba36cfbf86fd370299aa5ddaaa91332f029f2ed44467fbdec42ba4172d1e9a2388b99dd1b6738e02f950bede874d173b6b1305b58757a8664cffc953ce8f10175b12ff8dd329b22d4452f2b072bdc23128df3a0fff247106fa362804875c030000317eb706347c08ee6fc1e84d1c085ddf60d1f734a71aec5e62e2704315d87f8570b9a2fb0ab29bf820571524bef4e368fb9795d1dfdcdc53b955cf9c592731c02d9512ced812504e11b0dbc2385afa1d075d45e7b61b06adaa47b227e0ff3e309080799040d1aa7783d0a038509b1b0dee0d54d03d9fe259909f4b34b4884a18d5a933d056a55e04fe3529c317883412527b7ed5ced15d0d611aa8556c1805c8ede821d3cae944ada5fecc000428db07f176038d6f63a993704864d9c7e30e1ea112af8ed4b59f90360be3f255c9ff000ae9d98fe866321b087a72fa2b159b5eb3ff2070aec6a4012add92f8cd00119ffb2646d235ca16606ddfc181ca012fe609c51620b2c55c40ebebd44744168a4db2fc88c0fdedbce47f73dcc031ca8211fe3ab0e0525a81309fb6ff522364ade80ce35da76e79e2a9af00557154742a004c3e354e1718b7d231ae11a354723e560a9d69b81717d0875a99d9213f119e7cb23810af267d18fc5c57f8b6bbb8068015f605ddcd7328fe4754c404d5a8ba6ebbe9235954f6922b202e3e4e06c210445cb14280cbd9f66ed3a1bb72a9cd0a1745d45f34d704addf6ccdbb56783c3b9c75e0c8a7b227fb767187a3e000b3628e01d8fdb3f892eb5ea12434a9eac4eb05f5d86298197f1bb7e49106ea9b890f66b7ffe5d30dc7101c7a82b684bb4b40801381d533e305bb8771d8760f4bb0012cab534e114f50b5e6d3788f6c70e27f92ee353a4d8f50db0c557bd23d47a65c693bdc4ef78e191c0554f0962dc25dfe45ee073b0f44ce2aba78931dd926f72aff13b5c7a2571d5d3c5586ec96fa957f81d2f3d9378eaa9626cb7d4b7b4577c8e96de495cf53431a65bfa5bd62bbeea635429a6c34e576242c20e673721732c893344b2279602a6c6b57e103505ee7a1b4ee6fd7055d1dda0359fb6bfeff4895adf2c77b4dbf497144fa1c58fc95ea4dc35aa08eb7dea5af64afe87646f52de1ab588f136752f7b29ff23b227297f8d56c4f49eb895bd967f2d3c156df31f389566fbd7194095b9cbc58ca2acb365a159734d1f885a017bde66a7e93eac5ae83ed8ce47ebaf775d92d67fd635ea66fe49c6a768b147622f216f8d5ec4f49e72977a95ff23a99728bf422b627b4fbacbbcca771d3e9566fb179842b3fdef0c48d55ccbc24cb1ec99b5a0a9b1a60f46ad807bafa6a3793f5c4d743b68e5a3f5f7bb6e52cb3fd31ded36fb25c653b0d823b99728bf862ec47a9fba95bd947f0d2e956efb17988ab6f9ef1c4895b94bc50c91ecb9b5a0a969ad1b445d817bde46a7691f5c5d743b68cba7e9ef7b7d92d677d615e536ff25c65364f15a786adad63f602a65ebdf39904a7397891945d967ab42a3e64a2f88ba0277bd4d4ed37d78b5c87da09d47cbdfd3e488484d43c96673ed51d08216cd59266616659ead0a8d9aebfa20d40a9c9b2a293d9f4d2b953ccf8aedb575b6ad66bf43d05abbdf5170155dd3bfc1edd0d8cb70c7edf4398e0c65de942c31f40c3610d3f8d7bf4706213ee3016322c1c9c3cbfa975dcf37f8cb90381177b897125f93573825a6da231640720901e9434536df2492cb35e66fd51771b5ca510bd471720489b9854011eecea57dbea99758d0cbe7018a0128507ab4fb0a627d1f83f806aa3cb4487257a1b858f21d66289fd203c2fa57994c67b4d86782ca77d2cd2ba426c5dc50b70df9a82ecbd6c00d89399d2cb1a261367e2808b25755ab11a7d5154be0e944d4acda4757c1d7531aa94e2823a06c09f61908a0349f5268df9cfbf3b70b97afa0bf35768eaa4e4938734cbe2dd68070747b2b28b02c3d11d68442f705f1478a8402e1bb5c166cf8857add440afab5caa1325aa3554a8534ec4f8276ca2b21081e03c413546a707b5a60cba4b3adad8db1c2632b630a31c58880d1f86191b354b1b5b0c44a0111901140f0dc602eb90932cdd63921863490334bb882534dc68c356bd79d2232091538016b6b6a0d5a7ff4d309ac0f69eea74c0b4e720033056a6c734299a4d7ea7900269861fa0e53ce775dcea9a94bc343d39adbdd17347f6fa603181b28e08c47014fb183f827cdb1444cc9c0b54a6a5934b981fe1797612d2b1ead2b7c9801c6953a3e2b97172c71afe52df5b1ea0f12d6cc798db5d6d691c8d70c6e10fa4c04450108991e5fb13158ae054070cdc486d47c4a50200c9b4c011032cc7d45c6bbf7f0675724653dd82e07ef0a419aaafac59571896eaf248e80db7df95b4843e81256c00b6c5a73af8742ebcb672d1b7da70e5d41a358b588ec7d341c2a73c0643c0bd307fbbf779aef5ab610b20e5c0177a9007d3b044db7cef35f4ffc4fa01998462e244d47e2145be31bbab98b8357fea9c0999c1c84e5a7ea57701fdd122bc21865a0940f989c938d8ddcf22bff032b41397340bdba126334fa1861d2d821b6aa1504cb0b4e2ab82878bb9cd151b0756d03cfa40bce9d06ab4ce016ac687381be8740e57e3162d9179cd07446dd38b90bb85711685cb585cc723f88188589a5b3ea976cba3e81d5fe7084aed9288e4013c90e01e0975699f0411c5e13c4cf62bff317d35916da2f191cd78daef965e8140cc39afb13cef616d1e9a196c7548e400b576392a57342d22c1292e642489ab442d27685a43f9a4c0730a898ad425604db8ff9bf73b249c6cd181a732407f7abdd7647fc7db8ab24f989dd66e84a391e66f4f2ac9a5d08ecabee96d5bcfb14245f3457a82468a1b1b2aae47858737e0df6e379aadd0559383193a80c589064f754b11464693624e98d97cf261bd9cde4a367818981470a0cfda9ffe96fb2547f98177c99ac1db79620d93f6091ecd212d34352fa3fd594207f9081e33d58faf843a301d2b7a662a5604180c8507d88144e90b1c77b0813f16e52381642e4413a9b95211acc8f362112897744cc67259bb97b84105ac930788f7819ad641ecc171a845c9a05f536600599898e025b0d8d66a5f9ce68625d079a951728ed40e5c5da8a062e15149ecaf3583311581d7769e11232676a6957785d32a5592900568408f7f84b83a2219cb30b6ba15e5256528279207cc41b4c8e95a2f70e314f3e1da0e29fd8397e74708f6acfce068a92db8e98fad0ca7f5575dd1c71112ffa7a0a6e6574ec7b83969772ca8413b1436cd75dae1373720ebdfa8beec3de931a21d971cf8f58233d695f0e59519914db3025e67c2f598aef8506f813389c79b8a6a88d4d338e4ae45cbd792e4661f5c127381e1795d0c5c3e7629e74dd608691d8fb5401261674cc38c8f3325ce8c33f6eb38f931a5c725d68a03eb9bea0c5855e13b09ba6caabaac83135c13819d156038850d78753b501a19bb3ed4df02041bde9b9be74b824057eb5670e1de0136a92baaccc637971e5616ff8e7e9cd7d09a5c1416d7efc39c8f92c635b2b84508ba6f438bec09810b569409a675842aaf3ef0886fdc74159ef9ba84a15fe2911314e3d18d1553ca8925029c4fa555cc4323130d8d34fe0fa7fc8bb4a364b9cf1de2d6f60b9288529a7a4d9735647ac4bcc46f0c02d97e37facc228e908e596e6f694d18ddcd6a9df4d7253a67f4ffec04e919aa54a18a2815c495b654e01e57f622ae6dec4916aaaed19aacc6d2049ab6a5d2eaaad8b93815a62fcecbe04ec3e3a935900fdee12086437113c9bb3711937752f54aee74921fb1b6989d98eb86c0edf64513e35c38f6ac67e751765564dd0cb4b4e9edfd6a89a14e7bb2779e36db233127172e02123de3c208cd83d84cd893fa95e9c236bf6c609acd9528c1f26fbcca9bf50af5237ebcccb29d192da0580ea719c7039fb455cd40797eb2e2b714398e727f13eb487d637bf05f1ea5fc419a06104e5cdeb669386911995dd6c7c6e259ff066f8921a26a25bc3a449ca19c8bb6dd8a8c25f592acab722a6007db8bba0394c01a62d73855a93d5b48d14ad18ed74da8267b82d425771e3a488851c3cc1a2ffb02bb977e29e14f153ceef59bbaa376f9e8681007493ef37e3414eb72402c3500e1f7513f163e2dd70c1564f5bd9ee021d0cbf7c45ecd93d7e6d9d64c478d6a2d617b02138168c253e059b686e2602a62586a247841e1f68259efbf874ebf8fe03c7302ed090bad7d430b9b15efef421880764a98d244556a9a132b86c4dac3032d01500d909fc7800c70119758893d968f0510696c6dafa57d8c8ed016df964a07ba00c62397fd74a0db015936b48efe2289c7ee24e2dceb6abfdc15924bafeb19603cab1891b0d2b24fcf6526d62d8090628655b0047ef13d720f7eb55405cce0cd0cae5c1311111fadb579eb1922a8c981e310a27ef83705537da3fc6c08b90bb42584e655238624335b594bb66cf75e3d095a1b97c9ea4a36755cfa3f73bc2908deafa2f20d025f3f82f50508ceec1bf780987ee332ac02432d602673eed0e711e58860519b2940112d72c955a2c127e8bc5790425c02ab3f6671a2a38931858c652bd77a58d170862d5c3387a24d5efc193251866598be6b43209ef339299b51b53321c0877d2ea084bdf8fd6e065cd7d796585f0a7256e8829b8383299960e98d82a51de1ca0377ad3d4c4e12d46a3b3419850d0a6aed9598da48b7e12cb71dcaa74e8572ae71704904816e2107e690a9913a98aa2dd87ed69800dc08c7c64553e344afd59ce742c1955005af2b1c4d9d87be514366f7f33fc1a4abcffc01699eca3777b10b4ee4a9bb10f2e794db3f85afeaeda7e01ad1b269b07a6766e487c2ee24bf4a4db43183e6e69021bb3f667f0801b0df7f1d578dea61547130968a17fedf37a181e4718668250fa42857258aef85e48c6a64691a9a460858b4690a9160692844e9941411d2abdb2dfde444d1c5ffcf0069aa3f8880c60883807db47193f6aed0a09b4d2bd9145dbdeeda28d80afa034a283b26743308030b181163ff50d7d96b675df072286dee075e3f64b284209098d6ebf5e8225845da2251c4a66a54e56de58a0165e42f6ceadab3aa2105a0da292e2d61f92598fbcb45a6c8b1c396303e9a12839e64e7ac931e60e847d1fd866bcda5d5c4ba3e581fe10ba99b4a45f40e3fcc00204f72c0f745932d052e9a56d8d524cb62230b23cd079f744b5389a35825bb9d2a9aa3fcfa197a073d15b29cc197191f5a9b1bb353b584328f6ae8712b41db5bc0e3dfe60fc746bfb01c855c4c7914267de9a429c5fdb91326ef0d4cd843e4c49721027db1655e7c099e7103168d42f3ae8467e4d788c1d0874294085307499503473c9abdda33a6a61a32aa0867d8fdaa0196db6fcac1f127ecedb00d2820b514844a52886bf6ce3c4d3c47a49dc6032cb29c953310160dd615038943434a478375a7ba90e99a2f8c12611e7ec13850a1ea4a13ef60c6e290779e657feabb185669a835311b6c0235c2c61b0790adfeaa8890ab762ebad010c10db58f84ff0e1616485f87f5609a2817a20a149f3678ae0580a581965e39f562705da09882fe6433217f54f8b64fa31a3480f1e4c809c907d9e138e9bce4404ab02bc55300d93224d12c1c53e3e3c65334720b880a4338904f3f2b8b5a70630118309c8cc6ad8f649c409e1f5f94de664d131a1f3eb724d1d55a1e818e4bcd530729de090dd3bb080d0a45dee2e09a84f5474a03a36945f1cc3ae34baf7cc706649d2cd4a9de4f65c7ee679ccf78a2d5ffff4bf4b595f8e9d5584a255279d0d99fd6efdb0b5c71292c927bfbac515341d83896401f8ec02b4e22af0dd7fb6ec79def4b7321f7f7245bd6b0831e2dda1c9a42f6c7930925fc612bd439afb201b32458555825e085aa69119e11d28370ea8b62c4d40d96a7990010e6f07ad0e9ba6fa275d108c0bfe4df7478be008a7438aa3de44d5348b514e034934df6d4980117cbc082aa112fc260f4bed08f7206cdb71964a971090032d7bb4652d9fceb7bb72337d7ac2fd32436d6bd209aeab38e5d6fbd479e1cf5b426cf59364e160b5f806dd8832917ff2c71cdf9f43a87c2b2c65942e2b13b0df944669a0eec7422c4e94587be9bcd00e955ef4ad1dd324fac0d2584393249e9a9ac80c67ac5b205aacce72154db281c1c554a3dcc3412eeb8b01303d072968714d7d656b9d0a180ee11df4a15bca936c8220849fad013931fd3a72aeb972a0fdf2aa45a7ea1cb83b69e7c6d31bf1c622354220536823a20a72a529c8940254b969179b44a29ec5a87ace8971fe31cc479cac16d575dd109205c7641d9b50ad5e83adb71c4447b8eb6ed9a6eeab69f8d7ff281444f58c9074d029dba8e74efd721c710609b7dc60f801d216738163b888c35e37a6480cbd62eeef63e0933da08f5591416daf58d13701d20530dc0a2b3c1c91c6b6d235090017486b0d537056ad70b178612cede01c81f03e5acf1e802e775e74dee04c0483b28b5e71605ae95565d94e29bac16984e78277dc5115758e48c5a79f7701736aaff7ae9e9d68c7cdfd738c004e4e3a2cd5b66c6c8453260205c777a3fac2596b397e15d56cec5bb23685f23255651d8c0aa4005d377b7307a2306de98222560f449a4f16df7a98caafbc624589f55292f8d95631cf571942f8218cea79739e46c77a8247fa669d52bbc3eea686abd5691ba2f13a61cddfdcd80a9fe37f481d08c6b3d60c75cac448e3605fa10fd053e68c0059ced27e5f2798583f84372cb7d414a830b06001a5db0cbd44b1c19c1c76e0cdc9f58082aec25db7f9e35210c824355565a1c466fe58825522942676163f166c86afa0bb10704aa68ac017fa9bc9b16a4e0dcf558a5415ceec4242bdd546b4ed9a0c3c23cc036ba02efe26911a9da67b5032f61c1403868f3db50f2b29c7f120bc0ecf0350f8dc3400c16e48150dcc70c80f130065ed795a419bf197fdeeace02234df4a22503be046c2339d100287ada6be61f210f7345f1e986d8925e1bb0b88a663872bfa4095fc3d61cfe665fb0b395ff5da3743bbc9f6640dbf135d77976ad0474c24817457d5868a330a1acb13f4bef7bf7d426eaab4a0d8eef32e78c6636842cacd731ddabcdf8f40bd50512cc16d42889e9df9e62d75ea0157e56fc9dc063e18b6c8fbcdab952ad67c978746fcc3a520808aeefc8c170e0f5e8e645a74e864a489f700d180d91471758050ed12a75c410c701ddb434190ba8ed9ddcd8b94f5d29652cbd96eea36290159c8387cd44b98be07502b7d1e9ba95fc7eff6509b398d99324a5c45631e4989a18d463d611e43200d4c2661702c0f334f7ffe3018cf7fa7c66e417bee5b9f2df30d516cb8fb70e53e0e8dcf183dd57a858b1ef3e8fc080c04cd53642ac9efe6a5e5388484fc277c67458bdb037b836c9131d1bb6c9c77b603e72b6894eb7f00690a96b292e6731f9af2566ad8829aa61706eddfbacfcb99ee88b86ea8373ca44330185f995956119da8921926a61c0274f06f6e501da71013ab79cd3d1804fffbfc80bc20419fc2b17598d5ce42fcbc0ac83242625341257809402557da8bf374da655211dddb51bc8d56c4f801a34d0ce795eaffb2f6257937fad756009fbdc686826d6a74ee9c31ebccbdab4209adfb56484bb67d30c4624c8ef6f06ec11557ab585ff35b5d7612726da4f21f11dab75b48a3c9fa2604211a8aa6e6e8a02fa22809a18af5e0abd27f38d6d710446a85a8287f94e2d053ddf6a5ad80a06fd766b154d6ddfa3381a4df327be2218bd4b18a04a475f0e765fe113eccf7bb617251167e6d5b19aa1f71997bfb2bd3c5be744281cbdebc48cc694d464ab60415c68f546043b8d4a047df3d97f31f9c40ec081876101c0b3a8aa21334642939499f6b1cc00fcccc2b248ef229234a42bd233d61ea2ac5982c950c41d9fa207545c9f7e5148df6df1d5197d401bf543905bc56c0cffa148480836987c5291664289fb66b6aadad0a76ce4d1d21786e6c26e429e80683f3d03f5961ac8905fa60d8281108740a48d211240f1c46d163152d5c7f22d2b81b9fb5d77043422f897680ed15be9e9148de15613611ce201d5a961a9749231c1f47ae07ea38e4443a72d30c67731abe7f77e50fb37648fd334f1438aa027ebd92654b1480832a62802e92520735f2ccaa2825048364791cd452470660f08c0de8f0f118e99c9a77122beb5901a26a341efcde9b81d31f92d973f9dbbfdbe49d595a578c31b49e93d9000da3bb53401b038832c9c1b88747706900fe43a244f7cd2e4fbf23f9aa6415cb25425f50b6fb5b4d9d951bd05f3f83d46f37d61e83e367900b9622864e7a0bb3241359494954182740e8394752c7519829de8672e64c3c22adb754146283f8f250b95239f2921e04721d65ca566f7e03cd06d04fb7576303d3dec845165cc38c381f573991e91541c3ff7f34ac5ec87971b68c51f9becc568c8cdf934ba7c0af4373f17f1d98d3236107d510ffa61eff1e07947bf270a15a1c3d22e439173e21bc3038fc0a82a3896e3d5c849cda46a089d744ccc4f5dc7c05f141d700c6c86e1732c56419fad4ae3445b3899f811393948827395650342e36c4a3d3f8815c93999b71e273cb4a194f7a3bf0355da632cc3c636de9db95086f9d9bbd3a948b26700d0dd7180ef6848603e451060544ed20901a85edfc04b1910f55d0d3516f1cd22c96c13c849fa33b5b4c5885002e407a89d275de5aac37ecebd2a4c2e5c016b837020f9de936e7deff1cd4d656129e7282773f948be900c52791977919ffc2e34b5a5f7c87b5f3b7998132ef958faf6d817747d4ff9148d704c660167515fdbb54a88ad5bccd3a9390d440a908b3c278229113e6a30b2a6372171792d57293a5fe056fc96505cda410735c530d22cfecd2e178c5af61482917403617d5408affe89ec58496b80fc12348e08c18da8caa976ddf95a86c6d43a94316d0d60356849595649bd88f11abf4c8e76cb6e0843b8fae5de2d25e05e2e7a1a6dc0b18ee91b815d64f17f938a05715ecc18f8ee136a8bd098fec477babe91a88e496787c7fd4d8a435c9a6931726252363aa99f55d0dad11c2d436d804a2df826e9410492aabe2abb2c061513c426e55cc015ee486e3fe5d2f35b8f977de98d0f1e41afc14a568c8ea6dbf23c81263d76f2114884a18f0eeaac6468577b1b9c5ca3ee4e0b013fc95557009fc39d04463f891f10b1653862a8c99100c19e80c4ae3736a4943ef74cc7812feb13c340069e0574d72e9fbace026ede6d22f836059b51b9f80270c7dcaefdb454dd4baac6613cef53d2b4338dd1f4c0f76774c284da00f8f0e08bee02357896b01bf6b2d73a09a9e98b256c1c0f8d70826d283b6d69e7bc6c97cf5f87f6e9e7a9595c2323f529e342bdbc2708299ed641209b03234c93c0097338293f8ae2134940291a309fa4b439281a651a206385ece16764de1e5886a68eb3efd2f3d1e5bf2592f917272f449ef43e3c3357885545a905982ea058ab204c3b310a018718bf4e983c156a84e94fc50d58ddc566bfaa0349df2101f02f64f0e149e552077a033abea414ce3193c7024b668ff57167037882976b501adee1a61e464a7ab48a160050f1a92b8be29be63ac97c4983b015888daaec9c773fd56b9669028d184ef8e1cca35df8b3ce2255a24ee6f9b72a4c1561b493abbb5510a044efbf84664c2e862a756732b8cfe4e015436508e7c6a6c6c365592aa697f073f70dde809197a894548d47765c517275e5225d8148381b2b58ef866ec477b329985da4ce4c960db3bbbdb4d4f5648032bd9b619356e1a2de3245bb753651d9648ae5c3dd63d536c7854a28fe08fe2234ceb0f7b50d881630fade1cdcf1fec7187253fa5efd8c0ab13469656849288fdb8f06a473031471cb288499e4ce6a87ce318f5e989f12881c7b071e440c60c32ffccff0cdd1e8b06ef509d1a7f1730fa3125bbd4b864e10f208d45d553642f0f2962b5087ee48bdfd5d3edcf64223ec36c3ebf2d9c652eed3983a2287c4673673114b24cfaf99ec3e8d32f93f4a0249c5505450439081859c6fb86f14ceb3fe3179e7800eb0189036698fec97002124724580b027abeecf20dc90080a2bb32567e09777b87bc60d240fac2311883b84e2687b2c9300e9a709d9a24f582db0ca6ea67dae8fa0ad5ad1712187a11e8efb0c1f385ef5c897a117710de7e77e18512859c6046f2b4a58dbbe856f6ce35d639bfa538c2d9049c59e04ad33dcb8e495a87bb38aaab4992f6dfc5efb13f00ecb829f40ebdf1ae3d52aa92efe283b42746826d62a9c88baa77c1a01e059d88b41b899bdc771127896ca7e658b12b1b8c40829ff8b7a6ab6a9555a7a07711773afd282a32ad08047d871b73704750f583f882b5144a3fa1e9d91ac54f473c70e6fccd46861a98b721df2c3a53831ffc6d54601ade56ae3d3822a9679098800a7af13282887078451d030d9eb38fc91ecfe35bcd8643917b1d66d5be28bea303137d04ac138280bcd0d8623d7c480908d6bde33263dfc8071f7b87cd39f8bd8602869a5054477d87542b1499e0c487d56a3398839c195abc43a55c278ae594c90a766eb63283c6f1f0b0c5bf7c4ba1b9d4f263062069d088ef3877debaaa7de3561fe2463fae109eacad45a26fa6b74fe19184bfb361fa44cc9ae29fe2a4bbc7d50292b2e21ae12e2de2bbcbc3465c82540cdc20e4a54152a4ad217807197f2ac9d030084eec584229a1f76d8cbeb7be5cec18c206018f0d40837c1893ae3ee424d650f1e367334ec9bf0dbb22dbe8a3eaf61ac5923844659970745e517c79857031547025181e0bf71e569966ad0e5b065c547318e581f22c8a9e0698a7586d1d2d5420afa295cfc24afe34ec4e0f1c84181260f1ffeca482f82279cfb097d644a487d0dce3d80b7d3264a7903c4187db4cb680681086ee323787b3520084cb121ca8efb5ddb7972454f980286d3bc985552e3fbce2ec06d24261a00c80411b15e40483eeb950e051438108dd908c80dd0e9fc6c2dc1f18d9f07568df2fd51074bf4fd22f07d0628ac8ab3850d4c803dfc873eacc853c0f7c65335c4f00e4d13d79cced8aec43d2ab4630e4b3b347ba728b30402ca2983f2c4574a5fa49a57e9a0baa8bc46ef340962542e08e719049f6636939e93312afbf0303938b1333ca941a345ffa537a7d13ca1441ddd4d9f0dffd85e3b261b09fddd6c82320d6f2cbbf29a484e5f071c20b9efeaf76a0aedf867fed1da6d823218968670a225d9afef1ec31e7aa6ddbd095c6a9882348ce9570bda81c788465f04e74087bb6583ad9313e085345c63fba92527cf3d71bde1ccb7b5ece6e1b2a6d0953e8e098d671a4f64543e336178206b9099422431520a9ac00f63746626a11792d61756148895c06a078dfe988b2a14d2f96e6270e89881ea2c25b481f4c48b1d6841e15d5c9766c5106cdafdde7447dfee522be9d819718313449f026d2dc4dc40c916faaab289d921c774e85ea7dbc28d21180c172c064f9923d2d0115a118dc6ab93552212e0f05dd9a540501445721e8fd4fd41dc447be645704f65ba5356188943851c84739ba4d26db4d59539819afa918b18fd674d614c8dfa75812344d5495ad928f50ba89c053a86082c0d414ecf18c8cf2fe567b39d8cd438f1ae2a2a0511905aa12cab31b1bd00799051a3b7796a48dc1c501fdf044ccbcbf98caaa55d7044f4adaba44e9bab458901e428ded6ca215409a9aa33678794aa0dcd7cbe0ebc7fab7a9ecccb0e7ad3a6e89d490d9f7f9bba4a2d617ca1eb6d1c069561a64ce764e8eef6bdccf7de4f60c735bcfa51082f44bb0bdb87cbb86e23831bdf0454270efd0be66faad605d6888d1efb772e861764b3c36edea9711c3af0d04dc385b7b680e2a5f028a957c9341729dd5b826b71e7d31abe53808b81ced989a748cb027f92b810c81d7adf6b13609430aa830ae5331d42754ddb8abf53c6a88ec4dbbc51b10c05870768c91069cc3676ab4d21c542facf204a849ed831615ee164d8967604916515b75ad4cfd7fdb831101d41a3f7d61143f8a1e2d73e3beb722cabaed4bb5ed97453156443c9f31f50e0432aa49b54fad215c34aca70d46cdd062f9b95eeb0ecc78c5a8ef1437866f2e968c3f5db238a81c050dc0c679b231750b6697216acf4e65d33c10c4fc316a4b11012ff1578c54f03b6a2bc8fd2312d8782e6a7d3009392289eb9fec04830dd52b3ce7ef407ad37f49f6c9813e2e4777961f653e476fa650f358cd01e436fa788c0f9370c36c0e677d19f43830f0c1c78b339da443aa4797686594a1a657f6e35cc75f551543f95e6528c8e75aa18ef80ad562666e364a030cdf82d272bae8d8a78a63561a70a4f67dadc3bce7097db4f6f9419e9f82cfeaa44ffc5990ccc3c1f4c5390f3e4ee8d961c0d8e781afd57c7e894608c733a3392b2df40181a0e66273deba0d962eb87dba75195cd647d32e5d8df1de721ad766cca79c1e514e1fb51cc689b6f8ea811f3c41eeb60b5685d8c67c545c8d4a4d63f04d3997d36024df4a200b17930cc3be6bfa5349768fe6a44e4c956aec1310b8c79e2a62265051089510f2ba5466df1252e3d29c4cefe875025a0dc73c3ae11e5dda46957a1f01554c04c48de29c18eed3b602aa937af486a7a43ae1da6b919a165a30455fd643f8888b556b759264d70322082c32927ba2b87f85bd221426d30dc6b15f0c341c4196195658a333ac5c5980c72468b9101f6156a59226280472c5640a53b5fb568420e88e1985ad93e70d63b84036876c2bc98c3820ab16fc214f66f82c4e4d38014bce6c726422096a2554d5ea2e0a9a48d66b0208ac41c0e0faa3dd55f37ed022f65c2bb953e672f0809f3885c034e9a8f478896970423645c226472661f078a2aed75f00cc4e2fad1162746d220605a95eb3ecce0cfbef5453266e34460c8c297298d82abeb1ccad8726ee097dd5d1c8d498d7d2780d6df64899e342f14cea831cf4d7adfe57c946abbe0cf6beb317c176f4a373c612356821a9588a21fa2467ec0d140fb3c05de2a418978d004cf180c4de05f9631bdd561edf01043e374bde20acf38a7568c20b94383f6fd055d1cccd251c1f40f19b2d6d9bb50c2d2a6a6f4135d0a67bc2c9ee7ac15a052084d6dd5286f3e2ed7008c2d97053d86897a8164efc175361a5de81ce48228b9aeaec7844f1f098fc2e684578ce0e44174f58409c0b801e6a037667244a59c3cbfc8539dff1a27369663ef7a6e5e9450ad6ffcc356bf502365b01b0dafec6e4b7e2e9fd538b04ed6614ed1cb3f6dea86c20a41cc23c0748eef9af7c420620ccde82e429e091a328f539875274160e0667a105542f23813b504cc761f036bb0048728223c34b85123d7ce6ace31880b0e1b69e4f093a95503ef70373f085a3bcece7772a3d8e726050245fe19a3fe0e4c3710b0b0ee10330243b2e013d31c4f60d379e45c347c98faf2124a2b030d35881040bdb8cc8641890cca51910e1813399c2ff9a03efc25331eab88d83fc676867d496d5a8ad73170f80803e40f34ec7ad900d57305744f8ee679107fedad6d056e46eccd6f7995d66ec99a924f10e8757609e5a0aaf449953c225c07405c7e3ce6b8e01c41bc9dfd3152442378b6e1d128b064b3ffd1a1614b95a3ac4d53934e5fb7a93e750fc50bd78411367eeade14718612a582718d335e5ddc78f940081b826596b3dfb3a424f4817b44de80582c0b544ee101ddf9ae9d2c78be9f23178b04370d62aa15caa7a3000a24b5f3dd6735c51cd0ed03df33e391274593155f44a8155f68ab21de62063cdcb6a0483e6cb4f948f7afac3b39e279e0437cfae008fd2bce69379f006032d48b49bfa2b9104594b2b3bb0fa020952ec82900e7c791480f5357fc34679464b4391ac0115e05ba1768250611c16970336bd206d4be5fd9aad7f1264640efe4191e0042e4c947af12433c4a7ebe16bf899e2c629dd26c2b30a39ee99d0210fd33b96c7693c5804cc8b934a94e848c1beca4f32a1d7c988f5348c4246f1d039c49964244e1fff38141d3557199fb253a2111855faf437ae013fb983f4d75354bd721e6f020ccf2afef0e3a97467361fd0fa4308e94e6df07c7496818ce0c31a2c6777fc32ed012d9eeeb435ecc877d7640102b9259d6932c6e9468bc69d50ca1550b1c9135d7c628f6c4b468396bd7b94738039d331dd391b3895a44244ce967a4add5042401b3aecd8218aa23766087ec7bb4af3a14ffbdb8376eca40e95fb77f3971e3bae4e387080dea0d731dbcee095b55e72e3828ce085df5b9f93c9708ec850c551ca1bfcddccc5a9cec7974d66579c6192800b6b2ceb353ab3b76315230e187461d30ce0e0e05d6ef2ccdac58b971e0001f0eb23cb78d85dabedde21a8cef41d5881def8bfab36347b5dc6ec724eeafbbf978b7abb7eb61bc1cf1b2d4b04d9596cf9111ea4199412d6494ade48de3086b5bafd4abd8f23d2b8c4695df2106fd582425ad5c7c8b10ff6c98357dcaca9144e1df0f211955aa49375dc626c3b722bd8d745d88b1de278941ee7e8c38d355f452d2129cd00ac607c0671dbf7ff7bd473e1332b7ad84c53f2deee9b3948e3792f1feb08111d8f72e744cd6c9bcf03e9a769a4c303213a1beafdc66c27a749702aab8575a644aad71d09c671166505addc5c42a4779e61bd8eea4913abb46a0624d58072407c1d27d88fcb28297d0b1dab744fac71e5eeb4df3acdb5ccab15db3054d8295122ca4419473134d2483b7db09c1309d8d8dd412592206fa23b71c42875304a7f59cb44251c53367a2d575a818c13e3a14689ab56a785d2229807294d8e447fceb5f075f34bd78180acd48089dd061fbd84990f0b291665d8ee1d7499035799a1a3a03bd5da4ff23bea40cfaa9b08daa184968da92963251bfdaebea13eed885e127b02ae48e21bc8c830c968b0818d10dc617e785fd454692455b4bc976a820784aea0c9a3f1eebd49a1d2568ee4187c49793dbba165dc5c2554335306fe2a5491507cc6038dc45ea037f103ab1a8d3d80b4f5090d5ccf55e5812475132a90e7c640bd3f26e45ab2666be38e3178133090b80579ec77d7288622e2470b1fecab8809a2b50db7a28cd79544f0f8d1e4f519d98f66b45445a05bfaa8ec6d7ab5df7d26deede736fe218e88d400ba117cf69855ea1be52f5633ecfaad1975c007ad88eba8648d290e781b75c80e5fb82ebea3d29adfb3baff97c844ce928c70d9c05e1a49513f50f18212035ea128c8e8893e54043aed4ab72219c9cbde17f563e009553eef1afbc673ab3c31063717c5358a9d74361c4de0d9286fc804138a8374a21c057d22ad1a94bbbd9b148efb0115ab8432a294e41668c415b984704b68c5aa585a760b74d2aa2829ba6534b14a2a15ee62a19ff4e3205e68bb5bd9bd4852f378d41192b9d206fb99cd2817f41e9954dbd21d48bedeb5b26d422ad180f3fd24b60dc8779c315518c4d0213a4dd6665a05c4e1f755ce1dc747fc5ee2c87ca146b12b104f5c473d7d2f6e55553fafc276846c092bcbb24a2e368a859989f268fa3c8a8c60ae76f6d3a37ca2d9b14982ea70ca121a30596a510143baa35427edb6d60f1dbd518b5a764c8610946d19fbf8afb62c8d669420d79bfd4e8309e790cdf46420f18c14c181bebb5b87bf225892f6c4ffcd88442057230fbf25a6cf48cd7b2256e4b8f9798d078b91149aba8edb290f99936c5743103bb7730d9cdb8ee63e192fa143c68ad9614041a82ff7fac63470264cb35531dec96b07498c3bc16d53e6b4e409c5a56baa4f5061c06d935e18526a45f0ed9e924824edd1b5e6a595549ec3b01418d1909cec184084e6dfdcdf4abd05790fbc099193a3e1a0f199affa652802a26ab4886c8fda9bfa1c11e0d3f3b43b1dc89b9cc94ec963c4fe78b1efe2c86c18b8aea56a8c3a2cf4d5d501d7c774dda59bf19a80e5958011110df98a51088905cb514fa3d97e39db052bf0238a9a23ca50f90ac000a0bd9b01938c7a1a0a6f56d8a5391904d4843f82d3e7c6f24c28261cadcb3346b1817280c30171f32241e01a04eda3830f924d122421ec2192d20968f6de42fa7139fb81c5adcc6572cd9f0f60efdbe2e6ebbddc11b6b8977401a46ac06a48086bd5a317dc50e50c2c82960b60900b2052b44972ce5e558c6c015c8fdfbd3dce04856390899f4a4a303462c18b3d11f253927ce4b096f18a28c79bfed7a966ba320fd684f4ab800dac06f371edcd625ff5ece7aa3f05a0dfd995e7325d9941351297c8a804f466dc8add36b1c1df4b2dd38c11e883c506dda02e75fc54ba3dad5d97da4b3544ffb3323161ecdd51af3d19f3016be372644ed7ee6376d8af751b5c5518fdcc0c2b2a0029e1991e5b87701cd0519287585c20712d1fbcb052506a36c061834ede64e228e2430de9012ea70801eda59d191ded38006381144e98cc47ea51a70a11dda385698a965c1521a7c43aca09d86452c7cb14134146d672f50424da030b2e1694c2e5ab35ba088e591e1df194d2b526ab743112c1c31ed0d1e7f3c622642bf73159959c3a5f27d1125a4a476b4b192ae735ade3ac8d73d111ba95e132eb889c1e9c3bc706b2e389cb310743e55a46b5fd7b216c9a38e0fd4dc4ec05ceecfc92b97ed8b6e416a3413f485cfc6796d8a5befdd7b9a9031fe2b6ad06b73ae5d9801ef533dbf7f9f387881818a11235c611ba4c43bcb4afdbf9f631331768dba9e364a1ed31dfd6099e09006ecc2ff3fdf48cf07c1d2d666d33e146fdb9f799372cb430eba0f174c04d18a40b0b1bd7f3cd9cb8ab322120ec5b708bd1b070407c8360c2aa3aa03e53e69c39de422bb1d6c2168e3676f08789e2904314c7c9bacc62dbea9fb2813bfd192c520e4bae8315b245385a0d6e1340e478a73c2b02f72d16081b2b2d3b4b78ae5850b9bd6da728630173ccb5d56861067c10cd8b3e4bb9e5296546c0bbf64bc48a45169dc58cd94e40b43aeea26754cc19955b51b9ed43f8d583046bf580030208bdefba4b2ac48760b64ae47e3f405a3156722db58dadbde02ad78f73e48dd7783c55bcaa5ae2a4af0b2e8fae333df1a83256adcc2269b4a7909434fd0bea7c1c4f14c81e96a408376b081e484af0c05648cccc98887317940bee0cbdc3bbc8a090083e37170de6038c118829f91ae9018a1f8895e3d36ad4432806d8e526cc3132d59760b13921ab49b73d4d41de89199d2fc8cda8c57ceeeb5fdcfcbb8418939392108c1ede87275fe910056a7f2920e80f71bbf6e86985250bf7684e753944e0a4307a2871867b1e1584882dd2da20df820f579054188bbe66db57b3b69d91f68dc321406ab27c6e0a1b3cb3269d1258399f73673ff45d4338064bea03a307f763147487dbad7b9d5cac3fdc3ba7fa877738613f418f9b633dea744c4d807f5b7f25e09a31e84d687292fd6e5ced23a92f5cbcd8c4e98cad4121a939d3a0a7b6db860659cb0832c3024acd071c7883cf60e36b1461dd0f8ef473fa3fb330c7d2038e8ce0804656c30a9969ea840b98322d74d6f146fe22411c9e61ca079e0498fb8c868713c15e329c1b48f4cca62149177d10038ed444d80d3c10b8dba403fcffffffffffffff31c2f0a325441b914694089229c94c910a55fdca1aff541f151f7fb2b5424a29a594522623cd5cd11020d6d62cd901b80f3b0d3d0dac4fb6e5ba5a07981dac940c9392ef56f25daa8911e443653e193976b4673420d6f07810e833981f6074a0d6be92721d4a95ee9826c695151f3e6c0851c9f19bcf901863ccb480548c6072b09833d6b4aba06a0caec613ead818c2e2f9f490df222446150c0e303858ec9915cbc98be5ea1e82b9817727a9f239e5da69490d17396ce0d8a2e56508286306c606dedbd8d7c27e724ef89aff649ed33cc4089311a311fec448024c0d927b8ab97be8abad26c3d0a0b5cda7e07ab2a5d7e61699417be67e95d47ba76fba11194cc9ecbb9d327dea2d05c5c0ed43eec96aa5760bca356090d8c2d55ecf7dd204d9650d86dfee39f84da92965d3c4c8e16c8aacf102b8e6763ff95ea1bb4ca197f74d31f866c3f578a9f939a25e53ded3c92477264f20234ae75b4ede53a143f5d44829b57a2fd36ca552264d8c20179eea5fcdfe9dae49c83431ba84dceaf69c62cc35976c5f13e30704e554523e3307254baf34316e3efe7e5bf9a7af7a8e5f13a30b1ceeba73dcd263c59cd4c4d8c22c3d6caa4df5a4bccfe281efedf54a3f5f97853c9e0eefdce476c1574f72b2b07c96dab3b4ba1a2ed3c44884b312eb72c7667bcbdd9e481165d5dcfcb4cbcb3bf4a01640f8da9baad7e76ab2d7c4e8fc9f1863ccfca6d3bfa45ccab42f4da51a5fff3a3de6e79e092e915d722d55cfdf594a6b4df5a5d69249cd7dce09aebc7b3f29f7ff39a9abcddf9cce3da5d39d4b8e3d964ce7392f2d1e19e3ca0a86a420887ca9bdd79eb9e78fa5d43431aee69279d0875bb6dfddfcf692f2949ec1d5c41863e885386ee9ded7a77dba1832f99c169721308dd97a7972a85e82f2fb33db2e695b7dbcd69c8fb100ba4eed1c36d953cd879a152cd5bc7ed35b70b57aadb9a10255ffd2abaa5a870f7a0a58976a123a94d0a5b65813a3737e33631412512055a74387f2bd8570361cce63c428e404ac094ae77fdbd0506eb05fbea552b7e77e86624f4dc956932e9f6af086196cc2c91e9b0eaed48d411f65789b6ff6ea53bc1abb152387f392c790f5abddb6f56bdfed4e17221f1c2a3b84c0c4aac1b7efdc75b553321cc2a4567fb186cf5dd3c4f84cb0df4faab66d6a4ec53ee3c5189ef6a7543753fb6a6fe21023b11bb0fddbda995263b82eff051b101197fe3082bcdf9676aed666e3b9aec9803631422049e74bdaccf536f5ed9a18637c96d7b4f087488c5f84c1deeec1e66e2d93ce4d258131e7949d1c6cca6a3147c4a57fe588ff5079221d2623c6129480f55add382597963e093ba0927ea4c51c4aa872f779a6f5a11a6c351564edda9b8d2f5c21f35765e9526de7399923ef9923ef9d11f478c5a694f2a55baaadd99a1875f0f05f95eb27b7d9effd343172f843e4b9a507dbb1b8b9f2f4d43ef90e932646df64bee52514e3ca4b07478c2f1d1ea2b23a52e25fbae073a56d2ea689716565e5b9a87c3ef44345478cfe9b96cf875ad839c6cf87361d5079c99c964c679369f17c3cc64fc893c33b1b231d974c0270a85773bdd9ae977c5d4b13e36a2e9910cb0c01099c3384d0b129df74cf5ae3c970382c637cd911a3d323c618412d1e3882a59e9874afb1a4d8d99cd393096d382d201c2badb85d33b66e176b565d78c19e64eb7e21b335d96a9a95a45c5e98d302daece0b4b06c54626c61d9c4c8e2d209a2f26652cc5a7f7b9c9ef2185f0d0fcb8a081ebfb7bd58334d6caad2c44fac1072fa9e9b56394d8c1fd648114076b8ab992fe51e73dd9a183f215066e58c943352422e2c1d50894f3a53eef7583684b3a126c6cce786639b5482aee93bb76c5b1363a6350675e963da4dfa92f035317e68f331a201b6ffd694ecebb5575d6a628c31c43263e4c082fef8d919fa27c68c3531fe7f260f055bbe67df991cbae4cf1223686642208f26f3317e915c136339b927afb5463c1f223736f53f5ccbd635afd49e2646ef7026e3f118635cc16163888a7c338c30194194572f86ed8a79fa6ca68991486753848b10b066ece9ddba6cef746962ccc4b86eb0783c01e842d364be5029b8144ac63431c6a86cf493dda494dc9e9f7cae89f1f31d102c56357f4ee7ef94a5d4c408f27f50c6ffc86666424f24c69514100b212346168f77f813a306c41a56017d80c5e3c910f9643201e0220372f9643212d8c2031e88400738f0810d48400316c840043090b2c50532608109548002149840062430432e1588c091d7643212b461a10004581eb099ffccccc713620a3860b50a34e0a5cd1cf11809598001443a1b0d8835290bf0800252029c16198d0c716710a08103283580070a10f27808b0c5006c0880880702f001002445e1e2e40226473e02259b4921c1848efc07221457986082149867e18e8909265ac10004741181362d5c002e0001223bb0810c38222eb6f0400738b0010d6400032917a800052620810840e0010e68000316a08004445a3c2025d2220894e802090d0810d9410d64c0114551f48128c2bc02f4b9f1f209753e042dccc2c3097488a2e83b9e2069a4a4113da1141b29d146ca74020c9c1072821929469a208b288a52a247632425c626d8a1095948e3f3e191464a87533a9e9434b8d8a2853f463a2e31a601d2845ade534273e32911f4b9f12126b251833b92090c6002880951282362d0e746184be04411c6074900a208f34544a413e22fc2a10c0783c1f0f8783e9f288acce0c98440cc017d6ebcb8b412d010b1c6484af44f097988803e373e1fe26ca6121aa0851230ef1ec9ffe9b8a4f8c74c442786400217a2774fe7640231e8a8c0868d152f52b838e29b2d42f3c388647cc408cd0f23252525a5a38235528ef826f344fc3345247fdad0031e9824c2c8774caa80041cbc94c08b95f99c8c733c273fbc5001414ae739998c733c5fa204942c84409ee2851729f263b285179ab2f20235a24de9c28b14cd8d27e2ffff0935c98608a838c1901f3a708c8143478a4aca11df04a0a4c5c40c9331809884a1c3849944208ac4e8fcf8908ea4468c2cf2480e278345a2e19d32a2088369e1cf101e1ecf10d07f0f229d4d103234d20588c733c4f3e99411fa785a76fc0b36428e301090ffcb233c9ce3f1f19ce690274814617a788678c7c5d323e409c2399ed39b1f214f100ec7a5c727d429c3bf53c67b826078ec2083c37109d229c33fb461d9841145911930f1f32097cfb7b808e910d9fce7d5f00809c222d18822cc4a4a47db1c5184c1b15ef8f3c1e1dfd9ccf74e19ede1e488224c23c108a295ffc88e474714619cc4f19cde10e9b8f4e8788278d17199518451d94c31382288224cfcd0a6b399bf238a30d0399b33b8d802f4b991518c247ec08328c258c0059b176c40cfb2f1b0a484d8c8773a2c8257c393027a11805a9012a48b147f0ee78da4f0e43c288d28c23c353621e68036443e0c0ecb4d47237f088fd0a65306873772e32e250881f8c308e1e04f8f50e79be581bcdc78cc60f174de593c1d4f8be73346873543fee3011245911930ce9f8583bc7470300b835cc070ce46a3e385d311238a30eee5c1c0b42fa2288ac5cb0e87409d1fa1e770d8cb17d1f4f26060563bf2dec12cf5f118097d27298fa2c80c9814fa704b1461f625b4e1f0c78533461461d484c523e43b9e20abb9e41012448c1f43740429a3041934700049e91049f1745232a0cf8d102c46013b6044146142104551006e44118b0b28e531281fcfe6f3a158b484e1d068683034161a0a8d84c6a2f19ff71145982e1e1aa0cf8d900b4bfb6f3e21941c390c200403443be8800e74e0052b38a1887b4a10819103154451b42608c2091754524ad65831f21d7763bb90af0687c32c208c4651e4c5242211456588410220390045a48407ac20d2e2069a288a8220e1080928c24a9128da18204a238a7c3ca77bd351400a222d6c008128fa7ca7b34911414ae1404ae7399ae20506523acf1962c4a3a30504071a582202b126c4633c0b778874e49b01dab4e0006d5a98d3c2b2017d6e24e5e2348001e8379fd08a8a7c3332a01feee94411093e1fda9cc81978144504f80426eac1ff09817ef3090189a22fa228ca445ac80006511463047d6e705a5836197743bb90afc60b7f3eec81288ad088b4880124223b10e96c3e1feafc1e0a41064c1a305788426488960f818a480f27a5b3f9ce7f9cb3d1c816cf90d8f981430a1820d18d972fc221d2d9d810800004208001e448b9117ad04be8b3f10fe3f816313c415e7103b4e1cf8e2b34e14121f0d7811a1e16010ca0e3f1805a5a3c1f0f81f83929214e7949e9e0c0810315445184880078668dce26251e31c4914d4451c48af062124535a0010cd688a2a8232d8c8022c421d016bc5386e7f32878c9d1f16c401b35363b3aff02cfa7870e1617964d6703c48887f3397eb3c3392e9e3340de71e9e109d2f9229e2163788210e96c3a1b6679323ecfe2c271710f9030429e8e2624060e06b9e4c1c3391d239e4e914e19a11697221e0f07d47149e93017463c9c971de6706f9102fa9f2fdc12da7036cd9d1fff7109b94c508a73a7843e9b4ee9704ae7464a87f99b39386e80fe8559441045181b63c07c60e5b9c448a4b3097d3c8d23c58535473ee5e5e3617996144c1491e185e3ef1ed9f994ff803e375e3a0302710b17a228d2445a7cf01045180b843adcd9b06c3e12b4f1141697cea7f88605c446401e1f2481007d4a1a41d248f93f636fa81ba90bf9ee915dbc84361f6ef187cb58fa8018e4f23da19697cf0a925a6d05ac715e8de770588d4c6c0171389bfefc18a0cce75b62949bd0b368429b0f675a4062e888228c169ccf8787f4703838381cd6e1df21b21102daecf0ccce8f28c2200063000c02a208738028c21800d31b2f3ca5b8c4113c114511e6079fb800179f13952f44514412068f1164e2071ef8c12aa2282a69c01008218a2530010e8688a2e8c409496005061151162488a208838954140106b058c2255c1445254f4803004dd0fca1059388a2a80502182d5ab408416404086788220ee8c361e95137f6c676c161e7f74007f41e27830d1d09a0a183830c9d8e2221f4a0b34614451088a2688b48035b78c009222d3a8288a2480ba0cf0dff10e83b99cf87361916cf67868e7c08e4abc1388c4387288a3c9f7f41ca4ba7bc6fd4d8384778d08208694411a600041800460042425f446e422c1be6e838b2c1117a213e5e8810efb8cc004451c4a0fff9e292834867e31c8f8f1f9f576343e4080f00648e30cbc7d382b2a3b321b2f988f1e1e7e8f87842443a1b9087a323d4f94dcb46fec73fd4f252e288cc1002bd8b0b9e8cceb788a1c3c8076961f9f88822cc09876508e461c984388331c194b0c82321d07b144569bc7c782337f33bdfa0d08340fcf9798459a20843f289220cc6379997cfbbb8e0411d4f0be8c978f9bc8b09fc431f4fcb737ec88e28c244a107817018e18f6b429b337af391a0760175cae0705e039a451131a22892c5e7c3238aa23462018b57b882155114ad228a22554451948a534451648a288a4a114511294411455128a228024514459f88a2c813511475228a224e4451b489288a3411455126a228c28425a228aa4414459488a26812511449228aa24844510489288a1e11459123a2286a4414458c88a26811511429228aa24468818334026efedc77ac5f82d086cae79ddf07860924a730c5fa395dbd5aadad96a0d4c448520a5e49dbb6f6f518645e4d8c3e484851009251f09acb7a29e7c4d4aff21dcf69178f3b8928946cea69dd74fcda96f2ce060c151c24a170eafedb10f2baa9dba0f8442b5e572bb54dab1aff16483cb1d059377ffdef4bbb13aaa4f27b4d7ba772f84f20e1044ff6d5966bf02d4ebb4e20d9c47aea74cd36374d7ead9a98ab92ba337d6bb29d539264e253534db197beffea1a09269ad3746ec9a632a5105ee2adc6de5367abccf15b9a33482ce1fc13544d3a645d70b94a4c5f95b01faa09bf156a280193ea5f4db9a50b5f5d13e34ae82731596be61e7c505f4bce3531c6184f402289f6f261d29fd33531ea2089c4b2b6e07a6ae9737e5d99ce6b4219202490589241989e2ed6a47e530bc923f6db6c2e156c56b6ce9ac692c7c8902fc29c1c2a322071847cdf6ffff9fb544d2a22248d60497a3f4f73364c5be43f0c95cd6748182a2a2720618464e8d6b3d4ba5f53872b61a890806411ce5ba64eaab7a77afa2fd2e9902882674bcfeea04be76b08074812b1eb98a5b9af528e1d2a3976f84b6e11f2f260a80c214104b7a4166c0dc2f99ab56796e4103c934bec7cb29373e1d3a480c4100fc2d4fddc6bcafd5ebe48c6ff478c2b24859012aedea4785d7aaf5713998410ee9653c7acde6a6bbb577ca8bc3c181a10fbc03c8164100a366fce4cb1e4d6254722886d8fddfb5beae05c2b69620cb9b06476401208f80fead3fde6da54d69a0c8841386e400208365d4ad9206cd8da946e06247fe8956dfdcdf65ec1574c08891f74d57bd225a7af6d9b521323cbcb80a40fabd53fd8ed3c2dc93c8d80840f53659373b2a40b57f3d5ece15d6bc6a67aaed664ed80d81862a30718367ca8e4d80144850c123db09d70ea2fc66f3d768d01491e946bfa93c1870adde7e3f087c70b48f0e0a4b756f8bd9a9f39be83affa5de57a32a73c5d1348ecd0e46ca8baa527999ceeeba0187bf0594fde6fe94e13e3cb27d4e111eab06433c820a1c3ec56d0f9ec65af10c22f48e6e01e7b4d25985a73d05d9283e4f9a473cca52bebc548e2f0bb5cbb97987ada6c190e3d77b97bb7f7dc0caaa6b3217943cf778f1793ef0e195c6e50f7aaa1ff53ccba5cdb007bc9670bddddb7dbec61a30c1b3f545a184888393d62ec7c28c49ca9f22161c32ab9b83d572cf52e630d1a2f0292352cf85e9d4fd8acde3fa118fd55583c9f1e2f9f21d143a2864d03091ae0d2d5cd293bf766bb75869d6fd5eab9da92ddee9a1849ccc072a9b5d85a4f5df34d246558eef7584339d372eb960c29ad4dceaaff41e88bc720135bde6bdb7a6e9f849a2511834ae62b216b6fceb92d6962840149181abe526a2e9b2939b81c0918b4274c8b25ab6dd79e6a62d441f2050697b25debabe0927079619d84ab5677f3d589bb074917b43df7e6adae909b4a122e24b6ef3bf1ae36db7b690b53a9fbfa6eaeaf49b12f86a8bc0b87d520d102eff62ec6eaced9b50c755e8d5f610149165eab5287ea3e35960eb1a08db9874f49e8cb25c5245778beaf7c59314faaadb3022f57fba64e7d6e77b526c60ca7b953468c1c0eeb60024915d8d2e4ab49f8d85a9fed1d974742852617dba9703595a4eed4c44832857e4f9b5cfc2d2df95e3531663a653cc6062452e8e51a32bf73bfa22346154c1348a2c0ae5e37ed7db9ea0d6a627c8e6f321d0fc77f74c4881154c4e3d1a48038850b1bca630b154902855f7335d5d6bbf9d292eb096f77d97fad752ced2b151f244e687eec2d37596312aaf5b409fe7c9983ca1f4ed654a93241613776d630b1e6d6722f4896d0dd7ed373fa4d719a50098c75927071823ba7f4e6628b1e2449d0d5d26d6a2a95154324b8e4d48d96ae35ed5e90e911dc99d5dbb573934a698de0503ae726177a6bc9a916c1a95b373ab1563967b3a7d2adac20052c202142532b79a996d27bdcd21cc24b50c9265d32a8ce0c85909a5b377afacae78eb9996482b3e3d9fc8831a5802408d03f2dc74e3205753910d872ebc6aad4ef6dba7d92e1531689c60fe062385d652f9b904de683b51ad46e6e397debb90a8b4423c69d243d788dd3ece50bbe6ea84be5f3fe1123c6334878a0ccd7fc552fd70e9c7be9c60a21d181ec9654aa255f39756ce6003226bff12eb94d3ea84870c0f82d73a9cd65723566243748b3f75df2c95e17b644620387db0c55fe844db59c243550d9f259bda99fcf956b0d4868b0d8aa1b102ec3a5899d7bf13e0d1a9859bcf6dc7b6ecedf5bef4a13e34a46816064017f366bd59cb3d92f5bd5079858a86c269dce7e6eb9f283059c70ddc3e4782df93ebd42beba6d726e820c4ad6342b2d9ecf8e185b3c1ff781718583fc8a2df86e539a2cb562aaa4d6b1e3245d3ab533c0b042b66e6fb6d9da29c8bc470e30ab78add8ec36d936f892b9a454d1bea162e6977aa5648cc1012615abdae467de8fa9d7d8d3c4b8528218836050f1de9a9a9e6a6aea29753b1e738a7706df33a85c7b70faaf668a7749c17f4bcea55c5a0eb1dc705a5cfc3dae0930a5604eec3f4128576aaf15299acb4d4aea92aa0ae76a62ec3c27235f8d8ce3c08c42bfe5749ae052cbe95b93e1705c7e7cc498fc619861030c1b211716961ff3c9980446144cf53198da3fe7143bc6c516a1686bae6e4a155bcfe04a6d41c1546763cb67f3ae55fc5c84603ee1a673d58a5ffd5c0927c7e3e932309ef06d6ff8ebbabbadfbe785bcf0191e2343621401a613bdb4fb417fc79292e9710226973c715adadc796a9b78523ae9af35cf953a77e5c7e78b7c982583e9014613ebda9363a952d2f53e9e964c0bb3ecc064a23599feb9eaf64afae73f64c448020c261e365c6b7152a5dacad6ac7cc7c3d111e3773c1c89c3460fcc25263f099553eff1eaf96ea5c70a0e9516602cb15eef6abb9cc276df26104c2564e2d59abbb4767bed91efc4b812e3182aa10da745c827c4020c25d6ae76525f92ceb5b927d1a46c73ce55d7ef498792785e5df532bd67b9988b44af365d3dc98da94ad5209160aba41ef3c7fa945c8f9070addb67ad92c1d6c9118a49c77465729adcf220292218928233308d6877df744e3667af299711ae4d1b32d656ce26153bf23e3e6fc41386c22c623f2565e98f2db6560301a388d7ef0caa36f76cb29289f8b750fd924e2af3d632473c46423d308898cb97f267ac4a3d7b4b13638c190ee78dc4b86a940073085fd6da995abcafe1274378b7c2f7f857399842b0a770d35be8925b8d6d0f308450ad20f4b66d59277f3b043388e494cdf5566a5ead1a042388f9a0ae55e993e4d5328d81094452aaadc365f3d7b16517ce6c1840a4a5f6bbbf9bd2b6d2d22ccc1f9aa5d7aabc4d06d7748cd34cc413c61321e209a3c5132446221e8ecac3f8c1c9349d5aefef17bbd9402a8ce9034c33a99c50353ff5dc0361f8a070e57a5ee709d56ad6983df084dfbf53579b53613b82d1837b0fb663eb395feeb679f84fac14eb62ea57555f3078586bdff26b756c7973abc1dc61eeb37fe57dcc566a0c038c1dda659a8cc9b69e4c4d360e3075505dbdd09f5cec4bc13f82a103e4b4fe947bd5a54931508c433073988e597bbfc9d66373ad1f122307c66037a5d69b9fbe4e0c0d307150ebe05a4caa35b9f9aac6317048fee0735d8da5d7da6cabb9bce17193eae782ac6afd6bcfecfc8801c60d0abe061fb3f35d925f9301a60dce256475c6967f39c79a18635cc1d11836305fc89f9abfdab76f0d0dd99c6c7d5a4d552f03bdf01820178c1a563b359743ff6d658fcd505901260d69a55a757638536b4dd190e4620ace34db35745ddff232e425e48239034fc7ad4da9da33fc5566780ba75b0adbc2d6b4189031d6aa11e3b3f067627a8029c3bb05a5e46e53beb4a1cc0a86a440c507860c8ba72e4cd0ede495528de15fb6c6186cb8ba1faa1876b9b7a6b6ecf952590a4373303d86adfff5aa7a302c0593d356fde4932c4d4d8c3188c17f5c401e8e5c638d18df5d38ff69f9826aec1aaaee9d3df79917d8e1afe76dedb1bba69a1855305d589b18b25ce5f39d6a6b860d3054629401860b93d7af67493687ddbf31c06ca1d59f3a27d5594e55416cf8501902460b4efe27c6bee7eefcc71ce21d58c06441616307ffa17afdd2312460b000df54ecaa57b9941e5ea1f164961aefbe5cc97a458509182bf4337399da648ebde75a157c5535a5aced6253bed7c4d861ce188ba182f29baad327e6d3212f9522982974626ecdd27f4367ca7560a4007b395cb33df89f74362460a2d02afd642fd9540e997250706e55ef544f49f8e44bff7103cc13d253eddabb833331a70aa3038c13a4ebc6143eb9cc7a629a00155b9e16fe339652934a0b304c689341f92ca1bea95cd9fc1d32c697900ecc12761bff73ca9ae3d77f221825a44ddd64ef4aaad5d24d8252134a574cbe7333b937468c9a10b7c4f8124a182424a74fdd83caa5a9ecb50c260f9823b42a26f51f4b4ffa4eae34967c805c30464872b1aa5b275daaf9ba084cb683afcaaadeba6c0886086ce9b3dff6bed6ed94426c9861030c951606d2c22c0f3304a6fb9c74af4c39e9fc3b3186382424468911826a4ddebc98933ff5d6c4f8031384768d575a734dc6d653100304d59a64c92974f556aed5c4e899185778645e421b34544a80f981fb7fa74af9ea26e7835f3cd753d55c33bfd21589e9c1940a197b93955d3b734d8c1fe3bb27488c31b6430c0f9eeae41fa3706d6e36de24fb41d95014abca57a765b8547aed11e96c5e769884a2997aff5663a7f22907353730018554fac9b909173b255d73d8c0a1126367c31af989a594748b994dc836ada562e28996939932e58b5bab529a18bdf342dca413ad2f3166cd4d31b99e3e1e8decf07f4860c2095fb87c996bdced716f42dd4b703275de1a4ef934e1cd89a5ece61c5c5e9f89f565bcfc6ea5836c35cd0e134cf074ed958b69f76f9a2697604d1b3bc97c26dfb4890c134b3c377536c554cbfef4d6874925166cf046ebbe56b79cdbc5a94d28e1d4ff4afd6f5b7bee5de885984c82d784cd96a5934a39bdc63f62ec309104b7066792adcd0d134c414c22d18a7b7b7d1bf2fbdc1d269070db9274c57cc1f61a04f2c2e4116f3194ae5bfb3793825313e3e2c0c411aeee3d25fb3de9d41f6a6274c1c7b8e2021e63c4d8c222cd306984db6d50a6d69fca4d4d8c78acb1b5d3bd97adcdd44c16d16ebe967edfae4ede07c4441153c9de4ed0fd2ec90f13f1e07b72364e69dd7cdd10f1de3b7775cd3915746ec8172687504b39dff932a94a6d394d8c1b151689c60f134398148235e7aad84d351532bf092156314eaff4f53567980cc27127b99a2575424c04c1dcf4799a6afe6afe4e029340385bfe891b4b8672bb57fbc204103fe19ccae7cfd4d6faf4875d2a29d6ce3d6650be663e9e8c11fe64424fc4c40f909d39a926956acb96a7a50c933ee84b093aa74e13ea6bcb62c207d5c6cd15af74a8dab63db8c4bcca2fa792a97dd3037bfd5e50b69f6c425e267950edd58dd95be7fc70e2e139b6aa29d91672e375877f3029d9eede3f9bbe1d649372fd5cce6cb99eb20eda782a832f973e7edb74e0b49662dbce3b95399c03b74badf9a6c75a72e7e4e0f6b525e59cf0675a6dc5a1b1fa4ebc9cf436a12738bc77f219db769792e27d83636be206985882ccf5b76b43394ddaf0ba1bd46736e1d2c9920d0bc2e9f337bd9a6c2a9335b07bd7d45a4f767bad654cd4f0cdb972aaedebb676c234b0326f9d1273c552da4783fe73f6e7269b26462e6cf88f2d9c6072863567eac76a49d58a1d3483eaa514ff928be77cee32aca7ec7659754f4f4f01c1840c9297522d59b9b97a6ebec9189aa92735534bdb94746b2286a7cf165407dfdc9e92352ea08c4918ba17fc05a1db872bbdff610286b9af3d3fabedc4edde13ca68402e2d61987c81a9e6b3b596be4db51233f1027c0d654a4db125e55ced9dcdcb0eabe11ce38a8e229243423e1913987441dd635e6c1533f692ad26c6344cb8a0eb2de7b72e4939d96f614275e7af935b139caf89310c132df85ba9f17c2fb9a6724d8d49161cd3d91e6b6d39ec87c9040b0e3e4c69f12bd654a7da5859b1b1f25cb6b0b1f25c3e3b62617205d798fe4ac92a31365f32b102930ebeb576e7bf572a995461d7ced5a654f66a99950a4dfe54af7ed52b7f4bfd309902abe9529b49269602943f1b74fd268490d9d2ac7c3c217e814914be616396e073dfeff44ca0a05c21cb965e3f053bb9ce863f9e3179829470219b922dd7b049e884e9e0c3c9d4724c597ba789d103ea7874bc983461d7cbf652e2c7ad94a9091364b3267fd0b172d5ee352b65982c81b99964d81cf232574f235760a204f6784de59c7beac136c32409fd7eae756d7e2dd73f132430b7b7986afd3d42fab71eee374b4f95731323f854add7bda7b46fe5dae0a1c2625284a95ef3768bf183d075a97866670d1322ec3ff69e6349cdb6e06f58362b406ce8b0314405e41d174f0eff239b1e317e3ec3228f083119823b08595255acbd72652204b70b2e57869339f4a53b3009427af97cb7f5f49eef12174a860d35c1169c16975006062640989fd6ddb2af9b4971ff80a5261393ab1c848ba99af8805b2ac6d493bad0a9f297d032e981a6c52674966c77b55e334c78f09ecfb92e5b6bff9093c90e7cd9bae6e072b7ec206b31d1c1eb345bd373b5ddd4539d4db3786432c9c162ac2d7bf796716bb834311ed9cc678283fea52677f7be5792491e263780c9bdb135f549971a261b784b2efb41f8d67dbf167201f29bcf9021263570e98f57ba43d84dbea789f1e3c97c67c3e96c625c11c38406f3a9c993dba92f566f352d99593cdd6750e16b6a7972b04416ee75eb4feb57697694c462a54cbd74bde96a29960ffd8871e5853f1f1c38545478c4a822df0c0e875984c4a862a404169ca0b37313b677e75cf97b425ff28ae9fef01f530ab99d95182aae909f145caae9957db71fa2127a211a10fb8025ad988df93d742d9d36f6c98ad6f6937752aeac494e4d8c2da0ce8647c92a5c726e8c9de2f7d6d34896b144154d993b259b4ad5c418e2cc4a492a1a536eeeeebfba316e54b4d9dfd8d437e15b394a4ec1dcbf32a65cd75b9d821253a85a10a67756ebe92a2b8582da2bbdb3247fd98a8d2e9490825fab5e6e7da576ea5b8c2095314a46c14efde47dcb5cee6a86841251f03fd8535572eb99c7b8f232379f37a38c9250b4b6cbc5cbbdfbf9ea12507c6be2f4df7c7b954a9f80eea91bcb925b4a327dffb56b4b3cc1d3f56b8ee563b02d4f4d8c669474422dd5e07acd164c0e27e444afb45a29b7f64b19849a18f9133af29a924d2875e8d69c4aaac79c743f5442ff852cd104dc75ec8e5bb71194644232e9bf78b9b4e02e6b2bc1c46237d35b5dddd479b3924b2ca7fb7829fda4ad2b35311e794d89259e75f9a5c6e6379d2e6962f4945442e57cdb64f3fc65a75c13a3094a28211bb35cfbdcaaf53b46c924563e742e29b69eb37f2689d76d6aab92f03597e4cb9248f8ecd5e0f4b752d2fd2652028987c96743675f3fdf27969247f4c20455a7279b9b4b4123943802aed65899e3091b2159a92ff5ed69fe72c5085e76eff5d36f2d2d7d8b90f3b9a4bc98ffd4e5b3855b18880a4a14f1cf76f73df8e67370be4a114a129194724e39c1d5dae2264b10d190f7a76a4b1def4f2f39446ad031974d17f34e89211a634eca94be367d991d79efd87095269414a299f3c3a458257fd7241a258448cf2777bfe652fa2b1ec44aeb35f9d0ff137c3071b1450a7c940862ea532e5f595f5b53d7c32909c4af6c0bd5946cf95dcabc12403853ba1e4c6eb2abfa1f20dcc92ee16a2b534bca0fcc98f2e79a42f90e26ed8392cebba77bcb565a2ef928e183ef2f9ebd5c7aef9cae3d38b79ce13f9838c957b2440f4ea5c2e9d682ba1264b3240faa95bec94a4228e15bfd82123cc0e5a95c7339e1b7552bdf0c2094dce1217bbcd27c0b726ad57678a76daea57cb66e7a85d141491dde33de6e6fae63b89e3b9b32543ca1ce0f2f4ae8c0164af65455ae620bb9640eefaa70b2b24fb5ea4939404e4b533f65b5e46fc7613da8aadfd69afa0fb294c0e1c1345b6349e76bfc98b694bc61a9edc79673cf746a732eb6b0a182c941891bd46aa96aa5c53ab9d4c6c3c60f9550c74523a448491b363d2ba666d2a7dde6beb42e256c60f07bdfc24fdd2e75aea139b5a4fcb174b545891ad284ce92fefb2aabe21694a4a1e5bfb4ca9282b2798f1234bc5f8db9aa656d2c695341c9197ca97790b1cfd70fba45a3c40c497d2639a1eaf2f65a65505d1036847397901232b0e64bbd345d3526dd1719256358770d5d4b73b919f70f2911c3cfc46d972fa598adda0c88332fb905f4f24542615095493e96124f85c93fe402448c1230a4b9aafeb2797aab774d8c2b2aef9e202a28f982522eb95b2fad722b5bd2c80e7fda0bcd3629d636795b6d9f349a922ef0924d7d3f5baa0c2a6b5a3c9f122eb8c470e9da86cd0edd6a625c79b9e108d902439f4bba3419644eab34314e7e4e8c2b3e546489165462bccba55227d5934e13232804628e0a4ab2b0d0d95b394f06a1dab0b08481a3040bfb9ad49b09b227e76e5b3a66945c0126d5b7d8316def255f9a18399ce11857e49b11248d122b40c79c2fe738ed324becdd13e44baae024a7a6dabaf51a5366ef9e202554d8fef496bffae4d2db19a30b7e878e9229a8f3d5349bf53d3515865c80c0a0440a2bb5fffe93ccbdda4a2551784c9bb5dca5dcfad78682329e132e3ba66f9f73c9139ef972cec15d8aade598c68c1227f47450727250265fbbec3f4a9af098be564db25c4fee7109133cb1bbf5f978b532e896a0040c942441c8054a90d02bf1bafcb4bcd34c4913e3f4643420f6b14a8eb069c1718618182831429249b526e7bef6ddbc4a8af0ada59974d5a9ce5f2582775a69b165ced44d6599d01309fd17b364089f1cb2f595fc0e5b2995e7341791afd131851221402597a69da9b5264621254100c20ffcdfb5345b3fe89cdca989b1c4079325c758da09d33ee659d203e54dbdd4de521743090fa074b339b9ff3ec9a98b4b76e0e4ab941c7bf5e4538d3d253a00a594e44032fffeb6ce9b9b4969090ef49f5b4e299996bf53a68931c6151394dce0b1326c4d5bf93e98541363118f47c3f18d0f4a6ca0ffa69b4a5d2d55ceb025c69597500b0b8f8e1836bcb301e3e5f32e26b061a30525359840090d949c3bdd52b29f6baf0f44320b4e4ba9e98cb5753c9b8d6f38ec051259f054faa6b636df9bb5a689f10624b1504bbdef730725536aa515fe848ef0b06183c3611d3654ca88110305125890bc42cd999aee4e663f7f2a3214892b5ab198dfedcf275f5d3ad3c448c28a0690ac82dd4b9d2dfbc9b7eb5cb322860e1b200e07870d336c848e7ca8e3d161e3d5d8d4804415fb3c257d9f2c57737a9a184940928aa6a9adf7aa72674939092a1e6a5cb8eda269179b34a56c91a23714bbb11538c1bc74170588422017324c304f845d887498130012d0e7a58b7b3e2d020012b5b0c836416945e841ac78353c2c2ccf02009455bcb448966741415185ca640280920ab509713420ce640480820ab5097d34204e2623009453706325196c8eddfb2917088a29b8d93e6bedb9df1f27cd17e1213182c24029c553b8eff519fc9518dc8c18a498e9579ab0f725c76fe9286693df58a9263fadc44a14c949b59a42a714fe32170a35d76c133a3fd89e540b144dce85df3a1fc3c92b3ff1e939b78ae76b2c392f4fc097da26e85eeddcb5be130b3ed45e754cfe5b2c714297d9faa9e0eab657de84da6f0535419ebd1a639a504f296eacf12f6eddcf842feba5ba789dfa3f63c2dbaaa7ad15f357cca54bb0d7a48249e5a6a98aad06c512709737e97213833a57aa84d3e4524a505b62c8f629b1a95f6ba558fb24526be75c72c7eff5845212cc0d7a7a8a41e6a0ff8b44eb6fd627593d4848a94eaee92e5d6beaf688c476f9674b72bdbdb91cc1fdaea64f995423e44af786f355ad5ad766a030c27bf683ad6df37d2dd317288bf8e5a6d6b9dd54a689c14051844a10ae94ad74cec99e27e23905e17b72a9ca04792162bef4e483cb9f7298563a84520b4ea789e99b3e1f33446bd2a9f54f4a57fd7c1642766a8d7f394e50f162427837db96ef9f1a264e8370cc31e8d237e8d6b247114493fc6e55fbbf9b2e3d9440e892ad4c3e5669654f06847c73e9aed4857f68eab9e96be17cfd6f991f1c3ab9647aaf5c2e65fb03a50f6abdc9d6cf6e727943c987b54ff152cc29aff9aff7c0ee56b973effeb83947d10343876c4a9514ca5e9662f402250ff2ed92bf4daea7507d62ce0850f0b0702ec59cbd394d4e7d51ee90927cf773aec54f7a6b767829599b5c704162a0d441cac94ae1936b39a81443a1036cc75aa5c46687e64603caa0ccc1b1a7cad6d7af279b7b463c3a629c287248aa966b49a65733b173287170d2df67af520837a54581c3d4e5e44aa6ebd4432551de00953fd7d2f324e15ad30d492e63cd35b65c2696300c9436246f69e12a08d54d095307850dce4165cfdb5bfbcbffade1c1d9b8b56763271fa3a84136e65232a6509f42222869980dba7a4acd94bea74b1434bc2475cad40bbd49b75c07e50c9d7e31d896ca5532f1ca7ccbcb901c2a3388467ef8411837a098012ad8a63f5d69aefa2ec35cf86eb27bc8cf1923837c70b6a70e26568c558fc131b83c5be2c5dce2b46268d35db59accabd62b158605593967b375ab2519fc0fb7a4810206a69eb36ccc5baf06dbd37c5280f2056fadeee562eb586305353172f843a04c118e4ba300c50b89d5f23a5567069d883f91cfb310e96c72384a17dc196cf916be427d6c6a6254a348c60c3080907801850bbacbab4d28a5dc750735285b600a273ba86baa5ad9a644d102e34f4bfa7be6285980fb2b255fbc982646100b7fb845080e142cfcfc9f8a3d98f413db47b9024ccdeb93520b97aa5b8d15bcdde56cd95e3535330d4a15dab57e6a8db7d9eaf6c440a182842e673b66e94c4da9384250a6a09493b2dd757e4bdd93c2e46e6faa666f2954f281a2d0f6dbf14bb6ca5e4287423bd84ef539b8ea2d7d423763cb579bba533de63a509c00d964d8ef9c7b0ba1a62f509ab03df535d98bb1f5664b4c902fa5d6544a69ade90d97b0debfd6b37bfc74ed54422b6ec834ed27f5e75a12d4540a2183abda3fa6bc0305099fbabd2a6b9adedfef113cf95cea395790fb41453102c39dce4ef957a63715175ba8284a11b8296b3b39a957d8e62302744bad7bac5ff2959e030da19f9a2d35d7c4522f9642e84eafc1c4e4eaf9b4a104c1f542f7bdbf332d5557060a10a4f7375b0bf71d9b50a1fcc07975b1c7ce57e14b89e203d91c4c337d62a8ce54f580bd74fdeca1ff83ebc113a0f0c0357409ae6bbff5246b64a0ec80db6b29ad9a9a5a2e569e50e7870e1c9b32a56df61c48d8dacd6ed656a5d75093d9cc39e4478f1e2c2036f2d231668ca0e0a0dd767ae72a2937339703e506eef9b34dce2d9e537da2d860eebfc9bc925bb39553941accf9a052c93b95746e8b42837d955231e5607a37ddd4c49889310c1c39c408e38b18fdbfc52341394e66c112cba65e49a9dff3b12cda3de59ce325e17ad0cd9358b8c397562d29d77b5a6dc1092c2483ef25297dad5ecee9e4157025d9694a7dde0fd39eb822a5d96fb9f996f9b17e1d25386945f2a64ab22a73ccdb72561ce064152f555a2ddd7450aea4709ea8626eeb245bb6675eec899ca4422eee991a2f05f777424d8c6a8071820ad814cad678f1a7967e59361a901a6bace1e3e414cab9c7bcdf4de55a621f7201e24e4ce1925a4dfd7a0917f27a9a183b3df4a414fee43276cd16fbc3e9a458707d3a7fbbfb2bf3289c4ff9d4bf753d5f4aed4414eb9aea3eb76eea52705d4e42e11032cbc44b290c4e40f1942db9de6a33c67ee7c927dabb37b854b5f6c838f1445bdf1e3e95763536a79e74e231fbe9bcee9413bdfbcadb4b5787eabb09f7563ed99e41d6d26c4b13cee04b6eaac63a1389359e9a94af5bdded30d1ea9957295e8ba5e7e4259e777ae933b15dce0b5aa213f3c66b429695804fb59da17c9468efdcf926d9dfbca99e845a6b4f242177baf5dc635dcbdbbd934848c64e356937b92d17ee4262bde5cdd96cc6e6548bd9e0e4116bae75f716640a75d7114eae8509b6b657df0c9e71d2886fed9b5bc95b72b3b13c678b13464ca9ea49b67465f393ade99345b8afb9543db9103ae7d689229e9c2eb9c2c7ecb9f53b4904d4e678b6aefae670f90411ef957bee7d5f63a6520fa15eb1bba9feb5d5e532f4e1315edc93e3e5c410abade19c3d7f2e36590bb1ee75976a7777c974ce1342a4a5a0beb95a95ba5f3608b9de0c3ea8cb25a594b99c08629552d5c5cc5b3fc81c88b756b7d5d649faa6b69a1340a86fbd7057ee7a6f7193c1c91ffaad9dea9827f50fb5854efcc03cadb2d458a50999ad3e34995e2d766e4e79c287f6bebd606b77c3c46ac7c91e78c96549216b0ff67ffb0b39d1c37bf5a4b24dfbed9ce5b69ce481bd87739553a88badab1e277880afb1c4d83f97eddc52179cdca1956c2edf41d689a9f52776504fe792eee1943b1deb933a249f2c556afde69b4eb917277458ce264b4e6aba25994e4d8c7c3287754cbab92cb55f90b5656324732287e549a5723dd5f4d68919f9ce9fc461aa4308613b93caea89052770f096a6cf9fcd2da5dcb99337347ddc92335606e57c5005276e58d3e173aad3a7cb491b94b6f94c5b826c504dbf3e13379f0dced7b09335b8b69458793ffca9aa6ad086cdada5eedf355c5013631ade7c53994c6eae5abb7c8c1334bc6350aae720a757dbf8431d26123a398363afbadeab36b19e0bc889199edc76ec6a5542674a65ca905aa9b5bd60d3397da91f2a9d0f754ec8f0968253595328156c5f276358b834296f4fcd666f77c18918a442a7be95b34efd260c83ab739f89df6b6ccd76203038a5cee1fc9d2b77d51ff40526174fb57ee7744ab2f49c782149e6e042e66f49a76a764ebae06b4ae7a6eae7d6cc05b7b43d3974d8e4b74b5b78d3e5fe466324e3099d68e11f4ac91a5370a74cfe4eb2d04ee1a6f50ee7f2e5ef18e116d00916a073b5de4cfdd4c91594b2769f5c4befe17cb682dc6e76d93cc125995a55e0097b657a4e1364c593e5840a3f5fb55ade8abd7f29a7901a5b6b3ed72ed5ead771f14821b9f66d31d6e65b69b56962cc60a2701205f786eb39d77ebaf3ba26c6222750f0c7eb3d872aada758ce969327787353d01f9bad3565eec4096a95576eafc4f45de326ac7ccade42c80cf6f2036d4e98305933f898f2dd061d36d0129c7d616b70d564fc2468c3f9d0466324137a224ad877b572bf395e8d2de6719204864ed7b69418ec954a9e20614df76b41d5e58e00dd635533412657a5572246902e5b7aeedd63d94d6a11963377289fdbe452bf3b21822bedb94aeabef4653e19425bb22997ad252ec18910bae77aad5bdb77f8e623721284e91a4a6deca65b9db3e500424b8d39bb07593373d3e3f9fcc90fd65466a88d4185ea2df3ce084e7cc09cbba4d4b1e41e6cc73dd84daba0aaeaf5e69a4e78009535b8a4aa36abcb79b283b9e633c9491f9b3b25b7e044078c5d8213aa564cbff1cb41abb78350b59554d7aec1090e52ef93afa5b3d7569a930627375049a5e75cbbb5ebe5fdc5890d54cddf9d4ab2e7b58cf138a9c1926ae9b67aee9eb576384e68f0cff96a572b93ba63ad89714563328bdfa593f9daf950d79b9a18631c81892ca49cf05d9f724aa6f63a16bce6f25b6f9dbe3575bd9bc0425b63cacf4aa9f4de539997fe8f2724a4e3d980402da020367ca8848ef08081c92b5ed792be9a63cdc1d7d626ae704ecfd4639dacc9c4b82224c81a26ad70a6ad94838b1dbffffa3061c54fb66f557b9edeafff55c8ff5d6dcd97bb4ac90dc34415cce684ae50e5b76489a542bdc650eaaec2d6da739a18a1092a3e39e6d2e3f7948685bf6324c3e414afedabe9ce3da589f1e51302bdece050f97c8c1925989862db2ba6e93506bbfdad492918538fb5c61af3df66d6c4e8384c48b18b4da6e4aef489936a4d8c992fc2a11841018fa2a844108623792410898281200642103a346604004311002030282412880412c18856ed1c1400025560546c4228281847439238200c06e3288882188661188622290ca2304529a70572ce17aa34df4519b01c7adfb7a2546be4a3f3edbc7e5df3f2a5cb5c3f82625af1a308da8ef45ac6e2ca9281651e422b142b0af118ffe4fe7dce3eff7758cd82664f96d21aedc3b7bd1a40c7d563f4b8c66ad0d709b6cdf920c3418c3db5712464af9c3a446db762b4ef9ae2649f5043bbe3b2e48ea356d0e93b19e20cf1b314592739d84d68bdf9a4e07da45aa4ddb8bba8843dfa6cc350aeccfdf444421fb812ee2287a847cc2c42237aab22c3ae98a332466e88236025aeceab87ed7d51205fa935693d03f85380d9b4b3ce38af92271298ab3ad2638e6a0c52f39f1fe77ae7772f384f4a694e838baa0fa71923de196ff15e5f4ed185847fe982544d99efc9b0548b3d5111ca71a546fdd0c1af0500053dcfd5a6c88e28e375ea961f44c2478065c30757cb50853b41d5453af7f38f14b8ac24a937da2f0569a62d3eb0af5ce8acdbce017b1774040ebf3bb66ca0c6a423ab648acae421b2872ffe1f3d6158d2b544076fe58e6242aa1e02ddf49b7a637a77601261eaa95373c43fc2e39ddc2eacd3f66446bc35bf0c6ce50df4932c7bdc0dd5b0754f66894c89755d4399793072c70c4042f44591636155e4922ded81a5698584ee496a54c37ab280bdb3cd8640642f6045dad5102cfb4ae6ddb9390cec444c2ed2e81c011f9d2d556c9eddc81b66d2bb3973aafc55dd79b9c56557c6f6792ef0a74482922dded87ff2905c5b4ed6cd3103894808e49f34e2c4d1f9d6730792534f0ce7d7fadc58982005eb8f80c01635c1986b983f03ad3b43c407d83e2a9d837f4d486f3b5f5c49ec17e5096715031b4ce374ae1d607df1a17d931e0b27965d2502d5d7a401db378d8705c8a813e20bdd078dce4cf4ff12e782a15686e7703399257342331d1d3c98c89576418dd0b88e0a5af0af9336dfab0caf7691d9f6fdba97833008d831baf3332694a9c6703876acd8638897d37563ac2f3e5c49f65b1354e29bc65fd31e199fc88c196d59a8c79545767810579a22f2e1e002bfe2c45eac9472fb1533ce79dbad6d39f44bcef35969e80742d34ecd995e0a7e34c00c87679431633b4363bf33d1207089754b495f55a1c0df1d574184a1d3ee9a99e425c4b9be4892bd334c11b668e0c1889db4108e3f195a1427a26f0f1e0f9411a7ab343ee214c0d21bdc001a6ae6a8cb4afb3d686c7575804c1c94990b7a6f09f74075b765d4ad92b50f7c4a9521f3c0bd986247c1649379dae0f16822d8bda98c42418fe714ff3a2cfafd2fe734b76f8922ec603493a8e08860963359e4644486cbec2c4d743292d60b14f97b652aa4a6ac6b69d771cc4a7b1c9c55f4e3cdd45b79bfc7dee5a00a34e7e5b0d92e7410dc8127586c0f8ad7f9dd53fbded2cbc434aa1c91cd50dee6661bb8782c97138ad6faf1fab9c73e63def36c0d7844095eddce8670aadd46bf45eab750b0e299c22f241b0f7f5c7f9bf90a3de24d4c3ff6ae6b732811b752c5b5d16356aa35edb4c404be84428b63d5644db51f50965ab573ab97b79c9a052017857df658d5af82df9757f0d14c5089af0930febb69dc9eb2eca574dff70f6a46bfee746451a2b6890210c2365f969cceb884b265cb130f4b5480d56790326d5aaad228c38db99221f1e359288b7809c4621d315a482e3ffdd7e284c0328a6d965970972825887ce6d3d0984323753b3a72fa8f8e248cd016135002c0abdfbc153e1bad27f9025149573b14fd6f75e9886e08c58679a1bb45a5da05f4ddb9be7a512f04a7307be432c6fdd2d9cf17164cc392a80db8fcdfd4f551892a0d5e97e0d429d8e020de8fa2b52df40f480a6ed52381d9424b5e91c645092735f4611e42340555b2b7c227b5b35da1206b881e2f5ab0ca69aa7cac213713de4a9c46a1a7e3e060ae613a7b9940faba873dfb07d438609e918c17204d673224fc2f5a9f8671dc1da5b96d58ef4af83e54b0b0e9394c9b2d645ed9fc6032cb92116686052ccab9cf8b694b6414063948b8cc30cfdb03ed62137792afb881640e9aa3d4422224973ce345216307c9bd51a2f2958b6da5d27f99e47eef114a105aaa5395a176cb6d1c092ab6bcf53a7164f6f85249cbb508afb9dcadc1c34a8299c1d04b1dc52da2ed3aeac41460cb23fde5ab4fedad4fb351c14f0e0959312cd4eca21bb4bb706e0c781854bdb804b014b405b337df436c25709112ac9109c11b3a451bcab6bc756af8cd5ef7c4577ba2be02161f69cc2fa0a83537250803d39a98a6cdcf238409cb54f86cdfe6110cd34c213fe254d149b45a650f436b1d8205bd18bd132ceb9755d0a37789e403f91c7420f59a0dcb78ab7ec39cd7ca01b63160a92781619c713d67d5adf91ebca97c87135e3bf535615077a535ed50365dee072bd2a470c6711252ba0da66433fdf56cd3472a86a46449db80c0aa5b8fac194f1877d0b0a949cb962b830c853feb2293a04ca7f7762e743469f8e62ce300869d2b29c21541c8b3e500094a026979cc12ac26ba50a6b212a2d22cef4ffaa735008bb4916c2d5a72100108df57889e377b5c8a5b7d88a9996f989063120d3e3dca0fcdc73947a78fd4e738b56771c895eb4599438fe60f5f536968f9e31d9f61a99c1ec2568ca6c6b525b9a22b36e56421e7c43af72e14c93e8f602c8468dfec08f92170225ee7057ba84259d6bb38ef84c23779ce8b00bac43985e2a1f10803b9764f066da14abf6549830e6322323a11356553d5f1c2a17a08eb2c1526751ce5ea3cb97f308dc8e0525226bc9512846a799a743faef154e2a42c575a0673981f24c81069d77f1cd6c0b4d9f800e670bad9ee8db3ac6938d3a40b24d5c5633417de31a35efbb203a8f6bea99837235b4ccbe3bc578e6d605fceafd2a13f2802d0971e16c177fafc47e602e68caf3e2ac89e0537b20c7193e4863508fd7f55b9288700227432d43f12b6999443d7a2eb3cf26edde8b66dcb0bc56409c61f1441065ec13ff381fe48de811b136480fe4ca6e4235b1aada71396d102730f3c5e227daf1b183fa16609028cd27490727066c9c5cda5172aabd26c62e547c28a5b84d09d873c7607b95f0c83b8bb09e2414269e3baa9a18a67a35ec9f969d6ff55589b6729a01f5cea86d97a06d57e98556c78e0dedb1ec30be610337a6a35dd0c78a8a3f589bcd078076243f1618e320b815a39dc8b8aa9e2a028ba96f2aa3cd1321c21a26df4f156bcd080307975da62ddf323b1c6ef38f3857310a142783889a543ac016f0d5e67b59be56faad8950f4a96e811f9826faf696ec73b3efbc2e1d44e4be062f13f5fc4804b7c5d4e0f010bb98b577169a3a65c730d9cdb2b1c32151166d0d40bb07fed5f6489ed66ea2299a90b00b9c0a7e01e6054f0b71b384dc147771945bab1f3629bb6d6729a946bf39f5e30e5688733872a6d5fbe8cd99652bc3400b9630dad5c1272384b978269b9ac5b70275f6a4936d366893957bace15c56349f0afb689f23b8027e670b5353020fa2b19e405332b7dbee0892700adc1e9c699263e074ce6c128732f036c4196c408fae6cb07ac2d1449b6aa08c271cb168eb9ef6e6a75cb78209a55c214e72ea18dd3beb4e436594c12ffa76b3cfb39deeb8ef7c602f1e86034f88e0aa9a7d46449f45d41eb0dc4602050fd748109cac9641f56b2f1fe2db6cd7fe41ecd61fea3095eb7ec6b2cdb74bb6474fd85457a3b635dca6bde52d59c13ae210afe6aa57f0ee3200e564fdd0a336506e500429ca783533ff0888924d0a5689996b087f7a3dc49251f728a0abc53748402b640bde320b5fcbf536ef6801c53c4764e8338ae54d04fd13b0e67b8106aaeabcb86ac812ecf678da086169682411115e86741d7bebda4cf396b7f7bcb7f8f1814daabac88fa967f001e3ec331609e25f8dddb04ba1bc9548bf5a959b9f434231c0878664481dc9cf972d5fc8dceec45c251bfc95df6ea161baf3814f4944a45c7095c40355112e1752519c587f58482ea89ae834c669db1653789dd48929cb46d7894455671c94b669d31c47fce0d85d11a229b84a9a0197279e2201025ef9176b95a1fc9cc11507f59ca02c8752c272bf7041909b11611fcd8d2085f1f222c1f582ddb361966842f8b08921f122d651b4314ca4dd4b060c118d45ecb944fa391f80bf0872f4ba6735d5a83a685796d0b4f7dd4740689796db0837cc614827b2200d171705528c23e3021a55c901152048d51eba2f9bc72b7c445f0ac44c6c38cf25bbf69399d8450804beba8eb4a9d483e300e48a9036f8b3f9341d41448fe112c9341e622646ada5e1b0db2c2b63d4740c7db20a591458ee091a5d2d947554acb010e0a3cab7b808001123b4ebe1e5259b6240d57863f1b5f0bfc03aa83d0e492cc1eaf17aba0c6ed33e9fb936f53d10bad33880a66e4bd9fc65665a3ee20b3be7ef568388abeb1e3acfad81b4e1d5d06e18f5b8c30d392fec4813e31d9cccb1f02852800a5abf9dd9ef1056ba2c4bf6f941e225831234137c7015bdd30743278817b87a0eb0638587a34bb1c5ef832ea5f2901d89d71a076f282cb4ac9a855e30e1aa054f147c4b8e2d9b731f667aa873ef05946596f35a158475140d1f2218b9833b0cb1e8792f805e93feae199bfefd4224b2eab02b61587e6da13dfd42b2169b917d0a5e57a8701087abcce6a0868f281f633c0ce74d29092f8cf5215e10cf2e0997499940e28fd85e9ad1fc9e595bf6f765f646caaa685291656e594f742ae45004d9b6348ed91cab4def32dae565bf52ba4d282363844526f2150bbd3c2b4a690895fcd44ff5ad116084b2890d43acb2d7a11384d06a97cc48147484d478497f11d5500c78a680341f5ffb4ddf08f6cefccc5e686983e9d9cc2fd2e8c7c3e4aa8bd796739500dbf4cdf2c293d0859f42fed39ae0384fd506c0372bd3ea10a1d99d673dd3df1a2fb5ac03dafb37663bc3bff3fdd9ef446b9fa4d3aacb3f3d0417bbe36ad6b13fffe9d8245b38f7af6611adac30326034592038dec1e0d05dbe1077d4d44d8c6c0014a9fb9b78f31b6d82815285486d283dbcd3f00515ad73fa6f5be50b6aa9a0cd23b646c2a0d0cfe18306866e5db060e6634fe1f16c00dfcc19b99010be6e4697c10f1d594563d7e0f606dac264b45be437d637feb66388ff708b587afc25cd1e0072a2587b389902abc2468fc8c0700f058ee1e45d198fdc6de79b27f5db5a1856cf5e4bbf3400e010f811a7c6027e8002bf8e7548f96152c49a6c0e3dbd86d6c369e64299e3922a1c17183f4a16fc8c65e7ce14454dfcdd7145c623486aac4e9716057e78dc2d0e84bd7ab75d61fd441353ed323621c48df2dbbaf6ebc379711f64e001f445b56b4858cde9e230d00d3a19b86347f66c5ef385a812e394703c05bfad87890ffa1b02a6fc84f2b382e412a5bd0c6707171012ae0c9ed286c24b032f533828652bd98b7e894693f4e5ec1887485bb7a59498fce48467caf218798b5546faa7f9d4dca2f6d8b76c6d583c7a9aa49e0e3b146e29ec3641b3b9e97e81ee1b0666fabb17d42bbe166e0419f61ca7b78bc3f4a142c9e81fdda7693419adcf4384b0113f1cb0f60f4d70f9bf9ec5cdcaa26b6f85aa07a1ae24851c7c4870bec2b4bc498ea04195fa573882ca9daefe25d316c7b6904f0cbb1a7da57d430cd745f8552c1bac4f25affd0c6498f4208a3cdda45871471f1a03023be269ac423b263b0f616a36bc085a78cdbfe2fe1395ec96741fb03a0722c3d369afc1a01633f0d7498a85c92915cf3e40ec394a47ff353432f05f4660a95b80833b280ea1f5f0300f41b9aeafa0a65bda0cc15860c80868c96a4f7e14441cff6f6a5a00b900762360a722b6a41e4ad8ccc2c1d325d3629f54cb9fcdce715c835db41a14224fde99c6bd88f7edb9f2b052bdbd03f8136f0c30769b33228e0d76ac12b4b700305b976ce6a40f3689a1ac54582601ab69cda93bb9a95a8b4aba71d3764f5229941b8680551be0055d55876f2ae36fdbaf27f9b13c6a33d580518be1fde5fdc854ecab05bc05c10eb6dd76767a1aeef8d778c1490cd7674ae1d0da61c21dc6cbf7180658fd1f3db6d55bb484d0db6799e99365ad3e61675af9742f3f596065e6bc0f4687640adf3fbc78c2e1e42c268816d01fd8590e4625d68be2c798aaefc68d2ab34ec39df2bcb0ab0e52eeed2ab74728199efbe0d42a36fc3c598178d64851279f9374e2fcf2eb1cd54513b6aeb25f96c8950a390689acfb50f02ba1a8f0baa23acb770e7de1b8be13746aa1605e50f1fea3a67e6b31f73857a6f7a7ffb1ca8d12a5d70828d566938128909b27dd007845c8d3569436027eb44c5858c42212d0a6bcb20cafec1b0c8043afbb84c9247661aa33ccb2d6702736b10b0fb69796c9197bf7205582bfd6b91934e0b7d2e56a67c4ea9c43c30d4083e49e387a8ecf2caf07ef0733028d48676503090e102d08461a9a0e4ab233c5ff551bde6ecb2cd6de4c23f0153d985d5b922d17f6bd3f82984eaa5739e8881fe5e9903104a99bf795780a4c2e558b8c7e6c25fff1870b77adfbe72bdfa648c1cdaa0cafdaa95e206153f06d461c8ca88f4c393216e112ccd54ba7c45f031eb2acdacc6a7b9349596e29941505779c19042f0fd334ddd6a8ec00a434629fd48c8f91b17c85b2611b57980061250d3056b5102c310434d9a33c097b293d55c3070646731b3521a09fee4882c89939e6e27a7af08ed0306ad5c8e5d50b4fa7989088165452a9dd3f4ad8eb1e151f6d2c79fe18c8f6fb83d3ef7acb3e2393d08c22663f05360c573140a82d11ac324e65564a5f376440dd138bb201ed9410519e0e0ad25078c751c9d9d546cbab26c91b279a172611dc5e26e4fd32ab2bba9891d61da65a87bab6586678e38e2b0b03dc621330f08998a0934f0a8499026ada4ff7ce5cd6e19dd1e31334624184761d7b5e7783a834594ddd7e70cfef32236ae1ca9d4e484ec3061a02130fb84de88149569aab587f3bda442139b4dccdfac4f78084ba93230740de608bfdbb449c5569dfa877e9d4f2f8d7ac0c6201ee6848a582505f6d78923c88c41ad0154ff3c8d310c63d9a9a155fcae7a793076a7769c54f6724e7ff42dc6205bd476ae0d63b05c5a479f9369a51546540b42c21189db637d99a02882b5c19c8013a6c3b2f85853bd2c9a025391017f0e1f525707074f9a06e607e501459a307136166784d74928416c8ea9b1b62d8beefe1d81016fd4b17b305700231ac429da782011e080d091191e7c6200ea81c105ae2575a9b682017571afb64b9b1826a5fe256df33e834d006a08cf8b52354ba18e1457b23a271876d77d987bf03931d371e9c50f8f92bdf3c0792d63618a37676dbd9687e14e31d9264c324a56b92628b84f59fdc0c2c5ee1ab1db73cacd6419e30465d44e3a243b669cd803803b86fae6329e7e19ff916ea4cd338c3034433c9aba9f58a66e79b18eea0d801034ed90bf7e3bdf600be8aa5e8eadcc063000eaa7b20d9b84c9602b04e6ed4cbd0f8bbd5fd5bae48fce32f78c9ae393a0e7c3474430813ad810aa4341b5aa1768bbd88c27f96c4d2bb98ea35ff727593609811dbea5baf8fbf3940379b6636f3e81b09aed4d979c3624b7637a257e6d07f944fd1eb730955dccc027985a4eda25be282f84047ad234ef5ee2245590fdaafb50bc92c0ca84e3c9e20c905cafcaa8ac9026eba8c4d24e498296d7ab55a498ef2e10b0a4b0e42dfb8afc3bcb578f1c83a23bee878830898b2f865e4acb25dcf28a50945391205cb3c28081eec105914de4fba31c286c6fefad6908ff69108f9cb6f668e767ce0745bd116231d2c4cfbd667d66416b049f8ea9cdae2a123a04ef66161718b0588d299ac6cf960a191bdd261c870264dcb3279c9f540dfe50d2f586754de0d5fec929d61501caec7b3a9896c720995011f4a26320c60b4275b96c9d3b2f28ffcc5fdad5cbb90e1652884263b4131a76384427909f8b211e8d10171694fed5d9776601d5e442697cbad52f87ee79c710a421db0be3441ad23c59636434e670d6b5bd7a317a197207f36e352d4086b1ce38b852613d00d8843089a73ae82322a7214fe2eb9d29625a269c3481363cd2724df05154326807c4750a530612db45e7d0a33fb6755542d090a52bb261c580ba5b17fd8f2348c215edf06f4312a0181da597367a063a4ea0acf29544ec38f19256248e289a9f2ff34c27c4ed82c00f8789e3f7b6bd8e33f7c4acc6f399d8a7f5f4d77dfa8a5099f79d568357c84153bdbe8b5d3185494cc7aee5e45a44b63b06116cb18205a832e0272c34c44fd0583bbc3d05a7cbe2cb05626e1a9b607546b90c2dfb7a1dd09c3785a6df261bb76f867b1061ac3d313565081edd576fa1d7853152943603e8e681d5ff9bb6fb4893b93272fde6efe74056077f7fd622208b200cd41c6eccdff36b011fe08ca54d20a1a4e80661ce5a4c148d5dd5c137d658f6d373776ec21c83ad929f59a4df868c80be216c4cd5484e74cc74d02095689c965ad134b5e5da33f6c04e0077a8eca494b3bedecab0e03c75bbaf28a26d46e41f3f5e902f9e0a2feaa78e312a9325b45ffba866e8c90cb91bb299fff2adea1c3815c2d430495710800697f7946988f2c6f664aa1dc582c5ebd22fb121ec7c488fc0cbf47c0d3dc16214d5545b050739da298779b42a6dce98244d8bbb915e9c627248e141a2a7092fa05565ac7ee96028866df311b312657d62b948a4cc6f02ab5b1afced3069b1c0b658efdc3365f3ee52aaeb232c95f5e97065d62e02eeb9199aab3246b4331d58fb792f8d342220ec6ef8000b8c98d878ec668472e2105b21ab03fd288e378acaabd51067dab3882570dcf07ad3a299693ee7fc273e9d97bfe2d26f9710a75b1ba04c584a3bb0663d01c63f61ecbd81236145e2ed9fe84a74494db4b56ccfb8d5a02c8a72e097bb0bcd260e9b78fa32091be8c318cdf76d949b423164277dd6e5bb1a4b7bbfffb3083adc55956723878d0e9fff2456d4b0245d32c32860a73d5a9c5ec84f64eac7b70bbad085afdbc4e7bf5cd86f1affb7fa217bc6391703dbe5254fc0351d02279cbc0260a8400d6f30d80d88fee5be6b334b1a2bc3b885df127760c0e3beed7e139088ec912daf319cb31f51121acc55fca45507a21b746cf662074c11be673fc1eb171202e6c70ab0dafb90d8f9f089140cf8a2472857c73ce5a1586f9888a137b14d0ce89feedeaa715b0685229cecc1c5b2452f746d74e4c9ccf4cd211c92d5da6a9606a2b033b647bce0dc1cd849d30ba49f7251384700a6aa77b89f6317cab26d1fe2041d34fcbe0f481b332644a6ad2eaf0b88fd6f2d42ce82cf3ace1b57afe42137ca1cf4ced7c321538a6f7a928e4bba45920f46b39134738be23bc72e65a3dd1f714d458e71535088a5fd2d64ad7f203860978b5ef2ffb9c12cc9f1da67c32612b0770902ee39865835dcdd797637b4362fa22c748b97a1a36b444a40385b5ab83cb87470d04ebef5833667ba88ff9e5a0a9a586608c89dc190b892ab2ee40cb55fc06b632ee660e7191006b8f7323be53b0551d8feb7b8d43a1835f7fb936995ae38745c1ed57894e58d580334cfd01a1b14b937282e933253e1b8f31f05639e38886c434101caf629f7ed6343af136bcdb4125301569fdd6f5e5d7f046e1430569c708a1e3eae322de41d37701cd36f5b5663ec2702972c6b6fc8888bc78146039e117c02535108205431bda2b8217829f4de6b3bf8eddcedb4515e6194366c94b72c9898846412e5a35a97b29071d550cb4f8e2ea286731acde3a38cd38fc75cd21eaf14f21c4e36a0c27aa0ddcca0b3636662071de068b721d22d00565192cc89f4fd57f39632cde06ae28c39f50a5c42779e4908f5b561973d85a510fe9d60075ab066e6651981666f2b739449c561464d93024a6050215f6aeb4b5b88797001ef62766cf7267194f2b1b0cb6157d202b5a751bf9a07a959af7ab5936450837a940750deb8cee160b40ab01aa9fa103554919a4910d48722efeec2452f0770690e95a791d89d1013e75eff2d885dc8a36ba9a1234f937f11bd93e2705d176ee847931c18c440e04f66cdbaf439ae1656ed1b881ad1b575394dc330c54751fac88ec26aaf252560649ba8d555d93b6cf636aafacc688af067a1e2e3d99fa6624d838c3e2fea61f8eaa3df0e06b0ca22159f5833dcd93902c0ce18a49e619bc675b03407563d2ec0668e2636edd8cc9761669e9acddbf20d6f08d988c33f7349c0a3de3acb84079318434411d198720cab68eaea3bc09154eede78c81c3affba9d9b67c2bd4a2425aa22c219b2adea91b4095b4e1b326d154819227af476fac047d9055b829bbe9fda875140368d6649708a53efbe856accf62a4cc928f808f2ddf309759825564f96f146e86e6f03ad5c4760b110cf03b62991e1fc2eae83abef253198c5f4a034ed42e8f0114bf93c69884c906103503e86d5ae14df8a3798cc45f796ad744286770f4623553755ad0bdd48c8cca866704d1aaaa0025d193aa1e404bb25416d5242b079e37ace3f03507c9b787d15e309a2bf71b57e8099e5bb8772926b0e9ae66b77fb92dfe9fdd4fbbc52e432a08cc7aa9197f400547c4bb9106132b28695c7091324eafd8e922d69831488eae16db5452ae464271f5ebf6fa3036930f690bb00e31a884c03d9a80a1229b9958d82193210c234b4dc06aa1d24feccd34914d60855cd32e3b7a5681fc82f9d0333603234082f1538beae1686a4d264aa3fac888d4ed02fddb465c75270f79dab7de7990a2e2165158a041a088ba0da4b327e48a8af2512ddfe21b4534f9297c99487639373be6fd5cdb510ebda12d7d0e57a50838bc4d714fda7d4cb5b0ac089461ae196805ab21edfa815bd7f0ea0bb52c5aa1f009eccf96c1dc6964014e33e616632181196b17c549374bd0784c04ddaede20be2c628d7d1b4ee3d99ed7ee2ae21f034b8f51374941df3b9c181f25189b7fd02154fffbd3bbd67e6a5379ae94c45c909f096ad27da443588e1fccd3091a5c7dbe483581a8dcf675e462f30a9d073b73a0b89f164b7266adcf503d6ae0cac932430eb06d0d67a6d19ccfa6c3f29e228d4b76fab91d0215a222a81a8d49f096ade86ad33ab72e0e7a1bb762fd26c09a4e19131a5e25e9448f47b12515e835c02259e7893d564860b7f00396f3bada08a8bef4fe419e9f64b066400ee6284c66170acb5ff3822c019b51cba00aa2be32b986412ef6983907734d43138162770413480332867494d033b3d4d437d5851b0abea67a4a19e8cf783b1220e68800c1a0674ae0a1627c08f4f8b2f2fc7e12877aeb007b030fa5b72711649d69888c4586b6988f083169f3203ac663023a663871431dc51225bad783014b4006ec44223fd346d6fc600c819c90eaa132ea209ad6b950d8db41bb6a21f340fc7335a9e9d0c2ecf3b819c5121c63c7c421ba843c148db4b4b323dd148eb2b28df30881fa43252535934d2078bbc8eaee920db359e6b502373f9c466bc65a4cb0c8d46ba842e206aa3910e458575991bd50f70703c0312baa3912e1d1f8d549e96ff5c78c29978485efa422ecf5f314bf5386209760d2303cbd27b5305ba67cf6ca9e857acc6cf40638847914ec571b4dc327f84c4e01f46e4528d64f1823960f84e466c5d55cc068957ba76c2443916ae25e697cb5b65c0b9dce3c80d21dbc06065054d2f7eacd4a99ac9fe9245c9af420e269b0e8c035320c7549f18ec5b317f047803dbff70b18f8bc9cc1324ce36b82de2af91fca54d0d7ec1d614d244542eb8d711e55bbe5816aa6af9f4be42e4226d552d33e8da12c99e0086160b69bc0dc20ba5b99a8e7ba344ff354309e516dc3c232720912142206742f43eb1e75b627e75331e73bb57e3ea816f792a311aa5e8d8554d90ad4826de721bc0dd7c73450646dcc3b044cfe1211e854ffc5d20aacc4ffe70225524455760a0f6037065be10389f86f30b9d87166080c70c7898d868286150c7c0e6cdce3736c2df62226354fa9f6a480686af2d9013157956c4191fbdfae467c435afe39f4dafcafe70be9d763080ba84497a54acdd5fe4b32344b0b4ce4708584c99a52997bef8b68f12ee7034555530d955a517203ce0b91aad2ce1da1ec712bb4d7297273547635e07e5e2ab7ef2a1b771cf3b3dea770ccfffe25783c45ef63ad564f61e7c1c6b6943c1a1538d41ebef6658e144c64da3d2fefb41385737b0c9c0837daa2da11418d455d29f1214d9ba566861eb2fe2f1dc1321707033e6beb40a87a02cf1e408a1391f7d880bd61edb2a5fc571294f47d0e2c76fd58bfd39e4c7530df0182fbf8fd08967b58fa0fb8af62ccf0ee824374785025745c7ea078793cdbe02db29d391dc777b303ccf4627e3537aaed9af522ffc456a814cef60cfcbbd71081077e279edc6688abec5faea056429fadfc51613df7d6a8f9bd7a978842371f552be1f1b37ecef5516bffef70909187ebb9c71ce5a7750777fccad05dbd2096592dd1885b1a8750b085b5594a8052adf6182f83d63547285d10d2e0d43cf54342c7382fa8c310e722b2b5d361960ef98e0108ccc73a83e3da11ba94a53995f7b453e4fc00e8ae354d84449fac7de861b3048e22237dec99d527fe1f8e803bcf9cbdd0714cd244d27fa733b9c9c0cdb3d979cb71b1c8bf439021945c5632054a4292ec6c1f74465c2df9e7ce4bc4f66837abcc1736a5ae0af5d9e2d4a4e7593a900a10d02c6dff7ec2e25097a0c5cca624a2e67150889e2f837b2a6a4ac0b420faae41c70f0cafd4b991ec23d4e408c0cca6f833ccbc2d3e5b4135d7c968e7d1166f56d6d821608416af3f489d89a0b8792ee000310fe3b007b1f76d8f79a35fa893a37a17aff71d683a9490a11ef35b46d46e5ee12d18756d48f80d2020fcda722df6600c69d16cce73ddcc9fb5d058be44377b5db386f704e72892e310c0b21ee610e114ad2d1bcc43774f7007f58e0ff4fd974b096fafa078e4f975548869a8cf3233a0c2fa09590f89c203d7daf7afd8f68cc74c1472ac441a4819f950f23cb5ad3edd977b363ee819e74b19baee623b73c0a753ff7f60e70a4bc6130d9da33af1c9c370e7a109bb84e97d906f5e91ec33d37de93656362d27b78df150d2434a8f736372cbd76ff5d455fba73fd25ff3e02cfc34f4d339a64500d93fce8ab0df118247133b5eb5834e3a07b440f7ae8d7123890b0461b8c84a1502b4df6d6c6edcb9c6cbac7bb9ea0b8eeaf7120bb5b005f8514718a204ae4d57021f6f34fb85b2c3c3d8332e846dc992d7e024ccd78b2c7202b197b3c3a67b823906eed092724128695769763553af540512345e5fd42929c22b872c0530ca20082553f46819139cee1c7e8cfde432a868d5449623311f9a9dfddb7a5e0c34b07f2a93e5eb42c472b23970a4eea3f9009c062a711d38057b67930c2f7dd69901860767bf477e4ee36e5a29f8583702a737ecb6ded4bf498676cd1119aedde656b9defaae6a827adbf9718958b50d6cdfe2819330a0ac95246c77a2abb1e4f2de03b18b2a1edffcd21b629694f6c04d5410c9caa196351bac8c445ef094efadc71b1f6a1cc926ec152362cab2b336f93c152df3644bf91ee6bf9b4897b13e6941f907bd78b4660be062f4bc0d5d0934ec5f7afe828f3be1e2e1fae1e9acd1a2dd9fb13df3c151d9777d85e4d7b9b6d8e8c6aee64b6454ee31728336c9977d69abdf58920680e60e8ef5b1e6a83a302f87447f3de1549ca7dbf5f4a32191d72ba0425270bb7bf6f17d282b3a172cb4287b954c625118da112319485a050f5cc97c79871098303bf669d6e8903fd98dc464d6374a24de1f8eb479ffcb2a901882577f95fe9a721cf4b48fce8c3667037dd3c2ed43ba138331e13dc30be40a6dd07ea7dc2b78f527f0e866c442575230a4e3152525cd3225a9280912b287502d9c24d0fecb9f0f8ed52f600a5cdaa157061e270445124d4960c0060e2b1c210c7f1ef6294924342b7c9c033dba5d3fadf6560b87e9b7616c2bfde253b7d2e31860d38bcaccc6699979696c47600bd6c1d8387d25e3b9a9a29d15dcf63d1b9a10717a5aba28948d55d353dc2302e3ee97a9315945d07caf75cf13d30b4c4b7881a0b2ba198336408db7eeff1a72ba35452f2229fd3392d264c1e046109a6a723f283fdd0b295067689d026bd26cc986d97e0da6179199b121565e2455a6fdc4b408e76e13494a23ea8b6ca8c438880a25aaeaa490434f660c58dd74be371b083b4e14cf1a029b8c6d5b7a3f8fb1994df0628a8c0567c43d0739da80aec11785634f9aec6d4bf7f7267ad42b74d0833628cc871718424334368cf24c9c980d22531c71c6329539926d1e3d33df6df9f24c1713a059a704ecf34927c4e4282856a38a0fe2a1685247e6f03de067fa84fafc8ecdf5bf62d68e8c49dafc3c2cc531a75db46bbfac79ff77e32795e2cbb45d2419bc2ea9ee37a2b4ef05da56daac9e55273dde8292e3bfde07d48c6fea5f6b947a8efd0ef5a0e0fe263fa229d1032c8b979f1fa9fd8420eddcdb6566302cd707c662bbc3c3efd024320814b97edd27fe60b29046652381948c605c66d4360dc7f915285d1559d27eb491bf6c58d400c4c2451653e522209f8c7597e5c375f32114510a522ff96350c0dd6e0527004432d4ac2da84a032c86a0e8b75ffe6f1348b4deaa238e09a8ff7a2cf2ce82beba3073d428f6361798ce59cdfbb347c55bc1a17361168841b072cb6c1041e27d0ab3de0f891b8ef913e4c59b23144fd1b84d06887f21a646b2d96bb8894a6759791c1b243812abeefa16a114d1f14ad5a5dd3834e3997cfe62d397371f953f4256def55fb9b88c783641868c5ae4fe4b61fec596ff97ad3662df86cb0b85950bb11929d4e1815247a60c37635afbf45f0ade00407f98f41cec52bb1b73ef2fe81fcb23a6541b8ea233abfea3ebb714439d54ca433ec96d32f40e762541cf9a67c750f7a643101aeb6bee2ce9e69a27f540db51fab67f55c5bc2dac4afd505b6ec7ebad2415cf3c3b311f0e15b4be4a9b1df76d14dc00cc4927ff8ba3d00276d8166dcf9a7c01ee0df2db64e818a821fe0205eeef49f056c3ffff051f94189c6bf6c7669402418417af5447d3b237c1eb9504cfad72fea840798d2077b66fee5e74b040515336957858afb47d595ee1ca3c7bdcf8b4bf477ec1dbd687f99a1209f885cc13c087ea133b42197edf66bfd8339e1f4322cf20b9ef9a5ef8938ff1aa71fdac3ba0698ffdbc3246016712e0e1523cb10bf7f1d0806bf35552b9691ddcd4f0710f6a2ca4a003e6409", - "0x3a65787472696e7369635f696e646578": "0x00000000", - "0x3a63": "0x", - "0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01", - "0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000" - }, - "childrenDefault": {} - } - } -} diff --git a/cumulus/parachains/common/src/genesis_config_helpers.rs b/cumulus/parachains/common/src/genesis_config_helpers.rs new file mode 100644 index 0000000000000000000000000000000000000000..cd98c3a729d26cc4c7a7895995e5a479bf418ebd --- /dev/null +++ b/cumulus/parachains/common/src/genesis_config_helpers.rs @@ -0,0 +1,46 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Some common helpers for declaring runtime's presets + +use crate::{AccountId, Signature}; +#[cfg(not(feature = "std"))] +use alloc::format; +use sp_core::{Pair, Public}; +use sp_runtime::traits::{IdentifyAccount, Verify}; + +/// Helper function to generate a crypto pair from seed. +pub fn get_from_seed(seed: &str) -> ::Public { + TPublic::Pair::from_string(&format!("//{seed}"), None) + .expect("static values are valid; qed") + .public() +} + +type AccountPublic = ::Signer; + +/// Helper function to generate an account id from seed. +pub fn get_account_id_from_seed(seed: &str) -> AccountId +where + AccountPublic: From<::Public>, +{ + AccountPublic::from(get_from_seed::(seed)).into_account() +} + +/// Generate collator keys from seed. +/// +/// This function's return type must always match the session keys of the chain in tuple format. +pub fn get_collator_keys_from_seed(seed: &str) -> ::Public { + get_from_seed::(seed) +} diff --git a/cumulus/parachains/common/src/lib.rs b/cumulus/parachains/common/src/lib.rs index 3cffb69daac3fae6d5729a985489433f442bf22c..60040fda9928a0a7efcba3192fbccddab80cdab4 100644 --- a/cumulus/parachains/common/src/lib.rs +++ b/cumulus/parachains/common/src/lib.rs @@ -17,6 +17,7 @@ extern crate alloc; +pub mod genesis_config_helpers; pub mod impls; pub mod message_queue; pub mod xcm_config; diff --git a/cumulus/parachains/common/src/message_queue.rs b/cumulus/parachains/common/src/message_queue.rs index 511d6243cb8c47a0e137f9eec03bbb1afdf5e644..d6f2118e454fab1c584eefe9db3606480f067474 100644 --- a/cumulus/parachains/common/src/message_queue.rs +++ b/cumulus/parachains/common/src/message_queue.rs @@ -1,18 +1,18 @@ // Copyright 2020 Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Cumulus. -// Polkadot is free software: you can redistribute it and/or modify +// 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. -// Polkadot is distributed in the hope that it will be useful, +// 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 Polkadot. If not, see . +// along with Cumulus. If not, see . //! Helpers to deal with configuring the message queue in the runtime. 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 7bd91ae6774c61f93b12a91af298e64777980ac1..51ce5b18005681d0bff517691f31e2489a7eeb67 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/Cargo.toml @@ -26,3 +26,6 @@ testnet-parachains-constants = { features = ["rococo"], workspace = true, defaul # Polkadot xcm = { workspace = true } + +# Bridges +bp-bridge-hub-rococo = { workspace = true } diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs index 1f98d3ba964d8b808f42f17fdb44a4add24beb90..75b61d6a4cd7ae2be4222874a0e7cc586c0bf98e 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs @@ -24,8 +24,8 @@ use frame_support::traits::OnInitialize; use emulated_integration_tests_common::{ impl_accounts_helpers_for_parachain, impl_assert_events_helpers_for_parachain, impl_assets_helpers_for_parachain, impl_assets_helpers_for_system_parachain, - impl_foreign_assets_helpers_for_parachain, impl_xcm_helpers_for_parachain, impls::Parachain, - xcm_emulator::decl_test_parachains, + impl_bridge_helpers_for_chain, impl_foreign_assets_helpers_for_parachain, + impl_xcm_helpers_for_parachain, impls::Parachain, xcm_emulator::decl_test_parachains, }; use rococo_emulated_chain::Rococo; @@ -61,3 +61,9 @@ impl_assets_helpers_for_system_parachain!(AssetHubRococo, Rococo); impl_assets_helpers_for_parachain!(AssetHubRococo); impl_foreign_assets_helpers_for_parachain!(AssetHubRococo, xcm::v4::Location); impl_xcm_helpers_for_parachain!(AssetHubRococo); +impl_bridge_helpers_for_chain!( + AssetHubRococo, + ParaPallet, + PolkadotXcm, + bp_bridge_hub_rococo::RuntimeCall::XcmOverBridgeHubWestend +); diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/Cargo.toml index 86d4ce3e7ac829e959aa3169adbd22a257cc26a5..d32f983217069ee346feb2cdb2251e770174e3fd 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/Cargo.toml @@ -26,3 +26,6 @@ testnet-parachains-constants = { features = ["westend"], workspace = true, defau # Polkadot xcm = { workspace = true } + +# Bridges +bp-bridge-hub-westend = { workspace = true } diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs index d20e059f9fea48a649934b6ca2a0470327ee5e54..a9cfcda0dacdd3afa0cadc19f80e2805e245f172 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/genesis.rs @@ -20,6 +20,7 @@ use sp_core::{sr25519, storage::Storage}; // Cumulus use emulated_integration_tests_common::{ accounts, build_genesis_storage, collators, get_account_id_from_seed, + PenpalBSiblingSovereignAccount, PenpalBTeleportableAssetLocation, PenpalSiblingSovereignAccount, PenpalTeleportableAssetLocation, RESERVABLE_ASSET_ID, SAFE_XCM_VERSION, USDT_ID, }; @@ -27,6 +28,7 @@ use parachains_common::{AccountId, Balance}; pub const PARA_ID: u32 = 1000; pub const ED: Balance = testnet_parachains_constants::westend::currency::EXISTENTIAL_DEPOSIT; +pub const USDT_ED: Balance = 70_000; parameter_types! { pub AssetHubWestendAssetOwner: AccountId = get_account_id_from_seed::("Alice"); @@ -67,7 +69,7 @@ pub fn genesis() -> Storage { assets: asset_hub_westend_runtime::AssetsConfig { assets: vec![ (RESERVABLE_ASSET_ID, AssetHubWestendAssetOwner::get(), false, ED), - (USDT_ID, AssetHubWestendAssetOwner::get(), true, ED), + (USDT_ID, AssetHubWestendAssetOwner::get(), true, USDT_ED), ], ..Default::default() }, @@ -80,6 +82,13 @@ pub fn genesis() -> Storage { false, ED, ), + // PenpalB's teleportable asset representation + ( + PenpalBTeleportableAssetLocation::get(), + PenpalBSiblingSovereignAccount::get(), + false, + ED, + ), ], ..Default::default() }, diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/lib.rs index 6066adec52c30020e2c9b26cf2db303e0948fa9c..c44f4b010c0ac9ac7d8fc9b547946c7dfd8376e5 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend/src/lib.rs @@ -24,8 +24,8 @@ use frame_support::traits::OnInitialize; use emulated_integration_tests_common::{ impl_accounts_helpers_for_parachain, impl_assert_events_helpers_for_parachain, impl_assets_helpers_for_parachain, impl_assets_helpers_for_system_parachain, - impl_foreign_assets_helpers_for_parachain, impl_xcm_helpers_for_parachain, impls::Parachain, - xcm_emulator::decl_test_parachains, + impl_bridge_helpers_for_chain, impl_foreign_assets_helpers_for_parachain, + impl_xcm_helpers_for_parachain, impls::Parachain, xcm_emulator::decl_test_parachains, }; use westend_emulated_chain::Westend; @@ -61,3 +61,9 @@ impl_assets_helpers_for_system_parachain!(AssetHubWestend, Westend); impl_assets_helpers_for_parachain!(AssetHubWestend); impl_foreign_assets_helpers_for_parachain!(AssetHubWestend, xcm::v4::Location); impl_xcm_helpers_for_parachain!(AssetHubWestend); +impl_bridge_helpers_for_chain!( + AssetHubWestend, + ParaPallet, + PolkadotXcm, + bp_bridge_hub_westend::RuntimeCall::XcmOverBridgeHubRococo +); diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/Cargo.toml index f3c0799ad0f6acf14b3e99d4e27e46921c4e9cb9..266d743ca0c22c53160f877836eb3273f8735e2f 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 @@ -16,6 +16,12 @@ workspace = true sp-core = { workspace = true } frame-support = { workspace = true } +# Polkadot Dependencies +xcm = { workspace = true } + +# Bridge dependencies +bp-messages = { workspace = true } + # Cumulus parachains-common = { workspace = true, default-features = true } emulated-integration-tests-common = { workspace = true } 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 3786d529ea6502988f92c18955722a7a0f9ebded..b9c0c01101c6e7432956aad334b56a1cb27def01 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 @@ -21,6 +21,7 @@ use emulated_integration_tests_common::{ accounts, build_genesis_storage, collators, get_account_id_from_seed, SAFE_XCM_VERSION, }; use parachains_common::Balance; +use xcm::latest::prelude::*; pub const ASSETHUB_PARA_ID: u32 = 1000; pub const PARA_ID: u32 = 1013; @@ -66,6 +67,17 @@ pub fn genesis() -> Storage { owner: Some(get_account_id_from_seed::(accounts::BOB)), ..Default::default() }, + xcm_over_bridge_hub_westend: bridge_hub_rococo_runtime::XcmOverBridgeHubWestendConfig { + opened_bridges: vec![ + // open AHR -> AHW bridge + ( + Location::new(1, [Parachain(1000)]), + Junctions::from([Westend.into(), Parachain(1000)]), + Some(bp_messages::LegacyLaneId([0, 0, 0, 2])), + ), + ], + ..Default::default() + }, ethereum_system: bridge_hub_rococo_runtime::EthereumSystemConfig { para_id: PARA_ID.into(), asset_hub_para_id: ASSETHUB_PARA_ID.into(), 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 ebcec9641e7d9f122f5b6737769bc140d9c877a5..88d7348f50f28f7376acc9a1d73f068d1397f4c0 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 @@ -16,6 +16,12 @@ workspace = true sp-core = { workspace = true } frame-support = { workspace = true } +# Polkadot Dependencies +xcm = { workspace = true } + +# Bridge dependencies +bp-messages = { workspace = true } + # Cumulus parachains-common = { workspace = true, default-features = true } emulated-integration-tests-common = { workspace = true } 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 f38f385db65068928b0f83de4cdef998813c0dba..3ffe3d86b2acda13e5bbf76f1f0052bc3da58237 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 @@ -21,6 +21,7 @@ use emulated_integration_tests_common::{ accounts, build_genesis_storage, collators, get_account_id_from_seed, SAFE_XCM_VERSION, }; use parachains_common::Balance; +use xcm::latest::prelude::*; pub const PARA_ID: u32 = 1002; pub const ASSETHUB_PARA_ID: u32 = 1000; @@ -66,6 +67,17 @@ pub fn genesis() -> Storage { owner: Some(get_account_id_from_seed::(accounts::BOB)), ..Default::default() }, + xcm_over_bridge_hub_rococo: bridge_hub_westend_runtime::XcmOverBridgeHubRococoConfig { + opened_bridges: vec![ + // open AHW -> AHR bridge + ( + Location::new(1, [Parachain(1000)]), + Junctions::from([Rococo.into(), Parachain(1000)]), + Some(bp_messages::LegacyLaneId([0, 0, 0, 2])), + ), + ], + ..Default::default() + }, ethereum_system: bridge_hub_westend_runtime::EthereumSystemConfig { para_id: PARA_ID.into(), asset_hub_para_id: ASSETHUB_PARA_ID.into(), diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/lib.rs index feb59c411c8df488c748fe79e3fd485757fd0de0..e7a28ebf4a4690dcb0930e371433e8e35fd6fc8d 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/lib.rs @@ -46,6 +46,7 @@ decl_test_parachains! { pallets = { PolkadotXcm: bridge_hub_westend_runtime::PolkadotXcm, Balances: bridge_hub_westend_runtime::Balances, + EthereumSystem: bridge_hub_westend_runtime::EthereumSystem, } }, } diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/coretime/coretime-rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/parachains/coretime/coretime-rococo/Cargo.toml index 6af3f270a905f504b471374d0b29687f766409e4..94d43c5eee2f444fdbfd7004a9436217c9291c42 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/coretime/coretime-rococo/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/coretime/coretime-rococo/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "coretime-rococo-emulated-chain" -version = "0.0.0" +version = "0.1.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/coretime/coretime-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/parachains/coretime/coretime-westend/Cargo.toml index 895a984eccb2d049d849f8cf65b44cc667990e4f..2640c27d016b36a18d913d2305ef1d1cfb57b91e 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/coretime/coretime-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/coretime/coretime-westend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "coretime-westend-emulated-chain" -version = "0.0.0" +version = "0.1.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/genesis.rs index 38c94b34aa2e3bfc7b81f8ee46737ba4e153c659..2c34b7e96f5eac92830d7288eeacbf82f86fc292 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/genesis.rs @@ -27,6 +27,7 @@ use penpal_runtime::xcm_config::{LocalReservableFromAssetHub, RelayLocation, Usd pub const PARA_ID_A: u32 = 2000; pub const PARA_ID_B: u32 = 2001; pub const ED: Balance = penpal_runtime::EXISTENTIAL_DEPOSIT; +pub const USDT_ED: Balance = 70_000; parameter_types! { pub PenpalSudoAccount: AccountId = get_account_id_from_seed::("Alice"); @@ -81,8 +82,8 @@ pub fn genesis(para_id: u32) -> Storage { (RelayLocation::get(), PenpalAssetOwner::get(), true, ED), // Sufficient AssetHub asset representation (LocalReservableFromAssetHub::get(), PenpalAssetOwner::get(), true, ED), - // USDT from Asset Hub - (UsdtFromAssetHub::get(), PenpalAssetOwner::get(), true, ED), + // USDT from AssetHub + (UsdtFromAssetHub::get(), PenpalAssetOwner::get(), true, USDT_ED), ], ..Default::default() }, diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/lib.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/lib.rs index 91793d33f304fbbf4d1d2aeea0bf315730a5794f..92dfa30f2e839074157b2fe9a29220ba7d8fd53e 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/lib.rs @@ -53,6 +53,7 @@ decl_test_parachains! { PolkadotXcm: penpal_runtime::PolkadotXcm, Assets: penpal_runtime::Assets, ForeignAssets: penpal_runtime::ForeignAssets, + AssetConversion: penpal_runtime::AssetConversion, Balances: penpal_runtime::Balances, } }, @@ -76,6 +77,7 @@ decl_test_parachains! { PolkadotXcm: penpal_runtime::PolkadotXcm, Assets: penpal_runtime::Assets, ForeignAssets: penpal_runtime::ForeignAssets, + AssetConversion: penpal_runtime::AssetConversion, Balances: penpal_runtime::Balances, } }, diff --git a/cumulus/parachains/integration-tests/emulated/chains/relays/westend/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/relays/westend/src/genesis.rs index 172e6e0ac93ec9cf2ec2f95257ffa1af450ad5a1..f8d43cf4648dbc3bf3bc7f01fbbdfd5e4cc187a8 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 @@ -84,9 +84,7 @@ pub fn genesis() -> Storage { minimum_validator_count: 1, stakers: validators::initial_authorities() .iter() - .map(|x| { - (x.0.clone(), x.1.clone(), STASH, westend_runtime::StakerStatus::Validator) - }) + .map(|x| (x.0.clone(), x.1.clone(), STASH, pallet_staking::StakerStatus::Validator)) .collect(), invulnerables: validators::initial_authorities().iter().map(|x| x.0.clone()).collect(), force_era: pallet_staking::Forcing::ForceNone, diff --git a/cumulus/parachains/integration-tests/emulated/common/Cargo.toml b/cumulus/parachains/integration-tests/emulated/common/Cargo.toml index 7152f1dbc272bd8eef49e2343b2c5cbbeb9f1ba4..981ee5c88b4e28e57b4ff9c8361ca64432305471 100644 --- a/cumulus/parachains/integration-tests/emulated/common/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/common/Cargo.toml @@ -42,5 +42,7 @@ asset-test-utils = { workspace = true, default-features = true } # Bridges bp-messages = { workspace = true, default-features = true } +bp-xcm-bridge-hub = { workspace = true, default-features = true } pallet-bridge-messages = { workspace = true, default-features = true } +pallet-xcm-bridge-hub = { workspace = true, default-features = true } bridge-runtime-common = { workspace = true, default-features = true } diff --git a/cumulus/parachains/integration-tests/emulated/common/src/impls.rs b/cumulus/parachains/integration-tests/emulated/common/src/impls.rs index 8f2789eb2f3a0e45d5c98263a16a118bd63cb425..c0d42cf2758e9431c87e4187eb42b9ab703f7f54 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/impls.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/impls.rs @@ -17,7 +17,8 @@ pub use codec::{Decode, Encode}; pub use paste; pub use crate::{ - xcm_helpers::xcm_transact_unpaid_execution, PROOF_SIZE_THRESHOLD, REF_TIME_THRESHOLD, + xcm_helpers::{xcm_transact_paid_execution, xcm_transact_unpaid_execution}, + PROOF_SIZE_THRESHOLD, REF_TIME_THRESHOLD, }; // Substrate @@ -30,7 +31,6 @@ pub use frame_support::{ pub use pallet_assets; pub use pallet_message_queue; pub use pallet_xcm; -use sp_core::Get; // Polkadot pub use polkadot_runtime_parachains::{ @@ -38,7 +38,9 @@ pub use polkadot_runtime_parachains::{ inclusion::{AggregateMessageOrigin, UmpQueueId}, }; pub use xcm::{ - prelude::{Location, OriginKind, Outcome, VersionedXcm, XcmError, XcmVersion}, + prelude::{ + Asset, InteriorLocation, Location, OriginKind, Outcome, VersionedXcm, XcmError, XcmVersion, + }, DoubleEncoded, }; @@ -51,7 +53,7 @@ pub use cumulus_primitives_core::{ }; pub use parachains_common::{AccountId, Balance}; pub use xcm_emulator::{ - assert_expected_events, bx, helpers::weight_within_threshold, BridgeMessage, + assert_expected_events, bx, helpers::weight_within_threshold, BridgeLaneId, BridgeMessage, BridgeMessageDispatchError, BridgeMessageHandler, Chain, Network, Parachain, RelayChain, TestExt, }; @@ -59,62 +61,62 @@ pub use xcm_emulator::{ // Bridges use bp_messages::{ target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, - LaneId, MessageKey, OutboundLaneData, + MessageKey, OutboundLaneData, }; -use bridge_runtime_common::messages_xcm_extension::XcmBlobMessageDispatchResult; -use pallet_bridge_messages::{Config, OutboundLanes, Pallet}; +pub use bp_xcm_bridge_hub::XcmBridgeHubCall; +use pallet_bridge_messages::{Config as BridgeMessagesConfig, LaneIdOf, OutboundLanes, Pallet}; pub use pallet_bridge_messages::{ Instance1 as BridgeMessagesInstance1, Instance2 as BridgeMessagesInstance2, Instance3 as BridgeMessagesInstance3, }; +use pallet_xcm_bridge_hub::XcmBlobMessageDispatchResult; pub struct BridgeHubMessageHandler { _marker: std::marker::PhantomData<(S, SI, T, TI)>, } -struct LaneIdWrapper(LaneId); - -impl From for u32 { - fn from(lane_id: LaneIdWrapper) -> u32 { - u32::from_be_bytes(lane_id.0 .0) +struct LaneIdWrapper(LaneId); +impl From> for BridgeLaneId { + fn from(lane_id: LaneIdWrapper) -> BridgeLaneId { + lane_id.0.encode() } } - -impl From for LaneIdWrapper { - fn from(id: u32) -> LaneIdWrapper { - LaneIdWrapper(LaneId(id.to_be_bytes())) +impl From for LaneIdWrapper { + fn from(id: BridgeLaneId) -> LaneIdWrapper { + LaneIdWrapper(LaneId::decode(&mut &id[..]).expect("decodable")) } } impl BridgeMessageHandler for BridgeHubMessageHandler where - S: Config, + S: BridgeMessagesConfig, SI: 'static, - T: Config, + T: BridgeMessagesConfig, TI: 'static, - >::InboundPayload: From>, - >::MessageDispatch: + >::InboundPayload: From>, + >::MessageDispatch: MessageDispatch, { fn get_source_outbound_messages() -> Vec { // get the source active outbound lanes - let active_lanes = S::ActiveOutboundLanes::get(); + let active_outbound_lanes = OutboundLanes::::iter_keys(); let mut messages: Vec = Default::default(); // collect messages from `OutboundMessages` for each active outbound lane in the source - for lane in active_lanes { - let latest_generated_nonce = OutboundLanes::::get(lane).latest_generated_nonce; - let latest_received_nonce = OutboundLanes::::get(lane).latest_received_nonce; + for lane in active_outbound_lanes { + let latest_generated_nonce = + OutboundLanes::::get(lane).unwrap().latest_generated_nonce; + let latest_received_nonce = + OutboundLanes::::get(lane).unwrap().latest_received_nonce; (latest_received_nonce + 1..=latest_generated_nonce).for_each(|nonce| { - let encoded_payload: Vec = Pallet::::outbound_message_data(*lane, nonce) + let encoded_payload: Vec = Pallet::::outbound_message_data(lane, nonce) .expect("Bridge message does not exist") .into(); let payload = Vec::::decode(&mut &encoded_payload[..]) .expect("Decoding XCM message failed"); - let id: u32 = LaneIdWrapper(*lane).into(); - let message = BridgeMessage { id, nonce, payload }; + let message = BridgeMessage { lane_id: LaneIdWrapper(lane).into(), nonce, payload }; messages.push(message); }); @@ -125,10 +127,10 @@ where fn dispatch_target_inbound_message( message: BridgeMessage, ) -> Result<(), BridgeMessageDispatchError> { - type TargetMessageDispatch = >::MessageDispatch; - type InboundPayload = >::InboundPayload; + type TargetMessageDispatch = >::MessageDispatch; + type InboundPayload = >::InboundPayload; - let lane_id = LaneIdWrapper::from(message.id).0; + let lane_id = LaneIdWrapper::from(message.lane_id).0; let nonce = message.nonce; let payload = Ok(From::from(message.payload)); @@ -151,15 +153,16 @@ where result } - fn notify_source_message_delivery(lane_id: u32) { - let data = OutboundLanes::::get(LaneIdWrapper::from(lane_id).0); + fn notify_source_message_delivery(lane_id: BridgeLaneId) { + let lane_id: LaneIdOf = LaneIdWrapper::from(lane_id).0; + let data = OutboundLanes::::get(lane_id).unwrap(); let new_data = OutboundLaneData { oldest_unpruned_nonce: data.oldest_unpruned_nonce + 1, latest_received_nonce: data.latest_received_nonce + 1, ..data }; - OutboundLanes::::insert(LaneIdWrapper::from(lane_id).0, new_data); + OutboundLanes::::insert(lane_id, new_data); } } @@ -925,3 +928,49 @@ macro_rules! impl_xcm_helpers_for_parachain { } } } + +#[macro_export] +macro_rules! impl_bridge_helpers_for_chain { + ( $chain:ident, $pallet:ident, $pallet_xcm:ident, $runtime_call_wrapper:path ) => { + $crate::impls::paste::paste! { + impl $chain { + /// Open bridge with `dest`. + pub fn open_bridge( + bridge_location: $crate::impls::Location, + bridge_destination_universal_location: $crate::impls::InteriorLocation, + maybe_paid: Option<($crate::impls::Asset, $crate::impls::AccountId)> + ) { + ::execute_with(|| { + use $crate::impls::{bx, Chain}; + use $crate::impls::XcmBridgeHubCall; + use $crate::impls::Encode; + + // important to use `root` and `OriginKind::Xcm` + let root_origin = ::RuntimeOrigin::root(); + + // construct call + let call: $crate::impls::DoubleEncoded<()> = $runtime_call_wrapper(XcmBridgeHubCall::open_bridge { + bridge_destination_universal_location: bx!( + bridge_destination_universal_location.clone().into() + ) + }).encode().into(); + + let xcm = if let Some((fee_asset, beneficiary)) = maybe_paid { + $crate::impls::xcm_transact_paid_execution(call, $crate::impls::OriginKind::Xcm, fee_asset, beneficiary) + } else { + $crate::impls::xcm_transact_unpaid_execution(call, $crate::impls::OriginKind::Xcm) + }; + + // Send XCM `Transact` with `open_bridge` call + $crate::impls::assert_ok!(]>::$pallet_xcm::send( + root_origin, + bx!(bridge_location.into()), + bx!(xcm), + )); + Self::assert_xcm_pallet_sent(); + }); + } + } + } + } +} diff --git a/cumulus/parachains/integration-tests/emulated/common/src/lib.rs b/cumulus/parachains/integration-tests/emulated/common/src/lib.rs index 30e66ced1fb08f640d912a1c08c3354090b67db4..c6b8889730e51da2c5ec6891d7005c0dbfe3d31e 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/lib.rs @@ -60,6 +60,7 @@ pub const TELEPORTABLE_ASSET_ID: u32 = 2; pub const USDT_ID: u32 = 1984; pub const PENPAL_ID: u32 = 2000; +pub const PENPAL_B_ID: u32 = 2001; pub const ASSETS_PALLET_ID: u8 = 50; parameter_types! { @@ -71,6 +72,14 @@ parameter_types! { ] ); pub PenpalSiblingSovereignAccount: AccountId = Sibling::from(PENPAL_ID).into_account_truncating(); + pub PenpalBTeleportableAssetLocation: xcm::v4::Location + = xcm::v4::Location::new(1, [ + xcm::v4::Junction::Parachain(PENPAL_B_ID), + xcm::v4::Junction::PalletInstance(ASSETS_PALLET_ID), + xcm::v4::Junction::GeneralIndex(TELEPORTABLE_ASSET_ID.into()), + ] + ); + pub PenpalBSiblingSovereignAccount: AccountId = Sibling::from(PENPAL_B_ID).into_account_truncating(); } /// Helper function to generate a crypto pair from seed diff --git a/cumulus/parachains/integration-tests/emulated/common/src/macros.rs b/cumulus/parachains/integration-tests/emulated/common/src/macros.rs index 578bca84ce5a54df1973803888e9d9d8d1cfe511..68926b04bfe6d8c86396ede5dce0dbd8d8b4396b 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/macros.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/macros.rs @@ -403,3 +403,51 @@ macro_rules! test_chain_can_claim_assets { } }; } + +#[macro_export] +macro_rules! test_dry_run_transfer_across_pk_bridge { + ( $sender_asset_hub:ty, $sender_bridge_hub:ty, $destination:expr ) => { + $crate::macros::paste::paste! { + use frame_support::{dispatch::RawOrigin, traits::fungible}; + use sp_runtime::AccountId32; + use xcm::prelude::*; + use xcm_runtime_apis::dry_run::runtime_decl_for_dry_run_api::DryRunApiV1; + + let who = AccountId32::new([1u8; 32]); + let transfer_amount = 10_000_000_000_000u128; + let initial_balance = transfer_amount * 10; + + // Bridge setup. + $sender_asset_hub::force_xcm_version($destination, XCM_VERSION); + open_bridge_between_asset_hub_rococo_and_asset_hub_westend(); + + <$sender_asset_hub as TestExt>::execute_with(|| { + type Runtime = <$sender_asset_hub as Chain>::Runtime; + type RuntimeCall = <$sender_asset_hub as Chain>::RuntimeCall; + type OriginCaller = <$sender_asset_hub as Chain>::OriginCaller; + type Balances = <$sender_asset_hub as [<$sender_asset_hub Pallet>]>::Balances; + + // Give some initial funds. + >::set_balance(&who, initial_balance); + + let call = RuntimeCall::PolkadotXcm(pallet_xcm::Call::transfer_assets { + dest: Box::new(VersionedLocation::from($destination)), + beneficiary: Box::new(VersionedLocation::from(Junction::AccountId32 { + id: who.clone().into(), + network: None, + })), + assets: Box::new(VersionedAssets::from(vec![ + (Parent, transfer_amount).into(), + ])), + fee_asset_item: 0, + weight_limit: Unlimited, + }); + let result = Runtime::dry_run_call(OriginCaller::system(RawOrigin::Signed(who)), call).unwrap(); + // We assert the dry run succeeds and sends only one message to the local bridge hub. + assert!(result.execution_result.is_ok()); + assert_eq!(result.forwarded_xcms.len(), 1); + assert_eq!(result.forwarded_xcms[0].0, VersionedLocation::from(Location::new(1, [Parachain($sender_bridge_hub::para_id().into())]))); + }); + } + }; +} 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 87a090bf1ae68cb79f43b4c06a8871b7ccbdf865..0e43108a417be542b1612a9d22f6d644cbde0a1c 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,6 +51,7 @@ mod imports { pub use rococo_system_emulated_network::{ asset_hub_rococo_emulated_chain::{ asset_hub_rococo_runtime::{ + self, xcm_config::{ self as ahr_xcm_config, TokenLocation as RelayLocation, XcmConfig as AssetHubRococoXcmConfig, @@ -66,6 +67,7 @@ mod imports { CustomizableAssetFromSystemAssetHub as PenpalCustomizableAssetFromSystemAssetHub, LocalReservableFromAssetHub as PenpalLocalReservableFromAssetHub, LocalTeleportableToAssetHub as PenpalLocalTeleportableToAssetHub, + UsdtFromAssetHub as PenpalUsdtFromAssetHub, }, PenpalAParaPallet as PenpalAPallet, PenpalAssetOwner, PenpalBParaPallet as PenpalBPallet, ED as PENPAL_ED, diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/hybrid_transfers.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/hybrid_transfers.rs index 7ff6d6c193c9b414632b13e267220a59c770449a..7bb25d7cec623641005402442f98642be60a2b57 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/hybrid_transfers.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/hybrid_transfers.rs @@ -449,7 +449,16 @@ fn transfer_foreign_assets_from_para_to_para_through_asset_hub() { let sov_of_receiver_on_ah = AssetHubRococo::sovereign_account_id_of(receiver_as_seen_by_ah); let wnd_to_send = ASSET_HUB_ROCOCO_ED * 10_000_000; - // Configure destination chain to trust AH as reserve of WND + // Configure source and destination chains to trust AH as reserve of WND + PenpalA::execute_with(|| { + assert_ok!(::System::set_storage( + ::RuntimeOrigin::root(), + vec![( + PenpalCustomizableAssetFromSystemAssetHub::key().to_vec(), + Location::new(2, [GlobalConsensus(Westend)]).encode(), + )], + )); + }); PenpalB::execute_with(|| { assert_ok!(::System::set_storage( ::RuntimeOrigin::root(), 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 70dde03d75a27ed300f1dbaa18a9af6536df2c13..8aad4b392b2c2ab22a0603f6c2e434f3a92a9af9 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs @@ -60,10 +60,10 @@ pub fn system_para_to_para_sender_assertions(t: SystemParaToParaTest) { AssetHubRococo::assert_xcm_pallet_attempted_complete(None); let sov_acc_of_dest = AssetHubRococo::sovereign_account_id_of(t.args.dest.clone()); - for (idx, asset) in t.args.assets.into_inner().into_iter().enumerate() { + for asset in t.args.assets.into_inner().into_iter() { let expected_id = asset.id.0.clone().try_into().unwrap(); let asset_amount = if let Fungible(a) = asset.fun { Some(a) } else { None }.unwrap(); - if idx == t.args.fee_asset_item as usize { + if asset.id == AssetId(Location::new(1, [])) { assert_expected_events!( AssetHubRococo, vec![ @@ -77,6 +77,23 @@ pub fn system_para_to_para_sender_assertions(t: SystemParaToParaTest) { }, ] ); + } else if matches!( + asset.id.0.unpack(), + (0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(_)]) + ) { + assert_expected_events!( + AssetHubRococo, + vec![ + // Amount of trust-backed asset is transferred to Parachain's Sovereign account + RuntimeEvent::Assets( + pallet_assets::Event::Transferred { from, to, amount, .. }, + ) => { + from: *from == t.sender.account_id, + to: *to == sov_acc_of_dest, + amount: *amount == asset_amount, + }, + ] + ); } else { assert_expected_events!( AssetHubRococo, @@ -388,6 +405,38 @@ pub fn para_to_para_through_hop_sender_assertions(t: Test::RuntimeEvent; + let sov_penpal_a_on_ah = AssetHubRococo::sovereign_account_id_of( + AssetHubRococo::sibling_location_of(PenpalA::para_id()), + ); + let sov_penpal_b_on_ah = AssetHubRococo::sovereign_account_id_of( + AssetHubRococo::sibling_location_of(PenpalB::para_id()), + ); + + assert_expected_events!( + AssetHubRococo, + vec![ + // Withdrawn from sender parachain SA + RuntimeEvent::Assets( + pallet_assets::Event::Burned { owner, balance, .. } + ) => { + owner: *owner == sov_penpal_a_on_ah, + balance: *balance == t.args.amount, + }, + // Deposited to receiver parachain SA + RuntimeEvent::Assets( + pallet_assets::Event::Deposited { who, .. } + ) => { + who: *who == sov_penpal_b_on_ah, + }, + RuntimeEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, + ] + ); +} + fn para_to_para_relay_hop_assertions(t: ParaToParaThroughRelayTest) { type RuntimeEvent = ::RuntimeEvent; let sov_penpal_a_on_rococo = @@ -469,6 +518,19 @@ fn system_para_to_para_reserve_transfer_assets(t: SystemParaToParaTest) -> Dispa ) } +fn para_to_para_through_asset_hub_limited_reserve_transfer_assets( + t: ParaToParaThroughAHTest, +) -> DispatchResult { + ::PolkadotXcm::limited_reserve_transfer_assets( + t.signed_origin, + bx!(t.args.dest.into()), + bx!(t.args.beneficiary.into()), + bx!(t.args.assets.into()), + t.args.fee_asset_item, + t.args.weight_limit, + ) +} + fn para_to_system_para_reserve_transfer_assets(t: ParaToSystemParaTest) -> DispatchResult { ::PolkadotXcm::limited_reserve_transfer_assets( t.signed_origin, @@ -1136,3 +1198,408 @@ fn reserve_transfer_native_asset_from_para_to_para_through_relay() { // Receiver's balance is increased assert!(receiver_assets_after > receiver_assets_before); } + +// ============================================================================ +// ==== Reserve Transfers USDT - AssetHub->Parachain - pay fees using pool ==== +// ============================================================================ +#[test] +fn reserve_transfer_usdt_from_asset_hub_to_para() { + let usdt_id = 1984u32; + let penpal_location = AssetHubRococo::sibling_location_of(PenpalA::para_id()); + let penpal_sov_account = AssetHubRococo::sovereign_account_id_of(penpal_location.clone()); + + // Create SA-of-Penpal-on-AHW with ED. + // This ED isn't reflected in any derivative in a PenpalA account. + AssetHubRococo::fund_accounts(vec![(penpal_sov_account.clone().into(), ASSET_HUB_ROCOCO_ED)]); + + let sender = AssetHubRococoSender::get(); + let receiver = PenpalAReceiver::get(); + let asset_amount_to_send = 1_000_000_000_000; + + AssetHubRococo::execute_with(|| { + use frame_support::traits::tokens::fungibles::Mutate; + type Assets = ::Assets; + assert_ok!(>::mint_into( + usdt_id.into(), + &AssetHubRococoSender::get(), + asset_amount_to_send + 10_000_000_000_000, // Make sure it has enough. + )); + }); + + let relay_asset_penpal_pov = RelayLocation::get(); + + let usdt_from_asset_hub = PenpalUsdtFromAssetHub::get(); + + // Setup the pool between `relay_asset_penpal_pov` and `usdt_from_asset_hub` on PenpalA. + // So we can swap the custom asset that comes from AssetHubRococo for native asset to pay for + // fees. + PenpalA::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_ok!(::ForeignAssets::mint( + ::RuntimeOrigin::signed(PenpalAssetOwner::get()), + usdt_from_asset_hub.clone().into(), + PenpalASender::get().into(), + 10_000_000_000_000, // For it to have more than enough. + )); + + assert_ok!(::AssetConversion::create_pool( + ::RuntimeOrigin::signed(PenpalASender::get()), + Box::new(relay_asset_penpal_pov.clone()), + Box::new(usdt_from_asset_hub.clone()), + )); + + assert_expected_events!( + PenpalA, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::PoolCreated { .. }) => {}, + ] + ); + + assert_ok!(::AssetConversion::add_liquidity( + ::RuntimeOrigin::signed(PenpalASender::get()), + Box::new(relay_asset_penpal_pov), + Box::new(usdt_from_asset_hub.clone()), + // `usdt_from_asset_hub` is worth a third of `relay_asset_penpal_pov` + 1_000_000_000_000, + 3_000_000_000_000, + 0, + 0, + PenpalASender::get().into() + )); + + assert_expected_events!( + PenpalA, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::LiquidityAdded { .. }) => {}, + ] + ); + }); + + let assets: Assets = vec![( + [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(usdt_id.into())], + asset_amount_to_send, + ) + .into()] + .into(); + + let test_args = TestContext { + sender: sender.clone(), + receiver: receiver.clone(), + args: TestArgs::new_para( + penpal_location, + receiver.clone(), + asset_amount_to_send, + assets, + None, + 0, + ), + }; + let mut test = SystemParaToParaTest::new(test_args); + + let sender_initial_balance = AssetHubRococo::execute_with(|| { + type Assets = ::Assets; + >::balance(usdt_id, &sender) + }); + let sender_initial_native_balance = AssetHubRococo::execute_with(|| { + type Balances = ::Balances; + Balances::free_balance(&sender) + }); + let receiver_initial_balance = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub.clone(), &receiver) + }); + + test.set_assertion::(system_para_to_para_sender_assertions); + test.set_assertion::(system_para_to_para_receiver_assertions); + test.set_dispatchable::(system_para_to_para_reserve_transfer_assets); + test.assert(); + + let sender_after_balance = AssetHubRococo::execute_with(|| { + type Assets = ::Assets; + >::balance(usdt_id, &sender) + }); + let sender_after_native_balance = AssetHubRococo::execute_with(|| { + type Balances = ::Balances; + Balances::free_balance(&sender) + }); + let receiver_after_balance = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub, &receiver) + }); + + // TODO(https://github.com/paritytech/polkadot-sdk/issues/5160): When we allow payment with different assets locally, this should be the same, since + // they aren't used for fees. + assert!(sender_after_native_balance < sender_initial_native_balance); + // Sender account's balance decreases. + assert_eq!(sender_after_balance, sender_initial_balance - asset_amount_to_send); + // Receiver account's balance increases. + assert!(receiver_after_balance > receiver_initial_balance); + assert!(receiver_after_balance < receiver_initial_balance + asset_amount_to_send); +} + +// =================================================================================== +// == Reserve Transfers USDT - Parachain->AssetHub->Parachain - pay fees using pool == +// =================================================================================== +// +// Transfer USDT From Penpal A to Penpal B with AssetHub as the reserve, while paying fees using +// USDT by making use of existing USDT pools on AssetHub and destination. +#[test] +fn reserve_transfer_usdt_from_para_to_para_through_asset_hub() { + let destination = PenpalA::sibling_location_of(PenpalB::para_id()); + let sender = PenpalASender::get(); + let asset_amount_to_send: Balance = ROCOCO_ED * 10000; + let fee_amount_to_send: Balance = ROCOCO_ED * 10000; + let sender_chain_as_seen_by_asset_hub = AssetHubRococo::sibling_location_of(PenpalA::para_id()); + let sov_of_sender_on_asset_hub = + AssetHubRococo::sovereign_account_id_of(sender_chain_as_seen_by_asset_hub); + let receiver_as_seen_by_asset_hub = AssetHubRococo::sibling_location_of(PenpalB::para_id()); + let sov_of_receiver_on_asset_hub = + AssetHubRococo::sovereign_account_id_of(receiver_as_seen_by_asset_hub); + + // Create SA-of-Penpal-on-AHW with ED. + // This ED isn't reflected in any derivative in a PenpalA account. + AssetHubRococo::fund_accounts(vec![ + (sov_of_sender_on_asset_hub.clone().into(), ASSET_HUB_ROCOCO_ED), + (sov_of_receiver_on_asset_hub.clone().into(), ASSET_HUB_ROCOCO_ED), + ]); + + // Give USDT to sov account of sender. + let usdt_id = 1984; + AssetHubRococo::execute_with(|| { + use frame_support::traits::tokens::fungibles::Mutate; + type Assets = ::Assets; + assert_ok!(>::mint_into( + usdt_id.into(), + &sov_of_sender_on_asset_hub.clone().into(), + asset_amount_to_send + fee_amount_to_send, + )); + }); + + // We create a pool between WND and USDT in AssetHub. + let native_asset: Location = Parent.into(); + let usdt = Location::new( + 0, + [Junction::PalletInstance(ASSETS_PALLET_ID), Junction::GeneralIndex(usdt_id.into())], + ); + + // set up pool with USDT <> native pair + AssetHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_ok!(::Assets::mint( + ::RuntimeOrigin::signed(AssetHubRococoSender::get()), + usdt_id.into(), + AssetHubRococoSender::get().into(), + 10_000_000_000_000, // For it to have more than enough. + )); + + assert_ok!(::AssetConversion::create_pool( + ::RuntimeOrigin::signed(AssetHubRococoSender::get()), + Box::new(native_asset.clone()), + Box::new(usdt.clone()), + )); + + assert_expected_events!( + AssetHubRococo, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::PoolCreated { .. }) => {}, + ] + ); + + assert_ok!(::AssetConversion::add_liquidity( + ::RuntimeOrigin::signed(AssetHubRococoSender::get()), + Box::new(native_asset), + Box::new(usdt), + 1_000_000_000_000, + 2_000_000_000_000, // usdt is worth half of `native_asset` + 0, + 0, + AssetHubRococoSender::get().into() + )); + + assert_expected_events!( + AssetHubRococo, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::LiquidityAdded { .. }) => {}, + ] + ); + }); + + let usdt_from_asset_hub = PenpalUsdtFromAssetHub::get(); + + // We also need a pool between WND and USDT on PenpalB. + PenpalB::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + let relay_asset = RelayLocation::get(); + + assert_ok!(::ForeignAssets::mint( + ::RuntimeOrigin::signed(PenpalAssetOwner::get()), + usdt_from_asset_hub.clone().into(), + PenpalBReceiver::get().into(), + 10_000_000_000_000, // For it to have more than enough. + )); + + assert_ok!(::AssetConversion::create_pool( + ::RuntimeOrigin::signed(PenpalBReceiver::get()), + Box::new(relay_asset.clone()), + Box::new(usdt_from_asset_hub.clone()), + )); + + assert_expected_events!( + PenpalB, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::PoolCreated { .. }) => {}, + ] + ); + + assert_ok!(::AssetConversion::add_liquidity( + ::RuntimeOrigin::signed(PenpalBReceiver::get()), + Box::new(relay_asset), + Box::new(usdt_from_asset_hub.clone()), + 1_000_000_000_000, + 2_000_000_000_000, // `usdt_from_asset_hub` is worth half of `relay_asset` + 0, + 0, + PenpalBReceiver::get().into() + )); + + assert_expected_events!( + PenpalB, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::LiquidityAdded { .. }) => {}, + ] + ); + }); + + PenpalA::execute_with(|| { + use frame_support::traits::tokens::fungibles::Mutate; + type ForeignAssets = ::ForeignAssets; + assert_ok!(>::mint_into( + usdt_from_asset_hub.clone(), + &sender, + asset_amount_to_send + fee_amount_to_send, + )); + }); + + // Prepare assets to transfer. + let assets: Assets = + (usdt_from_asset_hub.clone(), asset_amount_to_send + fee_amount_to_send).into(); + // Just to be very specific we're not including anything other than USDT. + assert_eq!(assets.len(), 1); + + // Give the sender enough Relay tokens to pay for local delivery fees. + // TODO(https://github.com/paritytech/polkadot-sdk/issues/5160): When we support local delivery fee payment in other assets, we don't need this. + PenpalA::mint_foreign_asset( + ::RuntimeOrigin::signed(PenpalAssetOwner::get()), + RelayLocation::get(), + sender.clone(), + 10_000_000_000_000, // Large estimate to make sure it works. + ); + + // Init values for Parachain Destination + let receiver = PenpalBReceiver::get(); + + // Init Test + let fee_asset_index = 0; + let test_args = TestContext { + sender: sender.clone(), + receiver: receiver.clone(), + args: TestArgs::new_para( + destination, + receiver.clone(), + asset_amount_to_send, + assets, + None, + fee_asset_index, + ), + }; + let mut test = ParaToParaThroughAHTest::new(test_args); + + // Query initial balances + let sender_assets_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub.clone(), &sender) + }); + let receiver_assets_before = PenpalB::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub.clone(), &receiver) + }); + test.set_assertion::(para_to_para_through_hop_sender_assertions); + test.set_assertion::(para_to_para_asset_hub_hop_assertions); + test.set_assertion::(para_to_para_through_hop_receiver_assertions); + test.set_dispatchable::( + para_to_para_through_asset_hub_limited_reserve_transfer_assets, + ); + test.assert(); + + // Query final balances + let sender_assets_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub.clone(), &sender) + }); + let receiver_assets_after = PenpalB::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub, &receiver) + }); + + // Sender's balance is reduced by amount + assert!(sender_assets_after < sender_assets_before - asset_amount_to_send); + // Receiver's balance is increased + assert!(receiver_assets_after > receiver_assets_before); +} + +/// Reserve Withdraw Native Asset from AssetHub to Parachain fails. +#[test] +fn reserve_withdraw_from_untrusted_reserve_fails() { + // Init values for Parachain Origin + let destination = AssetHubRococo::sibling_location_of(PenpalA::para_id()); + let signed_origin = + ::RuntimeOrigin::signed(AssetHubRococoSender::get().into()); + let roc_to_send: Balance = ROCOCO_ED * 10000; + let roc_location = RelayLocation::get(); + + // Assets to send + let assets: Vec = vec![(roc_location.clone(), roc_to_send).into()]; + let fee_id: AssetId = roc_location.into(); + + // this should fail + AssetHubRococo::execute_with(|| { + let result = ::PolkadotXcm::transfer_assets_using_type_and_then( + signed_origin.clone(), + bx!(destination.clone().into()), + bx!(assets.clone().into()), + bx!(TransferType::DestinationReserve), + bx!(fee_id.into()), + bx!(TransferType::DestinationReserve), + bx!(VersionedXcm::from(Xcm::<()>::new())), + Unlimited, + ); + assert_err!( + result, + DispatchError::Module(sp_runtime::ModuleError { + index: 31, + error: [22, 0, 0, 0], + message: Some("InvalidAssetUnsupportedReserve") + }) + ); + }); + + // this should also fail + AssetHubRococo::execute_with(|| { + let xcm: Xcm = Xcm(vec![ + WithdrawAsset(assets.into()), + InitiateReserveWithdraw { + assets: Wild(All), + reserve: destination, + xcm: Xcm::<()>::new(), + }, + ]); + let result = ::PolkadotXcm::execute( + signed_origin, + bx!(xcm::VersionedXcm::V4(xcm)), + Weight::MAX, + ); + assert!(result.is_err()); + }); +} 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 c8da801a14bff5adea1a695313672dfb9c9d8437..470b4d0f389e8cfd49dfd9fb930cfd6898ec9fd0 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 @@ -265,7 +265,9 @@ fn limited_teleport_native_assets_from_system_para_to_relay_fails() { let delivery_fees = AssetHubRococo::execute_with(|| { xcm_helpers::teleport_assets_delivery_fees::< ::XcmSender, - >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) + >( + test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest + ) }); // Sender's balance is reduced @@ -527,3 +529,54 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { system_para_to_para_transfer_assets, ); } + +/// Teleport Native Asset from AssetHub to Parachain fails. +#[test] +fn teleport_to_untrusted_chain_fails() { + // Init values for Parachain Origin + let destination = AssetHubRococo::sibling_location_of(PenpalA::para_id()); + let signed_origin = + ::RuntimeOrigin::signed(AssetHubRococoSender::get().into()); + let roc_to_send: Balance = ROCOCO_ED * 10000; + let roc_location = RelayLocation::get(); + + // Assets to send + let assets: Vec = vec![(roc_location.clone(), roc_to_send).into()]; + let fee_id: AssetId = roc_location.into(); + + // this should fail + AssetHubRococo::execute_with(|| { + let result = ::PolkadotXcm::transfer_assets_using_type_and_then( + signed_origin.clone(), + bx!(destination.clone().into()), + bx!(assets.clone().into()), + bx!(TransferType::Teleport), + bx!(fee_id.into()), + bx!(TransferType::Teleport), + bx!(VersionedXcm::from(Xcm::<()>::new())), + Unlimited, + ); + assert_err!( + result, + DispatchError::Module(sp_runtime::ModuleError { + index: 31, + error: [2, 0, 0, 0], + message: Some("Filtered") + }) + ); + }); + + // this should also fail + AssetHubRococo::execute_with(|| { + let xcm: Xcm = Xcm(vec![ + WithdrawAsset(assets.into()), + InitiateTeleport { assets: Wild(All), dest: destination, xcm: Xcm::<()>::new() }, + ]); + let result = ::PolkadotXcm::execute( + signed_origin, + bx!(xcm::VersionedXcm::V4(xcm)), + Weight::MAX, + ); + assert!(result.is_err()); + }); +} 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 a887ee6a532a68eed9914d0df80d32c1ead761a7..d0016b5a1b158ea83adc3ec8d25be8d6de1b0bbb 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 @@ -48,6 +48,7 @@ mod imports { pub use westend_system_emulated_network::{ asset_hub_westend_emulated_chain::{ asset_hub_westend_runtime::{ + self, xcm_config::{ self as ahw_xcm_config, WestendLocation as RelayLocation, XcmConfig as AssetHubWestendXcmConfig, @@ -64,6 +65,7 @@ mod imports { CustomizableAssetFromSystemAssetHub as PenpalCustomizableAssetFromSystemAssetHub, LocalReservableFromAssetHub as PenpalLocalReservableFromAssetHub, LocalTeleportableToAssetHub as PenpalLocalTeleportableToAssetHub, + UsdtFromAssetHub as PenpalUsdtFromAssetHub, }, PenpalAParaPallet as PenpalAPallet, PenpalAssetOwner, PenpalBParaPallet as PenpalBPallet, diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/hybrid_transfers.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/hybrid_transfers.rs index 49dfe8d58394c9ae7b69fba75e8147e7fa94e2db..4d6cdd9a94d6b2b37d74c1586dd66986e84fa13a 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/hybrid_transfers.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/hybrid_transfers.rs @@ -450,7 +450,16 @@ fn transfer_foreign_assets_from_para_to_para_through_asset_hub() { let sov_of_receiver_on_ah = AssetHubWestend::sovereign_account_id_of(receiver_as_seen_by_ah); let roc_to_send = ASSET_HUB_WESTEND_ED * 10_000_000; - // Configure destination chain to trust AH as reserve of ROC + // Configure source and destination chains to trust AH as reserve of ROC + PenpalA::execute_with(|| { + assert_ok!(::System::set_storage( + ::RuntimeOrigin::root(), + vec![( + PenpalCustomizableAssetFromSystemAssetHub::key().to_vec(), + Location::new(2, [GlobalConsensus(Rococo)]).encode(), + )], + )); + }); PenpalB::execute_with(|| { assert_ok!(::System::set_storage( ::RuntimeOrigin::root(), @@ -613,10 +622,10 @@ fn transfer_foreign_assets_from_para_to_para_through_asset_hub() { >::balance(roc_at_westend_parachains, &receiver) }); - // Sender's balance is reduced by amount sent plus delivery fees + // Sender's balance is reduced by amount sent. assert!(sender_wnds_after < sender_wnds_before - wnd_to_send); assert_eq!(sender_rocs_after, sender_rocs_before - roc_to_send); - // Sovereign accounts on reserve are changed accordingly + // Sovereign accounts on reserve are changed accordingly. assert_eq!( wnds_in_sender_reserve_on_ah_after, wnds_in_sender_reserve_on_ah_before - wnd_to_send @@ -630,7 +639,7 @@ fn transfer_foreign_assets_from_para_to_para_through_asset_hub() { rocs_in_receiver_reserve_on_ah_after, rocs_in_receiver_reserve_on_ah_before + roc_to_send ); - // Receiver's balance is increased + // Receiver's balance is increased by amount sent minus delivery fees. assert!(receiver_wnds_after > receiver_wnds_before); assert_eq!(receiver_rocs_after, receiver_rocs_before + roc_to_send); } diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs index 59f63d3805908d29e553b3d710e2843165edfe78..0100e8e34ef3f41c12d78ea95b9f8b7d3ddaa7fd 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs @@ -60,10 +60,10 @@ pub fn system_para_to_para_sender_assertions(t: SystemParaToParaTest) { AssetHubWestend::assert_xcm_pallet_attempted_complete(None); let sov_acc_of_dest = AssetHubWestend::sovereign_account_id_of(t.args.dest.clone()); - for (idx, asset) in t.args.assets.into_inner().into_iter().enumerate() { + for asset in t.args.assets.into_inner().into_iter() { let expected_id = asset.id.0.clone().try_into().unwrap(); let asset_amount = if let Fungible(a) = asset.fun { Some(a) } else { None }.unwrap(); - if idx == t.args.fee_asset_item as usize { + if asset.id == AssetId(Location::new(1, [])) { assert_expected_events!( AssetHubWestend, vec![ @@ -77,6 +77,23 @@ pub fn system_para_to_para_sender_assertions(t: SystemParaToParaTest) { }, ] ); + } else if matches!( + asset.id.0.unpack(), + (0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(_)]) + ) { + assert_expected_events!( + AssetHubWestend, + vec![ + // Amount of trust-backed asset is transferred to Parachain's Sovereign account + RuntimeEvent::Assets( + pallet_assets::Event::Transferred { from, to, amount, .. }, + ) => { + from: *from == t.sender.account_id, + to: *to == sov_acc_of_dest, + amount: *amount == asset_amount, + }, + ] + ); } else { assert_expected_events!( AssetHubWestend, @@ -418,6 +435,38 @@ fn para_to_para_relay_hop_assertions(t: ParaToParaThroughRelayTest) { ); } +fn para_to_para_asset_hub_hop_assertions(t: ParaToParaThroughAHTest) { + type RuntimeEvent = ::RuntimeEvent; + let sov_penpal_a_on_ah = AssetHubWestend::sovereign_account_id_of( + AssetHubWestend::sibling_location_of(PenpalA::para_id()), + ); + let sov_penpal_b_on_ah = AssetHubWestend::sovereign_account_id_of( + AssetHubWestend::sibling_location_of(PenpalB::para_id()), + ); + + assert_expected_events!( + AssetHubWestend, + vec![ + // Withdrawn from sender parachain SA + RuntimeEvent::Assets( + pallet_assets::Event::Burned { owner, balance, .. } + ) => { + owner: *owner == sov_penpal_a_on_ah, + balance: *balance == t.args.amount, + }, + // Deposited to receiver parachain SA + RuntimeEvent::Assets( + pallet_assets::Event::Deposited { who, .. } + ) => { + who: *who == sov_penpal_b_on_ah, + }, + RuntimeEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, + ] + ); +} + pub fn para_to_para_through_hop_receiver_assertions(t: Test) { type RuntimeEvent = ::RuntimeEvent; @@ -493,6 +542,19 @@ fn para_to_para_through_relay_limited_reserve_transfer_assets( ) } +fn para_to_para_through_asset_hub_limited_reserve_transfer_assets( + t: ParaToParaThroughAHTest, +) -> DispatchResult { + ::PolkadotXcm::limited_reserve_transfer_assets( + t.signed_origin, + bx!(t.args.dest.into()), + bx!(t.args.beneficiary.into()), + bx!(t.args.assets.into()), + t.args.fee_asset_item, + t.args.weight_limit, + ) +} + /// Reserve Transfers of native asset from Relay Chain to the Asset Hub shouldn't work #[test] fn reserve_transfer_native_asset_from_relay_to_asset_hub_fails() { @@ -1133,8 +1195,415 @@ fn reserve_transfer_native_asset_from_para_to_para_through_relay() { >::balance(relay_native_asset_location, &receiver) }); - // Sender's balance is reduced by amount sent plus delivery fees + // Sender's balance is reduced by amount sent plus delivery fees. assert!(sender_assets_after < sender_assets_before - amount_to_send); + // Receiver's balance is increased by `amount_to_send` minus delivery fees. + assert!(receiver_assets_after > receiver_assets_before); + assert!(receiver_assets_after < receiver_assets_before + amount_to_send); +} + +// ============================================================================ +// ==== Reserve Transfers USDT - AssetHub->Parachain - pay fees using pool ==== +// ============================================================================ +#[test] +fn reserve_transfer_usdt_from_asset_hub_to_para() { + let usdt_id = 1984u32; + let penpal_location = AssetHubWestend::sibling_location_of(PenpalA::para_id()); + let penpal_sov_account = AssetHubWestend::sovereign_account_id_of(penpal_location.clone()); + + // Create SA-of-Penpal-on-AHW with ED. + // This ED isn't reflected in any derivative in a PenpalA account. + AssetHubWestend::fund_accounts(vec![(penpal_sov_account.clone().into(), ASSET_HUB_WESTEND_ED)]); + + let sender = AssetHubWestendSender::get(); + let receiver = PenpalAReceiver::get(); + let asset_amount_to_send = 1_000_000_000_000; + + AssetHubWestend::execute_with(|| { + use frame_support::traits::tokens::fungibles::Mutate; + type Assets = ::Assets; + assert_ok!(>::mint_into( + usdt_id.into(), + &AssetHubWestendSender::get(), + asset_amount_to_send + 10_000_000_000_000, // Make sure it has enough. + )); + }); + + let relay_asset_penpal_pov = RelayLocation::get(); + + let usdt_from_asset_hub = PenpalUsdtFromAssetHub::get(); + + // Setup the pool between `relay_asset_penpal_pov` and `usdt_from_asset_hub` on PenpalA. + // So we can swap the custom asset that comes from AssetHubWestend for native asset to pay for + // fees. + PenpalA::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_ok!(::ForeignAssets::mint( + ::RuntimeOrigin::signed(PenpalAssetOwner::get()), + usdt_from_asset_hub.clone().into(), + PenpalASender::get().into(), + 10_000_000_000_000, // For it to have more than enough. + )); + + assert_ok!(::AssetConversion::create_pool( + ::RuntimeOrigin::signed(PenpalASender::get()), + Box::new(relay_asset_penpal_pov.clone()), + Box::new(usdt_from_asset_hub.clone()), + )); + + assert_expected_events!( + PenpalA, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::PoolCreated { .. }) => {}, + ] + ); + + assert_ok!(::AssetConversion::add_liquidity( + ::RuntimeOrigin::signed(PenpalASender::get()), + Box::new(relay_asset_penpal_pov), + Box::new(usdt_from_asset_hub.clone()), + // `usdt_from_asset_hub` is worth a third of `relay_asset_penpal_pov` + 1_000_000_000_000, + 3_000_000_000_000, + 0, + 0, + PenpalASender::get().into() + )); + + assert_expected_events!( + PenpalA, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::LiquidityAdded { .. }) => {}, + ] + ); + }); + + let assets: Assets = vec![( + [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(usdt_id.into())], + asset_amount_to_send, + ) + .into()] + .into(); + + let test_args = TestContext { + sender: sender.clone(), + receiver: receiver.clone(), + args: TestArgs::new_para( + penpal_location, + receiver.clone(), + asset_amount_to_send, + assets, + None, + 0, + ), + }; + let mut test = SystemParaToParaTest::new(test_args); + + let sender_initial_balance = AssetHubWestend::execute_with(|| { + type Assets = ::Assets; + >::balance(usdt_id, &sender) + }); + let sender_initial_native_balance = AssetHubWestend::execute_with(|| { + type Balances = ::Balances; + Balances::free_balance(&sender) + }); + let receiver_initial_balance = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub.clone(), &receiver) + }); + + test.set_assertion::(system_para_to_para_sender_assertions); + test.set_assertion::(system_para_to_para_receiver_assertions); + test.set_dispatchable::(system_para_to_para_reserve_transfer_assets); + test.assert(); + + let sender_after_balance = AssetHubWestend::execute_with(|| { + type Assets = ::Assets; + >::balance(usdt_id, &sender) + }); + let sender_after_native_balance = AssetHubWestend::execute_with(|| { + type Balances = ::Balances; + Balances::free_balance(&sender) + }); + let receiver_after_balance = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub, &receiver) + }); + + // TODO(https://github.com/paritytech/polkadot-sdk/issues/5160): When we allow payment with different assets locally, this should be the same, since + // they aren't used for fees. + assert!(sender_after_native_balance < sender_initial_native_balance); + // Sender account's balance decreases. + assert_eq!(sender_after_balance, sender_initial_balance - asset_amount_to_send); + // Receiver account's balance increases. + assert!(receiver_after_balance > receiver_initial_balance); + assert!(receiver_after_balance < receiver_initial_balance + asset_amount_to_send); +} + +// =================================================================================== +// == Reserve Transfers USDT - Parachain->AssetHub->Parachain - pay fees using pool == +// =================================================================================== +// +// Transfer USDT From Penpal A to Penpal B with AssetHub as the reserve, while paying fees using +// USDT by making use of existing USDT pools on AssetHub and destination. +#[test] +fn reserve_transfer_usdt_from_para_to_para_through_asset_hub() { + let destination = PenpalA::sibling_location_of(PenpalB::para_id()); + let sender = PenpalASender::get(); + let asset_amount_to_send: Balance = WESTEND_ED * 10000; + let fee_amount_to_send: Balance = WESTEND_ED * 10000; + let sender_chain_as_seen_by_asset_hub = + AssetHubWestend::sibling_location_of(PenpalA::para_id()); + let sov_of_sender_on_asset_hub = + AssetHubWestend::sovereign_account_id_of(sender_chain_as_seen_by_asset_hub); + let receiver_as_seen_by_asset_hub = AssetHubWestend::sibling_location_of(PenpalB::para_id()); + let sov_of_receiver_on_asset_hub = + AssetHubWestend::sovereign_account_id_of(receiver_as_seen_by_asset_hub); + + // Create SA-of-Penpal-on-AHW with ED. + // This ED isn't reflected in any derivative in a PenpalA account. + AssetHubWestend::fund_accounts(vec![ + (sov_of_sender_on_asset_hub.clone().into(), ASSET_HUB_WESTEND_ED), + (sov_of_receiver_on_asset_hub.clone().into(), ASSET_HUB_WESTEND_ED), + ]); + + // Give USDT to sov account of sender. + let usdt_id = 1984; + AssetHubWestend::execute_with(|| { + use frame_support::traits::tokens::fungibles::Mutate; + type Assets = ::Assets; + assert_ok!(>::mint_into( + usdt_id.into(), + &sov_of_sender_on_asset_hub.clone().into(), + asset_amount_to_send + fee_amount_to_send, + )); + }); + + // We create a pool between WND and USDT in AssetHub. + let native_asset: Location = Parent.into(); + let usdt = Location::new( + 0, + [Junction::PalletInstance(ASSETS_PALLET_ID), Junction::GeneralIndex(usdt_id.into())], + ); + + // set up pool with USDT <> native pair + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_ok!(::Assets::mint( + ::RuntimeOrigin::signed(AssetHubWestendSender::get()), + usdt_id.into(), + AssetHubWestendSender::get().into(), + 10_000_000_000_000, // For it to have more than enough. + )); + + assert_ok!(::AssetConversion::create_pool( + ::RuntimeOrigin::signed(AssetHubWestendSender::get()), + Box::new(native_asset.clone()), + Box::new(usdt.clone()), + )); + + assert_expected_events!( + AssetHubWestend, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::PoolCreated { .. }) => {}, + ] + ); + + assert_ok!(::AssetConversion::add_liquidity( + ::RuntimeOrigin::signed(AssetHubWestendSender::get()), + Box::new(native_asset), + Box::new(usdt), + 1_000_000_000_000, + 2_000_000_000_000, // usdt is worth half of `native_asset` + 0, + 0, + AssetHubWestendSender::get().into() + )); + + assert_expected_events!( + AssetHubWestend, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::LiquidityAdded { .. }) => {}, + ] + ); + }); + + let usdt_from_asset_hub = PenpalUsdtFromAssetHub::get(); + + // We also need a pool between WND and USDT on PenpalB. + PenpalB::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + let relay_asset = RelayLocation::get(); + + assert_ok!(::ForeignAssets::mint( + ::RuntimeOrigin::signed(PenpalAssetOwner::get()), + usdt_from_asset_hub.clone().into(), + PenpalBReceiver::get().into(), + 10_000_000_000_000, // For it to have more than enough. + )); + + assert_ok!(::AssetConversion::create_pool( + ::RuntimeOrigin::signed(PenpalBReceiver::get()), + Box::new(relay_asset.clone()), + Box::new(usdt_from_asset_hub.clone()), + )); + + assert_expected_events!( + PenpalB, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::PoolCreated { .. }) => {}, + ] + ); + + assert_ok!(::AssetConversion::add_liquidity( + ::RuntimeOrigin::signed(PenpalBReceiver::get()), + Box::new(relay_asset), + Box::new(usdt_from_asset_hub.clone()), + 1_000_000_000_000, + 2_000_000_000_000, // `usdt_from_asset_hub` is worth half of `relay_asset` + 0, + 0, + PenpalBReceiver::get().into() + )); + + assert_expected_events!( + PenpalB, + vec![ + RuntimeEvent::AssetConversion(pallet_asset_conversion::Event::LiquidityAdded { .. }) => {}, + ] + ); + }); + + PenpalA::execute_with(|| { + use frame_support::traits::tokens::fungibles::Mutate; + type ForeignAssets = ::ForeignAssets; + assert_ok!(>::mint_into( + usdt_from_asset_hub.clone(), + &sender, + asset_amount_to_send + fee_amount_to_send, + )); + }); + + // Prepare assets to transfer. + let assets: Assets = + (usdt_from_asset_hub.clone(), asset_amount_to_send + fee_amount_to_send).into(); + // Just to be very specific we're not including anything other than USDT. + assert_eq!(assets.len(), 1); + + // Give the sender enough Relay tokens to pay for local delivery fees. + // TODO(https://github.com/paritytech/polkadot-sdk/issues/5160): When we support local delivery fee payment in other assets, we don't need this. + PenpalA::mint_foreign_asset( + ::RuntimeOrigin::signed(PenpalAssetOwner::get()), + RelayLocation::get(), + sender.clone(), + 10_000_000_000_000, // Large estimate to make sure it works. + ); + + // Init values for Parachain Destination + let receiver = PenpalBReceiver::get(); + + // Init Test + let fee_asset_index = 0; + let test_args = TestContext { + sender: sender.clone(), + receiver: receiver.clone(), + args: TestArgs::new_para( + destination, + receiver.clone(), + asset_amount_to_send, + assets, + None, + fee_asset_index, + ), + }; + let mut test = ParaToParaThroughAHTest::new(test_args); + + // Query initial balances + let sender_assets_before = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub.clone(), &sender) + }); + let receiver_assets_before = PenpalB::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub.clone(), &receiver) + }); + test.set_assertion::(para_to_para_through_hop_sender_assertions); + test.set_assertion::(para_to_para_asset_hub_hop_assertions); + test.set_assertion::(para_to_para_through_hop_receiver_assertions); + test.set_dispatchable::( + para_to_para_through_asset_hub_limited_reserve_transfer_assets, + ); + test.assert(); + + // Query final balances + let sender_assets_after = PenpalA::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub.clone(), &sender) + }); + let receiver_assets_after = PenpalB::execute_with(|| { + type ForeignAssets = ::ForeignAssets; + >::balance(usdt_from_asset_hub, &receiver) + }); + + // Sender's balance is reduced by amount + assert!(sender_assets_after < sender_assets_before - asset_amount_to_send); // Receiver's balance is increased assert!(receiver_assets_after > receiver_assets_before); } + +/// Reserve Withdraw Native Asset from AssetHub to Parachain fails. +#[test] +fn reserve_withdraw_from_untrusted_reserve_fails() { + // Init values for Parachain Origin + let destination = AssetHubWestend::sibling_location_of(PenpalA::para_id()); + let signed_origin = + ::RuntimeOrigin::signed(AssetHubWestendSender::get().into()); + let roc_to_send: Balance = WESTEND_ED * 10000; + let roc_location = RelayLocation::get(); + + // Assets to send + let assets: Vec = vec![(roc_location.clone(), roc_to_send).into()]; + let fee_id: AssetId = roc_location.into(); + + // this should fail + AssetHubWestend::execute_with(|| { + let result = ::PolkadotXcm::transfer_assets_using_type_and_then( + signed_origin.clone(), + bx!(destination.clone().into()), + bx!(assets.clone().into()), + bx!(TransferType::DestinationReserve), + bx!(fee_id.into()), + bx!(TransferType::DestinationReserve), + bx!(VersionedXcm::from(Xcm::<()>::new())), + Unlimited, + ); + assert_err!( + result, + DispatchError::Module(sp_runtime::ModuleError { + index: 31, + error: [22, 0, 0, 0], + message: Some("InvalidAssetUnsupportedReserve") + }) + ); + }); + + // this should also fail + AssetHubWestend::execute_with(|| { + let xcm: Xcm = Xcm(vec![ + WithdrawAsset(assets.into()), + InitiateReserveWithdraw { + assets: Wild(All), + reserve: destination, + xcm: Xcm::<()>::new(), + }, + ]); + let result = ::PolkadotXcm::execute( + signed_origin, + bx!(xcm::VersionedXcm::V4(xcm)), + Weight::MAX, + ); + assert!(result.is_err()); + }); +} 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 15d39858acca4b099cd0215a55d017b42a695dda..ee0f297792f8ca9f09818aa50520a964bcfd0d61 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 @@ -265,7 +265,9 @@ fn limited_teleport_native_assets_from_system_para_to_relay_fails() { let delivery_fees = AssetHubWestend::execute_with(|| { xcm_helpers::teleport_assets_delivery_fees::< ::XcmSender, - >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) + >( + test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest + ) }); // Sender's balance is reduced @@ -530,3 +532,54 @@ fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() { system_para_to_para_transfer_assets, ); } + +/// Teleport Native Asset from AssetHub to Parachain fails. +#[test] +fn teleport_to_untrusted_chain_fails() { + // Init values for Parachain Origin + let destination = AssetHubWestend::sibling_location_of(PenpalA::para_id()); + let signed_origin = + ::RuntimeOrigin::signed(AssetHubWestendSender::get().into()); + let roc_to_send: Balance = WESTEND_ED * 10000; + let roc_location = RelayLocation::get(); + + // Assets to send + let assets: Vec = vec![(roc_location.clone(), roc_to_send).into()]; + let fee_id: AssetId = roc_location.into(); + + // this should fail + AssetHubWestend::execute_with(|| { + let result = ::PolkadotXcm::transfer_assets_using_type_and_then( + signed_origin.clone(), + bx!(destination.clone().into()), + bx!(assets.clone().into()), + bx!(TransferType::Teleport), + bx!(fee_id.into()), + bx!(TransferType::Teleport), + bx!(VersionedXcm::from(Xcm::<()>::new())), + Unlimited, + ); + assert_err!( + result, + DispatchError::Module(sp_runtime::ModuleError { + index: 31, + error: [2, 0, 0, 0], + message: Some("Filtered") + }) + ); + }); + + // this should also fail + AssetHubWestend::execute_with(|| { + let xcm: Xcm = Xcm(vec![ + WithdrawAsset(assets.into()), + InitiateTeleport { assets: Wild(All), dest: destination, xcm: Xcm::<()>::new() }, + ]); + let result = ::PolkadotXcm::execute( + signed_origin, + bx!(xcm::VersionedXcm::V4(xcm)), + Weight::MAX, + ); + assert!(result.is_err()); + }); +} 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 a5787885329d75a05fe50ce18690fd6d4076db51..9f6fe78a33eebfccbe387df6a86336cb0ed1737b 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 @@ -28,9 +28,11 @@ sp-runtime = { workspace = true } xcm = { workspace = true } pallet-xcm = { workspace = true } xcm-executor = { workspace = true } +xcm-runtime-apis = { workspace = true } # Bridges pallet-bridge-messages = { workspace = true } +pallet-xcm-bridge-hub = { workspace = true } # Cumulus cumulus-pallet-xcmp-queue = { workspace = true } @@ -38,7 +40,7 @@ emulated-integration-tests-common = { workspace = true } parachains-common = { workspace = true, default-features = true } rococo-system-emulated-network = { workspace = true } rococo-westend-system-emulated-network = { workspace = true } -testnet-parachains-constants = { features = ["rococo"], workspace = true, default-features = true } +testnet-parachains-constants = { features = ["rococo", "westend"], workspace = true, default-features = true } # Snowbridge snowbridge-core = { workspace = true } diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs index ac08e48ded68dacad27756ff62b55caf6a1a7f32..a542de16de5fbe68c96f4fecb43db52d6e789f3e 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 @@ -16,6 +16,7 @@ #[cfg(test)] mod imports { // Substrate + pub use codec::Encode; pub use frame_support::{assert_err, assert_ok, pallet_prelude::DispatchResult}; pub use sp_runtime::DispatchError; @@ -32,8 +33,8 @@ mod imports { pub use emulated_integration_tests_common::{ accounts::ALICE, impls::Inspect, - test_parachain_is_trusted_teleporter, test_parachain_is_trusted_teleporter_for_relay, - test_relay_is_trusted_teleporter, + test_dry_run_transfer_across_pk_bridge, test_parachain_is_trusted_teleporter, + test_parachain_is_trusted_teleporter_for_relay, test_relay_is_trusted_teleporter, xcm_emulator::{ assert_expected_events, bx, Chain, Parachain as Para, RelayChain as Relay, TestExt, }, @@ -55,9 +56,12 @@ mod imports { BridgeHubRococoXcmConfig, EthereumBeaconClient, EthereumInboundQueue, }, penpal_emulated_chain::{ - penpal_runtime::xcm_config::{ - CustomizableAssetFromSystemAssetHub as PenpalCustomizableAssetFromSystemAssetHub, - UniversalLocation as PenpalUniversalLocation, + penpal_runtime::{ + self, + xcm_config::{ + CustomizableAssetFromSystemAssetHub as PenpalCustomizableAssetFromSystemAssetHub, + UniversalLocation as PenpalUniversalLocation, + }, }, PenpalAParaPallet as PenpalAPallet, PenpalAssetOwner, }, @@ -72,9 +76,8 @@ mod imports { BridgeHubRococoParaReceiver as BridgeHubRococoReceiver, BridgeHubRococoParaSender as BridgeHubRococoSender, BridgeHubWestendPara as BridgeHubWestend, PenpalAPara as PenpalA, - PenpalAParaReceiver as PenpalAReceiver, PenpalAParaSender as PenpalASender, - RococoRelay as Rococo, RococoRelayReceiver as RococoReceiver, - RococoRelaySender as RococoSender, + PenpalAParaSender as PenpalASender, RococoRelay as Rococo, + RococoRelayReceiver as RococoReceiver, RococoRelaySender as RococoSender, }; pub const ASSET_MIN_BALANCE: u128 = 1000; diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs index 8a674f89c9ef9cdc6c5c6512d93196f96144f6a8..f1e2a6dcca619f13a8dd00c77fefec155332e07e 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/asset_transfers.rs @@ -25,6 +25,9 @@ fn send_assets_over_bridge(send_fn: F) { AssetHubRococo::force_xcm_version(asset_hub_westend_location(), XCM_VERSION); BridgeHubRococo::force_xcm_version(bridge_hub_westend_location(), XCM_VERSION); + // open bridge + open_bridge_between_asset_hub_rococo_and_asset_hub_westend(); + // send message over bridge send_fn(); @@ -429,6 +432,16 @@ fn send_back_wnds_from_penpal_rococo_through_asset_hub_rococo_to_asset_hub_weste ASSET_MIN_BALANCE, vec![(sender.clone(), amount * 2)], ); + // Configure source Penpal chain to trust local AH as reserve of bridged WND + PenpalA::execute_with(|| { + assert_ok!(::System::set_storage( + ::RuntimeOrigin::root(), + vec![( + PenpalCustomizableAssetFromSystemAssetHub::key().to_vec(), + wnd_at_rococo_parachains.encode(), + )], + )); + }); // fund the AHR's SA on AHW with the WND tokens held in reserve let sov_ahr_on_ahw = AssetHubWestend::sovereign_account_of_parachain_on_other_global_consensus( @@ -521,3 +534,12 @@ fn send_back_wnds_from_penpal_rococo_through_asset_hub_rococo_to_asset_hub_weste assert!(receiver_wnds_after > receiver_wnds_before); assert!(receiver_wnds_after <= receiver_wnds_before + amount); } + +#[test] +fn dry_run_transfer_to_westend_sends_xcm_to_bridge_hub() { + test_dry_run_transfer_across_pk_bridge!( + AssetHubRococo, + BridgeHubRococo, + asset_hub_westend_location() + ); +} 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 6ce8ecef0df3ff66f0a39d26899aa47202e8a121..a989881fef09f45648a26f733158c9363cefe852 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/mod.rs @@ -36,7 +36,10 @@ pub(crate) fn bridged_roc_at_ah_westend() -> Location { Location::new(2, [GlobalConsensus(Rococo)]) } -// wWND +// WND and wWND +pub(crate) fn wnd_at_ah_westend() -> Location { + Parent.into() +} pub(crate) fn bridged_wnd_at_ah_rococo() -> Location { Location::new(2, [GlobalConsensus(Westend)]) } @@ -210,3 +213,35 @@ pub(crate) fn assert_bridge_hub_westend_message_received() { ); }) } + +pub(crate) fn open_bridge_between_asset_hub_rococo_and_asset_hub_westend() { + use testnet_parachains_constants::{ + rococo::currency::UNITS as ROC, westend::currency::UNITS as WND, + }; + + // open AHR -> AHW + BridgeHubRococo::fund_para_sovereign(AssetHubRococo::para_id(), ROC * 5); + AssetHubRococo::open_bridge( + AssetHubRococo::sibling_location_of(BridgeHubRococo::para_id()), + [GlobalConsensus(Westend), Parachain(AssetHubWestend::para_id().into())].into(), + Some(( + (roc_at_ah_rococo(), ROC * 1).into(), + BridgeHubRococo::sovereign_account_id_of(BridgeHubRococo::sibling_location_of( + AssetHubRococo::para_id(), + )), + )), + ); + + // open AHW -> AHR + BridgeHubWestend::fund_para_sovereign(AssetHubWestend::para_id(), WND * 5); + AssetHubWestend::open_bridge( + AssetHubWestend::sibling_location_of(BridgeHubWestend::para_id()), + [GlobalConsensus(Rococo), Parachain(AssetHubRococo::para_id().into())].into(), + Some(( + (wnd_at_ah_westend(), WND * 1).into(), + BridgeHubWestend::sovereign_account_id_of(BridgeHubWestend::sibling_location_of( + AssetHubWestend::para_id(), + )), + )), + ); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/send_xcm.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/send_xcm.rs index 3f2038b4bdd1b510305eb27b7564aa9bbd778c40..12f05742a0803f73926c2a1e7d58aa724ed16015 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/send_xcm.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/send_xcm.rs @@ -79,6 +79,9 @@ fn send_xcm_through_opened_lane_with_different_xcm_version_on_hops_works() { // fund sender AssetHubRococo::fund_accounts(vec![(AssetHubRococoSender::get().into(), amount * 10)]); + // open bridge + open_bridge_between_asset_hub_rococo_and_asset_hub_westend(); + // send XCM from AssetHubRococo - fails - destination version not known assert_err!( send_assets_from_asset_hub_rococo( diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs index 84328fb7c6d201b86edf9aba954d84b7598e3005..d91a0c6895f98c377230f48d984d393e9773aeb9 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 @@ -286,11 +286,19 @@ fn send_token_from_ethereum_to_penpal() { // 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), - ]); + // Fund PenPal receiver (covering ED) + let native_id: Location = Parent.into(); + let receiver: AccountId = [ + 28, 189, 45, 67, 83, 10, 68, 112, 90, 208, 136, 175, 49, 62, 24, 248, 11, 83, 239, 22, 179, + 97, 119, 205, 75, 119, 184, 70, 242, 165, 240, 124, + ] + .into(); + PenpalA::mint_foreign_asset( + ::RuntimeOrigin::signed(PenpalAssetOwner::get()), + native_id, + receiver, + penpal_runtime::EXISTENTIAL_DEPOSIT, + ); PenpalA::execute_with(|| { assert_ok!(::System::set_storage( 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 1f2d2c8ece2437fee863a888713ae8d868359b84..b87f25ac0f014125bc5807fa8732dfb8ee67067a 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 @@ -29,16 +29,18 @@ sp-runtime = { workspace = true } xcm = { workspace = true } pallet-xcm = { workspace = true } xcm-executor = { workspace = true } +xcm-runtime-apis = { workspace = true } # Bridges pallet-bridge-messages = { workspace = true } +pallet-xcm-bridge-hub = { workspace = true } # Cumulus cumulus-pallet-xcmp-queue = { workspace = true } emulated-integration-tests-common = { workspace = true } parachains-common = { workspace = true, default-features = true } rococo-westend-system-emulated-network = { workspace = true } -testnet-parachains-constants = { workspace = true, features = ["westend"] } +testnet-parachains-constants = { features = ["rococo", "westend"], workspace = true, default-features = true } asset-hub-westend-runtime = { workspace = true } bridge-hub-westend-runtime = { workspace = true } diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs index 5e0462d14882a9922c8d6232dd7ebe781ca5c88a..9228700c0198b5e23702cddead065437dbc6da24 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs @@ -16,6 +16,7 @@ #[cfg(test)] mod imports { // Substrate + pub use codec::Encode; pub use frame_support::{assert_err, assert_ok, pallet_prelude::DispatchResult}; pub use sp_runtime::DispatchError; @@ -31,8 +32,8 @@ mod imports { pub use emulated_integration_tests_common::{ accounts::ALICE, impls::Inspect, - test_parachain_is_trusted_teleporter, test_parachain_is_trusted_teleporter_for_relay, - test_relay_is_trusted_teleporter, + test_dry_run_transfer_across_pk_bridge, test_parachain_is_trusted_teleporter, + test_parachain_is_trusted_teleporter_for_relay, test_relay_is_trusted_teleporter, xcm_emulator::{ assert_expected_events, bx, Chain, Parachain as Para, RelayChain as Relay, TestExt, }, @@ -52,7 +53,10 @@ mod imports { BridgeHubWestendParaPallet as BridgeHubWestendPallet, BridgeHubWestendXcmConfig, }, penpal_emulated_chain::{ - penpal_runtime::xcm_config::UniversalLocation as PenpalUniversalLocation, + penpal_runtime::xcm_config::{ + CustomizableAssetFromSystemAssetHub as PenpalCustomizableAssetFromSystemAssetHub, + UniversalLocation as PenpalUniversalLocation, + }, PenpalAssetOwner, PenpalBParaPallet as PenpalBPallet, }, westend_emulated_chain::{ 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 fc8b772a9c7e02e9937d78607ceb2b106b16fd24..7def637a66e4f6eb39a63997ad042955ff06b086 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/asset_transfers.rs @@ -24,6 +24,9 @@ fn send_assets_over_bridge(send_fn: F) { AssetHubWestend::force_xcm_version(asset_hub_rococo_location(), XCM_VERSION); BridgeHubWestend::force_xcm_version(bridge_hub_rococo_location(), XCM_VERSION); + // open bridge + open_bridge_between_asset_hub_rococo_and_asset_hub_westend(); + // send message over bridge send_fn(); @@ -449,6 +452,16 @@ fn send_back_rocs_from_penpal_westend_through_asset_hub_westend_to_asset_hub_roc ASSET_MIN_BALANCE, vec![(sender.clone(), amount * 2)], ); + // Configure source Penpal chain to trust local AH as reserve of bridged ROC + PenpalB::execute_with(|| { + assert_ok!(::System::set_storage( + ::RuntimeOrigin::root(), + vec![( + PenpalCustomizableAssetFromSystemAssetHub::key().to_vec(), + roc_at_westend_parachains.encode(), + )], + )); + }); // fund the AHW's SA on AHR with the ROC tokens held in reserve let sov_ahw_on_ahr = AssetHubRococo::sovereign_account_of_parachain_on_other_global_consensus( @@ -541,3 +554,12 @@ fn send_back_rocs_from_penpal_westend_through_asset_hub_westend_to_asset_hub_roc assert!(receiver_rocs_after > receiver_rocs_before); assert!(receiver_rocs_after <= receiver_rocs_before + amount); } + +#[test] +fn dry_run_transfer_to_rococo_sends_xcm_to_bridge_hub() { + test_dry_run_transfer_across_pk_bridge!( + AssetHubWestend, + BridgeHubWestend, + asset_hub_rococo_location() + ); +} 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 bf894a3baf585da518287b3a30527b403c55aa3a..f037a05a82766f50bbaae06459a30eb068aa15ea 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/mod.rs @@ -18,9 +18,10 @@ use crate::imports::*; mod asset_transfers; mod claim_assets; mod send_xcm; -mod snowbridge; mod teleport; +mod snowbridge; + pub(crate) fn asset_hub_rococo_location() -> Location { Location::new(2, [GlobalConsensus(Rococo), Parachain(AssetHubRococo::para_id().into())]) } @@ -37,7 +38,10 @@ pub(crate) fn bridged_wnd_at_ah_rococo() -> Location { Location::new(2, [GlobalConsensus(Westend)]) } -// wROC +// ROC and wROC +pub(crate) fn roc_at_ah_rococo() -> Location { + Parent.into() +} pub(crate) fn bridged_roc_at_ah_westend() -> Location { Location::new(2, [GlobalConsensus(Rococo)]) } @@ -224,3 +228,35 @@ pub(crate) fn assert_bridge_hub_rococo_message_received() { ); }) } + +pub(crate) fn open_bridge_between_asset_hub_rococo_and_asset_hub_westend() { + use testnet_parachains_constants::{ + rococo::currency::UNITS as ROC, westend::currency::UNITS as WND, + }; + + // open AHR -> AHW + BridgeHubRococo::fund_para_sovereign(AssetHubRococo::para_id(), ROC * 5); + AssetHubRococo::open_bridge( + AssetHubRococo::sibling_location_of(BridgeHubRococo::para_id()), + [GlobalConsensus(Westend), Parachain(AssetHubWestend::para_id().into())].into(), + Some(( + (roc_at_ah_rococo(), ROC * 1).into(), + BridgeHubRococo::sovereign_account_id_of(BridgeHubRococo::sibling_location_of( + AssetHubRococo::para_id(), + )), + )), + ); + + // open AHW -> AHR + BridgeHubWestend::fund_para_sovereign(AssetHubWestend::para_id(), WND * 5); + AssetHubWestend::open_bridge( + AssetHubWestend::sibling_location_of(BridgeHubWestend::para_id()), + [GlobalConsensus(Rococo), Parachain(AssetHubRococo::para_id().into())].into(), + Some(( + (wnd_at_ah_westend(), WND * 1).into(), + BridgeHubWestend::sovereign_account_id_of(BridgeHubWestend::sibling_location_of( + AssetHubWestend::para_id(), + )), + )), + ); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/send_xcm.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/send_xcm.rs index dee411bea8b7353f07b5542b1a81f08ed2dbd966..ae05e4223b073de9b8f8edb7907b15a7a4cf7ed6 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/send_xcm.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/send_xcm.rs @@ -79,6 +79,9 @@ fn send_xcm_through_opened_lane_with_different_xcm_version_on_hops_works() { // fund sender AssetHubWestend::fund_accounts(vec![(AssetHubWestendSender::get().into(), amount * 10)]); + // open bridge + open_bridge_between_asset_hub_rococo_and_asset_hub_westend(); + // send XCM from AssetHubWestend - fails - destination version not known assert_err!( send_assets_from_asset_hub_westend( diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs index b4db9b365f390c820b83ca069912a5fa09e2a069..4e9dd5a77dd7b14ffb6ce24754ceb957ef14ea56 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs @@ -16,20 +16,24 @@ use crate::imports::*; use asset_hub_westend_runtime::xcm_config::bridging::to_ethereum::DefaultBridgeHubEthereumBaseFee; use bridge_hub_westend_runtime::EthereumInboundQueue; use codec::{Decode, Encode}; +use emulated_integration_tests_common::RESERVABLE_ASSET_ID; use frame_support::pallet_prelude::TypeInfo; use hex_literal::hex; -use snowbridge_core::outbound::OperatingMode; +use rococo_westend_system_emulated_network::asset_hub_westend_emulated_chain::genesis::AssetHubWestendAssetOwner; +use snowbridge_core::{outbound::OperatingMode, AssetMetadata, TokenIdOf}; use snowbridge_router_primitives::inbound::{ - Command, ConvertMessage, Destination, MessageV1, VersionedMessage, + Command, Destination, GlobalConsensusEthereumConvertsFor, MessageV1, VersionedMessage, }; +use sp_core::H256; use testnet_parachains_constants::westend::snowbridge::EthereumNetwork; +use xcm_executor::traits::ConvertLocation; -const INITIAL_FUND: u128 = 5_000_000_000_000_000_000; +const INITIAL_FUND: u128 = 5_000_000_000_000; pub const CHAIN_ID: u64 = 11155111; pub const WETH: [u8; 20] = hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d"); const ETHEREUM_DESTINATION_ADDRESS: [u8; 20] = hex!("44a57ee2f2FCcb85FDa2B0B18EBD0D8D2333700e"); const XCM_FEE: u128 = 100_000_000_000; -const WETH_AMOUNT: u128 = 1_000_000_000; +const TOKEN_AMOUNT: u128 = 100_000_000_000; #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] pub enum ControlCall { @@ -55,20 +59,16 @@ fn register_weth_token_from_ethereum_to_asset_hub() { BridgeHubWestend::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; - type Converter = ::MessageConverter; - let message = VersionedMessage::V1(MessageV1 { chain_id: CHAIN_ID, command: Command::RegisterToken { token: WETH.into(), fee: XCM_FEE }, }); - let (xcm, _) = Converter::convert(message).unwrap(); - let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubRococo::para_id().into()).unwrap(); + let (xcm, _) = EthereumInboundQueue::do_convert([0; 32].into(), message).unwrap(); + let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubWestend::para_id().into()).unwrap(); assert_expected_events!( BridgeHubWestend, - vec![ - RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, - ] + vec![RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},] ); }); @@ -77,9 +77,7 @@ fn register_weth_token_from_ethereum_to_asset_hub() { assert_expected_events!( AssetHubWestend, - vec![ - RuntimeEvent::ForeignAssets(pallet_assets::Event::Created { .. }) => {}, - ] + vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Created { .. }) => {},] ); }); } @@ -120,26 +118,22 @@ fn send_token_from_ethereum_to_asset_hub() { BridgeHubWestend::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; - type Converter = ::MessageConverter; - let message = VersionedMessage::V1(MessageV1 { chain_id: CHAIN_ID, command: Command::SendToken { token: WETH.into(), destination: Destination::AccountId32 { id: AssetHubWestendReceiver::get().into() }, - amount: WETH_AMOUNT, + amount: TOKEN_AMOUNT, fee: XCM_FEE, }, }); - let (xcm, _) = Converter::convert(message).unwrap(); + let (xcm, _) = EthereumInboundQueue::do_convert([0; 32].into(), message).unwrap(); let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubWestend::para_id().into()).unwrap(); // Check that the message was sent assert_expected_events!( BridgeHubWestend, - vec![ - RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, - ] + vec![RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},] ); }); @@ -149,9 +143,7 @@ fn send_token_from_ethereum_to_asset_hub() { // Check that the token was received and issued as a foreign asset on AssetHub assert_expected_events!( AssetHubWestend, - vec![ - RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {}, - ] + vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {},] ); }); } @@ -167,13 +159,6 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { let weth_asset_location: Location = (Parent, Parent, EthereumNetwork::get(), AccountKey20 { network: None, key: WETH }).into(); - AssetHubWestend::force_default_xcm_version(Some(XCM_VERSION)); - BridgeHubWestend::force_default_xcm_version(Some(XCM_VERSION)); - AssetHubWestend::force_xcm_version( - Location::new(2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })]), - XCM_VERSION, - ); - BridgeHubWestend::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); AssetHubWestend::execute_with(|| { @@ -194,26 +179,23 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { BridgeHubWestend::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; - type Converter = ::MessageConverter; let message = VersionedMessage::V1(MessageV1 { chain_id: CHAIN_ID, command: Command::SendToken { token: WETH.into(), destination: Destination::AccountId32 { id: AssetHubWestendReceiver::get().into() }, - amount: WETH_AMOUNT, + amount: TOKEN_AMOUNT, fee: XCM_FEE, }, }); - let (xcm, _) = Converter::convert(message).unwrap(); + let (xcm, _) = EthereumInboundQueue::do_convert([0; 32].into(), message).unwrap(); let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubWestend::para_id().into()).unwrap(); // Check that the send token message was sent using xcm assert_expected_events!( BridgeHubWestend, - vec![ - RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) =>{},] + vec![RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) =>{},] ); }); @@ -224,9 +206,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { // Check that AssetHub has issued the foreign asset assert_expected_events!( AssetHubWestend, - vec![ - RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {}, - ] + vec![RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {},] ); let assets = vec![Asset { id: AssetId(Location::new( @@ -236,9 +216,9 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { AccountKey20 { network: None, key: WETH }, ], )), - fun: Fungible(WETH_AMOUNT), + fun: Fungible(TOKEN_AMOUNT), }]; - let multi_assets = VersionedAssets::V4(Assets::from(assets)); + let versioned_assets = VersionedAssets::V4(Assets::from(assets)); let destination = VersionedLocation::V4(Location::new( 2, @@ -259,7 +239,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { RuntimeOrigin::signed(AssetHubWestendReceiver::get()), Box::new(destination), Box::new(beneficiary), - Box::new(multi_assets), + Box::new(versioned_assets), 0, Unlimited, ) @@ -279,10 +259,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { // Outbound Queue assert_expected_events!( BridgeHubWestend, - vec![ - - RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued - {..}) => {}, ] + vec![RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued{ .. }) => {},] ); let events = BridgeHubWestend::events(); // Check that the local fee was credited to the Snowbridge sovereign account @@ -305,3 +282,316 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { ); }); } + +#[test] +fn transfer_relay_token() { + let assethub_sovereign = BridgeHubWestend::sovereign_account_id_of( + BridgeHubWestend::sibling_location_of(AssetHubWestend::para_id()), + ); + BridgeHubWestend::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); + + let asset_id: Location = Location { parents: 1, interior: [].into() }; + let expected_asset_id: Location = + Location { parents: 1, interior: [GlobalConsensus(Westend)].into() }; + + let expected_token_id = TokenIdOf::convert_location(&expected_asset_id).unwrap(); + + let ethereum_sovereign: AccountId = + GlobalConsensusEthereumConvertsFor::<[u8; 32]>::convert_location(&Location::new( + 2, + [GlobalConsensus(EthereumNetwork::get())], + )) + .unwrap() + .into(); + + // Register token + BridgeHubWestend::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + type RuntimeEvent = ::RuntimeEvent; + + assert_ok!(::Balances::force_set_balance( + RuntimeOrigin::root(), + sp_runtime::MultiAddress::Id(BridgeHubWestendSender::get()), + INITIAL_FUND * 10, + )); + + assert_ok!(::EthereumSystem::register_token( + RuntimeOrigin::root(), + Box::new(VersionedLocation::V4(asset_id.clone())), + AssetMetadata { + name: "wnd".as_bytes().to_vec().try_into().unwrap(), + symbol: "wnd".as_bytes().to_vec().try_into().unwrap(), + decimals: 12, + }, + )); + // Check that a message was sent to Ethereum to create the agent + assert_expected_events!( + BridgeHubWestend, + vec![RuntimeEvent::EthereumSystem(snowbridge_pallet_system::Event::RegisterToken { .. }) => {},] + ); + }); + + // Send token to Ethereum + AssetHubWestend::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + type RuntimeEvent = ::RuntimeEvent; + + let assets = vec![Asset { id: AssetId(Location::parent()), fun: Fungible(TOKEN_AMOUNT) }]; + let versioned_assets = VersionedAssets::V4(Assets::from(assets)); + + let destination = VersionedLocation::V4(Location::new( + 2, + [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })], + )); + + let beneficiary = VersionedLocation::V4(Location::new( + 0, + [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], + )); + + assert_ok!(::PolkadotXcm::limited_reserve_transfer_assets( + RuntimeOrigin::signed(AssetHubWestendSender::get()), + Box::new(destination), + Box::new(beneficiary), + Box::new(versioned_assets), + 0, + Unlimited, + )); + + let events = AssetHubWestend::events(); + // Check that the native asset transferred to some reserved account(sovereign of Ethereum) + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Balances(pallet_balances::Event::Transfer { amount, to, ..}) + if *amount == TOKEN_AMOUNT && *to == ethereum_sovereign.clone(), + )), + "native token reserved to Ethereum sovereign account." + ); + }); + + // Send token back from ethereum + BridgeHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that the transfer token back to Ethereum message was queue in the Ethereum + // Outbound Queue + assert_expected_events!( + BridgeHubWestend, + vec![RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued{ .. }) => {},] + ); + + // Send relay token back to AH + let message_id: H256 = [0; 32].into(); + let message = VersionedMessage::V1(MessageV1 { + chain_id: CHAIN_ID, + command: Command::SendNativeToken { + token_id: expected_token_id, + destination: Destination::AccountId32 { id: AssetHubWestendReceiver::get().into() }, + amount: TOKEN_AMOUNT, + fee: XCM_FEE, + }, + }); + // Convert the message to XCM + let (xcm, _) = EthereumInboundQueue::do_convert(message_id, message).unwrap(); + // Send the XCM + let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubWestend::para_id().into()).unwrap(); + + assert_expected_events!( + BridgeHubWestend, + vec![RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},] + ); + }); + + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_expected_events!( + AssetHubWestend, + vec![RuntimeEvent::Balances(pallet_balances::Event::Burned{ .. }) => {},] + ); + + let events = AssetHubWestend::events(); + + // Check that the native token burnt from some reserved account + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Balances(pallet_balances::Event::Burned { who, ..}) + if *who == ethereum_sovereign.clone(), + )), + "native token burnt from Ethereum sovereign account." + ); + + // Check that the token was minted to beneficiary + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount }) + if *amount >= TOKEN_AMOUNT && *who == AssetHubWestendReceiver::get() + )), + "Token minted to beneficiary." + ); + }); +} + +#[test] +fn transfer_ah_token() { + let assethub_sovereign = BridgeHubWestend::sovereign_account_id_of( + BridgeHubWestend::sibling_location_of(AssetHubWestend::para_id()), + ); + BridgeHubWestend::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); + + let ethereum_destination = Location::new(2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })]); + + let ethereum_sovereign: AccountId = + GlobalConsensusEthereumConvertsFor::<[u8; 32]>::convert_location(ðereum_destination) + .unwrap() + .into(); + AssetHubWestend::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]); + + let asset_id: Location = + [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(RESERVABLE_ASSET_ID.into())].into(); + + let asset_id_in_bh: Location = Location::new( + 1, + [ + Parachain(AssetHubWestend::para_id().into()), + PalletInstance(ASSETS_PALLET_ID), + GeneralIndex(RESERVABLE_ASSET_ID.into()), + ], + ); + + let asset_id_after_reanchored = + Location::new(1, [GlobalConsensus(Westend), Parachain(AssetHubWestend::para_id().into())]) + .appended_with(asset_id.clone().interior) + .unwrap(); + + let token_id = TokenIdOf::convert_location(&asset_id_after_reanchored).unwrap(); + + // Register token + BridgeHubWestend::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + + assert_ok!(::EthereumSystem::register_token( + RuntimeOrigin::root(), + Box::new(VersionedLocation::V4(asset_id_in_bh.clone())), + AssetMetadata { + name: "ah_asset".as_bytes().to_vec().try_into().unwrap(), + symbol: "ah_asset".as_bytes().to_vec().try_into().unwrap(), + decimals: 12, + }, + )); + }); + + // Mint some token + AssetHubWestend::mint_asset( + ::RuntimeOrigin::signed(AssetHubWestendAssetOwner::get()), + RESERVABLE_ASSET_ID, + AssetHubWestendSender::get(), + TOKEN_AMOUNT, + ); + + // Send token to Ethereum + AssetHubWestend::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + type RuntimeEvent = ::RuntimeEvent; + + // Send partial of the token, will fail if send all + let assets = + vec![Asset { id: AssetId(asset_id.clone()), fun: Fungible(TOKEN_AMOUNT / 10) }]; + let versioned_assets = VersionedAssets::V4(Assets::from(assets)); + + let beneficiary = VersionedLocation::V4(Location::new( + 0, + [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], + )); + + assert_ok!(::PolkadotXcm::limited_reserve_transfer_assets( + RuntimeOrigin::signed(AssetHubWestendSender::get()), + Box::new(VersionedLocation::from(ethereum_destination)), + Box::new(beneficiary), + Box::new(versioned_assets), + 0, + Unlimited, + )); + + assert_expected_events!( + AssetHubWestend, + vec![RuntimeEvent::Assets(pallet_assets::Event::Transferred{ .. }) => {},] + ); + + let events = AssetHubWestend::events(); + // Check that the native asset transferred to some reserved account(sovereign of Ethereum) + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Assets(pallet_assets::Event::Transferred { asset_id, to, ..}) + if *asset_id == RESERVABLE_ASSET_ID && *to == ethereum_sovereign.clone() + )), + "native token reserved to Ethereum sovereign account." + ); + }); + + // Send token back from Ethereum + BridgeHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that the transfer token back to Ethereum message was queue in the Ethereum + // Outbound Queue + assert_expected_events!( + BridgeHubWestend, + vec![RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued{ .. }) => {},] + ); + + let message = VersionedMessage::V1(MessageV1 { + chain_id: CHAIN_ID, + command: Command::SendNativeToken { + token_id, + destination: Destination::AccountId32 { id: AssetHubWestendReceiver::get().into() }, + amount: TOKEN_AMOUNT / 10, + fee: XCM_FEE, + }, + }); + // Convert the message to XCM + let (xcm, _) = EthereumInboundQueue::do_convert([0; 32].into(), message).unwrap(); + // Send the XCM + let _ = EthereumInboundQueue::send_xcm(xcm, AssetHubWestend::para_id().into()).unwrap(); + + assert_expected_events!( + BridgeHubWestend, + vec![RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},] + ); + }); + + AssetHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_expected_events!( + AssetHubWestend, + vec![RuntimeEvent::Assets(pallet_assets::Event::Burned{..}) => {},] + ); + + let events = AssetHubWestend::events(); + + // Check that the native token burnt from some reserved account + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Assets(pallet_assets::Event::Burned { owner, .. }) + if *owner == ethereum_sovereign.clone(), + )), + "token burnt from Ethereum sovereign account." + ); + + // Check that the token was minted to beneficiary + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Assets(pallet_assets::Event::Issued { owner, .. }) + if *owner == AssetHubWestendReceiver::get() + )), + "Token minted to beneficiary." + ); + }); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/Cargo.toml index 259be790c3e5f789b8ee180fef068e596b2c1fdd..28d9da0993ff6cfbb3c535fc998eb634e90ed00d 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "coretime-rococo-integration-tests" -version = "0.1.0" +version = "0.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -12,16 +12,19 @@ publish = false # Substrate frame-support = { workspace = true } pallet-balances = { workspace = true } +pallet-broker = { workspace = true, default-features = true } pallet-message-queue = { workspace = true } pallet-identity = { workspace = true } sp-runtime = { workspace = true } # Polkadot polkadot-runtime-common = { workspace = true, default-features = true } +polkadot-runtime-parachains = { workspace = true, default-features = true } rococo-runtime-constants = { workspace = true, default-features = true } xcm = { workspace = true } xcm-executor = { workspace = true } # Cumulus +cumulus-pallet-parachain-system = { workspace = true, default-features = true } emulated-integration-tests-common = { workspace = true } rococo-system-emulated-network = { workspace = true } diff --git a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/lib.rs index ad3c4fd58da991af243db4b611fd1e6279429f5d..055bd50d82987926d60d6ed305670b97c7943d63 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/lib.rs @@ -24,7 +24,7 @@ mod imports { // Cumulus pub use emulated_integration_tests_common::xcm_emulator::{ - assert_expected_events, bx, TestExt, + assert_expected_events, bx, Chain, Parachain, TestExt, }; pub use rococo_system_emulated_network::{ coretime_rococo_emulated_chain::{ @@ -32,7 +32,7 @@ mod imports { CoretimeRococoParaPallet as CoretimeRococoPallet, }, CoretimeRococoPara as CoretimeRococo, CoretimeRococoParaReceiver as CoretimeRococoReceiver, - CoretimeRococoParaSender as CoretimeRococoSender, + CoretimeRococoParaSender as CoretimeRococoSender, RococoRelay as Rococo, }; } diff --git a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/tests/coretime_interface.rs b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/tests/coretime_interface.rs new file mode 100644 index 0000000000000000000000000000000000000000..584bce8f1df76e3298bacf731986b84c9b55ff64 --- /dev/null +++ b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/tests/coretime_interface.rs @@ -0,0 +1,235 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::imports::*; +use frame_support::traits::OnInitialize; +use pallet_broker::{ConfigRecord, Configuration, CoreAssignment, CoreMask, ScheduleItem}; +use rococo_runtime_constants::system_parachain::coretime::TIMESLICE_PERIOD; +use sp_runtime::Perbill; + +#[test] +fn transact_hardcoded_weights_are_sane() { + // There are three transacts with hardcoded weights sent from the Coretime Chain to the Relay + // Chain across the CoretimeInterface which are triggered at various points in the sales cycle. + // - Request core count - triggered directly by `start_sales` or `request_core_count` + // extrinsics. + // - Request revenue info - triggered when each timeslice is committed. + // - Assign core - triggered when an entry is encountered in the workplan for the next + // timeslice. + + // RuntimeEvent aliases to avoid warning from usage of qualified paths in assertions due to + // + type CoretimeEvent = ::RuntimeEvent; + type RelayEvent = ::RuntimeEvent; + + // Reserve a workload, configure broker and start sales. + CoretimeRococo::execute_with(|| { + // Hooks don't run in emulated tests - workaround as we need `on_initialize` to tick things + // along and have no concept of time passing otherwise. + ::Broker::on_initialize( + ::System::block_number(), + ); + + let coretime_root_origin = ::RuntimeOrigin::root(); + + // Create and populate schedule with the worst case assignment on this core. + let mut schedule = Vec::new(); + for i in 0..27 { + schedule.push(ScheduleItem { + mask: CoreMask::void().set(i), + assignment: CoreAssignment::Task(2000 + i), + }) + } + + assert_ok!(::Broker::reserve( + coretime_root_origin.clone(), + schedule.try_into().expect("Vector is within bounds."), + )); + + // Configure broker and start sales. + let config = ConfigRecord { + advance_notice: 1, + interlude_length: 1, + leadin_length: 2, + region_length: 1, + ideal_bulk_proportion: Perbill::from_percent(40), + limit_cores_offered: None, + renewal_bump: Perbill::from_percent(2), + contribution_timeout: 1, + }; + assert_ok!(::Broker::configure( + coretime_root_origin.clone(), + config + )); + assert_ok!(::Broker::start_sales( + coretime_root_origin, + 100, + 0 + )); + assert_eq!( + pallet_broker::Status::<::Runtime>::get() + .unwrap() + .core_count, + 1 + ); + + assert_expected_events!( + CoretimeRococo, + vec![ + CoretimeEvent::Broker( + pallet_broker::Event::ReservationMade { .. } + ) => {}, + CoretimeEvent::Broker( + pallet_broker::Event::CoreCountRequested { core_count: 1 } + ) => {}, + CoretimeEvent::ParachainSystem( + cumulus_pallet_parachain_system::Event::UpwardMessageSent { .. } + ) => {}, + ] + ); + }); + + // Check that the request_core_count message was processed successfully. This will fail if the + // weights are misconfigured. + Rococo::execute_with(|| { + Rococo::assert_ump_queue_processed(true, Some(CoretimeRococo::para_id()), None); + + assert_expected_events!( + Rococo, + vec![ + RelayEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, + ] + ); + }); + + // Keep track of the relay chain block number so we can fast forward while still checking the + // right block. + let mut block_number_cursor = Rococo::ext_wrapper(::System::block_number); + + let config = CoretimeRococo::ext_wrapper(|| { + Configuration::<::Runtime>::get() + .expect("Pallet was configured earlier.") + }); + + // Now run up to the block before the sale is rotated. + while block_number_cursor < TIMESLICE_PERIOD - config.advance_notice - 1 { + CoretimeRococo::execute_with(|| { + // Hooks don't run in emulated tests - workaround. + ::Broker::on_initialize( + ::System::block_number(), + ); + }); + + Rococo::ext_wrapper(|| { + block_number_cursor = ::System::block_number(); + }); + } + + // In this block we trigger assign core. + CoretimeRococo::execute_with(|| { + // Hooks don't run in emulated tests - workaround. + ::Broker::on_initialize( + ::System::block_number(), + ); + + assert_expected_events!( + CoretimeRococo, + vec![ + CoretimeEvent::Broker( + pallet_broker::Event::SaleInitialized { .. } + ) => {}, + CoretimeEvent::Broker( + pallet_broker::Event::CoreAssigned { .. } + ) => {}, + CoretimeEvent::ParachainSystem( + cumulus_pallet_parachain_system::Event::UpwardMessageSent { .. } + ) => {}, + ] + ); + }); + + // Check that the assign_core message was processed successfully. + // This will fail if the weights are misconfigured. + Rococo::execute_with(|| { + Rococo::assert_ump_queue_processed(true, Some(CoretimeRococo::para_id()), None); + + assert_expected_events!( + Rococo, + vec![ + RelayEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, + RelayEvent::Coretime( + polkadot_runtime_parachains::coretime::Event::CoreAssigned { .. } + ) => {}, + ] + ); + }); + + // In this block we trigger request revenue. + CoretimeRococo::execute_with(|| { + // Hooks don't run in emulated tests - workaround. + ::Broker::on_initialize( + ::System::block_number(), + ); + + assert_expected_events!( + CoretimeRococo, + vec![ + CoretimeEvent::ParachainSystem( + cumulus_pallet_parachain_system::Event::UpwardMessageSent { .. } + ) => {}, + ] + ); + }); + + // Check that the request_revenue_info_at message was processed successfully. + // This will fail if the weights are misconfigured. + Rococo::execute_with(|| { + Rococo::assert_ump_queue_processed(true, Some(CoretimeRococo::para_id()), None); + + assert_expected_events!( + Rococo, + vec![ + RelayEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, + ] + ); + }); + + // Here we receive and process the notify_revenue XCM with zero revenue. + CoretimeRococo::execute_with(|| { + // Hooks don't run in emulated tests - workaround. + ::Broker::on_initialize( + ::System::block_number(), + ); + + assert_expected_events!( + CoretimeRococo, + vec![ + CoretimeEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, + // Zero revenue in first timeslice so history is immediately dropped. + CoretimeEvent::Broker( + pallet_broker::Event::HistoryDropped { when: 0, revenue: 0 } + ) => {}, + ] + ); + }); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/tests/mod.rs index 0e78351bce03110a3722cf699c7c70fa9b9a5341..bb0387a4b3502bf4004255779d13c6e8dd386a91 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-rococo/src/tests/mod.rs @@ -14,3 +14,4 @@ // limitations under the License. mod claim_assets; +mod coretime_interface; diff --git a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/Cargo.toml index a8fa905d2e5ee4f1905bcf420fa135f8612c7796..d57e7926b0ec1fa6f73ed40260d8eb5976be0cd3 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "coretime-westend-integration-tests" -version = "0.1.0" +version = "0.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" @@ -12,16 +12,19 @@ publish = false # Substrate frame-support = { workspace = true } pallet-balances = { workspace = true } +pallet-broker = { workspace = true, default-features = true } pallet-message-queue = { workspace = true } pallet-identity = { workspace = true } sp-runtime = { workspace = true } # Polkadot polkadot-runtime-common = { workspace = true, default-features = true } +polkadot-runtime-parachains = { workspace = true, default-features = true } westend-runtime-constants = { workspace = true, default-features = true } xcm = { workspace = true } xcm-executor = { workspace = true } # Cumulus +cumulus-pallet-parachain-system = { workspace = true, default-features = true } emulated-integration-tests-common = { workspace = true } westend-system-emulated-network = { workspace = true } diff --git a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/lib.rs index 838ca6eeafb67db1c78159f78877a35a207c2a41..ac844e0f3284e16c7e8484ce69fcde9f04ff295b 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/lib.rs @@ -24,7 +24,7 @@ mod imports { // Cumulus pub use emulated_integration_tests_common::xcm_emulator::{ - assert_expected_events, bx, TestExt, + assert_expected_events, bx, Chain, Parachain, TestExt, }; pub use westend_system_emulated_network::{ coretime_westend_emulated_chain::{ @@ -33,7 +33,7 @@ mod imports { }, CoretimeWestendPara as CoretimeWestend, CoretimeWestendParaReceiver as CoretimeWestendReceiver, - CoretimeWestendParaSender as CoretimeWestendSender, + CoretimeWestendParaSender as CoretimeWestendSender, WestendRelay as Westend, }; } diff --git a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/tests/coretime_interface.rs b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/tests/coretime_interface.rs new file mode 100644 index 0000000000000000000000000000000000000000..f61bc4285a0cf24a2184d301da64ea48670961f0 --- /dev/null +++ b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/tests/coretime_interface.rs @@ -0,0 +1,223 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::imports::*; +use frame_support::traits::OnInitialize; +use pallet_broker::{ConfigRecord, Configuration, CoreAssignment, CoreMask, ScheduleItem}; +use sp_runtime::Perbill; +use westend_runtime_constants::system_parachain::coretime::TIMESLICE_PERIOD; + +#[test] +fn transact_hardcoded_weights_are_sane() { + // There are three transacts with hardcoded weights sent from the Coretime Chain to the Relay + // Chain across the CoretimeInterface which are triggered at various points in the sales cycle. + // - Request core count - triggered directly by `start_sales` or `request_core_count` + // extrinsics. + // - Request revenue info - triggered when each timeslice is committed. + // - Assign core - triggered when an entry is encountered in the workplan for the next + // timeslice. + + // RuntimeEvent aliases to avoid warning from usage of qualified paths in assertions due to + // + type CoretimeEvent = ::RuntimeEvent; + type RelayEvent = ::RuntimeEvent; + + // Reserve a workload, configure broker and start sales. + CoretimeWestend::execute_with(|| { + // Hooks don't run in emulated tests - workaround as we need `on_initialize` to tick things + // along and have no concept of time passing otherwise. + ::Broker::on_initialize( + ::System::block_number(), + ); + + let coretime_root_origin = ::RuntimeOrigin::root(); + + // Create and populate schedule with the worst case assignment on this core. + let mut schedule = Vec::new(); + for i in 0..27 { + schedule.push(ScheduleItem { + mask: CoreMask::void().set(i), + assignment: CoreAssignment::Task(2000 + i), + }) + } + + assert_ok!(::Broker::reserve( + coretime_root_origin.clone(), + schedule.try_into().expect("Vector is within bounds."), + )); + + // Configure broker and start sales. + let config = ConfigRecord { + advance_notice: 1, + interlude_length: 1, + leadin_length: 2, + region_length: 1, + ideal_bulk_proportion: Perbill::from_percent(40), + limit_cores_offered: None, + renewal_bump: Perbill::from_percent(2), + contribution_timeout: 1, + }; + assert_ok!(::Broker::configure( + coretime_root_origin.clone(), + config + )); + assert_ok!(::Broker::start_sales( + coretime_root_origin, + 100, + 0 + )); + assert_eq!( + pallet_broker::Status::<::Runtime>::get() + .unwrap() + .core_count, + 1 + ); + + assert_expected_events!( + CoretimeWestend, + vec![ + CoretimeEvent::Broker( + pallet_broker::Event::ReservationMade { .. } + ) => {}, + CoretimeEvent::Broker( + pallet_broker::Event::CoreCountRequested { core_count: 1 } + ) => {}, + CoretimeEvent::ParachainSystem( + cumulus_pallet_parachain_system::Event::UpwardMessageSent { .. } + ) => {}, + ] + ); + }); + + // Check that the request_core_count message was processed successfully. This will fail if the + // weights are misconfigured. + Westend::execute_with(|| { + Westend::assert_ump_queue_processed(true, Some(CoretimeWestend::para_id()), None); + + assert_expected_events!( + Westend, + vec![ + RelayEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, + ] + ); + }); + + // Keep track of the relay chain block number so we can fast forward while still checking the + // right block. + let mut block_number_cursor = Westend::ext_wrapper(::System::block_number); + + let config = CoretimeWestend::ext_wrapper(|| { + Configuration::<::Runtime>::get() + .expect("Pallet was configured earlier.") + }); + + // Now run up to the block before the sale is rotated. + while block_number_cursor < TIMESLICE_PERIOD - config.advance_notice - 1 { + CoretimeWestend::execute_with(|| { + // Hooks don't run in emulated tests - workaround. + ::Broker::on_initialize( + ::System::block_number(), + ); + }); + + Westend::ext_wrapper(|| { + block_number_cursor = ::System::block_number(); + }); + } + + // In this block we trigger assign core. + CoretimeWestend::execute_with(|| { + // Hooks don't run in emulated tests - workaround. + ::Broker::on_initialize( + ::System::block_number(), + ); + + assert_expected_events!( + CoretimeWestend, + vec![ + CoretimeEvent::Broker( + pallet_broker::Event::SaleInitialized { .. } + ) => {}, + CoretimeEvent::Broker( + pallet_broker::Event::CoreAssigned { .. } + ) => {}, + CoretimeEvent::ParachainSystem( + cumulus_pallet_parachain_system::Event::UpwardMessageSent { .. } + ) => {}, + ] + ); + }); + + // In this block we trigger request revenue. + CoretimeWestend::execute_with(|| { + // Hooks don't run in emulated tests - workaround. + ::Broker::on_initialize( + ::System::block_number(), + ); + + assert_expected_events!( + CoretimeWestend, + vec![ + CoretimeEvent::ParachainSystem( + cumulus_pallet_parachain_system::Event::UpwardMessageSent { .. } + ) => {}, + ] + ); + }); + + // Check that the assign_core and request_revenue_info_at messages were processed successfully. + // This will fail if the weights are misconfigured. + Westend::execute_with(|| { + Westend::assert_ump_queue_processed(true, Some(CoretimeWestend::para_id()), None); + + assert_expected_events!( + Westend, + vec![ + RelayEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, + RelayEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, + RelayEvent::Coretime( + polkadot_runtime_parachains::coretime::Event::CoreAssigned { .. } + ) => {}, + ] + ); + }); + + // Here we receive and process the notify_revenue XCM with zero revenue. + CoretimeWestend::execute_with(|| { + // Hooks don't run in emulated tests - workaround. + ::Broker::on_initialize( + ::System::block_number(), + ); + + assert_expected_events!( + CoretimeWestend, + vec![ + CoretimeEvent::MessageQueue( + pallet_message_queue::Event::Processed { success: true, .. } + ) => {}, + // Zero revenue in first timeslice so history is immediately dropped. + CoretimeEvent::Broker( + pallet_broker::Event::HistoryDropped { when: 0, revenue: 0 } + ) => {}, + ] + ); + }); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/tests/mod.rs index 0e78351bce03110a3722cf699c7c70fa9b9a5341..bb0387a4b3502bf4004255779d13c6e8dd386a91 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/coretime/coretime-westend/src/tests/mod.rs @@ -14,3 +14,4 @@ // limitations under the License. mod claim_assets; +mod coretime_interface; 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 44e6b3934f0e7999d15e78d0393aed129ada2455..2619ca7591d03dd2f5436bda502aec951c6a71da 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 @@ -107,7 +107,9 @@ fn limited_teleport_native_assets_from_system_para_to_relay_fails() { let delivery_fees = PeopleRococo::execute_with(|| { xcm_helpers::teleport_assets_delivery_fees::< ::XcmSender, - >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) + >( + test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest + ) }); // Sender's balance is reduced 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 83888031723ffffef24f86717741ddab1bf2598a..d9a2c23ac0c61ee1c730f44887fe99ae1dc6ffc6 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 @@ -107,7 +107,9 @@ fn limited_teleport_native_assets_from_system_para_to_relay_fails() { let delivery_fees = PeopleWestend::execute_with(|| { xcm_helpers::teleport_assets_delivery_fees::< ::XcmSender, - >(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest) + >( + test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest + ) }); // Sender's balance is reduced diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml index 0143c09036d26908b011f3319f4dda2b90dd9631..47e0983a41506f882ce0689dfe0518dad5b38376 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml @@ -14,6 +14,7 @@ codec = { features = ["derive", "max-encoded-len"], workspace = true } hex-literal = { workspace = true, default-features = true } log = { workspace = true } scale-info = { features = ["derive"], workspace = true } +serde_json = { features = ["alloc"], workspace = true } # Substrate frame-benchmarking = { optional = true, workspace = true } @@ -230,6 +231,7 @@ std = [ "primitive-types/std", "rococo-runtime-constants/std", "scale-info/std", + "serde_json/std", "snowbridge-router-primitives/std", "sp-api/std", "sp-block-builder/std", diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/genesis_config_presets.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/genesis_config_presets.rs new file mode 100644 index 0000000000000000000000000000000000000000..1dbd92d6bff0be297ba21299af3e50a10d230332 --- /dev/null +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/genesis_config_presets.rs @@ -0,0 +1,171 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! # Asset Hub Rococo Runtime genesis config presets + +use crate::*; +use alloc::{vec, vec::Vec}; +use cumulus_primitives_core::ParaId; +use hex_literal::hex; +use parachains_common::{genesis_config_helpers::*, AccountId, AuraId}; +use sp_core::{crypto::UncheckedInto, sr25519}; +use sp_genesis_builder::PresetId; +use testnet_parachains_constants::rococo::{currency::UNITS as ROC, xcm_version::SAFE_XCM_VERSION}; + +const ASSET_HUB_ROCOCO_ED: Balance = ExistentialDeposit::get(); + +fn asset_hub_rococo_genesis( + invulnerables: Vec<(AccountId, AuraId)>, + endowed_accounts: Vec, + endowment: Balance, + id: ParaId, +) -> serde_json::Value { + let config = RuntimeGenesisConfig { + balances: BalancesConfig { + balances: endowed_accounts.iter().cloned().map(|k| (k, endowment)).collect(), + }, + parachain_info: ParachainInfoConfig { parachain_id: id, ..Default::default() }, + collator_selection: CollatorSelectionConfig { + invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(), + candidacy_bond: ASSET_HUB_ROCOCO_ED * 16, + ..Default::default() + }, + session: SessionConfig { + keys: invulnerables + .into_iter() + .map(|(acc, aura)| { + ( + acc.clone(), // account id + acc, // validator id + SessionKeys { aura }, // session keys + ) + }) + .collect(), + ..Default::default() + }, + polkadot_xcm: PolkadotXcmConfig { + safe_xcm_version: Some(SAFE_XCM_VERSION), + ..Default::default() + }, + ..Default::default() + }; + + serde_json::to_value(config).expect("Could not build genesis config.") +} + +/// Encapsulates names of predefined presets. +mod preset_names { + pub const PRESET_GENESIS: &str = "genesis"; +} + +/// Provides the JSON representation of predefined genesis config for given `id`. +pub fn get_preset(id: &PresetId) -> Option> { + use preset_names::*; + let patch = match id.try_into() { + Ok(PRESET_GENESIS) => asset_hub_rococo_genesis( + // initial collators. + vec![ + // E8XC6rTJRsioKCp6KMy6zd24ykj4gWsusZ3AkSeyavpVBAG + ( + hex!("44cb62d1d6cdd2fff2a5ef3bb7ef827be5b3e117a394ecaa634d8dd9809d5608").into(), + hex!("44cb62d1d6cdd2fff2a5ef3bb7ef827be5b3e117a394ecaa634d8dd9809d5608") + .unchecked_into(), + ), + // G28iWEybndgGRbhfx83t7Q42YhMPByHpyqWDUgeyoGF94ri + ( + hex!("9864b85e23aa4506643db9879c3dbbeabaa94d269693a4447f537dd6b5893944").into(), + hex!("9864b85e23aa4506643db9879c3dbbeabaa94d269693a4447f537dd6b5893944") + .unchecked_into(), + ), + // G839e2eMiq7UXbConsY6DS1XDAYG2XnQxAmLuRLGGQ3Px9c + ( + hex!("9ce5741ee2f1ac3bdedbde9f3339048f4da2cb88ddf33a0977fa0b4cf86e2948").into(), + hex!("9ce5741ee2f1ac3bdedbde9f3339048f4da2cb88ddf33a0977fa0b4cf86e2948") + .unchecked_into(), + ), + // GLao4ukFUW6qhexuZowdFrKa2NLCfnEjZMftSXXfvGv1vvt + ( + hex!("a676ed15f5a325eab49ed8d5f8c00f3f814b19bb58cda14ad10894c078dd337f").into(), + hex!("a676ed15f5a325eab49ed8d5f8c00f3f814b19bb58cda14ad10894c078dd337f") + .unchecked_into(), + ), + ], + Vec::new(), + ASSET_HUB_ROCOCO_ED * 524_288, + 1000.into(), + ), + Ok(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET) => asset_hub_rococo_genesis( + // initial collators. + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed::("Bob"), + ), + ], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + ROC * 1_000_000, + 1000.into(), + ), + Ok(sp_genesis_builder::DEV_RUNTIME_PRESET) => asset_hub_rococo_genesis( + // initial collators. + vec![( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + )], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + ], + ROC * 1_000_000, + 1000.into(), + ), + _ => return None, + }; + + Some( + serde_json::to_string(&patch) + .expect("serialization to json is expected to work. qed.") + .into_bytes(), + ) +} + +/// List of supported presets. +pub fn preset_names() -> Vec { + use preset_names::*; + vec![ + PresetId::from(PRESET_GENESIS), + PresetId::from(sp_genesis_builder::DEV_RUNTIME_PRESET), + PresetId::from(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET), + ] +} 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 4c7356707ab61c0e3ef4c6a97f4c83d3728c5f53..367cf8a51f2cf0816055ace664fdebc8db59a27c 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -24,6 +24,7 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); +mod genesis_config_presets; mod weights; pub mod xcm_config; @@ -37,7 +38,7 @@ use assets_common::{ AssetIdForTrustBackedAssetsConvert, }; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; -use cumulus_primitives_core::AggregateMessageOrigin; +use cumulus_primitives_core::{AggregateMessageOrigin, ClaimQueueOffset, CoreSelector}; use sp_api::impl_runtime_apis; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ @@ -61,8 +62,7 @@ use frame_support::{ ord_parameter_types, parameter_types, traits::{ fungible, fungibles, tokens::imbalance::ResolveAssetTo, AsEnsureOriginWithArg, ConstBool, - ConstU128, ConstU32, ConstU64, ConstU8, EitherOfDiverse, Equals, InstanceFilter, - TransformOrigin, + ConstU128, ConstU32, ConstU64, ConstU8, EitherOfDiverse, InstanceFilter, TransformOrigin, }, weights::{ConstantMultiplier, Weight, WeightToFee as _}, BoundedVec, PalletId, @@ -123,11 +123,11 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("statemine"), impl_name: create_runtime_str!("statemine"), authoring_version: 1, - spec_version: 1_015_000, + spec_version: 1_016_001, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 16, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. @@ -213,6 +213,7 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<0>; + type DoneSlashHandler = (); } parameter_types! { @@ -265,7 +266,7 @@ impl pallet_assets::Config for Runtime { type Freezer = AssetsFreezer; type Extra = (); type WeightInfo = weights::pallet_assets_local::WeightInfo; - type CallbackHandle = (); + type CallbackHandle = pallet_assets::AutoIncAssetId; type AssetAccountDeposit = AssetAccountDeposit; type RemoveItemsLimit = frame_support::traits::ConstU32<1000>; #[cfg(feature = "runtime-benchmarks")] @@ -545,7 +546,8 @@ impl InstanceFilter for ProxyType { RuntimeCall::Utility { .. } | RuntimeCall::Multisig { .. } | RuntimeCall::NftFractionalization { .. } | - RuntimeCall::Nfts { .. } | RuntimeCall::Uniques { .. } + RuntimeCall::Nfts { .. } | + RuntimeCall::Uniques { .. } ) }, ProxyType::AssetOwner => matches!( @@ -666,6 +668,7 @@ impl cumulus_pallet_parachain_system::Config for Runtime { type ReservedXcmpWeight = ReservedXcmpWeight; type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases; type ConsensusHook = ConsensusHook; + type SelectCore = cumulus_pallet_parachain_system::DefaultCoreSelector; } type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< @@ -865,7 +868,7 @@ impl pallet_nft_fractionalization::Config for Runtime { type Assets = Assets; type Nfts = Nfts; type PalletId = NftFractionalizationPalletId; - type WeightInfo = pallet_nft_fractionalization::weights::SubstrateWeight; + type WeightInfo = weights::pallet_nft_fractionalization::WeightInfo; type RuntimeHoldReason = RuntimeHoldReason; #[cfg(feature = "runtime-benchmarks")] type BenchmarkHelper = (); @@ -915,29 +918,18 @@ impl pallet_nfts::Config for Runtime { /// consensus with dynamic fees and back-pressure. pub type ToWestendXcmRouterInstance = pallet_xcm_bridge_hub_router::Instance3; impl pallet_xcm_bridge_hub_router::Config for Runtime { + type RuntimeEvent = RuntimeEvent; type WeightInfo = weights::pallet_xcm_bridge_hub_router::WeightInfo; type UniversalLocation = xcm_config::UniversalLocation; + type SiblingBridgeHubLocation = xcm_config::bridging::SiblingBridgeHub; type BridgedNetworkId = xcm_config::bridging::to_westend::WestendNetwork; type Bridges = xcm_config::bridging::NetworkExportTable; type DestinationVersion = PolkadotXcm; - #[cfg(not(feature = "runtime-benchmarks"))] - type BridgeHubOrigin = EnsureXcm>; - #[cfg(feature = "runtime-benchmarks")] - type BridgeHubOrigin = EitherOfDiverse< - // for running benchmarks - EnsureRoot, - // for running tests with `--feature runtime-benchmarks` - EnsureXcm>, - >; - type ToBridgeHubSender = XcmpQueue; - type WithBridgeHubChannel = - cumulus_pallet_xcmp_queue::bridging::InAndOutXcmpChannelStatusProvider< - xcm_config::bridging::SiblingBridgeHubParaId, - Runtime, - >; + type LocalXcmChannelManager = + cumulus_pallet_xcmp_queue::bridging::InAndOutXcmpChannelStatusProvider; type ByteFee = xcm_config::bridging::XcmBridgeHubRouterByteFee; type FeeAsset = xcm_config::bridging::XcmBridgeHubRouterFeeAssetId; @@ -1029,6 +1021,12 @@ pub type Migrations = ( cumulus_pallet_xcmp_queue::migration::v5::MigrateV4ToV5, pallet_collator_selection::migration::v2::MigrationToV2, frame_support::migrations::RemovePallet, + // unreleased + pallet_assets::migration::next_asset_id::SetNextAssetId< + ConstU32<50_000_000>, + Runtime, + TrustBackedAssetsInstance, + >, // permanent pallet_xcm::migration::MigrateToLatestXcmVersion, ); @@ -1394,6 +1392,12 @@ impl_runtime_apis! { } } + impl cumulus_primitives_core::GetCoreSelectorApi for Runtime { + fn core_selector() -> (CoreSelector, ClaimQueueOffset) { + ParachainSystem::core_selector() + } + } + #[cfg(feature = "try-runtime")] impl frame_try_runtime::TryRuntime for Runtime { fn on_runtime_upgrade(checks: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) { @@ -1770,11 +1774,11 @@ impl_runtime_apis! { } fn get_preset(id: &Option) -> Option> { - get_preset::(id, |_| None) + get_preset::(id, &genesis_config_presets::get_preset) } fn preset_names() -> Vec { - vec![] + genesis_config_presets::preset_names() } } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/block_weights.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/block_weights.rs index e7fdb2aae2a01ec06076de83d94817e540e205dd..41e30725e753fef32404b858a873e2456618d56f 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/block_weights.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/block_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/extrinsic_weights.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/extrinsic_weights.rs index 1a4adb968bb7195428ea00d59cd92dcd3b6eea5f..3bd48f061bba079a76aa8f032ebecd6d40c2156c 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/extrinsic_weights.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/extrinsic_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_xcm_bridge_hub_router.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_xcm_bridge_hub_router.rs index 0a86037391b42d71340d8d0665a9210d8b9a0281..00ecf239428f03c9b967b975bdc5b0835ccbf259 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_xcm_bridge_hub_router.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_xcm_bridge_hub_router.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_bridge_hub_router` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-07-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-rococo-dev")`, DB CACHE: 1024 // Executed Command: @@ -52,14 +52,14 @@ impl pallet_xcm_bridge_hub_router::WeightInfo for Weigh /// Proof: `XcmpQueue::InboundXcmpSuspended` (`max_values`: Some(1), `max_size`: Some(4002), added: 4497, mode: `MaxEncodedLen`) /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) - /// Storage: `ToWestendXcmRouter::Bridge` (r:1 w:1) - /// Proof: `ToWestendXcmRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: 512, mode: `MaxEncodedLen`) + /// Storage: `ToWestendXcmRouter::DeliveryFeeFactor` (r:1 w:1) + /// Proof: `ToWestendXcmRouter::DeliveryFeeFactor` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) fn on_initialize_when_non_congested() -> Weight { // Proof Size summary in bytes: - // Measured: `154` + // Measured: `153` // Estimated: `5487` - // Minimum execution time: 8_078_000 picoseconds. - Weight::from_parts(8_455_000, 0) + // Minimum execution time: 12_993_000 picoseconds. + Weight::from_parts(13_428_000, 0) .saturating_add(Weight::from_parts(0, 5487)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -72,55 +72,9 @@ impl pallet_xcm_bridge_hub_router::WeightInfo for Weigh // Proof Size summary in bytes: // Measured: `144` // Estimated: `5487` - // Minimum execution time: 4_291_000 picoseconds. - Weight::from_parts(4_548_000, 0) + // Minimum execution time: 6_305_000 picoseconds. + Weight::from_parts(6_536_000, 0) .saturating_add(Weight::from_parts(0, 5487)) .saturating_add(T::DbWeight::get().reads(2)) } - /// Storage: `ToWestendXcmRouter::Bridge` (r:1 w:1) - /// Proof: `ToWestendXcmRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: 512, mode: `MaxEncodedLen`) - fn report_bridge_status() -> Weight { - // Proof Size summary in bytes: - // Measured: `150` - // Estimated: `1502` - // Minimum execution time: 9_959_000 picoseconds. - Weight::from_parts(10_372_000, 0) - .saturating_add(Weight::from_parts(0, 1502)) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) - } - /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) - /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: UNKNOWN KEY `0x3302afcb67e838a3f960251b417b9a4f` (r:1 w:0) - /// Proof: UNKNOWN KEY `0x3302afcb67e838a3f960251b417b9a4f` (r:1 w:0) - /// Storage: UNKNOWN KEY `0x0973fe64c85043ba1c965cbc38eb63c7` (r:1 w:0) - /// Proof: UNKNOWN KEY `0x0973fe64c85043ba1c965cbc38eb63c7` (r:1 w:0) - /// Storage: `ToWestendXcmRouter::Bridge` (r:1 w:1) - /// Proof: `ToWestendXcmRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: 512, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::DeliveryFeeFactor` (r:1 w:0) - /// Proof: `XcmpQueue::DeliveryFeeFactor` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) - /// Storage: `PolkadotXcm::SupportedVersion` (r:2 w:0) - /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) - /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) - /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainSystem::RelevantMessagingState` (r:1 w:0) - /// Proof: `ParachainSystem::RelevantMessagingState` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:1) - /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::InboundXcmpSuspended` (r:1 w:0) - /// Proof: `XcmpQueue::InboundXcmpSuspended` (`max_values`: Some(1), `max_size`: Some(4002), added: 4497, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::OutboundXcmpMessages` (r:0 w:1) - /// Proof: `XcmpQueue::OutboundXcmpMessages` (`max_values`: None, `max_size`: Some(105506), added: 107981, mode: `MaxEncodedLen`) - fn send_message() -> Weight { - // Proof Size summary in bytes: - // Measured: `448` - // Estimated: `6388` - // Minimum execution time: 45_888_000 picoseconds. - Weight::from_parts(47_022_000, 0) - .saturating_add(Weight::from_parts(0, 6388)) - .saturating_add(T::DbWeight::get().reads(12)) - .saturating_add(T::DbWeight::get().writes(4)) - } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/paritydb_weights.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/paritydb_weights.rs index 25679703831a13b8d1bb7fb7dd4d92fa84b1f255..e0b1985c659c78b66176423f6bcc64d546b39324 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/paritydb_weights.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/paritydb_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/rocksdb_weights.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/rocksdb_weights.rs index 3dd817aa6f137085b0e5fdf2b11b7f50e5c8b002..c6e91b2fcffbf65e3fdce2bc8fbac19a605d00aa 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/rocksdb_weights.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/rocksdb_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index 3d6ae6ddd1d210b5a94d39237d67d4378e9bedec..7478ba8893c1b609d7d950a6e0e39ed3d443a730 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("asset-hub-rococo-dev"), DB CACHE: 1024 @@ -54,8 +54,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 34_180_000 picoseconds. - Weight::from_parts(34_745_000, 3593) + // Minimum execution time: 34_364_000 picoseconds. + Weight::from_parts(35_040_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -65,8 +65,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `6196` - // Minimum execution time: 42_638_000 picoseconds. - Weight::from_parts(43_454_000, 6196) + // Minimum execution time: 42_755_000 picoseconds. + Weight::from_parts(43_650_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -90,8 +90,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `8799` - // Minimum execution time: 102_916_000 picoseconds. - Weight::from_parts(105_699_000, 8799) + // Minimum execution time: 103_037_000 picoseconds. + Weight::from_parts(105_732_000, 8799) .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -99,8 +99,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_805_000 picoseconds. - Weight::from_parts(1_901_000, 0) + // Minimum execution time: 1_095_000 picoseconds. + Weight::from_parts(1_220_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -122,8 +122,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 108_018_000 picoseconds. - Weight::from_parts(110_310_000, 6196) + // Minimum execution time: 108_117_000 picoseconds. + Weight::from_parts(110_416_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -131,8 +131,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_507_000 picoseconds. - Weight::from_parts(3_724_000, 0) + // Minimum execution time: 2_907_000 picoseconds. + Weight::from_parts(3_050_000, 0) } // Storage: `System::Account` (r:1 w:1) // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -140,8 +140,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 26_269_000 picoseconds. - Weight::from_parts(26_706_000, 3593) + // Minimum execution time: 24_965_000 picoseconds. + Weight::from_parts(25_687_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -165,8 +165,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `6196` - // Minimum execution time: 84_759_000 picoseconds. - Weight::from_parts(86_157_000, 6196) + // Minimum execution time: 83_312_000 picoseconds. + Weight::from_parts(85_463_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -190,8 +190,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 50_876_000 picoseconds. - Weight::from_parts(51_512_000, 3610) + // Minimum execution time: 49_874_000 picoseconds. + Weight::from_parts(51_165_000, 3610) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index bee6bcdf21cf3fed11ef63d593b5b0dfb1f0f10b..f6a883c03e9dd53124a229a827d5732f4973cd1b 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -16,10 +16,10 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-11-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-yprdrvc7-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("asset-hub-rococo-dev"), DB CACHE: 1024 // Executed Command: @@ -68,8 +68,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 440_298_000 picoseconds. - Weight::from_parts(446_508_000, 6196) + // Minimum execution time: 99_552_000 picoseconds. + Weight::from_parts(101_720_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -77,8 +77,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_313_000 picoseconds. - Weight::from_parts(3_422_000, 0) + // Minimum execution time: 659_000 picoseconds. + Weight::from_parts(706_000, 0) } // Storage: `PolkadotXcm::Queries` (r:1 w:0) // Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -86,58 +86,58 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3568` - // Minimum execution time: 9_691_000 picoseconds. - Weight::from_parts(9_948_000, 3568) + // Minimum execution time: 9_665_000 picoseconds. + Weight::from_parts(9_878_000, 3568) .saturating_add(T::DbWeight::get().reads(1)) } pub fn transact() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 10_384_000 picoseconds. - Weight::from_parts(11_085_000, 0) + // Minimum execution time: 6_959_000 picoseconds. + Weight::from_parts(7_111_000, 0) } pub fn refund_surplus() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_438_000 picoseconds. - Weight::from_parts(3_577_000, 0) + // Minimum execution time: 2_682_000 picoseconds. + Weight::from_parts(2_799_000, 0) } pub fn set_error_handler() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_126_000 picoseconds. - Weight::from_parts(2_243_000, 0) + // Minimum execution time: 656_000 picoseconds. + Weight::from_parts(683_000, 0) } pub fn set_appendix() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_126_000 picoseconds. - Weight::from_parts(2_207_000, 0) + // Minimum execution time: 687_000 picoseconds. + Weight::from_parts(719_000, 0) } pub fn clear_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_105_000 picoseconds. - Weight::from_parts(2_193_000, 0) + // Minimum execution time: 588_000 picoseconds. + Weight::from_parts(653_000, 0) } pub fn descend_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_999_000 picoseconds. - Weight::from_parts(3_056_000, 0) + // Minimum execution time: 690_000 picoseconds. + Weight::from_parts(714_000, 0) } pub fn clear_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_091_000 picoseconds. - Weight::from_parts(2_176_000, 0) + // Minimum execution time: 671_000 picoseconds. + Weight::from_parts(710_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -159,8 +159,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 55_728_000 picoseconds. - Weight::from_parts(56_704_000, 6196) + // Minimum execution time: 67_374_000 picoseconds. + Weight::from_parts(68_899_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -170,8 +170,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `160` // Estimated: `3625` - // Minimum execution time: 12_839_000 picoseconds. - Weight::from_parts(13_457_000, 3625) + // Minimum execution time: 12_896_000 picoseconds. + Weight::from_parts(13_191_000, 3625) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -179,8 +179,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_116_000 picoseconds. - Weight::from_parts(2_219_000, 0) + // Minimum execution time: 634_000 picoseconds. + Weight::from_parts(677_000, 0) } // Storage: `PolkadotXcm::VersionNotifyTargets` (r:1 w:1) // Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -200,8 +200,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 24_891_000 picoseconds. - Weight::from_parts(25_583_000, 3610) + // Minimum execution time: 28_197_000 picoseconds. + Weight::from_parts(28_752_000, 3610) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -211,44 +211,44 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_968_000 picoseconds. - Weight::from_parts(4_122_000, 0) + // Minimum execution time: 2_678_000 picoseconds. + Weight::from_parts(2_803_000, 0) .saturating_add(T::DbWeight::get().writes(1)) } pub fn burn_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 136_220_000 picoseconds. - Weight::from_parts(137_194_000, 0) + // Minimum execution time: 22_806_000 picoseconds. + Weight::from_parts(23_217_000, 0) } pub fn expect_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 12_343_000 picoseconds. - Weight::from_parts(12_635_000, 0) + // Minimum execution time: 6_221_000 picoseconds. + Weight::from_parts(6_347_000, 0) } pub fn expect_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_237_000 picoseconds. - Weight::from_parts(2_315_000, 0) + // Minimum execution time: 653_000 picoseconds. + Weight::from_parts(676_000, 0) } pub fn expect_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_094_000 picoseconds. - Weight::from_parts(2_231_000, 0) + // Minimum execution time: 621_000 picoseconds. + Weight::from_parts(678_000, 0) } pub fn expect_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_379_000 picoseconds. - Weight::from_parts(2_455_000, 0) + // Minimum execution time: 770_000 picoseconds. + Weight::from_parts(829_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -270,8 +270,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 60_734_000 picoseconds. - Weight::from_parts(61_964_000, 6196) + // Minimum execution time: 71_654_000 picoseconds. + Weight::from_parts(73_329_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -279,8 +279,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_500_000 picoseconds. - Weight::from_parts(5_720_000, 0) + // Minimum execution time: 3_999_000 picoseconds. + Weight::from_parts(4_179_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -302,8 +302,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 55_767_000 picoseconds. - Weight::from_parts(56_790_000, 6196) + // Minimum execution time: 66_722_000 picoseconds. + Weight::from_parts(68_812_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -311,22 +311,22 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_201_000 picoseconds. - Weight::from_parts(2_291_000, 0) + // Minimum execution time: 718_000 picoseconds. + Weight::from_parts(745_000, 0) } pub fn set_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_164_000 picoseconds. - Weight::from_parts(2_241_000, 0) + // Minimum execution time: 623_000 picoseconds. + Weight::from_parts(682_000, 0) } pub fn clear_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_127_000 picoseconds. - Weight::from_parts(2_236_000, 0) + // Minimum execution time: 664_000 picoseconds. + Weight::from_parts(696_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -334,22 +334,22 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1489` - // Minimum execution time: 4_275_000 picoseconds. - Weight::from_parts(4_381_000, 1489) + // Minimum execution time: 2_495_000 picoseconds. + Weight::from_parts(2_604_000, 1489) .saturating_add(T::DbWeight::get().reads(1)) } pub fn set_fees_mode() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_132_000 picoseconds. - Weight::from_parts(2_216_000, 0) + // Minimum execution time: 645_000 picoseconds. + Weight::from_parts(673_000, 0) } pub fn unpaid_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_265_000 picoseconds. - Weight::from_parts(2_332_000, 0) + // Minimum execution time: 643_000 picoseconds. + Weight::from_parts(701_000, 0) } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs index 2d1914e059bf7a95f629a821ce885fb02faa02a1..fab0141e6d41a6923f85b9fb363e03471abe1028 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs @@ -21,7 +21,7 @@ use super::{ XcmpQueue, }; use assets_common::{ - matching::{FromNetwork, FromSiblingParachain, IsForeignConcreteAsset}, + matching::{FromNetwork, FromSiblingParachain, IsForeignConcreteAsset, ParentLocation}, TrustBackedAssetsAsLocation, }; use frame_support::{ @@ -43,7 +43,7 @@ use parachains_common::{ use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::xcm_sender::ExponentialPrice; use snowbridge_router_primitives::inbound::GlobalConsensusEthereumConvertsFor; -use sp_runtime::traits::{AccountIdConversion, ConvertInto}; +use sp_runtime::traits::{AccountIdConversion, ConvertInto, TryConvertInto}; use testnet_parachains_constants::rococo::snowbridge::{ EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX, }; @@ -54,12 +54,13 @@ use xcm_builder::{ DenyReserveTransferToRelayChain, DenyThenTry, DescribeAllTerminal, DescribeFamily, EnsureXcmOrigin, FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, GlobalConsensusParachainConvertsFor, HashedDescription, IsConcrete, LocalMint, - NetworkExportTableItem, NoChecking, NonFungiblesAdapter, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignPaidRemoteExporter, + MatchedConvertedConcreteId, NetworkExportTableItem, NoChecking, NonFungiblesAdapter, + ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SingleAssetExchangeAdapter, SovereignPaidRemoteExporter, SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, TakeWeightCredit, - TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, - XcmFeeManagerFromComponents, + TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, + WithLatestLocationConverter, WithUniqueTopic, XcmFeeManagerFromComponents, }; use xcm_executor::XcmExecutor; @@ -326,6 +327,28 @@ pub type TrustedTeleporters = ( IsForeignConcreteAsset>>, ); +/// Asset converter for pool assets. +/// Used to convert one asset to another, when there is a pool available between the two. +/// This type thus allows paying fees with any asset as long as there is a pool between said +/// asset and the asset required for fee payment. +pub type PoolAssetsExchanger = SingleAssetExchangeAdapter< + crate::AssetConversion, + crate::NativeAndAssets, + ( + TrustBackedAssetsAsLocation, + ForeignAssetsConvertedConcreteId, + // `ForeignAssetsConvertedConcreteId` excludes the relay token, so we add it back here. + MatchedConvertedConcreteId< + xcm::v4::Location, + Balance, + Equals, + WithLatestLocationConverter, + TryConvertInto, + >, + ), + AccountId, +>; + pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { type RuntimeCall = RuntimeCall; @@ -407,7 +430,7 @@ impl xcm_executor::Config for XcmConfig { type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type AssetLocker = (); - type AssetExchanger = (); + type AssetExchanger = PoolAssetsExchanger; type FeeManager = XcmFeeManagerFromComponents< WaivedLocations, SendXcmFeeToAccount, @@ -607,17 +630,6 @@ pub mod bridging { /// Allow any asset native to the Westend ecosystem if it comes from Westend Asset Hub. pub type WestendAssetFromAssetHubWestend = matching::RemoteAssetFromLocation, AssetHubWestend>; - - impl Contains for ToWestendXcmRouter { - fn contains(call: &RuntimeCall) -> bool { - matches!( - call, - RuntimeCall::ToWestendXcmRouter( - pallet_xcm_bridge_hub_router::Call::report_bridge_status { .. } - ) - ) - } - } } pub mod to_ethereum { diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs index 83f4f9ec3dc5da6cde0edfec2ace179eb87c7fc1..6e10f9168990114d6518ebeaf832c3a57801b308 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs @@ -27,7 +27,7 @@ use asset_hub_rococo_runtime::{ AllPalletsWithoutSystem, AssetConversion, AssetDeposit, Assets, Balances, CollatorSelection, ExistentialDeposit, ForeignAssets, ForeignAssetsInstance, MetadataDepositBase, MetadataDepositPerByte, ParachainSystem, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, - SessionKeys, ToWestendXcmRouterInstance, TrustBackedAssetsInstance, XcmpQueue, + SessionKeys, TrustBackedAssetsInstance, XcmpQueue, }; use asset_test_utils::{ test_cases_over_bridge::TestBridgingConfig, CollatorSessionKey, CollatorSessionKeys, @@ -48,12 +48,14 @@ use frame_support::{ }; use parachains_common::{AccountId, AssetIdForTrustBackedAssets, AuraId, Balance}; use sp_consensus_aura::SlotDuration; +use sp_core::crypto::Ss58Codec; use sp_runtime::traits::MaybeEquivalence; use std::convert::Into; use testnet_parachains_constants::rococo::{consensus::*, currency::UNITS, fee::WeightToFee}; use xcm::latest::prelude::{Assets as XcmAssets, *}; use xcm_builder::WithLatestLocationConverter; use xcm_executor::traits::{JustTry, WeightTrader}; +use xcm_runtime_apis::conversions::LocationToAccountHelper; const ALICE: [u8; 32] = [1u8; 32]; const SOME_ASSET_ADMIN: [u8; 32] = [5u8; 32]; @@ -1235,94 +1237,6 @@ mod asset_hub_rococo_tests { ) } - #[test] - fn report_bridge_status_from_xcm_bridge_router_for_westend_works() { - asset_test_utils::test_cases_over_bridge::report_bridge_status_from_xcm_bridge_router_works::< - Runtime, - AllPalletsWithoutSystem, - XcmConfig, - LocationToAccountId, - ToWestendXcmRouterInstance, - >( - collator_session_keys(), - bridging_to_asset_hub_westend, - || { - vec![ - UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - Transact { - origin_kind: OriginKind::Xcm, - require_weight_at_most: - bp_asset_hub_rococo::XcmBridgeHubRouterTransactCallMaxWeight::get(), - call: bp_asset_hub_rococo::Call::ToWestendXcmRouter( - bp_asset_hub_rococo::XcmBridgeHubRouterCall::report_bridge_status { - bridge_id: Default::default(), - is_congested: true, - }, - ) - .encode() - .into(), - }, - ] - .into() - }, - || { - vec![ - UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - Transact { - origin_kind: OriginKind::Xcm, - require_weight_at_most: - bp_asset_hub_rococo::XcmBridgeHubRouterTransactCallMaxWeight::get(), - call: bp_asset_hub_rococo::Call::ToWestendXcmRouter( - bp_asset_hub_rococo::XcmBridgeHubRouterCall::report_bridge_status { - bridge_id: Default::default(), - is_congested: false, - }, - ) - .encode() - .into(), - }, - ] - .into() - }, - ) - } - - #[test] - fn test_report_bridge_status_call_compatibility() { - // if this test fails, make sure `bp_asset_hub_rococo` has valid encoding - assert_eq!( - RuntimeCall::ToWestendXcmRouter( - pallet_xcm_bridge_hub_router::Call::report_bridge_status { - bridge_id: Default::default(), - is_congested: true, - } - ) - .encode(), - bp_asset_hub_rococo::Call::ToWestendXcmRouter( - bp_asset_hub_rococo::XcmBridgeHubRouterCall::report_bridge_status { - bridge_id: Default::default(), - is_congested: true, - } - ) - .encode() - ); - } - - #[test] - fn check_sane_weight_report_bridge_status_for_westend() { - use pallet_xcm_bridge_hub_router::WeightInfo; - let actual = >::WeightInfo::report_bridge_status(); - let max_weight = bp_asset_hub_rococo::XcmBridgeHubRouterTransactCallMaxWeight::get(); - assert!( - actual.all_lte(max_weight), - "max_weight: {:?} should be adjusted to actual {:?}", - max_weight, - actual - ); - } - #[test] fn reserve_transfer_native_asset_to_non_teleport_para_works() { asset_test_utils::test_cases::reserve_transfer_native_asset_to_non_teleport_para_works::< @@ -1443,3 +1357,112 @@ fn change_xcm_bridge_hub_ethereum_base_fee_by_governance_works() { }, ) } + +#[test] +fn location_conversion_works() { + // the purpose of hardcoded values is to catch an unintended location conversion logic change. + struct TestCase { + description: &'static str, + location: Location, + expected_account_id_str: &'static str, + } + + let test_cases = vec![ + // DescribeTerminus + TestCase { + description: "DescribeTerminus Parent", + location: Location::new(1, Here), + expected_account_id_str: "5Dt6dpkWPwLaH4BBCKJwjiWrFVAGyYk3tLUabvyn4v7KtESG", + }, + TestCase { + description: "DescribeTerminus Sibling", + location: Location::new(1, [Parachain(1111)]), + expected_account_id_str: "5Eg2fnssmmJnF3z1iZ1NouAuzciDaaDQH7qURAy3w15jULDk", + }, + // DescribePalletTerminal + TestCase { + description: "DescribePalletTerminal Parent", + location: Location::new(1, [PalletInstance(50)]), + expected_account_id_str: "5CnwemvaAXkWFVwibiCvf2EjqwiqBi29S5cLLydZLEaEw6jZ", + }, + TestCase { + description: "DescribePalletTerminal Sibling", + location: Location::new(1, [Parachain(1111), PalletInstance(50)]), + expected_account_id_str: "5GFBgPjpEQPdaxEnFirUoa51u5erVx84twYxJVuBRAT2UP2g", + }, + // DescribeAccountId32Terminal + TestCase { + description: "DescribeAccountId32Terminal Parent", + location: Location::new( + 1, + [AccountId32 { network: None, id: AccountId::from(ALICE).into() }], + ), + expected_account_id_str: "5DN5SGsuUG7PAqFL47J9meViwdnk9AdeSWKFkcHC45hEzVz4", + }, + TestCase { + description: "DescribeAccountId32Terminal Sibling", + location: Location::new( + 1, + [ + Parachain(1111), + Junction::AccountId32 { network: None, id: AccountId::from(ALICE).into() }, + ], + ), + expected_account_id_str: "5DGRXLYwWGce7wvm14vX1Ms4Vf118FSWQbJkyQigY2pfm6bg", + }, + // DescribeAccountKey20Terminal + TestCase { + description: "DescribeAccountKey20Terminal Parent", + location: Location::new(1, [AccountKey20 { network: None, key: [0u8; 20] }]), + expected_account_id_str: "5F5Ec11567pa919wJkX6VHtv2ZXS5W698YCW35EdEbrg14cg", + }, + TestCase { + description: "DescribeAccountKey20Terminal Sibling", + location: Location::new( + 1, + [Parachain(1111), AccountKey20 { network: None, key: [0u8; 20] }], + ), + expected_account_id_str: "5CB2FbUds2qvcJNhDiTbRZwiS3trAy6ydFGMSVutmYijpPAg", + }, + // DescribeTreasuryVoiceTerminal + TestCase { + description: "DescribeTreasuryVoiceTerminal Parent", + location: Location::new(1, [Plurality { id: BodyId::Treasury, part: BodyPart::Voice }]), + expected_account_id_str: "5CUjnE2vgcUCuhxPwFoQ5r7p1DkhujgvMNDHaF2bLqRp4D5F", + }, + TestCase { + description: "DescribeTreasuryVoiceTerminal Sibling", + location: Location::new( + 1, + [Parachain(1111), Plurality { id: BodyId::Treasury, part: BodyPart::Voice }], + ), + expected_account_id_str: "5G6TDwaVgbWmhqRUKjBhRRnH4ry9L9cjRymUEmiRsLbSE4gB", + }, + // DescribeBodyTerminal + TestCase { + description: "DescribeBodyTerminal Parent", + location: Location::new(1, [Plurality { id: BodyId::Unit, part: BodyPart::Voice }]), + expected_account_id_str: "5EBRMTBkDisEXsaN283SRbzx9Xf2PXwUxxFCJohSGo4jYe6B", + }, + TestCase { + description: "DescribeBodyTerminal Sibling", + location: Location::new( + 1, + [Parachain(1111), Plurality { id: BodyId::Unit, part: BodyPart::Voice }], + ), + expected_account_id_str: "5DBoExvojy8tYnHgLL97phNH975CyT45PWTZEeGoBZfAyRMH", + }, + ]; + + for tc in test_cases { + let expected = + AccountId::from_string(tc.expected_account_id_str).expect("Invalid AccountId string"); + + let got = LocationToAccountHelper::::convert_location( + tc.location.into(), + ) + .unwrap(); + + assert_eq!(got, expected, "{}", tc.description); + } +} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml index 77130ff846b5ad32dd760dd9364c9685dd619193..1434c3e3b6016af13339f348c8ec3a838e6d3d6e 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml @@ -14,6 +14,7 @@ codec = { features = ["derive", "max-encoded-len"], workspace = true } hex-literal = { workspace = true, default-features = true } log = { workspace = true } scale-info = { features = ["derive"], workspace = true } +serde_json = { features = ["alloc"], workspace = true } # Substrate frame-benchmarking = { optional = true, workspace = true } @@ -233,6 +234,7 @@ std = [ "polkadot-runtime-common/std", "primitive-types/std", "scale-info/std", + "serde_json/std", "snowbridge-router-primitives/std", "sp-api/std", "sp-block-builder/std", diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/genesis_config_presets.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/genesis_config_presets.rs new file mode 100644 index 0000000000000000000000000000000000000000..b287dcd56219aa13cbfb72f7e3a3e5681581ea91 --- /dev/null +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/genesis_config_presets.rs @@ -0,0 +1,169 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! # Asset Hub Westend Runtime genesis config presets + +use crate::*; +use alloc::{vec, vec::Vec}; +use cumulus_primitives_core::ParaId; +use hex_literal::hex; +use parachains_common::{genesis_config_helpers::*, AccountId, AuraId}; +use sp_core::{crypto::UncheckedInto, sr25519}; +use sp_genesis_builder::PresetId; +use testnet_parachains_constants::westend::{ + currency::UNITS as WND, xcm_version::SAFE_XCM_VERSION, +}; + +const ASSET_HUB_WESTEND_ED: Balance = ExistentialDeposit::get(); + +fn asset_hub_westend_genesis( + invulnerables: Vec<(AccountId, AuraId)>, + endowed_accounts: Vec, + endowment: Balance, + id: ParaId, +) -> serde_json::Value { + let config = RuntimeGenesisConfig { + balances: BalancesConfig { + balances: endowed_accounts.iter().cloned().map(|k| (k, endowment)).collect(), + }, + parachain_info: ParachainInfoConfig { parachain_id: id, ..Default::default() }, + collator_selection: CollatorSelectionConfig { + invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(), + candidacy_bond: ASSET_HUB_WESTEND_ED * 16, + ..Default::default() + }, + session: SessionConfig { + keys: invulnerables + .into_iter() + .map(|(acc, aura)| { + ( + acc.clone(), // account id + acc, // validator id + SessionKeys { aura }, // session keys + ) + }) + .collect(), + ..Default::default() + }, + polkadot_xcm: PolkadotXcmConfig { + safe_xcm_version: Some(SAFE_XCM_VERSION), + ..Default::default() + }, + ..Default::default() + }; + + serde_json::to_value(config).expect("Could not build genesis config.") +} + +/// Encapsulates names of predefined presets. +mod preset_names { + pub const PRESET_GENESIS: &str = "genesis"; +} + +/// Provides the JSON representation of predefined genesis config for given `id`. +pub fn get_preset(id: &PresetId) -> Option> { + use preset_names::*; + let patch = match id.try_into() { + Ok(PRESET_GENESIS) => asset_hub_westend_genesis( + // initial collators. + vec![ + ( + hex!("9cfd429fa002114f33c1d3e211501d62830c9868228eb3b4b8ae15a83de04325").into(), + hex!("9cfd429fa002114f33c1d3e211501d62830c9868228eb3b4b8ae15a83de04325") + .unchecked_into(), + ), + ( + hex!("12a03fb4e7bda6c9a07ec0a11d03c24746943e054ff0bb04938970104c783876").into(), + hex!("12a03fb4e7bda6c9a07ec0a11d03c24746943e054ff0bb04938970104c783876") + .unchecked_into(), + ), + ( + hex!("1256436307dfde969324e95b8c62cb9101f520a39435e6af0f7ac07b34e1931f").into(), + hex!("1256436307dfde969324e95b8c62cb9101f520a39435e6af0f7ac07b34e1931f") + .unchecked_into(), + ), + ( + hex!("98102b7bca3f070f9aa19f58feed2c0a4e107d203396028ec17a47e1ed80e322").into(), + hex!("98102b7bca3f070f9aa19f58feed2c0a4e107d203396028ec17a47e1ed80e322") + .unchecked_into(), + ), + ], + Vec::new(), + ASSET_HUB_WESTEND_ED * 4096, + 1000.into(), + ), + Ok(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET) => asset_hub_westend_genesis( + // initial collators. + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed::("Bob"), + ), + ], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + WND * 1_000_000, + 1000.into(), + ), + Ok(sp_genesis_builder::DEV_RUNTIME_PRESET) => asset_hub_westend_genesis( + // initial collators. + vec![( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + )], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + ], + WND * 1_000_000, + 1000.into(), + ), + _ => return None, + }; + + Some( + serde_json::to_string(&patch) + .expect("serialization to json is expected to work. qed.") + .into_bytes(), + ) +} + +/// List of supported presets. +pub fn preset_names() -> Vec { + use preset_names::*; + vec![ + PresetId::from(PRESET_GENESIS), + PresetId::from(sp_genesis_builder::DEV_RUNTIME_PRESET), + PresetId::from(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET), + ] +} 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 ebbc000d1413883037ccab3048a36c0f8a2b8ec3..bb77f576e20dade2c4250c61c795afa9bda2cf08 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -24,6 +24,7 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); +mod genesis_config_presets; mod weights; pub mod xcm_config; @@ -36,7 +37,7 @@ use assets_common::{ }; use codec::{Decode, Encode, MaxEncodedLen}; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; -use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; +use cumulus_primitives_core::{AggregateMessageOrigin, ClaimQueueOffset, CoreSelector, ParaId}; use frame_support::{ construct_runtime, derive_impl, dispatch::DispatchClass, @@ -45,8 +46,8 @@ use frame_support::{ traits::{ fungible, fungibles, tokens::{imbalance::ResolveAssetTo, nonfungibles_v2::Inspect}, - AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, Equals, - InstanceFilter, TransformOrigin, + AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, InstanceFilter, + TransformOrigin, }, weights::{ConstantMultiplier, Weight, WeightToFee as _}, BoundedVec, PalletId, @@ -57,7 +58,6 @@ use frame_system::{ }; use pallet_asset_conversion_tx_payment::SwapAssetAdapter; use pallet_nfts::{DestroyWitness, PalletFeatures}; -use pallet_xcm::EnsureXcm; use parachains_common::{ impls::DealWithFees, message_queue::*, AccountId, AssetIdForTrustBackedAssets, AuraId, Balance, BlockNumber, CollectionId, Hash, Header, ItemId, Nonce, Signature, AVERAGE_ON_INITIALIZE_RATIO, @@ -123,11 +123,11 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("westmint"), impl_name: create_runtime_str!("westmint"), authoring_version: 1, - spec_version: 1_015_000, + spec_version: 1_016_001, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 16, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. @@ -213,6 +213,7 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<0>; + type DoneSlashHandler = (); } parameter_types! { @@ -264,7 +265,7 @@ impl pallet_assets::Config for Runtime { type Freezer = AssetsFreezer; type Extra = (); type WeightInfo = weights::pallet_assets_local::WeightInfo; - type CallbackHandle = (); + type CallbackHandle = pallet_assets::AutoIncAssetId; type AssetAccountDeposit = AssetAccountDeposit; type RemoveItemsLimit = ConstU32<1000>; #[cfg(feature = "runtime-benchmarks")] @@ -543,7 +544,8 @@ impl InstanceFilter for ProxyType { RuntimeCall::Utility { .. } | RuntimeCall::Multisig { .. } | RuntimeCall::NftFractionalization { .. } | - RuntimeCall::Nfts { .. } | RuntimeCall::Uniques { .. } + RuntimeCall::Nfts { .. } | + RuntimeCall::Uniques { .. } ) }, ProxyType::AssetOwner => matches!( @@ -664,6 +666,7 @@ impl cumulus_pallet_parachain_system::Config for Runtime { type ReservedXcmpWeight = ReservedXcmpWeight; type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases; type ConsensusHook = ConsensusHook; + type SelectCore = cumulus_pallet_parachain_system::DefaultCoreSelector; } type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< @@ -858,7 +861,7 @@ impl pallet_nft_fractionalization::Config for Runtime { type Assets = Assets; type Nfts = Nfts; type PalletId = NftFractionalizationPalletId; - type WeightInfo = pallet_nft_fractionalization::weights::SubstrateWeight; + type WeightInfo = weights::pallet_nft_fractionalization::WeightInfo; type RuntimeHoldReason = RuntimeHoldReason; #[cfg(feature = "runtime-benchmarks")] type BenchmarkHelper = (); @@ -908,29 +911,18 @@ impl pallet_nfts::Config for Runtime { /// consensus with dynamic fees and back-pressure. pub type ToRococoXcmRouterInstance = pallet_xcm_bridge_hub_router::Instance1; impl pallet_xcm_bridge_hub_router::Config for Runtime { + type RuntimeEvent = RuntimeEvent; type WeightInfo = weights::pallet_xcm_bridge_hub_router::WeightInfo; type UniversalLocation = xcm_config::UniversalLocation; + type SiblingBridgeHubLocation = xcm_config::bridging::SiblingBridgeHub; type BridgedNetworkId = xcm_config::bridging::to_rococo::RococoNetwork; type Bridges = xcm_config::bridging::NetworkExportTable; type DestinationVersion = PolkadotXcm; - #[cfg(not(feature = "runtime-benchmarks"))] - type BridgeHubOrigin = EnsureXcm>; - #[cfg(feature = "runtime-benchmarks")] - type BridgeHubOrigin = frame_support::traits::EitherOfDiverse< - // for running benchmarks - EnsureRoot, - // for running tests with `--feature runtime-benchmarks` - EnsureXcm>, - >; - type ToBridgeHubSender = XcmpQueue; - type WithBridgeHubChannel = - cumulus_pallet_xcmp_queue::bridging::InAndOutXcmpChannelStatusProvider< - xcm_config::bridging::SiblingBridgeHubParaId, - Runtime, - >; + type LocalXcmChannelManager = + cumulus_pallet_xcmp_queue::bridging::InAndOutXcmpChannelStatusProvider; type ByteFee = xcm_config::bridging::XcmBridgeHubRouterByteFee; type FeeAsset = xcm_config::bridging::XcmBridgeHubRouterFeeAssetId; @@ -1033,6 +1025,12 @@ pub type Migrations = ( // unreleased cumulus_pallet_xcmp_queue::migration::v4::MigrationToV4, cumulus_pallet_xcmp_queue::migration::v5::MigrateV4ToV5, + // unreleased + pallet_assets::migration::next_asset_id::SetNextAssetId< + ConstU32<50_000_000>, + Runtime, + TrustBackedAssetsInstance, + >, // permanent pallet_xcm::migration::MigrateToLatestXcmVersion, ); @@ -1490,6 +1488,12 @@ impl_runtime_apis! { } } + impl cumulus_primitives_core::GetCoreSelectorApi for Runtime { + fn core_selector() -> (CoreSelector, ClaimQueueOffset) { + ParachainSystem::core_selector() + } + } + #[cfg(feature = "try-runtime")] impl frame_try_runtime::TryRuntime for Runtime { fn on_runtime_upgrade(checks: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) { @@ -1866,11 +1870,11 @@ impl_runtime_apis! { } fn get_preset(id: &Option) -> Option> { - get_preset::(id, |_| None) + get_preset::(id, &genesis_config_presets::get_preset) } fn preset_names() -> Vec { - vec![] + genesis_config_presets::preset_names() } } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/block_weights.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/block_weights.rs index e7fdb2aae2a01ec06076de83d94817e540e205dd..41e30725e753fef32404b858a873e2456618d56f 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/block_weights.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/block_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/cumulus_pallet_parachain_system.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/cumulus_pallet_parachain_system.rs index fc63a0814d0a4af4ac9e264db8757e5abe2da36d..ef1a6a41cef9ad47ed849b1fc1053b045320782c 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/cumulus_pallet_parachain_system.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/cumulus_pallet_parachain_system.rs @@ -77,4 +77,4 @@ impl cumulus_pallet_parachain_system::WeightInfo for We .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(4)) } -} +} \ No newline at end of file diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/extrinsic_weights.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/extrinsic_weights.rs index 1a4adb968bb7195428ea00d59cd92dcd3b6eea5f..3bd48f061bba079a76aa8f032ebecd6d40c2156c 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/extrinsic_weights.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/extrinsic_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_xcm_bridge_hub_router.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_xcm_bridge_hub_router.rs index 21d15c75af553da60ecf0648186a26255bb72890..c0898012e9f32be0b6d6a09ae6e02a41fdf05a38 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_xcm_bridge_hub_router.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_xcm_bridge_hub_router.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_bridge_hub_router` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-07-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -52,14 +52,14 @@ impl pallet_xcm_bridge_hub_router::WeightInfo for Weigh /// Proof: `XcmpQueue::InboundXcmpSuspended` (`max_values`: Some(1), `max_size`: Some(4002), added: 4497, mode: `MaxEncodedLen`) /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) - /// Storage: `ToRococoXcmRouter::Bridge` (r:1 w:1) - /// Proof: `ToRococoXcmRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: 512, mode: `MaxEncodedLen`) + /// Storage: `ToRococoXcmRouter::DeliveryFeeFactor` (r:1 w:1) + /// Proof: `ToRococoXcmRouter::DeliveryFeeFactor` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) fn on_initialize_when_non_congested() -> Weight { // Proof Size summary in bytes: - // Measured: `226` + // Measured: `225` // Estimated: `5487` - // Minimum execution time: 8_363_000 picoseconds. - Weight::from_parts(8_620_000, 0) + // Minimum execution time: 13_483_000 picoseconds. + Weight::from_parts(13_862_000, 0) .saturating_add(Weight::from_parts(0, 5487)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -72,55 +72,9 @@ impl pallet_xcm_bridge_hub_router::WeightInfo for Weigh // Proof Size summary in bytes: // Measured: `111` // Estimated: `5487` - // Minimum execution time: 3_436_000 picoseconds. - Weight::from_parts(3_586_000, 0) + // Minimum execution time: 5_078_000 picoseconds. + Weight::from_parts(5_233_000, 0) .saturating_add(Weight::from_parts(0, 5487)) .saturating_add(T::DbWeight::get().reads(2)) } - /// Storage: `ToRococoXcmRouter::Bridge` (r:1 w:1) - /// Proof: `ToRococoXcmRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: 512, mode: `MaxEncodedLen`) - fn report_bridge_status() -> Weight { - // Proof Size summary in bytes: - // Measured: `150` - // Estimated: `1502` - // Minimum execution time: 9_706_000 picoseconds. - Weight::from_parts(10_139_000, 0) - .saturating_add(Weight::from_parts(0, 1502)) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) - } - /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) - /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) - /// Storage: UNKNOWN KEY `0x3302afcb67e838a3f960251b417b9a4f` (r:1 w:0) - /// Proof: UNKNOWN KEY `0x3302afcb67e838a3f960251b417b9a4f` (r:1 w:0) - /// Storage: UNKNOWN KEY `0x0973fe64c85043ba1c965cbc38eb63c7` (r:1 w:0) - /// Proof: UNKNOWN KEY `0x0973fe64c85043ba1c965cbc38eb63c7` (r:1 w:0) - /// Storage: `ToRococoXcmRouter::Bridge` (r:1 w:1) - /// Proof: `ToRococoXcmRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: 512, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::DeliveryFeeFactor` (r:1 w:0) - /// Proof: `XcmpQueue::DeliveryFeeFactor` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) - /// Storage: `PolkadotXcm::SupportedVersion` (r:2 w:0) - /// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1) - /// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0) - /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `ParachainSystem::RelevantMessagingState` (r:1 w:0) - /// Proof: `ParachainSystem::RelevantMessagingState` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:1) - /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::InboundXcmpSuspended` (r:1 w:0) - /// Proof: `XcmpQueue::InboundXcmpSuspended` (`max_values`: Some(1), `max_size`: Some(4002), added: 4497, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::OutboundXcmpMessages` (r:0 w:1) - /// Proof: `XcmpQueue::OutboundXcmpMessages` (`max_values`: None, `max_size`: Some(105506), added: 107981, mode: `MaxEncodedLen`) - fn send_message() -> Weight { - // Proof Size summary in bytes: - // Measured: `520` - // Estimated: `6460` - // Minimum execution time: 46_250_000 picoseconds. - Weight::from_parts(47_801_000, 0) - .saturating_add(Weight::from_parts(0, 6460)) - .saturating_add(T::DbWeight::get().reads(12)) - .saturating_add(T::DbWeight::get().writes(4)) - } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/paritydb_weights.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/paritydb_weights.rs index 25679703831a13b8d1bb7fb7dd4d92fa84b1f255..e0b1985c659c78b66176423f6bcc64d546b39324 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/paritydb_weights.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/paritydb_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/rocksdb_weights.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/rocksdb_weights.rs index 3dd817aa6f137085b0e5fdf2b11b7f50e5c8b002..c6e91b2fcffbf65e3fdce2bc8fbac19a605d00aa 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/rocksdb_weights.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/rocksdb_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs index f7891aedc496d188bcf4bab06c68b8d3d3373b52..0aeae3184627cc2162f779d8972202195fb583aa 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_fungible.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::fungible` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("asset-hub-westend-dev"), DB CACHE: 1024 @@ -54,8 +54,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `3593` - // Minimum execution time: 32_612_000 picoseconds. - Weight::from_parts(33_359_000, 3593) + // Minimum execution time: 32_651_000 picoseconds. + Weight::from_parts(33_225_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -65,8 +65,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `101` // Estimated: `6196` - // Minimum execution time: 41_144_000 picoseconds. - Weight::from_parts(41_788_000, 6196) + // Minimum execution time: 41_059_000 picoseconds. + Weight::from_parts(41_730_000, 6196) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -90,8 +90,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `8799` - // Minimum execution time: 101_340_000 picoseconds. - Weight::from_parts(103_686_000, 8799) + // Minimum execution time: 102_780_000 picoseconds. + Weight::from_parts(105_302_000, 8799) .saturating_add(T::DbWeight::get().reads(10)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -99,8 +99,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_682_000 picoseconds. - Weight::from_parts(1_734_000, 0) + // Minimum execution time: 1_124_000 picoseconds. + Weight::from_parts(1_201_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -122,8 +122,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 107_335_000 picoseconds. - Weight::from_parts(109_665_000, 6196) + // Minimum execution time: 109_024_000 picoseconds. + Weight::from_parts(111_406_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -131,8 +131,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_345_000 picoseconds. - Weight::from_parts(3_548_000, 0) + // Minimum execution time: 2_887_000 picoseconds. + Weight::from_parts(3_081_000, 0) } // Storage: `System::Account` (r:1 w:1) // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) @@ -140,8 +140,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 25_560_000 picoseconds. - Weight::from_parts(26_779_000, 3593) + // Minimum execution time: 25_234_000 picoseconds. + Weight::from_parts(25_561_000, 3593) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -165,8 +165,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `6196` - // Minimum execution time: 84_453_000 picoseconds. - Weight::from_parts(86_755_000, 6196) + // Minimum execution time: 83_416_000 picoseconds. + Weight::from_parts(85_683_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -190,8 +190,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 50_463_000 picoseconds. - Weight::from_parts(51_587_000, 3610) + // Minimum execution time: 49_271_000 picoseconds. + Weight::from_parts(51_019_000, 3610) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(3)) } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 127bc173c103741839ac2466b0d6ae7d5942ea7f..98ecd7bd3092bdb89d50f7f4c28118f55b7374a7 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -1,24 +1,25 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 +// This file is part of Cumulus. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-10-26, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-vmdtonbz-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("asset-hub-westend-dev"), DB CACHE: 1024 // Executed Command: @@ -67,8 +68,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 415_033_000 picoseconds. - Weight::from_parts(429_573_000, 6196) + // Minimum execution time: 100_823_000 picoseconds. + Weight::from_parts(103_071_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -76,8 +77,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_193_000 picoseconds. - Weight::from_parts(3_620_000, 0) + // Minimum execution time: 600_000 picoseconds. + Weight::from_parts(686_000, 0) } // Storage: `PolkadotXcm::Queries` (r:1 w:0) // Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -85,58 +86,58 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3568` - // Minimum execution time: 8_045_000 picoseconds. - Weight::from_parts(8_402_000, 3568) + // Minimum execution time: 8_226_000 picoseconds. + Weight::from_parts(8_650_000, 3568) .saturating_add(T::DbWeight::get().reads(1)) } pub fn transact() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_827_000 picoseconds. - Weight::from_parts(10_454_000, 0) + // Minimum execution time: 7_131_000 picoseconds. + Weight::from_parts(7_600_000, 0) } pub fn refund_surplus() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_330_000 picoseconds. - Weight::from_parts(3_677_000, 0) + // Minimum execution time: 2_589_000 picoseconds. + Weight::from_parts(2_705_000, 0) } pub fn set_error_handler() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_947_000 picoseconds. - Weight::from_parts(2_083_000, 0) + // Minimum execution time: 667_000 picoseconds. + Weight::from_parts(744_000, 0) } pub fn set_appendix() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_915_000 picoseconds. - Weight::from_parts(1_993_000, 0) + // Minimum execution time: 646_000 picoseconds. + Weight::from_parts(720_000, 0) } pub fn clear_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_918_000 picoseconds. - Weight::from_parts(2_048_000, 0) + // Minimum execution time: 633_000 picoseconds. + Weight::from_parts(669_000, 0) } pub fn descend_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_683_000 picoseconds. - Weight::from_parts(3_064_000, 0) + // Minimum execution time: 671_000 picoseconds. + Weight::from_parts(726_000, 0) } pub fn clear_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_893_000 picoseconds. - Weight::from_parts(2_159_000, 0) + // Minimum execution time: 615_000 picoseconds. + Weight::from_parts(675_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -158,8 +159,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 53_116_000 picoseconds. - Weight::from_parts(54_154_000, 6196) + // Minimum execution time: 67_236_000 picoseconds. + Weight::from_parts(69_899_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -169,8 +170,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `160` // Estimated: `3625` - // Minimum execution time: 12_381_000 picoseconds. - Weight::from_parts(12_693_000, 3625) + // Minimum execution time: 12_976_000 picoseconds. + Weight::from_parts(13_357_000, 3625) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -178,8 +179,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_933_000 picoseconds. - Weight::from_parts(1_983_000, 0) + // Minimum execution time: 633_000 picoseconds. + Weight::from_parts(685_000, 0) } // Storage: `PolkadotXcm::VersionNotifyTargets` (r:1 w:1) // Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -199,8 +200,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 24_251_000 picoseconds. - Weight::from_parts(24_890_000, 3610) + // Minimum execution time: 28_707_000 picoseconds. + Weight::from_parts(31_790_000, 3610) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -210,44 +211,44 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_850_000 picoseconds. - Weight::from_parts(4_082_000, 0) + // Minimum execution time: 2_670_000 picoseconds. + Weight::from_parts(2_833_000, 0) .saturating_add(T::DbWeight::get().writes(1)) } pub fn burn_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 112_248_000 picoseconds. - Weight::from_parts(124_454_000, 0) + // Minimum execution time: 23_459_000 picoseconds. + Weight::from_parts(23_817_000, 0) } pub fn expect_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 11_457_000 picoseconds. - Weight::from_parts(12_060_000, 0) + // Minimum execution time: 6_197_000 picoseconds. + Weight::from_parts(6_338_000, 0) } pub fn expect_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_959_000 picoseconds. - Weight::from_parts(2_076_000, 0) + // Minimum execution time: 671_000 picoseconds. + Weight::from_parts(715_000, 0) } pub fn expect_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_920_000 picoseconds. - Weight::from_parts(1_994_000, 0) + // Minimum execution time: 655_000 picoseconds. + Weight::from_parts(694_000, 0) } pub fn expect_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_149_000 picoseconds. - Weight::from_parts(2_394_000, 0) + // Minimum execution time: 810_000 picoseconds. + Weight::from_parts(858_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -269,8 +270,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 58_011_000 picoseconds. - Weight::from_parts(59_306_000, 6196) + // Minimum execution time: 73_136_000 picoseconds. + Weight::from_parts(75_314_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -278,8 +279,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_031_000 picoseconds. - Weight::from_parts(5_243_000, 0) + // Minimum execution time: 4_515_000 picoseconds. + Weight::from_parts(4_768_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -301,8 +302,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `246` // Estimated: `6196` - // Minimum execution time: 53_078_000 picoseconds. - Weight::from_parts(54_345_000, 6196) + // Minimum execution time: 68_072_000 picoseconds. + Weight::from_parts(69_866_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -310,22 +311,22 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_936_000 picoseconds. - Weight::from_parts(2_002_000, 0) + // Minimum execution time: 696_000 picoseconds. + Weight::from_parts(736_000, 0) } pub fn set_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_855_000 picoseconds. - Weight::from_parts(1_950_000, 0) + // Minimum execution time: 618_000 picoseconds. + Weight::from_parts(681_000, 0) } pub fn clear_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_882_000 picoseconds. - Weight::from_parts(1_977_000, 0) + // Minimum execution time: 647_000 picoseconds. + Weight::from_parts(672_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -333,22 +334,22 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `1489` - // Minimum execution time: 3_912_000 picoseconds. - Weight::from_parts(4_167_000, 1489) + // Minimum execution time: 2_496_000 picoseconds. + Weight::from_parts(2_617_000, 1489) .saturating_add(T::DbWeight::get().reads(1)) } pub fn set_fees_mode() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_911_000 picoseconds. - Weight::from_parts(1_971_000, 0) + // Minimum execution time: 637_000 picoseconds. + Weight::from_parts(675_000, 0) } pub fn unpaid_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_990_000 picoseconds. - Weight::from_parts(2_076_000, 0) + // Minimum execution time: 607_000 picoseconds. + Weight::from_parts(683_000, 0) } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index d61381d3f50b7c5379c71716f60addd6333adb8e..614ef81a4fa1b77ca756b83f143023647ee4571d 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -21,7 +21,7 @@ use super::{ XcmpQueue, }; use assets_common::{ - matching::{FromSiblingParachain, IsForeignConcreteAsset}, + matching::{FromSiblingParachain, IsForeignConcreteAsset, ParentLocation}, TrustBackedAssetsAsLocation, }; use frame_support::{ @@ -43,20 +43,21 @@ use parachains_common::{ use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::xcm_sender::ExponentialPrice; use snowbridge_router_primitives::inbound::GlobalConsensusEthereumConvertsFor; -use sp_runtime::traits::{AccountIdConversion, ConvertInto}; +use sp_runtime::traits::{AccountIdConversion, ConvertInto, TryConvertInto}; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, - DenyReserveTransferToRelayChain, DenyThenTry, DescribeFamily, DescribePalletTerminal, + DenyReserveTransferToRelayChain, DenyThenTry, DescribeAllTerminal, DescribeFamily, EnsureXcmOrigin, FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, GlobalConsensusParachainConvertsFor, HashedDescription, IsConcrete, LocalMint, - NetworkExportTableItem, NoChecking, NonFungiblesAdapter, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignPaidRemoteExporter, + MatchedConvertedConcreteId, NetworkExportTableItem, NoChecking, NonFungiblesAdapter, + ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SingleAssetExchangeAdapter, SovereignPaidRemoteExporter, SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, TakeWeightCredit, - TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, - XcmFeeManagerFromComponents, + TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, + WithLatestLocationConverter, WithUniqueTopic, XcmFeeManagerFromComponents, }; use xcm_executor::XcmExecutor; @@ -92,9 +93,8 @@ pub type LocationToAccountId = ( SiblingParachainConvertsVia, // Straight up local `AccountId32` origins just alias directly to `AccountId`. AccountId32Aliases, - // Foreign chain account alias into local accounts according to a hash of their standard - // description. - HashedDescription>, + // Foreign locations alias into accounts according to a hash of their standard description. + HashedDescription>, // Different global consensus parachain sovereign account. // (Used for over-bridge transfers and reserve processing) GlobalConsensusParachainConvertsFor, @@ -350,6 +350,28 @@ pub type TrustedTeleporters = ( IsForeignConcreteAsset>>, ); +/// Asset converter for pool assets. +/// Used to convert one asset to another, when there is a pool available between the two. +/// This type thus allows paying fees with any asset as long as there is a pool between said +/// asset and the asset required for fee payment. +pub type PoolAssetsExchanger = SingleAssetExchangeAdapter< + crate::AssetConversion, + crate::NativeAndAssets, + ( + TrustBackedAssetsAsLocation, + ForeignAssetsConvertedConcreteId, + // `ForeignAssetsConvertedConcreteId` excludes the relay token, so we add it back here. + MatchedConvertedConcreteId< + xcm::v4::Location, + Balance, + Equals, + WithLatestLocationConverter, + TryConvertInto, + >, + ), + AccountId, +>; + pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { type RuntimeCall = RuntimeCall; @@ -430,7 +452,7 @@ impl xcm_executor::Config for XcmConfig { type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type AssetLocker = (); - type AssetExchanger = (); + type AssetExchanger = PoolAssetsExchanger; type FeeManager = XcmFeeManagerFromComponents< WaivedLocations, SendXcmFeeToAccount, @@ -628,17 +650,6 @@ pub mod bridging { (StartsWith, StartsWith), AssetHubRococo, >; - - impl Contains for ToRococoXcmRouter { - fn contains(call: &RuntimeCall) -> bool { - matches!( - call, - RuntimeCall::ToRococoXcmRouter( - pallet_xcm_bridge_hub_router::Call::report_bridge_status { .. } - ) - ) - } - } } pub mod to_ethereum { diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs index 1c334d6f84f82fa8c045b05792fa452bc14be680..ff84bdea69f42aa8868cd9d700a6448d51d9b574 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs @@ -28,7 +28,7 @@ use asset_hub_westend_runtime::{ AllPalletsWithoutSystem, Assets, Balances, ExistentialDeposit, ForeignAssets, ForeignAssetsInstance, MetadataDepositBase, MetadataDepositPerByte, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, SessionKeys, - ToRococoXcmRouterInstance, TrustBackedAssetsInstance, XcmpQueue, + TrustBackedAssetsInstance, XcmpQueue, }; pub use asset_hub_westend_runtime::{AssetConversion, AssetDeposit, CollatorSelection, System}; use asset_test_utils::{ @@ -49,12 +49,14 @@ use frame_support::{ }; use parachains_common::{AccountId, AssetIdForTrustBackedAssets, AuraId, Balance}; use sp_consensus_aura::SlotDuration; +use sp_core::crypto::Ss58Codec; use sp_runtime::traits::MaybeEquivalence; 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::WithLatestLocationConverter; use xcm_executor::traits::{ConvertLocation, JustTry, WeightTrader}; +use xcm_runtime_apis::conversions::LocationToAccountHelper; const ALICE: [u8; 32] = [1u8; 32]; const SOME_ASSET_ADMIN: [u8; 32] = [5u8; 32]; @@ -1242,92 +1244,6 @@ fn receive_reserve_asset_deposited_roc_from_asset_hub_rococo_fees_paid_by_suffic ) } -#[test] -fn report_bridge_status_from_xcm_bridge_router_for_rococo_works() { - asset_test_utils::test_cases_over_bridge::report_bridge_status_from_xcm_bridge_router_works::< - Runtime, - AllPalletsWithoutSystem, - XcmConfig, - LocationToAccountId, - ToRococoXcmRouterInstance, - >( - collator_session_keys(), - bridging_to_asset_hub_rococo, - || { - vec![ - UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - Transact { - origin_kind: OriginKind::Xcm, - require_weight_at_most: - bp_asset_hub_westend::XcmBridgeHubRouterTransactCallMaxWeight::get(), - call: bp_asset_hub_westend::Call::ToRococoXcmRouter( - bp_asset_hub_westend::XcmBridgeHubRouterCall::report_bridge_status { - bridge_id: Default::default(), - is_congested: true, - }, - ) - .encode() - .into(), - }, - ] - .into() - }, - || { - vec![ - UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - Transact { - origin_kind: OriginKind::Xcm, - require_weight_at_most: - bp_asset_hub_westend::XcmBridgeHubRouterTransactCallMaxWeight::get(), - call: bp_asset_hub_westend::Call::ToRococoXcmRouter( - bp_asset_hub_westend::XcmBridgeHubRouterCall::report_bridge_status { - bridge_id: Default::default(), - is_congested: false, - }, - ) - .encode() - .into(), - }, - ] - .into() - }, - ) -} - -#[test] -fn test_report_bridge_status_call_compatibility() { - // if this test fails, make sure `bp_asset_hub_rococo` has valid encoding - assert_eq!( - RuntimeCall::ToRococoXcmRouter(pallet_xcm_bridge_hub_router::Call::report_bridge_status { - bridge_id: Default::default(), - is_congested: true, - }) - .encode(), - bp_asset_hub_westend::Call::ToRococoXcmRouter( - bp_asset_hub_westend::XcmBridgeHubRouterCall::report_bridge_status { - bridge_id: Default::default(), - is_congested: true, - } - ) - .encode() - ) -} - -#[test] -fn check_sane_weight_report_bridge_status() { - use pallet_xcm_bridge_hub_router::WeightInfo; - let actual = >::WeightInfo::report_bridge_status(); - let max_weight = bp_asset_hub_westend::XcmBridgeHubRouterTransactCallMaxWeight::get(); - assert!( - actual.all_lte(max_weight), - "max_weight: {:?} should be adjusted to actual {:?}", - max_weight, - actual - ); -} - #[test] fn change_xcm_bridge_hub_router_byte_fee_by_governance_works() { asset_test_utils::test_cases::change_storage_constant_by_governance_works::< @@ -1415,3 +1331,112 @@ fn reserve_transfer_native_asset_to_non_teleport_para_works() { WeightLimit::Unlimited, ); } + +#[test] +fn location_conversion_works() { + // the purpose of hardcoded values is to catch an unintended location conversion logic change. + struct TestCase { + description: &'static str, + location: Location, + expected_account_id_str: &'static str, + } + + let test_cases = vec![ + // DescribeTerminus + TestCase { + description: "DescribeTerminus Parent", + location: Location::new(1, Here), + expected_account_id_str: "5Dt6dpkWPwLaH4BBCKJwjiWrFVAGyYk3tLUabvyn4v7KtESG", + }, + TestCase { + description: "DescribeTerminus Sibling", + location: Location::new(1, [Parachain(1111)]), + expected_account_id_str: "5Eg2fnssmmJnF3z1iZ1NouAuzciDaaDQH7qURAy3w15jULDk", + }, + // DescribePalletTerminal + TestCase { + description: "DescribePalletTerminal Parent", + location: Location::new(1, [PalletInstance(50)]), + expected_account_id_str: "5CnwemvaAXkWFVwibiCvf2EjqwiqBi29S5cLLydZLEaEw6jZ", + }, + TestCase { + description: "DescribePalletTerminal Sibling", + location: Location::new(1, [Parachain(1111), PalletInstance(50)]), + expected_account_id_str: "5GFBgPjpEQPdaxEnFirUoa51u5erVx84twYxJVuBRAT2UP2g", + }, + // DescribeAccountId32Terminal + TestCase { + description: "DescribeAccountId32Terminal Parent", + location: Location::new( + 1, + [AccountId32 { network: None, id: AccountId::from(ALICE).into() }], + ), + expected_account_id_str: "5DN5SGsuUG7PAqFL47J9meViwdnk9AdeSWKFkcHC45hEzVz4", + }, + TestCase { + description: "DescribeAccountId32Terminal Sibling", + location: Location::new( + 1, + [ + Parachain(1111), + Junction::AccountId32 { network: None, id: AccountId::from(ALICE).into() }, + ], + ), + expected_account_id_str: "5DGRXLYwWGce7wvm14vX1Ms4Vf118FSWQbJkyQigY2pfm6bg", + }, + // DescribeAccountKey20Terminal + TestCase { + description: "DescribeAccountKey20Terminal Parent", + location: Location::new(1, [AccountKey20 { network: None, key: [0u8; 20] }]), + expected_account_id_str: "5F5Ec11567pa919wJkX6VHtv2ZXS5W698YCW35EdEbrg14cg", + }, + TestCase { + description: "DescribeAccountKey20Terminal Sibling", + location: Location::new( + 1, + [Parachain(1111), AccountKey20 { network: None, key: [0u8; 20] }], + ), + expected_account_id_str: "5CB2FbUds2qvcJNhDiTbRZwiS3trAy6ydFGMSVutmYijpPAg", + }, + // DescribeTreasuryVoiceTerminal + TestCase { + description: "DescribeTreasuryVoiceTerminal Parent", + location: Location::new(1, [Plurality { id: BodyId::Treasury, part: BodyPart::Voice }]), + expected_account_id_str: "5CUjnE2vgcUCuhxPwFoQ5r7p1DkhujgvMNDHaF2bLqRp4D5F", + }, + TestCase { + description: "DescribeTreasuryVoiceTerminal Sibling", + location: Location::new( + 1, + [Parachain(1111), Plurality { id: BodyId::Treasury, part: BodyPart::Voice }], + ), + expected_account_id_str: "5G6TDwaVgbWmhqRUKjBhRRnH4ry9L9cjRymUEmiRsLbSE4gB", + }, + // DescribeBodyTerminal + TestCase { + description: "DescribeBodyTerminal Parent", + location: Location::new(1, [Plurality { id: BodyId::Unit, part: BodyPart::Voice }]), + expected_account_id_str: "5EBRMTBkDisEXsaN283SRbzx9Xf2PXwUxxFCJohSGo4jYe6B", + }, + TestCase { + description: "DescribeBodyTerminal Sibling", + location: Location::new( + 1, + [Parachain(1111), Plurality { id: BodyId::Unit, part: BodyPart::Voice }], + ), + expected_account_id_str: "5DBoExvojy8tYnHgLL97phNH975CyT45PWTZEeGoBZfAyRMH", + }, + ]; + + for tc in test_cases { + let expected = + AccountId::from_string(tc.expected_account_id_str).expect("Invalid AccountId string"); + + let got = LocationToAccountHelper::::convert_location( + tc.location.into(), + ) + .unwrap(); + + assert_eq!(got, expected, "{}", tc.description); + } +} diff --git a/cumulus/parachains/runtimes/assets/common/Cargo.toml b/cumulus/parachains/runtimes/assets/common/Cargo.toml index c6740269339d808f14d9dd2879f2ddd74b4e842b..fb66f0de2322f30fec69baea941e72726b73ee4e 100644 --- a/cumulus/parachains/runtimes/assets/common/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/common/Cargo.toml @@ -19,6 +19,7 @@ impl-trait-for-tuples = { workspace = true } frame-support = { workspace = true } sp-api = { workspace = true } sp-runtime = { workspace = true } +pallet-assets = { workspace = true } pallet-asset-conversion = { workspace = true } # Polkadot @@ -42,6 +43,7 @@ std = [ "frame-support/std", "log/std", "pallet-asset-conversion/std", + "pallet-assets/std", "pallet-xcm/std", "parachains-common/std", "scale-info/std", @@ -56,6 +58,7 @@ runtime-benchmarks = [ "cumulus-primitives-core/runtime-benchmarks", "frame-support/runtime-benchmarks", "pallet-asset-conversion/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", "parachains-common/runtime-benchmarks", "sp-runtime/runtime-benchmarks", diff --git a/cumulus/parachains/runtimes/assets/common/src/lib.rs b/cumulus/parachains/runtimes/assets/common/src/lib.rs index 4bb593f98929e4dfcaf2879e759f863138f49282..deda5fa4ab9c43d10101032eda49d975a85614df 100644 --- a/cumulus/parachains/runtimes/assets/common/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/common/src/lib.rs @@ -29,7 +29,7 @@ use crate::matching::{LocalLocationPattern, ParentLocation}; use frame_support::traits::{Equals, EverythingBut}; use parachains_common::{AssetIdForTrustBackedAssets, CollectionId, ItemId}; use sp_runtime::traits::TryConvertInto; -use xcm::latest::Location; +use xcm::prelude::*; use xcm_builder::{ AsPrefixedGeneralIndex, MatchedConvertedConcreteId, StartsWith, WithLatestLocationConverter, }; @@ -138,7 +138,6 @@ pub type PoolAssetsConvertedConcreteId = mod tests { use super::*; use sp_runtime::traits::MaybeEquivalence; - use xcm::prelude::*; use xcm_builder::{StartsWithExplicitGlobalConsensus, WithLatestLocationConverter}; use xcm_executor::traits::{Error as MatchError, MatchesFungibles}; diff --git a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases.rs b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases.rs index 67b585ecfe86ff71f59888524353c12af1bc44c8..c80222142304b52e31b1e8c0343707ca6fcdbf3a 100644 --- a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases.rs +++ b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases.rs @@ -1143,7 +1143,8 @@ pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_wor .with_balances(vec![( foreign_creator_as_account_id.clone(), existential_deposit + - asset_deposit + metadata_deposit_base + + asset_deposit + + metadata_deposit_base + metadata_deposit_per_byte_eta + buy_execution_fee_amount.into() + buy_execution_fee_amount.into(), 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 e0b3f70c75468b69d3a660fb6777f536f4f7df37..d8676117474023cdb9bbb750248eb48cf59f2e32 100644 --- a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs +++ b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs @@ -542,14 +542,19 @@ pub fn report_bridge_status_from_xcm_bridge_router_works< // execute xcm as XcmpQueue would do let outcome = XcmExecutor::::prepare_and_execute( - local_bridge_hub_location, + local_bridge_hub_location.clone(), xcm, &mut hash, - RuntimeHelper::::xcm_max_weight(XcmReceivedFrom::Sibling), + RuntimeHelper::::xcm_max_weight( + XcmReceivedFrom::Sibling, + ), Weight::zero(), ); assert_ok!(outcome.ensure_complete()); - assert_eq!(is_congested, pallet_xcm_bridge_hub_router::Pallet::::bridge().is_congested); + assert_eq!( + is_congested, + <>::LocalXcmChannelManager as pallet_xcm_bridge_hub_router::XcmChannelStatusProvider>::is_congested(&local_bridge_hub_location) + ); }; report_bridge_status(true); diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml index 6d0fbd7d5c6605d09b358c2e2db2a1ef00b3fb7e..9a76e61ecb20c555f461ff3cf623cdc1009e605a 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -22,6 +22,7 @@ scale-info = { features = [ "derive", ], workspace = true } serde = { optional = true, features = ["derive"], workspace = true, default-features = true } +serde_json = { features = ["alloc"], workspace = true } # Substrate frame-benchmarking = { optional = true, workspace = true } @@ -97,7 +98,7 @@ bp-parachains = { workspace = true } bp-polkadot-bulletin = { workspace = true } bp-polkadot-core = { workspace = true } bp-relayers = { workspace = true } -bp-runtime = { features = ["test-helpers"], workspace = true } +bp-runtime = { workspace = true } bp-rococo = { workspace = true } bp-westend = { workspace = true } pallet-bridge-grandpa = { workspace = true } @@ -123,9 +124,8 @@ bridge-hub-common = { workspace = true } [dev-dependencies] bridge-hub-test-utils = { workspace = true, default-features = true } -bridge-runtime-common = { features = [ - "integrity-test", -], workspace = true, default-features = true } +bridge-runtime-common = { features = ["integrity-test"], workspace = true, default-features = true } +pallet-bridge-relayers = { features = ["integrity-test"], workspace = true } sp-keyring = { workspace = true, default-features = true } snowbridge-runtime-test-common = { workspace = true, default-features = true } @@ -192,6 +192,7 @@ std = [ "rococo-runtime-constants/std", "scale-info/std", "serde", + "serde_json/std", "snowbridge-beacon-primitives/std", "snowbridge-core/std", "snowbridge-outbound-queue-runtime-api/std", diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs index 779cc537ee96dcc2fadc38452dfcd01c4c320789..5dca45d326b83fb188a261d1c09737b828159ebf 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs @@ -64,11 +64,37 @@ impl pallet_bridge_parachains::Config for Runtim } /// Allows collect and claim rewards for relayers -impl pallet_bridge_relayers::Config for Runtime { +pub type RelayersForLegacyLaneIdsMessagesInstance = (); +impl pallet_bridge_relayers::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Reward = Balance; - type PaymentProcedure = - bp_relayers::PayRewardFromAccount, AccountId>; + type PaymentProcedure = bp_relayers::PayRewardFromAccount< + pallet_balances::Pallet, + AccountId, + Self::LaneId, + >; + type StakeAndSlash = pallet_bridge_relayers::StakeAndSlashNamed< + AccountId, + BlockNumber, + Balances, + RelayerStakeReserveId, + RequiredStakeForStakeAndSlash, + RelayerStakeLease, + >; + type WeightInfo = weights::pallet_bridge_relayers::WeightInfo; + type LaneId = bp_messages::LegacyLaneId; +} + +/// Allows collect and claim rewards for relayers +pub type RelayersForPermissionlessLanesInstance = pallet_bridge_relayers::Instance2; +impl pallet_bridge_relayers::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Reward = Balance; + type PaymentProcedure = bp_relayers::PayRewardFromAccount< + pallet_balances::Pallet, + AccountId, + Self::LaneId, + >; type StakeAndSlash = pallet_bridge_relayers::StakeAndSlashNamed< AccountId, BlockNumber, @@ -78,6 +104,7 @@ impl pallet_bridge_relayers::Config for Runtime { RelayerStakeLease, >; type WeightInfo = weights::pallet_bridge_relayers::WeightInfo; + type LaneId = bp_messages::HashedLaneId; } /// Add GRANDPA bridge pallet to track Rococo Bulletin chain. 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 83b92918dc4f442d3574d0476d25de90e9dc160a..c971fa59c68d7d4a3b6558274d61ad631246d1f6 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs @@ -20,31 +20,34 @@ //! are reusing Polkadot Bulletin chain primitives everywhere here. use crate::{ - weights, xcm_config::UniversalLocation, BridgeRococoBulletinGrandpa, - BridgeRococoBulletinMessages, PolkadotXcm, Runtime, RuntimeEvent, XcmOverRococoBulletin, - XcmRouter, + bridge_common_config::RelayersForPermissionlessLanesInstance, weights, + xcm_config::UniversalLocation, AccountId, Balance, Balances, BridgeRococoBulletinGrandpa, + BridgeRococoBulletinMessages, PolkadotXcm, Runtime, RuntimeEvent, RuntimeHoldReason, + XcmOverRococoBulletin, XcmRouter, }; use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, - target_chain::FromBridgedChainMessagesProof, LaneId, -}; -use bridge_runtime_common::{ - extensions::refund_relayer_extension::{ - ActualFeeRefund, RefundBridgedMessages, RefundSignedExtensionAdapter, - RefundableMessagesLane, - }, - messages_xcm_extension::{ - SenderAndLane, XcmAsPlainPayload, XcmBlobHauler, XcmBlobHaulerAdapter, - XcmBlobMessageDispatch, XcmVersionOfDestAndRemoteBridge, - }, + target_chain::FromBridgedChainMessagesProof, HashedLaneId, }; +use bridge_hub_common::xcm_version::XcmVersionOfDestAndRemoteBridge; -use frame_support::{parameter_types, traits::PalletInfoAccess}; +use frame_support::{ + parameter_types, + traits::{Equals, PalletInfoAccess}, +}; +use frame_system::{EnsureNever, EnsureRoot}; +use pallet_bridge_messages::LaneIdOf; +use pallet_bridge_relayers::extension::{ + BridgeRelayersSignedExtension, WithMessagesExtensionConfig, +}; +use pallet_xcm_bridge_hub::XcmAsPlainPayload; +use polkadot_parachain_primitives::primitives::Sibling; +use testnet_parachains_constants::rococo::currency::UNITS as ROC; use xcm::{ latest::prelude::*, prelude::{InteriorLocation, NetworkId}, }; -use xcm_builder::BridgeBlobDispatcher; +use xcm_builder::{BridgeBlobDispatcher, ParentIsPreset, SiblingParachainConvertsVia}; parameter_types! { /// Interior location (relative to this runtime) of the with-RococoBulletin messages pallet. @@ -58,12 +61,6 @@ parameter_types! { 2, [GlobalConsensus(RococoBulletinGlobalConsensusNetwork::get())] ); - /// All active lanes that the current bridge supports. - pub ActiveOutboundLanesToRococoBulletin: &'static [bp_messages::LaneId] - = &[XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN]; - /// Lane identifier, used to connect Rococo People and Rococo Bulletin chain. - pub const RococoPeopleToRococoBulletinMessagesLane: bp_messages::LaneId - = XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN; // see the `FEE_BOOST_PER_RELAY_HEADER` constant get the meaning of this value pub PriorityBoostPerRelayHeader: u64 = 58_014_163_614_163; @@ -75,33 +72,18 @@ parameter_types! { /// meaning of this value. pub PriorityBoostPerMessage: u64 = 182_044_444_444_444; - /// Identifier of the sibling Rococo People parachain. - pub RococoPeopleParaId: cumulus_primitives_core::ParaId = rococo_runtime_constants::system_parachain::PEOPLE_ID.into(); - /// A route (XCM location and bridge lane) that the Rococo People Chain -> Rococo Bulletin Chain - /// message is following. - pub FromRococoPeopleToRococoBulletinRoute: SenderAndLane = SenderAndLane::new( - ParentThen(Parachain(RococoPeopleParaId::get().into()).into()).into(), - XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN, - ); - /// All active routes and their destinations. - pub ActiveLanes: alloc::vec::Vec<(SenderAndLane, (NetworkId, InteriorLocation))> = alloc::vec![ - ( - FromRococoPeopleToRococoBulletinRoute::get(), - (RococoBulletinGlobalConsensusNetwork::get(), Here) - ) - ]; + /// PeopleRococo location + pub PeopleRococoLocation: Location = Location::new(1, [Parachain(rococo_runtime_constants::system_parachain::PEOPLE_ID)]); - /// XCM message that is never sent. - pub NeverSentMessage: Option> = None; + pub storage BridgeDeposit: Balance = 5 * ROC; } -pub const XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN: LaneId = LaneId([0, 0, 0, 0]); /// Proof of messages, coming from Rococo Bulletin chain. -pub type FromRococoBulletinMessagesProof = - FromBridgedChainMessagesProof; +pub type FromRococoBulletinMessagesProof = + FromBridgedChainMessagesProof>; /// Messages delivery proof for Rococo Bridge Hub -> Rococo Bulletin messages. -pub type ToRococoBulletinMessagesDeliveryProof = - FromBridgedChainMessagesDeliveryProof; +pub type ToRococoBulletinMessagesDeliveryProof = + FromBridgedChainMessagesDeliveryProof>; /// Dispatches received XCM messages from other bridge. type FromRococoBulletinMessageBlobDispatcher = BridgeBlobDispatcher< @@ -110,35 +92,18 @@ type FromRococoBulletinMessageBlobDispatcher = BridgeBlobDispatcher< BridgeRococoToRococoBulletinMessagesPalletInstance, >; -/// Export XCM messages to be relayed to the other side -pub type ToRococoBulletinHaulBlobExporter = XcmOverRococoBulletin; - -pub struct ToRococoBulletinXcmBlobHauler; -impl XcmBlobHauler for ToRococoBulletinXcmBlobHauler { - type Runtime = Runtime; - type MessagesInstance = WithRococoBulletinMessagesInstance; - type ToSourceChainSender = XcmRouter; - type CongestedMessage = NeverSentMessage; - type UncongestedMessage = NeverSentMessage; -} - -/// On messages delivered callback. -type OnMessagesDeliveredFromRococoBulletin = - XcmBlobHaulerAdapter; - /// Signed extension that refunds relayers that are delivering messages from the Rococo Bulletin /// chain. -pub type OnBridgeHubRococoRefundRococoBulletinMessages = RefundSignedExtensionAdapter< - RefundBridgedMessages< +pub type OnBridgeHubRococoRefundRococoBulletinMessages = BridgeRelayersSignedExtension< + Runtime, + WithMessagesExtensionConfig< + StrOnBridgeHubRococoRefundRococoBulletinMessages, Runtime, - RefundableMessagesLane< - WithRococoBulletinMessagesInstance, - RococoPeopleToRococoBulletinMessagesLane, - >, - ActualFeeRefund, + WithRococoBulletinMessagesInstance, + RelayersForPermissionlessLanesInstance, PriorityBoostPerMessage, - StrOnBridgeHubRococoRefundRococoBulletinMessages, >, + LaneIdOf, >; bp_runtime::generate_static_str_provider!(OnBridgeHubRococoRefundRococoBulletinMessages); @@ -153,31 +118,45 @@ impl pallet_bridge_messages::Config for Runt type BridgedChain = bp_polkadot_bulletin::PolkadotBulletin; type BridgedHeaderChain = BridgeRococoBulletinGrandpa; - type ActiveOutboundLanes = ActiveOutboundLanesToRococoBulletin; - type OutboundPayload = XcmAsPlainPayload; - type InboundPayload = XcmAsPlainPayload; - type DeliveryPayments = (); + type LaneId = HashedLaneId; + type DeliveryPayments = (); type DeliveryConfirmationPayments = (); - type MessageDispatch = - XcmBlobMessageDispatch; - type OnMessagesDelivered = OnMessagesDeliveredFromRococoBulletin; + type MessageDispatch = XcmOverRococoBulletin; + type OnMessagesDelivered = XcmOverRococoBulletin; } /// Add support for the export and dispatch of XCM programs. pub type XcmOverPolkadotBulletinInstance = pallet_xcm_bridge_hub::Instance2; impl pallet_xcm_bridge_hub::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type UniversalLocation = UniversalLocation; type BridgedNetwork = RococoBulletinGlobalConsensusNetworkLocation; type BridgeMessagesPalletInstance = WithRococoBulletinMessagesInstance; + type MessageExportPrice = (); type DestinationVersion = XcmVersionOfDestAndRemoteBridge; - type Lanes = ActiveLanes; - type LanesSupport = ToRococoBulletinXcmBlobHauler; + + type ForceOrigin = EnsureRoot; + // We don't want to allow creating bridges for this instance. + type OpenBridgeOrigin = EnsureNever; + // Converter aligned with `OpenBridgeOrigin`. + type BridgeOriginAccountIdConverter = + (ParentIsPreset, SiblingParachainConvertsVia); + + type BridgeDeposit = BridgeDeposit; + type Currency = Balances; + type RuntimeHoldReason = RuntimeHoldReason; + // Do not require deposit from People parachains. + type AllowWithoutBridgeDeposit = Equals; + + type LocalXcmChannelManager = (); + type BlobDispatcher = FromRococoBulletinMessageBlobDispatcher; } #[cfg(test)] @@ -232,13 +211,13 @@ mod tests { // Bulletin chain - it has the same (almost) runtime for Polkadot Bulletin and Rococo // Bulletin, so we have to adhere Polkadot names here - bridge_runtime_common::extensions::priority_calculator::per_relay_header::ensure_priority_boost_is_sane::< + pallet_bridge_relayers::extension::per_relay_header::ensure_priority_boost_is_sane::< Runtime, BridgeGrandpaRococoBulletinInstance, PriorityBoostPerRelayHeader, >(FEE_BOOST_PER_RELAY_HEADER); - bridge_runtime_common::extensions::priority_calculator::per_message::ensure_priority_boost_is_sane::< + pallet_bridge_relayers::extension::per_message::ensure_priority_boost_is_sane::< Runtime, WithRococoBulletinMessagesInstance, PriorityBoostPerMessage, @@ -252,3 +231,77 @@ mod tests { assert_eq!(BridgeRococoToRococoBulletinMessagesPalletInstance::get(), expected,); } } + +#[cfg(feature = "runtime-benchmarks")] +pub(crate) fn open_bridge_for_benchmarks( + with: pallet_xcm_bridge_hub::LaneIdOf, + sibling_para_id: u32, +) -> InteriorLocation +where + R: pallet_xcm_bridge_hub::Config, + XBHI: 'static, + C: xcm_executor::traits::ConvertLocation< + bp_runtime::AccountIdOf>, + >, +{ + use pallet_xcm_bridge_hub::{Bridge, BridgeId, BridgeState}; + use sp_runtime::traits::Zero; + use xcm::VersionedInteriorLocation; + + // insert bridge metadata + let lane_id = with; + let sibling_parachain = Location::new(1, [Parachain(sibling_para_id)]); + let universal_source = [GlobalConsensus(Rococo), Parachain(sibling_para_id)].into(); + let universal_destination = + [GlobalConsensus(RococoBulletinGlobalConsensusNetwork::get()), Parachain(2075)].into(); + let bridge_id = BridgeId::new(&universal_source, &universal_destination); + + // insert only bridge metadata, because the benchmarks create lanes + pallet_xcm_bridge_hub::Bridges::::insert( + bridge_id, + Bridge { + bridge_origin_relative_location: alloc::boxed::Box::new( + sibling_parachain.clone().into(), + ), + bridge_origin_universal_location: alloc::boxed::Box::new( + VersionedInteriorLocation::from(universal_source.clone()), + ), + bridge_destination_universal_location: alloc::boxed::Box::new( + VersionedInteriorLocation::from(universal_destination), + ), + state: BridgeState::Opened, + bridge_owner_account: C::convert_location(&sibling_parachain).expect("valid AccountId"), + deposit: Zero::zero(), + lane_id, + }, + ); + pallet_xcm_bridge_hub::LaneToBridge::::insert(lane_id, bridge_id); + + universal_source +} + +/// Contains the migration for the PeopleRococo<>RococoBulletin bridge. +pub mod migration { + use super::*; + use frame_support::traits::ConstBool; + + parameter_types! { + pub BulletinRococoLocation: InteriorLocation = [GlobalConsensus(RococoBulletinGlobalConsensusNetwork::get())].into(); + pub RococoPeopleToRococoBulletinMessagesLane: HashedLaneId = pallet_xcm_bridge_hub::Pallet::< Runtime, XcmOverPolkadotBulletinInstance >::bridge_locations( + PeopleRococoLocation::get(), + BulletinRococoLocation::get() + ) + .unwrap() + .calculate_lane_id(xcm::latest::VERSION).expect("Valid locations"); + } + + /// Ensure that the existing lanes for the People<>Bulletin bridge are correctly configured. + pub type StaticToDynamicLanes = pallet_xcm_bridge_hub::migration::OpenBridgeForLane< + Runtime, + XcmOverPolkadotBulletinInstance, + RococoPeopleToRococoBulletinMessagesLane, + ConstBool, + PeopleRococoLocation, + BulletinRococoLocation, + >; +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs index 01a762d4b99f94f39e7a6050ff13a720002063bd..be7005b5379a1fa1732cc5b4ae18842c8a9d229a 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs @@ -14,9 +14,34 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::{xcm_config::UniversalLocation, Runtime}; -use snowbridge_router_primitives::outbound::EthereumBlobExporter; -use testnet_parachains_constants::rococo::snowbridge::EthereumNetwork; +#[cfg(not(feature = "runtime-benchmarks"))] +use crate::XcmRouter; +use crate::{ + xcm_config, xcm_config::UniversalLocation, Balances, EthereumInboundQueue, + EthereumOutboundQueue, EthereumSystem, MessageQueue, Runtime, RuntimeEvent, TransactionByteFee, + TreasuryAccount, +}; +use parachains_common::{AccountId, Balance}; +use snowbridge_beacon_primitives::{Fork, ForkVersions}; +use snowbridge_core::{gwei, meth, AllowSiblingsOnly, PricingParameters, Rewards}; +use snowbridge_router_primitives::{inbound::MessageToXcm, outbound::EthereumBlobExporter}; +use sp_core::H160; +use testnet_parachains_constants::rococo::{ + currency::*, + fee::WeightToFee, + snowbridge::{EthereumLocation, EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX}, +}; + +use crate::xcm_config::RelayNetwork; +#[cfg(feature = "runtime-benchmarks")] +use benchmark_helpers::DoNothingRouter; +use frame_support::{parameter_types, weights::ConstantMultiplier}; +use pallet_xcm::EnsureXcm; +use sp_runtime::{ + traits::{ConstU32, ConstU8, Keccak256}, + FixedU128, +}; +use xcm::prelude::{GlobalConsensus, InteriorLocation, Location, Parachain}; /// Exports message to the Ethereum Gateway contract. pub type SnowbridgeExporter = EthereumBlobExporter< @@ -24,4 +49,184 @@ pub type SnowbridgeExporter = EthereumBlobExporter< EthereumNetwork, snowbridge_pallet_outbound_queue::Pallet, snowbridge_core::AgentIdOf, + EthereumSystem, >; + +// Ethereum Bridge +parameter_types! { + pub storage EthereumGatewayAddress: H160 = H160(hex_literal::hex!("EDa338E4dC46038493b885327842fD3E301CaB39")); +} + +parameter_types! { + pub const CreateAssetCall: [u8;2] = [53, 0]; + pub const CreateAssetDeposit: u128 = (UNITS / 10) + EXISTENTIAL_DEPOSIT; + pub Parameters: PricingParameters = PricingParameters { + exchange_rate: FixedU128::from_rational(1, 400), + fee_per_gas: gwei(20), + rewards: Rewards { local: 1 * UNITS, remote: meth(1) }, + multiplier: FixedU128::from_rational(1, 1), + }; + pub AssetHubFromEthereum: Location = Location::new(1,[GlobalConsensus(RelayNetwork::get()),Parachain(rococo_runtime_constants::system_parachain::ASSET_HUB_ID)]); + pub EthereumUniversalLocation: InteriorLocation = [GlobalConsensus(EthereumNetwork::get())].into(); +} + +impl snowbridge_pallet_inbound_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Verifier = snowbridge_pallet_ethereum_client::Pallet; + type Token = Balances; + #[cfg(not(feature = "runtime-benchmarks"))] + type XcmSender = XcmRouter; + #[cfg(feature = "runtime-benchmarks")] + type XcmSender = DoNothingRouter; + type ChannelLookup = EthereumSystem; + type GatewayAddress = EthereumGatewayAddress; + #[cfg(feature = "runtime-benchmarks")] + type Helper = Runtime; + type MessageConverter = MessageToXcm< + CreateAssetCall, + CreateAssetDeposit, + ConstU8, + AccountId, + Balance, + EthereumSystem, + EthereumUniversalLocation, + AssetHubFromEthereum, + >; + type WeightToFee = WeightToFee; + type LengthToFee = ConstantMultiplier; + type MaxMessageSize = ConstU32<2048>; + type WeightInfo = crate::weights::snowbridge_pallet_inbound_queue::WeightInfo; + type PricingParameters = EthereumSystem; + type AssetTransactor = ::AssetTransactor; +} + +impl snowbridge_pallet_outbound_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Hashing = Keccak256; + type MessageQueue = MessageQueue; + type Decimals = ConstU8<12>; + type MaxMessagePayloadSize = ConstU32<2048>; + type MaxMessagesPerBlock = ConstU32<32>; + type GasMeter = snowbridge_core::outbound::ConstantGasMeter; + type Balance = Balance; + type WeightToFee = WeightToFee; + type WeightInfo = crate::weights::snowbridge_pallet_outbound_queue::WeightInfo; + type PricingParameters = EthereumSystem; + type Channels = EthereumSystem; +} + +#[cfg(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test))] +parameter_types! { + pub const ChainForkVersions: ForkVersions = ForkVersions { + genesis: Fork { + version: [0, 0, 0, 0], // 0x00000000 + epoch: 0, + }, + altair: Fork { + version: [1, 0, 0, 0], // 0x01000000 + epoch: 0, + }, + bellatrix: Fork { + version: [2, 0, 0, 0], // 0x02000000 + epoch: 0, + }, + capella: Fork { + version: [3, 0, 0, 0], // 0x03000000 + epoch: 0, + }, + deneb: Fork { + version: [4, 0, 0, 0], // 0x04000000 + epoch: 0, + } + }; +} + +#[cfg(not(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test)))] +parameter_types! { + pub const ChainForkVersions: ForkVersions = ForkVersions { + genesis: Fork { + version: [144, 0, 0, 111], // 0x90000069 + epoch: 0, + }, + altair: Fork { + version: [144, 0, 0, 112], // 0x90000070 + epoch: 50, + }, + bellatrix: Fork { + version: [144, 0, 0, 113], // 0x90000071 + epoch: 100, + }, + capella: Fork { + version: [144, 0, 0, 114], // 0x90000072 + epoch: 56832, + }, + deneb: Fork { + version: [144, 0, 0, 115], // 0x90000073 + epoch: 132608, + }, + }; +} + +pub const SLOTS_PER_EPOCH: u32 = snowbridge_pallet_ethereum_client::config::SLOTS_PER_EPOCH as u32; + +impl snowbridge_pallet_ethereum_client::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ForkVersions = ChainForkVersions; + // Free consensus update every epoch. Works out to be 225 updates per day. + type FreeHeadersInterval = ConstU32; + type WeightInfo = crate::weights::snowbridge_pallet_ethereum_client::WeightInfo; +} + +impl snowbridge_pallet_system::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OutboundQueue = EthereumOutboundQueue; + type SiblingOrigin = EnsureXcm; + type AgentIdOf = snowbridge_core::AgentIdOf; + type TreasuryAccount = TreasuryAccount; + type Token = Balances; + type WeightInfo = crate::weights::snowbridge_pallet_system::WeightInfo; + #[cfg(feature = "runtime-benchmarks")] + type Helper = (); + type DefaultPricingParameters = Parameters; + type InboundDeliveryCost = EthereumInboundQueue; + type UniversalLocation = UniversalLocation; + type EthereumLocation = EthereumLocation; +} + +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmark_helpers { + use crate::{EthereumBeaconClient, Runtime, RuntimeOrigin}; + use codec::Encode; + use snowbridge_beacon_primitives::BeaconHeader; + use snowbridge_pallet_inbound_queue::BenchmarkHelper; + use sp_core::H256; + use xcm::latest::{Assets, Location, SendError, SendResult, SendXcm, Xcm, XcmHash}; + + impl BenchmarkHelper for Runtime { + fn initialize_storage(beacon_header: BeaconHeader, block_roots_root: H256) { + EthereumBeaconClient::store_finalized_header(beacon_header, block_roots_root).unwrap(); + } + } + + pub struct DoNothingRouter; + impl SendXcm for DoNothingRouter { + type Ticket = Xcm<()>; + + fn validate( + _dest: &mut Option, + xcm: &mut Option>, + ) -> SendResult { + Ok((xcm.clone().unwrap(), Assets::new())) + } + fn deliver(xcm: Xcm<()>) -> Result { + let hash = xcm.using_encoded(sp_io::hashing::blake2_256); + Ok(hash) + } + } + + impl snowbridge_pallet_system::BenchmarkHelper for () { + fn make_xcm_origin(location: Location) -> RuntimeOrigin { + RuntimeOrigin::from(pallet_xcm::Origin::Xcm(location)) + } + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs index 9880e8a17c2b80d34ae6b62f9c8a588625105e70..8fe045723107c58974c696ad9edf18a424b090b4 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 @@ -17,33 +17,36 @@ //! Bridge definitions used on BridgeHubRococo for bridging to BridgeHubWestend. use crate::{ - bridge_common_config::{BridgeParachainWestendInstance, DeliveryRewardInBalance}, + bridge_common_config::{ + BridgeParachainWestendInstance, DeliveryRewardInBalance, + RelayersForLegacyLaneIdsMessagesInstance, + }, weights, xcm_config::UniversalLocation, - BridgeWestendMessages, PolkadotXcm, Runtime, RuntimeEvent, XcmOverBridgeHubWestend, XcmRouter, + AccountId, Balance, Balances, BridgeWestendMessages, PolkadotXcm, Runtime, RuntimeEvent, + RuntimeHoldReason, XcmOverBridgeHubWestend, XcmRouter, }; use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, - target_chain::FromBridgedChainMessagesProof, LaneId, -}; -use bridge_runtime_common::{ - extensions::refund_relayer_extension::{ - ActualFeeRefund, RefundBridgedMessages, RefundSignedExtensionAdapter, - RefundableMessagesLane, - }, - messages_xcm_extension::{ - SenderAndLane, XcmAsPlainPayload, XcmBlobHauler, XcmBlobHaulerAdapter, - XcmBlobMessageDispatch, XcmVersionOfDestAndRemoteBridge, - }, + target_chain::FromBridgedChainMessagesProof, LegacyLaneId, }; +use bridge_hub_common::xcm_version::XcmVersionOfDestAndRemoteBridge; +use pallet_xcm_bridge_hub::XcmAsPlainPayload; -use codec::Encode; use frame_support::{parameter_types, traits::PalletInfoAccess}; +use frame_system::{EnsureNever, EnsureRoot}; +use pallet_bridge_messages::LaneIdOf; +use pallet_bridge_relayers::extension::{ + BridgeRelayersSignedExtension, WithMessagesExtensionConfig, +}; +use parachains_common::xcm_config::{AllSiblingSystemParachains, RelayOrOtherSystemParachains}; +use polkadot_parachain_primitives::primitives::Sibling; +use testnet_parachains_constants::rococo::currency::UNITS as ROC; use xcm::{ latest::prelude::*, prelude::{InteriorLocation, NetworkId}, }; -use xcm_builder::BridgeBlobDispatcher; +use xcm_builder::{BridgeBlobDispatcher, ParentIsPreset, SiblingParachainConvertsVia}; parameter_types! { pub BridgeRococoToWestendMessagesPalletInstance: InteriorLocation = [PalletInstance(::index() as u8)].into(); @@ -59,26 +62,6 @@ parameter_types! { // see the `FEE_BOOST_PER_MESSAGE` constant to get the meaning of this value pub PriorityBoostPerMessage: u64 = 182_044_444_444_444; - pub AssetHubRococoParaId: cumulus_primitives_core::ParaId = bp_asset_hub_rococo::ASSET_HUB_ROCOCO_PARACHAIN_ID.into(); - pub AssetHubWestendParaId: cumulus_primitives_core::ParaId = bp_asset_hub_westend::ASSET_HUB_WESTEND_PARACHAIN_ID.into(); - - // Lanes - pub ActiveOutboundLanesToBridgeHubWestend: &'static [bp_messages::LaneId] = &[XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND]; - pub const AssetHubRococoToAssetHubWestendMessagesLane: bp_messages::LaneId = XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND; - pub FromAssetHubRococoToAssetHubWestendRoute: SenderAndLane = SenderAndLane::new( - ParentThen([Parachain(AssetHubRococoParaId::get().into())].into()).into(), - XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND, - ); - pub ActiveLanes: alloc::vec::Vec<(SenderAndLane, (NetworkId, InteriorLocation))> = alloc::vec![ - ( - FromAssetHubRococoToAssetHubWestendRoute::get(), - (WestendGlobalConsensusNetwork::get(), [Parachain(AssetHubWestendParaId::get().into())].into()) - ) - ]; - - pub CongestedMessage: Xcm<()> = build_congestion_message(true).into(); - pub UncongestedMessage: Xcm<()> = build_congestion_message(false).into(); - pub BridgeHubWestendLocation: Location = Location::new( 2, [ @@ -86,67 +69,32 @@ parameter_types! { Parachain(::PARACHAIN_ID) ] ); -} -pub const XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND: LaneId = LaneId([0, 0, 0, 2]); - -fn build_congestion_message(is_congested: bool) -> alloc::vec::Vec> { - alloc::vec![ - UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - Transact { - origin_kind: OriginKind::Xcm, - require_weight_at_most: - bp_asset_hub_rococo::XcmBridgeHubRouterTransactCallMaxWeight::get(), - call: bp_asset_hub_rococo::Call::ToWestendXcmRouter( - bp_asset_hub_rococo::XcmBridgeHubRouterCall::report_bridge_status { - bridge_id: Default::default(), - is_congested, - } - ) - .encode() - .into(), - } - ] + + pub storage BridgeDeposit: Balance = 5 * ROC; } /// Proof of messages, coming from Westend. -pub type FromWestendBridgeHubMessagesProof = - FromBridgedChainMessagesProof; +pub type FromWestendBridgeHubMessagesProof = + FromBridgedChainMessagesProof>; /// Messages delivery proof for Rococo Bridge Hub -> Westend Bridge Hub messages. -pub type ToWestendBridgeHubMessagesDeliveryProof = - FromBridgedChainMessagesDeliveryProof; +pub type ToWestendBridgeHubMessagesDeliveryProof = + FromBridgedChainMessagesDeliveryProof>; /// Dispatches received XCM messages from other bridge type FromWestendMessageBlobDispatcher = BridgeBlobDispatcher; -/// Export XCM messages to be relayed to the other side -pub type ToBridgeHubWestendHaulBlobExporter = XcmOverBridgeHubWestend; - -pub struct ToBridgeHubWestendXcmBlobHauler; -impl XcmBlobHauler for ToBridgeHubWestendXcmBlobHauler { - type Runtime = Runtime; - type MessagesInstance = WithBridgeHubWestendMessagesInstance; - type ToSourceChainSender = XcmRouter; - type CongestedMessage = CongestedMessage; - type UncongestedMessage = UncongestedMessage; -} - -/// On messages delivered callback. -type OnMessagesDeliveredFromWestend = - XcmBlobHaulerAdapter; - /// Signed extension that refunds relayers that are delivering messages from the Westend parachain. -pub type OnBridgeHubRococoRefundBridgeHubWestendMessages = RefundSignedExtensionAdapter< - RefundBridgedMessages< +pub type OnBridgeHubRococoRefundBridgeHubWestendMessages = BridgeRelayersSignedExtension< + Runtime, + WithMessagesExtensionConfig< + StrOnBridgeHubRococoRefundBridgeHubWestendMessages, Runtime, - RefundableMessagesLane< - WithBridgeHubWestendMessagesInstance, - AssetHubRococoToAssetHubWestendMessagesLane, - >, - ActualFeeRefund, + WithBridgeHubWestendMessagesInstance, + RelayersForLegacyLaneIdsMessagesInstance, PriorityBoostPerMessage, - StrOnBridgeHubRococoRefundBridgeHubWestendMessages, >, + LaneIdOf, >; bp_runtime::generate_static_str_provider!(OnBridgeHubRococoRefundBridgeHubWestendMessages); @@ -164,41 +112,99 @@ impl pallet_bridge_messages::Config for Ru bp_bridge_hub_westend::BridgeHubWestend, >; - type ActiveOutboundLanes = ActiveOutboundLanesToBridgeHubWestend; - type OutboundPayload = XcmAsPlainPayload; - type InboundPayload = XcmAsPlainPayload; - type DeliveryPayments = (); + type LaneId = LegacyLaneId; + type DeliveryPayments = (); type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< Runtime, WithBridgeHubWestendMessagesInstance, DeliveryRewardInBalance, >; - type MessageDispatch = XcmBlobMessageDispatch< - FromWestendMessageBlobDispatcher, - Self::WeightInfo, - cumulus_pallet_xcmp_queue::bridging::OutXcmpChannelStatusProvider< - AssetHubRococoParaId, - Runtime, - >, - >; - type OnMessagesDelivered = OnMessagesDeliveredFromWestend; + type MessageDispatch = XcmOverBridgeHubWestend; + type OnMessagesDelivered = XcmOverBridgeHubWestend; } -/// Add support for the export and dispatch of XCM programs. +/// Add support for the export and dispatch of XCM programs withing +/// `WithBridgeHubWestendMessagesInstance`. pub type XcmOverBridgeHubWestendInstance = pallet_xcm_bridge_hub::Instance1; impl pallet_xcm_bridge_hub::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type UniversalLocation = UniversalLocation; type BridgedNetwork = WestendGlobalConsensusNetworkLocation; type BridgeMessagesPalletInstance = WithBridgeHubWestendMessagesInstance; + type MessageExportPrice = (); type DestinationVersion = XcmVersionOfDestAndRemoteBridge; - type Lanes = ActiveLanes; - type LanesSupport = ToBridgeHubWestendXcmBlobHauler; + + type ForceOrigin = EnsureRoot; + // We don't want to allow creating bridges for this instance with `LegacyLaneId`. + type OpenBridgeOrigin = EnsureNever; + // Converter aligned with `OpenBridgeOrigin`. + type BridgeOriginAccountIdConverter = + (ParentIsPreset, SiblingParachainConvertsVia); + + type BridgeDeposit = BridgeDeposit; + type Currency = Balances; + type RuntimeHoldReason = RuntimeHoldReason; + // Do not require deposit from system parachains or relay chain + type AllowWithoutBridgeDeposit = + RelayOrOtherSystemParachains; + + // TODO:(bridges-v2) - add `LocalXcmChannelManager` impl - https://github.com/paritytech/parity-bridges-common/issues/3047 + type LocalXcmChannelManager = (); + type BlobDispatcher = FromWestendMessageBlobDispatcher; +} + +#[cfg(feature = "runtime-benchmarks")] +pub(crate) fn open_bridge_for_benchmarks( + with: pallet_xcm_bridge_hub::LaneIdOf, + sibling_para_id: u32, +) -> InteriorLocation +where + R: pallet_xcm_bridge_hub::Config, + XBHI: 'static, + C: xcm_executor::traits::ConvertLocation< + bp_runtime::AccountIdOf>, + >, +{ + use pallet_xcm_bridge_hub::{Bridge, BridgeId, BridgeState}; + use sp_runtime::traits::Zero; + use xcm::VersionedInteriorLocation; + + // insert bridge metadata + let lane_id = with; + let sibling_parachain = Location::new(1, [Parachain(sibling_para_id)]); + let universal_source = [GlobalConsensus(Rococo), Parachain(sibling_para_id)].into(); + let universal_destination = [GlobalConsensus(Westend), Parachain(2075)].into(); + let bridge_id = BridgeId::new(&universal_source, &universal_destination); + + // insert only bridge metadata, because the benchmarks create lanes + pallet_xcm_bridge_hub::Bridges::::insert( + bridge_id, + Bridge { + bridge_origin_relative_location: alloc::boxed::Box::new( + sibling_parachain.clone().into(), + ), + bridge_origin_universal_location: alloc::boxed::Box::new( + VersionedInteriorLocation::from(universal_source.clone()), + ), + bridge_destination_universal_location: alloc::boxed::Box::new( + VersionedInteriorLocation::from(universal_destination), + ), + state: BridgeState::Opened, + bridge_owner_account: C::convert_location(&sibling_parachain).expect("valid AccountId"), + deposit: Zero::zero(), + lane_id, + }, + ); + pallet_xcm_bridge_hub::LaneToBridge::::insert(lane_id, bridge_id); + + universal_source } #[cfg(test)] @@ -207,14 +213,11 @@ mod tests { use crate::bridge_common_config::BridgeGrandpaWestendInstance; use bridge_runtime_common::{ assert_complete_bridge_types, - extensions::refund_relayer_extension::RefundableParachain, integrity::{ assert_complete_with_parachain_bridge_constants, check_message_lane_weights, AssertChainConstants, AssertCompleteBridgeConstants, }, }; - use parachains_common::Balance; - use testnet_parachains_constants::rococo; /// Every additional message in the message delivery transaction boosts its priority. /// So the priority of transaction with `N+1` messages is larger than priority of @@ -225,12 +228,12 @@ mod tests { /// /// We want this tip to be large enough (delivery transactions with more messages = less /// operational costs and a faster bridge), so this value should be significant. - const FEE_BOOST_PER_MESSAGE: Balance = 2 * rococo::currency::UNITS; + const FEE_BOOST_PER_MESSAGE: Balance = 2 * ROC; // see `FEE_BOOST_PER_MESSAGE` comment - const FEE_BOOST_PER_RELAY_HEADER: Balance = 2 * rococo::currency::UNITS; + const FEE_BOOST_PER_RELAY_HEADER: Balance = 2 * ROC; // see `FEE_BOOST_PER_MESSAGE` comment - const FEE_BOOST_PER_PARACHAIN_HEADER: Balance = 2 * rococo::currency::UNITS; + const FEE_BOOST_PER_PARACHAIN_HEADER: Balance = 2 * ROC; #[test] fn ensure_bridge_hub_rococo_message_lane_weights_are_correct() { @@ -268,19 +271,20 @@ mod tests { }, }); - bridge_runtime_common::extensions::priority_calculator::per_relay_header::ensure_priority_boost_is_sane::< + pallet_bridge_relayers::extension::per_relay_header::ensure_priority_boost_is_sane::< Runtime, BridgeGrandpaWestendInstance, PriorityBoostPerRelayHeader, >(FEE_BOOST_PER_RELAY_HEADER); - bridge_runtime_common::extensions::priority_calculator::per_parachain_header::ensure_priority_boost_is_sane::< + pallet_bridge_relayers::extension::per_parachain_header::ensure_priority_boost_is_sane::< Runtime, - RefundableParachain, + WithBridgeHubWestendMessagesInstance, + bp_bridge_hub_westend::BridgeHubWestend, PriorityBoostPerParachainHeader, >(FEE_BOOST_PER_PARACHAIN_HEADER); - bridge_runtime_common::extensions::priority_calculator::per_message::ensure_priority_boost_is_sane::< + pallet_bridge_relayers::extension::per_message::ensure_priority_boost_is_sane::< Runtime, WithBridgeHubWestendMessagesInstance, PriorityBoostPerMessage, @@ -294,3 +298,98 @@ mod tests { assert_eq!(BridgeRococoToWestendMessagesPalletInstance::get(), expected,); } } + +/// Contains the migration for the AssetHubRococo<>AssetHubWestend bridge. +pub mod migration { + use super::*; + use frame_support::traits::ConstBool; + + parameter_types! { + pub AssetHubRococoToAssetHubWestendMessagesLane: LegacyLaneId = LegacyLaneId([0, 0, 0, 2]); + pub AssetHubRococoLocation: Location = Location::new(1, [Parachain(bp_asset_hub_rococo::ASSET_HUB_ROCOCO_PARACHAIN_ID)]); + pub AssetHubWestendUniversalLocation: InteriorLocation = [GlobalConsensus(WestendGlobalConsensusNetwork::get()), Parachain(bp_asset_hub_westend::ASSET_HUB_WESTEND_PARACHAIN_ID)].into(); + } + + /// Ensure that the existing lanes for the AHR<>AHW bridge are correctly configured. + pub type StaticToDynamicLanes = pallet_xcm_bridge_hub::migration::OpenBridgeForLane< + Runtime, + XcmOverBridgeHubWestendInstance, + AssetHubRococoToAssetHubWestendMessagesLane, + // the lanes are already created for AHR<>AHW, but we need to link them to the bridge + // structs + ConstBool, + AssetHubRococoLocation, + AssetHubWestendUniversalLocation, + >; + + mod v1_wrong { + use bp_messages::{LaneState, MessageNonce, UnrewardedRelayer}; + use bp_runtime::AccountIdOf; + use codec::{Decode, Encode}; + use pallet_bridge_messages::BridgedChainOf; + use sp_std::collections::vec_deque::VecDeque; + + #[derive(Encode, Decode, Clone, PartialEq, Eq)] + pub(crate) struct StoredInboundLaneData, I: 'static>( + pub(crate) InboundLaneData>>, + ); + #[derive(Encode, Decode, Clone, PartialEq, Eq)] + pub(crate) struct InboundLaneData { + pub state: LaneState, + pub(crate) relayers: VecDeque>, + pub(crate) last_confirmed_nonce: MessageNonce, + } + #[derive(Encode, Decode, Clone, PartialEq, Eq)] + pub(crate) struct OutboundLaneData { + pub state: LaneState, + pub(crate) oldest_unpruned_nonce: MessageNonce, + pub(crate) latest_received_nonce: MessageNonce, + pub(crate) latest_generated_nonce: MessageNonce, + } + } + + mod v1 { + pub use bp_messages::{InboundLaneData, LaneState, OutboundLaneData}; + pub use pallet_bridge_messages::{InboundLanes, OutboundLanes, StoredInboundLaneData}; + } + + /// Fix for v1 migration - corrects data for OutboundLaneData/InboundLaneData (it is needed only + /// for Rococo/Westend). + pub struct FixMessagesV1Migration(sp_std::marker::PhantomData<(T, I)>); + + impl, I: 'static> frame_support::traits::OnRuntimeUpgrade + for FixMessagesV1Migration + { + fn on_runtime_upgrade() -> Weight { + use sp_core::Get; + let mut weight = T::DbWeight::get().reads(1); + + // `InboundLanes` - add state to the old structs + let translate_inbound = + |pre: v1_wrong::StoredInboundLaneData| -> Option> { + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + Some(v1::StoredInboundLaneData(v1::InboundLaneData { + state: v1::LaneState::Opened, + relayers: pre.0.relayers, + last_confirmed_nonce: pre.0.last_confirmed_nonce, + })) + }; + v1::InboundLanes::::translate_values(translate_inbound); + + // `OutboundLanes` - add state to the old structs + let translate_outbound = + |pre: v1_wrong::OutboundLaneData| -> Option { + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + Some(v1::OutboundLaneData { + state: v1::LaneState::Opened, + oldest_unpruned_nonce: pre.oldest_unpruned_nonce, + latest_received_nonce: pre.latest_received_nonce, + latest_generated_nonce: pre.latest_generated_nonce, + }) + }; + v1::OutboundLanes::::translate_values(translate_outbound); + + weight + } + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/genesis_config_presets.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/genesis_config_presets.rs new file mode 100644 index 0000000000000000000000000000000000000000..07048d54ab1ba96acbe3fd9c32f17ab686da04cd --- /dev/null +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/genesis_config_presets.rs @@ -0,0 +1,174 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! # Bridge Hub Rococo Runtime genesis config presets + +use crate::*; +use alloc::{vec, vec::Vec}; +use cumulus_primitives_core::ParaId; +use parachains_common::{genesis_config_helpers::*, AccountId, AuraId}; +use sp_core::sr25519; +use sp_genesis_builder::PresetId; +use testnet_parachains_constants::rococo::xcm_version::SAFE_XCM_VERSION; + +const BRIDGE_HUB_ROCOCO_ED: Balance = ExistentialDeposit::get(); + +fn bridge_hub_rococo_genesis( + invulnerables: Vec<(AccountId, AuraId)>, + endowed_accounts: Vec, + id: ParaId, + bridges_pallet_owner: Option, + asset_hub_para_id: ParaId, + opened_bridges: Vec<(Location, InteriorLocation, Option)>, +) -> serde_json::Value { + let config = RuntimeGenesisConfig { + balances: BalancesConfig { + balances: endowed_accounts + .iter() + .cloned() + .map(|k| (k, 1u128 << 60)) + .collect::>(), + }, + parachain_info: ParachainInfoConfig { parachain_id: id, ..Default::default() }, + collator_selection: CollatorSelectionConfig { + invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(), + candidacy_bond: BRIDGE_HUB_ROCOCO_ED * 16, + ..Default::default() + }, + session: SessionConfig { + keys: invulnerables + .into_iter() + .map(|(acc, aura)| { + ( + acc.clone(), // account id + acc, // validator id + SessionKeys { aura }, // session keys + ) + }) + .collect(), + ..Default::default() + }, + polkadot_xcm: PolkadotXcmConfig { + safe_xcm_version: Some(SAFE_XCM_VERSION), + ..Default::default() + }, + bridge_westend_grandpa: BridgeWestendGrandpaConfig { + owner: bridges_pallet_owner.clone(), + ..Default::default() + }, + bridge_westend_messages: BridgeWestendMessagesConfig { + owner: bridges_pallet_owner.clone(), + ..Default::default() + }, + xcm_over_bridge_hub_westend: XcmOverBridgeHubWestendConfig { + opened_bridges, + ..Default::default() + }, + ethereum_system: EthereumSystemConfig { + para_id: id, + asset_hub_para_id, + ..Default::default() + }, + ..Default::default() + }; + + serde_json::to_value(config).expect("Could not build genesis config.") +} + +/// Provides the JSON representation of predefined genesis config for given `id`. +pub fn get_preset(id: &sp_genesis_builder::PresetId) -> Option> { + let patch = match id.try_into() { + Ok(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET) => bridge_hub_rococo_genesis( + // initial collators. + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed::("Bob"), + ), + ], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + 1013.into(), + Some(get_account_id_from_seed::("Bob")), + rococo_runtime_constants::system_parachain::ASSET_HUB_ID.into(), + vec![( + Location::new(1, [Parachain(1000)]), + Junctions::from([Westend.into(), Parachain(1000)]), + Some(bp_messages::LegacyLaneId([0, 0, 0, 2])), + )], + ), + Ok(sp_genesis_builder::DEV_RUNTIME_PRESET) => bridge_hub_rococo_genesis( + // initial collators. + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed::("Bob"), + ), + ], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + 1013.into(), + Some(get_account_id_from_seed::("Bob")), + rococo_runtime_constants::system_parachain::ASSET_HUB_ID.into(), + vec![], + ), + _ => return None, + }; + Some( + serde_json::to_string(&patch) + .expect("serialization to json is expected to work. qed.") + .into_bytes(), + ) +} + +/// List of supported presets. +pub fn preset_names() -> Vec { + vec![ + PresetId::from(sp_genesis_builder::DEV_RUNTIME_PRESET), + PresetId::from(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET), + ] +} 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 c65880771e0863540bf6ff5687d5a8b18c8b3c4c..e3435f35f1788a5bfc49a9ceb1bc4271807f7eff 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 @@ -32,6 +32,7 @@ pub mod bridge_common_config; pub mod bridge_to_bulletin_config; pub mod bridge_to_ethereum_config; pub mod bridge_to_westend_config; +mod genesis_config_presets; mod weights; pub mod xcm_config; @@ -39,33 +40,24 @@ extern crate alloc; use alloc::{vec, vec::Vec}; use bridge_runtime_common::extensions::{ - check_obsolete_extension::{ - CheckAndBoostBridgeGrandpaTransactions, CheckAndBoostBridgeParachainsTransactions, - }, - refund_relayer_extension::RefundableParachain, + CheckAndBoostBridgeGrandpaTransactions, CheckAndBoostBridgeParachainsTransactions, }; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; -use snowbridge_beacon_primitives::{Fork, ForkVersions}; -use snowbridge_core::{ - gwei, meth, - outbound::{Command, Fee}, - AgentId, AllowSiblingsOnly, PricingParameters, Rewards, -}; -use snowbridge_router_primitives::inbound::MessageToXcm; +use pallet_bridge_messages::LaneIdOf; use sp_api::impl_runtime_apis; -use sp_core::{crypto::KeyTypeId, OpaqueMetadata, H160}; +use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::{Block as BlockT, Keccak256}, + traits::Block as BlockT, transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, FixedU128, + ApplyExtrinsicResult, }; #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; -use cumulus_primitives_core::ParaId; +use cumulus_primitives_core::{ClaimQueueOffset, CoreSelector, ParaId}; use frame_support::{ construct_runtime, derive_impl, dispatch::DispatchClass, @@ -79,16 +71,13 @@ use frame_system::{ limits::{BlockLength, BlockWeights}, EnsureRoot, }; -use testnet_parachains_constants::rococo::{ - consensus::*, currency::*, fee::WeightToFee, snowbridge::INBOUND_QUEUE_PALLET_INDEX, time::*, -}; +use testnet_parachains_constants::rococo::{consensus::*, currency::*, fee::WeightToFee, time::*}; use bp_runtime::HeaderId; use bridge_hub_common::{ message_queue::{NarrowOriginToSibling, ParaIdToSibling}, AggregateMessageOrigin, }; -use pallet_xcm::EnsureXcm; pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; pub use sp_runtime::{MultiAddress, Perbill, Permill}; use xcm::VersionedLocation; @@ -99,7 +88,11 @@ pub use sp_runtime::BuildStorage; use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; use rococo_runtime_constants::system_parachain::{ASSET_HUB_ID, BRIDGE_HUB_ID}; -use xcm::prelude::*; +use snowbridge_core::{ + outbound::{Command, Fee}, + AgentId, PricingParameters, +}; +use xcm::{latest::prelude::*, prelude::*}; use xcm_runtime_apis::{ dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects}, fees::Error as XcmPaymentApiError, @@ -114,8 +107,6 @@ use parachains_common::{ #[cfg(feature = "runtime-benchmarks")] use alloc::boxed::Box; -#[cfg(feature = "runtime-benchmarks")] -use benchmark_helpers::DoNothingRouter; /// The address format for describing accounts. pub type Address = MultiAddress; @@ -140,10 +131,7 @@ pub type SignedExtra = ( frame_system::CheckWeight, pallet_transaction_payment::ChargeTransactionPayment, BridgeRejectObsoleteHeadersAndMessages, - ( - bridge_to_westend_config::OnBridgeHubRococoRefundBridgeHubWestendMessages, - bridge_to_bulletin_config::OnBridgeHubRococoRefundRococoBulletinMessages, - ), + (bridge_to_westend_config::OnBridgeHubRococoRefundBridgeHubWestendMessages,), cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim, frame_metadata_hash_extension::CheckMetadataHash, ); @@ -165,10 +153,41 @@ pub type Migrations = ( ConstU32, ConstU32, >, + pallet_bridge_messages::migration::v1::MigrationToV1< + Runtime, + bridge_to_westend_config::WithBridgeHubWestendMessagesInstance, + >, + pallet_bridge_messages::migration::v1::MigrationToV1< + Runtime, + bridge_to_bulletin_config::WithRococoBulletinMessagesInstance, + >, + bridge_to_westend_config::migration::FixMessagesV1Migration< + Runtime, + bridge_to_westend_config::WithBridgeHubWestendMessagesInstance, + >, + bridge_to_westend_config::migration::StaticToDynamicLanes, + bridge_to_bulletin_config::migration::StaticToDynamicLanes, + frame_support::migrations::RemoveStorage< + BridgeWestendMessagesPalletName, + OutboundLanesCongestedSignalsKey, + RocksDbWeight, + >, + frame_support::migrations::RemoveStorage< + BridgePolkadotBulletinMessagesPalletName, + OutboundLanesCongestedSignalsKey, + RocksDbWeight, + >, + pallet_bridge_relayers::migration::v1::MigrationToV1, // permanent pallet_xcm::migration::MigrateToLatestXcmVersion, ); +parameter_types! { + pub const BridgeWestendMessagesPalletName: &'static str = "BridgeWestendMessages"; + pub const BridgePolkadotBulletinMessagesPalletName: &'static str = "BridgePolkadotBulletinMessages"; + pub const OutboundLanesCongestedSignalsKey: &'static str = "OutboundLanesCongestedSignals"; +} + /// Migration to initialize storage versions for pallets added after genesis. /// /// Ideally this would be done automatically (see @@ -219,11 +238,11 @@ 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_016_000, + spec_version: 1_016_001, impl_version: 0, apis: RUNTIME_API_VERSIONS, - transaction_version: 5, - state_version: 1, + transaction_version: 6, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. @@ -323,6 +342,7 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<0>; + type DoneSlashHandler = (); } parameter_types! { @@ -357,6 +377,7 @@ impl cumulus_pallet_parachain_system::Config for Runtime { type ReservedXcmpWeight = ReservedXcmpWeight; type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases; type ConsensusHook = ConsensusHook; + type SelectCore = cumulus_pallet_parachain_system::DefaultCoreSelector; } type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< @@ -519,174 +540,6 @@ impl pallet_utility::Config for Runtime { type WeightInfo = weights::pallet_utility::WeightInfo; } -// Ethereum Bridge -parameter_types! { - pub storage EthereumGatewayAddress: H160 = H160(hex_literal::hex!("EDa338E4dC46038493b885327842fD3E301CaB39")); -} - -parameter_types! { - pub const CreateAssetCall: [u8;2] = [53, 0]; - pub const CreateAssetDeposit: u128 = (UNITS / 10) + EXISTENTIAL_DEPOSIT; - pub Parameters: PricingParameters = PricingParameters { - exchange_rate: FixedU128::from_rational(1, 400), - fee_per_gas: gwei(20), - rewards: Rewards { local: 1 * UNITS, remote: meth(1) }, - multiplier: FixedU128::from_rational(1, 1), - }; -} - -#[cfg(feature = "runtime-benchmarks")] -pub mod benchmark_helpers { - use crate::{EthereumBeaconClient, Runtime, RuntimeOrigin}; - use codec::Encode; - use snowbridge_beacon_primitives::BeaconHeader; - use snowbridge_pallet_inbound_queue::BenchmarkHelper; - use sp_core::H256; - use xcm::latest::{Assets, Location, SendError, SendResult, SendXcm, Xcm, XcmHash}; - - impl BenchmarkHelper for Runtime { - fn initialize_storage(beacon_header: BeaconHeader, block_roots_root: H256) { - EthereumBeaconClient::store_finalized_header(beacon_header, block_roots_root).unwrap(); - } - } - - pub struct DoNothingRouter; - impl SendXcm for DoNothingRouter { - type Ticket = Xcm<()>; - - fn validate( - _dest: &mut Option, - xcm: &mut Option>, - ) -> SendResult { - Ok((xcm.clone().unwrap(), Assets::new())) - } - fn deliver(xcm: Xcm<()>) -> Result { - let hash = xcm.using_encoded(sp_io::hashing::blake2_256); - Ok(hash) - } - } - - impl snowbridge_pallet_system::BenchmarkHelper for () { - fn make_xcm_origin(location: Location) -> RuntimeOrigin { - RuntimeOrigin::from(pallet_xcm::Origin::Xcm(location)) - } - } -} - -impl snowbridge_pallet_inbound_queue::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Verifier = snowbridge_pallet_ethereum_client::Pallet; - type Token = Balances; - #[cfg(not(feature = "runtime-benchmarks"))] - type XcmSender = XcmRouter; - #[cfg(feature = "runtime-benchmarks")] - type XcmSender = DoNothingRouter; - type ChannelLookup = EthereumSystem; - type GatewayAddress = EthereumGatewayAddress; - #[cfg(feature = "runtime-benchmarks")] - type Helper = Runtime; - type MessageConverter = MessageToXcm< - CreateAssetCall, - CreateAssetDeposit, - ConstU8, - AccountId, - Balance, - >; - type WeightToFee = WeightToFee; - type LengthToFee = ConstantMultiplier; - type MaxMessageSize = ConstU32<2048>; - type WeightInfo = weights::snowbridge_pallet_inbound_queue::WeightInfo; - type PricingParameters = EthereumSystem; - type AssetTransactor = ::AssetTransactor; -} - -impl snowbridge_pallet_outbound_queue::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Hashing = Keccak256; - type MessageQueue = MessageQueue; - type Decimals = ConstU8<12>; - type MaxMessagePayloadSize = ConstU32<2048>; - type MaxMessagesPerBlock = ConstU32<32>; - type GasMeter = snowbridge_core::outbound::ConstantGasMeter; - type Balance = Balance; - type WeightToFee = WeightToFee; - type WeightInfo = weights::snowbridge_pallet_outbound_queue::WeightInfo; - type PricingParameters = EthereumSystem; - type Channels = EthereumSystem; -} - -#[cfg(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test))] -parameter_types! { - pub const ChainForkVersions: ForkVersions = ForkVersions { - genesis: Fork { - version: [0, 0, 0, 0], // 0x00000000 - epoch: 0, - }, - altair: Fork { - version: [1, 0, 0, 0], // 0x01000000 - epoch: 0, - }, - bellatrix: Fork { - version: [2, 0, 0, 0], // 0x02000000 - epoch: 0, - }, - capella: Fork { - version: [3, 0, 0, 0], // 0x03000000 - epoch: 0, - }, - deneb: Fork { - version: [4, 0, 0, 0], // 0x04000000 - epoch: 0, - } - }; -} - -#[cfg(not(any(feature = "std", feature = "fast-runtime", feature = "runtime-benchmarks", test)))] -parameter_types! { - pub const ChainForkVersions: ForkVersions = ForkVersions { - genesis: Fork { - version: [144, 0, 0, 111], // 0x90000069 - epoch: 0, - }, - altair: Fork { - version: [144, 0, 0, 112], // 0x90000070 - epoch: 50, - }, - bellatrix: Fork { - version: [144, 0, 0, 113], // 0x90000071 - epoch: 100, - }, - capella: Fork { - version: [144, 0, 0, 114], // 0x90000072 - epoch: 56832, - }, - deneb: Fork { - version: [144, 0, 0, 115], // 0x90000073 - epoch: 132608, - }, - }; -} - -impl snowbridge_pallet_ethereum_client::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type ForkVersions = ChainForkVersions; - type WeightInfo = weights::snowbridge_pallet_ethereum_client::WeightInfo; -} - -impl snowbridge_pallet_system::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type OutboundQueue = EthereumOutboundQueue; - type SiblingOrigin = EnsureXcm; - type AgentIdOf = snowbridge_core::AgentIdOf; - type TreasuryAccount = TreasuryAccount; - type Token = Balances; - type WeightInfo = weights::snowbridge_pallet_system::WeightInfo; - #[cfg(feature = "runtime-benchmarks")] - type Helper = (); - type DefaultPricingParameters = Parameters; - type InboundDeliveryCost = EthereumInboundQueue; -} - // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub enum Runtime @@ -744,6 +597,9 @@ construct_runtime!( // With-Rococo Bulletin bridge hub pallet. XcmOverPolkadotBulletin: pallet_xcm_bridge_hub:: = 62, + // Bridge relayers pallet, used by several bridges here (another instance). + BridgeRelayersForPermissionlessLanes: pallet_bridge_relayers:: = 63, + EthereumInboundQueue: snowbridge_pallet_inbound_queue = 80, EthereumOutboundQueue: snowbridge_pallet_outbound_queue = 81, EthereumBeaconClient: snowbridge_pallet_ethereum_client = 82, @@ -780,10 +636,8 @@ bridge_runtime_common::generate_bridge_reject_obsolete_headers_and_messages! { // Parachains CheckAndBoostBridgeParachainsTransactions< Runtime, - RefundableParachain< bridge_common_config::BridgeParachainWestendInstance, - bp_bridge_hub_westend::BridgeHubWestend, - >, + bp_bridge_hub_westend::BridgeHubWestend, bridge_to_westend_config::PriorityBoostPerParachainHeader, xcm_config::TreasuryAccount, >, @@ -815,7 +669,8 @@ mod benches { [pallet_bridge_parachains, WithinWestend] [pallet_bridge_messages, RococoToWestend] [pallet_bridge_messages, RococoToRococoBulletin] - [pallet_bridge_relayers, BridgeRelayersBench::] + [pallet_bridge_relayers, Legacy] + [pallet_bridge_relayers, PermissionlessLanes] // Ethereum Bridge [snowbridge_pallet_inbound_queue, EthereumInboundQueue] [snowbridge_pallet_outbound_queue, EthereumOutboundQueue] @@ -824,6 +679,11 @@ mod benches { ); } +cumulus_pallet_parachain_system::register_validate_block! { + Runtime = Runtime, + BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::, +} + impl_runtime_apis! { impl sp_consensus_aura::AuraApi for Runtime { fn slot_duration() -> sp_consensus_aura::SlotDuration { @@ -1031,6 +891,12 @@ impl_runtime_apis! { } } + impl cumulus_primitives_core::GetCoreSelectorApi for Runtime { + fn core_selector() -> (CoreSelector, ClaimQueueOffset) { + ParachainSystem::core_selector() + } + } + impl bp_westend::WestendFinalityApi for Runtime { fn best_finalized() -> Option> { BridgeWestendGrandpa::best_finalized() @@ -1061,7 +927,7 @@ impl_runtime_apis! { // This is exposed by BridgeHubRococo impl bp_bridge_hub_westend::FromBridgeHubWestendInboundLaneApi for Runtime { fn message_details( - lane: bp_messages::LaneId, + lane: LaneIdOf, messages: Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>, ) -> Vec { bridge_runtime_common::messages_api::inbound_message_details::< @@ -1074,7 +940,7 @@ impl_runtime_apis! { // This is exposed by BridgeHubRococo impl bp_bridge_hub_westend::ToBridgeHubWestendOutboundLaneApi for Runtime { fn message_details( - lane: bp_messages::LaneId, + lane: LaneIdOf, begin: bp_messages::MessageNonce, end: bp_messages::MessageNonce, ) -> Vec { @@ -1104,7 +970,7 @@ impl_runtime_apis! { impl bp_polkadot_bulletin::FromPolkadotBulletinInboundLaneApi for Runtime { fn message_details( - lane: bp_messages::LaneId, + lane: LaneIdOf, messages: Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>, ) -> Vec { bridge_runtime_common::messages_api::inbound_message_details::< @@ -1116,7 +982,7 @@ impl_runtime_apis! { impl bp_polkadot_bulletin::ToPolkadotBulletinOutboundLaneApi for Runtime { fn message_details( - lane: bp_messages::LaneId, + lane: LaneIdOf, begin: bp_messages::MessageNonce, end: bp_messages::MessageNonce, ) -> Vec { @@ -1186,6 +1052,8 @@ impl_runtime_apis! { type WithinWestend = pallet_bridge_parachains::benchmarking::Pallet::; type RococoToWestend = pallet_bridge_messages::benchmarking::Pallet ::; type RococoToRococoBulletin = pallet_bridge_messages::benchmarking::Pallet ::; + type Legacy = BridgeRelayersBench::; + type PermissionlessLanes = BridgeRelayersBench::; let mut list = Vec::::new(); list_benchmarks!(list, extra); @@ -1379,11 +1247,46 @@ impl_runtime_apis! { ); BenchmarkError::Stop("XcmVersion was not stored!") })?; + + let sibling_parachain_location = Location::new(1, [Parachain(5678)]); + + // fund SA + use frame_support::traits::fungible::Mutate; + use xcm_executor::traits::ConvertLocation; + frame_support::assert_ok!( + Balances::mint_into( + &xcm_config::LocationToAccountId::convert_location(&sibling_parachain_location).expect("valid AccountId"), + bridge_to_westend_config::BridgeDeposit::get() + .saturating_add(ExistentialDeposit::get()) + .saturating_add(UNITS * 5) + ) + ); + + // open bridge + let bridge_destination_universal_location: InteriorLocation = [GlobalConsensus(NetworkId::Westend), Parachain(8765)].into(); + let locations = XcmOverBridgeHubWestend::bridge_locations( + sibling_parachain_location.clone(), + bridge_destination_universal_location.clone(), + )?; + XcmOverBridgeHubWestend::do_open_bridge( + locations, + bp_messages::LegacyLaneId([1, 2, 3, 4]), + true, + ).map_err(|e| { + log::error!( + "Failed to `XcmOverBridgeHubWestend::open_bridge`({:?}, {:?})`, error: {:?}", + sibling_parachain_location, + bridge_destination_universal_location, + e + ); + BenchmarkError::Stop("Bridge was not opened!") + })?; + Ok( ( - bridge_to_westend_config::FromAssetHubRococoToAssetHubWestendRoute::get().location, + sibling_parachain_location, NetworkId::Westend, - [Parachain(bridge_to_westend_config::AssetHubWestendParaId::get().into())].into() + [Parachain(8765)].into() ) ) } @@ -1400,6 +1303,8 @@ impl_runtime_apis! { type WithinWestend = pallet_bridge_parachains::benchmarking::Pallet::; type RococoToWestend = pallet_bridge_messages::benchmarking::Pallet ::; type RococoToRococoBulletin = pallet_bridge_messages::benchmarking::Pallet ::; + type Legacy = BridgeRelayersBench::; + type PermissionlessLanes = BridgeRelayersBench::; use bridge_runtime_common::messages_benchmarking::{ prepare_message_delivery_proof_from_grandpa_chain, @@ -1430,21 +1335,31 @@ impl_runtime_apis! { } fn prepare_message_proof( - params: MessageProofParams, - ) -> (bridge_to_westend_config::FromWestendBridgeHubMessagesProof, Weight) { + params: MessageProofParams>, + ) -> (bridge_to_westend_config::FromWestendBridgeHubMessagesProof, Weight) { use cumulus_primitives_core::XcmpMessageSource; assert!(XcmpQueue::take_outbound_messages(usize::MAX).is_empty()); ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests(42.into()); + let universal_source = bridge_to_westend_config::open_bridge_for_benchmarks::< + Runtime, + bridge_to_westend_config::XcmOverBridgeHubWestendInstance, + xcm_config::LocationToAccountId, + >(params.lane, 42); prepare_message_proof_from_parachain::< Runtime, bridge_common_config::BridgeGrandpaWestendInstance, bridge_to_westend_config::WithBridgeHubWestendMessagesInstance, - >(params, generate_xcm_builder_bridge_message_sample([GlobalConsensus(Rococo), Parachain(42)].into())) + >(params, generate_xcm_builder_bridge_message_sample(universal_source)) } fn prepare_message_delivery_proof( - params: MessageDeliveryProofParams, - ) -> bridge_to_westend_config::ToWestendBridgeHubMessagesDeliveryProof { + params: MessageDeliveryProofParams>, + ) -> bridge_to_westend_config::ToWestendBridgeHubMessagesDeliveryProof { + let _ = bridge_to_westend_config::open_bridge_for_benchmarks::< + Runtime, + bridge_to_westend_config::XcmOverBridgeHubWestendInstance, + xcm_config::LocationToAccountId, + >(params.lane, 42); prepare_message_delivery_proof_from_parachain::< Runtime, bridge_common_config::BridgeGrandpaWestendInstance, @@ -1465,21 +1380,31 @@ impl_runtime_apis! { } fn prepare_message_proof( - params: MessageProofParams, - ) -> (bridge_to_bulletin_config::FromRococoBulletinMessagesProof, Weight) { + params: MessageProofParams>, + ) -> (bridge_to_bulletin_config::FromRococoBulletinMessagesProof, Weight) { use cumulus_primitives_core::XcmpMessageSource; assert!(XcmpQueue::take_outbound_messages(usize::MAX).is_empty()); ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests(42.into()); + let universal_source = bridge_to_bulletin_config::open_bridge_for_benchmarks::< + Runtime, + bridge_to_bulletin_config::XcmOverPolkadotBulletinInstance, + xcm_config::LocationToAccountId, + >(params.lane, 42); prepare_message_proof_from_grandpa_chain::< Runtime, bridge_common_config::BridgeGrandpaRococoBulletinInstance, bridge_to_bulletin_config::WithRococoBulletinMessagesInstance, - >(params, generate_xcm_builder_bridge_message_sample([GlobalConsensus(Rococo), Parachain(42)].into())) + >(params, generate_xcm_builder_bridge_message_sample(universal_source)) } fn prepare_message_delivery_proof( - params: MessageDeliveryProofParams, - ) -> bridge_to_bulletin_config::ToRococoBulletinMessagesDeliveryProof { + params: MessageDeliveryProofParams>, + ) -> bridge_to_bulletin_config::ToRococoBulletinMessagesDeliveryProof { + let _ = bridge_to_bulletin_config::open_bridge_for_benchmarks::< + Runtime, + bridge_to_bulletin_config::XcmOverPolkadotBulletinInstance, + xcm_config::LocationToAccountId, + >(params.lane, 42); prepare_message_delivery_proof_from_grandpa_chain::< Runtime, bridge_common_config::BridgeGrandpaRococoBulletinInstance, @@ -1511,8 +1436,8 @@ impl_runtime_apis! { parachain_head_size: u32, proof_params: bp_runtime::UnverifiedStorageProofParams, ) -> ( - pallet_bridge_parachains::RelayBlockNumber, - pallet_bridge_parachains::RelayBlockHash, + bp_parachains::RelayBlockNumber, + bp_parachains::RelayBlockHash, bp_polkadot_core::parachains::ParaHeadsProof, Vec<(bp_polkadot_core::parachains::ParaId, bp_polkadot_core::parachains::ParaHash)>, ) { @@ -1524,16 +1449,36 @@ impl_runtime_apis! { } } - impl BridgeRelayersConfig for Runtime { + impl BridgeRelayersConfig for Runtime { + fn prepare_rewards_account( + account_params: bp_relayers::RewardsAccountParams<>::LaneId>, + reward: Balance, + ) { + let rewards_account = bp_relayers::PayRewardFromAccount::< + Balances, + AccountId, + >::LaneId, + >::rewards_account(account_params); + >::deposit_account(rewards_account, reward); + } + + fn deposit_account(account: AccountId, balance: Balance) { + use frame_support::traits::fungible::Mutate; + Balances::mint_into(&account, balance.saturating_add(ExistentialDeposit::get())).unwrap(); + } + } + + impl BridgeRelayersConfig for Runtime { fn prepare_rewards_account( - account_params: bp_relayers::RewardsAccountParams, + account_params: bp_relayers::RewardsAccountParams<>::LaneId>, reward: Balance, ) { let rewards_account = bp_relayers::PayRewardFromAccount::< Balances, - AccountId + AccountId, + >::LaneId, >::rewards_account(account_params); - Self::deposit_account(rewards_account, reward); + >::deposit_account(rewards_account, reward); } fn deposit_account(account: AccountId, balance: Balance) { @@ -1569,20 +1514,15 @@ impl_runtime_apis! { } fn get_preset(id: &Option) -> Option> { - get_preset::(id, |_| None) + get_preset::(id, &genesis_config_presets::get_preset) } fn preset_names() -> Vec { - vec![] + genesis_config_presets::preset_names() } } } -cumulus_pallet_parachain_system::register_validate_block! { - Runtime = Runtime, - BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::, -} - #[cfg(test)] mod tests { use super::*; @@ -1610,7 +1550,6 @@ mod tests { BridgeRejectObsoleteHeadersAndMessages, ( bridge_to_westend_config::OnBridgeHubRococoRefundBridgeHubWestendMessages::default(), - bridge_to_bulletin_config::OnBridgeHubRococoRefundRococoBulletinMessages::default(), ), cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(), frame_metadata_hash_extension::CheckMetadataHash::new(false), diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/block_weights.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/block_weights.rs index e7fdb2aae2a01ec06076de83d94817e540e205dd..41e30725e753fef32404b858a873e2456618d56f 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/block_weights.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/block_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/extrinsic_weights.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/extrinsic_weights.rs index 1a4adb968bb7195428ea00d59cd92dcd3b6eea5f..3bd48f061bba079a76aa8f032ebecd6d40c2156c 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/extrinsic_weights.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/extrinsic_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 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 942f243141da9c1dcfa47d2e3a1ac7906eb22706..517b3eb69fc8020f0b22b182227eb3a8528d4152 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 @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa.rs index 4ce57b2e50161a812ddf17a9bbd20d5fc58682a7..a9cc2411a9c66be498ff3148500baf82ef6623f9 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_grandpa.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_bridge_grandpa` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-07-11, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-yaoqqom-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-rococo-dev")`, DB CACHE: 1024 // Executed Command: @@ -68,13 +68,13 @@ impl pallet_bridge_grandpa::WeightInfo for WeightInfo pallet_bridge_grandpa::WeightInfo for WeightInfo pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (r:1 w:0) /// Proof: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (`max_values`: Some(1024), `max_size`: Some(68), added: 1553, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Storage: `XcmOverPolkadotBulletin::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverPolkadotBulletin::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverPolkadotBulletin::Bridges` (r:1 w:0) + /// Proof: `XcmOverPolkadotBulletin::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn receive_single_message_proof() -> Weight { // Proof Size summary in bytes: - // Measured: `654` - // Estimated: `52645` - // Minimum execution time: 36_836_000 picoseconds. - Weight::from_parts(37_858_000, 0) - .saturating_add(Weight::from_parts(0, 52645)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `933` + // Estimated: `52674` + // Minimum execution time: 61_893_000 picoseconds. + Weight::from_parts(63_358_000, 0) + .saturating_add(Weight::from_parts(0, 52674)) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `BridgePolkadotBulletinMessages::PalletOperatingMode` (r:1 w:0) @@ -71,21 +75,25 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (r:1 w:0) /// Proof: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (`max_values`: Some(1024), `max_size`: Some(68), added: 1553, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Storage: `XcmOverPolkadotBulletin::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverPolkadotBulletin::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverPolkadotBulletin::Bridges` (r:1 w:0) + /// Proof: `XcmOverPolkadotBulletin::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 4076]`. /// The range of component `n` is `[1, 4076]`. fn receive_n_messages_proof(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `654` - // Estimated: `52645` - // Minimum execution time: 36_587_000 picoseconds. - Weight::from_parts(37_516_000, 0) - .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 8_655 - .saturating_add(Weight::from_parts(11_649_169, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `933` + // Estimated: `52674` + // Minimum execution time: 61_612_000 picoseconds. + Weight::from_parts(62_758_000, 0) + .saturating_add(Weight::from_parts(0, 52674)) + // Standard Error: 13_521 + .saturating_add(Weight::from_parts(14_530_846, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `BridgePolkadotBulletinMessages::PalletOperatingMode` (r:1 w:0) @@ -93,17 +101,21 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (r:1 w:0) /// Proof: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (`max_values`: Some(1024), `max_size`: Some(68), added: 1553, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Storage: `XcmOverPolkadotBulletin::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverPolkadotBulletin::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverPolkadotBulletin::Bridges` (r:1 w:0) + /// Proof: `XcmOverPolkadotBulletin::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn receive_single_message_proof_with_outbound_lane_state() -> Weight { // Proof Size summary in bytes: - // Measured: `654` - // Estimated: `52645` - // Minimum execution time: 42_157_000 picoseconds. - Weight::from_parts(43_105_000, 0) - .saturating_add(Weight::from_parts(0, 52645)) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `933` + // Estimated: `52674` + // Minimum execution time: 66_862_000 picoseconds. + Weight::from_parts(69_531_000, 0) + .saturating_add(Weight::from_parts(0, 52674)) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `BridgePolkadotBulletinMessages::PalletOperatingMode` (r:1 w:0) @@ -111,21 +123,25 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (r:1 w:0) /// Proof: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (`max_values`: Some(1024), `max_size`: Some(68), added: 1553, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Storage: `XcmOverPolkadotBulletin::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverPolkadotBulletin::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverPolkadotBulletin::Bridges` (r:1 w:0) + /// Proof: `XcmOverPolkadotBulletin::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 16384]`. /// The range of component `n` is `[1, 16384]`. fn receive_single_n_bytes_message_proof(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `654` - // Estimated: `52645` - // Minimum execution time: 35_536_000 picoseconds. - Weight::from_parts(37_452_828, 0) - .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 3 - .saturating_add(Weight::from_parts(2_269, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(4)) + // Measured: `933` + // Estimated: `52674` + // Minimum execution time: 58_971_000 picoseconds. + Weight::from_parts(62_999_984, 0) + .saturating_add(Weight::from_parts(0, 52674)) + // Standard Error: 7 + .saturating_add(Weight::from_parts(2_050, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `BridgePolkadotBulletinMessages::PalletOperatingMode` (r:1 w:0) @@ -133,17 +149,21 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (r:1 w:0) /// Proof: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (`max_values`: Some(1024), `max_size`: Some(68), added: 1553, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::OutboundLanes` (r:1 w:1) - /// Proof: `BridgePolkadotBulletinMessages::OutboundLanes` (`max_values`: Some(1), `max_size`: Some(44), added: 539, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) + /// Storage: `XcmOverPolkadotBulletin::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverPolkadotBulletin::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverPolkadotBulletin::Bridges` (r:1 w:0) + /// Proof: `XcmOverPolkadotBulletin::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::OutboundMessages` (r:0 w:1) - /// Proof: `BridgePolkadotBulletinMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_single_message() -> Weight { // Proof Size summary in bytes: - // Measured: `621` - // Estimated: `2543` - // Minimum execution time: 25_800_000 picoseconds. - Weight::from_parts(26_666_000, 0) - .saturating_add(Weight::from_parts(0, 2543)) - .saturating_add(T::DbWeight::get().reads(3)) + // Measured: `900` + // Estimated: `5383` + // Minimum execution time: 43_066_000 picoseconds. + Weight::from_parts(43_878_000, 0) + .saturating_add(Weight::from_parts(0, 5383)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } /// Storage: `BridgePolkadotBulletinMessages::PalletOperatingMode` (r:1 w:0) @@ -151,17 +171,21 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (r:1 w:0) /// Proof: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (`max_values`: Some(1024), `max_size`: Some(68), added: 1553, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::OutboundLanes` (r:1 w:1) - /// Proof: `BridgePolkadotBulletinMessages::OutboundLanes` (`max_values`: Some(1), `max_size`: Some(44), added: 539, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) + /// Storage: `XcmOverPolkadotBulletin::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverPolkadotBulletin::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverPolkadotBulletin::Bridges` (r:1 w:0) + /// Proof: `XcmOverPolkadotBulletin::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::OutboundMessages` (r:0 w:2) - /// Proof: `BridgePolkadotBulletinMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { // Proof Size summary in bytes: - // Measured: `621` - // Estimated: `2543` - // Minimum execution time: 27_262_000 picoseconds. - Weight::from_parts(27_997_000, 0) - .saturating_add(Weight::from_parts(0, 2543)) - .saturating_add(T::DbWeight::get().reads(3)) + // Measured: `900` + // Estimated: `5383` + // Minimum execution time: 44_120_000 picoseconds. + Weight::from_parts(45_914_000, 0) + .saturating_add(Weight::from_parts(0, 5383)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `BridgePolkadotBulletinMessages::PalletOperatingMode` (r:1 w:0) @@ -169,17 +193,21 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (r:1 w:0) /// Proof: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (`max_values`: Some(1024), `max_size`: Some(68), added: 1553, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::OutboundLanes` (r:1 w:1) - /// Proof: `BridgePolkadotBulletinMessages::OutboundLanes` (`max_values`: Some(1), `max_size`: Some(44), added: 539, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) + /// Storage: `XcmOverPolkadotBulletin::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverPolkadotBulletin::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverPolkadotBulletin::Bridges` (r:1 w:0) + /// Proof: `XcmOverPolkadotBulletin::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::OutboundMessages` (r:0 w:2) - /// Proof: `BridgePolkadotBulletinMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { // Proof Size summary in bytes: - // Measured: `621` - // Estimated: `2543` - // Minimum execution time: 26_992_000 picoseconds. - Weight::from_parts(27_921_000, 0) - .saturating_add(Weight::from_parts(0, 2543)) - .saturating_add(T::DbWeight::get().reads(3)) + // Measured: `900` + // Estimated: `5383` + // Minimum execution time: 44_930_000 picoseconds. + Weight::from_parts(46_111_000, 0) + .saturating_add(Weight::from_parts(0, 5383)) + .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `BridgePolkadotBulletinMessages::PalletOperatingMode` (r:1 w:0) @@ -187,7 +215,11 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (r:1 w:0) /// Proof: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (`max_values`: Some(1024), `max_size`: Some(68), added: 1553, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Storage: `XcmOverPolkadotBulletin::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverPolkadotBulletin::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverPolkadotBulletin::Bridges` (r:1 w:0) + /// Proof: `XcmOverPolkadotBulletin::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `XcmpQueue::DeliveryFeeFactor` (r:1 w:0) @@ -208,14 +240,14 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// The range of component `n` is `[1, 16384]`. fn receive_single_n_bytes_message_proof_with_dispatch(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `813` - // Estimated: `52645` - // Minimum execution time: 55_509_000 picoseconds. - Weight::from_parts(59_826_763, 0) - .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 7 - .saturating_add(Weight::from_parts(7_565, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(10)) + // Measured: `1092` + // Estimated: `52674` + // Minimum execution time: 81_911_000 picoseconds. + Weight::from_parts(88_170_136, 0) + .saturating_add(Weight::from_parts(0, 52674)) + // Standard Error: 9 + .saturating_add(Weight::from_parts(7_233, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().writes(4)) } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_rococo_to_westend.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_rococo_to_westend.rs index dc6c917c6d00462453fd2368cc4cc4dbbed8aa67..b27bbf4ff6c6323634a05c61690f2ab04edb5f0d 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_rococo_to_westend.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_rococo_to_westend.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_bridge_messages` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-07-04, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-rococo-dev")`, DB CACHE: 1024 // Executed Command: @@ -50,90 +50,98 @@ pub struct WeightInfo(PhantomData); impl pallet_bridge_messages::WeightInfo for WeightInfo { /// Storage: `BridgeWestendMessages::PalletOperatingMode` (r:1 w:0) /// Proof: `BridgeWestendMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) - /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeWestendParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubWestend::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverBridgeHubWestend::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubWestend::Bridges` (r:1 w:0) + /// Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn receive_single_message_proof() -> Weight { // Proof Size summary in bytes: - // Measured: `658` - // Estimated: `52645` - // Minimum execution time: 40_198_000 picoseconds. - Weight::from_parts(42_079_000, 0) - .saturating_add(Weight::from_parts(0, 52645)) - .saturating_add(T::DbWeight::get().reads(5)) + // Measured: `810` + // Estimated: `52674` + // Minimum execution time: 62_750_000 picoseconds. + Weight::from_parts(65_328_000, 0) + .saturating_add(Weight::from_parts(0, 52674)) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `BridgeWestendMessages::PalletOperatingMode` (r:1 w:0) /// Proof: `BridgeWestendMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) - /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeWestendParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubWestend::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverBridgeHubWestend::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubWestend::Bridges` (r:1 w:0) + /// Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 4076]`. /// The range of component `n` is `[1, 4076]`. fn receive_n_messages_proof(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `658` - // Estimated: `52645` - // Minimum execution time: 39_990_000 picoseconds. - Weight::from_parts(41_381_000, 0) - .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 8_459 - .saturating_add(Weight::from_parts(11_710_167, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(5)) + // Measured: `810` + // Estimated: `52674` + // Minimum execution time: 62_275_000 picoseconds. + Weight::from_parts(63_714_000, 0) + .saturating_add(Weight::from_parts(0, 52674)) + // Standard Error: 13_139 + .saturating_add(Weight::from_parts(14_630_892, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `BridgeWestendMessages::PalletOperatingMode` (r:1 w:0) /// Proof: `BridgeWestendMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) - /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeWestendParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubWestend::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverBridgeHubWestend::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubWestend::Bridges` (r:1 w:0) + /// Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn receive_single_message_proof_with_outbound_lane_state() -> Weight { // Proof Size summary in bytes: - // Measured: `658` - // Estimated: `52645` - // Minimum execution time: 45_940_000 picoseconds. - Weight::from_parts(47_753_000, 0) - .saturating_add(Weight::from_parts(0, 52645)) - .saturating_add(T::DbWeight::get().reads(5)) + // Measured: `810` + // Estimated: `52674` + // Minimum execution time: 68_950_000 picoseconds. + Weight::from_parts(71_420_000, 0) + .saturating_add(Weight::from_parts(0, 52674)) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `BridgeWestendMessages::PalletOperatingMode` (r:1 w:0) /// Proof: `BridgeWestendMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) - /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeWestendParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubWestend::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverBridgeHubWestend::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubWestend::Bridges` (r:1 w:0) + /// Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 16384]`. /// The range of component `n` is `[1, 16384]`. fn receive_single_n_bytes_message_proof(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `658` - // Estimated: `52645` - // Minimum execution time: 39_067_000 picoseconds. - Weight::from_parts(41_787_019, 0) - .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 5 - .saturating_add(Weight::from_parts(2_295, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(5)) + // Measured: `810` + // Estimated: `52674` + // Minimum execution time: 60_477_000 picoseconds. + Weight::from_parts(64_935_758, 0) + .saturating_add(Weight::from_parts(0, 52674)) + // Standard Error: 8 + .saturating_add(Weight::from_parts(2_008, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `BridgeWestendMessages::PalletOperatingMode` (r:1 w:0) @@ -141,21 +149,25 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgeWestendParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeWestendParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::OutboundLanes` (r:1 w:1) - /// Proof: `BridgeWestendMessages::OutboundLanes` (`max_values`: Some(1), `max_size`: Some(44), added: 539, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) /// Storage: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Proof: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Storage: `BridgeRelayers::RelayerRewards` (r:1 w:1) - /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubWestend::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverBridgeHubWestend::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubWestend::Bridges` (r:1 w:0) + /// Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::OutboundMessages` (r:0 w:1) - /// Proof: `BridgeWestendMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_single_message() -> Weight { // Proof Size summary in bytes: - // Measured: `501` - // Estimated: `3966` - // Minimum execution time: 33_107_000 picoseconds. - Weight::from_parts(34_364_000, 0) - .saturating_add(Weight::from_parts(0, 3966)) - .saturating_add(T::DbWeight::get().reads(5)) + // Measured: `779` + // Estimated: `5383` + // Minimum execution time: 52_939_000 picoseconds. + Weight::from_parts(54_637_000, 0) + .saturating_add(Weight::from_parts(0, 5383)) + .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `BridgeWestendMessages::PalletOperatingMode` (r:1 w:0) @@ -163,21 +175,25 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgeWestendParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeWestendParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::OutboundLanes` (r:1 w:1) - /// Proof: `BridgeWestendMessages::OutboundLanes` (`max_values`: Some(1), `max_size`: Some(44), added: 539, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) /// Storage: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Proof: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Storage: `BridgeRelayers::RelayerRewards` (r:1 w:1) - /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubWestend::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverBridgeHubWestend::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubWestend::Bridges` (r:1 w:0) + /// Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::OutboundMessages` (r:0 w:2) - /// Proof: `BridgeWestendMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { // Proof Size summary in bytes: - // Measured: `501` - // Estimated: `3966` - // Minimum execution time: 34_826_000 picoseconds. - Weight::from_parts(35_563_000, 0) - .saturating_add(Weight::from_parts(0, 3966)) - .saturating_add(T::DbWeight::get().reads(5)) + // Measured: `779` + // Estimated: `5383` + // Minimum execution time: 54_645_000 picoseconds. + Weight::from_parts(57_391_000, 0) + .saturating_add(Weight::from_parts(0, 5383)) + .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `BridgeWestendMessages::PalletOperatingMode` (r:1 w:0) @@ -185,31 +201,37 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgeWestendParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeWestendParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::OutboundLanes` (r:1 w:1) - /// Proof: `BridgeWestendMessages::OutboundLanes` (`max_values`: Some(1), `max_size`: Some(44), added: 539, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) /// Storage: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Proof: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Storage: `BridgeRelayers::RelayerRewards` (r:2 w:2) - /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubWestend::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverBridgeHubWestend::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubWestend::Bridges` (r:1 w:0) + /// Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::OutboundMessages` (r:0 w:2) - /// Proof: `BridgeWestendMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { // Proof Size summary in bytes: - // Measured: `501` - // Estimated: `6086` - // Minimum execution time: 38_725_000 picoseconds. - Weight::from_parts(39_727_000, 0) - .saturating_add(Weight::from_parts(0, 6086)) - .saturating_add(T::DbWeight::get().reads(6)) + // Measured: `779` + // Estimated: `6144` + // Minimum execution time: 59_581_000 picoseconds. + Weight::from_parts(61_657_000, 0) + .saturating_add(Weight::from_parts(0, 6144)) + .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(5)) } /// Storage: `BridgeWestendMessages::PalletOperatingMode` (r:1 w:0) /// Proof: `BridgeWestendMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:1) - /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeWestendParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubWestend::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverBridgeHubWestend::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubWestend::Bridges` (r:1 w:0) + /// Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `XcmpQueue::DeliveryFeeFactor` (r:1 w:0) @@ -222,20 +244,22 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParachainSystem::RelevantMessagingState` (r:1 w:0) /// Proof: `ParachainSystem::RelevantMessagingState` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:1) + /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `XcmpQueue::OutboundXcmpMessages` (r:0 w:1) /// Proof: `XcmpQueue::OutboundXcmpMessages` (`max_values`: None, `max_size`: Some(105506), added: 107981, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 16384]`. /// The range of component `n` is `[1, 16384]`. fn receive_single_n_bytes_message_proof_with_dispatch(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `789` - // Estimated: `52645` - // Minimum execution time: 56_892_000 picoseconds. - Weight::from_parts(61_941_659, 0) - .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 8 - .saturating_add(Weight::from_parts(7_580, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(10)) + // Measured: `1140` + // Estimated: `52674` + // Minimum execution time: 83_530_000 picoseconds. + Weight::from_parts(91_297_344, 0) + .saturating_add(Weight::from_parts(0, 52674)) + // Standard Error: 11 + .saturating_add(Weight::from_parts(7_197, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().writes(4)) } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains.rs index 8eb291ea14523b15d6c44b8ee72d637fcbe11166..3629d8797bf7fa3fc379864ba1fc4afadd59fc73 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_parachains.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_bridge_parachains` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-07-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-rococo-dev")`, DB CACHE: 1024 // Executed Command: @@ -65,11 +65,11 @@ impl pallet_bridge_parachains::WeightInfo for WeightInf // Proof Size summary in bytes: // Measured: `558` // Estimated: `2543` - // Minimum execution time: 34_889_000 picoseconds. - Weight::from_parts(36_100_759, 0) + // Minimum execution time: 41_810_000 picoseconds. + Weight::from_parts(42_952_442, 0) .saturating_add(Weight::from_parts(0, 2543)) - // Standard Error: 102_466 - .saturating_add(Weight::from_parts(178_820, 0).saturating_mul(p.into())) + // Standard Error: 108_155 + .saturating_add(Weight::from_parts(340_328, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -89,8 +89,8 @@ impl pallet_bridge_parachains::WeightInfo for WeightInf // Proof Size summary in bytes: // Measured: `558` // Estimated: `2543` - // Minimum execution time: 36_501_000 picoseconds. - Weight::from_parts(37_266_000, 0) + // Minimum execution time: 43_567_000 picoseconds. + Weight::from_parts(44_746_000, 0) .saturating_add(Weight::from_parts(0, 2543)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) @@ -111,8 +111,8 @@ impl pallet_bridge_parachains::WeightInfo for WeightInf // Proof Size summary in bytes: // Measured: `558` // Estimated: `2543` - // Minimum execution time: 66_059_000 picoseconds. - Weight::from_parts(67_139_000, 0) + // Minimum execution time: 70_654_000 picoseconds. + Weight::from_parts(72_314_000, 0) .saturating_add(Weight::from_parts(0, 2543)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs index f8bb983e80aa776ae7fe8c9a756935096956e7dd..b7318361c7d9e38992cfbaec711ada6cd1f16cff 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_relayers.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_bridge_relayers` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-07-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-rococo-dev")`, DB CACHE: 1024 // Executed Command: @@ -49,15 +49,15 @@ use core::marker::PhantomData; pub struct WeightInfo(PhantomData); impl pallet_bridge_relayers::WeightInfo for WeightInfo { /// Storage: `BridgeRelayers::RelayerRewards` (r:1 w:1) - /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn claim_rewards() -> Weight { // Proof Size summary in bytes: - // Measured: `278` + // Measured: `306` // Estimated: `3593` - // Minimum execution time: 44_224_000 picoseconds. - Weight::from_parts(44_905_000, 0) + // Minimum execution time: 53_924_000 picoseconds. + Weight::from_parts(54_736_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -72,8 +72,8 @@ impl pallet_bridge_relayers::WeightInfo for WeightInfo< // Proof Size summary in bytes: // Measured: `131` // Estimated: `4714` - // Minimum execution time: 23_902_000 picoseconds. - Weight::from_parts(24_702_000, 0) + // Minimum execution time: 28_608_000 picoseconds. + Weight::from_parts(29_081_000, 0) .saturating_add(Weight::from_parts(0, 4714)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) @@ -86,8 +86,8 @@ impl pallet_bridge_relayers::WeightInfo for WeightInfo< // Proof Size summary in bytes: // Measured: `231` // Estimated: `4714` - // Minimum execution time: 24_469_000 picoseconds. - Weight::from_parts(25_176_000, 0) + // Minimum execution time: 29_738_000 picoseconds. + Weight::from_parts(30_242_000, 0) .saturating_add(Weight::from_parts(0, 4714)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -102,21 +102,21 @@ impl pallet_bridge_relayers::WeightInfo for WeightInfo< // Proof Size summary in bytes: // Measured: `334` // Estimated: `4714` - // Minimum execution time: 27_518_000 picoseconds. - Weight::from_parts(28_068_000, 0) + // Minimum execution time: 33_174_000 picoseconds. + Weight::from_parts(33_992_000, 0) .saturating_add(Weight::from_parts(0, 4714)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `BridgeRelayers::RelayerRewards` (r:1 w:1) - /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) fn register_relayer_reward() -> Weight { // Proof Size summary in bytes: // Measured: `76` - // Estimated: `3538` - // Minimum execution time: 5_484_000 picoseconds. - Weight::from_parts(5_718_000, 0) - .saturating_add(Weight::from_parts(0, 3538)) + // Estimated: `3567` + // Minimum execution time: 7_950_000 picoseconds. + Weight::from_parts(8_123_000, 0) + .saturating_add(Weight::from_parts(0, 3567)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/paritydb_weights.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/paritydb_weights.rs index 25679703831a13b8d1bb7fb7dd4d92fa84b1f255..e0b1985c659c78b66176423f6bcc64d546b39324 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/paritydb_weights.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/paritydb_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/rocksdb_weights.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/rocksdb_weights.rs index 3dd817aa6f137085b0e5fdf2b11b7f50e5c8b002..c6e91b2fcffbf65e3fdce2bc8fbac19a605d00aa 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/rocksdb_weights.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/rocksdb_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/snowbridge_pallet_system.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/snowbridge_pallet_system.rs index c6c188e323af84d11ba396cb9ab4e97983bac33c..3831111f0977dd33b784a5ba9f4bf2686528f292 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/snowbridge_pallet_system.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/snowbridge_pallet_system.rs @@ -253,4 +253,14 @@ impl snowbridge_pallet_system::WeightInfo for WeightInf .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } + + fn register_token() -> Weight { + // Proof Size summary in bytes: + // Measured: `256` + // Estimated: `6044` + // Minimum execution time: 45_000_000 picoseconds. + Weight::from_parts(45_000_000, 6044) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 9c58072d402c91a13f63795bf5b4184f80ab2c0b..9a9137c18093b49a1c10ed59ba5e13fd9a2be2e8 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-07-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 // Executed Command: @@ -68,8 +68,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `171` // Estimated: `6196` - // Minimum execution time: 60_119_000 picoseconds. - Weight::from_parts(61_871_000, 6196) + // Minimum execution time: 70_133_000 picoseconds. + Weight::from_parts(71_765_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -77,8 +77,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 998_000 picoseconds. - Weight::from_parts(1_038_000, 0) + // Minimum execution time: 959_000 picoseconds. + Weight::from_parts(996_000, 0) } // Storage: `PolkadotXcm::Queries` (r:1 w:0) // Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -86,58 +86,58 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `32` // Estimated: `3497` - // Minimum execution time: 6_327_000 picoseconds. - Weight::from_parts(6_520_000, 3497) + // Minimum execution time: 7_537_000 picoseconds. + Weight::from_parts(7_876_000, 3497) .saturating_add(T::DbWeight::get().reads(1)) } pub fn transact() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_783_000 picoseconds. - Weight::from_parts(7_117_000, 0) + // Minimum execution time: 7_774_000 picoseconds. + Weight::from_parts(7_895_000, 0) } pub fn refund_surplus() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_589_000 picoseconds. - Weight::from_parts(1_655_000, 0) + // Minimum execution time: 1_577_000 picoseconds. + Weight::from_parts(1_622_000, 0) } pub fn set_error_handler() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_013_000 picoseconds. - Weight::from_parts(1_045_000, 0) + // Minimum execution time: 973_000 picoseconds. + Weight::from_parts(1_008_000, 0) } pub fn set_appendix() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_005_000 picoseconds. - Weight::from_parts(1_044_000, 0) + // Minimum execution time: 1_027_000 picoseconds. + Weight::from_parts(1_052_000, 0) } pub fn clear_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 964_000 picoseconds. - Weight::from_parts(1_011_000, 0) + // Minimum execution time: 953_000 picoseconds. + Weight::from_parts(992_000, 0) } pub fn descend_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_005_000 picoseconds. - Weight::from_parts(1_027_000, 0) + // Minimum execution time: 949_000 picoseconds. + Weight::from_parts(1_020_000, 0) } pub fn clear_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 980_000 picoseconds. - Weight::from_parts(1_009_000, 0) + // Minimum execution time: 979_000 picoseconds. + Weight::from_parts(1_032_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -159,8 +159,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `171` // Estimated: `6196` - // Minimum execution time: 56_726_000 picoseconds. - Weight::from_parts(59_300_000, 6196) + // Minimum execution time: 66_663_000 picoseconds. + Weight::from_parts(67_728_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -170,8 +170,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `90` // Estimated: `3555` - // Minimum execution time: 8_962_000 picoseconds. - Weight::from_parts(9_519_000, 3555) + // Minimum execution time: 11_074_000 picoseconds. + Weight::from_parts(11_439_000, 3555) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -179,8 +179,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 999_000 picoseconds. - Weight::from_parts(1_035_000, 0) + // Minimum execution time: 943_000 picoseconds. + Weight::from_parts(1_021_000, 0) } // Storage: `PolkadotXcm::VersionNotifyTargets` (r:1 w:1) // Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -200,8 +200,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 20_313_000 picoseconds. - Weight::from_parts(21_000_000, 3503) + // Minimum execution time: 25_123_000 picoseconds. + Weight::from_parts(25_687_000, 3503) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -211,44 +211,44 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_820_000 picoseconds. - Weight::from_parts(2_949_000, 0) + // Minimum execution time: 2_868_000 picoseconds. + Weight::from_parts(3_124_000, 0) .saturating_add(T::DbWeight::get().writes(1)) } pub fn burn_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_293_000 picoseconds. - Weight::from_parts(1_354_000, 0) + // Minimum execution time: 1_378_000 picoseconds. + Weight::from_parts(1_458_000, 0) } pub fn expect_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_076_000 picoseconds. - Weight::from_parts(1_114_000, 0) + // Minimum execution time: 1_036_000 picoseconds. + Weight::from_parts(1_105_000, 0) } pub fn expect_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_014_000 picoseconds. - Weight::from_parts(1_055_000, 0) + // Minimum execution time: 945_000 picoseconds. + Weight::from_parts(1_021_000, 0) } pub fn expect_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 979_000 picoseconds. - Weight::from_parts(1_019_000, 0) + // Minimum execution time: 931_000 picoseconds. + Weight::from_parts(1_006_000, 0) } pub fn expect_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_161_000 picoseconds. - Weight::from_parts(1_208_000, 0) + // Minimum execution time: 1_139_000 picoseconds. + Weight::from_parts(1_206_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -270,8 +270,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `171` // Estimated: `6196` - // Minimum execution time: 62_250_000 picoseconds. - Weight::from_parts(64_477_000, 6196) + // Minimum execution time: 72_884_000 picoseconds. + Weight::from_parts(74_331_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -279,8 +279,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_286_000 picoseconds. - Weight::from_parts(4_476_000, 0) + // Minimum execution time: 4_432_000 picoseconds. + Weight::from_parts(4_542_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -302,8 +302,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `171` // Estimated: `6196` - // Minimum execution time: 58_253_000 picoseconds. - Weight::from_parts(59_360_000, 6196) + // Minimum execution time: 67_102_000 picoseconds. + Weight::from_parts(68_630_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -311,44 +311,44 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_026_000 picoseconds. - Weight::from_parts(1_065_000, 0) + // Minimum execution time: 995_000 picoseconds. + Weight::from_parts(1_057_000, 0) } pub fn set_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 993_000 picoseconds. - Weight::from_parts(1_015_000, 0) + // Minimum execution time: 956_000 picoseconds. + Weight::from_parts(1_021_000, 0) } pub fn clear_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 966_000 picoseconds. - Weight::from_parts(999_000, 0) + // Minimum execution time: 944_000 picoseconds. + Weight::from_parts(986_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) // Storage: `PolkadotXcm::SupportedVersion` (r:2 w:0) // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `XcmOverBridgeHubWestend::Bridges` (r:1 w:0) + // Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) // Storage: `BridgeWestendMessages::PalletOperatingMode` (r:1 w:0) // Proof: `BridgeWestendMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) // Storage: `BridgeWestendMessages::OutboundLanes` (r:1 w:1) - // Proof: `BridgeWestendMessages::OutboundLanes` (`max_values`: Some(1), `max_size`: Some(44), added: 539, mode: `MaxEncodedLen`) - // Storage: `BridgeWestendMessages::OutboundLanesCongestedSignals` (r:1 w:0) - // Proof: `BridgeWestendMessages::OutboundLanesCongestedSignals` (`max_values`: Some(1), `max_size`: Some(21), added: 516, mode: `MaxEncodedLen`) + // Proof: `BridgeWestendMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) // Storage: `BridgeWestendMessages::OutboundMessages` (r:0 w:1) - // Proof: `BridgeWestendMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) + // Proof: `BridgeWestendMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) /// The range of component `x` is `[1, 1000]`. pub fn export_message(x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `190` - // Estimated: `6130` - // Minimum execution time: 37_014_000 picoseconds. - Weight::from_parts(38_096_655, 6130) - // Standard Error: 61 - .saturating_add(Weight::from_parts(45_146, 0).saturating_mul(x.into())) + // Measured: `589` + // Estimated: `6529` + // Minimum execution time: 58_111_000 picoseconds. + Weight::from_parts(59_123_071, 6529) + // Standard Error: 167 + .saturating_add(Weight::from_parts(43_658, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -356,14 +356,14 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 996_000 picoseconds. - Weight::from_parts(1_025_000, 0) + // Minimum execution time: 950_000 picoseconds. + Weight::from_parts(1_002_000, 0) } pub fn unpaid_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_001_000 picoseconds. - Weight::from_parts(1_044_000, 0) + // Minimum execution time: 963_000 picoseconds. + Weight::from_parts(1_012_000, 0) } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index 92368b29212119dc151cd02a2311e01a5b82ac16..2fb186703a88663fb6c3284297c91d7c7038298d 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -17,10 +17,9 @@ use super::{ AccountId, AllPalletsWithSystem, Balances, BaseDeliveryFee, FeeAssetId, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, - TransactionByteFee, WeightToFee, XcmpQueue, + TransactionByteFee, WeightToFee, XcmOverBridgeHubWestend, XcmOverRococoBulletin, XcmpQueue, }; -use bp_messages::LaneId; -use bp_relayers::{PayRewardFromAccount, RewardsAccountOwner, RewardsAccountParams}; + use core::marker::PhantomData; use frame_support::{ parameter_types, @@ -39,22 +38,21 @@ use parachains_common::{ use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::xcm_sender::ExponentialPrice; use snowbridge_runtime_common::XcmExportFeeToSibling; -use sp_core::Get; use sp_runtime::traits::AccountIdConversion; use testnet_parachains_constants::rococo::snowbridge::EthereumNetwork; use xcm::latest::prelude::*; use xcm_builder::{ - deposit_or_burn_fee, AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, - AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, - AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, DenyThenTry, EnsureXcmOrigin, - FrameTransactionalProcessor, FungibleAdapter, HandleFee, IsConcrete, ParentAsSuperuser, - ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, - WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, + AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, + AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, + DenyReserveTransferToRelayChain, DenyThenTry, DescribeAllTerminal, DescribeFamily, + EnsureXcmOrigin, FrameTransactionalProcessor, FungibleAdapter, HandleFee, HashedDescription, + IsConcrete, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, + UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, }; use xcm_executor::{ - traits::{FeeManager, FeeReason, FeeReason::Export, TransactAsset}, + traits::{FeeManager, FeeReason, FeeReason::Export}, XcmExecutor, }; @@ -81,6 +79,8 @@ pub type LocationToAccountId = ( SiblingParachainConvertsVia, // Straight up local `AccountId32` origins just alias directly to `AccountId`. AccountId32Aliases, + // Foreign locations alias into accounts according to a hash of their standard description. + HashedDescription>, ); /// Means for transacting the native currency on this chain. @@ -207,13 +207,6 @@ impl xcm_executor::Config for XcmConfig { type FeeManager = XcmFeeManagerFromComponentsBridgeHub< WaivedLocations, ( - XcmExportFeeToRelayerRewardAccounts< - Self::AssetTransactor, - crate::bridge_to_westend_config::WestendGlobalConsensusNetwork, - crate::bridge_to_westend_config::AssetHubWestendParaId, - bp_bridge_hub_westend::BridgeHubWestend, - crate::bridge_to_westend_config::AssetHubRococoToAssetHubWestendMessagesLane, - >, XcmExportFeeToSibling< bp_rococo::Balance, AccountId, @@ -226,8 +219,8 @@ impl xcm_executor::Config for XcmConfig { ), >; type MessageExporter = ( - crate::bridge_to_westend_config::ToBridgeHubWestendHaulBlobExporter, - crate::bridge_to_bulletin_config::ToRococoBulletinHaulBlobExporter, + XcmOverBridgeHubWestend, + XcmOverRococoBulletin, crate::bridge_to_ethereum_config::SnowbridgeExporter, ); type UniversalAliases = Nothing; @@ -294,100 +287,6 @@ impl cumulus_pallet_xcm::Config for Runtime { type XcmExecutor = XcmExecutor; } -/// A `HandleFee` implementation that simply deposits the fees for `ExportMessage` XCM instructions -/// into the accounts that are used for paying the relayer rewards. -/// Burns the fees in case of a failure. -pub struct XcmExportFeeToRelayerRewardAccounts< - AssetTransactor, - DestNetwork, - DestParaId, - DestBridgedChain, - BridgeLaneId, ->(PhantomData<(AssetTransactor, DestNetwork, DestParaId, DestBridgedChain, BridgeLaneId)>); - -impl< - AssetTransactor: TransactAsset, - DestNetwork: Get, - DestParaId: Get, - DestBridgedChain: bp_runtime::Chain, - BridgeLaneId: Get, - > HandleFee - for XcmExportFeeToRelayerRewardAccounts< - AssetTransactor, - DestNetwork, - DestParaId, - DestBridgedChain, - BridgeLaneId, - > -{ - fn handle_fee(fee: Assets, maybe_context: Option<&XcmContext>, reason: FeeReason) -> Assets { - if matches!(reason, FeeReason::Export { network: bridged_network, destination } - if bridged_network == DestNetwork::get() && - destination == [Parachain(DestParaId::get().into())]) - { - let bridged_chain_id = DestBridgedChain::ID; - - // We have 2 relayer rewards accounts: - // - the SA of the source parachain on this BH: this pays the relayers for delivering - // Source para -> Target Para message delivery confirmations - // - the SA of the destination parachain on this BH: this pays the relayers for - // delivering Target para -> Source Para messages - // We split the `ExportMessage` fee between these 2 accounts. - let source_para_account = PayRewardFromAccount::< - pallet_balances::Pallet, - AccountId, - >::rewards_account(RewardsAccountParams::new( - BridgeLaneId::get(), - bridged_chain_id, - RewardsAccountOwner::ThisChain, - )); - - let dest_para_account = PayRewardFromAccount::< - pallet_balances::Pallet, - AccountId, - >::rewards_account(RewardsAccountParams::new( - BridgeLaneId::get(), - bridged_chain_id, - RewardsAccountOwner::BridgedChain, - )); - - for asset in fee.into_inner() { - match asset.fun { - Fungible(total_fee) => { - let source_fee = total_fee / 2; - deposit_or_burn_fee::( - Asset { id: asset.id.clone(), fun: Fungible(source_fee) }.into(), - maybe_context, - AccountId32 { network: None, id: source_para_account.clone().into() } - .into(), - ); - - let dest_fee = total_fee - source_fee; - deposit_or_burn_fee::( - Asset { id: asset.id, fun: Fungible(dest_fee) }.into(), - maybe_context, - AccountId32 { network: None, id: dest_para_account.clone().into() } - .into(), - ); - }, - NonFungible(_) => { - deposit_or_burn_fee::( - asset.into(), - maybe_context, - AccountId32 { network: None, id: source_para_account.clone().into() } - .into(), - ); - }, - } - } - - return Assets::new() - } - - fee - } -} - pub struct XcmFeeManagerFromComponentsBridgeHub( PhantomData<(WaivedLocations, HandleFee)>, ); diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs index c7b5850f9ffe56620e26e0751a6cb04c07311bb3..7a0f1462e7a7d150c1a4bb5c016bd2af410c9d55 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 @@ -18,7 +18,6 @@ use bp_polkadot_core::Signature; use bridge_hub_rococo_runtime::{ - bridge_to_bulletin_config::OnBridgeHubRococoRefundRococoBulletinMessages, bridge_to_westend_config::OnBridgeHubRococoRefundBridgeHubWestendMessages, xcm_config::XcmConfig, AllPalletsWithoutSystem, BridgeRejectObsoleteHeadersAndMessages, Executive, MessageQueueServiceWeight, Runtime, RuntimeCall, RuntimeEvent, SessionKeys, @@ -183,10 +182,7 @@ fn construct_extrinsic( frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(0), BridgeRejectObsoleteHeadersAndMessages::default(), - ( - OnBridgeHubRococoRefundBridgeHubWestendMessages::default(), - OnBridgeHubRococoRefundRococoBulletinMessages::default(), - ), + (OnBridgeHubRococoRefundBridgeHubWestendMessages::default(),), cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(), frame_metadata_hash_extension::CheckMetadataHash::::new(false), ); 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 e91837af0b21336d2a2ac5684d73318e3260a1d5..20bd5d1291384d0ea6aee8b5adf8ba7c5aa42927 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs @@ -18,11 +18,13 @@ use bp_polkadot_core::Signature; use bridge_hub_rococo_runtime::{ - bridge_common_config, bridge_to_bulletin_config, bridge_to_westend_config, - xcm_config::{RelayNetwork, TokenLocation, XcmConfig}, - AllPalletsWithoutSystem, BridgeRejectObsoleteHeadersAndMessages, EthereumGatewayAddress, - Executive, ExistentialDeposit, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, - RuntimeEvent, RuntimeOrigin, SessionKeys, SignedExtra, TransactionPayment, UncheckedExtrinsic, + bridge_common_config, bridge_to_bulletin_config, + bridge_to_ethereum_config::EthereumGatewayAddress, + bridge_to_westend_config, + xcm_config::{LocationToAccountId, RelayNetwork, TokenLocation, XcmConfig}, + AllPalletsWithoutSystem, BridgeRejectObsoleteHeadersAndMessages, Executive, ExistentialDeposit, + ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, SessionKeys, + SignedExtra, TransactionPayment, UncheckedExtrinsic, }; use bridge_hub_test_utils::SlotDurations; use codec::{Decode, Encode}; @@ -30,7 +32,7 @@ use frame_support::{dispatch::GetDispatchInfo, parameter_types, traits::ConstU8} use parachains_common::{AccountId, AuraId, Balance}; use snowbridge_core::ChannelId; use sp_consensus_aura::SlotDuration; -use sp_core::H160; +use sp_core::{crypto::Ss58Codec, H160}; use sp_keyring::AccountKeyring::Alice; use sp_runtime::{ generic::{Era, SignedPayload}, @@ -38,6 +40,7 @@ use sp_runtime::{ }; use testnet_parachains_constants::rococo::{consensus::*, fee::WeightToFee}; use xcm::latest::prelude::*; +use xcm_runtime_apis::conversions::LocationToAccountHelper; parameter_types! { pub CheckingAccount: AccountId = PolkadotXcm::check_account(); @@ -60,10 +63,7 @@ fn construct_extrinsic( frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(0), BridgeRejectObsoleteHeadersAndMessages::default(), - ( - bridge_to_westend_config::OnBridgeHubRococoRefundBridgeHubWestendMessages::default(), - bridge_to_bulletin_config::OnBridgeHubRococoRefundRococoBulletinMessages::default(), - ), + (bridge_to_westend_config::OnBridgeHubRococoRefundBridgeHubWestendMessages::default(),), cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim::new(), frame_metadata_hash_extension::CheckMetadataHash::new(false), ); @@ -121,39 +121,31 @@ bridge_hub_test_utils::test_cases::include_teleports_for_native_asset_works!( bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID ); -#[test] -fn change_required_stake_by_governance_works() { - bridge_hub_test_utils::test_cases::change_storage_constant_by_governance_works::< - Runtime, - bridge_common_config::RequiredStakeForStakeAndSlash, - Balance, - >( - collator_session_keys(), - bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, - Box::new(|call| RuntimeCall::System(call).encode()), - || { - ( - bridge_common_config::RequiredStakeForStakeAndSlash::key().to_vec(), - bridge_common_config::RequiredStakeForStakeAndSlash::get(), - ) - }, - |old_value| old_value.checked_mul(2).unwrap(), - ) -} - mod bridge_hub_westend_tests { use super::*; + use bp_messages::LegacyLaneId; use bridge_common_config::{ BridgeGrandpaWestendInstance, BridgeParachainWestendInstance, DeliveryRewardInBalance, + RelayersForLegacyLaneIdsMessagesInstance, }; use bridge_hub_test_utils::test_cases::from_parachain; use bridge_to_westend_config::{ BridgeHubWestendLocation, WestendGlobalConsensusNetwork, - WithBridgeHubWestendMessagesInstance, XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND, + WithBridgeHubWestendMessagesInstance, XcmOverBridgeHubWestendInstance, }; - // Para id of sibling chain used in tests. - pub const SIBLING_PARACHAIN_ID: u32 = 1000; + // Random para id of sibling chain used in tests. + pub const SIBLING_PARACHAIN_ID: u32 = 2053; + // Random para id of sibling chain used in tests. + pub const SIBLING_SYSTEM_PARACHAIN_ID: u32 = 1008; + // Random para id of bridged chain from different global consensus used in tests. + pub const BRIDGED_LOCATION_PARACHAIN_ID: u32 = 1075; + + parameter_types! { + pub SiblingParachainLocation: Location = Location::new(1, [Parachain(SIBLING_PARACHAIN_ID)]); + pub SiblingSystemParachainLocation: Location = Location::new(1, [Parachain(SIBLING_SYSTEM_PARACHAIN_ID)]); + pub BridgedUniversalLocation: InteriorLocation = [GlobalConsensus(WestendGlobalConsensusNetwork::get()), Parachain(BRIDGED_LOCATION_PARACHAIN_ID)].into(); + } // Runtime from tests PoV type RuntimeTestsAdapter = from_parachain::WithRemoteParachainHelperAdapter< @@ -162,6 +154,7 @@ mod bridge_hub_westend_tests { BridgeGrandpaWestendInstance, BridgeParachainWestendInstance, WithBridgeHubWestendMessagesInstance, + RelayersForLegacyLaneIdsMessagesInstance, >; #[test] @@ -313,14 +306,29 @@ mod bridge_hub_westend_tests { _ => None, } }), - || ExportMessage { network: Westend, destination: [Parachain(bridge_to_westend_config::AssetHubWestendParaId::get().into())].into(), xcm: Xcm(vec![]) }, + || ExportMessage { network: WestendGlobalConsensusNetwork::get(), destination: [Parachain(BRIDGED_LOCATION_PARACHAIN_ID)].into(), xcm: Xcm(vec![]) }, Some((TokenLocation::get(), ExistentialDeposit::get()).into()), // value should be >= than value generated by `can_calculate_weight_for_paid_export_message_with_reserve_transfer` Some((TokenLocation::get(), bp_bridge_hub_rococo::BridgeHubRococoBaseXcmFeeInRocs::get()).into()), || { PolkadotXcm::force_xcm_version(RuntimeOrigin::root(), Box::new(BridgeHubWestendLocation::get()), XCM_VERSION).expect("version saved!"); - XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND + // we need to create lane between sibling parachain and remote destination + bridge_hub_test_utils::ensure_opened_bridge::< + Runtime, + XcmOverBridgeHubWestendInstance, + LocationToAccountId, + TokenLocation, + >( + SiblingParachainLocation::get(), + BridgedUniversalLocation::get(), + |locations, fee| { + bridge_hub_test_utils::open_bridge_with_storage::< + Runtime, + XcmOverBridgeHubWestendInstance + >(locations, fee, LegacyLaneId([0, 0, 0, 1])) + } + ).1 }, ) } @@ -354,7 +362,7 @@ mod bridge_hub_westend_tests { _ => None, } }), - || XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND, + || (), ) } @@ -368,8 +376,27 @@ mod bridge_hub_westend_tests { bp_bridge_hub_westend::BRIDGE_HUB_WESTEND_PARACHAIN_ID, SIBLING_PARACHAIN_ID, Rococo, - || XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND, + || { + // we need to create lane between sibling parachain and remote destination + bridge_hub_test_utils::ensure_opened_bridge::< + Runtime, + XcmOverBridgeHubWestendInstance, + LocationToAccountId, + TokenLocation, + >( + SiblingParachainLocation::get(), + BridgedUniversalLocation::get(), + |locations, fee| { + bridge_hub_test_utils::open_bridge_with_storage::< + Runtime, + XcmOverBridgeHubWestendInstance, + >(locations, fee, LegacyLaneId([0, 0, 0, 1])) + }, + ) + .1 + }, construct_and_apply_extrinsic, + true, ) } @@ -383,8 +410,27 @@ mod bridge_hub_westend_tests { bp_bridge_hub_westend::BRIDGE_HUB_WESTEND_PARACHAIN_ID, SIBLING_PARACHAIN_ID, Rococo, - || XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND, + || { + // we need to create lane between sibling parachain and remote destination + bridge_hub_test_utils::ensure_opened_bridge::< + Runtime, + XcmOverBridgeHubWestendInstance, + LocationToAccountId, + TokenLocation, + >( + SiblingParachainLocation::get(), + BridgedUniversalLocation::get(), + |locations, fee| { + bridge_hub_test_utils::open_bridge_with_storage::< + Runtime, + XcmOverBridgeHubWestendInstance, + >(locations, fee, LegacyLaneId([0, 0, 0, 1])) + }, + ) + .1 + }, construct_and_apply_extrinsic, + false, ) } @@ -450,15 +496,23 @@ mod bridge_hub_westend_tests { mod bridge_hub_bulletin_tests { use super::*; + use bp_messages::{HashedLaneId, LaneIdType}; use bridge_common_config::BridgeGrandpaRococoBulletinInstance; + use bridge_hub_rococo_runtime::bridge_common_config::RelayersForPermissionlessLanesInstance; use bridge_hub_test_utils::test_cases::from_grandpa_chain; use bridge_to_bulletin_config::{ RococoBulletinGlobalConsensusNetwork, RococoBulletinGlobalConsensusNetworkLocation, - WithRococoBulletinMessagesInstance, XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN, + WithRococoBulletinMessagesInstance, XcmOverPolkadotBulletinInstance, }; - // Para id of sibling chain used in tests. - pub const SIBLING_PARACHAIN_ID: u32 = rococo_runtime_constants::system_parachain::PEOPLE_ID; + // Random para id of sibling chain used in tests. + pub const SIBLING_PEOPLE_PARACHAIN_ID: u32 = + rococo_runtime_constants::system_parachain::PEOPLE_ID; + + parameter_types! { + pub SiblingPeopleParachainLocation: Location = Location::new(1, [Parachain(SIBLING_PEOPLE_PARACHAIN_ID)]); + pub BridgedBulletinLocation: InteriorLocation = [GlobalConsensus(RococoBulletinGlobalConsensusNetwork::get())].into(); + } // Runtime from tests PoV type RuntimeTestsAdapter = from_grandpa_chain::WithRemoteGrandpaChainHelperAdapter< @@ -466,6 +520,7 @@ mod bridge_hub_bulletin_tests { AllPalletsWithoutSystem, BridgeGrandpaRococoBulletinInstance, WithRococoBulletinMessagesInstance, + RelayersForPermissionlessLanesInstance, >; #[test] @@ -505,7 +560,7 @@ mod bridge_hub_bulletin_tests { >( collator_session_keys(), bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, - SIBLING_PARACHAIN_ID, + SIBLING_PEOPLE_PARACHAIN_ID, Box::new(|runtime_event_encoded: Vec| { match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { Ok(RuntimeEvent::BridgePolkadotBulletinMessages(event)) => Some(event), @@ -522,7 +577,22 @@ mod bridge_hub_bulletin_tests { || { PolkadotXcm::force_xcm_version(RuntimeOrigin::root(), Box::new(RococoBulletinGlobalConsensusNetworkLocation::get()), XCM_VERSION).expect("version saved!"); - XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN + // we need to create lane between RococoPeople and RococoBulletin + bridge_hub_test_utils::ensure_opened_bridge::< + Runtime, + XcmOverPolkadotBulletinInstance, + LocationToAccountId, + TokenLocation, + >( + SiblingPeopleParachainLocation::get(), + BridgedBulletinLocation::get(), + |locations, fee| { + bridge_hub_test_utils::open_bridge_with_storage::< + Runtime, + XcmOverPolkadotBulletinInstance + >(locations, fee, HashedLaneId::try_new(1, 2).unwrap()) + } + ).1 }, ) } @@ -543,7 +613,7 @@ mod bridge_hub_bulletin_tests { collator_session_keys(), slot_durations(), bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, - SIBLING_PARACHAIN_ID, + SIBLING_PEOPLE_PARACHAIN_ID, Box::new(|runtime_event_encoded: Vec| { match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { Ok(RuntimeEvent::ParachainSystem(event)) => Some(event), @@ -556,7 +626,7 @@ mod bridge_hub_bulletin_tests { _ => None, } }), - || XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN, + || (), ) } @@ -567,10 +637,29 @@ mod bridge_hub_bulletin_tests { collator_session_keys(), slot_durations(), bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, - SIBLING_PARACHAIN_ID, + SIBLING_PEOPLE_PARACHAIN_ID, Rococo, - || XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN, + || { + // we need to create lane between RococoPeople and RococoBulletin + bridge_hub_test_utils::ensure_opened_bridge::< + Runtime, + XcmOverPolkadotBulletinInstance, + LocationToAccountId, + TokenLocation, + >( + SiblingPeopleParachainLocation::get(), + BridgedBulletinLocation::get(), + |locations, fee| { + bridge_hub_test_utils::open_bridge_with_storage::< + Runtime, + XcmOverPolkadotBulletinInstance, + >(locations, fee, HashedLaneId::try_new(1, 2).unwrap()) + }, + ) + .1 + }, construct_and_apply_extrinsic, + false, ) } @@ -581,10 +670,159 @@ mod bridge_hub_bulletin_tests { collator_session_keys(), slot_durations(), bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, - SIBLING_PARACHAIN_ID, + SIBLING_PEOPLE_PARACHAIN_ID, Rococo, - || XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN, + || { + // we need to create lane between RococoPeople and RococoBulletin + bridge_hub_test_utils::ensure_opened_bridge::< + Runtime, + XcmOverPolkadotBulletinInstance, + LocationToAccountId, + TokenLocation, + >( + SiblingPeopleParachainLocation::get(), + BridgedBulletinLocation::get(), + |locations, fee| { + bridge_hub_test_utils::open_bridge_with_storage::< + Runtime, + XcmOverPolkadotBulletinInstance, + >(locations, fee, HashedLaneId::try_new(1, 2).unwrap()) + }, + ) + .1 + }, construct_and_apply_extrinsic, + false, ) } } + +#[test] +fn change_required_stake_by_governance_works() { + bridge_hub_test_utils::test_cases::change_storage_constant_by_governance_works::< + Runtime, + bridge_common_config::RequiredStakeForStakeAndSlash, + Balance, + >( + collator_session_keys(), + bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, + Box::new(|call| RuntimeCall::System(call).encode()), + || { + ( + bridge_common_config::RequiredStakeForStakeAndSlash::key().to_vec(), + bridge_common_config::RequiredStakeForStakeAndSlash::get(), + ) + }, + |old_value| old_value.checked_mul(2).unwrap(), + ) +} + +#[test] +fn location_conversion_works() { + // the purpose of hardcoded values is to catch an unintended location conversion logic + // change. + struct TestCase { + description: &'static str, + location: Location, + expected_account_id_str: &'static str, + } + + let test_cases = vec![ + // DescribeTerminus + TestCase { + description: "DescribeTerminus Parent", + location: Location::new(1, Here), + expected_account_id_str: "5Dt6dpkWPwLaH4BBCKJwjiWrFVAGyYk3tLUabvyn4v7KtESG", + }, + TestCase { + description: "DescribeTerminus Sibling", + location: Location::new(1, [Parachain(1111)]), + expected_account_id_str: "5Eg2fnssmmJnF3z1iZ1NouAuzciDaaDQH7qURAy3w15jULDk", + }, + // DescribePalletTerminal + TestCase { + description: "DescribePalletTerminal Parent", + location: Location::new(1, [PalletInstance(50)]), + expected_account_id_str: "5CnwemvaAXkWFVwibiCvf2EjqwiqBi29S5cLLydZLEaEw6jZ", + }, + TestCase { + description: "DescribePalletTerminal Sibling", + location: Location::new(1, [Parachain(1111), PalletInstance(50)]), + expected_account_id_str: "5GFBgPjpEQPdaxEnFirUoa51u5erVx84twYxJVuBRAT2UP2g", + }, + // DescribeAccountId32Terminal + TestCase { + description: "DescribeAccountId32Terminal Parent", + location: Location::new( + 1, + [Junction::AccountId32 { network: None, id: AccountId::from(Alice).into() }], + ), + expected_account_id_str: "5EueAXd4h8u75nSbFdDJbC29cmi4Uo1YJssqEL9idvindxFL", + }, + TestCase { + description: "DescribeAccountId32Terminal Sibling", + location: Location::new( + 1, + [ + Parachain(1111), + Junction::AccountId32 { network: None, id: AccountId::from(Alice).into() }, + ], + ), + expected_account_id_str: "5Dmbuiq48fU4iW58FKYqoGbbfxFHjbAeGLMtjFg6NNCw3ssr", + }, + // DescribeAccountKey20Terminal + TestCase { + description: "DescribeAccountKey20Terminal Parent", + location: Location::new(1, [AccountKey20 { network: None, key: [0u8; 20] }]), + expected_account_id_str: "5F5Ec11567pa919wJkX6VHtv2ZXS5W698YCW35EdEbrg14cg", + }, + TestCase { + description: "DescribeAccountKey20Terminal Sibling", + location: Location::new( + 1, + [Parachain(1111), AccountKey20 { network: None, key: [0u8; 20] }], + ), + expected_account_id_str: "5CB2FbUds2qvcJNhDiTbRZwiS3trAy6ydFGMSVutmYijpPAg", + }, + // DescribeTreasuryVoiceTerminal + TestCase { + description: "DescribeTreasuryVoiceTerminal Parent", + location: Location::new(1, [Plurality { id: BodyId::Treasury, part: BodyPart::Voice }]), + expected_account_id_str: "5CUjnE2vgcUCuhxPwFoQ5r7p1DkhujgvMNDHaF2bLqRp4D5F", + }, + TestCase { + description: "DescribeTreasuryVoiceTerminal Sibling", + location: Location::new( + 1, + [Parachain(1111), Plurality { id: BodyId::Treasury, part: BodyPart::Voice }], + ), + expected_account_id_str: "5G6TDwaVgbWmhqRUKjBhRRnH4ry9L9cjRymUEmiRsLbSE4gB", + }, + // DescribeBodyTerminal + TestCase { + description: "DescribeBodyTerminal Parent", + location: Location::new(1, [Plurality { id: BodyId::Unit, part: BodyPart::Voice }]), + expected_account_id_str: "5EBRMTBkDisEXsaN283SRbzx9Xf2PXwUxxFCJohSGo4jYe6B", + }, + TestCase { + description: "DescribeBodyTerminal Sibling", + location: Location::new( + 1, + [Parachain(1111), Plurality { id: BodyId::Unit, part: BodyPart::Voice }], + ), + expected_account_id_str: "5DBoExvojy8tYnHgLL97phNH975CyT45PWTZEeGoBZfAyRMH", + }, + ]; + + for tc in test_cases { + let expected = + AccountId::from_string(tc.expected_account_id_str).expect("Invalid AccountId string"); + + let got = LocationToAccountHelper::::convert_location( + tc.location.into(), + ) + .unwrap(); + + assert_eq!(got, expected, "{}", tc.description); + } +} 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 1c9d8c0207b993517100b8be2e194c75336b4103..a0233bf2ea452f89195a909e8d868df4a791396f 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.2.0" +version = "0.3.0" authors.workspace = true edition.workspace = true description = "Westend's BridgeHub parachain runtime" @@ -18,6 +18,7 @@ hex-literal = { workspace = true, default-features = true } log = { workspace = true } scale-info = { features = ["derive"], workspace = true } serde = { optional = true, features = ["derive"], workspace = true, default-features = true } +serde_json = { features = ["alloc"], workspace = true } # Substrate frame-benchmarking = { optional = true, workspace = true } @@ -90,7 +91,7 @@ bp-messages = { workspace = true } bp-parachains = { workspace = true } bp-polkadot-core = { workspace = true } bp-relayers = { workspace = true } -bp-runtime = { features = ["test-helpers"], workspace = true } +bp-runtime = { workspace = true } bp-rococo = { workspace = true } bp-westend = { workspace = true } pallet-bridge-grandpa = { workspace = true } @@ -117,6 +118,7 @@ snowbridge-runtime-common = { workspace = true } [dev-dependencies] bridge-hub-test-utils = { workspace = true, default-features = true } bridge-runtime-common = { features = ["integrity-test"], workspace = true, default-features = true } +pallet-bridge-relayers = { features = ["integrity-test"], workspace = true } sp-keyring = { workspace = true, default-features = true } snowbridge-runtime-test-common = { workspace = true, default-features = true } @@ -180,6 +182,7 @@ std = [ "polkadot-runtime-common/std", "scale-info/std", "serde", + "serde_json/std", "snowbridge-beacon-primitives/std", "snowbridge-core/std", "snowbridge-outbound-queue-runtime-api/std", diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_common_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_common_config.rs index 9bae106395a62a9d1cb6861ed3099b5f3bf963cc..0872d0498f85789edb4c105831754f6e0f406bf1 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_common_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_common_config.rs @@ -22,6 +22,7 @@ //! GRANDPA tracking pallet only needs to be aware of one chain. use super::{weights, AccountId, Balance, Balances, BlockNumber, Runtime, RuntimeEvent}; +use bp_messages::LegacyLaneId; use frame_support::parameter_types; parameter_types! { @@ -33,11 +34,15 @@ parameter_types! { } /// Allows collect and claim rewards for relayers -impl pallet_bridge_relayers::Config for Runtime { +pub type RelayersForLegacyLaneIdsMessagesInstance = (); +impl pallet_bridge_relayers::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Reward = Balance; - type PaymentProcedure = - bp_relayers::PayRewardFromAccount, AccountId>; + type PaymentProcedure = bp_relayers::PayRewardFromAccount< + pallet_balances::Pallet, + AccountId, + Self::LaneId, + >; type StakeAndSlash = pallet_bridge_relayers::StakeAndSlashNamed< AccountId, BlockNumber, @@ -47,4 +52,5 @@ impl pallet_bridge_relayers::Config for Runtime { RelayerStakeLease, >; type WeightInfo = weights::pallet_bridge_relayers::WeightInfo; + type LaneId = LegacyLaneId; } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs index 7922d3ed02b1fe73293fd7bf4344fb5609c3aeda..dbca4166a13513b23e09d01798cec73aceb1097f 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs @@ -30,9 +30,10 @@ use sp_core::H160; use testnet_parachains_constants::westend::{ currency::*, fee::WeightToFee, - snowbridge::{EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX}, + snowbridge::{EthereumLocation, EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX}, }; +use crate::xcm_config::RelayNetwork; #[cfg(feature = "runtime-benchmarks")] use benchmark_helpers::DoNothingRouter; use frame_support::{parameter_types, weights::ConstantMultiplier}; @@ -41,6 +42,9 @@ use sp_runtime::{ traits::{ConstU32, ConstU8, Keccak256}, FixedU128, }; +use xcm::prelude::{GlobalConsensus, InteriorLocation, Location, Parachain}; + +pub const SLOTS_PER_EPOCH: u32 = snowbridge_pallet_ethereum_client::config::SLOTS_PER_EPOCH as u32; /// Exports message to the Ethereum Gateway contract. pub type SnowbridgeExporter = EthereumBlobExporter< @@ -48,6 +52,7 @@ pub type SnowbridgeExporter = EthereumBlobExporter< EthereumNetwork, snowbridge_pallet_outbound_queue::Pallet, snowbridge_core::AgentIdOf, + EthereumSystem, >; // Ethereum Bridge @@ -64,8 +69,9 @@ parameter_types! { rewards: Rewards { local: 1 * UNITS, remote: meth(1) }, multiplier: FixedU128::from_rational(1, 1), }; + pub AssetHubFromEthereum: Location = Location::new(1,[GlobalConsensus(RelayNetwork::get()),Parachain(westend_runtime_constants::system_parachain::ASSET_HUB_ID)]); + pub EthereumUniversalLocation: InteriorLocation = [GlobalConsensus(EthereumNetwork::get())].into(); } - impl snowbridge_pallet_inbound_queue::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Verifier = snowbridge_pallet_ethereum_client::Pallet; @@ -84,6 +90,9 @@ impl snowbridge_pallet_inbound_queue::Config for Runtime { ConstU8, AccountId, Balance, + EthereumSystem, + EthereumUniversalLocation, + AssetHubFromEthereum, >; type WeightToFee = WeightToFee; type LengthToFee = ConstantMultiplier; @@ -163,6 +172,7 @@ parameter_types! { impl snowbridge_pallet_ethereum_client::Config for Runtime { type RuntimeEvent = RuntimeEvent; type ForkVersions = ChainForkVersions; + type FreeHeadersInterval = ConstU32; type WeightInfo = crate::weights::snowbridge_pallet_ethereum_client::WeightInfo; } @@ -178,6 +188,8 @@ impl snowbridge_pallet_system::Config for Runtime { type Helper = (); type DefaultPricingParameters = Parameters; type InboundDeliveryCost = EthereumInboundQueue; + type UniversalLocation = UniversalLocation; + type EthereumLocation = EthereumLocation; } #[cfg(feature = "runtime-benchmarks")] diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs index be4d40c2275f28f09191b782bdad88bc7526b8bd..e45654bc62bd23f32f03c342ee3eb37f4c8ad1a5 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 @@ -17,34 +17,37 @@ //! Bridge definitions used on BridgeHub with the Westend flavor. use crate::{ - bridge_common_config::DeliveryRewardInBalance, weights, xcm_config::UniversalLocation, - BridgeRococoMessages, PolkadotXcm, Runtime, RuntimeEvent, XcmOverBridgeHubRococo, XcmRouter, + bridge_common_config::{DeliveryRewardInBalance, RelayersForLegacyLaneIdsMessagesInstance}, + weights, + xcm_config::UniversalLocation, + AccountId, Balance, Balances, BridgeRococoMessages, PolkadotXcm, Runtime, RuntimeEvent, + RuntimeHoldReason, XcmOverBridgeHubRococo, XcmRouter, }; use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, - target_chain::FromBridgedChainMessagesProof, LaneId, + target_chain::FromBridgedChainMessagesProof, LegacyLaneId, }; use bp_parachains::SingleParaStoredHeaderDataBuilder; -use bridge_runtime_common::{ - extensions::refund_relayer_extension::{ - ActualFeeRefund, RefundBridgedMessages, RefundSignedExtensionAdapter, - RefundableMessagesLane, - }, - messages_xcm_extension::{ - SenderAndLane, XcmAsPlainPayload, XcmBlobHauler, XcmBlobHaulerAdapter, - XcmBlobMessageDispatch, XcmVersionOfDestAndRemoteBridge, - }, -}; -use codec::Encode; +use bridge_hub_common::xcm_version::XcmVersionOfDestAndRemoteBridge; +use pallet_xcm_bridge_hub::XcmAsPlainPayload; + use frame_support::{ parameter_types, traits::{ConstU32, PalletInfoAccess}, }; +use frame_system::{EnsureNever, EnsureRoot}; +use pallet_bridge_messages::LaneIdOf; +use pallet_bridge_relayers::extension::{ + BridgeRelayersSignedExtension, WithMessagesExtensionConfig, +}; +use parachains_common::xcm_config::{AllSiblingSystemParachains, RelayOrOtherSystemParachains}; +use polkadot_parachain_primitives::primitives::Sibling; +use testnet_parachains_constants::westend::currency::UNITS as WND; use xcm::{ latest::prelude::*, prelude::{InteriorLocation, NetworkId}, }; -use xcm_builder::BridgeBlobDispatcher; +use xcm_builder::{BridgeBlobDispatcher, ParentIsPreset, SiblingParachainConvertsVia}; parameter_types! { pub const RelayChainHeadersToKeep: u32 = 1024; @@ -66,26 +69,6 @@ parameter_types! { // see the `FEE_BOOST_PER_MESSAGE` constant to get the meaning of this value pub PriorityBoostPerMessage: u64 = 182_044_444_444_444; - pub AssetHubWestendParaId: cumulus_primitives_core::ParaId = bp_asset_hub_westend::ASSET_HUB_WESTEND_PARACHAIN_ID.into(); - pub AssetHubRococoParaId: cumulus_primitives_core::ParaId = bp_asset_hub_rococo::ASSET_HUB_ROCOCO_PARACHAIN_ID.into(); - - // Lanes - pub ActiveOutboundLanesToBridgeHubRococo: &'static [bp_messages::LaneId] = &[XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO]; - pub const AssetHubWestendToAssetHubRococoMessagesLane: bp_messages::LaneId = XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO; - pub FromAssetHubWestendToAssetHubRococoRoute: SenderAndLane = SenderAndLane::new( - ParentThen([Parachain(AssetHubWestendParaId::get().into())].into()).into(), - XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO, - ); - pub ActiveLanes: alloc::vec::Vec<(SenderAndLane, (NetworkId, InteriorLocation))> = alloc::vec![ - ( - FromAssetHubWestendToAssetHubRococoRoute::get(), - (RococoGlobalConsensusNetwork::get(), [Parachain(AssetHubRococoParaId::get().into())].into()) - ) - ]; - - pub CongestedMessage: Xcm<()> = build_congestion_message(true).into(); - pub UncongestedMessage: Xcm<()> = build_congestion_message(false).into(); - pub BridgeHubRococoLocation: Location = Location::new( 2, [ @@ -93,67 +76,32 @@ parameter_types! { Parachain(::PARACHAIN_ID) ] ); -} -pub const XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO: LaneId = LaneId([0, 0, 0, 2]); - -fn build_congestion_message(is_congested: bool) -> alloc::vec::Vec> { - alloc::vec![ - UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - Transact { - origin_kind: OriginKind::Xcm, - require_weight_at_most: - bp_asset_hub_westend::XcmBridgeHubRouterTransactCallMaxWeight::get(), - call: bp_asset_hub_westend::Call::ToRococoXcmRouter( - bp_asset_hub_westend::XcmBridgeHubRouterCall::report_bridge_status { - bridge_id: Default::default(), - is_congested, - } - ) - .encode() - .into(), - } - ] + + pub storage BridgeDeposit: Balance = 10 * WND; } /// Proof of messages, coming from Rococo. -pub type FromRococoBridgeHubMessagesProof = - FromBridgedChainMessagesProof; +pub type FromRococoBridgeHubMessagesProof = + FromBridgedChainMessagesProof>; /// Messages delivery proof for Rococo Bridge Hub -> Westend Bridge Hub messages. -pub type ToRococoBridgeHubMessagesDeliveryProof = - FromBridgedChainMessagesDeliveryProof; +pub type ToRococoBridgeHubMessagesDeliveryProof = + FromBridgedChainMessagesDeliveryProof>; /// Dispatches received XCM messages from other bridge type FromRococoMessageBlobDispatcher = BridgeBlobDispatcher; -/// Export XCM messages to be relayed to the other side -pub type ToBridgeHubRococoHaulBlobExporter = XcmOverBridgeHubRococo; - -pub struct ToBridgeHubRococoXcmBlobHauler; -impl XcmBlobHauler for ToBridgeHubRococoXcmBlobHauler { - type Runtime = Runtime; - type MessagesInstance = WithBridgeHubRococoMessagesInstance; - - type ToSourceChainSender = XcmRouter; - type CongestedMessage = CongestedMessage; - type UncongestedMessage = UncongestedMessage; -} - -/// On messages delivered callback. -type OnMessagesDelivered = XcmBlobHaulerAdapter; - /// Signed extension that refunds relayers that are delivering messages from the Rococo parachain. -pub type OnBridgeHubWestendRefundBridgeHubRococoMessages = RefundSignedExtensionAdapter< - RefundBridgedMessages< +pub type OnBridgeHubWestendRefundBridgeHubRococoMessages = BridgeRelayersSignedExtension< + Runtime, + WithMessagesExtensionConfig< + StrOnBridgeHubWestendRefundBridgeHubRococoMessages, Runtime, - RefundableMessagesLane< - WithBridgeHubRococoMessagesInstance, - AssetHubWestendToAssetHubRococoMessagesLane, - >, - ActualFeeRefund, + WithBridgeHubRococoMessagesInstance, + RelayersForLegacyLaneIdsMessagesInstance, PriorityBoostPerMessage, - StrOnBridgeHubWestendRefundBridgeHubRococoMessages, >, + LaneIdOf, >; bp_runtime::generate_static_str_provider!(OnBridgeHubWestendRefundBridgeHubRococoMessages); @@ -195,40 +143,97 @@ impl pallet_bridge_messages::Config for Run bp_bridge_hub_rococo::BridgeHubRococo, >; - type ActiveOutboundLanes = ActiveOutboundLanesToBridgeHubRococo; - type OutboundPayload = XcmAsPlainPayload; - type InboundPayload = XcmAsPlainPayload; - type DeliveryPayments = (); + type LaneId = LegacyLaneId; + type DeliveryPayments = (); type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< Runtime, WithBridgeHubRococoMessagesInstance, DeliveryRewardInBalance, >; - type MessageDispatch = XcmBlobMessageDispatch< - FromRococoMessageBlobDispatcher, - Self::WeightInfo, - cumulus_pallet_xcmp_queue::bridging::OutXcmpChannelStatusProvider< - AssetHubWestendParaId, - Runtime, - >, - >; - type OnMessagesDelivered = OnMessagesDelivered; + type MessageDispatch = XcmOverBridgeHubRococo; + type OnMessagesDelivered = XcmOverBridgeHubRococo; } /// Add support for the export and dispatch of XCM programs. pub type XcmOverBridgeHubRococoInstance = pallet_xcm_bridge_hub::Instance1; impl pallet_xcm_bridge_hub::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type UniversalLocation = UniversalLocation; type BridgedNetwork = RococoGlobalConsensusNetworkLocation; type BridgeMessagesPalletInstance = WithBridgeHubRococoMessagesInstance; + type MessageExportPrice = (); type DestinationVersion = XcmVersionOfDestAndRemoteBridge; - type Lanes = ActiveLanes; - type LanesSupport = ToBridgeHubRococoXcmBlobHauler; + + type ForceOrigin = EnsureRoot; + // We don't want to allow creating bridges for this instance with `LegacyLaneId`. + type OpenBridgeOrigin = EnsureNever; + // Converter aligned with `OpenBridgeOrigin`. + type BridgeOriginAccountIdConverter = + (ParentIsPreset, SiblingParachainConvertsVia); + + type BridgeDeposit = BridgeDeposit; + type Currency = Balances; + type RuntimeHoldReason = RuntimeHoldReason; + // Do not require deposit from system parachains or relay chain + type AllowWithoutBridgeDeposit = + RelayOrOtherSystemParachains; + + // TODO:(bridges-v2) - add `LocalXcmChannelManager` impl - https://github.com/paritytech/parity-bridges-common/issues/3047 + type LocalXcmChannelManager = (); + type BlobDispatcher = FromRococoMessageBlobDispatcher; +} + +#[cfg(feature = "runtime-benchmarks")] +pub(crate) fn open_bridge_for_benchmarks( + with: pallet_xcm_bridge_hub::LaneIdOf, + sibling_para_id: u32, +) -> InteriorLocation +where + R: pallet_xcm_bridge_hub::Config, + XBHI: 'static, + C: xcm_executor::traits::ConvertLocation< + bp_runtime::AccountIdOf>, + >, +{ + use pallet_xcm_bridge_hub::{Bridge, BridgeId, BridgeState}; + use sp_runtime::traits::Zero; + use xcm::VersionedInteriorLocation; + + // insert bridge metadata + let lane_id = with; + let sibling_parachain = Location::new(1, [Parachain(sibling_para_id)]); + let universal_source = [GlobalConsensus(Westend), Parachain(sibling_para_id)].into(); + let universal_destination = [GlobalConsensus(Rococo), Parachain(2075)].into(); + let bridge_id = BridgeId::new(&universal_source, &universal_destination); + + // insert only bridge metadata, because the benchmarks create lanes + pallet_xcm_bridge_hub::Bridges::::insert( + bridge_id, + Bridge { + bridge_origin_relative_location: alloc::boxed::Box::new( + sibling_parachain.clone().into(), + ), + bridge_origin_universal_location: alloc::boxed::Box::new( + VersionedInteriorLocation::from(universal_source.clone()), + ), + bridge_destination_universal_location: alloc::boxed::Box::new( + VersionedInteriorLocation::from(universal_destination), + ), + state: BridgeState::Opened, + bridge_owner_account: C::convert_location(&sibling_parachain).expect("valid AccountId"), + deposit: Zero::zero(), + lane_id, + }, + ); + pallet_xcm_bridge_hub::LaneToBridge::::insert(lane_id, bridge_id); + + universal_source } #[cfg(test)] @@ -236,14 +241,11 @@ mod tests { use super::*; use bridge_runtime_common::{ assert_complete_bridge_types, - extensions::refund_relayer_extension::RefundableParachain, integrity::{ assert_complete_with_parachain_bridge_constants, check_message_lane_weights, AssertChainConstants, AssertCompleteBridgeConstants, }, }; - use parachains_common::Balance; - use testnet_parachains_constants::westend; /// Every additional message in the message delivery transaction boosts its priority. /// So the priority of transaction with `N+1` messages is larger than priority of @@ -254,12 +256,12 @@ mod tests { /// /// We want this tip to be large enough (delivery transactions with more messages = less /// operational costs and a faster bridge), so this value should be significant. - const FEE_BOOST_PER_MESSAGE: Balance = 2 * westend::currency::UNITS; + const FEE_BOOST_PER_MESSAGE: Balance = 2 * WND; // see `FEE_BOOST_PER_MESSAGE` comment - const FEE_BOOST_PER_RELAY_HEADER: Balance = 2 * westend::currency::UNITS; + const FEE_BOOST_PER_RELAY_HEADER: Balance = 2 * WND; // see `FEE_BOOST_PER_MESSAGE` comment - const FEE_BOOST_PER_PARACHAIN_HEADER: Balance = 2 * westend::currency::UNITS; + const FEE_BOOST_PER_PARACHAIN_HEADER: Balance = 2 * WND; #[test] fn ensure_bridge_hub_westend_message_lane_weights_are_correct() { @@ -297,19 +299,20 @@ mod tests { }, }); - bridge_runtime_common::extensions::priority_calculator::per_relay_header::ensure_priority_boost_is_sane::< + pallet_bridge_relayers::extension::per_relay_header::ensure_priority_boost_is_sane::< Runtime, BridgeGrandpaRococoInstance, PriorityBoostPerRelayHeader, >(FEE_BOOST_PER_RELAY_HEADER); - bridge_runtime_common::extensions::priority_calculator::per_parachain_header::ensure_priority_boost_is_sane::< + pallet_bridge_relayers::extension::per_parachain_header::ensure_priority_boost_is_sane::< Runtime, - RefundableParachain, + WithBridgeHubRococoMessagesInstance, + bp_bridge_hub_rococo::BridgeHubRococo, PriorityBoostPerParachainHeader, >(FEE_BOOST_PER_PARACHAIN_HEADER); - bridge_runtime_common::extensions::priority_calculator::per_message::ensure_priority_boost_is_sane::< + pallet_bridge_relayers::extension::per_message::ensure_priority_boost_is_sane::< Runtime, WithBridgeHubRococoMessagesInstance, PriorityBoostPerMessage, @@ -323,3 +326,99 @@ mod tests { ); } } + +/// Contains the migration for the AssetHubWestend<>AssetHubRococo bridge. +pub mod migration { + use super::*; + use bp_messages::LegacyLaneId; + use frame_support::traits::ConstBool; + + parameter_types! { + pub AssetHubWestendToAssetHubRococoMessagesLane: LegacyLaneId = LegacyLaneId([0, 0, 0, 2]); + pub AssetHubWestendLocation: Location = Location::new(1, [Parachain(bp_asset_hub_westend::ASSET_HUB_WESTEND_PARACHAIN_ID)]); + pub AssetHubRococoUniversalLocation: InteriorLocation = [GlobalConsensus(RococoGlobalConsensusNetwork::get()), Parachain(bp_asset_hub_rococo::ASSET_HUB_ROCOCO_PARACHAIN_ID)].into(); + } + + /// Ensure that the existing lanes for the AHW<>AHR bridge are correctly configured. + pub type StaticToDynamicLanes = pallet_xcm_bridge_hub::migration::OpenBridgeForLane< + Runtime, + XcmOverBridgeHubRococoInstance, + AssetHubWestendToAssetHubRococoMessagesLane, + // the lanes are already created for AHR<>AHW, but we need to link them to the bridge + // structs + ConstBool, + AssetHubWestendLocation, + AssetHubRococoUniversalLocation, + >; + + mod v1_wrong { + use bp_messages::{LaneState, MessageNonce, UnrewardedRelayer}; + use bp_runtime::AccountIdOf; + use codec::{Decode, Encode}; + use pallet_bridge_messages::BridgedChainOf; + use sp_std::collections::vec_deque::VecDeque; + + #[derive(Encode, Decode, Clone, PartialEq, Eq)] + pub(crate) struct StoredInboundLaneData, I: 'static>( + pub(crate) InboundLaneData>>, + ); + #[derive(Encode, Decode, Clone, PartialEq, Eq)] + pub(crate) struct InboundLaneData { + pub state: LaneState, + pub(crate) relayers: VecDeque>, + pub(crate) last_confirmed_nonce: MessageNonce, + } + #[derive(Encode, Decode, Clone, PartialEq, Eq)] + pub(crate) struct OutboundLaneData { + pub state: LaneState, + pub(crate) oldest_unpruned_nonce: MessageNonce, + pub(crate) latest_received_nonce: MessageNonce, + pub(crate) latest_generated_nonce: MessageNonce, + } + } + + mod v1 { + pub use bp_messages::{InboundLaneData, LaneState, OutboundLaneData}; + pub use pallet_bridge_messages::{InboundLanes, OutboundLanes, StoredInboundLaneData}; + } + + /// Fix for v1 migration - corrects data for OutboundLaneData/InboundLaneData (it is needed only + /// for Rococo/Westend). + pub struct FixMessagesV1Migration(sp_std::marker::PhantomData<(T, I)>); + + impl, I: 'static> frame_support::traits::OnRuntimeUpgrade + for FixMessagesV1Migration + { + fn on_runtime_upgrade() -> Weight { + use sp_core::Get; + let mut weight = T::DbWeight::get().reads(1); + + // `InboundLanes` - add state to the old structs + let translate_inbound = + |pre: v1_wrong::StoredInboundLaneData| -> Option> { + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + Some(v1::StoredInboundLaneData(v1::InboundLaneData { + state: v1::LaneState::Opened, + relayers: pre.0.relayers, + last_confirmed_nonce: pre.0.last_confirmed_nonce, + })) + }; + v1::InboundLanes::::translate_values(translate_inbound); + + // `OutboundLanes` - add state to the old structs + let translate_outbound = + |pre: v1_wrong::OutboundLaneData| -> Option { + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + Some(v1::OutboundLaneData { + state: v1::LaneState::Opened, + oldest_unpruned_nonce: pre.oldest_unpruned_nonce, + latest_received_nonce: pre.latest_received_nonce, + latest_generated_nonce: pre.latest_generated_nonce, + }) + }; + v1::OutboundLanes::::translate_values(translate_outbound); + + weight + } + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/genesis_config_presets.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/genesis_config_presets.rs new file mode 100644 index 0000000000000000000000000000000000000000..0b270e5843391f7bba071ccbf1b40c96fff78ed5 --- /dev/null +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/genesis_config_presets.rs @@ -0,0 +1,174 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! # Bridge Hub Westend Runtime genesis config presets + +use crate::*; +use alloc::{vec, vec::Vec}; +use cumulus_primitives_core::ParaId; +use parachains_common::{genesis_config_helpers::*, AccountId, AuraId}; +use sp_core::sr25519; +use sp_genesis_builder::PresetId; +use testnet_parachains_constants::westend::xcm_version::SAFE_XCM_VERSION; + +const BRIDGE_HUB_WESTEND_ED: Balance = ExistentialDeposit::get(); + +fn bridge_hub_westend_genesis( + invulnerables: Vec<(AccountId, AuraId)>, + endowed_accounts: Vec, + id: ParaId, + bridges_pallet_owner: Option, + asset_hub_para_id: ParaId, + opened_bridges: Vec<(Location, InteriorLocation, Option)>, +) -> serde_json::Value { + let config = RuntimeGenesisConfig { + balances: BalancesConfig { + balances: endowed_accounts + .iter() + .cloned() + .map(|k| (k, 1u128 << 60)) + .collect::>(), + }, + parachain_info: ParachainInfoConfig { parachain_id: id, ..Default::default() }, + collator_selection: CollatorSelectionConfig { + invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(), + candidacy_bond: BRIDGE_HUB_WESTEND_ED * 16, + ..Default::default() + }, + session: SessionConfig { + keys: invulnerables + .into_iter() + .map(|(acc, aura)| { + ( + acc.clone(), // account id + acc, // validator id + SessionKeys { aura }, // session keys + ) + }) + .collect(), + ..Default::default() + }, + polkadot_xcm: PolkadotXcmConfig { + safe_xcm_version: Some(SAFE_XCM_VERSION), + ..Default::default() + }, + bridge_rococo_grandpa: BridgeRococoGrandpaConfig { + owner: bridges_pallet_owner.clone(), + ..Default::default() + }, + bridge_rococo_messages: BridgeRococoMessagesConfig { + owner: bridges_pallet_owner.clone(), + ..Default::default() + }, + xcm_over_bridge_hub_rococo: XcmOverBridgeHubRococoConfig { + opened_bridges, + ..Default::default() + }, + ethereum_system: EthereumSystemConfig { + para_id: id, + asset_hub_para_id, + ..Default::default() + }, + ..Default::default() + }; + + serde_json::to_value(config).expect("Could not build genesis config.") +} + +/// Provides the JSON representation of predefined genesis config for given `id`. +pub fn get_preset(id: &sp_genesis_builder::PresetId) -> Option> { + let patch = match id.try_into() { + Ok(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET) => bridge_hub_westend_genesis( + // initial collators. + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed::("Bob"), + ), + ], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + 1002.into(), + Some(get_account_id_from_seed::("Bob")), + westend_runtime_constants::system_parachain::ASSET_HUB_ID.into(), + vec![( + Location::new(1, [Parachain(1000)]), + Junctions::from([Rococo.into(), Parachain(1000)]), + Some(bp_messages::LegacyLaneId([0, 0, 0, 2])), + )], + ), + Ok(sp_genesis_builder::DEV_RUNTIME_PRESET) => bridge_hub_westend_genesis( + // initial collators. + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed::("Bob"), + ), + ], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + 1002.into(), + Some(get_account_id_from_seed::("Bob")), + westend_runtime_constants::system_parachain::ASSET_HUB_ID.into(), + vec![], + ), + _ => return None, + }; + Some( + serde_json::to_string(&patch) + .expect("serialization to json is expected to work. qed.") + .into_bytes(), + ) +} + +/// List of supported presets. +pub fn preset_names() -> Vec { + vec![ + PresetId::from(sp_genesis_builder::DEV_RUNTIME_PRESET), + PresetId::from(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET), + ] +} 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 1c26742fd67f827e6134f5f5a08f35f6c00938e2..b73d8e5c67faef52bd0a8cf2f64e3d4ecc77fe92 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 @@ -30,6 +30,7 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); pub mod bridge_common_config; pub mod bridge_to_ethereum_config; pub mod bridge_to_rococo_config; +mod genesis_config_presets; mod weights; pub mod xcm_config; @@ -37,13 +38,10 @@ extern crate alloc; use alloc::{vec, vec::Vec}; use bridge_runtime_common::extensions::{ - check_obsolete_extension::{ - CheckAndBoostBridgeGrandpaTransactions, CheckAndBoostBridgeParachainsTransactions, - }, - refund_relayer_extension::RefundableParachain, + CheckAndBoostBridgeGrandpaTransactions, CheckAndBoostBridgeParachainsTransactions, }; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; -use cumulus_primitives_core::ParaId; +use cumulus_primitives_core::{ClaimQueueOffset, CoreSelector, ParaId}; use sp_api::impl_runtime_apis; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ @@ -84,7 +82,7 @@ use xcm_runtime_apis::{ }; use bp_runtime::HeaderId; - +use pallet_bridge_messages::LaneIdOf; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; @@ -104,6 +102,8 @@ use snowbridge_core::{ use testnet_parachains_constants::westend::{consensus::*, currency::*, fee::WeightToFee, time::*}; use xcm::VersionedLocation; +use westend_runtime_constants::system_parachain::{ASSET_HUB_ID, BRIDGE_HUB_ID}; + /// The address format for describing accounts. pub type Address = MultiAddress; @@ -144,10 +144,35 @@ pub type Migrations = ( // unreleased cumulus_pallet_xcmp_queue::migration::v4::MigrationToV4, cumulus_pallet_xcmp_queue::migration::v5::MigrateV4ToV5, + pallet_bridge_messages::migration::v1::MigrationToV1< + Runtime, + bridge_to_rococo_config::WithBridgeHubRococoMessagesInstance, + >, + bridge_to_rococo_config::migration::FixMessagesV1Migration< + Runtime, + bridge_to_rococo_config::WithBridgeHubRococoMessagesInstance, + >, + bridge_to_rococo_config::migration::StaticToDynamicLanes, + frame_support::migrations::RemoveStorage< + BridgeRococoMessagesPalletName, + OutboundLanesCongestedSignalsKey, + RocksDbWeight, + >, + pallet_bridge_relayers::migration::v1::MigrationToV1, // permanent pallet_xcm::migration::MigrateToLatestXcmVersion, + snowbridge_pallet_system::migration::v0::InitializeOnUpgrade< + Runtime, + ConstU32, + ConstU32, + >, ); +parameter_types! { + pub const BridgeRococoMessagesPalletName: &'static str = "BridgeRococoMessages"; + pub const OutboundLanesCongestedSignalsKey: &'static str = "OutboundLanesCongestedSignals"; +} + /// Migration to initialize storage versions for pallets added after genesis. /// /// Ideally this would be done automatically (see @@ -198,11 +223,11 @@ 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_016_000, + spec_version: 1_016_001, impl_version: 0, apis: RUNTIME_API_VERSIONS, - transaction_version: 5, - state_version: 1, + transaction_version: 6, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. @@ -302,6 +327,7 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<0>; + type DoneSlashHandler = (); } parameter_types! { @@ -336,6 +362,7 @@ impl cumulus_pallet_parachain_system::Config for Runtime { type ReservedXcmpWeight = ReservedXcmpWeight; type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases; type ConsensusHook = ConsensusHook; + type SelectCore = cumulus_pallet_parachain_system::DefaultCoreSelector; } type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< @@ -549,10 +576,8 @@ bridge_runtime_common::generate_bridge_reject_obsolete_headers_and_messages! { // Parachains CheckAndBoostBridgeParachainsTransactions< Runtime, - RefundableParachain< - bridge_to_rococo_config::BridgeParachainRococoInstance, - bp_bridge_hub_rococo::BridgeHubRococo, - >, + bridge_to_rococo_config::BridgeParachainRococoInstance, + bp_bridge_hub_rococo::BridgeHubRococo, bridge_to_rococo_config::PriorityBoostPerParachainHeader, xcm_config::TreasuryAccount, >, @@ -798,6 +823,12 @@ impl_runtime_apis! { } } + impl cumulus_primitives_core::GetCoreSelectorApi for Runtime { + fn core_selector() -> (CoreSelector, ClaimQueueOffset) { + ParachainSystem::core_selector() + } + } + impl bp_rococo::RococoFinalityApi for Runtime { fn best_finalized() -> Option> { BridgeRococoGrandpa::best_finalized() @@ -827,7 +858,7 @@ impl_runtime_apis! { impl bp_bridge_hub_rococo::FromBridgeHubRococoInboundLaneApi for Runtime { fn message_details( - lane: bp_messages::LaneId, + lane: LaneIdOf, messages: Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>, ) -> Vec { bridge_runtime_common::messages_api::inbound_message_details::< @@ -839,7 +870,7 @@ impl_runtime_apis! { impl bp_bridge_hub_rococo::ToBridgeHubRococoOutboundLaneApi for Runtime { fn message_details( - lane: bp_messages::LaneId, + lane: LaneIdOf, begin: bp_messages::MessageNonce, end: bp_messages::MessageNonce, ) -> Vec { @@ -1101,11 +1132,46 @@ impl_runtime_apis! { ); BenchmarkError::Stop("XcmVersion was not stored!") })?; + + let sibling_parachain_location = Location::new(1, [Parachain(5678)]); + + // fund SA + use frame_support::traits::fungible::Mutate; + use xcm_executor::traits::ConvertLocation; + frame_support::assert_ok!( + Balances::mint_into( + &xcm_config::LocationToAccountId::convert_location(&sibling_parachain_location).expect("valid AccountId"), + bridge_to_rococo_config::BridgeDeposit::get() + .saturating_add(ExistentialDeposit::get()) + .saturating_add(UNITS * 5) + ) + ); + + // open bridge + let bridge_destination_universal_location: InteriorLocation = [GlobalConsensus(NetworkId::Rococo), Parachain(8765)].into(); + let locations = XcmOverBridgeHubRococo::bridge_locations( + sibling_parachain_location.clone(), + bridge_destination_universal_location.clone(), + )?; + XcmOverBridgeHubRococo::do_open_bridge( + locations, + bp_messages::LegacyLaneId([1, 2, 3, 4]), + true, + ).map_err(|e| { + log::error!( + "Failed to `XcmOverBridgeHubRococo::open_bridge`({:?}, {:?})`, error: {:?}", + sibling_parachain_location, + bridge_destination_universal_location, + e + ); + BenchmarkError::Stop("Bridge was not opened!") + })?; + Ok( ( - bridge_to_rococo_config::FromAssetHubWestendToAssetHubRococoRoute::get().location, + sibling_parachain_location, NetworkId::Rococo, - [Parachain(bridge_to_rococo_config::AssetHubRococoParaId::get().into())].into() + [Parachain(8765)].into() ) ) } @@ -1149,21 +1215,31 @@ impl_runtime_apis! { } fn prepare_message_proof( - params: MessageProofParams, - ) -> (bridge_to_rococo_config::FromRococoBridgeHubMessagesProof, Weight) { + params: MessageProofParams>, + ) -> (bridge_to_rococo_config::FromRococoBridgeHubMessagesProof, Weight) { use cumulus_primitives_core::XcmpMessageSource; assert!(XcmpQueue::take_outbound_messages(usize::MAX).is_empty()); ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests(42.into()); + let universal_source = bridge_to_rococo_config::open_bridge_for_benchmarks::< + Runtime, + bridge_to_rococo_config::XcmOverBridgeHubRococoInstance, + xcm_config::LocationToAccountId, + >(params.lane, 42); prepare_message_proof_from_parachain::< Runtime, bridge_to_rococo_config::BridgeGrandpaRococoInstance, bridge_to_rococo_config::WithBridgeHubRococoMessagesInstance, - >(params, generate_xcm_builder_bridge_message_sample([GlobalConsensus(Westend), Parachain(42)].into())) + >(params, generate_xcm_builder_bridge_message_sample(universal_source)) } fn prepare_message_delivery_proof( - params: MessageDeliveryProofParams, - ) -> bridge_to_rococo_config::ToRococoBridgeHubMessagesDeliveryProof { + params: MessageDeliveryProofParams>, + ) -> bridge_to_rococo_config::ToRococoBridgeHubMessagesDeliveryProof { + let _ = bridge_to_rococo_config::open_bridge_for_benchmarks::< + Runtime, + bridge_to_rococo_config::XcmOverBridgeHubRococoInstance, + xcm_config::LocationToAccountId, + >(params.lane, 42); prepare_message_delivery_proof_from_parachain::< Runtime, bridge_to_rococo_config::BridgeGrandpaRococoInstance, @@ -1195,8 +1271,8 @@ impl_runtime_apis! { parachain_head_size: u32, proof_params: bp_runtime::UnverifiedStorageProofParams, ) -> ( - pallet_bridge_parachains::RelayBlockNumber, - pallet_bridge_parachains::RelayBlockHash, + bp_parachains::RelayBlockNumber, + bp_parachains::RelayBlockHash, bp_polkadot_core::parachains::ParaHeadsProof, Vec<(bp_polkadot_core::parachains::ParaId, bp_polkadot_core::parachains::ParaHash)>, ) { @@ -1208,14 +1284,15 @@ impl_runtime_apis! { } } - impl BridgeRelayersConfig for Runtime { + impl BridgeRelayersConfig for Runtime { fn prepare_rewards_account( - account_params: bp_relayers::RewardsAccountParams, + account_params: bp_relayers::RewardsAccountParams<>::LaneId>, reward: Balance, ) { let rewards_account = bp_relayers::PayRewardFromAccount::< Balances, - AccountId + AccountId, + >::LaneId, >::rewards_account(account_params); Self::deposit_account(rewards_account, reward); } @@ -1253,11 +1330,11 @@ impl_runtime_apis! { } fn get_preset(id: &Option) -> Option> { - get_preset::(id, |_| None) + get_preset::(id, &genesis_config_presets::get_preset) } fn preset_names() -> Vec { - vec![] + genesis_config_presets::preset_names() } } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/block_weights.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/block_weights.rs index e7fdb2aae2a01ec06076de83d94817e540e205dd..41e30725e753fef32404b858a873e2456618d56f 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/block_weights.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/block_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/extrinsic_weights.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/extrinsic_weights.rs index 1a4adb968bb7195428ea00d59cd92dcd3b6eea5f..3bd48f061bba079a76aa8f032ebecd6d40c2156c 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/extrinsic_weights.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/extrinsic_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 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 9b7f7188782faedc0af4186511fd3ab5e1b02685..d60529f9a23727d1e80d6a6885a301f7e7082c9a 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 @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_grandpa.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_grandpa.rs index fa7efc260489bead50ba63969571e018248ddefe..74bf144ac712fffa2231832d5ed3c382dd0c05af 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_grandpa.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_grandpa.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_bridge_grandpa` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-07-11, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-yaoqqom-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -64,17 +64,15 @@ impl pallet_bridge_grandpa::WeightInfo for WeightInfo Weight { + fn submit_finality_proof(p: u32, _v: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `268 + p * (60 ยฑ0)` // Estimated: `51735` - // Minimum execution time: 294_381_000 picoseconds. - Weight::from_parts(21_868_057, 0) + // Minimum execution time: 361_133_000 picoseconds. + Weight::from_parts(406_081_000, 0) .saturating_add(Weight::from_parts(0, 51735)) - // Standard Error: 14_649 - .saturating_add(Weight::from_parts(40_681_012, 0).saturating_mul(p.into())) - // Standard Error: 48_883 - .saturating_add(Weight::from_parts(2_466_672, 0).saturating_mul(v.into())) + // Standard Error: 26_551 + .saturating_add(Weight::from_parts(40_356_046, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -92,8 +90,8 @@ impl pallet_bridge_grandpa::WeightInfo for WeightInfo(PhantomData); impl pallet_bridge_messages::WeightInfo for WeightInfo { /// Storage: `BridgeRococoMessages::PalletOperatingMode` (r:1 w:0) /// Proof: `BridgeRococoMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) - /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeRococoParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubRococo::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverBridgeHubRococo::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubRococo::Bridges` (r:1 w:0) + /// Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn receive_single_message_proof() -> Weight { // Proof Size summary in bytes: - // Measured: `522` - // Estimated: `52645` - // Minimum execution time: 40_289_000 picoseconds. - Weight::from_parts(42_150_000, 0) - .saturating_add(Weight::from_parts(0, 52645)) - .saturating_add(T::DbWeight::get().reads(5)) + // Measured: `701` + // Estimated: `52674` + // Minimum execution time: 62_015_000 picoseconds. + Weight::from_parts(63_891_000, 0) + .saturating_add(Weight::from_parts(0, 52674)) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `BridgeRococoMessages::PalletOperatingMode` (r:1 w:0) /// Proof: `BridgeRococoMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) - /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeRococoParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubRococo::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverBridgeHubRococo::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubRococo::Bridges` (r:1 w:0) + /// Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 4076]`. fn receive_n_messages_proof(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `522` - // Estimated: `52645` - // Minimum execution time: 40_572_000 picoseconds. - Weight::from_parts(41_033_000, 0) - .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 12_000 - .saturating_add(Weight::from_parts(11_710_588, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(5)) + // Measured: `701` + // Estimated: `52674` + // Minimum execution time: 62_034_000 picoseconds. + Weight::from_parts(63_355_000, 0) + .saturating_add(Weight::from_parts(0, 52674)) + // Standard Error: 8_231 + .saturating_add(Weight::from_parts(14_096_117, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `BridgeRococoMessages::PalletOperatingMode` (r:1 w:0) /// Proof: `BridgeRococoMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) - /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeRococoParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubRococo::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverBridgeHubRococo::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubRococo::Bridges` (r:1 w:0) + /// Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn receive_single_message_proof_with_outbound_lane_state() -> Weight { // Proof Size summary in bytes: - // Measured: `522` - // Estimated: `52645` - // Minimum execution time: 46_655_000 picoseconds. - Weight::from_parts(49_576_000, 0) - .saturating_add(Weight::from_parts(0, 52645)) - .saturating_add(T::DbWeight::get().reads(5)) + // Measured: `701` + // Estimated: `52674` + // Minimum execution time: 65_063_000 picoseconds. + Weight::from_parts(67_125_000, 0) + .saturating_add(Weight::from_parts(0, 52674)) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `BridgeRococoMessages::PalletOperatingMode` (r:1 w:0) /// Proof: `BridgeRococoMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) - /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeRococoParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubRococo::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverBridgeHubRococo::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubRococo::Bridges` (r:1 w:0) + /// Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 16384]`. fn receive_single_n_bytes_message_proof(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `522` - // Estimated: `52645` - // Minimum execution time: 40_245_000 picoseconds. - Weight::from_parts(43_461_320, 0) - .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 21 - .saturating_add(Weight::from_parts(2_246, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(5)) + // Measured: `701` + // Estimated: `52674` + // Minimum execution time: 58_688_000 picoseconds. + Weight::from_parts(61_404_716, 0) + .saturating_add(Weight::from_parts(0, 52674)) + // Standard Error: 7 + .saturating_add(Weight::from_parts(2_249, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `BridgeRococoMessages::PalletOperatingMode` (r:1 w:0) @@ -139,21 +147,25 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgeRococoParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeRococoParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::OutboundLanes` (r:1 w:1) - /// Proof: `BridgeRococoMessages::OutboundLanes` (`max_values`: Some(1), `max_size`: Some(44), added: 539, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) /// Storage: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Proof: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Storage: `BridgeRelayers::RelayerRewards` (r:1 w:1) - /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubRococo::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverBridgeHubRococo::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubRococo::Bridges` (r:1 w:0) + /// Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::OutboundMessages` (r:0 w:1) - /// Proof: `BridgeRococoMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_single_message() -> Weight { // Proof Size summary in bytes: - // Measured: `357` - // Estimated: `3822` - // Minimum execution time: 32_001_000 picoseconds. - Weight::from_parts(32_842_000, 0) - .saturating_add(Weight::from_parts(0, 3822)) - .saturating_add(T::DbWeight::get().reads(5)) + // Measured: `710` + // Estimated: `5383` + // Minimum execution time: 53_123_000 picoseconds. + Weight::from_parts(54_417_000, 0) + .saturating_add(Weight::from_parts(0, 5383)) + .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `BridgeRococoMessages::PalletOperatingMode` (r:1 w:0) @@ -161,21 +173,25 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgeRococoParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeRococoParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::OutboundLanes` (r:1 w:1) - /// Proof: `BridgeRococoMessages::OutboundLanes` (`max_values`: Some(1), `max_size`: Some(44), added: 539, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) /// Storage: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Proof: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Storage: `BridgeRelayers::RelayerRewards` (r:1 w:1) - /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubRococo::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverBridgeHubRococo::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubRococo::Bridges` (r:1 w:0) + /// Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::OutboundMessages` (r:0 w:2) - /// Proof: `BridgeRococoMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { // Proof Size summary in bytes: - // Measured: `357` - // Estimated: `3822` - // Minimum execution time: 33_287_000 picoseconds. - Weight::from_parts(33_769_000, 0) - .saturating_add(Weight::from_parts(0, 3822)) - .saturating_add(T::DbWeight::get().reads(5)) + // Measured: `710` + // Estimated: `5383` + // Minimum execution time: 55_140_000 picoseconds. + Weight::from_parts(56_456_000, 0) + .saturating_add(Weight::from_parts(0, 5383)) + .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(4)) } /// Storage: `BridgeRococoMessages::PalletOperatingMode` (r:1 w:0) @@ -183,31 +199,37 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgeRococoParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeRococoParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::OutboundLanes` (r:1 w:1) - /// Proof: `BridgeRococoMessages::OutboundLanes` (`max_values`: Some(1), `max_size`: Some(44), added: 539, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) /// Storage: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Proof: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Storage: `BridgeRelayers::RelayerRewards` (r:2 w:2) - /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubRococo::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverBridgeHubRococo::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubRococo::Bridges` (r:1 w:0) + /// Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::OutboundMessages` (r:0 w:2) - /// Proof: `BridgeRococoMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { // Proof Size summary in bytes: - // Measured: `357` - // Estimated: `6086` - // Minimum execution time: 37_136_000 picoseconds. - Weight::from_parts(38_294_000, 0) - .saturating_add(Weight::from_parts(0, 6086)) - .saturating_add(T::DbWeight::get().reads(6)) + // Measured: `710` + // Estimated: `6144` + // Minimum execution time: 60_415_000 picoseconds. + Weight::from_parts(62_057_000, 0) + .saturating_add(Weight::from_parts(0, 6144)) + .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(5)) } /// Storage: `BridgeRococoMessages::PalletOperatingMode` (r:1 w:0) /// Proof: `BridgeRococoMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:1) - /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeRococoParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubRococo::LaneToBridge` (r:1 w:0) + /// Proof: `XcmOverBridgeHubRococo::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Storage: `XcmOverBridgeHubRococo::Bridges` (r:1 w:0) + /// Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `XcmpQueue::DeliveryFeeFactor` (r:1 w:0) @@ -220,19 +242,21 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParachainSystem::RelevantMessagingState` (r:1 w:0) /// Proof: `ParachainSystem::RelevantMessagingState` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:1) + /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `XcmpQueue::OutboundXcmpMessages` (r:0 w:1) /// Proof: `XcmpQueue::OutboundXcmpMessages` (`max_values`: None, `max_size`: Some(105506), added: 107981, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 16384]`. fn receive_single_n_bytes_message_proof_with_dispatch(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `653` - // Estimated: `52645` - // Minimum execution time: 55_942_000 picoseconds. - Weight::from_parts(60_615_769, 0) - .saturating_add(Weight::from_parts(0, 52645)) - // Standard Error: 14 - .saturating_add(Weight::from_parts(7_225, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(10)) + // Measured: `965` + // Estimated: `52674` + // Minimum execution time: 84_340_000 picoseconds. + Weight::from_parts(89_615_003, 0) + .saturating_add(Weight::from_parts(0, 52674)) + // Standard Error: 15 + .saturating_add(Weight::from_parts(7_574, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().writes(4)) } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_parachains.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_parachains.rs index b4748f1417059e91e482c650f5046ef09b76a7af..87c5057cf9b862a0afe91303b50e9cd7e67dd66b 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_parachains.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_parachains.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_bridge_parachains` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-07-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -61,13 +61,15 @@ impl pallet_bridge_parachains::WeightInfo for WeightInf /// Storage: `BridgeRococoParachains::ImportedParaHeads` (r:0 w:1) /// Proof: `BridgeRococoParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// The range of component `p` is `[1, 2]`. - fn submit_parachain_heads_with_n_parachains(_p: u32, ) -> Weight { + fn submit_parachain_heads_with_n_parachains(p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `315` // Estimated: `2543` - // Minimum execution time: 34_177_000 picoseconds. - Weight::from_parts(35_662_308, 0) + // Minimum execution time: 39_518_000 picoseconds. + Weight::from_parts(40_461_018, 0) .saturating_add(Weight::from_parts(0, 2543)) + // Standard Error: 98_154 + .saturating_add(Weight::from_parts(479_640, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -87,8 +89,8 @@ impl pallet_bridge_parachains::WeightInfo for WeightInf // Proof Size summary in bytes: // Measured: `315` // Estimated: `2543` - // Minimum execution time: 35_975_000 picoseconds. - Weight::from_parts(36_510_000, 0) + // Minimum execution time: 41_243_000 picoseconds. + Weight::from_parts(42_293_000, 0) .saturating_add(Weight::from_parts(0, 2543)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) @@ -109,8 +111,8 @@ impl pallet_bridge_parachains::WeightInfo for WeightInf // Proof Size summary in bytes: // Measured: `315` // Estimated: `2543` - // Minimum execution time: 62_837_000 picoseconds. - Weight::from_parts(63_562_000, 0) + // Minimum execution time: 70_926_000 picoseconds. + Weight::from_parts(71_681_000, 0) .saturating_add(Weight::from_parts(0, 2543)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(4)) diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_relayers.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_relayers.rs index 60d81dc3082a86ea3cea3951d4ed59cf9be8ed56..74be73df14030b7115af80f636ec585139158d59 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_relayers.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_relayers.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_bridge_relayers` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-07-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -49,15 +49,15 @@ use core::marker::PhantomData; pub struct WeightInfo(PhantomData); impl pallet_bridge_relayers::WeightInfo for WeightInfo { /// Storage: `BridgeRelayers::RelayerRewards` (r:1 w:1) - /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn claim_rewards() -> Weight { // Proof Size summary in bytes: - // Measured: `207` + // Measured: `272` // Estimated: `3593` - // Minimum execution time: 43_132_000 picoseconds. - Weight::from_parts(43_923_000, 0) + // Minimum execution time: 52_499_000 picoseconds. + Weight::from_parts(53_659_000, 0) .saturating_add(Weight::from_parts(0, 3593)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -70,10 +70,10 @@ impl pallet_bridge_relayers::WeightInfo for WeightInfo< /// Proof: `Balances::Reserves` (`max_values`: None, `max_size`: Some(1249), added: 3724, mode: `MaxEncodedLen`) fn register() -> Weight { // Proof Size summary in bytes: - // Measured: `61` + // Measured: `97` // Estimated: `4714` - // Minimum execution time: 22_765_000 picoseconds. - Weight::from_parts(23_576_000, 0) + // Minimum execution time: 28_706_000 picoseconds. + Weight::from_parts(29_434_000, 0) .saturating_add(Weight::from_parts(0, 4714)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(2)) @@ -84,10 +84,10 @@ impl pallet_bridge_relayers::WeightInfo for WeightInfo< /// Proof: `Balances::Reserves` (`max_values`: None, `max_size`: Some(1249), added: 3724, mode: `MaxEncodedLen`) fn deregister() -> Weight { // Proof Size summary in bytes: - // Measured: `160` + // Measured: `197` // Estimated: `4714` - // Minimum execution time: 24_013_000 picoseconds. - Weight::from_parts(24_460_000, 0) + // Minimum execution time: 29_563_000 picoseconds. + Weight::from_parts(30_222_000, 0) .saturating_add(Weight::from_parts(0, 4714)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -100,23 +100,23 @@ impl pallet_bridge_relayers::WeightInfo for WeightInfo< /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn slash_and_deregister() -> Weight { // Proof Size summary in bytes: - // Measured: `263` + // Measured: `300` // Estimated: `4714` - // Minimum execution time: 26_946_000 picoseconds. - Weight::from_parts(27_485_000, 0) + // Minimum execution time: 32_618_000 picoseconds. + Weight::from_parts(33_528_000, 0) .saturating_add(Weight::from_parts(0, 4714)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) } /// Storage: `BridgeRelayers::RelayerRewards` (r:1 w:1) - /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) + /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) fn register_relayer_reward() -> Weight { // Proof Size summary in bytes: - // Measured: `6` - // Estimated: `3538` - // Minimum execution time: 4_658_000 picoseconds. - Weight::from_parts(4_902_000, 0) - .saturating_add(Weight::from_parts(0, 3538)) + // Measured: `42` + // Estimated: `3567` + // Minimum execution time: 7_521_000 picoseconds. + Weight::from_parts(7_844_000, 0) + .saturating_add(Weight::from_parts(0, 3567)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/paritydb_weights.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/paritydb_weights.rs index 25679703831a13b8d1bb7fb7dd4d92fa84b1f255..e0b1985c659c78b66176423f6bcc64d546b39324 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/paritydb_weights.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/paritydb_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/rocksdb_weights.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/rocksdb_weights.rs index 3dd817aa6f137085b0e5fdf2b11b7f50e5c8b002..c6e91b2fcffbf65e3fdce2bc8fbac19a605d00aa 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/rocksdb_weights.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/rocksdb_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_system.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_system.rs index c6c188e323af84d11ba396cb9ab4e97983bac33c..3831111f0977dd33b784a5ba9f4bf2686528f292 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_system.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/snowbridge_pallet_system.rs @@ -253,4 +253,14 @@ impl snowbridge_pallet_system::WeightInfo for WeightInf .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } + + fn register_token() -> Weight { + // Proof Size summary in bytes: + // Measured: `256` + // Estimated: `6044` + // Minimum execution time: 45_000_000 picoseconds. + Weight::from_parts(45_000_000, 6044) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index ba434ff29629ff89a92d6ea663e5fcf8184d102f..16c483a21817080bbeab7461339bbf4c30f3676f 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-07-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-7wrmsoux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-westend-dev"), DB CACHE: 1024 // Executed Command: @@ -68,8 +68,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `208` // Estimated: `6196` - // Minimum execution time: 58_505_000 picoseconds. - Weight::from_parts(60_437_000, 6196) + // Minimum execution time: 70_715_000 picoseconds. + Weight::from_parts(72_211_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -77,8 +77,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 510_000 picoseconds. - Weight::from_parts(569_000, 0) + // Minimum execution time: 968_000 picoseconds. + Weight::from_parts(1_022_000, 0) } // Storage: `PolkadotXcm::Queries` (r:1 w:0) // Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -86,58 +86,58 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `32` // Estimated: `3497` - // Minimum execution time: 5_597_000 picoseconds. - Weight::from_parts(5_884_000, 3497) + // Minimum execution time: 7_718_000 picoseconds. + Weight::from_parts(7_894_000, 3497) .saturating_add(T::DbWeight::get().reads(1)) } pub fn transact() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_320_000 picoseconds. - Weight::from_parts(5_594_000, 0) + // Minimum execution time: 7_662_000 picoseconds. + Weight::from_parts(7_937_000, 0) } pub fn refund_surplus() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_164_000 picoseconds. - Weight::from_parts(1_227_000, 0) + // Minimum execution time: 1_699_000 picoseconds. + Weight::from_parts(1_783_000, 0) } pub fn set_error_handler() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 528_000 picoseconds. - Weight::from_parts(586_000, 0) + // Minimum execution time: 977_000 picoseconds. + Weight::from_parts(1_045_000, 0) } pub fn set_appendix() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 509_000 picoseconds. - Weight::from_parts(571_000, 0) + // Minimum execution time: 971_000 picoseconds. + Weight::from_parts(1_030_000, 0) } pub fn clear_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 511_000 picoseconds. - Weight::from_parts(546_000, 0) + // Minimum execution time: 958_000 picoseconds. + Weight::from_parts(996_000, 0) } pub fn descend_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 560_000 picoseconds. - Weight::from_parts(600_000, 0) + // Minimum execution time: 992_000 picoseconds. + Weight::from_parts(1_056_000, 0) } pub fn clear_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 514_000 picoseconds. - Weight::from_parts(558_000, 0) + // Minimum execution time: 975_000 picoseconds. + Weight::from_parts(1_026_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -159,8 +159,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `208` // Estimated: `6196` - // Minimum execution time: 55_871_000 picoseconds. - Weight::from_parts(57_172_000, 6196) + // Minimum execution time: 67_236_000 picoseconds. + Weight::from_parts(68_712_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -170,8 +170,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `90` // Estimated: `3555` - // Minimum execution time: 8_487_000 picoseconds. - Weight::from_parts(8_800_000, 3555) + // Minimum execution time: 10_890_000 picoseconds. + Weight::from_parts(11_223_000, 3555) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -179,8 +179,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 528_000 picoseconds. - Weight::from_parts(569_000, 0) + // Minimum execution time: 959_000 picoseconds. + Weight::from_parts(1_018_000, 0) } // Storage: `PolkadotXcm::VersionNotifyTargets` (r:1 w:1) // Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -200,8 +200,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 19_803_000 picoseconds. - Weight::from_parts(20_368_000, 3503) + // Minimum execution time: 25_162_000 picoseconds. + Weight::from_parts(25_621_000, 3503) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -211,44 +211,44 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_185_000 picoseconds. - Weight::from_parts(2_332_000, 0) + // Minimum execution time: 2_949_000 picoseconds. + Weight::from_parts(3_119_000, 0) .saturating_add(T::DbWeight::get().writes(1)) } pub fn burn_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 822_000 picoseconds. - Weight::from_parts(928_000, 0) + // Minimum execution time: 1_329_000 picoseconds. + Weight::from_parts(1_410_000, 0) } pub fn expect_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 603_000 picoseconds. - Weight::from_parts(643_000, 0) + // Minimum execution time: 1_063_000 picoseconds. + Weight::from_parts(1_101_000, 0) } pub fn expect_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 503_000 picoseconds. - Weight::from_parts(580_000, 0) + // Minimum execution time: 991_000 picoseconds. + Weight::from_parts(1_041_000, 0) } pub fn expect_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 534_000 picoseconds. - Weight::from_parts(577_000, 0) + // Minimum execution time: 944_000 picoseconds. + Weight::from_parts(998_000, 0) } pub fn expect_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 694_000 picoseconds. - Weight::from_parts(745_000, 0) + // Minimum execution time: 1_100_000 picoseconds. + Weight::from_parts(1_180_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -270,8 +270,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `208` // Estimated: `6196` - // Minimum execution time: 61_083_000 picoseconds. - Weight::from_parts(62_214_000, 6196) + // Minimum execution time: 71_203_000 picoseconds. + Weight::from_parts(73_644_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -279,8 +279,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_261_000 picoseconds. - Weight::from_parts(3_483_000, 0) + // Minimum execution time: 4_018_000 picoseconds. + Weight::from_parts(4_267_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -302,8 +302,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `208` // Estimated: `6196` - // Minimum execution time: 56_270_000 picoseconds. - Weight::from_parts(57_443_000, 6196) + // Minimum execution time: 67_893_000 picoseconds. + Weight::from_parts(69_220_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -311,44 +311,44 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 565_000 picoseconds. - Weight::from_parts(628_000, 0) + // Minimum execution time: 980_000 picoseconds. + Weight::from_parts(1_043_000, 0) } pub fn set_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 496_000 picoseconds. - Weight::from_parts(563_000, 0) + // Minimum execution time: 944_000 picoseconds. + Weight::from_parts(981_000, 0) } pub fn clear_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 518_000 picoseconds. - Weight::from_parts(557_000, 0) + // Minimum execution time: 930_000 picoseconds. + Weight::from_parts(962_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) // Storage: `PolkadotXcm::SupportedVersion` (r:2 w:0) // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) + // Storage: `XcmOverBridgeHubRococo::Bridges` (r:1 w:0) + // Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) // Storage: `BridgeRococoMessages::PalletOperatingMode` (r:1 w:0) // Proof: `BridgeRococoMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) // Storage: `BridgeRococoMessages::OutboundLanes` (r:1 w:1) - // Proof: `BridgeRococoMessages::OutboundLanes` (`max_values`: Some(1), `max_size`: Some(44), added: 539, mode: `MaxEncodedLen`) - // Storage: `BridgeRococoMessages::OutboundLanesCongestedSignals` (r:1 w:0) - // Proof: `BridgeRococoMessages::OutboundLanesCongestedSignals` (`max_values`: Some(1), `max_size`: Some(21), added: 516, mode: `MaxEncodedLen`) + // Proof: `BridgeRococoMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) // Storage: `BridgeRococoMessages::OutboundMessages` (r:0 w:1) - // Proof: `BridgeRococoMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) + // Proof: `BridgeRococoMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) /// The range of component `x` is `[1, 1000]`. pub fn export_message(x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `225` - // Estimated: `6165` - // Minimum execution time: 36_288_000 picoseconds. - Weight::from_parts(37_707_751, 6165) - // Standard Error: 124 - .saturating_add(Weight::from_parts(51_290, 0).saturating_mul(x.into())) + // Measured: `552` + // Estimated: `6492` + // Minimum execution time: 56_762_000 picoseconds. + Weight::from_parts(58_320_046, 6492) + // Standard Error: 162 + .saturating_add(Weight::from_parts(51_730, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -356,14 +356,14 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 485_000 picoseconds. - Weight::from_parts(540_000, 0) + // Minimum execution time: 971_000 picoseconds. + Weight::from_parts(1_018_000, 0) } pub fn unpaid_execution() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 542_000 picoseconds. - Weight::from_parts(586_000, 0) + // Minimum execution time: 979_000 picoseconds. + Weight::from_parts(1_026_000, 0) } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs index 81705ee2dc94c9095fba3d04dc17ff1c64285185..ae31ca4cedf2336be6d103c73cc9067989928ae5 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs @@ -17,7 +17,7 @@ use super::{ AccountId, AllPalletsWithSystem, Balances, BaseDeliveryFee, FeeAssetId, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, - TransactionByteFee, WeightToFee, XcmpQueue, + TransactionByteFee, WeightToFee, XcmOverBridgeHubRococo, XcmpQueue, }; use frame_support::{ parameter_types, @@ -43,11 +43,12 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, - DenyReserveTransferToRelayChain, DenyThenTry, EnsureXcmOrigin, FrameTransactionalProcessor, - FungibleAdapter, HandleFee, IsConcrete, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, - SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, - TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, + DenyReserveTransferToRelayChain, DenyThenTry, DescribeAllTerminal, DescribeFamily, + EnsureXcmOrigin, FrameTransactionalProcessor, FungibleAdapter, HandleFee, HashedDescription, + IsConcrete, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, + UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, }; use xcm_executor::{ traits::{FeeManager, FeeReason, FeeReason::Export}, @@ -76,6 +77,8 @@ pub type LocationToAccountId = ( SiblingParachainConvertsVia, // Straight up local `AccountId32` origins just alias directly to `AccountId`. AccountId32Aliases, + // Foreign locations alias into accounts according to a hash of their standard description. + HashedDescription>, ); /// Means for transacting the native currency on this chain. @@ -212,10 +215,8 @@ impl xcm_executor::Config for XcmConfig { SendXcmFeeToAccount, ), >; - type MessageExporter = ( - crate::bridge_to_rococo_config::ToBridgeHubRococoHaulBlobExporter, - crate::bridge_to_ethereum_config::SnowbridgeExporter, - ); + type MessageExporter = + (XcmOverBridgeHubRococo, crate::bridge_to_ethereum_config::SnowbridgeExporter); type UniversalAliases = Nothing; type CallDispatcher = RuntimeCall; type SafeCallFilter = Everything; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/snowbridge.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/snowbridge.rs index 46a0fa7a664bb186d5e6f4f781a5e97f0ebc923f..c5f3871c07905e079e72dea86f521b65e1904196 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/snowbridge.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/snowbridge.rs @@ -86,11 +86,11 @@ pub fn transfer_token_to_ethereum_fee_not_enough() { collator_session_keys(), BRIDGE_HUB_WESTEND_PARACHAIN_ID, ASSET_HUB_WESTEND_PARACHAIN_ID, - DefaultBridgeHubEthereumBaseFee::get() + 10_000_000_000, + DefaultBridgeHubEthereumBaseFee::get() + 20_000_000_000, H160::random(), H160::random(), // fee not enough - 10_000_000_000, + 20_000_000_000, NotHoldingFees, ) } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs index a66c0f84240c80cef33d21f35ee505db3c4fe292..c5a9b8c53a93e694e6efb1df7f03cc401b6e302c 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 @@ -16,24 +16,30 @@ #![cfg(test)] +use bp_messages::LegacyLaneId; use bp_polkadot_core::Signature; -use bridge_common_config::{DeliveryRewardInBalance, RequiredStakeForStakeAndSlash}; +use bridge_common_config::{ + DeliveryRewardInBalance, RelayersForLegacyLaneIdsMessagesInstance, + RequiredStakeForStakeAndSlash, +}; use bridge_hub_test_utils::{test_cases::from_parachain, SlotDurations}; use bridge_hub_westend_runtime::{ bridge_common_config, bridge_to_rococo_config, - xcm_config::{RelayNetwork, WestendLocation, XcmConfig}, + bridge_to_rococo_config::RococoGlobalConsensusNetwork, + xcm_config::{LocationToAccountId, RelayNetwork, WestendLocation, XcmConfig}, AllPalletsWithoutSystem, BridgeRejectObsoleteHeadersAndMessages, Executive, ExistentialDeposit, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, SessionKeys, SignedExtra, TransactionPayment, UncheckedExtrinsic, }; use bridge_to_rococo_config::{ BridgeGrandpaRococoInstance, BridgeHubRococoLocation, BridgeParachainRococoInstance, - WithBridgeHubRococoMessagesInstance, XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO, + WithBridgeHubRococoMessagesInstance, XcmOverBridgeHubRococoInstance, }; use codec::{Decode, Encode}; use frame_support::{dispatch::GetDispatchInfo, parameter_types, traits::ConstU8}; use parachains_common::{AccountId, AuraId, Balance}; use sp_consensus_aura::SlotDuration; +use sp_core::crypto::Ss58Codec; use sp_keyring::AccountKeyring::Alice; use sp_runtime::{ generic::{Era, SignedPayload}, @@ -41,9 +47,20 @@ use sp_runtime::{ }; use testnet_parachains_constants::westend::{consensus::*, fee::WeightToFee}; use xcm::latest::prelude::*; +use xcm_runtime_apis::conversions::LocationToAccountHelper; + +// Random para id of sibling chain used in tests. +pub const SIBLING_PARACHAIN_ID: u32 = 2053; +// Random para id of sibling chain used in tests. +pub const SIBLING_SYSTEM_PARACHAIN_ID: u32 = 1008; +// Random para id of bridged chain from different global consensus used in tests. +pub const BRIDGED_LOCATION_PARACHAIN_ID: u32 = 1075; -// Para id of sibling chain used in tests. -pub const SIBLING_PARACHAIN_ID: u32 = 1000; +parameter_types! { + pub SiblingParachainLocation: Location = Location::new(1, [Parachain(SIBLING_PARACHAIN_ID)]); + pub SiblingSystemParachainLocation: Location = Location::new(1, [Parachain(SIBLING_SYSTEM_PARACHAIN_ID)]); + pub BridgedUniversalLocation: InteriorLocation = [GlobalConsensus(RococoGlobalConsensusNetwork::get()), Parachain(BRIDGED_LOCATION_PARACHAIN_ID)].into(); +} // Runtime from tests PoV type RuntimeTestsAdapter = from_parachain::WithRemoteParachainHelperAdapter< @@ -52,6 +69,7 @@ type RuntimeTestsAdapter = from_parachain::WithRemoteParachainHelperAdapter< BridgeGrandpaRococoInstance, BridgeParachainRococoInstance, WithBridgeHubRococoMessagesInstance, + RelayersForLegacyLaneIdsMessagesInstance, >; parameter_types! { @@ -211,14 +229,28 @@ fn handle_export_message_from_system_parachain_add_to_outbound_queue_works() { _ => None, } }), - || ExportMessage { network: Rococo, destination: [Parachain(bridge_to_rococo_config::AssetHubRococoParaId::get().into())].into(), xcm: Xcm(vec![]) }, + || ExportMessage { network: RococoGlobalConsensusNetwork::get(), destination: [Parachain(BRIDGED_LOCATION_PARACHAIN_ID)].into(), xcm: Xcm(vec![]) }, Some((WestendLocation::get(), ExistentialDeposit::get()).into()), // value should be >= than value generated by `can_calculate_weight_for_paid_export_message_with_reserve_transfer` Some((WestendLocation::get(), bp_bridge_hub_westend::BridgeHubWestendBaseXcmFeeInWnds::get()).into()), || { PolkadotXcm::force_xcm_version(RuntimeOrigin::root(), Box::new(BridgeHubRococoLocation::get()), XCM_VERSION).expect("version saved!"); - XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO + // we need to create lane between sibling parachain and remote destination + bridge_hub_test_utils::ensure_opened_bridge::< + Runtime, + XcmOverBridgeHubRococoInstance, + LocationToAccountId, + WestendLocation, + >( + SiblingParachainLocation::get(), + BridgedUniversalLocation::get(), + |locations, fee| { + bridge_hub_test_utils::open_bridge_with_storage::< + Runtime, XcmOverBridgeHubRococoInstance + >(locations, fee, LegacyLaneId([0, 0, 0, 1])) + } + ).1 }, ) } @@ -251,7 +283,7 @@ fn message_dispatch_routing_works() { _ => None, } }), - || XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO, + || (), ) } @@ -264,8 +296,61 @@ fn relayed_incoming_message_works() { bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, SIBLING_PARACHAIN_ID, Westend, - || XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO, + || { + // we need to create lane between sibling parachain and remote destination + bridge_hub_test_utils::ensure_opened_bridge::< + Runtime, + XcmOverBridgeHubRococoInstance, + LocationToAccountId, + WestendLocation, + >( + SiblingParachainLocation::get(), + BridgedUniversalLocation::get(), + |locations, fee| { + bridge_hub_test_utils::open_bridge_with_storage::< + Runtime, + XcmOverBridgeHubRococoInstance, + >(locations, fee, LegacyLaneId([0, 0, 0, 1])) + }, + ) + .1 + }, construct_and_apply_extrinsic, + true, + ) +} + +#[test] +fn free_relay_extrinsic_works() { + // from Rococo + from_parachain::free_relay_extrinsic_works::( + collator_session_keys(), + slot_durations(), + bp_bridge_hub_westend::BRIDGE_HUB_WESTEND_PARACHAIN_ID, + bp_bridge_hub_rococo::BRIDGE_HUB_ROCOCO_PARACHAIN_ID, + SIBLING_PARACHAIN_ID, + Westend, + || { + // we need to create lane between sibling parachain and remote destination + bridge_hub_test_utils::ensure_opened_bridge::< + Runtime, + XcmOverBridgeHubRococoInstance, + LocationToAccountId, + WestendLocation, + >( + SiblingParachainLocation::get(), + BridgedUniversalLocation::get(), + |locations, fee| { + bridge_hub_test_utils::open_bridge_with_storage::< + Runtime, + XcmOverBridgeHubRococoInstance, + >(locations, fee, LegacyLaneId([0, 0, 0, 1])) + }, + ) + .1 + }, + construct_and_apply_extrinsic, + true, ) } @@ -327,3 +412,112 @@ pub fn can_calculate_fee_for_standalone_message_confirmation_transaction() { ), ) } + +#[test] +fn location_conversion_works() { + // the purpose of hardcoded values is to catch an unintended location conversion logic change. + struct TestCase { + description: &'static str, + location: Location, + expected_account_id_str: &'static str, + } + + let test_cases = vec![ + // DescribeTerminus + TestCase { + description: "DescribeTerminus Parent", + location: Location::new(1, Here), + expected_account_id_str: "5Dt6dpkWPwLaH4BBCKJwjiWrFVAGyYk3tLUabvyn4v7KtESG", + }, + TestCase { + description: "DescribeTerminus Sibling", + location: Location::new(1, [Parachain(1111)]), + expected_account_id_str: "5Eg2fnssmmJnF3z1iZ1NouAuzciDaaDQH7qURAy3w15jULDk", + }, + // DescribePalletTerminal + TestCase { + description: "DescribePalletTerminal Parent", + location: Location::new(1, [PalletInstance(50)]), + expected_account_id_str: "5CnwemvaAXkWFVwibiCvf2EjqwiqBi29S5cLLydZLEaEw6jZ", + }, + TestCase { + description: "DescribePalletTerminal Sibling", + location: Location::new(1, [Parachain(1111), PalletInstance(50)]), + expected_account_id_str: "5GFBgPjpEQPdaxEnFirUoa51u5erVx84twYxJVuBRAT2UP2g", + }, + // DescribeAccountId32Terminal + TestCase { + description: "DescribeAccountId32Terminal Parent", + location: Location::new( + 1, + [Junction::AccountId32 { network: None, id: AccountId::from(Alice).into() }], + ), + expected_account_id_str: "5EueAXd4h8u75nSbFdDJbC29cmi4Uo1YJssqEL9idvindxFL", + }, + TestCase { + description: "DescribeAccountId32Terminal Sibling", + location: Location::new( + 1, + [ + Parachain(1111), + Junction::AccountId32 { network: None, id: AccountId::from(Alice).into() }, + ], + ), + expected_account_id_str: "5Dmbuiq48fU4iW58FKYqoGbbfxFHjbAeGLMtjFg6NNCw3ssr", + }, + // DescribeAccountKey20Terminal + TestCase { + description: "DescribeAccountKey20Terminal Parent", + location: Location::new(1, [AccountKey20 { network: None, key: [0u8; 20] }]), + expected_account_id_str: "5F5Ec11567pa919wJkX6VHtv2ZXS5W698YCW35EdEbrg14cg", + }, + TestCase { + description: "DescribeAccountKey20Terminal Sibling", + location: Location::new( + 1, + [Parachain(1111), AccountKey20 { network: None, key: [0u8; 20] }], + ), + expected_account_id_str: "5CB2FbUds2qvcJNhDiTbRZwiS3trAy6ydFGMSVutmYijpPAg", + }, + // DescribeTreasuryVoiceTerminal + TestCase { + description: "DescribeTreasuryVoiceTerminal Parent", + location: Location::new(1, [Plurality { id: BodyId::Treasury, part: BodyPart::Voice }]), + expected_account_id_str: "5CUjnE2vgcUCuhxPwFoQ5r7p1DkhujgvMNDHaF2bLqRp4D5F", + }, + TestCase { + description: "DescribeTreasuryVoiceTerminal Sibling", + location: Location::new( + 1, + [Parachain(1111), Plurality { id: BodyId::Treasury, part: BodyPart::Voice }], + ), + expected_account_id_str: "5G6TDwaVgbWmhqRUKjBhRRnH4ry9L9cjRymUEmiRsLbSE4gB", + }, + // DescribeBodyTerminal + TestCase { + description: "DescribeBodyTerminal Parent", + location: Location::new(1, [Plurality { id: BodyId::Unit, part: BodyPart::Voice }]), + expected_account_id_str: "5EBRMTBkDisEXsaN283SRbzx9Xf2PXwUxxFCJohSGo4jYe6B", + }, + TestCase { + description: "DescribeBodyTerminal Sibling", + location: Location::new( + 1, + [Parachain(1111), Plurality { id: BodyId::Unit, part: BodyPart::Voice }], + ), + expected_account_id_str: "5DBoExvojy8tYnHgLL97phNH975CyT45PWTZEeGoBZfAyRMH", + }, + ]; + + for tc in test_cases { + let expected = + AccountId::from_string(tc.expected_account_id_str).expect("Invalid AccountId string"); + + let got = LocationToAccountHelper::::convert_location( + tc.location.into(), + ) + .unwrap(); + + assert_eq!(got, expected, "{}", tc.description); + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/common/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/common/Cargo.toml index 3ae43075000ba2ccd34c9efe1edfc91681201944..9cb24a2b2820ea50dd3fab96b11fb0a198ee1bc0 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/common/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/common/Cargo.toml @@ -12,6 +12,7 @@ scale-info = { features = ["derive"], workspace = true } frame-support = { workspace = true } sp-core = { workspace = true } sp-runtime = { workspace = true } +sp-std = { workspace = true } cumulus-primitives-core = { workspace = true } xcm = { workspace = true } pallet-message-queue = { workspace = true } @@ -28,6 +29,7 @@ std = [ "snowbridge-core/std", "sp-core/std", "sp-runtime/std", + "sp-std/std", "xcm/std", ] diff --git a/cumulus/parachains/runtimes/bridge-hubs/common/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/common/src/lib.rs index aac6eb036526af4414b6f46b9cf8874a899072bb..b806b8cdb22db5386baf53b302cc06902c06f215 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/common/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/common/src/lib.rs @@ -16,6 +16,7 @@ pub mod digest_item; pub mod message_queue; +pub mod xcm_version; pub use digest_item::CustomDigestItem; pub use message_queue::{AggregateMessageOrigin, BridgeHubMessageRouter}; diff --git a/cumulus/parachains/runtimes/bridge-hubs/common/src/xcm_version.rs b/cumulus/parachains/runtimes/bridge-hubs/common/src/xcm_version.rs new file mode 100644 index 0000000000000000000000000000000000000000..72e6c697e44ab42d48fbd6da7e71e2d2e3d65dad --- /dev/null +++ b/cumulus/parachains/runtimes/bridge-hubs/common/src/xcm_version.rs @@ -0,0 +1,44 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Custom XCM implementation. + +use frame_support::traits::Get; +use xcm::{ + latest::prelude::*, + prelude::{GetVersion, XcmVersion}, +}; + +/// Adapter for the implementation of `GetVersion`, which attempts to find the minimal +/// configured XCM version between the destination `dest` and the bridge hub location provided as +/// `Get`. +pub struct XcmVersionOfDestAndRemoteBridge( + sp_std::marker::PhantomData<(Version, RemoteBridge)>, +); +impl> GetVersion + for XcmVersionOfDestAndRemoteBridge +{ + fn get_version_for(dest: &Location) -> Option { + let dest_version = Version::get_version_for(dest); + let bridge_hub_version = Version::get_version_for(&RemoteBridge::get()); + + match (dest_version, bridge_hub_version) { + (Some(dv), Some(bhv)) => Some(sp_std::cmp::min(dv, bhv)), + (Some(dv), None) => Some(dv), + (None, Some(bhv)) => Some(bhv), + (None, None) => None, + } + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml index 44a8646142d6c38d265c0b9cd518b1b7ddd192e3..915b3090092f3c16f126ca6f86e700bd8e6ebdc1 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml @@ -37,18 +37,22 @@ parachains-runtimes-test-utils = { workspace = true } xcm = { workspace = true } xcm-builder = { workspace = true } xcm-executor = { workspace = true } +pallet-xcm = { workspace = true } # Bridges bp-header-chain = { workspace = true } bp-messages = { workspace = true } +bp-parachains = { workspace = true } bp-polkadot-core = { workspace = true } bp-relayers = { workspace = true } bp-runtime = { workspace = true } bp-test-utils = { workspace = true } +bp-xcm-bridge-hub = { workspace = true } pallet-bridge-grandpa = { workspace = true } pallet-bridge-parachains = { workspace = true } pallet-bridge-messages = { features = ["test-helpers"], workspace = true } pallet-bridge-relayers = { workspace = true } +pallet-xcm-bridge-hub = { workspace = true } bridge-runtime-common = { workspace = true } [features] @@ -57,10 +61,12 @@ std = [ "asset-test-utils/std", "bp-header-chain/std", "bp-messages/std", + "bp-parachains/std", "bp-polkadot-core/std", "bp-relayers/std", "bp-runtime/std", "bp-test-utils/std", + "bp-xcm-bridge-hub/std", "bridge-runtime-common/std", "codec/std", "cumulus-pallet-parachain-system/std", @@ -75,6 +81,8 @@ std = [ "pallet-bridge-relayers/std", "pallet-timestamp/std", "pallet-utility/std", + "pallet-xcm-bridge-hub/std", + "pallet-xcm/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 0b3463f0df974dcc9ce46c1d811b363d5011bbb4..bc28df0eb829cc02ba455b363ce6bea51d702950 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/lib.rs @@ -24,6 +24,9 @@ extern crate alloc; pub use bp_test_utils::test_header; pub use parachains_runtimes_test_utils::*; use sp_runtime::Perbill; +pub use test_cases::helpers::{ + ensure_opened_bridge, open_bridge_with_extrinsic, open_bridge_with_storage, +}; /// A helper function for comparing the actual value of a fee constant with its estimated value. The /// estimated value can be overestimated (`overestimate_in_percent`), and if the difference to the diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_grandpa_chain.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_grandpa_chain.rs index f2f0ccecba132b9199f5ad75e124932fd1f5e320..320f3030b60a63e362bf88f467ac09afb78ff64b 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 @@ -24,12 +24,12 @@ use crate::{ use alloc::{boxed::Box, vec}; use bp_header_chain::ChainWithGrandpa; -use bp_messages::{LaneId, UnrewardedRelayersState}; +use bp_messages::UnrewardedRelayersState; use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; -use bridge_runtime_common::messages_xcm_extension::XcmAsPlainPayload; +use bp_xcm_bridge_hub::XcmAsPlainPayload; use frame_support::traits::{OnFinalize, OnInitialize}; use frame_system::pallet_prelude::BlockNumberFor; -use pallet_bridge_messages::{BridgedChainOf, ThisChainOf}; +use pallet_bridge_messages::{BridgedChainOf, LaneIdOf, ThisChainOf}; use parachains_runtimes_test_utils::{ AccountIdOf, BasicParachainRuntime, CollatorSessionKeys, RuntimeCallOf, SlotDurations, }; @@ -50,7 +50,7 @@ pub trait WithRemoteGrandpaChainHelper { Self::MPI, InboundPayload = XcmAsPlainPayload, OutboundPayload = XcmAsPlainPayload, - > + pallet_bridge_relayers::Config; + > + pallet_bridge_relayers::Config>; /// All pallets of this chain, excluding system pallet. type AllPalletsWithoutSystem: OnInitialize> + OnFinalize>; @@ -58,15 +58,18 @@ pub trait WithRemoteGrandpaChainHelper { type GPI: 'static; /// Instance of the `pallet-bridge-messages`, used to bridge with remote GRANDPA chain. type MPI: 'static; + /// Instance of the `pallet-bridge-relayers`, used to collect rewards from messages `MPI` + /// instance. + type RPI: 'static; } /// Adapter struct that implements [`WithRemoteGrandpaChainHelper`]. -pub struct WithRemoteGrandpaChainHelperAdapter( - core::marker::PhantomData<(Runtime, AllPalletsWithoutSystem, GPI, MPI)>, +pub struct WithRemoteGrandpaChainHelperAdapter( + core::marker::PhantomData<(Runtime, AllPalletsWithoutSystem, GPI, MPI, RPI)>, ); -impl WithRemoteGrandpaChainHelper - for WithRemoteGrandpaChainHelperAdapter +impl WithRemoteGrandpaChainHelper + for WithRemoteGrandpaChainHelperAdapter where Runtime: BasicParachainRuntime + cumulus_pallet_xcmp_queue::Config @@ -75,16 +78,18 @@ where MPI, InboundPayload = XcmAsPlainPayload, OutboundPayload = XcmAsPlainPayload, - > + pallet_bridge_relayers::Config, + > + pallet_bridge_relayers::Config>, AllPalletsWithoutSystem: OnInitialize> + OnFinalize>, GPI: 'static, MPI: 'static, + RPI: 'static, { type Runtime = Runtime; type AllPalletsWithoutSystem = AllPalletsWithoutSystem; type GPI = GPI; type MPI = MPI; + type RPI = RPI; } /// Test-case makes sure that Runtime can dispatch XCM messages submitted by relayer, @@ -96,11 +101,12 @@ pub fn relayed_incoming_message_works( runtime_para_id: u32, sibling_parachain_id: u32, local_relay_chain_id: NetworkId, - prepare_configuration: impl Fn() -> LaneId, + prepare_configuration: impl Fn() -> LaneIdOf, construct_and_apply_extrinsic: fn( sp_keyring::AccountKeyring, RuntimeCallOf, ) -> sp_runtime::DispatchOutcome, + expect_rewards: bool, ) where RuntimeHelper: WithRemoteGrandpaChainHelper, AccountIdOf: From, @@ -140,6 +146,7 @@ pub fn relayed_incoming_message_works( test_data::from_grandpa_chain::make_complex_relayer_delivery_proofs::< BridgedChainOf, ThisChainOf, + LaneIdOf, >( lane_id, xcm.into(), @@ -172,14 +179,18 @@ pub fn relayed_incoming_message_works( lane_id, 1, ), - helpers::VerifyRelayerRewarded::::expect_relayer_reward( - relayer_id_at_this_chain, - RewardsAccountParams::new( - lane_id, - bridged_chain_id, - RewardsAccountOwner::ThisChain, - ), - ), + if expect_rewards { + helpers::VerifyRelayerRewarded::::expect_relayer_reward( + relayer_id_at_this_chain, + RewardsAccountParams::new( + lane_id, + bridged_chain_id, + RewardsAccountOwner::ThisChain, + ), + ) + } else { + Box::new(()) + } )), ), ] @@ -197,11 +208,12 @@ pub fn free_relay_extrinsic_works( runtime_para_id: u32, sibling_parachain_id: u32, local_relay_chain_id: NetworkId, - prepare_configuration: impl Fn() -> LaneId, + prepare_configuration: impl Fn() -> LaneIdOf, construct_and_apply_extrinsic: fn( sp_keyring::AccountKeyring, RuntimeCallOf, ) -> sp_runtime::DispatchOutcome, + expect_rewards: bool, ) where RuntimeHelper: WithRemoteGrandpaChainHelper, RuntimeHelper::Runtime: pallet_balances::Config, @@ -263,6 +275,7 @@ pub fn free_relay_extrinsic_works( test_data::from_grandpa_chain::make_complex_relayer_delivery_proofs::< BridgedChainOf, ThisChainOf, + LaneIdOf, >( lane_id, xcm.into(), @@ -301,14 +314,18 @@ pub fn free_relay_extrinsic_works( lane_id, 1, ), - helpers::VerifyRelayerRewarded::::expect_relayer_reward( - relayer_id_at_this_chain, - RewardsAccountParams::new( - lane_id, - bridged_chain_id, - RewardsAccountOwner::ThisChain, - ), - ), + if expect_rewards { + helpers::VerifyRelayerRewarded::::expect_relayer_reward( + relayer_id_at_this_chain, + RewardsAccountParams::new( + lane_id, + bridged_chain_id, + RewardsAccountOwner::ThisChain, + ), + ) + } else { + Box::new(()) + } )), ), ] @@ -325,7 +342,7 @@ pub fn complex_relay_extrinsic_works( runtime_para_id: u32, sibling_parachain_id: u32, local_relay_chain_id: NetworkId, - prepare_configuration: impl Fn() -> LaneId, + prepare_configuration: impl Fn() -> LaneIdOf, construct_and_apply_extrinsic: fn( sp_keyring::AccountKeyring, RuntimeCallOf, @@ -372,6 +389,7 @@ pub fn complex_relay_extrinsic_works( test_data::from_grandpa_chain::make_complex_relayer_delivery_proofs::< BridgedChainOf, ThisChainOf, + LaneIdOf, >( lane_id, xcm.into(), @@ -382,9 +400,10 @@ pub fn complex_relay_extrinsic_works( ); let relay_chain_header_hash = relay_chain_header.hash(); - vec![( - pallet_utility::Call::::batch_all { - calls: vec![ + vec![ + ( + pallet_utility::Call::::batch_all { + calls: vec![ BridgeGrandpaCall::::submit_finality_proof { finality_target: Box::new(relay_chain_header), justification: grandpa_justification, @@ -396,27 +415,33 @@ pub fn complex_relay_extrinsic_works( dispatch_weight: Weight::from_parts(1000000000, 0), }.into(), ], - } - .into(), - Box::new(( - helpers::VerifySubmitGrandpaFinalityProofOutcome::< - RuntimeHelper::Runtime, - RuntimeHelper::GPI, - >::expect_best_header_hash(relay_chain_header_hash), - helpers::VerifySubmitMessagesProofOutcome::< - RuntimeHelper::Runtime, - RuntimeHelper::MPI, - >::expect_last_delivered_nonce(lane_id, 1), - helpers::VerifyRelayerRewarded::::expect_relayer_reward( - relayer_id_at_this_chain, - RewardsAccountParams::new( - lane_id, - bridged_chain_id, - RewardsAccountOwner::ThisChain, + } + .into(), + Box::new( + ( + helpers::VerifySubmitGrandpaFinalityProofOutcome::< + RuntimeHelper::Runtime, + RuntimeHelper::GPI, + >::expect_best_header_hash(relay_chain_header_hash), + helpers::VerifySubmitMessagesProofOutcome::< + RuntimeHelper::Runtime, + RuntimeHelper::MPI, + >::expect_last_delivered_nonce(lane_id, 1), + helpers::VerifyRelayerRewarded::< + RuntimeHelper::Runtime, + RuntimeHelper::RPI, + >::expect_relayer_reward( + relayer_id_at_this_chain, + RewardsAccountParams::new( + lane_id, + bridged_chain_id, + RewardsAccountOwner::ThisChain, + ), + ), ), ), - )), - )] + ), + ] }, ); } @@ -446,8 +471,9 @@ where test_data::from_grandpa_chain::make_complex_relayer_delivery_proofs::< BridgedChainOf, ThisChainOf, + LaneIdOf, >( - LaneId::default(), + LaneIdOf::::default(), vec![Instruction::<()>::ClearOrigin; 1_024].into(), 1, [GlobalConsensus(Polkadot), Parachain(1_000)].into(), @@ -502,8 +528,9 @@ where BridgedChainOf, ThisChainOf, (), + LaneIdOf, >( - LaneId::default(), + LaneIdOf::::default(), 1u32.into(), AccountId32::from(Alice.public()).into(), unrewarded_relayers.clone(), @@ -550,8 +577,9 @@ where test_data::from_grandpa_chain::make_complex_relayer_delivery_proofs::< BridgedChainOf, ThisChainOf, + LaneIdOf, >( - LaneId::default(), + LaneIdOf::::default(), vec![Instruction::<()>::ClearOrigin; 1_024].into(), 1, [GlobalConsensus(Polkadot), Parachain(1_000)].into(), @@ -602,8 +630,9 @@ where BridgedChainOf, ThisChainOf, (), + LaneIdOf, >( - LaneId::default(), + LaneIdOf::::default(), 1u32.into(), AccountId32::from(Alice.public()).into(), unrewarded_relayers.clone(), diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_parachain.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_parachain.rs index 0988528c3f2a0ae867d188d745a3b1e923ab0060..1da901e0bcdf9e8da142fba80ad07d053f3fabe2 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 @@ -24,14 +24,14 @@ use crate::{ use alloc::{boxed::Box, vec}; use bp_header_chain::ChainWithGrandpa; -use bp_messages::{LaneId, UnrewardedRelayersState}; +use bp_messages::UnrewardedRelayersState; use bp_polkadot_core::parachains::ParaHash; use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; use bp_runtime::{Chain, Parachain}; -use bridge_runtime_common::messages_xcm_extension::XcmAsPlainPayload; +use bp_xcm_bridge_hub::XcmAsPlainPayload; use frame_support::traits::{OnFinalize, OnInitialize}; use frame_system::pallet_prelude::BlockNumberFor; -use pallet_bridge_messages::{BridgedChainOf, ThisChainOf}; +use pallet_bridge_messages::{BridgedChainOf, LaneIdOf, ThisChainOf}; use parachains_runtimes_test_utils::{ AccountIdOf, BasicParachainRuntime, CollatorSessionKeys, RuntimeCallOf, SlotDurations, }; @@ -53,7 +53,7 @@ pub trait WithRemoteParachainHelper { Self::MPI, InboundPayload = XcmAsPlainPayload, OutboundPayload = XcmAsPlainPayload, - > + pallet_bridge_relayers::Config; + > + pallet_bridge_relayers::Config>; /// All pallets of this chain, excluding system pallet. type AllPalletsWithoutSystem: OnInitialize> + OnFinalize>; @@ -63,15 +63,18 @@ pub trait WithRemoteParachainHelper { type PPI: 'static; /// Instance of the `pallet-bridge-messages`, used to bridge with remote parachain. type MPI: 'static; + /// Instance of the `pallet-bridge-relayers`, used to collect rewards from messages `MPI` + /// instance. + type RPI: 'static; } /// Adapter struct that implements `WithRemoteParachainHelper`. -pub struct WithRemoteParachainHelperAdapter( - core::marker::PhantomData<(Runtime, AllPalletsWithoutSystem, GPI, PPI, MPI)>, +pub struct WithRemoteParachainHelperAdapter( + core::marker::PhantomData<(Runtime, AllPalletsWithoutSystem, GPI, PPI, MPI, RPI)>, ); -impl WithRemoteParachainHelper - for WithRemoteParachainHelperAdapter +impl WithRemoteParachainHelper + for WithRemoteParachainHelperAdapter where Runtime: BasicParachainRuntime + cumulus_pallet_xcmp_queue::Config @@ -81,19 +84,20 @@ where MPI, InboundPayload = XcmAsPlainPayload, OutboundPayload = XcmAsPlainPayload, - > + pallet_bridge_relayers::Config, + > + pallet_bridge_relayers::Config>, AllPalletsWithoutSystem: OnInitialize> + OnFinalize>, GPI: 'static, PPI: 'static, MPI: 'static, - // MB: MessageBridge, + RPI: 'static, { type Runtime = Runtime; type AllPalletsWithoutSystem = AllPalletsWithoutSystem; type GPI = GPI; type PPI = PPI; type MPI = MPI; + type RPI = RPI; } /// Test-case makes sure that Runtime can dispatch XCM messages submitted by relayer, @@ -106,11 +110,12 @@ pub fn relayed_incoming_message_works( bridged_para_id: u32, sibling_parachain_id: u32, local_relay_chain_id: NetworkId, - prepare_configuration: impl Fn() -> LaneId, + prepare_configuration: impl Fn() -> LaneIdOf, construct_and_apply_extrinsic: fn( sp_keyring::AccountKeyring, ::RuntimeCall, ) -> sp_runtime::DispatchOutcome, + expect_rewards: bool, ) where RuntimeHelper: WithRemoteParachainHelper, AccountIdOf: From, @@ -161,6 +166,7 @@ pub fn relayed_incoming_message_works( >::BridgedChain, BridgedChainOf, ThisChainOf, + LaneIdOf, >( lane_id, xcm.into(), @@ -208,14 +214,18 @@ pub fn relayed_incoming_message_works( lane_id, 1, ), - helpers::VerifyRelayerRewarded::::expect_relayer_reward( - relayer_id_at_this_chain, - RewardsAccountParams::new( - lane_id, - bridged_chain_id, - RewardsAccountOwner::ThisChain, - ), - ), + if expect_rewards { + helpers::VerifyRelayerRewarded::::expect_relayer_reward( + relayer_id_at_this_chain, + RewardsAccountParams::new( + lane_id, + bridged_chain_id, + RewardsAccountOwner::ThisChain, + ), + ) + } else { + Box::new(()) + } )), ), ] @@ -234,11 +244,12 @@ pub fn free_relay_extrinsic_works( bridged_para_id: u32, sibling_parachain_id: u32, local_relay_chain_id: NetworkId, - prepare_configuration: impl Fn() -> LaneId, + prepare_configuration: impl Fn() -> LaneIdOf, construct_and_apply_extrinsic: fn( sp_keyring::AccountKeyring, ::RuntimeCall, ) -> sp_runtime::DispatchOutcome, + expect_rewards: bool, ) where RuntimeHelper: WithRemoteParachainHelper, RuntimeHelper::Runtime: pallet_balances::Config, @@ -312,6 +323,7 @@ pub fn free_relay_extrinsic_works( >::BridgedChain, BridgedChainOf, ThisChainOf, + LaneIdOf, >( lane_id, xcm.into(), @@ -353,10 +365,10 @@ pub fn free_relay_extrinsic_works( bridged_para_id, parachain_head_hash, ), - /*helpers::VerifyRelayerBalance::::expect_relayer_balance( + helpers::VerifyRelayerBalance::::expect_relayer_balance( relayer_id_at_this_chain.clone(), initial_relayer_balance, - ),*/ + ), )), ), ( @@ -371,14 +383,18 @@ pub fn free_relay_extrinsic_works( lane_id, 1, ), - helpers::VerifyRelayerRewarded::::expect_relayer_reward( - relayer_id_at_this_chain, - RewardsAccountParams::new( - lane_id, - bridged_chain_id, - RewardsAccountOwner::ThisChain, - ), - ), + if expect_rewards { + helpers::VerifyRelayerRewarded::::expect_relayer_reward( + relayer_id_at_this_chain, + RewardsAccountParams::new( + lane_id, + bridged_chain_id, + RewardsAccountOwner::ThisChain, + ), + ) + } else { + Box::new(()) + } )), ), ] @@ -396,7 +412,7 @@ pub fn complex_relay_extrinsic_works( bridged_para_id: u32, sibling_parachain_id: u32, local_relay_chain_id: NetworkId, - prepare_configuration: impl Fn() -> LaneId, + prepare_configuration: impl Fn() -> LaneIdOf, construct_and_apply_extrinsic: fn( sp_keyring::AccountKeyring, ::RuntimeCall, @@ -454,6 +470,7 @@ pub fn complex_relay_extrinsic_works( >::BridgedChain, BridgedChainOf, ThisChainOf, + LaneIdOf, >( lane_id, xcm.into(), @@ -468,9 +485,10 @@ pub fn complex_relay_extrinsic_works( let parachain_head_hash = parachain_head.hash(); let relay_chain_header_hash = relay_chain_header.hash(); let relay_chain_header_number = *relay_chain_header.number(); - vec![( - pallet_utility::Call::::batch_all { - calls: vec![ + vec![ + ( + pallet_utility::Call::::batch_all { + calls: vec![ BridgeGrandpaCall::::submit_finality_proof { finality_target: Box::new(relay_chain_header), justification: grandpa_justification, @@ -487,31 +505,37 @@ pub fn complex_relay_extrinsic_works( dispatch_weight: Weight::from_parts(1000000000, 0), }.into(), ], - } - .into(), - Box::new(( - helpers::VerifySubmitGrandpaFinalityProofOutcome::< - RuntimeHelper::Runtime, - RuntimeHelper::GPI, - >::expect_best_header_hash(relay_chain_header_hash), - helpers::VerifySubmitParachainHeaderProofOutcome::< - RuntimeHelper::Runtime, - RuntimeHelper::PPI, - >::expect_best_header_hash(bridged_para_id, parachain_head_hash), - helpers::VerifySubmitMessagesProofOutcome::< - RuntimeHelper::Runtime, - RuntimeHelper::MPI, - >::expect_last_delivered_nonce(lane_id, 1), - helpers::VerifyRelayerRewarded::::expect_relayer_reward( - relayer_id_at_this_chain, - RewardsAccountParams::new( - lane_id, - bridged_chain_id, - RewardsAccountOwner::ThisChain, + } + .into(), + Box::new( + ( + helpers::VerifySubmitGrandpaFinalityProofOutcome::< + RuntimeHelper::Runtime, + RuntimeHelper::GPI, + >::expect_best_header_hash(relay_chain_header_hash), + helpers::VerifySubmitParachainHeaderProofOutcome::< + RuntimeHelper::Runtime, + RuntimeHelper::PPI, + >::expect_best_header_hash(bridged_para_id, parachain_head_hash), + helpers::VerifySubmitMessagesProofOutcome::< + RuntimeHelper::Runtime, + RuntimeHelper::MPI, + >::expect_last_delivered_nonce(lane_id, 1), + helpers::VerifyRelayerRewarded::< + RuntimeHelper::Runtime, + RuntimeHelper::RPI, + >::expect_relayer_reward( + relayer_id_at_this_chain, + RewardsAccountParams::new( + lane_id, + bridged_chain_id, + RewardsAccountOwner::ThisChain, + ), + ), ), ), - )), - )] + ), + ] }, ); } @@ -551,8 +575,9 @@ where >::BridgedChain, BridgedChainOf, ThisChainOf, + LaneIdOf >( - LaneId::default(), + LaneIdOf::::default(), vec![Instruction::<()>::ClearOrigin; 1_024].into(), 1, [GlobalConsensus(Polkadot), Parachain(1_000)].into(), @@ -621,8 +646,9 @@ where >::BridgedChain, BridgedChainOf, ThisChainOf, + LaneIdOf, >( - LaneId::default(), + LaneIdOf::::default(), 1, 5, 1_000, @@ -683,8 +709,9 @@ where >::BridgedChain, BridgedChainOf, ThisChainOf, + LaneIdOf, >( - LaneId::default(), + LaneIdOf::::default(), vec![Instruction::<()>::ClearOrigin; 1_024].into(), 1, [GlobalConsensus(Polkadot), Parachain(1_000)].into(), @@ -738,8 +765,9 @@ where >::BridgedChain, BridgedChainOf, ThisChainOf, + LaneIdOf, >( - LaneId::default(), + LaneIdOf::::default(), 1, 5, 1_000, diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs index c4b5e5583baa0d9a22307e7f1fcb52ca5e95b0e9..aac60bba0b53978fc72f7562a1e52480205be3b6 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 @@ -19,19 +19,21 @@ use crate::test_cases::{bridges_prelude::*, run_test, RuntimeHelper}; use asset_test_utils::BasicParachainRuntime; -use bp_messages::{LaneId, MessageNonce}; +use bp_messages::MessageNonce; use bp_polkadot_core::parachains::{ParaHash, ParaId}; use bp_relayers::RewardsAccountParams; use bp_runtime::Chain; +use bp_xcm_bridge_hub::BridgeLocations; use codec::Decode; use core::marker::PhantomData; use frame_support::{ assert_ok, - traits::{OnFinalize, OnInitialize, PalletInfoAccess}, + dispatch::GetDispatchInfo, + traits::{fungible::Mutate, OnFinalize, OnInitialize, PalletInfoAccess}, }; use frame_system::pallet_prelude::BlockNumberFor; use pallet_bridge_grandpa::{BridgedBlockHash, BridgedHeader}; -use pallet_bridge_messages::BridgedChainOf; +use pallet_bridge_messages::{BridgedChainOf, LaneIdOf}; use parachains_common::AccountId; use parachains_runtimes_test_utils::{ mock_open_hrmp_channel, AccountIdOf, CollatorSessionKeys, RuntimeCallOf, SlotDurations, @@ -40,6 +42,7 @@ use sp_core::Get; use sp_keyring::AccountKeyring::*; use sp_runtime::{traits::TrailingZeroInput, AccountId32}; use xcm::latest::prelude::*; +use xcm_executor::traits::ConvertLocation; /// Verify that the transaction has succeeded. #[impl_trait_for_tuples::impl_for_tuples(30)] @@ -129,8 +132,8 @@ where } /// Checks that the latest delivered nonce in the bridge messages pallet equals to given one. -pub struct VerifySubmitMessagesProofOutcome { - lane: LaneId, +pub struct VerifySubmitMessagesProofOutcome, MPI: 'static> { + lane: LaneIdOf, expected_nonce: MessageNonce, _marker: PhantomData<(Runtime, MPI)>, } @@ -142,7 +145,7 @@ where { /// Expect given delivered nonce to be the latest after transaction. pub fn expect_last_delivered_nonce( - lane: LaneId, + lane: LaneIdOf, expected_nonce: MessageNonce, ) -> Box { Box::new(Self { lane, expected_nonce, _marker: PhantomData }) @@ -157,37 +160,39 @@ where fn verify_outcome(&self) { assert_eq!( pallet_bridge_messages::InboundLanes::::get(self.lane) - .last_delivered_nonce(), - self.expected_nonce, + .map(|d| d.last_delivered_nonce()), + Some(self.expected_nonce), ); } } /// Verifies that relayer is rewarded at this chain. -pub struct VerifyRelayerRewarded { +pub struct VerifyRelayerRewarded, RPI: 'static> { relayer: Runtime::AccountId, - reward_params: RewardsAccountParams, + reward_params: RewardsAccountParams, } -impl VerifyRelayerRewarded +impl VerifyRelayerRewarded where - Runtime: pallet_bridge_relayers::Config, + Runtime: pallet_bridge_relayers::Config, + RPI: 'static, { /// Expect given delivered nonce to be the latest after transaction. pub fn expect_relayer_reward( relayer: Runtime::AccountId, - reward_params: RewardsAccountParams, + reward_params: RewardsAccountParams, ) -> Box { Box::new(Self { relayer, reward_params }) } } -impl VerifyTransactionOutcome for VerifyRelayerRewarded +impl VerifyTransactionOutcome for VerifyRelayerRewarded where - Runtime: pallet_bridge_relayers::Config, + Runtime: pallet_bridge_relayers::Config, + RPI: 'static, { fn verify_outcome(&self) { - assert!(pallet_bridge_relayers::RelayerRewards::::get( + assert!(pallet_bridge_relayers::RelayerRewards::::get( &self.relayer, &self.reward_params, ) @@ -382,3 +387,184 @@ fn execute_and_verify_calls( verifier.verify_outcome(); } } + +/// Helper function to open the bridge/lane for `source` and `destination` while ensuring all +/// required balances are placed into the SA of the source. +pub fn ensure_opened_bridge< + Runtime, + XcmOverBridgePalletInstance, + LocationToAccountId, + TokenLocation> +(source: Location, destination: InteriorLocation, bridge_opener: impl Fn(BridgeLocations, Asset)) -> (BridgeLocations, pallet_xcm_bridge_hub::LaneIdOf) +where + Runtime: BasicParachainRuntime + BridgeXcmOverBridgeConfig, + XcmOverBridgePalletInstance: 'static, + ::RuntimeCall: GetDispatchInfo + From>, + ::Balance: From<<>::BridgeMessagesPalletInstance>>::ThisChain as bp_runtime::Chain>::Balance>, + ::Balance: From, + LocationToAccountId: ConvertLocation>, +TokenLocation: Get{ + // construct expected bridge configuration + let locations = + pallet_xcm_bridge_hub::Pallet::::bridge_locations( + source.clone().into(), + destination.clone().into(), + ) + .expect("valid bridge locations"); + assert!(pallet_xcm_bridge_hub::Bridges::::get( + locations.bridge_id() + ) + .is_none()); + + // required balance: ED + fee + BridgeDeposit + let bridge_deposit = + >::BridgeDeposit::get( + ); + // random high enough value for `BuyExecution` fees + let buy_execution_fee_amount = 5_000_000_000_000_u128; + let buy_execution_fee = (TokenLocation::get(), buy_execution_fee_amount).into(); + let balance_needed = ::ExistentialDeposit::get() + + buy_execution_fee_amount.into() + + bridge_deposit.into(); + + // SA of source location needs to have some required balance + let source_account_id = LocationToAccountId::convert_location(&source).expect("valid location"); + let _ = >::mint_into(&source_account_id, balance_needed) + .expect("mint_into passes"); + + // call the bridge opener + bridge_opener(*locations.clone(), buy_execution_fee); + + // check opened bridge + let bridge = pallet_xcm_bridge_hub::Bridges::::get( + locations.bridge_id(), + ) + .expect("opened bridge"); + + // check state + assert_ok!( + pallet_xcm_bridge_hub::Pallet::::do_try_state() + ); + + // return locations + (*locations, bridge.lane_id) +} + +/// Utility for opening bridge with dedicated `pallet_xcm_bridge_hub`'s extrinsic. +pub fn open_bridge_with_extrinsic( + locations: BridgeLocations, + buy_execution_fee: Asset, +) where + Runtime: frame_system::Config + + pallet_xcm_bridge_hub::Config + + cumulus_pallet_parachain_system::Config + + pallet_xcm::Config, + XcmOverBridgePalletInstance: 'static, + ::RuntimeCall: + GetDispatchInfo + From>, +{ + // open bridge with `Transact` call + let open_bridge_call = RuntimeCallOf::::from(BridgeXcmOverBridgeCall::< + Runtime, + XcmOverBridgePalletInstance, + >::open_bridge { + bridge_destination_universal_location: Box::new( + locations.bridge_destination_universal_location().clone().into(), + ), + }); + + // execute XCM as source origin would do with `Transact -> Origin::Xcm` + assert_ok!(RuntimeHelper::::execute_as_origin_xcm( + locations.bridge_origin_relative_location().clone(), + open_bridge_call, + buy_execution_fee + ) + .ensure_complete()); +} + +/// Utility for opening bridge directly inserting data to the storage (used only for legacy +/// purposes). +pub fn open_bridge_with_storage( + locations: BridgeLocations, + _buy_execution_fee: Asset, + lane_id: pallet_xcm_bridge_hub::LaneIdOf, +) where + Runtime: pallet_xcm_bridge_hub::Config, + XcmOverBridgePalletInstance: 'static, +{ + // insert bridge data directly to the storage + assert_ok!( + pallet_xcm_bridge_hub::Pallet::::do_open_bridge( + Box::new(locations), + lane_id, + true + ) + ); +} + +/// Helper function to close the bridge/lane for `source` and `destination`. +pub fn close_bridge(source: Location, destination: InteriorLocation) +where + Runtime: BasicParachainRuntime + BridgeXcmOverBridgeConfig, + XcmOverBridgePalletInstance: 'static, + ::RuntimeCall: GetDispatchInfo + From>, + ::Balance: From<<>::BridgeMessagesPalletInstance>>::ThisChain as bp_runtime::Chain>::Balance>, + ::Balance: From, + LocationToAccountId: ConvertLocation>, +TokenLocation: Get{ + // construct expected bridge configuration + let locations = + pallet_xcm_bridge_hub::Pallet::::bridge_locations( + source.clone().into(), + destination.clone().into(), + ) + .expect("valid bridge locations"); + assert!(pallet_xcm_bridge_hub::Bridges::::get( + locations.bridge_id() + ) + .is_some()); + + // required balance: ED + fee + BridgeDeposit + let bridge_deposit = + >::BridgeDeposit::get( + ); + // random high enough value for `BuyExecution` fees + let buy_execution_fee_amount = 2_500_000_000_000_u128; + let buy_execution_fee = (TokenLocation::get(), buy_execution_fee_amount).into(); + let balance_needed = ::ExistentialDeposit::get() + + buy_execution_fee_amount.into() + + bridge_deposit.into(); + + // SA of source location needs to have some required balance + let source_account_id = LocationToAccountId::convert_location(&source).expect("valid location"); + let _ = >::mint_into(&source_account_id, balance_needed) + .expect("mint_into passes"); + + // close bridge with `Transact` call + let close_bridge_call = RuntimeCallOf::::from(BridgeXcmOverBridgeCall::< + Runtime, + XcmOverBridgePalletInstance, + >::close_bridge { + bridge_destination_universal_location: Box::new(destination.into()), + may_prune_messages: 16, + }); + + // execute XCM as source origin would do with `Transact -> Origin::Xcm` + assert_ok!(RuntimeHelper::::execute_as_origin_xcm( + source.clone(), + close_bridge_call, + buy_execution_fee + ) + .ensure_complete()); + + // bridge is closed + assert!(pallet_xcm_bridge_hub::Bridges::::get( + locations.bridge_id() + ) + .is_none()); + + // check state + assert_ok!( + pallet_xcm_bridge_hub::Pallet::::do_try_state() + ); +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/mod.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/mod.rs index a36a74dbbbc3ab4ec8413f1b92ede16289600413..9c5d6269dc0e799ef6fe89fb18fa5389c5cceca0 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/mod.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/mod.rs @@ -29,17 +29,15 @@ use crate::{test_cases::bridges_prelude::*, test_data}; use asset_test_utils::BasicParachainRuntime; use bp_messages::{ target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, - LaneId, MessageKey, MessagesOperatingMode, OutboundLaneData, + LaneState, MessageKey, MessagesOperatingMode, OutboundLaneData, }; use bp_runtime::BasicOperatingMode; -use bridge_runtime_common::messages_xcm_extension::{ - XcmAsPlainPayload, XcmBlobMessageDispatchResult, -}; +use bp_xcm_bridge_hub::{Bridge, BridgeState, XcmAsPlainPayload}; use codec::Encode; use frame_support::{ assert_ok, dispatch::GetDispatchInfo, - traits::{Get, OnFinalize, OnInitialize, OriginTrait}, + traits::{Contains, Get, OnFinalize, OnInitialize, OriginTrait}, }; use frame_system::pallet_prelude::BlockNumberFor; use parachains_common::AccountId; @@ -51,27 +49,35 @@ use sp_runtime::{traits::Zero, AccountId32}; use xcm::{latest::prelude::*, AlwaysLatest}; use xcm_builder::DispatchBlobError; use xcm_executor::{ - traits::{TransactAsset, WeightBounds}, + traits::{ConvertLocation, TransactAsset, WeightBounds}, XcmExecutor, }; /// Common bridges exports. pub(crate) mod bridges_prelude { + pub use bp_parachains::{RelayBlockHash, RelayBlockNumber}; pub use pallet_bridge_grandpa::{Call as BridgeGrandpaCall, Config as BridgeGrandpaConfig}; - pub use pallet_bridge_messages::{Call as BridgeMessagesCall, Config as BridgeMessagesConfig}; + pub use pallet_bridge_messages::{ + Call as BridgeMessagesCall, Config as BridgeMessagesConfig, LanesManagerError, + }; pub use pallet_bridge_parachains::{ - Call as BridgeParachainsCall, Config as BridgeParachainsConfig, RelayBlockHash, - RelayBlockNumber, + Call as BridgeParachainsCall, Config as BridgeParachainsConfig, + }; + pub use pallet_xcm_bridge_hub::{ + Call as BridgeXcmOverBridgeCall, Config as BridgeXcmOverBridgeConfig, LanesManagerOf, + XcmBlobMessageDispatchResult, }; } // Re-export test_case from assets pub use asset_test_utils::include_teleports_for_native_asset_works; +use pallet_bridge_messages::LaneIdOf; pub type RuntimeHelper = parachains_runtimes_test_utils::RuntimeHelper; // Re-export test_case from `parachains-runtimes-test-utils` +use crate::test_cases::helpers::open_bridge_with_extrinsic; pub use parachains_runtimes_test_utils::test_cases::{ change_storage_constant_by_governance_works, set_storage_keys_by_governance_works, }; @@ -322,7 +328,7 @@ pub fn handle_export_message_from_system_parachain_to_outbound_queue_works< export_message_instruction: fn() -> Instruction, existential_deposit: Option, maybe_paid_export_message: Option, - prepare_configuration: impl Fn() -> LaneId, + prepare_configuration: impl Fn() -> LaneIdOf, ) where Runtime: BasicParachainRuntime + BridgeMessagesConfig, XcmConfig: xcm_executor::Config, @@ -339,7 +345,12 @@ pub fn handle_export_message_from_system_parachain_to_outbound_queue_works< pallet_bridge_messages::OutboundLanes::::try_get( expected_lane_id ), - Err(()) + Ok(OutboundLaneData { + state: LaneState::Opened, + oldest_unpruned_nonce: 1, + latest_received_nonce: 0, + latest_generated_nonce: 0 + }) ); // prepare `ExportMessage` @@ -390,6 +401,7 @@ pub fn handle_export_message_from_system_parachain_to_outbound_queue_works< expected_lane_id ), Ok(OutboundLaneData { + state: LaneState::Opened, oldest_unpruned_nonce: 1, latest_received_nonce: 0, latest_generated_nonce: 1, @@ -429,7 +441,7 @@ pub fn message_dispatch_routing_works< unwrap_cumulus_pallet_xcmp_queue_event: Box< dyn Fn(Vec) -> Option>, >, - prepare_configuration: impl Fn() -> LaneId, + prepare_configuration: impl Fn(), ) where Runtime: BasicParachainRuntime + cumulus_pallet_xcmp_queue::Config @@ -457,8 +469,9 @@ pub fn message_dispatch_routing_works< assert_ne!(runtime_para_id, sibling_parachain_id); run_test::(collator_session_key, runtime_para_id, vec![], || { - let expected_lane_id = prepare_configuration(); + prepare_configuration(); + let dummy_lane_id = LaneIdOf::::default(); let mut alice = [0u8; 32]; alice[0] = 1; @@ -475,7 +488,7 @@ pub fn message_dispatch_routing_works< >((RuntimeNetwork::get(), Here)); let result = <>::MessageDispatch>::dispatch( - test_data::dispatch_message(expected_lane_id, 1, bridging_message), + test_data::dispatch_message(dummy_lane_id, 1, bridging_message), ); assert_eq!( format!("{:?}", result.dispatch_level_result), @@ -493,17 +506,18 @@ pub fn message_dispatch_routing_works< // 2. this message is sent from other global consensus with destination of this Runtime // sibling parachain (HRMP) - let bridging_message = test_data::simulate_message_exporter_on_bridged_chain::< - BridgedNetwork, - NetworkWithParentCount, - AlwaysLatest, - >((RuntimeNetwork::get(), [Parachain(sibling_parachain_id)].into())); + let bridging_message = + test_data::simulate_message_exporter_on_bridged_chain::< + BridgedNetwork, + NetworkWithParentCount, + AlwaysLatest, + >((RuntimeNetwork::get(), [Parachain(sibling_parachain_id)].into())); // 2.1. WITHOUT opened hrmp channel -> RoutingError let result = <>::MessageDispatch>::dispatch( DispatchMessage { - key: MessageKey { lane_id: expected_lane_id, nonce: 1 }, + key: MessageKey { lane_id: dummy_lane_id, nonce: 1 }, data: DispatchMessageData { payload: Ok(bridging_message.clone()) }, }, ); @@ -535,7 +549,7 @@ pub fn message_dispatch_routing_works< let result = <>::MessageDispatch>::dispatch( DispatchMessage { - key: MessageKey { lane_id: expected_lane_id, nonce: 1 }, + key: MessageKey { lane_id: dummy_lane_id, nonce: 1 }, data: DispatchMessageData { payload: Ok(bridging_message) }, }, ); @@ -641,3 +655,130 @@ where estimated_fee.into() } + +/// Test-case makes sure that `Runtime` can open/close bridges. +pub fn open_and_close_bridge_works( + collator_session_key: CollatorSessionKeys, + runtime_para_id: u32, + source: Location, + destination: InteriorLocation, +) where + Runtime: BasicParachainRuntime + BridgeXcmOverBridgeConfig, + XcmOverBridgePalletInstance: 'static, + ::RuntimeCall: GetDispatchInfo + From>, + ::Balance: From<<>::BridgeMessagesPalletInstance>>::ThisChain as bp_runtime::Chain>::Balance>, + ::Balance: From, + <>::BridgeMessagesPalletInstance>>::ThisChain as bp_runtime::Chain>::AccountId: From<::AccountId>, + LocationToAccountId: ConvertLocation>, + TokenLocation: Get, +{ + run_test::(collator_session_key, runtime_para_id, vec![], || { + // construct expected bridge configuration + let locations = pallet_xcm_bridge_hub::Pallet::::bridge_locations( + source.clone().into(), + destination.clone().into(), + ).expect("valid bridge locations"); + let expected_lane_id = + locations.calculate_lane_id(xcm::latest::VERSION).expect("valid laneId"); + let lanes_manager = LanesManagerOf::::new(); + + let expected_deposit = if >::AllowWithoutBridgeDeposit::contains( + locations.bridge_origin_relative_location() + ) { + Zero::zero() + } else { + >::BridgeDeposit::get() + }; + + // check bridge/lane DOES not exist + assert_eq!( + pallet_xcm_bridge_hub::Bridges::::get( + locations.bridge_id() + ), + None + ); + assert_eq!( + lanes_manager.active_inbound_lane(expected_lane_id).map(drop), + Err(LanesManagerError::UnknownInboundLane) + ); + assert_eq!( + lanes_manager.active_outbound_lane(expected_lane_id).map(drop), + Err(LanesManagerError::UnknownOutboundLane) + ); + + // open bridge with Transact call from sibling + assert_eq!( + helpers::ensure_opened_bridge::< + Runtime, + XcmOverBridgePalletInstance, + LocationToAccountId, + TokenLocation, + >( + source.clone(), + destination.clone(), + open_bridge_with_extrinsic:: + ) + .0 + .bridge_id(), + locations.bridge_id() + ); + + // check bridge/lane DOES exist + assert_eq!( + pallet_xcm_bridge_hub::Bridges::::get( + locations.bridge_id() + ), + Some(Bridge { + bridge_origin_relative_location: Box::new(source.clone().into()), + bridge_origin_universal_location: Box::new( + locations.bridge_origin_universal_location().clone().into() + ), + bridge_destination_universal_location: Box::new( + locations.bridge_destination_universal_location().clone().into() + ), + state: BridgeState::Opened, + bridge_owner_account: LocationToAccountId::convert_location(&source) + .expect("valid location") + .into(), + deposit: expected_deposit, + lane_id: expected_lane_id + }) + ); + assert_eq!( + lanes_manager.active_inbound_lane(expected_lane_id).map(|lane| lane.state()), + Ok(LaneState::Opened) + ); + assert_eq!( + lanes_manager.active_outbound_lane(expected_lane_id).map(|lane| lane.state()), + Ok(LaneState::Opened) + ); + + // close bridge with Transact call from sibling + helpers::close_bridge::< + Runtime, + XcmOverBridgePalletInstance, + LocationToAccountId, + TokenLocation, + >(source.clone(), destination); + + // check bridge/lane DOES not exist + assert_eq!( + pallet_xcm_bridge_hub::Bridges::::get( + locations.bridge_id() + ), + None + ); + assert_eq!( + lanes_manager.active_inbound_lane(expected_lane_id).map(drop), + Err(LanesManagerError::UnknownInboundLane) + ); + assert_eq!( + lanes_manager.active_outbound_lane(expected_lane_id).map(drop), + Err(LanesManagerError::UnknownOutboundLane) + ); + }); +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_grandpa_chain.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_grandpa_chain.rs index 5c1dc492a9d2999e6473ca98ece70ffd4d7de905..7461085330f27691b4da39e425aebcf64046641d 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_grandpa_chain.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_grandpa_chain.rs @@ -20,12 +20,12 @@ use crate::test_data::prepare_inbound_xcm; use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, - target_chain::FromBridgedChainMessagesProof, ChainWithMessages, LaneId, MessageNonce, + target_chain::FromBridgedChainMessagesProof, ChainWithMessages, LaneState, MessageNonce, UnrewardedRelayersState, }; use bp_runtime::{AccountIdOf, BlockNumberOf, Chain, HeaderOf, UnverifiedStorageProofParams}; use bp_test_utils::make_default_justification; -use bridge_runtime_common::messages_xcm_extension::XcmAsPlainPayload; +use bp_xcm_bridge_hub::XcmAsPlainPayload; use codec::Encode; use pallet_bridge_grandpa::{BridgedChain, BridgedHeader}; use sp_runtime::traits::Header as HeaderT; @@ -40,7 +40,7 @@ use pallet_bridge_messages::{ encode_all_messages, encode_lane_data, prepare_message_delivery_storage_proof, prepare_messages_storage_proof, }, - BridgedChainOf, + BridgedChainOf, LaneIdOf, }; use sp_runtime::DigestItem; @@ -48,7 +48,10 @@ use sp_runtime::DigestItem; pub fn make_complex_relayer_delivery_batch( bridged_header: BridgedHeader, bridged_justification: GrandpaJustification>, - message_proof: FromBridgedChainMessagesProof>>, + message_proof: FromBridgedChainMessagesProof< + HashOf>, + LaneIdOf, + >, relayer_id_at_bridged_chain: InboundRelayerId, ) -> pallet_utility::Call where @@ -82,6 +85,7 @@ pub fn make_complex_relayer_confirmation_batch( bridged_justification: GrandpaJustification>, message_delivery_proof: FromBridgedChainMessagesDeliveryProof< HashOf>, + LaneIdOf, >, relayers_state: UnrewardedRelayersState, ) -> pallet_utility::Call @@ -111,7 +115,10 @@ where /// Prepare a call with message proof. pub fn make_standalone_relayer_delivery_call( - message_proof: FromBridgedChainMessagesProof>>, + message_proof: FromBridgedChainMessagesProof< + HashOf>, + LaneIdOf, + >, relayer_id_at_bridged_chain: InboundRelayerId, ) -> Runtime::RuntimeCall where @@ -134,6 +141,7 @@ where pub fn make_standalone_relayer_confirmation_call( message_delivery_proof: FromBridgedChainMessagesDeliveryProof< HashOf>, + LaneIdOf, >, relayers_state: UnrewardedRelayersState, ) -> Runtime::RuntimeCall @@ -152,7 +160,7 @@ where } /// Prepare storage proofs of messages, stored at the (bridged) source GRANDPA chain. -pub fn make_complex_relayer_delivery_proofs( +pub fn make_complex_relayer_delivery_proofs( lane_id: LaneId, xcm_message: Xcm<()>, message_nonce: MessageNonce, @@ -162,17 +170,18 @@ pub fn make_complex_relayer_delivery_proofs ) -> ( HeaderOf, GrandpaJustification>, - FromBridgedChainMessagesProof>, + FromBridgedChainMessagesProof, LaneId>, ) where BridgedChain: ChainWithGrandpa, ThisChainWithMessages: ChainWithMessages, + LaneId: Copy + Encode, { // prepare message let message_payload = prepare_inbound_xcm(xcm_message, message_destination); // prepare storage proof containing message let (state_root, storage_proof) = - prepare_messages_storage_proof::( + prepare_messages_storage_proof::( lane_id, message_nonce..=message_nonce, None, @@ -206,6 +215,7 @@ pub fn make_complex_relayer_confirmation_proofs< BridgedChain, ThisChainWithMessages, InnerXcmRuntimeCall, + LaneId, >( lane_id: LaneId, header_number: BlockNumberOf, @@ -214,17 +224,19 @@ pub fn make_complex_relayer_confirmation_proofs< ) -> ( HeaderOf, GrandpaJustification>, - FromBridgedChainMessagesDeliveryProof>, + FromBridgedChainMessagesDeliveryProof, LaneId>, ) where BridgedChain: ChainWithGrandpa, ThisChainWithMessages: ChainWithMessages, + LaneId: Copy + Encode, { // prepare storage proof containing message delivery proof let (state_root, storage_proof) = - prepare_message_delivery_storage_proof::( + prepare_message_delivery_storage_proof::( lane_id, InboundLaneData { + state: LaneState::Opened, relayers: vec![ UnrewardedRelayer { relayer: relayer_id_at_this_chain, diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_parachain.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_parachain.rs index b99c275bd0df94856eedcc1cc008c1d7a0b1a673..a6659b8241dfd50aedbfe14ac2672756fa0a03af 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_parachain.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_parachain.rs @@ -20,17 +20,17 @@ use super::{from_grandpa_chain::make_complex_bridged_grandpa_header_proof, prepa use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, - target_chain::FromBridgedChainMessagesProof, ChainWithMessages, LaneId, + target_chain::FromBridgedChainMessagesProof, ChainWithMessages, LaneState, UnrewardedRelayersState, Weight, }; +use bp_parachains::{RelayBlockHash, RelayBlockNumber}; use bp_runtime::{ AccountIdOf, BlockNumberOf, Chain, HeaderOf, Parachain, UnverifiedStorageProofParams, }; use bp_test_utils::prepare_parachain_heads_proof; -use bridge_runtime_common::messages_xcm_extension::XcmAsPlainPayload; +use bp_xcm_bridge_hub::XcmAsPlainPayload; use codec::Encode; use pallet_bridge_grandpa::BridgedHeader; -use pallet_bridge_parachains::{RelayBlockHash, RelayBlockNumber}; use sp_runtime::traits::Header as HeaderT; use xcm::latest::prelude::*; @@ -43,7 +43,7 @@ use pallet_bridge_messages::{ encode_all_messages, encode_lane_data, prepare_message_delivery_storage_proof, prepare_messages_storage_proof, }, - BridgedChainOf, + BridgedChainOf, LaneIdOf, }; use sp_runtime::SaturatedConversion; @@ -53,7 +53,7 @@ pub fn make_complex_relayer_delivery_batch( grandpa_justification: GrandpaJustification>, parachain_heads: Vec<(ParaId, ParaHash)>, para_heads_proof: ParaHeadsProof, - message_proof: FromBridgedChainMessagesProof, + message_proof: FromBridgedChainMessagesProof>, relayer_id_at_bridged_chain: InboundRelayerId, ) -> pallet_utility::Call where @@ -106,7 +106,7 @@ pub fn make_complex_relayer_confirmation_batch( grandpa_justification: GrandpaJustification>, parachain_heads: Vec<(ParaId, ParaHash)>, para_heads_proof: ParaHeadsProof, - message_delivery_proof: FromBridgedChainMessagesDeliveryProof, + message_delivery_proof: FromBridgedChainMessagesDeliveryProof>, relayers_state: UnrewardedRelayersState, ) -> pallet_utility::Call where @@ -154,7 +154,7 @@ where /// Prepare a call with message proof. pub fn make_standalone_relayer_delivery_call( - message_proof: FromBridgedChainMessagesProof, + message_proof: FromBridgedChainMessagesProof>, relayer_id_at_bridged_chain: InboundRelayerId, ) -> Runtime::RuntimeCall where @@ -174,7 +174,7 @@ where /// Prepare a call with message delivery proof. pub fn make_standalone_relayer_confirmation_call( - message_delivery_proof: FromBridgedChainMessagesDeliveryProof, + message_delivery_proof: FromBridgedChainMessagesDeliveryProof>, relayers_state: UnrewardedRelayersState, ) -> Runtime::RuntimeCall where @@ -195,6 +195,7 @@ pub fn make_complex_relayer_delivery_proofs< BridgedRelayChain, BridgedParachain, ThisChainWithMessages, + LaneId, >( lane_id: LaneId, xcm_message: Xcm<()>, @@ -210,19 +211,20 @@ pub fn make_complex_relayer_delivery_proofs< ParaHead, Vec<(ParaId, ParaHash)>, ParaHeadsProof, - FromBridgedChainMessagesProof, + FromBridgedChainMessagesProof, ) where BridgedRelayChain: bp_runtime::Chain + ChainWithGrandpa, BridgedParachain: bp_runtime::Chain + Parachain, ThisChainWithMessages: ChainWithMessages, + LaneId: Copy + Encode, { // prepare message let message_payload = prepare_inbound_xcm(xcm_message, message_destination); // prepare para storage proof containing message let (para_state_root, para_storage_proof) = - prepare_messages_storage_proof::( + prepare_messages_storage_proof::( lane_id, message_nonce..=message_nonce, None, @@ -266,6 +268,7 @@ pub fn make_complex_relayer_confirmation_proofs< BridgedRelayChain, BridgedParachain, ThisChainWithMessages, + LaneId, >( lane_id: LaneId, para_header_number: u32, @@ -279,19 +282,21 @@ pub fn make_complex_relayer_confirmation_proofs< ParaHead, Vec<(ParaId, ParaHash)>, ParaHeadsProof, - FromBridgedChainMessagesDeliveryProof, + FromBridgedChainMessagesDeliveryProof, ) where BridgedRelayChain: bp_runtime::Chain + ChainWithGrandpa, BridgedParachain: bp_runtime::Chain + Parachain, ThisChainWithMessages: ChainWithMessages, + LaneId: Copy + Encode, { // prepare para storage proof containing message delivery proof let (para_state_root, para_storage_proof) = - prepare_message_delivery_storage_proof::( + prepare_message_delivery_storage_proof::( lane_id, InboundLaneData { + state: LaneState::Opened, relayers: vec![ UnrewardedRelayer { relayer: relayer_id_at_this_chain.into(), diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/mod.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/mod.rs index 106eacd799ca3aee1daea2769423976154d50337..c34188af506896d9fb4e18781b09d804077c3941 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/mod.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/mod.rs @@ -21,7 +21,7 @@ pub mod from_parachain; use bp_messages::{ target_chain::{DispatchMessage, DispatchMessageData}, - LaneId, MessageKey, + MessageKey, }; use codec::Encode; use frame_support::traits::Get; @@ -65,11 +65,11 @@ pub(crate) fn dummy_xcm() -> Xcm<()> { vec![Trap(42)].into() } -pub(crate) fn dispatch_message( +pub(crate) fn dispatch_message( lane_id: LaneId, nonce: MessageNonce, payload: Vec, -) -> DispatchMessage> { +) -> DispatchMessage, LaneId> { DispatchMessage { key: MessageKey { lane_id, nonce }, data: DispatchMessageData { payload: Ok(payload) }, diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/Cargo.toml b/cumulus/parachains/runtimes/collectives/collectives-westend/Cargo.toml index e98508ea02e63f7497f4263b4aea33d08f7e45c8..170d6d226057411e77de92da6d22cf68fe4fe641 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/Cargo.toml @@ -14,6 +14,7 @@ codec = { features = ["derive", "max-encoded-len"], workspace = true } hex-literal = { workspace = true, default-features = true } log = { workspace = true } scale-info = { features = ["derive"], workspace = true } +serde_json = { features = ["alloc"], workspace = true } # Substrate frame-benchmarking = { optional = true, workspace = true } @@ -54,6 +55,7 @@ sp-inherents = { workspace = true } sp-offchain = { workspace = true } sp-runtime = { workspace = true } sp-session = { workspace = true } +sp-std = { workspace = true } sp-storage = { workspace = true } sp-transaction-pool = { workspace = true } sp-version = { workspace = true } @@ -218,6 +220,7 @@ std = [ "polkadot-parachain-primitives/std", "polkadot-runtime-common/std", "scale-info/std", + "serde_json/std", "sp-api/std", "sp-arithmetic/std", "sp-block-builder/std", @@ -228,6 +231,7 @@ std = [ "sp-offchain/std", "sp-runtime/std", "sp-session/std", + "sp-std/std", "sp-storage/std", "sp-transaction-pool/std", "sp-version/std", diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/genesis_config_presets.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/genesis_config_presets.rs new file mode 100644 index 0000000000000000000000000000000000000000..30a23d7aaea458128bf5149670aa5a4addd97ee4 --- /dev/null +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/genesis_config_presets.rs @@ -0,0 +1,130 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! # Bridge Hub Westend Runtime genesis config presets + +use crate::*; +use alloc::{vec, vec::Vec}; +use cumulus_primitives_core::ParaId; +use parachains_common::{genesis_config_helpers::*, AccountId, AuraId}; +use sp_core::sr25519; +use sp_genesis_builder::PresetId; +use testnet_parachains_constants::westend::xcm_version::SAFE_XCM_VERSION; + +const COLLECTIVES_WESTEND_ED: Balance = ExistentialDeposit::get(); + +fn collectives_westend_genesis( + invulnerables: Vec<(AccountId, AuraId)>, + endowed_accounts: Vec, + id: ParaId, +) -> serde_json::Value { + let config = RuntimeGenesisConfig { + balances: BalancesConfig { + balances: endowed_accounts + .iter() + .cloned() + .map(|k| (k, COLLECTIVES_WESTEND_ED * 4096)) + .collect::>(), + }, + parachain_info: ParachainInfoConfig { parachain_id: id, ..Default::default() }, + collator_selection: CollatorSelectionConfig { + invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(), + candidacy_bond: COLLECTIVES_WESTEND_ED * 16, + ..Default::default() + }, + session: SessionConfig { + keys: invulnerables + .into_iter() + .map(|(acc, aura)| { + ( + acc.clone(), // account id + acc, // validator id + SessionKeys { aura }, // session keys + ) + }) + .collect(), + ..Default::default() + }, + polkadot_xcm: PolkadotXcmConfig { + safe_xcm_version: Some(SAFE_XCM_VERSION), + ..Default::default() + }, + ..Default::default() + }; + + serde_json::to_value(config).expect("Could not build genesis config.") +} + +/// Provides the JSON representation of predefined genesis config for given `id`. +pub fn get_preset(id: &sp_genesis_builder::PresetId) -> Option> { + let patch = match id.try_into() { + Ok(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET) => collectives_westend_genesis( + // initial collators. + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed::("Bob"), + ), + ], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + 1001.into(), + ), + Ok(sp_genesis_builder::DEV_RUNTIME_PRESET) => collectives_westend_genesis( + // initial collators. + vec![( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + )], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + ], + 1001.into(), + ), + _ => return None, + }; + Some( + serde_json::to_string(&patch) + .expect("serialization to json is expected to work. qed.") + .into_bytes(), + ) +} + +/// List of supported presets. +pub fn preset_names() -> Vec { + vec![ + PresetId::from(sp_genesis_builder::DEV_RUNTIME_PRESET), + PresetId::from(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET), + ] +} diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs index 21206d26dd573cc1473f3ba569433e9b7e61e7af..7f87d4701f9629deb9edaeeb438fc6cde5e3bef7 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs @@ -37,6 +37,7 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); pub mod ambassador; +mod genesis_config_presets; pub mod impls; mod weights; pub mod xcm_config; @@ -66,7 +67,7 @@ use sp_version::NativeVersion; use sp_version::RuntimeVersion; use codec::{Decode, Encode, MaxEncodedLen}; -use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; +use cumulus_primitives_core::{AggregateMessageOrigin, ClaimQueueOffset, CoreSelector, ParaId}; use frame_support::{ construct_runtime, derive_impl, dispatch::DispatchClass, @@ -125,11 +126,11 @@ 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_015_000, + spec_version: 1_016_001, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 6, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. @@ -222,6 +223,7 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<0>; + type DoneSlashHandler = (); } parameter_types! { @@ -397,6 +399,7 @@ impl cumulus_pallet_parachain_system::Config for Runtime { type ReservedXcmpWeight = ReservedXcmpWeight; type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases; type ConsensusHook = ConsensusHook; + type SelectCore = cumulus_pallet_parachain_system::DefaultCoreSelector; } type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< @@ -552,6 +555,9 @@ impl pallet_collective::Config for Runtime { type SetMembersOrigin = EnsureRoot; type WeightInfo = weights::pallet_collective::WeightInfo; type MaxProposalWeight = MaxProposalWeight; + type DisapproveOrigin = EnsureRoot; + type KillOrigin = EnsureRoot; + type Consideration = (); } pub const MAX_FELLOWS: u32 = ALLIANCE_MAX_MEMBERS; @@ -1004,6 +1010,12 @@ impl_runtime_apis! { } } + impl cumulus_primitives_core::GetCoreSelectorApi for Runtime { + fn core_selector() -> (CoreSelector, ClaimQueueOffset) { + ParachainSystem::core_selector() + } + } + #[cfg(feature = "try-runtime")] impl frame_try_runtime::TryRuntime for Runtime { fn on_runtime_upgrade(checks: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) { @@ -1146,11 +1158,11 @@ impl_runtime_apis! { } fn get_preset(id: &Option) -> Option> { - get_preset::(id, |_| None) + get_preset::(id, &genesis_config_presets::get_preset) } fn preset_names() -> Vec { - vec![] + genesis_config_presets::preset_names() } } } diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/block_weights.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/block_weights.rs index e7fdb2aae2a01ec06076de83d94817e540e205dd..41e30725e753fef32404b858a873e2456618d56f 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/block_weights.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/block_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/extrinsic_weights.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/extrinsic_weights.rs index 1a4adb968bb7195428ea00d59cd92dcd3b6eea5f..3bd48f061bba079a76aa8f032ebecd6d40c2156c 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/extrinsic_weights.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/extrinsic_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_collective.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_collective.rs index 9133baa6120cff6f13bf3a3f45ec5db4b3fe5f3f..d456f5b8c4606d38550408b132e941ce99f82c16 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_collective.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_collective.rs @@ -1,42 +1,41 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 +// This file is part of Cumulus. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . //! Autogenerated weights for `pallet_collective` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-07-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-29, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-ynta1nyy-project-238-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! EXECUTION: ``, WASM-EXECUTION: `Compiled`, CHAIN: `Some("collectives-polkadot-dev")`, DB CACHE: 1024 +//! HOSTNAME: `runner-svzsllib-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("collectives-westend-dev")`, DB CACHE: 1024 // Executed Command: -// ./target/production/polkadot-parachain +// target/production/polkadot-parachain // benchmark // pallet -// --chain=collectives-polkadot-dev -// --wasm-execution=compiled -// --pallet=pallet_collective -// --no-storage-info -// --no-median-slopes -// --no-min-squares -// --extrinsic=* // --steps=50 // --repeat=20 -// --json -// --header=./file_header.txt -// --output=./parachains/runtimes/collectives/collectives-polkadot/src/weights/ +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_collective +// --chain=collectives-westend-dev +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/ #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -63,14 +62,14 @@ impl pallet_collective::WeightInfo for WeightInfo { fn set_members(m: u32, _n: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + m * (3232 ยฑ0) + p * (3190 ยฑ0)` - // Estimated: `15691 + m * (1967 ยฑ23) + p * (4332 ยฑ23)` - // Minimum execution time: 16_410_000 picoseconds. - Weight::from_parts(16_816_000, 0) - .saturating_add(Weight::from_parts(0, 15691)) - // Standard Error: 59_812 - .saturating_add(Weight::from_parts(4_516_537, 0).saturating_mul(m.into())) - // Standard Error: 59_812 - .saturating_add(Weight::from_parts(7_992_168, 0).saturating_mul(p.into())) + // Estimated: `15728 + m * (1967 ยฑ23) + p * (4332 ยฑ23)` + // Minimum execution time: 16_539_000 picoseconds. + Weight::from_parts(16_884_000, 0) + .saturating_add(Weight::from_parts(0, 15728)) + // Standard Error: 65_205 + .saturating_add(Weight::from_parts(4_926_489, 0).saturating_mul(m.into())) + // Standard Error: 65_205 + .saturating_add(Weight::from_parts(9_044_204, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(p.into()))) .saturating_add(T::DbWeight::get().writes(2)) @@ -84,15 +83,15 @@ impl pallet_collective::WeightInfo for WeightInfo { /// The range of component `m` is `[1, 100]`. fn execute(b: u32, m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `32 + m * (32 ยฑ0)` - // Estimated: `1518 + m * (32 ยฑ0)` - // Minimum execution time: 14_418_000 picoseconds. - Weight::from_parts(13_588_617, 0) - .saturating_add(Weight::from_parts(0, 1518)) - // Standard Error: 21 - .saturating_add(Weight::from_parts(1_711, 0).saturating_mul(b.into())) - // Standard Error: 223 - .saturating_add(Weight::from_parts(13_836, 0).saturating_mul(m.into())) + // Measured: `69 + m * (32 ยฑ0)` + // Estimated: `1555 + m * (32 ยฑ0)` + // Minimum execution time: 16_024_000 picoseconds. + Weight::from_parts(15_295_443, 0) + .saturating_add(Weight::from_parts(0, 1555)) + // Standard Error: 22 + .saturating_add(Weight::from_parts(1_501, 0).saturating_mul(b.into())) + // Standard Error: 229 + .saturating_add(Weight::from_parts(12_430, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) } @@ -104,15 +103,15 @@ impl pallet_collective::WeightInfo for WeightInfo { /// The range of component `m` is `[1, 100]`. fn propose_execute(b: u32, m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `32 + m * (32 ยฑ0)` - // Estimated: `3498 + m * (32 ยฑ0)` - // Minimum execution time: 17_174_000 picoseconds. - Weight::from_parts(16_192_764, 0) - .saturating_add(Weight::from_parts(0, 3498)) - // Standard Error: 27 - .saturating_add(Weight::from_parts(1_672, 0).saturating_mul(b.into())) - // Standard Error: 280 - .saturating_add(Weight::from_parts(24_343, 0).saturating_mul(m.into())) + // Measured: `69 + m * (32 ยฑ0)` + // Estimated: `3535 + m * (32 ยฑ0)` + // Minimum execution time: 18_277_000 picoseconds. + Weight::from_parts(17_322_061, 0) + .saturating_add(Weight::from_parts(0, 3535)) + // Standard Error: 29 + .saturating_add(Weight::from_parts(1_725, 0).saturating_mul(b.into())) + // Standard Error: 309 + .saturating_add(Weight::from_parts(25_640, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) } @@ -131,17 +130,17 @@ impl pallet_collective::WeightInfo for WeightInfo { /// The range of component `p` is `[1, 100]`. fn propose_proposed(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `322 + m * (32 ยฑ0) + p * (36 ยฑ0)` - // Estimated: `3714 + m * (33 ยฑ0) + p * (36 ยฑ0)` - // Minimum execution time: 23_970_000 picoseconds. - Weight::from_parts(23_004_052, 0) - .saturating_add(Weight::from_parts(0, 3714)) - // Standard Error: 123 - .saturating_add(Weight::from_parts(2_728, 0).saturating_mul(b.into())) - // Standard Error: 1_291 - .saturating_add(Weight::from_parts(32_731, 0).saturating_mul(m.into())) - // Standard Error: 1_275 - .saturating_add(Weight::from_parts(199_537, 0).saturating_mul(p.into())) + // Measured: `359 + m * (32 ยฑ0) + p * (36 ยฑ0)` + // Estimated: `3751 + m * (33 ยฑ0) + p * (36 ยฑ0)` + // Minimum execution time: 23_915_000 picoseconds. + Weight::from_parts(22_895_005, 0) + .saturating_add(Weight::from_parts(0, 3751)) + // Standard Error: 116 + .saturating_add(Weight::from_parts(4_047, 0).saturating_mul(b.into())) + // Standard Error: 1_211 + .saturating_add(Weight::from_parts(37_038, 0).saturating_mul(m.into())) + // Standard Error: 1_196 + .saturating_add(Weight::from_parts(203_435, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(4)) .saturating_add(Weight::from_parts(0, 33).saturating_mul(m.into())) @@ -154,13 +153,13 @@ impl pallet_collective::WeightInfo for WeightInfo { /// The range of component `m` is `[5, 100]`. fn vote(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `771 + m * (64 ยฑ0)` - // Estimated: `4235 + m * (64 ยฑ0)` - // Minimum execution time: 25_843_000 picoseconds. - Weight::from_parts(26_092_578, 0) - .saturating_add(Weight::from_parts(0, 4235)) - // Standard Error: 1_785 - .saturating_add(Weight::from_parts(67_298, 0).saturating_mul(m.into())) + // Measured: `808 + m * (64 ยฑ0)` + // Estimated: `4272 + m * (64 ยฑ0)` + // Minimum execution time: 28_571_000 picoseconds. + Weight::from_parts(29_711_839, 0) + .saturating_add(Weight::from_parts(0, 4272)) + // Standard Error: 825 + .saturating_add(Weight::from_parts(39_661, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) @@ -177,15 +176,15 @@ impl pallet_collective::WeightInfo for WeightInfo { /// The range of component `p` is `[1, 100]`. fn close_early_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `360 + m * (64 ยฑ0) + p * (36 ยฑ0)` - // Estimated: `3805 + m * (65 ยฑ0) + p * (36 ยฑ0)` - // Minimum execution time: 27_543_000 picoseconds. - Weight::from_parts(26_505_473, 0) - .saturating_add(Weight::from_parts(0, 3805)) - // Standard Error: 1_054 - .saturating_add(Weight::from_parts(35_295, 0).saturating_mul(m.into())) - // Standard Error: 1_028 - .saturating_add(Weight::from_parts(190_508, 0).saturating_mul(p.into())) + // Measured: `397 + m * (64 ยฑ0) + p * (36 ยฑ0)` + // Estimated: `3842 + m * (65 ยฑ0) + p * (36 ยฑ0)` + // Minimum execution time: 27_742_000 picoseconds. + Weight::from_parts(28_014_736, 0) + .saturating_add(Weight::from_parts(0, 3842)) + // Standard Error: 1_221 + .saturating_add(Weight::from_parts(35_335, 0).saturating_mul(m.into())) + // Standard Error: 1_191 + .saturating_add(Weight::from_parts(193_513, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(3)) .saturating_add(Weight::from_parts(0, 65).saturating_mul(m.into())) @@ -204,17 +203,17 @@ impl pallet_collective::WeightInfo for WeightInfo { /// The range of component `p` is `[1, 100]`. fn close_early_approved(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `662 + b * (1 ยฑ0) + m * (64 ยฑ0) + p * (40 ยฑ0)` - // Estimated: `3979 + b * (1 ยฑ0) + m * (66 ยฑ0) + p * (40 ยฑ0)` - // Minimum execution time: 40_375_000 picoseconds. - Weight::from_parts(34_081_294, 0) - .saturating_add(Weight::from_parts(0, 3979)) - // Standard Error: 196 - .saturating_add(Weight::from_parts(3_796, 0).saturating_mul(b.into())) - // Standard Error: 2_072 - .saturating_add(Weight::from_parts(50_954, 0).saturating_mul(m.into())) - // Standard Error: 2_020 - .saturating_add(Weight::from_parts(246_000, 0).saturating_mul(p.into())) + // Measured: `699 + b * (1 ยฑ0) + m * (64 ยฑ0) + p * (40 ยฑ0)` + // Estimated: `4016 + b * (1 ยฑ0) + m * (66 ยฑ0) + p * (40 ยฑ0)` + // Minimum execution time: 38_274_000 picoseconds. + Weight::from_parts(37_886_500, 0) + .saturating_add(Weight::from_parts(0, 4016)) + // Standard Error: 165 + .saturating_add(Weight::from_parts(3_242, 0).saturating_mul(b.into())) + // Standard Error: 1_753 + .saturating_add(Weight::from_parts(33_851, 0).saturating_mul(m.into())) + // Standard Error: 1_709 + .saturating_add(Weight::from_parts(229_245, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(b.into())) @@ -235,15 +234,15 @@ impl pallet_collective::WeightInfo for WeightInfo { /// The range of component `p` is `[1, 100]`. fn close_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `458 + m * (48 ยฑ0) + p * (36 ยฑ0)` - // Estimated: `3898 + m * (49 ยฑ0) + p * (36 ยฑ0)` - // Minimum execution time: 28_793_000 picoseconds. - Weight::from_parts(29_656_832, 0) - .saturating_add(Weight::from_parts(0, 3898)) - // Standard Error: 1_214 - .saturating_add(Weight::from_parts(22_148, 0).saturating_mul(m.into())) - // Standard Error: 1_184 - .saturating_add(Weight::from_parts(189_860, 0).saturating_mul(p.into())) + // Measured: `495 + m * (48 ยฑ0) + p * (36 ยฑ0)` + // Estimated: `3935 + m * (49 ยฑ0) + p * (36 ยฑ0)` + // Minimum execution time: 29_178_000 picoseconds. + Weight::from_parts(28_752_686, 0) + .saturating_add(Weight::from_parts(0, 3935)) + // Standard Error: 1_230 + .saturating_add(Weight::from_parts(42_254, 0).saturating_mul(m.into())) + // Standard Error: 1_200 + .saturating_add(Weight::from_parts(210_610, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(3)) .saturating_add(Weight::from_parts(0, 49).saturating_mul(m.into())) @@ -264,17 +263,17 @@ impl pallet_collective::WeightInfo for WeightInfo { /// The range of component `p` is `[1, 100]`. fn close_approved(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `682 + b * (1 ยฑ0) + m * (64 ยฑ0) + p * (40 ยฑ0)` - // Estimated: `3999 + b * (1 ยฑ0) + m * (66 ยฑ0) + p * (40 ยฑ0)` - // Minimum execution time: 40_887_000 picoseconds. - Weight::from_parts(39_529_567, 0) - .saturating_add(Weight::from_parts(0, 3999)) - // Standard Error: 191 - .saturating_add(Weight::from_parts(2_802, 0).saturating_mul(b.into())) - // Standard Error: 2_021 - .saturating_add(Weight::from_parts(35_956, 0).saturating_mul(m.into())) - // Standard Error: 1_970 - .saturating_add(Weight::from_parts(235_154, 0).saturating_mul(p.into())) + // Measured: `719 + b * (1 ยฑ0) + m * (64 ยฑ0) + p * (40 ยฑ0)` + // Estimated: `4036 + b * (1 ยฑ0) + m * (66 ยฑ0) + p * (40 ยฑ0)` + // Minimum execution time: 40_296_000 picoseconds. + Weight::from_parts(41_629_338, 0) + .saturating_add(Weight::from_parts(0, 4036)) + // Standard Error: 162 + .saturating_add(Weight::from_parts(2_608, 0).saturating_mul(b.into())) + // Standard Error: 1_717 + .saturating_add(Weight::from_parts(29_637, 0).saturating_mul(m.into())) + // Standard Error: 1_674 + .saturating_add(Weight::from_parts(230_371, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(b.into())) @@ -290,15 +289,54 @@ impl pallet_collective::WeightInfo for WeightInfo { /// The range of component `p` is `[1, 100]`. fn disapprove_proposal(p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `189 + p * (32 ยฑ0)` - // Estimated: `1674 + p * (32 ยฑ0)` - // Minimum execution time: 14_040_000 picoseconds. - Weight::from_parts(15_075_964, 0) - .saturating_add(Weight::from_parts(0, 1674)) - // Standard Error: 854 - .saturating_add(Weight::from_parts(159_597, 0).saturating_mul(p.into())) + // Measured: `226 + p * (32 ยฑ0)` + // Estimated: `1711 + p * (32 ยฑ0)` + // Minimum execution time: 15_385_000 picoseconds. + Weight::from_parts(17_009_286, 0) + .saturating_add(Weight::from_parts(0, 1711)) + // Standard Error: 1_192 + .saturating_add(Weight::from_parts(170_070, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(3)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(p.into())) } + /// Storage: `AllianceMotion::ProposalOf` (r:1 w:1) + /// Proof: `AllianceMotion::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::CostOf` (r:1 w:0) + /// Proof: `AllianceMotion::CostOf` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::Proposals` (r:1 w:1) + /// Proof: `AllianceMotion::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::Voting` (r:0 w:1) + /// Proof: `AllianceMotion::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `d` is `[0, 1]`. + /// The range of component `p` is `[1, 100]`. + fn kill(d: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1497 + p * (36 ยฑ0)` + // Estimated: `4896 + d * (123 ยฑ6) + p * (37 ยฑ0)` + // Minimum execution time: 22_455_000 picoseconds. + Weight::from_parts(24_273_426, 0) + .saturating_add(Weight::from_parts(0, 4896)) + // Standard Error: 82_114 + .saturating_add(Weight::from_parts(996_567, 0).saturating_mul(d.into())) + // Standard Error: 1_271 + .saturating_add(Weight::from_parts(213_968, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(3)) + .saturating_add(Weight::from_parts(0, 123).saturating_mul(d.into())) + .saturating_add(Weight::from_parts(0, 37).saturating_mul(p.into())) + } + /// Storage: `AllianceMotion::ProposalOf` (r:1 w:0) + /// Proof: `AllianceMotion::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `AllianceMotion::CostOf` (r:1 w:0) + /// Proof: `AllianceMotion::CostOf` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn release_proposal_cost() -> Weight { + // Proof Size summary in bytes: + // Measured: `911` + // Estimated: `4376` + // Minimum execution time: 18_273_000 picoseconds. + Weight::from_parts(19_196_000, 0) + .saturating_add(Weight::from_parts(0, 4376)) + .saturating_add(T::DbWeight::get().reads(2)) + } } diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/paritydb_weights.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/paritydb_weights.rs index 25679703831a13b8d1bb7fb7dd4d92fa84b1f255..e0b1985c659c78b66176423f6bcc64d546b39324 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/paritydb_weights.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/paritydb_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/rocksdb_weights.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/rocksdb_weights.rs index 3dd817aa6f137085b0e5fdf2b11b7f50e5c8b002..c6e91b2fcffbf65e3fdce2bc8fbac19a605d00aa 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/rocksdb_weights.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/rocksdb_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 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 08b1d192b0be18c73b5cad3cc26ead88bdbacb64..f8e03303c32eb4eef03629467af7bfa4b947cd77 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs @@ -37,13 +37,13 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, - DenyReserveTransferToRelayChain, DenyThenTry, EnsureXcmOrigin, FixedWeightBounds, - FrameTransactionalProcessor, FungibleAdapter, IsConcrete, LocatableAssetId, - OriginToPluralityVoice, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, - SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, - TrailingSetTopicAsId, UsingComponents, WithComputedOrigin, WithUniqueTopic, - XcmFeeManagerFromComponents, + DenyReserveTransferToRelayChain, DenyThenTry, DescribeAllTerminal, DescribeFamily, + EnsureXcmOrigin, FixedWeightBounds, FrameTransactionalProcessor, FungibleAdapter, + HashedDescription, IsConcrete, LocatableAssetId, OriginToPluralityVoice, ParentAsSuperuser, + ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, + WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, }; use xcm_executor::XcmExecutor; @@ -81,6 +81,8 @@ pub type LocationToAccountId = ( SiblingParachainConvertsVia, // Straight up local `AccountId32` origins just alias directly to `AccountId`. AccountId32Aliases, + // Foreign locations alias into accounts according to a hash of their standard description. + HashedDescription>, ); /// Means for transacting the native currency on this chain.#[allow(deprecated)] diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/tests/tests.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/tests/tests.rs new file mode 100644 index 0000000000000000000000000000000000000000..7add10559d84de880dbe96117bd70fa6b82e675e --- /dev/null +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/tests/tests.rs @@ -0,0 +1,134 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +#![cfg(test)] + +use collectives_westend_runtime::xcm_config::LocationToAccountId; +use parachains_common::AccountId; +use sp_core::crypto::Ss58Codec; +use xcm::latest::prelude::*; +use xcm_runtime_apis::conversions::LocationToAccountHelper; + +const ALICE: [u8; 32] = [1u8; 32]; + +#[test] +fn location_conversion_works() { + // the purpose of hardcoded values is to catch an unintended location conversion logic change. + struct TestCase { + description: &'static str, + location: Location, + expected_account_id_str: &'static str, + } + + let test_cases = vec![ + // DescribeTerminus + TestCase { + description: "DescribeTerminus Parent", + location: Location::new(1, Here), + expected_account_id_str: "5Dt6dpkWPwLaH4BBCKJwjiWrFVAGyYk3tLUabvyn4v7KtESG", + }, + TestCase { + description: "DescribeTerminus Sibling", + location: Location::new(1, [Parachain(1111)]), + expected_account_id_str: "5Eg2fnssmmJnF3z1iZ1NouAuzciDaaDQH7qURAy3w15jULDk", + }, + // DescribePalletTerminal + TestCase { + description: "DescribePalletTerminal Parent", + location: Location::new(1, [PalletInstance(50)]), + expected_account_id_str: "5CnwemvaAXkWFVwibiCvf2EjqwiqBi29S5cLLydZLEaEw6jZ", + }, + TestCase { + description: "DescribePalletTerminal Sibling", + location: Location::new(1, [Parachain(1111), PalletInstance(50)]), + expected_account_id_str: "5GFBgPjpEQPdaxEnFirUoa51u5erVx84twYxJVuBRAT2UP2g", + }, + // DescribeAccountId32Terminal + TestCase { + description: "DescribeAccountId32Terminal Parent", + location: Location::new( + 1, + [Junction::AccountId32 { network: None, id: AccountId::from(ALICE).into() }], + ), + expected_account_id_str: "5DN5SGsuUG7PAqFL47J9meViwdnk9AdeSWKFkcHC45hEzVz4", + }, + TestCase { + description: "DescribeAccountId32Terminal Sibling", + location: Location::new( + 1, + [ + Parachain(1111), + Junction::AccountId32 { network: None, id: AccountId::from(ALICE).into() }, + ], + ), + expected_account_id_str: "5DGRXLYwWGce7wvm14vX1Ms4Vf118FSWQbJkyQigY2pfm6bg", + }, + // DescribeAccountKey20Terminal + TestCase { + description: "DescribeAccountKey20Terminal Parent", + location: Location::new(1, [AccountKey20 { network: None, key: [0u8; 20] }]), + expected_account_id_str: "5F5Ec11567pa919wJkX6VHtv2ZXS5W698YCW35EdEbrg14cg", + }, + TestCase { + description: "DescribeAccountKey20Terminal Sibling", + location: Location::new( + 1, + [Parachain(1111), AccountKey20 { network: None, key: [0u8; 20] }], + ), + expected_account_id_str: "5CB2FbUds2qvcJNhDiTbRZwiS3trAy6ydFGMSVutmYijpPAg", + }, + // DescribeTreasuryVoiceTerminal + TestCase { + description: "DescribeTreasuryVoiceTerminal Parent", + location: Location::new(1, [Plurality { id: BodyId::Treasury, part: BodyPart::Voice }]), + expected_account_id_str: "5CUjnE2vgcUCuhxPwFoQ5r7p1DkhujgvMNDHaF2bLqRp4D5F", + }, + TestCase { + description: "DescribeTreasuryVoiceTerminal Sibling", + location: Location::new( + 1, + [Parachain(1111), Plurality { id: BodyId::Treasury, part: BodyPart::Voice }], + ), + expected_account_id_str: "5G6TDwaVgbWmhqRUKjBhRRnH4ry9L9cjRymUEmiRsLbSE4gB", + }, + // DescribeBodyTerminal + TestCase { + description: "DescribeBodyTerminal Parent", + location: Location::new(1, [Plurality { id: BodyId::Unit, part: BodyPart::Voice }]), + expected_account_id_str: "5EBRMTBkDisEXsaN283SRbzx9Xf2PXwUxxFCJohSGo4jYe6B", + }, + TestCase { + description: "DescribeBodyTerminal Sibling", + location: Location::new( + 1, + [Parachain(1111), Plurality { id: BodyId::Unit, part: BodyPart::Voice }], + ), + expected_account_id_str: "5DBoExvojy8tYnHgLL97phNH975CyT45PWTZEeGoBZfAyRMH", + }, + ]; + + for tc in test_cases { + let expected = + AccountId::from_string(tc.expected_account_id_str).expect("Invalid AccountId string"); + + let got = LocationToAccountHelper::::convert_location( + tc.location.into(), + ) + .unwrap(); + + assert_eq!(got, expected, "{}", tc.description); + } +} diff --git a/cumulus/parachains/runtimes/constants/src/rococo.rs b/cumulus/parachains/runtimes/constants/src/rococo.rs index d10b5e7d3af4368d9bd50664b8be029a3f812795..be4b5c9711ccb0f176f58af771a5226be777456c 100644 --- a/cumulus/parachains/runtimes/constants/src/rococo.rs +++ b/cumulus/parachains/runtimes/constants/src/rococo.rs @@ -148,7 +148,7 @@ pub mod time { pub mod snowbridge { use frame_support::parameter_types; - use xcm::opaque::lts::NetworkId; + use xcm::prelude::{Location, NetworkId}; /// The pallet index of the Ethereum inbound queue pallet in the bridge hub runtime. pub const INBOUND_QUEUE_PALLET_INDEX: u8 = 80; @@ -159,5 +159,11 @@ pub mod snowbridge { /// /// pub EthereumNetwork: NetworkId = NetworkId::Ethereum { chain_id: 11155111 }; + pub EthereumLocation: Location = Location::new(2, EthereumNetwork::get()); } } + +pub mod xcm_version { + /// The default XCM version to set in genesis config. + pub const SAFE_XCM_VERSION: u32 = xcm::prelude::XCM_VERSION; +} diff --git a/cumulus/parachains/runtimes/constants/src/westend.rs b/cumulus/parachains/runtimes/constants/src/westend.rs index fec66cec2eb6a8df284a50b95c6b71c6a0d81c17..8c4c0c594359450dab0cdf4e9e64ed197c3ca308 100644 --- a/cumulus/parachains/runtimes/constants/src/westend.rs +++ b/cumulus/parachains/runtimes/constants/src/westend.rs @@ -171,7 +171,7 @@ pub mod time { pub mod snowbridge { use frame_support::parameter_types; - use xcm::opaque::lts::NetworkId; + use xcm::prelude::{Location, NetworkId}; /// The pallet index of the Ethereum inbound queue pallet in the bridge hub runtime. pub const INBOUND_QUEUE_PALLET_INDEX: u8 = 80; @@ -182,5 +182,11 @@ pub mod snowbridge { /// /// pub EthereumNetwork: NetworkId = NetworkId::Ethereum { chain_id: 11155111 }; + pub EthereumLocation: Location = Location::new(2, EthereumNetwork::get()); } } + +pub mod xcm_version { + /// The default XCM version to set in genesis config. + pub const SAFE_XCM_VERSION: u32 = xcm::prelude::XCM_VERSION; +} diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/contracts.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/contracts.rs index e8cc9d02fb0e4c6ac2db7ada26a641fea055ea76..40801f66a47ba179774fb57cfece417b064ccbd5 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/contracts.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/contracts.rs @@ -72,7 +72,10 @@ impl Config for Runtime { type MaxDebugBufferLen = ConstU32<{ 2 * 1024 * 1024 }>; type MaxDelegateDependencies = ConstU32<32>; type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; - type Migrations = (pallet_contracts::migration::v16::Migration,); + #[cfg(not(feature = "runtime-benchmarks"))] + type Migrations = (); + #[cfg(feature = "runtime-benchmarks")] + type Migrations = pallet_contracts::migration::codegen::BenchMigrations; type RuntimeHoldReason = RuntimeHoldReason; type Debug = (); type Environment = (); diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs index bf173fb618afc91601b11f2a31a93a8a928be15b..3377802e91de1ab6c9bccb7ec9408776d59c0114 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs @@ -27,13 +27,13 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); mod contracts; mod weights; -mod xcm_config; +pub mod xcm_config; extern crate alloc; use alloc::{vec, vec::Vec}; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; -use cumulus_primitives_core::AggregateMessageOrigin; +use cumulus_primitives_core::{AggregateMessageOrigin, ClaimQueueOffset, CoreSelector}; use sp_api::impl_runtime_apis; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ @@ -144,11 +144,11 @@ 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_015_000, + spec_version: 1_016_001, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 7, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. @@ -234,6 +234,7 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<0>; + type DoneSlashHandler = (); } parameter_types! { @@ -293,6 +294,7 @@ impl cumulus_pallet_parachain_system::Config for Runtime { type ReservedXcmpWeight = ReservedXcmpWeight; type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases; type ConsensusHook = ConsensusHook; + type SelectCore = cumulus_pallet_parachain_system::DefaultCoreSelector; } type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< @@ -653,6 +655,12 @@ impl_runtime_apis! { } } + impl cumulus_primitives_core::GetCoreSelectorApi for Runtime { + fn core_selector() -> (CoreSelector, ClaimQueueOffset) { + ParachainSystem::core_selector() + } + } + impl pallet_contracts::ContractsApi for Runtime { fn call( origin: AccountId, diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/weights/block_weights.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/weights/block_weights.rs index e7fdb2aae2a01ec06076de83d94817e540e205dd..41e30725e753fef32404b858a873e2456618d56f 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/weights/block_weights.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/weights/block_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/weights/extrinsic_weights.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/weights/extrinsic_weights.rs index 1a4adb968bb7195428ea00d59cd92dcd3b6eea5f..3bd48f061bba079a76aa8f032ebecd6d40c2156c 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/weights/extrinsic_weights.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/weights/extrinsic_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/weights/mod.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/weights/mod.rs index b473d49e20e67329d893e1e565330cbe9290c64f..850dae6fbd06ea95d3c7c87c09325aacb123c0c4 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/weights/mod.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/weights/paritydb_weights.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/weights/paritydb_weights.rs index 25679703831a13b8d1bb7fb7dd4d92fa84b1f255..e0b1985c659c78b66176423f6bcc64d546b39324 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/weights/paritydb_weights.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/weights/paritydb_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/weights/rocksdb_weights.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/weights/rocksdb_weights.rs index 3dd817aa6f137085b0e5fdf2b11b7f50e5c8b002..c6e91b2fcffbf65e3fdce2bc8fbac19a605d00aa 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/weights/rocksdb_weights.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/weights/rocksdb_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 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 6a41cf75d354693d232b9719289b8edefe1ec835..39fdd30a049857e83733c9223f2ae01fb410edb5 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/xcm_config.rs @@ -40,12 +40,13 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, - DenyReserveTransferToRelayChain, DenyThenTry, EnsureXcmOrigin, FixedWeightBounds, - FrameTransactionalProcessor, FungibleAdapter, IsConcrete, NativeAsset, ParentAsSuperuser, - ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, - WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, + DenyReserveTransferToRelayChain, DenyThenTry, DescribeAllTerminal, DescribeFamily, + EnsureXcmOrigin, FixedWeightBounds, FrameTransactionalProcessor, FungibleAdapter, + HashedDescription, IsConcrete, NativeAsset, ParentAsSuperuser, ParentIsPreset, + RelayChainAsNative, SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, + TrailingSetTopicAsId, UsingComponents, WithComputedOrigin, WithUniqueTopic, + XcmFeeManagerFromComponents, }; use xcm_executor::XcmExecutor; @@ -75,6 +76,8 @@ pub type LocationToAccountId = ( SiblingParachainConvertsVia, // Straight up local `AccountId32` origins just alias directly to `AccountId`. AccountId32Aliases, + // Foreign locations alias into accounts according to a hash of their standard description. + HashedDescription>, ); /// Means for transacting the native currency on this chain. diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/tests/tests.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/tests/tests.rs new file mode 100644 index 0000000000000000000000000000000000000000..02c4b7b3963b55c7100436f426062cf7d4e574dc --- /dev/null +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/tests/tests.rs @@ -0,0 +1,134 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +#![cfg(test)] + +use contracts_rococo_runtime::xcm_config::LocationToAccountId; +use parachains_common::AccountId; +use sp_core::crypto::Ss58Codec; +use xcm::latest::prelude::*; +use xcm_runtime_apis::conversions::LocationToAccountHelper; + +const ALICE: [u8; 32] = [1u8; 32]; + +#[test] +fn location_conversion_works() { + // the purpose of hardcoded values is to catch an unintended location conversion logic change. + struct TestCase { + description: &'static str, + location: Location, + expected_account_id_str: &'static str, + } + + let test_cases = vec![ + // DescribeTerminus + TestCase { + description: "DescribeTerminus Parent", + location: Location::new(1, Here), + expected_account_id_str: "5Dt6dpkWPwLaH4BBCKJwjiWrFVAGyYk3tLUabvyn4v7KtESG", + }, + TestCase { + description: "DescribeTerminus Sibling", + location: Location::new(1, [Parachain(1111)]), + expected_account_id_str: "5Eg2fnssmmJnF3z1iZ1NouAuzciDaaDQH7qURAy3w15jULDk", + }, + // DescribePalletTerminal + TestCase { + description: "DescribePalletTerminal Parent", + location: Location::new(1, [PalletInstance(50)]), + expected_account_id_str: "5CnwemvaAXkWFVwibiCvf2EjqwiqBi29S5cLLydZLEaEw6jZ", + }, + TestCase { + description: "DescribePalletTerminal Sibling", + location: Location::new(1, [Parachain(1111), PalletInstance(50)]), + expected_account_id_str: "5GFBgPjpEQPdaxEnFirUoa51u5erVx84twYxJVuBRAT2UP2g", + }, + // DescribeAccountId32Terminal + TestCase { + description: "DescribeAccountId32Terminal Parent", + location: Location::new( + 1, + [Junction::AccountId32 { network: None, id: AccountId::from(ALICE).into() }], + ), + expected_account_id_str: "5DN5SGsuUG7PAqFL47J9meViwdnk9AdeSWKFkcHC45hEzVz4", + }, + TestCase { + description: "DescribeAccountId32Terminal Sibling", + location: Location::new( + 1, + [ + Parachain(1111), + Junction::AccountId32 { network: None, id: AccountId::from(ALICE).into() }, + ], + ), + expected_account_id_str: "5DGRXLYwWGce7wvm14vX1Ms4Vf118FSWQbJkyQigY2pfm6bg", + }, + // DescribeAccountKey20Terminal + TestCase { + description: "DescribeAccountKey20Terminal Parent", + location: Location::new(1, [AccountKey20 { network: None, key: [0u8; 20] }]), + expected_account_id_str: "5F5Ec11567pa919wJkX6VHtv2ZXS5W698YCW35EdEbrg14cg", + }, + TestCase { + description: "DescribeAccountKey20Terminal Sibling", + location: Location::new( + 1, + [Parachain(1111), AccountKey20 { network: None, key: [0u8; 20] }], + ), + expected_account_id_str: "5CB2FbUds2qvcJNhDiTbRZwiS3trAy6ydFGMSVutmYijpPAg", + }, + // DescribeTreasuryVoiceTerminal + TestCase { + description: "DescribeTreasuryVoiceTerminal Parent", + location: Location::new(1, [Plurality { id: BodyId::Treasury, part: BodyPart::Voice }]), + expected_account_id_str: "5CUjnE2vgcUCuhxPwFoQ5r7p1DkhujgvMNDHaF2bLqRp4D5F", + }, + TestCase { + description: "DescribeTreasuryVoiceTerminal Sibling", + location: Location::new( + 1, + [Parachain(1111), Plurality { id: BodyId::Treasury, part: BodyPart::Voice }], + ), + expected_account_id_str: "5G6TDwaVgbWmhqRUKjBhRRnH4ry9L9cjRymUEmiRsLbSE4gB", + }, + // DescribeBodyTerminal + TestCase { + description: "DescribeBodyTerminal Parent", + location: Location::new(1, [Plurality { id: BodyId::Unit, part: BodyPart::Voice }]), + expected_account_id_str: "5EBRMTBkDisEXsaN283SRbzx9Xf2PXwUxxFCJohSGo4jYe6B", + }, + TestCase { + description: "DescribeBodyTerminal Sibling", + location: Location::new( + 1, + [Parachain(1111), Plurality { id: BodyId::Unit, part: BodyPart::Voice }], + ), + expected_account_id_str: "5DBoExvojy8tYnHgLL97phNH975CyT45PWTZEeGoBZfAyRMH", + }, + ]; + + for tc in test_cases { + let expected = + AccountId::from_string(tc.expected_account_id_str).expect("Invalid AccountId string"); + + let got = LocationToAccountHelper::::convert_location( + tc.location.into(), + ) + .unwrap(); + + assert_eq!(got, expected, "{}", tc.description); + } +} diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/Cargo.toml b/cumulus/parachains/runtimes/coretime/coretime-rococo/Cargo.toml index 07d133c80be7293b2687d92dcf0ef08713239fe7..80417ea00362c69ca738ee02c982695fa6a946fb 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/Cargo.toml @@ -34,6 +34,7 @@ pallet-balances = { workspace = true } pallet-message-queue = { workspace = true } pallet-broker = { workspace = true } pallet-multisig = { workspace = true } +pallet-proxy = { workspace = true } pallet-session = { workspace = true } pallet-sudo = { workspace = true } pallet-timestamp = { workspace = true } @@ -108,6 +109,7 @@ std = [ "pallet-collator-selection/std", "pallet-message-queue/std", "pallet-multisig/std", + "pallet-proxy/std", "pallet-session/std", "pallet-sudo/std", "pallet-timestamp/std", @@ -158,6 +160,7 @@ runtime-benchmarks = [ "pallet-collator-selection/runtime-benchmarks", "pallet-message-queue/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", + "pallet-proxy/runtime-benchmarks", "pallet-sudo/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", "pallet-utility/runtime-benchmarks", @@ -188,6 +191,7 @@ try-runtime = [ "pallet-collator-selection/try-runtime", "pallet-message-queue/try-runtime", "pallet-multisig/try-runtime", + "pallet-proxy/try-runtime", "pallet-session/try-runtime", "pallet-sudo/try-runtime", "pallet-timestamp/try-runtime", diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs index 25324bf1776420bed76df472739f55cce4295ccc..f16dae04f217c819b28f488278833a53929a66a6 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs @@ -36,14 +36,17 @@ pub mod xcm_config; extern crate alloc; use alloc::{vec, vec::Vec}; +use codec::{Decode, Encode, MaxEncodedLen}; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; -use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; +use cumulus_primitives_core::{AggregateMessageOrigin, ClaimQueueOffset, CoreSelector, ParaId}; use frame_support::{ construct_runtime, derive_impl, dispatch::DispatchClass, genesis_builder_helper::{build_state, get_preset}, parameter_types, - traits::{ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, TransformOrigin}, + traits::{ + ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, InstanceFilter, TransformOrigin, + }, weights::{ConstantMultiplier, Weight, WeightToFee as _}, PalletId, }; @@ -65,9 +68,9 @@ use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; pub use sp_runtime::BuildStorage; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::Block as BlockT, + traits::{BlakeTwo256, Block as BlockT}, transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, DispatchError, MultiAddress, Perbill, + ApplyExtrinsicResult, DispatchError, MultiAddress, Perbill, RuntimeDebug, }; #[cfg(feature = "std")] use sp_version::NativeVersion; @@ -146,11 +149,11 @@ 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_015_000, + spec_version: 1_016_001, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 2, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. @@ -246,6 +249,7 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<0>; + type DoneSlashHandler = (); } parameter_types! { @@ -281,6 +285,7 @@ impl cumulus_pallet_parachain_system::Config for Runtime { type ReservedXcmpWeight = ReservedXcmpWeight; type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases; type ConsensusHook = ConsensusHook; + type SelectCore = cumulus_pallet_parachain_system::DefaultCoreSelector; } type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< @@ -438,6 +443,138 @@ impl pallet_multisig::Config for Runtime { type WeightInfo = weights::pallet_multisig::WeightInfo; } +/// The type used to represent the kinds of proxying allowed. +#[derive( + Copy, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Encode, + Decode, + RuntimeDebug, + MaxEncodedLen, + scale_info::TypeInfo, +)] +pub enum ProxyType { + /// Fully permissioned proxy. Can execute any call on behalf of _proxied_. + Any, + /// Can execute any call that does not transfer funds or assets. + NonTransfer, + /// Proxy with the ability to reject time-delay proxy announcements. + CancelProxy, + /// Proxy for all Broker pallet calls. + Broker, + /// Proxy for renewing coretime. + CoretimeRenewer, + /// Proxy able to purchase on-demand coretime credits. + OnDemandPurchaser, + /// Collator selection proxy. Can execute calls related to collator selection mechanism. + Collator, +} +impl Default for ProxyType { + fn default() -> Self { + Self::Any + } +} + +impl InstanceFilter for ProxyType { + fn filter(&self, c: &RuntimeCall) -> bool { + match self { + ProxyType::Any => true, + ProxyType::NonTransfer => !matches!( + c, + RuntimeCall::Balances { .. } | + // `purchase`, `renew`, `transfer` and `purchase_credit` are pretty self explanatory. + RuntimeCall::Broker(pallet_broker::Call::purchase { .. }) | + RuntimeCall::Broker(pallet_broker::Call::renew { .. }) | + RuntimeCall::Broker(pallet_broker::Call::transfer { .. }) | + RuntimeCall::Broker(pallet_broker::Call::purchase_credit { .. }) | + // `pool` doesn't transfer, but it defines the account to be paid for contributions + RuntimeCall::Broker(pallet_broker::Call::pool { .. }) | + // `assign` is essentially a transfer of a region NFT + RuntimeCall::Broker(pallet_broker::Call::assign { .. }) + ), + ProxyType::CancelProxy => matches!( + c, + RuntimeCall::Proxy(pallet_proxy::Call::reject_announcement { .. }) | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ), + ProxyType::Broker => { + matches!( + c, + RuntimeCall::Broker { .. } | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ) + }, + ProxyType::CoretimeRenewer => { + matches!( + c, + RuntimeCall::Broker(pallet_broker::Call::renew { .. }) | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ) + }, + ProxyType::OnDemandPurchaser => { + matches!( + c, + RuntimeCall::Broker(pallet_broker::Call::purchase_credit { .. }) | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ) + }, + ProxyType::Collator => matches!( + c, + RuntimeCall::CollatorSelection { .. } | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ), + } + } + + fn is_superset(&self, o: &Self) -> bool { + match (self, o) { + (x, y) if x == y => true, + (ProxyType::Any, _) => true, + (_, ProxyType::Any) => false, + (ProxyType::Broker, ProxyType::CoretimeRenewer) => true, + (ProxyType::Broker, ProxyType::OnDemandPurchaser) => true, + (ProxyType::NonTransfer, ProxyType::Collator) => true, + _ => false, + } + } +} + +parameter_types! { + // One storage item; key size 32, value size 8; . + pub const ProxyDepositBase: Balance = deposit(1, 40); + // Additional storage item size of 33 bytes. + pub const ProxyDepositFactor: Balance = deposit(0, 33); + pub const MaxProxies: u16 = 32; + // One storage item; key size 32, value size 16 + pub const AnnouncementDepositBase: Balance = deposit(1, 48); + pub const AnnouncementDepositFactor: Balance = deposit(0, 66); + pub const MaxPending: u16 = 32; +} + +impl pallet_proxy::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type ProxyType = ProxyType; + type ProxyDepositBase = ProxyDepositBase; + type ProxyDepositFactor = ProxyDepositFactor; + type MaxProxies = MaxProxies; + type WeightInfo = weights::pallet_proxy::WeightInfo; + type MaxPending = MaxPending; + type CallHasher = BlakeTwo256; + type AnnouncementDepositBase = AnnouncementDepositBase; + type AnnouncementDepositFactor = AnnouncementDepositFactor; +} + impl pallet_utility::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; @@ -481,6 +618,7 @@ construct_runtime!( // Handy utilities. Utility: pallet_utility = 40, Multisig: pallet_multisig = 41, + Proxy: pallet_proxy = 42, // The main stage. Broker: pallet_broker = 50, @@ -504,6 +642,7 @@ mod benches { [pallet_xcm, PalletXcmExtrinsicsBenchmark::] [pallet_message_queue, MessageQueue] [pallet_multisig, Multisig] + [pallet_proxy, Proxy] [pallet_utility, Utility] // NOTE: Make sure you point to the individual modules below. [pallet_xcm_benchmarks::fungible, XcmBalances] @@ -724,6 +863,12 @@ impl_runtime_apis! { } } + impl cumulus_primitives_core::GetCoreSelectorApi for Runtime { + fn core_selector() -> (CoreSelector, ClaimQueueOffset) { + ParachainSystem::core_selector() + } + } + #[cfg(feature = "try-runtime")] impl frame_try_runtime::TryRuntime for Runtime { fn on_runtime_upgrade(checks: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) { diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/block_weights.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/block_weights.rs index b2092d875c8328210667da4cbb95de0642e60ae3..3ff2b3550fbf8511ea0c8ba3dee563b1ebb11b32 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/block_weights.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/block_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) 2022 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/extrinsic_weights.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/extrinsic_weights.rs index 332c3b324bb9c1b386257bf7953d37aba8f5af13..ab951aea5615046de82c8ede1a917e005f0fa5c3 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/extrinsic_weights.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/extrinsic_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) 2022 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 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..216f41a5a666fd0e3f1d9c2b2e05866a1196402a 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/mod.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) 2022 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 @@ -27,6 +27,7 @@ pub mod pallet_broker; pub mod pallet_collator_selection; pub mod pallet_message_queue; pub mod pallet_multisig; +pub mod pallet_proxy; pub mod pallet_session; pub mod pallet_timestamp; pub mod pallet_utility; diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_proxy.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_proxy.rs new file mode 100644 index 0000000000000000000000000000000000000000..5f95906f473ec5c887ac2c1648e06b304af357fa --- /dev/null +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_proxy.rs @@ -0,0 +1,226 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `pallet_proxy` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-07-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-ynta1nyy-project-238-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! EXECUTION: ``, WASM-EXECUTION: `Compiled`, CHAIN: `Some("coretime-rococo-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/production/polkadot-parachain +// benchmark +// pallet +// --chain=coretime-rococo-dev +// --wasm-execution=compiled +// --pallet=pallet_proxy +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=50 +// --repeat=20 +// --json +// --header=./file_header.txt +// --output=./parachains/runtimes/coretime/coretime-rococo/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_proxy`. +pub struct WeightInfo(PhantomData); +impl pallet_proxy::WeightInfo for WeightInfo { + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ยฑ0)` + // Estimated: `4706` + // Minimum execution time: 16_417_000 picoseconds. + Weight::from_parts(17_283_443, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 2_409 + .saturating_add(Weight::from_parts(32_123, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn proxy_announced(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `454 + a * (68 ยฑ0) + p * (37 ยฑ0)` + // Estimated: `5698` + // Minimum execution time: 37_572_000 picoseconds. + Weight::from_parts(37_045_756, 0) + .saturating_add(Weight::from_parts(0, 5698)) + // Standard Error: 2_896 + .saturating_add(Weight::from_parts(139_561, 0).saturating_mul(a.into())) + // Standard Error: 2_993 + .saturating_add(Weight::from_parts(73_270, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn remove_announcement(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `369 + a * (68 ยฑ0)` + // Estimated: `5698` + // Minimum execution time: 24_066_000 picoseconds. + Weight::from_parts(24_711_403, 0) + .saturating_add(Weight::from_parts(0, 5698)) + // Standard Error: 1_626 + .saturating_add(Weight::from_parts(128_391, 0).saturating_mul(a.into())) + // Standard Error: 1_680 + .saturating_add(Weight::from_parts(23_124, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn reject_announcement(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `369 + a * (68 ยฑ0)` + // Estimated: `5698` + // Minimum execution time: 24_162_000 picoseconds. + Weight::from_parts(23_928_058, 0) + .saturating_add(Weight::from_parts(0, 5698)) + // Standard Error: 2_072 + .saturating_add(Weight::from_parts(152_299, 0).saturating_mul(a.into())) + // Standard Error: 2_141 + .saturating_add(Weight::from_parts(39_775, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn announce(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `386 + a * (68 ยฑ0) + p * (37 ยฑ0)` + // Estimated: `5698` + // Minimum execution time: 33_858_000 picoseconds. + Weight::from_parts(33_568_059, 0) + .saturating_add(Weight::from_parts(0, 5698)) + // Standard Error: 1_816 + .saturating_add(Weight::from_parts(134_400, 0).saturating_mul(a.into())) + // Standard Error: 1_876 + .saturating_add(Weight::from_parts(57_028, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn add_proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ยฑ0)` + // Estimated: `4706` + // Minimum execution time: 24_947_000 picoseconds. + Weight::from_parts(26_235_199, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 1_363 + .saturating_add(Weight::from_parts(41_435, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn remove_proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ยฑ0)` + // Estimated: `4706` + // Minimum execution time: 25_186_000 picoseconds. + Weight::from_parts(26_823_133, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 2_259 + .saturating_add(Weight::from_parts(34_224, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn remove_proxies(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ยฑ0)` + // Estimated: `4706` + // Minimum execution time: 22_156_000 picoseconds. + Weight::from_parts(23_304_060, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 1_738 + .saturating_add(Weight::from_parts(39_612, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn create_pure(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `139` + // Estimated: `4706` + // Minimum execution time: 26_914_000 picoseconds. + Weight::from_parts(28_009_062, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 1_978 + .saturating_add(Weight::from_parts(12_255, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[0, 30]`. + fn kill_pure(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `164 + p * (37 ยฑ0)` + // Estimated: `4706` + // Minimum execution time: 23_281_000 picoseconds. + Weight::from_parts(24_392_989, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 2_943 + .saturating_add(Weight::from_parts(30_287, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/paritydb_weights.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/paritydb_weights.rs index 4338d928d807a41cc60ec91d86e91c81bb253631..db09e9de7bdf4ab08bfcd7f18b39b3cb1dac09da 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/paritydb_weights.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/paritydb_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) 2022 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/rocksdb_weights.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/rocksdb_weights.rs index 1d115d963facb39fe29d6258918fda3bc8d94900..855ec356bca93bd7be138d826adddedad26383d7 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/rocksdb_weights.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/rocksdb_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) 2022 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.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 f56a3c42de0210669f55f0f27feb23b44b055cd6..2eae13de2fd4032871b808e00482b0a326b675ef 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/xcm_config.rs @@ -41,11 +41,12 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, - DenyReserveTransferToRelayChain, DenyThenTry, EnsureXcmOrigin, FrameTransactionalProcessor, - FungibleAdapter, IsConcrete, NonFungibleAdapter, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, - TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, + DenyReserveTransferToRelayChain, DenyThenTry, DescribeAllTerminal, DescribeFamily, + EnsureXcmOrigin, FrameTransactionalProcessor, FungibleAdapter, HashedDescription, IsConcrete, + NonFungibleAdapter, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, + UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, }; use xcm_executor::XcmExecutor; @@ -74,6 +75,8 @@ pub type LocationToAccountId = ( SiblingParachainConvertsVia, // Straight up local `AccountId32` origins just alias directly to `AccountId`. AccountId32Aliases, + // Foreign locations alias into accounts according to a hash of their standard description. + HashedDescription>, ); /// Means for transacting the native currency on this chain. diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/tests/tests.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/tests/tests.rs new file mode 100644 index 0000000000000000000000000000000000000000..2cabce567b6e73a8f833d6922539580e6d23e9f6 --- /dev/null +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/tests/tests.rs @@ -0,0 +1,134 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +#![cfg(test)] + +use coretime_rococo_runtime::xcm_config::LocationToAccountId; +use parachains_common::AccountId; +use sp_core::crypto::Ss58Codec; +use xcm::latest::prelude::*; +use xcm_runtime_apis::conversions::LocationToAccountHelper; + +const ALICE: [u8; 32] = [1u8; 32]; + +#[test] +fn location_conversion_works() { + // the purpose of hardcoded values is to catch an unintended location conversion logic change. + struct TestCase { + description: &'static str, + location: Location, + expected_account_id_str: &'static str, + } + + let test_cases = vec![ + // DescribeTerminus + TestCase { + description: "DescribeTerminus Parent", + location: Location::new(1, Here), + expected_account_id_str: "5Dt6dpkWPwLaH4BBCKJwjiWrFVAGyYk3tLUabvyn4v7KtESG", + }, + TestCase { + description: "DescribeTerminus Sibling", + location: Location::new(1, [Parachain(1111)]), + expected_account_id_str: "5Eg2fnssmmJnF3z1iZ1NouAuzciDaaDQH7qURAy3w15jULDk", + }, + // DescribePalletTerminal + TestCase { + description: "DescribePalletTerminal Parent", + location: Location::new(1, [PalletInstance(50)]), + expected_account_id_str: "5CnwemvaAXkWFVwibiCvf2EjqwiqBi29S5cLLydZLEaEw6jZ", + }, + TestCase { + description: "DescribePalletTerminal Sibling", + location: Location::new(1, [Parachain(1111), PalletInstance(50)]), + expected_account_id_str: "5GFBgPjpEQPdaxEnFirUoa51u5erVx84twYxJVuBRAT2UP2g", + }, + // DescribeAccountId32Terminal + TestCase { + description: "DescribeAccountId32Terminal Parent", + location: Location::new( + 1, + [Junction::AccountId32 { network: None, id: AccountId::from(ALICE).into() }], + ), + expected_account_id_str: "5DN5SGsuUG7PAqFL47J9meViwdnk9AdeSWKFkcHC45hEzVz4", + }, + TestCase { + description: "DescribeAccountId32Terminal Sibling", + location: Location::new( + 1, + [ + Parachain(1111), + Junction::AccountId32 { network: None, id: AccountId::from(ALICE).into() }, + ], + ), + expected_account_id_str: "5DGRXLYwWGce7wvm14vX1Ms4Vf118FSWQbJkyQigY2pfm6bg", + }, + // DescribeAccountKey20Terminal + TestCase { + description: "DescribeAccountKey20Terminal Parent", + location: Location::new(1, [AccountKey20 { network: None, key: [0u8; 20] }]), + expected_account_id_str: "5F5Ec11567pa919wJkX6VHtv2ZXS5W698YCW35EdEbrg14cg", + }, + TestCase { + description: "DescribeAccountKey20Terminal Sibling", + location: Location::new( + 1, + [Parachain(1111), AccountKey20 { network: None, key: [0u8; 20] }], + ), + expected_account_id_str: "5CB2FbUds2qvcJNhDiTbRZwiS3trAy6ydFGMSVutmYijpPAg", + }, + // DescribeTreasuryVoiceTerminal + TestCase { + description: "DescribeTreasuryVoiceTerminal Parent", + location: Location::new(1, [Plurality { id: BodyId::Treasury, part: BodyPart::Voice }]), + expected_account_id_str: "5CUjnE2vgcUCuhxPwFoQ5r7p1DkhujgvMNDHaF2bLqRp4D5F", + }, + TestCase { + description: "DescribeTreasuryVoiceTerminal Sibling", + location: Location::new( + 1, + [Parachain(1111), Plurality { id: BodyId::Treasury, part: BodyPart::Voice }], + ), + expected_account_id_str: "5G6TDwaVgbWmhqRUKjBhRRnH4ry9L9cjRymUEmiRsLbSE4gB", + }, + // DescribeBodyTerminal + TestCase { + description: "DescribeBodyTerminal Parent", + location: Location::new(1, [Plurality { id: BodyId::Unit, part: BodyPart::Voice }]), + expected_account_id_str: "5EBRMTBkDisEXsaN283SRbzx9Xf2PXwUxxFCJohSGo4jYe6B", + }, + TestCase { + description: "DescribeBodyTerminal Sibling", + location: Location::new( + 1, + [Parachain(1111), Plurality { id: BodyId::Unit, part: BodyPart::Voice }], + ), + expected_account_id_str: "5DBoExvojy8tYnHgLL97phNH975CyT45PWTZEeGoBZfAyRMH", + }, + ]; + + for tc in test_cases { + let expected = + AccountId::from_string(tc.expected_account_id_str).expect("Invalid AccountId string"); + + let got = LocationToAccountHelper::::convert_location( + tc.location.into(), + ) + .unwrap(); + + assert_eq!(got, expected, "{}", tc.description); + } +} diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/Cargo.toml b/cumulus/parachains/runtimes/coretime/coretime-westend/Cargo.toml index 5029c82f971d134c251da574d396a2eb3d58c25a..25bf777047d0978e430f5cb4aacc97d3223261cf 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/Cargo.toml @@ -34,6 +34,7 @@ pallet-balances = { workspace = true } pallet-message-queue = { workspace = true } pallet-broker = { workspace = true } pallet-multisig = { workspace = true } +pallet-proxy = { workspace = true } pallet-session = { workspace = true } pallet-timestamp = { workspace = true } pallet-transaction-payment = { workspace = true } @@ -108,6 +109,7 @@ std = [ "pallet-collator-selection/std", "pallet-message-queue/std", "pallet-multisig/std", + "pallet-proxy/std", "pallet-session/std", "pallet-timestamp/std", "pallet-transaction-payment-rpc-runtime-api/std", @@ -157,6 +159,7 @@ runtime-benchmarks = [ "pallet-collator-selection/runtime-benchmarks", "pallet-message-queue/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", + "pallet-proxy/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", @@ -186,6 +189,7 @@ try-runtime = [ "pallet-collator-selection/try-runtime", "pallet-message-queue/try-runtime", "pallet-multisig/try-runtime", + "pallet-proxy/try-runtime", "pallet-session/try-runtime", "pallet-timestamp/try-runtime", "pallet-transaction-payment/try-runtime", diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs index a3051e4bf2713a4082f19df12082a4fb532b85bb..e909b804587f8c3792db350c00d394f923428d2f 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs @@ -36,14 +36,17 @@ pub mod xcm_config; extern crate alloc; use alloc::{vec, vec::Vec}; +use codec::{Decode, Encode, MaxEncodedLen}; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; -use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; +use cumulus_primitives_core::{AggregateMessageOrigin, ClaimQueueOffset, CoreSelector, ParaId}; use frame_support::{ construct_runtime, derive_impl, dispatch::DispatchClass, genesis_builder_helper::{build_state, get_preset}, parameter_types, - traits::{ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, TransformOrigin}, + traits::{ + ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, InstanceFilter, TransformOrigin, + }, weights::{ConstantMultiplier, Weight, WeightToFee as _}, PalletId, }; @@ -65,9 +68,9 @@ use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; pub use sp_runtime::BuildStorage; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::Block as BlockT, + traits::{BlakeTwo256, Block as BlockT}, transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, DispatchError, MultiAddress, Perbill, + ApplyExtrinsicResult, DispatchError, MultiAddress, Perbill, RuntimeDebug, }; #[cfg(feature = "std")] use sp_version::NativeVersion; @@ -145,11 +148,11 @@ 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_015_000, + spec_version: 1_016_001, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 2, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. @@ -246,6 +249,7 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<0>; + type DoneSlashHandler = (); } parameter_types! { @@ -281,6 +285,7 @@ impl cumulus_pallet_parachain_system::Config for Runtime { type ReservedXcmpWeight = ReservedXcmpWeight; type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases; type ConsensusHook = ConsensusHook; + type SelectCore = cumulus_pallet_parachain_system::DefaultCoreSelector; } type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< @@ -438,6 +443,138 @@ impl pallet_multisig::Config for Runtime { type WeightInfo = weights::pallet_multisig::WeightInfo; } +/// The type used to represent the kinds of proxying allowed. +#[derive( + Copy, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Encode, + Decode, + RuntimeDebug, + MaxEncodedLen, + scale_info::TypeInfo, +)] +pub enum ProxyType { + /// Fully permissioned proxy. Can execute any call on behalf of _proxied_. + Any, + /// Can execute any call that does not transfer funds or assets. + NonTransfer, + /// Proxy with the ability to reject time-delay proxy announcements. + CancelProxy, + /// Proxy for all Broker pallet calls. + Broker, + /// Proxy for renewing coretime. + CoretimeRenewer, + /// Proxy able to purchase on-demand coretime credits. + OnDemandPurchaser, + /// Collator selection proxy. Can execute calls related to collator selection mechanism. + Collator, +} +impl Default for ProxyType { + fn default() -> Self { + Self::Any + } +} + +impl InstanceFilter for ProxyType { + fn filter(&self, c: &RuntimeCall) -> bool { + match self { + ProxyType::Any => true, + ProxyType::NonTransfer => !matches!( + c, + RuntimeCall::Balances { .. } | + // `purchase`, `renew`, `transfer` and `purchase_credit` are pretty self explanatory. + RuntimeCall::Broker(pallet_broker::Call::purchase { .. }) | + RuntimeCall::Broker(pallet_broker::Call::renew { .. }) | + RuntimeCall::Broker(pallet_broker::Call::transfer { .. }) | + RuntimeCall::Broker(pallet_broker::Call::purchase_credit { .. }) | + // `pool` doesn't transfer, but it defines the account to be paid for contributions + RuntimeCall::Broker(pallet_broker::Call::pool { .. }) | + // `assign` is essentially a transfer of a region NFT + RuntimeCall::Broker(pallet_broker::Call::assign { .. }) + ), + ProxyType::CancelProxy => matches!( + c, + RuntimeCall::Proxy(pallet_proxy::Call::reject_announcement { .. }) | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ), + ProxyType::Broker => { + matches!( + c, + RuntimeCall::Broker { .. } | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ) + }, + ProxyType::CoretimeRenewer => { + matches!( + c, + RuntimeCall::Broker(pallet_broker::Call::renew { .. }) | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ) + }, + ProxyType::OnDemandPurchaser => { + matches!( + c, + RuntimeCall::Broker(pallet_broker::Call::purchase_credit { .. }) | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ) + }, + ProxyType::Collator => matches!( + c, + RuntimeCall::CollatorSelection { .. } | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ), + } + } + + fn is_superset(&self, o: &Self) -> bool { + match (self, o) { + (x, y) if x == y => true, + (ProxyType::Any, _) => true, + (_, ProxyType::Any) => false, + (ProxyType::Broker, ProxyType::CoretimeRenewer) => true, + (ProxyType::Broker, ProxyType::OnDemandPurchaser) => true, + (ProxyType::NonTransfer, ProxyType::Collator) => true, + _ => false, + } + } +} + +parameter_types! { + // One storage item; key size 32, value size 8; . + pub const ProxyDepositBase: Balance = deposit(1, 40); + // Additional storage item size of 33 bytes. + pub const ProxyDepositFactor: Balance = deposit(0, 33); + pub const MaxProxies: u16 = 32; + // One storage item; key size 32, value size 16 + pub const AnnouncementDepositBase: Balance = deposit(1, 48); + pub const AnnouncementDepositFactor: Balance = deposit(0, 66); + pub const MaxPending: u16 = 32; +} + +impl pallet_proxy::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type ProxyType = ProxyType; + type ProxyDepositBase = ProxyDepositBase; + type ProxyDepositFactor = ProxyDepositFactor; + type MaxProxies = MaxProxies; + type WeightInfo = weights::pallet_proxy::WeightInfo; + type MaxPending = MaxPending; + type CallHasher = BlakeTwo256; + type AnnouncementDepositBase = AnnouncementDepositBase; + type AnnouncementDepositFactor = AnnouncementDepositFactor; +} + impl pallet_utility::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; @@ -475,6 +612,7 @@ construct_runtime!( // Handy utilities. Utility: pallet_utility = 40, Multisig: pallet_multisig = 41, + Proxy: pallet_proxy = 42, // The main stage. Broker: pallet_broker = 50, @@ -495,6 +633,7 @@ mod benches { [pallet_xcm, PalletXcmExtrinsicsBenchmark::] [pallet_message_queue, MessageQueue] [pallet_multisig, Multisig] + [pallet_proxy, Proxy] [pallet_utility, Utility] // NOTE: Make sure you point to the individual modules below. [pallet_xcm_benchmarks::fungible, XcmBalances] @@ -715,6 +854,12 @@ impl_runtime_apis! { } } + impl cumulus_primitives_core::GetCoreSelectorApi for Runtime { + fn core_selector() -> (CoreSelector, ClaimQueueOffset) { + ParachainSystem::core_selector() + } + } + #[cfg(feature = "try-runtime")] impl frame_try_runtime::TryRuntime for Runtime { fn on_runtime_upgrade(checks: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) { diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/block_weights.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/block_weights.rs index 2bd7975bf98c36996520716c9dc11822d8287234..e5c41941a1cdbf391b2c9f4859c74a092af0e361 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/block_weights.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/block_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/extrinsic_weights.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/extrinsic_weights.rs index 898d72ec5b19519a77ec0b75bb65d757213b35d4..b72015884393acb36f6cce6695323463b21b7504 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/extrinsic_weights.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/extrinsic_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 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 f1050b3ae636261ff21674c3bb34c05bf6d232c5..216f41a5a666fd0e3f1d9c2b2e05866a1196402a 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/mod.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) 2022 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 @@ -27,6 +27,7 @@ pub mod pallet_broker; pub mod pallet_collator_selection; pub mod pallet_message_queue; pub mod pallet_multisig; +pub mod pallet_proxy; pub mod pallet_session; pub mod pallet_timestamp; pub mod pallet_utility; diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_proxy.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_proxy.rs new file mode 100644 index 0000000000000000000000000000000000000000..d3edc1a8b2008276553dee2cc802c437411b8863 --- /dev/null +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_proxy.rs @@ -0,0 +1,226 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `pallet_proxy` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-07-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-ynta1nyy-project-238-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! EXECUTION: ``, WASM-EXECUTION: `Compiled`, CHAIN: `Some("coretime-westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/production/polkadot-parachain +// benchmark +// pallet +// --chain=coretime-westend-dev +// --wasm-execution=compiled +// --pallet=pallet_proxy +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=50 +// --repeat=20 +// --json +// --header=./file_header.txt +// --output=./parachains/runtimes/coretime/coretime-westend/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_proxy`. +pub struct WeightInfo(PhantomData); +impl pallet_proxy::WeightInfo for WeightInfo { + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ยฑ0)` + // Estimated: `4706` + // Minimum execution time: 16_417_000 picoseconds. + Weight::from_parts(17_283_443, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 2_409 + .saturating_add(Weight::from_parts(32_123, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn proxy_announced(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `454 + a * (68 ยฑ0) + p * (37 ยฑ0)` + // Estimated: `5698` + // Minimum execution time: 37_572_000 picoseconds. + Weight::from_parts(37_045_756, 0) + .saturating_add(Weight::from_parts(0, 5698)) + // Standard Error: 2_896 + .saturating_add(Weight::from_parts(139_561, 0).saturating_mul(a.into())) + // Standard Error: 2_993 + .saturating_add(Weight::from_parts(73_270, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn remove_announcement(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `369 + a * (68 ยฑ0)` + // Estimated: `5698` + // Minimum execution time: 24_066_000 picoseconds. + Weight::from_parts(24_711_403, 0) + .saturating_add(Weight::from_parts(0, 5698)) + // Standard Error: 1_626 + .saturating_add(Weight::from_parts(128_391, 0).saturating_mul(a.into())) + // Standard Error: 1_680 + .saturating_add(Weight::from_parts(23_124, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn reject_announcement(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `369 + a * (68 ยฑ0)` + // Estimated: `5698` + // Minimum execution time: 24_162_000 picoseconds. + Weight::from_parts(23_928_058, 0) + .saturating_add(Weight::from_parts(0, 5698)) + // Standard Error: 2_072 + .saturating_add(Weight::from_parts(152_299, 0).saturating_mul(a.into())) + // Standard Error: 2_141 + .saturating_add(Weight::from_parts(39_775, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn announce(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `386 + a * (68 ยฑ0) + p * (37 ยฑ0)` + // Estimated: `5698` + // Minimum execution time: 33_858_000 picoseconds. + Weight::from_parts(33_568_059, 0) + .saturating_add(Weight::from_parts(0, 5698)) + // Standard Error: 1_816 + .saturating_add(Weight::from_parts(134_400, 0).saturating_mul(a.into())) + // Standard Error: 1_876 + .saturating_add(Weight::from_parts(57_028, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn add_proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ยฑ0)` + // Estimated: `4706` + // Minimum execution time: 24_947_000 picoseconds. + Weight::from_parts(26_235_199, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 1_363 + .saturating_add(Weight::from_parts(41_435, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn remove_proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ยฑ0)` + // Estimated: `4706` + // Minimum execution time: 25_186_000 picoseconds. + Weight::from_parts(26_823_133, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 2_259 + .saturating_add(Weight::from_parts(34_224, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn remove_proxies(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ยฑ0)` + // Estimated: `4706` + // Minimum execution time: 22_156_000 picoseconds. + Weight::from_parts(23_304_060, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 1_738 + .saturating_add(Weight::from_parts(39_612, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn create_pure(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `139` + // Estimated: `4706` + // Minimum execution time: 26_914_000 picoseconds. + Weight::from_parts(28_009_062, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 1_978 + .saturating_add(Weight::from_parts(12_255, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[0, 30]`. + fn kill_pure(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `164 + p * (37 ยฑ0)` + // Estimated: `4706` + // Minimum execution time: 23_281_000 picoseconds. + Weight::from_parts(24_392_989, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 2_943 + .saturating_add(Weight::from_parts(30_287, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/paritydb_weights.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/paritydb_weights.rs index 1c6d2ebe568cc81e91167ec723102eebde49259c..d056c8c46a6c89fc77478d7462f172f87ab1247d 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/paritydb_weights.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/paritydb_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/rocksdb_weights.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/rocksdb_weights.rs index aa0cb2b4bc377bae5bce9b18cbb78820c91f344d..a32b65565a1519bf4ecedd150e65f4b3fac6ffa1 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/rocksdb_weights.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/rocksdb_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.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 da8aa1c18bdf44e50643fe6b85cd755051e23c0f..1205be95c932964b4ebf05171d0544bae3d2694a 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/xcm_config.rs @@ -41,11 +41,12 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, - DenyReserveTransferToRelayChain, DenyThenTry, EnsureXcmOrigin, FrameTransactionalProcessor, - FungibleAdapter, IsConcrete, NonFungibleAdapter, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, - TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, + DenyReserveTransferToRelayChain, DenyThenTry, DescribeAllTerminal, DescribeFamily, + EnsureXcmOrigin, FrameTransactionalProcessor, FungibleAdapter, HashedDescription, IsConcrete, + NonFungibleAdapter, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, + UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, }; use xcm_executor::XcmExecutor; @@ -74,6 +75,8 @@ pub type LocationToAccountId = ( SiblingParachainConvertsVia, // Straight up local `AccountId32` origins just alias directly to `AccountId`. AccountId32Aliases, + // Foreign locations alias into accounts according to a hash of their standard description. + HashedDescription>, ); /// Means for transacting the native currency on this chain. diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/tests/tests.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/tests/tests.rs new file mode 100644 index 0000000000000000000000000000000000000000..e391d71a9ab782e69a0897dc8a7f980190b8c9e1 --- /dev/null +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/tests/tests.rs @@ -0,0 +1,134 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +#![cfg(test)] + +use coretime_westend_runtime::xcm_config::LocationToAccountId; +use parachains_common::AccountId; +use sp_core::crypto::Ss58Codec; +use xcm::latest::prelude::*; +use xcm_runtime_apis::conversions::LocationToAccountHelper; + +const ALICE: [u8; 32] = [1u8; 32]; + +#[test] +fn location_conversion_works() { + // the purpose of hardcoded values is to catch an unintended location conversion logic change. + struct TestCase { + description: &'static str, + location: Location, + expected_account_id_str: &'static str, + } + + let test_cases = vec![ + // DescribeTerminus + TestCase { + description: "DescribeTerminus Parent", + location: Location::new(1, Here), + expected_account_id_str: "5Dt6dpkWPwLaH4BBCKJwjiWrFVAGyYk3tLUabvyn4v7KtESG", + }, + TestCase { + description: "DescribeTerminus Sibling", + location: Location::new(1, [Parachain(1111)]), + expected_account_id_str: "5Eg2fnssmmJnF3z1iZ1NouAuzciDaaDQH7qURAy3w15jULDk", + }, + // DescribePalletTerminal + TestCase { + description: "DescribePalletTerminal Parent", + location: Location::new(1, [PalletInstance(50)]), + expected_account_id_str: "5CnwemvaAXkWFVwibiCvf2EjqwiqBi29S5cLLydZLEaEw6jZ", + }, + TestCase { + description: "DescribePalletTerminal Sibling", + location: Location::new(1, [Parachain(1111), PalletInstance(50)]), + expected_account_id_str: "5GFBgPjpEQPdaxEnFirUoa51u5erVx84twYxJVuBRAT2UP2g", + }, + // DescribeAccountId32Terminal + TestCase { + description: "DescribeAccountId32Terminal Parent", + location: Location::new( + 1, + [Junction::AccountId32 { network: None, id: AccountId::from(ALICE).into() }], + ), + expected_account_id_str: "5DN5SGsuUG7PAqFL47J9meViwdnk9AdeSWKFkcHC45hEzVz4", + }, + TestCase { + description: "DescribeAccountId32Terminal Sibling", + location: Location::new( + 1, + [ + Parachain(1111), + Junction::AccountId32 { network: None, id: AccountId::from(ALICE).into() }, + ], + ), + expected_account_id_str: "5DGRXLYwWGce7wvm14vX1Ms4Vf118FSWQbJkyQigY2pfm6bg", + }, + // DescribeAccountKey20Terminal + TestCase { + description: "DescribeAccountKey20Terminal Parent", + location: Location::new(1, [AccountKey20 { network: None, key: [0u8; 20] }]), + expected_account_id_str: "5F5Ec11567pa919wJkX6VHtv2ZXS5W698YCW35EdEbrg14cg", + }, + TestCase { + description: "DescribeAccountKey20Terminal Sibling", + location: Location::new( + 1, + [Parachain(1111), AccountKey20 { network: None, key: [0u8; 20] }], + ), + expected_account_id_str: "5CB2FbUds2qvcJNhDiTbRZwiS3trAy6ydFGMSVutmYijpPAg", + }, + // DescribeTreasuryVoiceTerminal + TestCase { + description: "DescribeTreasuryVoiceTerminal Parent", + location: Location::new(1, [Plurality { id: BodyId::Treasury, part: BodyPart::Voice }]), + expected_account_id_str: "5CUjnE2vgcUCuhxPwFoQ5r7p1DkhujgvMNDHaF2bLqRp4D5F", + }, + TestCase { + description: "DescribeTreasuryVoiceTerminal Sibling", + location: Location::new( + 1, + [Parachain(1111), Plurality { id: BodyId::Treasury, part: BodyPart::Voice }], + ), + expected_account_id_str: "5G6TDwaVgbWmhqRUKjBhRRnH4ry9L9cjRymUEmiRsLbSE4gB", + }, + // DescribeBodyTerminal + TestCase { + description: "DescribeBodyTerminal Parent", + location: Location::new(1, [Plurality { id: BodyId::Unit, part: BodyPart::Voice }]), + expected_account_id_str: "5EBRMTBkDisEXsaN283SRbzx9Xf2PXwUxxFCJohSGo4jYe6B", + }, + TestCase { + description: "DescribeBodyTerminal Sibling", + location: Location::new( + 1, + [Parachain(1111), Plurality { id: BodyId::Unit, part: BodyPart::Voice }], + ), + expected_account_id_str: "5DBoExvojy8tYnHgLL97phNH975CyT45PWTZEeGoBZfAyRMH", + }, + ]; + + for tc in test_cases { + let expected = + AccountId::from_string(tc.expected_account_id_str).expect("Invalid AccountId string"); + + let got = LocationToAccountHelper::::convert_location( + tc.location.into(), + ) + .unwrap(); + + assert_eq!(got, expected, "{}", tc.description); + } +} diff --git a/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs b/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs index 942e11e0b2574e0d7016f452f6808e8ddf633479..bc76f174b50752a6b10cda99b3451f7374bed6fd 100644 --- a/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs @@ -64,7 +64,7 @@ use sp_runtime::{ use sp_version::NativeVersion; use sp_version::RuntimeVersion; -use cumulus_primitives_core::AggregateMessageOrigin; +use cumulus_primitives_core::{AggregateMessageOrigin, ClaimQueueOffset, CoreSelector}; pub use frame_support::{ construct_runtime, derive_impl, dispatch::DispatchClass, @@ -102,11 +102,11 @@ 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_015_000, + spec_version: 1_016_001, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. @@ -172,8 +172,8 @@ parameter_types! { type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< Runtime, RELAY_CHAIN_SLOT_DURATION_MILLIS, - BLOCK_PROCESSING_VELOCITY, - UNINCLUDED_SEGMENT_CAPACITY, + 3, + 9, >; impl cumulus_pallet_parachain_system::Config for Runtime { @@ -188,6 +188,7 @@ impl cumulus_pallet_parachain_system::Config for Runtime { type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases; type ConsensusHook = ConsensusHook; type WeightInfo = weights::cumulus_pallet_parachain_system::WeightInfo; + type SelectCore = cumulus_pallet_parachain_system::DefaultCoreSelector; } parameter_types! { @@ -234,7 +235,7 @@ impl pallet_aura::Config for Runtime { type DisabledValidators = (); type MaxAuthorities = ConstU32<100_000>; type AllowMultipleBlocksPerSlot = ConstBool; - type SlotDuration = ConstU64; + type SlotDuration = ConstU64<2000>; } impl pallet_glutton::Config for Runtime { @@ -425,7 +426,13 @@ impl_runtime_apis! { } } - impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { + impl cumulus_primitives_core::GetCoreSelectorApi for Runtime { + fn core_selector() -> (CoreSelector, ClaimQueueOffset) { + ParachainSystem::core_selector() + } + } + + impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { fn account_nonce(account: AccountId) -> Nonce { System::account_nonce(account) } diff --git a/cumulus/parachains/runtimes/people/people-rococo/Cargo.toml b/cumulus/parachains/runtimes/people/people-rococo/Cargo.toml index c676587b1de433921b6dba683d2428051f9e5857..c969bb2985bd239781f15fe92acfe4893e14bf89 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/people/people-rococo/Cargo.toml @@ -31,6 +31,7 @@ pallet-balances = { workspace = true } pallet-identity = { workspace = true } pallet-message-queue = { workspace = true } pallet-multisig = { workspace = true } +pallet-proxy = { workspace = true } pallet-session = { workspace = true } pallet-timestamp = { workspace = true } pallet-transaction-payment = { workspace = true } @@ -104,6 +105,7 @@ std = [ "pallet-identity/std", "pallet-message-queue/std", "pallet-multisig/std", + "pallet-proxy/std", "pallet-session/std", "pallet-timestamp/std", "pallet-transaction-payment-rpc-runtime-api/std", @@ -153,6 +155,7 @@ runtime-benchmarks = [ "pallet-identity/runtime-benchmarks", "pallet-message-queue/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", + "pallet-proxy/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", @@ -182,6 +185,7 @@ try-runtime = [ "pallet-identity/try-runtime", "pallet-message-queue/try-runtime", "pallet-multisig/try-runtime", + "pallet-proxy/try-runtime", "pallet-session/try-runtime", "pallet-timestamp/try-runtime", "pallet-transaction-payment/try-runtime", diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs index 77bfb99669c6c2f58d5e6d3398b2d805b4a2ac17..aa04d01cf037267868afe0a5c934dd3afdf240e2 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs @@ -25,15 +25,17 @@ pub mod xcm_config; extern crate alloc; use alloc::{vec, vec::Vec}; +use codec::{Decode, Encode, MaxEncodedLen}; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; -use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; +use cumulus_primitives_core::{AggregateMessageOrigin, ClaimQueueOffset, CoreSelector, ParaId}; use frame_support::{ construct_runtime, derive_impl, dispatch::DispatchClass, genesis_builder_helper::{build_state, get_preset}, parameter_types, traits::{ - ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, Everything, TransformOrigin, + ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, Everything, InstanceFilter, + TransformOrigin, }, weights::{ConstantMultiplier, Weight, WeightToFee as _}, PalletId, @@ -57,11 +59,11 @@ use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; pub use sp_runtime::BuildStorage; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::Block as BlockT, + traits::{BlakeTwo256, Block as BlockT}, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, }; -pub use sp_runtime::{MultiAddress, Perbill, Permill}; +pub use sp_runtime::{MultiAddress, Perbill, Permill, RuntimeDebug}; #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; @@ -109,6 +111,7 @@ pub type UncheckedExtrinsic = /// Migrations to apply on runtime upgrade. pub type Migrations = ( pallet_collator_selection::migration::v2::MigrationToV2, + cumulus_pallet_xcmp_queue::migration::v5::MigrateV4ToV5, // permanent pallet_xcm::migration::MigrateToLatestXcmVersion, ); @@ -134,11 +137,11 @@ 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_015_000, + spec_version: 1_016_001, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. @@ -222,6 +225,7 @@ impl pallet_balances::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<0>; + type DoneSlashHandler = (); } parameter_types! { @@ -257,6 +261,7 @@ impl cumulus_pallet_parachain_system::Config for Runtime { type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases; type ConsensusHook = ConsensusHook; type WeightInfo = weights::cumulus_pallet_parachain_system::WeightInfo; + type SelectCore = cumulus_pallet_parachain_system::DefaultCoreSelector; } type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< @@ -401,6 +406,119 @@ impl pallet_multisig::Config for Runtime { type WeightInfo = weights::pallet_multisig::WeightInfo; } +/// The type used to represent the kinds of proxying allowed. +#[derive( + Copy, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Encode, + Decode, + RuntimeDebug, + MaxEncodedLen, + scale_info::TypeInfo, +)] +pub enum ProxyType { + /// Fully permissioned proxy. Can execute any call on behalf of _proxied_. + Any, + /// Can execute any call that does not transfer funds or assets. + NonTransfer, + /// Proxy with the ability to reject time-delay proxy announcements. + CancelProxy, + /// Proxy for all Identity pallet calls. + Identity, + /// Proxy for identity registrars. + IdentityJudgement, + /// Collator selection proxy. Can execute calls related to collator selection mechanism. + Collator, +} +impl Default for ProxyType { + fn default() -> Self { + Self::Any + } +} + +impl InstanceFilter for ProxyType { + fn filter(&self, c: &RuntimeCall) -> bool { + match self { + ProxyType::Any => true, + ProxyType::NonTransfer => !matches!( + c, + RuntimeCall::Balances { .. } | + // `request_judgement` puts up a deposit to transfer to a registrar + RuntimeCall::Identity(pallet_identity::Call::request_judgement { .. }) + ), + ProxyType::CancelProxy => matches!( + c, + RuntimeCall::Proxy(pallet_proxy::Call::reject_announcement { .. }) | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ), + ProxyType::Identity => { + matches!( + c, + RuntimeCall::Identity { .. } | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ) + }, + ProxyType::IdentityJudgement => matches!( + c, + RuntimeCall::Identity(pallet_identity::Call::provide_judgement { .. }) | + RuntimeCall::Utility(..) | + RuntimeCall::Multisig { .. } + ), + ProxyType::Collator => matches!( + c, + RuntimeCall::CollatorSelection { .. } | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ), + } + } + + fn is_superset(&self, o: &Self) -> bool { + match (self, o) { + (x, y) if x == y => true, + (ProxyType::Any, _) => true, + (_, ProxyType::Any) => false, + (ProxyType::Identity, ProxyType::IdentityJudgement) => true, + (ProxyType::NonTransfer, ProxyType::IdentityJudgement) => true, + (ProxyType::NonTransfer, ProxyType::Collator) => true, + _ => false, + } + } +} + +parameter_types! { + // One storage item; key size 32, value size 8. + pub const ProxyDepositBase: Balance = deposit(1, 40); + // Additional storage item size of 33 bytes. + pub const ProxyDepositFactor: Balance = deposit(0, 33); + pub const MaxProxies: u16 = 32; + // One storage item; key size 32, value size 16. + pub const AnnouncementDepositBase: Balance = deposit(1, 48); + pub const AnnouncementDepositFactor: Balance = deposit(0, 66); + pub const MaxPending: u16 = 32; +} + +impl pallet_proxy::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type ProxyType = ProxyType; + type ProxyDepositBase = ProxyDepositBase; + type ProxyDepositFactor = ProxyDepositFactor; + type MaxProxies = MaxProxies; + type WeightInfo = weights::pallet_proxy::WeightInfo; + type MaxPending = MaxPending; + type CallHasher = BlakeTwo256; + type AnnouncementDepositBase = AnnouncementDepositBase; + type AnnouncementDepositFactor = AnnouncementDepositFactor; +} + impl pallet_utility::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; @@ -446,6 +564,7 @@ construct_runtime!( // Handy utilities. Utility: pallet_utility = 40, Multisig: pallet_multisig = 41, + Proxy: pallet_proxy = 42, // The main stage. Identity: pallet_identity = 50, @@ -464,6 +583,7 @@ mod benches { [pallet_identity, Identity] [pallet_message_queue, MessageQueue] [pallet_multisig, Multisig] + [pallet_proxy, Proxy] [pallet_session, SessionBench::] [pallet_utility, Utility] [pallet_timestamp, Timestamp] @@ -687,6 +807,12 @@ impl_runtime_apis! { } } + impl cumulus_primitives_core::GetCoreSelectorApi for Runtime { + fn core_selector() -> (CoreSelector, ClaimQueueOffset) { + ParachainSystem::core_selector() + } + } + #[cfg(feature = "try-runtime")] impl frame_try_runtime::TryRuntime for Runtime { fn on_runtime_upgrade(checks: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) { diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/block_weights.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/block_weights.rs index b2092d875c8328210667da4cbb95de0642e60ae3..3ff2b3550fbf8511ea0c8ba3dee563b1ebb11b32 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/weights/block_weights.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/block_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) 2022 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/extrinsic_weights.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/extrinsic_weights.rs index 332c3b324bb9c1b386257bf7953d37aba8f5af13..ab951aea5615046de82c8ede1a917e005f0fa5c3 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/weights/extrinsic_weights.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/extrinsic_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) 2022 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 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..dce959e817becee6c2d568fed290b8ff96477652 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/mod.rs @@ -25,6 +25,7 @@ pub mod pallet_collator_selection; pub mod pallet_identity; pub mod pallet_message_queue; pub mod pallet_multisig; +pub mod pallet_proxy; pub mod pallet_session; pub mod pallet_timestamp; pub mod pallet_utility; diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_proxy.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_proxy.rs new file mode 100644 index 0000000000000000000000000000000000000000..264213c94d449a787ce46a6ba962150f2fe813e4 --- /dev/null +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/pallet_proxy.rs @@ -0,0 +1,226 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `pallet_proxy` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-07-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-ynta1nyy-project-238-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! EXECUTION: ``, WASM-EXECUTION: `Compiled`, CHAIN: `Some("people-rococo-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/production/polkadot-parachain +// benchmark +// pallet +// --chain=people-rococo-dev +// --wasm-execution=compiled +// --pallet=pallet_proxy +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=50 +// --repeat=20 +// --json +// --header=./file_header.txt +// --output=./parachains/runtimes/people/people-rococo/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_proxy`. +pub struct WeightInfo(PhantomData); +impl pallet_proxy::WeightInfo for WeightInfo { + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ยฑ0)` + // Estimated: `4706` + // Minimum execution time: 16_417_000 picoseconds. + Weight::from_parts(17_283_443, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 2_409 + .saturating_add(Weight::from_parts(32_123, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn proxy_announced(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `454 + a * (68 ยฑ0) + p * (37 ยฑ0)` + // Estimated: `5698` + // Minimum execution time: 37_572_000 picoseconds. + Weight::from_parts(37_045_756, 0) + .saturating_add(Weight::from_parts(0, 5698)) + // Standard Error: 2_896 + .saturating_add(Weight::from_parts(139_561, 0).saturating_mul(a.into())) + // Standard Error: 2_993 + .saturating_add(Weight::from_parts(73_270, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn remove_announcement(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `369 + a * (68 ยฑ0)` + // Estimated: `5698` + // Minimum execution time: 24_066_000 picoseconds. + Weight::from_parts(24_711_403, 0) + .saturating_add(Weight::from_parts(0, 5698)) + // Standard Error: 1_626 + .saturating_add(Weight::from_parts(128_391, 0).saturating_mul(a.into())) + // Standard Error: 1_680 + .saturating_add(Weight::from_parts(23_124, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn reject_announcement(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `369 + a * (68 ยฑ0)` + // Estimated: `5698` + // Minimum execution time: 24_162_000 picoseconds. + Weight::from_parts(23_928_058, 0) + .saturating_add(Weight::from_parts(0, 5698)) + // Standard Error: 2_072 + .saturating_add(Weight::from_parts(152_299, 0).saturating_mul(a.into())) + // Standard Error: 2_141 + .saturating_add(Weight::from_parts(39_775, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn announce(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `386 + a * (68 ยฑ0) + p * (37 ยฑ0)` + // Estimated: `5698` + // Minimum execution time: 33_858_000 picoseconds. + Weight::from_parts(33_568_059, 0) + .saturating_add(Weight::from_parts(0, 5698)) + // Standard Error: 1_816 + .saturating_add(Weight::from_parts(134_400, 0).saturating_mul(a.into())) + // Standard Error: 1_876 + .saturating_add(Weight::from_parts(57_028, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn add_proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ยฑ0)` + // Estimated: `4706` + // Minimum execution time: 24_947_000 picoseconds. + Weight::from_parts(26_235_199, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 1_363 + .saturating_add(Weight::from_parts(41_435, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn remove_proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ยฑ0)` + // Estimated: `4706` + // Minimum execution time: 25_186_000 picoseconds. + Weight::from_parts(26_823_133, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 2_259 + .saturating_add(Weight::from_parts(34_224, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn remove_proxies(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ยฑ0)` + // Estimated: `4706` + // Minimum execution time: 22_156_000 picoseconds. + Weight::from_parts(23_304_060, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 1_738 + .saturating_add(Weight::from_parts(39_612, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn create_pure(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `139` + // Estimated: `4706` + // Minimum execution time: 26_914_000 picoseconds. + Weight::from_parts(28_009_062, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 1_978 + .saturating_add(Weight::from_parts(12_255, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[0, 30]`. + fn kill_pure(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `164 + p * (37 ยฑ0)` + // Estimated: `4706` + // Minimum execution time: 23_281_000 picoseconds. + Weight::from_parts(24_392_989, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 2_943 + .saturating_add(Weight::from_parts(30_287, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/paritydb_weights.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/paritydb_weights.rs index 4338d928d807a41cc60ec91d86e91c81bb253631..db09e9de7bdf4ab08bfcd7f18b39b3cb1dac09da 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/weights/paritydb_weights.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/paritydb_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) 2022 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/weights/rocksdb_weights.rs b/cumulus/parachains/runtimes/people/people-rococo/src/weights/rocksdb_weights.rs index 1d115d963facb39fe29d6258918fda3bc8d94900..855ec356bca93bd7be138d826adddedad26383d7 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/weights/rocksdb_weights.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/weights/rocksdb_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) 2022 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 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 96ab3eafa785fe0135916d072666d7102e5027c3..a2e20e2778b621326ff39624aef261a863665d77 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/xcm_config.rs @@ -38,12 +38,13 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, - DenyReserveTransferToRelayChain, DenyThenTry, DescribeTerminus, EnsureXcmOrigin, - FrameTransactionalProcessor, FungibleAdapter, HashedDescription, IsConcrete, ParentAsSuperuser, - ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, - WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, + DenyReserveTransferToRelayChain, DenyThenTry, DescribeAllTerminal, DescribeFamily, + DescribeTerminus, EnsureXcmOrigin, FrameTransactionalProcessor, FungibleAdapter, + HashedDescription, IsConcrete, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, + SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, + TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, + XcmFeeManagerFromComponents, }; use xcm_executor::XcmExecutor; @@ -93,6 +94,8 @@ pub type LocationToAccountId = ( AccountId32Aliases, // Here/local root location to `AccountId`. HashedDescription, + // Foreign locations alias into accounts according to a hash of their standard description. + HashedDescription>, ); /// Means for transacting the native currency on this chain. diff --git a/cumulus/parachains/runtimes/people/people-rococo/tests/tests.rs b/cumulus/parachains/runtimes/people/people-rococo/tests/tests.rs new file mode 100644 index 0000000000000000000000000000000000000000..3627d9c40ec2b5754553a123fd58c84d33b75d06 --- /dev/null +++ b/cumulus/parachains/runtimes/people/people-rococo/tests/tests.rs @@ -0,0 +1,134 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +#![cfg(test)] + +use parachains_common::AccountId; +use people_rococo_runtime::xcm_config::LocationToAccountId; +use sp_core::crypto::Ss58Codec; +use xcm::latest::prelude::*; +use xcm_runtime_apis::conversions::LocationToAccountHelper; + +const ALICE: [u8; 32] = [1u8; 32]; + +#[test] +fn location_conversion_works() { + // the purpose of hardcoded values is to catch an unintended location conversion logic change. + struct TestCase { + description: &'static str, + location: Location, + expected_account_id_str: &'static str, + } + + let test_cases = vec![ + // DescribeTerminus + TestCase { + description: "DescribeTerminus Parent", + location: Location::new(1, Here), + expected_account_id_str: "5Dt6dpkWPwLaH4BBCKJwjiWrFVAGyYk3tLUabvyn4v7KtESG", + }, + TestCase { + description: "DescribeTerminus Sibling", + location: Location::new(1, [Parachain(1111)]), + expected_account_id_str: "5Eg2fnssmmJnF3z1iZ1NouAuzciDaaDQH7qURAy3w15jULDk", + }, + // DescribePalletTerminal + TestCase { + description: "DescribePalletTerminal Parent", + location: Location::new(1, [PalletInstance(50)]), + expected_account_id_str: "5CnwemvaAXkWFVwibiCvf2EjqwiqBi29S5cLLydZLEaEw6jZ", + }, + TestCase { + description: "DescribePalletTerminal Sibling", + location: Location::new(1, [Parachain(1111), PalletInstance(50)]), + expected_account_id_str: "5GFBgPjpEQPdaxEnFirUoa51u5erVx84twYxJVuBRAT2UP2g", + }, + // DescribeAccountId32Terminal + TestCase { + description: "DescribeAccountId32Terminal Parent", + location: Location::new( + 1, + [Junction::AccountId32 { network: None, id: AccountId::from(ALICE).into() }], + ), + expected_account_id_str: "5DN5SGsuUG7PAqFL47J9meViwdnk9AdeSWKFkcHC45hEzVz4", + }, + TestCase { + description: "DescribeAccountId32Terminal Sibling", + location: Location::new( + 1, + [ + Parachain(1111), + Junction::AccountId32 { network: None, id: AccountId::from(ALICE).into() }, + ], + ), + expected_account_id_str: "5DGRXLYwWGce7wvm14vX1Ms4Vf118FSWQbJkyQigY2pfm6bg", + }, + // DescribeAccountKey20Terminal + TestCase { + description: "DescribeAccountKey20Terminal Parent", + location: Location::new(1, [AccountKey20 { network: None, key: [0u8; 20] }]), + expected_account_id_str: "5F5Ec11567pa919wJkX6VHtv2ZXS5W698YCW35EdEbrg14cg", + }, + TestCase { + description: "DescribeAccountKey20Terminal Sibling", + location: Location::new( + 1, + [Parachain(1111), AccountKey20 { network: None, key: [0u8; 20] }], + ), + expected_account_id_str: "5CB2FbUds2qvcJNhDiTbRZwiS3trAy6ydFGMSVutmYijpPAg", + }, + // DescribeTreasuryVoiceTerminal + TestCase { + description: "DescribeTreasuryVoiceTerminal Parent", + location: Location::new(1, [Plurality { id: BodyId::Treasury, part: BodyPart::Voice }]), + expected_account_id_str: "5CUjnE2vgcUCuhxPwFoQ5r7p1DkhujgvMNDHaF2bLqRp4D5F", + }, + TestCase { + description: "DescribeTreasuryVoiceTerminal Sibling", + location: Location::new( + 1, + [Parachain(1111), Plurality { id: BodyId::Treasury, part: BodyPart::Voice }], + ), + expected_account_id_str: "5G6TDwaVgbWmhqRUKjBhRRnH4ry9L9cjRymUEmiRsLbSE4gB", + }, + // DescribeBodyTerminal + TestCase { + description: "DescribeBodyTerminal Parent", + location: Location::new(1, [Plurality { id: BodyId::Unit, part: BodyPart::Voice }]), + expected_account_id_str: "5EBRMTBkDisEXsaN283SRbzx9Xf2PXwUxxFCJohSGo4jYe6B", + }, + TestCase { + description: "DescribeBodyTerminal Sibling", + location: Location::new( + 1, + [Parachain(1111), Plurality { id: BodyId::Unit, part: BodyPart::Voice }], + ), + expected_account_id_str: "5DBoExvojy8tYnHgLL97phNH975CyT45PWTZEeGoBZfAyRMH", + }, + ]; + + for tc in test_cases { + let expected = + AccountId::from_string(tc.expected_account_id_str).expect("Invalid AccountId string"); + + let got = LocationToAccountHelper::::convert_location( + tc.location.into(), + ) + .unwrap(); + + assert_eq!(got, expected, "{}", tc.description); + } +} diff --git a/cumulus/parachains/runtimes/people/people-westend/Cargo.toml b/cumulus/parachains/runtimes/people/people-westend/Cargo.toml index ab7dd04bb78a3be348a5d71343ed14be9d0a96ec..64e956d8b6b5a506e72f725e6edebc69ef7bdef2 100644 --- a/cumulus/parachains/runtimes/people/people-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/people/people-westend/Cargo.toml @@ -31,6 +31,7 @@ pallet-balances = { workspace = true } pallet-identity = { workspace = true } pallet-message-queue = { workspace = true } pallet-multisig = { workspace = true } +pallet-proxy = { workspace = true } pallet-session = { workspace = true } pallet-timestamp = { workspace = true } pallet-transaction-payment = { workspace = true } @@ -104,6 +105,7 @@ std = [ "pallet-identity/std", "pallet-message-queue/std", "pallet-multisig/std", + "pallet-proxy/std", "pallet-session/std", "pallet-timestamp/std", "pallet-transaction-payment-rpc-runtime-api/std", @@ -153,6 +155,7 @@ runtime-benchmarks = [ "pallet-identity/runtime-benchmarks", "pallet-message-queue/runtime-benchmarks", "pallet-multisig/runtime-benchmarks", + "pallet-proxy/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", @@ -182,6 +185,7 @@ try-runtime = [ "pallet-identity/try-runtime", "pallet-message-queue/try-runtime", "pallet-multisig/try-runtime", + "pallet-proxy/try-runtime", "pallet-session/try-runtime", "pallet-timestamp/try-runtime", "pallet-transaction-payment/try-runtime", diff --git a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs index 3343d2be749d6570915afc60fe40b8a1bd5eaf77..c8131c94f15b93f58c4d5cea4bf2e986bf658532 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs @@ -25,15 +25,17 @@ pub mod xcm_config; extern crate alloc; use alloc::{vec, vec::Vec}; +use codec::{Decode, Encode, MaxEncodedLen}; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; -use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; +use cumulus_primitives_core::{AggregateMessageOrigin, ClaimQueueOffset, CoreSelector, ParaId}; use frame_support::{ construct_runtime, derive_impl, dispatch::DispatchClass, genesis_builder_helper::{build_state, get_preset}, parameter_types, traits::{ - ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, Everything, TransformOrigin, + ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, Everything, InstanceFilter, + TransformOrigin, }, weights::{ConstantMultiplier, Weight, WeightToFee as _}, PalletId, @@ -57,11 +59,11 @@ use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; pub use sp_runtime::BuildStorage; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::Block as BlockT, + traits::{BlakeTwo256, Block as BlockT}, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, }; -pub use sp_runtime::{MultiAddress, Perbill, Permill}; +pub use sp_runtime::{MultiAddress, Perbill, Permill, RuntimeDebug}; #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; @@ -134,11 +136,11 @@ 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_015_000, + spec_version: 1_016_001, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. @@ -222,6 +224,7 @@ impl pallet_balances::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<0>; + type DoneSlashHandler = (); } parameter_types! { @@ -257,6 +260,7 @@ impl cumulus_pallet_parachain_system::Config for Runtime { type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases; type ConsensusHook = ConsensusHook; type WeightInfo = weights::cumulus_pallet_parachain_system::WeightInfo; + type SelectCore = cumulus_pallet_parachain_system::DefaultCoreSelector; } type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< @@ -401,6 +405,119 @@ impl pallet_multisig::Config for Runtime { type WeightInfo = weights::pallet_multisig::WeightInfo; } +/// The type used to represent the kinds of proxying allowed. +#[derive( + Copy, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Encode, + Decode, + RuntimeDebug, + MaxEncodedLen, + scale_info::TypeInfo, +)] +pub enum ProxyType { + /// Fully permissioned proxy. Can execute any call on behalf of _proxied_. + Any, + /// Can execute any call that does not transfer funds or assets. + NonTransfer, + /// Proxy with the ability to reject time-delay proxy announcements. + CancelProxy, + /// Proxy for all Identity pallet calls. + Identity, + /// Proxy for identity registrars. + IdentityJudgement, + /// Collator selection proxy. Can execute calls related to collator selection mechanism. + Collator, +} +impl Default for ProxyType { + fn default() -> Self { + Self::Any + } +} + +impl InstanceFilter for ProxyType { + fn filter(&self, c: &RuntimeCall) -> bool { + match self { + ProxyType::Any => true, + ProxyType::NonTransfer => !matches!( + c, + RuntimeCall::Balances { .. } | + // `request_judgement` puts up a deposit to transfer to a registrar + RuntimeCall::Identity(pallet_identity::Call::request_judgement { .. }) + ), + ProxyType::CancelProxy => matches!( + c, + RuntimeCall::Proxy(pallet_proxy::Call::reject_announcement { .. }) | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ), + ProxyType::Identity => { + matches!( + c, + RuntimeCall::Identity { .. } | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ) + }, + ProxyType::IdentityJudgement => matches!( + c, + RuntimeCall::Identity(pallet_identity::Call::provide_judgement { .. }) | + RuntimeCall::Utility(..) | + RuntimeCall::Multisig { .. } + ), + ProxyType::Collator => matches!( + c, + RuntimeCall::CollatorSelection { .. } | + RuntimeCall::Utility { .. } | + RuntimeCall::Multisig { .. } + ), + } + } + + fn is_superset(&self, o: &Self) -> bool { + match (self, o) { + (x, y) if x == y => true, + (ProxyType::Any, _) => true, + (_, ProxyType::Any) => false, + (ProxyType::Identity, ProxyType::IdentityJudgement) => true, + (ProxyType::NonTransfer, ProxyType::IdentityJudgement) => true, + (ProxyType::NonTransfer, ProxyType::Collator) => true, + _ => false, + } + } +} + +parameter_types! { + // One storage item; key size 32, value size 8. + pub const ProxyDepositBase: Balance = deposit(1, 40); + // Additional storage item size of 33 bytes. + pub const ProxyDepositFactor: Balance = deposit(0, 33); + pub const MaxProxies: u16 = 32; + // One storage item; key size 32, value size 16. + pub const AnnouncementDepositBase: Balance = deposit(1, 48); + pub const AnnouncementDepositFactor: Balance = deposit(0, 66); + pub const MaxPending: u16 = 32; +} + +impl pallet_proxy::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type ProxyType = ProxyType; + type ProxyDepositBase = ProxyDepositBase; + type ProxyDepositFactor = ProxyDepositFactor; + type MaxProxies = MaxProxies; + type WeightInfo = weights::pallet_proxy::WeightInfo; + type MaxPending = MaxPending; + type CallHasher = BlakeTwo256; + type AnnouncementDepositBase = AnnouncementDepositBase; + type AnnouncementDepositFactor = AnnouncementDepositFactor; +} + impl pallet_utility::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; @@ -446,6 +563,7 @@ construct_runtime!( // Handy utilities. Utility: pallet_utility = 40, Multisig: pallet_multisig = 41, + Proxy: pallet_proxy = 42, // The main stage. Identity: pallet_identity = 50, @@ -464,6 +582,7 @@ mod benches { [pallet_identity, Identity] [pallet_message_queue, MessageQueue] [pallet_multisig, Multisig] + [pallet_proxy, Proxy] [pallet_session, SessionBench::] [pallet_utility, Utility] [pallet_timestamp, Timestamp] @@ -687,6 +806,12 @@ impl_runtime_apis! { } } + impl cumulus_primitives_core::GetCoreSelectorApi for Runtime { + fn core_selector() -> (CoreSelector, ClaimQueueOffset) { + ParachainSystem::core_selector() + } + } + #[cfg(feature = "try-runtime")] impl frame_try_runtime::TryRuntime for Runtime { fn on_runtime_upgrade(checks: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) { diff --git a/cumulus/parachains/runtimes/people/people-westend/src/weights/block_weights.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/block_weights.rs index 2bd7975bf98c36996520716c9dc11822d8287234..e5c41941a1cdbf391b2c9f4859c74a092af0e361 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/weights/block_weights.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/block_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/people/people-westend/src/weights/extrinsic_weights.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/extrinsic_weights.rs index 898d72ec5b19519a77ec0b75bb65d757213b35d4..b72015884393acb36f6cce6695323463b21b7504 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/weights/extrinsic_weights.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/extrinsic_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 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..dce959e817becee6c2d568fed290b8ff96477652 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/mod.rs @@ -25,6 +25,7 @@ pub mod pallet_collator_selection; pub mod pallet_identity; pub mod pallet_message_queue; pub mod pallet_multisig; +pub mod pallet_proxy; pub mod pallet_session; pub mod pallet_timestamp; pub mod pallet_utility; diff --git a/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_proxy.rs b/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_proxy.rs new file mode 100644 index 0000000000000000000000000000000000000000..e962123f216c2d75bdbc9b8a05944ac6ce5e8b26 --- /dev/null +++ b/cumulus/parachains/runtimes/people/people-westend/src/weights/pallet_proxy.rs @@ -0,0 +1,226 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `pallet_proxy` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-07-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-ynta1nyy-project-238-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! EXECUTION: ``, WASM-EXECUTION: `Compiled`, CHAIN: `Some("people-westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// ./target/production/polkadot-parachain +// benchmark +// pallet +// --chain=people-westend-dev +// --wasm-execution=compiled +// --pallet=pallet_proxy +// --no-storage-info +// --no-median-slopes +// --no-min-squares +// --extrinsic=* +// --steps=50 +// --repeat=20 +// --json +// --header=./file_header.txt +// --output=./parachains/runtimes/people/people-westend/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_proxy`. +pub struct WeightInfo(PhantomData); +impl pallet_proxy::WeightInfo for WeightInfo { + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ยฑ0)` + // Estimated: `4706` + // Minimum execution time: 16_417_000 picoseconds. + Weight::from_parts(17_283_443, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 2_409 + .saturating_add(Weight::from_parts(32_123, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn proxy_announced(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `454 + a * (68 ยฑ0) + p * (37 ยฑ0)` + // Estimated: `5698` + // Minimum execution time: 37_572_000 picoseconds. + Weight::from_parts(37_045_756, 0) + .saturating_add(Weight::from_parts(0, 5698)) + // Standard Error: 2_896 + .saturating_add(Weight::from_parts(139_561, 0).saturating_mul(a.into())) + // Standard Error: 2_993 + .saturating_add(Weight::from_parts(73_270, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn remove_announcement(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `369 + a * (68 ยฑ0)` + // Estimated: `5698` + // Minimum execution time: 24_066_000 picoseconds. + Weight::from_parts(24_711_403, 0) + .saturating_add(Weight::from_parts(0, 5698)) + // Standard Error: 1_626 + .saturating_add(Weight::from_parts(128_391, 0).saturating_mul(a.into())) + // Standard Error: 1_680 + .saturating_add(Weight::from_parts(23_124, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn reject_announcement(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `369 + a * (68 ยฑ0)` + // Estimated: `5698` + // Minimum execution time: 24_162_000 picoseconds. + Weight::from_parts(23_928_058, 0) + .saturating_add(Weight::from_parts(0, 5698)) + // Standard Error: 2_072 + .saturating_add(Weight::from_parts(152_299, 0).saturating_mul(a.into())) + // Standard Error: 2_141 + .saturating_add(Weight::from_parts(39_775, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Proxy::Proxies` (r:1 w:0) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// Storage: `Proxy::Announcements` (r:1 w:1) + /// Proof: `Proxy::Announcements` (`max_values`: None, `max_size`: Some(2233), added: 4708, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// The range of component `a` is `[0, 31]`. + /// The range of component `p` is `[1, 31]`. + fn announce(a: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `386 + a * (68 ยฑ0) + p * (37 ยฑ0)` + // Estimated: `5698` + // Minimum execution time: 33_858_000 picoseconds. + Weight::from_parts(33_568_059, 0) + .saturating_add(Weight::from_parts(0, 5698)) + // Standard Error: 1_816 + .saturating_add(Weight::from_parts(134_400, 0).saturating_mul(a.into())) + // Standard Error: 1_876 + .saturating_add(Weight::from_parts(57_028, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn add_proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ยฑ0)` + // Estimated: `4706` + // Minimum execution time: 24_947_000 picoseconds. + Weight::from_parts(26_235_199, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 1_363 + .saturating_add(Weight::from_parts(41_435, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn remove_proxy(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ยฑ0)` + // Estimated: `4706` + // Minimum execution time: 25_186_000 picoseconds. + Weight::from_parts(26_823_133, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 2_259 + .saturating_add(Weight::from_parts(34_224, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn remove_proxies(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `127 + p * (37 ยฑ0)` + // Estimated: `4706` + // Minimum execution time: 22_156_000 picoseconds. + Weight::from_parts(23_304_060, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 1_738 + .saturating_add(Weight::from_parts(39_612, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[1, 31]`. + fn create_pure(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `139` + // Estimated: `4706` + // Minimum execution time: 26_914_000 picoseconds. + Weight::from_parts(28_009_062, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 1_978 + .saturating_add(Weight::from_parts(12_255, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `Proxy::Proxies` (r:1 w:1) + /// Proof: `Proxy::Proxies` (`max_values`: None, `max_size`: Some(1241), added: 3716, mode: `MaxEncodedLen`) + /// The range of component `p` is `[0, 30]`. + fn kill_pure(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `164 + p * (37 ยฑ0)` + // Estimated: `4706` + // Minimum execution time: 23_281_000 picoseconds. + Weight::from_parts(24_392_989, 0) + .saturating_add(Weight::from_parts(0, 4706)) + // Standard Error: 2_943 + .saturating_add(Weight::from_parts(30_287, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/cumulus/parachains/runtimes/people/people-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/people/people-westend/src/xcm_config.rs index f35e920d7cb752a4e6e8e427294726f1854e5bb4..bec5b923d8ad9a23f1b176c9c4f909855df17422 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/xcm_config.rs @@ -38,12 +38,13 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, - DenyReserveTransferToRelayChain, DenyThenTry, DescribeTerminus, EnsureXcmOrigin, - FrameTransactionalProcessor, FungibleAdapter, HashedDescription, IsConcrete, ParentAsSuperuser, - ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, SiblingParachainAsNative, - SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, - WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, + DenyReserveTransferToRelayChain, DenyThenTry, DescribeAllTerminal, DescribeFamily, + DescribeTerminus, EnsureXcmOrigin, FrameTransactionalProcessor, FungibleAdapter, + HashedDescription, IsConcrete, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, + SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, + TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, + XcmFeeManagerFromComponents, }; use xcm_executor::XcmExecutor; @@ -93,6 +94,8 @@ pub type LocationToAccountId = ( AccountId32Aliases, // Here/local root location to `AccountId`. HashedDescription, + // Foreign locations alias into accounts according to a hash of their standard description. + HashedDescription>, ); /// Means for transacting the native currency on this chain. diff --git a/cumulus/parachains/runtimes/people/people-westend/tests/tests.rs b/cumulus/parachains/runtimes/people/people-westend/tests/tests.rs new file mode 100644 index 0000000000000000000000000000000000000000..fa9331952b4b53a6012ad72d0d9b1bba2f4afec7 --- /dev/null +++ b/cumulus/parachains/runtimes/people/people-westend/tests/tests.rs @@ -0,0 +1,134 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +#![cfg(test)] + +use parachains_common::AccountId; +use people_westend_runtime::xcm_config::LocationToAccountId; +use sp_core::crypto::Ss58Codec; +use xcm::latest::prelude::*; +use xcm_runtime_apis::conversions::LocationToAccountHelper; + +const ALICE: [u8; 32] = [1u8; 32]; + +#[test] +fn location_conversion_works() { + // the purpose of hardcoded values is to catch an unintended location conversion logic change. + struct TestCase { + description: &'static str, + location: Location, + expected_account_id_str: &'static str, + } + + let test_cases = vec![ + // DescribeTerminus + TestCase { + description: "DescribeTerminus Parent", + location: Location::new(1, Here), + expected_account_id_str: "5Dt6dpkWPwLaH4BBCKJwjiWrFVAGyYk3tLUabvyn4v7KtESG", + }, + TestCase { + description: "DescribeTerminus Sibling", + location: Location::new(1, [Parachain(1111)]), + expected_account_id_str: "5Eg2fnssmmJnF3z1iZ1NouAuzciDaaDQH7qURAy3w15jULDk", + }, + // DescribePalletTerminal + TestCase { + description: "DescribePalletTerminal Parent", + location: Location::new(1, [PalletInstance(50)]), + expected_account_id_str: "5CnwemvaAXkWFVwibiCvf2EjqwiqBi29S5cLLydZLEaEw6jZ", + }, + TestCase { + description: "DescribePalletTerminal Sibling", + location: Location::new(1, [Parachain(1111), PalletInstance(50)]), + expected_account_id_str: "5GFBgPjpEQPdaxEnFirUoa51u5erVx84twYxJVuBRAT2UP2g", + }, + // DescribeAccountId32Terminal + TestCase { + description: "DescribeAccountId32Terminal Parent", + location: Location::new( + 1, + [Junction::AccountId32 { network: None, id: AccountId::from(ALICE).into() }], + ), + expected_account_id_str: "5DN5SGsuUG7PAqFL47J9meViwdnk9AdeSWKFkcHC45hEzVz4", + }, + TestCase { + description: "DescribeAccountId32Terminal Sibling", + location: Location::new( + 1, + [ + Parachain(1111), + Junction::AccountId32 { network: None, id: AccountId::from(ALICE).into() }, + ], + ), + expected_account_id_str: "5DGRXLYwWGce7wvm14vX1Ms4Vf118FSWQbJkyQigY2pfm6bg", + }, + // DescribeAccountKey20Terminal + TestCase { + description: "DescribeAccountKey20Terminal Parent", + location: Location::new(1, [AccountKey20 { network: None, key: [0u8; 20] }]), + expected_account_id_str: "5F5Ec11567pa919wJkX6VHtv2ZXS5W698YCW35EdEbrg14cg", + }, + TestCase { + description: "DescribeAccountKey20Terminal Sibling", + location: Location::new( + 1, + [Parachain(1111), AccountKey20 { network: None, key: [0u8; 20] }], + ), + expected_account_id_str: "5CB2FbUds2qvcJNhDiTbRZwiS3trAy6ydFGMSVutmYijpPAg", + }, + // DescribeTreasuryVoiceTerminal + TestCase { + description: "DescribeTreasuryVoiceTerminal Parent", + location: Location::new(1, [Plurality { id: BodyId::Treasury, part: BodyPart::Voice }]), + expected_account_id_str: "5CUjnE2vgcUCuhxPwFoQ5r7p1DkhujgvMNDHaF2bLqRp4D5F", + }, + TestCase { + description: "DescribeTreasuryVoiceTerminal Sibling", + location: Location::new( + 1, + [Parachain(1111), Plurality { id: BodyId::Treasury, part: BodyPart::Voice }], + ), + expected_account_id_str: "5G6TDwaVgbWmhqRUKjBhRRnH4ry9L9cjRymUEmiRsLbSE4gB", + }, + // DescribeBodyTerminal + TestCase { + description: "DescribeBodyTerminal Parent", + location: Location::new(1, [Plurality { id: BodyId::Unit, part: BodyPart::Voice }]), + expected_account_id_str: "5EBRMTBkDisEXsaN283SRbzx9Xf2PXwUxxFCJohSGo4jYe6B", + }, + TestCase { + description: "DescribeBodyTerminal Sibling", + location: Location::new( + 1, + [Parachain(1111), Plurality { id: BodyId::Unit, part: BodyPart::Voice }], + ), + expected_account_id_str: "5DBoExvojy8tYnHgLL97phNH975CyT45PWTZEeGoBZfAyRMH", + }, + ]; + + for tc in test_cases { + let expected = + AccountId::from_string(tc.expected_account_id_str).expect("Invalid AccountId string"); + + let got = LocationToAccountHelper::::convert_location( + tc.location.into(), + ) + .unwrap(); + + assert_eq!(got, expected, "{}", tc.description); + } +} diff --git a/cumulus/parachains/runtimes/starters/seedling/Cargo.toml b/cumulus/parachains/runtimes/starters/seedling/Cargo.toml deleted file mode 100644 index c76c09a31234e6077f97e38c7e38fe4fbddeae86..0000000000000000000000000000000000000000 --- a/cumulus/parachains/runtimes/starters/seedling/Cargo.toml +++ /dev/null @@ -1,79 +0,0 @@ -[package] -name = "seedling-runtime" -version = "0.7.0" -description = "Seedling parachain runtime. A starter runtime for solochain to parachain migration." -authors.workspace = true -edition.workspace = true -license = "Apache-2.0" - -[lints] -workspace = true - -[dependencies] -codec = { features = ["derive"], workspace = true } -scale-info = { features = ["derive"], workspace = true } - -# Substrate -frame-executive = { workspace = true } -frame-support = { workspace = true } -frame-system = { workspace = true } -pallet-aura = { workspace = true } -pallet-balances = { workspace = true } -pallet-sudo = { workspace = true } -pallet-timestamp = { workspace = true } -sp-api = { workspace = true } -sp-block-builder = { workspace = true } -sp-consensus-aura = { workspace = true } -sp-core = { workspace = true } -sp-genesis-builder = { workspace = true } -sp-inherents = { workspace = true } -sp-offchain = { workspace = true } -sp-runtime = { workspace = true } -sp-session = { workspace = true } -sp-transaction-pool = { workspace = true } -sp-version = { workspace = true } - -# Cumulus -cumulus-pallet-aura-ext = { workspace = true } -cumulus-pallet-parachain-system = { workspace = true } -cumulus-pallet-solo-to-para = { workspace = true } -cumulus-primitives-core = { workspace = true } -cumulus-primitives-timestamp = { workspace = true } -parachain-info = { workspace = true } -parachains-common = { workspace = true } - -[build-dependencies] -substrate-wasm-builder = { optional = true, workspace = true, default-features = true } - -[features] -default = ["std"] -std = [ - "codec/std", - "cumulus-pallet-aura-ext/std", - "cumulus-pallet-parachain-system/std", - "cumulus-pallet-solo-to-para/std", - "cumulus-primitives-core/std", - "cumulus-primitives-timestamp/std", - "frame-executive/std", - "frame-support/std", - "frame-system/std", - "pallet-aura/std", - "pallet-balances/std", - "pallet-sudo/std", - "pallet-timestamp/std", - "parachain-info/std", - "parachains-common/std", - "scale-info/std", - "sp-api/std", - "sp-block-builder/std", - "sp-consensus-aura/std", - "sp-core/std", - "sp-genesis-builder/std", - "sp-inherents/std", - "sp-offchain/std", - "sp-runtime/std", - "sp-session/std", - "sp-transaction-pool/std", - "sp-version/std", - "substrate-wasm-builder", -] diff --git a/cumulus/parachains/runtimes/starters/seedling/src/lib.rs b/cumulus/parachains/runtimes/starters/seedling/src/lib.rs deleted file mode 100644 index 1fe72604d3731eed1dee3d27477e0e829cccfba0..0000000000000000000000000000000000000000 --- a/cumulus/parachains/runtimes/starters/seedling/src/lib.rs +++ /dev/null @@ -1,392 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Cumulus. - -// Cumulus is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Cumulus is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Cumulus. If not, see . - -//! # Seedling Runtime -//! -//! Seedling is a parachain meant to help parachain auction winners migrate a blockchain from -//! another consensus system into the consensus system of a given Relay Chain. - -#![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")); - -extern crate alloc; - -use alloc::{vec, vec::Vec}; -use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; -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}, - transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, -}; -#[cfg(feature = "std")] -use sp_version::NativeVersion; -use sp_version::RuntimeVersion; - -// A few exports that help ease life for downstream crates. -pub use frame_support::{ - construct_runtime, derive_impl, - dispatch::DispatchClass, - genesis_builder_helper::{build_state, get_preset}, - parameter_types, - traits::{ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, IsInVec, Randomness}, - weights::{ - constants::{ - BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND, - }, - IdentityFee, Weight, - }, - StorageValue, -}; -use frame_system::limits::{BlockLength, BlockWeights}; -use parachains_common::{AccountId, Signature}; -#[cfg(any(feature = "std", test))] -pub use sp_runtime::BuildStorage; -pub use sp_runtime::{Perbill, Permill}; - -impl_opaque_keys! { - pub struct SessionKeys { - pub aura: Aura, - } -} - -/// This runtime version. -#[sp_version::runtime_version] -pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("seedling"), - impl_name: create_runtime_str!("seedling"), - authoring_version: 1, - spec_version: 1, - impl_version: 0, - apis: RUNTIME_API_VERSIONS, - transaction_version: 2, - state_version: 0, -}; - -/// The version information used to identify this runtime when compiled natively. -#[cfg(feature = "std")] -pub fn native_version() -> NativeVersion { - NativeVersion { runtime_version: VERSION, can_author_with: Default::default() } -} - -/// Maximum number of blocks simultaneously accepted by the Runtime, not yet included -/// into the relay chain. -const UNINCLUDED_SEGMENT_CAPACITY: u32 = 1; -/// 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; -/// Relay chain slot duration, in milliseconds. -const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000; - -/// We assume that ~10% of the block weight is consumed by `on_initialize` handlers. -/// This is used to limit the maximal weight of a single extrinsic. -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_div(2), - cumulus_primitives_core::relay_chain::MAX_POV_SIZE as u64, -); - -parameter_types! { - pub const BlockHashCount: BlockNumber = 250; - pub const Version: RuntimeVersion = VERSION; - pub RuntimeBlockLength: BlockLength = - BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); - pub RuntimeBlockWeights: BlockWeights = 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_class(DispatchClass::Operational, |weights| { - weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT); - // Operational transactions have some extra reserved space, so that they - // are included even if block reached `MAXIMUM_BLOCK_WEIGHT`. - weights.reserved = Some( - MAXIMUM_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT - ); - }) - .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO) - .build_or_panic(); - pub const SS58Prefix: u8 = 42; -} - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl frame_system::Config for Runtime { - /// The identifier used to distinguish between accounts. - type AccountId = AccountId; - /// The aggregated dispatch type that is available for extrinsics. - type RuntimeCall = RuntimeCall; - /// The lookup mechanism to get account ID from whatever is passed in dispatchers. - type Lookup = AccountIdLookup; - /// The index type for storing how many extrinsics an account has signed. - type Nonce = Nonce; - /// The type for hashing blocks and tries. - type Hash = Hash; - /// The hashing algorithm used. - type Hashing = BlakeTwo256; - /// The block type. - type Block = Block; - /// The ubiquitous event type. - type RuntimeEvent = RuntimeEvent; - /// The ubiquitous origin type. - type RuntimeOrigin = RuntimeOrigin; - /// Maximum number of block number to block hash mappings to keep (oldest pruned first). - type BlockHashCount = BlockHashCount; - /// Runtime version. - type Version = Version; - /// Converts a module to an index of this module in the runtime. - type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type DbWeight = (); - type BaseCallFilter = frame_support::traits::Everything; - type SystemWeightInfo = (); - type BlockWeights = RuntimeBlockWeights; - type BlockLength = RuntimeBlockLength; - type SS58Prefix = SS58Prefix; - type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode; - type MaxConsumers = frame_support::traits::ConstU32<16>; -} - -impl pallet_sudo::Config for Runtime { - type RuntimeCall = RuntimeCall; - type RuntimeEvent = RuntimeEvent; - type WeightInfo = pallet_sudo::weights::SubstrateWeight; -} - -impl cumulus_pallet_solo_to_para::Config for Runtime { - type RuntimeEvent = RuntimeEvent; -} - -impl cumulus_pallet_parachain_system::Config for Runtime { - type WeightInfo = (); - type RuntimeEvent = RuntimeEvent; - type OnSystemEvent = cumulus_pallet_solo_to_para::Pallet; - type SelfParaId = parachain_info::Pallet; - type OutboundXcmpMessageSource = (); - // Ignore all DMP messages by enqueueing them into `()`: - type DmpQueue = frame_support::traits::EnqueueWithOrigin<(), sp_core::ConstU8<0>>; - type ReservedDmpWeight = (); - type XcmpMessageHandler = (); - type ReservedXcmpWeight = (); - type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases; - 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 {} - -impl cumulus_pallet_aura_ext::Config for Runtime {} - -impl pallet_aura::Config for Runtime { - type AuthorityId = AuraId; - type DisabledValidators = (); - type MaxAuthorities = ConstU32<100_000>; - type AllowMultipleBlocksPerSlot = ConstBool; - type SlotDuration = pallet_aura::MinimumPeriodTimesTwo; -} - -impl pallet_timestamp::Config for Runtime { - type Moment = u64; - type OnTimestampSet = Aura; - type MinimumPeriod = ConstU64<0>; - type WeightInfo = (); -} - -construct_runtime! { - pub enum Runtime - { - System: frame_system, - Sudo: pallet_sudo, - Timestamp: pallet_timestamp, - - ParachainSystem: cumulus_pallet_parachain_system, - ParachainInfo: parachain_info, - SoloToPara: cumulus_pallet_solo_to_para, - Aura: pallet_aura, - AuraExt: cumulus_pallet_aura_ext, - } -} - -/// Index of a transaction in the chain. -pub type Nonce = u32; -/// A hash of some data used by the chain. -pub type Hash = sp_core::H256; -/// An index to a block. -pub type BlockNumber = u32; -/// The address format for describing accounts. -pub type Address = sp_runtime::MultiAddress; -/// Block header type as expected by this runtime. -pub type Header = generic::Header; -/// Block type as expected by this runtime. -pub type Block = generic::Block; -/// A Block signed with a Justification -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 = ( - frame_system::CheckSpecVersion, - frame_system::CheckTxVersion, - frame_system::CheckGenesis, - frame_system::CheckEra, - frame_system::CheckNonce, - pallet_sudo::CheckOnlySudoAccount, -); -/// Unchecked extrinsic type as expected by this runtime. -pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; - -/// Executive: handles dispatch to the various modules. -pub type Executive = frame_executive::Executive< - Runtime, - Block, - frame_system::ChainContext, - Runtime, - AllPalletsWithSystem, ->; - -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()) - } - - fn authorities() -> Vec { - pallet_aura::Authorities::::get().into_inner() - } - } - - impl sp_api::Core for Runtime { - fn version() -> RuntimeVersion { - VERSION - } - - fn execute_block(block: Block) { - Executive::execute_block(block) - } - - fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { - Executive::initialize_block(header) - } - } - - impl sp_api::Metadata for Runtime { - fn metadata() -> OpaqueMetadata { - OpaqueMetadata::new(Runtime::metadata().into()) - } - - fn metadata_at_version(version: u32) -> Option { - Runtime::metadata_at_version(version) - } - - fn metadata_versions() -> alloc::vec::Vec { - Runtime::metadata_versions() - } - } - - impl sp_block_builder::BlockBuilder for Runtime { - fn apply_extrinsic( - extrinsic: ::Extrinsic, - ) -> ApplyExtrinsicResult { - Executive::apply_extrinsic(extrinsic) - } - - fn finalize_block() -> ::Header { - Executive::finalize_block() - } - - fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<::Extrinsic> { - data.create_extrinsics() - } - - fn check_inherents(block: Block, data: sp_inherents::InherentData) -> sp_inherents::CheckInherentsResult { - data.check_extrinsics(&block) - } - } - - impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { - fn validate_transaction( - source: TransactionSource, - tx: ::Extrinsic, - block_hash: ::Hash, - ) -> TransactionValidity { - Executive::validate_transaction(source, tx, block_hash) - } - } - - impl sp_offchain::OffchainWorkerApi for Runtime { - fn offchain_worker(header: &::Header) { - Executive::offchain_worker(header) - } - } - - impl sp_session::SessionKeys for Runtime { - fn generate_session_keys(seed: Option>) -> Vec { - SessionKeys::generate(seed) - } - - fn decode_session_keys( - encoded: Vec, - ) -> Option, KeyTypeId)>> { - SessionKeys::decode_into_raw_public_keys(&encoded) - } - } - - impl cumulus_primitives_core::CollectCollationInfo for Runtime { - fn collect_collation_info(header: &::Header) -> cumulus_primitives_core::CollationInfo { - ParachainSystem::collect_collation_info(header) - } - } - - impl sp_genesis_builder::GenesisBuilder for Runtime { - fn build_state(config: Vec) -> sp_genesis_builder::Result { - build_state::(config) - } - - fn get_preset(id: &Option) -> Option> { - get_preset::(id, |_| None) - } - - fn preset_names() -> Vec { - vec![] - } - } -} - -cumulus_pallet_parachain_system::register_validate_block! { - Runtime = Runtime, - BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::, -} diff --git a/cumulus/parachains/runtimes/starters/shell/Cargo.toml b/cumulus/parachains/runtimes/starters/shell/Cargo.toml deleted file mode 100644 index 8f3b2204cfe34b8bb73c6a9062c9a91df262a8e4..0000000000000000000000000000000000000000 --- a/cumulus/parachains/runtimes/starters/shell/Cargo.toml +++ /dev/null @@ -1,99 +0,0 @@ -[package] -name = "shell-runtime" -version = "0.7.0" -description = "A minimal runtime to test Relay Chain consensus." -authors.workspace = true -edition.workspace = true -license = "Apache-2.0" - -[lints] -workspace = true - -[dependencies] -codec = { features = ["derive"], workspace = true } -scale-info = { features = ["derive"], workspace = true } - -# Substrate -frame-executive = { workspace = true } -frame-support = { workspace = true } -frame-system = { workspace = true } -frame-try-runtime = { optional = true, workspace = true } -pallet-aura = { workspace = true } -pallet-timestamp = { workspace = true } -sp-api = { workspace = true } -sp-block-builder = { workspace = true } -sp-consensus-aura = { workspace = true } -sp-core = { workspace = true } -sp-genesis-builder = { workspace = true } -sp-inherents = { workspace = true } -sp-offchain = { workspace = true } -sp-runtime = { workspace = true } -sp-session = { workspace = true } -sp-transaction-pool = { workspace = true } -sp-version = { workspace = true } -pallet-message-queue = { workspace = true } - -# Polkadot -xcm = { workspace = true } -xcm-builder = { workspace = true } -xcm-executor = { workspace = true } - -# Cumulus -cumulus-pallet-aura-ext = { workspace = true } -cumulus-pallet-parachain-system = { workspace = true } -cumulus-pallet-xcm = { workspace = true } -cumulus-primitives-core = { workspace = true } -parachain-info = { workspace = true } -parachains-common = { workspace = true } - -[build-dependencies] -substrate-wasm-builder = { optional = true, workspace = true, default-features = true } - -[features] -default = ["std"] -std = [ - "codec/std", - "cumulus-pallet-aura-ext/std", - "cumulus-pallet-parachain-system/std", - "cumulus-pallet-xcm/std", - "cumulus-primitives-core/std", - "frame-executive/std", - "frame-support/std", - "frame-system/std", - "frame-try-runtime?/std", - "pallet-aura/std", - "pallet-message-queue/std", - "pallet-timestamp/std", - "parachain-info/std", - "parachains-common/std", - "scale-info/std", - "sp-api/std", - "sp-block-builder/std", - "sp-consensus-aura/std", - "sp-core/std", - "sp-genesis-builder/std", - "sp-inherents/std", - "sp-offchain/std", - "sp-runtime/std", - "sp-session/std", - "sp-transaction-pool/std", - "sp-version/std", - "substrate-wasm-builder", - "xcm-builder/std", - "xcm-executor/std", - "xcm/std", -] -try-runtime = [ - "cumulus-pallet-aura-ext/try-runtime", - "cumulus-pallet-parachain-system/try-runtime", - "cumulus-pallet-xcm/try-runtime", - "frame-executive/try-runtime", - "frame-support/try-runtime", - "frame-system/try-runtime", - "frame-try-runtime/try-runtime", - "pallet-aura/try-runtime", - "pallet-message-queue/try-runtime", - "pallet-timestamp/try-runtime", - "parachain-info/try-runtime", - "sp-runtime/try-runtime", -] diff --git a/cumulus/parachains/runtimes/starters/shell/src/lib.rs b/cumulus/parachains/runtimes/starters/shell/src/lib.rs deleted file mode 100644 index 1dfbe2b6c41c840846e1d64a4d87e7c9c1b0c7cf..0000000000000000000000000000000000000000 --- a/cumulus/parachains/runtimes/starters/shell/src/lib.rs +++ /dev/null @@ -1,450 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Cumulus. - -// Cumulus is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Cumulus is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Cumulus. If not, see . - -//! # Shell Runtime -//! -//! The Shell runtime defines a minimal parachain. It can listen for a downward message authorizing -//! an upgrade into another parachain. -//! -//! Generally (so far) only used as the first parachain on a Relay. - -#![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")); - -pub mod xcm_config; - -extern crate alloc; - -use alloc::{vec, vec::Vec}; -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}, - ApplyExtrinsicResult, -}; -#[cfg(feature = "std")] -use sp_version::NativeVersion; -use sp_version::RuntimeVersion; - -// A few exports that help ease life for downstream crates. -pub use frame_support::{ - construct_runtime, derive_impl, - dispatch::DispatchClass, - genesis_builder_helper::{build_state, get_preset}, - parameter_types, - traits::{ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, IsInVec, Randomness}, - weights::{ - constants::{ - BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND, - }, - IdentityFee, Weight, - }, - StorageValue, -}; -use frame_system::limits::{BlockLength, BlockWeights}; -use parachains_common::{AccountId, Signature}; -#[cfg(any(feature = "std", test))] -pub use sp_runtime::BuildStorage; -pub use sp_runtime::{Perbill, Permill}; - -impl_opaque_keys! { - pub struct SessionKeys { - pub aura: Aura, - } -} - -/// This runtime version. -#[sp_version::runtime_version] -pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("shell"), - impl_name: create_runtime_str!("shell"), - authoring_version: 1, - spec_version: 2, - impl_version: 0, - apis: RUNTIME_API_VERSIONS, - transaction_version: 1, - state_version: 0, -}; - -/// The version information used to identify this runtime when compiled natively. -#[cfg(feature = "std")] -pub fn native_version() -> NativeVersion { - NativeVersion { runtime_version: VERSION, can_author_with: Default::default() } -} - -/// Maximum number of blocks simultaneously accepted by the Runtime, not yet included -/// into the relay chain. -const UNINCLUDED_SEGMENT_CAPACITY: u32 = 1; -/// 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; -/// Relay chain slot duration, in milliseconds. -const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000; - -/// We assume that ~10% of the block weight is consumed by `on_initialize` handlers. -/// This is used to limit the maximal weight of a single extrinsic. -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_div(2), - cumulus_primitives_core::relay_chain::MAX_POV_SIZE as u64, -); - -parameter_types! { - pub const BlockHashCount: BlockNumber = 250; - pub const Version: RuntimeVersion = VERSION; - pub RuntimeBlockLength: BlockLength = - BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); - pub RuntimeBlockWeights: BlockWeights = 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_class(DispatchClass::Operational, |weights| { - weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT); - // Operational transactions have some extra reserved space, so that they - // are included even if block reached `MAXIMUM_BLOCK_WEIGHT`. - weights.reserved = Some( - MAXIMUM_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT - ); - }) - .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO) - .build_or_panic(); - pub const SS58Prefix: u8 = 42; -} - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl frame_system::Config for Runtime { - /// The identifier used to distinguish between accounts. - type AccountId = AccountId; - /// The aggregated dispatch type that is available for extrinsics. - type RuntimeCall = RuntimeCall; - /// The lookup mechanism to get account ID from whatever is passed in dispatchers. - type Lookup = AccountIdLookup; - /// The index type for storing how many extrinsics an account has signed. - type Nonce = Nonce; - /// The type for hashing blocks and tries. - type Hash = Hash; - /// The hashing algorithm used. - type Hashing = BlakeTwo256; - /// The block type. - type Block = Block; - /// The ubiquitous event type. - type RuntimeEvent = RuntimeEvent; - /// The ubiquitous origin type. - type RuntimeOrigin = RuntimeOrigin; - /// Maximum number of block number to block hash mappings to keep (oldest pruned first). - type BlockHashCount = BlockHashCount; - /// Runtime version. - type Version = Version; - /// Converts a module to an index of this module in the runtime. - type PalletInfo = PalletInfo; - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type DbWeight = (); - type BaseCallFilter = frame_support::traits::Everything; - type SystemWeightInfo = (); - type BlockWeights = RuntimeBlockWeights; - type BlockLength = RuntimeBlockLength; - type SS58Prefix = SS58Prefix; - type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode; - type MaxConsumers = frame_support::traits::ConstU32<16>; -} - -parameter_types! { - pub const RelayOrigin: AggregateMessageOrigin = AggregateMessageOrigin::Parent; - pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); -} - -impl cumulus_pallet_parachain_system::Config for Runtime { - type WeightInfo = (); - type RuntimeEvent = RuntimeEvent; - type OnSystemEvent = (); - type SelfParaId = parachain_info::Pallet; - type OutboundXcmpMessageSource = (); - type DmpQueue = frame_support::traits::EnqueueWithOrigin; - type ReservedDmpWeight = ReservedDmpWeight; - type XcmpMessageHandler = (); - type ReservedXcmpWeight = (); - type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases; - 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! { - pub MessageQueueServiceWeight: Weight = Perbill::from_percent(35) * RuntimeBlockWeights::get().max_block; -} - -impl pallet_message_queue::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); - #[cfg(feature = "runtime-benchmarks")] - type MessageProcessor = pallet_message_queue::mock_helpers::NoopMessageProcessor< - cumulus_primitives_core::AggregateMessageOrigin, - >; - #[cfg(not(feature = "runtime-benchmarks"))] - type MessageProcessor = xcm_builder::ProcessXcmMessage< - AggregateMessageOrigin, - xcm_executor::XcmExecutor, - RuntimeCall, - >; - type Size = u32; - // These need to be configured to the XCMP pallet - if it is deployed. - type QueueChangeHandler = (); - type QueuePausedQuery = (); - type HeapSize = sp_core::ConstU32<{ 103 * 1024 }>; - type MaxStale = sp_core::ConstU32<8>; - type ServiceWeight = MessageQueueServiceWeight; - type IdleMaxServiceWeight = MessageQueueServiceWeight; -} - -impl cumulus_pallet_aura_ext::Config for Runtime {} - -impl pallet_aura::Config for Runtime { - type AuthorityId = AuraId; - type DisabledValidators = (); - type MaxAuthorities = ConstU32<100_000>; - type AllowMultipleBlocksPerSlot = ConstBool; - type SlotDuration = pallet_aura::MinimumPeriodTimesTwo; -} - -impl pallet_timestamp::Config for Runtime { - type Moment = u64; - type OnTimestampSet = Aura; - type MinimumPeriod = ConstU64<0>; - type WeightInfo = (); -} - -construct_runtime! { - pub enum Runtime - { - System: frame_system, - Timestamp: pallet_timestamp, - - ParachainSystem: cumulus_pallet_parachain_system, - ParachainInfo: parachain_info, - - CumulusXcm: cumulus_pallet_xcm, - MessageQueue: pallet_message_queue, - - Aura: pallet_aura, - AuraExt: cumulus_pallet_aura_ext, - } -} - -/// 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 { - const IDENTIFIER: &'static str = "DisallowSigned"; - type AccountId = AccountId; - type Call = RuntimeCall; - type AdditionalSigned = (); - type Pre = (); - fn additional_signed( - &self, - ) -> core::result::Result<(), sp_runtime::transaction_validity::TransactionValidityError> { - Ok(()) - } - 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, - _who: &Self::AccountId, - _call: &Self::Call, - _info: &sp_runtime::traits::DispatchInfoOf, - _len: usize, - ) -> TransactionValidity { - let i = sp_runtime::transaction_validity::InvalidTransaction::BadProof; - Err(sp_runtime::transaction_validity::TransactionValidityError::Invalid(i)) - } -} - -/// Index of a transaction in the chain. -pub type Nonce = u32; -/// A hash of some data used by the chain. -pub type Hash = sp_core::H256; -/// An index to a block. -pub type BlockNumber = u32; -/// The address format for describing accounts. -pub type Address = sp_runtime::MultiAddress; -/// Block header type as expected by this runtime. -pub type Header = generic::Header; -/// Block type as expected by this runtime. -pub type Block = generic::Block; -/// A Block signed with a Justification -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; -/// Unchecked extrinsic type as expected by this runtime. -pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; -/// Executive: handles dispatch to the various modules. -pub type Executive = frame_executive::Executive< - Runtime, - Block, - frame_system::ChainContext, - Runtime, - AllPalletsWithSystem, ->; - -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()) - } - - fn authorities() -> Vec { - pallet_aura::Authorities::::get().into_inner() - } - } - - impl sp_api::Core for Runtime { - fn version() -> RuntimeVersion { - VERSION - } - - fn execute_block(block: Block) { - Executive::execute_block(block) - } - - fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { - Executive::initialize_block(header) - } - } - - impl sp_api::Metadata for Runtime { - fn metadata() -> OpaqueMetadata { - OpaqueMetadata::new(Runtime::metadata().into()) - } - - fn metadata_at_version(version: u32) -> Option { - Runtime::metadata_at_version(version) - } - - fn metadata_versions() -> alloc::vec::Vec { - Runtime::metadata_versions() - } - } - - impl sp_block_builder::BlockBuilder for Runtime { - fn apply_extrinsic( - extrinsic: ::Extrinsic, - ) -> ApplyExtrinsicResult { - Executive::apply_extrinsic(extrinsic) - } - - fn finalize_block() -> ::Header { - Executive::finalize_block() - } - - fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<::Extrinsic> { - data.create_extrinsics() - } - - fn check_inherents(block: Block, data: sp_inherents::InherentData) -> sp_inherents::CheckInherentsResult { - data.check_extrinsics(&block) - } - } - - impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { - fn validate_transaction( - source: TransactionSource, - tx: ::Extrinsic, - block_hash: ::Hash, - ) -> TransactionValidity { - Executive::validate_transaction(source, tx, block_hash) - } - } - - impl sp_offchain::OffchainWorkerApi for Runtime { - fn offchain_worker(header: &::Header) { - Executive::offchain_worker(header) - } - } - - impl sp_session::SessionKeys for Runtime { - fn generate_session_keys(seed: Option>) -> Vec { - SessionKeys::generate(seed) - } - - fn decode_session_keys( - encoded: Vec, - ) -> Option, KeyTypeId)>> { - SessionKeys::decode_into_raw_public_keys(&encoded) - } - } - - impl cumulus_primitives_core::CollectCollationInfo for Runtime { - fn collect_collation_info(header: &::Header) -> cumulus_primitives_core::CollationInfo { - ParachainSystem::collect_collation_info(header) - } - } - - impl sp_genesis_builder::GenesisBuilder for Runtime { - fn build_state(config: Vec) -> sp_genesis_builder::Result { - build_state::(config) - } - - fn get_preset(id: &Option) -> Option> { - get_preset::(id, |_| None) - } - - fn preset_names() -> Vec { - vec![] - } - } -} - -cumulus_pallet_parachain_system::register_validate_block! { - Runtime = Runtime, - BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::, -} diff --git a/cumulus/parachains/runtimes/starters/shell/src/xcm_config.rs b/cumulus/parachains/runtimes/starters/shell/src/xcm_config.rs deleted file mode 100644 index 741b3bcd752f5511b09add16865a5f311e3626cc..0000000000000000000000000000000000000000 --- a/cumulus/parachains/runtimes/starters/shell/src/xcm_config.rs +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use super::{ - AccountId, AllPalletsWithSystem, ParachainInfo, Runtime, RuntimeCall, RuntimeEvent, - RuntimeOrigin, -}; -use frame_support::{ - parameter_types, - traits::{Contains, Everything, Nothing}, - weights::Weight, -}; -use xcm::latest::prelude::*; -use xcm_builder::{ - AllowExplicitUnpaidExecutionFrom, FixedWeightBounds, ParentAsSuperuser, ParentIsPreset, - SovereignSignedViaLocation, -}; - -parameter_types! { - pub const RococoLocation: Location = Location::parent(); - pub const RococoNetwork: NetworkId = NetworkId::Rococo; - pub UniversalLocation: InteriorLocation = [GlobalConsensus(RococoNetwork::get()), Parachain(ParachainInfo::parachain_id().into())].into(); -} - -/// 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 -/// bias the kind of local `Origin` it will become. -pub type XcmOriginToTransactDispatchOrigin = ( - // Sovereign account converter; this attempts to derive an `AccountId` from the origin location - // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for - // foreign chains who want to have a local sovereign account on this chain which they control. - SovereignSignedViaLocation, RuntimeOrigin>, - // Superuser converter for the Relay-chain (Parent) location. This will allow it to issue a - // transaction from the Root origin. - ParentAsSuperuser, -); - -pub struct JustTheParent; -impl Contains for JustTheParent { - fn contains(location: &Location) -> bool { - matches!(location.unpack(), (1, [])) - } -} - -parameter_types! { - // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate. - pub UnitWeightCost: Weight = Weight::from_parts(1_000_000_000, 64 * 1024); - pub const MaxInstructions: u32 = 100; - pub const MaxAssetsIntoHolding: u32 = 64; -} - -pub struct XcmConfig; -impl xcm_executor::Config for XcmConfig { - type RuntimeCall = RuntimeCall; - type XcmSender = (); // sending XCM not supported - type AssetTransactor = (); // balances not supported - type OriginConverter = XcmOriginToTransactDispatchOrigin; - type IsReserve = (); // balances not supported - type IsTeleporter = (); // balances not supported - type UniversalLocation = UniversalLocation; - type Barrier = AllowExplicitUnpaidExecutionFrom; - type Weigher = FixedWeightBounds; // balances not supported - type Trader = (); // balances not supported - type ResponseHandler = (); // Don't handle responses for now. - type AssetTrap = (); // don't trap for now - type AssetClaims = (); // don't claim for now - type SubscriptionService = (); // don't handle subscriptions for now - type PalletInstancesInfo = AllPalletsWithSystem; - type MaxAssetsIntoHolding = MaxAssetsIntoHolding; - type AssetLocker = (); - type AssetExchanger = (); - type FeeManager = (); - type MessageExporter = (); - type UniversalAliases = Nothing; - type CallDispatcher = RuntimeCall; - type SafeCallFilter = Everything; - type Aliasers = Nothing; - type TransactionalProcessor = (); - type HrmpNewChannelOpenRequestHandler = (); - type HrmpChannelAcceptedHandler = (); - type HrmpChannelClosingHandler = (); - type XcmRecorder = (); -} - -impl cumulus_pallet_xcm::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type XcmExecutor = xcm_executor::XcmExecutor; -} diff --git a/cumulus/parachains/runtimes/test-utils/src/lib.rs b/cumulus/parachains/runtimes/test-utils/src/lib.rs index 940aa1b734dfc856b5a72cf83cc14b5f8980bc5e..3b38eee244f10931bdd7703cf5a59f88a363efaa 100644 --- a/cumulus/parachains/runtimes/test-utils/src/lib.rs +++ b/cumulus/parachains/runtimes/test-utils/src/lib.rs @@ -22,7 +22,7 @@ use cumulus_primitives_core::{ use cumulus_primitives_parachain_inherent::ParachainInherentData; use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder; use frame_support::{ - dispatch::{DispatchResult, RawOrigin}, + dispatch::{DispatchResult, GetDispatchInfo, RawOrigin}, inherent::{InherentData, ProvideInherent}, pallet_prelude::Get, traits::{OnFinalize, OnInitialize, OriginTrait, UnfilteredDispatchable}, @@ -450,6 +450,7 @@ impl< require_weight_at_most, call: call.into(), }, + ExpectTransactStatus(MaybeErrorCode::Success), ]); // execute xcm as parent origin @@ -462,6 +463,38 @@ impl< Weight::zero(), ) } + + pub fn execute_as_origin_xcm( + origin: Location, + call: Call, + buy_execution_fee: Asset, + ) -> Outcome { + // prepare `Transact` xcm + let xcm = Xcm(vec![ + WithdrawAsset(buy_execution_fee.clone().into()), + BuyExecution { fees: buy_execution_fee.clone(), weight_limit: Unlimited }, + Transact { + origin_kind: OriginKind::Xcm, + require_weight_at_most: call.get_dispatch_info().weight, + call: call.encode().into(), + }, + ExpectTransactStatus(MaybeErrorCode::Success), + ]); + + // execute xcm as parent origin + let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256); + <::XcmExecutor>::prepare_and_execute( + origin.clone(), + xcm, + &mut hash, + Self::xcm_max_weight(if origin == Location::parent() { + XcmReceivedFrom::Parent + } else { + XcmReceivedFrom::Sibling + }), + Weight::zero(), + ) + } } pub enum XcmReceivedFrom { diff --git a/cumulus/parachains/runtimes/testing/penpal/Cargo.toml b/cumulus/parachains/runtimes/testing/penpal/Cargo.toml index e16629302be6a90c25f365a72edb9dd36baebd75..96338b64558106d865aa251a651eecfda6b401ec 100644 --- a/cumulus/parachains/runtimes/testing/penpal/Cargo.toml +++ b/cumulus/parachains/runtimes/testing/penpal/Cargo.toml @@ -42,6 +42,7 @@ pallet-transaction-payment = { workspace = true } pallet-transaction-payment-rpc-runtime-api = { workspace = true } pallet-asset-tx-payment = { workspace = true } pallet-assets = { workspace = true } +pallet-asset-conversion = { workspace = true } sp-api = { workspace = true } sp-block-builder = { workspace = true } sp-consensus-aura = { workspace = true } @@ -79,6 +80,8 @@ parachain-info = { workspace = true } parachains-common = { workspace = true } assets-common = { workspace = true } +primitive-types = { version = "0.12.1", default-features = false, features = ["codec", "num-traits", "scale-info"] } + [features] default = ["std"] std = [ @@ -99,6 +102,7 @@ std = [ "frame-system/std", "frame-try-runtime?/std", "log/std", + "pallet-asset-conversion/std", "pallet-asset-tx-payment/std", "pallet-assets/std", "pallet-aura/std", @@ -117,6 +121,7 @@ std = [ "polkadot-parachain-primitives/std", "polkadot-primitives/std", "polkadot-runtime-common/std", + "primitive-types/std", "scale-info/std", "sp-api/std", "sp-block-builder/std", @@ -149,6 +154,7 @@ runtime-benchmarks = [ "frame-system-benchmarking/runtime-benchmarks", "frame-system/runtime-benchmarks", "hex-literal", + "pallet-asset-conversion/runtime-benchmarks", "pallet-asset-tx-payment/runtime-benchmarks", "pallet-assets/runtime-benchmarks", "pallet-balances/runtime-benchmarks", @@ -176,6 +182,7 @@ try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", "frame-try-runtime/try-runtime", + "pallet-asset-conversion/try-runtime", "pallet-asset-tx-payment/try-runtime", "pallet-assets/try-runtime", "pallet-aura/try-runtime", diff --git a/cumulus/parachains/runtimes/testing/penpal/build.rs b/cumulus/parachains/runtimes/testing/penpal/build.rs index c2fa89aa7028510ab3f95a152ababcffbecd280f..e47e483bf9c11300b2a8cb3399fbb0a464882429 100644 --- a/cumulus/parachains/runtimes/testing/penpal/build.rs +++ b/cumulus/parachains/runtimes/testing/penpal/build.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Cumulus. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs index bf39c02a3f59460156c37e6c652bc69fa7237a2d..05773846164f66c800612fc9037c4c596c7d1a55 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs @@ -35,17 +35,24 @@ pub mod xcm_config; extern crate alloc; use alloc::{vec, vec::Vec}; +use assets_common::{ + local_and_foreign_assets::{LocalFromLeft, TargetFromLeft}, + AssetIdForTrustBackedAssetsConvert, +}; use codec::Encode; use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases; -use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; +use cumulus_primitives_core::{AggregateMessageOrigin, ClaimQueueOffset, CoreSelector, ParaId}; use frame_support::{ construct_runtime, derive_impl, dispatch::DispatchClass, genesis_builder_helper::{build_state, get_preset}, + ord_parameter_types, pallet_prelude::Weight, parameter_types, traits::{ - AsEnsureOriginWithArg, ConstBool, ConstU32, ConstU64, ConstU8, Everything, TransformOrigin, + tokens::{fungible, fungibles, imbalance::ResolveAssetTo}, + AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU32, ConstU64, ConstU8, Everything, + TransformOrigin, }, weights::{ constants::WEIGHT_REF_TIME_PER_SECOND, ConstantMultiplier, FeePolynomial, WeightToFee as _, @@ -55,7 +62,7 @@ use frame_support::{ }; use frame_system::{ limits::{BlockLength, BlockWeights}, - EnsureRoot, EnsureSigned, + EnsureRoot, EnsureSigned, EnsureSignedBy, }; use parachains_common::{ impls::{AssetsToBlockAuthor, NonZeroIssuance}, @@ -67,7 +74,7 @@ pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, - traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, Dispatchable}, + traits::{AccountIdConversion, AccountIdLookup, BlakeTwo256, Block as BlockT, Dispatchable}, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, }; @@ -242,7 +249,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 1, + system_version: 1, }; /// This determines the average expected block time that we are targeting. @@ -413,6 +420,7 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<0>; + type DoneSlashHandler = (); } parameter_types! { @@ -442,7 +450,9 @@ parameter_types! { // pub type AssetsForceOrigin = // EnsureOneOf, EnsureXcm>>; -impl pallet_assets::Config for Runtime { +pub type TrustBackedAssetsInstance = pallet_assets::Instance1; + +impl pallet_assets::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Balance = Balance; type AssetId = AssetId; @@ -500,6 +510,106 @@ impl pallet_assets::Config for Runtime { type BenchmarkHelper = xcm_config::XcmBenchmarkHelper; } +parameter_types! { + pub const AssetConversionPalletId: PalletId = PalletId(*b"py/ascon"); + pub const LiquidityWithdrawalFee: Permill = Permill::from_percent(0); +} + +ord_parameter_types! { + pub const AssetConversionOrigin: sp_runtime::AccountId32 = + AccountIdConversion::::into_account_truncating(&AssetConversionPalletId::get()); +} + +pub type AssetsForceOrigin = EnsureRoot; + +pub type PoolAssetsInstance = pallet_assets::Instance3; +impl pallet_assets::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type RemoveItemsLimit = ConstU32<1000>; + type AssetId = u32; + type AssetIdParameter = u32; + type Currency = Balances; + type CreateOrigin = + AsEnsureOriginWithArg>; + type ForceOrigin = AssetsForceOrigin; + type AssetDeposit = ConstU128<0>; + type AssetAccountDeposit = ConstU128<0>; + type MetadataDepositBase = ConstU128<0>; + type MetadataDepositPerByte = ConstU128<0>; + type ApprovalDeposit = ConstU128<0>; + type StringLimit = ConstU32<50>; + type Freezer = (); + type Extra = (); + type WeightInfo = pallet_assets::weights::SubstrateWeight; + type CallbackHandle = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); +} + +/// Union fungibles implementation for `Assets` and `ForeignAssets`. +pub type LocalAndForeignAssets = fungibles::UnionOf< + Assets, + ForeignAssets, + LocalFromLeft< + AssetIdForTrustBackedAssetsConvert< + xcm_config::TrustBackedAssetsPalletLocation, + xcm::latest::Location, + >, + parachains_common::AssetIdForTrustBackedAssets, + xcm::latest::Location, + >, + xcm::latest::Location, + AccountId, +>; + +/// Union fungibles implementation for [`LocalAndForeignAssets`] and `Balances`. +pub type NativeAndAssets = fungible::UnionOf< + Balances, + LocalAndForeignAssets, + TargetFromLeft, + xcm::latest::Location, + AccountId, +>; + +pub type PoolIdToAccountId = pallet_asset_conversion::AccountIdConverter< + AssetConversionPalletId, + (xcm::latest::Location, xcm::latest::Location), +>; + +impl pallet_asset_conversion::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type HigherPrecisionBalance = sp_core::U256; + type AssetKind = xcm::latest::Location; + type Assets = NativeAndAssets; + type PoolId = (Self::AssetKind, Self::AssetKind); + type PoolLocator = pallet_asset_conversion::WithFirstAsset< + xcm_config::RelayLocation, + AccountId, + Self::AssetKind, + PoolIdToAccountId, + >; + type PoolAssetId = u32; + type PoolAssets = PoolAssets; + type PoolSetupFee = ConstU128<0>; // Asset class deposit fees are sufficient to prevent spam + type PoolSetupFeeAsset = xcm_config::RelayLocation; + type PoolSetupFeeTarget = ResolveAssetTo; + type LiquidityWithdrawalFee = LiquidityWithdrawalFee; + type LPFee = ConstU32<3>; + type PalletId = AssetConversionPalletId; + type MaxSwapPathLength = ConstU32<3>; + type MintMinLiquidity = ConstU128<100>; + type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = assets_common::benchmarks::AssetPairFactory< + xcm_config::RelayLocation, + parachain_info::Pallet, + xcm_config::TrustBackedAssetsPalletIndex, + xcm::latest::Location, + >; +} + parameter_types! { pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); @@ -523,6 +633,7 @@ impl cumulus_pallet_parachain_system::Config for Runtime { BLOCK_PROCESSING_VELOCITY, UNINCLUDED_SEGMENT_CAPACITY, >; + type SelectCore = cumulus_pallet_parachain_system::DefaultCoreSelector; } impl parachain_info::Config for Runtime {} @@ -642,9 +753,9 @@ impl pallet_asset_tx_payment::Config for Runtime { Balances, Runtime, ConvertInto, - pallet_assets::Instance1, + TrustBackedAssetsInstance, >, - AssetsToBlockAuthor, + AssetsToBlockAuthor, >; } @@ -685,6 +796,8 @@ construct_runtime!( // The main stage. Assets: pallet_assets:: = 50, ForeignAssets: pallet_assets:: = 51, + PoolAssets: pallet_assets:: = 52, + AssetConversion: pallet_asset_conversion = 53, Sudo: pallet_sudo = 255, } @@ -849,6 +962,12 @@ impl_runtime_apis! { } } + impl cumulus_primitives_core::GetCoreSelectorApi for Runtime { + fn core_selector() -> (CoreSelector, ClaimQueueOffset) { + ParachainSystem::core_selector() + } + } + impl xcm_runtime_apis::fees::XcmPaymentApi for Runtime { fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { let acceptable_assets = vec![AssetLocationId(xcm_config::RelayLocation::get())]; diff --git a/cumulus/parachains/runtimes/testing/penpal/src/weights/block_weights.rs b/cumulus/parachains/runtimes/testing/penpal/src/weights/block_weights.rs index e7fdb2aae2a01ec06076de83d94817e540e205dd..41e30725e753fef32404b858a873e2456618d56f 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/weights/block_weights.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/weights/block_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/testing/penpal/src/weights/extrinsic_weights.rs b/cumulus/parachains/runtimes/testing/penpal/src/weights/extrinsic_weights.rs index 1a4adb968bb7195428ea00d59cd92dcd3b6eea5f..3bd48f061bba079a76aa8f032ebecd6d40c2156c 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/weights/extrinsic_weights.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/weights/extrinsic_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/testing/penpal/src/weights/mod.rs b/cumulus/parachains/runtimes/testing/penpal/src/weights/mod.rs index b473d49e20e67329d893e1e565330cbe9290c64f..850dae6fbd06ea95d3c7c87c09325aacb123c0c4 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/weights/mod.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/testing/penpal/src/weights/paritydb_weights.rs b/cumulus/parachains/runtimes/testing/penpal/src/weights/paritydb_weights.rs index 25679703831a13b8d1bb7fb7dd4d92fa84b1f255..e0b1985c659c78b66176423f6bcc64d546b39324 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/weights/paritydb_weights.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/weights/paritydb_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/testing/penpal/src/weights/rocksdb_weights.rs b/cumulus/parachains/runtimes/testing/penpal/src/weights/rocksdb_weights.rs index 3dd817aa6f137085b0e5fdf2b11b7f50e5c8b002..c6e91b2fcffbf65e3fdce2bc8fbac19a605d00aa 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/weights/rocksdb_weights.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/weights/rocksdb_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Cumulus. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs index d0c421bccaf89c8a54e348c3ff04950be444a233..b72d6d232a1d13b89551cf5158d2da699b049e41 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -24,15 +24,19 @@ //! soon. use super::{ AccountId, AllPalletsWithSystem, AssetId as AssetIdPalletAssets, Assets, Authorship, Balance, - Balances, ForeignAssets, ForeignAssetsInstance, NonZeroIssuance, ParachainInfo, - ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, WeightToFee, - XcmpQueue, + Balances, CollatorSelection, ForeignAssets, ForeignAssetsInstance, NonZeroIssuance, + ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, + WeightToFee, XcmpQueue, }; use crate::{BaseDeliveryFee, FeeAssetId, TransactionByteFee}; +use assets_common::TrustBackedAssetsAsLocation; use core::marker::PhantomData; use frame_support::{ parameter_types, - traits::{ConstU32, Contains, ContainsPair, Everything, EverythingBut, Get, Nothing}, + traits::{ + tokens::imbalance::ResolveAssetTo, ConstU32, Contains, ContainsPair, Everything, + EverythingBut, Get, Nothing, PalletInfoAccess, + }, weights::Weight, }; use frame_system::EnsureRoot; @@ -45,19 +49,19 @@ use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AsPrefixedGeneralIndex, - ConvertedConcreteId, EnsureXcmOrigin, FixedWeightBounds, FrameTransactionalProcessor, - FungibleAdapter, FungiblesAdapter, IsConcrete, LocalMint, NativeAsset, NoChecking, - ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, - SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, - SignedToAccountId32, SovereignSignedViaLocation, StartsWith, TakeWeightCredit, - TrailingSetTopicAsId, UsingComponents, WithComputedOrigin, WithUniqueTopic, - XcmFeeManagerFromComponents, + ConvertedConcreteId, DescribeAllTerminal, DescribeFamily, EnsureXcmOrigin, FixedWeightBounds, + FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, HashedDescription, IsConcrete, + LocalMint, NativeAsset, NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, + SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountId32AsNative, SignedToAccountId32, SingleAssetExchangeAdapter, + SovereignSignedViaLocation, StartsWith, TakeWeightCredit, TrailingSetTopicAsId, + UsingComponents, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, }; use xcm_executor::{traits::JustTry, XcmExecutor}; parameter_types! { pub const RelayLocation: Location = Location::parent(); - // Local native currency which is stored in `pallet_balances`` + // Local native currency which is stored in `pallet_balances` pub const PenpalNativeCurrency: Location = Location::here(); // The Penpal runtime is utilized for testing with various environment setups. // This storage item allows us to customize the `NetworkId` where Penpal is deployed. @@ -70,6 +74,10 @@ parameter_types! { Parachain(ParachainInfo::parachain_id().into()) ].into(); pub TreasuryAccount: AccountId = TREASURY_PALLET_ID.into_account_truncating(); + pub StakingPot: AccountId = CollatorSelection::account_id(); + pub TrustBackedAssetsPalletIndex: u8 = ::index() as u8; + pub TrustBackedAssetsPalletLocation: Location = + PalletInstance(TrustBackedAssetsPalletIndex::get()).into(); } /// Type for specifying how a `Location` can be converted into an `AccountId`. This is used @@ -82,6 +90,8 @@ pub type LocationToAccountId = ( SiblingParachainConvertsVia, // Straight up local `AccountId32` origins just alias directly to `AccountId`. AccountId32Aliases, + // Foreign locations alias into accounts according to a hash of their standard description. + HashedDescription>, ); /// Means for transacting assets on this chain. @@ -265,6 +275,8 @@ pub const TELEPORTABLE_ASSET_ID: u32 = 2; pub const ASSETS_PALLET_ID: u8 = 50; pub const ASSET_HUB_ID: u32 = 1000; +pub const USDT_ASSET_ID: u128 = 1984; + parameter_types! { /// The location that this chain recognizes as the Relay network's Asset Hub. pub SystemAssetHubLocation: Location = Location::new(1, [Parachain(ASSET_HUB_ID)]); @@ -284,7 +296,7 @@ parameter_types! { ); pub UsdtFromAssetHub: Location = Location::new( 1, - [Parachain(ASSET_HUB_ID), PalletInstance(ASSETS_PALLET_ID), GeneralIndex(1984)] + [Parachain(ASSET_HUB_ID), PalletInstance(ASSETS_PALLET_ID), GeneralIndex(USDT_ASSET_ID)], ); /// The Penpal runtime is utilized for testing with various environment setups. @@ -316,6 +328,28 @@ pub type TrustedReserves = ( pub type TrustedTeleporters = (AssetFromChain,); +/// `AssetId`/`Balance` converter for `TrustBackedAssets`. +pub type TrustBackedAssetsConvertedConcreteId = + assets_common::TrustBackedAssetsConvertedConcreteId; + +/// Asset converter for pool assets. +/// Used to convert assets in pools to the asset required for fee payment. +/// The pool must be between the first asset and the one required for fee payment. +/// This type allows paying fees with any asset in a pool with the asset required for fee payment. +pub type PoolAssetsExchanger = SingleAssetExchangeAdapter< + crate::AssetConversion, + crate::NativeAndAssets, + ( + TrustBackedAssetsAsLocation< + TrustBackedAssetsPalletLocation, + Balance, + xcm::latest::Location, + >, + ForeignAssetsConvertedConcreteId, + ), + AccountId, +>; + pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { type RuntimeCall = RuntimeCall; @@ -331,18 +365,21 @@ impl xcm_executor::Config for XcmConfig { type Weigher = FixedWeightBounds; type Trader = ( UsingComponents>, - // This trader allows to pay with `is_sufficient=true` "Foreign" assets from dedicated - // `pallet_assets` instance - `ForeignAssets`. - cumulus_primitives_utility::TakeFirstAssetTrader< + cumulus_primitives_utility::SwapFirstAssetTrader< + RelayLocation, + crate::AssetConversion, + WeightToFee, + crate::NativeAndAssets, + ( + TrustBackedAssetsAsLocation< + TrustBackedAssetsPalletLocation, + Balance, + xcm::latest::Location, + >, + ForeignAssetsConvertedConcreteId, + ), + ResolveAssetTo, AccountId, - ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger, - ForeignAssetsConvertedConcreteId, - ForeignAssets, - cumulus_primitives_utility::XcmFeesTo32ByteAccount< - ForeignFungiblesTransactor, - AccountId, - XcmAssetFeesReceiver, - >, >, ); type ResponseHandler = PolkadotXcm; @@ -352,7 +389,7 @@ impl xcm_executor::Config for XcmConfig { type PalletInstancesInfo = AllPalletsWithSystem; type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type AssetLocker = (); - type AssetExchanger = (); + type AssetExchanger = PoolAssetsExchanger; type FeeManager = XcmFeeManagerFromComponents< (), SendXcmFeeToAccount, diff --git a/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs b/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs index dff7046f1972614d30210b6ac6d21c24e0350aea..5abdc995c00dbfcc89af8ef0a68c9ceb0903815d 100644 --- a/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs @@ -68,7 +68,7 @@ pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; pub use sp_runtime::BuildStorage; pub use sp_runtime::{Perbill, Permill}; -use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; +use cumulus_primitives_core::{AggregateMessageOrigin, ClaimQueueOffset, CoreSelector, ParaId}; use frame_support::traits::TransformOrigin; use parachains_common::{ impls::{AssetsFrom, NonZeroIssuance}, @@ -113,7 +113,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 6, - state_version: 0, + system_version: 0, }; pub const MILLISECS_PER_BLOCK: u64 = 6000; @@ -257,6 +257,7 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<0>; + type DoneSlashHandler = (); } impl pallet_transaction_payment::Config for Runtime { @@ -299,6 +300,7 @@ impl cumulus_pallet_parachain_system::Config for Runtime { type ReservedXcmpWeight = ReservedXcmpWeight; type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases; type ConsensusHook = ConsensusHook; + type SelectCore = cumulus_pallet_parachain_system::DefaultCoreSelector; } impl parachain_info::Config for Runtime {} @@ -854,6 +856,12 @@ impl_runtime_apis! { ConsensusHook::can_build_upon(included_hash, slot) } } + + impl cumulus_primitives_core::GetCoreSelectorApi for Runtime { + fn core_selector() -> (CoreSelector, ClaimQueueOffset) { + ParachainSystem::core_selector() + } + } } cumulus_pallet_parachain_system::register_validate_block! { diff --git a/cumulus/polkadot-omni-node/Cargo.toml b/cumulus/polkadot-omni-node/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..a736e1ef80c587ead7aad1d8c1b0159956a9d0a4 --- /dev/null +++ b/cumulus/polkadot-omni-node/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "polkadot-omni-node" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +build = "build.rs" +description = "Generic binary that can run a parachain node with u32 block number and Aura consensus" +license = "Apache-2.0" + +[lints] +workspace = true + +[dependencies] +color-eyre = { workspace = true } + +# Local +polkadot-omni-node-lib = { workspace = true } + +[build-dependencies] +substrate-build-script-utils = { workspace = true, default-features = true } + +[features] +default = [] +runtime-benchmarks = [ + "polkadot-omni-node-lib/runtime-benchmarks", +] +try-runtime = [ + "polkadot-omni-node-lib/try-runtime", +] diff --git a/cumulus/parachains/runtimes/starters/shell/build.rs b/cumulus/polkadot-omni-node/build.rs similarity index 64% rename from cumulus/parachains/runtimes/starters/shell/build.rs rename to cumulus/polkadot-omni-node/build.rs index 9c9cde9a25a1a8ab1780b7df7e068264459a31af..8c498735eae965dfd3686c9031843c2c6d13f992 100644 --- a/cumulus/parachains/runtimes/starters/shell/build.rs +++ b/cumulus/polkadot-omni-node/build.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Cumulus. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. @@ -14,14 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -#[cfg(feature = "std")] +use substrate_build_script_utils::{generate_cargo_keys, rerun_if_git_head_changed}; + fn main() { - substrate_wasm_builder::WasmBuilder::new() - .with_current_project() - .export_heap_base() - .import_memory() - .build() + generate_cargo_keys(); + rerun_if_git_head_changed(); } - -#[cfg(not(feature = "std"))] -fn main() {} diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/Cargo.toml b/cumulus/polkadot-omni-node/lib/Cargo.toml similarity index 97% rename from cumulus/polkadot-parachain/polkadot-parachain-lib/Cargo.toml rename to cumulus/polkadot-omni-node/lib/Cargo.toml index 066cbfae53ae7021664cb60305d6eb5b38d94f6a..3dd482f4adafb236e7e12678323776dd7ec83bee 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/Cargo.toml +++ b/cumulus/polkadot-omni-node/lib/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "polkadot-parachain-lib" +name = "polkadot-omni-node-lib" version = "0.1.0" authors.workspace = true edition.workspace = true @@ -61,6 +61,7 @@ pallet-transaction-payment-rpc-runtime-api = { workspace = true, default-feature sp-inherents = { workspace = true, default-features = true } sp-api = { workspace = true, default-features = true } sp-consensus-aura = { workspace = true, default-features = true } +sc-consensus-manual-seal = { workspace = true, default-features = true } sc-sysinfo = { workspace = true, default-features = true } prometheus-endpoint = { workspace = true, default-features = true } substrate-frame-rpc-system = { workspace = true, default-features = true } @@ -83,6 +84,7 @@ cumulus-client-service = { workspace = true, default-features = true } cumulus-primitives-aura = { workspace = true, default-features = true } cumulus-primitives-core = { workspace = true, default-features = true } cumulus-relay-chain-interface = { workspace = true, default-features = true } +futures-timer = "3.0.3" [dev-dependencies] assert_cmd = { workspace = true } diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/cli.rs b/cumulus/polkadot-omni-node/lib/src/cli.rs similarity index 96% rename from cumulus/polkadot-parachain/polkadot-parachain-lib/src/cli.rs rename to cumulus/polkadot-omni-node/lib/src/cli.rs index 15d21235d1a15207203c950c515c65678ce4c932..6ca328912bba1fc447252f753fb4f125a80e04a5 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/cli.rs +++ b/cumulus/polkadot-omni-node/lib/src/cli.rs @@ -119,6 +119,14 @@ pub struct Cli { #[command(flatten)] pub run: cumulus_client_cli::RunCmd, + /// Start a dev node that produces a block each `dev_block_time` ms. + /// + /// This is a dev option, and it won't result in starting or connecting to a parachain network. + /// The resulting node will work on its own, running the wasm blob and artificially producing + /// a block each `dev_block_time` ms, as if it was part of a parachain. + #[arg(long)] + pub dev_block_time: Option, + /// EXPERIMENTAL: Use slot-based collator which can handle elastic scaling. /// /// Use with care, this flag is unstable and subject to change. @@ -322,10 +330,9 @@ impl CliConfiguration for RelayChainCli { _support_url: &String, _impl_version: &String, _logger_hook: F, - _config: &sc_service::Configuration, ) -> sc_cli::Result<()> where - F: FnOnce(&mut sc_cli::LoggerBuilder, &sc_service::Configuration), + F: FnOnce(&mut sc_cli::LoggerBuilder), { unreachable!("PolkadotCli is never initialized; qed"); } diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/command.rs b/cumulus/polkadot-omni-node/lib/src/command.rs similarity index 85% rename from cumulus/polkadot-parachain/polkadot-parachain-lib/src/command.rs rename to cumulus/polkadot-omni-node/lib/src/command.rs index 320511ece5e51f36fc00f3bf365abe02861761c0..350dcfee1cdb7aaebf28259f952d9d95ded9fb6c 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/command.rs +++ b/cumulus/polkadot-omni-node/lib/src/command.rs @@ -22,13 +22,12 @@ use crate::{ AuraConsensusId, Consensus, Runtime, RuntimeResolver as RuntimeResolverT, RuntimeResolver, }, - spec::DynNodeSpec, types::Block, NodeBlock, NodeExtraArgs, }, fake_runtime_api, + nodes::DynNodeSpecExt, runtime::BlockNumber, - service::ShellNode, }; #[cfg(feature = "runtime-benchmarks")] use cumulus_client_service::storage_proof_size::HostFunctions as ReclaimHostFunctions; @@ -39,7 +38,6 @@ use sc_cli::{Result, SubstrateCli}; use sp_runtime::traits::AccountIdConversion; #[cfg(feature = "runtime-benchmarks")] use sp_runtime::traits::HashingFor; -use std::panic::{RefUnwindSafe, UnwindSafe}; /// Structure that can be used in order to provide customizers for different functionalities of the /// node binary that is being built using this library. @@ -53,18 +51,17 @@ pub struct RunConfig { pub fn new_aura_node_spec( aura_id: AuraConsensusId, extra_args: &NodeExtraArgs, -) -> Box +) -> Box where - Block: NodeBlock + UnwindSafe + RefUnwindSafe, - Block::BoundedHeader: UnwindSafe + RefUnwindSafe, + Block: NodeBlock, { match aura_id { - AuraConsensusId::Sr25519 => crate::service::new_aura_node_spec::< + AuraConsensusId::Sr25519 => crate::nodes::aura::new_aura_node_spec::< Block, fake_runtime_api::aura_sr25519::RuntimeApi, sp_consensus_aura::sr25519::AuthorityId, >(extra_args), - AuraConsensusId::Ed25519 => crate::service::new_aura_node_spec::< + AuraConsensusId::Ed25519 => crate::nodes::aura::new_aura_node_spec::< Block, fake_runtime_api::aura_ed25519::RuntimeApi, sp_consensus_aura::ed25519::AuthorityId, @@ -76,11 +73,10 @@ fn new_node_spec( config: &sc_service::Configuration, runtime_resolver: &Box, extra_args: &NodeExtraArgs, -) -> std::result::Result, sc_cli::Error> { +) -> std::result::Result, sc_cli::Error> { let runtime = runtime_resolver.runtime(config.chain_spec.as_ref())?; Ok(match runtime { - Runtime::Shell => Box::new(ShellNode), Runtime::Omni(block_number, consensus) => match (block_number, consensus) { (BlockNumber::U32, Consensus::Aura(aura_id)) => new_aura_node_spec::>(aura_id, extra_args), @@ -216,6 +212,20 @@ pub fn run(cmd_config: RunConfig) -> Result<() let collator_options = cli.run.collator_options(); runner.run_node_until_exit(|config| async move { + let node_spec = + new_node_spec(&config, &cmd_config.runtime_resolver, &cli.node_extra_args())?; + let para_id = ParaId::from( + Extensions::try_get(&*config.chain_spec) + .map(|e| e.para_id) + .ok_or("Could not find parachain extension in chain-spec.")?, + ); + + if let Some(dev_block_time) = cli.dev_block_time { + return node_spec + .start_manual_seal_node(config, para_id, dev_block_time) + .map_err(Into::into) + } + // If Statemint (Statemine, Westmint, Rockmine) DB exists and we're using the // asset-hub chain spec, then rename the base path to the new chain ID. In the case // that both file paths exist, the node will exit, as the user must decide (by @@ -258,19 +268,16 @@ pub fn run(cmd_config: RunConfig) -> Result<() let hwbench = (!cli.no_hardware_benchmarks) .then_some(config.database.path().map(|database_path| { let _ = std::fs::create_dir_all(database_path); - sc_sysinfo::gather_hwbench(Some(database_path)) + sc_sysinfo::gather_hwbench( + Some(database_path), + &SUBSTRATE_REFERENCE_HARDWARE, + ) })) .flatten(); - let para_id = Extensions::try_get(&*config.chain_spec) - .map(|e| e.para_id) - .ok_or("Could not find parachain extension in chain-spec.")?; - - let id = ParaId::from(para_id); - let parachain_account = AccountIdConversion::::into_account_truncating( - &id, + ¶_id, ); let tokio_handle = config.tokio_handle.clone(); @@ -278,38 +285,22 @@ pub fn run(cmd_config: RunConfig) -> Result<() SubstrateCli::create_configuration(&polkadot_cli, &polkadot_cli, tokio_handle) .map_err(|err| format!("Relay chain argument error: {}", err))?; - info!("๐Ÿชช Parachain id: {:?}", id); + info!("๐Ÿชช Parachain id: {:?}", para_id); info!("๐Ÿงพ Parachain Account: {}", parachain_account); info!("โœ๏ธ Is collating: {}", if config.role.is_authority() { "yes" } else { "no" }); - start_node( - config, - &cmd_config.runtime_resolver, - polkadot_config, - collator_options, - id, - cli.node_extra_args(), - hwbench, - ) - .await + node_spec + .start_node( + config, + polkadot_config, + collator_options, + para_id, + hwbench, + cli.node_extra_args(), + ) + .await + .map_err(Into::into) }) }, } } - -#[sc_tracing::logging::prefix_logs_with("Parachain")] -async fn start_node( - config: sc_service::Configuration, - runtime_resolver: &Box, - polkadot_config: sc_service::Configuration, - collator_options: cumulus_client_cli::CollatorOptions, - id: ParaId, - extra_args: NodeExtraArgs, - hwbench: Option, -) -> Result { - let node_spec = new_node_spec(&config, runtime_resolver, &extra_args)?; - node_spec - .start_node(config, polkadot_config, collator_options, id, hwbench, extra_args) - .await - .map_err(Into::into) -} diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/aura.rs b/cumulus/polkadot-omni-node/lib/src/common/aura.rs similarity index 100% rename from cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/aura.rs rename to cumulus/polkadot-omni-node/lib/src/common/aura.rs diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/chain_spec.rs b/cumulus/polkadot-omni-node/lib/src/common/chain_spec.rs similarity index 100% rename from cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/chain_spec.rs rename to cumulus/polkadot-omni-node/lib/src/common/chain_spec.rs diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/command.rs b/cumulus/polkadot-omni-node/lib/src/common/command.rs similarity index 98% rename from cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/command.rs rename to cumulus/polkadot-omni-node/lib/src/common/command.rs index e2826826d40ed556973ab057865f706a09a6f4ae..a60fc9232d91107777c133ff62aee1a2ccd75e06 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/command.rs +++ b/cumulus/polkadot-omni-node/lib/src/common/command.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::common::spec::NodeSpec; +use crate::common::spec::BaseNodeSpec; use cumulus_client_cli::ExportGenesisHeadCommand; use frame_benchmarking_cli::BlockCmd; #[cfg(any(feature = "runtime-benchmarks"))] @@ -81,7 +81,7 @@ pub trait NodeCommandRunner { impl NodeCommandRunner for T where - T: NodeSpec, + T: BaseNodeSpec, { fn prepare_check_block_cmd( self: Box, diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/mod.rs b/cumulus/polkadot-omni-node/lib/src/common/mod.rs similarity index 91% rename from cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/mod.rs rename to cumulus/polkadot-omni-node/lib/src/common/mod.rs index 907f09263fc1cc4acd1e33a6a4c564ea7060b8b2..37660a5347a20b8fad5b6452eed331f98806c8d7 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/mod.rs +++ b/cumulus/polkadot-omni-node/lib/src/common/mod.rs @@ -26,8 +26,9 @@ pub mod runtime; pub mod spec; pub mod types; -use cumulus_primitives_core::CollectCollationInfo; +use cumulus_primitives_core::{CollectCollationInfo, GetCoreSelectorApi}; use sc_client_db::DbHash; +use serde::de::DeserializeOwned; use sp_api::{ApiExt, CallApiAt, ConstructRuntimeApi, Metadata}; use sp_block_builder::BlockBuilder; use sp_runtime::{ @@ -39,8 +40,7 @@ use sp_transaction_pool::runtime_api::TaggedTransactionQueue; use std::{fmt::Debug, path::PathBuf, str::FromStr}; pub trait NodeBlock: - BlockT - + for<'de> serde::Deserialize<'de> + BlockT + DeserializeOwned { type BoundedFromStrErr: Debug; type BoundedNumber: FromStr + BlockNumber; @@ -49,7 +49,7 @@ pub trait NodeBlock: impl NodeBlock for T where - T: BlockT + for<'de> serde::Deserialize<'de>, + T: BlockT + DeserializeOwned, ::Header: Unpin, as FromStr>::Err: Debug, { @@ -66,6 +66,7 @@ pub trait NodeRuntimeApi: + BlockBuilder + TaggedTransactionQueue + CollectCollationInfo + + GetCoreSelectorApi + Sized { } @@ -76,6 +77,7 @@ impl NodeRuntimeApi for T where + SessionKeys + BlockBuilder + TaggedTransactionQueue + + GetCoreSelectorApi + CollectCollationInfo { } diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/rpc.rs b/cumulus/polkadot-omni-node/lib/src/common/rpc.rs similarity index 79% rename from cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/rpc.rs rename to cumulus/polkadot-omni-node/lib/src/common/rpc.rs index a4e157e87216e12f88b178740478f396424bdde3..85665c9b220fdb56eb773948997f05296d17af9b 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/rpc.rs +++ b/cumulus/polkadot-omni-node/lib/src/common/rpc.rs @@ -40,27 +40,6 @@ pub(crate) trait BuildRpcExtensions { ) -> sc_service::error::Result; } -pub(crate) struct BuildEmptyRpcExtensions(PhantomData<(Block, RuntimeApi)>); - -impl - BuildRpcExtensions< - ParachainClient, - ParachainBackend, - sc_transaction_pool::FullPool>, - > for BuildEmptyRpcExtensions -where - RuntimeApi: - ConstructNodeRuntimeApi> + Send + Sync + 'static, -{ - fn build_rpc_extensions( - _client: Arc>, - _backend: Arc>, - _pool: Arc>>, - ) -> sc_service::error::Result { - Ok(RpcExtension::new(())) - } -} - pub(crate) struct BuildParachainRpcExtensions(PhantomData<(Block, RuntimeApi)>); impl diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/runtime.rs b/cumulus/polkadot-omni-node/lib/src/common/runtime.rs similarity index 99% rename from cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/runtime.rs rename to cumulus/polkadot-omni-node/lib/src/common/runtime.rs index bddbb0a85d0365feb4a337673aab65081b676098..509d13b9d7a26c0068972dc6d2563ec869e07fa9 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/runtime.rs +++ b/cumulus/polkadot-omni-node/lib/src/common/runtime.rs @@ -49,8 +49,6 @@ pub enum Runtime { /// None of the system-chain runtimes, rather the node will act agnostic to the runtime ie. be /// an omni-node, and simply run a node with the given consensus algorithm. Omni(BlockNumber, Consensus), - /// Shell - Shell, } /// Helper trait used for extracting the Runtime variant from the chain spec ID. diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/spec.rs b/cumulus/polkadot-omni-node/lib/src/common/spec.rs similarity index 63% rename from cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/spec.rs rename to cumulus/polkadot-omni-node/lib/src/common/spec.rs index 55e042aed87e7d3879eb3e4c412d52f13d4ca358..dca28b3c28f7bf0f69be16b0b30fb75688fb7ba6 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/spec.rs +++ b/cumulus/polkadot-omni-node/lib/src/common/spec.rs @@ -39,6 +39,7 @@ use sc_network::{config::FullNetworkConfiguration, NetworkBackend, NetworkBlock} use sc_service::{Configuration, ImportQueue, PartialComponents, TaskManager}; use sc_sysinfo::HwBench; use sc_telemetry::{TelemetryHandle, TelemetryWorker}; +use sc_tracing::tracing::Instrument; use sc_transaction_pool::FullPool; use sp_keystore::KeystorePtr; use std::{future::Future, pin::Pin, sync::Arc, time::Duration}; @@ -80,7 +81,9 @@ where fn warn_if_slow_hardware(hwbench: &sc_sysinfo::HwBench) { // Polkadot para-chains should generally use these requirements to ensure that the relay-chain // will not take longer than expected to import its blocks. - if let Err(err) = frame_benchmarking_cli::SUBSTRATE_REFERENCE_HARDWARE.check_hardware(hwbench) { + if let Err(err) = + frame_benchmarking_cli::SUBSTRATE_REFERENCE_HARDWARE.check_hardware(hwbench, false) + { log::warn!( "โš ๏ธ The hardware does not meet the minimal requirements {} for role 'Authority' find out more at:\n\ https://wiki.polkadot.network/docs/maintain-guides-how-to-validate-polkadot#reference-hardware", @@ -89,7 +92,7 @@ fn warn_if_slow_hardware(hwbench: &sc_sysinfo::HwBench) { } } -pub(crate) trait NodeSpec { +pub(crate) trait BaseNodeSpec { type Block: NodeBlock; type RuntimeApi: ConstructNodeRuntimeApi< @@ -99,16 +102,6 @@ pub(crate) trait NodeSpec { type BuildImportQueue: BuildImportQueue; - type BuildRpcExtensions: BuildRpcExtensions< - ParachainClient, - ParachainBackend, - FullPool>, - >; - - type StartConsensus: StartConsensus; - - const SYBIL_RESISTANCE: CollatorSybilResistance; - /// Starts a `ServiceBuilder` for a full service. /// /// Use this macro if you don't actually need the full service, but just the builder in order to @@ -127,14 +120,15 @@ pub(crate) trait NodeSpec { }) .transpose()?; - let heap_pages = config.default_heap_pages.map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |h| { - HeapAllocStrategy::Static { extra_pages: h as _ } - }); + let heap_pages = + config.executor.default_heap_pages.map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |h| { + HeapAllocStrategy::Static { extra_pages: h as _ } + }); let executor = sc_executor::WasmExecutor::::builder() - .with_execution_method(config.wasm_method) - .with_max_runtime_instances(config.max_runtime_instances) - .with_runtime_cache_size(config.runtime_cache_size) + .with_execution_method(config.executor.wasm_method) + .with_max_runtime_instances(config.executor.max_runtime_instances) + .with_runtime_cache_size(config.executor.runtime_cache_size) .with_onchain_heap_alloc_strategy(heap_pages) .with_offchain_heap_alloc_strategy(heap_pages) .build(); @@ -184,6 +178,18 @@ pub(crate) trait NodeSpec { other: (block_import, telemetry, telemetry_worker_handle), }) } +} + +pub(crate) trait NodeSpec: BaseNodeSpec { + type BuildRpcExtensions: BuildRpcExtensions< + ParachainClient, + ParachainBackend, + FullPool>, + >; + + type StartConsensus: StartConsensus; + + const SYBIL_RESISTANCE: CollatorSybilResistance; /// Start a node with the given parachain spec. /// @@ -199,147 +205,153 @@ pub(crate) trait NodeSpec { where Net: NetworkBackend, { - Box::pin(async move { - let parachain_config = prepare_node_config(parachain_config); - - let params = Self::new_partial(¶chain_config)?; - let (block_import, mut telemetry, telemetry_worker_handle) = params.other; - - let client = params.client.clone(); - let backend = params.backend.clone(); - - let mut task_manager = params.task_manager; - let (relay_chain_interface, collator_key) = build_relay_chain_interface( - polkadot_config, - ¶chain_config, - telemetry_worker_handle, - &mut task_manager, - collator_options.clone(), - hwbench.clone(), - ) - .await - .map_err(|e| sc_service::Error::Application(Box::new(e) as Box<_>))?; - - let validator = parachain_config.role.is_authority(); - let prometheus_registry = parachain_config.prometheus_registry().cloned(); - let transaction_pool = params.transaction_pool.clone(); - let import_queue_service = params.import_queue.service(); - let net_config = FullNetworkConfiguration::<_, _, Net>::new( - ¶chain_config.network, - prometheus_registry.clone(), - ); - - let (network, system_rpc_tx, tx_handler_controller, start_network, sync_service) = - build_network(BuildNetworkParams { - parachain_config: ¶chain_config, - net_config, + Box::pin( + async move { + let parachain_config = prepare_node_config(parachain_config); + + let params = Self::new_partial(¶chain_config)?; + let (block_import, mut telemetry, telemetry_worker_handle) = params.other; + + let client = params.client.clone(); + let backend = params.backend.clone(); + + let mut task_manager = params.task_manager; + let (relay_chain_interface, collator_key) = build_relay_chain_interface( + polkadot_config, + ¶chain_config, + telemetry_worker_handle, + &mut task_manager, + collator_options.clone(), + hwbench.clone(), + ) + .await + .map_err(|e| sc_service::Error::Application(Box::new(e) as Box<_>))?; + + let validator = parachain_config.role.is_authority(); + let prometheus_registry = parachain_config.prometheus_registry().cloned(); + let transaction_pool = params.transaction_pool.clone(); + let import_queue_service = params.import_queue.service(); + let net_config = FullNetworkConfiguration::<_, _, Net>::new( + ¶chain_config.network, + prometheus_registry.clone(), + ); + + let (network, system_rpc_tx, tx_handler_controller, start_network, sync_service) = + build_network(BuildNetworkParams { + parachain_config: ¶chain_config, + net_config, + client: client.clone(), + transaction_pool: transaction_pool.clone(), + para_id, + spawn_handle: task_manager.spawn_handle(), + relay_chain_interface: relay_chain_interface.clone(), + import_queue: params.import_queue, + sybil_resistance_level: Self::SYBIL_RESISTANCE, + }) + .await?; + + let rpc_builder = { + let client = client.clone(); + let transaction_pool = transaction_pool.clone(); + let backend_for_rpc = backend.clone(); + + Box::new(move |_| { + Self::BuildRpcExtensions::build_rpc_extensions( + client.clone(), + backend_for_rpc.clone(), + transaction_pool.clone(), + ) + }) + }; + + sc_service::spawn_tasks(sc_service::SpawnTasksParams { + rpc_builder, client: client.clone(), transaction_pool: transaction_pool.clone(), + task_manager: &mut task_manager, + config: parachain_config, + keystore: params.keystore_container.keystore(), + backend: backend.clone(), + network: network.clone(), + sync_service: sync_service.clone(), + system_rpc_tx, + tx_handler_controller, + telemetry: telemetry.as_mut(), + })?; + + if let Some(hwbench) = hwbench { + sc_sysinfo::print_hwbench(&hwbench); + if validator { + warn_if_slow_hardware(&hwbench); + } + + if let Some(ref mut telemetry) = telemetry { + let telemetry_handle = telemetry.handle(); + task_manager.spawn_handle().spawn( + "telemetry_hwbench", + None, + sc_sysinfo::initialize_hwbench_telemetry(telemetry_handle, hwbench), + ); + } + } + + let announce_block = { + let sync_service = sync_service.clone(); + Arc::new(move |hash, data| sync_service.announce_block(hash, data)) + }; + + let relay_chain_slot_duration = Duration::from_secs(6); + + let overseer_handle = relay_chain_interface + .overseer_handle() + .map_err(|e| sc_service::Error::Application(Box::new(e)))?; + + start_relay_chain_tasks(StartRelayChainTasksParams { + client: client.clone(), + announce_block: announce_block.clone(), para_id, - spawn_handle: task_manager.spawn_handle(), relay_chain_interface: relay_chain_interface.clone(), - import_queue: params.import_queue, - sybil_resistance_level: Self::SYBIL_RESISTANCE, - }) - .await?; - - let rpc_builder = { - let client = client.clone(); - let transaction_pool = transaction_pool.clone(); - let backend_for_rpc = backend.clone(); - - Box::new(move |_| { - Self::BuildRpcExtensions::build_rpc_extensions( - client.clone(), - backend_for_rpc.clone(), - transaction_pool.clone(), - ) - }) - }; - - sc_service::spawn_tasks(sc_service::SpawnTasksParams { - rpc_builder, - client: client.clone(), - transaction_pool: transaction_pool.clone(), - task_manager: &mut task_manager, - config: parachain_config, - keystore: params.keystore_container.keystore(), - backend: backend.clone(), - network: network.clone(), - sync_service: sync_service.clone(), - system_rpc_tx, - tx_handler_controller, - telemetry: telemetry.as_mut(), - })?; - - if let Some(hwbench) = hwbench { - sc_sysinfo::print_hwbench(&hwbench); + task_manager: &mut task_manager, + da_recovery_profile: if validator { + DARecoveryProfile::Collator + } else { + DARecoveryProfile::FullNode + }, + import_queue: import_queue_service, + relay_chain_slot_duration, + recovery_handle: Box::new(overseer_handle.clone()), + sync_service, + })?; + if validator { - warn_if_slow_hardware(&hwbench); + Self::StartConsensus::start_consensus( + client.clone(), + block_import, + prometheus_registry.as_ref(), + telemetry.as_ref().map(|t| t.handle()), + &task_manager, + relay_chain_interface.clone(), + transaction_pool, + params.keystore_container.keystore(), + relay_chain_slot_duration, + para_id, + collator_key.expect("Command line arguments do not allow this. qed"), + overseer_handle, + announce_block, + backend.clone(), + node_extra_args, + )?; } - 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), - ); - } - } + start_network.start_network(); - let announce_block = { - let sync_service = sync_service.clone(); - Arc::new(move |hash, data| sync_service.announce_block(hash, data)) - }; - - let relay_chain_slot_duration = Duration::from_secs(6); - - let overseer_handle = relay_chain_interface - .overseer_handle() - .map_err(|e| sc_service::Error::Application(Box::new(e)))?; - - start_relay_chain_tasks(StartRelayChainTasksParams { - client: client.clone(), - announce_block: announce_block.clone(), - para_id, - relay_chain_interface: relay_chain_interface.clone(), - task_manager: &mut task_manager, - da_recovery_profile: if validator { - DARecoveryProfile::Collator - } else { - DARecoveryProfile::FullNode - }, - import_queue: import_queue_service, - relay_chain_slot_duration, - recovery_handle: Box::new(overseer_handle.clone()), - sync_service, - })?; - - if validator { - Self::StartConsensus::start_consensus( - client.clone(), - block_import, - prometheus_registry.as_ref(), - telemetry.as_ref().map(|t| t.handle()), - &task_manager, - relay_chain_interface.clone(), - transaction_pool, - params.keystore_container.keystore(), - relay_chain_slot_duration, - para_id, - collator_key.expect("Command line arguments do not allow this. qed"), - overseer_handle, - announce_block, - backend.clone(), - node_extra_args, - )?; + Ok(task_manager) } - - start_network.start_network(); - - Ok(task_manager) - }) + .instrument(sc_tracing::tracing::info_span!( + sc_tracing::logging::PREFIX_LOG_SPAN, + name = "Parachain", + )), + ) } } diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/types.rs b/cumulus/polkadot-omni-node/lib/src/common/types.rs similarity index 100% rename from cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/types.rs rename to cumulus/polkadot-omni-node/lib/src/common/types.rs diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/mod.rs b/cumulus/polkadot-omni-node/lib/src/fake_runtime_api/mod.rs similarity index 96% rename from cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/mod.rs rename to cumulus/polkadot-omni-node/lib/src/fake_runtime_api/mod.rs index 02aa867d70fecd45cb73e38eed734da8400eccda..bd4ff167d8f11e6501933c29235b7cf5bccebea7 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/mod.rs +++ b/cumulus/polkadot-omni-node/lib/src/fake_runtime_api/mod.rs @@ -24,12 +24,14 @@ use utils::{impl_node_runtime_apis, imports::*}; type CustomBlock = crate::common::types::Block; pub mod aura_sr25519 { use super::*; + #[allow(dead_code)] struct FakeRuntime; impl_node_runtime_apis!(FakeRuntime, CustomBlock, sp_consensus_aura::sr25519::AuthorityId); } pub mod aura_ed25519 { use super::*; + #[allow(dead_code)] struct FakeRuntime; impl_node_runtime_apis!(FakeRuntime, CustomBlock, sp_consensus_aura::ed25519::AuthorityId); } diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/utils.rs b/cumulus/polkadot-omni-node/lib/src/fake_runtime_api/utils.rs similarity index 95% rename from cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/utils.rs rename to cumulus/polkadot-omni-node/lib/src/fake_runtime_api/utils.rs index 442b87b5d77523150b4464739c23bbb7b80522e5..0b1ed5d82889e5f70898dd5b82696e78c247cf3b 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/utils.rs +++ b/cumulus/polkadot-omni-node/lib/src/fake_runtime_api/utils.rs @@ -15,6 +15,7 @@ // along with Cumulus. If not, see . pub(crate) mod imports { + pub use cumulus_primitives_core::{ClaimQueueOffset, CoreSelector}; pub use parachains_common::{AccountId, Balance, Nonce}; pub use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; pub use sp_runtime::{ @@ -156,6 +157,12 @@ macro_rules! impl_node_runtime_apis { } } + impl cumulus_primitives_core::GetCoreSelectorApi<$block> for $runtime { + fn core_selector() -> (CoreSelector, ClaimQueueOffset) { + unimplemented!() + } + } + #[cfg(feature = "try-runtime")] impl frame_try_runtime::TryRuntime<$block> for $runtime { fn on_runtime_upgrade( diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/lib.rs b/cumulus/polkadot-omni-node/lib/src/lib.rs similarity index 99% rename from cumulus/polkadot-parachain/polkadot-parachain-lib/src/lib.rs rename to cumulus/polkadot-omni-node/lib/src/lib.rs index 6aa2f656a48ba626c432871f85d44c2965c462a3..a293ab225c6f575d0fee14c6663391f7d59692b5 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/lib.rs +++ b/cumulus/polkadot-omni-node/lib/src/lib.rs @@ -45,7 +45,7 @@ mod cli; mod command; mod common; mod fake_runtime_api; -mod service; +mod nodes; pub use cli::CliConfig; pub use command::{run, RunConfig}; diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/service.rs b/cumulus/polkadot-omni-node/lib/src/nodes/aura.rs similarity index 77% rename from cumulus/polkadot-parachain/polkadot-parachain-lib/src/service.rs rename to cumulus/polkadot-omni-node/lib/src/nodes/aura.rs index 303ec1e3b298bf551bfbdbd118bb077fbb2dfa77..cf1ee91cbab5bb9c87b530057a90826347f310f0 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/service.rs +++ b/cumulus/polkadot-omni-node/lib/src/nodes/aura.rs @@ -17,15 +17,15 @@ use crate::{ common::{ aura::{AuraIdT, AuraRuntimeApi}, - rpc::{BuildEmptyRpcExtensions, BuildParachainRpcExtensions}, - spec::{BuildImportQueue, DynNodeSpec, NodeSpec, StartConsensus}, + rpc::BuildParachainRpcExtensions, + spec::{BaseNodeSpec, BuildImportQueue, NodeSpec, StartConsensus}, types::{ - AccountId, Balance, Block, Hash, Nonce, ParachainBackend, ParachainBlockImport, + AccountId, Balance, Hash, Nonce, ParachainBackend, ParachainBlockImport, ParachainClient, }, ConstructNodeRuntimeApi, NodeBlock, NodeExtraArgs, }, - fake_runtime_api::aura_sr25519::RuntimeApi as FakeRuntimeApi, + nodes::DynNodeSpecExt, }; use cumulus_client_collator::service::{ CollatorService, ServiceInterface as CollatorServiceInterface, @@ -38,7 +38,6 @@ use cumulus_client_consensus_aura::collators::slot_based::{ use cumulus_client_consensus_proposer::{Proposer, ProposerInterface}; use cumulus_client_consensus_relay_chain::Verifier as RelayChainVerifier; #[allow(deprecated)] -use cumulus_client_service::old_consensus; use cumulus_client_service::CollatorSybilResistance; use cumulus_primitives_core::{relay_chain::ValidationCode, ParaId}; use cumulus_relay_chain_interface::{OverseerHandle, RelayChainInterface}; @@ -63,40 +62,6 @@ use sp_runtime::{ }; use std::{marker::PhantomData, sync::Arc, time::Duration}; -/// Build the import queue for the shell runtime. -pub(crate) struct BuildShellImportQueue; - -impl BuildImportQueue, FakeRuntimeApi> for BuildShellImportQueue { - fn build_import_queue( - client: Arc, FakeRuntimeApi>>, - block_import: ParachainBlockImport, FakeRuntimeApi>, - config: &Configuration, - _telemetry_handle: Option, - task_manager: &TaskManager, - ) -> sc_service::error::Result>> { - cumulus_client_consensus_relay_chain::import_queue( - client, - block_import, - |_, _| async { Ok(()) }, - &task_manager.spawn_essential_handle(), - config.prometheus_registry(), - ) - .map_err(Into::into) - } -} - -pub(crate) struct ShellNode; - -impl NodeSpec for ShellNode { - type Block = Block; - type RuntimeApi = FakeRuntimeApi; - type BuildImportQueue = BuildShellImportQueue; - type BuildRpcExtensions = BuildEmptyRpcExtensions, Self::RuntimeApi>; - type StartConsensus = StartRelayChainConsensus; - - const SYBIL_RESISTANCE: CollatorSybilResistance = CollatorSybilResistance::Unresistant; -} - struct Verifier { client: Arc, aura_verifier: Box>, @@ -205,7 +170,7 @@ impl Default } } -impl NodeSpec +impl BaseNodeSpec for AuraNode where Block: NodeBlock, @@ -214,11 +179,23 @@ where + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi + substrate_frame_rpc_system::AccountNonceApi, AuraId: AuraIdT + Sync, - StartConsensus: self::StartConsensus + 'static, { type Block = Block; type RuntimeApi = RuntimeApi; type BuildImportQueue = BuildRelayToAuraImportQueue; +} + +impl NodeSpec + for AuraNode +where + Block: NodeBlock, + RuntimeApi: ConstructNodeRuntimeApi>, + RuntimeApi::RuntimeApi: AuraRuntimeApi + + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi + + substrate_frame_rpc_system::AccountNonceApi, + AuraId: AuraIdT + Sync, + StartConsensus: self::StartConsensus + 'static, +{ type BuildRpcExtensions = BuildParachainRpcExtensions; type StartConsensus = StartConsensus; const SYBIL_RESISTANCE: CollatorSybilResistance = CollatorSybilResistance::Resistant; @@ -226,7 +203,7 @@ where pub fn new_aura_node_spec( extra_args: &NodeExtraArgs, -) -> Box +) -> Box where Block: NodeBlock, RuntimeApi: ConstructNodeRuntimeApi>, @@ -252,82 +229,6 @@ where } } -/// Start relay-chain consensus that is free for all. Everyone can submit a block, the relay-chain -/// decides what is backed and included. -pub(crate) struct StartRelayChainConsensus; - -impl StartConsensus, FakeRuntimeApi> for StartRelayChainConsensus { - fn start_consensus( - client: Arc, FakeRuntimeApi>>, - block_import: ParachainBlockImport, FakeRuntimeApi>, - prometheus_registry: Option<&Registry>, - telemetry: Option, - task_manager: &TaskManager, - relay_chain_interface: Arc, - transaction_pool: Arc, ParachainClient, FakeRuntimeApi>>>, - _keystore: KeystorePtr, - _relay_chain_slot_duration: Duration, - para_id: ParaId, - collator_key: CollatorPair, - overseer_handle: OverseerHandle, - announce_block: Arc>) + Send + Sync>, - _backend: Arc>>, - _node_extra_args: NodeExtraArgs, - ) -> Result<(), 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. pub(crate) struct StartSlotBasedAuraConsensus( PhantomData<(Block, RuntimeApi, AuraId)>, @@ -392,7 +293,7 @@ where relay_chain_interface: Arc, transaction_pool: Arc>>, keystore: KeystorePtr, - relay_chain_slot_duration: Duration, + _relay_chain_slot_duration: Duration, para_id: ParaId, collator_key: CollatorPair, _overseer_handle: OverseerHandle, @@ -429,7 +330,6 @@ where keystore, collator_key, para_id, - relay_chain_slot_duration, proposer, collator_service, authoring_duration: Duration::from_millis(2000), diff --git a/cumulus/polkadot-omni-node/lib/src/nodes/manual_seal.rs b/cumulus/polkadot-omni-node/lib/src/nodes/manual_seal.rs new file mode 100644 index 0000000000000000000000000000000000000000..d00d7adf27e1d35e71f4a6b122dc34e223b981ae --- /dev/null +++ b/cumulus/polkadot-omni-node/lib/src/nodes/manual_seal.rs @@ -0,0 +1,233 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +use crate::common::{ + rpc::BuildRpcExtensions as BuildRpcExtensionsT, + spec::{BaseNodeSpec, BuildImportQueue, NodeSpec as NodeSpecT}, + types::{Hash, ParachainBlockImport, ParachainClient}, +}; +use codec::Encode; +use cumulus_client_parachain_inherent::{MockValidationDataInherentDataProvider, MockXcmConfig}; +use cumulus_primitives_core::ParaId; +use sc_consensus::{DefaultImportQueue, LongestChain}; +use sc_consensus_manual_seal::rpc::{ManualSeal, ManualSealApiServer}; +use sc_network::NetworkBackend; +use sc_service::{build_polkadot_syncing_strategy, Configuration, PartialComponents, TaskManager}; +use sc_telemetry::TelemetryHandle; +use sp_runtime::traits::Header; +use sp_timestamp::Timestamp; +use std::{marker::PhantomData, sync::Arc}; + +pub struct ManualSealNode(PhantomData); + +impl BuildImportQueue + for ManualSealNode +{ + fn build_import_queue( + client: Arc>, + _block_import: ParachainBlockImport, + config: &Configuration, + _telemetry_handle: Option, + task_manager: &TaskManager, + ) -> sc_service::error::Result> { + Ok(sc_consensus_manual_seal::import_queue( + Box::new(client.clone()), + &task_manager.spawn_essential_handle(), + config.prometheus_registry(), + )) + } +} + +impl BaseNodeSpec for ManualSealNode { + type Block = NodeSpec::Block; + type RuntimeApi = NodeSpec::RuntimeApi; + type BuildImportQueue = Self; +} + +impl ManualSealNode { + pub fn new() -> Self { + Self(Default::default()) + } + + pub fn start_node( + &self, + mut config: Configuration, + para_id: ParaId, + block_time: u64, + ) -> sc_service::error::Result + where + Net: NetworkBackend, + { + let PartialComponents { + client, + backend, + mut task_manager, + import_queue, + keystore_container, + select_chain: _, + transaction_pool, + other: (_, mut telemetry, _), + } = Self::new_partial(&config)?; + let select_chain = LongestChain::new(backend.clone()); + + // Since this is a dev node, prevent it from connecting to peers. + config.network.default_peers_set.in_peers = 0; + config.network.default_peers_set.out_peers = 0; + let mut net_config = sc_network::config::FullNetworkConfiguration::<_, _, Net>::new( + &config.network, + config.prometheus_config.as_ref().map(|cfg| cfg.registry.clone()), + ); + let metrics = Net::register_notification_metrics( + config.prometheus_config.as_ref().map(|cfg| &cfg.registry), + ); + + let syncing_strategy = build_polkadot_syncing_strategy( + config.protocol_id(), + config.chain_spec.fork_id(), + &mut net_config, + None, + client.clone(), + &task_manager.spawn_handle(), + config.prometheus_config.as_ref().map(|config| &config.registry), + )?; + + let (network, system_rpc_tx, tx_handler_controller, start_network, sync_service) = + sc_service::build_network(sc_service::BuildNetworkParams { + config: &config, + client: client.clone(), + transaction_pool: transaction_pool.clone(), + spawn_handle: task_manager.spawn_handle(), + import_queue, + net_config, + block_announce_validator_builder: None, + syncing_strategy, + block_relay: None, + metrics, + })?; + + let proposer = sc_basic_authorship::ProposerFactory::new( + task_manager.spawn_handle(), + client.clone(), + transaction_pool.clone(), + None, + None, + ); + + let (manual_seal_sink, manual_seal_stream) = futures::channel::mpsc::channel(1024); + let mut manual_seal_sink_clone = manual_seal_sink.clone(); + task_manager + .spawn_essential_handle() + .spawn("block_authoring", None, async move { + loop { + futures_timer::Delay::new(std::time::Duration::from_millis(block_time)).await; + manual_seal_sink_clone + .try_send(sc_consensus_manual_seal::EngineCommand::SealNewBlock { + create_empty: true, + finalize: true, + parent_hash: None, + sender: None, + }) + .unwrap(); + } + }); + + let client_for_cidp = client.clone(); + let params = sc_consensus_manual_seal::ManualSealParams { + block_import: client.clone(), + env: proposer, + client: client.clone(), + pool: transaction_pool.clone(), + select_chain, + commands_stream: Box::pin(manual_seal_stream), + consensus_data_provider: None, + create_inherent_data_providers: move |block: Hash, ()| { + let current_para_head = client_for_cidp + .header(block) + .expect("Header lookup should succeed") + .expect("Header passed in as parent should be present in backend."); + let current_para_block_head = + Some(polkadot_primitives::HeadData(current_para_head.encode())); + let client_for_xcm = client_for_cidp.clone(); + async move { + use sp_runtime::traits::UniqueSaturatedInto; + + let mocked_parachain = MockValidationDataInherentDataProvider { + // When using manual seal we start from block 0, and it's very unlikely to + // reach a block number > u32::MAX. + current_para_block: UniqueSaturatedInto::::unique_saturated_into( + *current_para_head.number(), + ), + para_id, + current_para_block_head, + relay_offset: 1000, + relay_blocks_per_para_block: 1, + para_blocks_per_relay_epoch: 10, + relay_randomness_config: (), + xcm_config: MockXcmConfig::new(&*client_for_xcm, block, Default::default()), + raw_downward_messages: vec![], + raw_horizontal_messages: vec![], + additional_key_values: None, + }; + Ok(( + sp_timestamp::InherentDataProvider::new(Timestamp::new(0)), + mocked_parachain, + )) + } + }, + }; + let authorship_future = sc_consensus_manual_seal::run_manual_seal(params); + task_manager.spawn_essential_handle().spawn_blocking( + "manual-seal", + None, + authorship_future, + ); + let rpc_extensions_builder = { + let client = client.clone(); + let transaction_pool = transaction_pool.clone(); + let backend_for_rpc = backend.clone(); + + Box::new(move |_| { + let mut module = NodeSpec::BuildRpcExtensions::build_rpc_extensions( + client.clone(), + backend_for_rpc.clone(), + transaction_pool.clone(), + )?; + module + .merge(ManualSeal::new(manual_seal_sink.clone()).into_rpc()) + .map_err(|e| sc_service::Error::Application(e.into()))?; + Ok(module) + }) + }; + + let _rpc_handlers = sc_service::spawn_tasks(sc_service::SpawnTasksParams { + network, + client: client.clone(), + keystore: keystore_container.keystore(), + task_manager: &mut task_manager, + transaction_pool: transaction_pool.clone(), + rpc_builder: rpc_extensions_builder, + backend, + system_rpc_tx, + tx_handler_controller, + sync_service, + config, + telemetry: telemetry.as_mut(), + })?; + + start_network.start_network(); + Ok(task_manager) + } +} diff --git a/cumulus/polkadot-omni-node/lib/src/nodes/mod.rs b/cumulus/polkadot-omni-node/lib/src/nodes/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..ab13322e80ab9123732b427107c540107c38068d --- /dev/null +++ b/cumulus/polkadot-omni-node/lib/src/nodes/mod.rs @@ -0,0 +1,57 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +pub mod aura; +mod manual_seal; + +use crate::common::spec::{DynNodeSpec, NodeSpec as NodeSpecT}; +use cumulus_primitives_core::ParaId; +use manual_seal::ManualSealNode; +use sc_service::{Configuration, TaskManager}; + +/// Trait that extends the `DynNodeSpec` trait with manual seal related logic. +/// +/// We need it in order to be able to access both the `DynNodeSpec` and the manual seal logic +/// through dynamic dispatch. +pub trait DynNodeSpecExt: DynNodeSpec { + fn start_manual_seal_node( + &self, + config: Configuration, + para_id: ParaId, + block_time: u64, + ) -> sc_service::error::Result; +} + +impl DynNodeSpecExt for T +where + T: NodeSpecT + DynNodeSpec, +{ + #[sc_tracing::logging::prefix_logs_with("Parachain")] + fn start_manual_seal_node( + &self, + config: Configuration, + para_id: ParaId, + block_time: u64, + ) -> sc_service::error::Result { + let node = ManualSealNode::::new(); + match config.network.network_backend { + sc_network::config::NetworkBackendType::Libp2p => + node.start_node::>(config, para_id, block_time), + sc_network::config::NetworkBackendType::Litep2p => + node.start_node::(config, para_id, block_time), + } + } +} diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/benchmark_storage_works.rs b/cumulus/polkadot-omni-node/lib/src/tests/benchmark_storage_works.rs similarity index 91% rename from cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/benchmark_storage_works.rs rename to cumulus/polkadot-omni-node/lib/src/tests/benchmark_storage_works.rs index c554b5b3d6be5602d84aa33c5d89020af26be74b..8502188af511256ee3c66126f98f6f8c9b6a7a13 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/benchmark_storage_works.rs +++ b/cumulus/polkadot-omni-node/lib/src/tests/benchmark_storage_works.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Substrate. +// This file is part of Cumulus. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/common.rs b/cumulus/polkadot-omni-node/lib/src/tests/common.rs similarity index 95% rename from cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/common.rs rename to cumulus/polkadot-omni-node/lib/src/tests/common.rs index 20926ddd91db53de52d32fb952ded38f8f868b97..d3f41fb50bc6f095b245154d310f98261c2d61e8 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/common.rs +++ b/cumulus/polkadot-omni-node/lib/src/tests/common.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Substrate. +// This file is part of Cumulus. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/polkadot_argument_parsing.rs b/cumulus/polkadot-omni-node/lib/src/tests/polkadot_argument_parsing.rs similarity index 87% rename from cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/polkadot_argument_parsing.rs rename to cumulus/polkadot-omni-node/lib/src/tests/polkadot_argument_parsing.rs index 9337da85d74b16f594aa7baacf3e76a35b1bb976..d1f497c1187a7cc6da300226c8d67b60312e8794 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/polkadot_argument_parsing.rs +++ b/cumulus/polkadot-omni-node/lib/src/tests/polkadot_argument_parsing.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Substrate. +// This file is part of Cumulus. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/polkadot_mdns_issue.rs b/cumulus/polkadot-omni-node/lib/src/tests/polkadot_mdns_issue.rs similarity index 85% rename from cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/polkadot_mdns_issue.rs rename to cumulus/polkadot-omni-node/lib/src/tests/polkadot_mdns_issue.rs index e3ccb7fe0fbdc2a7de5e04553985657b8b87733d..3b0b08e57f839073f9fd6a0f64cf57c6a3b989a0 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/polkadot_mdns_issue.rs +++ b/cumulus/polkadot-omni-node/lib/src/tests/polkadot_mdns_issue.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Substrate. +// This file is part of Cumulus. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/purge_chain_works.rs b/cumulus/polkadot-omni-node/lib/src/tests/purge_chain_works.rs similarity index 91% rename from cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/purge_chain_works.rs rename to cumulus/polkadot-omni-node/lib/src/tests/purge_chain_works.rs index 6415a914c7a3c8e1f381a08c29eef9cb3c1ebe55..65a946e890bd8e32cdc7e497ee729281fcd2655a 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/purge_chain_works.rs +++ b/cumulus/polkadot-omni-node/lib/src/tests/purge_chain_works.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Substrate. +// This file is part of Cumulus. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/running_the_node_and_interrupt.rs b/cumulus/polkadot-omni-node/lib/src/tests/running_the_node_and_interrupt.rs similarity index 85% rename from cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/running_the_node_and_interrupt.rs rename to cumulus/polkadot-omni-node/lib/src/tests/running_the_node_and_interrupt.rs index 0f4ae6992382c7a8a58da25ac110b97b6db72f62..a45fd7f4575a056aa135e81475ac7cabc6eaae18 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/tests/running_the_node_and_interrupt.rs +++ b/cumulus/polkadot-omni-node/lib/src/tests/running_the_node_and_interrupt.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Substrate. +// This file is part of Cumulus. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/cumulus/polkadot-omni-node/src/main.rs b/cumulus/polkadot-omni-node/src/main.rs new file mode 100644 index 0000000000000000000000000000000000000000..a86ec6f6fde61ae07faf07985dfcb19ef5ecf255 --- /dev/null +++ b/cumulus/polkadot-omni-node/src/main.rs @@ -0,0 +1,60 @@ +// 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 . + +//! Basic polkadot omni-node. +//! +//! It can be used to start a parachain node from a provided chain spec file. +//! It is only compatible with runtimes that use block number `u32` and `Aura` consensus. +//! +//! Example: `polkadot-omni-node --chain [chain_spec.json]` + +#![warn(missing_docs)] +#![warn(unused_extern_crates)] + +use polkadot_omni_node_lib::{ + chain_spec::DiskChainSpecLoader, run, runtime::DefaultRuntimeResolver, CliConfig as CliConfigT, + RunConfig, +}; + +struct CliConfig; + +impl CliConfigT for CliConfig { + fn impl_version() -> String { + env!("SUBSTRATE_CLI_IMPL_VERSION").into() + } + + fn author() -> String { + env!("CARGO_PKG_AUTHORS").into() + } + + fn support_url() -> String { + "https://github.com/paritytech/polkadot-sdk/issues/new".into() + } + + fn copyright_start_year() -> u16 { + 2017 + } +} + +fn main() -> color_eyre::eyre::Result<()> { + color_eyre::install()?; + + let config = RunConfig { + chain_spec_loader: Box::new(DiskChainSpecLoader), + runtime_resolver: Box::new(DefaultRuntimeResolver), + }; + Ok(run::(config)?) +} diff --git a/cumulus/polkadot-parachain/Cargo.toml b/cumulus/polkadot-parachain/Cargo.toml index 383e0f158bf49b30e743dc0fe530f5e84560fdf0..426679ce0cd59d3b26e3235292483d2a71b379ce 100644 --- a/cumulus/polkadot-parachain/Cargo.toml +++ b/cumulus/polkadot-parachain/Cargo.toml @@ -22,11 +22,9 @@ serde = { features = ["derive"], workspace = true, default-features = true } serde_json = { workspace = true, default-features = true } # Local -polkadot-parachain-lib = { features = ["rococo-native", "westend-native"], workspace = true } +polkadot-omni-node-lib = { features = ["rococo-native", "westend-native"], workspace = true } rococo-parachain-runtime = { workspace = true } -shell-runtime = { workspace = true } glutton-westend-runtime = { workspace = true } -seedling-runtime = { workspace = true } asset-hub-rococo-runtime = { workspace = true, default-features = true } asset-hub-westend-runtime = { workspace = true } collectives-westend-runtime = { workspace = true } @@ -39,20 +37,15 @@ penpal-runtime = { workspace = true } people-rococo-runtime = { workspace = true } people-westend-runtime = { workspace = true } parachains-common = { workspace = true, default-features = true } -testnet-parachains-constants = { features = [ - "rococo", - "westend", -], workspace = true } # Substrate -sp-runtime = { workspace = true } sp-core = { workspace = true, default-features = true } sc-cli = { workspace = true, default-features = true } sc-service = { workspace = true, default-features = true } sc-chain-spec = { workspace = true, default-features = true } +sp-genesis-builder = { workspace = true, default-features = true } # Polkadot -polkadot-service = { workspace = true, default-features = true } xcm = { workspace = true, default-features = true } # Cumulus @@ -66,10 +59,8 @@ default = [] runtime-benchmarks = [ "cumulus-primitives-core/runtime-benchmarks", "parachains-common/runtime-benchmarks", - "polkadot-parachain-lib/runtime-benchmarks", - "polkadot-service/runtime-benchmarks", + "polkadot-omni-node-lib/runtime-benchmarks", "sc-service/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", "asset-hub-rococo-runtime/runtime-benchmarks", "asset-hub-westend-runtime/runtime-benchmarks", @@ -86,9 +77,7 @@ runtime-benchmarks = [ "rococo-parachain-runtime/runtime-benchmarks", ] try-runtime = [ - "polkadot-parachain-lib/try-runtime", - "polkadot-service/try-runtime", - "sp-runtime/try-runtime", + "polkadot-omni-node-lib/try-runtime", "asset-hub-rococo-runtime/try-runtime", "asset-hub-westend-runtime/try-runtime", @@ -102,7 +91,6 @@ try-runtime = [ "penpal-runtime/try-runtime", "people-rococo-runtime/try-runtime", "people-westend-runtime/try-runtime", - "shell-runtime/try-runtime", ] fast-runtime = [ "bridge-hub-rococo-runtime/fast-runtime", diff --git a/cumulus/polkadot-parachain/build.rs b/cumulus/polkadot-parachain/build.rs index dd0d112bca7018bac1ca250d9efc3a3972b6ca41..8c498735eae965dfd3686c9031843c2c6d13f992 100644 --- a/cumulus/polkadot-parachain/build.rs +++ b/cumulus/polkadot-parachain/build.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Cumulus. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/cumulus/polkadot-parachain/src/chain_spec/asset_hubs.rs b/cumulus/polkadot-parachain/src/chain_spec/asset_hubs.rs index f6bf6375a35331a2b4079c6c1a499c3d39549251..ec2afc743de8a9d459787933d5c6276744f03b8f 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/asset_hubs.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/asset_hubs.rs @@ -14,30 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::chain_spec::{get_account_id_from_seed, get_collator_keys_from_seed, SAFE_XCM_VERSION}; -use cumulus_primitives_core::ParaId; -use hex_literal::hex; -use parachains_common::{AccountId, AuraId, Balance as AssetHubBalance}; -use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; +use polkadot_omni_node_lib::chain_spec::{Extensions, GenericChainSpec}; use sc_service::ChainType; -use sp_core::{crypto::UncheckedInto, sr25519}; - -const ASSET_HUB_WESTEND_ED: AssetHubBalance = asset_hub_westend_runtime::ExistentialDeposit::get(); -const ASSET_HUB_ROCOCO_ED: AssetHubBalance = asset_hub_rococo_runtime::ExistentialDeposit::get(); - -/// Generate the session keys from individual elements. -/// -/// The input must be a tuple of individual keys (a single arg for now since we have just one key). -pub fn asset_hub_rococo_session_keys(keys: AuraId) -> asset_hub_rococo_runtime::SessionKeys { - asset_hub_rococo_runtime::SessionKeys { aura: keys } -} - -/// Generate the session keys from individual elements. -/// -/// The input must be a tuple of individual keys (a single arg for now since we have just one key). -pub fn asset_hub_westend_session_keys(keys: AuraId) -> asset_hub_westend_runtime::SessionKeys { - asset_hub_westend_runtime::SessionKeys { aura: keys } -} pub fn asset_hub_westend_development_config() -> GenericChainSpec { let mut properties = sc_chain_spec::Properties::new(); @@ -52,21 +30,7 @@ pub fn asset_hub_westend_development_config() -> GenericChainSpec { .with_name("Westend Asset Hub Development") .with_id("asset-hub-westend-dev") .with_chain_type(ChainType::Local) - .with_genesis_config_patch(asset_hub_westend_genesis( - // initial collators. - vec![( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - )], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - ], - testnet_parachains_constants::westend::currency::UNITS * 1_000_000, - 1000.into(), - )) + .with_genesis_config_preset_name(sp_genesis_builder::DEV_RUNTIME_PRESET) .with_properties(properties) .build() } @@ -84,35 +48,7 @@ pub fn asset_hub_westend_local_config() -> GenericChainSpec { .with_name("Westend Asset Hub Local") .with_id("asset-hub-westend-local") .with_chain_type(ChainType::Local) - .with_genesis_config_patch(asset_hub_westend_genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed::("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - testnet_parachains_constants::westend::currency::UNITS * 1_000_000, - 1000.into(), - )) + .with_genesis_config_preset_name(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET) .with_properties(properties) .build() } @@ -130,77 +66,11 @@ pub fn asset_hub_westend_config() -> GenericChainSpec { .with_name("Westend Asset Hub") .with_id("asset-hub-westend") .with_chain_type(ChainType::Live) - .with_genesis_config_patch(asset_hub_westend_genesis( - // initial collators. - vec![ - ( - hex!("9cfd429fa002114f33c1d3e211501d62830c9868228eb3b4b8ae15a83de04325").into(), - hex!("9cfd429fa002114f33c1d3e211501d62830c9868228eb3b4b8ae15a83de04325") - .unchecked_into(), - ), - ( - hex!("12a03fb4e7bda6c9a07ec0a11d03c24746943e054ff0bb04938970104c783876").into(), - hex!("12a03fb4e7bda6c9a07ec0a11d03c24746943e054ff0bb04938970104c783876") - .unchecked_into(), - ), - ( - hex!("1256436307dfde969324e95b8c62cb9101f520a39435e6af0f7ac07b34e1931f").into(), - hex!("1256436307dfde969324e95b8c62cb9101f520a39435e6af0f7ac07b34e1931f") - .unchecked_into(), - ), - ( - hex!("98102b7bca3f070f9aa19f58feed2c0a4e107d203396028ec17a47e1ed80e322").into(), - hex!("98102b7bca3f070f9aa19f58feed2c0a4e107d203396028ec17a47e1ed80e322") - .unchecked_into(), - ), - ], - Vec::new(), - ASSET_HUB_WESTEND_ED * 4096, - 1000.into(), - )) + .with_genesis_config_preset_name("genesis") .with_properties(properties) .build() } -fn asset_hub_westend_genesis( - invulnerables: Vec<(AccountId, AuraId)>, - endowed_accounts: Vec, - endowment: AssetHubBalance, - id: ParaId, -) -> serde_json::Value { - serde_json::json!({ - "balances": { - "balances": endowed_accounts - .iter() - .cloned() - .map(|k| (k, endowment)) - .collect::>(), - }, - "parachainInfo": { - "parachainId": id, - }, - "collatorSelection": { - "invulnerables": invulnerables.iter().cloned().map(|(acc, _)| acc).collect::>(), - "candidacyBond": ASSET_HUB_WESTEND_ED * 16, - }, - "session": { - "keys": invulnerables - .into_iter() - .map(|(acc, aura)| { - ( - acc.clone(), // account id - acc, // validator id - asset_hub_westend_session_keys(aura), // session keys - ) - }) - .collect::>(), - }, - "polkadotXcm": { - "safeXcmVersion": Some(SAFE_XCM_VERSION), - }, - }) -} - pub fn asset_hub_rococo_development_config() -> GenericChainSpec { let mut properties = sc_chain_spec::Properties::new(); properties.insert("ss58Format".into(), 42.into()); @@ -227,21 +97,7 @@ fn asset_hub_rococo_like_development_config( .with_name(name) .with_id(chain_id) .with_chain_type(ChainType::Local) - .with_genesis_config_patch(asset_hub_rococo_genesis( - // initial collators. - vec![( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - )], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - ], - testnet_parachains_constants::rococo::currency::UNITS * 1_000_000, - para_id.into(), - )) + .with_genesis_config_preset_name(sp_genesis_builder::DEV_RUNTIME_PRESET) .with_properties(properties) .build() } @@ -272,35 +128,7 @@ fn asset_hub_rococo_like_local_config( .with_name(name) .with_id(chain_id) .with_chain_type(ChainType::Local) - .with_genesis_config_patch(asset_hub_rococo_genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed::("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - testnet_parachains_constants::rococo::currency::UNITS * 1_000_000, - para_id.into(), - )) + .with_genesis_config_preset_name(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET) .with_properties(properties) .build() } @@ -317,81 +145,7 @@ pub fn asset_hub_rococo_genesis_config() -> GenericChainSpec { .with_name("Rococo Asset Hub") .with_id("asset-hub-rococo") .with_chain_type(ChainType::Live) - .with_genesis_config_patch(asset_hub_rococo_genesis( - // initial collators. - vec![ - // E8XC6rTJRsioKCp6KMy6zd24ykj4gWsusZ3AkSeyavpVBAG - ( - hex!("44cb62d1d6cdd2fff2a5ef3bb7ef827be5b3e117a394ecaa634d8dd9809d5608").into(), - hex!("44cb62d1d6cdd2fff2a5ef3bb7ef827be5b3e117a394ecaa634d8dd9809d5608") - .unchecked_into(), - ), - // G28iWEybndgGRbhfx83t7Q42YhMPByHpyqWDUgeyoGF94ri - ( - hex!("9864b85e23aa4506643db9879c3dbbeabaa94d269693a4447f537dd6b5893944").into(), - hex!("9864b85e23aa4506643db9879c3dbbeabaa94d269693a4447f537dd6b5893944") - .unchecked_into(), - ), - // G839e2eMiq7UXbConsY6DS1XDAYG2XnQxAmLuRLGGQ3Px9c - ( - hex!("9ce5741ee2f1ac3bdedbde9f3339048f4da2cb88ddf33a0977fa0b4cf86e2948").into(), - hex!("9ce5741ee2f1ac3bdedbde9f3339048f4da2cb88ddf33a0977fa0b4cf86e2948") - .unchecked_into(), - ), - // GLao4ukFUW6qhexuZowdFrKa2NLCfnEjZMftSXXfvGv1vvt - ( - hex!("a676ed15f5a325eab49ed8d5f8c00f3f814b19bb58cda14ad10894c078dd337f").into(), - hex!("a676ed15f5a325eab49ed8d5f8c00f3f814b19bb58cda14ad10894c078dd337f") - .unchecked_into(), - ), - ], - Vec::new(), - ASSET_HUB_ROCOCO_ED * 524_288, - para_id.into(), - )) + .with_genesis_config_preset_name("genesis") .with_properties(properties) .build() } - -fn asset_hub_rococo_genesis( - invulnerables: Vec<(AccountId, AuraId)>, - endowed_accounts: Vec, - endowment: AssetHubBalance, - id: ParaId, -) -> serde_json::Value { - serde_json::json!({ - "balances": asset_hub_rococo_runtime::BalancesConfig { - balances: endowed_accounts - .iter() - .cloned() - .map(|k| (k, endowment)) - .collect(), - }, - "parachainInfo": asset_hub_rococo_runtime::ParachainInfoConfig { - parachain_id: id, - ..Default::default() - }, - "collatorSelection": asset_hub_rococo_runtime::CollatorSelectionConfig { - invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(), - candidacy_bond: ASSET_HUB_ROCOCO_ED * 16, - ..Default::default() - }, - "session": asset_hub_rococo_runtime::SessionConfig { - keys: invulnerables - .into_iter() - .map(|(acc, aura)| { - ( - acc.clone(), // account id - acc, // validator id - asset_hub_rococo_session_keys(aura), // session keys - ) - }) - .collect(), - ..Default::default() - }, - "polkadotXcm": asset_hub_rococo_runtime::PolkadotXcmConfig { - safe_xcm_version: Some(SAFE_XCM_VERSION), - ..Default::default() - } - }) -} diff --git a/cumulus/polkadot-parachain/src/chain_spec/bridge_hubs.rs b/cumulus/polkadot-parachain/src/chain_spec/bridge_hubs.rs index 754bd851b40ad326aa1e199152b458153b525202..839e93d0a67b316cd95ef56e9b0a67eaa9dc6485 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/bridge_hubs.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/bridge_hubs.rs @@ -14,12 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::chain_spec::{get_account_id_from_seed, get_collator_keys_from_seed}; use cumulus_primitives_core::ParaId; -use parachains_common::Balance as BridgeHubBalance; -use polkadot_parachain_lib::chain_spec::GenericChainSpec; -use sc_chain_spec::ChainSpec; -use sp_core::sr25519; +use polkadot_omni_node_lib::chain_spec::GenericChainSpec; +use sc_chain_spec::{ChainSpec, ChainType}; use std::str::FromStr; /// Collects all supported BridgeHub configurations @@ -81,14 +78,14 @@ impl BridgeHubRuntimeType { "Westend BridgeHub Local", "westend-local", ParaId::new(1002), - Some("Bob".to_string()), + ChainType::Local, ))), BridgeHubRuntimeType::WestendDevelopment => Ok(Box::new(westend::local_config( westend::BRIDGE_HUB_WESTEND_DEVELOPMENT, "Westend BridgeHub Development", "westend-dev", ParaId::new(1002), - Some("Bob".to_string()), + ChainType::Development, ))), BridgeHubRuntimeType::Rococo => Ok(Box::new(GenericChainSpec::from_json_bytes( &include_bytes!("../../chain-specs/bridge-hub-rococo.json")[..], @@ -98,16 +95,16 @@ impl BridgeHubRuntimeType { "Rococo BridgeHub Local", "rococo-local", ParaId::new(1013), - Some("Bob".to_string()), |_| (), + ChainType::Local, ))), BridgeHubRuntimeType::RococoDevelopment => Ok(Box::new(rococo::local_config( rococo::BRIDGE_HUB_ROCOCO_DEVELOPMENT, "Rococo BridgeHub Development", "rococo-dev", ParaId::new(1013), - Some("Bob".to_string()), |_| (), + ChainType::Development, ))), other => Err(std::format!("No default config present for {:?}", other)), } @@ -129,27 +126,20 @@ fn ensure_id(id: &str) -> Result<&str, String> { /// Sub-module for Rococo setup pub mod rococo { - use super::{get_account_id_from_seed, get_collator_keys_from_seed, sr25519, ParaId}; - use crate::chain_spec::SAFE_XCM_VERSION; - use parachains_common::{AccountId, AuraId}; - use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; - use sc_chain_spec::ChainType; - - use super::BridgeHubBalance; + use super::{ChainType, ParaId}; + use polkadot_omni_node_lib::chain_spec::{Extensions, GenericChainSpec}; pub(crate) const BRIDGE_HUB_ROCOCO: &str = "bridge-hub-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 = - bridge_hub_rococo_runtime::ExistentialDeposit::get(); pub fn local_config( id: &str, chain_name: &str, relay_chain: &str, para_id: ParaId, - bridges_pallet_owner_seed: Option, modify_props: ModifyProperties, + chain_type: ChainType, ) -> GenericChainSpec { // Rococo defaults let mut properties = sc_chain_spec::Properties::new(); @@ -165,86 +155,15 @@ pub mod rococo { ) .with_name(chain_name) .with_id(super::ensure_id(id).expect("invalid id")) - .with_chain_type(ChainType::Local) - .with_genesis_config_patch(genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed::("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - para_id, - bridges_pallet_owner_seed - .as_ref() - .map(|seed| get_account_id_from_seed::(seed)), - )) + .with_chain_type(chain_type.clone()) + .with_genesis_config_preset_name(match chain_type { + ChainType::Development => sp_genesis_builder::DEV_RUNTIME_PRESET, + ChainType::Local => sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET, + _ => panic!("chain_type: {chain_type:?} not supported here!"), + }) .with_properties(properties) .build() } - - fn genesis( - invulnerables: Vec<(AccountId, AuraId)>, - endowed_accounts: Vec, - id: ParaId, - bridges_pallet_owner: Option, - ) -> serde_json::Value { - serde_json::json!({ - "balances": { - "balances": endowed_accounts.iter().cloned().map(|k| (k, 1u64 << 60)).collect::>(), - }, - "parachainInfo": { - "parachainId": id, - }, - "collatorSelection": { - "invulnerables": invulnerables.iter().cloned().map(|(acc, _)| acc).collect::>(), - "candidacyBond": BRIDGE_HUB_ROCOCO_ED * 16, - }, - "session": { - "keys": invulnerables - .into_iter() - .map(|(acc, aura)| { - ( - acc.clone(), // account id - acc, // validator id - bridge_hub_rococo_runtime::SessionKeys { aura }, // session keys - ) - }) - .collect::>(), - }, - "polkadotXcm": { - "safeXcmVersion": Some(SAFE_XCM_VERSION), - }, - "bridgeWestendGrandpa": { - "owner": bridges_pallet_owner.clone(), - }, - "bridgeWestendMessages": { - "owner": bridges_pallet_owner.clone(), - }, - "ethereumSystem": { - "paraId": id, - "assetHubParaId": 1000 - } - }) - } } /// Sub-module for Kusama setup @@ -255,26 +174,19 @@ pub mod kusama { /// Sub-module for Westend setup. pub mod westend { - use super::{get_account_id_from_seed, get_collator_keys_from_seed, sr25519, ParaId}; - use crate::chain_spec::SAFE_XCM_VERSION; - use parachains_common::{AccountId, AuraId}; - use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; - use sc_chain_spec::ChainType; - - use super::BridgeHubBalance; + use super::{ChainType, ParaId}; + use polkadot_omni_node_lib::chain_spec::{Extensions, GenericChainSpec}; pub(crate) const BRIDGE_HUB_WESTEND: &str = "bridge-hub-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 = - bridge_hub_westend_runtime::ExistentialDeposit::get(); pub fn local_config( id: &str, chain_name: &str, relay_chain: &str, para_id: ParaId, - bridges_pallet_owner_seed: Option, + chain_type: ChainType, ) -> GenericChainSpec { let mut properties = sc_chain_spec::Properties::new(); properties.insert("tokenSymbol".into(), "WND".into()); @@ -287,86 +199,15 @@ pub mod westend { ) .with_name(chain_name) .with_id(super::ensure_id(id).expect("invalid id")) - .with_chain_type(ChainType::Local) - .with_genesis_config_patch(genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed::("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - para_id, - bridges_pallet_owner_seed - .as_ref() - .map(|seed| get_account_id_from_seed::(seed)), - )) + .with_chain_type(chain_type.clone()) + .with_genesis_config_preset_name(match chain_type { + ChainType::Development => sp_genesis_builder::DEV_RUNTIME_PRESET, + ChainType::Local => sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET, + _ => panic!("chain_type: {chain_type:?} not supported here!"), + }) .with_properties(properties) .build() } - - fn genesis( - invulnerables: Vec<(AccountId, AuraId)>, - endowed_accounts: Vec, - id: ParaId, - bridges_pallet_owner: Option, - ) -> serde_json::Value { - serde_json::json!({ - "balances": { - "balances": endowed_accounts.iter().cloned().map(|k| (k, 1u64 << 60)).collect::>(), - }, - "parachainInfo": { - "parachainId": id, - }, - "collatorSelection": { - "invulnerables": invulnerables.iter().cloned().map(|(acc, _)| acc).collect::>(), - "candidacyBond": BRIDGE_HUB_WESTEND_ED * 16, - }, - "session": { - "keys": invulnerables - .into_iter() - .map(|(acc, aura)| { - ( - acc.clone(), // account id - acc, // validator id - bridge_hub_westend_runtime::SessionKeys { aura }, // session keys - ) - }) - .collect::>(), - }, - "polkadotXcm": { - "safeXcmVersion": Some(SAFE_XCM_VERSION), - }, - "bridgeRococoGrandpa": { - "owner": bridges_pallet_owner.clone(), - }, - "bridgeRococoMessages": { - "owner": bridges_pallet_owner.clone(), - }, - "ethereumSystem": { - "paraId": id, - "assetHubParaId": 1000 - } - }) - } } /// Sub-module for Polkadot setup diff --git a/cumulus/polkadot-parachain/src/chain_spec/collectives.rs b/cumulus/polkadot-parachain/src/chain_spec/collectives.rs index 865a2a917086ea212aeb8faa4c8fc11ee95457d3..0d2f66b5acc00c14ba660bd8417c264aaddef6f1 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/collectives.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/collectives.rs @@ -14,23 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . -use crate::chain_spec::{get_account_id_from_seed, get_collator_keys_from_seed, SAFE_XCM_VERSION}; -use cumulus_primitives_core::ParaId; -use parachains_common::{AccountId, AuraId, Balance as CollectivesBalance}; -use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; +use polkadot_omni_node_lib::chain_spec::{Extensions, GenericChainSpec}; use sc_service::ChainType; -use sp_core::sr25519; - -const COLLECTIVES_WESTEND_ED: CollectivesBalance = - collectives_westend_runtime::ExistentialDeposit::get(); - -/// Generate the session keys from individual elements. -/// -/// The input must be a tuple of individual keys (a single arg for now since we have just one key). -pub fn collectives_westend_session_keys(keys: AuraId) -> collectives_westend_runtime::SessionKeys { - collectives_westend_runtime::SessionKeys { aura: keys } -} +/// Collectives Westend Development Config. pub fn collectives_westend_development_config() -> GenericChainSpec { let mut properties = sc_chain_spec::Properties::new(); properties.insert("ss58Format".into(), 42.into()); @@ -40,27 +27,12 @@ pub fn collectives_westend_development_config() -> GenericChainSpec { GenericChainSpec::builder( collectives_westend_runtime::WASM_BINARY .expect("WASM binary was not built, please build it!"), - Extensions { relay_chain: "westend-dev".into(), para_id: 1002 }, + Extensions { relay_chain: "westend-dev".into(), para_id: 1001 }, ) .with_name("Westend Collectives Development") .with_id("collectives_westend_dev") - .with_chain_type(ChainType::Local) - .with_genesis_config_patch(collectives_westend_genesis( - // initial collators. - vec![( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - )], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - ], - // 1002 avoids a potential collision with Kusama-1001 (Encointer) should there ever - // be a collective para on Kusama. - 1002.into(), - )) + .with_chain_type(ChainType::Development) + .with_genesis_config_preset_name(sp_genesis_builder::DEV_RUNTIME_PRESET) .with_boot_nodes(Vec::new()) .with_properties(properties) .build() @@ -76,80 +48,13 @@ pub fn collectives_westend_local_config() -> GenericChainSpec { GenericChainSpec::builder( collectives_westend_runtime::WASM_BINARY .expect("WASM binary was not built, please build it!"), - Extensions { relay_chain: "westend-local".into(), para_id: 1002 }, + Extensions { relay_chain: "westend-local".into(), para_id: 1001 }, ) .with_name("Westend Collectives Local") .with_id("collectives_westend_local") .with_chain_type(ChainType::Local) - .with_genesis_config_patch(collectives_westend_genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed::("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed::("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - 1002.into(), - )) + .with_genesis_config_preset_name(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET) .with_boot_nodes(Vec::new()) .with_properties(properties) .build() } - -fn collectives_westend_genesis( - invulnerables: Vec<(AccountId, AuraId)>, - endowed_accounts: Vec, - id: ParaId, -) -> serde_json::Value { - serde_json::json!( { - "balances": { - "balances": endowed_accounts - .iter() - .cloned() - .map(|k| (k, COLLECTIVES_WESTEND_ED * 4096)) - .collect::>(), - }, - "parachainInfo": { - "parachainId": id, - }, - "collatorSelection": { - "invulnerables": invulnerables.iter().cloned().map(|(acc, _)| acc).collect::>(), - "candidacyBond": COLLECTIVES_WESTEND_ED * 16, - }, - "session": { - "keys": invulnerables - .into_iter() - .map(|(acc, aura)| { - ( - acc.clone(), // account id - acc, // validator id - collectives_westend_session_keys(aura), // session keys - ) - }) - .collect::>(), - }, - // no need to pass anything to aura, in fact it will panic if we do. Session will take care - // of this. - "polkadotXcm": { - "safeXcmVersion": Some(SAFE_XCM_VERSION), - }, - }) -} diff --git a/cumulus/polkadot-parachain/src/chain_spec/coretime.rs b/cumulus/polkadot-parachain/src/chain_spec/coretime.rs index fec3f56e6d3520ddcee8985f6be388117df5756e..e42e95ad16f8bb77bd08969058b90adda8692f33 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/coretime.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/coretime.rs @@ -15,7 +15,7 @@ // along with Cumulus. If not, see . use cumulus_primitives_core::ParaId; -use polkadot_parachain_lib::chain_spec::GenericChainSpec; +use polkadot_omni_node_lib::chain_spec::GenericChainSpec; use sc_chain_spec::{ChainSpec, ChainType}; use std::{borrow::Cow, str::FromStr}; @@ -150,7 +150,7 @@ pub mod rococo { get_account_id_from_seed, get_collator_keys_from_seed, SAFE_XCM_VERSION, }; use parachains_common::{AccountId, AuraId, Balance}; - use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; + use polkadot_omni_node_lib::chain_spec::{Extensions, GenericChainSpec}; use sc_chain_spec::ChainType; use sp_core::sr25519; @@ -248,7 +248,7 @@ pub mod westend { get_account_id_from_seed, get_collator_keys_from_seed, SAFE_XCM_VERSION, }; use parachains_common::{AccountId, AuraId, Balance}; - use polkadot_parachain_lib::chain_spec::Extensions; + use polkadot_omni_node_lib::chain_spec::Extensions; use sp_core::sr25519; pub(crate) const CORETIME_WESTEND: &str = "coretime-westend"; diff --git a/cumulus/polkadot-parachain/src/chain_spec/glutton.rs b/cumulus/polkadot-parachain/src/chain_spec/glutton.rs index 136411b93e8bcc73a3bbf72510abb1f13ae4d20b..e70f86f725a19c556d5e4c13fceec1c980c26dc8 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/glutton.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/glutton.rs @@ -17,7 +17,7 @@ use crate::chain_spec::get_account_id_from_seed; use cumulus_primitives_core::ParaId; use parachains_common::AuraId; -use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; +use polkadot_omni_node_lib::chain_spec::{Extensions, GenericChainSpec}; use sc_service::ChainType; use sp_core::sr25519; diff --git a/cumulus/polkadot-parachain/src/chain_spec/mod.rs b/cumulus/polkadot-parachain/src/chain_spec/mod.rs index 82aec951704f1c3e7dac7cf6b0636e8b72e758bb..a820fdbd13e86e7002d758436f7a71366be03449 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/mod.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/mod.rs @@ -15,16 +15,16 @@ // along with Cumulus. If not, see . use cumulus_primitives_core::ParaId; -use parachains_common::{AccountId, Signature}; -use polkadot_parachain_lib::{ +pub(crate) use parachains_common::genesis_config_helpers::{ + get_account_id_from_seed, get_collator_keys_from_seed, get_from_seed, +}; +use polkadot_omni_node_lib::{ chain_spec::{GenericChainSpec, LoadSpec}, runtime::{ AuraConsensusId, BlockNumber, Consensus, Runtime, RuntimeResolver as RuntimeResolverT, }, }; use sc_chain_spec::ChainSpec; -use sp_core::{Pair, Public}; -use sp_runtime::traits::{IdentifyAccount, Verify}; pub mod asset_hubs; pub mod bridge_hubs; @@ -34,36 +34,10 @@ pub mod glutton; pub mod penpal; pub mod people; pub mod rococo_parachain; -pub mod seedling; -pub mod shell; /// The default XCM version to set in genesis config. const SAFE_XCM_VERSION: u32 = xcm::prelude::XCM_VERSION; -/// Helper function to generate a crypto pair from seed -pub fn get_from_seed(seed: &str) -> ::Public { - TPublic::Pair::from_string(&format!("//{}", seed), None) - .expect("static values are valid; qed") - .public() -} - -type AccountPublic = ::Signer; - -/// Helper function to generate an account ID from seed -pub fn get_account_id_from_seed(seed: &str) -> AccountId -where - AccountPublic: From<::Public>, -{ - AccountPublic::from(get_from_seed::(seed)).into_account() -} - -/// Generate collator keys from seed. -/// -/// This function's return type must always match the session keys of the chain in tuple format. -pub fn get_collator_keys_from_seed(seed: &str) -> ::Public { - get_from_seed::(seed) -} - /// Extracts the normalized chain id and parachain id from the input chain id. /// (H/T to Phala for the idea) /// E.g. "penpal-kusama-2004" yields ("penpal-kusama", Some(2004)) @@ -99,10 +73,6 @@ impl LoadSpec for ChainSpecLoader { &include_bytes!("../../chain-specs/track.json")[..], )?), - // -- Starters - "shell" => Box::new(shell::get_shell_chain_spec()), - "seedling" => Box::new(seedling::get_seedling_chain_spec()), - // -- Asset Hub Polkadot "asset-hub-polkadot" | "statemint" => Box::new(GenericChainSpec::from_json_bytes( &include_bytes!("../../chain-specs/asset-hub-polkadot.json")[..], @@ -226,8 +196,6 @@ impl LoadSpec for ChainSpecLoader { #[derive(Debug, PartialEq)] enum LegacyRuntime { Omni, - Shell, - Seedling, AssetHubPolkadot, AssetHub, Penpal, @@ -242,11 +210,7 @@ impl LegacyRuntime { fn from_id(id: &str) -> LegacyRuntime { let id = id.replace('_', "-"); - if id.starts_with("shell") { - LegacyRuntime::Shell - } else if id.starts_with("seedling") { - LegacyRuntime::Seedling - } else if id.starts_with("asset-hub-polkadot") | id.starts_with("statemint") { + if id.starts_with("asset-hub-polkadot") | id.starts_with("statemint") { LegacyRuntime::AssetHubPolkadot } else if id.starts_with("asset-hub-kusama") | id.starts_with("statemine") | @@ -301,7 +265,6 @@ impl RuntimeResolverT for RuntimeResolver { LegacyRuntime::Penpal | LegacyRuntime::Omni => Runtime::Omni(BlockNumber::U32, Consensus::Aura(AuraConsensusId::Sr25519)), - LegacyRuntime::Shell | LegacyRuntime::Seedling => Runtime::Shell, }) } } @@ -360,15 +323,6 @@ mod tests { #[test] fn test_legacy_runtime_for_different_chain_specs() { - let chain_spec = create_default_with_extensions("shell-1", Extensions1::default()); - assert_eq!(LegacyRuntime::Shell, LegacyRuntime::from_id(chain_spec.id())); - - let chain_spec = create_default_with_extensions("shell-2", Extensions2::default()); - assert_eq!(LegacyRuntime::Shell, LegacyRuntime::from_id(chain_spec.id())); - - let chain_spec = create_default_with_extensions("seedling", Extensions2::default()); - assert_eq!(LegacyRuntime::Seedling, LegacyRuntime::from_id(chain_spec.id())); - let chain_spec = create_default_with_extensions("penpal-rococo-1000", Extensions2::default()); assert_eq!(LegacyRuntime::Penpal, LegacyRuntime::from_id(chain_spec.id())); diff --git a/cumulus/polkadot-parachain/src/chain_spec/penpal.rs b/cumulus/polkadot-parachain/src/chain_spec/penpal.rs index 5645bf06b67b22ae9f799fb1796131a4ebca0950..006c5c9b53c254b67c17926770adff3462010314 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/penpal.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/penpal.rs @@ -17,7 +17,7 @@ use crate::chain_spec::{get_account_id_from_seed, get_collator_keys_from_seed, SAFE_XCM_VERSION}; use cumulus_primitives_core::ParaId; use parachains_common::{AccountId, AuraId}; -use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; +use polkadot_omni_node_lib::chain_spec::{Extensions, GenericChainSpec}; use sc_service::ChainType; use sp_core::sr25519; diff --git a/cumulus/polkadot-parachain/src/chain_spec/people.rs b/cumulus/polkadot-parachain/src/chain_spec/people.rs index 3c1150d95422b7016c86dfcf3f4c48ac8f60f0b1..b89f1c3a5fe675b561f08fb908ff5e7c07dac84a 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/people.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/people.rs @@ -16,7 +16,7 @@ use cumulus_primitives_core::ParaId; use parachains_common::Balance as PeopleBalance; -use polkadot_parachain_lib::chain_spec::GenericChainSpec; +use polkadot_omni_node_lib::chain_spec::GenericChainSpec; use sc_chain_spec::ChainSpec; use std::str::FromStr; @@ -124,7 +124,7 @@ pub mod rococo { get_account_id_from_seed, get_collator_keys_from_seed, SAFE_XCM_VERSION, }; use parachains_common::{AccountId, AuraId}; - use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; + use polkadot_omni_node_lib::chain_spec::{Extensions, GenericChainSpec}; use sc_chain_spec::ChainType; use sp_core::sr25519; @@ -234,7 +234,7 @@ pub mod westend { get_account_id_from_seed, get_collator_keys_from_seed, SAFE_XCM_VERSION, }; use parachains_common::{AccountId, AuraId}; - use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; + use polkadot_omni_node_lib::chain_spec::{Extensions, GenericChainSpec}; use sc_chain_spec::ChainType; use sp_core::sr25519; diff --git a/cumulus/polkadot-parachain/src/chain_spec/rococo_parachain.rs b/cumulus/polkadot-parachain/src/chain_spec/rococo_parachain.rs index 9f4a162e67f8d74f1df7a59dc3c1f1b10e10e9a1..39762f590cfe18bbe05a3ebbc49bd1c18476c337 100644 --- a/cumulus/polkadot-parachain/src/chain_spec/rococo_parachain.rs +++ b/cumulus/polkadot-parachain/src/chain_spec/rococo_parachain.rs @@ -19,9 +19,8 @@ use crate::chain_spec::{get_from_seed, SAFE_XCM_VERSION}; use cumulus_primitives_core::ParaId; use hex_literal::hex; -use parachains_common::AccountId; -use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; -use polkadot_service::chain_spec::get_account_id_from_seed; +use parachains_common::{genesis_config_helpers::get_account_id_from_seed, AccountId}; +use polkadot_omni_node_lib::chain_spec::{Extensions, GenericChainSpec}; use rococo_parachain_runtime::AuraId; use sc_chain_spec::ChainType; use sp_core::{crypto::UncheckedInto, sr25519}; diff --git a/cumulus/polkadot-parachain/src/chain_spec/seedling.rs b/cumulus/polkadot-parachain/src/chain_spec/seedling.rs deleted file mode 100644 index a104b58db5d2f03ac5c67ed39ab7246b48df9f7f..0000000000000000000000000000000000000000 --- a/cumulus/polkadot-parachain/src/chain_spec/seedling.rs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Cumulus. - -// Cumulus is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Cumulus is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Cumulus. If not, see . - -use crate::chain_spec::get_account_id_from_seed; -use cumulus_primitives_core::ParaId; -use parachains_common::{AccountId, AuraId}; -use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; -use sc_service::ChainType; -use sp_core::sr25519; - -use super::get_collator_keys_from_seed; - -pub fn get_seedling_chain_spec() -> GenericChainSpec { - GenericChainSpec::builder( - seedling_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), - Extensions { relay_chain: "westend".into(), para_id: 2000 }, - ) - .with_name("Seedling Local Testnet") - .with_id("seedling_local_testnet") - .with_chain_type(ChainType::Local) - .with_genesis_config_patch(seedling_testnet_genesis( - get_account_id_from_seed::("Alice"), - 2000.into(), - vec![get_collator_keys_from_seed::("Alice")], - )) - .with_boot_nodes(Vec::new()) - .build() -} - -fn seedling_testnet_genesis( - root_key: AccountId, - parachain_id: ParaId, - collators: Vec, -) -> serde_json::Value { - serde_json::json!({ - "sudo": { "key": Some(root_key) }, - "parachainInfo": { - "parachainId": parachain_id, - }, - "aura": { "authorities": collators }, - }) -} diff --git a/cumulus/polkadot-parachain/src/chain_spec/shell.rs b/cumulus/polkadot-parachain/src/chain_spec/shell.rs deleted file mode 100644 index 0a7816ab31932998e17e1d0ce4e04c5c6659e877..0000000000000000000000000000000000000000 --- a/cumulus/polkadot-parachain/src/chain_spec/shell.rs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Cumulus. - -// Cumulus is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Cumulus is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Cumulus. If not, see . - -use cumulus_primitives_core::ParaId; -use parachains_common::AuraId; -use polkadot_parachain_lib::chain_spec::{Extensions, GenericChainSpec}; -use sc_service::ChainType; - -use super::get_collator_keys_from_seed; - -pub fn get_shell_chain_spec() -> GenericChainSpec { - GenericChainSpec::builder( - shell_runtime::WASM_BINARY.expect("WASM binary was not built, please build it!"), - Extensions { relay_chain: "westend".into(), para_id: 1000 }, - ) - .with_name("Shell Local Testnet") - .with_id("shell_local_testnet") - .with_chain_type(ChainType::Local) - .with_genesis_config_patch(shell_testnet_genesis( - 1000.into(), - vec![get_collator_keys_from_seed::("Alice")], - )) - .with_boot_nodes(Vec::new()) - .build() -} - -fn shell_testnet_genesis(parachain_id: ParaId, collators: Vec) -> serde_json::Value { - serde_json::json!({ - "parachainInfo": { "parachainId": parachain_id}, - "aura": { "authorities": collators }, - }) -} diff --git a/cumulus/polkadot-parachain/src/main.rs b/cumulus/polkadot-parachain/src/main.rs index f2dce552c51a1b5ed95e31f516f25cf978adc67e..c8464be937ccbd198893d7b27f5360904e444a47 100644 --- a/cumulus/polkadot-parachain/src/main.rs +++ b/cumulus/polkadot-parachain/src/main.rs @@ -21,7 +21,7 @@ mod chain_spec; -use polkadot_parachain_lib::{run, CliConfig as CliConfigT, RunConfig}; +use polkadot_omni_node_lib::{run, CliConfig as CliConfigT, RunConfig}; struct CliConfig; diff --git a/cumulus/primitives/aura/src/lib.rs b/cumulus/primitives/aura/src/lib.rs index 826b65fddd2c695f0c0a1c63d77e9eab68fe0f30..aeeee5f8bafa16e02577f164dfe69e106c49c702 100644 --- a/cumulus/primitives/aura/src/lib.rs +++ b/cumulus/primitives/aura/src/lib.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Cumulus. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/cumulus/primitives/core/src/lib.rs b/cumulus/primitives/core/src/lib.rs index 6eafecfc3ff574689b5dd3125b97c0e5bade844a..dfb574ef33018fd138a767e5f2671d38afc32729 100644 --- a/cumulus/primitives/core/src/lib.rs +++ b/cumulus/primitives/core/src/lib.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Cumulus. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. @@ -32,6 +32,7 @@ pub use polkadot_parachain_primitives::primitives::{ XcmpMessageHandler, }; pub use polkadot_primitives::{ + vstaging::{ClaimQueueOffset, CoreSelector}, AbridgedHostConfiguration, AbridgedHrmpChannel, PersistedValidationData, }; @@ -332,6 +333,10 @@ pub mod rpsr_digest { } } +/// The default claim queue offset to be used if it's not configured/accessible in the parachain +/// runtime +pub const DEFAULT_CLAIM_QUEUE_OFFSET: u8 = 0; + /// Information about a collation. /// /// This was used in version 1 of the [`CollectCollationInfo`] runtime api. @@ -395,4 +400,10 @@ sp_api::decl_runtime_apis! { /// we are collecting the collation info for. fn collect_collation_info(header: &Block::Header) -> CollationInfo; } + + /// Runtime api used to select the core for which the next block will be built. + pub trait GetCoreSelectorApi { + /// Retrieve core selector and claim queue offset for the next block. + fn core_selector() -> (CoreSelector, ClaimQueueOffset); + } } diff --git a/cumulus/primitives/parachain-inherent/src/lib.rs b/cumulus/primitives/parachain-inherent/src/lib.rs index ad4b39b547c5f24f123e8abfd5a352dd1008adae..127a03b6525916069421a8bb18dd6569fd41aecd 100644 --- a/cumulus/primitives/parachain-inherent/src/lib.rs +++ b/cumulus/primitives/parachain-inherent/src/lib.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Cumulus. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/cumulus/primitives/proof-size-hostfunction/src/lib.rs b/cumulus/primitives/proof-size-hostfunction/src/lib.rs index 8ebc58ea450d4aea023f2a3af218bbd9eb3546e9..f17b3d3f33b473b70347d5f7667b142a8df97b66 100644 --- a/cumulus/primitives/proof-size-hostfunction/src/lib.rs +++ b/cumulus/primitives/proof-size-hostfunction/src/lib.rs @@ -6,7 +6,7 @@ // 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, +// 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. diff --git a/cumulus/primitives/storage-weight-reclaim/src/lib.rs b/cumulus/primitives/storage-weight-reclaim/src/lib.rs index a557e881e26b5f012363dc4b893566b45b8ae58c..2529297691e8bcc15e31d070e20975c1eeaa1282 100644 --- a/cumulus/primitives/storage-weight-reclaim/src/lib.rs +++ b/cumulus/primitives/storage-weight-reclaim/src/lib.rs @@ -183,13 +183,15 @@ where if consumed_weight > benchmarked_weight { log::error!( target: LOG_TARGET, - "Benchmarked storage weight smaller than consumed storage weight. benchmarked: {benchmarked_weight} consumed: {consumed_weight} unspent: {unspent}" + "Benchmarked storage weight smaller than consumed storage weight. extrinsic: {} benchmarked: {benchmarked_weight} consumed: {consumed_weight} unspent: {unspent}", + frame_system::Pallet::::extrinsic_index().unwrap_or(0) ); current.accrue(Weight::from_parts(0, storage_size_diff), info.class) } else { log::trace!( target: LOG_TARGET, - "Reclaiming storage weight. benchmarked: {benchmarked_weight}, consumed: {consumed_weight} unspent: {unspent}" + "Reclaiming storage weight. extrinsic: {} benchmarked: {benchmarked_weight} consumed: {consumed_weight} unspent: {unspent}", + frame_system::Pallet::::extrinsic_index().unwrap_or(0) ); current.reduce(Weight::from_parts(0, storage_size_diff), info.class) } diff --git a/cumulus/primitives/utility/src/lib.rs b/cumulus/primitives/utility/src/lib.rs index 3ebcb44fa439480762f4a667b3aac2ad35ef5b60..8530f5b87487e0096c7696e3ac9a49a984fed851 100644 --- a/cumulus/primitives/utility/src/lib.rs +++ b/cumulus/primitives/utility/src/lib.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Cumulus. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. @@ -99,6 +99,10 @@ where impl InspectMessageQueues for ParentAsUmp { + fn clear_messages() { + T::clear_messages(); + } + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { T::get_messages() } @@ -381,7 +385,8 @@ impl< FungiblesAssetMatcher, OnUnbalanced, AccountId, - > where + > +where Fungibles::Balance: Into, { fn new() -> Self { @@ -541,7 +546,8 @@ impl< FungiblesAssetMatcher, OnUnbalanced, AccountId, - > where + > +where Fungibles::Balance: Into, { fn drop(&mut self) { diff --git a/cumulus/primitives/utility/src/tests/mod.rs b/cumulus/primitives/utility/src/tests/mod.rs index e0ad8718b89ed38f686f3c854adf9cd185aeb272..80e72ef2826364fdcd29ffb408f75c69d14c8ddb 100644 --- a/cumulus/primitives/utility/src/tests/mod.rs +++ b/cumulus/primitives/utility/src/tests/mod.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Cumulus. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/cumulus/primitives/utility/src/tests/swap_first.rs b/cumulus/primitives/utility/src/tests/swap_first.rs index 2e19db498816bc02c65f278a7f874bc9671051e0..69239c552b8ca28be368f6ee3369bd29faf029c6 100644 --- a/cumulus/primitives/utility/src/tests/swap_first.rs +++ b/cumulus/primitives/utility/src/tests/swap_first.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Cumulus. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/cumulus/test/runtime/build.rs b/cumulus/test/runtime/build.rs index bf579f4121e5f6b48fa5ba310c76f7cdd02fa7a0..7a7fe8ffaa82e6fe2ab861dddaea12c44442fd06 100644 --- a/cumulus/test/runtime/build.rs +++ b/cumulus/test/runtime/build.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Cumulus. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/cumulus/test/runtime/src/lib.rs b/cumulus/test/runtime/src/lib.rs index 274f16ab630d6c0f9cff092b0ed79624e2a27a67..92a6ab73a2ebe3dbc33a970a0ca368373de2a767 100644 --- a/cumulus/test/runtime/src/lib.rs +++ b/cumulus/test/runtime/src/lib.rs @@ -42,6 +42,7 @@ use sp_api::{decl_runtime_apis, impl_runtime_apis}; pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_core::{ConstBool, ConstU32, ConstU64, OpaqueMetadata}; +use cumulus_primitives_core::{ClaimQueueOffset, CoreSelector}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, traits::{BlakeTwo256, Block as BlockT, IdentifyAccount, IdentityLookup, Verify}, @@ -132,7 +133,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 1, + system_version: 1, }; #[cfg(feature = "increment-spec-version")] @@ -146,7 +147,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 1, + system_version: 1, }; pub const EPOCH_DURATION_IN_BLOCKS: u32 = 10 * MINUTES; @@ -268,6 +269,7 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<0>; + type DoneSlashHandler = (); } impl pallet_transaction_payment::Config for Runtime { @@ -311,6 +313,7 @@ impl cumulus_pallet_parachain_system::Config for Runtime { type CheckAssociatedRelayNumber = cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; type ConsensusHook = ConsensusHook; + type SelectCore = cumulus_pallet_parachain_system::DefaultCoreSelector; } impl parachain_info::Config for Runtime {} @@ -528,6 +531,12 @@ impl_runtime_apis! { } } + impl cumulus_primitives_core::GetCoreSelectorApi for Runtime { + fn core_selector() -> (CoreSelector, ClaimQueueOffset) { + ParachainSystem::core_selector() + } + } + impl sp_genesis_builder::GenesisBuilder for Runtime { fn build_state(config: Vec) -> sp_genesis_builder::Result { build_state::(config) diff --git a/cumulus/test/service/Cargo.toml b/cumulus/test/service/Cargo.toml index f766d123632096a4af578fcf87bdb0b299008028..a1b70c5239525a641da43fdf328897a727d26d1f 100644 --- a/cumulus/test/service/Cargo.toml +++ b/cumulus/test/service/Cargo.toml @@ -18,6 +18,7 @@ clap = { features = ["derive"], workspace = true } codec = { workspace = true, default-features = true } criterion = { features = ["async_tokio"], workspace = true, default-features = true } jsonrpsee = { features = ["server"], workspace = true } +prometheus = { workspace = true } rand = { workspace = true, default-features = true } serde = { features = ["derive"], workspace = true, default-features = true } serde_json = { workspace = true, default-features = true } diff --git a/cumulus/test/service/src/cli.rs b/cumulus/test/service/src/cli.rs index 739c2d4bda160e588db43a82082b459db6f3dab9..220b0449f3392727228f99b3d10f2ccbc2165693 100644 --- a/cumulus/test/service/src/cli.rs +++ b/cumulus/test/service/src/cli.rs @@ -139,10 +139,9 @@ impl CliConfiguration for RelayChainCli { _support_url: &String, _impl_version: &String, _logger_hook: F, - _config: &sc_service::Configuration, ) -> CliResult<()> where - F: FnOnce(&mut sc_cli::LoggerBuilder, &sc_service::Configuration), + F: FnOnce(&mut sc_cli::LoggerBuilder), { unreachable!("PolkadotCli is never initialized; qed"); } diff --git a/cumulus/test/service/src/lib.rs b/cumulus/test/service/src/lib.rs index bc0fe9090d38ed533c784b58598f856ed5738e10..9f93572e9cea5f1ad8b3d064cc5e0745c305d9f4 100644 --- a/cumulus/test/service/src/lib.rs +++ b/cumulus/test/service/src/lib.rs @@ -32,6 +32,7 @@ use cumulus_client_consensus_aura::{ ImportQueueParams, }; use cumulus_client_consensus_proposer::Proposer; +use prometheus::Registry; use runtime::AccountId; use sc_executor::{HeapAllocStrategy, WasmExecutor, DEFAULT_HEAP_ALLOC_STRATEGY}; use sp_consensus_aura::sr25519::AuthorityPair; @@ -78,8 +79,9 @@ use sc_network::{ }; use sc_service::{ config::{ - BlocksPruning, DatabaseSource, KeystoreConfig, MultiaddrWithPeerId, NetworkConfiguration, - OffchainWorkerConfig, PruningMode, RpcBatchRequestConfig, RpcEndpoint, WasmExecutionMethod, + BlocksPruning, DatabaseSource, ExecutorConfiguration, KeystoreConfig, MultiaddrWithPeerId, + NetworkConfiguration, OffchainWorkerConfig, PruningMode, RpcBatchRequestConfig, + RpcConfiguration, RpcEndpoint, WasmExecutionMethod, }, BasePath, ChainSpec as ChainSpecService, Configuration, Error as ServiceError, PartialComponents, Role, RpcHandlers, TFullBackend, TFullClient, TaskManager, @@ -194,15 +196,16 @@ pub fn new_partial( enable_import_proof_record: bool, ) -> Result { let heap_pages = config + .executor .default_heap_pages .map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |h| HeapAllocStrategy::Static { extra_pages: h as _ }); let executor = WasmExecutor::builder() - .with_execution_method(config.wasm_method) + .with_execution_method(config.executor.wasm_method) .with_onchain_heap_alloc_strategy(heap_pages) .with_offchain_heap_alloc_strategy(heap_pages) - .with_max_runtime_instances(config.max_runtime_instances) - .with_runtime_cache_size(config.runtime_cache_size) + .with_max_runtime_instances(config.executor.max_runtime_instances) + .with_runtime_cache_size(config.executor.runtime_cache_size) .build(); let (client, backend, keystore_container, task_manager) = @@ -262,11 +265,12 @@ pub fn new_partial( async fn build_relay_chain_interface( relay_chain_config: Configuration, + parachain_prometheus_registry: Option<&Registry>, collator_key: Option, collator_options: CollatorOptions, task_manager: &mut TaskManager, ) -> RelayChainResult> { - let relay_chain_full_node = match collator_options.relay_chain_mode { + let relay_chain_node = match collator_options.relay_chain_mode { cumulus_client_cli::RelayChainMode::Embedded => polkadot_test_service::new_full( relay_chain_config, if let Some(ref key) = collator_key { @@ -281,6 +285,7 @@ async fn build_relay_chain_interface( cumulus_client_cli::RelayChainMode::ExternalRpc(rpc_target_urls) => return build_minimal_relay_chain_node_with_rpc( relay_chain_config, + parachain_prometheus_registry, task_manager, rpc_target_urls, ) @@ -292,13 +297,13 @@ async fn build_relay_chain_interface( .map(|r| r.0), }; - task_manager.add_child(relay_chain_full_node.task_manager); + task_manager.add_child(relay_chain_node.task_manager); tracing::info!("Using inprocess node."); Ok(Arc::new(RelayChainInProcessInterface::new( - relay_chain_full_node.client.clone(), - relay_chain_full_node.backend.clone(), - relay_chain_full_node.sync_service.clone(), - relay_chain_full_node.overseer_handle.ok_or(RelayChainError::GenericError( + relay_chain_node.client.clone(), + relay_chain_node.backend.clone(), + relay_chain_node.sync_service.clone(), + relay_chain_node.overseer_handle.ok_or(RelayChainError::GenericError( "Overseer should be running in full node.".to_string(), ))?, ))) @@ -342,9 +347,9 @@ where let backend = params.backend.clone(); let block_import = params.other; - let relay_chain_interface = build_relay_chain_interface( relay_chain_config, + parachain_config.prometheus_registry(), collator_key.clone(), collator_options.clone(), &mut task_manager, @@ -484,7 +489,6 @@ where keystore, collator_key, para_id, - relay_chain_slot_duration, proposer, collator_service, authoring_duration: Duration::from_millis(2000), @@ -492,7 +496,7 @@ where slot_drift: Duration::from_secs(1), }; - let (collation_future, block_builer_future) = + let (collation_future, block_builder_future) = slot_based::run::(params); task_manager.spawn_essential_handle().spawn( "collation-task", @@ -502,7 +506,7 @@ where task_manager.spawn_essential_handle().spawn( "block-builder-task", None, - block_builer_future, + block_builder_future, ); } else { tracing::info!(target: LOG_TARGET, "Starting block authoring with lookahead collator."); @@ -863,38 +867,41 @@ pub fn node_config( state_pruning: Some(PruningMode::ArchiveAll), blocks_pruning: BlocksPruning::KeepAll, chain_spec: spec, - wasm_method: WasmExecutionMethod::Compiled { - instantiation_strategy: sc_executor_wasmtime::InstantiationStrategy::PoolingCopyOnWrite, + executor: ExecutorConfiguration { + wasm_method: WasmExecutionMethod::Compiled { + instantiation_strategy: + sc_executor_wasmtime::InstantiationStrategy::PoolingCopyOnWrite, + }, + ..ExecutorConfiguration::default() + }, + rpc: RpcConfiguration { + addr: None, + max_connections: Default::default(), + cors: None, + methods: Default::default(), + max_request_size: Default::default(), + max_response_size: Default::default(), + id_provider: None, + max_subs_per_conn: Default::default(), + port: 9945, + message_buffer_capacity: Default::default(), + batch_config: RpcBatchRequestConfig::Unlimited, + rate_limit: None, + rate_limit_whitelisted_ips: Default::default(), + rate_limit_trust_proxy_headers: Default::default(), }, - rpc_addr: None, - rpc_max_connections: Default::default(), - rpc_cors: None, - rpc_methods: Default::default(), - rpc_max_request_size: Default::default(), - rpc_max_response_size: Default::default(), - rpc_id_provider: None, - rpc_max_subs_per_conn: Default::default(), - rpc_port: 9945, - rpc_message_buffer_capacity: Default::default(), - rpc_batch_config: RpcBatchRequestConfig::Unlimited, - rpc_rate_limit: None, - rpc_rate_limit_whitelisted_ips: Default::default(), - rpc_rate_limit_trust_proxy_headers: Default::default(), prometheus_config: None, telemetry_endpoints: None, - default_heap_pages: None, offchain_worker: OffchainWorkerConfig { enabled: true, indexing_enabled: false }, force_authoring: false, disable_grandpa: false, dev_key_seed: Some(key_seed), tracing_targets: None, tracing_receiver: Default::default(), - max_runtime_instances: 8, announce_block: true, data_path: root, base_path, wasm_runtime_overrides: None, - runtime_cache_size: 2, }) } @@ -1006,19 +1013,19 @@ pub fn run_relay_chain_validator_node( ); if let Some(port) = port { - config.rpc_addr = Some(vec![RpcEndpoint { - batch_config: config.rpc_batch_config, - cors: config.rpc_cors.clone(), + config.rpc.addr = Some(vec![RpcEndpoint { + batch_config: config.rpc.batch_config, + cors: config.rpc.cors.clone(), listen_addr: SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, port)), - max_connections: config.rpc_max_connections, - max_payload_in_mb: config.rpc_max_request_size, - max_payload_out_mb: config.rpc_max_response_size, - max_subscriptions_per_connection: config.rpc_max_subs_per_conn, - max_buffer_capacity_per_connection: config.rpc_message_buffer_capacity, - rpc_methods: config.rpc_methods, - rate_limit: config.rpc_rate_limit, - rate_limit_trust_proxy_headers: config.rpc_rate_limit_trust_proxy_headers, - rate_limit_whitelisted_ips: config.rpc_rate_limit_whitelisted_ips.clone(), + max_connections: config.rpc.max_connections, + max_payload_in_mb: config.rpc.max_request_size, + max_payload_out_mb: config.rpc.max_response_size, + max_subscriptions_per_connection: config.rpc.max_subs_per_conn, + max_buffer_capacity_per_connection: config.rpc.message_buffer_capacity, + rpc_methods: config.rpc.methods, + rate_limit: config.rpc.rate_limit, + rate_limit_trust_proxy_headers: config.rpc.rate_limit_trust_proxy_headers, + rate_limit_whitelisted_ips: config.rpc.rate_limit_whitelisted_ips.clone(), retry_random_port: true, is_optional: false, }]); diff --git a/cumulus/test/service/src/main.rs b/cumulus/test/service/src/main.rs index 9357978b769a4e8ec6c4f850d37290e3dee1b1e5..caa672e611f7cd47d42955fc1a8a33b4643f3097 100644 --- a/cumulus/test/service/src/main.rs +++ b/cumulus/test/service/src/main.rs @@ -61,36 +61,39 @@ fn main() -> Result<(), sc_cli::Error> { let collator_options = cli.run.collator_options(); let tokio_runtime = sc_cli::build_runtime()?; let tokio_handle = tokio_runtime.handle(); - let config = cli + let parachain_config = cli .run .normalize() .create_configuration(&cli, tokio_handle.clone()) .expect("Should be able to generate config"); - let polkadot_cli = RelayChainCli::new( - &config, + let relay_chain_cli = RelayChainCli::new( + ¶chain_config, [RelayChainCli::executable_name()].iter().chain(cli.relaychain_args.iter()), ); - - let tokio_handle = config.tokio_handle.clone(); - let polkadot_config = - SubstrateCli::create_configuration(&polkadot_cli, &polkadot_cli, tokio_handle) - .map_err(|err| format!("Relay chain argument error: {}", err))?; - - let parachain_id = chain_spec::Extensions::try_get(&*config.chain_spec) + let tokio_handle = parachain_config.tokio_handle.clone(); + let relay_chain_config = SubstrateCli::create_configuration( + &relay_chain_cli, + &relay_chain_cli, + tokio_handle, + ) + .map_err(|err| format!("Relay chain argument error: {}", err))?; + + let parachain_id = chain_spec::Extensions::try_get(&*parachain_config.chain_spec) .map(|e| e.para_id) .ok_or("Could not find parachain extension in chain-spec.")?; tracing::info!("Parachain id: {:?}", parachain_id); tracing::info!( "Is collating: {}", - if config.role.is_authority() { "yes" } else { "no" } + if parachain_config.role.is_authority() { "yes" } else { "no" } ); if cli.fail_pov_recovery { tracing::info!("PoV recovery failure enabled"); } - let collator_key = config.role.is_authority().then(|| CollatorPair::generate().0); + let collator_key = + parachain_config.role.is_authority().then(|| CollatorPair::generate().0); let consensus = cli .use_null_consensus @@ -102,15 +105,15 @@ fn main() -> Result<(), sc_cli::Error> { let (mut task_manager, _, _, _, _, _) = tokio_runtime .block_on(async move { - match polkadot_config.network.network_backend { + match relay_chain_config.network.network_backend { sc_network::config::NetworkBackendType::Libp2p => cumulus_test_service::start_node_impl::< _, sc_network::NetworkWorker<_, _>, >( - config, + parachain_config, collator_key, - polkadot_config, + relay_chain_config, parachain_id.into(), cli.disable_block_announcements.then(wrap_announce_block), cli.fail_pov_recovery, @@ -126,9 +129,9 @@ fn main() -> Result<(), sc_cli::Error> { _, sc_network::Litep2pNetworkBackend, >( - config, + parachain_config, collator_key, - polkadot_config, + relay_chain_config, parachain_id.into(), cli.disable_block_announcements.then(wrap_announce_block), cli.fail_pov_recovery, diff --git a/cumulus/xcm/xcm-emulator/Cargo.toml b/cumulus/xcm/xcm-emulator/Cargo.toml index ba1097fba075696e9d40dffc3c4753d9956615b1..8598481fae7670a2b57e4e52b418753501827717 100644 --- a/cumulus/xcm/xcm-emulator/Cargo.toml +++ b/cumulus/xcm/xcm-emulator/Cargo.toml @@ -13,8 +13,8 @@ workspace = true codec = { workspace = true, default-features = true } paste = { workspace = true, default-features = true } log = { workspace = true } -lazy_static = { workspace = true } impl-trait-for-tuples = { workspace = true } +array-bytes = { workspace = true } # Substrate frame-support = { workspace = true, default-features = true } diff --git a/cumulus/xcm/xcm-emulator/src/lib.rs b/cumulus/xcm/xcm-emulator/src/lib.rs index 8de3660c223627155a27f5ecc659a3ea030d7b02..bb2945dc267db568e44cdca2d525a47ca062c6cc 100644 --- a/cumulus/xcm/xcm-emulator/src/lib.rs +++ b/cumulus/xcm/xcm-emulator/src/lib.rs @@ -1,28 +1,33 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Cumulus. -// Polkadot is free software: you can redistribute it and/or modify +// 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. -// Polkadot is distributed in the hope that it will be useful, +// 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 Polkadot. If not, see . +// along with Cumulus. If not, see . extern crate alloc; +pub use array_bytes; pub use codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; -pub use lazy_static::lazy_static; pub use log; pub use paste; pub use std::{ - any::type_name, collections::HashMap, error::Error, fmt, marker::PhantomData, ops::Deref, - sync::Mutex, + any::type_name, + collections::HashMap, + error::Error, + fmt, + marker::PhantomData, + ops::Deref, + sync::{LazyLock, Mutex}, }; // Substrate @@ -296,9 +301,11 @@ impl Bridge for () { fn init() {} } +pub type BridgeLaneId = Vec; + #[derive(Clone, Default, Debug)] pub struct BridgeMessage { - pub id: u32, + pub lane_id: BridgeLaneId, pub nonce: u64, pub payload: Vec, } @@ -310,7 +317,7 @@ pub trait BridgeMessageHandler { message: BridgeMessage, ) -> Result<(), BridgeMessageDispatchError>; - fn notify_source_message_delivery(lane_id: u32); + fn notify_source_message_delivery(lane_id: BridgeLaneId); } impl BridgeMessageHandler for () { @@ -324,7 +331,7 @@ impl BridgeMessageHandler for () { Err(BridgeMessageDispatchError(Box::new("Not a bridge"))) } - fn notify_source_message_delivery(_lane_id: u32) {} + fn notify_source_message_delivery(_lane_id: BridgeLaneId) {} } #[derive(Debug)] @@ -440,10 +447,8 @@ macro_rules! __impl_test_ext_for_relay_chain { = $crate::RefCell::new($crate::TestExternalities::new($genesis)); } - $crate::lazy_static! { - pub static ref $global_ext: $crate::Mutex<$crate::RefCell<$crate::HashMap>> - = $crate::Mutex::new($crate::RefCell::new($crate::HashMap::new())); - } + pub static $global_ext: $crate::LazyLock<$crate::Mutex<$crate::RefCell<$crate::HashMap>>> + = $crate::LazyLock::new(|| $crate::Mutex::new($crate::RefCell::new($crate::HashMap::new()))); impl<$network: $crate::Network> $crate::TestExt for $name<$network> { fn build_new_ext(storage: $crate::Storage) -> $crate::TestExternalities { @@ -475,10 +480,10 @@ macro_rules! __impl_test_ext_for_relay_chain { v.take() }); - // Get TestExternality from lazy_static + // Get TestExternality from LazyLock let global_ext_guard = $global_ext.lock().unwrap(); - // Replace TestExternality in lazy_static by TestExternality from thread_local + // Replace TestExternality in LazyLock by TestExternality from thread_local global_ext_guard.deref().borrow_mut().insert(id.to_string(), local_ext); } @@ -487,10 +492,10 @@ macro_rules! __impl_test_ext_for_relay_chain { let mut global_ext_unlocked = false; - // Keep the mutex unlocked until TesExternality from lazy_static + // Keep the mutex unlocked until TesExternality from LazyLock // has been updated while !global_ext_unlocked { - // Get TesExternality from lazy_static + // Get TesExternality from LazyLock let global_ext_result = $global_ext.try_lock(); if let Ok(global_ext_guard) = global_ext_result { @@ -503,10 +508,10 @@ macro_rules! __impl_test_ext_for_relay_chain { } } - // Now that we know that lazy_static TestExt has been updated, we lock its mutex + // Now that we know that TestExt has been updated, we lock its mutex let mut global_ext_guard = $global_ext.lock().unwrap(); - // and set TesExternality from lazy_static into TesExternality for local_thread + // and set TesExternality from LazyLock into TesExternality for local_thread let global_ext = global_ext_guard.deref(); $local_ext.with(|v| { @@ -524,7 +529,10 @@ macro_rules! __impl_test_ext_for_relay_chain { <$network>::init(); // Execute - let r = $local_ext.with(|v| v.borrow_mut().execute_with(execute)); + let r = $local_ext.with(|v| { + $crate::log::info!(target: "xcm::emulator::execute_with", "Executing as {}", stringify!($name)); + v.borrow_mut().execute_with(execute) + }); // Send messages if needed $local_ext.with(|v| { @@ -548,7 +556,7 @@ macro_rules! __impl_test_ext_for_relay_chain { // log events Self::events().iter().for_each(|event| { - $crate::log::debug!(target: concat!("events::", stringify!($name)), "{:?}", event); + $crate::log::info!(target: concat!("events::", stringify!($name)), "{:?}", event); }); // clean events @@ -738,10 +746,8 @@ macro_rules! __impl_test_ext_for_parachain { = $crate::RefCell::new($crate::TestExternalities::new($genesis)); } - $crate::lazy_static! { - pub static ref $global_ext: $crate::Mutex<$crate::RefCell<$crate::HashMap>> - = $crate::Mutex::new($crate::RefCell::new($crate::HashMap::new())); - } + pub static $global_ext: $crate::LazyLock<$crate::Mutex<$crate::RefCell<$crate::HashMap>>> + = $crate::LazyLock::new(|| $crate::Mutex::new($crate::RefCell::new($crate::HashMap::new()))); impl<$network: $crate::Network> $crate::TestExt for $name<$network> { fn build_new_ext(storage: $crate::Storage) -> $crate::TestExternalities { @@ -771,10 +777,10 @@ macro_rules! __impl_test_ext_for_parachain { v.take() }); - // Get TestExternality from lazy_static + // Get TestExternality from LazyLock let global_ext_guard = $global_ext.lock().unwrap(); - // Replace TestExternality in lazy_static by TestExternality from thread_local + // Replace TestExternality in LazyLock by TestExternality from thread_local global_ext_guard.deref().borrow_mut().insert(id.to_string(), local_ext); } @@ -783,10 +789,10 @@ macro_rules! __impl_test_ext_for_parachain { let mut global_ext_unlocked = false; - // Keep the mutex unlocked until TesExternality from lazy_static + // Keep the mutex unlocked until TesExternality from LazyLock // has been updated while !global_ext_unlocked { - // Get TesExternality from lazy_static + // Get TesExternality from LazyLock let global_ext_result = $global_ext.try_lock(); if let Ok(global_ext_guard) = global_ext_result { @@ -799,10 +805,10 @@ macro_rules! __impl_test_ext_for_parachain { } } - // Now that we know that lazy_static TestExt has been updated, we lock its mutex + // Now that we know that TestExt has been updated, we lock its mutex let mut global_ext_guard = $global_ext.lock().unwrap(); - // and set TesExternality from lazy_static into TesExternality for local_thread + // and set TesExternality from LazyLock into TesExternality for local_thread let global_ext = global_ext_guard.deref(); $local_ext.with(|v| { @@ -824,7 +830,10 @@ macro_rules! __impl_test_ext_for_parachain { Self::new_block(); // Execute - let r = $local_ext.with(|v| v.borrow_mut().execute_with(execute)); + let r = $local_ext.with(|v| { + $crate::log::info!(target: "xcm::emulator::execute_with", "Executing as {}", stringify!($name)); + v.borrow_mut().execute_with(execute) + }); // Finalize the block Self::finalize_block(); @@ -870,7 +879,7 @@ macro_rules! __impl_test_ext_for_parachain { // log events ::events().iter().for_each(|event| { - $crate::log::debug!(target: concat!("events::", stringify!($name)), "{:?}", event); + $crate::log::info!(target: concat!("events::", stringify!($name)), "{:?}", event); }); // clean events @@ -1022,7 +1031,10 @@ macro_rules! decl_test_networks { &mut msg.using_encoded($crate::blake2_256), ); }); - $crate::log::debug!(target: concat!("dmp::", stringify!($name)) , "DMP messages processed {:?} to para_id {:?}", msgs.clone(), &to_para_id); + let messages = msgs.clone().iter().map(|(block, message)| { + (*block, $crate::array_bytes::bytes2hex("0x", message)) + }).collect::>(); + $crate::log::info!(target: concat!("xcm::dmp::", stringify!($name)) , "Downward messages processed by para_id {:?}: {:?}", &to_para_id, messages); $crate::DMP_DONE.with(|b| b.borrow_mut().get_mut(Self::name()).unwrap().push_back((to_para_id, block, msg))); } } @@ -1035,7 +1047,7 @@ macro_rules! decl_test_networks { while let Some((to_para_id, messages)) = $crate::HORIZONTAL_MESSAGES.with(|b| b.borrow_mut().get_mut(Self::name()).unwrap().pop_front()) { - let iter = messages.iter().map(|(p, b, m)| (*p, *b, &m[..])).collect::>().into_iter(); + let iter = messages.iter().map(|(para_id, relay_block_number, message)| (*para_id, *relay_block_number, &message[..])).collect::>().into_iter(); $( let para_id: u32 = <$parachain>::para_id().into(); @@ -1045,7 +1057,10 @@ macro_rules! decl_test_networks { // Nudge the MQ pallet to process immediately instead of in the next block. let _ = <$parachain as Parachain>::MessageProcessor::service_queues($crate::Weight::MAX); }); - $crate::log::debug!(target: concat!("hrmp::", stringify!($name)) , "HRMP messages processed {:?} to para_id {:?}", &messages, &to_para_id); + let messages = messages.clone().iter().map(|(para_id, relay_block_number, message)| { + (*para_id, *relay_block_number, $crate::array_bytes::bytes2hex("0x", message)) + }).collect::>(); + $crate::log::info!(target: concat!("xcm::hrmp::", stringify!($name)), "Horizontal messages processed by para_id {:?}: {:?}", &to_para_id, &messages); } )* } @@ -1064,7 +1079,8 @@ macro_rules! decl_test_networks { &mut msg.using_encoded($crate::blake2_256), ); }); - $crate::log::debug!(target: concat!("ump::", stringify!($name)) , "Upward message processed {:?} from para_id {:?}", &msg, &from_para_id); + let message = $crate::array_bytes::bytes2hex("0x", msg.clone()); + $crate::log::info!(target: concat!("xcm::ump::", stringify!($name)) , "Upward message processed from para_id {:?}: {:?}", &from_para_id, &message); } } @@ -1079,12 +1095,12 @@ macro_rules! decl_test_networks { }); match dispatch_result { - Err(e) => panic!("Error {:?} processing bridged message: {:?}", e, msg.clone()), + Err(e) => panic!("Error {:?} processing bridged message: {:?}", e, msg), Ok(()) => { <::Source as TestExt>::ext_wrapper(|| { - <::Handler as BridgeMessageHandler>::notify_source_message_delivery(msg.id); + <::Handler as BridgeMessageHandler>::notify_source_message_delivery(msg.lane_id.clone()); }); - $crate::log::debug!(target: concat!("bridge::", stringify!($name)) , "Bridged message processed {:?}", msg.clone()); + $crate::log::info!(target: concat!("bridge::", stringify!($name)) , "Bridged message processed {:?}", msg); } } } @@ -1295,7 +1311,7 @@ macro_rules! assert_expected_events { if !message.is_empty() { // Log events as they will not be logged after the panic <$chain as $crate::Chain>::events().iter().for_each(|event| { - $crate::log::debug!(target: concat!("events::", stringify!($chain)), "{:?}", event); + $crate::log::info!(target: concat!("events::", stringify!($chain)), "{:?}", event); }); panic!("{}", message.concat()) } diff --git a/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile b/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile index 55b9156e6a0ae811c8ef1d2bf8761452ae8c561a..b1f4bffc772abab2d18abca1a5f8974f6cdb2e5d 100644 --- a/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile +++ b/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile @@ -1,7 +1,7 @@ # this image is built on top of existing Zombienet image ARG ZOMBIENET_IMAGE # this image uses substrate-relay image built elsewhere -ARG SUBSTRATE_RELAY_IMAGE=docker.io/paritytech/substrate-relay:v1.6.8 +ARG SUBSTRATE_RELAY_IMAGE=docker.io/paritytech/substrate-relay:v1.7.0 # metadata ARG VCS_REF diff --git a/docker/dockerfiles/polkadot/polkadot_injected.Dockerfile b/docker/dockerfiles/polkadot/polkadot_injected.Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..3dbede4966a8ef7966cb10f2a1963a868f0c3cea --- /dev/null +++ b/docker/dockerfiles/polkadot/polkadot_injected.Dockerfile @@ -0,0 +1,52 @@ +FROM docker.io/parity/base-bin + +# metadata +ARG VCS_REF +ARG BUILD_DATE +ARG IMAGE_NAME +# That can be a single one or a comma separated list +ARG BINARY=polkadot + +LABEL io.parity.image.authors="devops-team@parity.io" \ + io.parity.image.vendor="Parity Technologies" \ + io.parity.image.title="parity/polkadot" \ + io.parity.image.description="Polkadot: a platform for web3. This is the official Parity image with an injected binary." \ + io.parity.image.source="https://github.com/paritytech/polkadot-sdk/blob/${VCS_REF}/docker/dockerfiles/polkadot/polkadot_injected.Dockerfile" \ + io.parity.image.revision="${VCS_REF}" \ + io.parity.image.created="${BUILD_DATE}" \ + io.parity.image.documentation="https://github.com/paritytech/polkadot-sdk/" + +# show backtraces +ENV RUST_BACKTRACE 1 + +USER root +WORKDIR /app + +# add polkadot and polkadot-*-worker binaries to the docker image +COPY bin/* /usr/local/bin/ +COPY entrypoint.sh . + + +RUN chmod -R a+rx "/usr/local/bin"; \ + mkdir -p /data /polkadot/.local/share && \ + chown -R parity:parity /data && \ + ln -s /data /polkadot/.local/share/polkadot + +USER parity + +# check if executable works in this container +RUN /usr/local/bin/polkadot --version +RUN /usr/local/bin/polkadot-prepare-worker --version +RUN /usr/local/bin/polkadot-execute-worker --version + + +EXPOSE 30333 9933 9944 9615 +VOLUME ["/polkadot"] + +ENV BINARY=${BINARY} + +# ENTRYPOINT +ENTRYPOINT ["/app/entrypoint.sh"] + +# We call the help by default +CMD ["--help"] diff --git a/docker/scripts/build-injected.sh b/docker/scripts/build-injected.sh index 749d0fa335cc1248fcde1770046fc7c1e31e9fbe..c37ea916c8394b5b8076710bc8409ea4fa6ee84c 100755 --- a/docker/scripts/build-injected.sh +++ b/docker/scripts/build-injected.sh @@ -40,7 +40,7 @@ VCS_REF=${VCS_REF:-01234567} echo "Using engine: $ENGINE" echo "Using Dockerfile: $DOCKERFILE" echo "Using context: $CONTEXT" -echo "Building ${IMAGE}:latest container image for ${BINARY} v${VERSION} from ${ARTIFACTS_FOLDER} hang on!" +echo "Building ${IMAGE}:latest container image for ${BINARY} ${VERSION} from ${ARTIFACTS_FOLDER} hang on!" echo "ARTIFACTS_FOLDER=$ARTIFACTS_FOLDER" echo "CONTEXT=$CONTEXT" diff --git a/docker/scripts/polkadot/build-injected.sh b/docker/scripts/polkadot/build-injected.sh index 7cc6db43a54a6b1a54fa4e917ed82185d28e93d0..8f4e7005b816c6c3ab44d2208eca59b1bba50434 100755 --- a/docker/scripts/polkadot/build-injected.sh +++ b/docker/scripts/polkadot/build-injected.sh @@ -9,5 +9,6 @@ PROJECT_ROOT=`git rev-parse --show-toplevel` export BINARY=polkadot,polkadot-execute-worker,polkadot-prepare-worker export ARTIFACTS_FOLDER=$1 +export DOCKERFILE="docker/dockerfiles/polkadot/polkadot_injected.Dockerfile" $PROJECT_ROOT/docker/scripts/build-injected.sh diff --git a/docs/contributor/commands-readme.md b/docs/contributor/commands-readme.md index 2bb9bd7e7d582448b8c8efbba937ca227075f1f5..3a0fadc3bb253505ca2127b62a85f2279183cb16 100644 --- a/docs/contributor/commands-readme.md +++ b/docs/contributor/commands-readme.md @@ -9,13 +9,14 @@ Note: it works only for members of the `paritytech` organization. `/cmd --help` to see the usage of a specific command - ### Commands - `/cmd fmt` to format the code in the PR. It commits back with the formatted code (fmt) and configs (taplo). - `/cmd bench` to generate weights for a runtime. Read more about [Weight Generation](weight-generation.md) +- `/cmd prdoc` to generate a prdoc for a PR. Read more about [PRDoc](prdoc.md) + ### Flags 1.`--quiet` to suppress the output of the command in the comments. @@ -32,13 +33,16 @@ The pipeline logs will include what is failed (like which runtimes/pallets), the or they keep failing, and you're rerunning them again, it's handy to add this flag to keep a PR clean. ### Adding new Commands + Feel free to add new commands to the workflow, however **_note_** that triggered workflows will use the actions from `main` (default) branch, meaning they will take effect only after the PR with new changes/command is merged. If you want to test the new command, it's better to test in your fork and local-to-fork PRs, where you control the default branch. ### Examples + The regex in cmd.yml is: `^(\/cmd )([-\/\s\w.=:]+)$` accepts only alphanumeric, space, "-", "/", "=", ":", "." chars. `/cmd bench --runtime bridge-hub-westend --pallet=pallet_name` +`/cmd prdoc --audience runtime_dev runtime_user --bump patch --force` `/cmd update-ui --image=docker.io/paritytech/ci-unified:bullseye-1.77.0-2024-04-10-v202407161507 --clean` diff --git a/docs/contributor/prdoc.md b/docs/contributor/prdoc.md index 0c8165af40f4d4f0438ab780c5f87eb5fe2ad62e..4a1a3c1f06880086d334b1bb5b1688e7d76aa455 100644 --- a/docs/contributor/prdoc.md +++ b/docs/contributor/prdoc.md @@ -14,28 +14,39 @@ the [CODEOWNERS](../../.github/CODEOWNERS) for advice. A `.prdoc` file is a YAML file with a defined structure (ie JSON Schema). Please follow these steps to generate one: -1. Install the [`prdoc` CLI](https://github.com/paritytech/prdoc) by running `cargo install prdoc`. +1. Install the [`prdoc` CLI](https://github.com/paritytech/prdoc) by running `cargo install parity-prdoc`. 1. Open a Pull Request and get the PR number. 1. Generate the file with `prdoc generate `. The output filename will be printed. 1. Optional: Install the `prdoc/schema_user.json` schema in your editor, for example -[VsCode](https://github.com/paritytech/prdoc?tab=readme-ov-file#schemas). + [VsCode](https://github.com/paritytech/prdoc?tab=readme-ov-file#schemas). 1. Edit your `.prdoc` file according to the [Audience](#pick-an-audience) and [SemVer](#record-semver-changes) sections. 1. Check your prdoc with `prdoc check -n `. This is optional since the CI will also check it. > **Tip:** GitHub CLI and jq can be used to provide the number of your PR to generate the correct file: > `prdoc generate $(gh pr view --json number | jq '.number') -o prdoc` +Alternatively you can call the prdoc from PR via `/cmd prdoc` (see args with `/cmd prdoc --help`) +in a comment to PR to trigger it from CI. + +Options: + +- `pr`: The PR number to generate the PrDoc for. +- `audience`: The audience of whom the changes may concern. +- `bump`: A default bump level for all crates. + The PrDoc will likely need to be edited to reflect the actual changes after generation. +- `force`: Whether to overwrite any existing PrDoc. + ## Pick An Audience While describing a PR, the author needs to consider which audience(s) need to be addressed. The list of valid audiences is described and documented in the JSON schema as follow: - `Node Dev`: Those who build around the client side code. Alternative client builders, SMOLDOT, those who consume RPCs. - These are people who are oblivious to the runtime changes. They only care about the meta-protocol, not the protocol - itself. + These are people who are oblivious to the runtime changes. They only care about the meta-protocol, not the protocol + itself. - `Runtime Dev`: All of those who rely on the runtime. A parachain team that is using a pallet. A DApp that is using a - pallet. These are people who care about the protocol (WASM), not the meta-protocol (client). + pallet. These are people who care about the protocol (WASM), not the meta-protocol (client). - `Node Operator`: Those who don't write any code and only run code. @@ -64,10 +75,10 @@ For example when you modified two crates and record the changes: ```yaml crates: -- name: frame-example - bump: major -- name: frame-example-pallet - bump: minor + - name: frame-example + bump: major + - name: frame-example-pallet + bump: minor ``` It means that downstream code using `frame-example-pallet` is still guaranteed to work as before, while code using diff --git a/docs/contributor/weight-generation.md b/docs/contributor/weight-generation.md index ebfdca59cae5d6f00b681d0b8ff94080e17202a4..77a570c94530c873a23d6c6bf7fe47e4ea2beff5 100644 --- a/docs/contributor/weight-generation.md +++ b/docs/contributor/weight-generation.md @@ -3,7 +3,7 @@ To generate weights for a runtime. Weights generation is using self-hosted runner which is provided by Parity CI, the rest commands are using standard GitHub runners on `ubuntu-latest` or `ubuntu-20.04`. -Self-hosted runner for benchmarks (arc-runners-Polkadot-sdk-benchmark) is configured to meet requirements of reference +Self-hosted runner for benchmarks (`parity-weights`) is configured to meet requirements of reference hardware for running validators https://wiki.polkadot.network/docs/maintain-guides-how-to-validate-polkadot#reference-hardware diff --git a/docs/sdk/assets/theme.css b/docs/sdk/assets/theme.css index a488e15c36b70ee76b14f429434daf0717bc0320..1f47a8ef5b0c4c399c35cd1fb0f6c9740d6b0230 100644 --- a/docs/sdk/assets/theme.css +++ b/docs/sdk/assets/theme.css @@ -9,9 +9,14 @@ body.sdk-docs { nav.sidebar>div.sidebar-crate>a>img { width: 190px; + height: 52px; } nav.sidebar { flex: 0 0 250px; } } + +html[data-theme="light"] .sidebar-crate > .logo-container > img { + content: url("https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/docs/images/Polkadot_Logo_Horizontal_Pink_Black.png"); +} diff --git a/docs/sdk/src/guides/enable_elastic_scaling_mvp.rs b/docs/sdk/src/guides/enable_elastic_scaling_mvp.rs index 38ef18b88e0d0af6ee6b25cadf577367a55c5e22..812e674d163bb098ae34a35d9154ce49bd341245 100644 --- a/docs/sdk/src/guides/enable_elastic_scaling_mvp.rs +++ b/docs/sdk/src/guides/enable_elastic_scaling_mvp.rs @@ -85,7 +85,7 @@ //! This phase consists of plugging in the new slot-based collator. //! //! 1. In `node/src/service.rs` import the slot based collator instead of the lookahead collator. -#![doc = docify::embed!("../../cumulus/polkadot-parachain/polkadot-parachain-lib/src/service.rs", slot_based_colator_import)] +#![doc = docify::embed!("../../cumulus/polkadot-omni-node/lib/src/nodes/aura.rs", slot_based_colator_import)] //! //! 2. In `start_consensus()` //! - Remove the `overseer_handle` param (also remove the @@ -94,7 +94,7 @@ //! `slot_drift` field with a value of `Duration::from_secs(1)`. //! - Replace the single future returned by `aura::run` with the two futures returned by it and //! spawn them as separate tasks: -#![doc = docify::embed!("../../cumulus/polkadot-parachain/polkadot-parachain-lib/src/service.rs", launch_slot_based_collator)] +#![doc = docify::embed!("../../cumulus/polkadot-omni-node/lib/src/nodes/aura.rs", launch_slot_based_collator)] //! //! 3. In `start_parachain_node()` remove the `overseer_handle` param passed to `start_consensus`. //! diff --git a/docs/sdk/src/guides/your_first_pallet/mod.rs b/docs/sdk/src/guides/your_first_pallet/mod.rs index 006d0be7ded35eaf1cf933ee7574536ebc0ac909..fcfaab00e5522e054326aff5d273fa86b9d9b3da 100644 --- a/docs/sdk/src/guides/your_first_pallet/mod.rs +++ b/docs/sdk/src/guides/your_first_pallet/mod.rs @@ -16,7 +16,7 @@ //! //! ## Writing Your First Pallet //! -//! To get started, use one of the templates mentioned in [`crate::polkadot_sdk::templates`]. We +//! To get started, clone one of the templates mentioned in [`crate::polkadot_sdk::templates`]. We //! recommend using the `polkadot-sdk-minimal-template`. You might need to change small parts of //! this guide, namely the crate/package names, based on which template you use. //! @@ -395,11 +395,11 @@ pub mod pallet { if sender_balance < amount { return Err("InsufficientBalance".into()) } - let reminder = sender_balance - amount; + let remainder = sender_balance - amount; // update sender and dest balances. Balances::::mutate(dest, |b| *b = Some(b.unwrap_or(0) + amount)); - Balances::::insert(&sender, reminder); + Balances::::insert(&sender, remainder); Ok(()) } @@ -417,7 +417,7 @@ pub mod pallet { let sender_balance = Balances::::get(&sender).ok_or("NonExistentAccount")?; ensure!(sender_balance >= amount, "InsufficientBalance"); - let reminder = sender_balance - amount; + let remainder = sender_balance - amount; // .. snip Ok(()) @@ -433,7 +433,7 @@ pub mod pallet { let sender = ensure_signed(origin)?; let sender_balance = Balances::::get(&sender).ok_or("NonExistentAccount")?; - let reminder = sender_balance.checked_sub(amount).ok_or("InsufficientBalance")?; + let remainder = sender_balance.checked_sub(amount).ok_or("InsufficientBalance")?; // .. snip Ok(()) @@ -717,11 +717,11 @@ pub mod pallet_v2 { // ensure sender has enough balance, and if so, calculate what is left after `amount`. let sender_balance = Balances::::get(&sender).ok_or(Error::::NonExistentAccount)?; - let reminder = + let remainder = sender_balance.checked_sub(amount).ok_or(Error::::InsufficientBalance)?; Balances::::mutate(&dest, |b| *b = Some(b.unwrap_or(0) + amount)); - Balances::::insert(&sender, reminder); + Balances::::insert(&sender, remainder); Self::deposit_event(Event::::Transferred { from: sender, to: dest, amount }); diff --git a/docs/sdk/src/guides/your_first_pallet/with_event.rs b/docs/sdk/src/guides/your_first_pallet/with_event.rs index a65aac324f07f6bff9fa85c9b9a2849188f3c4f2..a5af29c9c319498f4aee62e591d922144db4d8b0 100644 --- a/docs/sdk/src/guides/your_first_pallet/with_event.rs +++ b/docs/sdk/src/guides/your_first_pallet/with_event.rs @@ -54,11 +54,11 @@ pub mod pallet { if sender_balance < amount { return Err("NotEnoughBalance".into()) } - let reminder = sender_balance - amount; + let remainder = sender_balance - amount; // update sender and dest balances. Balances::::mutate(dest, |b| *b = Some(b.unwrap_or(0) + amount)); - Balances::::insert(&sender, reminder); + Balances::::insert(&sender, remainder); Ok(()) } @@ -76,7 +76,7 @@ pub mod pallet { let sender_balance = Balances::::get(&sender).ok_or("NonExistentAccount")?; ensure!(sender_balance >= amount, "NotEnoughBalance"); - let reminder = sender_balance - amount; + let remainder = sender_balance - amount; // .. snip Ok(()) @@ -92,7 +92,7 @@ pub mod pallet { let sender = ensure_signed(origin)?; let sender_balance = Balances::::get(&sender).ok_or("NonExistentAccount")?; - let reminder = sender_balance.checked_sub(amount).ok_or("NotEnoughBalance")?; + let remainder = sender_balance.checked_sub(amount).ok_or("NotEnoughBalance")?; // .. snip Ok(()) diff --git a/docs/sdk/src/lib.rs b/docs/sdk/src/lib.rs index 6dc87858530cf61d1a61426423ed6ae55c7fe786..35f73c290bf2c676bb010caee3a362fb5e14ee2b 100644 --- a/docs/sdk/src/lib.rs +++ b/docs/sdk/src/lib.rs @@ -27,7 +27,7 @@ #![warn(rustdoc::private_intra_doc_links)] #![doc(html_favicon_url = "https://polkadot.com/favicon.ico")] #![doc( - html_logo_url = "https://europe1.discourse-cdn.com/standard21/uploads/polkadot2/original/1X/eb57081e2bb7c39e5fcb1a98b443e423fa4448ae.svg" + html_logo_url = "https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/docs/images/Polkadot_Logo_Horizontal_Pink_White.png" )] #![doc(issue_tracker_base_url = "https://github.com/paritytech/polkadot-sdk/issues")] diff --git a/docs/sdk/src/polkadot_sdk/cumulus.rs b/docs/sdk/src/polkadot_sdk/cumulus.rs index 9bd957c7c1c07fec93b330ce8a8e7ec8118d31c1..c6abf9f7b4d17cc352beb531eb38b8eeef8615dd 100644 --- a/docs/sdk/src/polkadot_sdk/cumulus.rs +++ b/docs/sdk/src/polkadot_sdk/cumulus.rs @@ -96,6 +96,7 @@ mod tests { >; type WeightInfo = (); type DmpQueue = frame::traits::EnqueueWithOrigin<(), sp_core::ConstU8<0>>; + type SelectCore = cumulus_pallet_parachain_system::DefaultCoreSelector; } impl parachain_info::Config for Runtime {} diff --git a/docs/sdk/src/polkadot_sdk/templates.rs b/docs/sdk/src/polkadot_sdk/templates.rs index 03a7aa98198fa47ce7d19909b99a5a6fc4b49ca2..ab742ad5c3a2bd10fc621405ce8b4b407e93cc76 100644 --- a/docs/sdk/src/polkadot_sdk/templates.rs +++ b/docs/sdk/src/polkadot_sdk/templates.rs @@ -7,34 +7,22 @@ //! //! ## Internal //! -//! The following [templates](https://github.com/paritytech/polkadot-sdk/blob/master/templates) are -//! maintained as a part of the `polkadot-sdk` repository: -//! -//! - `minimal_template_node`/[`minimal_template_runtime`]: A minimal template that contains the -//! least amount of features to be a functioning blockchain. Suitable for learning, development -//! and testing. This template is not meant to be used in production. -//! - `solochain_template_node`/[`solochain_template_runtime`]: Formerly known as -//! "substrate-node-template", is a white-labeled substrate-based blockchain (aka. solochain) that -//! contains moderate features, such as a basic consensus engine and some FRAME pallets. This -//! template can act as a good starting point for those who want to launch a solochain. -//! - `parachain_template_node`/[`parachain_template_runtime`]: A parachain template ready to be -//! connected to a test relay-chain. -//! -//! These templates are always kept up to date, and are mirrored to external repositories for easy -//! forking: -//! -//! - -//! - -//! - -//! -//! ## External Templates -//! -//! Noteworthy templates outside of this repository. -//! -//! - [`extended-parachain-template`](https://github.com/paritytech/extended-parachain-template): A -//! parachain template that contains more built-in functionality such as assets and NFTs. -//! - [`frontier-parachain-template`](https://github.com/paritytech/frontier-parachain-template): A -//! parachain template for launching EVM-compatible parachains. +//! The following templates are maintained as a part of the `polkadot-sdk` repository: +//! +//! - [`minimal-template`](https://github.com/paritytech/polkadot-sdk-minimal-template): A minimal +//! template that contains the least amount of features to be a functioning blockchain. Suitable +//! for learning and testing. +//! - [`solochain-template`](https://github.com/paritytech/polkadot-sdk-solochain-template): +//! Formerly known as "substrate-node-template", is a white-labeled substrate-based blockchain +//! (aka. solochain) that contains moderate features, such as a basic consensus engine and some +//! FRAME pallets. This template can act as a good starting point for those who want to launch a +//! solochain. +//! - [`parachain-template`](https://github.com/paritytech/polkadot-sdk-parachain-template): +//! A parachain template ready to be connected to a relay-chain, such as [Paseo](https://github.com/paseo-network/.github) +//! , Kusama or Polkadot. +//! +//! Note that these templates are mirrored automatically from [this](https://github.com/paritytech/polkadot-sdk/blob/master/templates) +//! directory of polkadot-sdk, therefore any changes to them should be made as a PR to this repo. //! //! ## OpenZeppelin //! diff --git a/docs/sdk/src/reference_docs/chain_spec_genesis.rs b/docs/sdk/src/reference_docs/chain_spec_genesis.rs index 39e5993d020fa6cc262f8d7eebd4d7167ba5082e..a2e22d1ed1ebaedfd2eeebf75770d0df8885ed03 100644 --- a/docs/sdk/src/reference_docs/chain_spec_genesis.rs +++ b/docs/sdk/src/reference_docs/chain_spec_genesis.rs @@ -1,4 +1,4 @@ -//! # What is chain-spec. +//! # What is a chain specification //! //! A chain specification file defines the set of properties that are required to run the node as //! part of the chain. The chain specification consists of two main parts: @@ -165,8 +165,10 @@ #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs", list_presets)] //! ## Displaying preset with given name #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs", get_preset)] -//! ## Building chain-spec using given preset +//! ## Building a solo chain-spec (the default) using given preset #![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs", generate_chain_spec)] +//! ## Building a parachain chain-spec using given preset +#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs", generate_para_chain_spec)] //! //! [`RuntimeGenesisConfig`]: //! chain_spec_guide_runtime::runtime::RuntimeGenesisConfig diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/src/runtime.rs b/docs/sdk/src/reference_docs/chain_spec_runtime/src/runtime.rs index 195d1b124474dc0c076937c652095cd65884ca7d..5be3a59dc7bb84304ae38c9758439a2a1c99795b 100644 --- a/docs/sdk/src/reference_docs/chain_spec_runtime/src/runtime.rs +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/src/runtime.rs @@ -46,7 +46,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 1, + system_version: 1, }; /// The signed extensions that are added to the runtime. diff --git a/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs b/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs index d08f547bba616ad7be11cf7f2ca0e151e5e6583c..cc273685fcb4a416a78ed906c31785e8e48d168e 100644 --- a/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs +++ b/docs/sdk/src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs @@ -104,7 +104,66 @@ fn generate_chain_spec() { "bootNodes": [], "telemetryEndpoints": null, "protocolId": null, - "properties": null, + "properties": { "tokenDecimals": 12, "tokenSymbol": "UNIT" }, + "codeSubstitutes": {}, + "genesis": { + "runtimeGenesis": { + "code": "0x123", + "patch": { + "bar": { + "initialAccount": "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL" + }, + "foo": { + "someEnum": { + "Data2": { + "values": "0x0c10" + } + }, + "someInteger": 200 + } + } + } + } + }); + assert_eq!(output, expected_output, "Output did not match expected"); +} + +#[test] +#[docify::export] +fn generate_para_chain_spec() { + let output = Command::new(get_chain_spec_builder_path()) + .arg("-c") + .arg("/dev/stdout") + .arg("create") + .arg("-c") + .arg("polkadot") + .arg("-p") + .arg("1000") + .arg("-r") + .arg(WASM_FILE_PATH) + .arg("named-preset") + .arg("preset_2") + .output() + .expect("Failed to execute command"); + + let mut output: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap(); + + //remove code field for better readability + if let Some(code) = output["genesis"]["runtimeGenesis"].as_object_mut().unwrap().get_mut("code") + { + *code = Value::String("0x123".to_string()); + } + + let expected_output = json!({ + "name": "Custom", + "id": "custom", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "relay_chain": "polkadot", + "para_id": 1000, + "properties": { "tokenDecimals": 12, "tokenSymbol": "UNIT" }, "codeSubstitutes": {}, "genesis": { "runtimeGenesis": { diff --git a/docs/sdk/src/reference_docs/cli.rs b/docs/sdk/src/reference_docs/cli.rs index b9cdbd60e9592418241426c4f82aa9a3223559a4..6f393f267b0df33073701e648da35195da8a8b13 100644 --- a/docs/sdk/src/reference_docs/cli.rs +++ b/docs/sdk/src/reference_docs/cli.rs @@ -1,7 +1,7 @@ //! # Substrate CLI //! //! Let's see some examples of typical CLI arguments used when setting up and running a -//! Substrate-based blockchain. We use the [`substrate-node-template`](https://github.com/substrate-developer-hub/substrate-node-template) +//! Substrate-based blockchain. We use the [`solochain-template`](https://github.com/paritytech/polkadot-sdk-solochain-template) //! on these examples. //! //! #### Checking the available CLI arguments diff --git a/polkadot/cli/src/cli.rs b/polkadot/cli/src/cli.rs index 3e5a6ccdd3c25519e7890f73c3d5a1a49e41c9c8..eb67a39563421c87e4d129b19498535b329edc3a 100644 --- a/polkadot/cli/src/cli.rs +++ b/polkadot/cli/src/cli.rs @@ -93,12 +93,6 @@ pub struct RunCmd { #[arg(long)] pub force_authoring_backoff: bool, - /// Add the destination address to the 'Jaeger' agent. - /// - /// Must be valid socket address, of format `IP:Port` (commonly `127.0.0.1:6831`). - #[arg(long)] - pub jaeger_agent: Option, - /// Add the destination address to the `pyroscope` agent. /// /// Must be valid socket address, of format `IP:Port` (commonly `127.0.0.1:4040`). @@ -151,6 +145,13 @@ pub struct RunCmd { /// TESTING ONLY: disable the version check between nodes and workers. #[arg(long, hide = true)] pub disable_worker_version_check: bool, + + /// Enable approval-voting message processing in parallel. + /// + ///**Dangerous!** This is an experimental feature and should not be used in production, unless + /// explicitly advised to. + #[arg(long)] + pub enable_approval_voting_parallel: bool, } #[allow(missing_docs)] diff --git a/polkadot/cli/src/command.rs b/polkadot/cli/src/command.rs index 62d99122c3012701aa46039b13c3c05d3331d8c3..d124c8fb7eb745e2dec9af0a2dd35889675669b6 100644 --- a/polkadot/cli/src/command.rs +++ b/polkadot/cli/src/command.rs @@ -23,16 +23,15 @@ use polkadot_service::{ benchmarking::{benchmark_inherent_data, RemarkBuilder, TransferKeepAliveBuilder}, HeaderBackend, IdentifyVariant, }; +#[cfg(feature = "pyroscope")] +use pyroscope_pprofrs::{pprof_backend, PprofConfig}; use sc_cli::SubstrateCli; use sp_core::crypto::Ss58AddressFormatRegistry; use sp_keyring::Sr25519Keyring; -use std::net::ToSocketAddrs; pub use crate::error::Error; -#[cfg(feature = "hostperfcheck")] -pub use polkadot_performance_test::PerfCheckError; #[cfg(feature = "pyroscope")] -use pyroscope_pprofrs::{pprof_backend, PprofConfig}; +use std::net::ToSocketAddrs; type Result = std::result::Result; @@ -109,17 +108,6 @@ impl SubstrateCli for Cli { "westend-local" => Box::new(polkadot_service::chain_spec::westend_local_testnet_config()?), #[cfg(feature = "westend-native")] "westend-staging" => Box::new(polkadot_service::chain_spec::westend_staging_testnet_config()?), - #[cfg(not(feature = "westend-native"))] - name if name.starts_with("westend-") && !name.ends_with(".json") => - Err(format!("`{}` only supported with `westend-native` feature enabled.", name))?, - "wococo" => Box::new(polkadot_service::chain_spec::wococo_config()?), - #[cfg(feature = "rococo-native")] - "wococo-dev" => Box::new(polkadot_service::chain_spec::wococo_development_config()?), - #[cfg(feature = "rococo-native")] - "wococo-local" => Box::new(polkadot_service::chain_spec::wococo_local_testnet_config()?), - #[cfg(not(feature = "rococo-native"))] - name if name.starts_with("wococo-") => - Err(format!("`{}` only supported with `rococo-native` feature enabled.", name))?, #[cfg(feature = "rococo-native")] "versi-dev" => Box::new(polkadot_service::chain_spec::versi_development_config()?), #[cfg(feature = "rococo-native")] @@ -139,7 +127,6 @@ impl SubstrateCli for Cli { // chains, we use the chain spec for the specific chain. if self.run.force_rococo || chain_spec.is_rococo() || - chain_spec.is_wococo() || chain_spec.is_versi() { Box::new(polkadot_service::RococoChainSpec::from_json_file(path)?) @@ -209,18 +196,6 @@ where info!("----------------------------"); } - let jaeger_agent = if let Some(ref jaeger_agent) = cli.run.jaeger_agent { - Some( - jaeger_agent - .to_socket_addrs() - .map_err(Error::AddressResolutionFailure)? - .next() - .ok_or_else(|| Error::AddressResolutionMissing)?, - ) - } else { - None - }; - let node_version = if cli.run.disable_worker_version_check { None } else { Some(NODE_VERSION.to_string()) }; @@ -230,7 +205,7 @@ where let hwbench = (!cli.run.no_hardware_benchmarks) .then_some(config.database.path().map(|database_path| { let _ = std::fs::create_dir_all(&database_path); - sc_sysinfo::gather_hwbench(Some(database_path)) + sc_sysinfo::gather_hwbench(Some(database_path), &SUBSTRATE_REFERENCE_HARDWARE) })) .flatten(); @@ -241,7 +216,6 @@ where is_parachain_node: polkadot_service::IsParachainNode::No, enable_beefy, force_authoring_backoff: cli.run.force_authoring_backoff, - jaeger_agent, telemetry_worker_handle: None, node_version, secure_validator_mode, @@ -256,6 +230,7 @@ where execute_workers_max_num: cli.run.execute_workers_max_num, prepare_workers_hard_max_num: cli.run.prepare_workers_hard_max_num, prepare_workers_soft_max_num: cli.run.prepare_workers_soft_max_num, + enable_approval_voting_parallel: cli.run.enable_approval_voting_parallel, }, ) .map(|full| full.task_manager)?; @@ -319,7 +294,7 @@ pub fn run() -> Result<()> { runner.async_run(|mut config| { let (client, _, import_queue, task_manager) = - polkadot_service::new_chain_ops(&mut config, None)?; + polkadot_service::new_chain_ops(&mut config)?; Ok((cmd.run(client, import_queue).map_err(Error::SubstrateCli), task_manager)) }) }, @@ -331,8 +306,7 @@ pub fn run() -> Result<()> { Ok(runner.async_run(|mut config| { let (client, _, _, task_manager) = - polkadot_service::new_chain_ops(&mut config, None) - .map_err(Error::PolkadotService)?; + polkadot_service::new_chain_ops(&mut config).map_err(Error::PolkadotService)?; Ok((cmd.run(client, config.database).map_err(Error::SubstrateCli), task_manager)) })?) }, @@ -343,8 +317,7 @@ pub fn run() -> Result<()> { set_default_ss58_version(chain_spec); Ok(runner.async_run(|mut config| { - let (client, _, _, task_manager) = - polkadot_service::new_chain_ops(&mut config, None)?; + let (client, _, _, task_manager) = polkadot_service::new_chain_ops(&mut config)?; Ok((cmd.run(client, config.chain_spec).map_err(Error::SubstrateCli), task_manager)) })?) }, @@ -356,7 +329,7 @@ pub fn run() -> Result<()> { Ok(runner.async_run(|mut config| { let (client, _, import_queue, task_manager) = - polkadot_service::new_chain_ops(&mut config, None)?; + polkadot_service::new_chain_ops(&mut config)?; Ok((cmd.run(client, import_queue).map_err(Error::SubstrateCli), task_manager)) })?) }, @@ -372,17 +345,17 @@ pub fn run() -> Result<()> { Ok(runner.async_run(|mut config| { let (client, backend, _, task_manager) = - polkadot_service::new_chain_ops(&mut config, None)?; + polkadot_service::new_chain_ops(&mut config)?; + let task_handle = task_manager.spawn_handle(); let aux_revert = Box::new(|client, backend, blocks| { - polkadot_service::revert_backend(client, backend, blocks, config).map_err( - |err| { + polkadot_service::revert_backend(client, backend, blocks, config, task_handle) + .map_err(|err| { match err { polkadot_service::Error::Blockchain(err) => err.into(), // Generic application-specific error. err => sc_cli::Error::Application(err.into()), } - }, - ) + }) }); Ok(( cmd.run(client, backend, Some(aux_revert)).map_err(Error::SubstrateCli), @@ -405,22 +378,21 @@ pub fn run() -> Result<()> { .into()), #[cfg(feature = "runtime-benchmarks")] BenchmarkCmd::Storage(cmd) => runner.sync_run(|mut config| { - let (client, backend, _, _) = - polkadot_service::new_chain_ops(&mut config, None)?; + let (client, backend, _, _) = polkadot_service::new_chain_ops(&mut config)?; let db = backend.expose_db(); let storage = backend.expose_storage(); cmd.run(config, client.clone(), db, storage).map_err(Error::SubstrateCli) }), BenchmarkCmd::Block(cmd) => runner.sync_run(|mut config| { - let (client, _, _, _) = polkadot_service::new_chain_ops(&mut config, None)?; + let (client, _, _, _) = polkadot_service::new_chain_ops(&mut config)?; cmd.run(client.clone()).map_err(Error::SubstrateCli) }), // These commands are very similar and can be handled in nearly the same way. BenchmarkCmd::Extrinsic(_) | BenchmarkCmd::Overhead(_) => runner.sync_run(|mut config| { - let (client, _, _, _) = polkadot_service::new_chain_ops(&mut config, None)?; + let (client, _, _, _) = polkadot_service::new_chain_ops(&mut config)?; let header = client.header(client.info().genesis_hash).unwrap().unwrap(); let inherent_data = benchmark_inherent_data(header) .map_err(|e| format!("generating inherent data: {:?}", e))?; diff --git a/polkadot/node/collation-generation/src/tests.rs b/polkadot/node/collation-generation/src/tests.rs index 0feee79e763c94cbb6c1e1c16d6913ccd4a52bc2..7f76988bb03552280a17c9444de02101e92b383b 100644 --- a/polkadot/node/collation-generation/src/tests.rs +++ b/polkadot/node/collation-generation/src/tests.rs @@ -1090,7 +1090,6 @@ mod helpers { unpin_handle: polkadot_node_subsystem_test_helpers::mock::dummy_unpin_handle( activated_hash, ), - span: Arc::new(overseer::jaeger::Span::Disabled), }), ..Default::default() }))) diff --git a/polkadot/node/core/approval-voting-parallel/Cargo.toml b/polkadot/node/core/approval-voting-parallel/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..3a98cce80e920c3098b97ab3ef3888f9bf96983c --- /dev/null +++ b/polkadot/node/core/approval-voting-parallel/Cargo.toml @@ -0,0 +1,54 @@ +[package] +name = "polkadot-node-core-approval-voting-parallel" +version = "7.0.0" +authors.workspace = true +edition.workspace = true +license.workspace = true +description = "Approval Voting Subsystem running approval work in parallel" + +[lints] +workspace = true + +[dependencies] +async-trait = { workspace = true } +futures = { workspace = true } +futures-timer = { workspace = true } +gum = { workspace = true } +itertools = { workspace = true } +thiserror = { workspace = true } + +polkadot-node-core-approval-voting = { workspace = true, default-features = true } +polkadot-approval-distribution = { workspace = true, default-features = true } +polkadot-node-subsystem = { workspace = true, default-features = true } +polkadot-node-subsystem-util = { workspace = true, default-features = true } +polkadot-overseer = { workspace = true, default-features = true } +polkadot-primitives = { workspace = true, default-features = true } +polkadot-node-primitives = { workspace = true, default-features = true } +polkadot-node-network-protocol = { workspace = true, default-features = true } +polkadot-node-metrics = { workspace = true, default-features = true } + +sc-keystore = { workspace = true, default-features = false } +sp-consensus = { workspace = true, default-features = false } +sp-consensus-slots = { workspace = true, default-features = false } +sp-application-crypto = { workspace = true, default-features = false, features = ["full_crypto"] } +sp-runtime = { workspace = true, default-features = false } + +rand = { workspace = true } +rand_core = { workspace = true } +rand_chacha = { workspace = true } + +[dev-dependencies] +async-trait = { workspace = true } +parking_lot = { workspace = true } +sp-keyring = { workspace = true, default-features = true } +sp-keystore = { workspace = true, default-features = true } +sp-core = { workspace = true, default-features = true } +sp-consensus-babe = { workspace = true, default-features = true } +sp-tracing = { workspace = true } +polkadot-node-subsystem-test-helpers = { workspace = true, default-features = true } +assert_matches = { workspace = true } +kvdb-memorydb = { workspace = true } +polkadot-primitives-test-helpers = { workspace = true, default-features = true } +log = { workspace = true, default-features = true } +polkadot-subsystem-bench = { workspace = true, default-features = true } +schnorrkel = { workspace = true, default-features = true } diff --git a/polkadot/node/core/approval-voting-parallel/src/lib.rs b/polkadot/node/core/approval-voting-parallel/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..1a7ef756bdfc4d965b3615ec79b97319fc6e688b --- /dev/null +++ b/polkadot/node/core/approval-voting-parallel/src/lib.rs @@ -0,0 +1,958 @@ +// 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 . + +//! The Approval Voting Parallel Subsystem. +//! +//! This subsystem is responsible for orchestrating the work done by +//! approval-voting and approval-distribution subsystem, so they can +//! do their work in parallel, rather than serially, when they are run +//! as independent subsystems. +use itertools::Itertools; +use metrics::{Meters, MetricsWatcher}; +use polkadot_node_core_approval_voting::{Config, RealAssignmentCriteria}; +use polkadot_node_metrics::metered::{ + self, channel, unbounded, MeteredReceiver, MeteredSender, UnboundedMeteredReceiver, + UnboundedMeteredSender, +}; + +use polkadot_node_primitives::{ + approval::time::{Clock, SystemClock}, + DISPUTE_WINDOW, +}; +use polkadot_node_subsystem::{ + messages::{ApprovalDistributionMessage, ApprovalVotingMessage, ApprovalVotingParallelMessage}, + overseer, FromOrchestra, SpawnedSubsystem, SubsystemError, SubsystemResult, +}; +use polkadot_node_subsystem_util::{ + self, + database::Database, + runtime::{Config as RuntimeInfoConfig, RuntimeInfo}, +}; +use polkadot_overseer::{OverseerSignal, Priority, SubsystemSender, TimeoutExt}; +use polkadot_primitives::{CandidateIndex, Hash, ValidatorIndex, ValidatorSignature}; +use rand::SeedableRng; + +use sc_keystore::LocalKeystore; +use sp_consensus::SyncOracle; + +use futures::{channel::oneshot, prelude::*, StreamExt}; +pub use metrics::Metrics; +use polkadot_node_core_approval_voting::{ + approval_db::common::Config as DatabaseConfig, ApprovalVotingWorkProvider, +}; +use std::{ + collections::{HashMap, HashSet}, + fmt::Debug, + sync::Arc, + time::Duration, +}; +use stream::{select_with_strategy, PollNext, SelectWithStrategy}; +pub mod metrics; + +#[cfg(test)] +mod tests; + +pub(crate) const LOG_TARGET: &str = "parachain::approval-voting-parallel"; +// Value rather arbitrarily: Should not be hit in practice, it exists to more easily diagnose dead +// lock issues for example. +const WAIT_FOR_SIGS_GATHER_TIMEOUT: Duration = Duration::from_millis(2000); + +/// The number of workers used for running the approval-distribution logic. +pub const APPROVAL_DISTRIBUTION_WORKER_COUNT: usize = 4; + +/// The default channel size for the workers, can be overridden by the user through +/// `overseer_channel_capacity_override` +pub const DEFAULT_WORKERS_CHANNEL_SIZE: usize = 64000 / APPROVAL_DISTRIBUTION_WORKER_COUNT; + +fn prio_right<'a>(_val: &'a mut ()) -> PollNext { + PollNext::Right +} + +/// The approval voting parallel subsystem. +pub struct ApprovalVotingParallelSubsystem { + /// `LocalKeystore` is needed for assignment keys, but not necessarily approval keys. + /// + /// We do a lot of VRF signing and need the keys to have low latency. + keystore: Arc, + db_config: DatabaseConfig, + slot_duration_millis: u64, + db: Arc, + sync_oracle: Box, + metrics: Metrics, + spawner: Arc, + clock: Arc, + overseer_message_channel_capacity_override: Option, +} + +impl ApprovalVotingParallelSubsystem { + /// Create a new approval voting subsystem with the given keystore, config, and database. + pub fn with_config( + config: Config, + db: Arc, + keystore: Arc, + sync_oracle: Box, + metrics: Metrics, + spawner: impl overseer::gen::Spawner + 'static + Clone, + overseer_message_channel_capacity_override: Option, + ) -> Self { + ApprovalVotingParallelSubsystem::with_config_and_clock( + config, + db, + keystore, + sync_oracle, + metrics, + Arc::new(SystemClock {}), + spawner, + overseer_message_channel_capacity_override, + ) + } + + /// Create a new approval voting subsystem with the given keystore, config, clock, and database. + pub fn with_config_and_clock( + config: Config, + db: Arc, + keystore: Arc, + sync_oracle: Box, + metrics: Metrics, + clock: Arc, + spawner: impl overseer::gen::Spawner + 'static, + overseer_message_channel_capacity_override: Option, + ) -> Self { + ApprovalVotingParallelSubsystem { + keystore, + slot_duration_millis: config.slot_duration_millis, + db, + db_config: DatabaseConfig { col_approval_data: config.col_approval_data }, + sync_oracle, + metrics, + spawner: Arc::new(spawner), + clock, + overseer_message_channel_capacity_override, + } + } + + /// The size of the channel used for the workers. + fn workers_channel_size(&self) -> usize { + self.overseer_message_channel_capacity_override + .unwrap_or(DEFAULT_WORKERS_CHANNEL_SIZE) + } +} + +#[overseer::subsystem(ApprovalVotingParallel, error = SubsystemError, prefix = self::overseer)] +impl ApprovalVotingParallelSubsystem { + fn start(self, ctx: Context) -> SpawnedSubsystem { + let future = run::(ctx, self) + .map_err(|e| SubsystemError::with_origin("approval-voting-parallel", e)) + .boxed(); + + SpawnedSubsystem { name: "approval-voting-parallel-subsystem", future } + } +} + +// It starts worker for the approval voting subsystem and the `APPROVAL_DISTRIBUTION_WORKER_COUNT` +// workers for the approval distribution subsystem. +// +// It returns handles that can be used to send messages to the workers. +#[overseer::contextbounds(ApprovalVotingParallel, prefix = self::overseer)] +async fn start_workers( + ctx: &mut Context, + subsystem: ApprovalVotingParallelSubsystem, + metrics_watcher: &mut MetricsWatcher, +) -> SubsystemResult<(ToWorker, Vec>)> +where +{ + gum::info!(target: LOG_TARGET, "Starting approval distribution workers"); + + // Build approval voting handles. + let (to_approval_voting_worker, approval_voting_work_provider) = build_worker_handles( + "approval-voting-parallel-db".into(), + subsystem.workers_channel_size(), + metrics_watcher, + prio_right, + ); + let mut to_approval_distribution_workers = Vec::new(); + let slot_duration_millis = subsystem.slot_duration_millis; + + for i in 0..APPROVAL_DISTRIBUTION_WORKER_COUNT { + let mut network_sender = ctx.sender().clone(); + let mut runtime_api_sender = ctx.sender().clone(); + let mut approval_distribution_to_approval_voting = to_approval_voting_worker.clone(); + + let approval_distr_instance = + polkadot_approval_distribution::ApprovalDistribution::new_with_clock( + subsystem.metrics.approval_distribution_metrics(), + subsystem.slot_duration_millis, + subsystem.clock.clone(), + Arc::new(RealAssignmentCriteria {}), + ); + let task_name = format!("approval-voting-parallel-{}", i); + let (to_approval_distribution_worker, mut approval_distribution_work_provider) = + build_worker_handles( + task_name.clone(), + subsystem.workers_channel_size(), + metrics_watcher, + prio_right, + ); + + metrics_watcher.watch(task_name.clone(), to_approval_distribution_worker.meter()); + + subsystem.spawner.spawn_blocking( + task_name.leak(), + Some("approval-voting-parallel"), + Box::pin(async move { + let mut state = + polkadot_approval_distribution::State::with_config(slot_duration_millis); + let mut rng = rand::rngs::StdRng::from_entropy(); + let mut session_info_provider = RuntimeInfo::new_with_config(RuntimeInfoConfig { + keystore: None, + session_cache_lru_size: DISPUTE_WINDOW.get(), + }); + + loop { + let message = match approval_distribution_work_provider.next().await { + Some(message) => message, + None => { + gum::info!( + target: LOG_TARGET, + "Approval distribution stream finished, most likely shutting down", + ); + break; + }, + }; + if approval_distr_instance + .handle_from_orchestra( + message, + &mut approval_distribution_to_approval_voting, + &mut network_sender, + &mut runtime_api_sender, + &mut state, + &mut rng, + &mut session_info_provider, + ) + .await + { + gum::info!( + target: LOG_TARGET, + "Approval distribution worker {}, exiting because of shutdown", i + ); + }; + } + }), + ); + to_approval_distribution_workers.push(to_approval_distribution_worker); + } + + gum::info!(target: LOG_TARGET, "Starting approval voting workers"); + + let sender = ctx.sender().clone(); + let to_approval_distribution = ApprovalVotingToApprovalDistribution(sender.clone()); + polkadot_node_core_approval_voting::start_approval_worker( + approval_voting_work_provider, + sender.clone(), + to_approval_distribution, + polkadot_node_core_approval_voting::Config { + slot_duration_millis: subsystem.slot_duration_millis, + col_approval_data: subsystem.db_config.col_approval_data, + }, + subsystem.db.clone(), + subsystem.keystore.clone(), + subsystem.sync_oracle, + subsystem.metrics.approval_voting_metrics(), + subsystem.spawner.clone(), + "approval-voting-parallel-db", + "approval-voting-parallel", + subsystem.clock.clone(), + ) + .await?; + + Ok((to_approval_voting_worker, to_approval_distribution_workers)) +} + +// The main run function of the approval parallel voting subsystem. +#[overseer::contextbounds(ApprovalVotingParallel, prefix = self::overseer)] +async fn run( + mut ctx: Context, + subsystem: ApprovalVotingParallelSubsystem, +) -> SubsystemResult<()> { + let mut metrics_watcher = MetricsWatcher::new(subsystem.metrics.clone()); + gum::info!( + target: LOG_TARGET, + "Starting workers" + ); + + let (to_approval_voting_worker, to_approval_distribution_workers) = + start_workers(&mut ctx, subsystem, &mut metrics_watcher).await?; + + gum::info!( + target: LOG_TARGET, + "Starting main subsystem loop" + ); + + run_main_loop(ctx, to_approval_voting_worker, to_approval_distribution_workers, metrics_watcher) + .await +} + +// Main loop of the subsystem, it shouldn't include any logic just dispatching of messages to +// the workers. +// +// It listens for messages from the overseer and dispatches them to the workers. +#[overseer::contextbounds(ApprovalVotingParallel, prefix = self::overseer)] +async fn run_main_loop( + mut ctx: Context, + mut to_approval_voting_worker: ToWorker, + mut to_approval_distribution_workers: Vec>, + metrics_watcher: MetricsWatcher, +) -> SubsystemResult<()> { + loop { + futures::select! { + next_msg = ctx.recv().fuse() => { + let next_msg = match next_msg { + Ok(msg) => msg, + Err(err) => { + gum::info!(target: LOG_TARGET, ?err, "Approval voting parallel subsystem received an error"); + return Err(err); + } + }; + + match next_msg { + FromOrchestra::Signal(msg) => { + if matches!(msg, OverseerSignal::ActiveLeaves(_)) { + metrics_watcher.collect_metrics(); + } + + for worker in to_approval_distribution_workers.iter_mut() { + worker + .send_signal(msg.clone()).await?; + } + + to_approval_voting_worker.send_signal(msg.clone()).await?; + if matches!(msg, OverseerSignal::Conclude) { + break; + } + }, + FromOrchestra::Communication { msg } => match msg { + // The message the approval voting subsystem would've handled. + ApprovalVotingParallelMessage::ApprovedAncestor(_, _,_) | + ApprovalVotingParallelMessage::GetApprovalSignaturesForCandidate(_, _) => { + to_approval_voting_worker.send_message( + msg.try_into().expect( + "Message is one of ApprovedAncestor, GetApprovalSignaturesForCandidate + and that can be safely converted to ApprovalVotingMessage; qed" + ) + ).await; + }, + // Now the message the approval distribution subsystem would've handled and need to + // be forwarded to the workers. + ApprovalVotingParallelMessage::NewBlocks(msg) => { + for worker in to_approval_distribution_workers.iter_mut() { + worker + .send_message( + ApprovalDistributionMessage::NewBlocks(msg.clone()), + ) + .await; + } + }, + ApprovalVotingParallelMessage::DistributeAssignment(assignment, claimed) => { + let worker = assigned_worker_for_validator(assignment.validator, &mut to_approval_distribution_workers); + worker + .send_message( + ApprovalDistributionMessage::DistributeAssignment(assignment, claimed) + ) + .await; + + }, + ApprovalVotingParallelMessage::DistributeApproval(vote) => { + let worker = assigned_worker_for_validator(vote.validator, &mut to_approval_distribution_workers); + worker + .send_message( + ApprovalDistributionMessage::DistributeApproval(vote) + ).await; + + }, + ApprovalVotingParallelMessage::NetworkBridgeUpdate(msg) => { + if let polkadot_node_subsystem::messages::NetworkBridgeEvent::PeerMessage( + peer_id, + msg, + ) = msg + { + let (all_msgs_from_same_validator, messages_split_by_validator) = validator_index_for_msg(msg); + + for (validator_index, msg) in all_msgs_from_same_validator.into_iter().chain(messages_split_by_validator.into_iter().flatten()) { + let worker = assigned_worker_for_validator(validator_index, &mut to_approval_distribution_workers); + + worker + .send_message( + ApprovalDistributionMessage::NetworkBridgeUpdate( + polkadot_node_subsystem::messages::NetworkBridgeEvent::PeerMessage( + peer_id, msg, + ), + ), + ).await; + } + } else { + for worker in to_approval_distribution_workers.iter_mut() { + worker + .send_message_with_priority::( + ApprovalDistributionMessage::NetworkBridgeUpdate(msg.clone()), + ).await; + } + } + }, + ApprovalVotingParallelMessage::GetApprovalSignatures(indices, tx) => { + handle_get_approval_signatures(&mut ctx, &mut to_approval_distribution_workers, indices, tx).await; + }, + ApprovalVotingParallelMessage::ApprovalCheckingLagUpdate(lag) => { + for worker in to_approval_distribution_workers.iter_mut() { + worker + .send_message( + ApprovalDistributionMessage::ApprovalCheckingLagUpdate(lag) + ).await; + } + }, + }, + }; + + }, + }; + } + Ok(()) +} + +// It sends a message to all approval workers to get the approval signatures for the requested +// candidates and then merges them all together and sends them back to the requester. +#[overseer::contextbounds(ApprovalVotingParallel, prefix = self::overseer)] +async fn handle_get_approval_signatures( + ctx: &mut Context, + to_approval_distribution_workers: &mut Vec>, + requested_candidates: HashSet<(Hash, CandidateIndex)>, + result_channel: oneshot::Sender< + HashMap, ValidatorSignature)>, + >, +) { + let mut sigs = HashMap::new(); + let mut signatures_channels = Vec::new(); + for worker in to_approval_distribution_workers.iter_mut() { + let (tx, rx) = oneshot::channel(); + worker.send_unbounded_message(ApprovalDistributionMessage::GetApprovalSignatures( + requested_candidates.clone(), + tx, + )); + signatures_channels.push(rx); + } + + let gather_signatures = async move { + let Some(results) = futures::future::join_all(signatures_channels) + .timeout(WAIT_FOR_SIGS_GATHER_TIMEOUT) + .await + else { + gum::warn!( + target: LOG_TARGET, + "Waiting for approval signatures timed out - dead lock?" + ); + return; + }; + + for result in results { + let worker_sigs = match result { + Ok(sigs) => sigs, + Err(_) => { + gum::error!( + target: LOG_TARGET, + "Getting approval signatures failed, oneshot got closed" + ); + continue; + }, + }; + sigs.extend(worker_sigs); + } + + if let Err(_) = result_channel.send(sigs) { + gum::debug!( + target: LOG_TARGET, + "Sending back approval signatures failed, oneshot got closed" + ); + } + }; + + if let Err(err) = ctx.spawn("approval-voting-gather-signatures", Box::pin(gather_signatures)) { + gum::warn!(target: LOG_TARGET, "Failed to spawn gather signatures task: {:?}", err); + } +} + +// Returns the worker that should receive the message for the given validator. +fn assigned_worker_for_validator( + validator: ValidatorIndex, + to_approval_distribution_workers: &mut Vec>, +) -> &mut ToWorker { + let worker_index = validator.0 as usize % to_approval_distribution_workers.len(); + to_approval_distribution_workers + .get_mut(worker_index) + .expect("Worker index is obtained modulo len; qed") +} + +// Returns the validators that initially created this assignments/votes, the validator index +// is later used to decide which approval-distribution worker should receive the message. +// +// Because this is on the hot path and we don't want to be unnecessarily slow, it contains two logic +// paths. The ultra fast path where all messages have the same validator index and we don't do +// any cloning or allocation and the path where we need to split the messages into multiple +// messages, because they have different validator indices, where we do need to clone and allocate. +// In practice most of the message will fall on the ultra fast path. +fn validator_index_for_msg( + msg: polkadot_node_network_protocol::ApprovalDistributionMessage, +) -> ( + Option<(ValidatorIndex, polkadot_node_network_protocol::ApprovalDistributionMessage)>, + Option>, +) { + match msg { + polkadot_node_network_protocol::Versioned::V1(ref message) => match message { + polkadot_node_network_protocol::v1::ApprovalDistributionMessage::Assignments(msgs) => + if let Ok(validator) = msgs.iter().map(|(msg, _)| msg.validator).all_equal_value() { + (Some((validator, msg)), None) + } else { + let split = msgs + .iter() + .map(|(msg, claimed_candidates)| { + ( + msg.validator, + polkadot_node_network_protocol::Versioned::V1( + polkadot_node_network_protocol::v1::ApprovalDistributionMessage::Assignments( + vec![(msg.clone(), *claimed_candidates)] + ), + ), + ) + }) + .collect_vec(); + (None, Some(split)) + }, + polkadot_node_network_protocol::v1::ApprovalDistributionMessage::Approvals(msgs) => + if let Ok(validator) = msgs.iter().map(|msg| msg.validator).all_equal_value() { + (Some((validator, msg)), None) + } else { + let split = msgs + .iter() + .map(|vote| { + ( + vote.validator, + polkadot_node_network_protocol::Versioned::V1( + polkadot_node_network_protocol::v1::ApprovalDistributionMessage::Approvals( + vec![vote.clone()] + ), + ), + ) + }) + .collect_vec(); + (None, Some(split)) + }, + }, + polkadot_node_network_protocol::Versioned::V2(ref message) => match message { + polkadot_node_network_protocol::v2::ApprovalDistributionMessage::Assignments(msgs) => + if let Ok(validator) = msgs.iter().map(|(msg, _)| msg.validator).all_equal_value() { + (Some((validator, msg)), None) + } else { + let split = msgs + .iter() + .map(|(msg, claimed_candidates)| { + ( + msg.validator, + polkadot_node_network_protocol::Versioned::V2( + polkadot_node_network_protocol::v2::ApprovalDistributionMessage::Assignments( + vec![(msg.clone(), *claimed_candidates)] + ), + ), + ) + }) + .collect_vec(); + (None, Some(split)) + }, + + polkadot_node_network_protocol::v2::ApprovalDistributionMessage::Approvals(msgs) => + if let Ok(validator) = msgs.iter().map(|msg| msg.validator).all_equal_value() { + (Some((validator, msg)), None) + } else { + let split = msgs + .iter() + .map(|vote| { + ( + vote.validator, + polkadot_node_network_protocol::Versioned::V2( + polkadot_node_network_protocol::v2::ApprovalDistributionMessage::Approvals( + vec![vote.clone()] + ), + ), + ) + }) + .collect_vec(); + (None, Some(split)) + }, + }, + polkadot_node_network_protocol::Versioned::V3(ref message) => match message { + polkadot_node_network_protocol::v3::ApprovalDistributionMessage::Assignments(msgs) => + if let Ok(validator) = msgs.iter().map(|(msg, _)| msg.validator).all_equal_value() { + (Some((validator, msg)), None) + } else { + let split = msgs + .iter() + .map(|(msg, claimed_candidates)| { + ( + msg.validator, + polkadot_node_network_protocol::Versioned::V3( + polkadot_node_network_protocol::v3::ApprovalDistributionMessage::Assignments( + vec![(msg.clone(), claimed_candidates.clone())] + ), + ), + ) + }) + .collect_vec(); + (None, Some(split)) + }, + polkadot_node_network_protocol::v3::ApprovalDistributionMessage::Approvals(msgs) => + if let Ok(validator) = msgs.iter().map(|msg| msg.validator).all_equal_value() { + (Some((validator, msg)), None) + } else { + let split = msgs + .iter() + .map(|vote| { + ( + vote.validator, + polkadot_node_network_protocol::Versioned::V3( + polkadot_node_network_protocol::v3::ApprovalDistributionMessage::Approvals( + vec![vote.clone()] + ), + ), + ) + }) + .collect_vec(); + (None, Some(split)) + }, + }, + } +} + +/// A handler object that both type of workers use for receiving work. +/// +/// In practive this is just a wrapper over two channels Receiver, that is injected into +/// approval-voting worker and approval-distribution workers. +type WorkProvider = WorkProviderImpl< + SelectWithStrategy< + MeteredReceiver>, + UnboundedMeteredReceiver>, + Clos, + State, + >, +>; + +pub struct WorkProviderImpl(T); + +impl Stream for WorkProviderImpl +where + T: Stream> + Unpin + Send, +{ + type Item = FromOrchestra; + + fn poll_next( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + self.0.poll_next_unpin(cx) + } +} + +#[async_trait::async_trait] +impl ApprovalVotingWorkProvider for WorkProviderImpl +where + T: Stream> + Unpin + Send, +{ + async fn recv(&mut self) -> SubsystemResult> { + self.0.next().await.ok_or(SubsystemError::Context( + "ApprovalVotingWorkProviderImpl: Channel closed".to_string(), + )) + } +} + +impl WorkProvider +where + M: Send + Sync + 'static, + Clos: FnMut(&mut State) -> PollNext, + State: Default, +{ + // Constructs a work providers from the channels handles. + fn from_rx_worker(rx: RxWorker, prio: Clos) -> Self { + let prioritised = select_with_strategy(rx.0, rx.1, prio); + WorkProviderImpl(prioritised) + } +} + +/// Just a wrapper for implementing `overseer::SubsystemSender` and +/// `overseer::SubsystemSender`. +/// +/// The instance of this struct can be injected into the workers, so they can talk +/// directly with each other without intermediating in this subsystem loop. +pub struct ToWorker( + MeteredSender>, + UnboundedMeteredSender>, +); + +impl Clone for ToWorker { + fn clone(&self) -> Self { + Self(self.0.clone(), self.1.clone()) + } +} + +impl ToWorker { + async fn send_signal(&mut self, signal: OverseerSignal) -> Result<(), SubsystemError> { + self.1 + .unbounded_send(FromOrchestra::Signal(signal)) + .map_err(|err| SubsystemError::QueueError(err.into_send_error())) + } + + fn meter(&self) -> Meters { + Meters::new(self.0.meter(), self.1.meter()) + } +} + +impl overseer::SubsystemSender for ToWorker { + fn send_message<'life0, 'async_trait>( + &'life0 mut self, + msg: T, + ) -> ::core::pin::Pin< + Box + ::core::marker::Send + 'async_trait>, + > + where + 'life0: 'async_trait, + Self: 'async_trait, + { + async { + if let Err(err) = + self.0.send(polkadot_overseer::FromOrchestra::Communication { msg }).await + { + gum::error!( + target: LOG_TARGET, + "Failed to send message to approval voting worker: {:?}, subsystem is probably shutting down.", + err + ); + } + } + .boxed() + } + + fn try_send_message(&mut self, msg: T) -> Result<(), metered::TrySendError> { + self.0 + .try_send(polkadot_overseer::FromOrchestra::Communication { msg }) + .map_err(|result| { + let is_full = result.is_full(); + let msg = match result.into_inner() { + polkadot_overseer::FromOrchestra::Signal(_) => + panic!("Cannot happen variant is never built"), + polkadot_overseer::FromOrchestra::Communication { msg } => msg, + }; + if is_full { + metered::TrySendError::Full(msg) + } else { + metered::TrySendError::Closed(msg) + } + }) + } + + fn send_messages<'life0, 'async_trait, I>( + &'life0 mut self, + msgs: I, + ) -> ::core::pin::Pin< + Box + ::core::marker::Send + 'async_trait>, + > + where + I: IntoIterator + Send, + I::IntoIter: Send, + I: 'async_trait, + 'life0: 'async_trait, + Self: 'async_trait, + { + async { + for msg in msgs { + self.send_message(msg).await; + } + } + .boxed() + } + + fn send_unbounded_message(&mut self, msg: T) { + if let Err(err) = + self.1.unbounded_send(polkadot_overseer::FromOrchestra::Communication { msg }) + { + gum::error!( + target: LOG_TARGET, + "Failed to send unbounded message to approval voting worker: {:?}, subsystem is probably shutting down.", + err + ); + } + } + + fn send_message_with_priority<'life0, 'async_trait, P>( + &'life0 mut self, + msg: T, + ) -> ::core::pin::Pin< + Box + ::core::marker::Send + 'async_trait>, + > + where + P: 'async_trait + Priority, + 'life0: 'async_trait, + Self: 'async_trait, + { + match P::priority() { + polkadot_overseer::PriorityLevel::Normal => self.send_message(msg), + polkadot_overseer::PriorityLevel::High => + async { self.send_unbounded_message(msg) }.boxed(), + } + } + + fn try_send_message_with_priority( + &mut self, + msg: T, + ) -> Result<(), metered::TrySendError> { + match P::priority() { + polkadot_overseer::PriorityLevel::Normal => self.try_send_message(msg), + polkadot_overseer::PriorityLevel::High => Ok(self.send_unbounded_message(msg)), + } + } +} + +/// Handles that are used by an worker to receive work. +pub struct RxWorker( + MeteredReceiver>, + UnboundedMeteredReceiver>, +); + +// Build all the necessary channels for sending messages to an worker +// and for the worker to receive them. +fn build_channels( + channel_name: String, + channel_size: usize, + metrics_watcher: &mut MetricsWatcher, +) -> (ToWorker, RxWorker) { + let (tx_work, rx_work) = channel::>(channel_size); + let (tx_work_unbounded, rx_work_unbounded) = unbounded::>(); + let to_worker = ToWorker(tx_work, tx_work_unbounded); + + metrics_watcher.watch(channel_name, to_worker.meter()); + + (to_worker, RxWorker(rx_work, rx_work_unbounded)) +} + +/// Build the worker handles used for interacting with the workers. +/// +/// `ToWorker` is used for sending messages to the workers. +/// `WorkProvider` is used by the workers for receiving the messages. +fn build_worker_handles( + channel_name: String, + channel_size: usize, + metrics_watcher: &mut MetricsWatcher, + prio_right: Clos, +) -> (ToWorker, WorkProvider) +where + M: Send + Sync + 'static, + Clos: FnMut(&mut State) -> PollNext, + State: Default, +{ + let (to_worker, rx_worker) = build_channels(channel_name, channel_size, metrics_watcher); + (to_worker, WorkProviderImpl::from_rx_worker(rx_worker, prio_right)) +} + +/// Just a wrapper for implementing `overseer::SubsystemSender`, so +/// that we can inject into the approval voting subsystem. +#[derive(Clone)] +pub struct ApprovalVotingToApprovalDistribution>( + S, +); + +impl> + overseer::SubsystemSender + for ApprovalVotingToApprovalDistribution +{ + #[allow(clippy::type_complexity, clippy::type_repetition_in_bounds)] + fn send_message<'life0, 'async_trait>( + &'life0 mut self, + msg: ApprovalDistributionMessage, + ) -> ::core::pin::Pin< + Box + ::core::marker::Send + 'async_trait>, + > + where + 'life0: 'async_trait, + Self: 'async_trait, + { + self.0.send_message(msg.into()) + } + + fn try_send_message( + &mut self, + msg: ApprovalDistributionMessage, + ) -> Result<(), metered::TrySendError> { + self.0.try_send_message(msg.into()).map_err(|err| match err { + // Safe to unwrap because it was built from the same type. + metered::TrySendError::Closed(msg) => + metered::TrySendError::Closed(msg.try_into().unwrap()), + metered::TrySendError::Full(msg) => + metered::TrySendError::Full(msg.try_into().unwrap()), + }) + } + + #[allow(clippy::type_complexity, clippy::type_repetition_in_bounds)] + fn send_messages<'life0, 'async_trait, I>( + &'life0 mut self, + msgs: I, + ) -> ::core::pin::Pin< + Box + ::core::marker::Send + 'async_trait>, + > + where + I: IntoIterator + Send, + I::IntoIter: Send, + I: 'async_trait, + 'life0: 'async_trait, + Self: 'async_trait, + { + self.0.send_messages(msgs.into_iter().map(|msg| msg.into())) + } + + fn send_unbounded_message(&mut self, msg: ApprovalDistributionMessage) { + self.0.send_unbounded_message(msg.into()) + } + + fn send_message_with_priority<'life0, 'async_trait, P>( + &'life0 mut self, + msg: ApprovalDistributionMessage, + ) -> ::core::pin::Pin< + Box + ::core::marker::Send + 'async_trait>, + > + where + P: 'async_trait + Priority, + 'life0: 'async_trait, + Self: 'async_trait, + { + self.0.send_message_with_priority::

(msg.into()) + } + + fn try_send_message_with_priority( + &mut self, + msg: ApprovalDistributionMessage, + ) -> Result<(), metered::TrySendError> { + self.0.try_send_message_with_priority::

(msg.into()).map_err(|err| match err { + // Safe to unwrap because it was built from the same type. + metered::TrySendError::Closed(msg) => + metered::TrySendError::Closed(msg.try_into().unwrap()), + metered::TrySendError::Full(msg) => + metered::TrySendError::Full(msg.try_into().unwrap()), + }) + } +} diff --git a/polkadot/node/core/approval-voting-parallel/src/metrics.rs b/polkadot/node/core/approval-voting-parallel/src/metrics.rs new file mode 100644 index 0000000000000000000000000000000000000000..1b4ab4bd9b884e8971f7465a2af86676ba9bfe4f --- /dev/null +++ b/polkadot/node/core/approval-voting-parallel/src/metrics.rs @@ -0,0 +1,236 @@ +// 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 . + +//! The Metrics for Approval Voting Parallel Subsystem. + +use std::collections::HashMap; + +use polkadot_node_metrics::{metered::Meter, metrics}; +use polkadot_overseer::prometheus; + +#[derive(Default, Clone)] +pub struct Metrics(Option); + +/// Approval Voting parallel metrics. +#[derive(Clone)] +pub struct MetricsInner { + // The inner metrics of the approval distribution workers. + approval_distribution: polkadot_approval_distribution::metrics::Metrics, + // The inner metrics of the approval voting workers. + approval_voting: polkadot_node_core_approval_voting::Metrics, + + // Time of flight metrics for bounded channels. + to_worker_bounded_tof: prometheus::HistogramVec, + // Number of elements sent to the worker's bounded queue. + to_worker_bounded_sent: prometheus::GaugeVec, + // Number of elements received by the worker's bounded queue. + to_worker_bounded_received: prometheus::GaugeVec, + // Number of times senders blocked while sending messages to the worker. + to_worker_bounded_blocked: prometheus::GaugeVec, + // Time of flight metrics for unbounded channels. + to_worker_unbounded_tof: prometheus::HistogramVec, + // Number of elements sent to the worker's unbounded queue. + to_worker_unbounded_sent: prometheus::GaugeVec, + // Number of elements received by the worker's unbounded queue. + to_worker_unbounded_received: prometheus::GaugeVec, +} + +impl Metrics { + /// Get the approval distribution metrics. + pub fn approval_distribution_metrics( + &self, + ) -> polkadot_approval_distribution::metrics::Metrics { + self.0 + .as_ref() + .map(|metrics_inner| metrics_inner.approval_distribution.clone()) + .unwrap_or_default() + } + + /// Get the approval voting metrics. + pub fn approval_voting_metrics(&self) -> polkadot_node_core_approval_voting::Metrics { + self.0 + .as_ref() + .map(|metrics_inner| metrics_inner.approval_voting.clone()) + .unwrap_or_default() + } +} + +impl metrics::Metrics for Metrics { + /// Try to register the metrics. + fn try_register( + registry: &prometheus::Registry, + ) -> std::result::Result { + Ok(Metrics(Some(MetricsInner { + approval_distribution: polkadot_approval_distribution::metrics::Metrics::try_register( + registry, + )?, + approval_voting: polkadot_node_core_approval_voting::Metrics::try_register(registry)?, + to_worker_bounded_tof: prometheus::register( + prometheus::HistogramVec::new( + prometheus::HistogramOpts::new( + "polkadot_approval_voting_parallel_worker_bounded_tof", + "Duration spent in a particular approval voting worker channel from entrance to removal", + ) + .buckets(vec![ + 0.0001, 0.0004, 0.0016, 0.0064, 0.0256, 0.1024, 0.4096, 1.6384, 3.2768, + 4.9152, 6.5536, + ]), + &["worker_name"], + )?, + registry, + )?, + to_worker_bounded_sent: prometheus::register( + prometheus::GaugeVec::::new( + prometheus::Opts::new( + "polkadot_approval_voting_parallel_worker_bounded_sent", + "Number of elements sent to approval voting workers' bounded queues", + ), + &["worker_name"], + )?, + registry, + )?, + to_worker_bounded_received: prometheus::register( + prometheus::GaugeVec::::new( + prometheus::Opts::new( + "polkadot_approval_voting_parallel_worker_bounded_received", + "Number of elements received by approval voting workers' bounded queues", + ), + &["worker_name"], + )?, + registry, + )?, + to_worker_bounded_blocked: prometheus::register( + prometheus::GaugeVec::::new( + prometheus::Opts::new( + "polkadot_approval_voting_parallel_worker_bounded_blocked", + "Number of times approval voting workers blocked while sending messages to a subsystem", + ), + &["worker_name"], + )?, + registry, + )?, + to_worker_unbounded_tof: prometheus::register( + prometheus::HistogramVec::new( + prometheus::HistogramOpts::new( + "polkadot_approval_voting_parallel_worker_unbounded_tof", + "Duration spent in a particular approval voting worker channel from entrance to removal", + ) + .buckets(vec![ + 0.0001, 0.0004, 0.0016, 0.0064, 0.0256, 0.1024, 0.4096, 1.6384, 3.2768, + 4.9152, 6.5536, + ]), + &["worker_name"], + )?, + registry, + )?, + to_worker_unbounded_sent: prometheus::register( + prometheus::GaugeVec::::new( + prometheus::Opts::new( + "polkadot_approval_voting_parallel_worker_unbounded_sent", + "Number of elements sent to approval voting workers' unbounded queues", + ), + &["worker_name"], + )?, + registry, + )?, + to_worker_unbounded_received: prometheus::register( + prometheus::GaugeVec::::new( + prometheus::Opts::new( + "polkadot_approval_voting_parallel_worker_unbounded_received", + "Number of elements received by approval voting workers' unbounded queues", + ), + &["worker_name"], + )?, + registry, + )?, + }))) + } +} + +/// The meters to watch. +#[derive(Clone)] +pub struct Meters { + bounded: Meter, + unbounded: Meter, +} + +impl Meters { + pub fn new(bounded: &Meter, unbounded: &Meter) -> Self { + Self { bounded: bounded.clone(), unbounded: unbounded.clone() } + } +} + +/// A metrics watcher that watches the meters and updates the metrics. +pub struct MetricsWatcher { + to_watch: HashMap, + metrics: Metrics, +} + +impl MetricsWatcher { + /// Create a new metrics watcher. + pub fn new(metrics: Metrics) -> Self { + Self { to_watch: HashMap::new(), metrics } + } + + /// Watch the meters of a worker with this name. + pub fn watch(&mut self, worker_name: String, meters: Meters) { + self.to_watch.insert(worker_name, meters); + } + + /// Collect all the metrics. + pub fn collect_metrics(&self) { + for (name, meter) in &self.to_watch { + let bounded_readouts = meter.bounded.read(); + let unbounded_readouts = meter.unbounded.read(); + if let Some(metrics) = self.metrics.0.as_ref() { + metrics + .to_worker_bounded_sent + .with_label_values(&[name]) + .set(bounded_readouts.sent as u64); + + metrics + .to_worker_bounded_received + .with_label_values(&[name]) + .set(bounded_readouts.received as u64); + + metrics + .to_worker_bounded_blocked + .with_label_values(&[name]) + .set(bounded_readouts.blocked as u64); + + metrics + .to_worker_unbounded_sent + .with_label_values(&[name]) + .set(unbounded_readouts.sent as u64); + + metrics + .to_worker_unbounded_received + .with_label_values(&[name]) + .set(unbounded_readouts.received as u64); + + let hist_bounded = metrics.to_worker_bounded_tof.with_label_values(&[name]); + for tof in bounded_readouts.tof { + hist_bounded.observe(tof.as_f64()); + } + + let hist_unbounded = metrics.to_worker_unbounded_tof.with_label_values(&[name]); + for tof in unbounded_readouts.tof { + hist_unbounded.observe(tof.as_f64()); + } + } + } + } +} diff --git a/polkadot/node/core/approval-voting-parallel/src/tests.rs b/polkadot/node/core/approval-voting-parallel/src/tests.rs new file mode 100644 index 0000000000000000000000000000000000000000..215a707147fcb2de0b61cd05deff980322ce3240 --- /dev/null +++ b/polkadot/node/core/approval-voting-parallel/src/tests.rs @@ -0,0 +1,1178 @@ +// 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 . + +//! The tests for Approval Voting Parallel Subsystem. + +use std::{ + collections::{HashMap, HashSet}, + future::Future, + sync::Arc, + time::Duration, +}; + +use crate::{ + build_worker_handles, metrics::MetricsWatcher, prio_right, run_main_loop, start_workers, + validator_index_for_msg, ApprovalVotingParallelSubsystem, Metrics, WorkProvider, +}; +use assert_matches::assert_matches; +use futures::{channel::oneshot, future, stream::PollNext, StreamExt}; +use itertools::Itertools; +use polkadot_node_core_approval_voting::{ApprovalVotingWorkProvider, Config}; +use polkadot_node_network_protocol::{peer_set::ValidationVersion, ObservedRole, PeerId, View}; +use polkadot_node_primitives::approval::{ + time::SystemClock, + v1::{ + AssignmentCert, AssignmentCertKind, IndirectAssignmentCert, IndirectSignedApprovalVote, + RELAY_VRF_MODULO_CONTEXT, + }, + v2::{ + AssignmentCertKindV2, AssignmentCertV2, CoreBitfield, IndirectAssignmentCertV2, + IndirectSignedApprovalVoteV2, + }, +}; +use polkadot_node_subsystem::{ + messages::{ApprovalDistributionMessage, ApprovalVotingMessage, ApprovalVotingParallelMessage}, + FromOrchestra, +}; +use polkadot_node_subsystem_test_helpers::{mock::new_leaf, TestSubsystemContext}; +use polkadot_overseer::{ActiveLeavesUpdate, OverseerSignal, SpawnGlue, TimeoutExt}; +use polkadot_primitives::{CandidateHash, CoreIndex, Hash, ValidatorIndex}; +use sc_keystore::{Keystore, LocalKeystore}; +use sp_consensus::SyncOracle; +use sp_consensus_babe::{VrfPreOutput, VrfProof, VrfSignature}; +use sp_core::{testing::TaskExecutor, H256}; +use sp_keyring::Sr25519Keyring; +type VirtualOverseer = + polkadot_node_subsystem_test_helpers::TestSubsystemContextHandle; + +const SLOT_DURATION_MILLIS: u64 = 6000; + +pub mod test_constants { + pub(crate) const DATA_COL: u32 = 0; + pub(crate) const NUM_COLUMNS: u32 = 1; +} + +fn fake_assignment_cert(block_hash: Hash, validator: ValidatorIndex) -> IndirectAssignmentCert { + let ctx = schnorrkel::signing_context(RELAY_VRF_MODULO_CONTEXT); + let msg = b"WhenParachains?"; + let mut prng = rand_core::OsRng; + let keypair = schnorrkel::Keypair::generate_with(&mut prng); + let (inout, proof, _) = keypair.vrf_sign(ctx.bytes(msg)); + let preout = inout.to_preout(); + + IndirectAssignmentCert { + block_hash, + validator, + cert: AssignmentCert { + kind: AssignmentCertKind::RelayVRFModulo { sample: 1 }, + vrf: VrfSignature { pre_output: VrfPreOutput(preout), proof: VrfProof(proof) }, + }, + } +} + +fn fake_assignment_cert_v2( + block_hash: Hash, + validator: ValidatorIndex, + core_bitfield: CoreBitfield, +) -> IndirectAssignmentCertV2 { + let ctx = schnorrkel::signing_context(RELAY_VRF_MODULO_CONTEXT); + let msg = b"WhenParachains?"; + let mut prng = rand_core::OsRng; + let keypair = schnorrkel::Keypair::generate_with(&mut prng); + let (inout, proof, _) = keypair.vrf_sign(ctx.bytes(msg)); + let preout = inout.to_preout(); + + IndirectAssignmentCertV2 { + block_hash, + validator, + cert: AssignmentCertV2 { + kind: AssignmentCertKindV2::RelayVRFModuloCompact { core_bitfield }, + vrf: VrfSignature { pre_output: VrfPreOutput(preout), proof: VrfProof(proof) }, + }, + } +} + +/// Creates a meaningless signature +pub fn dummy_signature() -> polkadot_primitives::ValidatorSignature { + sp_core::crypto::UncheckedFrom::unchecked_from([1u8; 64]) +} + +fn build_subsystem( + sync_oracle: Box, +) -> ( + ApprovalVotingParallelSubsystem, + TestSubsystemContext>, + VirtualOverseer, +) { + sp_tracing::init_for_tests(); + + let pool = sp_core::testing::TaskExecutor::new(); + let (context, virtual_overseer) = polkadot_node_subsystem_test_helpers::make_subsystem_context::< + ApprovalVotingParallelMessage, + _, + >(pool.clone()); + + let keystore = LocalKeystore::in_memory(); + let _ = keystore.sr25519_generate_new( + polkadot_primitives::PARACHAIN_KEY_TYPE_ID, + Some(&Sr25519Keyring::Alice.to_seed()), + ); + + let clock = Arc::new(SystemClock {}); + let db = kvdb_memorydb::create(test_constants::NUM_COLUMNS); + let db = polkadot_node_subsystem_util::database::kvdb_impl::DbAdapter::new(db, &[]); + + ( + ApprovalVotingParallelSubsystem::with_config_and_clock( + Config { + col_approval_data: test_constants::DATA_COL, + slot_duration_millis: SLOT_DURATION_MILLIS, + }, + Arc::new(db), + Arc::new(keystore), + sync_oracle, + Metrics::default(), + clock.clone(), + SpawnGlue(pool), + None, + ), + context, + virtual_overseer, + ) +} + +#[derive(Clone)] +struct TestSyncOracle {} + +impl SyncOracle for TestSyncOracle { + fn is_major_syncing(&self) -> bool { + false + } + + fn is_offline(&self) -> bool { + unimplemented!("not used in network bridge") + } +} + +fn test_harness( + num_approval_distro_workers: usize, + prio_right: Clos, + subsystem_gracefully_exits: bool, + test_fn: impl FnOnce( + VirtualOverseer, + WorkProvider, + Vec>, + ) -> T, +) where + T: Future, + Clos: Clone + FnMut(&mut State) -> PollNext, + State: Default, +{ + let (subsystem, context, virtual_overseer) = build_subsystem(Box::new(TestSyncOracle {})); + let mut metrics_watcher = MetricsWatcher::new(subsystem.metrics.clone()); + let channel_size = 5; + + let (to_approval_voting_worker, approval_voting_work_provider) = + build_worker_handles::( + "to_approval_voting_worker".into(), + channel_size, + &mut metrics_watcher, + prio_right.clone(), + ); + + let approval_distribution_channels = { 0..num_approval_distro_workers } + .into_iter() + .map(|worker_index| { + build_worker_handles::( + format!("to_approval_distro/{}", worker_index), + channel_size, + &mut metrics_watcher, + prio_right.clone(), + ) + }) + .collect_vec(); + + let to_approval_distribution_workers = + approval_distribution_channels.iter().map(|(tx, _)| tx.clone()).collect_vec(); + let approval_distribution_work_providers = + approval_distribution_channels.into_iter().map(|(_, rx)| rx).collect_vec(); + + let subsystem = async move { + let result = run_main_loop( + context, + to_approval_voting_worker, + to_approval_distribution_workers, + metrics_watcher, + ) + .await; + + if subsystem_gracefully_exits && result.is_err() { + result + } else { + Ok(()) + } + }; + + let test_fut = test_fn( + virtual_overseer, + approval_voting_work_provider, + approval_distribution_work_providers, + ); + + futures::pin_mut!(test_fut); + futures::pin_mut!(subsystem); + + futures::executor::block_on(future::join( + async move { + let _overseer = test_fut.await; + }, + subsystem, + )) + .1 + .unwrap(); +} + +const TIMEOUT: Duration = Duration::from_millis(2000); + +async fn overseer_signal(overseer: &mut VirtualOverseer, signal: OverseerSignal) { + overseer + .send(FromOrchestra::Signal(signal)) + .timeout(TIMEOUT) + .await + .expect(&format!("{:?} is more than enough for sending signals.", TIMEOUT)); +} + +async fn overseer_message(overseer: &mut VirtualOverseer, msg: ApprovalVotingParallelMessage) { + overseer + .send(FromOrchestra::Communication { msg }) + .timeout(TIMEOUT) + .await + .expect(&format!("{:?} is more than enough for sending signals.", TIMEOUT)); +} + +async fn run_start_workers() { + let (subsystem, mut context, _) = build_subsystem(Box::new(TestSyncOracle {})); + let mut metrics_watcher = MetricsWatcher::new(subsystem.metrics.clone()); + let _workers = start_workers(&mut context, subsystem, &mut metrics_watcher).await.unwrap(); +} + +// Test starting the workers succeeds. +#[test] +fn start_workers_succeeds() { + futures::executor::block_on(run_start_workers()); +} + +// Test main loop forwards messages to the correct worker for all type of messages. +#[test] +fn test_main_loop_forwards_correctly() { + let num_approval_distro_workers = 4; + test_harness( + num_approval_distro_workers, + prio_right, + true, + |mut overseer, mut approval_voting_work_provider, mut rx_approval_distribution_workers| async move { + // 1. Check Signals are correctly forwarded to the workers. + let signal = OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::start_work(new_leaf( + Hash::random(), + 1, + ))); + overseer_signal(&mut overseer, signal.clone()).await; + let approval_voting_receives = approval_voting_work_provider.recv().await.unwrap(); + assert_matches!(approval_voting_receives, FromOrchestra::Signal(_)); + for rx_approval_distribution_worker in rx_approval_distribution_workers.iter_mut() { + let approval_distribution_receives = + rx_approval_distribution_worker.next().await.unwrap(); + assert_matches!(approval_distribution_receives, FromOrchestra::Signal(_)); + } + + let (test_tx, _rx) = oneshot::channel(); + let test_hash = Hash::random(); + let test_block_nr = 2; + overseer_message( + &mut overseer, + ApprovalVotingParallelMessage::ApprovedAncestor(test_hash, test_block_nr, test_tx), + ) + .await; + assert_matches!( + approval_voting_work_provider.recv().await.unwrap(), + FromOrchestra::Communication { + msg: ApprovalVotingMessage::ApprovedAncestor(hash, block_nr, _) + } => { + assert_eq!(hash, test_hash); + assert_eq!(block_nr, test_block_nr); + } + ); + for rx_approval_distribution_worker in rx_approval_distribution_workers.iter_mut() { + assert!(rx_approval_distribution_worker + .next() + .timeout(Duration::from_millis(200)) + .await + .is_none()); + } + + // 2. Check GetApprovalSignaturesForCandidate is correctly forwarded to the workers. + let (test_tx, _rx) = oneshot::channel(); + let test_hash = CandidateHash(Hash::random()); + overseer_message( + &mut overseer, + ApprovalVotingParallelMessage::GetApprovalSignaturesForCandidate( + test_hash, test_tx, + ), + ) + .await; + + assert_matches!( + approval_voting_work_provider.recv().await.unwrap(), + FromOrchestra::Communication { + msg: ApprovalVotingMessage::GetApprovalSignaturesForCandidate(hash, _) + } => { + assert_eq!(hash, test_hash); + } + ); + + for rx_approval_distribution_worker in rx_approval_distribution_workers.iter_mut() { + assert!(rx_approval_distribution_worker + .next() + .timeout(Duration::from_millis(200)) + .await + .is_none()); + } + + // 3. Check NewBlocks is correctly forwarded to the workers. + overseer_message(&mut overseer, ApprovalVotingParallelMessage::NewBlocks(vec![])).await; + for rx_approval_distribution_worker in rx_approval_distribution_workers.iter_mut() { + assert_matches!(rx_approval_distribution_worker.next().await.unwrap(), + FromOrchestra::Communication { + msg: ApprovalDistributionMessage::NewBlocks(blocks) + } => { + assert!(blocks.is_empty()); + } + ); + } + assert!(approval_voting_work_provider + .recv() + .timeout(Duration::from_millis(200)) + .await + .is_none()); + + // 4. Check DistributeAssignment is correctly forwarded to the workers. + let validator_index = ValidatorIndex(17); + let assignment = + fake_assignment_cert_v2(Hash::random(), validator_index, CoreIndex(1).into()); + overseer_message( + &mut overseer, + ApprovalVotingParallelMessage::DistributeAssignment(assignment.clone(), 1.into()), + ) + .await; + + for (index, rx_approval_distribution_worker) in + rx_approval_distribution_workers.iter_mut().enumerate() + { + if index == validator_index.0 as usize % num_approval_distro_workers { + assert_matches!(rx_approval_distribution_worker.next().await.unwrap(), + FromOrchestra::Communication { + msg: ApprovalDistributionMessage::DistributeAssignment(cert, bitfield) + } => { + assert_eq!(cert, assignment); + assert_eq!(bitfield, 1.into()); + } + ); + } else { + assert!(rx_approval_distribution_worker + .next() + .timeout(Duration::from_millis(200)) + .await + .is_none()); + } + } + assert!(approval_voting_work_provider + .recv() + .timeout(Duration::from_millis(200)) + .await + .is_none()); + + // 5. Check DistributeApproval is correctly forwarded to the workers. + let validator_index = ValidatorIndex(26); + let expected_vote = IndirectSignedApprovalVoteV2 { + block_hash: H256::random(), + candidate_indices: 1.into(), + validator: validator_index, + signature: dummy_signature(), + }; + + overseer_message( + &mut overseer, + ApprovalVotingParallelMessage::DistributeApproval(expected_vote.clone()), + ) + .await; + + for (index, rx_approval_distribution_worker) in + rx_approval_distribution_workers.iter_mut().enumerate() + { + if index == validator_index.0 as usize % num_approval_distro_workers { + assert_matches!(rx_approval_distribution_worker.next().await.unwrap(), + FromOrchestra::Communication { + msg: ApprovalDistributionMessage::DistributeApproval(vote) + } => { + assert_eq!(vote, expected_vote); + } + ); + } else { + assert!(rx_approval_distribution_worker + .next() + .timeout(Duration::from_millis(200)) + .await + .is_none()); + } + } + + // 6. Check NetworkBridgeUpdate::PeerMessage is correctly forwarded just to one of the + // workers. + let approvals = vec![ + IndirectSignedApprovalVoteV2 { + block_hash: H256::random(), + candidate_indices: 1.into(), + validator: validator_index, + signature: dummy_signature(), + }, + IndirectSignedApprovalVoteV2 { + block_hash: H256::random(), + candidate_indices: 2.into(), + validator: validator_index, + signature: dummy_signature(), + }, + ]; + let expected_msg = polkadot_node_network_protocol::Versioned::V3( + polkadot_node_network_protocol::v3::ApprovalDistributionMessage::Approvals( + approvals.clone(), + ), + ); + overseer_message( + &mut overseer, + ApprovalVotingParallelMessage::NetworkBridgeUpdate( + polkadot_node_subsystem::messages::NetworkBridgeEvent::PeerMessage( + PeerId::random(), + expected_msg.clone(), + ), + ), + ) + .await; + + for (index, rx_approval_distribution_worker) in + rx_approval_distribution_workers.iter_mut().enumerate() + { + if index == validator_index.0 as usize % num_approval_distro_workers { + assert_matches!(rx_approval_distribution_worker.next().await.unwrap(), + FromOrchestra::Communication { + msg: ApprovalDistributionMessage::NetworkBridgeUpdate( + polkadot_node_subsystem::messages::NetworkBridgeEvent::PeerMessage( + _, + msg, + ), + ) + } => { + assert_eq!(msg, expected_msg); + } + ); + } else { + assert!(rx_approval_distribution_worker + .next() + .timeout(Duration::from_millis(200)) + .await + .is_none()); + } + } + assert!(approval_voting_work_provider + .recv() + .timeout(Duration::from_millis(200)) + .await + .is_none()); + + assert!(approval_voting_work_provider + .recv() + .timeout(Duration::from_millis(200)) + .await + .is_none()); + + // 7. Check NetworkBridgeUpdate::PeerConnected is correctly forwarded to all workers. + let expected_peer_id = PeerId::random(); + overseer_message( + &mut overseer, + ApprovalVotingParallelMessage::NetworkBridgeUpdate( + polkadot_node_subsystem::messages::NetworkBridgeEvent::PeerConnected( + expected_peer_id, + ObservedRole::Authority, + ValidationVersion::V3.into(), + None, + ), + ), + ) + .await; + + for rx_approval_distribution_worker in rx_approval_distribution_workers.iter_mut() { + assert_matches!(rx_approval_distribution_worker.next().await.unwrap(), + FromOrchestra::Communication { + msg: ApprovalDistributionMessage::NetworkBridgeUpdate( + polkadot_node_subsystem::messages::NetworkBridgeEvent::PeerConnected( + peer_id, + role, + version, + authority_id, + ), + ) + } => { + assert_eq!(peer_id, expected_peer_id); + assert_eq!(role, ObservedRole::Authority); + assert_eq!(version, ValidationVersion::V3.into()); + assert_eq!(authority_id, None); + } + ); + } + assert!(approval_voting_work_provider + .recv() + .timeout(Duration::from_millis(200)) + .await + .is_none()); + + // 8. Check ApprovalCheckingLagUpdate is correctly forwarded to all workers. + overseer_message( + &mut overseer, + ApprovalVotingParallelMessage::ApprovalCheckingLagUpdate(7), + ) + .await; + + for rx_approval_distribution_worker in rx_approval_distribution_workers.iter_mut() { + assert_matches!(rx_approval_distribution_worker.next().await.unwrap(), + FromOrchestra::Communication { + msg: ApprovalDistributionMessage::ApprovalCheckingLagUpdate( + lag + ) + } => { + assert_eq!(lag, 7); + } + ); + } + assert!(approval_voting_work_provider + .recv() + .timeout(Duration::from_millis(200)) + .await + .is_none()); + + overseer_signal(&mut overseer, OverseerSignal::Conclude).await; + + overseer + }, + ); +} + +/// Test GetApprovalSignatures correctly gatheres the signatures from all workers. +#[test] +fn test_handle_get_approval_signatures() { + let num_approval_distro_workers = 4; + + test_harness( + num_approval_distro_workers, + prio_right, + true, + |mut overseer, mut approval_voting_work_provider, mut rx_approval_distribution_workers| async move { + let (tx, rx) = oneshot::channel(); + let first_block = Hash::random(); + let second_block = Hash::random(); + let expected_candidates: HashSet<_> = + vec![(first_block, 2), (second_block, 3)].into_iter().collect(); + + overseer_message( + &mut overseer, + ApprovalVotingParallelMessage::GetApprovalSignatures( + expected_candidates.clone(), + tx, + ), + ) + .await; + + assert!(approval_voting_work_provider + .recv() + .timeout(Duration::from_millis(200)) + .await + .is_none()); + let mut all_votes = HashMap::new(); + for (index, rx_approval_distribution_worker) in + rx_approval_distribution_workers.iter_mut().enumerate() + { + assert_matches!(rx_approval_distribution_worker.next().await.unwrap(), + FromOrchestra::Communication { + msg: ApprovalDistributionMessage::GetApprovalSignatures( + candidates, tx + ) + } => { + assert_eq!(candidates, expected_candidates); + let to_send: HashMap<_, _> = {0..10}.into_iter().map(|validator| { + let validator_index = ValidatorIndex(validator as u32 * num_approval_distro_workers as u32 + index as u32); + (validator_index, (first_block, vec![2, 4], dummy_signature())) + }).collect(); + tx.send(to_send.clone()).unwrap(); + all_votes.extend(to_send.clone()); + + } + ); + } + + let received_votes = rx.await.unwrap(); + assert_eq!(received_votes, all_votes); + overseer_signal(&mut overseer, OverseerSignal::Conclude).await; + + overseer + }, + ) +} + +/// Test subsystem exits with error when approval_voting_work_provider exits. +#[test] +fn test_subsystem_exits_with_error_if_approval_voting_worker_errors() { + let num_approval_distro_workers = 4; + + test_harness( + num_approval_distro_workers, + prio_right, + false, + |overseer, approval_voting_work_provider, _rx_approval_distribution_workers| async move { + // Drop the approval_voting_work_provider to simulate an error. + std::mem::drop(approval_voting_work_provider); + + overseer + }, + ) +} + +/// Test subsystem exits with error when approval_distribution_workers exits. +#[test] +fn test_subsystem_exits_with_error_if_approval_distribution_worker_errors() { + let num_approval_distro_workers = 4; + + test_harness( + num_approval_distro_workers, + prio_right, + false, + |overseer, _approval_voting_work_provider, rx_approval_distribution_workers| async move { + // Drop the approval_distribution_workers to simulate an error. + std::mem::drop(rx_approval_distribution_workers.into_iter().next().unwrap()); + overseer + }, + ) +} + +/// Test signals sent before messages are processed in order. +#[test] +fn test_signal_before_message_keeps_receive_order() { + let num_approval_distro_workers = 4; + + test_harness( + num_approval_distro_workers, + prio_right, + true, + |mut overseer, mut approval_voting_work_provider, mut rx_approval_distribution_workers| async move { + let signal = OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::start_work(new_leaf( + Hash::random(), + 1, + ))); + overseer_signal(&mut overseer, signal.clone()).await; + + let validator_index = ValidatorIndex(17); + let assignment = + fake_assignment_cert_v2(Hash::random(), validator_index, CoreIndex(1).into()); + overseer_message( + &mut overseer, + ApprovalVotingParallelMessage::DistributeAssignment(assignment.clone(), 1.into()), + ) + .await; + + let approval_voting_receives = approval_voting_work_provider.recv().await.unwrap(); + assert_matches!(approval_voting_receives, FromOrchestra::Signal(_)); + let rx_approval_distribution_worker = rx_approval_distribution_workers + .get_mut(validator_index.0 as usize % num_approval_distro_workers) + .unwrap(); + let approval_distribution_receives = + rx_approval_distribution_worker.next().await.unwrap(); + assert_matches!(approval_distribution_receives, FromOrchestra::Signal(_)); + assert_matches!( + rx_approval_distribution_worker.next().await.unwrap(), + FromOrchestra::Communication { + msg: ApprovalDistributionMessage::DistributeAssignment(_, _) + } + ); + + overseer_signal(&mut overseer, OverseerSignal::Conclude).await; + overseer + }, + ) +} + +/// Test signals sent after messages are processed with the highest priority. +#[test] +fn test_signal_is_prioritized_when_unread_messages_in_the_queue() { + let num_approval_distro_workers = 4; + + test_harness( + num_approval_distro_workers, + prio_right, + true, + |mut overseer, mut approval_voting_work_provider, mut rx_approval_distribution_workers| async move { + let validator_index = ValidatorIndex(17); + let assignment = + fake_assignment_cert_v2(Hash::random(), validator_index, CoreIndex(1).into()); + overseer_message( + &mut overseer, + ApprovalVotingParallelMessage::DistributeAssignment(assignment.clone(), 1.into()), + ) + .await; + + let signal = OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::start_work(new_leaf( + Hash::random(), + 1, + ))); + overseer_signal(&mut overseer, signal.clone()).await; + + let approval_voting_receives = approval_voting_work_provider.recv().await.unwrap(); + assert_matches!(approval_voting_receives, FromOrchestra::Signal(_)); + let rx_approval_distribution_worker = rx_approval_distribution_workers + .get_mut(validator_index.0 as usize % num_approval_distro_workers) + .unwrap(); + let approval_distribution_receives = + rx_approval_distribution_worker.next().await.unwrap(); + assert_matches!(approval_distribution_receives, FromOrchestra::Signal(_)); + assert_matches!( + rx_approval_distribution_worker.next().await.unwrap(), + FromOrchestra::Communication { + msg: ApprovalDistributionMessage::DistributeAssignment(_, _) + } + ); + + overseer_signal(&mut overseer, OverseerSignal::Conclude).await; + overseer + }, + ) +} + +/// Test peer view updates have higher priority than normal messages. +#[test] +fn test_peer_view_is_prioritized_when_unread_messages_in_the_queue() { + let num_approval_distro_workers = 4; + + test_harness( + num_approval_distro_workers, + prio_right, + true, + |mut overseer, mut approval_voting_work_provider, mut rx_approval_distribution_workers| async move { + let validator_index = ValidatorIndex(17); + let approvals = vec![ + IndirectSignedApprovalVoteV2 { + block_hash: H256::random(), + candidate_indices: 1.into(), + validator: validator_index, + signature: dummy_signature(), + }, + IndirectSignedApprovalVoteV2 { + block_hash: H256::random(), + candidate_indices: 2.into(), + validator: validator_index, + signature: dummy_signature(), + }, + ]; + let expected_msg = polkadot_node_network_protocol::Versioned::V3( + polkadot_node_network_protocol::v3::ApprovalDistributionMessage::Approvals( + approvals.clone(), + ), + ); + overseer_message( + &mut overseer, + ApprovalVotingParallelMessage::NetworkBridgeUpdate( + polkadot_node_subsystem::messages::NetworkBridgeEvent::PeerMessage( + PeerId::random(), + expected_msg.clone(), + ), + ), + ) + .await; + + overseer_message( + &mut overseer, + ApprovalVotingParallelMessage::NetworkBridgeUpdate( + polkadot_node_subsystem::messages::NetworkBridgeEvent::PeerViewChange( + PeerId::random(), + View::default(), + ), + ), + ) + .await; + + for (index, rx_approval_distribution_worker) in + rx_approval_distribution_workers.iter_mut().enumerate() + { + assert_matches!(rx_approval_distribution_worker.next().await.unwrap(), + FromOrchestra::Communication { + msg: ApprovalDistributionMessage::NetworkBridgeUpdate( + polkadot_node_subsystem::messages::NetworkBridgeEvent::PeerViewChange( + _, + _, + ), + ) + } => { + } + ); + if index == validator_index.0 as usize % num_approval_distro_workers { + assert_matches!(rx_approval_distribution_worker.next().await.unwrap(), + FromOrchestra::Communication { + msg: ApprovalDistributionMessage::NetworkBridgeUpdate( + polkadot_node_subsystem::messages::NetworkBridgeEvent::PeerMessage( + _, + msg, + ), + ) + } => { + assert_eq!(msg, expected_msg); + } + ); + } else { + assert!(rx_approval_distribution_worker + .next() + .timeout(Duration::from_millis(200)) + .await + .is_none()); + } + } + + assert!(approval_voting_work_provider + .recv() + .timeout(Duration::from_millis(200)) + .await + .is_none()); + + overseer_signal(&mut overseer, OverseerSignal::Conclude).await; + overseer + }, + ) +} + +// Test validator_index_for_msg with empty messages. +#[test] +fn test_validator_index_with_empty_message() { + let result = validator_index_for_msg(polkadot_node_network_protocol::Versioned::V1( + polkadot_node_network_protocol::v1::ApprovalDistributionMessage::Assignments(vec![]), + )); + + assert_eq!(result, (None, Some(vec![]))); + + let result = validator_index_for_msg(polkadot_node_network_protocol::Versioned::V2( + polkadot_node_network_protocol::v2::ApprovalDistributionMessage::Assignments(vec![]), + )); + + assert_eq!(result, (None, Some(vec![]))); + + let result = validator_index_for_msg(polkadot_node_network_protocol::Versioned::V3( + polkadot_node_network_protocol::v3::ApprovalDistributionMessage::Assignments(vec![]), + )); + + assert_eq!(result, (None, Some(vec![]))); + + let result = validator_index_for_msg(polkadot_node_network_protocol::Versioned::V1( + polkadot_node_network_protocol::v1::ApprovalDistributionMessage::Approvals(vec![]), + )); + + assert_eq!(result, (None, Some(vec![]))); + + let result = validator_index_for_msg(polkadot_node_network_protocol::Versioned::V2( + polkadot_node_network_protocol::v2::ApprovalDistributionMessage::Approvals(vec![]), + )); + + assert_eq!(result, (None, Some(vec![]))); + + let result = validator_index_for_msg(polkadot_node_network_protocol::Versioned::V3( + polkadot_node_network_protocol::v3::ApprovalDistributionMessage::Approvals(vec![]), + )); + + assert_eq!(result, (None, Some(vec![]))); +} + +// Test validator_index_for_msg when all the messages are originating from the same validator. +#[test] +fn test_validator_index_with_all_messages_from_the_same_validator() { + let validator_index = ValidatorIndex(3); + let v1_assignment = polkadot_node_network_protocol::Versioned::V1( + polkadot_node_network_protocol::v1::ApprovalDistributionMessage::Assignments(vec![ + (fake_assignment_cert(H256::random(), validator_index), 1), + (fake_assignment_cert(H256::random(), validator_index), 3), + ]), + ); + let result = validator_index_for_msg(v1_assignment.clone()); + + assert_eq!(result, (Some((validator_index, v1_assignment)), None)); + + let v1_approval = polkadot_node_network_protocol::Versioned::V1( + polkadot_node_network_protocol::v1::ApprovalDistributionMessage::Approvals(vec![ + IndirectSignedApprovalVote { + block_hash: H256::random(), + candidate_index: 1, + validator: validator_index, + signature: dummy_signature(), + }, + IndirectSignedApprovalVote { + block_hash: H256::random(), + candidate_index: 1, + validator: validator_index, + signature: dummy_signature(), + }, + ]), + ); + let result = validator_index_for_msg(v1_approval.clone()); + + assert_eq!(result, (Some((validator_index, v1_approval)), None)); + + let validator_index = ValidatorIndex(3); + let v2_assignment = polkadot_node_network_protocol::Versioned::V2( + polkadot_node_network_protocol::v2::ApprovalDistributionMessage::Assignments(vec![ + (fake_assignment_cert(H256::random(), validator_index), 1), + (fake_assignment_cert(H256::random(), validator_index), 3), + ]), + ); + let result = validator_index_for_msg(v2_assignment.clone()); + + assert_eq!(result, (Some((validator_index, v2_assignment)), None)); + + let v2_approval = polkadot_node_network_protocol::Versioned::V2( + polkadot_node_network_protocol::v2::ApprovalDistributionMessage::Approvals(vec![ + IndirectSignedApprovalVote { + block_hash: H256::random(), + candidate_index: 1, + validator: validator_index, + signature: dummy_signature(), + }, + IndirectSignedApprovalVote { + block_hash: H256::random(), + candidate_index: 1, + validator: validator_index, + signature: dummy_signature(), + }, + ]), + ); + let result = validator_index_for_msg(v2_approval.clone()); + + assert_eq!(result, (Some((validator_index, v2_approval)), None)); + + let validator_index = ValidatorIndex(3); + let v3_assignment = polkadot_node_network_protocol::Versioned::V3( + polkadot_node_network_protocol::v3::ApprovalDistributionMessage::Assignments(vec![ + ( + fake_assignment_cert_v2(H256::random(), validator_index, CoreIndex(1).into()), + 1.into(), + ), + ( + fake_assignment_cert_v2(H256::random(), validator_index, CoreIndex(3).into()), + 3.into(), + ), + ]), + ); + let result = validator_index_for_msg(v3_assignment.clone()); + + assert_eq!(result, (Some((validator_index, v3_assignment)), None)); + + let v3_approval = polkadot_node_network_protocol::Versioned::V3( + polkadot_node_network_protocol::v3::ApprovalDistributionMessage::Approvals(vec![ + IndirectSignedApprovalVoteV2 { + block_hash: H256::random(), + candidate_indices: 1.into(), + validator: validator_index, + signature: dummy_signature(), + }, + IndirectSignedApprovalVoteV2 { + block_hash: H256::random(), + candidate_indices: 1.into(), + validator: validator_index, + signature: dummy_signature(), + }, + ]), + ); + let result = validator_index_for_msg(v3_approval.clone()); + + assert_eq!(result, (Some((validator_index, v3_approval)), None)); +} + +// Test validator_index_for_msg when all the messages are originating from different validators, +// so the function should split them by validator index, so we can forward them separately to the +// worker they are assigned to. +#[test] +fn test_validator_index_with_messages_from_different_validators() { + let first_validator_index = ValidatorIndex(3); + let second_validator_index = ValidatorIndex(4); + let assignments = vec![ + (fake_assignment_cert(H256::random(), first_validator_index), 1), + (fake_assignment_cert(H256::random(), second_validator_index), 3), + ]; + let v1_assignment = polkadot_node_network_protocol::Versioned::V1( + polkadot_node_network_protocol::v1::ApprovalDistributionMessage::Assignments( + assignments.clone(), + ), + ); + let result = validator_index_for_msg(v1_assignment.clone()); + + assert_matches!(result, (None, Some(_))); + let messsages_split_by_validator = result.1.unwrap(); + assert_eq!(messsages_split_by_validator.len(), assignments.len()); + for (index, (validator_index, message)) in messsages_split_by_validator.into_iter().enumerate() + { + assert_eq!(validator_index, assignments[index].0.validator); + assert_eq!( + message, + polkadot_node_network_protocol::Versioned::V1( + polkadot_node_network_protocol::v1::ApprovalDistributionMessage::Assignments( + assignments.get(index).into_iter().cloned().collect(), + ), + ) + ); + } + + let v2_assignment = polkadot_node_network_protocol::Versioned::V2( + polkadot_node_network_protocol::v2::ApprovalDistributionMessage::Assignments( + assignments.clone(), + ), + ); + let result = validator_index_for_msg(v2_assignment.clone()); + + assert_matches!(result, (None, Some(_))); + let messsages_split_by_validator = result.1.unwrap(); + assert_eq!(messsages_split_by_validator.len(), assignments.len()); + for (index, (validator_index, message)) in messsages_split_by_validator.into_iter().enumerate() + { + assert_eq!(validator_index, assignments[index].0.validator); + assert_eq!( + message, + polkadot_node_network_protocol::Versioned::V2( + polkadot_node_network_protocol::v2::ApprovalDistributionMessage::Assignments( + assignments.get(index).into_iter().cloned().collect(), + ), + ) + ); + } + + let first_validator_index = ValidatorIndex(3); + let second_validator_index = ValidatorIndex(4); + let v2_assignments = vec![ + ( + fake_assignment_cert_v2(H256::random(), first_validator_index, CoreIndex(1).into()), + 1.into(), + ), + ( + fake_assignment_cert_v2(H256::random(), second_validator_index, CoreIndex(3).into()), + 3.into(), + ), + ]; + + let approvals = vec![ + IndirectSignedApprovalVote { + block_hash: H256::random(), + candidate_index: 1, + validator: first_validator_index, + signature: dummy_signature(), + }, + IndirectSignedApprovalVote { + block_hash: H256::random(), + candidate_index: 2, + validator: second_validator_index, + signature: dummy_signature(), + }, + ]; + let v2_approvals = polkadot_node_network_protocol::Versioned::V2( + polkadot_node_network_protocol::v2::ApprovalDistributionMessage::Approvals( + approvals.clone(), + ), + ); + let result = validator_index_for_msg(v2_approvals.clone()); + + assert_matches!(result, (None, Some(_))); + let messsages_split_by_validator = result.1.unwrap(); + assert_eq!(messsages_split_by_validator.len(), approvals.len()); + for (index, (validator_index, message)) in messsages_split_by_validator.into_iter().enumerate() + { + assert_eq!(validator_index, approvals[index].validator); + assert_eq!( + message, + polkadot_node_network_protocol::Versioned::V2( + polkadot_node_network_protocol::v2::ApprovalDistributionMessage::Approvals( + approvals.get(index).into_iter().cloned().collect(), + ), + ) + ); + } + + let v3_assignment = polkadot_node_network_protocol::Versioned::V3( + polkadot_node_network_protocol::v3::ApprovalDistributionMessage::Assignments( + v2_assignments.clone(), + ), + ); + let result = validator_index_for_msg(v3_assignment.clone()); + + assert_matches!(result, (None, Some(_))); + let messsages_split_by_validator = result.1.unwrap(); + assert_eq!(messsages_split_by_validator.len(), v2_assignments.len()); + for (index, (validator_index, message)) in messsages_split_by_validator.into_iter().enumerate() + { + assert_eq!(validator_index, v2_assignments[index].0.validator); + assert_eq!( + message, + polkadot_node_network_protocol::Versioned::V3( + polkadot_node_network_protocol::v3::ApprovalDistributionMessage::Assignments( + v2_assignments.get(index).into_iter().cloned().collect(), + ), + ) + ); + } + + let approvals = vec![ + IndirectSignedApprovalVoteV2 { + block_hash: H256::random(), + candidate_indices: 1.into(), + validator: first_validator_index, + signature: dummy_signature(), + }, + IndirectSignedApprovalVoteV2 { + block_hash: H256::random(), + candidate_indices: 2.into(), + validator: second_validator_index, + signature: dummy_signature(), + }, + ]; + let v3_approvals = polkadot_node_network_protocol::Versioned::V3( + polkadot_node_network_protocol::v3::ApprovalDistributionMessage::Approvals( + approvals.clone(), + ), + ); + let result = validator_index_for_msg(v3_approvals.clone()); + + assert_matches!(result, (None, Some(_))); + let messsages_split_by_validator = result.1.unwrap(); + assert_eq!(messsages_split_by_validator.len(), approvals.len()); + for (index, (validator_index, message)) in messsages_split_by_validator.into_iter().enumerate() + { + assert_eq!(validator_index, approvals[index].validator); + assert_eq!( + message, + polkadot_node_network_protocol::Versioned::V3( + polkadot_node_network_protocol::v3::ApprovalDistributionMessage::Approvals( + approvals.get(index).into_iter().cloned().collect(), + ), + ) + ); + } +} diff --git a/polkadot/node/core/approval-voting/Cargo.toml b/polkadot/node/core/approval-voting/Cargo.toml index bc0187bf49228a2bf5e691c4fddce89ede9dc225..2c3db866566ce882afc0874e5f8a20dd3148dbbc 100644 --- a/polkadot/node/core/approval-voting/Cargo.toml +++ b/polkadot/node/core/approval-voting/Cargo.toml @@ -22,13 +22,13 @@ kvdb = { workspace = true } derive_more = { workspace = true, default-features = true } thiserror = { workspace = true } itertools = { workspace = true } +async-trait = { workspace = true } polkadot-node-subsystem = { workspace = true, default-features = true } polkadot-node-subsystem-util = { workspace = true, default-features = true } polkadot-overseer = { workspace = true, default-features = true } polkadot-primitives = { workspace = true, default-features = true } polkadot-node-primitives = { workspace = true, default-features = true } -polkadot-node-jaeger = { workspace = true, default-features = true } sc-keystore = { workspace = true } sp-consensus = { workspace = true } diff --git a/polkadot/node/core/approval-voting/benches/approval-voting-regression-bench.rs b/polkadot/node/core/approval-voting/benches/approval-voting-regression-bench.rs index 41418bcc511f87a4f321d02c0d701dfe5775ba25..e202d1ee229d405627478265af1b1eb77462b8a9 100644 --- a/polkadot/node/core/approval-voting/benches/approval-voting-regression-bench.rs +++ b/polkadot/node/core/approval-voting/benches/approval-voting-regression-bench.rs @@ -53,6 +53,7 @@ fn main() -> Result<(), String> { stop_when_approved: false, workdir_prefix: "/tmp".to_string(), num_no_shows_per_candidate: 0, + approval_voting_parallel_enabled: true, }; println!("Benchmarking..."); @@ -82,8 +83,9 @@ fn main() -> Result<(), String> { ("Sent to peers", 63995.2200, 0.01), ])); messages.extend(average_usage.check_cpu_usage(&[ - ("approval-distribution", 6.3912, 0.1), - ("approval-voting", 10.0578, 0.1), + ("approval-distribution", 0.1, 0.1), + ("approval-voting", 0.1, 0.1), + ("approval-voting-parallel", 18.0758, 0.1), ])); if messages.is_empty() { diff --git a/polkadot/node/core/approval-voting/src/approval_checking.rs b/polkadot/node/core/approval-voting/src/approval_checking.rs index 96eb25626de8c167255027e1320b079eb8f60e2a..3774edc69981b34a0dd14c08d57e8a90a704d88a 100644 --- a/polkadot/node/core/approval-voting/src/approval_checking.rs +++ b/polkadot/node/core/approval-voting/src/approval_checking.rs @@ -22,9 +22,9 @@ use polkadot_primitives::ValidatorIndex; use crate::{ persisted_entries::{ApprovalEntry, CandidateEntry, TrancheEntry}, - time::Tick, MAX_RECORDED_NO_SHOW_VALIDATORS_PER_CANDIDATE, }; +use polkadot_node_primitives::approval::time::Tick; /// Result of counting the necessary tranches needed for approving a block. #[derive(Debug, PartialEq, Clone)] @@ -1195,9 +1195,9 @@ mod tests { struct NoShowTest { assignments: Vec<(ValidatorIndex, Tick)>, approvals: Vec, - clock_drift: crate::time::Tick, - no_show_duration: crate::time::Tick, - drifted_tick_now: crate::time::Tick, + clock_drift: Tick, + no_show_duration: Tick, + drifted_tick_now: Tick, exp_no_shows: usize, exp_next_no_show: Option, } diff --git a/polkadot/node/core/approval-voting/src/criteria.rs b/polkadot/node/core/approval-voting/src/criteria.rs index fb9d281e43bc3d0ed866ed0c2114ee64345fbdc9..669b6001538e8e75131d7f6aab0dbe73a0b6dd84 100644 --- a/polkadot/node/core/approval-voting/src/criteria.rs +++ b/polkadot/node/core/approval-voting/src/criteria.rs @@ -16,8 +16,11 @@ //! Assignment criteria VRF generation and checking. -use codec::{Decode, Encode}; +use codec::Encode; use itertools::Itertools; +pub use polkadot_node_primitives::approval::criteria::{ + AssignmentCriteria, Config, InvalidAssignment, InvalidAssignmentReason, OurAssignment, +}; use polkadot_node_primitives::approval::{ self as approval_types, v1::{AssignmentCert, AssignmentCertKind, DelayTranche, RelayVRFStory}, @@ -25,9 +28,9 @@ use polkadot_node_primitives::approval::{ AssignmentCertKindV2, AssignmentCertV2, CoreBitfield, VrfPreOutput, VrfProof, VrfSignature, }, }; + use polkadot_primitives::{ - AssignmentId, AssignmentPair, CandidateHash, CoreIndex, GroupIndex, IndexedVec, SessionInfo, - ValidatorIndex, + AssignmentPair, CandidateHash, CoreIndex, GroupIndex, IndexedVec, ValidatorIndex, }; use rand::{seq::SliceRandom, SeedableRng}; use rand_chacha::ChaCha20Rng; @@ -44,56 +47,19 @@ use std::{ use super::LOG_TARGET; -/// Details pertaining to our assignment on a block. -#[derive(Debug, Clone, Encode, Decode, PartialEq)] -pub struct OurAssignment { - cert: AssignmentCertV2, - tranche: DelayTranche, - validator_index: ValidatorIndex, - // Whether the assignment has been triggered already. - triggered: bool, -} - -impl OurAssignment { - pub fn cert(&self) -> &AssignmentCertV2 { - &self.cert - } - - pub fn tranche(&self) -> DelayTranche { - self.tranche - } - - pub(crate) fn validator_index(&self) -> ValidatorIndex { - self.validator_index - } - - pub(crate) fn triggered(&self) -> bool { - self.triggered - } - - pub(crate) fn mark_triggered(&mut self) { - self.triggered = true; - } -} - impl From for OurAssignment { fn from(entry: crate::approval_db::v2::OurAssignment) -> Self { - OurAssignment { - cert: entry.cert, - tranche: entry.tranche, - validator_index: entry.validator_index, - triggered: entry.triggered, - } + OurAssignment::new(entry.cert, entry.tranche, entry.validator_index, entry.triggered) } } impl From for crate::approval_db::v2::OurAssignment { fn from(entry: OurAssignment) -> Self { Self { - cert: entry.cert, - tranche: entry.tranche, - validator_index: entry.validator_index, - triggered: entry.triggered, + tranche: entry.tranche(), + validator_index: entry.validator_index(), + triggered: entry.triggered(), + cert: entry.into_cert(), } } } @@ -223,60 +189,7 @@ fn assigned_core_transcript(core_index: CoreIndex) -> Transcript { t } -/// Information about the world assignments are being produced in. -#[derive(Clone, Debug)] -pub struct Config { - /// The assignment public keys for validators. - assignment_keys: Vec, - /// The groups of validators assigned to each core. - validator_groups: IndexedVec>, - /// The number of availability cores used by the protocol during this session. - n_cores: u32, - /// The zeroth delay tranche width. - zeroth_delay_tranche_width: u32, - /// The number of samples we do of `relay_vrf_modulo`. - relay_vrf_modulo_samples: u32, - /// The number of delay tranches in total. - n_delay_tranches: u32, -} - -impl<'a> From<&'a SessionInfo> for Config { - fn from(s: &'a SessionInfo) -> Self { - Config { - assignment_keys: s.assignment_keys.clone(), - validator_groups: s.validator_groups.clone(), - n_cores: s.n_cores, - zeroth_delay_tranche_width: s.zeroth_delay_tranche_width, - relay_vrf_modulo_samples: s.relay_vrf_modulo_samples, - n_delay_tranches: s.n_delay_tranches, - } - } -} - -/// A trait for producing and checking assignments. Used to mock. -pub(crate) trait AssignmentCriteria { - fn compute_assignments( - &self, - keystore: &LocalKeystore, - relay_vrf_story: RelayVRFStory, - config: &Config, - leaving_cores: Vec<(CandidateHash, CoreIndex, GroupIndex)>, - enable_v2_assignments: bool, - ) -> HashMap; - - fn check_assignment_cert( - &self, - claimed_core_bitfield: CoreBitfield, - validator_index: ValidatorIndex, - config: &Config, - relay_vrf_story: RelayVRFStory, - assignment: &AssignmentCertV2, - // Backing groups for each "leaving core". - backing_groups: Vec, - ) -> Result; -} - -pub(crate) struct RealAssignmentCriteria; +pub struct RealAssignmentCriteria; impl AssignmentCriteria for RealAssignmentCriteria { fn compute_assignments( @@ -469,12 +382,12 @@ fn compute_relay_vrf_modulo_assignments_v1( }; // All assignments of type RelayVRFModulo have tranche 0. - assignments.entry(core).or_insert(OurAssignment { - cert: cert.into(), - tranche: 0, + assignments.entry(core).or_insert(OurAssignment::new( + cert.into(), + 0, validator_index, - triggered: false, - }); + false, + )); } } } @@ -549,7 +462,7 @@ fn compute_relay_vrf_modulo_assignments_v2( }; // All assignments of type RelayVRFModulo have tranche 0. - OurAssignment { cert, tranche: 0, validator_index, triggered: false } + OurAssignment::new(cert, 0, validator_index, false) }) { for core_index in assigned_cores { assignments.insert(core_index, assignment.clone()); @@ -583,7 +496,7 @@ fn compute_relay_vrf_delay_assignments( }, }; - let our_assignment = OurAssignment { cert, tranche, validator_index, triggered: false }; + let our_assignment = OurAssignment::new(cert, tranche, validator_index, false); let used = match assignments.entry(core) { Entry::Vacant(e) => { @@ -591,7 +504,7 @@ fn compute_relay_vrf_delay_assignments( true }, Entry::Occupied(mut e) => - if e.get().tranche > our_assignment.tranche { + if e.get().tranche() > our_assignment.tranche() { e.insert(our_assignment); true } else { @@ -612,35 +525,6 @@ fn compute_relay_vrf_delay_assignments( } } -/// Assignment invalid. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct InvalidAssignment(pub(crate) InvalidAssignmentReason); - -impl std::fmt::Display for InvalidAssignment { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "Invalid Assignment: {:?}", self.0) - } -} - -impl std::error::Error for InvalidAssignment {} - -/// Failure conditions when checking an assignment cert. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) enum InvalidAssignmentReason { - ValidatorIndexOutOfBounds, - SampleOutOfBounds, - CoreIndexOutOfBounds, - InvalidAssignmentKey, - IsInBackingGroup, - VRFModuloCoreIndexMismatch, - VRFModuloOutputMismatch, - VRFDelayCoreIndexMismatch, - VRFDelayOutputMismatch, - InvalidArguments, - /// Assignment vrf check resulted in 0 assigned cores. - NullAssignment, -} - /// Checks the crypto of an assignment cert. Failure conditions: /// * Validator index out of bounds /// * VRF signature check fails @@ -820,13 +704,13 @@ fn is_in_backing_group( /// Migration helpers. impl From for OurAssignment { fn from(value: crate::approval_db::v1::OurAssignment) -> Self { - Self { - cert: value.cert.into(), - tranche: value.tranche, - validator_index: value.validator_index, + Self::new( + value.cert.into(), + value.tranche, + value.validator_index, // Whether the assignment has been triggered already. - triggered: value.triggered, - } + value.triggered, + ) } } @@ -834,7 +718,7 @@ impl From for OurAssignment { mod tests { use super::*; use crate::import::tests::garbage_vrf_signature; - use polkadot_primitives::{Hash, ASSIGNMENT_KEY_TYPE_ID}; + use polkadot_primitives::{AssignmentId, Hash, ASSIGNMENT_KEY_TYPE_ID}; use sp_application_crypto::sr25519; use sp_core::crypto::Pair as PairT; use sp_keyring::sr25519::Keyring as Sr25519Keyring; @@ -1053,7 +937,7 @@ mod tests { let mut counted = 0; for (core, assignment) in assignments { - let cores = match assignment.cert.kind.clone() { + let cores = match assignment.cert().kind.clone() { AssignmentCertKindV2::RelayVRFModuloCompact { core_bitfield } => core_bitfield, AssignmentCertKindV2::RelayVRFModulo { sample: _ } => core.into(), AssignmentCertKindV2::RelayVRFDelay { core_index } => core_index.into(), @@ -1062,7 +946,7 @@ mod tests { let mut mutated = MutatedAssignment { cores: cores.clone(), groups: cores.iter_ones().map(|core| group_for_core(core)).collect(), - cert: assignment.cert, + cert: assignment.into_cert(), own_group: GroupIndex(0), val_index: ValidatorIndex(0), config: config.clone(), diff --git a/polkadot/node/core/approval-voting/src/import.rs b/polkadot/node/core/approval-voting/src/import.rs index 3ddef1e01c45b99ce797d64b78f2e2c88993471f..e50a2f91148990b65390a8dc595391135f8940c9 100644 --- a/polkadot/node/core/approval-voting/src/import.rs +++ b/polkadot/node/core/approval-voting/src/import.rs @@ -28,7 +28,6 @@ //! //! We maintain a rolling window of session indices. This starts as empty -use polkadot_node_jaeger as jaeger; use polkadot_node_primitives::{ approval::{ self as approval_types, @@ -44,6 +43,7 @@ use polkadot_node_subsystem::{ overseer, RuntimeApiError, SubsystemError, SubsystemResult, }; use polkadot_node_subsystem_util::{determine_new_blocks, runtime::RuntimeInfo}; +use polkadot_overseer::SubsystemSender; use polkadot_primitives::{ node_features, BlockNumber, CandidateEvent, CandidateHash, CandidateReceipt, ConsensusLog, CoreIndex, GroupIndex, Hash, Header, SessionIndex, @@ -62,9 +62,10 @@ use crate::{ criteria::{AssignmentCriteria, OurAssignment}, get_extended_session_info, get_session_info, persisted_entries::CandidateEntry, - time::{slot_number_to_tick, Tick}, }; +use polkadot_node_primitives::approval::time::{slot_number_to_tick, Tick}; + use super::{State, LOG_TARGET}; #[derive(Debug)] @@ -110,8 +111,8 @@ enum ImportedBlockInfoError { /// Computes information about the imported block. Returns an error if the info couldn't be /// extracted. #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn imported_block_info( - ctx: &mut Context, +async fn imported_block_info>( + sender: &mut Sender, env: ImportedBlockInfoEnv<'_>, block_hash: Hash, block_header: &Header, @@ -123,11 +124,12 @@ async fn imported_block_info( // fetch candidates let included_candidates: Vec<_> = { let (c_tx, c_rx) = oneshot::channel(); - ctx.send_message(RuntimeApiMessage::Request( - block_hash, - RuntimeApiRequest::CandidateEvents(c_tx), - )) - .await; + sender + .send_message(RuntimeApiMessage::Request( + block_hash, + RuntimeApiRequest::CandidateEvents(c_tx), + )) + .await; let events: Vec = match c_rx.await { Ok(Ok(events)) => events, @@ -150,11 +152,12 @@ async fn imported_block_info( // short, that shouldn't happen. let session_index = { let (s_tx, s_rx) = oneshot::channel(); - ctx.send_message(RuntimeApiMessage::Request( - block_header.parent_hash, - RuntimeApiRequest::SessionIndexForChild(s_tx), - )) - .await; + sender + .send_message(RuntimeApiMessage::Request( + block_header.parent_hash, + RuntimeApiRequest::SessionIndexForChild(s_tx), + )) + .await; let session_index = match s_rx.await { Ok(Ok(s)) => s, @@ -200,11 +203,12 @@ async fn imported_block_info( // by one block. This gives us the opposite invariant for sessions - the parent block's // post-state gives us the canonical information about the session index for any of its // children, regardless of which slot number they might be produced at. - ctx.send_message(RuntimeApiMessage::Request( - block_hash, - RuntimeApiRequest::CurrentBabeEpoch(s_tx), - )) - .await; + sender + .send_message(RuntimeApiMessage::Request( + block_hash, + RuntimeApiRequest::CurrentBabeEpoch(s_tx), + )) + .await; match s_rx.await { Ok(Ok(s)) => s, @@ -215,7 +219,7 @@ async fn imported_block_info( }; let extended_session_info = - get_extended_session_info(env.runtime_info, ctx.sender(), block_hash, session_index).await; + get_extended_session_info(env.runtime_info, sender, block_hash, session_index).await; let enable_v2_assignments = extended_session_info.map_or(false, |extended_session_info| { *extended_session_info .node_features @@ -224,7 +228,7 @@ async fn imported_block_info( .unwrap_or(&false) }); - let session_info = get_session_info(env.runtime_info, ctx.sender(), block_hash, session_index) + let session_info = get_session_info(env.runtime_info, sender, block_hash, session_index) .await .ok_or(ImportedBlockInfoError::SessionInfoUnavailable)?; @@ -315,7 +319,6 @@ pub struct BlockImportedCandidates { pub block_hash: Hash, pub block_number: BlockNumber, pub block_tick: Tick, - pub no_show_duration: Tick, pub imported_candidates: Vec<(CandidateHash, CandidateEntry)>, } @@ -328,9 +331,15 @@ pub struct BlockImportedCandidates { /// * and return information about all candidates imported under each block. /// /// It is the responsibility of the caller to schedule wakeups for each block. -#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -pub(crate) async fn handle_new_head( - ctx: &mut Context, +pub(crate) async fn handle_new_head< + Sender: SubsystemSender + + SubsystemSender + + SubsystemSender, + AVSender: SubsystemSender, + B: Backend, +>( + sender: &mut Sender, + approval_voting_sender: &mut AVSender, state: &State, db: &mut OverlayedBackend<'_, B>, session_info_provider: &mut RuntimeInfo, @@ -338,17 +347,10 @@ pub(crate) async fn handle_new_head( finalized_number: &Option, ) -> SubsystemResult> { const MAX_HEADS_LOOK_BACK: BlockNumber = MAX_FINALITY_LAG; - let _handle_new_head_span = state - .spans - .get(&head) - .map(|span| span.child("handle-new-head")) - .unwrap_or_else(|| jaeger::Span::new(head, "handle-new-head")) - .with_string_tag("head", format!("{:?}", head)) - .with_stage(jaeger::Stage::ApprovalChecking); let header = { let (h_tx, h_rx) = oneshot::channel(); - ctx.send_message(ChainApiMessage::BlockHeader(head, h_tx)).await; + sender.send_message(ChainApiMessage::BlockHeader(head, h_tx)).await; match h_rx.await? { Err(e) => { gum::debug!( @@ -374,7 +376,7 @@ pub(crate) async fn handle_new_head( let lower_bound_number = finalized_number.unwrap_or(lower_bound_number).max(lower_bound_number); let new_blocks = determine_new_blocks( - ctx.sender(), + sender, |h| db.load_block_entry(h).map(|e| e.is_some()), head, &header, @@ -400,12 +402,15 @@ pub(crate) async fn handle_new_head( keystore: &state.keystore, }; - match imported_block_info(ctx, env, block_hash, &block_header, finalized_number).await { + match imported_block_info(sender, env, block_hash, &block_header, finalized_number) + .await + { Ok(i) => imported_blocks_and_info.push((block_hash, block_header, i)), Err(error) => { // It's possible that we've lost a race with finality. let (tx, rx) = oneshot::channel(); - ctx.send_message(ChainApiMessage::FinalizedBlockHash(block_header.number, tx)) + sender + .send_message(ChainApiMessage::FinalizedBlockHash(block_header.number, tx)) .await; let lost_to_finality = match rx.await { @@ -449,26 +454,13 @@ pub(crate) async fn handle_new_head( force_approve, } = imported_block_info; - let session_info = match get_session_info( - session_info_provider, - ctx.sender(), - head, - session_index, - ) - .await - { - Some(session_info) => session_info, - None => return Ok(Vec::new()), - }; + let session_info = + match get_session_info(session_info_provider, sender, head, session_index).await { + Some(session_info) => session_info, + None => return Ok(Vec::new()), + }; - let (block_tick, no_show_duration) = { - let block_tick = slot_number_to_tick(state.slot_duration_millis, slot); - let no_show_duration = slot_number_to_tick( - state.slot_duration_millis, - Slot::from(u64::from(session_info.no_show_slots)), - ); - (block_tick, no_show_duration) - }; + let block_tick = slot_number_to_tick(state.slot_duration_millis, slot); let needed_approvals = session_info.needed_approvals; let validator_group_lens: Vec = @@ -509,7 +501,7 @@ pub(crate) async fn handle_new_head( }; // If all bits are already set, then send an approve message. if approved_bitfield.count_ones() == approved_bitfield.len() { - ctx.send_message(ChainSelectionMessage::Approved(block_hash)).await; + sender.send_message(ChainSelectionMessage::Approved(block_hash)).await; } let block_entry = v3::BlockEntry { @@ -566,7 +558,7 @@ pub(crate) async fn handle_new_head( // Notify chain-selection of all approved hashes. for hash in approved_hashes { - ctx.send_message(ChainSelectionMessage::Approved(hash)).await; + sender.send_message(ChainSelectionMessage::Approved(hash)).await; } } @@ -574,16 +566,19 @@ pub(crate) async fn handle_new_head( hash: block_hash, number: block_header.number, parent_hash: block_header.parent_hash, - candidates: included_candidates.iter().map(|(hash, _, _, _)| *hash).collect(), + candidates: included_candidates + .iter() + .map(|(hash, _, core_index, group_index)| (*hash, *core_index, *group_index)) + .collect(), slot, session: session_index, + vrf_story: relay_vrf_story, }); imported_candidates.push(BlockImportedCandidates { block_hash, block_number: block_header.number, block_tick, - no_show_duration, imported_candidates: candidate_entries .into_iter() .map(|(h, e)| (h, e.into())) @@ -598,7 +593,8 @@ pub(crate) async fn handle_new_head( "Informing distribution of newly imported chain", ); - ctx.send_unbounded_message(ApprovalDistributionMessage::NewBlocks(approval_meta)); + approval_voting_sender + .send_unbounded_message(ApprovalDistributionMessage::NewBlocks(approval_meta)); Ok(imported_candidates) } @@ -609,12 +605,16 @@ pub(crate) mod tests { approval_db::common::{load_block_entry, DbBackend}, RuntimeInfo, RuntimeInfoConfig, MAX_BLOCKS_WITH_ASSIGNMENT_TIMESTAMPS, }; + use approval_types::time::Clock; use assert_matches::assert_matches; use polkadot_node_primitives::{ approval::v1::{VrfSignature, VrfTranscript}, DISPUTE_WINDOW, }; - use polkadot_node_subsystem::messages::{AllMessages, ApprovalVotingMessage}; + use polkadot_node_subsystem::{ + messages::{AllMessages, ApprovalVotingMessage}, + SubsystemContext, + }; use polkadot_node_subsystem_test_helpers::make_subsystem_context; use polkadot_node_subsystem_util::database::Database; use polkadot_primitives::{ @@ -642,7 +642,7 @@ pub(crate) mod tests { #[derive(Default)] struct MockClock; - impl crate::time::Clock for MockClock { + impl Clock for MockClock { fn tick_now(&self) -> Tick { 42 // chosen by fair dice roll } @@ -656,9 +656,8 @@ pub(crate) mod tests { State { keystore: Arc::new(LocalKeystore::in_memory()), slot_duration_millis: 6_000, - clock: Box::new(MockClock::default()), + clock: Arc::new(MockClock::default()), assignment_criteria: Box::new(MockAssignmentCriteria::default()), - spans: HashMap::new(), per_block_assignments_gathering_times: LruMap::new(ByLength::new( MAX_BLOCKS_WITH_ASSIGNMENT_TIMESTAMPS, )), @@ -800,8 +799,9 @@ pub(crate) mod tests { keystore: &LocalKeystore::in_memory(), }; - let info = - imported_block_info(&mut ctx, env, hash, &header, &Some(4)).await.unwrap(); + let info = imported_block_info(ctx.sender(), env, hash, &header, &Some(4)) + .await + .unwrap(); assert_eq!(info.included_candidates, included_candidates); assert_eq!(info.session_index, session); @@ -947,7 +947,7 @@ pub(crate) mod tests { keystore: &LocalKeystore::in_memory(), }; - let info = imported_block_info(&mut ctx, env, hash, &header, &Some(4)).await; + let info = imported_block_info(ctx.sender(), env, hash, &header, &Some(4)).await; assert_matches!(info, Err(ImportedBlockInfoError::VrfInfoUnavailable)); }) @@ -1086,7 +1086,7 @@ pub(crate) mod tests { keystore: &LocalKeystore::in_memory(), }; - let info = imported_block_info(&mut ctx, env, hash, &header, &Some(6)).await; + let info = imported_block_info(ctx.sender(), env, hash, &header, &Some(6)).await; assert_matches!(info, Err(ImportedBlockInfoError::BlockAlreadyFinalized)); }) @@ -1122,7 +1122,8 @@ pub(crate) mod tests { #[test] fn imported_block_info_extracts_force_approve() { let pool = TaskExecutor::new(); - let (mut ctx, mut handle) = make_subsystem_context(pool.clone()); + let (mut ctx, mut handle) = + make_subsystem_context::(pool.clone()); let session = 5; let session_info = dummy_session_info(session); @@ -1185,7 +1186,7 @@ pub(crate) mod tests { }; let info = - imported_block_info(&mut ctx, env, hash, &header, &Some(4)).await.unwrap(); + imported_block_info(ctx.sender(), env, hash, &header, &Some(4)).await.unwrap(); assert_eq!(info.included_candidates, included_candidates); assert_eq!(info.session_index, session); @@ -1378,8 +1379,11 @@ pub(crate) mod tests { let test_fut = { Box::pin(async move { let mut overlay_db = OverlayedBackend::new(&db); + + let mut approval_voting_sender = ctx.sender().clone(); let result = handle_new_head( - &mut ctx, + ctx.sender(), + &mut approval_voting_sender, &state, &mut overlay_db, &mut session_info_provider, diff --git a/polkadot/node/core/approval-voting/src/lib.rs b/polkadot/node/core/approval-voting/src/lib.rs index d4b6855a44d0e410bbec757cae25eb7bd4803acb..0cb977c58021cec9f0a3fb23c203e5f61047a78a 100644 --- a/polkadot/node/core/approval-voting/src/lib.rs +++ b/polkadot/node/core/approval-voting/src/lib.rs @@ -21,9 +21,6 @@ //! of others. It uses this information to determine when candidates and blocks have //! been sufficiently approved to finalize. -use itertools::Itertools; -use jaeger::{hash_to_trace_identifier, PerLeafSpan}; -use polkadot_node_jaeger as jaeger; use polkadot_node_primitives::{ approval::{ v1::{BlockApprovalMeta, DelayTranche}, @@ -40,8 +37,9 @@ use polkadot_node_subsystem::{ ApprovalCheckError, ApprovalCheckResult, ApprovalDistributionMessage, ApprovalVotingMessage, AssignmentCheckError, AssignmentCheckResult, AvailabilityRecoveryMessage, BlockDescription, CandidateValidationMessage, ChainApiMessage, - ChainSelectionMessage, DisputeCoordinatorMessage, HighestApprovedAncestorBlock, - RuntimeApiMessage, RuntimeApiRequest, + ChainSelectionMessage, CheckedIndirectAssignment, CheckedIndirectSignedApprovalVote, + DisputeCoordinatorMessage, HighestApprovedAncestorBlock, PvfExecKind, RuntimeApiMessage, + RuntimeApiRequest, }, overseer, FromOrchestra, OverseerSignal, SpawnedSubsystem, SubsystemError, SubsystemResult, SubsystemSender, @@ -55,9 +53,8 @@ use polkadot_node_subsystem_util::{ }; use polkadot_primitives::{ ApprovalVoteMultipleCandidates, ApprovalVotingParams, BlockNumber, CandidateHash, - CandidateIndex, CandidateReceipt, CoreIndex, DisputeStatement, ExecutorParams, GroupIndex, - Hash, PvfExecKind, SessionIndex, SessionInfo, ValidDisputeStatementKind, ValidatorId, - ValidatorIndex, ValidatorPair, ValidatorSignature, + CandidateIndex, CandidateReceipt, CoreIndex, ExecutorParams, GroupIndex, Hash, SessionIndex, + SessionInfo, ValidatorId, ValidatorIndex, ValidatorPair, ValidatorSignature, }; use sc_keystore::LocalKeystore; use sp_application_crypto::Pair; @@ -91,9 +88,11 @@ use schnellru::{ByLength, LruMap}; use approval_checking::RequiredTranches; use bitvec::{order::Lsb0, vec::BitVec}; -use criteria::{AssignmentCriteria, RealAssignmentCriteria}; +pub use criteria::{AssignmentCriteria, Config as AssignmentConfig, RealAssignmentCriteria}; use persisted_entries::{ApprovalEntry, BlockEntry, CandidateEntry}; -use time::{slot_number_to_tick, Clock, ClockExt, DelayedApprovalTimer, SystemClock, Tick}; +use polkadot_node_primitives::approval::time::{ + slot_number_to_tick, Clock, ClockExt, DelayedApprovalTimer, SystemClock, Tick, +}; mod approval_checking; pub mod approval_db; @@ -102,7 +101,6 @@ pub mod criteria; mod import; mod ops; mod persisted_entries; -pub mod time; use crate::{ approval_checking::{Check, TranchesToApproveResult}, @@ -123,7 +121,6 @@ const APPROVAL_CHECKING_TIMEOUT: Duration = Duration::from_secs(120); const WAIT_FOR_SIGS_TIMEOUT: Duration = Duration::from_millis(500); const APPROVAL_CACHE_SIZE: u32 = 1024; -const TICK_TOO_FAR_IN_FUTURE: Tick = 20; // 10 seconds. const APPROVAL_DELAY: Tick = 2; pub(crate) const LOG_TARGET: &str = "parachain::approval-voting"; @@ -165,7 +162,8 @@ pub struct ApprovalVotingSubsystem { db: Arc, mode: Mode, metrics: Metrics, - clock: Box, + clock: Arc, + spawner: Arc, } #[derive(Clone)] @@ -484,6 +482,7 @@ impl ApprovalVotingSubsystem { keystore: Arc, sync_oracle: Box, metrics: Metrics, + spawner: Arc, ) -> Self { ApprovalVotingSubsystem::with_config_and_clock( config, @@ -491,7 +490,8 @@ impl ApprovalVotingSubsystem { keystore, sync_oracle, metrics, - Box::new(SystemClock {}), + Arc::new(SystemClock {}), + spawner, ) } @@ -502,7 +502,8 @@ impl ApprovalVotingSubsystem { keystore: Arc, sync_oracle: Box, metrics: Metrics, - clock: Box, + clock: Arc, + spawner: Arc, ) -> Self { ApprovalVotingSubsystem { keystore, @@ -512,6 +513,7 @@ impl ApprovalVotingSubsystem { mode: Mode::Syncing(sync_oracle), metrics, clock, + spawner, } } @@ -551,12 +553,21 @@ fn db_sanity_check(db: Arc, config: DatabaseConfig) -> SubsystemRe #[overseer::subsystem(ApprovalVoting, error = SubsystemError, prefix = self::overseer)] impl ApprovalVotingSubsystem { - fn start(self, ctx: Context) -> SpawnedSubsystem { + fn start(self, mut ctx: Context) -> SpawnedSubsystem { let backend = DbBackend::new(self.db.clone(), self.db_config); - let future = - run::(ctx, self, Box::new(RealAssignmentCriteria), backend) - .map_err(|e| SubsystemError::with_origin("approval-voting", e)) - .boxed(); + let to_other_subsystems = ctx.sender().clone(); + let to_approval_distr = ctx.sender().clone(); + + let future = run::( + ctx, + to_other_subsystems, + to_approval_distr, + self, + Box::new(RealAssignmentCriteria), + backend, + ) + .map_err(|e| SubsystemError::with_origin("approval-voting", e)) + .boxed(); SpawnedSubsystem { name: "approval-voting-subsystem", future } } @@ -620,11 +631,7 @@ impl Wakeups { self.wakeups.entry(tick).or_default().push((block_hash, candidate_hash)); } - fn prune_finalized_wakeups( - &mut self, - finalized_number: BlockNumber, - spans: &mut HashMap, - ) { + fn prune_finalized_wakeups(&mut self, finalized_number: BlockNumber) { let after = self.block_numbers.split_off(&(finalized_number + 1)); let pruned_blocks: HashSet<_> = std::mem::replace(&mut self.block_numbers, after) .into_iter() @@ -648,9 +655,6 @@ impl Wakeups { } } } - - // Remove all spans that are associated with pruned blocks. - spans.retain(|h, _| !pruned_blocks.contains(h)); } // Get the wakeup for a particular block/candidate combo, if any. @@ -825,9 +829,8 @@ where struct State { keystore: Arc, slot_duration_millis: u64, - clock: Box, + clock: Arc, assignment_criteria: Box, - spans: HashMap, // Per block, candidate records about how long we take until we gather enough // assignments, this is relevant because it gives us a good idea about how many // tranches we trigger and why. @@ -961,20 +964,20 @@ impl State { } // Returns the approval voting params from the RuntimeApi. - #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] - async fn get_approval_voting_params_or_default( + async fn get_approval_voting_params_or_default>( &self, - ctx: &mut Context, + sender: &mut Sender, session_index: SessionIndex, block_hash: Hash, ) -> Option { let (s_tx, s_rx) = oneshot::channel(); - ctx.send_message(RuntimeApiMessage::Request( - block_hash, - RuntimeApiRequest::ApprovalVotingParams(session_index, s_tx), - )) - .await; + sender + .send_message(RuntimeApiMessage::Request( + block_hash, + RuntimeApiRequest::ApprovalVotingParams(session_index, s_tx), + )) + .await; match s_rx.await { Ok(Ok(params)) => { @@ -1143,9 +1146,36 @@ enum Action { Conclude, } +/// Trait for providing approval voting subsystem with work. +#[async_trait::async_trait] +pub trait ApprovalVotingWorkProvider { + async fn recv(&mut self) -> SubsystemResult>; +} + +#[async_trait::async_trait] #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn run( - mut ctx: Context, +impl ApprovalVotingWorkProvider for Context { + async fn recv(&mut self) -> SubsystemResult> { + self.recv().await + } +} + +#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] +async fn run< + B, + WorkProvider: ApprovalVotingWorkProvider, + Sender: SubsystemSender + + SubsystemSender + + SubsystemSender + + SubsystemSender + + SubsystemSender + + SubsystemSender + + Clone, + ADSender: SubsystemSender, +>( + mut work_provider: WorkProvider, + mut to_other_subsystems: Sender, + mut to_approval_distr: ADSender, mut subsystem: ApprovalVotingSubsystem, assignment_criteria: Box, mut backend: B, @@ -1162,26 +1192,17 @@ where slot_duration_millis: subsystem.slot_duration_millis, clock: subsystem.clock, assignment_criteria, - spans: HashMap::new(), per_block_assignments_gathering_times: LruMap::new(ByLength::new( MAX_BLOCKS_WITH_ASSIGNMENT_TIMESTAMPS, )), no_show_stats: NoShowStats::default(), }; - // `None` on start-up. Gets initialized/updated on leaf update - let mut session_info_provider = RuntimeInfo::new_with_config(RuntimeInfoConfig { - keystore: None, - session_cache_lru_size: DISPUTE_WINDOW.get(), - }); - let mut wakeups = Wakeups::default(); - let mut currently_checking_set = CurrentlyCheckingSet::default(); - let mut delayed_approvals_timers = DelayedApprovalTimer::default(); - let mut approvals_cache = LruMap::new(ByLength::new(APPROVAL_CACHE_SIZE)); - let mut last_finalized_height: Option = { let (tx, rx) = oneshot::channel(); - ctx.send_message(ChainApiMessage::FinalizedBlockNumber(tx)).await; + to_other_subsystems + .send_message(ChainApiMessage::FinalizedBlockNumber(tx)) + .await; match rx.await? { Ok(number) => Some(number), Err(err) => { @@ -1191,13 +1212,24 @@ where } }; + // `None` on start-up. Gets initialized/updated on leaf update + let mut session_info_provider = RuntimeInfo::new_with_config(RuntimeInfoConfig { + keystore: None, + session_cache_lru_size: DISPUTE_WINDOW.get(), + }); + + let mut wakeups = Wakeups::default(); + let mut currently_checking_set = CurrentlyCheckingSet::default(); + let mut delayed_approvals_timers = DelayedApprovalTimer::default(); + let mut approvals_cache = LruMap::new(ByLength::new(APPROVAL_CACHE_SIZE)); + loop { let mut overlayed_db = OverlayedBackend::new(&backend); let actions = futures::select! { (_tick, woken_block, woken_candidate) = wakeups.next(&*state.clock).fuse() => { subsystem.metrics.on_wakeup(); process_wakeup( - &mut ctx, + &mut to_other_subsystems, &mut state, &mut overlayed_db, &mut session_info_provider, @@ -1207,9 +1239,11 @@ where &wakeups, ).await? } - next_msg = ctx.recv().fuse() => { + next_msg = work_provider.recv().fuse() => { let mut actions = handle_from_overseer( - &mut ctx, + &mut to_other_subsystems, + &mut to_approval_distr, + &subsystem.spawner, &mut state, &mut overlayed_db, &mut session_info_provider, @@ -1269,7 +1303,8 @@ where &mut overlayed_db, &mut session_info_provider, &state, - &mut ctx, + &mut to_other_subsystems, + &mut to_approval_distr, block_hash, validator_index, &subsystem.metrics, @@ -1291,7 +1326,9 @@ where }; if handle_actions( - &mut ctx, + &mut to_other_subsystems, + &mut to_approval_distr, + &subsystem.spawner, &mut state, &mut overlayed_db, &mut session_info_provider, @@ -1318,6 +1355,63 @@ where Ok(()) } +// Starts a worker thread that runs the approval voting subsystem. +pub async fn start_approval_worker< + WorkProvider: ApprovalVotingWorkProvider + Send + 'static, + Sender: SubsystemSender + + SubsystemSender + + SubsystemSender + + SubsystemSender + + SubsystemSender + + SubsystemSender + + Clone, + ADSender: SubsystemSender, +>( + work_provider: WorkProvider, + to_other_subsystems: Sender, + to_approval_distr: ADSender, + config: Config, + db: Arc, + keystore: Arc, + sync_oracle: Box, + metrics: Metrics, + spawner: Arc, + task_name: &'static str, + group_name: &'static str, + clock: Arc, +) -> SubsystemResult<()> { + let approval_voting = ApprovalVotingSubsystem::with_config_and_clock( + config, + db.clone(), + keystore, + sync_oracle, + metrics, + clock, + spawner, + ); + let backend = DbBackend::new(db.clone(), approval_voting.db_config); + let spawner = approval_voting.spawner.clone(); + spawner.spawn_blocking( + task_name, + Some(group_name), + Box::pin(async move { + if let Err(err) = run( + work_provider, + to_other_subsystems, + to_approval_distr, + approval_voting, + Box::new(RealAssignmentCriteria), + backend, + ) + .await + { + gum::error!(target: LOG_TARGET, ?err, "Approval voting worker stopped processing messages"); + }; + }), + ); + Ok(()) +} + // Handle actions is a function that accepts a set of instructions // and subsequently updates the underlying approvals_db in accordance // with the linear set of instructions passed in. Therefore, actions @@ -1338,8 +1432,19 @@ where // // returns `true` if any of the actions was a `Conclude` command. #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn handle_actions( - ctx: &mut Context, +async fn handle_actions< + Sender: SubsystemSender + + SubsystemSender + + SubsystemSender + + SubsystemSender + + SubsystemSender + + SubsystemSender + + Clone, + ADSender: SubsystemSender, +>( + sender: &mut Sender, + approval_voting_sender: &mut ADSender, + spawn_handle: &Arc, state: &mut State, overlayed_db: &mut OverlayedBackend<'_, impl Backend>, session_info_provider: &mut RuntimeInfo, @@ -1371,7 +1476,8 @@ async fn handle_actions( // Note that chaining these iterators is O(n) as we must consume // the prior iterator. let next_actions: Vec = issue_approval( - ctx, + sender, + approval_voting_sender, state, overlayed_db, session_info_provider, @@ -1407,25 +1513,17 @@ async fn handle_actions( continue } - let mut launch_approval_span = state - .spans - .get(&relay_block_hash) - .map(|span| span.child("launch-approval")) - .unwrap_or_else(|| jaeger::Span::new(candidate_hash, "launch-approval")) - .with_trace_id(candidate_hash) - .with_candidate(candidate_hash) - .with_stage(jaeger::Stage::ApprovalChecking); - metrics.on_assignment_produced(assignment_tranche); let block_hash = indirect_cert.block_hash; - launch_approval_span.add_string_tag("block-hash", format!("{:?}", block_hash)); let validator_index = indirect_cert.validator; if distribute_assignment { - ctx.send_unbounded_message(ApprovalDistributionMessage::DistributeAssignment( - indirect_cert, - claimed_candidate_indices, - )); + approval_voting_sender.send_unbounded_message( + ApprovalDistributionMessage::DistributeAssignment( + indirect_cert, + claimed_candidate_indices, + ), + ); } match approvals_cache.get(&candidate_hash) { @@ -1440,7 +1538,8 @@ async fn handle_actions( actions_iter = new_actions.into_iter(); }, None => { - let ctx = &mut *ctx; + let sender = sender.clone(); + let spawn_handle = spawn_handle.clone(); currently_checking_set .insert_relay_block_hash( @@ -1449,7 +1548,8 @@ async fn handle_actions( relay_block_hash, async move { launch_approval( - ctx, + sender, + spawn_handle, metrics.clone(), session, candidate, @@ -1458,7 +1558,6 @@ async fn handle_actions( backing_group, executor_params, core_index, - &launch_approval_span, ) .await }, @@ -1469,22 +1568,13 @@ async fn handle_actions( } }, Action::NoteApprovedInChainSelection(block_hash) => { - let _span = state - .spans - .get(&block_hash) - .map(|span| span.child("note-approved-in-chain-selection")) - .unwrap_or_else(|| { - jaeger::Span::new(block_hash, "note-approved-in-chain-selection") - }) - .with_string_tag("block-hash", format!("{:?}", block_hash)) - .with_stage(jaeger::Stage::ApprovalChecking); - ctx.send_message(ChainSelectionMessage::Approved(block_hash)).await; + sender.send_message(ChainSelectionMessage::Approved(block_hash)).await; }, Action::BecomeActive => { *mode = Mode::Active; let (messages, next_actions) = distribution_messages_for_activation( - ctx, + sender, overlayed_db, state, delayed_approvals_timers, @@ -1492,7 +1582,7 @@ async fn handle_actions( ) .await?; - ctx.send_messages(messages.into_iter()).await; + approval_voting_sender.send_messages(messages.into_iter()).await; let next_actions: Vec = next_actions.into_iter().map(|v| v.clone()).chain(actions_iter).collect(); @@ -1566,8 +1656,8 @@ fn get_assignment_core_indices( } #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn distribution_messages_for_activation( - ctx: &mut Context, +async fn distribution_messages_for_activation>( + sender: &mut Sender, db: &OverlayedBackend<'_, impl Backend>, state: &State, delayed_approvals_timers: &mut DelayedApprovalTimer, @@ -1582,15 +1672,6 @@ async fn distribution_messages_for_activation( messages.push(ApprovalDistributionMessage::NewBlocks(Vec::new())); // dummy value. for block_hash in all_blocks { - let mut distribution_message_span = state - .spans - .get(&block_hash) - .map(|span| span.child("distribution-messages-for-activation")) - .unwrap_or_else(|| { - jaeger::Span::new(block_hash, "distribution-messages-for-activation") - }) - .with_stage(jaeger::Stage::ApprovalChecking) - .with_string_tag("block-hash", format!("{:?}", block_hash)); let block_entry = match db.load_block_entry(&block_hash)? { Some(b) => b, None => { @@ -1600,21 +1681,37 @@ async fn distribution_messages_for_activation( }, }; - distribution_message_span.add_string_tag("block-hash", &block_hash.to_string()); - distribution_message_span - .add_string_tag("parent-hash", &block_entry.parent_hash().to_string()); approval_meta.push(BlockApprovalMeta { hash: block_hash, number: block_entry.block_number(), parent_hash: block_entry.parent_hash(), - candidates: block_entry.candidates().iter().map(|(_, c_hash)| *c_hash).collect(), + candidates: block_entry + .candidates() + .iter() + .map(|(core_index, c_hash)| { + let candidate = db.load_candidate_entry(c_hash).ok().flatten(); + let group_index = candidate + .and_then(|entry| { + entry.approval_entry(&block_hash).map(|entry| entry.backing_group()) + }) + .unwrap_or_else(|| { + gum::warn!( + target: LOG_TARGET, + ?block_hash, + ?c_hash, + "Missing candidate entry or approval entry", + ); + GroupIndex::default() + }); + (*c_hash, *core_index, group_index) + }) + .collect(), slot: block_entry.slot(), session: block_entry.session(), + vrf_story: block_entry.relay_vrf_story(), }); let mut signatures_queued = HashSet::new(); for (core_index, candidate_hash) in block_entry.candidates() { - let _candidate_span = - distribution_message_span.child("candidate").with_candidate(*candidate_hash); let candidate_entry = match db.load_candidate_entry(&candidate_hash)? { Some(c) => c, None => { @@ -1672,7 +1769,7 @@ async fn distribution_messages_for_activation( let ExtendedSessionInfo { ref executor_params, .. } = match get_extended_session_info( session_info_provider, - ctx.sender(), + sender, block_entry.block_hash(), block_entry.session(), ) @@ -1770,9 +1867,16 @@ async fn distribution_messages_for_activation( } // Handle an incoming signal from the overseer. Returns true if execution should conclude. -#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn handle_from_overseer( - ctx: &mut Context, +async fn handle_from_overseer< + Sender: SubsystemSender + + SubsystemSender + + SubsystemSender + + Clone, + ADSender: SubsystemSender, +>( + sender: &mut Sender, + approval_voting_sender: &mut ADSender, + spawn_handle: &Arc, state: &mut State, db: &mut OverlayedBackend<'_, impl Backend>, session_info_provider: &mut RuntimeInfo, @@ -1786,11 +1890,9 @@ async fn handle_from_overseer( let mut actions = Vec::new(); if let Some(activated) = update.activated { let head = activated.hash; - let approval_voting_span = - jaeger::PerLeafSpan::new(activated.span, "approval-voting"); - state.spans.insert(head, approval_voting_span); match import::handle_new_head( - ctx, + sender, + approval_voting_sender, state, db, session_info_provider, @@ -1858,7 +1960,7 @@ async fn handle_from_overseer( // `prune_finalized_wakeups` prunes all finalized block hashes. We prune spans // accordingly. - wakeups.prune_finalized_wakeups(block_number, &mut state.spans); + wakeups.prune_finalized_wakeups(block_number); state.cleanup_assignments_gathering_timestamp(block_number); // // `prune_finalized_wakeups` prunes all finalized block hashes. We prune spans @@ -1872,53 +1974,36 @@ async fn handle_from_overseer( vec![Action::Conclude] }, FromOrchestra::Communication { msg } => match msg { - ApprovalVotingMessage::CheckAndImportAssignment(a, claimed_cores, res) => { - let (check_outcome, actions) = check_and_import_assignment( - ctx.sender(), - state, - db, - session_info_provider, - a, - claimed_cores, - ) - .await?; - let _ = res.send(check_outcome); - + ApprovalVotingMessage::ImportAssignment(checked_assignment, tx) => { + let (check_outcome, actions) = + import_assignment(sender, state, db, session_info_provider, checked_assignment) + .await?; + // approval-distribution makes sure this assignment is valid and expected, + // so this import should never fail, if it does it might mean one of two things, + // there is a bug in the code or the two subsystems got out of sync. + if let AssignmentCheckResult::Bad(ref err) = check_outcome { + gum::debug!(target: LOG_TARGET, ?err, "Unexpected fail when importing an assignment"); + } + let _ = tx.map(|tx| tx.send(check_outcome)); actions }, - ApprovalVotingMessage::CheckAndImportApproval(a, res) => - check_and_import_approval( - ctx.sender(), - state, - db, - session_info_provider, - metrics, - a, - |r| { - let _ = res.send(r); - }, - &wakeups, - ) - .await? - .0, + ApprovalVotingMessage::ImportApproval(a, tx) => { + let result = + import_approval(sender, state, db, session_info_provider, metrics, a, &wakeups) + .await?; + // approval-distribution makes sure this vote is valid and expected, + // so this import should never fail, if it does it might mean one of two things, + // there is a bug in the code or the two subsystems got out of sync. + if let ApprovalCheckResult::Bad(ref err) = result.1 { + gum::debug!(target: LOG_TARGET, ?err, "Unexpected fail when importing an approval"); + } + let _ = tx.map(|tx| tx.send(result.1)); + + result.0 + }, ApprovalVotingMessage::ApprovedAncestor(target, lower_bound, res) => { - let mut approved_ancestor_span = state - .spans - .get(&target) - .map(|span| span.child("approved-ancestor")) - .unwrap_or_else(|| jaeger::Span::new(target, "approved-ancestor")) - .with_stage(jaeger::Stage::ApprovalChecking) - .with_string_tag("leaf", format!("{:?}", target)); - match handle_approved_ancestor( - ctx, - db, - target, - lower_bound, - wakeups, - &mut approved_ancestor_span, - &metrics, - ) - .await + match handle_approved_ancestor(sender, db, target, lower_bound, wakeups, &metrics) + .await { Ok(v) => { let _ = res.send(v); @@ -1933,7 +2018,14 @@ async fn handle_from_overseer( }, ApprovalVotingMessage::GetApprovalSignaturesForCandidate(candidate_hash, tx) => { metrics.on_candidate_signatures_request(); - get_approval_signatures_for_candidate(ctx, db, candidate_hash, tx).await?; + get_approval_signatures_for_candidate( + approval_voting_sender.clone(), + spawn_handle, + db, + candidate_hash, + tx, + ) + .await?; Vec::new() }, }, @@ -1947,8 +2039,11 @@ async fn handle_from_overseer( /// This involves an unbounded message send to approval-distribution, the caller has to ensure that /// calls to this function are infrequent and bounded. #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn get_approval_signatures_for_candidate( - ctx: &mut Context, +async fn get_approval_signatures_for_candidate< + Sender: SubsystemSender, +>( + mut sender: Sender, + spawn_handle: &Arc, db: &OverlayedBackend<'_, impl Backend>, candidate_hash: CandidateHash, tx: oneshot::Sender, ValidatorSignature)>>, @@ -2007,7 +2102,6 @@ async fn get_approval_signatures_for_candidate( } } - let mut sender = ctx.sender().clone(); let get_approvals = async move { let (tx_distribution, rx_distribution) = oneshot::channel(); sender.send_unbounded_message(ApprovalDistributionMessage::GetApprovalSignatures( @@ -2087,32 +2181,33 @@ async fn get_approval_signatures_for_candidate( ?candidate_hash, "Spawning task for fetching signatures from approval-distribution" ); - ctx.spawn("get-approval-signatures", Box::pin(get_approvals)) + spawn_handle.spawn( + "get-approval-signatures", + Some("approval-voting-subsystem"), + Box::pin(get_approvals), + ); + Ok(()) } #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn handle_approved_ancestor( - ctx: &mut Context, +async fn handle_approved_ancestor>( + sender: &mut Sender, db: &OverlayedBackend<'_, impl Backend>, target: Hash, lower_bound: BlockNumber, wakeups: &Wakeups, - span: &mut jaeger::Span, metrics: &Metrics, ) -> SubsystemResult> { const MAX_TRACING_WINDOW: usize = 200; const ABNORMAL_DEPTH_THRESHOLD: usize = 5; const LOGGING_DEPTH_THRESHOLD: usize = 10; - let mut span = span - .child("handle-approved-ancestor") - .with_stage(jaeger::Stage::ApprovalChecking); let mut all_approved_max = None; let target_number = { let (tx, rx) = oneshot::channel(); - ctx.send_message(ChainApiMessage::BlockNumber(target, tx)).await; + sender.send_message(ChainApiMessage::BlockNumber(target, tx)).await; match rx.await { Ok(Ok(Some(n))) => n, @@ -2121,8 +2216,6 @@ async fn handle_approved_ancestor( } }; - span.add_uint_tag("leaf-number", target_number as u64); - span.add_uint_tag("lower-bound", lower_bound as u64); if target_number <= lower_bound { return Ok(None) } @@ -2133,12 +2226,13 @@ async fn handle_approved_ancestor( let ancestry = if target_number > lower_bound + 1 { let (tx, rx) = oneshot::channel(); - ctx.send_message(ChainApiMessage::Ancestors { - hash: target, - k: (target_number - (lower_bound + 1)) as usize, - response_channel: tx, - }) - .await; + sender + .send_message(ChainApiMessage::Ancestors { + hash: target, + k: (target_number - (lower_bound + 1)) as usize, + response_channel: tx, + }) + .await; match rx.await { Ok(Ok(a)) => a, @@ -2153,9 +2247,6 @@ async fn handle_approved_ancestor( let mut bits: BitVec = Default::default(); for (i, block_hash) in std::iter::once(target).chain(ancestry).enumerate() { - let mut entry_span = - span.child("load-block-entry").with_stage(jaeger::Stage::ApprovalChecking); - entry_span.add_string_tag("block-hash", format!("{:?}", block_hash)); // Block entries should be present as the assumption is that // nothing here is finalized. If we encounter any missing block // entries we can fail. @@ -2222,7 +2313,6 @@ async fn handle_approved_ancestor( ) } metrics.on_unapproved_candidates_in_unfinalized_chain(unapproved.len()); - entry_span.add_uint_tag("unapproved-candidates", unapproved.len() as u64); for candidate_hash in unapproved { match db.load_candidate_entry(&candidate_hash)? { None => { @@ -2343,15 +2433,6 @@ async fn handle_approved_ancestor( number: block_number, descriptions: block_descriptions, }); - match all_approved_max { - Some(HighestApprovedAncestorBlock { ref hash, ref number, .. }) => { - span.add_uint_tag("highest-approved-number", *number as u64); - span.add_string_fmt_debug_tag("highest-approved-hash", hash); - }, - None => { - span.add_string_tag("reached-lower-bound", "true"); - }, - } Ok(all_approved_max) } @@ -2384,7 +2465,12 @@ fn schedule_wakeup_action( last_assignment_tick.map(|l| l + APPROVAL_DELAY).filter(|t| t > &tick_now), next_no_show, ) - .map(|tick| Action::ScheduleWakeup { block_hash, block_number, candidate_hash, tick }) + .map(|tick| Action::ScheduleWakeup { + block_hash, + block_number, + candidate_hash, + tick, + }) }, RequiredTranches::Pending { considered, next_no_show, clock_drift, .. } => { // select the minimum of `next_no_show`, or the tick of the next non-empty tranche @@ -2439,30 +2525,20 @@ fn schedule_wakeup_action( maybe_action } -async fn check_and_import_assignment( +async fn import_assignment( sender: &mut Sender, state: &State, db: &mut OverlayedBackend<'_, impl Backend>, session_info_provider: &mut RuntimeInfo, - assignment: IndirectAssignmentCertV2, - candidate_indices: CandidateBitfield, + checked_assignment: CheckedIndirectAssignment, ) -> SubsystemResult<(AssignmentCheckResult, Vec)> where Sender: SubsystemSender, { let tick_now = state.clock.tick_now(); - - let mut check_and_import_assignment_span = state - .spans - .get(&assignment.block_hash) - .map(|span| span.child("check-and-import-assignment")) - .unwrap_or_else(|| jaeger::Span::new(assignment.block_hash, "check-and-import-assignment")) - .with_relay_parent(assignment.block_hash) - .with_stage(jaeger::Stage::ApprovalChecking); - - for candidate_index in candidate_indices.iter_ones() { - check_and_import_assignment_span.add_uint_tag("candidate-index", candidate_index as u64); - } + let assignment = checked_assignment.assignment(); + let candidate_indices = checked_assignment.candidate_indices(); + let tranche = checked_assignment.tranche(); let block_entry = match db.load_block_entry(&assignment.block_hash)? { Some(b) => b, @@ -2514,8 +2590,6 @@ where )) } - // The Compact VRF modulo assignment cert has multiple core assignments. - let mut backing_groups = Vec::new(); let mut claimed_core_indices = Vec::new(); let mut assigned_candidate_hashes = Vec::new(); @@ -2544,26 +2618,16 @@ where )), // no candidate at core. }; - check_and_import_assignment_span - .add_string_tag("candidate-hash", format!("{:?}", assigned_candidate_hash)); - check_and_import_assignment_span.add_string_tag( - "traceID", - format!("{:?}", jaeger::hash_to_trace_identifier(assigned_candidate_hash.0)), - ); - - let approval_entry = match candidate_entry.approval_entry_mut(&assignment.block_hash) { - Some(a) => a, - None => - return Ok(( - AssignmentCheckResult::Bad(AssignmentCheckError::Internal( - assignment.block_hash, - assigned_candidate_hash, - )), - Vec::new(), + if candidate_entry.approval_entry_mut(&assignment.block_hash).is_none() { + return Ok(( + AssignmentCheckResult::Bad(AssignmentCheckError::Internal( + assignment.block_hash, + assigned_candidate_hash, )), + Vec::new(), + )); }; - backing_groups.push(approval_entry.backing_group()); claimed_core_indices.push(claimed_core_index); assigned_candidate_hashes.push(assigned_candidate_hash); } @@ -2579,42 +2643,6 @@ where )) } - // Check the assignment certificate. - let res = state.assignment_criteria.check_assignment_cert( - claimed_core_indices - .clone() - .try_into() - .expect("Checked for null assignment above; qed"), - assignment.validator, - &criteria::Config::from(session_info), - block_entry.relay_vrf_story(), - &assignment.cert, - backing_groups, - ); - - let tranche = match res { - Err(crate::criteria::InvalidAssignment(reason)) => - return Ok(( - AssignmentCheckResult::Bad(AssignmentCheckError::InvalidCert( - assignment.validator, - format!("{:?}", reason), - )), - Vec::new(), - )), - Ok(tranche) => { - let current_tranche = - state.clock.tranche_now(state.slot_duration_millis, block_entry.slot()); - - let too_far_in_future = current_tranche + TICK_TOO_FAR_IN_FUTURE as DelayTranche; - - if tranche >= too_far_in_future { - return Ok((AssignmentCheckResult::TooFarInFuture, Vec::new())) - } - - tranche - }, - }; - let mut actions = Vec::new(); let res = { let mut is_duplicate = true; @@ -2647,7 +2675,6 @@ where }; is_duplicate &= approval_entry.is_assigned(assignment.validator); approval_entry.import_assignment(tranche, assignment.validator, tick_now); - check_and_import_assignment_span.add_uint_tag("tranche", tranche as u64); // We've imported a new assignment, so we need to schedule a wake-up for when that might // no-show. @@ -2704,33 +2731,23 @@ where Ok((res, actions)) } -async fn check_and_import_approval( +async fn import_approval( sender: &mut Sender, state: &mut State, db: &mut OverlayedBackend<'_, impl Backend>, session_info_provider: &mut RuntimeInfo, metrics: &Metrics, - approval: IndirectSignedApprovalVoteV2, - with_response: impl FnOnce(ApprovalCheckResult) -> T, + approval: CheckedIndirectSignedApprovalVote, wakeups: &Wakeups, -) -> SubsystemResult<(Vec, T)> +) -> SubsystemResult<(Vec, ApprovalCheckResult)> where Sender: SubsystemSender, { macro_rules! respond_early { ($e: expr) => {{ - let t = with_response($e); - return Ok((Vec::new(), t)) + return Ok((Vec::new(), $e)) }}; } - let mut span = state - .spans - .get(&approval.block_hash) - .map(|span| span.child("check-and-import-approval")) - .unwrap_or_else(|| jaeger::Span::new(approval.block_hash, "check-and-import-approval")) - .with_string_fmt_debug_tag("candidate-index", approval.candidate_indices.clone()) - .with_relay_parent(approval.block_hash) - .with_stage(jaeger::Stage::ApprovalChecking); let block_entry = match db.load_block_entry(&approval.block_hash)? { Some(b) => b, @@ -2760,82 +2777,12 @@ where }, }; - span.add_string_tag("candidate-hashes", format!("{:?}", approved_candidates_info)); - span.add_string_tag( - "traceIDs", - format!( - "{:?}", - approved_candidates_info - .iter() - .map(|(_, approved_candidate_hash)| hash_to_trace_identifier( - approved_candidate_hash.0 - )) - .collect_vec() - ), + gum::trace!( + target: LOG_TARGET, + "Received approval for num_candidates {:}", + approval.candidate_indices.count_ones() ); - { - let session_info = match get_session_info( - session_info_provider, - sender, - approval.block_hash, - block_entry.session(), - ) - .await - { - Some(s) => s, - None => { - respond_early!(ApprovalCheckResult::Bad(ApprovalCheckError::UnknownSessionIndex( - block_entry.session() - ),)) - }, - }; - - let pubkey = match session_info.validators.get(approval.validator) { - Some(k) => k, - None => respond_early!(ApprovalCheckResult::Bad( - ApprovalCheckError::InvalidValidatorIndex(approval.validator), - )), - }; - - gum::trace!( - target: LOG_TARGET, - "Received approval for num_candidates {:}", - approval.candidate_indices.count_ones() - ); - - let candidate_hashes: Vec = - approved_candidates_info.iter().map(|candidate| candidate.1).collect(); - // Signature check: - match DisputeStatement::Valid( - ValidDisputeStatementKind::ApprovalCheckingMultipleCandidates(candidate_hashes.clone()), - ) - .check_signature( - &pubkey, - if let Some(candidate_hash) = candidate_hashes.first() { - *candidate_hash - } else { - respond_early!(ApprovalCheckResult::Bad(ApprovalCheckError::InvalidValidatorIndex( - approval.validator - ),)) - }, - block_entry.session(), - &approval.signature, - ) { - Err(_) => { - gum::error!( - target: LOG_TARGET, - "Error while checking signature {:}", - approval.candidate_indices.count_ones() - ); - respond_early!(ApprovalCheckResult::Bad(ApprovalCheckError::InvalidSignature( - approval.validator - ),)) - }, - Ok(()) => {}, - }; - } - let mut actions = Vec::new(); for (approval_candidate_index, approved_candidate_hash) in approved_candidates_info { let block_entry = match db.load_block_entry(&approval.block_hash)? { @@ -2898,9 +2845,7 @@ where } // importing the approval can be heavy as it may trigger acceptance for a series of blocks. - let t = with_response(ApprovalCheckResult::Accepted); - - Ok((actions, t)) + Ok((actions, ApprovalCheckResult::Accepted)) } #[derive(Debug)] @@ -3176,9 +3121,8 @@ fn should_trigger_assignment( } } -#[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn process_wakeup( - ctx: &mut Context, +async fn process_wakeup>( + sender: &mut Sender, state: &mut State, db: &mut OverlayedBackend<'_, impl Backend>, session_info_provider: &mut RuntimeInfo, @@ -3187,16 +3131,6 @@ async fn process_wakeup( metrics: &Metrics, wakeups: &Wakeups, ) -> SubsystemResult> { - let mut span = state - .spans - .get(&relay_block) - .map(|span| span.child("process-wakeup")) - .unwrap_or_else(|| jaeger::Span::new(candidate_hash, "process-wakeup")) - .with_trace_id(candidate_hash) - .with_relay_parent(relay_block) - .with_candidate(candidate_hash) - .with_stage(jaeger::Stage::ApprovalChecking); - let block_entry = db.load_block_entry(&relay_block)?; let candidate_entry = db.load_candidate_entry(&candidate_hash)?; @@ -3209,7 +3143,7 @@ async fn process_wakeup( let ExtendedSessionInfo { ref session_info, ref executor_params, .. } = match get_extended_session_info( session_info_provider, - ctx.sender(), + sender, block_entry.block_hash(), block_entry.session(), ) @@ -3225,7 +3159,7 @@ async fn process_wakeup( Slot::from(u64::from(session_info.no_show_slots)), ); let tranche_now = state.clock.tranche_now(state.slot_duration_millis, block_entry.slot()); - span.add_uint_tag("tranche", tranche_now as u64); + gum::trace!( target: LOG_TARGET, tranche = tranche_now, @@ -3351,7 +3285,7 @@ async fn process_wakeup( // Note that this function also schedules a wakeup as necessary. actions.extend( advance_approval_state( - ctx.sender(), + sender, state, db, session_info_provider, @@ -3372,8 +3306,14 @@ async fn process_wakeup( // spawned. When the background work is no longer needed, the `AbortHandle` should be dropped // to cancel the background work and any requests it has spawned. #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn launch_approval( - ctx: &mut Context, +async fn launch_approval< + Sender: SubsystemSender + + SubsystemSender + + SubsystemSender + + SubsystemSender, +>( + mut sender: Sender, + spawn_handle: Arc, metrics: Metrics, session_index: SessionIndex, candidate: CandidateReceipt, @@ -3382,7 +3322,6 @@ async fn launch_approval( backing_group: GroupIndex, executor_params: ExecutorParams, core_index: Option, - span: &jaeger::Span, ) -> SubsystemResult> { let (a_tx, a_rx) = oneshot::channel(); let (code_tx, code_rx) = oneshot::channel(); @@ -3416,39 +3355,29 @@ async fn launch_approval( let para_id = candidate.descriptor.para_id; gum::trace!(target: LOG_TARGET, ?candidate_hash, ?para_id, "Recovering data."); - let request_validation_data_span = span - .child("request-validation-data") - .with_trace_id(candidate_hash) - .with_candidate(candidate_hash) - .with_string_tag("block-hash", format!("{:?}", block_hash)) - .with_stage(jaeger::Stage::ApprovalChecking); - let timer = metrics.time_recover_and_approve(); - ctx.send_message(AvailabilityRecoveryMessage::RecoverAvailableData( - candidate.clone(), - session_index, - Some(backing_group), - core_index, - a_tx, - )) - .await; - - let request_validation_result_span = span - .child("request-validation-result") - .with_trace_id(candidate_hash) - .with_candidate(candidate_hash) - .with_string_tag("block-hash", format!("{:?}", block_hash)) - .with_stage(jaeger::Stage::ApprovalChecking); + sender + .send_message(AvailabilityRecoveryMessage::RecoverAvailableData( + candidate.clone(), + session_index, + Some(backing_group), + core_index, + a_tx, + )) + .await; - ctx.send_message(RuntimeApiMessage::Request( - block_hash, - RuntimeApiRequest::ValidationCodeByHash(candidate.descriptor.validation_code_hash, code_tx), - )) - .await; + sender + .send_message(RuntimeApiMessage::Request( + block_hash, + RuntimeApiRequest::ValidationCodeByHash( + candidate.descriptor.validation_code_hash, + code_tx, + ), + )) + .await; let candidate = candidate.clone(); let metrics_guard = StaleGuard(Some(metrics)); - let mut sender = ctx.sender().clone(); let background = async move { // Force the move of the timer into the background task. let _timer = timer; @@ -3500,7 +3429,6 @@ async fn launch_approval( return ApprovalState::failed(validator_index, candidate_hash) }, }; - drop(request_validation_data_span); let validation_code = match code_rx.await { Err(_) => return ApprovalState::failed(validator_index, candidate_hash), @@ -3572,20 +3500,24 @@ async fn launch_approval( "Failed to validate candidate due to internal error", ); metrics_guard.take().on_approval_error(); - drop(request_validation_result_span); return ApprovalState::failed(validator_index, candidate_hash) }, } }; let (background, remote_handle) = background.remote_handle(); - ctx.spawn("approval-checks", Box::pin(background)).map(move |()| remote_handle) + spawn_handle.spawn("approval-checks", Some("approval-voting-subsystem"), Box::pin(background)); + Ok(remote_handle) } // Issue and import a local approval vote. Should only be invoked after approval checks // have been done. #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn issue_approval( - ctx: &mut Context, +async fn issue_approval< + Sender: SubsystemSender, + ADSender: SubsystemSender, +>( + sender: &mut Sender, + approval_voting_sender: &mut ADSender, state: &mut State, db: &mut OverlayedBackend<'_, impl Backend>, session_info_provider: &mut RuntimeInfo, @@ -3595,17 +3527,6 @@ async fn issue_approval( ApprovalVoteRequest { validator_index, block_hash }: ApprovalVoteRequest, wakeups: &Wakeups, ) -> SubsystemResult> { - let mut issue_approval_span = state - .spans - .get(&block_hash) - .map(|span| span.child("issue-approval")) - .unwrap_or_else(|| jaeger::Span::new(block_hash, "issue-approval")) - .with_trace_id(candidate_hash) - .with_string_tag("block-hash", format!("{:?}", block_hash)) - .with_candidate(candidate_hash) - .with_validator_index(validator_index) - .with_stage(jaeger::Stage::ApprovalChecking); - let mut block_entry = match db.load_block_entry(&block_hash)? { Some(b) => b, None => { @@ -3630,7 +3551,6 @@ async fn issue_approval( }, Some(idx) => idx, }; - issue_approval_span.add_int_tag("candidate_index", candidate_index as i64); let candidate_hash = match block_entry.candidate(candidate_index as usize) { Some((_, h)) => *h, @@ -3664,7 +3584,7 @@ async fn issue_approval( let session_info = match get_session_info( session_info_provider, - ctx.sender(), + sender, block_entry.parent_hash(), block_entry.session(), ) @@ -3706,7 +3626,7 @@ async fn issue_approval( ); let actions = advance_approval_state( - ctx.sender(), + sender, state, db, session_info_provider, @@ -3723,7 +3643,8 @@ async fn issue_approval( db, session_info_provider, state, - ctx, + sender, + approval_voting_sender, block_hash, validator_index, metrics, @@ -3742,11 +3663,15 @@ async fn issue_approval( // Create signature for the approved candidates pending signatures #[overseer::contextbounds(ApprovalVoting, prefix = self::overseer)] -async fn maybe_create_signature( +async fn maybe_create_signature< + Sender: SubsystemSender, + ADSender: SubsystemSender, +>( db: &mut OverlayedBackend<'_, impl Backend>, session_info_provider: &mut RuntimeInfo, state: &State, - ctx: &mut Context, + sender: &mut Sender, + approval_voting_sender: &mut ADSender, block_hash: Hash, validator_index: ValidatorIndex, metrics: &Metrics, @@ -3765,7 +3690,7 @@ async fn maybe_create_signature( }; let approval_params = state - .get_approval_voting_params_or_default(ctx, block_entry.session(), block_hash) + .get_approval_voting_params_or_default(sender, block_entry.session(), block_hash) .await .unwrap_or_default(); @@ -3785,7 +3710,7 @@ async fn maybe_create_signature( let session_info = match get_session_info( session_info_provider, - ctx.sender(), + sender, block_entry.parent_hash(), block_entry.session(), ) @@ -3866,7 +3791,7 @@ async fn maybe_create_signature( metrics.on_approval_produced(); - ctx.send_unbounded_message(ApprovalDistributionMessage::DistributeApproval( + approval_voting_sender.send_unbounded_message(ApprovalDistributionMessage::DistributeApproval( IndirectSignedApprovalVoteV2 { block_hash: block_entry.block_hash(), candidate_indices: candidates_indices, @@ -3907,7 +3832,7 @@ fn issue_local_invalid_statement( candidate_hash: CandidateHash, candidate: CandidateReceipt, ) where - Sender: overseer::ApprovalVotingSenderTrait, + Sender: SubsystemSender, { // We need to send an unbounded message here to break a cycle: // DisputeCoordinatorMessage::IssueLocalStatement -> diff --git a/polkadot/node/core/approval-voting/src/persisted_entries.rs b/polkadot/node/core/approval-voting/src/persisted_entries.rs index 59a4618100515a179a3cdc8b72aee41024ff0bbb..16e231aa1a2dc1021cd8e111f2fb0533d0f74782 100644 --- a/polkadot/node/core/approval-voting/src/persisted_entries.rs +++ b/polkadot/node/core/approval-voting/src/persisted_entries.rs @@ -36,7 +36,9 @@ use std::collections::BTreeMap; use crate::approval_db::v2::Bitfield; -use super::{criteria::OurAssignment, time::Tick}; +use super::criteria::OurAssignment; + +use polkadot_node_primitives::approval::time::Tick; /// Metadata regarding a specific tranche of assignments for a specific candidate. #[derive(Debug, Clone, PartialEq)] diff --git a/polkadot/node/core/approval-voting/src/tests.rs b/polkadot/node/core/approval-voting/src/tests.rs index b912449baa4dcf55a5b22ae5a0e3dd33e6a10847..db5ffd441c0d06c39c70627994f9fde5ba729908 100644 --- a/polkadot/node/core/approval-voting/src/tests.rs +++ b/polkadot/node/core/approval-voting/src/tests.rs @@ -17,6 +17,7 @@ use self::test_helpers::mock::new_leaf; use super::*; use crate::backend::V1ReadBackend; +use itertools::Itertools; use overseer::prometheus::{ prometheus::{IntCounter, IntCounterVec}, Histogram, HistogramOpts, HistogramVec, Opts, @@ -35,19 +36,19 @@ use polkadot_node_subsystem::{ messages::{ AllMessages, ApprovalVotingMessage, AssignmentCheckResult, AvailabilityRecoveryMessage, }, - ActiveLeavesUpdate, + ActiveLeavesUpdate, SubsystemContext, }; use polkadot_node_subsystem_test_helpers as test_helpers; use polkadot_node_subsystem_util::TimeoutExt; -use polkadot_overseer::HeadSupportsParachains; +use polkadot_overseer::SpawnGlue; use polkadot_primitives::{ - ApprovalVote, CandidateCommitments, CandidateEvent, CoreIndex, GroupIndex, Header, - Id as ParaId, IndexedVec, NodeFeatures, ValidationCode, ValidatorSignature, + ApprovalVote, CandidateCommitments, CandidateEvent, CoreIndex, DisputeStatement, GroupIndex, + Header, Id as ParaId, IndexedVec, NodeFeatures, ValidDisputeStatementKind, ValidationCode, + ValidatorSignature, }; use std::{cmp::max, time::Duration}; use assert_matches::assert_matches; -use async_trait::async_trait; use parking_lot::Mutex; use sp_keyring::sr25519::Keyring as Sr25519Keyring; use sp_keystore::Keystore; @@ -130,17 +131,8 @@ pub mod test_constants { pub(crate) const TEST_CONFIG: DatabaseConfig = DatabaseConfig { col_approval_data: DATA_COL }; } -struct MockSupportsParachains; - -#[async_trait] -impl HeadSupportsParachains for MockSupportsParachains { - async fn head_supports_parachains(&self, _head: &Hash) -> bool { - true - } -} - -fn slot_to_tick(t: impl Into) -> crate::time::Tick { - crate::time::slot_number_to_tick(SLOT_DURATION_MILLIS, t.into()) +fn slot_to_tick(t: impl Into) -> Tick { + slot_number_to_tick(SLOT_DURATION_MILLIS, t.into()) } #[derive(Default, Clone)] @@ -262,7 +254,8 @@ where _relay_vrf_story: polkadot_node_primitives::approval::v1::RelayVRFStory, _assignment: &polkadot_node_primitives::approval::v2::AssignmentCertV2, _backing_groups: Vec, - ) -> Result { + ) -> Result + { self.1(validator_index) } } @@ -535,7 +528,7 @@ impl Default for HarnessConfig { struct TestHarness { virtual_overseer: VirtualOverseer, - clock: Box, + clock: Arc, sync_oracle_handle: TestSyncOracleHandle, } @@ -549,8 +542,8 @@ fn test_harness>( config; let pool = sp_core::testing::TaskExecutor::new(); - let (context, virtual_overseer) = - polkadot_node_subsystem_test_helpers::make_subsystem_context(pool); + let (mut context, virtual_overseer) = + polkadot_node_subsystem_test_helpers::make_subsystem_context(pool.clone()); let keystore = LocalKeystore::in_memory(); let _ = keystore.sr25519_generate_new( @@ -558,12 +551,14 @@ fn test_harness>( Some(&Sr25519Keyring::Alice.to_seed()), ); - let clock = Box::new(clock); + let clock = Arc::new(clock); let db = kvdb_memorydb::create(test_constants::NUM_COLUMNS); let db = polkadot_node_subsystem_util::database::kvdb_impl::DbAdapter::new(db, &[]); - + let sender = context.sender().clone(); let subsystem = run( context, + sender.clone(), + sender.clone(), ApprovalVotingSubsystem::with_config_and_clock( Config { col_approval_data: test_constants::TEST_CONFIG.col_approval_data, @@ -574,6 +569,7 @@ fn test_harness>( sync_oracle, Metrics::default(), clock.clone(), + Arc::new(SpawnGlue(pool)), ), assignment_criteria, backend, @@ -647,7 +643,7 @@ fn make_candidate(para_id: ParaId, hash: &Hash) -> CandidateReceipt { r } -async fn check_and_import_approval( +async fn import_approval( overseer: &mut VirtualOverseer, block_hash: Hash, candidate_index: CandidateIndex, @@ -666,14 +662,14 @@ async fn check_and_import_approval( overseer_send( overseer, FromOrchestra::Communication { - msg: ApprovalVotingMessage::CheckAndImportApproval( - IndirectSignedApprovalVoteV2 { + msg: ApprovalVotingMessage::ImportApproval( + CheckedIndirectSignedApprovalVote::from_checked(IndirectSignedApprovalVoteV2 { block_hash, candidate_indices: candidate_index.into(), validator, signature, - }, - tx, + }), + Some(tx), ), }, ) @@ -689,25 +685,31 @@ async fn check_and_import_approval( rx } -async fn check_and_import_assignment( +async fn import_assignment( overseer: &mut VirtualOverseer, block_hash: Hash, candidate_index: CandidateIndex, validator: ValidatorIndex, + tranche: DelayTranche, ) -> oneshot::Receiver { let (tx, rx) = oneshot::channel(); overseer_send( overseer, FromOrchestra::Communication { - msg: ApprovalVotingMessage::CheckAndImportAssignment( - IndirectAssignmentCertV2 { - block_hash, - validator, - cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo { sample: 0 }) + msg: ApprovalVotingMessage::ImportAssignment( + CheckedIndirectAssignment::from_checked( + IndirectAssignmentCertV2 { + block_hash, + validator, + cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo { + sample: 0, + }) .into(), - }, - candidate_index.into(), - tx, + }, + candidate_index.into(), + tranche, + ), + Some(tx), ), }, ) @@ -715,7 +717,7 @@ async fn check_and_import_assignment( rx } -async fn check_and_import_assignment_v2( +async fn import_assignment_v2( overseer: &mut VirtualOverseer, block_hash: Hash, core_indices: Vec, @@ -725,22 +727,27 @@ async fn check_and_import_assignment_v2( overseer_send( overseer, FromOrchestra::Communication { - msg: ApprovalVotingMessage::CheckAndImportAssignment( - IndirectAssignmentCertV2 { - block_hash, - validator, - cert: garbage_assignment_cert_v2(AssignmentCertKindV2::RelayVRFModuloCompact { - core_bitfield: core_indices - .clone() - .into_iter() - .map(|c| CoreIndex(c)) - .collect::>() - .try_into() - .unwrap(), - }), - }, - core_indices.try_into().unwrap(), - tx, + msg: ApprovalVotingMessage::ImportAssignment( + CheckedIndirectAssignment::from_checked( + IndirectAssignmentCertV2 { + block_hash, + validator, + cert: garbage_assignment_cert_v2( + AssignmentCertKindV2::RelayVRFModuloCompact { + core_bitfield: core_indices + .clone() + .into_iter() + .map(|c| CoreIndex(c)) + .collect::>() + .try_into() + .unwrap(), + }, + ), + }, + core_indices.try_into().unwrap(), + 0, + ), + Some(tx), ), }, ) @@ -1121,26 +1128,18 @@ fn subsystem_rejects_bad_assignment_ok_criteria() { ); builder.build(&mut virtual_overseer).await; - let rx = check_and_import_assignment( - &mut virtual_overseer, - block_hash, - candidate_index, - validator, - ) - .await; + let rx = + import_assignment(&mut virtual_overseer, block_hash, candidate_index, validator, 0) + .await; assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted),); // unknown hash let unknown_hash = Hash::repeat_byte(0x02); - let rx = check_and_import_assignment( - &mut virtual_overseer, - unknown_hash, - candidate_index, - validator, - ) - .await; + let rx = + import_assignment(&mut virtual_overseer, unknown_hash, candidate_index, validator, 0) + .await; assert_eq!( rx.await, @@ -1151,59 +1150,6 @@ fn subsystem_rejects_bad_assignment_ok_criteria() { }); } -#[test] -fn subsystem_rejects_bad_assignment_err_criteria() { - let assignment_criteria = Box::new(MockAssignmentCriteria::check_only(move |_| { - Err(criteria::InvalidAssignment( - criteria::InvalidAssignmentReason::ValidatorIndexOutOfBounds, - )) - })); - let config = HarnessConfigBuilder::default().assignment_criteria(assignment_criteria).build(); - test_harness(config, |test_harness| async move { - let TestHarness { mut virtual_overseer, sync_oracle_handle: _sync_oracle_handle, .. } = - test_harness; - assert_matches!( - overseer_recv(&mut virtual_overseer).await, - AllMessages::ChainApi(ChainApiMessage::FinalizedBlockNumber(rx)) => { - rx.send(Ok(0)).unwrap(); - } - ); - - let block_hash = Hash::repeat_byte(0x01); - let candidate_index = 0; - let validator = ValidatorIndex(0); - - let head: Hash = ChainBuilder::GENESIS_HASH; - let mut builder = ChainBuilder::new(); - let slot = Slot::from(1 as u64); - builder.add_block( - block_hash, - head, - 1, - BlockConfig { slot, candidates: None, session_info: None, end_syncing: false }, - ); - builder.build(&mut virtual_overseer).await; - - let rx = check_and_import_assignment( - &mut virtual_overseer, - block_hash, - candidate_index, - validator, - ) - .await; - - assert_eq!( - rx.await, - Ok(AssignmentCheckResult::Bad(AssignmentCheckError::InvalidCert( - ValidatorIndex(0), - "ValidatorIndexOutOfBounds".to_string(), - ))), - ); - - virtual_overseer - }); -} - #[test] fn blank_subsystem_act_on_bad_block() { test_harness(HarnessConfig::default(), |test_harness| async move { @@ -1222,17 +1168,20 @@ fn blank_subsystem_act_on_bad_block() { overseer_send( &mut virtual_overseer, FromOrchestra::Communication { - msg: ApprovalVotingMessage::CheckAndImportAssignment( - IndirectAssignmentCertV2 { - block_hash: bad_block_hash, - validator: 0u32.into(), - cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo { - sample: 0, - }) - .into(), - }, - 0u32.into(), - tx, + msg: ApprovalVotingMessage::ImportAssignment( + CheckedIndirectAssignment::from_checked( + IndirectAssignmentCertV2 { + block_hash: bad_block_hash, + validator: 0u32.into(), + cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo { + sample: 0, + }) + .into(), + }, + 0u32.into(), + 0, + ), + Some(tx), ), }, ) @@ -1295,7 +1244,7 @@ fn subsystem_rejects_approval_if_no_candidate_entry() { }); let session_index = 1; - let rx = check_and_import_approval( + let rx = import_approval( &mut virtual_overseer, block_hash, candidate_index, @@ -1336,7 +1285,7 @@ fn subsystem_rejects_approval_if_no_block_entry() { let candidate_hash = dummy_candidate_receipt(block_hash).hash(); let session_index = 1; - let rx = check_and_import_approval( + let rx = import_approval( &mut virtual_overseer, block_hash, candidate_index, @@ -1401,7 +1350,7 @@ fn subsystem_rejects_approval_before_assignment() { .build(&mut virtual_overseer) .await; - let rx = check_and_import_approval( + let rx = import_approval( &mut virtual_overseer, block_hash, candidate_index, @@ -1424,68 +1373,6 @@ fn subsystem_rejects_approval_before_assignment() { }); } -#[test] -fn subsystem_rejects_assignment_in_future() { - let assignment_criteria = - Box::new(MockAssignmentCriteria::check_only(|_| Ok(TICK_TOO_FAR_IN_FUTURE as _))); - let config = HarnessConfigBuilder::default().assignment_criteria(assignment_criteria).build(); - test_harness(config, |test_harness| async move { - let TestHarness { mut virtual_overseer, clock, sync_oracle_handle: _sync_oracle_handle } = - test_harness; - assert_matches!( - overseer_recv(&mut virtual_overseer).await, - AllMessages::ChainApi(ChainApiMessage::FinalizedBlockNumber(rx)) => { - rx.send(Ok(0)).unwrap(); - } - ); - - let block_hash = Hash::repeat_byte(0x01); - let candidate_index = 0; - let validator = ValidatorIndex(0); - - // Add block hash 00. - ChainBuilder::new() - .add_block( - block_hash, - ChainBuilder::GENESIS_HASH, - 1, - BlockConfig { - slot: Slot::from(0), - candidates: None, - session_info: None, - end_syncing: false, - }, - ) - .build(&mut virtual_overseer) - .await; - - let rx = check_and_import_assignment( - &mut virtual_overseer, - block_hash, - candidate_index, - validator, - ) - .await; - - assert_eq!(rx.await, Ok(AssignmentCheckResult::TooFarInFuture)); - - // Advance clock to make assignment reasonably near. - clock.inner.lock().set_tick(9); - - let rx = check_and_import_assignment( - &mut virtual_overseer, - block_hash, - candidate_index, - validator, - ) - .await; - - assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted)); - - virtual_overseer - }); -} - #[test] fn subsystem_accepts_duplicate_assignment() { test_harness(HarnessConfig::default(), |test_harness| async move { @@ -1535,7 +1422,7 @@ fn subsystem_accepts_duplicate_assignment() { .await; // Initial assignment. - let rx = check_and_import_assignment_v2( + let rx = import_assignment_v2( &mut virtual_overseer, block_hash, vec![candidate_index1, candidate_index2], @@ -1546,19 +1433,15 @@ fn subsystem_accepts_duplicate_assignment() { assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted)); // Test with single assigned core. - let rx = check_and_import_assignment( - &mut virtual_overseer, - block_hash, - candidate_index, - validator, - ) - .await; + let rx = + import_assignment(&mut virtual_overseer, block_hash, candidate_index, validator, 0) + .await; assert_eq!(rx.await, Ok(AssignmentCheckResult::AcceptedDuplicate)); // Test with multiple assigned cores. This cannot happen in practice, as tranche0 // assignments are sent first, but we should still ensure correct behavior. - let rx = check_and_import_assignment_v2( + let rx = import_assignment_v2( &mut virtual_overseer, block_hash, vec![candidate_index1, candidate_index2], @@ -1604,13 +1487,9 @@ fn subsystem_rejects_assignment_with_unknown_candidate() { .build(&mut virtual_overseer) .await; - let rx = check_and_import_assignment( - &mut virtual_overseer, - block_hash, - candidate_index, - validator, - ) - .await; + let rx = + import_assignment(&mut virtual_overseer, block_hash, candidate_index, validator, 0) + .await; assert_eq!( rx.await, @@ -1654,13 +1533,9 @@ fn subsystem_rejects_oversized_bitfields() { .build(&mut virtual_overseer) .await; - let rx = check_and_import_assignment( - &mut virtual_overseer, - block_hash, - candidate_index, - validator, - ) - .await; + let rx = + import_assignment(&mut virtual_overseer, block_hash, candidate_index, validator, 0) + .await; assert_eq!( rx.await, @@ -1669,13 +1544,9 @@ fn subsystem_rejects_oversized_bitfields() { ))), ); - let rx = check_and_import_assignment_v2( - &mut virtual_overseer, - block_hash, - vec![1, 2, 10, 50], - validator, - ) - .await; + let rx = + import_assignment_v2(&mut virtual_overseer, block_hash, vec![1, 2, 10, 50], validator) + .await; assert_eq!( rx.await, @@ -1727,17 +1598,13 @@ fn subsystem_accepts_and_imports_approval_after_assignment() { .build(&mut virtual_overseer) .await; - let rx = check_and_import_assignment( - &mut virtual_overseer, - block_hash, - candidate_index, - validator, - ) - .await; + let rx = + import_assignment(&mut virtual_overseer, block_hash, candidate_index, validator, 0) + .await; assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted)); - let rx = check_and_import_approval( + let rx = import_approval( &mut virtual_overseer, block_hash, candidate_index, @@ -1819,19 +1686,15 @@ fn subsystem_second_approval_import_only_schedules_wakeups() { .build(&mut virtual_overseer) .await; - let rx = check_and_import_assignment( - &mut virtual_overseer, - block_hash, - candidate_index, - validator, - ) - .await; + let rx = + import_assignment(&mut virtual_overseer, block_hash, candidate_index, validator, 0) + .await; assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted)); assert!(clock.inner.lock().current_wakeup_is(APPROVAL_DELAY + 2)); - let rx = check_and_import_approval( + let rx = import_approval( &mut virtual_overseer, block_hash, candidate_index, @@ -1848,7 +1711,7 @@ fn subsystem_second_approval_import_only_schedules_wakeups() { futures_timer::Delay::new(Duration::from_millis(100)).await; assert!(clock.inner.lock().current_wakeup_is(APPROVAL_DELAY + 2)); - let rx = check_and_import_approval( + let rx = import_approval( &mut virtual_overseer, block_hash, candidate_index, @@ -1907,13 +1770,9 @@ fn subsystem_assignment_import_updates_candidate_entry_and_schedules_wakeup() { .build(&mut virtual_overseer) .await; - let rx = check_and_import_assignment( - &mut virtual_overseer, - block_hash, - candidate_index, - validator, - ) - .await; + let rx = + import_assignment(&mut virtual_overseer, block_hash, candidate_index, validator, 0) + .await; assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted)); @@ -2029,20 +1888,17 @@ fn test_approvals_on_fork_are_always_considered_after_no_show( .await; // Send assignments for the same candidate on both forks - let rx = check_and_import_assignment( - &mut virtual_overseer, - block_hash, - candidate_index, - validator, - ) - .await; + let rx = + import_assignment(&mut virtual_overseer, block_hash, candidate_index, validator, 0) + .await; assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted)); - let rx = check_and_import_assignment( + let rx = import_assignment( &mut virtual_overseer, block_hash_fork, candidate_index, validator, + 0, ) .await; @@ -2072,7 +1928,7 @@ fn test_approvals_on_fork_are_always_considered_after_no_show( futures_timer::Delay::new(Duration::from_millis(100)).await; // Send the approval for candidate just in the context of 0x01 block. - let rx = check_and_import_approval( + let rx = import_approval( &mut virtual_overseer, block_hash, candidate_index, @@ -2142,13 +1998,9 @@ fn subsystem_process_wakeup_schedules_wakeup() { .build(&mut virtual_overseer) .await; - let rx = check_and_import_assignment( - &mut virtual_overseer, - block_hash, - candidate_index, - validator, - ) - .await; + let rx = + import_assignment(&mut virtual_overseer, block_hash, candidate_index, validator, 0) + .await; assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted)); @@ -2201,17 +2053,20 @@ fn linear_import_act_on_leaf() { overseer_send( &mut virtual_overseer, FromOrchestra::Communication { - msg: ApprovalVotingMessage::CheckAndImportAssignment( - IndirectAssignmentCertV2 { - block_hash: head, - validator: 0u32.into(), - cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo { - sample: 0, - }) - .into(), - }, - 0u32.into(), - tx, + msg: ApprovalVotingMessage::ImportAssignment( + CheckedIndirectAssignment::from_checked( + IndirectAssignmentCertV2 { + block_hash: head, + validator: 0u32.into(), + cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo { + sample: 0, + }) + .into(), + }, + 0u32.into(), + 0, + ), + Some(tx), ), }, ) @@ -2272,17 +2127,20 @@ fn forkful_import_at_same_height_act_on_leaf() { overseer_send( &mut virtual_overseer, FromOrchestra::Communication { - msg: ApprovalVotingMessage::CheckAndImportAssignment( - IndirectAssignmentCertV2 { - block_hash: head, - validator: 0u32.into(), - cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo { - sample: 0, - }) - .into(), - }, - 0u32.into(), - tx, + msg: ApprovalVotingMessage::ImportAssignment( + CheckedIndirectAssignment::from_checked( + IndirectAssignmentCertV2 { + block_hash: head, + validator: 0u32.into(), + cert: garbage_assignment_cert(AssignmentCertKind::RelayVRFModulo { + sample: 0, + }) + .into(), + }, + 0u32.into(), + 0, + ), + Some(tx), ), }, ) @@ -2439,21 +2297,23 @@ fn import_checked_approval_updates_entries_and_schedules() { let candidate_index = 0; - let rx = check_and_import_assignment( + let rx = import_assignment( &mut virtual_overseer, block_hash, candidate_index, validator_index_a, + 0, ) .await; assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted),); - let rx = check_and_import_assignment( + let rx = import_assignment( &mut virtual_overseer, block_hash, candidate_index, validator_index_b, + 0, ) .await; @@ -2462,7 +2322,7 @@ fn import_checked_approval_updates_entries_and_schedules() { let session_index = 1; let sig_a = sign_approval(Sr25519Keyring::Alice, candidate_hash, session_index); - let rx = check_and_import_approval( + let rx = import_approval( &mut virtual_overseer, block_hash, candidate_index, @@ -2489,7 +2349,7 @@ fn import_checked_approval_updates_entries_and_schedules() { clock.inner.lock().wakeup_all(2); let sig_b = sign_approval(Sr25519Keyring::Bob, candidate_hash, session_index); - let rx = check_and_import_approval( + let rx = import_approval( &mut virtual_overseer, block_hash, candidate_index, @@ -2602,13 +2462,9 @@ fn subsystem_import_checked_approval_sets_one_block_bit_at_a_time() { ]; for (candidate_index, validator) in assignments { - let rx = check_and_import_assignment( - &mut virtual_overseer, - block_hash, - candidate_index, - validator, - ) - .await; + let rx = + import_assignment(&mut virtual_overseer, block_hash, candidate_index, validator, 0) + .await; assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted)); } @@ -2629,7 +2485,7 @@ fn subsystem_import_checked_approval_sets_one_block_bit_at_a_time() { } else { sign_approval(Sr25519Keyring::Bob, *candidate_hash, session_index) }; - let rx = check_and_import_approval( + let rx = import_approval( &mut virtual_overseer, block_hash, *candidate_index, @@ -2887,11 +2743,12 @@ fn approved_ancestor_test( for (i, (block_hash, candidate_hash)) in block_hashes.iter().zip(candidate_hashes).enumerate() { - let rx = check_and_import_assignment( + let rx = import_assignment( &mut virtual_overseer, *block_hash, candidate_index, validator, + 0, ) .await; assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted)); @@ -2900,7 +2757,7 @@ fn approved_ancestor_test( continue } - let rx = check_and_import_approval( + let rx = import_approval( &mut virtual_overseer, *block_hash, candidate_index, @@ -3355,7 +3212,8 @@ where F1: 'static + Fn(ValidatorIndex) -> Result + Send - + Sync, + + Sync + + Clone, F2: Fn(Tick) -> bool, { let TriggersAssignmentConfig { @@ -3384,7 +3242,7 @@ where ); assignments }, - assign_validator_tranche, + assign_validator_tranche.clone(), )); let config = HarnessConfigBuilder::default().assignment_criteria(assignment_criteria).build(); let store = config.backend(); @@ -3445,11 +3303,12 @@ where .await; for validator in assignments_to_import { - let rx = check_and_import_assignment( + let rx = import_assignment( &mut virtual_overseer, block_hash, candidate_index, ValidatorIndex(validator), + assign_validator_tranche(ValidatorIndex(validator)).unwrap(), ) .await; assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted)); @@ -3458,7 +3317,7 @@ where let n_validators = validators.len(); for (i, &validator_index) in approvals_to_import.iter().enumerate() { let expect_chain_approved = 3 * (i + 1) > n_validators; - let rx = check_and_import_approval( + let rx = import_approval( &mut virtual_overseer, block_hash, candidate_index, @@ -3763,31 +3622,34 @@ fn pre_covers_dont_stall_approval() { let candidate_index = 0; - let rx = check_and_import_assignment( + let rx = import_assignment( &mut virtual_overseer, block_hash, candidate_index, validator_index_a, + 0, ) .await; assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted),); - let rx = check_and_import_assignment( + let rx = import_assignment( &mut virtual_overseer, block_hash, candidate_index, validator_index_b, + 0, ) .await; assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted),); - let rx = check_and_import_assignment( + let rx = import_assignment( &mut virtual_overseer, block_hash, candidate_index, validator_index_c, + 1, ) .await; @@ -3796,7 +3658,7 @@ fn pre_covers_dont_stall_approval() { let session_index = 1; let sig_b = sign_approval(Sr25519Keyring::Bob, candidate_hash, session_index); - let rx = check_and_import_approval( + let rx = import_approval( &mut virtual_overseer, block_hash, candidate_index, @@ -3811,7 +3673,7 @@ fn pre_covers_dont_stall_approval() { assert_eq!(rx.await, Ok(ApprovalCheckResult::Accepted),); let sig_c = sign_approval(Sr25519Keyring::Charlie, candidate_hash, session_index); - let rx = check_and_import_approval( + let rx = import_approval( &mut virtual_overseer, block_hash, candidate_index, @@ -3941,21 +3803,23 @@ fn waits_until_approving_assignments_are_old_enough() { let candidate_index = 0; - let rx = check_and_import_assignment( + let rx = import_assignment( &mut virtual_overseer, block_hash, candidate_index, validator_index_a, + 0, ) .await; assert_eq!(rx.await, Ok(AssignmentCheckResult::Accepted),); - let rx = check_and_import_assignment( + let rx = import_assignment( &mut virtual_overseer, block_hash, candidate_index, validator_index_b, + 0, ) .await; @@ -3966,7 +3830,7 @@ fn waits_until_approving_assignments_are_old_enough() { let session_index = 1; let sig_a = sign_approval(Sr25519Keyring::Alice, candidate_hash, session_index); - let rx = check_and_import_approval( + let rx = import_approval( &mut virtual_overseer, block_hash, candidate_index, @@ -3982,7 +3846,7 @@ fn waits_until_approving_assignments_are_old_enough() { let sig_b = sign_approval(Sr25519Keyring::Bob, candidate_hash, session_index); - let rx = check_and_import_approval( + let rx = import_approval( &mut virtual_overseer, block_hash, candidate_index, @@ -4245,7 +4109,7 @@ async fn handle_approval_on_max_coalesce_count( async fn handle_approval_on_max_wait_time( virtual_overseer: &mut VirtualOverseer, candidate_indices: Vec, - clock: Box, + clock: Arc, ) { const TICK_NOW_BEGIN: u64 = 1; const MAX_COALESCE_COUNT: u32 = 3; @@ -4543,7 +4407,7 @@ async fn build_chain_with_two_blocks_with_one_candidate_each( async fn setup_overseer_with_two_blocks_each_with_one_assignment_triggered( virtual_overseer: &mut VirtualOverseer, store: TestStore, - clock: &Box, + clock: &Arc, sync_oracle_handle: TestSyncOracleHandle, ) { assert_matches!( @@ -4992,7 +4856,6 @@ fn subsystem_sends_pending_approvals_on_approval_restart() { })); } ); - assert_matches!( overseer_recv(&mut virtual_overseer).await, AllMessages::RuntimeApi( @@ -5058,9 +4921,8 @@ fn test_gathering_assignments_statements() { let mut state = State { keystore: Arc::new(LocalKeystore::in_memory()), slot_duration_millis: 6_000, - clock: Box::new(MockClock::default()), + clock: Arc::new(MockClock::default()), assignment_criteria: Box::new(MockAssignmentCriteria::check_only(|_| Ok(0))), - spans: HashMap::new(), per_block_assignments_gathering_times: LruMap::new(ByLength::new( MAX_BLOCKS_WITH_ASSIGNMENT_TIMESTAMPS, )), @@ -5153,9 +5015,8 @@ fn test_observe_assignment_gathering_status() { let mut state = State { keystore: Arc::new(LocalKeystore::in_memory()), slot_duration_millis: 6_000, - clock: Box::new(MockClock::default()), + clock: Arc::new(MockClock::default()), assignment_criteria: Box::new(MockAssignmentCriteria::check_only(|_| Ok(0))), - spans: HashMap::new(), per_block_assignments_gathering_times: LruMap::new(ByLength::new( MAX_BLOCKS_WITH_ASSIGNMENT_TIMESTAMPS, )), diff --git a/polkadot/node/core/av-store/Cargo.toml b/polkadot/node/core/av-store/Cargo.toml index c867180e541bbf013cc874450e132c08f87981a5..1d14e4cfba37824f16bc49afd64669a08748a0b9 100644 --- a/polkadot/node/core/av-store/Cargo.toml +++ b/polkadot/node/core/av-store/Cargo.toml @@ -25,7 +25,6 @@ polkadot-overseer = { workspace = true, default-features = true } polkadot-primitives = { workspace = true, default-features = true } polkadot-node-primitives = { workspace = true, default-features = true } sp-consensus = { workspace = true } -polkadot-node-jaeger = { workspace = true, default-features = true } [dev-dependencies] log = { workspace = true, default-features = true } diff --git a/polkadot/node/core/av-store/src/lib.rs b/polkadot/node/core/av-store/src/lib.rs index 7b245c9e3c52f88d39ebe7d8252f1db128edcaca..9473040e8f5ed63bb5c207efb9a8abfa38e317f5 100644 --- a/polkadot/node/core/av-store/src/lib.rs +++ b/polkadot/node/core/av-store/src/lib.rs @@ -39,7 +39,6 @@ use polkadot_node_subsystem_util::database::{DBTransaction, Database}; use sp_consensus::SyncOracle; use bitvec::{order::Lsb0 as BitOrderLsb0, vec::BitVec}; -use polkadot_node_jaeger as jaeger; use polkadot_node_primitives::{AvailableData, ErasureChunk}; use polkadot_node_subsystem::{ errors::{ChainApiError, RuntimeApiError}, @@ -1315,10 +1314,6 @@ fn store_available_data( }, }; - let erasure_span = jaeger::Span::new(candidate_hash, "erasure-coding") - .with_candidate(candidate_hash) - .with_pov(&available_data.pov); - // Important note: This check below is critical for consensus and the `backing` subsystem relies // on it to ensure candidate validity. let chunks = polkadot_erasure_coding::obtain_chunks_v1(n_validators, &available_data)?; @@ -1328,8 +1323,6 @@ fn store_available_data( return Err(Error::InvalidErasureRoot) } - drop(erasure_span); - let erasure_chunks: Vec<_> = chunks .iter() .zip(branches.map(|(proof, _)| proof)) diff --git a/polkadot/node/core/backing/Cargo.toml b/polkadot/node/core/backing/Cargo.toml index 1b52afc309bc9e301f879a720d253467a5359f13..bd56a3ad693bff0094b6dd8b0ccb956b0aa49ee8 100644 --- a/polkadot/node/core/backing/Cargo.toml +++ b/polkadot/node/core/backing/Cargo.toml @@ -14,6 +14,7 @@ futures = { workspace = true } sp-keystore = { workspace = true, default-features = true } polkadot-primitives = { workspace = true, default-features = true } polkadot-node-primitives = { workspace = true, default-features = true } +polkadot-parachain-primitives = { workspace = true, default-features = true } polkadot-node-subsystem = { workspace = true, default-features = true } polkadot-node-subsystem-util = { workspace = true, default-features = true } polkadot-erasure-coding = { workspace = true, default-features = true } diff --git a/polkadot/node/core/backing/src/lib.rs b/polkadot/node/core/backing/src/lib.rs index f276321c87ed08f2f0d9f5e67901b7ce8492d4c5..4463fb34b5103da3ad618d985b80622071cc162d 100644 --- a/polkadot/node/core/backing/src/lib.rs +++ b/polkadot/node/core/backing/src/lib.rs @@ -89,8 +89,9 @@ use polkadot_node_subsystem::{ AvailabilityDistributionMessage, AvailabilityStoreMessage, CanSecondRequest, CandidateBackingMessage, CandidateValidationMessage, CollatorProtocolMessage, HypotheticalCandidate, HypotheticalMembershipRequest, IntroduceSecondedCandidateRequest, - ProspectiveParachainsMessage, ProvisionableData, ProvisionerMessage, RuntimeApiMessage, - RuntimeApiRequest, StatementDistributionMessage, StoreAvailableDataError, + ProspectiveParachainsMessage, ProvisionableData, ProvisionerMessage, PvfExecKind, + RuntimeApiMessage, RuntimeApiRequest, StatementDistributionMessage, + StoreAvailableDataError, }, overseer, ActiveLeavesUpdate, FromOrchestra, OverseerSignal, SpawnedSubsystem, SubsystemError, }; @@ -105,12 +106,13 @@ use polkadot_node_subsystem_util::{ }, Validator, }; +use polkadot_parachain_primitives::primitives::IsSystem; use polkadot_primitives::{ node_features::FeatureIndex, BackedCandidate, CandidateCommitments, CandidateHash, CandidateReceipt, CommittedCandidateReceipt, CoreIndex, CoreState, ExecutorParams, GroupIndex, GroupRotationInfo, Hash, Id as ParaId, IndexedVec, NodeFeatures, PersistedValidationData, - PvfExecKind, SessionIndex, SigningContext, ValidationCode, ValidatorId, ValidatorIndex, - ValidatorSignature, ValidityAttestation, + SessionIndex, SigningContext, ValidationCode, ValidatorId, ValidatorIndex, ValidatorSignature, + ValidityAttestation, }; use polkadot_statement_table::{ generic::AttestedCandidate as TableAttestedCandidate, @@ -625,6 +627,7 @@ async fn request_candidate_validation( executor_params: ExecutorParams, ) -> Result { let (tx, rx) = oneshot::channel(); + let is_system = candidate_receipt.descriptor.para_id.is_system(); sender .send_message(CandidateValidationMessage::ValidateFromExhaustive { @@ -633,7 +636,11 @@ async fn request_candidate_validation( candidate_receipt, pov, executor_params, - exec_kind: PvfExecKind::Backing, + exec_kind: if is_system { + PvfExecKind::BackingSystemParas + } else { + PvfExecKind::Backing + }, response_sender: tx, }) .await; diff --git a/polkadot/node/core/backing/src/tests/mod.rs b/polkadot/node/core/backing/src/tests/mod.rs index 10eb45b82d12544be09a188493d7114fc99c6d58..d9c1fc9499e50ed29490559ffde2416357d68e70 100644 --- a/polkadot/node/core/backing/src/tests/mod.rs +++ b/polkadot/node/core/backing/src/tests/mod.rs @@ -30,7 +30,7 @@ use polkadot_node_subsystem::{ use polkadot_node_subsystem_test_helpers as test_helpers; use polkadot_primitives::{ node_features, CandidateDescriptor, GroupRotationInfo, HeadData, PersistedValidationData, - PvfExecKind, ScheduledCore, SessionIndex, LEGACY_MIN_BACKING_VOTES, + ScheduledCore, SessionIndex, LEGACY_MIN_BACKING_VOTES, }; use polkadot_primitives_test_helpers::{ dummy_candidate_receipt_bad_sig, dummy_collator, dummy_collator_signature, @@ -434,7 +434,7 @@ async fn assert_validate_from_exhaustive( ) if validation_data == *assert_pvd && validation_code == *assert_validation_code && *pov == *assert_pov && &candidate_receipt.descriptor == assert_candidate.descriptor() && - exec_kind == PvfExecKind::Backing && + exec_kind == PvfExecKind::BackingSystemParas && candidate_receipt.commitments_hash == assert_candidate.commitments.hash() => { response_sender.send(Ok(ValidationResult::Valid( @@ -651,7 +651,7 @@ fn backing_works(#[case] elastic_scaling_mvp: bool) { ) if validation_data == pvd_ab && validation_code == validation_code_ab && *pov == pov_ab && &candidate_receipt.descriptor == candidate_a.descriptor() && - exec_kind == PvfExecKind::Backing && + exec_kind == PvfExecKind::BackingSystemParas && candidate_receipt.commitments_hash == candidate_a_commitments_hash => { response_sender.send(Ok( @@ -1287,7 +1287,7 @@ fn backing_works_while_validation_ongoing() { ) if validation_data == pvd_abc && validation_code == validation_code_abc && *pov == pov_abc && &candidate_receipt.descriptor == candidate_a.descriptor() && - exec_kind == PvfExecKind::Backing && + exec_kind == PvfExecKind::BackingSystemParas && candidate_a_commitments_hash == candidate_receipt.commitments_hash => { // we never validate the candidate. our local node @@ -1454,7 +1454,7 @@ fn backing_misbehavior_works() { ) if validation_data == pvd_a && validation_code == validation_code_a && *pov == pov_a && &candidate_receipt.descriptor == candidate_a.descriptor() && - exec_kind == PvfExecKind::Backing && + exec_kind == PvfExecKind::BackingSystemParas && candidate_a_commitments_hash == candidate_receipt.commitments_hash => { response_sender.send(Ok( @@ -1621,7 +1621,7 @@ fn backing_dont_second_invalid() { ) if validation_data == pvd_a && validation_code == validation_code_a && *pov == pov_block_a && &candidate_receipt.descriptor == candidate_a.descriptor() && - exec_kind == PvfExecKind::Backing && + exec_kind == PvfExecKind::BackingSystemParas && candidate_a.commitments.hash() == candidate_receipt.commitments_hash => { response_sender.send(Ok(ValidationResult::Invalid(InvalidCandidate::BadReturn))).unwrap(); @@ -1661,7 +1661,7 @@ fn backing_dont_second_invalid() { ) if validation_data == pvd_b && validation_code == validation_code_b && *pov == pov_block_b && &candidate_receipt.descriptor == candidate_b.descriptor() && - exec_kind == PvfExecKind::Backing && + exec_kind == PvfExecKind::BackingSystemParas && candidate_b.commitments.hash() == candidate_receipt.commitments_hash => { response_sender.send(Ok( @@ -1788,7 +1788,7 @@ fn backing_second_after_first_fails_works() { ) if validation_data == pvd_a && validation_code == validation_code_a && *pov == pov_a && &candidate_receipt.descriptor == candidate.descriptor() && - exec_kind == PvfExecKind::Backing && + exec_kind == PvfExecKind::BackingSystemParas && candidate.commitments.hash() == candidate_receipt.commitments_hash => { response_sender.send(Ok(ValidationResult::Invalid(InvalidCandidate::BadReturn))).unwrap(); @@ -1932,7 +1932,7 @@ fn backing_works_after_failed_validation() { ) if validation_data == pvd_a && validation_code == validation_code_a && *pov == pov_a && &candidate_receipt.descriptor == candidate.descriptor() && - exec_kind == PvfExecKind::Backing && + exec_kind == PvfExecKind::BackingSystemParas && candidate.commitments.hash() == candidate_receipt.commitments_hash => { response_sender.send(Err(ValidationFailed("Internal test error".into()))).unwrap(); @@ -2211,7 +2211,7 @@ fn retry_works() { ) if validation_data == pvd_a && validation_code == validation_code_a && *pov == pov_a && &candidate_receipt.descriptor == candidate.descriptor() && - exec_kind == PvfExecKind::Backing && + exec_kind == PvfExecKind::BackingSystemParas && candidate.commitments.hash() == candidate_receipt.commitments_hash ); virtual_overseer @@ -2753,7 +2753,7 @@ fn validator_ignores_statements_from_disabled_validators() { ) if validation_data == pvd && validation_code == expected_validation_code && *pov == expected_pov && &candidate_receipt.descriptor == candidate.descriptor() && - exec_kind == PvfExecKind::Backing && + exec_kind == PvfExecKind::BackingSystemParas && candidate_commitments_hash == candidate_receipt.commitments_hash => { response_sender.send(Ok( diff --git a/polkadot/node/core/backing/src/tests/prospective_parachains.rs b/polkadot/node/core/backing/src/tests/prospective_parachains.rs index 15bc0b4a113902ab118bb24f5075eb947b9d874c..57b2fabd43b0606ffe4ea7ffdabd22b2a335e2b7 100644 --- a/polkadot/node/core/backing/src/tests/prospective_parachains.rs +++ b/polkadot/node/core/backing/src/tests/prospective_parachains.rs @@ -276,7 +276,7 @@ async fn assert_validate_seconded_candidate( &validation_code == assert_validation_code && &*pov == assert_pov && &candidate_receipt.descriptor == candidate.descriptor() && - exec_kind == PvfExecKind::Backing && + exec_kind == PvfExecKind::BackingSystemParas && candidate.commitments.hash() == candidate_receipt.commitments_hash => { response_sender.send(Ok(ValidationResult::Valid( diff --git a/polkadot/node/core/bitfield-signing/src/lib.rs b/polkadot/node/core/bitfield-signing/src/lib.rs index e3effb7949eaea0381c556c47eae0a1b6f09dc33..474de1c66abd98cf0f2bfbef89ed10ec6670cc46 100644 --- a/polkadot/node/core/bitfield-signing/src/lib.rs +++ b/polkadot/node/core/bitfield-signing/src/lib.rs @@ -27,10 +27,9 @@ use futures::{ FutureExt, }; use polkadot_node_subsystem::{ - jaeger, messages::{AvailabilityStoreMessage, BitfieldDistributionMessage}, - overseer, ActivatedLeaf, FromOrchestra, OverseerSignal, PerLeafSpan, SpawnedSubsystem, - SubsystemError, SubsystemResult, + overseer, ActivatedLeaf, FromOrchestra, OverseerSignal, SpawnedSubsystem, SubsystemError, + SubsystemResult, }; use polkadot_node_subsystem_util::{ self as util, request_availability_cores, runtime::recv_runtime, Validator, @@ -80,11 +79,8 @@ async fn get_core_availability( core: &CoreState, validator_index: ValidatorIndex, sender: &Mutex<&mut impl overseer::BitfieldSigningSenderTrait>, - span: &jaeger::Span, ) -> Result { if let CoreState::Occupied(core) = core { - let _span = span.child("query-chunk-availability"); - let (tx, rx) = oneshot::channel(); sender .lock() @@ -118,15 +114,12 @@ async fn get_core_availability( /// prone to false negatives) async fn construct_availability_bitfield( relay_parent: Hash, - span: &jaeger::Span, validator_idx: ValidatorIndex, sender: &mut impl overseer::BitfieldSigningSenderTrait, ) -> Result { // get the set of availability cores from the runtime - let availability_cores = { - let _span = span.child("get-availability-cores"); - recv_runtime(request_availability_cores(relay_parent, sender).await).await? - }; + let availability_cores = + { recv_runtime(request_availability_cores(relay_parent, sender).await).await? }; // Wrap the sender in a Mutex to share it between the futures. // @@ -140,7 +133,7 @@ async fn construct_availability_bitfield( let results = future::try_join_all( availability_cores .iter() - .map(|core| get_core_availability(core, validator_idx, &sender, span)), + .map(|core| get_core_availability(core, validator_idx, &sender)), ) .await?; @@ -234,8 +227,6 @@ async fn handle_active_leaves_update( where Sender: overseer::BitfieldSigningSenderTrait, { - let span = PerLeafSpan::new(leaf.span, "bitfield-signing"); - let span_delay = span.child("delay"); let wait_until = Instant::now() + SPAWNED_TASK_DELAY; // now do all the work we can before we need to wait for the availability store @@ -253,28 +244,16 @@ where // SPAWNED_TASK_DELAY each time. let _timer = metrics.time_run(); - drop(span_delay); - let span_availability = span.child("availability"); - - let bitfield = match construct_availability_bitfield( - leaf.hash, - &span_availability, - validator.index(), - &mut sender, - ) - .await - { - Err(Error::Runtime(runtime_err)) => { - // Don't take down the node on runtime API errors. - gum::warn!(target: LOG_TARGET, err = ?runtime_err, "Encountered a runtime API error"); - return Ok(()) - }, - Err(err) => return Err(err), - Ok(bitfield) => bitfield, - }; - - drop(span_availability); - let span_signing = span.child("signing"); + let bitfield = + match construct_availability_bitfield(leaf.hash, validator.index(), &mut sender).await { + Err(Error::Runtime(runtime_err)) => { + // Don't take down the node on runtime API errors. + gum::warn!(target: LOG_TARGET, err = ?runtime_err, "Encountered a runtime API error"); + return Ok(()) + }, + Err(err) => return Err(err), + Ok(bitfield) => bitfield, + }; let signed_bitfield = match validator.sign(keystore, bitfield).map_err(|e| Error::Keystore(e))? { @@ -290,9 +269,6 @@ where metrics.on_bitfield_signed(); - drop(span_signing); - let _span_gossip = span.child("gossip"); - sender .send_message(BitfieldDistributionMessage::DistributeBitfield(leaf.hash, signed_bitfield)) .await; diff --git a/polkadot/node/core/bitfield-signing/src/tests.rs b/polkadot/node/core/bitfield-signing/src/tests.rs index eeaa524d1c631101915ad0209c67307499bab73d..c08018375cf30834890c1d2ef4e1e740d321f1c8 100644 --- a/polkadot/node/core/bitfield-signing/src/tests.rs +++ b/polkadot/node/core/bitfield-signing/src/tests.rs @@ -40,13 +40,8 @@ fn construct_availability_bitfield_works() { let validator_index = ValidatorIndex(1u32); let (mut sender, mut receiver) = polkadot_node_subsystem_test_helpers::sender_receiver(); - let future = construct_availability_bitfield( - relay_parent, - &jaeger::Span::Disabled, - validator_index, - &mut sender, - ) - .fuse(); + let future = + construct_availability_bitfield(relay_parent, validator_index, &mut sender).fuse(); pin_mut!(future); let hash_a = CandidateHash(Hash::repeat_byte(1)); diff --git a/polkadot/node/core/candidate-validation/src/lib.rs b/polkadot/node/core/candidate-validation/src/lib.rs index 103d29e8d269429be3dc94fa3f0e4d85d4c98a67..e875be9b5df9cb35612430547c5bf6fe0ffc4d16 100644 --- a/polkadot/node/core/candidate-validation/src/lib.rs +++ b/polkadot/node/core/candidate-validation/src/lib.rs @@ -31,8 +31,8 @@ use polkadot_node_primitives::{InvalidCandidate, PoV, ValidationResult}; use polkadot_node_subsystem::{ errors::RuntimeApiError, messages::{ - CandidateValidationMessage, PreCheckOutcome, RuntimeApiMessage, RuntimeApiRequest, - ValidationFailed, + CandidateValidationMessage, PreCheckOutcome, PvfExecKind, RuntimeApiMessage, + RuntimeApiRequest, ValidationFailed, }, overseer, FromOrchestra, OverseerSignal, SpawnedSubsystem, SubsystemError, SubsystemResult, SubsystemSender, @@ -46,8 +46,9 @@ use polkadot_primitives::{ DEFAULT_LENIENT_PREPARATION_TIMEOUT, DEFAULT_PRECHECK_PREPARATION_TIMEOUT, }, AuthorityDiscoveryId, CandidateCommitments, CandidateDescriptor, CandidateEvent, - CandidateReceipt, ExecutorParams, Hash, OccupiedCoreAssumption, PersistedValidationData, - PvfExecKind, PvfPrepKind, SessionIndex, ValidationCode, ValidationCodeHash, + CandidateReceipt, ExecutorParams, Hash, PersistedValidationData, + PvfExecKind as RuntimePvfExecKind, PvfPrepKind, SessionIndex, ValidationCode, + ValidationCodeHash, ValidatorId, }; use sp_application_crypto::{AppCrypto, ByteArray}; use sp_keystore::KeystorePtr; @@ -83,8 +84,7 @@ const PVF_APPROVAL_EXECUTION_RETRY_DELAY: Duration = Duration::from_secs(3); const PVF_APPROVAL_EXECUTION_RETRY_DELAY: Duration = Duration::from_millis(200); // The task queue size is chosen to be somewhat bigger than the PVF host incoming queue size -// to allow exhaustive validation messages to fall through in case the tasks are clogged with -// `ValidateFromChainState` messages awaiting data from the runtime +// to allow exhaustive validation messages to fall through in case the tasks are clogged const TASK_LIMIT: usize = 30; /// Configuration for the candidate validation subsystem @@ -155,30 +155,6 @@ where S: SubsystemSender, { match msg { - CandidateValidationMessage::ValidateFromChainState { - candidate_receipt, - pov, - executor_params, - exec_kind, - response_sender, - .. - } => async move { - let _timer = metrics.time_validate_from_chain_state(); - let res = validate_from_chain_state( - &mut sender, - validation_host, - candidate_receipt, - pov, - executor_params, - exec_kind, - &metrics, - ) - .await; - - metrics.on_validation_event(&res); - let _ = response_sender.send(res); - } - .boxed(), CandidateValidationMessage::ValidateFromExhaustive { validation_data, validation_code, @@ -427,14 +403,15 @@ where .iter() .any(|v| keystore.has_keys(&[(v.to_raw_vec(), AuthorityDiscoveryId::ID)])); - let is_present_authority = session_info - .discovery_keys + // We could've checked discovery_keys but on Kusama validators.len() < discovery_keys.len(). + let is_present_validator = session_info + .validators .iter() - .any(|v| keystore.has_keys(&[(v.to_raw_vec(), AuthorityDiscoveryId::ID)])); + .any(|v| keystore.has_keys(&[(v.to_raw_vec(), ValidatorId::ID)])); // There is still a chance to be a previous session authority, but this extra work does not // affect the finalization. - is_past_present_or_future_authority && !is_present_authority + is_past_present_or_future_authority && !is_present_validator } // Sends PVF with unknown code hashes to the validation host returning the list of code hashes sent. @@ -656,170 +633,6 @@ where } } -#[derive(Debug)] -enum AssumptionCheckOutcome { - Matches(PersistedValidationData, ValidationCode), - DoesNotMatch, - BadRequest, -} - -async fn check_assumption_validation_data( - sender: &mut Sender, - descriptor: &CandidateDescriptor, - assumption: OccupiedCoreAssumption, -) -> AssumptionCheckOutcome -where - Sender: SubsystemSender, -{ - let validation_data = { - let (tx, rx) = oneshot::channel(); - let d = runtime_api_request( - sender, - descriptor.relay_parent, - RuntimeApiRequest::PersistedValidationData(descriptor.para_id, assumption, tx), - rx, - ) - .await; - - match d { - Ok(None) | Err(RuntimeRequestFailed) => return AssumptionCheckOutcome::BadRequest, - Ok(Some(d)) => d, - } - }; - - let persisted_validation_data_hash = validation_data.hash(); - - if descriptor.persisted_validation_data_hash == persisted_validation_data_hash { - let (code_tx, code_rx) = oneshot::channel(); - let validation_code = runtime_api_request( - sender, - descriptor.relay_parent, - RuntimeApiRequest::ValidationCode(descriptor.para_id, assumption, code_tx), - code_rx, - ) - .await; - - match validation_code { - Ok(None) | Err(RuntimeRequestFailed) => AssumptionCheckOutcome::BadRequest, - Ok(Some(v)) => AssumptionCheckOutcome::Matches(validation_data, v), - } - } else { - AssumptionCheckOutcome::DoesNotMatch - } -} - -async fn find_assumed_validation_data( - sender: &mut Sender, - descriptor: &CandidateDescriptor, -) -> AssumptionCheckOutcome -where - Sender: SubsystemSender, -{ - // The candidate descriptor has a `persisted_validation_data_hash` which corresponds to - // one of up to two possible values that we can derive from the state of the - // relay-parent. We can fetch these values by getting the persisted validation data - // based on the different `OccupiedCoreAssumption`s. - - const ASSUMPTIONS: &[OccupiedCoreAssumption] = &[ - OccupiedCoreAssumption::Included, - OccupiedCoreAssumption::TimedOut, - // `TimedOut` and `Free` both don't perform any speculation and therefore should be the - // same for our purposes here. In other words, if `TimedOut` matched then the `Free` must - // be matched as well. - ]; - - // Consider running these checks in parallel to reduce validation latency. - for assumption in ASSUMPTIONS { - let outcome = check_assumption_validation_data(sender, descriptor, *assumption).await; - - match outcome { - AssumptionCheckOutcome::Matches(_, _) => return outcome, - AssumptionCheckOutcome::BadRequest => return outcome, - AssumptionCheckOutcome::DoesNotMatch => continue, - } - } - - AssumptionCheckOutcome::DoesNotMatch -} - -/// Returns validation data for a given candidate. -pub async fn find_validation_data( - sender: &mut Sender, - descriptor: &CandidateDescriptor, -) -> Result, ValidationFailed> -where - Sender: SubsystemSender, -{ - match find_assumed_validation_data(sender, &descriptor).await { - AssumptionCheckOutcome::Matches(validation_data, validation_code) => - Ok(Some((validation_data, validation_code))), - AssumptionCheckOutcome::DoesNotMatch => { - // If neither the assumption of the occupied core having the para included or the - // assumption of the occupied core timing out are valid, then the - // persisted_validation_data_hash in the descriptor is not based on the relay parent and - // is thus invalid. - Ok(None) - }, - AssumptionCheckOutcome::BadRequest => - Err(ValidationFailed("Assumption Check: Bad request".into())), - } -} - -async fn validate_from_chain_state( - sender: &mut Sender, - validation_host: ValidationHost, - candidate_receipt: CandidateReceipt, - pov: Arc, - executor_params: ExecutorParams, - exec_kind: PvfExecKind, - metrics: &Metrics, -) -> Result -where - Sender: SubsystemSender, -{ - let mut new_sender = sender.clone(); - let (validation_data, validation_code) = - match find_validation_data(&mut new_sender, &candidate_receipt.descriptor).await? { - Some((validation_data, validation_code)) => (validation_data, validation_code), - None => return Ok(ValidationResult::Invalid(InvalidCandidate::BadParent)), - }; - - let validation_result = validate_candidate_exhaustive( - validation_host, - validation_data, - validation_code, - candidate_receipt.clone(), - pov, - executor_params, - exec_kind, - metrics, - ) - .await; - - if let Ok(ValidationResult::Valid(ref outputs, _)) = validation_result { - let (tx, rx) = oneshot::channel(); - match runtime_api_request( - sender, - candidate_receipt.descriptor.relay_parent, - RuntimeApiRequest::CheckValidationOutputs( - candidate_receipt.descriptor.para_id, - outputs.clone(), - tx, - ), - rx, - ) - .await - { - Ok(true) => {}, - Ok(false) => return Ok(ValidationResult::Invalid(InvalidCandidate::InvalidOutputs)), - Err(RuntimeRequestFailed) => - return Err(ValidationFailed("Check Validation Outputs: Bad request".into())), - } - } - - validation_result -} - async fn validate_candidate_exhaustive( mut validation_backend: impl ValidationBackend + Send, persisted_validation_data: PersistedValidationData, @@ -855,9 +668,9 @@ async fn validate_candidate_exhaustive( let result = match exec_kind { // Retry is disabled to reduce the chance of nondeterministic blocks getting backed and // honest backers getting slashed. - PvfExecKind::Backing => { + PvfExecKind::Backing | PvfExecKind::BackingSystemParas => { let prep_timeout = pvf_prep_timeout(&executor_params, PvfPrepKind::Prepare); - let exec_timeout = pvf_exec_timeout(&executor_params, exec_kind); + let exec_timeout = pvf_exec_timeout(&executor_params, exec_kind.into()); let pvf = PvfPrepData::from_code( validation_code.0, executor_params, @@ -871,20 +684,22 @@ async fn validate_candidate_exhaustive( exec_timeout, persisted_validation_data.clone(), pov, - polkadot_node_core_pvf::Priority::Normal, + exec_kind.into(), + exec_kind, ) .await }, - PvfExecKind::Approval => + PvfExecKind::Approval | PvfExecKind::Dispute => validation_backend .validate_candidate_with_retry( validation_code.0, - pvf_exec_timeout(&executor_params, exec_kind), + pvf_exec_timeout(&executor_params, exec_kind.into()), persisted_validation_data.clone(), pov, executor_params, PVF_APPROVAL_EXECUTION_RETRY_DELAY, - polkadot_node_core_pvf::Priority::Critical, + exec_kind.into(), + exec_kind, ) .await, }; @@ -972,6 +787,8 @@ trait ValidationBackend { pov: Arc, // The priority for the preparation job. prepare_priority: polkadot_node_core_pvf::Priority, + // The kind for the execution job. + exec_kind: PvfExecKind, ) -> Result; /// Tries executing a PVF. Will retry once if an error is encountered that may have @@ -992,6 +809,8 @@ trait ValidationBackend { retry_delay: Duration, // The priority for the preparation job. prepare_priority: polkadot_node_core_pvf::Priority, + // The kind for the execution job. + exec_kind: PvfExecKind, ) -> Result { let prep_timeout = pvf_prep_timeout(&executor_params, PvfPrepKind::Prepare); // Construct the PVF a single time, since it is an expensive operation. Cloning it is cheap. @@ -1013,6 +832,7 @@ trait ValidationBackend { pvd.clone(), pov.clone(), prepare_priority, + exec_kind, ) .await; if validation_result.is_ok() { @@ -1093,6 +913,7 @@ trait ValidationBackend { pvd.clone(), pov.clone(), prepare_priority, + exec_kind, ) .await; } @@ -1117,9 +938,13 @@ impl ValidationBackend for ValidationHost { pov: Arc, // The priority for the preparation job. prepare_priority: polkadot_node_core_pvf::Priority, + // The kind for the execution job. + exec_kind: PvfExecKind, ) -> Result { let (tx, rx) = oneshot::channel(); - if let Err(err) = self.execute_pvf(pvf, exec_timeout, pvd, pov, prepare_priority, tx).await + if let Err(err) = self + .execute_pvf(pvf, exec_timeout, pvd, pov, prepare_priority, exec_kind, tx) + .await { return Err(InternalValidationError::HostCommunication(format!( "cannot send pvf to the validation host, it might have shut down: {:?}", @@ -1211,12 +1036,12 @@ fn pvf_prep_timeout(executor_params: &ExecutorParams, kind: PvfPrepKind) -> Dura /// This should be much longer than the backing execution timeout to ensure that in the /// absence of extremely large disparities between hardware, blocks that pass backing are /// considered executable by approval checkers or dispute participants. -fn pvf_exec_timeout(executor_params: &ExecutorParams, kind: PvfExecKind) -> Duration { +fn pvf_exec_timeout(executor_params: &ExecutorParams, kind: RuntimePvfExecKind) -> Duration { if let Some(timeout) = executor_params.pvf_exec_timeout(kind) { return timeout } match kind { - PvfExecKind::Backing => DEFAULT_BACKING_EXECUTION_TIMEOUT, - PvfExecKind::Approval => DEFAULT_APPROVAL_EXECUTION_TIMEOUT, + RuntimePvfExecKind::Backing => DEFAULT_BACKING_EXECUTION_TIMEOUT, + RuntimePvfExecKind::Approval => DEFAULT_APPROVAL_EXECUTION_TIMEOUT, } } diff --git a/polkadot/node/core/candidate-validation/src/metrics.rs b/polkadot/node/core/candidate-validation/src/metrics.rs index 1459907aa5999980dd82ec0e08a5839959167cef..76ccd56555f928188401d6a916f28c87768b4efd 100644 --- a/polkadot/node/core/candidate-validation/src/metrics.rs +++ b/polkadot/node/core/candidate-validation/src/metrics.rs @@ -20,7 +20,6 @@ use polkadot_node_metrics::metrics::{self, prometheus}; #[derive(Clone)] pub(crate) struct MetricsInner { pub(crate) validation_requests: prometheus::CounterVec, - pub(crate) validate_from_chain_state: prometheus::Histogram, pub(crate) validate_from_exhaustive: prometheus::Histogram, pub(crate) validate_candidate_exhaustive: prometheus::Histogram, } @@ -46,13 +45,6 @@ impl Metrics { } } - /// Provide a timer for `validate_from_chain_state` which observes on drop. - pub fn time_validate_from_chain_state( - &self, - ) -> Option { - self.0.as_ref().map(|metrics| metrics.validate_from_chain_state.start_timer()) - } - /// Provide a timer for `validate_from_exhaustive` which observes on drop. pub fn time_validate_from_exhaustive( &self, @@ -83,13 +75,6 @@ impl metrics::Metrics for Metrics { )?, registry, )?, - validate_from_chain_state: prometheus::register( - prometheus::Histogram::with_opts(prometheus::HistogramOpts::new( - "polkadot_parachain_candidate_validation_validate_from_chain_state", - "Time spent within `candidate_validation::validate_from_chain_state`", - ))?, - registry, - )?, validate_from_exhaustive: prometheus::register( prometheus::Histogram::with_opts(prometheus::HistogramOpts::new( "polkadot_parachain_candidate_validation_validate_from_exhaustive", diff --git a/polkadot/node/core/candidate-validation/src/tests.rs b/polkadot/node/core/candidate-validation/src/tests.rs index 55282fdf4ee1da540f8aa95f272ccee0352a7390..2f7baf4abb61e8d6a49d23d2f12d4cd058f8bc5a 100644 --- a/polkadot/node/core/candidate-validation/src/tests.rs +++ b/polkadot/node/core/candidate-validation/src/tests.rs @@ -17,6 +17,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use super::*; +use crate::PvfExecKind; use assert_matches::assert_matches; use futures::executor; use polkadot_node_core_pvf::PrepareError; @@ -25,16 +26,68 @@ use polkadot_node_subsystem::messages::AllMessages; use polkadot_node_subsystem_util::reexports::SubsystemContext; use polkadot_overseer::ActivatedLeaf; use polkadot_primitives::{ - CoreIndex, GroupIndex, HeadData, Id as ParaId, IndexedVec, SessionInfo, UpwardMessage, - ValidatorId, ValidatorIndex, + CoreIndex, GroupIndex, HeadData, Id as ParaId, OccupiedCoreAssumption, SessionInfo, + UpwardMessage, ValidatorId, }; use polkadot_primitives_test_helpers::{ dummy_collator, dummy_collator_signature, dummy_hash, make_valid_candidate_descriptor, }; -use sp_core::testing::TaskExecutor; +use sp_core::{sr25519::Public, testing::TaskExecutor}; use sp_keyring::Sr25519Keyring; use sp_keystore::{testing::MemoryKeystore, Keystore}; +#[derive(Debug)] +enum AssumptionCheckOutcome { + Matches(PersistedValidationData, ValidationCode), + DoesNotMatch, + BadRequest, +} + +async fn check_assumption_validation_data( + sender: &mut Sender, + descriptor: &CandidateDescriptor, + assumption: OccupiedCoreAssumption, +) -> AssumptionCheckOutcome +where + Sender: SubsystemSender, +{ + let validation_data = { + let (tx, rx) = oneshot::channel(); + let d = runtime_api_request( + sender, + descriptor.relay_parent, + RuntimeApiRequest::PersistedValidationData(descriptor.para_id, assumption, tx), + rx, + ) + .await; + + match d { + Ok(None) | Err(RuntimeRequestFailed) => return AssumptionCheckOutcome::BadRequest, + Ok(Some(d)) => d, + } + }; + + let persisted_validation_data_hash = validation_data.hash(); + + if descriptor.persisted_validation_data_hash == persisted_validation_data_hash { + let (code_tx, code_rx) = oneshot::channel(); + let validation_code = runtime_api_request( + sender, + descriptor.relay_parent, + RuntimeApiRequest::ValidationCode(descriptor.para_id, assumption, code_tx), + code_rx, + ) + .await; + + match validation_code { + Ok(None) | Err(RuntimeRequestFailed) => AssumptionCheckOutcome::BadRequest, + Ok(Some(v)) => AssumptionCheckOutcome::Matches(validation_data, v), + } + } else { + AssumptionCheckOutcome::DoesNotMatch + } +} + #[test] fn correctly_checks_included_assumption() { let validation_data: PersistedValidationData = Default::default(); @@ -389,6 +442,7 @@ impl ValidationBackend for MockValidateCandidateBackend { _pvd: Arc, _pov: Arc, _prepare_priority: polkadot_node_core_pvf::Priority, + _exec_kind: PvfExecKind, ) -> Result { // This is expected to panic if called more times than expected, indicating an error in the // test. @@ -971,6 +1025,7 @@ impl ValidationBackend for MockPreCheckBackend { _pvd: Arc, _pov: Arc, _prepare_priority: polkadot_node_core_pvf::Priority, + _exec_kind: PvfExecKind, ) -> Result { unreachable!() } @@ -1125,6 +1180,7 @@ impl ValidationBackend for MockHeadsUp { _pvd: Arc, _pov: Arc, _prepare_priority: polkadot_node_core_pvf::Priority, + _exec_kind: PvfExecKind, ) -> Result { unreachable!() } @@ -1163,7 +1219,6 @@ fn dummy_active_leaves_update(hash: Hash) -> ActiveLeavesUpdate { hash, number: 10, unpin_handle: polkadot_node_subsystem_test_helpers::mock::dummy_unpin_handle(hash), - span: Arc::new(overseer::jaeger::Span::Disabled), }), ..Default::default() } @@ -1194,10 +1249,10 @@ fn dummy_candidate_backed( ) } -fn dummy_session_info(discovery_keys: Vec) -> SessionInfo { +fn dummy_session_info(keys: Vec) -> SessionInfo { SessionInfo { - validators: IndexedVec::::from(vec![]), - discovery_keys, + validators: keys.iter().cloned().map(Into::into).collect(), + discovery_keys: keys.iter().cloned().map(Into::into).collect(), assignment_keys: vec![], validator_groups: Default::default(), n_cores: 4u32, @@ -1246,7 +1301,7 @@ fn maybe_prepare_validation_golden_path() { ctx_handle.recv().await, AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::SessionInfo(index, tx))) => { assert_eq!(index, 1); - let _ = tx.send(Ok(Some(dummy_session_info(vec![Sr25519Keyring::Bob.public().into()])))); + let _ = tx.send(Ok(Some(dummy_session_info(vec![Sr25519Keyring::Bob.public()])))); } ); @@ -1364,7 +1419,7 @@ fn maybe_prepare_validation_resets_state_on_a_new_session() { ctx_handle.recv().await, AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::SessionInfo(index, tx))) => { assert_eq!(index, 2); - let _ = tx.send(Ok(Some(dummy_session_info(vec![Sr25519Keyring::Bob.public().into()])))); + let _ = tx.send(Ok(Some(dummy_session_info(vec![Sr25519Keyring::Bob.public()])))); } ); }; @@ -1510,7 +1565,7 @@ fn maybe_prepare_validation_does_not_prepare_pvfs_if_not_a_validator_in_the_next ctx_handle.recv().await, AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::SessionInfo(index, tx))) => { assert_eq!(index, 1); - let _ = tx.send(Ok(Some(dummy_session_info(vec![Sr25519Keyring::Bob.public().into()])))); + let _ = tx.send(Ok(Some(dummy_session_info(vec![Sr25519Keyring::Bob.public()])))); } ); }; @@ -1557,7 +1612,7 @@ fn maybe_prepare_validation_does_not_prepare_pvfs_if_a_validator_in_the_current_ ctx_handle.recv().await, AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::SessionInfo(index, tx))) => { assert_eq!(index, 1); - let _ = tx.send(Ok(Some(dummy_session_info(vec![Sr25519Keyring::Alice.public().into()])))); + let _ = tx.send(Ok(Some(dummy_session_info(vec![Sr25519Keyring::Alice.public()])))); } ); }; @@ -1604,7 +1659,7 @@ fn maybe_prepare_validation_prepares_a_limited_number_of_pvfs() { ctx_handle.recv().await, AllMessages::RuntimeApi(RuntimeApiMessage::Request(_, RuntimeApiRequest::SessionInfo(index, tx))) => { assert_eq!(index, 1); - let _ = tx.send(Ok(Some(dummy_session_info(vec![Sr25519Keyring::Bob.public().into()])))); + let _ = tx.send(Ok(Some(dummy_session_info(vec![Sr25519Keyring::Bob.public()])))); } ); diff --git a/polkadot/node/core/dispute-coordinator/src/initialized.rs b/polkadot/node/core/dispute-coordinator/src/initialized.rs index 5096fe5e6891cd6de7c93036837928fce6cb748a..9cf9047b72734cec0d665675170e7e93ef42f31f 100644 --- a/polkadot/node/core/dispute-coordinator/src/initialized.rs +++ b/polkadot/node/core/dispute-coordinator/src/initialized.rs @@ -34,8 +34,9 @@ use polkadot_node_primitives::{ }; use polkadot_node_subsystem::{ messages::{ - ApprovalVotingMessage, BlockDescription, ChainSelectionMessage, DisputeCoordinatorMessage, - DisputeDistributionMessage, ImportStatementsResult, + ApprovalVotingMessage, ApprovalVotingParallelMessage, BlockDescription, + ChainSelectionMessage, DisputeCoordinatorMessage, DisputeDistributionMessage, + ImportStatementsResult, }, overseer, ActivatedLeaf, ActiveLeavesUpdate, FromOrchestra, OverseerSignal, RuntimeApiError, }; @@ -117,6 +118,7 @@ pub(crate) struct Initialized { /// `CHAIN_IMPORT_MAX_BATCH_SIZE` and put the rest here for later processing. chain_import_backlog: VecDeque, metrics: Metrics, + approval_voting_parallel_enabled: bool, } #[overseer::contextbounds(DisputeCoordinator, prefix = self::overseer)] @@ -130,7 +132,13 @@ impl Initialized { highest_session_seen: SessionIndex, gaps_in_cache: bool, ) -> Self { - let DisputeCoordinatorSubsystem { config: _, store: _, keystore, metrics } = subsystem; + let DisputeCoordinatorSubsystem { + config: _, + store: _, + keystore, + metrics, + approval_voting_parallel_enabled, + } = subsystem; let (participation_sender, participation_receiver) = mpsc::channel(1); let participation = Participation::new(participation_sender, metrics.clone()); @@ -148,6 +156,7 @@ impl Initialized { participation_receiver, chain_import_backlog: VecDeque::new(), metrics, + approval_voting_parallel_enabled, } } @@ -1059,9 +1068,21 @@ impl Initialized { // 4. We are waiting (and blocking the whole subsystem) on a response right after - // therefore even with all else failing we will never have more than // one message in flight at any given time. - ctx.send_unbounded_message( - ApprovalVotingMessage::GetApprovalSignaturesForCandidate(candidate_hash, tx), - ); + if self.approval_voting_parallel_enabled { + ctx.send_unbounded_message( + ApprovalVotingParallelMessage::GetApprovalSignaturesForCandidate( + candidate_hash, + tx, + ), + ); + } else { + ctx.send_unbounded_message( + ApprovalVotingMessage::GetApprovalSignaturesForCandidate( + candidate_hash, + tx, + ), + ); + } match rx.await { Err(_) => { gum::warn!( diff --git a/polkadot/node/core/dispute-coordinator/src/lib.rs b/polkadot/node/core/dispute-coordinator/src/lib.rs index 34d9ddf3a97c0be0c7f6e3b43cbc3a1b126b2423..84408eb9630539df3bace2ad5a0d1c25c0848018 100644 --- a/polkadot/node/core/dispute-coordinator/src/lib.rs +++ b/polkadot/node/core/dispute-coordinator/src/lib.rs @@ -122,6 +122,7 @@ pub struct DisputeCoordinatorSubsystem { store: Arc, keystore: Arc, metrics: Metrics, + approval_voting_parallel_enabled: bool, } /// Configuration for the dispute coordinator subsystem. @@ -164,8 +165,9 @@ impl DisputeCoordinatorSubsystem { config: Config, keystore: Arc, metrics: Metrics, + approval_voting_parallel_enabled: bool, ) -> Self { - Self { store, config, keystore, metrics } + Self { store, config, keystore, metrics, approval_voting_parallel_enabled } } /// Initialize and afterwards run `Initialized::run`. diff --git a/polkadot/node/core/dispute-coordinator/src/participation/mod.rs b/polkadot/node/core/dispute-coordinator/src/participation/mod.rs index b58ce570f8fff767a975487364cc6fe826c329e3..2220f65e20a7ca2908e0b97ce1e3841fa7186814 100644 --- a/polkadot/node/core/dispute-coordinator/src/participation/mod.rs +++ b/polkadot/node/core/dispute-coordinator/src/participation/mod.rs @@ -27,13 +27,11 @@ use futures_timer::Delay; use polkadot_node_primitives::ValidationResult; use polkadot_node_subsystem::{ - messages::{AvailabilityRecoveryMessage, CandidateValidationMessage}, + messages::{AvailabilityRecoveryMessage, CandidateValidationMessage, PvfExecKind}, overseer, ActiveLeavesUpdate, RecoveryError, }; use polkadot_node_subsystem_util::runtime::get_validation_code_by_hash; -use polkadot_primitives::{ - BlockNumber, CandidateHash, CandidateReceipt, Hash, PvfExecKind, SessionIndex, -}; +use polkadot_primitives::{BlockNumber, CandidateHash, CandidateReceipt, Hash, SessionIndex}; use crate::LOG_TARGET; @@ -387,7 +385,7 @@ async fn participate( candidate_receipt: req.candidate_receipt().clone(), pov: available_data.pov, executor_params: req.executor_params(), - exec_kind: PvfExecKind::Approval, + exec_kind: PvfExecKind::Dispute, response_sender: validation_tx, }) .await; diff --git a/polkadot/node/core/dispute-coordinator/src/participation/tests.rs b/polkadot/node/core/dispute-coordinator/src/participation/tests.rs index a80553828ac69184d89695a3fe4ccc716e11b83f..a6ab6f16df05e1c20b84fe0c62e446607b983a71 100644 --- a/polkadot/node/core/dispute-coordinator/src/participation/tests.rs +++ b/polkadot/node/core/dispute-coordinator/src/participation/tests.rs @@ -26,7 +26,7 @@ use codec::Encode; use polkadot_node_primitives::{AvailableData, BlockData, InvalidCandidate, PoV}; use polkadot_node_subsystem::{ messages::{ - AllMessages, ChainApiMessage, DisputeCoordinatorMessage, RuntimeApiMessage, + AllMessages, ChainApiMessage, DisputeCoordinatorMessage, PvfExecKind, RuntimeApiMessage, RuntimeApiRequest, }, ActiveLeavesUpdate, SpawnGlue, @@ -116,7 +116,7 @@ pub async fn participation_full_happy_path( ctx_handle.recv().await, AllMessages::CandidateValidation( CandidateValidationMessage::ValidateFromExhaustive { candidate_receipt, exec_kind, response_sender, .. } - ) if exec_kind == PvfExecKind::Approval => { + ) if exec_kind == PvfExecKind::Dispute => { if expected_commitments_hash != candidate_receipt.commitments_hash { response_sender.send(Ok(ValidationResult::Invalid(InvalidCandidate::CommitmentsHashMismatch))).unwrap(); } else { @@ -450,7 +450,7 @@ fn cast_invalid_vote_if_validation_fails_or_is_invalid() { ctx_handle.recv().await, AllMessages::CandidateValidation( CandidateValidationMessage::ValidateFromExhaustive { exec_kind, response_sender, .. } - ) if exec_kind == PvfExecKind::Approval => { + ) if exec_kind == PvfExecKind::Dispute => { response_sender.send(Ok(ValidationResult::Invalid(InvalidCandidate::Timeout))).unwrap(); }, "overseer did not receive candidate validation message", @@ -487,7 +487,7 @@ fn cast_invalid_vote_if_commitments_dont_match() { ctx_handle.recv().await, AllMessages::CandidateValidation( CandidateValidationMessage::ValidateFromExhaustive { exec_kind, response_sender, .. } - ) if exec_kind == PvfExecKind::Approval => { + ) if exec_kind == PvfExecKind::Dispute => { response_sender.send(Ok(ValidationResult::Invalid(InvalidCandidate::CommitmentsHashMismatch))).unwrap(); }, "overseer did not receive candidate validation message", @@ -524,7 +524,7 @@ fn cast_valid_vote_if_validation_passes() { ctx_handle.recv().await, AllMessages::CandidateValidation( CandidateValidationMessage::ValidateFromExhaustive { exec_kind, response_sender, .. } - ) if exec_kind == PvfExecKind::Approval => { + ) if exec_kind == PvfExecKind::Dispute => { response_sender.send(Ok(ValidationResult::Valid(dummy_candidate_commitments(None), PersistedValidationData::default()))).unwrap(); }, "overseer did not receive candidate validation message", diff --git a/polkadot/node/core/dispute-coordinator/src/tests.rs b/polkadot/node/core/dispute-coordinator/src/tests.rs index f97a625a9528e1d6034500ee1e2fd2447af98de4..48762a1d80be9220cf27a14b173d12129243985a 100644 --- a/polkadot/node/core/dispute-coordinator/src/tests.rs +++ b/polkadot/node/core/dispute-coordinator/src/tests.rs @@ -580,6 +580,7 @@ impl TestState { self.config, self.subsystem_keystore.clone(), Metrics::default(), + false, ); let backend = DbBackend::new(self.db.clone(), self.config.column_config(), Metrics::default()); @@ -2796,7 +2797,7 @@ fn participation_with_onchain_disabling_confirmed() { }) .await; - handle_disabled_validators_queries(&mut virtual_overseer, vec![]).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)); diff --git a/polkadot/node/core/prospective-parachains/src/fragment_chain/tests.rs b/polkadot/node/core/prospective-parachains/src/fragment_chain/tests.rs index 9886d19e5224efddf1ec13b630c9a4af2bd079e4..3332cbeb03cbbb47c1fe3603f3d4a5a094c7a676 100644 --- a/polkadot/node/core/prospective-parachains/src/fragment_chain/tests.rs +++ b/polkadot/node/core/prospective-parachains/src/fragment_chain/tests.rs @@ -1433,7 +1433,7 @@ fn test_find_ancestor_path_and_find_backable_chain() { // Now back all candidates. Back them in a random order. The result should always be the same. let mut candidates_shuffled = candidates.clone(); candidates_shuffled.shuffle(&mut thread_rng()); - for candidate in candidates.iter() { + for candidate in candidates_shuffled.iter() { chain.candidate_backed(candidate); storage.mark_backed(candidate); } diff --git a/polkadot/node/core/provisioner/src/lib.rs b/polkadot/node/core/provisioner/src/lib.rs index ffc5859b7756cad5a2818138d6c8921a617f36ea..9a06d9cff0cc3c435e2d06f3245220640faccbd1 100644 --- a/polkadot/node/core/provisioner/src/lib.rs +++ b/polkadot/node/core/provisioner/src/lib.rs @@ -27,13 +27,12 @@ use futures_timer::Delay; use schnellru::{ByLength, LruMap}; use polkadot_node_subsystem::{ - jaeger, messages::{ Ancestors, CandidateBackingMessage, ChainApiMessage, ProspectiveParachainsMessage, ProvisionableData, ProvisionerInherentData, ProvisionerMessage, RuntimeApiRequest, }, - overseer, ActivatedLeaf, ActiveLeavesUpdate, FromOrchestra, OverseerSignal, PerLeafSpan, - SpawnedSubsystem, SubsystemError, + overseer, ActivatedLeaf, ActiveLeavesUpdate, FromOrchestra, OverseerSignal, SpawnedSubsystem, + SubsystemError, }; use polkadot_node_subsystem_util::{ has_required_runtime, request_availability_cores, request_persisted_validation_data, @@ -95,13 +94,10 @@ pub struct PerRelayParent { signed_bitfields: Vec, is_inherent_ready: bool, awaiting_inherent: Vec>, - span: PerLeafSpan, } impl PerRelayParent { fn new(leaf: ActivatedLeaf, per_session: &PerSession) -> Self { - let span = PerLeafSpan::new(leaf.span.clone(), "provisioner"); - Self { leaf, backed_candidates: Vec::new(), @@ -110,7 +106,6 @@ impl PerRelayParent { signed_bitfields: Vec::new(), is_inherent_ready: false, awaiting_inherent: Vec::new(), - span, } } } @@ -270,12 +265,11 @@ async fn handle_communication( }, ProvisionerMessage::ProvisionableData(relay_parent, data) => { if let Some(state) = per_relay_parent.get_mut(&relay_parent) { - let span = state.span.child("provisionable-data"); let _timer = metrics.time_provisionable_data(); gum::trace!(target: LOG_TARGET, ?relay_parent, "Received provisionable data: {:?}", &data); - note_provisionable_data(state, &span, data); + note_provisionable_data(state, data); } }, } @@ -295,12 +289,10 @@ async fn send_inherent_data_bg( 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(); let bg = async move { - let _span = span; let _timer = metrics.time_request_inherent_data(); gum::trace!( @@ -359,7 +351,6 @@ async fn send_inherent_data_bg( fn note_provisionable_data( per_relay_parent: &mut PerRelayParent, - span: &jaeger::Span, provisionable_data: ProvisionableData, ) { match provisionable_data { @@ -373,10 +364,7 @@ fn note_provisionable_data( para = ?backed_candidate.descriptor().para_id, "noted backed candidate", ); - let _span = span - .child("provisionable-backed") - .with_candidate(candidate_hash) - .with_para_id(backed_candidate.descriptor().para_id); + per_relay_parent.backed_candidates.push(backed_candidate); }, // We choose not to punish these forms of misbehavior for the time being. diff --git a/polkadot/node/core/pvf/Cargo.toml b/polkadot/node/core/pvf/Cargo.toml index d603af04bf061fc16dfb00979e3aaf85b21adb60..13fcdc69a99a2b925cb054902ce8fddc17c3cf4f 100644 --- a/polkadot/node/core/pvf/Cargo.toml +++ b/polkadot/node/core/pvf/Cargo.toml @@ -24,6 +24,7 @@ slotmap = { workspace = true } tempfile = { workspace = true } thiserror = { workspace = true } tokio = { features = ["fs", "process"], workspace = true, default-features = true } +strum = { features = ["derive"], workspace = true, default-features = true } codec = { features = [ "derive", diff --git a/polkadot/node/core/pvf/build.rs b/polkadot/node/core/pvf/build.rs index e01cc6deecc22c574a929c96f0684d777764149e..e46f2dc5f55a94aa3fda4a6ce8b1806aa069120f 100644 --- a/polkadot/node/core/pvf/build.rs +++ b/polkadot/node/core/pvf/build.rs @@ -16,6 +16,6 @@ fn main() { if let Ok(profile) = std::env::var("PROFILE") { - println!(r#"cargo:rustc-cfg=build_type="{}""#, profile); + println!(r#"cargo:rustc-cfg=build_profile="{}""#, profile); } } diff --git a/polkadot/node/core/pvf/common/src/worker/security/change_root.rs b/polkadot/node/core/pvf/common/src/worker/security/change_root.rs index 9ec66906819f189910ccec6d61081e4575a17957..fcfaf6541c291b682b6decdb4de720e280da9556 100644 --- a/polkadot/node/core/pvf/common/src/worker/security/change_root.rs +++ b/polkadot/node/core/pvf/common/src/worker/security/change_root.rs @@ -124,7 +124,8 @@ fn try_restrict(worker_info: &WorkerInfo) -> Result<()> { libc::MS_BIND | libc::MS_REC | libc::MS_NOEXEC | libc::MS_NODEV | libc::MS_NOSUID | - libc::MS_NOATIME | additional_flags, + libc::MS_NOATIME | + additional_flags, ptr::null(), // ignored when MS_BIND is used ) < 0 { diff --git a/polkadot/node/core/pvf/src/artifacts.rs b/polkadot/node/core/pvf/src/artifacts.rs index 119af34082a97f412a5a9135c1f92da3fd5ee9d9..1126a0c90c8ce9f87ebc368f15a028d2d2e75252 100644 --- a/polkadot/node/core/pvf/src/artifacts.rs +++ b/polkadot/node/core/pvf/src/artifacts.rs @@ -56,7 +56,7 @@ use crate::{host::PrecheckResultSender, worker_interface::WORKER_DIR_PREFIX}; use always_assert::always; -use polkadot_node_core_pvf_common::{error::PrepareError, prepare::PrepareStats, pvf::PvfPrepData}; +use polkadot_node_core_pvf_common::{error::PrepareError, pvf::PvfPrepData}; use polkadot_parachain_primitives::primitives::ValidationCodeHash; use polkadot_primitives::ExecutorParamsPrepHash; use std::{ @@ -144,8 +144,6 @@ pub enum ArtifactState { last_time_needed: SystemTime, /// Size in bytes size: u64, - /// Stats produced by successful preparation. - prepare_stats: PrepareStats, }, /// A task to prepare this artifact is scheduled. Preparing { @@ -269,15 +267,11 @@ impl Artifacts { path: PathBuf, last_time_needed: SystemTime, size: u64, - prepare_stats: PrepareStats, ) { // See the precondition. always!(self .inner - .insert( - artifact_id, - ArtifactState::Prepared { path, last_time_needed, size, prepare_stats } - ) + .insert(artifact_id, ArtifactState::Prepared { path, last_time_needed, size }) .is_none()); } @@ -384,21 +378,18 @@ mod tests { path1.clone(), mock_now - Duration::from_secs(5), 1024, - PrepareStats::default(), ); artifacts.insert_prepared( artifact_id2.clone(), path2.clone(), mock_now - Duration::from_secs(10), 1024, - PrepareStats::default(), ); artifacts.insert_prepared( artifact_id3.clone(), path3.clone(), mock_now - Duration::from_secs(15), 1024, - PrepareStats::default(), ); let pruned = artifacts.prune(&cleanup_config); @@ -432,21 +423,18 @@ mod tests { path1.clone(), mock_now - Duration::from_secs(5), 1024, - PrepareStats::default(), ); artifacts.insert_prepared( artifact_id2.clone(), path2.clone(), mock_now - Duration::from_secs(10), 1024, - PrepareStats::default(), ); artifacts.insert_prepared( artifact_id3.clone(), path3.clone(), mock_now - Duration::from_secs(15), 1024, - PrepareStats::default(), ); let pruned = artifacts.prune(&cleanup_config); diff --git a/polkadot/node/core/pvf/src/execute/queue.rs b/polkadot/node/core/pvf/src/execute/queue.rs index 11031bf1074a297e246a4e341efe8488a2692b1b..096cec3501b8a9c357faede4e052e7256315428c 100644 --- a/polkadot/node/core/pvf/src/execute/queue.rs +++ b/polkadot/node/core/pvf/src/execute/queue.rs @@ -35,15 +35,17 @@ use polkadot_node_core_pvf_common::{ SecurityStatus, }; use polkadot_node_primitives::PoV; +use polkadot_node_subsystem::messages::PvfExecKind; use polkadot_primitives::{ExecutorParams, ExecutorParamsHash, PersistedValidationData}; use slotmap::HopSlotMap; use std::{ - collections::VecDeque, + collections::{HashMap, VecDeque}, fmt, path::PathBuf, sync::Arc, time::{Duration, Instant}, }; +use strum::IntoEnumIterator; /// The amount of time a job for which the queue does not have a compatible worker may wait in the /// queue. After that time passes, the queue will kill the first worker which becomes idle to @@ -74,6 +76,7 @@ pub struct PendingExecutionRequest { pub pov: Arc, pub executor_params: ExecutorParams, pub result_tx: ResultSender, + pub exec_kind: PvfExecKind, } struct ExecuteJob { @@ -166,7 +169,7 @@ struct Queue { security_status: SecurityStatus, /// The queue of jobs that are waiting for a worker to pick up. - queue: VecDeque, + unscheduled: Unscheduled, workers: Workers, mux: Mux, } @@ -192,7 +195,7 @@ impl Queue { security_status, to_queue_rx, from_queue_tx, - queue: VecDeque::new(), + unscheduled: Unscheduled::new(), mux: Mux::new(), workers: Workers { running: HopSlotMap::with_capacity_and_key(10), @@ -226,9 +229,13 @@ impl Queue { /// If all the workers are busy or the queue is empty, it does nothing. /// Should be called every time a new job arrives to the queue or a job finishes. fn try_assign_next_job(&mut self, finished_worker: Option) { - // New jobs are always pushed to the tail of the queue; the one at its head is always - // the eldest one. - let eldest = if let Some(eldest) = self.queue.get(0) { eldest } else { return }; + // We always work at the same priority level + let priority = self.unscheduled.select_next_priority(); + let Some(queue) = self.unscheduled.get_mut(priority) else { return }; + + // New jobs are always pushed to the tail of the queue based on their priority; + // the one at its head of each queue is always the eldest one. + let eldest = if let Some(eldest) = queue.get(0) { eldest } else { return }; // By default, we're going to execute the eldest job on any worker slot available, even if // we have to kill and re-spawn a worker @@ -240,7 +247,7 @@ impl Queue { if eldest.waiting_since.elapsed() < MAX_KEEP_WAITING { if let Some(finished_worker) = finished_worker { if let Some(worker_data) = self.workers.running.get(finished_worker) { - for (i, job) in self.queue.iter().enumerate() { + for (i, job) in queue.iter().enumerate() { if worker_data.executor_params_hash == job.executor_params.hash() { (worker, job_index) = (Some(finished_worker), i); break @@ -252,7 +259,7 @@ impl Queue { if worker.is_none() { // Try to obtain a worker for the job - worker = self.workers.find_available(self.queue[job_index].executor_params.hash()); + worker = self.workers.find_available(queue[job_index].executor_params.hash()); } if worker.is_none() { @@ -270,13 +277,15 @@ impl Queue { return } - let job = self.queue.remove(job_index).expect("Job is just checked to be in queue; qed"); + let job = queue.remove(job_index).expect("Job is just checked to be in queue; qed"); if let Some(worker) = worker { assign(self, worker, job); } else { spawn_extra_worker(self, job); } + self.metrics.on_execute_kind(priority); + self.unscheduled.mark_scheduled(priority); } } @@ -297,7 +306,7 @@ async fn purge_dead(metrics: &Metrics, workers: &mut Workers) { fn handle_to_queue(queue: &mut Queue, to_queue: ToQueue) { let ToQueue::Enqueue { artifact, pending_execution_request } = to_queue; - let PendingExecutionRequest { exec_timeout, pvd, pov, executor_params, result_tx } = + let PendingExecutionRequest { exec_timeout, pvd, pov, executor_params, result_tx, exec_kind } = pending_execution_request; gum::debug!( target: LOG_TARGET, @@ -315,7 +324,7 @@ fn handle_to_queue(queue: &mut Queue, to_queue: ToQueue) { result_tx, waiting_since: Instant::now(), }; - queue.queue.push_back(job); + queue.unscheduled.add(job, exec_kind); queue.try_assign_next_job(None); } @@ -638,3 +647,297 @@ pub fn start( .run(); (to_queue_tx, from_queue_rx, run) } + +struct Unscheduled { + unscheduled: HashMap>, + counter: HashMap, +} + +impl Unscheduled { + /// We keep track of every scheduled job in the `counter`, but reset it if the total number of + /// counted jobs reaches the threshold. This number is set as the maximum amount of jobs per + /// relay chain block possible with 4 CPU cores and 2 seconds of execution time. Under normal + /// conditions, the maximum expected queue size is at least vrf_module_samples(6) + 1 for + /// backing a parachain candidate. A buffer is added to cover situations where more work + /// arrives in the queue. + const SCHEDULING_WINDOW_SIZE: usize = 12; + + /// A threshold in percentages indicates how much time a current priority can "steal" from lower + /// priorities. Given the `SCHEDULING_WINDOW_SIZE` is 12 and all job priorities are present: + /// - Disputes consume 70% or 8 jobs in a row. + /// - The remaining 30% of original 100% is allocated for approval and all backing jobs. + /// - 80% or 3 jobs of the remaining goes to approvals. + /// - The remaining 6% of original 100% is allocated for all backing jobs. + /// - 100% or 1 job of the remaining goes to backing system parachains. + /// - Nothing is left for backing. + /// - The counter is restarted and the distribution starts from the beginning. + /// + /// This system might seem complex, but we operate with the remaining percentages because: + /// - Not all job types are present in each block. If we used parts of the original 100%, + /// approvals could not exceed 24%, even if there are no disputes. + /// - We cannot fully prioritize backing system parachains over backing other parachains based + /// on the distribution of the original 100%. + const PRIORITY_ALLOCATION_THRESHOLDS: &'static [(PvfExecKind, usize)] = &[ + (PvfExecKind::Dispute, 70), + (PvfExecKind::Approval, 80), + (PvfExecKind::BackingSystemParas, 100), + (PvfExecKind::Backing, 100), + ]; + + fn new() -> Self { + Self { + unscheduled: PvfExecKind::iter().map(|priority| (priority, VecDeque::new())).collect(), + counter: PvfExecKind::iter().map(|priority| (priority, 0)).collect(), + } + } + + fn select_next_priority(&self) -> PvfExecKind { + gum::debug!( + target: LOG_TARGET, + unscheduled = ?self.unscheduled.iter().map(|(p, q)| (*p, q.len())).collect::>(), + counter = ?self.counter, + "Selecting next execution priority...", + ); + + let priority = PvfExecKind::iter() + .find(|priority| self.has_pending(priority) && !self.has_reached_threshold(priority)) + .unwrap_or_else(|| { + PvfExecKind::iter() + .find(|priority| self.has_pending(priority)) + .unwrap_or(PvfExecKind::Backing) + }); + + gum::debug!( + target: LOG_TARGET, + ?priority, + "Selected next execution priority", + ); + + priority + } + + fn get_mut(&mut self, priority: PvfExecKind) -> Option<&mut VecDeque> { + self.unscheduled.get_mut(&priority) + } + + fn add(&mut self, job: ExecuteJob, priority: PvfExecKind) { + self.unscheduled.entry(priority).or_default().push_back(job); + } + + fn has_pending(&self, priority: &PvfExecKind) -> bool { + !self.unscheduled.get(priority).unwrap_or(&VecDeque::new()).is_empty() + } + + fn priority_allocation_threshold(priority: &PvfExecKind) -> Option { + Self::PRIORITY_ALLOCATION_THRESHOLDS.iter().find_map(|&(p, value)| { + if p == *priority { + Some(value) + } else { + None + } + }) + } + + /// Checks if a given priority has reached its allocated threshold + /// The thresholds are defined in `PRIORITY_ALLOCATION_THRESHOLDS`. + fn has_reached_threshold(&self, priority: &PvfExecKind) -> bool { + let Some(threshold) = Self::priority_allocation_threshold(priority) else { return false }; + let Some(count) = self.counter.get(&priority) else { return false }; + // Every time we iterate by lower level priorities + let total_scheduled_at_priority_or_lower: usize = self + .counter + .iter() + .filter_map(|(p, c)| if *p >= *priority { Some(c) } else { None }) + .sum(); + if total_scheduled_at_priority_or_lower == 0 { + return false + } + + let has_reached_threshold = count * 100 / total_scheduled_at_priority_or_lower >= threshold; + + gum::debug!( + target: LOG_TARGET, + ?priority, + ?count, + ?total_scheduled_at_priority_or_lower, + "Execution priority has {}reached threshold: {}/{}%", + if has_reached_threshold {""} else {"not "}, + count * 100 / total_scheduled_at_priority_or_lower, + threshold + ); + + has_reached_threshold + } + + fn mark_scheduled(&mut self, priority: PvfExecKind) { + *self.counter.entry(priority).or_default() += 1; + + if self.counter.values().sum::() >= Self::SCHEDULING_WINDOW_SIZE { + self.reset_counter(); + } + } + + fn reset_counter(&mut self) { + self.counter = PvfExecKind::iter().map(|kind| (kind, 0)).collect(); + } +} + +#[cfg(test)] +mod tests { + use polkadot_node_primitives::BlockData; + use sp_core::H256; + + use super::*; + use crate::testing::artifact_id; + use std::time::Duration; + + fn create_execution_job() -> ExecuteJob { + let (result_tx, _result_rx) = oneshot::channel(); + let pvd = Arc::new(PersistedValidationData { + parent_head: Default::default(), + relay_parent_number: 1u32, + relay_parent_storage_root: H256::default(), + max_pov_size: 4096 * 1024, + }); + let pov = Arc::new(PoV { block_data: BlockData(b"pov".to_vec()) }); + ExecuteJob { + artifact: ArtifactPathId { id: artifact_id(0), path: PathBuf::new() }, + exec_timeout: Duration::from_secs(10), + pvd, + pov, + executor_params: ExecutorParams::default(), + result_tx, + waiting_since: Instant::now(), + } + } + + #[test] + fn test_unscheduled_add() { + let mut unscheduled = Unscheduled::new(); + + PvfExecKind::iter().for_each(|priority| { + unscheduled.add(create_execution_job(), priority); + }); + + PvfExecKind::iter().for_each(|priority| { + let queue = unscheduled.unscheduled.get(&priority).unwrap(); + assert_eq!(queue.len(), 1); + }); + } + + #[test] + fn test_unscheduled_priority_distribution() { + use PvfExecKind::*; + + let mut priorities = vec![]; + + let mut unscheduled = Unscheduled::new(); + for _ in 0..Unscheduled::SCHEDULING_WINDOW_SIZE { + unscheduled.add(create_execution_job(), Dispute); + unscheduled.add(create_execution_job(), Approval); + unscheduled.add(create_execution_job(), BackingSystemParas); + unscheduled.add(create_execution_job(), Backing); + } + + for _ in 0..Unscheduled::SCHEDULING_WINDOW_SIZE { + let priority = unscheduled.select_next_priority(); + priorities.push(priority); + unscheduled.mark_scheduled(priority); + } + + assert_eq!(priorities.iter().filter(|v| **v == Dispute).count(), 8); + assert_eq!(priorities.iter().filter(|v| **v == Approval).count(), 3); + assert_eq!(priorities.iter().filter(|v| **v == BackingSystemParas).count(), 1); + } + + #[test] + fn test_unscheduled_priority_distribution_without_backing_system_paras() { + use PvfExecKind::*; + + let mut priorities = vec![]; + + let mut unscheduled = Unscheduled::new(); + for _ in 0..Unscheduled::SCHEDULING_WINDOW_SIZE { + unscheduled.add(create_execution_job(), Dispute); + unscheduled.add(create_execution_job(), Approval); + unscheduled.add(create_execution_job(), Backing); + } + + for _ in 0..Unscheduled::SCHEDULING_WINDOW_SIZE { + let priority = unscheduled.select_next_priority(); + priorities.push(priority); + unscheduled.mark_scheduled(priority); + } + + assert_eq!(priorities.iter().filter(|v| **v == Dispute).count(), 8); + assert_eq!(priorities.iter().filter(|v| **v == Approval).count(), 3); + assert_eq!(priorities.iter().filter(|v| **v == Backing).count(), 1); + } + + #[test] + fn test_unscheduled_priority_distribution_without_disputes() { + use PvfExecKind::*; + + let mut priorities = vec![]; + + let mut unscheduled = Unscheduled::new(); + for _ in 0..Unscheduled::SCHEDULING_WINDOW_SIZE { + unscheduled.add(create_execution_job(), Approval); + unscheduled.add(create_execution_job(), BackingSystemParas); + unscheduled.add(create_execution_job(), Backing); + } + + for _ in 0..Unscheduled::SCHEDULING_WINDOW_SIZE { + let priority = unscheduled.select_next_priority(); + priorities.push(priority); + unscheduled.mark_scheduled(priority); + } + + assert_eq!(priorities.iter().filter(|v| **v == Approval).count(), 9); + assert_eq!(priorities.iter().filter(|v| **v == BackingSystemParas).count(), 2); + assert_eq!(priorities.iter().filter(|v| **v == Backing).count(), 1); + } + + #[test] + fn test_unscheduled_priority_distribution_without_disputes_and_only_one_backing() { + use PvfExecKind::*; + + let mut priorities = vec![]; + + let mut unscheduled = Unscheduled::new(); + for _ in 0..Unscheduled::SCHEDULING_WINDOW_SIZE { + unscheduled.add(create_execution_job(), Approval); + } + unscheduled.add(create_execution_job(), Backing); + + for _ in 0..Unscheduled::SCHEDULING_WINDOW_SIZE { + let priority = unscheduled.select_next_priority(); + priorities.push(priority); + unscheduled.mark_scheduled(priority); + } + + assert_eq!(priorities.iter().filter(|v| **v == Approval).count(), 11); + assert_eq!(priorities.iter().filter(|v| **v == Backing).count(), 1); + } + + #[test] + fn test_unscheduled_does_not_postpone_backing() { + use PvfExecKind::*; + + let mut priorities = vec![]; + + let mut unscheduled = Unscheduled::new(); + for _ in 0..Unscheduled::SCHEDULING_WINDOW_SIZE { + unscheduled.add(create_execution_job(), Approval); + } + unscheduled.add(create_execution_job(), Backing); + + for _ in 0..Unscheduled::SCHEDULING_WINDOW_SIZE { + let priority = unscheduled.select_next_priority(); + priorities.push(priority); + unscheduled.mark_scheduled(priority); + } + + assert_eq!(&priorities[..4], &[Approval, Backing, Approval, Approval]); + } +} diff --git a/polkadot/node/core/pvf/src/host.rs b/polkadot/node/core/pvf/src/host.rs index 44a4cba2fbf864d16117ee98ff4034e2469f55a2..37cd6fcbf74aae3672e60c2de304e535e7018e6f 100644 --- a/polkadot/node/core/pvf/src/host.rs +++ b/polkadot/node/core/pvf/src/host.rs @@ -37,7 +37,7 @@ use polkadot_node_core_pvf_common::{ pvf::PvfPrepData, }; use polkadot_node_primitives::PoV; -use polkadot_node_subsystem::{SubsystemError, SubsystemResult}; +use polkadot_node_subsystem::{messages::PvfExecKind, SubsystemError, SubsystemResult}; use polkadot_parachain_primitives::primitives::ValidationResult; use polkadot_primitives::PersistedValidationData; use std::{ @@ -114,6 +114,7 @@ impl ValidationHost { pvd: Arc, pov: Arc, priority: Priority, + exec_kind: PvfExecKind, result_tx: ResultSender, ) -> Result<(), String> { self.to_host_tx @@ -123,6 +124,7 @@ impl ValidationHost { pvd, pov, priority, + exec_kind, result_tx, })) .await @@ -155,6 +157,7 @@ struct ExecutePvfInputs { pvd: Arc, pov: Arc, priority: Priority, + exec_kind: PvfExecKind, result_tx: ResultSender, } @@ -545,7 +548,7 @@ async fn handle_execute_pvf( awaiting_prepare: &mut AwaitingPrepare, inputs: ExecutePvfInputs, ) -> Result<(), Fatal> { - let ExecutePvfInputs { pvf, exec_timeout, pvd, pov, priority, result_tx } = inputs; + let ExecutePvfInputs { pvf, exec_timeout, pvd, pov, priority, exec_kind, result_tx } = inputs; let artifact_id = ArtifactId::from_pvf_prep_data(&pvf); let executor_params = (*pvf.executor_params()).clone(); @@ -567,6 +570,7 @@ async fn handle_execute_pvf( pvd, pov, executor_params, + exec_kind, result_tx, }, }, @@ -597,6 +601,7 @@ async fn handle_execute_pvf( pvd, pov, executor_params, + exec_kind, result_tx, }, ) @@ -606,7 +611,14 @@ async fn handle_execute_pvf( ArtifactState::Preparing { .. } => { awaiting_prepare.add( artifact_id, - PendingExecutionRequest { exec_timeout, pvd, pov, executor_params, result_tx }, + PendingExecutionRequest { + exec_timeout, + pvd, + pov, + executor_params, + result_tx, + exec_kind, + }, ); }, ArtifactState::FailedToProcess { last_time_failed, num_failures, error } => { @@ -638,6 +650,7 @@ async fn handle_execute_pvf( pvd, pov, executor_params, + exec_kind, result_tx, }, ) @@ -657,7 +670,14 @@ async fn handle_execute_pvf( pvf, priority, artifact_id, - PendingExecutionRequest { exec_timeout, pvd, pov, executor_params, result_tx }, + PendingExecutionRequest { + exec_timeout, + pvd, + pov, + executor_params, + result_tx, + exec_kind, + }, ) .await?; } @@ -779,7 +799,7 @@ async fn handle_prepare_done( // It's finally time to dispatch all the execution requests that were waiting for this artifact // to be prepared. let pending_requests = awaiting_prepare.take(&artifact_id); - for PendingExecutionRequest { exec_timeout, pvd, pov, executor_params, result_tx } in + for PendingExecutionRequest { exec_timeout, pvd, pov, executor_params, result_tx, exec_kind } in pending_requests { if result_tx.is_canceled() { @@ -805,6 +825,7 @@ async fn handle_prepare_done( pvd, pov, executor_params, + exec_kind, result_tx, }, }, @@ -813,12 +834,8 @@ async fn handle_prepare_done( } *state = match result { - Ok(PrepareSuccess { path, stats: prepare_stats, size }) => ArtifactState::Prepared { - path, - last_time_needed: SystemTime::now(), - size, - prepare_stats, - }, + Ok(PrepareSuccess { path, size, .. }) => + ArtifactState::Prepared { path, last_time_needed: SystemTime::now(), size }, Err(error) => { let last_time_failed = SystemTime::now(); let num_failures = *num_failures + 1; @@ -976,7 +993,6 @@ pub(crate) mod tests { use crate::{artifacts::generate_artifact_path, testing::artifact_id, PossiblyInvalidError}; use assert_matches::assert_matches; use futures::future::BoxFuture; - use polkadot_node_core_pvf_common::prepare::PrepareStats; use polkadot_node_primitives::BlockData; use sp_core::H256; @@ -1196,20 +1212,8 @@ pub(crate) mod tests { builder.cleanup_config = ArtifactsCleanupConfig::new(1024, Duration::from_secs(0)); let path1 = generate_artifact_path(cache_path); let path2 = generate_artifact_path(cache_path); - builder.artifacts.insert_prepared( - artifact_id(1), - path1.clone(), - mock_now, - 1024, - PrepareStats::default(), - ); - builder.artifacts.insert_prepared( - artifact_id(2), - path2.clone(), - mock_now, - 1024, - PrepareStats::default(), - ); + builder.artifacts.insert_prepared(artifact_id(1), path1.clone(), mock_now, 1024); + builder.artifacts.insert_prepared(artifact_id(2), path2.clone(), mock_now, 1024); let mut test = builder.build(); let mut host = test.host_handle(); @@ -1251,6 +1255,7 @@ pub(crate) mod tests { pvd.clone(), pov1.clone(), Priority::Normal, + PvfExecKind::Backing, result_tx, ) .await @@ -1263,6 +1268,7 @@ pub(crate) mod tests { pvd.clone(), pov1, Priority::Critical, + PvfExecKind::Backing, result_tx, ) .await @@ -1275,6 +1281,7 @@ pub(crate) mod tests { pvd, pov2, Priority::Normal, + PvfExecKind::Backing, result_tx, ) .await @@ -1424,6 +1431,7 @@ pub(crate) mod tests { pvd.clone(), pov.clone(), Priority::Critical, + PvfExecKind::Backing, result_tx, ) .await @@ -1472,6 +1480,7 @@ pub(crate) mod tests { pvd, pov, Priority::Critical, + PvfExecKind::Backing, result_tx, ) .await @@ -1582,6 +1591,7 @@ pub(crate) mod tests { pvd.clone(), pov.clone(), Priority::Critical, + PvfExecKind::Backing, result_tx, ) .await @@ -1613,6 +1623,7 @@ pub(crate) mod tests { pvd.clone(), pov.clone(), Priority::Critical, + PvfExecKind::Backing, result_tx_2, ) .await @@ -1636,6 +1647,7 @@ pub(crate) mod tests { pvd.clone(), pov.clone(), Priority::Critical, + PvfExecKind::Backing, result_tx_3, ) .await @@ -1694,6 +1706,7 @@ pub(crate) mod tests { pvd.clone(), pov.clone(), Priority::Critical, + PvfExecKind::Backing, result_tx, ) .await @@ -1725,6 +1738,7 @@ pub(crate) mod tests { pvd.clone(), pov.clone(), Priority::Critical, + PvfExecKind::Backing, result_tx_2, ) .await @@ -1748,6 +1762,7 @@ pub(crate) mod tests { pvd.clone(), pov.clone(), Priority::Critical, + PvfExecKind::Backing, result_tx_3, ) .await @@ -1822,6 +1837,7 @@ pub(crate) mod tests { pvd, pov, Priority::Normal, + PvfExecKind::Backing, result_tx, ) .await diff --git a/polkadot/node/core/pvf/src/metrics.rs b/polkadot/node/core/pvf/src/metrics.rs index c59cab4641805eca5dac8d60e554e21b79c97606..745f2de99e5899e996d0ff904e8394493d5cdd4f 100644 --- a/polkadot/node/core/pvf/src/metrics.rs +++ b/polkadot/node/core/pvf/src/metrics.rs @@ -18,6 +18,7 @@ use polkadot_node_core_pvf_common::prepare::MemoryStats; use polkadot_node_metrics::metrics::{self, prometheus}; +use polkadot_node_subsystem::messages::PvfExecKind; /// Validation host metrics. #[derive(Default, Clone)] @@ -120,6 +121,13 @@ impl Metrics { .observe(pov_size as f64); } } + + /// When preparation pipeline concluded working on an item. + pub(crate) fn on_execute_kind(&self, kind: PvfExecKind) { + if let Some(metrics) = &self.0 { + metrics.exec_kind_selected.with_label_values(&[kind.as_str()]).inc(); + } + } } #[derive(Clone)] @@ -146,6 +154,7 @@ struct MetricsInner { preparation_peak_tracked_allocation: prometheus::Histogram, pov_size: prometheus::HistogramVec, code_size: prometheus::Histogram, + exec_kind_selected: prometheus::CounterVec, } impl metrics::Metrics for Metrics { @@ -369,6 +378,16 @@ impl metrics::Metrics for Metrics { )?, registry, )?, + exec_kind_selected: prometheus::register( + prometheus::CounterVec::new( + prometheus::Opts::new( + "polkadot_pvf_exec_kind_selected", + "The total number of selected execute kinds", + ), + &["priority"], + )?, + registry, + )?, }; Ok(Metrics(Some(inner))) } diff --git a/polkadot/node/core/pvf/src/prepare/pool.rs b/polkadot/node/core/pvf/src/prepare/pool.rs index 4e11f977c9e7d82527111cc9b751e15d1dc6c7a8..67cd71812e5230bf24680e7453dc202abfa85ce9 100644 --- a/polkadot/node/core/pvf/src/prepare/pool.rs +++ b/polkadot/node/core/pvf/src/prepare/pool.rs @@ -343,14 +343,13 @@ fn handle_mux( ), // Return `Concluded`, but do not kill the worker since the error was on the host // side. - Outcome::RenameTmpFile { worker: idle, result: _, err, src, dest } => - handle_concluded_no_rip( - from_pool, - spawned, - worker, - idle, - Err(PrepareError::RenameTmpFile { err, src, dest }), - ), + Outcome::RenameTmpFile { worker: idle, err, src, dest } => handle_concluded_no_rip( + from_pool, + spawned, + worker, + idle, + Err(PrepareError::RenameTmpFile { err, src, dest }), + ), // Could not clear worker cache. Kill the worker so other jobs can't see the data. Outcome::ClearWorkerDir { err } => { if attempt_retire(metrics, spawned, worker) { diff --git a/polkadot/node/core/pvf/src/prepare/worker_interface.rs b/polkadot/node/core/pvf/src/prepare/worker_interface.rs index d29d2717c4b6e38239c8357a711a2899f4010274..718416e8be76a1be77e627264476094f70d823fb 100644 --- a/polkadot/node/core/pvf/src/prepare/worker_interface.rs +++ b/polkadot/node/core/pvf/src/prepare/worker_interface.rs @@ -81,7 +81,6 @@ pub enum Outcome { /// final destination location. RenameTmpFile { worker: IdleWorker, - result: PrepareWorkerResult, err: String, // Unfortunately `PathBuf` doesn't implement `Encode`/`Decode`, so we do a fallible // conversion to `Option`. @@ -287,7 +286,6 @@ async fn handle_response( ); Outcome::RenameTmpFile { worker, - result, err: format!("{:?}", err), src: tmp_file.to_str().map(String::from), dest: artifact_path.to_str().map(String::from), diff --git a/polkadot/node/core/pvf/src/priority.rs b/polkadot/node/core/pvf/src/priority.rs index 0d18d4b484cabbde70361019e7008887e419b350..7aaeacf3622093433594524451cd68761521409b 100644 --- a/polkadot/node/core/pvf/src/priority.rs +++ b/polkadot/node/core/pvf/src/priority.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . +use polkadot_node_subsystem::messages::PvfExecKind; + /// A priority assigned to preparation of a PVF. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum Priority { @@ -35,3 +37,14 @@ impl Priority { self == Priority::Critical } } + +impl From for Priority { + fn from(priority: PvfExecKind) -> Self { + match priority { + PvfExecKind::Dispute => Priority::Critical, + PvfExecKind::Approval => Priority::Critical, + PvfExecKind::BackingSystemParas => Priority::Normal, + PvfExecKind::Backing => Priority::Normal, + } + } +} diff --git a/polkadot/node/core/pvf/src/testing.rs b/polkadot/node/core/pvf/src/testing.rs index 8c75dafa69c2554df5d7739ae4bfe9b1ccdf5c87..9a4004f390378ff29bed4a29d52243c57308eef3 100644 --- a/polkadot/node/core/pvf/src/testing.rs +++ b/polkadot/node/core/pvf/src/testing.rs @@ -72,7 +72,7 @@ pub fn build_workers_and_get_paths() -> (PathBuf, PathBuf) { "--bin=polkadot-execute-worker", ]; - if cfg!(build_type = "release") { + if cfg!(build_profile = "release") { build_args.push("--release"); } diff --git a/polkadot/node/core/pvf/tests/it/main.rs b/polkadot/node/core/pvf/tests/it/main.rs index a4a0853189579ce96cf96d49f685c3de7de5af00..4cbc6fb04a8efaabbac8d56a5a90db624a3cc842 100644 --- a/polkadot/node/core/pvf/tests/it/main.rs +++ b/polkadot/node/core/pvf/tests/it/main.rs @@ -25,9 +25,11 @@ use polkadot_node_core_pvf::{ ValidationHost, JOB_TIMEOUT_WALL_CLOCK_FACTOR, }; use polkadot_node_primitives::{PoV, POV_BOMB_LIMIT, VALIDATION_CODE_BOMB_LIMIT}; +use polkadot_node_subsystem::messages::PvfExecKind; use polkadot_parachain_primitives::primitives::{BlockData, ValidationResult}; use polkadot_primitives::{ - ExecutorParam, ExecutorParams, PersistedValidationData, PvfExecKind, PvfPrepKind, + ExecutorParam, ExecutorParams, PersistedValidationData, PvfExecKind as RuntimePvfExecKind, + PvfPrepKind, }; use sp_core::H256; @@ -123,6 +125,7 @@ impl TestHost { Arc::new(pvd), Arc::new(pov), polkadot_node_core_pvf::Priority::Normal, + PvfExecKind::Backing, result_tx, ) .await @@ -580,8 +583,9 @@ async fn artifact_does_not_reprepare_on_non_meaningful_exec_parameter_change() { let cache_dir = host.cache_dir.path(); let set1 = ExecutorParams::default(); - let set2 = - ExecutorParams::from(&[ExecutorParam::PvfExecTimeout(PvfExecKind::Backing, 2500)][..]); + let set2 = ExecutorParams::from( + &[ExecutorParam::PvfExecTimeout(RuntimePvfExecKind::Backing, 2500)][..], + ); let _stats = host .precheck_pvf(test_parachain_halt::wasm_binary_unwrap(), set1) diff --git a/polkadot/node/jaeger/Cargo.toml b/polkadot/node/jaeger/Cargo.toml deleted file mode 100644 index 90a6c80e3d0bd3ab2567933bf94d89b7a00dc1e9..0000000000000000000000000000000000000000 --- a/polkadot/node/jaeger/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "polkadot-node-jaeger" -version = "7.0.0" -authors.workspace = true -edition.workspace = true -license.workspace = true -description = "Polkadot Jaeger primitives, but equally useful for Grafana/Tempo" - -[lints] -workspace = true - -[dependencies] -mick-jaeger = { workspace = true } -lazy_static = { workspace = true } -parking_lot = { workspace = true, default-features = true } -polkadot-primitives = { workspace = true, default-features = true } -polkadot-node-primitives = { workspace = true, default-features = true } -sc-network = { workspace = true, default-features = true } -sc-network-types = { workspace = true, default-features = true } -sp-core = { workspace = true, default-features = true } -thiserror = { workspace = true } -tokio = { workspace = true, default-features = true } -log = { workspace = true, default-features = true } -codec = { workspace = true } diff --git a/polkadot/node/jaeger/src/config.rs b/polkadot/node/jaeger/src/config.rs deleted file mode 100644 index 702a22e1245c8ecb628d11c95b879bc5bf88db7e..0000000000000000000000000000000000000000 --- a/polkadot/node/jaeger/src/config.rs +++ /dev/null @@ -1,73 +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 . - -//! Polkadot Jaeger configuration. - -/// Configuration for the jaeger tracing. -#[derive(Clone)] -pub struct JaegerConfig { - pub(crate) node_name: String, - pub(crate) agent_addr: std::net::SocketAddr, -} - -impl std::default::Default for JaegerConfig { - fn default() -> Self { - Self { - node_name: "unknown_".to_owned(), - agent_addr: "127.0.0.1:6831" - .parse() - .expect(r#"Static "127.0.0.1:6831" is a valid socket address string. qed"#), - } - } -} - -impl JaegerConfig { - /// Use the builder pattern to construct a configuration. - pub fn builder() -> JaegerConfigBuilder { - JaegerConfigBuilder::default() - } -} - -/// Jaeger configuration builder. -#[derive(Default)] -pub struct JaegerConfigBuilder { - inner: JaegerConfig, -} - -impl JaegerConfigBuilder { - /// Set the name for this node. - pub fn named(mut self, name: S) -> Self - where - S: AsRef, - { - self.inner.node_name = name.as_ref().to_owned(); - self - } - - /// Set the agent address to send the collected spans to. - pub fn agent(mut self, addr: U) -> Self - where - U: Into, - { - self.inner.agent_addr = addr.into(); - self - } - - /// Construct the configuration. - pub fn build(self) -> JaegerConfig { - self.inner - } -} diff --git a/polkadot/node/jaeger/src/lib.rs b/polkadot/node/jaeger/src/lib.rs deleted file mode 100644 index 7de4586068166f28a1d69d1787d2a960d298cf92..0000000000000000000000000000000000000000 --- a/polkadot/node/jaeger/src/lib.rs +++ /dev/null @@ -1,168 +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 . - -//! Polkadot Jaeger related primitives -//! -//! Provides primitives used by Polkadot for interfacing with Jaeger. -//! -//! # Integration -//! -//! See for an introduction. -//! -//! The easiest way to try Jaeger is: -//! -//! - Start a docker container with the all-in-one docker image (see below). -//! - Open your browser and navigate to to access the UI. -//! -//! The all-in-one image can be started with: -//! -//! ```not_rust -//! podman login docker.io -//! podman run -d --name jaeger \ -//! -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 \ -//! -p 5775:5775/udp \ -//! -p 6831:6831/udp \ -//! -p 6832:6832/udp \ -//! -p 5778:5778 \ -//! -p 16686:16686 \ -//! -p 14268:14268 \ -//! -p 14250:14250 \ -//! -p 9411:9411 \ -//! docker.io/jaegertracing/all-in-one:1.21 -//! ``` - -#![forbid(unused_imports)] - -mod config; -mod errors; -mod spans; - -pub use self::{ - config::{JaegerConfig, JaegerConfigBuilder}, - errors::JaegerError, - spans::{hash_to_trace_identifier, PerLeafSpan, Span, Stage}, -}; - -use self::spans::TraceIdentifier; - -use sp_core::traits::SpawnNamed; - -use parking_lot::RwLock; -use std::{result, sync::Arc}; - -lazy_static::lazy_static! { - static ref INSTANCE: RwLock = RwLock::new(Jaeger::None); -} - -/// Stateful convenience wrapper around [`mick_jaeger`]. -pub enum Jaeger { - /// Launched and operational state. - Launched { - /// [`mick_jaeger`] provided API to record spans to. - traces_in: Arc, - }, - /// Preparation state with the necessary config to launch the collector. - Prep(JaegerConfig), - /// Uninitialized, suggests wrong API usage if encountered. - None, -} - -impl Jaeger { - /// Spawn the jaeger instance. - pub fn new(cfg: JaegerConfig) -> Self { - Jaeger::Prep(cfg) - } - - /// Spawn the background task in order to send the tracing information out via UDP - #[cfg(target_os = "unknown")] - pub fn launch(self, _spawner: S) -> result::Result<(), JaegerError> { - Ok(()) - } - - /// Provide a no-thrills test setup helper. - #[cfg(test)] - pub fn test_setup() { - let mut instance = INSTANCE.write(); - match *instance { - Self::Launched { .. } => {}, - _ => { - let (traces_in, _traces_out) = mick_jaeger::init(mick_jaeger::Config { - service_name: "polkadot-jaeger-test".to_owned(), - }); - *instance = Self::Launched { traces_in }; - }, - } - } - - /// Spawn the background task in order to send the tracing information out via UDP - #[cfg(not(target_os = "unknown"))] - pub fn launch(self, spawner: S) -> result::Result<(), JaegerError> { - let cfg = match self { - Self::Prep(cfg) => Ok(cfg), - Self::Launched { .. } => return Err(JaegerError::AlreadyLaunched), - Self::None => Err(JaegerError::MissingConfiguration), - }?; - - let jaeger_agent = cfg.agent_addr; - - log::info!("๐Ÿน Collecting jaeger spans for {:?}", &jaeger_agent); - - let (traces_in, mut traces_out) = mick_jaeger::init(mick_jaeger::Config { - service_name: format!("polkadot-{}", cfg.node_name), - }); - - // Spawn a background task that pulls span information and sends them on the network. - spawner.spawn( - "jaeger-collector", - Some("jaeger"), - Box::pin(async move { - match tokio::net::UdpSocket::bind("0.0.0.0:0").await { - Ok(udp_socket) => loop { - let buf = traces_out.next().await; - // UDP sending errors happen only either if the API is misused or in case of - // missing privilege. - if let Err(e) = udp_socket.send_to(&buf, jaeger_agent).await { - log::debug!(target: "jaeger", "UDP send error: {}", e); - } - }, - Err(e) => { - log::warn!(target: "jaeger", "UDP socket open error: {}", e); - }, - } - }), - ); - - *INSTANCE.write() = Self::Launched { traces_in }; - Ok(()) - } - - /// Create a span, but defer the evaluation/transformation into a `TraceIdentifier`. - /// - /// The deferral allows to avoid the additional CPU runtime cost in case of - /// items that are not a pre-computed hash by themselves. - pub(crate) fn span(&self, lazy_hash: F, span_name: &'static str) -> Option - where - F: Fn() -> TraceIdentifier, - { - if let Self::Launched { traces_in, .. } = self { - let ident = lazy_hash(); - let trace_id = std::num::NonZeroU128::new(ident)?; - Some(traces_in.span(trace_id, span_name)) - } else { - None - } - } -} diff --git a/polkadot/node/jaeger/src/spans.rs b/polkadot/node/jaeger/src/spans.rs deleted file mode 100644 index efc1a9f91d195eeeae3191bcccecec611250f3eb..0000000000000000000000000000000000000000 --- a/polkadot/node/jaeger/src/spans.rs +++ /dev/null @@ -1,520 +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 . - -//! Polkadot Jaeger span definitions. -//! -//! ```rust -//! # use polkadot_primitives::{CandidateHash, Hash}; -//! # fn main() { -//! use polkadot_node_jaeger as jaeger; -//! -//! let relay_parent = Hash::default(); -//! let candidate = CandidateHash::default(); -//! -//! #[derive(Debug, Default)] -//! struct Foo { -//! a: u8, -//! b: u16, -//! c: u32, -//! }; -//! -//! let foo = Foo::default(); -//! -//! let span = -//! jaeger::Span::new(relay_parent, "root_of_aaall_spans") -//! // explicit well defined items -//! .with_candidate(candidate) -//! // anything that implements `trait std::fmt::Debug` -//! .with_string_fmt_debug_tag("foo", foo) -//! // anything that implements `trait std::str::ToString` -//! .with_string_tag("again", 1337_u32) -//! // add a `Stage` for [`dot-jaeger`](https://github.com/paritytech/dot-jaeger) -//! .with_stage(jaeger::Stage::CandidateBacking); -//! // complete by design, no completion required -//! # } -//! ``` -//! -//! In a few cases additional annotations might want to be added -//! over the course of a function, for this purpose use the non-consuming -//! `fn` variants, i.e. -//! ```rust -//! # use polkadot_primitives::{CandidateHash, Hash}; -//! # fn main() { -//! # use polkadot_node_jaeger as jaeger; -//! -//! # let relay_parent = Hash::default(); -//! # let candidate = CandidateHash::default(); -//! -//! # #[derive(Debug, Default)] -//! # struct Foo { -//! # a: u8, -//! # b: u16, -//! # c: u32, -//! # }; -//! # -//! # let foo = Foo::default(); -//! -//! let root_span = -//! jaeger::Span::new(relay_parent, "root_of_aaall_spans"); -//! -//! // the preferred way of adding additional delayed information: -//! let span = root_span.child("inner"); -//! -//! // ... more operations ... -//! -//! // but this is also possible: -//! -//! let mut root_span = root_span; -//! root_span.add_string_fmt_debug_tag("foo_constructed", &foo); -//! root_span.add_string_tag("bar", true); -//! # } -//! ``` - -use codec::Encode; -use polkadot_node_primitives::PoV; -use polkadot_primitives::{ - BlakeTwo256, CandidateHash, ChunkIndex, Hash, HashT, Id as ParaId, ValidatorIndex, -}; -use sc_network_types::PeerId; - -use std::{fmt, sync::Arc}; - -use super::INSTANCE; - -/// A special "per leaf span". -/// -/// Essentially this span wraps two spans: -/// -/// 1. The span that is created per leaf in the overseer. -/// 2. Some child span of the per-leaf span. -/// -/// This just works as auxiliary structure to easily store both. -#[derive(Debug)] -pub struct PerLeafSpan { - leaf_span: Arc, - span: Span, -} - -impl PerLeafSpan { - /// Creates a new instance. - /// - /// Takes the `leaf_span` that is created by the overseer per leaf and a name for a child span. - /// Both will be stored in this object, while the child span is implicitly accessible by using - /// the [`Deref`](std::ops::Deref) implementation. - pub fn new(leaf_span: Arc, name: &'static str) -> Self { - let span = leaf_span.child(name); - - Self { span, leaf_span } - } - - /// Returns the leaf span. - pub fn leaf_span(&self) -> &Arc { - &self.leaf_span - } -} - -/// Returns a reference to the child span. -impl std::ops::Deref for PerLeafSpan { - type Target = Span; - - fn deref(&self) -> &Span { - &self.span - } -} - -/// A helper to annotate the stage with a numerical value -/// to ease the life of the tooling team creating viable -/// statistical metrics for which stage of the inclusion -/// pipeline drops a significant amount of candidates, -/// statistically speaking. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[repr(u8)] -#[non_exhaustive] -pub enum Stage { - CandidateBacking = 2, - StatementDistribution = 3, - PoVDistribution = 4, - AvailabilityDistribution = 5, - AvailabilityRecovery = 6, - BitfieldDistribution = 7, - ApprovalChecking = 8, - ApprovalDistribution = 9, - // Expand as needed, numbers should be ascending according to the stage - // through the inclusion pipeline, or according to the descriptions - // in [the path of a para chain block] - // (https://polkadot.network/the-path-of-a-parachain-block/) - // see [issue](https://github.com/paritytech/polkadot/issues/2389) -} - -/// A wrapper type for a span. -/// -/// Handles running with and without jaeger. -pub enum Span { - /// Running with jaeger being enabled. - Enabled(mick_jaeger::Span), - /// Running with jaeger disabled. - Disabled, -} - -/// Alias for the 16 byte unique identifier used with jaeger. -pub(crate) type TraceIdentifier = u128; - -/// A helper to convert the hash to the fixed size representation -/// needed for jaeger. -#[inline] -pub fn hash_to_trace_identifier(hash: Hash) -> TraceIdentifier { - let mut buf = [0u8; 16]; - buf.copy_from_slice(&hash.as_ref()[0..16]); - // The slice bytes are copied in reading order, so if interpreted - // in string form by a human, that means lower indices have higher - // values and hence corresponds to BIG endian ordering of the individual - // bytes. - u128::from_be_bytes(buf) as TraceIdentifier -} - -/// Helper to unify lazy proxy evaluation. -pub trait LazyIdent { - /// Evaluate the type to a unique trace identifier. - /// Called lazily on demand. - fn eval(&self) -> TraceIdentifier; - - /// Annotate a new root item with these additional spans - /// at construction. - fn extra_tags(&self, _span: &mut Span) {} -} - -impl<'a> LazyIdent for &'a [u8] { - fn eval(&self) -> TraceIdentifier { - hash_to_trace_identifier(BlakeTwo256::hash_of(self)) - } -} - -impl LazyIdent for &PoV { - fn eval(&self) -> TraceIdentifier { - hash_to_trace_identifier(self.hash()) - } - - fn extra_tags(&self, span: &mut Span) { - span.add_pov(self) - } -} - -impl LazyIdent for Hash { - fn eval(&self) -> TraceIdentifier { - hash_to_trace_identifier(*self) - } - - fn extra_tags(&self, span: &mut Span) { - span.add_string_fmt_debug_tag("relay-parent", self); - } -} - -impl LazyIdent for &Hash { - fn eval(&self) -> TraceIdentifier { - hash_to_trace_identifier(**self) - } - - fn extra_tags(&self, span: &mut Span) { - span.add_string_fmt_debug_tag("relay-parent", self); - } -} - -impl LazyIdent for CandidateHash { - fn eval(&self) -> TraceIdentifier { - hash_to_trace_identifier(self.0) - } - - fn extra_tags(&self, span: &mut Span) { - span.add_string_fmt_debug_tag("candidate-hash", &self.0); - // A convenience for usage with the grafana tempo UI, - // not a technical requirement. It merely provides an easy anchor - // where the true trace identifier of the span is not based on - // a candidate hash (which it should be!), but is required to - // continue investigating. - span.add_string_tag("traceID", self.eval().to_string()); - } -} - -impl Span { - /// Creates a new span builder based on anything that can be lazily evaluated - /// to and identifier. - /// - /// Attention: The primary identifier will be used for identification - /// and as such should be - pub fn new(identifier: I, span_name: &'static str) -> Span { - let mut span = INSTANCE - .read_recursive() - .span(|| ::eval(&identifier), span_name) - .into(); - ::extra_tags(&identifier, &mut span); - span - } - - /// Creates a new span builder based on an encodable type. - /// The encoded bytes are then used to derive the true trace identifier. - pub fn from_encodable(identifier: I, span_name: &'static str) -> Span { - INSTANCE - .read_recursive() - .span( - move || { - let bytes = identifier.encode(); - LazyIdent::eval(&bytes.as_slice()) - }, - span_name, - ) - .into() - } - - /// Derive a child span from `self`. - pub fn child(&self, name: &str) -> Self { - match self { - Self::Enabled(inner) => Self::Enabled(inner.child(name)), - Self::Disabled => Self::Disabled, - } - } - - /// Attach a 'traceID' tag set to the decimal representation of the candidate hash. - #[inline(always)] - pub fn with_trace_id(mut self, candidate_hash: CandidateHash) -> Self { - self.add_string_tag("traceID", hash_to_trace_identifier(candidate_hash.0)); - self - } - - #[inline(always)] - pub fn with_string_tag(mut self, tag: &'static str, val: V) -> Self { - self.add_string_tag::(tag, val); - self - } - - /// Attach a peer-id tag to the span. - #[inline(always)] - pub fn with_peer_id(self, peer: &PeerId) -> Self { - self.with_string_tag("peer-id", &peer.to_base58()) - } - - /// Attach a `peer-id` tag to the span when peer is present. - #[inline(always)] - pub fn with_optional_peer_id(self, peer: Option<&PeerId>) -> Self { - if let Some(peer) = peer { - self.with_peer_id(peer) - } else { - self - } - } - - /// Attach a candidate hash to the span. - #[inline(always)] - pub fn with_candidate(self, candidate_hash: CandidateHash) -> Self { - self.with_string_fmt_debug_tag("candidate-hash", &candidate_hash.0) - } - - /// Attach a para-id to the span. - #[inline(always)] - pub fn with_para_id(self, para_id: ParaId) -> Self { - self.with_int_tag("para-id", u32::from(para_id) as i64) - } - - /// Attach a candidate stage. - /// Should always come with a `CandidateHash`. - #[inline(always)] - pub fn with_stage(self, stage: Stage) -> Self { - self.with_string_tag("candidate-stage", stage as u8) - } - - #[inline(always)] - pub fn with_validator_index(self, validator: ValidatorIndex) -> Self { - self.with_string_tag("validator-index", &validator.0) - } - - #[inline(always)] - pub fn with_chunk_index(self, chunk_index: ChunkIndex) -> Self { - self.with_string_tag("chunk-index", &chunk_index.0) - } - - #[inline(always)] - pub fn with_relay_parent(self, relay_parent: Hash) -> Self { - self.with_string_fmt_debug_tag("relay-parent", relay_parent) - } - - #[inline(always)] - pub fn with_claimed_validator_index(self, claimed_validator_index: ValidatorIndex) -> Self { - self.with_string_tag("claimed-validator", &claimed_validator_index.0) - } - - #[inline(always)] - pub fn with_pov(mut self, pov: &PoV) -> Self { - self.add_pov(pov); - self - } - - /// Add an additional int tag to the span without consuming. - /// - /// Should be used sparingly, introduction of new types is preferred. - #[inline(always)] - pub fn with_int_tag(mut self, tag: &'static str, i: i64) -> Self { - self.add_int_tag(tag, i); - self - } - - #[inline(always)] - pub fn with_uint_tag(mut self, tag: &'static str, u: u64) -> Self { - self.add_uint_tag(tag, u); - self - } - - #[inline(always)] - pub fn with_string_fmt_debug_tag(mut self, tag: &'static str, val: V) -> Self { - self.add_string_tag(tag, format!("{:?}", val)); - self - } - - /// Adds the `FollowsFrom` relationship to this span with respect to the given one. - #[inline(always)] - pub fn add_follows_from(&mut self, other: &Self) { - match (self, other) { - (Self::Enabled(ref mut inner), Self::Enabled(ref other_inner)) => - inner.add_follows_from(&other_inner), - _ => {}, - } - } - - /// Add a PoV hash meta tag with lazy hash evaluation, without consuming the span. - #[inline(always)] - pub fn add_pov(&mut self, pov: &PoV) { - if self.is_enabled() { - // avoid computing the PoV hash if jaeger is not enabled - self.add_string_fmt_debug_tag("pov", pov.hash()); - } - } - - #[inline(always)] - pub fn add_para_id(&mut self, para_id: ParaId) { - self.add_int_tag("para-id", u32::from(para_id) as i64); - } - - /// Add a string tag, without consuming the span. - pub fn add_string_tag(&mut self, tag: &'static str, val: V) { - match self { - Self::Enabled(ref mut inner) => inner.add_string_tag(tag, val.to_string().as_str()), - Self::Disabled => {}, - } - } - - /// Add a string tag, without consuming the span. - pub fn add_string_fmt_debug_tag(&mut self, tag: &'static str, val: V) { - match self { - Self::Enabled(ref mut inner) => - inner.add_string_tag(tag, format!("{:?}", val).as_str()), - Self::Disabled => {}, - } - } - - pub fn add_int_tag(&mut self, tag: &'static str, value: i64) { - match self { - Self::Enabled(ref mut inner) => inner.add_int_tag(tag, value), - Self::Disabled => {}, - } - } - - pub fn add_uint_tag(&mut self, tag: &'static str, value: u64) { - match self { - Self::Enabled(ref mut inner) => inner.add_int_tag(tag, value as i64), - Self::Disabled => {}, - } - } - - /// Check whether jaeger is enabled - /// in order to avoid computational overhead. - pub const fn is_enabled(&self) -> bool { - match self { - Span::Enabled(_) => true, - _ => false, - } - } - - /// Obtain the trace identifier for this set of spans. - pub fn trace_id(&self) -> Option { - match self { - Span::Enabled(inner) => Some(inner.trace_id().get()), - _ => None, - } - } -} - -impl std::fmt::Debug for Span { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "") - } -} - -impl From> for Span { - fn from(src: Option) -> Self { - if let Some(span) = src { - Self::Enabled(span) - } else { - Self::Disabled - } - } -} - -impl From for Span { - fn from(src: mick_jaeger::Span) -> Self { - Self::Enabled(src) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::Jaeger; - - // make sure to not use `::repeat_*()` based samples, since this does not verify endianness - const RAW: [u8; 32] = [ - 0xFF, 0xAA, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x78, 0x89, 0x9A, 0xAB, 0xBC, 0xCD, 0xDE, - 0xEF, 0x00, 0x01, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, - 0x0E, 0x0F, - ]; - - #[test] - fn hash_derived_identifier_is_leading_16bytes() { - let candidate_hash = dbg!(Hash::from(&RAW)); - let trace_id = dbg!(hash_to_trace_identifier(candidate_hash)); - for (idx, (a, b)) in candidate_hash - .as_bytes() - .iter() - .take(16) - .zip(trace_id.to_be_bytes().iter()) - .enumerate() - { - assert_eq!(*a, *b, "Index [{}] does not match: {} != {}", idx, a, b); - } - } - - #[test] - fn extra_tags_do_not_change_trace_id() { - Jaeger::test_setup(); - let candidate_hash = dbg!(Hash::from(&RAW)); - let trace_id = hash_to_trace_identifier(candidate_hash); - - let span = Span::new(candidate_hash, "foo"); - - assert_eq!(span.trace_id(), Some(trace_id)); - - let span = span.with_int_tag("tag", 7i64); - - assert_eq!(span.trace_id(), Some(trace_id)); - } -} diff --git a/polkadot/node/malus/integrationtests/0001-dispute-valid-block.toml b/polkadot/node/malus/integrationtests/0001-dispute-valid-block.toml index 43e55402e68c42fe43b7b2f02f6dd517eedb8281..fe1836bd71e5f722349bdd2ba76094a2e8254327 100644 --- a/polkadot/node/malus/integrationtests/0001-dispute-valid-block.toml +++ b/polkadot/node/malus/integrationtests/0001-dispute-valid-block.toml @@ -1,9 +1,12 @@ [settings] timeout = 1000 +[relaychain.genesis.runtimeGenesis.patch.configuration.config.scheduler_params] + max_validators_per_core = 1 + [relaychain] default_image = "{{ZOMBIENET_INTEGRATION_TEST_IMAGE}}" -chain = "wococo-local" +chain = "westend-local" command = "polkadot" [[relaychain.nodes]] diff --git a/polkadot/node/malus/src/variants/back_garbage_candidate.rs b/polkadot/node/malus/src/variants/back_garbage_candidate.rs index b939a2151e2359828da74d317c9e2bc0af2c6e0c..d6f1353a46a8792efcf3e98d27de075ea49e5670 100644 --- a/polkadot/node/malus/src/variants/back_garbage_candidate.rs +++ b/polkadot/node/malus/src/variants/back_garbage_candidate.rs @@ -67,12 +67,10 @@ impl OverseerGen for BackGarbageCandidates { RuntimeClient: RuntimeApiSubsystemClient + ChainApiBackend + AuxStore + 'static, Spawner: 'static + SpawnNamed + Clone + Unpin, { - let spawner = args.spawner.clone(); let validation_filter = ReplaceValidationResult::new( FakeCandidateValidation::BackingAndApprovalValid, FakeCandidateValidationError::InvalidOutputs, f64::from(self.percentage), - SpawnGlue(spawner), ); validator_overseer_builder( diff --git a/polkadot/node/malus/src/variants/common.rs b/polkadot/node/malus/src/variants/common.rs index eb6988f81811687442a36ae3f8ec225de48dd9df..66926f48c5e7f42bfc054aef667323b3da9cedf0 100644 --- a/polkadot/node/malus/src/variants/common.rs +++ b/polkadot/node/malus/src/variants/common.rs @@ -21,7 +21,6 @@ use crate::{ shared::{MALICIOUS_POV, MALUS}, }; -use polkadot_node_core_candidate_validation::find_validation_data; use polkadot_node_primitives::{InvalidCandidate, ValidationResult}; use polkadot_primitives::{ @@ -149,59 +148,21 @@ impl Into for FakeCandidateValidationError { #[derive(Clone, Debug)] /// An interceptor which fakes validation result with a preconfigured result. /// Replaces `CandidateValidationSubsystem`. -pub struct ReplaceValidationResult { +pub struct ReplaceValidationResult { fake_validation: FakeCandidateValidation, fake_validation_error: FakeCandidateValidationError, distribution: Bernoulli, - spawner: Spawner, } -impl ReplaceValidationResult -where - Spawner: overseer::gen::Spawner, -{ +impl ReplaceValidationResult { pub fn new( fake_validation: FakeCandidateValidation, fake_validation_error: FakeCandidateValidationError, percentage: f64, - spawner: Spawner, ) -> Self { let distribution = Bernoulli::new(percentage / 100.0) .expect("Invalid probability! Percentage must be in range [0..=100]."); - Self { fake_validation, fake_validation_error, distribution, spawner } - } - - /// Creates and sends the validation response for a given candidate. Queries the runtime to - /// obtain the validation data for the given candidate. - pub fn send_validation_response( - &self, - candidate_descriptor: CandidateDescriptor, - subsystem_sender: Sender, - response_sender: oneshot::Sender>, - ) where - Sender: overseer::CandidateValidationSenderTrait + Clone + Send + 'static, - { - let _candidate_descriptor = candidate_descriptor.clone(); - let mut subsystem_sender = subsystem_sender.clone(); - let (sender, receiver) = std::sync::mpsc::channel(); - self.spawner.spawn_blocking( - "malus-get-validation-data", - Some("malus"), - Box::pin(async move { - match find_validation_data(&mut subsystem_sender, &_candidate_descriptor).await { - Ok(Some((validation_data, validation_code))) => { - sender - .send((validation_data, validation_code)) - .expect("channel is still open"); - }, - _ => { - panic!("Unable to fetch validation data"); - }, - } - }), - ); - let (validation_data, _) = receiver.recv().unwrap(); - create_validation_response(validation_data, candidate_descriptor, response_sender); + Self { fake_validation, fake_validation_error, distribution } } } @@ -251,10 +212,9 @@ fn create_validation_response( response_sender.send(result).unwrap(); } -impl MessageInterceptor for ReplaceValidationResult +impl MessageInterceptor for ReplaceValidationResult where Sender: overseer::CandidateValidationSenderTrait + Clone + Send + 'static, - Spawner: overseer::gen::Spawner + Clone + 'static, { type Message = CandidateValidationMessage; @@ -262,7 +222,7 @@ where // configuration fail them. fn intercept_incoming( &self, - subsystem_sender: &mut Sender, + _subsystem_sender: &mut Sender, msg: FromOrchestra, ) -> Option> { match msg { @@ -281,7 +241,7 @@ where }, } => { match self.fake_validation { - x if x.misbehaves_valid() && x.should_misbehave(exec_kind) => { + x if x.misbehaves_valid() && x.should_misbehave(exec_kind.into()) => { // Behave normally if the `PoV` is not known to be malicious. if pov.block_data.0.as_slice() != MALICIOUS_POV { return Some(FromOrchestra::Communication { @@ -336,14 +296,14 @@ where }, } }, - x if x.misbehaves_invalid() && x.should_misbehave(exec_kind) => { + x if x.misbehaves_invalid() && x.should_misbehave(exec_kind.into()) => { // Set the validation result to invalid with probability `p` and trigger a // dispute let behave_maliciously = self.distribution.sample(&mut rand::thread_rng()); match behave_maliciously { true => { let validation_result = - ValidationResult::Invalid(InvalidCandidate::InvalidOutputs); + ValidationResult::Invalid(self.fake_validation_error.into()); gum::info!( target: MALUS, @@ -390,109 +350,6 @@ where }), } }, - // Behaviour related to the backing subsystem - FromOrchestra::Communication { - msg: - CandidateValidationMessage::ValidateFromChainState { - candidate_receipt, - pov, - executor_params, - exec_kind, - response_sender, - .. - }, - } => { - match self.fake_validation { - x if x.misbehaves_valid() && x.should_misbehave(exec_kind) => { - // Behave normally if the `PoV` is not known to be malicious. - if pov.block_data.0.as_slice() != MALICIOUS_POV { - return Some(FromOrchestra::Communication { - msg: CandidateValidationMessage::ValidateFromChainState { - candidate_receipt, - pov, - executor_params, - exec_kind, - response_sender, - }, - }) - } - // If the `PoV` is malicious, back the candidate with some probability `p`, - // where 'p' defaults to 100% for suggest-garbage-candidate variant. - let behave_maliciously = self.distribution.sample(&mut rand::thread_rng()); - match behave_maliciously { - true => { - gum::info!( - target: MALUS, - ?behave_maliciously, - "๐Ÿ˜ˆ Backing candidate with malicious PoV.", - ); - - self.send_validation_response( - candidate_receipt.descriptor, - subsystem_sender.clone(), - response_sender, - ); - None - }, - // If the `PoV` is malicious, we behave normally with some probability - // `(1-p)` - false => Some(FromOrchestra::Communication { - msg: CandidateValidationMessage::ValidateFromChainState { - candidate_receipt, - pov, - executor_params, - exec_kind, - response_sender, - }, - }), - } - }, - x if x.misbehaves_invalid() && x.should_misbehave(exec_kind) => { - // Maliciously set the validation result to invalid for a valid candidate - // with probability `p` - let behave_maliciously = self.distribution.sample(&mut rand::thread_rng()); - match behave_maliciously { - true => { - let validation_result = - ValidationResult::Invalid(self.fake_validation_error.into()); - gum::info!( - target: MALUS, - para_id = ?candidate_receipt.descriptor.para_id, - "๐Ÿ˜ˆ Maliciously sending invalid validation result: {:?}.", - &validation_result, - ); - // We're not even checking the candidate, this makes us appear - // faster than honest validators. - response_sender.send(Ok(validation_result)).unwrap(); - None - }, - // With some probability `(1-p)` we behave normally - false => { - gum::info!(target: MALUS, "๐Ÿ˜ˆ 'Decided' to not act maliciously.",); - - Some(FromOrchestra::Communication { - msg: CandidateValidationMessage::ValidateFromChainState { - candidate_receipt, - pov, - executor_params, - exec_kind, - response_sender, - }, - }) - }, - } - }, - _ => Some(FromOrchestra::Communication { - msg: CandidateValidationMessage::ValidateFromChainState { - candidate_receipt, - pov, - executor_params, - exec_kind, - response_sender, - }, - }), - } - }, msg => Some(msg), } } diff --git a/polkadot/node/malus/src/variants/dispute_valid_candidates.rs b/polkadot/node/malus/src/variants/dispute_valid_candidates.rs index a50fdce16e4ec41d6b9bea4f0190616f756d9193..5422167545cecad63f7f0f495abed380a4b8d60c 100644 --- a/polkadot/node/malus/src/variants/dispute_valid_candidates.rs +++ b/polkadot/node/malus/src/variants/dispute_valid_candidates.rs @@ -84,12 +84,10 @@ impl OverseerGen for DisputeValidCandidates { RuntimeClient: RuntimeApiSubsystemClient + ChainApiBackend + AuxStore + 'static, Spawner: 'static + SpawnNamed + Clone + Unpin, { - let spawner = args.spawner.clone(); let validation_filter = ReplaceValidationResult::new( self.fake_validation, self.fake_validation_error, f64::from(self.percentage), - SpawnGlue(spawner.clone()), ); validator_overseer_builder( diff --git a/polkadot/node/malus/src/variants/suggest_garbage_candidate.rs b/polkadot/node/malus/src/variants/suggest_garbage_candidate.rs index 6921352cdfc29c6fc6593e609e30bcee876c4557..ab2d380fbaf486fce9aefb84669eeba5638f2b0c 100644 --- a/polkadot/node/malus/src/variants/suggest_garbage_candidate.rs +++ b/polkadot/node/malus/src/variants/suggest_garbage_candidate.rs @@ -315,7 +315,6 @@ impl OverseerGen for SuggestGarbageCandidates { FakeCandidateValidation::BackingAndApprovalValid, FakeCandidateValidationError::InvalidOutputs, fake_valid_probability, - SpawnGlue(args.spawner.clone()), ); validator_overseer_builder( diff --git a/polkadot/node/metrics/src/tests.rs b/polkadot/node/metrics/src/tests.rs index e720924feb60c6d4ef3828d52312f96521a6f989..4760138058eb7180232ddb81ded487e5d7713420 100644 --- a/polkadot/node/metrics/src/tests.rs +++ b/polkadot/node/metrics/src/tests.rs @@ -1,18 +1,18 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Substrate. +// This file is part of Polkadot. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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 Substrate. If not, see . +// along with Polkadot. If not, see . //! Polkadot runtime metrics integration test. diff --git a/polkadot/node/network/approval-distribution/Cargo.toml b/polkadot/node/network/approval-distribution/Cargo.toml index 1bd3d51b5c933f2b7fe5bf5396687f3e7c59bca7..8d674a733470681f4a26335fa51592963165315b 100644 --- a/polkadot/node/network/approval-distribution/Cargo.toml +++ b/polkadot/node/network/approval-distribution/Cargo.toml @@ -16,7 +16,6 @@ polkadot-node-primitives = { workspace = true, default-features = true } polkadot-node-subsystem = { workspace = true, default-features = true } polkadot-node-subsystem-util = { workspace = true, default-features = true } polkadot-primitives = { workspace = true, default-features = true } -polkadot-node-jaeger = { workspace = true, default-features = true } rand = { workspace = true, default-features = true } itertools = { workspace = true } @@ -26,6 +25,8 @@ gum = { workspace = true, default-features = true } bitvec = { features = ["alloc"], workspace = true } [dev-dependencies] +sc-keystore = { workspace = true } +sp-application-crypto = { workspace = true, default-features = true } sp-authority-discovery = { workspace = true, default-features = true } sp-core = { features = ["std"], workspace = true, default-features = true } diff --git a/polkadot/node/network/approval-distribution/src/lib.rs b/polkadot/node/network/approval-distribution/src/lib.rs index a1bdc47e9fb251762e3a8c80e4d56ab3aa6eb7e2..876cc59b9c283affbcf3a1e708625d8ca41494fd 100644 --- a/polkadot/node/network/approval-distribution/src/lib.rs +++ b/polkadot/node/network/approval-distribution/src/lib.rs @@ -24,10 +24,9 @@ #![warn(missing_docs)] use self::metrics::Metrics; -use futures::{channel::oneshot, select, FutureExt as _}; +use futures::{select, FutureExt as _}; use itertools::Itertools; use net_protocol::peer_set::{ProtocolVersion, ValidationVersion}; -use polkadot_node_jaeger as jaeger; use polkadot_node_network_protocol::{ self as net_protocol, filter_by_peer_version, grid_topology::{RandomRouting, RequiredRouting, SessionGridTopologies, SessionGridTopology}, @@ -35,33 +34,46 @@ use polkadot_node_network_protocol::{ v1 as protocol_v1, v2 as protocol_v2, v3 as protocol_v3, PeerId, UnifiedReputationChange as Rep, Versioned, View, }; -use polkadot_node_primitives::approval::{ - v1::{ - AssignmentCertKind, BlockApprovalMeta, IndirectAssignmentCert, IndirectSignedApprovalVote, - }, - v2::{ - AsBitIndex, AssignmentCertKindV2, CandidateBitfield, IndirectAssignmentCertV2, - IndirectSignedApprovalVoteV2, +use polkadot_node_primitives::{ + approval::{ + criteria::{AssignmentCriteria, InvalidAssignment}, + time::{Clock, ClockExt, SystemClock, TICK_TOO_FAR_IN_FUTURE}, + v1::{ + AssignmentCertKind, BlockApprovalMeta, DelayTranche, IndirectAssignmentCert, + IndirectSignedApprovalVote, RelayVRFStory, + }, + v2::{ + AsBitIndex, AssignmentCertKindV2, CandidateBitfield, IndirectAssignmentCertV2, + IndirectSignedApprovalVoteV2, + }, }, + DISPUTE_WINDOW, }; use polkadot_node_subsystem::{ messages::{ - ApprovalCheckResult, ApprovalDistributionMessage, ApprovalVotingMessage, - AssignmentCheckResult, NetworkBridgeEvent, NetworkBridgeTxMessage, + ApprovalDistributionMessage, ApprovalVotingMessage, CheckedIndirectAssignment, + CheckedIndirectSignedApprovalVote, NetworkBridgeEvent, NetworkBridgeTxMessage, + RuntimeApiMessage, }, overseer, FromOrchestra, OverseerSignal, SpawnedSubsystem, SubsystemError, }; -use polkadot_node_subsystem_util::reputation::{ReputationAggregator, REPUTATION_CHANGE_INTERVAL}; +use polkadot_node_subsystem_util::{ + reputation::{ReputationAggregator, REPUTATION_CHANGE_INTERVAL}, + runtime::{Config as RuntimeInfoConfig, ExtendedSessionInfo, RuntimeInfo}, +}; use polkadot_primitives::{ - BlockNumber, CandidateIndex, Hash, SessionIndex, ValidatorIndex, ValidatorSignature, + BlockNumber, CandidateHash, CandidateIndex, CoreIndex, DisputeStatement, GroupIndex, Hash, + SessionIndex, Slot, ValidDisputeStatementKind, ValidatorIndex, ValidatorSignature, }; use rand::{CryptoRng, Rng, SeedableRng}; use std::{ collections::{hash_map, BTreeMap, HashMap, HashSet, VecDeque}, + sync::Arc, time::Duration, }; -mod metrics; +/// Approval distribution metrics. +pub mod metrics; #[cfg(test)] mod tests; @@ -86,6 +98,9 @@ const MAX_BITFIELD_SIZE: usize = 500; /// The Approval Distribution subsystem. pub struct ApprovalDistribution { metrics: Metrics, + slot_duration_millis: u64, + clock: Arc, + assignment_criteria: Arc, } /// Contains recently finalized @@ -343,9 +358,6 @@ pub struct State { /// Tracks recently finalized blocks. recent_outdated_blocks: RecentlyOutdated, - /// HashMap from active leaves to spans - spans: HashMap, - /// Aggression configuration. aggression_config: AggressionConfig, @@ -354,6 +366,9 @@ pub struct State { /// Aggregated reputation change reputation: ReputationAggregator, + + /// Slot duration in millis + slot_duration_millis: u64, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -488,11 +503,17 @@ struct BlockEntry { knowledge: Knowledge, /// A votes entry for each candidate indexed by [`CandidateIndex`]. candidates: Vec, + /// Information about candidate metadata. + candidates_metadata: Vec<(CandidateHash, CoreIndex, GroupIndex)>, /// The session index of this block. session: SessionIndex, /// Approval entries for whole block. These also contain all approvals in the case of multiple /// candidates being claimed by assignments. approval_entries: HashMap<(ValidatorIndex, CandidateBitfield), ApprovalEntry>, + /// The block vrf story. + vrf_story: RelayVRFStory, + /// The block slot. + slot: Slot, } impl BlockEntry { @@ -646,6 +667,41 @@ enum MessageSource { Local, } +// Encountered error while validating an assignment. +#[derive(Debug)] +enum InvalidAssignmentError { + // The vrf check for the assignment failed. + #[allow(dead_code)] + CryptoCheckFailed(InvalidAssignment), + // The assignment did not claim any valid candidate. + NoClaimedCandidates, + // Claimed invalid candidate. + #[allow(dead_code)] + ClaimedInvalidCandidateIndex { + claimed_index: usize, + max_index: usize, + }, + // The assignment claimes more candidates than the maximum allowed. + OversizedClaimedBitfield, + // `SessionInfo` was not found for the block hash in the assignment. + #[allow(dead_code)] + SessionInfoNotFound(polkadot_node_subsystem_util::runtime::Error), +} + +// Encountered error while validating an approval. +#[derive(Debug)] +enum InvalidVoteError { + // The candidate index was out of bounds. + CandidateIndexOutOfBounds, + // The validator index was out of bounds. + ValidatorIndexOutOfBounds, + // The signature of the vote was invalid. + InvalidSignature, + // `SessionInfo` was not found for the block hash in the approval. + #[allow(dead_code)] + SessionInfoNotFound(polkadot_node_subsystem_util::runtime::Error), +} + impl MessageSource { fn peer_id(&self) -> Option { match self { @@ -662,16 +718,26 @@ enum PendingMessage { #[overseer::contextbounds(ApprovalDistribution, prefix = self::overseer)] impl State { + /// Build State with specified slot duration. + pub fn with_config(slot_duration_millis: u64) -> Self { + Self { slot_duration_millis, ..Default::default() } + } + async fn handle_network_msg< N: overseer::SubsystemSender, A: overseer::SubsystemSender, + RA: overseer::SubsystemSender, >( &mut self, approval_voting_sender: &mut A, network_sender: &mut N, + runtime_api_sender: &mut RA, metrics: &Metrics, event: NetworkBridgeEvent, rng: &mut (impl CryptoRng + Rng), + assignment_criteria: &(impl AssignmentCriteria + ?Sized), + clock: &(impl Clock + ?Sized), + session_info_provider: &mut RuntimeInfo, ) { match event { NetworkBridgeEvent::PeerConnected(peer_id, role, version, authority_ids) => { @@ -727,10 +793,14 @@ impl State { self.process_incoming_peer_message( approval_voting_sender, network_sender, + runtime_api_sender, metrics, peer_id, message, rng, + assignment_criteria, + clock, + session_info_provider, ) .await; }, @@ -776,28 +846,31 @@ impl State { async fn handle_new_blocks< N: overseer::SubsystemSender, A: overseer::SubsystemSender, + RA: overseer::SubsystemSender, >( &mut self, approval_voting_sender: &mut A, network_sender: &mut N, + runtime_api_sender: &mut RA, metrics: &Metrics, metas: Vec, rng: &mut (impl CryptoRng + Rng), + assignment_criteria: &(impl AssignmentCriteria + ?Sized), + clock: &(impl Clock + ?Sized), + session_info_provider: &mut RuntimeInfo, ) { let mut new_hashes = HashSet::new(); - for meta in &metas { - let mut span = self - .spans - .get(&meta.hash) - .map(|span| span.child(&"handle-new-blocks")) - .unwrap_or_else(|| jaeger::Span::new(meta.hash, &"handle-new-blocks")) - .with_string_tag("block-hash", format!("{:?}", meta.hash)) - .with_stage(jaeger::Stage::ApprovalDistribution); + gum::debug!( + target: LOG_TARGET, + "Got new blocks {:?}", + metas.iter().map(|m| (m.hash, m.number)).collect::>(), + ); + + for meta in metas { match self.blocks.entry(meta.hash) { hash_map::Entry::Vacant(entry) => { let candidates_count = meta.candidates.len(); - span.add_uint_tag("candidates-count", candidates_count as u64); let mut candidates = Vec::with_capacity(candidates_count); candidates.resize_with(candidates_count, Default::default); @@ -809,6 +882,9 @@ impl State { candidates, session: meta.session, approval_entries: HashMap::new(), + candidates_metadata: meta.candidates, + vrf_story: meta.vrf_story, + slot: meta.slot, }); self.topologies.inc_session_refs(meta.session); @@ -823,12 +899,6 @@ impl State { } } - gum::debug!( - target: LOG_TARGET, - "Got new blocks {:?}", - metas.iter().map(|m| (m.hash, m.number)).collect::>(), - ); - { for (peer_id, PeerEntry { view, version }) in self.peer_views.iter() { let intersection = view.iter().filter(|h| new_hashes.contains(h)); @@ -883,11 +953,15 @@ impl State { self.import_and_circulate_assignment( approval_voting_sender, network_sender, + runtime_api_sender, metrics, MessageSource::Peer(peer_id), assignment, claimed_indices, rng, + assignment_criteria, + clock, + session_info_provider, ) .await; }, @@ -895,9 +969,11 @@ impl State { self.import_and_circulate_approval( approval_voting_sender, network_sender, + runtime_api_sender, metrics, MessageSource::Peer(peer_id), approval_vote, + session_info_provider, ) .await; }, @@ -943,17 +1019,22 @@ impl State { .await; } - async fn process_incoming_assignments( + async fn process_incoming_assignments( &mut self, approval_voting_sender: &mut A, network_sender: &mut N, + runtime_api_sender: &mut RA, metrics: &Metrics, peer_id: PeerId, assignments: Vec<(IndirectAssignmentCertV2, CandidateBitfield)>, rng: &mut R, + assignment_criteria: &(impl AssignmentCriteria + ?Sized), + clock: &(impl Clock + ?Sized), + session_info_provider: &mut RuntimeInfo, ) where A: overseer::SubsystemSender, N: overseer::SubsystemSender, + RA: overseer::SubsystemSender, R: CryptoRng + Rng, { for (assignment, claimed_indices) in assignments { @@ -978,11 +1059,15 @@ impl State { self.import_and_circulate_assignment( approval_voting_sender, network_sender, + runtime_api_sender, metrics, MessageSource::Peer(peer_id), assignment, claimed_indices, rng, + assignment_criteria, + clock, + session_info_provider, ) .await; } @@ -992,13 +1077,16 @@ impl State { async fn process_incoming_approvals< N: overseer::SubsystemSender, A: overseer::SubsystemSender, + RA: overseer::SubsystemSender, >( &mut self, approval_voting_sender: &mut A, network_sender: &mut N, + runtime_api_sender: &mut RA, metrics: &Metrics, peer_id: PeerId, approvals: Vec, + session_info_provider: &mut RuntimeInfo, ) { gum::trace!( target: LOG_TARGET, @@ -1028,18 +1116,21 @@ impl State { self.import_and_circulate_approval( approval_voting_sender, network_sender, + runtime_api_sender, metrics, MessageSource::Peer(peer_id), approval_vote, + session_info_provider, ) .await; } } - async fn process_incoming_peer_message( + async fn process_incoming_peer_message( &mut self, approval_voting_sender: &mut A, network_sender: &mut N, + runtime_api_sender: &mut RA, metrics: &Metrics, peer_id: PeerId, msg: Versioned< @@ -1048,9 +1139,13 @@ impl State { protocol_v3::ApprovalDistributionMessage, >, rng: &mut R, + assignment_criteria: &(impl AssignmentCriteria + ?Sized), + clock: &(impl Clock + ?Sized), + session_info_provider: &mut RuntimeInfo, ) where A: overseer::SubsystemSender, N: overseer::SubsystemSender, + RA: overseer::SubsystemSender, R: CryptoRng + Rng, { match msg { @@ -1067,10 +1162,14 @@ impl State { self.process_incoming_assignments( approval_voting_sender, network_sender, + runtime_api_sender, metrics, peer_id, sanitized_assignments, rng, + assignment_criteria, + clock, + session_info_provider, ) .await; }, @@ -1089,10 +1188,14 @@ impl State { self.process_incoming_assignments( approval_voting_sender, network_sender, + runtime_api_sender, metrics, peer_id, sanitized_assignments, rng, + assignment_criteria, + clock, + session_info_provider, ) .await; }, @@ -1102,9 +1205,11 @@ impl State { self.process_incoming_approvals( approval_voting_sender, network_sender, + runtime_api_sender, metrics, peer_id, sanitized_approvals, + session_info_provider, ) .await; }, @@ -1115,9 +1220,11 @@ impl State { self.process_incoming_approvals( approval_voting_sender, network_sender, + runtime_api_sender, metrics, peer_id, sanitized_approvals, + session_info_provider, ) .await; }, @@ -1210,7 +1317,6 @@ impl State { if let Some(block_entry) = self.blocks.remove(relay_block) { self.topologies.dec_session_refs(block_entry.session); } - self.spans.remove(&relay_block); }); // If a block was finalized, this means we may need to move our aggression @@ -1218,35 +1324,25 @@ impl State { self.enable_aggression(network_sender, Resend::No, metrics).await; } - async fn import_and_circulate_assignment( + async fn import_and_circulate_assignment( &mut self, approval_voting_sender: &mut A, network_sender: &mut N, + runtime_api_sender: &mut RA, metrics: &Metrics, source: MessageSource, assignment: IndirectAssignmentCertV2, claimed_candidate_indices: CandidateBitfield, rng: &mut R, + assignment_criteria: &(impl AssignmentCriteria + ?Sized), + clock: &(impl Clock + ?Sized), + session_info_provider: &mut RuntimeInfo, ) where A: overseer::SubsystemSender, N: overseer::SubsystemSender, + RA: overseer::SubsystemSender, R: CryptoRng + Rng, { - let _span = self - .spans - .get(&assignment.block_hash) - .map(|span| { - span.child(if source.peer_id().is_some() { - "peer-import-and-distribute-assignment" - } else { - "local-import-and-distribute-assignment" - }) - }) - .unwrap_or_else(|| jaeger::Span::new(&assignment.block_hash, "distribute-assignment")) - .with_string_tag("block-hash", format!("{:?}", assignment.block_hash)) - .with_optional_peer_id(source.peer_id().as_ref()) - .with_stage(jaeger::Stage::ApprovalDistribution); - let block_hash = assignment.block_hash; let validator_index = assignment.validator; @@ -1355,35 +1451,47 @@ impl State { return } - let (tx, rx) = oneshot::channel(); + let result = Self::check_assignment_valid( + assignment_criteria, + &entry, + &assignment, + &claimed_candidate_indices, + session_info_provider, + runtime_api_sender, + ) + .await; - approval_voting_sender - .send_message(ApprovalVotingMessage::CheckAndImportAssignment( - assignment.clone(), - claimed_candidate_indices.clone(), - tx, - )) - .await; + match result { + Ok(checked_assignment) => { + let current_tranche = clock.tranche_now(self.slot_duration_millis, entry.slot); + let too_far_in_future = + current_tranche + TICK_TOO_FAR_IN_FUTURE as DelayTranche; - let timer = metrics.time_awaiting_approval_voting(); - let result = match rx.await { - Ok(result) => result, - Err(_) => { - gum::debug!(target: LOG_TARGET, "The approval voting subsystem is down"); - return - }, - }; - drop(timer); + if checked_assignment.tranche() >= too_far_in_future { + gum::debug!( + target: LOG_TARGET, + hash = ?block_hash, + ?peer_id, + "Got an assignment too far in the future", + ); + modify_reputation( + &mut self.reputation, + network_sender, + peer_id, + COST_ASSIGNMENT_TOO_FAR_IN_THE_FUTURE, + ) + .await; + metrics.on_assignment_far(); - gum::trace!( - target: LOG_TARGET, - ?source, - ?message_subject, - ?result, - "Checked assignment", - ); - match result { - AssignmentCheckResult::Accepted => { + return + } + + approval_voting_sender + .send_message(ApprovalVotingMessage::ImportAssignment( + checked_assignment, + None, + )) + .await; modify_reputation( &mut self.reputation, network_sender, @@ -1396,47 +1504,12 @@ impl State { peer_knowledge.received.insert(message_subject.clone(), message_kind); } }, - AssignmentCheckResult::AcceptedDuplicate => { - // "duplicate" assignments aren't necessarily equal. - // There is more than one way each validator can be assigned to each core. - // cf. https://github.com/paritytech/polkadot/pull/2160#discussion_r557628699 - if let Some(peer_knowledge) = entry.known_by.get_mut(&peer_id) { - peer_knowledge.received.insert(message_subject.clone(), message_kind); - } - gum::debug!( - target: LOG_TARGET, - hash = ?block_hash, - ?peer_id, - "Got an `AcceptedDuplicate` assignment", - ); - metrics.on_assignment_duplicatevoting(); - - return - }, - AssignmentCheckResult::TooFarInFuture => { - gum::debug!( - target: LOG_TARGET, - hash = ?block_hash, - ?peer_id, - "Got an assignment too far in the future", - ); - modify_reputation( - &mut self.reputation, - network_sender, - peer_id, - COST_ASSIGNMENT_TOO_FAR_IN_THE_FUTURE, - ) - .await; - metrics.on_assignment_far(); - - return - }, - AssignmentCheckResult::Bad(error) => { + Err(error) => { gum::info!( target: LOG_TARGET, hash = ?block_hash, ?peer_id, - %error, + ?error, "Got a bad assignment from peer", ); modify_reputation( @@ -1577,6 +1650,64 @@ impl State { } } + async fn check_assignment_valid>( + assignment_criteria: &(impl AssignmentCriteria + ?Sized), + entry: &BlockEntry, + assignment: &IndirectAssignmentCertV2, + claimed_candidate_indices: &CandidateBitfield, + runtime_info: &mut RuntimeInfo, + runtime_api_sender: &mut RA, + ) -> Result { + let ExtendedSessionInfo { ref session_info, .. } = runtime_info + .get_session_info_by_index(runtime_api_sender, assignment.block_hash, entry.session) + .await + .map_err(|err| InvalidAssignmentError::SessionInfoNotFound(err))?; + + if claimed_candidate_indices.len() > session_info.n_cores as usize { + return Err(InvalidAssignmentError::OversizedClaimedBitfield) + } + + let claimed_cores: Vec = claimed_candidate_indices + .iter_ones() + .map(|candidate_index| { + entry.candidates_metadata.get(candidate_index).map(|(_, core, _)| *core).ok_or( + InvalidAssignmentError::ClaimedInvalidCandidateIndex { + claimed_index: candidate_index, + max_index: entry.candidates_metadata.len(), + }, + ) + }) + .collect::, InvalidAssignmentError>>()?; + + let Ok(claimed_cores) = claimed_cores.try_into() else { + return Err(InvalidAssignmentError::NoClaimedCandidates) + }; + + let backing_groups = claimed_candidate_indices + .iter_ones() + .flat_map(|candidate_index| { + entry.candidates_metadata.get(candidate_index).map(|(_, _, group)| *group) + }) + .collect::>(); + + assignment_criteria + .check_assignment_cert( + claimed_cores, + assignment.validator, + &polkadot_node_primitives::approval::criteria::Config::from(session_info), + entry.vrf_story.clone(), + &assignment.cert, + backing_groups, + ) + .map_err(|err| InvalidAssignmentError::CryptoCheckFailed(err)) + .map(|tranche| { + CheckedIndirectAssignment::from_checked( + assignment.clone(), + claimed_candidate_indices.clone(), + tranche, + ) + }) + } // Checks if an approval can be processed. // Returns true if we can continue with processing the approval and false otherwise. async fn check_approval_can_be_processed< @@ -1666,29 +1797,17 @@ impl State { async fn import_and_circulate_approval< N: overseer::SubsystemSender, A: overseer::SubsystemSender, + RA: overseer::SubsystemSender, >( &mut self, approval_voting_sender: &mut A, network_sender: &mut N, + runtime_api_sender: &mut RA, metrics: &Metrics, source: MessageSource, vote: IndirectSignedApprovalVoteV2, + session_info_provider: &mut RuntimeInfo, ) { - let _span = self - .spans - .get(&vote.block_hash) - .map(|span| { - span.child(if source.peer_id().is_some() { - "peer-import-and-distribute-approval" - } else { - "local-import-and-distribute-approval" - }) - }) - .unwrap_or_else(|| jaeger::Span::new(&vote.block_hash, "distribute-approval")) - .with_string_tag("block-hash", format!("{:?}", vote.block_hash)) - .with_optional_peer_id(source.peer_id().as_ref()) - .with_stage(jaeger::Stage::ApprovalDistribution); - let block_hash = vote.block_hash; let validator_index = vote.validator; let candidate_indices = &vote.candidate_indices; @@ -1740,31 +1859,16 @@ impl State { return } - let (tx, rx) = oneshot::channel(); - - approval_voting_sender - .send_message(ApprovalVotingMessage::CheckAndImportApproval(vote.clone(), tx)) - .await; - - let timer = metrics.time_awaiting_approval_voting(); - let result = match rx.await { - Ok(result) => result, - Err(_) => { - gum::debug!(target: LOG_TARGET, "The approval voting subsystem is down"); - return - }, - }; - drop(timer); + let result = + Self::check_vote_valid(&vote, &entry, session_info_provider, runtime_api_sender) + .await; - gum::trace!( - target: LOG_TARGET, - ?peer_id, - ?result, - ?vote, - "Checked approval", - ); match result { - ApprovalCheckResult::Accepted => { + Ok(vote) => { + approval_voting_sender + .send_message(ApprovalVotingMessage::ImportApproval(vote, None)) + .await; + modify_reputation( &mut self.reputation, network_sender, @@ -1782,7 +1886,7 @@ impl State { .insert(approval_knwowledge_key.0.clone(), approval_knwowledge_key.1); } }, - ApprovalCheckResult::Bad(error) => { + Err(err) => { modify_reputation( &mut self.reputation, network_sender, @@ -1790,10 +1894,11 @@ impl State { COST_INVALID_MESSAGE, ) .await; + gum::info!( target: LOG_TARGET, ?peer_id, - %error, + ?err, "Got a bad approval from peer", ); metrics.on_approval_bad(); @@ -1891,6 +1996,50 @@ impl State { } } + // Checks if the approval vote is valid. + async fn check_vote_valid>( + vote: &IndirectSignedApprovalVoteV2, + entry: &BlockEntry, + runtime_info: &mut RuntimeInfo, + runtime_api_sender: &mut RA, + ) -> Result { + if vote.candidate_indices.len() > entry.candidates_metadata.len() { + return Err(InvalidVoteError::CandidateIndexOutOfBounds) + } + + let candidate_hashes = vote + .candidate_indices + .iter_ones() + .flat_map(|candidate_index| { + entry + .candidates_metadata + .get(candidate_index) + .map(|(candidate_hash, _, _)| *candidate_hash) + }) + .collect::>(); + + let ExtendedSessionInfo { ref session_info, .. } = runtime_info + .get_session_info_by_index(runtime_api_sender, vote.block_hash, entry.session) + .await + .map_err(|err| InvalidVoteError::SessionInfoNotFound(err))?; + + let pubkey = session_info + .validators + .get(vote.validator) + .ok_or(InvalidVoteError::ValidatorIndexOutOfBounds)?; + DisputeStatement::Valid(ValidDisputeStatementKind::ApprovalCheckingMultipleCandidates( + candidate_hashes.clone(), + )) + .check_signature( + &pubkey, + *candidate_hashes.first().unwrap(), + entry.session, + &vote.signature, + ) + .map_err(|_| InvalidVoteError::InvalidSignature) + .map(|_| CheckedIndirectSignedApprovalVote::from_checked(vote.clone())) + } + /// Retrieve approval signatures from state for the given relay block/indices: fn get_approval_signatures( &mut self, @@ -1898,14 +2047,6 @@ impl State { ) -> HashMap, ValidatorSignature)> { let mut all_sigs = HashMap::new(); for (hash, index) in indices { - let _span = self - .spans - .get(&hash) - .map(|span| span.child("get-approval-signatures")) - .unwrap_or_else(|| jaeger::Span::new(&hash, "get-approval-signatures")) - .with_string_tag("block-hash", format!("{:?}", hash)) - .with_stage(jaeger::Stage::ApprovalDistribution); - let block_entry = match self.blocks.get(&hash) { None => { gum::debug!( @@ -2468,16 +2609,48 @@ async fn modify_reputation( #[overseer::contextbounds(ApprovalDistribution, prefix = self::overseer)] impl ApprovalDistribution { /// Create a new instance of the [`ApprovalDistribution`] subsystem. - pub fn new(metrics: Metrics) -> Self { - Self { metrics } + pub fn new( + metrics: Metrics, + slot_duration_millis: u64, + assignment_criteria: Arc, + ) -> Self { + Self::new_with_clock( + metrics, + slot_duration_millis, + Arc::new(SystemClock), + assignment_criteria, + ) + } + + /// Create a new instance of the [`ApprovalDistribution`] subsystem, with a custom clock. + pub fn new_with_clock( + metrics: Metrics, + slot_duration_millis: u64, + clock: Arc, + assignment_criteria: Arc, + ) -> Self { + Self { metrics, slot_duration_millis, clock, assignment_criteria } } async fn run(self, ctx: Context) { - let mut state = State::default(); + let mut state = + State { slot_duration_millis: self.slot_duration_millis, ..Default::default() }; // According to the docs of `rand`, this is a ChaCha12 RNG in practice // and will always be chosen for strong performance and security properties. let mut rng = rand::rngs::StdRng::from_entropy(); - self.run_inner(ctx, &mut state, REPUTATION_CHANGE_INTERVAL, &mut rng).await + let mut session_info_provider = RuntimeInfo::new_with_config(RuntimeInfoConfig { + keystore: None, + session_cache_lru_size: DISPUTE_WINDOW.get(), + }); + + self.run_inner( + ctx, + &mut state, + REPUTATION_CHANGE_INTERVAL, + &mut rng, + &mut session_info_provider, + ) + .await } /// Used for testing. @@ -2487,11 +2660,14 @@ impl ApprovalDistribution { state: &mut State, reputation_interval: Duration, rng: &mut (impl CryptoRng + Rng), + session_info_provider: &mut RuntimeInfo, ) { let new_reputation_delay = || futures_timer::Delay::new(reputation_interval).fuse(); let mut reputation_delay = new_reputation_delay(); let mut approval_voting_sender = ctx.sender().clone(); let mut network_sender = ctx.sender().clone(); + let mut runtime_api_sender = ctx.sender().clone(); + loop { select! { _ = reputation_delay => { @@ -2507,8 +2683,7 @@ impl ApprovalDistribution { }, }; - - if self.handle_from_orchestra(message, &mut approval_voting_sender, &mut network_sender, state, rng).await { + if self.handle_from_orchestra(message, &mut approval_voting_sender, &mut network_sender, &mut runtime_api_sender, state, rng, session_info_provider).await { return; } @@ -2523,37 +2698,38 @@ impl ApprovalDistribution { pub async fn handle_from_orchestra< N: overseer::SubsystemSender, A: overseer::SubsystemSender, + RA: overseer::SubsystemSender, >( &self, message: FromOrchestra, approval_voting_sender: &mut A, network_sender: &mut N, + runtime_api_sender: &mut RA, state: &mut State, rng: &mut (impl CryptoRng + Rng), + session_info_provider: &mut RuntimeInfo, ) -> bool { match message { FromOrchestra::Communication { msg } => Self::handle_incoming( approval_voting_sender, network_sender, + runtime_api_sender, state, msg, &self.metrics, rng, + self.assignment_criteria.as_ref(), + self.clock.as_ref(), + session_info_provider, ) .await, - FromOrchestra::Signal(OverseerSignal::ActiveLeaves(update)) => { + FromOrchestra::Signal(OverseerSignal::ActiveLeaves(_update)) => { gum::trace!(target: LOG_TARGET, "active leaves signal (ignored)"); // the relay chain blocks relevant to the approval subsystems // are those that are available, but not finalized yet // activated and deactivated heads hence are irrelevant to this subsystem, other // than for tracing purposes. - if let Some(activated) = update.activated { - let head = activated.hash; - let approval_distribution_span = - jaeger::PerLeafSpan::new(activated.span, "approval-distribution"); - state.spans.insert(head, approval_distribution_span); - } }, FromOrchestra::Signal(OverseerSignal::BlockFinalized(_hash, number)) => { gum::trace!(target: LOG_TARGET, number = %number, "finalized signal"); @@ -2567,34 +2743,51 @@ impl ApprovalDistribution { async fn handle_incoming< N: overseer::SubsystemSender, A: overseer::SubsystemSender, + RA: overseer::SubsystemSender, >( approval_voting_sender: &mut A, network_sender: &mut N, + runtime_api_sender: &mut RA, state: &mut State, msg: ApprovalDistributionMessage, metrics: &Metrics, rng: &mut (impl CryptoRng + Rng), + assignment_criteria: &(impl AssignmentCriteria + ?Sized), + clock: &(impl Clock + ?Sized), + session_info_provider: &mut RuntimeInfo, ) { match msg { ApprovalDistributionMessage::NetworkBridgeUpdate(event) => { state - .handle_network_msg(approval_voting_sender, network_sender, metrics, event, rng) + .handle_network_msg( + approval_voting_sender, + network_sender, + runtime_api_sender, + metrics, + event, + rng, + assignment_criteria, + clock, + session_info_provider, + ) .await; }, ApprovalDistributionMessage::NewBlocks(metas) => { state - .handle_new_blocks(approval_voting_sender, network_sender, metrics, metas, rng) + .handle_new_blocks( + approval_voting_sender, + network_sender, + runtime_api_sender, + metrics, + metas, + rng, + assignment_criteria, + clock, + session_info_provider, + ) .await; }, ApprovalDistributionMessage::DistributeAssignment(cert, candidate_indices) => { - let _span = state - .spans - .get(&cert.block_hash) - .map(|span| span.child("import-and-distribute-assignment")) - .unwrap_or_else(|| jaeger::Span::new(&cert.block_hash, "distribute-assignment")) - .with_string_tag("block-hash", format!("{:?}", cert.block_hash)) - .with_stage(jaeger::Stage::ApprovalDistribution); - gum::debug!( target: LOG_TARGET, ?candidate_indices, @@ -2607,11 +2800,15 @@ impl ApprovalDistribution { .import_and_circulate_assignment( approval_voting_sender, network_sender, + runtime_api_sender, &metrics, MessageSource::Local, cert, candidate_indices, rng, + assignment_criteria, + clock, + session_info_provider, ) .await; }, @@ -2627,9 +2824,11 @@ impl ApprovalDistribution { .import_and_circulate_approval( approval_voting_sender, network_sender, + runtime_api_sender, metrics, MessageSource::Local, vote, + session_info_provider, ) .await; }, diff --git a/polkadot/node/network/approval-distribution/src/metrics.rs b/polkadot/node/network/approval-distribution/src/metrics.rs index 60c7f2f6d3b895c1f406ed8f3dfbbbe5c499eab1..2f677ba415e4b45242fd03c96b041a061771efae 100644 --- a/polkadot/node/network/approval-distribution/src/metrics.rs +++ b/polkadot/node/network/approval-distribution/src/metrics.rs @@ -30,7 +30,6 @@ struct MetricsInner { aggression_l2_messages_total: prometheus::Counter, time_unify_with_peer: prometheus::Histogram, time_import_pending_now_known: prometheus::Histogram, - time_awaiting_approval_voting: prometheus::Histogram, assignments_received_result: prometheus::CounterVec, approvals_received_result: prometheus::CounterVec, } @@ -80,31 +79,19 @@ impl Metrics { .map(|metrics| metrics.time_import_pending_now_known.start_timer()) } - pub fn on_approval_already_known(&self) { - if let Some(metrics) = &self.0 { - metrics.approvals_received_result.with_label_values(&["known"]).inc() - } - } - - pub fn on_approval_entry_not_found(&self) { - if let Some(metrics) = &self.0 { - metrics.approvals_received_result.with_label_values(&["noapprovalentry"]).inc() - } - } - - pub fn on_approval_recent_outdated(&self) { + pub(crate) fn on_approval_recent_outdated(&self) { if let Some(metrics) = &self.0 { metrics.approvals_received_result.with_label_values(&["outdated"]).inc() } } - pub fn on_approval_invalid_block(&self) { + pub(crate) fn on_approval_invalid_block(&self) { if let Some(metrics) = &self.0 { metrics.approvals_received_result.with_label_values(&["invalidblock"]).inc() } } - pub fn on_approval_unknown_assignment(&self) { + pub(crate) fn on_approval_unknown_assignment(&self) { if let Some(metrics) = &self.0 { metrics .approvals_received_result @@ -113,107 +100,78 @@ impl Metrics { } } - pub fn on_approval_duplicate(&self) { + pub(crate) fn on_approval_duplicate(&self) { if let Some(metrics) = &self.0 { metrics.approvals_received_result.with_label_values(&["duplicate"]).inc() } } - pub fn on_approval_out_of_view(&self) { + pub(crate) fn on_approval_out_of_view(&self) { if let Some(metrics) = &self.0 { metrics.approvals_received_result.with_label_values(&["outofview"]).inc() } } - pub fn on_approval_good_known(&self) { + pub(crate) fn on_approval_good_known(&self) { if let Some(metrics) = &self.0 { metrics.approvals_received_result.with_label_values(&["goodknown"]).inc() } } - pub fn on_approval_bad(&self) { + pub(crate) fn on_approval_bad(&self) { if let Some(metrics) = &self.0 { metrics.approvals_received_result.with_label_values(&["bad"]).inc() } } - pub fn on_approval_unexpected(&self) { - if let Some(metrics) = &self.0 { - metrics.approvals_received_result.with_label_values(&["unexpected"]).inc() - } - } - - pub fn on_approval_bug(&self) { + pub(crate) fn on_approval_bug(&self) { if let Some(metrics) = &self.0 { metrics.approvals_received_result.with_label_values(&["bug"]).inc() } } - pub fn on_assignment_already_known(&self) { - if let Some(metrics) = &self.0 { - metrics.assignments_received_result.with_label_values(&["known"]).inc() - } - } - - pub fn on_assignment_recent_outdated(&self) { + pub(crate) fn on_assignment_recent_outdated(&self) { if let Some(metrics) = &self.0 { metrics.assignments_received_result.with_label_values(&["outdated"]).inc() } } - pub fn on_assignment_invalid_block(&self) { + pub(crate) fn on_assignment_invalid_block(&self) { if let Some(metrics) = &self.0 { metrics.assignments_received_result.with_label_values(&["invalidblock"]).inc() } } - pub fn on_assignment_duplicate(&self) { + pub(crate) fn on_assignment_duplicate(&self) { if let Some(metrics) = &self.0 { metrics.assignments_received_result.with_label_values(&["duplicate"]).inc() } } - pub fn on_assignment_out_of_view(&self) { + pub(crate) fn on_assignment_out_of_view(&self) { if let Some(metrics) = &self.0 { metrics.assignments_received_result.with_label_values(&["outofview"]).inc() } } - pub fn on_assignment_good_known(&self) { + pub(crate) fn on_assignment_good_known(&self) { if let Some(metrics) = &self.0 { metrics.assignments_received_result.with_label_values(&["goodknown"]).inc() } } - pub fn on_assignment_bad(&self) { + pub(crate) fn on_assignment_bad(&self) { if let Some(metrics) = &self.0 { metrics.assignments_received_result.with_label_values(&["bad"]).inc() } } - pub fn on_assignment_duplicatevoting(&self) { - if let Some(metrics) = &self.0 { - metrics - .assignments_received_result - .with_label_values(&["duplicatevoting"]) - .inc() - } - } - - pub fn on_assignment_far(&self) { + pub(crate) fn on_assignment_far(&self) { if let Some(metrics) = &self.0 { metrics.assignments_received_result.with_label_values(&["far"]).inc() } } - pub(crate) fn time_awaiting_approval_voting( - &self, - ) -> Option { - self.0 - .as_ref() - .map(|metrics| metrics.time_awaiting_approval_voting.start_timer()) - } - pub(crate) fn on_aggression_l1(&self) { if let Some(metrics) = &self.0 { metrics.aggression_l1_messages_total.inc(); @@ -288,13 +246,6 @@ impl MetricsTrait for Metrics { ).buckets(vec![0.0001, 0.0004, 0.0016, 0.0064, 0.0256, 0.1024, 0.4096, 1.6384, 3.2768, 4.9152, 6.5536,]))?, registry, )?, - time_awaiting_approval_voting: prometheus::register( - prometheus::Histogram::with_opts(prometheus::HistogramOpts::new( - "polkadot_parachain_time_awaiting_approval_voting", - "Time spent awaiting a reply from the Approval Voting Subsystem.", - ).buckets(vec![0.0001, 0.0004, 0.0016, 0.0064, 0.0256, 0.1024, 0.4096, 1.6384, 3.2768, 4.9152, 6.5536,]))?, - registry, - )?, assignments_received_result: prometheus::register( prometheus::CounterVec::new( prometheus::Opts::new( diff --git a/polkadot/node/network/approval-distribution/src/tests.rs b/polkadot/node/network/approval-distribution/src/tests.rs index 1ca571721ea9063c9794eaa2c9017fcecdad2e7c..063e71f2f5289f3108468e81273dbca837f1c07e 100644 --- a/polkadot/node/network/approval-distribution/src/tests.rs +++ b/polkadot/node/network/approval-distribution/src/tests.rs @@ -16,7 +16,7 @@ use super::*; use assert_matches::assert_matches; -use futures::{executor, future, Future}; +use futures::{channel::oneshot, executor, future, Future}; use polkadot_node_network_protocol::{ grid_topology::{SessionGridTopology, TopologyPeerInfo}, our_view, @@ -24,6 +24,7 @@ use polkadot_node_network_protocol::{ view, ObservedRole, }; use polkadot_node_primitives::approval::{ + criteria, v1::{ AssignmentCert, AssignmentCertKind, IndirectAssignmentCert, IndirectSignedApprovalVote, VrfPreOutput, VrfProof, VrfSignature, @@ -34,12 +35,17 @@ use polkadot_node_primitives::approval::{ }, }; use polkadot_node_subsystem::messages::{ - network_bridge_event, AllMessages, ApprovalCheckError, ReportPeerMessage, + network_bridge_event, AllMessages, ReportPeerMessage, RuntimeApiRequest, }; use polkadot_node_subsystem_util::{reputation::add_reputation, TimeoutExt as _}; -use polkadot_primitives::{AuthorityDiscoveryId, BlakeTwo256, CoreIndex, HashT}; +use polkadot_primitives::{ + ApprovalVoteMultipleCandidates, AuthorityDiscoveryId, BlakeTwo256, CoreIndex, ExecutorParams, + HashT, NodeFeatures, SessionInfo, ValidatorId, +}; use polkadot_primitives_test_helpers::dummy_signature; use rand::SeedableRng; +use sc_keystore::{Keystore, LocalKeystore}; +use sp_application_crypto::AppCrypto; use sp_authority_discovery::AuthorityPair as AuthorityDiscoveryPair; use sp_core::crypto::Pair as PairT; use std::time::Duration; @@ -47,6 +53,8 @@ type VirtualOverseer = polkadot_node_subsystem_test_helpers::TestSubsystemContextHandle; fn test_harness>( + assignment_criteria: Arc, + clock: Arc, mut state: State, test_fn: impl FnOnce(VirtualOverseer) -> T, ) -> State { @@ -56,13 +64,29 @@ fn test_harness>( let (context, virtual_overseer) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool.clone()); - let subsystem = ApprovalDistribution::new(Default::default()); + let subsystem = ApprovalDistribution::new_with_clock( + Metrics::default(), + Default::default(), + clock, + assignment_criteria, + ); { let mut rng = rand_chacha::ChaCha12Rng::seed_from_u64(12345); + let mut session_info_provider = RuntimeInfo::new_with_config(RuntimeInfoConfig { + keystore: None, + session_cache_lru_size: DISPUTE_WINDOW.get(), + }); + let (tx, rx) = oneshot::channel(); let subsystem = async { subsystem - .run_inner(context, &mut state, REPUTATION_CHANGE_TEST_INTERVAL, &mut rng) + .run_inner( + context, + &mut state, + REPUTATION_CHANGE_TEST_INTERVAL, + &mut rng, + &mut session_info_provider, + ) .await; tx.send(()).expect("Fail to notify subystem is done"); }; @@ -121,6 +145,41 @@ async fn overseer_recv(overseer: &mut VirtualOverseer) -> AllMessages { msg } +async fn provide_session(virtual_overseer: &mut VirtualOverseer, session_info: SessionInfo) { + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::RuntimeApi( + RuntimeApiMessage::Request( + _, + RuntimeApiRequest::SessionInfo(_, si_tx), + ) + ) => { + si_tx.send(Ok(Some(session_info.clone()))).unwrap(); + } + ); + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::RuntimeApi( + RuntimeApiMessage::Request( + _, + RuntimeApiRequest::SessionExecutorParams(_, si_tx), + ) + ) => { + // Make sure all SessionExecutorParams calls are not made for the leaf (but for its relay parent) + si_tx.send(Ok(Some(ExecutorParams::default()))).unwrap(); + } + ); + + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::RuntimeApi( + RuntimeApiMessage::Request(_, RuntimeApiRequest::NodeFeatures(_, si_tx), ) + ) => { + si_tx.send(Ok(NodeFeatures::EMPTY)).unwrap(); + } + ); +} + fn make_peers_and_authority_ids(n: usize) -> Vec<(PeerId, AuthorityDiscoveryId)> { (0..n) .map(|_| { @@ -335,6 +394,30 @@ fn fake_assignment_cert_v2( } } +fn fake_assignment_cert_delay( + block_hash: Hash, + validator: ValidatorIndex, + core_bitfield: CoreBitfield, +) -> IndirectAssignmentCertV2 { + let ctx = schnorrkel::signing_context(RELAY_VRF_MODULO_CONTEXT); + let msg = b"WhenParachains?"; + let mut prng = rand_core::OsRng; + let keypair = schnorrkel::Keypair::generate_with(&mut prng); + let (inout, proof, _) = keypair.vrf_sign(ctx.bytes(msg)); + let preout = inout.to_preout(); + + IndirectAssignmentCertV2 { + block_hash, + validator, + cert: AssignmentCertV2 { + kind: AssignmentCertKindV2::RelayVRFDelay { + core_index: CoreIndex(core_bitfield.iter_ones().next().unwrap() as u32), + }, + vrf: VrfSignature { pre_output: VrfPreOutput(preout), proof: VrfProof(proof) }, + }, + } +} + async fn expect_reputation_change( virtual_overseer: &mut VirtualOverseer, peer_id: &PeerId, @@ -378,6 +461,86 @@ fn state_with_reputation_delay() -> State { State { reputation: ReputationAggregator::new(|_| false), ..Default::default() } } +fn dummy_session_info_valid( + index: SessionIndex, + keystore: &mut LocalKeystore, + num_validators: usize, +) -> SessionInfo { + let keys = (0..num_validators) + .map(|_| { + keystore + .sr25519_generate_new(ValidatorId::ID, Some("//Node")) + .expect("Insert key into keystore") + }) + .collect_vec(); + + SessionInfo { + validators: keys.clone().into_iter().map(|key| key.into()).collect(), + discovery_keys: keys.clone().into_iter().map(|key| key.into()).collect(), + assignment_keys: keys.clone().into_iter().map(|key| key.into()).collect(), + validator_groups: Default::default(), + n_cores: 20, + zeroth_delay_tranche_width: index as _, + relay_vrf_modulo_samples: index as _, + n_delay_tranches: index as _, + no_show_slots: index as _, + needed_approvals: index as _, + active_validator_indices: Vec::new(), + dispute_period: 6, + random_seed: [0u8; 32], + } +} + +fn signature_for( + keystore: &LocalKeystore, + session: &SessionInfo, + candidate_hashes: Vec, + validator_index: ValidatorIndex, +) -> ValidatorSignature { + let payload = ApprovalVoteMultipleCandidates(&candidate_hashes).signing_payload(1); + let sign_key = session.validators.get(validator_index).unwrap().clone(); + let signature = keystore + .sr25519_sign(ValidatorId::ID, &sign_key.into(), &payload[..]) + .unwrap() + .unwrap(); + signature.into() +} + +struct MockAssignmentCriteria { + tranche: + Result, +} + +impl AssignmentCriteria for MockAssignmentCriteria { + fn compute_assignments( + &self, + _keystore: &LocalKeystore, + _relay_vrf_story: polkadot_node_primitives::approval::v1::RelayVRFStory, + _config: &criteria::Config, + _leaving_cores: Vec<( + CandidateHash, + polkadot_primitives::CoreIndex, + polkadot_primitives::GroupIndex, + )>, + _enable_assignments_v2: bool, + ) -> HashMap { + HashMap::new() + } + + fn check_assignment_cert( + &self, + _claimed_core_bitfield: polkadot_node_primitives::approval::v2::CoreBitfield, + _validator_index: polkadot_primitives::ValidatorIndex, + _config: &criteria::Config, + _relay_vrf_story: polkadot_node_primitives::approval::v1::RelayVRFStory, + _assignment: &polkadot_node_primitives::approval::v2::AssignmentCertV2, + _backing_groups: Vec, + ) -> Result + { + self.tranche + } +} + /// import an assignment /// connect a new peer /// the new peer sends us the same assignment @@ -391,89 +554,97 @@ fn try_import_the_same_assignment() { let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // setup peers - setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V1).await; - setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V1).await; - setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V1).await; - - // Set up a gossip topology, where a, b, c and d are topology neighbors to the node under - // testing. - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - setup_gossip_topology( - overseer, - make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), - ) - .await; + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Arc::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + + setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V1).await; + setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V1).await; + setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V1).await; + + // under testing. + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 2, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 2, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // send the assignment related to `hash` + let validator_index = ValidatorIndex(0); + let cert = fake_assignment_cert(hash, validator_index); + let assignments = vec![(cert.clone(), 0u32)]; - // send the assignment related to `hash` - let validator_index = ValidatorIndex(0); - let cert = fake_assignment_cert(hash, validator_index); - let assignments = vec![(cert.clone(), 0u32)]; + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer(overseer, &peer_a, msg).await; - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); - send_message_from_peer(overseer, &peer_a, msg).await; + expect_reputation_change(overseer, &peer_a, COST_UNEXPECTED_MESSAGE).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; + // send an `Accept` message from the Approval Voting subsystem + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, + _, + )) => { + assert_eq!(assignment.candidate_indices(), &0u32.into()); + assert_eq!(assignment.assignment(), &cert.into()); + assert_eq!(assignment.tranche(), 0); + } + ); - expect_reputation_change(overseer, &peer_a, COST_UNEXPECTED_MESSAGE).await; + expect_reputation_change(overseer, &peer_a, BENEFIT_VALID_MESSAGE_FIRST).await; - // send an `Accept` message from the Approval Voting subsystem - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - assignment, - claimed_indices, - tx, - )) => { - assert_eq!(claimed_indices, 0u32.into()); - assert_eq!(assignment, cert.into()); - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); - - expect_reputation_change(overseer, &peer_a, BENEFIT_VALID_MESSAGE_FIRST).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers.len(), 2); - assert_eq!(assignments.len(), 1); - } - ); + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 2); + assert_eq!(assignments.len(), 1); + } + ); - // setup new peer with V2 - setup_peer_with_view(overseer, &peer_d, view![], ValidationVersion::V3).await; + // setup new peer with V2 + setup_peer_with_view(overseer, &peer_d, view![], ValidationVersion::V3).await; - // send the same assignment from peer_d - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments); - send_message_from_peer(overseer, &peer_d, msg).await; + // send the same assignment from peer_d + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments); + send_message_from_peer(overseer, &peer_d, msg).await; - expect_reputation_change(overseer, &peer_d, COST_UNEXPECTED_MESSAGE).await; - expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE).await; + expect_reputation_change(overseer, &peer_d, COST_UNEXPECTED_MESSAGE).await; + expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE).await; - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); } /// Just like `try_import_the_same_assignment` but use `VRFModuloCompact` assignments for multiple @@ -488,97 +659,106 @@ fn try_import_the_same_assignment_v2() { let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // setup peers - setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V3).await; - setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V3).await; - setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V3).await; - - // Set up a gossip topology, where a, b, c and d are topology neighbors to the node under - // testing. - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - setup_gossip_topology( - overseer, - make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), - ) - .await; + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Arc::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup peers + setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V3).await; + setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V3).await; + setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V3).await; + + // Set up a gossip topology, where a, b, c and d are topology neighbors to the node + // under testing. + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 2, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - // send the assignment related to `hash` - let validator_index = ValidatorIndex(0); - let cores = vec![1, 2, 3, 4]; - let core_bitfield: CoreBitfield = cores - .iter() - .map(|index| CoreIndex(*index)) - .collect::>() - .try_into() - .unwrap(); - - let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfield.clone()); - let assignments = vec![(cert.clone(), cores.clone().try_into().unwrap())]; - - let msg = protocol_v3::ApprovalDistributionMessage::Assignments(assignments.clone()); - send_message_from_peer_v3(overseer, &peer_a, msg).await; - - expect_reputation_change(overseer, &peer_a, COST_UNEXPECTED_MESSAGE).await; - - // send an `Accept` message from the Approval Voting subsystem - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - assignment, - claimed_indices, - tx, - )) => { - assert_eq!(claimed_indices, cores.try_into().unwrap()); - assert_eq!(assignment, cert.into()); - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); - - expect_reputation_change(overseer, &peer_a, BENEFIT_VALID_MESSAGE_FIRST).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers.len(), 2); - assert_eq!(assignments.len(), 1); - } - ); + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 2, + candidates: vec![Default::default(); 5], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // send the assignment related to `hash` + let validator_index = ValidatorIndex(0); + let cores = vec![1, 2, 3, 4]; + let core_bitfield: CoreBitfield = cores + .iter() + .map(|index| CoreIndex(*index)) + .collect::>() + .try_into() + .unwrap(); + + let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfield.clone()); + let assignments = vec![(cert.clone(), cores.clone().try_into().unwrap())]; + + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer_v3(overseer, &peer_a, msg).await; + + expect_reputation_change(overseer, &peer_a, COST_UNEXPECTED_MESSAGE).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; + // send an `Accept` message from the Approval Voting subsystem + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, + _, + )) => { + assert_eq!(assignment.candidate_indices(), &cores.try_into().unwrap()); + assert_eq!(assignment.assignment(), &cert.into()); + assert_eq!(assignment.tranche(), 0); + } + ); + + expect_reputation_change(overseer, &peer_a, BENEFIT_VALID_MESSAGE_FIRST).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 2); + assert_eq!(assignments.len(), 1); + } + ); - // setup new peer - setup_peer_with_view(overseer, &peer_d, view![], ValidationVersion::V3).await; + // setup new peer + setup_peer_with_view(overseer, &peer_d, view![], ValidationVersion::V3).await; - // send the same assignment from peer_d - let msg = protocol_v3::ApprovalDistributionMessage::Assignments(assignments); - send_message_from_peer_v3(overseer, &peer_d, msg).await; + // send the same assignment from peer_d + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(assignments); + send_message_from_peer_v3(overseer, &peer_d, msg).await; - expect_reputation_change(overseer, &peer_d, COST_UNEXPECTED_MESSAGE).await; - expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE).await; + expect_reputation_change(overseer, &peer_d, COST_UNEXPECTED_MESSAGE).await; + expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE).await; - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); } /// import an assignment @@ -590,55 +770,65 @@ fn delay_reputation_change() { let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); - let _ = test_harness(state_with_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - - // Setup peers - setup_peer_with_view(overseer, &peer, view![], ValidationVersion::V1).await; - - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 2, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - // send the assignment related to `hash` - let validator_index = ValidatorIndex(0); - let cert = fake_assignment_cert(hash, validator_index); - let assignments = vec![(cert.clone(), 0u32)]; + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Arc::new(SystemClock {}), + state_with_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + + // Setup peers + setup_peer_with_view(overseer, &peer, view![], ValidationVersion::V1).await; + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 2, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // send the assignment related to `hash` + let validator_index = ValidatorIndex(0); + let cert = fake_assignment_cert(hash, validator_index); + let assignments = vec![(cert.clone(), 0u32)]; - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); - send_message_from_peer(overseer, &peer, msg).await; + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer(overseer, &peer, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; - // send an `Accept` message from the Approval Voting subsystem - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - assignment, - claimed_candidates, - tx, - )) => { - assert_eq!(assignment.cert, cert.cert.into()); - assert_eq!(claimed_candidates, vec![0u32].try_into().unwrap()); - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); - expect_reputation_changes( - overseer, - &peer, - vec![COST_UNEXPECTED_MESSAGE, BENEFIT_VALID_MESSAGE_FIRST], - ) - .await; - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + // send an `Accept` message from the Approval Voting subsystem + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, + _, + )) => { + assert_eq!(assignment.assignment().cert, cert.cert.into()); + assert_eq!(assignment.candidate_indices(), &vec![0u32].try_into().unwrap()); + assert_eq!(assignment.tranche(), 0); + } + ); + expect_reputation_changes( + overseer, + &peer, + vec![COST_UNEXPECTED_MESSAGE, BENEFIT_VALID_MESSAGE_FIRST], + ) + .await; + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); + virtual_overseer + }, + ); } /// @@ -653,77 +843,88 @@ fn spam_attack_results_in_negative_reputation_change() { let peer_a = PeerId::random(); let hash_b = Hash::repeat_byte(0xBB); - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - let peer = &peer_a; - setup_peer_with_view(overseer, peer, view![], ValidationVersion::V1).await; - - // new block `hash_b` with 20 candidates - let candidates_count = 20; - let meta = BlockApprovalMeta { - hash: hash_b, - parent_hash, - number: 2, - candidates: vec![Default::default(); candidates_count], - slot: 1.into(), - session: 1, - }; - - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Arc::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + let peer = &peer_a; + setup_peer_with_view(overseer, peer, view![], ValidationVersion::V1).await; + + // new block `hash_b` with 20 candidates + let candidates_count = 20; + let meta = BlockApprovalMeta { + hash: hash_b, + parent_hash, + number: 2, + candidates: vec![Default::default(); candidates_count], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // send 20 assignments related to `hash_b` + // to populate our knowledge + let assignments: Vec<_> = (0..candidates_count) + .map(|candidate_index| { + let validator_index = ValidatorIndex(candidate_index as u32); + let cert = fake_assignment_cert(hash_b, validator_index); + (cert, candidate_index as u32) + }) + .collect(); - // send 20 assignments related to `hash_b` - // to populate our knowledge - let assignments: Vec<_> = (0..candidates_count) - .map(|candidate_index| { - let validator_index = ValidatorIndex(candidate_index as u32); - let cert = fake_assignment_cert(hash_b, validator_index); - (cert, candidate_index as u32) - }) - .collect(); - - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); - send_message_from_peer(overseer, peer, msg.clone()).await; - - for i in 0..candidates_count { - expect_reputation_change(overseer, peer, COST_UNEXPECTED_MESSAGE).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - assignment, - claimed_candidate_index, - tx, - )) => { - assert_eq!(assignment, assignments[i].0.clone().into()); - assert_eq!(claimed_candidate_index, assignments[i].1.into()); - tx.send(AssignmentCheckResult::Accepted).unwrap(); + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer(overseer, peer, msg.clone()).await; + + for i in 0..candidates_count { + expect_reputation_change(overseer, peer, COST_UNEXPECTED_MESSAGE).await; + if i == 0 { + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; } - ); + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, + _, + )) => { + assert_eq!(assignment.assignment(), &assignments[i].0.clone().into()); + assert_eq!(assignment.candidate_indices(), &assignments[i].1.into()); + assert_eq!(assignment.tranche(), 0); + } + ); - expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE_FIRST).await; - } + expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE_FIRST).await; + } - // send a view update that removes block B from peer's view by bumping the finalized_number - overseer_send( - overseer, - ApprovalDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerViewChange( - *peer, - View::with_finalized(2), - )), - ) - .await; + // send a view update that removes block B from peer's view by bumping the + // finalized_number + overseer_send( + overseer, + ApprovalDistributionMessage::NetworkBridgeUpdate( + NetworkBridgeEvent::PeerViewChange(*peer, View::with_finalized(2)), + ), + ) + .await; - // send the assignments again - send_message_from_peer(overseer, peer, msg.clone()).await; + // send the assignments again + send_message_from_peer(overseer, peer, msg.clone()).await; - // each of them will incur `COST_UNEXPECTED_MESSAGE`, not only the first one - for _ in 0..candidates_count { - expect_reputation_change(overseer, peer, COST_UNEXPECTED_MESSAGE).await; - expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE).await; - } - virtual_overseer - }); + // each of them will incur `COST_UNEXPECTED_MESSAGE`, not only the first one + for _ in 0..candidates_count { + expect_reputation_change(overseer, peer, COST_UNEXPECTED_MESSAGE).await; + expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE).await; + } + virtual_overseer + }, + ); } /// Imagine we send a message to peer A and peer B. @@ -739,86 +940,94 @@ fn peer_sending_us_the_same_we_just_sent_them_is_ok() { let peers = make_peers_and_authority_ids(8); let peer_a = peers.first().unwrap().0; - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - let peer = &peer_a; - setup_peer_with_view(overseer, peer, view![], ValidationVersion::V1).await; - - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Setup a topology where peer_a is neighbor to current node. - setup_gossip_topology( - overseer, - make_gossip_topology(1, &peers_with_optional_peer_id, &[0], &[2], 1), - ) - .await; + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Arc::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + let peer = &peer_a; + setup_peer_with_view(overseer, peer, view![], ValidationVersion::V1).await; + + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Setup a topology where peer_a is neighbor to current node. + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0], &[2], 1), + ) + .await; - // new block `hash` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - // import an assignment related to `hash` locally - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; - let cert = fake_assignment_cert(hash, validator_index); - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment( - cert.clone().into(), - candidate_index.into(), - ), - ) - .await; + // new block `hash` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // import an assignment related to `hash` locally + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; + let cert = fake_assignment_cert(hash, validator_index); + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_index.into(), + ), + ) + .await; - // update peer view to include the hash - overseer_send( - overseer, - ApprovalDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerViewChange( - *peer, - view![hash], - )), - ) - .await; + // update peer view to include the hash + overseer_send( + overseer, + ApprovalDistributionMessage::NetworkBridgeUpdate( + NetworkBridgeEvent::PeerViewChange(*peer, view![hash]), + ), + ) + .await; - // we should send them the assignment - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers.len(), 1); - assert_eq!(assignments.len(), 1); - } - ); + // we should send them the assignment + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 1); + } + ); - // but if someone else is sending it the same assignment - // the peer could send us it as well - let assignments = vec![(cert, candidate_index)]; - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments); - send_message_from_peer(overseer, peer, msg.clone()).await; + // but if someone else is sending it the same assignment + // the peer could send us it as well + let assignments = vec![(cert, candidate_index)]; + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments); + send_message_from_peer(overseer, peer, msg.clone()).await; - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "we should not punish the peer"); + assert!( + overseer.recv().timeout(TIMEOUT).await.is_none(), + "we should not punish the peer" + ); - // send the assignments again - send_message_from_peer(overseer, peer, msg).await; + // send the assignments again + send_message_from_peer(overseer, peer, msg).await; - // now we should - expect_reputation_change(overseer, peer, COST_DUPLICATE_MESSAGE).await; - virtual_overseer - }); + // now we should + expect_reputation_change(overseer, peer, COST_DUPLICATE_MESSAGE).await; + virtual_overseer + }, + ); } #[test] @@ -830,116 +1039,134 @@ fn import_approval_happy_path_v1_v2_peers() { let peer_c = peers.get(2).unwrap().0; let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); + let candidate_hash = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xBB)); + + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Arc::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup peers with V1 and V2 protocol versions + setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V1).await; + setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V3).await; + setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V1).await; + + let mut keystore = LocalKeystore::in_memory(); + let session = dummy_session_info_valid(1, &mut keystore, 1); + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![(candidate_hash, 0.into(), 0.into()); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Set up a gossip topology, where a, b, and c are topology neighbors to the node. + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // setup peers with V1 and V2 protocol versions - setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V1).await; - setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V3).await; - setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V1).await; - - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Set up a gossip topology, where a, b, and c are topology neighbors to the node. - setup_gossip_topology( - overseer, - make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), - ) - .await; - - // import an assignment related to `hash` locally - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; - let cert = fake_assignment_cert(hash, validator_index); - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment( - cert.clone().into(), - candidate_index.into(), - ), - ) - .await; + // import an assignment related to `hash` locally + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; + let cert = fake_assignment_cert(hash, validator_index); + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_index.into(), + ), + ) + .await; - // 1 peer is v1 - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers.len(), 1); - assert_eq!(assignments.len(), 1); - } - ); - - // 1 peer is v2 - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers.len(), 1); - assert_eq!(assignments.len(), 1); - } - ); - - // send the an approval from peer_b - let approval = IndirectSignedApprovalVoteV2 { - block_hash: hash, - candidate_indices: candidate_index.into(), - validator: validator_index, - signature: dummy_signature(), - }; - let msg: protocol_v3::ApprovalDistributionMessage = - protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer_v3(overseer, &peer_b, msg).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportApproval( - vote, - tx, - )) => { - assert_eq!(vote, approval); - tx.send(ApprovalCheckResult::Accepted).unwrap(); - } - ); - - expect_reputation_change(overseer, &peer_b, BENEFIT_VALID_MESSAGE_FIRST).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Approvals(approvals) - )) - )) => { - assert_eq!(peers.len(), 1); - assert_eq!(approvals.len(), 1); - } - ); - virtual_overseer - }); + // 1 peer is v1 + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 1); + } + ); + + // 1 peer is v2 + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 1); + } + ); + + // send the an approval from peer_b + let approval = IndirectSignedApprovalVoteV2 { + block_hash: hash, + candidate_indices: candidate_index.into(), + validator: validator_index, + signature: signature_for( + &keystore, + &session, + vec![candidate_hash], + validator_index, + ), + }; + let msg: protocol_v3::ApprovalDistributionMessage = + protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); + send_message_from_peer_v3(overseer, &peer_b, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportApproval( + vote, _, + )) => { + assert_eq!(Into::::into(vote), approval); + } + ); + + expect_reputation_change(overseer, &peer_b, BENEFIT_VALID_MESSAGE_FIRST).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Approvals(approvals) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(approvals.len(), 1); + } + ); + virtual_overseer + }, + ); } // Test a v2 approval that signs multiple candidate is correctly processed. @@ -952,103 +1179,123 @@ fn import_approval_happy_path_v2() { let peer_c = peers.get(2).unwrap().0; let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); + let candidate_hash_first = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xBB)); + let candidate_hash_second = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xCC)); + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Arc::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup peers with V2 protocol versions + setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V3).await; + setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V3).await; + setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V3).await; + let mut keystore = LocalKeystore::in_memory(); + let session = dummy_session_info_valid(1, &mut keystore, 1); + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![ + (candidate_hash_first, 0.into(), 0.into()), + (candidate_hash_second, 1.into(), 1.into()), + ], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Set up a gossip topology, where a, b, and c are topology neighbors to the node. + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // setup peers with V2 protocol versions - setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V3).await; - setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V3).await; - setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V3).await; - - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 2], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Set up a gossip topology, where a, b, and c are topology neighbors to the node. - setup_gossip_topology( - overseer, - make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), - ) - .await; + // import an assignment related to `hash` locally + let validator_index = ValidatorIndex(0); + let candidate_indices: CandidateBitfield = + vec![0 as CandidateIndex, 1 as CandidateIndex].try_into().unwrap(); + let candidate_bitfields = vec![CoreIndex(0), CoreIndex(1)].try_into().unwrap(); + let cert = fake_assignment_cert_v2(hash, validator_index, candidate_bitfields); + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_indices.clone(), + ), + ) + .await; - // import an assignment related to `hash` locally - let validator_index = ValidatorIndex(0); - let candidate_indices: CandidateBitfield = - vec![0 as CandidateIndex, 1 as CandidateIndex].try_into().unwrap(); - let candidate_bitfields = vec![CoreIndex(0), CoreIndex(1)].try_into().unwrap(); - let cert = fake_assignment_cert_v2(hash, validator_index, candidate_bitfields); - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment( - cert.clone().into(), - candidate_indices.clone(), - ), - ) - .await; + // 1 peer is v2 + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 2); + assert_eq!(assignments.len(), 1); + } + ); - // 1 peer is v2 - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers.len(), 2); - assert_eq!(assignments.len(), 1); - } - ); - - // send the an approval from peer_b - let approval = IndirectSignedApprovalVoteV2 { - block_hash: hash, - candidate_indices, - validator: validator_index, - signature: dummy_signature(), - }; - let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer_v3(overseer, &peer_b, msg).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportApproval( - vote, - tx, - )) => { - assert_eq!(vote, approval); - tx.send(ApprovalCheckResult::Accepted).unwrap(); - } - ); - - expect_reputation_change(overseer, &peer_b, BENEFIT_VALID_MESSAGE_FIRST).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Approvals(approvals) - )) - )) => { - assert_eq!(peers.len(), 1); - assert_eq!(approvals.len(), 1); - } - ); - virtual_overseer - }); + // send the an approval from peer_b + let approval = IndirectSignedApprovalVoteV2 { + block_hash: hash, + candidate_indices, + validator: validator_index, + signature: signature_for( + &keystore, + &session, + vec![candidate_hash_first, candidate_hash_second], + validator_index, + ), + }; + let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); + send_message_from_peer_v3(overseer, &peer_b, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportApproval( + vote, _, + )) => { + assert_eq!(Into::::into(vote), approval); + } + ); + + expect_reputation_change(overseer, &peer_b, BENEFIT_VALID_MESSAGE_FIRST).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Approvals(approvals) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(approvals.len(), 1); + } + ); + virtual_overseer + }, + ); } // Tests that votes that cover multiple assignments candidates are correctly processed on importing @@ -1062,187 +1309,203 @@ fn multiple_assignments_covered_with_one_approval_vote() { let peer_d = peers.get(4).unwrap().0; let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); + let candidate_hash_first = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xBB)); + let candidate_hash_second = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xCC)); + + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Arc::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup peers with V2 protocol versions + setup_peer_with_view(overseer, &peer_a, view![hash], ValidationVersion::V3).await; + setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V3).await; + setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V3).await; + setup_peer_with_view(overseer, &peer_d, view![hash], ValidationVersion::V3).await; + + let mut keystore = LocalKeystore::in_memory(); + let session = dummy_session_info_valid(1, &mut keystore, 5); + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![ + (candidate_hash_first, 0.into(), 0.into()), + (candidate_hash_second, 1.into(), 1.into()), + ], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Set up a gossip topology, where a, b, and c, d are topology neighbors to the node. + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // setup peers with V2 protocol versions - setup_peer_with_view(overseer, &peer_a, view![hash], ValidationVersion::V3).await; - setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V3).await; - setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V3).await; - setup_peer_with_view(overseer, &peer_d, view![hash], ValidationVersion::V3).await; - - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 2], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Set up a gossip topology, where a, b, and c, d are topology neighbors to the node. - setup_gossip_topology( - overseer, - make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), - ) - .await; + // import an assignment related to `hash` locally + let validator_index = ValidatorIndex(2); // peer_c is the originator + let candidate_indices: CandidateBitfield = + vec![0 as CandidateIndex, 1 as CandidateIndex].try_into().unwrap(); + + let core_bitfields = vec![CoreIndex(0)].try_into().unwrap(); + let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfields); - // import an assignment related to `hash` locally - let validator_index = ValidatorIndex(2); // peer_c is the originator - let candidate_indices: CandidateBitfield = - vec![0 as CandidateIndex, 1 as CandidateIndex].try_into().unwrap(); + // send the candidate 0 assignment from peer_b + let assignment = IndirectAssignmentCertV2 { + block_hash: hash, + validator: validator_index, + cert: cert.cert, + }; + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(vec![( + assignment, + (0 as CandidateIndex).into(), + )]); + send_message_from_peer_v3(overseer, &peer_d, msg).await; + provide_session(overseer, session.clone()).await; - let core_bitfields = vec![CoreIndex(0)].try_into().unwrap(); - let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfields); + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, + _, + )) => { + assert_eq!(assignment.tranche(), 0); + } + ); + expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE_FIRST).await; - // send the candidate 0 assignment from peer_b - let assignment = IndirectAssignmentCertV2 { - block_hash: hash, - validator: validator_index, - cert: cert.cert, - }; - let msg = protocol_v3::ApprovalDistributionMessage::Assignments(vec![( - assignment, - (0 as CandidateIndex).into(), - )]); - send_message_from_peer_v3(overseer, &peer_d, msg).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - _, _, - tx, - )) => { - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); - expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE_FIRST).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert!(peers.len() >= 2); - assert!(peers.contains(&peer_a)); - assert!(peers.contains(&peer_b)); - assert_eq!(assignments.len(), 1); - } - ); + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert!(peers.len() >= 2); + assert!(peers.contains(&peer_a)); + assert!(peers.contains(&peer_b)); + assert_eq!(assignments.len(), 1); + } + ); - let candidate_bitfields = vec![CoreIndex(1)].try_into().unwrap(); - let cert = fake_assignment_cert_v2(hash, validator_index, candidate_bitfields); + let candidate_bitfields = vec![CoreIndex(1)].try_into().unwrap(); + let cert = fake_assignment_cert_v2(hash, validator_index, candidate_bitfields); - // send the candidate 1 assignment from peer_c - let assignment = IndirectAssignmentCertV2 { - block_hash: hash, - validator: validator_index, - cert: cert.cert, - }; - let msg = protocol_v3::ApprovalDistributionMessage::Assignments(vec![( - assignment, - (1 as CandidateIndex).into(), - )]); - - send_message_from_peer_v3(overseer, &peer_c, msg).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - _, _, - tx, - )) => { - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); - expect_reputation_change(overseer, &peer_c, BENEFIT_VALID_MESSAGE_FIRST).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert!(peers.len() >= 2); - assert!(peers.contains(&peer_b)); - assert!(peers.contains(&peer_a)); - assert_eq!(assignments.len(), 1); - } - ); - - // send an approval from peer_b - let approval = IndirectSignedApprovalVoteV2 { - block_hash: hash, - candidate_indices, - validator: validator_index, - signature: dummy_signature(), - }; - let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer_v3(overseer, &peer_d, msg).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportApproval( - vote, - tx, - )) => { - assert_eq!(vote, approval); - tx.send(ApprovalCheckResult::Accepted).unwrap(); - } - ); - - expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE_FIRST).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Approvals(approvals) - )) - )) => { - assert!(peers.len() >= 2); - assert!(peers.contains(&peer_b)); - assert!(peers.contains(&peer_a)); - assert_eq!(approvals.len(), 1); - } - ); - for candidate_index in 0..1 { - let (tx_distribution, rx_distribution) = oneshot::channel(); - let mut candidates_requesting_signatures = HashSet::new(); - candidates_requesting_signatures.insert((hash, candidate_index)); - overseer_send( - overseer, - ApprovalDistributionMessage::GetApprovalSignatures( - candidates_requesting_signatures, - tx_distribution, + // send the candidate 1 assignment from peer_c + let assignment = IndirectAssignmentCertV2 { + block_hash: hash, + validator: validator_index, + cert: cert.cert, + }; + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(vec![( + assignment, + (1 as CandidateIndex).into(), + )]); + + send_message_from_peer_v3(overseer, &peer_c, msg).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, _, + )) => { + assert_eq!(assignment.tranche(), 0); + } + ); + expect_reputation_change(overseer, &peer_c, BENEFIT_VALID_MESSAGE_FIRST).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert!(peers.len() >= 2); + assert!(peers.contains(&peer_b)); + assert!(peers.contains(&peer_a)); + assert_eq!(assignments.len(), 1); + } + ); + + // send an approval from peer_b + let approval = IndirectSignedApprovalVoteV2 { + block_hash: hash, + candidate_indices, + validator: validator_index, + signature: signature_for( + &keystore, + &session, + vec![candidate_hash_first, candidate_hash_second], + validator_index, ), - ) - .await; - let signatures = rx_distribution.await.unwrap(); + }; + let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); + send_message_from_peer_v3(overseer, &peer_d, msg).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportApproval( + vote, _, + )) => { + assert_eq!(Into::::into(vote), approval); + } + ); + + expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE_FIRST).await; - assert_eq!(signatures.len(), 1); - for (signing_validator, signature) in signatures { - assert_eq!(validator_index, signing_validator); - assert_eq!(signature.0, hash); - assert_eq!(signature.2, approval.signature); - assert_eq!(signature.1, vec![0, 1]); + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Approvals(approvals) + )) + )) => { + assert!(peers.len() >= 2); + assert!(peers.contains(&peer_b)); + assert!(peers.contains(&peer_a)); + assert_eq!(approvals.len(), 1); + } + ); + for candidate_index in 0..1 { + let (tx_distribution, rx_distribution) = oneshot::channel(); + let mut candidates_requesting_signatures = HashSet::new(); + candidates_requesting_signatures.insert((hash, candidate_index)); + overseer_send( + overseer, + ApprovalDistributionMessage::GetApprovalSignatures( + candidates_requesting_signatures, + tx_distribution, + ), + ) + .await; + let signatures = rx_distribution.await.unwrap(); + + assert_eq!(signatures.len(), 1); + for (signing_validator, signature) in signatures { + assert_eq!(validator_index, signing_validator); + assert_eq!(signature.0, hash); + assert_eq!(signature.2, approval.signature); + assert_eq!(signature.1, vec![0, 1]); + } } - } - virtual_overseer - }); + virtual_overseer + }, + ); } // Tests that votes that cover multiple assignments candidates are correctly processed when unify @@ -1256,305 +1519,335 @@ fn unify_with_peer_multiple_assignments_covered_with_one_approval_vote() { let peer_d = peers.get(4).unwrap().0; let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); + let candidate_hash_first = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xBB)); + let candidate_hash_second = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xCC)); + + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Arc::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + setup_peer_with_view(overseer, &peer_d, view![hash], ValidationVersion::V3).await; + let mut keystore = LocalKeystore::in_memory(); + let session = dummy_session_info_valid(1, &mut keystore, 5); + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![ + (candidate_hash_first, 0.into(), 0.into()), + (candidate_hash_second, 1.into(), 1.into()), + ], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Set up a gossip topology, where a, b, and c, d are topology neighbors to the node. + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - setup_peer_with_view(overseer, &peer_d, view![hash], ValidationVersion::V3).await; - - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 2], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Set up a gossip topology, where a, b, and c, d are topology neighbors to the node. - setup_gossip_topology( - overseer, - make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), - ) - .await; - - // import an assignment related to `hash` locally - let validator_index = ValidatorIndex(2); // peer_c is the originator - let candidate_indices: CandidateBitfield = - vec![0 as CandidateIndex, 1 as CandidateIndex].try_into().unwrap(); + // import an assignment related to `hash` locally + let validator_index = ValidatorIndex(2); // peer_c is the originator + let candidate_indices: CandidateBitfield = + vec![0 as CandidateIndex, 1 as CandidateIndex].try_into().unwrap(); - let core_bitfields = vec![CoreIndex(0)].try_into().unwrap(); - let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfields); + let core_bitfields = vec![CoreIndex(0)].try_into().unwrap(); + let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfields); - // send the candidate 0 assignment from peer_b - let assignment = IndirectAssignmentCertV2 { - block_hash: hash, - validator: validator_index, - cert: cert.cert, - }; - let msg = protocol_v3::ApprovalDistributionMessage::Assignments(vec![( - assignment, - (0 as CandidateIndex).into(), - )]); - send_message_from_peer_v3(overseer, &peer_d, msg).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - _, _, - tx, - )) => { - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); - expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE_FIRST).await; + // send the candidate 0 assignment from peer_b + let assignment = IndirectAssignmentCertV2 { + block_hash: hash, + validator: validator_index, + cert: cert.cert, + }; + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(vec![( + assignment, + (0 as CandidateIndex).into(), + )]); + send_message_from_peer_v3(overseer, &peer_d, msg).await; + provide_session(overseer, session.clone()).await; - let candidate_bitfields = vec![CoreIndex(1)].try_into().unwrap(); - let cert = fake_assignment_cert_v2(hash, validator_index, candidate_bitfields); + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, _, + )) => { + assert_eq!(assignment.tranche(), 0); + } + ); + expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE_FIRST).await; - // send the candidate 1 assignment from peer_c - let assignment = IndirectAssignmentCertV2 { - block_hash: hash, - validator: validator_index, - cert: cert.cert, - }; - let msg = protocol_v3::ApprovalDistributionMessage::Assignments(vec![( - assignment, - (1 as CandidateIndex).into(), - )]); - - send_message_from_peer_v3(overseer, &peer_d, msg).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - _, _, - tx, - )) => { - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); - expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE_FIRST).await; - - // send an approval from peer_b - let approval = IndirectSignedApprovalVoteV2 { - block_hash: hash, - candidate_indices, - validator: validator_index, - signature: dummy_signature(), - }; - let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer_v3(overseer, &peer_d, msg).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportApproval( - vote, - tx, - )) => { - assert_eq!(vote, approval); - tx.send(ApprovalCheckResult::Accepted).unwrap(); - } - ); - - expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE_FIRST).await; - - // setup peers with V2 protocol versions - setup_peer_with_view(overseer, &peer_a, view![hash], ValidationVersion::V3).await; - setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V3).await; - let mut expected_peers_assignments = vec![peer_a, peer_b]; - let mut expected_peers_approvals = vec![peer_a, peer_b]; - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert!(peers.len() == 1); - assert!(expected_peers_assignments.contains(peers.first().unwrap())); - expected_peers_assignments.retain(|peer| peer != peers.first().unwrap()); - assert_eq!(assignments.len(), 2); - } - ); - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Approvals(approvals) - )) - )) => { - assert!(peers.len() == 1); - assert!(expected_peers_approvals.contains(peers.first().unwrap())); - expected_peers_approvals.retain(|peer| peer != peers.first().unwrap()); - assert_eq!(approvals.len(), 1); - } - ); - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert!(peers.len() == 1); - assert!(expected_peers_assignments.contains(peers.first().unwrap())); - expected_peers_assignments.retain(|peer| peer != peers.first().unwrap()); - assert_eq!(assignments.len(), 2); - } - ); - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Approvals(approvals) - )) - )) => { - assert!(peers.len() == 1); - assert!(expected_peers_approvals.contains(peers.first().unwrap())); - expected_peers_approvals.retain(|peer| peer != peers.first().unwrap()); - assert_eq!(approvals.len(), 1); - } - ); + let candidate_bitfields = vec![CoreIndex(1)].try_into().unwrap(); + let cert = fake_assignment_cert_v2(hash, validator_index, candidate_bitfields); - virtual_overseer - }); -} + // send the candidate 1 assignment from peer_c + let assignment = IndirectAssignmentCertV2 { + block_hash: hash, + validator: validator_index, + cert: cert.cert, + }; + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(vec![( + assignment, + (1 as CandidateIndex).into(), + )]); -#[test] -fn import_approval_bad() { - let peer_a = PeerId::random(); - let peer_b = PeerId::random(); - let parent_hash = Hash::repeat_byte(0xFF); - let hash = Hash::repeat_byte(0xAA); + send_message_from_peer_v3(overseer, &peer_d, msg).await; - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // setup peers - setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V1).await; - setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V1).await; - - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; - let cert = fake_assignment_cert(hash, validator_index); - - // send the an approval from peer_b, we don't have an assignment yet - let approval = IndirectSignedApprovalVoteV2 { - block_hash: hash, - candidate_indices: candidate_index.into(), - validator: validator_index, - signature: dummy_signature(), - }; - let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer_v3(overseer, &peer_b, msg).await; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, _, + )) => { + assert_eq!(assignment.tranche(), 0); + } + ); + expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE_FIRST).await; - expect_reputation_change(overseer, &peer_b, COST_UNEXPECTED_MESSAGE).await; + // send an approval from peer_b + let approval = IndirectSignedApprovalVoteV2 { + block_hash: hash, + candidate_indices, + validator: validator_index, + signature: signature_for( + &keystore, + &session, + vec![candidate_hash_first, candidate_hash_second], + validator_index, + ), + }; + let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); + send_message_from_peer_v3(overseer, &peer_d, msg).await; - // now import an assignment from peer_b - let assignments = vec![(cert.clone(), candidate_index)]; - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments); - send_message_from_peer(overseer, &peer_b, msg).await; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportApproval( + vote, _, + )) => { + assert_eq!(Into::::into(vote), approval); + } + ); - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - assignment, - i, - tx, - )) => { - assert_eq!(assignment, cert.into()); - assert_eq!(i, candidate_index.into()); - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); - - expect_reputation_change(overseer, &peer_b, BENEFIT_VALID_MESSAGE_FIRST).await; - - // and try again - let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer_v3(overseer, &peer_b, msg).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportApproval( - vote, - tx, - )) => { - assert_eq!(vote, approval); - tx.send(ApprovalCheckResult::Bad(ApprovalCheckError::UnknownBlock(hash))).unwrap(); - } - ); + expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE_FIRST).await; - expect_reputation_change(overseer, &peer_b, COST_INVALID_MESSAGE).await; - virtual_overseer - }); -} + // setup peers with V2 protocol versions + setup_peer_with_view(overseer, &peer_a, view![hash], ValidationVersion::V3).await; + setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V3).await; + let mut expected_peers_assignments = vec![peer_a, peer_b]; + let mut expected_peers_approvals = vec![peer_a, peer_b]; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert!(peers.len() == 1); + assert!(expected_peers_assignments.contains(peers.first().unwrap())); + expected_peers_assignments.retain(|peer| peer != peers.first().unwrap()); + assert_eq!(assignments.len(), 2); + } + ); -/// make sure we clean up the state on block finalized -#[test] -fn update_our_view() { + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Approvals(approvals) + )) + )) => { + assert!(peers.len() == 1); + assert!(expected_peers_approvals.contains(peers.first().unwrap())); + expected_peers_approvals.retain(|peer| peer != peers.first().unwrap()); + assert_eq!(approvals.len(), 1); + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert!(peers.len() == 1); + assert!(expected_peers_assignments.contains(peers.first().unwrap())); + expected_peers_assignments.retain(|peer| peer != peers.first().unwrap()); + assert_eq!(assignments.len(), 2); + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Approvals(approvals) + )) + )) => { + assert!(peers.len() == 1); + assert!(expected_peers_approvals.contains(peers.first().unwrap())); + expected_peers_approvals.retain(|peer| peer != peers.first().unwrap()); + assert_eq!(approvals.len(), 1); + } + ); + + virtual_overseer + }, + ); +} + +#[test] +fn import_approval_bad() { + let peer_a = PeerId::random(); + let peer_b = PeerId::random(); + let parent_hash = Hash::repeat_byte(0xFF); + let hash = Hash::repeat_byte(0xAA); + let candidate_hash = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xBB)); + + let diff_candidate_hash = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xCC)); + + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Arc::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup peers + setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V1).await; + setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V1).await; + let mut keystore = LocalKeystore::in_memory(); + let session = dummy_session_info_valid(1, &mut keystore, 1); + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![(candidate_hash, 0.into(), 0.into()); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; + let cert = fake_assignment_cert(hash, validator_index); + + // Sign a different candidate hash. + let payload = + ApprovalVoteMultipleCandidates(&vec![diff_candidate_hash]).signing_payload(1); + let sign_key = session.validators.get(ValidatorIndex(0)).unwrap().clone(); + let signature = keystore + .sr25519_sign(ValidatorId::ID, &sign_key.into(), &payload[..]) + .unwrap() + .unwrap(); + + // send the an approval from peer_b, we don't have an assignment yet + let approval = IndirectSignedApprovalVoteV2 { + block_hash: hash, + candidate_indices: candidate_index.into(), + validator: validator_index, + signature: signature.into(), + }; + let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); + send_message_from_peer_v3(overseer, &peer_b, msg).await; + + expect_reputation_change(overseer, &peer_b, COST_UNEXPECTED_MESSAGE).await; + + // now import an assignment from peer_b + let assignments = vec![(cert.clone(), candidate_index)]; + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments); + send_message_from_peer(overseer, &peer_b, msg).await; + provide_session(overseer, session.clone()).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, + _, + )) => { + assert_eq!(assignment.assignment(), &cert.into()); + assert_eq!(assignment.candidate_indices(), &candidate_index.into()); + assert_eq!(assignment.tranche(), 0); + } + ); + + expect_reputation_change(overseer, &peer_b, BENEFIT_VALID_MESSAGE_FIRST).await; + + // and try again + let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); + send_message_from_peer_v3(overseer, &peer_b, msg).await; + + expect_reputation_change(overseer, &peer_b, COST_INVALID_MESSAGE).await; + virtual_overseer + }, + ); +} + +/// make sure we clean up the state on block finalized +#[test] +fn update_our_view() { let parent_hash = Hash::repeat_byte(0xFF); let hash_a = Hash::repeat_byte(0xAA); let hash_b = Hash::repeat_byte(0xBB); let hash_c = Hash::repeat_byte(0xCC); - let state = test_harness(State::default(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // new block `hash_a` with 1 candidates - let meta_a = BlockApprovalMeta { - hash: hash_a, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let meta_b = BlockApprovalMeta { - hash: hash_b, - parent_hash: hash_a, - number: 2, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let meta_c = BlockApprovalMeta { - hash: hash_c, - parent_hash: hash_b, - number: 3, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta_a, meta_b, meta_c]); - overseer_send(overseer, msg).await; - virtual_overseer - }); + let state = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Arc::new(SystemClock {}), + State::default(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // new block `hash_a` with 1 candidates + let meta_a = BlockApprovalMeta { + hash: hash_a, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let meta_b = BlockApprovalMeta { + hash: hash_b, + parent_hash: hash_a, + number: 2, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let meta_c = BlockApprovalMeta { + hash: hash_c, + parent_hash: hash_b, + number: 3, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta_a, meta_b, meta_c]); + overseer_send(overseer, msg).await; + virtual_overseer + }, + ); assert!(state.blocks_by_number.get(&1).is_some()); assert!(state.blocks_by_number.get(&2).is_some()); @@ -1563,12 +1856,17 @@ fn update_our_view() { assert!(state.blocks.get(&hash_b).is_some()); assert!(state.blocks.get(&hash_c).is_some()); - let state = test_harness(state, |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // finalize a block - overseer_signal_block_finalized(overseer, 2).await; - virtual_overseer - }); + let state = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Arc::new(SystemClock {}), + state, + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // finalize a block + overseer_signal_block_finalized(overseer, 2).await; + virtual_overseer + }, + ); assert!(state.blocks_by_number.get(&1).is_none()); assert!(state.blocks_by_number.get(&2).is_none()); @@ -1577,12 +1875,17 @@ fn update_our_view() { assert!(state.blocks.get(&hash_b).is_none()); assert!(state.blocks.get(&hash_c).is_some()); - let state = test_harness(state, |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // finalize a very high block - overseer_signal_block_finalized(overseer, 4_000_000_000).await; - virtual_overseer - }); + let state = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Arc::new(SystemClock {}), + state, + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // finalize a very high block + overseer_signal_block_finalized(overseer, 4_000_000_000).await; + virtual_overseer + }, + ); assert!(state.blocks_by_number.get(&3).is_none()); assert!(state.blocks.get(&hash_c).is_none()); @@ -1600,81 +1903,89 @@ fn update_peer_view() { let peer_a = peers.first().unwrap().0; let peer = &peer_a; - let state = test_harness(State::default(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // new block `hash_a` with 1 candidates - let meta_a = BlockApprovalMeta { - hash: hash_a, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let meta_b = BlockApprovalMeta { - hash: hash_b, - parent_hash: hash_a, - number: 2, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let meta_c = BlockApprovalMeta { - hash: hash_c, - parent_hash: hash_b, - number: 3, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; + let state = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Arc::new(SystemClock {}), + State::default(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // new block `hash_a` with 1 candidates + let meta_a = BlockApprovalMeta { + hash: hash_a, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let meta_b = BlockApprovalMeta { + hash: hash_b, + parent_hash: hash_a, + number: 2, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let meta_c = BlockApprovalMeta { + hash: hash_c, + parent_hash: hash_b, + number: 3, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta_a, meta_b, meta_c]); + overseer_send(overseer, msg).await; + + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Setup a topology where peer_a is neighbor to current node. + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0], &[2], 1), + ) + .await; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta_a, meta_b, meta_c]); - overseer_send(overseer, msg).await; - - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Setup a topology where peer_a is neighbor to current node. - setup_gossip_topology( - overseer, - make_gossip_topology(1, &peers_with_optional_peer_id, &[0], &[2], 1), - ) - .await; + let cert_a = fake_assignment_cert(hash_a, ValidatorIndex(0)); + let cert_b = fake_assignment_cert(hash_b, ValidatorIndex(0)); - let cert_a = fake_assignment_cert(hash_a, ValidatorIndex(0)); - let cert_b = fake_assignment_cert(hash_b, ValidatorIndex(0)); + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment(cert_a.into(), 0.into()), + ) + .await; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment(cert_a.into(), 0.into()), - ) - .await; + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment(cert_b.into(), 0.into()), + ) + .await; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment(cert_b.into(), 0.into()), - ) - .await; + // connect a peer + setup_peer_with_view(overseer, peer, view![hash_a], ValidationVersion::V1).await; - // connect a peer - setup_peer_with_view(overseer, peer, view![hash_a], ValidationVersion::V1).await; - - // we should send relevant assignments to the peer - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers.len(), 1); - assert_eq!(assignments.len(), 1); - } - ); - virtual_overseer - }); + // we should send relevant assignments to the peer + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 1); + } + ); + virtual_overseer + }, + ); assert_eq!(state.peer_views.get(peer).map(|v| v.view.finalized_number), Some(0)); assert_eq!( @@ -1691,42 +2002,49 @@ fn update_peer_view() { 1, ); - let state = test_harness(state, |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // update peer's view - overseer_send( - overseer, - ApprovalDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerViewChange( - *peer, - View::new(vec![hash_b, hash_c, hash_d], 2), - )), - ) - .await; + let state = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Arc::new(SystemClock {}), + state, + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // update peer's view + overseer_send( + overseer, + ApprovalDistributionMessage::NetworkBridgeUpdate( + NetworkBridgeEvent::PeerViewChange( + *peer, + View::new(vec![hash_b, hash_c, hash_d], 2), + ), + ), + ) + .await; - let cert_c = fake_assignment_cert(hash_c, ValidatorIndex(0)); + let cert_c = fake_assignment_cert(hash_c, ValidatorIndex(0)); - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment(cert_c.clone().into(), 0.into()), - ) - .await; + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment(cert_c.clone().into(), 0.into()), + ) + .await; - // we should send relevant assignments to the peer - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers.len(), 1); - assert_eq!(assignments.len(), 1); - assert_eq!(assignments[0].0, cert_c); - } - ); - virtual_overseer - }); + // we should send relevant assignments to the peer + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 1); + assert_eq!(assignments[0].0, cert_c); + } + ); + virtual_overseer + }, + ); assert_eq!(state.peer_views.get(peer).map(|v| v.view.finalized_number), Some(2)); assert_eq!( @@ -1744,19 +2062,26 @@ fn update_peer_view() { ); let finalized_number = 4_000_000_000; - let state = test_harness(state, |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // update peer's view - overseer_send( - overseer, - ApprovalDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerViewChange( - *peer, - View::with_finalized(finalized_number), - )), - ) - .await; - virtual_overseer - }); + let state = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Arc::new(SystemClock {}), + state, + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // update peer's view + overseer_send( + overseer, + ApprovalDistributionMessage::NetworkBridgeUpdate( + NetworkBridgeEvent::PeerViewChange( + *peer, + View::with_finalized(finalized_number), + ), + ), + ) + .await; + virtual_overseer + }, + ); assert_eq!(state.peer_views.get(peer).map(|v| v.view.finalized_number), Some(finalized_number)); assert!(state.blocks.get(&hash_c).unwrap().known_by.get(peer).is_none()); @@ -1779,164 +2104,176 @@ fn update_peer_authority_id() { // Y neighbour, we simulate that PeerId is not known in the beginning. let neighbour_y = peers.get(neighbour_y_index).unwrap().0; - let _state = test_harness(State::default(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // new block `hash_a` with 1 candidates - let meta_a = BlockApprovalMeta { - hash: hash_a, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let meta_b = BlockApprovalMeta { - hash: hash_b, - parent_hash: hash_a, - number: 2, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let meta_c = BlockApprovalMeta { - hash: hash_c, - parent_hash: hash_b, - number: 3, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; + let _state = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Arc::new(SystemClock {}), + State::default(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // new block `hash_a` with 1 candidates + let meta_a = BlockApprovalMeta { + hash: hash_a, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let meta_b = BlockApprovalMeta { + hash: hash_b, + parent_hash: hash_a, + number: 2, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let meta_c = BlockApprovalMeta { + hash: hash_c, + parent_hash: hash_b, + number: 3, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta_a, meta_b, meta_c]); + overseer_send(overseer, msg).await; + + let peers_with_optional_peer_id = peers + .iter() + .enumerate() + .map(|(index, (peer_id, authority))| { + (if index == 0 { None } else { Some(*peer_id) }, authority.clone()) + }) + .collect_vec(); + + // Setup a topology where peer_a is neighbor to current node. + setup_gossip_topology( + overseer, + make_gossip_topology( + 1, + &peers_with_optional_peer_id, + &[neighbour_x_index], + &[neighbour_y_index], + local_index, + ), + ) + .await; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta_a, meta_b, meta_c]); - overseer_send(overseer, msg).await; + let cert_a = fake_assignment_cert(hash_a, ValidatorIndex(local_index as u32)); + let cert_b = fake_assignment_cert(hash_b, ValidatorIndex(local_index as u32)); - let peers_with_optional_peer_id = peers - .iter() - .enumerate() - .map(|(index, (peer_id, authority))| { - (if index == 0 { None } else { Some(*peer_id) }, authority.clone()) - }) - .collect_vec(); - - // Setup a topology where peer_a is neighbor to current node. - setup_gossip_topology( - overseer, - make_gossip_topology( - 1, - &peers_with_optional_peer_id, - &[neighbour_x_index], - &[neighbour_y_index], - local_index, - ), - ) - .await; + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment(cert_a.into(), 0.into()), + ) + .await; - let cert_a = fake_assignment_cert(hash_a, ValidatorIndex(local_index as u32)); - let cert_b = fake_assignment_cert(hash_b, ValidatorIndex(local_index as u32)); + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment(cert_b.into(), 0.into()), + ) + .await; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment(cert_a.into(), 0.into()), - ) - .await; + // connect a peer + setup_peer_with_view(overseer, &neighbour_x, view![hash_a], ValidationVersion::V1) + .await; + setup_peer_with_view(overseer, &neighbour_y, view![hash_a], ValidationVersion::V1) + .await; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment(cert_b.into(), 0.into()), - ) - .await; + setup_peer_with_view(overseer, &neighbour_x, view![hash_b], ValidationVersion::V1) + .await; + setup_peer_with_view(overseer, &neighbour_y, view![hash_b], ValidationVersion::V1) + .await; - // connect a peer - setup_peer_with_view(overseer, &neighbour_x, view![hash_a], ValidationVersion::V1).await; - setup_peer_with_view(overseer, &neighbour_y, view![hash_a], ValidationVersion::V1).await; - - setup_peer_with_view(overseer, &neighbour_x, view![hash_b], ValidationVersion::V1).await; - setup_peer_with_view(overseer, &neighbour_y, view![hash_b], ValidationVersion::V1).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers.len(), 1); - assert_eq!(assignments.len(), 1); - assert_eq!(peers.get(0), Some(&neighbour_y)); - } - ); - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers.len(), 1); - assert_eq!(assignments.len(), 1); - assert_eq!(peers.get(0), Some(&neighbour_y)); - } - ); - - overseer_send( - overseer, - ApprovalDistributionMessage::NetworkBridgeUpdate( - NetworkBridgeEvent::UpdatedAuthorityIds( - peers[neighbour_x_index].0, - [peers[neighbour_x_index].1.clone()].into_iter().collect(), + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 1); + assert_eq!(peers.get(0), Some(&neighbour_y)); + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 1); + assert_eq!(peers.get(0), Some(&neighbour_y)); + } + ); + + overseer_send( + overseer, + ApprovalDistributionMessage::NetworkBridgeUpdate( + NetworkBridgeEvent::UpdatedAuthorityIds( + peers[neighbour_x_index].0, + [peers[neighbour_x_index].1.clone()].into_iter().collect(), + ), ), - ), - ) - .await; + ) + .await; - // we should send relevant assignments to the peer, after we found it's peer id. - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - gum::info!(target: LOG_TARGET, ?peers, ?assignments); - assert_eq!(peers.len(), 1); - assert_eq!(assignments.len(), 2); - assert_eq!(assignments.get(0).unwrap().0.block_hash, hash_a); - assert_eq!(assignments.get(1).unwrap().0.block_hash, hash_b); - assert_eq!(peers.get(0), Some(&neighbour_x)); - } - ); - - overseer_send( - overseer, - ApprovalDistributionMessage::NetworkBridgeUpdate( - NetworkBridgeEvent::UpdatedAuthorityIds( - peers[neighbour_y_index].0, - [peers[neighbour_y_index].1.clone()].into_iter().collect(), + // we should send relevant assignments to the peer, after we found it's peer id. + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + gum::info!(target: LOG_TARGET, ?peers, ?assignments); + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 2); + assert_eq!(assignments.get(0).unwrap().0.block_hash, hash_a); + assert_eq!(assignments.get(1).unwrap().0.block_hash, hash_b); + assert_eq!(peers.get(0), Some(&neighbour_x)); + } + ); + + overseer_send( + overseer, + ApprovalDistributionMessage::NetworkBridgeUpdate( + NetworkBridgeEvent::UpdatedAuthorityIds( + peers[neighbour_y_index].0, + [peers[neighbour_y_index].1.clone()].into_iter().collect(), + ), ), - ), - ) - .await; - overseer_send( - overseer, - ApprovalDistributionMessage::NetworkBridgeUpdate( - NetworkBridgeEvent::UpdatedAuthorityIds( - peers[neighbour_x_index].0, - [peers[neighbour_x_index].1.clone()].into_iter().collect(), + ) + .await; + overseer_send( + overseer, + ApprovalDistributionMessage::NetworkBridgeUpdate( + NetworkBridgeEvent::UpdatedAuthorityIds( + peers[neighbour_x_index].0, + [peers[neighbour_x_index].1.clone()].into_iter().collect(), + ), ), - ), - ) - .await; - assert!( - overseer.recv().timeout(TIMEOUT).await.is_none(), - "no message should be sent peers are already known" - ); + ) + .await; + assert!( + overseer.recv().timeout(TIMEOUT).await.is_none(), + "no message should be sent peers are already known" + ); - virtual_overseer - }); + virtual_overseer + }, + ); } /// E.g. if someone copies the keys... @@ -1945,89 +2282,105 @@ fn import_remotely_then_locally() { let peer_a = PeerId::random(); let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); + let candidate_hash = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xBB)); let peer = &peer_a; - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // setup the peer - setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; - - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - // import the assignment remotely first - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; - let cert = fake_assignment_cert(hash, validator_index); - let assignments = vec![(cert.clone(), candidate_index)]; - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); - send_message_from_peer(overseer, peer, msg).await; - - // send an `Accept` message from the Approval Voting subsystem - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - assignment, - i, - tx, - )) => { - assert_eq!(assignment, cert.clone().into()); - assert_eq!(i, candidate_index.into()); - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Arc::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup the peer + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; + let mut keystore = LocalKeystore::in_memory(); + + let session = dummy_session_info_valid(1, &mut keystore, 1); + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![(candidate_hash, 0.into(), 0.into()); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + let payload = ApprovalVoteMultipleCandidates(&vec![candidate_hash]).signing_payload(1); + let sign_key = session.validators.get(ValidatorIndex(0)).unwrap().clone(); + let signature = keystore + .sr25519_sign(ValidatorId::ID, &sign_key.into(), &payload[..]) + .unwrap() + .unwrap(); + + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // import the assignment remotely first + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; + let cert = fake_assignment_cert(hash, validator_index); + let assignments = vec![(cert.clone(), candidate_index)]; + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer(overseer, peer, msg).await; + provide_session(overseer, session.clone()).await; - expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE_FIRST).await; + // send an `Accept` message from the Approval Voting subsystem + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, + _, + )) => { + assert_eq!(assignment.assignment(), &cert.clone().into()); + assert_eq!(assignment.candidate_indices(), &candidate_index.into()); + assert_eq!(assignment.tranche(), 0); + } + ); - // import the same assignment locally - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment( - cert.clone().into(), - candidate_index.into(), - ), - ) - .await; + expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE_FIRST).await; + + // import the same assignment locally + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_index.into(), + ), + ) + .await; - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - // send the approval remotely - let approval = IndirectSignedApprovalVoteV2 { - block_hash: hash, - candidate_indices: candidate_index.into(), - validator: validator_index, - signature: dummy_signature(), - }; - let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer_v3(overseer, peer, msg).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportApproval( - vote, - tx, - )) => { - assert_eq!(vote, approval); - tx.send(ApprovalCheckResult::Accepted).unwrap(); - } - ); - expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE_FIRST).await; + // send the approval remotely + let approval = IndirectSignedApprovalVoteV2 { + block_hash: hash, + candidate_indices: candidate_index.into(), + validator: validator_index, + signature: signature.into(), + }; + let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); + send_message_from_peer_v3(overseer, peer, msg).await; - // import the same approval locally - overseer_send(overseer, ApprovalDistributionMessage::DistributeApproval(approval)).await; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportApproval( + vote, _, + )) => { + assert_eq!(Into::::into(vote), approval); + } + ); + expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE_FIRST).await; - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); + // import the same approval locally + overseer_send(overseer, ApprovalDistributionMessage::DistributeApproval(approval)) + .await; + + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); } #[test] @@ -2038,94 +2391,100 @@ fn sends_assignments_even_when_state_is_approved() { let hash = Hash::repeat_byte(0xAA); let peer = &peer_a; - let _ = test_harness(State::default(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Arc::new(SystemClock {}), + State::default(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Setup a topology where peer_a is neighbor to current node. + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0], &[2], 1), + ) + .await; - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Setup a topology where peer_a is neighbor to current node. - setup_gossip_topology( - overseer, - make_gossip_topology(1, &peers_with_optional_peer_id, &[0], &[2], 1), - ) - .await; + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; + // import an assignment and approval locally. + let cert = fake_assignment_cert(hash, validator_index); + let approval = IndirectSignedApprovalVote { + block_hash: hash, + candidate_index, + validator: validator_index, + signature: dummy_signature(), + }; - // import an assignment and approval locally. - let cert = fake_assignment_cert(hash, validator_index); - let approval = IndirectSignedApprovalVote { - block_hash: hash, - candidate_index, - validator: validator_index, - signature: dummy_signature(), - }; + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_index.into(), + ), + ) + .await; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment( - cert.clone().into(), - candidate_index.into(), - ), - ) - .await; + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), + ) + .await; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), - ) - .await; + // connect the peer. + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; - // connect the peer. - setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; - - let assignments = vec![(cert.clone(), candidate_index)]; - let approvals = vec![approval.clone()]; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) - )) - )) => { - assert_eq!(peers, vec![*peer]); - assert_eq!(sent_assignments, assignments); - } - ); - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) - )) - )) => { - assert_eq!(peers, vec![*peer]); - assert_eq!(sent_approvals, approvals); - } - ); + let assignments = vec![(cert.clone(), candidate_index)]; + let approvals = vec![approval.clone()]; - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + )) + )) => { + assert_eq!(peers, vec![*peer]); + assert_eq!(sent_assignments, assignments); + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) + )) + )) => { + assert_eq!(peers, vec![*peer]); + assert_eq!(sent_approvals, approvals); + } + ); + + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); } /// Same as `sends_assignments_even_when_state_is_approved_v2` but with `VRFModuloCompact` @@ -2138,393 +2497,589 @@ fn sends_assignments_even_when_state_is_approved_v2() { let hash = Hash::repeat_byte(0xAA); let peer = &peer_a; - let _ = test_harness(State::default(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Arc::new(SystemClock {}), + State::default(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 4], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Setup a topology where peer_a is neighbor to current node. + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0], &[2], 1), + ) + .await; - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 4], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Setup a topology where peer_a is neighbor to current node. - setup_gossip_topology( - overseer, - make_gossip_topology(1, &peers_with_optional_peer_id, &[0], &[2], 1), - ) - .await; + let validator_index = ValidatorIndex(0); + let cores = vec![0, 1, 2, 3]; + let candidate_bitfield: CandidateBitfield = cores.clone().try_into().unwrap(); + + let core_bitfield: CoreBitfield = cores + .iter() + .map(|index| CoreIndex(*index)) + .collect::>() + .try_into() + .unwrap(); + + let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfield.clone()); + + // Assumes candidate index == core index. + let approvals = cores + .iter() + .map(|core| IndirectSignedApprovalVoteV2 { + block_hash: hash, + candidate_indices: (*core).into(), + validator: validator_index, + signature: dummy_signature(), + }) + .collect::>(); - let validator_index = ValidatorIndex(0); - let cores = vec![0, 1, 2, 3]; - let candidate_bitfield: CandidateBitfield = cores.clone().try_into().unwrap(); + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_bitfield.clone(), + ), + ) + .await; + + for approval in &approvals { + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeApproval(approval.clone()), + ) + .await; + } - let core_bitfield: CoreBitfield = cores - .iter() - .map(|index| CoreIndex(*index)) - .collect::>() - .try_into() - .unwrap(); + // connect the peer. + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V3).await; - let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfield.clone()); + let assignments = vec![(cert.clone(), candidate_bitfield.clone())]; - // Assumes candidate index == core index. - let approvals = cores - .iter() - .map(|core| IndirectSignedApprovalVoteV2 { + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Assignments(sent_assignments) + )) + )) => { + assert_eq!(peers, vec![*peer]); + assert_eq!(sent_assignments, assignments); + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Approvals(sent_approvals) + )) + )) => { + // Construct a hashmaps of approvals for comparison. Approval distribution reorders messages because they are kept in a + // hashmap as well. + let sent_approvals = sent_approvals.into_iter().map(|approval| (approval.candidate_indices.clone(), approval)).collect::>(); + let approvals = approvals.into_iter().map(|approval| (approval.candidate_indices.clone(), approval)).collect::>(); + + assert_eq!(peers, vec![*peer]); + assert_eq!(sent_approvals, approvals); + } + ); + + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); +} + +/// +/// +/// 1. Receive remote peer view update with an unknown head +/// 2. Receive assignments for that unknown head +/// 3. Update our view and import the new block +/// 4. Expect that no reputation with `COST_UNEXPECTED_MESSAGE` is applied +#[test] +fn race_condition_in_local_vs_remote_view_update() { + let parent_hash = Hash::repeat_byte(0xFF); + let peer_a = PeerId::random(); + let hash_b = Hash::repeat_byte(0xBB); + + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Arc::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + let peer = &peer_a; + + // Test a small number of candidates + let candidates_count = 1; + let meta = BlockApprovalMeta { + hash: hash_b, + parent_hash, + number: 2, + candidates: vec![Default::default(); candidates_count], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + // This will send a peer view that is ahead of our view + setup_peer_with_view(overseer, peer, view![hash_b], ValidationVersion::V1).await; + + // Send our view update to include a new head + overseer_send( + overseer, + ApprovalDistributionMessage::NetworkBridgeUpdate( + NetworkBridgeEvent::OurViewChange(our_view![hash_b]), + ), + ) + .await; + + // send assignments related to `hash_b` but they will come to the MessagesPending + let assignments: Vec<_> = (0..candidates_count) + .map(|candidate_index| { + let validator_index = ValidatorIndex(candidate_index as u32); + let cert = fake_assignment_cert(hash_b, validator_index); + (cert, candidate_index as u32) + }) + .collect(); + + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer(overseer, peer, msg.clone()).await; + + // This will handle pending messages being processed + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; + + for i in 0..candidates_count { + // Previously, this has caused out-of-view assignments/approvals + //expect_reputation_change(overseer, peer, COST_UNEXPECTED_MESSAGE).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, + _, + )) => { + assert_eq!(assignment.assignment(), &assignments[i].0.clone().into()); + assert_eq!(assignment.candidate_indices(), &assignments[i].1.into()); + assert_eq!(assignment.tranche(), 0); + } + ); + + // Since we have a valid statement pending, this should always occur + expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE_FIRST).await; + } + virtual_overseer + }, + ); +} + +// Tests that local messages propagate to both dimensions. +#[test] +fn propagates_locally_generated_assignment_to_both_dimensions() { + let parent_hash = Hash::repeat_byte(0xFF); + let hash = Hash::repeat_byte(0xAA); + + let peers = make_peers_and_authority_ids(100); + + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Arc::new(SystemClock {}), + State::default(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + + // Connect all peers. + for (peer, _) in &peers { + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; + } + + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + + // Set up a gossip topology. + setup_gossip_topology( + overseer, + make_gossip_topology( + 1, + &peers_with_optional_peer_id, + &[0, 10, 20, 30, 40, 60, 70, 80], + &[50, 51, 52, 53, 54, 55, 56, 57], + 1, + ), + ) + .await; + + let expected_indices = [ + // Both dimensions in the gossip topology + 0, 10, 20, 30, 40, 60, 70, 80, 50, 51, 52, 53, 54, 55, 56, 57, + ]; + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; + + // import an assignment and approval locally. + let cert = fake_assignment_cert(hash, validator_index); + let approval = IndirectSignedApprovalVote { block_hash: hash, - candidate_indices: (*core).into(), + candidate_index, validator: validator_index, - signature: dummy_signature(), - }) - .collect::>(); - - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment( - cert.clone().into(), - candidate_bitfield.clone(), - ), - ) - .await; + signature: dummy_signature(), + }; - for approval in &approvals { overseer_send( overseer, - ApprovalDistributionMessage::DistributeApproval(approval.clone()), + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_index.into(), + ), ) .await; - } - - // connect the peer. - setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V3).await; - - let assignments = vec![(cert.clone(), candidate_bitfield.clone())]; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Assignments(sent_assignments) - )) - )) => { - assert_eq!(peers, vec![*peer]); - assert_eq!(sent_assignments, assignments); - } - ); - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( - protocol_v3::ApprovalDistributionMessage::Approvals(sent_approvals) - )) - )) => { - // Construct a hashmaps of approvals for comparison. Approval distribution reorders messages because they are kept in a - // hashmap as well. - let sent_approvals = sent_approvals.into_iter().map(|approval| (approval.candidate_indices.clone(), approval)).collect::>(); - let approvals = approvals.into_iter().map(|approval| (approval.candidate_indices.clone(), approval)).collect::>(); - - assert_eq!(peers, vec![*peer]); - assert_eq!(sent_approvals, approvals); - } - ); - - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); -} - -/// -/// -/// 1. Receive remote peer view update with an unknown head -/// 2. Receive assignments for that unknown head -/// 3. Update our view and import the new block -/// 4. Expect that no reputation with `COST_UNEXPECTED_MESSAGE` is applied -#[test] -fn race_condition_in_local_vs_remote_view_update() { - let parent_hash = Hash::repeat_byte(0xFF); - let peer_a = PeerId::random(); - let hash_b = Hash::repeat_byte(0xBB); - - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - let peer = &peer_a; - - // Test a small number of candidates - let candidates_count = 1; - let meta = BlockApprovalMeta { - hash: hash_b, - parent_hash, - number: 2, - candidates: vec![Default::default(); candidates_count], - slot: 1.into(), - session: 1, - }; - - // This will send a peer view that is ahead of our view - setup_peer_with_view(overseer, peer, view![hash_b], ValidationVersion::V1).await; - - // Send our view update to include a new head - overseer_send( - overseer, - ApprovalDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::OurViewChange( - our_view![hash_b], - )), - ) - .await; - - // send assignments related to `hash_b` but they will come to the MessagesPending - let assignments: Vec<_> = (0..candidates_count) - .map(|candidate_index| { - let validator_index = ValidatorIndex(candidate_index as u32); - let cert = fake_assignment_cert(hash_b, validator_index); - (cert, candidate_index as u32) - }) - .collect(); - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); - send_message_from_peer(overseer, peer, msg.clone()).await; + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), + ) + .await; - // This will handle pending messages being processed - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; + let assignments = vec![(cert.clone(), candidate_index)]; + let approvals = vec![approval.clone()]; - for i in 0..candidates_count { - // Previously, this has caused out-of-view assignments/approvals - //expect_reputation_change(overseer, peer, COST_UNEXPECTED_MESSAGE).await; + let mut assignment_sent_peers = assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + )) + )) => { + assert_eq!(sent_peers.len(), expected_indices.len() + 4); + for &i in &expected_indices { + assert!( + sent_peers.contains(&peers[i].0), + "Message not sent to expected peer {}", + i, + ); + } + assert_eq!(sent_assignments, assignments); + sent_peers + } + ); assert_matches!( overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - assignment, - claimed_candidate_index, - tx, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + mut sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) + )) )) => { - assert_eq!(assignment, assignments[i].0.clone().into()); - assert_eq!(claimed_candidate_index, assignments[i].1.into()); - tx.send(AssignmentCheckResult::Accepted).unwrap(); + // Random sampling is reused from the assignment. + sent_peers.sort(); + assignment_sent_peers.sort(); + assert_eq!(sent_peers, assignment_sent_peers); + assert_eq!(sent_approvals, approvals); } ); - // Since we have a valid statement pending, this should always occur - expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE_FIRST).await; - } - virtual_overseer - }); + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); } -// Tests that local messages propagate to both dimensions. +// Tests that messages propagate to the unshared dimension. #[test] -fn propagates_locally_generated_assignment_to_both_dimensions() { +fn propagates_assignments_along_unshared_dimension() { let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); let peers = make_peers_and_authority_ids(100); - let _ = test_harness(State::default(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Arc::new(SystemClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; - // Connect all peers. - for (peer, _) in &peers { - setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; - } + // Connect all peers. + for (peer, _) in &peers { + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; + } - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - - // Set up a gossip topology. - setup_gossip_topology( - overseer, - make_gossip_topology( - 1, - &peers_with_optional_peer_id, - &[0, 10, 20, 30, 40, 60, 70, 80], - &[50, 51, 52, 53, 54, 55, 56, 57], - 1, - ), - ) - .await; + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); - let expected_indices = [ - // Both dimensions in the gossip topology - 0, 10, 20, 30, 40, 60, 70, 80, 50, 51, 52, 53, 54, 55, 56, 57, - ]; - - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; + // Set up a gossip topology. + setup_gossip_topology( + overseer, + make_gossip_topology( + 1, + &peers_with_optional_peer_id, + &[0, 10, 20, 30], + &[50, 51, 52, 53], + 1, + ), + ) + .await; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // Test messages from X direction go to Y peers + { + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; + + // import an assignment and approval locally. + let cert = fake_assignment_cert(hash, validator_index); + let assignments = vec![(cert.clone(), candidate_index)]; + + let msg = + protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + + // Issuer of the message is important, not the peer we receive from. + // 99 deliberately chosen because it's not in X or Y. + send_message_from_peer(overseer, &peers[99].0, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, _, + )) => { + assert_eq!(assignment.tranche(), 0); + } + ); + expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await; - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; + let expected_y = [50, 51, 52, 53]; - // import an assignment and approval locally. - let cert = fake_assignment_cert(hash, validator_index); - let approval = IndirectSignedApprovalVote { - block_hash: hash, - candidate_index, - validator: validator_index, - signature: dummy_signature(), - }; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + )) + )) => { + assert_eq!(sent_peers.len(), expected_y.len() + 4); + for &i in &expected_y { + assert!( + sent_peers.contains(&peers[i].0), + "Message not sent to expected peer {}", + i, + ); + } + assert_eq!(sent_assignments, assignments); + } + ); + }; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment( - cert.clone().into(), - candidate_index.into(), - ), - ) - .await; + // Test messages from X direction go to Y peers + { + let validator_index = ValidatorIndex(50); + let candidate_index = 0u32; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), - ) - .await; + // import an assignment and approval locally. + let cert = fake_assignment_cert(hash, validator_index); + let assignments = vec![(cert.clone(), candidate_index)]; - let assignments = vec![(cert.clone(), candidate_index)]; - let approvals = vec![approval.clone()]; - - let mut assignment_sent_peers = assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) - )) - )) => { - assert_eq!(sent_peers.len(), expected_indices.len() + 4); - for &i in &expected_indices { - assert!( - sent_peers.contains(&peers[i].0), - "Message not sent to expected peer {}", - i, - ); - } - assert_eq!(sent_assignments, assignments); - sent_peers - } - ); - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - mut sent_peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) - )) - )) => { - // Random sampling is reused from the assignment. - sent_peers.sort(); - assignment_sent_peers.sort(); - assert_eq!(sent_peers, assignment_sent_peers); - assert_eq!(sent_approvals, approvals); - } - ); + let msg = + protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); + // Issuer of the message is important, not the peer we receive from. + // 99 deliberately chosen because it's not in X or Y. + send_message_from_peer(overseer, &peers[99].0, msg).await; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, _, + )) => { + assert_eq!(assignment.tranche(), 0); + } + ); + expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await; + + let expected_x = [0, 10, 20, 30]; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + )) + )) => { + assert_eq!(sent_peers.len(), expected_x.len() + 4); + for &i in &expected_x { + assert!( + sent_peers.contains(&peers[i].0), + "Message not sent to expected peer {}", + i, + ); + } + assert_eq!(sent_assignments, assignments); + } + ); + }; + + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); } -// Tests that messages propagate to the unshared dimension. +// tests that messages are propagated to necessary peers after they connect #[test] -fn propagates_assignments_along_unshared_dimension() { +fn propagates_to_required_after_connect() { let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); let peers = make_peers_and_authority_ids(100); - let _ = test_harness(state_without_reputation_delay(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - - // Connect all peers. - for (peer, _) in &peers { - setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; - } + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Arc::new(SystemClock {}), + State::default(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - - // Set up a gossip topology. - setup_gossip_topology( - overseer, - make_gossip_topology( - 1, - &peers_with_optional_peer_id, - &[0, 10, 20, 30], - &[50, 51, 52, 53], - 1, - ), - ) - .await; + let omitted = [0, 10, 50, 51]; - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; + // Connect all peers except omitted. + for (i, (peer, _)) in peers.iter().enumerate() { + if !omitted.contains(&i) { + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; + } + } + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Set up a gossip topology. + setup_gossip_topology( + overseer, + make_gossip_topology( + 1, + &peers_with_optional_peer_id, + &[0, 10, 20, 30, 40, 60, 70, 80], + &[50, 51, 52, 53, 54, 55, 56, 57], + 1, + ), + ) + .await; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; + let expected_indices = [ + // Both dimensions in the gossip topology, minus omitted. + 20, 30, 40, 60, 70, 80, 52, 53, 54, 55, 56, 57, + ]; + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; - // Test messages from X direction go to Y peers - { let validator_index = ValidatorIndex(0); let candidate_index = 0u32; // import an assignment and approval locally. let cert = fake_assignment_cert(hash, validator_index); - let assignments = vec![(cert.clone(), candidate_index)]; + let approval = IndirectSignedApprovalVote { + block_hash: hash, + candidate_index, + validator: validator_index, + signature: dummy_signature(), + }; - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_index.into(), + ), + ) + .await; - // Issuer of the message is important, not the peer we receive from. - // 99 deliberately chosen because it's not in X or Y. - send_message_from_peer(overseer, &peers[99].0, msg).await; - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - _, - _, - tx, - )) => { - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); - expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await; + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), + ) + .await; - let expected_y = [50, 51, 52, 53]; + let assignments = vec![(cert.clone(), candidate_index)]; + let approvals = vec![approval.clone()]; - assert_matches!( + let mut assignment_sent_peers = assert_matches!( overseer_recv(overseer).await, AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( sent_peers, @@ -2532,8 +3087,8 @@ fn propagates_assignments_along_unshared_dimension() { protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) )) )) => { - assert_eq!(sent_peers.len(), expected_y.len() + 4); - for &i in &expected_y { + assert_eq!(sent_peers.len(), expected_indices.len() + 4); + for &i in &expected_indices { assert!( sent_peers.contains(&peers[i].0), "Message not sent to expected peer {}", @@ -2541,1123 +3096,1029 @@ fn propagates_assignments_along_unshared_dimension() { ); } assert_eq!(sent_assignments, assignments); + sent_peers } ); - }; - // Test messages from X direction go to Y peers - { - let validator_index = ValidatorIndex(50); + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + mut sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) + )) + )) => { + // Random sampling is reused from the assignment. + sent_peers.sort(); + assignment_sent_peers.sort(); + assert_eq!(sent_peers, assignment_sent_peers); + assert_eq!(sent_approvals, approvals); + } + ); + + for i in omitted.iter().copied() { + setup_peer_with_view(overseer, &peers[i].0, view![hash], ValidationVersion::V1) + .await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + )) + )) => { + assert_eq!(sent_peers.len(), 1); + assert_eq!(&sent_peers[0], &peers[i].0); + assert_eq!(sent_assignments, assignments); + } + ); + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) + )) + )) => { + assert_eq!(sent_peers.len(), 1); + assert_eq!(&sent_peers[0], &peers[i].0); + assert_eq!(sent_approvals, approvals); + } + ); + } + + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); +} + +// test that new gossip topology triggers send of messages. +#[test] +fn sends_to_more_peers_after_getting_topology() { + let parent_hash = Hash::repeat_byte(0xFF); + let hash = Hash::repeat_byte(0xAA); + + let peers = make_peers_and_authority_ids(100); + + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Arc::new(SystemClock {}), + State::default(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + + // Connect all peers except omitted. + for (peer, _) in &peers { + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; + } + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + let validator_index = ValidatorIndex(0); let candidate_index = 0u32; // import an assignment and approval locally. let cert = fake_assignment_cert(hash, validator_index); + let approval = IndirectSignedApprovalVote { + block_hash: hash, + candidate_index, + validator: validator_index, + signature: dummy_signature(), + }; + + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_index.into(), + ), + ) + .await; + + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), + ) + .await; + let assignments = vec![(cert.clone(), candidate_index)]; + let approvals = vec![approval.clone()]; + + let expected_indices = vec![0, 10, 20, 30, 50, 51, 52, 53]; + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Set up a gossip topology. + setup_gossip_topology( + overseer, + make_gossip_topology( + 1, + &peers_with_optional_peer_id, + &[0, 10, 20, 30], + &[50, 51, 52, 53], + 1, + ), + ) + .await; + + let mut expected_indices_assignments = expected_indices.clone(); + let mut expected_indices_approvals = expected_indices.clone(); + + for _ in 0..expected_indices_assignments.len() { + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + )) + )) => { + // Sends to all expected peers. + assert_eq!(sent_peers.len(), 1); + assert_eq!(sent_assignments, assignments); - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + let pos = expected_indices_assignments.iter() + .position(|i| &peers[*i].0 == &sent_peers[0]) + .unwrap(); + expected_indices_assignments.remove(pos); + } + ); + } - // Issuer of the message is important, not the peer we receive from. - // 99 deliberately chosen because it's not in X or Y. - send_message_from_peer(overseer, &peers[99].0, msg).await; - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - _, - _, - tx, - )) => { - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); - expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await; + for _ in 0..expected_indices_approvals.len() { + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) + )) + )) => { + // Sends to all expected peers. + assert_eq!(sent_peers.len(), 1); + assert_eq!(sent_approvals, approvals); - let expected_x = [0, 10, 20, 30]; + let pos = expected_indices_approvals.iter() + .position(|i| &peers[*i].0 == &sent_peers[0]) + .unwrap(); - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) - )) - )) => { - assert_eq!(sent_peers.len(), expected_x.len() + 4); - for &i in &expected_x { - assert!( - sent_peers.contains(&peers[i].0), - "Message not sent to expected peer {}", - i, - ); + expected_indices_approvals.remove(pos); } - assert_eq!(sent_assignments, assignments); - } - ); - }; + ); + } - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); } -// tests that messages are propagated to necessary peers after they connect +// test aggression L1 #[test] -fn propagates_to_required_after_connect() { +fn originator_aggression_l1() { let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); let peers = make_peers_and_authority_ids(100); - let _ = test_harness(State::default(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; + let mut state = State::default(); + state.aggression_config.resend_unfinalized_period = None; + let aggression_l1_threshold = state.aggression_config.l1_threshold.unwrap(); - let omitted = [0, 10, 50, 51]; + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Arc::new(SystemClock {}), + state, + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; - // Connect all peers except omitted. - for (i, (peer, _)) in peers.iter().enumerate() { - if !omitted.contains(&i) { + // Connect all peers except omitted. + for (peer, _) in &peers { setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; } - } - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Set up a gossip topology. - setup_gossip_topology( - overseer, - make_gossip_topology( - 1, - &peers_with_optional_peer_id, - &[0, 10, 20, 30, 40, 60, 70, 80], - &[50, 51, 52, 53, 54, 55, 56, 57], - 1, - ), - ) - .await; - - let expected_indices = [ - // Both dimensions in the gossip topology, minus omitted. - 20, 30, 40, 60, 70, 80, 52, 53, 54, 55, 56, 57, - ]; - - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; - // import an assignment and approval locally. - let cert = fake_assignment_cert(hash, validator_index); - let approval = IndirectSignedApprovalVote { - block_hash: hash, - candidate_index, - validator: validator_index, - signature: dummy_signature(), - }; + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment( - cert.clone().into(), - candidate_index.into(), - ), - ) - .await; + // import an assignment and approval locally. + let cert = fake_assignment_cert(hash, validator_index); + let approval = IndirectSignedApprovalVote { + block_hash: hash, + candidate_index, + validator: validator_index, + signature: dummy_signature(), + }; + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Set up a gossip topology. + setup_gossip_topology( + overseer, + make_gossip_topology( + 1, + &peers_with_optional_peer_id, + &[0, 10, 20, 30], + &[50, 51, 52, 53], + 1, + ), + ) + .await; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), - ) - .await; + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment( + cert.clone().into(), + candidate_index.into(), + ), + ) + .await; - let assignments = vec![(cert.clone(), candidate_index)]; - let approvals = vec![approval.clone()]; - - let mut assignment_sent_peers = assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) - )) - )) => { - assert_eq!(sent_peers.len(), expected_indices.len() + 4); - for &i in &expected_indices { - assert!( - sent_peers.contains(&peers[i].0), - "Message not sent to expected peer {}", - i, - ); - } - assert_eq!(sent_assignments, assignments); - sent_peers - } - ); - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - mut sent_peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) - )) - )) => { - // Random sampling is reused from the assignment. - sent_peers.sort(); - assignment_sent_peers.sort(); - assert_eq!(sent_peers, assignment_sent_peers); - assert_eq!(sent_approvals, approvals); - } - ); + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), + ) + .await; - for i in omitted.iter().copied() { - setup_peer_with_view(overseer, &peers[i].0, view![hash], ValidationVersion::V1).await; + let assignments = vec![(cert.clone(), candidate_index)]; + let approvals = vec![approval.clone()]; - assert_matches!( + let prev_sent_indices = assert_matches!( overseer_recv(overseer).await, AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( sent_peers, Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + protocol_v1::ApprovalDistributionMessage::Assignments(_) )) )) => { - assert_eq!(sent_peers.len(), 1); - assert_eq!(&sent_peers[0], &peers[i].0); - assert_eq!(sent_assignments, assignments); + sent_peers.into_iter() + .filter_map(|sp| peers.iter().position(|p| &p.0 == &sp)) + .collect::>() } ); assert_matches!( overseer_recv(overseer).await, AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, + _, Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) + protocol_v1::ApprovalDistributionMessage::Approvals(_) )) - )) => { - assert_eq!(sent_peers.len(), 1); - assert_eq!(&sent_peers[0], &peers[i].0); - assert_eq!(sent_approvals, approvals); - } + )) => { } ); - } - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); + // Add blocks until aggression L1 is triggered. + { + let mut parent_hash = hash; + for level in 0..aggression_l1_threshold { + let number = 1 + level + 1; // first block had number 1 + let hash = BlakeTwo256::hash_of(&(parent_hash, number)); + let meta = BlockApprovalMeta { + hash, + parent_hash, + number, + candidates: vec![], + slot: (level as u64).into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + let msg = ApprovalDistributionMessage::ApprovalCheckingLagUpdate(level + 1); + overseer_send(overseer, msg).await; + + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + parent_hash = hash; + } + } + + let unsent_indices = + (0..peers.len()).filter(|i| !prev_sent_indices.contains(&i)).collect::>(); + + for _ in 0..unsent_indices.len() { + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + )) + )) => { + // Sends to all expected peers. + assert_eq!(sent_peers.len(), 1); + assert_eq!(sent_assignments, assignments); + + assert!(unsent_indices.iter() + .any(|i| &peers[*i].0 == &sent_peers[0])); + } + ); + } + + for _ in 0..unsent_indices.len() { + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) + )) + )) => { + // Sends to all expected peers. + assert_eq!(sent_peers.len(), 1); + assert_eq!(sent_approvals, approvals); + + assert!(unsent_indices.iter() + .any(|i| &peers[*i].0 == &sent_peers[0])); + } + ); + } + + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); } -// test that new gossip topology triggers send of messages. +// test aggression L1 #[test] -fn sends_to_more_peers_after_getting_topology() { +fn non_originator_aggression_l1() { let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); let peers = make_peers_and_authority_ids(100); - let _ = test_harness(State::default(), |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - - // Connect all peers except omitted. - for (peer, _) in &peers { - setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; - } + let mut state = state_without_reputation_delay(); + state.aggression_config.resend_unfinalized_period = None; + let aggression_l1_threshold = state.aggression_config.l1_threshold.unwrap(); - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Arc::new(SystemClock {}), + state, + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; + // Connect all peers except omitted. + for (peer, _) in &peers { + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; + } - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; - // import an assignment and approval locally. - let cert = fake_assignment_cert(hash, validator_index); - let approval = IndirectSignedApprovalVote { - block_hash: hash, - candidate_index, - validator: validator_index, - signature: dummy_signature(), - }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment( - cert.clone().into(), - candidate_index.into(), - ), - ) - .await; + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), - ) - .await; + // import an assignment and approval locally. + let cert = fake_assignment_cert(hash, validator_index); + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Set up a gossip topology. + setup_gossip_topology( + overseer, + make_gossip_topology( + 1, + &peers_with_optional_peer_id, + &[0, 10, 20, 30], + &[50, 51, 52, 53], + 1, + ), + ) + .await; - let assignments = vec![(cert.clone(), candidate_index)]; - let approvals = vec![approval.clone()]; - - let expected_indices = vec![0, 10, 20, 30, 50, 51, 52, 53]; - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Set up a gossip topology. - setup_gossip_topology( - overseer, - make_gossip_topology( - 1, - &peers_with_optional_peer_id, - &[0, 10, 20, 30], - &[50, 51, 52, 53], - 1, - ), - ) - .await; + let assignments = vec![(cert.clone().into(), candidate_index)]; + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); - let mut expected_indices_assignments = expected_indices.clone(); - let mut expected_indices_approvals = expected_indices.clone(); + // Issuer of the message is important, not the peer we receive from. + // 99 deliberately chosen because it's not in X or Y. + send_message_from_peer(overseer, &peers[99].0, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; - for _ in 0..expected_indices_assignments.len() { assert_matches!( overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) - )) + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, _, )) => { - // Sends to all expected peers. - assert_eq!(sent_peers.len(), 1); - assert_eq!(sent_assignments, assignments); - - let pos = expected_indices_assignments.iter() - .position(|i| &peers[*i].0 == &sent_peers[0]) - .unwrap(); - expected_indices_assignments.remove(pos); + assert_eq!(assignment.tranche(), 0); } ); - } - for _ in 0..expected_indices_approvals.len() { + expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await; + assert_matches!( overseer_recv(overseer).await, AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, + _, Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) + protocol_v1::ApprovalDistributionMessage::Assignments(_) )) - )) => { - // Sends to all expected peers. - assert_eq!(sent_peers.len(), 1); - assert_eq!(sent_approvals, approvals); - - let pos = expected_indices_approvals.iter() - .position(|i| &peers[*i].0 == &sent_peers[0]) - .unwrap(); - - expected_indices_approvals.remove(pos); - } + )) => { } ); - } - - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); -} -// test aggression L1 -#[test] -fn originator_aggression_l1() { - let parent_hash = Hash::repeat_byte(0xFF); - let hash = Hash::repeat_byte(0xAA); + // Add blocks until aggression L1 is triggered. + { + let mut parent_hash = hash; + for level in 0..aggression_l1_threshold { + let number = 1 + level + 1; // first block had number 1 + let hash = BlakeTwo256::hash_of(&(parent_hash, number)); + let meta = BlockApprovalMeta { + hash, + parent_hash, + number, + candidates: vec![], + slot: (level as u64).into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; - let peers = make_peers_and_authority_ids(100); + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; - let mut state = State::default(); - state.aggression_config.resend_unfinalized_period = None; - let aggression_l1_threshold = state.aggression_config.l1_threshold.unwrap(); + parent_hash = hash; + } + } - let _ = test_harness(state, |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; + // No-op on non-originator - // Connect all peers except omitted. - for (peer, _) in &peers { - setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; - } + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); +} - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; +// test aggression L2 on non-originator +#[test] +fn non_originator_aggression_l2() { + let parent_hash = Hash::repeat_byte(0xFF); + let hash = Hash::repeat_byte(0xAA); - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; + let peers = make_peers_and_authority_ids(100); - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; + let mut state = state_without_reputation_delay(); + state.aggression_config.resend_unfinalized_period = None; - // import an assignment and approval locally. - let cert = fake_assignment_cert(hash, validator_index); - let approval = IndirectSignedApprovalVote { - block_hash: hash, - candidate_index, - validator: validator_index, - signature: dummy_signature(), - }; - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Set up a gossip topology. - setup_gossip_topology( - overseer, - make_gossip_topology( - 1, - &peers_with_optional_peer_id, - &[0, 10, 20, 30], - &[50, 51, 52, 53], - 1, - ), - ) - .await; + let aggression_l1_threshold = state.aggression_config.l1_threshold.unwrap(); + let aggression_l2_threshold = state.aggression_config.l2_threshold.unwrap(); + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Arc::new(SystemClock {}), + state, + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + + // Connect all peers except omitted. + for (peer, _) in &peers { + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; + } - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment( - cert.clone().into(), - candidate_index.into(), - ), - ) - .await; + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeApproval(approval.clone().into()), - ) - .await; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; - let assignments = vec![(cert.clone(), candidate_index)]; - let approvals = vec![approval.clone()]; - - let prev_sent_indices = assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(_) - )) - )) => { - sent_peers.into_iter() - .filter_map(|sp| peers.iter().position(|p| &p.0 == &sp)) - .collect::>() - } - ); + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - _, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Approvals(_) - )) - )) => { } - ); - - // Add blocks until aggression L1 is triggered. - { - let mut parent_hash = hash; - for level in 0..aggression_l1_threshold { - let number = 1 + level + 1; // first block had number 1 - let hash = BlakeTwo256::hash_of(&(parent_hash, number)); - let meta = BlockApprovalMeta { - hash, - parent_hash, - number, - candidates: vec![], - slot: (level as u64).into(), - session: 1, - }; - - let msg = ApprovalDistributionMessage::ApprovalCheckingLagUpdate(level + 1); - overseer_send(overseer, msg).await; - - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - parent_hash = hash; - } - } + // import an assignment and approval locally. + let cert = fake_assignment_cert(hash, validator_index); + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Set up a gossip topology. + setup_gossip_topology( + overseer, + make_gossip_topology( + 1, + &peers_with_optional_peer_id, + &[0, 10, 20, 30], + &[50, 51, 52, 53], + 1, + ), + ) + .await; - let unsent_indices = - (0..peers.len()).filter(|i| !prev_sent_indices.contains(&i)).collect::>(); + let assignments = vec![(cert.clone(), candidate_index)]; + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); - for _ in 0..unsent_indices.len() { + // Issuer of the message is important, not the peer we receive from. + // 99 deliberately chosen because it's not in X or Y. + send_message_from_peer(overseer, &peers[99].0, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; assert_matches!( overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) - )) + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, _, )) => { - // Sends to all expected peers. - assert_eq!(sent_peers.len(), 1); - assert_eq!(sent_assignments, assignments); - - assert!(unsent_indices.iter() - .any(|i| &peers[*i].0 == &sent_peers[0])); + assert_eq!(assignment.tranche(), 0); } ); - } - for _ in 0..unsent_indices.len() { - assert_matches!( + expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await; + + let prev_sent_indices = assert_matches!( overseer_recv(overseer).await, AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( sent_peers, Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals) + protocol_v1::ApprovalDistributionMessage::Assignments(_) )) )) => { - // Sends to all expected peers. - assert_eq!(sent_peers.len(), 1); - assert_eq!(sent_approvals, approvals); - - assert!(unsent_indices.iter() - .any(|i| &peers[*i].0 == &sent_peers[0])); + sent_peers.into_iter() + .filter_map(|sp| peers.iter().position(|p| &p.0 == &sp)) + .collect::>() } ); - } - - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); -} -// test aggression L1 -#[test] -fn non_originator_aggression_l1() { - let parent_hash = Hash::repeat_byte(0xFF); - let hash = Hash::repeat_byte(0xAA); + // Add blocks until aggression L1 is triggered. + let chain_head = { + let mut parent_hash = hash; + for level in 0..aggression_l1_threshold { + let number = 1 + level + 1; // first block had number 1 + let hash = BlakeTwo256::hash_of(&(parent_hash, number)); + let meta = BlockApprovalMeta { + hash, + parent_hash, + number, + candidates: vec![], + slot: (level as u64).into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; - let peers = make_peers_and_authority_ids(100); + let msg = ApprovalDistributionMessage::ApprovalCheckingLagUpdate(level + 1); + overseer_send(overseer, msg).await; - let mut state = state_without_reputation_delay(); - state.aggression_config.resend_unfinalized_period = None; - let aggression_l1_threshold = state.aggression_config.l1_threshold.unwrap(); + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; - let _ = test_harness(state, |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; + parent_hash = hash; + } - // Connect all peers except omitted. - for (peer, _) in &peers { - setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; - } + parent_hash + }; - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; + // No-op on non-originator - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; - - // import an assignment and approval locally. - let cert = fake_assignment_cert(hash, validator_index); - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Set up a gossip topology. - setup_gossip_topology( - overseer, - make_gossip_topology( - 1, - &peers_with_optional_peer_id, - &[0, 10, 20, 30], - &[50, 51, 52, 53], - 1, - ), - ) - .await; + // Add blocks until aggression L2 is triggered. + { + let mut parent_hash = chain_head; + for level in 0..aggression_l2_threshold - aggression_l1_threshold { + let number = aggression_l1_threshold + level + 1 + 1; // first block had number 1 + let hash = BlakeTwo256::hash_of(&(parent_hash, number)); + let meta = BlockApprovalMeta { + hash, + parent_hash, + number, + candidates: vec![], + slot: (level as u64).into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; - let assignments = vec![(cert.clone().into(), candidate_index)]; - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + let msg = ApprovalDistributionMessage::ApprovalCheckingLagUpdate( + aggression_l1_threshold + level + 1, + ); + overseer_send(overseer, msg).await; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; - // Issuer of the message is important, not the peer we receive from. - // 99 deliberately chosen because it's not in X or Y. - send_message_from_peer(overseer, &peers[99].0, msg).await; - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - _, - _, - tx, - )) => { - tx.send(AssignmentCheckResult::Accepted).unwrap(); + parent_hash = hash; + } } - ); - expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await; + // XY dimension - previously sent. + let unsent_indices = [0, 10, 20, 30, 50, 51, 52, 53] + .iter() + .cloned() + .filter(|i| !prev_sent_indices.contains(&i)) + .collect::>(); - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - _, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(_) - )) - )) => { } - ); - - // Add blocks until aggression L1 is triggered. - { - let mut parent_hash = hash; - for level in 0..aggression_l1_threshold { - let number = 1 + level + 1; // first block had number 1 - let hash = BlakeTwo256::hash_of(&(parent_hash, number)); - let meta = BlockApprovalMeta { - hash, - parent_hash, - number, - candidates: vec![], - slot: (level as u64).into(), - session: 1, - }; - - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - parent_hash = hash; - } - } + for _ in 0..unsent_indices.len() { + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + )) + )) => { + // Sends to all expected peers. + assert_eq!(sent_peers.len(), 1); + assert_eq!(sent_assignments, assignments); - // No-op on non-originator + assert!(unsent_indices.iter() + .any(|i| &peers[*i].0 == &sent_peers[0])); + } + ); + } - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); } -// test aggression L2 on non-originator +// Tests that messages propagate to the unshared dimension. #[test] -fn non_originator_aggression_l2() { +fn resends_messages_periodically() { let parent_hash = Hash::repeat_byte(0xFF); let hash = Hash::repeat_byte(0xAA); let peers = make_peers_and_authority_ids(100); let mut state = state_without_reputation_delay(); - state.aggression_config.resend_unfinalized_period = None; + state.aggression_config.l1_threshold = None; + state.aggression_config.l2_threshold = None; + state.aggression_config.resend_unfinalized_period = Some(2); + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Arc::new(SystemClock {}), + state, + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + + // Connect all peers. + for (peer, _) in &peers { + setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; + } + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + // Set up a gossip topology. + setup_gossip_topology( + overseer, + make_gossip_topology( + 1, + &peers_with_optional_peer_id, + &[0, 10, 20, 30], + &[50, 51, 52, 53], + 1, + ), + ) + .await; - let aggression_l1_threshold = state.aggression_config.l1_threshold.unwrap(); - let aggression_l2_threshold = state.aggression_config.l2_threshold.unwrap(); - let _ = test_harness(state, |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; - // Connect all peers except omitted. - for (peer, _) in &peers { - setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; - } + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; - - // import an assignment and approval locally. - let cert = fake_assignment_cert(hash, validator_index); - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Set up a gossip topology. - setup_gossip_topology( - overseer, - make_gossip_topology( - 1, - &peers_with_optional_peer_id, - &[0, 10, 20, 30], - &[50, 51, 52, 53], - 1, - ), - ) - .await; + // import an assignment and approval locally. + let cert = fake_assignment_cert(hash, validator_index); + let assignments = vec![(cert.clone(), candidate_index)]; - let assignments = vec![(cert.clone(), candidate_index)]; - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + { + let msg = + protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + + // Issuer of the message is important, not the peer we receive from. + // 99 deliberately chosen because it's not in X or Y. + send_message_from_peer(overseer, &peers[99].0, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; - // Issuer of the message is important, not the peer we receive from. - // 99 deliberately chosen because it's not in X or Y. - send_message_from_peer(overseer, &peers[99].0, msg).await; - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - _, - _, - tx, - )) => { - tx.send(AssignmentCheckResult::Accepted).unwrap(); - } - ); - - expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await; - - let prev_sent_indices = assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(_) - )) - )) => { - sent_peers.into_iter() - .filter_map(|sp| peers.iter().position(|p| &p.0 == &sp)) - .collect::>() - } - ); - - // Add blocks until aggression L1 is triggered. - let chain_head = { - let mut parent_hash = hash; - for level in 0..aggression_l1_threshold { - let number = 1 + level + 1; // first block had number 1 - let hash = BlakeTwo256::hash_of(&(parent_hash, number)); - let meta = BlockApprovalMeta { - hash, - parent_hash, - number, - candidates: vec![], - slot: (level as u64).into(), - session: 1, - }; - - let msg = ApprovalDistributionMessage::ApprovalCheckingLagUpdate(level + 1); - overseer_send(overseer, msg).await; - - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - parent_hash = hash; - } + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, _, + )) => { + assert_eq!(assignment.tranche(), 0); + } + ); + expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await; - parent_hash - }; + let expected_y = [50, 51, 52, 53]; - // No-op on non-originator - - // Add blocks until aggression L2 is triggered. - { - let mut parent_hash = chain_head; - for level in 0..aggression_l2_threshold - aggression_l1_threshold { - let number = aggression_l1_threshold + level + 1 + 1; // first block had number 1 - let hash = BlakeTwo256::hash_of(&(parent_hash, number)); - let meta = BlockApprovalMeta { - hash, - parent_hash, - number, - candidates: vec![], - slot: (level as u64).into(), - session: 1, - }; - - let msg = ApprovalDistributionMessage::ApprovalCheckingLagUpdate( - aggression_l1_threshold + level + 1, + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + )) + )) => { + assert_eq!(sent_peers.len(), expected_y.len() + 4); + for &i in &expected_y { + assert!( + sent_peers.contains(&peers[i].0), + "Message not sent to expected peer {}", + i, + ); + } + assert_eq!(sent_assignments, assignments); + } ); - overseer_send(overseer, msg).await; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; + }; + + let mut number = 1; + for _ in 0..10 { + // Add blocks until resend is done. + { + let mut parent_hash = hash; + for level in 0..2 { + number = number + 1; + let hash = BlakeTwo256::hash_of(&(parent_hash, number)); + let meta = BlockApprovalMeta { + hash, + parent_hash, + number, + candidates: vec![], + slot: (level as u64).into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + + let msg = ApprovalDistributionMessage::ApprovalCheckingLagUpdate(2); + overseer_send(overseer, msg).await; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + parent_hash = hash; + } + } - parent_hash = hash; + let mut expected_y = vec![50, 51, 52, 53]; + + // Expect messages sent only to topology peers, one by one. + for _ in 0..expected_y.len() { + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + sent_peers, + Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( + protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + )) + )) => { + assert_eq!(sent_peers.len(), 1); + let expected_pos = expected_y.iter() + .position(|&i| &peers[i].0 == &sent_peers[0]) + .unwrap(); + + expected_y.remove(expected_pos); + assert_eq!(sent_assignments, assignments); + } + ); + } } - } - // XY dimension - previously sent. - let unsent_indices = [0, 10, 20, 30, 50, 51, 52, 53] - .iter() - .cloned() - .filter(|i| !prev_sent_indices.contains(&i)) - .collect::>(); + assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); + virtual_overseer + }, + ); +} + +/// Tests that peers correctly receive versioned messages. +#[test] +fn import_versioned_approval() { + let peers = make_peers_and_authority_ids(15); + let peer_a = peers.get(0).unwrap().0; + let peer_b = peers.get(1).unwrap().0; + let peer_c = peers.get(2).unwrap().0; + + let parent_hash = Hash::repeat_byte(0xFF); + let hash = Hash::repeat_byte(0xAA); + let state = state_without_reputation_delay(); + let candidate_hash = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xBB)); + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Arc::new(SystemClock {}), + state, + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // All peers are aware of relay parent. + setup_peer_with_view(overseer, &peer_a, view![hash], ValidationVersion::V2).await; + setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V1).await; + setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V2).await; + + // Set up a gossip topology, where a, b, c and d are topology neighbors to the node + // under testing. + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; + + let mut keystore = LocalKeystore::in_memory(); + let session = dummy_session_info_valid(1, &mut keystore, 1); + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 1, + candidates: vec![(candidate_hash, 0.into(), 0.into()); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // import an assignment related to `hash` locally + let validator_index = ValidatorIndex(0); + let candidate_index = 0u32; + let cert = fake_assignment_cert(hash, validator_index); + overseer_send( + overseer, + ApprovalDistributionMessage::DistributeAssignment( + cert.into(), + candidate_index.into(), + ), + ) + .await; - for _ in 0..unsent_indices.len() { assert_matches!( overseer_recv(overseer).await, AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, + peers, Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + protocol_v1::ApprovalDistributionMessage::Assignments(assignments) )) )) => { - // Sends to all expected peers. - assert_eq!(sent_peers.len(), 1); - assert_eq!(sent_assignments, assignments); - - assert!(unsent_indices.iter() - .any(|i| &peers[*i].0 == &sent_peers[0])); + assert_eq!(peers, vec![peer_b]); + assert_eq!(assignments.len(), 1); } ); - } - - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); -} - -// Tests that messages propagate to the unshared dimension. -#[test] -fn resends_messages_periodically() { - let parent_hash = Hash::repeat_byte(0xFF); - let hash = Hash::repeat_byte(0xAA); - - let peers = make_peers_and_authority_ids(100); - let mut state = state_without_reputation_delay(); - state.aggression_config.l1_threshold = None; - state.aggression_config.l2_threshold = None; - state.aggression_config.resend_unfinalized_period = Some(2); - let _ = test_harness(state, |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - - // Connect all peers. - for (peer, _) in &peers { - setup_peer_with_view(overseer, peer, view![hash], ValidationVersion::V1).await; - } - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - // Set up a gossip topology. - setup_gossip_topology( - overseer, - make_gossip_topology( - 1, - &peers_with_optional_peer_id, - &[0, 10, 20, 30], - &[50, 51, 52, 53], - 1, - ), - ) - .await; - - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; - - // import an assignment and approval locally. - let cert = fake_assignment_cert(hash, validator_index); - let assignments = vec![(cert.clone(), candidate_index)]; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V2(protocol_v2::ValidationProtocol::ApprovalDistribution( + protocol_v2::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 2); + assert!(peers.contains(&peer_a)); + assert!(peers.contains(&peer_c)); - { - let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + assert_eq!(assignments.len(), 1); + } + ); - // Issuer of the message is important, not the peer we receive from. - // 99 deliberately chosen because it's not in X or Y. - send_message_from_peer(overseer, &peers[99].0, msg).await; + // send the an approval from peer_a + let approval = IndirectSignedApprovalVote { + block_hash: hash, + candidate_index, + validator: validator_index, + signature: signature_for( + &keystore, + &session, + vec![candidate_hash], + validator_index, + ), + }; + let msg = protocol_v2::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); + send_message_from_peer_v2(overseer, &peer_a, msg).await; + provide_session(overseer, session).await; assert_matches!( overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment( - _, - _, - tx, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportApproval( + vote, _, )) => { - tx.send(AssignmentCheckResult::Accepted).unwrap(); + assert_eq!(Into::::into(vote), approval.into()); } ); - expect_reputation_change(overseer, &peers[99].0, BENEFIT_VALID_MESSAGE_FIRST).await; - let expected_y = [50, 51, 52, 53]; + expect_reputation_change(overseer, &peer_a, BENEFIT_VALID_MESSAGE_FIRST).await; + // Peers b and c receive versioned approval messages. assert_matches!( overseer_recv(overseer).await, AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, + peers, Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) + protocol_v1::ApprovalDistributionMessage::Approvals(approvals) )) )) => { - assert_eq!(sent_peers.len(), expected_y.len() + 4); - for &i in &expected_y { - assert!( - sent_peers.contains(&peers[i].0), - "Message not sent to expected peer {}", - i, - ); - } - assert_eq!(sent_assignments, assignments); + assert_eq!(peers, vec![peer_b]); + assert_eq!(approvals.len(), 1); } ); - }; - - let mut number = 1; - for _ in 0..10 { - // Add blocks until resend is done. - { - let mut parent_hash = hash; - for level in 0..2 { - number = number + 1; - let hash = BlakeTwo256::hash_of(&(parent_hash, number)); - let meta = BlockApprovalMeta { - hash, - parent_hash, - number, - candidates: vec![], - slot: (level as u64).into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::ApprovalCheckingLagUpdate(2); - overseer_send(overseer, msg).await; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - parent_hash = hash; + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V2(protocol_v2::ValidationProtocol::ApprovalDistribution( + protocol_v2::ApprovalDistributionMessage::Approvals(approvals) + )) + )) => { + assert_eq!(peers, vec![peer_c]); + assert_eq!(approvals.len(), 1); } - } - - let mut expected_y = vec![50, 51, 52, 53]; - - // Expect messages sent only to topology peers, one by one. - for _ in 0..expected_y.len() { - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - sent_peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments) - )) - )) => { - assert_eq!(sent_peers.len(), 1); - let expected_pos = expected_y.iter() - .position(|&i| &peers[i].0 == &sent_peers[0]) - .unwrap(); - - expected_y.remove(expected_pos); - assert_eq!(sent_assignments, assignments); - } - ); - } - } - - assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent"); - virtual_overseer - }); -} - -/// Tests that peers correctly receive versioned messages. -#[test] -fn import_versioned_approval() { - let peers = make_peers_and_authority_ids(15); - let peer_a = peers.get(0).unwrap().0; - let peer_b = peers.get(1).unwrap().0; - let peer_c = peers.get(2).unwrap().0; + ); - let parent_hash = Hash::repeat_byte(0xFF); - let hash = Hash::repeat_byte(0xAA); - let state = state_without_reputation_delay(); - let _ = test_harness(state, |mut virtual_overseer| async move { - let overseer = &mut virtual_overseer; - // All peers are aware of relay parent. - setup_peer_with_view(overseer, &peer_a, view![hash], ValidationVersion::V2).await; - setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V1).await; - setup_peer_with_view(overseer, &peer_c, view![hash], ValidationVersion::V2).await; - - // Set up a gossip topology, where a, b, c and d are topology neighbors to the node under - // testing. - let peers_with_optional_peer_id = peers - .iter() - .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) - .collect_vec(); - setup_gossip_topology( - overseer, - make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), - ) - .await; + // send an obviously invalid approval + let approval = IndirectSignedApprovalVote { + block_hash: hash, + // Invalid candidate index, should not pass sanitization. + candidate_index: 16777284, + validator: validator_index, + signature: dummy_signature(), + }; + let msg = protocol_v2::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); + send_message_from_peer_v2(overseer, &peer_a, msg).await; - // new block `hash_a` with 1 candidates - let meta = BlockApprovalMeta { - hash, - parent_hash, - number: 1, - candidates: vec![Default::default(); 1], - slot: 1.into(), - session: 1, - }; - let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); - overseer_send(overseer, msg).await; - - // import an assignment related to `hash` locally - let validator_index = ValidatorIndex(0); - let candidate_index = 0u32; - let cert = fake_assignment_cert(hash, validator_index); - overseer_send( - overseer, - ApprovalDistributionMessage::DistributeAssignment(cert.into(), candidate_index.into()), - ) - .await; + expect_reputation_change(overseer, &peer_a, COST_OVERSIZED_BITFIELD).await; - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers, vec![peer_b]); - assert_eq!(assignments.len(), 1); - } - ); - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V2(protocol_v2::ValidationProtocol::ApprovalDistribution( - protocol_v2::ApprovalDistributionMessage::Assignments(assignments) - )) - )) => { - assert_eq!(peers.len(), 2); - assert!(peers.contains(&peer_a)); - assert!(peers.contains(&peer_c)); - - assert_eq!(assignments.len(), 1); - } - ); - - // send the an approval from peer_a - let approval = IndirectSignedApprovalVote { - block_hash: hash, - candidate_index, - validator: validator_index, - signature: dummy_signature(), - }; - let msg = protocol_v2::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer_v2(overseer, &peer_a, msg).await; - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportApproval( - vote, - tx, - )) => { - assert_eq!(vote, approval.into()); - tx.send(ApprovalCheckResult::Accepted).unwrap(); - } - ); - - expect_reputation_change(overseer, &peer_a, BENEFIT_VALID_MESSAGE_FIRST).await; - - // Peers b and c receive versioned approval messages. - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V1(protocol_v1::ValidationProtocol::ApprovalDistribution( - protocol_v1::ApprovalDistributionMessage::Approvals(approvals) - )) - )) => { - assert_eq!(peers, vec![peer_b]); - assert_eq!(approvals.len(), 1); - } - ); - - assert_matches!( - overseer_recv(overseer).await, - AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( - peers, - Versioned::V2(protocol_v2::ValidationProtocol::ApprovalDistribution( - protocol_v2::ApprovalDistributionMessage::Approvals(approvals) - )) - )) => { - assert_eq!(peers, vec![peer_c]); - assert_eq!(approvals.len(), 1); - } - ); - - // send an obviously invalid approval - let approval = IndirectSignedApprovalVote { - block_hash: hash, - // Invalid candidate index, should not pass sanitization. - candidate_index: 16777284, - validator: validator_index, - signature: dummy_signature(), - }; - let msg = protocol_v2::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer_v2(overseer, &peer_a, msg).await; - - expect_reputation_change(overseer, &peer_a, COST_OVERSIZED_BITFIELD).await; - - // send an obviously invalid approval - let approval = IndirectSignedApprovalVoteV2 { - block_hash: hash, - // Invalid candidates len, should not pass sanitization. - candidate_indices: 16777284.into(), - validator: validator_index, - signature: dummy_signature(), - }; - let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); - send_message_from_peer_v3(overseer, &peer_a, msg).await; + // send an obviously invalid approval + let approval = IndirectSignedApprovalVoteV2 { + block_hash: hash, + // Invalid candidates len, should not pass sanitization. + candidate_indices: 16777284.into(), + validator: validator_index, + signature: dummy_signature(), + }; + let msg = protocol_v3::ApprovalDistributionMessage::Approvals(vec![approval.clone()]); + send_message_from_peer_v3(overseer, &peer_a, msg).await; - expect_reputation_change(overseer, &peer_a, COST_OVERSIZED_BITFIELD).await; + expect_reputation_change(overseer, &peer_a, COST_OVERSIZED_BITFIELD).await; - virtual_overseer - }); + virtual_overseer + }, + ); } fn batch_test_round(message_count: usize) { @@ -3667,11 +4128,26 @@ fn batch_test_round(message_count: usize) { let (mut context, mut virtual_overseer) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool.clone()); - let subsystem = ApprovalDistribution::new(Default::default()); + let subsystem = ApprovalDistribution::new_with_clock( + Default::default(), + Default::default(), + Arc::new(SystemClock {}), + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + ); let mut rng = rand_chacha::ChaCha12Rng::seed_from_u64(12345); let mut sender = context.sender().clone(); - let subsystem = - subsystem.run_inner(context, &mut state, REPUTATION_CHANGE_TEST_INTERVAL, &mut rng); + let mut session_info_provider = RuntimeInfo::new_with_config(RuntimeInfoConfig { + keystore: None, + session_cache_lru_size: DISPUTE_WINDOW.get(), + }); + + let subsystem = subsystem.run_inner( + context, + &mut state, + REPUTATION_CHANGE_TEST_INTERVAL, + &mut rng, + &mut session_info_provider, + ); let test_fut = async move { let overseer = &mut virtual_overseer; @@ -3817,3 +4293,420 @@ fn const_ensure_size_not_zero() { crate::ensure_size_not_zero(super::MAX_ASSIGNMENT_BATCH_SIZE); crate::ensure_size_not_zero(super::MAX_APPROVAL_BATCH_SIZE); } + +struct DummyClock; +impl Clock for DummyClock { + fn tick_now(&self) -> polkadot_node_primitives::approval::time::Tick { + 0 + } + + fn wait( + &self, + _tick: polkadot_node_primitives::approval::time::Tick, + ) -> std::pin::Pin + Send + 'static>> { + todo!() + } +} + +/// Subsystem rejects assignments too far into the future. +#[test] +fn subsystem_rejects_assignment_in_future() { + let peers = make_peers_and_authority_ids(15); + let peer_a = peers.get(0).unwrap().0; + let parent_hash = Hash::repeat_byte(0xFF); + let hash = Hash::repeat_byte(0xAA); + + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(89) }), + Arc::new(DummyClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup peers + setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V1).await; + + // Set up a gossip topology, where a, b, c and d are topology neighbors to the node + // under testing. + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 2, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // send the assignment related to `hash` + let validator_index = ValidatorIndex(0); + let cert = fake_assignment_cert(hash, validator_index); + let assignments = vec![(cert.clone(), 0u32)]; + setup_peer_with_view(overseer, &peer_a, view![hash], ValidationVersion::V3).await; + + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer(overseer, &peer_a, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; + + expect_reputation_change(overseer, &peer_a, COST_ASSIGNMENT_TOO_FAR_IN_THE_FUTURE) + .await; + + virtual_overseer + }, + ); +} + +/// Subsystem rejects bad vrf assignments. +#[test] +fn subsystem_rejects_bad_assignments() { + let peers = make_peers_and_authority_ids(15); + let peer_a = peers.get(0).unwrap().0; + let parent_hash = Hash::repeat_byte(0xFF); + let hash = Hash::repeat_byte(0xAA); + + let _ = test_harness( + Arc::new(MockAssignmentCriteria { + tranche: Err(InvalidAssignment(criteria::InvalidAssignmentReason::NullAssignment)), + }), + Arc::new(DummyClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup peers + setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V1).await; + + // Set up a gossip topology, where a, b, c and d are topology neighbors to the node + // under testing. + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 2, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // send the assignment related to `hash` + let validator_index = ValidatorIndex(0); + let cert = fake_assignment_cert(hash, validator_index); + let assignments = vec![(cert.clone(), 0u32)]; + setup_peer_with_view(overseer, &peer_a, view![hash], ValidationVersion::V3).await; + + let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer(overseer, &peer_a, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; + + expect_reputation_change(overseer, &peer_a, COST_INVALID_MESSAGE).await; + + virtual_overseer + }, + ); +} + +/// Subsystem rejects assignments that have invalid claimed candidates. +#[test] +fn subsystem_rejects_wrong_claimed_assignments() { + let peers = make_peers_and_authority_ids(15); + let peer_a = peers.get(0).unwrap().0; + let parent_hash = Hash::repeat_byte(0xFF); + let hash = Hash::repeat_byte(0xAA); + + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Arc::new(DummyClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup peers + setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V1).await; + + // Set up a gossip topology, where a, b, c and d are topology neighbors to the node + // under testing. + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 2, + candidates: vec![Default::default(); 1], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // send the assignment related to `hash` + let validator_index = ValidatorIndex(0); + + // Claimed core 1 which does not have a candidate included on it, so the assignment + // should be rejected. + let cores = vec![1]; + let core_bitfield: CoreBitfield = cores + .iter() + .map(|index| CoreIndex(*index)) + .collect::>() + .try_into() + .unwrap(); + let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfield); + let assignments: Vec<(IndirectAssignmentCertV2, CandidateBitfield)> = + vec![(cert.clone(), cores.try_into().unwrap())]; + setup_peer_with_view(overseer, &peer_a, view![hash], ValidationVersion::V3).await; + + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer_v3(overseer, &peer_a, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; + + expect_reputation_change(overseer, &peer_a, COST_INVALID_MESSAGE).await; + + virtual_overseer + }, + ); +} + +/// Subsystem accepts tranche0 duplicate assignments, sometimes on validator Compact tranche0 +/// assignment and Delay tranche assignments land on the same candidate. The delay tranche0 can be +/// safely ignored and we don't need to gossip it however, the compact tranche0 assignment should be +/// gossiped, because other candidates are included in it, this test makes sure this invariant is +/// upheld, see https://github.com/paritytech/polkadot/pull/2160#discussion_r557628699, for +/// this edge case. +#[test] +fn subsystem_accepts_tranche0_duplicate_assignments() { + let peers = make_peers_and_authority_ids(15); + let peer_a = peers.get(0).unwrap().0; + let peer_b = peers.get(1).unwrap().0; + let parent_hash = Hash::repeat_byte(0xFF); + let hash = Hash::repeat_byte(0xAA); + let candidate_hash_first = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xBB)); + let candidate_hash_second = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xCC)); + let candidate_hash_third = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xBB)); + let candidate_hash_fourth = polkadot_primitives::CandidateHash(Hash::repeat_byte(0xBB)); + + let _ = test_harness( + Arc::new(MockAssignmentCriteria { tranche: Ok(0) }), + Arc::new(DummyClock {}), + state_without_reputation_delay(), + |mut virtual_overseer| async move { + let overseer = &mut virtual_overseer; + // setup peers + setup_peer_with_view(overseer, &peer_a, view![], ValidationVersion::V3).await; + setup_peer_with_view(overseer, &peer_b, view![], ValidationVersion::V3).await; + + // Set up a gossip topology, where a, b, c and d are topology neighbors to the node + // under testing. + let peers_with_optional_peer_id = peers + .iter() + .map(|(peer_id, authority)| (Some(*peer_id), authority.clone())) + .collect_vec(); + setup_gossip_topology( + overseer, + make_gossip_topology(1, &peers_with_optional_peer_id, &[0, 1], &[2, 4], 3), + ) + .await; + + // new block `hash_a` with 1 candidates + let meta = BlockApprovalMeta { + hash, + parent_hash, + number: 2, + candidates: vec![ + (candidate_hash_first, 0.into(), 0.into()), + (candidate_hash_second, 1.into(), 1.into()), + (candidate_hash_third, 2.into(), 2.into()), + (candidate_hash_fourth, 3.into(), 3.into()), + ], + slot: 1.into(), + session: 1, + vrf_story: RelayVRFStory(Default::default()), + }; + let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]); + overseer_send(overseer, msg).await; + + // send the assignment related to `hash` + let validator_index = ValidatorIndex(0); + + setup_peer_with_view(overseer, &peer_a, view![hash], ValidationVersion::V3).await; + setup_peer_with_view(overseer, &peer_b, view![hash], ValidationVersion::V3).await; + + // 1. Compact assignment with multiple candidates, coming after delay assignment which + // covered just one of the candidate is still imported and gossiped. + let candidate_indices: CandidateBitfield = + vec![1 as CandidateIndex].try_into().unwrap(); + let core_bitfield = vec![CoreIndex(1)].try_into().unwrap(); + let cert = fake_assignment_cert_delay(hash, validator_index, core_bitfield); + let assignments: Vec<(IndirectAssignmentCertV2, CandidateBitfield)> = + vec![(cert.clone(), candidate_indices.clone())]; + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer_v3(overseer, &peer_a, msg).await; + provide_session( + overseer, + dummy_session_info_valid(1, &mut LocalKeystore::in_memory(), 1), + ) + .await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, + _, + )) => { + assert_eq!(assignment.candidate_indices(), &candidate_indices); + assert_eq!(assignment.assignment(), &cert.into()); + assert_eq!(assignment.tranche(), 0); + } + ); + + expect_reputation_change(overseer, &peer_a, BENEFIT_VALID_MESSAGE_FIRST).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 1); + } + ); + + let candidate_indices: CandidateBitfield = + vec![0 as CandidateIndex, 1 as CandidateIndex].try_into().unwrap(); + let core_bitfield = vec![CoreIndex(0), CoreIndex(1)].try_into().unwrap(); + + let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfield); + + let assignments: Vec<(IndirectAssignmentCertV2, CandidateBitfield)> = + vec![(cert.clone(), candidate_indices.clone())]; + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer_v3(overseer, &peer_a, msg).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, + _, + )) => { + assert_eq!(assignment.candidate_indices(), &candidate_indices); + assert_eq!(assignment.assignment(), &cert.into()); + assert_eq!(assignment.tranche(), 0); + } + ); + + expect_reputation_change(overseer, &peer_a, BENEFIT_VALID_MESSAGE_FIRST).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 1); + } + ); + + // 2. Delay assignment coming after compact assignment that already covered the + // candidate is not gossiped anymore. + let candidate_indices: CandidateBitfield = + vec![2 as CandidateIndex, 3 as CandidateIndex].try_into().unwrap(); + let core_bitfield = vec![CoreIndex(2), CoreIndex(3)].try_into().unwrap(); + let cert = fake_assignment_cert_v2(hash, validator_index, core_bitfield); + let assignments: Vec<(IndirectAssignmentCertV2, CandidateBitfield)> = + vec![(cert.clone(), candidate_indices.clone())]; + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer_v3(overseer, &peer_a, msg).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::ApprovalVoting(ApprovalVotingMessage::ImportAssignment( + assignment, + _, + )) => { + assert_eq!(assignment.candidate_indices(), &candidate_indices); + assert_eq!(assignment.assignment(), &cert.into()); + assert_eq!(assignment.tranche(), 0); + } + ); + + expect_reputation_change(overseer, &peer_a, BENEFIT_VALID_MESSAGE_FIRST).await; + + assert_matches!( + overseer_recv(overseer).await, + AllMessages::NetworkBridgeTx(NetworkBridgeTxMessage::SendValidationMessage( + peers, + Versioned::V3(protocol_v3::ValidationProtocol::ApprovalDistribution( + protocol_v3::ApprovalDistributionMessage::Assignments(assignments) + )) + )) => { + assert_eq!(peers.len(), 1); + assert_eq!(assignments.len(), 1); + } + ); + + let candidate_indices: CandidateBitfield = vec![3].try_into().unwrap(); + let core_bitfield = vec![CoreIndex(3)].try_into().unwrap(); + + let cert = fake_assignment_cert_delay(hash, validator_index, core_bitfield); + + let assignments: Vec<(IndirectAssignmentCertV2, CandidateBitfield)> = + vec![(cert.clone(), candidate_indices.clone())]; + let msg = protocol_v3::ApprovalDistributionMessage::Assignments(assignments.clone()); + send_message_from_peer_v3(overseer, &peer_a, msg).await; + + expect_reputation_change(overseer, &peer_a, COST_DUPLICATE_MESSAGE).await; + + virtual_overseer + }, + ); +} diff --git a/polkadot/node/network/availability-distribution/src/lib.rs b/polkadot/node/network/availability-distribution/src/lib.rs index d3185e0af809c3b92df929d265f1962884d3f096..438453814978caa5a19767a536b6cdeff0c05f5a 100644 --- a/polkadot/node/network/availability-distribution/src/lib.rs +++ b/polkadot/node/network/availability-distribution/src/lib.rs @@ -22,11 +22,9 @@ use polkadot_node_network_protocol::request_response::{ v1, v2, IncomingRequestReceiver, ReqProtocolNames, }; use polkadot_node_subsystem::{ - jaeger, messages::AvailabilityDistributionMessage, overseer, FromOrchestra, OverseerSignal, + messages::AvailabilityDistributionMessage, overseer, FromOrchestra, OverseerSignal, SpawnedSubsystem, SubsystemError, }; -use polkadot_primitives::{BlockNumber, Hash}; -use std::collections::HashMap; /// Error and [`Result`] type for this subsystem. mod error; @@ -104,7 +102,6 @@ impl AvailabilityDistributionSubsystem { /// Start processing work as passed on from the Overseer. async fn run(self, mut ctx: Context) -> std::result::Result<(), FatalError> { let Self { mut runtime, recvs, metrics, req_protocol_names } = self; - let mut spans: HashMap = HashMap::new(); let IncomingRequestReceivers { pov_req_receiver, @@ -156,24 +153,16 @@ impl AvailabilityDistributionSubsystem { }; match message { FromOrchestra::Signal(OverseerSignal::ActiveLeaves(update)) => { - let cloned_leaf = match update.activated.clone() { - Some(activated) => activated, - None => continue, - }; - let span = - jaeger::PerLeafSpan::new(cloned_leaf.span, "availability-distribution"); - spans.insert(cloned_leaf.hash, (cloned_leaf.number, span)); log_error( requester .get_mut() - .update_fetching_heads(&mut ctx, &mut runtime, update, &spans) + .update_fetching_heads(&mut ctx, &mut runtime, update) .await, "Error in Requester::update_fetching_heads", &mut warn_freq, )?; }, - FromOrchestra::Signal(OverseerSignal::BlockFinalized(_hash, finalized_number)) => { - spans.retain(|_hash, (block_number, _span)| *block_number > finalized_number); + FromOrchestra::Signal(OverseerSignal::BlockFinalized(_hash, _finalized_number)) => { }, FromOrchestra::Signal(OverseerSignal::Conclude) => return Ok(()), FromOrchestra::Communication { @@ -187,15 +176,6 @@ impl AvailabilityDistributionSubsystem { tx, }, } => { - let span = spans - .get(&relay_parent) - .map(|(_, span)| span.child("fetch-pov")) - .unwrap_or_else(|| jaeger::Span::new(&relay_parent, "fetch-pov")) - .with_trace_id(candidate_hash) - .with_candidate(candidate_hash) - .with_relay_parent(relay_parent) - .with_stage(jaeger::Stage::AvailabilityDistribution); - log_error( pov_requester::fetch_pov( &mut ctx, @@ -207,7 +187,6 @@ impl AvailabilityDistributionSubsystem { pov_hash, tx, metrics.clone(), - &span, ) .await, "pov_requester::fetch_pov", diff --git a/polkadot/node/network/availability-distribution/src/pov_requester/mod.rs b/polkadot/node/network/availability-distribution/src/pov_requester/mod.rs index 6c632fa7efee6ecec2d11f7bb9f42cd8164587f1..5e26ae4b7a7043519638c71c17963f3ac1c44da3 100644 --- a/polkadot/node/network/availability-distribution/src/pov_requester/mod.rs +++ b/polkadot/node/network/availability-distribution/src/pov_requester/mod.rs @@ -25,7 +25,6 @@ use polkadot_node_network_protocol::request_response::{ }; use polkadot_node_primitives::PoV; use polkadot_node_subsystem::{ - jaeger, messages::{IfDisconnected, NetworkBridgeTxMessage}, overseer, }; @@ -52,18 +51,7 @@ pub async fn fetch_pov( pov_hash: Hash, tx: oneshot::Sender, metrics: Metrics, - span: &jaeger::Span, ) -> Result<()> { - let _span = span - .child("fetch-pov") - .with_trace_id(candidate_hash) - .with_validator_index(from_validator) - .with_candidate(candidate_hash) - .with_para_id(para_id) - .with_relay_parent(parent) - .with_string_tag("pov-hash", format!("{:?}", pov_hash)) - .with_stage(jaeger::Stage::AvailabilityDistribution); - let info = &runtime.get_session_info(ctx.sender(), parent).await?.session_info; let authority_id = info .discovery_keys @@ -189,7 +177,6 @@ mod tests { pov_hash, tx, Metrics::new_dummy(), - &jaeger::Span::Disabled, ) .await .expect("Should succeed"); diff --git a/polkadot/node/network/availability-distribution/src/requester/fetch_task/mod.rs b/polkadot/node/network/availability-distribution/src/requester/fetch_task/mod.rs index 278608cc858df5e6f586606c5b66d37147d67e15..5be6f2d223a8e3d1ee0478defea9193f423050de 100644 --- a/polkadot/node/network/availability-distribution/src/requester/fetch_task/mod.rs +++ b/polkadot/node/network/availability-distribution/src/requester/fetch_task/mod.rs @@ -31,7 +31,6 @@ use polkadot_node_network_protocol::request_response::{ }; use polkadot_node_primitives::ErasureChunk; use polkadot_node_subsystem::{ - jaeger, messages::{AvailabilityStoreMessage, IfDisconnected, NetworkBridgeTxMessage}, overseer, }; @@ -129,9 +128,6 @@ struct RunningTask { /// Prometheus metrics for reporting results. metrics: Metrics, - /// Span tracking the fetching of this chunk. - span: jaeger::Span, - /// Expected chunk index. We'll validate that the remote did send us the correct chunk (only /// important for v2 requests). chunk_index: ChunkIndex, @@ -154,21 +150,9 @@ impl FetchTaskConfig { metrics: Metrics, session_info: &SessionInfo, chunk_index: ChunkIndex, - span: jaeger::Span, req_v1_protocol_name: ProtocolName, req_v2_protocol_name: ProtocolName, ) -> Self { - let span = span - .child("fetch-task-config") - .with_trace_id(core.candidate_hash) - .with_string_tag("leaf", format!("{:?}", leaf)) - .with_validator_index(session_info.our_index) - .with_chunk_index(chunk_index) - .with_uint_tag("group-index", core.group_responsible.0 as u64) - .with_relay_parent(core.candidate_descriptor.relay_parent) - .with_string_tag("pov-hash", format!("{:?}", core.candidate_descriptor.pov_hash)) - .with_stage(jaeger::Stage::AvailabilityDistribution); - let live_in = vec![leaf].into_iter().collect(); // Don't run tasks for our backing group: @@ -190,7 +174,6 @@ impl FetchTaskConfig { relay_parent: core.candidate_descriptor.relay_parent, metrics, sender, - span, chunk_index, req_v1_protocol_name, req_v2_protocol_name @@ -279,7 +262,6 @@ impl RunningTask { let mut bad_validators = Vec::new(); let mut succeeded = false; let mut count: u32 = 0; - let mut span = self.span.child("run-fetch-chunk-task").with_relay_parent(self.relay_parent); let mut network_error_freq = gum::Freq::new(); let mut canceled_freq = gum::Freq::new(); // Try validators in reverse order: @@ -289,11 +271,7 @@ impl RunningTask { self.metrics.on_retry(); } count += 1; - let _chunk_fetch_span = span - .child("fetch-chunk-request") - .with_validator_index(self.request.index) - .with_chunk_index(self.chunk_index) - .with_stage(jaeger::Stage::AvailabilityDistribution); + // Send request: let resp = match self .do_request(&validator, &mut network_error_freq, &mut canceled_freq) @@ -313,13 +291,7 @@ impl RunningTask { continue }, }; - // We drop the span here, so that the span is not active while we recombine the chunk. - drop(_chunk_fetch_span); - let _chunk_recombine_span = span - .child("recombine-chunk") - .with_validator_index(self.request.index) - .with_chunk_index(self.chunk_index) - .with_stage(jaeger::Stage::AvailabilityDistribution); + let chunk = match resp { Some(chunk) => chunk, None => { @@ -337,14 +309,6 @@ impl RunningTask { continue }, }; - // We drop the span so that the span is not active whilst we validate and store the - // chunk. - drop(_chunk_recombine_span); - let _chunk_validate_and_store_span = span - .child("validate-and-store-chunk") - .with_validator_index(self.request.index) - .with_chunk_index(self.chunk_index) - .with_stage(jaeger::Stage::AvailabilityDistribution); // Data genuine? if !self.validate_chunk(&validator, &chunk, self.chunk_index) { @@ -357,7 +321,6 @@ impl RunningTask { succeeded = true; break } - span.add_int_tag("tries", count as _); if succeeded { self.metrics.on_fetch(SUCCEEDED); self.conclude(bad_validators).await; diff --git a/polkadot/node/network/availability-distribution/src/requester/fetch_task/tests.rs b/polkadot/node/network/availability-distribution/src/requester/fetch_task/tests.rs index 2cd4bf29a56356dc56dd2538467da0f2078fccaf..9d4ac5bc4b1b961890e7304fa57eda54d57e7464 100644 --- a/polkadot/node/network/availability-distribution/src/requester/fetch_task/tests.rs +++ b/polkadot/node/network/availability-distribution/src/requester/fetch_task/tests.rs @@ -365,7 +365,6 @@ fn get_test_running_task( relay_parent: Hash::repeat_byte(71), sender: tx, metrics: Metrics::new_dummy(), - span: jaeger::Span::Disabled, req_v1_protocol_name: req_protocol_names.get_name(Protocol::ChunkFetchingV1), req_v2_protocol_name: req_protocol_names.get_name(Protocol::ChunkFetchingV2), chunk_index, diff --git a/polkadot/node/network/availability-distribution/src/requester/mod.rs b/polkadot/node/network/availability-distribution/src/requester/mod.rs index 0175161af70dfa706e02bad07ca9fabd4bb1109e..2338250327246a05ab7d9be469c07df8d1625cda 100644 --- a/polkadot/node/network/availability-distribution/src/requester/mod.rs +++ b/polkadot/node/network/availability-distribution/src/requester/mod.rs @@ -31,7 +31,6 @@ use futures::{ use polkadot_node_network_protocol::request_response::{v1, v2, IsRequest, ReqProtocolNames}; use polkadot_node_subsystem::{ - jaeger, messages::{ChainApiMessage, RuntimeApiMessage}, overseer, ActivatedLeaf, ActiveLeavesUpdate, }; @@ -39,9 +38,7 @@ use polkadot_node_subsystem_util::{ availability_chunks::availability_chunk_index, runtime::{get_occupied_cores, RuntimeInfo}, }; -use polkadot_primitives::{ - BlockNumber, CandidateHash, CoreIndex, Hash, OccupiedCore, SessionIndex, -}; +use polkadot_primitives::{CandidateHash, CoreIndex, Hash, OccupiedCore, SessionIndex}; use super::{FatalError, Metrics, Result, LOG_TARGET}; @@ -114,21 +111,13 @@ impl Requester { ctx: &mut Context, runtime: &mut RuntimeInfo, update: ActiveLeavesUpdate, - spans: &HashMap, ) -> Result<()> { gum::trace!(target: LOG_TARGET, ?update, "Update fetching heads"); let ActiveLeavesUpdate { activated, deactivated } = update; if let Some(leaf) = activated { - let span = spans - .get(&leaf.hash) - .map(|(_, span)| span.child("update-fetching-heads")) - .unwrap_or_else(|| jaeger::Span::new(&leaf.hash, "update-fetching-heads")) - .with_string_tag("leaf", format!("{:?}", leaf.hash)) - .with_stage(jaeger::Stage::AvailabilityDistribution); - // Order important! We need to handle activated, prior to deactivated, otherwise we // might cancel still needed jobs. - self.start_requesting_chunks(ctx, runtime, leaf, &span).await?; + self.start_requesting_chunks(ctx, runtime, leaf).await?; } self.stop_requesting_chunks(deactivated.into_iter()); @@ -144,13 +133,7 @@ impl Requester { ctx: &mut Context, runtime: &mut RuntimeInfo, new_head: ActivatedLeaf, - span: &jaeger::Span, ) -> Result<()> { - let mut span = span - .child("request-chunks-new-head") - .with_string_tag("leaf", format!("{:?}", new_head.hash)) - .with_stage(jaeger::Stage::AvailabilityDistribution); - let sender = &mut ctx.sender().clone(); let ActivatedLeaf { hash: leaf, .. } = new_head; let (leaf_session_index, ancestors_in_session) = get_block_ancestors_in_same_session( @@ -160,15 +143,9 @@ impl Requester { Self::LEAF_ANCESTRY_LEN_WITHIN_SESSION, ) .await?; - span.add_uint_tag("ancestors-in-session", ancestors_in_session.len() as u64); // Also spawn or bump tasks for candidates in ancestry in the same session. for hash in std::iter::once(leaf).chain(ancestors_in_session) { - let span = span - .child("request-chunks-ancestor") - .with_string_tag("leaf", format!("{:?}", hash.clone())) - .with_stage(jaeger::Stage::AvailabilityDistribution); - let cores = get_occupied_cores(sender, hash).await?; gum::trace!( target: LOG_TARGET, @@ -182,7 +159,7 @@ impl Requester { // The next time the subsystem receives leaf update, some of spawned task will be bumped // to be live in fresh relay parent, while some might get dropped due to the current // leaf being deactivated. - self.add_cores(ctx, runtime, leaf, leaf_session_index, cores, span).await?; + self.add_cores(ctx, runtime, leaf, leaf_session_index, cores).await?; } Ok(()) @@ -211,22 +188,12 @@ impl Requester { leaf: Hash, leaf_session_index: SessionIndex, cores: impl IntoIterator, - span: jaeger::Span, ) -> Result<()> { for (core_index, core) in cores { - let mut span = span - .child("check-fetch-candidate") - .with_trace_id(core.candidate_hash) - .with_string_tag("leaf", format!("{:?}", leaf)) - .with_candidate(core.candidate_hash) - .with_stage(jaeger::Stage::AvailabilityDistribution); - if let Some(e) = self.fetches.get_mut(&core.candidate_hash) { // Just book keeping - we are already requesting that chunk: - span.add_string_tag("already-requested-chunk", "true"); e.add_leaf(leaf); } else { - span.add_string_tag("already-requested-chunk", "false"); let tx = self.tx.clone(); let metrics = self.metrics.clone(); @@ -272,7 +239,6 @@ impl Requester { metrics, session_info, chunk_index, - span, self.req_protocol_names.get_name(v1::ChunkFetchingRequest::PROTOCOL), self.req_protocol_names.get_name(v2::ChunkFetchingRequest::PROTOCOL), ); diff --git a/polkadot/node/network/availability-distribution/src/requester/tests.rs b/polkadot/node/network/availability-distribution/src/requester/tests.rs index decb3156004ef6dc52332ef6a4998a3e33ed8ed0..021f6da7e2e99e834bec7b2df05741df25e17469 100644 --- a/polkadot/node/network/availability-distribution/src/requester/tests.rs +++ b/polkadot/node/network/availability-distribution/src/requester/tests.rs @@ -15,9 +15,9 @@ // along with Polkadot. If not, see . use futures::FutureExt; -use std::{collections::HashMap, future::Future}; +use std::future::Future; -use polkadot_node_network_protocol::{jaeger, request_response::ReqProtocolNames}; +use polkadot_node_network_protocol::request_response::ReqProtocolNames; use polkadot_node_primitives::{BlockData, ErasureChunk, PoV}; use polkadot_node_subsystem_util::runtime::RuntimeInfo; use polkadot_primitives::{ @@ -208,7 +208,6 @@ fn check_ancestry_lookup_in_same_session() { test_harness(test_state.clone(), |mut ctx| async move { let chain = &test_state.relay_chain; - let spans: HashMap = HashMap::new(); let block_number = 1; let update = ActiveLeavesUpdate { activated: Some(new_leaf(chain[block_number], block_number as u32)), @@ -216,7 +215,7 @@ fn check_ancestry_lookup_in_same_session() { }; requester - .update_fetching_heads(&mut ctx, &mut runtime, update, &spans) + .update_fetching_heads(&mut ctx, &mut runtime, update) .await .expect("Leaf processing failed"); let fetch_tasks = &requester.fetches; @@ -231,7 +230,7 @@ fn check_ancestry_lookup_in_same_session() { }; requester - .update_fetching_heads(&mut ctx, &mut runtime, update, &spans) + .update_fetching_heads(&mut ctx, &mut runtime, update) .await .expect("Leaf processing failed"); let fetch_tasks = &requester.fetches; @@ -252,7 +251,7 @@ fn check_ancestry_lookup_in_same_session() { deactivated: vec![chain[1], chain[2]].into(), }; requester - .update_fetching_heads(&mut ctx, &mut runtime, update, &spans) + .update_fetching_heads(&mut ctx, &mut runtime, update) .await .expect("Leaf processing failed"); let fetch_tasks = &requester.fetches; @@ -281,7 +280,6 @@ fn check_ancestry_lookup_in_different_sessions() { test_harness(test_state.clone(), |mut ctx| async move { let chain = &test_state.relay_chain; - let spans: HashMap = HashMap::new(); let block_number = 3; let update = ActiveLeavesUpdate { activated: Some(new_leaf(chain[block_number], block_number as u32)), @@ -289,7 +287,7 @@ fn check_ancestry_lookup_in_different_sessions() { }; requester - .update_fetching_heads(&mut ctx, &mut runtime, update, &spans) + .update_fetching_heads(&mut ctx, &mut runtime, update) .await .expect("Leaf processing failed"); let fetch_tasks = &requester.fetches; @@ -302,7 +300,7 @@ fn check_ancestry_lookup_in_different_sessions() { }; requester - .update_fetching_heads(&mut ctx, &mut runtime, update, &spans) + .update_fetching_heads(&mut ctx, &mut runtime, update) .await .expect("Leaf processing failed"); let fetch_tasks = &requester.fetches; @@ -315,7 +313,7 @@ fn check_ancestry_lookup_in_different_sessions() { }; requester - .update_fetching_heads(&mut ctx, &mut runtime, update, &spans) + .update_fetching_heads(&mut ctx, &mut runtime, update) .await .expect("Leaf processing failed"); let fetch_tasks = &requester.fetches; diff --git a/polkadot/node/network/availability-distribution/src/responder.rs b/polkadot/node/network/availability-distribution/src/responder.rs index fb08c47125038edffff6e5d7cd57d986ed5fe138..6512fcb7f656ac4ba273df0956160bce62e05c50 100644 --- a/polkadot/node/network/availability-distribution/src/responder.rs +++ b/polkadot/node/network/availability-distribution/src/responder.rs @@ -27,7 +27,7 @@ use polkadot_node_network_protocol::{ UnifiedReputationChange as Rep, }; use polkadot_node_primitives::{AvailableData, ErasureChunk}; -use polkadot_node_subsystem::{jaeger, messages::AvailabilityStoreMessage, SubsystemSender}; +use polkadot_node_subsystem::{messages::AvailabilityStoreMessage, SubsystemSender}; use polkadot_primitives::{CandidateHash, ValidatorIndex}; use crate::{ @@ -193,8 +193,6 @@ pub async fn answer_pov_request( where Sender: SubsystemSender, { - let _span = jaeger::Span::new(req.payload.candidate_hash, "answer-pov-request"); - let av_data = query_available_data(sender, req.payload.candidate_hash).await?; let result = av_data.is_some(); @@ -228,12 +226,6 @@ where // V1 and V2 requests have the same payload, so decoding into either one will work. It's the // responses that differ, hence the `MakeResp` generic. let payload: v1::ChunkFetchingRequest = req.payload.into(); - let span = jaeger::Span::new(payload.candidate_hash, "answer-chunk-request"); - - let _child_span = span - .child("answer-chunk-request") - .with_trace_id(payload.candidate_hash) - .with_validator_index(payload.index); let chunk = query_chunk(sender, payload.candidate_hash, payload.index).await?; diff --git a/polkadot/node/network/availability-distribution/src/tests/mod.rs b/polkadot/node/network/availability-distribution/src/tests/mod.rs index 3320871bceb5ad152c3dbd3483a86babbcf8994d..078220607c37fb3cab981e1356f620ea34445b1e 100644 --- a/polkadot/node/network/availability-distribution/src/tests/mod.rs +++ b/polkadot/node/network/availability-distribution/src/tests/mod.rs @@ -45,7 +45,7 @@ fn test_harness>( let (context, virtual_overseer) = polkadot_node_subsystem_test_helpers::make_subsystem_context(pool.clone()); - let (pov_req_receiver, pov_req_cfg) = IncomingRequest::get_config_receiver::< + let (pov_req_receiver, _pov_req_cfg) = IncomingRequest::get_config_receiver::< Block, sc_network::NetworkWorker, >(&req_protocol_names); @@ -65,13 +65,8 @@ fn test_harness>( ); let subsystem = subsystem.run(context); - let test_fut = test_fx(TestHarness { - virtual_overseer, - pov_req_cfg, - chunk_req_v1_cfg, - chunk_req_v2_cfg, - pool, - }); + let test_fut = + test_fx(TestHarness { virtual_overseer, chunk_req_v1_cfg, chunk_req_v2_cfg, pool }); futures::pin_mut!(test_fut); futures::pin_mut!(subsystem); diff --git a/polkadot/node/network/availability-distribution/src/tests/state.rs b/polkadot/node/network/availability-distribution/src/tests/state.rs index 97e616f79fb759c0800946b762aca03acba9c4bc..53d6fd2c530fab2c05a317b4fec38d23a854d00d 100644 --- a/polkadot/node/network/availability-distribution/src/tests/state.rs +++ b/polkadot/node/network/availability-distribution/src/tests/state.rs @@ -60,7 +60,6 @@ type VirtualOverseer = polkadot_node_subsystem_test_helpers::TestSubsystemContex >; pub struct TestHarness { pub virtual_overseer: VirtualOverseer, - pub pov_req_cfg: RequestResponseConfig, pub chunk_req_v1_cfg: RequestResponseConfig, pub chunk_req_v2_cfg: RequestResponseConfig, pub pool: TaskExecutor, diff --git a/polkadot/node/network/availability-recovery/src/lib.rs b/polkadot/node/network/availability-recovery/src/lib.rs index 167125f987ab8f9af4f1d8b91ebd33d80bd05039..114faa2859c4c9c5947414aa49cc0025a2433605 100644 --- a/polkadot/node/network/availability-recovery/src/lib.rs +++ b/polkadot/node/network/availability-recovery/src/lib.rs @@ -57,7 +57,6 @@ use polkadot_node_network_protocol::{ use polkadot_node_primitives::AvailableData; use polkadot_node_subsystem::{ errors::RecoveryError, - jaeger, messages::{AvailabilityRecoveryMessage, AvailabilityStoreMessage}, overseer, ActiveLeavesUpdate, FromOrchestra, OverseerSignal, SpawnedSubsystem, SubsystemContext, SubsystemError, @@ -387,9 +386,6 @@ async fn handle_recover( ) -> Result<()> { let candidate_hash = receipt.hash(); - let span = jaeger::Span::new(candidate_hash, "availability-recovery") - .with_stage(jaeger::Stage::AvailabilityRecovery); - if let Some(result) = state.availability_lru.get(&candidate_hash).cloned().map(|v| v.into_result()) { @@ -403,13 +399,11 @@ async fn handle_recover( return Ok(()) } - let _span = span.child("not-cached"); let session_info_res = state .runtime_info .get_session_info_by_index(ctx.sender(), state.live_block.1, session_index) .await; - let _span = span.child("session-info-ctx-received"); match session_info_res { Ok(ExtendedSessionInfo { session_info, node_features, .. }) => { let mut backer_group = None; diff --git a/polkadot/node/network/availability-recovery/src/task/strategy/chunks.rs b/polkadot/node/network/availability-recovery/src/task/strategy/chunks.rs index b6376a5b543ed75d511571ec48d2fee8cac6a7d4..6b34538b62662fa5c562d9d78e6cc5c18fabd2f7 100644 --- a/polkadot/node/network/availability-recovery/src/task/strategy/chunks.rs +++ b/polkadot/node/network/availability-recovery/src/task/strategy/chunks.rs @@ -107,9 +107,10 @@ impl FetchChunks { state: &mut State, common_params: &RecoveryParams, ) -> Result { - let recovery_duration = common_params - .metrics - .time_erasure_recovery(RecoveryStrategy::::strategy_type(self)); + let recovery_duration = + common_params + .metrics + .time_erasure_recovery(RecoveryStrategy::::strategy_type(self)); // Send request to reconstruct available data from chunks. let (avilable_data_tx, available_data_rx) = oneshot::channel(); @@ -136,18 +137,16 @@ impl FetchChunks { // Attempt post-recovery check. Ok(data) => do_post_recovery_check(common_params, data) .await - .map_err(|e| { + .inspect_err(|_| { recovery_duration.map(|rd| rd.stop_and_discard()); - e }) - .map(|data| { + .inspect(|_| { gum::trace!( target: LOG_TARGET, candidate_hash = ?common_params.candidate_hash, erasure_root = ?common_params.erasure_root, "Data recovery from chunks complete", ); - data }), Err(err) => { recovery_duration.map(|rd| rd.stop_and_discard()); diff --git a/polkadot/node/network/availability-recovery/src/task/strategy/systematic.rs b/polkadot/node/network/availability-recovery/src/task/strategy/systematic.rs index 677bc2d1375aa75900123c39f1fbe59c8902ea69..8b8cff549912eeedfb3b682502d25e0b930f5788 100644 --- a/polkadot/node/network/availability-recovery/src/task/strategy/systematic.rs +++ b/polkadot/node/network/availability-recovery/src/task/strategy/systematic.rs @@ -125,18 +125,16 @@ impl FetchSystematicChunks { // Attempt post-recovery check. do_post_recovery_check(common_params, data) .await - .map_err(|e| { + .inspect_err(|_| { recovery_duration.map(|rd| rd.stop_and_discard()); - e }) - .map(|data| { + .inspect(|_| { gum::trace!( target: LOG_TARGET, candidate_hash = ?common_params.candidate_hash, erasure_root = ?common_params.erasure_root, "Data recovery from systematic chunks complete", ); - data }) }, Err(err) => { diff --git a/polkadot/node/network/bitfield-distribution/src/lib.rs b/polkadot/node/network/bitfield-distribution/src/lib.rs index 029401e0bd51478ad7144e2790c6abe3db52ce3c..3003f970a64180f657a6745de048f325d1a2b746 100644 --- a/polkadot/node/network/bitfield-distribution/src/lib.rs +++ b/polkadot/node/network/bitfield-distribution/src/lib.rs @@ -36,8 +36,8 @@ use polkadot_node_network_protocol::{ UnifiedReputationChange as Rep, Versioned, View, }; use polkadot_node_subsystem::{ - jaeger, messages::*, overseer, ActiveLeavesUpdate, FromOrchestra, OverseerSignal, PerLeafSpan, - SpawnedSubsystem, SubsystemError, SubsystemResult, + messages::*, overseer, ActiveLeavesUpdate, FromOrchestra, OverseerSignal, SpawnedSubsystem, + SubsystemError, SubsystemResult, }; use polkadot_node_subsystem_util::{ self as util, @@ -177,22 +177,14 @@ struct PerRelayParentData { /// Track messages that were already received by a peer /// to prevent flooding. message_received_from_peer: HashMap>, - - /// The span for this leaf/relay parent. - span: PerLeafSpan, } impl PerRelayParentData { /// Create a new instance. - fn new( - signing_context: SigningContext, - validator_set: Vec, - span: PerLeafSpan, - ) -> Self { + fn new(signing_context: SigningContext, validator_set: Vec) -> Self { Self { signing_context, validator_set, - span, one_per_validator: Default::default(), message_sent_to_peer: Default::default(), message_received_from_peer: Default::default(), @@ -304,8 +296,6 @@ impl BitfieldDistribution { let relay_parent = activated.hash; gum::trace!(target: LOG_TARGET, ?relay_parent, "activated"); - let span = PerLeafSpan::new(activated.span, "bitfield-distribution"); - let _span = span.child("query-basics"); // query validator set and signing context per relay_parent once only match query_basics(&mut ctx, relay_parent).await { @@ -317,7 +307,7 @@ impl BitfieldDistribution { // us anything to do with this relay-parent anyway. let _ = state.per_relay_parent.insert( relay_parent, - PerRelayParentData::new(signing_context, validator_set, span), + PerRelayParentData::new(signing_context, validator_set), ); }, Err(err) => { @@ -430,9 +420,7 @@ async fn relay_message( rng: &mut (impl CryptoRng + Rng), ) { let relay_parent = message.relay_parent; - let span = job_data.span.child("relay-msg"); - let _span = span.child("provisionable"); // notify the overseer about a new and valid signed bitfield ctx.send_message(ProvisionerMessage::ProvisionableData( relay_parent, @@ -440,11 +428,9 @@ async fn relay_message( )) .await; - drop(_span); let total_peers = peers.len(); let mut random_routing: RandomRouting = Default::default(); - let _span = span.child("interested-peers"); // pass on the bitfield distribution to all interested peers let interested_peers = peers .iter() @@ -487,8 +473,6 @@ async fn relay_message( .insert(validator.clone()); }); - drop(_span); - if interested_peers.is_empty() { gum::trace!( target: LOG_TARGET, @@ -496,8 +480,6 @@ async fn relay_message( "no peers are interested in gossip for relay parent", ); } else { - let _span = span.child("gossip"); - let v1_interested_peers = filter_by_peer_version(&interested_peers, ValidationVersion::V1.into()); let v2_interested_peers = @@ -594,14 +576,6 @@ async fn process_incoming_peer_message( let validator_index = bitfield.unchecked_validator_index(); - let mut _span = job_data - .span - .child("msg-received") - .with_peer_id(&origin) - .with_relay_parent(relay_parent) - .with_claimed_validator_index(validator_index) - .with_stage(jaeger::Stage::BitfieldDistribution); - let validator_set = &job_data.validator_set; if validator_set.is_empty() { gum::trace!(target: LOG_TARGET, ?relay_parent, ?origin, "Validator set is empty",); @@ -914,7 +888,6 @@ async fn send_tracked_gossip_message( return }; - let _span = job_data.span.child("gossip"); gum::trace!( target: LOG_TARGET, ?dest, diff --git a/polkadot/node/network/bitfield-distribution/src/tests.rs b/polkadot/node/network/bitfield-distribution/src/tests.rs index 4ed4bf6b38c50ca837456db70881f32cf4b43f5f..66a3c3f70909b8d3e0c1ed516c0ff01130de8474 100644 --- a/polkadot/node/network/bitfield-distribution/src/tests.rs +++ b/polkadot/node/network/bitfield-distribution/src/tests.rs @@ -25,11 +25,7 @@ use polkadot_node_network_protocol::{ peer_set::ValidationVersion, view, ObservedRole, }; -use polkadot_node_subsystem::{ - jaeger, - jaeger::{PerLeafSpan, Span}, - messages::ReportPeerMessage, -}; +use polkadot_node_subsystem::messages::ReportPeerMessage; use polkadot_node_subsystem_test_helpers::make_subsystem_context; use polkadot_node_subsystem_util::TimeoutExt; use polkadot_primitives::{AvailabilityBitfield, Signed, ValidatorIndex}; @@ -86,7 +82,6 @@ fn prewarmed_state( }, message_received_from_peer: hashmap!{}, message_sent_to_peer: hashmap!{}, - span: PerLeafSpan::new(Arc::new(jaeger::Span::Disabled), "test"), }, }, peer_data: peers @@ -124,7 +119,6 @@ fn state_with_view( one_per_validator: hashmap! {}, message_received_from_peer: hashmap! {}, message_sent_to_peer: hashmap! {}, - span: PerLeafSpan::new(Arc::new(jaeger::Span::Disabled), "test"), }, ) }) @@ -1024,11 +1018,7 @@ fn need_message_works() { let validator_set = Vec::from_iter(validators.iter().map(|k| ValidatorId::from(k.public()))); let signing_context = SigningContext { session_index: 1, parent_hash: Hash::repeat_byte(0x00) }; - let mut state = PerRelayParentData::new( - signing_context, - validator_set.clone(), - PerLeafSpan::new(Arc::new(Span::Disabled), "foo"), - ); + let mut state = PerRelayParentData::new(signing_context, validator_set.clone()); let peer_a = PeerId::random(); let peer_b = PeerId::random(); diff --git a/polkadot/node/network/bridge/src/rx/mod.rs b/polkadot/node/network/bridge/src/rx/mod.rs index 7745c42f78a173b2c835f4b575171bd25146341e..bb99536f78334d024d8fe60974a7303fa783a39a 100644 --- a/polkadot/node/network/bridge/src/rx/mod.rs +++ b/polkadot/node/network/bridge/src/rx/mod.rs @@ -45,8 +45,9 @@ use polkadot_node_subsystem::{ errors::SubsystemError, messages::{ network_bridge_event::NewGossipTopology, ApprovalDistributionMessage, - BitfieldDistributionMessage, CollatorProtocolMessage, GossipSupportMessage, - NetworkBridgeEvent, NetworkBridgeRxMessage, StatementDistributionMessage, + ApprovalVotingParallelMessage, BitfieldDistributionMessage, CollatorProtocolMessage, + GossipSupportMessage, NetworkBridgeEvent, NetworkBridgeRxMessage, + StatementDistributionMessage, }, overseer, ActivatedLeaf, ActiveLeavesUpdate, FromOrchestra, OverseerSignal, SpawnedSubsystem, }; @@ -89,6 +90,7 @@ pub struct NetworkBridgeRx { validation_service: Box, collation_service: Box, notification_sinks: Arc>>>, + approval_voting_parallel_enabled: bool, } impl NetworkBridgeRx { @@ -105,6 +107,7 @@ impl NetworkBridgeRx { peerset_protocol_names: PeerSetProtocolNames, mut notification_services: HashMap>, notification_sinks: Arc>>>, + approval_voting_parallel_enabled: bool, ) -> Self { let shared = Shared::default(); @@ -125,6 +128,7 @@ impl NetworkBridgeRx { validation_service, collation_service, notification_sinks, + approval_voting_parallel_enabled, } } } @@ -156,6 +160,7 @@ async fn handle_validation_message( peerset_protocol_names: &PeerSetProtocolNames, notification_service: &mut Box, notification_sinks: &mut Arc>>>, + approval_voting_parallel_enabled: bool, ) where AD: validator_discovery::AuthorityDiscovery + Send, { @@ -276,6 +281,7 @@ async fn handle_validation_message( ], sender, &metrics, + approval_voting_parallel_enabled, ) .await; @@ -329,6 +335,7 @@ async fn handle_validation_message( NetworkBridgeEvent::PeerDisconnected(peer), sender, &metrics, + approval_voting_parallel_enabled, ) .await; } @@ -398,7 +405,13 @@ async fn handle_validation_message( network_service.report_peer(peer, report.into()); } - dispatch_validation_events_to_all(events, sender, &metrics).await; + dispatch_validation_events_to_all( + events, + sender, + &metrics, + approval_voting_parallel_enabled, + ) + .await; }, } } @@ -652,6 +665,7 @@ async fn handle_network_messages( mut validation_service: Box, mut collation_service: Box, mut notification_sinks: Arc>>>, + approval_voting_parallel_enabled: bool, ) -> Result<(), Error> where AD: validator_discovery::AuthorityDiscovery + Send, @@ -669,6 +683,7 @@ where &peerset_protocol_names, &mut validation_service, &mut notification_sinks, + approval_voting_parallel_enabled, ).await, None => return Err(Error::EventStreamConcluded), }, @@ -727,6 +742,7 @@ async fn run_incoming_orchestra_signals( sync_oracle: Box, metrics: Metrics, notification_sinks: Arc>>>, + approval_voting_parallel_enabled: bool, ) -> Result<(), Error> where AD: validator_discovery::AuthorityDiscovery + Clone, @@ -766,6 +782,7 @@ where local_index, }), ctx.sender(), + approval_voting_parallel_enabled, ); }, FromOrchestra::Communication { @@ -787,6 +804,7 @@ where dispatch_validation_event_to_all_unbounded( NetworkBridgeEvent::UpdatedAuthorityIds(peer_id, authority_ids), ctx.sender(), + approval_voting_parallel_enabled, ); }, FromOrchestra::Signal(OverseerSignal::Conclude) => return Ok(()), @@ -826,6 +844,7 @@ where finalized_number, &metrics, ¬ification_sinks, + approval_voting_parallel_enabled, ); note_peers_count(&metrics, &shared); } @@ -875,6 +894,7 @@ where validation_service, collation_service, notification_sinks, + approval_voting_parallel_enabled, } = bridge; let (task, network_event_handler) = handle_network_messages( @@ -887,6 +907,7 @@ where validation_service, collation_service, notification_sinks.clone(), + approval_voting_parallel_enabled, ) .remote_handle(); @@ -900,6 +921,7 @@ where sync_oracle, metrics, notification_sinks, + approval_voting_parallel_enabled, ); futures::pin_mut!(orchestra_signal_handler); @@ -926,6 +948,7 @@ fn update_our_view( finalized_number: BlockNumber, metrics: &Metrics, notification_sinks: &Arc>>>, + approval_voting_parallel_enabled: bool, ) { let new_view = construct_view(live_heads.iter().map(|v| v.hash), finalized_number); @@ -963,13 +986,14 @@ fn update_our_view( }; let our_view = OurView::new( - live_heads.iter().take(MAX_VIEW_HEADS).cloned().map(|a| (a.hash, a.span)), + live_heads.iter().take(MAX_VIEW_HEADS).cloned().map(|a| a.hash), finalized_number, ); dispatch_validation_event_to_all_unbounded( NetworkBridgeEvent::OurViewChange(our_view.clone()), ctx.sender(), + approval_voting_parallel_enabled, ); dispatch_collation_event_to_all_unbounded( @@ -1081,8 +1105,15 @@ async fn dispatch_validation_event_to_all( event: NetworkBridgeEvent, ctx: &mut impl overseer::NetworkBridgeRxSenderTrait, metrics: &Metrics, + approval_voting_parallel_enabled: bool, ) { - dispatch_validation_events_to_all(std::iter::once(event), ctx, metrics).await + dispatch_validation_events_to_all( + std::iter::once(event), + ctx, + metrics, + approval_voting_parallel_enabled, + ) + .await } async fn dispatch_collation_event_to_all( @@ -1095,6 +1126,7 @@ async fn dispatch_collation_event_to_all( fn dispatch_validation_event_to_all_unbounded( event: NetworkBridgeEvent, sender: &mut impl overseer::NetworkBridgeRxSenderTrait, + approval_voting_parallel_enabled: bool, ) { event .focus() @@ -1106,11 +1138,20 @@ fn dispatch_validation_event_to_all_unbounded( .ok() .map(BitfieldDistributionMessage::from) .and_then(|msg| Some(sender.send_unbounded_message(msg))); - event - .focus() - .ok() - .map(ApprovalDistributionMessage::from) - .and_then(|msg| Some(sender.send_unbounded_message(msg))); + + if approval_voting_parallel_enabled { + event + .focus() + .ok() + .map(ApprovalVotingParallelMessage::from) + .and_then(|msg| Some(sender.send_unbounded_message(msg))); + } else { + event + .focus() + .ok() + .map(ApprovalDistributionMessage::from) + .and_then(|msg| Some(sender.send_unbounded_message(msg))); + } event .focus() .ok() @@ -1131,6 +1172,7 @@ async fn dispatch_validation_events_to_all( events: I, sender: &mut impl overseer::NetworkBridgeRxSenderTrait, _metrics: &Metrics, + approval_voting_parallel_enabled: bool, ) where I: IntoIterator>, I::IntoIter: Send, @@ -1160,7 +1202,11 @@ async fn dispatch_validation_events_to_all( for event in events { send_message!(event, StatementDistributionMessage); send_message!(event, BitfieldDistributionMessage); - send_message!(event, ApprovalDistributionMessage); + if approval_voting_parallel_enabled { + send_message!(event, ApprovalVotingParallelMessage); + } else { + send_message!(event, ApprovalDistributionMessage); + } send_message!(event, GossipSupportMessage); } } diff --git a/polkadot/node/network/bridge/src/rx/tests.rs b/polkadot/node/network/bridge/src/rx/tests.rs index 601dca5cb8a3c4db817d2c4681058392b9f7686f..e3f2715ef2b07fc2738b244a1e92cf672bf5d2a0 100644 --- a/polkadot/node/network/bridge/src/rx/tests.rs +++ b/polkadot/node/network/bridge/src/rx/tests.rs @@ -16,7 +16,6 @@ use super::*; use futures::{channel::oneshot, executor}; -use overseer::jaeger; use polkadot_node_network_protocol::{self as net_protocol, OurView}; use polkadot_node_subsystem::messages::NetworkBridgeEvent; @@ -529,6 +528,7 @@ fn test_harness>( validation_service, collation_service, notification_sinks, + approval_voting_parallel_enabled: false, }; let network_bridge = run_network_in(bridge, context) @@ -1380,12 +1380,7 @@ fn our_view_updates_decreasing_order_and_limited_to_max() { } let our_views = (1..=MAX_VIEW_HEADS).rev().map(|start| { - OurView::new( - (start..=MAX_VIEW_HEADS) - .rev() - .map(|i| (Hash::repeat_byte(i as u8), Arc::new(jaeger::Span::Disabled))), - 0, - ) + OurView::new((start..=MAX_VIEW_HEADS).rev().map(|i| Hash::repeat_byte(i as u8)), 0) }); for our_view in our_views { diff --git a/polkadot/node/network/collator-protocol/Cargo.toml b/polkadot/node/network/collator-protocol/Cargo.toml index 8a7c384dcbe8b72acb895a253d0b4022b66fc147..304cb23bb6aae5cc06e034b1ee6acb849196077b 100644 --- a/polkadot/node/network/collator-protocol/Cargo.toml +++ b/polkadot/node/network/collator-protocol/Cargo.toml @@ -14,6 +14,7 @@ bitvec = { features = ["alloc"], workspace = true } futures = { workspace = true } futures-timer = { workspace = true } gum = { workspace = true, default-features = true } +schnellru.workspace = true sp-core = { workspace = true, default-features = true } sp-runtime = { workspace = true, default-features = true } diff --git a/polkadot/node/network/collator-protocol/src/collator_side/mod.rs b/polkadot/node/network/collator-protocol/src/collator_side/mod.rs index 401525d02f1150382d85e98c7e0996ef1d7ce235..af9beb535f46abfc323efd7acde4323a4e0ddd3d 100644 --- a/polkadot/node/network/collator-protocol/src/collator_side/mod.rs +++ b/polkadot/node/network/collator-protocol/src/collator_side/mod.rs @@ -23,6 +23,7 @@ use bitvec::{bitvec, vec::BitVec}; use futures::{ channel::oneshot, future::Fuse, pin_mut, select, stream::FuturesUnordered, FutureExt, StreamExt, }; +use schnellru::{ByLength, LruMap}; use sp_core::Pair; use polkadot_node_network_protocol::{ @@ -37,12 +38,11 @@ use polkadot_node_network_protocol::{ }; use polkadot_node_primitives::{CollationSecondedSignal, PoV, Statement}; use polkadot_node_subsystem::{ - jaeger, messages::{ CollatorProtocolMessage, NetworkBridgeEvent, NetworkBridgeTxMessage, ParentHeadData, RuntimeApiMessage, }, - overseer, CollatorProtocolSenderTrait, FromOrchestra, OverseerSignal, PerLeafSpan, + overseer, FromOrchestra, OverseerSignal, }; use polkadot_node_subsystem_util::{ backing_implicit_view::View as ImplicitView, @@ -200,6 +200,11 @@ struct PeerData { view: View, /// Network protocol version. version: CollationVersion, + /// Unknown heads in the view. + /// + /// This can happen when the validator is faster at importing a block and sending out its + /// `View` than the collator is able to import a block. + unknown_heads: LruMap, } /// A type wrapping a collation and it's designated core index. @@ -278,9 +283,6 @@ struct State { /// our view, including both leaves and implicit ancestry. per_relay_parent: HashMap, - /// Span per relay parent. - span_per_relay_parent: HashMap, - /// The result senders per collation. collation_result_senders: HashMap>, @@ -339,7 +341,6 @@ impl State { implicit_view: None, active_leaves: Default::default(), per_relay_parent: Default::default(), - span_per_relay_parent: Default::default(), collation_result_senders: Default::default(), peer_ids: Default::default(), validator_groups_buf: ValidatorGroupsBuffer::with_capacity(VALIDATORS_BUFFER_CAPACITY), @@ -848,12 +849,6 @@ async fn process_msg( result_sender, core_index, } => { - let _span1 = state - .span_per_relay_parent - .get(&candidate_receipt.descriptor.relay_parent) - .map(|s| s.child("distributing-collation")); - let _span2 = jaeger::Span::new(&pov, "distributing-collation"); - match state.collating_on { Some(id) if candidate_receipt.descriptor.para_id != id => { // If the ParaId of a collation requested to be distributed does not match @@ -1082,11 +1077,6 @@ async fn handle_incoming_request( let peer_id = req.peer_id(); let para_id = req.para_id(); - let _span = state - .span_per_relay_parent - .get(&relay_parent) - .map(|s| s.child("request-collation")); - match state.collating_on { Some(our_para_id) if our_para_id == para_id => { let per_relay_parent = match state.per_relay_parent.get_mut(&relay_parent) { @@ -1141,8 +1131,6 @@ async fn handle_incoming_request( state.metrics.on_collation_sent_requested(); - let _span = _span.as_ref().map(|s| s.child("sending")); - let waiting = state.waiting_collation_fetches.entry(relay_parent).or_default(); let candidate_hash = receipt.hash(); @@ -1198,9 +1186,10 @@ async fn handle_peer_view_change( peer_id: PeerId, view: View, ) { - let PeerData { view: current, version } = match state.peer_data.get_mut(&peer_id) { - Some(peer_data) => peer_data, - None => return, + let Some(PeerData { view: current, version, unknown_heads }) = + state.peer_data.get_mut(&peer_id) + else { + return }; let added: Vec = view.difference(&*current).cloned().collect(); @@ -1228,15 +1217,18 @@ async fn handle_peer_view_change( new_leaf = ?added, "New leaf in peer's view is unknown", ); + + unknown_heads.insert(added, ()); + continue }, }; for block_hash in block_hashes { - let per_relay_parent = match state.per_relay_parent.get_mut(block_hash) { - Some(per_relay_parent) => per_relay_parent, - None => continue, + let Some(per_relay_parent) = state.per_relay_parent.get_mut(block_hash) else { + continue }; + advertise_collation( ctx, *block_hash, @@ -1282,10 +1274,13 @@ async fn handle_network_msg( return Ok(()) }, }; - state - .peer_data - .entry(peer_id) - .or_insert_with(|| PeerData { view: View::default(), version }); + state.peer_data.entry(peer_id).or_insert_with(|| PeerData { + view: View::default(), + version, + // Unlikely that the collator is falling 10 blocks behind and if so, it probably is + // not able to keep up any way. + unknown_heads: LruMap::new(ByLength::new(10)), + }); if let Some(authority_ids) = maybe_authority { gum::trace!( @@ -1310,7 +1305,7 @@ async fn handle_network_msg( }, OurViewChange(view) => { gum::trace!(target: LOG_TARGET, ?view, "Own view change"); - handle_our_view_change(ctx.sender(), state, view).await?; + handle_our_view_change(ctx, state, view).await?; }, PeerMessage(remote, msg) => { handle_incoming_peer_message(ctx, runtime, state, remote, msg).await?; @@ -1332,26 +1327,19 @@ async fn handle_network_msg( } /// Handles our view changes. -async fn handle_our_view_change( - sender: &mut Sender, +#[overseer::contextbounds(CollatorProtocol, prefix = crate::overseer)] +async fn handle_our_view_change( + ctx: &mut Context, state: &mut State, view: OurView, -) -> Result<()> -where - Sender: CollatorProtocolSenderTrait, -{ +) -> Result<()> { let current_leaves = state.active_leaves.clone(); let removed = current_leaves.iter().filter(|(h, _)| !view.contains(h)); let added = view.iter().filter(|h| !current_leaves.contains_key(h)); for leaf in added { - let mode = prospective_parachains_mode(sender, *leaf).await?; - - if let Some(span) = view.span_per_head().get(leaf).cloned() { - let per_leaf_span = PerLeafSpan::new(span, "collator-side"); - state.span_per_relay_parent.insert(*leaf, per_leaf_span); - } + let mode = prospective_parachains_mode(ctx.sender(), *leaf).await?; state.active_leaves.insert(*leaf, mode); state.per_relay_parent.insert(*leaf, PerRelayParent::new(mode)); @@ -1359,7 +1347,7 @@ where if mode.is_enabled() { if let Some(ref mut implicit_view) = state.implicit_view { implicit_view - .activate_leaf(sender, *leaf) + .activate_leaf(ctx.sender(), *leaf) .await .map_err(Error::ImplicitViewFetchError)?; @@ -1367,11 +1355,36 @@ where .known_allowed_relay_parents_under(leaf, state.collating_on) .unwrap_or_default(); + // Get the peers that already reported us this head, but we didn't knew it at this + // point. + let peers = state + .peer_data + .iter_mut() + .filter_map(|(id, data)| { + data.unknown_heads.remove(leaf).map(|_| (id, data.version)) + }) + .collect::>(); + for block_hash in allowed_ancestry { - state + let per_relay_parent = state .per_relay_parent .entry(*block_hash) .or_insert_with(|| PerRelayParent::new(mode)); + + // Announce relevant collations to these peers. + for (peer_id, peer_version) in &peers { + advertise_collation( + ctx, + *block_hash, + per_relay_parent, + &peer_id, + *peer_version, + &state.peer_ids, + &mut state.advertisement_timeouts, + &state.metrics, + ) + .await; + } } } } @@ -1428,7 +1441,6 @@ where ), } } - state.span_per_relay_parent.remove(removed); state.waiting_collation_fetches.remove(removed); } } diff --git a/polkadot/node/network/collator-protocol/src/collator_side/tests/mod.rs b/polkadot/node/network/collator-protocol/src/collator_side/tests/mod.rs index 74a151c168dcab581d66e4f7be3530d703e0fdd9..23954f8d781bdfa7c78a0ddd75898aff1d893bd3 100644 --- a/polkadot/node/network/collator-protocol/src/collator_side/tests/mod.rs +++ b/polkadot/node/network/collator-protocol/src/collator_side/tests/mod.rs @@ -18,7 +18,6 @@ use super::*; use std::{ collections::{BTreeMap, HashSet, VecDeque}, - sync::Arc, time::Duration, }; @@ -42,7 +41,6 @@ use polkadot_node_network_protocol::{ use polkadot_node_primitives::BlockData; use polkadot_node_subsystem::{ errors::RuntimeApiError, - jaeger, messages::{AllMessages, ReportPeerMessage, RuntimeApiMessage, RuntimeApiRequest}, ActiveLeavesUpdate, }; @@ -299,21 +297,33 @@ async fn overseer_send(overseer: &mut VirtualOverseer, msg: CollatorProtocolMess } async fn overseer_recv(overseer: &mut VirtualOverseer) -> AllMessages { - let msg = overseer_recv_with_timeout(overseer, TIMEOUT) + overseer_recv_with_timeout(overseer, TIMEOUT) .await - .expect(&format!("{:?} is more than enough to receive messages", TIMEOUT)); + .expect(&format!("{:?} is more than enough to receive messages", TIMEOUT)) +} + +async fn overseer_recv_with_timeout( + overseer: &mut VirtualOverseer, + timeout: Duration, +) -> Option { + gum::trace!("waiting for message..."); + let msg = overseer.recv().timeout(timeout).await; gum::trace!(?msg, "received message"); msg } -async fn overseer_recv_with_timeout( +async fn overseer_peek_with_timeout( overseer: &mut VirtualOverseer, timeout: Duration, -) -> Option { - gum::trace!("waiting for message..."); - overseer.recv().timeout(timeout).await +) -> Option<&AllMessages> { + gum::trace!("peeking for message..."); + let msg = overseer.peek().timeout(timeout).await; + + gum::trace!(?msg, "received message"); + + msg.flatten() } async fn overseer_signal(overseer: &mut VirtualOverseer, signal: OverseerSignal) { @@ -603,7 +613,7 @@ async fn expect_declare_msg( /// Expects v2 message if `expected_candidate_hashes` is `Some`, v1 otherwise. async fn expect_advertise_collation_msg( virtual_overseer: &mut VirtualOverseer, - peer: &PeerId, + any_peers: &[PeerId], expected_relay_parent: Hash, expected_candidate_hashes: Option>, ) { @@ -620,7 +630,7 @@ async fn expect_advertise_collation_msg( wire_message, ) ) => { - assert_eq!(to[0], *peer); + assert!(any_peers.iter().any(|p| to.contains(p))); match (candidate_hashes.as_mut(), wire_message) { (None, Versioned::V1(protocol_v1::CollationProtocol::CollatorProtocol(wire_message))) => { assert_matches!( @@ -739,7 +749,7 @@ fn advertise_and_send_collation() { // advertise it. expect_advertise_collation_msg( &mut virtual_overseer, - &peer, + &[peer], test_state.relay_parent, None, ) @@ -849,7 +859,7 @@ fn advertise_and_send_collation() { expect_advertise_collation_msg( &mut virtual_overseer, - &peer, + &[peer], test_state.relay_parent, None, ) @@ -910,7 +920,7 @@ fn delay_reputation_change() { // advertise it. expect_advertise_collation_msg( &mut virtual_overseer, - &peer, + &[peer], test_state.relay_parent, None, ) @@ -1031,7 +1041,7 @@ fn advertise_collation_v2_protocol() { // Versioned advertisements work. expect_advertise_collation_msg( virtual_overseer, - &peer_ids[0], + &[peer_ids[0]], test_state.relay_parent, None, ) @@ -1039,7 +1049,7 @@ fn advertise_collation_v2_protocol() { for peer_id in peer_ids.iter().skip(1) { expect_advertise_collation_msg( virtual_overseer, - peer_id, + &[*peer_id], test_state.relay_parent, Some(vec![candidate.hash()]), // This is `Some`, advertisement is v2. ) @@ -1142,15 +1152,25 @@ fn collations_are_only_advertised_to_validators_with_correct_view() { distribute_collation(virtual_overseer, &test_state, test_state.relay_parent, true) .await; - expect_advertise_collation_msg(virtual_overseer, &peer2, test_state.relay_parent, None) - .await; + expect_advertise_collation_msg( + virtual_overseer, + &[peer2], + test_state.relay_parent, + None, + ) + .await; // The other validator announces that it changed its view. send_peer_view_change(virtual_overseer, &peer, vec![test_state.relay_parent]).await; // After changing the view we should receive the advertisement - expect_advertise_collation_msg(virtual_overseer, &peer, test_state.relay_parent, None) - .await; + expect_advertise_collation_msg( + virtual_overseer, + &[peer], + test_state.relay_parent, + None, + ) + .await; test_harness }, ) @@ -1199,12 +1219,17 @@ fn collate_on_two_different_relay_chain_blocks() { .await; send_peer_view_change(virtual_overseer, &peer, vec![old_relay_parent]).await; - expect_advertise_collation_msg(virtual_overseer, &peer, old_relay_parent, None).await; + expect_advertise_collation_msg(virtual_overseer, &[peer], old_relay_parent, None).await; send_peer_view_change(virtual_overseer, &peer2, vec![test_state.relay_parent]).await; - expect_advertise_collation_msg(virtual_overseer, &peer2, test_state.relay_parent, None) - .await; + expect_advertise_collation_msg( + virtual_overseer, + &[peer2], + test_state.relay_parent, + None, + ) + .await; test_harness }, ) @@ -1237,8 +1262,13 @@ fn validator_reconnect_does_not_advertise_a_second_time() { .await; send_peer_view_change(virtual_overseer, &peer, vec![test_state.relay_parent]).await; - expect_advertise_collation_msg(virtual_overseer, &peer, test_state.relay_parent, None) - .await; + expect_advertise_collation_msg( + virtual_overseer, + &[peer], + test_state.relay_parent, + None, + ) + .await; // Disconnect and reconnect directly disconnect_peer(virtual_overseer, peer).await; @@ -1361,14 +1391,14 @@ where // advertise it. expect_advertise_collation_msg( virtual_overseer, - &validator_0, + &[validator_0], test_state.relay_parent, None, ) .await; expect_advertise_collation_msg( virtual_overseer, - &validator_1, + &[validator_1], test_state.relay_parent, None, ) @@ -1498,9 +1528,10 @@ fn connect_to_buffered_groups() { } // Update views. - for peed_id in &peers_a { - send_peer_view_change(&mut virtual_overseer, peed_id, vec![head_a]).await; - expect_advertise_collation_msg(&mut virtual_overseer, peed_id, head_a, None).await; + for peer_id in &peers_a { + send_peer_view_change(&mut virtual_overseer, peer_id, vec![head_a]).await; + expect_advertise_collation_msg(&mut virtual_overseer, &[*peer_id], head_a, None) + .await; } let peer = peers_a[0]; diff --git a/polkadot/node/network/collator-protocol/src/collator_side/tests/prospective_parachains.rs b/polkadot/node/network/collator-protocol/src/collator_side/tests/prospective_parachains.rs index ea8fdb0e04fbe0ba29303b62ae1d45c63d43cdf4..348feb9dd1dbc01641556b25c813e4f4c205c870 100644 --- a/polkadot/node/network/collator-protocol/src/collator_side/tests/prospective_parachains.rs +++ b/polkadot/node/network/collator-protocol/src/collator_side/tests/prospective_parachains.rs @@ -36,8 +36,7 @@ async fn update_view( ) { let new_view: HashMap = HashMap::from_iter(new_view); - let our_view = - OurView::new(new_view.keys().map(|hash| (*hash, Arc::new(jaeger::Span::Disabled))), 0); + let our_view = OurView::new(new_view.keys().map(|hash| *hash), 0); overseer_send( virtual_overseer, @@ -45,7 +44,6 @@ async fn update_view( ) .await; - let mut next_overseer_message = None; for _ in 0..activated { let (leaf_hash, leaf_number) = assert_matches!( overseer_recv(virtual_overseer).await, @@ -147,18 +145,10 @@ async fn update_view( let parent_hash = ancestry_iter.peek().map(|(h, _)| *h).unwrap_or_else(|| get_parent_hash(hash)); - let msg = match next_overseer_message.take() { - Some(msg) => Some(msg), - None => - overseer_recv_with_timeout(virtual_overseer, Duration::from_millis(50)).await, - }; - - let msg = match msg { - Some(msg) => msg, - None => { - // We're done. - return - }, + let Some(msg) = + overseer_peek_with_timeout(virtual_overseer, Duration::from_millis(50)).await + else { + return }; if !matches!( @@ -167,12 +157,11 @@ async fn update_view( if *_hash == hash ) { // Ancestry has already been cached for this leaf. - next_overseer_message.replace(msg); break } assert_matches!( - msg, + overseer_recv_with_timeout(virtual_overseer, Duration::from_millis(50)).await.unwrap(), AllMessages::ChainApi(ChainApiMessage::BlockHeader(.., tx)) => { let header = Header { parent_hash, @@ -238,124 +227,156 @@ fn distribute_collation_from_implicit_view() { let head_c = Hash::from_low_u64_be(130); let head_c_num = 62; - let group_rotation_info = GroupRotationInfo { - session_start_block: head_c_num - 2, - group_rotation_frequency: 3, - now: head_c_num, - }; + // Run once with validators sending their view first and then the collator setting their own + // view first. + for validator_sends_view_first in [true, false] { + let group_rotation_info = GroupRotationInfo { + session_start_block: head_c_num - 2, + group_rotation_frequency: 3, + now: head_c_num, + }; + + let mut test_state = TestState::default(); + test_state.group_rotation_info = group_rotation_info; + + let local_peer_id = test_state.local_peer_id; + let collator_pair = test_state.collator_pair.clone(); + + test_harness( + local_peer_id, + collator_pair, + ReputationAggregator::new(|_| true), + |mut test_harness| async move { + let virtual_overseer = &mut test_harness.virtual_overseer; + + // Set collating para id. + overseer_send( + virtual_overseer, + CollatorProtocolMessage::CollateOn(test_state.para_id), + ) + .await; - let mut test_state = TestState::default(); - test_state.group_rotation_info = group_rotation_info; + if validator_sends_view_first { + // Activate leaf `c` to accept at least the collation. + update_view(virtual_overseer, vec![(head_c, head_c_num)], 1).await; + } else { + // Activated leaf is `b`, but the collation will be based on `c`. + update_view(virtual_overseer, vec![(head_b, head_b_num)], 1).await; + } - let local_peer_id = test_state.local_peer_id; - let collator_pair = test_state.collator_pair.clone(); + let validator_peer_ids = test_state.current_group_validator_peer_ids(); + for (val, peer) in test_state + .current_group_validator_authority_ids() + .into_iter() + .zip(validator_peer_ids.clone()) + { + connect_peer(virtual_overseer, peer, CollationVersion::V2, Some(val.clone())) + .await; + } - test_harness( - local_peer_id, - collator_pair, - ReputationAggregator::new(|_| true), - |mut test_harness| async move { - let virtual_overseer = &mut test_harness.virtual_overseer; + // Collator declared itself to each peer. + for peer_id in &validator_peer_ids { + expect_declare_msg_v2(virtual_overseer, &test_state, peer_id).await; + } - // Set collating para id. - overseer_send(virtual_overseer, CollatorProtocolMessage::CollateOn(test_state.para_id)) - .await; - // Activated leaf is `b`, but the collation will be based on `c`. - update_view(virtual_overseer, vec![(head_b, head_b_num)], 1).await; - - let validator_peer_ids = test_state.current_group_validator_peer_ids(); - for (val, peer) in test_state - .current_group_validator_authority_ids() - .into_iter() - .zip(validator_peer_ids.clone()) - { - connect_peer(virtual_overseer, peer, CollationVersion::V2, Some(val.clone())).await; - } + let pov = PoV { block_data: BlockData(vec![1, 2, 3]) }; + let parent_head_data_hash = Hash::repeat_byte(0xAA); + let candidate = TestCandidateBuilder { + para_id: test_state.para_id, + relay_parent: head_c, + pov_hash: pov.hash(), + ..Default::default() + } + .build(); + let DistributeCollation { candidate, pov_block: _ } = + distribute_collation_with_receipt( + virtual_overseer, + &test_state, + head_c, + false, // Check the group manually. + candidate, + pov, + parent_head_data_hash, + ) + .await; + assert_matches!( + overseer_recv(virtual_overseer).await, + AllMessages::NetworkBridgeTx( + NetworkBridgeTxMessage::ConnectToValidators { validator_ids, .. } + ) => { + let expected_validators = test_state.current_group_validator_authority_ids(); - // Collator declared itself to each peer. - for peer_id in &validator_peer_ids { - expect_declare_msg_v2(virtual_overseer, &test_state, peer_id).await; - } + assert_eq!(expected_validators, validator_ids); + } + ); - let pov = PoV { block_data: BlockData(vec![1, 2, 3]) }; - let parent_head_data_hash = Hash::repeat_byte(0xAA); - let candidate = TestCandidateBuilder { - para_id: test_state.para_id, - relay_parent: head_c, - pov_hash: pov.hash(), - ..Default::default() - } - .build(); - let DistributeCollation { candidate, pov_block: _ } = - distribute_collation_with_receipt( - virtual_overseer, - &test_state, - head_c, - false, // Check the group manually. - candidate, - pov, - parent_head_data_hash, - ) - .await; - assert_matches!( - overseer_recv(virtual_overseer).await, - AllMessages::NetworkBridgeTx( - NetworkBridgeTxMessage::ConnectToValidators { validator_ids, .. } - ) => { - let expected_validators = test_state.current_group_validator_authority_ids(); + let candidate_hash = candidate.hash(); + + // Update peer views. + for peer_id in &validator_peer_ids { + send_peer_view_change(virtual_overseer, peer_id, vec![head_b]).await; - assert_eq!(expected_validators, validator_ids); + if !validator_sends_view_first { + expect_advertise_collation_msg( + virtual_overseer, + &[*peer_id], + head_c, + Some(vec![candidate_hash]), + ) + .await; + } } - ); - let candidate_hash = candidate.hash(); + if validator_sends_view_first { + // Activated leaf is `b`, but the collation will be based on `c`. + update_view(virtual_overseer, vec![(head_b, head_b_num)], 1).await; + + for _ in &validator_peer_ids { + expect_advertise_collation_msg( + virtual_overseer, + &validator_peer_ids, + head_c, + Some(vec![candidate_hash]), + ) + .await; + } + } - // Update peer views. - for peed_id in &validator_peer_ids { - send_peer_view_change(virtual_overseer, peed_id, vec![head_b]).await; - expect_advertise_collation_msg( + // Head `c` goes out of view. + // Build a different candidate for this relay parent and attempt to distribute it. + update_view(virtual_overseer, vec![(head_a, head_a_num)], 1).await; + + let pov = PoV { block_data: BlockData(vec![4, 5, 6]) }; + let parent_head_data_hash = Hash::repeat_byte(0xBB); + let candidate = TestCandidateBuilder { + para_id: test_state.para_id, + relay_parent: head_c, + pov_hash: pov.hash(), + ..Default::default() + } + .build(); + overseer_send( virtual_overseer, - peed_id, - head_c, - Some(vec![candidate_hash]), + CollatorProtocolMessage::DistributeCollation { + candidate_receipt: candidate.clone(), + parent_head_data_hash, + pov: pov.clone(), + parent_head_data: HeadData(vec![1, 2, 3]), + result_sender: None, + core_index: CoreIndex(0), + }, ) .await; - } - - // Head `c` goes out of view. - // Build a different candidate for this relay parent and attempt to distribute it. - update_view(virtual_overseer, vec![(head_a, head_a_num)], 1).await; - - let pov = PoV { block_data: BlockData(vec![4, 5, 6]) }; - let parent_head_data_hash = Hash::repeat_byte(0xBB); - let candidate = TestCandidateBuilder { - para_id: test_state.para_id, - relay_parent: head_c, - pov_hash: pov.hash(), - ..Default::default() - } - .build(); - overseer_send( - virtual_overseer, - CollatorProtocolMessage::DistributeCollation { - candidate_receipt: candidate.clone(), - parent_head_data_hash, - pov: pov.clone(), - parent_head_data: HeadData(vec![1, 2, 3]), - result_sender: None, - core_index: CoreIndex(0), - }, - ) - .await; - // Parent out of view, nothing happens. - assert!(overseer_recv_with_timeout(virtual_overseer, Duration::from_millis(100)) - .await - .is_none()); + // Parent out of view, nothing happens. + assert!(overseer_recv_with_timeout(virtual_overseer, Duration::from_millis(100)) + .await + .is_none()); - test_harness - }, - ) + test_harness + }, + ); + } } /// Tests that collator can distribute up to `MAX_CANDIDATE_DEPTH + 1` candidates @@ -505,7 +526,7 @@ fn send_parent_head_data_for_elastic_scaling() { send_peer_view_change(&mut virtual_overseer, &peer, vec![head_b]).await; let hashes: Vec<_> = vec![candidate.hash()]; - expect_advertise_collation_msg(&mut virtual_overseer, &peer, head_b, Some(hashes)) + expect_advertise_collation_msg(&mut virtual_overseer, &[peer], head_b, Some(hashes)) .await; let (pending_response, rx) = oneshot::channel(); @@ -625,7 +646,7 @@ fn advertise_and_send_collation_by_hash() { // Head `b` is not a leaf, but both advertisements are still relevant. send_peer_view_change(&mut virtual_overseer, &peer, vec![head_b]).await; let hashes: Vec<_> = candidates.iter().map(|(candidate, _)| candidate.hash()).collect(); - expect_advertise_collation_msg(&mut virtual_overseer, &peer, head_b, Some(hashes)) + expect_advertise_collation_msg(&mut virtual_overseer, &[peer], head_b, Some(hashes)) .await; for (candidate, pov_block) in candidates { diff --git a/polkadot/node/network/collator-protocol/src/validator_side/collation.rs b/polkadot/node/network/collator-protocol/src/validator_side/collation.rs index 96ffe9f13db3562429eb68cdfdc05da801c33863..58d9ebc577260ec692e11a622d3c350f08285e58 100644 --- a/polkadot/node/network/collator-protocol/src/validator_side/collation.rs +++ b/polkadot/node/network/collator-protocol/src/validator_side/collation.rs @@ -36,7 +36,6 @@ use polkadot_node_network_protocol::{ PeerId, }; use polkadot_node_primitives::PoV; -use polkadot_node_subsystem::jaeger; use polkadot_node_subsystem_util::{ metrics::prometheus::prometheus::HistogramTimer, runtime::ProspectiveParachainsMode, }; @@ -319,8 +318,6 @@ pub(super) struct CollationFetchRequest { pub from_collator: BoxFuture<'static, OutgoingResult>, /// Handle used for checking if this request was cancelled. pub cancellation_token: CancellationToken, - /// A jaeger span corresponding to the lifetime of the request. - pub span: Option, /// A metric histogram for the lifetime of the request pub _lifetime_timer: Option, } @@ -339,7 +336,6 @@ impl Future for CollationFetchRequest { }; if cancelled { - self.span.as_mut().map(|s| s.add_string_tag("success", "false")); return Poll::Ready(( CollationEvent { collator_protocol_version: self.collator_protocol_version, @@ -361,16 +357,6 @@ impl Future for CollationFetchRequest { ) }); - match &res { - Poll::Ready((_, Ok(_))) => { - self.span.as_mut().map(|s| s.add_string_tag("success", "true")); - }, - Poll::Ready((_, Err(_))) => { - self.span.as_mut().map(|s| s.add_string_tag("success", "false")); - }, - _ => {}, - }; - res } } 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 cbf00a9e119d95af0c254df31babf67652d9da25..deb6ce03f43ebdcfefd698853f2b44264e36dbcc 100644 --- a/polkadot/node/network/collator-protocol/src/validator_side/mod.rs +++ b/polkadot/node/network/collator-protocol/src/validator_side/mod.rs @@ -39,13 +39,12 @@ use polkadot_node_network_protocol::{ }; use polkadot_node_primitives::{SignedFullStatement, Statement}; use polkadot_node_subsystem::{ - jaeger, messages::{ CanSecondRequest, CandidateBackingMessage, CollatorProtocolMessage, IfDisconnected, NetworkBridgeEvent, NetworkBridgeTxMessage, ParentHeadData, ProspectiveParachainsMessage, ProspectiveValidationDataRequest, }, - overseer, CollatorProtocolSenderTrait, FromOrchestra, OverseerSignal, PerLeafSpan, + overseer, CollatorProtocolSenderTrait, FromOrchestra, OverseerSignal, }; use polkadot_node_subsystem_util::{ backing_implicit_view::View as ImplicitView, @@ -420,9 +419,6 @@ struct State { /// Metrics. metrics: Metrics, - /// Span per relay parent. - span_per_relay_parent: HashMap, - /// When a timer in this `FuturesUnordered` triggers, we should dequeue the next request /// attempt in the corresponding `collations_per_relay_parent`. /// @@ -723,10 +719,6 @@ async fn request_collation( collator_protocol_version: peer_protocol_version, from_collator: response_recv, cancellation_token: cancellation_token.clone(), - span: state - .span_per_relay_parent - .get(&relay_parent) - .map(|s| s.child("collation-request").with_para_id(para_id)), _lifetime_timer: state.metrics.time_collation_request_duration(), }; @@ -1066,11 +1058,6 @@ async fn handle_advertisement( where Sender: CollatorProtocolSenderTrait, { - let _span = state - .span_per_relay_parent - .get(&relay_parent) - .map(|s| s.child("advertise-collation")); - let peer_data = state.peer_data.get_mut(&peer_id).ok_or(AdvertisementError::UnknownPeer)?; if peer_data.version == CollationVersion::V1 && !state.active_leaves.contains_key(&relay_parent) @@ -1264,11 +1251,6 @@ where for leaf in added { let mode = prospective_parachains_mode(sender, *leaf).await?; - if let Some(span) = view.span_per_head().get(leaf).cloned() { - let per_leaf_span = PerLeafSpan::new(span, "validator-side"); - state.span_per_relay_parent.insert(*leaf, per_leaf_span); - } - let mut per_relay_parent = PerRelayParent::new(mode); assign_incoming( sender, @@ -1338,7 +1320,6 @@ where keep }); state.fetched_candidates.retain(|k, _| k.relay_parent != removed); - state.span_per_relay_parent.remove(&removed); } } @@ -1983,10 +1964,6 @@ async fn handle_collation_fetch_response( Ok(resp) => Ok(resp), }; - let _span = state - .span_per_relay_parent - .get(&pending_collation.relay_parent) - .map(|s| s.child("received-collation")); let _timer = state.metrics.time_handle_collation_request_result(); let mut metrics_result = Err(()); @@ -2067,7 +2044,6 @@ async fn handle_collation_fetch_response( candidate_hash = ?candidate_receipt.hash(), "Received collation", ); - let _span = jaeger::Span::new(&pov, "received-collation"); metrics_result = Ok(()); Ok(PendingCollationFetch { @@ -2093,7 +2069,6 @@ async fn handle_collation_fetch_response( candidate_hash = ?receipt.hash(), "Received collation (v3)", ); - let _span = jaeger::Span::new(&pov, "received-collation"); metrics_result = Ok(()); Ok(PendingCollationFetch { diff --git a/polkadot/node/network/collator-protocol/src/validator_side/tests/prospective_parachains.rs b/polkadot/node/network/collator-protocol/src/validator_side/tests/prospective_parachains.rs index 472731b506ab1c5d59a756a9e80b47016bcceb6c..dff98e22e3db49bd104d9fc59ea1b7e36705b9a0 100644 --- a/polkadot/node/network/collator-protocol/src/validator_side/tests/prospective_parachains.rs +++ b/polkadot/node/network/collator-protocol/src/validator_side/tests/prospective_parachains.rs @@ -103,8 +103,7 @@ pub(super) async fn update_view( ) -> Option { let new_view: HashMap = HashMap::from_iter(new_view); - let our_view = - OurView::new(new_view.keys().map(|hash| (*hash, Arc::new(jaeger::Span::Disabled))), 0); + let our_view = OurView::new(new_view.keys().map(|hash| *hash), 0); overseer_send( virtual_overseer, diff --git a/polkadot/node/network/dispute-distribution/Cargo.toml b/polkadot/node/network/dispute-distribution/Cargo.toml index ccf1b5daad7c3b7f3dd7d5525cb619781c01bce8..b4dcafe09eb60f9991af206d7c826d559f32d00a 100644 --- a/polkadot/node/network/dispute-distribution/Cargo.toml +++ b/polkadot/node/network/dispute-distribution/Cargo.toml @@ -38,5 +38,4 @@ sp-tracing = { workspace = true, default-features = true } sc-keystore = { workspace = true, default-features = true } futures-timer = { workspace = true } assert_matches = { workspace = true } -lazy_static = { workspace = true } polkadot-primitives-test-helpers = { workspace = true } diff --git a/polkadot/node/network/dispute-distribution/src/tests/mock.rs b/polkadot/node/network/dispute-distribution/src/tests/mock.rs index ccc050233e8408d09e8cbe6261a44e682939d4b9..baa857e2eb6878a5342ef4f01b5d2aa8e3c8e777 100644 --- a/polkadot/node/network/dispute-distribution/src/tests/mock.rs +++ b/polkadot/node/network/dispute-distribution/src/tests/mock.rs @@ -19,12 +19,11 @@ use std::{ collections::{HashMap, HashSet}, - sync::Arc, + sync::{Arc, LazyLock}, time::Instant, }; use async_trait::async_trait; -use lazy_static::lazy_static; use polkadot_node_network_protocol::{authority_discovery::AuthorityDiscovery, PeerId}; use sc_keystore::LocalKeystore; @@ -60,64 +59,60 @@ pub const ALICE_INDEX: ValidatorIndex = ValidatorIndex(1); pub const BOB_INDEX: ValidatorIndex = ValidatorIndex(2); pub const CHARLIE_INDEX: ValidatorIndex = ValidatorIndex(3); -lazy_static! { - /// Mocked `AuthorityDiscovery` service. -pub static ref MOCK_AUTHORITY_DISCOVERY: MockAuthorityDiscovery = MockAuthorityDiscovery::new(); +pub static MOCK_AUTHORITY_DISCOVERY: LazyLock = + LazyLock::new(|| MockAuthorityDiscovery::new()); // Creating an innocent looking `SessionInfo` is really expensive in a debug build. Around // 700ms on my machine, We therefore cache those keys here: -pub static ref MOCK_VALIDATORS_DISCOVERY_KEYS: HashMap = - MOCK_VALIDATORS - .iter() - .chain(MOCK_AUTHORITIES_NEXT_SESSION.iter()) - .map(|v| (*v, v.public().into())) - .collect() -; -pub static ref FERDIE_DISCOVERY_KEY: AuthorityDiscoveryId = - MOCK_VALIDATORS_DISCOVERY_KEYS.get(&Sr25519Keyring::Ferdie).unwrap().clone(); - -pub static ref MOCK_SESSION_INFO: SessionInfo = - SessionInfo { - validators: MOCK_VALIDATORS.iter().take(4).map(|k| k.public().into()).collect(), - discovery_keys: MOCK_VALIDATORS +pub static MOCK_VALIDATORS_DISCOVERY_KEYS: LazyLock> = + LazyLock::new(|| { + MOCK_VALIDATORS .iter() - .map(|k| MOCK_VALIDATORS_DISCOVERY_KEYS.get(&k).unwrap().clone()) - .collect(), - assignment_keys: vec![], - validator_groups: Default::default(), - n_cores: 0, - 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], - }; + .chain(MOCK_AUTHORITIES_NEXT_SESSION.iter()) + .map(|v| (*v, v.public().into())) + .collect() + }); +pub static FERDIE_DISCOVERY_KEY: LazyLock = + LazyLock::new(|| MOCK_VALIDATORS_DISCOVERY_KEYS.get(&Sr25519Keyring::Ferdie).unwrap().clone()); + +pub static MOCK_SESSION_INFO: LazyLock = LazyLock::new(|| SessionInfo { + validators: MOCK_VALIDATORS.iter().take(4).map(|k| k.public().into()).collect(), + discovery_keys: MOCK_VALIDATORS + .iter() + .map(|k| MOCK_VALIDATORS_DISCOVERY_KEYS.get(&k).unwrap().clone()) + .collect(), + assignment_keys: vec![], + validator_groups: Default::default(), + n_cores: 0, + 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], +}); /// `SessionInfo` for the second session. (No more validators, but two more authorities. -pub static ref MOCK_NEXT_SESSION_INFO: SessionInfo = - SessionInfo { - discovery_keys: - MOCK_AUTHORITIES_NEXT_SESSION - .iter() - .map(|k| MOCK_VALIDATORS_DISCOVERY_KEYS.get(&k).unwrap().clone()) - .collect(), - validators: Default::default(), - assignment_keys: vec![], - validator_groups: Default::default(), - n_cores: 0, - 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], - }; -} +pub static MOCK_NEXT_SESSION_INFO: LazyLock = LazyLock::new(|| SessionInfo { + discovery_keys: MOCK_AUTHORITIES_NEXT_SESSION + .iter() + .map(|k| MOCK_VALIDATORS_DISCOVERY_KEYS.get(&k).unwrap().clone()) + .collect(), + validators: Default::default(), + assignment_keys: vec![], + validator_groups: Default::default(), + n_cores: 0, + 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], +}); pub fn make_candidate_receipt(relay_parent: Hash) -> CandidateReceipt { CandidateReceipt { diff --git a/polkadot/node/network/gossip-support/Cargo.toml b/polkadot/node/network/gossip-support/Cargo.toml index 83fdc7e26191e6b2568a9733b163a0f0239f6709..c8c19e5de070fdb981a484f78d5349161fd6d71c 100644 --- a/polkadot/node/network/gossip-support/Cargo.toml +++ b/polkadot/node/network/gossip-support/Cargo.toml @@ -39,5 +39,4 @@ polkadot-node-subsystem-test-helpers = { workspace = true } assert_matches = { workspace = true } async-trait = { workspace = true } parking_lot = { workspace = true, default-features = true } -lazy_static = { workspace = true } quickcheck = { workspace = true, default-features = true } diff --git a/polkadot/node/network/gossip-support/src/tests.rs b/polkadot/node/network/gossip-support/src/tests.rs index 09622254f523e5605f8b203645819b2ea176dcfd..399f29db67da8c029cab8b151eff220e03398b68 100644 --- a/polkadot/node/network/gossip-support/src/tests.rs +++ b/polkadot/node/network/gossip-support/src/tests.rs @@ -16,12 +16,11 @@ //! Unit tests for Gossip Support Subsystem. -use std::{collections::HashSet, time::Duration}; +use std::{collections::HashSet, sync::LazyLock, time::Duration}; use assert_matches::assert_matches; use async_trait::async_trait; use futures::{executor, future, Future}; -use lazy_static::lazy_static; use quickcheck::quickcheck; use rand::seq::SliceRandom as _; @@ -56,39 +55,29 @@ const AUTHORITY_KEYRINGS: &[Sr25519Keyring] = &[ Sr25519Keyring::Ferdie, ]; -lazy_static! { - static ref AUTHORITIES: Vec = - AUTHORITY_KEYRINGS.iter().map(|k| k.public().into()).collect(); +static AUTHORITIES: LazyLock> = + LazyLock::new(|| AUTHORITY_KEYRINGS.iter().map(|k| k.public().into()).collect()); - static ref AUTHORITIES_WITHOUT_US: Vec = { - let mut a = AUTHORITIES.clone(); - a.pop(); // remove FERDIE. - a - }; - - static ref PAST_PRESENT_FUTURE_AUTHORITIES: Vec = { - (0..50) - .map(|_| AuthorityDiscoveryPair::generate().0.public()) - .chain(AUTHORITIES.clone()) - .collect() - }; +static AUTHORITIES_WITHOUT_US: LazyLock> = LazyLock::new(|| { + let mut a = AUTHORITIES.clone(); + a.pop(); // remove FERDIE. + a +}); - // [2 6] - // [4 5] - // [1 3] - // [0 ] +static PAST_PRESENT_FUTURE_AUTHORITIES: LazyLock> = LazyLock::new(|| { + (0..50) + .map(|_| AuthorityDiscoveryPair::generate().0.public()) + .chain(AUTHORITIES.clone()) + .collect() +}); - static ref EXPECTED_SHUFFLING: Vec = vec![6, 4, 0, 5, 2, 3, 1]; +static EXPECTED_SHUFFLING: LazyLock> = LazyLock::new(|| vec![6, 4, 0, 5, 2, 3, 1]); - static ref ROW_NEIGHBORS: Vec = vec![ - ValidatorIndex::from(2), - ]; +static ROW_NEIGHBORS: LazyLock> = + LazyLock::new(|| vec![ValidatorIndex::from(2)]); - static ref COLUMN_NEIGHBORS: Vec = vec![ - ValidatorIndex::from(3), - ValidatorIndex::from(5), - ]; -} +static COLUMN_NEIGHBORS: LazyLock> = + LazyLock::new(|| vec![ValidatorIndex::from(3), ValidatorIndex::from(5)]); type VirtualOverseer = polkadot_node_subsystem_test_helpers::TestSubsystemContextHandle; diff --git a/polkadot/node/network/protocol/Cargo.toml b/polkadot/node/network/protocol/Cargo.toml index c9ae23d756cfc4d39d44286b7a567ed46969bf02..3d51d3c0a5659ca168a39e29f537d1028a0dcdbe 100644 --- a/polkadot/node/network/protocol/Cargo.toml +++ b/polkadot/node/network/protocol/Cargo.toml @@ -15,7 +15,6 @@ async-trait = { workspace = true } hex = { workspace = true, default-features = true } polkadot-primitives = { workspace = true, default-features = true } polkadot-node-primitives = { workspace = true, default-features = true } -polkadot-node-jaeger = { workspace = true, default-features = true } codec = { features = ["derive"], workspace = true } sc-network = { workspace = true, default-features = true } sc-network-types = { workspace = true, default-features = true } diff --git a/polkadot/node/network/protocol/src/lib.rs b/polkadot/node/network/protocol/src/lib.rs index ca0f8a4e48493691d7c1cb43579e9420c8a88274..f4f1b715b926870cd4eed7c2dee25f13e9d2ab7f 100644 --- a/polkadot/node/network/protocol/src/lib.rs +++ b/polkadot/node/network/protocol/src/lib.rs @@ -21,10 +21,9 @@ use codec::{Decode, Encode}; use polkadot_primitives::{BlockNumber, Hash}; -use std::{collections::HashMap, fmt}; +use std::fmt; #[doc(hidden)] -pub use polkadot_node_jaeger as jaeger; pub use sc_network::IfDisconnected; pub use sc_network_types::PeerId; #[doc(hidden)] @@ -91,31 +90,16 @@ impl Into for ObservedRole { } /// Specialized wrapper around [`View`]. -/// -/// Besides the access to the view itself, it also gives access to the [`jaeger::Span`] per -/// leave/head. #[derive(Debug, Clone, Default)] pub struct OurView { view: View, - span_per_head: HashMap>, } impl OurView { /// Creates a new instance. - pub fn new( - heads: impl IntoIterator)>, - finalized_number: BlockNumber, - ) -> Self { - let state_per_head = heads.into_iter().collect::>(); - let view = View::new(state_per_head.keys().cloned(), finalized_number); - Self { view, span_per_head: state_per_head } - } - - /// Returns the span per head map. - /// - /// For each head there exists one span in this map. - pub fn span_per_head(&self) -> &HashMap> { - &self.span_per_head + pub fn new(heads: impl IntoIterator, finalized_number: BlockNumber) -> Self { + let view = View::new(heads, finalized_number); + Self { view } } } @@ -133,8 +117,7 @@ impl std::ops::Deref for OurView { } } -/// Construct a new [`OurView`] with the given chain heads, finalized number 0 and disabled -/// [`jaeger::Span`]'s. +/// Construct a new [`OurView`] with the given chain heads, finalized number 0 /// /// NOTE: Use for tests only. /// @@ -149,7 +132,7 @@ impl std::ops::Deref for OurView { macro_rules! our_view { ( $( $hash:expr ),* $(,)? ) => { $crate::OurView::new( - vec![ $( $hash.clone() ),* ].into_iter().map(|h| (h, $crate::Arc::new($crate::jaeger::Span::Disabled))), + vec![ $( $hash.clone() ),* ].into_iter().map(|h| h), 0, ) }; diff --git a/polkadot/node/network/protocol/src/request_response/mod.rs b/polkadot/node/network/protocol/src/request_response/mod.rs index fe06593bd7a0f58202471483cc06980ee35a2926..296c462b508d643cd09e9079051cf3eeb8534e7f 100644 --- a/polkadot/node/network/protocol/src/request_response/mod.rs +++ b/polkadot/node/network/protocol/src/request_response/mod.rs @@ -51,8 +51,8 @@ use std::{collections::HashMap, time::Duration, u64}; -use polkadot_primitives::{MAX_CODE_SIZE, MAX_POV_SIZE}; -use sc_network::NetworkBackend; +use polkadot_primitives::MAX_CODE_SIZE; +use sc_network::{NetworkBackend, MAX_RESPONSE_SIZE}; use sp_runtime::traits::Block; use strum::{EnumIter, IntoEnumIterator}; @@ -123,10 +123,12 @@ const DEFAULT_REQUEST_TIMEOUT_CONNECTED: Duration = Duration::from_secs(1); /// Timeout for requesting availability chunks. pub const CHUNK_REQUEST_TIMEOUT: Duration = DEFAULT_REQUEST_TIMEOUT_CONNECTED; -/// This timeout is based on what seems sensible from a time budget perspective, considering 6 -/// second block time. This is going to be tough, if we have multiple forks and large PoVs, but we -/// only have so much time. -const POV_REQUEST_TIMEOUT_CONNECTED: Duration = Duration::from_millis(1200); +/// This timeout is based on the following parameters, assuming we use asynchronous backing with no +/// time budget within a relay block: +/// - 500 Mbit/s networking speed +/// - 10 MB PoV +/// - 10 parallel executions +const POV_REQUEST_TIMEOUT_CONNECTED: Duration = Duration::from_millis(2000); /// We want timeout statement requests fast, so we don't waste time on slow nodes. Responders will /// try their best to either serve within that timeout or return an error immediately. (We need to @@ -159,11 +161,8 @@ pub const MAX_PARALLEL_ATTESTED_CANDIDATE_REQUESTS: u32 = 5; /// Response size limit for responses of POV like data. /// -/// This is larger than `MAX_POV_SIZE` to account for protocol overhead and for additional data in -/// `CollationFetchingV1` or `AvailableDataFetchingV1` for example. We try to err on larger limits -/// here as a too large limit only allows an attacker to waste our bandwidth some more, a too low -/// limit might have more severe effects. -const POV_RESPONSE_SIZE: u64 = MAX_POV_SIZE as u64 + 10_000; +/// Same as what we use in substrate networking. +const POV_RESPONSE_SIZE: u64 = MAX_RESPONSE_SIZE; /// Maximum response sizes for `StatementFetchingV1`. /// @@ -217,7 +216,7 @@ impl Protocol { name, legacy_names, 1_000, - POV_RESPONSE_SIZE as u64 * 3, + POV_RESPONSE_SIZE, // We are connected to all validators: CHUNK_REQUEST_TIMEOUT, tx, 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 264333435a00ed074e0b1b47b96a51ae2e3e4441..8270b980919454a85ab323a03c40dd8d638e183d 100644 --- a/polkadot/node/network/statement-distribution/src/legacy_v1/mod.rs +++ b/polkadot/node/network/statement-distribution/src/legacy_v1/mod.rs @@ -33,9 +33,8 @@ use polkadot_node_subsystem_util::{ }; use polkadot_node_subsystem::{ - jaeger, messages::{CandidateBackingMessage, NetworkBridgeEvent, NetworkBridgeTxMessage}, - overseer, ActivatedLeaf, PerLeafSpan, StatementDistributionSenderTrait, + overseer, ActivatedLeaf, StatementDistributionSenderTrait, }; use polkadot_primitives::{ AuthorityDiscoveryId, CandidateHash, CommittedCandidateReceipt, CompactStatement, Hash, @@ -632,15 +631,12 @@ pub(crate) struct ActiveHeadData { session_index: sp_staking::SessionIndex, /// How many `Seconded` statements we've seen per validator. seconded_counts: HashMap, - /// A Jaeger span for this head, so we can attach data to it. - span: PerLeafSpan, } impl ActiveHeadData { fn new( validators: IndexedVec, session_index: sp_staking::SessionIndex, - span: PerLeafSpan, ) -> Self { ActiveHeadData { candidates: Default::default(), @@ -650,7 +646,6 @@ impl ActiveHeadData { validators, session_index, seconded_counts: Default::default(), - span, } } @@ -901,12 +896,6 @@ async fn circulate_statement_and_dependents( None => return, }; - let _span = active_head - .span - .child("circulate-statement") - .with_candidate(statement.payload().candidate_hash()) - .with_stage(jaeger::Stage::StatementDistribution); - let topology = topology_store .get_topology_or_fallback(active_head.session_index) .local_grid_neighbors(); @@ -933,12 +922,10 @@ async fn circulate_statement_and_dependents( } }; - let _span = _span.child("send-to-peers"); // Now send dependent statements to all peers needing them, if any. if let Some((candidate_hash, peers_needing_dependents)) = outputs { for peer in peers_needing_dependents { if let Some(peer_data) = peers.get_mut(&peer) { - let _span_loop = _span.child("to-peer").with_peer_id(&peer); // defensive: the peer data should always be some because the iterator // of peers is derived from the set of peers. send_statements_about( @@ -1513,11 +1500,6 @@ async fn handle_incoming_message<'a, Context>( let fingerprint = message.get_fingerprint(); let candidate_hash = *fingerprint.0.candidate_hash(); - let handle_incoming_span = active_head - .span - .child("handle-incoming") - .with_candidate(candidate_hash) - .with_peer_id(&peer); let max_message_count = active_head.validators.len() * 2; @@ -1699,8 +1681,6 @@ async fn handle_incoming_message<'a, Context>( NotedStatement::Fresh(statement) => { modify_reputation(reputation, ctx.sender(), peer, BENEFIT_VALID_STATEMENT_FIRST).await; - let mut _span = handle_incoming_span.child("notify-backing"); - // When we receive a new message from a peer, we forward it to the // candidate backing subsystem. ctx.send_message(CandidateBackingMessage::Statement(relay_parent, statement_with_pvd)) @@ -2079,7 +2059,6 @@ pub(crate) async fn handle_activated_leaf( activated: ActivatedLeaf, ) -> Result<()> { let relay_parent = activated.hash; - let span = PerLeafSpan::new(activated.span, "statement-distribution-legacy"); gum::trace!( target: LOG_TARGET, hash = ?relay_parent, @@ -2095,11 +2074,10 @@ pub(crate) async fn handle_activated_leaf( .await?; let session_info = &info.session_info; - state.active_heads.entry(relay_parent).or_insert(ActiveHeadData::new( - session_info.validators.clone(), - session_index, - span, - )); + state + .active_heads + .entry(relay_parent) + .or_insert(ActiveHeadData::new(session_info.validators.clone(), session_index)); Ok(()) } diff --git a/polkadot/node/network/statement-distribution/src/legacy_v1/requester.rs b/polkadot/node/network/statement-distribution/src/legacy_v1/requester.rs index 8a8a8f3d624a8d713fb1572cfef9b74a7d9aadc0..c0346adfe101f566234ca3a37968c89f53d4d711 100644 --- a/polkadot/node/network/statement-distribution/src/legacy_v1/requester.rs +++ b/polkadot/node/network/statement-distribution/src/legacy_v1/requester.rs @@ -28,7 +28,6 @@ use polkadot_node_network_protocol::{ }, PeerId, UnifiedReputationChange, }; -use polkadot_node_subsystem::{Span, Stage}; use polkadot_node_subsystem_util::TimeoutExt; use polkadot_primitives::{CandidateHash, CommittedCandidateReceipt, Hash}; @@ -82,10 +81,6 @@ pub async fn fetch( mut sender: mpsc::Sender, metrics: Metrics, ) { - let span = Span::new(candidate_hash, "fetch-large-statement") - .with_relay_parent(relay_parent) - .with_stage(Stage::StatementDistribution); - gum::debug!( target: LOG_TARGET, ?candidate_hash, @@ -102,11 +97,7 @@ pub async fn fetch( // We retry endlessly (with sleep periods), and rely on the subsystem to kill us eventually. loop { - let span = span.child("try-available-peers"); - while let Some(peer) = new_peers.pop() { - let _span = span.child("try-peer").with_peer_id(&peer); - let (outgoing, pending_response) = OutgoingRequest::new(Recipient::Peer(peer), req.clone()); if let Err(err) = sender @@ -182,7 +173,7 @@ pub async fn fetch( new_peers = std::mem::take(&mut tried_peers); // All our peers failed us - try getting new ones before trying again: - match try_get_new_peers(relay_parent, candidate_hash, &mut sender, &span).await { + match try_get_new_peers(relay_parent, candidate_hash, &mut sender).await { Ok(Some(mut peers)) => { gum::trace!(target: LOG_TARGET, ?peers, "Received new peers."); // New arrivals will be tried first: @@ -205,10 +196,7 @@ async fn try_get_new_peers( relay_parent: Hash, candidate_hash: CandidateHash, sender: &mut mpsc::Sender, - span: &Span, ) -> Result>, ()> { - let _span = span.child("wait-for-peers"); - let (tx, rx) = oneshot::channel(); if let Err(err) = sender diff --git a/polkadot/node/network/statement-distribution/src/legacy_v1/tests.rs b/polkadot/node/network/statement-distribution/src/legacy_v1/tests.rs index 8e6fcbaebbf1404f123321e2fe7ff3e90d8cf43a..5e00fb96d74f0299dba296a6b141a2c275c3643d 100644 --- a/polkadot/node/network/statement-distribution/src/legacy_v1/tests.rs +++ b/polkadot/node/network/statement-distribution/src/legacy_v1/tests.rs @@ -121,7 +121,6 @@ fn active_head_accepts_only_2_seconded_per_validator() { let mut head_data = ActiveHeadData::new( IndexedVec::::from(validators), session_index, - PerLeafSpan::new(Arc::new(jaeger::Span::Disabled), "test"), ); let keystore: KeystorePtr = Arc::new(LocalKeystore::in_memory()); @@ -467,7 +466,6 @@ fn peer_view_update_sends_messages() { let mut data = ActiveHeadData::new( IndexedVec::::from(validators), session_index, - PerLeafSpan::new(Arc::new(jaeger::Span::Disabled), "test"), ); let statement = SignedFullStatement::sign( diff --git a/polkadot/node/overseer/examples/minimal-example.rs b/polkadot/node/overseer/examples/minimal-example.rs index 86a1801a5f2d8f08f2c176c53171031de3fceff0..807e7405ff1b76e2c5208c26587a694fc71935d7 100644 --- a/polkadot/node/overseer/examples/minimal-example.rs +++ b/polkadot/node/overseer/examples/minimal-example.rs @@ -24,15 +24,17 @@ use orchestra::async_trait; use std::time::Duration; use polkadot_node_primitives::{BlockData, PoV}; -use polkadot_node_subsystem_types::messages::CandidateValidationMessage; +use polkadot_node_subsystem_types::messages::{CandidateValidationMessage, PvfExecKind}; use polkadot_overseer::{ self as overseer, dummy::dummy_overseer_builder, gen::{FromOrchestra, SpawnedSubsystem}, HeadSupportsParachains, SubsystemError, }; -use polkadot_primitives::{CandidateReceipt, Hash, PvfExecKind}; -use polkadot_primitives_test_helpers::{dummy_candidate_descriptor, dummy_hash}; +use polkadot_primitives::{CandidateReceipt, Hash, PersistedValidationData}; +use polkadot_primitives_test_helpers::{ + dummy_candidate_descriptor, dummy_hash, dummy_validation_code, +}; struct AlwaysSupportsParachains; @@ -73,7 +75,9 @@ impl Subsystem1 { commitments_hash: Hash::zero(), }; - let msg = CandidateValidationMessage::ValidateFromChainState { + let msg = CandidateValidationMessage::ValidateFromExhaustive { + validation_data: PersistedValidationData { ..Default::default() }, + validation_code: dummy_validation_code(), candidate_receipt, pov: PoV { block_data: BlockData(Vec::new()) }.into(), executor_params: Default::default(), diff --git a/polkadot/node/overseer/src/dummy.rs b/polkadot/node/overseer/src/dummy.rs index fc5f0070773b74e671d6510b380bd27b861ef84c..d618c0c7ca95355ab20dae0649ebad1c70d80b72 100644 --- a/polkadot/node/overseer/src/dummy.rs +++ b/polkadot/node/overseer/src/dummy.rs @@ -88,6 +88,7 @@ pub fn dummy_overseer_builder( DummySubsystem, DummySubsystem, DummySubsystem, + DummySubsystem, >, SubsystemError, > @@ -131,6 +132,7 @@ pub fn one_for_all_overseer_builder( Sub, Sub, Sub, + Sub, >, SubsystemError, > @@ -155,6 +157,7 @@ where + Subsystem, SubsystemError> + Subsystem, SubsystemError> + Subsystem, SubsystemError> + + Subsystem, SubsystemError> + Subsystem, SubsystemError> + Subsystem, SubsystemError> + Subsystem, SubsystemError> @@ -183,13 +186,13 @@ where .statement_distribution(subsystem.clone()) .approval_distribution(subsystem.clone()) .approval_voting(subsystem.clone()) + .approval_voting_parallel(subsystem.clone()) .gossip_support(subsystem.clone()) .dispute_coordinator(subsystem.clone()) .dispute_distribution(subsystem.clone()) .chain_selection(subsystem.clone()) .prospective_parachains(subsystem.clone()) .activation_external_listeners(Default::default()) - .span_per_active_leaf(Default::default()) .active_leaves(Default::default()) .spawner(SpawnGlue(spawner)) .metrics(metrics) diff --git a/polkadot/node/overseer/src/lib.rs b/polkadot/node/overseer/src/lib.rs index baaff9c7c9f63c5cff812e5ae01097ca538c4d75..87ef63d8a5d70ae8b053a2c8ff2e98bb377fd826 100644 --- a/polkadot/node/overseer/src/lib.rs +++ b/polkadot/node/overseer/src/lib.rs @@ -60,6 +60,7 @@ // unused dependencies can not work for test and examples at the same time // yielding false positives #![warn(missing_docs)] +#![allow(dead_code)] // TODO https://github.com/paritytech/polkadot-sdk/issues/5793 use std::{ collections::{hash_map, HashMap}, @@ -76,19 +77,19 @@ use sc_client_api::{BlockImportNotification, BlockchainEvents, FinalityNotificat use self::messages::{BitfieldSigningMessage, PvfCheckerMessage}; use polkadot_node_subsystem_types::messages::{ - ApprovalDistributionMessage, ApprovalVotingMessage, AvailabilityDistributionMessage, - AvailabilityRecoveryMessage, AvailabilityStoreMessage, BitfieldDistributionMessage, - CandidateBackingMessage, CandidateValidationMessage, ChainApiMessage, ChainSelectionMessage, - CollationGenerationMessage, CollatorProtocolMessage, DisputeCoordinatorMessage, - DisputeDistributionMessage, GossipSupportMessage, NetworkBridgeRxMessage, - NetworkBridgeTxMessage, ProspectiveParachainsMessage, ProvisionerMessage, RuntimeApiMessage, - StatementDistributionMessage, + ApprovalDistributionMessage, ApprovalVotingMessage, ApprovalVotingParallelMessage, + AvailabilityDistributionMessage, AvailabilityRecoveryMessage, AvailabilityStoreMessage, + BitfieldDistributionMessage, CandidateBackingMessage, CandidateValidationMessage, + ChainApiMessage, ChainSelectionMessage, CollationGenerationMessage, CollatorProtocolMessage, + DisputeCoordinatorMessage, DisputeDistributionMessage, GossipSupportMessage, + NetworkBridgeRxMessage, NetworkBridgeTxMessage, ProspectiveParachainsMessage, + ProvisionerMessage, RuntimeApiMessage, StatementDistributionMessage, }; pub use polkadot_node_subsystem_types::{ errors::{SubsystemError, SubsystemResult}, - jaeger, ActivatedLeaf, ActiveLeavesUpdate, ChainApiBackend, OverseerSignal, - RuntimeApiSubsystemClient, UnpinHandle, + ActivatedLeaf, ActiveLeavesUpdate, ChainApiBackend, OverseerSignal, RuntimeApiSubsystemClient, + UnpinHandle, }; pub mod metrics; @@ -521,7 +522,7 @@ pub struct Overseer { ])] bitfield_signing: BitfieldSigning, - #[subsystem(BitfieldDistributionMessage, sends: [ + #[subsystem(blocking, message_capacity: 8192, BitfieldDistributionMessage, sends: [ RuntimeApiMessage, NetworkBridgeTxMessage, ProvisionerMessage, @@ -550,6 +551,7 @@ pub struct Overseer { BitfieldDistributionMessage, StatementDistributionMessage, ApprovalDistributionMessage, + ApprovalVotingParallelMessage, GossipSupportMessage, DisputeDistributionMessage, CollationGenerationMessage, @@ -581,6 +583,7 @@ pub struct Overseer { #[subsystem(blocking, message_capacity: 64000, ApprovalDistributionMessage, sends: [ NetworkBridgeTxMessage, ApprovalVotingMessage, + RuntimeApiMessage, ], can_receive_priority_messages)] approval_distribution: ApprovalDistribution, @@ -594,7 +597,19 @@ pub struct Overseer { RuntimeApiMessage, ])] approval_voting: ApprovalVoting, - + #[subsystem(blocking, message_capacity: 64000, ApprovalVotingParallelMessage, sends: [ + AvailabilityRecoveryMessage, + CandidateValidationMessage, + ChainApiMessage, + ChainSelectionMessage, + DisputeCoordinatorMessage, + RuntimeApiMessage, + NetworkBridgeTxMessage, + ApprovalVotingMessage, + ApprovalDistributionMessage, + ApprovalVotingParallelMessage, + ])] + approval_voting_parallel: ApprovalVotingParallel, #[subsystem(GossipSupportMessage, sends: [ NetworkBridgeTxMessage, NetworkBridgeRxMessage, // TODO @@ -612,6 +627,7 @@ pub struct Overseer { AvailabilityStoreMessage, AvailabilityRecoveryMessage, ChainSelectionMessage, + ApprovalVotingParallelMessage, ])] dispute_coordinator: DisputeCoordinator, @@ -634,9 +650,6 @@ pub struct Overseer { /// External listeners waiting for a hash to be in the active-leave set. pub activation_external_listeners: HashMap>>>, - /// Stores the [`jaeger::Span`] per active leaf. - pub span_per_active_leaf: HashMap>, - /// The set of the "active leaves". pub active_leaves: HashMap, @@ -801,11 +814,10 @@ where }; let mut update = match self.on_head_activated(&block.hash, Some(block.parent_hash)).await { - Some(span) => ActiveLeavesUpdate::start_work(ActivatedLeaf { + Some(_) => ActiveLeavesUpdate::start_work(ActivatedLeaf { hash: block.hash, number: block.number, unpin_handle: block.unpin_handle, - span, }), None => ActiveLeavesUpdate::default(), }; @@ -858,11 +870,7 @@ where /// Handles a header activation. If the header's state doesn't support the parachains API, /// this returns `None`. - async fn on_head_activated( - &mut self, - hash: &Hash, - parent_hash: Option, - ) -> Option> { + async fn on_head_activated(&mut self, hash: &Hash, _parent_hash: Option) -> Option<()> { if !self.supports_parachains.head_supports_parachains(hash).await { return None } @@ -880,22 +888,12 @@ where } } - let mut span = jaeger::Span::new(*hash, "leaf-activated"); - - if let Some(parent_span) = parent_hash.and_then(|h| self.span_per_active_leaf.get(&h)) { - span.add_follows_from(parent_span); - } - - let span = Arc::new(span); - self.span_per_active_leaf.insert(*hash, span.clone()); - - Some(span) + Some(()) } fn on_head_deactivated(&mut self, hash: &Hash) { self.metrics.on_head_deactivated(); self.activation_external_listeners.remove(hash); - self.span_per_active_leaf.remove(hash); } fn clean_up_external_listeners(&mut self) { diff --git a/polkadot/node/overseer/src/tests.rs b/polkadot/node/overseer/src/tests.rs index 8e78d8fc8921a8bd26686e295b584c4e1fff6433..46864a482e2a6d12ff45003dedc0638d5e38718a 100644 --- a/polkadot/node/overseer/src/tests.rs +++ b/polkadot/node/overseer/src/tests.rs @@ -25,14 +25,14 @@ use polkadot_node_primitives::{ }; use polkadot_node_subsystem_test_helpers::mock::{dummy_unpin_handle, new_leaf}; use polkadot_node_subsystem_types::messages::{ - NetworkBridgeEvent, ReportPeerMessage, RuntimeApiRequest, + NetworkBridgeEvent, PvfExecKind, ReportPeerMessage, RuntimeApiRequest, }; use polkadot_primitives::{ CandidateHash, CandidateReceipt, CollatorPair, Id as ParaId, InvalidDisputeStatementKind, - PvfExecKind, SessionIndex, ValidDisputeStatementKind, ValidatorIndex, + PersistedValidationData, SessionIndex, ValidDisputeStatementKind, ValidatorIndex, }; use polkadot_primitives_test_helpers::{ - dummy_candidate_descriptor, dummy_candidate_receipt, dummy_hash, + dummy_candidate_descriptor, dummy_candidate_receipt, dummy_hash, dummy_validation_code, }; use crate::{ @@ -104,7 +104,9 @@ where }; let (tx, _) = oneshot::channel(); - ctx.send_message(CandidateValidationMessage::ValidateFromChainState { + ctx.send_message(CandidateValidationMessage::ValidateFromExhaustive { + validation_data: PersistedValidationData { ..Default::default() }, + validation_code: dummy_validation_code(), candidate_receipt, pov: PoV { block_data: BlockData(Vec::new()) }.into(), executor_params: Default::default(), @@ -802,7 +804,9 @@ fn test_candidate_validation_msg() -> CandidateValidationMessage { commitments_hash: Hash::zero(), }; - CandidateValidationMessage::ValidateFromChainState { + CandidateValidationMessage::ValidateFromExhaustive { + validation_data: PersistedValidationData { ..Default::default() }, + validation_code: dummy_validation_code(), candidate_receipt, pov, executor_params: Default::default(), @@ -950,7 +954,7 @@ fn test_prospective_parachains_msg() -> ProspectiveParachainsMessage { // Checks that `stop`, `broadcast_signal` and `broadcast_message` are implemented correctly. #[test] fn overseer_all_subsystems_receive_signals_and_messages() { - const NUM_SUBSYSTEMS: usize = 23; + const NUM_SUBSYSTEMS: usize = 24; // -4 for BitfieldSigning, GossipSupport, AvailabilityDistribution and PvfCheckerSubsystem. const NUM_SUBSYSTEMS_MESSAGED: usize = NUM_SUBSYSTEMS - 4; @@ -1028,6 +1032,11 @@ fn overseer_all_subsystems_receive_signals_and_messages() { handle .send_msg_anon(AllMessages::ApprovalDistribution(test_approval_distribution_msg())) .await; + handle + .send_msg_anon(AllMessages::ApprovalVotingParallel( + test_approval_distribution_msg().into(), + )) + .await; handle .send_msg_anon(AllMessages::ApprovalVoting(test_approval_voting_msg())) .await; @@ -1101,6 +1110,7 @@ fn context_holds_onto_message_until_enough_signals_received() { let (chain_selection_bounded_tx, _) = metered::channel(CHANNEL_CAPACITY); let (pvf_checker_bounded_tx, _) = metered::channel(CHANNEL_CAPACITY); let (prospective_parachains_bounded_tx, _) = metered::channel(CHANNEL_CAPACITY); + let (approval_voting_parallel_tx, _) = metered::channel(CHANNEL_CAPACITY); let (candidate_validation_unbounded_tx, _) = metered::unbounded(); let (candidate_backing_unbounded_tx, _) = metered::unbounded(); @@ -1125,6 +1135,7 @@ fn context_holds_onto_message_until_enough_signals_received() { let (chain_selection_unbounded_tx, _) = metered::unbounded(); let (pvf_checker_unbounded_tx, _) = metered::unbounded(); let (prospective_parachains_unbounded_tx, _) = metered::unbounded(); + let (approval_voting_parallel_unbounded_tx, _) = metered::unbounded(); let channels_out = ChannelsOut { candidate_validation: candidate_validation_bounded_tx.clone(), @@ -1150,6 +1161,7 @@ fn context_holds_onto_message_until_enough_signals_received() { chain_selection: chain_selection_bounded_tx.clone(), pvf_checker: pvf_checker_bounded_tx.clone(), prospective_parachains: prospective_parachains_bounded_tx.clone(), + approval_voting_parallel: approval_voting_parallel_tx.clone(), candidate_validation_unbounded: candidate_validation_unbounded_tx.clone(), candidate_backing_unbounded: candidate_backing_unbounded_tx.clone(), @@ -1174,6 +1186,7 @@ fn context_holds_onto_message_until_enough_signals_received() { chain_selection_unbounded: chain_selection_unbounded_tx.clone(), pvf_checker_unbounded: pvf_checker_unbounded_tx.clone(), prospective_parachains_unbounded: prospective_parachains_unbounded_tx.clone(), + approval_voting_parallel_unbounded: approval_voting_parallel_unbounded_tx.clone(), }; let (mut signal_tx, signal_rx) = metered::channel(CHANNEL_CAPACITY); diff --git a/polkadot/node/primitives/Cargo.toml b/polkadot/node/primitives/Cargo.toml index cd642bf16ff9bf7d6ffeeea2ce0dac752128bd3c..7185205f905b6d24fa5a148d32572a106794d7f6 100644 --- a/polkadot/node/primitives/Cargo.toml +++ b/polkadot/node/primitives/Cargo.toml @@ -12,11 +12,13 @@ workspace = true [dependencies] bounded-vec = { workspace = true } futures = { workspace = true } +futures-timer = { workspace = true } polkadot-primitives = { workspace = true, default-features = true } codec = { features = ["derive"], workspace = true } sp-core = { workspace = true, default-features = true } sp-application-crypto = { workspace = true, default-features = true } sp-consensus-babe = { workspace = true, default-features = true } +sp-consensus-slots = { workspace = true } sp-keystore = { workspace = true, default-features = true } sp-maybe-compressed-blob = { workspace = true, default-features = true } sp-runtime = { workspace = true, default-features = true } @@ -25,6 +27,7 @@ schnorrkel = { workspace = true, default-features = true } thiserror = { workspace = true } bitvec = { features = ["alloc"], workspace = true } serde = { features = ["derive"], workspace = true, default-features = true } +sc-keystore = { workspace = true } [target.'cfg(not(target_os = "unknown"))'.dependencies] zstd = { version = "0.12.4", default-features = false } diff --git a/polkadot/node/primitives/src/approval/criteria.rs b/polkadot/node/primitives/src/approval/criteria.rs new file mode 100644 index 0000000000000000000000000000000000000000..0a1a0ee2367fc0d129f2c96db10408825c6c5863 --- /dev/null +++ b/polkadot/node/primitives/src/approval/criteria.rs @@ -0,0 +1,177 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Assignment criteria VRF generation and checking interfaces. + +use crate::approval::{ + v1::{DelayTranche, RelayVRFStory}, + v2::{AssignmentCertV2, CoreBitfield}, +}; +use codec::{Decode, Encode}; +use polkadot_primitives::{ + AssignmentId, CandidateHash, CoreIndex, GroupIndex, IndexedVec, SessionInfo, ValidatorIndex, +}; +use sc_keystore::LocalKeystore; + +use std::collections::HashMap; + +/// Details pertaining to our assignment on a block. +#[derive(Debug, Clone, Encode, Decode, PartialEq)] +pub struct OurAssignment { + cert: AssignmentCertV2, + tranche: DelayTranche, + validator_index: ValidatorIndex, + // Whether the assignment has been triggered already. + triggered: bool, +} + +impl OurAssignment { + /// Create a new `OurAssignment`. + pub fn new( + cert: AssignmentCertV2, + tranche: DelayTranche, + validator_index: ValidatorIndex, + triggered: bool, + ) -> Self { + OurAssignment { cert, tranche, validator_index, triggered } + } + /// Returns a reference to the assignment cert. + pub fn cert(&self) -> &AssignmentCertV2 { + &self.cert + } + + /// Returns the assignment cert. + pub fn into_cert(self) -> AssignmentCertV2 { + self.cert + } + + /// Returns the delay tranche of the assignment. + pub fn tranche(&self) -> DelayTranche { + self.tranche + } + + /// Returns the validator index of the assignment. + pub fn validator_index(&self) -> ValidatorIndex { + self.validator_index + } + + /// Returns whether the assignment has been triggered. + pub fn triggered(&self) -> bool { + self.triggered + } + + /// Marks the assignment as triggered. + pub fn mark_triggered(&mut self) { + self.triggered = true; + } +} + +/// Information about the world assignments are being produced in. +#[derive(Clone, Debug)] +pub struct Config { + /// The assignment public keys for validators. + pub assignment_keys: Vec, + /// The groups of validators assigned to each core. + pub validator_groups: IndexedVec>, + /// The number of availability cores used by the protocol during this session. + pub n_cores: u32, + /// The zeroth delay tranche width. + pub zeroth_delay_tranche_width: u32, + /// The number of samples we do of `relay_vrf_modulo`. + pub relay_vrf_modulo_samples: u32, + /// The number of delay tranches in total. + pub n_delay_tranches: u32, +} + +impl<'a> From<&'a SessionInfo> for Config { + fn from(s: &'a SessionInfo) -> Self { + Config { + assignment_keys: s.assignment_keys.clone(), + validator_groups: s.validator_groups.clone(), + n_cores: s.n_cores, + zeroth_delay_tranche_width: s.zeroth_delay_tranche_width, + relay_vrf_modulo_samples: s.relay_vrf_modulo_samples, + n_delay_tranches: s.n_delay_tranches, + } + } +} + +/// A trait for producing and checking assignments. +/// +/// Approval voting subsystem implements a a real implemention +/// for it and tests use a mock implementation. +pub trait AssignmentCriteria { + /// Compute the assignments for the given relay VRF story. + fn compute_assignments( + &self, + keystore: &LocalKeystore, + relay_vrf_story: RelayVRFStory, + config: &Config, + leaving_cores: Vec<(CandidateHash, CoreIndex, GroupIndex)>, + enable_v2_assignments: bool, + ) -> HashMap; + + /// Check the assignment cert for the given relay VRF story and returns the delay tranche. + fn check_assignment_cert( + &self, + claimed_core_bitfield: CoreBitfield, + validator_index: ValidatorIndex, + config: &Config, + relay_vrf_story: RelayVRFStory, + assignment: &AssignmentCertV2, + // Backing groups for each "leaving core". + backing_groups: Vec, + ) -> Result; +} + +/// Assignment invalid. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct InvalidAssignment(pub InvalidAssignmentReason); + +impl std::fmt::Display for InvalidAssignment { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "Invalid Assignment: {:?}", self.0) + } +} + +impl std::error::Error for InvalidAssignment {} + +/// Failure conditions when checking an assignment cert. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum InvalidAssignmentReason { + /// The validator index is out of bounds. + ValidatorIndexOutOfBounds, + /// Sample index is out of bounds. + SampleOutOfBounds, + /// Core index is out of bounds. + CoreIndexOutOfBounds, + /// Invalid assignment key. + InvalidAssignmentKey, + /// Node is in backing group. + IsInBackingGroup, + /// Modulo core index mismatch. + VRFModuloCoreIndexMismatch, + /// Modulo output mismatch. + VRFModuloOutputMismatch, + /// Delay core index mismatch. + VRFDelayCoreIndexMismatch, + /// Delay output mismatch. + VRFDelayOutputMismatch, + /// Invalid arguments + InvalidArguments, + /// Assignment vrf check resulted in 0 assigned cores. + NullAssignment, +} diff --git a/polkadot/node/primitives/src/approval.rs b/polkadot/node/primitives/src/approval/mod.rs similarity index 98% rename from polkadot/node/primitives/src/approval.rs rename to polkadot/node/primitives/src/approval/mod.rs index 66883b33367bceaeeaa61cb7152e735eb8dbfcc6..42342f9889a935d23515815a16b3a1175fbc15d2 100644 --- a/polkadot/node/primitives/src/approval.rs +++ b/polkadot/node/primitives/src/approval/mod.rs @@ -16,6 +16,12 @@ //! Types relevant for approval. +/// Criteria for assignment. +pub mod criteria; + +/// Time utilities for approval voting. +pub mod time; + /// A list of primitives introduced in v1. pub mod v1 { use sp_consensus_babe as babe_primitives; @@ -25,8 +31,8 @@ pub mod v1 { use codec::{Decode, Encode}; use polkadot_primitives::{ - BlockNumber, CandidateHash, CandidateIndex, CoreIndex, Hash, Header, SessionIndex, - ValidatorIndex, ValidatorSignature, + BlockNumber, CandidateHash, CandidateIndex, CoreIndex, GroupIndex, Hash, Header, + SessionIndex, ValidatorIndex, ValidatorSignature, }; use sp_application_crypto::ByteArray; @@ -118,7 +124,7 @@ pub mod v1 { } /// Metadata about a block which is now live in the approval protocol. - #[derive(Debug)] + #[derive(Debug, Clone)] pub struct BlockApprovalMeta { /// The hash of the block. pub hash: Hash, @@ -128,11 +134,13 @@ pub mod v1 { pub parent_hash: Hash, /// The candidates included by the block. /// Note that these are not the same as the candidates that appear within the block body. - pub candidates: Vec, + pub candidates: Vec<(CandidateHash, CoreIndex, GroupIndex)>, /// The consensus slot of the block. pub slot: Slot, /// The session of the block. pub session: SessionIndex, + /// The vrf story. + pub vrf_story: RelayVRFStory, } /// Errors that can occur during the approvals protocol. diff --git a/polkadot/node/core/approval-voting/src/time.rs b/polkadot/node/primitives/src/approval/time.rs similarity index 95% rename from polkadot/node/core/approval-voting/src/time.rs rename to polkadot/node/primitives/src/approval/time.rs index 5c3e7e85a17a212eeae2e8b6b9b7cf6dbecde036..465aae23c90e5f45c9ea113284e2b934f8a057ab 100644 --- a/polkadot/node/core/approval-voting/src/time.rs +++ b/polkadot/node/primitives/src/approval/time.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -//! Time utilities for approval voting. +//! Time utilities for approval voting subsystems. use futures::{ future::BoxFuture, @@ -23,7 +23,7 @@ use futures::{ Stream, StreamExt, }; -use polkadot_node_primitives::approval::v1::DelayTranche; +use crate::approval::v1::DelayTranche; use sp_consensus_slots::Slot; use std::{ collections::HashSet, @@ -33,11 +33,15 @@ use std::{ }; use polkadot_primitives::{Hash, ValidatorIndex}; +/// The duration of a single tick in milliseconds. pub const TICK_DURATION_MILLIS: u64 = 500; /// A base unit of time, starting from the Unix epoch, split into half-second intervals. pub type Tick = u64; +/// How far in the future a tick can be accepted. +pub const TICK_TOO_FAR_IN_FUTURE: Tick = 20; // 10 seconds. + /// A clock which allows querying of the current tick as well as /// waiting for a tick to be reached. pub trait Clock { @@ -50,6 +54,7 @@ pub trait Clock { /// Extension methods for clocks. pub trait ClockExt { + /// Returns the current tranche. fn tranche_now(&self, slot_duration_millis: u64, base_slot: Slot) -> DelayTranche; } @@ -124,7 +129,7 @@ impl DelayedApprovalTimer { /// /// Guarantees that if a timer already exits for the give block hash, /// no additional timer is started. - pub(crate) fn maybe_arm_timer( + pub fn maybe_arm_timer( &mut self, wait_until: Tick, clock: &dyn Clock, @@ -173,7 +178,7 @@ mod tests { use futures_timer::Delay; use polkadot_primitives::{Hash, ValidatorIndex}; - use crate::time::{Clock, SystemClock}; + use crate::approval::time::{Clock, SystemClock}; use super::DelayedApprovalTimer; diff --git a/polkadot/node/primitives/src/lib.rs b/polkadot/node/primitives/src/lib.rs index 685a9fd337dfd3ef0281d681d74a116f26943e3c..99d3a3e515b82e9a89127aa62817638ec2991877 100644 --- a/polkadot/node/primitives/src/lib.rs +++ b/polkadot/node/primitives/src/lib.rs @@ -59,7 +59,7 @@ pub use disputes::{ /// relatively rare. /// /// The associated worker binaries should use the same version as the node that spawns them. -pub const NODE_VERSION: &'static str = "1.15.1"; +pub const NODE_VERSION: &'static str = "1.16.0"; // For a 16-ary Merkle Prefix Trie, we can expect at most 16 32-byte hashes per node // plus some overhead: @@ -105,7 +105,7 @@ pub const MAX_FINALITY_LAG: u32 = 500; /// Type of a session window size. /// /// We are not using `NonZeroU32` here because `expect` and `unwrap` are not yet const, so global -/// constants of `SessionWindowSize` would require `lazy_static` in that case. +/// constants of `SessionWindowSize` would require `LazyLock` in that case. /// /// See: #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] diff --git a/polkadot/node/service/Cargo.toml b/polkadot/node/service/Cargo.toml index 216aa10e8acb8697ec78fb6e860d8d2f92c93b58..8d50b54b2fdc88bae6d3f132f46b74f4ce4f0efd 100644 --- a/polkadot/node/service/Cargo.toml +++ b/polkadot/node/service/Cargo.toml @@ -18,15 +18,13 @@ sc-consensus-beefy = { workspace = true, default-features = true } sc-consensus-grandpa = { workspace = true, default-features = true } mmr-gadget = { workspace = true, default-features = true } sp-mmr-primitives = { workspace = true, default-features = true } -sc-block-builder = { workspace = true, default-features = true } +sp-genesis-builder = { workspace = true, default-features = true } sc-chain-spec = { workspace = true, default-features = true } sc-client-api = { workspace = true, default-features = true } -sc-client-db = { workspace = true, default-features = true } sc-consensus = { workspace = true, default-features = true } sc-consensus-slots = { workspace = true, default-features = true } sc-executor = { workspace = true, default-features = true } sc-network = { workspace = true, default-features = true } -sc-network-common = { workspace = true, default-features = true } sc-network-sync = { workspace = true, default-features = true } sc-transaction-pool = { workspace = true, default-features = true } sc-transaction-pool-api = { workspace = true, default-features = true } @@ -50,22 +48,17 @@ sp-block-builder = { workspace = true, default-features = true } sp-blockchain = { workspace = true, default-features = true } sp-core = { workspace = true, default-features = true } sp-io = { workspace = true, default-features = true } -sp-keystore = { workspace = true, default-features = true } sp-offchain = { workspace = true, default-features = true } sp-runtime = { workspace = true, default-features = true } sp-session = { workspace = true, default-features = true } -sp-storage = { workspace = true, default-features = true } sp-transaction-pool = { workspace = true, default-features = true } pallet-transaction-payment = { workspace = true, default-features = true } sp-timestamp = { workspace = true, default-features = true } sp-consensus-babe = { workspace = true, default-features = true } -sp-state-machine = { workspace = true, default-features = true } sp-weights = { workspace = true, default-features = true } sp-version = { workspace = true, default-features = true } # Substrate Pallets -pallet-babe = { workspace = true, default-features = true } -pallet-staking = { workspace = true, default-features = true } pallet-transaction-payment-rpc-runtime-api = { workspace = true, default-features = true } frame-metadata-hash-extension = { optional = true, workspace = true, default-features = true } frame-system = { workspace = true, default-features = true } @@ -73,18 +66,15 @@ frame-system = { workspace = true, default-features = true } # Substrate Other frame-system-rpc-runtime-api = { workspace = true, default-features = true } prometheus-endpoint = { workspace = true, default-features = true } -frame-support = { workspace = true, default-features = true } frame-benchmarking-cli = { workspace = true, default-features = true } frame-benchmarking = { workspace = true, default-features = true } # External Crates async-trait = { workspace = true } futures = { workspace = true } -hex-literal = { workspace = true, default-features = true } is_executable = { workspace = true } gum = { workspace = true, default-features = true } log = { workspace = true, default-features = true } -schnellru = { workspace = true } serde = { features = ["derive"], workspace = true, default-features = true } serde_json = { workspace = true, default-features = true } thiserror = { workspace = true } @@ -93,13 +83,11 @@ kvdb-rocksdb = { optional = true, workspace = true } parity-db = { optional = true, workspace = true } codec = { workspace = true, default-features = true } parking_lot = { workspace = true, default-features = true } -bitvec = { optional = true, workspace = true, default-features = true } # Polkadot polkadot-core-primitives = { workspace = true, default-features = true } polkadot-node-core-parachains-inherent = { workspace = true, default-features = true } polkadot-overseer = { workspace = true, default-features = true } -polkadot-parachain-primitives = { workspace = true, default-features = true } polkadot-primitives = { workspace = true, default-features = true } polkadot-node-primitives = { workspace = true, default-features = true } polkadot-rpc = { workspace = true, default-features = true } @@ -128,6 +116,7 @@ polkadot-gossip-support = { optional = true, workspace = true, default-features polkadot-network-bridge = { optional = true, workspace = true, default-features = true } polkadot-node-collation-generation = { optional = true, workspace = true, default-features = true } polkadot-node-core-approval-voting = { optional = true, workspace = true, default-features = true } +polkadot-node-core-approval-voting-parallel = { optional = true, workspace = true, default-features = true } polkadot-node-core-av-store = { optional = true, workspace = true, default-features = true } polkadot-node-core-backing = { optional = true, workspace = true, default-features = true } polkadot-node-core-bitfield-signing = { optional = true, workspace = true, default-features = true } @@ -172,6 +161,7 @@ full-node = [ "polkadot-network-bridge", "polkadot-node-collation-generation", "polkadot-node-core-approval-voting", + "polkadot-node-core-approval-voting-parallel", "polkadot-node-core-av-store", "polkadot-node-core-backing", "polkadot-node-core-bitfield-signing", @@ -189,13 +179,11 @@ full-node = [ # Configure the native runtimes to use. westend-native = [ - "bitvec", "frame-metadata-hash-extension", "westend-runtime", "westend-runtime-constants", ] rococo-native = [ - "bitvec", "frame-metadata-hash-extension", "rococo-runtime", "rococo-runtime-constants", @@ -211,26 +199,18 @@ metadata-hash = [ runtime-benchmarks = [ "frame-benchmarking-cli/runtime-benchmarks", "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", - "pallet-babe/runtime-benchmarks", - "pallet-staking/runtime-benchmarks", - "polkadot-parachain-primitives/runtime-benchmarks", "polkadot-primitives/runtime-benchmarks", "polkadot-runtime-parachains/runtime-benchmarks", "polkadot-test-client/runtime-benchmarks", "rococo-runtime?/runtime-benchmarks", - "sc-client-db/runtime-benchmarks", "sc-service/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "westend-runtime?/runtime-benchmarks", "xcm-runtime-apis/runtime-benchmarks", ] try-runtime = [ - "frame-support/try-runtime", "frame-system/try-runtime", - "pallet-babe/try-runtime", - "pallet-staking/try-runtime", "pallet-transaction-payment/try-runtime", "polkadot-runtime-parachains/try-runtime", "rococo-runtime?/try-runtime", diff --git a/polkadot/node/service/chain-specs/wococo.json b/polkadot/node/service/chain-specs/wococo.json deleted file mode 100644 index 0ad7334685f1ffd209d00c0c43fc53ce8d12709e..0000000000000000000000000000000000000000 --- a/polkadot/node/service/chain-specs/wococo.json +++ /dev/null @@ -1,218 +0,0 @@ -{ - "name": "Wococo", - "id": "wococo", - "chainType": "Live", - "bootNodes": [ - "/dns/wococo-bootnode-0.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWQC541JNa6dguvifYYjwPnviscJHqbwvoNDMX3WBubPJZ", - "/dns/wococo-bootnode-1.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWG9v9Aexs6EvBYAwy9cqLyw25BRi2U1RQNQ2r5QJRxfFm", - "/dns/wococo-bootnode-2.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWNza3xSzCbw6phggjKD4QyqF8xvVpDFk7ctkoM5c1PQz2", - "/dns/wococo-bootnode-3.parity-testnet.parity.io/tcp/30333/p2p/12D3KooWJ4ngb7S1Lkq5C4ZYqfFuJswxTE3UC5zjui5TLhAULTRU" - ], - "telemetryEndpoints": [ - [ - "/dns/telemetry.polkadot.io/tcp/443/x-parity-wss/%2Fsubmit%2F", - 0 - ] - ], - "protocolId": "wococo", - "properties": { - "ss58Format": 42, - "tokenDecimals": 12, - "tokenSymbol": "WOOK" - }, - "forkBlocks": null, - "badBlocks": null, - "lightSyncState": null, - "codeSubstitutes": {}, - "genesis": { - "raw": { - "top": { - "0x0595267586b57744927884f519eb81014e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x06de3d8a54d27e44a9d5ce189618f22d4e7b9012096b41c4eb3aaf947f6ea429": "0x0500", - "0x06de3d8a54d27e44a9d5ce189618f22db4b49d95320d9021994c850f25b8e385": "0x0000300000800000080000000000100000c800000500000005000000020000000200000000005000000010000700e876481702004001040000000400000000000000000000000000000000000000000000000000000000000000000000000800000000200000040000000400000000001000b00400000000000000000000140000000400000004000000000000000000060000006400000002000000190000000000000002000000020000000700c817a80402004001000200000005000000", - "0x1405f2411d0af5a7ff397e7c9dc68d194e7b9012096b41c4eb3aaf947f6ea429": "0x0100", - "0x1405f2411d0af5a7ff397e7c9dc68d196323ae84c43568be0d1394d5d0d522c4": "0x03000000", - "0x1809d78346727a0ef58c0fa03bafa3234e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x196e027349017067f9eb56e2c4d9ded54e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x1a736d37504c2e3fb73dad160c55b2914e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x1cb6f36e027abb2091cfb5110ab5087f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x1cb6f36e027abb2091cfb5110ab5087f5e0621c4869aa60c02be9adcc98a0d1d": "0x10006078f6e6a00db1f40097f0d07953008b04cda71ad831e70f37e93eb2b40431010000000000000022371e9715d00b3a21c9a899ba3eafd11f5143b821b159b864025ba1eabdb6310100000000000000e6b8162c3e767f8e61892f7fcd06d27041d806e5e0335c59dcdafa5c8e181c5b0100000000000000585a72774ca9465ba0e7407e4e66d239febbe906cbf090169b6cfa15dd44e5770100000000000000", - "0x1cb6f36e027abb2091cfb5110ab5087f66e8f035c8adbe7f1547b43c51e6f8a4": "0x00000000", - "0x1cb6f36e027abb2091cfb5110ab5087faacf00b9b41fda7a9268821c2a2b3e4c": "0x10006078f6e6a00db1f40097f0d07953008b04cda71ad831e70f37e93eb2b40431010000000000000022371e9715d00b3a21c9a899ba3eafd11f5143b821b159b864025ba1eabdb6310100000000000000e6b8162c3e767f8e61892f7fcd06d27041d806e5e0335c59dcdafa5c8e181c5b0100000000000000585a72774ca9465ba0e7407e4e66d239febbe906cbf090169b6cfa15dd44e5770100000000000000", - "0x1cb6f36e027abb2091cfb5110ab5087fdc6b171b77304263c292cc3ea5ed31ef": "0x0100000000000000040000000000000002", - "0x2099d7f109d6e535fb000bba623fd4404c014e6bf8b8c2c011e7290b85696bb3": "0x10b691bfd2cd584abd1531b7deff6d0e34893960b59ae550348c33abd76af4cb490e93248544c963f34bb9cde63c97f85ef7a1939d3c9075907b26edf368fe846e5ed9fdbd8dffeb5324935a7fafc536de96d62abee0a05d7eefa961c1cf3de266ca24971e2ec596d510c673f4f8d36d0a8a407b59ffd0643f621369973a335656", - "0x2099d7f109d6e535fb000bba623fd4404e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x2099d7f109d6e535fb000bba623fd4409f99a2ce711f3a31b2fc05604c93f179": "0x10b691bfd2cd584abd1531b7deff6d0e34893960b59ae550348c33abd76af4cb490e93248544c963f34bb9cde63c97f85ef7a1939d3c9075907b26edf368fe846e5ed9fdbd8dffeb5324935a7fafc536de96d62abee0a05d7eefa961c1cf3de266ca24971e2ec596d510c673f4f8d36d0a8a407b59ffd0643f621369973a335656", - "0x26aa394eea5630e07c48ae0c9558cef734abf5cb34d6244378cddbf18e849d96": "0x000000000794e321d00fb2d42000", - "0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01", - "0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545", - "0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746b4def25cfda6ef3a00000000": "0x4545454545454545454545454545454545454545454545454545454545454545", - "0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01", - "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da942cd783ab1dc80a5347fe6c6f20ea02b9ed7705e3c7da027ba0583a22a3212042f7e715d3c168ba14f1424e2bc111d00": "0x00000000000000000100000000000000000064a7b3b6e00d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", - "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da994dc96e49150ac7c3ab5917a8d347ea0aa7ca70cae6201086232336a1535399c34f372320c0aa15d68c4cfa493079f27": "0x0000000000000000010000000000000000407a10f35a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", - "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da99a0d9ba64d584162e7d1fc85d6d19ad1005203395fd92c2b411136466e88ef74dd6327c0a6f32b3be7e38d56a2e1324f": "0x0000000004000000010000000000000000407a10f35a000000000000000000000000000000000000000000000000000000407a10f35a0000000000000000000000000000000000000000000000000080", - "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9a1e0293801ecda3bccddad286cfce679fa6a6474ec1a9234888f7b20d3978a706d386d0f16344765faa48cb376db331c": "0x0000000004000000010000000000000000407a10f35a000000000000000000000000000000000000000000000000000000407a10f35a0000000000000000000000000000000000000000000000000080", - "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9e39abd9d6d25130391c9ff6fc64a35ef18caf23bb6b8cda7f3f90c1dc2b2f6c2b7143b13c956ef0a12dfe486e83cb758": "0x0000000004000000010000000000000000407a10f35a000000000000000000000000000000000000000000000000000000407a10f35a0000000000000000000000000000000000000000000000000080", - "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9f4c6172605184c65d6c162727408dc0be4c1d81c7e8df384e1ae9667a668825c56e95b0f7d3b1ba2f7539d4c470abb1b": "0x0000000004000000010000000000000000407a10f35a000000000000000000000000000000000000000000000000000000407a10f35a0000000000000000000000000000000000000000000000000080", - "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0xb9921c77657374656e64", - "0x2762c81376aaa894b6f64c67e58cc6504e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x2aeddc77fe58c98d50bd37f1b90840f94e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x2b06af9719ac64d755623cda8ddd9b944e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x2b06af9719ac64d755623cda8ddd9b949f99a2ce711f3a31b2fc05604c93f179": "0x104a611c52c43142e11767e4443eb56b908babae266b4f446271d11ffaaafbb16ece83a2b5c733f98b4018856a1fb0bdf0138dd883cc93a883f97de48b762d6b12ded28f03696a0c9f9dec223f3cbc44c4895d8b243ebe5cee12f9f02bf0c5043c9e3e67bfc0daed31db022fce484b2cf0d757e9aafded1988293da74301275b38", - "0x2f85f1e1378cb2d7b83adbaf0b5869c24e7b9012096b41c4eb3aaf947f6ea429": "0x0100", - "0x2f85f1e1378cb2d7b83adbaf0b5869c298ef7dc060436e4ed803af07632b89b65153cb1f00942ff401000000": "0x481a2bb5d6b9d282f3597f76299e767b1bbf06577a886d6def364451d4a95a5204000000", - "0x2f85f1e1378cb2d7b83adbaf0b5869c298ef7dc060436e4ed803af07632b89b6b4def25cfda6ef3a00000000": "0x481a2bb5d6b9d282f3597f76299e767b1bbf06577a886d6def364451d4a95a5204000000", - "0x2f85f1e1378cb2d7b83adbaf0b5869c2ff3ae12770bea2e48d9bde7385e7a25f": "0x0000000002000000", - "0x31a3a2ce3603138b8b352e8f192ca55a4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x3a636f6465": "0x52bc537646db8e0528b52ffd005894a2041e01468413521028ee4c3a03850c0b278ae1c05fe510045e30c88789ed5828071801ae10048d27c39f9eff086b9b6f23fd1d87552800107259f316bc3f938f9f81ae1d78d1903cc5c228dfe9c050dc7b93bdf796322599028915a61196126d79ea34800c255ac677be934ced1499782df3671103be5364d28fd4930a906ff7cf6e8bd4b5393500e74f1202e3cd66bb25bdfdde7ccf633fe29e95e39d5f8e97ce19c5a7f7f447eae9eeeef3dd67f77bb76dcc493ffd9e19cc6f5bf67bcfa9057250f9b71fe4008c202b19f9fd5964c23fc52622d86cb62b972db7acf46e3fd821b7d1fc36e296f523b593b465fd476339deee07616dc4b71f9ce1d846fd3c6de4df0f6ba379fb5964e294b412c185aa7977be77a78d39994f7f3eb551f3ba6591c9b4e5e4a0b64b7f19a8e6735a01f2e5a072bcfda0d74625100aae8cf3cbf0f697fedc327ed0c924e37c05cc5fe29695f4f223353849bf212f799a5bf3fbc1194e92de90973c4daeebd45d2b3deefc7e91973c4ddb75b2e05a09effc7e1c5ef2346dd7c9826b85bbf3fb7378c9d3b45d270beefc7e1633a036db9d8fd46087cc4be697fedc327eaf8dca79fdf94127bd96f1178dfefc2c5e405bc6efd66b193f52831d4eb26fc84b9ee6ed5ae1eefc7e91973c4ddb75b2e0ceef9fa20246db9d1fb611df7e163398b63b1fa9911aa9919a496e193f07b55d1a2f03d57af6182037c85c13e37c7e7e25d1a27f40d3e695a489246e24d94ab244122592dc923891a44cd11b49b614bd507447911a454220f9a2e88c22348a42509446911c48d214f9501444111d455f8ac0147900091792358a862882a3680a24531471a0a805246f14d990cc81040d247420c903091e48d61495510404923b8adc20f1a2c88c2232908081440d246f90c081240e24669090416205923690c881c40d246c20d12aba02899b242f247121090b455b90d851c4a6284dd116456314c581a40b2468909c4112022475146d21e1a2088c222a8acc24c12a12a3488ba22f8ac2282a53d406899a222c3f27f870116405e48b4c0b912000d0449028826c395744741c01e3481547bc38b28634e288185cd6912fb83a9ecc6b7930381dc0e1008e961e2d7ae0003a1392f14f78d66c06c70188866f8aefcc3c63aa31e77818de082138cc1626b063ca118a11e4871f32260833011113a099e0cc045b4ca0c504464c70c4ec61080e212708c9d236d0db04369c159a061f5bb40c568b69e342d03708a11282c6871600a2f82a8ace04e182134311acdfe1ad78153e88bfe157f8241e8b17e251f81f5eebcbfc97e7c0d0d6bf30d4c5501443594363866810fa3244c4501148a818da61a8035fc39f301447cf143d65becbf7f0576058de09224d1059828812b32366453c1244789885a95d33232640629685238790aa1f567e35b44576466685ac0a1c15e81c436010a9304447ad6288d6903543b6866831248c2169867c3104cd102b867831648b216a868831a48b21600ce162c815e11a4249082121f48690182130e411424f08951182c26261b5849c1022039484101540480845012446480a202584a6003a42c88cd018a02580c2e4a090a3454809a13c84f010a243680e203184ba84e410ba432804424c0875114283d00e423e70250835d1d3069011422c0861cdad203588b8217246cf193d680455116486c81a3829f84843223181160e17223b10f152ad009aa2c895213986cee80941112c3331403600d500644565b06f9c89a1376618cfc6bbe1acfe8c08ee184ad37366e80ccc03436e43a8a074f4680d9102b683c5a3c78a508d908d212d863cf1d3c64c882155846d009d300404302086b6f859e3b70c19d383054c871fae19113f6c005d195266081343cc0c89a2a78a214df4a0015f18320550094026c4de0c210384a5278b1f356279f49881fd40e5a03b0c71e26db02f301f864001f322e445880d1c08c912fa41880e3f050c4c111b28e64b781e46aaf7020be32088208f7721880786e6e81963288f66c189a8558d058435abe1278c190d311d462484d4308af1f1c30f184fc6cf18432efc6cc572188241a807211d7cf4f023c69bf165fcb0f914be852229fc1213042181580fbee6a9be0a081e0038c1d5781f22c0e38b8009d143c79025e81943b21863800d21f40656c40f1c436aec1d30227aec80bd016b031b03d605901b8ab470573d6e0cb9111a6202218e702114840f20261842c80c426e98d930d4829007848410f2c20718528c8f21c62992702131a3e7081f5e90a4e1a34b511b3f667c5570577ca2f0c1e203059984cf989f363f5f08754186f986f819be309f98a13486c6f826be0344c8980d31f4450f153d5bfc11301e6c1e40290055c9da7c60da04224504b501e2d25d7c404c31b80e10ad80e40cd81148b2f8be3417afc35bfdb851d7f0127cd2206133040bda45b3fcd657401c780f5026a80f4031046d016d01d2020443510a8d03100b3d5c00cd00d4012246c07aa8b61f33640c3721888ca03282d87c37a017820881440bcf010805ce8e1e2fdc8aa88e590c45687ad2e821038914456a7abee8b9e26789cf123445cf9a1e36453d14b1f0f3464f9b216d884040c404d18da889b103130dd00af54acf56671561d1a3a6c8861e307aca40d206280b123186b001ba1a42c6c7d0d385102c402e246921d68107025ca2888b24b61e357e941872a69a415206500b40321459118b014914496010fac0cf1a2120623c8c4cc476f0e183100f3f5ec470189d1893f071c5e8858e31761979105a414805a11484b408a120748277a9587c98c7eae9ea71f357cf042582f6e05544cce89903266602377ac2187263e88e1c3886d0bc14435e501ce80d425c7e082015605d80d4e86163088d2036681b1f85bf41b982cc781b7aeab06c0c750dbd19b223e88ca13080e0701bfc8608de0c893114829e3886d608c500fb006dd323076844cf1b435cf4ac010b6288cd0c852049c08c182283560d7105b9a16c1e049f45101543cae04ce8e102e2ca39c10716a2d5d00b6411454aa86e0051f598d123c6509bca06908d4819f6cc58424f9abf62b4fd2de4a23c80508036803380348059a00ea013e00d6013600de013200ee00ee11aa22d360dcb65d518aa63888da13582b882e40872e3080f477c38d2c5c7161f2dc8e29075c9e438d2c2110e1c7161c8367445f66656c2cc16048d206b044983481c940c227208591169038819d40d206e828c1184cd11218e147164082279fca0f0a325e887202282809099217343e606481040c4001162c88c213c86ca206283680ea236666d6668ccc8808d8191814101d405f406a80e225510d9828815416e08e243101c80e4000408203ac84a90a120c342a4070743c4072220202205112680d4b40f405c0ca9e12c0cb5e12b10214174055112631651184468106d11b540d4012217884210e211de31011e445413e4f139bc12ff8117f3543c11cfe55978135e86a7e1a178338fc363f92cbf52c5f8dad427be313e2b3e293e2abe321f990f04df125f0e5f0f5f97af86cf876f852febfbe123e2e3f271e083e17be1bbe1b3e1a3e1dbf2b1f0b5f0617d1ee0b87c42d4339f0edf0cdf07380e7074705ddc1b9c1b5c159c190e0dee0cce0d670657064706d78663c38dc125c189c19de1d0705770487062b830dc119c115c111c11dc109c105c101c18ee039c07b82d2e0c0e0cee0b6e0de705d705c705a7864bc36dc169c145c141c18de19ee09ce0a8e0a6e0ca70645c0cae06ee06ce068e064e062e0b9702a785b371287027702b703170581c0cdc0bf54dcda3e251b92a9b9a4645a39e315da83cd426aa13b586fa43bda1da50b5d4126a0a3404d40e1a079d81a641d1a0773817dda26fd03aa819b40c2a86b3e16b20e9a2705027681395066f837aa1587405da855ed114280d9405da8297416ba043d02f9403f4054a4583a042d4351406aa023d81264191a046d030f4082a866ea139d01f681114086a03d5c1dd7034aa09455c1e858f71285c0a47a26885ea8307f1055175f00eb80e3e43bdc2b5780a9c0b5c0bed45d7d173f417cd469bd170349be6ea33da8d2ea3c1e8375a8c5ed374341a6d4787a0c9e8aebea3c3e831ba4d6f751b1d47cbd169f41aeda6d5a85e2a1deda57f681dba868ea1b934077a86cea13b507b987974d56461de314330ed98757c2ecc35661ab5cb44639631dd9874cc37a617138c59c5e4626631cf7c66e69aa935bff02fea0ef387e9c3f432393061b0124214d4531981064cac07cc2a077ce0c1930b5880011b2820013306240468e0e40018346162800258305fa00089d191a40f0d705e9e3b467ced4a5050309054058ad2074949e074294d4a4949af181e0c258a4a084e49504d4953aa5c0f9aa62c41896ac04fec0ea612951402568ad20485090a0692a844254145354da11214b383a1a43c41c980d2d3942a178a890a950d9aa6804025013656479791181d44b1399898a0446162825200393e88f2a04989290e5652ea20894a5452d4071e4c51cac02c060743314d8112a524254949a90309626f30950e9aa6248df0a4148253d2942a37aa49098411a06cf0313758294a1395a62428519aa82420ca0d12e39242258a929408626d30d4084a4b5023283969001963036a8a0146b8646c0d0a626a04c5d26011949a7ac4d0e033f8832854aa28c5626ef829040fa674d044254ad2942a57090350683d3133248895a12446064b999214a504f51442941fb1360ca534256aca13950fa2428092e441d2942a174a15294d509e2e101263c350504a4c416263ecc4c4d8e2a8a610a84449ea008a12534f2c0c7e0201041010901303833ba042e5a94a5295291f403d293565804ad2084a4b42b12f588a52930753a09a428842c5ca94109c9492a29ea854517a328aade12825a8242f186a0ad4081d28296120694a953b42074a4a349f58170ca514154215a829527c625cf00750503eb03135acf4542549e9694a084e4a4f49501d58a902144bc3514d4a207460a58a2cb60543294551b19294d444e527a6058f80812434504c4a4d4d503e8892540584a70f92a434854005034d53ce4435518992e40482942c180a04284f50aa243d51c182a9740024a6c550514d4a5694ae600f9aa6486952aa92a4140545ca14294d50a2148959c14a52a8440912ab829fa87400a5046586a1a242a81215029521312a4008010a0a8a506c0a861205454a521395295294a2a0a6403d295591205686a1949ea844491a216a0ad3140c40619a02c50a948f9161a82a215079f22049484c0a867aa21205450503284f503680c5a2e00c4ca9729f94969ea8444001640c0a56520a4129840538408a92071e4071c0506c0c432d4551fa20ca130c15d5a4f4a4f441d3948f39d134a58358ac098e3205aa69ca5394084a49233055a90225e949490ad39410684b51a830d17e62378602126382a124fdc440c0202441898222054a94120274624b68600a13942951539aa6f4c494e0a52854983090a4141542142511947ac492e0272a0508792a3161004a5453081d244545517a9a22a5694a52d28f263b591dc867cab09d21a3eb720de49bcd7667cd7f705f1a5f1ab3caec684c285663b5c66aadb50a4da0ee6ee4aef38e763d3277eecdccccdc51eeee66ee3a6606996b771cd31cf6ca1dd3d0f30eab3b129feded1030e2a0bb3bc88ea4411ed01fc003823ddd7bce6ef72110e4ced199ed808f7dfa00290fc875ce5c77fb9c5f03dcd9c13ec25d77c43b4a3d2fa49476775d73c8ceed3977cd4e2973ced42975dade75ba7bbb7b5d1006e2a0fb6cf68e015d7b17c21848c83427877ae8ed79d4d3d1e9605de7dcb5bda3edeeec614e377bb6c75d91912e80739a67d8711ec8cd3edadb79d28675be41b7d75ed7b9b333656705544e00d8b5334936f51c012c343570ef3aca0eb213a20988ba09dc3fe6cf9d9939ce39e738e6f873671de73866e738678e391c6ee6388e73c6e1388e63678efbf8e3188763ff98a773cc4dce999bd971983f9c8fd9793abb73b3e7d15a996b4729f53ca6ddde5c6953ea850e80d8ecba7bcec9516a01103a33ed3a6f721fe0a313e4c9a041c76036051dbcc079dcfb0765ef26c8ee3983dcddd81ee49ec7ee79edce1eed3af62031a849539fee5c698e7b8e91168570c784727777eededdee4e475a8436a5a304eeafaedd3ba75d7b03754073769d01e6ecba6e4eef66e798326d8f36b357bb4177aeb53aed997b538f9967773b13e18e1974f77e396de6d9b9778e7b74ba479d763b1070086477afbb69d775dd8feffbda81747b7beeb9bb33b3b37b17a0eb3aaf0bbb473a76de799e4723e2ec5d77eda60d3a77333373d77533e7ee66efbaeb983d9e2137737787eeeeee733ab377f3f4ae767b77b367d7753b3c3cb6eb3acbc37d4366666f76a7b4760c32651e42ddbd870fb377eecddeeded3d643fdcd99d65ee3dbcdbf33ccfbdbd87f774f7e93edda7cfe993b677d7deeeededdd7993ddbd0787d9dde3d9b39dbd2773b777ee3cdbd99ddd9b6958dd27fbecf6e9dc4e296d66e76ed1bbf6c9ce4dd9b9eb3a77666fa79fe7eed51d67327b74a7bfe6a8736177c779dedcf5e8e9c13db9ddc7bd63ea4e43cfa39ce879f727b677ccdeed941964e7f666e7913bee98c97677dadd71473def80b477dc75dddd5402ea1dd376caccb0eedc7b3ad85dd731e8d4636ea79d7bedbaa6ee4e29a5ee79cc5dd74d29a5cced4cb99d3265cacc4c29336577da75dc31779d3be5cee36e6f88d79dc7deb1333ba5eedcee94b9e3aeeb9cdd9d99b9e35ad9d9a937927b3b3373e7eedddedd59d075ef79dd7973d68e0003a8e05f0015d6755dedba9d2501e80aa3d5ebba9c6e6fefbaaeebbabb4ea7761d775c531ad2a6d49bbb7677e79cd6aebd634fa2459234dec33dc21315109440e03e26252b4a4c4922400121821f1e50614af2a0698a071e5061fa815305042851504a204c89fa1f0c84003553824a02818a9426284f16270a2a69e9894a06a2949a827e40c994c88ea4344149527a825205042a497a3041894285898894294c50ac2441011949517a5292f24425c98802244a539ea28848f00309152a4f4096a240c10013139527221440517a02f223e4e7474f14d4141194b8a82850a09aa03c4525454d0101042a3224533a507aaa627d624d4a55aa40997c040421aca4f4d441d21395282851a8303141f9e123204c896a0ae1c9fa7410154210104268f21141e983a4a8a6294f5592a2404d01e1a72789511011a864204a881105487a94203992844469090a1306923c689af20114940f7c88a024a5694a52d314281f44e9a0032a9f044a8208949692a244506a2a124194920712f87882a2c4142549c903284f54a2a092a4304df9204a52145308503a509232e3a29ea04441498004a8a8c9832950a40e1f811a41692929ca14260c845045f60425f462245294a0a0949eaa8040254ad29392152a51949e92a0404989a2947464c988022455a228411d7d1f4489828281a40fa0a6344de9402664840ea63c295901b2d2a4e441f84852139529504f4a569294a2a03c2935297990148023145080448a521295a82a233c45498a124149c84a93921fc911a33faee6dc547d3de15a8deb6ab529ef09d7a6a65c7c529bb5599b3953535da3fd646ad6a6a6dc9f4c4d6faa3635459fd4e6d454835dadd63de1da543fe1da54ad466b944e4d71ed7bc25353dd93da9c9aea27b5599b9a9aaad5dca7a6fa094fcd27b539359f706d6aaa366bfd646a6a8a6bb5eec9d4e427b5c93de1a9a95a8d3e999ab55a3fa94daed527b5c94f6ab356ab798d3ee15aad9f70ad56f327b5599b4fb8369f44a0663199798200058503f4b9dfb2e672df6dc0010b574efbe1b2368a71ff41b2271f6ce15269417ed4b8b5481b95dccbb8b2f78e41c8c19336aa4c4683369ab73e110b636efd249512ddca68c8dcfadc463f6ec96d6e151550e576dffc51dced0769501bd57fea293a49badd7bdd377796b9085bf0e35edcf397b3fb263ddb911c59d62eb75476cfcf891974dd77b624e1f6f3e56c396f37e90fcef66f2793b34ee677fd1f106beb72649d576ee93ddfacc68c204f1b715f9f8a17785f92f02683f99c2dfbcaf51ee4b6791d89e4dc953bdbb93c376059c1e5a39ce71fdb487c7e9e367a3df3ff7c501b79cf2f6b23ee6bf7458aef913510041fa94332e6fd7765bbf4c513b807b1409e5723dfb0b22e7d903e08d666f8df073e38c177f20b3eb81473d27da51d4ee7a4c3e9c0f71efc5ecff77bd926de7fff695dfaafbf5dba54ffb3e5bcdd87ef91f37e2e3679bd5e2fbb54bfa40f55530054ed86bfc43d5500fdce96dc87f67b24bf9f07d690c5d09652377c8e2ce9e53e87f4b6623e326954396fc57c84fb4943f290046bc891fe3924d33cd2c96797c4a72f3e8b4c92bc19c5770e7cce96e28336067ef7d5097dd1c640ebc47bee3d90235fdf2d795ee7c4ebbc0ce87fb6e41e7c1024cbf063dff3059f23639f6d521fe49e2f16984b1f7cb0c3a5d783b69cb7e43e7cd0c317197e396fd862133003d0321b29c1f7ff2c1b29bf7705d0076df97aeec179b979e7e56699a482cbcfee8553007de5f2e3f012befc444aa666edd5e10a2b90f12e14c66451418769420836059b9c0f5632569fbbeb4f8e8cd59fb7e34e87be04d7bf7b26eb3773cc31d7cd676e4e92d7dc49f67724f748ce91f582959c0fce89659c7fc5c34ac35ac326e75726b384713e58c526bc86eb6438b39244b7d98e2d93e07a8d9b9e05c8538591beaf30f6072edf60c8e1b60d976f2e505dd9e55b0b4464108585eb963525b85b86c04214973f67ae30ba13976f1d60e3ce31976f31f8708b5cbe71c9628a17bcaad8ba4e4f2e570dd7690ae79651638001b3e196515e6c00c7b865140a63d45b427151c410125ab7840aa375d9b3d9f8cde5bffcfd02e8342f7d9092f56b4d732702fcdb96371be2b8f4e9cfef49c69cd434977e4d734ba8daa55f49be4efce6cd9bb276fb6b1c773ec85246beed60e69297bc60874d46702b59d3dc295ef0567aa68d6a277ffb9d04bbc50be69beb34df541bcb60beff05f3dd46817cab81eb46f5adb6ecb732af7fed3a799d246f478197bf5667df01e64e7bfd27f7cc04e5c8d9936ae45b0a59d7bba0fbef975eef7dfdf0fd418eec0771c818cef32d6b17e743b2e438eb9473ad44f5c5f916c918cecf9bf3200d9b2cbdef079b8c65406db60b3ebd7ec16fb26f8b17e0bc48e67c48e23c477a2892f482340c4996e38aa45fb08621c921b822d917a43db97694cbf9fe90643caec87536c58939e127debcb9fd7ecb9a1236dbed90e43597239be43e87141fc945b2f252bff7ae418d09efd9f3fab9c9fcfe2966d0ef3d8843820fce108764362ed8210ec96f5cd0431c92e3b8200d39127c1c12aabb56fa7af0342ff8f5ca051fe4c48965049fc559c248c526bce6820f72f53969cf3939201cf0798cad848b43125df07b7d83e2331bf940f1c38f2cfdbe3e7c85dfeb41f2291445eec3d77f6439efcb321779d98fdfbb5cedd8326aed750b6b9938bb24c011467af996820b238cf5f22d052ecc468044310c45f1270982288aa2f81c247681e0eb05823f4910401004c1e720f08deff3bceff3befbeffbbef778c9f71cf4d9a571aa415acd014eb1b95c58cbd848d7dd246d59e7b191ee27090208ddd38f9730f1784919202b8d5df760ce057c8340ce9d737776e7bae7c8908d74d4631af99602d5addfd147ea8efbae7b8ebc80eb5a07f002be390f32c95b2eac65640b97d908cf6772de29b2919c908de47c934c23ce8b1f3ef84ce3ebbff7be7ba691fb5a7a3727e7435ed29ff3222fe19bf39d0306e80f3a9993417781b7f871dfdefbf7de73ee1ef7819df74e5a99d7f3be9bdc8ee3fe6eff7a76c9fbee594480f7d9b276bff94979eeee3d923b49dd9eefb593dff6fa913a0363ff0473989f8cf9cff76f32e6ef24064897b948d97df74ef2edbef39ea2c22ad77b16337872bdf7f8828e2cbd5ff2ef1ef448fbe4c45c36db8dea715fef1e15ded77bcfa202aadcef91da239f9c2cb8cd46fca3b8ebf5f5ffc827efa3b8fb7a169d24ddef23b8df7ba4930577b291cf3fcb5cc45f6c52dff3c8f95d392f7f25cbfef92c5e30af3b39b68cbfe6cd8e0a90ab8ed707ab3f38c57e1ad8b52d6bb7a754c7ccfc1ddf493a49ddd939f965713e528339d3325952cf9b4cba3b756fdebcb9e37a4d3af9f5ac93d767ee3bb2ecdeca7899a8edca2dfb9dba3baebcb956bcf25ba2ef3ddb72bcfc4d86fd563aa7272bdd85aadd6999f0233993dc7754dfd773ffb24ef38e4b3f32aa2f7d1bcb60deae7f8b4cfc3992cbf9dc3b69a5b92eb9b7a593d4a55fcbdaed66e7d952ea7adfe4eceef67e42d52e7fbf9337e1efe79cfcd24bed92ff7c169d5027f3dd2ef1377fd3dbd2b2287880e5d8a296db3f194b5e32ffde449fd436daee9cb6cb7380f3c11cbee1f333d1fdf8f5bc6486e4b311a2cbcf65e091a4f79e23f524f94ef105be9a7c39f3822c32e14e676cd9ebbf077f8a4ca62d6b777ee48b043dd2cabce5bcdf4fd2cabcdff71ec99782ded31d675a27bfb1267ee74ffb6c847f5e8ffcec92f7e053d1c9bbdc832198c355277cfadf4f5184ee3fcb3f418f2cbdff5eb6acddd764f2026d2975c19fe4477a4f2e32bf3c408d89cb5c64fef748ee2475fdf317e9e4d79f3f3203e378fd91ba49be1d69e472a4478233f4c8b2fefc8e8c4deb342f67798ccd76876e37391e40c7baf49d741a97ea4b70cb6aa79696f1e59bd518706a6923bef49578c913d1edc25fe3327e3eb3e52c9595111af7573b342d6dc473e90349d235964c776a69197d223236a0e27e3e525319cba9e5d22f998c4b5f8a7ff4991c331c61c31160b66c361af79436491bfd41851c4ac061091a959511dafcab1d1a3f9555d3babf22006d3e475e118026f5c501f3a536f019fda975e9732ff5c501dd73560ac967f43b2bf5c501dc4f2bf53ea3cf5922bedde697acc5739850a6cb1a5df82046167fe95350c2c8b4da0b6696a8a18626d40801078d69fc25d1a5cfa203de66a34d2d6d24c1a53fb1ba690d20f39292a8966394c18b7ea574ea7a6573a9ad25d7d1ef6c13674bee993c08ae5b2df7a5db52ea5277c256ac7039dbc5ab5f4e5da6da0c976b1a00d5edd62fa92d45b8dec45660ddfaa5062ee56c13b7b9d56a7901c104699ca05ac0dc9fcb372d2e5cb0c34a7691e29fca79f3e6cd0cb4af8bcd078c20b09c61a33f9dfccd9bdbc51624851d5cc87a12cafa00195b54156e73bbd82648e384cdca00a86e22d8d80a2c1a7fa9815b9f5900cc13947c3bc1cc8d9ab74b09dc86f6591bb7a1b195e29f6aaae1062eaedc61b3d13c6ba3b2d968fca5d4bb2dab8034f4487e4afab7088109d234f186a62306183ce481e5cd1b1a3fcff5e696009d52b2fe9c9d57ebfb7be4582bd7e11499740a2ed69f976f27c470612dab4fc979410fe97395ce4bbbbf00f9691bf17bbcc4a77befdf3bf7cebdffc7bdc7f93bf7debdbb831cc9b732e91790ba9ffbc97d8cfebc7d639403dcd78ea3249dd76323dc6b805c9ff212eef92b2fe1c8242dab285ca296d567d14baff4de8f5e5f5fe647e0d7ff6923da32efeb2be125cf46bcaf0f9263cfbc7f915ecf3c1f9e1fc17ae67d7d581b7d5f9fd5b865925bdf072f99b73ee7bd5f712477defb6751737f5ae67dfffc297a1febde6fdf58679de69bdb3ddf4e58e1f2ed84142eac65de734ef47e965be65536c2cf226523fc48cd91f34e75adb60701721b75fed496f3d2b023e7ad17d4a7df95749a6f9af8d34f618b4504f8d3f79edad2bbb4fb293a894055c215db752a3310023cde5ceea16a97b350f5b9659d2de97725993caa12ae5ca7f9a614a145279e2d450059bca03e7d2a36e19ed948593b725e8e749a6feefcca84da5806fef4fda95deaa7d649ea967ebbce7e02ec1e9c2213f0c3afef91e2332fc179ee411cb249ed96e183cf62130f67c97bfe902cbd9f0fcecbb713a86e65235c8b4dba5f24bd7f91f382208b08e8ffbe17c9ef5f24cf6791c967cbef3bfcc879c1476a90c9f041922fc8e2056ccb79f9c3073b042f785d6623253f08ce100c1fa93904498f0c5be6bf34860ffeeb965e06de0dedd2f70d92e5f7f3919c4527fddf149de69bfb7a2447ea17c9f723f97a7c39b29294a42de3703a1790a18cf44bd8ad9e9236aa44b77efd24f5616b6efd9f3e6a5aade0d622b7fed846fdb5d6fa1c54ed12dfa640e6c93ca580dce9842da33c1bb451f92ec46ec9682efdfa9d57ff76dc97ace67630a4362a3d0cdaa81c3f68a3ee2b68a31fb77b0eda886f59c2dcee2bd04794e6ac86c7dceeb98d80dc92e7964a6e571fa97fc0263b33f3cced1eecb0c9be302d6ef720f764c7719a6f2e77cfd346f4bbeebbe7a0ce2e31977b01b983916f5ae2b8a577ab41cdf5e7eb955e7b4bb23632c14dcf2f613f3ce6368fc3aebff33c9a34ffd1792e009963622c4322262f09ea11641c708491b68ced738f3f2561e414d09f834abe997087fbed1fdbc87fdcfe0f6f39dea0b0653006701c380ca1668e225a60210d1aebe84cd97d256d548e976f573adcfe227dd4b4fe0a9c9c776c1fb73f9c36bc7cbb72e1520b6bd9e49b1d204719db0b8540a6c0d80fe274012fdf4c98e1c27e402b4770c99286096bd8d042e3109ccf41251139c1495b36692b80b60c0459bc80b3e55fee3dee7468cb6c19e5af87b511f8fe3f6dd481ddf7d9f2fbd77f5dd8b2b18dbaeffdc18e9c17fc5a16fe142ff0b9a12dc37f7d47f20dc928bfdf83b3793a4e47f20549beaf0f76e77b7e94e569dc336623e5cf9db6e4bebe3fe87372245f0be67b6454bd33043b14e172cf64e9b9c89dce7ca622579db065412df3effe5bd65924679292e5bcfc1c19e5977f9263cbfaa7c8846db55ecb7abceff911b5d96c361bcd5fc64bfc7ad07dcd4a83bf930f1845088ca5574d70e1765b8db9fd3f9584b56c963f773e8b0f18e7f3bc745601b94190c39a036111640c8c7cb322e2d6cbb7aa3597756e93d2283083d165393e1730b27880daa5d47a764e03185180a41b1877e6fe0ef95bd4a07697d83a4d5dfeb27f9c4954dd2edfaab26ee95dfa43976f54715c0a722438aba841edd20be6e5beac5dce7a4d5270a53e484526d5450d6ad759e43ec6d926f372cf59af65f3795e0ea27669e4026630bff4ef4926c9634e6e02f20246ae7a7f5b7f5ab90bfafd919adb28d6a4dfdfb9b2fe923f7d8e7baf8dfa7237901730d23badd732faf4c19c0b9ca62e656a3100727b8ed746fefdf4b98d28d822133a450d6a949c9745af00c86dd41e3209b29291def082394cbdb08dead35f7327f9346f7fbf93f45964e2342bc9372a37179ce124f936e924df4a8a34ac24dfaab42eb751d39fefb5d1bcf35b6432c3cb62136add7a4dd6e89c50801c446526bc446d5456f06d54ca2a6823fafe45daa8bbfe631bf18d4a89ebcfd3474d0bea6e99e4fa8333e43e1a7de6cfcf4cdb68a97f3e4f1bf5fb731fb1cf9cea70cbc296cda70f76383f9ccf7dc434fef076f9a7d8cf62937efa19f4d3b08d9a6f6d05c80df27350396db7795a8d7c3b21cc9db6230f99741a97fcf996d3d6976f2790b9f3ce9fa202e695cbb69c36fa4c5aa1979f924e722ebf93cc4b4afab10c5e975aa79ccbcf692ed12df9cd2dfd9b2f39b78cf3e7e5a6807f8a4eef345eb7d3d654800c65ec2fbdebfeeeefee58bc5c7777ca41ce8485aa04354a00f36dd4df2f6ba392deee0fba5db9e3c8695da46ff973fbb99f0ad194916f5794b87cbbe2c22d65b7bfe8f2cdb67559640281b11c917a1cc336e2effa971fac245f27e70559c9382fe76125b99f2213e79e450d6a9703a798417dce96f5eb832d6630defa2cd296d10769e8e4bca0871c597f8adcb292e7ce074116395bd62ef74e965e7d77b2a4977ba4069d2c0d70fd41163b5b8a70bb77b2e4dbf5af64c92d13a1ca074f38b8fe25b7acdce0fa971583eb8fe495e4db914efee6cd9bae5bdfc9b2824b1f9ca193f42bc997450574d739d2a9bbd596f3ba759a6fdebc7973cbdaf5a724b76c5adab2f94497816adca45740268232f2cda6753d32973feca349e387b511df6c5c2effcfe5e720b600e425e37c7efe4090db88e79dcf0fe6701b316da37e7e708234e436e25b7a9759a7016410d132be4edfbfdf452601e12dc77b99244bd87d91e506ecb5b1a90c5dd7755df7484db9e3265d13abb94bdd7bdf91fef4bb0ee7f98b1025e125e0f3fff491cf98d3438700574febe1d9797e591db24bcfeff08c3edf3a3b0f6b59f7fd3d48dab2ee77489e9675ef437ecfba7f91b296f520f9ea907589f1fbf7a31e9d570fe9f5ac7b1d726c59f72349d4b2ee73c8242deb1e873440cbba17490c5ad67d486ad0b2ee41720336d23dfb91929e75ddcf9f6293be2fcb2debbccf862de3efe8dce9ca033867b73ba515e4c2f99dc8040666645bf27010235b6e8a19f92b0e7b62870ed257fdb8d0f35ac6d4f3affe4b95a77799993b9c52844bafe75dbe3c69fc92835207bbc9fd6023954a39a5e11bd12fbd861388efbe20835d05823cc01474e97313f4c227ff817ae9772213fa3f2de3e78eabd47bb287036323fc55c624a2682dbbaeeb28574639e8c20cfb04b6250657c9a53f8718e7334f268a1031ceffa15fbabb331b296597614a5cfaee3f8b18e73b138fb9742ae1484ee9bb130118c167ad5b2629791a899770c046c42f1fe74bd99192227d24f39987dd336def3d3b9918cbf10523fa1eeca0fb7d8b4e34a0850f5a605d2622341ab44ceb4e9cd167539c2139af48d625c65289123fca797fa41c927d36c3cf8f787ae65df8201895dc97123fa2fae2bdd5d372dedf006df4bd67e4bd3f066dd4afff3eb4e58fbfc0772625f77b179d4895d9a20398cbc46a5e24bd2c7eb665391da964b256d8dc11debccd57732bac8f30f0597d253eabcfcf498cf4a55a562b12632d62fcc9a58dfadb46cdcb648636eab28068a3ce7601d34661da28d653cbadcfb71b7d56bf8e97b92c65aebac4581f030e78491363c95bb7fe13296de47dfd0f6c555aaa8d4a26d99ddf5f5f040d3c5b321a179970368a4e22d848fda6a0e4e9c8ba04fbe4d2f33696cc756bfd2a51a51211a617aa2ff4ad9ee65f7fead0467366b511fdfa15b4d1ec8cf8d60e03be9e65e2314e36b043166eb62e136f79f47224bd9464da14cce7e923a679341e6fa59545ae7f9db76e055a6b65e798f9613cb37d0b121ba9404905828c600aaa49f86541b3fc9f89c4e45a49ce0d90861388d2fbae1ce97931b6f5287ae7f7876d34c7a9c5dd97eeee1d13c980f87e7002c17d7f254bd8e5ae563642df7d7264c9c3b7926578d94bc246ec58e500e9fc26a7065b6ecb0daeff244ba23be79cb3945d0d5ad6ef241311a3b98dd4b2fec963ae920ae65473c928a164c9d4c45b7769da1a9d2e46c9edf2bbe781558f49e879484db9839e954c7e1e58c9f1b48c83fde5385bc92ebf299d5c71fa5964a2c49b350d0882fc4df21721bacd632a1074f9a538aafcb94c419793dc1e5bd63cd3d6971579264f279392cb4fbb9978cc9dcf4d4f81be3bf3d72f398e9fe35fe298df997d3ac13f8118e797f4b677a713e3fc19067801572b737daebf44bf6919e5b73efde1a95f524a2b69cb27b73e25e707c6f934aade5a2db7aca45fd6ba54e7176aa98fb58d4abf6077777b7b9b00f697eeee4fbd79bd96cd257e4b66667a998dfccbeebbf4d4d7dfbc611600fdd2bd979c924ff346f56527fce6cdbccc4bfccb26dd8b71deb65e543bb1e0d67cba0b39979b60c3c3bc324efe014ef2029e4c7ae19a73766bf1920758432a3261faf4071be9a7e20564155964422d4fcbd848bf93208c7ceb0188cbf363142fdf7a9821e6c49f3eb5a506ae5bbe7407907bfa7397f3b4e574e29bccbb4ad6a2b694b92d65773e7fffdcd20246e7bb1fc7d7e9dcf851cf8fb2b948e829bbcbe708e6e3c3fcb0cda56fdfc146b1c3f8acc1f8acdb8bcf3aebe685cc2d27176bddb26ff4cace2cd8f7f0f99deff9f93e4a0fd6c396ff3bb684d972cc6a2e5c3e3d5d3e226cf9f8d8dcf85cf95c71f960f96075f574dd925e91c1e30a1a101f2d1f1f981a18ecfb7bcb8f7cfc88c52ccb8000b13f7efcf061a90f1f21ecb1f44c988fe57efecc9a59647aba19e7d6f3effc54f39cf34cabe2f9a9f539e487669c36e727999e1f3eb42619355a55587adec7c2aa7c1e4bcffc614980591f1f3bdd40d56edfd848378f2dbd1d5b8e3d6c09d3b1a56cb4e5f7e7907de3aeae33626ee800196ee4409335192b35e838618d37b4cead0853060db63a92a07d5d471c3cd84cb0f54003d1a8c2a2468b2f96a0b1cf705e7cdff271738587125c58dec585da48a05924502f4fb44193f1a0e696f44aebf60d47261b5fb6847db68479b6fcdbd9dbedf6cb6e593e632f9c2d3d30d59663189ff1a5b6842179b9f98cafdbf22f989dde9101e4a092deaecffb84f1f1230c981f3e7e4ccac567fd3ee89c5a5d3c3d3c3c3e3e3f7c7c7cf8f800e9e9e1b1555878be0af6f33eeff3dd13fbe98000f998f7ec473eac8f07f2fdb0e0330d6649f0b124f4fceb418a058a4fafc49ca7b61c9df7aeefaeeee2ea5be7613c07f33dbb1722cf6b37ee5bf4ab7aacabe9792c3c3bcfb4aa1eeb5abf43522e538b8bd6eddff9726a79b965dbb888bad8bacb8fda8bcffabd11d65e6e3f68bbdad63bb6bd4c5b7b193dd838b5a697d94566d2fa695683e2e73c1799db5cb7b558cb8f7a78d4c07e039fcd2d1f3ddcf83cfb6cfc9e9e679fe13c16e779f659f8cd63d967e0833ddef3bce863d967dff77c8e0fcb3e7bbdcfeb48c12cd1f7532e7ee4c36ee0b3f93e3d6cc96aee7c1d5b7a3da32d79ebcecfb1e5c863d96738b6643777be684b58684be6baf341cae5f613f9d1cb96de67cbd123ea88bc6abdf9defd6a3dbac71b200781ec47dff3835d5c93f69193c65b97b5a8179fd9bcab118b9fdafcc83f0bc665c4babaed8dd4d580d1f223a6a9d9a26ea81730bdc55c7ee54755533b34ef3d2fafd04b56f8cae2f2555221172c3faaea1e0be759bff2597f671dcbfdaac3dcabdbef6a2cbf774f9ffbf92fcb6ebbb3cd59f6d9c4ea2d375c6e7fd9616e7f63f9d1e8c1467735306abbdd58358ca583994c7bd82dc7fb914c9bb7e9c64dc771cd1b98d945268c4debd6576ac8f8acb9b9d5d4cb6deac58f1a8828c020a3032a4c41eba760fcc85fb0e38e2e3ee0e004ad9f86f1a3698502176fbade6c41eba7373f3a22450c63bcb11a43eba764fc681e51470b57726cb1d19a4bffe4e2475293067ebfbebf3f0ae347dcf75709fb45f023d062e0b3c662dafce86537e89f577ef4590efa2796679f90c0d90f7cd6df4d5d0d1be97735b7dfdda77bff0de3a45c5d8df60885087ec4a0f77d39b95cb69ab4ef73741e879f5faf073f143ffcf173be9c5797df2375ec0ce366b4138cd5a4e588767a19f937b09a349d7f795f32992ff9067ea9e472f825d1e597f991d406cfaf434e32640e39d5e090738b9c6ebee7f744f099ff063e139f7f14413baf5e7662b52d617672f1d9b4e55fd6d22233bf9c59a5ecca3cf0fd89bcd07b7f259e47bfeffd31f023107c99cfe683f63b5b32993bbf7cce967c837d76acb65472e797a3e7336a4ba23b6b7f25801c54f66dbe7fc95d4fbf642d6a4bb6a5ecb22db9ebfae73096330bcc2db9ab8bcb8fd4a8d9da72e3a69c64fc48eaa77668fe33cb8fa4c4e7be76e1835f3299aebefefb926fb5f3befb52c92d67168e952241e4accc672d4502f7e5cc0aed83646e2f0bfbf816c6b363a7040c673df6594974fb55eba64f802307fb765a3bee719eff43efbf17188a3cdff3fc9ee3e48c3a3b3d3e7635693c3d3e3e803c959547fbf130a7b222a2c52c901f240f8cdcf1e1d343c278c81f7c64fe0ee975df8b8f802190d80f1f092224023e8223e68c3a3d804c772ca991bee8fbad6df5e9872fe5e0cd766b5b0e16f7be7b29f7a72f155aa9e6f29983a942347e38e0f87a291f5bcb672e35b5a4786265813abdc5552b35b97e82c9f95be8b6efd636db2d24fab8c07081f9beeb2d707ca6815f7696f89a5923ce973ce6d8c7b19e96185a1868c7c6faece85998d6d40eadb39ecffa99c659a651cfa9cd7e5501725039b9bc8eabcfff7e449f9f232b89c52989c53b52aae73d52cac74fedd0a8ac441ae5de6ad2382c4abfd297f2de6ad2bc97aaaf8feb5b4d5af557cfced6ebad26edd563a5a8ac441ad59505b49eb79a34a92f0ee0f99e97f261a57cacd5a4f5f8f3f8ef58a9b9e533ff1ed66ad25efe3ab68acffc3f2bf5c467fea3b59ab4ea9f635f0ac75a4d9a67a5443bb3a4d867fea1b59a34ce1fb413cb675a1ef7dc58b3e68d6a71cf5b441d735d7f7d39b75e766a8756ffb3533b34fa4cebbc679af7e504e3911dc99195a4e4fb6c4e9edaecaee3ba23fd1256fe2d651f9af1a93ef7fc2f9cf7ef7b4b82087ebf5ea025e10b9f3efb51684978e5fc7cf6a3aaf043333acddb91a155852f755544cbb12480389684b0e3bab82e3f126d95285aaecb67fea1ad0ac3ee2799ce960fda2a109cb7694bd8cb56bd1e8bf7552fcb617ce64f2dd7f5d9aaefb17896042cf30ded0b7da6557d967dd63fc1f8acb37d68c627eebb8b87abf642c60bdf78b8ba1ca735bd78f123292c3eb543f35eff2f0f06f3fe358eb79c5ea4ae8a681f1e5a342c4e02fbcc022e9a970fcdf8b76d399de87f68c626fd654766d2304e1ac6b2cbe2baca2e4bcdf4d2b7ecc87464fc687af199bfe78d230c76cb8eccf567ffbfb62eab23d365719b5b725a5c17a785e4969c8df3c2d92a1835150ccf2de956bd7add9262d13025c5aab7f49bbb712e7e6b2ee742a66caeceb291f172bbbc94b38b68dabe643544a5777d668dd38671defe727ae9b82e034d79b5da396ec01f13d0b0f8168d008bdf01a4439e9fa06e07090d4bdbfaad6647f31c9afba07904349780e6568afbab225a535d19a161f12e9b1547ebaba6f58bd63d680d84d696d644686da5ea5f15715c0115968e764544ebb7729a579a8b34efa179109a0bd1dc4af95f15b5d3da568afed40eadeb9ae31e8bc36ae5ece856eaaa88e67f554493da71cb3e9b3fb543bb2aa27193dec0f7a3abb19d3e95d5c368dd53598d34eec1e7f7fc88ea2a098d8ae7ad94d05e6f15d2a8ac9ef6bdf7fc303fa2bafa09c51c9c1fdf2a88063eff487525a3515929a1e93c9515116de7ad64b41e6f05a38d4f654544e364b4ca47e653593d8df291f9cc14cce7df2171f8c8fc1ea4c847e6eb90397c64fe488e4cc1fc1cb2878fccc72179f8c87c91f4e123f343d2071f990f92302e9aff223b3e32ff23433e32df233da6808fcce748207c647e257ff8c87c4a06e123f39d14c247e63719011f993fb533db0a90a36fb5c315505d11d1fcad28ad9d56fafc9df7bdeaf387d57a3839a34e0f7f7e1eaee08af6700557a10f5770c5e3832bb8fa81710557413fb882ab22b4b6b0185770e5fd7005576310aee00a26842bb87a5a3f7f045cc1958cc61550cb2257e07604c215b47d9fcde79fa2b3b7400e2aab960c6359bd5432e263a1b730df63e1334bd401e62a098dbe14162f7e64a5dc9a60014db4525236ce4a716f8205b4cf4ad58070c2846aa546a842e22a88298878439332c1021ab5525366ccd005172f6a5aa039fdfa5456218d0bf21ed7edbfe2a17d5ce584fc8b666d551f4f79de074ffd1ead08be8716c5a2f65d7ca92f0e20dfbe14f8e44bc51ea47fbb0d7b20d4f60f7b29dbedbfb908f4413e3f2ff5c5013beff352bdf352dd4b5df1d0fc430ae6b67339981f8ff352606e3fd7ed202b253e907d6ba546d24a2181e0c7ac14bd01b1521466a5fcaddf7c5829e6e1f91e2b45b55c2b024bb17460ffb2525488a53488b52a42f3fefb72bc7dde0f908ff578d1c773dd7ebd4ed66bebf67b593dff656df156cc4a890fc43ef159bf8f95dac067fd3b568ac867fd9d95b22a42eb3eb452148c73399806f3c336d7b3161107137df8bc8ff1b99d9c1fc1dcc6f91cacdbe2e33856f8229696cd8795e2dec7ce1e56aa8acffa75acd4cc7a5929df92f2accf4a75568fed2dde62aeaccac3ed58a92f0ed0f91e2ff5bdd45512daeba5be38a03ea775fba5be38c0fbdab75b0f2bf5bd8e9da3959a607cd69f63a5268e95722cd14a3556685bcb36c1f09925eac0a2e067a5a8ac42dacb4a7d71c0f7f4a5e6d6edbf4a42a356eab373cb67fdf4392b35b57cd65fad547bb66f93b3030a3aac28828a2b536b7261b3831969ba60e9ec544b4144105bb0b843061ab5936bcbb9e85616d5c2d2a2376abb791605c37de95c60b81c0c672b97adfaa5676dbdc9aab6623916fdb2b5ba376ff2e022cd143260515baffa36d9d8a20b363690e982c634ffb26db7bb2c38b0851813aa5883c634b7d5e6b3fe7272ddfe39c338bfacb6db5fb52e034dd9da6c9f1576b8e302be73ce39e79c930cd948efa0c60e655ca6916f35a8b9bdc3181e7f73976f3bacb9b0490711ecf6f3cf97b511ccaaebf6ff1469a386b58c87c90ff823b0426e19b600b6ac5ebed5e0c667fc8cc34728259dc625fa12dcb2d6ead4a55f7e086036b7da2e5e7d26bdaf2425ab07c145f3ac8dadc0a25f7ed71fc92929c105278b1390e04ecd5aedce82798a3beff42bd76d396d974527f352eacf6459bbfcec34af3f33b311fec902c80f088740a779bb051d6ef8f59b849ab7bf450cf87e7669fed74f938dd48ff2ae13bfb9e0b7d824fcd70bb421c06e14ecd6ef8fe42bfa610b2edcf0bf07c11af6ebbf169dd00cbe0fc19f24f81f19be8b4ebc9fd3321b9965dbc946e67f0f9265dff959f1ae93e7618b9ab77f7e8d9bbdc664fa4337dd8f6dc4372eba6ed77171c7edba8eb363cbe8fb0923df912d65616c7fca12e459886e6a6650639b210d17561a30b6a4c9424b9a301d88e0f22d4d0c17e7f22d0d0a69ae9c60870973506db106165ae4c1826fb1020c545bec0003d516496871aa2db6d8420ac71254448d30f028a32a051e8ca8e3882ddfe59b167268c1058d07dca6430b4d04d1460d31bcc1a34b0b2f2be431e4f24d8b2b2e77f9a6451926dee0d178108d9d1804a79da787a3f57decf478cd10f438f6f32347084c07c8e7130187a68b2d0b343854f12d0b342ea8f059a059a16acc020d0a545e1668aeb0d02cd060a9ea2ccedc61421667daf8cc5c61450eef716fc1d5f7ea1533a470860d3ca810620e3a50b0ba42a3e30c1be399326e397e0a200795ac4544c437240c0c8081066d5432055d5993480638a375c6cc35d3c42d89c88c5f18ad33429439d365fc86028711043414c1e58a16c4a8e0c4992c469ca122e20d1376082147155c41a401a60c2ac4f0c09a21d21c91451455842193c4134a8060cc58c5003156229a00f3050824bc48a1c31359b719ace0b2840b1fc0ba3d45165a90daddb3b8baeece17a92d6b7d4760d175392517cce934e314934c4771e5363905c063aedc36aa9ee884b3612593748dfefef53b91499d7ffdffba9dcfdf894ce60599c379a3da39272fa9f391dca9bb9f5daa4f4bfa50b55be79c5a3a6071758512578cc1e16fb1895fb64e7eb99f5ea3b92fee59ac8fd42293cee36a061c579f726037c57a39ebb191f948cdf463235f69d0028bc6020b2c80da1012811d47b49186082670a8620e8ad3849823b2ba60c14518397c08297187095b62182166883884782cd882861b34d08839230738668d8918b884d1410e2754c87ac3b611d4060b9d1455407401430a255410c38d2da310369aa0218e2460d042838ba4c20133a6f88289243820a60d164221388083184250f96045171b5bb8a63138d0859736389801c61a2cfcc8c0c4095cb080e8620726d478615187981cc6d80a030c34696ca92fd070460520b276f0210c1a5b7e9c71238b2b9cc80185338a3863cb1759f3851757e8f8001a17dcf8019c08e30a398ac0810b0f98214249424c9b2a8630030818cac0718375071a14d098238f26c8d8e25150c7950c3cb031c10626dab0108406ad1956508335039a366cb6e46c91214d550b6cc6546d31c6161d074c61c21a62c8e8e1033088c1428c871daa60e80281196cd26c6d89a003319480e14a1a6adee0100690019c29238c1b5b43c0e104183d6eb719dc50820520b88ef8620bac6675a3e1035b361adc4063cd169f1f2e9210e384aa3ac084c08b1e266ea860f3a14bab0c25ba60a18fbc18e1810f98218607d470b1c3682491031a798c49022b0c355bbe342c809d0504be943122cb863a4030038ddba2cff06b714bd8eda4ae159c6093c51d5d7c8036fa116d4d0d5c64d1f0450d1968fd7f59ecbf48cd4133ccf318ee2bad7506132a0f8cab473f622b60b8d5c6f3b5d66ae6d63d587145adb0b99bc1c34c171e17cc8171f37084444597cff8de10e6c83a630bab336668a071672e67c676393329a4d05dfa0ab07682177668c9a005870c6b689c05d40e3b30f76c2cc8c441464bc3904287e58a32545c9913b0b8e04d31c3145a6e4f81c5e7f2adcc1a655ca06568b86ac1ba2db3c3550b65be58b550660813ba051a74f956a64c1924b82bc0cf051abc1dca7ce92edfca0c41ab0e2077fbc8c17b90a03c5754365c1b17164e728a1080267861d7759de7519145c596d7cee51b1560c07092543cf1c28939e1ae7fdf72042a05d83438f779bc643e7bd8e3061136040e79f90605153950a8318693fcb97c8b02850b6b59adb5725d98b1cb2bfcbeef7b81516cf9b87c8b820c4e288aa2889323850b40976f5170f1bc0e3de12423b87c93620c4fb8e3beb3c3bfc3d3e3b111b652dcf1e3f24d0a36fcfa3c1027e624bcfe7e4ba8ebf63303e4fec08ce10b5bb66c1913e6092b7a055a29ad0a88a3082caea4a1421d4ed068994bc790a1ea145e976f63e000c1182e2c5ac6a841a565cc1958b48c3973bbcbb731665a85460185ce12c45169f127b0b43c31c3094ff090a54fe07c2edf9e08733f50b100a942acd70c39dfe5db133c5c187b4fa638b30ae40e46faa557d25b9f6f4ec471fdbd7efd00e4067f765963e64e3277b6b925cc89196ee939b1e6faff30d1e6fa24a7ed5eb64ca6a52213a7a03b27d545ead9dd5623df9cd0724bd8edaebf97738aeb0f53e2faffdc9a3073fd833c0a4e6c32a760232202babffbdb965d86975072966123b3dfe3256d9d7809517d01e4204a29a5944ec14bf8b2a5e0ca38c9dcd2bb5d23ce3c42ba818067d308e91605cbf8963ed7a711976fd9e322ddc27038058074538227e03bb58ef07a47adf39dd908992e04394a88640f1baf3c9ad32efe6428a5d4bb94524aa92d29a5341ca37aadb57aad95d65967a5b5ce699d66a5b5d65aab2d6badb536273707e84d1a36defe9b1a779299fd0a98649a6a9cb7c7e522030e236b884ea6146d38cd3797cc14005ffeae917f76e1255093eace2e6c847f09d42cc3031be16fcac3a5960c2f81e236efb6f49f525cb7930c1be16faa916f8fcbdf5846f0f28d0935e1104c90b9acc5addffeb1eeea91f7f58b2831401b4defe7d69ef1974447af2f2b789163cffa3fd2ebd9acbfe35ccb7baed1205fa62037f87ef4f3fc1854f1237f8e3dffbcf9d17c3d3fd2077e347bfe89e547113cdbe71f95f8133f0af21c3e073dcfef3c3f077ed4e379c8f34f2fcf2ff323d8f36fe047429e5f043ff279269f882dbd4be49b8cc95e8217fa4afed82a20fff31c6981defe7f474660ab38a197bd47ce6c55d79f3d4806d9aaa07fa017c920fdf5bdd72187d821cfbd04df4392b6ca921ff43e4821b66ad6f3deff20ab625f25e497e8ba449ea7082d8890fff91cb24a88cd3244ab125fc803f917109b058716c1c76cd58fcff91f9ba58756d53d9087c59ee75fbf4356c578fee7717e1ee7796c16a755f9f89cffc8aa1f9bc508ad4ae77f70fef594acc2b1590640ab025fc8cb66f9a155bd1ec8c76c161e5a55ece9f720ab7a3c9be507adca7b20df368b47ab8a7dbfcf6c9649abe2f99fefb159385a15ce0bf96ab3f4a055f57ccefb9055329b2546abaa1f93c06609a255c13ee7877c48560db1595eb4aa1e9ccd9243ab0a5fc80bd92c4a6855dc03f991acc25255a305d92c12d0aa62efff36cb8c56e5f339dfff933fc92a209ba56955e3ff3c96aa1acdda2c04a055e5bc105bf5d9aaf1ab74beaac757f1fcce679934214fda2c47b4aaef81d82a209f65d2de56cdafeaaff2afaa4f3f02b22a66abb24c5aec85d82aeeabbaaffa7e7e10b26ae77f6c55cc56f57c95cf57f9f8aa1f0ffb2c9306c456815f157e95f855398ff3484e849cbadd1f248c94f98cdf07b981cff87d48117cc6df43ce2c9ff1f3909ecff87748229ff1f72039f099cd67fc2339bdf88cf97148253ee317c9273ee30fc979e5337e909c607cc6ff22613ee3ff48249ff17be4073ec3f2193f47ce303ee3afe4fb8c9f9218f88cdfc92a3ee36f7272f1d9cd67fcb539356baf0be7f3fa175a2d4f3692d8c2d55a9350e1d65adb502449a490c3c85dbe2561c22d7f72ed5a545a2ba572bbe3073b6278c30b17ad36c1061d31f8081beb582ff0450e611071060aaa386855cc15a1182f5a60e3f95cbe21e1c62dc7f733aa5377ca468511e6f3ba2391e6ba169abbbbbbcf36469ccb3724ca84976f482c71ebe51b1261e800cb2ed4edd417fe2f4b4c4cfc6d9beefca9aecd6e027a6dd44d7af399e79cb59e730a0210800004d0825d30a71b4b25d7c87f727194e32819d4320f7cd532115d8ee338fb3e9b39801ef56ae5b830335c8e0b83c3e5aed038ae5236d4b97e1083e58e1cd0b0230d9a5b701d8923aec32e5223758744d61d2fdf90c0aa68c8e00820c6cce53819408e0b2f37398e7bea3dc7711cd766e4b88ea42da31cf7238cc65dae4e36235ffaf43d0ea22c705367d868228b356dd26c410027ae9d2a3e10870976ac40c31d021083c327e6871b6aadb5221113c3f7859598155e47bc500572109718135460336b1e1fa51485365668038b095814b1240c1b310c970a6cc25c8113e60b3639976f47587139dc8c501346cb113770d58af8e28b13a2e0001848b010804b7f3ce2d23048bcc086e7f2cd88372e78f966841964cc18b7fc887333b9b8a9e533ce92c0d9d4705d5b57136b7211c1cb15911f75df5f2aa94fa3e4fb8cd3aa640c02f4eb574b82f794c6341a8b4c3658c0a4755fe977768a7b5aa5be4060b251c414369af7f4a5a6b8efdeb353dc9330d5599baf49c3051b8d3eaddf0ff3234a81dbda4bd940dcf95e6210a8cf7d7d4ac620409f7bfadca49123696badbba30124f2a3a6f183395eaee73cce1307afffbe7bb0c8819487d40c101f7cd092d0e34507693aef9609ede1e9b8b9f3e9d69ddf7179cf539fa73a5d8f17bd07c50745d0c6fabfb669f96c7e0f35b1eebaf371faaab31aabb9dcf9957caa2f82def7f81e96049df79e69628fdff91d4b42f73d9e69e0b765d23cbfa3f33ccf2207ddef3cd6d59d2f72b9f379c818f7351acf83957ceae720e7bff77fe2e0f538efef040129f0c3cf790848893ffeebc12972a0f3de4f9183ee7bfc0654def708dffb1e3646dfc78b083e9baf43da627466ddf93d160c0f49e6cedf21635c98db9d2f7224613e7b829ad7c9e989010c78a7270e6210787dcebffa9f388841208704f1739e693dbe2d93faf475be7b50e759e440fcdb8324d22163fd359af7201519c034aad7e74000fcd7b7c881f8394f3908fff52c7230da1c1bf3c7d112431224d9672f32e676036e0ad47864ac6b34f6d9d458abed9d01b9c71823680c2222e0b822a20d2aa0cb3722cc5c269e2bd133a571441877bc49c1863234a34b7f24a2844b4da0d10f72cb44348a976f44f870bdcb37228e5842646626828819de00b941fa54866bb992948db4378d7c0343c465ca4beaf7575ec2b30317369da4538b9ca3c9ac00d1498e15ca3e27e0077b16a501b98d687bd7fb8e97741c2fe12a2fa9ce4bbc79494f5e32d908f71ec9dcb44bdcfb73b439d0eb7e4ecf96def52ef75dc771b552eadebce47acffdcca0bbd563efb9f63aebd4dda57e2ffda16ab76d7793ca007a0c5baacf7d19c26477ec5e6cd25d66235faba51e4ce6f96c723dd198930c07bc440af8252cfc92b54a465326b93a9ff3aff1f93568a3eec8f359ebd4af7bf14191c47910f4beeffb6c599f7ba4f6ca0dee12597edf971b5cef91fab325d1fddeaba577716cf95d912c3dcb44743f269d0d496ed908e690fc6aa3fa738ab1fcefe3580925899210895d63008f18673fc28af012ae497697e8d7eecba0db0f56f27b0eae7fadf43fb274cb24bbfe1446edc83ee3f96123f367995abfab2572d2ebd632ddb84903ce5b59c1e5247de4d16e4390b90901c624337e501b79fdf945cae96450cb828869410d195edca0c695247378795051cb44746badd59f2b6381040a2868c1b505133a14b5a967a4f02f83e283de8f463feaecd7c260b199a20937e8500303adbed746486efd51cde58600e3f2ad7e902d99882eb50fc538ff83c22c6ef944f58a1b613c65b2f4082bc7cb030214aa0d63c5893f502c401a96e31d61f5cb221318b9093166aecee526c2b4a95fc6f9e37cd84f4999822e0a251c511a6d19fbd1d8b20fe94d88142e7d8a85462fb52553d0e596f98f63461efd887a4d8e3c3d9b5f71187d5cbe09e1c2a5976f4260dd72bcf369bb1620b38ed7b2222dfb52e63a3c2d8b515dfa31ce36e9cb3ddf40c075eb2319208edb5f45aa8ffc66b3d9aed0fa356823be8159739b68cd6d5a395e0cda686c2a6aa3ba25bb39a20ab96c369b8df6856f41e8704b8ee3f6736403c8e0221b3006d98031b45ac697a88d7a5e2569a3090070cb0a1a40461c4ef2651bd4b2eed2fdb4ac751ac066bced614d0f70b9f4bb07733c8f965dc73db5657ff71e39bfb8b9de775fd45ceebb69c5d83f29dff0f5fc2017fed42625fdef395b76ffb2b10cb8ff9efbcfa3cfd9b27b4ecca07befbf8d38491e6367973a9e0cbae7de4504703f9d7d1847563bbacf00727f4138df0357977efdd275812edf8098e2821e7ed84663cb380c5d6c52cea7dfd9d18617843a0d6063c6867689fb7e6acb9fb23e0f7dce2e55fb2de3ef2d353142e000868b2e38e0008266e4323f524de27d4469fc1c3447983e84f961861eb47a5073fbc11c0e9a5d8ce5ffd77d831fc9538af1f57c5f3ffbd52dd1a70f5691c9a741edce09ba9841f7d3964bdd4f917e472e711f39afd7d16791a3f39bce06076b7ace676666e6c9fcccccec313119256e7c0073b9efb807bfd65a6badb582e0d767b149fd7ae4d1e67b7d346d369bed8ae65935b7f494f8d1cef773c04b7ae87ce9ddefb98f9e5419bff4eeeba79ab19cb6696b23ef73826e15eb87f5b98fea83df83dc808dd4df21897a560dd0556a1dade795b20b3e0e59fe055f244bd8053f24cbf182f689de72daf856b219cf7a9ca5168c4ed338bc3bb7b4f007bda7a447bf92256f6ddd3ad38c257789e047ed7dd349279d74d2497fd22eda45bb6817fd52762d51cba82d2710b79fb6c8a4da699978eb723f45272268e1831657b77a7ef479b7bb89dddc6a97a8f5c296cd2d8a830702499287d326a2bbe41fb68c1fecdefbe69e7ec94474bdff1ef448af65518cddf3adde653ff268fd9ea7002bf34e4b6d13d1e5ec12fd6a476f360a58b8bbfb7cf7d9dd4f1d0c22cf6f063d3e68f103d17ca23e650aba4e6f54df0c7ec2b8d1e6cea7245310bdf3e95c33452693c559df4526ee64d73fc5f92d7acf390758bf6466f66a994e346477bc38cfef2d31bee8e190329fe17c77f7e1731f853debbe7bb07b1c5264225aaf65dd23794896de9db6e47991e5d83d4c21c6d2bb1c67270557461e73e70c337affa4b611af91a17933301ed3557094a467f53bcf1b4718ec5f46d4b23a7664395db89ce5b15dbe476abe75ed5f445b12a151c3662edb52fc293211dd26a2bb14feab0c6d4974c30fe279fe6f239d92e7ee7c8fe70a5ad6cf4326e9d98e057fec41becfe6e704c178ae28fee8473d46f6d968a72dc3ef1ec973c8f2aff83864395ed19601a03da8c409954b94899a24a5530acdd00c82800623156040402c160d482492a6e8f50114000e94b04e5240934bc324c63114648c3106180300000400200446866cac003eaf82cb8762cecfb5d69ac2b5b976b3a285b3e016443f2c8212281b4d9a62047eb73106fceb9e929881cfa5c24c2e5c64699e1cb73040233b3f120b4f0ea9a64ddafda0210d0dad697261b0131fa73ec8ce8bb7741d1eb8e40067e26f738d2a23536ae20d0bb48bf66b7723b79b8b4355592db0a1048cad7565ef4d6dd2262a4fb3efe6db2f69eafe8c7b55f1f1f2822be9e0471579b298153a6357d17fb35900ba581d6035b772a40c9a7d57ebe026f1b85b8ed51c69f66f6ae8f1cabd79ea8894bb73574af906db9db503bd2250fe2532581d15eea62d5130ddf849ef45aaa84fa3b544dc4e716a8d16611fdcd957db746d2e2b3a6b8595de76b3a0db565bf46d2e2b1a6bc54ad776b7a0db565b7436372b7a6bc54adf76b1a0d956597436172b7a6bc54ac78e4bf767aeeef5c3706ebba5b0154fa92bebbb98b85d222f26aea7e03c9f20db9b43bb8f5cb1cd9a06723f121366eaeceb3666c46d56b38dcdd4da751833629b1f8d8537dc4f9e29c471623c6f04b75e2b8913f1a61285ad81f9124caad0129b59425997f331e0d8cf0e0b4344ebe0496bf73473172f5c18b0ca0c8a8a0d9408a4ae8ad146228056dbaaf084e1759afdd721488fa5ce01a0213a4f7e33f8b0cb535feb9d2f55f43524f67e3500d36bdf17c06713203ef960fdf9049b9729bb7cc294ad6d482509a0c7f268272c0f8635b6fdc948e319d0d6b009d24c6e643b444caa19d0e07369032869986d0506df0b1358e52170fad77c0d56b6412f38f0ba98c0160f9153866c2a84785dba002a2f88d3b17636f13264d7dba058e8dae9392285d8cb329ede8c6183d0805000170205aa83affb536497d0e251ae2cfbfaa8daccee07cb8eaf9d1a0c4cf9d7cb5cdf66d210ab1d9a397f1f4c554997e5788a4c5b9919bf6d350644ef023efc4150cfdeee60fd6c8d0ac70d3172f0034401a2c1065510439da39aa8f21751b2f9ea4524a21bd0784f10d9bc63e754f2321dbb36a5d9bd3e6184a82557e2b9f311610cc117d731603e8efd8d3fa52fb4d5e6c2745abe22118535af8084ada365cec08d000717973990641229e184ed27a4cbf83993c813b4e308dbe6095d7251a81dad02f93c53a4a95e86c39d9d232486a20f46aa36cc3805112b9f5e04a4c0b253dc26cf40abd127871fdb9d8c5a8152bce49a16b005120fc2df87b56e6d515694c58529564a25f306311696deec25d0e055564eae4dcaab8e5fd2a9aa492ff1734bb1731481358a86ac6b6cc7d4f36b3d16530db734263dcf0ebade076df71228f8d86bb9aa12d737f7f7167345d80fbd81b5487ff53c0a80f95a5bf43f60afb286d4afde8002305ecb36e8652193cd0f7aaa67ef9f67d512fd2d9ef749eec406fd21304cbfbe0db4482dec7fb68e152a053d9237b10dfda185202cc7ba45574a8ff48f7b7f5700c272ac5ba02b5d7c97dc99dc827e158a40d88eb545477983f4996cd17f8282f1f3f691ae90dfe8f1fdc426b93fd98e8ed222004cdfda0c5defc516b23fb1195da88a70308e650bf487161b64fee46674a55184c276ac5bf48b37a47ff44a06c1fcebdb5fb25209f3df4d6215aaa84df226b6e817c3c1f8f94d638ab542ce771b2b933e921ff7aac2c70dd4b138117c4b8b3eba6deeda3ce55a10f8b22d7a89926243a7cabd0302df7d953462df25e92e45d1490c96b50093fd37062b000995c5604bae3551d9e08636eb0e1f2922e0ecb2649a79863b40ccfed4382ea62e007674278a7853d42ea2e05716537610f31156ad7187703accd757039c5c4659554e62e924180895ac23442acd0cab2ee5891fe750299157088e6fa42dc2c2e90d331a4b4a0eeb1e465d67530d898a1cea28104953428ba6fdd52f7f42d1caa05d766eabaadb45f048047cea499ccb6614ac0a28ce668b0058d1f4af49e8639de41b9fc38caa2e6dd2c95fae219ce658b826ce3091fa2f2ca9b92425137f2d925cc5141b07fb82b7c9401211ba46ae8f8073f3203384fda9d21976b6159fce446dde30b099d891a5f804b478dd0f88e5c1c795aae44d564b51c794ffac07ee7a99bceb5b391f8869f2c319c7bbff8bb2cd6705f41f0471404f8417b489f4da895e1c90b2df62b2a41f1e37f2818e13a4d021f818f940f5b6eaf49fb503649c650ce3e3b95cd817257518f41d7fc4c15550e103677051f7c88ea4ae6304751fc110ec9b9cee01b463f26c5ee776ea245dafd98598397cef2ee10be32e00d09c780e5b4584db773d0bd6f43c7a16d9e57ec27e80c7be749be9b6bc3ad47bec109535220eb6b4f4b2a11688b4d8e467960e9071e8face4cc436dc564be2057c0a4f8564b43525f285730a351f51ee38977fb6034ee773f562b878bbf1c8023f097c0bc15d3d2bd643616b5f066c14fdd6743b7b411dadce4b2cf4c3844db5048bb2264042119ef6ef98e0400927223b05a2c85be30ba66f999503c1551191f26afb72463b08c3a77260a7cc26f6e95becf0d014c7778e036a8c6cf447c019f8251aa8b11adb1c97f28dbfa081a96f3081ecccbd7e6082875109ce1f536db93e2c933d5a49f8eeb5afcf1300fa5d5deba0643ca31e885230ddfae9c545af766e187883f673d5b9475b43aae2ed0e0bdb411edb410816311d01cb4abd370d8e62f766e2a9831a5346a455f3cd8844fe67348588e9fea6f845f45c24850efdf382f77c583e243edf7b787d1898dd1e0db84ae2f0c9f19a33227b56b14d6cde4098c3fe8f6796a113a6f214d9c424bd6e907cd4a96c3de522d1450f5b3a69711fc781fde320b5ad0d3201cd60826589bfb98129ca8bf7a50130bbc254b964e2299af09004d2fdf462cd05d382d9ec4a6f73291850da4d9cfceae618f6b9fc6f54ee0e22be96033a95c54499d4f0b9b5238d307104f2ade1e5121f9739a1141ba2b4a52e8e4574fe6612859878f81d38b43cbb71c6b1caf219d1d632c41843eca660ec5b8f1a206d8ee4c93494370fcda6ad43f91e5a068f5e5f5457e44e265a825baab93dd29016a8cda7cc5b85db0949ef68c9408a2389182b71139087aaf806bd86baaac6efa846e49da0646f4f8de5793ae29ad1d73fd8b8c6e9fca0136ce8b941a8fd029c0d4e1afff42bdbf6c6c270152e92cb8ef6c66114e69f132a90950b9b3293bab2e7bed2f2c55119de913760e42f5c9868d13c28b2b544c4cb55c570b371bbf45fe4292bb6ea073a7195719dd04314497ab71152c69fbcdb0e211dacce55dfa1fade46b1f4a7b4ffe62b797a994b063dd72a002573e687b0961b88f1512c4a491666d6c2ca23a40ebdbc6ffe06261f50df138d50c4a69c50094dbaf21017203329144aed8dc30713d1edb5ca11ece2527e10bbc358c415be3458c0f48ff9ef1015af9d961aceccd875f163c22ce46a01bf79c674cc8f5be9a90324f39c0ea699ddcbe503653aa34ab53777edda9dbcb17e8660f3e09fe8f4f0e78ec9cb51054a5ebad7c5c7457f39b1af6e1f70a2a0d618d00d613226c85b4787bf192bfc40323bf502f65718d1a4be772c4e5e19b9d48ab2ec8df8b37f545dc10267028842ebb5045dafa5582daaadc0760ba805b601b72c6c55a3eb0fb40761fcd34a67cfc7fa298282fbc2a3841bd9faf5fe2665694aff3d4cde122ce2464ce5b5e277eb561dc812d6909745589f8b7023a677415b5a21e0442ceaa0b6f2d0d8b532bd571683ad5286f8372049a7b6d7ff4d695607db7173f317c2307207634140ab4697ad91797f2a05f0b01975876b10d9ae616009012a50b070c3a9d35baa0877864182c7d729fd13dccfb84f87f420dd92fb54d97f66dda04fb2ff42aab61077f8b5ac2d40f345d343904f6c99fc34afbedeb1bc2ef4356737e894414bc52878ffd111af22984a3b0e8d834ca37b7975608bdd45a751e10816e65cf7a958744c9c4a0471f9b336d28782df1c4b4a074cdfc7cb1957297b23f74b2f6ceba491ef61d496d3e9d763cbca2a852fb8504df1724891755056e5a33141003f6de2521d109812097dbb4c4bd0f0adf5fc28c5e8cdd1931af507f4fc7773eebf8ec17e0c835dfe309434f8291f8bd2292869fe086ffabe8dd9833178a58e269af51296e089d21b08e925757c46edebe58463b34141f00bec9b7d9f57893b561206dc3aa391c9d26e76ef0df8ccf24495f6d701851e382cb81f74f18d4f1718249e0267fe1bff021b4cc7c08bb8c24c7fe76ed3565dd854c7fc15c0dee13c9476bc6dfc086435288787bfb982a776e9aa290b297b9e8b7c834ab24ff2626e811ac2186231edb0114d7373dab53b98c8f8c3f27ab095321349581d9ab3f901164a3fcd6058ddb031dbe1e3c56d500eb7e727a00ab793e42d04b4a5b6524d25c3b543a9a89cec89c351aadd423471cf883d1a7147b7789a1be71cb97143512431d586f3d950bc068ac8e624faf03bf67374a31e4bb3b9c667e70d02c449c9c0ef604d2f3181fad84ebeffcaf4a856cdef3670fa7c6b4e8f436705c135bf04008939d81cfd9fc1344836281b375fc075bbd8e3629f4c4a1e1d164f14c3c84cc2d683a2be1724d2c6c6ad3d69b84398ca59ef9de1d4c08cc5a1c15808c026cca8bc7c1b894d35882fa6730b9df4016b1a74d86691502504dc18188f6d2ef3e948169ef43e23af8e2388890179df775479bafe7c39715ae747f0c1832462f6d6f9688a0d201a9996d540fb59dc8e0517d01ac8ea50b5a49ac2a3947b6c564aa146e1d802ae903d04e504e8b0af2aec4d2c86ec62f7e0722d509e5f388e5a472d00c7a889b1c86656d70ab81263a8843c35b980809e77d684cd9b18fce0507c6227e7664f823bd5b1c4bf50a0b93431009aafc1be750d4e94931d8aad58ca2d0a67b99d388a50799bac45e277deb2c158a40e1cd5292b2065d2818dbf0e7ec144381c72598292358f692c0cdbb18ead17433d90a2902211623b2bb2d3316c0bc8fc073c87792b2a0834d056f6be63862368503007fabeac5a6e37d8243d42c62f49a60b3f702ff8adf6ae0b7d1103c0192a2dfa82f3d0d7af7e32579f4205a90fec8a36be6f645ca416f1a6f2b09dafc872202bc4d4f65cc1ed55638028508991c6d2988fc954c71ab40ec46c04834f958c8ff241b5c0617ff93df3610a39264aa9526e2ee336975134b99bace08d179b640858a4c06af0f93b0b9978420856b2dc02679649ea078eb4c7c264db17e358a6b2fd94d599176c6da5b801f22785e364936e34f3d17574c47395015f21b36dbecca695c57852987a0f212b70c234effd22f16d588c1c665061a2a890f65ff16602e0a417cb0f77f366cd19ea1bb2152df36739bb6ba51a85b9e10a75149e17c0cc426ceb59f3edff614cf4962fd529f6ee374bb81bd1ec352231ea41cf99e5c4943c12e28a5c9ed5de708413d3ad334708769836d837606d09af0a1aabae4adfccfc135b693f59dd041ad92ea50283b71cbdc1c89ca61a6b1289c1c7930db16fcfba1988d5b208e5228b7da9df858c25ce86257336736f8397be46d18aa9a88283108eaed9ab4b92f6df953ecf5b80f359d6124a3c33a1bb7450eac8014c2e6c6f23314ba1daa20679a275e5b675f6e97a34ae9973b735d8dbc43b057eb89475f8286b185b50071bb64041867a9ec18cc8f0be06051fc1255ecf67c0840a408da017e464a580d284f9132cdcfd27c890c2ba717c0394b546a4dde92938c19ab2a47daddc6c9850667c4da0727716b54025a7e464548c0f2cca7cce9f4b87f8f1c9ce107c9ecab899a11686cff8281179e1c9c172f69e91a73d2bbf76f20412a14d31327b8cb820c5e10e82f0112b1d373182e03fe490a3081d0ba6b67977c43d68d6ce478295b7e0678309448ea13a0f12899f0a6be27f8e08efa36d177088568e1dd4e2ecc53c53c6ce28e730a6e2f0a4ac6d9d555afcc3805638f2200214f2b8872368e8db1ec79f3707b4765ff0a1abfde7a141b736cafa428742465efe61cce10287227448280d932d4bf37a996d398ad58f4546d4d776428b8a409019f824244d12d26c9a8220209182dcb93b10bc2cfc7c3d1129af59899c015f1724489c7b3be79f37824c6427dd9ff129e671f36fc5037ecfd74832ffd6119f6248764d0c27125e23c05d1a20f2cde3fa0e58552312c24677a2e31e9198fba43a6aabcf66c358175d133147b41fc27abae6ab291202994fe056f1074f4132cd094310fc7b3647bd41fb20b97e6f4696a2ceee219f2035ac7e1f5d184a545fc167f7eb76a794ea94106ecf0e46d0cdb4d712935ac3c5acdaa9ede4ec3ad662f4f78f4e8e10417bf47303e58543942b24cb457dfe6d04f54d64ddcb8a6a19add0532fff8bd29f9d306bcc00a151766821325916511747e1565f29aa1bc0f65ba8fe6bece2abcc0b9e745b3a624c0364ca8d377dd234575dcd5a11854014538a91761961f546f7db96638bdedc076bc69cbb40d6835111cc9cac3c94bf76b2c6e76fcadeae6ecb2b6a4a050e3c4d022c391191200e2331b555240e54cce04c407e9029d0579e35bcca2e03b4d4b5a5c0028e87990e0fd511318c998a6a8d0573ac5268a2c43f8f367b88c6a527204485f2eb30744e32a0fa618778abdedfcd917cecb480b04b36ecaef6752784dd7e812f4dd69ea941b5247625dd5383c0dbbc3d970aeca28153c98b063a6bfdf075ea90553099499631bc39910426bc4df15e05d4b986e19d470deed8add9ac6a2e5822c78fdd7fbacaecf93444e5380f66a04280312d0a6014adb33e0eaaeea300dfceb401cd57306eb21f48b7ce3bec03ce82020b03751c08a0330f2363b0661c7990f6ecaef03935f9deae0558b8612c6e967abe56a986ee8cc7c8e9ec64c86c8c92056b5a966663e995538d88bfbb2368a14d824b1cab3412b297420dfaf94f53931c303b16397a243b5f4e9020abe2eb3987860f2e4314baeabd039cc2c799270fcb0c83463d6139ec2adab5e06ed873f2d74c63988c837d0a1b2b686e3b966813830cebc6e003c5c20e61651f982dc24a39455e458475af067fb9fb302f601974b7156b0fd345722fcff232932608262a63b12ae4d82571cf36f28691714877f141a2b36ace71478046c960030d5107834d8e6b4b1e523c46be157c3f08a937eac4f647ce1efea3dc1c001a950b5877ba75a0666dfdd7df6d1b5489397a841db6d7f6a7424afb0617246a3f6cde24995ee53538b2d4248a420fd1d1004f9c4780e5a28379b65c8bdd3300e2143bac63d1bbf3653d0d90cf3e3aa318dd4638bd5df39340732d58fbea060f181854359a6d324f8a9fb91a0d72a5aa69f1d54e25578d80734df34860ad6de4aaab51961528afa8fc199e0985a0a55fc0e0cf5213a5ccaf2c19ae216e625f7bc19decac39a8a4e4c9a8bd6c99530a9a159a9828315f0d0de303dd19c9084f4f82ac7cafef3824fe76347c63a78f3bd3ca748d6181b3aae4589f3dd9a158a31354fda8f23842950fbdecff517af1abdc582d3729655bb3d62ebf82b2d76202b030ab3edd6b3bcf9872a21020d098cfe474131d9f8881b8e320b1d2f92a1e6a6155ac7c825afaea89859cb6925f8db13a1b368bf035f966c361322d16cc9c7c432b89e3fb4c9f5ede8060703ba617e0ee3e1be41e8878e61e691dcd91971a1eab52bb2094206f3ed31a90a9c7425791b93596adb98188dba61a0ae98257d82925271625a1561e25fe62a5f4ec490cb33f1f84f291e709047901a3d230d647ae10e81eddf6059a3a14f3f0aa1dcba6c58ad04fbd54c860925e988a20112329eb460e6b766d7c632a0c995e75cf2525126e0454b7c703cb30ef606c5cbe3a620e6254bcb7ca8bb31cf844bda553c800a5dbe1fdeb65a9827a40ceb129e8cc8c64857030d5b2ace7aaa7d6448771bad1ee3eb5d33446ddf86c5340cbc30eed683ce3154670352da535a4ae9ca877ee99d01da38f76b0761796b9ab45fc4132b95dfc91eaecac6d3641156fa3af3e9d7e5d58686c8fb1dc59e312c5f8610c9a3f461c14c33a8cc29d3dbca82fa0905d291fad098fce9327bf3532eb4e80e41124c7f7d6fa25969148e92796844f79b9b14a1a775ff7679d81647cdfbac382f3478b3b10c861c42d1b9eaa7998aa9de41b83946f682f8aeb0dbcb1b7007df86e6fcc6a4cb5bc08562bae4233c01a32dd8e2fc98cd8e2da5162b48ea349c46318c13d941b79f7b9017a1abd747550b30ae18250f4c9ee2569f77ae15411261d39c1c4d8cff22d71ba34249f7805cca4e0357178e9fd05bf89eb50775e10cd84868345c6c19246c124a82535508004e488ccdee4287b70e51d1c48cf2cdb65925c82a54f6d2bde84654497bd64446a74890674443317ad4bbef9e48e7acf7f1cac6a7add9cba48340aa658292fff206192b2378fff77e64831a0247b663aace9094902e6eb7c8320db16c9af5ff806e02ace02bc30a0e22dfb0a0b1a457b10fc1cb1e90c2f215ce9cb11882650895dae92d4a29cd625c6401713c8c2c8805f00988201352816c6039d3e6940624e396af85bc5b2134e0fed64a53847d6be900bc525e93b1627789b4e2a8d94986eca33ddc4c2ca4c68d895d5c6d1bd70cbe2683f9d7603e67570a04604593c6e14f003064186e9048bac1342ffb4f09d369e4e42f3f816e1122b211b6fda741efa7a66ac9c44d18999f0ef7d1599f3446fd4f543d63d1326271b94317358cf39f19a8d56a0cd99a323b8f1cf3dc9505705877e4ef2b042e12e2a7e5c04a4e40487694133e740fa73127b9e371fd5ddf549a7e242a405e1af5ffa45a71a2492f73b840a28bb9e5d6636664e06fe53ba09dd289b400292d86def32387d22d79a4e63ffec3a170a95cf7fde59a8ce47bc1ea19794da82272661351f8826e261fc9cc4a4a748a8a88e0257772d98c821f2643cdfddd7044c4cdef8fb4cc22a1222286e49c69638eb59530400a4112a5240cf8b74f458ccd6d989ed13c52e16f0052a5a064bd78415466aa9a88aee0f0c936439d7a0c0e36749b66f8ced871d8ca8aae5f9c0ae7e7c5f69b1e42d70dbbea1321e922bf10e92d5e5713a5cfbff493459395622337344fe1f1c194a02dca875cd68446816d23401f3b45340b9795e33b6f5dd26b22ec2d04b6c20a0cebe12e58c3631d323007d6dbba7717d602432eca6c06424342e21373b78f45585f65c466783a445fe101750795d558f944d2c2b2bcc618562cd8af6207ec66713b460b523020115bbca4838b84077e0c84c8264dad76ae4048c29671554ca45da725d2bb84bae4b6ebbce50ad02580a28a2b460b8f1de23d99e7152204bf5e8e99a00406b08af4f3b70fd25c3b63ef07f5a148904af9a06be3649702f69bc8cb594034539c8055f06f7de2f988e0a0fba5996630ff34f0628fa3398515e9888d72125991169b1c957d4777445eccdc86f00c871ccb28e68d5211b3a9d97f910d5ac17451bea92b1371d4423956a3670743e0fdc94fc15d668e7f67dc9fa77755663d2ba5bc5289e839bcf36ee0a7ab7c0c7b3b6273b0b95ba65c30d0057e4272a3dec8670c32b03176ca5e0c0564a5c682bc10719fa01d0486eb8ccbb65121561aecae76102cb1fd130f39515656430584f2eaa8bf83a31cb928a59bd0c5ec33084323ae99060bd19bad9a6ff4db1302b8d23d957c21d256b11b73fe9c8442a46a54e1a194406a7655556b34d7551d4a873a373e98336b12b7ffd5f1ac623bc89efaa95227444d9402902da3656e2dd42224356fecbb90c100896b103f18127fe06348828f7e95f138c1dda1bfd3b80af0eecdb44c0fe55373848d88bf6773ca637f18331816c4e1f514adcf5888885c4e82cbb5aa6981666dcb083a28a5528abce836ba58cda4ac12866c25a53b439a35325571cb418c59cbd5f4b0a14435fae728a6c22eb63ba21f0b02ea2c88fc6931185faaa8bfb170bd997c49fad6c4c10ec4e85af1a8c0551b2c071552727596e7a4c6383ee81591c8d51e04b0348e40668cf0864db44c470607b66bce09bbd9d49aca8af8422db542406249450b70c2a88b5474aa05ac6f02541531b1a3278774a52ef50243b7e684e5cf89e21967a60f66f59d95f665af2a46132cbb3a40df1fbd2597b46d04ac644b14d8d44edbe6ced96b750c35d7ef65a705cdb35782dd7817a771cb830a69582b64ec18fbb470b69de10b744205b2b716c1a45f84b35602affa802c781ecf05a855512adc67601ad6066ed965e7d09c66d4aa3f07c1b3811ab615dee36719a67fdb391ba28c101822f0e947fe6c40e29a2461f883045d5236c6acf98c0d4e516d92bda8ca55b8e86305f984477984b78ee01a071e50aadbc6d8ab4e18b65b40a35963bae13d0d8d03567c6265cd69087cb48255b55c8d084eac238b3217c5ba8f431d1ee73956585bd296b339df433356d3cdd003708daad999fe7548a5836ce2cb614013208e2710719fe7d7cc959e5ad28f9b2d2fa8e6005044f000c636eee67ce69c2c488f9059dcbcd6445fdb0582467c26e8529c410a3a35dacb2d4f909c21033f090c5909f5cec4aceca0f84639f6fa8cb5592bd2d98dec5c945fc4de62a410d4684fb0cb10745872293a626bbb63fb4d5cec198789dfca975030edc7303d840b4d270da1e501a54704f93d6ec1b760d006e107ddfa245070f3049f423f512795b690beb048cac6ab104e6d8110a3d95668ef1ddde484f24866e74c6d05234985483dd58a2883896e0a096d090b2ccfc5743cf49bd84912e521a93345511cce6733dbc0cde1447a1f17c7d970279fef0113a656fd0c67090c154ecf47f79c15d315f5bc5f4431880d4bd71f733a54e1f72374db709cc62f6fe04b38e6fd57cd18ff31c08c4904180dce7972eb2819181ec8033f3cff01bb91f8193a858065e1d784f1258a2341d823856b5540c34f8bea03c9f184648040b4e15a404f17d7a4861e0d3d06472b05904e3a09bf5c84baa6be6db017b810fac420fa7593d0fc364d40726300897a58b4bf0379d470245036ea5842be219028d27c120ac55cb4dd390543216ca982372eea7599706757bad8b70de3512cf63e37aa5bf7f18c017c047a3a40db8949ba7c75981a12cdb6232847eb244bb58e5addaa71491a1c38d7873555d1195ba9bd300bfd56d5fa4a39b577f4e8768b08c820f17b6e2c83f881998e108488ffc03be9e18ceed0cb1e13f768d62015d10952be1042d612e023d189e7261c2fb4ff6d155e563a330b5b0074dc8c2c702cca6257435070d63781ca0f9e6cd9707485ce13a144ebe98a8671d8cc4e03be923d102034a14d5b3bae298de44a9a3b98127742014a90bfda0eb4c554c4125b5165c8ae33bbc8b061e37383a01311d55669b99a4900ee76b2557c14caa389dc69fb56046612bf1035f44aa8d7d4688b85a2f272c74120f667e81e696af47b9dc0da3c1df39fdb836d60bcee929b5775a45c799fded0457f4fc00be748648df675fd1601db4122ebe132aecc9842e16fec36246f444adad8618f3161b71ac1127c22300d35a323ae0f85fac514c2e2cd319f2457965303b24639825e55c98463ea021693af1df30fde2c8ec6affadd3864e0f0ad06b9168b0d0c14c7a5073490150ea22b9f87a9f36981a3c68e54a9bc47066681be04a5dee16d6d4aecc8bbdfec2aa01a8d6159c7b99a1f5194d3eeb4c6fe818d6ebdc5343ce0fc379e6a00b9a1d5e5b869480eeb868dab03167ccd0a55ca4de06879308f49daa2f715d0d6c97bf50f4f42708ca49380172195a1595a672380633b6cedd1b05ea060b6ff7049f67352aed578b16161345c4eb0fd44a42d55566234916a2d84a079065aec1450dccbe353ec376275fc8fbe3856e95a799701755d821cfa047dc05dc734c5b62acc58f0523fe0f68effa2755d9329662c1c67da38fb806348d9f06a9957d84a7a7d905efa582ce6212b564d0fcade56ec6e5cd899bfcc1f6960cc5e12228a757992bad2ecd074b3605e912c9033c595d0825ed60f89cb9cc2fb14c03d3e5865db4bf2ed76db903c3b8849797558e53497a6cacaab99016b29423b3d399297a093806c6a4b385f75c2bb2781582517417de24e744c3f2289395784d549e3366b99fefa9edad9b67691dd2d1bcc990091e4e786ed8a1bbc12b45a48005d83dfb439985f39e983a89a4b52ef6b82592e26affc2145d50f75110a3f4e84b0484b9fa67c8ca1b323f66e9b1b28e73e7f673734a42263ca57fd68a3f1efb29952f6a68310ec53a18c815fab46a52a375f07253763788165256f45940a4386d9563b9bf00a009b42218848a4f3e77d23735971b941462a9001807f46c8ce58293a5ab526443391d6d07befff21e20ae805fcaa225d336dd0142e9149c2a63d883c6c1aca1789b76defe3e2ef3571de63dda4eb1ec57b86477a5b8d674ac2ee963193a804b78c73b9c5cac05eae8f6b819bd574a4d94d731b6047b2b24684b72989be179e2b241de3c4fc57366424ed7877ab483b1be00b912d3a38fc262bb00342f81d94e1d8c9b432bf09d6fbb8429b73abe4ce58b3ee5d352c8d68fa96aaef56266a0f0b467defd9c0adcc0884683b2e6276280651624dbe5c107a6535980792f472ae2e28490b9c14eb309346d2ee2488d9890a2a4df78172ccdf422ad98502a333e1eb7c77d01b8885a03a13bf84bd46c05b899b0ae87b50f2e46ca2566918feff7bb2ba06c244d7974d9de6d3fa0c86893e3aa91ce4d3ecf223a4b931b6eec80374ff3306e23a8dbdf267979172a8a4d8cd918821926d3e93ee2598c3d03ce99ebea59523b2f578ae365860f99a3ec9b1f11267f878f279d274c0b740a6b3fcfc2f74e60f50a92d0189b9a36511ff3db9c8d28bb643595a5adbbc87cfdd213422562cf307ab0a88c8e528c52612b35cf27c8fc03e41ee9e40a7353d333a1de661a7cccd91da7e5ee1b660aab410a3d35ba4b80b95ac67c4d1bed1e3f8e41be022a76e48f290cc5b7ce8f7b3efaa74136271262b8bea133efd87b7b8b0e40df52f220b0f4bd6dca9eb30c69ea89dd187e94a8b5491c4f2376a740fffdb4ee5c61c202e01f903e8de6658c6fa97ed23ce494472da69e78da422df06f92b944710a521d648d243b2c31df30bea187b2a6184e86b6d9174a35a888c25419505530a81d89cdf0e53284215b7994967971a2a3f288c1b09061669f595e191ab202e53576d5c7445cd42e2876e393ed1d7ef2b074e2f344afc1566fb7d10854ba1e0207b52d13bc235cadf0c77afe27417881ede993174093ad11804c8016383e8021228543858594d17ff2c0bc8850e52d2a6ff939caa8a50fc96d4d8d7294be8715a405219c85f607540038fe8da71d73cb17954001bd0d6d64e478afd09dab1831278e8400e075aec38effc78f819f5f9a9b9626e10c29e9acdac9f3cbb3fde043b5850af8720662b1b2a3d3ec2dad1aef992798e91403c8fc5d004b85075e72cdcfd17277332c73bcba6a5008271196b006fc34b4d4b86d5fa169131f4b0d970a26cbcb16d5ab44635c2110c16c49dec6ea7c61a3f2a4c2a0b8f495b795dc704c8a7a40f7299f191e400004ffd0401221295210ed78537d53fa5a197865acaf5d06c271ba08f664ff4a72a40bde666af7db6122cd1d2ebc1a08ba0d8dc3b0688091d626b7039513254cf367c90641b65f017d654ecc1397cbc380cfe89a615024bc8d979fe4aa3c11ce5073a204279a53f437068b33386c6e6b583ec8a5c538e68d1614fe2c5d1cfda510493ba803162360a98d522644091a30f11153e8123c9d04cd166233a0172062231c0b85c0b0e810f5d68970b3a3fcd4443587d88778e07800ff0fc8650b89148ae7d61e62cb89135f25ade7e4f80612ef765326564dfa6c8a458e6319dd0e2e3713422c52742f2c27ee814bbed56f037fa4318c6ca8ca406bd4119cd983e0368ec684bb2262c3dab68ce27fe52aad9237ba6543b6dac382ec64e3466e2078b5824aa5a03d0c66edf71a70bc8e4b0a6d7d32f8a93ee70df2601cb3ca4beeebbdc0b016fe8c2f29e1998ed8a9a7bc67d7794ae275bc0379ffbe2e2076bd8d882374a52dc20d49220d955009c01dd5f35fe3fc5b0f0f26068747e74e6d410e8bb7c258998e2e1351d91261232e6ab8cb7693551bbdee21f07dac98bd4c2c06f96689410d4871bc4a33aa4f42e4c871c9c067643a19880be554c3ddb2b91795987d6f4f00501f24cc87750508966be3c27f681c35fe4aa37166557431ae96e21d8db35c6ef8a4923aab921ff83fb42f8290e3aaa97eed961bcd54b024f37c446f87e2d4bfe41c79767c6c4842cf002e4b94d438f9c7910c68e858e1233d204a14cf1e28bbf95ca976158f4a2f002ef6191d800840fbb3880eda65dd763c360e25b25c9bfaccb14a09116eaf8907e3a749b9e944f06daca1bec60fedb8940142cb596adb2908927d491a3cc00a5912936aa14eaf78bcd822058cc6b9bc81ea951cba07bccb33ca03d0f078efcf8f944e01337a2e3e1f740a620a9ee6e0cfc181068473be6008e410f67cd134ed81c81d219fe0d58dc7810ea1bab2b3f37bd46ea7e0df1d8a2e22d944c05e13bc09da9c1554ad12043f5e4656e0b845fa4768ad419061f862a2f39677929c1e2b140fa471e01fb5c8f8ddecc13dfb6477940b444f2708325e6518837631837d07399ba49802db594ba2fcde6957e8b1e2ef01beb0486f55834f58a12a641b4f3c1e5011db971be73dfb351116296e0b6d2ffded4ea717492ccd3d4ff85c584bde21b6ff38c77b09ab9517ac8f6c1adba5fecd8e69f76573332436650c19a6830012b822f585dc0e754ad9d5ffb80736f17610d02ee211a1f4b691242831405c4da0f3bb63ccb0b8842c679a9844e1769b406afd9df8f1c185f7a5cdaf27030a8089abe04a43bdfee115689cd5d49709e73baecaccd3c35065e4c5016bf02da8635da9b863921fa74694198797d1f4c476fafb62acdd9cd6ace13811a1e7698817981a511990981c94b263925093493cc42c1324f437a505f79f91c8805a087f6c2bc4014e09d46155f1a07f5bcdf1655e42622f119d89442cf2f04e3e302ca8bdf706f68c6b1544f7b4f0c1f873f767502402bdc1a9e6c4b57fc0c7b8a532e63d858b1402d5342916a118f47b481aa82e62954d6b1fa79ac1ca6e16a1ea4a81ab6c3bf2d6fef8865f04ff8a5fdc9de2167d223ed628d9502292b90708cf3b9956bb3863130d4426b43ad384ffa40d3abb59f05489ae7b4a4a15a03ba654e996213e493cb471a5c2694832ea0735ca94c7f9a2e11a2ee5c27db0ca397367947313c24ba8ca9da0d5202f0879487ac3a0938ae7ab8f4f12c3868e376e283409dd46d7a90a680199bf1cbd0c016a9b1f3b3890559697a64d1f36b1549a718491efe5bf4821ae0db4e7de2c86948527e62560332b5de90b3131f2207eecf27de8e248defc14190016b621045225f40403a16846be7465520144315583cc7930dcf928662522c1c542111268e5c3e5eebfbdfa50b0ba25130525db8a30fad0198c02598a4041a1c3a28ae934d3152c182d08ca21302b81a29f93234f0882c50f4c2aead4fe0a982e011ca49ec43dd89ca65e9fd4e3d8ae4d7e7043d263d349593976b8af67cd9826f28f49649a7682b9e7901ad1e04249174ed60a39804469cd95a9d10d81ed0f2e6eadf5157d10c91bd661c1a805fb18050cca10edc8002c83c12ab16bfb787fa354a71b4cc7276adf4e9be5e474adfd2b76b5ac02dc8e17f325235ad142e4f3d890ca1789b1534bd4800f421fcc0e1fdfee96e7bab63302e20781aea6dd03bc6981d488612f279d149bb2f84b83eeca1a5c1a32cf9791991f6e38c06608db697be973dc1bf697783906b156747a42e02bcec59564899fe6763a52d3cd1a84a776a4675e695bccede27ba13a60ab79e7aa6a6e509fd5616250527ea86a00a3bd609c926a4bc07f4c20ebae18ea8e5ec2dcd020a4025a3049743a162e90292f64adadca2933f8ff4c4496da1794aca21bfd3f19f8eea592600afd480c95ca5a7904745a9561c6b733051e757232b8920fe6b617629719f7e867fcc38d9dc76c41e217bc9cc92443ddbc6650a11bd6ce36e3b20a01c314d244d52ae85bb1d0ecb36ad42eaa5193986eafa5d216ac67e483b1d4c21e6fdc730f81200c5ebcf3ef30da45fedfc2965e4d6841fb1121542222b4fc11d168a2cd6edb567eca780a9cf5c21f9c624686dbc9737c7f5e3339a8442a11446ecb6cd2cc18d0946a30cc4157317bee4e162061afe05d49bf6bdae800e1e3053f01e9438a507801c53c1b59acd5d5c9c9a10bf2fdd369fa574b591c6c7fade3b8f5fa830730b3e37b82b1b4e92b0124ca5a0a10c15915417a206c056744823bbe521968388bd8e980922d25df6f431dd1b3dc3e80802dbadbb6afdaf8b22d5fdb25ed7d8bf5b5b8eeae646da02873b2605eb86785f75c38cfca59eddc9787e5e0500e9eae39de9ccb5166b9818e6e5478f6f7b542703a4356bf004e5b41c5a7cc6efba5ced0f01aef42bfa2aa0cf1d941e833205ae7dbca6f5e232788f00cc65035626c0aa0e77e381e952b544757a815141c5eb6524f4e7d54cbbaaead0aca33bf3dba9ecfb58259107636f517737d3f8501c6410749d8583b8bd11708f7a007929040c05dd1bc763f06899e0540e26715fe3f88bec8d2dd0feceedf8cf7bdc0981296e1fed5ebdeb581d9d85d24fff1dfee8cdcf9f85405405a1d63e7fe4226dd132a19975198d9071affe8541a18b1ce3665d6464f94bcc356210d7cd24e8db6ac709a8a5d17d2f52bd9b05234901c962d8314f4d077a709a0e7ba526852ebc61908b48d3240bed75c769b8895e0f14fc880fdd07e5229373971a4ecc463a8e7130d5bb1dd3f2f42bb7306b6c5e070a849ccbc5dab181bd1a2f3d4ddb73ac1399e133feea459ed8d555a0add025cd11634b39fddaa61b84b875805e3857d092239f4f5873f4fcc2a93a3952131a61bf821231172893574ae9674f94c7ab0a8e061cae60bbb56b6024b51e14b7d31f6eb6aa3a491058502e0d0f4f0572b85100513128d02c046b6d56cec21c8c9aa4a411a32a42bcfcb49d57319ed71a29cecca6c0f6de8862390d7e37b78a77ec196ea8239d1d1b06c16816e58ed2711ea9ef781d30770fdad5d9d16eb0788d718f55863db9fefa4e03d7a213f5b897189c2974ceb06fad5bb6944cb88050a6713991adc5d3f511f64bfc4195ecca2edad235ac6d39b431881bf145b88fab84ec5bcb77872773e783b9eb7646d474084518cafea8f906d0d93dda09e89b4fd7a246d0f36a68448211c4b02852f042d4b783aecd0482a0d70e6180fe838af8e9df78e2bb5c304a9af4d44a80973aa8f03a7325170da19f0f4650ca02e0950dde9d3d12ff0e94090c373e5344ee7de32add94ada20a30c9579bae908d89fb9b4542a730b784e2dd6d878c2852e566a01de3e98465d42759ae35447bbf10c8fa4809f93335aa1b01b0a10b363687bfcf4c55e7995c237e35a1bc9ed7c4e78a9166b38517ed17176f94215b63128c613141f48f11afa0380ab1069ad336cb7df1096234add69ca6ac5251fc48306d53f506426eb0c7d6c21389818a3196efc2448ed07ceed9eb602354a44ef682b4e1b60865964f54a3a601429cfc0e30d2226cdf3ed3236e850fbbe3a2833862fb2562379d215f8301dc6c3b15806a78f41def7bae83827d7e617d80b55a5b11e929376e3917da90471f30c928f0b59a8cedfdcd225bc9f939394bd00cfac11cf3e2c59b368d340451af5684c87cb6239e3280c137ba7f84d04a53fc6829ff65b169cc3384e326e385a1788884ddc5c12d436fd6699b863e1a73242a01b349277207b78d53622521c069e124a2c942bacacd7f65d663cb1bf16d32f63329cd15e81306681e87358473967cd33ee8826e632c05ad2d53f85c73981c9260a01a2c8a89a79007dc1b4483e144ee238ab52945020a53da023945fe074f500129126aca5a5b6036b0705b4c4b6fd9c6ab03a2f91286aad0400cd5db4ba28faab50a7e7f6c4509d413f73e6652a5b41d36b27ab08fbc216ffd7cdc1337363b0a96baa56ae00c04af90fb7cbc2b2a79964e0940c9e86095cca56b40d3931a98f1fd5d8d2e283f3652afb720efc78883582b9c3ee7612e54bd5e0cd2f10829b77f9bf93a7e955f30b33fef351f090600045f5e7d8048e65abe2475817d70a4dc824558dafb7dcdd813cc9f9dc7315177dd7252908faaf23419d14c19159a959d562751c26160e760718d905dae85b95b17690c9162cd6ce71bbba576ff5b8bc0c35245b3c5aed8b63f3d2e8358dbf3e396e71de18af3e934e5660402ca00f0da9701fe9a9f8a0a2b48521f3becbd7684410ffc6138e073aad52c8237a2d18691a7fae91b780186c8a2e2c1d8a614f36994620cc9960dfff9c64cf8d9f653e84129ec7483550c43e09191c4a711496b165fee52f9703db2bd9c7827e74e54d812a0e5b11fbfb0f47842475abb538b7d68b128e95469d5b53fda076b44bc08cb1d0ecf0d56cf498257e8d611df02726377a3e98225981bda288e5c830be82eff9382ce3baf58829b164cb941d2788b4869eb2e4d726c8c57a372c342d50672508cdafca699f039dcb9e9976f9135cf88d24915b1a82505eba34adb37dad3743d1e21f1cf69a8edfec93e9ae17b8eb50e72989e36899bbfa8ad67388fe954c987a31625699fc358348d7379cba9899cce1a9f1fa0edfb01a39ccd1db3b0515f929705ea91cb0def5061a6f146cc56a8f92147401c8aaa7afbb96b2f8b02bdf3e28a141efe8e080b9f4c90187c2aed72d33ff8270d2ebbe915ad3fe19d60d67710ec7caf0e37d0ef451ff76e00f9abee414e0cdd9d150f2fb2c846c9057cc0bea80a21a0dbc10aacfb1e15877e5ed5bbed932d34f5b53aa29990294a4436dbbbedfd3a14b92042bd7311a8286c55821c71bd4238b54ab46b58052c850ab5178734a8eef11a0d5834e22f617984fe6bda844a90182b1847a14547ab03b18e552f28f1ef885f21c23a44741a5473b1157e34753bdb9eb9fb53cae4fc5085ed9f34a8c03e597d3f71ded7ea05262a0c29bbe11c303a662ea23597ecbb1d90e7e87014efbb5eda763c60d7e00de3c11a4e05209721227894a4607aeb0d345293f540abe61af357523dac4a8183211c93c88be70d324c390ed0fdfe15ee18144fa11016c5c2f5d6920edbfd8d12f17423a5ace94b535ed840b6985fc80a68a14d3d5518ac970a79973a990a87d4aa664e87834aa2766713ad41edc85ef9659e504cc99626012ec0e2127137096e4904edd1e7eef44a0ed16f3a55ce1f48ad06b78054ea818a72ce76623fddb460a4d0dcc056b5300db058ec0451ffd91624d047e6f804aa8c1621265d84263f775209f25e6a04be2bfe3dba127dfdd2423ce673b187f0b2af791025003dc221aa1a0ffb180282e55b6bd028bdf6919b6a64154d226f7c24a3d14cbf25ec66d60aef96bbabf19820d0f9f935c88bf2c28b2a6d29ab96d194c5a6e02a39bd14836629d0409d730f32fad846ea64d5c9adbe9b6a41cf302826497a2bb605836177cdd8716435d01fd6bcd4d3cf2bcbdfb16dd5b8dd94c72faad3e1e06fea6065015abc3621e71fececa9f359c608a85085d84a6cac8c78fe7993d23a75f37c390b86b1182f92987ef3c8884caf1d71da327dbc8089ce2d578aafeb000013cd7c38ca879c62d442a25523627420c0509471ccb3c66543780c962cc8d7fb87b9d8730fd00efe21f806b5252a41838bb16e2d6454f747da4c329f03f27fe1ff34442da8b899602ec124c19fca9423643b09e487fdd10ed257f7a2177bf7afc9ad1dbbe18800791a965e55a528c433063a4dc9f90dec4a51128f7900bc90b1bdca0dca45cf610b657dd897b055a1b478e79e326042ca212eb4b314e6726db888e3bbfe407452d201e610c2bca69d580fe55fcdc0e1f90676892b1255c5a65d716222145c9ea4273b3e1b725cb33708331a3f3d7350363bafa0c24e0330d92d7514c3633bbcc7b0e45abd0a957cadab2f65a56fa8ccad50d3d2f86091d392123d9684cda1e1ef7e27e162913547206d70dda236a302c8a104837b1e4a6f22a8102527d3e4869db0ea03fc4ff34fee564b71bf784a6c3b0dab7f69ec34e8dc629ff62581830e6290684041acfbea00c8d04176ba0a8127ec1a5c66584594eb51604ab753b9240516e14a563e75f285cf2b093f422961400683daba17e4926bb9e2c18b928f02d39a044599b0005c0a25230b6122024e1059a02094f8792eef5d2bb4edeb1fd6647dac018586e76feb2eba6db812aadfec394c7a50ad3880512189d9f3b26f30ef33771ed3a826d65286d39eef9b46c409bd150769e1da02b1f033603af6ba75f1d09f02dfbe1d3a2cb32bd4e9cee44fd435f7a870c28b1633290fe4ba8b1ee4ba1195109973315015d7cb7001c3ef242f22a5c5b0412adc938773559f7439cdce4356f901cfff034648b76cb9485b4dc0baa875182062627e15703e36cf1c021e5796f7b7f5948de6d711eee38d1beab86e689b08c562ef263da3b0256a090a01cc7f29986ad2465984a82d2285b29ffb8b4d1609b6977c313f389f0742bfca305e96dbc52b9222c2fa96455c4b81c6657e0cf32b5307a4bf84dd7ce2b78f69341434ba5c73af70b88d46ec9d9ccb8aa311e40e9499f0641103a346531e0e705a4b00d7eb6024096e47467b2fd340cf009d0244931a3016cf4f3f299ad062130c7b759ec0b455d8819532bbdd81e619ecd52ac946b791cff8395df53d0c43e702b8f5bc7fa23e2113f6df8aa4a260a3c0c78773c43b45474e2b8ad21721a90dddf1938bc2ae2d673932d854a82aeb2f2f9e109c0882a224c2a29a1f29425616a970fd6b1171bd9369a14a541a943f6dedb97d523f9b76f8c094f18d9d14d908b641a74a175b94ef9176411bd3af459057e7169a2ebf0837c465b39b1bbe5b623a1f51523fa5fa8422f4ea8b0a0d2a810c238b0992cc0c17ff41e38b80490b987f0a4bd35b0b010283d61ae8f8aad7325e1fbd949be78c3fe2829178b047fb31dabe020c816996f3833636093f50a58c74398f0b2096bea02e75268c0f3875da06528ceacd992c09214e9cc2b5172b010c1c7c5c4ba648de01e7a9a65e170a4ac6f9076a129b34e367b8b61e2ce0ee576b620a34f4aeb6a1e0c5a0f8cac3f166ab29e930b0d0fc3f11a46360b8e6ade77efa3874da75fca5abd5d7783e253082c5c7d28748dd17e6820ef342e525c48c4574bbe38c371d80d6a7ebeba9064219687aefd1a0a8c71655642203793889aecda940ea85b96d1dd266a0900e06bf6ebe66e027ace0017e9a782805bcabd5832fba43c4511f1496962b1cc531ea511d2f434dab7f3f1728813d7959f65533f878b1c1677e65700cd0f886605058d8ef71a43ede73ef200f1965cbcb5970920e77dc8d9db47bfee212044baa074fc89b337fad2afdd509219b61104f4db903526344cb1b000951ea55e99780a0b177cc0fc8623130c40e044174d6e055a4dd6b2fb42de21d647f39fe081dc22a5f5577c8260e031fefc54323b8887c9baae08c4643b0a8ea7f2073ab3ef8bf556a0e9864e8610bd2633b411f00d0207d90503cd684c2c079b47bac495f0d2ec6e0d1188a23645e91df49a3ce4d59922f25e4d4bc3e4e34ff965db18f78bff91c27e2d7dc8b33bea9186bce80eda2f072d0f4010126ad4527ac06cfb100f00da150022fdd9a552acff0a5398e38dcfb85bd364eeb87087cce3210b2069a61fce68fac169d1e3974ae5e8b871143cf16bbbaffa7e27857c077db17a8fb0f81abd21db1b3f952d848983d995023d2510ce92a1f7a1d10d40ea4141b426acb2cb274d3a051fd02f04b010dc0b5521d5519eb2bb1868b4f3449ad131e0b93f8e67b11cfd88da7d00ea7c4d80596ec2cfc26bae226bcd6cb5aa7cc828c8a17b0c51f35b320ffa711f0bc19b8fb87844d5479904bad7cc0131b9965e90585529015e128723266172ce89d40569709989f34d0bbfc80e466b7130f679cc2d66a318c9428e619dfabdcc2c116539333ebd7d7ae5d8034626e7d872879628bafae12cc12b9e4107a502c5dd344d6604e03b1c9f20056bb255a2fc972aff986b3366dbcaff5cb4a3dc27d6c2972e18e64b47760c46c731da94c7f6427950ac7975ab2c4137a5a38548cc16520b79bdc74320c0c4ee5d05c8985fdbc0178e46291829bc74212af80158020b35b46acb10f4bc1fc1aec672e169bdfbabb2480973455af4b98e0eed42988cf5db15dc4547b94aae7d0933a58e7e0c62f1e1447d255bc564ee1a6e2aec9fc222b69149cf45500bb19a086329d780b442a24ac98e5d9a98593f2142c97713fff18be0d33f69a325fb7a8e09888a0d411b5dfe1daf57ab1da01b21f95991d9ca3010bac9420123142f3ed9aae8bb3533b2beb0c6cf5a47266de9f013a19e19ba8b75e052272838d81becdd34c488c088ec856200c94db4ac655f98e475f98b6d0d667fab96bfb474675bba5b7c348486cc542ae8c314f522d3e6a648e0da2dbfcfc727636a34484820de1b1245fbdba5284254dc704dd140317a99ebb209c3c4190fe837e0a9bacda2bdded31681c1976a56912efc9aad35f5eb945fdd341d94bc06ade6ee631243d59aa8369c8ad8275a5b2637cd7ec522a3b99cba7dfad92eef7119fc33c68feb71949925ed195d5140b47e9c2c0debff9e4a1a96ed25005889a5a3dcc9d0ef912f74a98a71024d3b57488d6f467798efef8004f17335f905a6042b27174cb0f3ff70e0ff4347bddec9100623a069e82015359ad1c8b008ecf75c02359571a64348caf4177da4d75b743b3f10bc24776711c0409f8b4d40ea5682a0e34027f3e75ae85e786882d7747b7e5e7012e32430731776aa14f05266f43b375468a52e83e926d2660b5e8d299249dd197aba85bef1e634db6ebe9eaefd0ed3a938cbdd48166c6cb75be3b3c9a49ed2c8076da9406ae30b964f3eedf92eeb97e17d8e1957292908997a90c34117fca37eb6f309093acb80a2c957d60ea26bd8cbe1e3b06e79ba99a18306eb0ed8152572847c27187878c6564e6c834bb6cc59849aaeb814d0eb22d33fcd193fde201d383afe17138fffea71605a4a30578b1a2b5cb0b0a4405720d6568fe3379bd68c58cd907941000b6bb03255988ede0148d1d0c40af65c804245f20f62f7f5a4d488f80db71b18f97ee9d592ce3c96982bea82fbfd08e877c22702f980b55428796925ee10281095129c9c851fb642c2ed9c9583474e25dbfaec645c13dcc4928723cc59471b9e8a03ec98c026a2e24b75dff0321693dc8a947c43a4a1b00c4d8b0d132aab4ddbccac38fbba37b3c0854e0e512d263eeecbf1da16bdd1b9b0c10294226cf350621bb37a980ecd37e63ceaa308c3c2b9a7701800818d607227fd02b1fc11a5bd2a0a468d807ae337133537db20f1e9b0bff969f241dff4fce387b4a9984750b09d9ef2fd14b3cc06934a9d874a8ef4201fa1f45a2927834246fba5f825b164132add32b5f824c0e9bdbf8ad95da535a9b392e3fc1d476f3617f58f34e92783dfb34ac33dde1a6a223aebb66d2142098775d82788e5e30f4f08f46385718d3676b9b8291b924079e15cce146ca885007d263bad75b17290a9617dfd5bd32ad616edd80eae20b2682b2f12aa8d24a84cb48355c2998b235edcc0abfbcb938ab128ac40aa043218e22035952504171dafae1e772e6442c7a4aa2adcd4066c61920d3058b647c74685cfa6b85e3a24c926dad2c25ec945ce0a849cda6c0642aea96a4f39f5197db3828ecd3f1b6970e14acb17195c7dd14a8ef5e5d5ea33e2c1d8ab4ab400d69aabb49e21c665d0a8d7a3f6b6d34b9babb6eb61712c098c1bbf99cf86f7d4a776129acd8da0e3505b3c44ce91805b7c85b9f7db3e692b358472a3184b1a8300a7c872a593320d87aae2604dc37dd141f8dd30aa8ee696ec3a0ed87860af25b9815024e4d623116af07dddd840ff6874c7f9309b40e605a9117799462b03fbc6009eb7b2bc0c80e31e89398393df1371c2730a9da598230c6a33d74ca46586e6e4a15e8581ce6c758cec6c13410304a6ababca34b2f2222e833996066ee30c15885a0d209d4539d5c1649a01af7c047d96b06bcf179eac6ac84c40e2a7525d072ec6cb3cf1afd4277ef8cd171eed848ac72c75a88590f18537597dedb298cf0d89efd2b544949ea6375cec96dec66d1b64a15ff7961844f6c5da0be5be9c75adfbbe8c5ebfcb5dd9e7008938357a00c0a97a55b105cbb2e901f9d42e61d37ead65f6edbc78ab95e7ce644ecf479ad6da4d0d3a2c037417e96aed421103d1481b3bde161ffcb7d717ace7224ef92ae8c7490a840741bce7edf8f4ecb8d73b9e2229b1fea44c725dfcf608c911a138516e9be057c6f672bc6d5881abe68ae211b2765c753195ae9d5f836f000a7ca2376c33a4b96ac606946159495a6a7eb206567ca49d361211f684f3730bc483507c76eb469463f34717047f581ad9094688c0a4e805c066bf6ff4b7496ec25b3e7654e4503ed2cce07a173330d79d3ef439c5e24b485ff918766966c3f0417e2b269a42aec98758ed262824461b2461190205a0f64b09ad0d5cbcb8a5475b3a102071c788f28d299d5ed249e8a31191483cac336c9b669d4b588cf6ae8cf6c3428bf9ef121ba781490bc3067fe9524b49455d9f3c63040fc5aa21e2ee1a9c4cd5a4db1e62a5d6096e6767e9d1306ed4505529badcb592ecfb02f535651991028c04684024cfd83ec6b70d7a2abb0e2b809dcc3df9b4030b16c1d271061418328008b51cd13869c8fb8e3dc55fd910cf60756f3fcddb187406b9c936e2b328c725077cdd3260d6965cc4e625a4f7537cb4584c7111c7769912dccbd83901e1acc0e85f62361b5931353991cbd153a8e651e3cd2b7e0d79aef4fbef7d7ddb866042d40dff4a8438c8de185c7bd9fb07e9fce07c58ac019b1e3c1815b755b2d7c60601d90d4ca6e2765429028c79ce84b2c45afb8270bd8b8923c0b0e3c67797e283f0d7ffff3b18f3141e19d502c678c06fdd096e27a72a4c36983deaed3955a897916c87a25ec2e9fd4bcf51f81fa70203863a747f21544824184cff1031572338a6376404799a05a81accd94eab18aece2db6cd112c615dd05cb14c81c80c049450d67884a07f2dc27c4d6bd11533c258fc3acbd2220745e7866626ddf3de4d738a9f4c7ab4da342b0c0b1fbef982d8b378d68f9c63bdb1a12d5166d7ddedf57ea1e258da88517a17409f6dfbb7d93d35408db035e7c395e34214e3d2dc14fabdf7fde8156c51036a07279c5b304ec29274bb8a9f70fa3bb5581935bedc72750d77cebe5f39e3ba8e1bdce16d9e025e32a8012709ee806172d9f10f45c0418511326320c4fa324dbe4880612983341679a985267fd89707d8527d1fff6e8d10d2c82664efbdf70e280a1e0a840930c78c59a80b62a4b502608e79230e608e1f2feb80d6c6ebcefa0af3acee619e1eb1bbc7193a4269e8c527cd13a1b779383a217ab7ce457c42fcd16c1e311b695de4ddcb45db6d6720c0c6b9c9df968db4160e329b3bf57ebcbfa4ce6cfeb6c96a90f46ccbc2f0177fda09d9b97c346bd0883572d3876a1cc21cacb3a8d038f478b0bcfc25b5f72f7e75ae35a7945dfee257e7dec246483dfb4b8a469387286e546a4098c3289e039863c66c03e6983b4e30a2b2f47ad387740e610eef4628243595576efad0eb10e6e8de51d1c93fd3a7cb545ef9d52361f335300c7b2061f334b090c763c56375e871f93a244cb3363cf1d149f36cb98795a1630c317f20c036d2ba3ec8ecfa4f9b41e69765238d87c836bd6d9a35b803f6b41defc295ff7e3ac8ecf889236d64786f2da59411e390b028a594b2fe7b65ffae264f218456be5ad2fc6d1d6f83b621b8b5900cc3d58830e8421f1804a70f4f4730a0be908236a195d2dac319564439a20b233988d1240d16309c2963c9059c9a346150a521635cd79279860d4dc66d358c22e387fe2e65818c2858c32832ac20430568220c5022d10a92b4e0c4922a59c06012060aa048a92a94a182242790218a0d3ec07084909aa72c672c21250a2e5e384305031479840c22b36a99a6699a96a1b92e4dd396ce6c9b1018509880c203d5192918d29c5a4b03050c67b274c185a9cb134baa34350d654bc21862c9942890e8c0c517225050040d4ac0449168c0e862e9099a23619041e40c4593c688a5336184d11306174d6c2c90a9b77ae1620b1446b408a3045431c9174a645f988005321998a1f2810b9ac4926c184575c58c172e8e6c2172132509aa245fdc400535b940f24510934c060da3cc983133a6bffbcab4c59831669a78610506108b1880507a6205438841128f98b981c8ccc28c1066aa68f18217fcd46c3ecb9ed45cf5448855d100f25476b1034eeb2c0e2263ee26984b69853bec2b26a2364308fbb26bbfd5f3317d8ca0b357fc11d12eca1e44b63db775d123a26de3a471b26ad7ac5d4866f788b267578b330b072150959202510865072744d882114228699465347921a55bb4d2e811510873c4493d1f1ed1942f9af952096b0a77403aa394a14c94324e554899a5c7434e28e3a39705dc31e79cb5c619e7a49389c639a38882461169a54bb668a506219416c2ac07ae669add2c948a4f54326a3acd298aa5669ad5a49ea864d4749acab40c4a3d51c9a8e9446b56a59ea86414ad54ea896a4221a594500a0a5751cb4cb1c01cf016098ed64cb39bfd718a5154f2494ad19a6956fb7ee054748aa2924f35d3b2efa7e154748aa2a235ab59748aa295fec0a9e844e7cf07a7e4e5f73363114a99a594fc548239ac1417532f1b301c29b830aa8c527f2faafeeccb060c07cf08d96103914a43dbaf663111261d43d99becd0b6652b34a7090918d33053e1a1478251432a645741a34074ca7c7aca03f308da699e001cd2324da2b20ac1c5a04c0c9426970ba3b890eacf72a1c4059718343504229586b467a7a2e5ecf84ba169d1916c9a83cc86c769e8f91040c34c45050a44a79c555512425855359451c90ebd0a7c3094e5532b36082184509b19ad9566134208ad0da2b75a17cd393728a5b666eb0f4208a99cd375a3a89d84c10975b709756d47a8ab7584ba5947a85b7b425dda11eace8e514ca82b3b36d1f013ea6edd84bab0e39c1342281b465a63765d383a68ad74ce154c08218410c2292d9c70bee69c52374648e7dc5622b8351031030b6450a206254f3811450d468c4832658816889043087141892c5166b85ce1c450e6c2163c2c21020a0729276503a8c6508d99ea32c605635830446c61cc146354101404617d869bec509651207728ab95662a197c86e1f4d122280852f8798e074f7648a4c90ed96fd770931dd20ef1270488541ab2df30938e212d08ed7a1ea2f834f3278427cb2368870de40ec11d490d2f34a3525b0c4df8ac4f95740b375b70e1a59431cfc9820b2f63532c30071052167167116cc195873b64cb4f5c95608e08c54d2f930a0be1e76a0821a4ffe69c738a39a71415fe9b734e0a735017048210f6dd9e70ba7ae274d1e4e69cb4e10aa8a45a67adb5f77eaf20087de6617e430dbac805371e526abb19c58df0a3dba684fb6174523a55706dc3a8314b3c31e604ef197384ac32c68619269d4e8c5902c5a4713284182c9c0c91d3304a4c97179021434669cb16366c5165891e46c47b5ea76494d2a9a527622045142da0d03054896cc142dda2880c4454326932326b881071d3304a8c11bdd3304acc1315a7969f26b85fa6be9421f365ea4b94724e4a6bd53129ad35cb344bad9d32da9a659a66eda659bb6dab15d7d1ae9b3276db6ac5715de7756151166bcac8e2b2eab8aef33c16abc56241b57c4b4bcbdfdb4d6c23addcd09b9b29e38dac2261f26ad44824b2498d54529346aacb172f5da4ba708956629528a54629f18954930696a9653ef079203fd8e5f3408784c97351ca3929ad19cdb2296316e59c94d69ac9a8d19a659a66edb6a2abd59471a5d96d5bad38aef3a8e74d19bd15d7759ec762b52ebd77ca783d56ab75af8dcd0d0ec5c19932e2dce0b85c39393468d478d1d76bcaf8b2e16e7070ba895d1e08b15f1236f109b4733c1072ba8969c01ab4468d29630dcbe2f1317b3ef687d413e6582bbd48983c08379a6a81394e703fdb5ae08e9c96af4d7007ac4e0618829387102e408b10420821860459c44861218b1825629880f89159f4e1ce865162967ab8b261949821dfc54c1854f5bcebf593c1808cfefe339f7ea815187431afdc551617062d4832b3689aff45e2893cec3d302df233e81e065894e8e07abd4069f2706fc328183c350c064fe6d4b480a245136d880e3be8604455c60c3334712a628cb8d48466e125cbb2ac669183992154b0400c5392a18c052d94322d9894ec20a5699a0e57d3b0b841fa402cacd84b801228d9e18521674c118338040ba88a452cc90e5946352c82a85814b1b4c3902a3f557aa68c602187a91f5c9839ec348c9a7232b504879e1965eac8549139c534a51406cd9130655c7012468b262761aa30e2248c1461a24ca9b4560a258732d6c8d04512481c31a404860b0d0c18d7752f09392c010410506081e24915982a565a1461a12006532aad95c22996f8e209314fc4e8a18535608c5430501ca9b75ea1a62e69baf7bac210b1c31565ae60614ef14087261bb27c011304d4503d03cc11158c0992541e6e28a50c0053821598254d0c00e3640566880b538657c328306284514225108f0f696357ed45ab67ae8eb948fb8ae29a8ba6b77ac51b2edabec23ee4370d7f49add94c63cf9c646344c17c22e374c282294306cd40d1f20512499cd800859a39e73c921d3f67944a4a4a4a4732cc8c21869e187e60f881a1c7e9ae1a467d49f385ca0a30ace8e26659c6e46699154a481fd89b155da40feccfdeb6620a2bb040329b5cf1735f3f2c5372b586515570e9ef875511357da07d4f1551aa90a1bfffbcae69dad7dd9fefd5dae1764dd38e5ced54cbd60553d20792913e3048c80554d207d60d5bec9230146c80810d450cd9e28392195528d92a9a94c05e195d9dd63b7de8e9675b2ba3036761eede06e3d89c758524bd0dfe6877f9662a61347f498d93bfc8800b633520e9f91c353def3ac7c5aecedb1d0ed6dccf843e9a5de4fa3cf444c8b9f7e1bcc667fe681cba8a5aa76fe1da14df2ad3ad7bb1127ce9f99c13b13e6d3e5fbd1ff4ad1ff7f8a3ddd970197210c0f9fd6ce5ee75de9c464f84ee33f3709f679d621e2e13dd73b739f571cf9d953fa49e36b9c53d0b2136ce6fb202865c9abd1f386fe5229cdf5ce4bd7524bb958f64e3e42efb886f3d7a445e86af7c343be7f4adfcc1fcd1c847b35da739f98bed72e14f48cfd6bb731d6ee1e811cdae037d278d9e081ff7d6bb4c25ec3b9aedcadf0a6dbbcee3b1fac4728ad0ca54c2ba73f9e68ffe687697bfa349654fa0ee337f47b3a513775e5e4876e9d2dd295e7de20fa9e727eb1c0b7f2974eb38e720073be9fdb867dd06b7f247dbe62c0c25cce6d1eb7193bfa4be394da16dfed9be67e54ec25a17923678fe067bdd5918e7acdb642b61adb3f047bb75e8fdb0c954c25a792542ec2e17ada447b4ca5f524f1cfc659f581361eb8e420964f39bbf23798ff3a223d938ff5c876fdd0643096be5ef48f6fd77248d6477e72e2eb2396c0ec6a6782e5602a8e6b8d9410f049bfc09699bb37ef19742db6ee9fc1ecafbd67530943eada2351d24f6cd61db60d8f6d3eb111b270789ddfd62d82e7c24896477b988fa88e7ceddf50ee3dc85897058f94bea5ffcc5beb90deb2d6cf31b7c59d8bb8dfdc5b2b9d851fc71eff0179b87cd3b7cdfc21f52b76e83896e8612d67a47bb0e73671db6388fa8cb5efe843477d87538f68ae655fc3c4aa1436a4a0f3d4a673642eaa3d831e6747c0d79bfa04f9420899a31c87c212589288e78a23f30a9b706608c22ae009344ccc816a82369e8113538c49e5ef0f3ba659eb0e8ba3852a728d19242093dfde2e48c2bb09082e2a2ca962bf40726515346a8b8a411828b17461c69b24d71ea1ac69e5aa0c36d8d7452fad40492d85394112ef4e98a12b7b00c19acd8912249bad45b8d4c21e9628086311ad6f46d18a3c10b321905f3a5bf7fb9374d84b9341ac6680882aee0b61ac668c081b2204683136ac5bd6918a3a105155caf618c04699a0454596c4a98ae0d6353aa18e21533221573814c9922ce50c2d444ff9edf8c1319265b921091aa41cb54cb8c121a502021860f6188b1c3154395882e5d2c899726daa5d53036c50922b22935c450a7286942cd6032c12063246629608c70c90d6352a0da6b1893e2837d2226e509c60eea0538eaa48b9329b4a56ce93f675cd3346d8634358a528dd254a338d5faeb625aad344d5b729f4463f284c9d38f8553df2bcbb20c8a12d20776b153dc2f2f2da40f845206d69af6c35429a5558b413993699aa6695ae682c6f49f6c053186a718aad417b8bc2095314d891509154bdb8603932f8c0061354dd334a9e903ad912e90ac564c61e212271903550c69cc60a23ac24292180a4c30840c4a456ee68299e9d4249748fd4c19a6a40f6c19c8d8fb1e09d3344dfb7b7eed9fed980c544cda2f61308dfe5ed35cf8ffb346d3340d0625a40f6c18bad8ab691a5396d11f622f78a182ad844b1225524c4b44cac54b37998a4274166b42c605da650acac49719a79452ce4b39e315386ad470c6071c4cc48a90e923d945aa348c3191e9988d3e6814de10e6c84129a551b2c22139a91377524aeb51a8231099978f5fc498bcf4775bd0a2e75db116caf4ece969b36c0585a486431991fa80a463387deef49947c1892b3fbf4b75a505466c7152521a8243578ae851a059041a48266ac37960c488541d89b5f0c40b223122638ad0238844b5ab618c881538d5f4f630d2dba802a4357d08406450064e4a4a4a4380608c8527fd71a0a3e9c4e2037dba02634b4d34ace9b31ae9d253d3209a66e9cfd514ce202a7de2e9ed855332090ad52383885022d9895c8967820d3da9bf782984e95996e5a2792ae30438288130070f6f70570611217285e967a81e66c8f4a12bf4490dbd1eb6a906544262e40067255052cbc31d19dc611bfe1e19697d64a47552438f47aceaa4183d408615306c482021c30a183624906c0045005d74128882d185ce6c2da4b1214c74d6303684c94d42911420c22a85522c95d35a09460baaf1e4c51866383571c61243110817861c691a42a408185c14419345139a98521435cd94561a2b4ab49ac6c9437226960228549090811233c60f638ca18db10686334a6374918d110611a52825aeb84bb5d65aa5bca85be5a56467098e5164694c12413cb992c5932358c4e872c5f8c2033d4dba12c391303698a98229053e8861a525c6961766e882091b9cf0b266cc0d3d88d182184ca0d888a1871c4e20a50a1dca5c996285060c15344bb090504a29b582260b8706892629689a5e689e98700d634a42d840b98e0822e6c30c3b249182072938b0e82284124b9ab26061071d4d9b64e00a2a33be280286142774e88a8605a6a720a6c494b24c21a9c59474e8efbeaaea0a5dbab6614ca9043125253f02380060d0250008b808a302a83252c4804a95333cacce08b185bed6334fb433342039433d8882c3162baaa4a0a162a85209e30cd6992349cee0348caa32a386269e8651555355524e9452baa64a8934554274fcadbad28486ea348caa6a32cb58f3459135655a580306defb9a73aea1a2089a1b144811d740b930e79c66ac01e2755d5d0efddd9fd1fabb61f8b07426022a50aa12c5499a1f7cc8218c285d9f9638a12d545a2bcdf9a10a8c2643b23c85c1218c178c9c0963090b67ec9a26aad59a0d8d111dae6c11c30f4a88bce00630a86870358c3a038b0d9a6b514dbe5a5ee62fc2182d75dd2c7f2f1a63cc166ed0f0ff6e34fd97d3f49fd7f415066ddfa0cb9fe5329cab0ce796219c4111fac4cf53b883a7a5cecbe6ce8d1a51840c2668ba41c30dfa99a437503a339a61f86a6affdded9f9d59a6356f56a3b235258d3566514a29a39410f6e4ac71a12f50c38194f247fb47a9a438e8912b8fe5c6aea7266d099d8c138c32aa920ca96a5a930607ba74461367405111a77cb0a4276ac474f981c9174d4031246d68f92bff9f1f29c44882da920311246862c904b11ae01429c80203314d5445bc94008525630c81260c1250a0787942010044c85c41021ec814e142c3093d989902e6092926579c48808207b62471450b66920881c3949b2866ce78610c274a674429f5abb5d619b3fa2ffe933282a12ec42e6e861a0bfb39d3f31c0895d184a7e7cd68f2f33ac34affc02e3d3fd70f0424d07b9e030914a3f4fc4b8d969eb7315f1ef48cf387edf86d8712b6610bc2a6c11c47b3351fdacd5938bff873fd9eb3f966730873acbe3a17e18e20b6bb2077c21d415edd3d3e9e4220d842b3fbbcb79ef3ae0b627b7b6cb5aeb58783c4c38e39b7bd1d85a4ee8cba1f19699d9383d8ee320712f6e3adb09084cddb600748d83cda1e3968ad1edfddfbfdf77342eb5c766ee5ad56ddca6be1a3d9ad67b828bb4dce5739b9a8bbedd585a4c7b93aeeabb770d1eaadd79553ed13821869dddd2856b591eb473c446fefb0bd5c6414abbacb416cc7cc810bc2e21ff38794b3e11320cc71647bbbd0e4a2d1ecee42b2bbf71cfc25757721e9c23fd8c80101f1b13e1151a515d0303604550f11d5b086b1219ef41052cd75774ad5f45c0fdc61737821074820ed9f0d570e0534f420e82bbb01b7c2f2f224f0fcabb1f3ef7ef6b334feddc971ddb66d5b878b56d06e5b7cd7e93c1acd5cd47df5c3469b3fd972c60f4e7dd00573ce4f478edffccc73cb45dd7734fb9b597bc5dfed9a25210949a16b8c95aae92ffb912ca271213df137bb5e7a44b3b789ebb57ca6bfd8db8fed2eedd9b9ef48fe488a304f43368d47fc51aace221553d70551c4289882cef907b998f00373d634b4472d3e9ec68564d4a24c9841bf9c8512084784559ef8d3c95b5e5d7b46c20c8a4e3ae7c95f8ddef1fecaafd7f042237b692a0251fdfc513fe36999d8c17cccdb9cdfceb37f94ea8bf334def27e745d1fb57b9ddccaf7b2ce6bd4f82b3bcdd7b7935bf52c0f84f8a22d7bf762ade772bdcd3ab9cebff2f7d3d96be4569cf7bafad45c57b36eceeef0a421243f5a06843a2f3473b0cd5d58c3c1acc7d52ffe6accacc30942a0af46d37f96be07ee084102dd8060731df4b6cb9fdd7474cc5f8dfe74348d556a748af71fd4a2b7b8dd5c54df7dfb273bea45ec20bb512f2e531c703b1cac89f9fb8947b51341fbd61175f1f51bfe8e64c7cc63af3dbb09332846c52a356adc7b2fdd9e95c04177ff4ae0a07f5a3b92ddddfb475bbb0d7ec11cf1d603a17bd1cddc35ef47d79d0ef489a73f54f85cdcf154e6af040e7a7b2b7f3f2cbcddcb1fedecd48bd841973f14927a3b778f87962955af3057b3b69f0a5dcfc12825eb1b41d7dab42dd75aa65e44036479ea155fa51ae12261346481caf2d45c8b936fd25e4fc4ab8f1cf75a3daed6ba4247428b7217b29d72b8e943dc3dbcd462d2cebde21584ac4eb3d2eaddbb4c84493b3dcf7c933d9753e878382434271ea17e859b3e947dc34b2d5b0f8734bcf4c1503dc530cb4c35ab404f339c3e51c82aca32b5a936fdc482082296a54b55928258104a3455177ee8d2b4610c082dfddd7b81ad41064d81c0d29489f938a90b5c3c8e3f94e1d24af626d1772f8ae10a574e0032946519a96624ed40b8d7ecf4ab8ce4e548b5ca48d46a07e29dbed6af0ee4e6f51c969c73df72bc02c4f5ec3723d9e49b91329b91b48c44e9b97b4f36f7ceca518af59bac94fde6393962a9c7c94837198995913c29efabdbbc012d236967dd26c727fa564662ad32d2eadcbd3c02d21520f65d46e232d22ae76bc27cdc7274a28a727afd4c61894af751ea54d6e6d79496af5496ed3f18759314a55d524a6a2e762f1df7470bccdaf8246a79327de6e123ae97988876643a9a3e66cb9678b68e06b7514a870b4fd4ce9bf06fdcc83bd6f264387d7432eded1c8fed37b16dbec25f0acdf3cff52f3e290201f6f6ed1ed38683afd4ef481ae7242ea2ef4e0092c1770e04c7750e7532d2959ddf388e0ca74f06b625d28d8cb414ab86aed4efe4781d0cbb633de7acc3865e8fd8f13458a771d6699c8b5de52162bd3beb1d8fc7fc3af478d4b8907ce1cfc5c1162271c71380e8dcc68164709d2b2700b9719d676023c3bc824e46d2f90db8bac43a45461f5455ad691ea29c73cf3977a883e150ac818d62c7cc43c49dc6b9d3c837bfcfc146d1e6d2e3c143c4bd3bf72e07995de4cab08e1e8fd6639a1944052f998b740588ce77be9375f2a7cf84dde7df1dc0bff31c0e11e0d7c13b8fd8023adfc948397a3c12d8b9ce2db093756e01a11b02381ce2b9d0bc81e1f44162fa10cf77ae839b00f09d13b9113d1fb463fe84743cc7edec9c931e09446eecec7c6705211b9ce7366a27b54ec3d80f4e4d420be9d7390f464a1ae239573d1288ec9ce9433a171280f31c7a1568e9e01584fc3c59e9b7711b99081300ae739e1100f0a69ef31c0eede0a60fedfc065e6a3101e03c1b5c48cafc69df2c90afc383938662fe58df727cc4de6f0e32fb8375917c7c0b1365ed9d0078009884e933ef8361f803d3675e0029f4f741a9e3a60f6df000e0a556555555d5d006990900cfefc14b1f0cad0080ac42ce7048fb0a7f45311fcdd62e876cc4c284bff3f9146690ce27cfa78dcf0c3ecfc23630928d33e11fcd8ee7c1483c67c247d23913fe0d8c757093ce7f1edca4f31bb7819b74ce730f176997e732c04d3a87d327030c878e6fe02d7f4793e76876cc3af80acd461f74cc45f630aaa39719b4f309e947989f56626ee9839ab9df78065ff1e41534381cd2f9dfc119c4b4f4c19006e7f977347bbb6d08633f7cf94187e668f71914bd5c1d3ff5c8fda2162a133c10a6d107fdcca4c16dbce3f209b2e59934f88d7fdba3971904876666d2e03a5fe56fcb47b3650e92b5cd5434c82a6460af979ecf00c3e993693948d623cca01b59c75a1b7927d6b59ee6222a3ba754745eab8ed4e1d1c13cf985a5527314c3a8dec12aec601574ce456f049d6cbd09c0219d7ca74f54438fc7083a592ab554a221779c903009f45d255ade0610481a4764023617bdd80f50585a1e07f48ce8920e456ec0e2e415cb0b4c5890b4e0c3181fbca0103b09abd0b3a1801656240cc22aede15618624fb1ae42f38351332ac619ee07a38222f499f25b179b9e9fd7e9adbf974d7f2f9dfe5e33521f2e8fcac2133028d1f50ef8800492b74f5c16748557d0292365659d65aa31380e7a705bd6defb7ac15af2a81192e9af7ef5ecabec8910939e7409904cc77c24916ab5f5f35cc5b2ebb755fee68fa48d5d8d84d80bc988635bdc437e491dbbb831536ac54a9ac72b52b687277b5f363a2b61d3f640f59eee9140966ea150c4ac54b9e1880d312b53fafb4f0b9ea4fcfd8b00e18c2c659499e28297344bd4292fb4e062069918f8305451d0b5d66a9fac48996225ca0fa45694b8ea82bb358cf1102340c3180f643a6b18e3e1052fd42653b6263918a99f0d4770e0014a4b539312b4c91225f4b4491353520b5f7028c2f4248910349c8089199449950b536071ebad5bb68022028acc1157c29880091395499718ba58170f34f4772fb7f090d0c3def32f9139c598952443ec10eb9c86b11dacfcdcfac3323a299dd4cb5d358cf1d0448c8718e5f2f7f090a549052650dc52bdb532a952995c59b203193b6cb1c5153a1489e2de31aab82d3254dceecb14b79392e2ba1ac6aa40d11654d941aaca932a4e036818cb618c1cc0682249149b2215085654117a6a9b9a8e9c8006335600262a49119b6409127ac6eacc4699b8f4540c8b56d151b1c48de19888612dd52a3a2a96e82913978aa58c04081692a822f414490f1409554252820dc90e44febf07378ce5b044d36818cb014b7fffb15f3858c3996803177983163adff5777b5abeb3742acd0993e8af87e2e0454688632c87272d8fc303fb751289fe6c646a2677cee69c4df71b6c933fdaf7147fb6efa3c7837528816e7e2f346df0679bfe2677f2e256dfbba721ac714cedca0ca965d1566bce09e3b80e6b1dce7aabede97038fb8ee6f1d8ce6d1c27b36f9be72356d11eda1aeeb06c4eeb3a1c81b818bf248cdae839ffc3a55f61050cb9f69986b743cf6e767a3e60156dc844d3f8b8fcf534f70ce7df6d48a0968532c87a39af38b9cb48c0daf57a07e0fce6f53f1208e7c3590175af34f26737178d4f2f04d834f251f53182aaaa2aa92ee1a7bb2e07df8569e374bfe96e835bb83beb6db1edb84dcb5db649377196b17ac430c218e36a750ec2c715f7898de08acb2bbc7a9176bbcadf4fafae7dbe751874836c6036f8f8855b2cce831c6bb5c2b023ed819be776aa941bc20c62dd0fc64502499140b06f90401d0fefe7e22d8e31bbb6d2342dd3f20a1b7d90651677b7d923ce628c51b3efbaae7b66332139bd1eb6bfbb65efb68d0c439843cb9f2b3bd7d95b57401f8e8ee7380e56ab7bd5e3e19deb70769bbff855b7bad0f4563a665074faa9435cf82f3e7985307de45bf9b3f65e2e5cb81e09a4e1ed1f88a712887bf41e9f61792381bacbdf649d4bc278acf29530a3687109afce1ebd10a80d228d91242c2ad95809b33087ec88b8f04a4f884b1b4a685711de4804cc2855d727f80cd6a79b655a966959ad35a35419c5d589938f506212242c52aa84c4f510471ef9e8d431aa85244782f4e621862d53e82a65c542d348eb2aa4e57fe098424d8a4e12162f1f9bd2355aa620424b7f47f22b8183ce3e29b594d20be92aa9643dc7cd67194603d04b4ccbb4f478cc1fd48b6800f9ec328b414501111487e26d17b91424a654129664a4359cac4b20994fb37035eaa9b58c83210835cc284c5ab191f6cd4803743502cd5f6c4fcb3dece785267d12443b9d4443c3305d3384396a3c0a496de1f47ac4dbf9ee5c0d5c4f03d7a4bef9c43854ad9dc7c7bc769d6b3c5eaf18def5ed2d1be5d4da6b9452739faf4f9cf38a59f5d9b49f337f499c0782769c5b8c731b5cc4dde62d7c83795e874d63ded76b4c1d9d73d7c93cafdc23a96bfcbec6efef8e07c2bcb5af816f4e03df3c077fdeed5db8c8e6da6bfe921a277fdced85e60ebeb1c145f65772ef700fed36f39c1ac52a6e9b1b554fbbc5a99e7935bf59eb3d2029899e19421cf69a2702bd263da3d83de8b57f34d34f99e11f09935158b88638eafcc41fa5f3f007cd1f124f0ff81692124218a4b9087ebee42a08cee5da70ecafdbb08bc9ddfe759d5d3b35caa53618d0f421c900f9f9eb3a5dbd66587a3cb2735de6a980bcf709c87b39a937fcd9debee2f017b757ed96835eecccea5c496946ab7fc52c1ec253a8696bd5f2d7f576f86a21dc3119b0eae4fc0adbe9537db8df7bc6b8c588a98459ad8370079cf61589fbc1a8a33a1f820370cca9e18b5f32dc78a865db495885e1c28e19c21c529a129628a10ed3226e0831c61863cc1fbdd00cd7040993778084c9f7e8de14ebe80e771206a5469841d6de2be58010b8fc597bef96bfd7eb30d84fa740e365093a7cdcdc900218e29374c42630877c8dbaaeef158bb8dfebf57abd92b8985171ce76360509f421b596bf791362b8b1e351d719764998bc094732e84a2aa957b3dcef2da5bcc49270ea9911c11db17b64d772440708b1e08e0eee881d3fedf3161769405488da20aacfcee160ebddd890401991aee7f1619fddf50a7b7a37ffb2b3b4cf3ee7b3cf6e40a2496b5de6f1b8b9c5f7365acfdee3d2fb51afe52ffbc1dd66ceeb7a3caf4752dbdc8604a2399e7aa798e7e6382058d773f097dd0777d7b9bb32cfcd797ce0dc3bcebd1196e250138407f8ece7691c07dbfc06f7486a9b4ce3e2a25666e1222f6f588784c5fc7559f60eee40814aa02c476b351cabc546b16a6e266cda8f328b4fd87e6c6065bf6956a3210845af47f68c76300784393e160f2298235e48b69098e49256697eb6ce7acbbaea3f1b3bbc7f76459f9dc57a77da43cf51d65b871eab5559b5b26a8639e23b2cf7b318aeba7a3cb647af87bd76a1b9e1795b424fcf98613a1f69969e0f1b5abec862b2e214433f4f37ae9b56c222dc3e4ae97744eb6114fd808b52090e76e0c258124a2d2fef5b2debb97ab23ef3a7bdbe851530e4b6befdcda3f57be8b56e08b05b997a3e608be561bbbaf651fea852296f692bce2bacb5d65acd5eb3d67e9b9cc5dcb76b78d3364d4866759ba26ee7b6c32db397b0ace727b3d80856d5aa652b7afb8a43d0d345dab343099f75f62e890b25ac669a19f9f98162ed4f4f8f134a7b5eaf27af1b3ba249ec8824360d63506a8eb8a15f1226e5122256b7b2590b1e70500645a5ef477640a1caf4777bda2662505f62d5f2d1a65446a818949586b12d518d8086b12d4a9a6b5979eaf1a85a6efc8ea6762189a9846995d2cc515c9b5cfa9a6d0de2d6118090103b885d1f935cd930b60549d363590107250793301c3862ea7a6556484e6dce995dcb2285924a4ae9948ff1f58ed4e883aaaaaa4ec2609d210a387bb8af1933428d11696214353ed9ed96769c734e4af3e76a9abf98a3069b9e4aa0861d4fa92410bdc5100861360e98038f70975c2f1f98d6defb7afdb00f9221e1fb998279b9148d92ff9174e2fb947969ea378c05d1458834fdd3302604136d04992640c358115f3a5a5b3ff805da6d1e7a3e563de1ab0782760a3f614f2313ca6bf81b8655a71bfffdd8634002f5c4ae9946c7cd0d2960a2e3e7350016ecd082973caaf4a8d617669c61a8bca2be10a7f4e4398977384873e761d3476f1e7af247b5635555557bb14af628a7e65adc59361aceee1046ac9de3e2773ba336b3f17675211df1373be36ca49fd9e7b55c2ee266ab88fbca7b9c5efe6473f63ba2d1e622eeabd34af3479bded624a42c66df0a337ef547b2685e4853fc4da2d95a96d5db78fbc52699968b28f57e7c124aa0d6bfe814b97fdbb32c7fd129e372feb47c24bf2d6759f62f9b998df45ce659ddbe12c57b3fe2e94990404db103fa2ccf0bcd9ef4b379b5d730cbcb2cb5c947b2b35f9bd1db65f98b9c5d793cb618d51c8761d38cc3943687299d343675462ff1cd3ddc3dc3f52d9c91206135c695d56ea35dd947576f187634123ba8acfa1dc95844b3dbfcc52a8938fc1d4d1eeddeab7d5d79590143ee7734b7dfd8d777199757af466207d17ef5ed24508fc7e6d4356b0dc3a694d2d6b21c8d44035421318925ef1053dd759d4ba8c46b17ec9a9864cca119010010000316000028100c098542912c8f035d1f14800980984a6444150e235992a32888a120066218438831c600640c40062968cc06c7d6aa6cf15efb8212c56a10b70dd666790d283b4401ca2d5ad676b843b80de83885b0fff6b496b450287d2fb03940b9a5edfcd1235cc7eb090f5e05af9e0e80ddb55e9221b9a3b423cf2fc1c6328a41f1a53ff4ee92cdef7efa5dcb4b5255c905eeec7ca8243e146cf16c3333f79bf7a1968581ddccb18897661dfc79258478454a2c27754b26a6f52e7eed08b25f15b8a75270b85059415056ff99310bf578b27c600e3c18cc0f5aa93a6041e6b68afeb8da1f184e13e471bac2c836be96d12bd748203b38c2861359581b8a953c1991c0b085cf3da0c241959072f035703d45d0d3afe19b71a616b552ac40ba21863774ff0616d76101903231a510d0c32f2a0f3c54fe58e97ecd14dd1429fcd5dcf9dc03dddc1fa88f45bf649bab3317d6ffb410096b3a7fca0b7038b50ca820b929817433ac87d5260bf59041ef8457a20199e31525084fdf5a0305e21128005afc71ddeba585092a6cda5565411fd3ced687355ef39e86b03a70a18d2bca2e429fc8503af4018aee5bf68cc3cbd44d0d4569f3a79d1d077b1045e4939676cd9171c8cb5376a85a5ac0f7503f20ea8caff640513468d1940ca5eaa12173c98e063d93e7bc1cff26a0dd9028c3dd11e7b5670064a90fe5e6732977f8a76ff15db05ec89ed03aaa246e353087766446ce3beb75f1e06fa206ccc10d61ee400d053c0ee279e8a2d86984e6343e4933c72f388362f511bae7097493d0d17343d5d4d1e01a043c8864a50c48269072030b0b852ecbdd622e845695032d1bf2d95c994a7a115420b9694e78140fd269b917c41eec6a7a623be660cbceb563a9c023f760cac14b83d453682a248ed7d3ca9989bc80783a3bc9f3baf59c6664b648daf094e48690f30260dbb135730cc152d34447d3a218b3af0c3af77b91f184d3896c989200528d7c688b33eb27e133cb029d58d5dee8a95684dc530cd8a166220277f8852c30e78cb196c1937639ae73fd2478eff23af9b20cdc2332a5e9af48db22b123d9bec1c13be74e309e179c22ce70a19c995079ec30beb41a592a01b37990768069b164a4ef7643b3d3a3e5128cd29cd9fd002af84eaefd9bb517daaa32b5931b8284bc9d3c2b1d2d8d0915812c6684b9f212ba5a37cc15f1199960db6e486ce20a88b8d0511c8da23688b226cdc5c0842fd096f67c10774ef10991e5ad44cdfd2db77bd0ae5e92ae2aff8d9be7299fd8c98ca27a9cf05aff568510c1165f8f05c154b6eac195ec18149777b6a14bd73454b1c8f8b0a0465f15487331efaf87f0f75f97ed2f04845712c40484e9d27f6f0ef423045421d49201ecdbdb903db2ba846f79ebb293cd76a32734738504ed6c7fe57523da0ae84c619c202e41b8255a45a0bdbb6001cd3d75cae76d38fd6ebbbf6acb90cd3a0f306a60b2637a1a33b4e7926b296c5b8ee9844cb92371f4a4d46d14e04715d6c9797ad2ee088ffb74e2ba397d8d06602833a6a0934b1f74c7249b0d0007095488aefcc0832c8a4e1b16ef74885157804d6e996423d424ef371aa3d2d341b5de1582ce3cbd25b6953ad7f05211a8ddbe159838c161486adb51faf2d41eaa08911afd5d88c0e21e4ae48ed424cba8212f5e2c7048576010fb9ed429bba1c372c8cb8525afd111884dd3ca64fa99e1afddb87973a922d6de849fd2ba5f1f28f043151cdec077ddb1db82a0608f88dcf070fc3ca8283ef86422001f54ddf2733c956ba9716a33ff21d214117d0853f5a053148216e74d1dc3776586d5b67b327d8e45442a369656c06c414576dfbf7f6019f79df604145f407be3fe8a793024bb92804d6a63fe1874441579857d8d5c8be3153ac73ae402d7fbbb61af1e526840a2fdf3e46d3e83390453e0c4b6f0e65c76711e860080486ad7ac862653889da7594261ac6b1e971aa8f088466f225b11f9fc932ca31ddfd89fb6c93f02cf95cbf5e1b2c6f1a808fe360fa0ae1dc9e713f3840e7b21b61db2fe3cabc5989d0207b90bb1f4ab296ed1bffda0deafb8d2f8e333eb6290d59d8f3b6975b9bc7b4e80e059d0510b0a4fb47edfa89cfaf8e281397a8bbf8a243074259a9a05776ecb98a8cdcf3b71fb8ab137061ec2270b698884763e1e2f9870eae07b5bea0046ba15bcbfc16b9e71da2cb7f10c88acbda5448ba2ae10caf364cd645313e585b2c37814c189b15265276d3a13d84c8dfb4d09d61f3870deb64002e656d6dc1e00907407f74b29677e92c251914c1e5e188d085c4e436e20af21de19853cc69f3ba600b2078851142c0349791b9ea6338ee153cb7405cfa3de9ef6623df13e033ef52fa5cc29210a6b66b9af3f9ecaadc62f36b80550cbc800503fe36b140b19b72b253039b6dec6689bd1b6346ed6c3ed6186861a57b352b18e13833fcf35d069f315ca6b8d82b6d7fde34107b56efb7d5dcef698da71ed86e2fc79a2bbf426d5df1530a5f576e4e00af49181fa4a8f70708a2030d8670238b9d50b6d5c5f0b68f334a797598d5a142bfc373378523c571b820844ce86dee05c888add0942038628497feb52cfb2c6276b889f6ebcc4515c07339c573723f77490b247b205d62dd64f613abc4e546343ecc333635b2ecb8d653946937f29607e3fef560dc8ff222a3d71a9f3c1b4a80786e524a347d1cdfda5a3ea18208d1e5586a70d57d9d156237c3cf65d0486b3e904e1a6b34302e2120387b3e8e99ca21f6cb3692618727fda2d6f7f57160d48e4677f19db9319b2c894ad0535a2b5cbd7a22a211c49c99cd9ae76edeac5adbca24525e56ca667b234e154cc7e2a411658f057093fb954bd3d90650f64809518bf5b5c9c40da1560652ffb1b7af91d0b46644a6caf5f293e68fa2e150a2cfdbece235fc112800513052f0970937a57b57dd8d9bb4d284aaabe8451d2b9d604c0eb6cbb1619b67665c4ab74f91759204001d55b98927a22ac84b2fd0810a3af83aabb50b07079bf1649d43d72b8ee827da3a28ebd2fe268bbd6a632052abbfc271c99ffe3cd92a4e7c3faafcdf2472009125ec29821d6a4a66395a6a0e5922f29024d92e7c099d8888490bd304117be10c16e38e57c66c7c4e2b85a5eeec889779caeeda6e15c6acf4b9186a4579c9924133ebf74221f9d370999030f4d129fe52d6d357c3a625ec56e783d76d77987644ddae926e7220556bfefff2d4c268857f87bf2b9179e99680f3d2829f80576eecfe383fcc64acf15b6f24a206560c62e310298ee20c60638a44e486ff1684a3603a1ad1602bb52407f67721f22aa2a3755d5120de44c0da0c81f4cf9ec307ee388b2f702b959d0f7c470fbfb8dbefcee814ed7fbc79325207274bfa142da1636ae51f6eb6db2fad33da00fb904570ae90a32e162425fdd6c235ead51835169442eeddc9f9ced91f6f625a5116b1441b1e3bf2a690925de4982e16ac29c2523a35ed106ac92252774002df30f2ee8c35b39015bb21a1eaad635ae9040008aaf56f30032920ae4955d902bad3262067993fcb0dc509475f8b8558d5ca5103bf684bca77acc2667473993ffcd09e80fa3c43dbd80545688d674024ea496054e6f1ab6d999882c6fe8c6e195c1af35be01284b510475ba4226c9deb8970cf6563e820b9811cbbe45e9ae8ea29a1ea4488b197e9969c87d8b7717b73c87598d4a6f91318ff02ebccc2408147c1f14263bd4f82b933a90092e618fc39e4c7e4ce4736504bd4475ae9e532aa731268a8d463c0ff6804e25e2cc12f346e45fb3594b275146d02484177833565c598ab765d3dacbe24bb60cb7b8ce27cf6f5b905e420160ba614330d35a1f7d6eb3c0d23f074c4b5d81b768e0bf59b278155333b07958bee4977d1400efa4daa7b78e0eac6ef48f308b3042ae36388d2e183dfcec1663c7766475e757fe2d449ec9c14de72bd5aa033bb623fafe0c7cb7b07d4d34ccfe56ab3fffac847e6d08d1596976ae38663d0bea0d220cd1c5ae4555b312ec59ca86322eb0eaa52e5192ee224fc822d2d23258d3911a0cf8e2295deca0d9ab23fcd08e00305422a0d5868a555b4d5b3faedae3e1e935864f3b701a13f9ab16d07d15b41a08104c60568bd4f2e951974f8b593b1acce185605c1239e12b2a361bea9983c56651a44d97e8a1b024a9aec307782ee8d3d94f996236a845d810dd1e88986df5c87ba9d581b46403e2aad4534bc3effaff6ff316a24c7c3d5bd958975097e404a537a761f40809207a96aa3ce8b2b0df2aa4af642e5c462dd1f7ef64342ab5547d34f7f40dd9d42cebe246628c443014fd960bfdb40873ecdbb67c9f1f933163e6a34b3a05d3ea58af89ffea5cce1d057a80c62d939473bec8004d51194782f9cef79f20ab64b56364bcc1851bf19f9100229921fb25f26d0ec44f5d8e903c19ab41434ff4184e1a94fdc3730a34e57d732222a0db7348723428c4f31e829819092f0eb4281f9cad903ae8e8bdff57c9c72ea76990a0aa685294c14df494aa96f115b79d5443b5694ec77c3881519db863f5f7b5327f61db6b6b63b72c6e7ddbe1d1bfd72967db3ff9a211067783d5f16070064400a3252859059e623801720fca17df6a434762bfa5de879cd2b24fcb5225f43d970f08468865f21a5a20cddc4811d472d2ec5926eb96a0173bca4c1898a7777149740b553c00f291c8816524b6f388bc5d946e70777a972aa6243865a36790b9120fcc1e91d4a7b8e88627ee80dac6578d0274bef7cc20f93de209cc92e88728080dbf47b2f746ea874aaeb32978b984a05b50fd6bfb9848eaa7d90e254fb2d1048a25181f6dfd64b2e2b679576d73baeba9b1e3687eece1e5e4ab9e4955b196112b34061fa24e067efc76f3346b9d644a12b2cc0a7e61101c0533bf6601c97eb6ab51e4ea3bc4088881373198e5f1b60fd08a5105f8a67c4f144fc6199b7588de0fdb3a31cefcdd2f225ce7358f9d16cdfc7d4326c440a1779e5f93edadde2e01d6fcd0eb91f74581927d11fcd694d685172d13c971ae385c23ed65dd78be06c07dfeff10fd2d44d419b6b7bd4c7c22236061358383d5db8b0aa56aeafc406115fcce24ee778c4812b5b0b1bc05bae35ab8a009c4a59b9d7ad8131d64a4f261a1c12ff998538971ccbf748ec0a0dc5881893b62082e9d118f2d1a64eee150815b45a096a96611f22beceeb71245af0566886f249fbfccd8ea1a8836247f552301eb461f52f281606b7e40a0e44de2fa8880174862031f612754d839e815b4d0d386802b0781c1004b2278138005e3dcfdd0d85746ad253531daf2a54fd1f1af4b7eb3696578684ae94b509d043a64d2742c07ede78a20c2a7f8cc2be6832e1d39dce86d1a3e3c97f324f3836bd6063432a90c0e6f9b320bae1ba8a0a9934bfb9046c48191e496ccd817b93177357e33885bb246dcdc1d3f6a0a3323f18b9c8c0d466f6571c63866157b143a0406a8e7388a63b5d0af7088441f9a4b002955251a462effc72c27a603f8b5c0f4f418c5573e65b4d7de19e699f03a436d142b08b4d84b315d34fd324b18ed0d23d4f01a742c9a1f3e6075c1964b2a56afad1128555325d29cc101dc80a9a3807d3f93dd3253cd780ebfeb7530bd06d7bbe271c0c86ebe3d8b57c2d4f0ea2c677e349106028a75270c67250afa46909252269dec56ecd8e0189dd8f1cd661d7629eb7549c51b38f299ea49a4183444193fd1bb99989b252f09915d7d010e7c058e3220f1d53171352b9bca134fbf2a9a576764ef8897f040ce936e7b2da421933f93115ebb9517b6a96464432f91b31cad710b53a0417d5679e32c3d814b38fb02738f19ae0f6abb374ccfc9ca7def3064df14605eccb08752281826f9624544d5d55539bad9d580aa85790a13fbeb33f0128229ff0abffc1b48c5d7ca38dd600074c5b692975bc1960d1cc82758aa6a5e6a9f85c598a5bd9b8e6e90a21a7f27382a334e0b09b7816a5cc856b99218fa0dd16c1b9486842ea5c0426d8b3fa52478684a6add5a4daa9157f437589f1c864763eb348bdb6ecd4b7b678269eb3ea69a1a1dca51d2a8816875908a13eb65e8135cf7a7effa0e63363655a11074afb52d5650a2be9648242e164bc1ea69a0a492a08c9947b0fe1bbeb7e640f09999ee8108d4c3a2449bea5d221bba05b8c67065959a86d8bbff24aa89338687d0002711bbdbc6d87cbf8d013f177b05d9a38cbaea5164dc2d5e97909b15069a3b86e2aa3d28a541d05717e5938871f79c0c2e2ecc7cee6a9c1574ca9c52a12dca8ba140b80b999122b9a963e4bcb601e56e26bb9492d0eb7496d0e2d2b26d4529aa80265e3875870fa78ad215abdde436bbed15a81eeb9a9eed20a38e4e1476d01413a0f3ed4251a41cbd5ca54df10758a5265b150e56114c5ac001c93d4c61a3a8fb2bcbc9e7532ce9bca75d378af6c7375024462ae80269b7cefb5cb7cdd890f7ed2cfbdcbedcb2d9371106fd8ef349cb93b6884e10706a87d794fb3315dc9231399b0d21fe75c35a78b05f59e1a609256c6a31014d3a6c672272ff44573ae043e220496792514e0e090dec069edcf3d2917a14b1684fb7668cede95f7fc2ed009ffaf96d1bb4ce5428f92fdb66065b9aff5fb3007fca851f001e6a9d7a6c2fee5c6ff000a52443a164db3a2b64b652ec191853ed6913c5d09a5c331988c31d5946dd0b56bcd60fe84335ae49b61b0d04301157be4ab220245eb6d33a6f60b1fcb37ad3e039b1265df048b8b2681deee6b5a50c8ed94d33febcaf2b246e8248fb5782f16d47b4bec466d2e9014ba7844c386630655d361eb121495f46078642c0891266d3c4548f8c52fe9aa1f7ce2954bcfcb3fbbabf783bb014c00c3fffc04428002950a57050a7418c3685339a05be3e5e231683ca87770ec4a2aa8f6655739475b774780d271d145a5d58c43666ba27c369199ef2f503ef06fb041a5c7ec4bea4f90bcd5e1d06d2f97c837095922af02391c526d3ebd03b7ebabeeb288ef398b6489477e96f58a9df4e15c7d960596e83f0620ed14be7540df0673f4adb1e74ca547c088ae3f4ac0c314fbf1725466392e543488e0983ad25bde5b1b0c412d9db7dc3d21f87c0ca55e39d9273d187407ab7c9f1f7a9b6c8be763410cff6eb522966808110a80ae50057044280aab96fe4a8e58823b0a5adb5f503b0b1a043a2859bc565315503b76850a642ef36204c5d7b859b645f7e3a99fbd4b72ae0017ab66c519a2d54507698b74517698be03d5f4793b9e01469bee052d3ba790fb1bc71d3a0b7d37c06d14306a39aeceb3fcb34225f4cdc494abe71a461e83c07badad06adba1c650532a560f8491ad02482c11e56518a50ebce273f222974d61555e75efc7952bb19f5f7473565e8ec7f29ae192b586ef27c3496ae02f54c27cc76224f0fb084d89566ebac2377d7168853167f3b00e812e2a4b7f72d9223e26f53a51274f5e169b4f4f269d6d8241e58de388d2377ee9d00aff5030cd75e86a6818a4c4c2509a3ba1f12c5b45cb95b1e7f577791f3fe016571a1cdf5409993010f52fa9cfbd2678a88657192d10a0b12a63582d0dcd130e863a900885ef0ad2952e8de9958fef33dff29665051e8055571099a52faa8afc21021359b8455183e281460ac3a31ac47a2d018e36fc72c05b9b368ef13487880412c648db879bdf8510baaf55149c24efb70ce0ab8064971e03f79040df765536d8b1561316b0c964c109b2ffb1e5d58184b1533f0a2210d90b4b91df5dc7294a848225b0c213ff407f121a5a25ad9882d80b9e48e5ca6776b7ecb2096e3f0617c9394d08f598b4978525c632af99c26bb17c0e03f4d50944e147382fc6f76d805d03945188b3962478c0b6343701296267089a3edba182cf3ca5ff9eba65a56c41bcc11a4291febf35da25c58c2fc24e44786a3177a18d8920850ccb523b5c4309a9a155cb45b3d6a2fef4dab2eee3f42c0308b9a24505020545f1925565c336bc9f080ff80023fa13663492614e41db01707102a8120ad7395021bb11f23e81a55b81751c569c9e8eb80010a8b4188d3b1314df973255b311c5cdde162111962a52fd9c4ec1106b0fe7583f0bb112a84fa260da185fc83f1363263a13093f3195133233faf58a1376734c82c502da5c0f0a6b3d17a404eff93ce504190f7a30664b1a9d10b45547d4ac6200ad998e5a8ce398123e18a658548f3f31e2d04b7e62cd2d5e44bdc926c060d0097f7ba8efafbae641bf3e561006ec366f8d6e1e581b72185219068a6996f67410378e712073eed8909243cac8e35a34b5b4af2bb1d9d2c2c30d87c02f340f7a215e4f01dd04beeb6467213b8286c87a1fd43e4094c40c34fd4ee791a4062e7528b22b8164c8c32b8156388aa65a291a99e5bcb33dc195b4ca9307095c0a81b7e18cf45652432b763755279f3a605a51fa3975f30066e1749c2c2ca4d0210bfe34cc1073a6d93ecc5818cd8bb071300c794ff761e4084867592ae2f041f4c25f30ed08e15bfe7152e4a53bcc3c62eec15be40193e150dbfd02bcc3e1c1c110737fb476d29ced5c40a86cb476e46c724ab1b25d1e0bb9c1cb2ec3026efb8442377048edd5b5710e7f2188d8524623d15d11285bf86fffb6f03988356825dec96ee6bda8db1d3e433ffed76e4807176043939e1b9296b9e104dd59eac28c7e12770b6019598f78e33befa1bb606bbca0b9eed3059d66b3f4a154b4372db1327e75cb0688554905ba9c5cad48d3513d72c188c53ed9c945790622d1a427ae6f277ee6b519f7d8af822b7ee50d647c5bca816ffed298a345830dea5ba6cb860091cd6ade07dc43346ffd508204d72d651f98c33a4ac5310fd9777f518c936aec46f643eb42dced05017f0dc17964a6c9c3212501f81d7c17fc9cb2fdc9051e8cd004fd91afcaa2d86568c7e952e9b3a2d9791325a58b95382d6ee12f5aa3063c48081fae17f0f35d062fad442059c640650442c46390e0eed547613ae7bd9eb59b1c56526dc4d9d94678fe3d9de1762fb34896e18a80e4706a35597d4a6388cbdd5f4f957bda701f117cc1a94c3417c01b1ac2e827c3090f220e223a48c921f1fcdd4499b895d8c3e54f099747760d8e92ff5749b8f30317c49aef2874be7bc9c56699e96d82b235e5562181e3b6de50bf1cffe537451226c9f057eb43ef2c60d1084e80a89cc2ed4b73845bf16c800d240c7cc5a85ca7d8cd51cc1cbe7eb8f1fe860f4a5d41968397d871e1bd59c67b2b0ea0f5f8f813867f94b4504d2420b094200b43e690616abea555beac01fc87dbf60314986bffdbeb7397072b7d8b1a8efe3b5404557cc80b61abb0c52592c75eec1a7067aff372d4a55ce3384640925c1a3d35ca1d0eef0e81481a31c8ad4bc0e3fed25905130130f02bdb885a393fad88c94ce861772aaaca8c1ddd8a665554cb3d8722b25b56bd92fdba74ec847703a4cc7cc28a68f01ca7351b1ae28e9055e3d8cc603afe9b312d4eb66a3ae26921a751c8427031cdd3fb3137ffe0006c92066ecc0989160560e48170fe89710b6c708d348b3dceb2d0c52f8522285f2c14b8ebbfacb17ee13ce8702c2aea21dc099512ed4b448224e49096ff72e4db296f693acd99cc96181bb380a8491a0caf81afa24a6e42b626d7f3785eb08e31cc0e035968a34e33678976ab254bbfdf05bd47c7c0412489d33b1f53a23fbf4f257164a5a7a7baedd20e535da24153b1b5a4ee7acf262e3ae10c724e0871981a9fc85c49f0ca6770854002ddbcb1bcc3ddafe5899e98b11bf6e12217211bcac19de9785448127e4baa876cb00dc273cfe34d93f7c11cd970a6a05810beb10a5f71c58ca2f622428ef1ff03c7e8df8dd0a90bb4714cdf7b2e81c7e8ae41e66c3dda953515410adb0ee3bbe15495c34bc83097a1ab1703e16e31922f48f32e9df65e71adbdcf98abdeab3277a4c93ac8c58965d674603b79ec5f11ae88ce92869c0fe3a214aa7048f5759db4df02d0a0a167bfc6ad086c53c82abd97e88b0dbe9bb7c2f58d5e3f8ae8362272895351b1cc5c8a80de2f76dc4b9daf2215661521fb54118fcd03c21f573a67980c1f46978ba1366c515d8b360bea577010fe9fd2da66fc83305b320086e30e3a31edf6ac4ad71a93a0ad79716aed6671e189f089f3603a85f605319b0ec31a58a7d731eb3aed37cf59cf18e34223abed453845ef1002b4e4fc41d37840c6c79f8dc3ef144920f05e191e8d1bf5d48e15ddc16746a7583688061946d68b6bb027cc5ffb39a279a6565f30c6ac0c893b1588013fced2582554a8d312058a8161602e996b435e05cb7c3f8ea7fa5e0ccc7b17580b72148dfadbca34dc7c2dd3a9d97c4203021de590d34371f917c3dcce11270200db35699a461c73262b1d2760b7199711cd07bde1fa1d2797ad1150ef6f5b944a414c44e33d1dc71dfa60b70001f9e535b35eb3d8f53a32e10c82678fbfe2a5008a24eb1d5806c393a55499e58b0fec0265df7a2dc67c16acb04ad8f4ea3364a1cec30025a4226edacfe4614f9b82c2393309b18924bb77224b7bc2052c06f39388428cc1361377f23850b460a39006c714ac9aaa1cf6a80cccc4edfbfa71f10b940828b971ad6045985206b05260481c36d283d2c23b022fa6fef61e87799900d5254c2bcf4d5d9f06a6f729184fcab5ca2384db8a5b626c6178e1b49d815c1d3ed11f9014b12273690bd82de336a01f5f55dcacef1fb31f00cc8f001d228312c7fd3acfe77b19fbe2222f56f445e57ee1356d3e178a89b3e38fb4b925bb561064c9bc819a9b22f468bc9d47f0913ca812ee284e3247c8214ea5a6c44501738789c15b841e462c319cba24f951219203cc53e9a104131c1020868060f0467a4242a08e33b178b5c73e9d7755425795eca72431b03443ad05fa408832535f277a6e9becf0b30829c008a21d3ef669666587d607b465dbd521d2ab4a89728023fd0ad9c1750a0d1490f0e83513028ec54c0d23a15588af731bb3ceec4fc452d5d93674e5570f9de939998e3d424d5e49eae634a4b312269e12bc0e54ba2ec36605696c0a00035d2130374a12d150228b081f350eab00cd7f4e1565d020dd28c247b8464d316b709ddff78a9f92c5ab03653e352cba4e70d94aa09e70b9be13ee3a3988ffc46e09072f557d32a14e3a18a42487812c5cfd4453c212b99bee5e9d2b6714816265a6cae3f9b12ff9b9543fd1c7995d226a65d6abc8e425d1a0aaca155a47d85b52066bcafca582dbe7869649db20c0a3f9494050bb836b23f23da39fb5d9ca634b95dd495fe0461375235d91b9af9fe87d2d2cfdbea89d3ed8390330718ade607c80d50ec049cb93c308d4ac0ad15a7fa2c79361c4c5cc2c5101b8fa94befb358dc4bc408b5778f1c1c658a583fc99331d7c1a9310ca9e030166499e4a616be4b17ab21345565019c2639652a7481e313041a51f116007527db0512e978a18cd009c7ff50a4fc471212a7086cacdb69728055c090aa2363fe19b931e14e421c68495ee01550d825305462db56857f5e5fc8e410a754153ad3c9021905b6bfec3c32207bc0e9a5e508f30e2a1408e7b1f1380fdac824e86721781b38b4aa1c7698fe45473fbcc68b5c06cc7c31f65a5bc23c89dd5f8e7db80dedf20b1c4b3f6a4d7f01fa505390784cd0bca3c3d80d5fb0bf09e4211b2d35bf8d5e5c8fadb38bf2205074096637e58def35484b929f290f24d02e1a3f5d008b7ad5a5864cb1f6e3b9d6c4061ff33563bb4fb13e7167ad7ae620d015759a0c74b8b68b685da5309df3e0d2e6ab112768c608191a45897ce2fe42ce288c023996ab748fe7520249fcdfca0496d7e977f1a88869141fc181d49936ba5d637f55f617bad72335978f3cd324140defc4efeb3a111cfaf60fef8a42bdf84ff1204eedea657525c155850f0265eb298bc4a4511cd82607d1f3e495b616f35b7255ee78499376c872fa5db0cc9198b1a33f1214c74d6e72c69a53c72e2e85036b841e7aa092d600db4ce3e60721ff9a2d110c80b56c0e41e49ef642098d1b24c23e852eb442293da81af607240b8dfd4300d43ae9f8c59ec7067240ef8550d3da90835032d132a3dfb2e911dbc1ca0590631ac66d247d7d25b625188d03b1a2663a2e32f1904909505a297ee4f7d144dd40fbc7394946f32d9ae07a552993a54dc25c753ee8470577fe5d92096925af781767b8e122d1665a4f9226716008e8dd158cc31813dce4261d6b4cb362070b6f508dd44d5f3b06913d4e3b6c866d0402170e06c82edd24235e02156c3b1c674d467d3700a200b664ef0517f9368091c292ba6ca69b18ae6df3ec8e5f7336bf3923b65278c95c615a327b4d2727f4786bb98b7c61aadb4184d559cb2b81c18c720e347f51deb82052dc75ca51950d305f33f027ac5a02f60da4d003d57c7dd41d8c526461e4d1fb1214b3be26359fb3009c5036aa34cdd8853511c6a37239b2a8f7c1867bbf2aa17cb34f430d56d15a00a0259c41019ef4edee4222e1c0000137253957647eebf0571f7a026671f174781c06b51d9ad0016bb621db66e44dd3b0b514b1f8dd766b22bfc766ddd6934a17b2e62426552c5deb30e018815900f3d3067414b12785c99f2363b9fe9b7599eed9b6cb7d809a29a784985c97745d931e747bd84a8297e1b79641e019cc5645338a591c85d33749e159bedf41353178d4870a78a63ac7fc2a9be8dd8249c64e25ad141b17ef426040efd19f5c8ab4f65a4b121379fea7548aefe9a280897207a05075a7f989f9676cc1f378afabfd6594830e1b8fd7ed923ad58baa8549bfda2743c4f14e5bb5c3773f0b8124fc1180c61d7022d058182058b33561b3b121bfa08c3bbb10509dc3b32bfb5d6afee9317ee61ffe6ad619132b8ab3b747b851977bbb94db951c4b9d7cc11cc8a95808e4c789d39c4c606c90de8b1b3df6ee04a879d3367e50afc53c9b5ea81ca0c864d078afa883b3e9f595d630980061d8fac0a531cd98bba02d3c33afeae8a2e5a7459a8a71d84bcf14876b08c603f38ac8163ec22ee8b15314be0587ecf6862592bf5a2399b19f855fb04e5da7d0fe5ac383db990fcd27a5763e8bb8b19a3d8a47331a5b8ec9c1a63e4447ce92e7b204a64a951c72152c730c91fc3213d380debb1713d10f8c54790b21b8fa484ebb1651eae7bbe3b8852c214b6c2163f2f5d4f60c44aa72cc0c13b4df1af4912b8cf2d966416ecb8f62de26cd793f4000680ec9afc1229dddea6ce94e9f413850c708d2bc332d1c382917b32f876d93cc76dc552c7c3788f0eac2bba57b2756eccb3bfacd017b2cd8a011ecf952ea59a79712878e90bc6f4f7f47d8de4f5910f23c2a2c836b28fce656d84eef186c2d780d9548b93491fa477356b89e30e27db37e005116a53946ccc07aa5f90c73c147c6acc0f12b3d1fe7198678fb5da0ad0ce011b86e6720bda4fcd5b2c9506c790484a93d127f8040a06bed9079bea637af7fd36f760a8da33d1c8cfc69a7fd42ca289b9072a858a092c0f25129fc6a54b948ef3ee61f0d627222870467b6b20fa045e1440a75fd3cb4f04224b7eaa522e33bc086833fee829696e09429813f5302bd0593f02c4f6104b47b6ba58f81a5e09fc0f75e4b93d1e0574509c90e884b840ccb7778121f5a6228f4e468813296e2c47655be4505f3657825ab3f31e4a1ccd39d91337aa09cb7252b31ad223defed728c52ee26bd8e09dff503750bf4d6ccb2718cb6720d81e5717e9d6fe17c08fc607a6b1da238fdcbdefe2c7f0989113b804db2881839269a7a6bb8e8f354cd5553fcbb1aae84e31e8daaebaa6f0c25a07e9cb4d3b3e6d59adfe15ca3d2b44a943c725f8e2721eedb9d2f1d16cc0d7792d30c4fb5de7607886f5740a4612e8f2bbb9c2a0c871819ca707ef7802c29166ea55624dc007cf36df4922f482bab26b03d9b8b6ce222b50fc6c8a1cce983d98ee58c36bdc189b1f1bf36942598ef0d8e104c80b05c659e60170c260745dcaf2abfa2016db3ba30822d1202e139dd46a2b281984b88554c6277eb87d033ec5ebe5e0c2308363fcbad7cb89745d485e398264dd918743ca9264a04dc244a5a5aa832850912a79f120a026418b205aed0ee02903098c0afd6a69899a8fccdec6542c894b47c74fa0c5c5c89b7b636edd87c4478154f947ee8d43ea9abb173bc2a9fd055a130f25948fd855f4d52151371e73cc8157f66c747406a716501d09efee0f8e9ab9f548b532164a64dc8b019791bf3b11166de98efc818c9d07d63b508f3395d4337b9286ed370ee30b9bca8a7da489799a66c2b35b8bae4fa3c54470388a08b0bd54887a4c0e7310186971ce8628443851d7c059c136d03ef4309b2c59d196ced534e9cd0a1b8c4ac783f2d22de95ef13c3846c5f0e21246848c5a5bb0fa11821e8cd4ddda6223c0d4537af5d0d0acfb7ed405a6e5a806d981df0211537b5f7e4b7fdb1766da1adc70ad1e256490ccea08bc1e756b3bfe6bdaf573afd34f483d342db89cf81ba9a29352736c80c776748ef67806cc810af38b274812c27f364366d475612d77b2a62fdf4785e72db5de881f7855d773616cc1979afaaee1b401278a2696441e1b2ec5ca6a31cbd76a672d24426c8db989ec839fa2426a8614bcd39b501af308e13ede8ffd7091570f9c813235f080fa68313a4bfd87d81d6eb5c49969c30850843229773f5c8951a697150d6fd9715422e8bdcb9b9deec94b06dd4376d3f2d749c6407f6a63d699142ad3bf76ddf228d04096f0ce29ecd8a7d1b580b37fc7cbc7ffad5d0f582372f3a19e2c4daf2c44c1a68818b46acfea1e034bb94d6b35b3d62a67ebdcc2cc5e4e027e74429dd9b582f049a398299cc8344261c09cf5bba1e4cefdb7b1465b7f0265ba9c47d4fc8b728b4854aece7e7732430e1c685641b4a2e34ba19daafc95b9b5d11470c2c474ef537de8558e9e9a195a7ad5e7221223b08dc480e749d5957771cd920c484d797411f3c1b1103a6682733eff6061cd47bbf032ad4156f0a97b16bb2a062adb76ac7d6377bffb4edbc40ee47d77e96d4a3e112e50d4b9a6b83765c3b68d89419cbf3a00d8c199df11a2cd333b64e677bfe92735537ff126df850e5035ece0b5d488abb723d910cd1bbd2c8df8ddb190614e5ea5921b09c3ac445621277ed7ebbd4a672041294fc9f51b03f3aaae4f33b2ab64aaaa9233279b1e57f6f1f36c09dc16684f302cd42692e92dfd9c422b946c652bba057108979edacb18509a1013b0b378885f46fe7af83ff939d045e44dc86223a62a446b7185136cd73dc99a69545c2aeb2d692daed276fb6be7243666105901ee2ec36e26a511820c178f8cbe9aee3e343366939a7a994948451822845a27288a7c88f4bc71eb97139b5518af8d1053f8edbed85c6130d8eda72430d68e6c842ae9912c7e6f3c88182587df144395013fae5d814494f077f526f6521e9bcbac44581a1f3ad523227e0d7d1327a45dd08b160d8385ce09bed4a6d9af86b278b23b0d31ff7b54a136e275dd07ee0304378009fe09bfe7276f9e815948a8f88b1d522a32ca64ef56cb46b5830680c57eb7ba1a7518dff8001f2973aafc1635c9caf583e90db0502bed409dc11dd65997fae8e07c50aeb1d6d303a00bb7a16b063592e66dc9ceca0a5d7301dd6b1257de7adb0b91113aa15c6f4742e23d310c4a44c30db3cd7e6cee70e6ec7e4f587f1b2e96dfdd225eedc1d0ad513705df13a56d641656128f34ab673873d04326cd1ed45f0edaf8e24dd8c2005958bff6eaf848065f06100ef124a7d6d1ee9fc52b80b59c9473dff0072a88def6a902d20d67b436a7131a9949988e36a4033f406a5363f52cb0aeff1999466e940ab9274ced4ef565c3e9e792ac3a4c57caafa0ee4ef62d5017199c74e0005d65433fc06906a262a17b24212d73c6adcd0a3e414cd994fdbf1bf36f562d3ab4126a56fba10b584177b545e1e19e5d2e82f201a95d1ae1f3b3599f94766a7abb59fa1e47a4c6d8e6689ed50367e12dab77e180b0d5d5606f5f5d3d92007798f2d1ca8e44006eebf381cdd918fa3a0b2c00c16bcfe6cabf8a7bb308724e3b854583a6226af7c53b4551dca41c4008fb145d83a04c9b44c980a26ea1522e8c45cd02d9645fbb85eeb6e9e0abb4285a1975ef9fb7f82a86359b9ddda2584894c58cce689ac0fd8060a92ac9e4fae9eb32f8bf4b54a3c469d9bd37e0ec022d73f7f61e5814f2363eb2d295de67e4f8c778e8a269734c2d6c351232ae614a72a261454815822e91276097f9f45fe28215c1de875ec2bfb518fe0383a446799cbab5346093e16a6a8037bb6066aab1d0a80b26d5ea1979215a8d9a1a865e633284daa888696b7870bfc6963c8c3bb537cebfb66b142508bc8333d296708edca5263e126ee82d3813a069a1b79875908728c11f29c219753961a7e0c1ac03ecb61303552207d2cc413c8923f9687337773b4176783c015734209fa9f55c2a301d69fc729c12556a42fec8b5fe36df6ca296f79f476e915024974baa2b34a820e7e3d3d9b7275c80eaa0030c8477f011bde9610115e3398774a129af37450a7408a5f5a66dcf1c2fc4185d7a85261443d286ac838ef08c3ae5803f5f274f48f809a8e8a887e36956416cdbae7cda4975f63732f60b295cedd6518b0ef9c01c7bcf5bffc20f5655b3cdd1448185da6a84bbf9a0099c6f68e96ddab22a80fed7f0445a47a53b380e7baa0ca7df3c0cd937d21ced1e66424215e8207b08a5dec4e4bf8efe680b037ba49424de2ed21c670f3f5fbfe96e10acc2bb0eedd15a1944fc59b99e9537188f3ee2e1bc8663f2b363f14561c342f016a284713ca4c28d9b9fb21714928592aa4f7157069a2fe587d592b3b454529c5025d59da8a4e9eaf44bbd11e2d38dc182d1a39d0d3b20d713b2bf2ec01b84f93f6889064038bd76208010eb550e2ca5bb9c712cf4edcf43b8b6ba70a1c0ce65c2c467252825a44f36f5a7b5016ce0708f4044a7357319b9d63f05d75d2f328d819c7aa81ee57d6e1d528c0ccfd65e27049423b65831ebe04740fe1310195cc7ae049588d8e72ff2c12bd3491e747412eaf4ddcab40b9490f2ca742e41f6cf75446ead4c47888376bee3f50494246b12fb60189990382f4110c34b8513e4bf993bcd4dadc74391c0fa05e47d0e18fd2ce8d4e79beb377526d845e7c4632fd1555e447021ad4138c59517ef2a8689dc34079163c01bf2205c1dc6f15c5e9d6e314db332ec252625894b3709d2cf9b7b4367864b54ef44b2463dfed4b286496d82cb7dccaf38905a89379132557f6bd0706b84d4ca751cea6adbf5962970d65322a4f2c473ea65dac6e464e1e2ffe24e7b7a9e59e9ef6f2b3602b87b4a201a2fd8d61f08e017520aa3008b4fb2e5647eab8cb53317c3904ddb0522b4a97ab86b189e678fbfa3e15b9a05fe8634b5528f5cf529e4c5ba9637c95217fcf3f68e56415ec1723d4cb96f4aa0d4015319c2267a417ebaa7e815d2d699a1ec310c70947e3c123c358ce6d3256cd6ba8f34f62da6ff67e5cd111ea0142038bd2232dacaea817f2a7af66a07dfb85ca91eb945470fcf9c3fdc1e3163dd61cefcea3634c3622c4ae897e4ac36b0a16cb0f6fd7b37de0ac567149d277d65dc525f7b07339c509590fde99fb489fe5d20ebd114839c5760ce3956867adf90700a9a3654a8b72668e21cbf71b6b03f5f62dee43f877fbac927a45064d4c06fdf551204b38e0b64e468b03605e59633653b94e6f1103eaa1748e67386db1a50a0c9d27eb487339bfee8243c3e00a688eaa2bab66fe557540635fb3be3eba80ce9ce13c0e6509f66bd0a6385d1c810c6221f0e6a980be45229f9f9b90b3ae99034332b73c99607f171e8ed69191dd6299bf128cb6fc05ce49aecb7e0e73da83aae60a024d491b4bd32e3fb5fc588743a40f9e84c5b229cccf50d7b9546dd4c26194d6a6426518fb0259a9133c2965e56b2a0fd76076b60cf5e780b80228c4db5b83c2ae77d5840993a361eb41de02dd815f0f9f01c69ca85dd0ba054fb93ef67d56f43ddc70052d1b50cdb38b216b850c2e8adaa696fdf4938093416e40d6e2c8b9a30bcf00c72d8325a6fe8503cd40250026a55c6b90a0ca855d6ed97198b45fea374982e0232ef23a5f560c855ab1c87e0b0b3f41d7fd2be210990c97905f22ffe7d88e5729604da2c2e0cceaa58b067e4e98170192081eea355341941cc007ac885f992c690017b030219fe0ee653b9edafcf90c55aebe6823f3c13ccca04bb6af869e78f50ebf893f78667cd50dcba94d93c00d8dc56ede45f406908f82abae471e91a6e55d7e887573603785c611007c743fa995f92901f2167f046424fd02715e15859d993a23fb06be33b59287297052ffa0649ad3b5573ec8a1e3bc107f442e88055f493f9f08233bef448e3a31e71dfb64f8070a36d826f3273678d731c26415099de10fc613c9f3328b49263e332bec8a6849e7d9944dd4a17bb870ce6eb6525a7f70fea139a465050f32236842e82506cc8573a669076ac27d8727d3019a199713e69033c2ca2bd47c584b090ee463efa420c7cb2a64534d389816ee4be8fc2f069ed8a1d5af85bb128b9b7dac14c42d8ea3c22da4486366a88d5180285af55910d3dd40d095a95629c1a5afa15ad3928fe8db55cb3669975fe1f83d327a14371943e91ca35b3fde43ea6b8eff6d9335e3e582bd875a168212e2be5bdee74b5567b0640bd8bc7057041550250639b3f10dda7ba720d59455274e5e0d788b9845481ed3a5c5327067c988feb0b626bce89f4bc0c47b74eb10a08364b7be65e69c8c8330c4f471c167a0458bcc4345725812640115ef9c13b5f4a039b36f51da68b338255da43a7e100c8cf8c11e1724b5739918897e82a378c534ee9d633549f54c6d871e1977f98e9a329f45647068277fac21e5ae41b0de697946557108143e8373b408003e49ec0d1049b6690ca88883d6c4863fbde0c7c88762d5800b00484a1dbe566d7964884cb8ed0906935fefa897c38621490be2cc5009db028d9dd37e4f29b8107d16bdee514e88de670a23b15d20f35d786d25d420d04736eb7f285dee5b88803adb9081463d9ba9c1a7e52f42a051e29c5c7caed8471f9336ff911e4ca4eb341776498aab4e5b4d7a93be347e8e002a8a3e071a59f79058a2ac388c40d833954e15c3780dc5071d33d0ebcc2a70051db367a63a5a03be0303df446d6ee182df467b4625ba4cc1932818b5b1cf029f7b88a4c034f8e90959d204de867aac866033aa704c27c846b889d3357655f3c018c22c6ad3c05005f7887451c64da4e6c81184e690c9df59074ecf6ef7409e84ad064c53985e75cc836868458321d84fd69473a94abe37c7b6d94f75ac23f09cedfc2e8bedba3cd37b27da476646ba547da11e6e3a713f6cc19c4d280e7ca4aa82f62b37d55c0fe9576c5d7439a0cca6a235e7e113f65f3e472a431e9511d00419c5cee4e4ce7b4e44c475d0b85be7ca17a48a36080adadef9d090ccc2021546019bdfe232a1a748e54ff9191ec2fae0e3dc6751148a0dbcf4fcc7a128321897958bd3402d9d4116864137e02a34325f04326062552e0244ac69c0a1a0f97955830f3e7130a773e59801b51f062ac1db0bc24476025133070a00a8fc6e30c61ca725f85a6267c66d3d361c4d3c605080946fd178ede29c62b618d3b454e4886138916112a051f9cc92599c8fe4373c17447e1b1c29b10895c7ee4d4dffcc3ee0098e3aa0cef08866c22811b994d096c353349fcc980df74335563a822ff00098420a40f67add5de7480946a7afda320b56f2020dcc11228171e200a5171a2ee3b0461c96d2992a931c5173a2ce86f9038dd68a78715f395310a942f8d9040f281643d90207a6f92cc423ab596c0013f15112b21715cdb33dac1b2d3b1262359431f17179d326cc4a1319ea2cd848e54027b257a473060a76ce3a09665a05e0aae208cb9fb16c4949fb381cd8ca52b083ebecc3a87611e2ef75723d97cc06f9e4a33a9ca436e06520486367444552a0ae8ead41007dff37f1bf9032416fd1b101abbd5771720d34ee963a498618241de0c23e9db5ab38ac6c3de3c6ec2c9bdcedba3472b78cce0f0309fbe63b9477dd4064eb2e854bade4885d906efee473e152cf1b3d73b4ea5bd6d692aca9d6523923b502f4115df15a31832935e03217e493158f1f25594e1d8969bda0dc630926da5b4c1375093e7021c01b8d4cb7430b83c6cc54103c2f65db6b3ff5018c3d24444234634a8fdc74db6bd8bb0fe331505b480a01d1926834a8d5147c52d2621c535dcfdeb525b5d593da377976fc94f3caee650b9ae6a5667aa48abd5e91e03ff7af89e9f41ee2388df4c50ab7b3f51d368f99487ec89f9af479fd624ea986a78678005a3b41affec490ed424742d98b3786ed3f2b8a8abbcd420b58a85806da8b4548ba15623cde091355cf42fae38ca03c6cdd6e1dc23648e3772fc81e82da65ad3272c106d6a2c568d9c8a44c925ab6350b280171db8180f8308873a7e018d9b460b76327deb813edfcd0e18e3f7a59255e74a56c9f3a51358385889f1476762faae57df924a663b07d4b0c593b59bbdaf48dbe11616322d7e3247750c65e4c2d5bbb1073eb6140645ecfd4db3ed6e7ac6511090af3904ec3abd0210180af84f8b3361a9b84ffc6a6e9882dcb0e9cf7068f97086ddfeef52022552da0c640a1090ca946864f9821434387a3150582c7ed6f22b42365a57ac4e71926b306b521c8251d090d907d65f29ec525241babf43ed9426e6ca6eeed209d8ca7c5e33299720193308ed6c6352b092cfdc089619a1457f0f8a6f5750617c6ad9e7cd0385dba6ebf8e55888aefd911e0fc0c0c4350d63a19e8e1e459b442a6f1c0315b6729f5184bab2b7f5f064831b5413e45072d9391973a663073c61c58cd00e014f133ea9ea482aa45d90090012d99438053fbf6cdfe37ced73312fbe43256483b0dd9f46af1e39bee4397a59b27feeecf8cb27ccb5cc88eb4c409879110533a159f5ac9f7a439290df8294cae6564e59fb2f487102be04cecbd3f52ce2e6abe3d0b0692426e035d08e2022e90537d9e67a5dd0cddc7c04760ec2908cbb2db5c587f71538c2def77521dddb71f4dc8168ee6b8a1483e8c98fdedea3ffd80adafa14150aafa6182c118bde0be8938808e25ba9d360e50bb6a53513698bf58de3af16c4f8c98dc750d42277a060596c568cfa97bec61275d1d6b469c78a36a11bf007ab13ba1acdadcb907af0f1f0df96f914f6e7566254fc020bbea44a3ae178a5f18d93beba6c9fb2eaa73416a817bb8f706b4f15525ea1710ef404ed5798e001779aa1dc7b728af181552abf577c01bc60320a5940d80a04760ad4e31bbfe6f301e76332f4997cbaa9ecb358809b30b8480954d8c7818289269d4de4d0b2467c14bfd423e8c138d0bdb883e2f1d2dd2c9e8eaacaede62057529f49d9ee02c3e2451c8b8d7b2df454ee40dfec170864268e8de62e755691115d8f23309a5c6a00370ba98328ca5070e2fa532257ba20cf03368ce3999ddf2bd102609078481a02554b5fca1d454348fd05e282d3d21aefea64610272aa1bb47875c21b8f7edaca793435791e9e4cfeb506a02f641ac5d2eb083dc23339ade6cb5c657de9be9b04d92416751ccd04662be131c84ee6fa1e0a38c94766880151f0b53037608acba22e7bb2b2a8077c5e5b91c8263a481694d385b0cee5cc38701a71bd2e9ac9771ec0e7b4e661f67acb2ad7a9a2cb0ba7da9d8a5d5f6b75fcfa6206a4c482ff049b7a3a04709cc083a1be54c7610a1f508822f73afa6611e9afc78b576484fed6ae822959cb78f69422b98508954ab7c79b95824b1482172258e88969377268fd1b348b619a76db8e9fdf0fdd8e8d80045ba87d9d75f5c5a9f64391ebd5eaac69c8daa3b519d5f7f023fb26d329c7ee932307fdd47d527ff411ba29a1b08a29635da8055d86e74ff59784695bc28abef3661a4898834dbd7615c28a08d9150cf64312957f9fa91cd9049937c8e5196d372a1af0f31c7c0fee2eb42f20e289f99b274f485d67b450fec3ab60715f234bb8c4e88c4f6fa6a890a9df59c281148cab187fd13cd746dbde2e34eaa1b8221b4491e0911b2fafdaf0fbbe7469869bf2d7713cd66cd64cff5aabb39363f924a4f35323787ae977b95e5ad71d768915834911bb1fee75cf6f4acb4ed0414d756eeb646e56bca5a13823382e481357bc8eae2815ed85fbf3708bb11da9921abeb33f76f13384d73062555ff33354b9bbe2ac1a195ee83bfdd6e9da9578de214ca5c72bcd87268a413adbf31bb4abc909d838f36e30a131f4e6ad133ba8798672fcba0843935ba1dfab93a478da81461854c9d91ddd87bf21391196e603f8b04dedd75e8de045315e40de25ed81ed24b5df8980cb752bbc3d5c242d8ecd51048d20584e61998f5101eb746fd392cadddff1da0ed90ba3d59e8ab4de9abb48489ba60ccaa6b46e065833be418eb7f5936c0c31291901e469bdaad6ca6ac3020132afcf0439d45ce5a44bd9b5a41d2be81af88051ef584a2f293c0a3cd2641c52d1a4b6c97426a8482140fef9a106e59f44ae74b694092956ff40c509d708e095f872743e36281d0f33270aad9a994a1555725e34efbaed519bb8be302ef913f92377e6eb60a63c5bf77679292ddf6582567e51facbb8b96af850cede7f5dbd8ad6efcaa0295f2cffb26e7e1dac5e3942b18ba32ffb9f4bd556e3f8bf7e81cd315231dac98c2aa286dfba488828e1dc9beea9fe3c858d0faeff15516c808b1519250de18e1523f894463ef93793dc2388d8fc56d25703834758037fdf304cefecd07c43029cc46f2550bc0d8be3aeb45050b97b62ad3750cbfe4abd83e1c8a646011b65db92ff612096e819e33aaff68b769590686b5e6fb683c02cb507df1359ded7f8cdba091db2258c0a53d06607c85cb84d0a507c5eb2f3a56c7fa21032485fe37b06ab50c0d171c283aba1e35a7e577fddfcf4d2958329e8f8aa2d25dac7c37d5ce0ce75550018edc35717705e614e76e15d9ffdc751c4425da2290c84d775736a74b6a50b2f33cf63f6ae54d9ba27d9604d3e3ba784f0978f8df4f308a28e0c1e46c6c66446dad54808e1a6c65937580aa8ede59396a57d5e182b76150b87151378968a11dd1bdd135ecf85ae055ecb44f335439f6a050f594723ed36570b3e3c5a55eac21616e8e87c55d1a60142b1875710314f90fc6dc682b8e2d3ee908cdf302bab5df7fd17e4422ff0c7b5810118997a1c602b30e2364637c1d161e8044a93abd422b0d8011ce092f93018f95296490485f6bd5305810d082d077760b4d36125ed811927cdf46955475613ee5a235fc7a2326abca841a03d7732f4741a55b27948349b14b514c97c50484518c0cb21df7878d02fee7785c707c618f053c00708c539bd8a087fc87198f876260d03ea35c4c4400e0194474a979e39ef9a835cb18f4a659923b5aebb3f36815eccef8bf07eb5dabc0292109f2e8b870a812f8ee60b7f1277b1c495706f98abf9d06e970d08fc8cd0019571dac46bc0697742f688bf43c60b9d328b45b17e67ac5ad46efb28876de3cfb87e7db43ed6ea098391f9960b0136225e44a24992aa674d42c94443e69fcc78bdd4869e17e62571ccfba5146de0c64c7bd9bc9bebdaa0a4884ed4badd217e8a0bc2d9ed00abe7530ac025f166c027fe54eb26ea9380601ae10f9021e734d909afe8bd40a4828ae476d784df2408376a827a27b5f41fcc70eb9449184c31794219f4e532fe70ef5e8bb0c85edadd9942a20e19c906d0b4dc3706741a5eaa714069132aa4f35e02d78f729e7cf261d8b9246792bf2e0e0024c9c2e376f72a5958776e6702a98c6a5aeafe04928dd86635b7f4ba980a9de4fabad617517081e6cd6a2cb3c756baf921513863a3a881a2444627ca9dccef87a173d36bfed09a1135f02b3794422d933fc9fa5814e0df6777d8f20f3b799f42f3df20b9363291f907b8ab4912289dea57a6c04e6b72e40cdf328d357cec17f878eb0ae192117d589cda5b81f123fdee172eb359285d7e5aed07e1a8e54d802565955cf31f462276cc53fcb984cbaf39c8712b618ec73ac0b837ba4734bec072f00b642082a8553fb061683bbb5398abdbdaf19a96348e89f8aef7322df731728d54f502321c55410b6cd92c9ff15bbd0fd4b10b4c7db050bed9aa0654af012a5e65485c30d0514a3ad532f37f2e47450000583da200024082214aa8d26e4e0324b2d1ac75c32a68eedd333e057f5d8038be5058e139ae8eeb3c8fdb89e380cbbb8f0a34c4810b5e7982c3c919e14d4a29fbe0dfa2b60921e5b78490bdf7de321a045f047904f7d36bd500bf3a0cfc0ac7a078c08b63dbe5bf7e6d61e72fec9794ae8967ae6240f234c32227f3e49ee140e9152c5e320da611d46fd7688e9ed7869deab5a81ea79e97a5a161a611d8d7a88a3f273dd2248abd22a4a7a74d9b2dce16a7bb08e92e42341390682620c92aa5a5073c49c9f5d48a1ca0107a30fd9455ecb4d2926b1ecd5915397956cc81ba06ca295321aaadd5de58e189939b35609cdabcaa17253a3428e7665484cc6e6062c1a498514992c35e11ca3dbbc242de72cfae10d9ce51a4f96675175df2a7483d5d839f610c0aa6eaba09fb4e714c7a13df4b89b66f67e154df83e9bb6ee21814cc44d04d040149f97a987cad97e7c130d65998069638664489b69b78103681632ff0f612b153a2ed40b83b187ef742158e41b56af4bc3bace71d8e41f13ca68281ff704c0aa144dbc1cb4bacc2b110285187278e49274ab4bdc3725ece4f1c7392db558f79bf46abf7abd3cc8bd2e9e7fdda7cde2f79f105ed09fb0586fde24291dbfe13c6a060dd7d704cbe3a26d57b7e432628986a7bcf9958e400b19481126ddb5938a930530c8a02fdb2d7deaf1ecc64bfbdbeb05f5ed82fd5bcac72240be59e559121773d7b7e4b6cbf9a02fdd20e827cd9570f6d58494aa1736b6265a80e59b1563eaf56d741d05a0bbeb360e87d077dac8d13d876df771004c1bbfa0eb6badb107aeb3650fcc441cf7ad6eb700bc35070e00d45affbc0ef20f8893783206ead70ab86d05b87091df679a0f87d608795541e0882e03b1004411004c1d63d47914e248117c267e5ef47e8365a5808db38819f41100441a196d0bd6fdd7b2fd81da8f5d5bd0714c25a58e83f3e610d0f7cbf75e92581f8bb5dadc2993d10045bde57ffde0a4f5c284401deae42d8891a2770eb5f08438185c05fd0de568dd64f1cd6c227304c08831f7d714dcc1f31ef42f146ee6afd7eefc5a2fdeabbd174dddbb053f22eebadf7febebd0f3ed6745dec894b2cafde4024d71b7ce45aeb69ad9556fafdfebb7df7859afc9d6227dfaba6c4c9d5e7157cbdf53eb8ff3e58bdfbe7590fb7402c63853bdcafb7de7d56df3d6cafd52bbd09d857dc5abddefb0d5bab77bf1c27defcb55658464f97f79eaeee1df6b07823d76ff3c47bf1122b7bafd7bc155652fd2eb13ebcc4ca3fb8e2abaaf6b7565bebf7fbee86dfadb7abd5fbf76b6bfdeebd0e8bb65e5a7b3c8ae5d6534ac74c1973bddfb06725deece373aef3094556aee02bbe1ffefe8282ffc2d56da8f2fe62de13d7e7d68298b5c4f2eeeabeaff79efb1753be9b822577bf2b2c36e5d56df885e28dfcfde7e6ef2097bf83170ac17f5fafc22f9c1e56badf7dff3b7823e962ef1604c39957a1d883975879aabcf0da7bf60bbddf50bc21e3fe931e0c7b7089057e788995559eaa090a8d1b5c4bbd20e584953dd0522f48c16062ae5c9899c9629dcdc0cc665b6633391548d7b39344b8eedc79cebd37086f97cdd5662b824c2f6bbfa3754afa894db93b85ec66cf5309394ff6daa0dcb328292da30f28d88143102549c87881c1cb131f760c919e164aa8b51699a3c29c147cf8a105515e568c1721d5cb90a71ce480391c91c23f14058328186cd390382cd071410e282b300993b7281d798b1202e44c2692430aee304142ca8d54de7eede4cd4b113fb05008f8886a52a5c7d4ec04306c52982f32427152021263643a6c51415a3471ea0c91c38720468058f9e2250642ec192369a494d288141d80644489e6c501a418ca111000071e4815f36298c5d398aee95980124dd91c842055cc8f4073d398471f982eb1edd4ec0482791be10421480ee411a9c2c88308d42cbac02c1a515c3929f2860fcc2359a65df353529944537220a54815f313cd1da9439e1765903ca553cc8bf20431a408d2c89568b63927a55a501625920074117d0d3be8227a8e22bd8928529051a6ae17995e4ac9743af1e0c8c14c173a6da28e9c997599ea22666a6a6ad605ca8c0b175ba60b9620645ab9675c58c0c5890d3b8c1973c4aa2c50d792416f1f83e2a67ced9d372d14eb95ec459ecb53194adb79de8395e4edad2743debe477b4f2893c8da7b249ddc371226ebc6ba5e86ed59cfebbaf7e9904af57defcf2f6cd78ae90a7345a34c912e758d567c9f4af5beb452f743d7c57d3748817e3dedf0de7dc777d5a5d7aae1bd3bccc31d867d3889dc4d154da2f1e4bddbf1e1a6443b54f8e24b89fa5a13cfe47ae8e49dacbdbfdd6e9193b55beb2535599e933073ed34557b31aa27c6a60bf0e276cdfb407b8bf4deb583a18fbfc0db30167301e2580cfc087ab298efc9e2fb40bbed7987aacfb03bf7f16cef4231964512a6d89435b96df69db94f2f89b35bdbc6ad19999bf588f6d992c7ed1917205976cd75629defe49453ab732ad94f5a513032cac271465ab09ca0258c0f2d674ab038685a66a0054ec7c5d88cc6b47089170c451e2cac9bb7ed60c851221e8945a09903acace14d62126af66ea95cb105cfb6852c70dcedf21cb7711b9dd10194ec7d3bfd56abbb82ad7eef731c6b7af9ac66ab55a39e1e04d683dedace7d0bba7c07864c2b28d8ea4067ba4038e673f0b19f7f0f0a5bda83de2f1ab634dcaa714f0fc2ea411833059dbe5f4cb2171432f97c751a3205bd55e3e7f7367cbe0a0aa142266e4b62851c25e2790d79b8190e69722371afd680f72c69ba4df5e158d32b0625ff7d25c3a617688300b96759e6642d44b2d7aa417f6f23e8ab7314e9ae5a367cbe3aece717c7a0b8ffc07c5e3e3354df3c1a4fdfc12d0c615e9479ab41bfba8da053a4d5e5e5bdda633fbf18b67ac5319faf30b7c4a2de0c158d12ef0c3371a0bf981ce56ccf61f5abc3e8ef6d95eb318107fa173205fd63058550ddbf90e97b094ecab649d936b1b11889acbc6d6f9e737293de0734ab5458844ea20b61e08178368e156a4bac1906907a41cac47fb00f7651227b164eb75f2630d47c09ccc4f4739f0361a6efe045178da71d3fb8c5f9e0d6b6e3c3ad9eb7380c83dd5310b77a708dd5eb5b1b0661f5fa7bfa1608f7f4176b5f18d335afbd00b3c03cd232a08523c8acdd4cee647b494be4b51a52f9399287ae67d743484f3d595049bb2a31372dbd3db598a32fa8bc06c4a5956a162fa9329d2fe8824c504ae98a2217540b97abc888530195b84874403fb7df5ae773bf0e5fe27339ba0b74ab218ebb2b582c10d6cdc6df5dd1b526e5ce4f9c7b362526f7065d37b7aa801b2ad3d5b3292f59662dcad56470c5b79847a2ca681e892d1c308f241df9a189245f2bb29424a05e12a51c5c91cbb5d65a25a5de6c61c99d2b9517d8346f8694dbf66dce229da7ba4f50d51e773d64514c226b9358d365574c977dd174d9bbae415dd1e5b2b7ddeb8a2e17daed8b8c387b953d50d5704f8f8ee73e414dfa27a8966f56c7ddae9b97abdd27285abf00344972cfce30c908c83d9b2a2277a00bd7872b5ee225f4f392627b5f4ca943ed6e2a5588570325f74c0d938c807c5b4c5710d7034abb978b7a7aa0591a3b999eced46cc974898fbf2aa672ce183a1832cfdb5b07436679e95f50253a8ebbb7fb3e41f1f88b355950a026779e9b427345029a445a0aae6abae6e5e4d4df7934e7edb685dd2ed15e9bf6b3ede9cc13c7d0a45cf383932c1ae55944631ecd0b4860de480e963cbf62ce1eb2d860f2ec54da2b6651226e68bae6f3a533acb4b8de16cda38a6f9a2b7fd453d239bfee1494dedea6d9529b72e7160c304a0051775fd2194d36f42c5725fb69b7a730709db9c9ca99a58a5a4b206ecb8b20cba95569ad2ca9605852694aac94714bc66e16738d74c52dec25d7528d07e3d69eeb5f504acf256dd20660d3704bc63cf680cdd5c889665597956b9db756956dd3b648db924a6372af5c79f152ab69784995ebe50bb5258303aaac74e910f3ce23ed12690450b35219f51a164facb073279d732ac92b1da7c5e02a5129cdd489cda6867cb967533cb2fc6d3ca717a3bd9e4d25994d3de94ed1d307ed08a575d36ca55372e0f6cc4c9b5c6b0d5d4f0955cd4c9989d363cb925551519a547043c3161de312a91cdab0c033abb6564b274b9112ac99fac10c10362a7c3340d990c4cc103327ac596266878f32449409224a7dadf5b59e709bf3922c9612f77469d1768cd05cdef0f8c10447a4941c5113860004b334427aecf084c6870f9088dcf590bb6a3a341d649af49715d891f5406448112767ca4cf1290366c8900c5f782c0903a58434536562d8cad400848332dbc1ce6c071334993453264e0fed6995258365a484808cd39814eeb41419213a8c20a30219148e28a2082644fc880194212e30e24299313a469e21c4b40973041133f8a092050a1b3b4d80903b88a943270c99303a8479626b28ba440b6497945cf8b07a49769e864bae3ca515daf2e6499eaf4b2ea12d6f66e0e6aca1ce1043586b2d105ba5b5d6fe30917b261525558309d0481559018d940940d0ac72cfc0cc91c2319dee1cfa854dee4f1c77becb3dfb3226f76512b7e69e7d0193fbdd803877daf005060be7aa72cfbeb060df7c91593777cb3dfbf2c37ec1e1824e4e39299247bbe5e46e93bb7bc5f41ac5d0069a685e883cbb081c79be872bc125332ad242a220cf4b7c63cd95f7a06e378558012fb44de465bb61b10399069786ae8ae625cc1a20ea1841040f241d0f131e2356361c385831f2a3ce953063b8cc2714cd0d3c26f8b06809b3810a0788913077b63cdcd48279ffeefd7b0f65564fd78dd1d9d5295c9d429ea34494a8674331d483c5a17abab1e691b8a4129754d2a962250dc8729346753de73d01c2155d4379bae014899c9c31e4295f513a99bb763deb8e9ce79cc0ec4d1d4979a65c794a69e87a7e060749cfe0c8a02487b1860102a5862751a48e308101098ab41d1ac62a6390c584acd1ac91693d8e4822e4383b40614a9a1ad89861f23497e81052447b83429015888c2a645bc6a6c1b8b59c27697a1053068e0a9458f951846b400b4a2823c40c737880325d70e3a508179f04f0303e4066c78c2b999d517bd34210233b8c6846997ce1c7d874cc8c0e9045913f7499156c9093024d268170e91ce4938ee2314e19bd5c2665304e194d30aa5818652518657208a0b941b3429f51520339839e49138c18629c1a8f64478c358c1b1c4026934f498c0b90a501e334ca6418eb141939902d2933ca1e258e1865dd430b9944a18190748a185b8751460227a391cc881690482a50903833fe304a1c58d66b84d0ec4ce226851edb4c41dedc3cc99ec8240a904959bf09e382b1875187232e64b2ca281bd271646d240ef28bac4da7d122b784e9199c30b21965438c6e464ef62588b103991063016431b494dc21c9b4606c1efa4a1799182cd285a632031c15481f1d4410306393b16b40345eb98214b24ee24e0a5de704b288f18651fa30caac8c329e2e467e54e141899311b385376a8c344046664432800c0b0ac639308c7072b46c85ac719077a48cedc38ccdc882110c80ec05515a46993432350a692f5d462ba34bf666446244800c8851eed059240c54181920333316c99888195b2764e19812ba8ad4dc0831ca5a8d948c3346b580053cb8d0650ac90a1305b3372b3032676421837223d53411a30ead069a96c49d3b94522a4fa95419dd015a40912d708042705745f0e49eb199937bc6264df674905f93468d1d6d0629ac82d47fe867038427485d33c75b834410adce87d84c6143038f498446c748a36324d3c4c93d431327afc83d43f3248ba0e2be983dd41d7408503366cb1d2832929b6a9b5e52925b3539570e75d4daea66b6fc517dd419334bdc9d62f366ccd36b1e8cf91773ce1918c039cc4af5d3fedacf6b5e52a594ca4aa0fda0fd2045a049dcc9d249768a29372cdb654550731d413b095c1e81849bed65abb035363babaa1ed054d49351b326e9b76d58046ae711b5a14b71bb76f333d4a26e5173d01de04aa0484a29ef80e9926f315d73ae072d281227258b355dd0cba3b89406b82fe68b3975744477835a6badf545ae6b50b785abbed6ee755dd3857d7d9151bdea40f3c862570c9965cdd1c190b95f00cce32f6eb2d05e9b5c7a133a02dc183f1d0c9907707bebeeeeb6aff573c9c6620d5dcca2445daebd0f9d006ed70ef774313f7178a56440f7dbb5d35a534d9776edcb0dc0b517394d0200a094dee95a51e482e18aae16f3a8bbaa36a04107436614abae75bb5ad574598ab516aef8dbbbe06eed0542c1e113f7096af3b25df651fded6e4053dca297d9664be7acd20949d67d82923cfeeac9a203811a2c5aa87272c57b8158281e042bcba43bcfc5e8fc0fe6aa55ef656e1eb5ea4790ad8a4590995ee6257df9da6ae6ccf2bcc074cdc3028e80a8d4eb570c7b523ae668040000005315002028140a07c562c1481829a3f80114800b7f883a563e2a1305e280300c08034118c3200083000000010080300882501008a1591ee2edf6dbefb4632431d2638457e0950f90e50fec5f3819971108be0694fb6ceacfa0deac733f030dd040acdf8de7c501c297974a675e52dbfab1762036ebf7e827fefc38fcc96afd7b4a85d254231da45b38712592239d16bdc0b443ff6ab70e4206ae8619aeccf46a64bfbd2d2f064c7a4de210a918dc964afa1ba5efcb55b2adb69154569056991759e5dbd4a656a81a986d9c93a788b2b7bdedc63c9f5ef8b21800baafa6a6f250a55a63b03aeaf0a24728e38cc8b162cd68b2f7e8f11ca84a44d021c43c346116bdcc4c067caa684048115b99c938071c6598e78904b10fc7146638d86eeabb45951d151385531182fce71929ca662aa680f74b105872eac4cbb44414299b4cc72ed9914d4bdcb86e40e98dee59934b466cc8a3338e280aef86583e6c88a6f01575e73e4440f4023b7d43fa98d1c7ded49d206bc780e22384a1f8d1989c489c3d39b75e76f25b1209737ca2599ced168126417b17fde707e1538cea3ea118036d6fbb9e3415184935a3d16126757e492deeabb9a7bb75b90bfaf126159675aa5cf90d1a9bee9afe04dea81b4fc78c5f8a3db935d964c330f2b7c97663ebf6bbfabc022bd6c584b87912e1d2a3bb82b8dd2ab094d736f72d39d159dea273d37eb14ee8154b8d3811dfc15fadc48515ea84c9b7d6160f30eb96f360e47c609550a51c36d8ec8ec1c01995a46eb0d758ebfe2bf07592f4050fbe6ec0a68306fbe74263ae99cd33de641742d5f3371904e8a1ad95cf1551b191415001c185bb5552a402602443bb6a1ed34ebdba9f2532fa261e6eb407ca16a3320fd5580e9fa7bfcf59fd5d24e9281ddf75cf1909638a975ac7b38c64b555414c73c229f7706d16873da1ba0b618fc50642e5ab548aaac919d3394891098f93246c6d4fa15bf17e0e0044c8e7a3e5e61ebc1f52fcf42ccccd03c6c3b4596c905f8a6c0f73108c7992d680504d9a3aa702c8637cb300f730e22e823c8a382d021226fe0e1a3dd3ac76fa5110a93bb3ca3d4ab703efe256f2a73b5ea0e229abb10ab1a2d623b3010cc2a7be67bb66e1eaf9ba071f3e3da041c0816020427d2e178c0715ac3d4acd30c4a287ee0f233db809e2a2c6632ee733e70173403ca8f8f6ac6bb1aaeb9b8369e8ca87dd23dba9d8e079b4fc43f42cd35b642fca4e55011a7a15aa1e3ce8913d53385684d0c74d957ab8e1f7170fb9b33e94f693be63113c0f2c68ba5e2ce7b3631021224fd43619844508af0fe33d38f5b081a01fc445ba27b26ef6601e4c358b404fa4ee02d803a983229007691b7cb0c0ce370e1c482ef2fb81c7ea36218414161ee3368bdbe3c31920fa3e40722a44f1666316328e8abbc1032eb702ce03778e84f0f9f658dd2cb2792e274d888b569ef566e8e197102a1e1e98ba9eabc7300df00729403c0f1a1f0fe63cb406e2e2c753b2b5c2437bb3621dbdee478fcb191e84591d8fe9cd02db63b9cd10ae108b3e78214610740ab182eeb18bf329f3c4dd24f1a082e3a1dbb4205ef981c1b40166d44a25bd568c7945fd6af6ce4941f001077f02eac32d8db749a3ab864cab0bf6aebee7d8600cc21abe0f3ea37a78dbd21e341089405cacf604eaa400f44c1dc007ed1af0a062c7a3bf81c5fb0474a5d3be3ce8a1b8a11f68f56601f7c8840df1fcb8e31ff80f7511817b03fef351c2d9da8371353f78218cfe7082b0f1a13e1017340f649c8a98c752c74c0179823a2904780c75650578366916829ea43b20449e753de69b05cbc31ecee7e181b76631f3c4b8481ede1f12f5b038889f2d0f480dff414220ca02abb778fd70f82b64ffe2503e26e2963d54370441f903570f628866105b88be0f753dc843581be202e2b1db9585d163daf83f98206a1ff87c20fd41855c4f6b530bf18118f1c00c62f310de8815681ee38e72f11e94c6405ec5ad7bbb20ba170fc9b1d27f74b362b9f5354f8f5b337c4172b57c0d20b131d0867fe4f3637ab320f370ed6a63e0b1340a3c58507878ee6d102b7e3c65772a44e3693d98130fb17f0837a82c24eeef27c5f303cd67e62174c37b90108829583daa8d1084951079727a70ea18573d071ae00f16201ea7e30f41138459aed1c808ca8e7f193f2f1ee71753e159b2d5023105bd67e6c0fa403d44283a1e4e3a15550f0b348bcca342ee42e6d9c0a988f7b0b059e43c2a701772cf906dce43680f748170a7650fb866c1f2b097f3797ae0ad59cc3d31d7481078763df4b6ed871442b1c784cbab2aee97db0d0fc10181c803ce43b3da565a9667e348bac001fd042385e5b1dcf8ce552e981e375a3c84e70f2978cb2de2c385215685cbd1f2b613a2dd22b2792ed87452c83d0cd32c528fa04384e8f480b80762fe8253145d7ae0d03b12b028e7b1634df60b60023b43a07ad97bfeb985ee56e6dada45630f3bfadb1443b81202cc98e0c0125b5ee2c17c40911f31fa8f70a2c4e7614a8f32575b4a66cfe99a8eec32ce74002557ca3a3210faf6bf365beb225250cf601e0e972b780b41962be7e0d21baea8575dacbd7ad494a21cc60c8e9953e3fd75220d9503a3e7945944e780a92f6ae417f1b37bfd0c1f1955221184e8c77f88749a708f5a8aeccae89d95b9c2eb8e6744f81a1d9a8e593271185d2e42c8789de397102a3538c6f33e16b41cbe84448160e240fbbe8d036bf4dc717c566030590c214c10fcee5d914d1170012a9d51f3b411e2ae1e7ed6a729ee390dca342ba4a9aaa7a14b01bf44360f3d9c8050d6da5f320ce4d189d38db0b09e2349424ebb11f0014670bfa6e8d47eb1a74c2bceb2acb8adff1e70f75d96ab1fa1e8ea08edd7b0dd95a88e03444af1b35aa58c4406026b083affa05fa7c8fc9a9da02bb8ce0c702e3fb4ee15a23ce8964e6a7f5195bd53763c8b48100f335f72e9ff09a08c3170134ece422d212a9498b410918f46e155e9c8912c8a68f24e2d1cd188991caa69f3df9e9f7229162a862f7e3bd65c21fa47d7d7b21a6e735b08e414111753275098fd5f89494e20f02163b0ab81e2a1131bd44a2d3dc637537e35e4a2e3ffc05a2428dd32402eede9822607c8476c5cdc7bff23a05b03fe24d7c99cee8eafed0c1933dd8f560c78669c2c692a31622c3cd4f9548c5cd5565916396f13c14d08dcddeb5d31600a1541c2b1f17c8936048c69640171151a0a28fc7784cad2f04d7255e794fd767a0f90e3ade1b4428dcf5928d4376aada0d0a153a481125a7eca2b2f22ca1607829a008ba378e129dac44d10be352c165aed0ff17e528a6071489f4b5910dfa2429580809188cf21ae9609997de2526be78f54b9431f6ece33538a58c7f581d332f69262513d5d3fa21a5bb71224b1e3dcab0545d9c94713013a71c7ac8028443a27178f83b3005ec4393cb8581dad901f9b57c70d03d27f7dfc1576c7d5cd3238f420bec1d81cfe41f64242594924439ed2f0ba5bc0e41ecae1d99aea454f0801da82477b1420ea9dbb9238b820e50a048e00ad9fbc29e26b5a3cfa8d8ec871e84260446db2b999cc3219fcfc86f0b0c0e93b9eaafefe75fb8d40fd7c990f09762df1a2be369be08bc0c90710304f87ec367b35127c16ea4094b78e10e54f8d7cf1eea287e3e64fd90a68e1a91af66c681acec05f0186cb325821552f19beb5652014ee277c539ab592f1642c17fbf02054585db2804fbd262a5483c5a9d2276ef4fe1990ff785dc4712a13821ea9d6419d72dc576279f3c6aa16dae5b3cd211c5146c4f1161031408c9c2bacba5a49bd2065106e837277a06623e866e17028d81b23b0585c7cbf0728c4226462e23fb4166dc421f3f4734b735ea00467adf44dfabe191cd0447a5754421e510d221203ea6b0e79d5f39a8ae1d851407c47fa09065cf59c7d446ed8fa49559f216ef2e600a3661a0e3e330c3e086a5f598056b7481b9d310f530324199d8707e19436616b00893021b50cdc80694103717e50a731d62602cfa9dc26bd016113f8c03c33555f90d322b3d8ce881ff46bfeeadcdeabce3c4020268ced7c0933816729dd66dc232497f4cb8ed0069bf7fcc9f9be7fcc8e069f6ee2c207a2ea0b4b79229af5aaa4b42e5015ee9024e2df142c04144e407dd0a73ca68c80966410bd94d77798338834d45045902fe282e80cec78b376cc2d1672430f7f370245491a171ba12ecb83adb1f4328cc3e6477ebb00d04c848ad105be62adcb5beb1a23846a828d6b2e6c927e4d7274bf88409643d801ad0c674b1e75b39449a361b1cb200221f3d82d74206764f421d05b35f13ad3daea5210f150d0bd893f864b85a4fa3667d830cc56c3056132ef55c2cc2a2ec8ef6109792208a17f928d0f8eef96132b7281a8add9ae1e97885a5682e7128373b86ce584489808e738f357b3b0780a917ba89af76e19e8bc0f1d35b52bcabbd963166dedad2a520afb7ab8cc32e88a4e156bd4e886354d3463ad9db35c23edb3aed8d70eea6c94a20dfdfa455d2f34219ee49ebee88658f9206137cd5d355b45ada842924868085011a0c9ac95d5e00ea3e33d8a6d42a0b5aca5bee8c43c0d9cb9bf407248c98b20c5235464e3910d75a48486c8a5909c416d20a77a7159af20be7569de6819abf6e9f81abf4cf4b94198398b5fcc5201492cb2c4e5b7beda7d6b70a9f920d3503d05763106c1a98f4af801d976993cf74d6a220f76224215884d083a592f2e9095ac330a2511b40d25c400babd1f040771b7101f9961926e20d0932a2ef86818ad4d408a8b040f368c02550b0beec27f1b7d140c6a64e30ab9cbd896a7bdfda80dfea4c81c96aa1b1fcbb130c9644ab40d51009a87153683e08abb9049a6bd7f055d4cd4b1c003539664ac68ca1faa396afbb6dbc80ae4c921826a167b94d7b99420e9e3e4ea7d3d5682bb2e5daa2b6188522292ee40e5d94f938493426c3eabe2531e3d26dd17c234363016988e703746a96120b9d8bbcbab4652e4afcda56e7e15b4588f2029ed83b44a24d2703d02206456bf859563044044f1e78190762d681bd3717fb4b5b86a512afaf1bef1ac607f8c9b7b01088fe8fe91ab083da6c5d9c7a716b9ae31c831a055313f35bdbd61df1e327bdb1d6a69ab3be7914ab4faa46f8ad563a4d99030de430f6f429258244ed3cdeae15157577621b586163110eb02b5574ff3a728e9719527d9bb92bf70a9b6aac6f793f201d263166a99682b9ce312559360abb49272f8c0fc17fd0ce063b2fc5c07dd2448903b7dacf784069785ea4673ed6c806b2d0308b8130892943748fc3241a620acea31d05c05f1ca77f05b05c6dded530aec54a12856393b14e8b36206947772ea412ed2fb40ff06ec7a3b3e66c36868fc5d0d47da604b61e0abbb4134680a989042b3825aed0ab179243a1e02e500fa7b1230730a81a5dca50c7e1f43a0c839b3f0312f462c741ccf8dd6b6aa1871619948a2efd3bf2170574af0a5a1023bbfcf128712a8cc2446745ac76d0a51a9cebb4644c6ff6fdabfb8e8674c8553d20ed48cf906f17190d14a0cc9011dea0414a1d5d97cc7b829f441694c9881de47969bc8cd55f11c9530cc127bef75d1c1c4f0c212b25d8b0a2d25ff9ef1a8bc99b02258001d309e96f609547aae9eb9aea7d66c9fed926a1db1f54810267901fd8d0842cf65500a4c308038ff42afc6e61e368eb44cf9785474c77aae1dceb08b1d9704c5224effb85568a41a24602e637bd4300d4b0dc38bb4f2ab7b82671e03e297a015c9e3e8a99f6c15595003bab193a835651b28a479a3bfedc6fe76728322274f11f1fba2a44883302af587a0fed3cf67a039ef998fcba4192a92c1427eccaa4223d670ecac4f4d8138e3aad52a16b1ec526621303b224e0fc81c14bb5b6dd0033f9f8844f7607c1ec6d654354ef604d52040bd1e66dca302231e1a4a8ba2f8a3f8706d653e3336cae37bb15f7d7e1bc3269f392ac09f859d432c4f7ca0dbfba0cb1cc7583f74089d021ae93812e01135cb58daf6c4f7e165868d4e285b2a265175051878b0c4ba168d8dcc19b8986ba346a4dc566211e832fc2892b227faf955e46fc1711faf70fcf9c2345e7ca22df0fa7a95b5b314f07ecd618d3f4bc38e195d2257c0b7c00af2ea78e6a4f99e532c11f9bce3c6ebce68ec60b99e72c70efbe087d97135ecf5a309a915e5ee12908b7182f8cddc92c48358025f8a80ddd27587a23b8e85eda6c09f3b548fa1ab35029842252e6482c10950beca836591af7739a217e137d7d3dbef6c18cd41197a261132f0650c48046ffdf18531797fd82a8444f899f04de2dcb95aed9c1f72bda9c4ba7f93c9fe8a4f52464c75fad5d029fa1b955793bf45a0a282f812726804e7529f2cea109afe5c4dd71efa6d9a4e6c75d7d3a597125f79dec530b0dfa53617f6d480e6b49f6df051d89a8a026d3e8c1b31e6367c87ee8ded494cdc56bddfb9f493b47f1bd472f8ad9a297a891bb8e54390ada64768845c17eb1f8918480a5f6cbe23dfa63c8faa491fec967cb708a8bb5600601c362563ff5169ceefb9a141ff5cf6b758504054fb0d599f80608d8b572f8f366950d3f954574b2607e0c8705cc53a5f2d684c3cec6ad0fd91075f745cbdd1e4d039fdb37ced06d8628c9fe820d3a5db08b6d904b4008f091d7e21c0305455edbbf45713f15ac93d5885a6989b55d105a9955aea4cea5bdfb58fbdba99574344b5e4bd708313b369753c84f4f7b795d9fb740a83dfb63aa56968b38fc54617cb74cd97129b11541635c07d6aaa2b40c9316e31449cdb86946dcfc64d0246d156c6e2cbe333d7920c3d0bd028f53821b4a04cb8a999484ca0081ee070123174b09ab04cb24543e1debd7ba1c1036de4972fd1a3a80351e56bf0d07c157530f00195c1555e8f71d988a54831e223f988d6c99da0fafe3511ed4847860dbeee1ce88640d5292187bff05fb592152c864bce893c454296e8466c575a85a725b87a5f1c7539f598a8eacf3c9693d28955ffcf50611873bbd24c7fe35979c86900dfd71329f167505efc38e8e0ef818412721583d59782301f36362029656d68534d4b1961dd25610a3c8338dee31b163b65b6fbc0b435f1dbed0289422cd1b8f06409df03d9ecc19150a224f8f90ba32536c073dd2bfc0c60897d9a2c020f0d4b003329d14106868ec6156cd820329a4b7d12ea45d49bc86e70acfd475c51ca8803913efd08f771dfd2c5b2ee10c725511d2fd25336651a53040efed31172ebd2a067a92ddfc9641486f91de77e3a90f6ff202e975c42e4ddccbc9763e1411d980a311dfe2e1b9114608f006a0ccbcf949584211b6666c19838e0c8f645c7882b2f050b9623f9fd8e37f5dbbe9a7edab58cbdf615d6aaeaa212837ea171a95189fa3ee9d69a5cb6c02441b18968204f30f28a9fd75b51a934600938977cbc4b583885641e54d891e7484863772246d94febe22589ebc204ae10b452f68d4534518288ded5f4edae8ab54b65878bc408d021c89e65cb3729c151b432fec9338c59bd129c359b4cd9adc9c3901d70dbfe8d578333d2fbdb0d20eefc1075c3503f75eb5b52cf22bce4a39587fea0c9bb52e8853ee1de60d6615593d6275adf804834ca00cf57b7c0eb6d32e1a39c659606cf45c5ab7c547eaefc9c0b826e3700a614bb393f47141d102238d9feb071ebf6ef9809bbd48d3d6f9635a30ea06d8a8bd399785f212bddc1fc52bda3afdc11c50ababf90a6171ccd6e202b334181ef1260b0f22499e03580078f63c388b45c20564d1f2fde7c5ae30c1fb2692c63517cb23420bb4c145b7cbd89eb175f30910be31343a2bc6d3e04e37c890483c77a07b43556b0b3a4c5f7ce607ae431fbc123e7031f0e54f53bb580f1e3c0340735af1547e0ba73254396ed03d9ebe92bc68ec300b417ccf32dea983bb313d16be65e34205032b9e7d4da6e580bff49d063658a0205856159e52297c4d6cc3d41d3f5e610bd32a9661c6996a315b6edaf48e4a304a621fb718c25af9ca321287d353a1b74c5ea167bec2cbbd0e82b458fdf6dc52ebd755abc4bbae65578206cd807c5a7c9456889cb9df7750040d6ed8319fe81cab9ab5ba5d3b18c94c4dbf45881980bdd9b36146a961333f444ff16def30073ebbe2422397b5859c63508fb77cb395e75d980848c489ae1d64b6e96ca722802591a121ac153026a2c2b40e7b73a845b9faed3a99e09a787145f78f9c347a54a5799fb16966980179c6d036b88dd06a110747610938df9cb15718d97f2ad6f5245019f2aea649c7d1d5c7681fdaff09077198c4304d4a24a58feb62d89cf6d4f79145ccca206ea76e72ec1aa63420eedfb9950c1a3e729420ed42bcc3e01b1b856f46a3a16f28e0a73e82ca8ef62283ff4f6207d6b830264f70081915381cb63c784e794a825823e0e94260b194588e304377b6def672e6d74b1836e5a81adb206572279b8749ff0598ba9f69f84064669a7c569fe340bf91def17a3257817cdbe1042ab9424471d01b75a207ab39e18f31c2d17ac4b840a113875397ec1d2b7798e0e52428f21543bf8ac29d78aeb251dd89002d5c66d868e2038eef9d41d03629d6be33812e2d0c651836b2226a9c0e7ec1fa211764cc30540f88850277c5511e3921d9bc556477e95c58bb76ab1b483db38f0dd4ee4b11defdec7fe538962ba95113c05d09541987df907d1869c16f8d3a1c6057674f174c3ddc7c7378a8864b9c5ecfb99ec8aadae183c9f69ee129672bf9124a6f2a7fd80be1f9910490af5ac05d197e706f3861a5ceff1025d573e28d87c1999fb265b963b348e70ef247e1953cbf045506c8c7408f7550943358e1b1b78907ad428e17229f23b89b3603f3ac2c86d0c74b02df6c18873655bc2f95b9012287ca08161c601e7584f773b74b4631c9bed3636b655f82fff20e4de1d9569a9730c87b3fbd23ff18b87c41a6dccc4141fd496a1a8679d4b247e0d1b4e6b9526eb20a503713465f96d29f2eb9f6ed7ce046c05edb27be3882bca1c9828aa5c02bdff02780801c4ee39d4d698e18f6aa09f5f327602a17ed5ed06812475542efa2b29e2786063d7193f37ffcd39d05d13d4a1332fe1878f7a7f334784b9e70afdd6b98806225d2a7e5eb08ffe3d761672bf6d7d86fd5b89cf652b7cc1c5c67e4a332b6da9369e6d68ea526b5d6756593050bc9f1c10777dbe79dc048a9efb0c5f8c607e014842a9d3dd067c0068c651d594a3b6211f49b250a265b9492f1e34732d9d87580e7bb20417b517c86a075213a312d4a71029b255f72447fb6b1866a19bc278ad525f0991ac7034cbc5d86690cc3d931cf370f093a187409799d85cb06fa7b03ad6d71c6a7324615d9fd134783544c624f2045dfd19a4d287907c08dd72fe958fa50f3ce37e6eec3375ae0ee5e985ef15268808857a2b6e991732ef98f6b9b3cdb7a3e57f8aa07ae71c6ffdb6f6297279506bc71e4c2434d1ccc5dbaa096fc96d780efcaacd2c9970a99b1cebb6d07a8d19e776c32589f0f390b20fa249320b310a42b37fda82f26aed5e8c852ce3c6812f7e3031efb9a7f3b55e6487096caa1001e091d033862c5f59949471fb7863128d266728eadeb00d21ab2943ed65d0924fe9bed4aac9276039a5b4fa2d331c51c943460940cfd8887368dd36c4106748101ec2771ac47d6ddf0fd42c09a6212904c3a4f3bdd24c8c5369a340e8230c6dff21c9b7bb8a3ae3781ecda7fdb150c4dd60fc2fc8a7fdb162b2fd654daae46fe9a37364aeabcf064a9cbd0c2207a2aa3936b5a83ea31d17c0d58b5d4fdf2c90949d37febe66c6b5dd14356a6fc8325e4418ee6be17c3c54842e8ae3737247bf35debfb03da16d748303b9484faca395a6ba11fdb47d4445eea2e6a0346a1dcf761c4edb0eecd4b6b0a982bb12ed4e592906312f1f7aa211561a8e0189b5d8866954ceb31db74c391162fb8e7ab4e65256e2f503591d70d423e37fcb6a4ff5fd15f532c156d72c0a1ee11ddb20f2382a762e8a9ed3932456de9df35cbd1f953ee3fb0fbe010165158661751d29e4d653b3110399a8ca03eaafa441030ae21f875fe2c731a747be546e6a059c7c87f64563afc3f98c897098ced67e9cad036e167c7f41aade1469a15d3cc047454e1d31f12e6e275c113a2ad8c2fed9505839c7da1781dca4d6d180eec1c9c3be3740d462a4b87ced13a61250750e2eba8addde2e64cc73e343777812f04902f886c373169f51149154e50863c9ca634746723fc8c7037b87aed78e669af242ea569c6a0bdc00d43bfc208f6b2d5bee0f2f45699107768aec7d22e4f4094407f745b93e97781fee3f1067487f1fbd783f18b60a721f08fe4097be6cce2de41c3a9145c1372eaf55480f2462f5803ab34d2cd1f5ecacad36dbf9af94cc75ee103abaf9c4e197e5e67169feb2a35fe8dfff97eedc88947419c83d70a8b8c6661481467413a7cb195e5af69f77fa6d27fc53d95f530ad5578e2a9c2deb32e87943d7b4c95ecd01afb7ad0afd73532f9965053d2a77df0aa3435120634336d51dca6c8d2735a413ec6886935a38ad1d2ee097a6bc6e0e333def699ba3b90baef2c9ad71ac92b93c1047d0ffabc7a55f856bca93f3ebabbbd21107b7149d48114a572456a2c55d68d5995ad55b2307720443712ebb502425d41bfddd742b359210d5c73eca955be3ff31f01ce8f54c456d8483b360510f5fe323f393becad2b39bce57a59160483347d2a4d6bc93b5ff018cabd2cc1322f4ef7bd2fc07f33f171663dd661304e1e4243a8f7a0e0b24b14390f4ea2c077685c6b9f954d45fd26ac4b0269276bfed04cbb86ca7ad290ebc97e208cbb4bcb3978cb97f7bbe74d1a615250a8b065d8bcd4aacde4e2b96e7a1dc3e3afbc6457894dad26c08076aa914f0ba01a6f61069a78a35a3f22c5e0a626211dab76019d512a47b5a06a17745de35e0298aaa520a2152bdf54175df99f1a97ce70ceb313ac54d6286169dbc7901127c456b5df91641c21a390650e19a5e4a9c49e78d07c8fd79ef0a3e6266060e498c7796016ccc1e6891c103b44cf6649fd1d0a8d80377e3751a55274d22c9d4590f708ca9938b015c0756b19fc7b55aaafe47f2f8c3fce9b591956202dd722dca77ff29fac3152cd857484fa2601ab51db913e397d529c215bc02f38808019ec3168d4115b7f049b9cc042b85687965e7121e6bc87184cbab6a41b73f227736698c6fbfd7b2a0e556820b3af3938c38a3fb0ae59515309b4c91b966712e540a43e68aaadf3c17028b752c2fa59a1eef5d5a09516dce5e8fc8923fc9398f7b7cf96443920295b633bf6b5584fa88ebf98ec4bd066ff1e220ccac8f56338c1c7dce599c2c7a14d6d52bc342d17ecd01d2a3af9736f4fa7d96444f038bd638e230f421c8662df253c827c92e1532e413d3ea1913e153f2e476dc3ce09e52abc3f2d47a5387ac5b774a882f87d784d822d5c74d212ed62eee95d077d14c3f8134b41698a9b34851ee75d2bd2560fd09d6213f58e40e7ef9a3dd6cd004c863b3c88e366305f6943482c4bf7b1608410e535c9a36aba1ac7debcf626c107302c4b99bf112650e9767603052d721a619a3c159305c0f1285b7885dafef140cb1ce773e21d115bfeb09866cd361d76ffe86addc517cb5a09d39652b0e8095f1e2db6d4db3fc19a5274334de7679198e3f74032e81fafbd2cf4cc8c2c8743634e4128260a8598773b0a87bf3032c4911e259464cb27e363355a4b720081925c38faed0515dcad10e6803c6f0fb2e1c7e12d6ccdac261b4f61b6ca0447f018aca8145809e51ca84be86a3729c509a1aacc61306f6107e3e8b037b8ca2751e4ac2b3535d150ad093ce4e3b4597a3138e2a3a677cf67f9be48de44cabc8a73295851a22d8b1db987b943e14c3d3c065c7097f9458fb3d2e19357bd0b3c6f5b888bc9774d8ba2fe3510e1725c70925ad1d70cf8544a9c6bfe2240a1ca24a5a2507672d31b6633d473bc20b380600fd355065d7a2847483e5c719ecd76e4592812745517808147dd9c2a87ab1a9108255d1c2e58a34e10b0fd85cc0e43a88c088d95a10f52a46601d09464cd58fd7b0c64972a27fedfd696066a2901c51c4493bd2050ec0e668a49c445c9a66051b6964ca3e7f2db35741dc4880cd3f995b1eea5d4ebc76a4af6aecc138d92d48587a69e8026cb2c0fe66ecb37352f35792d2a622db150d6f5bcc6308b05dd0e4a3b7972d05020cb570f24200a9f21d15970740bacc67119e2b77ad02094511c28616d5837bb96bb5005d0732f769ce59afbc511d7d99f38bec675ee1acb8ef60b32e593ffcdeb7268bf4cbaeeb2e533c283440bd3569297836b4f9d67ed064a37b9542f1474d7cb3445ad47580fae772893607eb29d64b8c6fcde5b543d0ea1b0aa72093cdede472832ae87a4d7717689e7df144906cc004f4ad8d6871c1b7b6e36e2d83eae122c330d3329035642d4895ce99c7820160f3e41932f0c8e649944a3563471e169bc7527491bc86193ff81cdf4375d310eeb2d6b4b777b98f8a4b068e7218024fee652d9c9d974d39b4367f819d990d9ec09ff1038d31bbb4e4463f0a44ea8d5b8974cbbb805998a2f94383e054d213c12f4db8eb9fc82657659123fce94cb7fa3c6772abe0ad55c9cef2ce36bb06c2c40a0875d716713deb7122c2ecf6c35d0244537c32fcebbb9cb1a6c0ab3acb4bec9a76c20bf7140f924e8ecaf337c4623e4d2cfdbb669c2972a3f0b7f8aef52152c053e235bcc0013ff671e5f5ff328d1a6d383029006bb00e1c5c57dd80d515c5e5351434746bdfef258e3fac55a1afdaa098fa9b469cf470e24eb6f8d29ca2ee3ddcaac5bb397309d748036853d1ff50e0138dec27a930b7360a3d452c4f03aa7b728058304c5a4a071711631ab52662bccb7ad70acaa50c92f2a50430bc2699cb9bd5ae999f297e27f1be956c2f8e2ab63eea22181e3e158747dea1c635755764cd95960bb1d3fac69ad8879f300f43fdd2dade7b6f29654a524a19b20881077b07fdb1f38704e6fa49fa40e4cf2cf6ab951f85342430fd08c43ea604ee8ffd32dac0bebf312fc84f7eeca517665f28e31da1be8d3288fd6ecb2ad484c9f6af961cf37d3639831ca560e0f24a326f1f5aebd2cac6ee5fedb7fad6dbacb52e6b1fccf7377bb5cc36c5a890ce12f4b79f4b7eef4202b3bdf602916fff7e28df75dfbf2728df7eaefbdd095afb6df4e38757c96a181238c84f96b148c6af562e4cc9d685a61c73cc5819fbec35a24923a44f947dd82d1f635c0e29bf1676c9dfbe5d52b6d47c7eb290cef7953afbd66541ddf685f2b9b751dac942b8ec5dfedbe705b19f8b1b21fbb0cfe58fb9631f66dfaf615efdf6b2daedb9faedbbfae3d77e30639f615f2c92616757bbfa430273134d3072d242d162b41144daf9c2f92ed77e342229296172943f3f977f4f3469d0100459bebbb42e24304720f233b094a7fc5c4860962f3fffc2fe8200913fe5cf8f68522552b4641b394a45a923058315189a3211510af2eb4f17fd9094fae6a81f97c0324757fdfe76d58fefaa0f66ff0f09cc4e148be2f3b03de6b427f7db5ab31b23f7ae4ccbb0cd5ea4fa91dbb42c04a26915054ed6fed6c9ad032b02cfffa4d4e1c2b2b731c60f2247b89f1049391efdf11322b39d1cfd7450195dd8db7f4d72f473611f1298eddf1df5239aa08c51931309285c3210fa6dc3bfbe7b417eb2f4c23ab911e8d3ef39bfeb5276d1289426b996b027fbcb3a997ec3c9f33136997ebf540b64b49ffb5af8960b0173059139be90cef283fc64f969f61322fde52744e6f8fd84fe29ecc03903f3b1b7af4d4bbdd05f49e6c69e621f100c04397a21f6b17b07f6f3b5f64299fb72417e7206622ce24179ccc7da132285c8dcf773edb04f5f49e6fa3590f94a327b109983c83c33d9337cbfb5d27fa55ae092efd77e193bd6185fee4202f38c09d2efbce5f01503b3c24164269a20fd07bbc7af4ce7107dd944997e8cb0e9635c8eea91878c9bf7e476ccafc3cd0315b0bc5c8e5ec0521ebadf7fdfb91d3588ccf6ed174e212328c9dc55158916278d2012abc261bffc3a4bb5d04488ccf4bb0ed07e6291fc9f6823fe0b2bdca76816b2706a0aa24d9b1715350e21f5a4082916a49eccc1dcd0c98c38f63d6fdc20012587208e1c0d0b55578a052b23d8d48b1ca59e483d2183f9e0162842c74d0c4d554cd1c41195caf48991dce116cd51ea09d3c4a24915132c1f8a8ce0e614e97fa1771f5c60c00c1a288488f29282a7233f33a6cd28b961c3b2196101c8c99391ee7692c4499a6e62076b628593344ea8c83e394a39b19272e745c09c528709b461c1ce961f9690c28c991ed0a0799de9260d21327206965a4548ad2047aa490f6646861f2e4fcc9ca953e68a16288a1c7999254a32a9252d34b9b2522be869828d915a0244fec9516a89188c0c0f9898172f2530312f2ad5e444d672946a52220a336384d51378ee3ca96192b1d24f2cd41ca59aa85a4a29a594524ad98f630ad18a5d2b864172b395a3f2da89ddfb4550be8c5c0833538fe648811eb00239394a35a5c95a46ffc3b81068a6af65514eda3ef4bf9a070383e7471adb9573d298f4a3d26e96b1ef74da218d3286f2bbf507a74d69532ad5b425532a4794524a293582bd0478705fc1963451690167394a35b1905b4ac955ce729452f28409fef532f2194637bad70c29256e32cd514a499b7cc3f890c3970f3cd8a72ff6cb965be68a0c542c104a9ca450a244852c7a98022b52e7e21027cb117982b850d43499b152e4604350c39593a5aa8afd014a9215ee144998965401270a3b022690c0b92abc99e204189233f648981c6eb8754a00830c760629b709244c6e0d488a5819acb0b273a7892594b84d86b04cd81481e6ae20c6668183dda2e5122102ecc90b728e38b958478e582bf4d82bf0604d4e5ca6aaaa226c560a2c961e6e123231783981096e1123b0384cb15e8cb0b084912497092cae1057d80a746c9515582e465c2653547eb04d94e1010cc684cb3d024b1231d81b8ab4104491a6db258b0e1516092892c811011263dcdc2780b04444d91774b836d8f00309ac15a72857052a2cb1a2820a9607ac5b458aa939764d122b10e236ad712365b5ec70a1a0b298218610b8808226f7881493915b822bdedc7962654ba0024b4584a50205174d9a9bc498a72e4ed0e094f5648fc882e172c97a72a9b2905827f6ec90a7c8133b2ce102d5fdd2c6b6f1c10ec1c3942f36055b4c90c5952b9505958b250b27ac49164af46461ab98c2481d3671aed5116782b050a8b164cc189103096ab82f64598345952e5840d182c50a73b060ba46aa1083b1289c608c093c1883b307636f2cd49a28686c93317abc6049b6a8c172a3f65499b227ca8db387c985628f91337a6c0b79ac0b74b0e2d82790b860de5c5963078d11632c09bc5c24b664c172e1e8a972f7e88962023d4cace83102464f993c4cd0b13dc4b12c20a1c41b356bdea04933c686f162a3d8729560b9575754997345947bc3154cacd41546a8e8b933e4b15174ccc4b14a90b039bcd161cd6501cda562cc0d81172cb68800cb94155560b022ca1d6305136bc60a2356083df64a9e3a744e10e7224162046f745873a5a0b150c60ce1a5862d3460218355850d561429584c6c142c2362f414c9b3023a9892385b90b82978e3c39a2ad0d81963dd78b1366cb95eb0e4a9124494364ce25461c45eedb177a6b874a2b84a348145954d638475e2063bb226877f69a0224d0e5fafd7192ac834bd627e4571b0478b930bc6c95ef550afa692f815114dfa6a054de855144dae982289d3142f646bf000c164c323c4120d8f122b64160dbe56e0105f81c338d53c6a98f89b16faeb9ce03c7b56b842d2e4aa0524779e68536a122a323c40e01162c3a304a5b41dcf159e26ec566badf63a6547b20dc3302cd3f268c9f3847118a04d7a795a6c32cf9b9f9f9f1f8cf3d4c92327cf1e24ad56abf5e2c5d594ab16362dc338ad083c5994e0d9d2240b3c3ac8b992b3042381339183cf298325cb997026be14a9599706bbc28d224bf30163c267ec133117883b5eb21ccfdca1e14e95a965c1b325164505e09192c30d4f933d39bc57399eb923e7cc1d387782c03e211aa7c50341adec42ef31cd191c11f47f39b7691976639aec5edd566bbd3f2a9caa51762cd336ee861acc7d1877dd3f08be5e46463da84c79df006a4a73a56f64a7aec1dde7766f75f7eeae39d09a83df0d1be5f946797e5d1331ee9a468ded938cdce0c8be10d7cd2b610e51af9223a5db6f238ba5b5be043712ce1b34699284ec2feb67d8fd91734cafb58db29d3bab97f7bf491b98bb5d2fc6a13be4d6cd8ed63673ccad73b5725b866df7c0d9fe4b7b652fec8757f75763b39bd06ac7ab2c9f29cb194a35f757adbeac5d3fec059191959535f1f5af3ffaaf19b386cfd321aa548b28610ed17f5a0387fd7cbff6ba09d6ef87121c39efe350ffe6c5d8a3ded7a2b2282caa6a750b7bc7e7ce31df6b5656d61552ed60d36ea94ea8fc7e2aa97c295b4af9c5392fdb9b7c8c79ad01d7aa65b1bae63b3e57fbb5b5b5fefceadf956bc9ed23702ade42eef715620d4c879aa28dfafd1941ad69caa11be55cbf4c4d2c0ae351610604942c73a6cc75a8cc4ecff084a4b13aad4778ec1bd968b8e5d5daf2c3b8eb1a77ad0ef3fa7ef5fbc5d7ece6312c6d6e47fd227737487271ab9313290eb13919adbb629996f432d3695dd661dded9eee86bba19e2822fa9d494162b07f5f8c5f5c5eaf202120a064099a92737df35bad5273a91d8803d8607bd7eadd995eb1ef34aa23583f563b69a451c4f62994a7d0fca2db0b7203e5c66a0d2c9426c3ae0c1d9eeb67148be86b8025f64d123a41c8b36c76383055a1a6a850158a4dbd2128fb57a35c4b6a548daa50f2694a4b85daecb44fef6e2aa750a8bb81004a4e4921a72e1b9753536c72fdb00566ff64d504e96f7364d5ddb09c6a2aa250c9294a8536e51eb2146689ccd297dc5fac53bf85727c2cea97de1784667d8d511f10602877fbbc176be037ea3e83bb49b046d567d36464ffec0b6564ec1b1206cb27a13b1b6bffc8ed5899d2343f4ad02c91e6c9f280b9edfd9fd6f0a9d7c8cd8c081a75f55e119c41c4c77b0eecd7e6e138e4af7938f3302f468c51c04646b9bfaeb34b5099ec6753ab6470fdaedfc51bcebe07418b69c0f2c3a826aa897d9366b8a04ac10a4e98c1d44c3acaae76fe06bb7774764622306a23afb55b88671a1c487bcfc0c2cda33fa5e95e4b579a48f20b42b3e078d507a664f92f050ee594906bc03c8a74e89b49d32af74fef7ed704ea34ad50b0618995eb875d9e4e0da047f6a3afa98915ede47062dd0d4fab598709d4b4f2aaf6924175c08e3bd1c10de54e21100eb9bfa192dce987a88286caa13f6590fdc3a606e0c69f1a871cfa53eeeb4ef287268cb853cea07c883de3635a837bf7bf5fa8e4461cc9de080a4c4c4c4c473eaf699a578ffca88f34ba691e3d9a475add344f1e71dbfd987f249e81855d483f40fc71e89f6b07cef2b1667d702ca2afc9cdbf06e27fbffd43eaec5f3da0407228fb03f2b97664ffbaf37625bf92e9edf4a6ce8e658c89606d224d6d56cf335a14129a207d70d2a060f8edf3fda24450f045372df3f1301da2336c9003961f5e14b0cfbf29d906bf7c14e8b5b57a765e087cbdb8afec4202e242f2af5fbf10c8d61fce665361a9c724305b8b06dbccfb38e4efd8ad60f6215e3d5b7fe93747ce4f00a9b4de2218dcdf17d7cf82e5874f635f7aa5be70d240541695fbbf0adc8f834e2f298b9a21501625e34b0c0f54c099995c3f04caf427a7e395fdcba66211fda021b07c1c34cca632fdcea2e2126892dc2c2abf708a027537902814caddddf21b4b3a44bbb91cf3c338fb30a6c97d63065118d4ea846ae0a043f4c3edfa7c48733b0814901d5c195078ab11270524083176609f7e471f7f21fef97cbe70e630c2b9dc175e8cb58fca26cbe590d9b36c924d2578264cb066ff7ec422fa187cc1f237eba520efdfad4635b95f72483e56c2dc896a724f5042d92d4a324051b1ca910a558eff94e3fb546e2919a272bf8d3684b4e572b862f49064964a7db911e4c7b7dc08f60b3d7af5e9ad9b679bf4427f97f48f55c9da11c22597ed1cde2e976c97ecbc791cc7433e752ab2ecb12c7b4c8a942836510a9165d4c5ae75ecb3b71c12fdec2bb703fbb2cf85fda663663c4139f3b42fb91dd7de2577935193c67d99464db635dbcf031570bc9ef40662ffeeb05fbb328e99c3ceec7ec4ff56b93ad244df66c9f6ef12fb97be5b9192fdab8b8a55ace50273d1a2f18b4db29934688e30381d322adbaff3bd7d5fb6000272df1f8843e276dcaf2778ffde98b34fc7cc7182dacc38a8d5b64ee87543041f3f92861c71b2a265d2f83269ccdcaf59e0dc7664c36eb7c5f940eedbfa9b643369704852caca97cbedb8b8b5a37efdd1d93252f791513f53347fa72a4c394ac590850a56fe4935249b95957595bd68a1d6f16b1d5a9c5168cef9d7b7cbf1f0c7fe6a3b341fee351f7c5b139ce187af792efbc5095a6d6a5b1099b5cfe77d7ce47dce0bc19cbda671a1f30092615253ea64ee43dffcb5d7ac177b29db2ff4a7dc08a1cb7e7b739fcb02c9b0f76f577f7d211ed967df7e9fa78c396255d89d680aab33a36bbe957a012b47a917a46401e428e58255bedb9efc394ac100276bbdb54fcffa332066c7ecc718d85f8ec750ae9be442983b865c48a0ccae1d33cf177281a51c73f3a83fbf70ee703d98fd8b6cb4385f7efeedd9c8e5684ab1eab6524ec7ac2f65c8e998efb28ff1a85ffdb1ef19a49f2a332ac833b9bfbbb4cdfa6462699f4b3079c5326d9b4166d332ec8a500235ad26f665a3750cf10d54958aae40a9144335d9f4fd9bd193ce395a767db80d3b537d5edceb0ae55ba713e9aea2aa7a9d2ec54e63396555cfee57ee9e5ffbb196f432a361158a8fa01005ac7defae65a2268724ebd7b1c897724c416f755ab51afc6ed84d557577f75a6b9d23a03d5c2cd336231d36dc755dd7753d66989aba1b36726a015bac6a9b731c56a9bbfdf68e92b1a55f3df0bdb39e898a6d42d7e51befc51d6f5f8cc14f0204635f6a85bcd85e2cab75c35dd7753306143c12b5567797d46badd475907de78c7aec547777f75a6bbd17e3aefb07411c5aa2bca913b1facd2cf68d712593b9db8bd92cfbb06b9da6ba8ed16ef45a6badd473f00c886c8c166bb595ced8d47a75afb556fbc5bdb6dbbbe4156dd4770777f0bc0802207bcdf5fb58e4fd3fbe5b09bd97462aabfc6299f7ecd9b3b1110c466d14fbb65012afd5dddd5a6b8974ed1384a86ed8650c3f88e623e84244b4acb18b310ad82725136badf47aadb5de08fac188cedd2eb6b7a3ba1763b006257d6f7577ea1fb9103008132b06619ee8396787b1db5627f45814789b52be9aa63ab04ac7be58d2f49de2dd699e6aadee3ea17a4aa982d675541bee2e7e7579d5971a4d66d9f3fba32ea4a66a3aca845cbb2ed7d851fd11b16f7717ac03bd2252bfd486bbe1f844e4823b3a44a7a72465d7961c3c42ecfb3bbcd0b535c3ea77336c7e999a7a21f03617dcb346d206e648923ff2e8813fb681dcd103f740bdde11193df05bdd566bddabc3d1be3541da94cb516badd5c60e6ad7ccb3ea6d39daa9e35627dbc818630d2fad0dfed7ebf58a7de514f7c936576033faaab5d638e4debc436e23f14a6164ffaa13004660a7a9eead6141f4c6c1825883cec7c2e6faa52073f8b16fd339e7a9babbbbbbd75aebbce285fa7037dc4ad45a659d357aadb556ea598c62df24ee9473592d02bb9d79f2e8f58280cc3ae23e02c421ff2004ef128007f62ae042aa9dfea5bacb2adbebf42b75cba54a8dc31d72ad7bbd5254510f3ddaa8565be264fbe18b21446211fd9f2811cb7318b1366c85d59901072b5985037d22061911455f5300a534ea540a707f5ffc3edf3df87a9752e453eeee3855a37c1213ab6416ef5baf70dcc9aa2064550e25a592589762c522fada172c9f52d1482fc5c2aa02024a1659953d2a86a8bead32d76e015b54ea627aab33f1bbe188a77a9d26f0d8774a3997982a21fb7727055ddb5dbb2031f7628c810a3c688a6659aed3361f2fd221ecdaaf443cc657c6beaf3ba91576eed56efbe6f5a04354f31eafa91e5353537d2f363b091294720fc97d9340796b74fd0deed075d706f510debb61b0bbd8b958a6b9fbf7c518638cc34b5454ecdb4426d02eb617cb76c061252aea6eb8c1f8e489fac558a66d9c52f5a1ebbaaeebba8e0419a8a8ee869faeee0ba397da0aa2a9b55afbc3da21d756b7eeb5da7a44f528a9727728d5c8289fee24973855adba68e97cbef0725d47353087dd2b8b8e99efd7690610c453776bc9217d9256fd45cf9a2bad9cc945d04051770bb5296d2a4b2943b7ea80940e4cc9b2034e59034c5963cafed30b93dc2af7cbe8148be447ae017d14e990b4ca52ca2cdc9f75a92d74acaa5c3f74ac3eb29f467505961f6a54474ba146956975abec502f14c95edd2a0345390ab4f8011e932b9a1c7639dac0d4406d9d91d1eb05821fbbaeeb2177fd8de321b91d314a666d46397dc2a5dcd12bd8b53dd7d003b9e5fc3ace16753777293ec59fb29c69b2f43413f4a926fb4bf7efc6ac9e69a69a59e37e7fdd1410b3cab9cc21fab99627dc13742dae254da6b4ebfc63d80fad9d6c0a96924db1d9944cb3e4d0b54cd1244da64ce58e2ab23d16a6977a111168d16badb5d65adddde90b318afeb1536df818fc26e2e16eaedd9b791bf4b877eafefdf0e69003086c2081eccd087ebe5f583b90db659c3ef793b1c8678db1c87f89c6be44165cb9b28128f75761b876eef6859436c9d42977966fb3fc9a727f3f1dfc622af717ceaf49142af7d70b4788dc3fa7be688742e50027fb540e69148c5cbfea3441fa00c042a3a61039a477c374def0050aad2d07b7a49c4ea5f47b6db531bcdbbbbf8d89eaeeee5e6bad93041b6e2a056ef9f4762f48c4dcffbeb41e63014bd9fd9bf385fa42dd0fdef042e00469f4e697ee85c08e83214c30c4edb295163fc03f51415d9452762b7778658cb1c3a496b5afc9f0e6a9798c45336ce1db2a73efcf0fe80a037ab70c4085cd9c566e298c5bdf57a973fd29e787e3ac5e03775c70ab139a43fdbd94ebfe1a428b1fe07edcc25882e04a348aa68075fbdfd7a496959543224c4cb9bf14b0ee16323356eb178416b5bcdb047b3629d1aca6b99de2540366769b0ad3463e60dcba715488037b2d6a778b5cfdc225a85cbf3dad65529ca237c77d1d92434a406e2c7e3e48f87a58c0c28c7da7b6395ea84c35b14c77c3d7cbce79a4511f8b33dac836ebe31df1f9247ff4f31de5b850d6b89b371441ff1fb5aaaae801d8500472ff47397db2efc6227f0cfbc20de4eee881dfea81ffac77c3d55a9f91fbd36675d395e9034de61511f32a4e2db97f6689555743c42cce6582f4f113da34b5e490365dcd2a2e48e655eecab4839dba41142d9ebe8d35623cc59e7e0ccfc621ec5b783482d80379d85fe0616f6564ec3beca3dc0818f68db54cdf028f3e0e5a2541b8b103fa1d804cff4746a6ef9365fa8549461ce7853387b26a48d6bccc8b2d3ba8f4b3fd5dff7c72316593279c7295328928637f2f51bed8c55edd927c719ce7630ed51034a714879f5b84affff15cfe3fdf7aecb91ac873f5e76a00d073ef03e8b91afaf381ffc547a4f540bf74e4e3b91af0f3ad77f9e7e3e75b2e1f3edfc273f9bff8a523179ecb3f57035c7c8bf7e1e25bbc7f8b8f480dfef968f12f3e222e7f4c87ea2f1db578222eff52f0f1e261bc03e2d1914b87eab71ec64784088c87f1bdf8968e5cfcd2910745f011c77df1a805baf0e2510b2f1e0179f1087bf1e8c75b3af2f1968e364fce4fa99230c1fad41bc0093036e563823d306883e9cfc7416b705f3f05cc211c13acaf55c1fe3d9decbc4fc4973f279671f6f4a0d3cb31230b7f9e1fca276f08add1803844df08a625327d2732a55e2c92d3648231872fea3399b27f036883697c2128986a5330a54734db21340d4880e58715a783246784b0a2085043022c988200bb817a0e4080186e609b065058fed00a583ea594524a69e490524ab38fd1831cc618a3b66160c700dbc6819e02705b8c34aaa4b43ffaf2d96244d1c1cf8697c418638c45780b72e2e0026d423864004281418b0dc885f642c6e9132e5dd0da06b044f7627be5b1e0c2d886a08061c1d6b179d16d1aec09ba60e3e04dab636c18cce0c2b5c5a0a145d00636015d0c360e6cc0196c728ce0bc8400d97c4cd3ddc87106518e2fbf2b611081e537a59452aab58c4d0325b2fe6d003c5c6f7bb1a9dff66aa235d85e3050a16d033158c6d911001b57446eb0bd9460f95402404883cf7b19196010e48a312ee862b400c68b968b1640f8c787037de204e34f1713f4d24bb7f7f6895c0e0cd4e2714c17b171eb855e337028a0878f1f25a0a0c474c4494a96818969a638b520a7489902c4e5903d48f925a6916470e4ec79a8b26982934904476413c6524bcd144a013774b8310485275828280df9b1c2ca8d2c2218993132e10714ab1f2598e073c7c968ca02667c9941428f28543d7c90c039218fcc68d2e355e625020e2b510d0e0588b02d81f5924c3e88a2888c5e2c18bd6618694ed421ba22013c0382ef02f84260664515684501a11d92843a2c85bef6daedeecfc9e5b850dc2c492867707e806470cc3c25cf59967d77831870a6c53ce73797437baad11282c6d8cfbca4202d0c0a83b296655d967d3868cb3b2128872584f4afbd37ee64fa25056517d6c5674227553ff392baae6bb5ec6b2774393421d3bfd5bebadca2b6304a6a89d93ef3925ab715b67296354da35f987ddd4e68e530c9b91c5b15a25f522b036d404428e01c7eda6ad9873828a88574b68fe3a4f1f33d6bf41c92206cb203ed7ff0cf5b2f44216fcde5f8f9943ae38c5f931bfe6467a52d8ccf675ed266b5ee664fc36dcb99176e597bea855bf6d1dee7af04e1ca0eb4205bae5fa894b7c731339fa75c0e9fd7b2b2b2acc8db97b465ae33e5be213903a3e0f8d43ff39468a6f289d22d7bda050575b8d5c25ba665f6ab10ed338f7af45b82106507f4370f07fd82d0dcf5a93724dbbf958764626262722135a0e1c280e32300488f122f983017841c8ca991024c948b2530ce510a0c940b1ac06c712103982b645c5803a68909ee295accc1148f194ca500428235396bb0abc0c51078f3a155678796121b5a1481b91ca5be8cc0096b394a7d4982efe09f2a6cc0581451700c2154802f9bd614d87f6871046e3438087cc7b4a0c058971660b0dde2e20a5f2c4074bc5469517991d2f201df2b5a64e992670ab675701250745e14c13707a039d8e628d5e5861c6217497429410e1f57813b47a92e5780cae09aa354172b524f7ac8d090c37fdda777c58fd9a543e6f8fde498437e6440d49ce019fa351cf52398efc7d068aa071df294e61045a107bc4d30dece625cbd97cf008620fd05ccefba4887260e29259df3f33141da6382340337b8f354c0a4c124673b1169f487734e1957e0ce73769043196da49c722e4d29ab341b3fb168ca3658e6f027cf973fd146cc22782e19b128462e030c825c312ee82c80f1a2e5a20510fef1e1362dc3aeadb1281671f471cc1af48b71487e8c20904b734e39af2111fa8cbb467ca13d784a29a517e79578344d6a4ef07c6dce386577c7a739242225a5d487062dc61af4e5df5863befcee4b201e08a69480162308734ad94108ddddfdc590524a638c31c69e4c9a4610fe8557dae897d1bf7eed9e12f5eaf14ba173a7a029a57fe3bd4dbddb7bd23975ceb9013d408e10b3d65ae3a431ebdf5823c3708e1cd24fae1f2386710b58aa9f17d656961fd552bd76b56d6bd8dedd400f8823e7e73712b8fbb7ee966d25d9e0f0f31335d003e234a1fde109d2f03e9df369831d94a81fbdfa309ac0f3c36995ed19ec02e10b3aef8740027c90e5df1962190816fa635efb17ca1a7f831df0a4534e8ef1b153d2bc82e2d4d556acd65ab3af2fa30d98adb5d65a6bb5ef66b4ba550ffbcccedaf58478464c1554f735fbbe24c5b8fa0f3078ab35c3aea5b8d57584e3f6de97bb7badf5bbae5a5badadd5daead6bd565ba99dcea4dded7aadb5d65add37dc755dd7755d8f104c4ddd0d1bb999aa5a88a0a014a972f74ffb34d73d63125911a84e80fbfb6211680d07397caf1aaa463695b16a42a5052106864b25a35ec05db451adb576e6fa75b1c82f58ba383a0a436e755badf54b6de8d7a665443c8011f4dfb4cc8522d8ee6d83163fc0d14cae1f1020fb90770150c4b2fb191b603c066af1da17be8bd6635ff87af18d617c2f8c8c5aaf970b106cf10fd475f8fbf9cec4be372e05a1c5a8f4eb1a4b82cea119100000005000e3160000180c0804c321b170246771b60714000d6ba24262422e1ecc62418ca4308e8294410819420021c010023364354e004537f1204098669d1d42ac3462d2c53de8a8328b40eba4ad3dbf83fbc6b37bc8bd36424f55ebc4f293d5ac06eec74ab957233e8243f47de080008d98030f1a84a231ec52f64724bac43872d6f2bca42a59468ca3249eb08c2546741f8902e7e14d890d6a44bb7291f1d40f4a03b09bcad3dc422353fafd8586691e58cbc31f877826a4bdf66b2089efbe03c00b3969af6f91f2f0c7eda3299bc75c8d5b483d97c957e62b6f62983c4bf265d65a21db8ac137f5db83dbdea3a98df1181b572f7891f20ef5b6a62481036f44b20ae80861f430557b8d223435de8b26ae00735432a3b4fb3610bca06415d863f59ec65c21a2bb728fddefbfc8c17d2326f79ea421fad4f1bdff0bb7431dba4c6e4580faf0cd228b00babbfba569b1735935a814a3e4c3177bab6dc53632b644c0798ca5422def76f9f86401459dfca7cc7eee1a4d6c1fc2d27dd76502522e208ec03de3092af7916cde6e5cf2794e97380101bf91351e7ae7d931a2baebb8c7e9e8bc6f71c6675496b04b6721c04e13d6f8730d0ece03f4d2e6b28444358baebb7262e6ef43f5fc4af6dd7d5cf51d39082ad3451b80bea0582330b10d78c4423c4a04b57c04fcaf5a5600ad745d438966f65a3e04403a623e0997f1c170aa6d5da17521552b685912bb68ff58d1a57e8a6d5714cf55efea284be6a8576f2b96613086924aefeacc1732c60cf1c1b534e152f57a5f81d83c61d7119e7d9fce517c904e8bcd512f65460242bf368c1479d7077615e42d189974aea3e75d7f96ee3cc8ed2e83f6fabf5e24572e17f50ac8cf40add54297235240840ffe1f1cb6be09b4f9081c521f00d48399ea84a156e322b2bbb71b0307d927ccf9fef2de5c29777deb9914dbf270f77ec89f9b904ee1f5d342cc7e59a1567dcd172fa1308ed87e1490a364abc3ce7cbc4795ee460f84872d4bef5f3d1362e133e73f305cce160d78a1952462d6ba5228b6d4b0871e235dc1e5ba1c7ffd3d00d0c34309645e9bf7551f3b4b32df397c8fb89de1e2f42a0d3129941696d27401516e1e157a7e309ee9716c634d880bdeace90053991f97f68119bdb28f830d0d8b9f8a0c3356ee029534d055e526660c481747bebc5b6a4ed4df4afd6ca84f8952209acc88dea0d77f84477bfcf236c1e6660770210318c90998e54b2981cd966aa7a1c2b2f74236e48a801896ce192b1582b804beb8d2619014b0fbbe8e3c99c24bf0921e6c5d61d673c5410140a31c1901088ac937ab7100e72dc71901d49d5b2654aa17126f8d801fb35f165b356b6246ad3a6000514ef61920e05255e3ff12e94aa888c5a370e82f8ad67a9824fb8042fc0bdc4e5881254e6541f33d84a995d468bcc2cebce44acd81b1e1d05d1d523a05ee5d40ac4a2a6a72e2d8fcd2f06be15e25d04d1e9bbe34254754fd5878d0d376431022608c3a7c14dce8e6054af0fcc7fc099bc54dc1e4bf63fd9276550521458a28409feb32df3178c245224cb496780e32f00230198c00520027814a2b589f85039de2e675e0cd06c6035a1f67579babc59b64c546b75aea5a65c78af224dd5a99d32770c35662ca6c3f1af8ff4634349423565aa85100d44c96cec5094d9ad7ee46704119c1c83abf6c7bc7a26d4da74c537f062703c566f6d368bde2171af315dae19a996e2ba2c145c425087b2ad063ad355d2badc20ada21387b28d4e15d235d8b63ec805559320f971f324de5e92e83594fafc7cac296605f8151e0f875c2153256a5545addf36154a33b33cc7a25252baa0dcc5eb2c4afd052995ddbb1f3f7c830baaf519365a8e6b9d634ab99287f84e0198c1de0b1ba87719fe8dfc083f40e5ba9ccc9980f28e33c72a09ace9c712370fc9f991f5e827a7f1869139e24b73060ae9f02c3fc01da4dc0bc1230c1113a9ad5f8ac43bdc69396c86fecc47f9f322866a6d9844496526a640b837e734160c7be6f07141f16b78dd1c9dc997e2046f603dd48ebd4ad8b62a743dcc7dea6ae64a0162e6c08a335b0971ab85594492111cbe275164253c67bce0f5ef0ffbbf387210d902dad7f38ee6ec5faf9c0b6621720a26a523c52f81e9bcb73ad43f6046413086aa524aab68216e7f2d34c8bcf867fa4d6f14f7ef0d9440bd662cfcef3412ce54c27288f88439a5a029c4d46e8113069594839b1b0eac3b028968f9d484acbf2332552e93fae60498188802174a924f7968ba66ff46188f0945497c527e4aac3877b1010b0a07a45508baba9e3a1d62c16a3d939a648524b903fed28eb1297dfedd7b2413b4ab8f9ea6db912689db7a8178bbbf956bc4a5af7ee2a7434bfcb69689bf57727a9d65033f7a4cc1bf17f7aef30e7e4bc3f7b1952dae0573a9d0005dfd77e1485807da9a5f9d46d1628f1173e9cfa485ae30284b29b57c06b24aafbc802218740fd8a441254aa6be674a125183ab0d3af6c3bcef9bff92e64f0398b6360d07c48beb0293a3b73790b42f161d41b184aa1b6c898dfc38a5bab1320a28af339b1b53aec3bee52e55aa7274af34372e089d457c210504383fa555f744dfac828f220511080747df5be53f6349c3750b5b8787b9b74670454702b11decf259e26d53a71b3ce1117c3fe226a6a70640af6479b162f416e1ba400524c51abc74d83f8298ba22abe59d0e86f8cfc8bdf88135e6fb87a37faa72817d43e98e3144d8e27e4973e76d44caaa56fe39451627a238a639766f2e0590d70b09c24e00155491613a810229e08a59099a3869cf6a41d36c593c4ee68813dc1d43696289d42b7934abdc4a2b7e8bdaab35569d5346d432d1c1648751873e6167134b1772738b94bd491bf717d8b2f7fd29a798261051f6d7b16ef9f8615d6faecb86851ff5422f61bb6e031fa03604806a7dc4ba0612ba74ac576a4d1096ba759ea6c2ddccba16dd48d338b9027a22be34c650d21c1dc07e2cf4acfa2701e192a99fef600457dea54efe25036e69bf7d89b9e6b00c22bf26cd60f59af269081add21368d94e084b93c9c4b757d8d6a1ed1683a14a66a7e69a40295a23039a2e3699bf877b77bd52863c3159f8ba0560220cbdd7f26873dcf2bbca4ff5c4d3fac439223e6db06a00764bb5f543e4996b361f282fad3434077c0cd0f7c9c7ba0bb7524ba424f1627dd86fe27c764a99b2aff669cae9931becdb6c9c18cc17327a6d59c88929bcc686d49731493191a6a560225a0045bff1dd9434b8fddb1f2d25c101352fde95062f3495222de6cb9b5edfd002e76b4788fbc3e273406cdf8218134f46dcfdb5c3649a528e34e0246a0cafa72d6a95615831a61f67dc216ebecfa65f56d0e7f2d9614dade0dda6687c98376c2fcba247d149bebd742502be6257b80368b732df38faf7904bd13d6b58c786f9936a4723e3757b642abe31698f0da91e7729581e4ff930fcc2f6f61f628dd0974be4be0aff5430046c9b7fca4a818299a52aa6fde9fdcdebaaf9cb84cf19824a329543b0a01969f95b7a77b1b6aedfad4a6085fcfe7d4956056ffbcd4dc74fbe3c26746d911f563e48f21247f61055b8b8d540931309bb66b09a5108dac72a078442a5385dc05f52264463a75372965967ea74baf305de0ac02221d59b989cec9045be911523277d299fe03e59cf340467d23df6d54b1653bd0a7d253eaf22338b83c9454faab25d7690eb8a9bc53d96c957c8eaac44c55dd8d4d659690cc971b56c132b1745ce8843570adf143d86879aaa46a4dcc8d11731df4946f26bba6230566de3e520fc8c2cad7f4f8a123b5fa90df3465bc2907ad09216bdef6451958736ce122d99735b2d9761c48cd568abaad92e885c5c3bfcb80acf38d76db1802d3d63bbf01f651061fe8be941ab412b002e183d4e5150fe28a5a437bfa3ef338f1651dd12e80e148631d48d974905cc096e46774e32418abc0c8c4844ee1ec5fd558bb71188a3a8bff0c495da4be7f1192025ce9dc1951032548c2b4650df823c44a6632065b9626e349649e55abcbc1c2886bc9b2617187ff57ed60cf0948319fa05cd4dc81b3c4d5aba7e463318e4b0757f2cc52def6353a6f4e785c53820def6e45245bae401c1b13c7a67fc9abaed72f4ee868f1d732f99e4e388f65d2d50966d84b9e598061673025b9be74f7eacd839af3f2b492d8afd4a1692e36fe182953469b3e1cd7610ba6ae796c5007f64d516bfec467b1ce8a303d0dff2e97691187a97b8891a6b81796ba7718753d74fe96995a195d2b056356e946a22dfa9304c2f1f02372b8ad924653f48c90f212824bf7756f5ae398d50b9cb439dac010b77a765ca0573fe2d2f88194b46c9b130fda564ece68af56a087fccde015265e49aa67c0d00f3154fb50322de75011e69d1c9c4f844874a2c7187b572a978f1de4e873c1b3c523116d563558133f21f09762332a3b02fcd3d6336c5494cdd875b1e4ee61e293fe2999ac86eb765b99e101e2c9d1c6aa4fa0086d6a24c9348f4c06bcfa46342132105472bdf6dc723d56546a30eb5a4d75518d2c62ba82d1f68c1b164314fd6066ed81b48c94541c847b8364577c58cce2d02c422a9d818d6b0f63401baa4ed3138cb6d4705ebd6b0091a46442aec265d0744ea5cb0e7e41d2953cd1475952253386abff5e6c042efd5a029b8471cb5c8600e2bac5862c12258924a79a0222b053171da45c01d77580a8c6bf2ce49f4f902b9f15bf923ff7c02701fd62f2b480396ea778028be42e9f9780017678797f222275442a0f894f014467909a8548fc2078a291dfd6e8483082d8fbd805b147b200741202855eadded19e17594c22f9f016c16a9b441a7b4d221c1be8b82927fc26e5b4620b01f32fa864d6f5ca82a6c4b7759294c3237a332435881f21dad3ced5e23209083e778122bd86c28ac58a906cc07cc5f2fa40e40d135247da7bcc4352b0cf703e00f692750e7a73e0dfa0f909878de1fb7dff43554aeb49d413126956d1b8b0c15f1f12e366eebe394520daf071e0eb964db8a6930707206e5990e61fb8257a70d58e3d789b30e7d977fa5231612e84a4cadc167aac0660055952bf66fd7ac7480704539d2ee374d3060567934ab1914e3e520d10cc6a38af8af560e528d23b30b7abee9086cc4e91fb8ab97f69321c34a5d20bc904c32a24fafcf3d19de525da9078de43a0317177a48a0d4bb6eb03688c99068696687362adee054e8e8abee46701bfbf0d9d95031c5851ed6d793bf37069ef4017b3e9a49b720acbbedaf8cc1f5fc505e1bd7db115634566c2534700d143b01f58ad027fbb4ba10b3847ae1ddaca1ebee871f4f43fffda3ae5011dfa3c6fedfda3bdb9eeb2f9093f49a78ca04fd1cb52c7694c93f0d090cb59de09173b26e846d4e591622d6257e1775d7e2712fbffd0a41408585b0830d566d9cc76d4ffb9c5c0d9c0832e2eafa7eb3b7781aaabf758865aeb4b75a62d71c21710c0ab2306056a4059d25a5a072999ea50b7c02e4e7918180bd065407e4b214920986947c002b2b7bb884ba42540fa911087c172a810b50122c6b76084b793f14cffa724d114c4049c47726d9a6940da2a8da4a9211495ea5022bfcdf93af2d11ebae4871913995fe1303b3ff5ee5cab0f6aaade1fee8534d15ac4171492a885ea3bc8be9a9bbcc03b933143a1a8ed70db473e872b5550ef0056952e88e27e7e10a1081524cbf45d1664a751693fb52a8eb251a2461d98061576d0f00502da2a686d0554982204ab9b9c324789e854d08edf8d4b1bbb7ba5477a8e2da4cd87a6879e904b130ffb5001ab68c5e9aaa59a92d1318e6dbb31886a95868f296424879df0785c9550545ccc857da849b1829542e738cf6aec6c4521ab3f20dfab9a27471a394c23f1ce31c0a2ec96b4ec18e11e669e1888de64d34f2464c46074b7b3cc947198360780ef38f1feb59d91c053eeb88489351685f9860f15db08ceeb9f96c7b445f25f6d98c435ecfaf457921d03e05071bd2f1a812f64d82db4dc6cd1249a565502c6d045bc1a1387fed9c24ccc5c4fe98b086862f5e981f8ed17870a6bb14ebae117de40a75112e3b6ba1cb420d856b82c0feaac3357773d38df8494657efaf1736cee2f1df30eb00810f959511f1839203c50f42e34f0c67baad4833da8e0370ce221539a829043a5138ded465b2a06a717ab00f7d94710046e31b13033dbe92921e2fb913d8736f10c8ea495c4809b72b04784a3da4ce9b13fc6162b867a61ddfa37dc581422a45d3dca020c95e1ed974bdccee0f55725176a1924b561e15cf960e503c97ff591caab065fcf8f21c89fe54202fd799a43c96ca3ffe247b2394b7cf3bae2992a6ed1741cd4a0c5701e7c7681562a4267ec92db4022f46fb1858db88bedf7ebb089e12369f553c449955d81664dc26cd0314ae6c217d91e080e466b07950d3ac5d983759b176ddce22dc37762297038d348b54b8018f955bf4a647469959c86925389f2637a046fa4eeb5d927973abe49a4031c76f0fcd120c08490dfbae127ba6d50bf3a754fde660bea7d8ed87bbcbc7a1885b59970f07770505f00b17958c1395a74cba31ac04906ef02bb52066cab6017e2611ad2d5a11c1bb3d687412636e218dabb21964a50976c4e14b894e8b0e1e43dcd6aaa27ea4ad252399250a020823601dc15d12b76dba5196a9b8e523ba1dd4e6555a4fca62ce9fa71803c63a6a5faa19aeb1816d5b3ae534b44a9b58b9027435d020e535ac481722c914120473b91259b350206bcecc617136c1d1ccaa4412fcc80000aebfc6c3ed00f486c55e339208acd4d75a8bf6534631a30ecd42a08739ebc788d81b6b740126f79988bcce5b1730988cf85cb1ec3d49db6fe212acb91ce5ed91b6467bd34802b08cfe57222e3e670515de8162cd1bcb12c815d9f360dee239d8fbc4c64e671473f1ec32356a251b3a34527a0a44440c222bca7b25cec4fd523ff692b5ff80e309a1f4d9254aefaa46eb2d45661070d4c1ba70c7cc51a3f7cf7bbecd2287f31380438cfaf74f4deb62b3f308f3fa7b2f95779311ff386ffd71843cdb5a45c628ec79562238a322cc6afb8120ae3b434caa3c352b0c6a8b04cc68a9af7b03c46688df855ad90220654cd364dd93e4699da98c4bb4fda37307aa3a1bd2e5d504ce6a960b26bed5fc845ec748db0760fb5ea574929fc6ddccf94b6804b0a76fdb1ad8f6ff97fd9bc94ed34c0ca6b3f883cdfc52e9185fd9e574fa73f036563852d1924a635628638ce8a509c33d0ae5192ae581f2f0205589e545c2982c3c24e2a2ce2270fc95b62b65a4105fa621818771d3468b5c58687daa56d2089382782488f85969cbdd7a17c999c0fb7e01c91c5c1c3106a32b7bef3131b945eeb16b5c14d6ab60e295e5805cc52637560c1a872508f5e0f3c08a067dea7118619aa0896ce1ceb979a710b9e2bbc17a83cef70f9fb8e17578c61557cb8017367136b397961a82a8fa7d37421fe070f68bcb6e151688aa7749f4803012eaf0f2d50dd899d32f5d4419d7c7fbe767ed7a44f3ea022afd4181362f808062573ff9b172f84a5aa426e66f18e691727ba9310b269f2a43da6ab6347e27ad1db2e3f15c7972e107abccc464f6dfd3d08e0e119b7c434507275ba9d4fb300935b5941b022d85592fb208563e20e2dd24810c2ba8841b04eea969af085941c0689a8245c79bfeea7640f53a1cefbe0253145975fffe89ec4abe4338713f310657bcc84a021e35f8eb9e51dc4e14336272663a08af5150bc8b71e601829ed7a255684793e9b06a77681a7ad9a1a001977cf9c3748bdfcc2c77a812b028b2db9d46ce341e6ef6d96deaeb5ebdc7fc6700ebfdbdc1cd25bbb00fea7eb0e910e61ff736ab56844abbcde86428f45f1c6ad583f9257d2e8964db9e90b1fa72778fc02898ec9e02ced6d22c2427c5f497e1bd8cfb5b469a940606ea1bb1aad8e73429b26f07c2b697bfae9bf8b64ba85de219e67924271d20e4f2d0fb464cb7582d9ef65d4f5e89202959fcd77832fe3081e74eb3e4b232c6c377733432fb5567d9b38fafb3dd88c674bfb222b73eacc66670cea3678d963d907b15740b9b45ff6628da799d86b857239f0a2c334f3ea8f6dadde0a9cf4b018c3155fdadab661b0301372fd26ec1892a3db86352cd72bc56ffe4825d768c5a79dac8aae4f5c9d2a7d5d72e2fda5d915188d2a2579d30049b2e9a42e6ce2af008dc4f139f8ba293c8690ea455bb64f514692ec0b7098be4210816ef8bbbeff759193c3097eb4283641c2753660741899f97d91c3d1581475a1cefd46d24c314b7cdc2dd80333dd62901cd3981c6c8d71b507c74745bb6d6474b2736e30c4c863e8a42cb10d850dd280856d2c57cebfb50d190414557e9836e95090cd599a3164aa281317b55cce0604634824f8155c615aee7a414187491b00caf1f63c3ba4e50e18411485aa2906acd31303b9403f31ae7d983952cc0dc5436343aed0f41ed09511fa04c06375d53d2cfeb49b16f522bb8a5f7af755f182bd02a3816ad32f9bbaceccbbb099f35a662a8d8ac43d279a4ef7bc5f7a61bca549fd5433b6dc620108f65e2d1b21905b732e64e1bb00cb51155dc932b3a6d5e8c702eed6d6e1c8a35030b8f45cfa2a5ed895dc8d1666de1f6df55db55a3201bc8a832e46d7f0e8592d76def18ef9285dfdae14505b5789f3cfb4e4e5fa4051f0dd93a03b2d3f731e77bd1ad93166e32968b8e4b802faa890bb5cc33631f0af86634e8ed396ab8490b979c005426b6fc5d78a8cee25f0e6c546f2baed71a03473b5567e39aeabc7e6f493bd45992b7c50c0fb96d46ddf824c6961ef33b1edef0eb446e8b1965479838e6d01e3ec1517ff38db0b9add8e17cd789608f38b06e9a6a5a7acc0dc7bac96bd0842fcc9eef6aa04dad4bc11240a5ad5e924e651328bd7cf360951f870237d7890b113f9373f3fca2197247e62f5a1857db5d1bec4b856a50656a1c18e8241736773b2e3723295cf8644a1319686ec988d67d0e054b636abc976c3f6bbfc59ecea1f46328f16e41c4d2067c9742f0e87ba510ad9496d04e000e82aba721dd347557226e9378ca275680683be3ee3178db50e30a63fc69bba7dba2fa85aa28ba797245441ba367a2795ce0e6b3450d74144991312f871655771b7ddc310e8b1f680485b6f4892c59a1177c05f63056a891bc3055dc7720a83f585eed1d75c03d0a807338dd558b8112716e5edf797c8bc00a65ca319c83a80aaca8f0bd794da50f01654d79dbf4980d951bc3db460a1fadfbcc9c2c4db6708f4e4e2e95af4bf7dda5c3515be3bb44082163d5b340745442ace13eb73cfe35834d177359022fd04b3233af577b53710e6fec36f79c4f30b69a0369d58f0519fc26cf8e3c674ab5bd55809416fde0a2b1aa9355e57c4a2ea81abb6359a9d25313374ca2542caa2e814dc5a0158011c8be688a9ad291cda7a90175a0c038f336cec9aded85b797da10f75ccf733effbdbeb9e619af3da2a57c10ff20b160bfa4a0863ee36ba07e8720da4bd26babf7bd97a003001f6ce86e1b406241dcf171e8699dbc2eda696dac135371c3140e800da5e390f7f1bf04472934157e827705ac9a316b667e4d40673079aa582a43307537755177d58769adae52f1bd1f34897071eba7908dfab4d66abc6b4d14836a7b0c05159cc80b39fec4401d51fa3b820e6f7b5a6150a0aefe3dd946d9e609a5d23aca8207c0c2c31e91c5e0c24e61aaf142c42520c8fa920eaf8813060f39781ce300302c71acf4141cf897c782821ae3fc01bf939eeca6700540eb6ffada1f6abeb9a7b57ab584618a0ea5d113a901d0748f2555785eee32bad03018736019a1612b1924da14b830918d25941033f9171c9c7aef27a3e53bcaed403a500b24b5ff8828554706221bfbdaaf5d0d05826a067f211c949143e76f06bbe3205774411c693a714770d28e50060e630633c4f5eefc72a031592cc869a4fb4d1b94931fb1c82af83bb2c1ab369680b0469dfc1c4b67637e1b0921748bbad5750f1b4436a3253c3bc0eb785f2cf97f23f1c981e681b6d85f980725363d480757a58bfdb984cb592ac709eab610bc34189bfd871bc5c2035e45320b3696070aca618cc348dd779829adedeb048a24b629a23c4dc0de1d8dd4bea80108e3999dc1649f8e7703e61ad37a8ce402a1dfccd60613eb2e266ff37dd1f3c7fe642efaf399e774fb5fa903aefea4e9273536ced5a289b8a805249df7759e33b80edc1fe3399f49f5e639f50af83b0705b12b3084e24c56579828b495e56cb3d818751f41a0dc793b9c662f109570a3cb2bfbadb48b7fc0e64c62da0cacd261e950fe20f70096c1790ed05de4578fd3ec83646f2da8d053b3434fe740e164c9772925cbea9b32b2883ccdbae6bcdb4e569fcf2b59d9badf9c95e2b12b00e2eaba0e6d1c9427473f62beb92830d0bce0228e7c6f86c37768a08c93cd64a43ebe032df7cadd57aff524705c2434a9c52794b25b58b272417847742798a3fce053400e84b9a02b94cb3adc7352e50fb8eb6d0c831c7f18dc05381ff6f307e81ebaf11c9972da53515b3c1c83c554e452a3c0e23b2c04dd1ecd360f0d659e72e15d7bc75f65f7c9c30401505df05283f84965ca16cf983c9e2fc6d2b194a006b92b148b956d7920d4769256b806f20c64859a524b2af6d47f3471b733f06083bfaf0aa328acd8767760161107a2fbf44d1b4da93e19632d8c092a03b452b8e355b47366f0ce44d07ce7dadd4c7e6596b79cd7f9af8d910dcb71c8b22b69cff629b1ef559679e72a622e971cd9fba274904f6be889a615d41ea230444db24391d4b15572c9acf2a8409aeebf15c72a4b0d9b39c5d52e82da799fa579d4fbe2bd20b71b701c29ef2b4f08b5fa414a0014b40377fa92153518fe091a130322775f73448fa39eedecbe80c6cd332cf74ee8432a12bd025a10d3410919625ecb95f8310ba557d3f14ed8e7731e6d9cacc8a7ffd7b265ece2077decba0a95246102add7a8ec4fef73a4762f5de8320803d385c96dfef62306999ac48565d15a32a919e02c0b13bfb58654d9edce1085cc8eca4bf11de91c89014c983648a290a6911e693c30d163b677e04ad3257b28687fba51ab45264bf925d994b3ec10ea50ac2055c2cbf7c3e31f541306457605f99d40437fee0cdf54264f9a6d6f34fa7e4cb6f00b3eb3efefccf91a0e16d8995f9c731d30a13e4a0c1fc1602d0d0832075470872aa3ce4a6d0a7c071fe99c0213d29878c23d05a4a0aac2cfc339aa010ec8c632511915331eac10c42cdd7812df8afb895160dc51b30f51d49cb9ae88904a4ca89af4606c86325f286e15804452938574a541743640597b4dc1444ca2d9401cc455ac2b1740c936e2039a9ea35741524a88d22a4459030f5e62c4916445865fd050594950cfc2d81f6195a6e054811561f4389049c54b9a63186890f79c1e4a43d4815883f5065617d32317186392c5c0ef64fff81848d45298fce425e202f012afacc46c59b8c85131f812cf46c2b1f872b613a5e5a0c02f10ff742e02cd29cba30f9f7b85c9a296c28a3b7084129be6a00dcb05c724b17d959cd56afaa04aff4f362946d822eb6c6bb59ef4c400cf04d981125991271554c4a124e30c4890be55a45d16e9673e3f30f5d51162103bccfe7b2778c76c63801939116da462bdfad6093ea196001e698202260739559ffd79ec2f767f679dc2b68daaff32b6e9fc142c8a8da1cb0097202e6655082bd71e4a470a734fdba57e1195158b12a099f59afab45790f795e6bf2c3900ee7165de261f6c1ecebb14f2f01d79ed01ad848a90151fdcd92558db54b653defff1b7b3430a42451ecbfabc43d8ad1fde3bc444056a055c2b3abae14a0c991a2e0b8f1a6168ff0fdf3efcced9b9209f11dccea5382434b7878c0a395ffdacdea0fed51628232ecc8bb134be464c6d02c8f91d6cb0ab3dda4e7cc29b07b5887ed9a2cd39f689081b9f61b3b34260b8239113db45912ce6d5d18c964cd3729902ffbafc3bc3f1846463b221af38b71981c0c575016bf6707b8fd32d91624fe4daadc136d11af2dbcd4e7dd6718c13f9a08dee831cf0f41f22f2cc3bfab49c1c5eee358d714356dfa7efe49c8d833f69e3adac4cf46c8393606f71165192cfe67809d45637bd223abbed8ca63fbd2923673f45363890322f624bc0897d5c56b33210a59f8a369ada30b8976ce00ba3d4837ab00cd65c568f5c0ee6d2907090136eeb70a5f1755e6816eee74d51d39e9fdc244b96ed45550127ff8d9d9e7deff2386733ace8062daeb3e028455854fc992e9198091c44693949356310c04f7df476c16fbdadfe03fb978dbdf11aab2d6e2079490e980c1487d1e751e7d8d892da11eb0b3e3bbdf5bf403dbc4c12784de1941530e86990c4538728a0668dfda43000a79fafc9093bb9446f39142805bdf19a3c2f41fb3326e853751a1684fff93b534437a76988e699a214191551422bf7959ac4e49b0acba0d9904c82c2a5a677c3283bd0c627cbf491180bf369ce894dd2477c26a071c1ebdc71922dd200020fa5d1aaf3c68f8d57b4ca9be90ff23b4e26730817c47a0a2493302f91a5a405bc23968dbd93b5e18269854d459889cb108eeac40b398c2d7cca85d705d6bd878033cca24151965185ae97268779a3be2b166a87c1c92d3e87904d93f5198b64558ec64fdbb4d81ca5b15161d618f4164129f5448ec8bc4a017db75beed4a807b8fb1e45a52429c4509f52a7a8beb524dd7e1c0efcd2ce1e7dbd5d25fffa91694c6b6267e36ed15aaf5137704f7ba26370b0580857c8b6cbd0bd3754e47c5770910cdc2511aa5066178bc75c7c579939f0018a82619aec8671b85517e04288128a5451a009bf9b00b480960e7fd0ce322d386753069574e1159417a677a74eb9f6d46719d598cbfb78da040c89b33062d441905f6ad711b580124637d8311525910a2f15da35b85cf16ee4b63ff35bd31952798fcb645c2c02059695892caf323282b70ba79405631480c886579e0c3e91cdc463b2d43081da8a3670b7c62320818a07b68df01b5d56038e162d3728bd3840534e8e8d60b360742d4e638bfc90a08f7ebbc02d0735db8ad259059e1b6035eb40885e4d4e67b9adacaa2da338fd1cfb0c4f0972ac70808c02802b8a58919d08aa0aab95b3730fc67e4a03b87ed1a8606f934a58e095d8a1f762dd226071792ae5e0052b27388736e9ee3a098d5eabed81325eb98b395573896b8661f5fbde9171981f04a1041da243ee960fbdee1e634dd3b5d6a8d907474301c3821149e2a9db4f2e878c22eed1f4d6d003c9623b591c71e196c4eb1efaa7d38b2fc2d256785ffa86047e3943e34a7e5109244eefc30a8de1c7d5503800d2c0db3c2585b73b61c8088e39cde54fc7165715cbdcbc4b004335ec5b319c93302fe809f1887cc319dc8e205898e45f39cd2902b3bcb41e2df9d518e6493c1a6a9e4667a331fab4ca605fd8c699d1782c085895572b1ed891f18c12c09d8f13962a8449f1718f14b529b79a309868f40bd4592d4326772c6e852793f0e28a92cda7918189d0ce7fd116943964778484e911e2067aa90108f14c2153031505a92a9efef80332fc4484f029068184894efd54c7f15c0c9331fdadda472e1ad8376a406c397d10e865196fdd308296706b428fc70aa6cd8cb2a090b8ffca6e3d7a75f153bad7cd1bc3e891d84086ec02731689e5de6ac2c2c3565f3bb16ed1230598e367a815fb282840ef4fb48fc3e1ce24fadd7392ad1b33edeb89ba5edc9438cdfb11d56c620225937221855fc6b94d18bb459fb2fb218a0667dbe4f8540e1809ca1989ac9113504c887e4bd36cf10988a83f2203ed25884c12405a1d0fd2ad7721e4163d26d254a247b602e0cb7525631376140a8af84133fbe5f57ae183bdd7e6a3192027375bf201e45e0a38c58ebfc83cec52af2386519a8a1c22873de1a9916636295bbc8fda5aae8016885a516359b019659f16c884fa26fb81182080d00360690c669863b66e61f936858050ef28d1c4853173ae1cd992bbd253912ce03ad8d3ed02c219f854c54e2f24a5ff4fc9c2a5d3fb2e9c4b90a2ffd6f5efc82a245297d89a2e19755a773847b9b65ace7b245de542aef594db563114e0079a4e89b8d7b844dcd11a1a5008e37280e222ff44bce9f2f3552c9d3ca76ec5546b8390e1556143b11419957765af818eb048e067884b94af53b66a5478551d3b1ea513fbf51f78dd10e308dc1662eb47012f3f6e4ca1085a7dc0491f19179a20ef4d4dbe7e9f9d0953c98659461410b76ce636631503ba55cb95d2d4a28ad04913863acae09bd42e0d0ab297d16503c5bdb4ef9de466eb52220c457c18356aa58f360406d059d7f1237e69124bc876f17cc37e4b1e668fc8814abcb5f204d3886ec519561c41c7ce89a94e04bc9c4fe16bb3828e5dd928ca2573fdf07aea351f2630e5747c8cc0493b0f4543f329ee64d4ce57fc24196d8c2f255e6d9c1212063861e1217d728dda92da6932dd49721387ad07b9ad03ff55c291bc1172ce7320eee7d39ae6d774c0ea1330e7fa38ef79d5aeb8d36c82c8af296afc9590fc6c2645e2c558763a5be1a750779666b0994d08a35170fa250ba54f18d2ebc0f4311b998fd1d15a348da10145cfc68e7f6db433900e1a3b5f1d996e4653665fb8ba79a18e44bcc3d83fdb52784b88cca80994743c856044708a47847c832040764c6795d64803c858361154336d8de2454bbc24a9c916dcdc89e2c0e0313cc0a640837b8326f8a376c403c2ad11abc4e22f7457cf7ebd8a421228e8f60d175069c6b4c84abf1cab17f212e44cba8e3c63802f7762ff5e954352363d1f54944b8fc123d3dde20da549c8e2d56d3784fea93c8eddc0791f553c993dc3b20ad088e20f3e469190d6cd01acf4f6fd5a8937b761c40f27f426c28f414a719d31e11ea8e6d8b1edc9d03a3e66287502a7e901a36f311492ec3313d6efe1e580b792dc5695a8f122ed142d4cf05849ae57107c28d4650bc480250b92a547ef497602763e14cdad20772bc7209f787439f53d9780ef48e6436f7e1017753552eb222e115f157f202f08eea1ff8d4e132b021d600e579ecec2551f44685a645500b3fb7d96f645a06be9344314f44738cb94dff929cacf7d51ef3fdb5b6cd8b3563d05faeb9a1f41e7a873fe622bcb9056041c98c47e61c52149f96dc9034ac99dadf404ee02dc0a9c1ea93d81862400f03d0fd0c61394748472f613a2faddb723a896c48a6039945a42a811bef4ccbd2795acf8d1aa64ae815b2b352025621c32963d9b4d3356a0e658559afb1c5caf84b646a826331e0e6ba699aa6c0ab5b1a10e622be199ad14282daad931c6b02db7b80edddcb11a616c3880b62a579987711d36f6aef6fc6493485d9e3b703591f6e8bc2e82b11e2ed002f4e8a51474491a494e0ba87e1e6e913fd12d89fcd82e65655eb39443550723928082261a3de1d8e067d6f8699663aa153676a1591854704bd94edd31278ee4dc524a73a28c12039342645579af50ab5691ba5f34f5ba8697118a4d9c5636926efe535701bff43331cfc25f6cba8728a348e10b714bef3104f93c429729b52daf6386101676c14a0013630998d617085104295222e3e26b6f5ecfbe6bc29d776accce90695b1919236c88d8e59c3c62c5da1049f54ad773bc69ca184542726cfbda6d790421dfacf75b4e3f85228306ab21b05ffb25ba1de79d6d915dd23c510ac4337c78fda5bd2faaa8926b89d612ff84545d8fbabfa08f2758616b6d796c4c3246a2fc2ba93b1732fe290c79b2fc4a64020ff3c49afeb3e75c763a4f69ae68a6c0282f967d62125e4463f9618a426512936cddfe1a14948c4b5b23c9c16354431cde981044975ee83ecaabb2a61de18933518e56ed7f54380c61d9594bb0884224b8229bb5b03858688998cfdc6c56b6e36fbfd86cb485f5ca60bb6c888de80f6247105849bcdd65ed21bbf195c6b2b3a4158e6d7499ce5141f0fb8e9908374d7f321cc6f38e142638a9852c2f5cc22f7f1202342cbb2921cb8721d10a44df8b22025b4525f914f9d7ce208e5e9bd993b41315c555f84200aafa3f10189dacae82fce368450ae3e103c1a98fbefeb0296ed27db2b429cee84ef6a9bb14ee895f0b916f8283824d08fb6823850958e6b73d25b05437ea94b23fd01223c9f1fbc82764f3523e52558c8b7ef95ee531f39a15c2a6f8999d0cf30e0c7abdceee2b603dac090d96963f03943f6d85becce3b470704af2feefde22162cbb892caa0882de665aeb3b958bcbfab09cca6f145840ad6dfc588d2d643b606c7b9a33c1ec52e4d195a390879c5abbfd3d4297e85f11c0532840d8c2398a0c7bab081bf6887c415aa28ee1fb24c743a9395eb8748886c0630c191166bc60c64113272244dacb917a9b04a1a76638776443dd27feab2e7b3c7b2066a7c4d1a83786f2696003253e463e653b2cc21e36b3311742f22977285127f9650d2af273198a3a09f5c4ef4587c8e8500bc22015940b566b4f859a07cc49aab416db2b25e9f8bc5df4db51d15a0b628f03bf82904783e492052efd0479cc4a504934c363b085faddd9502469333844ed51dd91e50636f25f35accae352e9bff265a4245e1d4a7132e9349bb00975571682707b1042082eb121eea2da0d2ac4032d4aa2db4603aa54157209a5a8e0b0283433aa5afdae38ef5486cb79ca34d5f62463a28e04c5e6586fe2a7240d27018eb4295581f8e9b4eccdd3921d14d41d67262e3c1aa0b08141e1279d96272f1a8dffc7847614bc194deab718744a984e1a5efd38b7b8242e152a9d0f3519e1c777955126a7a00be80f90ca550273d08603cc4bad30a36c59e545d22f3f5d00dd7fd964c2011a76168ca4e681c4ee0c1e0562498c13bd32ca92496268896cc3c8dea4f387934593a9bfce91948e78ce0c0058346f2c1b77b1c4f38283113e57495fa0825741a78b05726675468585b9c7010b42d1700b85f0d131f0f7e7b2ae8a4509c09b09df2ba531de55987d70bd5d31623bf8d77f9c532808bcc611f78b3ddda2e4a042f906061beb8b217aac230209a1f2e060c145e249cdcb3b2eb2d7fe8fafa12cbe630e5b2dcc60eb265164bc85aff27b1f875c3a5b7142b06742e349af1024085905d67b7550d6e775da89756a497894451d1c73af8edc4333b60e10c51b8968c30a5a0107718e1421b89df7dc270f73653a448be76e8725475bbee7b8bb58ced24329bf7afe78f681cfcb5faada2396981826de59327255546f9f47a92988ce988dfa227dbd0198fe96f0f7b776f8f532e3d534702346f73b780789c2b8868342099390e5f65b1981e5cc33ea255bd96deb1d079ec6a15296a5a20da47980f42e49bcc2fd48674f0f6c473f2e2eea749d042938cac486826af0ba4879f8a4a6cc92fb30b929e97f711c2710f3683f3d9c525fe0ed9a61a186294894087410296a94b61e0e85429edd007f3d88b24632cb6e3f7e8df8c5e27c72ff055b20601472f5727d6f0094937f5103008e932969c910d94a5ece242b735af0eae7e9baccb3a71adf072d25d2d88d54990142be4ecf91a8ce684b949dba0634f887ca7b71935ab169a10021b0524fe799850fdee4b75651bc6e8968d33971a0229e39fca932105ce49ec0198481652e5a16a123334c8f575460646593f2d4b71f50e1f445f1130a3700786f64c815ac01cf822f92083913c2a81e075eb7c0f0420b275b149a65912bc2d4b10afefa21aee309d702cb2b51715d54fce35ffacbcaa4d10cd43647dc7c506cf861aaa06cdd63522013460667b6acabf5f4d68ea8ddb668787dd01b55ed6545abe849c22c1abf3be2eda0dca8bdf260023ecda47e6f5fd508d6614a0bc07ce446b435415425f2b8c0277e553a7fadef744bf9e81a56c78a3191d8152c1e18a17b8e10b8a71db20413ff69c4664161c2691b171a694bacdeccac8d688992a793cc3000154a281682975f303a2f6d0c1eec89285853072f0c3f93423f0df6bea003c102b07f2ed0bd08c1945e193fc0b10057d74cb0062b0380a95940d4cffd97a1cd7fc694afecdb7d2fef3fa248b3000e740bd031bd98c99864eefc7da5f2b289fe4d555bf49f136fcb6a01127a85e5499ece29676c7978a777b5ec6fd13a0d6c2840f976820805547bd876efd072363d865c5580623cdcc969e95da7a287f727b7c3beaaf681730788c91d13eb3fbf565a6a7d42c15dd85273a8fbd3f819a036d4ec5e37cbd9def065d00be7d34ea70b00504093cdf01168c56f52c786ac9ea23a57952fca8aba27aa6f4e7baef016129edad125c9c967ee4f8ef3287cec25a0911a47386a2ea6c89058a780f71cc7de86ba982219c478c68ae5ddeff428dfb7a6a4320c36ab637bcbb01263dd0133e6d8acb7c153d569090b0355ae09666483c2ed0c4e4d7d63e29f70f40bbb8b5cb7f06df40124a32b8d335a8577880ed1e837dde0d010b00f4bcbfe20bebc2ccbeb4bbe7ea0f7326a4adf7a2648f3187ae24365e91c191f88eabdb1d868fd0076fb827551493ed4306c54c5e2f89f0d1ad6f5022fcbb0040b03d5ece3a3ead037ea8d2433bc928ea1d77b0b9b39c2cb1c355244b188e7d4cf4de5831c4a72fafc9e9f89ffd2a94f7e68c4d6a42eec19d02ffc65752568659751de5a81b592c57f8b47007f50fdd01c354d57f69955154b1ed00d39c6d464c7bbc13b2acb86198500d1f58edb82da5721e0fbf6781ea99ceb9c2206ac37f8a628aabd0ffd36c14d087c54a9b16fb8783214bf9f1101588bfbe99104ff0747dd7d4f3687e3e65ae5a7c6b3f0ba117ce38872d378a6af300e1335bed149b9e43c07d0d6cbe08c8e182212bdf88da265088f529cbd678bf6fc3d2e270cd0126124a140e5051c969ea4147070e220eeb912781de9615bbae9fa1dd043049c91afa75cfb3b20aa43130ebf9b1fa7d8c5436ec1320c5cdef60ca6fcc1a4a6baffc1c7c868211add81e63e0b6925c28cf143513796eef271885c2f5ce83a59740df059576529fffe3dc575d2a342469474aa7c3f32c732514ed7101f3f496556ef0765c7f068d14f2d90d74918707b3531ffc023ef9f3fb098f0d59fe745865b01e5d59c429fc58353ed2775341cd97f501ab80f9345e69165e4758a6b147c84e66698a645e976202f9728e164e324269c3a117e3e6c543f1fa66c71025ca7e78303aa832ab0ccfbf375b396288911ac2de21d0ab05a79536fd3f3567940d607463e18a793f87c550be0a335bc44b1c886c22acbd298afac8d737a5b1a30ab37e4b063d4d21daada28152c7afbc6bf9f8ab323c102e0b244ef189f4e934a30ace1ae5d4d5acda32ee9fe84c1fc9661b8d3333608fd3c4fdcdd05e697f90d27131c498c9e190374038bbcf504cc4f681470d4aaa3e58c9efe3b102a17c1df6bf5296f1d19d7f0bafba75b13c9cacda73ef854e3447e0281125f64673d43790751483be4a3e490708506905dc4c2fed074ff96b9cc311996149ebf7df31c2cef81f9840efc46e72b97b1358f3d4224439602a2d333d06df47af5bdfa80054649eb43e8d1c2f1512f51fb6d862bc69f6297a2c7859cb2ef366a47ba4fe2dba220d04271bc41e835a13d7e100152be16e3034014367bafed000066668fc023a701f68d0c2ee343de46c8142e1361c7ef2b8b20598f73838fb3a8f02614c029f919c8d35aea5175d2ed6bfb2dd256aa2b71739ca5cc5dbe59df0a9a07123cc908a00fea922d4c0e1e78d125cb3a6b65986015a428ae8483120ae019b106d5ee5b1c950014474d0e69a6368888383cd841282ca6d0ac1d96093e6c13f8825de136b333c432a2b491b3a2f427f8f3bad64c195b7535c156bfb3d5075a2aa5a2fce8ee39504901e9d39696b1195567e79b7a1a91af090301543664ec60b6b76b1d002c32eef821dce153a3d0ab4aea03eeefdd4206a781376a395c1597d3e6a1264ee58b719936fe339c4c57aa0a35125b072205c247bf61e1da9b3786427854c893309d2706c7b210b71d89d67687da7e0907540b7fde869853962ebaccca98fe0d2851e43b54bf2f42a404782fdaca31c29398bee519b08ef34861a25810e5994541a0468a924595e5f845bde8bfdaab81cc67e230b9d75a8d1bed62669f5027258b3681c5b93c5998909673725e478d2c7f83cd3505a33a02e9e1003348d2081a707ef197585814bba8c920f4351bd24327d04747dc67118415edc23e5da530f042491db8d6a0ecb82b87e9659ea5240348d1f5d85365b18b88815306b5b96430a18581c294fd6569193104a7503276bd52509dda5a7db7d676583e7659d38aa78a58052ac61644a48d2c6e075720369aeb3d6a4ae35def2e12e74a65d0ddc2eb3c030eafe03a6d736c0adc4684b9344267b8b61df9b200b14706ddc4f1ef057a6eeb0d9d0ac18b4ae558366572c12029824dba6bca497a2af6473f9a82f0ff813ef07c23f052ac2d76b7133befc9b659f2d5e04c1be47f4d8acb3ce5c84eb81c4a4a1528958468862dea7e8c555a6e24825021a6b4982bcc2c322edd1c9ee7be21eb7bf83fc5923b14d62ea17617b107355670f769803197e0823382b1924e24c4509ef4cb4f01139c1194ff50bd0017c4a451c2f0a54806477cd8e708f41cf3d0da5c7fa7c1e01e260485d1f8fdc47ebba92e9a487df58ce6e6ab5c816ade8f2405f2d2518b325f031d17df1a353a89801daab9e3bf4cd4d7a8960c49b20a4574577fc61e162f94b3ae16705532c1ceaaf615862951524ed189bf0a99f29c46a59e0fa36698f803987638c496abf9cebe69038fa53e85126e98e3fa9037e45e08d45fa473fe91b0a9accc7cb0c7951647903510ad037f82fe328766c122332c46748c208ed7434a882be380afb489cfa02aa8a48c954362b81db9c517666d634a4049ec472a4a53f151c6a67df2e8043a59f2530d9aaad1e3fde621d921769db4fc88872a8a452aa510b332d10df25d1567d7d18c814245367615b57faa1db87e425bb5680fed7d247fa512016ab54c03ec41dd31eb48cee89b7ccd3ac0d7d815e50b61d1f7c5f7e5649a94f5d2fb86fdc2918807b2f3d9177b3632656316b642521f1004c0d4ce6b208117d51477f25745539eb56df676f9ace2493dfe9f3cde7543f15f64927b1c7ac5c0c0a1d1632766afa17c13c5c65ce670499a3bcdfdc4fa5b1b1b3c8dddd359d9a51c902bd36f44b5bbfd518570439bb1a6b897636502830c2cc7a30993e9dd9c4674c934916e6ffdae50377ca71138b6659067d7646ad0e4d97ba66cbf8ef39ac292f2a3262f0050c8bef2e92f95aab23955dbaa558f1c401d73d7b2a003511377bba5a933698935481391f5b38770d4513d9878bcb81d0d26d69977fbda81f206816eec2bd86bd7490fe9240105b9f3143acd7f9cc029dc9b94502feee672dce6d1231a088f085113f4d15300ac10814eea3c658c3cf2e6a4c2edb185350fd05b574c6780baeedcf02368d9702724251a4a7603b06a2a992a59c4649d20b567d81fd5f667b928d31d76fa5aa6722aeede4afa8e487f885fea162e547698c3f29a9cab8fb641b6e3222a55e95f124929b63526cacdca2f55eac60c54469bb82429fca549c470d38a825dc77561a8ba61f0b26949382c4478e7a899f45bbe48d71ca5dc7111f393cdf355383f3ac8c864425f7accefe5c7c1cd59c77df9890103d43b7215da0d9277bfc34eddf72983c5fac7651e0661a87cafd3a2df19408dfe05d2679382d02eea73624fb22ade7a4580d45549f20bb957c4fd32731257a5c76aeafc81941c656169cfb43b6f9d28e353fed2ebc4fdf8bb163eb2662525cba89b45464e9c62d92b3a06a796007594dded2352d5043a9925fbd45643eeebc2db0fb06aa553709c131fc269020483972f7ebb75f2a787f3c4600a734eb35514ddd61f5eb01d9b52e67c4c3a20d982a86e014b74af0e91b078836bd0044ef511f1e11de462e0eece781f040be4aea39231d9a909a7b051b5c294363837c6946bc3c07f0eb9a266046788f323f7221b326501e62e1044e7985865b6b32aefb514f4e83bfb2f786390e561015f308cbed514576850f1b3893944787bd79c1bb74abc824fc6a7df04e70853c8269f62291abfa2f7f7c6b2c00bf98a49d4e6a3702009274feb8d6dae69ad4867f1b375b2e728e2196cf3d051d7ee92deac28f44125bbd8400fe390524cc453a0dc41167c34a61c16da1fc58c1a3d7011cf70bbc28a2594426a4b318d6b551adbbc45cb8abd901686d8eef7c5e5dd8e9a574372207a8b268dd22db4b49ae88d7843610b5998d1613b8b38376e16c8274ff1fe87a61514318d6ad7eaa1a2ba33ae578fd6295ee6005948b66e406479ebfe9b0fbf36c3b0b9db4a1d2adf232b76f7851eab71abf0caaca84366b10ff21aff76585ca272d24119c50c1107cdf35ed6e9c36f60bde593c16437e1620eedc70e67421906a2702ec138eeaed890bd2054d9c5f296611b443570bfb99b506734326186457077a5c4e3eda2cdbd7f3f5738685f8e37f7811459ddb6770e488744549b742d20c974200c8e522c96a9b2236ca9b9f71735ccb790abd46627828684263a1ac10b37eb2869d7996f39939a281f10ac724781425494041b0cd1272a9379967fe85fefc0746ea854f806687089badc1b1c850c805f20d523d0966a2bfe0c4dccfdfdc88ce00464b89a42d943718426f1448f2aad939d78d11e7dd284b4f9cce72337e3dc2c6c622128c6868f8b946884fca3cc3d1015383a8619e0c22c23988413bda433f7b7cadf132c3dc2eaaa8196ccbdac2b6eef8e0e5bb77121c4a32b08d76d05e0f0b756d561a5e66a56c28b212e269a7e378bbbc0a514690da57ecc93dcd26ebe6ce9e4d1ca00d89e9681853f8c0973008be73704e9990bb472059a0f570797a5990c8a4401590b739e575425424780600d6c2c1aaa42cf7beb7910517b5adc8046126e66e60556fa81d82c5faf6078e8301f55308ac7098e9f9d787304a021026c4fe26af0b8d2aa61ebd15ba8c27a8ad847920cfcf42836b38e8fb8dcccc6d9fc5df77e0726db388a7327a2b4bf4d2f81b26a4fafc7383970d4ef7b2ace91866618cc51e51966eee5723e219c2c8da621616d94a1b829f96695d3bcf1c5e701a0e7989ca284f4f8a0089a9cfe2bc98cc6d46d732a417da1f9f46721151dfaf96bee119ebcc355b9b899a15a58d8beab4c3cbdac6da3e124123a68284f4442d9e18381a92fb8d032d9dfbba7aac84ab327b057fa9e33e653d391c1b909629d410bead3e8db46bf7a10ff999b6adbcf9c8b2b411aba15633e7564c4c9843ea3c9d9b26277b49c65c701f14cc102fcda00a4c9fa3595bac1055490f8b1cfde75e1c44eb15fdddfee6ed20ca9cc66fed32165c22b864921c69e4430679f1acf4c25732582a9a3e3bc2ccc524670968a041e784356348b4272bd756b41003026155050a9adf39c81aaac633c2ae453706b7c214d98b34ae87d4c391d049d357e62bda59599db6a816c84393884961ec9db3470ab2bd99a2db2c81c350e6b19d5f8f44d1a507b7555acf83fe2b21f30f5168348b07fae23d50b77b3a3aa3120e9acf650aaf77c526974260c607c77f7801636ce2c2b3decd2c4f2a868c31494a3ae1a9b3eb0b80a56e231e0120d57f3803ee5e050ab86059d9e5a094f9126de892608ed3083b121d1c3cf4a2c113eb7da787d279d9c5087bee5e381cb7911d7547e75bd2a71d3229f7fae4f546023b420950acdd6dd900b9a5104b2673c2b7a69529eeece9a10a114e741fc4d5db5cacea392881f097b934399585e34b1cef20d05e8d992ede2848220a9c770e3f706576e09bfbd937d115d5435bdd6770a7be580df35477e48243c7d67da58c4b3f113e30eec4f995a36b58688b0c5397f76c3e11de8ffc22591ee4372568b076c77b8cb16918bf835489abab23a12653671bc2f30baaf4e75b74323ba72bb83020c7450b0e526f5ff2d46a563843471c5e0a20deb84e94aae718979cc7f80e2dcc6e0fe275066ac398f8bf9234ef31cfe0f7a7cbfc8a990231726a270eadfdde087b2c5ebb5e4767248849ab0e051ca1240c826a6536c1edc907aff2077c2d613da4d9c89a0c2e95ca2c58a238d94d485be6406c3fcfddb5978108aa3e392192ccdb4a17d3256340f7e9207cbbe293c200e9593d6c55aead3f54da81351ddead8bf6332c9f4944259fcdf2f313e897292d78cb405a1a66e3d8ac8ade847f35d3e5c0fb5d897ea07c25c391d9e42f6d69e591a836f380fa4f34b73e58610c42359e3f38246ead4ebd69de4275fa023b181ac60b6d976637653de4a81610d94bfa03e9ba25f9f99de6ea8fc5600c2e5f14a36f0b0f61d445dff6ef86ce9ca1b599c353373466fc47dc19be1fdc0beddea45a2797033e63251d4fa33868e630d992f78c339ef11fd92bca66b32479dbf92697b7e8669bde9ef2b39743da2a6a68f906cf27edc44026e5f8835ce71f46ad750f1e845eb90ff74c27c7e4283bbbe37682abb550ca8e0aeb2b70864b296720c3a29d05ca01c1b474942def66859419dccc37be3cb5fa2a5da0e238abdac9176ca3fb342259ecc10c8e294ac87ea12d0d614d15e603a438848e8386b39324e15b68e46fd1288a7ce261e4434b6f41aaf64de6914d1b444f59da7d5c8ca0febdf190e5616a57cffc42af2cecc654cca81efa251b5fd97090744069d4adcd36b67b346446c9c41883b9a1e4d7c90a4d705b343c31a4834001489cf9169419420d891ab0b4b1f78fcdc29952e85e074746698ba3fad9538d8d3febfe38a213a9538a323db9f43f9c0e7e2b9ac606d16977906ecded9b2579fe059cdc7c8bd706b19895db5dc1b1a9a9d76e1475e695b68ade8f2a0a4470253643d99ff64e0242a042f7134a0cede28b3773ace0d1886f055c128b1127bb72ff58472b3fbb357e6816ff0f8a637bea0685c239377c16432fd918980c8932760c08488b85635bd4ed8d07f3f7f2fc8345e326d811b2e7c43d7c06e692a4d16c5e5f62d979373b6f831f941b48ddb5e5a6f2c6a2ca9dda3ceeae70a9eadf7888d816ed8313c975fa948213781be7a9350af17607e902f7e6f944822322bb885c1e21a370a15128e903376711ae4d0f3a0a82c6d40765307fc2bc1fc2bde0ff4829952f22d618c9aa447e3552bc353aaec1272e5471d0de4484e135e416953a4a0b73483dce11472c3f3065a0d1479924f6f0939838c35afc6e2ac6e442a6c0b6c8fbdea829de695d26876856e50a87ee3c500818bed4505578d318e5e8a7eabb0fa37429995ba630738dd248c68ae96e15c9b91e9f7bd8409f28da82ca4fcec5371a56fd11c7346c2e158d3cc2a201fb3c9c1ff445994ec8307f0e04fd081b3800fd6804a57e97195bf5af69de73ed74da7d6d79f6b60fd5a7172c0a05da505c7d1a5d42ad0169f09e8f06d761559a5ad1aa0292144183f168b16edb23178fc37cd69549dd1f295eb3ac1717c4d7264c566d7b1f4cfcaeceac67e0a23af1f1d015d130e324933d0bfc555719726d6558d949082cf4c8a1a4d675a7d5de95bea64d058903d757baaf95cd003ee495b2cf8ccfae950b37cf92482695b338b34d673287ebe10954d0fd57e4dc2443f099b2ed66815dd979c72b1b1ebb8c0e906a5c6c1ecd4eb3f0551eaa83efd680e3a0d8e973fab72a37b3ba0036f0dc6226f49220badceac1df6accc5165754a15f864a5016f705ea3db451bf1039d623aa54c1676e7b5209ca53ad575827328daa51bcb316f55f1acd6f50e7e80888224a9b00cd5f1554f9c1d817475900402e429b4232e34d190d28e10c89ae2d8eca07253f37021bb8c4b180da854ae2bdade8c2d8c4a61295db236b15001a8905401acdb008c8faea2b7801b1d63909cc4d075f61ef1fec1eb4d071f0b2b360afa2cdef866377617d6d7c574622026a90d2bd6ff9bb14b54319cb14e24c0d788ab82aba96b563a953dbb465d8e3425b280c277711fc691bf61abd83b7896b543dbece055618fc9c95e1662c55821aa3990f33c2f2d77640e3f316d8f9ce258d14cebfd934a1f7abe51e3de00757c4c739c7a721b97388983b5af4c71bbf5e7d7a620c2397b5a27921ab57cc78c9bc543e9349aac14dc1f077fc50ffafcf2237fdc105a659c8bee5b3fae351dddb067bc6bee552eb36989f6d8e4dcb2730068e4dd8271f9ecd3f46f18731c2f47a9f6a059587d37766e84f2bb2c8c32375fb89f012e0127e7d2637c0618f77bd464da4742dbfd4bde65eda98385e41f7d9330da7af57250efb3424d790b1be60ff4c18c2422feb0c630d530b94bd6bb81f9239528569c6b542bb41aa86cd680aba1aa59618a11d460b32f81207b5a70d5ac5595bf19e9594d2bca4af85e4989fcbb175108a9ebf61075fa0d16cbe420f4b9f350bb141ada1499b4480c1cbbddea0708914932621186b4517970637aa40808914fad5727b6eb949a60f8ac3aebc18c66c3e623cca8ca0f51823633b488d4268ed01069ae70a2acf213c5a915f4b5170d01fecda19b8f1a0bfba2264dae0b97c394c473bf39db5be11c50c4aa77b0e7b2c2ee65cf96ac2f0502e455032a41d7b35c20cc59451dd40e978334902c07e15d18bce9d600a41b8dc742351a088416d3bed988622a8a590fb1c9899778bae1bb9523f340fad9c50946bf5df00e5fb86ed2fb1d79ceee810704d8b197c779dcd7f3e897a46bc9de7b6f29939401820a2a0b680ba39dcd72264751c4168f78c4e3cd36e3d9ac2c45d5be5a5e91887befbdf742f9a47ae2d5dd1d2f0fafb23cc053655003cf1663dbade96f2e97cbf5701110d46a9bcb1a6329fea8ba72ce244966f2e648922473cea5bacdb73fd37cac62759b6aa9beff3cc0389ff1d0ab1bf67a38cff9cf82e8d4ac09bdcd501dfff10715ca80d1a2ea4267ca553ec4a60e22c0e8ed250a3d38b9d2db7b62265f2d44354cb614a1b7e7c6578b9c6f30c14918bdaf3057dd709d71cc41af167924df7f5413a38a8c37400cd601c4abaf623e2ccfebf0e8e8bccedb2f314e84200eb8678af8207b9e1761e7653cea081e67c96e67e77754cfe9a4370b1f5dd447352e5bdbe34f5ffb46a6b4fd45c1d13b0bb5fd1519eabcb32beed09eb80cf0d5797788eebc1ba530ce4ee901cca12ffaa4306ec50b3e40fd7e18e762bd2f4e89408c83f100840fbbdc7a5f0c629cfde58259887126826033842fb7decf438c73d164ce7ac511fb1dea8baa5b14eafb1e605cbe8aa930f5de9cbea608a5ef93eabe3c7da7f4cebb5e9612de17873b8cbb543ea408d7fb9997aff0a81b7ba354ee8957764dfac284a7d357dce99b77220fab5bcce9fb56dd396b44afd8e547eb35708f0188ce9f3dcdef7abd699aafd49e0e82a8f32866cf3f74bfb6d4ec4777b52ffc03fa59db2a451081e136ab77b47542a404edd774cc73d9dace1758813892e4bfcda6a6c06633528a81739712c1608d4d166be328021147123416f6c40f8e913ca1a8b68ba5b25370e66f16ba25ed3867fedb6cb75b93b6b5214baa1498224769be8917a6331569fb576db98864675c27cd3dcbbec3719ccdb33c6b7cf11f1f21b9395f42b499dbd2a0f7f70c160bb2dac0d4367bc24cccb3806ee8fd4344a03af7ba76787c6836786c8f4e656ec33521b29e4b43a6bb3addc266448f0f4fcd6d41467c7c69ffce5292deafc9b2246db6db4d934a4f3f332a1d20f096660684028f8c00da1fabe9fd7a2ccfd9ecdfb3ec89590f4142b01a893742b4978cf6ab4d2501979080a00dc139b3b2f4908f2122d794bf4a413f888a5a0bf0593d0b1fd96614bd7995906ef8f4b4ce540e6b48505295a491f0a25a671ab515417e144b48d4ba88c1a0945a89b32a6bbb73903d41440923db294b7a7b02077adfa882f3f5c3e2a3396805a775a6daa5a1b2d9ec95b31d9d34991ff9d71ce759b8fdda657af4ac2b473deb2603bdef3a3aad33d9b47d97eda802a31235126afa365b10d0ecc7e6d313b3d9643c3b3a28ec75ba5a369bcd669636d236da3000c37987fde8cbffc5d9ab4691e12a9dc951ff20493351cbdb024a9224a96e5114c51ff7ed893727b4bfb22c33f918cf66a31dd5adb14d63704549410179b221c0f9c076606f4b355b8b30ae1fa82b76fdba4c964becd78ea5de2e70de2c2c6a9aa669fa2d4bd9ac242f49925634c0bdaa1d495d5ebfd7afdf8bfd71c62216b18865497889e636bfe52d97b71a7aeff5f975f5030bb46569b65caef3b4544bd65a186c8cc562622c63bf317bc2b608bab4274c1375c97c0984112daa01dd378dfa7dd97cd7f15d6f17ae1a3ecfd70b06b3c56228aaa36381b7a5246b6532ecd77cbd5ceb64989001cab205cc9e983535611ee7b9a27df486ca744e1ebfa58ecece0e0f8f4c166b71f371a1e55902e5ab647f6787874766976c4f8fcda7366bf1f3e2c0b5cc9eb03f4b87e855373ab4aa8232f5ebf1b84f2174e960a9e976cf9bc9a057454b7214b31016121212f26b61b0ed420b39605cc76fe96d02fd63544b7cc4d3f373ef7541a1fb14af92cc85f6c8d0598fcfab458b9fa0202122d0abde9692c4a1a1a13c34847d68e80e0d0d0d599fd90ce8d52228484888461b1a72594b44545484b6f8d29e989d404157c862d81e416757d0290b9ac972168ebe6919d4d57dea1bf432c0d54231daab057aefbdf7e27cba9a6343e918f4aafb242afaf4d542bde4bdd69e6cce7befbd379f188dd9576a04eaeace3ed0921c4b98b0ed420ff90cfd0ccd808686868686827a8686868686fc56005bb7b8accb854e15d4f6452a2d667124f348966669b65c2dd7f9b2f6b42f1bbb9d3c60b0286d3fe6d336456dba1d4a5b1eda7acf6df6b3b01c86e7b0b2b0aea6a05001a14c41f95d1ebd7be3f26edc9d6369fbf8aacabe58e5c1922fdd5cd71de39cc5eb18e72c8a6316c79124cbd2244bd36cb55caeb3e53acfd70b068bbdce0b3b6f2c765e546767e7b436dda5ede939ad8fcf697f2e0fcf7953d98b03db73efbdaa03dd6651f85cd6da6bd5105cd65dbe5de87c95ee936174de206bebb61707a22eed891ff8736e8e1e5f11d059b15ac6ae7695a7a721b89a0421b03ee8f2950052df1fba20f747612e97cbade62b0146faaa24b87f733084baf54a808fbe6a08363075b627ee6f806a2b667f952f9284ed1268d8eb057b89b0d7eb057bbd60b0d7e97ac15aafd7cb086645ec37db1809732b8c314e5d34a0f74590f52eb1fb6b03f1af3d614370e7e0bc4710da00d5f6adde595f545b9cd56c4f94e0d343617075676dad0838c0babc4af73930656e2b5ddc7b7ff474fea88702e24f30ae2e16021cf4832b1d0faa763a08960470b170dbcfde96786a2d7a6e4e4d1293382555d4257e0f3e5d964f6caf1d4ff15af1baccf3049241016af57c2bc6337bc166b0d7eb63feb222a27bb6fff76d3769d0b4a3ee9b8eba6deafe98ba6730f564e1ad1e9e54b6442aca7a91fd1511115d19a2b118b27e940361f401817ab108e2d6b4745bd2fbe658f0e83dc1889a8468d4581c9150280c23868cc50d093d37041c5a55d16e4ee8b76dc945f323fbb3bfeb130adf34f16eec6c6c098bed96644bd2dbfe9668c040876e43341a15a1a0df9f2414a45a3f1adee084b0935a9d24aab76587a95fd3dbfe92b0ae3e70db7d9f97cff758cd63754bbb2cedb541ebb17ac3b5619958b0389afa3bcfd38c99ae173fe67a20e82de18565c2d636c3e28454d2de5cef3a8dc08586e9e084ccd7514fb7394f67e7e07695f2a3ea2ef505ba6df782a925aadc763eff2d08c8fe6a5709bf6afde80ac7df49b7158a20e7627adfdc8541e717dd56e50156774d63d3344dd3344dd3344dd3344dd3344dd3344dd3344dd3344dd3344dd33481ccccf4af12c69836c318d3f4beb95a97600742412f1412120a83828442e10fdd57b86f6e687555f543fb0192a1970782089c4200da0ce88c347fa3321a8b5ad08ff9b39909125a788018637c060131ee5d24024e17a903dbb94e17f836dbce0f2c1d810b0dbb4012eba8db49984a7495f205c6d471867f40b7652ab7f9ce773fdfe1b840dff90e07f4590664fda8bcc059d3d2ed0748f6f3030444b423229aa157c99ca9d98fcccf663e3f9025d96ae80e1897fde8dca59d02427eda0c8a751cd0cce512cffbe2a0f5a6ba4f6dfeec344182037b947f93424cb0c7cf637140afc1799e27ece634205dfaf5d63a9556e9fc57ecf12b01e7dfa91f8d73b19dbf4e3a8f3e7ed24983abf481eab63c1505c550d43b85ad0dff2c25735883eb64c91fec5a271d810bed7a7f2100a373e66897fa57e987711aa8bb545de30ccfd40bb465fe0cc4b82bc447f9b31028547140f74d97cbbf999fffc4697995ccf26ca5fb837314bf27bd5a965ecd935e6daf2eafd649af46d3ab6357bfae765d8d0b72ad6adc4f596ad79ba9bd4aaed3f53332f6db8436496853032126f4774c9bbf79f48669f34feb74855729bf697e36c922262849aa33b7e5306ef6f9ff3ae13973749106fa98c5cd1ea3f9b1650a3624ebe8623d1a62f604e3844855841629c208e61c2a66a6d6727cf34d5504f34755a8837104db43efc85aba5080163b68b958cd54a803b374b1126d41ad5d5ba2280a75307eeb5baa08ad1fdfc54aa6421d90ef7a972a82ebc977b1ba5ce9dbbaa02d222ea875db5d6feab68d7ee0587635adabc0698d0623a7ef3b61c2335aa273ce6648ba81ee92e74c6165a28a1012a3127ece231cec8592038c95d5756764f458c9d466b959ef93322f60ad2e7c8ee99d21c3ca6a25533bc5c3eab2e29d2224861c2b99da2a2bab0be8144a7214c404907649c08ae1855584116640842bf923dc281beb98d904ad56114620b192aa751532c67554adabb043b4293ff0f0a3e5142958f9ad2ea2f3beb30933945a7194363ca6ae9ab748596ff08852182530298c4031ce4ee14dedd61f1b1ce3d0eb2482fb94a8a921b78ed0d2c25b830819c056e9fbb64adfed42498b93dceaaa9d29caa0d9615836644988f81c19c69040e91cf9015665e42956448d8b063409d68f949053490a5a46a2e848c5408fb08149f9503b92431092140025e9018604078f92283b455029393f48aee82819c28e0811fb622ac92166240518122433253fd1c80d4148c2c8928c8949f1701d910195521233920586232675065582024f122d4758801951b35384cca9444b7924064c490c3c4570bca2aa5c44a488437c98446028a3905c79153187cc907f6511dd6bc8d3586408932ce24389dc400e715312a10a1a624546adc94374d8611353521229614705344a86d71021446e9043c2b488d0500e5143f27002f18a6a131bb2a58cc2c15564ca8cfa220e319f642267cc21c056911bc42146ca23a2d06022534a677269fb9667fc8d39d1850e7c0b145ec1fc0a7d8d3bd30a49e0ce7de295ff6e6e04febcf713e21f292c7322b0dc61e028bcbd7aa2effa7a39bc23af7aa38ffc9dd8777b18e739581a35d7dd1d632b7ded8a6b87c0d06edd88cb45037afdfa891ddb13f9955f1b5c7b42c422c61ca05af5142ec7802ae37b7189dfbf5ca2dded7db57035ae98b8cbd93d97270f8d71b62bec5ffb6aa18a3bb75d372fcc7092830d14e4a870040d2366085170e2f3f2a2088aa9ad131f15a9ad4a16c04542dd20d0245cea5b67cad61fe7b2442db557e9df3a531ecb53e69fa2b20a8c52ebb9f5d6998ede3f8f65eb9cbd7f11ed8f6cd67339f4d6994cbc7f1ecbd609436b49fe44b624134847b60a8c4ad4544f4fbc7f1ecbd609437764feb7267fda52d389241348d67379a2416f9d49e9fdf358b64e18ba23f3997d40d3fe2c06e02fa4c10094509c4832817464abc0a8444df534c5fbe7b16c9d307447e6330ba2114001fe400750408a9b128a1349d67379a2b2d9cf5b67bae0fdf358b64e18ba23f39905d188bea6fd3b9880ffec0113b8c0821437251427ace7f24465331af9fe792c5b270cdd91f9cc826844afd63cd80012d4df07031b50b1748105296e4a28ace7f2446533da7b2ac3a13891c4644205d2d2d105360b2a4861742ba154fb0004ff1e1080806352b1748105296e4ad67379a2b219ed6b9e8e00504271a2290967820949c5d192ed820a2c304a51e2567bf70f21e7cff3f6c58108390034e198542c5d60418a9bf55c9ea86c46fb9acd539db7cee4f4fe792c5b270cdd91f9cc826844afd68c6c4849286e4b3068f8efe86838ad0040138e49c5d20516a4b829a138916402e9c8568151899aea2906ef9fc7b275c2d01d99cf2c8846f46acdc8869484e266c152d38e7704f5476ff03060e1b402004d3826154b1758603d97272a9bd1be664bbabd4e4f6101b020c52d03250c50b038e1f4feb3201ad1ab35231b52128a9b054b4c4d1fb078e2e11febf108400618b0705a0180261c938aa50bace7f2446533dad76c49b7254fb3bec082141adc02a094010a0c4eb0487232b1020900474d365c054c462a4a2cd546289fff0b884f6b10800c3060e1b402004d3826154bd67379a2b219ed6bb6a4db5253ecf4d4f5d69904f0fef93a2dbdff789d2eb0e0fd5bd7694df1feb0eba4df1fbd4eb7f7dfb94e1abcbfec3a29bdbfcf750ac0fbcfae138af70fba4e19bc3fed3a9d787fa2eb84c1fbff754a7a7ff53ab178ffda7532f1fe46d7c909e9fd91aed38af74fba4e47ef8fe23a01e0fd6fd7c9f6fe165ca7a6f75fba4e15bc3fd375c2bd7fd375327aff15d789e9fd595ca712ef9fc17552f1fe1a5ca7da8d4aef12ff534abd4b0490aea3f6d72dedaf410076b47f063eda1f8320edcf8248fb3ba9da7f8591f6070092f66f42a1fd7116687f2626edaf6285f65f4aed55f2bf20f5abe46f419aaf927f8ab4bc4afeb7542945af923f8a547695fc4fa4b3abe49f94d2ae92bf89f4af923f525abb4afe47a9ed2af9dbd2a4abe45f417abb4afe46e9d255f22f91365d25ff5acae22ab92a3b5d35a0a84b1cc9d26cb9ce172c86eee8c048a46a9aa69f16a57b3694d2d27ddb2cb685324bb7a5d2f87ff0fba43de9b6561abf2cdd164be3e749b7cdd21886c6af83a64f1a3f0c6adf287d97e09fdd08e04108bbbca22d75bfcd547793fd95eab6401b24d551dd37a713d57d79bd7d7dfa264db96c4d6d01d33ee9e64c58085ffc5e8e64cee5488ee398cb91ccb91cc9711c733992399723398e63bee907a6fdb1d0f8bd1cc99ccb911cc731833042f958942399733992e33866164f3c9abc1cc99ccb911cc7319b4be548e65c8ee4388e7989c6cdcb91ccb91cc9711c733992399723398e632e4732e77224c771ccb9242f4732e77224c771cc495658362f4732e77224c771cc36e1b0e6e548e65c8ee4388ef9831a5597c7efe548e65c8ee4388e190471d3b4f8ad3197188fb9cc39e3990c3d73ce2a56dd84b2815908208040769b357d200b41e30741e37224cb911cc7717c274b932ccdf2cb59a98670ce40c80fc2b945d025ced915444404add1de769d32b84afe654a4bbf56c403dd1968ff3183eb24aaf62a617c3aceaefe77c70cbbe5c80b296d56909a636415835a7c418b28646449a2a6078f5f142d2ce435566690a10d0d64cc647943837421898ec8550e3288b182e54d8c34bb29554b56903ac2062a3964065458628236f0680343106c4852acc22128670d9a16c0007145da1e80c8b5c0438c1150bd2b6598aaccf074668c0fbb1dc0aa23533c64612204101c575686c099a0468e0d4286e0f0429520e922852e6f72ac70028c2c6ca80021ba817155839c1ad6b440c39429a2288aa228ea40040a54d0e880e605333ff0f0b57ff1dc411591c6461b2162d6ec83ce3f9d73cea2288aa298a489912b2798b11292c47b70930437ae3dda3a91e1041e222a09ea0cabd4cc8bac2b1f328490d19a9261c6913145864f8609478669848c2732701647b2345baef3a5d3e3e136f7b9ed46b9cddff358b64e186a6f94e3eeefb704ca71d7e7f33d39eef67a3c768ebbbbdd85e1b89ba3a1ed87f081e33cf0c0710ac047fe58f6f6c78ce64ce58fafd91f7f5b6a72a61fcfc2995eb01f4f00678aa16f5f01416fbf0367d279fb0f70a69db73f0167faa1fb55e8eda7bdfbb350eebde5abc53d82061f0823a986d76331a66f72ec9a44dd8da5ace6885cddb09c1ec2244fe1a0e913ba2ff076f9a1fb95f63cef7fa93c03dafb5fa033f1d0785a62d07327fd21f4463a219d212a4b7f04bd912ec8e58a996a501ad01be9805e56d0da8fd91be966af970b0834c9e636fff1f3635241976e6ef31f3e3e96c4425934b9cd7ff47c8f88033a801fb237d25d2a3fbac015d003b8cd9f006ef3d791c1c49334594fd2d478216218f2bc6972c51d6d74c4da0059a2053646caeef024cac60ebc2657a6b27298593314b9002f851d478ce88218712955799883a5aa81f4818d2faa284bef4a8e109ff9ea4adb78a52e9468809ff7461092bd4c7ace7a2e3792427755038d7ff7272c93b8acb6e697ba9de941e9364bbb1dce824d5ac4b3198afa85d2fed3e2af258e20cce5ac508b2e7af6878022fc177b36e3b2d862fb98fc22f79114c7573dc0aba0f6efd9619cb6e12a3706c75005366b0ebd7b7827d8a1c995188c88a143c6ba7b7a9aa7770f54d2950a5474594a7858adbbc7e782de3d52da7ff7fcb4ffee813246e9a93146e9b1d193ebd169efa1318535052728130687c12bed2f83c2381731c24cb901cacb0f4684ac2de82df315210306166c58a2a6c6ba65521ae3d05bf62b929542929e1aa08e75cb6ee82d036aff2d0b6adfb22ea30e598f5187ec878c27eb01e92de331f66434c69eec862fcb27cb6d994efbf3fc30cec593951d67726489e184322b0b7af34ccd30860a18149e788962c3ba79807a05bd7982082032e7c8092f549841c8ba7954d09b67a8fd37cf150fd61a1e266b789af0f878a4b4f388593c6216cfa8e319753ca38e67278871299c12b85061d31b23c43bbd77d4e40893664dcec81632ebde196a1b7aef5c4d400629147ce4baec08b2ee9d2c9d82de3c39edbf7974da7ff3d0c0b28305cb8e969da91da0f61daae0922094e18e6fef48697f9d2b8c83400f5a9854ada9f16bb2e21a7aeb6c29f20688304f65daf858f74e8e86de3b3a0708717246386307189ed6bdb34341ef1d5e8f87f6df3b503b40c4203a66c4203a6874843a43ed3a56a24f87cad7e5a7f3fbe94cfd80da5f478771568e9424249ce0c3148d124ed05b6767c20c99253f689022e58675ebf0b4097aebf4703cc9b98a01c4c90d32ebd681d23abd757cda7feb4869df3a4b70e8c4c0a133c30a0b8e761d18220f0d8a3c5428f286da1fed61dc0a83eac96e871c1d7a2b9ea1378ac5081ea66ea4a0838da875a33e2d436f54aa05c41597286a8850b265dde82f86dee894f6df2850a354620c54871803dd81c24069a037b4a34fa22e962585712e7ce86cc8b041851f479cb0c2d03bf6fb5923430a1141cc21d364ddb1a9177ac7807b8ac70b7290a430049a75c782ba04bd63c2a195f6dfb1ab18969758112fb12331a8984f7b2c4a187b12c6785cc57457b1dd558ca7fd61408c73b16308d50e47600189a8c124e80d0bc2b0a3d2a1431703140feb8609714e6fd8b00a09951ea8c84c91938275c3e6e80ddbb1188c22302a456056603fd89476d814295894146cc914ac37058382f9b4ff6b88712e9a0451c20e2345a2b4e890a3f7ebca043a60b901072f0b971fd6fdcad271f48689b942058798a7184480ac1ba683a3376c7783de301eec29d7788dc9355e645ec19750fbab2aef5e535e54b9f7927afdf26b4afbbf7218775550e2e4ca9ba7ac39eb1bbd5f3aedbc145e2c01e2451160d6fddab9d1fbc5539f9cb440c3191e5e08b1ee571bbd5f50da7fbf7cafa83171c6ccb9cad27ec2199e55c3d32aeb04669dc12ca1f63f7918e7e2871b334222acbc59e188354befb3e7019829210e2a1c5a0062dd27141bbd4f1f1a1678109a70264c0deb3ea552a0f7f9d3fefb9c3aa75c39715cd1f7f7e98276c13973a7eedca17a9f37a8aca8b0b4bfcb87712e4890423363481318ac80ac6bf476b93012e3c91a2d504d8cd0b06e1796de2e2d63866a5648014c8c34eb76a9f1df2e218e5cb7ab0a337105c14c5c425c375c4f2e1ea7de2e20f8e782817f3480c2ab56166e67dd2d3818472b0ac6d192d2926afdb4b7a0605e0b082f4a3b143ec2ad1e6e0931ce850bc2334d8218e105051fd623f4368746dc049126cb1a1cb0acdbbc4aa3b79955a5cc0c573c20094324c8ba5b577ab774da7fb776ad1b6ec6f4e266cc2f26d00c6a37bb5c9950fc6a8af6377da6d4367fdabfccc2383bf461f93043e3c98696158dde66ce4e09218ca1460c9b9c6fdda6ee8cdee64e7b0d35783e72e0d2244a93759b66f4367bda7f9b5026102c6bb0b029bb945556da4bac6039152c81c1a0f62f7718575af1f144881833b08258adf42e79598260434557e5f3a1cabacb9e117a9766e01c41454610389cf1adbb2ca37729a5fd77f92ba1ec50d6d8a1b48105a78421a47749038a1fdddf6495762856be1f090ca33719d4be6eb28bf7207b780ff2074983bc413e69ff4df280ca8222739bd44d614084114eb0449d6075c2ba47a02e42ef3138d4fe7bbc1ab1e28c4ce28c4d46df28a57d5ce2b991470e88f61f77236f8f3ded20303324492e8735715058877a8b46ac80c30b3324e400c4c8bac52119bdc5abd91b2961649409734388758b5944f81e75238d312296315aca758b4bb60845bb3845bb48a57d888f7cc39ca5137940df220f266698a0c9c26190ded9ea97a97eb90b30ff80790a0804e30608e0902668b6ac3b8fd13bf77696dad13b2fc19163e0d0f7779e919573da330ced3c6cc5c382c241ed58e842071d32f42893c6cc0b1e6b95de182abd2108205d789070e4ccbab14f8cde58ca49ae4c0e090d412552d68d7f61f4c6534636a6ba31b08e1b43dfdf7887c630b4bfdd58877798a73dcba530ce5a098961aa87353bf0c06405a3b7ff4a807983c310bb304760acdba7844ec5820b319c19daa05ab707f517bd5d38b4f22b526fc7a28017b15e8e385494f627ed3c78dab7efb4ebac930aab44458595a628ed4d4b44de0a59b2f818a690c33a84de3738ccf2b11dc63a45dffb76bfc868ff0c58a79755baef7a5f9e178d0229283e2938d82027c6baafae8bdef60a1643460e34360c093659fd27e038bb1ea00967ed32bee48045b5054cf0caea5cb48b1e5747f410e64a162c6e56dfe22287325582f0e2e480e2b2ba104ca3f657cda04a24a0dbb5d3f751c8a961a1bb35d4f76b6ad0ddcad2f753d480a68042770ba8efdbd8a0bb95d3f76d2940776ba7efa76801dd6650df4f8103dde650df375102bacd9fbe7f4402ba4d287d7f48886e73a7ef0f59a1bbccd2f7915280ee52a8ef979881ee724adf47ba4277e9d377ca8f9d26c97ff26d78467326176bf9b1578b12f66a41061581ee92a7efa78882ee32a7ef0f55a19b1ceafb29a2d04d02f57da439e826a5f47d5a0d74933d7df7e9e587274368fc65128d1ffd010a0f1affe0638ac6ff7ab5c0363d3e3a468fe3f8b671fcd94def53e3a72e1a0ba171151d4534fef3d502a3e497d6f56a41b65e2dc65a10748f57fabead0cbac7297ddf5604bac7a0be1f1485eeb1a7ef071141955a40b798a5ef2bdd40f7a8d3f76f57e81683c320314080e95242e3378fc8a05bf4e9fb4744a05bfce9fb331e505115a360d7cf99ac0f5576686a1a7f8904447716eafb4856d0211ee8ce52fafe100de8ce53fa7e900fb407ba734fdfb7c540371eeafb3618e8c6595cac74c9014963268bc5063d8a484ed08d79fa3ed20fdd184adf27c105dd7ea5ef93d8826e9cd3f7512041b707f5fda127e876287dffc604dd9ed3f76f4bd0ed3b7dbf66028aee40d118d05b18f40606dd174ab7d1f965355bfe19cd715e459bd0a2c50dd5e871a3975bf197166757ecaf16f9ea30ced5c347f7afab175b836e574fdfdf2e297ddf25e54c4bee153940b4aa3176413953ef1607fd1b1cf49c839e72d05b0e559a53414ebb801f887ad1f04ddd6c7fb35d12bc84e05530887759474fca27ba807f418df1efd734a6000492324aa4172ef389e7836a1da1711db9190683c39385f34a9779af1a3cdf0d980a31a41be80b2fddd8d35d9dce0ea89fd4ce0952c026cc830210283bfa017b6640fd6cef836403eae7fa39aa22059cd2f85d3318b2c65d561610520b407138250886d0110f3cdf8c7644e51ce686743d2917518c17701c669ec6ef2ae205af82c1cf21ccbaaa0a12509b2299707df7ef94bef7fefd571715bfc8c3b8b32783a2f944298d4f9f2328fea339b953f70aba48e47a525f3b7d8ea04838501b44189fb67ff29c69bf867a1c6afcafa133ed5750e3c7ffea624bfcbac272b97ea0d773ceee6f67492da0a3deafed1654e405357ea0be39bbc5476e94138b9871a497039a4204911c5388209263bd249c0c7d58302aa35404295ea1a0a4c93ac2cf708591cacd3ac2d0d43acef4c5b81437640cd792cc3e1f737aeb98fa151dab08b74910619aac23e42b7156f2c754040b0497758413a8be0a78c010400d08e005bda54df8c85fa90b7a2353d79530c4d43aa6970417566fc1841aa0d631bd466eac2eb2cc5ea60c99e03aa6178d95d5d53aed7d71c297e03a065946310621da6431020607197284b09a01e6e39980cc1a3725d000d38209eb189462441c538a958ca7f2c90c2d4f66fc744cdb273372c0d222813241a498e111216c83b3a0985a41890e5e7468c30563f91c3e6e70128304333a48cd9a4c692b84b64e74b0d2285902d15dea7bee406966d03dbb679660e90cb475b2244a0b39404784116e1042aab7eabc75287f2dc0ae3a3a2aea630b2eff5d9b31c619bf288ee2288ea288313e4770b5891ca8433910ddbbcd86d51a0d74a3da51d47fe39af147f2fdfe78bd063eaba1b85aabd63f805d61daa3fb68e9692c15f277fd5d61a990fff9792553215737c01abfeb99945bd9d2b60a9b307aa33717ba65a6dba64d9254b76961697eb568fd7d6d805be29b1fd8f4f84ebe7d3939fa4b5df2a352bf44f556ea2012d40cb537dd520dec89fb4126a0af77e9d2b64a1a2edad5b2e5a3ce95b3bbbb6aff031f82c06daeba75b76e1de79cf39679ead302ea3f8b01bb19c9b224c72360a46024c9f1861646b2345b2db32447373c18e2a3214772446a73de7b84d5187224c714aecfaac84896a65992a38e23ac46921c7f7073a507940c7224c7204fba287132a6857194338e24902339c6c87ee443e6c3921eb21c6a0e21d345a920eb61c90e16a8e43568016a1af4be5f6bcb6ff3e3fb982471b624eaf9b105f47d2c922ef4bd591473a431ce30ac8abba8968b3abb02bffd8b44dfb7d7ae660e9e7b6d30a4af6afaec097f211d39b727448c028d5da38f26c8246f07a4332d4ecce1231773624ecca91520dfe5d75e0ccbea25f36adf3566ecb04cbe38b062f4f8fedae0c82c4bcfb0b195fbe112eae1abd376e31745f1e6b2548f30bea9601d70577fd77b04fdc3d511ae63ac273213d0d7151ca83975062d7f3a5c64317ec860a0798d4e148a8fd8510115bfc84e40c2e31123532303fa06e5624386c30bb21725047770f4d8a0838605ab305b60d8c981eee830859646748aa0a490aa730679a3e7c6a07ed436385c799566e0ace2766bd5da647189d45b0c92297010472d1700816e83339236b500a780812c53e017a54906bd7131421c4a63b6b42801812d24362d170a9bcba0322eff62b1de62d0bba43072be92c4da90170c8d8961cc76061643fab7d96e370f3a12438f94624075e6d8e0ec689cc3d2d92153411517ed2f1e0d7778d818fd8c0a4f09a1f6ac149388231383b591851185f185f866a74667c59036dbedd6d4342251f5a438d221f165b431ce10e7b0415392c9123d2bfb10945da16afb60c4a03d1164c3cfec4b1a0cb66c26cc807e44120254f4454c172345091222831291f8820ae5d36d2874a00d896866b42128434442312a444552f4f62cfb3ec7e244213e22fec642c6dff807f91bef10832e5cd9d02e0e6790627076d37b36f2b4bf18c447a80c62b0e8bf0005833ac33fc539b64f6740a88e18b447fed6bec8c5e23c4b7c3353831a518a6232e6a5ea69456f3168c52fda4b40ed0a9cb6ef4ad5f48b88d2215a2a940601a5e92c4d7f529f9e589a653c3b3a689aa6a959a6e428a6d8d3f4620026065f62306857d89e5c8a125caa1bf4be7525dd0e3537bf5248004a5b1c0c523cb1c8473054e4a1232ec848591694414628a5389254234254daae86ca202920a51db3f2b6e6271f15e133abd223877691b0c8f2500d3c4137ec5cdb093a483e3423f1e0ca24842d51c49edd1f7b391485ccb72a233a435011b9cc81de77ecb717e8fdf2838066294cc8003edafff5e680765546e5321163a603ba73920d7497474a486441773e8f4077a96d76858bd65aebd65a6bef0e97084028b5ff7d970c7497362b025283506aa10db0f7b02730072f6d55e725a580a23c06f85ae2233f4a82ee8c2e41793ce73953fe5d6a9d97d3582d5106e5c0753e9d9c2327a7b17aa6417799452cfab50f739d8b0861525b274b725e4a1b9aaf462bc8691790e90336dd81fc52b40b138430def8c008d1c604cc83ce34bb226b4bb59b11040e14354ad8ace33fe07afed1f5cbaa4bcab5440574bba4fc97b46dd2d655c4288eab88d11ca32a232b232ced4670a48c8052464129a1f637e2619cad92821738526478f283308ade463d3447a4a4c42899a2c2ba8da03414bd8d7cb92d4dd00c0922c30c69d66df4446fa39ff6df465346535c358c70b86a18b960a433da6937bae1dac171ed8c60b87ac32b57af4496f62fe1c338dbe5ca8b255a803f6a20013bd1bb846fcb0b6aac54ad80e5c5ba4beca07789a906ecc0c342048b1a2266d65de2a77789a0f6df258425aac6940832a6849012bd1250da4b001996b8312cf19455229755425762a7fd6b53403cb97253f5a3a189de3520961a2635048181cc9a75d7825a07bd6b3f64958172041161e204d75d63a277ed4afbef5a560d4e8b492d4a8b494d4a4d4afb4f7b0dcaaf06e4578b02d678c05aaf06a5fd490831ce0e212c22e4c7952531aad6257a9310ea40f5420c45b2f0725837892bad446f12595b786fa8a81932c5c2ba6b49f4aee9b4ffaeed6a375a3590f0d2aa81c4171250484c2141a59d440b088916107d7f9358a2fdd52c8cfbc091210c2266b06145b722d19b448ec44efb1b2b53c670598363ceba49f0b494de2436091240e2ac89c366788581de2a564eed92ab6aedd4a9d64e05b6766a50fbab3b8ccb808d174a644cd912c411bd552f47544172c6061abe2feb567bda88deaa7d93a5431539327a40a265dd6a11bd5529edbfd59f0ac5546b98aa0d35a7eab4ab3482f8c80cc219a6c2e17078a5fd53288c5b81c29cac5f112cdcc0244aefd40160825015c11a62820aeb4e89e89dfe56189cd4f025d7c40a9b75a743f44e81da7fa7c1b40b93b40793f4474a23bd913e694f79fcb27e692ed569ffff61dc8f1416568842254b9b22d61cf4fea967c1871edcbce0bde9d1c3badfa7f70fb5fffe2b2cb3c733317b7c93e7f1403e4afb2f31a1de847a13ea8b8218b7424e1a1b457038a1861d84e85de4e2c906126cb0d1029c15d0acbb0807bd8b461e0a37b8bc0903a565dd4541f4fe9cf6dfaf7b1a668d222c668d222d454b8aa0144d59d2bb88cadc154199bb22df2e9222cad215f1b4ff2eea15f15863660d1a222aa22e4455da89acb2887e5944535940ed4fa4c3380f50cda0d21d412487901588de443b385e6008b243814c967513f16ed09ba877e288950ebc34bd1a4dd64d046583de44be357a1349112df14214c30bd10c2b2c38da8960088782c221a170a8fd877a1867bd649541814b121276bfb506bd87a04a6832868d9a2f5f86f4d63d04a5f7909406e4d028218a90c90b22eb1efaa1f7d094f6df43c021aa28433aa20ced1882314463e8460abd879ea6685753b4aca9a19cf6a749619c15c286609037737a6d7ef0a137ed07d42ed4fe9b36d4fe9b665506a1152983d08ed09e683c6840b4d3a24a9aaea4ed4a1a4ffb0b0131ce6a6943c29c345cda38a9d1436fa1a0d05028ab4a6f5a8e06a3c42144a5c46145bb50949094d04f684abbd0149e508f2704b5857cda3f68887176c8c20c537c6082a5872f2b0fbd83ae2eb0f1338302950d6194ac3b284bd3a0b7508ec6973342b0f060268799750be9f40c7a0bedb4ff16e2093dc5091a1327884cd09420aaa02e28f40eaa2a83a4caa05f1934a5e3054169ff1de40b8a1a1367cc1ca02a2b2ced40708640c021507028d4fe403c8cb3728ee8a2be544163e58d0c7a03f5807cda898021851d622013e7c6ba81a47a7a03fd78d01b684a3bd0142a4038a800b900070806108d24bd816e0087c02b6096f69ff9300e045382d83862640b112ff00ebd6743ca1cd126852e3964e1b1ee198e41ef599a2a3162868849018fc6ba6740ad43ef5950fbef997056556416a4c84cc8ecc6ec69c6c384de332052b39cd44c27b5d3fe3f531897fef0222589148f2b403ce9fd0314fe5c69fffd93f50387d4f11385d4f123e507c84fd4cf12ed3f50c81f1ef9d3237fa0b4bf8f10e32c1a1c6cc0700299aba71d5618f4f6198e09e24b95135e8f20d4ba7f5ed0fb47a7fdf7cf4efbef9f1ba4093e5e48137cbef84099b27d80c0ed4375a4b74f1792864f144943dfdf3e4b723b9f9ef6df3e50dad7ed03c4cc1a336c744f971ee1f04a7b0fd655cf549e6ba854891bb06a1e430a8dd00c30000800d317002020180c0a265194c33018a8f80114800f57d4466062349587032946410619639001041000000084101828a2a1028567d1b226b4bd4aa59c74df112fe9e745961ab089a06531c51865e1868367715b8b3ee44983ebbca863a5773581d46121ca4663cba6cd4e0343c4d04673c1d5e7ac7d4ca5d49a8a52d86c639fed8a8b53ad8d484356b2f15da32175023c5fc1a1a3406aae947f48ed9b5b460b9a4d5bd7db88c6be3fe1c1ca7c60d8bfd9407f0c2509760f9f687fa5869b0978248ac902e205ce24157cc869dcdcc279362e2dc234c9d64ad65cf3bacc921ded1c2908afd2b8a90c0ced1d3b51b5ac94de5dd89131936765151e21bab3a28e5847cd3639f6d321ab887fbac5a7489530ee2052d3f73d3ec92aa346609b5bd42c5c733a10bdc4d189ee47eb5f105567229052bccd95dade9f3c9a3cb901f0de6ddfba5f1b3c0ba5a64046cd269ceee38df62d9d5759420046eb4a22e92f545c77935e85aa3873e962a4d61790ac017f8d8041b62b4c1d6d07a096110d7641b481ab4530c9240d7a106ca74b45111886559b373f21036996b03a3eface203126c1929a0290dc1cdd73498cbc29e224310f46d68cd41753b372cd27ea7672120712ca92066b3f3e342fe9cb3fe3979619c98399fa90d8cac953500bddfff3782a7673ef857c703a275484638e631d55bc5c8683ea81e3b2397812174e72ef64762d26bbac995189a33a50b9f1aba67050ffd359bbfca6bde5373d90dc96d312d0342c4dfa498d22973c4a2ad04b56fb8dc30c150b00998a885acc67c2b0bd4ace29bdec4c75e3484c2a5e642a47e31245a87e440544a057268c2353fb3372892516cd5782e6e25da7d3a25c45556c9e0f4ff21582cbb96192aeae59c05cb5e01c51f615802a46339bc463b0b84f264f2ecf6a93c4f09f00f63ffcb14f0420059e02a411e59cf116b0bc95778e49f28201595f96aac13a961b84ecbdc97228473f85a22a9d7c598e510a0c265ebc3b94131afb280b09b54470987f43b6f1e71c6bee53941322254474f2d010c398198da6561582dc7411df68d6599d2fadfa528ffdff6f9c95f985ee388ba1c500249e55d6b2cacafc1fddff87e4a4f43ba903975ad434713bb177a999b640a92aafd452a6bf4cb9d0538af63b4aad4f25d336a5789b5002998203eae2f7e737c5aef2444cafc3a383dc68eff571b06f34ebacb232ae8fcea11db5540ffd7bc70dbb47def4a5bc118c853a91177d6f9e43f3e80f8d47ffd102f536df9cc1631fbaf9a24ee4c5ef7d7e1855ea7bbca4b2f3d2cf131f950ab22af5dd04988070c6a17f9442154d9a892de5f2fc94b9b94e602c4b8ecc5db611f17526c389f751ba5c2f10d1a8077f8c1a48c942839843dd5f41271aeff52fd08538eb9759211e697a1999e804a12fb0e8c584dd2f985a6169b3e59578a6a020f341d1397d4e52e85a1ba67098f43b87735909425ac10f2175b1cfb46f35a17b6e7c7d2cd0101b81bde7894cdc708291c5237415eceec07282d278fe506cad1ccaade18bd176a20ea2030fa84a30c2158dae71c199330fdb114f199d9ae04a85062ba6e8c111e27863b4fc4c37eb1304b2e753bccc3122117b0d3147bb809f72d7847fedeb897fde4ea9aff7dff4e223f7ebcedb48d945a29e904eb2a694797707b43287e155a2a6e08777ec35167c8946eaa18dcb15bea2d20d87aadba98cc825b71ef8d46f3d64e7e5fa5c72591e78a45df446a5a579f472c9072d54a791f92676684950142d2aa602a91cc422bd3c44e8ff2025cb6a2ea81bdef5113c9f9f8fda7440866650176dfc4da3e4159f51b2c02345cd75e72bf41670993b0228bf78604d8d34c99bbf6e43ee982d81661ecd77386183097842cac467d0ee659e022248fbd1950e168a9b2a8f5587c04116ec5021d03074a5f6685c81f5a493c2294f99e43a7e43f17dbfc44a0ebf17455de1f77cae669e79e37450880ba930c10f9dea0d92c375067bf00f0e7e30c206fd543c432a57acb63b0bc3efa813c644fc73dd07b058fc8359eb3911bf7228c07ccd59c0f92c72ce021668fee6e8382a7f19d150f2f746529f09a0fda529f91bf3c084298937db7ceb9f032a3f1fc46598242a17d63190c43dcdab7d9331ac70f3808993201dd575d044aee4b6dbdbfc981a58d0111ff588a1d359b5fedf77d290ffd13442b56c90090e73c8fa2700f9ecede018d5cee57999d28dbd5b48b2dad44f39d44027c01feb69d853d488c0451d267dfb78c78ba0734921d160fc45cd86fb7d86b85552ad4662692d6df52b93006f7b1a373b7a6f3d5b77c80eb9e51a0f6d6eaadbecafe6aeb883808693df48b55a07e13d3f0706046950687b68f7ad37beca9641ff79a0c2e0b8402de30e4cef6a3b39897c2783f508e9497bbad79b0260778a527b07e189f7b2f918d0a13c8e5404d15d2f8ed982c379eaab88783372e8d074a9ddd99db710b6c1872f57e79cc91cb8737f0422c9d0ea5d62bab1a109148c24bb35e9cef7d2ef3f0155967cc64e075e0dec6f46d72d24c5be29b919f89dd651e4f522be9414b7455572a4c2aae1f57181b1d674e20b76e78bb6958d4d821c2016cf0a6a6a0a8d6e75be026dc4b689afdd3cd2cf2838ce73b38c1cc2733567d618944c7874d939629c762e6b24f211a07a54925c7000dc8d48db39a73b10191a48914541df8ad9918ea0c46241c2ffa4f8b8a24102e1b1912ff428964ebed4882f6b10a84aace2f59914e782ec559ae703b6b0dcf2b4c06800ada5fe480cf9705d8d1b339a5226c9cfa3a96457721029c1b6f5876809b0f7159d6d12484634c2110a0320b9499edae7d03057f54bc87d4a3ff5141e822e329513bf2a077be5c6ca62ab8a9a8a140fc9e54f0d717496273551e062372cd6ad0d4b85a207b9b009fd5cf7f61cb32780d619b7f9a0f9e8ce646ce5cfba5e12a0a639d3202e42dca0eaf9ba2f8323fd1a6dc34c58875bb4044b03893cc11fc7b77d6a20830f4e5b5a24437c4b87d7f5d96b2258860fe01f86796d1bd11536b125fdc8be02aa2efcfdbaa2e97dbc1b77978354f675e7dbde6fcd95d1b22d7501aac64268414c5bc939eb3020b137ca5722ab5c16909b976fec16e9390007d4b678ccc3e356ad977c784b40496f1bfeb27fc7649a0f1a28966fb1f834eba1753297e8604b5058a37de7c076deb2ea8e450b4b56b4e47f0e89f3021d5680f3f87e9ae0b97083d8d8aad8a9ce5f64d3f3fdfb057d7a0bfaf50b7bf50bde61473b77f6ec17ecdb2f74c7ce5edd027dfb85ec3c1d1470dcd98753508b830a1dc273677f4e413375accb2ba8c1a1426b71ee7011ec74f2f63b930d3d8ffd6a65965b9f5b5c017e0353b9df2c8fd9fbe21530d34e9e3b7abe4ce285a2466f9707a6b33deb6eaf450d2487afeff2ba21164091469c783bccef45e927b9fea057078f1233d69f0924248a1d6c2712ee45e89e39261d79392b51e82e2a670cbd7c8db998fce4305da877b85e3eb7574e3905c4286d6458a333cbde24a5623e325f5f96910ce1f53cb97816650b8eb3430e6a9892c282a237ed55ad9e3278835a83b5247e478877fb1a2a3eb90bf41361b9024dc17199ee9f1148f47824be669f6e43bfce83dce230c598b0db7b95f5a72baa661b1188ead1696108ec74f856efa6564ec5e523d757072e4955f913c109735d514c4009458b569a72eb621835b4813dc750b60562feee80d4f13955e79685e3bca22f843fdc393487af7c0daf7b34dce7d00adae649bbe62fbf98867f338aeddcfb9c697a5492a6aef43982f5f3e7bb02f0cda3cf2bf322b77922963d6a7a83207ac661b7c61e79450304608150da40dda562d261472254ecb95e7f88d2ec5de289fff0ad3f75c0fe0941996247d82a0a3d1b62411caeab5f5f50e22a0e3eb4158c5b95499550348ca6f91bff91dbc0db282cb24ea729667a0c71eeb6b631f2ae6b1d3bff222a210e534969d5766bca7212538918b2ef5477af396468fdaab81ec8d4876c9f4d1b941a03dfd012d79e234070d053757e72142ab2e49c9cad9a7aa907432dab14832def283cdaf021bc2aae20416f3fc979a87c7e0394666f3f2b98bf15bba04b262e3847251aa568a2044bf629f0cc3ff820a72baeda9c8e5841bc756f2c4cfd2e409aa563ed87425da2ba5a77ee11a129249a06df0f41444cafc91f0f45f3908f2acb21ba7f5587b3c5aded7a81a6586d3ee091c4984aa0edab4d921e3bd171d5e4aac059d120df811fb17cbb409e5770073682a8979aedb3997414dc2d13646e15eeefcace6113fe29e0f08a57361c1f87a3146be1171454897bbc2e96f66b97beb8135e545dae14fe51f1338c15b0f6d8b1e881cffa66df5aaf269736b56e488d29fe7ce176ceed92c7219b3053b15896195225be4a2d9a62808e7fa42d8fc273799b92a633ac81a5c6cd8ba6908b5e6b753b12093c56a4da3ec816672240c2a181f6127ee30250d426c0b99841b643840e848249cfff7d83443aa6de74654c12e0f48d39d6a2afc799949680c7106b74570a424f66936fa7b062973ea4fbddd33c27aaa21ca283efc9358d64317bb1c55e041ceea0e95d2b0caf7d7a65751410b5736848ed24f82b56eff78a68605126c998837017f1d99e7d646ff8a4cca18a7dd5a7a5e185383403d7a1c9c3782f604cf072c5747101f6c6e7f5a2921a086f60d5d7abe6acb1f4f2ac9c91c6e8f8e986014cada507ea12e471f4c21d016d2fbe187724a5699bae881d11f76f74b09aeb9a67ced28417b5331950e2fac3ca6691f83f4952c695e82906025c39f6abc9ac2ec43258a01b61340c22d7e5892dadd5cf5690697a600ceee77a0a4beb7afb5d35c314417af0ba2c5eb94b235589400af20760600b05d2681128575f6bf668b177ecd90fbbbf24dfc606f8902f4282f6888ce0eb82f6d858483e7c22da033e445f28373659cc196d6cb79ac686b11d0e432a823d35d8a303e5889bf581e6ec9ad382ce74cbef14f5c233c6ff6364bb69d4f660373eccb5aebc5befd1f13cbc787640e3bb8c104e2c63a8d43eb4ef8fd084b11e198d65cd9ce8d86b9d118bc8fcb308718c4a152cf0023f000a2f5b43782b4f985b25742f06388e3556ff9cd7f7954cd2f6d2ba79b82b25b7a98a20a5d4fcce6f14dabdce4016084a664ec485fcf738d55b832a3e75d080ddaf2e32e9566c3d66fc30dec78b722394df27d9e157ede7b68ece25f0a40f0264737e0e2f1389de87099e0a3b31c8daeb7333964662aca9bbbe3c7cf5f87c7a4e8afc65b9e702224d4fb1100f47d986d2a8ca60250de7f478bce06ef0ae81cfcde8e1b1d64d190224878754af858f2aa01282e74111eefc677c6018d8b48eba0e536944ad8e45c9205cae6f76a2d1b1410f67b8317e0259a84ea119b77c501a5234577202f09bed685de86aee0493b5e7bf743fef78d6395e5a2b5e800bb07a2a48be2126524ecae1f4b93a06e2ec6c08abd08fdfff615dd7bbc6e5921ebe320d757fb77ec0c4ffdb7c6f0d17596f7e514af4efeefede5a03e9bf6bd3b8ec19b9f1a96f0501f56eec721997db30d26154c2012a33c492b0e9c33a97d7bc63e168b10f6cbaa3537c321c78d6d6656fd040ec0d5129223d26cd445fe7094baf17f61b90212442c69599cbead1f48059a37fd37bfd4ab1affc0d7b8ec77622316b26d6d05ce27093ea064e3ba655870fc4936a21af4aa5c46a982c9d9d0d4a79afb583fa9345727c99ca696186cf96dff78eaa5db7fa415723431364c324eef73e0ae89f96e5f60b9876964558e239a85ee3655ae8c9355ea490997197a1610ddc19192342e89f3009165cb3a16e4e9be718bd02baa9b07c0d06449a47e9555c3564891d76b3b40cacb5ae403399194c1ece526b9392fd19c7b772bc50a8a4112450071e9db6471f26b2df469a28fd1a4763851322cb89d80f3caf01596740b5aacb4936c1fe70ae6f8e83faaf638a472abe4072b6ba0a07550d7b85ea0a5293fcad69b215605e162c3684da7e2844a9068ad9fe7af78d25b81ea379eb3fbf7a7d0ab175462d06e818e7a1d3a1ed6ffce0cb879661892ce96408c1197dd3eb7ebb45b47618447247a81e958468957239e9b68f20ca183c34688254011dc1f04f13f336e723fdafd02f7489c51c22c0c65caa1b3785b00f51720094e8ee82abb94c375dae55e40b25610a6b3314ac9b7a1d4852d4f1421616bce457d168bd575bd30834f7ac090f5fdaef2019e5e972330e2291e9abfce61973a275d580239dc0588a0bf20a0a0cbcb7ea1452396e555076f698c230d0409a394d059c2939b6b6068799c6561f881940d4783c7757f249c0e43150ebdb46a7bfa35ee8116270fdcc543c8c384c4ee3b6708268888c711c96badb4bd94f3ea1446cd28954d0dde9f6f0aa32d958cbc888c9007ec16fbf9781d2affc84bfc4fd61b22591fa2f3394a26eeec3621a9691b98ba1191f6a26e202485d91f707e6338a442da1af8c0736f28542e60c7709bd07d65a60ecdc35ec16758404a8485091a777335c80e28c74f8d274a72e0cdfe023daa9bcfcea45ed1eb15e6b717ea7fd344d9d949ed93be01e4a61c3fc3e9dd269151dd5a16d55432033c79990f314d8b13f11d34112a1913e31b0cfc2087951b224bba2656bf57548143e0e66c20d7c807763135f7f1ede1edde019f061a58f1b8d5b20d0a3f320bee3ee0014084a2ff19bd6e55aea5130ea93410c87393729020cde15958ce506af4bfd1fccd2a569747df2db5e50a551de30b02de3170dac3d52217c0313adbd213cb0b7a3d8f59dfd57de8939fd884815ed9d67909cf4db43bcf71c33a7122b9655459c1000ab2bb3556c404f9d6349babae05d1c69c1e532bdf64cc952f0df2de1e2d4418711d670653b40b2442d89efe14e07ff641adc805998984b362f5da445067e2154e56b158dc9c51fd3373d76646497ad798ee7aea6c6f625819ff2e7a76c623c46806cca3bfb9861ac348d2523f8ad089cac7f642f883e9577e9630a8c6c4d402538b42b4da3bdd853984d76602be7ca06bb7fe9291e426c25456e3692bf8608c0634ada05c8786818290a87e1c8c1f22d19552f452239ee72e1cfb9b340252d1cece58c3645353ab8ef9702a93236a55a10c50f7de444211a462c2c00a585328d0ec47c5cd323a43f0cb798160bd190c813076ed7080659baa38fccbda191699185cd64e8e69b4983b739d2cc9c3edbc266364a87b235896d7e6735a8775e89538e42f099a39e889dc6c51b4edc2967959a2980fa00e5671ec08b48fde8f9022f1753013a5faebf182b007d4249c70ffb9985c7b6a8849f8699cccbfb6641934059925bbb51f0385499a48b2762871a96cfb3476c322c7f186314439a66f0aa4225e491c5675b151278bffddc6f2825d15cb1c0d2342fb20302cad79a54a4ff38cbbf1dd74d087abb4beb57f1e353bf7c690bb72efb47556916082c4315ceb2f3ea8f0ad12678f328acccd750230f213eeec8a74560c08a5ec12a5530a1117a1ebdc93c92afb32fe87c5efd30d84c140b2eee3b879d30c9f7283977c74dbdfcc9d7a89f942dbf71a1678adaffceaca09bfbf34655f60c34e72b05ef848293669023e68a3f95bd5ae8e474023041e81bea132c9277514a208e64b64a89376f76f06ada142887412aab912eb542a3492009193e3e1c3eeeab1e13ede5705a2afb94b24720df5f0ee2d7589ef12c0d1c8a1371de62c953b2116f8084f3464952051eab56876095d7f419a3ab8931e2482a783f7c5f034891c612b7122297e03c343c76b4a080d35db53265c1165a2c73564150bbd6560815f3f3e13aeca3ae20d0590db6eb3cb64de7df43b112a27cce72c47f9bc4bdc540a2c603e3c570093438b3d5eec5657d2cd67ccb5a402d11ed8bfa569b6659ac2053f645f2a4103f87a7cf1db1fa721fa24fa09c48e7a6abbad7e924cacc2f01a7f498f066546c42dc185d8574791f83f2c5d9913ef0d805ff55ce4efb32c769ae26667a12bfbeca27acc8ec22c753120ece2b72102d4beac253ec18f014cfeaca7839c458a8600a81397adc23acf24d0e97260695ea5926dd6b7f8b05f579b0a670b790474b4f367d4c219ab5c42d3b656b3918d448f311d5e9160701b31b92a2e445fff06f08afc1eb09d0c11d14b06e00615e14465fcb5998396a5782d09dea6fa6480d1daf972cafa083ce4dfefa8d0e7be1d88ec7d34934ec42a9ff4817ef12cfdcb2ec3544899425c5224f659d665fa09ca9d2803c49740c6276de2af4d3c6021f6aa5a936a44e651fdc6e8a663ec3dba3f635c3c8bb97f37ecff498da8d89e22a7d89133589abce94c7affc6e7c02d6b0406afdb20ad1ac4fc1e838a520afb287e81e57026ef8285272826f77309a60c1dfee4b2e271c390ab98c0bd7660a2afa669f42faf5be6a1efc66fbb407f9ea12113a63e40265bbaa98590354b604012760d34de84cb3e768dd62bee137d02af13d1a6869de9257f94da6795e0d64be436942ba7b67a206184071c26551ab221c017d1d20824cc2d5eb61c288a77d89dd7fff174b055e0c3c40b924657775e94fcbb7f9010a95ac3adad9604a0cd9c0812298407315fd7933223d3f0b172cb41e30f4451109380604d97dd5d383faee1b76f5566576f7c3631fa962b86a2aa9aaaed904e539536f886b94f392c93c5ebde8295b815813ebd77e566b212aa0ca10aff53752693b6087d54508e3ea0f5e40d1a757335280704c69b78d2b72b4e9eb3379f02510971b667f212da94b6a492d82f743d2e0e6da7dabd71e9da373701dd9e37b688fcca17d7c8ed375b29696f36a5ef4777b780eeea17d648ecea13db08ecf91393c8753a327d7c406cde46bc7f6db2373681c9bc37b740fce017360d2f9d7f357b373b4201dbce305bbc7424a224bec29755a4fae893df55f31fc93f2e07dcceb4cb4f74cdf395c9d7bbd6d77fd7ac86139b8723d743fec87ed61bd0bb5cfda31ba6b41bd4b2500b80be8aa271f91ab7fee8f74aea50f885700f68cfe398185ddb844ec45e3abe69d45d756db93e408b27252e2d93fc9bc3e5a1d2ee46fe6534a7dbe8f94e2f32f44daa28544259bbce0ef01ae791f30858791be5d850909df32437d8feab36c7e87ed6a6d2702b4728523e57e11140a3ee7287007a4747c8c1727d5a48408c062546f750bd27d19e2955dd0602327892931b62814cad2f14afbae02a61dafb7ce0bec9f9549bd3ed068132fb2383bc3e8f19d5ba6608a40167122d318b50fed5b9d04e085363c3a26408a80268088986b33151256edcf6fecd7e96ce75b3b4485815baf6a29f2f5d0a3e284cf5a6a18f2050863de89a1b3333ef86d34aabb8088932e23a470d9252e94dc1fa04df083f5494e208bfc1bd017e406f30a3f1a78da234d755ee809602aeedf6394ccc1fb0136e27f526ad102c5bdf6da0680e0f1ca5171be99c6f1d01a09a6283102b21e50d1e23b296dd55ac3014027fe946f0b3562667cb5b0cfa4b188932ca030b1e0c9803111763f50d327cb2489bed2f93f3382e84c21ea4d2ba78cf6b2ec6dd1c144486d9ad55e504324ce8ebe92295b4e251a38020307fdfbdc10626604289e4e4f5f8ea3439a144f1b2d073035eac09e0cae7406482313f8810c17ba02a61faf544f8a6968f4db2ccfc4a76a69f7dbedb9491a57c90faddf9f1a0141d6bcb8c5b00014d0c89b6ec324346ec7fe9756827811c1d622296b65ae0e2b6d0527769b704a4113309c4ba8415e54aeb93c46b57cab965000c12d94f3718f1936e00312aee53f120c4ce03ec4c6be50a636a7c45ee98644d4b7a1365133414596eacd5ead2ab245c4cf78b23a33040d309c6331a4ee4010fa1934a8da4ced4c8047055502f0127350707608354098dc29234bef41e2fd5ed10314592bf079370c38945b0578643907cd3cf8fae0569e07afc681178e0a8142b0962ddacee3ef8c452b162f8e9ff3273744fc073714fcf71b04fd158d0db6fddfbbdea60002fc899980cebbec04f3c287e5bf4cb30e873133dfc35c783d44b0547844b1303c905888785cb22c7a78ecc1465a5df4f6f0e04254c2138f19eaead357d2ec9f4a5a22cff4c5316270262f6c96f4789704f45497a2f6f096d0f49097ae7a565df2f48851e9aa6d89b9ba53b370f78cec400514d89dea00f55f2beeebe98eaf0e873157e276bff068c6d9368e2f396ce3e89283da38bfe4b08dc34bce6ae320b5b336cebe376be3107ae261603419506ce4a2cc84948de18b28c814d70c470b975f8633d9311e11dee1669c78f7a2dfab984433c1a07ea6be2a473e987cc41f405b3812d1dd15d9b5511d2a2a450ea965389be9cc0773b38205e30815c41df02196a445293b13d1d1a26fd6c6c4cc909bd0a8a088654885f3a3deba262be87326ed18b68ebd62960bbdf1cb3cbde89609bd57cbeb7b3dcba27a2b96a37b99cbc87a89972f7b17963dbd027144206ccbd684b1f27a43d70a97bbe5617bc374beb3a0c3cbbce99df3bede0d771104234f774ae4c9b3de6e85a4eb91542f5eef403d9a9b591d819b50fd967b81ba835b4cbd1677bc3a831b499d9efba6fa2ab7a74b41b96f5b7120532721d556167660ff85bd17ebb5d2314be469e989f2eb5af32a9cbd7d06092abbe057824317ce2538ebc219090e5d3896e09c0b27253875e1840447550d2438e614f59fb98f36d4a76118a86ee0ccc4cc98d5fb98093e74948a8e963257cce7a2f9dae06d947596ea122fc8235a70f752bd9de988c119261a0eb30d406605c6f4abf244a0c2756d696ca76337dce3a9328286c0a05986d02958fc3d82d718e8dc08a750f72b6be9f1ccd25cc649ac96892aa10f2ab57a629ba77549eda1d95b9ffd16cc6c2a7094fbea48badaa42fad157de354faba147d0ae7a7e434cdbba79f31ce265aa40a80b61c8a2b7c5e317913fa7d144f9ebbf823d6a677061847ab17f38ec44d8d68db16c080ab804144e8c2806a257926ba3c9e566095db1a86ec6df7e799f65a96101ca89fd16ed28f0ad83c60d7fba8f9fdab29659c2206f1406ddc512a664cff8833b102dce8d19d0cedb615bf1293d08f5bf1b88a6cb407f8804ec13f432eadc2e2030062230332ec9a5dff825a0690d67684b3f61cd9ccc064a802982a647fb2cf1313d4b317e5e09b2ec6ed650e5415220d82cd48264109ddf685a09767e70c63d2a0d81542523b55c353f1fd6f538f39574ac2e77080b4e66fafc71ca746c24ff383f8ed6eab8b39a782de550097187478acd5216c1bd91b0ec8466a5600e01ee950f87eb7adc79c15be743ae0fb6c1202c832b9cca327fedacc55eb62ee0a7c4d057d9f4d826e2c635293c240f1eddc5e8fb9bf022f9a5b949110b9a5e463fb620d31b5c29cbd73f640d113fd6ac7aaea317785444b5dd6f84020a378092c492f2152384cbf9ab94a5dc8bd95723fc60de2dbbbad16736e35301fa261f9dadd5e8b72be02f85d9f4bf59962658660894f1ed79c504e25770613e3a174fd6f578f392b30242df839f6099e82b8404d488be577775b3de45a01fe55f0f3d9255c29d18338513896aefd6d75216785a75d057d3e7b0308a52980e861107d75c72ab52877056ffb4d329b723781bc0bd70a09174de891aa1345fcc3fceb1dabd4c59c6da1484fb147717a27229326d0532b72e1f55e6c7831c4bfcad98e151d76ba0563a81928211dceb0e54ece84ed2cea3bd25c11824642d7e4bcf37664512eb435c7ef0f851900c735ccf4c382c6cdf374e7fedae8e9e52f2ab89193f30fe155838ae33869157f59b684c153df87f4316e70b8e36db83783ce8fe12e80cc62f54d48d3dafe9c8a29a78702d257110ee6e7641404edfac842e9a07309f02b6229e2ebb99fbd8551ea96c639d183058e7e13bcfc791612270f8a8680e62df22e6c75a68b95c1a017514fc6bd7b018c64c47c9bacde136e2d1d30b8bc4e3b1b33e948a9ec0e833d48faeb2242dc2d1668f7a25734ccd281241e90fa3d940ddb679f6fffd1713b467be7fae0ee21d150b7a3bb8744a3f05f3084e47044cee5d1264bbf13f38c5a65f3b1e1b63f6ec0a23ef8430e18902889184ff82aad61359131700aa3292841770ef8114ab9ecf609cf7a98a841e22eebf3045b1702f547c46551db1544826ac12c0fe9ba185c45dc7fb7fc6953d9a2559c97715008e885cf92eb320f5f0f816ea24f4067a01be806ba897e99f4efc0e4fdebb04cf9dffbd7d1bffcc1fbd74999e67ffdee80a61073107c6d38f1116dbbd3d4058e717901f254fdee3972666b801b1678fbcf35657620146b81630c1798a4ed0586ff0103ffe50623b017dee2cc87ba4d5aa8908a4f93e6f355e1bed98a0f6020f4f0e31c98aabe5b3597834c00ad0a10e052f025d48738a0fd9f1e8174e84d8e0ea976121a2d46d8ee822e00f654e65c1b67ab2cb8489b869ee2b80f98dc1d47d2caa08802ad049fbd7007220f225a546aa05abd1cfdd4b47106771a385fcd07aaf50e51085f0fcc91808c54b9f04062016e3ea3713eaaca3228b493b436befd8647726a53bb484dcf1b1b03cf75aaa5725b89a96861c5c6f03d972c39e1692faa4f2a70f00cdf29bd0f8afc7dc07cef7f46d7393004343761a6cbb5d4c0c000920234b6adaa43689be9b7be08631b6819b3ff95e2239629f569d5a8a0d63a204111954c1a6b1b5ff5cf3e11f6443e9c09f5d8799827a4f046e73ba3a389c7aacb8a9a0022dd547dd65dd3915e50432640d4914f8062e24aae004167cf927f675e80ab0afc5a0bf96f7a03d1a3e2a6ac932688d6a81bbe0e60b28105264c6c614dc2043a865203ea4bdf27b190ed85dbb5ebd16ef39ff9d84d5d388f1e08cf0e2ba868fca8aeab8afb3f6d561b702d39d2aea62f0ca5b7d26518908dc83300e29af1a554b98800c40e7773f9773e09138b1a37bf8df3df447219f7284c62b14bb15bfbf32d4dfe709cc3f8d1b4aa665d872b978006f2b9959c502a0a19d5c9095bb24609c55eced6af4b0ef753725cb06ba2796c4cc5fdefde4f0a1d8863c612148f811cd46e9716c910943223e0c2f860e8d11313e4fea18129e0553140672f149cd8e1b0449cda0706cd7d53aa46b894ba29507d2ea91d59c12d2384451890950d8814b93f4750e0d00b3828ed99e4348230bb7ca7d7dc7610bdd519b89939b207d6ef9d0a3b90c0210a1e768e2518a4171d25ec4bae23ed4cf423201dedb4a79945cb2f16a9b56d8b16bf32bbdf79d42d3e9d8f9ef7e742a91d406890e34267c532d34951567ffec2fdabd79c7627e52d04122441d3c48954d6cd6d460e22059e98aed1b84038d72c73300b48564844f3ec7515ed13b93a50c4a2ac1b9245ce754d25db94ebd36b6202b722223c045dc24dae00d1ca6d2cd30a98d3952d27945519d31552127332d5b8e187a9975c9d9e513f5c0d08889719bbf200e616f3fb8c15efc3b1c8853123c6a2a4e15fe0ee646eafcc7d312483e1077029374b3c1f404865e1c7e455c48bfdc1a8002b268ab16fd3297be05c19a12608feb9b2d613d54799543e0e697e1415facf1c18eb2dc0e60e2dc3a3312fdc3ed6dae375bd532793f3986ee8904d1dc5b8b12c1a498636ffdf8854efd07791298deecd21d71f13720f58a13620cb31da41592e40224070337301f7a035915c4c0f5353b03f086c41c202932cdc18d0bbe73bce7ad6c6281f0b885b1d202a36cc433d9fbcb8e8cba2de3174ea6adbedc6b2ff77b4eee8f913aafe2c609315bd5b6d7d00f91b053e577415bca05ce93efa9679a009e418a86d6611f10b4fba5cbbd820eea42b464148d79fc873fb3244ed335166262d8cfd8f5cedc873543b4300529c35e9c3950f1b32c281e87852989839b21e7580c57cfc6ccf3490761c32b831f97850d7226012a14f0a6931a6ffabdff8e2e9de5b05bcd84d013e037dc88d1740e8f7a52f9f464ce248eb3da9ec7b200e0442df92aa20cd071ebd5d825e91fc2132d168a2834a5abcfee500c8e185594b7f24b6de524dedf0ff4f5e35fda40cacc3849f68a198b1599097e19878a22d3e19d12d129d5aced427c8eaa9db502a367f74c3fdff949c330a7ae27e9e2970c508634da8a1316791a12a931c08d8313a6840f268c2e75b4c6d81ca6692f3ec3dd784ddea53330659eaa075fed6fd8b6168580938cb5f162b8502c5c68b00d2b024069641265cb02e2e176b40b071756c76efedd69b1f152ad3f99d4eadc5c8b1384418ef2c5acf5c17148089861725b145e4b1c707cfbb6733dcc93ef8dcc277d6e730dd1b3bf6912fa31cc9b876cf39f67c2853c03ce2af404a9730011804c724d60f4a29921565c0c7a50c5d98280f42a1f3a9cf501a2960b183602f3a657224c03f1831c6501396ee74630f9b327fcb782ce2cacb0fa4f74ea54c93440e31ea501a836e381ae2877f03c9b6cbab911cbc3e5a6a409d03ea62520f6f53cb4615cdb89977c40fa0c41fac30f87f57536b991b148ab5978f7c80705910d1c2d4abdb3b7ee14cce8f6043f3dc86cf60979f0fd2f48269405f1bc2586a022ae9d5abf6dec3f678c4bb1cce343a267032098a6ba6f9053948cb9799b0d2bf052574b1c401995954c13810d1ba899afcc0928e13b2e7256880dba3d21906ab312469bb44e4f4ce9f30622375c687377951b36681b3d706359ebf52b0ebeaae35614eedcbf19cbaa9b339ad43788dd31def415e456cba24bb9137afd07f907beb8fb760d321a4c9b8156e55ff8abd81992cc8350e803d9102b27c2d2ee31c443bad80ed05e409cd1783b9c813bb14568eae9f6d696c7ca30c8363fd8a639a8e2d36b83abcefc378777621d1d49267e441146573e065cf870efe1dba001698fe135a17dcd58d06a312ed03ab477d85afd134e9590adc96965c3abda91fab64d13b043d14e1491eed12f5edaf405d43cfb52bbb8c242919ee4b76177c55dbd2c29ae5dec778bd99f723a175fc28c6a5f95f6a66f9d651890176a4745c7146101afac940fbcc6999f111461153ab359caf4e083d4e1b8ac932d4fe6c807f950681a87eeb65394e03f86bd5ac195a0c5cc98ae71b416e4371b78cef8f5b25f1673b39eb1cd13bdd7b04847a95908173c54f480dd4de8ea70fd983d60764a9718c6650268528153e6d43e4e33330570414979911e5c86574660483a4f09de2eb87646354f704943d56dd1c598e00a74fed12a9949eebadb7a915b46bf40be5e6a1af00b8a6693a965ff3fc22d4a778311f42de5fe7739d2d18eab0a256a894fd3704d7e56c9a48edd8004c6d03946973c10965f14fa39d29d8f94ba61356c486f3b184aa1b06652b97128847d1ac732899b7f8a4de6f49088f40894d200eb8c66499b956600fb9bdd0aeafe6680eb99537d76be89e01ffff5546018f0eb85d4d10566e6b10da940e877442d2d6fecabf3c790896cfc821e2910235f3029d0f951cced15ed4f034e13db06bac69085438e4fb9ecc2a619946bd462a66a84bfc950fbbcdc6bb7ec03bbcd100387dba042b3f0d5e0be12cc21ffd972253d3a337a9ad019787a63971a66d47a6963dd361e1ca1aa1e9179105e9262a08cbdb9fdf3812696cb0be08afc13bde57688a27781c8d06dfddcd218dd8060d809c77d5b8edc2ddf29c81dfcbc9304eb0d22df8796540303232f900a48c3ac68783f2d2fc97541918b1a72629f3eb17eb035c5f94a5c86eb1e468b23c94e344f067ee83e4cf98fe570ac8b4a73b8c7665282db0ee9dc191bd75dd82913269faade8d646d1802698991bfd21367ac1a3e34e3b40e01d0fddbfd74e6c26b50741330cfffac215a4e180a75a1f71df3ae137d20f8b508512db6e31429cfc46ed9390126c1e5d0c9f45b194221284e0306691a03124a625546c10759f01c7fe2c21a8a901d8b8d63192fc440c714b3dfcf809bbd9ea10ac64f3a4bb82d655b6d87942c63f47ab1838e607100c5e97b19aead2d705a9e12299a84238518a0c58585e94233f4ec00fe7be027a899f67edc633b8e9d2d08c335212aed5e6ce8c636af6f7404ce3d7876173b5fc88228b0ac1977639624b9005694284551eaa7bddbe3c7f28e6ae7b95ccd4cca36da2be974c3bb430accfbbad0fd6136de14b390494152e523a689861098bcc326c4873def0e0b0910c5cf0641910cccf4421318f1b60a2de169a622f5abe1df6533fe0cdd118da80c68bd412c624e5fd0104b3b9cd8d69a6a096a64dedff94554f5b078b5e3f5286907f0918618b1ddedcd874deab8125d012b0be9efbd8b5c95f78a5ba1bd86dc733aa708685ed6c49bbb65f02366f4cfb0d31c2bc8cf9107163586c376be1635e86a691cbc46f78450dfb898dd418fbf10d8425a3cecfd22cb6c3d9a1e466f2036d88cfb17943cd205ed514a8b0be831403d2a8a2bdf4d439f0ec6a365259cc8d557d099aa83b7577e33f7b1cb68af3b3765b50ef1b1dc7860970be9b1e98f118b749d8d71690a88ed77c88f5aa70e1e76d44433d71e4cd46e9bf67f3d37d65fa8326109b1b130bb12451af9b7afa4cc3e5df7da16d05644e4a2bb4753b037d0c5e2ac00ab937a4d31b36312645d6753b3f6a5eb58f4a0765fbec04368b033517491997a3da3da37faa168a45bb995336b7774dc8aed81a69a646a2e08a3cf67de7494eb5387bb89074b833eca7b902caa2bc1015567b9301b52175703ac71632c511ccffce2c8070a08c33b66f2c9e949e13240813f735d88077e8676b078d6c75c07fc204977023d0b657fc00b3e31b01a53d7eb711dbc28700abff02b88dc00d81d60d3c11c545aa188466301f881b0937792c8b768805cd4208cd73736ada525fc8c782af35c4cfd224143aa62e46be898f411f6c668426e8c33a68b073125e44a4419f3245fc1504d2da3a4cbbff70fc74c46c94ba9a1a76c4c3879fb69e84d3813ee7e9fc81e9df6286f85720608a894afcc912fa763373353cf2617eb40186e92987a22a5fd3710b0769018a3f106b1318518c1c0ddedadc63cfd9b074dbfb534173cdc53fdbc60010f5c9aa1aac82f40f983d97e53f9ebe7032037643f0b9db9eccc250cedc93507bc95018cb6e77465434b599e57963197091f8195205fec4f167ce1b03a0e883dd002152d0941a6e28d271db486718655d7d2b1308f1a765cd8dec57b7deb13c0437ec6fde2b57ae38f14fffddb11bd0419c2583baeaad3aac00afba6033c67cb3922699ff036986bfad45238c6b05fd2acde7a86ff49648187395b7ebf6dcee53b1f71b81f768eb2b2840236644da9eeb7ef210eaaad08481d8ae5d3897e6505e291a684726c6936909a03f48163984303ec0d5f8db6b93ad20cb95f7f071df6020cbd08edc38049c787d7311362cd5a4108a5bd5a62416b581d1193bd51a2ffd472c5101d89a3367360a8dc32d80975c07e77875c4136c204f2434e1419d9a4b894193a01c2d5a1dfa239d69272f0b925cc606a082cabee18f40278ebde45b6cf1970e6c380ea9e19c089c066b23b1a6e5a5fa330823e0aad7720c793cb468512b334c53a39bd62320ed505fbcba97c6b760c80480000820cd66de59fd510bfb1960d3c7ea2b1b54bdac4227303457957245d25509512c4e93739ec2afb53dbaa73853ccb117a672bdc149a85adbee9fc168b9d40d35074708bc5c46b66047833e6345bed87786c79c160e0c1c4574aecccd09f55e7c284b4fea038a2cb54d339e6663ff69cec6c81932fab2ae4f090997bd482c56a97c47a50063651a58b7a91123657298aa8c94fa840b7d3b3a9b0869c7f94200aaef806395c21b2c3f566fd8108db8f6049934d13120a0313512a8fe54cda45e3b1b0155ab939dc0ca90110770e3076d75b6958f0d8c89c9a9bfec020dac11e46cbca59a10a16d2fdef55ad60f83d59656e64c9a27bcf09c664d59fd4d0afcd00cc7e648c7431b9f17c81752a0573f8cedc2a75d0e18fa11582ebd949dd7c740c4d829f12095f307c082918654d7d6c14030f44c323a0a4f9a6cdf86ed908a475f7afc0634dc44c44e284c9548d903ac2dacf762a363f27c5eee6a6091897325c6070ecb54bcd61d1b003ebafe19d6f0822aad3363c4e9e8c89b6fc8e103b07b9b96b49dae3b071e932c3a2d50d30905c6391dc70bf1d8c8c72b3a9dcceaff228f113ab9d9b32cb263ede28c45ea9946f1bf390a0c43b29841fd46c7de1a18453be4a26865652faa42761deff2a2146589506928b56da4cde2cab07a203b8557606431438eca2de8a82fbd0dc7d4431c7ea94d28c05a0f1b704acca08bfd454d80127a03c46b60f8f3e82c28abec20e5b6a8aafda09fb02bd29335d8d5f4c10595dbe0b39199c75cf07fcc0053461ccb96670768af79a356560e9d9e6faf8bde9f3dc0059ac5317337f01c1d348490882408e8457b45665e5843f0013755c3faf689318d49872c77fff511fc1b449b565f9b20bc871ca0d475cce1fd7fd80a86ece074c5cc815556c5766ac8922cf81ea414ec7bbbb2ef1244a9b966ce8bd7518111bdcfeb49a780351efefe6be758fa8d2c458802f1b31b471a0bebfa8157c2d0c0eef8cf38e0fad744ca3af32122f95b6c4c12e9aff3ac388b65bbadf75dc15c8a6c9baf16d58bc3a0f78e4a983a78999181f06fdaeeeec2c3c780ff77e4b7622143109f59e3b5ad08d4ea1335c11ced461aa86985f183400b4d1e63ca800d058f0f6134286a72ceca0f6f2728710fe6619afbb23a58b6c4b0ae238de05d828ad92e212cb7ff13b48891035a72b67f201c572299ea77e31a213ae305639b6cd3eb43c8cf9bd0a7545283c7be8601f1d89f52436ae3a5c41a312e3bfcc1d80d864cd6edf9219ce7945d2921226d5f6249a60ab33438ebc282276424593cdc632bd0e44858c5469a80a7345103ebf7442aa7465f6d0eb0de58c73da48f634ecbcb35e53bbaf7210e7880089b7cb43ebe51f11c3953dc991dad8e51cde111509452d5a6ce1107560489c00122a9cbf58b6005e385b06966acfdf94a0880d4cb20dc513bf0466d38be6ee6f04b27ac26be728608d2da3fe657497c27a4754bc75b537e3720c22ac16448f888c60cc82b108890dcc39e7f64fa101f26a1c526d9e99ac8f59f44fe111fe0b619b693bec9de4255175374ac438448c33677ac55c58c1e3f2a20d2f1e61aaaba98e2e33522443aa6b963b52a66e4f8d5039106ec371b75ba22ff55030ac907b90455930891e6b86bbefc4ccb678a85684014e615508125c236371ac462c4d302bd1a0d2d05a23f4892a38ab63cdcd569f0ddaa031198c6039908414265bec58105bb1586c04b2642bfb0c8b5daec6d707ed782bd3e28f6abada80f73c6a698f1dfa155af140ebffce40bb67b4e5c4d494ab2414c0c792b71757274020cd5fee1fea5a888e92d184f85f039672661a878e3c2a727f72f57a3248fe718c5ff0c01cb65a659e21f060d96a1bafc4865e5cf81af6f2cf3f08982999f0cb9de55b4b3e6f5049b8b85a9da501517ef92ba97cc67e0608514ec5b88946a17454b7298cdce4c3716e616ed3004ba06e539f8819881ef8c9bdcb6666b3549d8e561b3bd10a154a85cbd99d234c107acfe7971ae72ab052bab4bd5cabf5255e662bc0c501740ad431a6eaa6e62984dd978535d766eea8a9cfbf80695992e1d05b38084583104c1280085c0bc4ba286fec61b60ed2b2c5ad276b5731438b2546a7ce3552ca3e5e935c054764cdc46d1824e529407179c71b8d164e78e00660b20ff56e8be331c9370ab9b933f32bcba588314a8006086d9587db7a6bcd3f412e30968723b51d28b816ac0db93d9a23641d715120193274a9a9ba7672d948941a3d9619c44716b426ad3df25d37332c9dc6d2f83902e0390d87463f821a4e69f3158d8741149f204333e18afd55765fcddee8dd07cca0d080ab786c6827c195dc7ef22ea6e97a71c8be03a9280f86dbad1569fe9a68862bc004612058cf04c77fd00a25bf7a4addcdfb30a536c30f30fa44e1c5bf6bb1b2f66ccd5f8aa98888237d3b42c0d29ce3d005f0d914ad084333d3baf9754df851f59c79d58ac0f757be6fc9c18bbc290737e887e7a5271fd4ffebed2f0be7f1876b43fb6bae3aca3a395d5900a334234c209bb2e0abe53fb08b50fa792c9fe12ab2f405312be4f81737bf8216b1c9c25e82c3312775fcc1690055a0a63d77dc514eb9cee5685863d020b7a1097475d6be7254ae258be7dbb5ccc8b4ee8a6a94e0259c32dd765bb6a6b40b98d6481b8faaeb96992fd948150c7fdcb4537447f5658677053f564b416bd6fe5670e81d8f154aa682bfc744105eff8c19d02550015d3bb6e042fbb03dbab781684401e214f74dbd90b54a01fb581ff6c49e1c0791a077e13b50157ba655d2d6f07b80ce3fa172dae9f9a7f7afa7f8c098fce93b693a12bd6fa3da45b8f617d0bfb7bff8b1f8ff8f3b300dbfd6fccbdfcb928f5c8fe512242d7b96a8421aae4003727b500998d8e454b3787138fa864c5b6b46f1a209de9c3f02af4f45521f8359111f157ba59be8fa25b83b47839f84c37d112caddf0f5fdfaab531ca97a325aab1eb7e22bae5fb79aeba7eddd507fcc7ff83c7f8da1e7038b95a29e26b64dec95f07d45b61a0baae9f04d6f02b15647ef3692f2d2f48c98bd376ecbdaa06eb64a3c00af251c28dbee8c26226b087ecf06824a0cd9f73c8cb91efa9eaa0b6144167644ded947b0a7446907be0c909add06b0fe049bb2a4424b35eadc591683636f0394e1e51d1e9e8653c15e10bc83dff12bb48409fb92a8a1af5985a938b224009d8f8416b3e9af838e98e3de49146a588007f94d3b94a7eabe88cb5beca323c056429fbde4b0c34bcd6e6f82d0e9f1aab02faed1d6e3ec23b356018162f66a09a75281af460c29728535e84b7f8e390afbc6ba9e5852b29c04e37efd03a516e6745908b930bdd5ef4daed39244c2704abe8556fc02c5904cb6178252b625039810f53c877ef87fdfd8f252afbcfd034d1c1c58622e4bc212d681b51d08db9a94a035da3889880f78555a73de07784380b2f1b243b1bf8f9337dbed3c8b715926a6dd38a053b5c69f6bfa309f515e6aedffe191a514997758be5b8c12cba1eff8b75f69087a42e1711ef078114c0a8ed9a8d5f2e4e2ec1edc457d724281f3fd58d98b3e901f8ff88d9e79e2a4ad3587ffce60d48685f7a171360a72dc45d79a2beb41496b15ea85ed41c68f0783fd515be44bd3103ace6e54f7eb0738cab3031df84c8c7a80e29bed08079ce086aa9709b62f8b211e7d7554c480abb19b5fd3531652a665102a474837e8a4d2c0fb9819b412086166db4c2ca5754a1a78e1a03053dc95714f79d4d226f407390dd53252c2fede0c6abf44d9a0ed5526962652bf3f2451274c92674229c19932dfa71c7b369eb0548e9ccd54d9288dd20e12e59faf84bde276f99b49d866a200551038c60d94a2b13ed104654ede6062e8493914dec00e350ec9e0b42833a68faf2ba142ea2612dec00fc62e5f4369197461c9ed67e2105ed31f94a1783fbb77c85d498832ff42720a43d69938086fbe0f0839e6c7eaaf88b9db1a9512a0976fe216624a74a978201d6d2feb74ec8810abd413646267f7d4fb09b39dc0c1e1c449230adf4af95bbb06df8613b3c93f385f9e1050c69dac6317b7beffbfa27ec701ca498e6f771f0449022acbf40328283e1b82e04d8185e7ebabde41f2058a7fb2d51965489405a21b9c1be0e79ce7f8b7790c7a466ff8772272b0abf3cd3ffa26120bae56ec0bc3724a932b52f29f04c84f6d498c6cd398150dc34e9e35cdad0a76960039f009982cdb4573b1263e0ac213e006030a22ec0952aec6a39ae6919f952f83dc416c0440f3aac0e41008c79d6031ba0e93bd2ade7edb7ecaaa7e051625d41f3363e9b6cfedc11cbb1b4319e64e82b970c75a1008db60cb8bb45000f64130041e96407fe30bb96853bc5a936fd3961a45d1b6731074ac32cb4788d572f860cd7725c8972418286564ab180bea1a00668c71168f57c4ac1abd7659587b25977041a7bff8071464e21007a5c08c54bc9d30eb41830902cc2f19c9ba5dcbe2589adfd2377289a3494b9e63c0a48cce3891665daf16cd77c8d721bd3924bf59bf64dde264cac94d35bc2083eb60fcb15a0a28cf91a584bdc3043ddf3041c5457d3beccd3be99947baf1ca1d03c6106405cf4d08c68ec79223202b95468781c1732c3431590086ee69b844fc690edd4d26390942a527e34b6010ce1f4edca1949f1ca605a67180394d5dc98b7f006860fdd81078ef586b1011604a2ffa20525374b9391f5b9ceec44a4bc8188aa6f0771d7c47032a8ae2815920d2324e07ae1bb517385f946d1f81df40133407b665049b347fc5178c491eec9d8374f88277766276dfcdb9f8e9bb110a11e230102fa2f89b8e723b995efa8190e1daacc12108ee5bfe957286691482f66eeb4da49432a514fb0711080f08eda2fea35da8a77e8342bba453d7591c7e4a5da35ef541a54ebffa5a986c170e29b0d06094c1217cb55a55ab1e1a354d93a9a749049a403f745d9992e40cdc6432991ef04529b236c88ca166320d1541f1a607b0011459fb7325c64f55cddfa1308230ad214d4e27c5228c15cc28517bfc0e2191910592110c69b625cc0beaccefd00e92bcfc1ddac2c687de8a51b6762d0a2f6728690d46dfe2473364916c2b00d69d04a443a1d9d9b65b814a13357d343beb279fc0fae993546a9faa59fcda0e2faa77bf57ccb8c1872dce3553c720e95b13eed14e74f91da18152236b7d61abb891b5d217169d6b7ac1c8df4d83eb377b94032d6798dc24cf602829cfe9222121219a18076140c57ca4507cf10dcc2e5988a7481f16f2915fc8322939d949393b0e65f2f6e89426a7d4644a7ddc44b338e52d4c68aa2ae731608bfb56d13b5f18e7e1ca0704d29d972324b2643b3a33223cec498c4a82c2ca6fd1ef10143d4ad4690fbcfc9ef13bd4031b1c4bb552e547afe28b8180ec8b650c145f4522507cb10681f8fcc319e17143fc8054a75a65f1c541b023f1ec7189671917b11110cf7a68ab153d9d346d8b31517a4dc56ca89389524ab90a3b47a901925d90b8b3397793b37d606c71334a07c6c35180f8681b02cd904582991c8a6988c935d73e249a9bbe8531a047f6b1db31b9b6327db19b80e60b3375b78f6ec59bc88a1dc1c82e407d3a095f582183d1a8c00e856627950a13264c1898e943e233fc763bf4a3d160f5a3d9998b04ebd13bd4e77784064a8b7e0e4656f7f47422abe3e742bb918cb29fbef1d19c4a693c21a414a4b9c64a4a3f4ab128907ea5f81d8a62889705cd668065968493df7270ec755769d9d1d64a428d4633b1e0f49a22a6f0177692e3d05ad6deb29ec2445b79a176b514bb5186025b9c34b8ff75d1cf5e2143e9c34e52cdd49a22aa7fd8493d6656ab9fad4d991e85c70d0de42588af91712d0df6518325085175b0af06da9eb2383798ed829f1e723fc4be7a8a113fbd26490ddb68a36baba5180bb9c89a2e25a51c77ce28de987af48edb5037ba20d2b98300759366f22af5ed98f25183d353ac794af34d6a50fb2d06c5281da7fc0c9a517efac6296de4a3034b18a3166089d4ef1535c6f8c1b55a211ffdf4255e8a1b90369ab335adf694130a393c5419b4a7699fe13f6ca39e48ed8597226bbaa605958044dd7829be7a8b654dffe2b9fcf446e3a76f3a38ea315283d36953cadd4a318bc2e7270b0d45f183521ada810f947b5b71d4db9ae3a5f85a58ece7fc69a3b5991e838c627eaf7459e2c3fa2ca0ba20fd0939753aa8ef08c3b6a0069f10404b95daf427314a0c929f0b732a45d6f412c4a893c6e44ea3c3301a2245226bfa0cb09cdee123c307032bc517ea8b2d128c66088ac6088c631f0d946d61532ed1661744fbd8c7e7a76f1eff44d67494c74091357d87cf94df13c9410d4e0e3ae2a47619b5ab7d3a6b31791ca541290d4e5fc9aa7fd846517efe5e4143083f3d0a3538dd871ab2901635cda49962151f94e4f4a2c892453f4f260f694ea7fee9adc5c94fefa3e8e3b149bf72fc3452bbb60aa5253f7dca4f0fdbcbcfa59fb33366303ba12a948cf8302ef9d9493ffda4997a4abbc218f4d3a7c7a076ed1091949f1e9fb46bb9fcf47ea25d51cbcfef54a03a14f1f463194ff1f88906a77f2ca853a2cc03c3be955043dd82cc9805c5885ce0e44f7e8a3f284e9327d946dd009a211c121a2831ceb0230c4339e70bbba274c53717e3a7c578286fe5536bfaf30da9414e6ab041f61284a80d665f6cc5f6d2e2f0b3f71aeb6223c6b2af5e5ad2f2ec354918a95d26676ea3f8e22f96c5cee599d178f63594be8df848e9287e6127f938511a513126c62f4cc5a09ca97792d414e3c9679f7c9e5a4bb3d837e6da88070d6d147f7cad6fa3f8e23c7e212b01b9b979d6111259393b7c64acb4c3473a0db2b3d219332bd29059e1c597df2207051561a61829a5d3a3a79add04226cfb0010450ddb28063724197acddba85d27676f2dd1fb18e9f7826abed02c9ae8f2e357248ceaf4f74a196a7ce469d0490dd7680212c63b66d58780c387bbc401220df251fc39b5ca0654b01cfd16e1970a16629c2133e3e88ca0df347e878abce021bb05463364ce8f262e4c763bd3e9778406cacabb092c4c0149eac21609b6301a28d417b62ae37df40782ade9dd4456cb0c25be51d8fd72643a421a64dfe153fdc33d52eaf9fec2557221dc231ece4b65e758a4ec4afd04cb47ef28edea3e6ae9178fa0e82da55d3b44c54acb3ec69f2f3eeca28fb12476884a113e6c2c610b7de481d187fdc4476f1939de134aa03dbbda8805256b1bd0c3d7c186b23d3ec96559d2e5ee490846420c6a2897a4cf2519fe52de58da555f7a0bf5146f59eca2c5e197deb376edd0d00f5ece00939a94dc412d35d952b2c719c43b78ca8e70700ed6c1377886554c6347fa4583222fa538e9d948bb68bc93b216af5888326fce414664473416a5f308ebead3f1852be939beb04ac7318f545fa8cd7c618d2115fae8724916656a294aef38e93de40c921ea5a3e229464d3a8d2bce69c927ec0afc1408c34ba742b36c47c778d13ebfa5da0b0044fbac22273d8d93e24bca3a56145ff2a8270be160a0993935eef9769ef89ab336d22e1e3ee3b6955e5389c6bbf7cb47e7e119392a8ac5a018fd285c19551d5f8e2f5ce1f8c2aa8acd7ca106543f68f55e510d39a5679f3323a599ac8867e18ac6eba02865d157d5953ec68ec99e7d3e7a4d633cfa048686522a976a951e64d023ebaaaa6aa5438a9c2043898c1e72e0c345193ea83298fc506b55d55fa2151861e15bfca048660b178c10f12647487c2d6c66fa6068d08c4abf560744951e0a0db6695685163e55402105d58286ede7a36f13169e7e71345f2827cf69d9ddecd26432994c26d3f43297941a4401900e2bfcc24c7a197d5b3295e6520b138602c287274deb52e8ef30df2454a98cda3ca751dec6dac5d1376f4697b0ed5490a78f867d61aa6645d73e1a76edf9ab3b54fa69b1f3a1ce9f067bfba1ad45f9a1872f5874fc122531828732ba1cfda8b5728d3c5932c7ef86add47067617d1e17c0f0123d9a7e8968a841fdae4035d4fea88c280db0445d7ddd0eb50357981851c105131cbf44350c612b420f3a442b607aac7451e35bfa704dab9ab6ac76ed0b4d9e364211883cbb16351666885fe8cf1f68a5defc68ef0e7fec501b3b7cb86a52e50f926509fa25cddab4ef58139acab8420c13264c988fdf8e05db67a7825c21c6f8c5109f7ef3a3cf9fd1d9d31aec72a8ecdd458df5823ea2ebec74df68252ce176a9f5bb9b7baccc7284a0d0ae28c632370760c3c973bac6c552851a7bfaa9802c0efd20c07ffa6aec4ddf8fa55a63bf4f3d3ac73ea7270b61939a2c214bbdc9892fead223abe553a99b3c5c2dce0276ec0bc78a960f5843ddcdda485583d24df446264da93740728ac8d0af95100a6c7c5961313ada2e4555d5a3a6aaa6a4b249df56e392e8dd679e796e8f86bffaace3d95b3d773b37f77ce3993d9a49335df514f53c9ddb2e66676da457606da4b7d73d4fef07973a95e2ab15595bac2b671a2dce517c9d5c8673c9f58005a58753cb4bcdc399f452ba84f403d6b5c282f2a4e90859419eb4d64a0042bde1892ca9d3daa2f2f76b5e17a785b5912e5d665f13609674108c971e7a2fc3fdc14b0f77e8ac8d741298a8dccee60fef8c499f33c9ec9366cef9d277a4f3b40b072fe9178f1f179efc76991d6a2b95ecf1b2690cc5c396002a68dcb871d7b7188f81e24f0e2726d3899e28cd31445d9fce42dd3fdc84813ce4273f43d5a3b849b3a6871c35f93e7f3e757ae43c6eb20d6edfcf8fea43fee956b061f5546784f98db91e7e26e8b7c85129c3e7974a972b9e3d1afac510db883d7513def59be4d8ab45e858e237ca4b634c0cf14fbe92a2e8b7c971ecd9239111b2e74f86fe18524f02ed4fbe518ebd1b4cfc66e2d8e37af09bc6b177c38aa731f95251daf226678fc6e4aad77c4371eca13e7a4b458908bf6d5cf44cce9ef6834b1d81fce95428be60a041ac8b63148806d1295c037542a3fc848163ed621fcee1d5dfc63f413e7d03e7b0e4d9e78c12e3fdd06ffaac0d7f355abb4e58ccb67cc87d8de92e860a1a34bcc983d241a0fbfe9a78dd7317847f1a9cdebaa1ee771ee3ec1fe33a2dec6c673bf3d15a1ce6616da64f3fc0be9809b3a6cf70c787fcb37ac1cfd64f16e2a787a70f5bfc43021315e49ff91359d43bcf07508dad0df5295b1c1c3f9dfed0e9b336d473fa67fe746ed2ae1d62234abb70fc7496b5cb4abf7afc0c7d41fab933294271680c363ee4299a5212d0875ce567c841b31ad0c74ebb6292969f637cb8483fa9c71da9ac3a31c2cf734af61b73eba9a29415a3674f2dd59e7ee998a5b0b46b87b84c79fe30555453b376d5e7a467a1212e491fa6949e838290c387a92fcf736fda158ee0f3ec2bc6b383ad1ebca9277e53523a15e54bc95e8baf007869163ba79af8d4133fbf5491fc02a0d4a00967d4f53055f42c01b0c1f511aa241d4de9e18a0d003ee8ee6eea39624b2f3d47acb2b66bfa783f3a7d7e492f7b9b37ae119ba9354e3ba16eaccd9cf9090294875ad68d5e16ea670928a5bc3b3da7e5f46dc648294571ab7669ced3773bcaa98a81a547b934ae68a494720c51bef6c9152a88ea136314001895bde7943e130537efc6daaf93afaf64d6e646083e9e962523828f2c797a69828f53ce2394d229e56ef45dd39ad6b4bbfb710d4aa72af7699f2524283197b893b5dda76ba2d428a38c174d82da0acc775d03c6572b168bd500636f85373860195e2c447893ca08d7cbef9767ee619f2d53bcc94475087aea93fba97a39bf98e227183bfc0463f6d39dc91738ecbe0ca004d38a3e54fd091f5d3be3a8061f7d81d0c447efddd722c1ba7907dbde545003659545bcb367203c796915946be46489357c0802119ec0208c97b27b29a5943dac50f161cd950f7d843f8070f1850fb4c8404b144fddbbe8e1a9835d1c3df596173378ea375e2c3df500c8bc04910200ce142f5de302cacb1f6879e93dba20b25640280856a6502386c4114868704129a534a71f60b1051b2f9d4755ba42db51e5c31e3e3eac711941f1d4575be4f0d47d8b2a8c7e9efaaa0b42c11960b04312432c99014689d0162f3b4829a5d4c1a97a64f9b0468ba7aee9f0d46b16264f7d9505e9a9bb1638c8c2064f3d078880289c70620635c8e2450e60128c20c860464a297140b15750d7b010f1d42b16242cb678eaaa2e08ddad428c31c298d2e58a1b80895f0c01071d4aa2b8e20a0d0b262f7da60b222b481bf01428073a50234b1757102107183d22ead14186a7df58e9323fb158ec860674f1819352ca2a554f7dc31e4fe9873f3e04f274ab8c508518b32733242b6eb0a2ca154a9efa5611c5534f15a93146aa0a255e4a8f91415e745922e6a9dfa04029a574eb6ed70591b30aa0498217394043c98b95a39e0804329456fb94524aa76f6b7445e3a0582c868316bd7829fd06051d29a5d4eaa90b329faa6a9efaf8d1830f81845b85e8c3b5f2b4084f5db6cb004f5dab519efa8aba33f1d4c129bc3cf5160d9efa8d15223cf59c279eba9076ed1015609ebad605a13b53ead94c61bc749a84b5502a639b21f70d78e91a151fd4f0d265f59bf85a201031eba189227cd97283d8c51092361f0438aa2108b6925ebee410c8d3a2a7be1af54abd15d3f3c09ae00388dabf73a29cf3586473ce91a0bc73249b732e3ddca4c91dfbf4783c179a1539df1c49e72847c2f9d639ca8770bea1bcf32128ef3e6e73ae8b007b39d4e9d4c335e27161714e1e1d03eb62c082d119c8da44df50a6afd51f78030408c56c8c94925c9ceed74d5812b238351f9d059d1d02f8a09a7c6bce0045b57fb737e650ce5d10289c73f75b8842a15028946f1724a7230b424eaea1b62dba109d766d93066348a8a2b687cc71634cc7749ed331d3883a4a8804368e3a4ac8e9033d4632ea94b31a6e9190188b620a3544139a5fa21e29c07819e19748499526cc84c9951db4d44085ab616527050b4a426469a3870a304813cc92102bfb2d4da2736cf976d47882294292d8c5971aae08716444c31195ab5aa8fd5119b277bb77bbb77bbb9db9bbbb5bc951d2168443195fc0c880b38163c79ef5a045e968e7070f8fce8ca7d65a797476663a3b3c3a3b3c370db2b71a64d6e1d969970bfc5a1ba0f8d0ebc686dcc152116a1c39f970e5a4872c86da0863c41361a010220b16a2243e59200d81a1011320303fa06109498911db972caa0c855182490db498f925ea2182c915552c99bf443d3e4149315f7c51228a6d1182131e6aa3890f5731499dfd7005155d946c21424e01c66c8a2dc89892b74fbf443474f15b98a94311ac84012344962628986428b025727348693b752a842953ca4ebf707a4296db83ac6c8ecccc28b48b59fe6817110d3c7cb8622299d8b6464ae93cf1c5f513e95c4a29a59452cafef0e606885cd2a094dbe4780821633af1454369e847d302e7313ea4f3d487248524899d94778e24464a27125f33eeabbd599ceec66a1539c6935a2a3fe75dcf62e0df5c07b57d1ceb3c4eba942ee5b7234b9ddfb7415a532f663e8e8e889c1ae5454b91c8919050bbc25d8ab23848aceba641230b3b61a5c8889400846a24fa682d2eeac951ce421a0bc5578e905094b53172c2baa430129b630292b5c0a519d5210b27381c51654e188807557393170132dd008aaf1b5f2bb2a2d66ab9a7c33ad8d76e0b3676170cb438257c7476b238719545a5a207ab1a7ce424baf8bae1f186e3702433ae7224371c874f9fcd747416a73d7a0bfbe2202a032151f98c23c1e1377c7afb8eca677c0787dff0ed68fa6b41e5333e44f5ddf08dac2138be1b331f658244c3e637edda3c02f1d1c3129e5d00d16f78a8cdb8cac3150e67af591ce9ec383c06f236b266bc8dac8d0175f3de7c7b01dc505e8b4be5df3c6c6dfea3c1a8d3eecd2e05d3f708bfb9c75504da52cede07da53be3587a9be2970ce32be1d7b0a31ce5fcddac8f80c3442f6d953d8e7aff350becc17be007efdc27694cff039bd796f1ee3c5c0618ab48bfbd65f58b238d2638f97d68f7eb113066a31508b815adf8a2f09cb294245dfe1a56a1fd81d5125b8365d65a98ed1a2584a69de30e4e0528dc0c0ff4a79e7610f0f6b3ebcf9ed26e5edcdd27c7a95f56d6f6fdff617aa2438842a1b9ca8f837f1a55ad6f4e93b9ca8eb6c5a47799b3c44a1d651a8da2e546b5a45edeb665927af5c83bdee27cd7b3f233068dbe5bbb7cb0b444a7cf89b226abbd4b8d8a94d58a896968c70f746d626fa669364c926499d1eee929156177573cefb4798eaf373e2cb51d4b9d307c3da448fd1f4c97c55653f10c128729431c618390412026922253a3c57351d6aa8fd9113e69b9b1b1e42d49097382fe1e118d3b0ae4a24be5691151de5330b46bf591b9b2435be4e7b78b39ad4a47d1c9b8934ecd26a47962a635744954ab2e8218a016104162fb1258c90f9f9d8e22fbc94034e9a50529e1423ab466b7d6b6db0b0d05a1ccda31f605fcca5557dc0a0f287ad6f99b0549b07345669714c1fbd531e7a9fb438de474f79bbac5323f1b18bf8d8416c1e360d6b08a93e032cd25c20e5d577625cc6d965be2349f0b793f2fa1d497d7506182f8af70b65984f1df46d90c6ee0bb5ed0beb6a89cfda44cf69d6c223881a72d2c7460d46ef5c50f98785e28b85ee32432ad2220bc11fd63e4e5a587f42369347a9c72f9ce4949778c42a1351d9a3cccfd69b0f22b3f4a8d5ba5af92fa1a89063294aebaaba66a2a813cb2879501a6aa30b27ddb7b2a8d1898b61333c6d99d4e6c33fb2aebed03f828ffac2cfa7c125339e91c8ead8fc9cc86a6fa3c64feb86a8ec1dc5b892a5892b587c214a92044cecdb9b73f6e4e738aa9ba3bcd560cbdf6e3fd084a5aa81ed71574dc0b713591cfe9e59038defe5f2ed0c14bdea71deb6de0178a8db2d2faaea439204fbcc872487e3f06db0a369e186cbf8ce8d6fa3ccb793e3d3725ae52d6cc65b18c8a46e8326ce0b6f9edb422d04ffc60c0e6fbd5d56e733deee0dafdb640b0b5bcec106bbd5ae9039865b7fc3d95703bdd615753d04a9cb783cf1b5a7ae617d235fe7af15390984f55bdf6db52bc69b3fa6f3f5c2193e8cf1d44dfd42235f9dbd30ca9e390a1f013dcf9e7d9e7d7362f56230f2d543157c2a06d3d72fdc94e734c738fb16e3b53c308d9af2ee039daf7fa073ceb78b40e79c0745c63b19f7d12f196f07653c179ad57df3f05052b7cfbf55835d10d5e36a833596ea0d072c331f9aa6692d50080b3e1446cbc723c6d8b6664630084fbb46c09c83b589ce375813a24777a15ffc44b3a2f314238d27b26430410795faa4503618e26bfb6ee28d520addf3ac4d741f4018f12c4ef7a72e85eef9e3296b13bde5454592c40eca4f8e44f3cd91a0fce4d265ef80dfa132a23c77ec5bab8b8a24899d93a31cc9e69a233939ca650b9b6b3ee4e4289a1634df7c88e6db7724899d8d3504e5a7ef080058b045085162b0ed5b45d611454962b08525c14222abca0bbeb1977393029006a3f30a235451ca71993c3a907ef550a9c29b0f4fd4706542725a3744bbbbcc656b83be9232caa8ead1aee8926fa8eccb4d98a3047110476957d086fcf3f3ec424f64354051c34cc49bf5ec3a74a84588c4f080a52c7b1b757d03914ea73c2498c14ce70eec6550a972777757daa089adb97671fd238718f5a6156343abbb5b56832b1b3d6eceee6e8cdd2bb041ca78747472d6137283fed9767458588ecc8c024bced9a29431c6d821f79ab74b2f279e342981683f5a6094abcaa60f985c36689ab1638c4647d835df2e0254869603b80177c39430ba8a96bace521aeca674b9a8358db2cfb2da69518332ae227396fcb2d94a0d5936a5a85d56dad5cccccca1369b425baa98f0840d5b92383b18dbbd41b0bba6c1c8f9681758bd5514757d7182b0edeeeeeeeeeeeeeeeeee7677735134dab62646891af678f6ae02801fd42efa5ad6a0137559f6ecb314f8dc5d972ba59431b089ba6957fa7a5a8f1eed9a6ef2612e11c020c5f892b2582cf604b6becccc31f2501b43fdc3b6350dae0d8f8e0b66558e7a543a74505795631d293640f1ae09aa52e0c0be52397faa06bb6a26a75fb8dfaaca181afc87e6400e306a112248ec113759c9bd76c5856dab4495a886f307020178d9af57a2da523a33730fef05f039257bf49c3c670ccd0ae0b3f78716784ec52d32cfc9cc2c5f88421bbb147a3cb7bc61db9a982715f5a409d414a106fbe3a2862821d41494107ba820541421d445013d3b8a898982b6bb1be5eeeefe18604b77445ddf2844134aa670376ce9196a430996eb6adfad356ada4af0476cf1f0304ea9034c8d344ad4e81c75a9f96a9a474d7a74d30b5c8c73a3976d6bbcc15eb92683945a556d8f5d2937a60555484e1a753d5c764dab75b5720f35ed679465a7c096260dee3b7b65fe56aae9a66fb575a52d55ae450359bb7dbb18824487007f478f9350c0560c47bee312d8466d99610cdb2e8d39a787375f77a8d3432d32a1c49a56eb2e98b3365f54168a14217254a4d65a8b10e1c011110e1489443850a475841cadd177b8472fb42bec1de7aaa64485a493e3d7cd4dd6e8e9b238e9c18b13a2580d49b6343104c68c2446bea34b8d5fed90a8ec32a4e1a447071770ed6aae5dfddbaf9e932b7118cad77755ed5a5f2d57f9358fb5a10d055e3ac7983e706d22cb0235ab80e827cf5701d129f835efdf5f4bc9b151bba34fa98294809447ba3df637828eb304fdcd1d4d5466d9b6a67fdd6b166705fab33dcde35d1bcd9935fa8d8046e73521763853108105a3cbeea88546b4a8d576cda83820ab9a1924454a4b699774a17e492dcd9a8e34a58bcc4b91fc32eb315af27367939f2d64240021eaf4b085b40539862351e8a7d3c4c853da35d35dfa15f3f3049616ea26b4fcdc643238e3c346fac95306523ecae619b482c98349cd2106453e6186f881dba8126377778c313633470370a9a1169be2439735c62c26ff68d56788c15a1a6f268eb18fa6695a4f748ecd22447a7a89121de289301fae92fcb024090e92b010b4d05046121bc4d840c592990f4dd3344e72861041f01024a5062321e634d139a3d6b19db08154030bc688d1243114c60922a6f4d5aab72356416fa332b70abc2cc1648c2c3c309102e628c993a13690bef480d141932eb31811664409c02f910d67bcc08a1220a29e35ae18e32706414b4a73ce394da7d3696a4e4f27133dcd93364d7482e08d0b1a7bbb18417c41aac109d73fc34bd4b41abeb0218a2e8266663f6441225212e5c77892860f609a5019424e89524a29a594329a20ab21479404880f9d65f18a1664c41eb460d2460f0d3cd044df5f57799cacaefaabdd95ba9e0122767011461a51887cb4c861064d6491a38c31c6c83ca80cb54104eda3a861630716d55d7ad4b4d5aae67783f0a1734a78a8ccc387da3951d91f30afb0a28551684e53a36a426234455914a2699a2624a73b27e6a020240e85b13293411882933037240123238a7911625ab146951d597770a9d5e46df2d8a636f1f8536f8d7af48dceeee841a2e4995cc6b441fca244085e3c91e397c8061e3e5ccd7e884c3c3ad8eb608d8fef6fd520b89520a56ecc95a045e56ea150aff145bfdd41a5caafdba2aeb76f4d7fe1eee9dcdae9a469da69e3b4537f45909c36edd4ae793c2e346b3b3992cd519c9f7cc8e6a893733ee4e4dc7764e37a615bf7476fa845a8464d9a0975a293eb340afa68fad1aeb0d5bb141d09a70323471647d620093ec2071db88dbd93ff68d7a9571e578ec391e470952359390e9f289741fb986d974eb0ace84690a83c8723c1e12b67df51790edfc1e12bdf8e86bf16549ec387a83c870e5f7d34ed0bd391a3bf1bbe7908fe8ccff82639ee866f93f36ed43a232333939ab971e264bc9af246207f1b810cf5b92e5decbe50d3f2f214b03738bc9cd6a91524d31a125d5e1ceadb3b9a0ad2355ff500dd29f595bb69004ed470f5916a1f8d693bd30781eebf69970664714c1fb736d1a3ef783281f0b8e651d3d835d79c6a9ab62c1e4f4727f2d09c06a3b7c3e342ecee6e55d7ad89261c52b3b935a76a30ea9055eab3fae26cee51abe17cf704ce3713966a0be0e2d07cf4d6e29c565cea17bbc56a97ca02629c6bf57659298fb5a8fd4222bfdfa1bcf3f6d79361be167a1fbd864ff0d684e89184efdf3c5f9be828af7aaad50e222a9d433535db7a1671c4d506bb5f3e6171742c8ed4a458b662790b2a1f25af3850c4355b87cacf9dc9bb0b32024aa9ec99951144e54e4ed248f944bb76888c263ea63e7ae917af4089abb46b87ba1c3dcbc820fa908fb61f32aaf890bb701163f9e8726eb1b963a8f671a048830c3bf9eae4d521b47aa445ac3bbad060f42dc6efc6e47dd3d427ca656894f782f781b5891e9da7a854ad75953d6d152212000000000315002028100a8603c2b1703011255df70114800c819e4a72509689a35112e3484a21630021c00000000002402432800ec2a808552a4fb95e25c5402877b4a5322d7db62dad6c132e8cc756a4ab81e467b797e7045abb852912568df70f2650b5325d894d86cb1fc2cceaa7f4a53b2411aa121acdd32841150f884341ff71075239aa00e0526bd5454689f81feed7cbe5330f6f6f72b81670d41b36820203c55794f2836f7a796f07e164cb9b3b43d0016f70e564abf27eb4606e107015389585f2baba0c8c52640f1b1faefdd18d9055e4ae3f183452f2c3b66a93c019d85ab8db0c9a7b9b136bb06e96f31e62b54639297b856d4f5dffbb68f348d062a8428b6f611e3b2c76bff2327c84eddbb32181e5292ec5284bc410db3dd79ee29ba29864a41b021cf7446b00d8ac59741a00aa43ec6f1fd360fb1b4836eae4be14065841aac902320d092400b8a798f35921e1d51d4ff3f147acce69c0f460f8396c2a22c5b7ff9c50b7aa5bbf9544aa1c0ce3d95293916278f35f190aeb3a3b54bdfca840eb9f550d09049291ea61c5d00bfe950224b653fb8406dc248996a80eea5ab516c74c6d51be03e63642d4ac53c20bc5601d4086d6991607992d789bc818b9659ca6fbd2fc77c3996afac980ca8c8aa9557e11041852b0db177de6785769f8b49423c0adfcb36f48dab1ef4bf1b349e6df0bf8e8890bd33aedca4b1689ca362fd2474b522b57a577d2f56cd7bb92cbdc466675c74ccb2bdd8689419dd5fe3a6c44d030c386f35fbd9bd9a9ca498b5289ea49700a75f87e22562a6269701704e77a9a7e4480b610bcbd0fe25d3735f7a2a43e078501d2f9af3b59b8a5a66440f5e38471bd6cf69fa3f90932d28687fc13351cef734ae645ab5f29cedc44d8817098093dbbf291e9187bc99e8338aa37af87134a5fc516b17500146cad14cc38a7608757295d269bb159af1fa6109de23b5a714f1aeaeba285c7cd7eb65c39e79f267d9b29685752b2b88af24c37c1a6c3967c726a5fcb6bd1d5fa89f69092be5429f631a294585afff90e75fa52ef394fe4b2e0fe97ceb8aee2e283ce4b46e8dc1f18a9521ddc908423b1c4ecd4bd2acc3f24c6478b9953e8dfc7d5875812b7dc4ce8bbedb54901748eb158e356d49810288b124a72f5b6d435665baf2b8cb75096b319ff67452bd40cdce4294b6ce9fdf5af6be2bfda72adb6f6b9723e63a662172d86a72002c924541e502b94bae59c33286660c22b142ffd1f98446c5f126030176f82b2f4880fe2209090c54cce25b153f5d41278cbd6c77f65b646848d19accd5f525623fdeb31ee6b37a429ba7b2b9cab8ca746afd325db899c31211d89849a3f13d066b1c1c82208a0c293e4baab5a3172bb148c91d0751ee8d6794dca74a6ce15a8e4c9f2acf084aac54ea835ddb951b78e42c56a12604453ad76e0588316e79e630022e155121fb728956d9b76811ffb0ce937023c1d7fc14726302557195794b42d74395a4c6e109feefd1746c751979075f45c15627b13447ca5eb12dabdb048c69f6b57dfdab4cce0937faee28a92691933da5abc34254f0ed4a6abe8b3efa618bdb6c4ecb559b46767514d52ebfa1013ba60cd565893b26c8ed10cb1b2e78c29bac76f7331f8d8bdad6382f9e655b80c50231fa9370ae1970134101afa358bbb95ebd91e9890b60d34a69a7f54ba088177bb87f51f28cc05e7f33352ec8e2f390c6d4fd6460feb20890c3accdf720a74ce331fc07f3fdc30ceecfcaa3e1a1654611724779164b12356a17bb06e34fea13a73e82db1931752ae161b988091310fb0178b06b301acefd8d260fa04902d20075eae7b7bfaaff3abdfdda2f5f5e79151dbc8149ccb305cf86b0334e991fda2892ae2700d445a095ebdb940cf412bfc549df3fa26b572d04c744b619c405d11c802a9ca58bdc8de369d9b0239be6a0506777a896fcb6b24345cca61cf87bcd6ef55f86ed6970822b2870f1310f08bc9737ce0ee6d2206af904c10fe6797a711cd8a1e250f42deb59981232a19f3dee4632f17adb33d5610af42943df892e6ba7aa64d4e6bb62fc6a06d12843650c310ac4b2e029378cd6144948c0d6e014d0bb8cca0568e591bf220975977724d48a3de1fd542f5357f4b3de0dc2187d89d4fcfaf6a0f3358bbc9d93149c1b463199cf5d9f0b1d90f2432f74f1bfd3b81f2709154832542c5e468504ec664a982205732bdca8b6ee4fdd2dccc7bb118c72306f47efe4554c98e90d8b51abcf094501279343567862e011460778d1bf114dee3585745627a44745b196b9d2276a810a6b327ced8fdb186845f59eb566201a617d6bf523c5ab83af73c578487239e3d48c725826560b7f99c623237ec9b71508160067a13bf28138c78d9bd65062c827a3272ef80fbad6aca177f0b5cd6f173d0277df712be1a44c2b54cb1c9e29362219b72baa2e2b457f389fd05accdfe0a589c31e53d5c04d06bc65a6e8c10d9bb6bed9e7224583e40805f1304c251b8d4bb028fb74a38e71c22139cd09382a323dc625e2390dee832172017b7d039976528f78c71062247a796dd27046ae42a3ed64270406fd0ea1dfb5916522ac5ea2ae86af1f080aa94ed907988ebd543c0a545c56644e595bbf39d10528afc2255ffaa7787730956b206f3db977fb6aa8b342f56a79c94f041cd6836379e8805b65dee2e410ac10f95767d87225c31733938e818cc5c4b469ca7204c79d72d682d32036d18d14b2c8f656e92940437ac0bba5b916409eeb5782e2d7951b9f5b708784f608ba7a00b2a335e4c050d251c5608ffbc1be4948c24e4ccb21f065f1c04fe681901e59e6492125ef744e2e98383f2fa442c63039cb224a06a128dbb4843142a17637ebb289380cbf280e12fecb68f4339407b8da83d45632c22dceecc156c962840235d25c4bd3b4edbf400115dddfe5f39785d87ab2b8b32d832406289e8c4b55e31d6e9e3ae1f589afbce00d42e944f91e34dfa49712abcbe3ead43d6d9c0ff44d54a31d623275ab89e8f7eb21e9f1a384457c6087ce273b46ffeda4568a9ff255206e2d9eea6d89a9feab252cf05c848cb4e1666cc24a5167d96850a33ea51fb387b966c434179cafbf7117eb8452449874cd3bc8f116209570c5544c873e91c5540c39a0f944c615b971c1b8bdfc4f7863a1c2a4792bd5b34279b13594776348647bf43c49f5e3533fefa0143ab7a838913f1885118996d9e01b25aa73fec0c500df6ccaf657be6ddf4617b83efe0e1b948ff2f75e67133fbffd7bfd358796c50c58628cce8985da8600fe27de7e01c4645be40fbb4fa36965b28586d7230c457c4a0ac0d50f64a8f2b7fc4befa72fc3202dec915acb054fce64d020b15812f31ea363e602b88bc809982841404bdb4373832e52762e490229d0990965642a84585c504d8a7774acfc68efe24842f97392817f5e8cd01a407220dd2ee666866a95b0f56404542cf2cf260fb33bd1bd503a0345ca6f8795f256f24e7d70a9bc3dc6b74663f0aa66ba497734a4c42f947bc7d920f26cb9409dae8ae3a50c4aa175f286e158b49c779cb40ac970b41ce465d1400ca9fac15fddb8b003bb716775818859f1cde3d5825ff9d3b28bdf58e08300b9519a4746b3123a84c0222f70ea6756eb6f2efaf2c31bc12349e35971fa222c211174177709e20812fbad902887a647be6ee7c8e8cce768f3ceb3f31a653e725087fd234d37a3f889c94ce2bcb3eddb26cf71a7fc6a638fd3809c7dc2755a6f9a5805df71be07364dd414864ffc1cba49ef49b1c4891a395cc0430d12e2b63bcd4184a9171c102008010696f7efbbfb72fad429475639b5087b270501d64373b3bb86edb8bbe381f37454fb9b449cf75d3b0526c0865a9b92f1ca8f3a0a5fc1b605bab481afd2b68d415d0e33b083d0faddec5e43fadc4482c8d1a42c0ca264835adbf5a3a1e87d40df4f617ae81ec061088c0d387e6a1c99e7038cb1bab9185663156f827dcf500cb8b91ed11f1d7c212219bd1bc781bffb1f131c65f40b787e223cbde936c2df7c23a941c94cc768303d25289c99ed202f63b0736fb0698abe47cde91c70b3e9d33a5883a07bc9859171d6b7db2ff157f737dc83826bdb6c8ca1b71df1ca807b08ab2429d790c91b5bb7bf36d79876b393d4e2c5aa608866d26d48e9262f09206188151881afc08b93c26ad1b1445730540b221edfae68fe16fcd8b420c1e48fc0562f9db0a2eab33de100e333dfb4a0b1a55262bf09534e07795483f5c10a5a46912ae088e205c88ce5e1a38a1c846571b2032e008849c5c8324e042cd53770a519739f7c508b37753f91105e88008f72d6a8bf6c2027a76b3b0b0280965c8b8f817c85f9f889d5464bf47a6e66cfd8a09a4721ea586dc82697aa6a581ef0c7cd76302fc6e1575c551109d02c3460215a800fb27030695a0703cad545ced9cd18cfb14e71e80dfd89bdf75c8df576c80fe5d9e00c41718425afabe6de4d3e61f180e42491a7a909053c959f70659f813ae9c3af5d34289cbb39f52fe57f6b39de01f9b9e761f383ff6ed3f6bba26d383c6732edde05b97e65aba099eeed9331f2fa4b7dcd34515fcaad39e19c2ab9139cb4b274bb55fde5324d5f00457edfe73fe683844238b3c17e1c79acb2d7e5401c27a915cc57d1e5b25148d2c7b5081b1cca4175f4c5dc88094ca5125b66e10aa80e61d30e1c632818d34566ef28e46780ce21c841f0a599986fda3b4d4fd7cdea9a199d0174ad401fb74cb49379903254b7ee5b630b32e600cc44cedc0f53037a8dc0f7b3fb15c11f75e270e8af42f7196d1b222dac9ae3b7da27fa45ec9b6aff45ed42129087b66da5f9e659f65f45f94d2879dac8afa01129d0da1f511e919ea44475e612ece7abff5b3deb8c484ec93e9c5178357ae7128998e7bf83dced982d0c0287897bc817fa6d9bcc990e0433bc02f9b43b17a84539835d99a2896a6164fad2c2ac160fd99696bf54434f011ecb891ec7108f765db3d2c4541d4d8bfb13910f057284b6741dfa0be56c8c7d8e52ff0c20a9d01f5931e82cf23ea3b0aa764d32f152a99e4b5b05fa6104837397f138e99f3f82844cad4dc16f429e0e0b7ea3e1f2d9890390dae6d004e5a43a76e501e81f8b077774a0f6264f74d213490281f60c65ca604143b3eeaa9bb63ab4bac530f79d5a0ddeb7089abb754fe6755551efef6432aea5ee0f6ec2e19681e8e676059f5428383ec7f456040e624d1e840f31fb1811b04a7b30206b20afc9a62aaf65400585099160ddcacdebdb556c6d13dd34c003678b5618129ab3b0c355446eb4d94e00a69730bec25c3a20ed4624889fd2fd59ec8f5cc4a34146fbd179227e06b607d7e0fa0100a2ef832ab07358ddc28af6844bb0aa3a5639855a5634c08e806fddc225c981b66c378ae56ba5d70e160150618086a2368e2b262581e50fa1b29a5fcbc9bc5dae05ad30ae7c28d1d9b85d6ad4994e073ab74a50c13a6ed2e5d7b33af534f04655322e3cb63ae8234c1e6161a7616a5814a0981816279488a96e1551d5c5342bcad5bae2998176451ff42a8de544d0624f02830bd5d87cf84c6acb4dbde9a63e3db1fe66d9fb84e66e096d2d35b4595a8f0a86576f7b6e98605447f4fb643774df77d595032f9596c79449f36a5de9953959569ca84e8f5b2733f6bf08386a0cabb23d56c9ff6742c5e2d3ac0620e3677fbbf8f52f37b3956231fc64c19279b3fc650ec3db12fe4e2d8962179e80358f6e76d76d6c53660e8d279b67beb98a0812de38c03e8574c2610f14bb08d89e29987a072864a53c101ffbf61ab18ea55b3eaca7b3ade282a63efe5a3175bc2b3f09100ccf1c1d43584d432a4368b5d2373fe3971f2161d46dfa498b7b371c72fee807bf5c2eca052f8e6a40b0c9d76d47b11e858e6a885a8e82980cc7cb0a6830e8f16cb644b4dd5a9edee77db4c0b4ab352e3570599cec618d498e23e7152bb4da0921ed19e168746beee3abd6897a3f4883046e85fc3eb1b8088b8e9afa71f60b37123b53e6c34a08ec60be102f7ef59fbadf8b87881c5e7c803758fb9c23d577560c8e44244a716b17d27c7cc6a27a90acd1c868c5e15e2f051916d7162244af5758355ff02694affee61386b05c5e5efbbdf4238254ccb5b1e84a873d9e9d49d900df30fa2bba1e586b20a149d087dfd214e553920c61d339f8f26d483ea53e0f629bf88322bd0027c42816415e0eaeb3a33c063a795d2399287c30454b90b358059dd792f47b7e0de3e5cd7088f9c0b266d87170b3e5c06c1377ff203e31081fac0b59e180d3a2366abb56320344c66fd4d988b695a9a82b1abdf700d78a45542170e02a0f3a1a6b6b8ec70e5af774501b437c92e14dd35e057637ef69a7e464f25e530885fa7c4df7da5cd2d4ae0eecb16a8074506c05dac22de0a84169a7e8e78194f646684cc99f5ee8253edd0a6351f4deba98798357341a81b03f8e0a81237385f6458b44aca09e2507ddb7e17cc9d29cf0855f8ca46124cdfb0b6fbcef933dc0f2d9667450b8da186271b658760d7bb341eebc1eb0d7547435485ddf74762b7bda2715a14b25bde8fe160a485a69a79e717457ecdfc4a6d27e3c2fd8909440030329e69bf91e4b88c2a34eb9b20fb9756044fda72ef6765e8a2d481fb0ab41e7c501d8b765c20ed5aa5e8e99930aff86b0ffaf584b044432b93e6ecc92129147f97e9fa1cfe02755c74a70daaf2ec1bc4f02b784217197513c68368d578fb05b49861082700622618196047ffa57937199da5430b9abd64a742fe062f4a09cc62e08ed91197d9dd3f9fbb66769b53f1ef4155bef863362570c14e21709c1045259418b6a38ebb7f7d0cef00266c660d88677e33eba8e346afc9fd9b9987af6e227bbb525d08e5949bc667e3822c21cf696f955eb0bd4abf2cdd44ec5a19599b91e991d8692ee54927079e5d2da0b6aeaa05956f86ed35824e603a965a7e87879b1c468463ee02afe038a6217de7e470f9d06eb3fed212e6b85185a323363978ac299171752d3f71f8e5ed114c276797cea9c939db076823c7cd1e43ad138534a31fac2158f149418137e78c9a2a999b1b8a0758390d1c02c19f83f7606d9534999d731109ed88c46a2fbae928dff18a7005faa4fc7f906c8907d5447131213418150cd65169a9222e75d292a236a38738af53c6f9cd2334ae16e94ed185e8cdc65490f30fb7340c8808ed09e43bdb5902c7530b1cc75b01a89e07457bd87e3dd577965b7ecfe7334401e27ec1bcd2b2d718873d602324a22d6bb391cfd8422e1472d2d130013c2f622aa97ff1788ede82d061dff9bb1942bf327cc1793ea43f14cf4f2dcaf3a9e58cfdcdc225b11f3b7d4db6f345154354f4a5e9e164c5e3fd9d74b22f0d9bfd7cd61e608807b8c43e60f9ec275c484d49ee064476f0f63dddeb761ce8a23950683d6bd04938e97faa68a191934987087afe3eebb45c31f7284be837311e8a78badde14be6e32f8d05f9cbb25e702a684a327f0f26ecf6f8829357a18e1c6ec400c6d2ee2afbdaff08c5dbe7dfbcf0ae44cea24494fb2af785054ee380974440fa7a9de9e43fe0f430b2ab8ed2008c9f61d4803f855a596153ba1e4c08c508008be4f53b7d210e7048b618b0fa4a09e55213ec64f01968589293272116724be2b4fddf5e6eb8e4ad01f89ce03be24bfd6043c3ce5da58ec1360a5fc22191c02b4c11de47f8bda7ebaafab0a99257cd3ff9d74d8466d9b923e05f167072cb48df748638e5f770cc730aefba7550d4e36da07ce56cd81f02743af15085ca23ce7b446d317eb9d66c9c2a7e111314b743331feb623e4c0f8976f686ad85a08b8754e761bfb8d722286358db2e05f782d3fefb6a9f09bbea62c7484a032d6e0e7d7df3a0780ab127ae914ea3608fd0447230d2aa86fe9d74943a6633414cd45f34c2b2bd7667a65756128cb7ceef3ac488a98956395f0aa423dbb3d0f67ab66e0f100a44803a72674b708ac3e21491c93c861bf8613c5980cffcf6a0287433c42811556c2e5aea46f7feb515a9c3b59a001c662062d1244f3929349bea716973c447a8522df25016b1accabc96cd2660aed5a7ee4adfb07dddd5b14765ffff875c08829810f832ae60bec71d4cc0f6b725f2aca084aba5fbb723109593a822defccbd14b780059f10a7ffd11c0a223813aeb0128744b067b2b26dd2e8081a8f5029922be06180426a12ad24ea1edbb9d800eb2510cbeb874ec9603e287bc0740e8ee23553ebb21cbfbe378dd723308b1d5cb401dfa10361d79dcf24ea9ce298934a3acd06bc29f9b65dcc047e21d8233970a25daf91b1162aca18a89d576024aa59adc023fd1ceb1e0f80310940a810b73e3186e18af795b38a5e1883216a20ead6efa701c85d8d3b96ebf0424b8dab333833a3912904ea94fce5d8b1dee9e52bb436453e445f62e47d040fa7d6d7fa395fbf31d4440298c7bf0942ec73bd1f2650166aebc6b82030cb1a448a70d2c3c5d34e5e3cefece78a053ab24a430929ecd644ebd283819339269c130e1ed2eb9b14f946cceed5a6b8b81e165a147bbf047f92a02f5b90e67c8ffd1cc3eab31c136a325c67f89a335d44093181bff05dfa66e43ecb800f6ee3ae898063489165374f059d133ef60ccf8ebaadd88af388c06c0b20aa9848b853dcc8b3ee76524a59a70be573095c428c18631b4874811602933942b55b353911828a2ab041164f63c3cb1a8d4a0faee8ed2e835d08a350c6a6e0da73b45d7e5891c91bc7b336015e6ffb485b5c6c39d2c079895fc10663a50d4b54689f32392f17a512ea781002ab53563cd5edb6f17e8179f621ea1648ff2d3043538f48bd5acae50e8f40e8c59cb93ae58612b75e44ff7f982f53d08dbde05c638328726fbc57a271bb27e976be6c8c237c941fb8d6e003e772c44d06f10322adab5f7057f84de73ed289210543ac1e8d61bca04746fdd2025cf5e8e756bb065bdec5f444e2e665557d84b540982c6d8cbca1052ae827bf9857b172469fabd1ce5edb5315e39fa5ee674be04db51c19194b33527c1435c8e3e915b3102bc949c61a397e8367f7466bfd9c339fa2553b9c2712e93f0f80665ed62bdba01f147b607e92bcdee5e99ea2b25ddbd7526d76181e306fd2596b56bcf3f74361edd5dafa42b8edcaaeb3ea43072ad2ec4ccaa8d8b511bafcb663a97c457ded1482e79974851bec3fde53f24340b689c3bf9223a779dfa30b4075d5b4ee5c4dd4196ed311ceefc73ec541afc733546f16ad1cbd467520469b180acc12c337a63c318c7c45000394b10e2b5f2729911db28a70f7b23de1a6652aaaaaff025ff792a4c3677222ffe055dae072597875220059fde7192e859ccd9c8b0b0b7019334654e8c42aba7ea771806f13cd6969f8b3577074a9a24ba5f393ae93ba09776c1c3ede2d7859884a3ee117af959ff1b3f0bc27eb2cfac902ce9784fd2e00fc14ef04a1b547a464a765919f1a2688f5b1453f95b491f18c9d9b2f8878bfb88611f9efe9de432e95e1eca1351fcff06fbd76de318ed459113a488a46fea0cde8445fc48650e46caac39c01cfb07d94c7418a84bfa00eb5f095c45f08338d0a4e5576f23b71e846e05a422e252a44f1a55b3707fabe45bf16aba908f824fbb9124c5d29bc9cac674b39751911d49b1dd7b58f5563ce2d675910f4c7c78b065cd4bb1e036261960b4bb30b5a93f4b0cc613dec7593f523a8dab3c3e113027553e11d846411355f0b080e2dbfb9652f132b04a7e883b7362067fc7bb4653250f77e1ee98b4d75462af2be22873b2266ff3199c2e77434de3c7b71fccdb78cc82448f864a49923812b9d18b8dd65f33376280ff989108684b31bf8a99366021f39a7170b70537922d9ab953772d17c291858d2c62829367030dd94fffcf5a0bf1119ad55d52cb48437110dde5745b7bdd944e4fa9c0f83d5d37ca9c83decfc2e0508404e217f7cc459b73336a17d94cd8cd7e1059160231691a1398a5af42c7c95123f680fcd59a57ca08d935c80ad86fa9a61812aac52e804e613b49cef4b9a16eb8ba1ca77867235a4b38190aa2efaaeca564ebb7e55088b1c81050f841813646f47a47e051ea2bd19c0f586cf467b23ee6db094039afd1e609d25c808b4842c7fa310ee7d35e5261d27610ef6976888ce064c8005ab4a86c4eca818ceee60bd85d110dca0e463772fa48c390c11bcf52a91ed79da2620a55f2c5cc78d5a730676de0a2e2fbe65ca184958cb7cd191569266aa259b1619a0d0fed1b5de08bea0f84ff26874db511c274f1c7dc8ca10f981779ee57255b078ac71992ccc0d46243dfc391f083105d1b170070d8f07b8a605e206fd8e12e9f0e56442e61978f7723fa3d2ba2e274244475f12c5eda44208aae74a4247e5ef5ba51a22e9abf0137dc87b413fd9b6ab3896fd1827eb18d21114798c1ec351099763dcf370679e5923c2ba485420eb94b6b7a3977cb9768c2e80bbf6340bbbf39e2750a508691f7e8f7767adda94f952171af6df48907216bb84e9a7dc4da8039b63fa85ee1d400d0d16b147918a2291f40d69b4759bda84c54d44c94b51b0816335dd16af028d226a4d0a2acba998e67c15c944674bd22a2a9527391b535d925c30e595edc8c3198d0521eed115025f580736ece28abf8dc3b1a27623ac00722d3fb8322a6be927b4b82b37e4a919518898abab14c30cc9ef2af219a9b21bd1e040a15d82aa6ba6c18ebda9271e0750d42f27ad683018761e4196adfb40bdba6930c016187617a9c0ac518bf9ee3f21889f0fe402b77d5993761332bb6b0309fad100640c59305209be186ab8b0b1f0aa3c03c0c7d25273f058879bf4fc3965129040d247ff7b697328a843970c86b6182a4edbab4e4f1aef954934a90ff8e45ca6053cde370d30a2485f4f50b13e93bbb9462d6b277c274a743f97ab5a0effa9909c998e5d036d99a7e5eb3198fb15f4d286eec617f2e6f5edaf47b3571145d0ee726ab0dda81c2c7bcf3d1a66226c6056084c4b6513a5d08ffad865d39819c1e354308cd0978d78a7a6262dd941ebcd232e01043e48040519df68f85e98b18d7be3608e618c9851e42ce1e0d39a8bf388d3a95780ff8f8d5c6c558262b182f8b9fd14b8df9d09facd8b0a0cef1c3436b1fbd34379c25342d89e6446ac220b04e10f15ca07e52f6a99ec06aa1f80b5e7c47cac1f70e8a90cf60b8dfc2953d62b8a20eff90c1c8a2e65453954f21634c56d62d17e67135ae13ab5b05a1dd441309ef81ad6b9a5c599823fdbb1107641a6273053a2eacf1e5de558ba72079965accf28fa5b09bb8d48464f830c16aca8f4f3c08139c90a5013ea3956b9934f8ab72f987fff7b599203f6d4ed67143273a30e987281792ca7d508ac8da38a3fb96eb419638c7b3eea6a36363a463cc1a4195eb82c697630a87e46b17f11f2239f32b231ffa2137cdcc20dc40ee6e31ab0676c24902151864ebc0d09526ac3903869ce4bd0a264a065cde78dbab18fcc7c4133171b1e69bd4e57b4877701326b13d34da52ec1947941ddcb94061e6f68237b3e40c7a52ad300f915a65e0b64d81199c5602e82cf477f5c859546770b2e816c8fe5d03c58498477e804df1da3574e6c5e0c97226d8c92d330db14ccf8dffcbd27b51b0dd3bd450981b7ad5d9ce5cab02b9ffa6f24f5a348a3197fb6324eaf86166abf238488002f018844338c962b70d8750200672d098391d9e2c45aef54e09d91174bc02ce6d0956d9347d08357fed0619c8de225d0446e4d72788be38458cf6c15e7cebf2cd8811361f178e4d74729ccbef8a0de8ed6c99beb156ca465c1d2291e1d23c87559a567e0a461cf8d2e3f728129fbaef387c7436dd524d5fa166e9d356c86289d7460f0f9944aae5dc3474dc300e48c09f61f409e4f4cdbb29165c2818c9eadbc41e81f9923f702a317e4e06d3a38ada5162e8c899f9951f3de0a19771e68fbab6708b9aeddb16a4dbe4a968dcaec3c407389da6bd92cd0c65fa5161d64faa8344cc680e3252936e9188a6540ad8e26f5d113a5055e439df0a8cffd81d567085b93252772f61ebb019ee97660b77e78d52511a22ee5050409c215e1e67636caefed4f2c90e12fecb8d19f689686f6d169eec4cd0f43f7a2d3e7b84475bc7bfcba39a928234b9ff4aba8bd1debe83e8f927cfd568d8c11711f19610962ebdf78830c9bb44ef8cc2bc3342d5eae70033bb89254b179693a1b5247e6dd62a68e1a24236e8c1101b000bf7d14230a90736270b3ccca483aff9bcb93b41e53e8d6dcb82583ab745802f53a1943b08c9ddc40ff6958b5d8ce4eb0d542a9ddb596a227844fae18bf0ee835761576665c02d878677a180317492444202f4cafcba7e2f86ee301f96a61c3fc107acb7d36684e7ee4efd52fbb1dff185b664fa3a3398cca908b380cc877c520851c9ef3589765f1d3adcf88869a2fda3584afd61fe8a1818238fe59c21c19c4b054b5c0c46b812e45952622b66d809227968e7f50c2d19173a6d470dd249520c74bf1089a1f50306030f665cdb31904b7ca0f534c5c5ad92af887618a94716d3d5461f628a568aa0b5e36d5942d8954d19f42e813340090926ed8fd68d2bbd46688414be9ac4406e28d292e5d42f496197e5848812468d86632304e72571542f1af3413ba296a18182a82990c554d1e9758df5c3ba4ea48d4756c3a79345722b24ebb3a0e6982ceeeae46c921aa6b735a4b69b6936b1a0f2d3302a101155311f3c5e8976744508c2cd3d96101fdaff1b27b72e7d1f97a669b3dbc795292be76735878c6ab4131060af817a794e6b6b6823517d0de6ce2634f3e29137304817c9cfc6181c2c977494f21a749a9ae1838c1e0a5650b4e6d370c0a5fac91b5364883b26f988d4104bd41b0d2e95d344f1a691c970c90837b23275baa872366014223be4aefc2c230d229b7b29921ff665a6c4ded0bbcfc87f8ecd010d9046882c8bec836b0f6461defc16c8999d98dd0bc9e650884fa113560112be501342813ae5990de32f01850cdd57ab967cb5069e387c7c455d50aed64b71047504d495ee22f0bca340f5f7d812e2a1f53ec10183ebc67decce1239f204066676f735180cb3bb0d002eb78814f58ec80e96908ad56d690bbeaf25e4d985cbc18960cd212792e697226835b3964dec804b23c5a9855a57f6051c31f9aa16a1d043f12d13e768c326aa28fcf547d37e9cc2895c122b8df44b7817d33a228585640c8b474dd39b0e751045ac7e0894253dae41f98f222b9668408e0796c61f49cf01e1b0351cce659a875317415032dfea0cbe13a6e4ded76893b3fae48a03e08bab72e1a0e8efbd909e11178fed8351f5d545c1139750fc061ec6bc9f38aacd045c73c32c77aaedf16cb24314d64f58427ee5a3d695d7353fc4bf4e925f6bf1f2431d20927a036d12c5e62e9a5e9bb110d642a0153f0161036e40b357219b69ee27d546a9cbae3b99f24c0e8dabfd1207ff9d14743a274411ced4aaf5c25090970df5501243c418def9e4b2522d41812b45abc0b21280ba3b9da5dbedc76c336100059ac69b2f2d38ec5aa950bea0d898a57e6e5ddcc73e545f538b5889e47a6e0444e41f0ce3f870e5e6f31eb2e33acb21b807e2a195be733b2457c002beb281d879c02a48f37a691823ef83ca4181a346bf7f90567806734ec1c4aca334ab69f1d49bd154591068d341f52a2d650be0bbc3a7a84e2c9c50e0ca0b70cc9b39be6505565d9113587220e2665bdd20a4e32080516585ccdaa4214b01c9849820a3c8871f69f03503b4cc08273d3260c3452bb938775cc9b151ddeee3ee3cc841ed320ba1237d819904410aface7eb71b773abc45d251b755e799288b4d63f0ba373d84ec4b8ad973d824c2f6d57781949c12af5a7ae72ed8bf97745fcf44b588c4582fad6460b562e783dcea0ac4c52a6502981046c3997786703ca5a32b8c9dff99e9e8ee4595212c1e7cd930e4bd69e48134302d1613976fc2682cf184a0233df653c65939ca5a845b674b81fe387d4077f09e42f426e4df9fc3211797125dbd5ea41242c5730b7d21ce2e75b21e8151a7664f9c9810743e50908ab7161fe87c2166c8ef2391c4d5b1987f869f80b8caf3c64a45cdf6160cbf6b84db051bcf87f974209ae7eae64d190fa00c30863c010681e3af6646d57b7c2cf09fe7fae7d9166d9b85235b232733ba64b46f048020e3de84514775f25a4bafa16c5c488f8511b7daab4f03c3cad651f2ac654109de20632e9b452c52ab0566ea0c9835b42195023c8a97405fb38a5d34eb7dbda2704b6387595d01c88e57e58b70673db036bebce155252597bbfe10d59fb4291724721387bd87c3bb36dd596a41f05bf49d6e0f235905b84208d124358b994e1d438c63923a331a308e1794ef41de6dae1ba8522a7a35bf78b770475d70cb5979f97c194b333c8b0db7db656519f855f55c6e4b68fa5608e9f948cfa97b6ee00ffe50666357d4ebf115f6e2c9b4b5341946c564e3d9f31934a326bccd07078f6c3c807d940bf5af88c2bde5ea59d7868d4073c1aad997d2b6219b696af9ac8eefeb368202c8b10e824f4a59ea281905da0e43d4c3356e3cad4dbb8286b95271b63a6a9bc2b16fccec6ded99b89304c7517e7e619426de64c61b4bf08f266a70df417b2627da759bb83ea4f2a18c148236b1a50a6cae115009fa27b1a3e9fcef9598dcf2a585a49e24b1afbb842a7e3550280c2c3d801bdf0fb264dab30d091e4f9f74c01cb89e8d0c9b85241254b9ce330f98b90e72450305744e8b05d1b854edc4d2d4469e3c50d1ef870fc5721da6132c95cd7b0c44adc4db039fcdb2a1087f2d5058ab6d3b723a7a145b6b02deac6a7b069b0c6cc61e7be7fb9a7bdf13505bd11b02a57212e08c5333a2973bd62d4436b5640be6665c269580582b868fb1204008e6e292a539299563692a650a319c6b5a426f4523ebc69afc74b334999f5f71bdb9785d3d59fcd4b164e9720153a852d8a205a08f35894e936f460146ffa1e0715f56283e7216d0d8b3658bf3f0a5cc0a7abfb0a69d6de130d801bcc1306eae3c2219d192065ad138a08ac57fd51ed1ff34e2411be2020d63d83de2d2767bd0507763fa8c0b574988c0a67b5efce75aadac42995e65fbdc1aac7b47a98bd4787c15820309e39328744c08123f365c2a13abb04d8d763e01ac20ab4e5b2259bc6a738ed0146c0a87ffab50775ae16b56e3ff714957a33aec254a8c86a3424652edbafcd9b0ade51d0a74b4d3f6a56b41cf1bc4b7d6a584d4dc8a0ab831bd7121a39a79e974b172dfc8d12e8bdd0f136bdf40acf712244da352f5c61962a94e8b2d44bfa81bcdfeeee22e24fa8743f5ad0df096efdf172249247904021ae8efe15422900c00c3eff20a16050f83ebcb5d3d02c4300e44aa52f83a0702306dddbba84209b96feeb11e0498264cc50babf4545716102301459c9eec3cfeb4e55cdf1f3ea903f709f2f8f3aba7e3fda513c62951264f2028089a7fbb8b409bedbb3a73c22607cba5f646004b19eb537e9a0ca6893de572c00633487a8cef45870234e3406f4c6e2f85ea5d5559a277ed076bca495a8b682fbd7e40d346a8c53cb723158df84a05b5df3e69dd5d0c2ecd3f5d20b37389ca5c45f763bf77a37b5d274f0d43028ebd991186aac32b2e565c4081e64068340485e9a16ff12eaadf5684a87f31cc17a27fe7ea86fb6c055429bff042975cb51aa8ecf2af6ca113dcc59504886cad62318161d8542d0a08270136143ba2ce4570f81fe09a68337b1a73a50b372143de237e4848299d755f9e33b7e681c8ebc993ba1035a7c1407120bd63715e9c99a529cdb3b6980b756f19b0acb850ef62eec5af6b38482146c54847c25b81ac1989de646f191df313bb571628c2b08f2e7f8d7e710f8e14faf4c48874d40515d211fad5becd201d060e9518c3f271f1208456d86affb134294f9235fd3f6fadca72e835be09f727891ff126340a2217e746795df2ca29a340fc0237cc9d2a5fb83a682ec4896dc3b34b9abbbf8fcccde25a0a22dc78fbc806fc54389f2454c2cbabfde26e6ac67dce8f592a536425c321aa1f49a1e2a05cfa708b903b6fb1109b01970669e955668533c3295e107d65dbfc2c26c7a678b4b88c3d3bfa8bfec9dcf7cc888cf58902a128ffec68d9b006d89887836a7bf32fa84982d996b94cbde669db19df1b606ea50f23e06fd616c900c1a04e7800fa5dd62160043d6a1374d043630093a388f014d26f7f8428e6644d0b5d0825a3fd8155e2ee8fec9add6e992269d148528a551552c4bd45daabd0a7621f5c93e043d6de44f2e9a15e5ff19b7201424bbb4037c4e4a517f7da55b45a1e43cd5ada7b90115d7a5a0020ae94a5c08d5cffb2100d2fa24a8b889aee275a582966c51566144948454b70c732ce4fd5732aa6f19846660f3f8ebc4fcb471e5ac46d5e726341551e40b6423a158138cf32d7c4f8faa72fe42884a7a11ee538e21b06953eeb2b2d42a459368ce074101650f4b4fd9e29ac78aa0413b03182a0a119d9fad4a37fb7e1fd39a8eb9f15512e7217a21b215cec76011f2a70b84b6ae3f6763d3a9cd0b07397f1a91ba9bf73582483f4890b80ddbb3eaa099e42b7248f2742b92b89ee7e84e0991bb8ce15c58aa6b0d478cdff13bd04890bee0c3eb3d352a2959bc0b642c5cb2daef458844ddbdfc4eb0863e88c2d462634de9012dd88bdba5f89001b64bef1ab18bb90c06ad32bbc640e07874e96fdda94dd77b0c5b7c443eeb3656d3b843ecf0ed98b0980568ac67bc0ccdf39d8e503892ae348105070cc046f3631185873c22f81c0c0e87403d1a72e4c91af386211f8fdcb26e1320966ff3216ccc2e44c54d0c6041d9ed3be79f1fef9cb68165bcd58a03f410ca2ffed909a3b331377b40f7785b42d8989db773150ac0fe839e924e0f1239b7c9f9b06ee03c1f4fa2c22fd1d16eb731d86aca6c5aed6e8dd8d151bf44000a970464880b33678be32582b3f7e6f182aa95e97b50010cb34fd642ded0ca23d708fb364f7ab36582039b7a34cb006670fda09a2c1f5bf3f0da2978bb0c8395abf5cc7ed676b2ebf7227f52601e27144f54d6ad740c3e3c8fcf67bf7a86f6a62abafe7729952519a01e5a01b81e9a57f257f38f310f410e322b9d07b800a0aab4e3f5c5af2fe5d0dfcfeb2046b7b53485b8ac9d70c600683cd70da2a6e336a867c084d4c55101716fb1b0cf9a37aaacab24e7147517d9fad0e72a2dc8b63c488260a6600c5582da101d75626cc85c34613a4f2cccfc24863a628fc47a5c812d3876f501e94e09695e05165a298f56a8d1043a6c23c06fb0d3e0d7c81b9dfecd43a535e0adce36743b5f2c2a8e591fdec67849249acd45339b77037e5688859586e2b8527686648ecba0f1ebcd5812508a296aefccffe1011f59eac70ff2e6cca1b781e26651b9a8f0a853c51ede9a0060e189705abba66312a9fb5c743aa0908816491822aa30b298491b7afce5489a7ddb4663904376656cd8e8cc113bc5f095092d9852382428e9e6cdc6112eb124aa17a91196c97e53bc66d1f7c1a3cb96efffcf8b526317cf410530512a661782375212aa73405d4543600e5682510d1a222c60938144c0bfa7c36e0c60f2ca2359c0f413eb31059e45580a6ab975835f3f897364c787bfa60ac80b6dcd2a7a09fcbeca5d8c4a57d37c3779879a198a5be3dd8dc74f5039a11360b9952eff5f8bc0ceba9effda60027092c5ada7912d9e8ea9a881c9e2782549725445843f451bfd99c52d2acb64f7b6d07f9ce6ebdb474ed9dbda55825801dc21336196c48faafa2068d9b8e75674e94b54978206d24987a0197f12e22ca50044e41d17f7acf9f52c59a1a51efb1f082e99dab7be12a57115145b8cab507b976b28105340aec6c0dbf316178c64dfb384d84f89a525256933082cf79e9849878ea29b1c8b30e8ca6167556eefdb01dd31c70868dd791427b72220722f513321d3360584f13e53718d8e5d4addca8b92a3189207d8c331fff6bfedbae65a770ca88512cb6c82f12f6c9d9b6e109873ee17bc6f2475f0972a17b5eb10275817fd12677ffe50bbc82f6d20858ca622b9344f0db6693d15ebcc1090c0b655bb77c2fd5b2efb15a067076e79cb6b7dca71f244c9dc66488b716c7c3c607c14d02f4510cf9d37ed98fe978ec9182c3a220fc460bafdf2302f9419fd7c445002b0a51810b388747db44f3698db0c72c476345948bf99179130230b536a6fcdacf12b9301d5a59a8c56dbb9875d5132e75fa7070b9a7b342a599d74cfcd400dcedb9337850ec75cfea95c72f0419bf2920c760d39bb82dd76772a09daf08471ddd72896625b621a1e26b929a9667e77a654fdbd1a5ccf570b9b2b7a583a97de53f320ff4807576d82867f6398cbd514d3e26bb3fc37fa0fa8759fe8f87d09acff0cf4cdc752f0e873b67f9770c948e4b4247a5e7dd1640d0f549d116a5463d82c665f9b81c17124f10f1a27cb490b73654cb59bf7d93045deb21568392a890e6e40d9de8bb2e4b433d1a925f989a43830f0d751cc4e38e7b26ae25864cf89500b8687151159a7621276e45685757a309775559dafe2cd0cdf03fb16bf307176089c33e1d8f540cd8dfe7238477280d0dbce0bb6cacc01d09374bf04a40a927ac25c4e4adb7a6ca45e49a4157bc444640765c629999ffb4ab00748684b749b13c7caedf82fb8c2aa51cff1abc06e0310d06f6ca3206db4f7de2b2f1f0ed7c2631f984d4a7a7fdfb404070d9fa98cd760e4757867325558b01e00e5e4b859d6a9b49ec0d467a9d7fe4054c8762bd0b8f5f1fbcbf420b5671de585db87b69c19e52cfa62654c50b1362ed6d964f1edb5ec89c75027e32d9d5e9e48186dbe49f09e893aab69a54925b1917333009a9664cc60e5c570f5af34a81a0b0fc6b423fdbbfdadb3b84b4cb353d1755ed7a7b889294a839939f533e7b9316073984b512123b3f45930d6ebbdd9876c29c557debf32fc50c0bdf3651307c51daa06fce71f19a99026b1736db47d5c3eecb1b5b6a222c6d981e27928ed18285cf369ef2f61e86b5acb7fb1f36b11cd88402bc2861675281c82468edb800c7afa177e6dfbc134518c08c54f7cbe1d5e3f696161736300df90a79853e8e8674c591eccdc7e4847166d860d25f01fccf33368cf55ff14dc92303bba0ff7d72eaf58793823c109ce6defb5b2b35615a0e11d130b60b5a6c05a0829412619a325c400b8cd26c5d66b5c6d3462ab8e9f17c67095f67d6a89aff37e50d0b8f0b0b78cd4114863f0dcfa90f218a9753b2e13f1f5bb7e724831c24203ad16ecb96197327997e7e5e9e84ffee65f2abdcff45f18dcf8b868c41397e845207f19d92f8455569a6986911ece9c540d9032fbcbe41377b0fe555caeaf94d1acb771f2a8f5aa7a1e081e01dcc75b5e90153bcc273550c5c928c23ad17635e3b3796d4981b3a4bbbb6b8961f1fdb9debd12fa6e8885c77c594ffd50da3ce7f90643cb448949dca8e7e9b3ffd622c807920915da568c66bf68ee24127661a503148e83ca14e0e3945f2c4db94120ea62e7f19bf1a49732b3728193076343dd50c8ab38999b02eddc94a3f15465a84d4d4ca0c3b227214fe601f1708c0335559e3599b38e64754630ea889bbf388afcaf4a03f1cac116be6becfa46f3898adc332aa6b42f98412a6543c325162271b42f9474f676a0d3c6a08dd7600b2deeff48e312e15622941a8db9b001f679a920cc7f5ccc4a4b205b0c137ba699125d2f51b03ee246d27e1270e0a2948c0e263658a8996d3b290e3748ca6e420f017817a24386422731266c82d3cfeb7923fd5825d67800843ec312edd94a0c3e78c6e9de955b68a614e16e1cace32e3e90b3217db517fb8fda6cd49c13fc07891469fc9f949a1118504c9c6c339c66bb9dae28b5a23bb588c7c245257a140ab95ff6e909d59d4e69b57d2cf06406d3850d34d5e609574b26457585b1a0fa610bf8dad53cd7d173a41e89f6ac95d999f9264f7c20fb887ec0317b00c8247a13a120fff976f81269d57d3026a95c7855b8bae352e5d30838755012779645fe7580c8772fcb8787431694b780dbc58fb47b4a8d1cb2074588074b20b9a10ed024c1fc80141e3ba170f0778e045eb34b3fc00cefdf196997d587dd964f87efb26f62a0579af24276a934a4958bea1b54834ae07e42c9ba21b0b70e1d5c3a1918955773706d97d8371b13a2adb59d00f276fcc7ec80a87f4ac7e6aeb918d93dd02582e88449e30899c752bd47db30c6cb6c6049e9c44a4d698da3cbeeda3c314bd7912bd1780252b9944f512003a711c2b8b7cf8624aaed96171aaf50fc11d941c58acd79a31101b3122a740e326871b6694cefe771397f3c7ca7c0f24d8276dc1c3af0abfbfe1229bef80ba6753c327647fe9638a591209b694b3a0f49ddbeeb1fd17c4e9b4491c642d3afd131bb7f6cd67b475e4f63c0cbfaad87ab9f08bac8fc45812cd79f75cea5ae1001d957772e82b1c15f4be7cad543504d578a38b0a3a339775fe44d207cdaa1c7a6ed851cf09fd04ed9d0c3316e2f6d4feb43ed0b1932b625eac149693abcee031b2b9dfec38da3b7f0b5c526f07eedd0e4405bd3f1980959f80b35c105938eb9e826a8052cc022a78b927d53e1a4e7c2dbdae05d5cadcd29f7200834fd3f253e408d3565d9798e03f00eba3db52e302401d2b223c13dbdb271e317472dea84bce7628e1a4e71724ce5e3e2152d8d239e6b6690df69220e262c55b786657200c4fc8e257f9e3fcd127a0fa95cf646f0da9c345528307aeb297771a162e5079b7baff9d18fcdeade68f2e00c8f8f25aa6b663c9dd719bdb6a423c02f09566a858b496fa564a998d7581004629c23ef19724a4678acbe1088538877291d6c010e7f16efe732ec4597b07a06501c61971a001d6754ea7c431a028e053752596b9dd52f55781633b62d1ec2b634102d2cd821eb90c95bc13d8af2d3c2977560e73377b6fe2e1dc035d92e31d3e67de794ee811bd0fa83ec9b17c1a5724f82a918397d59958394e5b41a91583567db30015b68414205742aeb9fbd088327502729dad9315fa0ff52164a4f86d41bffa1b279a951438e4d7724746a617d55c8998fad29ce3604bbf299fdaeee26e1a5d6e7129af07732c819badd5cabb79b138c4d59cabba6f08087258c0bba8cea50abce7fdd217797e9a2a5bbc6a12f1100aa7f3ebf7ce049485ca8627a07d63b9d9f07a1bc6f35efc86d3f01114932915800e7f64d26ae80ae1268c159f89493c92e60918027e88b6dc67f301417d9e30c3e8da6e34f365988fcaed473b2ec90618a2cfff308bcbbec65d1cd0c1b3c4b3b3313af64b2897b6e8f8600347bb3752c9e6c21b7803366d09a08c98ebf0884c6c15e5840fcab092c7bb0da44bf0d4ffa2210e3800534b8c7fb2a9db40c0f7f343667a6fde031e9b3b39e8da1d8151d3725b3651c7068f602edfeed979b04dde5ac73a25f6983e1da1a389171578e182fcc5f2441bedd29a68199a0b5a7deb538689435db0fe9d3d462511253dd457bd1afc1c52f1b9b607c80a024204436c8bf4cd7c789c529b43bd02117bd0dd2d8fab1c39941552cb2c01cdb8972105f5164620c2fd534f13ffc0ac5b80773b9d077779a68e091c9f2c940dc0506fe2fa3be52921aef990bccea222a6c06546c39e89d9c2c0880068e10d01248a5b86ad801ee705b114960d2e5517b7f355d8078a70c0746c89fc5375052c730e1f0c2e933267d808fc168d4997113727cd30cb8dcf380f2e85e42f676c32bc4ae2e23a1d031c04d3a57970f59bf819bf35bc658d04b09eadff8ad7887306574e7fed804400ea432edc0b0a3c966865d5edf81297414d972d5d494f4fe4c4b641eaf028fb5971c74315bee15e5240d68a7e0236b2b508dd54850805814ef946f19fe642494d489913744718fb06cb1d82904f7d1dfa098b349e4188008737b0f87b07c2a5b0113cfcd7dc5d32971aded4ae19de2365a4be2b4b2938ed6987e118aae431a8bf3d87597b485821270fe3480d166c20cf1a0da38719ae7f873807e82dcd3172ee1b99c650930fe4c54a90f6fcca49e0573b64b7aacdd160e5943a339a4e862a22e7f7a352e3a275b39d46433e9923f881c198ced76faabcef021a69b90d5413ece452df6465bb83d56272c8dce599363016f546f577f113d6a39585125c01f6bccfe84064c8e1a85f625ec0ead56c2b5bb00c7e31d63ea284b0ec93919099b32318a029205c6f307318374d66aadbfa6c3cc79269ffbda3b72efaa828e0bbb9063846bf3307c54bedccb4dfd567545f315411230fec3b30c1314f7e072dbf4d8178c3b36e428df152cf5e27a97f70e69c159b07b5e8b4873244d276d27f7196c29ae2d6f3c39df5787e8c18567a33e63f948ca8300b2f0ae1c37595242da3e9ad1355666f32cd44bf5baf3742857d4ea6af83b981d85d5be1b2ccacf9590ac67745f433f6d432fe11a152e549d4d9bb581e4b0d44395d895d717c710abefb56e564c4267682ab408eec0f793fe9597c97dc506ddeddade255e8c02b81ad7bde44535e78d7d2ecdbe4e5ebff4b92b15deba365be43ba8ef4f559d9958538e402998193c13dc6d6073ff6dd9f1240c573ace1310b3c0de1511b704b1f47e1b840d2c4555a3fecd422819076f3d208fc6bef066cd26ff70082fae29cf3da1059c50cac77ccd52d5b856ca1c7b2fe1d8c4d5053923e3c78363559e3115f23a9965350bc4f4489e894fa3e561b75b81117f8c46b80ecbd71849419073e4c131bd5746e709638a4fee14fc921490a455899b4212df1aa782fd633214704a3c8a7ab9b1611bb1138e67180aca70d801eb07d6c8ccc667346d17e20a5089b00967bedc0f7b6872d433550f0422510014b59a9afe458ca36c3e71a3bcac368543ae33a15314773593d7026d0f6e8f8a1c8816e216f8b3db51bbdd004d0b5ca53ede082cf999978285edd26cda35a891b708eb2a0e2722f9038e8965c1fb08fef374dade4b0d974abd24d3f922b17c5c9841329c7ca6f75e58a58b324b4b36288b7a4279344a4dec1d7209c68707c63e76c6e6e349a36646351c4803b38ad8ce0a1d1a958cfa294ce65094f1a7a2572c4c020742f9eb3bc1fc44fd7e0d216bb39356c984187ce40e829d192de3d92fdd8466e1b548e8fc8d8ce2385b88df7b3b83e98b1112fe2d3e4ce94389de905aa3c0959789d25ef34fd5e9091cfdbb1833a84d6a59c341ac9f9eb5c511b573c40d1f3321790a01a8531e11013d6cb92f6320e6eccdd96bed1bee7aa4055f1ece91c61ddbf7e0d5c27fd489da9045cef1c280e5bfc096e246da436791539fcab8cbe2bd362bfed42afa363b82e1072e27f256f7dfd202d0fe568c4a5ad757101fda7a479fc5b9f2a83d9e615067d4a7775dc094f750ce33b0e55f4d04bcb2b794698f4b551613d2245856c6ed4546968227bbeb00eea07d2999835c1b94a3319efce077f4c056ce1636eabdfb40da7d50ce25d78c17083b809ffa1c1625e41090e45284b4c60b0ca231ea6a60fe4a8a35755d4c6d10eb600c45d5033b20ce42d00395e4cd5cab140ac091e67005e3dd200c9dc5c10d37414549a5a7f523165abdb762a25bc974b006eaa960e19a6472b369d9f5d62d9fd80f1218c2b1dbd4d64d94a863f2c4965b46631679a5dda65e81d35530d8ce8fac79f4b9ab2ad50292857237a6d395098eec9c9f6b87901c6cee8538ee7d34882fd077f19a193b11e9c83264bd29840a34d7d52f6517b6e03145374874b730fbc8e301b8427b0ceb8d2ace0d7b8829c2f8b7b48a2f3dc10564b09c3a2bb180589aa3391ca002049b91e4b192e373cc4d28d26132e500d1a515fa6fba9327ace55f42f5ffc667c19cee3a099021cd0133ddf8b884b1d62ee6115400f6ebc1f1048ed955153e4b79bc0b2eacad34d239d193613c52019b912cb4fc9b862258d5eaf33bc924e46a47b9f3e4c6acaa04e4944f080b8f2929e298cce19c1e671c24d493e85b79357739444ccc8df8bfb0471aeb0c27a4b12fbfeef4506c094aeeea63a49127206226744e4e95ba555609218dd4930018752fd4c4aa63b868d83ea7b8fd0e800f1a45661befd7bec24a35c35b0837edcb24c39dcb54e2a00a18cde5362271e6da2ec20a97988fef4569948d3d3b562037fb67e1a80d43d1df838c00a469da3ed5c392e0877eee70b1dba11aa2c1f8ec6ae5cd456425eb7a05734f24f0fa89fa5bc48cdcc3eff48ada1193c83e31834a88676d938f3e6898f8ed1e0ed54bb726c2befddec0ec35b771fe0778f8e53c8efab2d4b8726c8d1a4bcb0597a34a77ba2f038197472f0b04cd7e9244dea459361030b1c958c8b320d58925104545b23f38db098d338f9db5b0b92101c6f29962314f6f18d26716037640fbb883184defacc02a192a7501ba2f000401ccc561cb39415d6ad2a1ae24292b88434e89ce082725b52966e8223c213d2c6811c1bbbe069cbbbaef502ba7003f4532a1a73406fb700b2c50dee7b38068e006d229ed367a6dd94f8cbb485b2e4816517eb9af9fb35179989b29a0264843a48aef208aa0c2c56e3ebef47822ca61823f7d2a2ccfca60b487986cfa57ea5a067e182e579cfb3a0db9d05f5f2f1237222aadff5fffb14e724d15daf0b6943f98a3441ad29d4a85fe09e4efe6c3a6a459d8ca4dc83e12ef039e77d8ab3f384a786f71c9ec53d91b12f4296b0e0a3a02a84dca24930bbae36b6080b174658d2e2a148d0e6b9f65b0a18290696edb47d83945d7e64b1be93525c4a06e21494691a5ab1a30a18e89131e840d6d2fc3979e6ff09998bca72ab992e8bbc08c2b387363ea0f20e4adab26137533dfe49930af6b5c692c6c2b51f2f7e12fd8f55829f366595b0cd80f6045f4d1afdf11846fc1c6a4545e47726316b1f8f6bf12f25df23a63fade7c81589f41d3ffd71a51d5f2e97407907eb4efaa7bf2a371f1279b403fd24c1c4a495546cf693661453ae30b82214e1ad9ec51125d00c69252ea172121dc1aa5ae52468d403682484df7910d30e0f9c6695d7471df5de22bc35426ee49ef5a8a52118e053360a8b0330ca7e14d4a3e06622d2b1d2bf75851c3c5bc2daa7c3cffd6d2a1d2060ff22552fdc0a323617c9fb749c693e811f5b6ec29234d6a679416956143b0077a2ff3be93859ece54b8e558a4f4c8c6192b870d682167ad9f839258ab3a7bd92e244d594abe9ecf72d2472b96c80063c603d274be0cd4c4a08b189931c3c607172f298a1ab3430e760eccd784521c3ff7bfd75f5734c96996ad851331f13cadac914d362a9582e5c7b178b31dd5e9d8f60e6242a466758cfe024100965d049ad0d4afcc8f91bbaaa1904eab5484079259e6396333a686769ac44fba7a9c96bfc5945189d6d1fb1be68e4c289bc244ce998753b029695603cdb8d4075acac5c779ab3529a9508af7cc9e816f610d76271182b7e65f8ca33a1e4ea9569e9d125539928d4409734cd54a455328bb17f2482ee1a5a89b447a2093dc4f89dd4253797625f8df74eca2a65435944290f95039a7ea3816e55bcde5187d3612c067f16f367512877f262ae34a78d99bbaf8a79980dee5e5042fd416bdc1ac3bea313c7cbccea36ac56e9ad659561a2bc38ad703584367de559a764dc22638b5cb3fb76b44b228e50be00a9d02b4e377471450e24f13325f001f2481663155726ba3912b10a303aac52cff9524796c79cb33a53c1492c29769937732e285968eced219cbf395e5885302de7a929962273d6949568b0163252e4e6f4283b34a7706b1e9131d89b2d90a851380b79382ed5912bacfd00f5891ba5e63b641593172b9659a34d6ddb3054e7d8610dce671e79be4ecf33ce2be9079998eec51e300a426541601bc7066e66163b54c1771c708e35fef3aa314ce92ff59efc702abe825bd6e3c4d8b8ad26938e252cdb8a62635c25598606c1d6d13648b640e74a9ca11c3ac36439e9b2622152638bb8b1de3d16be08e066cc1040dfb2c86fd3e57f4e4c64c54817c989aad6f5f54e5627af7ab1fd53589a6ec7a9537fd6494c1e25c67fcba3061af34959de4710637c69b05228b0e0b3e80582402894969d45b7341545523c40c3b3d86f09ca8e1529529d4661dcdf88269480033a720e805b30ccc24a0799a9bf0c88b14bc38603246a0a987ab69e85a65cdfa5bd72cdd838e9037ad41c38c5a386d94db9f48a2df06000106faa88d3f3a167dcd5e41d40410bef82b89745b06b53dfbed761212ed4ca4636d964cbbda54c49a6a007b5073d083be84b7e22238fbc9741ad6f8548244c08b60a2794962d66330dbe262db528574dcb1632f9ab8efad7439c2bbab35a752f9ba47c799473e54b26e9d43efd6ac924f952495e91bfea28087f8b1cb9f31770c19d43524952611c4a300ed9c44e968cd23c124946995de54ac9a55b1ef9d1917b87b2c7dae431791284eaeeeeeeee7dc5abbca32fbb70b0418288b01798ad2533dc30a94c1a0fd99236ad746f31a198ce201268d9d205f999b1fc3e96a6d4081e00fd8c40c2088ca3dfda124620410891f6b18f64a643826b369bcd74fc4041d911922cfd2dfc27818ccbba46805a927253e3e1e423c8c9c9c9e9471945d663bb39a6ac0fde31e3f607f10ebf5d5140858b9ac04f5f46396a71262b280b129ddec9a9abddcd4d77a2a14697388e9bcc851ca74365abfdae8dc2f04ca6b05029cbe53e44a8b26577d7da4d5b65d053d9eda993070dfeb428e23c507b3ffe9a30afbb0891dbbd578bd85bc391c8edc221deaf4058bdf71f921a1658b1416ebe6bdd8022742d10488b3e70b0a34be4c0c56a9c29384038cf7a2af33b649c0ebd077290fb55bf7f95b5b9324edb076a0e1ac7823490b05b68d558f97587c5e31b234f91031d1eb4cf28b20ed15f765ad7b66d9cf8a3a345dfc2b2b88264664a397e4efc3576bef3b70adb73d5973f96c0ba6a2cf7cc00ee87f06f3f4b605d2e9cb27a0657ab4b63cd19cbdf3d53ca263563d95c0fd9410aecc84e22b48ff6995fc170d52bb2dc6208cce3cfc4a3c0dd7d7bfe919db65a6dadd60757f7cab25e59567a19ad4ba3ebfc342e6f51e2e49c0a52e79b7a43f4db62f991b0f3d0cb6ab21cba6cab488e6116583c454484201f4ef2458471f46b20c56e2e570b091123ed73f3fd4764ac7dec0d7773f3d6da9c9c9c9c9c1f2552ce0d0e0c18383716b461a4fb6e9056ff6365bd08c4abea85ab07f20eac5c3361f620c238a4d82d1c3db82d3615fd6584ea5d4fc4b6a268e2b23077dcd538b2f3fde933a51b83459889aa70dbaaeb6a573bb0088f4666f71cfd154854f4b02af237e6ba716864fe48000f91bffdea3d5ac1078d34f79b91f92301555555979b5356ff63e31dfe7939b43030e1a45600a6ea8a3bba947c9374a21940d8f9f2e74549e4dffb4b59fb30d843be0712098d2c56c68eb46eb0fddf03b5e38cadffde6b136cf10202ebbf9fdf07acef5e8238f37760bcefc0083fe80be30892daf5d6f59747441349862fae53e91751dd3203d2f59fd2c45b6490c1f0a610bfe23a1716c6b3ff94f0e3af23b5be171211d2831d194acabe3f09edc33821f390191af1fbbd0c8df8657d7df97dc0faef5be0900ffc1b199c807f7cf9dd4b17984118cb4a382dddee3baf82a3fc311fc8d9ebba50c6901cb9dd94442dfa4bd916b6a725058eeccacc2003083b8a326ad68479c7bfffb2dbeaedc87a0e2dd0c172e0fd8dc779fbfd50ae7d1c1c1c9df774741e04be3aa18eabf3423ae028dfc2f89c6750e84a23f4c208092073589d166f8cb5f5a3fdec78a3b6c26fd10b57f43bd084c9c9a79ef5d78d8fc2f2e7c8ad8a273bca952fa300f990524a29a59453148887bcf289b40f5f09c34c4b1654ae7cc962ae7cda3c1d50c5b4d665dff552268de41107f97256ca1e188d2f6c873544da6eb0331b488e344b1d41d23edc0fb12f85f8eb041d223064132ec22fd17998fca5e3c5ed27c23b6a8c1f41213140e61c7090e1f63327e991c2324c27dc09829193f31b0e0e33b37df9a3b438cc8ff3d632b39065599061800ccbb9224bbc3d8a2bb1138f2089110979174bd9d9143e3b90024442e4c76d9c5eb36aafea2adcf9b17aee415885957ba1bb715ba8e335f9d6e16b7203e522f8c851023b2b37a4fb8d9ed0faef7bd6f7cf05e21ddded97b21fde0132133db3622c5f86b5c29d20d6ef00f99edfaec2ad566bbd2146e8283b505e79bd5f7d07cabb715d81a3d0ddb66f7b064d98f272208326ccfbd65aeb4384ed037f9ca76bc43e1b6ea056bdf52047c98576d2a8597ec952324bc972b214414ab9b93f3333f3d87339195358e73e4858023f5de692b0948653ea7308a45ff5db5f1546b9f06b18d99fcc9bb59cab371dbbb95bf701d46250102f2e2f5e9ef5f588e7eec93ee3e45bbf5b8256c795054341c19793fe7cb9c30feb381950557eb785fe358a2c9d2f43b75115a3f4729d6b51da67c8ce75bd0fe14be3fd6512ef1001bf5c4ec45f40327ec633c778e69c67066512e3f0a7014a24504669d145c03c33c2517279c5084729c4f5d70947574e0823c409ad75adb89a18c67268b3b0fc74869c5c3cbe61431ad1b8884c02f3ba7a661f01b0e8ef7ddffa1eceb76f81412c0178a1d8c3535cebe9790e5ddcef5bfc3b90c2ce7f4fbaf8b9c93d3ff73605565e8e8774ab70e747f7f541e8c25538f26f40de4ea803b9e70a7e9cf71b38c57b9d7e790fde689e6bfd356b306939b1dc399464a625b924979268605aec7fa239cd69fe92b0db6896e4d2683d944a5e2305e950be0473bfa4d23edcf707dd7cdde8ec64ec260ce2daf5327cb0fb207fd5ee168ac19ea060abb7fe9247368869fc5d00f9ddd6348566195143b8bb872c841725f524c60bad89cb1526252a5190a6a8c8d4a0a29c9ab80dd40866b03cc80050c508f541c70d19ec4f8f16eeee38cceab2e17a19f2bbc0e7d0868c31b67bcae666d7e6da5c2e39bfe50c02f4f3c3850b171e445177145d722ba2bab2c49a28312c33d07400f1f00114b055b8e4c60d503021ab62d4c40958957effeaaf1c231518014543115670817912d117221078a15a010de6310ce1440b2a0002052a70df0d8b09d250819aad914aea64ed231d8bf6612d6b9eeeab3ba32b9f86b33476e075d1b88d73bb5f86dcd7a3c31d0f8bc82af07ec1620716a7f4d7ead4a9a6d2fdaae97b619dea6234ec6254758a2ab5cf54176b1f6ea516975ab4b29e5bf8f598b615d5a26411b558a306db4aed23252b1ce9b7c00f64c96255ac7fa502a7b07e822ca266c5da87be7c16d8549a1573964c498ded62619d6ab135b3632baa15d53e3b2e1b756aec64379a36eb819a53e21dfe7c31c0c1e65c2ea28922ae09f28e9c522b79ac6200023bb6d2c829d13356032adbfa0dd0d8b14e6d10b335a8528d9115e3942655baf23f247f713225eee8ca1fbfa52b3fa62bbf46019258897465ada82bbf226257be9525d99193c9fc85f3b24ad5ca3c96204a3160673ec3bade19f6d4ec8004b6bf93daa743c9d4f2700aeb815a89a5c40aeb14a754a738261d9c524f0fce92d739495eb549d8fa2cd78fe5d2b2125a2e768bdd0244e4f18d9183baf3d348562f4903d0b06932c7fcf9ed03eff0b09b48c1d2f7fac28861e0c53a7db2341c19aa979cc7359be63cf3aba5cd2eec9af3cce79eeeec69c2f6d7d0b438394a39fadcc655ae7295e3e8acb5387f9b5339d0d8da9c3b73ce71d6ee9ccf417153ba780eb999b9796edcc66ddcb6f156a7ed208de50fc093e56dfe56abf4e4e4386ec61d49ee890bc23c521ec99984d2a2dc5ede2963475a9492e8693eb1ac31a8eaec5ce0bf516277786acc1d3f0d147f9d838de51b523675e5dbf679970d69c30f35530390b2fcde063a587f29bf9b78475f07798cdd794078c2dc79407702fdee19c65f7f863b40e6f6d38350ee3ddc01e248e87b178ace63c57a0e4e50047b7e8036864d9061eeaa01a5a90cd31d9ba6636d437545bafd9ef4ea847778a8e54a7ffed12f3ff7c4c441c13b64d841308efee6dbb2319f63e47c727f6e8a7178c13cfd2d1b733bc9ed97dc93f37c378717763ef98b6b729efef9c43579b4277f414903f8f75727dc0af8d55bb8cea4013ceca69019c7b85af5f40840001860b0b3939323313081f51fb9a6d175594f7664a9a6db2fa6c5213ba15a7c6a41b13bae266b2f3311050bb75f724fced3dbb715efd84c4ffe6a26e7e9af341cab65e29eaec3b6b09f9ca767aa7f9c4f524fb7a7092bbf2235e99e0d921749deeeededdedd6ab55aadbc6738428e34ce8594cbaa75031dece84744fcc5fa9ef542b09b23fe6abdf74230fb4230f97ec43bfa08112b76e49acb3a8ffc015c91038d1d19eafb2f44227edffa8f05b27680787fe342d1113198f74230d600aab0e3fff4388ffc0e4660c71e1f1874b12337b9b8acc67c61a3c57525b034dd916b4c7e6500454b0e24288af5257f71171e60f29da97de47b96f6e1ae0481af8e2acbdd1d7f2d3664b9fc81c05d576a5176e1e17a1447f2a416650f3f6a71c8fe955fcea4704719cba1c6cee90911b201cb06a0dfdd77230869634910b2024da85f0485d85d75947e3dbaeb9c73e88cb0ea6f498ca5d3dddddd3d69adb3ce3aeb9c3d9148223568d490995a9a52f21c8c0aa28a1873e5f2d7c3c33a25350515e58e3c05e5ca472265463743f6480662ac0946ecf52caee01c72c60f96b57481e68e2e15259f6f1733a75273e58e2ea586ca95ef64daa7a8985bcaeed1b63f10e6fba7a3cad27710fc52facc38e6d3903b2c767a38762369517e7da0f6a8f6e910aa7d6acd4165479eea701c10a5c46e9f838de50240c5dd31d05277887f87637dfa9e9f70d4521fb4ae4b25b13556efb05528c4799abe83f5479eea516b448c801b8e03a2aeb0052c6bf142eab2162fa0ae7c242ee5f506f67352e30947d7a5c0ee3fa0713b21ce337bb850582ba1a5c8f8ab03b27e471887f49a4bb528ff63b2a390d44dccf2c77260a85c477f88f48c20063d906bc92209cdae5cb19e931b47eb5bd0c53eb76df2e9cb8d6e9259bf052111827dad1f6b502be8db79008c6ffdcef6f237efbd0f3cc11fc9cdce03fc5bdf0a19c66a8127dc00b9790fabf3d4bbeae72f02386f6180d579b897b547d0172211827def6d1beb83aaf3480efa80f8b72a1018df0a91dc3c8cb03a0ffdd1fd0604e76d0c2702feadb7808b1dabfcc013beb7ef09c9150a387f332fdd444a2bf7b4886f9bdfeeb72e2c226b384a8e03b9b9cd184e58a5dffad7809b7007c817ee3ca0f5feadfa81adef0fc8f7adef817ca3a0119f5db392eaf7bccd23447484d81182d4a0e9e3d69ba7df0335f74cb97f2ebc790b84b063a5bfbde436ae72ac258dd1bd794e3e077e3da4f4af876c85a3d06d3d07eefcb0bffd0ef75cfd71b5f3a332d5bd796e0387ac565fc3d9e2b67ddf02fb7b9e94cc366f9e0baf63f127b57041a5050d997bc380efb7afa3ac5f0f19c2f7db03f51742ebb7705c7d6ba4dd618001fef52d30c28e555e7048ebbb9f5f09fdab6f3d078ecfb2a1c3da46bb6df4b722dc463790efe75555a37394db9a6b0cf477a17f1f6ce14e085cb8f3a3be7be3bc5b0db7979c91b69f64c55023862c1cf8f001d4e4a3d65a7d00f16802e2e10388870f79449d9569b38db37497d25dba4bf7210a054487b5d66e340848902020415c02524a295768824a29a594533cbe31c57be23d90356591652d5ad0eefcbf9eec29476a2a8ff9912b3915bfd2af19504e1475e5bbcca1c8a66a51c6a65a94376ab0634f5d39e5b1e68203565c01668d105b54b125863376881528a1a245a52b71a2c1249504d1f2999b13a651e982c9134e3c9121c184e18895344ed470683302a6f9829218585c4910058b12496ad6b030a12891ddb460835ccec305cd077ec26989232d94a4bcf0329f6c51a2649b459469439db00f6089154e30540598232749cad8eef76cb4cf9c377e86366cfbcce962efd60ced0ad679a094f2130738810d391ca93113049926f475fc504aa9d74aaabcc31b890829bacc5e08b3048a2c9ecc9914dd1313c0a02e687024c56989194cc0a60817b2dc74f885851c9ca84266892fb3851fb8762299e8e0d127494440fb659439e7ecaeebc2ae5b385a1a4a77274157213bc2882b64a290010a264d3cdac439ca09055ed6d87046cb10462b70667a92e0219b7a010c132d4bcc4c169224e9da49c7aa2cc142658531544eb0b9029a6f87a44fca704f29a594524aa907599a82e0824492279a8051152ea594524aab0e16db3fdab83abc8376173e6527f8dc0e218015ac70822ea868e9c10bd84cc189305fb091a228894bcc045e3b99928522563e73631ca04cf1214c973654cc5094854a913506112a185521c5cc89159660a1592c2389c2a585262e9234b182a887eee739adbb134a2c4102043740bd1055c5900d394fa058347ca9a18cd2136698288981fb49b375123d9c846a0461ca30c981851f66922db1211d9171c8a04201fb93282db448b1028c1398196bb3cd1f323f3d45efa8ea02e60b0d8a1665aaaad254273db236510111290062080b2a947c71054bc6161346448f7340822a555a9a98e1c2429b2a315f076460ee5de40e9a0e48cc906ce0a18aaaca8b950184955183d12a658ed89eb49525684c9420f64312166ceefce72e55586e9cadc021091a155c8cda04b1a6ca4a4dea090ca5400567c898a0062b5ed0a083802a1560d0266d9d78e9e243d3942ca2a6aa564e325083c2c8051e9048e20b3577fe7b1935d52bd0a5c6860627ba3c99d245988d86a02150c508881e6a59ac400b4ff385961833d9c3955663f9c20aa54ea068511475441651303ca1f9624a6d29424555c28ce182091f5450c2b9f882c8a33ddf35ff25d39ad5f6a4f90c12558c9081082956b50554ebc9b7b7d684a9624a0d4458a1428b25e68ca1ed2513a11ab644e1044d1924ae58521302c66d58f38e221a014f706813851a154ec0c2191fbae5acc692051a5a5400d305549b12b031b303d733488d1d6d50ed298877f0e859050b189668218d175bac81cd3277bab084e8872e5176488a0136c9207949d89e908336767471d0405d04dda087ed04963639c9ec8febc7c5b936ea4c3e27bbbbcb8072da7577779f6ce386d5a1cd24e41834dc0d9415519c58fa48bafe22681943268b3b32980da8b0233f6df4fd3798c2b6e8a328765e9d5b85413a77ce372af3a74f2e277f107f09c4b8fe5206a55f39342d6d66d77db274ff225e97d3d1a54da9a4316220d1aa65e9205551bbf3c1777d308ef9ab2bde0e88b0bdfaed837441dcfb20e23cf33790132f7f3b0dd8be3e90edebf709dc77bf024fa8d22110eebb70a74fd8be863b1d06c12898f91d1844e30b4b9fe84783a5b43b5fec442ff4616be80ac2b2124d50dd919db68f8323dbfd73caa9bbe5fb1021e8c80ba70684cd911835998aca16e718a93beae009d5bd454d769f733233b3e419e5a66b3758e9b700de289ded459779bbfc0df26c6219ac183a393070ec4dab67b064d4d6c7f2565df5195f0893c17e6a302db29c1df22b2d3135556f59cffa0889abc5b6af87cf39674d800c89d65aa766fd0b18caa7acb53afb5b77f95ff71a030833563a27db9835cc4072eeee93a3dcc671ed5bd39eedee19dc9983c7bca399673061d56ee798c95cfe464375f967e8445d7b69302da6a3b8eee911454f003f1934544dc4790123266ae3a85cfe2b553ac7715c68745b124d212d72731c94cb512e3ff5a4cb6e24a59424b4c82f638c95928316b9bbbb5d3b3fee88020c25a6ecee6e29657777cbeeee39635b73ddb8d093dcc68526cc4b85584929e55c8554ae6449da684b972ebf735a6d475bba74a72b4aa9fface66a257f25dd12396de9d2a56c2f9587afea6ab55a8523fdd97577d565e8f726856dce16a791bed2485ff9fd84598a0ce69c73eb2bbbdda5cb6a5df4cef60fe6edb07e61e44dde41011083f28b33e057ebb9fbb100970f70998db8ccfcc465293e90d2df40df9ec34b2970fd63c0dff985b04d9673b6b850320e4ae7106eda9a36f16afb7c358605802b83c72116e00097c3d15f76ff7c3a19874fbfbbbb14a61b27ece4e532952b5992b6366a281da58928ad950c1aaa28e6995eb9d65aa5918412e5f24b222783868a84e621a7d35aabbb3b4b674ef71b159c37e698c3917ea5949bdb9c7352caee54b20ef3f05329a5942c49a170e138670dc7ea944afadb7bb47f9c736e28c8ee2c6e3696ddac5cb970745df60d344299524a299d4f277b13b5beceec5651d85964e5773d228650c35f41617ba965b774972e25925b2347182b7ff5f23de01dfeaa60105b1086f5d3a383fb7ac84929a51dc7753bb37bca715cd7e35f0ffaae1bccbd7cf60224b1fe3bf4a7dc9c7a4b6fe92dbd654b29a5cc80c9fa338ffc4ec746dfb8cd38e437795578f31d1cbba022095bfd0a64310edade23118279ef75ff1023f316a93ff26fc0ef140f785f90170e09c560434850a822628a336ad8b4a98279bf4408e6854b58bf0ac322f5e96f44ee164ef19e05a220634d483319919224b021ae82c968c0bc5005ef97a0b04408b67acff3bee807e63d0a5eb85aad566f8114ebdf4b50c0414415396839c304089e846455101111c97e60de0ff91731984ce4a8623198cc05f37ee8662a168bc16415e63dd1d0cafb2111c90b51109a1246c99a1d68d021491330a2ba45e98b241811d1d005b50a5bfd5002a05cb0d5130d71555391085b7dd14fcccca262df7ef2749ad35d937264443a57375896f428bc5aeb98525db24caea67712693d4f975952363b1a25d28a99198ac7e7d1ba9078ce3959f393dc0b1eb30fddb6e9328a8e2757aeb34bda5a766ffcb4d2aedbf869edaebbbbfb19765de71d333b1934542474775dd8ddee3c24b3bbbb4f1f6f796146923539d5b1cb724932c926e9246931e791b323242429aef1948c597e621aff5cee69fe5e629a1c823f436faa178463b516840c80906d84e3ef841e8e3d34c2f1e77a758563b533c2d1ca084797d718e15875c29c100a46d804355a28a87a138eb5157e61c7905c8e149bb1199b3177f7a6ef69e2a62e1cc51a8e3d3f488ee4481ef398c7a0a0a0a02652add6363d3b235124d757eb426a71de71224da409ca1ca997d2dd5dfed859e4732fd5ea944ad29c735a1a268bf6d91e5975382dbbea5637f6266c7de19c82f31ac87056dd94ac5b75329c38385fe0bc831ac8706490dd342745dfc0d1e0932ca5e4592d6ff040cd93e52dd5491b3cf795f681f1fccdd43e599ada4783e76f2eed93c1f3b753fbdc78fef6d2621aaaa39ad62f1751bfaa918572f96554bf2a198be6caa47e552a52579ab991c454230be592d02f59c958344e542e312c25f340a643a6546533663237027dd6620d5d96c6ce16fb5dd643f4573b89566c0c7eac2128e47fac17fc682df8d105801fdfc68fe2ce8f3d97a5dca95a97729efe6aa792f3f4cb26558bb516c94c342d4e510d409a01486520356ab11f07a433257f4d27e7e9a74a4ea3dc9e52764a6120154a81b20fc77a41385a0bc2d10580707c1be128ee84638fcb9cbc1a4e349cee2a1c5dd7a55ce10c975a4d998c70ac752ae92829ddfee9b4b1604c279cd043d6db66fd4db3bed59d24a6f5dc44ad703c81ea655494eb2f5949ac7044410c110a541c1585b964b9c11a4857159e0d969db31a99b35a164be9eeee5e7dcc7477f6ba6d1cc7715cebf85e4ee736fe1b22072cb69e57ef6dfef560fdf7c3ef275be1d7faef6f5ae16cf1036f78a11653ba9516abbb0abb9fab1eab6e0b3924eca663a7d8775083220bde689ed7c1ca58ec7a68a3c5a25673683cbe3176352676a4506e277d2725b55447855deb31c3850e4ad7cdd4748d429968fa45e9a47482e917ad626cd484d2af6d369b516649332ab7e795db33cbe4727b9c5ed0f46ba3d1a86893886674fb1d4cbfb62ae64691b9cdd52c96cbcccb6d6ec979fa2b073a96a8dcfe46d32f8e1ef9ab83d23cfdaeb16bb5f66e86a4a33476b3a5ee0ad36d1dd4a99b1959f96337bbddd12aadcdfcd5cd66dd6cd6cd5eece9f16d46f357add568355a8d76fbb79abf2acd79fa6935da569bf9ab2e2dcd96664bb3dbcf2df9abce664b4b348ee62faee63c4d6994465d2e6b395a477375b48ed6cdfce7a7a74714ff5dae6e36e7acd56a351a8d465b5a5a5aaab3d9acce6e733577b998abdd7eef6a9d945134a72d2aa1e7ce8f59a90110195861e52b7d415564a568563427b3ff9ccc25341a4fc70f43f55cef3beebaae63fe6ee53dcf3c4695f54079d491b1accab560e567251479246753581bb318bdc2f349ff03796d48b9bd8df6e9b9b15aedd3a22c7623076ef7b5ec6eb5c2c9383cef0bc70fdcee59f43c24b7ebc2c96ddd5d1cddbc60e5bbd46556adb5d65a2fa8b6b9982e65ed9f55ca8a739946e73dc3ca381c0720544caef0a209095868327d30734435f3e568dbb66d6b800f4d358480c90c0886983d98b1e28b90a06688e08c78e205a31c365a78cc3e733ab7dcfe029f397d2680ad8c924e1627dbb66d5b992f1d519db587299e243134e50b125073e9882bdd154d92b0ccec4064868905445b983ac50af4a99911b090086228095dc31d110ba3e4ffff460a5277fc9e4f0492d987a5e4d9b327cf4df23b13ef8e76dbb629a5b685d7068ab53a34427047fb32565e6bc6d7126aabc5baadd657b088cf78a0a63e3e592f19cf6fe3bde7ffe9d70c18393a31deeab0885363d7282408bfde2d68754ab8372c4b4407ac2c2339e06411c7e2dcbc7c1820b388f3f25b805ffbdc3e1c8e48eecd4d38672b2cfa58a08f163b0402d3581e3e589c0d96d54306c8636371fb7c3404c2c24db5d65a6baddcd699b1365c57a7bb1e3cfeb1f3fa8c19e67b2fe5b394f2b92bdff90b8113e2ee04c77e0d085d69a42fdd7e30984bc39d1f3de4b2971496c47081b7c899ed8042cb10496ef092041a2b4558d46dc6e5029fe9a49958b30ec63c41d1224605158480872225fb33ac406489216614c590104c72a8521402da529a73ba12822a15c8b23051297aa1e584cf6451810f3f7051827261872226b59b7092182b9820620b1e8c7862ea20053700162c103b5145b5b80ca166dbb6add665674b114d86eaac12cb192cba335cc8eac42133a36ad344a6450a660e67d8e4d45a8081524aa9cf103de8f0c30d4b2091c554aa25e5d4b0381142c51341a490e5882f4773ca19256ece88694106029dcb5b642d744870146cd274f1830d35f0602465c9190b9e9879428211ec606609a9852982252b603df9a425b41c30466f789e5e45061a84a04226b543088e58b9649461b2f2a5cbd30f52a6e08106822553749e54b9a4898c0aa559786269167b82c50a163878e17ad2b206053039c126071cba78b10352540c26445d76a6133944d483cb09910d392ebb9b58c94ea6a81da18594238080e1062ee69cb3461296efbcfd339f36dcf92fa3c2520ccc50022d4c65b068920446670d972a3143c40ab21234a5604d94a1a78e82f4e9f6f5b6ab41d29284d2941254340c7d5975a165ce89e48513a927589431028a19868ea83b247512da70660829696a35c8305404dd21a9ed11b4dc1ea5d982821c5ca87a4a22651e0d4599c14cb60cb5d0a24d9454ddf02ba186a8942d18f538cad0d808000000019315000028100a0544229168344c6459f40114800e6e90467a5c2e15c8a320087214859031c000420020001042666686a62800a065093dbf6c504496aa7ddb404fff664ab1060d8e68a73476cca6ed2024646232ca16e72b5cae526abf39f664b3f6501b51220d29896a1c7917971a598a8bdf6149c9eae39135a8054f6aa4b2d2a44968f119f4b1558bf43f21741db85b2587145fb623740d14e974fdb37067c4e55d115e45277b1327be339cdda378dc1706440ead81da87e513ca87adc32e68692ffc3ecbaa548e0772080ac324bfe3d7dfb46fb673a36d93913303aa827844d94d8ca129ab2e517f57b21215f57df0997e516ccd05f36ba74760d61afad47d15d5fb70032dec4bc712658790e5a18fe233aaf7235271e429e2aba44a2dddf0652a33a467c7a1f7419caa98936881152a439e592f2843710091c18d892ce30cded80f7f2a2e6c89bc5b53aed1aa7ce5647870ac9418a027c8269e48c16f60a3221c24094c1fb3508009d610cd4423e638d04602bfa72999722eb78a9885e3426abcf2ac779153025321cedca34b2b6ea542c8f4d0a33559e6c9569d49b11caa14e2df02984b210e23c52ff20b402a76a30eb380facde81aad14c86be08827846f868a386952fc6a29b20c01634cc59c755a3ea81009c65ea4f9805090c917a1fe7d7138f609f1d2c0f9d0c4ad89aaf4231e636091165f105e4f39087c0a338d9e01fc4fe365ee729b9bd4fc98e50750657557dbcc4f68fcf18bc24b35be2a21e608d6410a0adb27f350de37a14aa5f5e8585bd83d8e56f5ecc5fd401b5772f161c45a7957ba4a0553b1b440d486adb1d095fcd27c350ae726de169984d78ffc561f4ceec2575904358d1846d2503950009427ea931c76f3c9df2a3811eff245df237df6b9f14cafc7b55baad2792be93b88c385c4b81edafa40b6e139be65a7f5b7d8cb04570951e166b5405cab1c6a7c4c3345ff2dfdf03370cfa9e1dd15e80bbdf81e459f24c2346334bdb2a0e797c4c794fd4e3ed737d4d4001d1f40467a15ef4e53e050a5ddab8d277269c8c189d544de76cc31f4d9e1744d1d670af7636f2415cfd398a0323fc7de3f3c27154a24aca7ccf33eb2767800eb54f7fca9e84e67b1420ce7ac0d2ca3f8561db1d79dc24e7985bc2010e4adfe55f12dd1759cde71e505c27aeed68cf3c492796f8bdbc2371c0ea5b670a936ae89334f31d2a1e5513fb79a2d989fe972fc9a186232ff36ea5e536e87436cbceda0a463bde6d3fd2256138d81677e6384178dd6ed12465d3d9f2ee6f698a62b31b33b88d70db67fceafb83f84894a8a42bf060c3ec78f69f889a2791a261e990e078747bb260cc2b0a64f264825f78f92f36da8990b99e6268f31967f358f2ab1ed9b03d8e3b05e2a75280db2acfc999d5033df7339c0c6f937ea29d04213f927097708eaeec3d22a17ab7d580a7984022855e49a4194acd4eff7ff372d4b5e3371b85224107bcfff78cd8ea228b945c79fb0718fd42e47a8c7fc1a5898e67b29cfb87ef0e29a0cce1d35c6ab3e47c181e2fbcb3072407fd055fabea29fd6838f94de4b9c56547b4f910802421db18b66c874d1d2c8aa40dbb7725ed6b5c7e0998b44bbc9f30b68ed0630e544a7c43e0ad4924fbb0649e6bbf213e77f052913598ef443b884944583668554f58805ce335154c2b0f9f4dcf29521f94eca229ec2fcaf2979698492c116ed33c3a71e1d66d81cb9bf55c5a7fc814d642f63816b858861c953394ca605869f9c8101e1b6276e68c3d137350a0a0c034e60b0c8a57425742534280203f22282e9770e04270dd721d6ac24f5b4767a3114b520c8599defa88fc0b082db03c76da5dc2114fef67d139f15fae1425f7e097685b50efd4af36b773f5227bbba5351a45551464e23bce1c4d8139b5a3c792e2f8fddc88e20c1eeac58160b52f147aa62f652ecb545db0bd82011a1c8209216ef27ef95ea67dfd96fbef1b1962415fd805c83627cc6ad24d147c155bd6840eb896664c873bec27fe360367b34d7804c7a0e3714c809048739b929dd9c1852d11c882cebb0e58e3b6b7160676816794e4181c966250318881fcdeba17302b7fa9415e2d24b1cd07f619d422b527d76b235860dac82a3670af26d9c5ccb993c2d3595ce6a42374bd11b04042b0f6cd4c0af45369d88fd473f725903af10baeaed4d78651767714791a924da4541c1901f242f7974686860910da5e5015c4f6a0505aed81446b6aa93c45d6c741e185689ef245c505255240c5416ff1b0031d0f6a36b94fd0556df128920a1f4d7a2fac4158e6bf4652bdedc8b002c8f70e44f2e83cd11e13cbd59a49775df661fd4e6b3482e511da432d16f5681c609da701c989e853a9d4c9d1f8b79fa8939a6fc2fda568da02cd381d47aa70eb12ea02e669898a3ee2be20c30e60306b536b6a8f215269ed4cd705b330426133ddc90d00991a6f9a8e981ff2a9c168aba817f725b6c8ddcbc03572d4f458e123963594a382b1326ef0fef325ff407aec894644d1770577a3660408797277a72b2e234e3b054da1aadac966e54414369dec98e29cded083dad1ee720ebcf7c7072dfffdd41e5a16d41f4f81861e636954f738f970f690159a9e5b38c16297e9867acbfc30572f43a571282d771a8b08e63acbbacb38e063e757904d8984bc9ee7a357e3ab3b94b9938699562670267be5c1fcb17b56401fc0403a8a0b94c322b283cd5595f135d8628bd8cb040c8e1db824b81f99a494305b01c1fae5686ee21ffa014d50519330383e06ec6f29e5e256ba62abe511269e31415143545515244c90eea3ea24851494679155dc440105224ea8a314a5e673465f0eb64521bf16f23edd24ef6b339e5cac99ba55f99f2b89118620ee9874e73483b30d41351e81cd277c959e897c924fdb94bd1964d480935d5cff66e56503ba23f359b52c1a710b3112b25feb320fc76f904823f23c52b08d91cfe3cb5150a77b55112e00d0ca9423c9d716de94220c403548e6b3eb9cec53f453143973d127348bee6b28cd1111040b91c6de30878f51fb64f50dfc5f55d58c5cd3f48dd2fb28009bc944ca7328bf9392b030f2a87f4c88279e5e5b9055906399c23cb27d9e8f624205a5ca3bba5b2b1c1ab7931a1c852bfde5334b6235f35376d67fddcd74ed798e8de81298b4d256cd64473b3e9cfe15dff217e3d1e5f0601c90abca48ec41758f0100b9544e09b54d893c3445358356b21ac8376c6f6776728a13bf1d13557e44c6ca519babcf7ecec45be63fc524ca6d48f05c21c40ff57a97aa4a90f81c7faa489402da6db3ff69b313ab804121170a8281028b78e18593af0542ee5794a2369459c144c20c803bbe493699a13f6180436de47054c2500d5ef801bf434b5d7eb44f9f128727565ac2e882213995a939498ea4d1629ab10837c18e020eda17ef90352dcdf942501b5c177b28d36e420e2a7a6412bb3d848e58e1349f847476353ae28a744a5b040288797abde9abec0a03333c780c5d13b81cde57e2e8b19d912ed535110c7aa72a614498ca969a5bd8531a02f031544f542e7f2d213323dee6913fdd64d7b5da11455a69052a4f36a0404a638458c41fc6d696ddca7795d99f412c7c2efceaf38223eaaf5f6007856d6f5d0c00bc93d046e0ed1a009485e9e06daffc55349451928d83656f8746e2a43b1782750aa293a86fd53bdc0918e1b75c6d5ffaedd7d6f55b8d3b1607ec9d50d492b869abb416438c91319651e699e5ed710ae6ce461e7b7c78597830fce2c4b31048e6b5358b0895d50397fc2acadc67a7a0e6f6ab947a8fc92b2b53ca35575eb5ab28bae1f4140152466779fc0d1e9dface6919fd554fa2b6538b857cda5c03fc62e30d6dc381b94200194a49f58c5bf71c817be00e09ba0900581615f7a427f67df259a53ec2df5f30d6dff18867e5136d1d502120190cd5345242fd4089938008e582bb78c9b107e34a1f3dd9e13c6fc98282332a183f13b152a86b5043d676059eaef05d42d024dd8a345e74651a9a8eb17b7af5de6357cbd199a0986cd7af543c0af938e406192e7f9c4d2d797f5bfac88f478b67382e452e7c893741754dfbc85bd37acb62d8af1397c7b7b62ffa65617dd62bc5628eda850beb702e46c149ceb670a58de59b83b6aa0a3f111668e193e5f2527fa48cc1be7ca19be08c82ec02ac816c096f0b67f30e3dcdb83923867b7c5e71c823ea8f87229c4bdbfdab1b781f7af3df637707fb467df4a0174b78d0a29b8deec4e819aaa958544a7ee28acf486e649a26026934330cc2f9c27c090632d271913e2209a474ed7e31fb1f88f9c3efa058d1bb1dcdaa8bc1b0a94d30f8ca5d4643a0cb7c9daba4131324ea917c8ebed05026f671c0ec55c22f3cc0536bf878f06ad2b0fcfdfa8b52f1aed249acddda836e2161d68f4fdd14efba11ac89f75537a191d74992e19eee045c705adda4dcbb09c4c086932458f4feae4e6ca1a8b53e01c9fa8c1211a0535fd9121164e6e3d097d2512f8869279a6be29bb220f59578e35a545cafd31657a2e90e2a3e6173fe1050796c9be751f87f0d7cbb92877bcdc7bb21a3e81a2b4449b916810795804b70c1ee835d29de063e3c11d76f7496856c69abf6b08d88cdf8dba5d75282e4253d21e5f6727533bbeb0674c4dad10ae43d643fe3c7a3756815d21d76763fbebeb57a676c7deb32a701ee91b649a738f1ea7e98502c0c0a7aea68a9ad25c9328ab968b7ab8766400234296ec21f2705327d94cf0dbecfa922e55434ced87e18eb673bf8875bc402835f59c40f87218dd88cfd6d4f1d6019502149dd6e312a1c12b52ad406a150c425e7d4ac0c4bdfeab516cb5db4a615952e90efff8c0a95fe5e2f567101dd081b3b9909cb9dcc913df642d291869d2b7f2d227a211a1399328722c64ad260c3a1cc309d3d859340e9e442c7a89c30d9867f46aafd4947ed8a830345d62945c02253e051aa2087c2c2addee5235368ca20809611e5474e1b77c635214bae3ee3099c3cf381c9f5a31acfdd66d1fe938aa931ef3f0e6c9638541973712352262945234aadba2cf7871d97d74e705b03bfb2d1e98c92a7477a1d82b9e469439e537d65c2471b20d389b977f95cfcbcffab0ca650b79dab1c4c036aa132e47db89809ca78c8db9f2bc5bdb93092820728e52db84cec26e434cd03ac3ac022c4457b4b7582139ff1692eaf0036fec2a20718f855c64d401f8f26cd85701dee53e5c47fd9e553135a3387ef64d523027a4b3ab21184287a38f184287b991f43a5d233a6e9a6aff4d54fb8ebad91a8297c273a013a30ba32a26ecffe9fe4819fd9d07faf0eafc5272684da6db9fbd3a7f760cff0b62a3754ac8f86f7753cdb5f0bf443caa432fc403d727f423965cb3f927c12905b8a324d47f5f62f6604b3e4bd5d46fff22a8a78f0c841a39531c770c82b019eb7541eae96cf570baaed7501fc7ac126a217aeccdc5aa3fe1213d4a0ebb8bce5c9b4c07621585fed13e07f51904b6f16331d97509d019436c090fd02320bf2f2e4f823437a88886a991775bae47c505c426b7990182c23928833aa10c28a965a6ce88d0e79aeb164d0a10fe1889868a0643afc945b4f5b0f1ed249ea0cebc915f69354130bbb0fe770e089a5479716e32da2c1a50388747b2511b28721a3c2f288a9a553d127726a3d11d6b1ca607c2ee3e6ceb01638cf6f885d998d4dced1c2763b722f58eaf14a444b6ba3766fdeb46b3929485e62df897559a4297461d00c1ab184eef05fc64463b64e7b04bf563186abf5b08deec51881483faf549b64ea429c99bbdc3c181a086f7e495d741779ece2da2e4cd9555276f42b79d0728e5cd3253645ce74d59a5a8cb06370c3e480aad45fff1efe43abc495823fd841de39936bdb327b199b93662d65e2c3ed426ad838fc45bb6026d4b7e7333da1c4d12938130d1444c8ed435c96beb6b926da81278d07a3d186694a2515b20e9b8de574f4194fe8e2b9f3d46f63fac16518f24d421659723c57b4658db8673734f0cc2507e0141864e0dd143304b87d146c26ff54d9f7462e2b8aaddaa0d82783a4dbae1579bfd193a874bf5ed63ef38dc034b5a4bee5fc2683597d00a8c119c7ddb2841da7da13e506485c82be5120c794fbb205cc2299bc32be0a730d6c7aea321893bf25d652064d4575b872a41fe77d641244b610ef5144244ea0c3898fd232e118f912f04f7541921c3cd0fcc9139f03947f8d36aba1632d52c3147730420d100921c1c80e6bbf016285cae4522d163c51137d7789e1846cdd453ced01581cab226804f2156d3ff671ef1a38fc02620fa26ba1be00ba05be203a5ed3e71b271beaa3b9615730c97737cffc14bdfd255ec13911bf4625f05f60b3ade365d8c31397aaefa9c1b8666e7a3d77531a98f1f0681627d98de82ad95b24262a9532b1a17f85856542fbc90b8e21cf2b5eaf77fc2c1c676b81d9e8a5d8272c87c0cc032868432c3b2f02acc5f04db773942cd77aa971f60499f7bde6451271150689957b52141f7cced7163e919219533970c0c40a7f0b13330bbd35b72c41cbac2f9aa8e7c2b28498309c3939c28bbb140d9f7bfae812240e04052e1c8d40d49bbb866d5601161e789c515a53bf160dce1f86fa02116dbd3963ae3552e5fa811b506564517f93243b7476373c9b40e2549b333899c8b66508d5e4e192b22f30877f5e91179e4fb61e198ea98fdf1225e43d865f5f9cac666ab4903dafeb8a48135100f462a878090cff895daa9e89da47dc603849e357bafc21a7bd2881dec5820366b97b823b9b9e54c8f45cac662d8f3aefcc9323a4f9cc9c4845c0d76da07e431b6cac3bde5146f2ade50b2c72c53f59bd2b323ab47ae4b21f5956e9aa4db07a0e824036a6d5ba9382e448d99ede78883b5c47d6ee89a540e9a518dbc73ac84fbbe89f1c7b41ae58acec27930c913054b2c0ebab033355fcbef42e26f85b64f41208051176a0743994fdda0738f5b6a8b698591f12e4dff18d8f78f623406d7b0b74e5dd62bae8885a02533a49847e23fb8803247731fb41c4da084d88596257193a48b023edd10ba81ab58905ca9ce95a80a57432874d3bb920834a957e83c578e0090d50e70a291677d42441f9334035d6327c163471e03c667d4140567f342b6d1e40fd937d1632532b885ac7cc4bd90e2f8ac14d8f7889a3c027730ba369015207537b863a40de12b4196dc615e02f4c3a274022626f694c9b8a2c0228acdb5c65778bb9baba8fb172d2883a9861b6f3f6104441813f06e17852b8cabc19ae28fbdd9aaae263148b4800c99e9fb83c583191a7f545a67bb3c17c2626a5723a3611829e08e60035c45baaf8c86163668ad0b435f62da346b37774b438fd3573d17834aef4fe8383fa60042c418b5a62082959115588fa7d16310c8457cced8b1156a556799c00646e4049b0c46720e44e1446328ac76d4df84acfbc0a729b1bfcf380c65cb1d37e4c005b6986a63c8a661e040dc1054cf8a0d4ba29262c8197d9d50f71f900d1a96d94bdb56f0c9e025f32f1fc4bf06bae929ada326dc5c313137780cf00a4a0b8721caa6470a9962291b308e8f99e3777af824e2de7cd20a7e2a716a58bb8eef172d51e570856d9f66a5bf15a80fd4b11a35b07799f8471558cbba42f18b4766a6ea73fdc241d2a98533622bfa284ca6baa2b2c1d9492052e1dcd6dad17236ac157c167871d76456fcf274805634393a34c5b2d6dfecf49c5f9c5b6533a538568cd4dfb5be14446841b7ac5ba51ea5cb9fa47293406002116307b20de5c9543d142be9113be570a1f28e976acaa1be9d5f9caea408fe7a7d3a9417fc5f62a02933d7382a9b6910cf7b55ea85479507daba4eea2d90e78b78db939c285cb280496444a713584ca79ff9b40ba0db389624bb382b9f0f9a968e3b72050c3b746a31ed70326fe9de0d6c4cda5caeb5803cb7bbf294e06bb907930e3e1832d932a2da98b29a7130cb61c2d0d0033e03a56392ec53ef8d509a9129fa9e2a9d2e4b4394c6ed26bc6d488754d59a6a8b119b8e35d27b586111b555690731f9bccce207ea69eb884ab73b1b8cdca07e09a6f00a3ba7576e63b6efd5f83795aa4735c34baae27e615a81a2578e80b9d2fd23f4427238b34eda88ba7bcf32c3c3757d5b0c919c0ef3936d2918b492c577bc0860a4ff718c54d9493ccbe24bb69b1c6a832943a43df9367e2e08cd6ecbe18b04f032380f0eaf37a99a017b603048ce63f070b1c9f3d57a10cdb712dd571df5d825551a2ace482a75031ed10246e55f5aded649fec41b984010e78b55780a27c763551668eee2cb87aa44edf8c13763f44315a16247d840a4611b7883863f0f01157952df07a92feec012fdbd56455d7806d8b431ec284fe18d4097ab625f583fcc7412d9cea5a18919024f626131e7e391e3ed8656976637afcb74cb4b6f1845c0cf59e63298b915d013f0af616dd3f485cb6794d065f0c849c734d5dc1c67daebd5553ea009d1d2586e8b2dc6162ff09c4fa369595427a27bd9098f2d33889700f14ecca7fbf489d4ce93df5c77344b5f95951082f854f8aa79a02751a63fd906c8f31e7e1b33f502aa10fa99825d6eab147861e41f3814bcfb063d5c31418c04d19263d2456872c13cf7cabda24feac75fd74e1ed5ae1f38444aca26ac4bb123e4e5ecacd5222a1cbdeb6578965fe5287a7642792a9b91dc8ae41851a80a2edf192a3b8d47a369c259a76803517d91800869898f1c2e51608c74df167c6d62d6e550df86f920042dc71ca3862063ef02225dad6b9c5d08d50a1fa5ea20738c567855a366407312e9802c4681d11ca3db883cdf9c2ababe724ec14585a6d00970b7f23b7782f117421558ceed2b47b9a8b73fdfee016e5df0b5b7144ebed4102c93cc5e4123999d853d2a0315501cf1ad062ccf5e8770c08ab53d231fc098458b2b6af149abe84e81fe23065b2c923d7caa2591a8865b17bd4bd1c9bceafc9268574a9b75d211bab0726157d758474e14f3e7e501170bf9546a723ee6bad48516019a99b1509db9b8cf24862019995073ff25ea52770790bb7ccc46b43b44adfd68be9ea1e316958d21dcb1820653468468d1e1182931391d736b46fc81912a70e96552ecc08be14a7bfc2212e16c297c8f145214d92a06a3e2a84e1db35376b16f00e14287a4fd6656f5f47162e383845f6082ad45d5c41c0ff779ad87e0bdd983e1602972502754558e05bd67188c516c97d7d2130dd743edcdf387369574decbc75276dad3277fa311d7a9e191b17a1072cbc420ab91eb2c2a1e156bc280cc81f65c12f7f2428e6829ed2c9edbbc36a4b935c82098edd1a4c0d2e9417113e59efa56ac35956bef0abee79ff0efdf9a3a4e287f673b921fd2affceb4b0e0d34af51300f5e40e14863938687f2f40c84a49a951513bd55fdb1a11b28cd64310c90b45c9bef227626e36fb220c74973c8514151571d19ad1129e1699b006946e2653460fb862d08e22cfd4adee90ec77ad529662e9eb072c1cc6963c8ae00c4932580aeea306ce20e1a7da908a7af520693ef98c95618f6f37148884711976d78afcc15439bdc60d3f585918f89daf552d149d3d81455966ae29977e1dfeaacc40ca55db3a47af48f11a4e76421970d40e088ba85ed7022967ef0390bec7315b8eedb5dc47a22cd16986e8f5ba5910b5121c06d57b2e073f82930d8015b2b65e6855c9aa02fb5f1e44a02d71f2ecc4c3551202e8fe323de716e1f83d63008afeb5788697ba52787c27e42c6d6a470988fcd4d083c671bc5200da87d46009133da257ca0c893d43cf622045e8aa187a93017a8e829c9ad979798de23309344634ed4398751109e885d75ff35a682d97472a48353bd55300b13d604ca6d580c518e30836f324073a12c5f69ec2a77ecc01696da5465d4ac085f48e400b9a236df8b96497485a67511919c14c41944ad4d260c00f03fbe051a74b18eee885aaf67d5d2fc624203c90e2796bbb0462fe8b987942fb1ba008a11606301ddecaa95372e041743853f8ad1733434b387823a4790259c4fc352e3c56e2e231a7fac7e6bbf82b02c74005250e760193f973fadc7565e3e6e41f43bd0c319b94879f8e935ac9fb1ba5fa81d697ac5421871ae39dd02cc94cf1dc52309e79a855288fca8c14fae5e8f345f6ee68b8cb8f0a80e1d25cd917a15b9b13aaaacbeb5946461b3388667e81000165cefa952a189db9b5dc043ac511290a1e757d801fcea739265ddeb2c0c63f6b7fb1fdadc7b9c934fe9ac2a1026c3cb94e127beb194d24922c379a8bac470b1085dec6f8eeb412a1fc2b39ef07c1c24cf8bcfbf38092d614130c661a262324b4eb40a623f68aad2af3bec53b3cc53e3414ca1268bf1450b7c85219a9a52b1fb15d3cb4650243714ce16a450932b30d0d1a189023265cdeed3ad34bac0c2195d63f16055a9c6e8cd3b5cb41a1402c97378e54dc26245195508ae45a60f23d919e14d53367856e98faa023e0f5a272eb5e33b707708684f485395f9a0446162414cb51f3eb82a1d1e2eda68cae9e8779382e4b351ee739532df23b4d5e649c7717816db2953762a6abfe490ee09210a5a10b43041f4547b34f5575a8b46085267e4b8a62cd287ca68b8c3992b8158e4045b715fb9a38501791e27b551bb4b14245256da1cc000ea844553e4c0a421f5bcf0ccf556d9dc3990a3abd5be94ed041cd5700c590854ed03ca0eb3602d3bf6c57bb7c5d7443c9625e6c11e520f6f87b49dc824df319efc5d4aa3976dc9ca24f46c638f3f37dc896ac989ffeee5612433bf554eb5fa50f3d624bfcf514f1a64f6a9eb8a6c745f0b53362c9e64dac1b378bcbc24b1c63438dff71e203ad2d1cf241041d2c5c1ea4cf5718782a455ab5a021b6a19a19b8b70fdbfd362946b6dfa58602f34dd310eeb1367e078b851fb53b5de0cf50c49b8b910dc47ab492345c05a721193b191d11d185e86fd52e1c9bc41008c9b5aa09f022e5e1463e66d5f2c6933c550dcb33bc8771cbe24758b9cde905e88f62ce969f4c61cbdb2a5384418a9905181743a24bd87dc88a877891e4673c38bff5b9dcef9ff4d4b680ec8513054f7a25c114aa34fe100556960903f479541b145726dd74008a9c407a762ed531e8af332914079432f50318797dd74ad980064048b853eec9b8d6b4435bff03dc74be662d98bbe56a2fe39698874a121999e57606593359c5a04ad28dd218d2d89984b48e6137425750d8d33166b28ac58ea6470a93b588a658450ed57177538e9b7f907eeb62867c39faed6ca0bcbfc97f41c726e2e53758bd4756a45609a7784f305b2694f91327df19605244f2c10430d361c25894d934762e1d70d962bbf3c4cb4fa8ba63ce250c1a73d6a3551952b4ab82306ff73c507d76dad79c7dc722f9a7a8e5814dafaf930f052bbc832b061000dc9f06c184306dc32b420d702c26a22c904aa05d6e9b1d4593f519f3a5180a6f104243c4521a464ce1b46885875106a5122dc6c75379f8d22cac0429272d101daf4c90abadb50d3716fda708a06064ccbcc8d384cf070dfe75b17fd5793276f465c114e23f4d391d19fc9ebffbe657f7f81b8c34c4a4147f4191d73860e4a08421106fa995c62c6e55e601d621b91e31af0445f0a9fcea90ba3308cd2361ac8c3849904e2ba50986b5a1c0e1d9b92ed008f8a082a6783c607ed6f16dd8cf47df04541484130473915e620ae96aad9d0ba28e20f0b22a699e7db17f5cffe2b750826fac492ff33dbb95623fd13aca35fed5ee3c361942524aace7fe0a907bd10125b032d4c56a669ea50a4252cbf1acb9738cc46545d8413edbc6b70311db9317a9dceb59c605819730d01a4e2ce8ae578415a7397cd6a9e348d70502cfd81d421b90b8d4b772c63de7c3454b4537628d9c9144c47be6d09fcd53eaf7020314551783d9c78289bd163550b273f6092966fd1f379a639104519c2905ab606b75c4e2e611ae24c2de20e48c7e549265a3d04770d7ce89385c4fc5894bc6b406da0cdc073f54cc137ffa292e6aeccc45420815f24a66ac33233fff885239ab024ee0cee950af4d536b5580422b1b9952f5c712aa07c3d38a456eb9faeb42080c7390c59a4a889d6986db142c8e7c6b34db8d8b65a31115e07d35ec4c279586bbcc205b929fbbeb911786d9dc2675157ac493ba1f188a4511101ab5c385ef49b0d9ad18d74e161628a145273d8ad292e6329a9c9005314c5a4b145d4142faaa3de889b4d4a9d415420adcbcc1266933eff510b387dd68fb900d7940c1a152a828fd0ef02fbcfb830bc4547fb03190cf5c9878bab97f1b034582658d2c8f51039bc04f1e3542736352be29137836616a60c4a308e93010d63125ad3121656bb22b984d92ab03214f7c131add49e978177bf5031722bbc26f1c304a9bdf42e1c450e0102b6d0940dca33d4d6b447d03d152d32a623d9652e732e0f2f5281d27b4de058f10a52f1fe989651441e3e2d7eb2d9a731c15c4a55781e2b82cf06ca0c440d777c087708782b335ca87b32dd8a14612c98bef99c24f862bd52d408c3101d3fba752305d56317b8b5fc484044844a901481a0498395e178a7fc6d2d96aff4ca881fa39324ba574751bdb1c6a5f5c0bd69cd644144d13e8ba58ecbd5e68e8f97b89f7d3a138c02b206108dba40246938664cdb5e4011fe6fc3d4083ce41faa469fd03d0c72da0cbc692c477e784b501e12f167b24606802d0c4d110e0f227efc108ca1ec50be5b9fd3ea83679104c2078888b290cd554280c58461a4cf437787f7d397b84996de8ade2006bba191a7aad241734d0f2ea4397fbea860af7b67b70d9180be2775a163afab5ede52d6be4a188ee6ce6377cf46cd9d058186f761f3109d5313d6005fae63c2b40f92504432cca10ea626b2282ff0ca9b6e43242e6d5782798ee9e38cb5e257fb3130d134489ab7318be6e625aaa52c27f53526fc90e62ee95abbd76195d452e8e9da028cf2cfa76525dad856cff524bc93113a147d694af9f979cacff9b6dfda464847f869943c29a8bf840657eb63106b520f55a5b6f0a2406483fd0086e4a2118f1d1b74253578bf11c75433a0073091bb78a109049cdfbb8148aebc597721c0f71fe2f243f60992f6fa584bef85139290a81192a201c330be31b40d1eab08c4c40224434705cc634da9d61c4ec6c21958cc30000e78b1a5abaa78481bd9d946423099d079b5955c1128fba89674541684fd07e44946c56af4a0ce7725176693483c7dc41d401e827b9ea57f1813592d5f306b43ab572f7facc85572010d279b707ea0b50ecb26aecaced20165a0ec4641f55588a322fc3ecb851151846f555a7f32a98b5d1d04b5648e220647258489dfceeb82a6d52d2eb40aeab252ebb2b7fd8e107b00365df95adaafc417fc2492db94f66d71fdc15eccb9e5c376fa7eeb8c674127eb8aaa1eb91a19336ed7678f0513b4fd6d34ed54ae57881a5a4d6643af3512cb58d32269235ac533b93174984118f39dc5bb88955aa88903847200f53b408d29daf821d601658337453729d2e6471ceb504566bb32b4126af9c005980c23301318a71309033ad542109e845753250480bafb50d8f9687f663a90bdfeee663ca0f921748cc0f6b5857b1dd5e0039e6d6dfcd3cef4992e95df30f3936ed66eb7d0797c6f9c53a34c1fbf278ae8b3331b6834a845f6f4dc2837b4a5bfc70ee3db07f00887ae0216513bffdda6169a4d1b661fad79556b6278b33291e3570fbbfa710faebbf3b05cca2b68a16feb6677b3b40292b0a21f0bd38cfd6d64f2e40424f2d4397d90c826b1b8a28df020f86c6e221629e6ea26e2917931817c220cf6338aad2ec501b0704dd8366479f995c32449c3e8932e32fff4c01088b7d103b6147722eed3712955a3308acf0bd4b6c48600d01c11dc95e3a7a4df7f9469fba1bdcb07b40039fd4d5e7e4c66f0fff8b145bd60833d6f34d6b95e4f76c15edae47a4c3ba274ce81f05210bd55389f8b248a2ae01ddadb23988d5042929fe26e47ebc0474ffd1f6ef0f90ab67e73a307bb11190cbd39bf8d409ccddb2fedbe7d54258fd4340732984cda953c1b047c0dc63cf9e60634c7439126f5eb9e045b2c729422de9b04e9ad44ae7fabb85bdc3fe5b73bda650082c4f7c654195f15c6c301d52c082eb28c98d5598ea2e6550dd9fa66e591980a1074c85b1a8b8896c1e816fa6b7e1803b9f83d821e9283f6c8a20162876a4c985ee4d94892f38ce1c2e984913f2a70e3380280258240efe79a74897d3d160c23fe8773a64843471c5c978b77d4e327c7229349524719834e03258be06b1391a4d8178c7c98a988850f9cd5695865ef15838d6f853b0a262ed31113e6bd62c091c625ad205b25a032c9d94dc001612d1c0776d58615313a5245118b4c0b91ae7c83a0a2c08e64d33daa2f19a10d86db15a9d2256b3cb469848674c993ebc060c62104ed690019820c82297a2fe08c47ddb2083c7ed2d233cd370081c8fd634450201c58b2fae6350ad36d48bf21ae9d484556c5beff0a3d4fcfedfda7d9c95fb145a5621410ded5318739ed405a7e714c1792a42f13139ce59718be22a0ec17eb83fae87ff2cf380f2cec44d080ce793e20a1c1dea1289307570fe60eb0abdeb90364862b1dbd2c1d824c329f1800aaafafbd1f9f98205692a723cc666538a44f5db2feafeb5462632a53f2c41d22625136064ff41cc0ff3c9b92b0c65d375ab6ce69aa8b1729bab958e9ff20e676f266ab3d07c11cc894c8f17c57264353c686b1c08f37b912a79d98f834490231952d49f408e61934d602b40a929b3be021bbf0a5b5c6c148022611aeafb58c620722115b54392935078fcd5a4ba8d9c99e0c0872fb9266708c52e41c4e625657bcc2952c040eda7f1aa49e4040f989de08ededc9b8ef35145921dcf159137893a9f6dd89d28c96e1b4905bf2b88200501e433042258c5ce6b0948819c2d108cabec3d57d7cff2bfb11d7212031328b1b0b58039a11fb6c0f2ba2aa5a31170a9d0ca0c0dcfa99fdf90f024abf8c40ff411cf364431cf0e047fb4c58f7a0ce3c6acccee0ac503d11c02c24156e4291cc0590aa0b91d0c2f6357df111a55a0402738a120f4c698b38f83aa32a540c1998a3205c1c75336726a897246909ddcb9948635ee5dc36b71cdc6f09e265c1d46a4905151cf685dea21a54614bc160a82c5acab1d23832dcbacd74a7900606220bca802fa82193b8f49a849cff8642813d0db9f3553dd606f2015d0ebcff6e602b5db02b00ac46419e3b1dc2067ba44dd1499a38bb2ee5d2e1fa950198f2016c6ce62794e2e8e23108bac3c105d00b3da024fb00818564feadbd7304766abc8fb83f8a191b2e3e5b3f012bdd2b0bbc82a7473e0f047e27ebfdb0db52fc3f52e8a05c39d21de8b0c332639ea281623871210d9b28dbac557b6a650d051b1c9bb04cd8478a98182473fe66f2dcbfb0412abf5a5fd7f8de8ad38c90685c50d9492b46d0f108175a67359b326e0e924dd2c679946b3f2b40d00d855833de01350eebc1b0b42122e319e009b78ebe66afa451c9a6a02ab35aea04f50e414728f2e5383ba9c6248bec5b6e210c5dd809a24941328d87881ac81860ead46c715dbeff9335625c56a03f28e2584a8d2591e6294b850825a664b7dd1e359344b28f7734a25ce785a9aa579ff73927367b91bc9f91444f1f1a0670c03ae655a0c99403826d5348cdefc1ccab0842748714c1c419c4821f9fec081a8087123b5bb1e79c179792870151edfdb5b84d878775cda460a95a1507236c9d673231ce6ce73028ea481e2c8172395a4ec836eda25b8838f9076757ce42622ab1639ff82aa2210322f215a4cf49a0c3c60cfc2b11001a914f8f858d751a03c395628cce3e4e9b5d88962504e8562d21c455026af654e510631361144b2ec48770a0ec1284df1653c5fb317f57ff4bebaef2cf1bdb356cdf7a16852dcbde399d15e752531c4bf513247f284ca138db46e1a9d0d6529e2459c1b61efb9f5a90c13bf56a11eeaadd6713dd2eaf601958a1c014ffd2f4615e72d79c2e94094e990202a0cc939bde89cec17793cbb08ee61462684faaab5685ccf94c71c661ac3643edace8b6b8044f3c642062841a6045fa221684d31e6d3e1f03a5e3ad502b41aaf55be90945aa8cbf13c5061d5d53ff4363d987334088766c40b107510087718ad2d93c86e17b7cd563e1e84d454cad49c7583075821365c37968cf795da6ca8c0c94a4737cb38cbc6c146e0a6fe6ae38f257823c98d7b66b656fa24f24222439e13f8a57c372574e770d15f8f70a47cdc62911985c6b5a77c2f3c351beaf5527cfd5c44640e3c97297d9b4109b05204ad13e771acd73ce406a9307aacac8233ea03ad7d6c63ede4ff98fbda0d9482405c032f525523bf2489bbbcee0b8d1099121cf62ed1b5fb2727ce88ccaa70f5d7fdca9aa0bf0e651811aa36608fcaecfbf2d718688c7f22128893f04bd0ff7d4c9b3743c7f892e33553b43dc09803f0c330b7aab0f016bd2719675bc521325331ffa34c5ae7d6788dabb45b1b13fc155d1ddfdc70d9f2be5b65e8a26eafb57b20ab4369a31fc0fa65d61f75e9aa3fe4aa360f5defcf11dcb33bbf16f8a0cb727ed34f11789782e95be7dc9b6016ae7a3952ad03817db3acd6c4668857812430c58692656af2d50154b4b3bdc8628328ffbeecf4b30c1226c9aa5d11105bf215595da1ce3d20130b1a72761b3470f355ce3a326f28a8dcb3ff48d6d34fd6925df59f26b9182f5726341907ab623d09f9d44364102ea4433fe0410050ac4d12b5a53a000f3e49d41370ae5c23df7058c3138e4d0ff85c4038ffd51953007fcd6be5eb65ad35e80c78f5e07223787a7a924ee0a8caaa7aea63106d8b30da083e4dd666e2d4aba2f2e66b3924c776473aa32ac459bd5740621f8bc758954b565cda150e3312e52fdd247e996f428cba1021a2fc366644a677dfbe2498a165dca0635b445b0fdc2682e49ae5965c9690ca72bb285cca9ad85e6c5cf70e1144b095d1ac160421ddbd0e8c436fabba076217bc0df2e69098a94dc61cfa27f77a44f931903ab8e91476d6c84432f44db63b687ac7c02678e1900688cd583f4a3365865fcaf4a66121bfb3d934a51a3553afc33bc1fd77f69a1e508e3c9b51f0341d6fe840f48a1ad883fcbb5190096e26a73ff80f01e80eab14f49fff97d7bc0b59324ddc3de48e8e8f7cc0400eb8f1f23d8c588a935607dcc83607a066b512448cee86bd614a8c9e5a43807a3d0bafbe64b240b475c348f98480519635c2e12cb1a0b2b06c696193bbab880baa2ebc861f34ebd88567cb5dfd94c1cca7225e4e9a5c659772e68cb8a2421497a41851dfff759df3c68701b0325a3a564668fd41696d627ce4928d52c59a924eb67f69058d87fbde1daf65ff3a850d1007d3df8f928a95c4ed1fe464a6f90850fe852571849137edb0308fd5ecaead386fc9a10bccb654fcee75c8f4d7be7743b54380898208ec7e00b825fbc3cfbe043f8f8e984a78211f5239b55a02085bcb0c826800e86baa5ce80d0971a0421caf6983a99c9293fcbde9b4adca7e2a2046c8b82c46520407b59c3b6ec71671584096aae4a5599378a4105aa98a1880fada3a425e4be9a8e04089f90ad259420241e53b6985aacd0114ea26c2de6525b032484ce1b8e4f84ad1411ab8e6b418c946974d9b80c10d6359719432885f6cca4800ae38d37aad0be12041924c7991b0e125210d48f3ec8afb195ae8df38729abc9d8f0420375873902a1cecc0465c036d00f4cf82090462a11bf0e86d3692b4407834ffa296d534547ce5fbe8ace3f05642644c0103e0a5740f415736a8ea459601e19e97651e8519ee21f5a1ba608c4a9c7d578fe2341f0d46c82cb680616c6bc64508b182c66def08959218e632d01514187ec8f7aaf120887f0b6f62af4c5e1573a5fbd69d9cb828824c9f6fb5196712b151c0b4bd399b603612122ead94dfb45d9d9166b1d586f2f48bfcf64cea22e44065b0ab69c9c8fd28f94f7b1b72e1a6a8f8f8b1d10062c5a4f6469478c824a3b803272ce6ead3bc0bc19e500802ca65a93e3cee933cd714a8acc95ad4273509f3ec9312a5c099ea2ce5c433716e4eed08ecbec1c61adfe9b8d1ce6a416267506b456ef4e0a3d613ea3f1d5e5a9a25e4a419a32cec9a55a51d3db8a5c6ecc5366b4a57d1e49138c8088bfae02a5b6b6b4af3d210f78634fa95c2f10fc0055c5824dc7ad3c1db3510cce39b0cd5a3ab1918d886b3f684b90fefef9ff90eb8b33d82c8fc289bdc52b6ee50a4936f9ee16d6c60be67449b5feff1e69caa548a2e9e8b946f40984f23696af49a382aa1601e72f6b7820dee37e986eb88faf2482159d787d66b0f047aeecabec55772c935692c55ea28ad62febaf88875051169598ab1934396227076f698c0a3f89110c0c4700f4673a62a90297e88f3da14b5a2d3aa58717c4f89866c4a9c00eebfdedff9d668e2c6f6d242eae85a9084f426c410fe1d44ae6741d094964aeeec02ff42c8875dcde95047b246f0fbabd94a1c49ab000922c1ad692b9f05e09108bcaaf3c43d9504f2d84cab2dac7268b1d2eeb24c9deca36192dd2c8336054ada7d8cf1dda0dbee65abe717dd1bbe4306ea0f08a8adffb9457aa6a70bb10e260dcf5e469604f521844d92640c8d659e1dde8dc99b2e8d52a7224b99cccfe2561f3e5b81a5f18f4025d72af11d838a599049cfb4bcaec88a1f7ae5f817a2b815d76b4b512051ba8760604a423d981f0612c035a0c4da439384ba284755890b5e5b896977585b41ed1ea82f93efd3fd3e1809174ba5783447558466889180296c4bea8b1845d7fcfddb878230fb9b57703a80890b1efad457b3a47fb51940b2566b4e9df38c0c2f5f819181f3698d1aed7aa5f8a1ce282acc4a301e76a00de92b1de5472300d563d0af93b11ba4c57f1abfe3227d477d0f4c00db2ab01aee036eddc2979af698db533f34b0a8444f3348617a469278a26100c71bc923851590c43e92a489a200aa06d132d7ef6d870f74245276d6b6fb3705c1e394d68a626599e3e68d65dd5e77e0c20efaa0c0526201b21c15265a6f8ba04b610752401f03731cd59176888b08c4fa601d1643c5860fb5b05ef664596429bc55c9a73f1b922247ee85fa00125f7483ab40601e150471a07a217979239e125030aa8c604899803569798945f16e8b4b25a3a3017221f275dfba39a4285cea54c41487ca6f41edf641a4e37d88dac49a37274398cab476c5a41995a86f8a7b363fadd101066d17fc3acd7f97bf48198541f98635db7976b460bd4316996698568ba71b05111ba2c7107cfa865716251774d00a611ec93bb1db5ee478c6368f023001592c63219e6c60571f6692b8f0b743959ca23139007983ac00c6fcfcafef4744eea2d84fe8b6a4537063b6b5f60d7a243af9f736ace6d0181a1c4ea5ca5e86b6a74e2a15150b368462359ae456c49dee344166aeeff851949001687c9c5c2c8d07ae87d02d2a43416f215ab08b41bc01ab5ab4ea8a50fadf5a26372cae6aea42b4974e5b7b79c00c954d0b20ea6a250246b8faaa54331b60f929dbb5ec0daf0bb825a70fb9ed9bce41ada71a6480226748b3a77c046124bb2806b1cadd3600cccc2c9d67fd60e488324723ede4110573fbcd51103fbb1d9d99d00729548cf0eaf9e32f42c8cfa884bf291ba89d39912010a1a757e1964c013bec0474e88839956bd6fc1625e418e4817321ee27a3c0ac9aa445e5062cd9a4dda1cd03618855890d505191527ec18bf0242d41d1c2c8e18b515412ce9c5c25cbdb0babc51012751c5ca96a8acd9430c24c63382320002dfa7658c7075621cf5506c4c42216ee4d37ef2e8575ff51b50536e11ee0cdadb6ba14d96619209838955f55d10d1dfd1c81c44147b3482a71c5482f1d22ab8fcb9b3842c846af5fbd50eb0a52c34a8153fefb2e2549af0dab472000468795110ddd0f9acac0e8e6865a4da3277bad91a6dce137f2e59b1c4bd58f9c643842dd7bb6f1eee70dc6f100f2f4bd795b9593de1b013ea8183343130b189607f1523371fb34f1ea0b682aba5b85e58af42941e15da5195cde5e21a9e9460adee2e007dc17c813389817131f8b73629e29984db0c538aabdfbcb03a7ca261ce0d71c2d2224acfc72c097b869549a793d74f51b9a852dac9658dc565017b1720f86ae3ad6a319ce19d5d5158b6063fc20b4a252aac4d8088d283228385c9dae38606c74802e7687b7c56b7315a194873e0bdeaca6ca2e155ab085bdb7a774681a384c77c563bf89e007786a007036a9cb72dc96686d1a50586892d438484864eb6c87c2af3d1a65fa25ed3cc6336a2361aef15951d7230a15234a366784881c601cb6a985dae6bb84e6b0658e4ceb99142712ebb12d92243233c6283ae260569e26a73411d6d52400f23dd2a596696ce47a1ec23755029f82484428bce73a9c1c914988bbfc3457fef481e26c779640350ed24df20572422bcfaa1cc2ee25df8dc58c2c6fcae8c41a3e3bec21326dace00b43ec224e7980c6d14cc2d53887d07567b6ca7b52e5340745244a565616680b1d8a273460031268c1bc37699829f047830509e1eff340729b319132acd0d51823e2505a5c9da66812111b2f073b7725676acd95c5bb7ecdd0036f16ce4613378846f6e48b04968a54dd66d60a5dddc4b1bd0b7bfc1de38ed37f8a660859d866f169e006ea5595c34236b59f26643c6210a461e24079a6c42104287c43ddc58ba90bfcf5e2fd41282c56d83281446a0be7a40e4325db217c95972e30396e40344b6566b3bf3ba5e9b3048bf62c7ab17277e456342a4d89d47eaab541bf93139b3e309edaf0382c00ee76d80897583d867a84d04b69edc69869c8ccd893b3b700f73eca19b9c9b948487b4262a54b7ea3ebec5d07d8b901b434f60c6cc06f9f8c4dcb991a998afcd5f2be90a797e7c820d9c9796e064a868593309b851e2fb1933e65ab1a61e013019afdc28740a2240877f43dc15223de8b5125c8700cc2290d7476984c6c75c7ead9b557076bbb61fb29ef92069aedd66d03bf1fb1202e979a45e6f624dc9ccac9297a24a6bac975f85e26488037947ee907648d2a3791f9ae7e7ab132aeb7390841463e863e520c475247adcabc09bae7de65d1e30bc9982b58c643c1a4b172b346759f1182d8b2248dd18a90064fc5b3b71941daaef704e1a74dbc018b0c6b34eb712b03aad7367ca9685aebccd2c392545a6d5e94a459fb770491ec0328beb138c4773f3e8bf761b2a70ca5dcdb2a8091ee6aae4a8874a1f1c3a3de48d9657a08875bf0ef2ff67d800771d5b986f78c775bfa835d22274626918c15b9a00aeb375bfe04569e1cbcf40f2780e9af3f13cc0b45cbcd318e68d8aaac87ce119eed8c55d5283b2dc557fa95c0d9555276ffe569c1b7866d1da6cc4ffe8531c939843e8aeb2381c8dcd9d094a3b3908eb49c8672dd0f513fdca68d9dfe3dfd91f4f2f9c748ca93c6b701cbc501f605542bc3c97d725b7b6390ab240d1091a76aae011d50174b2bd22898af32edb2617a88d0d8dc21da2224750f84528ff9cc4faa691ff0df48cb1f0644f72075ce7d484ebef7c8efdd6a36e5adeb9a0fc5b41eba288c1f91a3257890f542661614ced1df3187a53cc0217e20cf03e585b1ac191176e87fee82af1466dfc775f3a4673b0d1ededecce65a92d379f17391a704b53abe695032038c656a77991879aa7b72b8da7de34c07908ff89fff57afa5f2f2d55493bb663599c28bef515df8463ca1a318923a1a993c1fe7b4948054f1f429c702914628d6ed229484085b174dad8067795f7d26dcf96891f25bc5d869e8db5e98f43701a5f3fc5e1266c0c2b991815bf82b3608f31b84542209eb4e4c6042af3181a59148834feb037f6e59185148370a10bcfb48784e28539d126d720b2292c7bfb8f8edd1a61d5487df099652ee9a225c6d6f2f8fd7d0d17c6e9811cc6d00a8876c0c0a085e78556cbbe08687e293196b5b61b812f0d36c5b093e2438d51dd386219cb72563c36018398dafc44858b1f3889c53277655c73d4953af814f852454ec39fd94766487bb24a263ed18542a1071ab91dbfcea59f0109c5ba588c6232d4b07f3a6ef3767d6a553c757b66c719174cd0a01007319edc3ede26487ac2b98f68c9dd540fe300588e7b37fb4e206c3c0511d7e2027f01f6dbfb59694f5075c8fb611d50365cb757b1a2519a325be31290621a1ca4c428a3d1025b354bfd8b8057605fa3c91b50d6f4be8944d55c54659bd472db65c58d9fcb962b92b371ffd3a30303ae709f886ffdcd43ad66a8a712dc280553fb64fb092cff36fbf125960224344cbb28ae91f2577407ec036ba6f7b7905cb259512470112c3cda89cd5e42897c2947bc65585a0baad60aa64080824bd66596c85db037b5e6c321c58225720cde8671736577595bb37b3ab21f0e56f580f43dde22b4ed0e4ce1aa56a1f6004f833c3fff1c2908d1414daedf9230b9b25ac72a746da7abe0432b44d5a0fc58f11400faade84edf9bace4e00184226033a6ee810323b6e42bc8d1ae567476e9f350c857c66092d61c46d7da01a4d7358f65c306ac4a9dbb5dc35c84a470b178454d61f2899160b52883de05d1ba7f671a600a27f24710eb51cd5233eb061c22c0e1d677402e45615910a2fcf05ea652649a2801f9e073b2cfe2425e4360200b6411407a26e029160a8e10ff1f385e3da663eefe4000b9f2dc12bcd2ff1ac8436602ccff0fe6fec342cbc9d0352e22240b56dea0671c984cc6eb35d97a65ff15f48e005c72b553f873f7604e13d8ae26f8885aa08ca81446503a424d6ed8710054cb122acac5e8e04856a16e8029101a3d438947ad409f863cdbd083b7812cdcebcdd70dd8c4389640b930e8de5d725acdfb2aae937d595932c5acab8e221c531c007e89c3c8beabc422aa8d427c09dda74b54e2be9d81669bd2fdee0e3b1a0a5a514103921c593be91049441d701085a0624170cd677505c79aa68619a3e359a32338e4cd20be1a1ba8a38ada1f603662dcbb301205a25e1d2a83ec0bff82316334688c37386ddd9f470b971b23d3b21ab05260f86f49dab32634f465dc7cd4789b20672d251d8ca37f71f4d88454fa274644734d345c9b1986532d009a639c76b84f1d22563c2ad752b99a99b39b4956d9dfcf964f339bb9cdc66a08fc3026fd2c60ce3df7a83ad128ad1adb9602cfb189041a2bdd6979b1542269c14253e76e188bc2373c4eaa7bf015f49a75f6d1b09c653b951be3c156db45d4562234aefe52520f5bcef29c4ec1d2f9fd56a69cde9040b0c4e32067a320bdd662e82607b826bdeceaa0adc4d36a35c014377816a67e90e225f855490e788b00abadf893d2082d04ab7c58abe7697ed078380c6a180e2e2e95037838a183c72df89773fcc749c62516d9a7e97486e16516ca8ac4ed3fcce0dc6419b348d1eb3ca7bafa4e07241639ae5386346623ee99ff99d177b915b204d008fa0fc039451d722c1a674bf633652320ccaa03c8f54dd89673cedc2e7afb3ac16c75edc44f03a9731d02bcf8ef80d720420f3c1a22e005ad8800b3dfeca1682bf46efc0005239ba00a87003c286bd88d8d7670cf7108f809cc0ab835bc93db9c1480159cd1122f456e3d04c464fd3f8e7fa75640f22c00ecf6b43aa97e6c91816cef9cd1fe08fdb2c57dc77fbaab0752fe5179b9af0692891c72bdd041f338d9136ffd9b68ce0ac603c7763e0890303c5d553b9e2b5dfc0df3ce29b81d8521449f6996f4d1b2f1cb6833b65b631d5ca7c35e382a4f18ae80c68056452a269eb33ce2c1463f71696e38c4cce75bd0b94e97a438bb133b65606b12bd173319303a5d9c830a17ac6798e8076d3ae7ba96e92f48b6afc2b4cc0a5d921a83af00f45e2e46140df51af25fca2c06affae02dfd3f9e88baea2621bf4e6d9d73d6ea550cc80743c76d98df57193a61a6f52b4e389bd6c8296cea196e9a136a2bd2711f543f3f0fd2707262cd9f865d80ac98814de31ab2e5add261d448c4a6397304b8c697c487dae57a09714b907a77b88178c1a10561c2cd657a183ca31bc229234428501ecc3a3f438afe45a391a9a632796bde66f5de5932371f33f21f3d105c7551d45d3ef3801fa88dfd247d32e99995396e297fcd218470f14c81959e0aa0ebc6c828f009266305964f8b5997ef9393fecb42387daf570ce7346d2a4b765b998fce3b85b8dd22facd2101a10f01f41cf0dcb1aa76df5a2708de4ed664560a19730ddb08c473458a4ad8ec254d041023674c25d330e060e0d37c9f0d6d9002a923a726c6409eee403865b584a533918949c6fd4f5a842af43783a1ee80ede83a32a6d1821562100a47708fc5d3ef81733c0012f4f12506fc632ec1a2217de3daba7eb4367ab340749597230a9be0430f3d3bca0c3d8f58923d8b7e061410f7b50ed84ff3ea142720317eb653f9ee6203bd5e113471f6f6bc35fb0a2bd93004e14a2451713553130283fc8d1d8bcaf62c38a164853881ebe5ed7d3c0891e81c9c21e97b15c6a67474a7f025685baa6f8000616172cd3af61e94ff376f6667cab9ee5755d4ee674594012db8d85f4111082ec35ea86857d20ef57b749101f06524fcc9ac7ed96af2ffea419d149e7334b1c218f7df12938103573f70f47fca209b114b787f878b4c00ccc523d96069c2dbfd63090bd03c8615ff10621f2a10fe3a7c62e1c0ac997fd9c3d027dcc6dadc96e5311985d330a57bc4c437060ab825911ce9f92a2fb458e206d8dd0088f9708c60663c36870157ad5e1a0103bd4ff6cccf0b6de03ef722e647fa4d6ee0fd56bc4619cfc8d5c192185904ac87f6448e38c9ff4dda09402e86de0bbb24fc9c2c310557e126895b1e866268100211616e5c3818e9e94368d37d4f57b06002217c0be9f53ff58e20c6a10bd55420addb251c778cbec661dc046ba5b7fb5a10180fa80464dbb055e23ef9a7b1d22413fb97779bf811647c7ff04909dfb8a3059659ca5ccaaadc8f3576a72e039ed6d927c8b0c125ba4ca2811e597e67ae803dbd13e1f71fa5b3e75f0413b03f153d843aef9651ad23cece89762fb9d4260b7fd354e2433f29787fe7e967361e7b4af457bf3a1794901c89d5d905be55eed84f02e2b70802edf7823b3de9db2ed9c30b86085875b7f821a5695f8bba7bbf3977784b822cfcb0a551efa30a865ed193987bf0c3222e3464feb738d61b36bcaecfdea84219ad07d83e022ae172e7c1daa637c37daadb68d6743e3a0e19877eef584bf16641f87033ec606a3f183f8ae06a0b80fe100a8e3ca805f16575eeabec5917efa6f66c5ebbdc565ab94c3c2d65cb191bdee9f52005c58043a1cac32a9f3682634aafc4c88ad3c10538bb4ac8cab72368cd9e47d18576023c5861a8dcd4cd5a37bdc7544e13db55cebe54f8e9f54d5231e2fa670fbcd40b220b968cb7f643ea7416473d670ea67e62d362dc0435470093bd9449bc5a83ec6dcf3c74aaef4dd92319c092cb094320c9c6e4c3e3e15c8d0ffe8b361b850d5ec2776b6c9efacc511dfb6e65d2c6bfedc8d94ec87cd079606b4478422d7e695b7c5bb517206209fdcd682bfd65dcf8929b2d639ad98aa14374c22a17e8484862d13473489e520e159046f76bea9618563cb75608649457b05ede1b8458cd818969ee0cb1ca388dd3eb2838ea832d700a5fd2ad009730e04b2d28fa9a3b8f13ae2f9bd023cd4faa1ba8d3bd0a28db2076b8d0e9ed9ddd79a1f2517917c470a7678bdc63e5ac9ed11ae74955ee36ef002a423338b69d2d04cf5e9ba6a4490eb38210cf058901ffcd6275cccc815a6914c362ad90b797128c43ba854b21dd520dbc7cb0a5f2cf96ac8bb98e8ff5930ac39acbdd398f29cfe873f6bf396dabb0a638791aa13ab80079935341fd03757f42d395df7399c8c6442cd003ff139bc6a69dbd4c47e77d32cfdbaa772ef25881025718907dfbd874169ff110141868aab505ce4727db30e8da258030d5f27abe7d45d0d10abf151149e2f1121f44ec984db3dd7171de2017122f0eafebc95ef37ab58cfda03e215e038bf4d004fc5b8156bc2092966b237f1d61307833a7013d9f546260b80549e786f485d74188aac80836b09e038a30f4a09fbc2928da8dff24c44754a5ebe225cc930221d9516543b930f645f9846c7c02a103d5431fe031e18a669ac52d58feb56ac0440c4b51152b17248455f5e247f857de8ffa2d921733f030889d538f4536801f4f45b40eb63b1ba2f46f5498f9e1eb3861d9fe69fcd90145f41030a217195417ad9136ecf67a29d69fe45103322244cfb4e53386c566a482f750d50efdab3ac3160905c9a7021d1f3bb6d3b1529186a15dd75ae19c60a967c8fd9cd50a7b76ac0abf7777d754f2766b41b1a16e466e0065f94becc730e6f8c26f8628fb0d8a25f4715da1b29151dc7364c18d59cbf6f3b6a46b81131a0c006fa97cd8e118ab58ee04a16cd1ad7ebdbece865eae347b259f4c739af7bf1651752aea438c4f532eac5c7009c9b0e27b7b3f32d7caf445d156577446d4cdd90559a170f14119260af106681929f04f13fb882820a8c55a892b3af68aa6462ebaece5b71916e2a5ae18512783fb6a081cbc4a77477afbdcbe06c53ed53f25a151f40e06c68af8bd440493b56e555760d3170b5735786eb655c886a72aeb30595ab237760992e898078df48bb2fa253ff2de8c8588cb02c27c4038dac6b3028c437e45bd2adc2566e28c8260f8d5bfed16aa4e34b1cf63d62c632585a6c5817091794a7b0aab9ce2ab5158847b36cd71c89773421a9d67efe9101bf3cf4ad44bf4b5bd057998ade1f59bdcc81795f3eac169ae23b60c0649c37e1598577ee6af72e6d7ffd6b212c29cc25464f4aa73fec6aa1510747262843b52bcb6331aca9c35439d6997165ae6af13fb6b6a81c14cbccfc4db0077e3f9365ab5bf70d8e3c56022d8f3c81b25eefa81049017dfa933c0bcc39511b67e716b7f0fc6a812458ffa60224c0e54c258ea8f88457b62e14282b3153cd1cf388a7416a1496d0e1290abeb21bac42c7a9e6862d5b03e0f5dc5cacf8c2340e08641088a725da64b5e5c1f175cfac24694e8fb2cbb7029f7d6e69496e2fbdc20ce74a6aa78c3561c378f97fa3b405fccecb5da286281c5f45f5b5767507cb6e39d04535fc5916c9d0b58843bcfdbf43a0c5bdac3aee06e92b66a1cc9fa81fa6446c04930e68e5a839f08dd3d6c9b19080567fa2b18406c058c824ab5730784fd267dc0f77a205a6c499c138e1c4692e0ba9203bf3245e82092fceb81d1d256af900535879fa50c83160bed8900fccce560e4a4a93d117247403c405303ce5bc119ed3c533b15a3089f4c273ca6bfb87513d6a158d493115d7adf8866416bfc0618dee93ff283dfc16db386d381fb42127be0575422eb6fc8bcf2655fedad96d5a5e3ea8d7e58126d3a5ba3ff032c5e29fe7e1d76673e07780544b97ade0e18bbb023c88c5fea11e97cd33cedc475d89ba678c61ec7d32fd3e58ab1f231481027639688688848e4cb7b15240b05e0e3bdda93b8e752f5d18df440d09cef84f81e5d595ee3d48ce05ceb6b7b95d84da29125372624a6319e84cbe57d655cf8905c9774b60175162e9a68db790df257e71bc8c2fb4ad855ecce84110616e0f22889482e0bf8de7d2e4115cc7be4c4ca99bd7b8a638e5265ac02f7f3e47c01d2a0b2e7c7d1c87e302c136a09fe64795b527124d31b848cfa71f5548da638757a98973ffef2623b8f0758fb7ebd594ed9231e9b01c992ed98e52c3b5e485418ef82fbfcc880ba5eab4d3139d81fcde0eedce82b673e83a8221a6b752dd0a513fb0e6cd8066a67196be7d7f40c00a03fc71ec42e964d5ef77e4cdd1debe93ce46cfc7c4d251229a6fe4cbb53fe0664abd4446ab2bd4cd33251d4135020a66d522f9eeeada961fb63670761e5f0508318ff96ed0ba1e98ccf354de95f3cb10a9021e48637ac2cad1089274a7bd21fa2dbe790b21b558fdb17b61df4d1188e09e327164bfc9d8d5db2c9d4d2efa6cceb4d994582c8aba5488d66fd3523d09fe40e4ac1de4a12cd936b43bf46984b6b52b32604b670307982ef0addc94ac1b5930a7de2a9af13e6b09246d0023a49bcd37c52012dba814b04c945c837700498f7be48e227258e0007accb1919a90c486cfd3ac0dd0924f3cad159c763e59d282b61b09bf04e8305b8a82b952736aba0510f5eb61bbf1ab405a7df18d368705445910c0bc65342c8ccf3c101c3dc7cfee7345055e6cb176956ec114a92d4d71b0c84de90660e8109a06ad376a4c5b9502064ae97678929f0f5956d0e24ec5d6e6e00184585552e9e5c748bccf5ec0eeea19f8d7c32f279c4e7d19f478ba74260230575a710afe867aae3c90d28c4dc47781afd646499a28dddc5c50408f14ced91799a2a6088a657f56b9ac15a2333eb441575b9d36c0268e5601513bb0df1853404ae3abd3721f6f89d3e5e4c606e19aaebdec9baed58a2f3f801ad8c5b80480e47449e6d52e6aed02744c293a014cddd175399d0a5e75ae0f2d3411462eca12b797659630c04e95768f9e9d3417502d21ce74f25a73fb3c2ed76b5b84a694fdcac365ea11f89ce3e4d8417f7d1c5e8b8de25ac887ffee36083e6cba4bd356a5c32e92ee15dd86e300858420842c96926083d7701370cf4e7d73457de25b777b316f8295f3b02de61b7d714887f85cba3eae2752fa07c9575dc4e35dee11eefcaa6ecffb7ce782870b9c576f6c35be3e8a576285881adcef1c74af0e7b2c476135845e89802da40846075f773426e99c810a376ee6df1cb94f318202ad08765e5714590f412fcec02ba656a805d304cca9a0e75dbb9e7efd7dfbdfcb6fdfb38350435fa7aa56dcbd93c7a83eeead8ecf8fd86055072cb6ae45320bae5eb43bb8b1a8886efeee7d5817b13737641c193c51d2ea63ebd7af2cf7d8817514a7eb790b6f7de524a29b7945206a4076907d2072cdee35a3ce743e77ae81314b92a3d86ee38e8145d3f47a5733cf4096ac089dc0e7d822d38fd28d80ec304fd3aa3091abcee3a011867e89ea381f7bbc5efaf4c5e0baf8506238edc735ee765bc2ab95f674cc1f69cfd1b0063a9c7b1e4e258be7e2c3f83b12463178ce58bfd1884c1e2d6c39780ede11516fe0e5f45f80a9f8ee5bfe86118be1ecb172cc459c9f07344ae92e15b20ea4a86bf42fc2a19be8f7d89b48722eb5588a5de5f8178024781587efb5de27e1c1105dbed875cc91a6b5ddae0cfbe7a8f7e2b91760fecdef7519086fdee2992cad3ed0d963266a761ad0cfb29d8eef7de13f4be8f0c0e2e743d6f0cf8c8e090a46bfdca607ea07fffd4d9a309d646560f8e7853a79cdae3624ef7abdb8936f8afc61c38cfff8a365acf1a7384dffa1f8297934904126ab0c04f31f84dbc82df63dc75afbdbfef7963d9d9ee5583045014c1bbd7fb6e86f75d775de2eab9134ae0c2d0e5ea72ce99036978cfd92f145bef12f95f5183f0e9bdf7d65fe6fafeae2586cf45d77722b8bbaeeb401a22386e318331f277600c8e86f7b3929d0663e4ef4e28c146abf53a6425d877d996fe4f74a1dfd31bfc1efcf45f1a7b6f9dabce9a6a9a3d0f863792b68793e1d94524781dcb4ad5bdffa67ad745f5ae7ba8debd4fe540f67fb4cbef75627eaf25ae9e8bacdd755d476927e6ae1bcb1b95caa7eed1a72a0d3975fd9d1054a6faf71d1759df12575f03bf0839032a5c0609638d4a8225b87e35e6d868fdea572dd69863835fb1e458651dd3336b55a5e7b17b0fa4a1bf1b3d9caffb278297f7bd1a95c4f8b5d8e98739e12664a0fbe9d4f177630d126ff0fbfb205412ff279628d49ec7fde057307f5f411a5fce9fbf6ff4795149fcddfb541feb7ddf9e32175f155d88af4ae2e7e29481e2f72e5185f0b9b7a20a382229c62a8969ef9e25e6d02163bd377ebf1f14bfe7c00678a393fc17a491bf1bbd09ce70b21a57b0410312f0fb2c1432e0bd373a61d990f318fb44b2925e774118de7b9e27ee2d42f719648027727d1cc26427925079f48f5f492d7ae19c0d9cb73ff280f1d7a83c12e0be35e6d8089ff5e1eb904db004fb7cccb181c37a9c91bf0e191f7535407dcffbaefb95986363f5fcf99883ffea75c858628e0dd6b7be35e6683deb75c83c2ece2e54d6c85a620eaf0b0f468cc856a8008a8533b2590dd03dfdf1f1041336191727182132c82608a35be96eb3c609a5ab054c5898ae24d5a4151010c4089264f79de0bf39a88c525518ad239d72c3bf979c89177af95f105e6ace5d2f1f126a4f0637164340b11773e95d544a5549fa742a00697859912897c37174aaf250559ea944a4aaf254d0e957a20ba0de549ea934a4aa53207c6ba1971549a744844b2f6b519d555b2795120035bcac489d7ed2fde251ce9a94e3386b5a6b31a5f59df0e86b1d7b315f5bfc5b33fffaafffb7fff38399648ddcd0e9e30c99f0b04fa521b37f32045b15103f38d16f9f4a361fbdfc98116551fa3c4da7f4756e9c7a09e865e962de6c39bb00a097b3a977a0a65ed25a11bda443ddfe2ce992559f6fba7dcebef601a6dbe73e4ad0edff8fa36e9ffc71a6db8f092575fb2f7c6cddbe084cbafd1a50ba7d0954004cb7af815a996e9f033574fb1e00f2440302650b22058950b74a4296e8469d726eb8124d8ad6bf930342324001e4698b5013a427aef4cb5f5bb88ba44f6f962160a0b203922b4666326e8a10679cb47e01af4a6ee15e956716e1a5ee940bf2583d38473fa7e63187f7f87374bf5f87582327a77ef73a6439c61c07ecc76f633ffe5a471bf647ebd16ff325fc8eadb963f5a8ebfffc80cf9d07506eb4b65a25365dee54d72f7e823fff7b8b6491eadbaf36c9f6e4d8587dfd6a450febb2d2bafe0ec5133f3107bf0e99751273f05803c8325998c61e1401c4e5c2a70cfc1a16a6ff3f870df6f5c72c4c7fd78d397c2c4cff37d631871d73bc65fa9f32a82459f765aceb37000f2eebc69c3d56a092fa3560615aff8a197f2dc41927bde45dfffed23275ed8122bc9c55746661faada88114ad736cd8af6f47eb64758e8deeed7b50786999f468c29daa1547c15e8e312bf9deb4009f37a15ae7a04f2f462c75cfab9e950e725494aa72552337453b5dc2f1935079ea63ad395f5918c67fc1b3f86973be85a881a823ce09c3790bd208df5ab001fa5bcf7f82325cff3d97399d0227e2c89dfb3205db9ff53ef604da5d6279635ae9c43205dbed7b9b8523b6c4509c9504c5fa24ce1a25e2c8238edced58a52a499f8584d727b5a74ed55aedb1b55687ea122a45f5c61e3da93d768ab33d357559aa22e5095d42876a5254f3cd8ddac31d91507bb89b8bdac34d59187efc64e501ffa9a90b082c480e099fb23d15686aac53629db2d91e3ae66d19d828a645f42f386271443fe768a9d3b7602cedad63fa2bc6d23ad17f8da585a2af622c6d54c76fa76c0f05aab04ac138e51aa73a7ece667b2e70441f672c5fe15872fabad91e9d0a9b02a1d3eed6319d62801ecb3aa6533086fd146c97eaf4331e4db8360c2aaf920204e8e507490b5a901f90883a7a12448734605a6996602201279c304ac24c0d619a7039a8e1b1b3a8112ab23a62105334e6091a8854c920b3529a988a6080c71494162812a09122c3210d934f091f384d0d77c73c0a99f03443564c6855f2106e523a29a594ce3927a594d24929a5a212c783629ec71ac2e9dfa8b7aee64e3df79c576f130274a8872fdec27f7bbbc08485bf80090b3ffc0cc6f2153e0663c9c3bf609c0a9fc558eaf073c692876fc158be567047e1636ec2c2f05f47e1ab380abf82f02908df153eceec32e80b082f756fc0ace44e82d42be82b9658eae82c16088e390cf81e7c19df833afadea504fae63fc1b0e3e304a807abdf62e9a2af58a2eb5722ce7fa2b741182b0674ffbd8ceebf5147f7c4502c73f4f0b7f889a504baf75efd76f83e55470f45146a678d13a01e80ef89a58b0eba1e1471bea31a67fc6048e1e58d02f4fb1cfd9658d6a2cec5135c48755aa34f3a96aab53cdea854bdbe8e0de8ca0b6fb56a2c90c562ad2a0ec769f1b0b562b142d13e8ec8378bc56271fedc86ad15c8024116eb2b4883c562b13e90f581ae50051c16e71febc1ff4056abc5f9fb58ceaadfeaabab55ad2c91eb6088c3398b87227f1cb1c55babd56ab55a7d2b90c5fac015ceafa042d6e2cfc356c80a71582bd68ac55aad56ac0f64ad3e10e77178785b5c74bd8be384add6ea592bd66a05d25e592bd68ab7be157231fc9087630b27e45d0540101e6ee5f0e77b61f53acff3bcef30ee76015eecdb75b1de9195c4f85f58189e780482cbef4895675531a8c2f7afc0fd3b6fb1c3c97bef4d690bec3e77bbfbd5efc711edbbc4d65323a395488d565f52a2911ab55adfa2e0ad1d27f8e1c7bf12a1a811893fd7ae133fbf9769ac56abd103f8f78a252ea092f871564f81985382c5e1c49c1238155cde4af41121870e59ebbb71f5f559e2ea2bd8802efcfc7677616b77df8d4e5aef635b22f854125323dcd1e886b851e5f12c08c3f3b13d9ccccebacef75db2a8f1d241e49ae249df0a360dd5bb57e1288b65bea110eb9d8ff5c41b5ff7722f69d7e306629dc5dfa776e057100638a2503b7f3c925c5f895c7ffa2d71569857bbca71de57c3c3093f667b5a1f3e088260ede1c08d58cfcf5f3ff0fc2577bdc82e5cffc71f7b3e7af569110c2fb9be376bac7904c30f5dbf820a59abf52d9cd0c7e28853e63a81f3c40bc2e81e479cb2187e9738657b855f87f4becc0faec0f7c0d56a7fce1b7c13bad7dd7bbf4513baf7c69c3dc6a07dbef72f29f8633e61fb31ffc95a8d63d979fa393eeaf8698742d58fb1fefa7da1e87a17ce07c2f0f658ae10da68b5eedd39bcfb8e658d0e7ea13865dbfb4ebcddedba0efcd52d2714b81a3db0acd1bb07c50dc47a59a39be07d37de8875ed7d19d39af3d7eb7f6ae3df6417ce7afe78f5187c4cfebf9e8bf3668d61e8af25bade8503c35ba177bf5be29863a395630fd124062ab21cad77d57ea04271cca160cc318d58d224cb11fe0a2a64ae6f0515329c7156039420f48d63769f3f14a7ac4523d6f7e3a07d03b1bec7b246bf11eb9a627af32a00d0789be36e70daf33c4ef49e8a3826f83ef57a0fd28f9b1facde7e969b9ce33c8ef3eed5747a4fbdfbe07bf6f3e8eac1fd6d4e7bf62fa7bb7af7e7794b1b844aeef9c1eef3f5475be2fd509c5d53d7534a690e1c972f96d8e8d03793622fc8d893af59717e56ef3d7c6feb43fc5d71863862a9bb37967cbf4b7fdc58ce1bd62b2fa97b2fba17f3be7b7cbfe7e9f7bef7bc7d7fef172d0ac6f2e57a41862f5ef4f245dfaffb0467643c86ef7df7adc4dbf5473b7745d673d07371163a2efab6208cd54f105c79a0e77ddea8bb91ae686b4c93319d459f4a4d983aebfed557fce64efdf91be053e985aa5e9f4ed1672d53b01d07adeaf439fc93e299bf3e383f98f3b6be39959a4cd139d081f7a9d484aad3afcfa25713e67847149ab4a1f5fa3866b55e3c1cf4e7cfece1c84d9058bb7a2b70f53eb5d4fb27ebb553e8d1b0eaf03b11c7e49e3f276e60deba09e173efb5debbeb5e8ba5a7350883b6996ae1a86f5adf3fcb6aebfb675997f45dd5770f27f3a9df7b4a31dc98aafa56228aea3d55b64bfaa6efdaa56f9085052cdea77a6e662d34d997cefed96227bfb11d79e29cc6d0be634a336ee7df0665f00fff25ce1edb4f55493a1505df3ffb544222d56b2f6bd298de4365fbeb6d7692d32fba26bbfeaeb97efd5ce5c9e1b1e0f57f9567c5eb91e3388eac3c15fc579244e27aefd72359c91d95032f51b87dc5e32f69148b1c0b7ebfe8ae587791ddf5ddf5ea2ede5d3fab6bbfeb2798c1dc69b1f3e2ec95a9f2e871ebf7de637d2c100685e2200cfa54c9913ad513dc8ed82a130744bf4f509ee77dce5f6fc7c58e8be7c0c5cececef53ccff3bc5283d7b9b7a53fa703aa3c5bffed53c94852df397d7fa5559bed01ba47468ee8fb2b92cab3a4f600eda4a15adf5f8b6acf8c22692339eafb6995edb1b2fd1da7932a4f9941dfbf4b3aa6efdf2575eafbf753a7ca93392b5d6a53e5a1336efe155f4f98b1e263ff5af11e99f334868a264ebf7b154d54fc2aa92bb9ffa58453b0475ec9fd52c27d2c8da2529527e7f7f7bdfa8df3fbe99bdaf3b2fd15a8f64cd9fe0ee07c4ebf1e8c3036f818544518ce2575ee87bec1dfcf79eff5fdbeeffb3e17bff3b01d173b1cb878dd39e860031777e7eedc1d17238edc37780ec6146cdf60e4602229d6a44aeecfe004149451972a2900f1870302e0441c4e682f13c0cfb9e344009c38e74e37bb00c429a3417069ea9c88834eb07b9f1a55c9fd9d48a72ab9ff8a94aa92fb0520d22a0b93aae47edde25ebf06dfe2035b4a85cd2a4b5d9a9e661819a3e5c74dc74135ecbdf71e01aaaac2e64cd3b8ed85e48948a3d808628e5ecc262b629a8430e2490d125b9a96e82d63dcb440857e70b4468c0f5b76bc2942864b3044638c318611c6696f8c31aee11ac585b683140e9025ccb4a182540226333c09cda5a61d435a781282169e1cb1c46da162fa51017e8db748716649128efef08c80f01777711e46484bce3967194d20b1b0c919e1c3105e02b262cf2c9989ba41353614904c52445104c3945a18126c6123c54792eca4c5461bf2b37715ff7122f223c622a1a4a6889604638c4560a2cac2a6662ac31454b1bcd853e5ab6f4eefedf5bdf7de54153683de68205e7b0dfd920a9b307a1455d8c42f3a80c18c97216b82fcf0862b02cd952a6c7ad08047fcd58e31c6980d5285cd24cd54619bcdadc2662f5f7d6f9b44c76ea03a56724366a846618bdd4a54be78f66b4a6d7d2a4131ea59072232241d01e5079b28b24cc4d0678548a8eb7e7ace3f1dd59310206183b204872170121349bace084f49d7357156a028c95e2c1017db8f33453fb85084171e444e9ae0c20c09c55a53678e9c5ab60c35b1f0029a293780d122a4082b448fd7f9b7b01db02c0124042c1c197db15243579a320282f1c61b6f404a96a9265fcadc3044966f3d278de979cb1a37b60c88882da185c683054e001f784f016248c460d02425ebebfa1f8beea1603b9d41ff56100667394bbf0ea1449484525a6b138ee3b87b95889e281155e192a8a8717befada00c97e9585b8307dd6bede5eefd399f35c4c7decbcdce71b509d1adf2bc5ed46216887cf35455aad631757e6daaf42708b3e7365558fd273d864bbda8febee91503f58a6b16d85aaf5f826055743ade5b016ab38e8bcc0f864c04d0e9011db2f303fcf53d9a450b546175b4b31d1c2fb1e1218c048ffaf38886e008b0837337ee51d795a42020400fbc524e8f3abd477d82f6a8fb540a78ac1e7cc5acf7ef28458e8dfddd7775bf0e598e8d6e5c41856c8f36d0d721abd3963509927ebf7295b4138a0317a815b0b5d63c4bdd80fad4a2c999e3083afa7c159ecc9f321b9e66dda336e905a2c9d94ad541a83de14d498954a7559c2701f592ec7409512f0bc0535a5a7f5379b8a57a81115e914cda9a5fd62adb53a3aa206082a24aceae01db539fd67f52ebacdbb47853a703291cd75aabfe4cbf9e73075238ee400ac77d762085e77941155ed623fb765679802ccdda2acf50b5327bb8230bb3ffcd0f3657a60a2a8d93999b182612247de99643d2923fe73c9e4061641ecac3bd051bd03180fbea7197c7579f9f7bb1e05ef5a80329fc4af99278451aeb5125ed13f129abb17a50ac5516665f8b95aa1ecd1dfbdc91eda951266c469511034502a72f372d4c56bafdba03529dba48bafd8ba4f22c29aa3cfaedd47354758a7b43ab806aaddb2278598fbafdb23275bb7afb15c9f6806fbf2679d5c7eca113469b1fd0bd69a5adb0345e56daacb3fa19a8d7c7b4ca8d2b11fcee91a7c63b41a7b4b73eeb539c1dcf7aedb37b49955cba20899735c9ce2802f8d3b74995673e7dbb547be854f89607a5f727edc93f29a5ab67b99e7e65b23d384fed5285d10fc7d276e9948b76463da05f67b6c7f2a030fa94359616a9531dc0b1b423e8f4bfb1ac47de585a1aaad11e4bdba4d3d7635989f2585a17ee585a229d52981d4bfba36b6a932e708197352a69667beaacc2e6539ab6d11b5ec3c29832514f509d52a7da25aa97af790beb25e27d80d73e955c58c223c06dda30588ec2602962e3cdecc5638c294c528766c22aac4bb23d9d9385d5cad3455532cce4e99a98647da72427db73bdcc229c3acb3e53bf37caa3ae8eaa3c17a9925c8766f6dcae4daf7f75b063c5d7ea7091e845ba48310cfa8ee250b837bb6bf2d3bf368f52ca3da5f869e74d166bef09d0ecbe5c4da1dba7ba82a0dba7baeba4e4916eafcb52eec518634c01fc02c7e92bb4f941ee93766db6471bc0fedd31790630c98adf1ee0ed48bba48ecb24eb9797c8f67448d7c7e4e99884e023b6e722591853e5c1b48ecbe4c142934442b23d5d9909ab5fd1f4faa5c6b4ee664b161dbb9ba5d4720e743a8377fa611a0567e84e31adf2cc49d2af94cee03ac542668fc6471e74d22207bce2c60aa306d01e45dc2d3873e0944e8a7441c734fb7a611ae6fe3b6b478759b3b2bbd1b983adcd9875bb81526aef9c734eeefae8b3e7fa9830adb3b596e7fa98640b2ad65a7b6d13afeefb544a65def1d2acb5d33e9d77367766dd4f5a4cb15c8b29f339eef5e25c6b7aefbd39449cbee5b87b4147eaf35a6a9f5210dcee51e79ee7708ffa7c3b764194524ae9cc52269cda25b87b83eec5e106d191e2c08d73ee70d65a8b4410479f8917172970ef0bb72e5dcb71341a85a3511be58e74ee5c2d347ac1d4f5ce2817254aa7630e0c9cd6a3f90106f3ce2aebe85aeee7732f8f054ae957c9178dda39db19959d510c0cfaca69eed4e750387d8fab74764673a7febd53cf1e1693003ef0f2125da2ca53d419f1e02c70faf55a5a579d5167d419b18e381a28a51c476905b99fd3ceba7272dfa7d212a8ec8e3954905d9bad75062356f853c70b45db3dfa8522d7bd19869fc552855acf8f2352b085136e9f8584570fbed41d7cc9777ebaf85273f025df00003a1ab40880477a30cfdabcffc46ffcecf713d4956785052f16ebe8d539ef7cf2f05b5f6f61f81edcc2efe016fece48c74c5818be8b193e07e16f609bb070860fb3cdf049db0c3f00b6197e0b6b9be16b606d337c9db18c3df5f00130962f80262cfcf0c75b1308638ccd1c55d8444ada32cc30ededa6ffbcb14a5c68d52a2e3522ce54c081921f6bb0d4209444c8fdf191a4262134c49281a9c2e60d860c4efa628cf19526523ac6242822a281e887500c97479accc350125bc570098292aa008c143055c02c1911c3123ac2915350c617638cf197a28e31c6180b69ca39e759adc2e6909e315bc25f51685a1047a46e89aac2ddaa30422b261b9c202002274830c8e0449829ee0e162e103f8ef01777718e874ed0fd88f07383ee114494001d868c647c31c6185f0952d431c6b8468450abb6d912c51426a4ce39e79ca59a2a6c3e8d910a418a0a9339e7a01ba4c78c31c618e79c73ce38e79c73eef145a79905d19bc50489854d8df10f3a15b350b3d90c864aa7fac4d204149d7a9d2d5dd02ea245e04b1927d87821a2c8963b6b43c5aafa89e1c9d7f91f9273d85396f8c95df6de7bebb0a4c266d113295542d06109c7b009a02859b2959e9862e801c492b958e39d8d1c55d8444a62bac530e2b4a17e68adb5d64fe81f410cdd278c31c6337e60da1b07c566888049cfc0c3b6cdf0d4e15a0e8ac2352c738b0faa302b7e9e7c9dff225214fc0411a180cc11192432499f73ce5989a7abc498ab84d45582ea4df0a42594c72c097131e49c739e42626153df5ad7e59c8942109b402459d8d44f95f6e4ebfc733ce89031c618e31c442dc8de7bef1fa30a9b4748494c23fcdc706c4b112edaba8031c63dbce918631ca4c98311a6dc5004cb9a26211edc146d767c3b18d99b08a19a85e93219e514817283f0b76d606f80600b24d10509342efc90d122c51331dc1f74890452f72e19d145087f7117e776c82e71e151e5028d31ab7950521536a942807ac35f510c310424e70064db5898a45ea7882692448fa1a322316c8132858a8537536e8e3cf93aff78e8b66507d14107310523882a5cabfdd423b800c1d084eb4a11aa9f5be3b151b83001a3e66f8c236e9a8885e55625087f7117e7b8b6267673ce99095285cd11982469fe83338773ce39671c948386c421319d71de37e79cab3855d8849a51254ad32841232033858d922e5038e91ba08acc608364f16c5a6ea0629b5a6ca0b2b5127e6062046ea072908205ee0441fba7822b4c8c02e0048a3053c84045197263c38aca9029c594a316923c51e5049baa2a2db0aacc30e408204a724083b444941b17a33654805ca142f4e4ebfc43d14247e5e88706258810e2862778f821e4e14b0b686c8582a20718920cc088cb0e188ccc36341d3dec1003458a8793241c38364b2cf8e0b4b4b1c59069b53b173084b613c524c60c1a1794da50910189aca9716b3e3559781009f1cf39e79cf30fb5fbc3102f00192b4c84b450b325e1d42c01c25fdcc57976b2e50711007102c91fcd66b32217499f4c225842c25d3cd810fef01777f1c999ac24ad8640189262176b9c31c618cd5085cd25454fa4541901cd527ea1a3e6880f486badb5ae6bcef4b003153261764863045093a1275fe72f00a64d15102e360c8d996a0aca4d408498e1b2c80014d5731eca72039adb65c684885110246a1033dac7f295164954dd99a58814d31291294236454154a0e8f1f444173436cc04d5100ba48c3e9673ce67de54d804aadd109c18fa9c99e09c73ce79f6f8dda5cac2a6be351c369b8c2fc618e3304015366b3b8430432f3a9566684c063a7e3461c30e4c9cf010c4893646865e370c646f2821fe96895b626b736a01b4aad9027cd09684d6f4087a414b144e41578b1448423c27292a6084642172c2e10c950f239cce38ef9b73ce4574a9b0d92442114ff795998060a2c65fdcc5276f13829ca58d0d4143c812d8701027e37664538253e616ab5e9c5e27c893aff36f95a859c01cb12df1a58acbf2e58d92bdf7de49552a6c2e75697a1a232523890a27bd19aae24d6e865ac641684f208e88f1310b3215e408291fbea22db60e98e4e0a44689251ace345134c3292a02c3de144869869f5d04d5020b6fc10070ac3405f5c8926ba042f23d4006344194a18a326485891d43b05e4a52bbe5fd0548164dad0b2ae6521b2a8aaff090a646c4accefa54f2416ad67f9218e129a98153f2319cc354ec669ca166b359909c73ce39e779a5cdade73cd57daabe0ad822359b8208881d4666a2e813ec20822543f9044a0c19c118e399a587a68ef10d5bf9e9f85d4344bc0e213339dc9c6ae801081da29ef012249673ce4f482c6cea5cc3f20485c3f2c56631c6184fa9aab0399b8144bb5553b3d94c48ce39e789c546eb3957753a332245a7543405d01014b19c733e32abb049b3dd194790fc90edc9d7f9671a53c3772404b69a962344b896731055cccec045c994a9bff8de7befbdf7de4b63aab07973828a9aca31685539bf48624449d011962233475c19212e999f179e08f122468b11646e43b7f0ae2102a766fbd9b62f40f636c10c31f4e8888c7ef01777716e6b5451b3d90c08c618cf2b4a48758ca36e103913c38f1e40587e8c298374af94e981c91015f7e05e679cc3a82a514b515d7854d3eb8170427e899fb9232937d93b96a03df93aff4cba9665c76dd78480b1a9b0b52d53f860c2057194df8ba10489854dade586b11891fac15fdcc5798daa9b518608071182d6272e2cf830a3b110a41f1968b6d96c3664d3fa54425a428b715c882c6c6a261873790b91ceb2c588080d374db0e9a18d9317922c9145cb9955962e4e465982a0dd294c827c0c3f89dd344916367518aafbf3e4ebfc3f55234fbecedb40e2855bd5134f88e008a704161990e0b6a8c162a496f1c57a573253163675d0ed02db0217234554ae9e73ced98c940a9b556e0866965e59e8079029ea0844b3208e83c4405d3a279636433d67a8be4208277e4c69117204164e4c11c7c388097f7117e7380d96aaa2d6951e7c0c71c3a960c89b4b92854d7d827cc3a089dbd663ec5299237a78a1618476a6523c55d81c23452582146f44980228c79098603d05b9f75edd612eff30d1749773ce415ab014bdc966d6d4780461b5112f5d6078692287a061472541ebbaf9e64a1311da88ae8b8ab23f765fc880126a7c3842ca06c5170e4a088721199058a1b929b2238935666c2e8c6c94583f5b9e0881f76302af078dcd941a5a8fbd340589fbf331175e550aaac266d416416aea55839cb03380d0b6c632cb526f7bef9b86a8d48b5989635a2a64230000000802d3160020300c08060442b1348fc338ed3d14800b659050664a2e248742410e03298661180641108461106308410618a6a454492000145cc4f1f5c5acd8677a00f47d7aa2bfe9581490966cb80a536ef0f5dc93f51788c7ffb28c22f55f7c6d68b8a7b681db8c8f8ba539e401d52bd96b7ff35198cdeafc4f13eb65a523ae8d31048f5a5112e2a3c06e53b28bfd76246aa452445322aaae0cd70acfe5d5218305725b227e7754979bfdf45162e3d76c410dcb302b1bdcb1ffd120847f7ee7502c4cb50ec073d93cc81df03e123562372519533e4f45fa99159edacfa47df528bc3515096b1ae98f7f8b8a2abbb077549e626674860ac0d1f5c2ac7fe351f4b86adbaa969c4a8e730ee0b5603920a92b2d3ba2fbd5218e79c3fef8c010529700452ef4094674dd75fea9e459f1e3036a85f71247cbd135693c11bf44da6a56edccc465d64a5b998a1f3534e1578fdac71b75f9f1d77b0024e0fef17ed73e205dd6d308faab382ae44a379a806267f40f61d133ac3185c614ecdea3af9ebe38e8205dbb4aecea2032f7946c8cf92f32d402a7ede3cc80fa394b0fec1d608feb8e85c5121beebcfd60800bc01e514a15c33412fef3410a002d0960cc90e5410ae0f880918487b67d42321beb3e1af8b4232612e89d1e56e25b306c5882875f11a65c12604b6360c0190d907605adbd1b9113003a30714d9b134c710a2f2f6eef2eed055f4ed311794cb762f0b48495dc744a08b2d833a5ec2e576b9c1857cb0db30fab18056d3d8f665257ae14c971b96394b93c01b04dd102e2a4a533be836ca441ee06d584aadabfca6db57beb12b07bab0b472eb7265033b2adf6c75237067c695e916ec2a8cf25c3e525f67005479386510b1fae51e8e23c45597b0fefbb49ee33636538dc884ffa7e2a46db441be4e0e002d75db7f5f85c990c47a477a768909d8ed7c1d7aa15080538127b76cb9dc85aa317a0a1c18ec21fd0324106b82b6ec9d170121f654e5f3d5f5998e89318297238f09ad95a2fa176971389d21962c46a85eb6b583bfed7b168747ae9f84da607e5c6204569e2471b7dad3494f2863b9c969e6bcfd6f99132a3c26b6b98b27b428ec70cb5d89d0ec59db2939011cfd8442de96904b2da2855851811f8f2162c135619675646eabeddec779158b2b8cd10d10841a13d9f1c15a3f01a0fed93759b4cc79bf863850ff213ab10442909f91ce48db6e60c579ac04fa915cca4e8ceffda425386053a95cf06e6a8dd71ea15207fb8ec1e9ab06b65e173e45a2ea4054c396f0fe95d7f0ac3684bfd7fc0166061d151700dbb890f26cc31a49f04117ca6b0182bcb949b35a944086765b2f0623d77072f10b46de2ccb0796c93ed6773ba2cdf05655777822539f309488576d3d80983c9a1db0923cf0c041e954d61850f5749babf566bc2a42bca5a915ffbe4f17a505588c984311fbd30bde360bda14b94a0f6b5715ec602a2bf64c1ea77952f1c8c44cdd7020e3febc425fced38feff9445bd84d347958b2b0be63156d5bec0c5af6b6c15f12e043b2c10e055946ed5d574a38a4f5fffe421fe79bd3b312ffa5278ef36dbe830a377e90e999fba1ec9350d7c59682f0d4d4017d8fa394cd8cac28d0b40a0006c6ba49e7878c0de92ab4fb9f0e9b716eb7b4aaed8959f2979d421c9975f6bac8e2b7ed29622aa99c5365864ec4e1dce6a3f052dc4690713531b0b9d3ff8a001ae846c6c48d4970016bbb079aed682b6177d24ab913243ff207d3910733c12da76ba4a7fb3aa79052296688f5844a31e15d3c0383cc3506d35196c8d16ba4b09a69646656d800e8e22476274d0c0dd8b4e769110d5f0aed94eb0398f44749bd0906bd3caf1e44e246e11753b7e3ff721fc2e05d4c223aac11b68aa7158af220af9f6d05c43d2500b4b426f1cde69951f1e91acee0b21857932413773f1dc5c8dc32cad148a8336d23aea8b6421e07840c587e52ac3041baf6dc98dd9e5247b5e819dbfd2921621b69722559efb945744a3529ccc8609aa46e560d116ec7055f9c23549e5e006bce60eccb620328516048716e6f54cab190162a22bc33a1d6d72be471e93163b7d3f7eaf6a604f9665697bd2fcf13303ffba588c6c5980e03498c180e9f29a63cedc90a6b1702bfbbfb5af3da10dc2f3316673f1237f1fab898ea92f1b7d711f0b74652df2564379105afdccbccf267771358f42d22f818676808e65b05973a2a15d27fd091a9a76841bd0fb67adcb342be0f2322bd7296d651c368c30b315fd8177c2f11391d51a4b900368345a102a94a8dcd58e550f6cee1a4177b793d230ef04058ee5babc6223a6931a57e1abbf22ab84b8e427e4352a952ae3c3bb325b97fba0f088737084d5742eaa9e5a36efd9c545a125526959d34b18119ab91bd59b72058e265261a3e51b2badcea2177efd978d3cc23549dfbc03471a3fe7b0f5f1958ac933e5d652011b6392cd41249d2f63ea75d9fe31c8aed70a1755b4a46f343b758bbde235a03ac4ffe58e361b789676d29fd0d84c9c23a5f49a5c0df8077edb4beb88610c0a4c4e9cc90cabcd0e1a14d2221ef71df60e668f3874b8ba669ee21d124711bbbbeab192a7bac54fb889ccde28419f2b394963f25a97e309cd422798c9b33418ed2813067c5e119cdd339a0fabb59ae94641887e689c43a3f16de572b9c6c7f7031ba1b4bc109c90ce980ef5f864c734a715e54f9c9036c446d016d0e1a16f610a974a4947ccb456044fd79bcf3f7d83acdbb73caa50bf600fe299f8dbdb3ecf58139ed8682807cb271326b04788f64d8c286428dd3950a7241047fa1c048048d7981a764fcce56d4cb8cfaa08eab1bbace4ebd9776d4f42a08fb1ad484c4726cf0deb6f7427caee5d7c004f9940e5ad384573bbc8510e8694f052a159347482c72a282c9e3cbce1705cb91850c6fcbfca7c0711ec57bcfa5af6719759f8ed1e8df861fca0c6b05224c491dd86fd49d28772dc6f4fa7fbccc1a0a6ecd7d60518bd8d6732c43399298cb01395bacd951a8cd806456c4e8086c4359328d1e0990cf512a28a3029624b43ebd1115eaca4f21a7b9842a9a80289f097dca19e9514aeea4617c7315cf223630072b4cb56343930b2145770c595080a20870b9c3c1bdf48492fde25ba8608015f459a987e14731466bda3f11cf750a1166494861633be59cff4878b42ab1f88f141b34307225288a1979058608a2f55b5b953b6470cf9bcdb5d32c3c6b8400d9fb5413df5d8c6cb36addb8aeca37cb5d719a5c9adfdead54f9b14b340511416487dedc755af3469f6591be5021f253038e204b940908d9e73f457dd6c127d76dde6d83559c367526d84b3d183fed52e2a877e7ea81180848ebf691b921c89f36812454c3c90a06c61744fc99ce31a81e18872d23bd22e1490f207f40e575a467aeaf1fec90a47ae159a2923575567c895181ba7eff1628dac31dde6d3e005c04f20b9be56203a0b8bb59de1e4c1c06d36c55fededd6d5da21b966d1a0c345e2b0dc38a6150e0b92a2538ae44333dc45456d8b22e3dc56f4871c20ef5db0c79478d4a8833978a42b5e1eb7749b960dd23a2bbc646ef0f178a70bd2ee4e3de8bc041b071c3d96b45df32a0b4109feb5f13023f70023ce1b861c7316d75d0f225bd0eb15af59f68f27b19a5b69bfe4989dd1e4e386bf09edc480bc284e62faa548d2207231c9bc3210a87f73846a641e6889c663a0493daaac742d3a68285d410da1519f440a8788a7e77e9a3dd35cf2cefe0be4aa2cbecdc7781187c18d3efc24563dbef10190470ab87961c6cf401040441ddee3cef718ef1054b133b48b4a3a3b16512051dc6a4b7bb1bfe78ed65d3f6b469305b2839f6772c8d621be5979342c53c0bc9507daf7470d98ad3a10f1d613a56e6b8de6a22296661abe95359619e4bbbf7791ac5217c34bb1a188e93a33749a83f35cc82bacc9908f46dfc008643249a923241e5cb4b274583e9649c10c60cef2b7aa79d0a06cb2cc95230ee28e31f33d64cb45a13dcf778210dc98188d09d490f0a60b6fb658aa6fe9184fd651941c069799f4e2fb7af2158e71565e3cedf5e8dab0224b4fe8102e1e31a184a0cd1d94c583f485816b265e4b421964ec835e79b63f802214341b0f9960a49ec14161f11ba27465c2e46a1596161c69783cd98cd2ea83703694a55b634d818a0a8422e3922c2588f765afdf637a3c97f243db741660a5a3a9929bb812966a78bea0fe324bb7f60b4c345813f61e09815dc93ca5f88292a9013bd0cda87917b2e69eb402cf73518eff8a93ba3a663233b28749e09bc8e63727e1140559786c5008aa5be33654190510f4e6990c8a08d8dc187fd741ab15987fbfa24c792632f01133fb29dfea5b866286c53420b1d2ccbab23c5457f730220f1e9fdb604e1793615d9b6518ab544f3d31dd939a4cf05514b1f29e49f79b7bb2c655bb9825bd9d98b1f3809cc87999980c44d0cfffa6d8082f7bacc4f734fdb2509160f1d366643a5556deb713c8d39eb899f5a69f75ce56ccba28e0cc2d1ab7120da15185ed84a93a28eebe3e99fccf60f65f3dd42459e31a95c4f384a0603aff91d3be37df9fa910b78e3b2049b7e23bcdfdd7f73e59ca88503d06d6c471ceefa2b4a6ef3550e34431523bf562837fefe8be66dfacacf1ae75d419d532f88858db7a1a357d564a0661a8fae818ebb8fbcb2e9278ed1321065d602eb82f50635fe83f421d3a841bad38c7f1b9d599b1ef65afa43b808ad07c0aa1043b327946994e9c4d7bc98ab41dc3e2b473e36724f94fdbd024d20a09a025dbdc2c54249e130ca4486673c632481e4f6b032420aaa7cc0a847c695d3502322688e2a422cc5a33d2aa1bda03a3c53cd206481b64102df4caf2932f17383197897e5c836d941a91385dd833c637b15d75445018fb035f3d00df9e59db60781f5240c68651359e315c5e1e65c65742c9e23543d4a2e8f50606257588cb9398caf7569d70ac9fd1b6c5fb939550d02d0cd700f3b1e507bd6dd459185d7d9e817d6c346ecadddbc0118265b6351b3e565b6b59ccc47d3501965c2a866572ecef491cd285c3159f6f11caa632a5230476cda523c29c5712b159e50be8076d0e631371320fe360dfad8225d5c0820dc6c72d5a81f58311527ad378a742c65805c33f0bb57a364a04ab29d84de9d7b0ee2daf1f5152131c8177eabc40a33a4789976c7876d52aef93edc575a2600b6f90290567345fb07fb5897fef3c865808afafa0eba3f6b9480a85747d97115998681f2e5153f7709ece09f3b5c355ac0a1882f07fb964d2531eaaa82eb427a6bd52804c6bdc1e475ff3c1b08067a0e2d0847a7138f736ca511499f501bb2dc1b2d230231a227eb8ca76d39a987ecad158bd9d010d5a51b1fd916a45e435118dd796be418648281c6762ca02521f58f32105c1a0feb77314eedb1e83865652d981374874420a042deba90932514ad2b706a5ffec4fb3a6fbf84159db945601d7f84c83dfcd8ffa854eb42185ee10e23fc9ff698ddaafbc365f0104f87ab53316c75960ca83582186c10569594ec52ba39e7d3428218812e31c6fbcb1e595ab268dc95a7a7c0067256abca9902aabe5532b825d490d1b8c08f7c821581aaecee613036c349b1fa54247648e13e9b3f9c0caa151e22bc411a13a8f4c92882ef5089324ac21b0d6a1e5ea381953a9ef174d3fb7ce662028e5a8f341a4d9af33e737658ffd8e86b03fd89ab29c6512cceedecb1e1703f6e2c51a49fc260288bafc976e19c161e4442495291df0e083e7cfe81c543dcac7d3af62f70b4e2b75443573f62a77cb1e87010d6b3cfdf4ddd9231819dffa9afab8a5e2d92f64ee9a743028f3317f6d29b74ca0d53571285d5494eb0b350979f20d20e4b46499c496a69b1b99d4607e92db7904c2ab05df4a4798fdb78a6b03ce26ced6290d4b625757ffeb1c0d339906d9d896989952c59119b8cbcc7b1a4a72dfe06ceacb09b94cf2d3a4973e181b18c49f94212a9b950c7e7992d94fc94ed67453369ead79a9200529d2c32fa1af88b4aeef4049f358b22dd17a2778f134dd8ecc8cd0a644684ab8517a0a89d60bfd2344f79d5b2f7b526a1caaeb0484cb5cb874b31d5768896864c9f6fa805085ba540a70c025e66e85ba4281c9bd5a340e956f3581bb0ac9e9649d23a6a17280f3369628e14376151853ca301a6445cd74e14dee6c65682cec5a1cdf362f527b35c768f486e5b4c9c9df3e3568db0d2a6d3707837a2ae78cc1c8647fe8c1b5e304db5251d7b2979c96ece6ca6a0d747d301675a4fb71712b98e0eb418307e597f9e3a28edca92b8a89f77a1011cee88999da98f8432c68c58aa4f1049856ebaa675dcda8e419c2315887a6c73342693ec9133ac9c0036271f35eab122715eb5bad3d17468771a21d74f9daed68a30d08e52f1e8800b5b85e569670813c093fb156d8ab074c5bd1fa45c118cc4a589500ba025d8973835f75748a5e2e17034aa50a36d4f781255262f5d8ecbcb7c1da674a9f6872cb23a601421a13af29d1708b61227d9228d9798013184111409b5b62bb1f60932c3bb15e7700dc3eb9dbf27248a8f8ed0a4d8149ff43b1d3ed7bf86497088d980587efdf6ffb561a3e66f3e06659a435bc5b455ca6ebbe05ff067c1988861d860caceb407a6562ab0da54a417e5ad55c6e0ae3f44d91bbc9877f623f211fe501e6c8f5572a777464474078c4cdcce73656170bc58598c87887aa7a05123c96937e3ca28ab39769dae38e3af4a5e36bfa0b2fac019160c95482758645af214163343f124206a5607b0c5a579823291992324fd4cec2effe45cd5486740b3e3650914989ce4ed27e586ca28fc0b600f53f6a1358fee1d97f6251e89549811a26ebdaae93fa16132aac6f1aa110314dd11475eadfe6605170be4431206b0ddc53008e1c11f87dc2a90698c8450dac600626802c1191314300fe29ab01c46213d085269733985fd7d40eac2788521fb5d627dd79652fcd884bb63b42d52047d212e30b773001f3065a5fde3c65572e90fa18ea654a870736c116e4185a194bc177c4e48da42c8e029874d4e39238b2c70e9fe148c6c1430305f7fc557b8f552037382e33c68121da15268ba01e0f86dfcc5a08e15952e27724fb949eabbca72e0a8500f4e6f3def9e378806e07e954c91eb9a0c6f91481d92c18c156bb95b313258a40fd138bc881afbc906815ced8de33609e367d5b6aa377f3f97a8737c0157a27bfc15c727445940bc7fe44358410bff3f9cc0e5cc95d6ece8952888f41199ef09096d9aea4671da7db0ec61fcb6e5b5488ab5b2c50a668b953f7009a9e8def04b79d2ca6066bc846ba43f11d83216f267a08f8ae1683d5dd596b0f756b37bb7bc98ae76e932aee5efdae819adeee36334a67517e4aac530dfed6a31596287f287872676c57a89894691c60a41afc099c83887bc90c8d72dcbb3b2c723e358156bcad4225ca954c715de43eaa84c8d302f33e4a9eec86318a2fa6bae0da6a1c59b8aa7623277681a8cd78b8c4d6e4b125701a3c98d5cdc83f0a1b41d8f47638b1d112bbe8317b19ba5316ba6376835014483d009d7dae6e0cf13b984a9d140fbc4a276a0a6a5afdb6535dddaec8f4dbaf8806d07cfbe135b43a110f23618cd115d3d9b8a8638cd752335ef086697c548094da524d40bc3aca36c9694f325377ed0d5626075544d7e34ddfa8e4f5099425470f99df2eff57590d9c00f1d9487e04c15827dc16b38fbd09701f1be5f5f1c73d9621d2df5ba47f0d6eeb60989cb3724286560ab80c92c865bce530785412f695f6df6b3771f7c7fabcd7f6d4d472949b1f50c6a74e7862432a49e9ad325cf99ade2f8a7683139135905551cf773bc1a7c7928e381027f38dbca8a5fb8dc3a9e576b4979e0eddbbe3cf867f97a1d2153a6a507114dec1db730eef84dfe66f824373a96da85bd728f8e261c3058bba8c43568fe18cfb0398489357910737b48a8a0aa9b9c4c6b70f3ffb81a368ee891f4862d500fa2f2122c08b53b4587d9a5854dcea0f8594ed58c385c46a8942e7631e138e4b9bfeaf7c718081d53cbf81b855b56229e2fb29b4ef08fb2a2b9bdaa1bb4b6f1105a3da5dfaac1708920a3722e668ad9be499d5e40685240950e6187799c99d76810f6c3369ff6e63262d67e030e8dd6e7f209d315371d8d5d21ba366028a7cc720cb2c9883aaacfb1b29f5e123c8ff496a578a4db060f9880f09d4ca7b8842bc73d30a85be39a4e7d11ec6afa46c438bc9b1b4118bcf2d1ad27ddbbe3196369bc10bd49901c427a82eecb766e6f840b71f04d1b88f7ba57ef4c2858d8f97248be5b918c357daac02e1f79aff2f4a06e783880fc4f2d29c08c5b286d3a62da19ff5269f3f049dec4514dc46dcd187191540737df54479cf4485a2f5c0fb7891091e9d0eea9843b710f866f511ac2789e815dd9cc7862ae35f3e2b4b26a5ea16082c99bae6117611e972f010127ba9162c9c06cde264e71155e47e697f404824d0c71ddd18c21c359bec9b16ddd7805bc8b05ac43ff4537a3de5136fca10d1226f7a6aa6ad4a17755f22fb672e7967082c8bd9c0380cbdc590d1753e849ad8bb8ee4d03524457afdea5a352f1dabd5d11fa0df0a57d9218bf9fb24784bec82f273ad8e1b498cfe6f70d94ccab3a7ceebe6ed60a4a19992dc38bf5add5b80a5ca20ccbed33ce140d03d2423a9cab858209350aacc00e434d654f3772867be118759556526b6be89b3a63c7a7a545385c0f0ae5d258fb3a5b2fbe32b2b7d4f40951b79bcaf4905122ce5ba3146969ff5ace88d6d60d3a0808333f225582e800edbc405716e7090c7deb05dac382ac4e82c66e4724f7a876c2167099c31b1e7587a1922bef0af7fd9443cb92742a071be9b678502584cb931e0e44de019cb1cb6bf07fbdbe53b235e00b0316c2fe249540731f684e9074aa07e15071b75133e779f1062f856b21d1dc2662616748323349f38bb6d4965d199ca7e2d71eec6caff402d5e43cead273e3912cb8650c748ce24d8c0409c47883f3da46026c4995b828cc8e0255f474a0b0e2894f6ed20afd2aa8607349fe1f7fc0ebf0184ba3f8988840afa2b202fb55dfc5797d7fdb005ecdfda5191ecc5c0b2489fa249a41bfe623b01991188eac13d6f320a836887fa091871d8f88bbeccdfa1dbc26e45edb88aad83a464405a5e976a7f11a9f4cef88e93cfa236879826a0e8186dcfdc67cd33d05ca56b45252288523bf35fca50ada65a9f20e57eaa0e8a28b900b48dbdb323cc312eac053043682e235e6805a1d50de9b771065a8cf77e3d21a0522136467e0260c05fe4591882f08fcb46f11c87649843725ff536dcf788436792cb3b9a446fd9c02aa2b5773b4d7474ab7307e8836e157a17446c7692fded6dadec52a300cd95e214735a7675adac5aaee5dfec255dcd178dc9b890f8f0876264a9d840a1d7da44502ff19133a9e781a1ab6c17cd2800922ac4f7d251e21c9d43d9ad1e0261b7f0dee137cb5f57fc65a274bf8241c89e856e9f129434fc48f1c711d8449942d785a515c38df209bcf41fc665be73ab583c32d7f4b31ede1e19d41acf9f10d5e91fe9f624d733fc3a19cb3d2d59619290e94e1feaaa9b7c807027a973a0b8d6cdafdcb3625a5ab1b40825f92fe8a53a275e88110f2693df378875598bbd15a72045c50dc061cd41730263d1903cf2645f55f18a230e69618c0d90872ecd3489845ee286ce4f4829ead84f3261bffc6bf1be9efcd0fef090400d0c8a36caea68df0d88d4b37ba88ef7047fc8a47ec2b10a791d4dc9997d3e0bed6c780edf953e3099cf9274545e078cb5d6499f868d1cf0b665f69f276f87b5bd8b8ab93c3783278e0915612d39513e025582411a6f0030c8c6283dd69cb1df1ba539b309be5eddc9b90098c1567de59c4a4f0990d1a38774850213eff7f4e048010a2f56ba0ed3929cbb48cd57c2563fd50091cc3c13114e66acbce96d0d03f37e3076515b981d1c606ce395f832baf1ff43d57af47b0e0dbb60db3c598b5ff6b908f6d6ed61efa9995404825a342ad1da40c78947456f5c1cb1401db0828b2929a5d33a89b50ffeed9be412206edf52780baac3ab2c6d3d1a38f0c80978af9c44d53f1b0c27d3c330d26485737fc560035d34ee26af95ed91f4117611e1afbab95bbafdc5bbc4a5ab79220f8fd09f46fbbaa6e030d950fc9bf90e3b35a765c0b1919a64e7f701591902a0528980b718cc78b4241a5e25a761bd014ad8490429fde16e1a895d2b12c505e0051a13bf2b732dd4c6e9c01870266d1ea083792919ed879957533a1a7216b89d2b2e3600ccb688a24a16c7f392495148134f3d5078a7499934f15208a9c19facdc72f28aaa7a38c5297ab150f640be12b0a9fd1aa90a48f3926271f0dc5b46d028ac133b4fd88ce951843e405dc5150a77a339896a6eb12bbd318a43cf6f2efc2c275dd19d9de1747b8da94b9810fa41a7c62cb26c374b72357c013665f21b50576458f5a06dcd73a23cb7769195b0e38c24371fa8e69321c0beeeb6e5a3b7588591766a3562ada6f24f837b429032ee9176ec1ec5583c271c47634ff35b4cefacbaf940b7e31a7c7d3b4faeda2401b46c25fa2e3eaf3ae99a537ba3166ac4a68e7469a710e1130447a552122bc227eb78641b1cd1254be7956c264cf179cb1f2e7884fcd6c5a1e90a41ef2cca5862a5ead2efa0c43e4247163b6694cf1fbac73cc55c9debda94847b2ee3461c6170cf353fc64b5a6a77d22888dd16237a2ad23a9c5aaa1c7bef733b2efddc58b096d5659ef33b41cd47cffd79ac5daf0ad6ac1d70a8ad7b5c93e0ba2653e8082825af58806bcbe43f7138b2b93e618252c0cf3c85fe76a53f1522bb8ac153a3459c1844a130caad5856c4ff9ee09e2ef604b35fd3f6dc873948dd40f93ca41edd1626a3ac3420ce835a429b051cf6857f22b070ed90a05e3912f408b64f790f3c00dd3b9d86ddc0a8c82d0647e9efd10243afc288582c1e79aa7fe8c0d8a2f3c482a0f3fe0dd117113610ef0f81cb0f39a7a1f9e43a1f504a5c2dd1a261bff4a6025a426fd6e6b62db933b5b6473d558c53da96760b88e0adbd56fa0e98da85ebb07176fd7e2c7fc518b431ca5b25ff42c198b994c7a30f02f568f6b27412a3bff85d010cfafb42d81596f1821001c1b1ae5dc0452bfc9e14136cff29c57f94fc29dbf2c38178272f4b81be1d2c586671e63f761e43982e064d94cb79ce38cdbaeb87db35fa5b68224588c09d54b0f17a7eb6ef30b323810d7ecf30929907b42179c0a1826d1f678c54fe9a68641f989997412255f016317eb9123aad5e903095cb23884a7f9bf593fa57202abd01c59282a9a5759cf5f0712fa917fcd5168b97be8787a25f481e9f12d67e233de7bee99600bd0e316108d2e8ebba72c15a2d9b1994689eab0a21ac86930d340de12b439462215b92d29f05f59ae2cdda3509a866565e66ea5da096e2d76f918eaed266684eb7761bf782f80c2a32640260814b9a5c6d5ec5fde49f173d1252e54c767750d118d4a778152d76904167e95f781e6ae033042136632a40faab62f4d3ef31783914e6d9575e5432ba5404a1395e70c361bfeb3fa3f93d218bf51b59925b3d6e7b1019136c2a34f152f734e3ff896f93c443e34061db9ebea0b4eb3b1eadd916b63df842ec427c1297d9a29cfc3c54fcea36f79b88abf8f4556fbdb0e58dad5d23100ca50bcc8877be225baeb020d486409470f2c51becfc2a5042b640f79d0dc2bc04a3bd1578b27a471f1221c87c08de0883e1f60a9873929444d5c6cca3481732cc8946a83501783f9eb96b8c45d84022941808db8b135fe5ed5abb6eebdee1d609c2aa91aee045c4d59b9141c0d01d13d28c5500b76d6e67f90ed2593701c0f85a2852474ff0d13c8efa2658e4998421214378b3cf9dca22fa858821f189de812cf34ac5bc595662a16c725126634940bc494b994ca558318be04f63ba9d3886fe5aae44add493de1f11a0c05e01138d97590eda5257418591013176b735ceb6160991458425fdd1a28b747a3092aaca17844d445b71c5af25f6ce4487f02bbc71f96db7af99f63c1f0fa944d009115ad12c0192db328f1b222289634ce6535352c848455a9723d50de8dda51776b29630d0d96c6750c90f2de9548e27af9c3d3108bfc052d7961e98e609f784fa0708ca908cb72253383e0e015387e45af5865b1c4faeb69c49ccb5fd54b9ba307b01f67580be33164d157b9463c95a9935bae271ee01c6dde6d9928d406abd9f1b3759410ed3fc6cb8e89dc6fbbfc4795287311f8e2f2ee6bd1126ede90e41ad4a3f0da04c3e957c24c28b693749aff1ab218ab66d46cb168e442becde62a5000faac101c768a4eed28c9e57b01bbc97ceb6f1b2fba46469d01c6cb889956b374f2d0ae129db4ebb275edec2cb803ed113d088c3070fd5ee4503c895557ea554e37a0e46ae5526d5aa54a07f59bac03194e89c88bb4267cbffe442b169936768c43ad974269a7400b7cf6761650498e106d50b95001ecba8d5a89fae53c28a7744fa979a433950e10d243e282e2d98dd02ef6121ee010aa0e2faeffe085d9b813fcf39f144d80d7db5908848e951e8f1f14abab096ed187d76434b52674221714c43e3f6a855de037f5fef75d16b4a1c4943e1de9a5c37d43ce0d3f6a008ec4643729307e0a79822c9df08ec2e71959662a737b782098001388d7662b211fb80ddf4033c08c3bdae9e95d33c39a2ebee067a33c9fc6de9c7738336b9c4cd4f196f4ca8f5c496e730c6acfbce980be9a92e7790e53eccfd785ab7cb0a7b93f9aced503a66d3e3747ea432c33b88d923c8bdd884e5e6fee0f009ad4b8953ddca736e760b93d64d3971fdb9fadcb65b603d07af2a671107c9b957b6583a3410f04dbdc40d4a3fada90b6fa7ae4601e0f0a8ce008816371042619cf9f9df9131ea9ef8a07c8d1987994f81b869901356e77e6eb503edcb21acbffa425514606ddc471d198746f47a403ed25481afa12cc854ec5dbec02e0832eaf4d0ecd73d024eb7df1c62204f52a75ef557b36012b0fc191cc8a77cb319ee90f88e31e5e8bbb57644a3c2926a155f30c54f2a9d6f4a3d3796d605527f99ca408c79d2f0db6714fae9e2aa96474b61d5521e50197d6dba13b507973de61bc973f3d32adcb468f21832e930e266a71b3bc7da11c40a490459793c9f1ef56a4b87ccca436e9c6907cf2942f494420fb40756df2b25f6e854eb3fc971e8f16a9c2020f79f1ecf4987bd3bf05ed79e5ac06ae9a30701d14c54b09f65d68fd6b84a7e6285e1c77899f02169f9618af2b05266b38eff8692f7ce892bf70612fa2eecddafd94491d9c96a6aa3bb2263dc68e9f0a5569ba145e3d14b4024296bd5843c32fd46ea1b2be331fa5872ef525fd7050e1608488d8c1dd5ef35b3a30d91ee4692da05c1b76d8dc780dc844095e695dbbb78bbc78343006748154800879597eb52452e7da2846855e6a6ecbf7b05ef7bcc2bdc3d8048851a3207f6d8bde9f120428af4a1f13abca1fe427b2cbe1be8c344bd74bde366b95042e2f7d0c0e863b053c0c9f67c831b4ec711a616900a0017576a363c687d520bfd934be8a337f90f806cbff14ad9022049ce6d244e769a3b067ae285cb9f6dee0ca2949fe5f360c3386a735d27aefb4e31101c73151b1f3c639eb725fbb2565cb63bc8b4380db3dc9f601cda9a496499eaf55f88d23c735d70b727aed17fd3cfa83c2c368df544bd88580a2ae3931822a6c56a9082b2d94a5d1089af18c7cd4840ac189aa265590f0110c328bf970853abc92568a80404a8bb1207af3c5b819cd6f3760e38d2320a1b3e6948715b8e22eedf23334f80225a3bdd679c46c664e88217e3615ccd992764343870c08a4ec26e8e6a24fc2e14d1e9a4121e0e6c31ca15edd180ca52be8f6ecc8f5c881ba12ec5ed0260a52c2e7af3b15cb8072149d62241ac7026149a4398ef62bfb063a4a905b1cc7653e443a3333bccc81d4f73595e615e71f516c680116c3c2642d98b76f710f9be08d49e1d1a44e79fc51d88cca4e25e572385b571c91e9917b18921a263d82137a42a888c615ac097230be41613997c181167da7249abbb3072bf8fa49c277f90238b61dbc94e3932a9d1cfb35f51587fe6648ac847d94a194d71f0e4107cb096647e6d120cab11f07df345b410c4eaf93c484e602a0e551d111c84669354f49d7af2978e979b42981065e4bb49d0a1f3115191417021e141b1f66396310a26884884ab87dc124d2b40be5fee5fed0dcc248bdb49bebb9b7165336fe73f0c16994b24a6ade844df7184bf3c7931a8895e567bd4931c18882acba27ae5f29c8c54d317becf0d9e083aa3e2c3028f91306052e33e08aade03e71864451a57237b15aa6c63a42b32d181008af1eadf60d2a788eacf5e1f8f5e519e9c1da2bed8c88b37d3a033087e47059f060b41b4f1703a0839cd0b9c7c535ccb66ec014ade130438da545e9559b3a0fd205c2bc5cd3b4f37888670b8328d026b137e081b7992a9cde4228ee5b2c7c262e3a9a3c48c11000749e508cca15f3bfca860ab5d6272a93eccef9d27d34e81f77a999d17e04d8a340d8262c8e52a69c14b51684dab046c2d10f22f186b4a7be4e287c7866398c01d08568b808128b196b4cfdf5a50a519e89d665aeaa01f116132631710a69b18b52db21a6afc84d6b259a19a2adcb01313f51da8e52a64222a12c6d10fc5a67a4fc3741cd17eaf06697096743e29067966b788de258203209a8eb033f5922189acf0def82aa49c085982049ddc137adb121c47253a6067255c84c978933ceca7b8a9619e5f5bf951b9a20dfc4003ad09ea2585f1e1a7fffc2c40461c0b3bf22efaacca329214ed3fdd3c1140c843ec5c27f26ff15f291e6c6dceab5d2daa3aae8b4d77999c120d039a0f5a7db9db20570a4c76bad4cb4dbfc4c254f37d0890bab72f88997ac5b709270e582546be6fa5344674b668e3ccc20e31a5cd810cd13e4ccb053f6ca0fc0327e1e87440fd3198502359e635040674a0fe1fd456791b3ba0c2515a70caa0152587ee9e0e033a4b15a87cc313e2ddae6bee1b03fb6fd3b0f09a1a1613d912238ab9edcc30add23af84a5839e597087c28796073830c432f958f0ac92f3e79d0b75d9d3808b8ec25d94375b30250173aee472d1f433fb8097ca9190310fe15d5f95e3f8656b8c0fdc4e18b16350589ea34556a446e8f46cb127540ded72e39a8904e60b7b1901a48c03fcf060d9dcbb365460b6bb22a7de751bd1c57b1c1c8c4d3c6dd2eaba87e274df7319a8275f9ca8cabf57d4d42a0cc1b6c46eef8e86f66f125b35eadf8ef5f009818948b37b5e3786c8f8c4d3b61f088ecb623c91d4c1d063b548ec70ff93698ec40fb1b351edcc0abc2f9d9c231ea7859b4b703ffb1ba0d84767f9d59142643028de6589a61c81f9d9612a4b9fc0058a3a0ef4af08f4e1b267e4d09856a8080b5768b434cfd115f226799a599c3d292b9260c458692468beeca40da7742ab40de86390a70fd80d50ff690072e54c12dad1e7c01421f49bfdf93837db8502e015cead83c11513619db7ce8244b9c836d36e3725aa96acb14e0f2137ce2d9581437958c0722cecaa8be8e51a55f0ccb324c521e5a29aa73ded909f8ae2ba61b1a9114c2309ef1a86a3033685c5c855ff09cc2d8a3c8a18eb01b2ea275d4c339735dcee3d97293f50c47605f8b43270e42e56a3fa37894303cf4e500a8ca5bab2a0d1c3ed271a66cd663bd99c362bac00cbda72b0e87c11d2131528f91c281fdba0cfb100ef3a7f724a6eac92dcdd3b77975c315860af8a66396611291d8f0a4474679d75c454aa54f9eca62223c62618dab43b44d259395a73e02c8cd8807e26093558c10d6ad747fa082463f4ee98cc8b1130c6a759110f3f43f5635e64684e094b51a38b56ebd000efdf8535e3593cc04887d89b2ff9bb2df8f51f178f2fd520844c8023badb7703d622ba228cc2d6669746361c752bc23b80a1eabcb567d2b0c6abdfb748b8239b50b2fc946563f6c135d20d79a174c4cea6dd6d804a927900f1ffd3096b57b091200d00ca52934f3c0776587c9f83d8a905de1b90c02411f3f04a2c75a9d6562a800786afd1076934c412747ca8faa480a018da09c06be6343a242cdcb502663c48fb7559223be7c384b69dfea943208e5e6a241b51fe268248c7c8965e639dc2098bd28171da4b9424fba41e8e2d07bce483ec863e355a1c3eace14c58c10ee4dddc64a1ea1a9b430c831b324e16a7cae069d7b89106e2d37b4067a2b64a28ee15b4543755fac47e00a3c19a90639feaa0940a6a4206a1f868cba562757ed6b4ec01de866f88d07158b2b3e55bfad0b6f4494a678818e5e270b65f3796cb0c41ed9c8004eb13cd8c447b3333811c4bdc66eede360e6f662490ed04948f13554958a93002a8c27218d61f3b7766aea829244ef63a4e537220c5a02db087e9ed0b2735c402df274f43b7243f7b0b3767b317458b9721fb440856ec89437372902a36913e09975ac73cae56945fe5e68154ec0d0d41350c60bf25a5648bc8318fe5e651f0c90503cba7189860b361507b2d6dba6b8f957d8a62d31e38aa10eb95dde55a586db1c908cd316d9dc6da4cb02f101a00092f8cc4e2437ef332167d708451175afe9f414b599bd45c751ad33b432d0d30eb9a7caee0b4dd5905726c3741e323c7f48513f77ee4ea717554b4398010e17ab3b5a1c258df1157a1f64dcd5b81f877b62aa9137e59c591794f79ad02a6b6db127d138adbe3244d4a1603edc735e7d4848e578c883562dd6861881d3854af25b97b94c22bc03e4f56daf83cf50e2289db4b3a88bd4595ffc1349039cb6e74edb37bf3bfa19f89f1e7d7a12d59c3d137b59c1b0591823ca82f70c6c0ef7999547b4f25a253a2222b6b6dc8538067aad7b23864535888c3f07c23570ee4c166b51431ee47348e3bae6d7cddfbfde562d6aa524d2c09aa6dae8867cb706865d17f07ef29fcddc7e6b4fef0b52ab9b93cf8de14af0d80df470e5f1e9221f6b19e4fe70f7e163c6b982972759afaf64be7146f4b9dd1d3eaf152755607403913d0980a90291234b704c2c078c15f82388754ff6228f90a4bbb5ccebf9137068afa6efc90097b006f6f6c8a29f890d1048df750048b800491cdc8cd5370fd7ec369cead78045932a308e5eaba1c58371e6316a30fa1f313da0b95209252cd3368582f80e3b00041000f88048fde4beda159c613e6030f1b2f506d23d1dfef3a90c81a3af931d37db4dce2ec9af02ea4874d9513b81a086cc958b54e29010bd70bfa39240c0a15211838a94f31e103d380817e04d270de59c426bb82b1f303a53953480d22c0e53acb70df4388ecbaa626892af12cdcadb8f6d78bd5a7407db58b21511d0658745f6ad0e80840f3ad83a6f02b0dc8efff2bd3f1db664a758da63782c76ebcba36162c7607664a6cb16412acf7ca5c1a445e8cc6ff1395bb31f7463250633034dd2a2fe447f55c69811ace8b56763e6c88bce2f384a480e9c884cbd9bb0ba342bb5aef03f4cadb0576f7ad60fce87b7f5417d34829cb16ee7b914265f7bd694881f79bdd9fa84c307c52ad2804b87c728467f055e986386d8cac36d861e6ddcf8c1db922d5132254b0296e431d4140ba0d506c873a7dd838a448d8304711531bb32508cc1e938f5cedc037810b6fab436433d8e6c705cc298adcd1b2ba5e29998d6e6162ec11f290d9b0a7060d0f373dacbe367992bbd5426522a9aadc1f53dc884f36b44fc9a3cbeeb5e20a032549162d369fafdf8638593bf672383096d0ed0c61fa770c1408018f33d30e12f3ee1562360ab27e6ead08c5f8f0285144e03c086a02e0fdee50654ae935d97f5f7e1490b2bba10b22db7449cd51ffd794e8f813fa5cb6ae924ed8f5d6a41d72999a8477eac0e1fbf6ef382d5505db31284f4842864bc087fdccf693f24240038556366bfa0167e2829fa976dea3d59194a3e9b238694a400e5dad742414dcef6c83459725fb2e08e725bc29489f3a11e9d5cb7aab02e73d5ed0bdc9d5474e98325dbf29635d8f3b6a595998aa7421ce226220faa6793fc66b56164ad01e744f4ed000c28785e92821205c4f4db68bfcd2dbfe945a084e83a39ae513825b87380a365f5714ca9208e3fba00f40e1fee8414dbacdbad1c836463d87646438cdc1706b34bf5da10e25e9585a451f90265c381ad722cb3d09f430140fe9c34077500019bd2e1b1c3b7149019218b4607a551945e33ac509c150a7edf42dc0b80d9fb425f17812abd2d46df5955460f6acbc219a08921b3dd4ede1c5624da88294db4b7605f082ccfa8b298b10ae24be1c4e78f19edd466465bee2c6d5a4445ef790df1f29521d34f907b9d48c57f6ce82c91bcefc5c2a2d6e6a9e6f66b13a53063bb75a020f37fed04f0e81155991bbb6584a9c4a18eeda6d88c01cb5965e5a4434032ff7c9df0bc9b5dbdd16b57a1bf749381d17e8edbbea62380216cdfc6d5e6ba4ccf692cf9b420c25e104b414026cae1cf54ff13a2cbd4bf9b8b88731647c3bc9b9b688d72e208139594ac36f83ed2dee804c88df06bff5709b013af34edfa6483317d962741d7166f8f4161b370d4e92454b7b065bf9f73ed982f81e6b4843c6d62a2f5623eeaec53148f8e1abdd543d32cf5819701293eb29c3c2859c3467a30cf7dccddc24bafa1b6ae1b14221e20ed836f2377d0c2bbb9c3fc0d8cee131ebd901277202068b98e1e89888a01455066734243a76ecc15ffcb0eaba89129cc5c861be43ecc539b97d73e1ec898a4ee623f589be0b75e72ed67ddda6ddb7fe6c880e9eecab18d80a67335596dad9f0a68514f085042786992ef749de1ebef0071c6a857da30227cb446689cc4ffa23e15cefa8bb2f8a040ad3b1a2b1d675b611b4aab0deb2d24640506105edb8f6a3eb1d8f8319e68f638196c11d015099f5fff75774edb3900b152c8cd39629fab9f7a024d0849bbf8a8c53460a407cf714f73e8e5269f9b17bef455619efc620b6e0eec68496ee70217015822d16ee92c0ad1893746fe659d2f3ba7d0f73591f71594a966ffdf8bc922361b499e8d4677a85a51c8349eb7d3bba0290f5f581937299cc1be593a30b4387333bcb4003c9d31d14fe60d7b1fd59db58bdce62d690d7d9a1cc81d963b00212d3bdea37309cbb3bb6b7204c8a54a005fd5073859f591fe36a0bc398dc0ce16e3cecfc04be0e3ecdfcb07316dc484d62735a63c77f18298ca8c8cbae4064f768e0d77283213855ead0075e22dddc5100c2099d1f5b2e0817c4343e6e04a80ef18410f20c18f78296770c5e53bddf78f562e91f0a37e06b55c1c1c7027f2cea3a45781b4e5da097a40e83195b7b7dc8a1947d9bc430b66dae5618980ae11a7069eb26d8593c76ffdcc73d3c76e0f7cc56df32e05d6d4220c6ec5524587b3e70a726b69c2f402bc58c9b4646e5d08443a8966cdbe439d491e49277903ba4686bf5c65f50a369738492d6c351d085507cbffa3926015da181f1c68082988896a4ab0e691a82a8d7a4a4e4ad6b664a4d692e402b4d2c4538297c606013d1136edca4c645901a6ebc3eaa6aadba4a8aaaa8737392f3fa5e9f3561534bca77eec11378700f534c99f65c506b207e5cf85d2ece6f4922d73315a0bd092b50d2a38951befd976bd844a73be9dfd474fa7cad624d20c5925016de07b220de4e8d7d08a635b05810116f8519d17048e86cfc049bb5035d1a91a72f70408e89a8a58886480ef81e678b267ecd1d59e502b567e574157bdb8f0385fa284168f409625543ff536fed657dc955dbc60371e4bef0275ba6f55fc6db29db67e54c18d7bc4cff130300302b3c1644d58c831ed7eb9bcc89f45232d48ff877ab84176ecc5da6cc3606d7e033974306b47b22fde33f17e9c2147bc883c2eb39015c94a233c09b1ea814eda96e51c3707d43da7c757a222ffec34d7c01b5bf33d95037dc4033bc6b8b2d73de8790db9659f40ad74bbd0174aa727574b0a68adec9fc6564d1c478d6652a5889bb292f5e4dcf0c2e2ac499dc28b1f838e73b8f99ea0b3efd12046c795938f7cbc7ebe04d5ac4adf4bbf2498a3742ef7038289bc1a5515cc0d2ac54eea0e91aff090e31f646fceac42083b46cf2bfa7af3c1211146fd1987562c8ffdfb491e6a8193aa12e71a177b8f40b7c81399f8e80efe2f9b47a2d7cdeda16c0a1310bc2d3b2f45d9c6bb82db9a4d47c991b657f01d418a30e61f1efec955b5d4033419582d7e0a41f42c8c184fbb8746c06a3b0476af310f3ff0a872407d17a2f939a5946648900346303eee8eee293885f9c7f80b23bcaef5aa05faa87e0646b65c3590f62001d7990b118ce2ec6edf76034a3c3c3b3b293072383cd47f2cca0da7a28e978940306ddf833f3ca489e8de5d86adfd2b5f065048706394c3401fe77d69ab0f0ed1b6dc23f55c01bbe3e3f906672e33805c33b63c32d9f33454450cc546d98326333b626743c175ca30347c5a5293609dc498906b222211fa035a2222ca32b3679a1f847843546081df85cfebed718858759452a9c1ac518f4456c9db745277f8f63fc82e0503b5cde39e8266342ae9f5c68500acff458594779d7d2ca1480ba664b571bdaf62866d163aa2ca9d8b350908f148458e96f847072bc0eeb52ec0547cb3975f080467aab76caf7c983a3a1a3d6b2ffe73614b55437a01f705ca29e4c2eae81771c53e95ce6415b55f5c3d8f94435ededa5ab23e2ade3f1c9c6aac2ca01723de4c066c3fcb7bcd7da2b6c723acf5cfa38d0d7b1ffdaa9d2c8c3655f5cc8d4aa39768d4c3f34a0ddca9e98df163758790e3dbbcc627042034f95688201656651e11d9210797cdc28a88cec1b754658ca45cee421c3836f3d44e698673eea1111366a0ddb45301340aab3a591208045420e6cbbddd69d7ef2713213fc4f7e8694ecfc9c663845df46e15aa9dcdb2b9b91258e543ccf12c288e9f9291e2dccad994f56729e45643ba7bd2e76c33d17900a9d574906d66baa2d3823d288441a5a84f4e2dab902d6ecedad32ea9a7d204377783825181e5d38c90834f5c778b0b6495cc97862b96389480e556a3344d9280e40d1804844594393ee26d865ae2f36197a6e09b00be72b08c4f71bc2d372a96995d29e25f62caf78d3f754cda66da871d31c5f64eaefeb9735b34dc4654ebc0ed37d30746b91283040e9e47f5a68d72b2ba0964e890bce4118cc569b9e31c501568b4c211849e20801ef32c6411113f5da6e778207d73c0d5c01c2780d1e14393021da7c54925a0ca2ccd271d996566eb12b8073ed43b3a9ac761b4841bf7226a9c41ca0c7067626c28e139e801f81b0b6cd430772ac75c024272dc8d7635208166dfdfb262e8920507508adee94ed3c10a03530e39c2a71534f13c28685a9b2e0b83d8d5c074661e7d26af6c3939b771df5ead9ef9e650a039c8981af5815f50f27b17f1a1fe08d5dd45efb26bee6252911c4ffa8bef1382bf00864e3d3b174fa57fd0c5d72360fe74b56be05098dd9e7830dd76f70d309116f85dcb5ee172e871aab69b219b74360e6d0684bafbe52e3104333f4150884562185b983c3c046511235832cc1ca922f1d64f1d1c026cabd828031930a95219bb593dc7e3ce7e08d77bfb9019690f641fd2db115d0d832dd04ce7109deb548cdaaba424def333cf8a9a4d9dced2bafc5236cc82377b40c04bbfa7ca00cddeb8493851b7e27cb9de1ea2d26da382a7bc3d54e8a8a231374613c2c928e9fbde524677abdc7b6d82677893506227ad6dd7edabd627384e95c0e56ec5ec2c4278e01f445cdeafa2db586830cb666e44614be938c6b5ffeab87ff61b3a272062b844ae111ac2e6cd86cb8992c18540b59cae6f57588cbe34600d079b8a32c541c1128d0650822dc0071726696cdcf1bdc0c399b19bfcf2dd92906d5cc6d949045ac1e9ddb916000f1e228912021b28d7c129dffbdd02afb0b61797ba5aca603d83dc9d2fa074b021d8a5f3c05e22208261ae1b6311e7cf2e575c1eb17c4ff33c484800f2438a92f571b6207a29b962e8e3c58c3859beafccfdb4e1edb6ddbac22c184a161200f645ab435fde33ed86d22d5e27c6fc6907c5fe471eaf4d442d8cd7c099d79bba947b977288dce0effb2b845101aae2983156708e65753ab40f498a72929fc9824c545f13672fc6b5863fc09a5df3a7e862bf074b7c304d8ad6137fe399b736d0b39cad0df424f752d23650b21e0bf59ad7f235abfeb6d8eda7c1cd36584fb30bb0d0e44e916dfbcbfc020b03f502ad4d6badb5d6607345dc10d7dd30ec8ef8577eaa6f8646a55e2429351f717717d278733804becfe78ab821ae3b94bdbbbbbbc7ee88ff18b08af0a9d01ad1ac6b9822fa94d1a7903ea5f429a6fbffeb58a2a12affff9ffdec773a39040b9a6d694d2cef5f0677651050bdd5dd1daaa6be39b74da07a206e5455cc45d0c5c265455dbda8ad44071b094f37927854d32f171d7b5049aa6f5e369dfa44e529b2c401d782b4b4b63dced4ada496172cd90c4ea1bb3b2eaabe59ea552bc9e55880b48072f0dcdd999a3653d466aadafb88094b87e53c4e44114a9fa409ad9329453011d0047f20a3d575856401bea05b7a0098e668c94e2ce794055be3d9ecc819368bdfddddddab82ea9b8946e40e51a5dc3b031835a951d58ccd51b35435b55531f1002c0ac4a0f32b624dec18443912d91186688ca24440a39c3e789dfbf0bb45938c860fb0e27162815f526d79036cffffffc56f7d33ab6d2d484b6b5b1e8e569dfa2811b3b7cd6d3adbc6aa37c09e31b1a3140b6e527023800bacc024999f1466a209e0717beff6d6fa748f9d0e74038a817c472d9c329cde7befda507d73f1985c36496aceafaf2f9f1a3f391b28342e0682ae5f2e4a3a57546820e16cf6afe399eb5a85fa3434a8a1440d356a28526b764742a64254df6c44a2a828b70bff586bd01c1b039f667edc7bef7d0453df7c8b7d41a283e831baaf3e8711852185e115a3f6aedddd3d7adcdb6f23fb69a9413c35db55d76c78b17baa98d99cbd2901a894544c2aa598cde7ee3e2daa6f3e2651a64bde503af46a6532483eddae0569696d0b4b8b9270a4b4d61c42dfc85493b6e1939bcdba543c9e197dadc4ae881be2baf1278dac339b2be286b8ae0a4aa8e08a0a24d74dc33822581c2b399cfbffdf04eb7df8d7cda552c32a5c5c8b1fb20b2a6631f784da326ac1878aebb6e215b48d80366490524a8e8beb9bd94f71117a97a68bdaf5ff6f13d5206ca4ed7f4b05be7b1e6bb59aceb3110b5e4f77df48ac67d5edad42f3eeeedbb78b4c45e0183a62c63bfaf63782d099aa85049615a3539629cd1467ca33059a12f54b1396264e6d0e2390c9f9a03e8a767a689aa3e11fb140f3ffff248eea9b93cb260a09a71629f6863c57e5e3bd1f56da7b6f64527df3b2e97c424f9051dfc0dd7dc409adb54ec152df3c05bbc53e1295e06f205c7d30b8ffff38c5f5cd6cee1fce33c3f5cd7e3483ecc542c8d1837b86acba66e819977875aa2fd472ae23d1e9b06cf1082761d3cdfcffdf30d5372b3545e9939b2a91141c84ffff308f01563657d6d2767b43751b9c4127d1697422b5fbff2dbbddbb7615a6a7bef9370c01530471dd97b2fbd1810acabb2087844bd48197d813763640e3274091749f147999c0fc91bad2a5f4e07ce0c00fde744af20ab021b569ad7515527db392a9d454521565e42d484b6b5bff2f0a5b14b0ee926090b200e8b9e1ec304a45457d68fc8406007a89a20d4901d881707ae9e13aeefc64e9112b7b3724a3838c0d878b1e24b36548cb086d90c94af969a9b74c75d31d1e00b7a3466f018f898066448e4b6badf5ad4a7d33d614c40dec066225fe9196d682b4b4b695f9845cd775fe5aaa6f6e3a35085f4f374f9b3646349588a9e9c4f0d0e9a0ecc1dc38117376963b27ed23fae2e18188476c23bb71775f7153df1cfb82c49315c61ff9ff77f5fbd4231541387677af515bdfccc64bf1948a9a989cb0a508d751949c2d8df7ff0f77698021800d37c00cd1f5cdac6de981039c5658c1c51d61301a6697037228417272a06379636822e5c48b7e699292354de134e049aa8172b9eb7c1eb6a236ca5655eb6e9bd183092be7834503cad5ebe0e375cb1ebb652694ea9b99214c28c9340a0c4d77f85dbbbbbbd5d09395540537456a17980d2856b5cdbaeb46feffe19a0601e75add22739f50eff686eaf6de9b0798fae65bec0b124d7a18dddde19c550a4ad55619b38b9da2d35557dbbf0cc58d7d429fd1a7f4e9f5540b09732106e7b24a9f3228a355d824e8fa66d60dc1bdafb10b003086eb9bd95746edc7448f8a334bf374f85e078d106edd954b00f507ef88954d76d0cd3a8c0c9c9f38455720f14b711cf6806480f48e0ac6369a4eb1c06a89b318b7202dad6db93c66dddfdd7d17ab6ff60589c6103be48f046f0e128d274ba488bb3bac13f12b5c8f999f1ed197618bfacb350d254cff718ddc86ddde3d5c2dd6d99c98242e894da293f8dc22303b174a5e2f98bf79087449572216713b8367331f49433d3a32e828837084c21e3ede622436288edbcfa7ea1e29e0d552f2c452ea0791c5a17909bb85c561beb0601831cc18860c53c22d484b6b732b9461efbdf72642d53747a52644af5ace04a7e77bd7eeee8e4354df6c6491ed098e12c48d47d4a41be22daacc2506d569ad754d7d73aef71b168f499bbc145ddfccbac32331a68d92e78ab821ae1b1bb13e2dc2cd484f4a5a8b2bb813844206158a1b97979169415a5adb96c0a36b07d4910ca3095d82c587b73a32c68eb91d1f0d98b1b475f06393c2c666f22c854c3a29be70dc347974dae647a765e1fbc97cac4f8526792b8ae633c6828405cb028c66cbebbbbbbbf7620fb2f570eeee4e7223e9116f25ad58881896b2d80922132dfeb2181191ffffffcbfcffff7f8da109c4a924339e6bfcefc26ad75aebd75aeb9da2fae66372d9743edf6407ba59994f46830364d290a9e5b2efef45f5cdc7e4d2d571091933a7cadddd9d17c74a9a7a1eeb098839a08699a8aff1c933b9c23ddeb90b9fa6af98c76319a422047cd7eeeeeeeeeeee62c5f2833b0a492b668ab4286ca7872e706cb2173439eeee59b9f5cdec1a2fe522e853e8f4eeee70507d33d188543243c04a3e1c9665599665996ff37ff84a6bad35b2a7bef9372c1e4140267f64e8a9450b3f3c9b0533406f7d21a5994250c5b670d8eaa93d378497036ea47dd3746d04a32d2dc76feeee2b39f5cdbd9fc9ca70bb6ec4e2c4f9d820d962078ea0db12ae31ca41e5cc31942eeedba66c64303647dc90789cef011a5eb6d5d9d24cb4acf1d4030f18c2bdf7dec8a0fa66a211a9649620957c03f7382b6beb9bd91a3da11b5f39828e5f3aa8c2650775ffff9fa01ffeb5d05bd7b611b5e686c43bc519bb652f76c98bd629b7211ed1cebabbbbefe4d437f77ec3e2916427a979accbce40ce7ee2988dc6885a60a9f173b30e3bb70cbfbdf76619aa6f2e1e9301b0ce75bdbe68aa6f763e35082fa06ed6d78d0b272420289d6b415a5adbdae054145014d199a28699250777dddd8db76d8cb7d1b78d41d771203d113f61046cd56c4b31a9212f1bc160c650ae46315ab064ad3062394277748a77edeeee24b7dede502a782b8a0ed4639f6e28592afe6b9e8e313092d851a3868a28b40aa940d5bc09189d60d1f134e5ade81e1b0f5082e0081ed2dcd411f5ffcf7345dc10d76d6ac8fdffc31cf874093140e123f4d1555342540472812115d58e3112a99ebb4bcd0e1f47b71dac1756348fad0943c316a5a83ddd7d32f4664c8496614c3a6060c9b2b50043ef08b1b81e985ac996b6e63ac4f984aaf90ffbec7839255982dd4884621b1b242e3a1306c3920eb349c4b3daf0d2c61b51d90dac5d3eb95ac2ce18566e7d33fb2b86be762eecfbc76620ce609c0109022c8e50c1710304d228ae963c407daa2eee74541ed41e3ba22ce0ff7f727df3ff7f0cb3e400b3b41be9e086d4986e1946f6eeeedbb7cb7a559f330c91538cbd7041c1883003561334bdfab1a44f1ca132a463d58bf2e6256b0628e71793318c736e39b1ffffc312743ddd88efdadddd91788e1fdcdd7902e8648e62741a11f5d0c0c321da80e38c5fe8d5a2b5d6563ff5cdc3e2315962b514b6de234464816eed74ce55d4beb28a59aa20622ce378bab648baf9a145a3400c9085af3124e3c1ea42528960176ec4037022aa6f3622952038315de4ff63b4d65aeb7777ffffffb7c1c5764c96a313755099ae2ae754a5b8b4987c2e4b136d692debffff51ebff7f9c77c55218b2b162e9c6891c321f78f145f65c76e0b5202dad6d6302587e0b6b6bba0526bd49e3ffff7f9665599655c1b923da48c16a93048cc2b1a16ab10ce6b7be998db5d65af792ea9b974de7131a35e94985ef83c66a3a6e7bcb6493a35f7348c3126f6d693a65ef1b29450cc8f80130e57c188af0d6ffff3aa6fa66a5268da28bd27ab318e898b1d212450586a35988b142d5c0ffff5246f5cd48a506418ae9e26478ea095241369aa71d463cda440c345a645432847a46cc6b95d66a35e19a9bc05acc6bd9c6bff4c5923867d76dc6368dd2963e35904aa8f46c9e08e50014f31998040d0371280ce418e3061400070e8888c87468e0381e0804e290300c060442412018000480c16030181c0800e150326bf978007ecc1c439bcdb579f4ddb22aa2219d581ce6b8205eb04a0af55dc41b5218cb40237be5343fa500a58c387f0643eb08620753abcebfc05d14105b7ced075e454aa2b5dc89a2191f2e10f7e078f1ca4db72fe924efa56e8c4b1e092b5df28ffa87f6c6b2c0a4cecdcf3c02febf37a1796abe834ec1eca425dfe134a0383a878650b17a383a5bad599eedb2bfa2310aedea97028c2a77696d167f1b2e0516c79945925ac38e986e0507d3e098fbda2a9b72a3a1286c099e2189e7f2491e93e94fc76368f92df029385f9b2c5c763da187f0d3156f7d03d7e5edc61b3c2e187954b6ea4b5cde7e5347aafa44fbc63cc0db7a1641443bfa687bf1014411289e74dab42a10b5f75c8d83a4ce314ec5eb1937f38638584938755ffd375c2ef57d02500169f092e3b4e301e21840f03e395570a6cbdd7ba624f3eb06c09fcc12210bda7b7e85ee6a366368332557052d85364b61087e3e5cd048fc42138c4cf569b12c6f12fae10488a351e19f87228b9614c87fb86a8087d73328b9e6540d10794ab38e76a1aefe0f811eeb3d2e8f2a0a94dc69047328c44417e891071e2e796cae4b70a8def412b093056d10c4c1f501051e62d33a83ffe1016a01b20ccaa94e4afd9742a9e83fc862893a57d2c4a270ca3233d26bfe5640ab04020dce44ac54d4fc059760d89814abe8d0ae0bce31a80fe112ab1b632de2bf7877175a988dc3423f10173de33454cc54b7bde2a59d1021a3eadbbb30c8cd59f5e7a3b341533858f09f9e57d3bd3592ad6d019bbe910456508f6f47f9c1eeb64fc80ccb2919e422b8a928a38f2c94307d43b8956a26af0224d2c85fa8be93067e80d13a240449bae09a617a086f87ab236efd433815390066cd4dc8d1e7241617046d4ee3c00eb23d9322ae1b878124638c5b8ddba1c4fb1fa2503e2674468c9ade1335497e74db048952a2ce42ab7d4091973278b1b173abb9f9a307611d761f4f2102a9bd5469b6cbb1037ca407a092d47a79157910e7322f837ba7f25db036538e76d118ed5c0acf2f718510046fc9c575f757d68c25e3324c6c7a97ca6a687324b60fad72d4c629b4797385547a9d57c4537d584dc520b61608e5b739ba17061d3672f659365d3dde4822a4b10a2fba79a63e0d4fa6331a97ffc3c00fe81d25964a10f99531ebda1a366a1b98d852ca4b28d662c437e231a76472634facd946fbb8afd9fb52e7310129f98da2316e4fb30e61731f59299b4c1dc8c7cec7e7d9d3ff409113f7238f718a8903778a95704efbeaea53ab39fd0fc37b677ea9b5544d4a42feadb97bcf35ae40c4c3a32d5e7b2bd31b71f9976f13d723f56399a184b36be76d3fec3c498d60c4af634432ebba81dc99a882c1f0e77a52be34b510254bcbd9eb63252b6e0d7e4574a62d51fe34d340ff162d05a0a456a704c85879b4e61f3acf6a8bae6a59661921c8e6b1fbdedc06f1b1b455cdc4067e18a5f8c077875a2bf7be871ebe1ed3bbfe72f0fd46402b55bdf2396429105a8e0e1459705beb4742ef268527044babf053eb22e4ec511b5649ab8dcdfa8b669420da024d850817a5b24f4c0d346dde2f412107f5b191f91140dc09824cdd18483d7b0df8c3c70dec819a3cdc4603376187495639f65801300e8cd00ea8e5e136211d17f6006310bfe710002c91db4639fb88a704d4940b55a46af179206c614bf78ed6457ed3f8295575c9ece4d1aaeb0a18f8d3767302941e5aed1a778bd3b7efb1d27c973ce6c808c1b540aa3d6f3f4b17d1ddb2692dfb882697fcf6728b6f62b49eb2c22a5acbfc644b927f052cd8a5cc365a22c76c9a314ebc7d55e70aae315aa4530afe2cbe2668245dd5af01eac10ac709b51148dd6b6e6c97cfb37c21e9a016c102a1cd3707ed5a5ffcf61b6b7b43838cc520954966373ff65836447edd03855106b1f042bfd9bb2a5362bf26f2cbcd1f5b0a471f9647d7106d42bce9bb6493cf214d88d5f51711346dfa781ab183bcf12b6548e3d64882c8d8e568135699876e3fcf6e0a6a6bb28c93111c91d0f98e1239ca2f06d19f846eb271641d7c05883af3ad7d424381a8f8905e4e153456ea3cd2ffd8e6755da9a687b4e6f13a80f3fe559082aceaa16e4305a7b3d14fc35ee73705ab1c8b63763d2a6fa43321b131675123ca543eb9005714bce255c802200a7a41b5656190d24da920200d5699b9b6709c8aa62a1ea5b86dfc2a9abddf8f24d29e96d3e646c27240be3dadd8d6d1c762f28f59ceb1203a1e28d5062d6e7c1802d59a113d502d8507c38819188dd6e2f3f040dd190a820943b40d40effe8de5bacf4743e6cefcd35e03d935adbbc945a035878d4370bd8de552d6644d9edf4c4534175f6ef3104e6b30f9668a66d26fd434887612c3a2809d506cf2ca5c3aed3b72830053a9a0dc65c7a03fe324860ff36e006c8bf04dda145f2082ac03b570931ae03b098d4a7ef14816eb1a2a96ab5e3825641d38e305035ac1022ba663cfe59102ac384db8093dc1f7e727a6912d133c45e33a1131be947c075db85fa38b95ae4596b1e91dd3a6fcad250fb051e338941420c9fd1b8d7640340aa5bf6d9e03983d3b2bdd2789714f4df6001f0a43b3d579b03882407a03a7bfa4ad41d2f83ad15ad231e4907bb93bb7a18ea02e44f0f3982adf6444a2af453c032741fa8d7ae61b0d7c36f1ed2c2e65778e6fadcb17f80842be8b0c5e8ec942def2cb49d6e170c4b1fb1f4e74e8ed5ad047706391d9e9b461d1e60a62a3ee3854aacf9c29348a920350ccccf7cbb33e01038f98e6d0ba1e178c866f39cc27fcee7fc0655b98592bab48d7f811426a99b1c121aadb55858c3c401ebb278cb6e95ebac4c6bb2dc6643bcfe2aed104e9ac0cfe96ce95b56eacc185f27eff04274ba0297842d1d04fa04dfb1ca5304308b8c42b34dc40724ac41bd49fc3265adaba394a60aa8d71c163a257ee016e70cd714f2f9dc4d3be796192cf22cb290930f5689706655206789352d374a585378666634b04a946279265175b581a2e96ade44ced9550d4236cab62b28b1f626bed264e6768ed0cf07ef873133f6488c93edad42854788976c7705d299caf0f60fa6790f378828e43d5b071da24d3e6a6e44c891f7e8997fe04211dd8028fa4b09e2028b10568b8ea7bba237ceead691d31e5c97e52f0c3163d57ab58c541098be009797ca4e00a7ac1e9e2e08afe34657b0e345148099f1c28d936fb923dc77a43832c964aee807a267baad381651887a19feafec85c73f3638a8e3f182ff83bba669a4a73ad44656327b4d521422899e827191bc4b9ff2b6b269cbab5de885adaf83aa8e08459c634f682b6a84d46709281ba36b4f9a65ded307eb5b5dfd7043a56b2e16fe8655e973e3d40e3f08a89690fe45bc87711c1c9cb2a478550c18acf7f0568bb0865f501b54e8f1ea07a6c7eb374e285e5a4141377b8924979aaf9dbe1867a4ba06e694a7c79e1c644bfe16ec7bd008c959f614f9cf9bd4893979380ba4b01765b7334a4ab57d7bce189aa0b81aa9ad38b04ce3f93665ba25ed8f32d2631906525482e8eb037e5ae76412988ce4e299a4d3482206aeb654052f2263e4fc7a6d659f20d1cc72dc7065df33c1e116b9925326d6e4b54eefd748348b43d3d1ae1947a767802c845e4655e84b525485eacdd8381cd6913638454c5d623f006d99ec1629b4579621e51d71c4aa7ac4b7dd215302c12ad0a0273d7b0923e90127c28c93d35cbd7bfd65c2012b4668ad9cda92c498fd98040566b6e159dda5465f6d9adbd076909b7506782fb007fc3bf27b5b8799d8acb606f6fd47563fa7eb0534ace2fefa1001d484baa1321cc266d360557fd2159844d056aa2ad1a69f0e55658c92570abb6a7287cc3dbb88e714a194e8171256c6b37e5fa0a3a1a70811ad6bee4a1e8e68ab09f1064c428c5dcb98cd1b7a14a76f3005cb67042a99bc9340bd9dc01e300a2677eb27b1890ce48afd02e333ab49fe921c55486a750736ae158c2c95d0ac381cd0ef1ba245a2aac7506a5f7c6b2b770b1c0335ec3d29af73d3c509c9475715b3b9edbc9e234c3b31eded60e21e60316a7e4371e53fcf1dcc601851544e6272f6dfe484f23b4a4e045a117ac962c80a2d044747f682337c51fef2ae9766d1f50f9e7c42ab475bdaf9ee4ba75f21dc4bc27c2c3e99c09dd826530f240e0210690835115f75578681b8ef20ce684368a5584d808c434cf33a6e66608b710478bec4f3cb232d2859dcf85799725634f7dc2984c3631831aff6218fe034ea37a9b327ca1884208baef8b8b10655ede148d665c4dbfb8bd2d83167414662c409f317272a5127b774c9bcd4849ebdb571e5264711a1446bfbc3a49c3d0277612da1d22b29963030abc76c891655318db238fb89634c0990f5fe047ab2da16513d6c4c21d8dba5e63142b8e81345be26f1d5cd9b9ea1484e741f8700fba7352346e6060c2132f9a2664d56db2f98a391cdb5e32b72155612a235d7e71cc9d65ba049539b073aef48c313b2c213832d248534e6dca314d6f3ce90240aa1b2600249e850ed61c61b1e153e0b5eb7c7ba4b31e58c0ea7f64817f8b0e283a9b0ce5c599a1edf0918117d5ce5a1d7d58342a584dabc0819bd782ecfb7c2ed9d1228fb8691ed8782d56eddbde20cb6d97a46e09caa272d0e46f798918f5f241150b53b4a6986fd4353c608727eced89587e66fc11448a08a611120793f4452664fcb976696a9efd74f18b8206d0d3883f05d6a0302db1b4b1ee1ad1632336d27a721f6c1e18efdb5aa351b3e5e6539e56a0d4b883e8b948ed5f936d56fcd956fe71c08fdb1a25aef5c7eeae7d664e538251f6baf673d849fab8514e64e133e106ee7f8aaa03a6c47c4d1f17d6b0298e2b77cb29fba3927a87163d7bc2c0693700e962f364d5156a13f3ba3a868e26412ce9caeda4f09c437025abb792e996250922d53c5b2d0de4317ed96ae3725e98cc2b42223e4f067f3fea04291f3c66a8dc7e83fa1730593f0748dfd7457a743ccb9ad7edac2a2455a6e619404477bac5eb1385b6ccf5371d0e29718a8c4372549bd914c3f876c7513997b75da2599d158f388c1f77bdca954e49adb0cc753b2048cc7f714315cf214b81c8739497c49b750c7dd33bd6a282e69280c6f45e921ad2d3372aff5b1547808c23547e33ae6fbc0372c36fec6029c546f923cd737fc90065cc97100a3d007e39a23072d217728188f198f3afe4ada875674a52c842978041b5c2cb5ed77525629b4cc3159bd26f84e181ef29d533d3e24ecce8ed5ef399171fa357ec5de94220d3e0142243fb98d3f1f1542609a34b061cd1cb418ba71d166648a9563cafac0ac586171c643626eb3875c433571fe8137e61b74ad945f134e30673793a4d01418d3df26c89256a49f33401cdb660e652447fe103586331238f8ad7dc9f5963b6cc44fe9ec622e3f1f1740d9a9e4e076f136ea89b9b350fe275a695d7b37d8939f1e96285fc235e48e98c52c33a8096c5e03c447f1c81a6ccac42b6d9475963dccad0254c24cb916c97c2e0e15ab6a961d94a0b1c541f41c4fc171faa3893053c84a33b6b0ec25c532f703d8d1b400c377cfbf590540f0e4498fd800830be952f37f124d03a8d6c8b03910dac91d680cd40cf898d4a79c91d3a645fe9d60c64aa958bbde2f71c8e1971ca2210e1199f143882d4cb005d3c5cd1b1a65f5e8855e466b32dda216204e86391b2e6559137b17b388fdaf7feb3e1a001ca90aa1e1fa658856695aaac3020a686ac1ce494ec8d15a8f5bd3b272b8216394d0d9b5d5f8af8206b82306509f832e0c585ead66dec6df749b0c7eca105574472dec734983a1cc32993d05d0ae559d65a89c2b21629a90bbe508dda51f4dcdb2813470293f7409fdcd335b1cf8953c8de21f9887a483e69030e90c3d2d17c37dd0632fa1579b822d3ffde15f61f4868aafa7ce89b1944e9a212387bf47cea2103476522a66f5a7f5297129625c702705fc790b0b35ad5c3ba31e3e0972f82041e6791833b4e27b4d188642bad5ad127a46c96dc14785d27019785d97bd819063e8d75badf0a27ec30a016f1e514247d750c6bdd124fe3c0318889418425a6b15d2a989b38a270838f421120de66a6a78f5385a33beaf4ff13af909957f740fe31d071b403e7b27153a4b73438dd7b1a14d78459d6ac73b9560aef244deb0cea099c993490f2544a1e05337869fca053f16d395518dfc5349cbe7bd36d0abdfa0d4dc397c89f1637a2dde4ad83d15cb3c23dabde9ab69946f5a319779271892a52022d0a7454e78b61718b7b79d1646a5587fc36a99c562f6c817799b0e6bd42972a7a32b4a6cf73e50a63109f0270b75e699b3305f22e44aed0d7eeedc2310efbe542245912689fe43cfd95ca414336241339e0e5611556a56c0df0960412b74293b9ce780bf0f5511421c138a6c4f904c049fe2680dc6ba304beb9b4d1f89b93a3431072d0a92a1c739552de036b3ee30b69c9e031ea4c400f12c6d341dd01951a971c0a931e1cb33792468e8896f8acea7fb908337bdf8e3bec6b14e85cfdbedcc458bf6600031336a3e8683dbfe06edb37b80f19846e0adeab5403b83050aa4386be5039e94db5e62e9deb3cadecb9a64d03c9806d5c584b7d9a03731a527fe036f30483269aa051dde6c770cbd08067f1d81b480bb4497b63862e8898c236cb4510936cd2ccfcb4b3e50003603430020e36a4ab26aee0262e7158aefaf2c4b447cc16ce1a5d0d114943b2a4cdf60b8840834b6384b1a150b5a3c45cd9aaecaff8dfd0922005b844b7f610c5c1478c5373a6291e06673337bbff92396f638162e149f7adfa84dd8bd5fc2a51edd27924a9135a8e42baf1f31cb8ad293f9d5c61a849ef2be47907839c10fc435f55eee4e47156ddb9e6fdab77bd259623ceba59d14c98819c1501f30d5c5a6566db13424c0b41d48eacbb58a6d75b028f120d06e7196f21c9dd5f7e62e4d1a4725810cedc7b479344161c09e5fc9edd277a38d9c45f9d3faaf84ba75ea24b68848e35adf7fbb36efdc412109a042a1d744d9cf3afe09d1ee5f379ab36fdd0c3f4f3c00ffdc7e7632ba3115dc637a001b4c631e9c2313a62eb9f9db79e6da229668867f0467556d02ac4df488275faa2bf50e73920d46c5a301d6ab508d0c3844e1fa12ce26c8fc7398885983e1aec2ce91a9ff4d79c52746896c316016d5945a107307b80553411e52e15d22f9adfe2819006aa85d8fdd1d7348e8fa541cb8e2605f0d903e9467f2c9f9d8be2214d4a4a6a2ab647d1b1b6382741c71c9fb7c7be066dad42cb68998feb50c44fd9536f45cee8197192a59c87b130941b38e07ad5cafddaca1b0eb5017fc0e7195a88d679eea0e04f9745d31073058c73992aa956a2c8c4d06f055297d4ff090f98c47a981aa1973801e19fa164b378c05e5708147fc502462d1aa0f752e3b95d65114ce2ce220d4e0fb4637835b217a34d9a6be536bd1cd4839defb0da4cac08c98d49a117685cb9494b74caea90260e05e95eff663a3ed2f249dcc61449f545b397a8b6e88022417e0d279383ba937e707a7d139cd3c9e0402bb5536d18b43bcadf75ceb4449f785c1e9c76a72bcf3c7dabb856435352c470d45c5abff838d1c115e621128e799d5b015474b88b2b646c5507a9833ea3d585689de582edbe6d8e1ea16e94cd6f171ec961286cc8b202f2359eedb94c90b49840a666c25df5cea04c23ccc106646a66084ef8bbed85e0a4b65a3170bd80c23591eefef70537ba023d4e7bd475e95e0ae01f993d384d36b424e77b1ca683449ce55183d114cf0a5a2302080ad9a0c64680be41741905f103c92a5b87f2add2aa71ca8319ac93c094f54b86ded82a4059f55edb04b850ab036d328fe7e9826e440ee22872406790e50fd28a4d9d51a9af681b6fe69ae595c1c97e077425425d8a76d2b248e7a8f002868d2d8b1de7299733837508c7111c0a150044b2f1407c4a09ea8fade288d1930fefb7e3abdf3d80934a3b1883e2c19805e116ad506a6012284883408758034490b9cabc410ac812c1100dd87024be46a031bf83edb2398fcc58cf02a077b07be5feda2cc206700899926731abd95872980e7f36b52a370c2ad9142f4a64139859cb7031226ae288926450e678250115814698ce4ece839125e5e32889da946f24e06bcbb27d39b7589208da63bd47ace879b6f2245d2ca7ae186265b3f9bf91aab440014a015e0c09af3dc639866067b7b6c1169d6e0cc63918e8288c88dd19127c865c340e223c9bdb30ba03a4eea229a51a62be35bfd6ee5fa1b021dc369bfbd1e71eeb82691fe38477424920763d8be4e6a19237bcfa6f3228119d73c973d5e4fde2bb972e7de4a51ee27e620894a479ee350f05c6cb0dd931bd9b75c60660b65d903924aeec5d51c7397ff571e4f59cd14c2b35cd3d341b0bdf79f06b354cfd44e5e9f116a646c06d3f577a747cd945cabd16f491eecf6af0bd48b6643c485a34bf1764612b3f9a97fb8825e4fd921efd16391374a083c8b0ffb4ef64161ead6684901278817f467f18210da480aa7925c039f8a2cc7a81afcb75b5b5dc747d3ed681d4b55029a25376667f9d44f6f22a155342baa764595e21960aa04cee7f3df0c50d9fd53b5a58406d8981589dad7184002fb8fdf5a8e35a1e8d386d7bc841dca238abe4342613c0ce28db32e3c40d807447fa484106cf8b244531b00602d8b48923e0825d6db061e92493004ca88e5c56a39911d316c02b2e8fba525bc9a05053f42ac4ae481de82504c0281abb85a171ff362d6bf5d0d62b0190eabc8e39c117b9af5891c98ca1e9da879225f81a9ce96d6847cf4a4d1a3c8f0c3d11d5b0e539e45864dc58fd406c6903e578baec9a89a44765cd6e45b14b7c6cac13a72428a7c78b430bdfd8c0fc2b88c88f144b832b589d541808adf6a21cdab0be327546e1eb249681961a2cfdfed236557d6cdbdcce58da5614f41458f421371b47b6d268bd25ee58cb6d34e807641ac8339bb1689a62004d30decf6b7851a94cac0a3d124f30ba7003aeed29ddef00f82ddc2ee6588dd9e1b1f96f9f75ea06d6f6465a1e4f5321ec9acdea72c5266a9f59adb5ecfc031f4b1ffc58d3c9026e29d07260cb4049c760aa4dbc51c8f6d832366ec7f6b4b6ce95b37a2cdb30c8725e4fabb5beb9825aa68fc03684cd241a585a88cc4f8588fffec5faad031ae6693246b2f70dae97d7b9581d593a5b683ce0c262e1d224dc5058a737c1a24360f75770586dad22255c559aebf76e8156c1aa901539c2fc0054d511702fbc3572a445ab5edd116c11f3f9c96557f54644c0d66494d587f23e14c76ada816ce316f27e132891f51887b44b41ca5049978f60dc133a9ea7598cb633873c09ba51254ba313b53385711afcb0836de736e59579573c9b7aa2570c58a6676c6d7df767b0d0065f30b476b115e2bda6f4c95a8e99e79b80bc3a93cd8b3eb4989192418a7211c1cbcf2a0786190674016f8fb00b92cb2d17dc52421b6d2a52af7c8b3658ae8eae2a65752451f5eea46e3c6552a7e9c1275466de0e70b440fdccf8515613bcc80ef5af9edc6765fe70ba8cc9cc88ee06f61c0579b543cf3dd384553ec748ed02263156bd57b01842ba4cfc08b14864eec0ecdd441a192804a39a3f20c82cc2d51b269fa02b8af2974bb7349c4fc480ba794c1bdefd7f10063a5d3244279bb988f1285ec3094c3fbefa0c5bc9b024b5025063176670edc4e5dc0bb44662c2cbcd55ba0a51012447d3e86a6f498a56c21dfa429a12efb09865f06df8f038f8be9af855af6db89ae78ed3931bc6976bbec4c55d470216fad2f6a09e09e4e592792dd07cb4b970104953f24f956dc320f350cdc32196487c605508fe1a1097c6d76892ab79bb6d8523ee0577d334137b1eef821c977b3c78140e2d85013aa30e6efb79c2f3ae1113929f0d1b56dda226ef43b3bf9542ad4ccc10374d7c5e90e91065b3d0a453140273ca2c90a8aed1f6a963ea87ba0351a08fb2f44829225b287ca11d1d5c837e441e2ada06b6324fcd9c5dcea8bc004727a48e87aaa19c1399545a224fde195bc80214ebe2f54353778da3078e165d8bf726a8e4bcf18dd2e8ba8f8de964c5b422b4e3966dafbc30a6cfad6e0689c5348bcdc858a4865cd743114336d60a8265cc31d92d15a62ba4f91c4c5cb140354b699114cd279b7582a43cedbe60a89a43bf38264ced48737728f942108593828482af3ca1f26034fee6a6485177f13dc548a3f8f240a1374e6afc2b7a5e2b4bd94d91a00160c26907682dc8406d5652adb8bbb481fe85994c9ff21fabd5fd49e21c3d1d741fd1570fe420347cd0bf4c6d22f4c82fa30f701581d0fb60eb1bd1fa17c473df6e4ed19ada68e0c5efea39814d783fe70101ccb0b85f812e9f5596ed7b8253605c5792fd8ffc3bc326e44b0d047c401cb4bc017a28e7c7bc63d790ce2ef356243ba69ea3ed018f24d3075b0d641bca696bf4567503b0948d5c4d94ae114eb927e913deeed182160918873ae99c3f2d4c9b6230e60ba2fd6c4edca2bbec40b8ca00a0e085839c6b2037504eef9c77b2e79e62f19d12ebc179618f3ed45bde8981f4a626f6370317d0d35dc462d15bc35db41bca6cad7efa465bf9a81c14dbedf29847a1cd05a75684956f74eb01aa37b9425e508a49a30eab46d71126c6a7313efd19913061ba07847b235e3e4e88c67591e33855cc68f90431b196583174658a63708c240008220002200900200808c31825052145b3a475cd9326b392d6c2d22a2ff25b93d77b0449db5ba6249394325904f3032f045cdf838085cfaa4bddff7f0b3a2a71c1e71920e98251c5246928054782e5b708b9500d0d493d54dea3cb0c92ac32614887c4fbfe302d3ef5367b1b68f991b32bf7de0b24acb749f40e80ac662dfa07806e01a3a8f5ffef4192073d4c3f11c4b87a7aa91bc0aea0df74814968d2e0e1f8d9df63e9691e126f89e7c493fac9a18667f882a84b88c631046a7d2149922467f8ea6d028346494c276640596981f5fcfffbf418efdf0e3c58f536b7c2f8099e317e62858fedf2c808f19a87efbdd7cae77eefbd426070f589876f74904e8290dc922449923288ea6d16212d39494d7d0732aafebf098ca9c34876fdffbf8a55bdcd6216178a2a5d28ae7417b7a2e7eeee7e7b4d9544c554feef62e8ed7629d8005a21872e8a4a7d656991a1d7fe65e536cdd84d251e19e68767242b0745481cec9664d89f1c46afae2fdd17efcbf705fc0a7e195d1243603076b6c9bc31c667e687247d58e2fb7aa6109167022ea89eb2dae0426bebbeffffbf0ca47a9b4b24036438cdbe0bae3e11f66d8ef7de7befffff7f0d8ce3b248bc233f20d3f7a27b94498e251f76d8efdf7befbd42a0ae7caf5b71a0ddffff172179f0b3f4fffff3ffcf63abde66980d788cd9a0878f09c2a72be2ae9c9535e52cff6ff8dce89541374cd8a2a5eb4b92233f3283871d6176b667e4ffffaf98fa365f44170d879a680df5064af06aa14425a84372a81e2a522afcf0d2c4032da27cc08df4c4b041841befdfcac4d4b739fe154d6f491351e6bb498c7e1eab5f5ae4c00537a3d5abb7f923da40ab6886bba084502faabc6c78111aeeffff7fd0a4faa2c1d7236a0292e4e9aec85ac1b5a2cbed7c127c629483ecc1605d25c93381a732592e9d4ed78356a63c957cf161c27141f31812f5c81afae2bf1001b8d098c19544c50716cf8f2f27315b570c106e365dd5dbc46ada2a9bc2cc70f77c066e8a118a294925f9f3f28379f93911894311872c1cb870e8d23d076378ff3dcd010b5c2ed4c3367c83f110d9db41aa005289d9b154030b583b1662ccb4803d54375d8251bc25514292d778e491aab7c93335f25495b351868c48546fb308a903e2d20a42841479a005312b9276aeaf90af2de2f12967654d59fe604a7c04ffbeffffbf922346f9bf01512f04e9b9c012036c7ac14aa90165005e8a3f3540f428e1e81bbea066622528fcff47c2d1ad1a61ca59af876df806e39f4fe6bb3e4b867a3908611bbec1580879a56749500d1706b59234127dfeff3f86effd6081801f4f760c591756305c507a4203c8510e1ccee4e20677e11564bf64512e55495946b8d4e0f581a5a00f3a61bd4da295a3d015b1d10ab1234245bbffff203d32c88f0c4244062922832091e4470dc9bf24c6ec85d1d2d01130bd2a5b25d8f0012212833ad90245076fc78f18620c89817f2ae9d5dbfc8d4465d1cc032548d876048b058e2cc5ac2c5a42969e8008afc01003110b8fe9c7d2d6122536d408428166ac7861b6c028d06b743282b89682c244a5cafbdcfbe28771fe9359d1f56d8e3fe5acac29cb1d0e06030fdfdc1eb6e11b8ca5048e2bbd364742b6af03ac1fab38a7f3cbd16c757c5587061541b15b9a4f2be3800eb07580b5daae75ce59c3f645a6d87d330786e99871bc42705ebec54c312cc72ad35a4e04db248aa9099196f97c40603098e7c4ceeb1ce7f5cd4270a4ccdddcb837e94e828f257cb973b14b4bf468d74af0ba84101541f1fde112ae6c803c874aaa72acce313b2ee1ab36fa4eadb5b66b1d74f63d0f1c241260a276adb826c3b51b56b371c00ca6c120cc609ada0829c41a568bd54098996d1bd8c00636a062758c5aec18b65875227c694eaba1c04639e21c0e875347d83a426669301c8b591acda2e5545c12f2cd72dab0c8529ec3272805e4397cd2e5f283b16f141740ab9c7d13c1e6145dcb4d3a0315d1d8715b3a031561b18fb3a412bbe082d98dabb56bad55576badb5d6da15d75a97544aebdc35dcb480b6920455313a2db0b6bbdb005cdddddd6d69770b2d43abd495227c002b25ceb959eefaca395ba696ae28296e5a299bb41a8f724e4c43e048d4113613285631bdd302da36dd33101463106e28a594769d976219adc9f4d21baa149bd3e6df6badb516ac46b5d65a6d5e3188eac23a73a54cfec050098e58c5f38646332d684c6d28b585c8ca8d71121800d4900731b7d414260d7bd490739338346a41eb1cf628e6c6354b720e04b679da0ac5dddd7d45f53691969ca4a6aa2e037c57f702657aca59595396f706dd7b2f96a47a9b4c1b6081f2d948f5f3ffff6f141444516c49dab9d57f8f78a5ea6d4e5509ddbbeb26de7dffff7f5edfe6f8ffdfa3a23748fdb0f58008d0c72b9a7256d694a5ac67c348a5687b312f2b8235624b564bc5a5ac25aea52e213a21bcfeff8f81556f732b458c30ff3b2c5f6a34f160eaa1a16406d59c32b24fffff77de778743212b050a5c6517a17b65be1ec6790d3ac583ed7abc7f49bff7de2fa29ff8b22a7d630f045652a5422afebfa0204b308b51f90383179157911792d792979397d4c5ddeba167f774939490f4ff8fc3a586f4b258113d0e21efe91101dedf58b7804ed8baef1fe2cae7aa573e960f6da128d8231fe6c3367c8371932e22556f73aaea8a4c5104cbe67816af2e16293f30d8c5e3b55258883fb8cae5a310ecab46a17befdddd7bef1554dfe658b4fbf2ffdf34a6dea6cf89157a636460707112bed0bdf70e21f66d8e5734bd7256d69465cf865aa4576ff3474432a0481116fa72dfff532431a5e881fadfc9764bf5369da4a6aaae50ecb02e09628c86bbbb7b540e191a394c5696c6143109ca91846a848a42638771f7a7bff0701f496a21b222c38910304945973292e4090c8cbbf7de17c07a9b41a3142f921cffffff93bb7b7755a6074dc2db498ea03cf45ca6becdf17f2e17a175f20161fd4c7119baefffff2f14e2e57ffb630b0625e64554d1d69799609504941047002265edfab6fc1b665f6ebc08d365384647b4e355b24bb9e539f471946772e45c894b18f1cfffff67dda19407ec7c5e909c80c971c5850e1838feffdf0acc05157777cfa105d8547424c9c0000390e730cacb5101ae8a3676316a0067f81419b4ecfeff9f0b8d91de1066a4e7eeee50becaceddddbf2cc58cf4dc2a4aced9421c111f0255414c925c8199b1647824240319a9140d80430b26a09f989f211d4ea29031ecf8ffff7f20bf3ff60a8a4673218943c4e4dcb4ca3dafba8754e13050ace7ffff0a16d8951a64fc5cb8bbbb0f1956d0055094d1084f0cdf5490e7b008950c803c8745a4720ee3c6296182514f86a04811f291f245a3257482631bbec198449409c4312a146fa0c1a3eca001c9c969e0f23def79ef46092be7306ebc81afbbfbd80e5b4a355eb470b72d58ec38b553c96727653be4390c72f594242be8f3f7ff0ab73029d5b8b63c87467e467a4627c480be7c294d3980d1c211e30b4a8e20a0ef852cc3af21158e7441f2a5eab564f998ed3fe498e75029f847ff3df80689f971ecfff747e6e23a219215c3758e8a1f528cac90c5c89413a91d225388d450a79002c0af223a23567e98bcf0e501098ce11c33e44a0c4641475c7afc49949328ab1e4d56b910249aba480d9b92e4ee1e3609c911e4396c227216b32c2d3e8e6df806e3ac1764bc0d6cd15a821185051d4a4e6e978f5a9471866f6253720ee36498bdffbb4d91a20b202a28f9f2216a0d814105656815c99caba1c973a834c649591c1edde8e0353c223201f733821e9074075f47f930a53d4cc155100c35940f443aca0c51354cfd21478b281d921a4a0d8ef6e003821f1d6589afe78b945c0c875c6532cf219115462c6a5e74140f4f5dc6d041224128508dd4532d4a08158b1d43c1ddce74e106f7d3655996351e9e72aec4bec2038f9e072732a2fcf36c392b6bca92ca7fcadbb13c874a5e9ee0fee7ffff9180e9915950422e45862f3c212e3ddfcfa96678600d2d563feef41f48c9e80e83e090bbf10c3793e73008973c93e75009c8580582136cb020e3f9f27324f1d061545499e7300a09d528fc27e5cd7318a5d5c14609cb731855cc396c6b866cc9dd3f8c0a66993c87512f74d5ac181ec108aa756f78b4cb398c836201ddd30d4850e00174a3c9025a859150cc0e0a9520aa17a67c3801a01b41865e90806424c1d9f8bb7f0ae20d136028066940d97126d9dd7de6f0118b10c3239fb2666834264b755f9c77dcc400a771fa0374a962779a456d1077afb94fdaeff718e0309813dbfe39d2cdafac58d52d4f631fe7acc16a393be79ce338ce398e739cd7f1949976c44eb378a4e56891e6e98e1fcfec343be79c53459e7f5d28d6b55fe6448d4d99ee5895b57f379d3d33b1117edac54deb5f6ea96ca2c61c97fbbb296e99138d6bfffcfd65d531a0223af6b348bb69fdab7536adcd6aa359fa4bfd035152e02b6d8d69cd75feb2a5098049999b184899c3188926310910c0535aa44c36c17c74e6a929e7687e309f5561e62937b6b400704d0b90b2066cce7da0f9e5c64efb00556d8d33e852561013e49a6e5ad038032ee507a80988b9590b15f0a606147b2d675598b590bb1641307705bc0c6a9415e72620e66e3c017102ab9a4e02a8dc13e40a8a36496cde64f0e4eeab2129478a1bcb4a10d886c3e1c086d6ae959132e7beead547fad896b78633c59370a8ca9cfb72e746a73a22e7bdecab237e75c463ebf693ea081b8ba72f79af8e98998e53831a9b1b00b9a1b64cacc7caf5f7330499894ab1b02896a340b188bc9ad9826235291bc3203a9501e162af91604377bcc693ed5cccb33b3e8e77e30e9d1758fc20d89d5bdbf5d0ec38ae3bb6cade6b735e60b1894abb52a6d8ce9c7d04c81144bd99d13ab082c4303c02ac661381710412c36aa5cc07a48a146c66fefbb5842362907204791e11e3cb944e1d982ecfbee55a2398b72a1bcb39656674b6a4445133516d5486a9b435779c9b756085e3c7235cfc359b083c1ef158ebc0ad957da3f86b29d87c3071b6e5d22b369b44e330d70c5747cc8a558b2eac595ce8c2ec5db5e84217de1fb15c0893388ad0e5d4a0dea92bbcc44b458b5d2ff112f3a8bbc24bbc442a3109747e3a3e1da00e30eb08757c3ac022b1594738edbdbe7706284ecef53b549e5c6ddf6dc5d6da766badf5b78d41ff9b707737515598a836fff69a5388d99a3362d99c502470335b99a675aa2b787b7b75aa4eb96ee5ea549ddaaf2e36a94ea53a3911709e448bf1ffe36ac3187bf5fe9a3392305b6f3792666bc596fe77775b776f5bbd7bc4c6ce194958acabad74a94e5cdb02e9102aa2620bb4c06c85340968813b5f2f53cc79b586001e272d1e6d2d2782b54953030194e895e095f095f0e512502578257c5cb15947e44290602be744a8311a146d6bdb0073c5df1264555bedffef6f1cb3ff5fcba9c8d904bff1db0c5111b5bdbdaab009b6db04b30d51822c9913956613ac36dbd5d2d93356b1776c698e6d755aa5cd8a8d9ce618274facaf55a6e26edbde3e76e34aabfd458bebe3bcb5b63e4eb559ff3a9dbe8e9963244824c0c405726e8a1cd8aaf096a56d0b6125e7706aadddb359f794951db1538308f2c43745a829dbe528ab1941bbcaa495793a0ee7736377a74066aded9b22f18a446e9c81b8cab99bb173261e9bce16eec4d3628be9ac8d73ce2e7e502fa5f3d6a9719cc1c394e270b859a3bcb2fc119b37398f8b1fcc1c12736cdcccc1cd9cb26f22d41b0664282dd707c44924e270389cd36a69a7c515c051b17983279d934e3ae73f9d18a839a5a53aaa2ba98ef4a2f3e920f02a29d7139e2164c73d628bc92165d7644aeb07b52b0501945da8c4adfa7ce7cc0809009000b3170000280c04850382288a9234c7743e14800a378a446648344a1c0d85026150140844410c03311083300cc44018043028c8d2d0e4014eed174c4bbd5e4256559250678a784d071a2f4550eaab072e2e6854e9a3f81910214337feaff0952191194d4bf7df3623633e8fbffe23e2e1abf5cbb8de99f2ba5e9fbb47a08cdc7d96814d56ba2a2295e7737051601d17a23d3c97b8e212077e4be1389db2039b9b1841ee45f8438c54c8f1a0bb2357f6bf2ebb1aad5dd3ebc70c5a3f8d9abf52e17516cfb34db02154928c205c783a5ff59e5eda33140ae64c2ed10b8386535d645489c4a62baeb8bdfc8a858ad34e631fe2e28cf1b51500e076bdd24a7a313119a71988baa1775755e6a79cbdb7dc58a67edce494b24228b57d67c4a27ecbf6ba89063681a17b03051d09510d29e39d38ce8ac221772ef56459c1795da054118952853296edb506a5dadba6f56fa74d29d2f2943046799df33c8fbabda8596113844b4b85abd86626913dc2a9c681ab5593ffda7c74c520582b3593703b6e56a022353dd23480d6f0d9d93333ba39b5cb64cc0090bc6bdade45c3403b31f1413312780a430314a98ca31fb180244405e862101aa0bb829f091f88f0ce1a1c1b6736a688f97ab168b152d590037db7e85efc4de597c12327ed45c0865e3b886373dad8351077e3cd5bd91c16ad172b669081f1bba7780df11a5f3a09aed4d6cbe2f7c3fcbccfb5eadc80a808bc97bc1f8a04fae12254fd5f0b069395314fa204c5113e992839ad0cf9a68bd75ee1394a95678ab7cfe1ff68b0ea68a16836613fd93a9b35ae6918d13ebce980972b40e722d958c93471d194797047883a60d6542ccf6f1b907dedff81584fc481b56a231ec6bbac7fe98e3a2260aebeb9245cb3bc76904c5b766cd4eeef46f5ccc1338d4db58128a51a64a07fa156ff2e54d5e2a7005b5671f77d6d9cfee5df37fa2c4cb0d9840c19a77edff35cc92bb12178900b6d99ed99e0b8ceebacd6f2ac190ab6845a2ae51b4f4ee211677789c906eccbf28b08142a6ea8c7d4f4a83249478d99a4d2f96c851bbf36f681cdb51ac4b5ddcf326062bc36d0d6c673d187c76ab2a37ebaac2c99ec1a4f85bad5dba4a6175402d3c513085b374496696d6f52a395a4d2904aa338de892311081447fad1fa5572aa275b7f095a58b5598d02141da5d969daf9789457c0e3df9b51661b5cd94a3abe79864ae3d439b489b3acdb7216b187a75fa42c1037b1bb1efbee9cbe90d701f1711118a4f09da710753ea52f3217978244908b8cb2b4a2dd9b29fad1cdcf7238f930dae7a1631416b187a75fa42c1037c1fa9ceec7f81eb825d7e91d331cafb60eee6bc1d08c7d92b4102260afd7aa52087d0017d02421f2d18a7e5d93519090c8e7d517c561d9330ab089c222fbf1f48b6871ce2a3cddf4eb5dc653d00d78088e38c05791926804b86cce41b27d7459f933646ea8bd8a971bcbe504cdc08ae3de741a2cfbf95b7fcb01c7f56c556c24c1819bfadfd3e7a5a4b19ad3d0e54e9f7031646f7219cafe4b14cd5b2fee3b802f38d1581f5b7a56ddb7fe7b2154e4bde4b0698d31cf76e6aaf447957e398e52b67be46f042453a9dedc530e3e43e1ca2431f5f41c4ef10366c2b20ba3203cdaf1431bf32a24fcf7d109f526daf710da18574dbd7f4aa17b1e7b29e133fe63a9946cc101a0c31e6b54982c55653488b1f0b8d546f317e9f74cc9b80c66253909c8861be06c15de5fc53f33410ba953cadfe05dd5b3fda2a22c54d2cc9a4c6bf3276b07bb43e6b5f88b04f0556cb07a97b03c3bfdb5b1424ac188503e367356c3d860940fac6f0a93579c01077c60c72a2a962ed6624de0bd0fe88bf8c924c2ec98579b0de7d7648d2169152a026593a4749ae464fc826a7cdc63adc62a9267242fc31c8ccfe37f299f563da3e3276cbeb3a461da4c3cb3fc243bb952a341714c0f88ab7e81dfc3d8c6602a75e56e09d1552170c260554a966263032b85c90529f79851950e805f3a0df574ff7e5a0200b8721bc22b684951be1cbc453412f8c1dd72608323987f931013a5072bd0f3b7a754529d689f63336a36bf1b8f94f74e100b41e78e9c1feda952cead86179e40b21b750f3a3e47279b84d6f01a6463b52e98ded4bb7c0ad4f5646bac849bd17f783749cdaa48832b68c33a9a279e78a240634541779e0feca9937033b3121df74b6077252551e37afc6d12999dc7250ebbc92ac13f2bc6900089ff0cde51cf3fcdbc24ae0a82fe9c6933f3eea8be9cec72f066970d52fde815e5a7e578dfd04a3d6b19841877a851b4d3560944317d1505a4da58095d96b9a2c6f9c00e8c8f836448927d2e6791fdf7f48fe92436ea9ca8ad51d2af74194f5937e0213882af63cb506c500a0a72d4217fd58301087c07f4d414558f106b88184aaa91dbf2c77042c8081cc55ed40178ab43e2f8f8f02c791770688221faccf55f87c30d3de10686cfef779055557b7f672500d0cff9adac065b37d6e5c064087c95a44d80d7a542ea032bead38b4badea4fe0762418f637fbd852327ab894ef0a8d1b6fcafd2e8c844ec9c2a078114bda8a6e1680c19a048686ab5f3d1be2fcd7e8bb98c4b914653521408f528a94661e4a7648fc5f26628d561a63a24a06d893b0ed329e89ceb097dee6c27b28acc477fdc3754f6098fc4339c03662ca46f9836844177ef7005bc7a91656f3db16efd61825ffddc88ad5585f1141c402d8f4716445777ef0a3988cd618fce3fc8f1c52657d051f466f00f9a937a913dba55c496f5860c406ff86e405124b5fa0d363e90d402f4fff436f263a24b54cf82bc38a4aae795eba96556f4c5b08e52286b79660def27f24ac2a412c7d05f9172a0785a8fcee34a9552c462b57b391c4548faf0e7256f29957d16bf0d95448d96493ba0e0c60da685e4c57d7200069129169b67b9641c3ec47874ce2c8b3cb0eeeb8770a4957d94239a6ebc0865bba29d55b20744ebcfae8b8caf4b29663a946388065d7590a622a983514b282957649826c139244373e8c1566cd80e5000e027879cad4c7069801b530a70437d2d42bad24ee4c3b003c0d3b2632fa0a3f369e0450f7ee9cb328367853cea081ab115e2ae1c864bc88e7f4709d0e0ed3b09a3f687ff8f49fd28dbea9e5c042cc64f2b83afc9b3b642875cc53a5b4c7ab4a5c562b894c5169065462423e1a0c4a1a1b0a7578b6333099cd5026af52e80bc361eb94a6ba3446891ed19739ff8f749d1ccefbc2626935e1cf3207094a0b5a28c5329b280661ff0c26d5851344151886c9dcb235e168d2c5f6040dae5b85b13d27ba9279ab043f3214f6082e3c05b892d013782e150d03a4a725932e055fceae3f443f3adc1ce1e63a08c1c20c821f0ee347b43ed25f6aee46d7c73b0c9689440290a6a8116ca721bd03329a8da193c2bd60cb1b1b46e6e8e1db463c6b8d89eb119806850e9ae5ace16e361c6ed10d5a4d72fa38a7fd3b5488dcfa2e5bd3b0f10c15f9eed13d675b2e1f0796782b7c075a7994b9f8f419fbbc6f89c2b3d1ae83d84aa1b311893617650964a38d9424167a7fc815ea64b0abf3820558b2f2063f14322db01f0297c567ceb028ea6365ff8dffacf1b49c94a8b88545da540d200053c97c67f4ab1bb3c740e4ff307ee67cb84b092c4e474b1f78270f62cef62ad40cd07c25c214778e731c92b81a41d2e8cdae0777579820696b2024e9562f0cda3bd67f7a989eb6a4c3a5d96f76b4e2e5d2fe68334e5e97b68f58b862c0cf30e4aae53eb6df73da17e3bec6adb63b08e9483d5c86e0c84477755c9a874fe673434ce9ac731b3fdc817c6923899e5bc67f67d2b5f4bf5b335e7992b39ccc6704cd21c6a1c00b73f692808eac28e51d922bc8cad28a3ea762bd61c657140fd080c7a7d90f04ba29f7f1be7f04efa0fb09f0e7fea9598f1bae408a27cdd899d3c08a43e2046c4b58b688c0ea6aabbb9df44c9cfa714e67837a82ffa6093fb7cb94bd35e6a0456d60515c5afbc2361eea8e182716f8bd936d8cd6a2b09de73eda909ce4b3adc3985d2920224f613076e1ba17e081cf8942b235d26ab7bdedba443c0d0aff5b32b3678d5b6efa67abcc515044be2507e0b7a8fa8a7eeb344a101cf2b0c0f2104094b2992f0b66e18e9077d9e0601d07420ff93232e020f0f9560a8c965a0e4cd010d9ff1666004284961d9e9102591b2e7f31ec7a6dfee3225c879fdc432754f2c53e4abdc5d2abe5b09d484bc192b83ae3bbba022189711d228e33d95f5cd414c7630198f7acd5baa10b2db99e38814e4576717cfd0e25e6a551fc535a819dffdf23a5e2c079a988b2c2a2a228f5cfc238a9018978c2b6c6b979e8fb10e44908b02ad12148327e754d295df3c493a2693bccd6d72612d74616a8d61a6ca6faab184b82dd9b8c290cb1926ee22ae0985bab94742eb42a4da8d6412c4fc8579a3c23ec00c92a95e02472e1ecdd24de3a98898f2065b7b9384e7ff51f4a1f03d6a6c4c8aa7a5691983842bab2f97ff5c3477538672c27575a8cff1ebaf9c34336a65ae222dcfbbc20642d32a2e461f0a2dca0887ae42a94254de708f4f94f01034139bd8efb842b881081f12cf0e6c6266aed4c7341d494a6702cd4c16892d27b29095b7484010190a54962357be24d2aea7834c5930105b8a242edbcf76b1fb9a7b8b11a037abfba267b280a879b7d1e3206fe6427115559408efc772fb931051afe87db2660144741385f08834444bcbf5d9bde54317e954210bfbd8fb786c9603ddcff590ecb402a59eea7469f7a37d1d2782c791ac703460b6560ab909e73cbcae5299cffec0e01f3d50589e4c642f8468c6cb439d3b30f1fd09bc54b1f9062a0d7aa699a0d30ab7e188c117e65717cbdee4cac1badea10f1bc22b420498fbffb20098356c5cb93b0546bc3804d0452a49e0a92063d9cf1dd18a21bc5e1fb1fa5f17013cec7e72f2eb2ca4c7541c8a71f86cd2c640c5e500aa25fe97610cc87a71bded107a2c161f1f0dbc164e1f2a02bbe0b31394b4e8806a7319f90bdeed33823f6dd0f600353d8be7ff1fd2b0fd4d3947aff74bbeebaf7b7000a666e2c287ebb087abb2804b5503ce71700653c780551a29b64bde174499f67862ed4815233f175d024c6fe7d91a56b351b8ee14d76816c2f4a44f29c84a57bd514c1cfa627bc6aaccc3c5a7773071940fa6a20d7cb722ed17c665f3868c694631579cea9a99ba9a5ce146d7c8f1551a8e131bdffa9665b38bccb6bae1a65337cbbce5d2bb175d71e12e3d093c9724a63cdb6382dc84760a1ea878691e49fa9464dfe2b94785a3f3902a3e6117b120ad5c2ca7cb2358395be67dfd28d99907d0d91a4bad064d6a8a418c342a1bb7c314dfa8f3d2e7a70dffbdc0a09e34a3953f40c2bd821dd41efc2be496b5831e9694c65c03ccca1d71d82d89128d01c1c278c8d28d06caa003daa83e4685d6e47f6f1e5c6ff1c872d01a24ac9511718187da197e017fa66ad488318866177421cb168729563e15682a1e28621c553880e3bd5bc5df16a1015d4b8483ffa88c1cc3329e063400437e819e330f85f16d30c6237c19a7a5e1f771053125e56ec4ddc4e493f3a31b4607473d29bac4fe1d61e65f14c72ce576c03e1a52aa86fd00910da9eb552ecf2b0c963f835e164fd43de567a7c78c9d701bc658a3bcd38e9c23c31f0b80d01fa2a66d6c0b6db54637b7e7c817cf6a757fc59b6efdd0d1728537f6e9b3483bed5c2f29967af2a0146e19698eee12c51c31517b1bb692b0fb78296a4c6ecd70afdc62338a298b84bef98c46ece8d181f7841449fa8de22d2fd70c7cc350bc11572fc27311669a943f5c3d0cf188aca77bfaf4cb5ceb31cf98fa3559e82684e9c18ad2da52076336fb0c53320a0edb858a3c3b706228d9063740451d2f16ae82682d5b28f5a5fe21889b98fbd785dd55267c6fecc9bab0dc2e4c2eeec025860610aa039a5d448d9a0c27b5bd553daf571272c22ef86105b0919a2ec94a34130e03bf6b7d2bd229e5467885dc68de1a4b4f0c8683b028a499f87dfcbecb61ec00344a4a023fdc1be3e02ceb3e2aab3476b7f9da64f8ba68d7c4fac2420085f4ded87371c8db3032e900b30116ec4416788da10e4b425d52070273f6a0956746728301f0f3e564dcaf348fead42ae28ef007433c3349dbe8eb8aab8622b5387ea77b6ddafa21d02da7c9dbe9d314f806a7b906177e6c49800a6b646ddb36379c9d529ca1d6dd7c987c6fbc5cb79bf9dedb1bfead3abebff79d5f1b26bdbe5341df53a6f46cf7a027e077d87a4429b03143b724775ee341dedae833a008c75292757e72fed5258a877d0dd107adeeedf3b72bfb3dd3d546c0bb4f8cf7f27c4359965f5420e4dda7aabd4c5d654bd0266947028d22625f2409696b15b4a9736a7f38fbc3dc4ca155e91a63ea376aca8790941701dbdbeb39d7e6e619c65d5d4a9dc792539a666815976bece9fce31400f6b6c9027d27d8aeb8c6b94a74db56c46fd23475fd0063e44c5622b723a9576a8ab1d411e01861549d91d1a128dd9d1a5da2915f55bfb6ea61191d46db4cd9fdd421cd6e81d1cd7ad4f87c0b0e769f6cb4883f612ec38acf3aab3ca9b7af288e231e1e344ff0e2386385e2afc72afdb5c84c76e969b2cab591e67692bfc56d5c6bd8b6ea12ef7ae00c3dcb306770d6715d8472352172e22d906279f80010c6be21049b584168f5d1eed045ac076e28454d0c36f8da2e304da957ed71505a81ecce373779fae48eb78d9b4363ad30dd591a081a227534d78bb35ddcc1deb6a182fee2ee93f371471cf0fc8772f06a7aaf69ca81b447f5864a0dc5048b07951fee89262def05f979ab72f0273d8718e7c00a302b89cbe8e8b43649ad432699963ee2585527303db6884b8bea4e8e4ef06e8d315d6cbc4865b6480db59e438fb722372cbb499b8c21d4903cc3e99e8322d496d0b32717ba3048f8d3aaa81a49201ea4d089bea53dbf5ced8c2ac9e2a1f6c5e14969a06298deb1f6700c22f875d94c1bc4058064ae4d5f6f59a8445777e646344fa05a087e4431f1427c07b2a757c066a18df2a532aa996e15d700fe454774e2e110801ec15d1ec88a0bede61e9d48f2b363a6abfc5459e0e925de2bf74c684679e7617b085b27edd8be8c8702359eecf36e90c31e9b145bc259dfcc598523ac33f246dd45a610b1721b2168ed30afae4022746b4e07e73397bac093b63a79f2d8c598fa4effc3bd155e0f42049df0734f723ac78a78d7d47ecaa9771d68f0224f7a413bf97efeb7a2eb511fc39c16d85f22efccb4dfcf43dfe6e09f23bec395d7a5d837ea3fd718fae7ddbf2d4dd2b4f7d80e98c9a46203624ccc8394ec028967d50f3d4c5412e8334481059360a592006cec83cf51c78b09623089b391327c8e5324f5dcf93f5a4e78b051320cb41522854dac1c5c8a8d27f014b8ac06d40cae9cb77f92ac1b603bb413f936a8d70cd210942acc194da9256863abadde39128cbaf1bbf00a638be735435d1ff47a1a5e4c477639d129baaa42cf77ce646109297644b1678574e3b790b8708bfe4d3e42d3d5c6e8d0cf0d3f393ac3ae9efe5020943ebf62121e4d43698a3219ff7294c93b43b5df4da6067db29985bba96a9bd6b6dca544023e920b271a072afe162c825e76bf89af798fefe8ff34c41372e85105d1c14380451c2ee94474d8ad5f1d6566af0ad63b90d0245306e3725ccf7e4582eb2f60eb47eadfc2a9bf65d497ec979ddcf1845c5c7f78da0c88135daf2e81779018711078a6f7cad048be9f92f5b469f2348084dd1c6ad637f5c94994184a6a4d6210eddd2b32728d6888760cb3d16d6188fe1e3ef9aba8833a67dd1ac00ae03a91e85a128ca0f261176b8dce8114214cb41824ad00c210a069e7e18090bdf8f8f228fe60492955e4aec8b5c6300aa8d0bcc6bc2175d3344ef0124bbc5bb82184d9d9ca02c26b36abb3236c9f7991d8fb6922189c831cdd35eac5273be86ae2e701f4d0f4cab6c2f5d0b79a24fb3dc25a68101b495253025c3119f64a6f64ec508692d9625e37b8e4454fe63206cc27e6e25c19dd7faaa8307b5f4ae497bc53f0a06bf1cd9249d4c0d8d9aa3bdac0314d2ffaa76dcf0a62b75d869a2a46002bd42d8e3c2fffaf429b3c41d390e0cc752ecfa859a9a9bbef342f86ed23a256470bfc18bd3dad9b0cb423a6d6cda30f088da51a4de97237cdea0969e3d22308ec89a4a8f346def193f8583f63d97c1b620c069869dd1466f7c86b9aed63ac8deba4712b2514efc505cd8e58fb191ee421d0622d218b90485fd5aa8a4dfa79c5ceb935905dab4aa282c3886014b55178b87ae822283279ad67f74c2e19c95089ed147bc6ead21bfb8d00889ef5552c9d35747daed3c06502fed52154d23ed026535b3e1093065484539f03f78a9f6b7e0ccdf88661283d9ac68d6fec011d19a6a4f892e80d52e0d5b0d882a84f2d173374c586cea81c2c08d4827c7eef741825dcd3327a8c82b5e6c081373b64445cbffea60f7848494a3269380daea20d65e55462b809caef920a9ce2ce72f7dc4d2d9b0a08bcec92ab6472f7fe4771029bedf167115445e9e45d822dbf088cb3562df5612a8d4b56e74d76c05b47751d9e0f47d8dbbee564292c004b7a003c52426982a64ea16ea8c0f671aac521e36129ea736378c57701401c5e94561e2edc10f620499e97af8e971af78cc0aef62825acb8435b003967d4e76ca28849d8535ecf597ee3e1c772a87a695adede38a855845b97cfe981b225c7f24758cdad0d9c38a7b2050d07160081421e542990b6951228372cdbbe400fdb6025b07ee63b75fec8911a012b5e6ae7ffdf41741070480abad0bb864e7262dfaba3074674fa52cb7a614ea116850284dfae5163fa8ce273cb63dc25672858a9581e86d5c0a522d4143ae7d80bbae160be05301f23716165366a16c68dc8fc6401c45b1f098ae5270704547543a8bd0ec30fd116db9034cb75fcb7e338e4e1de5cc6d6177fc27f2a9c3e62d4b84f98bb93488f939aedd5f8ec92f411f7d8876b0029029307797694519a681acfc4bb5b4005c75b173d0cab15591e826ed0c688cc0316bc1a7e2818d6cddc41933623ca8713a3a8048d9c233383f3790ad432d25e1eeab9dcc07021e91d075611cef6328cbcea50b79bcdf1e292fb52589fafd374f0b5615e353cabf7b72ec2f067136d357638af6ec725fed91401241fdd8117627cfe58601ec3cc20c9a15427d2de78bbdf27cca425c8e02841656491a490c2539d225293627e0b31295ffd8ebdf89ec2e5bdbaed054dacc4a9470ec36a218613ea85575282435c8a00853cf473ab650334cd691b5b27747a13821470d60622bf0b1c8ecf0c85d5f621a98198da6d2aaa3655234dc89e36b15a680c88678a2f1557912b369e2ce9b0a7d858d6cb745f1b531426b488efd37f3741acf2577ae6f1f15595f8ae54a33fb7bb447dd681bda9c883fb3cbba63ab5295ea7601ce753daced2a92b3296dd218ccad7887244efc374528eb71da2e4f2eb43a44c493378726abbe92502dc02fe1e175b5c06448371e143a68b91d235d1d5fbfd937967648ca5bfe589cf79759f146ed6f6533d478e782858d758eca964c07227d94190cfac7064a5e753418b37e7ca37a255d76cc04bab062df30361648f521168a696d90a95443a991e8b8c87f92f902f7102761a1e94343c98854323f58bcd7daedf88a9196c700f2e07a542d553307cf554690da9031665bf4768334d6579802100ac568856ab8d4264cc4d999f338273e9b487f4361b16a7d44d45b2c68846cfb8c1c843c69e683c1f5e562b71566d388a96423020963f8881cf7339d6a5b7c9ae0c088422cb9bacb5a88be4ad6981c742c5a1e9d1c97138efd6dd0172ddf61ba0ea6586836511d5314ba99c258ac4fa9b493e5988c70221bf5426d4ee7764882af094e68e56ca288f504d243a98452b36fbb0bdf5e6d06435cb12b8e835c790405a331a2b6d8e9b5e543272c87288a8ab88889822438ba498d0c65c5d083556bc6c1ef018899d7ba4b20be937c8a616531314ba01743e956e7a1cbc862dc025f15001bec29ee0dd14fb022ef4102b4ce684e78c22f08f37f484b11d4d508fc13807a2a7c085eddff9c8a7b1863419ce5c65bf4b41f0f82de8ba4801903a0f7c0f6b61aae7dcfb6d93b33c5a94dd8bd612def927a76d9ee99dd201ab1aadc3217696e8841fc49a6be8e04c71d3db4647e5ea2d9c5ff23f84744fe2ec0c6e30260642e22d6d54d7d9c84917adacd1b86f4309ea19b4f32b067e46e4cff89f56a7c3f6085d7a147a1625153ea513f1b5139afff626e370023534caf5b677d26454f34e54cb7244826a89bf3cb50466054eca273ed2e6af2916e05134fcce004e50cad8ab567ab59be868e74076199b0a3d46650c0b8b88e1c5152d1699af18bd27e22ff0e8bd2c9f999ef8a8dee38b38dda500e67f78087166db7c96021db6c53eb470d22a32babca348d27a0ab5152da10881c965e22a3aac0b3d6230a8d26a049c13ad0073a70783ffa99d1aba82e346c62a06db055705114d0e19ec8e0d2439e770a173a4fc781b27523626d27c52dfd755f339b3acaefccc08a5a57e37fd1d34e27a7064e530d8a1c877bb6a44bd64d13bcd52e0caeb3664f32f220ec8db716c39028155987046da14885bdf560df441fb046440c24c4f8df456ff6c4a15852cee9e2bb1d2d740569a237d84b95435054295046d246c16b1ad5851f1b72a914485ab3ad295f7ba57535aaf6397aaca8d7d5777d0f786ac96f1b9f30e5dc92e0f28009b4889c5c4b27e63010cf61c33dce42276c2a599206a22209b432937e283160d501bb114427287eff7494c84bfda77fd7ec4166089f5db796832f346ffb58c269f91adecaca06b23c9c886c29068354cd0e2aaab5c31bb7d6edffc890add6e03c49e2c3a2dc1ea077e264d51b44ae2aa20cbdcce33eccf02da8ad606f8e63247bae24c29888131905294c497dd0f3b8afc4bb943ef5afdfa0c48e513e1b365fd0c507e53e79b3411a2e04fd564ca3d0264effbebd4007034bda66ddab911d1222ec9379f316b93afbba2f610ee5423062d717b10ba322279a48ec2cf753a32a129ca120d08192d038b22b02c2e12a57c60f37f46389bb822de419b33b7276adf0b1fa69861f07457c159cace0043c5b9b2b26baf48cbc0387588d4a4efb26cfd6aaf6c5d2e874ea4606f40a98f4b23d7b34984d3ab5e83bed512d26b67a7891decf494883976b7eb3a4db61348870184aa2c63682e61ac5561a824c273e45089ae4800b3531e157366348794899e63944ae41bc845d468ad519380eb78088ff96bdb9302876d9d4659712b34738cab0a646211365188da9a561d305473394f52064bf991904e0624c8221ff29be1c0a571ac9031cf268299a335dfeeb4e6ed08699c7f711e050d23f961c82f6f33e118a517651e9d7a6c5fe5962506480c6e2b652928e81ec37ce174bc6580cb6de821947aea2696ae7b98e5ab2e78d77d4f1cd3b5ef082c06268033d736616226bca938ebc822165710fd7806c2cccc68ee4c310e85b492bca15a5f7a1bd1a672ebf0b1b032c3ef090e0f412b7e650a8b54ed2a84240ab90db3743a638d3e2ec145413b32c03df97f0d5289bdf9f51c6f7336b628562a79014995a23481e5335acd6f2a64273ef6e0df0f97a2575f0a46ee5a0f95c98d885f96470cff4e32854d988ee6e261301ac083dae3fb73caa28fc666576b805ed0e0800654382ae946c42a15e9bf6bd31ecfebe8b6b78322e897206cea0096a1137413345650a347808f5885a651519b30b6825fb9b02a9d8ed20a7403f6604bdb2208b91a7fb59ce643f12ee483251acbf8bf847b83f45d7fe65412c9143debb7ac3c846d1354323066e0ef0924e932670e7e6d25cbf27dc405ff6fec38f67a5795053bfc04e91058b1fcf15ddecbb96cbaa87467c1ab06320fa35bd938da682c4d607d40d60c0b50e5367f06240d4fdca5b0490a3f97178b6c6eb56c9678c7dc8f5a44172668beb38da136b823e6be144ff29b6f3cfb5e48640c670f78dfd6cfe40e1296c581ee91debe739cae80e47ad80a2dd9d7d94bbc6078bbe470f67bb217642032b35f213e0797cd0c3286e7ad0b6fae370484a4dbf772d11164067c52a46f8bb96abea4c8f56628f27680d672b1b77f8e9bebffef7641ffa623c7c226d137b027036e1365dcc4c5515a1c8cbeb67d25644421f4cae292ad35315f60c6d2833a6e81b7131ce6b0a044b4913728e120363632b09a1abe437acbf232db2e9508c1bf547079ae2b378ac4c18b55a4b66b65b2257f18006172b4de292225915c96a34d617a665a3685cf5bdfc24a47a660817e55a489d39a63af44d49aadc71dc2762b5dce66ee25461eb82a154546116ee8de15a9e497c44fc092b223d98394076c537963fc7e0d5cdc55ed9d91872451c36374d237a236974d64cc2a59bc400cea9dd07ed2aab28256faa85580bea0bd48762ae977ffc1facb24d101a866305cf1c1c24e0fafd8f31ddd17417166879b0c5647a97323ae81a45b4abe1334f2f52b6dff6974ebd235e605b9f2bfd6e0d9bc273d26a913fb0b0f6a36e28a3669fd85177979b5a6c03ed48874e8a084ad468a07fa69784ac34430eef6c278459c914dd12741924357ade724149e91709fa95f202ca4e750ae1afe658938d73484def6c0ae52f360f824614f4be6d9bd39bdaaeafb15c9ad7d4980e11d6ea10df44066ba1a8afbc2e49c564207d125d3757fb1dbc6b95086abc4d2d7da11647e2b83d2b1e44c71f98a1284b82231901dbddca530803ee7436a23d70a269ac4adee089701d0098a300cab055ca464083202e37ae0739ade1b2bedf905e3232ae2adb40f8139195b04d2fcd64c17e14fb2381fe168efd52f915a875e5d5bacd83cd6eee0941248e27188a9516ef71a31fe2d30880cec39decfad5c844308600d1a7205e80e5a3354e7089aa7a8568d00341ca63aa574a36a150afdddb1e18afc6287a8fef2138f15a9904ef1e058b26c5742f9ba3f670893a3e21f83b12cdd623f6751dce7d6b1b453d827c78af32e7f0da7962ad448cfe5a4c6784de2980bda9e1ccd1bbcef8f9adae72b10974ee88b6a80fff67315592d1eaf89ed567ca5c3b8cff34a6c37be24c704d299ce83fc5feb8c8dfc4ff42d49f428d785d03a00380c3e7a47077ed90516cf2ed7a385514e784def81704ab98a99486df617cf6407c10ee6f82a1dc8b41ff8dfc722667de016f81e0894edac7b2992962dfeb49aaf868e511d8ee67fc7a8c5caec10c426f77ac1f3e4b44cef874c0134998d67aa1a3c9f1ca2ca47462feb3d8eb07a80ddef585490d9606c1bb1b07ee7340e74d50832ecaa81bcd9fb0335e79fcc6793787c952c42d11dba50d20ffe0496da0392d32edde32018c3fa363c3ef282cad20125a08c07a40850c21c291d19634f3c3413469c74e49e02e88d87e3597c84eaa8bd56942e124fdecec881004615b63cfd55ade023d207b417f2d3b84f05f04459e379d0e0c364eecbc1b682874ad2f0cd90d8def363f3cc39423c18d3b35e538c4351f32b95734bd065a23a7b30fab871c52500d8f417bdd2a7ce0fadb3e398afab18cf587b8b15272d2e5fcbff82a2b2cb568a342bebc420d6a4938588549945e02441f42c70d3a5db313d045c0b81c6af942b8ac3f367ad35b3bbdcbdb0a4e5bd454f9e86094f070222b1d2b795d82cd7c22834fa8aaa91886c874cbab380b6ea4dea4f3394c13f1f3b9423f99c19cf3d215fc889f594bfe5a84b67a2880484b6c7bce2d99d89c5ffd9e8cce5a96adbb9bd6a15102b64590ae40a7a834a64ed2543cb4f76b364177bcd20a53f271b454d5f8359f4e1c4d4b0468dc046c1cfa64da4ecdd2c41d5a6919e071ec6f7a8d36b83bad89891f4a862cb5ce64e1462f8cb75363291a5038896d36aad2cf5e72ddd7dac63c275aaa453990c6d4d55d36c6c70f32ca8c131ae78fa065831bb40f4c876df499c7e98c7f61fbc5fe476862c593021b93ec245f5dd760a7b2b284d1949fc27a594cc9215893dd49de270ac1d695f80d2ae5d658973a6f3dddb73079eda5ead98efd557e84984d1f9a147c4612bf9cb17b9966a5344c2899b86ab817c3545ca3d3c6d36b747332657be140fb7c1d0dc01a3ea2e4344479248c723d9b34f0500e0977a20ff42ce657ebba3bcce06deccde5f8f5624d9723a712343e67f0432567d68bc416c8657eed3e8c71013fcd22edede9257ce27960710dcfde875c4e51ee9cea40b8873880418142d0412a95319244554bf1a0ca90a671270735e9b7b344417a47f49b6af1727b34823c2152ff98f3f20e5fb34bbad7997354a82abbdd74d5da57541f4a630bc1774d4dd516328d869d4411909bcdb3e621db115874247c2dd789489e1568ef9ae6b3479208d00fa921ce8d4cee9a6bd93f4efb7531a40761a97184999aef965afb27dc712dd0802319df80faa47a220271148520615b03489737a13ee8067af853e5d42481073315e93ed487ed67eaa6630e4b687034be9f946fa60cf01341e2516a90c5832f68523efcccb4d65658ce182947a1e3ef3b988a365fa036d719cfaba3bd1dd4e812de3478dde16ff3d4f43f6cc2153fe432feba75595969fe69c582618759eaef5b0d715433085a52ce61e59f5e2681f4284e31244c8dea186e1b67cbfa8004c851222fda6791658fcbc8bb19bcd62e128f6829837df5e64aa44ce2dd6d857ca55ba77c547c97d76ca4da0ca5ba0a79b7edc3355be5a36f20fe7074571334eb2bbaa26034078e5e208bb514cdc8e834d52b9b33024b27baf67ff148d00e577afe05dc7193276450c86d23da1ef3fe1c8e18723b01b3167bada30e0761054266b2085562df5685beddd88ea27154472e43209080904f354ef8064cea3b9ae6ba9293583a97bfd49cfd4db4c515e84871ab7d8202dd3cfa023b9ed445ee585a29c9346837225ab107d7428107a605b2024809f08bcd49e77e31d8f8fa3bade8201853700377628b6b27868db75a82651277ac750586a267a8d6b84e95d743311db89459036e5c64fe904e1f2860e5cfcba6f684b70cdb023a20f05f71d34ff6794d53fbff5f42d4da69283ea778cc71f78481260498fdc80216a2b8aca8bf30ad8bf71947662987552b09d81e210448e8e4d907bb9f0fa82131aa06bb6290d39e235c64e822105f47c2e5912c8f9aa673c4a6f0ea872eae181e89f5ae0ff862240d50c9365ac3f66ac3dc091cf87e450309041235ba254313d4d119b0830f5325467475e3e25daceb4b20be2c13cb84867ccf5750e34febae47d51db582bfc5ca150dfd10351da5b758c595077404d34e4f277a736d94df0173de10763601258295911a59414aeaae97bdf50a01c1a924e83a89550547eb8dd5e493d670736557f43783dc3da162ee48836b1319216dba35556bd5993c7f31219ca5d90f8640a8aa1603539ef4a70528c591e3d98ba0beff6e1a373b84172e5916727370bcd6d064cd9e41d2fe381aa2382cc7a0894443cdbd35c25ad847b304083dfb5db2e964e1f21693030bad5350faf263408e88ef15aea1061a316db71ee63c389e8e001be13b646d9f0d16092423db0bdbebce8435992cc08d06486f4c2e2a563b377baa4b55a9ac9ec9332fb38a1f404eb41a8bc70325be2b2f4d7696d8da5b48cf36103bf94937ae8cce21ea836d40297e2337aaa7a24fbb7d59826d95b9fd148a39507e903182e3ed090f02ac4ef637ca3b9089e250ecb4435a53965a6b41cd0c44c52d102dd34d50b72d4be85885d17711ad1017c189b716383776ca1aeb6d50f66d9a1da4d8385873d4edbc325eb4888d0a8e032583ccc092b5778bddffd7c39e6cc2ef36daf3b4a78e9e378cb6faf4675fc6747ccb4d0fdfc0c8bdbdb6bd0036f4b934fdd3fcbe4590667b626e726670a3a76fda64c1d581f8c0f48ee6f1266944ae9be9f618b44c1a11f6441234f4bc601dbd770f794349e0f89d877c536f79c19e354151cd773352a0d26041b8c6f5ba787960a517d176181a42673e1016acc23c47cd3306dc3fa090eeeb38605510617c6e7fc47c0a5a9bc23600392239455a5886b4bcd5c27408646404fa58cba456305e55a503b96d31dc07edd030740ae233d813a4e45a32669b93e5c430b61421ac57a8c39cdbcdb3819d3984f60a00301cee0910e6eafb824e49e7346bbcc7dcf57fe4a2eab20dc2e1ed73e8efbdc9965b4a99524a01b00a4e0ab509f25d4a28656729df9a8dca739d98d59dd42bd46f73cef41f4cd131aca9a35e58548952332c4a3ecde2a36c10aa9276a1caa743eb83a462d83e53d01743c714d42994976f3a6212ea14e9e5a37cccca638874ca5f888318a87e3174be183a3e957bea174327864e8c1f31763ad5285505c8694c19fd66c6423f6f4505880e752f29dd4b517bf249dd8b92f6e46fdd0b114af792e46528c8695e781c25bfc76765020a9aa6208b7e322d2e70a6772f55ab27753b75355c689d4014a72f40967091d39e34d29e741703ecfabdc3315aa904a900714f6c2a409d822f5fc588dc41652a3d7c194ec35d0fad8755c26f7fa669f396469bba98e328f8a52eaa1c059f83524a19736abc542be50c17114e5655aa9c4605a851f2e5f6a900c9c7ac2fe50c559a825480e67fa53654f956d7f56c8c748113e402a73df93f9461833cac3646fe0f64d85ce0eca0ba5e460e55f917fda01426a8f2a3434d6580e07f3ba8aafc2f864e95bf83fc1d749e87d5cee0aad0f5ec8cf912a7cad9efa20660676b12120ce37e6c1f4cdddf196cfb8bc216e6d3a1a867b42c98c519236f391bbf943408cb8a2635d9715a12927254368ed046b356bad30e29c75f8521b57c482249248924113cf1583eaa3f770a2a91459249a7a06c62e15839968f4e4d95139dfab1d3292096904e9d18aeda939706a32679e0c1b675bad5c59df674bab0c1ef48e5f20f46d83e4ef5ab2cce38b1911647518b916ef5d772ba19a11ffa1536b1d9919c68cfbbeb56ea47caf95577fda14ff2444a56d6b4689095202a6f3dc9076985329517ec919acd88ebc1f6653c4dcac1c1f6fdeaa4fa2624648dac6c43b2096539cbcda0fa3b0783ada200d9f865c430af36dd5070b0adb64b8317d4681016b8d1202b324619317a8a3488ca674384a86caa51b6333386ad258a6de5557854e8a908ae07cb73bf352c3d3c56e8a988952f7d89f650f9955f56298285621cc57454ba9e287d568d31c196604bb0263e2b2999994816c100c2829c46e57989ca194e65c886ad47ab9cca2f89a60e06e4287e16161e0f145487a0d126286c4962c708b78004bb8ab6708494d33d5b0843d016dc10619372b650fd93449f2482d6f4d10feeee9646fa8f4409f93920b0fb15507bff1364836b626924d1a25c8e210d2bcde67fda3b7155c9f6fdea312e63cdc649b531febd011b2e2aa4365cd4a63353f975144b6c89f839d53508aca3547e63e418636c9793af0315be0a954cb41a1bb0e1551b5e859ac639812d93faa4b9ac995b9bc96283afd908a2f290663e3895d3a8d04ce7e464b4aa186739eebf52c1489a0dd6a43ddf843626d57f43a9fe1b932667a8fdb06867c0df95c9db32194ea3f2590ecbaaf2c09ce15790c214713a9baa5331d53f73a269b81f3f6aafbc4a8a3ac7832d08e7de4939b248e5bbbbbbb9c3f13f8d32d29ebf4aec20fd5517e4c39854efd8dd29affa4715a7c3b2c324c3a9fe180a0a6632f93eb692ad68367384f234ba351bda4c16dbf6a3971f7fb5992c2c78d22dfb90c2559b525d536b35200e13c7a9da0b0018b638bb95bfb62dda606958a82906895190b68c67cb7a3ad543f5cf86340d91ea8fb50f51d874c026653f7606bf4a76494122c217915099970791676770e461e14a1c4fcbcad4c2d4a2454b8bbbbbcb3ce96c4c0c36a8c3d0136ba4669f0e4ea83a04551d84b227da3fe3f96213f58b3c3564937254b575764e1b2117643b7d9c0ad3d918ffeebafac30cf8e354964efd4c264ef565f581608a7606c7169535caccc572c1ed60d3d919d005f5c2c6dfdddd9de0cb8bcf68928d5f05feea60e36751c1d998f8f185f5449acdc6396836db43fa4d6d4731d826d604b0e907bf17557edb174d50a12c924452497bb40b1b3fdc8ab2ae7ff53f8d7a4e061bcbb350ce89469172542829a7fb7f9a8685723acda9de48733c476a8b0fb66f2454f96591d370efa552f72b477194ae74009a21c3b64be851e980ba8e6652497bfe94126d8cffb685ac67dbc2503d3343751e1b6c19cf0800598ea33ccb21e5643930c2786a6e48a72c1f22dc4fa72c2023dc914e9d20749911a2c2454619a54b7b0ee98c15b68f9443d221f14098f1746abb5b31b5376386ed93445be4d9a139641c902d07ad8657331e8d497bae35a91c900dd32113d26c3624ed3907562f86adb9c3315ac180e1799a4de6a1000000e6549c8a7322068546633df4001db2e0542cbb62e848dd2bb610547fa87bc5500d6acf0ccacf6836271f2d8e4705c3461ba9b91f31462b5a3162a3f7b746188659d8c8e280b4c713b34b9b57cc2e6da27c76691313fa70a0bca5d974443919c000341b58b3f069a015612a4b6936d66327196b36463f3386ad47086c48094a0e7400da19d9fb8b0a3538a1849248d36a5835a331c7af7ead7f76ebe084ea8f75b167a4d9f02aa428377081c306e3a8fb24c5117b349b297f76f065b4309a8ccbc126835a61e35f79963fd570516155f9f83b7fb5fdea57abed4a461261a46c9d4665e55768a5fb2047f9ab740f44ca5ab6fca01ffaa05ffdea7f3aa5fd9117fa9f37f2407fa43da15fd1701a0dbbd15b1d0e14ad3915c454b019a517df0e05c8e1708055ff8cc769a28ed0f671aaa8c339c18d3ee360b03346b59f9786532dd782a5d9845aa83aa7d9340b4ebdb41f569bd0ce58695959cffde0ba65cb7e6ea74776dc4e7bfe9c4ae3549c133a1c108ea7539c13dc0fcae9b4c701891dc7a3916013da86b6ee043721a7813332ee061ba6c2e35404cbaffc0aed71ea67f9d2b3fcd6a8504ce7574e45a83cf71ced716a1ac4f4352bf4a6a6e987ed54fe8edb71947fa933759caa07e099eacb742a3f7c49e42820182708c6a94d25d7ae3198d83e4924896491ca69e6dc36938994f39ec795321e7eb07d3a04a5d47dccd4a9affbd829dbc75c325565272afb307524550fc0bf44da8140d790721ce50f0020dbc7a9be5f459d9557a19c8ea3fca15684ca479d5f390dcbfb3f50a71a32914443d5ff8d348d24924c1e49f42189389df65828d7c30fb6f81f2987dbd9bea843c3f23df55ba9dfaf9ee6574143aa037d51877feac7fde09c68943f6b35bcda9453b5e78f92c1f6653cfda736f1f0c6f4ab7473052b8001b2edd4a9860b58bdbacf03131ba7c281906206c87669587eb3289686859256b6215415520e15d5bf856643cab1c1c62f8976c66867c3993907c8b615184f3a7fda719a958c6706eb3994bfaa2bdd49676234b81a6c1f2967a53bed384a7ec63303af30e3e154271d156cd67f9c4a487ba49c4ef90cb68f9443ca69cfffa5b6ac36c69fcbd93e524ef58f537d51a77e9c8a24a4fac3ee3bc5e0d453fd49399d8a3e549dd2f941da21f174ead4bf24219d9a3d433a45a45393b422fd908c7c50fd23edf91bf99f07fa9566c3aa3acc9061e3fa71aaef5755070119278046cd341e62c4f4e0030050de0e3074900980007e9819408a46064c0e04a0330a608003748f8004d0a8304a6f5e9993dd987e4e559d53714ee8547f4ea753a6eacfad38234d3383fbd1a92de2031527a4470af5e37caa5b42b4503fee87dba9fe32c2d54fa7a216aaeb206fb6669d1e144055001d02e4b824404e2480ca075b188cfbd1317667d3276793b965460cdb375b0b620757ac28d2a1ca98926a066c61be169cea8fedefc79e9db152f7a3cfce803b83ab34db448cc10dea7e33161dad3606093c3bdce0635063137189e8137f28c366c3455dd91961d1826767f0632b48bf163c1861b1c2a405cfc6f8a314ad30a9fe2845284c5676760677116d8c4f0b4edd1c7618aafefbad5874a46a4927c65994a2eec39e45128c03a6fa0f7e0a29cb20e59c724acb92f39a02323377b725a324f204150c1b32cbc72073949625f789fabdb7351d3569e67fb57fca04b9e20c62acc21663d48480383831c6d561c52c0ae1d57accca628c1a10d69c3487578beec6b084d70c20197a5b8860ebc76407f3a31c6cdcd06b1482676d252a3f4b8e1f08a4d48480d6f6b427578739b5a0a068db2243404185fdebbc3d4c545b67b98728422208bbc0441107dbf68630085811204081bf88138434f14d537b1bc4774a18d704ae3bc992c9d4e2bd056df13d4cbf359ed0f6f5cee628ed3f19557baf5337e663af6559463f1c48d4f9721d35a2f2afee867c6c76362f8ab136c41228797d7bda6a9493b4474a799595f715ace525081bf74248b4b7b897f4d340fd30ceb43f3939c9b2ec6dcccf581ebbd9012b0be52eb00fda5fddc7437aa73dab81b4676d5cb7d215c17d89f680a2c7e9b92fa2f44e7bf497aef7cbbf08ef7795ebeabfbe290f28ae76e9ee9493bf1cf7277ababe54faebf4f2b153c77d77a5f7aea5ebfe1edb3232329ba3ac6ffadea3290d47594338cafb94fdd60f87a95e9fc92cb3d622aad667970c3ad2190de2a2db9eca4b3ac412dc979e86ab741bd7f9735f447f89f6287d3f47fb7b9cde7f6b52baad75dab3de89f6acdfba56b5677d0341f9759a93e4be8896ff165a04f72ddf427b949ea35bc3f2d49da4949408976fa13c4aeff23c4eeb5b7cee2500dfe569b4ffe95b5c680ff73fd193fcee2f7d7f899e1ed0f2a73f9d4e44949e7b7eecd4f997ba7eae6be94e1d0f2888387d89f2e0010504b666891e5010e1f2a5efb18e7af91e2ef40847595ffa177fa2eff2a7ff58258217540343382fb44419c75196e582768ea3acef010511a5e75abe4789f2cec7382270396d4d0bdd1c3504673dd7358ea3acbf640685f0635951240427aaf52bf4fdd00fc860613b4388eae73eb2ebeb201ea29e269c64d529ab71aaf5d68957ed59bf957fdab3de8a5e4852bf16e285faf5902ed41d2e5843d69b56dbb391f6ac97cf40ed59cf40d5abca7a4b552deeadef1ca729bdf518d76d4df378a05e5476dff5f2fa2ffe975dff99aa4b954fc3af97dd07ebf572ce396a203b3b4e036bacbf9e3599d11f5bf6f3b98e6b5a49a7b2b75e761cd49ef5a58e851c653d4ac8b6f5244bcf3d11a5e7b82f51498968297d0bdd66a93b8dbee56f6ab8ee34a2a707705ffad197280f28469488d2b7501ea511fde4ffdfd49046ffb550b5fe74a27d2fe9d4d73bd57aeb74426b743d197d5bdf482c8b89e358ef39eec33a0d59df49a460fd8936dab1be8158cf418c2e9a3d57eab626fbd6aecfe6f3c4b267ad01a30e87a9ce1f75df56b94ada994addd6704b70137eabdf7aeb7d07a55fb20099610efb80bd2b43199900c28937a6351bd8d56671604987ff6247ac31c6998e041bbfcb77fab9bb5b724e93094208654011f4776a6b0c99994e5dfbfb5bd6ed9a82e6f33f8c274306109bb533f009ce19c90c01638c1126a1614cb1edeeaeb71b1318b69ddac760f7d7f68fbbbbbb67f7c9fa14a8c07667585e5b903094dd19563cb168ac3792b9081b6536c66e1921840288bccbb0c9feff7f8c4e31f6f23301f9efbe8cbdd0b045a86d9091051ad547bc60f3e720be3acd4c33c866492f88b70f1bdec0081f9fca44438c584a77b88f8f107c7c563f30a2841864c157f13b868c2adb8b21434a79e1489691e1a1adb340a32cd05e29071b44e11542e95062344a97c3b3f87daa80e62fe1600bc222aa283f040a35a2872a782c1a88a818814e8d120d06151b6aa0906f816f875c58b17d2e4db3bd05b64e031d7b2118c14ec9bfe6100a688fc60b156cdf06d5789665fe43d050801114e0557d49c2f6c9703df90ce59b3213c6e11c56310feb00ad56688788b6a888559cb3f57b55460603a504610f79aa86423f233e192926952ea52375281d0eef84624046d5e8b7802efba8d9640f71784180200646c0c053318a434695d222aa237a5ae86a10aadf16656d8146391c8a58633a65b0edd0830d5343d1aac61f091a3ba38f68cf73b0f5b305e8113237d8f89977b44e1902130d31b1002341a623dcc35d6022266cdf0a0d19ea15ecf866a6d83e191468850455151d0b362a4a700422fc9010abb3d9c4363308e113b64fc6f487f3ad2e89583989ae4c6fc0f76f20d7299a198c8bc9c7d635486a7b92da9d620117c0752aec322ab66f8928cff6795ed1ca58e11729d8ac7708e3d58c025c007e31c2f6c9b03e3ec397fd1898a95214e971a248143e3e4070708eac400d8c066ca8fa0a0c1aef080b744ae5f791e854caaf367f31f829eef759480f0f611f26c241473ab5fd3e0bed0cd26f12262afdb6fc5a6153a6f6fc4febf9e3681a995db24d96a8eba3eebad48571a1919d36c53958cf7f877d1ce5af838c4f276384d3f09007dc7f48e88587de646a41b3c3afc3e876f895966e2bc47cb6f2b497834df513ebf9384daf7c7e6da29ccd67e3c074d7eafa81b15b79224f6c6a4e317ddaf39f2b47f9cca16752facd174de497e9857ea6cd05fd3617ea3f4ff49b2dddacdb76e95c395e31cfae557bfed35ec55a6bc0c461c1f65d446a895e403fed390756a39518462b0d036a2f46d983fd50cc08b66aaf071a6cab4efd74ea4ad21890d35cf41aa26fd8becb7baea0eb48d3ecf0fe1791e9d3298c083ba7c08854c77ab0211d36574e83f154ffd192119351130c07cbc1583a4c85613ad88fd16a94d28d80da1b196994ff9189644b12a404d58dbc6ea4ba60745792468d761ce57f296994bf8fa9a7faf38c7cfc68095393ed5aa2d315a5eb7ad271d5eaa8db24f5ea24aaeb150e4e8dd76d15ab2da8706ae66a065d3a3e9938cdd6acdef4a6edd299db6452fd61745b0329b6447b5d4d546c7f0d557f0219c450571c04c6b643071bd51f2b3bc6cd214d03e3fdbd676867c08740aaf7ecd0c180d1c146313694439b8e802d0cf76377bb6ca5b9d7638703b60e210e0f622890abd0c1c68fe3b0c745f57ae34c04cfd3ce76dad9fcf99d6be019b07e0ca506770d1bf0bba94ebd2fd6fd1a504606324766ef010f90e1287e198edac7a2b4566468cdda0366d6da037cdddd3d467f9b0dd818d56863549dce1a432c410212d8cc6451796a367863f87966965683b75e5a8d4bb3d170a4f963281090b181a13cc086b6dcf3c47f32363a815f234648215f41583fd61db7838d7d67276a3652a542e176b07db0a7c76668a8baca55bd2a2ae21ddef936682bff9e740da71288298a6d6adaffc024a3f483d20f4a526cee991a648f6f526c526c41d86ef733333333f3f22eefeeb5ee733608d68b1b585489de2c2a7ea42ba8f1bf8d4b50e3a2a1460dac0ff1a453281fdf009dda3e3e8dd8e3341b855c3027ab3007fa8828759d46468cf16bbce28c5ba4d27993f6e2a774cec497745ed4b992f6e29f744ed45e7c194ee33d8e8a1f818266ec9ca2eeeeb6a4775317d239edc5b7baf6d15e7cd9b5aabd18bdfda38cf1f752917a6201c33820def291fa03c66388435d4d0894ba746e4c0ef72cd650591362ab4b4f45c89afe51fd3e5083349d8eda7d1766c8220eb535215aea5214ae18d46c5c158858bf1e756ecc641f5cb381283a6a5875bf46d7a5f382975f57bc668cd7755d578c56c7c832c618d73b616e6603b6305f0076aae4ddac3d8790218419f7df8a4b898e548e0ab267c826b72aa34f95454b4052bfb8aa52ae963086fa452355feb478d4237f34a45391a8ca1f85a17e2322558241461564f9a3203882262ac7be1815a66914d0bf344c6ed88c18a2858f8ddfd4556c5fbf9f4fc0c248fd64e0cb604b43f455f5a2b85a51c057ab59db61ec6ed8706780f5ebd8a51f7f8e1d5c77633e0ef8e7b02aec7ae3aca3fdf9e3f388f402dedf0572f0a8961039bc5af3b1ed2e906337467e90cb9a17c891cdf6ac979605a3e5b3ba95c34ac26b7710fea59d2d13e286530c783d0541bf9b2fed6cb0bd660718c1a99b1121d5a56e1123aa5a440766dbaa8a9edaef1263657fabf41c1bb8a92eedf2feec2e5d873bacfe34dae9e7f406fc7df9fb939e2e7ac37ffedac8e1d21e7fdffcd1c7a7df4dc57e6b86fd37fa484d23eca71018bd31fffa1b7334ff7ae1862dfea4dff537ac3fc9df7a536f4c1beb0f5df6a10e21205d8ae5b0a65585155318a95d5d2ba6e0a9180a3f8672f3e2c706e3a1c4d1470ca3a720e0636f033e466f2af3c779719474bbafd052fab1c17708dde9891f568690fb198bf762c8c88610d25310fcf06df043da7db0f2befc79ddd41500fb209f865b0de30c5b578880d1f73f3df9c39a82ed653211aca490d9c5b83d9aecd7a1b432f8b36510ae87fd621e1801016b5b72d44e04cb267477f7010884b0ddddddd79977d9e3df801b638c71cac8d46563f6473a309960e66fda19ed6fe297ef3fd9b21a4a973242e855b8649dd262062c78691b88ebc5ab031004d58180ce909ff99d067a9b0309a5e52058132cc1c08b20901a10977481cbceb0b229eb7e8cce114687b15d7646accd9a7f47fa37b0a035bdaef374d9195dd7f9b27179749e7334fb8b36feef2b3672d9199625652e2bb085819833bbbb333bb3bb67edc11fdbb7451e2110c61e1a6c23222bb056571149b49a423e62133a569214401d39a938836559564ea4220b17ac98544c9ac069d4d66f6e4e3401441343965564a5836a499a0560c1c451a624acc4ba4590a0d298c062821e202a45b612159b17b6ae6e911e2aa4b861fbba457ad89092646ba95b440a1890966c2eea1691420a9a209090e3a887f9a16e11299e5061dd22ab21f57b9924eeeeeef2dbfade19496c75672461aafcb0db24e6af06bb244e40d4486fccda1bb8a99c837f470eabae0c8df60cd01f239dd918ff4865a6f3afc51cd99237e2cf0a7f568e6f25e1358755573a057baa47246031c2e7c840c867fac5282d18234ba8011173be40a4a8fa50b708112a2ac6d9c2a659028a5310bec3ab3f135f03057fa43ba47cf8b0012fde7a6865ad09c11d4be698b5c7c409dce3aebca64557d26beeae9846802defeef62e6f154a3e98d9dbfda699add8c11165d0c577b9306cfbee1e3dc61833eb2d8fef91dd610fef6edc655ee6ac891c82544689c1046fc31a27b5e9f6085c3881dd6b1ac26e25b8b9096f0c0a8cf25f48ef1cee37915b0dddf0bb97b7616fed35a4eba82177d38c75c76c2a707cde5dc89561e38f304ace8263e49f58ef46b62cb9bbdd4b186e8cbf1620fd62c591d5cec27e7c76dfe57f40d7344d09416525b85b083baf7424d8f699ab43c885aeebbfbe0ca13b8410babbcb38ad69b9bfe16d6c85f23f7777e9524a194d1bb3045b195b8a776185dbf4d474eb55d710dd7dcedd6f87338e0c72120873209f004208dd23d436c6b38de1973a5808368613ee42c81176f127a42b04f601c238d9074208b7bdddb803561e089d77f73714cbdd37777722308890c07c7d77d97ddd7757ae15d9e9b631356821b2ed77ffc670f70df38d3977777787e839e784bb3256bcbb2b572c4bf02c14ffb6e8c6851f29ec87102ad1dd3cc03ab8bbbcbbbbbbdb8960db67662deceeccc6bcf0c0bc4b7bfcbebb307e59dd328f7523e4c802feab5b66ef26f3fc6be5e73fea3ec8ccccd12d19a78df42e7aa0728cf1999d99a373e50ec66ddfdd8d69d8e50b075b7f13d65d67de5d76385760f6bebb7177bb07f0b196018420c618e5ee7ef20633ee3abd0117c6107a1d6ec38731c5eeaeef3a1530ff5e15d2b6158124dbddddddfd829db3427803424f02363bb5b066b3bbe7f47f71d30c4980c19e106a42c00e52e40948ea2723f38423b57f8646d72e62c5121aae59aec0b6412c585523fdb57f26a80923b5bb7b9d9979f4a526b6eb4f1e7bd97d292f3feb74a0bcca6ff7b13cb6dbf6b6bba4dd256dddbbddfd396664342a6392f6afcf5e7b79d2e920fdc98f3a1ddbaf3c4ac7d261ab6d07892c24425de9b66e87d7ed51ba1bfb288f5d9d8eeb7778b5b9680eabde5869c00abd91bdd67d39ac7a7d0a4dc2eb45bf8cee8fba8f4473585b77e3e4faafd75f3409af237a02e22bca7f45a14978cda020a476f5df2e1bedc907e1b4ebdbddddddddbf7e73eccf6c4c2f75d998feeda6414cf4ea603aaf33407bfd5c67cd5eef7d4884eddedd9984d7a5b0bd5ee90c09d80e8e68f3ef7064ebb94b2c02063f891018e9eed2a55b32ca8fef7f23ca6949e952baf4fd02063bc6ae91eb8df8ec438c52ca186394b1932e31d20dc58350a45ce952ca13a45b63f52598bb4308e90de8188531848803ba43ffa5d3795cc667daed4599f66eabccccedab44df7877181620830b32902079382a36f838f807ce6bbea45f67bd5b0f9f465f73be35d78fa4e0540c05a742fa6d0b159bbf7cfe66758f5b5352702a8dd601941be3524a098ff85e52eaf25dbae528801042226afcedb02d040cf9b99b58802f5fc6a80c219431ca18a3c2d6c27f6eda98a5524ad957f049a7e4688aff6ff042ed77d927345137186c9ff7fcd64b3afb372bb6a146c6915549d98a9625fb07a7f2f1968c452041ed35d47e18202c2caab32f0f363819c14f09509af0f302136831c61885a4c1045524610c4b38c2179480445302162cc8fe6188d0440718ccb7b7d05d3c4666e6f69e30c2b853acef95c1c86baee4d1e8e5cf39929347c320d963108b0c63224eba2fdfb22c26a09490660926be26fb116833fb7654185d3af53b626f54af118f3a3d06d1b621db965128fc61e4f85b37fce30042b0d9c88e6b36d773c8037e0c9a12932329a59412c69101b099189fcb0a0c94f48395f7a7deb8247c39b47185504a7ac25e62d8cfeeb480add85bffc13afa9b1310f1b1b7111fa338f647c2b9f0e662188339c22cf8d74fa7f1c7a837a2d7dc2e997a833f997a59968543a6f2cbc7641a36ae9675fd1c3dff474f230a44d78feb097bbfa9183d15111f3e0422c52103ffa64eeb67675950fa8d54c2db9d102d282d19a7e4872f21c4e17d61e30aa1fc68fdc7929e2c4ac43aeac3e1d5283fbaece0c70e421fd8ca8ae4f6160a1beff433b94cbad210a6dee087a9567cfef811074cf5ce64868df906d3d3fc8f2b3d4d2b09ae32d613a00005302ef0627c2276c727e210282bd8f87b6a0cb8a990ea80b56930c5143744f6a7f67b4eeccffe0861bb872ef408b6fd888d6033d16d4ea087239b7b50f061aabb3156f854ef93f19d8a5507acb14623476ad74433d46f26ca872720fcbbbb9b3b1d49c01d2720aaa4376812b3423a5319e35161d4b11b03e90e779ac3abbf7cb77663befda9103acbf0954e39836164b57739ac4e87e760a73b72e4b0aaf6b34b228755359b13fa25e155a3dfbc6a464ff3b77e36cdf4ab31626b7ee7b0ac3ae90729d34f099cca4ae054ff52135be9c8667d7fe9c8367f7f4e1d8d5373588e5361241cb08589f186b631fe48449bb91b13df7aabfb9088d2b1d6acee460ecea22631abf548e8689c3a690e8b66edb9c935ea5bf37fb3bbbbbbbbfb17c69180313337e14162e44757f7eeee529edccdd643c8902143e896a5811d1ee8d49416ec38e8eaf888507b8ee308242c30b4434349883ac55f64830ecae15eb0f512d577f7374bfeae543d2ad944b59ba8c6a7d157737773f7c518866118d67cf10df7822d0a176c5fafbe8ea27f3a8a9db195e7e39dabe39e9d06d23c3ca43d16386c1fefec3010e6e9a0511558d685921a36decf610a5bfcc2c6ec9752f053f7f71bd2c05c1461eb8fcf71bb1b76ef0ed943459c5004480b1543813b7634bd21df7fe90df8fd315248d7c60eaefdfecb2f78b0f9cf954e71e5ce8ec42123b67737a8aa68d0c0baa9b7109d76a0104beae748b0ce85da7b2183ed5b28f80f418411429c64108794aca66d27ff7d45f97d6ce8a6a9ebb4079f2d6b63fc4fba1731d83e8b8602fc2d297fce193f7b1bf1339afd4dedbebaeb459d05b3ebb666617cd4de032c0859af98300cfb936e1d8565dcafbd8d7eed6feaec3e58a796751776532f2925c7945b2f5f846c2e03574c2e0fe3b505a1f439e79cd0ea97d875132b36bae0b4e04309218cbb9a50ab59a781fefd5a37dfda9a9697216cf05dc674cdd1c7c73ac6c774baebbadec6d68bce64b1fdf537b5bbef6536ea9b3a3b0d649485c03e584fc331cba5bdf5e67a6ff99cebbb128259ec0bd83adfc6d6f937b5fb8ea83359c47ed9ddd4a502601f662a0b01db4eb5ff8eaecc1a5301b00fce837a85ee0efb318c339f73be0df8f36f2a77d65a3bba4aea2c601ffca1c50c84dddd5db8bb70777737b2c092d29216fc852e2974f77508954068c54bdffd1bd1dd7de19b868023460861840b172efca5d0a3304dcf84052740f9524a990a21f4604c01abc07621847083d07ddd7dddd77fb777b00b77f7fb17ba69fe0b08e1ee3a5c1dfcfc0c1952840940f60507849999b98f307f5cc264495194c9184669314b6b5e56c6f31a3133377333377733777337333333337333f328632ce34c3b41e1666ee6e66ee66ee6666ee6e66ee66eee66666666666e66eee6675ec26449d1cf91a01fa09f153be130b28a9bb9999bbbf9086ee6666eeee6e6666ee6e66ee66e6e6ee6666eee66eee6e6666ee6e66ee66e6e6ee6666eee66eee6e6666ee6e66ee66e6e6ee6666eee66ee6666666666e666e66e6e6ee6666eee66e6519b9fe9f6f0836d33333333333333333333333333333333d3853fc0b69f07256cfc71ce39e75c21c833957d2800e7ac0ffd5f4c910e44958798eb762f86b0f15bd65b5fd83ecea97dc339dc710e3facc11fb3d3ccc9809bca70fe09be6c8d0127486157d89ecbf06e7e3264649ad4f8be84a1911a1f2efde04f8dabe8bcb1a33cf92b0b5bfc1c5cdd710a82e9a744650a8b2a8dc62e2c6c417266cd35c408388b9a6ba65331daf3fe7ba2966cdf09fefec76ef90203b6301fd3e88c860216c000d53637c346994906b6c98c645cf4eb213e4ca43d9f93552b43c646ca3ded390b710b1f391df480aef180a3fc5b54a162525dd56e8450751f9240576421a77ed91559c0a97b0592a22b9024a97b0592235720b1a26a75af40b2aa3cbdba340925b33d1e148142109c400436b1b407988dd576999fa0e4094dfa8753e1477b333b753eb62f41f5487d31d2deea6705e4f342a4723f5e80549e3a513ba81576755bbbebcb0e6a2775bde8a086d4f91fcaa7ce971f6c729a1455d18eca0b50048921458551173e2e5675becd45449cb0dba8d0f6a5ac5e55677fa81dae88e1b0cddfba45a0f85163fd5e86be9455116aa7535d86cfa4d2a934b066be0c91a17e2821a89def45a7ea3815f1f2f123edf1e2c2c5d06f0d0076b688da41f1d4f93bf86043ed380dfcf9281ea789b4a7ce77c153e763336378d1a9f363cfcbb753e78b903a7320da98f92d516c2e74eafcef858807a4a722648de5a288477cf12f688f9a222cea8288bad84959653bf58b45acd918a1434b4bd0923a7f3f97faa5ace0971234a393a3a4cedf2f36914bb8d42f16750b13465245528e4549397f53133b528fa326ec4824a2eb62c3757d93945c2d3a5211690993f65454baeb5b742392924e2d29494722222921ea54ffca0a0a0aca9cdda5f8e4f0e4905429909254357a74b5b8ac7c888cae1e430b9377293e9dc27e7e8aca697e60b2692d061e08238f90a8d9a080486959735ed768c4861e8cd8f41084f50094659a767282e201226488cff66ccc146283ead9341bb0a27836663e46ca38201be9495a0dafa27c78c8696ff2e0a3bd99a2d9a086b4377f34ea0ed5d3dea4289fd56c503c2a2a2b59ca68842282c353d150abf626b692719a0dea877b4eb34111696f964a5ceaa6289ff6260b0bc7955040341b148f89c56432ada8a4984ca4cd6432a1984e3493c924773841a5f438cd890808694a4f0a8fd39c88b01ea6f058f41d357f3522ac8794077c8b9a1c35df058b6df4a8386c5fff45bfd1aa6e1d6160b4aa713d3ba9c5e5f429485a5c5c5a84300076a20032dfe5d432a39a71a253510cb04b014a09d26c864859396abe090a5bca639ec10ec5e3a81e47cdc720335cd2c9f7813afa8149a76eea7c2cfec0c4697e1872d4fc39fa8189d46a78f587a1f6e6a3deb0ed155f3852bf1f86ea7c17382e723af5fdc0a4ce1fadea87daf9614635a3da195c678aea6508b5b333463f4754d48f6bfff7325443a68ee8d2abc5635f7f8bf78ea6a83ae5d5f9d83ae9c2469f8204c39688c247369442b444872125757e4a90d3c09af9d86868b4b3311305a453110b9e4eb108e9e9d4c910944fa7567e3e8a48a722181a00abb7307513686f7ea9fb5044ea7caefb50ab3a9fa5fb483975fe4af79178ea7c95ee23f9e8602d4555e7a7743a20a9fb4c28ddb729a9f3793ec9a7c8537cf0909234cdac998fe9ec8cad736b564c7aea7c92929efac5a213fa9174b09dba15f1d41290d378a4a1f9a4261b90d36ccd0cda84bca14d680b4a59b5913a494275fe87ad7c10c3e6e25fbe3f33e208213e2d2d707c7ec422d7c2e8ea22a2c84448e200c5253336a9532ed19a8d96bdb8f8cf5461ea7e10d0884e653f7f029dd27efe119dea9fbf00a7913f7f08a7b17ebe37e7f48a88efe2852e01c548eb0ec5d3dec4ae6bf4578763b40ae2e269f4cc286a2732894d244e9da89d0e05a4bdf92e3a144f0f6016396a7e7fca6a3ed61d12edcd4f59d599b29a9ff2333f05687e4ad1fc9426f3538ccc97a13aff4b415267ca509d294475a62ca93325a8ce1f5dfd32d4a96f895e92c47a21d1a9f9176aa276eafc991d9696c185a02abfb4836d6bb6263efc518fcfb96da31db953a55c47c997f387912a3f0275ea48d3d0d51659c2901aa8d2a864cd96653d0b198d0010000200f314002020140c87c462b16834ced3104c1f1400117e96527a581749d324c8610a19640021041043000044046666260d020ebda43f7ab3ccbe7a0dc09a74248965857fb3b7d0aa343991d131898af16686613d801f00902eefe80fd6c5e0372a4d0c6021d9337fa777e9e1581a3e59a1887a142aa468729f587169e065bdf718488758729e1c071f2877d33118c6f816767ccf0a992361cf633c054dc8b06a36418a45fd29c76587983c9616c4100d651c269c36ba92290a182d002c0144a9549140dc557b155efad27f1afd1b1743e893bba442645fe0b342e64848372376fbf2967909bddf3fb247865d2221c3de048e98238c84b02fdbe847b9c87163ebb023237a1861b42d4252b3974ae865cb23be07bf0dcbddaf319c45d8ca06aa56262a56d1d08ddc0fa294ead932601153b44d84d419e705880dd32f9f2f9974e6f3bf1e4bc0363f22961967ab22aaeda5641c2717980ee6010078a6af1d456da08184589e6a475d06c583e7db9898c2d543747304a174989ccbeda7c838ad4a8bf4ccdae45bb7a8c1cf2562577f143049c69331ce1b9b513a21025b4b513dc33d2b05f19e89289fbbc255179b60af2ee0272322ab1a91543c4c193c40a235a3e1bfc6e24c1ac6cd254341f70e9c3f4f513d681316d1006cf704103f2c81ed7de523901a16c04c53b72f988064974a6bed0008ab40fd6e61e4d3facf5a6dc0eda9c88a976350533a6b2593c704249ddcf15a8ee99c8fe62f718d43f3cb3bab457891f9e823bf392955050da096e39db6ca7d2bbe3b58831b0a1065fddc33c2f4a4d5c353771f3fb577487668fdd4f3f8181719b2d5c73f49879c426284d85fe6baddd61fa4a08588de3e859b42de9b598d9496d7cfba3830acc7ee3b55ee5698863fa7f01f716b4d7b5caa98207d52d6a27585b5cfa30532395b9f5215ca0cb31ef1aebf758844015b7f850c001ba295326d16d9cea28310d0066a190d1f29288822104934f4fbcd64aca97602f43e6d20691ee576b1d07f83c7c9d48bbf7b57e6769cad132483d763bfcc8e3686a8aca268e5d4e0f47c5119f814054ba41b65790976db62b9869dcae109869e19d07c0fba0946885f02e25444a731052edd7022301627f9a8ab6f4ee7c7890819a0c7726adca0ba3fb4b9d2b5a731b8ab11c68ddf9ef08d80f8cfd3d5bcb4baafd47ee29c0a7ec925747bc739f3294b3481720f3c8480f79c4febae43e1f9338b5e783b0035bd8aa52ae18d7ba9f26308c61646d2233e9782f885e6ccdf3d7b4c2722bdf7077e345a6621e1f4048525e1ad0e5260b7b39d01f034bfca2a08438c73f900c978a1690e8c8a824b2c9cf33b880570f42c5de80621eaf07a60c252fd90e494609a96a033278b72ed1912155c8973f54d930d20bf4fbba751f88fa2932c189c2cc85b3504c7b611cc030da5c884a73e16afea03609b2f04b1d180e1e4d3187c7516e4ce7ca83110129c1b9b66011aaf2436ec73ea17d8e39584c0c5c1332ae202c81bd0e23a2a08fbdfabe66760a1fbc836d8b92a2e1c35021101e68003ba58f71b817b029e39c27a6149f828c10a5ca2b85215341e20ec20ea1829db06f41eca0c068dfafb586e455f775e0cff6b455d2e5fe94f0f09456170b2e210afc840120d7dbb536b6b55266b2fb2a1905b94954a2573411b0f83265fd7287a81e89966f535a2461b41f0808ed8b177075bfe7ebc576cd5b1a3637051df711e3f00f8a6008ea877b5ffd139e7485d17c5460c7673c013c0e58262c636e896aaba1fca6aacf9f7f291bd0243b500de1d07e77901788ca064c6b200618c17c722c9d57c5de683ed2cc6e5f09fc0b0898fcd9789e975802f5f679622b2951b2d68af53b9364debee750863c6081411ad40f2a9ce0facfca042f979f49bbee9ac6ef4439a59805f87522d04c26315c5e0f453a3b02f1c26cebea5acbd7eb2bc88f5577172e53353ccab5328cf4404e2dca8ddc5f1d669c32c4710fbbdd941206a7674ad4c2fbae0b0e2e5c9e54be3e699432f0cb76505fe5b0444f6d3803b6fe063c134227f69482ab9e88b89d537c49ec113d971bf1c6d12b7fe1896d227f07156cc4c91f99c8ab815fef95d3368f335c40194d4e036617eccdd4c102b88137ee004b9c74a9e21fa38c2c0b4362eb44ff5ff71158e7077f5fe18e2c8faff704191102a6eaa4ee39e79b5492cae949a8c66f9263d4dd8ed2249859c99d7d79b0d69f817a658678c95fe43a6dc6b62f3d3b39d9d84abc4e21badbf69aea5ac230384804c3ed0af81c8780da526da55bfd950b6b8e36554a07364999b4701e7fa711bd451ac53c4e1de8010ef7d2c56424bd11de74a56af02bd01317a85b94e5f360e5112ac2500c163aab8ec20d47ac3ef7f755479a96b71604afbed1ae76e9e09b244cefb9509c82ada2c9c5d5777a22c202a9377ad1cf101a1476e9992513c12366321de1fb9b780329fac0aa3c51ebab72f4eb2c9d5a4b21959c445dce49374ff8382d261103fbe24dd001793dbaa765e72b7d20d593e9815e85e9094b94fef5c7f835af572d055fa62248356f19c6bdf2f727be3ce5ccd9f297371ff9a1a18d46c779746c3150e65d90cb88521e30865e602e1edd97ef8aa735a2459c047af581ea64164423cd70faf3602fac4d2b0562f89b4909e34143275bad74f4d261fae702a2030aa3275af4bab4316837f228e98c0b04efb3c8dfe67081cff743e4053179881fb567a9591af0d169921ccc447ac77eaec3eb4ee99e44fa6102e3555ab24d97aac56d25d34c87c1ee37abeb6ee0933168d81d6ada10c84856f9eddd15baf4b29111772a71f9f129151d17b5ae77bb7f0324858c6a7b12924a497fed34905852261cea2b834a77eea7b4a47195634ec62e9548df4cd5cdee36381d95b959213601f0522b4f8eb1ceb66e4c7d473fa5c1d345b57688bc0a0d935d7241f1c28a069e22ac0621b9c01b6ba4d2978ad1ac368ff646a63cbfcc16e383aafc9621e22f57098d6e25134c90e250d68f4704e548c173ae3b21b26921b8712217900292dda6a9e5c02b7ee016d2b35fc604f70baf999e9a3c3fa93d4e2181d0e65a273f9f09aedac56feb291b4679e84299f4c06c6759b8586801354592731238913244c81b2ecfbb30897c4bb8854f958c8b98cdda971e328bf50fc0fddc11e43f6c8e5255c0f36926244a9205cb2c5b5c97454cd43922647e48505a5687c7205dce139bf7c53454237039ad37486e34dfa269ee9c24f608c1ee8a5510afc0a784e7af94dc80a8698685d8dee15cc612d144cd3ff711154ee70a83563b66eb64a14b127ef0ce8077d29eca70f3982822534290ea3592e5242f27bc8d425f5c8af1145278eaa111f03ab4a7f04128bd38385ea27e6d4606922b93e0eac6e5ef8c07da489198d372ebd570c0b97dff7736c724e96e6b7e496e96ab4d55ba921ac26ff8bb563345587caff0af7a9a58433558db0aaa4680be3f2708f79ed3d78afdd805517f9382876f0677332925558c0e6bf8b7939449dee3e447a7cb0daf98721cd625c7026b8f0802b04fd09b52b4110520e32a6c84d479acb0f0b571d4216245c311d1b5823255fcc9affad019f1cabc520dac89ee1701afe3d22c7d4af6519eba4d8a6d1867a8832b17072e702143fe9a3709d2d61bb828406b7a81fb5fc791d9d1bb36f71eba5c29eee17d50acbcbefc7f46dce4adb86519ae0d33ecb4e274ad259d73ccc9802395fd550ffb81c9e35dd97b9883331a0be88d9242518d417bfa81fca9b65db892646b35a415547ec09af411b8dd568ff1128e0eb37c24c677ffcca819e18e4095b531fb5ce0b228216067e30fa6915801a806c2c33060e05d21fda2b7e0159a04dfdf14bae5509f71b19cb6e08344a9f0308a22fdcbf6bea7de189a308d451ac52871afaa3b4734b51040653e7cfef2f28331ebcc361e6f6af1b486f39f5512b89bcaccc84aa54dabd977eed0398242632b872e43aca54881503fba875824fc098841cb07158da3224dbe2ef5189095d1e5dc16073606db98f5a17db0008682c55c6a206636fc0fa8268554d432d34a78a3e5ee15d46c24d90c24d925bb0a62fa8cd58db5c1fb50a530107438f7b99de7ff5f8f60543f800813ba7bf952d277f99e6ef724f1fb5ae0b134856a31aa0f1034b27f5a6eb62badc61346515310fe5a6cfaa71b9e3bed03eaf5e7830b678d245de013131aa7219313d75c20b9bde72494efaa85552382af938d0279ee1aa87797eadf581bd7e92431f90da5a18870ed0278d457e2014ebf204e35e2e60a5f8faa3ab73defece45c80a2bd14f6bc624586c6060808cd8717d6c97338ca65c393a8151e130dc7e005b52262e32d23268ccba10f003662a5746814c410cfffcf3b5cc124c7814545c99f02888c82bed7923243d5cddbfd5f228737b51e9ad9fe6a7179e20e22350b444c5be72517764b53d56b4b2f5fe40d040cbea3212da985a6f5edf808acc80ac8fa064cbafe9e30ba690b8112c2c21715fbcc85d52dd8e2bac597d3f2830a2b2764c09b68cb697af3742a22650e627011fb58e56073983bf47b7d9b637b40e2c3713ed603f78dfffc4d49dfd4227a364988b4f57ead845fed029d6915e759a25a2d79db08af8a553ac23bcea344b44af3b6115699316b7a619d4ecd415c437a5154fab3ca6de13a3903679617cae4b57786a7cb7fb7da079a0759d661119fed38351d1781a5eb154440c0ae5f218750d3d8114604e1c97e3e87fc15d8e3b3ca5b913b3e281caffad88016a2f5a1cb38821c2eb7136a5b06829a25a8e1287c23c677a4f9b9c34c12ac8a800ef2b4f1911872e90e62105d4e9f97ad4471fd202c0044455767cb5c3811757fe15a447817aff8fc0fcdb60f46f9a7c9c1388082eb412ab46a1f418a1d8b8294cdd138d39ddcb953d31303164fc7d371adb36f1bc36a521c0da5ab4ee5de48656e07697fdba0a862a2c41115059e874a27acb8b3d9b557bd4503d3a4ac759bce186dc1a44f8d11a90a1a94b22502eb944a4c0ecda333a8fb0fc75d166432a368f42d3640e0f119eafd898ec285d354d32061cac83b2f4981abc4dbcff38ed48c04f4fbd7dbd0f9d27bb70d37780fc6f90915df7b071bb7c07a8431d0c6cfe8f6571544453dcc1f665a6b11150e6872a13a48fd201e90e162bba6389a30edf970a98232710d9fd9d182380ab346fd2b38e7b0e9411ab9a71ba04412a353b14e4f0f2ec14a313281e78340987f3ba6227f230124c27e620e20ecf4b5022ce579852807a2eacece9bb24fa28cad162d05d07ea76086ab8512a4e4ffefe6d1a2632aa2e7699d64d23e19e455c163c892fa43a5708b778bc1ea82d9ce592300911ae0aa5e15f4b6170e24a808616038c50ee2c5682a9d70628f555ba41503d7046a0709777dc8c8537d9cca28b91868417176d493ee481c20f0d13673673d3fb179ecfa6475b360a7c985a6e2060cdf273c273dc9313c0fac319aeb5430f770b106df17e061e60952555d64caa9e206e21b8f482b8ad82290f1ad06c6bad7d07eac79543b69ed3d951289bbe51f41d9fb1b494805dda2a4d53294ca13b0f209c2f5f87e1d0f245db88024c63d777d4e76b8ca2c645ecaa3894ddbca33e7f42d1d2c932519357e63e04373bc01287857a2f61897db68fc4333d9c5b9c09bd46244d62f17ec07013361188a26f00ffa4ee91143584f5cc39f407a1eea88a886a2bc9d10aac5553ea8bbf68539a428839da65afce98c107e643a4e3e5be02204a9d7735a9715cd93d885a9ec71fbcc357604d444b5c5f7a6c73a5f74ee5d7c260fea1934d1f02b79b9c23547a0ff44474879e263b565d108d9eb735becdd4744a4a5274018f9fb5f5a5b05d680f46699d5cb078e2ae22f09ee763fa0e2e014636ce6579940b231768124b778432f72e2968805b3d726549404f5f88a84a443694bea6f406b4c3b72cb341a0a98177a09d90065c4e24f039980b56cd4ff28bf962a450e0b0f506655ed72fb3ce14065bf6cae925c8b6fea6b339bf5963452ea45192a2d6bd3788300d88b5114909a3b2d32176c274509440394fc0f02f2b414460048d9e1cb0badc2ae049e00f2542c0f38971a2eda721d5f9e08e216f0d829cd68d7c01d720209536bc279a98dff9c1ec16ce7bb29b7407d88cc5211037291b37aa2ec628190dfed98b81a051b7584c83407ed4791b514b33bdf5cda9d7b21182c55fd176eee826b61c4f50db1319ed308dbaf88146e9dc40ca9f1632fb96300ac7b523c11c294ba46e36033116ca7e068f1107da0eb005c81727cee235d1f808463c1b04023013c34f2de13fc588176f171ddafcf99c60c4d55100f53b3a20b5363947871dcb01bb25eb93cf25203eef18991e8d75efca90725d0727260b47d9ba10dd7bf06eebf0b6e1a61bea948e19a49284dd5e60d97c2ef3ef988981708c3e017cde1c28a74be3eaf7f340ea9c00a48098daa434199fc5602546601d499a70db315ae99a04832ebde01e426a1767709c48fa76be175ea4a98e7798e0f1414632b7ce5c1b3696f645450a9b607ad8e787d7c6e2d56907bc33eaf807448bf95bf9fe6c835dcff7bad4963f38c4ab267612e032215a2803cd0e47a1c6f38f0b3e9697632ac35a8406adbc206359ce3a70f0668cb35fe31507868034b408600b33b8c6a598e82be768727359b8139c99a34c5b10e3561d93d13f0beadbdad44df561acea2924d2306e256443550a8a9c868e1e4399dd39509b127ac608f008d52bb78e26a0098a9cb77b9e21a881e880920b444275e516cfa9919e65d3546a81c95da7e780d8c20674e01ec68c9a88c4f778866d22424233f53fa350570fd510e4c4ccba81b42c97bc04e94af3f98b2006cf9ae5fc55951e260b70835797f02103110f10b87effce9e06012e6f1949a0ff87e96871a1cb7239715a661d90d7d617ed5850fb84403fe185de1b4fc4f545ff09e5f20741fbe02330cc1d751d2636fa5b1620b5b22641ff509fc5b3ce3844b5522b96104539cbcc9fecb473cac4b60508f59d090e030ee4c3a3ab8fae9112174cc48c0205f045a524eedd0f31de47ee9eee6f05818c04a9b55bf4ece38a243faf3e474a60f334e20f9210bc32e75ef3175ae5ccab55610282841744df7ebdbfd25f9c5d3e45c8b17cb880b730ef0b19828875fb6dea9313afb0078e1ade9494c7feee1b521b39793d4424115b1585c7600ebd2228a9bd45a42e1ba4c1c76f0056afb64b95a7c199c925e634d5b0c4454783f6f497656f5e1ec89d80b7f3dfb2c6fb15036f8fbe01889fa8d821cbaa607afc79dee644e9b152bdf4c501da7342d09740d9976671ded8f102e6db2bdbad4137c6353d07c62928842f58a1ff1be61f5adb280cfd8a19aed81a96f9c3d54cb7f686b14f05dd1431c4bb279700f03bab229b696597640e3a49218edf6d8fbb811800b25310ad93c0b0f51b09e57876d77cfd13e2b00aca86291db63ba8443bbe49223a88be21a53e6612cd8152ee044803881a9dffb72344132152815d8773ab550e166384aef86ba5d085d71097d6e2dd68ecd6d6805c0e3bda86158390c097b0bf0099580000e4377e014609b921bca7d5f44660f47e0ba47308111a265533d889492dc077a1d8708d85d9dc65277766e0b7a6db019181630e56642978112ccbabc88316be830bb3aec64c32292e1706862bc84be8a78abeaf8b3d3b98ff02abfd7bb31792136e4e08190ba4288126e4a57e5b26b7e933233fdfa5661d3b677caa32deae9491a0e8a1b4f4884121fd40cc34659e511b5f1764ba3869c3a29c609ca95adb1fb9f77fc263a969fdfa6126fe218c91c1a73978c907032e9844bb52c20e8f807428f64258380106a057222a6a3a64b2c12303e3dbf0182eaed2a0924b21a13c6bfd6c18db74ed7490ba92640bb9edc9af0ed2b397ea201d6c384798fffdc78d92d2b633d07ae9e5ba4607e9b45bc9b57913a15dfe283f07096a842e0efaa808aa9ec6f1aec88ac65e84f6f2ed0fed8ced5d305fd784108ebea61214d74ee02d5e3c398902dbc65c8fb218d30709b3278f525c54ba012baefcbe7434353dd98834f71cd45ded711829e28aab2166644f7c6a457181065f9971722287e5c75adc96cae31819a48556b2a176434fa137a9cd53334c0181d66d079982759bee99ebddc4dba39e4f35b84826aaa424458c57c28444350053fc1c9e426fb735868b1974ccf4f6518ea6a272df105882475abcde4de227de0229ed1e4d020a8505a3fa8e21748e26342ffb4071a1c8649456a8ce9406344f99e56a3d110eb80b32e2ba5c26e0fe3d016b70a604239e59f7a9952ea5ed40270e07d648fbee50a4e7104ba17439930510bbf83236dceb5190eebc0a6b79eb99eb8390d7aba7580d3b7c433e94bd0077df3572cc060be2318153d0bf71f9fae3e4b7701e3eb1a52cd0edc13b7cf8a0961afd2c4dbc0f5cfc4b975cdc707f081cf265a710c1b68c6a9f07305af9cc9f5b1263235a34e586bb1a7f31299cdfaa83b0fce234fb8c82049fdc71d78787dfc9dd487052e46b6dfd7690d9bce247e6c8fad0834ee7b0df15220d5bffd2ba6082ba8f6ab1ab498f5cb37b06b082113b79d249cc81d1e9fe7de075565f9aa27341c0216717ccb17c3639b9109c95d72b4343917b53977f2b0baab1596a1b526db8c3986441173ae27b1f15e64562b7ea17e058efeff412cc6160e8357e785e371f17812954350cd1739079121934cca83910520f69df3ca739530cfa36e10c8c4880bbb55bacba515291684338c805df9c117334942e0e3eb671f219ee22f1ef651e12dc8c2451d6401a210f30ede7f15e37940e5a21a67f5774a05980f8ed32b4face018d9e3dfc03a4b596d89bf5524e25cb686f06b25e835c01be43f4dfeb0e0841d2eb072accc26122ad115465e02e9cfc86562e957f750631bae26eaad962b55c2678caa5c6deb81f3bb12c574210f1acdff73cecfdde0a39db03c058116629a4558fa4992a0eb88f02002fae04244ba9bf17cc08906134649b194018f17bc95ee4c772fd1325e31c0b3f318e7525b19191c3ba9a9aa9e6bc80ed27b51805645953b1ff3c674b110f658dbbd3d8a97ce1514867b6ab20ad8625b70d342955023018171b013478d0b610a306399c4f7989d56bb7482a3a28bd3f3b55516c2002065de59a8bed1b10eaaf4abc0176b478c1e576734549fe185457589c57a2d39f3a7ba4e9e66ab32532eef82e5bf80f9585cca5422888dad240266f938e4a49888b24446e7cf42275e72ddbca377e0a102d6120136e6752f6b2bf2c3c5312f47fb2e678b3081a90bdee9060cfce12bc70fec16ed7526ec339f2716ccc827c42b4705ba1e4156db5a9051128d47c3ab73c7d5ed2b8d902a4885a49c395f8db9f5529c05be93311120181fddba14126a03d9ba12e8a8389956687d13a8b95b49e297d02cae01c4a6acbbbe1d59baa26ba0e5371d2b53ea0a600e6add41b6f64fd91d816fde115aa715019d105536ce78981adb49f0c8dad9223229a5144acbb4aad089e1220af4f33a5aad647d54127154d53b5c0f922453e6ee4b1a3d229953209ae6152e10164a2c5c08d965d8d327cda24fe5fb2a6235746881ad15a164cbb8de1730b454fcb3ec5079194cf694d224da933857978617a45a309da26f7999e06ffbdc25308918291d8c48063833250cbb5483d74f8cc1342d372371168954b51a471512b32ff504f260d21d83605e43585723bab69333341799eacf37e9f2878cf3b5c6054547164a75ebe6378672e6432cfc7e8b34419cc72ddce61bc2e5f3761bbdabc5b34c0f7ce10d258decbc2c42bef96ecc016020bafab552d94a2fd9840dcc1bda9f20e37b803a6eb15bb376d59f1d6ca8bfe3332d6ee7f28616c22d92e534d9aab2568cdfa8a56709b1f71676623bd5c2bb670f743b637a8ce599e8046aaf6363e55f2236a82b8761f0b951d19828ea23694dcc9e9c660c0f1494f833ee9ebb82a1bb34a32d5950b645fd7b10a28e3fc5f3bf2f507020fd1cf1620926ac82bd2d856d6a8838215ddfaa0cd08a7f5b51018bc7673fe750ac7ad6416df27164412eb7e10c0dea6f71092f6d2fcbf6e5431790a4f5b336d5f25c1bc529ce9e1b7ae8660a8fcdc927d4de21c4d759209d97838e7c0372b64f901820f1ae18982123fe49cb6fd90ed865055b663e02c60b20f7cc38131fc0ccaa109c9eb515b10955896a9541864205a0219b02d4b7c5792a772f2a4402aa4d3b2aaf1025d8d091ab12cee3fc6821b1946b5340a88387b71479ca5528f14b03064293a68c8c87084851dd8bf65c0b976827cf2c38984907bfc55f2cd93cdcd58b8c564a2b32dc522525551adba2bda8ec3aac2e7119a1879035f35eeb4a0c05b2d018b0d36dcc16d9707e6ab93e6e82dc4576acfddc2068850eb415015b6374434856bb9a30dcdd0691ecb6fee349667050262f9975cd718537b2abe0311d7ab25bd954a377145712e95f6800da866b61fbd3d049ab99455bf83ccd9207de6200d5f5c2cf7cc1cc8136a9d9f994830c0fbb86671155ca451079867237519bed9c5aa9718aed1c6e794373ef68d33b5259a49c3d96a5458bb0e9ce5a0cb98c204f06e8367cd7420a7c902056ed0f0ec7b93307e4e927e754bbcb84365fb24f953c0200b6020a9c2ea9a2f987c6c9e80f0da56461c6019e4c4fec01d6a9a4f70e3832baf290c5d80dddcc0a102ccc3005499f81e5135abeaaff2a40671458e15e3a81196d8f578e24e26f28121e758d6146e393cc4dd3c43be01d4b62f5629c947e6a70c0da0a45ff9163dbadc03057105b46abad11db7c0988958037bd437fb91fa61abe08998862f1dafed53db7ffc9ef13ae2724224d337bbca48ab817ba5f920a3a03418f7c8e60acc7d1bdb94f6e44d747bd0ec56a8614043a6ed30800c914807e3020173afa580968a09762791d0b90230628e02af5314d1741091b0fe17b04e9b6a6d7c62db661bf55264c75eb92891c4d9f51adf5c0fe47777b3fb90d291dd490c059edce9d20161f1f2448d124ec98bf53cfcc9854adbb5521e6cdf0c51835a5a931288da758269fd15b657c0b31697c24f2c286141564e735eded7d8615af2893c7a345a0422f2481e44552b156ab793c9e867d718990ea056418047d7e868a435462c2857a2e7b6149d1e46d834e1898e6fb8fd483962981b1226f213acaf20d758c59f171dbf74661c324de5ca4fce748972ee7147ec0938592359afde74b3be78231001fa065563b911b73604b939f8b1a3ff43b65fa417aecc1012ae5f511fd845bad91bd6b49472fe2ee314870046adfc2e79e9ed6a98ef553d2f83ca8a0b8796ca741a2a4e441897e2116c8fe84907ce9c4cb52d9e60c45330969cb1fb9f3510064b425605313a35362e7f9f5e1e466b5d96669a815f158f14294fc634344984e60787346eca1df173c00383de39c6a7538998a20370176f5a82b47a24632e4cb4e1598fe03ec3232ec5a9740546c07638da69f4783b031c11ca84673091ae0771aa031bb5d962d1091d663ca678a5a6b4d80e22189fe736825a5e01dd24c6eb4aaadb608b14ec03425603e33ef798a2c50b813fe3a87a1129140e1c0df6b0b61af1e2a5f52fae3c1effd67a013b3bdfd0212bcf07fae45e180ecc626bd0fc22acb84d6d2ccd9ad90f7c57d247593bfb440b184999990cbfb5b9763e2cc6099373088e67b8b8577a8f89f3d703b9319ff714f62342b2993ec81db60cb5635addb088249fe192ea2954b00ed0ac4494050aeb88cc196109bb87c4220e241752a8d26f97365a8792cda735bc3f6081cd56ca9a561f8a28c5a876aeb1729934f19f71377ee17fb83d2e33a99e27123c6efc85d04d1545889452b3fcbddb1c66c15bd3916cd730cd6d90a5cde97b8506a17aace9e35f9ad84027436ee0e03ae02025e800ba5864ae68ff4c8d2e9c31d0667e7b290946bdac6e9b7c248b5f4450a4b3db6c4cfee4d8fda1d9ca783eaa9c6bca600aee7d930adcd5c0711244f078374bda6ea47b4fbfa81845e5536429141201c1c605ee75c1545a48074316ca384e6938313f7e4a4bee086e472e36a0349eb2ead70005bb8489fddb5709174ccd05e428c5b31292a6293be6aa4659e0f81ab6eaebe63581f25c610e773ac266f95d617034c654ad7e60ade9d793cdde2c6c7150b2d6044bcad73cda8a473520bd041a3be83b4343aa028b3b02c0bca7905725a15b9e379210f778c3ce3203804ab09414030f782e91821129b333541e2674b2b8e55c8ef72d9c806798c77b44a050e6b64236a55eb8c0b63318000df409f9daaba074a753eb56df52c79f5231b0dc5d738ca0f19cd92a39e128ba787a29e83b50de15b6ab509b817b442abb7668e5f4336c91567139292e7ff0b60e5d8a7c376a3743b8f9466de73c48ac332a5c11d7cdc005a0532804ed5a84593dddfce520f41b1589312535e557b3d1081f2d922d206727b3c8955c2909e0feb0acdb39845368d770b6a2b5c8597d11feae810fc01aefba41c7b7fb4744fa00ae557cceb96878b0d646895a82afe8d63c140694a901cd969f95b7e856e96b8d6f6317b97a99716aa26faebecb7f04b4505e51c34bf34899df1a90ecb8c33c05f8563d2069c5633d21d0462bb072782378d18a8485a74f27ce5befa570688417c24248892d353bf46d8c5e93be7774228d5829f4e93b6fe1893ee07071e0ae50db5dee2940d1faf4f872624aaed2f29d9e2453aa045918a0ce7a0168b14aa370ff5a3c9bb7dea8437a2bed39e410e9310a503eaeac5fc6ce3e84badb64b50bd0afb6039669d71292a7bb682ba47aed37ec16949efaa58ee604c3e31b74f3564049fb3ca50e10d40297c77d5a08d37c7df62e7b5a5249811c9c1e74f824a4073121c5961840324c33be30dfaf3682fc6f43a68174379209af03846e64c2a1c62f5fb21eda84837c50284fd389e0c42dae4f6d69919e8e4960e2428cc2da2a22bc066f496753e72a134d88f8ab46e37edbd278d228bbcb3f4bb388549de2ef746b1c6a7c3e5290a49f96d85608a583996b25e60a9284dcc0e48078394316349cf2e347e2e084e7926366855c97286514614612da0a1b337f5a050010a19b855852f32064f071624a03a29cb5575769817d588477584663dda2047b6999e28c8de5a6224a64021d96f5bd9a502742834af2047d164fa09927d1c1cf551fe6070285a9be888999fbc5a15a5dd105f09cd30250f90dfcc8bc4a6ae36cec8141052d16c616bf0d58face3455172ae43bd003b079e05aea0f1d90454566134ebaf2cf69153e0ca38e0f28c8a7d4e99f4ae5570747bede8b02c8319d5eaa3a8e947a5a5c31b989f17c8caefb2ff331eecb6b90a18b00a872437957a8472802a90b2800dd957314d565b85f62b449f8f67a7082d96a3e46ee70bae9f41b909662178573083075de851a869256da034f1cff29ce605ce9aec7b6e3065fcb3c93363cf20f6bb354e6e06fcfb0fb764f3e68b70d1074b203cde63b6e5a50ef153ec79f29e6d40c80686fbba6ab185553bd743b1661876ab650e60d76665d159e3209dd9106646b2dcae1e490176255ae5a031e6fe087f628b9b9cfae5710aee80adbd831b3a641e0b20f7b0f9b9033bedb16d807aefa38f5ce28fed307bccc071def3663e758b07920988f832f3b82bdb4fb0ae7631ec2e6771aee5d77611d591fc5cd541836e20a1268f1b648935c949b1305c1f7a8a839c143e882e7064949b2805af97db067b607b14fc37f389b56ce9e3402dbac35bb5d0e0ebdb3ed0631fccdbfdee3dcc9be4f2b2a3ea7f84dba22e6f647c60509ee7104c456b83435bfd8a889b4451117f9e70c4826103ec542c142e4d5a02d781e7c381530b4e1008af96913c0219771381c09aad1830f7833fa1a08b1551fc57b954fd3c68e6e0b72e083090a5298d6a255f3b6749397ef2ca95893c1eadce08cb13407bbfbecda46b873f6a1700cedec08f830dd031c9b03cf0d46789c33e78e878373d200e3cb883f4a1ac244fe174e2bb7044a8cf94158600987bac4c88c151327cb224ae0cb05e2264344883a1deb1888c94256f27acf9a07246d1c4246c67ecc60e41d03e4c41cb1f33e38a39c33f163481d9bbdec34268f2057968404ab42ce94b0176074a9357226d8f396deb1510ad776e2d970a682796749b7b0b74ed66cec0e4feb22a569cdf4d8aec99fa8936b985203c3574c0f4d0eee041f25b6ea0c6d3c591a0b81b1e937dc6e62c67bc7ae9a627c37dc1f80b65657eb03b1013cf1bb6a817caf7ba277c371713556fca618e596aaf2751044416e999a9df65ff6c1768ab51176b3c50ba8a7d1030da56ee8d53f820fb276d5fcb3e014cca7aedbd66ad5d7a5b88179938381cb7fe4259de7acd7117ece63a3e25f76445f661ed03a8fafb7408b98c43f3675854c2ad9477de2ba7fa437b3d382a3a8262569ecec1692712e39b9dad67cdfaf533ac64fe316fc86151462628beb9c32f902fd32df6062460c80f7ea4ec3135f8636722a802fa5ef13416b9f6e04714b2230cfa2fe37a1105decc2ccbf1a9e495aed3614235e7daea635383198b27bf1631a4ae3d151015c1512704655d3cf97307a4ac612c56b0262161a908d2fab7683d2355629727adb25d8b52e76fca6e684c2c14ff3117902848c1b42e9dc23fd708c5b928819c030525ed45f1a3b7623b1d387c95c3e5af1940775bfadc8ce2b533c189b83514f5a0650402a95ed2a2a2ca5d6b3d46f10fdfaf0b407a79c2809b8b81041d7d4964251fc2e5f4fc1af34e66caf82f4864d2f61254468185f0300530132d0e003080af5caf801e6e4715d80419a3a486a19a817cf0dfd7af4d80407234841f5dc2db86b3377e73876b1b33eef2145f92fa3dc343eefe02b2984d10ce07069a7e503febcff834f45f7a31d3ca66f7779ecd43ebb4c7c6be4b12f94ae1bb593a17dff9c276e05e9e60c0b2c04d28abbca44e53b5d0e6949167a77283155a20929876e266db6e17f8282f9bbf84cef9b8dea88c38af58d919892617044bba51d20a9534a5d251f2dfbce27d62034e040b0212480d1a61d2a2d6ea7c8da9c028c8da6c893f6c6ef96d1d72653527a998766a11bc20fbc7042a3afaad81e8cfe572699defcf83f0247918ad4da2c1cf702930e2c22c9da491517c28f19f7cf50d6cd6129da58c3f7ea649e75558124c405ed956abdfc4892c22bd78c2a3da204782f878c931317df8a6d53cf1cc36c609116b82b1c299cfa3db033c63c69be7e3cbdcdd415c0bac43b6c49c8f159fa7008013e62200f0ee5cc74faef7e91ebb4cf6b8711fcd39a19119b16130bf0cdcb5867031b18b65adf1a26b99383c7f9120e2350e58d68a0c7828ea205de67aeaf9376d9833d1298e7c6487c2dfcbf42a53aa0a0f99ed410fe26f3e4e5d24fadeaef8b4b80e9428c06f2047845201ed7cf6db84fc93d9a294a9587a8129d60b354d84e400835e7353c2899b029240e0afadd8c9a05bf2677f492f50e78d493e6e97b9c54042b418bb54c293c5492d5e050f62197292d1dac08ccb4e773fc4bf11775e815f8524ec7c7535c87dacbb5b27a4ac24684863b04519691cc915594b038a1b341d8330658d7102a224db2d5ecf49413593855105fb89fc635e447e848a58134c0e36a5412ad69d5957f1bc5ceb2d1edb34ac2df0c3430e79ad9fbae694bd1a6ee41bb4510a57531e672401c082a40b995a33bbc0282cbe27b49abce90c059b7c5be86ea96e2c048e6f16e8bee66361db8a0e21e8225c025d08cd3416e4adfb0492f5089ce506c21853efbac077e316c11c894d09fd06ed7311011311bf5650c282801c6c711030de0fa0c42b683c627da4b650e36224394253e457de767572953bf919f23b904b941e238364c737fe0ecb886babdc69680cdc59529cd0a94ebe4e0b77c9364b3ba54b6f5f508c1b3e4836f07dcb985c0f4b679d937c219c53f800af589918b77d7e896da010e8efde020d0c84fcd85537a7682d3925c81e15bb21403c48cf6389b4422ef9eca2ffdf2b44c0c6c8fb6222506922cb5d14a2eebc67ce634550332e231bf29e35b2fb14209edd16c738821ab91dc682534df8a6815ba338f35b2813c85d2e58387b16be1240a174b0e19223a6a93c1e494a2f33f8a604930811f4b20b9de6d86f928f3c2afe6366d250ef27dfc371fdb7e321c852fdfb0bb6e0e3d754bc2006c0cf9f5ad44a75199068d3aacd6610979ad0d2336acbbc9671dc484b4d53cc0bb0f986e54d3a13760696f2ae2b0e4dc92a52570f2c014d2a4982871474ec4fd1ded035da6ed4369213b63a5861a3df30854679b2c0b96e2b0c4ef1aafd85fcd94c47c5d583c408ac392e735c04509514ee2ee4ca3b290d8879926f20294e2b0c495d577e63c1e822f743c6c4bd2d385662f5a1a872555821ef24844cf4cd84ec0f3d5b66e5d46ba814cf464155b054e0f0fa8f1b1d11780685c6edab535f11ccfbd1eb58e71b9455f8891f215d282b200ff4fe15ef658b1d57f365e3e905b9fecd086b1407e7735507f9c3309b943e1964b5d1ba3ccf0304a3236615c6918e7c9c6cd088dff0076bd07b8cb05da31bb8e7d53be1a3a2f35149d32a3a3b7afd4ef417ef850de8c7cdd2d6f5eb4aad603ecfb00716e9dbd15a52a43066ec7d129ecdb834e23ebefe89dea48fff861303f7b6ff2f2e465cae8b104f037df9fa4043f5562d95cea51484b8944dcff3c728930c801f69856713ecb365d0701ad76051e8204cafcb50add00f9e0d6d0c76a1c3b03b55192d3f9759391748d4f4c136463da8c2734e17422eea51234e4d213c18d3de89db53387a3fcbb3852218f462fe48ec2ff32e8b572659a52b1e7d6e50d967b6aef649f2409a06fc7c05a015326a9aebac06fe839a5ab9e9bc14928910cc8b6379eb354a78db5ed94443c163dd9c3def17870311666446c15a89292e92f04d25bf78ab9503b7df02688a6e902c75b51ea03248c0675ea4ec5ffe5c63b5f6e8ec4f6c21daec248349455f26224d5a6282260d6fbc5249cdcbfd1f902d1aa39b5ed9932375ccb9ccbac28b12c13edfb67949da00f50cdda7d3f0f04804234762547d20c40a0bbd9ba62a49d822048d279ce5f85705da7b85fa24e30153dca19a90cbacb06d9c5c41a633bd65e008fbc6e25d161cd6916195e719146f285b42c313c2964135e064c03a0a8000c50e0c0b638b752aa689eba4aaed8a2fe24493da19dc35a1fd6d7a19e17c82836b2421d4133646315b017b567c9a618dcd4282345032b1649e5cf0c451267909f03b62e278ded470cd43dcda5649625fc33d1c42f0259a30c0ebe838e9a0f4b9d128da068734e25ab3dc09ca0a4cfd6bd9e48b821755c4f5738c96f20e6984e56420778cd13252cb973c74426720c19bc4334d8aae5a5dce625f06d722e67a93dd6f6cd486a2babbde889e32beefcc33fb690b57501d0134e68342e63738ecb3e0de7fb46e5adffccf84b9f7981a9d3de575a9f8bca96517041a8d937c6e5b96ec1dfd634110a5cda48e498b5a78ecb61a2a4464a22719cc9c4ab02298c4a60e5c9eba5917b757d30032a7b6418b6a56045f8cda0d251a0b47912064127e3b74e99c57893359f819708763f1ac698be9f7ea45986311a59999a4b497f00d2658a305133581c99238c7247b0251ab4c5a046ef8b9130feff03cef07fe6e82c3c4d64e126bc5c71422c916da7896f2f9a8877406a62c4089e07987980fdc159f624d6ec396893d000039a4ce7f0594c42cf39cde13b572f6a64f551a91c7088319c74b9d28a7505be9fbcf28d55880bc4377ba34885e1135cfece59396ef3cbe442d99b7992472e89c3ae619048969593ad102b859885742928261329f617c1f9a59d8a8a1a3249130a1c3c94a0e1be8b8bbc80a55eb314c6b8fca99805ac2827e3958853097016010037e731a27d969d4837977109a7aee5acd298305be2a57e78db7b98fd29c050866f8351db498070400d1f75fea1216b6bd85b61061f9956886eb4a1f404cb22a728127a4c8d78e69011c66e44270029af75cb14708ac70e75915bb8d238afeebc268f241a1989cb343d1d181805233b706b0f383b2222890fefbc80334261324cdf22ea9c63f8b659126fb8553c31311138b1a6e959fe5acf600567ee4369883721b4e80d90af4cdc1dd293f938b98c6009481c0ccc1f4843120227527dd00b7f1c7df930203b463f25034bbe035a96d79b1f2908fd5f6bf366aea321e97ce642e5a025ce8d77b39ade9a39a46a5e12b2f3c44bbc5b5f6297f1a3c2a4272a739f2229f9e2fe84fe2609e8e1ab97091e71ff21f3e3856515d0888795425f269650342ea3df80c67d2c678a5be24ce30392c64dce50c71c9e0f49145b7e6033af2c0820fdca07b04d53d90fe235efb6c054237fe620b67652f218b2f9a2dfc0fe4ac199b65bffbf11583d46286474a4d726cb3d6f3efa0327d054a3cebbe3725a4654386faaa0536a36163d6b0b74b0c215bc9150be753600387e664456ff17436986452f4469fd03aa6124e4abe7b974bad040a0f8b35a8c88da98e475cf4832bf71f648eaf68bb88a6029c373e08bdf043452a5abfcc9c37e82468eda73d7e9fb5aa18957b7189bf84b1dfd400f94390cab8e1a77b938205393464481b88c54e28865b0ed0e6c75033fd6097748c8b38287c801541c4c29192d416e39e5208d2da8a00538bc9a654685da6799d022bade6a866d74bd41cd0de98f4a8da8e8002c8421dd05da08113b28615b68168530a79799453721c7065b4f8d49560e908373d5e7f34e112d3387323700f5bf8807b5e57ecaaad47f663fa44e849e9a8862c9757865791c97ab6662a1d6c248cc70389a2b351cf3d149c71a6ea3b2a635c6a3ddb243b4bb2622b75ad1fbf117d4ee21e07db3e6f37c1d61a237dca4dc2396774ef69aefdbe002175ff86161430de034c802c9aa86b227e8a6ba3a96a0ec4c906ef5c5c576fce083219d8e765a212c2adeb74bc79a8bdd59ac5b55e5894d8b5658aa4d061a675ad201cd13caca7c4b27f24c5a245599f855734d2b272251f3888d26184b5c127d3d356a0d763595ac100b081a834fe4ec20b849d59ba6f5961fded27b30a15cc4a022251258da5df25954df1027b80eba515e1990bfea6a4ee228ed4dcb69c39ca47111913cfcd99aae6df8784b88b6598343dae2b7a826ce98f71f90a796a382eae88f16d46f162a23eac9d423e1e2b16151d0b05ffd731c601f1af9ffd3bfc064f98a32e35efc6e3fad756ce46dec4899f66c090d4139c43f66a370e61183f6ee0feee0a0b2613a2fedf7cd65badeac6d23cd9c27f4ded8940141104f81e4f422f6025aca5f024a3d016170a2c325366b7117fa1ce0af85f53c7002072d22c37c92f708f1bc32de98723bfec26ed8fa328ed8f49e4fe939121b779304ddb430370182c24df4be4dfecef7a35893a2054b1b1ff78b8919ff6250a23ce1e1530ce1b88e9b01e019c7ad07a64ecb758fdc1c5cd906c78bdd7b10de453839c0c8281c135c14be3a1af4ad904822a1af63b0d162268317b29760811a099f066dbf85b9b2f262ba5e922614ea63c68c9287c3bdf05f4352f3207c73193da6f22c63500659dd313cbb017f6a421ec38f1a67a2625366109de50c746d0b825e3d67696405dc6f1c31630e00ef86bd9f7b13440e02ae2c49da91f17a28eb1502b5dcb3bfae9e7e9800594f01e615f6d50f15693a41fbead2bf4500ead0c352ea46b9dcd17d512223393f9c4e7da660b27ac8013da763dcde938515efbdfa6839a8e149de287e3ec1526b944f80f3b1970ad68acdcac4f595dbce3b4edecd83e2fc3c897f09bf09f73304fa56473b398e99cc7d7420a03b83c39d262e9ce6f45574904c16385f46e3c1c742307e6f8d1842f1d4586433442a11ef90ce4fb3e4e8c222981c752502913f42639a15dde86c9b99bcfab25a13eb2f72526a183d6b7fed40a0098156aade6e87e89aef13ac03140b2a7536a76571c51715e97c70682e92ca9c1490c4f1b27f7925e0b8af745294fbbdec33ce750e3352f16e8ab56211a3b44e3127b2080b0fd89418dc9cbd525ba5ce5002d213def2782a9c5e9653568efaec602b0c6ed9b6ed2dc4076a241486782113e376037b1d205fa9244ec1a9d71fa1815d1a0b548916ef3d8979beeb1e3cefedab402ed67db1279e0668c7095452b9be3246de5dbee1d0357d7dea7e0b9534a67b939cf8f75825b1ba3f834ab3b1a82473c05139160e4dd69b2616f82927930358f7aff61d081b7cc704f50d62189fb6a0150ada8c41922bec42c121033358f4a7aa040768d3e6ab92c42685e6a50a5bb7089e59585651ae43ab0110db310cfa9524d90ea7195c6e8e3480a2279a26572278b1092a74c0bb279fe8bddf17409e714b7b2297e865700c1d644cfa4b7c7ca200f60912827a4953a88046f7c0f1a9fd7ce80d7f20ab17194f3c8a670085e1d189e8152e151b04d4439bec81261c017ad6064678055089a8b0176f237730dd17bde55a1bdc35a96668abe78b6d2ac2912de5f063b17e49dbf34f0c178ed81cba9f986914b46ccd5023c70db9205d8e07fc4ccb31be44ac88141a5d0bb89f9e2237f9806424cc23e2d4487dc9c99dd8102b0211b6221e20ee7227122e74c64181f2c677a198b908e61bc29368d100c97c95c71e8b6da2e2fcf45d1a83dfab1647d2f2d7682f21f65700ba6f5078a938ea7f5cbf7ee45d1c795e3ccd94923df3e31a236ae7fbfb4435e275c8434a6a1af10e4bfb63bedb3826a48adb21586ce9d3728a743a11511334fa1f61eaf431cc4ba2896e57105244f508eed7b49cb34a2aabbc0bd6283f23dcdc36a28cd28e526593e99c322392cf00a3f1e21d779f10217865864713e08334d9ddd34c5cd1a5055fa64379b72127b79ba5a3f60e9b2516dc26f1382f34a96d3ba742ef04082b0043799498e07de278fbf19451dfcb378657d1c6df4fa6e74fb9157ad287ebbc4f52593cce3bc61fbe648860381f35fa3d92de437d48993fecbfe8f507f3fbd017605aa0c5673facf249bc6ad5b0daeaf8cbf7ff6ab16ce66720a2a44f7db5421b5d7f6bf8449bd8adce853205d3896594be02a5fbc2cbdb50d7315b11c7df3f9d1709c3e457415461bf1f9816c6b5c1c83fc76ac010f9b741e8bf0301d2160bd59234e8cb2b737d4087de22a55d80f04860f9c2a110312d1b3028f66f380df4e684c6c8280511065accb3d4e215ec2542fc632f4f08aab82d2a8eef7ff78967421b0ec19c90d2937d6e209d0318414e33f4890c40fdcffb9a27094bcfa35ebdf607ab9342678279b21fca4f7f4355cbe1ff4de91bc500f80e0807fc4466e1e6f258e8a47727f4574fa982b29ec616a43b1b97957b12d14db8c742af0340c4ecc82ac69eab2ce8fe6858b8fd5d073f66a994050a4f7110188698d355119454f8c79460709565976014370b25acd8a9f11714d01a50ab805886384ca16022065479c915c6d7eb816e6af7b99618cefb716e2d9889489eb6e2f28afdcc14ae8508d922b5b6bd1464b59b1057836d9585ced5ef2183e3a0cf0195f9c9551b8017d368bd8a2f1d1f82c9cd35cc90668cd1dd7317d066946c475bc1d51eaae65066194550000ad372baa2b880625fc2f426f036923b86c113b5bb2a256a447ea77a7aa2991536c423a330d0c758ab36c7bf2bba71d2cc43a76c04f5bb5c5e3dedd7c12f1e73231018177503eb45c9cc38c563bbe40ca8416900503c79d53d8d82f44490af414c849d9349ae42a8126cb2d040a8cb08b703696cb083409d81bc404ab19f260057c33b30d35368e61415f0f6f09aeb26e84b73351841f1d0c52bae47955dbca4a85f8ff98c2fb74ed08220bae0384081352a59298355a2f6f61b0a232048b03e0d267f0260941e6a283661f3b8585371c267023afe8fdbbdfb1293368f101ddb17a0ca7b9fac82183608a7652e39bcb8b59522ba6df813740037c639df81c5479ec8ae65f6510b1792b8d45cc3a5b168042a0e05493aae69b82ce89521e666a653f8bf8402bf2e7595bb4d1daccd216726184138710483e40eb8e74408752c79d08f17ba089640b98669c9d509c4613a52a8fe3c81b4245b19170a2bd0558fc734589df664ae62e2b1596593871d1c14539225d7e0a12b63e04ac4811dcecf95156a4a5766cb0a9e20833607c785f30ffd9ed931b432669948470117172929ee0f073b1e8bf4cde4daf4d8b4a45e26dd9c2410aeba9b08ca3d38268bfc9a450d04c127dd347d8c7a92b7e16be785000de369ba0911796193122bc74184f62922e679e3e89b83a62fa891c5f10cb9f1506d8c18c94a09a3acf0d713cc336c855eb46409fec463d0f9126a101a419deeda53bc8dfb415885541874bb16a7446361d62cd639e8232bd92d61c60356925c96f41f103048e9cf79aa5b9d11e93fbccef538f56e49867efb5c4f751d6b173b6e210c8039ccadf8a9c47c5d8f36b5c64f2f467428a833f87b0c4cd83775de45b87d7f937178c577b17b2fb573c07c9ff5527f3456030a3026992d096cd88a5700e7a1c42a6699ea13c6a92460244bda9178310f6656b1c69c656415752e1ea1a842b4046a649384fc6d6000db4a3b011273dcf5ec7b7f316c9271ea843d7a3961a439064703815f7e3a401f269ed8ddb1bab90157650542c02436c3271093cf3af5127a3b5520648f64d60b30521b8c077c7f3cdabf8ae3c91c0e8a07c620ec90410f5d2b60da632c39e608ccfcb1d1603c0037f3783c44664dc91d0f9a6504e8d2ae6be1c4dd3df7d78740d9638dd59d2a3ea61eadb219774f630d169ba402dd026127fa16121664ee0fe6b6d807e0f857123a913023514a5e700f4ae4387f7c70c3a6c1784a459729de554a7011004c4e89a7be259248c02c3b80e2b14ebc00ae4472538c0fee7e3da913fa73cd6522498e31658825cc448a2267550e76a23efb86f1f21dc6655b1fc26ef18bcb2f3cb4c5d2dded5f5fbc20cd9db7383cc71318da43dc9f30b72f7ea6ea8546e05db1e28683d72c8798f2bb0875d02f35a2d303b26c72a97a7ab5561a6eab92025e85a4b80cfcd5c0c00fa4b831fe12f4da8d874ed434a84bb3d1fe92711cc79b2bf4c4b23aee08faf239f18ddda7bd8cd9b173751d5e9586686ae82b0cea682ee7b9f5d74a03c2011e0d9e0e2af53a6d4a6d0abb692de70ee7be39557710e1c9dc4f657b1d9769103f242eab355c59089fd573220870e0300ee91ae4437f47d8c4b6014419c687746514d82a83a6fb0b0cff1bbcdfdedda33d2ced1f228a6514b400956be971c17aa0ef1bd749a51f00fe0e6f24b0e888c91c4567c9168adbb628f63ac335d180462e6992a8040d6d60ad8c57bfce2d6248f8ed60828614d817762b4f4f62d553ceaaa17b1a266adc7fff0d4d412eacb739c16e68f508d9000f2a56339a5bd89eb16ea0815ae93347cdd09c327ab602af4f1f94a5b411ee133d31e2ff149afb6bc3081f2743beb1b8392510d4510a45b9dee80cfbb6fd3c2ab8a57fede921bc3eabbd83737ca0086c343cbe23eb6f8e499e4022e5f50a860eb377f82557f11bd7367123abfa96873e8e8bdea8f56ce37e15730c63e7435637d2c03a7d553f32583a4841cc8478ad7486bd48d0906c0c330d09277c94c4927e89e185e8e067cc68c205d44914570af084397c4f14972ece02c3aa821cb3c778f9327523e5a445f996593bf8036f1f09f8936969ace749d4d2944b3812e0a7a81d33bb5406699345c03afe500ed4edc87b385cd5bd2f9902774cace66c25234e96da38cdd40c0daecdeb08ebd2f51f0fc56e8ac562c20e459e8a7838be336020101fd9ad9c4e7d49456fa09c39f412d60d7cec3424c115049c76748ba7793e54431b0b131883c3d6160f3440a2d40129cb1d9e1668d4c1eb66729c822d9ffbb36a11c1d9a560066f9ed1bd964970103e1c3246b48291ed6b6d8ddc8450f4ba632b6b284730b465d338bf927eae182a974377ea4103f40f29b31725d896d54ba2aeb4a93be942626680b3e9be88845c4b9fbeebf6aae8d12dda9aa1fab58dcbb6fa2f24f0d1cfbc20c10b26b4900134b4ab4c18ff0e3a3528e8ba697448cb07991ce47f93fb1f1c2c56f846a7715df0f88808b778875b826d6e67b4ee2077f452b67db801439d976ead1b46163df770e18377974d0412263d9c7f59ea91c1a10a09d2d35a026261a06970d0f5e62863df06cd5a9d2601e340f7023db77a90b782ea2d18971585e5a3acf249c8f02374100498f6bd402217e959e58a3fbd3f9df3962eb93a818e8a3b1968c2e90c1240a1ac2f0a7f4b100a4192e4d6a9c789a70138ca587d01397371dd7218588cbd8eddb8e13fb1fd7793e1a611f2a53fb1c97d74a5f7f0e26f2311f958c6df2421eb89f9673d3ee301d470edbb702ee77e9720888f7b3f0b282e70d4aae5a4098b4e4f8ff906167f8964455a4a2119659ce3747a4d6fb86d06a7bbc3fa5790251929220d01e1c26a240ead266d6a761b6eb574f306dff937dfb72b799e0dc07ba72a7219e5c228fc1cce4ebaf13a0d231b6f8b38cb091ae0b40863f4164804cfa31637acef0c491aaa1e0568cf91f967f1041f250abc4a91e6f5615b0421c5cd56d7255ec71a8da04808c343a7748008388d4c20b9b7c15002926f1ab23b00053eb9c03e8c8b443f4ad76a67aacbee3dd957c493ada9eccd552397bf3049ade7f128a3e72de932fbd33728dddbb819a501d611214a083a46eeee74b71dfebd2ab31d9d887686bfedc5c6feec41f40e4cf0c9dac1af4e5f30f7940624027512e02f19a9968203f080d20a9c8d31682e59e8517e4a7016a9de6369caf2240c00144fe0f0e32d1a09fd7f432622193fc88882524558e2a8d0b0a5139a73040ac87cdb4af357bcad7dd3e6a527613800a2da0d812e8d092deccf94b20221ffd33ca528aa0502322e5d0dbae226c5505483c29a1ff439a5c2eea9099a39530215756d112749b8d28acefa3a862b348e1f543fedde7f851c55a0149c714b870ca452dd9b7f3d023577bd5e5c00b354ea4b0c372f5ed28cec8dccc5d210ef8033283ae50b174353fd61436bcc25e1bc9469ce4b039058e8d61f00ead8feda468afd15618016d587b0c66968c6fab7606c91cf8f863c25e9d8ab2acf21443789358489e90ea46a183c560e00b65200aea1cbd01a50dc218a50a886c495e6b2bcf29ecc7e7075b443c88cd823508451f51d4cf929499cf6911b87c8cea2ce8c07b6635b2187e8681e847a7e1c1b3db2f55ac5972da3f02732685cf371c53ca0cc45394ba744cde7a8aa60a79d701abe5b3491a03e07c4c5149cbfa8e4ebafdd7fa3b2cf1cf92af9f0e60bcb9ea55a7540334cef7ff813adce29c78e828b22354a6e144af1d48eef825e66f32ecdfd974e27e938f333cd741ddc7a02ba85c4454e0bab7ebff8df4a0be9ba7e1496e328b12cb6f50b9d81e60bc99d0a7725961c2f3f74b31068aa3df291c1290e9b2d89bd11652b50809ada92d8215e6262d5d330e1c472a4571593f7114228df9b1288f157790ce1c9090192ebff1638720b509a58985bf9ab3bf5bd2712f89e034dd15d6dd05f60b6122d070355a9f5c43d56bf27c5ad7fcf42f5cf2c3115866295cd55ec8b2762bd8d4d9d49ce42efb2348cba6b11377aec20345b0177649f8e14111a2a0cbe3274a4c011b69e931af1f4d40526c10b350108c66a4242852d4d76ea204356fd2df8e8f82913039a137b306525828258f49af9ba1966d9fd6907bcb3687839b115f249a7aa0500f17ec4f831c11f50b0d8fd614be4006b7408eec34d1c423e5eb964dc8d8601fb2cd7f705a01af427bc78fc12d4723d5ae0d043d74d1512fd92f34cf0e0e8f8718fe506c525f7947d5a18709981a1710871936ec6bdc4c077376304a451afd9933079f82126b17a0a68af052c4c02737e057b932ff5ca21e01ac1f6fc2daea40a5b3d669335d2dd83dee46a8f7846ab7a4a3b52f11b96916071f8091abc8b084f4e30bffb405bf34a9734476f4683d55f892560a7e967fb02a7d516c8089a298217370fc6651c29068da5aa851b9ca6e89526714a460c79da5d9fbb570110042d7c0fbc012d048cf6d317bfa4cdd63846c06ccfafb839d91ed699fd6baa116ce3710baae375992541542884464867ea45f88b309b6b353d69de23d4ed51b1b254f471b1754f945e6500564fdaae9b57122dd338e758ad5a66be8bc8f332da8717091d1d18a64ec926427992a35c5f29e0810fdc6e761cdacb1424979e61c1017d1770d9aeb27e605c1e0eb857d399b90054248d9de5dd425569c857d614901320b46fa85e3f02af61ed5b237483c00c788da3acfdd33b728554b49e43f3e35ae160b3bff54cfd44eaeafcda5d0fdd386ae3024b25f898e0280c86716ba6e7d49acca80bfc6d9ae2a6f7b2e1576863e3e10a1481a8f1a324475dfaaf74803da1005d164a9a054e740571a11a4f9cbb68874fed11b6276ced89d91fc1e42df7df2802931191e0138fe49f3c41299d861027186197b15142ff3907bc633c15e6b66bb07ab80bf6863fe1dfa90518b0ce0eff4829887a8dc0c00824bda431115ab3dec2215eb26bad6137065c2cc29a8335be62ee76ec51e2df4ced7010238eb779fb157801b85d86e737eb52663f86ad3d22cdaa19de634c9dac3a115274059d19ecc81e4288851c4c698da92a07ee6ff05af0a4771729290d62cc3c68c66a493eb9763d7ac464add0ca4b6c05cf737474a40975ebbdafbad40bdb5760b2b778cee9a33f88e139acfa116730724b4a3799eac29af292a1cbcbeee61167fdc57fea94f92a534a226fe30452f89aafac0554d5a40af1fc9fbb82b0652b1ea925d151d67d50c5d9fe7ecbf5ba7c8936135a4031adfed82c17137dea5f77e5c2f198e420b1fd8ac3ba3521210f04ef2cbc598c9f73a03c12e0f429a44223dcf903f610e0c919f2bf0f44fa50946800422ce400badd767ba9e2639821bf12aa575feb091c7b505dd80da65f2112440bda30f80d5267d90814a60f12ed8fa8d9c0d25ef069ebe32c36cae60d2681d80dc181ca2d3cd1245fe08b4ab55dbc4fa111fd820e48ac03c9da8f81d934a7506173e5aee824165d58c3a0d5611e8aa73474d5c22916a7f0b9e03152d669dafb209d51d05f7f953212c7e690feffdecad66a97a19f75abd2f32cd055ba1648ff5314197166819c330aa292315882a736dd068640c1aff10f571220e1d8335d0d729cd1dcef251a775551053f9a05a746cf2c4a998c04c4bd37d3561359ff752a65153404ae981e1666d514f2cc171a41774ff7eedd2db667be2b8dbd5d7a49c048c0d97201e6f6be118b46feaac62bc4be75146e02da7debe806c875d97d37c0a5dfa8b014d0ce1f77a918e0c380652d010254c047184d320eabf81f9d4de0061c787d0680ca390168f14aede3f2cc2e1446f2426142acd0e2be52632133d42fc9186157c01620a449aca7e295931ee037e9071781c15f795726e0e591f107a7f0e2cd3503b6cfbaa2374067649f191dbdc9f5d2aab04813101999b0ce54f2cd3b3106db9a43a82fbe3197a4c0b7abcf41db710be10b266882be4b927a12696c38995a57fa09499af533f128f4cdc370a303b98229bf5c1f60e84d500b9af33f732f9161d373889797a543676c9e9738b9a2053e36934878a46234569ccabb9ac51d0c972f16d32fb20f71cb5337318d97d1f3c145a8f31b334a0969c83620d805b5e8d886f875261b270df5592a3238c039cc6336ccbbab2d8582de7884549bc679f2e7501b4791fb35e61845d2b90f9c3773b0cbd146bc017c6e4d638e4e8fe3e13f9194900eb2018ce88806a35811d40f0281b418a4f6e20ce4edfe041a3c9cea7db10be6ea154c92132d697fa53ab04b304432d55c3b5aa49aa6642163b0d7813f2029579a56d2038022c2250b30ef8f82195613d9712344d25e752629835f7efef199bc49bc9bee40176d365055747149d53ee6d2c3f540d4aa45ee28bc104c1f63ce37464ebc4dbdc1a04c54f23937f43c231f8fa5747118a4f8fb78217819a157245ef942b161c3b430b7693e3b638852b8796230f74aeab38a9a0f88cb1d637241360335a58339666e202aef2b33d8443391ddc20965b195d5bde4dc4432ebb965e1a49bb9ffdd647c395a3db5fea4d245bcc5580efdb297c07306327652054a4e32732445a29e657e7955ce39effac409e69c412bdc604ed79110c7a1d4c247d7a896c361f630eb7102ff9bf36c004185263ab47cc6f2dfecd28ce0ac3b9bf47b32290e52125a515a417644a20a231063e05e3c2254b6a199e39c2462743d221fcb132752cb45b7d817a1fc940c362812c07883b80e6c42c6fc7f9c05138c2eed91666f30b61dad593b958cda767e8414d58cded5a8e7ac20680c0e732673113fbc900f746df0e3d8576083795ec9452a4d723b28c5050a261105a05d574eb969d4f03b712b91ed40b802a7af985ae28ef1bb8b46776151ba2e41c3f90c0d34f931a1a2db5d58e5d3f3ead0991256a7b6ac217b4fe1e2ac2f6f355f6a473d4b3bc6971188c71f20fc03fe675481a206c23fd0a9f9e37590579a8e23b721da080233c0da08131caa8e956b8b7c0ca657380883c97ca9443d557d0db7dcfbbea079c88a0aaf2e9fef3259db5e8807085eed4077abb05f954e8e83c9cd5b4f491fc6face21acc841d7ebf2f8163b75af76b0a0e79cba3b7bedcdfadd6295ffb377c6c65b64cbb245cb7a1c6bac59badb1fac1bf30fe19799c35529dfb84e9dc55a5e7f3df31119068c02c3ad063abe30eda0e73746ab2d264c4fecd3cd94e46b05ed9fb83c31b8fffa24c042a830a144b9314164460958e11ecb0f70bb4493fc27b8651f74877787cc55f5abb125172625ee16cd2d3880bd3239f9a9b469843461b1fbfc64fb7ac3719ddf94c0355c19498f208eda70fdb3ad0c8abc0fef27bf18a0065259be35861340b7577550194b5502a4370d632fe555cc1098cc500905b3e49bb3d267869ef508a52382f69d5f3277e05581bacb426c5eb5a63f1ec59ea1dd2510d02219158e98d9e1ea5b6cab047a39909204170cd8cd083a1d73e91517945ea003c3ae922156e4fac1f0a6b6b3e25e21d99c1e0ce606881765793044d63ac18698d7482e9d29aeff2a672e6886c507c356a4203d71e3b734ee60bd4197d947a02b18ef6309603ad2bae5f67a56b3edf59800ce5e59d08405256765e310ede2920f6fc21d85d78a8e76dfe87111582700b289562ce14e311743640a5cd63ad2d3eebda9fd2dba5a1e8fd6844c53dbfd6f1e52dbb1765ee61f73c27549002bbd7ddfdd8362156b56cb4509887aab5198fcf442ecb4380e591ded27ed42cf295f213dcdc4ef640354ded3b3548ccdb21a82ec464aa93b147c1f45118d99794cb79f87381390676e52b6207f9147e402f4b55a77bc4b863409d06d14028a8a6bec2cacf263bee494bd358ad1f1ae721eb7e4f1336564d640473f17aa5702d28e800c49ee61f3bd07c2dfb367a1935b6a28115fb3417186367606f05c16a83316bbb684ed01b4426a3f87f4708e1b983a9c569e952142eb194a7bee5bda5549755f321c1ea968c0bb40be7ec1c98571e814a85585e485253cad90166b808649dd11a798f734be11e3201df7ef0bb6c40e5aec2750a241a2c18a5c11646a8805b9744d804f5824a9b874143dfa4f6131e51a6955e23914085882228bba5275dc0aafe2d7a496bf74e86e36d0d0b7dfebecf08807caf37270e584f2131f4774e2eb2254c79b81b3a9de9b0a08f549df53fb77e43eddf5aaae9a817abc53cf08c6f9c9bf8bf27fcd9561a3c851333a218cbebea89e1077af8e2948d484837d2923362925c22794123e936de5df2793cf7160190bdd4b9b7d7716eeed16af4be4f4c660a357481080d70f74bd43e78a168f7daa3e887b693b42a62228f09fa2ba06c823b0fcf422a40eca3e39010b89c51d2df06558eb01b90ed29759a74d59a7def9247e822b686d42ebf6a40d2dd81ac06ca24b1592c2f9abb692ba20755d91b9224a7f1eeefe2fde0ed86972cca5bafd6615b3f8b8b189a99c2a86554411e41031ccc06de31670625fbe0b04ac0302a4332bf171b62390f79205725df6a14a7f4287b6b7dd9db9dbe9f2ec0cda855815e87337b987ac4726c7798a687ead64ab75d972ce0daff55c424f970f1b6ef7aa3eb02e123fcd4aa87ff6068382939b35a3dd642e04272b77ccb766e37680267f9ac91fa887653ac3093c0c31ceb3552851da5474f8bfcd6b63f992d00884f14f0b0d64517e941fdb13348c0db52645ca1d1b643a8bb24c98e32b793489ee5995cf489e81e54d280bd0b27f939f202919fbdb2ef0f28333ecde59e70c4b0111e40784eec32dbe4a86668d99180253831b1f5aa0fb577333931538e85fb412363c0e992c233d87196580446c6e3a0e9b4ca3af006295dba8181a7ca559b0d94b60528a96c4a1852e4111180bb7315a427b887cbbd5b397175c059e082e3aa3f45af30ef9da26d80dffdce66e1987a7d8fafd578bf83078daeec007d3256d14da1461d6bf746a9e83bb0d69a0f6bdede3bb1c4b99629f0054a81546f95f2c42cb91ace7c5f6094e110c0f634cf12fd89b47ca62463c6f0a30c1260f8026868828b552c481baafdfefc6b6ad6c0b98b685b037c3468e6197dfeddd71c31fbbf4033e451d821327d1310090dc9a13da0895750ada5107767866b95b4d5308cd1ba22300a62d55a48955c6cba6a09917dc4d2f29f5ab2e4a5e8b5a4849f03ee65480e43a1aa37792a17f468064da81dae738d1055b174c7572c27ad3318834c8a7ea41d91789f325321b818f997face9e54f86cd2fd3e8e810b9ef82495f52f002f39dfe79416767c7971ca78f9a5dfa22080b43c39a0caac2880450bfb676ab75c6a24afac0e36826e93aa64aea9935ab04230382ef26813dc68a112aa826867d319cda03725c5e9af791ae074da51fccd6f5c481e1d7cea928f32d62f435ebd73d76d86b35c1414587afeca427cd928b741ab4965a54f3b4f95ef449ebb4a91821396fd671ecf1ac1bfa2b13daea981c3118ed575c0f16bfd41bae96be70dbae25096ad3a41306d00e57b1136ab49308b3989365481e2f8a1c28aae369bf8278fe88ed26dd977085501d0cc146dbdef0c7da70e05c70c89e6ca7c45a55a2adf1e496ebd2c8e62b8187aa2366fcd5e016c5dd74b86a8a6ab0d96ef9ae822ff1df09562294580691d3466799d6ca6af376a274ccbdab583f97a53dfc1ed4e8aeb70638bfca8369a6e88b11255a1998591a2b473854ac02223683ef5d138384a5ffe31f551efd6b012bb6fd2f0abd841a2973a173ae256fdd28b70b033043c042f284ba651c230a13cb07c0fcd54ab4f3b7e31a1064b9ba4134a6ca4608edae5f0216b1f227f5c20d769d7a4c0e80a649a17767c5cec0dbc7e9b29121bc46da0213ef9c1d576c7d94c860eb0e5c0b8bd7f02b7b6d6d184a003d96d37f532e5917c0840fab6e3e076b22a9d144496a327f54d2f13971f9c3bc4deda09358132ca35b0f1cb0b4310481e0b9d7f6f303ef99984c92fd0074e0648b423efa6199d2d9d1374bf0c40671f9d11eeca99c03d976b50406c25b9098fa63f088871f3ba208d65b47d1607f0b56238d5760deedb6a2b8faf424709597db7a598821924a119694beb8ebed553d99d1db023ed27d5909578408d5f60b91b14194243736ca311b5f9069a5dad726c24db15aa9960829e3ff28174f69061b5091c7c9506792fc551270a185480b9b5e0b49104931e99fba22d59703ddebecc87a2ec176b2696de400486e2f800104560229ca9004cbf44bd7fa7cfb971ed745eead24a145e87a4881e7b66f23307048b1c35ce44950ac64496c652739354fa7be895d82d1a2e591062155a2e97cb19c7693db123628e042b51bbbaacd0b5c3c586186b10b5fb09d8b17a8862c5bc6bf04dcc952f8376589348916bc6ab53563e8ca24586f28254ad705d975e76761b2465c052c79fd10b81c81abc576e8e89c2955ce7aa67225a953400dcfa6b4f1d99e3b891685785a7dc1b86d1b0b21b721a90aae49c0c170231266110b3eb44813c1ce478a70c4c96c6e803bcd5658fd402b50b954bd20c6225ec9cd0789f697eb519518003e77444fa6a135788c33c3d5ca193b73283997cac63867514afd9a7e36f1188ee7f6b7b5cfbdc21bac2cda14d839bbc5cfa644343e17396483c220465f10d3a20661f617dd0f96edf9968ec0f5573b0e1fb64c0333af5e97169063d6418598d3e79918fe0d6e2b344de5742aa39fe22f786ff7ba4cfd14d0051065554c3286567c2fbb7c5fb6614b91077a9d043f9a8c930429271e1c3c6e75dee520d04efc2e18b5d36ac84895e33d85ddec5209e9971d6c1479b4ef21f840cb4b214f171c6bb0a9f5cfd6722865d327ac09dab35582a03ba8625a04848a49543c3a9d53790a6b942d3c0610499cb8eef606f80f077b599aaa638b25b557499f9eaf8b3f84cc6be39908f40fe108f626878ba25277816e7622757b0754ad0086e93a422221f795cbe2eb0ade33c454ddde8b4f6fe51f0c866271cbe9fb1af4d1c6490bdb7d04c78e1aada5f6aac296288c4fd20957e948d7d992d75883c1d21c3af091e98a86af81801967a6a29e969575fd442f3ea56e751727d5fa22c28a13de38f54554d4d75f4cfd753ec786950a0556f330265807a9822a6a0033e6f209308ab83f372dda24ec67b314d9e10c7a28bbdc3d6fb7117b7f6780496709a6571bd1918ec3ea7b563563e9489fed1592add8c6a3b3a1cd19d8b4f949092028e8a0cee123042d15cda6cf8928fe429fe6e50ec64dab6ecde6a5b59ed6192823c6b14997836268e868e646139fdcc85baa1fc6adea93b2d8a376b6397706bd91b22d4d74da4df53b937c770cc492fa44dd45ac74f3f431f532b80e60fea0dc77be8f63b37d2c806e88892786b962e023423bbf46bfb3f588f9b9d13bcb4708c3ca27dd98dce6ab2483876c2b2ed942982567c6017119bbda2a45860025832f732ca959bb92a1b1122fab647bf7736fdcd19aef771c4f623b234c25ee3b2295b5e14082a2537f5fbb896259e7234166fd916d3af6925ef06ec15195e7199fd310a2d4b8761ac63325825419e08ee11778723752acab59a9759b73055be692d6121ee600fe207c8c803381fe065f0d94a65eea8f454c4e21571db4835b67a63c2512ca9d78eb6be55fc0a9a64c48609a57476dbb6cf4003b7ca6708730d14b2eaf2dab69d40d920839ccd7300ed6deff6742314b26faa9a176d8391d9fc66ffbf896ffcf6333af5bb0eb0da2454a675efd415b1755aed8cc672c13243414e53d03fbbb4f68d14c8b642280728021abe8a34bc32a296faeae4831300276693c4b572eebc41326768ae655571ef70fb52341aaea33b52e6141d35ed3ea3c027cf4829566add92a529f4285e0f0d41922830b4ea8fa5cfa1027a86004d55d626f6b69309e1c4ad9603562152e356667a902239fc75a37e8a964e2a9b85b9149718807ff3cd2d42a32af01c6cc178dc80bd050740ca347f13ad6c435512f854191d5c94da4de425597f1339fbe898104a487341a2a2ec2298d4fd0c995d408ba56b67c25d81f57f731fd2c997a969c9639995a0cd12d6352eb6fc25059480466118af8c6a01d4275977b3bcf8f266318083383e193ac7c69b5c733fde412db20770efd2d61ad9da611fcc5b750b554fee0914784ccfd2d414a4b7546ece9273af5edf7c499aa11e458562a5a366a756734b8d95b3bae7edadaa78c8e3f16760800040e4005aaa04400e00f1507f499b343251661c355bc5dfbb1aec32300c11c4dcf5c266c240c21f3969e88d5b345b2ef8c16db39027fc4ad4bd10cf4946d26db32150f2aa6bfb6acbaad68bfee66a8cc95f79f81cbb2ef09d86e07b9e59bc7894290db74f7d250e350449c786c3692d30bf25615c8ad13128ac332d222e1cb556424f70deb220825eb5bb9b43887ada31e835794b852c8f6eff5239f2fa301bf0e2b440dd6e4656338078aca805cdbefe92dbb47947de3cffc028ba214784b86dc10f54c849a2300fee4a97af4e3154e87c130b0ef205cb7484a49fe788f29c569a6cab98c112eb0ab3364f9d4efc7ff1e2381a9144d31dfc1d4ee21cca69ef95efc490b76201df50707eba8c515584c48b070f8e8bbd1e5a241d353d7e7404f1b703cfa07c507d9425c27f63d193bda5521fb6c29414ad0099b58140e07ad94e35ec0eaaa4ab57bff9d57430b0ed18eab6029765862b53926b74b1202d3c1fb42a82e114618b9ec2384dee0c5701d02d08fe37494e52ac98b841b93764cb712932e2269b370345195b2fe1a8472b550ce9092a49f835b81f3f03c0536e5723b21442dda5f2c15abc53ff30b4acf09d377046060f2a7a4f755613b4fef62d64323f07ac9116c68c65925ae0b2f9fad7339bcbbee1a4d485bab2f1a00c746f9749a4ea347be3ecbfed92354c8fe5d92223ac4f347881f5525d0a675dbe9a9a0723ba33d8a80cc65cb4cda4e9392d32597f46f54995f0fb5abf65caf02e59065c63242b7ebac20089772cf5f6775ab67c565329d7fbf7cc29fbeff37825432c2cb722a360f40b3e4173ed936429e327db4558ed9f7987b445d84c8c126f4ebe9f7ccddb0f97776709408b89a5e8d46f79a0ede2761ea19bad432e013428298a3bf768301fec9a67e62b628d92121b33923fc7ff1327751f47beecce2b37554bd8d0dc151a67c6c865c1c659cf8ad2a4e854dd0877cd58f6bb5712b1895f1f71e19e03adf3489115ac776272fa3964871573994d2d268287f3d8bfe408b6d97308b95a19672b87c67e58992c0a6cdf390b2ccd5272003fd87ab66592bfd75377a03624a1b0435b14a02969a4dcbd1585d06532fe23dad0e504211a5948b75fe0f8bdc10e1385971c40bdd450c82917ce0c042bd5e8413d233e1e3989f0f8dcd8cb7a1ffa2f6db0976efeb9760d7625e1daef685f4eb2d89e9c50e07c33ba50af50833c8a66c282ca0a291dfe539f050a62d981e9d63900375416e35bae4803937ac851a979400ab04f20b6c6b2982d5771757df59421b4eea69e052e461f767157920aa8ad167a996fa78f96c1be5acd2a77d41dd6e2be32929fb8c13bfa48bc6cc95029857a628bcae691580d119591443224535ce58b11b035346c1d5d8974f9afd26b45d206c0297f1bed225d141786eff326f1907ab4838fa2ce8cf2fdc22240d62d9fcf0be72ea3ad1cb73f58ff599d0d37b97a1b3c887577b6383100a2ce37da58b443ad0ca503499e02dee9e64e8998b2d9c90952cecef1ee68990247c384dc8d01564c998deda6fd9e1a4be855e86667f459f3c3886525ad4a90af2a9e56dee92de6a6856bc8babc22e408c8902799a4ab6a31f44b1155bfbf7a4fd26e7ef1a210fe457760fe75a2ae0231920928ba47efbf807f244b02aa7e1fc181b4aeb0089d25c380072c15e835aefc1d1746720d43aed4b5128cb764f506f8531cb50a33e105a510fbe2f41dc12ff5e7335957ed36f6c692d866eb0042f4859558757bcc97934bd52f75b40582f4ad850208e824113b12aaa2f25e810d01c168f590af6f0a5f53fbb4ae253d2d7afce2d4b70518c3a8df4fbd0241176837ed7f9e7b57ca2689875baebaba7a81e588524408b3ad58bf038e1471febaf45cd33a9264245a5eb9b647d8762c2a700510da3850b0448db40de2c211c89f8c06b64ab82dd66bd471d40b9687672cba17aa2e0f84ac0cd96572d6c2aa9be6cd4c0a7ba8530f30a8a1639f713781530cd8baeeb974c70e7256a5e19b31f0e5270ed5fd551913550613f6c8456f3892922a60b2c35032a30d66f5930165aa5037d958bad7613c6141c0347cbcf13a2ae9d53eddff1b4921bea3249ba41918cb938996f64f8b1e320defc7c7e1febf81e26c6eb0f33b1d4b0ac6e504789f78cf18deea5051f1f6a9092ca46f6de64cbbda54c29a5b5062907f50648dbd7d96d5f0f6793d6c2557e83f1be7f7b2efbcf7d58409b88fb7ac8c4030af3610fc1782aeaab6ee1b42fc6f46bfa587be3957fe39fa5e91df04f1bcb982f2e2cddb3f646f59a969ab2f5bc7bcff6f5e8aafa5c48e5a28cf4f1abc1feee099d1ff2cb0704ff552a4a597bd3dcc4d2fceccdf60d842ae30b0d50b52ffd4e7fa82f9c34a4aaaafd83557bee63fde6b19646fb62b4e7efe13534400d1789b534ec44a8d5d07b42cd009d648018946a194cd87666ce4612c14fec74620f989aa15847f3cca041031ab298555936332ca19f2580a0821b585125c75f060aa80c97dd3802e50f456054311110d57e1c8f431a2974a31d5f358a75701d4ab2ed4c11f1f9670a1d954fc8ae1ae3170e69a4eadf1474a9af96dc7851166333010f497a7288146112f800054f4e7c2245fc891459b5408828e4c42fd2a5000805396ef32487489125929213bf26fd455eb4c8f1af09ff7e91e6076c1b71909f0307bc627f43c6b3f6173bf05fd728fe63efdb433829c88832a2cca83dd92e3d935266ef9ef4ac17734fbabb673273bfc1a5c4fcf3ef27d9cc24866558e69e3d69d03329a709fad52f492489b4e0a0c84131c60954e7d1049a4013c8db316f0692076620b5b0040d690d87b4506759966531ae1817105c98abbad11240090db9daedc2a44b4a77b9cbc8594ace64d6e9c46c6eefae6c99652f9755f7cbb26c6659f6383d33ecd7e3cf8432ac6ec4a4e024878883f8d348d57f6223618dd448fdf29fea44eed32ffff19f8dfbaaec23645f42a4902edaddb1f640e7c88123070a5e9ed8dd23dbcecc8fc9322cc618635e31c61863c464fc187fe712ce313a629b93a3363cba66c610aa67b89ba30e1b2931a7e0c0c13a24588a13473ff7cdf9a6676179adc3c13a58e4e230f5943a196ae9719c0ba34d0e1c93c53a16470e29774a0fcd91a3722773c8219a4012aa5f770a212a533801545975a768c24afda9560c655ce439e74b3a5f0bb9ce974fbd9303b63abfe6fa35f98bd3dbe4b876a1f62c2f3dae74fa187fd26fc6f9b2f3c14c510dfaabf6436a7aa14a1ceffab749a536529d9f753adc05dfe8d7bc207e711077c13ab6ce9f9a79fb0365247feef8ddd418115d9d54e3cf460f3d97a6bf9becdada084ff537c2038f5e8c7c1c70aabf9118f9c5e0420dfe4ee369d47e1a0b88a6da8f0335d57e538d3abbf5ccccfe5b1be12bb5bdb0880eb55bc7bd0d74cd81eb5ea95b717c63fcb7d6b0ef5f0c2ed47e1c76a8ee1703830bb5df85da5fbc52b7c8c5591483bd641dad43895bd6c15ce4d451f7a5a6356ad4f9dd50a3f6a0f5064eaaf3067e75de264cb234407ea9e469a5ae73805c400d7b763e1650e777c302eadc9be5d81e4d1be8ca01ac4ef9c5af312c367e2a5edcd340444654aa08ea9c26aaaa2aa4d47e19d0862a0ea5461097c6555ff98bbebdbb2d05125c84d9adfd6e5e9a1eddc5267999635c949f67abe86d7777ac65c7eef6ee96e284139397398a8d852b1c8bed29ca85dd9dc7d8766765e6ee66ee3e62b9f9c5aac17efd9391338b52b1a0523502a28aa7f05203ea88be01ebb0cc2c3414f217a5c9b470bbea5f17c79e33971ae2a3ead6ad61fc62604055ec5195db6ef9e7636b44a2b5d4c854c53b90ff2d02c46d810db6e64d5f052008cadf2d194cb6aa1836808a49a0c6de1556aa180726f604a05ed3b22bcc838932c68e31c6c85b32c98f6769ab56b6903cbb4658a041e3094e64dc76a2542362bd55e8003486abca0f95eee1e6719ebd7123a39e410da70e554c9ea5e9971e10959f9e88b1509bc76b32293d537ca8d4eeac4aed6c0ca31a4a9e1e2f557e9911112e82d048e5a5914fa36b603f6cd84e214514f760aaaaa6a28cfa52330fdb5534d58843ede91eaa6e18e79e4bdd32cebd53dd26e75e0bad5b89738fa56e9ca96e1ce75e57b7cec4b9b7d58d8573af54b7162ed3ead6b29d38f732ac6e28ce3d59b717cebd58371929d516336386c93fb934f123086a232086a0f68c988e467c4b648b8121db6f379659b7e5eeac1a89dc816dbb156304000530b92c8b312301a67a1778196cbb37fd52438cee9cc4c60bc6d8fe72bb4df2dbe86d443291c844702936f94fe8607bc03b6463c362ad0f2ab6831a87bc36b1ed4c36ebd7dbac04213629c3f5aa9d852894c15eb5371b6dbb37546b5998ba600b15a8d1e3292766811a33d3fc7869b0ae5811e998acedd5c06a66cd3b1dfadee9b070a4a88deb4290aec75facd92e8879c5be585b1e8380111504e920060aa068428423e4645ecb177298b735d790488d6179fab17ca8215c35d118025d7062a58f1b6b29931d0cb1ce2f8c35235231ecb18ee76cfe0c06dbd240d707422055ec6362f0e6b86b00f61a4f2fe42b755ea95ae600ec596ae64d166e2249a70aab9ab1a75aaafb7de3dff4dfbf8f715950ebedaa59eddfffd27457ff61d0c20b680b2fa0fcaeda1fcaeba191eade132337e602176473069105d11263ee7418f4c2670b9672370a8542c5e9cd8bcdcf9d8b744765f6bc79cac93133f7c79c7136a27b748961980dcf21f3fb9aac3859323b434580a4ab5cd15582ba8d784e4d2b95b66d9779dbdd65664dd36280e8d6dddd1d876ac40065df3534c2dc4ca7e238aeeb3a1b269085858552dad272da5545c1cccccc5dd799be72ccdc73ea98220b6d51a9a2601365666e693971a7938b8b0b9d28144b47468a53454197994f27171717e6dd17666695aad3f190687f21e5eeeeeeeeeeeeeeeeeeeeeeee8f99b3ec9bbe72715145415f5e64a4524582d8a809ac8a8251a8f9425f50d29d7993c1c9e874646ca9948ae3182f687fccac52a960603a9d193366c4c4c4c8c8c8b0c0b1c0c2b63468d4a851839959d5f56066d40c957467f6f6f6f6e62d26468693e974a87b8c3246140b5e12687f214783468d1aff1ec72cc3020daec67352782bb43fef6b814b434b88dff103eddf5ca4331c771e18d4bf30522e0aed6fe6e149b57f4e19676c58a0af5a0aa1b60c7ca3378c33aadcc5ce22a6c46b9a49cbc03a9a057ba3ff493b69172c4d7fd835dc22ffdad582122ce37850db05d3ae6ed7aeda055bf3e309550ea80d5a890acafaa3052594334e15050dc159358f4e24df2d51f92a0048402cc6bd99459d8e1b551ca251fcdab53571ad27692a400d01841abe0b0eb4d0012a0b2aeb4aa185901641438d6198c4a4aa772693999c4fc9e248185f7ce1544551aaf66a34c2ab4e1e2c70168a6312c3304c0a2c5e1593ab6ab0478a1045c9ba524831a286f4977012c347082940b838a12f75a5c8a24a104d8a210b2429b2f89182083ec8a288270348c2891fb872a2c7297922c70528b208ca895e4c0f72b6e6a2d78a62a5b801114439455894a02ab300837582185408ca298275e16d41ae1ce911712b496c90945384a506440e608411fce414c18ee05dc020477a445a8b2326ace414613922671b015818d5708620780594da6fc3264a92da3f33a31451fb3970334c4ef76f2e0d7f15400c01e5446fbf4092e3416cbbbbbbbbbb46f844196394438ec304951f820df02b5778417ed0796fc47618c0ed16dbe8567f32d94ad99ae1f0b081880cac175e4819167b72cccc4fa63ad141a0926e612c560e214376f4cf16d9acd11e61b27fba154e1750925622df3f2848486888d5acfe384a01aaf029a25bfccd641f2be9964c50fc62db9824625b30915cf69ed74de37496687d44e5031ab6cf775bc9d443cf62757372946e9f7b7f21585b00a2db3caaa72a7f07f21a1c39fe1ee42ee443ee446ed4ad95e2c84af5f728dd3a619f6201c53e6c1f77f9ca7f4e4a55aa0759c7e4c9da5d47a8fc42cd35bf7b9aa741f7817a51b7b0f7f718b85c49b7e2113df0e18aeaef407b13bfa50506c6aba1bbb8ee835f4cf69b3313933dd7ecfb1ebc148488993fe6c580c1807d31b3725018a2861e84727a64598789a7eef7dedc345b591aff8d89bc2612f9cabf25ffe8c8c888886868e83dfcf06d9c80862fbf08a54157f1c05d2e55dc1091a7330209092968bb62343434448420b8087717b53a688eac5d35ab52c76a54f77fe7ffca5f87fe81dfc76f614820628b306a0f21043f0ac6430d41095c710431a4f0021d9eb0018b6505f82c56945045c48b872a3108630a3c47c076a8fdb409a130784933e15bfc37c1232042153504a5137e650c122481e14c20810c181411a98aa228d5764055f71f5cea16f548e118639312c4b430b1041260d058b7688914b868a96ed11224c096e82188d4ce38bd6842852b5016303ef935d96204282ac50a1e9a4041a9a0b46e51131cd03068aa6e519324a068520294942b271eca8971a2826e484130c9a288899110a722e86907340ad445095a040a53b78849095ca0d059b7884910a7218aaa5ba4441496f0851529585019758b94802288bed42d5282064aa05ddd2225a8aa488921b222257ea85bdd222552a005618300a507202215464544aaa2284a8340c5944c88ad080815a8ea16f534a19eea16010104152e84117530120410485c56e8c0053bb0c0098c270c1c5850610a57c8e0892190d0c4d1912200c51bf088c1846d0b221fa41c77d2440644394e9690c349497216014173917686fc28c981e8880d3d3e495ef8d1ad1094214af5b937d8af0f6eeae2a8fb03d81a70a5cd16af4e70fb42ce769a7f6797a67f89a64d0fca9ecc603118d6d32d2440d5a1748d0f49c1850f122ba1d51928f2702154430e4ae2e2a886fca4fa6351ee744b5ea95e05fb257451778d50f721b0352c0fd81b8de5dbf195bf69ceeef3a90a02dd1ed4f504d45d03f8dcd5f44045420abd0bd438e79c99f6cd7f61ce1e22fed8cf3d7352ba2163df2f0d7b1790697049693666246bcd67db993006695c8360806abbeba7a7a7a787b537daf3647e013508feab5498a669da6b1be74dd40ed989da3d27a5fceab6e7dac7a77dda47e3b40ff9a56d40c07ec76b60bedf86cfa28931ffbe6f03da4f96d76c6fb3a357363fa0fd218b3dc667c97696ec8f95433a2056e740575f37658fd391c56a50fe6fdef495ccd1a0fced9648f9d393f2713a004ea88d0dce4e0e3ce4ef0eb95498c8f7c83cd808640f453d2c38a20739c5951791ccde6c9592d99dd99dddb9c80724f80e2f222a76f0a2093c7bce28258665d99c1a7bfba5fd5269db38aedb5dd3e4eddd35e9b0743e629db4a57b4869686b399ddc770818b2f7f2224306cb40a15e64743a5bea25954aa15c4ea9540b4da5522c2953974aa5301905b0a5e005dbb651e8fe76745358436da3069767db9970c66fccdcb1b12b68482b95284c15312ce3743a1f319b736a9a562a95b60d5bba343adc6f9c8f58b94e87e5b7aed3e97e3399d6d4e9d0df58b869fa8d72a56efed6b2bd1ef724b16c6aa5d3eeee6edc9958680b4cddb4ba71dd4a1ed78df3b86e9be4388febd68d722a2250feada5bb61e3c1eb4acd619feb763128612529e2914c4c8e21abd47d49a53120af99f1fb12698988b691847cc87fbc46f59bfa2a781175df89d8565b41a2e219a3a9748df3f8ca676fb4df69ea1ce84813197f8d50d288e5b7f4266f65bcb0705c894371a78e4a2b720a13f93a7d385b3ea426d6242af1f84ad334a4a30d664b9c7b8b691be71e6b1e637d4a72fad1a697ba754ad2328a3cead6494983fd35ea01ac40f21a2ea93667a5a23c9694f285c54ab3acd39999a675a75cb2aa3cce2835e3897c1a2ce2874bc205c17f5898d0175f4de955bf7c65732bcd6eefa3ed9b41d9d44ed4e4a1a8a092aa5819c3a77f7c2514e4527262325db8166e7aa8176a0a8a0a2a0955a557fd45fc9c7e4e4127a0d3cf29c9e987ab8b42725169258f2b578e6bb7795b5d38f7ba1924bb7b3a61613c588f7c49a424d56b6998043911e2e28994dae00c2b49af2954663665aa05b5b5ad14fd33753adc9b3a1faa28133b7dc0080b82021624b3c4e5b7e65e5e3a9deca3b731aa2397122089c5731faa5c3ba241c3f33a9dd2eeff12e83e8eaf271fdbbecde35c0d3637868d731c5843ed48d3269276a41dcd17932027427e6472b1f349affa5fbe925e0df6f74a29e9ff4e47fbef7cc4aa8a4243f9f2a13ac2c92ce5c24af3f4b57cdf1776952a1ff7953e0e68f654964e24d0d2f06b4192898c815422d5821900e0dc7381f19a40607366683c2ca8f1b4f69372f5f8aaa79329349fcca11527a3ce6f0635d8134af6a16a6e1f3fac475ac1782492275f1a92f6eaf11a49a557495ea325f94ae3e9968685c6c3b634290bf657d17a6a6b47d4e3da05c0b4220047804e47be07868d0270d3e35c4a98785c5083eda457fd5154158afb7cd24bc9d38e8cbca6c74bcd70fc007bed48320d57c22a8bc74bf3324ad7a80ac0b91753b71b9c7b336ac95be2f2d25b3263c6278dda05ea2b16e71e4c95de12978fdf3400e75eaa6e598671a54ee794a4c1fe80d143816ad89f5c0df649eb743c202bddd2302f87e9d5e9c8292c5c0e1d68d7f99855fbb4a3a5e9cfc1030d4f3f3f5ed3d26265f2d01ed5c967fbba4af6f9917ac4d4f590b1030d4f3fd817ce199e14f215d7b89c5e642480530d81aaa25019ec4bb9e46b477bc3c15449b97abc54ec8349e25e724ad7b860fd2e29405382a8085519d288a66846934aca25a9683f5ec331e19c682eee89f6c341d182b822ed27ca506d2944dc14cd489be2d28e1a9cc101cd4c9312d3e9649f7694cd29b992b61d803379ac1a0295e9b0eff443a1c8b4a36e695298744dca957239a14f5450b822222e4aed9411272575c44dd1a6d4fe2de5f21a14955ef5a3aa00a1c6a0478490a45c45a4885e92d4ee1725b5fba4e4f4e3e29d92d0c03e14121898b6a5aa8b774a52a3d3d1bed30f57057b896d1fc53c2df31220bd25311f63647c7f8c979ae1cd97375d0db6922a365458f2a572529bc97c184f26492bbeea57795ccae35ebe6af9e28c96a67f7adc0f97a43917a7a40adbda6d2bdde2aa58b0bfdf799cc7795c556578da91affa5f3ceda521f9aaffbb1eaaca955c9e028143aa25ae0a07867644fd6275683f7ca37f6fb4ef4f29e91a97ef4fb95c3c4d4aaffa5b3892f18243e2a8746bfb7e2e6906cdd7de38e91a4e8a942953b4a350beb4a350beb4a350beb4a357edafd1f578a9d1f595a2abf26f4f4bda4f8b4be5ff013b9df960e72356ed03d0f978a9dbc7e1f8010d39a423fe9043aafd1bd8c1d0b16af24bde4fe5f0f4533d2fe5621dbb377aab9cd2c1b9a7ede0dc9b9b02302f36b8373294c574eab8ada4cd98192663016e2cc09b5b644cbb34fca53ac355b785229a9196de80128bcdc73e22e09439d3db1ca63ba57603768a3c82944d4ba2cccea876246cce7a4d36c3fcd247b32f7d325f500cfbcd9b5ff2b28f5e581a9a61fbf911d9bebb194a9f7d44be3b086c8eca57fe31a734a43494b3d872ced6cb8add4298af7999f6d94744a54da0f4f323529abe72da5ff23647f33647cae77580fcbcb909dbd63135025ef72eb8dfb9ec53fdfd6d7877778360b75836d8c78a159fbaefed9f799c118e3b28c4cc06b956a1ca641f3666562a65d9c65ffae2a66ddbc6a59fa5df7e2b6ddadc782bb13699b3e8ee49f0145514a2a5811285c6fd26e5787797774ed74ad7d206b9c94d1a7d72541ec9a1068d429534720f6551946e85544ae956288b6451b7d6f59248dbcf708a07b48500f9b146cccc3c51a4cb9f5bc39f8ad6904e943fe5f5969510534c501bd5b570ef25541b7b3fd4b61f8b0914dc82f2ef744f813b61c2ea9ff99918dfc683144608cabf7be38fead6ca8f5b4fb972b033fe97bdc1af0a7764d4e885d55b7a30357a18a858163fa25eb02fac0135a4364790e744a12236c49c4ef8b66db387f60cd9f77bc65f4f7fc83cfdd8f33bc6cc53f9f9f9f999c75df3ee0682f3a3c12348768424c9f0b45a61144287f0a8a73258184938383b7264b1356b6476c53fc3d9a8ccfd82d780be923ffd0b553c1afc81ca6f0ea75bed80af210d34094a98436d106401a121870d222936502246c4932bbc98d881358661ad8abb62907330eb9fa592a1b307576441a44550e829a21205231b1f39c6d831c618658c3146ee6d87211c1e2fc810e72cea81a2072936f95b8c33f42ac9c6c8c686394a299923330c0f462c4b8c3737b75286de1dec779f86067798336615efb490ee15b2d3bbb33f6ce20b391ad2bd42768e6818d240ba45dbdb8b2c67724e37cd389fd86e059438e49ec52594e03f66e041126468a900d9a4b5724462ad90bef0c3a388eb0fafc1414285d8f0e307c6e367698468b846fc535bbc6c2323111bc98cadcd0e9bf9fe3b6ca8541435d70ab49f5d563889821b25d990838b099117120ff8fc60212b64211aa8c44eeb627cc69ef7469431e29001092223f64082b8cc62361fa7810009e2cd204b34df231eac1c4358635730334b8eccdc3f8880044142348fc098689f4e7da657fe362cd51428ab5291e9964d4f8e1d51c8d0509006e90f28702285cd9edfb3dfa102cdf86968d0f9736ca77b47080d42765cdbf12c46410856aac418b128638ccdd18806a39d2146b1a84707db143220b69df1191b9d6347dd95e999d6667809a1c187930c19638779b827c3e4048abcae52c55ef94e691d357e02f6c60e1e933f1cf8739835014b13ffa647edf7a9809001b5df63f0cafc8509a83e6abf0bf1176abf4a45296bdedc55fc92b74176f8a31e88fc41bac59f3710ad3f195e20e261c3fac0061d0a54c8cb3d48db81fc4d4658236a7a4da986e33c54afe4cba7316835fe16e7cbdef057853bb583c16bc681ae13a6c2106bd69a865a1afff085aba1c6b82baef640c1406b4824beec96420fbf94eeeece557abbbb7b1047dcde3416314c6232722c35dca1fd6e7534606ff8c74f8655103404b9bde91d3ad4cb13d4866ef9f70be956d7f8de42220d3146efb1cd68faee77ba25a4c1b8d360dc69303e0dfd020515e23bdddace371a21ea41f64625049d5e57f76ee02bfed30bb2343374ab9f7a110d52d6d200a9b1028ac3e3856e953e3e4bd6bdc2d108511a211aeb6accdbf3fbd89edf4825f295fb92c7ee38f886f6ed711efd62bc6ede36f8cdd0609c3dbace2f882a899abefb991ca5f78fa5ee2bbdd7e08f6a307ad43eecde3ece49690bd99efb6df3541e457d8dce3de7d1294ae5a5cf068c1e63906e457777ff21d650649f0e054fae80d453d4c3048924a0cc259048e28314824e498a30a380841090b80071c41149f0988e8c684136059398c9aa457e2ccaa8699f69f363d4a216b538638c32fecce2cfd0ad6ce587d360b429a23fba35438ccf030735046b8c2f5f4974aba38bca0fd1941f7e20628942fbe5ee6a4621f9b204b109c9051055663edeb33747427e24680ce9d353b221530f4a50654744951f4a2e3a9fa5918f79426300fdb0696869e4c72091b150331fafe1a6f44a3e959f2a2e6e0ca02abb234155764884aa2cf90c55f9cdbd1a944f83082a3f2cf9b42c0d9f2abfcc40ed9758a8814aabfc2dc6c8340994dc2c3ed914323eb40e57a86264a31b9b734e1658802c68e3f0701f865055604482969147d6bddddddd94d2a6ddb1bdbbb1ef054fb2e19543337f405c0734e4923c9043b7c245226264f4c35705947982e82c82babbbbbbbbbbbb637b77472050a0f80b3f24a9a1ca08f945ece2f5b2b1a33f768c317ee923c738e78c33462cca18637f38fcb1c006fde3122d51f7b7ffb00912238d31fee0450de98cd90e5c042849188f2c46c6be92174ba552292bcdac548a338bdf0b2c8f8b6818d24595e722d6ce90219c23453f2c51b91891a03b68820fcbcecc9aad09f6334a25194adc6c8900129434b8d8cb1d0d4aec931810f67e605f7a4ccad2ae344dca9f5262ff43fe9801fb701a943645345ca31f0b45ca8552e5f328ea21a2ca5f425f4ff054b92ad59c35dc079ef7a8b44df844ad648a46000000006315000020100a088422a1682c8c7449451f14800e73884276583a9647931cc9711c848c210611430c21041822333344346a01a564d300cea0c9ac1a2203948275aa1d91c069ce227dc9b8cf622de73110dac35c5c5cf6ab97188ad269b41d59fac43d0a6921abb0574d5491f74264ff4f4d1f41379c5ffce0299dd8c009874bd37be50477288c8a9c320d28f6c675e6dba70c040ecdae9c2bf624d4ec16e35c78dde8f728eaf4db2c39b642f668b23d06c796171c43644676207ced8847d2599a8a62a4ec28e50e4d0252e095ed8b786d0b918896ec53b300e0ccb3b2f95cc8990042322293f49b1704568006afc09840eb2ec68efd40015035d05289fbd5cc89c43ec2459d85800c3d27a03d409bb6ff400601c401fa6f7d6809b1204399d1968149664a4f3097a11f6919b1b145078fc6a7e6181cdbbd2efbf00db2848c223180659af3480e17099f05ecf823f63247cb08ec6226bbe892b42d60876f46625875412ea2c643fb98097cb54b0e66ba781189e1658d4160bf64c6f3c990200d357fbf8f6a457106b36c1e9794c3c62915201bfd0dac603c9d6829b03a892eca892d3909fa5d5b2a6fb65f834efe8abdd4a90c3344cfea1c74ceb3e9f7358868a5c398f95598763a15f773177f2f5c8eedeb5eb1620f1cba264dcbb932d633117d8923d53731ae9ccc00cb82bd4a466e3518238fddf60a514aa63ffd82f57b7e45ab76b5ff5c6937e712c4e79461896703df60fc90abe088d7db1e1cabbceaef18c508fdb21cd460c8d0250f9e2b71195eafd761676fe564df88a1717345dcb34a534b5a63417dba9a00b9394a3771503ce1ca3c156762e6b27b31c8d39e402bac0ae1af3ceb09589c5d77494d3528982b050fc8d7e9b6b11a2584a15d28c158e6cd12b0c3cfda0d57b72eb6290845a01de0b6c242b576c2fb80eb767a87a185c3fef73f9718cbb809089e387b0800654a4cf0b0b867be9518c41a46410153de74703e4de6226a8a1246c4f3352767fc9147dfc91745ff24b8209538657b5762e8a12f876150f94f4f432bf777e87e3d22f1204616afc32d5e7a576b1961d64a132f03b7899ad8f7795a799711513eb09398f9710806e19384984533e73e763fa6babe09c371fb6e4d4f69c67958078c129ec6364a80cc1e7c99aa76094d2b33eabd05d027ab064c019273622564a71a3bc8db4cbd93883ebd0147700ad700df13a4b6480391bd1bbbc616b75226d2e2d8e889989c1b087321349e2bcec7409432fd4d5fe1ce324b25b0fc2bcb08699412a92710d381f8f71340a115b11edf1b052fd962afac38f6b5cbadc438d38866289065c3a117721dbb33964f1be7a1a728d68330cd4aefc1de374f17d72a7fc3c73648bb6e1855a2de08f43bdff0cdd2c9635d24343d70a42857f5f4552d03a6f157b2f674328ce4adbd38fb67839225167d478fd1ea9dee1966ea5a924bbad31979833e0170b38e436743ad560202bc6527384df10a3453260a8bc5b773588768f238a156aee152a000988c88f5c5603ae9679615d8305d6b88eef66d1128b1bf5cd11bd6e1de8f3ec59fb839ac431947f6330b17344ff48294b2665b03031f6d949cf6b00ead92416997393774bb5e496a1c08a2d18b73091d22e4139ad51fbc006a8499a4befab5d0857b9f3f10825dcbb2b5911366403ac45694bced2a7fca4865eddb9fcee07d34351028a7b7dcdb8fb699d8a700dd64f25cb6e601a7e30f2afbf76fe581726a5512ded92203180a9f6be9e67b3555ae84f5e5eb828420c87e3e8344205a00dc75360ed32ff01429752329c93fcc3e361c810af2d00149679e648f6ea66ad1c246f63cb75f929e64782eeb53d326580c1208af10d6d5d0ae85ac6e864c4cedc48315be13d71153073004226851b0463ec769754216cc8b2f5889641d4f5f9710cd2bc079b2fa3464f40a7c5f160cb53b6f22d1df294b1d756a81707afd4c564ddc3d80372a879b76a0db4c8cdd5fe0b51d31e412ea84bca4e5282682dd455197534356f78ca4c47b75f108e59489f16afaea6206bd7a0e2acb4eb9ac9c73a3fa4338fa7ea12783283d4b90f9888897c8c474fb12f6e8f01a0b1e2d4a16a476bc6ae3a342b6ddc6e0af7342f898129d27faea1f3af2fc9d932912f6f4fc483456722c3cb6bad492c07b079de5dbe31a200fde8e6e600c2ce660fa4fe5a08ba10000026ad5bba916873674000221c308be9f53721ea05590cc406281602df5876f00edf3ca1265df2aca81f60de3c83cc4808761a17cacb91134134d1097fb12ead24420e276d623409b630e4043a763ec88c9e23c0ca0041604977cf56227164bf69a4ac62cc71d7ba35e02767929761b3a0faa54eb5813f654332781e20f1c20155b855cb390d7304919a6c981a2242847cd0b050aed31cc4adebdd56f02188284071ed7be2a19c6b1d53a230c8ca79e7438e0a749fdd80dc7b896fed30c629ed670cf33cabbee0842042cd7976290c116a3b66cdd6b4bce7aa6fd73baef7afdde2df4761bdefb4fb7abb4b3d9769bf7a545fd58f625f32d66bfdd976c99cdb588b7908ebd79936cbad4cf71f731034cd9036ef03df3f41c15d60363c2152d5280d4674ac66fd2cd9964c92f6a2006ecf6e5bd0445baa1dcf538070897a471e045c55518328bfb988de426167e93435aba81dae739ffd335cc83a43041d78cea65a29f226b4b78f0e83dba7f15cb9d6809fd068b76b756545ee2a17e4683a80728137e98dc56855c78db6abe4c30d96459ac188558c22f1c8558f4c78a748744d10f6af4d565ab29c9024f3a68342b36abcda717813abd15b77374532abdb1402ed3874ae47372d5419115802cfa19e3bf538b627dda8709b330fe865f1af85b4bae2ea4d9bddfdcf2d8a8a567eff26156f3d4bbf4c75a6cf11390977a2d5a302c67bd18e6957bf6ea675b6a69d2794594c547d2ce855c9744739882d9e0fbe332eaaf1ea581369bc22dde627034319a8006fa337236dea12fbe66d4d5271beff600edc7ca786b63696ca047fcd124fad5c40304f66b9e54f1c7adce1ad33d5a0cb61f422a4fb2e2bf8609d16b6257759e5a9d3110a9dff99108bf2f889efebe1e72cb341c430c292880a1310d1872cb866338830a2911537c5f406c4cfa1ce82c882fc21e0f83ae5506239dbf8bffcc30c56b6e04830afcd6b24d7468cd6d683a8e9356471e8da3a0ba9be2eb0a43d68e1adbf4d2dd96244ecf21fa3c183967acbb8becd0c4ab7956406361878cf547dc807106ee19556041f474b25c0b781674b02f1a1400ac75474da0f026fcacc6c7be804938bd45ad7b157bcdc22e1b753101b5e43aeada54e7dace1ff44b8478f0e904e15327c38f883dbeff19b7c5f59b28a1397f904b5d95af7bd847d259bb75f012d88e14ca7b0f029af554ae78dd3a9857b5b9c96bee01a48c357acf09ca582d74e69c66fcf175ebcc2420a7bd6fc71b45e870a8962f6d3f204f5ba72c771769d73ac31bca7e04e6f08b968041cf9ffc61cdd2a38b606a463e83140f177380d6fa9bef68f325636a705a4435ae01d5787f53dd8edc7563c5c785b7d50b458185e60c14920bc05a3a0a577595aa9cdb555943ab2668363835eccdc88573e2dc1aa7182a25d5440ad306489c6cef378daa2bee676e9d84d40f779144ee2260241610b34e5177d6b81b31a4ed2ec61022181e95342342e590e6ffe14269ff72e39218e3cf8a323670ae62069704b38e9f160014fcc1dc8e389a989d80845e1b7c266da9166dfb5ba67c7eb84903ca894204ea3c380fc97c83b5eefa0a8e1c29991163b6f3c4a182da02d0a28b4eca9ff508161689488895cd6b502b5a4adb2926aa8b20f55f1f14a1eed308129ff2678c3b656daa0c7e5971dd80e009760bd6e527f886f914a6cdb04245e3f268b8f74d36ba8805273a67d01e7c4560a83b6d41471d50abc0134c8bf1bdc28f04fd81b5b3483d8574ca04028a927dba0f66864a0542b0c348259bf9b0c2fc511a5d7054385bba269970626513929016b378daf291404ccb9b8067015fe5a5114a94631407f930ed251091ee7326e802078ba9e0ca054b8e7beff971ddc56b7c7e7ce77c943be438f2215d50b3bc8c9a91fd4fb318371e35d97284a54987f2384e46fe73e3fc10b548efb5ccc77495e3d1653e1c91178950725c8a12391e471a06b23054998ace1e31efabada48a5729ea9e7419bd96ee8bbe4ba77be596baceedaf754ca5ac4ad7623e46002eb8f0268b8f9f3ac4f44bd9ea58930e1adc792a5918ce918eaa5d2c6a8d8a34d36e325d4e413adb5d6ad12393eda6d92766ce4426639b644c762f7d63d10580a231c0d2d949a08735a01e536a24cd8adfe77b2951158437b5122956f709eb8a91f3ff38679fd3c7d2540296e60b9151cc8219967eab30528f2f1736a12854ee4464375e1dbfd200efc6850b615457fdb9227d06e81994e094a86efcf112b23198eddd4ed45f744898e95f70c08b54f96e58878f90066f076da291033ad3ef310935df18a1ec1c7184ae9f6103d72f7b42f2be5d7e2d3425954b21423d8c707838a9cbe21c41d3f6405bbcab14333d9659655b9c6b87d3234f8dbba0f5e13a93620671d926cc7d2e32893e8e45ce8a60b8ed555ab3b888b386130e0e60313fc87bdcf96368d33078865ee743b3c979683d08b63b5deaac54491595658f2e3a2f27f11f1390d81bf8148328b2975d13d610bd196ab0eff4199622d60af727878dbc090da5f8cceda7c1f404cd1b34f595691f99ed08db9837598ff835bf0534a10c0fe7a4e3cc8bbe1f1268b816cf9d752d3113caa0b820e2a15deab489750f17103d5903ce4c2062aeb9d0d0e064d03f494d37627e2e5f043cb869c1411898eeac319c4fb06da4f4ddea90e0cd5c970823da5167dc746ad11a823967a188c45f13c7eefe313ed3d49910558c79f5065d22a778c5662a333b28b4519057eda0cb4db07c92c633ddffc5db251b8fd2a7d54715088c015a76c055672f5efaad29436e9d8f5bebfeb45dae770b08a0b1573e9329660e2cfce960337d29a3df98c8bf7bd400f4dee0a620adc0b58afe554bb35acb6a3755dacf3123762221aa95c4c9cec6086a99cd5c6770961f088addc1ab289b54af08379fa764b6783857ea2db73317d286768fa16287ce58397e048a3e5beefb887f4e7bde7aeaada410f1e73a41170fca1f8cac7567f6e31e0846a4766f03f667140bfe7cfcf8f09414ede47c3d1700c4db84c04ddbc242fd6d090dd1a493dba57b74b56725dc0899a4fd555bd3d9ea11473a42c96243f0148a3113a4937e4aa32237873e5a4189b5b32da07e9a455786d60f1ba25935e9fdf0b9231c706a644e2441f9e903f4c1468e164481c988cfe3c022e9ae1287136329cc2e66be825adde773b3e4d6125455851cf42d0d2f869d86af2c9511bf0c0868b41e60a8c2997dc314e8f334eb7adbd28266a4627cab616b4b612d129f0c709c369e4d86abb4ad541bb040cf139692dfc16a3c35b4eaf1b71e4c5fb2027b686788ba15e54feb9ee5689c038f21c00dfee12ce6aef638bbff99fd15c726a6e7f5e022978e43edb748c2f1614232011f96207c31e1d47b388d66b98faa905000b675198b810cae7569055f182ace474034a13a7d4e9e139f9ea722eaa221388d483de40ee5da25f33248b543cd8a280955af06312d05f646c444fd9a4816d2e019302cf3c65f64cbf03253557b3930954c13951f7bc741436fa3588012d5b7be2ce0e5543c64c8396a7ff657af4911a81e01fb40d577c014b5d7298d6fa5e8a06138284bcdb9c9a8dc0e29d6576fba07a88a4007053df6f158462d0324ae6751932b9c0552db8f4cdd49224ea9d9f1dd44fac3a91cfab0fc9194434cea8fc709ea99fe18537e8cf07974aa59cf17e421d0afbc58470ea807a51c6d8ab7968e47738216f050bcba075d498613b97abb3b482b6af2cd7927faa4865c9d3323d35822e23e1ce689ecc1cfa05a2b7964484f641cb1768610b552c4614b5ddd9c09999901c013aca942c660f65c3c706a65da865cd2d51bf42e4ca6d00820003a8d9babc4796c5610a364daa66c62783d7461d17d538e41be62d94c95f24c5fc22e91e2c04b356ab1ae7f1996db9842e9002edaa163e76b36115f23283188eefa3a40d9098c6591baab6c4aea0d023f2ea3324a3deef5942a3f7fd9c338d253424a5f169a4101c3b1f998afffe49b355b2422812b0dd4de06d1b7a29a801b34593a6e975f2dbcd61be3c6b264e5ab20e518b906ef43caeb479749a485d4ebfb1c8ee02860cf9df9647b5a9b6bded8916ae882945cc5b3c6f631b9e5f0d63d96ab46d00677c91fe054e51b27bce8024af39cf0ec3283ba8d59c66ed7ffc0d7982c6c4ddb495a60235655d2bbe5c15545abb7ee6defe23bbafbda0525f0dc6af316bb97b94bc868e30a67023a767c250728f74160163bf5f1f529d7ce05ba10bcaa5b5e80b8bcd59f214beecc431bcd895b13cf528ae56fc0f5a33e38ed182965be713cf073e3162d21b9844b3e6f4cb645bd753974a08964571e163dc5929c2dea5ff5a4f56429aa22994dd06c6e9606ca618dc909f967525ab067bea08e576cd5cf7d8b9b406eddc06ff2c4551b8280bbb1d74f269b5bf2b05eba780e98177bf4432e8490b257f637ed360c6edcf7276e837b8830af9c6163be613a99bd19dac659803fe32e6da22e4a1c2481c951ffec3e2a47536fcb57cd84157418f9e8e8eaf88ea07b7b5ab5af2961f72428f6bdbc31d328340a94a988aad226e8dc908a5ff36b21a5816c8d6b60e8634482ef12040cf17f278e4dafdc3bbec06692ba1ddf1c0efe61d32fd4fa5af59451c38e6cf2480dea46d5d8e0ab0947464f01fd57c0e3bcd1c05d1ab77aa8bb5b72a592117f662a4842fb27fdfbad2dc6639c80af4b9e0372beacc2be5daa1d2b8f27bb077a087ca54c1855665df429ffe1bf0a8dade0305f0f7983bd8e52cffcf09a6665f9687d1a2c6c034af9c4fe1deae8e07314a1a5b36a079942bb9fa727058483ea13f6531d187d5bbef6c19a56ea2f5ba7d90b6153577a6d0f49194f54e1b401cb2c8e60423c24b9485b63697da0b90cd821f378b541d89b65ec0fb582d499a2f5c25cca60e71f204e2aaf0781ea443cb4d57173d4b205a7a8aac01190df9993587eee56af083fc62aa9144b6cc02849412e3fb9541882738203402fa3fe787571b700976c520141263087480d8792ba9d398a4fff8298d35652f7105dbde4996039c818d46c8c74e12e34711149460228f0bf0a7f0b7755c8432d778c8531ddde08c02d3d13b65b0fd0324d0f335bb8dbd253b180c9762d87a07c579b49f2622d04590a308b43f2fcb13235261ce2b3660d86e133965d87f43d88cfb6282d4d80e12022ad90b3ddf26e2c97f3541fa2440631e892c51ad44f41c3e5cc3dab515851edcc81c586b275e49a724d93e956cb3b4e0679e1ce57bafd008694bf36ba40a8dbed9dcd76c058cb81660b56f341579fe8e586c5d259321919738cab8a1c3c1ab702218a2adc6f9917fa34ab750129b8657570576499a071d7640de7c66c05236842e76ec78fb20864ab4e9d2cab4013e7b65b0b94609f3950cdb2b9581350876b19475bd722742b318566319a8415c011330828160646935c4a6ab0023c53dc652b8b6007e200d1135bdba7d67f5a3f8e41b95f542bb24a5df08525c205baf7459372abbea27d8b158bf90a5ef46e12171e80cceb35c90e108f84c168e87995020b7930450d77d2163c824eb6cb7376003c19923b98effa4a4c64a71ccc8dd94369b2643103b218309a3f5e8d551008de9e6151b7f4fb9d9f82993e0223225f35230b59067e083e11f1a9d3e0b80a62d6ab8cd05c33aa3a29ff8f14c2fd3651ec0256737cec9cf8f6216da733dea3390d8b0b74221483010259932829b69d8e7a6d470ab1f340d289b2078e9c33a9364d37f4230ab17b4832a6ea86c2a3cf7df57b0f2bbd86ec64659657f7ac3b79acb07fe56b6d9a9f3b344f1afbc240bd6a15c00cd974cb484cc44dd95e9889b6c9160f6a36e4b326678826d2230a3075c206ae725eea9801df5d9973745729251e9778a99371cd383c78385e79ae942b6c41b47da8dc15e81ee894989254bcd515cde252ae3a0c9610a023267f7b019c7ab7804d02c15414b955c9d71f9fe2ede2d8475b114b913756c37e57d933890f9d0b0c60e6ac690c62ec3b12d5700e71ed8c975a90cdfc3c7498ee04071dc590ccd06d548c5d91b2c9ef61b216560be9c887a4e5c061ddfe04f9c50c46491b4168fb2ce1cb7c3664921237fb1a51d419525792776b5b615d63a5f44649d1de99c63deca0162a7bb213fa858f22a1a294934c4924112b56fe2b81ca0af5e4df817690ae6726ae24c18ed18139dcbc5534e7e5001cb2588290af8c974bc284abdeb0f2741b017eceedee0cedc16af16005cbabb12607273e0c8f8808d3f37cba3c5b0cd5cdbcfddc065e14c4867d660ecf1de8cc0f08185498f07ba5670e4ee206e3e47ab1b10c3384e84f7d40c85b3660164a27e4f6247f24799ac3ac8606c630e0efe8245171e2ab13686fb18db1f8871acb4cf292b48317265b61e14270c443aa88739e86990cbb8b1108154f28da0dc93bfdc3b8315ce6046d3d370620e6065a414e274980bd190d7b65804eae04ef8782ae5b95baa0c404f39a30b7eb32b31d8f1576237cecc85cc04a1f8aba55345cbb71af3b5b8a3c327ba4c51f8e7cd8784efa6049e10ac79fc4718d576eb6ceff8f83c22c394ef5eaba2d720f2fb042f6be6d695947b8cf972a2920fc85fd17a54364aca817c75bda24cd244d81a79c30d400bd219d9723efe424e5460e062846388787db7e673edfa57996dd9a5d83009bdac74db8a2ee116b2c42b82011c8aa32fb283e9b836de14138a81139b2f997fd4521702a54388fe52b8aa1f95df26436828004fe13444a1ea33a1dc3b956ab22b97adb754ffacca8b397dd8f58742841ec699129718f11fe2c094b2a4161a55e261dc3a2c77696e122e379f97f2668214596bef74be4d6a84f20f22eb7e9ca680b9d33bdd65c39461bf902a1c0cd94383ecef820b518e1b57ff583653a8bb9e32e2d20a97c3afb77c3033bcab41cfd93e5fb1bb45f2139a08b7fe1967de43c69b3a541d88feb26d6a4af7fa4d6b32bc3526b473144b0d461eab842cfa0a7470b50d7215b7a6f5e6f5ea1aa26beeef4437d2a4ac44a68031b09b7ba40186594c36674401a0de29cac6ac1b3a92ef0e9fdedd9e44c60a29b2a765844376a0b52612c6bea042953da1b3b4b3bc9343f55a4ba4bee8df4f8a8c99e8a7581491fcb2906ccdea4f82367362e1b8eb0039c611a8d208c1a43c51cfba7ff1b06a698391cebc2eb08a5a8be5ece3fa2a84f2e927b2db9704b90ced7b11bf5e60692c6be6f1218b1a015a15459c4ab88236eacc6f36d58bac0dffab6ce6abc87f5950893a2b524e5b54548594bb716467047fe4cb3b4c32c91c845d4fde8c153564012fb54776f4729d5d7fc6ccc3e8daaeea2ce448be3ce28cbe3f4970c1c20fac51df700682e9d9007dd442a3a6fa514dd21dee8cc3a2c089534308fddc0bf8ee4d6742fde2805e4099d27178defc07f6fee7b474de3fcc11b7aba4ea9476e7d48cd1d40e141aaafff27dc7f4291755223c515f75d998947408a0e2821f86a53caf23f17f2100a5eda6280288bb1028f0000927a3c51628c482a7d276ad8c4887ed5b1c0e90fd6eeb9a965426f7d8de17bce152984c76aef60095ae8c8468fa80ebc0c83ec01f1255b813ff2e564c0933fd05b6716868818a27f138ee2c2e630ca49da2760c5f8ba5766ce264f9e9c6857bf4ffcf9c7dbdfaea63c9c0ffcc96b519a2765d2a0d7e11fce728ff440123dca04062d669e0249a4e6c1907df80100a606f5f6c13a21f755615a000f980cbe2db0e22e04e81b02459e3e99bc2ecf4591753f6ede9701be91e17c5663550c949bdd498eb0ca82302022111e25864b687ed3934aa0ac0c38e0597cbd42a18c83ba32f099cb79a94f84de00df861f91406849bf57d7ea6bb132790fdf65be39040858c8703b529c61282f1da19d2a675193ec74e5b2063b6f0743fb29074e56eb9b8d98fd335719a0c890e7977178cf3b10a8745e6629de440587afdbcbc5b64d86f22e9f1c80e24432d42c87b5ba9834fe0841b0bee226534c22ca22de25498f9f4a12c0ee1b8510e81aa04b82911c4009fc6aca705ee8cdf5c208de15e380c26f501f142fcd92ee96cd33f4a4530b7ed7345405e6f0b22c78fdc00cde1c15af49ce7df831f446869292e7fea6de1ba63b44a1ea3118165501ca0f9a811379179e5efadd8b1501147204535d6890d0f1ac503a3094845acba2c47b122d9124b0b871cff4ec99f717ecc2997ca82f719e5afcbdfcf426ab20f670dfdeb87480152bade227ff7657d64f6e4786338578efb5642da1e8e17c975788e013c3c7190d6aa63b3a2dfd50342fc2e3c18dd1cd5297e668ca9af7d43d5420e9b9b5783b85634845019c62d9ecbc851ed333db894950aad994c6aebf67a2078e025627664a1574b7547adb496a6ad03944be562600ecdeb3b059af34f531e825bbecc91b724004d3a23f2b306609a82ee1daeab9550865b62b8f261ea0a6bdf277a174fe8a61e4c675e4f91062cbfedb6dc15b6a1b9de2767f2fcc02fb84d9d418a882d498d449efe22266d1f17037a3daf4b9431af68acae32bb3af30c1ca79fbb1361e36624a53eefa1e168af7eee53407256ce862febcdd93507662ee1d5ff34cb9b21922bb7c4c18fe5331d2ea13cb8cd04cab11cca0304766ab1be3bbda347b21b2daee5070a3b1b30a4a309120c6acb9b0496c8e194a8c23b5508068aec0518ccf220be97725d71d80ab38ad713a788d29d7c680552c59263c25fbcc933d17b71d718bf3f01158d564d81f3f6d2047cf9ba2b62562c55892a49cf70d795b0edebcefe88b93c369b257b467ed9af22b3a28856beac0098addfe28b868a60eb87cb23e00deedb1497768386f0f4523a3dfdb533243c3db1b8e8bbd96cec1ab8e9a39a112c2449805ac11416b2fb35d5acc68bc7ce8711f5a0c9091f2906082a26f093f43211ebb28fcb49074d98550ea9e4a2f6d2e8a4660fecbd3b1e87040fd6f47159b25a5e2792b12527bea93c59abcb2341c6bf08439f365de145bd360e60abbc0b144643373435fe2e66a5e0414d57df1de383f27d5ebe5aac14ec2af9758964ec76797b1dc27f0859c9c507860a101358a7ac29f2cf479c0abb33835457d57bc508eaa9892716553e31e4aa718040d290dfcca986789e65c7096b98e70e1831713a305a4832f2dc1d9b82018650ea1ad909868cd1ee42fcead511dfaf4a38253bb0d372802087998760f42dc1a288ec8760979079db60740de531118767e0e1e3193c3ccfc2a624f1421e77f82e5a927d14ec6fcbc62b91f6d67688d54ddba655002e11346deef10642640bec650d143b88277596aaf2412f1aac868abd7fb84baa33ed754f8860126085aed77fe6cb237e65f33dd8293d5f1d4eb7e3c71f106c3855666a4070642299977095cbe9b565a733fa7b7a2d0fbf90f5a80ae83226097203fdfdaf9d58515855dd4cc40b60b6f2a7643f368c140c2a68c93d319b58850b0369248ef6f6f265324bf53ac49dc2cb248f1c0c20dd61b78abf1ebca2cb18e631cbf8f58fb540f7347d4eb46d4f560ba2297a2ab076b161f84dd69904a37500993d120035d476b613878b78257ec2284762a44a1e95b3d0691219500e4feb9ec9555626489904216912a61bc8c99f534531c1322d919c4718597053f8e68706a75604008c065b9094eecf144e09b1488b3002be84d6e4a54a35eb124de03196b653810eb42e1473be4811dc57aa05936c56a059afb3b814229abd30ec6a602acadf77aac0a6479c88f559333fa36a1910a79d3443274a36d49b5e7f89b68414cdbbaf11867ebe3d2a36c8ce5b67e74452f47672d07e7d9e542171e7068f1832bc155995a7dc7093e06dbdd5b47b326daa6ad659eb55b08b6e793735fb16b68cb16b88bccab69a6d3600a120c2510b830d4a856149f68360055d52b69bd14d0b2636dd5462d979dd2f16330f513242a6194436fc133652036bd3ec2294d94f621ba1ff4807ae04a0373976375923fa32dfeb36d2903412eb889ae6f4b19f0fbe22020e4d20e56e06629833261a697a8b74a12c86a405cf6903c21856b46654cf81f726b3899d67a6175914850379bfbae2cb240aaf9e4253410b1ac21332caf0eb15b804dbca870670d1591fbbdca4324adba945336004ddc1144c899a165cda946bf11e0ca39c77e0723fff3b81626d8a625f006d80c5d3e1c5eaa8f7c33c1225e6c27b88089dd4ae91f2e506cdf7cc9bef5a3c02fa266e857d9c7aa316311b78408a7c401804528c702b57c0b5d763e7dd97f06b5fc7249635614131992f5e241d215b9fbd90aff1a64c5b729f85fbfe9ca66efdc905bbfaff6e684351d3c3a0634860b6945c31e464e096e5a7d0bd6c12cdc3a3b74595a97c166969bf22bb9b1ae0c6496e07d0e7775ff1466196b7d0ea790f35bb611f2371bac2b7d6b283ae5163039e4735014a890284450b2158a1eae95e2790fb0811ebd6bb78a928a2fbc5b56d9feffe49ac361d90c97ca0f5a74ecd04e1ae98c2945290c2e4bca2e11c0e07e048d182f26f8c1478c6413c1d36cd3ffed2e00daa2d9e929edfdaf4a6fcd99a750ce105acd5d231d3fd77d06d0f6df041f46c0ed9d858cdfa27f1f7bb7c2a3d2c7800255ea1d3da2ba7422ad56ff5720725ddfc2a0db6f5da0e7cd7fe37df60aeca395da2e77bef94700683c70a4ab0c6094bda8eb236dd3c489b5072438f76f9fee25b4480fb07803d2a863150fe80ff365847d76ed606d8bcd8c53e15a30bc0639e36c6278618281b927a13f68111134aea64cac007ac1b9bf52dc938809707ae8b69183972be68aaff327f7169e2d1228069d5fd979e303e650378a741dd747652fc9186e756fb2ff2fe13aae4682bea633d24be85214391e8cfe38aa05a72d4f1e84072ebce7d24603562f67ad6e49bd0a153fb99a0de77f915c66a7c57045368fc623fc1992fff8f5ab008f4b6fa0a283f070bbe04e0ff9ae35cd3c02b88d669b60486cc3066a12cceff85fcbdc6261e0314ec3b038597cd1207d33c1cde2de9ebd7a856d491ba61d084abba56b8fa0ae9b014de35fd6a61d8bfda42b7430896dd820d184080d588bf40d65087167b3b88712c75381b77a95f2a79524fbddd4a2f3bea680e655a4b22591e132deea53f7c44428d7facc772a450ccbbea805102772080c4a44bb643fa3b27f4f05574ffc3d2a3a5310726dd8a143da8c10915b1d5245497c5b30064516b02a5fc8cc129f10ee97896126bb290046f09fd177ecf9ba13f12abe904d2b517f49ecdd2f73e0948c191be4dfb157b17a35a02b9134124f508876b0b51fd9402d2f406ec58530f54e7b7c275e1e2c9ec09e935ded31fe63ff3579b65d91f03f0dadcecfc09b5e710f52d851531a33f07c11f06fdaa7cb678c95f069614dee53992b665846d2a61642bab11d24e7b8deaa8a2c01699a120db70cbfabde42014e331246988a39f2d5d880822190f2fa9b316824197c4b330fd8463d13855a88a3ad2481447b00e94a296c6dd4e042a6486b7d341ec64ea6d29fbf46346aa4e169d4f92ba53385f663ec781ab394ffea238d7ff4a196db3dbd8fc2a149237bbe3504c975d0afbc0e75e494d06d78d3bed49b10344dab1b61d547d6d0480b0a24b10d1c1a1ede9f6b4a71d56dfd2da48d403806da531ca75e24bb23cd52be6e0f86b29a27e6e587c9ff12f2dc272406baa47765c44b2f7601a3879e81c777d82a58469829b74697cd3bf6c8f5166f9a3ed56a43ce47183c7b15fec0ed66d13dec7c7b3497a4098b795c78fb0ef41a8400a42849adfc61ce9c4d9219f761362d65e7e8b26de09cc66dd46c4cd2a34f215cbf26188535a146cdc8cbcc8b87fef353c1bac4aa6e1fa9ded4bb94a166e5666098db7cef8ff689f2fb33eaa5a9be2e995db34d2c3f775aa6a0bc37cebe4ffceb42ec15926d545b2264879483d38a68398cd7500fc8dc2936ce2c8b4a73c49bcb193cb50838050d5b23f46db21b6111ca201b1272b48cc83524d064d36176892f1cf5d80b3bfcd7043cdd52102c2f192be4e558eb7fb1a362c002a2116ad39b697022888d385ed42c192883190c8e156a2cbe91596d3a840d178150ce12de2d6ae6f1544dfadbbc1350f17e94b57431d4c138908514d0d4f482505add3888462aed9f15340d30f361ee83b81b392839923d5f50c24288d6aed45cbe30d92e9918df682e43cc74648352e352891bd63ce7625cecb128114d2a3d8b29e46b4a791f88ccdb737858cd08cafdb9dd612f3b3ad8d20ed12b8324fdf9b1263d0131fc3f94e437698ad9a1a5cdc8a6782ca9595084fcf49fada6cfd7e83e1801378a9a680948197722c92df68fd42c3bf412372163b4cbf1ae9fa1867d3d5720ddfad74696480196b6027e2135ab3c7ca51165b949d8c56005da63d05a910f4efec09b5048cdaa6e2dd64df1063543bbb980ab6f0b8ea910163ecc4255a0626162659af13c036d8cb8c32d70ff8bbec85d758c3d5e93bcdfa4d485d8d0ebdd55f126faa44bd1649c05074c47cde296bef9ea8490f3b9d5e6ad35942fea51ae37d8bf51334b60c4569d6f09657484f67e5ec83999b978a146cdcf03ea56004ca3e6158c838acbc3eae3dfb0fae757ce541d9aea55946b2c2aaf5df747158d2be5879fc4c1bbbb4346cdb5c0546ca03f9b316a664fb0caf1384a16a63f38a62fd11d56050196b73cd734f84aca9de410b9b1d89184f241275d30c189ca7c4c86f321995028a6662757d45c0697746e766996397c32a5f3f65b22ae579f710e5b484c5b81024f5968b49ba266e68245de5464e69792f9a02444aa08915ec0f0375133e9fbbf5c92ef6756169833ecea4f44cd0ac7de5aef05ec7507dd1e06fce39eebd493a48bd47775baebe960493d6e3b9a91a95b3ae396d98d5dc7243de6943d57e34b6b676797d042772c436086c17f909fdea8c2fce633196a3e66b28449640d3f4365b8c6c56c1b7e3f8584626f65869a152c762278e145bfe77e4657688a0d00c04ab1071561d2ba481bdc43bce715723282a5220ca99a09ba9fccdbb42ba2041f814ec406e9073bd6e39df7727c2ba49258a6a54ac5d14e26ae88e626cb0146d74158fc170a2a65fd5b3dda8a0b6007a2d7883e4757a0d70913aa1a0e71673f8300bed97688f8dde82910ee465afcaf844e9a28ef8d761cbf5bfdbde3d86c476a5e4d6ec49066abf4b4380b608c3e6aba4ff0a211c01e679ab9f1a85ba222c6244c83040e872b83a1ddde3329d4a74fde63eca5a3f0898fe6751bb5b20a2d081d34111154fa4c81e5b7579518b7680d42d0dd971255adec5d12a149f9d783eb957f6e4594d5e196d0376b6d5e9d085d16c4b3af8ec7255aa81b1d0908ea484cfb2d0f07eddf71d0e48fb94e9c40cb4bcd24e9e7857432ae56809743cc04e9afcd5186d333d4ec982fd942c1f054e42fc39112c45a8c83f9b5b68a853d58fae328033e961b38821c48365112d70e4fff10417dff0142959c40bfc0472051edd4a0e310fa49fdcf77548ed040f9e1b1ff7894f47fc2dc45e944064c0f338a706a3d8c066c0d1092bb2a958fde4a0c94df30ef134595edad2fcbcb6ab11e2db80a1637a97f9ae18d6f86c2f714daab10a197078817faa3dc075b835d3af2b90c8db2654b27bb04f1ecd4876104939856b8c0b9af61411c753ded80dce74b8eef08d075485df56d9c8ad94c4a3b450501df3f778e572bd8b951cd0e2c5b3e56073387332894885711d5d5340e8af0b5fffc36a115376d6d2ee18baacdb573a52841b96134aecfaeb622150a3363d60b4ee8f60b0918973cd0b260d2234342623f9114dc5abdfec730df48b27c203623fc7e0eff065f8870972c27d6dcd4d5a4a3402403c9d456083cc9ee3ef928736750023cdb938cb1b946e4a38236c523f880b120537c8f3fcabebc11293009cdbd821b4a860f0a359a6a6ca952bc02959a13acf83af0d06123845334897dd1fdf3095bd94e0d1a76acf32c7e14132005953cd750172aaae69e891988d03bf25ddcd0ef7a850cd6e5cdfe6fe5453f1063a5ad81b3b4c3a834091bb912ff48d8e38a5174986f36bcd219d07a510f4aaed8f3514024c321062cc6ce7a2320c1c1b0077de004d01a716194446a479b4b87b40707dd59cc5c0223ebda9bc0f8661f69e5de5eccfadb282c6295e5837fb580f33b74f86fcc61fa3cfad6a91f0b2717783093d61693abbfa5a93c598610b91a1c4d42480037b43b1e51f798be3052dbeaf2373ca30773964cc21e872280029a08fb83304cf2022a7ee0cfb1a8dda7b5bab7cabb053c443387d3cf567fc0718bd44f4463586a70bca1e96617eabe8f04751e6974590b29490fefa2b3e3127867cd3cb629ee02fa9d07ee2363358e6db390f8d0bd54c17c278491c9550d62f3091a9c1213e2e416050a1a8c7585e0057c308223320d817327b8e94f9915d9de5694ecce8847abdd0751417057bb2e368bf53cb7480d4039c36742260c61dd744f453b9db1553283f1d37f75e172f10d9589fdefddaa015178715710d3a2800c5e0a22a1d72910249171ad5458666eaabe3e246d0368d4a2af7e1294622be487263ea466ded9213bc24a7b0f5ce42e19b030a3fd81aab6561b778b1acad2912760f548344f5b1fe012f83249463a91fac36906e69b965834a766456bdc80f6ea8d971cad07cb163dd0bd1f50c41499e311e9a703848ff950c5604e9ecb50f99e5861bab58a1be56cfd56c208706f8af76805fb564fa4693a08265df50c6eb67c7931b488f1baab70773174a5c28a785799e97f6875de05a7774939ea4426e7ddf0fe41f2c0909cdccbbcb788fdbd48d97bc55f558a64131e11c810339eaef0e82770ac45c90b97bc780eaf7b87b73eddbb3657d63b76d97d69747c14121fc060820930e3b6e1e2280c2c73aa7fe1a324a5a96b685f9b0443a806ba3ec547731f7b8f4970bff051c90251a909cb766d1a697bd2e693c36c4ced36c810038a5afc9ec176049df4e1b4e72fbd3c799980175126fc8bf16d06c6a6ea4d3e94c2aa2a3878367635e3326b3dfa017a04c77d24743e53e072b6b830401948ca0b42c596ce9483a1b4b8ae435e751f329707d2f52c80f5a1f72c76f32564af24ee574a157c788c3da86ca747499afb6a091e36ec7c6d773f45581ab15afb313a1045ad2d300262e7f60025b337fcf9441ff9c0e3b4e663b0326265840cdc2b5b4df7f8a519de49da06b5c77e2d3ba44f0cf4b08aa9b4338d731107696481c7666c06912840d68c2097cfeff95df2f60ba0d481b22b9441e2b1c57d4318e89a3511f4d858ece53082dc6198a5549eb2840cc222e86e49efeaf1daaeeaea8ebfc9930b5fcd085c092d2add35fd5da326b4f268adc358e67b8f0ccfb2eebae0dc3c31f4e5aa58f36b4069cb201fb0d514397a015262d2ec36b409a2a2a217dd60ada5887fb8244cbe5a328fa07db48892efd90a0fd693b34161477cf6729cd62a0e7e5e506765ca75a2f2abc1958632c53493e1559bafa8d82c20779af442bba558c0b274cf4f8294223270641a245336b71be32b75cf878c886b0e8e1bfeee4181029303511e058c0bf5ee7ce296e80c3252ec3a4ebbca62ac390cfaf573db8df4560771ca2edcae35799edb494314ccb45d31b7510791ccf11d503e0a7d864fcb3c57fe801232863b3d4f1e4f89e06b85b8332588fae16a800d5b5475137457961c5350c01f2f101f8e1396bab5fa2f288ba938efd6f19207c0e9aa75681a5d5f240930a948f67d352e73d50496a98f672c97aceadb2e725284b99c171980f9b5c1f22bee24ef379b5d673bb5e9f3bfaa871daaf6eddd2bac49c013f27dd36534dc5d3fd0c2d9e5a4339bd60f0a4a09ca7e14d5a91281ec45ff1cee3e2d456f852243a0c0d32f887d00d19936ee37e663122fc6798f7b1dfd3cc2fccbe1b6bf78a31f246a16af0b77833df327b3798083c4d17140dba702f4fb5aaa3d090a8482a875f45349e3186f2805a252bee9c6e42ec2941e73d94db8677118f110fe729cad428c4d123aa907df2bb193721b1c8017ef2370d45296bce7f7028cbef920be8eafd38f5e325dba1517752c2f460b50133ac9ffa264bfef3d570ee497585b81faee653a28f7a9db1d120c6f0750555d17cae5d869419b5a45fd800039dc979fe1adb447e2b4017556dae3abb121c6cd400184fece4076a4b30a44e2c40c860da55ff075ac5ffa12e11c21ac7cad4f65e17c0f8538962c3ab5cb5e2ce0352426416411cf81871705dc8063920aa887a1b44491cae9e78838c1baf6ebd90af718df28c531d09db877b8ce84f49e4d421aa2ce75d16e21d2c8cc284110acec2d610febf1ad579692dd004ac28b35bcb9c54d27c2dc8dadb749ddf83615a76d12f40e48f35def7ad54036075ed09820eac3d504f24b2d8826f69271fe2d40e023be9b792629fae805c2a88067da57ef5d0feca1582a20c39c97cadb8a493ab16d4249eeebce27e02a4c2dd53e822f2361870148fbe210e4645d01923f6e2ab6cf5faadb67af8aa283daa913be0d3d31b886358823edf43bf4e32191dd987d2b2653bf16e8bdd85acbf1320da62eed80ab45ac955bbbbd2dfda60c93a1f132e9e43738acc82bb546838793bc55884309951d726657b38b674d44b5df216efca7d18bff7f383c6cb953c3633b956f75a8b1038a4b3d67b01e69dee7be60b072678be9f790f2fe645f08fe664560e90f3a670e336c36dd147fb118d17c16a8b6c454b310aba7992b016d40e063e5fb7d64550f9458d7032122b8c445907a49b0475484d9e3440e94c6872139ac2f957d596f9b0c54e88cc401022e34d0d4f86283b37c169e09d2a8a40ed931458fc41c08e2f956a93b6196145837e50f8b1d0d5bdc3f888b42ca105ec609000e4fb373a778f0c698a9d4b1bdcf03746f2cf0546bb2333004f35b58256d1ab25917abd93d4cb61675df821cdf0759b3f48b19829e041538423513e913467b4a830ea57e2200b3c932e90fe5d395f4fdac69669ad7b6bac9496f278e585583b78c7b118b725539ccfd0c2b96fb412bdc2b3b4a9519af8aadd3e11ff1614022199e60e739513a7ab567cf8496c7d14fb6f37802562492be2c87563f5cd5c63e79d55a1acd42057841aaba3edef5651b26ca6d43f9c0f36a05b05fc9f159f89bb3bca882171403d2c7d9d523bc829da9b69beaeb2af83f70382ce0c2e61c65052e341ca27e0d2b97f676d1aab539258961e3db1ba4199060988b36833cd10166960c9f55ba2d2a6d9c598151296bb6cd1066a740452eea5ca83a6c84c019073eed6e698da24ce3e03fb3ea60a3f891e7d89d910506b2fd14bba68e114a6b0137955e8a1f918966b94ce6200f471030d5770950002a17aa6fc82cb1eed1cc122393e4d2b537a1035c3a02e7e714e814f70e1a72b0ecc65a5ac44ea4f5a925c16a881266a98f3250e540b72782fbe9d76549e291108dfd200ef590a815d54185498b338a10185fda89c8811479ff62b0aa99dbd880ead58de12dd21b19c09fa1a9ff2051892c79c17c526ec6a44dd2dda1c64170794eb882a33cffe576d7e6bc208481027a591d88ce7e3672056c2d2ab97cc046eedf3c2ca738756b8212e1819cbd5c16091ac44052a0eb44813f8636b24231d54e8995fc27eebc13f7203c756c024b304f5426107a0f3ccd5c1da7dc697adc61bb1f73b348b1e1107cb9635ca428f33025b6fb03220584b0969b418e697093830b7e374e9b101d851b42c80fd0371b7c51bc0ffd53db8be0aa5bb8d58f52e62dcb9474f6d462829fd117b962b5afb1814d33ce7857d86c303f0195d899e26e363b59c82b37d0cfa29c515a96c05c0c555ad94dfb52ad367d48960a2d4b6bf357adf8be01e2188aea388cf2f05b5270f9f7f428002b2b29f60ff63d40480d30cc23cd5c27bb83050e9b7f3ae485ffa68723c61e29ac850a70262c5f8adda2eb2374aaf94b1b169ba8e7b0b761125a84e9e17f992759af0f1ffd9ec8ef95be9523011e99127e7d6140a426e4141dbf404b58c651ff94bb45c181a85ed24214e7f7bbc0481370eb6c168fd0f22388940eddadd960599a38ea50337a2a9628290b2ec40aeb6cb90ad080c1c3b9fc973b41e85d5915eaf4a668ec1de8d9a12f8ad98549139d116f4652d80fd540d4a04da7d85ac68664a90eacd345499d96c72caf8872ed680a5b50c51b19e0bc80fce10ea6e50ddd4a0b579d5521dbf871e99344ba926082aec0fafd10d34da455b09a323d6b2be95fb127b5b63613b22e8eef7eb87b4df949dee3f197ac912b36470eb754154c855b056f230702d5b37a62d95cc2d792714c946379a6ac2cc3cc8214d903a8394ce6175dcec4afb435a6a3e87ba5c57376851cee485c0481ea1547637667dee15ab1c67be1d0c89292664e841de5f1656a90ad04fec771bcd56e9007764167c1f45f66ecc1f6f3e4e2518e113d87c8e4926b2a3ddeb5a7d09a139f6abe70cf182398b9e02e0c44d8058576b06c5303e6f49116a2b99a783f19c9aaccd926009143bd26eab40659bb7ef8cd502b7be8423ff3ee3ca738f27a073ec46ab6be789c157d6952fdf6e760d1d65aed20b4f8b433386e7326e3c736398fde62fd582347e1bf7063fb33e87c68f5cdae66518d4c2bd22f38464cf92fb768998044cf67bd0a6bee224479722a34f37c703bd059c1cb5eae6b8bc480d580af626a8b6077f1474a1c593b20f75f430178d7e2eda4e93f2cc417e524f36436970eeb48beb64c00521e118e88bae00c718a5822ef1c0e44f0a4ad1eac5b8123c0901eb73973024f03429eb8059fa3118143fa4b426dd5adb900875a4e80731665039b82e09bf30b71fe92e3d260ec5ad3cee71ee480d28c69a85ac284d05617f8c05c8861565a3c0ed2a18f9094dcbf87c652dbdf039abed8ea7a185f222e2c97815fd2171931a8997ec9704f1a20b1b9eb6bba32c09afedc531290f2ac9957cfdcc68e75b3f26c1d90ae208b0529d479296172265738e3622261fb1a142e4f65ee6ad0b93d4a830e3149159953a8ae2cbcdc1c9100a51633144b0233ef67d1f684ac61cb60c647a9228753a9533b108dc7d83585e65f4429100db52773cdca60fdc5676807b2c29e4cfbb9374c8dc10e343d9c450bab9e4c0e0fc76a61dbf5cd75a1f3a16b37400f036a9ad6788b33392ebe7db4d6d8e62783b9d868b496484e79482da57da6a3ca8d09a0ad88c581cc68442104bd5cab9c27a1ba9038612f7d774ffc5d3fb28ee32825797e895363d1b7c401bd536c0e388cef100f08e27d75d56ac85fa4e0ba0c4ccc2ab2fb7221814e38ed048ba0a3b1fe20fb1861d45bcac37521a216c6af787360350f44d193f297a7c120311aecd4f4a5bc7b99ea9e908dc6c797e9bffeecc0045b9d16938f867323cd13b9c262f2aa772899fb246ad8dcd001a521f683ba62ddd0305a91321077554c4e7746a4a338a48cab08bac32d5109d78bf927ade143b154deb8dea8c30fc20675c551ab2a2279664349f42631fdb5a5c3a561123a9198ae945ac39c4327efd05d0d5dd68115d32e8ab2da58633e97ee61977bca4d52f29c4f01e38992ec211ff20beb0eaedf4c35475f982e3f31cafa82da8d50ea950ddde46712b23d6b4c62e19e0e855d574fd98c28a1882b0284069eefd8a4099b94d6b50c6e24debd2fa5f2a02ab6bae5c10c2b2a05c05f3ca80f254fd890860aec3a442f49c35681cb2595881a494ba56dbea34db1becf4fac2024447c70201d398f5682b915822f690e407d3c6107402a7f02f50fb04331c70b3397d1a0b03c083b574ab78ea15bf3c508712a33cfaf0e034940e709a2db66b558ad2328074b57fe1c6c1ba5868861d707ff9bbaee664283de8e41f99cb94a64f16b745866405207e7c4c841e3d902d828b3ca09689a443e6140544bfe0b59f93263d7695c41ac76aeb062feb5742c5390aa2ee52443a8a88e25280fa15ceecec44e87243d90171af93fd2757eca2722a3636945796c28d6a1a22f729aa6f6207a34286acb0c2c8d569fb004a5d63424fe79cb6c15d1311fb1899d7b457159895ec05c22d423bf614168001d7d84504291758beaefba84665321fb38a5c61a4ffbe893b9f7ac6e32b69f327eb495e02051464fdb1d2b4a443d8b1b505052e2aa18b8ae660e02ba19b1edc4f7514a84bdc81e20808b742f029e891601e12b95e538f620d1e9e96332ffe9636434a88f29ddb8cef0994a9ca541aa8fc589d1c68cf7541c856d5a827b309b5544fc20ab6075124509434ecc5a16183a836967ee53bc53173cfec21e659815b3b05a5466e7daf4a7973bfbd415294c1ef82aa33a006d9d4f235882fa48adba24add53a6f1cd5e9b7133d5ba23df6d8541740a7cccea9cec83b783bf614cf7640dab7cea718442a03ddffecca23b30efe244522166e863ddb9912d746b650e9243808e4c04cdfd1654a395049efd31c76d2f9cb1d01881f1c4263df0a119bde59ca279d85e23cacc60e30c56c1691f03ad389c38d63d6cfd43f53964260ed610600b63fdde9ee66ed3ba8aaf24b787af1384c9ead95515139f13f2f9fd42797f2ed381bf3817a9681aa4a8a79864013e966635a65d59090043afcaf254274d97b116d2df16274cdda682d3386a28e55452cf911381d2cac3ba07fb004339eb67bf1532676597fd00f6a79bc55e331069cfb77b8be986477031fd79b540d529601ff38ee9230f7674c7487ea436ee15313da24869638342e1c3d08b3dcba7b14447b7ec125647c3cf2c5546e45bba5c4451c9999425b9c48a9dc081a80d90c7811e6cae593544b9f84ba5f93c62e01ec903593c6a7829bf2520a8106f8fce58fdd271415947b6a50132de245731188b725066838f490b3f463fa483e213b5ee2e74618277a691177278aab4528b901ad764eb207147f70ca972b177ce88de4b50babc7610d3ac42e05b8ee7c1b0727ea9348dc8fa3e22c538c41bbadb48a5f6c1a71467568000093673e4649500cd95765497d1897e1f36207d5e144970149fe327884a987c15ed78b13f48c65c802d2733c7bef5e3c6e7ff9da7d072a717d0068884191b85adfbc7621de843948425a16a5ee3f64274e6d2583e89c6773c024fdaf360ede8913d7a8834b0546dd101c196305127687098a0fe1d1f12727463e848453f15a0f799f873960d36353f35898c7a600154b66bd0010eed898ed0537a38908289be2b500dd3b58e6066b1296b679100a8cdf6fef73c3ed76f3d53a99c0f2416fc465c9685c958dbc9c6e667a89cc24ab483d258899aef92d3cea39f3cd983fe9744df868b74fc68b045a89dcb44bd62d4f77997d0b4e97bf5365df0b694e829b325e689d8878c786e732fafc195c227f85ab4f2803bb19a702051574a5f058b746ff62bd64e36a9f19bbd88e8a6ebe9685381f76aef7516eddd92f0e26de040f1e30e99fb7ce1991f2a0b37f037961394f0e8aeeb0a19ab84b4257a257e419c0f497496cc68375b1e1348c6b69af3cbdaf292b474434c91289f01ae3b91fcda47c0e5102136418f71b37b9180827544d83d1c038f0a172c10528513cbf8606436a13ae1848c2b6623b346e65f30f8574571fe28fa7196e4151c8fd6c1f44c7dc900c3bdb93d13ea98f185e5bb9c02724c6fb394cc6e26ad0cb6e0e8c6c7cda5f53915715b37eabf64f7b7b899cab0ea5f510ac84372c007661f14cd3505f64732dc42e1fedb5e8baf47a6b9f44608e7e5b5f17a4c8335b5b65b5cc62e016ecabac3a6ff62b953ccd7e4c187732cc6e8206aef7d08f575d50c8d80de4bbf3bf81b49c72ef2ddc0b2cb7adbb86e0f6abf3a13eab615942580f553ca436fade7ac08893bc57e53544f221819c715215c89d0e83ca248fa98cf06379eac253d938f9aa1522c903ccd980100f1b939052ef9471c1e97c4a31328e17d09cdb6c7c8a4c82ea413ec7abde291aa1a3ecbe16a6ba53ccfb9558d9ff19421e1625a0196b79baa39bf9337d327c7d980442b34f5b34295a5658344965ec2018860f7841daffcf04776f9f1ca6152b4c98b2bc36ba3abc94f2bcf4ab2570d5fe012999123be1d5f90799f1ee075e4aeedfc8abccdfeefb0dd8f106ef426923143ac2fde916ac68480ef35d28ea4f293dc297e19cca4301c8b4aeeeaa8e63f9bb427e8d272ab1880e3c0d010a45b1e4fd3e081516bb13677927236d8bf1dac0fca5eff0706906802adda38ca6330c5a1c310557607d7724d2dd7e1566e56497026b4bd0cbe273824102dd4dc80749c02a495fea8007ae42703374c8d436b606af9c1acd233bdd209f39e3c36b2e57669b2405f6b683825a3b7fe54578da23db7f573c018d650ae2917d925929f2c47d1aee2731038b4fa2b31deb12fef669a232b8fdb1c595c600385c1adb2cd5b484b55267b7e53c0dc63098ed6d0dac39155bb00a2dfaddb57c3c258e05b923812b5f47d038b8909ca3ed80df7ce08ee1e812d9dcac0a4b011105f85cb037f2e556d13cb84dafdd7c0ba1134fa1f9c01686631281970c0cfc98c2f0c21b7d89ef51201c97a0fc9bc2306898bbd51168d82977d1c136370ee50d6b68bd1070218ca46c1c91fff78001e851d923fe9090c34fbdac82a7ac10188fd65311210384bac5ef0896250a4c021a1a13d7b6fab8718fa8f95e8eb2e9e5334a1b572160cda8296f304dace4cb0120ea8d56d4020e609099625c8953cab475048ef868e0a3cb13d20fafe9532f078fe5b4b613b89924f3c63f83ed3bf5cc678270b407e8307514ef3d89058da445d5ffbaaa507e1b60c4e3a69a4f5f01b12a058079e95df1b3ce7ed70389849e5beb0c5d4e56814a5607459851a57f31783c99859d986a718c7d503a103c2678e823e904f9defe09700d6ecec5835906f7c10532474e82966bac9526e4abe781398fd4b8f8e340a8c6935b24386849a164d51cd6fff7383cbd77d8991393588031d98ae211b3436e20351475ab2352ea99e107318d6467ed5a912d4788a3781824095473d905c93c9814b5c88ed23a1a3aedc6c3840521b32ae3f9372409cfd558447bcbd56c6ab912e4b62143c2d93d662d8343b1eca5e2de0ef63366a2ca5bbb9da494b15965ef34627aa35340395b931bb2130ac1905b89eb1a0a81d84e0a747b5eff9ab93838610585be7beb400c53dfbe5139971e9b4198daf5925602cc1f326cf48bd5c1684ed968d90deb892981b859c1965b3600b36468a553d6748e74814c1ece14c0190651c04093e887f299c082ef4e4fdbe56a0cd73c5258c94ee7fb264852f60bcfe1e633d1e212d438f8863181148158aa8b27d89811f4726cd3ee2565e370818a7dbd3c81084c3089dd3c3110e71e1631c55a7bf5ad40b310e3b4ece36cceddffcafe5f2997d0b9f122c9ccabc7022be455d994bcf0120d4fe8182feb8ce702ef8f4b38acd281d94e618ba1fe9f782b0e81f1677d9142c61f8ba9d153f4119b2619751b4adca0455f27dd34d62680737f5480c4c97eeef5c38f37c7b89994382c35cc23fa911ac798beccdfccfd258dc7ae1778d44003330da771b10907ed9b055191169902c43915bad2e6bac38fa78baec7b7e214f8864c883214a419aae09c6fe049eeb954ab3d4bb0e2e0475d74f37c994dc9e4ce30b9b2c55a4c0beff32698641ecb15ee4bd3d4785513f6a8242d4c69d6c56c756071bc38ca4bd5760288f42d1133866125c7e3458ca8ad036f672688741d3e1252074cbdf08c05bf14b1efff540bf549e0ccd1660c28fbb08c5d434ba0cbac0a1c17206a26a8e266f28051ce80fc7e7898972b188832a316380d236a117550db3f749700415de5058ca65bb248786c668e3e514b01cf0c3c5d14d3a238aca4fe98a84798608a836770c6183d99e8ee233f5e00edda2c294ae5ddadf1191cc1f34942e11f9fefcc31240efaa6ad7a4f61f5c3c2d5c35eac418b8c7080da1c4af0233cb10a0b851f12e44f937861914740b46d6a3a98a4b9961ba39c6709ba1fb14e34e149856f52609e07aa867f62213149ab091ca52724ba8a120eed232b8e65f8d90aa517a77a3dc72b3c0bae66d655f2c71c6cb84aeaaaf607117455b1a0bb016810f804608003626e6182015077b317ce5be114170b2909cfba44ef8c626c9c6d993dc19a8bd23b125928ac390044de9cf0eab2ca3922da2539622f3a068fe49cccf5e42fb39db64c228b1f611269ac654ea6f8272489ff53dd2711526c209d6667f7472431dd992bbb4654bf7fdf7bcd6e304b276b9b4aa20cdca0a43024959c20beceba773c9820a6121fe26b7bf8ac67e7530591051f790be5f8e8a9670d89da721973c894c16c521f03a12bafa0688e01577b228da14c7e485d748f38f7b1ffc0b463729fc1911f59666af96977613edec814ee3345fbb1c5db4a4fa7a9df2ffcea36266fa4b8274d489347c85f3ca183227816ed00e5c525e873af678138f0d000febcf10af03ea6461e9ac74bb98e5711740a30cacaee36132934eced014f3d653b1ccdc3c2f0f4c447489dc8b9186b59bd8cc56faebcd42284a125497b3d25beb1b3505d7b9c05dd860ef90a76c771db676f3ad66f81d4495a3247f2811ad47a1f8ad8b30905da319cab956b7abbec564df8746e163a39f8704a36a0786b260d0e2c15695c1ce72f17563be5b8b4c5f7a7235c444d703eb7b3de92c463859f4f1cd78ae7389882d0bbae6381c0963a2c8257bb1ddcd0136adcdf5cae38e10ba72f141cb7be7c2ea4d388048f2c639e520feb16393ecd82f5666c00eabec7303310bf01172de690c366a6e7a4ebd758b775689f86bb5485028a9941535a304ec29a0eba960768b7ab6bd04ab9023b9dc204bf4150b15308b8fb766da3990025bcf339c46dde1c8f2b9b41d61d4375a4c200ed43c94ef93c4e2a6cdd22bab164f223c029945dd3906275374fd4965937215b6e29534a3205ef042805f4042cb3b1d0130b7db29a930caab6bd7a9daafac5e89e76fd27c3c9a1445f8c5aed97d57a88c6643932590d4726abc956b0ed603f104ca8f265b67ebd2895d96ad042c681cdc5a8ac5ec2628c87bd954c8a6359266aa38cd1a34b973fec480943416d178e5825ec0435d6a44b9942f4a49d627a0e10429d356c8722a0637ffa39689af5ff16c9a55ad016f1d1110bd1928c2cf1df30615ce082ba462420017352fa7afd82b04316d89ef6f6a78323fa41232320415bc43529341f4fca28e37cf96f0ffce5169c4ceee10271739ae0dc74b911f2431819c205e2c83032cbc85db7ad24388e939c7495b0662a3b8e48654e6f6db1293d458d5162a1a5a62a4c9ef7867eb0569b62eb245642aca47af5f4143566f5c442ab2816fa56b715d42acc8a86981afaad6c2bdbcac9575253700dd6e0181e6a5b952d3568834fd17d82bcaad437b818ac754f3f075ee8f60d98e4e486d73c0aedcc928f17759b4a62a19a133865c6acb6400018d00f42d85aea25dac028659c738534060aca391413b7077e27a8bc3f7bc319aaf17fea6e366cd86c6c738682d2a57bf428a56cae045c8b6081e04b1374ecfd81ef3e707092bb37320edc1f8fe14ac7996166602dd4c1f95aa81f6881a36127c628a3d4e0e36105bcf73eec3595554c72500395dd0b7bd227085655a759cbe8552c647404dd00990102583fcf6331d10f52412ac8c6f9068f714c972a36c2424a29b986707b985bfdc51354347e7c1677ec1863437974972e635c62ce1e275c4c921a5b146b2c6001b304ac082e2c50ff7accc7fe04a0488d2d8a5e34112390f941fe46f72f1fd9efac84c1cac17ad6831f61df8f042abbb73fb00785ca20c09fb05f10a0acbac73ee063d6fd3531327b1e006aa0be038cb0ae0c21c3d6f702f46d1cf7ae50b0fa59fe0ddb059234a48ccccccccccc2ca3f31be81ce7ec6a00ac134d3c01b70b2c4d0019053f3c695458f09b33a22368b405be36463d3043d785c38504fd5a19d8159cd00d6c4c052f68104a247821a17d83151da3224b141433a8f6e089aecb6504ddb7c15a70b0d0392711745d2e1bddaffdc0892245d7e51a82ee63202a68841c36bed0fda64d9b7a41220ad6ca3ea6555e3e32628f6529a01f1bf1111b2d0c52b15715049514cb032e745d2e26ca347884065e6890ec09259a7d41bfb5cd499b591e78a1eb7275a1fb523fd26c4195041757dcd8c0be49b4b1b2ae96122a5b19581e44a1f26d30944d188db9a02884b3a9c8719ce4a4c7588ec042eea34a67752bba94fc1c47d96c89790f7e1321a1dfec687facd06f6d397c8b565667c90f5b207e8f6174c9304ad8052aa43e5c53c8312a1c9cf3b5e3d28c142b98bafbcc864c0a28e3e89b4977fb897a68b6397ac8c6f6f0efd74285947e0b889a17748b38ca183dbae4d616c5f7fd993f398f8bf9b047a58b4949a58cf2bdaea35bc44f5c5c287c0650fa7afd7b3964506071e9527292f3d85541e72afcc8d2a3092acbb81e7cafdddd63f104ce73d2a29b2f5b28fb7a7900747eb5a10c21ad376884eeeefe157a7777bf972e5dba74bb5b76ec6ee619377737f30658689eafe666ee4908b4051db3dec142d0db333eec757bcd43c70ef6ba0b8ffd915104fdbcda3a884461a13d27a5afd556d6fecee491f954d6b3964829638cd13061f5f149587d8c2dee116b17430004623debc55eb3623cecf5372b0944e8e779449146fcad2ea39865dd3ef89bdfc5ccbbdf7a166ced43f8dc1046e968f1a4ecc2d162c10a8d5e013960b82f567c7105911aaf90c28aa51a778770ba308104cd69ef7c50bdcf64a9fdaf336d6aff4ad5ee2188c4e5073335a31b7c83b983dd0c0f83a89c24dcab40535d56dd6140c3e1afa76f56dfa91baac4e0e5a8cc0e6d24911c2f553e28a145298a891c0225a40a2420843946d0a08a7e3311563254c9b10066a8a8336afc199fbaa9b10a227272a041b5ea5c8844fd8cc1715c99a9a39ca5269d32688ce07c7c5aa6aa88ab2a278621ad9dd7138e1116169bcb8622aba3560e43ba1acb0a9189e568d58484f3c1a8eba2447631127c689a4242c20aa2a6aa1842c295a729354448c8b2341503dace6b89d63d3bcd0c39c25465a6091112988cf8b586467768782dcdb8d951c62833f081d2ba5f9838c3c522135535464e7ac1e1a8c6bd559d354274a8501c263a2aa03875bf8091a203d5a9fb05cc151580e1f284baea7e01b344064cd056dd2f60c8fc97db9268040b5656ce562701bee127612bc782ded06f5f04b0f2f7c2231b51a4f550176b312fe6b1177dac60b47be27c45ff78a6898b1512e9871abf8df010676bda22788649aaabdc7a4a543bb1f5122aa2355c889048b6fd719da9727bb251517db36a9b05d97ac8b5c4e5e4c93525ca55e5c624b5e58c8dca45fb66d5360b9a512af77c726ccdb3ea03b0b2c329f44537284245b0451c0d1a757f831561020bd42fb7889ddda1430eaac1637f68a071beb68869cec44210d47f7776bc6374e65a8ce503f58fd1a1140b50cab7f4883ad43fcef6879be36251ca8f3bdf7b519d1bfd6b8399cbb833fbb3f825c738209fbbd1ef39ee1c77e72674778ffe12eed7c3658c5344aaf06d4c1ff5129e62ae6aa336c24d3c9573d8a1d5c73dd47d2fb59f9b7a68157330b687d947962871543f1e8f6f7cc6dd18c009fa4527b53f3a61a18f972a13b7455c632d0231a3d1b6ea296a0c0958a87d00995ef70c800dfd96aa851e378fd15f3c42bf1eb38f0a5b5d2505c2163190d9cd2748fe745057c60e79ddd354d46fd7d4fe4fa8ea981e17933232344a673dfe1c0f883958ab1c0fc8b1822b10b688a5229b0eeae8d46d03050152183a8c2b43c870e132e33057172ca9f2d76b04cac4fd87cb84d9b50c64236676f723a054100b357c8aba4d3d45dda0c210d56f46349bcd66b3d90a3235f483361b740a62a16e4939992fbcd577942c08b9830b34b9ae38ef3858ac2bae5bfdaa9b93151b817b7ac5838dc5f2c2cbe1d1dda3cf085c4822ab55952dc6f7a6e733e347e7b808a3374bc971124ea4b6b1522b45dda65ae9eba6aed24c68ea37abb7af6b5d536ab5adc38861a18d1e63f46efd593afbc3efc1ef563bbfce0231b350618c2d0f7e531d0f38e4a84b2334f8781ea3d39fb5d6e3a73fb39143070bcd08f3f5b8557dbaf77085976a18b38ad23400466867b10253bb71cf2385a3252bb051a10a1a19e1dcfb018525faed8d689fe091020a4423904c5483b9e56702e91c510da21a4435b626c56ae95246199db9e17250c67c1569384a19b955f7d0f31c5fd00821e4f6f6d8cc62c9282778dc9d8ac7aecabfebf1afc7421c3c4f5d16d2934ae973e4ab8810461c8f45256a7f74444b32b2e496438326fa412323205ff3f13c46ff800faed52d68499732cae8b345b570d116a60b0704d3c5d3bdfbce1582cadf10f6a8ecabeff759b59c89e81ee7b3628ccc1e29fd246d6483cff019a662a18fab180db3e1a60db0d763847e2cc5523ce56dc49061b132847cc41f0a6ae458571637dce82e6b9b1d1e73ee60ef032a4c3a2da874c778631d98d1243417023ed70863f4e89099ab4b45c668f73057d7c5c31eef53c961a505aac760b1e48747441532a96728b3c1b19dd131909225334320c8524ae66676ba72d9730daac77ac4246696c1ef497b366df5d3c89a25f8a8fdeefce6618d6f06df659c5cdcee7e81830d036bb2d0c7fdc63e66c8c19c9d9d9d5d46f7f61c58dcbbbb3d2264d93d1d638c13896500281af7e5c3e52407e4fbdf683fc7391786c1d240080a7737f62f8017d8065c719268d21554e8b29061b5785af85f4a052929a3a6b29385b6ea4205198244901fb6b9bb0cfbbcba905bf6bac7637428fccfed71b09ff399bd4c99c25f5890b7c53bcce3b32177b9b59cc2d486bc69411627cc3cb9c1452a3fdda232bf6c282e191919f130d43c3e4135eab74e61766ad12c9b75032580fac12ab57fe196a0fa41272818664c6d3885864d6de8068853fdd88898faf11227eac74ea6d4a81f5761e22db59d188ac37041ab41b57f522e9c6affab8b26b5ffbb4853fbbd599593da4f54d5a6f6db88c197da9f4387174d6a3f0f15bc58aafd1b88a1f6b740822f90187d41e6c88d0e34375a24d5c040525a0203872630d8d89c9ea2c2a8ba4925b15fde88a9fdb0aa072323314757683fda24c190446d8a520c6e969a9a6a3fdb160a8813940c4e6ead96ad2e172852f5eb6ed95a43540ef453cbe9066c39d575e160d84df97c716154a64b1b281bb40122892edcc8f795ce4205a85129070c7d99b3b13d1ba99723319bf18c6594326600069b2d4f663cd36172e3a1c80d882046d567f68ac2c25ad9a7b3348d7632640155d18f917a88f541ed67da166df7bbabf76fc1e0514323031441f93f3ee2261eb25dece31d621f43a9a1369297aef6a4abd8475f1f7cf200baf981872ca9d9e10c14a91a6e8cd4dcb43fcd46b0cab585c2ff6095ce9215ff62529551f7a3d1166d4739ff84ee67cc851e76f4cf7781bf8b27b8d03f63375cf0bac7bf7b1766dde33f1f7eb3e7f5108d1db1a5b1637eff8ccdee81557f1faffe3c56e0211e5a109b25d5dfc9fd39195fe6fc4e158486afc7684480bf633e7af0f7d1cfaf1dabe7e7980ffe558cee6b670ffdbc017d88b59302f7c4f77a8815dba9eb10ba4b97ee4b2c34b75b029368af14d88bb78fde7804a5a132e965ad87f89b047372d58475c044bf176b86d4972dc6dc966a844d3542a7c9097daf14e2abc0429e26d6f8df43fdf1a58d0323030b1832aa509ac512c59962e7294a46923543c8385ab3b446c9c682b1e76566b8ddddceb0ee6eeeeee62582993dc6e8edeece0cabdbdd8aae12be7280df67c702c891dbdd63c710ea0160b0d3396297583175bfceb950090abf5b9dbac1eca10780fd37d7e97c0fed8d7dda2869637ff8d76603c7cae4dc6547f7e8eeee4210c20819026d4308bb074777778f9ebb7b373919bf17ea42a0fd5983500a84522094e2d1a394125d4a81508afc95fe90ebc8c98e5c6c22471e3d4a29d1a5148feed16cb4e852ac40111672d7918b524a29b919a10890adbc66d0624038c434d8c3af040af57f8ce53583c6801fd3daf6b2616bd1edbacf40f1595d0745e80a93d885c3634c8b81722eff8ea5ffe45e1028ce8898c6b48e9569fbc3efd2c66bc662af014c81b00585248543afeee18fabfb7145d881406c6bd16c345a123b415a20fe2a532acfb03fbc43a3ad3a9801164474e65ece1138e8300dfe8ce88aa90c98c6ab8747472ca93913322d55b7c3533b59ed7c6a57bfa0dafd22d5ce4bedbaa7b7dafd2b4d0eb5fb4fa3a676efa9f9a1763fa31a52bb9f2e1c1dd19c5c111c27ab9cab7e3e952ba2721c1640540e0b302a876688ca6501e48a2eb8dde8015054a42affd6a0a89c994b628830dc99b441051e4550a2b93fec85aac00347c8adc6e589886633f87ac1dda53250e8d1dd3d762c7963bbb7d0f2b8bb7764d9eaeeee5e085bddddddf37ba1e244a192713ad775773bd5f081ddc1acdb7b3e146015e8cabd8ed705aa731042d83b7c822c6445d97fe7d1238f1dec31cfabe5ea1d4a77e868b1375b6e20b7c7990e0b4634e5be3e7b458d4d6a1360bdf82c69c2026af733961fd47842edd81280025c0ca24072ad6ed6d60a419f997744bbd58a1565ff1debe716753c5de4b589785e2d57ef50eac209e21e9ecd3a46570938adb945ddec98556539c9ecdc3326632e1cdcccccdeb284e5584e2073dc2e17f61f59724aaeb9c9b383dd5cf9b3d0ce9db3cbc3f9afd9eb5f0f458507a818398421d3218a0e69aaff0c287260dac085c35b709312c59418aaff6bca98eaff72e1f07777e979abbabbbb03004a69125f468c6ab4d09da2014409e2449431fe19bc7b0d5b6c41630c004a923050caa011801351103de9010a1c8454c741071cdcd49ea069a6101354ddc9069a0d553734e184290547aaff8c484a75a814b8c959410d567880c324c6938caa23511d2a0989ea3548a9fe2e170e8f31c6b864886cd174e3c2cc953137620d3562b154638c31c62825c2c7535d56dda7facfa045f533504ba06002447528266daa43351153a168e0a1e604c1c48e92284cb099a14bf55fadd1d910907e80528254fde98b4c08f5dba1814132aa9e6385c683575d05323740d5e713266114f9a2054b52d59b956f51fd766afc36112a09971ae3bf604318a5355982148962ca0da7812a890ef41bd0a19028ad0b9a60497164861f5bf57f7df9160c1c809974e6701c57436a9dc1b9c2dae9f3ca8d7bd6015728114d565a945a459e50d231d95845ac489a586aab1f50b82c525d141a240b96e60f4d225315a74412df79311d750f9923ed002c51110b24bcf3caa2d43d3ba18cc4e0880f35a823418e38c08728fafa9d2c2ff85ab3441052a0c9e00a4a4388113446dd2f64a2d4efb520c33403192f65a8a84235a8fb854cd5ceeb83ba5fa64062a4fb19a76b0ced7ebe8cd0cf8b2fdba3356a7cefdce5ac154972d9bd0d8e1911ab7b2cd41e7b3e6c226b29420d41431352bf8de2a4a0882831bb60d0a8caffaeeb3ad7b3bc05e30b600c853c5e6cfd36ab8b7d3d560fbf83c918fc0ed6170bb986a8eb7b55d7efba5cadd83759b1af475cb92000eb629ffb204016b40755e7d4d4dd833dff30dce31d05bdca1f0707fab5a89a7aa893ba878985e0df6e51514f4f365b53d30cf796aaee1d55ddbda95c89c7dfe8b97b17a91a942caa6c579ee062a4090c4abcb822c90c9124649808d105cd90b894113414a698fa8206c5140bdcc825475574fc080a8bb64112499e4c11c2840aa6663842a245d055a8dd8af871a44d112d5249182f3c49424452153b60614510242ea6e73c45e8f79aecb6b601221172c311f2551323429e9670d412c30b162243985b91a5a8224c34215626ed322d90c2889a268c9c18934488137777774e729cc338c903039d81850cc960d5a45fb84149073260806881a484a4fabffcd185bb03e068d64393bbaf9851c39921b298b2c3174ef248999d85e213cc50fd3873268b911c8c6c1341c050115c86cacc4cae5853a58d151954ac6862470d9b17c4d0924514394eaca96d704333840c28aa2c1c9c49b385979cb9bbbb5c5ca9fb414423e8d8ddf78dcd9ea2709f27e5270ed7b93f32f3ee72961947ec10216bc611d421ec8405bc79f3e6cd8df92c87edee98b11add1d3ae52186bb3b4308934120d83caea4394a01a0a83f74ee5dfe748f2fe194d1a57cc6da999a626f91a8a08a51351082b6bbfbb7ddcd42372d004cd0ee9e5272dc199dc6199595f3b17fdc814010b6fe31b6fb43bd6342f159cc321669651b0299a6a894adba8cd4b1d100080053160000200c0685c30181409aa88134b60f1400115a80427c5a3619c763418cc3300aa218800108310611638c210421a610111e75f981451cc4099e59853b1abc58b45cf63d339aa5ea852ad448e43b8d6ed186b1fcad85e08d4dc5bbce427949c70002f11a255ae68914ba7c3e195f9bf7713de11ff974e5b4aa45c4b0d742cde91c86af6e818ea30e1e0105a389695b35c2f47d4c320cbb6e11d47249fe4da06793aaa541913fe04275cf05aac4990925861d1ba5817496f05eec82382fbea1692cc07684f445f4769204b913224b130ebbe6fd88b3a36554e05abce84a227498981d6f7bce573538d3b2b4b7fc39daf2f31e36465fd36bb4b077891c9d3696f994f7feb8a30f360faeba432c88ea02d4d5acf13be068a4f5a3ffa7ea1ceddb8e8ddbdba96dd4ef38ccbd61e0db4b744e47301536eacf31809087c7c7ac3482a2864177cf5cbad1c60e5a240cd468c8eae9ea7bab3b2fe0fc582632256b77ca82640d5ad555256a263e563f429837b983818dc07625ded0d0941b05f30cec1d458387dfff9dd1706b3db848c808d5d6facbd9cbfe57c4ed0ba9e4830d9456c57006b3ba51557e21eee864424c556588aea1408f35cee3f454b64fa02933cf4ac925e659660e0d4c81075afb1649d7aa2fe1a0e1c567b1c9e59433d9712cf57180c07acd480716770579a2a98d08c8427356e833b4e13b3d0bfb9e0878d40ce9a3363cc574771c48f2b577150ea0eb163dcb7aa6f83fe9da45ae50a97344fc637bb2c64f44a88228c09f16d2e5a7cb666e5d0ee92b3972dc816a9ed0d64eb030d79e0d4c7bd7e858233240a6a8611f71b2616c019101e257ed917a9b2a90e7c519138a75dda7204026002489cd8755b41a1e7c4e8d12c32f236a8668607d15c24165bc138ab1b941768a95c278eb8fdb0d2db215060229717a70a510a8d8b0d5f4822547d6f0952755fd2c276c1b8d5e1040e0f22c0c98be1ce58a3568145d4edbe110902736584fa6c2788dc5a7c25d50caf419dcd1630f4c52e5f949006f5a024cf17e10dca9f6e6f11f46f83aa138a5316b97926e29d3c5531e18f24a47d84d80de4972a1263675da5cd44269cd7936a1acc6bf8be0d6fcd52bda276df08cec518190fd0e2f7eb50f52bd2341dd79a49745d363bb48709a2dcbdc3d9c0317dbea3b988a57805965587fa39e69a2a54638aaec8dc6dc5d81ed50506a339591a05469f6a406f11fc2e226442cf40a9a34a7d0d992f84688bc22a48c49f173729315410efdda53fc1c7fad457bb68ced758f476cf9c8f0fcf45aa19f072855185d082536569194e649a977724cdbe043914e4b8b45170737758dd37276ffd5dc8831cb335bf18adb887a9f61c22e1734849175eaeca4af8f5c8fc88af74038f33c73ac0d4042ef1086bbc71472c5c31f4476dd1b2a187529ed1d1cba7d8dcea83d3bf677bbe38cccc51c4b27ae6083e5725e9518662ef028dfbf4c37e1037c2b3ba93f64804e9168648df62eff1380b6351943cec710d0fdaab2af7bffc90acf089f35ffc8b85dd105199fc41cd858bf70d42a59ccde8f8f0b63bb8619aba054aacb9b4e4518caf7c9770b153328253a678ca865967a7c476b33a709efa1b164fa7efcf311f2db0f1f204c2c9440314b59ed90d9631aa0652b1c4ab64c39e1e0fa4e38d7f76a38276050d03a7cf4fa5826f5a923c3ab467b802958e7fc4d5e38e95b05558f69d827c39a478e6b03a077d1b2eb9979f3dd9fd30b2444530285bb4ad38ab82171d7dcc93bef6cfa8580eabd88ee415d9f58f4627b65ee0021c3b248079c03fc7286362690c06c15416a2ac09b76ee1c698640a61de511e008b507c46a6714d49d10332053bedda1bcc3fa62f164cef3bc216fdc8efac20de86015c76635e99138cfa8f589db0801c03e9c57b23fc89a285534b1a2469299c444403f91d1c5cb9579541e3efb095e17e66a60eae48234026748504c8db4aae81d67f2551eaf13fd74e4d74d0c7c3ec5dce658ad8407451a0059f6cf74ef51c14ca9d877eb2456c90e9a9759d43ac304a94e99f5efe2535d10a0fee3fc80230575834b6c0a98070198fd8bd01a05caf52d202d460c2f5e25f5dbbf81db81b41b48989761686d61630b07403e834717730ac41e172c737d89611a246cdf8dd4c33f87ce8266e2864cc10c5b4de0a2863e3b76e85ff222dfb040f3c8ba3b1986905da0c23eabf28c81ee0065f65244e49bfcd26959f1aa4e569d01b5e1ae0d9c9c2b0edffa4f495784efceb80d385e225b8f4f507a4655c7b0141445eda3c30ec6edbd1cc74532083534509899f52fe055651c730affbd0d85f9c5784ed05374bea2d282bd9ea85e1b42b76453cfb614b05ee9e86ae0b2f2b1efe15bfb4c18aa592236551199db4be3dee0af527949e2eb0131ae11bca465bcb41aa3f41f78b5aeda853599f5ab2c3291ad62fff4deaa1f3af478acb08c782d9c5770e2ea736c38094f91a2affd6936035f610afc8bfdb19b40163740ec8d5702111f5072393c1422d3f64f15e94bbc3dd0a884126e387d840848658b88d888d638bfc7442a65c97499dc7dec2dce11cce1e59be47b1f820c646d63aab4b12069d535b4da24d19eaa37722b8de0abc682798722890ebf4b8d51a9644d96bc584c5e641ef0490e1c1b84d378fdd3467356e41703a7c86c24959318906f6054f3910cb314f849c4ae883832e27886e0c3f7c137ce17c7dced7abbc4a674ce30f3d85e13bd62d8dd454cedaeb913cbe52627179f15afe721df9bad493525d534eff6f77ad0abd94a6b9742e63cdbd9ef316fcd36a9e94b74ca0f67f64467e3802f7cfdc4faf38406603b52529d4da944bb06f073ce9d3c1e9f9067836930af7aec69a266e48f3d3686d08a723efce7ddb80201a3125018fe9ceb279c1361d934a1de105e4b0742accf77725c8ef9c4b8672679e65919db6132394173a90885f875d60a8660c3d8b828569c74ba5609d13de1535b61d07d63d857dd7c2438bab92ffa50ecc7d12cddd791c9726e3b30ce2d191a7ea1c31d0648acac0d2d4d857b6a59aa6422147e35f1b2cf84bd2448bc00cb04455df09b550e25cb8475bd98cfafc854e16b148f10a4a44e1a55269995d4ffb12830be03a5536249404e3518a3542185986e03652eea3782619286e289131fd2436883251a14983c4062327e3ca09e60f27d1b3c84f11e61095081bd004a4168d841afc68b6444ba650f12ceb9d84a33af744475d6b907c5e915bce9b8f5abc0ca5c525e42155ce819363340a6accc5bee1b5d11d3a25d3362d7466b1b9944987460ebb8f0143a64feedda734da26c489ea5e7ec780af0fd58bf47bf012127482ca3b7d1c75545d6a4e9780449cb20d9e66b83e6c14b5853d013c2037c34e0e6125777e3a88a23eb26d3d3a8bb0db7d8ec59d43244af996622793c64e62ab7cd90d6ac807c88318f45de408cd494d899fecc69f660f460cdb44a62a7e44d7968e0593f9330802875c707f64d8e3497484d47ebee8e91a4cc776b186f9e27c8dcb47e33040279e5af69de092c7778f74e3ecba87efc7e3fcafe4c0c94eb844f80408e712fd674128167b0b5812586c63f9e617115eb031a3b4b6700e1bcf51324f13319a6465fb05b80c43cadd31130c5c8f3f38bc21e290bd54b3eca42ca8b6be342ca2a621a84802251682603745bb441bd04068ac426dfc2cf4ef27b2712f1fc457acaacfb9d25a9bc307e00479d6522443d9a0ccdfe77de3d592f219f5e0ab250b1f2d2e4dc25cb6b3eaa80d784666a3ffdf25839ac4037b7e85852087052deacca052d172e7cc908b63ea598ce53b59623927fa301738c00344c5091287753d2eb64ae62ff8ff550b07e4077367b38e29174c8ce525aa3af7937a01dd80dc033c932fd08cfe742ce8151598c0c1af34a73acc849bfc52fdb6b4104e025a0eb65785037134e6c51e5a51e2ce1ac1a2466cde452c265b9d1f4b0db1820dbb2a34b9d6cb2f12b6d7d7fa998fad2ef4ac68965e8c83fc360f1029061ba7b6506983721c8369cbb8035f2f1c86cda0cb14ddec29a01483753ae08b794256681feeb0a135435f863119a10397cf05834e13d18b3f1c9ce8d94db88dad8c762c9a494dcb7c9fa5874ecefe543c9202115019450c1ebc4e76db369b05b5028b7e698b693e0ef566bad97c11bb6ba14dd80f8918a9327377419c488aa19dfe2a5c89674897b8b3258e3969d14b6bad1dd7620cd42ce80a6188bb45c8dd60cac486f2e5737ce8e38af5cda6c1dbec26a71507cb771e4188334b4d68f5d5ee7b9e67e06c5febb4bb0102ca4fb3e0ded265effaa11ca98a04e1475ffac3ced828dede66d4235d87d6919ffe3b14ef843fe19f30e76d51de9e9cedd62bf0afd67e7177f4c98afac30818f1a2c774ec4a9ff53005300a349a3a91d63e42ec536526f8e05f2a97c6b1380c28facdf340c56d192c84973226df012e8606225a8bc202841ea9748403d4c3b462b1c6d97019542f56c330846f0e1e487e01c4c1f640c714701960ab9ee01330bf9a2009603b9ee00330079a201cb86dcee00330179a200cb875c1ffc4c1d494a4a01ebaaab848263b5201d4f5d047d834e516de3008c4d60d0dc47f77d10601340caa50c03c7b202ee7571d49a8a2cb8bd8c02ab44f263e6b0ea5105c22be2dd517b377b407928aac396a784a1ec1765ec3dbdd8fa29d2da63ce798ec5fd2318c72c2c75d320a21854ed9efa53b930ebf64ad43e3b8f115ed583c107ff9cbc040b4ab192011cf3c0b1dc8ea6a3c32e1480c0848dd796e03007ef87045a80cac00cf8d633de764a854c411067bab5672d20c17fbc25a696435ddd03c404ce85af4ccd671115245942e57c48c7f9f5029ce8d614e19ee30ef6c757d34134ceb2952d64e4375c85eb71ebc9931c262aed672e59256b351e9536425091a636bbeb752f628d1be5b2958bbb08e78041684094721104b65b78592a96062931be31fdedabfe07c042d13aed36769d3c1a0626eb0e573b686a8134f42e503b1d82bf53f0876218020204d070af3c0e3640b080a7382d857c0247bef6fc3801cacb8ae8caf6cd1ca071cc1b25f6c27116601ec6446a5298fe59693b2078d6087e9667a40aa3a25d83464ae43edb28a93470a220e23452e4d934cbdb7a8c6df92e8accad5608377ca67212d2d7ebdacd3a6fb1023d5064dbf1094f062aaeb75e881125a76460480f726314466f118ac45a0d6da91fa8ca971c31a6624ee5e207154cdfc590ad6ee1eaae212dd1cea777d037c3087445b1832f1a121c0d04f0c1f496aa45aafdd6770b87cab1c9086836df0ab03a16e98d7b375e21a4bf7f41d187e6aaaabb96c16d32bace6bbaca20a2d935b1fb30e78a502be0c1ce11962eaf4a25ecb4c191c1c2c7c8e7a1c2e9a5d0b533b498ccab83d8355c4cdfd5379d7159638bfd57ffea1c8e2fe900815ffc20c8a134bae1b1a3693c5bd03f65884df1d89fda934c62b054d4f68a0c145b2ea890662da436171353d8efa49d0797dd8106c354cc991537bbee0319429d95f3f134719336b4ca47e3d44d8373a50a37f300c6ddf2ef527338f014856dcbc830da4db524d05f3c0ef25ec0b7a964d204eb4f604adc8936e1b0bf8d4658c230ff9b2acd642cd0dc79b68f28045ce8fca2f36d2465d387ca34ff590bb52877fc39348c68a24327990333d00de74b3b4834a69dad1e74d7dd2024f9df8084bfb914d3808efdb4bafb2900021a24388b5c45335c459083f419b9797945430dd6129aaf6add04d5c65327dd24f995e2d22a4864178fd4ed136102aebcda6201a59140caad4812c5578555fe34ba5fdd1a59ea3605b907818bedcb94c69c3ba3bec41d54b29e58c155d5073ffccad80be7c09f2d755090c9c93ebb35cbe5017ac2944a5fd3171b62049a65c2a4dd0d91dc8e6fbefe6447b8f78642c7b9955bc2887a847ea23002e1a80767d60c8bcf45a4d004e6287b840aeb68b78cf51d605dc457891bf771f06d6d7f3d584dac98eb5ddb96c7f5f4d1256282a06d39656babe588295d8b122d578c967e0402c01efed26edc358b26944fac9590f06884ea4e9c3c3a4d770167b6999997f2fcc43d6befcc8aa291a24149dfea17bd7a3c491f68e38364d8a6c9d0e8b7f63adb628dae65198169af833885efdac645dc8ef66466e207d829811e0416ca8efa86bb28539354dfe003f3e93bee09f41c4c8976167cd4b854467daf6fad627ea7b5583121465f75ec7550100b8d8039a52802029791997da738c4b1812ae82020b39cde02b4de26dd501669b5332c80d0e9279721635eb8f50a462e5b2b9f110eb817dec4caae4f766eaac3a08ebd69d09fd46515735f7e417ddcd9ba73982da2064f415aa5071235185edeb068195153613f1cb5c4c7a7b261743de29c9c965c342d2068c637122808079a1d7c70e02f7ba49e61d12289c8d1a5b86254c4b527ff3862cdc980f5d907c520400872b1025e75a68a09ae19235037542d228b68175095932394a860cf01cb234cda77573284e3027957a7d349281601a5b5d5534997f1e074a93b6c110726c5febac236adbf276a37f63e8a52d9f4783928c3dc67f2c0ca9ce4c31c14d45052ca385d89f3fb11d004e965ea7a4ac701d93b08ed24c967a304a0be021e49aa18d905790c8dc9bcfb9556380e326bc18f4fc1982d10872f882da106c97a71d993b9f5413031fc4e9f02b78d53461591845c92ce421e44653c0b27613b236f4c0a0a8455f83ab3a75f16a04860bb2255618c91c46725d6dcd0b4b60a6599c0a6fd5382f407b77f0037bc453286451d5bf8948218886368534c79b702c1df12ed6e6862d1516c2e6f946da95377e48d8ba544859137b0ffd7065379c02d75f306054ae98ef0b92a750b69ebf93583b2338f932ac7e3cef4518d64d3509500a8c9ee28dcff756bc28a47280984548b5a1400bdd60f1a1d66b0e3572f4bacfd56fd96db9d6489361d121f1e855a757bae50ced7ae99c1257bc02c7911058d46f6ed96db717229d88e3bfef92133f9f9888cff350b75bad2ff5a4bab88e478c3bfa602122667fbd7742bf5197ff5037b5f5b1c9bd9ebd5cbbec6807c289f87c994cd82ffe4dbfadd964095eceb16bb41780f40bffb7b6dd0ccbdb6fcf6b58718b4d7f474ae47ff7085cdb4c2254abbb6364148d741dd63692e3ec146dfcde511f429ed8dbfa93a784188a1c02783dfb2484614c694ca7b0b25c1995301f150f5fb84f9c3c09e3c3a9ed63ed9a6e2769702f9d0e8da2e524dae876ee7e2cb039b3951ae09fce6875af9456ba3c8ea216fe0cd3b822d7b653548962b62e06cd1cdee2ae62531cd3b60843389eeb1589493e3c550b16bca43931d74bc824234ad2d07ade83966d1aef427f2f4a9faaff326d146e9b1fc9c7145e8bdfcc6b81e9e6b94a247ae824e3cf8bebc58039f3f1af242ef8fcccb3dbe75212028c4d30b03764da7408e2047b499de9b1f625acefc89649387210dd8aa96813e19513d43ef3f2f4ea8972d40d8139cbd34c716c607a356f08ffee08d2d907f78d26ea5916c69d04558b58cc6663464e5b4a8cbeae22b05e8c81c486d43f896eb1762e11e0eb0799bd62687f11e98fa68d616c11f0ad69f3bd8d7f10bfefbc153f64c6a6f9548e11472e650649b6aa8ca437cb9b76f27d7e0f6df561db11661899c287b6429dd35393915d92bd7059a730dcca0e16948f6b6cc06f4fcea32653e88b7f7ba14732ebde082a8e02c8b0496cc7a7c9f1d4dc12c9b0a6c793e1a124eba4990ea63f2e6c121891ada1c7c455fa1ec2f02622761b8747d412619851af567c91a43333616e01165bc9b0e9751ea4f36fe264cddf296328491f8ea382c09523402b5e318d2af8aed68206627a580dc6e5d04f54df6cf17a75223612960cb0efb39c80b72e5557014b9e6a37a02ce8c05e6d79d97faa540c83e1c7c1d52296cc263b5ee75f4a76724da7926c6560ebcde61216407e5af14787523254581d358957448f9406a555fac9d1f4b99ade2eb7931982eb0ab852a13063b7efcb76cfbdea452ded11bd3617950736fef05e684c6e7ddb0a395c0db80c5a8e2b568c49d3af847bbc88fb2a2a38996e55662b487679ef49ff54effc6a1012173f3d0444bba24317c7f7bab2e6a097c3fef689bea75bc268e62328ff2824f4ae3bbd15f5cf3ae7e34f651494dd6bbaae6b0337b93046161b7c2f4a6085793c9e7536d40575b5cdc7e1f7a65acf408c46f9e89f26f35540616d01b10f5d90427a2e8631a90f5f3a7dc7a354fdb7b0a85a74adbc820ea9e33e208de2495260b46f82cbc82aa8de9d0c600a5618e9e51cee8ada6f20f8939f00ff27627fbbb17cdf5b15f16bdf976125a64be5beafa46542a091598c9171fa9a6b0ad37839c51efb1d49c929f81498c2a9706fff988c15c134c918031d48763a399dcbdc8433e1c029ac3bc45495a6dab10211fe59bcd3411a50a46d0de5d1f435a99f2ced0b7091ba29a9fbe45a0c2c4ad6e3d70ba88fd841c6fe904eae6c81938817c139970d1a2d9c3d2e9d42eea1695e5c207b1adf58167f6aa5987d01d81c0239ad6ecfea5993ae24efd35830a598c44ee5f662e47688cc1d21ac241b75d1521786d3a1c376ebb7a04f2c5253017a93815a808cf0424bc837c4a975b30d12d51a611045e1d61d26f2afb4625f42786f5bcbfd4fc58d24db78fa869452c42c909a5ade3958b44406e057cf90ff49bde70be3808f5dcf0db25907ac7d5dbd001740736cd7eec35075cef64f439493c77141e08422df8abaa94726316f058cd4979cde96ae3d980e2e697ae2ab27ffd8bcc3850643cad181e35ee5691b1e50e751e0802fc60c122e50cb46ae1869683e1f3e60a885c4a54ececd9083e21901a938f5042692addfd3799d1834601b72a5729b7f620fcd86680c7bbad7c7115a2099f71a47a8db0f2b9482fdfdc0d3d6a44f64773e033e3f6369f8deae10b41a8715b4a6f9453a6d3db64021d1e617f9e173e1c6e95b6f5fd54bcfd1fcbcc02364e91e242f8fcd335d803d20a1d3db535133a83b2302bd8b44dfcb7ef3354204c2bc5dbccd05cba0189251ae8e84762e61e0d3956b45e51d39085ae63917f9e5fe8836f81f512741314acfa766cf21d51beba4fa1630501479c65debce709abf50c81eeee2de4e00605a071769805b8e2581be09cf840703386efe89c8cbb7bcfe3699304002395cad2e9de66df84a02b26980d82666c0dd5457a8cccc4c28ab88ff0ec9ad6b3c963ed1d6135c2774db0cd861775222c1e5b44a40c64151bcdfd971df05b7e2441555a0b9a09119cb3061bf34eb724b58b4d522ca5422ff0b55520011474362f542ba65d4e365641400b47dec7e6ac363d966c6a7de1f7204d271e0535624d3f16d9b2ba8dba218a4232d295add17b45c2723d09cef6e3225019e8bee4bbd642d04cdacc81a60ac47e0d95db8f6a66618c8e4a06ce4c84d57c137de5884fa322e0e2ab6471bf39ec2b22d8d2a81959d9000597fb16c212ae2603d7412cfd039fa7ccfc7d9207c8f5039b234842792736618e66a0b09115c0926fa09ccf063bd05e896cd55eb8f9b2ba5bd961549300c24dd6285e29aa8d2164a73bbc6c041938957bf72560dfd77150eda4934cd9130b386eb44af3b3882a8ec4a8ae013dac03e9184e3c6c851c4722d3a4833518487233c6eaa01913439f603d5f626568cb5606aa079fbfaa3f8588b7890a772ccd012382db3a221060d81850fc4bbba807a47158f4951dd4590e8b3c4a4d8fe77d84f60ed822eae635ccc83a3dc0936d23dc68ee4aa0902dbdbf80d3cb08c549b208c29f9239d4e5807b989527c1f51b135ae941a5e144d6b4392e166cbd5bac7c4227e0254a7af79afc433089d57cfc1bef8f56b403dcc28d6b0c0d56a044ee6aed730faecee0c115524e1e2cf322980b72cdf5155c56627f9dd3d1e3052125f24e6ca2238b4cb668ae82d2909c1d043c72bfbe1dc1844a7fadb9fa2fcea3aa9b47784d460e1f411ee95dfa73f617c243e45e3d8e0d1719b13220394c806aedcd384c9d3f80f1218cf1bd52435e6be0b90e3d83510b736a38edb42ef89847df99bcec8e9849fd67251b6e66680ff0b4bce3aabfa529a7055e111aad5ed7f4aa1dc1b71415e77422f4468310f2864af8bd2a8b35545165d0c44304d9ea5ed0724015df0e00525546180598e2c30020bb79199a933da29e5737303187d47528ad76e9b577ed13f0611c159e30d2bed0fd8dc9f91a0f00853856da62330ddc45c01969f6df14b1762df0ec2980a0c2ab118de0f9673f9d28031d6f4a6d2ba16344a613820e93d132c1ecaed26f3c6229f518326d34051349789ee8d028ef66806037044ecd28c2aaa0b142cc1dd48a0a2cb9ff22f881dda73b10c89e7c9a2b6fe567412549529e6af19e641b6e3556d5d964d7d6aaf6bc843a179b34b2424619e16372d31a0b2e0f486c1a23431397ac20c02f11ef5ed468ef12f16e4ba92b99bf28e029953e19c7864cc90835d0b802a5e1261d0fe15c7a3a683f50bec72cd9ee4c3a5970acdb09194f4e3a912116167f1404d833944247b44ed863d3d6c7988b4158c68fb68db87774de1bf6d4cd0335ed5a64d005ea40c8c829afa8214251806b023b4863466c980c7d4e664f802c0a53badeea3777278054933b51a2d070bdd93f4d1ca9801cfddbf402efde3de6de175c6f06fe41d28659588a3c4ced994dde075f0d36bbe8e9f681a1a8c8c21240c10201ce052a87c488c0ba552015d5de33f4dca4db4a7a636a5a667a380afab98b0092e4dee52f8e9148a9bef873b704b98ee09463ed5ea52e46f40ca9a7e475a974a7b690cc08d081bed99d0a715f1f5d7b50604d1d420e2433e7ea15647d5d623d6eb194094517a0d056f9c9067f591c5eee5fa0295ef8b8680a6d77fb5cb3284d2e669eedb3aa105ffad86b10c3c0bc8c6e6e06d11f6ecd1752468d6fa16214c8480e746183413e62feff402bf6e443f6ff3825294a9b35acef54e7fee1c7bc07d9c881a585dcc4da53f19805d260f4160f99665645d7c892944ad25039f53c90e4b982308099cfe4831ce4e0e8468c1406c3dacecef79cf994d712846162eb75d1b548907c9ee5f57be83ab39dd022a9904da689e75be7fb080a50ff750c0d5771d9a5a8a993fcd60102c49edb87c3576fc89310213de499606a181e1e9b4da6d923768713e93a0d6efdab3fee982240b710a8e3c89c944d0ee1b06e6f332947d9f442a2571d5a743d9e336b21ebbec5b40619adc2be20cca99aeb468cd31033334c1a47f70e54c54d0ca507300b6fa4ea5db36048d6934325da8947bb6563874e2fe2948cde05c3f9281bb006df167caf6a3a4f576f336528639c35f40d3e5be9d869689723e740095ab0effe6a1799e958a523daf33aeaed05a22acc73629986e5d13546675d93477fa33e14afb5c2599b54c4550e1d57fdd4792a8f5a1da5a249ea8a501b02413d346c17a44827bfbbace15b1d3707152fcbb606abc597b58da4ba948b088ca6dee3c1114cf151ba8582b50483f712dd1ac0de2fce2262f9d984a212e85ecd47d5d5f6012eddccd304ad77315d1b3d588e81a453ae7b9e1056955f78174d7777a3cd366b68533dffc82b29faa47b11e25c787799c46c666b7c0135c80793582e48bce66cdbd8a53a349bf0ae21c789c74066838d3b1267fb6fcf3c0b14bfbbc5220f3cc078779f7140e6729a6662ceed2edba89b24d4fd944e8a47115ccdf54e593b12e5bb4114cbf1df2e4ca41b5d795f9c8aaa408975f60ad4a91b0313bc41e5cabb2d4d46f3be5c6b3e8faeba6b648cd2dd47a8dba1d7b0e61c08c7705fc8d9a6bf4a58de53294fa90276c2e43808321badbaa0b74970e9231cfc1f359c165dfa37684abcbc21c2c2488d14403c54b93b0d35b5eb7596db900e4f3f74b4e9135980ef0b4454edd9ae689c9580afa39861a2fd24870c6c78da2fbee4e7af6d19f35b0d78e0ebe8b3343c0d5f935a36a227a9fe4589891af39646bd1ac3a85c84baf8515fb8b54ae2e2c4dbfa136cb9e55f6075d4fa5e3203f59d0b66eeb5530425249a8e8dd288dcf3c60020bbb4b11629e441b8c458ad606e207df425a0fc312716b11b67def82ba818d37dbf4b874a07aa6c39c483ed8b29cc80487032b9214cbb9892479f7936e6d67ce79816487cd1ee3c8d2d542072ce03711fce740454cd01b2fa3a302a62bc2898b455e0796aa88ab271e8ebea74fc84f2710518e0391a922917bee05aa89649d7f30bb1a470a5a6290ad0810428d90fc2bd647e65e6004b6a1ec009b895bb1a418814ee3ea1db5b2bc36d9ad281320252a2388bb2724a9afc22867c7d8cacddeb200511bcd8e997b4a9600c4706737763b858813b5cfa885b3ca891fb4123c46abaae3eb59d65e58b607f60b785a351b8854b0fdb945cf627f9be28a897b136428c5dc7740778c7d3a570f64ec8c360d13ad42e489df6184e4c24c1fd68584d92ee6b04e789678b185ee9c2aacd5edf2a0fe68946185ea218d05030167520a2e0280c92f38c07cc3b964601070925c9d31ca76e82a2163bb799be7e2d4143a4d6e3f50a3a2245a5842a326277d6c70d90d22460ad2b48e4f4f6a309a1594ec6eb087c8980225d6ce8c1dc07679aa420dfaf072ebf5077ae3811957f740baf1a9ef8bb2384de5dc09df702128e3e9687427f6e3fa136b4a4401adb010c49dae8089384226e53f42bc05a23cc0f0646758aae5dd044909a7c12f8e54dae8013212609906df64117cbccf6340679a237691116d430358b596701f28afa0b2d6dc9584e6e241ff54c8493a7bbfd621dad50ee1f8ed4ae6379841738cd4861778384b86a1d2edd3cef535b3939395ea7bee05c345d351aec2c36e4d7b4b1749c45e4b0991e89707f18b1c9c2787b9c96ea60c7072c87f85f1c51eca72ee6d0d16baa8074e8431edbe829c802541041ad439ed2003b091d21b26c46fea000bbe0711a3ee29d3554b5d5e8f80b62f2522ff6ce0cbe46597fed9f662a3c4265c4ba848d64563e39df79916a6d0739d9b26cb16de58b12658f19faf02f73f9f851d36c60fcedfe99c58e9a9eb50f6efcfd605c77c41e2e84bfdc8a3cba5901cc09dc6c49eaa49c6925c45cdad57cd82295566aa39783365025c27766e9b1359de7e7c0e0610a56a905c68c5c8fcc095058228f39bd508d5c4642a67333a9736d65e7aa781440d2b5e2e25ff018a9dc8f68063a24b143533d80f9b80efd1b41902fbc4d61b1726325f58dd5515a98db07990dd765b19aee7198fc1bea2f80fc15e62898169d9df130cd5bc53b76369acfc6ea40ba3085648234dbec48371653c1dc3a8ea2f62a2ad8609edc373d813ba91178f2f38301185c40aa3728ed82496d01179062fc657ec241dca102619c63b2675aee076fa30a05269a0512402ec9c319fead6b80d8d14dc66f69fa157d064afa9224d111a44193852215eb803db33eeea1810c532d749e81238f094f09dd1fc6ff18b2b2eac72d550c834d38d5ae98845fb7288b8b24561cc7bfe8d9fc40f4e5210a5cd326c81a9ade7e9e2d99192f2b00d5a603013c7472d1c844760731b852118d9a041d1d41f88ab73479b99fc040c2b0bb5c301aa47bea6297e67e0774e72113edbb2a4cbf9f78721982cd7edfd2b010f3b8cda691e939d59ebcb03c2035d11f41e48a66ae3c486110846c864f3d572a48fd687242060322965e0759619b10a4f7c64c13eeec8bacc08c4a0787799b6c5adccac59bfc005cc6084e95816af7e50d1edb74ae53049393a548636c81e045cf2e8d5e7eb363f364edaacbc0ce6a50950b8d2382a9c0d69c11b440a28ac519c3c521fa22c78338ae77427026c71ef40b7f47530c1f5df980333f3b661dadfb9e97f36752db2e2b92896c091d124886b3e8337840397d9a701f387235f3357389422d9395695e2b6330e51d812750b9966b8becc9491fc8e6a73ca01d8ba1946ee40b6cb5f71014889c43813e974f2e353afd9fa8ab11b77cf75ebd995d77b2c5191210021346e7943098e31cdd7dff5becae46366763d5a7cc4350d206261c36c0b8011a193a49da841381a11ba76cd0a914d3336f3d788cbbaa2459a5127e3f9502c4803c0bf1b5741bd3c14088d4c63e32d51d0f5a8b58861156114c81dad8e5adf274c46d6ca16ced8552e4cff3a4f5ceba9006d1a02cae6c0304ef743bd68bb1ed1319284fe8158d4fa66144464eab60aa3886c99c155e6e8ad148abeb315c3ad28af938097083f1bd635fb53041f57e31b25c1189f8386a8d0f3fb6325c5d48483706978cf09fea40d083f88ec0fd71f10a350f0cdd70833a2ed71fc0d686e98a3a8b097459c65b4b20e0c54a82a45f2be850f288bb3a8158d909e98c44eec8ab73fb444512b32804b594fe92114055389e06a91f604e88a2d0e708e630ce4dc8e1b4c78eb40f1bb7b52019c0672ce4b31d14b01e1649839b14fec9ba483dd421beb27f0f9898314d0bf5691a4dd73a605b5ab8fedea9e991a780525eabae1cbbbf182a877a32185af1e864c7e3f5101f1396fa3020015d0f07ca4f30f17ad39469cf4c01b10aecdbd8168b0f6293e0ac66cb01d0082dfe77215f9c5bf5db63cf3907c1ce76154f13ec8d06adff687c19ebf5a1e885c24f3e1baff2cd25569c7d5a1f01704bd2a2dcd32953bc7b3703b2c26b9cf0c8400a2e8f5683d3ab933c4d5126611c72982d81ee1ad474a59cdedc07d42597e05db887f2759f8411fc73bae83c865a55d75c34002e53a20ac8d6ca98e3c064678e5036db699e760024c0ae87f709c2aee93d947019260f3c0a7749e896154d28f017c0a5c6c0927460a014afce47a8b338288c295bf94d4aa9eec3d349b8c5e21780db499d5644b5581f5105ab9fc05f114e3b0c9bcfc99a136b84d8fa08cef2cb223ad8d6af8883fd2d1b293c6fb8c711078695af200830e21d1db8e253ae8665c16fbf7482b58dc9d33a5931eb2fcb001e2d48e2baca865c9518a4cae65c792c6207895648015d0fb1dc4bba326dc608350209a69eb648ce30f89ef32986736b3e4b74bd43db53fb90412a7e053c2ff1715b82d452fa36a2609db78981a552ecdb69bca17a6f4615543dd946af4490068085b0c88f7a54c71fb99369f18c3b77c6099b5782c1a0a99aa7b6bbdadbd8d522115fce6de07531e3f77d3d0f8a780c6c4362aaabe56dabc0ac48108dfdd3061be88b7ab6f89e1f91ab0287a5cb86b59e67025c1334902d9bdd4f785a28bee4b351011038f8810d1b611dd3033f2ec648d86889b10e3c433e66235fe90a69927bac63a2224712fbd50968ed0ca9a45ca2e3f6a2d02aebf8c1a4772f7818ca79e52ef1ec487bc7973e1bbc3789a492f8ff665b378c94cff4a973a191ec11d4819d553c1a2686bd52d678668995a7492e887fbbc12c5e10337d603c1c90878185dc3884c51fbd8e4c09583b8cbeac68c42f74be2b7dfffa2b217d13ce20d2229ae0b452f3e1a45967fe384f794b0912d46829a8d42813274c842e6f8e68b164d40116e774fe36e46779a7d8da1a52d9c3a8d7ca459bfbb2a05b0ecaeaf1fb636cabdf23a40769154d06e6198a3c637d07f9d149067298fcbeac9ce88d8c3639ccee81b2f6499f347d22c36602372c1ed36256a6356cc0391ac9ed7e2bfcb738322c07818315db9f61a3db340f6989a738e7b6390c0347233431de2964e3bc944e3621b08a8b6bc44b9379c1b994334c8131dc7ed096671e950ff90c435e2a962f04b44eac54e8b1ccfdb02d87c5a08cf4688f7217639e772975a3ce9db169d221cf89b3650391dc11857450276ccaf9cf54cb84ac4895fe68e4812141897f2ea075d14ad404e516a7e5371074ee75dcfd4f21d1a65e8724153206e0550c3137f8d6c127cb8920f323e23a5310828dd5eb89c434e50c0f46918ef4b8b0488009f53e713b8bf9b6b3babe20064e26b18a0a9594bf87301721cfa051d30e8e3004fa40a7cb359388f20f6c74309666d5f5abec838d193c98a92db36cb02b31fa39e89c8aec64f67fab1052a9be667f64e9d87862ab3437d2b3c1f31d2d2974a9bc3c71c0938db3e150a6f7cfa0164ce597a27e3e9f48ec9fd826ececf026dce8913af69127705f8d7293b3828001535a24837c38e51c39ace2885678139d35a2470df16e6cc931cd1f1d31e7b889624c6690a56208da5afe799409b54473ce5d9aa5568eb5953ac305592b757e80504d9f24b81b2f8a9696b6e812faf1eefcca5975181bb77bddae5be9755b3f2ecde750750fa33a25d697be72e96b55f0674f4fbd6711ba34f8359f45b860056727923777e54208eb71bd6195802c257a45fb973f3a775c817424ca7c64b23514f3c365ac8e45948f594dfbb37f4355230fd7a3d1b6301a51d5961452c895e2a3ceeaf2914c230f61ccb8f5f4443be32d8f1428ed928c3330e9852867f94116e1920b8f985d0b409ed447d40542c0da572a34763ff6f9daf00fff35338a64e51f929d8248ccd23bf17ca8f7fedbcae9ec452c9f447cd1a6971f35b04fcf7f8a54743596da1411f660c5074812bc1faccab89e7a066cd6fa08374e87f96cf1eb64a56f87495c24bb5d03c4e87f69fab9a50d56110199df5daf47ea2cf3c884da8f6796eb82e82e405e41b32bcfec5c93fc535aa3ca8cc43320bb37a960323539acf418831bf7aec91887a20a5aa7e4fb83f75196fbd98e0d3f94ccd9ee6eaddcd1263addafcb81a5e8c9f4abba44f8f281c77e7a7db35fb1caa55bc604e1751c050a1198232ef98c3d260610e31a95de5a4e0d0803aa141e3b8f607410ccd920886824d10a63b94453bb1033233a5f36981e744c18f7dfeef266b31175aa028499d38eb7391b2a029592ff4dc116832ed78d4afdc63ec302beb9431d534887af1b931bd5e7b6afcdc337e0c1fba377adcda5b0e0c8c1909711836ea6ebca97884d019cfa06a99e18707cc85fa96050107239bea442a248fa45954f305a688b800355910a814aa7e03208f05c4d5f2d9879bf9e3ea01de08d14b56e3ddad4ba3d1d974968de4c0eea7411840c760bec3ea75b526bfae5c8838486027f2fa1d05d2ba302ad75713d298cdb6371ab902d108e164e4c46f86bc6a465b09731f5d174e7ecc4df3a1d936c6c242ac07bb784acc4fe3d5aa6f9a2d741c158c4d063f7465ded55fbb6e7155032bdae673f6bc56686229be1bb26d86cf98f49c845db3fab18203a2493f1fb9a150d6278e025379dad240f4f4e4d3acfc4aec029649223a8348068ee40cf809a316976440f7a607a77ef4d0ff52d7005e888f8c489d487fd129a308e047b133a46da98e0d0c4c2824783fc4831a705a28d07592b228a6f71a51599b350fac68d913519f6d9d6ce44b8e03d030587a6e2d710524ba76a0c9a82294e49d252742b23666ae5f199a034f983494230e7be7502ed50f42bbab28a1e87fb2432b781ac3c25395a6578c0d46a7b0558736d2a81d93cef43c448c51983505e3f1d54c2fe905c1220034d35343a285c856a32c7675be6e409de78b0a783db881c7913077546c8d8624ceb031f9935976581df1a42452ad1bf72ae10dd0407036416689b04f01318f821f57b0ba498eff089bad77d82068b409efd4c8009eecc4b0e1d04861db4f2f0d0df81c43a5861def3c727773fa2fab4dd53720bb3b319c4bc6246a38926f5d0713c3311fd12134a96c8d9c22441f8688a71b436644e4578dca0901ac586f4f41e3f64f72885017a6d19aec2a0013ff77b0ebd6a63bb50145b06b727afce48f83a2df9843acffcbd1a498414a247f3e544e530f453c9ac2e61f06d4fc900c7ade859868875c209c1e2d1994c40f6aabdeced7430c766c90f1ca9c5b1be7a3c0e7a95c19128cd1c404606326ba742d1bb03fbd1fb2a149096fd90fe67b3c55aa73b02a9f972c5a8be90a7a0c68eb89c33a999160e498aa5788aa54d991869c7367ba3f29a1f3d7616bb36cfefc5ea6ae03ff85d758bf9a3da9ef21a7bf813673a99ec72af0c84b60690e9c63299770514db7d9403fb351fb127aee082f4b75ab9656592a63cf110985a39bf07f448c6f5f81a5a8f98c7907687718bc47a8f35c67a914157a84bdcf52e3c48661e27c0c9d4ae85d88ef297410b0f04f6b08115a068a0421e79e3047de12b3296e186776ceb7514c4564001dbda03aa5bc3bd12d84564bd77c7546e88bf7519f444fbfa5002894106cad10a413c66ac43feee7885e8e10fe1b37717237a403fdce3d16bd7677be3c96d073462a10835016fb0a6f189f66b727b9c53475e402f2491ea54a9fee32a9b59fdaef50192e8794c01fc4650fb57a4d0b46dfcf4814293c59fff99ee64ca86dbaccfaa661083dc78c2d246613f13b74d1a154ecc17ffae8ef4b4ca67bd14fb5af73be1e0fd46e48731da8c62ccf9d3b28611adc5bb2e31721f20e7019ebfcb299cfe20f0283a8b384a7736d07f38fac3843ba1db06f93635fa4110992eb445b8a30f6137bbaf2e93b876fc70a5b3005d79e963ce1ac0349c735e180accc9623911fcea422c42e47c540f58abc76a1af5818a2a1c26f02c73e352a773ff032946caaa31766e5f7d5dea909d36992c0a9772b8807cdf519b73125ad4288bf8c233174c6a85e06f433cf7a603ac1ae70cba78ab57fe6be6446b18164ed7ae4b3c29929c9b3822c0890abd4cb2a9870cc7fa173b4b8a119d5fa4595543af65fa896307902ba501a923c674cc2cd13d9067cc9a80d40e9e158e4fad882ff67a7858508a2020dc2677b87e4cbb625ba028c474e71acad69d2295d04f339da3cafd1edfa64121b2b9a5fd509081d18eeec8d4738971ad32dc96a8707f594b0b1e7e08f1e99192b594ce9593091c28b71b946b7c1ca0e6bc8a83d8c63ae5c989a096e02208ffb15d896b091cfd3b4b892740e9da7d3c30d00eb86dd808fcd0d3e7b389e8f799b14493364fffa24f86cba161c7ad8a2666289d8850516224090e5df6cadb0e5c58b9ed0273fdfd1378c0f82ceb26202701b20d2d97f01d24017a7d808f261b04c855f2131025bda47b0256c8f25061b37505c7bda4bef955c5f3b391a2c89e7f2125dc51f7a4b5505729ba6babc64c9087905315f9a0f5ff6a15a1eb16be08db1d5d68e534e02136b96a08b03eaacc48a1a344c838f9ec6155b9e0350a7cc5360b9b77153ab1ab48118e4f6527ffa711d26b104700836a0f4ab69ffb298fdc250657825e4727c86d49eef827aa1cef8483126c743730ccb6ca1717d7c82a0ddf544d9eb6f2a1a981f7a95b0e5a7de3f3b5837d05fad753eb0f6ae4f88c0234228c74b57f28b73f22bcc049ff4e7fed71f319cf34316a18e59a9caa298e10d0df1fe387f07b71703da7a46df5a8d538ce4798ed353a05db3bd81e435c56f0096432cad1da052d16c2e0b9fbf88209cc4a819fbd3e5d2e0c08238d42aafb4f414764c01be329711118632e8c23bc85f3e56ce299d4dba0d95eb07da5e54ad6f2c332c4032776fbeee9ea215b35b45443b24a67fb31ba6414fe085082872ac9ef41bb11e802718cbdb02e8995d4e386e538481822f2eff1295f8c8904f4d2529ff55fd7e39ae9eaaa17794f5474c00b614edec32c65027fcd7e6e8ace322c4f09484373c39ed46a6e5ba631d6d42315041a001c919ca0c9460158a005151fefacb7a2639d60c815de8239f76285d4be00c8e29c3ab8c1c547344ada81b60ab282212ae83966e0a4e14eedfd2542020e0ecac783728d072a95eec10c5d06052d6b24657a6fac4599ae961a0f30284f9c2620d274db3b3397e53957c5237bcf3f901d7557791dcc06b132b9f7943500b332b0bdf5eba35f0dea7bcd5fbba84d1fea3e964d7008f8116a131c680a63bcfb5465d8f8a222acd950ea3d6efec76f57535857fc6415fc7817fdf7cd35e37ceb3eaf735c6df80df2876bf20f8eb6048a9027351548b248c1774030d95323dd740f67b5b78795c10d1dd335f10114203eb0ba4ca960e6ef93f56609b583d72c253f4c3ad59c4f42871fbe1475a238e3673916b9ef9aecb5e8a445f2cc7c389fcb08f08813fa8829702bcfe47ba5dc4a03d6424ec52494e37b9a53ac8f3480cf520899b90d3b3b4c00633eba6b4a851b0640fc80454d9ffc55d306935e6b10382f0e76784813153c86e005896378e03c55103169fb94a93c37f7ebec3944cca7fcd41abe28127eef1bb0ff1459deb8998702b690731d6816c1ac4d0ee8a22a5861f047cee9c7c91b52e3875db85cf670346b6dfea9537fe6bbb2edc09c2bad35a06f4a90797b92bb614490b7c278aff1e08294bba62e9596f079036299f9c6124c4c7359fd8d6f53ba49a7128991417ac3fa36d94845fcb2effa3a255b306447ede4d90a33b1c183a8ba44d8607a7c35142fe39ca90e585f8bf5242acc14036b2e7eb314af0207bf6049c9fca22741b776bb570d8475c90d28579c87325f93d47e8e9e0bed15675e7558498f3d53ec74d6f20a6bf8949506ecaec7b108a540905b50c1a4d4b0a9f744d8aaea39ed44055f6ada1fbff0047db67e980694bc05bf375640a891cf2d8691049610d137ccdd7bd2dbd0468518b0d219bd1430225298f5a12e520d8da1220d9a56056230cd5d87d88cc7f4378c519308dc32459c87cf10988b7f0d173549045afbe7316f991e19c40bcb63118967db4ace0d2713839e299e155fe29645a4036291a448e2a296428651e76b8917aa80f0035bfe4cde994c2531e43b8f95ff7c659991e942a0e84cb9953ba5c692fdc204fbb071746399297388070aa9a38029d770791eb2ea7247bc50c7edaf747a6fcdbaf99be2e124c1c7ff588763c4b9ddb4a823231aff1ae1a1701c0ffd5431f9b197bdcbd6110bc584e995dbf2a3de6485d57c93e5783d13f0eb4a170f77bcb727bb6c24788e78f434a953c6ee1c66048851a4e23351d544eea8509850a58e1268474ad8e4a9ad7a76f3b580b4e2e9621a2eb027a52050a432ebb2c7e146d974277c97755b873bde786b28f82cae3b4315d62162a6dd0853a844c55d7bb556e31b5d8dc3af7583448542c95823f2420b73da7cdf1315a29d08669eb55ab60bd3debf545a76a9af21e60e71ccd2909cb5636ac9e183e44ea9cb285aa36791eeac5cc2715febb458e6d4760a73c89c64b6c72dde937577e8db1c1bd7b33fa65652267507f21cec45580789d1e844aafb9769a47c9096a573a586a77a93f291351ecb1290024aa1b8707bf8ce3967e6d88aa28b1a59f7a50a595cc4878c14fdfe6215276313fc0b39bd9c353a8e78edb2eb38d9dc999bf00b0f3107003df723c27656f850b441ea195c1b86b1bfd5cae5271d2c52016e2e48b919ddacf099b3ac8a04a50a96bf52878d9b62d770c61cfe0d9924481cf50a5dc9c141d7ac4c06c2b30830c331af236eac8f8694228be79e16ba1d848a89cc268e824264c54c4c224af51d94bf22bd99d3c9a2f493551457948298a528758a2c4a5f5452eade6bdc865874934797b0cba892927125e9a22a064dff6f60fcc8572fb8bb28ddb708bf86a2941b5e684949317ef57b8c39fa9d2fcf4b7e55166e23924c0069983fe045a9668a922f14ed9ee2a8fc6c1ef127834a9ccc6395a36e109dcd0ea45c037a60be7c5e40103a52074533bf136b7f8978c5634883d0aa92f485d8c58844c095b2a72bfbda1ece0e29bfa2855fa60fbea6bf6c241357cb5dcb03f4c81439d3129f1d22ea8085405f30337a666761d5d1ece52fd232f07154f964ef12eeb6a54c49ca93079f079d07e1890aa6f99ef8722e99fe4785ba1af649913b84352c46c37e9c61249b77466a584d16c6be862199917233ac8c7a3522135265b2930c1d40084f5566843efbd123168a3d1536a3940184ed00a333457046a206f0d03d60859f1640105900024008a155a1cb1df5c6cd4dc691c24ad44123fc59e53b5bcd58a5affa41d8398927d8ec2921b83e48233c55c135e14e36efac6568186d18a8ebc6623aa7ee540ca4dc8d794c0431c757a5cbfe7223b0421942180cd4f5e5429d1ccb3344bedcd733193a004708533998157e08f3764298bbee9ddd19ce28f18418f9d260c0f585d8733ab16a748d00d4009010eef0cd680ed415c2603de486c9d001a61447054365233b5033058b75d648284ea372805328f45b3275ff859e9d665da3f1cf3a77f959a55bb667ac9eb17ac6ea196bc69ab943e9d2fd5a969619ad71ebac59f636f2a70caeed9a29b83e6debd7ff85108943070ad59fd64b9e10caf5ebd77cbbc367a91c0890fe056aa66fe3c50df00d4b4bae37d6f2e0330223404a635adfb9f7f04162d2ae1a6403b2a5eed0ea4148727400531f5df6a83a5f75c76aa9bee940ee729d03e5fe6ad25dd5b9ab5fa82c3bb9b3e41e7e5385e5534ae9ca84d26fba3329ddc12e937e1c2dc20ffe77f0cd8e813eeee881791c0d9e0092e667f03d514a24a2df3ba4b6dda0f4b79476d520646dc467a64444ed016aae362635259576037cbf2f8f6eede83246136c55e0d1b1f07bd1939e60b363b0f01355da8b8c0ed8d665adb76ed9db47b774f8b94844368c923528861ae42e99982791255286347d5f0c093eee3bd23a36aab0c3866c08f490935549af9abe30265ff8e0ef99eae4b85c1f3cede0991d5b9d73efb4cc92554923a1c51481ddb5c20c2130fd195260fa95ba781c0d7668a5cb5a77aa2eb703d8ef851deb1a04eb1227921ead06d5a02d3467565aacbf6556baa43810b424c0d682a3093386c0167c4a71267db5e0581a2b2c859ca0fe1ad4adcf842e73682fd23ea4d14824b2c660540a2a0b6c760cc73c5b73df279226cea290344f60f820d9315a135a2d442da425e214a41deead39785e4b91c1019bf063c5073f14493056695d6b389a8e6e81a40935d7ff7db8c272b93f249e60513b585211effa7d3444b21fda9181645329d9846e5d2b3eda1f47d38fd565ac63373a76d365531f7ba757fdf4752fd9b556695d928860d7755969a0b0b4ec8fc2013669d77fc7b8694a80dce540bef2f77e9d2e97c3e1cef3a467937e85e63880edf7b97b9fb8ead3e5d9a5b7290aac0307a9083e75c86b5c9ff59571e8a89982bbad37ee5465774b132d61832d1506d90ace17100d34f107a28126f6473e3ef55d0e8d3e6cf4aa5f1c819f892cb2c82db3e30c1eb0efba551fbcc1ee4c2580be3e5f752a01f4f5beea7fe1f5c24b487d8de80b487d8dc81951e096b593f506b2dc4997bdb3347c57965666b1a861cd14ec24265228bc903b0753ef5ab26bcc7c21c42ab5c862a7c7f3be660a76d1c616f8849e94933d7cf4aaff850f7e13ef70d78aaefa6b44c9fd3434a1dafd7e72218879381ab16804528100bae17c7d38c040386e7f1f0b2d744bd5e507fc1be0853ef287bb60230e1d5a4e0f48f5be0fdf5aeb8f2e6134c8fe0072bfc2bed65c758a5dcd9192ef8dddb75e6d1185ce22991954017bb568809a451b5be0cf2ed4ec42cd3868cdfd0c501a843543be4fe9bd7f2b49860aa4cb1f4cb0a95275ab8e8038f3055f0c9ff4d4bdd2c63af3d57d5215b81ae971d3f72bed15e66f0482d282edd35cb3bb30cca9f2d3091c3b09cf0eec35a307dc9f0a3dcddf0d8a4c126c3ff7806f47b2855edd0743213856134c1183bd484f70e35359dff348b385308610d8fb9d7c59f8f0cd29743d928528d7bbf7698466b7d66cb0babc2c1772541dadb1ef7dc80b897576d3e57def7a649d75498a01f78f1074bfea40ebd92d8fe28890ef57247dfbf67d0f9fa31c48694c97cf36698640c277795064e222e503c93f9d5c4e17dc126cba9cd95f1c8134911b72385fa2a8850c22274f45b0e972ba9c80884f7a1cdd12bd08d9df3f140abdcbf8a1b2108d96ba70b3d76f9bfc5ad45709979ffcf23ef2fd9711888b7f791f4117632f718509d86b060ef8c67f0cfd539c2338fcea792f8eaad30c29a1afe34d97a12f8d38ba0cbd3805364f39f4aa6e89f5455f4953fcd187fee462ac29a01274be484c60f33f150a816f431fe660938e108457b8a13822e4d0dfc0a4793ab99026c624f2c51187bfb8703d700c8946b37e95ecd9ffc694016abedff9fe252570e9cbe507eceb02f0de6b2ba983f82c895dab03b0865e3dd93f21816bc52fe4f453670c816f37a55f76794f395491ae6013e31e92cbb686589cb51bdc703402a92e30028af33502810d7a603386c026dd926d55e29b92e9586355f603362910902e4957b05973b2b50fa45b395565ada8002c5abe7034d21928d172deba6323652ba55d350b5b35d22dea53fbc91648492d5b28b7764eb76c4e0bddf272c8220e5a63adbd48f00c1c708db98b852f02b4e534fc902497edb3608fe0b63350d7c72a0f47b2ad43b2fdfe3edcd657f68e355665329064c0a62afff7dd6fa14b5b67a02bfb4370476cd718adb14ff660a14b91096949b7388501bc842580385ff7745e710a598d7569493b60b3c6beaaaa39971d80fd16a08f82f7236a81cd9002200f32775723b007748b06359a67bd9c873b6fb69aa7a3d56cb7d3c379394fe7b9bd7ebd6bad5f1228578f8d4a6068f1cc7a568667b29faf5d5fcd9ebc6f7dfb859c66439ef5665ec8561c51549385358a53fdeb9576793a5fcd284e2814331282ed047939bb2d40edfddc2c9ece5de0965ef5e7fcec0019d92109caad24046312cb7d2e1ba3e00b0675d9f6fb5341c4642a9e60cad6f4d32d99dc3f52c1f74d97c9644beadc25f4d968f499afea0887df4bd8a58971ee9fc41c56464ce18511fe1105d8c44f93095d26b3a920b089ad0d8f9e7e59bfa6128d4618443577b9cc572daa8d58d63433d44cbe230e472ad814d532e8b5a95b4b8ec62c1abf178daab65d2e1ba960d3d60f6d00b0024cd9ea9b9d8371f152c2f4e7c7c5649132fd94ad73dd1233fdf002b64a6debd7696db75eedd7cf4d9a36df07ed583e8d254b776b9d88776db56d59ee7d7509dc4ba63e7ba469bfcb9697345fc94b96ff659b69ecffbd392bd5ad67fa30d5ed25dd90d7d6ae7cc0e6d7e077eb1373b8ad559429c5b16428d3b71ffeecd7f2b2bbc85a15cc25b19326bea098e3da2fb4ddb25dd66c0588bdeeeeeeeaef757fb506207069af64cbfd868ffb5e1cf611a0b5e3e8c4e30dd83cb9d98267cb354471680d40efedbdd7f3bcdac701dc7549eb8e0020cdd4fa90e33911152a4958c9272f532a4964a135069260432dd19ade22c4b4464b3655a80960ea33822bd93ca94e14a7e6fce12a485eb4cb4efafa3199529c7641715ebe7edd519c92f8f55d46717e288ecbd74b9bc8f6bd24d97ea85e91ed7f0d25db17e964fb238f22db071fb5245b32bc49d0266c48e261a957849c341418744c110f4b60fe6574f120581a5f1e043d2c8d2f2f8ea5076f4882791771c47fbdd0271a816128248df66913d9dfbf5e91fd1b4af6779decef51647fbb24bbf7a60dcafeaad09b3789ec9ffadef46cc8fe3714c7d2934694e84d0f4bf6f742d2f862f466c849f61785a4f104bef9c190fd5dc8846f7e4e647fd268cae29b221eb27fca0030d9fe97614617e30e5a53ff65ac345a53bf34d61dada98fc73e694d7d97d165b4a63e69f41fb39ad5a453b0303b4af621b98a6c7fc831a21dd80478bdd027fa767dbaecc7daecf5425fe80b79a7cbfcc75e2fe4edfa74d9f5eeaecf6b693bef5c0835d41e787878787878787878787878787878787878787878787878787878787878787878787878443133e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8f4c36451dbedf5429f68048622c905975e5cc0c498644e2f5029d50f0d666d29d48b938ce9c29889a17219ede67e4c31302e5e4a313e452b6117d28fa82af37662087abbdc3f1ac917b2dd28447e70aa9fec031a996c13045b5bfec9a6c8f613934521d04063ca364130a5b426041a64843139b6049a3b739001142749b0084192173cb0858807748c18b18490d7d521dfc729c822887c7578ddcf33f511414e36f1e3e04da9ed1dd8ea44104129a5f6f36a2d2c168bc562b15a63fe361b2c21ac1b37426e685ec8d3288594305808569d849cba14c2d2a8656983c5aaed2a3f9d68fe63b07d2f6900b4a67f0003f01d44f5eb907bf65e4b29a52010f1f5175a2bf2788810a1a71e77a7b1a136aabd2b4d190095fd3b9b10c84eda20404a8393a29416615194624add9eac0ca59e774d21198d81a13404a188eae75fc13457bfc9a0e1fad6dddd2d08455c2f54c40bf56a1f382a8560682f4da4945a4cf24477775b23d5bc59a63276c0f5afe8b5da1084223cf710cf1801297d118140847fd6412022145620108aa89e4c06bc1411cf74b2eeeef6ce18c1f5698de9326badb5d61d8422704d0d01a188eb9882a8179d425577550824e7b6ec2f83961273d077eaae52914b62a3b3a2521f9492e89ec8b9ec5c76ba24ba9c2e279259c1b11003f2d123b6f3742b23a714e7e9a91a6872628801879033051d4270b7369be1505d210c36dbe9c013f6da6b6978b906aaf554c65dcac080e6595f5de177fb62d7ebf314c9ced30b75ad694dbbb58d86b6792d43a152aa53ef74ac63304c9dd3b099986ba5455ae0ba2548243b897061db796405da01729179e03cbdd0e770b8243fba8a4a7d504aa2a3c1de5391ec565912dd29ba41241315a1aece89723873f5eab448a2cbe972b22f949d52ced8e9224b729e3a990eca2d76d39d5f2d89eef44263a5cd62aaad46434d27925131e414d51c186c2756635556eb08a4cb1f600837f48015680b2cc4807cf488ed4e17e8eaea8da2bac2d357fde628f4a13537f0d86100cf5e4532dd13b9a6425d7db614b943297d8e357091f5d9525cb4b8ec5c766178dec2538708628e1ecf6597c4890475b9ec5c762ad07db51999cbce454bbb3cb2ab744e24eb9611370147ecf442f72c9f863fdd7575f7d49d3f3af886c562d568d5e954230015464502151911c9c2337778764b4abbae95abbb5d5d4c05fb40043a50ea314a3b5701af5f600d53f4d5af7ec100a650a8d67649718c6200438aab2a5789516762c4f0f7d50d54769a22b866950d1a68f4aa7f8403b634c6f5aed6ab70b9b19d1b6bb75fee44057ba211188a24175c7a7101630a29c5499d30d08b1c3df52768f722448555577328544a754ac9c030c1988949cd800393692a86cd2f77897061c3f9ea92f54cc9806d26193d9cc817afe63d95263be510900ea618b1a7da50a90f4aaaf05405064d37ce94ce66887c10d5d67a81c066fdf9a135ed35af31b9df5feeba65fe1341cbfd343ff54ad50252150d7dfa73e5a4729b87c51391836168a85b7caa2798b6b49a3f6013d75aebedafa4115e1bc09c267e2d3eed601930e0fed7d15a66a32971125a81cda6d1dcd54d8bc9ac4d169b65295dca880114484430c3112e08dab16cacc082077eb4d8e554987f3f155b0718889f1d3cdaa69f4609c64fb878de985396ac5a56160d16f5cab6e9b2bc2969dcb04d6d48a34ba7f900c6b8cc5dc2c801bb3ebaf2f206597ede14b6963b9c216926305eae621360efd62dda320fca77ebd29eb8dc350ba55c01367b76b396b433b36731d8273b5722c0f696fba9f5722e50925361a99efb22f6dd60a73a65da4200800a7cc10f07d83e4c101826532636a0259b3813f9e911f4e34aee07b2c203b4e84841051e2348e1c0b983c71052727f03709850c5c94a6de8344e36c61b183c9620bc3c40271eb6dcdf802f54d9a6d4c230cd298c50f0cd0dba8ba10999e526066c7e5f2d811424f79b608899fb2ad7da03ae6db79b96a6f5aba574ee3afdfcf390cc48a5a93c24a71a2f0873de0ddd5f73d71080394d449f0752d09226c5ddd8187d699d344334fb884f378abb203cc126cefe9ee3f22a06dc4fa42c858092fb69d8f87efc1c0a91acb266ef56d657d2a8fd3236700211ae78e1b3812d98f5d9a75eb26e32405dde1d24e794beff07c5a1df45e8aa3f041248a4894b3821f12ac826ddb132dddde4fae9aace92317e54a06e9974d743085aeef7a1b392650bb522d7aadb0175ade84a67b262e8421040f63749c866d5517fb304259098a20228a8642a4536ab6ea436fe7894416d7c01b9d2953f7693010ec8f52925698e53d444dd22e57b7796fd58edb9bb7b3774effb685a807a30ac6f14f2af9b1bb0c9baf1cf9b65034503fe6cb260904d7ab2be18f600c64eb6e47e98a7b1a1b2ff654983bff29f06e64422a2fedb2aa721d83ce5b6d1029ba79920566e4e9c843cfb611a1a5129c0eebd7d5bc9d34b6503fe68116c966f04e839a1e5d0b6e9b6e9ef210216dc5fe9fbe8c356d27ed48e34c8b1f83881e57e20d4c2d41c1b37366e9434fe4785b54d3f49086cd220ef0710b10a4c6929a3027e4e5e25d96f8d341b979d7a5bc9f63d5c975e5bc9de83d56c5cf6baf4ac47d21bc213dc4fd3038c7de820b2e4fecea2c5615570b9ab941bad9f0f6b3f7ccb4ddd4f265ddaba744aaf3b69260de48d42fed53b19361b68e73550b7b2d41a47604a4beb301b4e5272bf8a5a181aabcaa1df7769a9d7f1c4031c30a1a73fa53159df04d70f820986a1c1f5a98f3590d0e1aed1f7519cf9d9e2faa2effb300e91b46993954c610424be90669f582188ea7804b193fb1b7085c70a326c52dd0a3c1a90834daaab05a0c1a2856fb2e4fe1bd4c248a96f6f84fc0b47dbf4e319366f6e6ec88801d3a7b4f4ef5c9fb4b4c6c9134a00861e48956c9900f991fdfb0525d9690bce247b0837dc2f03863194efdddddd3f0bf816f7daef5f80cd8f660b560c894b4e13bf65dbf4bbec807b62ffba64ca2d798a823f871ee660f33b6300027793bd1cbdb9417dc29c26d6dba954d79dd24b29beee273f1dd736fde0073d1504d8f433e3bae567b7a4541721d8f472397739edc3de12baeaff21f7132f0a0dc8bcf16ab4a62603089b2cb7e1edbe10d8d87480cd4a0b6380ab0cd7ae75a539c64018c9fd3034254e9ac91218d6ff857e05cc69725fc8bfbca7342ab8d75a2b49df02e56ad35f83065c491e3b4c36e0930ed83c79df1e89c36f4ad616d06403aeee262b3bc0d4016c9e9e52911d407168e6a1420e3f13ab80619600739a5898edee211a7f68c12e39b81fb603e6dca734e6775e5ddbfcb44dbf8c0c38083edd65ffea7637e80275cbec5defb45cddb572b3f49629d8b45aba77f671b4ebdc65bfafceecb6a477b27218c37ca745019863af4dff6f81ddffebaed276e0725b51426972ccafb23a9351b2ce8c5459cb6739b8caaaac6b6eb0ca3c7c4fa71d32bfb2938c94e5643959ae73b20ab05961a45c5f0a5ca2900b05619fd298f52cbbc6da0646049bdf59820a039d2929b8e2d55f5980bf0fe3d3e9ffe62687525447c18f2ebbd65a6b85b1f75e1f405c60b8878f1f40bae57f7f6092fb617ab8fad5392a6badb5d6de5abdea55afc2e4fab7f62581f4187f903ebaec37ebcfcf95763dacff0bbf1080f54569ccde027fb32ddf0f3bb2182ebb151f5b6d876c5623486498c0fd0da3df4e80394dfc85fceb3e8b060dbd6dfa4d58bc68b45ed11ab69d6e7f96b85ed1172dd72baa002c5abaf5c427fbe87c019b45250560f101410560d93958c3f6c9926956800b72dda2516290cb0ac0b2eb562ae76400ad8377a451b0e47e5e764c41f1022b0b746038ab1025370479d911e78617a52228cb0c2f3bd2279a7881d609273a45d015148102f4b263093878c1004a620722535e1ae8132079612c3ae05e97fc7cd529285e96c4a10e975d521b4f9ec07510f65f78d1785f7bb26389e2d01b5a63bbf4f1618bd34ef87aad31db03e2d8da6e2f3d7585c1b87a2175d1f88b9f87dc695acce1df47ad7d82660ba6dfa9c238823f6fc1f697d4d05631ea64af09055e0c0a60fce62ebf9d67a62ef399db3c5d0fc99e14dd355fe9bae5436829056db9115a761ea360d38d982ebb478ef0c0435eb2c4fa91233ec4fa90aed967eef242a4fb34d74b86320806752b26f78f51b05fbf3aafe60533a728138d88b2dc5f45b8918f54417038ba8d6c99fea8e6aed40849a63f3a425ba72e69ee523dfdd18ce2949e8e532420f2c9f4454ad0160b5de6280e7efa221cc579794a7774e6e9dfd35d309efec5b9eb9fd66009755920bb83bb936d50975dc385c1b4c082ba2eeccaeea5b92bc6bbb03b1f807015504ca1ae3b3382aefa4d8bcbf46d14eaba5705164aa66f9fd0d6c57599b3f4ed6de48205ea16d90b9f5293cca35e9c4221538c2a08feec158d3da9d40e6cd0ce0a1004c51c3d29f1851643a33598741d29691465629150175221d2c41e8066b013f9f44a1486632ded4ab4971a5e62eec188582e238c7cf2cf6452d236dde508b992261a152f347761db8bb194115d5c280e0b25e6e899f1f1ba3065c6d4ad6dfabfd27e9e2ce62b180a282708ca0abaa2dd6c4a9c4fe8ca49fe8196504cb9e59ed16a3652ca26d6f0ccf8a2a45777acb9aac0989898ef1466d1d833339392d5e0c3cc0b0d9602675e8846172a8c680a0bf8dfa43bdc79f3956966a2996a26db12baea87915f48148e069b8981f9c9e13069a27cb2c80a8f747064bb9db84a9ac41abe7db31831c41c234f15049b299b0792225c97fddec8c5222115261e4e10e15236116ec6466ba466620f64b87b214d941451861239138534256d8304365f6831b66ed9a77da310d7ecbf98a3fe8fa38fa571fc53aa448bb50468eb458796fda55d2907344725d4853bf1065126d2c41e68d92fd6c694adcb940846ef25254a3ab16ea1a2b0f27933cd4c345ad35fca0136f14086aa0d6018618c972c1a7b62fc288608370253a53115a335b3d44e6a86140d1505f532a2240005f58496fde78fa61b8c99d154ab8169663a4257342e48d3ec881128c2c58021d6702815337e1fa44470265405c122488ab52e41b04313ea237b6248eca2b12786fc3cefdad148248a628e1725f7a5460bc71bbc6a432484e0832384ef228bc61e95ea63c69c9817f2bd7284fc2b47087e913ffebf3762e0cbf66d50b7425ddb1dd05628052d61ee2ad152365f5d23a82bcc015ddd5b69d75705b455ca829617267367e428fa406bf48fa3d88492d0ffa3b884daf4a746d1368a37d1d640c401b94b8c42578d80d02b86146bb4463f0942b844381394dc26272f6f9a98646ccaa1df748496f012ab23501cf067b426940249ee56ea082d5341e8ca49d32535c34f8ed6f4a76cee4255415739d0e52974d52d9412b42c65e1938364b299b26191b25991b2e54c3313ad4b2535dbedc4f9aa2f89c291300436c5da8747da2528c221c9d549ae3fb99d097625b9fe0b697e455e68c8fd33a3c318411b365e0240820d1b3636620eff5a475fc1a738ac02c0c89844e17a4c4f020da71a431cad51a2893750d74b0d2f315da82bc96433dc615aa9542a1161c3a6d51a39add56a591648bed0a698b94b94b5280b73dd2a01754885b64a33a0e5ce5de1ae44739708e735505748057d89b92bd4f98ad6ad97590fb4f592032dfbc39d4b364b3b97fcb2cb3225d294119568255acc984a8d299aa75c4654d0e8820f5c9a41ca4809a85ba921b40c77ee42694157a958b8452a48ee1f513a5ad32f8e281c4a8473170a0abaa222a773174a09baca0125e565d62d54145af68e0a2b70a82a7041d934e142a32907d3116a4262a29950a3c966ba995aa659143c933303ebd6f78d64c788e7ece46aa66c9e93eba76c5dcd94ad6bca56df9cd951e59ab2e5fe94ad5b4e5237d39872d2ab7e97ed264cd9c41c29275da67c48d9523ed890f5e1fb811165dee2cc471109a18f1f68231cc72f6c855666b430d8cb472f87a6f1348a79d1d81343d25efdc7b8211cba1ecc49a4116b34620e91c907628e944daca56c291bad9c1142080078a1bdd48839441274804dd38c96fb4db56e99622df70c89896652d22db305b7f58bb54a615cbc94b00b490c618023d18fe40737563327e90517edc306174f60dd0b8de5d553b5d62651258a0d86a8c284861a1747aa28994195232fb52a3ba51360520f5c1c81c12c5cf060910c604a40a5054b5061e2a990a008cd0b162a3d180b951c4a4fa8c868c0305b4a3c60100b1d0eb12089620a2ccad4c78929d9fc378248aa88133e3821821438c10376e2066bad9581133b3a4ce1c14fd9f1c0b8b0e24a0904539e78e100c699fa4c49e2862936173698d2838b9d29b2124cc9290dc160a63e392d72c06296cf55f1e5d394b9267250723d44c9e570839c0c380a2cea00e6078b57bc5cd1c41332c03199fa3471440e8b99fa3421c4973605baa769a2872642e0328227525ad862ace4498d9658f0aa927d451794cb758bfa3c01cab2c5ad5c0bda4b530220c7f96cc9d55da16f2fe7e016cf018dbb5c835ef020920053d29230dd7605b89bdbaea47123e5d5c91eaf7a63e982b22c7d681bd36f38dfd9912b8be15805c26605fadb4d97fb1d8a149c6fb802ddd8cdbcb1bb135e9a0cc09c7be6b66199acfeef298e5b99afdace727fe85f7899a71c1a2d2cd7fffe85978da52a994240e8edab107afb1fb9732041d4572127ef25af8dbcf515eab0c9325d87a266c74cd7b9ee96fbdd8a5fe9c15e5914ecc194a435974dd765d735c1fda52d69dc1306375a13fff691d2b2000d858e154e1cb79cdc6fc20e15aef834cca9cbbe0c2738a5c27dfb975cc157adc35d29157cc0377a4e26cdb17cb553dedcdc701ae46a2b1001b443852be7ed04017691015787e2a7df680d0d09f66e4f684d7b365ff57b37a7a585badc2d7bb42e691477cbfd3a52f08a78329f39ad07dc58075c9f3563b1623888f061d990626135ae60b3cc250d59c38288b8d55a7dd411c326d551ddff90ea726d39dedfc73096da7a81daa67fccc166fd01ba3ff5a75bee359f56a8e46cb7263d0c0d8d0000000800f314000020100a08c582e18030cc0345550f14800c7b924272589d0bc4490ee330ca20430c21841000080106189899192902003785e9c5854614280c374fc0c372a733ea5d31214daa140a65a8db3c8ef3f165d8fbebaa82c88e2d80dee085a218ebf4ef8c626a7bfbc47ff5a359e402a0a53c5eca43119a1fffdf1840b80f13fa552b3875d5bc7276fb1ddebaa266a389ac23915075e94d542ba95ad6f1fecd19e6a1de6ca5d20ffc5e2784fbb66386512261b5370844ad7facc07f55e16d24d2e1b03d626cf7a2b14384cca659bce1b19c59e9ad1060384dc3474a9ab6e6bfda666d145e89da85d470614e564b989adf9f456431acc54ad398894c5fd49b07a381ee3d11bdd69008aad52ea247eadb946603e50a4c69c485f8f06550eeca89f0c30d5993f1b7f036b011fb3a896cb93ab0e0351a4a4d6b30825e3946c368548e453cbf543a2a8b5a35bb2c2c1d19a043e4d53bf89b9ac57052d1fe4527592f98e00d09d5da9a0d3848db67026335303313d4d95179bd1bdbd5644cf7beafe7ba046bab5c4781b7222eaad0d6672b99ff1842d65de11c365d7b78abcc4ee6abea7f9d5248aade9c682816be1500ab303bc58be62f2d81d11ba6f1290318e91d00c73b98b9da745d354d4a637f2c078dacf78cd222d27fecef534db3e88cf933a11e0a4cb0114d539118f1a2b1cb4f282f0f3827717b93470d6c7844e2b91e8a8921d8c435aea0a68fbe6b36f04c34e9bb592fbd216cc2f25de031204cd0d789ac7b989715c311ca848f996688075a6ce1d5319457c65e10e824317ba1855d0c99b6faa68bdd62e366dee1f42b97fbc422cfdfc23446b5494402032bb14d62ae2b8cb6f9a482dc032535f6eeaab1d04c803e655993b6533b43ddd1f2490ba8940df82599d6f61bfbe54a03c131910ac65bb7428392b7673dafcb8885e585b84498a2e7d64c3a8fec88808b9d7dea5d328b33ab43a7a2a1012ddd99b14382c399d5807a2f55176cc4fabf52359690e7be5041c6e5277cc92a11158c88ed9c48c7fdd9afb078aac4c826004916f2c58336e192a14d59e2941d8883975045beb1f75fe33402a6bd99effe0eaf5a80907bd909e8fde64ed07530bfb0fd3cf778b508ded21f27018a4c67b3519dbe2c89a231cbaac37d8b12d50c64ed701219e539128ec27649ac6191156761c8354fb4e5795065799d8a3a4de43b0be3eb090d8d31a23ced5813bd9fa93bc5d194cfaf63f7ebe19f31f2a52896026d77fdf7e3705dd12b238164898455887826364440a2a60198fd85e802166fe5e331770d464f25f33a3d75c5f434d31f939112d4f738b8e363c3756f822843d4fff03f87c86e626b33cccc9dc120a462c76ba391a6af2d7c656195245cd9e9832f984b83ef2fe5ced89f5fb15539894796b3b9b98c669ce9e0b4206efa6c6387b5d144b2882e76d7f0148db818415dee683eec8bf24e3edb5a512c86ded4b366c4bd90bbe4dc2e8df1324bd07024e277ae6bf7823b5c8f6fe47381af3f1cf2c26019d4f45516bd1be6660487ca330c438ed350de74fa75b281574b9104bd21af70cbda1a125487749f62d885c20872050b98a6efc2cf2a0ec494a66540d4156f2ab4543506161cf39d7cd382d8de143aebee686a28aeed1697e9f7676c840626efcdb699296ea04f7128114c9d7fad739a83982092b3b0912fbf390a374fcf3ad31c57adb5ef1f306a7ba34e25a729c7d1f1d894b10372fa9a711b330652579b0d246ba1786194b56000b105e7d3c427657b10d89636718cd0f5c39e806db149aad4ed1b701009775bebf6b28338b2d93cfb23b50a558ebe41623dca1b53c9bf666ffad4350b09c191ba980ec6780053b3be17efa12dec25b97f041feef1a0e6a2721d627d911e2489248a3256c319976ed5b5d551e96cf348c35fa61063cae8a3a929277e6fb9ef9c518878cda2b1529cd36d07dabc9218ff431faef7719d76557c135eeb98a211c753618f80e74d49f658b23baa87e8fe52081885eb53fa98a1e9e1348019587251a54df3947ade5e40a0f173cee27ffe9462d58a5d086208950c801732a460cd9da202a7507a08ec5838c2b9d985afe809c3f1abaddeb4b999e971e1ce3a931346d7848aab27b6fbacd591ab6e1d0c8815c52b198a4eff7c84e78f31015226cad651eb601cc9f06465db7550345dca3a18b25e91a79052e6113e6a9d5d00ffd8493ef549a958370ad607ecee378848b0e77090506a77f041b25bb21ab4d2a57256684c3a44518680f17927eddf06928385228612e472e13ab74eaf38533803de6c99cd5e57ba828592995e3d18bfcda49be54e1b89841b7fa03dc0100dd59a65743175b2e0e49e9fecc4973807826b1479a7d30727d02eed00123d0438d8d6479ca6e88e6f7807f714701a2b6864d7af2c9193f499295becf6029efed5ac1658200a305fbae74ec1bb0f2b55b3f1f8dfa6ddfe3f8150876e92947850082c806c4a2cf9c9af519624ab08096c105041fd69ce63e31c6a55df234618e874e0f2f975f7f435db8f4fa4d8a8c177e8acdb1c6a0bd0aed07db1ddcb9e30b3d15359473ad0ed979666a0690e76887ea1f30fa0ebbcdee1a4e1681bc4526979ad92a732e822138cb2c93b4c4c63cbf75f999390ada7c1359be00186929b17c9959092e9ba76a02e2529f4c330355c2a694da2e3b4326c9092945026de4e681341dd997d94340cafc9ebbc1d3bd822624ced2051c998cadd87250692d0a9d1029d77133aa1b06e66d99d4ce6051c19bf8134a01f80ee94796f8082b6a4052effd0da2ead3bd450cf467becb3b65258c67458ff17f2446238eb70b13c72e3eae96637695da7879eafb0c3818e868f23f47d0e143ef3fc778e0ae8d7e94e8171911dd07114897dd3f3c31be6059458887be37bee6c7d270da68bb7b5dfb6a4f7961b12c7db2a53932aa7db110d8d8bc2296a2ffcef7d689082bec549d732025a6d2f2f44b849f602e538647240278dc779b757601222adda73c123cd902a234f9672d44d4cb0495d2e64d9b02d30ea569f3498163f139ef20f4998d89785df54750e6732138d01d9f250ea8f59994c7d21b6151c84d72c88ff752237e9167096aa6c3eb0ab993abd873981d2a404482bb94597a826015778a6ac057f888cd79d29c3a00dfc2fef49282ed2a6266abf456b3854998e0a584f050f78833a885d73eaae359830b80e4afa987c511de0b0cba37c3f8b63b5c6ada30a26aed62437a303b8e62b24da4056b6c0706719454062e3f786fa46158da9274f098ab040677ad2dc0c7919a1f8cfd9860954b93a10c6cf49463a610137c9257c13bae84eab191951044930ec13c378faacbf0594b2260176e01ffc827fa954a914f92e9232a32a419ef8728600d23a160f76420369f227637cf672db2ffd28693734c5c5d2b289c578d3fa0928241c9e163d6ab17ac3b3ca9ec155adbcc090496375b82d6cc5b588e35ebe453b07021eabfb0a942f3ffb378495e61a1d78cba7c425e8c661293aca950eea7d5c1c4afce4883931dd76d2974caccb262e386ce21f4d0b3f737e9a1c9ba23c4cde11bb1747a22bb7c90f9b3922faf9b7d28b46c39de3ca52b49efcfb5757310674689c605a80efcea79bf2acf0751c348326f6599d6b468d9e5568b39fd49e773199f7d84f186967210c134690a6f3356b8d266174e9d6ab9a3e4280a78940c3f4318411e911a9fb00c268d84026b1820304150fc4fa7be227d03992905f6830f2f0e713a638e00e8c013a4ac4159ff0652187bb02058f8d5934570a330ad89acb494b244bbf14fe03a3485b1c18794fc9c0680b083cf0c2421ce65ddcc504462c1767ff2341724d53c088f11f29562a3b14b813dfdf98a1856eff03303a66dabb9c1cc665e32d2c61daa34aea3afafc8e76d4b02248a8c40c765c2fa80901d28a4908ffd750673697d65ea23a6e35e376de81096526b578d188fce5a2d26d316f6e42e4710fca1392ae81b4ff2e740551ca5bad15d04f914200e85d762febf3c92024e440087bbe5d210e573c4cb401ef70fe2f6014f2a7209dc4f15cea393d57b8a97be3794ba0d894aaa83dc7543ef800104f27df09b9027fc27239cd4b3ca6cd0796cc2dd1d06974fc8328013c933a4477dbfcc530ace5e3541c3b2bd85aa4745ac96d37bc9fc0f032718dbfd306f4b68c21a1a80ceb8aa025a940ce6218d3cd6f894f858f78b0b6ff31451ae5acdd47a56546a838b022d69c6e04908a6472d6537673508b35c06eab7b4984bf18a3ff8fd6dc0e9be5fbb4ea6b5bdb423e69d8513dcc60f2e38bdbf843d77644265bed89379461183764c508bc71ca6da83ef059fe83eff967f3cc7ad53e727510b7911bdc8fd6f60a32e346a722596865d2ad6e733d4c9cf7b0decbb8dc1d4eab8d6c68baab08dae24233c4423145cc89fd5e59e2b8ff2af6a4c5aa6e6a00a0c8d448587d4d2edf36271360b74b3d9badce38cfecf52bc96e3a87ee05f38bee7014c2db4fb0d1028c9f0f268b0758e45eadd5b883f9cc3bc0c27ca4a48d39e7f546e6ddbcb203d603f52727097ba64c4ecf0d641ae9ca18a2a09782a5807147878af8c4ac19e73593d0f27bb070fe965ce4d0b25ae3402bbed728e9e1995aea2c60834b4db5afa729e1e100565636afb523df61c8aefd14e3b232b0167268c5ff0abf539c19dad74aafe59d27e6fb024603827678d0e4e134dcfcc0da78d98327e6f1f51eab115d81f1cbcbc5704756eba77cfd09adba1a83a7a95a128abf32c74b0b07585df4b88d735022f8f5e7d291de91661616971cd908e6659e27218dc5fef6e4c82fec6e29d9054deb0bad1246e40a0952b8f8331123e7c38c388ad7d20eb41046385007baaac781461e644a0e3444d5f75943677832983d2da785c6618f3f151486de79a4e24536fe47f9d7c08e631be9084d437ae32ef69a94b1a131aa0c764bcad508fbcb44a31d5b3e20692e7aad3082f838699e45e4b35026d32e1a2af11f11b9885505cb502606a0334d6fbaa6190708389b95d30e636abfe370df90c12e4cde7bb03e58b2268de156591e66c6b0b56ef038b7df11db96969f42e33770d3a6d48e060f76795c7014c9df2ffa934a4ea8622c1cf8c6c661c12158f2a66fa63a26051fb4ec1b4e13c221955a711544c975290cc74e95dfd63c0563e359a81e2ec46ec31b1cc37a5745b294ea6b2f046f4b575783ceb26a18b90170fa96100904c00391b761768bccaa6e20fc8cce9554ae24a06fb2af242f00a2dff5ebbb1a39e4d1eb8ef2059f1a4989bb1d443e43a4c8862b3ffcfbc596542ea9d62e4c9e1999a1bdd194d665522825b870a0d45bb2d15388966e38e4299e39d1a167fe1c222af15bad1f0210d2eaa0bea1d2bf8feaa4650c5aa849b2f9ca8033ab47264aad5c6f9d492c0e11eb23d502c97c32120c6eac4170ddd40de8b48808291c0887cb7c81ba8e8402ddcd824c29d267ec669a30f75fcc7cf5daf744a282b238d3ec29aefb2c4302c2078c15e7a114ee774627421e8510574234ffb4aa2169ef554f8cf98dce22c21a08995db4be16daf63f7c5b63bd562e57115a5879c62356366a6d31b81343a8fe94e55979c1ef1b8bbabe4d29a72df86ecce58baf078116b9f55135384d2f21f1aad51fad6d96ad1cc0c9e9e6ba71e9351974ffef4941ebbad658c2bf1a880ce7abd545b7f71823249b02ee6043b475a939bafc723fd99d4957d0d4ee6da0d22692d83a0e972792ff57ffd811fcd5d6cefa89846f39821807e2960250d117dc55af96c8381a94a95bb9cc6f0d68b0a8dd64a084c1adffdb6f64882b97a140bd0599c67d84b6db3d059dd9ba8bf630533cdcf5a575cd696991a578c4de8e0b265ae33474dd4f35eeb6a4e85565895a946ef1d7ba91eb3c839a0b1a7081800ebf4c695f9ce3cb0acb96d7675cf177ed4ecc914ff7ac017f282c4cdb787bdd72f04ee1086377823c51833c795ab3e61d4e3c6a97b0925180bb78a4764180b29517b40bd12e7f9502bd442ce3e363006e28743a9f9d400a0e06aca09a9568aaed3126855d85e20927af35d8af150c07b390350b7a45a04fb1d1f0b62ed8e59980d544ef2445ca50b4d8184173527c5a3791e1e7907288d334751df5a94f1392234b08af1cc38f09fa963067c9e3ae9ff45d7ebaeee5a35e8e853aeb8aa1341b231539bf4415b3c3e5d125f139dfed0b1e7d0ab6a9657634f8ab9120fe46e7c6992fdf1c9d66b196898c01766cf908c7dbea1506b8a5326ecbfa05aa44113c1a2460963a82edb608d0f05eac4d889abe6d3a0d214be5e9c17f3367e2a9319df1663d84aa187fad452a38adac4d93305e439f5fa6c0cc84399f0e076b87a0940213c1d74d58befac94dbf9bec47984de8d7597d212d48d2e4493d22299fd6cd46e3df091f0217ed0c452a454b0f0a7de412300c0c5ebd40548494d6986ec84d007bd603d85cbedcd8392f2ba6360e89c682180103a15f55132f9bb4ff1c42cf7627b56b7992348843be94c6905247924aff08480ad485a479032059268ccce2c772310c4afa9028dd1b539871a3d91e206a1767fe587eb3b6c1d1dfa88505f757c50b8b444ee14dadf8bc2942f6f4be2f2d6f5914f890091f0e74bbbcd359c10f1a17027473ae6ade582950ba30cdf75d4cf2bca3b29d4976f06da1226a2be40565256648b30ef98709663c5d51ab33628745a004dea0bacc5074ba0c8846d2125b24253043cb4a2b413f19e84e965609b45012772b468ae8c5ae6ec8d45c308186104744f747beab7d40e048d2ed5b00076e68ad7eb6a7d368a91ed5f8a0179e6c5f370011f84d0d96a8f9e5d0992e27e857f94cac80b1e1b2ecccb8ebcfeb3db674d9d139c2406279645e97c8e61745f24ed84426c0d9a4cca3d9a70f479e02e97f8870e13671597d6396a74bf8ebf0dc3c2bdc165dcf30faa4a7fc8ebff9ddc7ef30dc3b21a4328b453245a97722d0bf9e782835b00d86de7ac8f19ea4d9016fd7fa7c24ba1f13148f893a5200ca57595151cfd531fabc78919d192fe076cbc90a4aeb44fef43a405772a6d32bd90718dd6715dc1f3d77e9c3c2445bccb83b8c21c2d12b296423a292703906165757f254a727aa79e6ca317090ab5ef8ad615863c7236369ae6f62cbe1701a2eb491f108fa60cb88c45b7612147c14426d3b1845dcaf654b905c2cc76a1593b269916814b5517ff12d915aed7520275b2cd58f19ba76e9aa415df5a0a701b122f4b036fac4878ded1e98d3dbfb6846cfb382dc903c139135206ca146d96a9aac5634391c473021d68b8e5992fbb7e78f6927c9976aad788d01f351f8fec5c7b3885e5eaffe99562e113f2b74335734b544ca998a37145e9d21bf37872a8b9622244ed4b0aff631efb6f1897f1c3e991ba016b3b4c906a2e00d80ca37417464b95b16fbb1685deead4b5ed146854f702f0452a0e9e71c89f11093cc42b5f091f689e329936b5e110fd54a22829e988369025003cbf5cd80eea5cd16d2c1acd4aabe1065c889ec7b4094713904973feec14570da958a9d39422ee7181dc275c9752574633f786882560194d41ab20550ba4614f311c2375b367695ec11a4a895e96ccd91a9af80c04ad39d3216521cab34272e4331792f61ea4310d9896964865a02866646bf60d710d06960b826323171ccf1a8ec8dab68d861346b054c0bb65d27b0ba93d06a448df7c55e6005efa2f992f9cb561c2396de3ccb4f37d95e23605407b3ab9bc14156b80d02120575b23e208e015151c5b96c40ce48210e185b8291fb6b3d38dcf10f4d3aaf906ebe3e747a8f76bd967fffec37d3bf12a608822d8592db7eb0e4d716871bdd2084e75d1e516c45bdf0008bfd1bec9c220808da493218b2add20ae1349e6fe2435dad86df1a54963e6be2b2120372464c9d1737da475485811edaf53741cd904d692dc03bff926e5a2a58485777e5bce67ddbe93027da8e1b933d3cc7224d30b07d133b49d95cbd1ef8be823c325429ea733f6ddd5ff85e75726d492000f429ce268336342e9c45f7eed1de0b546ca46a761582af681456d41612472e55f158008eadf6489dd45db5e723e9479aa10e5b0956f10c3c33751a99639f44401b0f741cb84fce33accd2a79c46363455fbd97fccffeffaf07b89ea947004c6a6fd34a81221342b8a889aba2fc8426e868bd520736dd71568c7f213bc6909b42802957618654c85d5c22d4f67f09cd7428fb6a99062f67eabd0dfb57f63d9bf6b5414e8bea770cc63fbe1690dcf295fe35637e40917225b2e61083d71f4a214dfb218c7866b274c366c10d051232660e92f795aacd1c4d1a9d2bd62633166bc5fe62256fef493a86ab15f30b4f54f797a4f5c29011bdafd281126cde266747fc823be87e2c181b81f20022aab95142113aea228acf8c1b5de5b9d7ffa8083b91d0f1bd7b7a89b155d403efd49cf6b887493330ac1f5d46f5eedaf40248dbacaf40ea86296885da2d36a1a4a185799124a0677901a867f7d4e9249a92c8db69dd3adc889a47174aca0e4052164d423e8c2242c69a46439d569b6e3dbd66b1499c859b71aaa9ab240dce7601323e7f821a0edc859b97c20c5bd1664484ec2955894074e1330e259b8b4a207f9bf62bc8d027108660c914e4f4196f06ad7a203510ccb5795fe328711d49e9a330c08651c9de00995efd35efed0993a27dec350786563960843a11fb8be9751c6c4aa168031a0a9dff4ce04c0a3cf3a3f9259557a3814602b72cdac51976607a98b5169b3f33fb19fdde818a31a1bea268cb970ccd7ba043a13d8eb7bf2979eec28a03373137ded94963ff0248f69e8b2facbe4f1cef16b5468ef942549fcb4e75c9f56221cfe22952c9f367551ebcb032586871eaa89a833ef5852a7628aa74e51ffe426b6f1fc82b503f1494ed7f519f3a6f138688ffb4ea83a10c8c783d1aced63e3ac964ab5519fca6137707febb881ed6ae553468954ccafbe86c5804d65a5fac98889f67c0d4cf33b6bd4db19409c9049665ea13e8526f9e82f2e8960d8a4f4c3dbd8b8a5457334f922aaa92794d490ec1859b46260948f4e0ecb620546d66af41efde01b8c01380c337345d65c4f878bb253b9140bc3b281cdd078859415840217336dee8be3f53fa4a4dcae768fae88aa85a58e25fa82f86f521383675fdecbfe1dbf9d501a1ead03611ed0f57fb3162ef1ba69d8bb82b2d118ea5395a7bb49f2dc98e0b8a6757db57aef90201f74b948d4af693fa8327004d027f4e8970f987a5539699b742d09dba0d2f628aa05bd922168af21e690c6a1b7afa598490ec538d925971386a66c422bb6432e043d6783de94875ca8ba830cbb90993822e51dfbdc3bdcd87e30d5d45bf49c3d3e8d5d3cd05a8e1febbe96c0b3a6ef20f758a9b89bd7e13ca0d6e5eb03df79299f5935477f6920a7a920cbe8d2c4bf736ab4a6c1c9354a7c71f058537a86d9d76c61a8e1e10de7c8ffb3d566922f4b64f7ba2d5b1c528db5e1b8b3c75739623d4fbb9f580f9d034ce1718ccf4a5094cbe21d589be0e94107c56db4e495474ac8e305091a60a99a81dc2a8dc437ab2278edf4ec80b97a67f6afaa7a6fdaebc794f1b27c449ffbc9cbb997d413aed9f84c5c2df9153c907bb9a95fa3dad358000735857b722b08de3d60138f80deb2129a2ecd47e9e88977148166ce7f85f1ae4fdd4921a541d3a83bebbb8d4cbb75d6cbd86f0ff67029abb506ce4c6f61fb6325d62e002c16686cd3dd330c694054d7023674a5ea56b1d19cafa92b3cbb8e0ed47e656d0dd6e5855fff01b27f4e62fadf84da4b0af8abc583db43e24fe8091a98a95bd9f41082e982de3e4ebed784ceafbc1ac6d7ea0f4cdb41fdef97e210dee561128ecfc0063e46f0bdb4b7ced57273335a91f5985204266790ac9f59fbc161b4de2bcede896ccab90a93e7613a280a10e4433b5aa3f08e8cfa9be9fb2780608bbc376f0f0bbe3e4137430994d3cae90274a80f37c9dc6a670b670d60da5dd6aee52677aaa2bcbb2011ed40785be1e5e03a6520583a09b542374141399814f67085ea702ab4fbd0b74e2cc9068b0a1296fed47811039117a094c9113d873a534fbaf110e3808bd3f5897ed58358773b820e3be1939b5792dc68591fe1ac49a5ed19c2c01c8bdbb40bac3cc0577d655cc47a56765cb84e5045101b6b92a298899edda5dea35aea5f2e912df72aae292436f5b6565c2dcc7eba47c64e83e1e9adeddfef06a5f1f44c3259bb1453d4e1c443a73134b2d95fc9d7a6001e4d41f72bf5ae44d2f4a5f08277e36011d9997b408e1bc3090d2c269fb73df499a8f80f6e666ab4e1dd3a9db7dc18b4a048f754a9a4f42c0c6f3f7c20456e973bb3714d768018142bc3866ec3702504447002f85b4a6eff3ee6c8d574371672a15bb42deef1ebd5725eaddd5fd4651d8d31d6c74cf10602f0dd7290b78abcb39afbe967f3fb244f549c5f80c80e2f6a516e95a1801c0eb1a6f98eb6ab7d37b37624281a39985fc34849223c2eca64d6f2a0748925bcf7620c07a9778603a1ebdd3db433e5d01e6853eb18a1cd74f2d318ea1b63588e3eea3039212b78dba7c85ef0900a8cfb7fd2b8a9d86a908a36cb7b0eb6bba8c6a787d334177f85de93a8268358b7043166e9f9442f08c426330a0948fbf6065781a1fc3a5907a74e9e308c420735d77fb5e12fa3acfdae5ee7a798bea1295d2907a1714328849f767e8b09fe0532731af798074420d91d4c3513a0483b0f4f9b1c834fb3de15de6a9c5017800bc2a79bc0efb954fca0f716a4abaf0a2415682b946f9816dfa861c15f0e7ff6d726fed29998e0e91e9b688442977a19722b1356d817a616a299acaedab5452c9747474376b899390caa7f962615ebfef12d84fc96304a8702fd9b5853a52fdf6e27efa4c679a7985aaf9fc215a0a556a230e8c85b3ba225234bd3b468ff3db649df5df4606ec90f1dd8d233067a112f36f1a24a5cf52253057c9b840fdf11c30135ccb4a6c000b05075c103a9aa23ff08003de9a38f15d4b15d026ff4fd0873fd755aef41abae64cd37c43037811a26962f517b399a625cd4f911494cd85177ff89e844946777b8388dd1fcd00df1efe9fd0ee6da0de8c0616bd360e580592f6076c969f4762a9bfc03fc0f22a1e2da3492485b98d4f4747c0d857598e84f0e010aa5116c29878cca6a335ae9a0d28f757b6b9ae813053b2f8e504a3960119617476875db735459251741e3b1ccc79726bdb2509d54485366488e45c12507b5b3c08658786c61081a17d992f5cb05da1aceedcd4fb5a6896fdb6b9c393075aba19739c91725aafc612620f6d3a6cbf0c4be029cf66e9641ee10d08f11a3d1f3cc11265dd1a19a792970f4d6e48cef91acf8ae0fffd4fbbb07d383fd3f1313dcb28d1814b7e2b5c6adb011c467697a070b2d87ebf758284d7c1ac4cf43210c8e3db4557e30c2479c9cbc5164346b9235bf8177265d5ee73ff6c1606702e56b0aed5776f9715bb3faef7fede1ce2f34351f93f68efc5d3333d840c1a475e9fe9648cdbd0ea459d30c55d229c69f70de3ce8c65cdedb86e2152cd6dbc2c9f033e10120f662c964858bf11d49e1a4e1e0b767a83516b15e7f127f7a0091a8c37d7bcebd5d577e64a3df6608d13d596fd2c0bf4d23e3e0a4a58bde0b5d230bbe6285b139452c81de28fbfa2b089687aefc2bf7e2549cc73cabcdd31e7101d6a93d5ec6ed151ea5d43ca6874300534315baf462c6fe73ed9d26655f5df0a2d959ab7d8208fd52e343d106cac15824948412b112efb41926fcb264df3d69a005df0fa15f5264e80bae42abf43a95b3b14366098805dba3877b86269ceea948e94529d5745b33910f72d2c42f69e42cc39bd68cc26b71533f191bc3accaaffecc8576c8578c96ae94c0c2df6c77fde879a738b92ab771599dd6ad7250169a5d85710ae4ae07360fc16b978e898962666d8873c1d395ec89ab7f3fbcc8a9f11f760f0f0f06c0bb3cbcd5cebcc2602a41f2ecdaf89d0fe971edfb01f42f35510a7a8ff59c33a3ce3a65b55442cfd3e32aa1c0532f0c6358e9e18fa30b8dbea43c02c4265055317046cf05dafcbb93f2f783df4c1286a247e89e39a97d7feead71d1fc8787cc7784c03b6a445e22b590d1bc9cdb753a9e96528d597be848f2293590233e9ad75696c10133ac226d50ba7c4f24ceb42d648177ecd6448400d5327e1f1226f6d2b67d641734ab6a19d41937e9f62e6c58890574c6519b750d3daaa2a37332a3fd6b418d1ce59c8e221c29e0172fa3e3e38db31df7e5c27b0f9109c930d4b435c2db02b89c24d4c18c777c13ea544b9b39c3704d40a67d959da44a69818455d6cdefd81e2a96d1ac1ff9273ca816ffd40e6f7ed7fcae7c01666a4e84bad372e89be07243757dac2045024d53f67a8e2c8c5f21249b800a4a2dc94030c76da3d5e5230cc7ed85a62d32ca9059d1c8ab631d65c8ceed9f82e50651eea96595c989f3b5c0a590cdbfe7070b6ba45877effbf55601deae5e74a17771193691a45cb6d0ef9c585e560954126a58089a17a9d948c059cb1f3f401c25d85170fd5340d45f0c3454f98866ea6f6c51ba9193d306da27ac01a16cf7d65db31ee97356da2a443e820cd8d21077723dce0703f366f87c3dc48ba7cd74e8f8485dc5d639bf43af23f0c1453448079bdce6d4fd310f52ceb9cc99bb6af821fe46f23648d2ed002ae1a175ddb77bb4197a713d9a2457cbaf4bc55e59bac8a7e30256693d02f8deba06fb0899f8b38783d24e99c9dd37cad4c943be73e74976a27ecbeace08e5df25c8aa329359ea810780fafc61aaa29fd0a2eb88647b121beab9f8a87db6c17036a8da67ced175590df54a0cbde86a9fad33392f41050c588972bf87d6f929e09d19b1763b9ca643afcf164d22358d624dd174ed83309df0c7733c8d2ae120a7d9f95eba0c1070a0e8d78ced74075e230c9c10b6676f3975bade8314e84449bb614b335386a413f1b256ccee631b24354947d198b3db6ce63d750abddb0189711236b54a0925cbe284dde377ec713b77a47424f37e90140ee4d16fe2daf2ad34c0f36806fed13b4c5036d5202ff573052267e3605fd0c5c8b7718c45a51a43f844cf2715a7081a1f31ca57e972db46190ce1a460c539b9a990c8dbf10da7ca21d1deca717147a5e00e09a6281db6e67a5456724cf09faaa580e9447c44c0b0516a2b6342a10af1ebbc303899a63268694ea78588acf623925cd5ab1847ae7ce40256280221b9ffcc6ea8d7d5322b2e05055187efafa34d418a79e34d4dbc166fefb32b5579ecb95f009e49f138793a3770ab11a450187b3a921fede13940335f33078f4c78c0e1df9170e2d47c669c2112e319672f5ecd6a3f76e7ebb635bc9114412172e0eacad0f82d5e59d0fbb52b984fc6878b486022f1ff3a2e750647f8a56ca0786a6716775febc434080d31647719833c0ddf24d1af2f2c10e4e1bb23324c3a7bb872e1d62eeae5043c6b888346347d05af68066e914d2b95d062ff17f981532606d3d58a02921bb6bd113548f20c273eebe8b3ecfd52907d37a2e4cf4b5a88b7ee614a6aeb5b3bcdd9a97881f00e13bd90e57958e975851b5115e9fc2ff020d23b204c20e0e53fe7021254f4234dfba033a382ab275e551ed23bae1fb6e9c394154cc2f15c049061ea519b9f271cffdf1e042075821b41175cad34b73c5acd2403fd93c95936e50bfee1b19aaa7420822f31bb04949a9e5275e51d196b89fb4081fc058daa70343ad5e5d1a48b679b5af06a340743d8d3893403a25f7cc0084e3e51fdab5be0f3c95d5c2b6638c5041ea6d91461b51843445fee04b0cdeab7ce47f7590436c6bb54f0e038228c5d14c904aa74f25b561bc98cb706289e355fa107bd65154f0389eafed7a8e3b03f59ebaeaf79d5ac4b15df9478b15dcd25f5257d9f809b2ac6bd9dc63a4900965a69f8395c08be5c5df61d4155c6ab015161779b418cb6a07766f06f21cead971ea3c1384de3d23c0073fa514b9490f9a743c2d34a92756564786f920e21247a9cdaa71271191e24e3505802636e518df2b478e4b53468ba20ee98d961c0bad501096a567daa74d31600a136ce0af8fa845f538a7e28356bcfdd0afda6552ed9b86965f9e3624fd26f05bda58357d480bf6304aaa88e7287af3865edaa79f7e80031eefa4cf3ea858193c56552e3c07aa35e145022c057c8056d1f01404d1464ff0e1526aeabaf0eea805d03c16a996b1a3e3a96061dfdbd5a045eeedd4dec8d6b5ff4e85190664bd36a94a17fb213d5978fbe49b35ab047c1d095ad6e0d230c40be1ddef54602b467676c4f0d4c6fe8b99d0025f3b7e4c20633d16c99d9ae9322963ca548f28b1486c2d0012df3947cfa5557e8401d516c98c206591e3cf80c6bfc82adc4251df6a32fe5379d73e289e4e8f3c85f8ca836919155e3ea4cb0500bb8880616046eeb987ca46d26fea85c1d7af1b1d4365d3412d7909ba473f2e01c8cb097c542fd78e79b4d8c906bf86d38c858a094cd79d329de47ac2218fbfc8928d30e7cf5922aaff429b7e2b831b7282bb0c542944a221b888375722efdeb7568f0462db846219acd3a664e946074ca485a86b8c2fa91dd4d7973840bcd4410f08d3b48d5852fdf37a5bc207ca5b88ad8094121c0f36afe0c272ad136600b2634a6f6f0d536657819c3e64854664c4b48305d8929ac9a27ec573a5970cc9478d18f5a4470603c32e19d2c697ab0d48bdd82a11c885daa7345639de560135fef034b2ee1135c03e44373e27509ca7ec2730a34625f4b334636c1b956b9bbd43b25c8074bb358e0f968c474f7609f83a25275b37544629a30a3980d896356ea0754a14010c3cea65d1ef63a6929cbf87bf5b8d24655e00ad191235da6183b59410fde12cc5a3f3493f2c5be9918650edd71564d24c880d4ed501c42c753f2c6068013573f004a927a6631d38b213e42137e5500b18d590767b07ec26b5d7b7f2cd6743bb25c8ca7ec2cc6b55a398e502e0d73c219dd87e1af968058a288face77e64c2a1c29c8f59cd9ed7c4189694cb4be2772c378907bc111b505288b1d60005ffa0ec2ffc38df58b81200680136639ec5106ec82cbd06668bcccbe5aa5f0a189773685e8ee7f341e834337d67c91444c3a4041c67ff1e8ca2dce1c890e31403aba71faaecc4a13aec17a533369f5a9d5c7f96ae467cadf5da62cc767bb061391328aeb4f9cf93bd756416f8b147fb0f929bfe0f39873787ca09e5e6898a6042ec6e7f29502a9aca6a7b6d45e18a73cb3c61910c6519514b821603e7a9196931f52024056f49ce6a736c287b10ca4d3846f1a0799ad3e859ca369e331a2a5ce3f227cc15e897ef1857740b6b5bd841595f02c1be3ed19ebd5011dce8698435d29369a73b13be78c4878944c79c08d6736ab32ab93074fd7ab6d3534c5bcad82d26d1b6498f79eb286d9759998dd40cc6afcbb98e40ed6b1ba17ddb64751ddfd2ae73eaffd49521e92eabde9a9ba31520c3d4aedcf14e019e1f6614eec622753ca829e5d3ccf19a0702d671a71bda12d4261862505b6fd511ee06c8e287c9d22fd0cbffb5a3cc77af8be89c3c77feb65c0999e713bc17d5c6f7c36546c1ba22120253f5e3cb577c91221fad4131d94be7913f124568a52862e275ac9d3e4ccef35e22d32d550e2545e34c4126346c31185beee26f3a232214b742e0989991179a8d5099a9e95b46f5244e1f1e75b8ddc3ee73e4b075b1ef9176f3b3408ab3cc5c06c5aeff11d789e3cc054ceec2c0c627f4cbca3e4d8138f7f5aa654ed3030d6199fbda0ddc9370980da0d5c31b368a648aec51aa71247ed1ceaaa70b2af6313e0140dafcbadff3dcca92ca02dc62225654e3675f39573bb1ad0701c83ba28c16fa4adccaaa9bf8d3d0720dac23a5168d21d4ed5a1918c1563d93d773871f9cb3b6790019c68c61e3fb7d9850b31f9db695ce5a05b1c92b3114305f26de01a52ff66bf88fcc252e596fc6b0e1e3b3c20dc07244dac4b70275c6b0f1fdfe94e7d0cd2371b509c8d674b18fda97d47f8f9c6e47b8659862b2028d213da97aec2a26596553146af8ae3066b66c09272f82dbb44f4c9bf7fa06d6241e2be2189eed52f1ef59333fc0b1bc7a11dbe17b3b6eff47edf7d2735f7b77fc9e0edbffac2ffff6fdd0fe1d32eed5717b3f6e3f47ffc7f6efd0ec56c0888dd1462c65f54061a78ff4c71693ffb1eeaf9e3329434145b6479efe11f7f8bd1e76efc3eef7d4dfaffb3d7aafc7ddffd4577ff73eee7e0f33f6f6b8fb1f752f4bff61f77ad8743750898de84b2c6e75f0d43d0d3189bcaa17631f0610896273856a42938bba91afbfd3082340e6cc9a380665e7acc4635076664f1c07b3e7b660c19e73503d5daa4ae776a1e2bd20ae11fbbb8125eb91f032f6a63cfeeefbd0ed1d67774b41d9466a01a7ef18d212c50c0537ab4c815e937d02092af0caeaebfe7a98f33ed7dc513b9888d3367a13a4ad6faa738e3d39b14fae203f6d276bccd1b517cc8134673026432895f8469e358650be8dd89f58f5b1c5e67fc3e290e47dcc3a231b91b03f1f05fe9fd892a75a3545d71dbab56f0d03158166fd8fb5b242a64b63e6f44fc9092e69eb10ca2d3b0b2a4ea23fddd40c1327eaaa55ffac7e1c0c175f406be1cf5333a40757e71d50b8b12aa965305ba0f7490c0b8727464e14c9668ab1016a228a37f1dba7d2acfcfb36dda06a7f1785711ca444e5b36534528c7a0c73e20024b65977faf2db57007c2bb2c70fb939723f9130e27e63b2353b598c1ea9751313f609bd537b65407bc35a0b67be458b8bed69d95ceffe79fd5609c2b801f5b30671d46f7de16a501aa75f5a401fc888b5e23308d0790d2bd61e176f6d9592e13d7956e5008b01273b05223e5afa905f2cf60affbec51f340fda1970ae50795d5f3176b856017c96a722a283a3dc1a5053daa206696238a08693b926a45237113ec8e4bb4f81c9da1a18f69cfa19d2d2176c851264b831798895b7f10f632c39d086e1ac2a7a91fa3f79ea8e2498c7833218ee62eef3ef6fd905893ef8485426ea672d0c08bf1e3f823873165487e2c5ea851c42af6cb01cb881046bdf9c0d5a93c95aaf471688fcf2dc2ccc02562e0ff012981a72036e34fa0be194e5b1e467f0eadae83997b6b3c3fb3f5a73a4304333da585154ff63d054e60ff9c835cb5c2c63476669d6f780b3221f16c32bfbc93131b8734f97b530fcf91d9c307b8d9454468b507bde9ea0e37b5aaec885dd9b5f0e9ffc01c0894fd4e785c298cd5ef32589b9fdfb23fb1618ec1774f85535a2f800450bc2209f82de7892b0a9d39f630f1665ba056a3754199b4238f67a874281a9ac8e21f802878e97c3ff482fb978ea2c5be1f38e8a3bfd092ef5cef7a972e01ee31bbfd62f7e89a1f0cf0a9cc4e9de107672521798789a13f7215d0654058007ad62cf692fdb9cdadc2af24b2bab0aec98803509e077cfe40b2c2c754d5d86e3de655d31a1df2a3ae8fbfab52aaa79be4b6f646428b6c1fd3a457433512ce0178429d8af4a0213d55a11750931f4410c87659ab22a0102499afbe529c5e0a2b2b14f2a99343ce4ba39d7ce64370974cfae6f2f06c227d11f49fbc32c2631a4082e8dd12aebdac8a70afdd7448c833b3bb0506082423530af88b7d46842157ecf560c772eb1b51bdf1b5da777afa660c2c444c7459b28738a9894d6578d29aba146f552c260291f241fae56b4035e8cae26993784aad1de9be750f43b18aa9d80311a879e0f7f6b3fdfde854816302b3613bb008bfcfff5f5704713145c742b9330d59506235c36934471b4a9c94b12524d6a971bc2c0b64d60b942f3ade7a089854028916a8d2c5c599d7df5ab48b4999e005b1f520b85e8486013a761d84a2cfbdf5d9d4a338f83fd66df292aa2a8dfd6a0b7425bda060fd11b13fc7d68c80a3177439bbd04d7a9c571dec07def4143509fa06351480425e8a2ef171638943b4b23cedc9624e20c5829b9e30b2e37337be212fb40b0db2b6d5c35733582d304faaa14efd971add0cba1dcb58c3c8d51513049d3168c43923f79dfc3d26c5c04360abb096c010672a6d4d3a1cf78375ea791d9700225603d8789c46cb12f7f2327886e34f888f1c2373ff48f1faadc28330767fbb019fff4d203063b94f49ce07486f5019acf6887ba9d529acf5d57b4fa8d5d27abcdd01e2b5fff007a13bcc9ef6df22ac558988c55a65089d99e476fbfdef45a4ee9d0852702e5cc5df283ad7efabdcfdf6d99ef93cb22aa1f015af1ae916d72fedafe97891e6a56415792bce8acbcc18a1edbf1e24afb1e130e51faaf481faceb3b8d73869e5dae08ae75bc9de06bf3615cdadf58c2eb9bf23030418f26f1869c51ffdfedb57ffe4d6461d777173952108b984cc8a73e3d7ecc4d5e53f10d2841b035f122f16da1e8438994a4c06e9179afe44fa8bbd6cd0128e1b7debc31626782fbd4852fd5f862cef2996c666867535adf5bd482832f47a2da34eef4dc4ed4913609d2b9331ccb9bf1028b5093815711bba33c364263e8e4ff3fc8680a4bc68153976e49ebd17b34516134c128ea8ba1357d88c91da2f67bb1a0e5901be444b74e38d9a77041a303a2d54fd0e58d1e4dd4d4e4fc063b62dd7b4d4b6edf3d89c838bf945a009910e5600ebeb13572cc0c98bd8ab5af9baa339d490fd5c6c15de46a2771f46cc9f0366e719d911e57a358826412b4fabf0864bb9d25517dccbb36c07b572fc81b3b5a52e73a33e25b3deb908bc6dfc1996434c3650d40e068d16ba1608cfca57ad614013a77cdc24a33f2a1a62e0b88a562e6e1090b34f445985da48ef65cdb255f69e866aed60bd133cd129d874acbf2352a19c61a7c2b0d81d831f0cef6c2a5d2acf4c283c63ec5ab274c94fe4db62957c745b021d9919739d9481c7022e56af24d3636aa9cf0a4b0cfe233b1ca9c5e204959e6d48e13a6344de3b33d8a158175e14d263d981da7643b19ece08b962f3ee097a4ef0d6681b48d7b3815b7bfb58e73728114e5179f01f18fc54ccecef11f587cd7ee460c68e8920b3099c8f2503324b74b4678f8ee4aaed721d16cbf64233dff433d555b1efc426064c40442ab313e48660583be12e8fb6cdc04015ab99c8691abdb6a4dd14fc29b5e848c7bf789455e69c96e028ff9fa2a38e4a4e3f42c2bc80200459c89559fc51061960779a632c3f633fece7150fbc826b4eb301b1e5f6838fb142580956a20abdc681b25829058b5c8cdbbabbbda26585f24c28b3bc7adb243b609fcbfd76d09b11c6f705f9eeadeff866bccbe347389ad29be2fe5c797721c1a073198bb26cf7b99e407d146d508ae275036b0c69507f7ffb0b028bcb3b9f28f558c27bb16d6f088f629cf387581c3dab679065d4b4b8b7598bfa8dab796b6d39aab61d1550b54b0b734e3969753178d479604100109996972de361b64991a49b08b79b1cd248f0784c5873c0218e4afa9cf2f43e7e05545546ab6647caba3b06e3055f665756d114f43a2f6cccb3600da6dc3b2c25ce1bcb8bfa5a752a45ebbe0e9acdc6029366620d77bc3eae5c05fd495d72abb6424b9466658d1b6777050be21d119149f8d8fd6c1249189780ec55e2e5bdfe8c7fb58fa2358864729f46ef9a46166881bad1cc2a46df50c9e043cc93baf442b3bc75f0a1e96db3c7e3bd1dc28873b271aaa4914ee9cc4ab1f400b9c801a430dbb8b240f80d82f6c98becd844640e8554359653d2d42c41dbd930f302fc524b7928e13511f3a135b01cefd9a55424396fd36d228a4686f87f69a161085de34d071c201f3b3ba80a1eaa7cc918488d337fda443135ac8ea84c347d1bfd22206b96084a2a4a756d378085f7733f83450333693bcfdf838c62054ffd32c21c79beb678286d0b51574def42003b6ca583f58d5cb95437a0fc2c41d1c3d69a9ee0296b8b12844211828a9e9026f5536e413fabef906fe8182af1c5c811fbdeabe300d6f2358c0bd95a99ad0f7435bb5d59c7792b46aa77c10e5e016670ef4eeee5f37d102bbd764a77179e061d317d7080d626a0aad29f53b8ebaf375933f1b403831d87ad272720c33580ab174de6c961cb644a03f5c22c7a43b13decf8583373ef12ae8a70c40ca0190cf99d077fe09f0452ebe55426792c8201479c8826971ef0bea342e5371612a678dded6a918cc7bd5a0b9a5e6fd6f3e39e0c3b0ea2ca0d8d42004bf6319b02452148c0c22131ad76c26743bab6a5795eeab94fe1d9056c404bdc76d14780d428ce558b97f8228235577bd181a2dc3dc71141bf089d1b8e786213de70a80e5bd0d420faf5f6bf7704b61ba97164e1a4bcded88e2fd28674008cb1356b892e45d402bbad7012915bf76f1a98ce802f8966e74629629a575928d5ae412de5d41ced84ad18bf4eb51c0f017b6c3177678ef9ebcd15ce616bca0ee12a8ae8f599caea89d2eef0b453685e629a488449f0f65be50a454f8ad50c32989d540256300f4b6e580e1854b880c9b137bbdaad06f3464e2bf6f513dce03fd0510715ebc046a8e5777b8a695ba9bad6d6633a21b36b4b0eeb22af91ce04a9dc0d51557d2191f12ff34cf93ea87cd47d756c37db2a81ec4ce307e49dc123aa1f4f741078f6f2aeb43de00adbe2b5d44d0acfbe75a1e76583a0c5c7e365e2d160602ed2316a4070a88c4161fb28b5516059d6aa6a22b9c59c4f3e8c6acfe2e52eaf46317fae1b623425974f7e85414d3a0ea46c41b263af5b22264e19b93b5b7b163085ca418123a2c3f6461a1f3b4a4386ed5a7216cea36b844bbcb7b6ee251c0c930010fa65a6a9073e818c7e41bfbe0e391559f61e1f6e22b87268b0db8dcec5198ab377e5884dfc37156721f92128ec27308f8a0908fa07e823fde2c1dd16af053a1d112a08c565d5dd7b327540d393572d5ad81917ae51b39f1bece4fd01009badefdefc6a8e585083bdb9db266cd89b9abda94aa2bf23e3ac1a8be7c8a2852a33d8bb7c030427a983c8bc8bd8b5d068e4d388e881e59c0356b99e2503452d2b746b73e90f348302d442d45d8880eb95aaa6b9352ac017807247bc9b1a8c50b165d4215c7a37e2b7f3ccf56657eee9ae67dc29631cf22c68a623ec567d0e5367334217d88d8a55040c6efd0991d7c879c20f82e6aae7a843f7146dafe4843a378d06899875514aaa832f6b1010b4bbdda6bb1bb009ac885b1d2015e82d2dc789e8e138c06ececef52748214fa74dad2c9ab24c55e36b32601f0148028cd5df91fc00451201ced4c08c6eb19993a283e636b2a38b49278d866ae920e9c4f16fe7e1df53c27bc23f3d9cf9070fd1ebe56a7bf88ffc38f8749f48d403790785924d6b7f1b048384e5ab4b9d3ca9119ea26a02109e2efd84abec599932d917c5dd98dc0ae5dd552883e8c6e077009b7203dba00a53ad2d8703eae5016a5d901d9ee1f588a827d9e4eb5e241235f8ce57d60a38c2bb0188efce5ef99d3fcc01b107dd40ff4193f82847766bb85b130108866420a54c3386739b34cf9c8a17fc020f61ce8106fff68db26c03330b356bd55ffd62a6fae11c1ba2b9ae984ad3f442cc3a50236786ce28df9ba4cd5d83f106fab5205106a1a395e054d7bf228149a140e71a8f44d5adf6a72f413cefacbfaa7d7d595e400fbb504a505a42041c48a13b85eb4e3278deb36536e32c0f754cb9faba6a16d076176fb11317e8077e361e97ddc6a9687e34d20decb900772d7ca1eb8715c4dd6569f009cfaedf3b810f3dd07400ab03dc69431f2d48c37655f00742d8e1b959f9972fd37e8615c059a315606e8c07495765f6d4bafe8d956fdf6023ce6f3fbebf289ceb9ce40064d78503bc9d26014cc25f43c461857521a695becd8eedb30c9b06cb248cbfe4bafbdb8e879a092932913adee89811103ee65bf36650a5468e0f3b2d504f1bd38744238bda47497b5350a7349b7ac7124ca7a75e165e3edc7f61c8a7b870ea841d1d1baccb90a4a0f50c6169901bae0fb0b112c5594822af8a20a5637eac0513e1ed07ce595c2a1386977a4e4ad207e1f1c0b2bb026699116b1778c837826673e39e11c7a44d9d1814c43abd23c968044d4324e6df216110c932bedfe9ada727e6f862d12b6f06f42a0cc8759408e3488b0831861c5bd33d82e71ac8fab886ee9c5081d1275b1bd6ef8708d587ec29f1a8da6782120c302c6aecdb81d22b0300adfb4d4a625d818c738c29679b384afa926e5ec687fe721682ddb2d2d8b8bbe59c1e163cf1262d976d6690dd1febcbfa9d021010407014adb061a5c6d86c2532f8ef69bfe79ca88ae41a57a8072056182a749726eb75d09c70b7768956bce57b4adcc2ab80efe4a2332f8b314605d531480b9bc51a9487e432576ff8d8650ced370d68c5255285a9a676b8cc10e7842c2efb07fb2b4d7067ec404ca76abbda7717c9db9501284f1a49b158106c99836d4adc48c046e28647f08a0f8cb4221232c1e230437a166ceb21350cae41959014de91274d92b002d2bf889a608ca8fbd5af0ae6ef5e6d5d135900ba4fc0c2f95363e97226202697c3f1e2e16fdb7e384444beb28303c1c291413a43643b1560cfa4be2f7a2262a557458c52033bb6202bb40c8d5fff41e2147515240e6d75f3d1cb1613f80cfd7bf08efd87709537ca7bff4f725424e825633a007a33c8d72d2c3c9e7ea3ce9cae849736ce6468bbeba45238394586779177c7ed39c42f29ca6ed639d24cf8db322a392090a919b76b8ad22757c07bba299712be6248a09c176ef84adfff6c7ee3ff832c9cdfd893838b58baeed7e762da42acea787d7d5e48de889ca8145187f0a8e96dc0a411f8897fbab1dc6d0f8eb9660cea256e61e02f18223e5089745d36f915de2fcdbb53554b4ea8fe1e6f672efef7639100bb71d670b9bd9bc5ea5ec506359197768b01438d43b82435f985da2cdc090cdf46ce477191a48e0da8a730c22a9ca82b6020cd6c0d6d09886a7138a0a3c874f8e4a066ff9bf1c5c8b40a5ef66d1c016c9fee06781e7e78456a7404a40d317fded50d120d6f2ae7b762736608b7573d692d42daf0ccbf14128836fc6be15355a65d4b00730f8d36af26c76d7087fe915ce63857ad1dd5bfe4ac474499ebf5c77b42d83a955714b95ca266511c4a919ede1e4c92ce99d3cae40ee7ef43e56793afb58b5f6b97cbeb1211e63e5f525a1b6f62d9d7568e8c4e5396b1f19bc07179e15e4750380985411f376b3dbc078d9efd89f7181d42f6b02234b4de0f279c3a3ac65f172c204a6bc666324e3485398d8da4e5bdae6790efae72f891730ceaccd73bb17d6fae63fba9d052b6270ed9b6656f0a4a965e38255c447047f40c2bfe488c1a6ce6c60fbfdd47029f64c3cda3367a288cb292ba374a2229d4e04daccf04b43fa9ad6db5677f69dfb111db405d05de7cea443b12bd61077b1d609f305dee7139c06e4d8e3a95d7a74e7051e039f55efb25c20a56d188a427df7aae7c117b900b65ab1def5667a7a0a7f2bc457fd21ebee29f09056bac5d8331d70f97df48af3f3ad627e8933fa3d845800892f41e685f1983d289d407d01ede813f1ad7cc7c8f4142be8b6106874035f7114806b938417d0deaf7d112d4511ce1fb194eac1531331bc06ac3df18e8c7a61abe266a9892bc4c2a8cd1225ea867b5ac14987043a2906c4f3c72499e8b05b767db9741dcf079d60dba789442eba2ee276c6fadcdd9b54330e1c23f91b3a90c193e9c130c9713534a44ff0b00d90c8e094bb2887206408dc61a5549c60e81e62d76878c50360620bbfccab2d3f718dc20a7b43ae445647aacab43bba855c8609e9df3b02c765650db29f1095f69cea93b102e97709d11b465f5f163bd5efa96422ddf3739c15d6b02001670ab94b45ed2077e3355e652a133b01bf8b9ee05ed8a3e43454e1b0964781fd01b541c106f234038471246e9bbb112d26faf02b8a3e0f8ab285fa841375887c7a2a4aa406e18294a84883013e7b3f11fd65aa48e2b80662e87f3cbfa7d85cd3a1e113b840a2bbd7696807ec3db07ead9430989b7da1d39b7046e2ca156b79666382cfebe577f15949ab98434695ba8fbef87a8fc3391e49972466bcf46eabd8423b13320ee5a775a31587a5e41a5a447c0350134b4d7902f10d1d172cd928e1d7b8c16ab99aa470d118b2e3c4369b8aad2c7bdb61cb095a026a7ceb4045cec46f3a82c09108568680c0c594eb83252518ae625f34eac77767fb86bfe86faf8af2aeb3dfaf0aa4743edbc04bd43f480212b5e8c06d616d89d749ad0818bf2a04f9e1aa14a8a718efcc66a5b04934017b112a4bef30eae5f729f6968ba10e68cb11f4a40adc120fccf304849d8d1ef8ad9612dceb12068e4f49267db115c10773ccb2b26e45d04dea31742e23c8b8a200a73082fe7e0746953f9c5c3186b46bacf66641bffc9eca06fa41eb01bea40ad1a4dfd17aa510ecd3571ba26841b069ff34af224700ce7b150409153116b68f789589d409dec1cd98192fd6a0af48f9ac6ce2ec3a4de26ee75893a2f46930e7f07b03235d2c234a185d7b9c58fe58d13e83e6752675a1e3a3c2da9132b5f7e55a593af69b6301cea52bf7d6f008fe769dba07fac9e808d108c2ba752f4116c09fb166c02fa3d0fba63644cff82e58381cf547e703343e14a9680a2aad15a2e1e66c1582b045a317347afa81467e659bdc517863eac777f044b1ea63b2cae433c48cfe49498e989ac9679721e63327d816845bb0bb7f664b7903dab503526f6d24e18646385b785889c0c61787104c21ba18d3342a64d1074d3ae240eeb4bcd517f8235973fad20a64a387bcedc29dd83f502accb831341812a51e62012d4ff3d460f03f07145099396ee6606950e91a09e5a7faa590bbac57104facdf2d5848fce0a06f84c4a9af0ceeffcb28a4e66144bc12171cb66d62a4139321eff9b30da104a1c85538cd838a691f5d9edb9be71cadc69fe3c6d3198346222c0805ee34c7293102dfe88288170cd1efae85e4aac8c858f7c64093228eb67574f0acc13ffeb9029d1deb37e7501400410189d3a4c67d70425e005cf1685b0b69d152bfa9af894f9ab7c15e60b2537f864b37aeb03f9b3f2f79a353f55029a6d9b1a66039ab53d53fd814d21b43b42f7e36a7ca5e3dfb0f31dc64eca037ad53c091d65ef52bb391c31d5c53f7a73af98cc3753b90b0f1e5274d91029c00cf0f1d0def063be2bfc4854ce33be0fad071dfb6c4f4ff67e372dca1af78d013a909949f9ca9aa069a542a8979f25d4a115d7f82c5b9977e5a135bfb1fca5c97badc0562346e9d269c50545bb3166f526a9f1c160956b61797ca9d2dc3261d063ddf66aa8368086dcbdf4c59cd0ed778eeae4dfbaf6a545cecd22f849d89ea144673b1abd2bfd81c8fa77ed962a51a980ec63fabb68b4d7e94ba2ef8474ff93d45b431035efaa9049927c4942d4f69f777e1402c54df2a4531d52da29aa63a6e5bcc7cd1ddba773954ee727105f6154198410f17699af6c4b079d59ef300d810fd90b6ef986e7bf61eccc79c593702e480004fde06fac26297ac2de1e16992dbfe2dff1a8e1d5d0e3ef416a07f898af15f189459692c3e127a694cb21b9db05980028b90b3c2cd16e8ea64fea6d667adbe8dfc323609d18d7bfaefd8a538439d71e68fd486489748d6075f88591ce4225bac8907bcb750942503d9350d4faa4103fdd1cfd9f39f4473887637a577d1854c9178ba30c8b4c49854c7459aad663bd05d3770f5c33c886f9a395bb71ba01d1eec2495cae8f01d9d6879ec471254821e173d2b193a4f1321f3af2c47b5b206ba5ace0360057f06a5ce252ab1480ad9035f5d58ed257bed9972fb20a92f4568b05066c1ee149500323798a26f3132ea5b1f20f76191161f0a18d7c5e9c5534604622e03f955e97bf447fdd9522668a988b10e37ced23597d5e1a1db78045baeafacf6aa42f224317a859bf366198c84d351fa4ceea3fb01bf5139e01c3a51fccafa0f0223c30d18ee2dbab4a6ad60723ef61ff2d1362999e04ef6b2d0eac53bb86e3858e81b9511fc26ac4090a5e0f3991807344ee8c076b25f7706077ed846c08cee490f02b74c3e1db3bfb0bd0514c761e30264746973477abd44e7566412b2b5736d5134483ba6fec687fe258e1f5b33a0886e6917d5cf96ed1aa3f879ba04700d204c90e99c1e1a790134bffd32d3ecaf686dc878b8e716beda618807472251f135123bdbedabdcf8562536fc1febebcbb29ab7263041aadfa510b4fb7b4b567ca1d3e86ec871410c5053e775f35b7442615e4d79681f1fd48928a7760b18a77372957010adb751b8c3522609862e6ff1746252c2b630bd79ff4a15c3507d50239f9e3f9a0a7f47032b50d52bad4f81ec0a517c528b2d4ff5772e70d0eddb24ab4d6789d734e64df70ba97b4f0950a98f8d83c129f1f6e3f8f238aa48a5261253aaa20ba0e184e95074347796eb87e9150089e3fdf2061a209449e5ce5fd476f98f68bdee78281a24fc0e6bc42d04fa3c9d793dbeb34be44752711b0e8d9f57877be8fa08cf9ebdd07c1d799a10d01e58fa9890e1089cf8323842ba0cc6f14c6ed111d2954e722c31d12e170181f6231033acc44d35546b5cefd4bc950ab68aad07166d41d22908fb8545c3600ae26d4e41aae7b7da8b5a228acc858330425ae4a8c5a17ab55f08d80ac63d5334d2e84ef3716210b3f1157108057354db868345b6a4f648de524cf444f192b58009a1e8ae4e69eceaaa01f60cb83bcc109905d619f8ff52ba83f9b527c83a7d9e2bcfc61b0840198e45175d5f17925c9449cf19d42864e4839bdf683a11f1f5452415f78ab151e412911e6ad4ac96a08473b1517bff763815b3b1684bb3c8ec4e65a63587ee50cb8492b67f7dfa3259d8e18b4008563c0f31a0c9065b12091576fd7f1a18a52b62d6ab4d9e1c3bff10dcf822ddb0f85268b66e67abd079c190e2f4cb3a0fdb0c2826102de806b33fae2027f2f697b064b594165b3909deadc2545c0b710b985640adff85c96ea25a13ebcf751825500cba7740f35c128faf7c2944190831a36a5439f0330b6e3b25622c4110570a2387a09c3a540e53410c3eaa8d3a8189477b75259fb66cb4bceadc8231214d36e7ade98d972c401731c932b6cec9b999874cdfb30e647748a7400919a542c1ca8ffb97489f20ef5011d81842db6b0f2081d41b8e74584555b255edbdaed78e0b6284b0be10b48048a27f187e574538cbb588b31d6b3bfde32aa96b4c13c9945cdeb04b339ff836e8e2689e5f44b52c6263f78f987537262691bca385f161493a3738b6850a342852517e2b90dc337cb871bea077839ff63a6d6bec7114f2c3de0a46425c3e22837895ddd230d8c1781a4c64aec51bcc510b2789dfcb9a96f078a45c84b8f53a0c39fb54da4216f60ce70d65c0d065fa8e28ca87f46b1b9ac4e41cfb5e535fb1dc5df7085b1332d3ac0aa244411b14ca75f5e0ef10107183052feacbc5cd500d59fac2a24d04416390470c5d8b4d3b22c7355b4a76cd206954ab94755a6757d143cd3ebe2d2db85b6cb1633492ad8e1ffa3c5c1b409a9cad893ad01ba58ce1001aa4882353709851377fb7d6ca63f4d5f8ea166fd222f2af9a78192b89b52f4b5d4d84d360c9f5bd43a13e487450db116c9fc5c5c4e3624da965e6b5ef90b59504f15560d9f10a6c8a6b5ddfd7edf706b564fd624b9949a60d0215024702660e7feefcd598daac56a156f4d9685f7d8eae0a5fef49141786b94fc5bd9f63ad47e28f7c42edaaf75bfd8afa5dbbc8751cd79cefd7f67b56b7fbbdb7ab6775bb3507c5f7b5874d3b7d6f57cfea76ab668da94d2ac5798bb3cef524d2bf99c3acd13d3d45d7b9318adc003067a5e4903b00e6a894eca4d791351ab9eeacb350fb84d784d52541720b2871e00b7f1261d573c53428920b437ff43ea4a33568ede719bac28de54845b9303c05bd7db4868f7e53ede74be9306cd7e6d879fadfea57ddfadab6f441c55c850a60faef6f70ffffff7fceffbc1eef7f1e8fff2d8f67753b1edd6afdfb5eb73746d973cce6ce4c1f379667e6ad574396643f4b9c49439944efe7eabfbfc1fdffffff39fff37abcff793cfeb73c9ed5ed7874ab5d66f7dde71969b3f98f69cc1a4d5c1a1305b326cfc85acfb350ebe3ce4a21250e8769cc15c8a5318988cc9a9159d3cb5ef6bee699ee9dab47751dbda146c85a9ea138ac248e1f640dadf1f3b57986da2885a0b5129767a928447a564c93aa02b55181ee68d115da5f4f726790dc17be5e8eb1ba1c3bdb1cc3e707f9ef6f70ffffff7fceffbc1eef7f1e8fff2d8f67753b1edb4d8f5a8f205c3b53d80c705569668761d34e7ffa512fcf4e3a6c3e1583fff42ae6a19efbbeb7dfb3badd5fd79cefd7f67b56b7fbbde756dfdbd5b3baddda73abefedea59dd6eedb9d5f776f5ac6eb7fad4dbf2810661843770a20a810757e6e67c65319f988abef7debc0681ad20d01a0466cd3914857428abd920175f39efdb65385e46977b75cb07abb85c977afb6431cd19ab2eb228430ce32628bba5d2ae490acb5c91462adabdf796f4c5c149e5fcc24199c4f0645269f7de9b429d26a472cea8a6dd7baf5649e16d50dc4146a07d237e3ee24dbd2d56c95b3b6471a539e7d0b662dba8edf27db3411e8a3a720e6d3ab076195f34edde7b5f21189d412f214284f85e381bbc38b2abda89094599e6622886a18c3649d9200f6932543636c83ffc859355461733f8850b181c883bc8e52f48042ecf3767182f5cccc8c862623098178c02984c5199b033d639e7101f6185c0b1b91dce0d2677099d0ab24cdc54d095415663f86c3d6cadd6add62915ca42ad465348aba88cb931b7daa050a228fe3c8669d46ff57ded3746ce981b7363ae45da481ee6cdd4e15c4f9d524889d3bd5093518752d0ac29bbe9c3bc8d3bef75ddf7ae33699556999c66da3d5808fa84bce7d89a63381d373f76dc9828d5fba9283cd5c98599e5d9fb3ec77edfafab74afaad4cc954aa55ff77d35a6362a9cea475785fcaffb8efaa326b56808682dcacfd7d3b55f89dfeafffaef8c2ec748bf29ead89a6a598bfceafdfab5fe5a6b57e8234a5571ea9c3bbfd6ff94f56f0c4fd6c81c7e0ce9767880d3fbee755dff31b569b56e3af4733d897433c76fa247d66c6c3820bd1eb9963894a75de81765dd75c8dd300d6a248a8a7da7aff77501a653e98422a152a3146aa2aa52b5da2b164bf681d997ead435ba5a442d2e219717d0cb4be505f3818189b058188bc931320c82604a8a11948138c7e4307641d005de19118b1f988fca4b05f4020ab984442da2d135ea54939c5c69891d842b2cac1283cdb257e28d8925067b97aad244cd516a4442ed08545036482a9d4aa7e9349da653e98422a152a3146aa2aa52b5da2b160babb542da2df26f9eead435ba5a442d2e219717d0cb4be505f3818189b058188bc931322c930965643232d90c8c730670cb28c2c3086b56f49c332a4db73b54915f2446f6052e24d103880a282021c287871432a8e28a2a8aa2288a4cb40144e0d022c2fb19863cf1392011410d613886800200e8068c4f6787e8e160000bdc3eb1a27ad92438294ef8f3f99472e00029b5d82eea65718c18b580738508fb44c28ebd92c1b4b15fea6589e094b71d532f4be48f46b041fa61a7ea65737ee0728e309bd830ea6573a48c96ec560d5b14e925198a30b1a7280285a4c446dd28d1b06136438030a2bc0108294639e8944012a2c3c3c94ea997d5c921b1a0c3c45ca2630549ca669550e6d8a41e7e972b147b46bd6c477244c7c23cb2cd1aa58f8dbe3052b24f41f66fb3d4cb1a516246d92bf5b246a628996c558d12093da8388243c49125371ce19940ec52bdec911e9b204820adb055ea65839881c4ee967ad92070600e24b9350140bd6cce0635542f5b441bf238e710c45ac414a8721bbc3c8738a4c959bc6208de9c2fda006c02d00b22fdc40669a09c75688b6660917a00e6613449d92a291b7ca1e61cdeeb420d739e516f3d3d6aec744dda1476685c27072cc0c42f67dda1061c2106176a3c089c3c4901c4d598f2fb0ebb53819311857db9cf3a74c1b379088e2e1aa8cae0a9b4f06601d8535b3eec0f5669a1005b6cf07f605369993320f29b13c85cc502b0a77e5c32421e866b786bd721320fdd120fe6074035db50a3a447c5393004b30dd3d0c29a1e98e6d3230c6fe10e3f15f31087694e15f3b00d797806d610de609acbb23ed1c62ab9a1d2429d8a9bd0c29c8a790866916400ab68906c005433c7a4022c1238d1539367618d4c80450d6bd85653b55051592cb1a24795f1edf886695e9cb069b78f07e459ca315f8086067926e223c724c72b9c85630edef26c735c72ccc135cf4c8e39d849257e726c3aa1f22c85573ce42c8e790579d6e29ca73c6da5ac749566ae4a3347334fa14ea5f4e61829f39199779a394bda2353519a412eda073a545ae616803d8d336f5b9dcefbded715c4e19a8c39d67109b898e3da0ee43d017bf04cf75aad56c20cccc129bf3be3b2b91c0cdee01d7abc9de112590cc3dce672638364089a815c5846945bb8863715f64981f0c1a51d19508663722c8489309f97ca0bc825d422728da9e6646b655f57be2c96d55695686a84229d4aa6f334954e24d42865a22caa1532ebd6c84569c8056a5171f9bc88af1026c3702c0463ae6c8506822a130228cb31398c81a2f85379a9805e40219790a84534ba469d6a92932badd083708585b569ec4d63ef52559aa8394a89e2af742a9da6d3740689bca804cb75ca96d188200003190000c23014c59128c9d11ce894071400144a60505c54309c47a481582408e23808a11806401886010000821800aaa8625204f073b99f25f659e99ff5feacabff2c6b41efb31443e3feac82e1d47e634d3e4157188fce5c0a6a87c2e4935b61f7d820f73a40b91b76f559585be0c6ca1afb2cf4cf7297b3e83f6befb36ecfa2c7286e8691955b819225b751ec8067d83ef7e9b81b965a3883b10229ebaeb3ea3f4b8f65f90df24c3f2bddb3dc9e15546b35fb049d61c8722b3e9fa87b61e6b23d011f4b69911e791852457cb296bb064746562899f4ae2fc4a7cb3724b664c87755b44b37937bb67eed99fb6c0f4fa0b929e4c90a49ef610afa0e858ec10ab7d1ec0970005c792ae9717a39d154b22e5639129c797139f44bc987eff6e3e6faa848043d909df57ee3bda299990b8a13a89758f6add6b6d4847341c809721052b5410102e49c31a8c279ed209ae90eb0744d0c8f3649c659541519cdb1ab85c2a37a907bf6758a1370507162ee6b52df2adda593f5a47147d8b5a4faa741407e674eae16522444cb8ab88e6e4b92bba65d206895d8d48c4db0ab13e61ba543981d360e18ef3624630566fc3da50e58bd704bbaa88b900d20e1ad8ddc424411726e49ff0da50c170df0a34bcf4d4ef699ce91f67867df31aac00df7514f5a330e708919c9737e500f412cd60d312b5eb11055d0b611ed5c6e0d026d89f2447abdfe44480e8eaf252b7adab16ac10866567d03a1183aaffa1eeecdbb19df785f95280f58bebf4f7b2439fa89cc3d498703f61d33fdb3dbc735d0e34017b1f9ba8001715947d1f07810d8ce0911dc9cdb40b9f3aef732caa3d2951d496abae9179755483f0a9f518207ea7cec31ddfd55a2e18f2827a076f2680a8d3fdc7009bbec40d7abefe7e91e9cf730bf96698723014b33fa7068773630113720f651ff385b15fae75e8f25e2ab811c8447cee128c4b69a47ccc8fa47164f9ad99d51ca1a4a83ef5c1fdc17460076643dd230ba4814cc9f2f71980a2b20049158aa9b6c268ddebb51512509121e9eec274001b5cf557a6f533af6ce213c084af1c5a87dbecc9773ec7693d3e78820daa40dcfc2cc10a9b54a905bc5a7ab2906200d09b12c4f206a491bf84d3068228db2bd09ddf90884ab413d624b88b459a3f3188abbe04d7ebb70b3c6f0eb31fb30e02ded55f8ac96ebf4eaa8f76e603d5419fb92ce670cce2ddd38529f16a728da8331388da67aa39d1b794108248670ace17d9455ec8014741b9a9f27ca8e8a96cd6c31412b087b5bf9d76dbab519e47c57987087510080e1df2baee809e2ebdf659912fc455c7ced439663c051769f237538166259bef75b49bac1d731ac8fb550223e9bb237318d11c3b875ec114ee4266de1171ccb4d8cb414e6d7ded92c77e0b229be3e42164198da8426adf0f2882c8230d5362a663267c9e9c714aabec7492067d4f3a895232454c0cfc547cd623e1c26e3b841d4dcbfbb1d39c52c8238771ba121c11eeba306420fceec5e2b875925079a2b300ad3d4fca846125c49c1d416a1e462ca263469859747641184299bc00d6a3536ea2117fa3ae19818c4cb3e899720aee5905d3087df33e415c6b99bd8d68ebfcf9857107336a14d2bfcc4c75448f82d96d48cc6add6e6a460c25282a5f6ccd2ba63ee450a16e0ec415d30db22c56ce1998c7e1f30a2d1c40103bfb53f52caf2cf0d7d3b585d9df589c900d3b5032a0309d544bf8b502d99ea927e932d19d9d80dfa1c25d85339d7038b671a64c8f3fffbad2a3ecfa89fd8f8b4a57da6a577653c81b1ab19c1cac63385bc5d88ffcf129495fef7b46cf81f23a4e635fb83b840147397fff4ed7daf8bcf33a38ee5f1391e26862320479e7fefeb75f138a3dec4c6a797f6322dfd959ddf7fefb76a16373b9b517a88502f5bb483c925b6bbb2da1326e001e571f3bcae94c23194f2038d4e199a4b547247723a7b5e574bb8704d66ad9901fa8e6a91c2d49912cfa8919d4ce6adcd955a5354113fddcc89618c8c90006f9f4cbc88662db718d13c403fa9618f189da1c57b984bd97ae8fee01639fab814baa6ea01a41492664ee3586faba53355014000f00400eaf0500e0762305ccb0e57169fe279c5142cb5666e4d37e3402ca18e8253cfc5c8650a73849f9fac9779581aeadadaa419ded0682a6a00e60d4539cc732a5e30942caf6472b29c1efb76484d96ad0c10d68a4012641a008a21bfc80092ee17161e3b6137723085ad97086d0bcdbf18050396475201983c15f9482a2c643a15adfa68492a81a93150644038028000e00900d4e1a11c0ec460b8960186f033de5ce12e27fab0b51607d2895a407b903646b5eb388a65d1cf4f2d14e4b5844ed73418af6eacc7fa56f277a040cea06921deb4954d5cb45df152698a7d2be1a542c710557f85a6ad79fc521dbbd39aeec024f655f6b9d45408cc632ec9b7ed08194ab12820f2deadc10f5decca8482549961ed65b251cdc95704fcbebbe8ca875878a95bcd7db0a5cab940a111a8d8ced2931279b04a8a13082d27962a35f3304d97be057835157a3fd92c104f13f300654bf066c61bb3eb92df08a3e43b4de79211b0f01e14bea4e9a421ce4909a00f2f0fb329db19a85235e6e4fd25b55ccf0826e8d8cc7386613715830cdf6b517c6f1d066aa0e51d58e06440736dc87a62ead5058b16cc37a62dd0638aa315e25ece9ebd1e54d7746b00aca9f1a0d478a3a263fd2152d862c228fe0bee0f2af6b953e1b9dba8baaf93122ea6339c7b1b710bec055620d26cb1c8c6e01a0eb000a10f361f74e3bd0adbf060c101d8df84c67d3002406cfe93add5024194014efa20001ba688fd566ac4bf154280e27fa7c38b4424c59f9495bb7e37175cffb671aa9e0f16eb53704a0035f129190cc1c9e9f8e050dda4d01cdf8cad4be4e8f442406237bd676a6ec41d1128da561dc28fb3a272f26671ad888ec451c0676417e0e57e04bc9cb9bae1802740a370063ba09fdf321eb2d1594902f6802aa88792647b8d249d8dfbc0cd00d7b16e3ccf9f583c391c393e60b0646c7c61cebe7012bdab8cf73bde8cd24024d625a46a3c50b7143ae1c158bb8a3f4ff3bfa802ea2e67243749b612da2a13a0d5aa08ab095b1526439bdcde6435147bbf7e8b4894b89f1cd4e233944e20e6c4f15a425ec05f2cfd9b276440941fdd8f10dd1b2e4339d9403593db61500dfe6e1f23a16a204c186fcaef47d38e60c0a0b53411311e7a3d450bb9cff6bceeea0ac35e63d04b4cc14fd4b09f21eb7383e9f34a2d568d5e08c2cfe8194dabd06911537d0777987dda7e2c10ea497c221a7f03d733866b3fa98a5701b1e6105dd7e78e019a3f9328f98298f4972106b88f519c488aeb390011085ef2281d16a65e248c288417b3fc03929301359ea22101a6651ee0d2a4ff37d2ccf00d885b17673199d6df2883d521e5aa9824a726c76899d8c1805df0c53f79fe375d205bf9273326211352aa2d7d9f44e89a84d77419d0f02ba7df69ba57342887ad129e7ede51d32eba5fd2ff0f5e00975958fca5136d0e80236fe2e53185faec9797445ac8255ba4b4e7775e18835fa4c85426311c645a831a4de9baec4f9af240e39a03726f605bed660c0d485b9d8fbfb14496f2d3c1f6f88f54eb5ddd112f47c09235ce7620900325f94a369a4be51837c8f12fe6388aaf438e668168b60a803a019c46b1300afe7d35e69370c8960fa302468b9310a257d51d17d02cc7b444793db54290ff4dda2cbd2e75a51f80d2283529ffeede9a2be23fbbc3873887b1d319872b0c2dc04fc3fb3e867186d5b813447b22ce74cad0dcd23749d742b66fb64ea3a02b6614ab79e3a1f50dd425e94e3385106621d43b378ba161a9d10ab30aeb65fdaf4063efc3a150005afaf8335b5a5628b40c59bdf1e07be612de1efcae842e1356f8713424c412a5b7cfdc644c361e6dbc97ff612866d28a93bc6fe7f8c5d183de1e3300d36fdc35332d73174d007f6e99df3e2d57c85da6bb98630234156d6f71d65b4de7741fb720661eab47a1ceaa0d8e41496278065aa010d53f65f40fb382d028fe6e886d561cc613fae6bd25f3c9770e503732bd26fe3d4dcfe0f5c560bc0d5e6c03b1cf802cb1e6606dff0854ba441634d5c52222c261120b79c85b149767128564600702af15d2d26dfd427d8e3b86d726501d3b51dca405eff10fa68820c8bbdc7e6865527b9a52a656179701cd20436631ba3c2d586ed6e9ba3d197f93fb9675112b88367128ba72b3011254eefb44e85d2bb7e9d789990593c65b3712a88c9faba86df71921490f54b9596fbf2d13da30eec83da707c7a534665fe6acf0015f4a1e75085708628ca72d2f626729d8db9c9de6616d6188e66b3933347d740516306d25b908a93d850005dc46c5adb75281c9634da958c96cdcb275bc0ba34ee6901bd49dd33735be09ccf6131cc352f293c67af564cae86e6f61c4dc4aa06c0171588ac62311743b2e7412495e5cff92bb214b79a29eab1012b3598da1883b225077e9b15033dd854dd659d9e7a56d3a9a842214f01cea8517e6af79316b20fdea939aad24638dd20f959f8c8f5220b780699faa61b15401202a562577dae8ad3885e84f8d64997142f53d59b34b0053b9180d4cdb91d7f692eb585a758aec6990d711ff31dd459d723ce2c2bd1b822de3730ce7d32911471dd892f2a19918a92c19ffe38fe2a54e2da9d0a1e6b2651511d49a602acf2310f66caabacf9fac0f5124b6c638c9a4944d39262169df4564998943842983e9f02f047d34f1dfe2189ea8782e09e2ab451247b594a4f9ef8826d2a837d207590924d72c4d1d0e25f6372e2630dace566e75bf0a919738e324a4b6e3a0ec19989480350da41b954a12921fc184e1b28fb027411bcbf3e000ec68ae4ed7f5664948c914f28c465ea6165f7c58183c9a9a1ee03edcb4bf1f0a3d24b6e1460ae2ed58bf22e3768c66917a465de49e4f9422e273dea03ea6ed06193f2605a6f95d343dc0d47d186e237348bee22fe0caeaa50b1437a1688477018abca869d6ff4933cadc22a2d869d624ae0cdc6712d34d2765c93768ada51ff9def9826d7cf123ffa83b1df6dd114525147307cf0b99f42cf8286a6fd33d951051b75f62c1699e454706466d88caa8f0e21c16dd747a0b0cd894287efd1c7a200a477951a507df43743f59ea1cd7a167baefce5c780899fb657cfe9cb53edb8543e20b05ea151fced431ef3fc79bd58164030935d85b4f778eed3a9f64d623176654ca6acb8eca10115da0befef1686133c7299e9f3cddcaa28e159e892c50352fe31d4fe2acb09cc80c01ca9c6c814a5498d0c681835ae33ccb02553bb8f9d3aa8069997e1ae9cb397fbad08005ca5cc7f5f4bcc8febb6be24ab8b98d6f1e37e2c505d80e576635f0eb4f854735facbdc1b3a9cd54db525ef9477b7c875c07a77222774683213ea4f3260b9c696566cb7788344fda9e21185a3353a9bdbe644722a1b338ce685fe245d47323a90f2bfcec6ac3fb9837a36f8dd7f1fe3cf2bf021fda97e0c5ca3ab65bbb6eb8e64ddb1dd7a53004d8bad4d108c7479b462ec3227a6b49ef924cfbd11550f102b116f0893410203ae04f81ba4ee8e5230067908119ede282d723145a4d481852aacc9fe1987ff01e61a98224af8d011131614c595e839259401954d619546899aef6c3da75af2df94db59659a26823135ef4d9e53fd3dc3e663e42bba19869caec9c8d05132adf4c873da8104de1ae07f05fb4b2cdabb9789a782ac01de833ad061feb31ba555bcc84ea251cf69c777f15475c19782e484cd7b1a5fb5070ade3c76bafd6753e0b4d35fb4c19a1b527dd81bd24f1b0151728153f0bb71bb8611b201c3070ecd54133fb149e08d44553d2963ba1331cea40f096b32e70095dc4001d68c97f46c63aa5e1b56fedf2db182923906ec4c0ab42fc191030e2361cc2a4cc89ecc0796c55185118a3cc9bb8d8a8f1be34d8121eb58f3a5e6e2fdac780eb4974f983bec3302884fd632207fb7096bbb0d170c527f1f3d9ff043404d4aa5f17fdcea644df84d21990d2746950522b94d8c4b68031f4e18d276185c7d4d9e666153b9bd0c82802b3c9a08753279706509f2409e5effc6d114546a8ac3019cbba46bc4366060b91b8fbdd22661537acee98df14753d50e5b8f973a2fc5eca63c1ef4c108198290c2d578c6238fa6a78907c00d9164e8755b7fd4ab2c7e9b0c4cc5e4049176e7272b0796c10cf89c4341d628ecc0fc3bb27b5d7955793b8311458a33930531b652701d24dba22fce50a8915a33a5c643288c671b5184c616e686c6e9a04e80a6782a65f84bbbbf7b39cf08a6eba1257da6e2185efa0041aef0e499ece72246705f89bf2d71108ff98f397a2a4d227f29509fc69a253fd74a76979597c4cea9dad6fcc99fbb81fb35700fabddd55438addedca1ae20b2fe6bcf089c2966f21267b81ecd46156969fc5fa46dbe5b9eb6c494fadd46d4a4836b5844f69d0c398ba15023a546aa868750986733b20ccd2ccc09ccd1419d00cdc88c8948d9c52a96cde412f95431d4071a3be526322210f59cea70bd9a206190b1ec6d6b335922cfe4081c8f3f4bbef06057a02a559e824e99d8378d21b578a392d1156b7f41bf6b335588aaa5e4298a12919f8dd123f5b1561e612841e4cc8e2441c87d531f9524b53b7b88590cee5589ca9393527d1d99cea25325550a0cfcf43683fd748ec8bcc8bc90cd3d0c584120194e30622883719396a3f0902403209140527a00250b7102b15f01a703e6dfb815fab06d8573f43bda38969560beda3df42d29248ac7ee18a49864de6ffe81c0d3f83eedabb6bec87a3ae80be2d01452d86d547d5c886a12512a6b5ce88ea58ca41141296e310e2508a291512967310e2e69ed492429f5ba866f56354bd27323094aec9b010a0a053a6050ca41e3069ae9b91f0d2df454b9cac393b35fcf12a408d7c30b6ec8e95eb450bb8e93c6e28e59acd003c277f44e7f309200089270751600476002618ae11ce7386bd37305085a96f954135dca1ea1768981590ae05146a614d9253ba594520a6d0394039f032e5cb08cb8dfdc8bfe467990d5d3888b08263059ddefd0ce53710fcf50615f8a7bae2bbdd535dcc3bdb6e7e3a3e1c4fe8665124c9027a0005340ab00fbc0bd1bcb98c7adf5b6b72dfcdc9e462ae8b49f998fac87e60224f39092a4a4a41c3a6ad4a88123870de61e8b1eaff40f6cbc297aaafee767ee2387dc877d57e896beacf4f4e451281ae5103a9208b66c2107044def6dc3a336d0d96c46e3a298c4933c0dfbead8b95e8f61d0e9743a2174ba5c2e97cbe5f6de3939688b2466198dad7387e97558d1630a685f84ed28515040fb1b96c362599dc3ccd19c73be330f9645010318f7d8dd238a4a38c618176157e210048c3590c35803b9f36c85409b6d869996dd174eddc7dcd8b33d7aee53d78888a036bc6d5796718f4a64e3e0b4d96c428e64e2dcb3aa75ad25c4f9356cdf4b81994f5156653831ce332dbb50a7d6b0565667bb6717e95805f9c985b2813a553e137950d08062a04150b481731c677befa1cf078d1a47435608cb830a25dc6acc1d44004ee9f1c4023d2a500505128ef06eae3f4f14f5616518e3dbc379c4d84a1d7f3e520787a2349acd66e6741ecbe575ac9563b0117ed4372eec4b8dcc0ccaa233342550c772ea3b6f31b8322907dd64e0628e62d81c84358998bddafe9b58e1d4a004fe0cb44e0a67f6a6cee665715e025f386f31b8affdadbcc5e052d038f3eac66130dfe23f5de34060d3c7d777796f7cb9dc9cbe35d3defb8816d6e2745e0e4e6747c551ff168393096619a89f5534c5260ff7fcf41748d331ecc92bcb3e39e80618c341d30fc6679691ccf05fdcc2fa3724ab19037fdf47c979c3bf8ea79a88e4a0191c8585b80903f3f8b14fc7b43f6b60372c2401fbd1a0b01b96fa9b48fe3330bc94550d83f1327e9966c6df348f8606f59a978374c8fc1a1e41a8978eec3450376928edd3437ef634e8ccbd4138f7c9c740ad8f7e89a3ae90e8d5248f8fd02c27d59d62fb61202e2ffdc6087f5b4e7fd9cb5ef6b2972dd2341b4cf752bd94d5ec6552ad43f3b26a82c7a9613c8b97cf1e06366e2c17274b99eea95d756559eae79674031991eedf1e92ee977437a53bb8851b6a47a5fb49ba7159cd4739967dd2a874de4075fcec759416ea981eea589594d4389e278a7aafe2df6f99e64fa6f88fe98df257a9e8d39b54bdab6ab3d96c36d5a32766a5f24765987a797c62aa1cd33c170fd5335c19ad4056c2bba676c7c344f211901b361bf96fb0c9d28719a24248858a5e433f2f301e355141684888e5cda18ed3eb2f8b5f59be3227449c70d0644a93274f1ffff18421a2a1a12541fd6f7049d3fe371855e58956101129e9059b44406cda28c678c50a9611bfb22b53b20b3a71e167dfa37ea58fe684e7644742c67983514affdf6054d2e757e8a22cfad0d0d0100b16324c56ac58b1a2458b162ca3162d584657967d0b960bfc8b9864f99bbc237abd007242a118dcfc07df42a4cd7ff0cd37d2e63fa0726cddd6e9b4d66dddd6e9469861efbda9dcc06e1a8c7d1fffd81ffb637feccf0985241b48f64a1cc531971bc5205641ecdc46b28398a103549c9cb0d8b0b1d9ede4e474c3af0792edc4ee1edb89dd3dc4511c5722942dc289fde8139db638ecb19dd8dd636f91dd620f2428e472b95c2e972301cb0dbe9d58be9d38df2cdf4e54b0006d383939e11eeefdb0f9e69b6fbe3710bb0812eea9e963784f4b4d608c9198909890989098b416e680c007507c8b5bd4e9c421f6105cb73787da7c081970002218ac4a4ada9b236d1e3c72a408d30f28be85489bffe05b88b4f90f51e41b76c5e0e63ff816226dfe836fbe45a4cdc51f5039b66eeb745aebb66eeb74bb2adde642a4cd837b73a4cd834054c590140c06b11831826ab3b7b84700d7706f2c815c5909443830a6025c72de7a8bb3de7b73ceb917592ef68ae562244bb3b346b49c2e2f30b48ef6b697706be270c7cf3d0717872fac01e4b5c9d55f20f8b0af9b1e96bf9da6a7a9981ac07b5a6a2285f1c0706238396a4e89203b3e76a6848e8e521f518faad7f65d46706f66a86b781ccf13457bbd9b382c3fee85a0b37399f47627b60387c7b31135df2bd5680108207b592d62a4d98001553c3c26521e9a166102073949df5b5acda4f93d12fbb8b5de9a30f4e6df70ef9421c8cf7d97d5191d643e20bc58f63543e533b399d15cb49a9f13246e1a43efb0206ad435337d110110100af406458a2099a05a8c2b58b3a90802e30336f556c75c4dbed577d9055121b4e385d7db2bf3f1704e0011ad70419162058b73d758d05ab46c1cb457a4ef6d8611870220b708c5a6a217b5d8039c746b788795c42166fff52e7708ebf02ce35e0ff7b08e07cb8205cb887b165e489f47ad217178336d8e7c595d1ae221bcc29df1b8b20c05f74e5f8ba21d9dc7732529a27d8ef46fb887f381abc1d1fe954441a311d96c2c584500a041d0a39a10f4d5e96259de1bd10260e21e8a7b29f6f95c919d70626f4260d425e849ae5d7619e92dd083aed477596d03ae1a9b6b5323012a1cbcd5b4b171bdeeab063bf999c05bcd57cfe98c55a73356f3e6e6e5f2d79fd9041286b0abe97d2b75d9a042b0b79a69d7298ec23264b7ea1a1e885a4d9caed39c0353d36ae6749d92547a54ad668912d909d207bf1c1d1daccb409454899d1d221fe8c0a783d551c8841b7a40dc8e4e714e31c75dd9f65d598c65b47ac07084d5c0d01bd488fed6a08303c375c6aa733a963e72580323b320c214e0e2fe98effbd817694e444eefaab92f01f86bc07d9fc36e0e107e195c651971d8c5b979d9b86a4cd3e45bf3b486e4d0ae39bdeb8cfd009dbd2f6fde5db9d572cd9e844fc271244b9e1e63a93b9cff76bbf9af6cafad6c75ed5cc14ade2e5b9beaf282736e13a68219a2dfbb057fbe5b9dc74bb7e7de7c6119f193f7983e8e322f21426c5097111a12352ed4e62573b37fcadb1defb539845204ae34b639845204ecb3afe4b910ff86711e5f812f711f5fe22b7c8c2b7d62d9964a7c4984f11ec771c9394b6071b5d2e93c31af5c5a1e6db978746c8db07375dbff9b3b2cf29a1a6ffc79ba47acfc981a5989e98dabbd6d794cf4c87cb637c132e23c983c73ad6c5b947bdeb6c0f376661a5bb642eca97f79abf27634213ef6b7b2d5b0a95fa666b9f4cf1c9f9f3dbf91674fe7cd8fb29f7d8ac379737ddcf6fc7c23d7c61cc4f333d7f1b38bef72508c9fc840d8c879dbdf47d66362d6d2ae720a0e0883688050f455117dbd1288b106e7c5000a3f19c01a267c54e322c29b99862461f2fd9ba485debf8d49987aff2661dafddbfe99e983eff26f920b64f998b3b013524ef262a8e158c53c01e5c4300a8a45479ce9ca0167bdb9b81ac9d2ecac11f8babcb8bcec92fa0b0b63fc0273009309066db1f6dead9818991819254d323332334ab2cca03328e7600411280d4a23228d66076848d09058f281922823891a1235bf254b4b6e2c1901edbdf71ad79227238679a1a5a5a5c5e635be6e5e372d31e41cb5e0cd5e8b89d2185c92e5c6df78263b26bd8dc2c0c0c0f8f4c96e658989898949614ca69842ccccccccc070c62e858686860627c7494d4d4d4d4e09f209239141674767274ac798349e3e76facdcdcdcd8e3a6e1e6a4c8d19d929c43a4e4bf8685671707070784c942851a284899ee06e1902b3a6643e321fa82c50533f2553a2a28c3d8acfcc67368589d3c86232a3cd68bbeacc00ed87f613958577fe9cf839a1046a65036977703e9d003a61026dde09840208484a6c42b33a9bcd6628502c9932fefcfcfca448b1e437b2a66073879aad5623973a8e97233615013b58bf76cf8379736e5ab8a471fa460ebae529dd3bd2383607c9689f06a293744b964d992a6799fa4b1ad74f8dcb41471aa793344e0bb1f8eed0dc00b318c4c841b855ba21030116f45ec0b8a0c895836e77a86e1444fd244b1e0383de61318632eef0ef715fdfea07f3055996649973ce3973929324274b7e018c171d8605305ee420b62f0ee43ed3e45244383007e12fc3a73815cf926bd70a8f07b9902c7f40f8901ce4f3730fbf6ffad29bb963d6b87f5786661567507ad3df4a6f6eee6f63c9819cd6c753b5115d2a5f8b2c39300711d9ae2bc35ef4636ac276745f96b9ec300e1cd23def7ed5d3ce474ec2ceacf6d5b87b99c70fcc6bde2e05f84bd22c49d324733b944265966404158ce379a2e8d5365acee86fa447b3ca928084ddbb5d0810650839f6ab5dc61a190c8021833bc220e07af0fdec6394e408c1a8a63bab3b58bfb2fc985778454b8f585ee757168c05d2934841631c61991c4958ec0a5dd2b35218a351697ad284e528c1d89561df32f388e5f5f47a7a3dbd9e5e4fafa7d7d3ebe9f5f47a7a3dbd9eb6dff7defbc3d5576bacf1355d449cdbdff80eb68d805c38d92bdb5d21f3afa47e96f9f966cfdb2b74c52d8a9cf3587be894638174ca1d11f9dee5a09cb50fbfe48940ec763b24392a41296c304a8f223b1f8218af5761c34652528dbd7a0ccc7b6f947f7d06ce5839f1c537aa2e141fb5d67bdb08b94d7b1477705929809973367767f638c689cfe2dfd4ecc7eccfec3d8df6e457986fce7750a4a10617e201e7ad53d7aecf037d470e538173ce5b3eaee4f8d89fc12b3ba4f36c5961e10a082ef01882340941b72e56723bcb4fe8851d4a3624f9546822c62004288aa2288a220c4520e0ab4cfd9ba7a28948d9061c130ece3358e58795950ebef62d94562549950f14c15db82c11e67b56833c3cf144241981ae1de561ceee67ef8b5e1c75281ef8d95f906b98062e188525c520ddc3f731e238cd40f43dab22cc8be6e83b89a288b3f4254c2557beff7437c159025438b26ed5acf98722bc75c69ad3192b962a52ab39769d6e283c44ad26e9036f35cbaed3adc394a8d534bb4e3194232b00d77d8294e16ab2ba4e310e138032b59a23ba4ef31086dd6ab6749de61d4ca85acdf3cc51ae00757ad7dc825d46fd82aeea0452c2436a3561ba4ef3ae06a7a83537d9416abd35744038b5e6274fb8354749a169cd1b80f25b3307a89a569344d769d68146d48a9d503549ada62b9038a854ec9c8aa963030000802200e316000020100a864442498cc228946c730f14800d597440685a3692c943a13820863110033114c3500c08500c8331cc38a39052790b06ef7603c01189de3639f212fdb5dc99e0418fba4407b7b7da5f49f0ad1c3216c30ca8372cfcc5293f042cf120ecafd1298e0c37e38be34b145e988f1a261dfde866cbce9d510ef9944592345140e2c2b2d67882ffed0871468ebc3f1d4e52ebe59e1155df5495e284a8104f74975fc0a1c3ff5363facb2b48814e9e0265ce73267a61383036da3097386e4cf603fe9a8426c620f88bfcef64c038e0ed0c10ca4da507da3eb5055dab9565b2d06eb55bd072bb8a3164ea073a1459a96d957e1aaa4ec39a3c3136db725bab9e15233f79d1f6cd1a84d6019b4ad7626ed377830228626403184df78d591d18c3d630fe456284e2ed64eb42a0416a598d9e1043c80d75933e8e0127ca19a13fc2c49bdfb134ae52508eb1b3259db0cd0b68b5e10c07814f2e0f35a7d495a5ba0504b2ec4db0c06889281550f98b54e8572041ee1674b800a40fa81510a82c0537b7a60040d731165398a0653f5b39a2112524e8992276107486fd383ad377711e71ba5f26cf0c378e024a6786cc3ae0c2003f78c055bab0e77fd577f27a0b5be62d266b5f1dbb2533eeb82ae63ae6b4045e1aeaf36bdbf2acd6968749cf127a640d110422e145a9f8567f87ddf0b3dc9af5da6335754a5a7d10702865e21aceb07c44a6359629fbd079cf12070b632d10947f73f33b40688b47889049c6597004961af2c4fbc4e5190d490ff4b39dad438a50d13df70ae9d611236b9135b87a24e0de549fc21b9170583ab628e03c6b04b9aac3b683f85542128fc733f78a30d37e855d61c652cf46c5c738af38e7d52d05caaa66cda3a4241b12d4b47a743e3c53568ba83e5f11de6476d3f3cb51bd441703310efde96eebe2883f2046eda54d0757abd1f5110bd241401822175aee07188a25a58bec87427b01c49a0e7c350d88b715661b2a1a5ca34036642d4334478963538fd2eb741d30d6391807ad894a6e15b6085f74bb0173222c7fe5dd28a2714b31ecda409b85f05de20baeaf699bca83365b90b65b8e06c62c01ba02a1900897cbd335f7441fd66e70397af49cccc2ab4f2ad8f3aaf9260cece89f54a08be006aa23b8c6b1f1f2df343508e5645f2c03918cb378a2ba25fc6b6b2fecd0752b77d41b7b65c5be11eeed424e2a52a99cfef79f7b105a63d573e1615d0ca3ab03dab0a11a97c5e31bd0c08e068944e8c3047bad12e47992b540a22ee240ea8de994bd3e3924d35ba8358a529211abbd468633dc67fb908d7fe939e7998f73acd84106136f0b7d2c7340b04f198677a3fbed8f0ea8dc5a4caca1cf08698b89c905daaa711bee8873649e4cee4fdc0ab1293a64b5e04374effd29205209fb24cf411b0d6f1ad0b51c0283c230a2704ed3d1f8fb1239349c5f9ef676d0c8498d2a9e05645880b1efddb0bdadaa247e3b8dcc98999c8aa0f666cf76de890481e69c985de28dd555509d9442136caa14f189beb91a37a71411ee4d7a3dd1050a369b1ad08cd0405890a63dabfc29717cf4c035e1b28cdd2ccd3fbcd9d49eb4cb850d673f8da36e55ba3be954968d31cc1441b5319b1654a3e6438c6077fe19defc2c041d9a9645317defe1ad867a4818e98305cb34b4ab05e94c99a55f4ed084ece3a32ee938f762fd96bdc15623798927b588243600b8f312baf02b2c59ade03a0bbcd98f2f71ba61cd5a922719cc29db93217fec9ea820bd999cd48ecb4880353b2d0f981f72daa7e81e8a91e464e2b75ccfb5255104447401063c5769df2defb646cfbc48b652c8d1a2ce9ea7a20cdf11d334b6a8b8ee96d12c39a7e01be18036a7666e3459cd7704f40c0a1847efc5cc7e4e797d9393e2c4bf186bb8451c40804555d985d29c188f4284770845da1521f8573c6f2e01ebd0ef640e3183bde11cf186c786d3760e650d07c0af7c438976f377446344e7d8861b90c188dbe6522dc3e4066224029eab3dd6e5cb4880fbdbbd981c9e4b3289bd1fe9540ab0fa92be9f668c49c8e4540eaa71ca3a13c15872ab5474361168cf7eb1c6740b008ccbeaf8ca1a003e484539a50a8e93f4deb95722dd7e097ac3b23c5a7f5dbcc3ad1851b286d50c19f05e2cb81ec6ba545f5456a03b8c91bd3884ca5414919a4463270f264700ac496731aae2884dc858f96441b2a0830d2a6891127a3847436c9c711420af1160868a48143d5c6c8574467eb61e6aebc9281cda3d60400be2df462bee755581f7ace1376009ff3e0944d6e8c3ab4533040a08521a1ea8f05d63f6464ee397281179bac42088fd2870ddda680bb49feb0f366c3e40bac715e30370072b8bb9abc4997cda8cbce00ec6a8d4e290852fb872e156950592bcc018c63166a25f4e13c7b2b012fd7471819715309173592ded240f4e1fcb3f81c0d7704258cae3847e64ae6755fd4475f5e2125559399959ef9f05a70879cb3feb36ff91ce9b94fb09510e59953397d7eca689e7c2caa068d42525cb4b99d81f530bde6c723fbdc6d5460dbe769ef863abff0883a04444fc103626559785513e74daa99efe444bf8afd19046002b9dfe8459a955ab1d6de82cd170572bb95d48827a4db3986d4ade42899851834d889901351039a64bf15e13f7d3697ac1ea4294a52bcfe659fb46afac3cd90b96d31caad4af927095f249c533901dda18f50207c5a1102412b36398e5c44f00758915732916cffa8289a6956ee0ff6cc040f5034b71ec1b15858d0f2433df36e9ad95486e6eec0586db75d76d4035d61a51d9d9872c5a5ec6ab103f9e95448790368bc9fe0dec075e7685df800f21612d137378baf47c1d0a875d28c43c1808d98a99c62291c6e0369358f867782e8d13cd322331a7459e0eb4808ae5daaa40295f9ce09f3dd7e890b38c887c037bd09c1657018b03e01165528b1ecadfc8d5d8c5cf6b44d5a272a4499a02523dabc1c37fe4bdefe6b3dc8dd00d4bbfad5cff58a31c86a5d8eed537885666cc7d1efaea7b07145b3eca0a1aede8a8285535ec50d5e6393f4fcbbd45061a8b8a97e008a804bab46d8ab9b6a1a713160ad11d4c36e612996cd86debbdc7d985d7a3b564b34ba991d7c66fa40af45c68eda570c4a9763a36498a00a1d20ad8ab69b549751c5de9328bad724a11d01af899a529b4b6523ae2543b0d9b1444243c30fe9a7521e512fd2fed35291438d7928e2d09b13c0a8fb3fdeb93ac14c78803f542a56da406e7fbcba242935a1a11297af8fc4acea70815fbaabd94f288a4aa9b9f3629ee8f1eb5fc8c56983c73c0cf45ea2f21639220363b1bdff0b2a09a11da681e90e149fd74fd4e64257f39e03712c6312e9915cf71097d4277ac8197896446ce1bbc01f3b152c5705c2f36ede3899aad292e73d907e110c7f9d28af43751dd6a713f6746111e4afdf483568b86390e108b0b455852cac457d8dd2eb4fae943ce6a29b0aad0a05e78e51b7e94842fae3d5fb71f3bc14ddad31301b471f3372f0618aa8642025d136e8036495f4aa40c7133bc1e82b26790bf5cc9ab4e338cd23accc0b40c144d29a342f68d337fed661118c42238495644b67afa3048e19630281974655109f14c107099877be7da103c6680f41847487f4f102f8c7a1b506164677dd5126855128855c5c1a3a9ba2ac9b7b19540b537d751d97b6aaf6de20e9395f5ab502d5900b4b9bef97f0ecddb2f90063ae85fad578bd31dfa01a0e9aa55111b88fc70f300358356d39c47c166ec9f00cd6c3dabca188f39c60e9c0fae8e7577a73459349d21cf4a4317398314e618de945f78865b6e080db6661c2982dba9e40cfc8b0e678fe3ece8863f9b76f5c13886224278fefa81e320cd4df62f436af885dcbbb85bb7d05a64bf1f4cbfd3c4ae9815ef6c23ec75551652e31285c1dafd02a9f9318eaf656785e37d8b704d7dc9b8e410ef0cb6eb14401f0d0f9e04757175a7e5900a4d9f60a3800631774f9a4140183ace0111f87daf2afc2c1fb5aefd461828e5c44add667eb423a843d276770ec4cf540939a08a9d1cc10da9aa5579937117acae73f8884547c888b64021dbd879bf69a6b131db28217b44a06345f45b4fb7bd2798594e359e0e6c9ab58417a8b4843f2ed5d9b33efec45aedab8f9c389ded7c83a8db127aa432ffeb4917cd0cb753eca9b556991655a044f3213a0e982d59c918460e8e3f63fdec6e1654c666eeb95fccf16befe3cd35494546fd25cd9307ea92829a215d22260d3b4674fa82ec121c42ec5a26da4bae266b8a8ad0fdc19a7b436ee4377d689db1bd62ba76a130c65b6f30a3da8cfb6cca4881343457f245aa460d9e9d4e507ed37216ffae2c99310ba467c9be26216afa5e72708b07fd6cc52a21b19c3b6c3b57f8afc19ed021a37f02ebc8acfa5520a29f2213718f87e26eb88f6d3075644c368365cc3cd2a1f132ac369da44d806b61d76167a883292b8ab781144fe68881ebe3cae0dd4b1798695ea1edc8af02c85583be0672e078073ccc0c0e40f2188990566e4436ba7ddda43fb3a556254bfae1cebb9412525a52a35ae24fb702c5c3c04f2f779ced70e904940ad74d8a671f3f59787d5cf23e8d3a06ddd418a25549beb327faddcd843bd2e877a9299755e9718c4cb128d79ad41eee003ed147f658ed3609a03722d13205a5a8a808d46b8cbca88528d403b010a3979099e5fc4b2e73e9d75f290f97199c94adc83f49c709618619ae409d54675875b2d6192b1266c9b6cdb498759a7032595a2eb325fc1f6a85895c6493471559285d5627776d97380d24cd0d2278eac4ab146700fc96a062277c4d47c31fb16e12cee4f1397ad1b01fdee3e9c56c1fbe3a3a3433f8fb27346d95a163bc7cb6a01314f53efbfed9386251db06b5ff6ce46468469b3f14932fd96a571d25d1bd6282692488f57878b545aac98fdf649b0fd622bbc2412b29feb91e67f605121dd70b668c304fbb2eff2c401dabff6289fba7dbcc7a717263657a26f348fb8535143122e2b356f0c1a03236e0ad7c3951ec273c90811ee79d49e131052c29838a2b4219020cfbf779e4734e04d5dab73a2d517417a4659a140bae010030fc5099bd38785a47a7d3c94a34560ad46a155f0f993eb783764e15a106ac06f75b95e7bd507a149361a3cb9db8cb376516a94214e5e1f0f08e2dd179ca6ae998be6e74065c0f240d8754d792cf93b60c249a9f9b1a9090af19487c821358d2e61745a7899fcc0386abfc9d49b5d3296900363891971d7f240b3e19e98bc4e4abc9febdfae996c3a5f4bb6eeeca7881e7a7ef196db941da0e63941df5218d0c46455ea32d9b0fd745c8eace3c4270318a967cdb673d2ec2b9467d386765f3bb3e479cc234ffd75b3b0e28f8a8edfcee25ca8ac41beb461b84a395f1390fab48b3957f5453d58f5e0f34ea90ea6a87a07e87e9918f60b8a55f7b26e9a611ef69195a6585aef990177c3ae4174c11c67885e987c9043b9ef3cbcbc40b911a66ab700da64042fca38a5ef822d3d743738d95d5ab37d2fd7fae491ed30f2097e7a58d0a1288635669e569afe98d017245bba24e0c906b85868016fae30e332f5e5ab1fbb9da5940a0d2fbf37c709a5233b0e778b29a39bf1bb5068c3c904a9cb4ef9355f72ea6cecf7cc3298b5773d7fc4129b029440fb97525b0c0c8ca77946f0661302e41dc3980d498f5c4a209fcb2024d72d8b9e67b0751a801da389f3e512186446baa0234a1dea8e7d8a188e52e57031a39de9756b1a358880108aeff696c0816e115c073c9ca5b3f5920438c55fc48652930912d8c48f4b262d41d49b35101ddb001d147a7ebbf53abbf1490e9d454af6faa95d9bd8ee814046d6b172c919bdd434b7cbc7d4966acc95547a446b575c31da3ef3e20c6d754cd3a3c7ce2f6c899b11f4f621c69a036c1bdf8385fd14fe98b11ea6b30004b56e9305edfc10d061ee9d2c60a773acf85374a47e624967adbb92ffac7d66dadde91b41a4f469786a65394754c3613dbc4b4139f3eab7de0e9150461d1aacdc5aae71f1b7e9d15adaec458ae181f7dee38752d8f91a6657a5f24cf8a86f87bc3a36abd4085a5155769367f6df0ea5f635757f91afc4675402c135594bdec4aec163f9ab26634a58cf475066625474af6e6738f409924d966d9e095cd58ad4819469a2cb2fc6258dbba630a9d7ca597ca03775ab2c114204c02eaefb6370fdb55276ed86daf6ca6587befcbcb0cbbb631c2f2d5f07c1929c6e4f81cc20d3e9c3d7277ecab4588ce7712b1b02722052668dd9d7fda67fb19e67f16b28ecec9c9cd7d712c7a8b483eab5bc7b4a32c71de778e5f1c786d1e8d7e91d8fac5d6dd2bcf3e206d5a3a2b7d947d5510246a22283ae3466a3cb095ea2c8e1891680d198866947c6516d5ef2e2cb75261b282637e338a1fb899752dc88a7acf8affe0c586987a3e570c8007bb7c31e89863ad576756470ea23d8593b46c12e5c03cfe1f8f99e0c8378fe64c35c676739369e5851f6ea768a88f53a355b58e5124ef3b4e03387834812c6de99058b589c85c0143ac7689c0e0d744d32d859bad781f00ee5a929576a9282949e8669121a0c5718d1dc17b05760049428feb359fc1a0cc5786d3013712edc991bb512fc4b33756ae06673278894d2eb7371114eba9a710b9151b567d586fc91805521b9e5b6c88da11d8bf082488b049bd5eae46a6b47000fd546a22861faecaee776d6e2aa9b839ce94237b67a44f424ae1581b29824ab12d757b546d9df77a4dffa3b88842a7488efc4941ea834dd96ad7aed4d91b8dd628038e06602c06ffd56438928f27c654500df180f15a5d07d6bb27e7a04d676b34dd92ac5541f2dc7035644222563de2a7b4fec251439c0bca10cd0a9185ea003372c023cd5cfcf769520333d964b66e02077d650a5da27821468c4b59d15c684727d2497f9109cad73f4b03e15f35f8bbd081905e09d9a673b5c045a7c2b61f43087343c32183ad79bc3fa646008ae17c2b1ad804c58cdc3f3206f9d9083c77a66cbafec2a8654c7b24e3ed00ad002b44c055fde4118849d2b1c84a949d58e2fead659347fa66fdb4103425653f8837d37c2c9df451fc57be161c5c3821878d93c462178d178b8e104ff2f92704756284076c0c21e75413e49ab5f64ccd820e22a77b0f38b14db5adfa63b4c3e1a0f23340433cd09a3d01e8cbb310014a45f94d420ae680080beb76598aa9e26cc5e6746ed98866cde4183063c80c1925f0f11ca33742f1be3627c3e4812b1b08bd390ceeea18fc1bb3cb204e11c7df6b82c2381b112db7ffb5ff6ca3c0b6f5c893e8d5090def747dc1eb7a41219b8ca3e4ea1f7fceb2648c628a68be780fa8ec76a5a26c75dab720c80572d97bf2e16bf73481af76229674e0c4d0a592236cb2c61c451c61e12367346150679692875841f00ee377589f22f7d6aab65769ba6bf6c5c0197372aee1bd0510eafddb36a95f1930c6341962f06cf34e786d8a4699c04a38746a0980c8b017a71cfd9341773a382ef7b32f3e93c62f8b128fc72ab03c70a219b0211e60ad3dc2f128e5e5738b7120a36416dc54be5fd8931bd28c671e5b2a1a53885947c98b26b3268b818a4171502e44156617d398994ec18ba6b18ed607c59b030df119d5a711a01c69f9ec3288a711f05cb3b2e9ea45c76e475646787e1fb79a2d9b0e83789e01cd2d2b9fae9fe1ea18d7d21639541c08c00ff0031168a94fb92822527a0e9292c9f93aed1b256538df35a3be10382533c113bdd4668833431a567c7dc367c81fb4ce9fbaa0fa47b0a5cebbf5f0fe181b253913dfb6df1bce060fa741b3aab0ca78cc028466a55d8290af101cd516ab5f33a35c2cfaedec43714138bb93967dfc33add774687c189ac5a365ea356aa5096a91f035d9b1fd446b563c88138447a1f3c803ee81e9eac8d7da3b0d5c9bbadecd13fad75dcad055a10862a91fd4c60dcd0bdd2f8f756c2ec2a423e1ae59039dcc62aade18431125447af59b50f38b4c249243deb1cbcf73520a299eb07a25a13796c568988e56901bfb8f4dc967e7410ea310db6d89e059bc7f37b37e125475e052db88dd0952971fae45c33b7d58875a15d2f3810549600b2f027e1cd95a51f80a56dfa4c02936edb3548c9c3721019ea63578a1b762912893c19c7583c204ee73f45764e2f9859c78ca970e898a3405da4a09e97e4311a1319442aa0c78c220f985edf9a42bb33e0a42921e3e5c176038cce86cd496d4067c2af9c72d8370b8740befb5d58e2cd6f164c289e9fefe4cb918d5f2199f3a3ea05f033a3f5d943cccbce368ec0e2adb42f1086a8303e2dbf2565593320d88307c29dfb89d459ab1cbcaa328a37f7647b1b18aefc74d5b657a4cc73ce100d5e203682fce4134e1158e3d4d31d452c83aae9eef0ecab1bd17d0257157cd24bc1234b833a78a418a36b804ea86adcd50cb2342e3f351c1cb7bcf8a4a730ed13b08b0154d577b5da8e61038e629fe43a3c8f4254cbee923b056da040469476a02154e278dd7f96c175411f5e46368a4d981ff80c8665f167a310207a72112433b59766bf9081861ee4f373403e9020a201ef8211821c8a4d49aa2432d76b15a024038854d9620ddb944b822a031dca72baa244c7d62acbeac6b86f835673936312e94e634881c98c28821a28365e1d68180a4291d8219f3399e993dd0c2e7334d5770c5fa5c91d3d4e2cfcf25ef49c5f4951a1c39f7525e9042789848ca98dedd8dbe1fac51683d5c12b528287bbd4fe1777923f310f0d5cda469902aba7b570f88f1a29e6cd995ad8f0122358795032df7c22e1e57e4af13fed5f7c47d92c9d565accdd6a20a040dad5485475e251d9cdc6fee1c75fb3d7efed89619753ec3b330a27d98c13b61d2c2d5c10e35bcb14cc9c890104458a07933c1c421542d5f0f34b81e958b4ff4d89c4855ab44846bd7f3c65bbaaa8833da18007ba323564a9f25f4f907597317542d6dc4b1bef6f40c0fc76562707623fdfa4408f57108383fe381e7c557cccb807a054daf76f14a284d03e912973847ac5ad6e2b17336b75290dadcb7060982eb081861a0a56e71cc747ca883fb776be2c33f7bf14eae6fc2f6c84eafff77c0a4844abf7000336708bbc9d56201f74637ebb6c64d82747f7720d342a65c7a27719941a8c76f62487fe356f7511d6367b8bc40c5eaf58018e1266038c6704a0f4f05fbc5b2c2a5334083712c29e8c670d9f1fe5b88afedcb298e5b11b2054e72a8413c934170b8d852b4027bc9594a10893f248bf18d0872e12a7341c5bdec5c502122edf1201074019815ff4cc922a44a3339a7441c38a641ff717d3260f27a2556b4ff39e5ffb080ff503a91832c03f57023382840d08b1ad007a3815b384e2991d9853d060950e45b3e085542e91c060ec4467d2b8eb6ce0b463b40b04313a98cab4b35c46074e77dc031d1f0be4903ca3497c515630ce2954cf2bcb6d96c234e8b1f7e4d54b3681263ce023c27c31645056b5cf554c127ccc7a67a13e78ce2c8c4e3b812d02640b5abaee2479cacbc8e49346bab600c57f5787e4c65195096f53f380e3c4cac3c86bcc41c03fb69915f226eaf9285d4e20d54652cbd317afd2fcc9fd2ae951c54d787b23db52fa599a089a35c6ebff455e66f97460fb1eff24fba4e4dcf21d66e5355702e1c5cef443815643a294498b36aaea5c4d74c1f4735e9712c6087a1ba840e2acc90ce53cc50a722bcb7e8ac0a317f5786f1911810e116a890f733fe4b1b1a6fe5288c1ea00b432053a5086b254e24e4123b055c15bddcd9c10e1bef4a17fc5a523e96ca2e5e9aa2cf609cfcddaf2a3dd21da014c9383b763d233c5e918ca53afcfdddfa0b4f64923f005bdbec8915add863820a3526c2608fd78737145281e32f7bc3ba8b3fcbcdadc587fae3d6c3f3d8f342e0706fe1b223cb4a2c11fe0cb708c4530ab8898ca95ac4019a09904bf38e546b00fe3ce8297441da600f72983e82dcd6481a12b4ec1b9555b8d713b7402b1dd21a92fd3d1e5f86e325658196dca06287bbbe3afbfc77cd495e33e016f1edf7c0513bbe525cf5e3aabf2a68f252b23844dee24bef730bd3e9e20d29bf687ff4e55ba1b95eafe032340340dbae86dd3d4c252bde881a9600678c8bbbc6ee3cc03a974d870358a9dbee7d5c8f947404f673d226b462900dae53a3982c5a036da11422ea637990f79ebb59f9e14cec5c0e50496c58b323ece403e2ae8f7d4aa9c4ad1ce38470654c7c9b6e289d546000298d974b4cf53d457aeb4e77d1a19be3c2496a1d3f83113389241d1df62c3aca340eac2c755a644e9299c5a24272a2091ce00c1bf75c80e8a0b783f0ce4b88297b3644daf8fb6c40706572a1e20cb12ec7fc836fcdf51791876e19b555b849f03f8da07953c6aad1daa4389388f7d9561cb8ed8997398aa69851d932f791cf7355e65024d4ebb68d71be2f07b9de234ea467269276e20873b42c3fd184cd10051bca36c6f4d0e135e75223614688c519192b32ebc04c0a628fbd6c392826a4d5cffbec36bf34b4f99f64722a686b7a26507781c13bbca21a0b06971e0ce4e933372b1bfbc64a3971a9edc1441f00bf9a63fe22e88eec90fb4077035ee0b90028ea5be380552f44244419de250dc14677a5ec79bb013d848f2365105a94a8b0bb4423244e86091ec9f11e3becc0781ccbc6824d378f15b001578d6ab5a81ae32f0495b3d34276a92b040c1e26e7162d7a96cd1a3b3b5c108df8f71ff71a71bfd4ab412d4798930ba85ef03f0f51dabcbc72a6a0193cd85b372bf250cafad866591f8e9d5cf364b450a0c728a420ea1dae231bd7dbc7ebe2fb48dcb1f16a51e652649faf17417ff57ddfc1be04117a89d87a92a35c63a10ba56020bd5fd2a4b89945507f6c947450244ab9c31864c3aa16e04a2b6c8d04abba3891e5f4c55d3c567e83d13e01d4afe8c731e9ae1a8dc802fdff75f274f51696573b00ba1c58cf31db41d6af7f2894f1529e800cfda9adc538c7671131809590acca53c909285f43d7503b1c35448dda743554d0c2f838812aedd16ec57070b36bc920523b49df50c309c526e2f2c552674a1146159c48d8dc9895030565384138c607feddddab9fd30cda96cdd557864bbc787b534e58c865a42d47dc8dc4e3555b6166f0c3d8ad9e3e7b75c6158267921be7f936b2326eef45bd36f50a5b4c43673de92ffd438492962a22071c73bbe98702dc14eb6695b7332032b70b879c81b41724e986067daf86401113985c8e81587965bc711e46ea20de702959f4b24a60f2886f3f0422773f545541493cf2a071d11b2ea6d47597a5a30bb0e0d8611ec6e86428af6fb06d5c02ec4411f5eec443391c55659fd2118f99b735a4dd9e6fd8350719e9a06ad4592c0b45e4e87b4f4a8ffeaccf393a36dc906dbdcfc501acb9d877bbfcf661fc8dd305407c9b95259c877123fd1b5d779812c6a3e26ea22b1791f2be18cc1e5f4eedadd548280c9d20556f9e91f2642031b33094c5952ede49a262cf1e909169763a39015024c40039ab0340d2c4b0d887ab54547909f0f84186470d57b765db22856bc1f6f3f54fdfb12597c9924080d6a07fe6bcc8931365858165fcd80b804a6ec07f7de0b3ed30ab6d9ac37edb242afa77f2766082c4470d2475401a7ea0636cb635c5db8988a8dd3457f756304d50b67b883ef85b2a0230d2e396220d289e741e69feb9d2de89c6d3afc64767104588f128aa3001db7d6d89ab5fd74ea15028f731b48985adb0e43bbb9c72c4046a606ba4cdcfc493302d1e12c06761376fbbbb9833f13a167e43a972ec09e9c58f83063c366e8611c380460f49c0bd417bb58576547b6020a1c8c416f181037d16cf78fdea557df686496c4a6b25b70ee151762eb6a68587769dfbc3870c58820585093ff769ff325c1c41105051d1f37c70a7a796a7b6b69734fed5f876bf64ccdcc8731c1dff8ab96941a16518332dd89bbf0038b3c10c09788ec39270baee9f0daa256271c158ac251fdd386220380efbb3f9790754eb8fe06e406936a8680ed48749068f2fd9f62c80e858de56a756451017bc8c6dd503197e96c932b1bece4c08737952f14ee0a1f72ca74fd5bbf8ef239e0c7f5bc5fed4b5c67bac47c8128a975f23ab0740b2556e704429dcc6c86db15952dc442a556f60d4bd5dbbfda072e307b66c3997744923a182b73bdcd9b67833305aabe53bf4e10682e92ad9745083d02eaae8a24daa9f4e0d0d60c7570d2b002c4385c275a1e6eec2c9d339e20e806f71ffaf045d88fca113e40bce4ae8c822193a6da803a6d04961e09dda2074387ef89e884304da32983264fefed83a18d4da39ac3b9c23f8d2e0bae7e89c5907b3e701fe490fa903d8355503c847a9b8ff6b5207053c074a60ee5f6bddb94fcc7450263e2cb48570e6c7d56ae13e14f83746176c133ad4765ca58afb2c0ae7931785bc49f727ef411bb3d14a1116c10d9bbd8ad5a997463e622bfdc96b1a09d24fc4227461c96412715812a08b8b8511792c87d8740ac970796e070e3514a3643b0685bdac05531f4395c38755e7b6f0178e604132bcc73cd84147c1a2fd1dff083450c060b58cbe65247879168a886323ebca3d74d3c9a1b3dd69d6c5cb9a4d0ee2d0ae49a57d8b81d660720a2d9dcdc22a3958f0f29ee1f3547b61bc77924acacec3de0b317024c48b8d9ed33568fbe59f812e4432098e505dacd31aa8a32c562e0cf2829eb1eb2f180fa80199ba6dd05eb63efb8e0d61730b18424d8db3f395dd96cba7f85830e56ec24c4bb39dd5b9931110306259ad161362fbde21b4bde781cc1853c59ce4e2d95849b0852a6c391c7c219763653a12ca701a8dadd01a0861e001748d3e6d72b40da6b78ea70bbd8dc4da93a75a493d85b5aded9fa19430b653bc2d12f4fe89f21d11767f9a228b06381e8e240d62eb9aee33d79249b5592774e70ab93d88845d5a0a102b619940a9285606709bb3e603b5d511648042f3359a52ad24e11ccf0970e1175b6d25156f3ac1053c3e32b2f7fbe27334ac20b561d6d08414ded0d9b33615e8036feebbbb13bb374bf9ed2ea0cf1adcbca023d2f511bf5ee4ac21f687e8b21cb0b20cbeefc74b04861b326deba64e5400490b0bf0ac88bdb42bc410e8da3b4f3419b13f34d5776d675b2e1a0d4a50f0f34219bcb72cc617e62fbb1e33b73544d6820c823335b3754a4cf0560e3513212678a741a46c1e368378189239e948dbeca3c03b0374209892f02d8c3f9dc56b30918994b5d7b3e0decfe2032fbe32f8298bde9d95de3b8b9e67d51b2bff7b8c038c0f04fa5b63a958305c554d63991148ae5fc76ebd7cbd2f157b5b3d1245acb403d3331159e94314fff28fb89d5ab31670fed31d43b0660004b349f606bca974783586a9c1a680491ad9abfaab542c6d326ab4d42428a4edbdbbc9bdb79452caa00aae0a490a28f7b0d4dd5bb0aac474a12d5c6fa60b3d22c6a0df58031106fd66a1a3ca4c01411531d0335c2ab8dba5e9cd0c294eac3611460874f7b4d1255c350a8e32474b3cca524a29f3a669c93c143c85cc9d2f5ca9eff7dcd931efab0d2dcd0d841e1de57394bbb93385d0c7a9a2bd93f63cedbd4e58b5a12ef49ee7551b53a6a5fc791fe1d13d1c67389af93abcbf4ce3d22315cc1a7d27cc2a3d6ad339ccba8f1aa575528b4aba6bce8d1eadab64568f95b7deca3f5a5f794dcc877ba85c25b37ab0fc5039cb6b62a287c4dd309a75ed87bad0b360200a6365ad4766ade7cae0e2af7c6b439a528fb3992f0d67be52a7d794982ff0f45ace7ca14eafd1d3e9b520d776342143a84d8a2a25653569104d9fb252a5703d22d60dc4da81583f10ab08625d2286f41fbda43652485231437a143d09cb25272c9960d9044b27339457b0c031e638c6a88c46dff7893eda36d2e54758be9b43f3f57df48d46fff21c9ae168f4296638121a2519c9233c464eaacffaa8d5de3885cc7024f1f79dbe9feae974fa4e790a99e1e8f5fb88e8d149df3c009ce04ebdafa70d31724650834cb1935300a93f1ff0593d58ce3a2bff609d25b3c846e4abc4af8795b37ab06091593d5a677116f9c7df9ada90b624d69841f487c2b2219422b1999990c983c371ce45b658f419e53da347fff0e99399d1f2f2a7fce111de215dfafb27ef8d5670c598daec90c30c29ed41676e6ef7ff2bf1931919c1825932ab07cb57bec2f295d7c4b0302bb37ab08e8f59972bf2fbbcb3708c6191a34fe63da3695c2c38c6a0e01fbeef525e05b37aa8ac5ce535312dccead162796b8545b2ace0a88410bc20c607ad8263cc87496fe118a3b5708c51c1318685630ca95ee5736b8ea6530ac5a61085d523a20b7ded800834fd0934bd14a2361106a59741e8a8dacc57d5f4f50667be589abef2d42313f6a22a315f5189088e68aaf5ec6a736a44d36aa3e9479fd6335f7beef8ccd7d69cfcccd79e5b76a7da0f3427f3b55d4372d27a343dea8abb23130fab38dca267e8d52d473563b0d0c5962d492fe13da34b29f5abaccad553d5b15dfad65a6bcdfbdef418bdb81404e9f2a278adc78942e594bc6d2bef7bbdcccafb379fb22c7b2ce1a84d78d39cf4ecf4ebde451c3dba7e3544d7a7de75d49330a9b3dc5580956a835f79cd7b74d70c294c10f7a7df67ef2ed6aa8987d3ef0ef944977e9f91325246ca48198965ca3ae4135dca9794593b48f9e491504898446a995e3a09e524d69de1dd575fd3f7bd47b9a49b300b7b58a504bbd1a572a43cc37a9f82776907f0a6fbecdae56f0e74f4bdea5d74c08d4a7280a3eb572a3ffde23da3ef4f988572d24fa66bbaa67b4ddfa1be2c946c3ae9a5df77f13365160f9329d727a8a7bc46cb6f070fd6516ec2f289969f77d3c73aaa54ba572add74d687ad174bc9fb6ad229e95e269d23dd848267f46997be5df0b179c29b74954ce3884bca1f0fe06f0e02bc2907a1fa7d09d3b84899c6a541958a65b592eb28fba03af54ca71e9b29528aa479d701da54805d3ae937d7196e1f54a7f22e427f3c6801482593eb7b75c1f8dcba63068c4cc27b7727917e3c70fafbc5dd5178cf68d4a9e5b26b3063e55da359bfd6de5beb492927bda33944cd5d85a4a34693328b07e9adef20e596d7e8d2e387f2127ecaf74f8fd4f4583a0a0e5bf8250df743a8e41f5d7fcadbe67d5167e1d153706c86547f3f25954c8ff3f4385f5779db53de375295ea27817987df0e97aeb9bbf7d89c1f8f2eef1a7d5138769dbca7c4c890f66af48cebe5250c5b5aae33155d7fca5b65c2aa9f8e9255149329effb9b4cb9f4e3385d4a281590da74ef79f7de8c3efd94e3b312de529b4ec27b46d79cbd62d24db854c25393f046c9342eedddbe9bde872fdede2750b3bf1df7155fdc8204b77ebb74a5716d23b4f7a40509eea67169eff171149b5fb78d60a2e3ce8c8ed7aa56a74b57406af9f8f140e9d664b99941180391c8d2217b7d1629508c5aa5caa89655b85e85c50cf733ebea27933d93d1826a1c2ac2bd163a5dfd0493d1b77fcf322dcb3222e293212dd2b493888aa8888a44930a3b3f459a26d2ac280301811e339c9a46339bf4a8e1b1922602e530bf03cc18b0db2acdec335d7125e1aaadb522c96222c48896a40b4490553483286148880c3f9a977d386a510e85694056742fd0c33045fbae3a39ecd0c3871f1c0081194e306b402d125ca977d5a9475c393fb749932baeb8b95162c9122aa898620a29a4883fbbea4424376ee85d7566cc78c2dd2a1512eef6f48e3f780a77c71f70897df709443b275708e1d241d86f42f6c72601a230dbd3730d4b4dd33491a88b9ad470cddba5331df27b46cb6bf45adef276d36cb729852572727aa838426e0eab075064932b8e6562839ae879fb4361762ee9a1301b346d108f0ddad6c80e8e90f9da556708939e263f499c00cdd79e427a8a88ccd7f779d19177df22237a5a23f3657166b839a130ae676b325f5294a30189723420518e0624cad18044391a90284713e57044001244935f01a49e1a67e4071a9026a462df89724440443a221f5192098b45101205011224e211f5889088764444f4bc48933aeceb0e91084c5d95f21dba52709c2bacc2291c2788e3d42eca3e84163d36e96c8de04a4d67bee2ce7c09d186cc97e8f31a118d4733325ff7289fd78ecc57e9f39a8ff6a3e7536638b9d34647bd2bca84429d640978a058a2077532f19827882a88b505918918c620d622d1063107319cd76989e05e4728800ce95d75e8123a44a998a1d00ca7a0415200e5f0e8e9845e31614eaa8d0a0875889437b409984540689329ca594191238625b47cd14224ee0e592e3d63eab864de754885a93a570051583d4174996f818e819eaf36d0f341146848eb44185394b3440abdaf96b10e6187043d7ae618a17588d6a4284714b4335ff3558368103d29093781a34a1dbc4a486b820ce7352548ad09b18613220ed500c0ef2ede487d6b52d0b31e51e1aa8248a30a1942846786291ca38cdde95d75620cc0ae2d10551d51d509d205223bf3c5fa9162c2a21080a2921134e9c978443085ded5670b63d0bb261102d2bb6a4014e6d2010a9470c203450802932d579d19e2dc3d8574e46bb4053c3ce3c9b55a7b543202241badd112fba07acb320919835ec319a61c93d39c61022e20710288121eadbd0b5d9f6298c14ea4d24774cdfe419ac5a26fd8be8b32463a6a228bd9708c11590c435db416ed9aa196e590baa498d00e01950f3f4cd8d6694d7e8a61063b31f50e91df7c992eda8b2b9c2104300d6e0864fb5462e2d870640e04628da837eca22e9ad4a1301b254eac11436b3446c5a3356d9e0c97ca4f4cc3f9cc98a1f6d8c42f3374cd50fb9d5c0954bd43fdc9842f132695d0244e8c9dbbe390d6b44360beb44b9b6d2f8f9ca2336801888807a210715c0c871d54aa3f0c5daed30c7f00e2871e333b5ef8ef49c5fee6d0f23036d3e38717d73dbd7859e5b6842dff9998c1cef4f8615b9dc30c5b5c9ebd21cc0d1c4eaa9367d1d0c59d23393f427686f800493105119e9d0de5000992a3b213d668ef9a71c3bb3b42bc1a77763222d94ecc986c67a86f79b9d6dbc187192e9819201c407323766386cbc692c0e6e472b976e0ba8e9b9d887bcb8b8a9e5c3bcc970f335c3033403880e646ecc60cd70c98192e97cb4604303652971dcb849c965863c63a918beb3a6eee300383e39a7163fe25c258c2b510c001f4f16387991d7088e190030e3eb11c0b05755d3a63a9cb0b17dc6d339128933f0061bd1f7acc582dd7db3317239a6b107947d471d3292a82134e54e184134e38e104e7012456a88ebf51c4591b2a924e5d69b6e1aa6d38d3d18b611655a48d39b5efc96dfb2c08fb7a297af6f1b0226bb126424e5a33695d8cb6bc1d9152564943459c66b78e74bdd187623aad5097b36a96f370628de94d15441b5ffc5850e3e48a3566c0f4f0e1070bfcf882c41a25d3c927d640325fabc7cb9ff94a79bc4c3261f2474a0184684352017e4b2874fcc489b0f82796d0f172c88449cb132f55106d48224458a0e3e5cd840d415de6ad123afe02d1865442c638831d8a4f40b441b3243ac17563ee00e87819d1c60e32c654d97879b14be2e78d8e72c87e3a349db2faa142f5f303e63de737941c923c435bf24c9daa825c9243e466ce67a56f3404138972493334a30894b7bc02887a9b75ede072752d235232994da68bb421b71063cccf1b3d6fdef28a2e4839d404ab65e4ca25d365680b57d2bc9af70b2e4dd3ba00024dd3344d4819e896820edc0a28e81d6afa42e955ae4f89d682ae8bb18696553a638c519331b6e0665b8c1693c5c4d0982ef3342fc332a65ec3b5661f8f1843841798e146d3344d13ca2c0082279c30022f6ce10c4bd0a0488274821c22594f8d01bdd94076c3031b6c47dc64200c1e38010a88e808610a5a1038214784d8ba4f9a42e1e998c48a1698a08bb1463667cda69453b3973ea3534eb905e982a9f552d66b236cd63cb740e3e7084a3ee0640a3f3c409041568324341d0d892ccbb26c05160615891f114c4dd382d0c48a9d26430f9b0831d1fb2e209db5314e997dd68c6619cd599665d18859934d2a24e5eaa10b2674659206755d8caff83d679d93c658278b2a98b833c638c3205a9519d56a9609a95a2624d32a95a7995629955a568508c9b4d065f4b2cec70afb1e819830d2631cd26268c355b5742ed1060e32c6fc765bb31612bed1a5eb3eb98eb3dde43a2eef2134518843186e39871764d5b66839db16ad5298ab3497c37c6597deac56c6980f6f74b197d102f1610fcb8fc7b543e8ec32cb75668719f6982e93478c311fc61a3ad81ca206e405090de59043fc91999631e97c6081b41c820b871362122d5fe3e34b45d75a230c4ad029a402c31531d65a7318431164a024890baea0424cd57c741dc90c0c216001030d74f609c4e4092f4a066bb76ddb6e96183a818e4c1a8630b14107e6197a2a131e2c21abcd9141c3b6370913242421738621d590187c443b505911cdb4acd4048e1217a56312211374408808aac8e5744c22d4832b849b6c43a209614990ce3e3f996c0731f4240943152d55084318b8508528f59411c35883859e77f900043d05a0a77c78d3dde32e923afb24961975e18e8ec8abcc97ea93ba44179ac30863867a5291d5726c8a7013dfed773b5ff865bacc7b18266cada0de52cb3d7e228e8a28bdb62aad18633ec32c114675852120016d2e8872f2882eb3a582fb19d2e80aba72db9f1c228c5a3f10dc6d6b08408479618cf89a32946136271d11b155ce14615e8c315fdf45527c65af755b293b49e98888add61861ccc3b0e26699969fcccc0d975577c6e6a87bb43ec004901d9b982d47ea427514ca9a20a4e95542d65e88c2264f37c2ac79d4f7d17b7c1f7d7e947f1499b9c7e828d67c69f41f2ccdcecc3a0078d47b38bd948b28fd54ca3782df6b62507a54a7df872c50dc7a233d33a4983583ca31ea623a6bc6741602b4c7a0ceea41a2324bcb2c05988e7a0f282fe5228af4301d7594bc7a0fa8a31c251761ca45b6ccd261fb3cf77954661d20f5d5595aee21f515ab07d559579df51803669676f03531a78c02f38f22de7fb0ec59d94fa8ccaa67694779b9878965b3047a947e8f801803fef41ec09f72fee1a22ef4a50cd4c3a908943c73326556cd1240408c291df51e4a47e5fb2250f28fe71e6e06a22666471fd4b74b65fb7cd41425e531c6948bb0749841709f2ffd84ef5118e526ee1e16a58e8018937a0fadab1e3f96762dcfacee432b1799675df59a9854ebaafc43f5562eb23df59a1895eaa71701be08551ee2f4d45ba7918116455de5e04fdfd2063cea0a385d25b36650a8cb1893e932c5d25698a565d64cea51b3ae927facceca45b8a7e653f847911ea9aff2eaf745e414b1ca32e73e75d6696480b52377a80b3d0d9b8bf294977e3fd6014a47b902ee53326b06e52c04c41814560f32867419437a6caeb08c516119a3616964863c33e43992f5f44c9b119e3cd36687080f4fcc24e283c427cb49eacfcfce8ea5dd41d4e69d4cdbebbb8cc36f9961f68b49f852173ba70d75c146ee9e3e538af9da936706ed699364be3692f9d27c7ee6ab9edaccbc270e4b02492926909ce23368f250589c757bf66e66d56aaf557bd56e758561c5ad7ad78f87f62ddb196612a5869ba728ba2e723252bba0707564a1b7c7628d987dfbb615416fdb5d3c5793c741149498e0054aa2b022667bfc666ee6b35117ee568d8edc1dea6d7be89a2fd145f6f122014497edd166980863fb86d11bd7d222da04bd869bc0b13174cb700ed485baec8e7a30acb853d348b71bd365bb8c485a2bb8e145d7aeb2df1e3faea31cb53a7ed6ce70b3dbb7db5b9b374d186e505cfb6db5c7d276f3605871331db75f1bde5881e1da0c33c3ed5f08666eb224d58be55c397461528855a31015f72e9cb9597a63bee86dc6d932101656b8da3c0ec1ddd24726caa26bd7524c20ed12775b3d69de524745d5811825121a4851bd9a5d4a29a5d462ac5ee6a9722e0b2cdcebadd230d4855ec57387c8fc87a14b661655b8a2580f341cebc18e7624f395d97c04109c416f79ab8e645ab63da6014161dde976859c17af0a86745da3dcf359ebe5e82273943d4d709738cb26d385498a09ee691888a0776452bd252efd86e51433a417e18e7ab1a094100124e152fa23d227cb9ecf0ad7011098af5fca79d901b3cb71baf468203c70e7c3a0efad91de6a43e242175bba986d67590b7343a80225a902289caf0b540513edf3850fe81d97e87aab6da1eba53e8937a64b85717267cd3e5fb3d7f9f99acd2c7b3d27fa107afe2df335b36a86f599add5060a95eade189ea68b4be4444e93a647a9cf267d49085d9f692831a4476573ce7c230cfa08505a4f831024489021e044a10a258ec00132ac2071b72bdb7b2a98152656986829a1a584182874f1b1a5b5843c809c4e24d04edd7234cdbeef8ba3efebbe6ff47dd4c5388aa3388aa3910fa9a5e7718fcd1396babbf7799ed779ffba4ccfae93ba9bdedc11b5cc02138fe5313e70a5ca61ad4fa59589e8afe33c0e732132197da3d368341ae5694344c49100e93e6c6cd680862b0309f7fb0b2e5c16ee7387c25876a6102714c6922791397764f8e06eadc783b1e400992f9d1deee3885c16967bf9f47a4d0a59efe3f28a07f79a0c4fe82fdfcfab6424e172dfd566c89371840bdee0ee6a0364f475124f2173880c1edc967ac251c7f8c08d12d727b9fbde7f08a3b8dc4faf36347a1fbd68c2c3342d5a9e3e77bee32460cde707587b22c6a02761ed488441b51e1947ee3c85694141228bf2d8ec3a2700f92e83b4204d2f6308b7d69bea12831bc40352bf8eeff686f3c9a4ba579bbddde89a335f52ab5e36b019432b104d2711ed83884d7f973a4618f7376f0a50fde56dfa5780efa3a1f9ba99afefa44f14d67ab4a10a50ed08fd64525785e09a2e35ea5d17f3befe24cc0d15d70c55f8727acb55a978f7f459ed7da31178d463b36657fd5dca572f4689d834782f89a0e025fe1e3ff9819fe0e7a743045f750ae78a33437a13ae3755460fee9e3b2e98dc4dd3a2ef4b2f7dcf1db0ea6b844bfad67a6e2dae7d32f34984215fc39282bbe7936ab372f97a53e5e713d4593e1e1364c1d448d7cdb0eafb15def7a9cb0f9575dc8394c7c7d4b42786ebe5000000086ef7fa31267533eb00381e7b0f39ae7311fa391ecb4514e9a19fe345c48ee331a606d963034f0f8f919ed7aed12efe02fc8d1dc77b88e51a7ad079f6e4cf68172e729ce18b6cc365dc701b601cb4010000c0317e830c182f311e3f19178e811f3f19182fee22efaa5dbceba153bfefb42fce50b31cf5ee395e1373c1cc3a008ee7780f1ccf917f1401ffe3f6c0f11ccf917fcc9e5c1303e67d7d4130b5fa05ef7d6ca2f07ed137a3c2aff006c0e3c7830d076b780abb5cfadccce23175ca593c465de097db80e510b9b3ad0eff02bfc0c02fd848cf0ce29e6a1004c120eea5eea4b78dd020b5a138d486de4c406a30752f792e963df75bf218c9b2678672025483990819040787e2f4e0ca203654f6641becf404cdd79641b444f2e2c5bdbcfca989a9899136d4e002b5ba8bc2a4cfbdf449ddafee512e6cc8353cd2ec22dbccddfb7b1c38034d34cb490fd29dc8b3f32575f613478f3103328d32a1e803e352b17c7b7fa1be4fa3b7c0516bd28694dac6d7ca47ef364f7bf1fb22f52e7e5267be5232f66df5f78da1301cdfa1067f0acb71f03138521716d73886237569f149fdc5bb94fc0b2c7352974f3d3673e048a50f6d61f14e7e291cf52abeb8bc043261f3052c761cdf56a73c46588e14954e790cd31c1807ee3a4f7e3a6696385907575efef824992fb9c1cbd5ea5dfc529e3a885779bf6c981efabefb645cce82b74ba679d12c0f0fe2dd4387e7be1d3395f78c4e1dc41bfcea1777abbb9af1e49e82342e0da29e8277ea3ff7c98039ced045de56a3f22e3dcc3cb0749d67e74bbe1be1fb1678c57a9536c0b3be7dcc21e05509dd200d3e4630fbc974eaf63bfa6c9817df2e17dfe1caf735c802a24030f5fbecd381d3a9acc918f7f5db01be45de52b7b897a3576923ead1bd266d447d6f87ccd7e8066916964940242b5db87bf6f0d01c7be543bd6d7786bac86f382e735c9ef2fc32e5f239e47160e9435d5e64177925b364e93de58f9fcc0adee035f87bcf4ae17d9dfa0a6fa957ef40bc236a10af1e3f03b0ce81df113598673cb9acbcefff1a7dcf721466adb2219a856b58b00f4da7644dc6f8102dbfd110bd82a50f964f3243f91969bfca3b4ce57d5606f3cddbea15dce2c7a34b1f0aa3ac15c7b558165488a2f2b67695f7bd5b254f7d6cf649e5d659795b0be67d75ebfbaaba3074b95adf302acfb87e32ac633c8f7c3b52cef215bc6bf44a4bb3acf2aed1ab83785f83acbca566fd9786a1eb3e5ed655deb1f0f4c1b3070797756486d2874a8e4bfd79db548a5b39885b4fe1ed23d32c3ca49c25df8ba35ec1296fe17dad52b9042f3f7de62b46fd4e05b3de494f85c5e397957d68f7b70252df4c93f22d75ca5578b8f4ea60ded247da0c71b2a3c10bde1c21219fa11f26404e34784110fc8ee0370c0b6fd4538f1f0f99a357acbc81d0ac8378bb34f88bf74110bc8f5116f69169162b250be1d2abbc67342af58b55aad4e3e91195695cfaf44f86e65098175deee5bd776fdea54cf3a24947c93cdc4c22e1a8e956f9f8ec0c604a1034d1494969b59b96554a27a53347461f4dbd69b32dcbb250c7586b16c30da67e32f14b60e20c6fe838916434a3b9d67a15fb9249bb5197add2cd87c4057b6b085d30330870c1d80c8380197bc31aec4d7265b541304d1f83a4cd4deb8787bba136524a23a5477411565d79b7e8998d10933786dea9c49e418a2cc48082115d5f6b199c98a109339441d7672ead033838c117862021899e2a888081347c810a66b809038ece10860072416751e80c064e74a426b327c86c739be1ec2c6745d6e63d71f8f9968e1385d5d62da35678cb9c487432f3b185270b4fff1bf3154f1fd68a5be4f78ba62e40f252cb24b2546bedb4ce098e931ceb834854451c57ad5725d775a2ee15e54d986e65edbe75dfba0ee528ef5070ad175dab55d41de507944e7a9ee72181132541c5894c2825530945b4719c674772249de4889ce49fbb5511a5643afda084f6aa3e9a26287ef241bd984e27ef74328519e5a9984c97524a29a577530666f40411367ba80e127a680c7a680b7a1061f26742f163842568bba14820f9237f64920d054802c91ff9237f807c482d530f3efbe9643aa1502693c96432994ce0515906fe844fa719339c59169b20aeba931eca83c00c29159ae9a182e791e9d2a3e46eaa33031481a80e153ff494baa378c7994a94b0d4e012a80e854d1dea426feda53a54676849d0143487e2cc70882ed97a50a8959fac3eedab4fa6bce5c1afe0185d4c6fe118554c288a02df81f87bfc3cefdd77c1eda7479a32d5993a5487ead02054c7c87c39892e518f9cd84497a869eadb82df1765fae99d09d31daa43754ca74152795b304754de2a2de9ce7c99323565ba93a90ea529df5693489f38f3c53abdfcb9aa873fa64b6c329d4e4741994e28479d50defd84c2cab1199a1e9b2816309d5ee2984c4f894e56a654361dcc1b954d3f491c5346b99438f69784f75442d38ff0f6e83faf53511961862f137663862a9fd55c9dc174797a2c5de2929452966e32992e4d2553b6a57cea58a5d2a594524ad9bd843aca095d325f92c2a20fa5d435831b757c76d4bb9bb2c4bb9a2ca04fd57af7a43aa150264cc4a651a6c74f9a72407da28e7a099770133646609e1ed8c4942e4b37954a97ba54ba7c290b313f1de44d9986ea8dfa046ab4e9258c797a60a3e3c7032a9bb2109c96a72ed3ba8ed52541f9fdf6edd4a328586a104805b56da753ebac6fabf218b74a9bf29647bd8555708c2e2c5cca5bfe74c2db3f19d43b14eebc2f87a8bb771f09b5c9922c459f39c4847e6418a22e517fabd128459502512753c974f912962559f229994cbfa79fcce93ba66b3afd74dc7337612122900ee2a6e4d8c55bca952a6f9bda2a54de0f4d32df97280a8efa6e1fe1cff391e9ed9c3ddd22dcadb255ace4d6bcad6aa29ef006dc8884224ef44ac72398014813410a6d6f458f40a54c8a264a8fcd1d1a5d1fa5a089425af439b485089b38d16528d650d174b260e89641531c307820c2a4cef72481a629f88e4e901952d54ecd724747ea5cd70caed440d06abd7b526d95fa18ad98bb868960f5607a13a76f5ce5b07cfcaad444d73e1de493274f9e70b8ea13b6c946986e7a67b22922847cbd114584903ad4cb1c048dcc42d46f99866ad11d806344a5c78e5219847384bbe790c8e62477fbc8ea4707957087e8adb599f684446648a708a1b8532868863df3e51da13e2938dd25674da27cc2265cc231ba6c79edf693e93e7ba9f1b8bd28d38145eae08c1e9b232673e889fb2adc3d876ea41077200313a24bd428585e7e2e2961157591b9f42df36442edd01d9adb521739850fdbaa3ea98342736c863950180ed3075a3fca41416197f7b91c14459d150887c24a0f5d30580837cb7b0ec1cca12115ada51cd20441a00981090d54269068c1151b0d133b5dc0c2133d3a68424332841b021337f841fc028dd48a2be81566846d769b73e69d5dcbac2ac67735467a2db764af59a5f1f392c9a6cda0a7270c3d3d3d3d3d49c0d1b21ab716d52acaac4d7bb769ddccb20df1456411c31923baccc718f32c303d75b63d36ed76b91f1f3d7e3277341a4d3cfa25652323f91d2140749498574c2755b89175f549e5a3126356cd1f0aa3534497782a8096e8f8f9a3256da2e32792f9ca320d9a619c4ae079933343392d3ee53a5bb26f5038821315bcd010895bd2c4008624f474d529a64b10159c0469a9679e3034b8f80af17b0ed15730a1b0ba2397ccd7bcac416628a7132de58f8e653c77b2b0a0b22a251e30c2a3c4031990a1b36f314802063a9b223b4326850c67093acb70aed059078aa033559665d911c4e08328b4e88343025deda3a86208a719051b9470800aedf20125480ca938b00da159544d6a2fb3aaa94e5ae57470a710264c6ce68b35e3c3aa0bf7ba4908c270b2e076aaa6549943e1cc3b0a01d52a536515eaec7c49e98289dac36dcb3bcb4851bb8db0792d6f5d8d68d9b7565a552a6baf5d6a3eb22ce480d614b73eaaa86879d904152b6aa522dbb56c70a9ddaaea12a1cee738435ae5a659ea92595c29d5e8f4b137945cfa184b79e2d2571f9ace68165ba816bacece17959e66338b6326a7d452ea99c919f442ba982d626cd19b524aa96591650d6fb72ca215aed5a978ac2f65b422a96e2ac6186946a9e97432a14a204aeaaa48ab51cac7f254ba16b72262b1dfb0963316b585b6982ee40b17a72b5851e59e6951c52dda8ace25214aa26294b658034364d1257bcc2680aaba6b4175e7e245c5f2059d3286e128bbf6849db09bed1a2079226b5fe88ebad05d6dd1729166358d85eeb6ac3b8b7527baee3816dd752bbaf35abafb547437225d9492e9b4610d0c112d80fa78584dc1544977aad5d55d0acbb32a9fee5add0a0ba7bb8b7487adee328bae25d35d0b4b75f7c2250c4734c708a35ed66453bc085b842dfada98f17564e156ba96a7f2b14629a4d555a1a44aa00965ed7c9de27c99c0520a457557a494519c2f8fabf15a465dc418bad808633ed688cfa690ba236559969dc25c97c26274c0051c10da4015a4f08498ac083ad3c117862459f244154ec4643f4a6ea84045a184bbb31d3ddf52c2ddaafa1361d4fa136bace879fa196b4c4d35c784b336365f328bb3db9a45d1e767651c71b73c95a24cf3a2b363215e7410f4128b5c226b39eedb6645798bce3d362ddef27ed1f219d621faeea1ad102f7af7d041886e5fb4e8f7ca2be5e595a181bba30ff7d97215d5e95a2b3abd04ea8bd64e3524d45a2ba694525a69ed6a27e23c6fce395fa3299df1e4f3bc53cfeaae761c3ecdb0562c825aebeb8b6bb69c46336ba2748e7b57c291bac48f873843940ec8683442c1bb6a94df4b7afc64f78b6bb4244dbc67e8ee31ca75254e8477d59604a4990de9624ba7da26de2ff3a6c8943e127b323270630ce9f6db7624dc75d2a7ebdec9fa1a3d31f6f0b65ace78c27da34c7f79015e9ca1a7ade844c231467e3ab65bb9695eb496af92c1dd61cba6a92d276f6647001909d236e8a804075168baa3dd256d88ae69a2efaab78f88e446ce9b06ebeda21cb77cc3b763bb2b461a81b48667cc9967282c6e3493d99be18a16ee0c9df1aaeb5d631891e83a43fe066943ea7a4da1e8e45523b89bdacc781a986437e16dfad5947ee061aff07ac109f52e26423ed1241074b95cb0d761e7e22743e3b159430035cec54f06e6b1d9ad1899a14cada456a8d4bd20085ebcad0653e0bd57fa701cc7711c7765dcdc1d817c64dcdc2d7d3477f999f9ba0765b8d541bc3a0a53cde58a936b37777884681a466e348dd300d2699eccecb6515b636c6ef2d3913a8dc7288cc6258dc7e6e452a9542a954a499e498586f9963a75449323906898c3fc0829524f3987edbdfb6eb55aad56abab7d9cfd643af9ada4cfaa5a9b7d3a52efacbdf4d3913a4c8da7602e35cc53f0aed130d73e1d52c37cfb74b8f1011c85f75530d7e5135a7b1f61d447287e3b508e1f9b99a77df7306cc3868d6bdf3de9dab88da75ed8d1cdbb76351f79f3c5a15071be502b142aef1a99558c4d993b91770f8220088259eed44f060473769cc87162078912aac0d171093b377acb1c1634197d3a467ff10ebff8bd0b2a8c08d1a37752dce8d1bb0c00516283448f5e83129b2646f4e83084e89177b19da18bfcfc1a5e830a8cabcc9717f9f2f2f2f252447c797979f9510323b3c0951aee8289d021b50d7fa92136b5eec5bbf8edf86dde2fdad65af3ae3615c7e7de1b10d3af5ebff8f8b9e8583d3be951b79e92e32d381ccff1d8ec3a19f39ac380917be4784cfe117b8effd0c7f19a98f8b53c0766c138011e637060168c981802e42262cff19898965fcbe3d7227399e3525cab95e30c53f2b6dbd3f6da57386a174c848c0d7fc93af0af76f114de57e7b3c04448dde5d39ce30cbd5c430ddd47effeb9c85bbe902e729c6198bb987918bd7b0b2642ea01e46dc371b5a1b01a037e325cde3f340782e0ca39ec63ead1519fcce89dfc88909ad4a285c5ea16af8076f4e897c7468f6e933cc9d151c90d80b44a472537e8d1499ef8e8d1ef3bcdb30147dd227d361500b46deb34c6f118ce7116586a1296ba05961a63fb162cb50d586a2a44e5673a2af47d715cdf071c59df871c43e8d463efe2e79af1e47e06c417d3b8748a3b88531c88bf5f7cbaf401802a8bf6332fb28b94b972faf130facd2c1dec511ef5ead546d5caa8bc02f3ca47b78f4d8d6b622c8e9a058e2d70d42d38ea18ae8941c1bb468fa4d05147fd7d29af174bedcdd7aaf5aac177ad56eb2d14eaf16bb5f2aed1a82ca37c32f771957a07e20d7e7e05e85e81a98300bff21d2b0753e7704cc12cf045ec2e707c849bc09159e0713cc757f0b639959b886516987580398de7c8374560afe3c837456a9c038f292280cf4fe666160f98d338cc69bc033d2aa0d031c91321dab3826996c1bdafd1570798dff88d1cc4ca611e593ad0f8fdcd41ac9cc6a3ae91f34e798dc3c2bc5d1c76f96e05bff80f9dba5dd101b6de516f05db7b100461a030b802ef2a5b1d20f7ee377ff4d88c510ea73c855dbcc05b6a22288dafc0d4f808ef1f1ab642c305f631358da7601f53c39c08f904bc75fddedb38875d07b18defab5d366e6ddcce97eba28f87ed49b633743d7e3a6c64e9534300303c2e1896485e34b04cb281bbe54e077ade7c814fa59ee252a9bc6b34f74e469bf271f776be6af4bd277d321c2777be94c76fe53cf375b3367b5aa36ef6485db22cb3a71bcd60b97da9c4dd960e82208759403c010982602aef7bfb2d7d58306ac5826b3487a35ec568c94aa158436a397b5400c8f2a24f06cc1b3c98bb8b5b49eefd59dcdb098b315707c026c6900f40d42a291cdf2ad5e384e9ab3e43613ae71c19474e459dc255efe220c0a79e4a5d45e59de6a9a8c408630740effb14c7711c77f1c63f3478ae9b1e6bc7bd8d16d360bdca37c818a9c7a814923e2a2c75d749cfb26ebfc2bb46af568f4d2e5fbcb1be07f1668006cfc21d8727103f8efb8ea839207a58200bd768f05a955b9c7ab7b27a27bd152c35c86265560e51b3be236a56667d5f106fcc007dc14b83352adf206304405b954c6d52b96706c51ab2cbdb6e1a572b6f95765d1967f122df701739c6e5c7438c9b4c38ea19784b9dc5788ccb8871518cbc695e7496675cfb0d8fd11932f00d78ab740054c7fb94695e74dd3db4466f34ac0272e5bb4a52e153a1ef49077fcf7a17bbcc8bdf8efc56eb5dcce97684fce81b6ec33510a46d38d855251ae8d14a34c0a3e525120974df22ef1adde2f7663b6486a3679f4ca355a8742c3775514c1dab11081000002315002028140e868342c160280bb31cdd7d14800f84a04c6e541889a320876198428610620c30000010000110a899d90000eabf0d84608d5ac7964f775033ba8f6c277d1dcdecdf172299baa2042d9c30339ae6b09060c04581df86fb93bb50f438a49f81cc7caa8dc7ede65e4e82d856e0add177ca0423226adad00edd23c9fa5f248843d893b6b2f0a010835df8d94f0d053d06eff1c42d56eec92e1d68279f27928652a62a378653bc850defe6979b8313adf53c4442ae6aa04be0b6445b7edffcad9924f362f44d52ab8687feaa6525009d4b7638b214b05dfe047e9b1b52bd14c8e4dbb044ae9e599e1576047cb0f82746e10b10fe169ccd54254f7718a2ef36d9b615ea8ac2fc73658cbfbccabe2065cdd5a3531ccc5decb56883c9bd0472619980320663848de8cc061560c62f40ec8f3cba6fa3654256ed8ae6ebb2ffca3db30f82cea5aef07e9f3ab1cabf7abf94a60662f1061a162b0141e746494b12a8c5d824053acb5b3d75c3eb37d98e9517c2542cb01950552140e637299c68a9e0bf09cf8870caa114973fe645201545801647f6e3163c76a8c1d480097c261d814506ffae6aa12adec5bd29003022250e0068640b6a628560c3c255e58e6894f799aa86dfe1460cf72c3216ef82e85d6439ba943c2b48f92a76d4b0436a7f062e3498a6e958314d6c80d6dea3b4bc7f70c1b373f9288763970081eed6f4235da91da8bb75f67f923ac9f2d183cd35f695047a3d26a8bd26f64b4880873635e27b684b9db80d4318b71fabbeb8244aebe7d6c90aa5699a934cbc3e4c6c45c9c9050933c7aa0f3061f5ec4c555f1b63a8a42c48118b0ac54405a4013d5032e03a4beb26e5a49064336f7d88e4d6c4f42ddafb23d58a9a47eaa0ee938c75ace53cf9c9a678a1dc004b9d73d912f9cd6b74ebec01b93add9ea057041835b09ff55c3801f26f51722ddb8d7235276fc3676beacaa18fa03cc94b758d03b59e67f2ccb2279733131efbba1433cdaf0abae6f86c0b7c386039c4f3aa1d5d9de99fec1619d519593a656b59c609ed65eb1abf10e51e28f39b3f0f434f5f77f1c4b2f9a467c75e2f73e844f109285995712891b1e40f338836bcd88c01ea43471cee6e1940d3a7c46ff754289606c17dfa9b31a6cdfee2eeb0710c0c2f79f101f3feacaa37b605cf339f5f224914ba0f12de6463c9cf30990ac5fe616e5ebfca919908a86d6120820253b2387d8a628ac0e2cd64183ce9b74a57ff244edefbdf62891334896a19cb3fefff177f46780be06a788d3164a362e255d75a1bf1f09b9ed27cb840d83956784b190365fdb8728e392d044e38d3b760c7b706960f1647d5247470a5bf33f9dd5ffd144c4f374fb970f2b7e136e72be28d2321875491bcca6c41290e68f74b4d182451afa09f7dc155b901ae568d67e1e5c0be5b7c10171a1825ff31a358ce20a17dac13f312d48a13dae162f7c3dbab7152e3c27c911a08343adf64410d324dd81948af66e867bb107c30e084339a231cd4cc3a38f5286558ae503ed0ea4e4c3ab51799636eb6a9d093710f92994477158246e7e034e6ae1accd55ce3cd1783d243fdc63e17a3aefae93cf669ef3df4b2782f070c8e45a17a1ba81f2c71b5e4f6d2c9aeb10002d689c1e01916e01cc1c93412798ed68b0ec9cfe506655bbe1fc85a5a710971a011dbee2d3a44c3f12095f33906a62fea8e4d8739425e106778d4315d387169233154dccc92d64a18d476937b39f1b3301e40d25b4cc4405e347054f1508955fb9780c92873c2f104b3ffc1741766c6b4ca28df042abe8421a0f9836a50118797a6535281e5165c4681c5145cdc02c32db89cc28b2b588c85c35bb83c058b53b818afc0efd5d64b508a976ab9706baddee362df93520a04641a6fe605f96cf9f646dd83533818dd3f128530451b637a4504da8fcf69bc935138d98583af705805265778f28507abd0f00a4ca6e0640b0f56c1b0154e5ec164170cae82e12b9cac82c92f18ac2e28bff68caefcdd0cc08f67eac24690ef9971234385c0ce8822890ad94f92dab4263b5184922a4531919567e279dac37df0ec4a842a1ff885065c49d00fb9586ab1ea4c6e7aaff94d0bd0eb42e145735ad486cc04027794fcc14baf76604f84a00923570ce98a96c87699736c63fda28eccf42e7b5e6cf67a0ef678c0246ac88b8adcb6f499d8d02eb6718ded95803a6b2a252f6b03aa8d8f3cd597d5593c075ae7cd7e5daae35515f6e469dfea83bb6dd4224a9dcd4b97e907b4c3f7e8fd5881624703d9d111ccde91ca9b4079c93d59efb020d672081d58afbfaa99d33ae57dd7a7cc25262de657066759d637c350415c52c76d5f2d749b02f9639642ef4c0b3af3784c99d966e41e570f1fd852ea2af29309b279d445b65fcac6ab09770ac8601b63949be2a0ac009be718135ccef19a7ec643c08f505b8e37f79d7945449f8721244e0fc382374fe0f68c88c2bc70ffc29686f5a7374dc8206056104aade846cd89d45e4caab8b62358f2c9919291c3f3600e00dcbdaf4cb56ed6e1b6c817c77171127e59983d31e77c952ef4e51a18536fc7e01a7afc7c3c0154dee18a011e5d4036ca4148e5ffd62884823976dfd8ef62b7f9f8e0e76de032956a99e55a040e017b5ec6b0f8cb158be7fce8b3b78c4bc234232895f5096078af868fc3522e306d6ba2a0c1183d5d5368354de2723766c25397db5b6759cb2e9fb074a5db11245b43d4efa3a8eefc192d2a9e71ddbe00cb594a918c0b834a8af903c377009d020105ca61a72964433ad99405960e19d001d63eed0b81c8dd3ccdf903d93e0e5e0ed5c0271ca527be5c59c2b860eaaeae2a9192941d467d96a700da808d8a3e2319504881ef548d8772e3509460a0477370670b9ed353df6d5f64a194045dfe3947528564c124d477018950226b788ea125fa573cc630a249839a4c40863cbdba48306f1e4adf7d1f63789bc71732f257fea37236da1992f026d2dafdc86595f4ca883826e1d3b8bcbbcfe894945e7108fb028cb02cfd73c5b8306cd62034ef25ae9dbda19da7b9fb8cfe6b25fad0388e99a89b1d3ddd459207cdd09ad4d1f8e00b85516da2c31028a46a695ffee1ca379b7ad3d2e92f4a5a0bc89edf1fdacfa1144d046b403cd2f915a553df420e41eb653d99516f92d7a2dcea6c2bdd84f16e2f9cd27f7706e05cf49442dba291671cf4fa66384c8dfeb452505bd2ff76a931277588802e6b4ef4548d638169ad917605132d6eb25ef6eea538aab61d63b12139ff020c2aae17914118c6ec6e54f7b77df69d58032b66f284ac64f161fcf7b808ad0b5ee5898010f8e309475e362a13bf52381c69664e9450dd66d7dc1b525a011b10b889f20f8d57f5324cd0f0b3b969a103696197f5c69a191c819761f9fd7415c42e5ec8adf7dffe9631946cb2c4554b2b2a8503ea54c425347184acd2198c9abb663bdde78c3dd31c2469f09fb6b360cc8b71862612daec322861719865a4f506d424fe51786a82d0711649993962f9d664b462084816068b0e3e0c1d02b1ed8900b001ef8301cc21cf7d2449d764d03d311c039e52404e97a4f0c42475aaa095a9e4d47e1a394b71abb1344d04bb467a9642ee45aedfecf027be36a611f9f1070e96659ab704c0b036e45d00390d15cd1a98ef43b25bbc260378bc3bfd666c27bc7b0aad2ce391c2b8214180925593e669889e486da8009c58ab5d677f920f791a56585424d8c835f2792b78e4f53055994769020ea1b132a3ed99c5924400daedb9df97c7957accf31641200d040e035615a2ef812b4817440e01fde026babdcf2e19be236ccb2d44b14968b5ee0e464f80ef2329b27f4e132615bfe48b3af6d5c0fcec3d647426b65cca180302662c9481cce19e4ebd9c3c5b3ba2b4bc1697bc38ba4248f1f0b81d47fa62122beeb3ec962e98b4266cf8cc0e7d678d2be296567bb7a2af1536383fd89b0314058a6d7b8eaa6cefc87dbfce5245639653ec5db264eb5e3534f7c1e3b9092677943b8f4b19001a7d0cdda68518824fcedd85197b677483fd68c898ca5df5d051948ddf83b258a12bc9ea803c1c8dba2d8c4f40d9c4319d9fb2ba03f1584c40e43e39d218f1adb9c2897cd16ac96202b50e4fa97c18c06175bb680b2a9182cc0e0f363ff4aa73fe883bf2df8c2c1a3ab50f44a56edbbdeb8ea1d300d911f38e55bfe2b35ec842a1a3292e13a1d60d0280ef760ba19078dead2fa9e700a57c556046fc7153e516365a85a6453504767723f06891f2cda940ba4d11ba97795a746f946ec4b2ea265032abb866dfef9db8206d63f4897ddf0a2e43111c0860d2cb6d74ed86b1400b80582049f2d10f19188f4746b278a8089e365f2fff068ceaf019ba006a4c69950fb8e24607f2a843f0b3cd3ae121631bbaa355edc1080d8671f3dd9ba8822d4ae3412ceddba504d87e000d505660513b07f03a00a2ac469bbb38e4191b3b83e0f9b0320bde701d1ce05da875e9e70a0dbd4585087145906a05f68018a2e84375755c9d98f612b2ee18792ac2529034b9a055c025c819e490ca0a989afb0caf6d1f9c18573996dcaf50d620a8d7bc3d7c20e639faa6a826e16ae312ab05e407b9bdffabbc5eb0b70d5e6f4b56c2ba27775a7090db623d674c9843dda9c6d9c1ca8d77a02392be373be9c5c1fc73b0fea84f4a65e90da3d852e09447ad1e44dded3bd38fe1434fb8e9158776ed14ab6d7dfb1f18c5bd6564f78453a6ab2cf2aa3eec2d946cb79937ee7b757fb39ff172f578590d9d9da5fa8ad09acd4195688d78be9465b156cab88b24800d59b2bf0b95031b525c5638be1e723c624d37b5b8d6d3c628c0289d41cfab5cf01e06c4e0b44e3296e7c2ca9f02596a4b38c50bc78610eb607c431d2343191c4bd88ca65985fa007c09872a6744936d3620c430f583473139be0929303af06b937bf1977b556c81a3ae3187227606f2b0811ee4eb74bd23c4dfea329434f9f909630eaa11c3cbfe65b9c41f39d9becf9924284face44e392577bb40ba7c3d13a8dfa1ef665ba663e26a48783e388851003445f2330ecdc5039ca6f909d286ee03c2fe5d923bec3a3c31294ff2ba4bb1103a00bfa3001a49fe4e42b26de928d76c86bf7ba8332039da7bdde103cb7331740c51e3ed5be1c0426779f68367afdf2986222bc1d3123cae8680fb78dc88bd4fd2ad3bd923c4267b4d0a516315ab7ab661586c46684751b9a36fe8b0efe7ad92d75b9018cac0a829fc7bf18838410d3852c20161ac83f5a70eb1eb4f61ff22564d5eb28b3c28b3a1ad79b0705cc5118516e15ce045070a871df102ad456cbc70bb6283f406d2b0ade04d0ef62bca8db5fc32ae9d2c5e4de6af257e6698b77212cee17704f947109151320989a9910621019448d68a80ecb11e6c239eba404b79820c0379fdae2c46f4d1eef6e67fdcf04461e720c509353ff8057616494e02d254e444fbf8dd662978e5c283b165715b617e2a948db84bad6537c7e551a489243c848185e4d12859f817dc5cc25d5b6d2e0209c99d45444210ed99a7bec4ec5276a12751efc5fa500e5afea445ff68d374210d40215cd6480350b1f60f99eb4fd49e068288b62781395291c0b59c247de60455fd90c8b89061dc5e5b8d4cfd965a2e5b6214fad6819ce9a6c82e1d12926cd78bdd1c5e81400aa608bf2a9d297a2f82f055080e3064fc2a6ed7bf8e648cf7bac947e9e32f8a3a6cdf8efd3616afe6d65b4a09883933f20f23ed03f574ceaa68a8e55a58d535f60006e8b819a73b5e2331cedafa83340bb3e988bfd0d9b0843cefdb5141be6db89f03d42d891a353cb6035865ac924ee1a32ed2c94f11349d9cc8ca28e3cf13a021d8de0ae55a172e9c90eaecd2b8fd702bff22864d4c8048e4667ec5f2aa9031b8cc4027c37709e3ad592a0fa16d5de61222f8145c09d9a4b0f5de193095ff3467d03b28bd871df9c5f45e54ccf99261d980d4f3f31f7421c8eba6d6af3ce2a45b6169caf3d3dce7119785eb998032dd5db6a684ef9cf01462c7e6be5cd638941966cc0dd46252c48fd169df5ab3391a31c3bc7cadca0f34df1bcab34e3a7ca2eb016169219b8d253e2ce00a56fac9f29685e8823f54df12b1f754d3ee65ace0884137cbb1c65df6e8a4010f454cc732d32dddcdb5e96ea0b3e5efe0c6f0c7a7b705d4e15322aec0cd58384fc8046b925b3feb3ffd7601023c7abb6802246c19838ee2a6d608e2df5f3b82a23cb8fd378b2164574945c08b6fecd19dad9777a26fe385a1ba8d8cc3f0031934802c1ce833939719848380772045005e253864fb44d5a6a2e92e70393633959ef0a41c161a56734ca2f9f018252c1b7987240ddc8b6ccdda4dc7a3a40cf10cbf96e4a7aacdf2575f85aed526852e92511a0182e295efa668ee939dbdc4bb761bf95f0509914eab73a065e0cb1c4ff314e573f3cafa67cc3637fb5699194f1eb3f44ab088dcbdd5c446bc954eb20589c80289e25ce7672d217345c4897771fbb9cbaa10ca5a385c9e781f1bceae67c408cc76b307fdf5cbf984977c2fa4290e0d7792043dc5435de893cd52ef619282fa1ee08e7082c2ae181063c8810c29d68c4e48b2cfdc590d990e1944feb0d18a7f3804058d3cbe703a4ed6941f11567616c8f773eb79417a242c879280a7a08809ad71fa98fb46ed88d93c82b227eac990b19cb7a3e5547a9e5d6912160958ce83f5ba7aaa16659db47ca60751174c1190fa21ec29a3e4f6040fd56dc8b2f6f7241cc268538c22003b5018b0a6c37069a647654254a0c0e55078409740c1b05eab2d630c757853a76a108176ec2fa8c18e6e2a8571228f341c9083050b5ad93abd465d92a42e1b0e5b8d18233c59a7678c3ed026235009557c2b5d28835bfba5ea70e1ee640b275bad8201465cfaf67c302bb459c0c015a14f834c16e7d248d0c9a3844c22c81d97195e08492999da947568ab1099ab74b19d6a9f7a3df8ce97ff45114080f390e016395c00f2a703628da9de9b331e1cf6962c0e4e246a5dc60f01f81f19c33b266a80de13d44b23150b753c7fd447dc01cb5896648236cc50ad20e585ee5712ff61f7b3bba75d9752ee3b7fef42f377dab4fd132bd6ffa425963e3680b814b07b80735e626cb1798a4698a456c5642a73993645f40819c03a6ea32cd35817360eb88d7ad1183f318b48f37c3dcef7cf9366081c07265daf28d4b1991b995d15d474d3ae9db8ccd3bdbfb585769f670b9e1a40d748fcce11252e1a1410bfbdb00358cf997b0f4e176bab642de513e2ee9bd2b3e67f3976a5c6e5d8d15683b9f72b0eae996ce03aa337a983bb00e5e1ce534d074f2d9babeda674dd8d85eacd7679e409ec3b5becceb790d3717c1ada7900baa17bb5de8165752936079b2b3de650122c21dfff9963553872dfa894280ff734f00eb84648c823c28301bf754651d9d2dfe77c868804ac24ee3e14541cde65ae0a7d3cd9f6a4b6753161ebc527aef59625e716633e643cf66a5a76e1aa7f14a3e4b880c435475a4f864b61e13d8ce14899e7f7cb7424d585374cc65533598df71c243f4fe95fde2632340931fd07aa158d5dd09e7d38fbea2a0ce023956db175956ffce88318396ef3966967ffafe522ac55306610f3fdde3e6a2e587f9442b6f0594aed38d19ae6496161fa270615c6a81c08949fabaa352e568036b13f3aaaa3e48b8715e494ad456ea1c63f51d02d9760fce38309bd5bb9b169e0e72bd5987ceb3e16836dae54208154e01f0084ce97970f2eaf8e1322ac891f50c88afe2b2771cfe2287fd35820c647140fd45a41dbe88ef3eb564b098ec45c432f7889613dafe571b4f51118a8a86083bf364c24e5f602069173afdd3c3032a5fb4a249a13d1332264b6b8c52b4017c423a17b2d1394e25bbeba7fda1ca85d4cdf6043e86c6eb67b5c658cc3ddc635b95a61ef62ae89f6849b3b939eede1080d28a6df1fb60260448778efc4a002a28cbb002cc212d11f21b59686a2697561c87de63a752a260ab8298ad9e6d1e1afcffa504e43dd037857f9fbaf9605b0b0f7f7360d2e2f4f9d5c6050e71aa028584d7c632f5abc391b595c57d0006bb3467d1b87f722c27064ac24860579443df61c16654dc0af949c1b41c339736fd6d988834473e7c24ed4f6ea9bd8c3a7a2752dd86f57e98010b691ab247fabe5652b82fd328a55600954335130f756bb002f09555fd0192e4cd8dcbba4a0d3035ce0d42ac055ad6f3c31d9615513fb18a89506ffe6972efb9423f6bae3ee075275968bdc82793beab1d9f0af63da829520d21e992091df1b3b9c40c3eade108ad88ab815cee264603d40b5752e1e4cccf3b98516c5e281c974fe9a51ba906a570f93c0f21e26494f3fe8b24380da8c86ce38107bdf6cecf9fc7d71faa2e6d873513998b24a9d452fb46f95edb724e08df2d3cc801173a00e2361f6c51efb67d4845c997aecda81bca85ae65a44e413c3f1175d7e9a4fbd73b04f5fea47daa40f00ec64e95cf27d3c39a5baf45e336aab283b8f0b2fac96fbce34bb702ce2204921e1a3c7368f632b6c461de9fa5e524d3609b13f8538fdfab3b1c47acced0e29d80ac0b84cb4f33cbe80a1a3146a1822be734d7b8edee2afe1c54d9e1cc9cc1462259fe66a01bec026a39a4ef079ec27cc025e39aea2ad8c15a063b3ed8f25af47c94d3dc6bb8469e9fdfd492b288192613662bd8023780e6a88dfd93f047bf885b54c17e3b974ecfd19cab37458b214904937a68d1976e50af9482871e97e97f4155d7b20abfb97afa32cdb66a88caf716ba04eb1ca044de6fbe8c4607ee396315e1a5c1e1bdc40faa99891e9b3cc708d15c1affc1c827c7f0bf28c819ad45255b0272a8cf1f78321dda886085d958bb08845529e0cda7c563c5f6acf106e982407e6eac31a6366d83596f8bc61792bb0a9ad0dcdfb8ef21063ee017d9f30fd440bfe699a64e1958af259b5e30ff9f15a6fcb75ef7d1142d56c5209befff4d9a44a2cd9fae95f8241efba54b44e966678925fd6872532deeaa531c6163639a9a2bd0d4f9182c09ebf9f5d71e18027f57ac086b352601eece31bb41e3266e5d51fd8af998a8eed9f4e88119b010734ec8920dd66d88fdb39cae2fe4760ceb3990ad7ac3c4a7f388231ef888fa721fa75beff8355b900511120a4d7ab0e51ed48d3d24a5bf81faa59ce50d55e2151d5e77f4f2f3391552777602b438a04e9cd15210b1f4c9c7e53d48d2504e3bef8fd10714f13cc089e4a5157132c7a821c0840aaa51175c9f7cba0b7f331f16b268805d232885873c020fd041379cd3ab02c2413e7477f59f43e7b0175bc643403c24e7bdb67843d69773972a73b01f07234fe050728e979bad273f0e7c75a53fa02ff52184f8785f0fc77dc4d3dcac5094de6a2924bf13737d81996edb5c59bb2af2ad2db85201f08507f40061b0385fb580e1fe8c3692677fcd0594ca9f837243fc1549ae731133b0e920160b88601c8cfb6dcc1580a608e6ee58a0c28c33103443c9ca203de01e3e4d691f98119f90b5692af0becf8586acb38ee9d8572485888e22d2018318ac4b438ca9c5ee1622434b5e13d43fc874b72e58d47344d3918c11965df210275e0a7fa2ba0e3385cb00ae836d9259378feb67d8c37ac91f2cfb068f29fc5504cdb470cd12fe6237de31fb6b9800d357ecf59fb5656e2b58d8f548c579816f0c120a34b1f964e688935be94b90efe0038943f014df94bf021a350485e7747ee725f7b95b300df6b4a3a86e44db6002a83b24e661a5ee14fdb7f92505054bac1259828de9f9e58a10446c472dba4d341a1751df7d307d997857e9c6c9a328dcc2f40f63a5ee0dda372d9b163105798f2472b7d5cbdd27da952490e2b4551de821d68ca8205107974d4e60d946d68a9f7e65d6919f3c6b845e3244426cedbe0aeb3259d6c4a9d5b3008b000c2341670fab29b669ee436ab70b9259b97da87a320f7e349c88fdca9b1b7a78444432a2825797e37285481c6a59f6a7f7c52f6ab4bea00d7bbcef4e4f6bdd7537f841e531c894f90e6c360cd02ede301a3b4461ef3e838b7e7dc6335c87661312809dfef1479a35ab989ae0d0654a8ccd68b8b2d62aca65071675128afec0684913712b1773b800795cfa28d55960b0bacc874ead99453f0ca7e68519008e68100563f681f0235fc910cc0c957c5666d14a7046914dfd13f440a1519c02b3e03575b3beb63904efa1854db459766a84fcfafe611b16dbf78bbe81b3e97c1921057b010ac14eea2cb8356bef0066fbbef12a357c70b416c62f46271968e13d8f99728971b98bf9fdb45f7b7bb35f0ef44d776d1a5dbaf978313a76c7bae5fd75ef79363e12dbd6f2e4f2eba6a135fc8baba2263738a8227184829ae772d1a1ed07a8717d58b0729bde6b73f670511a31690fcd2cf534d7e1c6c663e4ce7579ede364cdf63084ed4390149018c2f867903bf67a7e79391ee95c534ca6d3b961ccfe2d7b8eb474f3116fd99f4e8c49b5118e7b0dc63cc2bc59a521f2c2e656c2f634ba01e6cfb9826d16e6a633135b13c9a6f81b651fa7cedc53e4ba3cfb2e21a95be59376bc6e16f69f70bf9b5f2603a98b181e0c569d479510ea08a6c4c43793646eda580e69fa20c2cccc5e60b00f1d3178003ffe842f06b8f57d2c67967e3d4c2317347a440e76dd9f8a6b50c4ec4eb4e518abad7dcdf1e77406fbd710f86a0b730c063ee9d2efe1b41bdf4bd21da8269dc30aa0499bd19c778eef77f5e6aebfbf3a5d3894fa6de962ddcc4bd398693e5ecb737cdb762759463301ee4e74e0d0224c7b6476d83002888359fdba2c721b70fdcf71564a291bbf58c411acb9ade0a06cb82e494bbdb4ae0c1bb270b108e18d55081f7b37316e3ea6f0e0b285ddfc951518e103e045abb10e2c86ec6bfb66d34c88d4bf1e3edff9bf9be0ab07bc4e5c632a7cd8dab1d24f94795f8591e1deff5d4d7a721c7af7e540e8297a30879cc6060745c5e65c11f9a2fa3c22be5a21782aab203160cec52a48b0f3a4679844661ec7a43d34e9a08af3d4061772aa460bf9657d5c064ab712d2fad2b0af887f7090f3da1237d3bc7748444afc63618fae3806e08b3a3e086f386d1c390b197c7c02505514542e560cf74cfc6d5e3396266784489901284c3a2fc4e102b829362aec7af79b68ad99f957611c6d1895962eed65e83936b83df5a9e2a2dcb2473afe7f37a531b16a8468419ea7e2e78f9ca42062649b6d8d1a8e9a3a92428ed26a140bc98e64a67d469b328bfc541147b382ada34aa575948f8eeec71ba0bcdd08d808fc937993d8c9da698c9af976f66922a045a3dfee3dce5a5d82d149995c05acec8227dfda8c1f8e99e2d14985b1b5ee26506a31739ba80f37916956263380c9a38a12843a1f9ad95b5e85472fc662e70246032da9be6ec6ce126e2d63e3229531be477df7521a3bb8ff6202195ee0443a008b6de142053708bb207c601ca2579a178bbdb19f7998ed86500946bea597a613c751de216f5ba35e823172124b4cf165604a9c89afcb98bc5a17ce10d22c6a7c983fe2e8a3e5a17a62513562cc660885481f905d330d51ea43b3900bd81b101aec55df07c54caa9aa409e652e875551f1a0edf6230ab7fc1a937f2ed4bde598a29fc3d26f62e7f0cc81f1868182f089d3e0e794657591bcbf43f03f77524a97f3b4e927eaa06bdcb04b1db4ac3ac410a81a363ac3afbf4f324af2c25bc9b934ab0e785051a2ac895e6bddfc75f73ed36f23e682066ccf583fa2a60cedb94973c5d87c33f16ebdf5c5bbb86c30eeafc3b76c276f7391de7553376f74f03a000914ad41adeafddea589153b4c89404b9d6c5f1c05879d5ac6691411a7673528a3685118e4d43449189a10d711a0b335701f528a031597641e2f8aebeb00b91a0aab6e6b6b1dc79b81b8bd70a05316baa15076fb3a26f8f68d530d6dacd9e8e76f43792ea1df7c3ef3eed9447464aa01325fbb596c12aadd0b688f3c76b345a94d083773cfc482f5dbcd86c40e09db94e0a070f0dd02a843d6153c96e1962ff03923e5be09d412dc50663256a8ad74092ac12b49b8e0f52bd0c12723e439103848119871380a61a531e45749c2358b42203938f2aefaa338ca5f1ce42b6a963cc72b3cef7be1712f83a5c75ec43401fb57c106bb194b8f36fb94cfcde7d51c8e925a514d3b36d4f54d1c7b0fb7a62f63632b869325bcddceee48e7bc03675e446335ac874c555715bb1fd15305499e23c511f106e7c02e257c70de997eed5a918040a911781dd304d28ee10ad4318af93a9602d9106ff3c9e6f74c76a74914b408909d9ccbe945d4123bff791903e900505e63417134e87f74193828d659ae0b35758acbe73e5a7631c9c72b4d538631c0d886960da2f60efdf0402993e186b0be88cb3a1aa3595b18ce51261a24dae766dcbab4e57ec190228db74c3f3456f495abcab449a106dd17b7c42d234e4508d4745b32c880bd6499c84f3e46f994616d644f4a3c8906de4d555332b6bf8652dcb0fef2289b1e4120efab8b780bcc90e896b0d62d46b0298f2a8866490436dfb5d1de01e7b3c3362780dc9427d4484b86776ee00612a3dfafd5969c315e53425fcd770fc2c07eaa4c98c06e3c9c4461e85148ba293713d764af5fb403067ce587e883c422ef24eccb1aeca560367d5ef4201ccb8c20f239d9043fc5a33c6d5d8ef9d3c8abce045ba08cf0e8e66862c119aeb380ca8b87b4a45bcc4841ea0cbaa0f5218b861e80f71fb91686886d4e2d2d9f154568e9d264ef015dfc3fa061358a53b815affc139b77e5d5fe4c6f1b6d7fc8ba9f31279619a097f1f544871a8e9dbe9e3cd0abef161203b1bccc36d1013167e327c8962f28591bde1290a2d4b4e667aca5c96cdc520dc14a4fa510ad6ae2cc6e917c3551dd268f1c7451cc0e30bac9899ac417bef95cb453d61114f7439aff16a078b386ad9ebfb60437dc0a17004d62f10837f0d62c8dbe53a4e95eb59878f52f42cf76fdbbed6aaf58fd73d31524e69a508161243102fd19e1f136bc196c75d7be357096a0b528e88bfbc62d3a9b0288f9718c32c59e4f947f78dd8a0267570abc58a7f8eaae7b0269d5b466ae4eadf0451ab36a83db8c2d06670db9468663aad0c3589aa0cf94d6b29e74e36b998337b04c9dc673be2abd59e42eea680bc119bb281f084f1c7eddc6c9917a527756a0b82b82695b7b147abf6b0a30208cf8e104d5a1a3f282a54d248c01a63b9dc7a76e9aa4dc984140d0797912522ada42bc1402834e86b9101a18de38e15bbf221a9f318670cba47d5c4b3539c59d743bdcba68bf21824c6839665c995f3eecc3271b2395281ef0c07f803b27a62b3bf573da8eed0beb9761a511cc5b1cd01301de621f1ab280c059070be9206d810610fbca5cf4f03c84f453e94ab14425da72371fdd3cf8e6aa06e908a46c68c2754aa6d6b084a48e46e9b94c9e7a2e7c759c389927967ec2b5b834e0695e70c77b0c620a5f7e25910095d631194366a8f4f87c106f60403a0fb43e03eea59e052036fe7720ef863cd52e63c51b4eeb072ce8b5d0ae9749532a543109f1503c6742ba7c600bd87d532d8d3d45612706050b852677c200ed123f8eb58178757ab85bc04ca5f859d14689c202ec3c3ab5b4312f58f606430b8ad506c72d6a0c83a4d739de4bfb736cff4520a8b6b1efa1e0ed09eee56c4610a8b0384b282fac8f997fcdd4aefd120114d4f5c4ab969a15bb13104d035610f33fd07f7110097d34f3cb31c6a84840d77516c07bc567f24dece5e12cd53d104ce412a3306b0ac12604e8c2bb97a5c686716548bb5c98af61685c8aeedf7b96a9694bd7dacaa1a5694e21be422be132a91963e3fed03e5f179603407926bbc0311b765ec22afe9d6b1900a9a622196a099d89ff5d7df9df1cda22adf21d6fb28978e874a3d6430c19578aad9fb2c0d43cf1c0241ef71e8bce834e8a59d736c1b81a5ebed419d1e5cdca74123c50ad95376bf60987b1633f379bc8755041cfe03c0f6a12c175538f5525e00b8f8ed2b29c7f9d805c95db2cf1dab9ef4a71f183c1c1da91dd14137e0e52f426be503bc781170e67db00f983381c953b8e874987023c03b17153b88338f08d9a87c1bbb6093b0d8eabcc47f13a7227499890550ad4791edaea905ccfa63895e6540754a3e5d3ddde05811c6752b65fdcb55f537e4884ce6f98eca2e43f41b7bfeab44c44ca1881143ee26451f3d43af7f2a51b680100ca5ef7c04ea57cc23db699b8feb4e7413f285762b71297a25f1ebb14179c734008d7bfd9aed7765b4ef940c1bf0e912b5fe5821e7137b93466244e5ac308bdb8c3ca9395df43190f2c619aa1a774f8cd3b2115d44736d66ee402c20c5370d8e4f9a14fd7f44fe0c2b6bb37fad43870984abfc3e7bafd4907ca5e6648fcb2f0abef6b3ca2c25dc3a6c37772a2374b79435155773a25d5026886ec6cd0d8996f96e9433018f771da17b45bfb709f540104e6006233b9b64f827fb00b785efe3eeb641d4a2b3845ffb34912eb85939ea6ccd72d089f4cc0126b41c18678caa80efcf667d88bc5f52543d95bed461638569e4f0098f29c133638c91b00e6743579a89e7ae13be0bd02035497b4471e9867a796bb49fe1e8b760ff1b8c307918288c16d1cf635a824fb8dd364eef559abf1b6d629d18a6c537dc413a0c363c7e543cd86d65000bda0ff2b2720c318b5c48f1589da2a02a4134befdf688cc847a66db508c3ad722cb1ffdfb2255f6f9e6779ae423fcf3dc660f4ae885a9d3c176429e242442f4324d77c33467345f066fc4f1c2df629aaad786d948c97b9d8ccf966fccda172300500d180e95533ee9c59af9a94ad95dfeb0e691c367e7266cf3b84bd81f63c92faee9afb1280d63bc448f61657fff1a18761cdbc3ccde2242253ee12f47565bf62196a91d292f4c22d814e98e7f3e9f92b57f41f37ea6e303713ea457f35b1fe14e80edc32efb3974f033b8f9bb3dcf743b5aeca05837a76b9165268ddddeff51f9914a9aa75b92661ee8a178639c465a422baff88533275aaaee54005c6951923400cbf79ae59a502afab0106d9757adf0a75a35aee9bb77449768ff695bcad682291b1ee99648dd9108351d5f2a19c4a9a100472d81b09f2f402d6797e23c1348aaad7ccb0230baa28e7bbe0bb1aa0a166f0314ce6fc554ce62237e26f4ddf5eb68f7a1bd051ae025ec866ee8099c8256bb60c7aadcd713642f28751d2f8995ab3bf905fa8e086ca008f0576dc0f50998014c6f91ebdceca1a3dcf4ddd8dcc9b04bde7c14ea93234442c3ae0b224bbb3e37290af11f36131c75c6e9bd9b3835442092d565ef6275c48c788bab26181f6cff0dcd3c45ad61cc16fa5687b472ee9ffdd0d4870e30b5cbab039bc9036f2f9905c6375627e9029bf0f441e7468f008fb76e81f8b1aa65e49df9b3bd7b921dad9992c34d5e1db2ccfaf140d8b18fd7128c74f175fc248647708b49ede524c0917a78580ad85e20722e5d466f4148c505c8fbf600700bb94cc4c8adacf03424192567418c61acf45e03c5e5e8ba0f26f129400c44b1ae339f11e3a93ae199735920c1c63a96c9ecc36f033011ee04123ad0e4a948f6742a33a9e124c1a06adb9c7bdb525037e2d68064f351f98652e4c2ca1cb3dec4825dd48ac4b02ec263172dd55fd594123a84c6126b1155bcdb1dd03f5325a7b6e0aae2c9a2f8094964acc260844567d0a04acb30b365be788775f9a73a54c89b586a1af797e4a212585c2be291156aa1b43536f2945e194d29c3ce1332f265ddcff2d4bbf9d2f6a5dba49b405bd05397f440f6826039aae370461617cc4b6f01ef93cc16990673d2b6af2c45d6de88d306a407836a9a0c502e588d0d8de0b7aef80a1a84060821a8c49e75c54215fa9096a12c089808d2841ba865458584bc5e751b0bd003ce692c979bc6ca82d8ab588f6b47c61039a43685c13eae6b8980208781eb145923a3586a1c9d18659bb6efa3078b4fe817182420a1a987ec64fa257b5a9f859c4031021c3a251b7b42c8b772c125e2233b5701dea1db15669126d61fa27fd652b1f8ef2f68bed2f32083901e5161fb8ce9001e069e745089cb056813a9d68a2ae40be437f021341926e597361edd46e8371b78e12095c6653f34e32737dcb9fa7ff887708314b4756a78be91935948a9d3fb25767336e077e99a8856eee956775bb78767896a3fc7a49834fbbd018eb02b6f5da9e67bb9a3cd9fc47ad45e22ed60c1b433f513f352e002c096abcd2f70ceab802d55a99af115faea472258fa7727073f266b5f51e09e282dbc93e87adec84a942c1c921300acb229ed21225215b2377db715c5c1401150106bb798c0bde5f868ea93fcee61b8116183799e2a7046035a1a4b012d9727fece2f93165df497cda2e2af581c7177b8a6c123a9ac7f92036acac3f8d153f49448cfa01cbd1a2d87c92896d493b487552c3cdd66b1b80887b7a48fefda1e1bb32d5d5dfec93089949c69fd5231feccb3c21969f50cbf36419d6576d3f0464e19a77b14cd4461e87b46006cf52d08d04ed35d862d23276718b90a3f696293d0d2f3e2cc9537fcfca09f4fee8150beb6944dd9a138a1ca3d760b3b41de093d50f311de38244a9a65e9cad5f058e77cb1ff78510bae6ae90a6ce2dddeb8d2a26e231a2c9181c044708d0a7a1f1e05d09e24d1165b1674cfadea2c4e79e95a93ab4b15eaa52524ba439af421bb3c9e586eea0848e5b5c3c33766975b2f43b60c1ff368a6152df0cf3f9868a43260e851c8132708170d3abf286c3e6032a086e9c3e32f4eebb0a703c39548230d0415d78f178a05ced8e56a2e506f1b98e4dd65b990086d923691ffb0b184091f00e1f236171f636821a70b6a0210e494fd8424c26cffc3346d88b417b8a389f3fa041b28aab92464cf279b84e58084b4e421b8770236743df1c8f2dd8ddd5c7afdd96e4d50e23d766553521e8bcfdb65661c9d6d595767d7846d3f0021c797981da37deb06dcf5de78ad6a9666d182ba1d37e42e2df5745faef7592f6f4eca8e1ab252585d761d64939889af92e789a8279697cdc5e77016e788fa26bd6349ead490deaf6eac687b330297759139c91207c245ef7dec1e64e1b504920593b9c3570d911376343574d266dbe92b85faab5bcd4579915b7f734dafb6c32498aacc6ded10d3c2ec7d8d4719146781b313d411042bc2ec54df032a929b711e8f59aaa987d678b9d9d3dc0b77bfc22a743f04a0ccb4e9e88a9afad39446bd21de91e53907a70948ccb7991ccb01202610fe14c6fc6976ce63a21643c2d536bd31bd42eba848bfb4a6683b5a9cae5291a35fd0c2a728a350bb639ce09832ca5bea781fdb74005549b5d838af5bd199f3e65ec89f56e7fe090575765ee2a92db30347e571555507df82f78a1aad66962b54fcf9b9cbb788030ca7761b54e7e0512ab16c044d6f9d6540bf00a59c7c13b81e154ea3ec960ad24edc73a3ec83a1f104ed535a32b060ac0bab1d50c0f596b88f8f1dd0e533d609aa44a2428adb0549f20e36cbbb5a9d78b0574dad38b2edd6a2b2f4f155eee59e4cdf578bf00662967e5dfeedd80941be83cf6f4dfe4a19e1ac9fceeda87e0604a8ad677363fd261645e4e44df3bf5fe22a81cad9a2c04a28aa70a217f12cc867b0e458e3e286365d3ffd8fd349b7040b9c02b849924cb1e2ccfbbe2ce81b92b969d17015b017545d7dd35f9a1c80f131b51fbff2076b4c802e59abbd0f1630c8d67abf190ac0e64fb0bfa9017b5379f185096c6acf4f340dd2bc672b51709d78972581de129d847dcb5d67623e3ec20023350cb7d1c0ed120035a79007f2112de0cd4f8aed861b530e3fea630c25901045eb3bbba2ab06dadc237ecf7feb12a52f48028b66329862343d1b054b648ab1cb0c9f179a9f16373e55fa2627ea9e0b8f5c797c861d9863678e6458299cbb22655606bc109b3609cd7c03ba0c739515defe3d5b78053ee117843c93b23daa7191a448860c7d066ca17a4a702c5153c81d84f60bde9e2299327a17301df0d65650e514bd836680042701138a5f77ae44a90f88ccf47bb55313b619e1723537915f3f04d363a8e5738285b2ee85bff92166beb329ece20fe6699b3f03aeb85b13ecef64a0f14ce5a09078f84ab8c80ec56fc23f2e1444a6186e35198d1d8e52d58e0e32a34b83b8a65489387f613681e5e383d88c329ae080d270cca47e307c86978cf2c0d4c13c6a2adfb797f8b99c16fcc0bdb4074bdb148f612df181aae16184358a850052b5de4e57894faad3c131b17386c0e793a42b593e82dc75a2a044ba05b0eef757a3a0be45c92560280499f5b14ae62380a9d2a23c449f3e4dd1becf84dbb4f369d3c1d26d7876937839664439741930db1ecd77b6fc2366ce9bad0724ba2a007875b99a7a4ccd4c5785fee4b15179dc30dfb69b1b16079cb6beae37ae639dc1a8330ce862764668abf1123e34692d8a18663499544614c154753ab42f897324575ff786c1dd3c6511c9b8b57c8745fb3032186cd505b67abad1866b9b87e742bc0df940e287769234aac4c043a2615c33b74a003a90bd62bb8fe934fef2aee954c38be61df7757c1432787ac5efbb752629798dbd662b48241a66ea1d839e5c2abc8d3a101001686832c47174b5202cd73166e5fe87b5b63622ace49a9585ec11eabff50289457d012863dcea9cabd1aefb93c6759b939d926a7dc5220ac8dfc7c67b80c670ed1786ba08c7af2d4ae938e503f4a32f06dba2d691f954c6d4d2b08225d2662c5e8f842ebd87c117e2626fc54312272707073fb16b4b009507f07c14cd59b2a0ae3f96ef8036d681abda3d4e5b6523287675a997745597d514df1731d256988a9423eb896611bdfc6aa08f271c654419e5b11524b2dad1a155d2f8fcea1c8bab0a3a99810c15c92071ca1689ba2a3c75f7aa26378f1e3c596a6ba3c0081b74e6c2fb9f804429172a70dcaf38a104ae8e1a29a4bcebe89c0970947d278508e19c787c07d0524e02a9007cf793db53b263379ba675706006591a154d08db1f210e0e0d48e4b9979d88599b9cb36191b5444a5e7b2eb8ee89f5b847a847d8fb2703cf0047c1df1fa50c938c4403aa8851299c30f5bf809e6521c7d58701f6ec9297e1d477483b5690c1946a9949406fa1df4dd3995280e8859d63fb008ad11089f4311b2eb07c45a82939430a6e0acd86f096a4c89d0df5e75fbea80d0a3b24d234dc2b3296c182c6d14c26e5091647e6635f3f7056d57513dc161c8d9868081cd92f894152a8fda6d90d7969a4ee786b523c240b0a60b97d906504d5840a1a0af8552085a81a29e980a37523a5ad25c0fdc12a231e56f71a572f2c6ffbf8413ddfd7211a2a5d9fac0a1b5242ff45cc30920b65e8db5945295649550892d4c3609a122903ac1cf62cfba1f50a7abf63aae2696472a2180e5902e3bc0010d5757865ecca31190882a02ed626176e1717b40bf1f91f2c2eece43e565fd0a8df7e08f3849ea23f490bca9eb2865fd1b37da2f3a958cdfc72304ba11236b1b399e070194fb2cc8f13a579ba8047a779886f345aa166a978283d0dbb65f0f114cac58d244f3a09ff9613481f05b910649b7b31695bcd3df9b099f422c825a0836b20d47eeda034f668167077ffa89e15db2fb46a9b6fa8918d6da4dc8a302639fb79f945c977f3dac11fc7efab9a45397bd16a7421664ad21140c109e4859122d963d0bdcf12fdeebf53a66e965224e69fb4a1f63a281403ee8ffed384ba3ad7014dc5466226ae5e8b376a515439a8258b636d7243790fecd946c009748e73e7d348eadb412b65818e8a8d101a8d2b32f888a6bd63cf88fb729bb7c868c63a67916fa803e1d2249617a81abb457f882d561853c38f09f28a3eb54accf9f797e23de509a41ca890b22f6efa421e817055d2df9838fc228d60d3b4e3f961622f78bdd52ac232b5b35b83b114c9203b8592769b704bd5f5e7ed5a3d97269ae8aff99341b4cd8827d3925d6714ebd7c044aaad172e433cd83301694e93532af341617714cd6a8101b394f227462c34c1ef65a7f634ca33f3fdea16629ebe154d090e1e84414de7500abbed835fdce108e606bdfe0940da74def081bf69f2ee9c41852696bbb8410a08c7763757d35d5ea6f83b157f22f6be803735a9c9392bc1232182025b417cbda5b27f5409c468177346012fe76caaf2dac5a551831eb0093b88bce8ada67bf7d738b721d99b2f271ba9d99d9dd15527bff7137806840edbb6afd78256913c350326a1c3481c2abbf260a619e03b08b835306639b1911aad689f794bdc489064829f51beaa7d6e2947e01e1ae9a64aee283729754768d6772462aa0a3dd70d341af5fe51ef321b78f652a0ff0310c0486bcbd09d9fa9ad00b1a21f9501bf9c58316added8b2bbc93b4a06da9fa5c720fb87633d0295a3a2cb2901ba9f91a96c8193814d637fb877ed620d414403a8548a9c63aff47db75628c660a72ef44e306d4eba348c4e71c7c025c49cf81ad5095f4f9b86fe0e7d43477115d0b0a599968a76714b8e55814920dbacbe9944d154ac75eca9ff6f523117e9547cebe98c6369edd3addd3d8b8d0d5351fc8552266b4a54ee7055a4ea981e30b5ac54d849a4a4728dd2459af28ea5f8e9acee0e60faf4caa13e12ccda61e740908a926c62cab00132eda366c9a62e15ab983377787e56f306444f34b0c94200f5a9c05d43e129434c07fd8c1fc66df6d192454ead4c3ae53778c71e0d919f4ae7d9f0748c63adb80094ba99113a1017d7aed0807a072e8b1a71b5d915efe7478de22d90f097662bd9a614269295233d3c8ad26ea33a0df37c8de331467edee87360fd4cd5f1d0f6f54902b3e7a875f6a04fb46820c59f680d5b2032258692d97165d18deb22e1245602ef9b156a09aba19ad133e51c028788e87a57d00a2f9664ff5f56fc7fc90848a1e5086b7aa3e1c4186a762e299c2456caec5b062e064d49526a14f1eae45d2e2ca96667b1ee01116ad1a93272a3776e535a5440551c10556b9a6fc120ec5d9a6591d6ef3820303ddb65dc9f2d660e1bf58bb5d8a81e91a3469fdbdbf5cd2b7040c206fc7b790514730bf3e0aa5e67ec37985a7ce84fcaa39554ad29b5472dcf23d207c17964d7a236b0d8326f4482f45d077e9b162e299b0bbfb3b21679094d6ebc545d67bcf109568496d6da06ed8dc6c5ef358252a4a1840a5ad5f0c4d7ba74eaaddc2c6a17c085c266ec4eb0840441c57d9278f92008875313b0f9c04b3149ba2ecc586423ac70835f8d59aba4f9c989032d027f9d615032cc47387129b3081681f4091bc6a1b3f364faebe9d14a61bd99e8804bfd6bc4b3b9b9730ee0db7ec0ff1d8a22fa5f2fd2eb6b7da7b032de38ce1cb03863374d3218bf7ecedd77f1b7bf73daa10eff791c2fdbf8b22b37d3ab5167e0286f75f1521be6febd4ce92ccbfa687542d482fdf23251a19798a474f23930bc52261948204b2d41a27cbda83454b5dae6d490fb60306b5d20e82c0ecc75b98c4240f11f0f759a745b8b262fa6bbf8eb7d1d52864ccdf77594610a572630d9e0ed9f871c5c36f76180d5a5778c8141fda976506991d2587a1029c830858c6d97ad2442947112a1dbf401ae435a81446c4055dcdca12b3ac08b9b72479ca4c4e8668716b1f77a35512bb4ee98657138927425815e9fadacf63a44a7f7ced7e87340ec8ff9dd1efedf7c9881c192a1359c8f41ff9e5eec32eb7de080756b5bde44f17e9f66de8e63416ea6cdb22ec722d2158f512f060032ed8b0d8f4ebe64fbc7112ab1692663ca37559c05c17a6fe3fbe201c2a1c3cd7108a6c715e8d8ef1c5771a023e088a8ced037a168f8bab7ce6b48b61d1aa05e2bc5801e31afd3ed5f9a70bd3fd4c8278d25e6ddf559a015caff8eaec2475040307a8c7c3888af8d03420ce5d71e4ad8d43103879f0041dca8832e6d067bec8ac3103ee20a5ccce39ab6b546e1fe7115f7bbf285a57d21a5d5ae408ed8372afa720fb50b04f47f7644e3058b6802fded8d18778a89db4020a7b9cc192cde6044d2bd5e363693f47f79e638b76075a0cd7b25eab66ef4e7b6f5aada2eb9fd860ccb1ff10766eea351145a9071f35d421f6d06e4673d23556a2e943b54811451b36b8209fafc81d908b44fdaa6000ff35b45438987c46f853215cdbde5b74a2743c1b82649491cf2a6885fd2e76b1f69f3973b06d24cb3b140c6ff62ddd4f466d9e394309642db6dad8cd313f708f018881b53ae2f57f202bb2d70cab654630b1acfc966a3cb87a5346df5415dbd968eca78a355363edc503dff0c1229b8a1318755e122a3ac84bc273da1ed33171c8e78b3f8cdbc42be982c6036ed63401b8e21cedb6199c1bfc5d20511830eef57b636493b78c81425a85ea985be7b584cc914a5c11e4a92dea29a2db922edbb4fddd8160d60a0ecc3f4f31e9e393f18710f05c272697905f00b429e2b369ff2d15344c6a1e02442ac583c0d2d1e04e52697f3d59952d39c0342fd09d7536bdd42df2112c5f4e661ee5af31432ea12bc73f3e36301f11a6cfe80ea3782fa988ede35c1a88869564970b972e2a003bf2077eabe33b28c37865bf323fb02bcaf6e7e20d58b3df0e96e38cace6e79dc0786dee70e200ef7fbf8ae171c6587537b72b16993c29d35f28f24269b46e083f8206d4d6986623a7a26a36f29a3e115067a5030218c3066839491fcb015cffabbb09fb505f9067057f7bff7d1332220644a6f14b7d6df1b56138d714ec1263f4029cd4253b0559792da586c9675b31a02b6a5b15a4bf4e955e552f01a377832cd2c56b7a8937426569a9fdc2076e707cce33282149faee8df2dfaebbd5786ae3e769ae8e4bfe8834393d586a0cf3e608551ff051ffcbf28526f5fb6c67b2ffa1c7bfb1343eadd97930a5485321025c9db16d13e1521f4661b0e2b7892b9670e960d7886a6c829792e3b19742fa1ee61ab1c38310aa4171aa99a1c05ff33e14d423e0a0f754fe2425285345a32907450495388047fdcc5a6141c32067d5f39c93596f08efccb48b6c5c07942432223fa289ad9871884a9974e6b405783a0f05392e008d0eb66f6106db45441561df11d12ac9fd095d446afcb977d664352c73390beea618cbf0a86d7b2de2e90d5d19e6f59aa3fd9baebb495c3b7d93fa876aaa0a41136511682a41f0dd38ade123846aa099429d1cb53c7bbde95543bfe321c346ea2581f9d51700cbee9d54e611ba985b3b5a598f43ffadd5b8ecec0701912e97290869c67c515103f370e83b365250def0ada58ef6993f3154330f269341a60a168255d70aba3b1e0e2cddf93f33861760aff0ee2f2e20f169f5e3f7f0b7a6b29234d7de4c513427042db2006ca74518fa2a0b42d4a24051270381072527804ecf964122e5946865061299c91f1d46301fc6eef16facc9ea6f092c951bf48cb936c4c0f5ed219949203dbd956338e42c669934400042078188c40e23957bb62ed3ea331bb0e47cd209054c4c2dcad672def4807009969de12083910a8baea698319c98bd8b60b76d49de5b6852cc2421b0cfd32dc16a4d66004382ce171908a088d1055d13c6502d08b271cfca2ccac54d9237722cc33298acd557404957467fd5d8af9e6b1a3bc783ee4de908ff7e51fd350a9eab6ee0c0a5a361a008e016316c9cacfa2a20ab363bb4b7959ddaa5f124285c91b1929349672f09ff381017db232b6b0953346b79436b0d5424ee46b4fb276a6561e8fd1eba36da9a5ef1da9afb9a59d8c28adaa5dcccf1d285c10571bb51a00cc8232ec4949e324a0a32d82c299af7a267ca35d69cc1368565071097bb02ae2589c6a4479d92769957e0b7f95d1134e171df55823d0c76e90acef6d2887c201fba7414f0fa624d520a1f854d4b6495f8ca5be04ac7711fbf7e096edadff6d99fcc254401cf84db603b36204a27a06fe41a9961e1d5dfbafb322b5e2f2ee9b2e44ae7aeabb40c7e7bea9415754acbdccba3577400ebb7413f2009903acef1b54c940def66b7786c55dbee7abce22c75f2689780e5edd420a2397e95fb63cf30d1276de24a3dcc91219650822b92187effe31056f9e54e71b2ca0ab27d5dca6d7a1dc6618753d1193bc1de8b5c8c50af580d122b91f41e21d8e0bb6a9d002eb049d4fd774c021680ada83783d4eac09c6abc3a4753c6f804e85ae832c12fc789e5f3ff031c18fabd908c86508a11502fb4a0e904fc1d4c337b999fa6b715a6a60ef5f3062b8c09442ab1336b07c52e7abe1d279bba2dcb71c70849adeecb37a5859cbbbdeaa144854e0f2332711142a81cb17e636af5c456a57f49802c6ea78038f326b506c116f971553db0af7d62380ac7b4629646a89c1743f62aedfbd27212831ac8d756819c3941ffc21c59ec639a75d41840a2d55d808b75917f65c4dcef87bb0d2bb3aff62c2d758cb958924976288d44930eb0bfa51c146b3356533bab23ac960aaa4ef52deea04a2358e9f81defe5213909d05e4293da2c5b5e211721b1bd47fa370fe7786270a07c3c069a9e98bf20e5a318848e527b6d6511981d19d60f8810fc85a70876e6991196328b54a4574d27d0cb4d16d12176f603b70c46d5d33bd6e0e6682f79ad09193229fcf9c1747c16e0041d45d77d2bbf35a49775481d4cbce302f80f4cd4508b6a466e0d13ef386263bd4197a6defbcb34c5245576edbeda1f77c05aa07e68fa3fc8f0d68c50c65ef0a952a15ba5e4fc0e20a6a96a85669a4e86400ffbf1f3a3af525c9d59e98e8952783f0d30d3a55e64730019c5d1c154cc97a8fd0eae9d047c547e054a84ff8fccac345961fc3407afe5526ebcee2777f166f6518536d264765c38f12757ee4c5caeef632edbc7aac4b0ae21a10a7f4b36e443ac4a95f223eeb9a3f15a85ae80ad23b18cfd0e9cbaec1f812df7d13383e048ddccb6834fb177c72974048d6977b4ec03482d2dc1f721642031f230fe8442017101626d84ba1477cbaa3698eae40709ce2fd79c9beed74084c99020b4bde94a868094419328a04e3d294fc5112d6a44f4c929e1a69408f56341b321c2e839e2e1d42f3ad713822e27ff27041bc45be05043f3264ae4472e973c0f01d5e2133aa02776584f2cb0edf152df5ba39e39ab900306742c42ea7fe7f3692a5d8b06f1c9c59f0f677d52c3e55f6661f2cc6d308e47e7a007ef9739132eb4a0afb60bfdde35d7c67087a87677d5d568120437a0f29994b79c75a22a752542db469bb1c4c9a0de0e3749e042ae36d5fbb5196538700d1f4884d83d6bf9b250adcd7bf0e89c02057e656154f0a3070c85db69c3d1dee29fcb198726e54e2a58faad9afe02e8dc5774fd614a0761f13c89d7f3875ebd4895c72386abf3e883c8ad0b2bf0a0a648a1bf3b7f0e20645e95829f2213781ee0766ae6c7d6586b8540ee4f99c8cd655a673d36304c59b7794317948a9af2deedcc66b87042140ac0fb33b47ba95f78a4a147e679f38203a4764264c0cbef7b07967d50090c2de6f9de9d39a9b3820adc7c059945d3775e90aaea6340dd5e5e444caeebe572f25ef0b3eba292335c0757e1745749cf24f1dc23e4ea117596bb6676c6a0859c670319a94bbab076577700bd042ca316d25645f8849957bf4c664968a0995449eb735d7ce1bf9cd852e45f196824f6670bad517f3d1efd2b8e800cb1207a5f8702072994a504a9832126b450c251db7a0c69ee054aa406aa143f9c467e872e751744bd72eb9e6bb5972185eb9930c4ca4e8bc7ce2b39f6c92d1104cf9857c2c0762a81c4f363dc5d16ff23993a4d448fe4c2f0b0d3352de4d1e74b5bb3b36d989ef6cdb8920b3cae4995537721616aacac9bc2223c698373843350249c0660e176f0d4eeda1875b249033d4f27584ba557097d092d8fd3711fb60d764529a92587e89aeccaca41fa86433891f0aac95b22e99a3ba34c5bfb051f0ee019cf6c3f0559b62cf88528a80ffef640a9ad737974afc013e140941930bb4aa0b7afc1a3e6bb016c1902a06a616be7a2ebcb73672ae44b82c71e76785ee4f5fef5ba61b612590ace7b7a1f5a6b6eb0c19cf910f3a7f1ebc75834275711a14119473d9f2445971df5f849780091d62b24f360e968d0cde95be78b47402004ab02cc9e64c2cf0910bb4d1669e7cb8a2591f0ebe23c1cf8074f0cdd9c39b19355d6771a42e591f1426dbdfc5def188cf931421886087218266318582566d9027e7e4a3ffc928754ae147f26cc23d6828207aa033b43c33342460444dc68a34d92764e3a761819e11e83810e5e8c4a7d8f53173f86a0f34112212e11a9d57cdc51965324da0652bc456964d260242938c0c58db7fc38714044a59b3d4b4b1a41029be1164436fce4040a846bb8e4686a84dbbf1ab26b7c43930d80a6b3e34e461a670fe8970abb80c9198b0f0ff5eb10f60572944a5a20a1583448adbe90ca05391385d09627975e6d3636fbda76519c647547b12079e8e5890a1a5e5466d220fb6670863b8b804fbbc154857b9ac5eb4b5f8b7ce57271bc0becd0706b8e381ac1b25dadc4da8d348dbe1c29a10291688cfd89284e55a957d6ea4d4440be63982120b85898aca798553b00c50d5cd69bc512ea4064a2c45af190cacb6f8c7ea6fc2242d47234adb7861696a2c6dfca688125b033acccbbf15b2b665211389c373d921d3398f09b01dceb039e3f221018320ffd3c1a9b183f1f2ab88e804e0b8781a3d1b12278114e58d744b96dd9c54a9f42dc6e6af397a8c7edde98ea0344646adfb3bd2071989cdbdcbf018f2eac62f47d900aa4b0c3dde406f5bc1879363cb5ba592004fca1adcec3b239106d8eaccde125ff937d2288df7d8a95f075a7264838565eea004022c99bae261e47bc40b3b5eb83c4a0a75e5a37e478ae9eabc307e40d234e9fdb3ea932869e80c9b9a2a0cbd12b8b720acd86a512505676627210d242ac8347d8bf50c7a558ecbf7472e4a9eb2a67c1cbf533ee81892ef0022f2d2126d6b5a42d0152478b172e42344b1ee55da21f4fd82ac9beb66ee264ffb82acd835c598e8b9be24edc482c1d19c1b9860546c532ecd9252bca20012e77b0d0edd5fafbc4bffb4aa738e36fc2040628367d0a1b03088320614535e074d3a69d089567485b92b79eb50597d3b76ceeac071b10c2bbf999d31a31133534d3e4c5f1d204003254b51b161a1cd13cfc094201dc9d74977146f2676f68d6aa6270da4fa4cdf4a95d6c64a91ae8ed9132bba58f8303dbbbd54183c8f5691a502e9f49c55d7113b48db972dd4d99b46e845fdbf8ea8f1c9b82e4af6761cb1cfd4cfe8aa8a441b4988777f30ba1fa7dcb12b48041cdbe2b7e378d1c2307b01678eec11f2cae287b6e4e0c8f72ed094763251ed35ac903fc14253f63f344b1d4a31a5bfcce3f11307e1c5128c23bed3632da95f879abdd066ca7b3d7719c8b47f445980a6b546bc29c219507b9a0daf8e6e1f8333ca733c0c99b1e5ea48090bcc40328e526ece835add38ed077ee518f3932d10984e36ded2e646f947232ce73003e892220af241e3984175158114132468d5a360b534c5a69e503efde54a3684a2cfac9596d69562220723acea5ba39547e66d3a91aba498c38f2e6397b716455541ad0dc754e147135e238419858826bc8e5c61914815edd1880b23386e619daa85c3ce7d3703c32f45ea0242fbe1d01239391153a418894fda3f4a05eaa0004124da06c19657f18678d63d28e6ed807655b42c495249d0e748f2f80238e8fca6687b4413e6941212d1b001dfd6397c8bfbdf2be85a1adb1f8cdf71ac8dee0f189f33f59d44117d09d85f8a018894332677aa8599fb17c8f690ede161010ac49271e16179074c440615cbacca31c0023b481ec5ee4f844778534adfa851254d0b158a3245562bbb0520ff33c3c19ea3bef2122bbc4abeab0cc3794f93a1d69b10041a514b40534b705611617ca1143afb75de9d8d68fe73284ba29c5e154442173a977ee380edc32c83742f3a3fca861cff9d474806a29cd5ff61bf60c33c42f1b5914c06e8c3a6f202fc196f75c5f2ec6bbac9848b728e730f5b58f6a7ce9bea71e90f721e3fcc26026b34cbe032211236d1fabb29ce1d89b35f3bc655155e4587e6f04ce540cbb97cb8d94814d17ebf7b065eea08125a35352aac884c60be1015fee6f001fd57985be1459f08314fe1f875660b6ff096a28cd45cc93eaa9b3fad91d718512958788fddf6624483cdad7248f8e8008abaeb9731a1ca66304824b69ed02a11ddeba860d08d2cfb825f94b4772da9488e247ca523ba2e5be43c2809deb8c4f082e5184d6d0382ef2e36a8f3ee9e7a3823480553317c3809bf011fd533297770e856c4fa4c8e6c582ea605caea30a7cfe5105f7cbdf310f83dc6cdbf8cac876f74b73288e81ab2a34ee50baf65806de08d05aeb0edfca43ae8a88e4b34795d8dedd5fdc845b1817e88c802d0ddc8c92d0145c8b7090f7fa1d04c75572bd817add98a99170da53bc8bd1a8f223c83ce14c671956ce4615736bf246193eba43291b5574dc2e4327b9504a5eb66c557677909c0af9e0960e7d7b5cc7472f26c8bb9f6618ce5a870c7bf73298df4de28b3db38ab07cd4da56267097cded3c0f55a8a3ea6aea128cd14cf3c457a34f5a05b0078a03170157f4ed97c18615a6240ce3f650f40cf3cce84d24e5423f5c508beeecaea3f2679d53ae6e9449cc731f5a5b0b295fb8dddd6ac86535f1e52b6e855c7056338e9dc9d907d904621c269931b59a3b26529fd7e7f3482f84082d10fc5739d3d1d16a1e465998fa3b64f67b95abb9f222f722a2ef28c1f139e12f6265c809c3d14400f503a524bb89ba01daa174b59d79faa7d4e809b7aa4fea9cc0527e610c13e84ce5380059c64796d70c0c7f859461ac1b0d55b464f969fe3f11aac8dd2daf909643802d9902f20ece249a34cc82542afe6cec71a325d334981a4e9922f743a53331d8e520634f1f6cf2234338487e9beec3343242150dec47d9bde477d6a8d26229af131f93e8af22cceb26626bf1e1f597a2642e9e89c76806e0768c36fad86f7f1370d4b84967fbc85d0aa696c07cf2f2a704070c834414a4c38e8c9af9674066961039d49d528251d33f74966e990260670ea0f1d63322bb833106b1676898940e9a47aa85a707a288ae6b714ec92140ce35987050744c4da9b31e6692726ad27f2498e33d5548805e9966cba26993978e900424a44127faefe34a7a7c8ab2382f3b293558a2d13fc733d242beff18c1aff096732a95296225a8abaa54741450f05c3ed182b16aaf1f5336908f66b797fc7d66c2bbba60e37173c68492e67e9e066f3e30f3667d506271334d52f143180ad186be6d5bd667b6efcd9c140290f5a837a3e7c76a061e6c67f302df2125d868f5d9b36f33d58137843e58cb2c29e3170ffe0e77618b1dfc2e1f1b7a887ed5280e6b12c7133d0d0205b1c9c1bc68b89c0a104499440ff5acc4e89ad83730690e38cc74c6a692c46cbe4579f2398dd77816a0bf78363202ee9dd96db880b4986048351476794c29d60a1312247119a3c31ab1226bc865864e01191e4b99fef7c088ac14af5af64b2c18fc868ad279752689fb71ac63936f12bba685e4b71c7e5218b153316f0d7c265114cd618da57b3b288801521a38b26b4ea4f4a9fcf07261b1bf8b6ef2bce37c1fc3a0b4d7bfa5794e8ff3174626ffe3ed87116072227b8bc707a0500d26338543d562f74235fa19a098baf41f45a747842fad7dc14dd5c8416917d71fc737d16ddabb3c65ec7093707155e1ffa90cc8928e5f410ebcbf79cf49b2e468a16119a0af3799ec8b899224b45ed52921be44d01767dd22b27ffea362e6eb850f1dd8c495309361ce4a67d1d8e718a0183f52430688c3829f26314d178a9dca21490d141df3e9423956bdac30c370f436b00488c12cd31c4ba9f9c781400ac2d992862a421c26b29521f92b5ef90f6c16ff340d79a17fc8d3d23d44d03e821162c416f9dd469d20ce2b9ab57a72b6570b0c6706481ca33645cac71889f895e7ee77cf3f248c9804eebda606b1cf51a27432390047d747b2a70956811d3943718b7d38fa9f8eb3f6ffe923fa8710140b58def944237115d66f361ebe2eb1e5643738bcf689f0ace66ee584ba5c2df54a2108f457198ba0b25e9df96697a691cc7e996a4128cb5dbdfee7f83ba0fe965432d7014cdd8eb78bdb59857a33ce00b9381e9beea4a245d00005248dbd037e45f0d81c0d967b703f7a7316fea3d097b395ab894ab7db1326a32ebc06d85fb83351113232ccaf79fa6b51005c22a2fc497a1abad27e1b29b25c309920abff357404913b3fe150429c20e703580c61af7d2d9245cf722987493ba9030139286f7c23e7b03e1fa36028c1181d5df446061ebd1d7e7496b7a3369d8a2f6c9115802a84368dc46e0d17dea7750d6d588f118f2b2a44e730f9bc191af7a133a969b656cb9eb57bee54f41bff4c3f723267d392495764b146b31df5f43ad625ac25188f8b73521ab07b3d33abd6fd04261cbe110ac52b7bed057ab063e7bd83c33b8b4cb37397153a4e8ecf39d3444cd99632f33215c42741b03b23ee4f5d8191503d42d70f3f64005b88e530aad170dbc649d3782bbec4f1eb44d08645302e4b50fdd8f0e48206b91ce7b4069f715576f64401a18b66f9301f6cb2fa7fa6eaa803914ffe563f58824d43d7274ad4ca93f0c09d815687ab8063704744426dd0a6928c5bd4fb5a62cfaa95144b618e33ee1d39f4cd4b1fd66f8509949c0a5759d00f00f05fdff0354b52cf4157e44afc916912bc90a41dc1800b8b6ab7428be7a62506d27190d4c00e7af8994b9c59546100021a894a9431f09d135e0479ec24506b48284150ecd160d84bc4934e25330c125445e92eab7fdd9f8e90a8d2efc7d3e2b26e11f4a45282bd2fa92ab87808e017069ec0f57dc0a6edea0005c506a0820a2ea8436886cbb6e44036c7bc9c4d64fc9e4b8b20c5de14d6ef76d8b56fedc82444930f42776a8b6911b912849cad96a1c8b49bf7a65152ad0ca947c5fa2731677140415fad330dd3110e0c06c1121017a97ad0a9f462bd6d8078045273e08f5aef85d40c397365af87f7aac4c921f0a58de95330b50fa5b830942e847dadf0def84a85527483fffcc0e07fb6a0d6433a05e69dd6271412832a8f6cb7ff2b126711be20d37338ec2f730ac17fc507a1710e5d0804e1637dfd3ea3b334cabf7ef73ff92ac06d25a79ff7c568c621e1ce5397040ac5f271336db3ae10146e6092802d1b4fa44afef373d71166c9761be273ae375e469aa1fce49a9b127a187bb7b1a2c291ba020319855068cfdc8f61ae96ea746800dc184cb4a3e5d8e12661707c4671da1081e22b150c09943846337c90f656ca95c11fe34c52f1723eccbfe2d2a329dce9e3e341974d781fd327b3c7e061a6034ceca28ce8781c695c724e1b15d2be44f28527c340b6999950354ad62dbc189b20ee290dc9c133b09e89938c6da876924f14a22452901c96a28c9e93b9aa4de4fddcd6f30f1814638cf026b258862c997de8481f6b56913381a9410dc28f416ef7d3ac2521a6d802c17a53fc24801933957015cdf030af802955c30da15a70365509f835539fe1c56cc348760c19dca557aa499a25dc5aef1b507a31c165d01515d169a070a2f7cf1777a5f8d0a82b480a0bec85061cc06d7ae801f048f3887efd746a9c451542b3e35215bee2d775bdb7bcb94a494019509510941092fc82692e578ae56a2b0b285e5cf04ed7745d850fef8f70bdf3f957021c24e2673156fb84818faf1cf7819cfd223a3cc1897ef16228dcc2b34c504764b0c182f002b6c287f461777d86a9ab7ed60effb1b6919d048cbc41a6de886172fb1f3352ffcbfcb0d65beefb27dcfc0e1ccdbe37be55f09f6f36819f9d3327088cadcb7448e43a9e0d0e213c66109879f3f1c82d95f905ec815ea85abcd97e937b47962d1e3687b29e05be4fe7a2516e611a28bbd0b37d638d6a266f38cf92591d267e718a359ffec8632e7597b68189c207d1b13a4efb58786694c706682b4878661b216658835281d226482f4b5b0f4c3948df6fe3b728843db07a43c7ab025ce0f7ff79eded2fb3f37363241f789e35fc469b47f76ebda436689634cd0bd719dc286f1f52f08c1869fdd7d74e7f42013f47ff93ddf85724175727339fe96e363a376e227cb1cce57047282a5d349723f86bd7c0eb9cf2813f4e2f57a45d15c264865bf1c9aa03f99607b90b75c0431066682aa48d3459499e957a429405c92fb073b3f3a6c9a9c32882fb3098c15d6c86c39aa5b2d335ff1a55fb356661fb368de48e9d887fc35c1f6974b94e9afea437e4acb342e857dcc3e84e7ab02d9d005e3c1862e573e99600320e3c89739d61e34cb973fa3c8216f656fcd1839755ac67bfc15f40a7a05bd825e41afa057902ff1d6cb5ddeafea5f805e02d332b38b1d1d66155a256482ddfdb58b201a2e3346ea783c9dcbc627828d1f270d70c8c687a9c28a7ed3bc0a3b7aeea58e639a1252a765408a4154ee9b39fd3fbb642257ae448cc13ec0031cbcf2f429c13bd32f30db948de54ed5c2c60fa58ed466c6648b514a29a38c314a39250d369d4cb160e9c71c819a10ca320335e1522dc9f4a5a494650a560033490f99bee90524acf4628220fd6e07762e99313dd388192355ab48a3d332dad39f3b9d096c28b3fcd024caf1b31cb51a4c58ac0613b60613d6b3be22bed00cb72bd6a00f638515610d83fd4263c786a64c53305ed6b4458eaf2c71cd225555363465ba837e6d820de5d05cc51af4a1ac70615ba85d13ac5dd8500e652a87e6d31a4cd86e26925254a63468465daf4c3f6c57a6b40613364872780126ac9c53ebb29735e6ecde20bfc81946575c963e465128f926d7114161931f5dc276e51ec518e7d08e0f14534632f1992978cd95aa09259db962d0d388dc1fa7c44db0e1943f5ffdd233c99c3b139c497a10c01136c98c9152636526fdd2dfddddadc404dbceef7fa108cbcd2431471c1ce1caf27a2b4ab07347c70b1b6a53673ed165b942045bb5883f63d5115fba444a669179c1fa850d6d1f8947221588c042a786fb4d24128944a2c7ee0d11940016713e4e0c0be95a368c544c234526e893b3d90db321332694a3d16b5366fb919021121852832962d80d45f2e845d394270e8be4110e8b88706875cc0f3263b4ec3d62df3f6446968e8e0e8f9829c6f1fcc5175f642143ea4b9c09766a6221352b5f582345ea900d63cb86203f80d07003a805ac9a79b958b51d2c0d338686fc39310a0bdbdae4806039973d6773de724e1be71c5d21673bc5324de44d45a3ad1ddb26d7b433dab5734e33c7a8538eb39cd63393dcfcadbb6763c7664fdaee73ce894d6c3ac5de139373cec9f59c73ce196bcf66674c15551c328da07f46330ca3ae65ed3346d20cc48822e81f9d8a2c4045d5c6b3760446318ccec0a6e4089482259cb03547a01410c1450a3ae0852de508c444145315d694231013ae570b66060503c4448b89010040e3929dc61242466082032657408109508084ec0f323183ecffd51c0ee34e5d88d215a250032b308187043b1810147777b735877b3507e532f6114a0e40c65e5bc2878c7d577360d6b11c6ca0042a95ab3930093ef6a819234190310ccb820f1943650cc3300cdb6a0e8cd29f2822f7a42508398c4f5850489082127420fb5b54097ac8fe5f8227d91f842922fbcf2c91fd71287185eccf6388ecffc3045b64ff203c64073a410fb2bfa8e670af08552491448d24a6c8fe1a129cb32287a655100258e520fb674f00410c8c09f227875c76530c171a3646d005101240d4b3c21184600416d9818eb8c1088cc8de041f021152c85e8422a6c8fed60825b23baa066c9182096040850d92980213352e45f62851c89ecaeeeeeeeeb9e4c3c4ed0739a4310414300ca14511449c0066094cca4a3bf5f440eaf9312981c45784ab9403226c0fa98a247a34a020b280447744cb1e51441d22c86b620427c450f764045b10ab0a240414921811b1c30161842808d7b6022334136a14797c80443683112b11104f138cc06658f2a3ed70023a0311aeac8911ccd033d48312cc20440f508f138a98a1889d1d245c1b8249bf446d8825829881a7498c1cb06009ae172c810adf137ce08155c9510aa1254a2210aa014948680a1311845c53080d49c18410bcaad0d1b23fbd37f3cefe98fd8ec6e1a48fe50cc58a5348c18bec2698d47cd75e5ec186363f08bee6380517565cc186a8dc42f1580c6bacbbfb318f31a91ed8d3a738e61845f7c70d62041b462755b041f08f09562eac24c2e54843c4eecc04a58cb586886598a07ca98019034e503e8d1eacfc68e7ff8c4945ec59735531b79850e4cd1bbddc36f95b0e5dd366b2864393267a0c6322ec699c4de48d8f58ccd86f91373947d786126c187ff2e441c38c315868238f64c8a3d168f4a39f339037641efdac394650e4589dbee7d16fdb633735fa98318ec70f2d32232c511fbb4b70dcced77078747958c15a6d82950b7b337a77f9dbef90bf7d4e1e92472fda760cc9e1e8b5215984ca199e339030f86337f4dc4f33293cd0b3459770fa44187146e923818001f221e8913822091bf0a0822b86bac07cd045169cbbbb5309848cf9902463184a72808b216858cc188661188d34c570a171030428c8fe280e64ff0743d082ec1f7b7802cc0e6dc26a5042405bd907a06082211f9e1f865030844d1598e000882636566001f52084216c865ea81a54413f5a820b2ccd510a2dac909b48a145143a4fbc78820b0b7242e17d8f630a6d31c16e62c618a7478abb893987bc8802806273d7840da713aa84747950f782d8f1d2d5a54ca693551333864231632a17b3bdfcfe6929668ce8255dcd6eb5ccfc892f437a263f4159fe860561589053e12eba439738141fa23ab40717f228593ee6930906c2861e4485bb668cf7f21dcaf7f261646127abc92a95bde7ec4b37957dac29bdc328c2b6ce1c9aa0d074e2dd94bf4cf9462ff56629cb6f26329b4f2791dd93653791652fc9f23b8f521df33d3c5f1394f37112369c4338334f8c53d8c71ad2c79711a7b0f78c7d8cc5a9f832c747402a620f722ae60b4fcc82b0f23fd38f38ec1d0eab3922d668134eba276ce8411e3463a28f99ea111fa77ad09b7efa3edecfeb79d60b2d42582f4f4a5be04cab65680bb75a7485698bae5a7445e94a47cdb4158495ff0f8233082b7fb644a20edb640d3db9f9ad9353ab5320477f86fd98e0122b7ff32bca5ace329637da77667a65a8332ecc64dd79dba8b3ba556dc33f5e82ac6c1970fbafda8f58438e46598cc924d270185ddba6b5ccf6117b94e5b84a811cd9b7671f6b46388c41221c46bfdd5c6717c80483ea94d1bd73acfd8834b1e3dceb8fd8fa1169a2686e5d6cd125d4e46ca759cc32393b6ba758268a22919c2dc2324d34e222c7c9d9dc881b6d5ced6cb456ceb6d2b31fa9748aa7939c7d8a27ee942251f4b542a150572aa65272768aba9458f52c51a9b02758ebc7098b9544071bc2a2602fecf5840f1325562b29650625736541d993ac85450c93b3b1ac25676bc69c66b73ba558e659d65366ad438c114570bef427530645c54301b7c852ce7891258e2c9f2694f5435b412c168bc562a9542a954a455dda1393ba5c2e970a0acd899502f5244f2a1c5b148a3ac5324da552a95422eaa22eeaa22eeadab85abbcef3ece7dfd7537ed74475e88eaa07a44c5a412ea1178bc562b158aa7e51a9542a5513d64f8b0671b5f3582c168b65552a954af5a492c9743aa5a4a894bce5412ea1d7d06ab55aad56d88bd52f2c168bc5a23b54457be88a32299956abd56a754ac158180b63612c8c85b1f2fc8b5a810123958a1183a5c55b5a7aca16525e694cac0faa09e83faf219dd56ab55aadb097b7b0d72bc895e70bbd865660ac56abd52a85bd5ed80b7b6196161932feefc5337cc68c9e72c6f7f21defc95a596be52b10fc47a19c89fb781367f94f8b8cd56ab55aadf2fc1917cf98e1e2d2420b2ebce02fbcd053bec0ea20974ed67a81e03f0a65add64356c766d4ceabf2f4d1d1741e672d2d6b65590bcb5a346b7908ba6c2db8e0c2923cff05d50b9cbc1470b0ee903b79fe4bdd21b3962402c00100809e12009ace04e77bb9559a4ebfb0c0ef9f0ecab35d51a76534cd8a7088426538fcf7d174f2fc16341f9fdb651c2dd39a662dca85aaa3e66ef1a1455dd7ccd2aa56b54ae50a7510638cd17964ff7e183598992bea82125f0dd298b9414accbeaca0cb7be085d83830cfc810696417a394b2a38dec3264883494668961620d6d003e32f6f145fb7ab51f19dba4282e21fabed8345f1c0bdb8db9093aebad5017590882e0fae2871d747cd82104408882083f580f4480b558902487200d212674a0092198780089a01a2a601886f90083128228428b2754403802f603ed89122770028d6c61aa0be34094d2e5c7dea40f92b0c5c4a6df5e8c7d044a6218cdc10d44c0012c8a19e0805601045368a14541870718e10a203cf04512be18029b02c807095b8e403e4fe437a635172347a01598404a391b680537a094522c5b41502a47a015088d389148241a6d3d260e7cedaefd357eed6a778c78023602f938819123900f93ac32442bc2081727251e582d47a0152c891e821de4073800b4020fe4ce5c7204520116402a70bd400ac92679f3bca5bd7c42dea0af790e71325186290e1b4b1850f9848481bcd164c6b4765d1e035b5e56c6097a8481c90e13250c9ae4bed67740b3df60b46cc04ed1f7f35131de72cd35a73d372dc78d6493fe518ecdfe0eed6d906cb9db3794a1fcdabd9793e58dd91b7de56ee892478fa3b7d148f4ddcfc67c59f962c86a3fdf84f3bd4f99d82989616a6cd40fd890e3b09e27624bcc124d1126ce25306c63be440bb34245cc718a278020c792c89a28d8a2cb125d73b32ed1bd00b2b09f9b9b55474e761ce6dc24dd7fa2efeef0354ec4efbac781672688d339f64f93b540b17eac23292b8ed2860d15b47234428e404e44c9404e40c9a2f72158ecbb2c2ca6058e96f95e7e101b84cc188ef4b2fa40a2811c5ea0636b5dfc5863c631637c19565c1647c53355902aeed2f083f4ddae39e2cb1a24d6901fa5ec9120385fc61616a17cd88fd339a7c37249cdd15886f1c765e657758e90c082126e3ac0ce8b04a7035c4185236a8e9c1ec9b44211a200c4e4a1428b13d0d0a2e688094720f0a0e688e991481f9c8024015cd4943ea7267e8b8e65f9c2ca23999e6e0ac849eb17ff139023f6736a60a0c2a6503946f655652776bff260e763c1ce230850810e4f6a901ca087269050537ad33d728a410d1213133525ec446f224c44aab0c7308d097a868b0c913e3eda95401596f4a50fa310672dc7711298a0c354614d7ffa212d235599f5fe8909fa0f892ffede7f38d4349cb9cd307b4329c6b289cb27a40fa5da67f471f47c7a3b7f1c93fa907ef137dd30522c77628dcc3fba8488de47df5d9c39dab2107d0684fb915d9c2297c804fd4777c8f522004d58a992467cf60830e114fd9e1993e1147dcf144b95e9ca2554d8cfaa54c6b00d1e8348ac2e075b74991f310b156c4b1b13941fdd7718c99afc6e68247fbf611f6db00f8d3ce971b4bfcd7cb7e9fbd1d7fcdd66621bcc9de23007c72469da26658c9f12dfbeb46f7aadd690f21f4bdf7dc4a1ca6b3e22b622edbbff143fc5e6fb88f9e89f3848c7704a077dec6d2816b1ff6e486928f190b9f47d63d041147b785f837fc4a9efe37f8fa53e9cd241faef6d481fb6c168487f84f50de77bd8c71761f8a6f41fbe393d4ee789713a97b09f3efb534ad86157c1389dedcf141c4a7b43247d9b768ed58763efdf16871ac8f1b99aa37ef4d139e2748eb37e042d47bffe50acefddd048f6bea3db2ffd56baa1567a1bff12d6f1e130277fa4c6a47e1bffb6b11f27488ada7ce9ef940effef6ddcc6e2fa19866fea539b4a6b1065acde2983ea10879c1b9ccea50f4d9fbd3faaff86996ffa63d69ed29fedbd5fd20d8be48ff4a88f5306f5fe3deac6497a1ceddd9b1bb21c643e7dccad43e612ce4166ccabcdcc18be993db4a7f4eba7bc4ac9ded048f657b936d8a75c9beffbebd5f1e5b4f75eb297844313c6e9d29d39a48f434e9639457aef4bdff77b2c9446f2fcd2f5d1f8bdebe3fb7ed2d5f1e178a9fd1ae2b4bd2192948efef9368da30d86dbc321924cc13093f4d67ab58f43c2225a96a2288a53478bf80623196766ed238c8f0d3570e51b012b180d1b4ba01ebb21f6f3b795be9e576eac89b5510f030496ba086cb471b9f9225137d6cc19980fc000d9c8596d0a1b5f65aa2ce18dc94c349710fdb6d139e79cb2b3996533d61e37f3a5fc82a66cfab1dfd1d8d532e9fe2b5158f0a9d0eda37750ec3ac8658719810b73ceff17390e8be4c737a2cfb2cd0517eefbdffbf7defb3f995c29fe0ab3aef2bcf5e31c3d7ed964822c8742b2e47f9ce0a581fdbf1feb0eccdd1bfa8e51aa480efd715c3691424231655e68123999bba167aede50e6fad90d8364aeab37274f0848186864d1d55e74fdb19889ae6cecebefc0bee29c3cbaa18747f20c83e4d1b66108481868e44cc331fa7c79129342fbb7c8c0cea7617c22334a9edbfb861f9565192ac60a4bcba7607cfdeeebabfc56b7cf483ebaafd8077dfffa2a246c43b1e3fad9ab546b5352eaa59f4243cb932856a1f459b25f51b9998cef5ed61a543ec329ffeced7737e59ff25d949c726c93f2f6b7edbf3f9d4e26933be7decdc95e229948cf7da954a29444a2a45b499596ee09c709fe73f74bc1a1067ac8dd5bec13fc7fda12facbc031e860b1efeaa3e529be697912be6179fadba51f5b54300b658991c20e03e37446bdafe05066d4874872467ad275ca51096453e63de9292a7bef6a8ed1671909bb09879ed1ecdd3dcbb26fd26f9bac36231ffe248c63b6c8c0caccd13b7a8df61065ee23776f46efe3fbbe52e9db9ae3eef6ddb6fde8bdea387d09873297fefbde64fa28d971c8dd27f9bd71be5092bfe9fb6cba61ca6b7fba39c8cc7ddf8c7eb319fdf636f4fb52e94fa7938a4aa974baf64b4e38ce3dcff338ee8ebaff2e7d1c9db23de9c9670a737269fa73bfc39fc339d9bba167efe93599bcdf2667ba27d3cdc9270c0109c3f7d9ff6e7d8fef040b261c27bb87f7a6dfe1bde97332e9869e49ef373c92dd74c32039d92fcecc250c010903f7a41bd2c8dce3e8ef5a1c6794ee8648f2e613a4388ef008033d1ae881b2827a9851720e92b2823e90a5fc6e9930b0f01fef11652f1265da6b38c47118c0407b427415800384918107f704ebb2dd9452ea43a95319f88ff7685ac470285518d644a3997bcf4fa6578699214238846c1883e61c22e453fb91b225594da28c8d30e8c98d4d41286f1efde8b28d5eba46a38e4f2441dec8300ccb24883e932e7923fa2401ebc9a3ebf25a67e004bb250916a6a37c82d224d0c8f2a1f59efd0d0d07c31cfb9684816b82b51286a8b5953060d3c94bd334acb3189dc88043539eb4bbbb656b77c6860c4eb0ac1fceb9ab602e812277b20c0a0f3c603b3cc81b31c625398ba1f58bbb6461c308822e7b883276bef0e07d5b3de89feec99b977d74c11efb8dfb56715cd4b450a469b87fd85e345dad923762d6defb879e19a3fd64eedab8a31b51fde2a2eb13c466b6b0fe4d1f07f5ec2794ecb3b72de3fa41de9039ebc958cb7f6c6c30988d4c29b5a15d14762d9d58d8f82f2701e40d39613a8a542bb5a23e42a91e79034bad72fc1925cf0f89c0d8024b1191a57a6008cd182252adacddf964822d0c8a4c01b73f8298605d30a66bbae28d41073b9bc0d82245040ca11913532beac392dfb3dbbddb1bc4dc79d0f61f1b5078d09fc6a19d99914b6815349dac95378f453fcc5a389db3f72c9b42c2807df63363e2679f49216fc49cb5a64ea6573ee14264932b6c286513a48cd692099126fbf99207a2b9813cad9027145057d822e4f9314a20e4b964f6c4e8c20a4584fd414a1e505580e9226a4cc8f1e506a48cd62f3a4420cac0f4cbfc20d94f36452bc74a5b46b25a7228592d92c59a3071de0bc41a1303f265fe7440ecb1e207140f7986720357684c5085a8204df0f4996067032bc23edc0463648dac70b2246f5e7f74b994d225995b273284363286b9e63e97b8934c2f0c0d307acb1fd902c546c73f12956764c011a558228718a23f79f3644bd2eca9b7b7f74b663db39030f4d3ba237bece3744d28f246c7dc4f7f8632bd34f0903b303fb8b052933b52d5435ad3a86bb375a6504d7c942b0b7943fee4585db63016f759867d183feb8fd1e550debcd8b13b76c7d838667c6d46ac175b86bebfed97020c20ccc9618c2186f0480ee34b1c16117d67b1af07546a9420714a29a594fe0e3335f4951ce917614dd3344dd3348a95b8bbbbfbef3053e358498c2fd5e6cb12c73880f85366f917490c628205254fa008210b4ed41c71427e9448b2b91d812588b9c990116964c8686989342d2d2c2c91868525d6983162a452912695820123d2c080b1b21269565650a8488342a9a8441a15959494489392723a459ad3c9648a342653a914694a251229d29048b1c6a4f93e6b238db5b1c6a4f1bcae8b345d572bc7451a8e8b3526cdb68d469166341289228d48a4699146d3b22cd2641986451a0ca334d2501a6b4cf7ee48d33de79db146a491b1868c3572a4c4926cb2013644651c844089dc35d3022187a60c3393984768e1587801b5c0a549e16a893aca90625c402fca7c1f55b28c0c441ce2cb8773228e2f951b45220cfd52c62fd23216cf9e38412ebef6578a56a18b9410319931dc733ffa30befc85727737fdf94d8909b688896835c19ee9eef872d5ec61ee789209eab4173d34c1fed15ab98372531c3eeeeec99d021b8a56a295c8a74ecd082c438831c6e6dcdd29cadda7f8e6c18325083f208e1999a6114579c41af387879c9d82144f0a082bdf3553ce6274ac9b0631c618bbbd5b7a9bba7b668c5aaac7bac7a875db25b8da9dd22c882ccbb22ccbb2ba43d609c1cb3e6b8a655ab71034d10b5c7e1c6d94528db32622a66d1361de1c47bf526abda986ddce3277e642c05eebdea1b5c518e36a934d5d084a29a594fab643fd0855b6534c243a029689462391e43cd2c8ec5d5ae3ac8988c9e4894622ea2da234bb31e7706e4a99b351cd91592c8bb267b5a1b7f3ccdb5731fce3e508d1378ed3382ab99ecd299669224da4b1e66ab69c62999669994ab25e7345b10c0b61a24baa582f8cce09135d52c572ea214c185d52e5ed6ae9b1f2e79c302d3d46f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f04001084c27cf97f2c7893f6939cb7fdc09ab9b62be72efc9f3355f227291a8a714b90ac35c1a942879bee825b44366d768e4c33de124cfafacae898fc7792dfbebd9a4ee2e799bfae4a7534f796a1d9f76a95842d8d05f292aed982683fbd699abc964facc26f3e7c5fa6905b9da4767b6747474747466904be8e5e3e3e3e3e3a3a3a3a3a3a3b35992c9f41ad2993b53c5fa6905b9582c168bc552a9542a95aa5d3e73e5e3e3e3e3c3c467366161f4f57abd5e2c168bc552a9542a958f8f8f8f8fcf674a419558f247b664904baa582c168ba552a9542a55bb5eaea1a93377a66ab2582c168ba552a9542a95cbd52e57bb5254b895d425693d2bc944c8ebb0582c168ba5ea97a952a9542af9235b412e6f168bc562b1542a954aa582c1c2fdfd8444a1f88a43fe62491508fea350d6ca1eb962d278fa4bb2582c168bc5c268339685fa01c17f14cada9726645fa838d4a29a662df517f597bf02e191fcfd2c79f6491328c913af1bb5e9b0e77ddf1dde32ae7a6fbdcfde1049a6587a1dc5b3727e84fa9f20487b90567737088220182448101004c120dddd0db648adee6e10043d779325994c2a29a3d37f1030081804044170c727481452675bd3502821b971387acf4731964a38d446d6dd296d11572aa9d4c0f529961ec71c7dff361a8ddec4b9b7fd0887377cce0146c621277b5fbb76f5b0f7e8ad37dabe9ff6f7db8f6e28248fbadb6eaa8bd186a83c4a759847f4defbfa42f2e6617d6cb4dd70fb0fc7a083dd300ea5237c437a0fdf987ef438e668c338948efc4729a712572a6d5f1a71236ffb51a9866df4d51b89b6d18bb6f75048eebe35126aab3a606c7dcb6d28cf7e8c8d4b3844e5fa1f0e51f55137c697da1bbe11fd48348aa24d341abd77fd9612d8fafd38da3ef673c337fea3d196536f2864fbbe75e67ac30e67a30f478f836fdf3714527168bd38346f384492471f3fc4a1991bc92c123d0e2136912524938036d20d7964d217f298c95908c2c0f0c8dfc7094eafa3f4b3e748d997655ed7759d97e1194d9ad1b8d1a83b954a9a3591345bb2ddbbf75d7dd89fc9bebbfede0d6772963d8c03898159f0f74bf62894bd4a626096ecb91b7634909e7b99d12c7bd3e937cad9db3dbdde63eb3ebb210fea655ee6655ee665dee5bebbf6fd726ea4eb2ecbc5dc0361c1dc7f5e101be3595e09875970c823dbcfc9dd6bd9bbdd489fe11bd273f8c6f4d97797c80c35652657c19e722a7925afb3dd7b25afb359c66519b7753cf2f7526b99fbf4f1c3d8689941bd0692f7dff2d8fb183827bb8d1b613c6b5b6c7654f61a60262865c878fc40f08c182fe381b4fcc790d1f29847bf389521e381b07c0c252c38069ee9179f71ff8239ebbaeede70bf7de7e1306726e491297dd4a7fce84b3daa65ee372a6554c29fcae9c10cc2c0dc3be3751b0ee5bdff326ecbb35cfc31ee8c1e94e5f12b89f133be6b8a012c8f1f488c9f21e3ef0391f157c6fdbf0fa4e565fc7d252d3266601ccddd99258f5a83109e81f1b33c9019b4857751d20276c1f4ca887167b0844466f2f6a28e1bcd2c6bf79bac3d602648bff86779252d1fe3654d31e09fe5813c06d2823fc6b7b4cc78dc82523203db7ef1c7d8f60b4d31e0fe3f1019dff217c77e512203b76022333919c764b931ee13994112c42373e1c10f279871e1c10f26c8b2b5325bb02248de3c2fb9c94fee20983b6b290ebb1f4e9d3a757a32953c9008584f7ee248a693fd5a494ebda79608e8370471caebbe74c36afacd5a1bc4b337e5fd576b4d7941ba6ab21613014d26121110cc26128943a99c524e277bb25dd79dac7b75e7ac77c320d90b5d323d511a82a56fe9b01f8c3663593fdf8f09fa7f5cfda71ae964729f2f2590080882415a06353a95503f3ae1520ae964f2489fd9d377d7f4a76b632a0529913011300811900898b9caedf868b600434821041660082996b0c5b6ddd086bc9d2ce9a3c1a953a76f43e666d8ba926934c2b649f8a394528f888ba771294e4db51d5bfba8771caaf4f6de6f1da5b4faf07ecb4146c6212773bf7de79d77de7178a3efdd7ff6b9efaef41ec529d2d314e9bff73e6e76db708a84b9fffe7bee3b1bb24737af846fec73f8e6f49d4bee9eba648bfd54fd3b1c46ec2a299476b4e328ed28a51c0e9d76de945a8d5e4b3fd432473dafb58d06f76864b79131a97bd22dfd772baa65564829b1512a38e58432e110f5d9ee576eac33e4e4ee1d872eb9c32111978df36cb0b6b3b6b3b63375ddfbaf986cd7ad907ee5bafdecbf9b83cc24cfb32197bee47ddc0d6df070f77eb927dd6f66ee86161371e1b6581710b3f77e431bb2bfd775dd739d0b1712710989b8d09c25394d6cc8f1353bd385127e5c66ba50421429f3b484add6ae7e5bddbacd643a9548a65a6bfd5357bfdfbebefdb6fafeb6be57aad61ac9d56edfd36efb6fabe1564b58ada5533d5592c964aaa6fa5553ddbeae437de9b7d2f7599f31f8972aa5dfd7fde96facc561fdadbb371dedbef4f686362c92bd0f55327dccdd09e72073fdeec6ec75f4f59e7e746d4ef5bd5bbaa111efc32109e3b47767bd9fcd8763bddb7bb5ab9cecd3b4497ae348f7c3a1e7ee4998f3bebb5be9fbcee29bfecf07673b27976e7824379db3fb4ffda62d4ed3a33ed61a4edf3d7bfa7b977aded7fe537fdf4fab0f7f1ff66feadb70dc3e5af7b8a74d7a7faf716afbfeeefda6366c637ffb2fd5c37e383c921df34838061dacc537f5bdbea163c7154714c6e97c7a7f0f872a610ac6e96c7a3f61537b5c73b6db7bced4efdd1ede6c733d5bc3e1ec9f3d7d64bffbad2bbd0ffbdd7fd7c3dd95550709bb91fc751de626e8f5936ee5bebba5effec3da043defbb1bba8f0ffb16e2b8c77c6fa79df1bd9bef63f60f8748f2f79d96bd95b7deafdc385fbcc7d11de963fe4cafbd75ff8d9b6e8f64af7bef7e4fbab6bbf5bf1be24cebeedfb5f19e7bf7ce439cd971b3431c23dc118d366df71b8e641cd9b60b2379ebbcee43237682b6dab7f61dd7ca71ddd3efba8eeb7c6c5f5fcbd666abb6ab5b67ad114b6feafbed8d64fab5bb61ed3ad26ff8c6f41d4ee70ea73bfa1d67bbbab9577777df6c465e69dbac7b6fb9d2f527e138419cb6f4763734923b124d09875ec3c3a11bc1e9dae17487e304b98f951b52c44853e9f20b8f89adb9df36fbfebddb580f31f0face6e9ce738a4f4a39652ccc51be660b544dd4b2ad544fa9453e9e9db6baadefbe783bef5d1fdf6f6b90fdb749bbf7d7fcebbe14603e9bb6f8bb9c7b8eb947bc7a9ee3dd5fdf6f4bf54876db6df30f0cafedf467a4a3111aab2f886f41dbe31bd254255d992b09b4c2a35e5846bc9b92f71d66dc7b96fcfbdc525ce9ddb3a7777f711f7f48efefbbee9ce8c213d2d3dc5de7b2fab0e8b6987bbc7d1248f49367d77bfb7b73ee7e190aab2dda13df82061fb3dd9e2d1c7d1477bd0a71f0e73baeeededde5a4a4dcfddd29bae4d2927bff7756fb570e36ec8ddeeed0d31f0eab88dc31d56e5fa31cc197d05822a808197dbb40f53ec5055fb304507b27c7a92f6035b74d9606cdbf7f37e4f6f276374c2d59a452e8eea8d13ec7048c4fe37730c3a58ef9543cfb1ce2ccb7e07f6190ea54ba884ccd8e98256aaa20100000103160020200c0a078462b1581685a920741f14800d79a23e68529acc634916c4308c53c818640c10101110012019492300405760c1a75e65406bab5607f8b8903e4b9754f369aa78cd6a6526e41aee82ee4e6269bdea235dbe7b9207134899c4e47f8bf0928dd96704bf9a9000260d98d345a495d0d4661fdc45cb75f6b8a3d54c00791ed896121b8c13bd4b943f14158a805f7c4f7975cd69acfedad2aa54bd7e52e36b42852747bf240b29d80b0596a7a3a3e4fa59c1d641d55ca6e43da90b04e084d2f4165ab72e8b15554706e5944332075ce5feee98863afdfb0c136f3ee10dfb1fbd73d3705ccf9f47239dff3d07de5974bbdd74a9227d1216f2ce54d01bf6687c8c1247d2965d98e17ef5373cd7d7cb7c75ffcdbbcac8b57f82531e9e359f40dab190b8b080d3da6d211fa1e78b1cd173e0c2d51157e20d7cff036de747a76150c5aed31d69e215f2fe09a76ca3be96b2dd751e3c97c71137cf81b4c9fc658e6bac342ea79b0356261c3f82334710b24d406e391962bb4c29d96b77234cffd8eb9b3f41b24d07e4985cfe199720b5435c00c917179b49321a9fa3930d67c3c55576caf6f7da19664169361260bccf49d79f0070806b9934da22d36ada9efb2ae824bb428eaa379c45d1ced85d37743859f743026d13d18fcde50fe65752cbb437850cd0b263d3472e9f2680bb1bf3ee17b51b9eb9e598962f7483732d4570d906b8d94e89b8cb8cc29951893463d2bc46fc5d86538b708df1f2125a97fbdcf8cb3f57560388c006fb2e45f0d0c097fd54ea2cf1027763beae953b69a3862e05854ab478dbbe18b5b80d3376e0d83d7e015eb38c62cf9adbf83feeb178f719498909dce41bbc1361ec2a104b47cbfd82884e6ed3cb340906ca8e4a35098d2965466d73d25fbfadfa5130ebb606c20ef254c80a1a638346b572372478ee57fd9b56cd7bb74a43c98a9a92eb9c3072cac6733927dc8651b1174c32838a50dd8fef9a24ab20721a26d95a0c3e12a5f2979aaed63bd5020a37719ecf304ccc060ab302ad1e6700600ac5919c3a52ff0479db42637b9ddd3168b7b4ae31799938cce18f593a0bfdfbb3993edb7cd1d993048c889d2cee39a30d0ce816a7a68953688ee519299e917f99103181b68b93bdd0c0bb5a5c9f48c9eab9a963dbb93c8910c151712fb0cb3c848df91528e14c2f31d5ebed3d52c114efc386f3653cc35f0b6fceeb1bbfde27c15e2823119d0b63f012b5a3450ca67c7329950fb9cf971efec45e2274c691d5de24e9c8c18a9d53efd60709be4d61d757d657b080f1b06644083c21b6c51fa4966ae4d787e488dea4d4dfe7d85dd1c3d1158942e4751d11834f4e16f7d4f0a94b67c723f18808dee5d8479711a57404c450ff002a38a2c0246c50056b490c5dc0221f0294d06d60766ac9f353843d3e2e4cbef2f19292b850c9e3d929a18bfa78aba7682ec2c553faabb18d099643f7f4fc03e0365bf4b6e42f0e98c1abd52318bdc274c2f8bb59e8a26a1af2cb793da68cc884c9ebcdd63dd4f44da0933450ea370a79198d6f3cf8990af1a742c2bc12d1203f904edcf9ca52d39475f337f98fa7441c1d63af6253876c1bc697bc0f03c30c4a89996b98070a02e349a16471f7f228a1696bb4cb7c41dbdd226920c9c3e9ef5e8dc5294b5e99b1c3d35bea4d2c8159151aef21b6efd50cd4c6a2ea949bb7a26314b34a9063c7102ec18bfd65cbc3d04e8ca51c51f4fc00b68abd3457f02c7ebd4e10eac99555c6b72783fc6be383169cd1d5ff6c8beb1370055573c1e5ef3463da04d17f75172df1393b7372708193f9192f62dc76dcbf9207552cd0b2caba0f853c8fecc37048795dabd81d01e27a8f91bcb4ec121d0823222d0f31373464f079ec83852ace8b9187ef234feb6b144d7b4729d42846bce9e6d90823ae763f847a772090e12480ecf0821b927a249705aabc64df3c729fbfab6e7b36d06c42797dd342f5924222ea9055a80fe52dbfca67b5d1ddb99a546f651584ed1e445c00135c97b7e1ab456b8a67ddaf2947022d995f92efbe243a7aa0e2b10a5188d2bd84a0e4d5df06f46ef2a87fc0c7eff1b52e7cebfd77bda55c751dce63481cf93809fd8110e1dcb4888d1b9ca47ea11791463d384b99ac6b43ce5fc0ea8138dc17988da215eed6c0382e203e5524aa2ac6223684d0266656e8d059d0528e544781755d63f76f563c0502de9c154cb4b97a8010725e92546cfbc4c6d2b7abd8015998f72c569e2ba45faec919d4027eed4de6c7c8d4433557edfaaf21741732dfc5ca3346ebc04838dc290bb924e65f095241504b21929a86024dfff090ce9da43796c47aa922aa62b2501def9e2b1c01a01c09493d9cc828a4b5bb8879905fde74c67152b624b8d0c27206caf3924d82946d94db8fae1649279280bb7d186dbf8538f0d5bbc1b636116d10e2908db5ec0ec2533876fc13894c269c1d572280ed49097dbb04595c03e87c598cb34ac81d3be358456825c966cdb1604fd0f0b28cf738646de8da5e9eb7447757a563a2beeb38900b9a3d746a7da3733d19479169d912d2243030e764cf7e8470c1be71493d005377e6c320857010a84571808f9842bb338c30788a25fd139c8be6cb81a0b83e62b4a2cd513e7641025a02601bb01531bf42077769166ceab6bd4f6034764d875e344a904a2e3b56df6b601729a0c7e26510ada085a1682a828b9426c34a9b39bb90008ef19021151f1fa481db56737824cea9c9a85209fbb968a37f5030eb19c0f1dd187d620d93a5e57c416c903c1477f9288870841024f08475783a92f6304c80479047a89d62c5c1593ba173cf429f5ca2603a18c26f0556e1714ace4d18ab9923936e68ade840a246b9fe27d6959778bbfa890abc5e301ed1bb22b41175a8cceb304f18f5461c3f5e531a2993e6b72e0af74ba4066175d59891d82d4149190df1da7a9547ddd8aa0f5979652a2a0f5cd131f73fa63b135f7623ab5dfcdf77f1203e62d8a392d4b0824032c2e03cf1f265b0c36e1abdc604ae4bd621cf8a265bf820c5948f36c2488565f15eee144926af7a6f0d14f1c7e4183cdd0b8293c28905f5a5274ec298f198ecb2ab3f627480f2aad12fbb680fa30b8991ec3d28f642f2d08f0f0173fc0d3fe8ebc49b4a55386291be267333f87b6fbcfc2ba1ed8279a12f279b6d552c8788da077b37688f3fff3a772d1523ec76b870414c341c7603d527018bc2676f6e70a146eee2daff12194798b022f793cc5200cb6922a1c424a074b0086bed5ff092756b633809260341c83c75fc2f196f40675dcbf8f4501730ef381d213508386777d358121709eb826fd9a69fd48ba508f1d0677098d999a4addbf431d05eba19b29495765864c04c2d5e1d0c6f046e38f46d7d41bbb2978408f4fa1fe3186330833f49044fd16fda4049c60411370ccc0b9f981e2c634b51d8a72a1d7d8e1d97a6646e3b66985701855154c5402628dfa72a9527ef3218ff32adf584d934d643a8552ba6dc5a08b83aaeb39554b00a5179a159dd3d66bc48932e2a5b60c2aa7e179b7c1499e595fc77b429ff4de3c9fb879806973f8477d79eba9cc20245a404e25631836268e438d0519b3c5f4b542ec14ba5276c10e58a3e56c254191ad9b58b4e3827d07309f53462085a88fd78d1b6d25a9861b3440b15d63cc91611e07fcaa8c645c4cf7d6ed7bd2ea799578d050344ad9feba4cba28a457c8145be1e8a2d440469815ffb7f84457564ef8041fcdb8d308dca759a18216cdd8558c14ea988e68fe785531102e42a01959133b722ba4469376642772eac1652c749f88bfb15d1c912644853aee90470c44958e24137d6427c1369e9904a8d2ae7830c9171c15be022044a661a773f09b14ce16f72e43e5e78996cef13e2731b48c2ce52414c3fe03df40d7db0c50307912466e7c431b39985b0e5852887fac2b317096d3893917b585dca0e2e44bd8eb9d423626ab1a0e4633d89f928aca9395b59b522a7053504d05f48774561028650fe3e0d6d1b6c5f6b5748d52a16956ee4740fced6dc58284d31a8e9b64decfe27a938172fc7c5a19191115248ec2569b11643de148515364194c2bf03d21d63212b7687d0941011c33d0b85219e671b5e3232d591296587e3509a626380f74811b8c7f0a41e08db44b884507a0ac0fddab5ba34e2031426a463960a6db504cb1ca5e8ee19efad621b62a2914a0415100fbc7ec13a0fec97e13d85cd805c6e8c2a607153c9cfcd050aacea4cfbcb7bbceda8934e52017a0e3733e4da238892718578d9f0c4b9d6fe6e8c70695181dba7bb292a38bee99fd5a5d2de270281480aa597ff5b426cc14fae509225dd6d7f3986f8178bf788079ca1aa822b31967b6d78185ee7edcec36a9101380221faaffc7561783da6ddf4d3055092216fd3ea5811c0127bdb449498e2c64ea4f01e324d5a4a0dc2f92fd92e1705e8de6fa1d53aaf387cdfc83bd35b04188211b5cef012ac42415ff39c4bab5a40cab006dc026007f93441156e5a040c01007b8877ea0dd07c14303aa42a9bb10ef5cb21470aef8eb73000b87da01b1c84d1e541fef7470841c21a59152a47801cc7cdf6a79fa39932ac07fe9c2c74e7891e42f8467255eaef57c25745fbb201ea4c75fb85ffb6f76448a040dd348d6ecbd0bb01b27f4bda2b25142704079a9c92e78c1382486fe2af0d82394efdb40a41e7537fac87b161bf5113906d93ff75e87f881524798f1ea72bdbb091009710c41ae51f1682e7ba6b8ec91602da40b6742e4b46c08ff989459b1eba5654acd7fa937cfb9a3b2233f7187a73c86743ce417b8b7955c6ce8468ce79dc9cf661da474c778d2a12c049cc6c1b80a9dc911bb8c5c610ca0ceb979c850c4c17bb86011fd79bb3ba9e2328cc73721e1fe826ec2420c1fa23d3d925b8f87e352df303106f08b5903753434cd7f90a44baa5a3306a73128f6b8d5f61601b6041abb2e7139000565fcf431ae608780b02ffd841c9cd43e6461200722493d96153b2d0bda15b7a23b8bb820ce6e63a2ec98d8c458160ec730b253dabdc6538354245240adcde762b40baf396ab1651da451208151dade01f236a650afe14560e265d0542c85f3449d08b6dc8a2148e9a94e2826104cb4cc83ee2cb607a6135ff4e280706d4ef1c04dba2e2f4a811e1f2630d31b739e91d57de529558508be1253f93799fb9b2b3aaf46fb816eaf0ccbf59d70d31e64a9504c8408c7b23cb11550f43daeac96854975a96e6fd268df29398414c53714e7324201689dc5a25e0c33a43804d8ad9938bbc803037cb91b16c2d9ffe43e713f3df782e17e98a6b616879e4a5afef025441893dac9c95fd14f871cec5e4a57f5d814d8285a052889e23eb8bf0f818ed177a29cbe70d699a4f0628c86dd5b3b4ac82c3ce314080e333a2d1269816a077f9a8a129679a26e94e0b88836a8c6b8dcf5f92fc9d013717dabe2308f42776004139711782afc112106e2b1ae39724266d05d851e1c77a75febe6ef489d7a9006d858f7e3488f40dce4a9032673d61c941f39738427fe8039a873ac0622b2c53564d7e3b075602c00b200b96ddac1f2bb280d59c9bc11c3b300194e091c0cf93c31a8a094faf5e456624f5504c90d8612187fb2a693e6f28463172e759585fb7381493a785b78f872c6acf2c135f75641a92aaff1513d99e388015f30ccc2b4e8ef06c4c51e58e0cabac58448b58f72fd72fee8dd3eba5dead0a74786f572e26e281fb3de162bef2fe60c19093802da7984ec00fbd36b698479a0a768681208be00e9c5adba51b05c78b826e7645bbb17977b7f8749aeec266c832f59ac4be77146698b8f740e782055e6337be26186a37d8aed69c96339399fe4de90fe0fb5daab9bcdb62e516132eb8e305c3f5d1f4c7748e67a8282ff2646c4e4cc33cd9626118b81989164ff64f9a4cd1d62b295d99d75ec661c38e5fec26daa78345cff6b55f4266b6348fae940411d3a67f053064a631fcebf1c9ecca6ddaf4e302b372a4596ffa78ec92bbd53d10f70bfc465c8ce34f87ec1f85776b7ce5c77f7eb9c80a134a08629f511021da839546ac90d3ce6722fda4f4e837c0297bc76ae98856ab95b1c8be542634da04ae355707c6a9b41fd26790c4e26ffa97b9d2e4f927a99fc18ed81f2cb14fde921614980c00332c6972056cf223bd46e0b58bc89090b6610798d312e2fbf79c5a10a19378c15c0fb4804ebf9596c80a10aef8ee24b7055ccfc781049f6ecae385ae42c5771d10f4d003709dfe4e4f0a43d59511e0f86901347b270915e09dd51223c221eee8ba01d0be23d5033a4ec7c0cf733c6fd724b059774be2a7301e0f2276ca5c604767d804c14f0828e2045090d8ba38362ab218b9cb80ef50c508104e3b5e723c38c4dae08cbe603207f894fa63123fad713fc8b002d7332d442e4951c3c01924322f2c80d9ab5872e326f341d7012f781b7963eb1cc67a3b4260a30d38d2aee9b65b7475b801469cf951c5edf79408f61ba03a097c909a0cd06e029bbb26e433387da9e0b155bdeea55b7c6b2a29b46adc84ca2eaeb3ad91c9bd4108404d44de4202f34a5251c529b5c45a5b464e5eb96e4aacfae9b9f80555a3d93adaede0568a905f0455d1df6e04bfb9b0fca7d3c36cb504bd311a8dab59ca9ea1ff54c1c623da21dc0742945de7e9b6c59f835eec50d59986580b710a3a5b6246b7b5c858aac419e551b787ff07965315d08d0d142c19dd3688b5b487e6fb664c84a8c4d46fa05e9aa79ebe893df2672fed1ee53e7231e0cb4ee5a91f685d1960715c0544527342967e06a1439a4f2c7930a1e1667673183d1cb3486236480340c6423cad0680afb38c848c4ec7c0e99db76c2d67c7da9ad38f492424c88341e45ce13024d9bcce6c03fff8ed10d2835f298b13c870f82069f7630513b0804210c63e7ff5d18942a85bafb7339ca3037a3013a71e98ec72d3edfa7c97428ca5a8f34348e307abe50d09d55c679e3607dbfafc8bd601ebde2c1056c461779f8806cba172220f331807712d00f133f024abcccd06477701508c1ca2fca9f980b6823c3815075f520b6e58f1e050edca798bec4a13c5182a4b0bfb66b435b72ff608e238cc25ab0e07c007e811443fe5499789992883cd4ba7cf838f04f863ea66ac8f07042dcc081f205df4841c31c2cb3f9b47d617d3615cc06072e00eccbcb055577527e7d36f25403096e60d7673450f924a71a6bdab4eaa222a18dd7c0dbef054c082f9d9c2c039a43d449da1ea70f9046b3f12e0892abb6dbe77ad239660b88c95ecd6779db4172abbc5c9440fcc863c54889aa80d0ea75e9e533f40b8faf71cf1cc265cb8e5d25de54e3fd47b9178a77fd949ce7b665c02a7617221acb41b903dff88f1f26a9114a4f54dc66cc3651f36859e8aa971409e49041c2d16f3ce76379b09b709754ffe69dbe5653f35bf515b0feed52e46f5f34e8f27e70268ebffbffadb55f46645e7b7d407685cac116c146a80a8079a73e592b672740a1e0542f1279586ad149efb3c74b9bea20a8affab6ee260b4a9df824cf63e5c53f41eb2079691b12ecac1e17a00a8bfb34c64751f456d94b4b977e7fce578cb55519a6a28aabc366d8e4dd5cc9ad0ce3e4c3218080b2a1201887d12ddb1b0d1a572f2246e2f52d2a674d4c783e1f5f059c41a9f57f69f83467bd86f46af2d6704fc31c2baa6e75a93042e49750e88470088ab2d1b38a48ad18cf598ac09895a8b8ccebc9ec89a107e6670471b60b681adce6f31a2c868b1f78ca181ddee20357a4e31802b75dd01aeb72e239a8dd6ee6b9dd20ab2594ae211984e6a00fbf4d3e88c6f6e1f94997e9df69a115335646a01a5597f9edbb220d3731bc48cb55a4994482494decd5161c3ab562dea4791454d96feeabc8b92911e7a8609cc6f8dd2ae8546a424626d5d78ed561e32282300f9f9e337c166446a6ffd6ff5d8690d2eaafc07928a2c28aa3fbdd394be4e7e560a66307ea8baa56060e2e0971c583a97c083049e2856143d0a3a9afcd7bfad75a12a3486281b4693f778a187acaa2df992ba5e456f54d42f91b4ae699208f521a2015bde0cc562e236e071f70d06c89ad39c06a094e449ee799a6e6d8f7d9f5fbd4c40975781292ab2cf8f398d70176a99782acadb10dd40401dc10097b5c9595e17e91daf667e463461f220df470fb18f143b7f658813329dffb20db68dc2b52e30e13d9428403b2acaa60d58dca0b1b41361ba62ee669a23a1708d411e3673018da2e2d638e925c9f45b5838713c385428bd73fc3569ccb821fb047c942d6347df7e7c916b6b20ba8708acfe3baf5b6d27e6dcfc1f62cc462b4db53ce5c5f0a495f89a5f9338773dad09dc4eb292d305bd3d636bf4d6b53e81c220fecd301496537556ed3e191931f4b705721d73909fe674bded724bff659d8708daf2a671ac4b8abce6002f5ce8066b49f8d61d9c5b9174ae0608933f9c0c5cd890820e70498aafe81702c4e1b9a57991c82eb47789a797cd9d43d19ff873f9bd67e31ad35178f1d22a25e3725eddaea6739f25082892d0bfbc35c28857ec1a96b71e2a40828044e81fdd7b91282e2c1f16d264ac300b87c3893b923b3ac56cf01fb36d5f68987f2e3505a24a3fa929c0499d5cace0278ddd807c51b855e3f26c29de58cbcee9ee8b00a21fe255fa023e578e5c2699cd94b7dbef28af4b1c8946312a661e1650025a7efcde04f7eb0b5a543c14aeb7aca9a9cad633553b05081425304e1a931cc14b5e34763b809d4a52df1cc6eee1598354791e894cafc42e76dcf279c91d4931d6a9814f7094a5e6bd5080442c24deb76fb50acf1b568b4c69ab1b959a8b42ecaf5cc8a7cc74c37762ba71096c0793e1a4201c91c66120083c45a8b5cd5b8175bcdc2e932d1828c9a70ab899f6cb38d6a958955e22d0be1e34edbda9b08ef5400a49bde54743938a688d7c26175faaa4f84995c95e063a4300b076faf618b6e6bf07838fd305e83a7f02add93fabf6005079b090944e05e14310ab2cedd8d2af726e3dcf408b5c25b6c80c658498fcf0b9db82c4325e4181c2179acc4e85c75726cb093b4c4514a693b606848b5ec20f07db1fdae1ba818b1b0a6d70d1fe6f1165a5849a79154243ecf22eb4890a65c10fdad6570b9614a5e033225154337771808aabfa368de88c95b4574c9e8128fa3a82297c9d5989c62be37aa45ad08b5c2b84cc24358f56fe3313a5e539f49d3377be96ad92eb298db52cd84e7ec6c8e70ace8392fc96f3bcc127adc39145b12c27bdc3b996d90f87e6fd24619ca6bdbf0f2a8feba940bb963733763b6ce220bc7484d295f51ad772090566f40c923177574eb565d24a61bc19b02975b3824bc76ab9e25eb56c7280bf1adea882ce1d8142497831470cac04dbc4ee3b1c2d08a52c4240dc078b407b2578b01242ebe06c5de14cf803110c6b84170b42b865f0130829a647c211fb4f3f7d661eb2b1d5379c9fb638a3d63ec6fe2b67155ea1d6b4b080787dddbe172fea9b2935847cfbc1dea83ba84aa1e07eb36184e7a96047e74846293e00920c492c43aa0dc0312e6569df70c12880ba7ae38da4a285d6d31fef2e0be3c0965ba90a8a63877c78e2695dd83390bfa4ed9d9f9ead293102eb5f74efaf962fef1e4169c845473266a6dfbffa4d7586ab3130d5d8c39d2cd521b7497d574c7d3da15e52013af435c1d838e094ac4ecd30aab9699ec7a9b5124db87aa0504d1a28f389c24f56490c7e71dec9ed345a25355efdf2ee53e9c8e89ac379571c124a06d903a1df626dabccb2e61da7888842ddca8360c7440275d253a8ee67f2ed3fec42e3659efd3a37b45a7687aa91eb62c542cc0683c4d30ffc1d7d6a9661c8ec5a6951f8fb623fd00ed8b9ee30b9cd5f1c3422e6625b35f092566ffbea3fc942c5535baa4c0aca0645f7035f18d89017562faaeca24bb1039ffff6ba960f17084fae298692ad72ec3a1e955d2613efc62e70de4eb6029b360daf6206145e987572a86f07f21d6a661ee15c2a2df234f3fecbc74c7b475c977ad0eb389c24b588d6273df71aefc3e1a8f01b2907f0ff94c3fb5e24c8fb0203240ace2fe64e5e15ce020473f8a6680524f7ff5b0de36f4644870d03eae14daa13d3645e87c7ede5020f6b118ef78221b09e09ee079bdc04c8ad03a867955053254e1bea64ed732abb146ce9c902e547bcd26d0447bad2ef4a0bbe2aa50f5a10a3f67553f0ba29b09face8756d09048097301a8e06212ec462629ca0450011a584d5eb2ea9875da0de8194b4c0014a635af516e596dbb5c816c7f46e21f601baa1f852c96ee263e88529f616909db310d559a240dd565d962a225d652dca9f83f48473107749c41a376899ddc66e257940477db488d15e9330bfbd7ccb3ac088790d7d4ece94de090652e0f31974ee46c436e476580092ea34bb63443715aae71b89202b35aaea372d7846613eec5b304fa03e4a8d4de2b7f90222298383172833bdb7540893b788f4857b695f5527157277627610734f75b7d1b31ce90074c20140e7499467438120a6dd653404d988f3b0b702ae448089b655be805cbe8f5e47e0a254de51374126bf991178394d8896149de08bee05746463ec98038cd4462fc0a7e8390fd8979951f82e53a35d3c65d684156ec364dbebc356cad299c3d89fb537f05b7dc2398844ce6763178814d84d48c3f6b88da9f3daa8dbb31d9bdafad77549a0a5968ec5894727caa6f194cd26fa2a8d61d8e9ac5274000d5650512215b1a4861bf740b4d92c646901ec7642ecfacc0220a82ffece4b4abb218d66597daedc72905c7aa3c12b6b6fc7cfa37c22b1ff31e8729e6b618cdf1a83f1060b26b238f16c6e84a1e60e266abfcbc3e122103b7040b9270214a5a7c29e3b586a28f1d7443f3b5c91664556c982ee982066182f98f2e43ebf99a9dde64180569087a2023317fe2ab06d5e3f631100ee344e158e2788673c978033f4172f956dc716072b7405fc2facb0f6428d75fe588cacf418298bf159b0fb803a1a254d19a8885e52bede96f29d78a816dbdf1be24c4d9b2288bcf6ababd0d9df86486b83de6032e52a944e64c493a3ba3c653f8a9539764b1bb0f787b0ee31bbfc3130f7f977e2867073779ea14490297ef7237e24a2c25f1bd006b12a04cbafd8769c71dbb64d501a6e5bc4806c5d07150da4fe61d68034730dd8a05c47bcb8860238942827d9d54ef083a2b406740f59be5648e229d37baae03b7c2456a9cbb5f1d4ede703e12537cc6f51e5bbb478b1a2c10426d6af225d8e2356eefac526b1f760416fadba7f92aefb8018defaddea8eae968254d9927baf087d3db9eb6b911359ea4ed4a701218e3f476a235468e8883253b05cceb4a923cbdce35606316798314083d1215e060fb7cbfc4e88f815e7f39e61ef476a469e7047ee6b39de100b83bd69a7ea98c8757b029b34a0632181304fa50cade7990b62864a18382517bb11a9af4c4bba0120c4700bae15051400004281debbb4d09c8bcf39795ff8b41376a7cf06e89da3a262a3c3d84304ab7866c679d10c5cac97a86c2c232e064df89ff00e846c28b077d775483569e050ea7a8b48182bc73769fbf52bb7af6615f20b2fde2e74e61cbc33ead473eb47ca0a0d7398ce6c54bd10c1d822c94280a0bbe58f1aa3c2996ec09ccecd9bfebd16be130243e7598d5288cc8d7b5b77348dc94138433c6c36b289001470548bed68f8fa0dc7a4f03206ae64d1b1ce24423a6a6a4dd73da94c8a3eb0de493464c9746ef8133a1078179535666cb7a52f3c347c880ed55539bff157debdf1d152a902e6a43c60d0bc9a23603d5b43e050db5616aaaa2ff24c88ba16100a4ed7efad9aaca3fe385bdd31776e66e97e255f047a6051e243cf15a2ee81e46aad9637aa014caad61e4aa26b7cab66f5d7f8346a5e90b7b982ca94144d96b1e7153b040e53ef6abc71a30e749a2c338af64e4c4089389640bb1b6a5f795e0322f86f17d96255c1ee671f67453875b4f650fa922265eb16cef72a947a20d325c34d0d19670a3c801b63b8f06f49298da17f666fe833c285173f294f2d547a5fec8a856d0d5339c2c4ce7c01e6fdb11c33f2abf7d617840a871e9320df81682fd27c41cb463d823ceb0088f949e25568fa0498527596e01075a9c294195d695fbae2755046ee5bd91f1c200b04159a3ece2efa73dd79567de02b3cb281ef91f159ef934f5187162c3a10e19a394e62d1ae3e46974c76dfcaa880b017360a1bcb9faf6c08f73342a0700c2d6ad6ad071ac808146e622877e73a2e1a225c83516584489b012331395dcdd3206478e59aa553d1b3508e56cbc66c5a5f46166bbbce1315ce1d4fb32afbf9363dd0ac9b5d413abd5b127bc4d325e38398a189045efe53708ff35a703f60f08f73927f802762e2c1ac512db1f5acf9d68a27757b585f87f50c98b7f237cedbbf9d03b1eed7d5f914a016a981d33d7204c9af2b2d8d306420dc4ca04f96e5be9c2636f5f9a85703fe6f4adb42921744cc93e60cf1a1753e538342b020c49d85bd3afe4c2909163948f382b37d36feec7110f15aa26076a893041eb22dcb31656f638941d400a48670c4a16b49c89cfb76f402214c978e73a6fb2d5f03cc6c937d19545825bba0b7204ea730833557993ec87aaeda443ff6a14b61d6f7f773c6a1371134ebc1925aa9a208c7867a5bfb9081b4234366899019acafe1647f05e18165f1654acacf3425838ccfc9faee4ce3bb97d2b8acbc9a604084ee14baf7808364d4427524df9c2057e6dcf2ecddcc1848cf5c71e39a6841c9a675ff8f32feae7b09ed342df2f46ebb18678e4d742f2d63ed48bafebf92fecd20238793bf6caad08bfaee7e66ac5c208267729f1201f76ed3cfbbfb3f106c82feeaeb9c905f960827499f851d83a17824c500d1c256d0740a4dc47ba41b525f4a340d991a11558c0160077a725f9d532eec5b50fd5ef3cb5c768586e9f42aba2d83309bbb91760bc0d89da8575af011cdf262d02a0d7b4e26d916ee2cd105a81a20d1cb8f5827ef4e2936ef45e5963a5bf86bb5f333ba7c5f003040ed3809319b52293415111c3949c8244d7063f986d1c399cbd9790ede1567a7cf27503c677d15266322cad1a6ceb61d1dcac33d7da90c36f81a5debc4540c61801b34bfdb1af505148c37cac65ebf7b92080f1b8698efcea1d9b8e215c39968d713743a88a1dc9f616c2574352895d50ab52f8972eab35d21fca110e3f52c43bb1d024db288dd75a8931bd688320bed130b8d31198368f0e8d2d2145b45c4980bb6f2eae2df5eec6e01276f6269e7c94978c3e71537c586b90abdbca281a9ba4053faec7819f89b40ac880cb010d72b317e22c735cc88bd24e001d150d451eb77ffa5d4fd91322d4cbe7426971baa2cf417100455fc88f95e5939ab71cbdf26adb1d13d385ee8a9746ce8d7e1762d1a920b78b330c117fb8a6efa327423d327c5a0fabc715d262d855158d92306dba45348496721c868efe9cc6f32e064ed12d401f751acd863085d70443365ebfa8b9fc7634da656857de1401411a20d9c6b5f99b6ef3b882c1ef45609e492261eb0ae54947c2619e2774fac151eadc1af845c0c6a22154a092d018fa325e845384c6614f8ea66420be00aab30d626c81fe85d916720ab8b1823531a6cbec5d0b5b5d1c29d87b0d1cee85a0eba8d98f0924c8b5666cd962dce234817d4418b8ee7525107bb78119624e4ff84d02e4b9547544c81f9ededa75996e366562abccb5670f313966acc48d551fec41d093e0039b479004b76176206a2c921e61e01612d7fa12e6aa48f84b8f4464d8ad42577b17d3a43dcd273fa4d1da242e6028d90ddf79b6822f9aba501264e6e4132076f5f9e3245b7934e303daf40f16e649791045a23a2dea75e188bfe62223cbed9f6f4485bc0cadeebe913513fcee98ad27dd994097a1539d19181f39be402e8df3ce5b81130440b297177bf0180250b164e4a872d576754c37f8f340b9b5a2d8c21fade3daffeb045845732e867d4636403405452c6f81bf20a2c4b79cdcbccb47aad4cff4d3c0666705bea4f1511f6f1c47eb03f51cd6dce3abdd63d68ff4bb4306df9e35779f344075a8f2936c88d193d58bd619f6f8a15e4c68c1e54cf796c971b7e935fd58d993e68bd536c961b952adf1c2be81b139d8adebfd3af7a734e07452f4dfa6eb180de5eefa5a267a06fe83b86977eb35f71cd6f7a13bab0f21a0aeffc01bb3583ba03003a771ec9b368e28c4f03386563af0b5536ea2ed0c7de39c9b63b93d2f5f750f538e2b5dd26d293bc276825e19e74144b01c6b308c5510e575782f140c12e0c1ca86cd719d9af0806d373fba7dc049cc6548aa7123814e7361eaab930609ea69ceb070cc0c7126112c7165753f7ac61f54ba150da9d0789d6540d04a55581d4c352c0ef0eac4b07bcb46c6fc170653ca9633130fd6f411564d59e06cb3050fb483eae3dc27caa7203707beb1c7122d694fd9bd5798c90201ffbf9ea543abeeb2e30b70e41a018f374d692f663d218c97bdab3baa7aa76a4f2affae830170f18bf802ec6800f13a41c0d13dc3d368a5a5f20541a8707cee62c169886bc73f8a0124e488440b00f7f0ee03c4ff335e228b6c06b665b63a419f3a72240b14eb5318228923beeb6618424eb20d81bf6c77428a95b63e38e527593b96ecbc238d57352e5d07f4063c30fcc85bb65f60580a73e01fefd85e8c00bf33b8f8c6cbf984766ecca3c493ce1cfd2efe7ebd6c3f2588a08522c48d6037c021713aa880d7ffd302bd8e6261fee75e6004a4361007b55eeb3ba8d6678dee2fc9dd0c61b3547cc33dadbcecba6f78c4d972ba9be8cb7cea3cd6878939b0d9f70cb58b855f27a32d5649fdbf0279bf48f7cb5659b0bfae822a4485c693fa2887b25d42563dd2a831d6a01f22bc858156630be840a2cf792e15086982aa86adcdc0f8c4ae0c181c3224b7b50fd6f87888f4a5e326d0433386773536d8bfce21c87149d91896b4975fe2357415498c353ca263fed9ad6fc1b8a43cb89ea10092767b7e57fd303a3f1c7f6a944f564529fccb83502c01846dd8aa8c6a60da62728d9a0faa3e365bd82d2bd9c812cdc910c8a0a51748d0427b36de5c261bbf1316bb4a981c0413b1ea5b281a2b4737da003091004f1ed1239e2ebac17445e40584078043b6a0781665637b707a47a6c6610e55501260182f374110aede099dd12b340d79fad918009f5001ccbaf1c7b6aa4dd05f3c290c8247337dbbfa92109778d851ba5a82763dd2acffd7d2b6f972f823c66d63c7da6af0fedca244d9777ce2f550e9c22ae3e496ae2a42991980e8c07091aeea791df2f9d72dedf3c568f0d156d0bc9a8a7f92c638160b1aa0fac018eaf9fd15a534eadc4e6681e960eb3356848568fe5d787e4c4748c83adbf1ed69b723bbfbe3f19a91fa11c68305a8350307627b1e23d66e7a4e79f4a575f265d8f4c354958fea6da0348048a815d8a1bddedc8504b56ea234c4d633721f0b3cf72bada25532eed18f3e1379c2814b906a55c68e23213db54ef8154ff5a3a7c6c94a56249cfe03040893f39e5491e5bd1bfea20e1ad2eaffb73e0b3091120e9913338618f93e6ef2182e9f42e451d43a085683853c25917cc539d52776a5a224cd873782d794f921ad20c64ee2b9496cd443a05809b2c0fa6e24a983e74219cf1b3773f48e72835ae1acc00de47da586e2738c7e1882aec4e415e77560668d9d1e619a8291123effe0bee6b5aff05a0ed7cf61dcf5fdfa9c8341387bad5ab88714ef3140aaa416646084e3ecc4cd5a8a02dde9aa6adcf23f0da912a6e56f8996cef7e9bf3152c142835987aef421458f2ee6fbfadccf0861903db5c7c372a52c86c964721e2ea1899e91c83fe27f0f7586210f78ef0037dee70f72a1c9e604e712e34ebbb4aa504e3ba5141f1036f77ec4a6b7c136bf28af0d7cdd947207c7d563285f573523cc0511f9d6b096bb602957589f9dc8ed8264e68ca20340652fee6fa3487c98f5b44a7c5d1991c06203b6c90fab30bad414dcaaa6ecdb35a5af1d4cabe400834692240b45a0c85eeec8ffe0e7071c5dbe13424cd4bc8ba0777ccadbfaf4b26e5414ddc21b82f7cf3da2eb2fe5ca384e152800785f0dba4dbce0824b06f43c109730a20456c8db01ffaaae2091af81b1ec8ddd95af26747791c8fefe0ed2a0fd1d4defd29a98fc8324f65699c9b1bb51acd05543bc55134519ddd66ceaf77f43644a4fa0a5bbe64070735b658dad268d0ba5efc6927f9e098db68bcea94f1f456b4f087a5087e0b6302062ed980ce00a602194da1b5c830280dcddd9726c9c56aa6682b87444682dadef4bc5f4f9f8608c6e3f8e7ca44c2b921443eea6704b6e88165becdb150947c219646e76c6c167d2a1a190210b741d790bf426ee30b7e8b919f91e33b2ebd9418bc9136418649f7b41afd02a01e0179bb8a53e79d84a6c81f9a98e5672ab63f015a04b60315f7732f0ab60c6b01a35090418bb0ad13ff701fe4c41e3b3b3543364250b30c83ff9655f9c25a083dca079ce5486c9073a8b235faa96f4682d3ecbabe9025ffd82aadad1c1caed5c0df094d70a7b511652ced3f729b3d3944d3c341fa8d29b7be476b84a3d1ac66a92ee79cce469464baaf1889d58fb765ac54e020f8fb34682b92e9034b43fb85160a8cdf522c2a7170da3e7059f03265c269d980c74d593d548d042ec904bc53a3abc71d2caa9e089ff0b517d7ce11f6927c044ce8f7369802d1cae9137886147e0842599648d7ec8bfc461f6783dbae5dfe8fc2cb250d68fd9fce081813c79a37b1414895c459b28e8a1e057234a7b7615434e82e6c1e71f1d43ce350dd91e9d1b9c2d6546e7db11848ea941b44859e9fc7f4256ff708bd80d6f481c498df58794b228eb2c2dc939419692ef4ca92788b7e936129e4b011bfc6a3ba35a3ae7b57460addd408a432827fc4b576f5a818a7f720ee587b324f59e78052287aaa0a432afc72010494670301876589700728a5860237ffac34a4411c4b31828a0a83b409227e01b504fd50a41e680d689aac73e4c914339a07dcef2101fb4f2eb6b7f7ff86e1262c3982371ec6780f4167ccc62f6685b3be66ab87253de0484c90183d769c60344d452c510d653bd1831b9d4d9234fea688ee262e6c37ec5d1f6146e8c3becee841f54c1ddb80d3ee0c6a1e6e341c05dc185b8bfe28f5f53de0582f6b601da68a7a0da1a1c1834f0757b9d4bec8e4df3d9a87e5b57e03ed5985b01c583f6f3193b3a5ed43078a5750613fb3410936983b387644f7512936987738cd9a390d62580b0958d442f47520a49bbb1c6282094c68deedc8ed07375e8b5cd64176b5978b057ad3dc74834bd60763a9ecd9a579c34f74d10b90b459011a4ad623b33a93d8348ccc9eeaba1a92abab53ef7b7f4a0277379b579a656ff994a221df34dac4a0ecd367433d8c7286202a97fbcd23642215f50ac50de9ca2b637e2eb571e09f090e94dc75a8c750f7386c1293d5047798b4fb5a8a0b8c5358bcf14b2a1f657d07ae1125a92f63d747d5d68790c324fe71052eb3dda03d952514f135aa0feba7a4071d2b6615ac7bacc1232e9fb1bb21f045747a973296486f44635f083fe612af13edd514ef08d75abf80e2e3f31104c906f06c440f1f74fb096c2ea9d1fe186f415fc885b084c18b476d5ba70fdef549bb9f20f769839522b04a02c16b895090ed08f8ce977ed008d271bb52696e134141f62e9b1afc440ade304e19e26b2cc9945a92c5bef0229f5043d65053f491a1afbc5a86a4f5ea4611429c9c57a96572a3ba5f0a5912494e112d4ba95889be5e567d7a8b8c0e135adfc23b5409ea22aff04973a702b46c86ea733a824752838792ea0e7f9eeda375e439a8fdbb33de8935da62e40951d42b3e3c13c49837025c9d2adc3b9b4ee717c6ed67b44695529f8ceb6519f56adbfd484e25de812dc494a3b70f3def9043a50b02f3f857b57e2f8e8bdf1e4d93f707de17c109e09936d144f4b6159d2191e4a52fc9f050afeef0b8e42aff3e95a7b19879c6b7c27eb51fc1cf6f4bc2890f84209266d21fa76f1403495d3d108acd29cb4f56f78c851b8b3975cc896ca518ca7b908086a99b4738f047f1ee01bba97220aabc3648c4c5aae46905900892bf8a33ef61d010f5cd87c2a527aa40b71263eba965dbfe2bb985bbdc4bcfad42739619031a5da332c568478cebe30c62d3140603da2c4d22926ad7d448201618ca676e21c4f60bd9013609e515f7bff885e030f1a4b2ca346754729c62e8d3998aba6f638a350c62aebbafb30fabf5821e6a3c8a690ee0a7a0f8791256c4d5f431a684a1a59f292cb01cdc95b7c2c07f888f0efa3806b498bbb64c170a5057e00d38d3695b7080dc405c5eaf8f68ae2666447af30a6cd482664d3c182f885d8f18619737439c5e96593f269e50e2157cbec8b459e861b72bd4b63deb2b7b8fce0eb6f5c1c2c192acd58c166588c28d27c2d2a72c5c8e7cd87e4319022fd5cb1dabf868bec6640b1fb00659e227845d6d743cd511d93a336681b5013ff4f14451bb3986c2e422da8767bc4319c0a2427f22b6b1bfdb6e16931ffd012474a9ccc7112b76305d856180044a213e7840fa1eb4824ad7c3f68e17df9ab3bd8c33dba08b9689fbbecfeee38ac0b54e8bb7ab035ae14178d3061ff49b0fb24d0214fdb582342c08042db0e20a563353f618094005a4fcdf4a4e49fb1e3b217a4764becd2f7a2d8392907298cd33938318d35529577801725f660c0a88beb06dfd2adf471c9349d4df1fa9c16ce51595038794fa7511d1378662aa95df405173fd4c6c32d2e397fdb8bab2d7d2aacc6f4750ef786b6a8f1b41ae10671c464516b8167d1ce3992ac8a7828546a38517c01014c98e0db637a9c581675f4ed835d766e5fa12542c6c6fe7da3d2db436cd7d53c9d3735c7be313957e2924760b4a29feca253582e1e78705a3d7098e56aac78b47b896fd9083c64bb736b6716fa6b4906e650551b531b6247be7b50d152a59a79a56bdb7eea876123978f26d8e18870e665c8af5eeae32293193d2dae6b4765a7383210cee84576e6f6965827bd445e8768d96d8dc72fd9e48ee23c3bfe4c856258ca6356ef1ee8ed0102aaf7191001b13a4de583c7c84d110364afe28aa4993d0fee8c5545adf288771e4ca263b9aaa24eafac76eeb501c6c95c73e9383dae7cf6ba86b51989fbc3ec0192083c7ceb5927b59076a3d62ce661fb1fb3f0b1ae6150a15fada5ac2759c2b8a09d3a08385b236c7d188f87c6aab59d46ee4243f15c422bd740ef08e45f6b32fbbcb62ff6a410d5bf2dd6ce47f73da6794c38c73394890cdf5feb58b3d18f255336477f870ab659096c35ecb7d4d576682cc1937c2b20f8eac0309d497d4f7af519294ec01c40e5095658c3d72ad69f69f3cc5d551e4b97ef8d707d8b411dca793a1cf8ec80da172f5ea4b70c4eacf3c4c9d5e6997a29c776a04a0a20929290a3aa0da98eb69a5f6e939b32c557fe1422c8142d93e0d2707ec4d0647e17b5b9f41c944c9c34de5c35133eb3e734d2359558c633c819bca284091cf5cc1783687d6e9aa5a148d0200b7dca5308e4b0fa4abba9f35984060d65db23061ae9061ddf52ed16d97af437d2811a323905085db0d6fc949a7163a2dd03a5c076fbe616d86847edfe4fa3b8cf39d61875daa1b794f9b88f9a03afbfe8391907566fd2e20c79bd03a9ec956dbdddc263a893d9ea39f65a79a803c4a14084ed281e49941cfb215fe9743b4a13b92f2d4b701cee43b770a8710ab6883c0b3166c736291c050bbf70d0280188e0c0dc6189aaff09987b3be3ce8181c0de2ee164b3751496c83b04c3cf654b008a2f66dc4351975d607395a0c98ed6ac9c2d6a5105f4d490bd711ebe4202e84249130f88512d673e049ec025390ceeb443a160d90a12e548ab693df150813dbbb60c3888857af233d687a35692c4d7aea0a4b78352f5b98c3865abf28268d89bd62ddcea7786aeeca31edd6688df26373a140980ff817088f4e05443747a40a9bebafb99c9e7dd2dedda2a89148f95ad376d9ed3f1ec6e329680f7881574ed71d3a20944a4b2fb1b95b443d2402afa1eb4e7f0f85c6c371a4856be53c77331df4c84571949a5296d77bb106b4eee9ef8c4abcf5961f8f67a396bba5f0d98931c76596aefe379e69f61a706ba4132ec2c89370cc7f12366c2ecd5d62f1b6af16de01333c02d0b84738cbdd26c9393de5106acbc02330ad033619d8a5fe060412d648c8e9312fc25b9338272943ac1a524e8ce697a9a61a2b1a4c2aff66c693652f3d81c70783999784c9a2bac2bd0c3017e4e3e74c41cc019e36fd18ae750b92177e70a62fe239d46712c5f384a281902cc18a7c06c5aca3fc144ecf893651f7d8eeb9dc6214982b5170f1fc26f21363baeaed873490bda3b031e59599e03374e33a3dcb14f29d6fd9253fff92280d6a886f954ae501e6f5f6827831c96e2d04dd8fda042c1b8a47e145e8627c79bdc04e567d32b64725345179e52796eb00dd79981e583922a109e005e20b1f9580afff01a9862c4ce5c9b8e776cd22ad4fa659e58a0038a61648f35512383a2cf0a1f07babb039d832e1ac25efcdfeb10ceb602efff594c74bc63fa6652edbe0944186e32164e003e65e171bfcb4f3244b0c61d43fd8030a781ea77e91715e52ad46bb7b12de2be5cc38f114df0eac9a88dfe18cacd3aa968141ab7c648380703545ca9bb0e3400c9820f4e0f7b57b00487fd0447f18435a9a1327a8cae06e43d527679dccaa1264c88b5500ba32b81c5fc4b5d0fa10832ecf596c7d10559db5154a327317688e33b47c05ed8b49828080598a7734e3116474fc9b98b438486fb6a307eff5429c211ff33729d3db5d9498daea99233e984e0b6391b90101ceeba3b60af265338bcb66357972adfdb6a14bfa5000a19ea1e4378b900d75ed8f584c58b19efc7ffd35a408556b2b798e7f50dd13c207a20e468a58ae90155a18cbd10ef365dd154e562506b742b4926a3fd01611971ea0080458e8939b65db0c0c2115c2f86fc5ddcaf4e5322fb80ed613c99901dcd00c28e73a0770721103cc0e601c6482af5dce0e3c18a0fc1ca1392673c94570d0f3906b92b6a523eaa546702fc3aed4b1d6ba5da998e56d9ee9d02a1c4166e1f2ba67f841b16604d3710db944227c1954fc26f38063176b4cc7a681bcc3a33aa81ae7107a6148206cb37bfb0e30feb436c5ec8dfbdff738438d0f7fac0141cda486450121411c5199119c707a794744706f1f798b73792d446ec8cd14151cc9cb4564007e074512c3a7994486542fe0c4cfb7a91ed32f460d60200f131b8cd2344f86d1ee83279b893f7a2b379bbf74b45caa18b9afacc07cb28e62f4d1f9b0baab8d416756c23185a35e2fbe73000576677ac2b73fa48654c83d4b3c5691d1eedd8b121b143dd84bed6a59b3204a41524e6b2a3aed69e960051dc0813060d849a07308e9d4f16e166991d1aa83c85fb9533be10d6e3ad26e1abff2d57dda724a1039cc19b83fdb0db15d058a5b8cda4f6fa48e64468938fa0a338d6b67cfa4946e5c8a16568600a6a33ebe2251be49d4b0f3dea3fcfd5f20b02165b2784bf630bb0c10aec157a36e96c9a5bb5418888c123b4af00e77fbb1e0da196c3e29323a6ccb8a25507224ef9d22dd78647fce0521a8034674e201110f127928573f2666ef0e12d6ab545477b0135167d387a6168d89370e14849285647ee42cb30ba8ed3de798304ca0d8ddc3fc71168c40d29707a8c6dc306c595e75f733a87579e2541fdb74732daa80921393d0db753bd1527acec21b889906230ef681505ea1a24a88016b1ed7a0694c578041e021653a4f2110a371ca53055c288d4d208549abfa093bd5cd0a5a846ac1e4ce53a93565d2026ea5bab6bebc759831715203e417b00e684fb4f3861ac40e7bd30d5bea172377961b603846c0e5dc66ddd0c789ec3399f1c06f0480cceaceababfdc7679705ad5b4f3addae42834171697f2d9cc1705bd66148ed5463c69a195d478f749e00c274fd64fd20ac14eb7d9f3f2d69537a7f0e719421f91290dfb83b4b600c7064312b80b1b1d1dadb8ec6dc8ddfef4adce74cd467d862a4b81fe3dfec469358ce7824092b4f1110ec84124d3f83c2a44c9e938bd6643ea3f90c8a1407e28038532067059425dca205e014e1ed14a6a4dfc0a29382987b5687baeff3885c314ddc515ccdc4e0c0141adf28dcb9acd0aa5cdd18c7aad6587e483dfad0cf21702fed9e6125a3ce982197dd3332471fee64901ae93c59cc9ee1f55355f1c0e540593a21cf2b663a1b35a27c25568d05f73e51da21af59ec3c938a3c842daa0ed3176ea0682c8ae2e26ba96bcae4ec23cf217d0e294a72b091d39937cfc80cbdbfa71f5184f65b3c308c81d5f48117b23413b983f7cad29b1e2956ec16247235967fff838363f4ca7a563c744ddd170d5c232fc96b8e98d798293b2a81014cbbd836a9c651d2ae6d487cec298007e3292c74641271c92e4ac5c6dad1174cfc52da4bf2d107178b050b5231e1c3bf4f25017e3622bb922dee5aed86ce426c3989cc7807510f53b614726ba4c50493c1ba91d28b9bbf8aab9db433877ac3281be12949f6e3e473a50a851dea4f2589c80798c75c6788201387bc0f0d54815d8766ea2494c19dd2e280d6aa36d9583065a06faf386cca4348770594dd6d2a81eac2190f5a85f5c8b6906a1545f10d617bbe30631cde2a1df3ce5036d8d2e60a916498eb4d31267c564a087adb6942391c373d1d9b371614349cd7a1e19e35e79aca64284ee87e25b89de8fec17180a6bcb76c3ad5aaa1a8cff5420cd83acc4a53940db41ed2d8d2d85a98a963a6333456d01b9a018a7a57638596598baa0dc695ccce18754e45ab37768b95390c975ec166b6eac70d72bfc0c0544762775e763d8da2daf2e0357db6005d66dfc03be771022bcf7bb804c088a753c0ed23b3981686c6f8892cd759338f351e51bbaed4389e044bd1cda2b8586a989e6c5af6ac4cccbfac8109ebf5bc3b2c5be181e3b0bab9795c481a609007efa17d2810c96ec629321cbd11d19e016ffd1c67ce6b741c78898f017506e6ac6508db53f20071b3ec4c1703b3bb9ade0d4ae753bc0a8b69c2329db8b739c6257b059e160f12f881f0913ccecf18d9c88656a80fcba824c8c6a6666b22ab1516ffacc2414f73fecb20496d9a3272326f631ecdc657a59591dd35d8f2f1c0cbdc391a60b8a607262d0102e1900eea884c3c0c294cd5c47205fe2735a2517c6360ca4b37da2ffb7cd81d37f1844dd822a2cf03afe842941a6dcc49833164a051f0c5c058744dc9e8df3ac9f65b828f169f256f630a47eb8c578ffeb61a91d750eee4be03a3f4c7c6e837d1583d18e9d3f2e9ed5a2d80003c02744a21899296fa940a518094b168e0379db323fca36e637ce1290be68d5e6fdf80d5f8b5e9d6652a37223e42d4f1f24ee954392dd6345baed2b6de110586519a7e501c0075dab882898d30fd2d105b54cb4816d5e572c0f568b0986e133b988a3944fe320c0d97f8cbc524286a7ca5abc8894220f35aa272193ca0cd2dadd01f2795b110c4dc5f83a5bdcf6ab7a35bde1a3aa92314977884240685aaf1e0201790de5dcd735de1fc68dbad7cdb6e0fd34090ab3ae4cb8bf2c65218cf520d8096cc01d1114be822fa39bdbd1dcc1a209d709d9582e27c8eae9d3fa0c9332f3d20a30ae2afea4007607fa8fdca46c786d82a126f62c8f326480110d4ac20fa24b57e36bbe661090337052931f57bd73d8a70a119c4d555b1269dac5a58c8a9b26e6e6d00b2c19a01a9b508b9a32f3541381073051dd09413304f61894c3046dcb919f440fcd20b9c2873796904c6d20087edcc097dc960c8c0b16ac292a1530f014d8b35d86bcbb333d87380ae6350c6d99f424beee3a17a9502a617d9905d099c2ffb80811461fafed82c56d05b4cd9cdccacba916c07817f4a2575d4ab6a75722817966baac508dade1e6b8639a8f8d8eaa382956bac929b64d8390f549aabf971a4bda2ae39ec8ee842eb35995115862c36eb9e26c2803f7505c84318cdad2ba4c8e605e87b5bda35b565876315b006b1e7cb9776846c79e067043b56fb324ed41496c95f328882e75e3f2e96cae3f416f604123b9a65ddffe799ed4992377707d3a158dd76cb0622f98947d61dd5ee99ceee096a90d3fb61c73e93e5389590ba57df48da2380428ccc45632488707eb69a35a8536f8768f3452b2952b31a6423ec61be4590cb169e6904f9c5ce258ba63220da0c0fb4c80687731d2706b2778ac14733911ebadb0a68f6f02adbb22e1f383238f4fb793bcd08a71a921a5c1047dd3f67504238a60d77b5e747c62360554a24aa45a83df26dab07d097b0fdfb48539ce5dc470f4ee63fa1ad98c16470eb44069162b1141202e10b22d89067401cd7c54bac942f38e81a8bed5910c57721d0019f9c715be670b2c0a496af1ef9489ab0cd2263a3c5e99437735cc43213eae77e72d11afdd2c751dc42f1f952bfe64609adc998fda890ba769c5ab4c49759d540ed5ea901be116bdfe11888ba1f65e6004673cd5429e2834b840608c9f43d03deec8356e8d5ec24c855b5243af3c2ac10ee3c32a7329b99171fe49c44286141dd975ea480227143c817a9a67f1e6137acf0ce807a1a71855a300a447f996ea92bf0deac15d4cdaaafdf19f63cbb8883cbe92cda2e16fd4fec42cef6c0ab176fb6e35d3a1b1904107c1a1ce4116ba4aedd1a25a7a3dd0d6d631a250ace02b4840a23c6ac73cee6fa8c8613d46a61ee15e5194b42efd83fc30162574378cbcbb67e20b39a51a23ccbb86114926aeefb835098c1b9b2fccc51ba267885c50ac5c26dd980a540e0868937882172a9066ce102856ea47d305fa2bd37429ec0cdaba3652ddce47fede58814d112f1f6e4a0ff70f6e3fcbd2be8f1f2ddaca6facc6232fa5688a9ee5c5a1483c0677e368ee3407ceb34957a5dba428b11123f64465215e6a3f4ca33341491fae1ec00651ccce2d575d2e09157aa060d29d924307a4de2bcd35921ee88d8e14e5a78f8aa8177a896fe247b00fb68f30f5f3b92fb7240d4615a1f54c80f9f4d25451df61ed60721ec677f417aa7b49d9bfdcf7acb9fbca2a54940ff768c0401f81f1508a4cd6d3b2ed0b6aabe874f0293f5b60b9e685bbb82a3efdab4ba7ce3f407b41d5a04f0eb04a11e1afb8a6138c1e8e46f4f2b1a7556c43124d1eb3f3dd816dea9109cbd4308d0adbcd945fd83920b2fa53969f84ac677b40fd6a3b31cad2cf5cc9a0b2a87b4e69fe16e312df6985f0aaef9ba84b9aec1ba0de6c599999202564c22cdbd898b27d6dd38d3bd0f1892ef61c7e00e42eb9716c45575256678a164a62550fbb023d6be8e6dec00f711ed76a277e0512844b74befb77f0317644c7c0e6a6fff6af57eeb640bf6fdeddf5438df9d755b6d609657a485e2a07d52093843be3619ccef602b765a04a4f10c7c8a0e5bfd00d7463dea4d3d5d7adbf2944a146784c059be71a274422b6f3c2301b0148c081e4899338874c9692f72b052d96c4d93e1390b9cc7456fb1c4daa2788875865adf460031b50b0fcc11c22989cc192b23b2e3709c83c0422a5d9e7202c346bf01d95656eb30c274fe258b579a4bb9dadc631f4516e660973d6cc1d8161409f81f1887d5770c461e3495a0a46a55e120bac1ccfc21aad0c4ace2fdbd399410fc7af3871ff1a97a76da23781e2e6fe9ea2a02440bf70ede82569e8c7a1ab04735bffe7042ec839a59e59d530d3e97a8f04c7a44cc461c41aed6960147a599192cac7c96fc33324a30c5b909b894eba4486ffeb143b7ba548b8b0497f68c1bf13f6bdde8d96234c409f84d4288f3b57b930571dad601e204f7b34668e9e26362de50f6b2c5c9fafde1ce5c5914071ed4d613f30c5148ea4bfff27490c9bfe07bbeade50d115264464628583e105f785f0a776c7b803fce92741b321ae96f0f07bf41a505f8207d7d0d7cffe042d0504bdd786e3bbb15a1d6ad65202895c93da9c5aa3f1ba3a2a50252c2eaa7513601e9c9c10dc06d34a156642e922545218c4089073573275ad39c28c5b9e6512b43906c9950d528f1a06de3569e565b0e101fb22a4e37b8c3a577e1ec129f22495a81d26cf49a3b0f64c08983dabca5a01acfab05bfa9e24b22d41991c3dca043cf26578db1e504f77a6a6b80468cb63620b0dab2b598b16c2fbb84470cbcb4db236b8085416361ce07d52b80e0000ba655aad5bf850502d156d6f97ad800af44f01284a17cb2c6277fe93700714b1576ff02231c2d614665bf99ed5b2c1a29c2250d31ea1e52ca51a3d593217e130d37632abdfbd40cffe452ad561f7920940f2ed6dcaeda64cd857d3567f35d1f45d9e0b49758061be39a93877e15aa39c79ada1fea5104bc3ff3a23482beba3f59bfdb5edeb01a50336971fd518fdc88b59868b97c2e6c06fbf9d07d351bfeb5b3c18055281db5df404c1c17995888fbd4dce09822c40ffd5f0e588a598d070c834a673263c7bce922e6227c266e5761fb205909168bd09f436ada1a3a1dd171e828ae2fb2afd22e15805f9fd5e072edde6208afce066a642a0b4e9620a7b548d8ac70711ba5da86e341ae9a30d56443527f420990198ce87daa0ae674489c753f1eae7526a0e008ab7ffbc062424ace4cc24990518835b73aed071bcc48d1068c80d8dc04b66f1746402e0ee46eab251a1fb82df96908cc2d9a171368d851e5c2d9a5eaf829e3960c787b42573c0ebb20523ac51b982ea832f7d2a46a9eef3dd2330d56ae49062d49616cadc8f26b0005191e5459623caf1d57b04224cfb8134333c898f06a1fe992ed9c937c1202399838ff80d148a534fb4e381b4cef44c9d8ecf7a08ba5f4f7b798fb6acf6474d8ed1393d3acb1feb0441f11d8daf7c14150886634468ee6d619281d036c11eb437282be10ec3fe35a15f270a84ccdb305830b3afea3ef8194389469f5e18032c5fbfeed844b2f844a5411aae8c91663c6156e3aa7eebbc5d69cb7c415465f0f9d32f05fc936a35d0d7e4b82e7e3b18aaee0f1e60651936c78a9d2310f691d41b86e1855bba726d55e9960f141f859386853affd07072d3e7eb740bda16322df09badcd992e8617ff69a896fc4de9e83cc9f099d99b9bd5ea8957f1eb8c9bea08b42464e2da27e2bc6d2c282b80c5d08e195e921f89fee2429afb18415fd8286c499f3b2e0ede440772c80273d6b8ec998c76331367054a1114d933ba96db6994c51c19cb06433dbda51d741bd13aa92f24f20b38e892c8d15c62b4e2791436e403f082c81a6b8c0d558b57aa53793492ce77aec5a118dde8607cab614fa9547ddfd28ce7949672bc808a0b743a0b5b8a0ef1eec90bd7110ec87506e75f332dd29cec5c7f6b2da8f2986da8f2b1d936acc0c39d4aec269250ab4e4b424d456c91762c2ce720a8ba1c70d5b4815729d8d59e4834828742ef8c4edb20d5793ee0a41f6918f9b96f8012115af661a833cf1c4ae954e56c95b191228bc61d9f7a18cf41b3bc1710886c13ce731c4e9cafe0cbe3597fc6dad2293b4fbfe1fdeee34e60aa99864f02e3c5470ae3caa1374fcd01f06de438327054258dc2c6c2f59f9665f060621dc9dc20449a5dde95435aad88a7447d01fd136271ffdce167adc86ca67945dfba5aeadff05f2b9234e52b989afccad703da8f7e64666de178cc27c0a57bb5e2ee6e3e88371304c00d97f952fb251a99b192aa58052fecd569d8d9510a2c247e8c634a99410ff77ba090e53a8f0d1d712a1be38230d7471788d0a6f749c56b4bb8170bcad854c99475045adffa396893019318b13f5b3811248465b77e3faf51d4cbe2742b71561c7a7688a72be6fb48be0b41f37a92634e00fb091216bdc10f63f6292b0f6a4bca8e268909372f3592a8978e1d624c0f89a8e9bb4625aec131bfd9f2c644c6f88f081981c68af5d1279469c1802f110a2e1bdf760722f6c9b696621916cf13d50ee85b051753fe194278578c8f2a141ca4fcf843cf2e1c20921a8ab10cac21712a6c8072575ea7627b7f67f397b91c17ea4f08f6eb6982aa64eb3246b86eb1543ceec1dec2cca229688e49e74fb560f04e7524bb617e87e6569c7753d83dac080062bd46eec830bbe3ecef358a507907b8c3fe0278ffd28c4586491239a827728bc35932af688a4761bd1c18d0cc7d9d6b1a060e0116d41ff24d745fa629575a0fde62801cae27d48e34d2e3e924c7f0227ba3f7e7d25fdeb312e7bbca6183789101b4ae7c40bf18d695b1073247d571a8110b10f43435ad4b64a58eb3d8d8c41dadd459f131dcd611c9e7ce4949a49427f8ba60837735b852c3f5fd6a9230b4ce7d3132c6ffc16fcc92ed3b331e3b84cc14aa2460b77cd75393f7452a12b8b8ecacce5adf11856713d17cb7305f50c0308d8c6ce5d4d44c028641d8b15a8a3a2c7528a98fb56b9baa0066b7a095afb6e48bb8ae504ecb96d582001f05aaa6f53c0ca687a8285b91df987cb4f1aea87e47d57773cbc13dce3a31acf94e998cd8392e0cb23fbe2d8aecbd89d0a6353adc7d63fa1227a16d78d2e77335e51c4555229aa8272562a77257962a10b52205515ff2ca0b54ca1fa429f8d8fb8b9d5c95c114c22709d0443f3e10f963012812f9fad93b9b2f79f28a05116c21981ff0c56dfdcb35849e1cd624bd81c35c687db1cd6eaf5e1364018f48ee309b93cdfec9f23da5b84fc8b8524b44551a271a0f69fe838c6511bd0fbe0047be91118f45688f88112b52c4b6abf71a79565a5f46b29ada1d3cffc745be5a810b2e4e27410770a09029c91d65a43cf8dd35ee55499a3d56f858a46c554d3a2c18adbd3b6aa54fbac15d39a929fab5c4b9fd969ab7aaacfd0e2b7928aa62aa962c302cb4873228052193f3bf98c511a329f9c6f8c101fd7ba3bc54cce3e704734374d6a6e4e772ba43aed6b15622914a73e4e100d957e55b9e8848a7db2ab5a19fd407e9b7ee824162bb46d55e85c994dddb23ae1ac53ece249125f7ac98e03bd3ee32985c63f427998d39e58a57dabb7ed6bf3c293f9efd0a4a3ece2979c04dc7aa7c7d89dfd8c647855c21a6837b1d6c2b3aacfbf37bd672c4e35333766180c50ae4210b2b069c764a0019e0174eb7ebdbdada91880bd8ef613516d41267219b7c67e0be9fdcc69e8600a590ba3580e6eb65ebb38605e5f7aecf0dfe679e80f0d578b95fba35658134253d93e337e8cb30432c4b1410817805aa3906c6fab7bda302ef00ed66a79ada4c3568092d50c0f9c79654074846fb4e363d4bbf8f2085ac4dffd80ae0d98a4f8a3f5da67b9c1c5fba4ecbf13e0c6ba08e871346cb981d078a34e55175c844d78ebc486daec0190c45a43922c75122686de5928f8d3fef60e941e16dee6ebbc055a97a0b8fe159726c916c198628b714f5afcc492c0380d57508eb7e5cf8b9531808ac9ed053ee176a00d4d6b678f8e0c052116e4639c8ee7eaeb347200edb1d431e46fa85b438bddf756659f49ad25297bc567dd7ac8c48f35e5c613a2d4765794a9daf8daf40e4b4e8669b66294b62e6cae63e97dfcc25143c18fd02edfbd833e44f45c35816a21694ccaaca9277dd2655c8670c89ecfa61043fee73a6cdc411123de06968233c7468269ce29f937a0bc56dc7dd72945e2e873577c3d091e25a1d7de3406b2506b2466a1ad01e6826700608545ff16d99aaf8f9761a2c8c0c3f8a267f862e420fd64561761f1562fd9eea78c8a08876ab761f9d9d2b89abbf22c20b7f74f114ab4f6f01a92965cbbd65971063be820238b2c6dd137403b8db6061afee943331eb7c91afe354bd0a2aafcf2e1b0e4c7827cc9cba67afb20831dac63c34d9fd5fb209fe6c628f3c45b654f62773bcd2c56b5fd29a48a6d60b682f00692c3ad47c719462afa6f0879b667e6be3fdc71adcb240c1fede0c2a1a633c439c971f37c045ea70b21a8742c5234d2d7af1f6b4a7afbc139899190bef1eeccf50a82cf425376a6c9ad91d8915a41badb33fb04e42322a4b6ef815ee716672a51f6f2212130114fb5c1bf5c33d39cf90a6a9a0d3cb7e1a244ec3904d6ccbc16b4d8836c5bfaa42a81c3711e739bb2c4a1f78edc00aef5c86a25c20f1e0f31a106d75e6300f3237a3733f8282819f5a832359ffb1ee90b25321ae41a6c37f5ce3542121f8367d4dadfa7550aeee9bc416d3e4c1a745fd7ef5e31d0b220f515e4decad23c63a0af2dc397513d7fdd49cf75bc38280d456ff4f44a9a50e43f67de8f1e98f8215deae4e1ff0991cc18aa9e8624e916e593ffc9dc50d76758f44e1545fd4a39f5a0512827d2ca4b9dcb604481c264c5949352f94b79d2578d15d7f79352b1b950d90b438f5eeb5798fe30630d8fcdd2e9eb543c2f457308123d6acb82ca9508a77010e650576bdfbaa16a15fede3494777ecd5c7309c120441ff9e095c8e71121401f8aa2855f35a34476e0f8c6494eb058aa32319d292958422799792a100d13aac30436437773646036d79ac61d5c21c3c60afd37b5eeb7107a9eea44712a242132d3550fde04ca6eb9864485f4ae8dd85afd8eef28b2e823b18aa7dbeb6f8d8f593b28676495f3e76cc5449837ff2e7cc38623118b4731795a5c3e80a4d924b05cfba59f7deb85b98cdcaa2f7592f6e0b0a803a10a23dc2f400a53828a0cbd1a1555416982fd27bf59998465ad42770d8578ae2bfc0de6fabc754dfa69a8dfd747861338e3d821f12936a73ecb4c7b3a435086c34be6b0354f69db26c184090ee31c96c78ac682e3f9d542e572eb3e7801359889071f1080bebd162cf36a075fb52e2d56b1abf62ed4250dea898ad8667659ba21e51c754968208986dd05ae1924fc752aef93c2f19e3394a59fa6be4f90bae1a3393e5b92129118b87c4e9a85d91ad68aa2a6d8e1af13f25192c1b6c6405369e54002d0d404bd7775f3981f889ded5ac71229169d288a34d60574bbc0e02d3fd8661862b324a92f88ad74ee3c2516156f64178c9eb704def822f35296a36505c204c1d6f8d7ec1671e385106fa62cb47101d65884be5d1f8472ca073ce6b742d98e2086ff7cf38bd0abfb1982fa82d78075f94b9b7b049818b86cef0accf1e9c00277bea42e3e13febefd08b21967aafdd9b12edec9bac11fe158931850a756df515b381f7ca488ea11e7f5192aba45261374a21ef44bd42867d48b4d343404caf9d68b56609ad1a4c57bc2664fd0a02322619f61778d150cd3eed563dc441c07ae42728e62836b3657a09b43e73b9bd886bf69a39b499af07048f6d033c78319f59f49934f616c1357f4fd2d69ad91464468652fadeceeeede24b708a9088c08d1895aafd2e084736a3ead5869a873d29b74483a966e1f8c76177dbd3dcf9f8b58cf8d40c8455e456e449535f69e88f5d0a821edde88daf3e89c1363bdd9e1d9f8369e8d3fc46efb4afa130683c6d8343e65430e54062c5a9eb8c15a17d6fe8dd05ba3429f4d0634f23f0d706185bc86172397e343aff8eaab4500822008822091d37bc3e24944cf7bc35622d56d65ade66a0eafa91810f2cc057ac4f8cf82eee2355ac8ba220d807ef506fe3ef38bb581a77e89a228da404de9eb877d79465f3fecfb485f3fec48de1b78b391e2bd81355823b15b63f3ea309ebe991e187295761181f746c8a1ee843ca33bd6e1230b86e867597751837e2f348bdb237f77a12fb64746a04c06a23010f5cce108f4976f8d7a08cae887d1d783e083a00a21089a1795a1b0c8dcca3f797be8816e586f7f704d922ed3659a2a8becebad3850626bfe66cbcefc704309c97e133561a653c7500f83b1a68bdad3a537dd06ccb365c331d3062ae4f22297ba536f0fdfe5a1deeadb70683debfa614faf36c4cbab86f786a63aab2f57691b692345bfdcaaef27eb5dc48257885eb921de08e8dbf06ac3dbbda1a9acb1fbc706eba171ab66f786463f5d99d5d643a3b234567dec447dfa10d7db985356c6ca14a6655ff409831163b2f61569abeb197de287fdfc99052842902bdc50802204b1c26635342d023c337eb395644c0673a16abcbc171931b7054676055af695206bb1dde447df647d2fbe40970c44d509334dd374c46b32a66c1d8ac65d19632c1db125cde911fba2affcfbe91aab882de92b5f4cb13877048447384744a65a0682ebebcc141545105f26298aa21d614ce13c73443daa56fc6aa50aa5c8da48b78f26bc9174bdbd7d3d5ed9cf566ad7bfaa3bf912b5236a67aa8a34190cc691c516576bad5dd9d3a5b105cce9185f2e49d4e5a22f8a5aa16cb4b231cc1386d4ca86908aa295186988a2285aa1e6be56282a01fbf9ca7e3ef3c3851ca040092bd4b600e3420e5070856b0144d18b14e51aef5d1bd121d42f65dfc887b736f0e1a1beeeeb144dd4bdf4decbbdf74a39ca51144539cad34a0a5483de63a46521ed36004355eeb97aa35f2bfabb518f0fefbdef46118efe0e5957049d3dfcdeab5674685d2bfa7bf72a6dce9cabeac715d75f0b7aac286b8e754613e5e30ce96b877d1fe96b875d1f5dbe2ae9a73bd321dda156522e8c5fc883ef5e08ba40b7468fd61d76e654d31f8522026f4c928ef4c539bc4858b202ea771567e3d9aad157c5e164f4954b301fd9c0539688f59ac98d94766d5cfe5edbe5d3fbc4a00dfcc9b2ad04fe62e556026985d7dd8b2fc5d830068773c30d25252424231b56138522f9324953f4e7d2459e1df64a4ab62cc076fd730f1d7aa742edf27031be3c5c0cdbaed85e0bb76c23b2554376a3b17c6d447fda06fcabfae9abaf95941c51d841d36c9802e746172a07b1c3830f3ef82009492b21b1765a12129220820882848484a4b29a8c7de67ce69b1466ca2455483be42963320909094926d9308a6014e1eaa236c4402a8a22385d4882200982848424088e092e04e5b792cadadc4af2e5424d988b84796825d125ebba798af2df26ffde4259f3104553343d4f14e672b95ca2e812e53c5d951e456f25b97a522bc914f5dee572c138e72c49b922025fa877b9e561824548cf738d2278b8137aadc487476a4204ddc8da5b0b41d646519499264c86925e69a6c343b73193a1fbe8d0adbdfa797088b48da45f09b2361f1e826c741f1c8e80a40c49d26f04492c0ec2409566ca4cd31445534445fe81d308b9e6d53753f891241262d5203f38dcbf0701387abe77faf3589ca3287de5d8d4318fac6c5d4520403ace607db16cfc7bc97a1e1c6e11f455a5af233492be706cacafe9f9d2a9f53c3848fa62991e61a43f9bbab03823581af27d849f63c8deb0475c7b0821f809ed07879523d80e1f7695dee561de7b1c4e890f30537963658cd117096208018e33e1709e879998903f73a29e8989a2094a87d1c61e029bbb38045e7ce4514949553d496fac76a18178a1477474f867a55a7dc8d5dae44baf39cfb9b98b03b101e271263eec7aab2f1f36b4f822ad70a6290483c16030cf9ccf7c3e7c5fd57671fc8c39bb469e59e76ff487b09b67acc91b4baaa64cbdcd21e0423f6ea31f67028111cde1707cd8186b449e1d8a8b3b4d48863097902a6b32199a3226bb7879431df38b643194e5801117bf5840bc58e16c32a7c6c6bab046beb15e5c3e00317217b7960346746401f1426f32276f1ce0e22307c2c5880662a357381c1f5cd52a2a6422767d55ce595f44705eced54a553d192a1f1fbe622b3ee7e9d74a85af54ef27ebaad2c5990898731411b1f3afbe88d86faaec73b533d62bd5be54396b321486a2288a882bef693a94908a83ff4a3859644289aaaaaa88d838e45316b11e6712cd103c41b89a88a888a8aa8a0a1f88789c30032740b1526d0b3027ccc00934c834e905d5bdf6d67cb3c615254939acd000bf2a5babaf226007babd7d641f67f234a4af96675feb6279d0f334744e7f347f7a662af567da4387af90b5cf68eca10884bc3ab421befa87904ac54191b52e28c2223614fd1ddf5a9be8f736437a13e91faed610b6d6652f59cdf236c6ba73c9dcbe6dff1eccf090d18fe6f0b6521a0c74fb6a3190b5d02d86421a5f4b2d8df2eb0f5509d4f711a87edffde39e0854fa71265757db4294de8b1f59d3a14c96773fbdc3f2f6f44bfa73fcca9ace04df5fab7381a08bbe720cb1f31f4789d8d111fb0e9129625fcd331a7d15b1a3634ef535c4be47ec485fd2f331af2c22f6d59774fc9c7af665d21660791bfa83204892ac1f42aa2236b6c756f46cdde717b29515726b5d45fc10a22be8fe50696bf276fd43353db5d8df058122f6f36732e6304d1a038e5d715cefb806bc37a4771cedab52ce2ced62e47b15e6c9488755b2e225e91643e9bd012fdde34bbff7d2ef7bd2b3bc0cb8fc5e5ea468da68c211d57ae990875c2ffd1e9fde09b17499da962ebfba632f5da5499195ec8397de838f922829ba38a2b0141ddfc84ed685634fd29fe45e7d56bad3b1f56ee4c7d2ee851046d0eae03fbbbe2c495092aee2327c1046af6ff5f0127389a82f0664b718af95ebed76b91d5f7c9ec91c1301c7a13c5003643c8be7a129312f8a9beeb4cf5ffadb167f996c945edf8a7d53bb04dd1ab435f7b6b35a9b8d29912d7acac25cc5b1789b7e1e5bc26acca6eeb4dfb9049b0167d3f05ab1f4ac3dc6467d36a637f2741eae582b52e9e3e5b8bfabedf39854cd9638a57fb7068dd603b7b49eb85b7b6b5b46cd966030606b972654aac978767d6ab51042f820445b8baf51dd81ee8dabfdc563acaf18fb9d46fdc96e8deaf4d582c33d1cc7e8736c675d0f87cd604fd957742821dd69f7047297c22bd22caddd31add2e6cf0ecfd12ec6bef0dfaf187bda90779a3aace5c45a5ec65b63d606378dcd35cb3906e35515c7e0ab3d61331e7bbb561bad67b36b9b734e32bd79f315d2177327f4734ee8a273ceb9f85ccb65378be688809f921b1eb69f95524ebb9a1aa2b99a934cbbadd254ce89cd1ea22f9676216c510b8ec66997e3d15a6b1c36a3b5abb41336433ae7c48ebf6750b3b8e79e7bb45def7958b3f2d380ab4f1a0bdcdc3b14953cb5d63857c8a3bc734d9f3018b2fa45fa49fb15321232d2a08dbea68eb1e32bddf455017d72ef3de1b43b8d053d6349e7eee2c56d0dd735e817da857e11b593d65aab3d8901f51b848cc11873e65cabae2731de4ff0af07e13b848b7172e2c44948d3fc6077af6f24ef3ed61f6c2ba594b234ab134d4a6bd59cd1c92e964f7db51d1d6a3dd801a22bd5b552d58bde50ab6cc1c15c8467ee6a5574a873d25db61eacd23b8c9cbfa233dd4462a0816997999a055e914b59a918659b36bd834379a009d99d86d6a5d17a13550e85233907d91b158c52cde3afd6832db1ee30f9093af6104d65af0df70ebb61b7fb0e2f64eaca696af4ec5a6bd4a8b0e160c6bac758f22aceb1f6e0cd4734c799bc53fd38112dbd5cedf98bb36bd5d74abddeae3bf8c349818f2b1567d7da5ea52c6d53bd99978fe738949caba0d5aebf56aa1d1d5fbfde96f59d08aadfc1559b3ebeb72b246b48d676f0f8c8348c0fa4fdf36a2d92fd3894475e1aa590b22d1ad552321f4da2b6658b1b838c697a41d8b130b22dda9b2c0ae6836531dbd9ebafc982ec78e8979a55f96b6acbf25a8334fb21311fad5d120b82af0dd97e259592a69204a9add0ad01556bad6dcebafc59d3a515551c932c0ac62e96c7de8123ec4714e98be559db6458f683c1609fecf1ecc7150fb1b48b49b1218ce9c5a4d8f1d1ea8b41f683be6af68345d1643c35204929a5ec478ed6729b0402176cbba567b173d8cf153d9062f7c0895d37fbf33c43208826234ee8d27c749193ce492b9cbcac420e2925fc95bfd0592d95c066b8c3ce7d6557dbf592bedabe97c46243c6e09d639a39ddb9af87fb829ab2082f59dcb20afb72cfb1a1d5e2293b954d9037ae66236569fb4a7d492c76fc331f04e4cb4b2da56459d8bf1e1ff14ed2065478a92f06f5a69e3e36801dea8b077678a30a69054bdbf092586c9aaf0de95047f70685129558d25f292548edad01d50be3aed6b32f7c65d7821bde311897b462cbc76b837d3e5af062ad4adfac4956d14ae2f520be9e24ecf7c862534a2defa51224162231d090af950e650d90bf64677a335f1f9f7c5c59b155e7c0817cc543ef5c7d31796fd8cde8beef1e5bf3c1e16aabaf270a9bc5492554e0d06515f1d680a8f5c03dad67c73d31b0e1a5810d0f377c7bf250a36e3c70bb78a156c6d82184ce08b79f531f44b10fb09f2b7a60861ff0903e08b205b09f2b7cd0434a8b6a25cf74cc297dd9b7ab7eca4f9694bfb85356b5a53cc6cace44e247a7ac9c237cf5b3fa8251eb0e14a966697be62aaa200822d803d8cf153d64ccce15ef9ee3c557df6b94d7dbf86d5ea5b9264e5d546b542bbdd581a228a2b7d12d3d4bdbb6630f45573ea3b0b1cb19c330911a9d9e1d932f4cd3c8306b92bea05f19b76df5a4ae0906836aa9af9d9b648175b1f758f02cedf56a1affb1aedcb6657bb647f8c836b63a46e0d82aad19e17694aff562fa7a9b5d79b7e0c8b1d9b18ef8dac066f1ce059df76733eb723bd61acfaccb47fce554b0af7696b68d709bb9b7f159dae5746cf8cbe9e881077850851f00d927fbb9e207516c287c40865dddc507820a76c97eaef841bc35f0d9b51615030d99056ec9d2f03d7c74fc8ba5bd6ba313f5753b49d4638b97acb9a54f9fbf58d48c348687e227eb71f16a16b86d03b2f594bac31e0fdfd963dd4f5fecaee5802db84d1454f78784caccc7bc942fb090af9496e2aaaab61757c89d77b82fb62fd7048321ab91476af878a0a32485074cec761d74d084dd1ec4153bd8e2ba26180cf876a57e188ccb31f1341f3dbcfdb46b82c160577167a88f436102095a40c3a1d4c00a1e8481c6f3c7aa8f43693934341a6304a820904f2247122dcf92521ac08727a594524a29a58cfa790c20e57befbdf75e7cefbdf7de8b524a29a594504a29a594f24929a594523e8774524a29a5942f3713c69a4d41653c986c7b111854c8cfa53f7fc04303de1afa9a6dfae403467700731c2d3430aff12ac01d7f13fdeacd7ddc44ba87921d3dd2efa164db5bfd120675c708faeb61feec791f309827d14203a393a03e696d043dbc845eb428498300f3247224d1323bc0d240a306cf3d576c768c85c1606ae01e2b089083ec5040a0a191040ecc344dd3344d13354dd3344dd33f904263d2496800093f38010f7e74610551d0a07e79123992b840902040f1851518010662a0313d074627216130180c0683f9075268607412d35b3b65e5c7847d32610db6444dbac18811f1f6f62a8d06c4aebc7db8ba03f7385a68dcd7f65ee32f790bb81d830cdccfbf7b9f440b8dab93886ee5e7c397961131c01a04f749586badb5d6dadf7bff81141af739aeb6d6da4f67f20eedbdf7de6badb58f36c9c301f5712835a000102c0d349ee6616e074465a72a68d58343bb880e9e984e5c618311582e04f57165789812d94a40091d3ad0b19f2b70f0c4cefbb902073a2a00b6c85749c00e4dc653ff62bca83d623deccdd74a6685e8d34a281d99d6f55a762b919ba82d7c2e08d15d4692a43f4daadeacea92f55a6badfd10019187bc86483967c8a4555629a7a5b9b46293b90d8f0d8f7b777bdaa868f37ad2d94b248b77c7dc8e1d9b1d328afdd8d082191c4383f1bdf7627c30be182ec66e31468b6a25f1b3de5a2fbe93939393ca5e1020942c0894b24909250be258107dc5c8e29c9914d75a9149d171ef312976fbc17ee416352885375d7c76b2d93102c7332334e7d9b5542b11f9ac7ee5adb97cbdee45b48a33697a565a37898a9eb98a6e8f157b1ed31e22ec791da4cf5bfda479acf1237d553970204b1de33eda79fa3147dd4fd88ca93becf9e91306c3619ac422c8d3a4a394d24e34297511931263d3c449b19363196ac1c1880031881d623d905482a474ba2325c4a49b2e526fa2438f18929747790b2fcea66c959c0b1445517b8c9205c906630666a68820ce3a6caa297d8ab516a210f422cda6801e0f3128d21ccbe4d42e3d5ad54193801d18d064d0c738bbe35c28875b70341f0d086c02760f1eb7632ba0c48d0b6c467478db94f713c32a361c41614c38f767eac8b1161c2d36d8844ed4646bcd873b83563419c12a5a6badbde98bb55766736b21228f2dba9387bf26f7cc999dc3bf77bfa61eae6e5c70d625350bdc38087b3c47e5d2a34909ab6887d1bdc1a6cc45c3cbc17d71afac075f1b0d724c384ab3200dd6a015638c23ddb8e0a08b589a8758156b3eb2435169d9e5ccf1a1c9b0d087782ab2aa1c94d1af79f9cb5e628c71548f7f59cd85399db3f8f571ce9cabaa52cd02370c8231c658be05d9928917455571815e046b7ac8caad04bfd254e8b2617ddd33f15a901d1b17d88c0ac5d3a706658d0b97e6420b21259b51e82fa82673ad145248d1a9d59c4d0a1b6765a7a8187e0d790bc14f476bba62ddc1af6f315a70c478d6b1cfb163c3734a5b491c999610426831c3f8e921de36ebb629a6ded4cfcb181fe35914f12c6e1be53ca5218fba136f4366c83117295e7e362632676350244443d87a768c3b622bc66a65a4972ffe2c1767967c6541545e938117c24f081d840fc26b5e77e2b1958f7b12eb4efcc4f336626dadb511cf472af6d41b7a7b090f21bcbcb572e00004fd85f538937a17d6e3743c1c021f628835b4faaa1014638c31d288040f1cc96e1817b9e9dc2664081d88f0504d4604e39c73ce39e79c0b71cd3df7dc8b21f25841041d4486b09048e4369aa5bdc839b769cdb9fc13e74264e336ce422ee6759b288a44acfb900635194c8ff738a6f3d0848fb16b8dde498c288aa24863039a12633f0e45537203079f59984a2aa9a492a5b51c9452f946df2e97f5d2ab34395188460d7a24cf9e759597519df274cad359e59cf2866da7fa0b9fce5bca208d29a59432a919bde82af5067afd94f252bed13b267493d467c597d64ae90ba594ca8bad15ba356cbd1024a5945242f8d392ceaafe2ccda19579b430a7d6fc64d9ab384671f3d14a66130caad71d7908dfe09a6033a624fd7128313b147849cb4b5fa56ba25a40d867fd3cb32cab3456c6cacee446f3d135716fdf7cd8bf5bc33a1d1246292d69bd0dd1295f0b6e130ad87cc2f160b136685547efbd17df4b2ffd84f78c7eb6cb13b8b1df7a35fdd65a6badb516c1065f83afc1d7a6141d093a0827149149d2e17c225e389fd81182208869e79873cc39e61c738e39c79c6357771ac4601458648c31c61893e06d3c703567ce55f53bce6e53477d1d846f8fdd3b9fb8f3092641f9c6c3dd550f8a4bd957e3d178341e9a8bc6da35b6b95cec9070e190d88868f7215cd6b8c33653b08e524eda5a9b928e0c467cb065f8f4787ab81adf8d2e342fa594524a29a43bf075361b9fd67ceca6ab889b96947038292917c60dff92120e278525561d198c3bf76d3c7c38d6b468415252389c9292bfaa328ffd1e2184556cc91277fdc512e70361153bdecf997355fdb7474a4c89913911a66b50a926c3be98d98cfae7f8514d869d10c28e7c1a84f36f9240c9e65fd31d76f887bdf7debbe13512285e88cd807eb130c6589ceff88ee2c8604c078f1dacb1ea88457cf39055232a32c6586433186b2ccec8d8646133c647cbae16f2eff5f90ead472342ab027a43e2d55369c54785cbce5a9120f61b1d760385642d74d4a195a4c3cb9974619031c618638c3128b95f1b375058271264a363e30267ddb4d9016fcfd236b41b1e3b3e449833e7aafa4b52645c2012cee45d5afa66252b9205122e2585c32929f9ab2a67b6d9416996b61dd49b7bfaf8a81009b2d121adbd461e7f6d786c9a232a3243e66c727eb3e36c44531d26a594253f7b5962b4a46475ce39ab373b248c3e59b75ab43524f6fb15b71009d28810b74062072c2288c47e2f6ec1e6a59472b363a5511145b2909708174402e551e14cdeb5a822888f942870b028a9625fafc77e32070ee4954645a302ba10936c8b2dcda04285434949e1704a4a7ef7a8707abee3ab2f912c76b49b1d2241a894526e763018f185b01abe30f29e9287e4fae73aa834abfe09a9208d6bc1c1a4e8dce9dc64536cdcc81cd157db8c0978dd1309a99c4a0b0e169f0b1c93e2a9c03d1db23d1d3b9ea56977d9cd1e4a9d66021209a9d814d0a26288179f6a3885c930299e0ef95a4746e419132e6c0f5b837ea1617b3132d90b179a83a591cbd91453b329aa73ceb129a4a89bf60a2347445f7387e82b431acd615394945c241c5d2cef416c0a1910427db1bcf71e156c0a46c5100ee54ad8f0ebd6afac6dd0acc7f59c967edc7bfa711763e23dc97a5c46059b824d81c548db64c1ec2b01aafc8cc760bc0959ee8fcdc0e181f1fe8ec37ebfdaa69c85025b7ba178feb23ca28b852ec820841042811b752e6014e337c783feb23c76bc8b287596b633c626c614fe41c0fd84a0852e1f317ad25775511e193fa234465f34d5994b06535d44c368d1f465d2273524044108eb130623d6960fb9d94a529a0ca99ee3507ca8a8beaa063992be28a594626b4b76c55ab11863b794520a4f9816586881851658d8c8daf6d78a75c57834af0dfb0e96f64568a1c58ab5e9af16b6461e1b6ab930912148af8e6414cc3db3aa9b734a684ee7e284626b9074403873c5b6895b6bc1d11e617b645aec0bcee7d88d3d4ab8165df02221d6f398549a1b643fa7a6870cfbd19988afc707fbd3f18f9e1782cda8ba033d4a794877d8744250c79eb1180d4623e4b3d2588f73191b92fa903953c239e7524a7674cec9471da295c29c73ce092184eb214414b1e5d22e5b8041b019d6ea1b0f4a286b406b454c08f6f8604108f1219a0cf827a17b0f5f88a763201d5b3190a88e16dbd9f2400d64cb01be46106ef1b47059b4206c3f0d83d043b41278d1c740c3d3d7e3435ef1ede1b8a721485f9c16ffb6d31d88deea2684106cc6a33bf6f48c6a21180cd689510bc160a8b4272b449fcdae8d79f80a2d895d05cc98a145afe22cd54c741482c18012c2e7ac0a9a0f1d3798155d134c321d55b826628cb001714d5016a37199a47c7db826d8a18746c458b399c46bacaa99c467d82b2b5f634dcf5ca8338a1323458756d2242bbb2668cb6dd5033822c394520a5d46b642b1da48b7203af67369c52763902643aab8776be043169641a87cf12d089494c669e311cec4c57ba3eea88f8034d552b3fd768abbbada353f0860db49f3929533d6b32b041c4ae62e67493f4fccfa796246b3f380667864705b342de283ba05692551fe6a41e02fd9824c52fb110e25332532ec2a13615ee133d6f39840cad291a49fc704ebc3f66c93ba63b57cd3b107a9238301e908a9858043799ee789c2f344e1791ef634878d6069d8f2d19a56a20073b105893c6e9b967082a345f7de1af70f8e9131ce5674316e325e8cf85e1786dc8170c6d873ba19e7e5a15646d9b4d86632da687b4acbd25986fad40707902522204b30c6d8c49f40b0be31adf82c01c409bd049b0cc8648c2d719790d82d2a9f1dcac489168464785b382d5a166c47cd80b412c880c44778a13f57fef47bf3ef2f93cef70fd2d78a95f44f3e9a7f1bd21d0afdd123b8e1d51d7c08929dc980445be7e12b5ca1f67c8c314e80da9d78faea6c965ef545ed782df9fc80cba5cb64d96612e5ebb317ebcec48441871ead8bda7643d0dcf135f2f3a66d258cb5b3035902c81240965882735b4642764e504ae9bd114f9c134d06cba756668c9d3827d88915f5614840aeea2be31ab996e82c7ce2b9c0f1682a607b72229ef8535fce0905b0fa36e79c1b3d33733ec4821a8c9da7d6753fbfb14442a8beb20e1b5e74b372e0c0253dd94607fe69ac599e1d7587bef6d0e076f364b2fd7ccf6f3e43ae6acfca7a9a4926d9ce461b7488e983bd29d1aa9649767c55e5ac8402e82b4ac54d6bce997355b5b9dbd4272c8a0b149e27b216b7650d7916c58cf48d69451f3f823419f351c5b95b033fb230842cc8b41ec84395f6712893743129d88f1a103e2ae09e5d5aecd18274b6188debdc5f5f802177e6e176a76287ece78a20a860d766739371a799a51c94872cfa6c834e6b5aec2a803e3a1c832c258d7ccbdd470bbad63c49c90dec2e424b4bf683c18087134232c85555fd55538a5c55f92f4f28aaf9448cd2ab9cab5c5dc555f792d5c1555549d5a52b495596aa1b27ea124d32579f1ef52e6fba8f5e0c799a0c86ba4c5148d657052afd342ff3308f7a97373de93ed231c6c860401fd0071c13d1a96062d2ab939dec64273be569724c4439a2f2af8b0e18197b76289ab3a9d2643091c4281cb14b39724db8265c134f6af207b6d64ed05a8ca79d62ecf72b468d3e59576ec1f1709e1e519ea2e951c639eb2bc6b6f9e1931329638bc4e2115a706c74c88d0b2468312d3844826c7460f2e51e7339e662aed272267afe453dba179198e8517425912ca20c864539badfecd8ecd8ecd8ec683da412248557472d38ee2f9773ee2f3c3a39f68d978bbcd6e574cc0b853250dafb9b7fb1aebc5b70449728bacf17e972c997e76b8493f2f12f18e91775a3e685541a195dae7921af7a1a7dc5d8f7534663f649ca913e6133a01306e3da0ae92b9ecef9326c32d1038bcd4ab09f2b80d003e87aacc693fd344dd334d94f9d1cdb426661ac555c94d3cc2f567beba6099ff9b84e599dfb678a5ea7c99e9a4e594c5da545f77ec25c97639bd11ceb48fd627938c32272618c66699b62f99215614bd90f06c3da1eeaa6fa829f9b76a9d98fa9c91e77ceb90b39f9724e3827e86dc161cf713c2ece37ff5e7cf12fe931a8278389a87c2a83a1f415634397c7267b6f84efc9c9c989bcc239e72628a185b779b363b3637ace50863294a1cd64756abed63e67e853fe045d667f7f49b7d045e2f4cb5a0d45f632d55f52b6d7eacd8ecd8ecd8ecd8e9689c91090bcce25ff9e6333eb82f05eccbc480e1c880c06740883811806621888612086b98ac340c756a7622c06031d638e217cef3157699833e8416111e6193f826e65e66114bbb089492cb3885996b9ea9f465df4c45cd691be38186aeed58a0c0644d9e9105a6b4d6af7ecc70fa905078b82f9906c442f4597ec542bbd48ab9f0e857ad5178b88149dbe9efa45dfbb899efd603fd80ff6436af10cb0b5d63827272d381eced323e23089d5fc5e959865d9c9c949d339b3e801179d73ce59f9502925a5929e4379484872d80ce974cc7925b489b1dd4624e45265268c464a8d7c06237f99e474f9262965e89e9c5439d3a8102ebcf7b228ee998fcbb68835c676efbdf7be77edbbb3fe41d1e373c41a3ecee45688eaf8a3cab7c927b887bad347e543e97b2d389e674a4129a58e4e289ee7a18726fbff6bfd9f688d32d2bd6032bef47dca44174cbeb4a272959172fea45fc344652a91911201040000243668cbd2afe1858b918d4848a5c960a8cb24e5085fab211a6395dfae14ae8bce39e9de9a6c8f31e6d8068a0efcb3d9d39a48908d0ef72494325eda4711f9daf4c5e875f5deb7d786df6ef41ce176ada690aeb2dca2b6d6ece4d0161c94439f1e10a52da6504a29a5a716b255b3b4194f4eaa1c73501f92e7714f8e51c6bf28677ca27c424a8919425fd1a13f525ff7b447f3c190a0b2be4ab0be38565f291a2ba18f2d8fd1178b028c02c4624bea972a6cb8fef3a09000e09a3f0f4a001ef2e741114089c96bb06e623d8f4989f53c2602b09ec7240016002c12cb06eb792243426b4b5f4f8f2d7f7da5bcd01787b3a588fe969a8ca42e52923247158a5bd65921c8c51823b35a0d22a3d8f26d78d8ac84a8442dda67357468040000009315000030100a858342f1784498e4dadd01140012829a4a6e549608d4280a620a21638c010246440000000064340100212dd91f2871efb40445a5a50e7d40ab9f9c3a9621a5dc0d18b915a418a7e35b30409a17004c36b880ff37a612f9fe95aa9704470a5afcad7dadb9ce4dd7f3bf93a43027689bdaf0f94de5947e8d2f1e4c60753e77c507cdd9cba66ea0f7829a1fe588123ea597b40d352b01a01378725f0998321c34f41b384743675e39f4b62d7715e0fbe6e1826275ee4d913de234690522234e712b86b570f85c9838ae4dad6ed9e22ca49538ed87954428ea875a2b7947fd01e5cf12f7b81b847ddaa8698ae29490faae8cab026ac75bc48ad34132b8c1cabb562f71ea4d896f2a8862f28d6b4a03b3162c88c65e9e89a9bfffd2bd5d7d8290198cc7ce6d64798e6cdca03aba779365897245d8578b8ab4e717e96e7c324d4c163865a069e4afa8e357c1af98eed5a4590cdbc41bd24514d203ea2cc0fbaa0d41179fe02174e929d75b2f22e03962d0e994b7b7904136a01574530a191cc00fe611638d0bd9a96fe1d3ec2f929b36135cbc61a3bcc77c3ed0f96cc5f9c380c77c8edfd6ab81c037cf37272ec68d90d2974c7711ec5ad3c5e8c9f0a98b2a496484008c967b9df52eb08555fc62503fc4282ed66544f03a02a2d90f5b064335d2386e026820e8340925e5ebe4d48e0a34f936ea09bc7579ae61635ae3d2274ac8274a61a62814747cccf8e094fb0d90e06c821820b0a7acc4c811a603339ae5a52b420a2e1fb917822a94919c0cbc13041c5e60db6fb24fb4ab5a339ec84ae8eb8c913595eb8b3cd1d4e206fe2e0d04f93ff0b95308bcd9137cf29ad736e8d1db7390b323d0d2134d08d181f1a2ed7023c74c71f07ebf9cac6a07fdcb88430dd1775698ec940a6c75671a7819c85d6d47417bb5fe7421d737da1dab09d8d809015849ea87273fa475e599f38b01be9323b1b888b836df938c9eeab0a0cface8b3ae546951f4fbadbd3ab6c53feac931d3b70c6f495a4f1248a6e881e2b399c5bfd83e7d3a3d73d1ff95b0735c61d1bc4f8cd675cb2e929ddcb711127f9d50587635fae128e83a9ab780c4d566b3562dd461229d4e3aabeb54544ee0189472f02c985d59a7768801c7ea3ac99b8e9b71075c3e19b9916f15f6060897b94214cde3c7a154548a708a7a12e9f3c157a127b0e6c9585459ae3c842141400e33cb455c3c9a90501afbe1a614e554573534a85f5eb4831c10bfce6a63e89cd3e9d604cf3419fb0c4fc704b481237ad24af5529fccb369143f8a7f2e5853bac1f26fe71e686888e2d4e8222eedd0b93c17d4bc77782e709fced9d6aa0a4ff7ee7e5230ed6e6a4b95d838ccc354d62ad334289522c6d776931d3954604f9d429597a2d13d75bd8abfbba39f5774525bf9d606bf3119b416a8662055917bb20b4032895f0685c1e7c09455871d852b9a7b79504f0864024d5beb3608eeb90b7b27c28b5307e9974c7e04430b837bdb56b8c915d87b255d0cebcde5b8560f1e7af8f722099f337b27b74160beafa7f32bb352c2063bd19bb9698b779439c4dea63510f8986528bf14f12665b6d63d88832600b117615fd132a806e59ea6bf55b40537478871b7f82cc62c84bb25601a901adf6e537b3efa2a1ae96c74036a915600d6ba8d4267ec0c00d17db048b01c913d0b557d37ff8685489924c059d05f7e3345814ca8f447e1d1f60028d653dd6e45f3a70c200b68bc06776e9641ce3ef7a0a158d90b167784427313781e956bd5c35feee82222c3ef49dc566b34853f16912aef380ae406fcf4d1cc41e412934c3dd426ac958ccff108c7b3d328301fc1118d1975bce4aa7f710a6e013cb32e02e2061d9b656d992408c1f93760f33149c89c60743e641300344e7eceb86a8b7c4c061a7192c7efcec644c0cea969209492db322d8c27f4d5da07cb95f72b0e5701719fc7039ca462c19d8bd5f41cd3294ba70abef429e992b0cb8558ac92ec7203adb7dd9eb11d54c0637c6bfddeec9430a4406847f63b8e4fcdd2e49ee9a89624c7b822a5d6890da80fb3635262738b83a7be594a70dd484ac12a809960bcb130f354145c62db955f57111c8d2082794db9e8e87df3165b89e77b0b9258ca30f7eb6f6ace7140b554dabcd8edca58bb47e49b361f222df628cdb25f9a0b35ad103a6d4ca384805a7c1f726155b9a5950eabb451c4cedf512b7db6c8f7a3865446170eee47571f45b4efeadda281774bc54da540734cb24c46f7efcce25926c36b76b35ad214a8dac3d9a1437453aac881580711ba0f70ae93681cd83c03254725c26061d30dff3783657f4e7abe4275c8f97335fe615ff236d0ea892430ec22646af2925e3962a55116fa922ead79e6576a77e14dc86c7fcf7af8d4aebb8eebd68121e095a1b3eb40ba96ffe02829238458111bd593f13a5eda5b8e80d1ddc1ce6ab654bb64db26a23c4049059b3142a066d15ffaf1ec96fb2674e42fdad3a7debf820100a18627138dd52328dc85e16773e32cdfaf306194e55c8f6b4008a70e3d94365fe4fd10d83be46c6b12843424d50b9c0e6eff241f1f32b8429d70b30a6361df388f88177e4fe4b97217f3fde1fb8a4209b59ddd8878242ac63dd2c99a6cad44ff2fdcbf4b7dda6e272e409e79678eb99d317023a487eda09b43f358d871b1da41448380e76a88e07fae1069c6de95e70e000336880df405399f1a7c10beb75e7103901772d4052c551a7ee9326fb541fe2206e9605546671e627727add9c7dd7cfd87ed7d00a1afc8397afa9aaa2594fc787ad3a503e48d44d0074a0b277068184645aa544a105be7866c9a23f73b33f949704b106b2266012252bc197fba74c61bdab140203d5c85207d3355e71971f941453aff6cbfc4ce1c0b288277709eac6b7a6e7c605c3445d7ec0ae7673cd1a5989848a023191bf4d5686754a1c1d7b2ec68d08cc97516d13aa00db84845bfc48e8b05e2da1bae4a0c8b12f69347c64b21477782b3a03191a5563647509612998dea7f8bf98c0904c464111a0a6258c93a5f2ad53128023136fff830695bbaeb6454047687fe834c68ce43a7e14ae93443b3082f9888c182797439079c468fab565da236d4589e18531798a778f031734a208a19271d4dd3599c951fb7e8ea88cea31c8713d861d4f69ab69a28683ff057b9297cb9fbe0225dfa59184960c7366bd74ba1eef1ca027c9f63087588fe308598d203f80178fbf2415720368299ffed94899871b4618eb2c181cb69bd8e0ac5657a7bb8251d1db420b1d28cbe08c7ba1c9fdfed9f856e737e5d038433154ba94a2fd91df0d36f74be6bfa9df8ed1d1c1060172f7712218cb1616e27e678e7dcd92b55717e670457c1b9c143b866885fe1091051e3bd6c391360b655111023f8ad154a395ee4de906a7a88a7623730b475ba8bf9a6701aa56738a24e625c16c5355e98ee076c100e1555444586d62318659367e4200eb8684bb064510161206b4a300ea53709390c4a90b2c9318f72b87257a8c7254474de6033f646ecb3367441929ced57bf4789cd5053db486388d2d0c4e9fdcd3eca2941c4369d4a5012e706940b3db66fd6d51b88fe671d03063e14d895aa7959731cdd9ceddafbb58a3dd32d16e1f9026aa02dd23b53fc7d2eaf57bb4a7817d203c66a69473a2a69fd27062eae68ac4040920756fe68baae622eb038cdbe93ca5c48346fac22c43980088103f9ef45a976520e2a8e4c66ddbf3dd08e172c016ac5650d5db05ca7554a6b5ce92287a0d702b971b9bc6bcdbbd679d70764233161a7d026afed9ad4b79992f550be69b0d8e2e46f7333f3dd28924f373f81e8420de1ac27e330e62000300a55c6b7b5c8bbec682bbcdcb8dc12a0c8e7b9431f6908c64b6b2377bd311ae274ce54ee22f596c33c34f3c49db8bec07fdb090993d8338496fbef7f843c0538c5ed724ff6c4b227e83a7e5611121d80380a4bbbb469f654149d384d5174a9b53940bb1a2572737f6b87f900d33c70f149790a0846633d4d9a704cd0c497b26ca935394a79ef87f46ebba7c7e208432e82449bd60f1e4589a814353ed046571ba774fd041369ad0eef92c138a02d36c56de78d8d923e524c0133e9ffed9757f4a25606cd51ce54e67c0b0b41a26612898e38af9c9c517fa4bcc5a2eb4c892b623ab3a05b6dbdd4aca5f40a89ea408f8ceb45322bd1b126255b68fc8c9a3f5252f239f205730839a3e904f1fc92e21a3f82d21cc852e307a3f66b169bad118a59d752b85ffc85b7cb3d4973d9da1c84cb31771409bba0e42c7710a9849ab010ad37e23bce2c9726d02547e1f69b205f729e0886494142159f8148e3dcc2a3f4fb082a6d04172f77a7cb9ac9653a08acae43acbef92d0cff5b1e10d38c54bb589b7ba37529515a7c2aeb32507e19fd3ac67fdf9eaff0420d83d51bae150bee0eeac2bdfe06bd586060212cc3bf6b23e505f399380fc2f1181e9783f2f6ec1c030dbfdb6a878752b9b68816e99c88119f1aaa3b36fbb4092facaf9c7d5985a64700b00c9d067fc937d967465e3b5c47430dfd1cfca8bc3e12be1330c8974902f64b498bcdc688f2622a3e36f6cbe291cec393b0afa4802b8af920e491010e9a0126fc42b45e199be63c70b627dcc60c3dfa9f3c144a9748fc4630121aaa0bce818a178f2f6d8d2538681aaf382f8b9bb31589f25a88b5f4c54d3938f57bfe196ea99a631613abdb784aa24d1a996603f148078c4c2a62f12aca529518687a074a342a20161e86e89fcf2a84a3c4df302d6e492453016c77c765828dac36a7a26df925c1f48770a7249bb1568b3da1d8c40a2603f253f2f17b8f7aab515cd9b58448bed3dcf6d670b28e5a2eaee2688fe840ae59e6a4a96d61402dc63825c63741242d870654c45d450dfe1887751713602f002f9761116582248c9fff057e3f6cad673d92ad038090593a00657b344565acdbe7491d4eb9400090bbe42da30936209c3035339987e3e5163cd7c748886f6a1a27af116a6bd58b55154200a296ea7b670b25edf3cb7de32580eb9db5511b1ffaf72b2478e3f58f4dd1985a6b63d2fc4bd22cd24e886ff6846bc724803cd876f3767bab01f155e7d85e3d78e77de3c6a4aa5f85eea76824b6aba27276c38e73633df2280cc497087a85a686ca8bc29b7e8d8bfb4bd6f0af20790f21df5746e2fa6a1fc5001be6e1e8cdbf2f7c6800512c3772ebf025be77ab8bbb13569eda77f17e0fdc11d81dedc03a819ace8aad3bfbd03f2dbf5b0dc0824e9f8b752e7a7acf48a14b5a25abb94fcc783da012e76c19711dcfbd106f0ead63aed77e6a1ff0813314cca4c616f4fa7e0fd2e0b98cf6ff1ee8861223bc4f739d0366198887f4c600b099f4ade1fd9cfad18ce23d921ea8077459f70211b314cca3782698ac6bbb8fd658a4fcc6653359a86e16381d1336f1bd95bffa65499073de28671588bef6ee4caa62f58c25cae42ee25d89559edbbe18538646cd17f5adc201d8a2390b942ca53ca75c533b52205457208d1de14ca81fc4f8cfcd618c110c07749c42412d3ab96c742d04958f7b0abad2d622c92d8dd3a1953d78c1d2216aea0adde641cadc097399c771e6be7c904d1c0723b2724522c2e1f2919d4c1620573a397d8e128d2bfcec57e7dbc4ae3d0e1c782b7fafae0f2ac123dfeb8b75fd086535dd1ede96ecdae77462e89f81cb631a3a2f758fed4ccab444813f65c7e93071d7739ae615668683e5435ae258db20a6923007b4465875ba6766e0f8d918dcb55d8024160c529bfe63b854e0b6e05069346877240cc070a0fb4ad47168138628b7dd5dc480b460e460220f94db4c29ef649675929a5a118dd85a40b9c08936ad8674f111e1ac86a9dcfa5d2c89e340baea475c22a3468b3223f527bfaea7a5066b656b4a7a00e3b0900708516bd0f0737ed857e9901e57c66eb04e1e92e2da5127a8183ce33335547e70a4945e97549ccdf25f09afa3f9a7ff6e61d7e0d7bbc13837d58a1d8fd17e728062fb843963df924c2656338923997c1dc1c97664a2d5a1efd19426b27e8ad0dfbdc49cb91a2d653e6dcc57ffb856ff66ef4349da5530fc3ad8701e4fd45f6829fdb29ad2ee7fab72cb877afe72f519f8fdff0c3e4819e5fdc0c69b4ef55ffb3ec643c891cd052bd1e7227d6b5d8afba31598e1e1002b7f46fa1e6090190b67238556c6a00365690c836b52c4225f4692c2afc1ef9a9a79a5729df72adb9a8d8d360a1173a238f0b81f0cee0bed55bde85acec05b42740c687dcf3a509f4c78be689aaa6118a7f5f83d5ccd81237e3f7fa64cc4ed9110e6be5db605f10916cdd36921d1e8581ea16fafa33e376dcfbe0f0b2dfe36dda2fd92338367f18fe27b5ca708752897a1fb0d3d65e6fb8b0e7472639b704815430767aae22bee4e065365801b3ef3c6bf5b46e0538abd0f4da1c30e4208b9a940ebbe4352a2065084b4d517aa329674aca15749bf4be902369db5571c1b27576c129ced957ac235257d129bde471f98ccaa6f85e41944d9be3e710ba573d776e79c10a87f14eceb619ebce7ecd8a04099a742e1f79f7b9abc1922a83e67b75c5e6cb4c49ace1133e7dfdf35f6f40d6212aa35687d0dcae93f67f85a30fbc650090bb6c700707c7c61db6d2b5df67776e2c863bcb1b20b1c2055bf1af0713503109f1d90986fc6d218d11a14abca1267bc3abd68afb831accb63fc456a9c3339567bcc8903f9fcab8063fc700c890df20f16938f666e330ba33c47cc3490dbf611f2911b3f53e92df41f6cf85bf09e8bf9efc3c496410c7118d910f44bb6e485473701247cae0c77e18c11872e0dc2f014cdf08892288ac7f866c82ee37e045e1b6953ebab4bd6af615325050c5d85aaaf131257385141f609bf8a1704f5027319ebe044bbd1288043dc381fe876d09fa36f42f8e6d9e30e11e0e5512f725f936bd99a2846c935843040207299c80c94d1994b662d329de56033e6886e5bebccad7c23d862082b91a9e98213911d0b4c29aed3c959564af0f8e8f45cc613cd01451729a3979d067a2c5476ca22d9bb81c41a3a00ca2b97016ed0f052f28ff085a62c42e3ca325a8648d603374c6f826a9e9da88794780d43ab1ca467e74883e099d27d752a38550be2c002809dd38c6c0c71e063024d7ae904e13683577ff082e356a61f6fa4eb97e86442fe03c0192feb63300b79c08132145e85e165f60b9a4e288c7868ff64d0befd82ac7cc4e10444d279bd508ea38fcd19ec7708bfe5c6620cf2f39d430c21b31d5de761f3165fe74173231d282bde9f06f7375510cf682e4435258580a9c7c0010f46efea29e09a4a7e9a0570e3424bd4fccb738032cbc3c6e6e2afd92e6346c472636c9c81a162a68a4b896f1858759eefa9194ef62d9f697de5c39cd028b800a8cbc0200fb3c693f7dde5d4e8c56c08661f62f8e0cc6be6304a670a7a79c34435bc70d14268d0bb9659782853d7e67a911661aec89bfdc67d9fecbac7c1b56d2ddbd5f54025785cbb7eb1b47fd9d5a089b73ab4a97d57e4b9283062cd6e8c1c8c70266bdacf665db6fda79645fea384fe74e754548b12bbaef3f6614d9dcf9849c15a0dc3408420c502934378221a8a8ca196def96b211710519636c5ae3cdd4afefefbdca456afd590fdc0af6cd7fe1774e0c97ba72556b7fe103b8065187becd103cb862f9db90d969b09836d61b9e67fc70fcb1b333872987b919b83caa1543a6063f93921356f8e65eb63d91d0d6360b27d4196756d6f1125d348fd4a0473b6047318b74ee1959e1d7dd24b5ed52898491edaa99ca369a53e4529984d48301383b95a69307bee60869a9a132cb558207b1f9f6818aec4a7634a42da8133b00d5a929c4d544ac81ba05992adea2e42443e05f691dbd5c1e9eb81af1734e8d3566650e96767d190e162266c50600aee7899ec4fe0139d69624e39fe0e3ecca637a597211fae73d806dd79928df4ac53448c59ee2d6a425dccee9cba7b413316deb5c74f84d9860aacb05803ec58b98fc64fb3aad7ab10fd27733032892452a97766674b234862e6e16425048df6832918928335bdefc783a55f1a2f82426ad39b44f77be47d68a0c9ca0a5e4adc589b686cd31ca8c56425b6d18fb3158b280da591766cd56b32978d8366cd0532b1cf70e84c5e5c5fce0b417af7e1a6e3a8ff7106c237755499923806c56a53586835ded30df2773fd682bac9d21416c5fd0898df5b2cbde8042bdc4d0f1b047a5a7c0c2f75099003d7146ef37ba9501acece34c376541f31cf15eac8bcc0c02bac434be1f1f436198c453d3171e569a2d6fe04fc95f377c81700e321c3df76cbf5aa7da108cb97a37238d844ffa635ab865a05fe94cedbd59546bc6bb816dc3ca1609059cce555739090b19532c118d34f9477df572d02dda2c405832cc710c823cf7eb33cd90cb226fb19fcef0b0d8a320c2f48b06e473e40b6c49665de8f7480d9f51e2c796fad1c1191227f7c672e92bc7743b80ac71d4fb91e029d71bf50e931402feda29868a072c3049ef6ce5e154ba312059389c5e578d0ce0e53fa47ca1f71b93ea10798e99eb423be3e92ada7af73cc72ab506cfa6d949d999b30ace5ee9d38f2ac0e7c8b0500132cb99d3d6d5ccc556e5328d8b815e4b25a37d95e5876c0e9fb422a8552afbc501ea3eea2286049d6ee49ef1b4e4a1e7a3b4fecda930bb5091aa546228160c3d045b3a9bc6b6c800ae5b197249c9429428ae56708349422301825bb3140fd3426603346bd3d25ad423ed083a04d201697d581f6184787f03b1d138c66e6b688d3069f7a6395ca060c9aef326b6a2669f5261e33893f29c355fefe110a3888fe3b2ed16e2a0017b7454e106edc624d2d9c6e4b6f596485e36c35aa77c084c523ba1fcd25d6f870f8c6a093f6ba476fcedb0d30ee797f326f8d43f80e08d973f11357ebcda3a6799b742f3405673f1fd752706f6d180b66a2ea9c6304381fad4cba629bff9d982dc609850a190f51253dc0b19812d051191e20a9ec76220e76c90b4c4ffcf17036e0895aea405c9860d03778e2f9cbf7c68a53360477103728d0536944566e7ad65ba2e86f3cdec444da6c89395af660ba6018a553da67afd294f21ad851624521cc6476ad346b709e76acd78abd95c4ad14a59e793e29e73c860fa8bad28663c6be6ca03e713aaf9410580b811a5abaef2723759bc2c0804b4b9109be703af3333006af20c16f3ca88727bcce1ae0f206912b896e70e0f3f95a9cd2562e2923e09dc6a322ea2c4e0e9fe01edd26c07483e8c67e2dbdbadefeef089b9ae1a69b370a703002508025817fe30e0ef332820436335e4ed0853f2472b9d9cb025cb135a03c7ea384da3c34611e396411909e9d50fe0959ffd03ef1d5fca6173c1fc2159ac1a97c80b1f1cb36cf53f297e8b9afc505531be4e2088f8f9c8385b78d850d492c642eebf309f53b9030e5bbe710b95ba483ed81ad543e57cb5ac92863e56f895bd6623373de04a3b97ac046d444dc8bda03791b4f4be707d9f8142a49d1f9e28f08710ed6881ac86d2d22becec5f6e5e82ff49b4ca0603d699d62e85d690dddfa286dc7626cf57694e5595d83724935789877dcce0cf2b4445601c941970c2212377a66c62f4df1525763c30aca30b266cb160e3b6a117f10ae52598e7b697b1f843508534fa775e8c940963de5cf35292dd9c131e08d3dc560dd058cca898db6807266b76f6651a86d0f075547b4c2937871602691bcefcb8313e47b2471c92d43c6715d525d8c5bb16cfdfa4cbd0be58fa5dbd7c7c0ae557e093313a04ec706f685107ed044fd44a9fed4a76078f2e0316ae020af8cdfc76f31406e1ec5029ba4b4070a34056757aa14a32501e80b8b9e067f0981ecd1ffbd07cf0119d17f85604f512a06c702f9842783990fde268636f7dcbcf4fd592f9b8375e89fb5c13cbc6cc808c0ed738b46d78c40b8355111bf6c260c511a590a5959aac9e9c1a91211653f8fa0959023c80cbd670faae3c668074ae3c1d0420de651476a11c8dfc9e34b258c87ffdf8805bd9b3b1a31427a82e3375314565d02a6330625cd98fad738d5b98cf158695b2a950a65ff7214e331cee8a6aac692d23cb1c791474ae5944e0391dbaf02ca6a37257d98804b4cb7d624cd3565e1e01d05ee2c6d2ded6dbfc42de3c13d8b8dc99864aa9da03105119b208cdf676b6f2d3a50dc3a4b1e43669571495960fe1352eced63ea66ef32c63176787b0d457c1737f7828a78b46cf8f9a3823a37f7786be1d59c8786edbd18ff28c9d454e1a8607b6cb1f0fa3680b5b9971e6c85753f9bbc81c53f013c4c2cc2835a195e2e40173b46c4581c66748480a340451dd70226472349d5c10946d58213c94b4d6617f66ca2497fb29cc1b4564458796aeee4694570f433d7f8d1aa52158937516e0ef9fb02d4a8238a81dbb513d9249cd00681908c656275070cec5c1b0a42c67feb05d9bfb364c643cdf96709567a3bd2bcacbb2b8300b2a45d9403c120e8724d24e4a786390fb8277de4ec58af842050ed85d07308dee097320ea1f009c0a659464333bdc1d270772dd57d821838acf23b869d5458f3a016eb28c4e98b0bbad7dfcff576ff5051f84f6f01c6aceb03e90b4ff4439d699fad4a15bed7551e5174f1c04cf9f49e4f2af4f3a7d79c99da122953812e9000992a99e867c176818f7b8ea4fe23bbbdacac3060b4cc41173adaaa29fec1d3c970c40270f0d5229c09dcd0a09d63f7128798baaa38145347e252e9cbc784ae9130e80d25ad7dab38529bc96bbe200f5123d5993d28e33543bb4562ab37718a1f28b512af02a1b5dafb3ecd1405323f9adce14cfef055421a27e1ad4a23b779184bd6193fbe2c4da78434e30c0fecaedf65e76648fb10cfb02b4d3f1fe69f02c082113bce24a6ae996289903cc20eac2aae2f506888d11950abb77ebde98d50bf349c183f93a6e9137fb5cf99964e1dec6c2a92b7f4695aeb92767b6510bd7612950653136805d9719db6a9248afb79830f83c0d9f5d4bc9c12a669fac18df63d801bf42d36c8875a839592d3b4635285673004bee48d0c8356a02f3c276c9e3e3b0b69b8b4734db490cf5057b43e593f96bbc563df5c5426267836533b13a0c7f73caddc6f051b1efac8f1bede171a53399599d0322d741590279ac4bd15d2ce7ca8ea65cfa4f0494c3b80ec82666fe6e799e369011799647e7e9efc52b3e4a9aed04e0e40d2d6037a18af319ebfd322264b6a9b88d81dc72bfd69cdfc66cb4edb12599cf48b78bc9e9ef8ce1e0ff44836833ff093c6af3f1981442e1a7a6fa62b43d25588ede8aa1a0ad3906fa0aff43e5a4561fc83f7989c06bb8cc14672e36cf7394ad9766514460c1b09f3962bc8698e3fb734c1057dab5b6a209df6825eef645992767c025595353eb481de1e51a36da05c8a1acff36a744fcd21de716fdbf09f2f1167bebecfa9bd27a0925d20b865c6f4e7cb9f48e0d85d9b89a3ffa70fd09d3838be4d90238c0f8f7f027474a2d7483b9a3c4fdadf52333b75fc9cc48d3dcb15809ae97c910f259d7c3420128c7c45f4ee999a963bf59d8b532544135f71860c41e32b76028448bf7d54352d5624a309a2f03ef02db5676d5ea76ab63eed65c07df53da33d9a7c604dd15e2cb43ff1f880480261b87c79c55c5689ac762048230bf11f9e2450ccf01b87e5e52498446cdf5b22b5c57ce676e86b64618325636bd283ad5b553d827d9d19e980b0d6bef752050106f46663fb6b76cb6b79a9ca5cd5785988d08ecef6a8139956f16c29bda7443a1719918d3f5be2e23c80256f5a24f540cae906ebdba5aba843007b7d11c5e053bdfa118a8daebfb336e14d91ead334139cfcfafef94e466952bc0b59ca4e396f57e6b9bab00786d2e0ee9df1ae96e113de2f6c7d0effca529da6b0997b0f340897f316d943047373040240e1caa70d3406d4be0d8719a2cfdc76c5e928ff6c845d33d16b1d1d05408721d5ec58449a9c1da28f06dd74ba0d863e24ba6aa0fe0b6730ecdd3ed590301351fe0150f160125f803efd6aba3a9553dbdcfe40ad6ad247bd0c42632e9937d5e7ab233ef7b6ee8555c894aff251f304cc8b3c9670f5a9ca79526f45f6184eb8ce30a03a9b78477903600a9382a172c9dcd70d48ba54dc3f13fbc035cc4b5d86f15aa5753b29ab18575b0a35b1a3b8ca3107ac5e9ad2b15b0d5234a5aeaacc1a954cbbd09c629f34bd5db1a01587712b40ecc619ea4ac600a3ec173980344349087c32f1dae7a0e202c042815c3ce3572a1c76de70ef3269b9879411893cd5e90fd53d94a61d85325729117c9278982e7a09edc621ce97d0d16bd29ba2e887959a414521de0b5e3309cf106b250983598da50729523effc2b8cd01ec8dd846f03212b8a5613896a5821791ca6614c6a5346f467053f88c509a0a1e77457185e9555bc5a8b3360cb6d96868ed4916e45a23e5f42334801ee484820200f1ba89d1b6d9f6c71b6ebf15346591a882744074ee88c7f9b46886bc8585956595332bbeb9eef8b0775cae08e0f90a9e93de3fab7c06d6e22a89ede2edac8331ff4470aed3d80a2b3747260ec7e8c9b4d7c75e46d72e8e14bbcef8ae7a4d2022526ecaad30973cd221ce87632972939ef3f0cf17e547f66548e6dbe80dae37e42ad4f08660d5c62d0d11b3a636620158aa73c8782a6b2aeb36a5a67aff75625d33b26d6947b61d53b7df289124ec6ccdcc9ce40648e218b3d89ba42583d52bd67f512b4a84a95330ec0abfbb5f7cb75c1e4075e0f7cf155c1549e2b298b1364f14370a7d7dd6ea5bc8ea68c5388d12f2d4fd24c2a12497bd3316011964bc6431c89c00e6821c0ea121fcb0208d0bcb18df21223eaa051f0027a297ceccef0b3970edcf4b934a622bdaf7b6514be115a47f5a70b36010808cf92933c635ffb1015c5a2a48d568459f637408498cc8c77a289d413cbbda45143db64b044c21e5f7c292fc4ea2cbad45cc94aa9079831da807e3b50c9744eec26106162602839885c961c0b4b95c4788bfb64ef9d93b6ea1c4edff05f9b49e09cdadd6c8217e2932adc31d83f9ba8dec5cc9795b5f5ac495c09fb459d16b6610483f402b08f028fc1420760190785b4200c4d81f2ad6dd2b7108042f9640acf5c4082d6e265eb34747d2db2e43289c51394d4a5591d4fa773767a0603af2af7134ff540cbd8e454ca0d6949292324900dd54292f25f3717a4937e93a5d427d7a2b459f7be5758e90d8f3a581fc9b5f93a5fea089d2393de1b43653ed19ecbf3c799d691a9ba118d8b4c51910e55c79da601a8b3b3d272e69002d54ce16ec5c99f2e6ebd082f31aa3ce9a0690fc21d71f83ce8088de89b7c5092623bd6ca3f5a1c68addccdbbf3840331bf3b5f64e66274dbfebee1701d6c91f8559d73b266729f643335d18b845968c0b0db0a5cbda4c2fc36ac826da38f7f66adfde65a6f7667aa2e2a83da3b050117c9c44054821378fb2c2a962995ea85d4eb0f5d8ff4ecaf2913671bd94e9ecfc096e9761219211c00e31f390353887c1424033fd3c4cca2052e7249c8a10ec35e358f0ce7197197962c469927c2033fe7d7459e7599ab0686721c8f7fd0e842ad3c606acd3253203d2190cc83553b19cbe91740f399365afd1563465d6a5a9345f961518fcaeb2bcb3941958009990111ac7494359005b1225535979db57a7a9b4bfc3b0481cceb5a0e8f83bacf9407f0492a4df064a206af93da215bbcf5645a939533177e82d0fa7eabd37f2814ca8d9a48e8bccd06d6fc2f4c72d68a3f671b23dc31d2b60bb58da65302c6d84a635f5da68ce9c7487c7d25a09baa5b4ebb8f2feb4c9526188a194215ce31f0c453a9d2e591b25ac98aa819b954ee4e00a5be9afae51f049d3b8896bcb40344c2590211e6d729fb4db0fcb71f968248d58962ba5ab7f940d84864ada1146fa85e325cfacb6a2dda44c9acf785ccf42d3465a21926ed23fb7a21cb5d158c83819f2e6c8282bd13b61c6f1166997db26d2eddc7b3fd5576b9934d8393710265b843a9fa4a2705f3ddafff9a5817ed6890a34ad0ad57372157d32cc54746e3de4c99fd10c05ac834e38452a3aeb9ec4452bcda2c7345a1f15736021bc09461b760a4218746673342651baf26739831b4a1c7651f7b9dc49bd8bc2a48f2d3a683f119d2fff47dda4a27e14339d68776c757af0f5e16309eebf4b81bee1b7a6883eeabc8b3a51e24ff43f87c668aea5cff7f820f90eef546569d1b458942c2a99cd687f7a9c9a6603ada53811d153512c1feb55bca9417d6e6421ea0a12bdf920ee4509fe34312f6fa81c0a5346eb4c2f72e2a7a99f8314409a717ecae4efa766522513a94339af067b216d3291891bd34f63938b8b130250dbf3164309b37fb70e0ee848973b9e01953dc3722da482d2a498c0eda179bedac053b9fe1ca6bce5bb928bc741d75246012c91132767b14579d1cfdf14a04cc24ddaf44e086f45013df4e04380b84b9247d1b3ab9b7235facb9372072fcc243b711e2c4851a7f67cc11ce3ea6595007e2b3b616465e78f3b96bed3646795f44d96e9cf3352e5266f3d9dd372a0418b9d514f29aa383b863fc517e95056820f84e2616691219d7a01509e930de717ba3049c16105f0810403c6e21e99e1011f53c3384158d1a4eafc0916effbe23261d4f2c9e021726b142edac2bbbeb891f86dd88bef569d91bd378c45031dd28da415df61e660cada264e85f5c30758309b8041b93b87c85795e8acbe9cbe32254578fec35baed2c735be00278a183c691f4a1c56afeedc7aff9581794dc4eb999600887d43bbdb7cbb1b87d777d12ee2aa8c5b7bff066629757fde2a531912dfe6f7c9f8b765212b239889202ced7c2db3f91e3f955764a0d8d18f64b414d7541f22622ba971af4fad271280ea5b6b42095a17583b959d13e7def7957fc1846424f2f9ac90decd13c978c2962f3668b090c70e7460980340d376a8835cd0e75e214ceb99a15fe35b4ac74c3bc2b2c9ecb079cdf4ef35c860dec5469883c1a44cef62261bd9550bd81d738551de2b07c1a0b9567051bccaf32ca6f11aeccde2618cb65a6b43c2f9fa454f84dea40d6d296a25483d907bdea5bb2342c4f5ec8a8e28f3815580654f4337e3fe82e5a2519702528f60c62a10f230c2075418aa0e5ed5a4074146b00ac1559a74b073024c3342517f1f3aa6e9346c7f8cf9fbd13705ecf8f68ad86ed2c5f7533ec806d74353ce652aee1c35913fb864b7ce3377cbdce88a8855787393eb02758420c1fe332c4ad70496c8e3a02f259092cf54e8a77ec8e259479b6def35c77ae60b9b334e6e642cc4809a05bd6b0a0db4cd615a3b992c039a94c8a43c46136d7f0e955ed9c009bae9c2dd39d9349a448768fbe813f4844e24ca7cab47f8373b6160cf230ffb6aba179f111e8e14b8cf0160b2bde2c0310af550a147e24e51bce86ddfda9c849941a671e2fb27d7373316816bba800fad607f79f54ac0464246d28c8032cc1ab4cc137422ba85ab316993f1fbac610e4c8cb00abe726bd44cbc738503799b5699070b88d5152495f0c2a114c55ce9795009554d107484aeb6db15cc51f7f08bef78d99a327cd01339c6c61dbe0794e6c59b903949735aebfc09808d1acb99b9fbee63baf699a881b81bf32e889a37af3edbe624c40c09ad71b4778fc1ae2e8030d6b9145170d98a079ea3ebbd183830ca9fffd7ec4bf1b31c63370dd0f8cd690a0a6aed98466faeb55bf9d7fd0340315f6947ba7d3288e6172551308e226aee472018d2dbb53ef9e2f440185d0f0481a46db819087c611c66be67ed1948e59b752867444886700d0664f06fde271133f35976294fafcd275e92e6e3e664bed08da5221f0b9adea8d5fb68a056fcd30b0f6dab4ad8655334d2641f84c71790116b0077c979f33083c316dd8c5aa2402467ad179ef00da599934b8235fb1989bed229cc55754d47bbd3e7ffa820a0fa5455de9f1e3206aa45b1fdbaacdd59bf23ef47c46c4b68904116799f5f6890bba07e4203214c4dfe80073870b3c16ab217a15c524e29e5c70b03f7888e4b3b4fa8118ee49765e507a47ac71033a2e6ba5589be25edfafbd0f0a7678eed8c99819a90cd0554365821b0b23070c66979a777c8f80d502011c689307364344e58b828184da20ef2f92f64467d742ad2b5badbe66ea52def35ee3222e69d2335259e57926dc5138e02e83bdf29c722801389c44b4613244f68e0aa055cf04245bccb4111394377869cb744c2844e3b41414d62a8d6f1be3b79209f13ae41f9d43bdb38287d8269f62059089e2c3a9817b3246f67492c4de39d476acb926070d56589b550c09a381c4995f388cf45817bb1b4db1124c10516d089939b2b81fc9ce4f802649070d377affd2fbe8bb4634b67bdc4947fad4a48a8253d0d34dac7e58ba492fc630605347ca86b7f2a8586cd84400075a80d5613fa222bb9df5234e6b2cbe524c9512a5409b9049ef5cb06a326389cecd3e7e7b70e7664374d2ec86c36b10c85a8f455b9c15fd194e836cdf4149c4cf5945c7dab22b7160b7546133ece7574c4c2b9d9ba852b161ad1def6ef53a8e3beb091e184dde87ac7c43e24c6cdedb7f5276cf33ced77335bf78dd842c1cfdd5a917044458945dd47551b56d0a0ba1d0af8c4ba81be5c805aaffbb1275289c9b45e2ef41fead48ff596a31de3995611cc9446d8acfb26b47a56dff71c78f05be27e98b6b0ac283410be5077ed7bc8d2707cda9cf0fc64cbb0fbdfb7a70b2677d2b8eacfb270d9f704eef33ad4822b1d31485922b9800d138e7c26ad18775a648055f48cd37da7960bbbcebecb3383364f4c4d7f34b0eaf26d10ec3c8a27489aff3e5bcb9e7a57524dd634f43b811fbf4ffaceb1a87d43ff90d2556a5adf8cd1b53df29e655ff4fe366d91210fab0b45dbf5211b978c000d039a4c62c60c096afe8ce4ae95a1ee06520ada6289286c3ad31a60ffe5d7caa8cb807214d8396b60d1082460066370fe0df489285b81abfe3d5cbeba391f88166e9148a3a4d710f2fe49f70795445a843e39edb842865eef9a4a82f0df7fe0284df565bcaecceb977985d7ea594aaa871dec1320d540f799d1fa22c1ce12f008c18f7384146aa8a57206afbb3375e37d1d24205542051f754c10f120ca23d633339b7a9e46e06809c6cdd3dace0c7bda10dddb190a48240904f3aee8dbbf40669152a8066fb175518133823f44f7d90fb5c0eb829a67fd459af8800fece42a4a75a6c736597e15070effc244e56402bea1f977340d53e15f610913165a12db993bfefaed605dfe29cbbab10edcce171a07d6ea5f456ff69c1485be64a446cc082bfd3ca55000cc47c307876bf02ddd59bc4886d5f5899b8fddbee557994a05f3e287bb8e966217adb517037d5ef3f22a2e26c0ac8fcc7e63e9a14d020d1eeca25ef6be705e0aeca04ba3291097bdb04428a6d05971ec972270a4e7e46ef795d920c0f0696f8a128048103bb64817fc2bd420265cd11d4fff2e50ca162c0476e068592a946828bf4faa86f2384bffc9d59ac2dbc4ed121a8a0de51bef578455ff9f4d699514a6202cdd82404935ab65fd636aac12430b9bbd94d0035db4aa08a34f6ba68ba986d850b1e0cbc4b79b55bd087eabb1df647d676f4a549c8a54fe7be1f0510109b4f6220bd8e46944220b863d3a894fae17899c2ccd56b8d89aaaa7d66c88afb90370294ed24048e28df1bae82cdc02bb717ff9d304bcc07138cdc8abecb346df8392313f86aec7e1375ee15b0c5e32c9d2866334cb032073ae5951017d6319fff072e558335a200894a9a50d099d914c541fc9d6a0a618c392f618f63b04835434eedd00bc6c4222d8b5e5c27c863b89e5f28395542608daad51c1a2f157ef8178c91498aeedefd98a6cb57c5c053f05af72c3468b56f749eb00686a1668592ca53c7751dbac176475ed978c2925b66a232e2a0b995752b53538b1ac2efc3ec6803d20dc37e66b0690880e7c0898cd9f3681feaa05e3efbe3028416b2fd80a68d69e2208245ff0a5bdfa7b6b3352ccc7bc7e992778b524ac2ad9a39ef327e903288c82b9212a73ae4c952096c71a9795266051c463e985606adc8d5307bd3adf13287fc4a46bef2d5659ec9c09c83add7a00d56d405e2f1cc07d87e9f0f1f10a81b9278ba107dce20270c51d50a0865c1c5ffeef887062c56396788c863d33139673233d17e58f4a9dab32198e9aedee39e9a6c36ed007f0c3d65cf389037b0c98a02c24738dacb4295f2315cad40435c408d57b1bf87895463c8014c4698b99a1331b3132f44b7055fe532126c3ce670ec7ef4812028340bdc4327a88531b154f0ebf78c94ff8ae1097fe9cdd146058925766d940b59c3d5b991e9a321aecf86a0f52eb5b173d6be2e8b931dbd6d68616ac65ea52e69ccdc2fd6185a73b11d6c9d995029194595fc390d3899b1cd51c7a9352ffe63de24d76a012a96bf9f37c9ea978d2c770592bec379f5a7bf33f597ff5fc54d1eec322249b3fccda8439e1d2529c02104233f984d04c44d1eda324debcdaaca742c2f5158ca3d9741d691e1c012a2e5ffdcf054065bc572650db16631d1d6c42005f6fca545c96c343687b8800fa21504dd9b84549ed3563af6a3dc71199fb7615475b77ac762fc613f28c6d56157ad1c3741feb84d0ab5081db807a19db564b5ab3549145c040df2bc45a0b3705c5c427fe72daa67490847dc304556aaee5734693b0335e3f039703a2892233c02d366524f80d9ebaa65c514df97da4aafbea4e180ed3b56f7a4196dc8f4a59a09d56e03bb00a04635afd31c5265a3bd0a964a58b9f554d029a76dfc50b7d6c2065407d5ee194302954d82d9a5e53e87db713681ff1b97db315f4541f09171dec898a103e55df286fa9ea947685af54bb3f755dc1893a378af6e013a07ed850bbd2d3e444ead867a569f8a493f34d1de5e4dd48ce967310ca6ff5e1c331073015d7decd8f22c809a10ae97a53320562a9dcff173300a32ca3b328a573f74a87bad76cc91d65c0f59de7445f568b75d53f4677f5e92ff9ead426630726a87d5df9274431e14ac429fddeabfd0ae7360a74aa39c1bed63095584754d9f67a279664c0ee78f31e98982a0502c86a9cefdaf475377a26a4c01d6b581080b5664d162f6e83668ef73a6453b624d762c6927ed730cc07d35522f9121fe4bb45b7f974ad26ae52933659eb1ace4c8c55e227ccb61f9d564152e61b08bf463ab0f866a49151357d16561b2979571cc97ff7ec911a9131f2f4b35eae2e6eec34a338f55e6727d8e912c8a959d4e54eead6d3e5e0ac6f48f5ae8e8140fad1f5daab839186d9e48aea2887e01c32e48681734b51bc3856d547468456f03f947f0dcb424d3943c55da3465b07347f5456c61ce13b0f6e04a975ea07acbc0a687e60132ec6755bcb2d9eb8a9504097485b7ff214b67f0a52543007aa2988e8ffd0866323030e95026e2c2585ad9fab2481496a0350d9c8e8c5eac3806af044a21c543342bf28491beaf5f63cc2366741611c4cf13a415cee019e39223b067cb70ca6b5c950f549c844b6e67a8338e0dbacf931dc2f30f0fded2d27765fb5380be10b55094d713bb29059f3a126ec4bce59ff7926dc3388069beb1231ac79c862bff82988983fe846f9bf8e33ace9e03a50841396cf996f7a0559045f08fb0ca1ee66545ccd234e8712120343d232eb62e5d53c3eddf424be17c4c4ed7e7e9c5cf309f62a4bab41c1dad99c0be19878a1946405c38a409af8c40ec95bd9f4923ce3db4637b2703e5874f90d9a11f2d76987bbf54d57452d0f344056a861e459e58de599e636ccb60707debfbf082a48fe400d37fdb69c7e44e4d0a8e6ecff9e4f0ce44009c3681b43cccc7eb824e7d507a241ce02c98279526b264e82ae3eec19643f9dc98baa2cb1d4b0d4a906a229a85c9a80751d3965a059e348d1a14b8e233ea98504318295e75f1a5b1a6ada4b5b0ced697c2685e8c1cb0c4dc367c50ee36e2ffd8e25f5e39a93aff9c779d2adb53a9799abbdb23d9f48ffef37e7582d1de8bd2b198b7f8cd111b450836f402f377c93a33b10f97fac0d5fd869cfeb8d793ac2b85cb302528dc31571cc488271fd91857d176a52290b8509eb7c8df25678d03f71ab6291b2b34d2249682c54dde54e0189a86b2f7b0f0f175144f5876644dba0c25f1f16fe88959cfa2bad3d4487fb669cab18cfbe5e969120551c40a1c4950b0ebc1e1179326ea37cdfc70c001a075fcf9b0cf4f11d49c05a58c1d39768dbe19f8c70937c2fb17445f8073ce2551c44e0fc36be79bf528d29614d00af094afc2b10ec628ab71bc312884740c75e6e322dc3f69e6a23c8ab8a3ef7b8858f4173305443521816feae31528024dc955dafa74b5d35dd8c678fdb3ef9592f5eac3d10b08799104833e1bc7c57976e46673bc28872a8ff08f3691a6944e607f6ff498bdc6454a86a568df7a0ed15b7c494a117c2eead4173ca7f4430166dfd7892f294c18d19bf9f6a16ff7c6e41fc143d50534689c243ba973e33cd1410e071a759b05aed2aac6b04e52b708d33075dc86877c204f2bdc02350013ea54d1fbf392c501b52465db4e845a812d8b7b0d0d16b153df66a02c1434294308c2c33b9ec679a92e0ccbcfd85d0de0172f881fbbad0147716890534ae48e0a90be8a871fb26513bec76970ebd11bdb6f5273a21209f8e9df9b19b68a1015f4d360a43befdd312dbea12ca6a76733568a5d582bc184da966e4f59408d8e06721ac6e9ae2bb146a1533453b011355729d0981d2c883c52dfc31863c65db896b7857c2c681bbbde75d8739f06622866b36fa0703bfb466c102b0a688834de3b85a2859d2b190ff1a452d8f924d869b4561c0c718e4d5b6b35fb39d664886bca19aeee8e1cf5d2c59608d253de0133ec8a549b8f24173e1c69f399104ee8a6927f8eb1b22edd444de5fc17b76238cf0b21b308cf185f753ccf5b23e70722ed70654a768dd72585b81d4482f918fd394b08d91d4d376823be6cdb109133123afb81eb76a4af9f35f811b3d1034d60f46e1b0f996d3d7786a4bebb7f246485688620afb19e087a428769301098e02d36a6b93271822125032589506a22c0c1f32f71da06f82ab32ebdc5e2c087970c647dceab51ba6b0a4ac47fa97126a37cdb3b5bbd3eab8343f6d798c39681abcc312a32d2c0f42bda613f1159da7b8c8f2969e0b3721cc0eddce774fe43a11f1f876f1127b23af4493f619fef24b43085acf7f04260fd53029d6ea7c71a295ad31e6be64595f645f7c76ef229066e0430659fe64320660aa8a5e9c1cf1a3daa16cbbef177196a918e239959f2447be8425019290f48a031ae479327919833b1c2e5c502806a53e9bd7051653e4705f1a683012c05f8a5fa09df38e21ab8229be06506eca1dffc0104b7362884ef1a6dc106f221408f32871765d21998ce67362961f3ca7c00f070f9112741eab69714343cf122920d65b9f1cf5ff1c4ca4e0756f8051b1711a0361e6db83e045c3cd9cefac5369bb6850389ccf04acf57ea47631b5b19e454c63f5f0ea34510265b5b4afa00565875268a1f386c5b4272dc8c122d5ff11b9b8263a2ca31bb8e776533b26633407583e6406e6723cef5e9cb9da31a8b7e3f0087f52af1c88e7d92606f6d860ffaa507857b24775eaa7e1d90bb0804078bf0091a2126bf2fd0ea9655a7a3ca4f46273483d1eef4bf8af1bbbe1ebabca1043504f57ece37fccc59bee3e4fd4e3e9fb08e785c8451c9e2de77dc265a388e47436f849b431481e14db432e72233270aaf8d528c8adcdfce7746bad1073044a79787d8130492b4cc7244ac5590947b98355ca703eac5a699b9bbd4bb771dd06f02567498f70bbf222d35cde3183bb1f3b8e0dfc9c44ae03089ee58fc38e8e78248fba3cf94da55c683073a411b7e90b969b50ed1b3c785678c637f438299b6f6e098f897f53196461c4bb6afb52099c9dba4a5d29f45ee4cf1569549c33c133586e4d84ba4342c9bb1d2c642371630816f2ccc0e70b76285016f2c59010c90db83a318df2748b2ebf630a4a3d6263d3c1c3e94c9c21a20e006a52d8735401f4a8a381900d7945c8169480a9ab167f08ca473a6ed127a869459bc9af178ad808a8b4665634f9bc6127f0241627ece8ff6161cece7e5c4c887ba0194d151e3e77b3ab3ee5c8f1848494f5b0cae58e4c49116c0c7edabe114075c8e336af6eb648ae6aa718e2baeb12c98eb6bbecd4c69ae3c99f75c9595319648df0c3d3b91f97c8b87b7050516509597ecb73c99a1d94b9eab36bc8332f6d39eb1c75c5c5b4748d5a923a55f55d9a93d1664a403e656307431ac7e37451ce07089ab3b14c111e53e703265113caec363c5850a3e577b070d3ca00c81d15784042f1cba18ab4ec32552698664716eba09aae43040796a065759194541facb9d1d5cc593ff3d2701da857d338dcabfbac799d3ea6b0c6c6e595befd3a81521700b9e305cacb342266d4e5b232fb3c211644498728ad1028b0a9393e341c62749501103723ff25f27378fc932af1c809285ed4918b1fb5ecd8366876ef4c3b6d9ed006f57336e7f5a6cf5c988f06b34d69d413133af4025e3e2f394835d36dddaea233fab61440df58e5c8422f050bddf80b7d5435c2b1a80967ac088e92a87558a966dd94cd56ff82275bfbc2eb7214132eaf68b3742534518b685912649b020395e3127a3c1709d7ad9a89888dfd41814273c8ebf0c51eb76fd96ca3185d478c0b7216236bd0a394ac869bd9b403b6f91abf29db087668b3a9b5aae6bad6976b048eae7b264029c8a4832f2f5ec98ba5a4bf95045a5de0aa5cd5531e5d872a0f397bbbe88710c90096304ace22dda8b08074e5a1723a87caaaa38236be99c7f5f4d0296f81fc441cd9b22c9aea39da6aac2a5dbc066e1116dfa691d814e89385d248e0215653faafe3f47e994365e0acabf532202ff058493497de9ef3c6519e770af7f09cc928a65241b749c1e0a55eee50164cba724f795c3a2fdedf7426ea003699f8293855153cb7062ad74ee9f114a7dece7b4ca724c80cdaaa4e46b549321c2aba86228e0582ccbeb65f2bc3eacc03aebdffd997cad3b6b3981f9bd63099d84759b734ee22697ceb0e03e1d96e585b3c7065a4b15ef6f29e6774d35291d413640747261fcf9c0d7bcae1c15f78c2e23242d7856f6beeaa9d9915e6174dd9630288bd7325a6e89aa5f9e2476a1ae7306d119869ba823c2327d6be40f70ba0032d6ebdfc1649013217a79004a24de2a9042b749280f00c4885b6a051eba0656675b4a3d96f2dc57775ba6fa4320e2a9f18e1135e49fecf703c085daf8c91341c2f8fe2ddda0cb0c37133cbf21857dd18727fa33d8ab250e38d76af85658ee81e9e16487985419b77515f2b36275eb19f611de820175ac93584f5eb9890cb5374318bd19a68aea7c70136f22a0e10439933e565b8b32a5d6287efac688dfa958cb66e745a1ea35363c25b6ea7fec95370db100550693a62aebf8214ed7f6e49dc8896209e6fabe34b70d3c41f4212aaa628a40317af227d4369272aec452322e226235ec73398c349c6ea7f108a8d030a1ca02f1564a158e6e1392dc0a8f1d0838321e94986eb2af8ed0ed3e11907db528bd98c99bc18704f194a214c857f1ca3e2163008600a10d7f014b5166f6b39054ea0acd88e7b385132f79d7d1fe3ddce707cc247b42ba787f44a2361da04898210e3e7effa7585f5c9390d230c49cae5452e2ada75f17b56e559781ed92ba438562076b5f90f8521f93550a43aa6c82af3ff3210f0d83759058fdf3ebbcb29adc28e55c5181ec1d6cce41409f743df30b7a80285256da24dfd8c6eb29087a7a3baeb017dfb2177a0e5266f475703c236be20848d8624c59452187f19d81855a33b31ed6062a928ccdbef93b159a9dfcf89912b9a679cde60eec35073f699981b6c508eb9730b71f79174ded861a1ab37c7ce91af51afad09a669e9863e261d9e0999708a6b4d9c336aae553c12a17f6eef5e17f5a20a8ea164ac2d5ed55d7e1b28002d9d557e8eb847692487198fef522f763eff00861578d14e87d170385d0f37cba2af0b181b61d7cf52c209e39155d551143cfc93a08201e0aec096c44ff6552e425a27063732e95f12508d01f63ef340a67a7b9b41473fcd730b5bb1a879003ff7db2547b66232868113eb2e3e4d05d21fc50dffa49ce4361d03a429aed57bdcbf0a71e398d028b68fe4b0895f0474fc19aef9bce1dc373251a97ed1ce9a914639031a64c153dd883ad9be982c1ba818c0319990893381786ea47c45217cd41b0197e9cb205821085fde01a677e74d7c184a76b49418453be0edc6e3e4ed7421c0c9104e5de9bd153c22666434e13c754d2c523c8c873cbffb6a2d75b518c40cc72f3dd36514bba36a32ac561bc4faf1a5ec024ab91b2dc1ae63d32a72d35c690d5a5ba24e9c8a92ba781e0961ec806f815f83a25602735d99fb42c8b4759202dcaa6627804bee3713d8c3744f15cfc8fd65d3f66e505e578b9f19a960841285792732cf49a41cdce682fe087323966b5348bde8934586d3119b946af9261ce248094e60408b28a3c3d4b11fc297456ceea3bf1918127da86eb527faeff59f86a093a68cb6737ed6c15822f20bef01b1c6b2a80d5614f3640e8554ab23df1e0ad9994331ad3c89abade2749458d62be912f98fa9bfeb8d7c5525d29b34b3c89c5f4d401567c36962e391d763192839f21af7c5b8ecde6a2b0b4f383ed2f9c1d6042cb5eb00c40ac9855a2a2259e7dca7d97f9dec0d222caf78e316554a05bca0d9a4d2fd48e83d29df7e50e80651d3b593409aa2c31d3a0c4b5d42ecb299997893b29018f7b7b0abf815d6667dc7caecd9f60fa300e03544d151352b4a2afb00d6f95196a4a614f18a031ae7bbdef11ff2a6123ac0f833a1bfc559995a582658dcdfc2ebf0ec351c852dc0453e2cfb384d277a2a01a45847c32806180542e0fd932b9329c59a4824e36e55250c43382b57586ce688ddc381fdc62c3f3ade1201e04292ca0a8544df456759dd2b6a2606f8bb0625fff9065df2e4c683584e1aa98edca9ad6889869672253dc72c2a0a99e006bf1c8cd1039a5cd1b2ef3a55a5f93723c474463455fa66f5ee44123118c08e0105857a8e03d3c8ce2b68a236fbd1d139489108e904c8a9da687d88c8bcae557e427318028bd1c000c1728a92d923a274101304fdb4a8b5cab5f5c01a95c6a788331744b2cd4816d491b0a839672f47d727104bb8b951ddbda5e13b2e6235fdf79ee7bb4a6e0e4d3ab11e085bbe5f6590a5193992e91e46326be6944737edca20c8f05f7d3926319c6a3b39275863dbdef6262bbcfb92e9f7c28f2ed730efa65fa549e821f7ec06a9b909bbf47af7c5f3acf31ec96a859d1d887771fdda4648abd5a636cc47a97e0cd5ffdd324e109f8b5930fc328ed84de88db398c151626115364a633e5fac8cf0c03d5e9cf13ddb6986a359b4c07b257aa6fd238d166b655ba4c7770c18abf5a6de9050535c4de63be976bf8ccb11bf1f5897c834d5b95e4f79068ada74651cfe0b97dcb1ebf928e9460166ff71b8656726ac2eb7e319e5d3358cdc0d3af046005aaf11239fc820c435e70f398efc103b31fb11cc359d5613ef11a8233c0331b58fe3425479dca0ccf1e1938968b3ace920273aaa4fdcd295c6b30d12dc62216a11506dee5d42dc781427d98340e96c9b0886855634481dcda6a9a69d33ae51742e244e76c4e73aa2ba595a2c5bf0b04df5eb6be7e0cb1d1c27e8358f2e237dbb077fd166e5431e1fb9122746af47b41ff630618d525bcf93fe68e148d2efe7ee8fadf62240fe828c40aeace8b196a0ed80c59cfd0aa8e082978a8a08bf061d7ae3d89fa324e1dda0785602595947e28329707b063426e880bb158a1100198ddc3ce0bb8ee81262b3c37645ec2650bcff1537ba516126b5919be596e85885011a4afc60b18530da5db27aa7b52863f84c35f90611da51698fc2dd2dc8b4d060ce807943887868796c0d47d7b8d8fa0a5a5b22031470d1dec1ca9a575d05f5c2042fbe0ae93adf12f9317e48406f69a7add64e16562bf48a201cf12011697e314ed914da30c16e68e37a98fb73b8baa9d7c7dc24284c1cc283faf4ea271ed2fee77750eb1267388648dc6489352bd61e941a07513c56b0bf48cfe181765d4dc5c749c600f4d10b396a69c9375496563c2d62011f0044d6182a91a0aa648c26f7191ddf159a878f53754c2d988004d11903be7dc82cff833790520cf7861289fb625e89a5b44448caf31420eaaa50dc592e080b1f5c496f2002b1c373110c3b38b91917a6354d0fe93386648c15242421062596ca40501b151e9fa4d0dd06d2f572c4f5fddbea3a00c9f9b0672f746473772e05be012328ee1e8060709f108f1877052c071738c3f03b6e553c7caecbe182ae1270ae4c14abb547e0fde87383bd6e68d2112adf473e365fca59ee51a1a442f1abc99b46995f6cc1289062e1297630b54a60eece1d1b246582d26c8d75628068ce041540175d17389ee1553aeaeb9d70e785c97aeaf2dcc2f2edc04bfdb8f3c384ae0097a47c106d0b5636f27a90268860b3b6ee8c4b593123c69b13b9e472cb91b21035d780469dee7418500385322a4f9a80e82eed844b133375b93546ed4fc19044af7a0fdd0b9351edd396f0ac2238d5b2a997620c4dc635faf5e258a24c3d99b11e25811b0f7b8b2dcba685bb59cc600b3c2f440984288f692a2d035a84e011c40c1c791425b4c592b1be5e94b968a81c42085181aa3a429b2b742b20b4a2a310b627e84d5040ad9ea4c5815a6c80f9d8021603150e369221725a0f7ecd5c0b89d456d60ab640c09727d35eba899b8dfd14a8480d00ebc46fac63ec5fe50dc14bd483d3a60dc78d1a12330900b66a650efe7e9f853b9f3a53088009ed49a84fbca3e3ca66597a06853e8af562107da9eec4208e74e7fd085be54c92d1cdab154decc2d350df2220465df743e3821d1aa85ff7471a6187e4aab6c5a5a6c06960006e846db4d5705260771601c48872c5220532d4a03b7a5fd4587b793e50affb2cebacf3d51251eb8c69f931d6e159c2723a4591f2addc9769beee50fcc1921f40d2260a108c8be3c522eaef6c38a9a0cab2eecca7596f5f245d18a9a2d0c2704445b22f2a86ef86b056ae1ddfa197aecce4b772e55e7c8d871e66964c2b17c6aab56312b7ae1a5474a57c245c545263757ac8f8c79afea74d7f52790ac7daa002b15eb94b11aeb56b587eb9ef17249b4b4843845e2ede491bdff5985e405e791d02659de1928e941c204ac0b8b3add73f73a98eaf732c738982d5d7a90bfae1c8630e4ce3435960c9b1343ebd67f6a1d1779ffe6ee1d08554f70ad6f8546fa8850ba3e28437846fa857dedf26b25b8f09f995c9bd0b2e4a52090d82acc18e1889e2cbfe9bae2e52e2259f58c9e2dfeda8f1f5627cf4dd08a0bda1cc1e9de9c29257266d5d0344b89f58dac00fce230df2d585c5b07fe8d08993bd611a8888e0f46aeee4c0ae494f39f82469be7713ac8cd8a9c3d73cae11de62364e08987345b29402a1f474fe104b4bf7f16914f84392635d1a477a3d5a36727e6a362dfaeef7f3fd7a467b2a7569a94739a6e69cdb31bcfeba8853a4de2dac712fd7d7b235b28a850d0199f158a33ef05428a93c580fc64b92e0c691fa1bd3ec514f91d24349987215a51dab0b98ddfb4f05e30b304b58d080299b92e31a7bb76c842523229e157589a7bb9eb266f39228797126f2949158e672649064086d3c26828c080bf21684d5e59fade81afd64da3656c4db47793976edccf2573e1eebe34cd8eae97144e284edcd5e6e41f73288ed8ee233adf51cbdc17e6df5895cbcb7c71b05e58e048c42d3594b5d1ab10d6a3569536ea346a46dfc0db27a8d117ed793c8f54b407011b34787a75ac278868600baf803e60761f7b4241ed88f2779206db70bc3b7d3305f0f2aafb3bdea9be36cfa70aaf26f6b12be12b80bac67635e47eec8ecf5b5dd3cf75f823e968009fc6c8c446478ecebc30d25045fb9c7208e171c451c97df97cecd2e9600b8abed86da240da11df30fb8af183bec6f76de7e624305d2215ee319654c601a2f1e3b23db7b1dd34108dc6f2887383512a8e468c8d621a159560cabcae47f577ea9c1b53680cdecca2f4b64ebd4e00b35230b34a254574657f41b95a4485702cfdb21dff3c6e9ef4d99ba75590b9aacfbe8cec03e0676769f50256c471d576425e184d3e3e66a38170447855253a0eda6695a40a4f11894fc43b6966e6caad4e8df6374c511aec2d9b1605503821a3d6078a66a1d9507012f35354ef8c853422ef953ff8b702a4e0f2f2a5d2723840dfed5aec990d61f8521b48341d21a5e847e54699d8361f95ee2bb99015f424461acbe286dc1673f508b20e09428acaad6af093b4586717d896190424571fdb70aca8e263e6b7d26fc3cf9b61ab360aa090ca1a4538da3ea32594c556132cae0c295433897f54cd0313a321956cbafeb1970f0c42c1a542d549fd2468efda835f579c8c07efe2d599c832b6a7cdb3db767c90005567a231f5c81a66a0255efde002e56c39eedba79e656ab6a27f5bf10df4127c01df57d048acc03763ab97fad006f00065cc48e3fc3161929ddbb379283ab11c52493fb43f6cfd07b8146f6df970e8e171516a4659604d3727c4922478c8cf97bda65de91bd4c08511df0919efcf5a9eec0bb380d77091bb0770d7ee772290130d4a74ec3149c26cbf2194429343ee98ea69a27fc946bd13c572d9b2b47b4d37fe3b314cd7e34ffbd858f968cd74d49b5aa83239fc709203b2dac6e0aa71fb28b91bb30f2e2e46ea0c9933b60bd70474985213fa35289ed83a434046b123c156595d717f49f010b6c95902a970e55421467767f5d05d35a13f26de7eaebd39113bdaff98f953e4aa7a24221c3fc4140887ed54d574d0450a030e1bf0cf5ec4ca70ffa18e0fb664797e1922c2e4343873497a933a055ba0f0b5764577400c6bbb61c8df34666ba577d84e67138df98e4115915d52adf68aeda71231bedbb54eca82a5920f264fddaa0bddb223af28d4a29407427b2bb7eb39e568d86a4098feecbe5041e1866991378288bc107fdca7c0311ce457ac299f9138a4423d8ce14aa06f04e11ce1e015e6b0760bb75c2e91faa0839c564be1bbdc16085defbe4ac43f04be70941e8ed10f02e426880d4f32fb4700e96c4a2229fa5f7091d07ef744377e3e2f4d6ea8a2d3658d41786cf737e3d2838fdc1731060d60a8c168d9352866839f1b966d2f49e5192f4fdd40cd952f942aab7de3d72cde299c7460b533d922702b44d3f83075bd67eea47de0e5f9b33fe2c6c523d9fda2b5e11aaf533eb8c21518a286d67b4f1ae0b192dfee6c8e08f776adce38efc9d00a1b68285252eb91b4861b10a70e73dd7658013b08e4621bbaa6ee24400bc45aa000eab79458af2ba0bd408d21bfae34d7b607c004a06a3a98097e7d72774701d2abf921cc06e060058d7837cfd15bc351a073c3980a1adfcdc9ab391909e5df1522c3ff391d4461469c2a4f3bea65af0d69fca969d1efec2ed05e0fa73a1f76010929920ea8e8d8a6f85df983e3ae476c32d80a3a756a062dd4ef3c503407f77b5ffe50209ee6e1c68d964da5a9dc2ad34ce3db4c485ca6dacdc5a490ba9f3b88dd9bb681c71919714c0e4f260d14302fc50d8f204985313935c4efd36f33dd5b196c79f764f19ff40fb24ac019b83e109924b21691eef25c2a56f7535b81e7f80f3ad3706f0b3364e2a0e4c474836d72ebe770403822f35381af9969adb35ae06df776b63ff2e602f5a238c127c3635d44156d708635cb8a70cff74caf8874f192340a21e18e6365b0041b89a2c6abe846b71e27ed472506c8b834a198f162d43b9aed0882b25cf27bc3d7b01bc2b07f93b8419d3136d96331ee1862a78f15933e4ee7cb8b81ee36fb26bf823351daa31d803dd31971b8fc628218e8f142658a60aa9cede7cc44c1951ef98eea66e0f74c7c0663e127080368954e25a4499e685ef98f4d35303e05c05da245e240ca4748489a2c78a261b7a1890088e35decb0d1cc92ec798ad632db85e16cd956a5e922a75c03314300ca516ee3bb83956b0dc38738e3572ef3a792ee66ae38b05c419ed3a311413ce0898228ece19d45f71725a33807f6d3b47c4a69f3981ada0838a5ae79d4f6e9e1f07fbc431e007ba2810ef5c2d0f20820f911e085158482d651de746bca6c6878f07e43cbfbde52d85e50f5954379fe5488e4b81739c45123cd9487f93ae2c8e4ae59ebf3e3ab5489aa7a5831b55ba5c01273d68223bb88a7f86cbad335c7aca48ac961494a00f915118382f1a17ef34fe0803aeb778333f68a25f50d2bd290e1d9987262c8d6abe8db255c4f0b0acb2f9c43a0b878dcbbef742f182503fab150197e35b51fb18e551d14175f86e6219f67b4d20162b8bf9b4ad6d5258854037f063fd61c5fdc8a25e913115f499af968aa4014098ee0804a13c55e85c7b01628eb5614fd935ed0af1392ae6ceb289efd715473e5b423ec56d1afe85acd2e76065a4a6d35cbf7359b76c6962866ddc3dfff33aeb5345ad859c5ec47f451f5663b0a26d73060cf781015335f7c4ac76aa3a1fb68d8b8d39cdf28e96f5131cf45b18700ac5736b57a551099683edc385808728b0d9617ddfe141652c00918f69d274883045e90c963ac0bb428bfcc3b8d7b29ef2cdc199cf199ac32e30cd85f8299773d00476210e020f49da73d8348c62e92292e61c7cac2d0f9fba5366605d8d2533f313077f70f325805561cc73b653a7acc7998ed37f6602e0b4a50eedcdd5da00b45972fb133031a63e385f7fd28ae5d1039cae9556a2970e18aee8c17a21dcd36b7fa904115b56974e60d78684904778c6fcbd4ffa43c8bfaee8f9ec8db29a60427e266fe24fe0af9c9836f495849f4710f21e27c80c483869925103a6252dba89b725a1d6659bad1935b0cb6bcff1a0e059c06f9e8a6017272ea36c1d2cb52a4b3c4b5ecd5464e747768549dfc7d91634d1895a4dae36668b4450184b825fd4a9ca61d03add78dd57b47907ca2e1e23b0648c225fbce9b0ac49067fc8fb27d2a6028e5d715d41f7c2b76676ce9d475931d4b80080434b4e582ce0e207e281edbec8811e793c8a786c151bf21023ad85bd92a3a5e3d5639ef53f01597ff30fa31d73a71d1faeb42c9f245b18151a884de5e6fffcb30fd42622d7b1707d6981128cd6560a15bbaf795e14fbfebb9145ebd9a2172db8402fb3b2a73f6b66e94414e0fe1a6ed3b6ea8d345b396cfeee0f4f58766df749727096c1754cec27f28ca0fafa4a170dcf1a7e0b6a3ce6764026d81592a9bd73a0bd35161487f310f0003e47a5cfa2b919c1a24d129bc43f9ea12dcf7a4d0a85b98a448c368382872c0d646890a1811c0de4b03a665406a0a3cb19def25d8f927daa51e37b947af342a507ef28a5c7179782084830390273a0d5c5a2f08adc01b50782ca00c44322b4548ecc790c6fca705e7dba1de07612cba9787a857633bf5733ae5263e29e33434b60c0856664b1d7ffef5383d164c99933d1f65fdd5f0b66322b135fc8453e7b2f49867f9af013c996e23798af8a1fd5b052a42cbf912e135afdea31beffe1da2f0489159c1c41f061efa387dc9efa66028ade130528a927b1aa4d21a7c942d35c08625d3741f65381a66e6105750d2c6d3b6d76b5a3a335b6682b072eb23d6ab114ec85c5fcd5f79c392588b3ade826a2be4de62b4b944ea35434ec09ef2c33e7d6bebd17b10603d153514aeff1706a302db44a4af1fd4e3da0531fde58d1cfa350210903f7e77d6dced6081d09fa10809fdfa6302c6b47e218551c04e6644124e8a4beb43115845ca1613af476cac1becd36667807f7cd693fa12f61eab7276690fac0b8a9b835f7694ce2e7ebf9042d4d69f9af3aa10d14ea91b91d360d99f9d245c2e8c53df59d4caca1c8d28c869cbfefd7c30fb6af9029981d44a150d744f4f9b9c5647c428f66d6422af461d113efcccea1ad9d7e4ac96e687b9e79e597ee3bc652a72b1a4ca5671e95d9875cbc8343b71c1e2e20497a25dca6f6b443d79a68cf3f000cfbb718a2ff05315868afd459015b3ea5357bd02e7abff469ea6ec8a0a23625d0a6cb8b9122fad5147eafd7e306845f289a25c181801a05d16f5f74cb81f4aff6cb9e0a54972cdaec467fcd39ee8c122fa1ff6e7cdeb69e86d2d9286fb65c0ca2109c98ba7f988c81d8c938755be02b3e4a56d12df9e29e998e781531f2049a4eaf7a635f056994a004641263b7d5f988e7799a0181afa3e5c5f8a9c5a4fbac6b84bbc40bd27cb3ce57bb0d412ee39a7c003cb9e0e8753986fc38430d1dfb19ba1c36379162a72b01e0ef933699eb1c6d6090e4fa6f3d605f01437be65c6794944cd48227063c61e435f28b60716b8139a6b702251082ed6d2639c7375fb70276ae1560779cae025d7c96892fd46509648c881c8a4e6c88c0ee22178f76809dbac3af51dcdb601aeaee910972c94557044d76b19c0d68493d062626f58a8b3b1b20034c7c9c982689f54c8c253241fbf41b312cb6e19271c2d43a3729d9e92bf1a0832ea44d5cff20c91c775ac4a4ddb18d5e628c9772418c46c928e6a4ab10fbd398e26b95d328a5637937ac86d3229eca5e5ec90830628a9f84f80bb3769dcf36cef1d2007d9c8115de09238dbfcc7c6376e57e76cbea3d9a6a0261a27edf4f20e9ad1cd1ca081ea4d6e77d6461e54b6547a2d8a1e8dc5f8aa010a3172cb048b993958a9d8e0462fc7a1006c5a87df697cf73e393289ee30fcf624251021f9cc185a4c8d9d08f7588096504d0067b1b0a378ada94337af5f0fac87663722aa764e0554c7b3811853e957edaf4d7a0ce9d75dc3086b02e23a89881af2dc22052d9d31f672a72a7ed5a8ce4ff8849a9d8e9c1b80b552aec7abbac928ab15c08af0b30778520d5341693b3def3e273a92b0515d3e373bd51c610f0994ac8077832698d55fabeb19a7382facacd92bbae4d116bb582c501eec2a43ff10c3c861270715de32c77687884fccb633af262ffb76931ee1aad21de625803da455d2cf30c8adb5f9fa50785fc4217252f3568bef3bdccaa1acebaab7457e6f048ebd0d57e0c87ae11717510c994c5cf27d275b76091cc77c51b15b0870bb6353f329ec69dfc2f48e70c638d3d4488e50aa3b41f73085e49b3ad4bda3572dee29c9d6a6a7357aac6c948303ad93f1d60b3dbbf779d07bf9c8df15dfc05e842860144a8622e491b770a95696f386d0f9c5eca6f3fb3f08a5cf81c9cc64ae45d38cf2cc5c8390c0d6606a1d99c97e3c58f84bf01ff77c9e6128697d889b6843ce2ee6f4b17b8e35f92965007f9231b842f1574ad77a44b15f313ceddf483f093e125aaebbb8267692ff0f0c1d41164e734d5d5e53f801865a9c3d15ff963c9601d3023b222272bcccd74633eb7c8c6a044d92cd801823351b3788c13822832a097e9782c763ec2a5d57b00d008e65d8977029a47efabd81603934868981b2f2708864d3f6e3711518eec2c0ce9757ee829b5138ef68586df391e338be81c45dc0688d392412abd8c00a61ed81650abd334ba6c312c9bc217c0d2e70cd5bb8dab1c060fa2c901cdc73f77214b16e1aac5f2a06883d253d9053fa8befa6dafcffffa5f36948ee037bf904af8d7efd1e1badf4ab8a887dbdf3f9a4138fe2d4447525e3fda2a400c268a4481b5f5f1dc769bc7e0f87489c9f695109d6ed5fcbaca22ba65984d87c20f1a753cf2e226e33528227ab308a592113490202498f363cb4e8ad9504b666b27e4f26647c900e23d02aef39939fba3f44fcad0cfbb3153f734027d06134725d46518a1832e0a6dbab071f2ece65fbcaefad2fc68bf9f1165e1079e7dfb3b97119369a6301d81ce4e10752a08fc031c88aa218f81d24933a40d9918423cf582bfc2310129cc787d492a415023c44c85f517b2a898eb14d90d34a63cd0fdad2d06a5839ecac7c76abc63f0a8be765808682cb36c5a528522fc4ba4de7afeed472f766f317c5240a5d9bf1c3b4c4181a6e9d2fac526676bdb9f6b53e5c60985c49e746483cb7e71ae61cb5721f247c20dc115e7a0d6c83699dff836c8e6cbab924617768119ceab0691f13d3756d40a7f83742957ec306e3bbc23218469e9a9f81266c16e93ea528380f54c1cf9fff002c6bb18f8f2d532aa019f7f5045d6e742e7d7fee747546ea1577369c16e0e41ef242d017ac43dcecd465404482dbdf9432ae11da22291c6be4346cb1cc4a7c50ac39ccd354b8f1b38dba7f62a4edd9037065c0ef4b0c4505a8b6f2c294d0996d28c5154f5c6a374da83f83f698cca182f5521ecc2941ec61cfea73226e98d8b181313d76782683cba7c312e2729e86085f9f4001743469e935afb92dff309176bc1219dd44ab74616b5580737a98e58455d31443bd3f449e184af11c88c7bc5d031bb5b27566c218d2870bd01b042a0e8868329e6d45f84d9e1ba2a72ddd82d42d3156c1cb946fd5144bb382a1eeb1c2122a849cac6ebab9389b58e14543ab9d4e8f26b41cd3d03a74dec64563bbc2bc974ee418feeb5271a73c92e616e27e670d8db3abaf38a3297533866772a14a40f7f5e0986f820a4125466e99d25e744211d7b26937f57e3bbf0e581ebad763a0206e02083559fd8bc7b273bfc419eb7ecbfcf791dabc804f146773c2c61fe8e58552e6616a00b9eeec1a81384dc73a09811bd5f504407e44cf8241849054555653aa1c84745551418543539445075c0f7781a61ab31cadc949d2830da1831d3b9e83782e1b837bbdc13e811f42b97bd2ac40beb3bdb58f45832b5b26f2c9a71e84278684a1aefe16983105f479ab70f9d527fe1d751e9acf33c35a8dcba9ecedea27c97e8c63f1d946080796bf3697e123c77813c4719d50ba35c7985029811f7dbf301033e5200eb3b4fbbe875f1a6e3197b5fbb3b89c417175415e86d8a00ad768a077ef2026742104b2901ba22406776c832fd42f5f7b01b01e2e3fea8fea94d9685dc45924ac5c141e68cda6467f6596721ce32e5aaf2ff104b56077edd8dbf8ae3ba7c4c0c5ce2eb667d781ed95bd41966638bcf7cbc8de2fbc4478250ea4ef459f61df26a1711f15918d5ebdd942e17ac8faa6913a04a8f0bc0c00680f5b9409ec3c1edfdfeed46e7776c0ead78fe07ce91c5af42ad6595dd636dd25a2cb0890ef47009d01c83ed54d3d837b9f5e01dca3b6f7817895488bc8714116ed8e8f80d250cf95f544768b766ce1f5a659d11083f516207e394479feaf29efa6df2bdc07545de1190baf49c18cbcdadd401d8dddbbc4ebc1e6ed9d76c61b3cff961b91075d200964dd5a2eaf1923d28d2ac0c1962d17d81b46991fca6a53c99143e8b6e205a7cdfbd1df5fea86cbdd55b7b4a469f0ae66f810940073b45e839da504f25ec7b8158acc5f0306a2a76920bfdd7441250be90cb5cb24fdbf4073adab293d61a7dfab3b464e4291b3ea6ecc6e7742d1f1699a397a81f59eda805829f18b5197a5c49af0ffdaa2b31ffd01e6a21d766ce8f77db34a3979affea4cddbcd82e1e35a3d9041b759432f23454e363b5a34ac1b4a00328eac0d45b0819c12f9f1b03b122fd6662b8b6747a344655e8fb639239e092a930e861988669d050c1bba6fa807a81eeb370e5a4142f7e54361d66c469a84e8ef2438bdc60f48a5d279ed1b2ec69393320053a7b03cb0f35c9a575839b58676566d4a48c79bfad7474e431f3a2c7794e690815c9776923853f5c347e303b3e94067f6a41976c0f63640128fbb9c4535192375d59df22e588d3b4344b06ae8377982f9de295d1cb246b012b1cb7445619d44e036a17366d5b3090ba4a07593a4d1e9f8a47dc4be0d8e28c1aa5ad9b1855ab88745859792efad76484fb5c489374c01b0e69d4578165078ef55ce12a949c9acef73a851e55b3167a03ca3e63899c5c37499b4465434412b2b7dc5b6e29a54c4906be067f073b06d12396d8d1db3bc618638ced8e1ddba0d027f87f8dd22f65aa91e94b0c090ff8973692c57470d4469a98803a265b9043111d6b39860e34e8ae0d831a9bdc690b0d4ce47eba446904b99b92a090228adc0f97ec152840824bae143d80a4075722c2a1328a42048e232abc3072d4742913f6ce277870c3912838e7e649172d394cadda9a4e9db4430cd3a9294aeac8145b44698a1dafb85c2b2f563b3342f065891d229f99a5176421f26172bf7293d3821c8ab8616ad1a0d2859316549250094886490b742002628a49be84131d62521347c45dd9605a4c7124f757a197d2a09da4133154da49c629a81f9e8831a589885533118b498a542c0a18a4b03264763ee1270d86c839445ef1871fbbd341e4f57a15c910432847e06f2f24824e2c6d8b114d1041279987748ef89b6cd7e528eabe80ed3ac698713543ffeefabd935846ff31a48f53f7e971cd23be1a5b64fd2aa5cf8fd467ccda61350b189312f7a0c157ef3c31852dc295dec24a124a69adb516e5cccbdee5c3a23cb3e859f2acbba23c9f7e9017e5e8c13c3f28ca28ed1691acfdf91b7d8ecee9311f2017e7d92d87a01272a053da256ed5da819f13936449322a29d532f92a440ca368738b544ac8719df83872f4f9ee0e0e0f57851584220fd7fffbcf69654d589b7536b36c9bf3db49c772cf2d72be7a9acbe50324292cca20b8196c5251391f6f653e3e3d2ed85357ac9e1e9f1e1f1dae3f94f567d03eb4e0da64b3dc43b8444eb11b854b5465b90e757fc66ac6f3a48430202017aa7b165552ca949a7adeb445a058fe9418c6226d8274fd3f9859e082f21a266bf729364b7c22d59035b6c8fc556b8354c3c946498b2091cca369e4ade853925cb75192e47a9c4ad337969494661e37f91acbbe7ccebb514766aec5da16c15f7af188baeca6b5da050c674bf942a65fd1dce13a9c753aa5c17e50cb05637f3bbab48cfbb1883cb19e584faca73b94fddc88f84fbd305324dbfdf8ee7e3759e9ed27bcf504d64f26cc7fb4867900a6ce8aa90c577bf8e4e936fc9193562432ed274f5837ad2788b8c0a22d02ad784a3a979096e2572f2bc6a50f28d3fe7e1231ed2712519d7d7ad280f2079f644449b1dc6dede4dc2edd7363ee0704146fb0e004049d808072708732271688363084880b5c1bb5512399dbcc361e42d6fa288223a804135250c147171c41259890820a45808070049560420a2ae4e076fe80725682ff682fb588f4c125201c26f0608108083ab1523d116793f3c192d09932c1e5e65949ed945d31b8b9e9808c3b946573db88b86deba4971856da171929a8b082100f1faddea2b7901255656fe1c47ab130e7cb349f92a1cc96ab5f380977e0b1a813bfde3077c8fee4625afc677ec996b2a58c29e3b61a4e16db28a9e1e42d89bd8a922fb992ff5459ea15ebe6735abef4c54a8b61b8f4e15db16e3ea715e6765eb16e3ea765a3c486892b29ddd5dbb896eb311b25359c7e7862fe406339da28a9b12566b3589843abd66d8d57f7ba97ee39aefbc6d30331f783534c88375a2dc9c50ee631f2d7ca5c6bc7357f3816a01351c661d822401464de70cfbc613debe63b05155610e2e1e3bdf45b6963b8f44d484185158478f818e3b60929a8b082100f1f3d402c96092908ddb4763e38a5d5e3ead99e3c613a4195c85556b4311c911ae7d8707b70e5f637cef9d6e7b4fc87b6952d0f62fd56dac63172e9c3a6db397efcfed8f381c4316380e2c3f8f2c6c711ffe3e7c468b17c281b30339038664cb612e5712c4ab5205968e14da5505d0ad5f5e8133498e59c734e39256a7ea89fb2bfc26d6e0f7fbe6fa977fcb9f20845eebd78c853af75b8e21ef27ba452b8478b4031ea35199b19a7699efc6a9edbfc20b7a8ce9b1eadb09b34d623d623d623d623d6638919fcb990677e7aac5a6766eacc8c8ac6ab54f3695495a752d5aafa5a594fc363cda854aa1955ad5555e1cc4ccfad3275557dac64eef7d4998a7b3ede089a41f3af2e1f8cf56c800522492e4034c4074df577b723fb0cb52da35ef75c576fe57cc4a4ebd08fb1187def74745c0a453b8ec5da66743783ae83f5c07a603db09e0bebe9baaea3f4530de002cbd49bb427e55d4a611d75f504d119a59424a0e6cb9eaef64f6fc3b5db55c5a5e4a2e29ae29a38887b7a1d96699fca3c087924feeca185d627e571592a7b992b1e8258cb7efbaffe76f110ff1ca77a7a2a1eca7a64578f663bb4ac8168eea8ef855c7f6a9007d603eb81f5c07a603d45a8e69c74423cfab51e5c605c847808f58075af799d500f9810a52f04e322d4033e650ab9640a63419e37c3eb21d4a3c6eb57d4d1b8a4a22ac42356c9f4857808f18854327d211e714aa62fc4234aa62fe43f144798b7e806cb54fb524f69b066b4116b35a306cf80319a19d57c1b0f0613b647191dc3c9828739373c20f7eb976708fbc094dcb98b6488bf7e75f2a1d7828742aa88844fbecd091b7c3883ecca1f1c93bff8de395c1f017e77403ec717105720414c8585101e605e1d3217b2f61fb57267f601940b33f919fcac89b81cf1941dadacd5aa79304a29e5e3104d19e3cc44ad0c6f11b24605d73db674e9f187a4bb7728a4725de518b190f33f29a55c4d6ece395b06f6930f3b06c43c045f42af422ebeec2d291b2eec2ccbb2ee96347a4321a394f1e1c76c764f59dd4b438ae18c734e192b919a8fe828a9a474526eca0aa108527a9379e385fb7abd8af4d77e6f8422777ed6b13bebc6d2e6a0924ecc79cb5f7627a03f5c79283ec755d7de56af7af199bd36a085ce8d25fee1cab59d066b650823b83047a20d86b3c5fdb1c132c54313d7a146e2f261076a57552aec4678764210804e003c0c801c0f70dee6c6c60c168d1a3534ab1919d50078764210804e00720080bdc7f1c0c68d4d0d1aac193534ab1919952b0de07a43fdf27b870afb174ed590c771007cb8fdf73ffabafef2d643103e5072256fbdfc490457e6dd8da490ee9c93356302a02cecfa5aff8183e0ad17ce7b37ac771a5b8dcd667b6d336c8da5b14ba28e733219ec25d2fd9cc984f523f92a0fcc7fc8fd3bb12db93f04ecfd02c0fd3aaf16fe2a08f8bb1fe08f1500faeacfa1af2ab91f00f873e58fbe98723fa62f0f27c1c12ff7007f2c1bf8bb373446a54aeeaf91bdfa69bce0929c0cc682658739c5ca9e247faccf9970fef4e544d1628489162339c779da46092b1f4a081f627782104209258452ca78bb2184b13f5696527e5cc4cea4bb48b68c755fb2f69fc5ad9b7f91b6fa972c7e16f723f22f31d65a3ffe8e5a63ec62abff507cfb9af656dbb4cf5f7ee60dc5f7f78fdde590de6a4dda2c8bbf23239ab033f49fed354d6e5e670fa2a0fdf6d11bf2686d7668df42675f94e96b4d61d1d6f3c8c29c123cc61863b5495ce787eccfa5d67b59ac7f586bc6d918331a792009de5ce28d5e12bf02693fbb57d56015685678067f2be3a4144e8a37765ca7b9f8d05b5bd9d6b67500b9ff27f74704e4fe09a5e4fe8d8707f248ff10aeb85f432ad9493fe9770e92e0b95b4cee079554780ebf6d2e4e6323770072ffe402b3c41ce449c18f50336824b8820b17b12f4ec09298600a30be88410a1c98ba643a0869a2a20c1f7670c28909b8c8721012657081254a64612204114c1491e1700497314950c044f1c28823213aa6e062c911182b7a8022bb21c8045070440a131643ac6088cc06aa2444033c7851c5952852c00394d89481269abc1c1501430737d410441a0589824b145a74b9f28351093224428a928011b3041035c0f00512599420b208d3d4440d98b20f0851e22206263f0cc1d445936c86299cdc2005521457f8942058204182234a629e9e803012928015901103199ed4c0c49523416820020642281991850a80c83c10c1100242192490e289873161bc640802c58a952426f04288271e3223428a7e10b32504626021a2091d64317cc04a0e947c50f2e203530683101e2a478e5001156378e08bacc803aea0400a2f5e638ef840f69a4004897569428a284370798d1122450b7a300289d3135d8469268448303d2801032563ae98610733a06011e3022a62208687322a500328548e8cac60410a74102448154a65c8b083172b53c40491f1218c327008c3c3ab8c1c0419120302cc17d891172041060452011aaca060891094a08a12f42948982527b61c016182299c082384014ac038c9628b2e4e98c608228b1072440507225022850b198e5030420060c4e94a1024703a32a585083283912f8878419519a4f8b2839019d0508694232d596c58e245080c579ea822cc0ec0204911420c136e6025071e9488a99243972008a862cb6bcc92151469226d90250c2d4250e408930e5ca6911bc298000c16287ae861a99708a192444597264937d030b21e94cc0086a631ae608283a2d7eb0a1a8c481241920d2818454bc610a206482c512287a42df48b294d9260624b1450a26c7103211640f2822dc0e0220c185ca80d82c42c940c299696805902860ceaa405089a7859818509ba40d2346419420c097658c28b2bbcf002032988e28a0c61d45082221a8820900a580b580003244fe010a50b0ae4a00b2f72a8d2258a105c4030aa68e294c5abe989305a8670a2871c40a9e2431522b22c608715188591021b94a8c116418a44602446162550810e3cf8a045103244e902851196d7103121d6c88820b86802050a2c289620b20b27312f38cac2440916599ae0181bbc404a11466c5802832b454a926220e60a11351809c104b7dc8083123d7079ca418a95a21a3ca0850d488800065dd8500328e5cb13276cf0a50b2d49d040480198280204465f54318514ba04b11062c41364852a8e8001858b112cb1830ca2c21a2612b761fd5c52014228d257a5ff7ceb5a9e1ec59fcc40f42f0310f2c4eaf56d7b190062c4866bd4a9ab0cc430b1638c317a2c364c628931c6189bc3e28d05684a8d4dedde349b628c31c6f62b31c618637b538c31c6fe18db9ba2a4f2f57abd86b45c39d313164e38538c31c618db63d2616c8bc5628c31c68e58628c31c6d834c618636c6faab16927c618636c77dc3e31c6183dd6ee4d31c6181d4b8c31c618db9b628c31c6d8d34a8c31065f23b5d1079ff20fbdc47ecb3c8c0ee4e3adf942f1068e9a69acee041df3c182a7aeea6b79296d032e6b3d1f9deb056ace55bd7398cb867d3afe8bc29e33755c208956b6fff1060abb90c46704ea23d0382841152f0574e005fc423d0abf70bf0341b1c3aea8d9a31f0186d477ff41d8c549f4a7f008af0b1500952ccaeb3c9696f399140b23ad9fccdc4de6be9ccced648ec35ffdecad77b3e2eacbe9ffc3270fc9fac22cb2ee402881aa5db5b5e664edb9306e670dd5d9ae9e567326b9f7976e37ed7277c3f8d3a46b16995a361f66d97c4dd3bca9699a96414de62abffe50f654010db90f4270f335c242a0ebb98db24d55fb5766d50650e17c2b0e6ee7fb30cbf0d73fed0fb894bfa22679a8fb21ab83fe7deb55fa5f873fb8a4699aca43691b57513242ae7130763007c5eb75de388b53efcf35ec54ea61a78c32cb1f8805c4cab2efbaaeeb5c447049c77cfa032ee5a159bdeeadb5288f7b2020200e8350c6d5991fe44273d3aa0b66d72d15b01c51f8eb8ca2f11f50eeefb7f16a78349e2533b3fa7b83db1935935633322a9bfaba69b0e358ef9bd618ead88a53fed2efbd25f84fe7b8b09ca4fdb75adc67d787d570adae00d8b89774d2ab933ac96b9e183be67cc6b9a5549665339b2964fa538a52e67ef39c6559f6506e8f4ab9dc362ffb5a2ddc887cf27ce80dcd9773ce29b506cccd9b1e8b4654e5f9b3429ee96e2bb7756e4a3deb80de8ad95076429665866b1d6361ce107df9b66e77abadfadb50cab3cfb6cafd0e2dd3be7bce5ff33cb56d9bb6695ced8af2fac8e557efdbc9157e287843f5b9b795db7e4756bffb8da39d07398e7bcb59bcd5cf6a26e914f2efa350ff595cd4a3aef70d15d9984251879afcecbd6671d2b7d49b165ae97d19b723be26b7bfbf63fb9bf23a9437c4ad7deb65ae39aa270e52ce1f921f5fc628fd395fd8262a6a9223a543fedd1977f47d1a893a31562c44454f64f971fbfedeb40c12e1ee840d0f416fc51cce5badb56be1dbb7b6efeeeeeeee2eda71221ecb1b5c3292d44c4679dbbe17b20624f3bea2fc6ddeb7f3acabf2946797fe8351705cfc290cbdabb2ed6414deb687b2027921dbef85ec01e91752545b23dac9b5e264b9da90fcfe21f9ac2a71b55e61b05ddc5d0ef5c703dcf8fdf11b322d0e5307e1b2f0a65ad0e54a74534a2985f31b52a1177929a55d67cda66c30aa84d9779c55ca38247e6123a636a3d977cd3268210a53bafb20a594b2e99c61fe9c90ce49a76c17f49c5576cb870ca831ce8fb2bb7b4a28a07c6be1436f4d29e94b2f6440e77c2c373efca0285b05012e9e80324bd84a923ec4524a292184524ae9020b21fc0cc287199dff01941ab38c62222e4f1ca5d8dbfd4fb7b18ddca494524a29a594669ca49452ca715c4ccba59452fa284e72f25e95aa29fd996512627f9a84b39e4922056fc05997936661a2ac358ca66d36725d8c31462e072a8cdb9fea74b420bcb1ca7033ddddddeddd3eb35ad1d034cdcc8aa686a6a6a66635235353a3ba353535a91a545753538382dc0ce0fbcbdad4ed76093fc6087f29c3ff31ee1791be989256b924fb31ee776be56a13d68f716b95dfdc17f726f539a96ec7cd55c940293f76cfd9dddd447146664675676666666668a06690b02f24b55218ad24810fe3421add61e23f5dc748b47be22f26b9538afdbfb834f8932956afa2dce436a594514a2923b5d65a2ba594524a29abd69aa6d5aa4929a394524a29a394d55fc6eaba816372aebb94ee70db2cd77528940f90ebaeb87ba94aa59a2a95b55beeed562e3d02c1815b3a4529ee72583756142a956aede4b82d958acb215353934a7de62c9c91a87b210f0b8845e33432332b9a9999d58a86a6a666068bf55e6ad4b059ad68686a6ae08c1929b8b3c3730e0d1ab7c6eabb2192bbe849eea2278a9ce4c60eb7103de48151b27b3794524a2965ff00792411b8b4820f904b72dd10df74396e6c441b366cd8a014d7a803ff8b972ff7e3decbaa8675938366b5aaa1c9e13424abcbc1ea723496dea2717363c3bd3de6b87278b033717070240e4ec4c1c1c1c1711c9cc6c1c1c18135356ad8dcdcd8f0c027070e84ff9e777360586d6e6cb40d285f0c1b37371e743a6c7439a68d9b07e3e234740f705c7a6cc8827b442b0f032027009da1bea2dce57e97b3a9e772aff75fdc7e10e416bdb59ff1aca751c3a6e6ffffbd4140411d472c7417fad546ecf4397dfa9c1e03df4db6393b3b3785ea38d7e63ec3ecee293b72dc5fff9179ffee96f9eb759723c8bbcefad4e70f714fffb397d511b0faac5ab99d65feab3b647a676766932effd9deb929e3755679df7b76a772a9bfabbf7767a73f76ac9b958e0ef773656f1407e76fb0f338cf7a4164fe5cb766da0eaff591d7524a2041997e7fc9fd02026119e22f2853fc05598b9be3fe8358be1da1c8851f04a142f49f0e8726a18f1d12f309d4891ea84891e76f1a26824a993ad10315589e953ad7d3881f1bac7e30c4f5b795660f2109ae8abf6f83dda4e0d6aff1d1bc72c52314b98e03eb31f87677ffc531c4316350e83163f21011fed198a8df883163babb718bf0e488266238a289185c3ed37b2dd618238b89751304cbe1036a9f6f20a534081683c99923e58f9c3c1471ed2e0275fc67a83864ffee819575bc204f11176e48aad70f97b5c1f2806ee68424ec40123a432823646fbc368a3a6ea4b299d15ffc67e52d9b195887e719f67bc97f62f66f261d64a779f257bc612426e170c388b5820548e41b38b8fc402ba268c95101fe3352b69160fe6353b98c87ecdf51fc27db2103415641208b91a51c16733fdcef8280e57ebd94594ef1462beaf8bbac5c1b7ff31f5cb2331e136f2179cb3a4743f3b2cb51f31676333ee57dae9c7a9ab71e912bdbf75e8a3afe4b4ffc07cf97d0604782e17e8dc4a2c19df9af9174ac7ee68322f4166e231b42f186cdcdbb8c0a5ffcb5eca73c20ef5cd962fa7dd45a64a7aa578ddb2346308225ea95999935c571dc0fd5d457cb719cd534ab751b0862542fd36d322a0ee66eab28eff79f0e853ae2388ee338c975208849b985d25a42832691416388fb51188541125c1bcc065d6283e119492c2cee672396fdbf1949d946cc95290cf23885cd486a2d59d2ea4ce49b36b89d65d5b2b72cc418ab53e7e9b972f4be4e5282cb45f1ac7ec30449976413e20d162a0bfe4efe4112f7c65b2148a5b46c4ba552196abb5acd34140a85aa194dea6b261daad3bada659dedb8ee76a9aeebbeeb6a0b7fae5433711d7fd4c6ca70cd6ab693d36add14aadbb1ee33448165688594a79ca1155262b905b75936c4730b9e87b6af70abb16e36c397fecd727cc9dbb6e1fa1a867562b9757402f3c18143c6f316e8e6dec453d5221c92a39fb296afe1f37461940c210bee47dfbec32b6f7f2e99276e21e6f9d6bbab86b15b9879e221892be6fcb654413c722347e23f44bc355532332b9a9aacc6a3f1bc19194f055b4eff217253a88eb35b24d22feb5de6f278c3919090be46ea23f7ea48d1880b1bf89c969da432ca967c12e492d2156ff41497abf5f13a2ceaf803b95c2c893fd675d8945c595dc5f195c4fddc916abd978524002897be53d506a04e7361059146b2bf5455553c4af94d02a3ccfce78323730fdb0038b2c3285991756f412693fedd310e6fd5c870b8dcce997b0d0a04e43fdec499e07026aea8f3465e3948e7f7bed83b6f5283fbc9238f78e52dff20f78aa30e6cf59f16661e923f1fbe852dff4a03fed4bf2c4882f673beddb6ef7edb220bf26c18d2802dea3900429d8b6fd4a1af23660db3200f5de5c65cbb3774dd8a97c3a0bbbce5ff320dabccac5c33bc1a8fc65b79339dc672ba1de79c71f6fcfef98d7dbce553c78db75c32aa9bea38bbcd1db3979a09ccb04f33f19fda4ac91b514a1fbf1124e52b07d55607611cacf5ac1b6347c8a5b54304445b3b40d403d2637e00e521f9dd9262114a703f376a02e94eadf7b2589f3bc9feb39b70b8df751ca6f28b452b272785eab8d6e60d26b693fb06f5df8ddc3fc28d99effe194fc6defe0a26a5a558d9bfd64eeebaefbaf78124d4ef7ee5d77fb4ef62ce7d4ecb1575baf65fda9d8e1c8ae405bf1cfbd5fb727245fdd7e52ce3fef32cf310fea8fe23f273ff73216732f8e340ceb0ed9850a97e2e4e2a39f5b0cb91a29fa5fe2b7a9287ea0fcda72a9584adafe614fe50ded7e5efd610fc25ec66ea7f7ee346f8b37df71984d0003b2dc29d6e23fbb733e7fc8c665996d1f923a7b5adeb4bd89aadf95aae18429e1c556b4096b39d45d994bd2aebe393adeaa6501d07b4b90381e3dbf10102721d1cb4c8baef24598bb5b7da16b7f31639ef0c76969aa62a124293fb8326cdfa41d2fd362548c24d769ab4830051e4fa9faa88f7b972631b18e479ddef031b58fee0a5e3266f4a902728561bd84e9297a4a42c27b7bc596adcbc1f309679dfb4f883b1a9ba1bdeeab6cdbc84526d3f7b98bb4fa13a1aabdc0de73758cf714999dd1a89ebf49cd67e7b5996b93c22b8043463326665d758cb8c8ebca31ba0b4a834ee3884da319a1200280043150000200c0a078542a168308d3351531f14000c7ca2466e509a08644190a330088220438c3104004000200000631063640402046bfe893123e32a9c2fb4a74f2eb2769b351aa1f27b2a01abf765c94066222f6aa0922a7a2a11c524af0c4b4a9da9c27156cb3410324db4ee3b9b52e09a667a929b92eaf886fe950c550d36f82aa2ffc5034e304070ec7798ea24ce3796bc7d404efff58be84bcbd040a6ac5b6deec077d63f882a869507249930435fb6068af8e3d8cb821b07180be490dff80845b1c6028916897623a883124ec6c871b562484fccf57717aeb317c0521df88d7b29cd3ffd8a19b6a8228a13eaa05668a8bbb42e680b0d0c6958e18408d9c40cd015746d3c423dfacfe2881431b800c722933027efabfbc03ea32c8e30e032ae21bd8b218d6459da154d66d0895558092e4beb036cde196848b9afef59c43e4630b190601b431cde1a1838abe21d731a2ed8e3f1babcb0ec9b91bcbacc6a555f420acd1f97bc752d7b8461a8817168d04b3e62657f70b3fe73a58acfc981669c22155fb323e8c5c22f7f1ebc42a622456940ed726c629650a6ee919112a449df2fdd1a9bff24fa3f80992917937a9f1ddc45279a7d6284cf190a208380943bab64001b4d9f4e0180aa189ecbe5c8b706f729116bf543f55c00d6d3479f635917b2487d3c66b4e419395ab17785cf9b049c481ca77259ca1758b9e30339ac39d65241050668b8a3778c2ab5d327d9a7ce313a9e2cf062e42c55c8162a6767a98e0aa5c5c79657a4733c7a4b6aa008917856dc9c5c28ff30f708d5c115378fe908e2a96e12935a57cca5218d0415de0af56456fdb8dafc5fe6f612a6f27c2a4dc106348a04fcd2bb03a03c99006562b7de007e322430f17a7efe0956052a41a8a60c3a20cc876f642bf6db6fbdc87ee8225ee2f00fcbc1a2b317a3911490e2853c9b5d1c7bde11204aabe77792eb42ea737de51b6009ff30fd4e269a309b9290bf6312e13fff2e99246f1c463e3a1475da3683001e0314f68c654d62a3443dbc312a01de80b0380e781d8f1578a6057be8f99b11572bfdc02b70eba3c0556fa01b1f1fb80d54091a8c91d005428a9ea8796a26b157eca38d3191f7686236d1db0075e83b3c93310177f58a02e5b8d8ca8aaa0c140ab4f5798710856b3644c42959c62a1a8b34cb806cd868da76dcf047c24106bcfbbae385ae640b15e69e012020b244fe8f07393adeb431a9c013d90c4954775effc45be8c86d8a8cb700c83bf7ef7229af3244fe41b9b16151df0638af39661c339c3ca448cd30969a752b043150ba58d07235413c8efc9df3dc1b045c8837a599c31098fe1a92b8fa8f014c95243ba9aa352f67a0b54f0f8907ec0e986db4b6d2891be7313154c8e5c96bf1ac2d8e84b2a835d5f594113b00b706b282ef94adc4a5c57feb41fed5e96919edb9483a0494fd94e4b2ac14ef9e738eae402202c174329b18614962f9f112d55b22237a7afa97771757afe41e7a7091edcc1ca04ecd1b7aebb623aff462d6e4f850fc595423761268aa21e16201e1d1a2a0ee4dcf67f2f93c5672e23ada29338c5dca9ffc7fdb82e72865d7e699636a6aa57aea55e918b1bf79462297829d4df1ff0f7cc787493286ae0bce7bc70af020f0f52d616e5eb5ac3ba0228e413a12778dc210098c9d5bae0446b58e30c0dca470c7333ebcdd4157d97f1665e0b0ee0ab5ad016c3e1491b9421a098388265292071008fed2280884a963a8319cdfdfa3f13819722a927a4390b3114eefacd000b4095b39233ba0c8f3f65f133607c84e40302a3e47b785a1c8e3fbcec9ba0c60ea52245ca07b01accd312501e75fb8aef166492089f40b0330383d2e287a12de2237f85984a8611ab03d4281e386e5c16ca3b13cb11c6a5a2df5f77ec531a5e0e9d3641965a8c96ee1dbc24d73e77435a4411022af1d02faf6f18184926f35dbca2c044759ecd4f3d74ed531c107500403081d11e185c5e0323ab5f7914919e2071ed32ba1e0072e2ddf8def33df6854ea38cc88ee01b3c4efca3b20fdece5792552e4a96103f3e0f8c53ad18ae23435a0107c7efd10cefb3a016ff4d65ae3df82f68f4bc4cc7ccff54133f86c900df6771fb4a7e91a04062b8fc1c6c892be0d56804f7d08e2687f4fb22fc072c1fdf5ee75a2595c005ecf6f0d3b2a554c92513d44c50bdedb2c2279dba1c0f65e8fed0d670bb7bf1acc9f4f57b4462823af917d8a2ae736a761989e01c5b20e29a5ff8880e7f55c538a78e8f5b001f359b5e52e3fc6054796e4c59e3cd8d95697bd9ab2953dcd8a44865d191c2b32e9f88ba889654f8bdd824e297fc42e6025d22adb6ff105d17e54b8caacded6610e989ee0ba8fc19f224ad9c39f3788a0594bb6a713194cd2762494f8e15903ebde6a63150aabe63954105a667d5bd97bde6eac1ee4a783952d2d457174041c9a85d2e95e0ea5b8c9479afe0203a0371bf4735820cbe09d0ac7120df94c2ea32287f2dd31fef596bd6b407517cacc1cca8f2c2c2aeb0dc66e71e650e21ea2a69f8409930d69a23c5880a44b278984c0a027770e0ce1289da1f552064211e6fe2f019d32610f08c40361917ff00eaf447cde5891083ae6f143578f6ae6616f43489c6e41c47e82b0259a774ca76e156b573a04e77ba3d8cd41f31aba06d38cc95a667c025cf4811905b68b5193ef3b534d6eba667f93d7f17393045b256a4e6297bc166f2807c426a528a3a73d13ff3ec449361d3bb6a4ecd7115df505cea1a93901aa08a34e90d19c1db5c56f1c2b6698a5921d3655aa10f17594310c456a19f8c93cf4addd02bf6ceee61028a7482164085685467da6d1e1ec2ad53ca5f30c1cef07f797f5bb2a855912b16926883837b448523d203f32a525a38c257ed4590495198706155bb09ec28a8c33d934820bad116009a0349a91928244b6f245c9b49ceb0bbed528a5731ac1bca2cdebc05788690cb4cfad6f1a39844c40fabdd0f1fa8c7485e770a606e91eec8162b5f63e69d2e8bf619e42dc0ee5f687e74e234d499d4a7ab52958ca90d7bf8518d2a947c6ce40bd5353debf8c28d1815e2f3ea3ef083f72e3bef62022d805c27e22dc1816bab144e337399fc8ed6b92627708f0749054facd2007516da4fd44a44d330c3ed710e35f8686922ffe600d5242df7581a5cd491ae981b990672c47e052ef1367aa867a74c6a699083271a108b90dd79730007492e6e92a9e4f97b3a1f03d66a49e8b5de36f43b7fd087ac50ae2525bb1a1868543dfca54eca25986a8177aa69dee721d5266d1ea3e204774adbf6cb6060bfa572151df852844999991aa8ae4bfb8ae9c19c1714177d82dbdc40bdb90766f9194f6fefda468b8e45175a53b3fc573acb341c374d02c6d71207d350a71a06402a6c2936261a192927d9e7f61c15989641997f68548b8a9a5cf86c5646567ee16a96144b5be763294ead165a3b029cdc35abc23798c44d78a30d9d3d74eaf4d7bb6ee0e24653780d3c42feb10e1ee6b4925ed543d74c6afac73f5811be0c1880897ba44bc46bfcf03cabdafd5c031f858e3048ad3d392ef41b371ff5e218a5d9e4e20756b7286735e00488169e067836d56e233a2986bb0c985e363cfce76e8ee653f6d962c514aba1edc0fbc46926834c2896f12fb4d03328e950ca553a5148c0c05c44cbaed167a740d2be76c86db04fb3a0c81273d401bebde71086257230111e7f3b3d05e4ed0cace565c11aef8d173822c863f863f47f4e8e9132eeea174bbe57486e73c699d84be524d152c10c8289c2368d6218dc9d68df6593ee185bf65d32b11d07a715f24c7cb201d2df32d49ebddb81908bfe0d2b139d24c7806bd48b79b6aaa5fd5b3cab46d262c2be82e041e123b49f7d593cc14a55086724aa164587e38ee373fe028b6773cc02f5ba24f13f89d2481067b4416d0ae2f2df61443de44f13e06ae85f07722cb2353db1dea23110e67440144927acd030462a5b5abbe1ee01c3700aac9606177a2fc0832a28683ef2de9386d89d208ac8b608bf3093b09f332fb2749bf6e5bf1173f4ea4bf898a60e7d5cd12708eb480fdbb4391b79127e26b08ceaaf29e055eaaae84b5444f9a51f22da6ba784d0064bced24d903cd2b29a47025febd76acd82cd484c3c8955780b7a3456de15a465319751a9e7311e95d1f6acbd568b0610b44d4e68a45ca434a2abd80f41ebd992a6df2a77d05e5d891b245c7a32012c039fc1c93e7680083296ee32c5cc0887e9c4fa2670d007ea98f24c3835e487172636ae4eaef6b1270176dc2c2378137eecaf8fdfcac72ddac615b815ab6115779e579bf69e7f53665abdaa75385dc53cf734399e53ea1d8a50bb8dd756d2874b2b117b84d1360cf3e1468b229c333607347a50a1907d82032398218448b7c657e6146aca52a4b6e3f10db8e432c2dc7e8f95e288174a2ba2ed69a96238c1675cb14745323f021a1211f68df31da7d6fe69f4d498f6149a355713e0abdf438c4a7ac47b28cbb85366be8c41960f5292c148566ee5b22eeabc53d04db4cc019da80e4de588e2b1b94135bc0a4479a97cacc8f00051a395966e27a205446d91f913e678791bc1ab9b9ba2b5186bd52214a0006f719575aedd6da89f2e17d21706b84f0c0f4fc1d69c574b3887a68855440a048613a28760fb17d558dfb21a76ff066dc4baa5b4a196953368faf73374017aaff1f8ad9a5611050b9ddf8d5a5dfabb4cfe70eb3aec89b0c9f505c3d3d97a421243d3a86a618b9791616d2ab2c58303e017b6e5e0902ecf010e795093a02b1e4bcc2b2531c3c56ead68575e705ba5a6b38e66753e46c8af4677036fafa514e88cfedd8060ae28cfb8d347046d6bbf929f91251d416cdfd6b64d9aa7add94179c5f914c3113136b6495a53cac780e59f0efb9eaad0b7d93214327f93620150a6085c474fb6e648223dfe3644446522fcdf08037e9bdbf5406ac4f7ffca8089d6a605c3d863f3ab99e993b828b881941201c170fca60da2c7064fe57bd3666cad6f49a11718b6c3ebfe4dd6253787bc696d33b448a93c69bf7d132e138ec9b62e0673e718230d54a29cd2f6decfc8417add8b88153938eb872a10340cb4ba415876de1d82e678633e8306bd745d767fca63cbd6a07ef39714bf96203295056c868260773f571d9eea7bf2070ef226f846fc5acffd464e7656921dac84432b5c3b353104b0fd733971fbf982e26e365af8e650921492036504aa18cd29401ca2fe3b9c8eaba7d9d6560998a2c89427f89c13ef30f1321d8176c3fbf65f087dd4c3d2af92a4107e64e2805c8df8ef55a101d174334662329eac55e8a8dacc4aa3442ba63ba008e87000db0000b8f7f117825d6ade3a3080dfe052994010859d4df6bd3cbd3be646fadc72837be8d04fc6a29104f70b7ba633c74fa00a660350d1271d8bae84f4ca55dc50637c71caed4b6c4d7854402da3260e9aec5927b45c3dcac30e9466d05a441bdbd26de337c50abd7b0bde221deeb30bd306d9ec5e51e7689b95fc5c6b0ae5cc29579deaffbf6496245752a827eceebe4a2a9faf3e68507e8d8a51a90ac6dc5966ad0c75d5d8317255f4874531486c791bcbd601953e53d7f5d0bfa44902e1d45e6acaf76ffa77a4b5dc863e807f17f120680913acd0a1471bf3a29c45134a4094b3bbfa1f9f1d5cd4908135e5b1f93a671ec23e7b84597f4dd381e5c5f1f6f220e79b140fe127c552bc9f1514f6f46113e9be7bc161650c41fac794caa609c0f1c3030d12b0cdc4043b9008a1ca27a8f60834f604485219eccd973d069ba368c77f5b6641667176912175b055664b4de527ad732ddfa191e8893772088e61c0492113b23fab1a92afff811a0554b86c984cf09fbbdf820cd1b5072b68433d4703e15fb33256c63aeb16ae88745fd034667cd6d53041a2c205deb1355ccbb0b6497f4936bb11d1ae7c8fff38a1eb995607c02c30a20ac8daf8ec999a387f7177e4a005d28bc7474ada3b4b4762d4cebd2c6572eb816a7bd01b329b56991d97de01cc4987a7c1aa683d11af35dccfccac1740442210d701792ddbe7dc6aa949d714183324d0d9084cc2cfca74afe412cc486161bdb0e873746ccdffd634b352ed45edf5d9efbd6d008467e0c20778bbf0d484dacab2ae406a57b80657ed30d63c3c381d8bac1f0c87848405d0382606451d9254594a1ed7c546adc82117b766b0f2f40b151d943f22246d78a43cbc824631964360155bf42b2d114d763c8ba5d5186bd288bc82a7e9fd6963444e2a1b8474579ab463e560803079d8d20f88f09184cb9110708ebdea25d824d5cfe45d1a1d227e7ec272d582b6af28ecd4654ee09eba8bcaedba99ccbf2b659044a235e7b754254f09f51b92504e050aa03236c4db0f97f9860764096487deefc7900598e67799df3d3d49fff78e3069c789d8eb7b9016cab8936a341c7c32ae7aab6e78734dc0a814fd4823cca0dba96c564db1827e30709941dd3022c8b8aa7915a612e9f0528533d6c9e3ec0a6ee94fb6035c7ac672916e29da853d66a9480694702563498beb5cb622eb681b67b272083761528b6ac2276681f9476d10b049c924f069a356e77dde68ddf86a7a45571085305e6aad607bfcc6db6a0fe732f9fe8012e3c01555bdafbaba247a6aeb78118c284370ceae08f10826cb16d7f55d8ae450597438eb70fbea50200f70baa6b7b8f100494da10ced4572f8fad69cfd8686fa2f7e47e617d3d13c6e03d81d985fef0625f77872db1a921004059e2886a3bd50d63c05a6d9c60feb50eb9fffa9b48db80cd0767c238150e8274f3f35d2d875073a7fbef524219868d29a8c70797a16773716962832c8127f061d233bc4cda01b356e0b4d40073605100c9abcfb403823bce6ce8f0f0bee5eca727da268d44609534872872a33fdb7d0a0400b1c88e5b05201ac8fb4e32a25db0291626eae68716a52fcd396c84b8e6197a27d5236a85e9751ee59156461bdfbf4ac41a264f643b7f2b036f361ae983996cd6759c4efb11d193d56b7965e09a030b01d04e1f72652811bc7e3f9421a2437488948141c398cbbd067fa65212cdb43235f19279c6cd6c399624b9faf45205722e4cdaa61f289a79f3dfef286232d004e752a54e07ebb573cab46507615f54396a3837d0a84a2169037e4a2e83691fd8f5d63396dca30e85dde67b02f39d1335b7fc1f6666c95f73c3f1867c55cf34ec8334189cdfd8e947e5bcc56124f9b4c28fe78c3c05ef1dcc6547889d4947e4721d6041ee6e81d3191ee8f169fe4be04306c564521d1d8b692e2d77cb6b45f0bc1df676cbb620f5c465a59530e5adac77e5d569e36855e0a00101cdafdc69fd78cac42b682756cc2df7eaf35404d50572c5eeee7e0330f0758f8d6e6cb17ef30217187732c694e31e3f58ae66732ce30d621cb117067230c5db923484a192647533add8e1d962d08b36bf5a4128d05aff8e57abf05ea7ec3b611d023604b19aa65cfdab53bab284de18cb184d90945952e2b6f56d9e81e5ea46526458e12352d52a12208405b27afb5c7b38ceab1de8bd86bac46b9cb23c8636b2b04c640954a41a7866cbf4e748c48bb4761c952c9e5d474388f21698042798b3bbf0198d51866087b5e6f895b0e17465f9ce7618049e4a6011a47f792cee7a3d6ca8f2cff800fbab4fb91d536b8c8c0c35f7e3ce99885cb8745b574d00b1dca3e4b856d847c9f830e89764181387fbc76f90bb529ecbc5f4cc851109d28b137b95b3a6b18dabb209488dc3b71dad9da463b7d9d6b7d2086cac17ff68e074b313b42ce09a966bd0fe745fc5c186be092d229f8e4278242c999c9a4282bb5e74939dd384f926db13761e2daf4bb32cd13cdc5d962d1db86f091bf393e8911a9d21e845dda99a00bb287785280dbbdf90cf32a5c3031bfbeb7f8941ac14740d188ce988348644ad2a43c0b557e54935ac7a3f73ee0547db9aa73edeeb1524678b45eaba3c29a56d441514dfa959b310b5f124dc4880bd16965f041874df1e60a39e2172d642d3ec63d3bb45df557821af7939df57f6399528dea98a84a6aac5b715ca57473130374ad8f5b0727939116253ec570a6684963fade08608714e67c373124f84e9de01ce2bd3604c44821db1bde4d62a70a33b36bebe1bbafcf7d73540f95d0a2d34ac39dd647969d63061f04bc777c663571971b3ab6f51c88e83fc5219f3628a073bc39d87a51b4874d616a41e93054671c01d9c372f91e8a4db40333f9f645c0529ba975e094b04ae448f3aeb90f093304537a84393ed1915dda0558def96badb283af479464ebb8ae549a5280bca38e652c711356b131830ed15ed67cd763f77cf13bd43ec855e4e0ce1577db6fd4a426bcd23963ecec90984e49e21be4f2f58e48215804f6dd92529c7c68d4769b19bc93fd462927f1753ca8f6a65f2872908899d4f131d060baab92a16cf311b0fbb8dba224c9ca9049f9d6506b8bae59338c87808f6ce88a9904f0458507ea6a33437274b075e508782f300a9270f08340adc1a3e91cf15f66758accdee91cee5822441d7f98eba72dd53a80cd616e44fd64e47a3972660e31432f35365b6919297d9715193d25c3b301f39cf979257a1209ddf4de2802d3b129da1142d228afa5cda021c36e49e3e560efc42071f0c9039bbc4f81aa4144e97f7cb6fbd219f1f6ec50824d4d870535330064f9810e060a13c07b54d82c0302351155c8458b67848a9c1b91a00e51a3e7790ec102f1b7adf7cb923398761947ac629154fe3db45768d60e90c0cfc6725489591fedfb0c8b12c36eb64b2de909e3115f9f2d1199469bd4b237ebfe0aa984533a4800e78154fb067dfdd4f6eb451f07905d64db88c8e9912316930491345b2e8f301f791d37e95699b4fd3f47f4ef448e5021369348054a4ad0045c788fadb39b3d919880466a0d482c56b98faabefcc90409cd58a7b86115d22d3d0361199897fc501d36c1f82c388a38382e612774e44b3c4bc166d6164f4ba3cb74f70e05742cee89141ca2177dc824d8d5e2224e7cdd73ce06f463c0b01a435fe8de18f3374f0fae1425282eec6f03776481a6102e904d4ce2a4a79a6e2f67a81d1305f4710fe389c4ea4e78edaee6be679eeefd8897787de295d1b150ebfa91227afc8811d2c67b8ee40f8c70be09f6862a9f966bbd517e8416c00ec22f660d6860fd8a68cc01a4ea9805c121b55354617022a8658fb85e651509c1c6dd9cf400e997738666c13640ae21fc661506c58045561f30cf091cdbd0d41b5b7cb2cfc4ed8c810928d0405a71ba4112d510120e058f717c386e40482829dc4ee2473b79094177765b7230113001d43567da9c83a860795b41eb7ec45c3a639405a70c47a52e56df074b40f35b4c0c357de9343eb67ce5e5d4a836d981fb780661b9a2e0ff1ac33d6edb4642336f32c6567206402fcca2f33f454e2465fc25e86522d7f7eb6861f942c57b397b7d5832ad296e9ed82e3fbee22c37832fc3730e16521684f655e440f19bfbca313f5c44159208bac5937a5bceba2658f9761ea811d02e8036d0241a64c6a24b6f8661213ba6edd0cd329bb8a58f267a912b337e6d9d7c7d2ce61821c05faf0bb30016e05532bf62bd2cadaf4ebccac78e3f96e9675ae99d47d91496267fb78c23fc0da7a05401133efb7c4e8ddeb7b4de4908c135e1c42acd502da1ea951cdad5844841d3298fd054a3808852874da6416aa0ca493b3368cb25d15a4d2b9975d40d7db40c94048deb9ee544ce810cb622eba0d36dc5abd164312ce5a19e17cf1a27d428b7036b1414acf0254943792bd2c6204c77cf20121abca192a33d2009575bca3d327ec1e310610a6d4a01871779c31a05a9cba8d41f35f217b1d0bf7e825b95c571be03023b340476ce101b5a00aff7c24bb372bcd308fabeef56a0b511defc0dd9d7408f2e947eb65556d8a0ecaa045a341f0c64e7bb70ade975766b7706b8e5e7c0098abb367c3d16d21fc6739dbe5924ad2d6f2928749c7099c61703ab1512c4ee732e522dbc413170988b2353e3c76e809ec60c428330cd2b49257b811e940979f77c72a316c890678c2f6f220268b75be4bea46a86fd9bfb956a1076056fec12f72b770fe315ec3e0a6d00c9f08d1ab519d0f8799d11050cdc4d4fc091a422537504d452e01678e81e29eb84e90e813eadc56aa4b2b52d2860f71bb6523b6f21b62355d7564cbe0ac8ec11370543cf55361b79f7ef7810f57064424dd4662315ce1460cea3dcd1faa323a09ac14432bcf23a9588230c04dc436d4687bf18b3ca80e6fd31c9a5f151f99b6799c55a6c0bf5c6218d531bd94e138d083098d1f4d0b3e9c56c4c52c97dae1c1b07b2a0b1cd917a34127b006712550cc62631aed5406bd607f7bc8e62d6d47285faa5cb4fcc747d298fce9ebbc524e4647da5e8f048bfc40da5310173f9c1bed06349e881c2b51e82a0b4552a7eec189cbab6fca3338b4c92c185cbb379603d77a6d07c90cb0f661aeac456f234a449e31e9edd673d620bf1d4bb3319bcf4480b511aeb9840c14e79f21bc4b572201e3f61525c29c2da87d0e93d7fa2423af48080b7b89c3c90c07c88b2ec8c0a227e2d8ae1a3b50e20cfb5ef54fb6077bb46981c97fb0770d8520751edfd26b28e348751669e6a2c8e53b41ccb5ecbe8080efc882505a55d49f3c58a95893b2b49238e4fb38c15fab09a4300978d62ea6b1dfdedfcca4c92e6c552eaf1ef776e50f1cd264479e1e38d019da2b44deb34d869b872d6344698806cc8f71d324d8024494a74c5c269acb5531665c048bdabf525d1538f9299057b5fba04e4a8d32ab4a51b3d1532be439449343a35545aca8d3ccf637115c29d9818646f989212cf741ca4da6d1060aca7803381324d60d302fde0c43bb4afefaedafbca097e0dd498ce4c32ab4688895a6b0e8204fe4da7cc6d267cdbf25e2b0b311b25ad25280a0fa57dae5815deef70e9bad88c4377f3afc6ec07a12eca7c73204d351bf65bd67b52280f20606e320f8f4cc3709448832936e09ef7cb2124dcf2829e2c2418e0e004ccd3022888f574f21eb33e379d15c56f317df1f209116076c98d6d9b475a151eda87df73d5aa1db0304a405b50bf6a493c8a8183b28efc586371005d11b1eb6ea06d89f7a53e818207fc03051edf76f203771f27565346131ecf1cce5855d1d7ef89bec973e6e3ba1928f776bc01ee362872dd6d18b27a580bbf5b2e801818c1f91f8ef7a8e66ea151a4fe8df8f9026df95b956eba1c4ffc38d9bf0019a7c0105de53383cafd7d9fd370cba7df64a280df72c76e870eaac1fd7791b080d043a33f731081ffa25a4c10735ec993e0dc15e7f2dab136fc7e05cf220e76934755a4958089417b258f35d1d41124d0f7dda9b9628c8c31e8e9e4fbcb5a372439bb5a2893304b6c0a7118e36cb4fee0b94ede4703e0975fd2ad0956981fb92323f3fc439415b1e463acc00ccb2d189eb34e4b914844e2e46c3b3758a7546514a18b08d2a6571e295ed96caeb89b4607b0cc9b75ba418729e11257a0c3165a34cb31765bbf81d3ea5827f25473c0217ba396c53064d654b2c80958ebd563294768d30cc27690888725684f6dfb9f3910c6f91dd11a97472f05b2ad2bd3b10f3dee802b458bb3f5e3d29a924ddcb927d83add34fb63f4df39dd9b00007595fb7c33acc4b503d7ffa2ebeabc5f9c0b68f0901e79985c13e54b5349c9d0c6cc47ba90c5220af2a713e74e1adb097d8131c264fec3a7b209bb96f45bbe02e74c3fb3ff262ba852ca2dff943745a3ce2951795d2b0350419f070248b63cbf3fdd921144d9afe9644ff63c3aae3c73f6e3eff9e58720b8b07b74215531dc3f917e65a3c8fba48dade45dc3c92adc97ef326e3dccd91cd9c4824ff5baf4901ce12307c2b6f12a005869a8f522ade63d04432f99714ab03a883d6005d07b8144aafe4bfc5901d29d697134ae5007175d75466a6ce5df8217841e863007439fcda359df4fde36ffa412af7b32875cb2476fbcf42ab21faee57802bb59aab5ad5b9075e49e0325cb200090a422f185bfd1d532c2f79dde7f8ef5bc9bd1f0192e870c86d8ad266ff57e1366fedcaa80f01a243d48b5409a2085b1d6d6e40200c960e9a33ec7946182982b85a944319f978b590618b90287a9705da6e745488b04a7ba4a5f83b31cc2725bab763e832550a40d20006df0300001802009c5b3f9e746809ea9d5b08b95428fdbf89804ef085c1bf7196c1bc6d44b2b27afa5efecb102ef21579f90997e1ae262b60a7123de9f0c11baa64242e5d8ea5ecd83323eabb1a5e0e1ec60a7c39b0af6693732028c5a9fae25bf37fc351af7fedf096e9e035391eae1e0484c6a14e5db1e2b9f8da71bad6d8f8663a23163e0f31128ba15e7cd391e08b2ea37dbb520752f2951a56e81ccefb02801b40b80d99bb60439be15a4f7d468e4177d1b8a5d7b54cff24a12d39ff2b911196218b5cd29eb379d0b1592082f2037b62222e2f2cb400397a10c7c8f683e0def6bba3d5b4b43f5e1f6cb5c7e55f6a8774b8a208528b45e760ff8fd5e0103e2042261a2f92233234ada07c27111975bd9fd71d6c483d9eb5f270f0ff1c8362aeac0a4ee12d2e209a83a978afc9931f38a554a8faa7ee2e82df8fbd14a80d926c0fc4cf49fdc45a0467d652a4d547bf6381df3a761f77ada35e1ced07ef8b3c127863c26a0c5ef1e84970c11ed7a894e2ae2963e49148edf972f19e3fb5572417c8221edf3d57d10121c8dde94de26bd19e98c2d4c635a56ec6357fb3862f71856094863089c4d1234ed400fb7dd1aa12925b0e68bf847f804da8dc830571090577020e5a55e04ef7181547c530038043e1c01c37c3c407183caafbd69860f6b589ba6254db8177608f122e7bce1540d30e24e856b45aeaec50d769205b59214a814103cc5485e5b45c44509a4d91b4e39e9cf8a5e45081c94f36b517ce33f42792af9db968cb8438322e31caa0874f3731dfd34bd38ae9c14036c9d7aad342465c91b5c011a45c4e242c9348cb8e99704c8f3451f7d5cf4ae7ad07783e7ace6ffbb2a0cd7c29f163537f288a45c59e349b33d1358ab8d490b062cef81ef1119df73a3de3c97b55973a453f5b4e3b811f5a317aad4b38f5653938f4acf54b40fb34ce8f72aa22339915b0f5b3ca4f17650b1e80e8261304a062271612bb61a2fdb04cd40fbc736ba8e0ea674a3c13eee12a8c2c1d80138468a45b58833d815d4a6d6d5065daff69578923463859c571034b6e92ccafea6289611d2cf78dd16df58f828f0ace4b068d82a08cf717704257fb0bb63335738d9c11c04a1239e32af3bd25441b8038443d95fc3ebccc59fd8893a220d2b6570ce3498575e1a03478eddabf63e90e0bd18f4b3d0d4083735b91189b111faa936f1289840e8ec531c0b406a627cd8943e4fc0843713fb5ca183b323d8b5009875f06d35e3796d348c34258ad217c96018538d326d01764061b135fb2357f92cc08ca27210d33c89dfd62699c597a0d308b65f661a05081cc43aec6b4aac764049dcbc19801fe3a2b0253442200e81bd249c205d0cee81306c0f49a34eeabe798238af425116a5d47e6e0fabf8c2bc1f48a98d6f7c6db0e4d5944cdd0c683cc2d0c093a382c6fe5173d88729e299dc2f21a30c8fce6ea67f2958d223df78780f885a45f697489f214c4af5d4838c3ff2e16d2acb9f7a9ff27880f1f4f7ad8b2001c173954f6e2b09981a2e1934d842de62c947e185e1dca414912a41d1e90e1b091cf995f025427553528ed868a542072a8040fa76eaff12d6c2e349e3059525cb883c554f1a1594ae7d5d6bc712fdcc210c8fd8eba8aa9135ad2ec506dc955ca08b7687b606cfc5ac98f8df990b131916766ebb00a2e7694e90472591627407439912a2392d95c9ded261442a2a264c4a22db0d8bf423e3accde97ea751d0404a25f742e221cf3befa54c6c186c66a1addc05b2bfe045c169b4c63f3fbc80948abeb8e8f413b6df78d134131e710d3c41b00f6ac56f6e24b1e0451e0e1f4e7dc0ce837f200f543441fcd151f871c3b0ae92475af0cd5fdae2860ec096d6a2c6e0e6999a960db1873a3bda775c2f568ff6b9f27e43ae83aefa6bc9aff26dac3b3ec765beb1735b20053fb0004d8c94e058661f4dd58cc903c4858ad113b227e651fec20c5fe6b08ebbb3523ebe0cc91302ca929c05b6c84c13afe3dbb0a7a2d61657f9c26de8a0c6acf9bf1229503a49d305ef44788ecf4f18d9f94a5ee5ef7696287e76b16b9df1b42c95777159c1e112d1040692b00826885d56643a868ee59ee258e0a7a4b125b5cb79a82791d23bd71216d2fd2bd8444f49e5fb174d639e0f6386999f2c2fe878e3a204ca11f9d33eb9e89745077c585eb6f4b5ae110caa8954fb48c09ea30903ee4b35b8e5789bc8eacf8fcd8d83f8830aace73e78a1b2c3279d0beccc6506fb92e7c0ff40f4c50bc766401a575eb16b7dae80e26ee01d0782073c9679bb2a15ab7b4017c0cb0b3a261aeac73e8162f0ee82681da8fa2f068fdf529fed2987151a99342118301ac0065934095fc04217075ad3c1e1b2d03197623d0aa742e305907a21667c6aae99e66ee2a713481bb04aa5605ab5ea92d89ef7885c3f9361103bb4d1826e77e819e3db654da41d5ac908a9030cbc0e7005c0cff5105bd066c40e4b3953025561244f3ce1c87301c4fa437bdb62bb7732ea1d25c665691e7da44c5d3904194ddb9eaff4a66fb9e197d210a9ce3308c76a694d3312245458970e53e7006c8ea7b095c28155c88fec5c65793e02610ead4f3eb4d0f0aefdc5f8f545b760def6e9ce0015c7170275301a8e0c97002101e7f9455fa61e442d10db68a73ef87e0cca377a92f945ed0884d2ee02e18dc82cc59c9da1f718975c93812cfefa4c8af8c3250532021b75ffb666ea560d12a8d4fb7b15607938018d13e72f12f5cf1ff17ec72d40594cba5a0e204f414d4156661f9701f4b4286f63f84dd5321d0189cfca8e71789b6b816ae8ed4a1098c701e40644548857deabfa5b9a6e5396a0fb33ae18e9373400fe2557a89cbbb6ca6afb601be5e013ebeaec19e16b7d50c04fea88e51d3868c5ddda5dde33328f47596669f6d6acb80f7eaad67650fba1dc90a8850e6ce8e38b2b81f2f45fa24ae95972a425626fb76a5a25cc21eba375d085f3ef8cd1bb0b73cb3b6a538a20db77dcffc57282e9abd75cdb9db7d361e878cad92691b0d1f4e6733fc02e4f48408ba1038f449394af64e9674be81632cdc1d2327176b0ee1eebfc32b04404285e2c8fbe6ca50f931ec8d953d60d34eb50bdad7b3886dacb2ac19859b4d579c11b2c2ba8c727357e525ff428f70dbc76bb182e13961675061001a7634a02a90266cd162b0b3cdf9e854e0c20774316c503a284212e351deda620d6a24992527899f4a05c2c46f6ed9684bb65da1c52f4a899cac7fe913a32c0581160f4fd0bc3eb9a0079c1b99ae694bc637917f56c15e28c00d6dd8b590baf00512a6f2a4069043669afba91d518562a594bd8acea37c3d042680bb352823a67931533919124dce9c5b8c61b458f6919c22c438fd98d5c3bed55736be4bc44df32f6a12be668e0865692245b29d52ddae7803b0ace59148a95af7480ba0b953619e3504996ea4e10031c685583d7c68f7183648eaa6c8d8d0327849545102198fdcc11a0218fb7b57760b885fabd287855c87eecc8452cb6ecdcfc641e2b867360b68b766c679b76678ee17879cdd55a10d13970c12ceed50e01b0c5c334b4613aae69a2ef728352254dc69c434b81d43a8fc59cadb55f9a74fc66dd68dcb1ceb5888264c1b79469f3bab5a702a1b68c2d857d9bdc22d51f73a2d68c4581f462dc16a5c5a5aa41b64f489b196ad76daa41a6701cc3d4ea2410c2c9ce5ac731aaa6d904efc9b397d10283956ebf0efe95ebeb1ba306b18c1f9d513495107636583b8ac06cf2251295a1f444d3daba2dbf2c0c62988242a246c73b43608281e2f019907f10508821e8441ab33f60b83c0006a5640246b44a433d601135bc8e1f40e1e434cc95358e720b5bea8c641985692a297c483d12d809976b1816fc0cf4ff05a487bd868671b65d2e450b21e2c111481a60df339695f0d929e6a42c856e822c577d71a74f8086e7ba81f65a2e070962e627e850d04e11610a2ec9c5066387a97550488033e20467acd79061745d90496f2609546c52ceeeba7fcf97cb03609d381c6c74908f2d2f4c227d4afcf532d91bee1159a505777098fc8be5a83f2ce04a9df03542c2665c479aaaff4ae2a584c0da01f0027ce89006fea8efdf88144400095563ce1e451b9d8cd074196a55157456517b710a9cdaa6619a3e44cb13583ec9fc4425aad59b3a30510af4a23260b4c54808cf87a608d5146fc1d9c7c03885ce24e03986228a65b411a91c493bbf468eb1172968de63f2c54bb6236519cb0cebb072615d8ce098c18dbeb03c114fc2f3f959829d76fe33f355ce630bcfb9ee0bd0806e723d0356230a867772b16e64e26f8035fbd3c8c2b08937d4149326bf94c49ecef2e550de6987099e91c69cb95d76f5e7b54c896c5f46ad8819bb6558148e61618dc14ec7ab7a197cc6090a2cd16a4aa8ea3f3aaa9191871e9b01ce6fb67332c6f4e92430d663c6d07274446f7f6b6c51b0f0149bdc822a5e41598f142412cd7d41ca2bcd614aef8c760ed1453fb9216ac2e3bec6255540d4f7304969052d45ea6ffbf8b869a4cde7ab422af845a0f4eacca4bb6a587103b6cd3c63abe6544e9ddb4a6df143c746602bbe779a3f6734da2441d38842a552cb2872b25b186afbb15801003d72638f534d0e15584d220bea96e21c272a4d8467753927cd8feb72f93650c6eb0c1792a8457ad1f9fb84fd0af4154f1c8517be2c9b758e7d9765ac82e16a80544088285244f5bdd0535de1ec80421313fd183c33f0c2c5b5c183aa8745e53b5ed3b218ce1ae44c35a594f4f43fe8dde58206194ff519498967780d96bf82ede6705cb44bb980044f80e914315ffdda8927e1df68b309db627786fd7d2852453c7c26d790cfc8939bda982b097de9c373266b252541c5b2bf68077990fd1e59ada4ba431fc71c94dd202a91b7ebb81f68c107646f0499f518c3b1ddeb10f0934d4d8b2e0818d224cf19d262db2b2b74e00746425d32bb90afaf36d69752f8cb0f2ffe2a2c04b6d133e5c6ce7f2e98023aaf5959f0b10f253e37ac6fd6e11d6a7288207bdbf5c4ca0d0a74801b22f811427d0a245cae47d7cce9c137e346f7d3b3b8ba0503d0a71eb0a165f393279b914ef6a79b723cbf13b79e4a0cbe5d99644d748c0719fcef0efff594c6e074d45ad6e8cb422db4e0ade446539973f421117bb1eb7cb8b4e8897a46871923a0f39f1090a543b82533f17355390262e0fa4d179b879294d1ce32a83e215c6c0e70b7cc0b142de00429d272613427634ef88244ceae8adf5f4de8ee1739a91173a97c0ad8bf24242e7b14f08040dd7c80b3b4f7d205d49945e182951806471c5500c3f242c27a5b36a757c87c40590c138bcfadd3eec4ac3725afa4ced9d1d30a7cd5bef0bc08996c0022a00f5be25b7554bd35e4dc67ff1d26c328fa9e921b95988847a1b84d7f3be2921237f7a5ee51c6f0d4e61af3a316f206c062667a3a5fa28b0e8d332f622f36eba28cdd60704a308ca336390ff0bbfd06ec89d8b8f8695b8465519af1050834bc60b0f28089c6fcce51d7fae3ffcb1f145c58731928826b108b34306353b3306f3097666d46bc84aee8dd03aa581c0f2bf76c1e4132bbffdd75aa79df01509ce8c44544325cad4cd8c01fd0b08d5522fa32f7f01143e0014906556bf809ba509cc2e35a827360c924d7ee888ad66c6716fecf923a8a45433632bb8d68c48033c4aacaf4ebb73f0cc6000d67201e0d56522144687c10bf24440e9a738203f96c735ff97380cab1f399a13100d71184e3f72be4d4084d969142771249f587dda1127719e4f029fd6cd8f9ce9c7e5d00a6cd7ebfea5708684461c40f2ca14e1d92302e3761384d07e06647b2c3b331d8ee8283f3f8ca71f87f3f7d5473fa93337b224e5aee8d9bb1a10212316a187e181658c4f61e9da0468a4b3634e80902fdfd9ec6de2040f5c8f299a2f7ebe997f0e2f6606e61d20afd5b997883ee8fe6d28b745e45c6e2834303a877cfa4e7a8d03ece6a679950fa5e56b538f8cd758ae8b023d8afcfdcfbf2a195c2c89369232003338ba82f3e3f49d97a37066b4d8db8a5ba32fa36f3a88d461ebc4e49473c5605e729e70737e4188fd39779c949621b35bff92c316e083c8fd2f22320f0517e26588178fe16ce181fbb068e5bf8515e3e00437e820c84a336a2387ba900d9f7abf888e8e1197829707ff8068ab0060da2a2d4430d07cb100bcfc547b52d0b72925dd33e9ef402dfec2817fc3a53c260421babccaea051f1716c82486bceb98759bd37fd134bc30602238ae109038005b19af9043e5bc6218d63818d4527a3e22ab6b030271b630223e097e08de88203f8e16b25318cb3da2da10c938a70366d4b45517191f2e88ed254673dea67848bc2ff873b78f57ad296831caf6dfcdfd875bcab36e81214899875941b2f7bf9d6d6330b910501e687a834efb70c3715d332c1779fc0520cf20fd311d9656b7e27e24cacccc201613b759522be981e3bebb21fa310e2fa4a884351b7a580364702d958376d8ca837348171bba8fc2e13c9069d84da8d63831a8c6017b2e636b562bdcd4d293f6c271f8595fff0c79889e349f2e4180c9599c6574e1551ad4ff81079c5aac7055a92dfb03fb0e362f33502db2ec8b5767449034eb63ebf72652298e0987c232d38bcab0b95fcc06c481f7c6c1593841736b840dcfa599a2994e012404d313da97810d858225d152f51db76fb3704b4356c3b15c5af8154231edc04f31f7867d989cbff3dcb02d56722a53053b2c7b34955a845e6fd74d2c051b99c932d5610cc9bb5a22cc40fcbfe0a9e46295ed6fc7422b4a843fb4555b2274df6ea90f73f754617e0d91512a7a94dc0d7492e4ebb2eb9bbabcd321952db99154c41913643c57a86500f1e994244946b626468d99a3aaa517d5339e681d71c1dfed6104a3073a4c8006dff672f519ae6a0192656eff591b7e2a8ba0c10845c34a8d8974ba08ddbe43ab31fe86be60214778a53e8b69f929e278653987b65f544db5a08f5334eab7e5be619dcb6bcf6b7539a0858f6d21c22f0d92dd890c67488dada2d868ab1d172261f4dc704ee70960b2dbea56103d0827332ef1581ecbf216180b3eb387cec4532ded0e541256c7800b4e1761e6b84485cb64abefe3e5e05937e3f28d42a6f8db1eaecc265a02dd06945be38d0bbf3d28cb2d2f3b38048587c91d8b4317e4e09216c23ba93047ebcaad31008eb9245ba39a4f3446c1e57dfb9f373a435dccb8535cebaaac4cc86f394bcc649af5bc74407543df598bb88383eb398c960d81f8b0e7866931032e3feb81be9971090ef05bc65b53ee46e5f37287df797b2e3621b9cd193d1f68638b780c67813bd7e83efbb15c873984bf7557efc02103d7db929b1b035c92ad51cd71724d295b97dc4c89e5c45c1fb94bec4ea67623aebc99c4d3af1f8cbdd6727d90409c5cad272246000c47b8f6cd92eef6071093547e17e95b8edb983ae65d000d3db99948376b575f6d03bc4a64f309434821b090a2dfefc6cde3df6ae97a3fb7063ff9f542d5220db09c92c49b45b23970c839f35e04a88ddec42bc5750755b3dbc4780a28c31f7d8f86c94c238f091ee94d98f77105e00d0084dfebc7c54c48db53170023623c999a34eca65cf34c34c23b9338791610a853fe1f6ec1efe516fc3f6ec1cf1bdc82fff671656622b4d7aed45f9af465c4e0a13fc7987ebeb1e133d5ae471af8e0601eb7d7a17a5c42180b8ec1d03e2200fcb7e395752a458900d0288ff183f779e4109bb7cf1c44babca6d802cbceaeaf1451f3e4f5a715f6e9f79f6472ab7c0ac1d1b78dd833ae7cf04db1a0599d199096bfbf432c51e81c98ba8a7f54f3c78b6b1dc04e8cea31a92e89434184623b17fba507d2d3681c21bedb977772d8d99e2606cdbb9521d319f9af920f3575b568791b92ad81aece5912e89da4eae04c4e57d9e4a89774a5c4aafae53f923d0260a921524748e43a6598db89a5950b008f541efe55e81ce5f29497002b6c87628000729a47067c8f13f5c5b56ce34197a53ac90f4e4ddf07c752f8196bc29f06ea19ed3e62d531e09418c957ec34dc586f24cbc050c0599b2ed391e84e6ce3ee06e25c2f6b86e64830330787e2d7a3a4580fd30fa3a7be8b3175a899937f32f17b9307645445ea302db19729d62bdad0007fefe6ba18d90dd1d39513c596d31905b8b93da631f669f892262f1c949f382ee6aed52ddd59a919e7470659e0ee896fcc9c191ae81bc1426bb8202af1138b0bcfe30d26f731dd0ada8f94df8ce501a2354e5397e7781f15a2ac68b47dfd848eb27f77d05d383001010cf7d22302c268f4ec4bb3dd90048515d656e7a0ddfa68344f57e214671cb06103c030d8eaea3ce51b3ae0c4fa6e2836ac04b49922bd14b6554db6454df0383ab299ffd829eb4073a5ee6cab03843dc491e88db118538f7a07121820527b909423c21c2431ca8ec4bd2d0b37d56d9c0f8065d1ded90f3ab19043711f641898ed3f261b861b01e80b13c5f4cf9c39e2b1aa47d96598a0f7802c9da208e9547b21dd0d80a970a218fdd1e7465403e377feda0f777c03bdc49e40c5d87b5ce6038e6c1e63966e0afdcbb421d539e784439172d4e0c6ecdcd240de1017bb91c4b34918109b6f3c3033f334902f8487432057578da466da361c5923ff76384fcd066eef48aa699491bbecede64c5860612cb3a9f578cecc49f007b8edf3635d507147074d06684e5fb658c5146ad188cc20c73557627142113b9afbec446a1ff76380e71cbcd637aac71e6ec214ac84b8e72e51009ac850aaed727a34d3ee713a4df49e8d51134c4154fb81bb25bdd1e9039ca9b1e606f8ee804540e3c6c82ce192f98d683792c97932f190e7a3d15a1fb62ba90242ed2601e968912c5a8e7727a5f3d57b9a27d6385706053e9ed2d89b04da1f914f773449ef657c3fd46d53d650ad9b63f63b59167557c753186b850a7dca431b0f7a531c6b3966412f068f95d7d0ebe1b045141785af02028ef557888f1f53e11a869796e66e224fa8c00723228ad398718aad5c6e5ad543e774613b51cec77a2c0461f5dd5943dacdecb5d08d0fd4d8b76d4420e29d861e563c5198da34f406a45c33a8e786a1ed921299805e2fd14c87efe9df489cbdca020a4495687a79f693c14494e206aac142f370278b66601240d24aaf05cfbacb44a2cecd980babf1c164c4a978b89dc2d3cd847e22cfa194d6c74811c331be6478d1cf176d5ceafb56e282639cab315418bb6531a1a0a544b4aff5c21a583e7c371d0bda431eb00927be6999e8edee83691eca86aac97283bec07442cc48a4d96b0cbe4f03ef4308022221eeee877a2e6a7b90e8d5632c6494812a887b3ac77ab2a1a5016e6ae060da6bfb2593ef77aa146cb59c4878d86b5a4393f322b03b79483683a32724f13c99c214a98e8e9f308701db62800df7acba61999bbc78f100d7096bdc189844f2e13cd1be641730ab5685515890a6ddf6c5ec7da01ae1d07c73789b8e1e896bd726d31cbc3b7b6bee590249a79bf55f6b919a06799c4ee60a49328529c0ddd07fbdbdbd80793c6235fb07436334d11101cf67fe54c5b1af1a5dc17a00b29402d396286d8395bce34f4805dca5a49713deca5526255a520d438df16c90b78badf6a8d051f342c2de508dbe2aacf8773f4ff1c20a303e5b37afbf7940c43ffe3ad0371e097e993ec6e519949b92138c10de0e258048105323bb188a2a3ff948ac5da778f3e459e0ebe70c42be5d52f07e0a8322173e6e84d02b468deed2c3f2fcb2620238b90c258ee0875a0008c00435c9e4ea6d57799c3bac4dd163d78ff8515f7b9143adf5ab2843108606de97ccdf3cc68ffc46247b6f16d9ec91416ed00bf3a69fdcf9b15407bbaaada981eddf94f712a18d3686c06ada8d628041c7d4eb3e93466a7361d18d7a6fea21480aaa5b5c6f6f374b8e42c45eb605666479a0091c5ead8c1b3633dc65ae67e6c1b22567ef6bffbe7a50dde4c87d106db4d073d96f990939a72f77cfaa18dbed1a9a514d2f99f136d7d2f71e0253f6f102a32d92d61fa10d8b3cb6566b06354227c94087237dab0528f8abec1af90b4603e1415fd50e5e2e8ec2f69bd17df0606140ca9e3e06004c8c7efae791ec7e75732e1b55f7b7dbc2a9ee584dd19bc68d68820526014d937fbceef8d937817f133febbebe03d92ffbe32931cafdf37e0bda34aaec7cc961661301e42de9760d2174d17e1b07aab90f33ffd031564a1d081ee606df80da811a969b46f2eff3b63691f80a595e7c22a166173e40581f31f9291c9cda93d3b13c5b3c1d006a3ffabc5f4b40a5d00448a170a78727a8c584ab0856f8500a50c486eefb1f03017a8060785e7c3d0821d142b7a962a60d6fab0835cc7ebfd49f82cd156b9ff275d5f65d665e6a57287c0c87c5a1ff1e5ab11480694a5d6583f53d2269c6f27321f9389db4b1275795d15e475bf5c81bcec2e600c27d5009ea07a1cf62ab784ce1d8e697c4a816c76d14ede9b68404ef697ea3807f91f9f28f9629438b594a9b48e50cd81923d34762dc206d19dccd62224ec6179b5e9476db6f521c8b0d9818b12cbf6d808e8270976458e782a178ae822bf4b05125f38f7cdeec44c328f7497c58f97d49a598819bf1eedc86e71e34c92340088828b9d978ae721e71cf30faf6255058b12db72b0f3fb4c378800573271fce2777e1dc5739c7142653abe4ae0ee916ad887919fdbba0fca2859e52aaef6c4d35dc3d1a43888a34149c79f688e15cab819be2f18031381f2b159a821148e0cbbe28b4b11a4b522b98a8595f7c70ffa305810f7a382c601a084d10f4c67ca9ffa2a51490f421e18de27299907d144750c03fbec141667d4573e23098cb21766e90145ed9ba51b4e7634bf74203e53dfcdf9019f5bad21188768850dbd153e011a8d0a3348373ac9c652415b6b673586fc34ef7a06d2095748dde4043a6ec6cc30a8688a44adb5fa7d01d3af8deea7027f64a7f1e5185f549257dec5413a1a7af83e80a00105e2b35c630f170f111a2d10ff9c0ef1787b7e3c2ef37823b4a26b58f368000009d67e828038beb1dd9f54294455058b909e84206423d75474cf911db2a118d72c04ff520a5d860a00449088111aace0984246e6f483aca0734d37a906dd0d1c76c406121ac5c1ea2adc7ebb31043e8ddb291698e81f12c60d1a5e96de0bd2f30268456f35e1b89025abcf42c391f7735121bdd5042bee59193ba428ba524afdcbdde69455f11e6bd765fbca828b16955b800d8b8037e3ccbadc960cfb1f36ce9723cb068a056baf62dcd7898eacc4dee2d1efec537b79408e4bf5c42398656da4d725f3b4fe36e6b3ecae3858176574305b3bb1c0b4558ded2ba78b0353fc13924b4a126e498c82df3e50bb6d54c8b18686ad90e9bd785645afca4977697f356595748185752d1d631cb79594ecc013cbcc96b9687926008b1fec4e6690932d654408b1730a390c835aa9f64912a326d5764dafcf76b5edfdffc9b6ca91626049ad8e5861e5e92fd1cf5524e1c66ae243dc8233c59858de6a7091118f6cd9d17ba3dd2de5c9b707eeccf089b3e0e1eeb0865e15586e774d0b9ed30a430b68706a29fc80c70f234cb2190770a968a23ace08602088a1162c8e24348043fe8695511a15312e52d76c475d87ec127ec4f17154e878821588371e886656ef2a83cef50b66b4c4981c32a879e7a4b9d04a9761ce53f798af68f0f5cc54409dd7da936ff226adf5556844c8ff1f434f09a31e10a34a396199c670364ae30e719f29716c8b0adb91fa0bbf8272a405aca99460205f01e6c89b3ec5886f1fb403628ac2a711fc86f6fd9e748e8ba29c1b581bf26f022a4fe2c9246a01ff94ea4d56a2d4d7ca2308ece96192402b43d8ed3b7aa0fc974e49c6905e17ab6be776671146aba8aafc6dfb35ad067c7e7457c93c275d31e78dd87dd9990121615146296075d8b5bf55784d498780cf9c096757ef445d62fe2e15890a6a665f36fd1386c84918c6e417f52a07797a01008838b3aa2c9c48590d1987beccc4a669c1eb0ca0e272a1ac53c52fc2565092ccb38d377c6d7baad01e3b019c6f5b797ac88c1b976bdcd1ccbe21c6a16ab08bd6cb02c50b9c6b6433d3b8abce66bf9887f992cfc106fe0a176e4b221414855c71c718165e6a51f1113f9beb2a09dfda7f05feaae3bc3c979673f1de585649f3953dac28b865b00aef36b0ac155d6bf8b8c37ee86c464a75ab7551b8f83c867ec429151a8f8268adfc12755680018f0b990e0821d9bb1bfd08ecc2f465ce3aa8f0e356148fe736053f132ab114c37f13db79ae8d761bf69a0f0522cb1ca7ebf9c10e40422b6d12f3805de7c40554779ea1d62926f4c860a2d999ebadb36bc851f1806b180cb957cf3e4102718782a829fe920d382ba4e3f603b11680e4ba82e187eb393f2793bc7d18b8d998b162b63eef6567cf32e333e3df5e825a6188fdbd7ab1da82551f0dfda9f574d4fa4f32a25eb5324d4d5d42215e07e5044d553c91f185ea4a7276764fe3c4fd245d00ddda565cf749a20229389e65e003c54efbd55de81db224eb916406d066b9f82072747767e3248df91873d42911b4527174edfedcdf5b24a621a43ae2f06f3db316f7ba9d321d2324b45b8279671492499217319690a509accf2de828d25fb2d932bef9318f5344db826398ff58f1a248728f3eef2429e4003fdae4f9c691736e261242f3de77062b1872e0ce3f748c4eac8be874093085987f363846cd99e348aaa8b190e024cb05c258b4bebf839e772e2a83c3e113c3ab8484f44e66ac3762b4cb9274fb80d4a4aec69627df79b313a84b39493be8ab12058e9b3ac39fa33586494eda340e9869a919dc3c5fe02d9ea54c04742efa6dfd8e96dcec1d30d75ea69e7b7b37a7cc37c8f1e9d4766be8579a09a713b9d121bf7d29bab707e0c2806ea798483084b17fee6c3cd327f483ba24bc9cef2b516b2036f2eeb7d633c1b9ee16b2b915a2683160c426b1b17af515be1a195a67ddc4d5149b1542fd7edd62f02025348f091d409b3870fcf50626b198e92be7ce18755099ae3b429231631dad024ad0c32b4dd38006954ea3e817436c68daf344cf88692d9788810eccaf67ecc860a207d172f25b64245344f60aeb604ab65820e6c8c5f53190d221265b23b4221af0d942dab0b53609bd7cb44fa9baaa4e25a27710c0c41b6c5913f425319b1e7acba3c9fab9a45e4f5a5ea894c21d9130f5f8333a809c177a54972a1e81347fa26dc28ece2cf0059783cdde0addd2b617986341db6b9ca1c330db8a1eea85cc4408d9e6ac9099c686f0e26502fe4cac3afb330c816b8547808e6d6c726880e1effcddc1e24c961573ff3be3a0f072822172970b533c03714dad7885287b260314cb8a99319c2a595b8a6a831bd4fa18a94be76dbb375e3e7b4dcbc0f0324bb19d1578c1021901d28c52b5b6891dd01610c4d807d44f48fed640ce9f4487df09f810b45befdb660a3dde25ccf4dfd53774575051b4fab0dcb95f53161dfb1ed1336defe35af78b6240ccc44afd46bafe390ffb0fdcb1698cb4167433fb433b5210a4d4ead02f1a13b677c13dd69d438d3b2a69cf98d555f9ccf7654eceef736e0e625b3442aae859177a09a7aec800a503955e55b9d8922dbc91966a946c24a7c8ada938096e41839cc71669cec204267635cb9b5eb26885357e601a040b2ba04ef4268ebb31638a6bf101cb403442366a596c541372bc5970e1afe72a4692a41c9be434b5ea3505ed0c393cd3a3d40f846583b811b3ff732eb1d1fddba5ea777ebafd9773ca1945fbc4c95bde6e59bc999d8a7380fd1bb07f90e3dae2288b28890a638260c12a647b68e3ca28fe5e84bc25f13dd07918c74f7071179141fab1231e26bcd8e4b9b1bb6f2753b00b7d80cb196b3a4ca1a92025c08c076f40829f99b86d90b74c3c5952b6d12aaf12509ab2b3b86a252b2284644c8bd9035d36855d0c4dbc453be38f27cc38fca8b0a61abcccda586d654cd502573ea5d5a15c67dc0e13abd2ed37f6c40905186d0686e908bccd8ac21e4e6a42a7d72141261d526a99d12d761a693af5d47a5583655a92e3784d64b3cc436b0de3c6a9f8bf30df4665b4d5454ff2684866393208851249334fc11dce5263e006d770fc2e5c0a90fe3602063375fbbfb596141cbd40c0710334753b1139a6201e05a62c5f94d8568700852961a6956e105678c1160f65afab02f1c80f3b94b7b260878d9f26eb0bf39bc6e2083ac49f51fb5cabb49e7e111f667fe7a3629b2778b784932373f411b62ef4635dfcdc003e4706413eaef5f73faf02cd21ea3574db5c456d5cac6004e7abc0d06ffa89c3e32e712c02c5d3a26b9e3e07b84b99a936dc82c8c1089ba7996af052a0f214e73dd6df3ee7a49dd90a55910d1b7dbedbba64c2d80813d1e7b2730de4d46786bd2122b2aa7e1f4cdd008b9bf1fee4c46bd881da6d048799e2726ddcdc772a158f9acb701ef9fe5b9ee699a26a8555280833fb2a7b730519f559cd89e23e82bc9d4c4258ed9c70f0a22e73099162f89a96b6903358f5563139a31d77657bad8e328b3bf601540b6b8ccf8719c9d99e91077fa95d29ef284d037a15011ce309430888175d4765014df89c6518f32ad85f102f646df376d0234272a7e8bc9c5579e8cb95b9a1a9645ef2abb4099205d50d3dac0c235c06af174e2a01a561d66767d1851da4213c914fddf16e7a1bca26dae153aad8416540d2f19705de15ac54c40188d7bd733e915a507f560db112bb1ea9bad2cb8818f10a469122c7f0546919328506ae1be4afc173fbdf7d82db13d0372bc991cf08a81c03111df3421a88381c05a21a16b33d6106ce879bd03accc41e14fbb6e575140b4c7349498c06de5920f07d66ebdd5736aa77807d158cb22cc9acf5d61d161cfce9d07f46781089c37cb7c5de8d6c118b950a8029c3cc102ab143835afd13b4f6664ac458a3d99275dc4f5ddaed9e6012d73e114d5d84f8fae8ef50a7fc8d412c61139104533d4928a1acb7f8c4084e3d98d6dc91be39ab82ccbdd53d2343d322fa764bb5cd56b0050fc22f5287f19dbcbafdb05a543c46f5fcfbc342bffeb9fa0992b01ff8c30786f5f3124e64d414517a7198ecf1c92794bf5672308a090547723f7ebf62a9b6e62006916b6fe59aa2ff6c6b4ba973095afe5df0cc31c9ce1eaf0f172c819764f3e1e2149baa168c5d9794461414d5cb1ce2c2c0cf8079d04476ea26bafb6c0cf73aa4d8be622eb085a031f2ad86e45a85091f9de7cd9d623289e46b7392e6275c528be87036036e8e7d880ea1a04aac30df90266e56fed5cdafb67b993701799fa00ed18a8ae002ef4e8907b903bc44c74b913c70235b307fc035423de63bef6779b237ab22da57ad063d80610fd0f20ae69c0f1b4e8c619d85c01ed39afe388d859da1bf0160fe863d544dcdcb7ceac619200bd9836deb4b2a27a1e202b56720bfe881dcbec096ca1b5b2c85851134ccef51471c5fada903173c20990d9cc10ff5c8e83a0ce08ad0c9d0ba9f4dee063221152adb2941f2b63123c9edf1d860a09df4c80779fbff949ca1e8fc1032a7fca107ce59fd7b209c6dda334628e3cc535bafdbf3c1fc84ddaa5fafb8800aceae416ea3ebf080482f35a478666734cb07ca9e00681784f85affd07224674fa26d291fd8a1bfd41cc165636aae1770b9709458a0117b2f5b5b2ef2a19d801d46a8fbf5e35b3535c75885d54b68cdb8b023c8614ba08cf5e812c72b901c0fd230059b7e22e407d74b9f29b01a09d0cc09b06683bc0103c84c4a86a488750d487e673fd569868ec575a748ab0fdf7b93f3d9111d303ff34ecdd0dd48c962d56fc6dd94af21fc56a8ac4317f09ed4a14c0feccbbbf09f8bff312208d8f84d7bae1f44ea4cada8faeffa27befca17105e7ed30ac46dd9f8619cb4c22845f729ecff0f603bc70f2c65d32b4c98d9154d2a11a00bc01df0c7090748e2e4951ce03f58c661c5b92b95bcd13cedb69f5ffd84abd9f72338ac0da677be9b801cee8cbc3bfb05f0ef0a24294062a59aa90ffeca768af31b73a6cd3e4210689d714222acbf4566a01ae03f9501d4504423a46a4cc5b2fa3749d3d2757307d45e08c4d803fac9ea8e5820da59a503d58d23d2cc8d3208e47738e08f35e329cf511e6b5e8d43cf2332a9839f684e4821ea3f646e0d431c3e00703000439b243dbdb756b41770b0987a7688ffbed0914b210319201d1a5949c8de7b6fb9b794322519ec086808a908b3f4052b453bbb065383a23981d63b540de763ae7da191ae02c09cfd4612c9b02fab14a3184fc7667d6e328527887836288ec898b1ccf3119f5e29a794f2f29060cbc2115a1778b0708476a5e9e4a9c287c74e3aad1c25e141b219a16539820532e0f1c1114daa2001cfb6e4080d0c9e2488a00b7153128fb8e198639c33c61899a1e0628c31c628654f39e5945386a7e6c110f0e3053ec68e3176ec18638c5ec418638c912ff37031c61863646e792347e1b8cf021c23f9e81c77cd5d923c24bbccb7ac9991729774c419950dbc1e383cf3903746238cbb249d877c796be20cbb8e0679ce928a077d67243f45a0e3a5a88a2df884430bcf93266ce71cd6b5a739fd4e2e2f17ebe2625d3c7771f18eba8bcbf86c94a142350dd278b4ccf989e3ae8fee3b79f46e1a6c1dc2f88af1f1e7088971aacf79e5fce49c9f381932fc85c89225329ce3eee56547e98b6fb21f91929b6a57faa6c9adb34f97968f33994c764e51da9cb39fe92b62dd887c1c7b8964b6d4994c44c8df7cc60f6d249a150b20ede6cc64cce9651603d39887c3692e9d9b1e8ea473e254ac3d03a9a4b2194c41e94207938f742db9c05e3e1e73665925cdd78459e55753e3f2041527a8af1fd48a612048515516912d7b3266511fa911facd92dae8d9dd440c8ceb0b29c8d7344261fce4d8cbe58647d843c9f3d2e7c5438944122d1e4a2443c422607aca73d8516278287764b07c710df21bd66ae386d5aa56162656f94e7d2b4ff9fd904851840867791857d57019245bf255e383711a1ff60228c2c2f88baf56156605733f9a5a25912fa09f5f23d6104a1f154ccfec54f261563736a8fae2f7afbe2432891ca144e253fc35aeea2302829d87b94beee5b294abbe94d3a0f165ff7d3ecf9f3ab98c2fc6c95f1c85ba27c73c1e8efc09757ab991b55afbd23c8c55e44b3e9142323eec0b827df1cd4ffd23e37bb1e2f292f1b5708f8ccfc623199fe9bf20d89046c657fa2f08afaafad1fdc9eb8718f893f731764e97621e8e8bf419c62b8434613cea79b1951fa20fe5ab8bf2a194ccd2a994413ffa064c4e205f2d2f3872fee877c427223dd6260f273fe1d94bec1e7bc7ec9c009e7d63bf1165d85b6dba33ceb0df7456fac29aa7f92f447df5ad9f91a8748e485a95253898269e48d0801f16369c4f5b867a4f156cb5c6bbf9e6739bdb746a641a99dfdd4f30cd002427c7437620ce695d7b9e77936681aed36280ebd0006506d7b1a1a85901306a96cc51025c0788eb28c075a6ebcca28ee279f0b099bc4ebb3ac9ebe8b49229ddea22aef23a7ce57518cbeb741247f13acce475b8c9eb78ec27cfe96c3a3ace48ba255d2b1d5ee27558c9ebf804ba25533af1caeb442cafa3334347c775e8964cd2d1a1e331dd9a3e3a56c7a4a3a3e3b3a85d3508701c3f9c8703b80c3b780c3efc460f1e387e87b80ce0da8d6fefdc0eef72701b3ab803c03f1c1c86005ea300bebac153364e6300aee2229f59e3a8164b87005c6aa0f11c6eeac117d0c39d3f0df6d78303f916e0d15bc037952cc07b78356b88b773433efee96a16106f9f43beb8808f081b8f8123f57bf0fa3d5cd58f1e7c01de832fe01ae11e6ec83d7ef8c17df8e2e7fcc01e4ece5df28387fc413ce78b712688fbf08547de078f1e8e0f7ed34484e4e4f8f0c5ffe18b3b847cf183e47c4484788c33391ebd20395f904ffaf0c91f3e29e46b2c0dc6da12c4739c7d7a0faef385421c734ab9203348909c20df9ce2a337857c4370395f909caf484e4e8e0bc9b942b0d717a200bfe99c8f9d931e0e900be4e3204e84fc2037e44b247b05b8904b847c05d01e68901b1ef920ceec395f189d7cce0f1e0ea0011ed6e0000f651ee0e107010f5311f0b0e5a7879a043ce49f392ee312f838cab003f1c52813812f4619087c0ff8d81d20f3ec0df861003c721c88dfc8205f023c0747c639990f8873dcc9cc04387f464f81dc21a2934fc091108af80478f470124027026e589f871bda1fa803ec70c37d74510f1ed771ee10d9ea240078943ae4b0e3febd31c00d6ba4ab003e04f0c1e1b2eb866be31ac0953537acad1b5ad69d3e3302b8a1d57143d4db10e5a907e0a3de3eb2355db348b63849cab4d770696e8e0b8019c671c34ac3278022ec0cdc0c3ec3a52f439d63c087e4073fc277fc70a560d0b9ccc30125085222fb0981ba287519da1543bba8df7015c9164f89e9a2481d862e8ad4ef2da2feb5df48ddc60d51d43bea9c9174c916238932d4a9af5cd46bb8943c7518976c4d20ca24d9d221ca50c7f2d45545d469145589a8a419322952977193a68f4bb66694394d9f97e9e3e2d372a74b7ac9338f475692204d4df7e1a4dc475188e0e5a4410be8951598a6d620a09708b65324de431ad21e1d5fb09427822f914543044b1e0aa421b26264072a51ae4ec734ea73081ac42c9dd2200e829320d04c1f220c03bf226c514a49697c45a2ae0b1db0a0ca24474720f839fa16aa44cf45cf2e54d0333bf541f5d86a45e322149115da6a5031c6183967d97d32be8e8e303e7a49c5099e7819a91370cc68e28a0de473962c59b2c0a0c41cb7d0e8a86996e7ece273f20d4ddef98c4fc60b8e2d796ec22995e62c793e9f4acce32179644e6fe4a4ccb2a913cfe5ac61f7d64326c27a9fe6f593ef79c939cdc329d512b7e679e92b62ea36cfb3a68efb4d16e99c080876be8811fb1e8e7679e83e2dc619ae1e4e268738f1e9bb0b2c283e318d80085178011684056d80080bc282544cb0202c887f8880b020c98caa162599a5947d8511cc94254b1698182a4ef084e49e341f038a2540f13c27b30b58ba6c16c2e7f6fafe3e0c06c9d2c9587b38937fc42810e108b9bdbc4033af956bbbfc86e8be3d7a3c44f70dc5778c5eb7115a1b07e331180fdc4773c331c618976c38e8f0f968502ed99cd2d9517a6b4a60431be33b96dea03b5aa963dcbdd8b50d86da576fc49c5e1a3a92329f3ee263172322e5111ddf7fe002210fcc177410a822d44d7c01212187c6054843d0500184bf80e7c3ee9b8a21809639ea31b4c5a9caf3173fb2a3f3285b277f71294aee01955755de5505b1fee23e587fb91f90e2032f6edd07970f9c62dc233bba17b73ee474ad94e922424efe723d70baadca3199543ef08e16e71d2d1e77bc78683bf71c1577986a831d8f79f298065b3e6b4d9ff685309f61eef5c4018d07438634c8ce116d44af7d61c4c251c11d3d3b0d1f3ce4000d712226f9768a4464f18fec8b75d3d97788d97ee45bb543fb918f1e0f39bdb1ef3e4e8848898d699048163dfb10d9e28ea40c378ba322b2139183f2eccc2131bfbc9c900c13d8f6107bd59867c9845dfd6685c732f36dcecae6b3937d2e37a94954ada15a168b86c49976cc4310acd5ce595353c343e24c0c38b0d2e38e11e6ec39b1efb5782d8d246cb684e5399d9bb3622ebb99c0c5189ada20a845086638a48723c39a9b18d8e9e9c022b66d45429acd8861529a24365dca7989c88b83f4c1831004c0a1e646d64829312a65110cc3b08ca5ccb2eb9f3d350883a39ed8c3b9c1031bd6f4a959b2b31255efe9da0675342b8479a20a06e6959b5dacd5f9bb946b7f67d90dfb8a8c4cc298c7e66caf7443ec0a524a29598b4f9ecbb42cab9f47b0a22a26817020dd350de2fafa6c4ea22554f97845143c8ffa78450dac3c877549963f936e6f6457314cabdcf479d96fa47482469a2c867da59b851abf18f9350df45cd3bc4e4a89554a51b0c55002cb4bc4bf809431b2c33b4949373538344bfad15151111191942d5bb66c59eb0e93524a6f104334382f8d8e06d97f4c6069567d461be9f54e41d28f694691b0196da8532e5e52a4973dcc1d51ca481e54e2d144cdd9712e35f0f286754af74d2ecbfc374ed3ba6f729db4c26645ab7102483942b0287c7414ce33e6c9b62c337a7ae3450f1d543b78b65d8d47fb3424118b22ec278f45d12ccc5a14ca1d13ea568e949895cf91ad7aa5b1181969485aa331b120189f8d05f580e7467accd3ddeeacf55423300fefa878c8cfbac83b8ca432136e525df3e3260d528ed2511ae4cb4ca28836fd3d4bd853f5d01a19211d191d7190110caa5e157bc83d288e22ceb4d7f00e23c19cca4863ec248f4f3ef56963890d1d300166198f3eca127ba059e1118caf031a14126dfabb41182fac2c55c1867cf43e366260419c2147cde206bdb0f2aa3c2171a65de899ad743ee03cbb4cb043d07ce630adce9e6a9605cb79f6855ce4a657e764cd6ac66dded9e6bdd5e7b6af0234e1e615a099be7d15a0096b7d3ae5d6f2089cc9bd6e6a1a4776adb23705ebec9ce90b4dd154f24c1e3dcfe4cd909d8826a25f08ad8b1ed7d9130ac6634a3c8bd0b9f655544b7943ed0a534a9965db067ec76d58456d1703a78dcf805027ba7b8a860041c38718912e2fa7407ad83cf206314427e17100d11007c8d82c229ce5c3138f16409c698faf62150fbeb5c12457820d2b0d59448315b639dfbc9b56df343ff298d76ddb36c77cdb5cfae6db17b9f6a847e6b68dd3368d931d6b1eaaa4c7c79c1deb3a6e6e1e55ed20fdce29deacaee3648742a1b480d134f6a8ddd9a0d49c72d56e9839f3c5b6af61109e4b0fa72439f6887096dfbc7e4210e12c4f44fdea4114e99ddf2e917a39d6f1d508311367da8360be447080d5b9566d4e518b689ae69b7685c8bcfe569d08b95d22e4638e82e1381b6180d90c2c2a1144b2e70cc8e71b6a4e247b26423ef70ca7b363db86615fa43f030b8bddd9bdb22d7e6cfdaad7b9945e8378997131254f29354dca1b5fbfcd2cf32a65cbe45966fa4e51a67ae9ab0e8875d6fac3f4591de23167b0661806645e21c93c626a7144c334d89ce4081a8e87061b88065b4a9a1e38be80c0f80bab6cd5faaaaffaa2d1612bca3a0af4394f35082436ac60373737d71f8c05db96d92cbbb5634dd3b4b885f53594f4aed65a6b2a865f0d2f3b26862536f31b8cd2a22320983caf66cc7c3dcf6f9aab37ac2e1c766b8648992eb10744880ac857fd60cf3cbb324dd8ce659ab02123dd3881e52b48482f247e1935d85ca5c1b6820aa309d2b39fbc35a0d5ce7c489129e42b45f8b7ad7ddb980aaa8bb060fc17efc5b0c4462b5135bb606cb0cad6acb0eff14acd23df9079dd82e5df3e22ec17d1dc88fdc83942670da641f64dd3aec43e7a3ceecbe61b88b05f84dd88e51bd8b3cf39876caeddcac04a1e4cc0a6672ebf6cd6ce9e347031cdab67f2b30cfb2cfbcc43982ccbbe5e21b82fc440d5b4b05df7a97e749e771e1f094c64d779a7b9d61e4e473d1ceade37bdfb24b779f4b44de3e87daa1fec9a6b57084df54362af7dec51d7ac14b02fe37a1e9920a81fe83c603b70e2c0cb70032f3d84a1189093084fdee8770c61ca37c75ca59442ad7ccf2b563e7e1a9d442304de166c48abd02acd8ab7062c2c7badf3a5735256cc6586b94480eac7a456489039e6ee730616a17415f5234f6d8441937d34fb28cf9f5f7fb77bf51830cb7a5ed58f9f5735fdc8d38461f91621a28117c288acd90d896433b0b02c1b055c1c80edd8025b3947fbfc36ced12e23091bc6a250ba8f2a580d08f1e0c239daca48c2d66a2dc6629772eed0e01cedcd7ee4f986dc021bfa77a338477b12a8ee6e77fbdcec22174b5a4ceafc555ae2f764d7f1e07c5679a57420d14a1e70dc5d66e666173bdf261099dd70469f5be4da6298662b9739f6f1cdbe4e7ec1ab8732f222cc45f8ab11fa582ac61aa2ccc7990b22d13727bfd0c84f0f703e6f58ab58ee68c3da7537b2987ffd84903e437e99b536cf3cf3ae5b9c67dc17c3d78d2c76bfaec1da8de57ef5187e6c8db3f4d0b93aca0a28989b4d73e941b0732824edf687dcf3e3ea81618addd0fd86f2fc8d7ca1f886569f8065da5b509b6028be7d720ebe917a3847ff58e9b15f190b6abcb1b6c56587a9e401f1a61b6e6e830babaa7ee46b8feea633ccde48a1cf3ea42ccbb41d5af4b28a6518a6c2fcc807215be8b548437410b27583c4af20b5c42a5b3c4b8293387b685cf942c1f2336f5edd1bb32f7bd33c34775bde1d6905d14fe7da5508689f57955d8e986a76c106fdf49f9f1e1ef9a6e2f5f179cc2246aac7384344e6edd2434023203c72e38c8a1d731c155f22d95722d97740543fc06f965ae8ae6b87d5a9f10daa1d5c66c9f292db0bb604d3a1b653b558372b77e41c59f3c490e0d89c59d716d5d9aa6955b5c3129510af1d17980f01a259f231cf91647d441b97c79c871cd9c2d1329863e08a091bd63c56834367d9678e6a168ecf9ca6665a8936353efbacfa6810335981020a0a965d06e162ecfc748eb1182936f98b6a92fc740a466cd12248892d948d8c283c45f849a93c8d82508c51b628400a4a787adfd434d825c6a1f9e6d66a2d9d124a4ab169d4e09c39a2e09a9b1b0f6f1a9c5e5353235bbc23368bb358ead428dae0054b3dac5952fdd6cc5963438f0d6b242dea61bfc8fc9016fdfcaab0edf416d999dd65819b1e63c2d58b088f1a8f09bdb4616dee1211f93403ab37b27aea3f5d36bf986576c71ad11234b79f3ef587b9ddb146643d5de3e7ad81d413eb15dd89331d54e351477146daf0856d42ca155dfef0587dbc25a238237d7efcece1dc2c6c0f5fb9a2cd74e9ab2922cb04c432d560d5e46b20459bcc615cd1a67a4b50caf5692620530da613a9844679be06151cbc84996207c6156764ca15f432bb3590e28c741b7e6cd0cbecbe2add7e5e3a37397689fa4d67b7fe4c356950a65e2917162c7b9872ad5e1a17b86b64ab9c13667e21cc1751cd3d9d30b19c52ca29a594936edbb66d524a29a59432ab5c6bcdb22aa59c524a29a59c52ca29a594534aa9691bd7759e572a994c16b3d6526bb7ad6a5d27a594f1155a186c606383f3969a4b9dc9eb6cad3162944adeb9984ca89a1baf539f4c2dd6a5e5e5e4e2f2f2723ac5882163c68c548ac6cbcbe91423860c193366a0502924cb37b4919999999999999999999999f9aa54303012060606e6240305822026ba0213dd78c590524e29a59c56c6a965cc191e0e4aa582e9a2468d155dad5673b592bd5af16ab55aa55234542a18981a35562b1b36dcbf8f860a86615452ca29a59493e33e2ad8be3ff86b4829a794529e561e0e1796af0dff2e0c9c755c9052761d6f80080bd202c652daeef47961fbb2bd46b166580c2b19ab192b546ab55aad563462ac56abd5aa5986920e214cd63230270c001fbdf37030c99894fcd8bcddcc47ff9c605959d7f50c1ffdabc2865d8d3e93c81c33b0ecdd737277cb898532edf9d5bfbee608b2eccd51ce2c37a8dca07283ca0d2a37a8dca07283ca0d2a37a8dca07283ca0d2a351c7db55fdcd14636a441e03db29a735146ee59aa95b9a5accec59e9327c7f94df764f639e7dc9e0c0142e1275428dc84befa06e56b6f4f1ac318db5e0d5669b3da1ad6f5a7626f51f15d42ddba8b7398cb7779a44c75ecbb3d52a6c61d160a76fad5c252ebd52f916cb578f55b245bd4abdf23d9c27c7ef53b45b4e97c106d34a7dfe569015477e9f9ead8f5f9ea7d77a4d89daf3e47b0e18d9e0f980fd4edbdae6ef9ee4bca54ef8a6ca8f22955b14df4614a887a3cc04f09355861786c98f249f9a47ea24de7d5533188369a574f01a57c6ccaf5291f276c37e7d52f4f9794a9d795c30b7b85640b0649ca54975ffdbe6090eed15718a45bf4f50996739626291134199990f0a4988ae24c957e5dd7c4e315c5996b64c3ad88475503125fbd069e68331d47514b8de7da36417aa8f2994171464a0f4d47decdc2d29d94129175771aacac6b4404ab87a925be7a4a68aa7c4cbe7da5bbd338b8466c4cb428541c94b6a018a87cf53025f437a6f8eaa1cae7862bceb4c97dab415fddf4853448de97f7d56f9366957cc22035abe4d7755159b0e1f6e175511c3bd1a6c6d71a5f7ac286d7354d53a287735aa2c17af269b0faa9a7c17a9d60c353cfc9e7d4235bbca3fafc4c470d56e7cf84d4a0e9e84dd07c4317f1752bb2c257f7b2930c44f56661397a3d523d71a64a71f2d551af6863f22a69f0d5c3194a7cf5193dd1463a47bbe9f558fdbcd5390d86af2158f4d53757add66e2ed4ab7aaaa74e1c7c78eaf9ea3206a43853dd861f8bd4a3c69b6ed157975ff559fabcefab735a9723c9ca20e430485fbd08ff75459bfe0a7385af4478f06fd7e34c751e389ebb4baccf5a6f93d415f6beee7535585d730f0bb63dbcaeaf1e63ea15a286a315155c8c0963c850c2dcb46f4799a03fbd4ab3306a2562f9b9332952124cc94fc790882dba844825b6402983146db89f537c7ed22528f1132b020cfa45516ca1b80fe9127e9e1a9c3c3438316f55fd90bf43bd2c7673fafa68705699b64a0e27aa1f1b52a756ac5869109c2145a23b718679c7746ae5c443b3a6db00045b647e88922d1f2145fae93aaed8902251a46675156aa55949d5a7532ccdca7c3ab6d32c9e66519f588f9266613fd813cd6a9f8e45d12c55378b60b79dde7a8ad1539ba0a03091846699364cd3b4397976a7780a5db638cf328e6298e79dfbe05d1fb84b43edd47a6231a30d4bbf6949e34cbc553ebaf671a88aa31fc2c6a12a88fee350154ebca12aa4a8b28661bbf90b398cb3145b09b7013b63948f5af838e48413f4e468c21076708037ca1749f8ae82434456868852f01c77ddd4070f509e653cf3511c1a8ae24347d23f5b7cd63374e577b4d3299da659d1083cf8067e761ccd9246cfae03df205d070b3ec421c5c412d960caf73d31d1ed71088a0d629aa5ea1be36198b021aae70a17352ec65894736c551cf16b8880c6886597f27962d89c92cb32c7bef8cc4b66b21dfc11911ee3ccac12063952867d48b3721ae41ffc40a826f0fded3d8434c8b3e7c7e0850de311941c211c8317164a100d6494f444a90c54ea26f5a1288661ecd2624b5ec76d5a8d1813593299aa093485356f9a6e3259d962264d13462bb2b3d5a2dc411a1ada1c94aa42dad6924cb1d6a90516e7d72c8be09d532408aac08635341d73bc31f304bbb0431ac421e52bacf91a59e3f46f831fa5c49e5d05b66b4f7777ff708cf970b15926cee74be6d133dac4af75db9c9ddbeae4e6dc6657b5ba75edc9d5fb2362d6ce9e369e3f5c1c806547a5c006c1b0393195f4232f6fc80f848e1e78477c551d4f29a5d479363798028b84dd31813a08d5e040035eb18c43e8a3c7a3a6d60ae2f8f913aa6b90c08615359fb0cdca32ac72b37628cf47837d136b8742a150d8ad19977d45707ac4bfc1e14e1ce617382bcfde9ec33f413aa0c0531a21078ebe6f163664250d6a40c543c9100ff4a01bb36b21f613549d9d75c03f54f340834302b7bb572c0c914a8ce7cd82e52eba99941dcf8d1b9d754951bad1d1d9adacf370b848b6ba242dd361c7931dc9d0511a7bf4cbdbf13831c337e63482514457e18c5a5e7a799e42f1ede10c4fc1686f20d9ca80a44c7bb5a83003faf606ca8abac84e41a1302141c130f19488aebc82b06bbb1a9f01c956694a5015d795522664ca883a9eaca8d4f168dbc6b1a5876b1de8ce53e7e88a4ec38a2922c380b977d09d162ebc401af2e2c85ab1a88f435e0865f12207368b172f80822d7d1cf2a2c7a5898d11c60b126c6a8b5311eca90a172a76460f5ca4b0319a7092022e5081a7050b3be38b29ac6582152e46d8978f43549ab434c1da8f4354a278c2d68f43549e38a9c07a1f87a8acc06504544410830995225a9ab0dac721a424242b521869e7055c8c094f3fdf1e637c4286f4e78957422953ca93ba83289f4fdbdb81775cd4f6837d5ed8902ae901c377f6c8d6e6431bf5d3db8fe4d8a51004cf3dbd1e359e9aa09f6f8c3392d927da60de252abebd24146da89bbe60c1b75a7ea4e7136de46c115ae295c4e52109c2f2dd02d45d6921ea9d4fa88a0ba8c6b7fcc8564769997623a0294155aebc5ee8e7ed68533d7ac71b3496327336b2d431af470d9f92509ca1eefd90458cd4ef3883b926330601c7dd25e6cd87878da73e630a3473fac553ea3328da48a72d444fa977946e997e76a20d911096d7155715ea26a0a774f399e28b6f24be3d6c21e28933edd82774c505a4b9b21a6ffa91adcc688a09a88a29e88ac985c5f4aa484c423c311ebbd94a09cb1ed24f7ea059468e33537294a79f13144c68beecc0d28f435388de5fae98e26489293e9805b8c14b8c7b020b28b6ce5396b8420a46358f79a9c61552f8f13c1cacc62eb408810b98c8a2840a7e3226be4882ed64d8028c9fce61188669e00a12a88024084646c8206b411694f0188e0843146220250a5dd882093f7df370260d26dc38fa313ac282d15802141e7382d130828213383ce65914c2bc5842e9bb222da23c01cb4fd3132c62f1081fc23ce631083430b7458f3d61076f8a6243eb34782cc663180a75c5886ec76cd6351ac157263c2baaf55aa38c8c6699141688e0850aa218525e90c4c5083f272924688152f42285090aec932217294748624242508b142590a0f464c74a7942128f8a2493142a4a744525295d3001d7e365c193c47642550154455067b340010934a1242e0b1794a0e630e4dab280a204590e46d0b2e88109b01c8ee0aa5914410439100108093e395491b4c5097280b27385278722f00c65812589263924e1c990134690c4939f1c84802c6a0a495d64f9d0fa0a49f7d1823a62c066611e62d46aa10b242cac8d8f435d50d1a204eb1f87baa0811576f571a88b244e4dd8d4c7212eb0f0d2635b3e0e71110597cd3e0e7191454b09ec8c8f435c10c1250c6bfa38c4051330ecfc38c44510c8059094893f87a64879978f43435bfcfc383465094fd3022f4dc1863434a52d6c91f91d4ecb10c7c7cfb1b7432de2611a94492cff08a2c1269a828b31e125fae925e762d5762247980f3b6ef1687764abe5b600d5fec8f3b3e5e3a582ddb8ee44245ba61e7982d22ce9af183bae2453901514923d329d8a505344d6468208a25cd1266e2568f2339c41e5a7cf403ab22f34882dbb135f80a24d8c9f76043fdd88d8320925f1d3811a9c517c55d8d02454f2e92628cd32f9e4d126212933dd411abb63bf979fef45c98bcfcb122f3d2f495e788464cb85c8a5c8e5c805c98abb243df8b2f3d3d924f473ba40e969708906a794d2e463ea6970baa9c7d4235bb14353cfcf17a09f2f40cd9a3ffd258a66c52129497efa4b50b3bc9ffe52f432a55b1cd4f3c2a45971a8080bd04f0f5f9e08fd642548f9f085e8e78bd1cf170ad4acd02414d4acd0da1d9350b3a6b744d12c39859f411fb630f9393ddce6d1bf00fdf60ab7d777b4b1058df0e506370b1b879480f4357ee72f91d7768a182949604e948f606a666666250d36fd69b0a9e9b9ee981ad1a34b8b4edf4e8de8916cd1262e2a44a1a89eab945912a5cc08ac8db12351c0c5983075f4ed5c9c3489cf8ff634d8b29b1c6dca914e9a95a8e9b9597ac2869b4fea492a4aaaa841b05345cd8aa9574aa8c1a61ea65edf45fac3cd876f30c1b74f1ed9a29bcff69323c902bd7cfba664fb912dda14a8c14e117da78852518ab0a1f173f391410a1bbd99fda6a4c1ed67f339b2edf2c32d65032ec6843658f98c062cec26bb6b64db654bd9b17331521a9d8b71869d9f9d166d5aa3010b5b791a8c49d260e63c3d3024c180a5c1cc638c31d280859d3142315424815f7c46e5b39828842d1fc608c55cf199a7aca84db050404dc1aff0598a0b9ff98c19d8167771f94499ccabb57c5db215c3e3aecfc0a46853dd940473848d0d720c4f8ccfc71059e6e16956928c5af199c7564aa8e5a987db934d28dc889ed29e5412620b06189294f099570f879e4ea7d3c9c5c52485063d3b058aad139094c9fc449588ac938bba247d5121d93a39fbb66da6a28f36a6d872716a9db918dfe9b35d47031636a3010b8b752f1fefe06294507a637e3ac61743748a29ca8911923299a3aeb0618c4f8c8f928c8968f4bc4961df9af8cc431924de43199ee75006299e8732143d773f529e69043d0b4919a01fc4259e5d349478761baf68e3bd0da1670f6d40d13c8c2912a2e17aa6f17aa6d1f31cd2f0790e69fc8c200612cfbebda2cdf6ec3176a2cd4b0c9e672b3ef3d88a118a32314988b1c118257c0643d2b6f32921d98a118a71356b9514f383c85a8541533142b275025a2535ebe4ea55189175fa410433530fbde3e5da000418923e73976f552465b213906cad90a48cab59ab2be2e90727a0a72348d92f251467b237dd15519cc9dc8629ec2a29da9c3e731b3bd1667ee63000451b199fb90c51b4317906942483f4314c2489f16930f350e68a50a6e8b3166e95e4d348c43091510f6decb498e855fda04e839e3a024a4b5adc48fd252e3f9fc5a3efee4dbb7c3c750712294a9d6b1e4ecb9782d232996f1e0e5f2e25e4e1a4a0349839b1a10b3145cd12821243141325f3939368133f9ba5cda759262622cbd4c4f6c587ddcf67ce715f6732492999062cece66974e3ed6a1fe7b4477b451bce33df9488aceec7b79e68d379e74404330f2912da13ca226a4fb4d13c3c39f98cf67ce69cf66ddefd348b16113b27907c7b5644527872b281607ce6a129e93319a8f8ccc355d267ae7d3208c59922fc27273142dc0593e24c961212caf29a17e19450b461cfb83bb54ffb2cab91146732c7a1c466ec618cd0676e83159782c9ca63abce7b4e76d957faceb3cf3688991efc25618df5215b2d3da2c6a8f4ed9495450b5591000000005314002028140c884402915838249635517d14000c89a2486c589f4ac3208829658c318000000000080080c8cc0c012001573354200a96e330070e3e4e609e1debf746848aad11d53085029a9606485cf099bea0eba609fb91637a0ea70b5bef55d5c3c8f49a83e0172d3997e222e8b418470c393836269d31ad974f01315e3d5ebba8e2c22b13bfa914f062208a692ff142731a56b03f597cb42f1c8bb3438c58d335d7c18e073689b641c6bb309323f092b83b57a9f12b38465c65d8d6084e2cc910c9fbc3877459814afc0faf6264fd99867d23d5e4978d6562ab50266ed4929bfe872afcc1c3d42510ebf39f79f8bda03fdeeb476501e8cf18ac6b31a21c7478ea8cb76975533810551ea71f404d648afb0a565e409282ee5b0aada09fabd0a2e4bc44427faa9affa867470deec993a99ac1011caa5d71072ab0f7cbc6ceb74bb9995f65b13332fc13e0e5959671488300ce489ec1477eec56754098eb86d5e1ad08e0d2df9ab2ff07e00fc053807f2b23f02f74207e80a050dd7503e746ed6bc9dacbcedae4422afce1b89f41e82b595669afaeff25da15a93f8877362ffb4a81a7a8a27737b9e40f9ed7e38a14813ee38a3b6f5d03c8b32e637984b5673dc3508b3758bc259eaf4f5b8c26669060231ea18650581b673316e4147ccdd16c70165722077e0ac326c74b527c563614dc069ac15ae10abc2380d10bd9ee73f3d1d46b7651b1e4868738a343b6f47a8a559767af3216dbe308fbd7bf1542e00884c1a9258e6e1d7469c5ecc107725ca49e04abbf37f330a98e2ad01f77c0b414c5f1a651f4f4559b3330a678e2c20752da9cb95fedc3840c706a67c22d29faf136320054ff56090480e4b3c17abb7d0999b8a9df5b867c50df2af56ebba7f6bb1793aedf53ca71d372613b7d494f6626227595fcf3c9843a3fe19c10d6f1c249c788b51098862f33abc01d87597fd049d3d16bfc8fc69fb819f484bc7562524806e0939f72970b941900356fe74ccce5f39366db5d8b9a13f79a0474952cba7d2e4c57b1c315aae8368ba2e04f270d383b65174a2155b283793cc1aae0430aea83bf3321b2bf1855772593e76a1bbad4b25ce0c4b64146a64657c46fd8d27c7b25549c549c8910e065d078e7c6664cad75c8667b8a737f921d51aaa4baa834f7df5f48a52e4b1e149a5e091f1e77e7cf4ab13aaaa462f1cd3f5757ed6ccbc6ac20d05e224f537ce1b5f3ea38b1da442e983562e862926189d0cbcab2dde692388fd66b457bcd6d33ebde1bbde7fa7eaeef0fb4af7e63cd84a71e7d80426cf53485244d58aff448f7db44cb4b7b2d2d9f300a5625b1d5d22d54bfa0849d99a44fbb4c03bdf9c32f35e7de73275cf2eabbcdda14518cb4170e91d5ecabaa1199559a01de3919c5071c05f1ce142f54924a96fd9e9dd090c1974c77a24dc9ce3c4f07238dcc7037c91d77ed4c2500bc68e228d48c7e692f549c22194b1811bc106efc634fa9d44f9e9bb536daa2ef62f33d4d295cb6823b085bab2380ab50993b98aa91527ea20b4d2e4f676a8d78324f2a666d5ee390d9e0189cbf63c2eb373543a5b7531206409cc5359a9857525a6b589a57b24b8e28c34171761849c2c3ef80da60bf6d7ffc7611f6f9b72c776c00c9cb51f572e76a14a2317cd350ba6cd03f9daaf599f96fd895f4680d672912b265658bb406810bfe4b4363f4b76e704125854c245ab7af1b46c4c63f92e2bdc11539507ed97c50ca4879a67be56a525973ef4e4ade2299a948183180bed50d995b5846d23aacf9101a06f823050968c6990fef8628117bf31ba6eff5a07a08146edc78e8514c767272874790e86fe9c61df381e254b4753c2978b5b2bff1bff04db74cff17dcd5b0fad4d27666235814780f86e330aabee8e37d24e443525ab510992226069d9a8d79c777db8d1423e3e2206fe6617d74e2056df0d689cea2f6d1b0f317099be84455fe93b833542cdfe169a371bcf7777bdcec82094e38a037bb75f5d3ead0ce8db0a4634132a0b1bf925c9684677a7b53a95c8266546e1da48a96b1c22606a7b78453ac42d8c7cd3b8afc7412fa9d3720809664020e8092241e25ea55422f04cabc0ceac69bd7b17f0f9caebac4ccd96f9a5639e3074ad870c400186df64b5f071c0e6154a4feca8065281dbce4d2d93b1d7af522c178d35c816c5cc20409208e7c35444a31f320860d208cecc52741f34f2c6fd6cde3cd876c649706df046e11655f741f850aafcbed80fb8c95768dc1dfb1ec2395707e256f46c18eed810624cf081f8db9e82d8c04359fd20c35a3b04941fb9151076809a6790203f6fb4c9f42d33ad5342c292487ea7d26155e5b831add2c2c482047eb074b9bfa96c634aaa95892680e530ecb37752d8d6994538365890ae020fd6ec526854960ffce9f5949871b9e6c8fed9b9dde708997365aea130acdac0c8f06d2ac462a8a71e8f7563665ec01d63405ab48adce59a732c1da199323d8b04b1de4eb4bc6f712fcd8192e3ea157e59d973cb152d173e2e265f1d8f6ae2a3377c8b92178dacb34f670eab0e204252f486ee0532b21dfb58c2357808388322e422c109a08e624f1b6195647f25195e6671ffbdb93314147d5223a094873ad0f31ef29431e19e063534897d966eae87c5802980afa5619fc9fb76302b0342f18d599138641c646cc8937b9e5dbc89739c4aab23cea83e3b61d735fee205d26f7d031326f6fefcc1f1b9adbae7037f4f2f5f5b143b1db1b001dd0f253c66b4722b40837fc9afe750f1bc4e9bd4d223787ad5c04339008ab99089fed25783262ce8ac35058b0b2f9fe17b86f4214842d5230e837694e123dafddda4198f9f0cb6593ecbd42f3d6e73b4e074bd37c055fbd5d624334f542e738f2cbf7dff0af61a467034ea62a748643b121a73cb46296c580ed55d9419113d18345938692450a7e2ceb05bf7415d4d3c80e5ccf3e99ef16ed45eb431ca90b59cbae63bd354dc2eaf820cc62af95a338f9c72badf7f0da2c46c14ddd8ebedbe510328a12abf21d6a764becf6487a90670a12078dc3e620aef0648da42a80a9d95d04909f41102eca55217530214fc51a8eb61090c0804faaebcea6ea5023c1119e8ff619ac8b083cd7f57e49f5b7a2918145ef10102bdd36ed87981cd25463a29cc59028c1f19440814d739cecef6a24a83e8d6fc6406a01a5bea47771a38a13243c97b9ca21765884ab88e98eea66f251c4d39ba5dfae290b3762b4280f44c30f24b37b997a34d59b9d03ec063bbc55df6f6c981d5b0a667b52e44e064ce4cf8f30f1c74c2a146c2f74197d8937ef59d9da9dbbad932b3f34a68538f1adc96730e4f03d459c4f46242b247cb3322e6c2538db50390dc9e672ac0250b09f5a3de086d81290eeda6f769c440aa0bbcd00fa5e50703880e44eed5ac248a643d05cb624a870068053679b0c72e09ba6dec1cf20bc6e97acb261338ae66a88dfd1721dbe830ccad71878556c3d5c6c077c88d8c33544147c1c021fa21d09de1bc393896fa23a8380b587b9bcc39a32bcd155570737e140a815bf27ee24ad9e0603802c2cb65cc49b137c393140b5d9b900349dab62da9f797e78bf2ff39e6cde8f811d8ce768a35d5f2cec310a9ac24cf18efff8da4b2db35f822f92d4ca95c1884ab140338cb1d25cd446f20ec58dfd704c60d7b38cc44612f08d0630ee2d70943495a8a48690428081f9e05051323c2c016e64a02828f77e5558edf69a742bb9e7f612b645baf1ade07d212be13422d1d3789aac812a75dc1a0462073989ddb491d6351ae8206828fbfd154588ec7926290080bf4b2d89aebacab03f21eb39100f7b48334adbaa3d703041a61b304c224101b1169d2fed4e4b3c051bf5c93d3f25af7153dd08bda667e8385f8f04bc32a7c00701d442b97dd5c79ed58417b6ef5d1b33194874e27dbd8b86a9a65ae67ff5f4f4d6d7610ca18bea119d2c2876598869bf4e3bc27d3b443222ff2870cec71da0745fb700ff277f34c2553020eacb755f64b8df5bc0a1c5bd182d46ee4e78ba026e9975fa9976d185b3b0e7495243d62827405b402694e3cebc74f2cb41b139484f71c56858cb2a256b878aca18fb90b01b7d7bbadc13d464afae185a967ca7614a1a12f3bff6c0cb8d256446ca9c81b0d76b1630a6e20f948d2b4185a7437efe088a284426264238b125a559ce375c7aceb2cc6d06bcc51c0924835331258b131440cac2468c78399ccc086879fc84ca85559f1f0cb6213b8eb18c4ad824090084a8088be72c0832abbdb3c9576744ed633c62f90d3953cea592e0e5ca7978c920a07596ce2583cf6345abc57ecf4c8168eb97cafd96d84fb5f9125996072c64b98eaac754fe675a2a35da288caca743d4e942a9fadb3a3d34a959083942157261848f5790b03deb6919a6cd11de8134e345432fb2049b06126b05169152fa9e3462b1687997f4bd5c970d4683bfbb4d8014f9f0385da4b86315eb4ef9b396e00dbbac7bd268c9b5137b348ea8ce6fac06516d2018fa487d588aba6dc8026c482521e397a341b4db073c74175a2cee19f2368c58455d70d5ffecbfeb84e0bd31fac5dab052ecae70046695ac6850d6dcb0d854c336dc4256d440401e45ec4c63e51e7b5d53088dbfae8a032c58bc363b4ac65c06d2fd2224332dd188f73f7d15a71539fe2cd049c9aa4b5c2502c99be086079ade0590993ff90343ca45bb987e8557067ea886b1b8bec2165bc31769c025fcecf5765cc39f8ab164cac05cb8bdb4b68a58cef2199c17b7709f89fcb52e94dc9462cadeb7ec7639c2975c91b1d2f3fd488e8130373736d09515c621c31b5661030ab1a123ebdfb28024e95a62cdbd13ec2cd69ad445e9e8c012b546d63088e95cad1feeac41b671da4f1c80614aab20b0e7a2edac87d9bf1735bf09ba7ef32a54cf1a3577afbda5686662a168e31426020c371164726ae4593b639bedaa9c5ee92294fe2ed051eb3e3561ade66b35cdb2d363b17447772d67a8a092c1176bca7c5ce1d0a220e0651cd64d765c499e185a8d80c107b7b02434e36e2f394344172298dcdaa31a6336f82acee40a2fdb3376f661707704044d2707b46a1744c038b825e67e94d88bc49e0f103c8172a01e07d7cbb29d336d5b6c3166ce04c7038df965616393b17672a3de11376ff9934d28ce287fbd68508db8a958fde157065cba0e4f7d9bf4547f9c4a306858ad437d3f3638c5f7adc0fe92a7886aeae0882ba6f4917ad2c790bb58172c484b034339c2c9cb07b376b3be379064b5e28e98bc4b2b56da5746ab327c7513ecce4fc2f32814f5e7aae8d9f557b4f71c499b75b3afa2242435bd2872d52461972a35e3b039f536d49cf69e27b9ebf5ebd5b0ef26dfa948bbd3967495c0ac5cb186bd85c9acfb53a5dbca64ad998c42a3f4a364f64fa37b743629900186dfcdb91bef7226012dbdd8934666f8e4d88b980f7e51c5f69da86e8047a1bd9eaaf6852bf83b9f9e766998689ec215df1ceb62325c201c737bad8d1e8cbbf74441731a8a4104c1a4edd31c7abdd94cba07b824ae7c824e6c4988925136ad6599454ce73f81d84c72149ed1369717bccd9e65c9d43bba6fc84618e77a8bed7ea817a537bf63a767c801345245bbfb393288b058dfaa75882cd726f000587fad6546bf66b1139113ab1960d6fecc145862e938457208852ab22b723790a868961fcb07eea2208395f0c413640809d3885fdf382b01fbafe6b8ee0ec9d4a0de6eb3e609590166d09244db4f95b259ce10faa4356d41823d5fb47adb669a9f10777caccd3887da901ef405ba92262795176c0865e10c5c9a99a637d55a14ea807a671d551945d8c3ff53fb16838482ae8a597b96eedb631dda6f06bce448b11afb30c6b23cbc0f380868bbd34d0d5cca335f77cc77f74a68f92f21accacab12811376e75aebe82cba1ebe3df7c6e627837acf45887ce9d0a273835d28308a8c77bd59b5089130bd40ecf24265efba7c7edd95e5cf6ef45ccf51ea847e2ea75e46583e6d7ab3bbd13d16feb3096ebd988f90f7d37833ab1c431bd1ef7efd3e12555a2b339908b05ab83fcc8e0273c91cacd1e2ecf4f0c78fa8bddf228a9e7da823418f883538329ca37f5d8658f4d356fa38dcc19611c91226620403e9d4d805844c04eec14d52af77e80332cbe525fbbbe266acbd6d831660f0ce4d06e10c7030f4117fd8aea78fd4635658136958c7b1a10724aa95a513188708a4eb58da7a46150d4b5ed5924b807b0b3a654c6b4fef8b4c9c1ca5b123dfa6554aa6c29c7b55399558ac651b95d63540ca0ca551316019e872c5b7be4f84c52f4b42b0c954e196bb38d0933fab92b8c2ed03aeff63c7f171b128fa6d2c019c46a52a329bab741d80d2e9425175f6bb7c58332c6c6fc5e5ab5c2c9cfee811e29725ad4aded731404e66913a244f262e301e4206a8156f13600dad87ee058a94261dc6430a90212e4c8b829ec95eacd02ec185d807d1d2aa4c499d0f1e337e8bc0061608e0f6f42d28af8396f658b70eb0628221dca21b4e86c338598c9fdc1dd2255f7ebdc30e2d941eadbd7a6f278b04a33d98c02b842efa2637a23815f1251dc8dba11cf7e17798165bd724727be4bbb4b5d197e61cf0bcd3c5688e3eb140f94b734e3db223dfbb5829dfcc81a12b8bcc45857d76f6bb5599cc1a526972e9d1770157ec2cb0838a476a54171b1028b5af5b5b74e85e15931a1a42073f25edf8000f85983d9251e72558f2cb2b9f95f1198f1733dbb7d5dab7f10dbe7c00a80b522872b21af4799279518b4f5ae7212a50332ac9a05456aee085f101dd9ba384bdd7966a3ec0f8b6aff7fc901ae4c288d2b4b5a23461a042a73904fe9d56c227a3ff738d0e5471657c83a3fef16f16248ed7086e3c64f08aecddbb826e2f1b503264bae148f06154dd1f96a9c01c15259c1de5cc54bb8e0fa0c9051ea9cd3b34dc03a122232fc874c883d318011f7504f1542cb20605fa0e167edd705ca18dcfd472515a0e86c4edbdc408ddb24654d8e4b0ef1ac07263c063ef16d53ba74c47f4de83c73e6cc7a21500c2410083fe4118aa7df06c9a074e27c591fc80d1126afeec24571a90243f7972b1a7e6c5c31c84c1cd94d0a9157e2db5366ab960b30eb01b9d40bcc7d95905d3f8b70186df067c62e54294ea3dc571fb1ba7b59a5bc2ea8bfb51382e9f34d2b426613997ff15f969178192f4fc286ecb79c1147268bc7d3312db2240056ce8e0859b9e2cb8498964c42bc128c274316e576680fd6ba59fc1251cd6402080a029a6312eb60a328688701cacf528caf239afbcb2a46264faf7009028c2fc9c4a1e483a2d1855c79ea0a8cc9a9c3d78a4aa775119cd798595716419e0831dc9eb649f9bfb4aa45aeda0b2aed2809267cc29b0193821cb46df2346f7343366b572d38d13324466c23044de8aec3f5828c3f6b933ed01ee81769cca6f4ae8c4c426353c1d1a9efa74a2640c0855c3ca195561d4fdb9a642372b2199162797defa3e83c276f5154223d81367a72a6b929ee4de0b8beb2e5be9209de131331182315f30d57b6466ab2bae33c947ddabae4fe270f6881af04170237ed807e319d221272a90a4c7a86099a8492053892b24280880b111f4432f9c3a3d49b8e0ec8201bfae8901ce424c02358846602991a8a4c18eb9c4db82725b036df851aecfa0c5e5e58e7ba1f8fe2d78e224c46b9a9ad584f2c7a2ad511f1fcd3c4cc94e013369bb769331bb5a3969db799994d8da6f28d89f5049e1339d917c8419dcb2aca1541bdb7d6fcc8f67644a9221cf18f7222c25ff48d81a9237ec46654515aebbfe3b9d682c5a49c6a8ca726722ed2aec40901419146192b7441159427c3110951f0ff402ba557b2ab18172c6919932f68ae1c78f3677e9f1f1c50260829e4fb3a4598e3f87a86793339248ec1a431e7d9937c0a1210314a805b8e3057df4f67c38b2e7e36be5cef8d6fc0451fb9fbe0bfa6d8f90c9b3375e64b71682f6ae50bd84a10618912e44e65e00b008870f9d92ebe620506bb60f1828e20358502c7a92f9402691fb1c77fd46e6c62a610a607413cc6d4ef1e1fc98157a835f0ff997826704af0408fb8cc5f4910a20f0abc1f80f909e33a8a3915e1ac0939d476e5a199cd4c9b11708162a668e9191524408d4e572ded8c3ee386df86fe0379fce6dc52a62ea32ca78a41adaa7ba4d9380ed73bcfa80895c12002cfb09fdc0d6a3d4dcca8f11109a10209575312689a5803fd7b5106b4baa7fa4b165a8ab589374f8690c61078fe14f4f22e21fcf7351707b18f60b7131bce1a2efb71b192d752bc4d30d567f5238a24e675b84ccf44ad1c23edb9ae3e8f722e02f2dd5c54725750741eef02b46ee410a17ac32081d9bf1cc572d748731cb2412a966afc08d951cb5602080485b672a27de8f30090d684a06887c92bc2400072e9d6f470c50140c399d7f31bb757f78190353c08d4eee8bd56ca7fd1e2f1b079e6aabab0af4a180832777e8303829d8ba2cd14bc40af1bf97de08273a6b00522cbafdd18ffb5437b292cee41fcf60114bb1d40aa26dbf1e1d4bbab201d85d705ba171d7e139f65288d3cdebeae745d41ab0af345f33d406e0de5378b92b4a3fc75c9a4bc18fcae09a10aae06a47acb120b209ba5ecae162d4314ed0c82ab408e5e493f5d26c8070acda5fc9dd022609536ce2bf77cec9df5032e376c39730ad6beb2b979747dba3554db17cc5257db49c77da6021a0bb157860287b3857cbf39ff7980d2f60ae184d18f65534f0ba7a3272c9f681f62c5e236de6421cd1b583f0562cb95d6151631529895188969cebec2f7600a17db7bb2e108bfce5c5ae803c3b5cfa1fb7c29b39b5ab2d3250b823a732d6a3565607406696570a1919749a3a13f9c8e77d74d2f3f9c36fd5fb5b18ca39566cffa908942eb70174d8edd603ce3335664c5fa997625896a392579b4129be65ea75f056d465d1c6be34002dd02e4ca0b39b4b8cccbc5f6d6c7558b9a65fbb1adb5e6c9e1ec05952fcde1f886acf97027fde52dd8beb664ceb4bc0a707f5c8e729fa054ec96a5ec83154694183e989576f0b505735840e1bc1e01c461612ca92d9fe4535e6f86fb5d7ec20e6f6f0bbf99a49a7d07958e7f70e465d57826527b578233614d6359151b991fbfca9b6a126eb5c723d9b565a258ea5fe9e2c002dcaf8aa6609f0bab7bc4008773a3ea41d5caa86a794a959c253448ae669862712a816c66d353050599b3d14d8310166e1b774b29b6c49290e790e65afa5b52326ae2a8da7bc91318ac7d624d015f108912a2be1f9f8ecb881d9b535e16ac3ad0681725ab07759dbbbfc83497c8712443e455520a9209ed0a383b0354cfa0e0021307d26a2efdfea7cfc812b5f7d0a76d2c3f7dd0ecb2ea66a5214cb9913e835a9e5d706e77c9de31207bcb2c1913a16fa2740ec8c71b884b69dcfa65b878c212dc96f1abab2379b8380d12803717416a4750803015b670377a1a18c1b26a91a40d41adb2187a16d75ef5a9c9fd7b25b0e34afed06dc092719dd75e4459561788d475261a46b765c2c2ad7c12446c7b70e44f307f7987f09fc8982417a4b38aa441c23e1d3385f9c48995dfd88467001401ce4119dbcbfb745a98a14bed76865baa18a724e1b56b764c8b91115d800b00fddf1a01949be723d9f1eaf6c214fcc1483a59ac1b89732f46d343612309e32e294e00e8f57b5813145f409c31e980ca1c9e9a2457e945956a58b70e230e5db6dcbe9459226215cd205b5ce1021fc8ee97ae999842daff6538bf01183fdd58b20f1d02a3b0a950c8e1c4112b96136be8ed381f9c3c8b6ee16dbd781174f642f26945f92f2799016837f4c4ba011f8f82143397664ba310777d759c5f6e169747e7c5db557a171f48f7864906866f54111845e7e98c5008ae08cef0ec96e4958b123f905f4a8959fe3f0f65cf068e2997b9b115a030b268efa6be0e741066ee05c6aa44c015602ef038cd62a698fac3653bb1270705fca687fa3317b011b886588515fcb382d6820d819f5e0a29053277cbf2b98669187341988fb14a07a66eb94f051b746ce9735014b818b4a0b465a0ad82c25dce94596fac7250296d554a94fab54ca967497e490babba7d6ca48d7091e81007e508bf179a166c32149422bef70aecf31bf7279eb98d797b52bc09585c34c3b5bca296e3666f367e1eafc714c05110644e2f6c41a24ffc98683ad7797e714799ba7a54d28dafb2010cda1b5559bacfabab8a91358f33fc511642c7cd403a449a95a7bbe8e40ee80e4cebe39f1af111c2ee3f350a574844d3dd72e42478ebe261dfee1f8889e21888d92e5b99c55d69c02ae143270f05aa844860f7f52889fa0f306fc2580f545dfbba9e229fa0fbf9aad089292cccd29b974d85051bfaeac61e8e17f7c20549d61343b1109757ee996e4ad0951ac51241aed803cfdced22f28667faeead3c59469168233766f02b4b5aba3f9b642eca59d1b18dd9d9f1c3ed901e5d26c6bf044c379a0e7ad3eb23eda18aa38f30d078b3d5260cf49b9b6b14ff19b702d180ebb90ab73e32e12897c8d229acf47f266c9c7fff03389813eefd7b5feec0bdbda25e13ea72f38f87876054150796319f683616a364abb4fa2bb17c4fb507cbedf81784c13ec5310c1ef8ba8d3963eac7891efb906f857fc63ceb555eee457b855639c39f2df54377d44879eef0287c1e936670dba20a25f6063a38c6fa392d0b285e278799e4abdcd4b725452f12d71073778903d8f11f33c0ec5613a57dad1c4f4f9bfec3a4f5e700dd3325bd7a49fca389f3d74fe7867bf33a0369f767124af7bed6ee1061e12cdc7e82b9a74a5c8de3f65584c1c65137e68873c72790757331db91cd80681c043b62acfff3192f13cf9c0c9bfae65c8200089f7b8392c086a3cacd6d8ed2c22af82af33203eda35a08193604d10adc0f8abec788c61d8b7f5f9ec67e46a5c7e49dabca1b774314c799e3b5baeeddda2769229fb45fc3bcd5f51b3d246909cc10c1e0a67e2e8844d61c17333ae1b4094a228fef8d973186669d1597de0c5d58035109d2a9cb8042d1175884412912d63c1b9bc98626664cae7a85eaa154d48b4c96ea73dab87dea77d093d989a5a5841a564a2af06cf1728ed5df3ca6eaa902203193ff26326eaa959218527cdf15d7a0ffea920e7f21edc14063195de3538f4522e05d86c57c6460fb91adf2cf348e3408563fe53f0a53a6325be4e93530c867aa4dc79f435fa9f5c789562a958c96ba53c073487eb0283bc715a9638a263e633ee91f98e7141d5b4d08823ee535227855b0be0c99addbe8879f2892fa3e56e50bed0cc4f7a1c87c6fb00dcf93ff631e37104fd5c26f9927c9f7b799d8c21858463f1fc83b19a5f255c23ad6c407e2551236aa715e8883bf099d700bff9cc7de02d4d242efa5af92813c7ea38b2732378ebcbd2544e807eb52dceaa2d62430b68fe883708b0b4f36dcc2337995d89d7521110c036f8d7c20a0451da2687513168b80aa81c4152b6d688515cdb27b4b7c1f06661d1a408e5f77a5ade2d17f9a46cd7b5ad60d6239b7e548dea8cacbb26ee76ed289b067cfb22ef74a15ebd7aa0d1f3b05dc8e33c22d698c401048ed32edfe29828ea6ddba06dda113ba434fe8873e062579eebc22792df8703053c1821108d3fb20a4d057dc1419c6845402c27c9993777ad62d60ee0250403af1e77e6d2065a9858985694b70c3f1af00b09182bdba7c41ff0ba54bec584aa41d5cfa9c0bdf91bb6a7a5244bb31b05e68bb0582993637a2af5874f014959d29f1942b29fea84947ed01f5600dcb7b1b39cda9c616505240eac88abd2201ad4ad1507349b829ea969064d0aa09e8771729250c59b6c6a3b296628c7f0028a0937a23911e60c3c3373a37a5cdd8b157c1aa4c69c8b40996353f54283f8b86a8c8312007ea4b379daa0b7709ad25d30886e8150a934167dff3847e9824c82c03a8dbeef3e729ae8d9b500459070c8a79a6b33b9519a437ea7e15102caf5a3656c8e9563bc1bc4edce21c01010f28037ef9a62f9dca9dbcfbd8ba28ea644a59a8a56fa28dc2b7c1d5bfaa94de30e17c054ec1554785f093bd1e3587bb7cd0e20562a7a8fc3cdf291a2c50fbdc4ca35441d58e07cbb29ea2aff1e6c199f9594ab728895bdc73d4d0573d0104072bc06e071501c1f474003fbda0e395a6448b50ce9c9091b5ce863d1dec1a55cd793612e3902aaa5297d5e0ed06ccc6fd53889fd435068ab2e6bd336f3478275e3d0af10be3509a3a0d2a3d5380dd0e7a9c41308f064834165df1c1b8011995537fa7ce97644c3f5c5c310b26468dcb90bccfc2dcaeb7c0627458a8ce7dcdb1f7740e4c3e2f094067be444f114340ef346c0bc1f6c96afe5b4c34811c76d18794eb28b37c8885db80eb4379880029e6a80a73a42ca13359b434de86ff28e026d8fd81221bb851a192920c708387d412f5bb1569ee42f85a11ca7a7addd541d1b087d9380f57dce6638a0f7d1672fa2bec93a213f2d3488827b0cc24a1effe10e2622617cca83cfbcc5923a5fb12ef5b5cc446ecd05a47412471175fa546632247ecb1097b0c83575c61b7604ad18787f6ab771b65d9a0811628e05f7e03262e47f22c1b422ca00525250f9cf518dc85d94be56c33b430cb2dedf87246c33e28b60c993b84a9b47711b1895e8fbc16b40d331d9ba75bea327f6c35c39ac75fc47dabbd7ee14b7285a8637bfd47afd68a6c24426f35e02c835f79783882d130abc6b40ef6237b002a47c46867dfcbbf30ecd1e4309a236330f70d11e66af451a5d6312d3dcae9fb098e189222559c00a85e2876b6a77ab5a7df34ac5574d5b17ee693fcdb9523dd1d5208a4672ce419215d3bf2a2fc8fa38d64c7b0de239566c8fcd14235764eb696b4d50c45b394a01d052ebb4b9b6cf8b272a8cbf30632d436c32d0f1187171c7ea6ebf520644609f3b09e424a36afdf661ae818abc993d72be18afea381a1a468c1f470a8a344d697da9954a2bd543ffb9001b4c5bef1d307cb409c6719bfa4433dd2d362b3d3cbb21195d163ffbe0f4a324e3c9ea6f8c13e54bb78d0e774c22006b3e62d69e8eb387e7815acaf2015f010daa4c0146294e9029cd4202469ba88266220dd523bdf50f7f4a7d3919d9d8379a26591309672bad29cd21413a4d442f39d2b9d8018eb931837b2e43cc7adb53e480b5535c28a74341c0c2d918c49fb55873b673842c2298dde6f8b9575112925b0ccdb0f2b74bfb08ccd6656d299bb61e51d3c6eaa51781ebc2d0caf8669148133fd6a652862591e73e861476db04f802fe94394791572433f5d53828262819eb4966f7785cc12f8321b8c95988c17353c6bcc456dd9e77c4b499443bf05a905ace783810a60f5b30af2a150496f85ae26ba2f7da1e399c05780c1a8d0829dc3ef5bbaec472f5db5f6cbf205e35b19dc5b52c3b375b21444648a0aa38572f261e5c099b1166e0f4e232534babcb0588c0361b6a9e2ccc3a0b7e0440796b67df93549fdd4f99ea5a557de0de1d2c303e6dc524220a67265c18619a89cf229d8c176ab111cfb4affb555557c63ea39e0d5c15463dca3b73b374ee143c9857fb6c378673c2bf12e987e42ab396078f16841795475e7ff7ab84a4f1aad0d3c13cb463b4cf3c8529ef7a80dfc538398f9326a84bfca54fdc2880d527769c5f37c9b3317613f48939468f397456f5dd694726f221e544bd69e44f3ee2d1c58bac765af4cfb88bc235f31b48bac05ac981b62156e94d059df188654f264aec0a3fe654e08f046d08df523c0622142b871b08052e42180bfa837abf67078b9644fc955738c487e500a959e6f179a959a8c08b1c5eae3fe6800e616157695c04296da4f1f065279721e92d569fde2443f784b9c603553bd80cdb412d88c581113430336d369fea435b292737a4e04c9f0e10a7e44cae99b86f23f984102b5535c88900c66cfb557cc3d935e394394f78ab3f5a222be79e035e2f00cb09336288f27f0b349f73ccff526b0a299c1f49d0af9be3d97a9273330aa05bd943747bcf91501000d675ef0d7c340b471876a2e8e1422c89ea157128a7e4f6a439ddc9623973ef64471fd241dac44b5bf461089d1b08ebd028e73720e3a3455217ec4add21565a8b6607d09ac1786fad02e974b7075958792a9da266b6cc1e8dd568b997410a9eb79d2b508b8b6f9c1a70837eff04e900daadea3f78bd73768ec06628fad90a55b5d4c012fd5cba3b9e48be7f7db004478ed819bc76a79602a0d66202aa23d19188800a64a4c6d7a9a29b05d9d625eaa7e7d293e0e376ce3ba625e2a3d184b374c575a0d0342b2081f6fc44b699951695950844c324e065e2adc2a6bd481234eed94ef920ac5a4188c1251efab4a432e33913bcb4b096290ed8daabe617c7cea1ebeeb3d2f5598a6a351589f71b9bf7d8e4f5eaa74a07e2c97ff9ecca6cc8ce488cef9bbed1087f791d64cc16540211294e24c0019f2a34e7ba4a4891f347a6d216bcc67d5ae0f19407c3e52c13d8cd6282d3abf37d15a2ca2bfbde2a466b51ede19a17c6bd2e95797e2c93694b0f94b0cf015ed612512189cbebec76ed220b43bc3762bc2dad6b2d30b683c64078958a1f4c8ec881bc3f8a85e91834165310fe4c099e16e87682dfe80f3a67580fd6ca326b4aa23a519c63815ca5ab509923ec10eedd7736933e088b0d426d5d75bb4cb31ad6aa345f8f5bcf79c8537dd3cd6dd41199161e44a8f7e58bccb2f3f1407d8e8aaba16f8cb9ca788a85c8a2beb9b27ce534d859527f043b4f09bf77b0b2c45ba5aad4ed17e24a98c8c57f19f08bdadcdde950382faf08d448ef6e58873a5e03ce6c9613c946320c501bb4994c581d4a2bf231cccff2dca67d2241ba0471328d4edf966b72541fccc09c8c774b03e0e5553b907e257b3a19c56b8d2acaeae4eab2e502b774b9cc0de81b20d5f46cef5d07adb16ae3a1976c0d2c7521f57921cdf6182bbe84cacce045affb8927476afacb340e255b7a6ec0225dd62953ffd4c833b9ea12b0104b4999e54d17ae0fea37efba6386fd1b11338e2b1201508925434de3573a24c1771b846b10435266ae9c2704d81aaebc05fb84535a6e278699abc24e13922b955c52c3b49a252daaed2d2fca970ae5c0a2dff344f15588188d1a7104240dbf3e1911bcde618181f00a4fb3b57450452d3b5f4f6c087466f2db7e33f714887f014d6d6190a8fb6acb347104d132c971a9aa86e0a4130c8902b5ffe7b76529d4328c8b385c9ae54ae13530207eb30133b9fca05e748e1876ec7d8e332b8b048b6715751a8a75f885914573259ac90e6d1abbc4f4086b3139aef8a9008f5730c566f58a1f536072fad4a919598a8a48c12abcf5a3eac7eca751f2e4d84c458804be3364515a9e72bbbddbbc1fc59964d2c691876e61c2ae073c15472832927ab00f107e722f621db0f436b3b944f5b5ee940752b75c8debaefed89f2a8ff90fe2a647586b2b6475fdd66a8e7c997c58116c0c00651aa2ef301c23b6f845c93360382611594c38e4ed6feb79198c20d31c8fb58ab9b9a8a5885dd776647110e80ef01f8cc63e0af56e483831fbba468a92188e5f7bf988fa0385f29f0ee58c836b0409de16db6654e98971cc24c78229f8ece46e16e39003dcfc0f708b0bd7c0a818ecc837945d1e3d94951f764e84dded151154b8719d8c971f9106f487002d124a793356a96198eb6b32c03b4604a531f10bbedabbc6e3d7c9e71e89fd14315dc3b72c5b94e16bcd59349dbf12ab27cbe7208b4305554e2464918728082f9904ef8263e10092dfc2eee2071cd6766ad63dcd7b2052f81a9e75e9679828ac45b7622022f3108ab9f2e949f730701a4009a0ab1f1899bd78e880a94d238516f5419b3b037a0be592ae101c193228f61baea6996c9c967c6163137e052a7cf8fa9eadd04c912b33eff3df5b0fa089b8f02e6568d305c7a3ef2c7d9781228b63853cad4ccd52facbfa6137580b6801467491f20935a6c211229c1a2739eaceae49a6b342141f83465a6c9bc5c2a94ba3ec164fd69692a9c1eede9f54d872081c735bec8f6fa07034ef53568897eb04a590407163040022381d45b8d5e165afa573943a0baedcc4f1b0cec213142ed7195a20ff74abde5dba222238e79ca3e1cb87a85b286b8d43deb0bcc03506aacd0704b756f3ed3424e42d093de055dfba8fd07eda14073c10c16bdca6cd26d87cc24ba18d4a7ee3d46ba597a6999dce4f17467106b6a305801016ba60b6761b1edc22f9169ddd961a92864b979f4b26322c1953c34cf6d76d2ea6935df905875776207a462aa1d939b0b5e6c39547f7a3b210b54e96a9d42c13c7a49579211c7d622dc9d8d153983136e797c65f717d475471c560232c9d0d68f517bb941bd47c19a945a5212e071908bae6030c7d958234ed7db6bad3a910b6c5de5d28160faef55f6daa9e05346f19dd2733e0c2524c53b659156ca7e70f1979b44e10461649068d49de37acad24d09a5710ee9eb648a5adc8563f3594e40280857f1de1369d86de3883dd9bc60095c190aa6b0098d3aad8039792995cd58d9b6e001d2b85cb883c7b83ad14242cb1721a05cf5a8827f17fa8743179b1876b2e7e3b88a359e8180adeb05e45c6370915040993b0746bf78297a1ca045460440557382c27a05347c2828835c7c2dbc5d8e4092793109c2ba9b558bd163888f024dbbad9835ffaac0c4091e146424f99a0a1d2bac397464e55ab9d2ca8801704c5569b573888ed35bad27a29776989c558ce5699e4e0f39d50f53f5560e07fe4965aeef5334da1f6f695e586597b7f9a955b74e3307ab92561a8524737a1297d463a43fb8e0a1663b054c313900d40037d7789dfff0c40fe5854f3a927ba09d2060a27d5b4818e1c361d7fe69bc95d069889cb80289414b1565db155133fd7e871c1d8575622d46d79f7b09254778eee0f80a685e5af861afd4a3d0cc1881cec70e52e86e90734ba515d51d6123e72d8b5431ccf9a7d9c9503ed4fcd6613ee012ead7a78de0cbb83008b9536505d3b8032cf9bc2d6b86994dc70aebf3943b4365fcc0339ee6abf55eb081c150e18d58dd73a64c235fac9164dc5ffce8025439dd9c39259c80eb1d8ed67f89dc936b888fd64d3f089efedcbab7e663681b40dea308fd75e964e681b1ea7927317f1e2b5397de05632253583ce4e01f4c3c599740701c82eae080d668383ba74f777fc742ba0825a8c7dee01bc5c68e47230e0f42ecc4d1759cf1458f31de333be15177fd1f884143839fe1de30fbbf294f5963e1a1b021f2b2c76f4f50a27be138247cc2c7d47ab826fd7b3c269479ac64c84f0df345a88c73eae1d297b85574c0389a765c73b7da39c97cb39afed0c694009391e6c7f2b0b0894727a1b1d90d7b05ee5b8024a3a642d726a163a391fa02a25ed180659f79f4747bee448b4d08a42378eb2a7943a6ede66e148aba1b4130f9eeaf078aa318d95faf9a3d087f905c7aa3686b6371ad3c48a033d126b894fc5a60d0744afc553ae0c826e6fbb4172bab747bc56ca4d66fb55782b18a4e147f6fbccb71c21990d2d4997b46695bf948acf95bd6c831cf80c3e9ba774bbd89e8284ecec3e961739fc99c0ee2e112ebbf5dc43c023fdf9aab87a3aab3af85671db7870a4d72fdd081297673dccb0a1da99c97296874bfdb5b2abb073d4dceab39a3a7060e9a130aa9e3f70bd8c86fe6260fdc6be88b923553d94783475424b035135bb543fa330c610c21f0bbb747e8fcf3b346a622e7850f9719e0dfafeffffc6b9f1a12ff26d91b280cceaba8d28bb31cead50c83bc9589f13251cd87bdfa6a1db5cbe509ddf01d125835efcb65156220128a3caefe5292f1928ceba1c69aeddc846952adc62b940543a1113f281c24bb894e332b93a588e47a8d66b03c1a91d583e3da0393041c8a5038715f632894b34dec5f63b61a71997fd33bda08aa95b8d7672b52b930473f648d1576dd985a50f075c42d24cd39d9b0912532f4e01b885ee00e7a4e379754817e5a13d7643f29a2d58a0119797e3c7db39c474a8c2201e81d584fe7ea3702b4c64d1859ba1b0540abbd3dce58c020a2e419afdf8c7604ce27e5d6ace3786eee88ac3e834910cdcd23759f7fa57b98d20e1f1f961a1ac26a53e8035e25b7ee4466a186a415432ff46e244a1130bedd422c2a40d202631a5e81e33150e7d530bd1a6206019a746959aa444d51559aaae3a81cf566928d85ae22d1ee1942ae55cdd5a20105edff1f2c2935200b1c2e53952a992cd9d6c4133f9f7ee798d49029ca54c1f4c0546899189bcc4bcd0d74204d2c2639062efe142fcca34bfd2495f217a3ee2815dfcdaae17fa39d73166cd66dfc89be144fc2ab1c1ca9589011839d3cef2d744b43dadb493585ebb3ee160b7798197a30649388db0d2c6fc474024e52595297f72a4b76a9429e1964590634d442acff2683f5f54a9b83a0c81c8f861838e3088c7918e05ba027c61ec579e084b550c7a0c0f5de01268d7d7c2ebdeec9075370a1fe2629694fe7c0573a7c93b9b8fed6f34e2a20d8af1c22d378ecfd650aa2830bfe0f337bbc865e43632e4c43f4ae6c99b71053bcedf274bf900382818829b6ce86bbe1485a4b201b1adee2fb4d3e6f94960c385e3312b9cf4dc5e7f77e398d850ddabd76be2d0baa5c3baf608f165ec070ee6f29d438d3b359611f482eca35667771e420d8c104a3672b8f706b8e81a6a2822921ec9e16cabcd164fab451ce92bb5e6b0d6548b34697014ad8605dffc86dafb1d81bf392c1627f3714d8500ba6c7fea7e901a034be957fa061e31ddf76e4bc7c98e584bf0fb3e7d10be6f0c35efb938d0136085cbdbba8d6d25cc554b1a1c409983581c545006863620f3d7fe5fab54ba858613b067d14f307fddc89f12f86b7a71f4748063d151431e6d71782a8200121e4237c0de11b884855f943fa2a58673c874370ab5120156b63575796afccbc9204708bd5e3d1602bef771605ec788d24607872aa9826e281db13dd19a4e3616578c19e38cafd137f8ae45596d411010d238a610e616a322c2c00c5bbaa769be6b2a8fa00cfcce171117034480a413e851ef3aaf3d99b35ef6b7e7e28085d7d844f9ce016bb2a8c4baedc8d92007a6cc0821603a50fa7aa558300b8d6345e6e24f37c449da52ed8dcf9d78d0aee143f4b00ca959ae095f8ed8cc304704601377464125078893488f4c94bbcdeaac3b81ff77dc921653c05e7810ad05aa595934eda2affcd442733b4be76bcf5171883109ccc51d11a8d354b1c1384380aa88062595cbff8970b884de2cd8600fe18b33780b4a0c54f466ae441d97675326a47b8764dda6b0b66712a144118d5531bf6f89dec49f54145ecc428da6dcc0d0e295564fd3fabaf65dc8e3e6fd30174b240e84a28746ef070539e1344a66f9759a34b10d53d0f0a7780fe9f372bf0637966baeb6e0996353914e01dd3391ddff56a48ee2c323ff439c64c5482cc5eb367fa811f7373311a64ae5b3fe2085b8466aee195dbcbe1fd95e8d386e6537a1ddecc537b47ff4b65f274cce73aa67e24c5a0c4eae83ac06795ae3bfc6c393e7d1403330c0d48ddb6ba58157665375e37110f3160924e50f28e0520b078506456628ee1bc1c5417f88394312e53d165ff54c3555cc454f56b72b9e43657322ffeb7608b8832db5db0c8832523b1c67aade6cbb43c150e0bed5ca6b003c113bd723346572559c07fa5475dc2295d3dcf01a914588731f9c1dacffd8f5e267acd7758d6f6d59f51dfdb9635075ad64eef8e4369fff1c35362643e0fbcfd98a0e3d508598d6b9d2c1e23e958a2016993607c8db6e3a4098ce5aa50e5600eb7339630c370540de1255378a87903bb4d5287610db0725d822d3c64b495c8af369eb68b1b248a65a11d0dde54ef4bcdcc05b4a3f1a2a9cbf49bbdeba85e66bfbd93b5b4657065f9f6dd8cacae22b55de95220cc6d494e5db4f9422139ccc61d9639508764c0f1da40e84337cbe0174e19230a0f0a89ba0c2d739fae070c651db144bd7a3696a2db67244b7c70ccae73173664c8e6d32de60ddfca48d0b2e4dcaaaa00d2f58af2a98fa33b3c8a8f59e256af880d78a0e5302eec2756c29e2b7ef49e562ba3ff8a936475b5cf88138313be800b5038e8b957ea40d30f0c93d011fb3f51eb0f4128ac2c6f9e063407d69e159d462e1c26e814f8232add8ed28f2134cfde9826382638e41bb2bd15fb3413bd0e9f5e877d4dec0b38fafe2ccc0a878633cde468bb964869d32ff2e5e1444c6f195d123ce642273dd453685dcd38a928c10d38846853fa799639a40d8a3039da5899ad8cbb040d5d3e7a367e62c4873ad4eb015645a3298111403515f044a78c5236d70983d70efb0465c4e00d4d0368e926b2f61cce10a200fdc1e401b1509a103152312ef9a815afe21a931f9c8d3455c957a6ad6f629f152e49c68c7c923b66a692e7ed39f87887cc3a7a474c31f4c38af0346988d01fef73568848bcefa4267bdd3e2571e1a85ab47c7a28ccaf1840ef3cea8898331cd24538dd68ff604a427f540752a548c0fff5ea6647165d748e72388b78db6f91f4155c5551527cc700a0ea52269b2acec74a53c499318427dc898afbbaaeb21a4d972089536f6da650caed00cd2d93658c7a9e5e141c5243d88bd781e5afa982f3f0b6bfc0f68175f89c31875e7970d43511d3b034188d1461228609e23b50763b87f7bbca5079cc5e9bb60b44f5d85186a8909e02427dba609e296fb01d2934879cde4d27fea1d4141b1598d3388775a4643f8a50909b0026b875911e5311cb19eafdf141c05a7e6a6a302ddbfc7c2ab8a40c3d3818a519b2fa69f266d76b97e737480bcdc66ab73d5061197275e14c350b9c59e3cc0b62c0f323759b16443ac5f6f59002807e2df36e13eb21b985369670792a9c508d4d3fe4f7ecc5e1ee4cc4d301fa02b54911e99fa7f9f5a926af563940fee5034b3bb5f0c119aa3921b56be25c1d0ec83390a570150319ccc1b00623574723adefba7609ed0a2f01244fc7c489395c67033a447a4bf816378724df91d42f2a62346add80a2c263729f2f5b043275825eab4869e0925b778d71246b431e32b45ab40bcc0e3925b7bdba2abe339e146875298ea83d21d76feb034dbe2f63e880d5cad0581be4925b170dae1f732376466409ce88acf36e3b5113d609f2ad31be99ddfc23607a45b9eae65b69a7f806409fb4fc42228153c3bdaa351aadfc2c9e4f145858a05e61e2d3024e2062a1dc0181e9b7ae7e433202cef4276d75c4d798cf04b1a793678df3622b690b0bd45bcb60bda22caeae9614abf736b5b9a7455127e1e219a403eca741af523ee67a7a19285002035094693d06d5dfe3409b4588740675e084c13aff151fbc9ccebf455b97b0b52e65621d22523763c7fd8591c862412084c06930cff13b230742f683c69e7434ac856c58fa9f0f1645f74cce9e1760ef0202f69d89d39535cfec4b21bdda931f8ffe807f99d0fe2f4b23b3002f635af480f1ac664778c5e1d91f1b281fa74b871a5fe426cd401bf658e85e2ab4be24c8939c220a796170933116407fe5279c67edf4bf13edeba41ff64fb717afa51284ae716f69844afb75448e5bc586fd40b52aa75e8d351f60d87968c71089c2976903fc47d32301cfc416fd359ded58f64311e1b16764d876719146adc200228d8f36093bf4ce74d66e686ab712de9fa6584017981cf16480104bd4e50d5980f405f4bc47c946a282056e7795ce46c8a4909777845270d98865588dfc84855440d0a7c0c126c807a15c0c2c7155d27311d21f45d2496bd0f9bac785c90d3debb5388c804b37f3c54a0b426bec337163c5442124f9ad38fb408cd1e953b022be3052c5fae0f227c504a43c378d4fcbbe05210f94ba717981f61b7f547d7d0a342eec40476abdd66778238bebd1038af2c6d68e3c33cee4b1628962b5f13f9c6d1e1992c3c78887c56474dca7e241c787203f0b375d200edf9656e79e86d2e1eee20b6715bdd95f459c4922dd071d4f0d477c6a30a629a272c9b805db9170f3d5dcf94e1346d1ca6f1a449f0484dfb7009ff46836c622e67e5cc41d0ec619932141a977aabd5ea20745caa7dbb9a1e20a55f9bdfd0613b66e0873d9ac2b4984065f300bd7e27ce7a5371f50ca8f8acc9a0f307b4e576573a37e2936f6013eec4bc343502c125db29d142f25264879c88c0106d682c2e45a554fad3b12ab5bfa285221ea1c6f33a4e2825c3ae9707fdb309db964ae68067dafd468a81e9aaaf633fe0d8d06415bcb566674ac6d6dad59130b4900ed510089c95204a3480be51ddbbc286de7339e3be2e9e64d517324d8530639c5686b8c06c92946523885f6bdc732c56dd018971460c0da33708a501601484bc1b0acf6205b0e93d66b08fe207fe98379956bb0bde029b313302ac7f73190ff87363c0b793aac57357648a2d40d93b36117c47e2f8de5e9ee4a5d6c875ab2208c74eefe280308ca66f18acb2b38e29592a5ecd5308db1c58568063f27d19e0124f805880f6c558362b27b39072ae28d62d8ad7d7102964b0cbf2ab1c4de234b3c2a1358db6f6885eb9d8ca2cd8b13fe2ec989602adcd7f0d64010c2b067839be21b03ba57bfd754f88e675a7c641ef2b8a2fd6c97b9c43683ce82fac49e915a22c8c6befe91def503248f5301111aeaf64b9c33f14d58230060edf888113b48598a83ca51d0d1544787c5f41c63417868d1bde72e62aa0bbf6c12d3e13359bd89097a8d92b5c490804e7bfb226bbc5a0f907321ef430eca93fd56c0e1fb99e0cd72be82a639e90ce4fa02298bb99a811f759f5624e5a891988b14899a39a42f968e64dad025b42252dda81d51aae4665574c6ed30f33083f2d30738e486528d4610ccd583c614485f51c7cf3e264804329c87ab497d31fff8c08ad9976f501382f7a3b08fc8c6b7220e4fc84e8fb10eea11807f400d6226182cc516b873b20a91848f242b9b40e9dd7110f1f738876b0bd7ee85e751af82d13490562fe1746e35337b680f820d60c90873d5ecbec393dfd53477cc1e8ae04ff4c44bececfccdf94c823955acd3553bcb0dafb8c1ca8f23ee77f3f841c8e6e3d2b907efee4b70443ff912b317210bfd824b29ad4c447e1751d6e2a1892f010d3a14c939d68d90f7e270d3c3931a52702ef01a500ca9047d26383a69e1f29884540a90970846b15031217762f87150e4d983f7c5cdf9c95c32f8cc283799305596c2aade9bfe958835e79bca506a538c86ebb7436dd5d55dedf208ce456d0e000cdd3c9b06927607d1693eb14f1f8871e17f9bccf926588b920e64662a580d613454872c9a338ee7ad83bc5dae85fb8389f3a7ec982084fb7c4d7290705db20254c9e18924e2fe0e6cbf7c6eee183308021e0eea18f09109fd0e00f029eaada757ec03dd93a198a7c18f0c58a48926696ddee3dcd5af39911c9636529d87d179f9e1a0bfbd9539c22bd1628ff78d3d1745ee121e87fdb1e808d9983b4cd8aaf02358c5ca143ad7a6eed6c80e26f544025648ffb20d8426de3b89d0fd9d7e8e388ff912f1718e00809a0ded0b553b803ff091c1c4f26ae3b0f88f67fc3d46f18662ea38f4cff7b70d4a23b0ae198e3b64cd07a33ad55030ce82e97b9f13eab26d50bb79a646b0bcb950adc0856cf6ea8102c2036368608205a5b37ab42141778dad7bba2eff293ee7d04a9928d42a522cc1580367f60cbea560a8381a8c3893ff10c795850248b4c7ed417dc0f20c6b1a14f8de7fff197a66b161d1359b16b0824e973048a1512e5881fb8a5f80a7c41542895c603ebb23956ea652ffb1e468f4fd4ddad58744b9fde965f9d5b06ae96ab19f6167847e2e0e579bbe87fbd1dfd6c7b773871eb5e3da731eb6fc9f8f3b07e71d02e866799b7571ffa96d8231f659cb4d051fecf38d7c2b75a3c80e1094a5dd1a6b0698a793c0739af2407ce59bfdd0e648df58a696970c5c8ae98ea0000e274921fb58227d6f416085569bb91b16200fef07ebbe5bbf5408db8a70adf0f9e73cdaef46aad5af31c181fa448daa3eb0b67f0a2a725fa73422510037e41d5d4ef4444d6cb1622d4adaec65fb83521b88e743dac8283d31269dd4db018403b049ad50342be5aec080697598ab386b1f00394a590fd78fcc21ae922e6ac6c258cc8e1d8378d469599f8adb1a45e678ac2c36172ba531538d7da52013a539538bb5c596d2d44a1be556d12832ad746966897874296e7225566b48d904f26c58d56640291d7d4ed5a62d12d6292d4c920025b1a66e5196ba4304aeda7b30a0e57ace963375f978b64d589cf95facec0fc97c6dfb2285cd0c7c349ac96dbc3cba93c36ca00ef486458431a5d330c2c718ee42c4b415882af666ee938f10988d00f148d01fc688f01f4bfaceec6c020cdb8c87066e7f9112377d0cd48692277e24570be28a89a7a245875249838dd76c506ddcafc77032e7260ef8baa4cef392bb9f5e13dd3980099c1ab5e93aa007ddd7a80220b0febaa6691aef116f34b47f92f5f5ec7277f661e43a9c04d643d1a3caf59df355f25c6d8b865b9a29e10b842fcf858ff908bf8ca23f6cd2883b35ed8c73a2e1f321740a1699636c1d543bf793e0c7ebf0b249a5f488efe8a6dd82882187e7852ec142ff380c9ff9014b9cf560ca9f958a9fb5d39b9346b6cb3aa75076391c9fea249071d04cbca4e260bbbafc2ff79707dcb845a65e64478ec592912b48250ad179f072778ae318d82d4737a8e51e6fd132542909e92404b44e6772824e8f17f12181ceaf6d4ede4a608bfc2c231d10f661bb5cdf8a1d59cb3c0e4f10873c5490feeae709e8da47c0dc990c8698056c3b1690c70c403f7290ba7fdc619ade70ec0c67076f4ec26cac26ba69c8443d4cc6c72434662d5c415b1b0dfe4ab4d6eb29f6b3551cb4c7a9cb92f4eb9acd410cec0563cfa7ea30fd70d3f7e50772584a9b15c9fd78b4a7e53e473d62e903631607bfd8efd60026f5688965ea863b968359b6d4a34039276125673ccef6524103cbc850caa00e1fa3065cddcaf1726addf7e39c648facd483819dac8ea47e6daafaf462935c69d2033e6a13bff4ea8304f9c5b8c7a84a7d2d789fa0710b2296b3d07f386bd46cbd4e2decb5ea7717695cd48a55eb22f82769f96aa9a840655beffab5e5ad06dca26a613f40f3fb29fa0ea666bdc335cb7427cbfabc71bc8495c56256d21400665195fc56e63272259ae65cdcb11cb2ac26eca579b73f4b9b172f8fb2b3c8e0fed0f699e88d7b88d9bced31b5709c1de8fe6dfe4382106b894bc20d8a1f8ae9414026573e60c4c1c910ee291d611a2adb9be36d38185c0f7ab7f2eb95a92de70d2777d895df67fc02678b0f8ac58f616c7abed0c4afef46770d2d75978a8ccafca56c3d4671d92ac63a83ab845997b63b7c872026e9397e6f59398deb7e4ab816e9d6df9f0b6039ee3ba894320e8edce1b1496a731fa2ebcbcfd1afbcb1033212758a39f405e8af2651fa77b674ff992ba164ec5e7c2c702931fb5515abb0ac1b4259f99f7076dba01d25dc0a1441626c78e227aa4cc6790a0f0e464d6cbb2752d5029ccaac69885ccd2370a517ab4715af57de3abf0ee225698ba9064548c65b937ae798b6cdeb2ef4f49f0ae7cffb01c9d42fad9c46f9e288bf7c8c2b5a436d77537aac7783900f33b5eecefe6c512fd48c37ca977cb5c4286ebcc0b534f9f7c283eb305bf481df6f0f57856835067d9bbbe787332783b4d55be5e18599bafadd48ef6c362b0bfc34020918880bb21fe99572f06b3a293d608ea05fd736f1b6d07c874fc1ac18969459fca06ae2bbb5e462507f7f5c6c0c065d4a3e3a9630548e8cffcf8ac25c85d2d282324724290b74f9fc1ea4121235e611e3bcf60c4b320b5f8ac9d8a36bb24acaad56723acbd5bf00a42f800f24a73520bc5b3951d0be910bf11d18dc9ccf3c65fc955f99780e395f2f205c52a83de0f09bb892f327019d4d8589e5abccc6d0db8cd24fe1f0488bf18212b952f7211729bd131a87c610bd840a764ac05b1b3cfa464dda0e786bc7a5e588f74a9b86faddcd68bd8735b32b5a00cdbbf8112d355bd456696c1c85d6222b17ca58c7992760787be0af5de4f1233eeb6e5389c0803e7327156f60cda8d8ee2492b35bc5971a91b5b26b672d8039ed4406785972a8d5857981e537d40fe23226ba7206db13e426cc41cf4e35c8fc64cdae05607a6448009137f17a41a550f2e4dcff28375bb91182ff944b313fc0d7f29db53a0cf841c82109411d1b71474795d0ebd5ad7f28b8429b1efb137521e6159b489589a01dea988e2c895c7202f58fe307daa09a702e43350a1a9c01f9e318abde69f426e5272228495be7da83b9d25a45d9c195d5d0c86c1932155f2ff0036a3256059f2142e64087852fb3781ab69ba395203da12c5e3eb2a3cff697b1643f90d7f1050e138ee7f4dcc384fd4b1e1f42af514c428c3fd97b830e348d80dce0987ec4745a1bfcb1d53a791b80e416232c497e77c98a0494c2512a91afba6029d6d3cb5a901138115ba1c2cb1a5911f74c97368a47bf989f0f0501485fda589c76660cebe61c558aaa1a742a4539a47fcafbc2e3603d9bb68c8229c141d368d67b79358721bbc7ce011f0a094debabe3c7efeb6a9ea081c021784a4874823154d390876d78ded7c6368fe52e0b066fceb83da54e3993eda3df1757a17f23b43a0bb04ab533b0ad81f1a88acf7154dc8d6f7a56c7003b27cc1c32842008dd516b37841e6ef719feed7834c55fb513e2452af5320075fc35f064e7003144e1b3e0ae9d19c6716c2ed54ef770f3264c1cad1ba237daee33d27d077cc7cc28e91ee1143ea02b06b59ae29a884a6355d4de34ff239b0969c180937e7467dbc3767285064bc715a559acef04e5e6a70c59aa2acf3b6876b534fa2fb1e539207e797f2d8eb3fcfbbbf657da41ca11fdbf4dba3f00393fc3c184c76252ef0d3506432dfc70ae8539088ff98c9a5113d1633eba06a29fa3fbfd365120d450e49efc069ce8d4063f4293a20718a9ee4c9b49a3075fb01677d585e2e76e0dc62476a7e1bf965b6410d634f5cfa626f251f07051aadf472934b79e53452a6daff67b9b9fed9f1fc4f8216cafcec13070437c151ab7c37494c63f8308b96e8976256b3e98a046b8d2e1ad637f74135a29b377f2f202caef85c4ce0dc76717dd3a1ce727fbd29a54d3652dac10be44c3985df8efc2b18fdd17d30aab84642c28aece0799d24e2ff00377841753f45ac6f22934f7ae19893a0ad9d37aa80a45a8ee9d6f29b93f9a38974136bd9ca6dac2a3e9560c3445e953d8d404f4b8997ab953d3b53bd334b9214fe1c83b93f90dbf80e1ec9334220ba297c38931650aa9f87534e9001568aa379815f36ffffa31df45d3e500664a4e3421601bca9eb698296bd95d329dad918e181e68421eafd63b6b5dbbf4fb65554495c745e86628817d1514e0e949730249aec7b54dbc96a6182fa54ccab9969277aec66fe606cdc34b78cf1b3103976ca77c1ce57f98be9814c7645537df8098d2f5681936919c555ec0c767ab1482472e1e80e0a06ae8e600958625dcc028d5241375bfaa808a4eab16dcb212f3cf994a971fefa16b2a4780c6576906c64538711a710030b3740d85ce2f679cecc652cac4546db72d0c32d11bc3ade5f519ddbb01b1fd220c7f26ba7f53fdcbeb334aa3f601bc75ba7d565b670269629a0516fa1215761f5a0f2b9b11997b230a94489a911fff2332f8c5689a876e24a8bc0bc19a7d4d6daad04b1b74c3782d31c7fd155b5683ea37ee5abc007032190d626adfd1377c0135fa58b74d3268dea4340fb5790f8f04f6fc97762044b049c9a28f23622e482cb19650e1f7b5af645941623e0caa09c3451850af213fda1c4c0eaab0d9f5b55bba345f1dd16691c18ea564336c6165b316bdf0c0aee0f2a2bddb043cd8807640195fb91b4d6daa16363a04b3e9dfb3113b0344f52220b3816b1d6b28e545c8fa461bd5b3528f361a25e08d0ea1f945626cbc0dfe7c11688ac1a5cfa46613c3355ccda643c9f4c103b030344467b34600ef64ebfec870b16e61341664bc5a5dc567d19bc885c35df925c6092123be236de696f79268e1195511022501a21a6a12f022064d9358cd72a5e4262f0ed99988551ca2321184220d3a43f03720bbb8c73c8eab0acb9897ce0652cfdcc38337e1264ff8685a501ff4a9485abcad723af5a81a0fa0e95d6071d50d51d055daf433583df61513f9c2e399d53194d601e8866654c5561e6f109b69c94d72a0da6f090ee53ac00274bcde1edaa5b1428433db059265729115425bf9c56a4c223dbdd27e8467d9e3e35d02455ca51ee9f8e5391d3509a221fc814bce2f0bde2aa9020b491430b77fcef47a8fae8dae92f9885da01d496a3a6aeaa830210c3f61d9fcb2a343d851a57b7b95ec258d92bbbd4a9680c64a1244aba1ee561941cc3ec895ee8e326cc15d3c3d98a34fbe2374266f2091dde17db4f4f6ca2e7625441138c5b8373f3f5cda4bedb17410daf835d28419e9f0057eceae14ff4bc97cc42c58898c19c96e1cba836169ca114ee41fe41c4545cf83fee8dda225b04eb45fa7f68cbe21021c2b7debaf679c416a142df1d12471360db069d88a528c6691834602ef342cade1b054451e9652b9869afe26d0e83226f0378507a991b9a0a63e4b48da84933155b19c1239ee3413e68125b136ce12a01dcc626d5d4fc3d0c6aa1c423d8f2e7971f501d4eb189a7a25f370ea2eb9210fc5d097d7e88f83203a5e7b32a021dbb36cfbe3a1cd69154e6773c4c59cf7a163ce9b28a76d3124eae3d5a8d060da2d8a6b5692181fa76bd2a7704170ecf447e17e7b7064921de0a1327ce63e38c0b47d0a67d7f8f3a346669ef6900e79b8a0e05c3693915693fa4ad67741a96ab9185728840ccb447fcc0a2aa02588c419b85ef1087022efa2d3c9abfadc970e653b23531ba7baf2146d96a0c57c3f705d2987e2e015e2fde291e17b0e953c32e04ab84b85fa3be589e77d8b16e78d1215d8b927a51c2555caa5c4e0986052181b59715f14394ab996218c5620288b6339031f1568682cb2e68683092ce124e5a131c7bed06e99c9e84ef34ca87da5496a22c4b604b6448041cd5721f75b0301b6aa26bf912f60ebc2cc077b59fac0c187c611ad9eeebcc529340a21d6f70cc1656f16540e0068f7bd564991fa7291e5bb7085a2b12149c25367bc0e71867be1d3cea22e2651bd35501f10050dea81f7d490c6a42f45919b84a32fcb6eddbae834a70dea2b06896aecf812e0df163d505a327e73832d6e138eb70e0de88aaff542846f132668dc878ea27d3baa860908883af4435892da467f7812e8a690e7d11a094e96f8c84c8f66c8282bad18c17a610e2aa6e73bb39f26fb76024f56d58cba5c09342664825f653405a2de257c0a775cecd05cdfb6d270e12c50fc781aa76e0a8a83cef18bff34111aede5ff155e1a69298e935e15d43e4768dd5599ddd909d2de61a1be60040f00bea18dfe6b0f7984aa92aaefb55e90aee036d71d4a50b611b0041c55ac805e77dfcf3e10a028a4b58c5b280e62839dcdf1069aa81b7143a42cdccf66b46f0a013d4f393b5a744c4669f7b2c0e3054100b0bb0ee7b01381192c60194db1b3f594261fcbcebb485707726ec12e391754ae2b9da486b6da60afb5d9fb34e32af6a8e8e6d930fb8a47efc456537ff9fd163bc32ee0a4c64f7e4c3015287c1570b9ecf2d17631ae44e07a3d0172625cb2bd8f4e1135dbd6831a562f755fd251eb5260d49aa158b175fca07cc19cc671a8d58d9c40809638e7f662fbaf38832ffcae4f0ffee62988c76435b820daeff3f3a60714e95ea04ca5415c435fd5c86bad21027a46fb67a7e160bd3da10b24bb9def223b62577c57c037afdfe5c02e9a18bcd8fe9f8330d53dd58b962b615ebde5f359b3880856b82597ca311dd6364f947840eecbba9ec0333b3828c04510f89470678199207316eeb74c2d4c86e2cde91e4ec5ff1960b25f2b830f4efe81e743ba25c2169a8e66eae8ca8484e06a3c69ec67e6d836373048c4995868483f39ba2ea311f3bd3477115a7541b372390c8b59f1ded4e78feb5622bfb1eb4c44adbb40ca3a985830ebb001d5a00bb1647cd639464cdb228e73cab5bc61874b99d758ced895eda19d8bf9e95c15a9580ae57239cd4ce3cd4d694dc8bad6b7beab126b3f4123fe3465cba0be979f4ca55cccf5d7d7c9cecea2022264b00e5ddb613a16b7ef10eb87fa58da18eb152376ff3412f952136ace0f524160d05dab881c2ad4fb092c82828aec5a7ecbde5a150fd8468e7f9caa726b086cd339635aefb3a4e2bd71b949e7ffb04df477be201d1b2160eb606f1f17cbcdb4406f8bb37f0fdb3d109d54da98753f8202b113c51db7b77c66d527b397da4fade4926721e34bd609f4cfee6a09c8b2da2d1e6dcf9964179c9b1b415e2634a817ef59c55db439705a2dd2a0a203cd9c5490eb6c9b0b6c90563c681827d8937549ba374bb0db2d6ea030c96e8f38f65bbf702f0b2dd0a430722ed8f7e7f74e26b26a3c5b0a06866740ddc3c95c8e16555d0dff51037127e8d6eb7eec2a20ce709cf366f4e79aa7f897d4f97ea5013e51fb1f66c53b0295cb08a44f7e4ee115ef0df9b8a9fa3257e8617edcb270137a10ce5c34f3e2338733f65810cbc2e8c6f038b1b44ccd588f8e46a99c552c3c070d7696fb11497097e2c9460e937a727fe0ced8083296e332bd8ebd35203e083f765b24a759d35ce0f6943910859a770daf21ee2ab41a9bcea5fe8035a47fe9fb756c513d98d5fbc9b6d2bc32be4cb361cc5e82dd8f87ddaecc505ad831564c2de31d37bbc060cba80741cd3217945f596e4b38ca517656316ad5dd237f0dc3e42b00a24cc986ed0564b5a3101abf4eb00cad77aeae11a6e3d99ce43653cf8989f6a0f2714bec9bbcf4e67bbb33ee6259c76d14d409ce6414b0d3473e818a79b2cc5e8f7f815e73f7983d8725a9db633940b73fa99aa5aa3b30d75c4ae7d30d9a5a5c6601930713f7ad8ed88dd74947dc8afeb1c25e91068aec5f1e19ec78082def51c38f5e2c38c943c809fea071559266cce384581148993d909c66072cf3d033a7033e8a6e42d3f36b0cbf8648396d3ef9f2c4e8877b9dde88af45174a517da687a0f052fe092cb6dcba6fb774302908088fdc804fb16b05c54f285078bad653cdaf23fdc75bbad4ae283f3e8eae366dbd6b7183236aaf1f3347dad3ab262bfda3ead201989f7b57feb442fbb869efbec9a55a560046ae586f2a97444082671525d1ba066f735da5b9b63d0ea360fe7faf9316af87920fb5f70323dae78537f854f4ad1fcaa03081d0c4d1b44eedbee6e1652cb8053a2d4ca0f5635595502e2a46b136038429b8c5c2aa9977443e4870e03bad4d2d5d630a85ab55a3a88bb268d6b7432e11b146c7a5f96a7dd728cd5f89d14633dcbf07ec9204eaa8cbbe572056aa9a0ef1b3b2742fb0fa03f603a4aee5adca5f20e386fd68cd55b17abe11f18fa08560425bae68cebea85efdcfb3e52bb025fd04a9dadd9c29efe7bbdf78a5727b6c442a32534b1ddbd6d496ecb6b6f79632252903e80ba50ba40bac06cc473d50ab70109163a0f9281c34457194b68a59f56766cd40bffe0f88593fdeb380bef7a6e82920f43b658314bfffe8fda694d3597c70356cdb62b1dc4848a222963fee3f2a2c0e22d851de28117f71468e4c40f674c012993e1c43513df714903021d94a543d376330c85665fc90f188e08ff28cccfd77ba53fcee1cbab329035ff52324f47dea8550cff22ad52b1141107cfaa00a07b1e069f317f7a80f4a6125e287badfa5207d90fbeffb1ef59482283c8738ee9bb253873d3c655ee4adf1c907c24bcafdf376c0cb3e746d4c2196fbc743d9537382795f29a5b41be39882fd1e7c0e85598e59eef5533888c8ecfca12e4b7e2a892583240af5f2513888486220d4a7503888a280fc84f36590f98795dc7102a5bffc355940d8bb2c20d49f4ef886e6b174e5de59a66e6891c33afe92af4aeaae449f747db11c7a779ae2e95a9d2facccac1fa1fef92d796616fdfef414037941a8f75e892874fa0f4b17f21ee5ddd3a58f7a25220f8b62960da7f71ee8e4e120228a81bc4779d8fa8bfba06f829c689dd816b9837ebfa84c7225d068b4293722f44e0f7329177989e3d6c4b4a4947464f361c6e4d8f39bca2073ca7628fd0ab068ced90d468a0aa5b4d6a62b559aac745dd7791e1ab52a683099b70a1a4679d5e144238bbc9ab70a1a43c8ab9e5e0b9a867af04d010d9d1eb4e84329bf062177a40791e75ccaf648da5cc8f2795a00c19427258fd4cb39e79c9c0c603efd696c61657754c8f2bdda837baf07eae5f7791fee8cc2630b4086fd9cd7df63fae8e91f7247ffb8935b82c153b993d761bf3d64389f4b43f638ca25cbc791db479d11969b4a39f13893472bdddd5d762c6350f6e126fbcb00e6eaf2345a1d323b82b9ed977d986d54ec4310994c81d498dc31bd096708b98009317c6085c8082dacbbbb53ea5426512ae7eabaa420e7d456895a81f528df184c4a605049b24d9162548be28436d4640a4dd9ed8a95a6a531ba503aa2e2e4eb3ea20dccecf22451b293d2bdff1e7c4ec88c4d190b45b0e38c4d59cc1d96b2f63f0c27b6a1d502eb39ac13e54b4f01f150a81490796a54ab05f6f45ffbec54ea7d1ae57f721781ed4e4c6121fef2e731023bdae730a59c13e44b8b3ffd938fc35e87a77778747a74d83c1da2f0e8cfe2461ea9c362c141e0a75e89f881d2e67938b03ccba5f22543fa635046bdc4216d4900124e16bf6469490012a2742833776d872b5b126d54d71c7c9c33a196fa5622d2eb5e71507dfa41fdfe4ac4cf41bfac795913b36cf0ef1f42bf622418091251a63680fa1ae67b414440feaa77fce32feeb90fb31a50df1f087c1a44f4e1a0987f12fa4874de5f1cebc33f00d507ff0629aefcc53d888394884a4415264afafaaa1f755e8a2aec538435c37bcfa261be27566c63442c1ab2e7bd878b78ffe18c840888beea91b0bec748286675cffa5498d5e1131e5239141e42bf9fc8c3720901dd7b78034ab217440444dfddfd1d2761797feea5bfdc41ee8fe49312960d14a83efd1810a5943ec549589e7ee59e62d92f8c412087640ef9c4f28525a9499b347f71dcbf10ab3496f7f56df030507d6701d197a27798c34566d1894895a3089eb52b1ecf3be7b0876587eedf94045de6dcdd391f478ea3dc7b1dd2aee3bee3be4827a9e7755d877bec5ed2ee69f7947b493b0584cbf3698df2499774e952ba5c1dba6060fb471b9a1d6d7bf7c83e856d9d9e8de4dd537af744e5cfc13e79f9ab20953be47760e5f2d7815c3d819ee70f059e3e1005821feac422a9120d29a5f4a314b79c5d37e7a4a0e7469f748d769416e6854e9f4e29a5547e9613bbcb59b914cc3961f29cb5dd51681031e79c52da6491ecb4b6bbd396d6d199754533b650297be8083ee91a53d46335362876f4d85196f43da9e6a3c76c465ee4d9d5d7d86c79ec9a3cc6c5fc45ff34cbf444cbf454cbf46bbc3c7e37141032fdd1f3787a41a63f764d3fdfcd5f4fecc8c56e993e17a3b645b32347cbf4a9c768a72c73401bcaa3c792523432be3997d8cfd21b57597665d0ac2021d3ef9875fab30d2b60e439a717df51763cd6ecd45b4e1a0aec286dd30476a443cee4477ee4477e94fb670aecd8b159023b97965869ab62050879bef4ec2ad48181ed38aa845a2bc5deec6ab2446a4112f2ea8ba349619e28796459e5c624bb6459850968d8b98507f64e07682e32bd6cc45ff36f44987e947ca80d48acb061821898808818088914ebf82b05a47b927552905190c7f0491e794e9047796482dc95208f92899647792b628333aa88918510a2388138491000368a88410c84c858c2429cff0e53628411b880c20a95279ac4f93a0eeb418d1c58a1c610cc407a82e8042a7ad0c5121334390115e2fc1f8749329a78c2065c1c618829ec409c4fc461048862850645008293245811e71b719812d9154a4235228258029338bd69133e155ab044138804ca14faa0f62e2fa69022828b0244820825513441d1148232ccb0c2065e283141954220d4595cf19674162c682a05c9892cb809cc94208115319011c500c90918e4962c919c78b2c6f785c005267840b931840763dfc24061888e895d59186bb358630913bb9ac91289b67402a3ec0418ec8e1d3c9a60e5cb8e0a1624640a4e45e8808956c4fc25a6b0c9126989a2fc6589b4040ab6d08101820d6c10cc80414bcc7a9655cc78419319443142d8ceb28a192c9842984102ceca14ac34a18a130e898984094e2c109ad189c40f28480399d4220a149c617482275a5c912236454ec0cce70aad0a3676e0848b32446e3291b993109a9854a86ca004c735814ae6baccfda0886e04527cdd0890684e48339086c0c44509aa18a20ad1756eb24422811a48244042ed62e49e8a8a44adb5561a861494133630c2892c3c1a46955b12e428a75431e3097f1d30cb2ab72cb22acb2a372f3822609840f07e06d41e440181365c4006164458e1822a3a2773befd2c53538a4f8e3a2218537c411b01911511884090374cc1b96e2b462a8861a568c5030bcbf25c5d0df34ccb00c27c11e0f0cbcdecef9282937368d8c4c2abfc204af65af61f7fc8a3ec5de864afb5de196d6ab57175e816c666c35fa5f1b5883342cbf48cf186a13ef4622b75a9554ad488a2b9b7789c759fee2a0481e594fa2c0c6ad1f70c04794461d1660fa594274b592bed6696f573f77a07e55e4f3351f0549d949c7bb89a415377af776453fa02da18e1f9cb613cd877eb90bf64ee2ef39cbb3be5a60c78430a29675618c26ca795ebbc13ea03532c2adbe2f202132333b3a2a961d9c8ccc8a87023e6c60dcccdbffce5a0b864f9dc0924ac26712ca84933f36254d2ac491c0de5058a2653936270cbcad96a499b2a4b99c5f460643c5ae0c2619939ccc747066726079524032325894a9233871ec91e3a933d2e05460b30721ff9f8f8501c21286d920849f74b9a12c6d1381a6744a3525229a9943ae4c10218194773588c4ac96152f4acabc2977ccf7be538e558c1934d72c7cc19332b742d843a3a01087978421ddccdf1af1c619863070bd57b28e7b0ee24ffe423e4a680fcf840c1ce6f99b13432ab554ccb9ef974ca53ca8799bda11d3c74e4d14a4a2136994fa70590725610c1c7932d0000f4c2ab49f5071206b354875087381a0eb27c6a83598259ca5dc5aa524154c82bc8d0c8fd234793a14d53c8c81e090017409df0f5d7769241c2544adc19b9a5125644b05e644f6ee12a25152a49a574230a3b7b662513c3cdbcec91304db479ccbe530f3c8f761ccdd569763e7deaee3c50e0ee1e95d246b1f3c1c9e3899def7528dd4fa31214119cbabbd3155849e70bdc9d52eaee945277a7947e2c2e30333537626456352dd4cae5e5e5592adce0d8504a9dd65a5b75add6b4e8ba4ed6994a5da80b6e79ee920242a54b9f76e1d49a29086887e339eed4678d6d515bb6703929b9b83c0ef7aa91776b51adddaaad2db496962b2787bfb0a854392056c3be56705a650d3fc1ceaf2d95a668ea05d2861d713005c48f38acd6662cc70a94aa202baeb5140d4f29a5b516e446767c52a2c29e50b2e8a3cce44f5082524e4f0556fe689bf8a46b6c69da7d580d6b5d4029cb5f4d8b0bcc0d944d4cc73975ca751c751b99191a970e3077837de240d98332d0e6a895ec8d1aea5cd7c2754ebde36edc7837f2b99f9d57abf3439dbcaefa043947a2422eb2626473cc02d69b69cbb03e373b0e578f06d52d6addc2da5fc38ead44b30567a90b0e81b25a55a92026f86e1ec7595c58f5011ddafcbc2d36d6ab1a1df64c923d5df48e634ea8c3e664398fe31fe7c6dfd8bc0af5e55df2c45c0c498958964fc1af7504ea4e59589ec7206e052a368f5c14991a5899e78c5c1373461df68f5c11d76d45c5c432466a099c3e5d7eceffdf781b30f2f731416676fdbc302f5ddc930edb276571c5648febfbb9aa9483c715cee3d1dee0d1e354c03678ecfcc2bd74f91717aeb58b1dd8512e4de1847aa7251634a5dc6ff3fd944aefcc0fbf9b26b35afde8e5d5cbb4301eedc5e36ae6c73efa990222e38468a6584e86b95887fd29336c4ff9f01ed1bc47f337f068696853ba6db0ec108f358f625d996baecc2395d1d51d5d7986ca643f50983bba5eee0f93e362b9dfe5fe305b92925c79ac49ce543beaf78b831a0899ec99474e448719e548d34eca33099446e4b6c2284bc95c520ad480e91db7604ee390c2b338e30ba9f1f9de61125b8b1b9452be66f7dc73a0a738e7e26b296f10767e83f1497715b8953bfaadec91f3069f9cd327ed39c715a51e766125974610754adb29472bad5cad7808ce29a5b48a2a3cb9e3e4814cb9231ec8e43450d94e57dd3dd751a74eb90e01d4adf4ab9dc909767aad778ab329cfdb0d527429e67881339de40ef9aa270f7872e9eb814e0f5884789280228fd22693a592a4c2832aa93cea38e20ba45ca1209106931a247144ae3050a4de4cf623a464973b5c12b4dcd4cfa048473c5141c18e56a747c3266de61184f94196a38e5c7be4fae38f5c59c82ac83508b9be47441bb9be5d45912b5211644824590d8c5022d7e71123d7f7a94846a49154eb1a4db9d65a6bad2d7891e97bb7214a90e9775656272c753252106283abb59ed184aa3c90327d0a849332ae54b1a40512142b70c20f52b5d65a2b151a41a631c8242d84600291104738c208b1aa20d733cac8b5e65a6bad398460471d1d28b83c1b988293ec0929304d3193c22c06579a908aa00121891878c06404264b3168c184240413c088a107638049b4ec80074ae8c2da6459850769bc44c1be6459a5074b7455b8a0659a65953180e04ad467438d366cb57a66418aa55d75926308169c43b3c99c4d5aad774ea793f7420c50ffa777ba5cff88cc507da0de915b3431c9f54aae5e5791962891845c3f86de7130726a062e8912e4893df9198809c3599ba2817b9b0e576734b93a94471d7e23ecf8b9b2c2aca3c3faed841ffca83c1dd637c28e3cb928096e297b77677f1ac54af6a74a6334ec469323c5c6c86eb3bb2d76052c420a6296470ac648a764ffa61dcb9ffba0bf435920437fc91d5aebd05f9e8a44f961eb724354c82e539f9c144e4a2d940203ebf22317c50573459c94919341f6cf61043bea14392c7cb7ae1f57383f7ece8fe18f3a21c681c39cdbe334ad5c98a37135ae8833923db6de9148414821fb73537ae7c5614a44179f5ecf491d7a171d5e156c767243d347cbfb73495c17fd5c1313e6329f957b393d9697d33e4debfdbbc8612eae8b7ee186260fae89f99a346ff1e836c8aec2238745f6ef58f0c8cd20fba73c3c722fc8fe2087476e05d9ffab78e44c40f1c84991fd4f5c920a52d891362773d573df511a569bfce55c1a438ca184a529b4d6b4e6d19aa7c2f706760429cd2cd7ebb0654c798ef396615d9b4579ce245833cd39abf9ffe68f86798f3c75d834cc8ffce53f5b38effaf9abde51fdc478f4ee2dd37f712e068f56068fab0f1f8f3a3778e4c9ce84b1cb8e1c4665fef25fbdce0d976557018f9e0d1eed51d0903b93124fd2a87314cae0f1e9c7e0715564b9219937c44471aeeaba2ead39ec878d7cd157438752b15e8f4c7558972cfbb76279faa061e619b8a5ef192b1d764fe9ee12a4d4bdfbd1e3569472553a953d2567c5275d172757079c3a7c7dca79ba79391e8dad5c666005cc1317c09061b92cabb4c1022f63d50612b9cbb24a1b429d46d10d2d82f388095be1d4e471caa6cb7c5465c1c1fc71d56ea443ff1d3a6cad6177e82e918b4387926987a321ff59e4b0ef57df14692acf5976d83442be441e972e53b9c353a6238605fffba09e14d8a288c544107b33651421264a9168761e69ff47c1bef33f26297aaafff3c0914eda2c5052dfa921534ce1a0242288a7ec4e5b91bf7ca89643874e24a5043b4ea9e4596f167d5b8422e3932e2c869d7406fc31a67a3f7259d61610f3e01eb9a3ff43e56fa67ab46151dfedb444addcab9cf4214169650fb8f37d67d4ed809751bf1a808421000332033d6840f238ad010539c3514621f70b9153c8331c2711479e0a82f2062064be3a4806e48efeefe4729e4ea73e9d288a85be4ff32077340b363b73066e0f9347ab7113324a36727fcb3672efe0030b43eafb7320f2de9db9937962eaeb8522b3628a6228b21753d89bd9d1cb766826552a79a4b9e7f79dbf39c1965c0a08573393a67475383bd3940c61763c931a4af760129cd63b4f7aa762ec4ffad5b30b999d4addb22c9b054a37648a1d0e4a22cee94fbce6b40edbf138bda896215bec993029a70f8a82950f240f72bf9c120b1e37c2d6efdea75563a86e4d1d766b0d3baa9a72ab6eaa2667ea37d23bfd401cc912e675dfef237b288fdcb25b7e919b0b2dba67e6b0910e1b081fb983059be5b26c31f308f3171de6f1974a0d27aceaa66a6259d264631e934f3e4bce2cb14aee98df796620aa3c5fbeec9107f86cdee6a394521e404941eb29f8a46ba4dd6afae87058237b26ce217754d4444954f7a7793a9d7c708ec6a60f8e2ef942eef4ada520c3fa304f8b9452fd5405b2cab0fea8c33b7d7ddaa47766bde384d27ae749ef784f8b60f0a8a357351566c134208d4d500c3b8eb4c8003e1dd6f72b2486fba3796c9061e8857dbba16e1cc5dd9f5cc99572c89ef9f543577221704727afe35027af7bd4c9eb38d4c9eb38d4c9eb38d4e7ad2a618e0a02a95fa77458dfbbe24ed9cdf7a4e77d373937923deda37bd9b7e9a3c3b468f2a85fbfa3207b26a62f903bead7ff3850ce3f15853d5b38b0f406f3e2d2dd644216eef4a3471386322f1f58fade4731302f2edd51d782c75fa9f0682d883f8e16d14e0713cbd3332f65c115f75fc76ab3acfa99af4f552077e81732ac5f67302df257fdefbcd3a38a74e491c66a3c8fc6dad56a3543bf62aa2477d4262c7d4ff25a7dcfdad57f65323232323232329a320f654f2befbbfa1c0efeaaef79ab955452caf514851d69514cc9c87694a4e4032696a5d7728dc6640e5322d6a7430ea3a2b4d5a7b4a735d93373a54b2ebdc21a272c860fcef3ac5dadfe431d1f4e5bfc51fd7c2e15a465b34eeb34baa9d1f05ad8caddd44dddd44d2f2eece8198da7d8cc3e3d931c760209f364b2a704d28792c3589ee2d1230af1e8d55a3496377b9179d2546f241b86991d32dac1b32baefee7f9e880976b7d69247b529e67ed8aca78704e31ebddd0e20e5fd86f8a1d3d996784849d62fe92c99ef9ab12c853cc8be2146322d3ff62a75867cfb376b5c2c9e3c4c9238f27f3177d4f265bdd161b0cc3ceeb357fd14f1565fa9ecc613bd01cd669c817fdd5ca5a4fd652f084b0485027e88f530799fe97b2a14515769abf287dee361a9207fdd60dac7f9f5e49b6621d489c52364851f532f503be4faf6cf6a9c7386c9dd80f04b98bcaf57e4bd8b19bbaa97746c9a4aad23699243ed15e9c4c165017c4ba418a5e8759f3adbfa618d462f7b39b7fa518bb32f4e4c67ad7e4e27584ddd3c791c9a3878e136c2d1604351c84a1480a53fa0acf8f5a1e67139e187c6c35b4ac490806981fd43bd2b674458dd93f404939e0d0dd6f8d269bc85d297e484936242a61e40f4c1ef3fb06a1f9c8e4317fd6666d4619234f9a3c7f2a09606842818a6c4ea14b5060238f33291585136459a9cfd8d06dd63ba38c429ecf22a24333e99d0e4a935a631e4027362700096accfc81e9c3318d14fb79be910ee71f993e68536d8bc6952b576a000a0186ab97747bfe373aba8774c997fc76eab32758d1c117052bfd5fa7804c1957df3d074f32ccec79728794a1ab184a477192ec180a8765dfce65ac21250b7068a98694832679a38229d3645985093639012b9164d0946db24492c111f953b17e3e25be42cd07fa1e07115116fd9f1fd48dfa5e89f83aa8079fc3a365f951a78a524c3d37bbfff117f7c30d521482837c1e95d49805f4c5c0ff7e62a21e4496e3ab9dbb7ae1ae7adc950b97060097a6854ba37369785c1a162ecd8e4ba3e3d2ac70698e2ecdebd2285d1a1c9726e7d2e05c1ad7ad89f9abff6f4deb7b295d007f65847ca11f5f21225e1422c28a52b630522ba40b2385411e636864205fe89734376ebdb716f9abffe656a32aabb93792582d911b06d48a3ce2d90f13c715388e6ebc4cc9a042cc8e36339b273587a9f0364f3a6c9bda4ac97a1e6d6e5c29d2cc2214010a34a2ccd270c2d6e0a0f98b16c1ca6e32ebf0bed7dcbfb176e6261d768cacc366d2613780f534ffa51a60f337300be8c67c420a2488d15c1610cdd77c0d0e92413843a47925e247c384f5fc34c90525ace7b11e359ee1a0d1e41cdd50c1083bc6c8b0cc61377fe3476fb4313187dd603cb3ab235879f2bf52bc71a5582fcca5b952b4b93194886b319438622a6c970a426bae1469ae14698eece359ee161ea064a681a6e8620a4f701c31dbbba5aca5cb889c3099ec40643e8e22664e67944e2793368fba984fe6d094328da6add2dea9484449e4fa3af48e5c82e319ebb01ef1577ddc8495484d5cd19202322d0e26769c33224b188a95484dd032a76261826d422805e6b0c2327d28700ddbbd4462824ae65efe89e34e2f330abc6008381a0642045340a008621c575896b872054965225fcb0b9bbdb0dccfecfd111dee0e1d562234652ce9e0423cc54a24268a32e74d2772c97b22bdc342bdcca7399b349a654149bd14c11fe7d5e9e1fd389d304f08ecc8e3e3affae015e2affa3432fbb9660f03d59fa4da6589e484a55a1f07c9c35ccacd71f3a58a4f051e589ffd2de50dc2938f844fba3c1c52d8511eadc0882558e4495d4b3c161c52582135485800e40c72870a41064f7690a7b4fd90302138dc0191fcd93ee02ac7d59713d82e4b242666797c1db848b1354b24269c50a24a29c7d95b7c2de7e468e6169e5d652ae58e96fec4e7b3a5d3e9a392ec1d31a94f7f222f69138124e78dc04b29695359a7a4799e770513249aaa3882e94876c42c2909264c61306152431a5a021148ccfc253d940d74565d7395e32a8c1ad625cb2a48576092b650c2e5b6c514b22d8866c040d1f2046b38fb746aa951c8d7ac331e4cb033093bd43bf5bbce7e00c2cbb30e1109e5117491de0e78d96f8b75e63039c6e4b1cebe539e31b96d268a469b605b5bd82977b060b34ea71fe9d0833af42347e6dde11ae990498adee9bd3924f36982ec538a268cb0de4f267348270c2b8f7ccaba6790b0d085c81e3a80ec63cb78caa33c62c166a93b0b94ef3b35a4c5ef51771ecd29d3d6e190dce19f0243874d26e4cbdf07904bc0f3e129f31735c08f900e5996588f05df0c93f93fdcdddd79dce93b1edd3df497f4bcd5aabb714b7fb5926ebf7d3c3f7ca44ecb0fab0cb99fb3625a41d0e5a04cbf1bcb577d548e5cff7f04c0016c9e9d6b12d5af5a90617d8186ecaf7365fd16103046e055d2615e651169e3aad0e12aad95b634d95396484355bc0c61bf2c91866a5a7479d471e29086a21862c2042dcfff78dcec0766a3a08923f27c3997e4c9c3937d78faee60a443c924a5fe047ffe1187713f6bf303f1e779286b4fab95f7dff1641e87799eb5ab556834a54c91de398bb899397c64faf074389f6589953ff2d4ae4a1023ca9cab2558886003e248ef8c3af2fc69d4e1acf96b823de7120f3af43b66f5cfdc78d65ef36ba08099b5595212d16187de1972d2301b1e6a57f2945dd43bb336a5e4d9ffd323cfafb7c7d5e970b60e1dce1da6d831cc93e52f73c3ac96a66c72c9a5ff0e05df0cc2ce972d492d989244e464b504cb12ac0e111d7e6a1df69cf98b07a72c9196402553b03d296c0e1346206a7d182a66c9773c9df4abbf060a4bb19cac391da8cb2d6d96729809e5f69913f93ebd23918a64b93f86dee9e1ea577f436171c02c5360b8167cf23bb42a6995cc24ddf3f6e958816d4979d2a54b141e95784e054901691f927483cfeadc81ce6350eec2702faef08a00c294ae880e69092294b84289f8a4cb660973b5f37d3108f109c2ce9f5eab4f6125d62177b8661f8196abc702c5ea903d33cf20c741f4413c83ec3b8883589ece5bccadbfeadb771ce48f047c8ac22a1cc48291a4debf533dcbd777dbf22c3fa4e559548fc4beea87a8dee55d3012969f16acf32ae92f1f9d1e6cd0af5f7f84a86c8a7449128a9118f11726e22fb94463acce31ab1be24f3112222096a78fc4bebf86d0f7778c8405c9a4f9abbe7fea413c87fc555f8ae38ca570d82fcc077f0988299e2fc4ac14fd476808fae00fe19fa20f3e907fea7d9ac96129ccea5ef54d0e0331abfbd44bb1b26056f72c2fc5caeaf08f7df0a5d8a4c3cae4b3c95ff5596ec768b6b03dc33dd4617d9620ec387b6809fcd14b4d5bea7e32f57ddfddd3a3504afa4fb748f73e7e5157a66ae8efb8a67d4bdc1d03d1d9ddddbdead4653fe1654f90f668f87e82299a5345be57f1c0a6f84a5685e791bf5a6ecffcd53fd97bbfb389d290079d7aadcca60e5b825c7dfad1ffc0d3f5282cee2e923aec9e32da5a6458966ff9ee19f5ea79b999624c4fbce63427eea49ba4e84c3e2b4db8993af41f9ba9873a9475485dc84614c4f2acefdbc86116b33e0ce5e5a568bf9fca3aac42fcf5e3af6e91220bcbdb1fd2f22a8c04232102527dcb23092202b2af52c5543889cbabde6220966fc141442a8c24b4cf823d7f893431db36dc46ab36accf2bfd427b2ee36a090be609632a234dc588a83e0f3ab985b2145b2896376958eea5147b268af3e6d883e95713d963237730cd261c37b0e3bc19cd9bec9141661bb963b6cab0631b11fd2083cc3d86dcd14f03fdb6b5cddfebbadb543ae49a8a47d15432876ba0184686d6a1432264d8969f4794eb3a1504fc540ddfa3fe43a540b05335744f67911477e7d70f36537f1b7553d923b327c53c3b67decc5b68ce79f39a6a0dae6c1aecd6129a699442b21e6aafaf20c3b60db75187a9302c87dbc8ae96b0bd6373184b101150cbbf4dd55031eba78d6c344256aaeeed88bf7a08244432c8ecaf219010391ea1b4b4b47c0b4e8264d65a5ec6922099b26f1925d312173c8dfc155361d98269bf30dfbeea47795ba2c273e62feb2fc7de6cc96711d8363f58ca1cc7f1b8591c18c91e96ece9ccadbeb05c646e6a91b991875e3153033b6d144fa31af73a45f626f3705fc510aa3c31842198d210b947dd9fd3fde89f7ec8f7278c840889f71f1e82e4f4282c45f7fef43edd61960da8ff1e08f51f0e22ea3e48ba0784faef3f91be8e1756149a18bf4c0e6c7d29e2d0a8388a89fd53a943eea3d058b1b3884ba9c08e3a3a0eb381e37e091dabeb8c7a6766eee7154c999b6a348cb534a78cb30ba5cccda5cc4d2b99ebe4acf58eec0194a5ccfd8cf2d371dca7626064973c243e1d3a1dca23a1d3a37e1c76fa7e210e43b5bc12f1047e0b0e6a791023493dea835cfec3488880528f7a242c7f7a2522121824dfb7e0088285f2f2484022a0ef5b5e88c35e30cb9f882912b1bc077a79f0bb9f22cb1f09cc7fdf1eb601fc970702ffe5fd91103906fa1ee69180fff248883a0c0483bf47f282756416c42eff61960daa7751bd0b0bc87ecbdb6f79caf22d37a574755c206aa644f45930cbfff4e0a7c06739063a3dc80242fd8f2b2110f3501ec29612fc25797440255d7347f8d0cfe775783ccf5623febab349bfeacb211dd85132e5c8ecc9f3c2fb52ac91fa3bf4ce4824d35fadac752cbdfa54ca71400031d261fd1b3eb023cf9c1cce40189962479ea1f6e1f9019322153068b33b29b24723b90810347bdff2bf2bc4f7e0682417f1613665206686f2814fcaeffb79fd8b65eadffbf4d712871dd607e2e14aa50e6a69c751a9434aeff6a043071e75c8eeee80f2093b28bd7b1e74d8de819977c54308963ea177b4c9f527ad093072e81db0e688880e4cf5757a38c9f57fb4011a36c5fa524789a23c8a15268f1614e39bad23d8feafdb9b5229dd55a094e4795426eb29bd473d520e98e4f930405645a024914922d138b3cb73e11522fcd77f453ae019ca07c27fdd202562587f421cfebc145fe92f10c4b287dc8777082522bdb3681675d82efd3ddf3f074e722bcb2a3c9892270f47ee182bf4c09e7cce29353ff22093327d943b2dcb241f64cdd71e64521e9514e522353f47254579e2c63535ef1d75d854024853dc72bf8d6b67be3f8142b69325d213b6ec42cb54bea44b6374481f7cb94134df3fd3b86dfd02fd84709648505cc9df7f94c72d0809eb21f9a24f3dd38622d36f27246c2ef98b3ea5f4712c81cd247f7d3b6097c424d3efa9d43b63fdd3d351498c39bbe890a63ea5ba2f0461bd9729ee02d1347866cd504f782a29c99f0c80fd50e0cb0da2799987b932d93e8512f33efdd337d9a323ab5439f52925f9a29f7a29c1e6543695ed599ab0b0f8cc69517c144521cb2ed2776961515263a9995a8dde615930c6e89dee894cbfadf44ed702fbd9b0ac5699ba8ab0375922cda86499fa11d209c3f6c3bc8e133bf62dd3d5ed5b3d85f9197fa12b4c9bb07498dba83cb692a525c8148a0c96bd18e329ef5373a943ead5facdc486a52fb344826229d3a6dd6e6d73286c9952b15e1972a1be79131e655899aac15ba9773a04481ce40efc6e14991518621ef56d8bb1d23b5792d6580ac0d21335f7a5c9d421d5e94194c77ef283a70667ea9d3a46a7807ce083ef799e973d7cfaaa045ed59faeea53aa996a52e990aa700fb1dc6e2202bbf33d95b948f7a7a71c1e93644fa66800bdf7e91df08b00d17972b7480dc2fda9bbe32cd27da521e57dd2384fefd3f344435287347c5fbfe2a9d4b1e81394b210a4d91559223d71bb9125d26c8a8cf2d8185d64511b838d2ed868425a820ad8a329044c63df6a6da559138d4aad77c699948b8c6090bd18d34af49d64fa72ec279946c9a34d96634bc9f457365a83734fbddea9315788c87b225eeca7b94930112ae676937e39a1f5930ee967c1ea3dab7634ef48a410c4328dc9544ac374245293a64c7fa849a6454df2e8b44c5f5639838a3c7a944ceb779cc792b49b5097d9d40cf4e5b68ac0854dfc5a5a5240bcef547d96140d3deb268d69d667bdd3b7bed24234168bc568227d1fea9d8e0599be37913d3353cffb540a88e779b2dee48b7ead3908828decf54d878fc84ca178a87887162964237207fd97cbc30b2c62344aa04e7822e8c8137694b721feac212fdfff8283e60870202699469021be7ce3a0191c3491a02226e981428cf924d208e2cbcbd490fe2056fd242f2f45d690d5cffc0a07cd22ae10418498a4b5d0028933c4979fc14149a614b0183a81f88289b897c1418ec5172b281293482a2898c10fc4184ce47595f6e976779b3563ac19a42d732f6d99c353a843da432b1b3563089949f72eab1982229571835c9b7873e8e6a443ba8652aef8c9a47548270bf80253dfa72795dea998356b1d46f9dc0426744ab9d9711665da4f5340fd30578a2f578a2e341d50ac2dcfff1a9c9ed7e0c4416776dfcf86d09cee75e6fd2840c274a40df35d90b01f1fbd435b06f91d86f27d7fff5559e7cf7781fa359fe606b6e671d66c7098b3e633d97363d2eeb8a5378285c91269891a799cb37abd3c67b26766f92350879349e69eabdd7f9d9c3fd09973b4ebfc027538b9d994cd3267b32124cc07f99ab227083003216133169bb33a82747acff29d7778f4feafc743bd3878995e201d3a9c32c887f2fd8fc33eaca393672a48e71892605fc72c0c6d315034b560cb2d25cb7f1f703fc9587a87e370119bef7310c4f47e4749b0239ee110819472e766f6b19c23d993fac7a9eff7fb9b59079145d031b247758f3b2577a4f09f5477ac4174a80f34651eea5011ff99a1dc789bcb02aa799bb7c1413eaabcc076630676b4999dfebb712b957ef54bf174bd2c53275c8f686af0833ce21a07c471ff87a3096b63836f6a374572c6cc723fbe211b65a50965a543555387aa25d518df04ddc6608a6ea2e4be29ca3db39283306af3088d9bd5dbef6666756f68374e6e6635b11a19985fe65be69663de490d93cdcc867663646363f39382dc9c6194306e393803470dcf648f2ad7dc09c50ba4c024831005d7cdec5744b03730c652e8d7c8644feadb0919762fe54ecdd05833a331923dfe2cbf92c91eef6d661fcc8f354cf5c82626e684eafb6b8654df2f3fd6782d3fd6cc5cdebb3833b9a3fd6f6237b36c632f31b9c3314c91dc814a4229a196504c1765a5c33e3add544baa2587a96caa25956a49a50243f6ccdcbf52b2a81f5536148e1b237f51d9d80fd3a505cf626435883cc6c83c39852e38909b75d83635342b50616d9e7438de70e01a91a906d87c0d6601d57409b07872136b5e8978e36fe0a06902216eb09a59bffabb9a59ee77d23b9e7d30a2811e32c0439111033b5cc00215d08102139000910840e0013938a0010cc0610143140094801b6cf841c0016a30000d059841880c4180c44000b1957d060083007ef8e809000f6ce7851e2e00a0051d1e2cecc03757c70a395ee18c56c351e4b01fbe1f87119ea9967ca896248f7eefbf9ceb7a9cfb1fde9cc771713ec70dff7571bc8e9be357b8afbf7785bfb93ade2f4bfee10addb0fc77736f88df046f2e287e0ddedc94f839a8ba5676edcd65116dac439578e3e486866f663733235768032f8491582c161339f03e5c205e03d787cfc0d5c0f7708d7c919b81e7e1f6f018b8457ebc3cfc052e067e873b7e05ee05de027787a7c0adc0eb702df012b814f8095c1d3e0257024fe44ee01f7023f010b844de01f7019fc385c033e03ae01b7073f8055c063c0eb701af80bb801f7271f8045c053cd01df236dc04fc8debfa1b2ed023e0daf03ff786afe122e00f707f9e865bc31be01ee067b8347c01ae015e863bc30bb905782057860f72853c012e90ffe1fec770837ceb12e0c51bc3fbdcd6e72b3e0cd7e70770f3ffb830bc00ee00bee7fe781f5700cf737b3e00d7c7ef5c9e87dd007c8fbbf32f5cd803e0f67817ee0baf7301f02d5c179e85abf33c6e0b8f2f0bbfe3f2f87bf1dfdc1def17cc35563a8462299ef5ce7833cb5dd3746b989ab0239e8d354cf9867653bb2902e2da0d5c5beb179a7633bb99dd38c13387f9f0fd98866b0ed3c0f7e32287f5f0fdd8c86119f8e6e1bbc8f7f88d81efc74b0edbe1fb3193c32ef0fdb8c96116f87e7c735805bebf1573980edfdf92398c02dfdf1a72d804bebf35739804bebf45731891ef6fd51c1681ef6f15390c02dfdf3272d803bebf6573580edfdf3a729803bebf95d4526a2db5985a4dad9b2be692b9865c3317cd557315b98c5c36d7912bc9a5e45a7231b99a5c379c188e0c67c861e2f7e3cc1cd6c24242cc3644a2d413e189adefc7a1e1d4708a708c706c383849384a384b384c384d38b79c584ece50ce2c879653cb29ca3172180bdf9f63cb397218c69e88bf3f27c9613abe3f47c9612b7c7fce92c3727c7f0e93c35edf9fd3947373188eefc7117358cef7e390390ce7fb710c39ccf5fd38660efb6fbff1eefe3a3ceef4b2e8dff8df7825e287c1b03446724703d5fc0d0e9253848074136bde2f10cddfc041928c9a48e3d9ff4b5d283fbc0f17cf300dd7701136c2367c8493b0125ec24cb809df5ab196ac35d49ab568ad5aaba865d4b239a095d4526a2db5985a4dad5b4c3634a3d58a8c6c47494aae251793abc975c389e1c87086fc8543c3a9e114e118e1d8708e7092709470967098709a706e39b11c59ce50ce2c879653cb29ca31cab1e524e528e52ce530e534e5dc70c470c8700ce1d34be902ea73b4902ff4ff1572c00b912426feb7aecc137fc038b3a37a64e4777cb3c0e35be7fb6b93c35af8fe7a731800de85eff1fddc90c35ef87e6ee6b09d877df37c3f57e4b0007c3f67e4b09eefe76c0ef3f1fddc91c37e384c00dfcf29390c861fc0b7cff7734d0ecbdfcfdd1c2612e0fb3b99c362f8fe6ec86140bebf9b392cc8f7773487c9f0fd5dcd6142bebf2b72d80cdfdf1939ac00dfdfd91c46c3f777470e33c0f777490eabe1fb3b25871de0fbbb258721e0fb3b2687fd7c7fd7e4301bbebfbb39ec864fc037d0f77b430e53c0f77b33870df97e8fe6b0057cbf5773180edfef15398c01dfef1939ac01dfefd91c868323c389b96eae2617936bc9a5e44a721db96c2e235791abe6a2b966ae2197cc156bdd5a4d2da6d6524ba995d43af257cbd6326a15b55ab4d6ac35d492b562f8869b305ec24a38091f611b36c24518d3f04ce680bb92c9167cb831466e8c066eccac871b43cbc08da9f170638a8adc18a3f1c6d83070638e76b8314917b8314a16b8314b15b8314c3adc98260adc98db04ae4c4c02574646e4ca0c45e0cacc207065680fb832b51cae4c9103ae8c5103ae0c03ae0c0e5766015766c89559ba324c324d09b832b71bee4ccc863b23fbb9334308b833b303dc195a0d77a666803b5344c39d312ac09d99e1ce08b93332dc99207706c89d89e1ce10e0ce887795baab7c57433e77351bc05dd160b8ab9a00eeaae8c75d19f9b8ab9ebb0ac05df1dc15ec06a71ea8146db486510d115224020000000001a314002028140c084542a1482c9e69c29e7b14800a86aa50705496cab22448621432c81062080080001918111899a16d00e3a27020421000ebea78bc1894cc21a6cf3eebbe01aab0b2992d6fd09561a3f1de133a9935ce6c1ec054b6f795f941d6912d8da016431dfc2632dd19b0390c5e72c34e59f295ce1a16365d3a2a65269901867639924c2bec378637b15042c64a7daf713a0361f46120408c45a4566660afd4756dcf84c862cfb784cecf71d350b20913ec9c3e83390a2f976bec8351fd06451920747715480249ab61db31b5f264806cdf0aa5014b767ace1c103a7dcab2d993423b6fd5deb7a2ff79cd187cf343240ce18f3068683090e1c93041d44661e616d9c371256dd95ad71db513baf5e4db7fa1d07d774b11e2b80e1ddf2babbb7213edeb1034d98e8c9c7d6dc6a3cf2879c85ae3cabd3cd289eaa3a5380bc40406cf0b05da10c0508d81051b2255ada0f11f302026791bc44fc8e9e19e4887ff73044a844a3dae4fe7696a7c41a186c019f1eddd057056eda8bd165d7c65afe644a0a2f3f15dbafdfb2c2c508e9e60f2941180c7a869e654cec4633073d4dfff2c4b0036d48ef2163d50978e10588c5aac7e603b28d7fd544495c28a29a36b08e01026bc7e139521d1bf7d8fab668bf0a4a4425fa4206c4b4f071ad8c1d2d804e33ef15230bceeb5086cab314f049511cbee070674ee96df525cd9dab1303b203beb01ba979d36ed400f48c0c35bff9c53966ed8f57ed2375342f6895a23537533d1ae179820f685632f206baf399b7010c8344be448f21c72d739d885e1f777602406e41acbf4003d431e48b1204e02849ec8f7dc3f5297606d68e6a7bd72930611ba14385d6c72f25e486504c2f52aa00704c1b5eba4020208f2976468b5577624cc85d35792c5a4a6daa0b8800630f8bb2c5d6b948e230f09f1619b826314763fceae9205c220e682cafaffa9f1b2da4829d469b670121c47c5f97d8bc1e02cfe948123576c5f13a61d3522b8c91b4f4a3b3c1d2d386a7d40a39cc56fdbae79eeefe9beb08c27ac78b51482572d643782cdd4dc11f6cd3cb11264097823e148f6b5eb2290a54d9a5a042e676621d90e390510576e0bba440bf64948a1b66949e60cd058e3ba37df8d7007a89b14979f69875ce5a65fcb11060e8aae9f1096aa00b11c4b5547483558d0fe90af27a643a6fe93832f7618f172b7519eedc96fa6b8879c28b4d5cac8dc3611835f817419f854b4ba39bad7ca9ea9f1219ae9d64d47b26ac58cec4216cffaa8e5393242d54b318b81cb85f7c2c183e743d5b55597179c8b5539332b98a03a603969e0e748a276701c764c3b5735291a4750fad9022e512de626bf376cc38b999eb6c09bafc97343955ed3fab8109ff8a4093ef590f2c77ca7fddad4c3cb2a03fdf18c3b10b7c550ba9aa9c7a5ae81989ea1d031e8c84e9d02434c05291644e49ce6cb34f66f9eb12341500815390445388983e72c5c7abe91eb1fd3940a6f65a0f2fc090d0ba0e045f34a273ca53f029aa6fcf536f95e580a55a36d2dc3b7a363c248770ce6bfcad111415f6ce1b99ee179b17fa2b6666f533b7e559a686535514d4f7230185dd0841fa14d2c7b9cf01272eda06a878b9578c37a00a3cd784172f7a01993a88368b996cb51e4cd98927ad38a81ab03b480c98cf7b37e68333de1ed019de302a31e7ebd122bf06f35275ce5981107c7044c79d317350255e484793e3d0839c66bbd2b0bfd571745165d0477add144307d268a4f56f74f9ca1f25c8bdb20c5760f2858d1d60069f383b8b3b58b7600d611c0491a64071282032611de34194b435a88c51ff576e09799143f200544dbb09221bca6e0e8e8f24cac782b263ef399a42e06ffd429b92e4054a82bc10305c217c3ee4944b42235b20f67c3139f1f0ec43bf690f8c94c50ffb9168e36d568c7dd96f7a7ffdc2c9c04348817c5686196ec78ab4e23c06e12bfe45ed9e98c10ebf67816ccc030ad2912a405794f21f48b6e8f4cc20a091666e9d7d7142177d99429407944858c7ec4f92ef261611d2a2d610c5d743b61602d4f5b8ceb4fe79455bfb80f9c8ae060fd9ae2e22a66b08c00397df7e6335769757ef8980c82425309fcd167c91db1d5b7b1c6dcdb57ce3dc048af46567b0efec453ee0fbe1a235c857cbf8efa740d25d112752b9018a1664dfd1ad49fc42065bc79a047a0243915e5d45544f46139b2cf4379568cc7b023fc07e3fd970d52119b655258c2bdc8c54a0f10842c558728800ce146256812f49ad3072c38b5f8b3d88996bccfd360e205a40f5ba8e861a5a6b54ccad40d790feb321e2ece247d15a92c48eb10fd055593f8b9c24deee61414555d2e7eb731c1745549a32baa998d1d7095572acddce2047635670809a53e00347e3f955ed9416fef6bd3589ab9228c8cd59ca4fd9a46bc01dfb385f3056e5a087dde148635e5da1471d843abf4c903fb17667619e16786022abc89425c189a221680c66e2d0914dbb3c410201e30369e1c01cbd7345104b8888bbf2b019ba4dda45ada0d8ebfe4503eff97743955cbb4775840feb0eed06b308d4c1dbd19a84532fedf86b7b068f480c4fafa277fe92da155ead48d2cd732baec6d314237df03863c8f32bb650171f037f44c2016c4e7057372adaad2db9f99d5c8a0f7f70f6ba83f4c79281d449f4ef79caa769227233fc5aa7b1f0598639b9451dfdca4f8cce42acb838fff867d35f8d75f45b2b7cdc7092ab6aebff75e76dd53f2bd48e437c3d0b5da4d194bcf6c8ad3d8d7d17a321b9975987d517dac4c96df7e81a711ce49f82f3f986bdc9bbdbe29645d2ee6607898dbbfbe0f2240a13b8f9a82002e18f278ffcbf120b8d9151b849e9e947e37f188eabc67c77b2d6cf7ed6aa2bc095b87eddbce521167f57a413666a86278e86c28752187a767dc45da999571645e96323137ffec3b6cbc85b9bdd17a32ecab44f3ee2e54e82279f0ee6d14adddb3036dd8856e42afda55f581ea7bcb2f6b8f8a82c4f25dbc9652e0eeedf3c3b5f27e574e82077d5f2fc50569c58c1e34fd00e9db45fb2bcc85a1a8f2498d063b4f0c2435de8405c1c21052d1c5851ada90a416c88731d3bcb0a8ca609aee4d0e0e88a414bd16c9b50ef486403704b870afeca00037f5f585b17206b11553b92477849f625da15f913a5886262151b7d74994ef5480e88a19245a7b1d186b7ef317e3c6850b110ed05a87217bb7b84d091cb0ebbbb94b3dfe36b36c581c80a039c2b7a96a7d9e6a8e01e42d10104a7fb126a73d788b0c223c41c82da42606959f4645909344a29d9ac38ca6b1c34417fd61b09bbbf634f5a034b3271f6d8a8a5b7467be936ebfae354663d3fe0edfbc93763a9873f4261767b07bb11e2ad7470a5b69b3acd1dcd1c756080fb79a07118b0fb19b4ad78f9cde0dad55541e3ae6dd051113e83bd3cebf1d733c09d49641836f9d4bcaf4572a3c656dbc0cf5ae080297df8699f0c1d4260c543a438c556b5d78911cf44140ad60d2e4d298075c9b33677db63cb62f7f63850e5c561f665d4ff7dea13952d5ffc2fa2e8f0d9c34fd50154971a38bb871b06ab6e84b3017c73d3d89136bc6a212951e80a8084df018e1f1e5f4dbc6414482a9af062a9dfd46cb38a505f4d843f76f7e9650027bd9dc9764a58565f6c357c86b8a8477d9abcde4471a3adc92a208cc7e335df7c5569fda8c4869bbe493597c290f1b6d3f7896331fbffc84d31f52b689ff94ab5a5169c8ec2d089e6fc66ca88b2bbb00d70b1146d0f5660eea36d28ebc37fd4db4f71dec453e280059c522c610e727be41108d5ce1d302a9c534ecfedc08d7861c238ddb8b538e9b794d1696f25ab792d0cd8ca72724d8aae924d115286ada288739b6ec572d42967b963b8eb181d60c1c035d91f6af97dd86563e3ac5206eeb4bdd57cbf632642aa0e64e0b00d5383afc15ed5864dcfb18038b0c084380086faaece46e203e01aa120258dc9b096c128b3f903c86ef2fc7ba8957ef564e55273aba93cc31d78643d0b190877525c61ca702ec54828025f53c9507ec05187c7f94f7566d43807c62a9a5a054f1bfb70295ab3e353a7570e6cfcb5e32368f600bb02b1920005d7c4dd79807b1616f2f1a0b4d2ff4f94d1a4c4da1d22903856d8d5c84b5777caf41cce3910d1ae9bd164cb5d9daf7f4a1758a60452935be84da4ac5ed577b9b00627ec0e006a1c35b9204710afb47cdd51a4bf69eaa4753a4ba75d45d8db16ae7b56a37458adb47cdd908af365eeb4b4bb4ae7dbcf29a7303566cbcd787967855f7a83b3711a5ed2b358337ecb1a2ee55736d928b5b33aad91a842017427522567e0329e511d21a629a354f45fd4dd0a7654571ee59597a8767ceace115650e8de124195b6613d0c421b0eafe7ecf4e2fedfdc96272755ea68b64a61611d39299df971b39d4e0e1c36abc06f518b05138b90eb59854c20c167997fe90cf9494c5eb289e27358aab6b317be8ca276f1121277111b5fac5e38eb77203f78d0c460e653737d90e8290c3ecb778859ed5ace654344645cea7f0b1292297426152601e8588456171280883c2c59f60ec09823b61302766dec4c29a189c891a63c2e24b24b6842fce95f04e12d245a21f24d2f7083f4764af11dd31e2b845fc5304e912810e11c21dc29e21b22b84fd08116f10cc0982be40447a80e07d3ee4030d87e0d67f40220de0602fa11565a9edd74e0d62a115250ba64a5b66a38588b1222d1aed42ad0c595c32ce191b39691d6abc542cf02b00e249e1d670bb3acf4f5a4869a7aaa750389e6898ec3ab5af2a1211cd7bbff5ce9eab5623a2be3fd4dcadd0969ce22059958da7a772e0be49d817bd1cd267097d23ad45b9cd60bfa82450be17e80b0dea3b0c0c87e4bca90c412f9021974257de6bccfd8a72586c989a6466b0783db51284288789be2523e7518ec6940eb6e22255573ebe42673fe272a6deda27df7529cb6bf2d705d049ce5e875cba12342ee7345400f7cf7e5fb1a356749ae8b22d048a7504db2229fde7b86cf24f69e36d3657f45927a94cd33a55cd96fdba6adaaa54aca955a896d984c82bf192591990a2725dc9e04dd82a913b7c0f8392738d0f30ae1a2eb81c6e54e072d7c06de391f7340870b233d738e05792811c6e246b2650f9d56461ceabb912b73b71bb82951f65687f24b98040c56e1c7d00665e8a8c2e8d25bd4ccb657ddf6e0cf100ebed9292d5982eac645e47931e974d5ba6639c9c9697e819ec3c609328195fb18332f1d78ecaafcc89ab0ebfdfabf7b19bd2729321408d2a006bcf79651e62eb93258e5c6bc99860afa460a5f981e37c4375dfdee90fda7d8999eb566d424bc4384b9075ff0ecb14a1140a17145846b23b8a07086a76a5e955bca75c5e9d3b12e389cc042d7fbb1a220560e67e9aebcd422f0b7cb8494fc9e92d3041535702a97a6bfd25c222084324286d21fb72d4314760bd265355d4076bd2fcb2bd0b732bf11e37877e14d10613aba06cbe95f9d1cab393eda9735930073320adadb2f8ec769c16dd9904d9e83d31bef9d5c47794855bbfd25333c7d0ab89dcbd21af479a15ed59cbcd61dfb2a939f6c058fc39e3f5ffdeb555a1c3637c3195eb7930d6badf77205b4bebee20427682dbd74dd161f4b9e4e27c48268ef1ae437240c22ce5d16e8160c68c48358f75889fcf9be083d4cec0d5944c3f1b991f9ba06f7ee3b83f8fe78ae414935b5962b537e00d9b9f7c827571ba2e5a0452d1414731c3d628ae09888f062486fdd5abd438142fd02afd2a6ef5c0caaa4973e231260e299a7f58614b5b72e0ad2ee06bac3c350eb71d872efeb8f90d99dc902bdea47fc3b7ca35164a0224fb95fda4ef5fdda39732bedd5839eb4b0adb8daea334327d9df635d0d814885a4197daf76b7f3af504c2f2349409c353e369c2bcd38862a7b9cd719dc60eb2873986eba6d398e39211578a40dc800ecf993788f6f109f6bbad4d36cd0915d169be892f04670f5262aaf7b73512c0c677b26007f869eff9eca4fbc838bcf41233bf6f26fcc48d3223dce6320e7ca8c77a992f2be24a659f4d1bf401c4565b1af1887fa100235cc5b9eb9850e82b42363ef4ab3d3186c2be86f61041c78ebc19dba7cc708f728284f851f2d5057e02b2795ad682e7f20b112fc1011a617933c181bee1d865c651dde0ba171b621b24aa624634f3068e062730fe2ec3e41130368f23bcc8decba6dff746969d3097f62486051df98e641fbad99887e1ea8ea268c2531909a858ad034c0d78aa0c5c36c36c2397fae594113043c281b231975fb301ca41379df3b438d56da317b74202f132b5296b04353d038fa0c2242ef6ece7d0260ff0bda32de3e67a0fdff58085800ae820845243b972560e0a40c9cadb6c76f9514db9c12b310936c0646cc7c2446196c32a1f63553d88c9e01ae03eaf0f7118b66b13b3499483f4e67b112823a27327483d52a28b17b815f6038657fab887395ee89ec5230d5790e77683171650cfc370e4f10461d5241cbad65979d8657b359d0523907e994931d3d7639fcbbb283b5534b7bc2b51b948bd9e5bbda34205c88f2b3da8a1478d359987233fdf112378cf117d4e0f1d0a6db93740d2a1bdfc06a9305daa30e226c0e2a876d6faf30937811d7986ddcc4175915c35182197549edacf40454f66e98d022412cdf68a3ac2027ffaf0f72df74e88b79b26672753367a655526b1c7cc81b16e318ac0ac910494b722a45a9a9b7a0b0c590cf7390c92c6cd2eec8a042551382317b6f05706e04e2b36f9581e3fcdcc563ce8c566134518d486f25112b31274e45ea29bc520d28c26fa0923bf131ad6fcd4c3885a8ec14970c3cf53f99498a04959d07ad25be3b68ffaeb3528970fc45fab86c40e790eaf436e52815bb7084deb17109aa4851f492ff1b40b45d6f85000e98a54260f8dd063aafb9c225471094f976051486106cf8e8aa2c961c06745259e68256c24f9a8cd38c9baaf75010aec59391b3abdb4feb692f809542a1922d31e4bf06f4a7633d689be76a9009f67fe01072b758f2c1c89458fa2e7b91fb46a14f178450d435ac89f40ece8ade4205b82e82d4ec043205cef359db064508fa2349d34f5e0bcbd34be6ea28762aca471fea39650a5cc558dbd17a0bb903eb5e6649bb5638b6217906022fa5dedbc8ae1f32fb7cd209df25a0199fb9657ad9df9b12738d0cdad503f3b8d897affa4bb3e2401b2303f1db9704f1f2ff4eac5dca2a9ee747678c0587839656b7fa4ff942d86cb8a2ba478ae581be02862b7574c243eb2f7ebb118fe86aa42f530447753adbcca6b9932423ed9a8f5f2219e1b55beb22b33323f1d84bcfe319a1b15ce85014a677013da29942e966ae18c68a186495c41a0a0ecc7367d4e03f103c6e3ffae56a86d3f52f72ddea99afff85b2889ade978ad111445dfdb30141ba66be45bec1d224221653e3db7c57e062a4f5e8483c430bafa8552f52ec73ec364aacf6e1fc2fa07740743028f71c93c0516dc72c4a23faceb3c38952ed39c601d05b0ecef8eb0ff69dfccad296049e6ace7e0e00eb46b7b5b23ea7986610d151b3441ea1b57430b30e71cfc106371e94381f948c1ff3989e2b58376c0ecb320bcf28450c181858ac39e4ca151ee974f4a288df38217c86cd364005043e07a1ecdc2fadefa3939164a007f905d07682af9074d0dd3e6aea464a960d2e78729aa8ad90984b5d0a1138b527626b47c710206fa2603b58f5f701b4d4de2da80036bcc547700408c22f6bc408575060b71443e7aa8585cd789c4a2fa7e9fcdda061d439c76fba9be0d9cd0b6fc8731986cc8ae8146ba044c04d943af11817834c318df0c23714cf2b111099ce40b8084f79578d17cabac8d8468cae4ebca601f1a2eacd1fa3c85a3155b63f92cf319e14a5e6b0c078e373936a77013804f7e76391294524a65cc9394c32ba3dc4c971a8fc7e5bc00aa32fb0b623606f77e0b83415087baf4414925d9d8d68e4c1dccfe3a68d74836acee586adc70b47ff3594b08b8e792d9bbb6388c5d3bc513ccf9b6b406dd98a95d1697df4b6db83d487d067a3b6559b11d497dc8976001c44c83ec55c8f6e59202c57e33a2e27f40fee8b660e04c589bc189f7c8b57e01e89e3f7044f3855b830ce252c7a02384092a4f782b5c407a111c67471f8b47fc4e1b0d5aeb58c3fe8e7889f8f84fac97d12ea61e61b34dc65b953931d328549cea98fe0f1a88bd7ea9dac0b5ad7ef121956115382ace743121fd96add0cdf1b4ae92ab005af06715a177a2f2e2f9a7d63d399454e1d852110a73effdff4f42d14afabf653185d2f64efc6083076fd489bdf9844720bf2bbb38b2fb84482d7a04928ecfad5070bd2c0e6d9a1ee7511a235e4196686b5a7ee9e3dee593af8296156e3b3c56883a909fef755664960788b7e0b642444d7ef18f25626bb298dca447556732dbca645332c10366d456eced4b433b51e46642054f12f23a0e780900cd22083ad4fb7444540c17c5a077323625f090808c02a8d6df1c1a64b236aaeb03f8f8dfa73a18e1183d38aaef72b0185daea25cddf3945a4b898ab335f3d267af5a6b5841434b2780d48145de482b059825b53d7bf033fd60494029c9c4561ad536aaa9bd907abcd6712150e7e9195f6f97b863b91ea191ba58114485b86ce2e66f87136035b2590e223d75d7aa2545d860b01f3c6e87dc0d2a611ebc9b4b11b65edf028eed0cf390130ee89c7f9472dd206983c1a99c955d4139102d15d39fb6a10d29b3c15ab628177c2cdc96d2b812c93e383fb76752b179cd0804b1e4c12f0bf39c9dc032c3420c9651e85a05fc5dbf299d9ecb150030be739e68675dab9341de3311bab7ef78b31a264cc95348637b5b2d4a4e965621f86e41d2c33f829378ab1453f94d54d908c6cb4eb9a4297a20964774e8ff1becafd75cf6d42d2c63b806d2f95d0d091f727e41889935863d017839605d494324fb8b63ad6603946601621916903a706b5f6f81bf2f5c4fff6cae22c43fcb79288925965a23b963086798db71c458f4a80a3e0c5686d5bcc71c328116242b7ccf82abf74753526ad21514a7698b46bd6f8ae1de968f8cfb04785e84392b7004df54e94c4ce00aa60921ba56b50bd2a78a62ef8fc3ecaacf98c9196d8f8e24d56479fffa425e029edbc60340627999c4d121fbcdb4b36a286b2bd79db8a7c6aafdd92983f59b48f6c78fbc496e94a593bb44f52fe97e7fa1832351d8a8d7c5095c4a1852ebc9d505df4b080ab48761989b7591ebe42161d3e5efb6548b421b79de85387f9945533d5cbe6b0fc1ad89d4ca214c308b73ed1dd4ac3ec79d23ffec511438c2884e95b10507de6e094e411ad162ad66b2ea21cd383296ccbc8ad6ce83f174dc7fa0e552ee0a03b4fe336d496482f68bbce96cbd9fd612215fd452c636d94b631da5fb9806087c6394e2fd63b80853b419e30a0c872759341c27d5b25969941e7b2fbb840e987aa5a78ffbbc38c5713562fe2b431ef38dc7d3175b01a9524dc8163cd30eff66caa1dc7a13f4c5c95ace03b6cc5426183ed6ee34da4bf4851bbba4dfcd85a4b4a2387564d6225b7c15b8d5ba3623f0b3c9ff7f95f19fa9a2b22872f341b86d5ed0f21769e6925c542693945a2327baba42bc0aaef5da406b9fb7f79935c858832dcece1f89d6fc56d10874feaf46ac973443054e3a3fca9e7cb3b9dafb5df13f1d9e1fdbef48a13a8b2474e83c892cfe73c330d9a2ef630df7c70f949b4282962cbc5f0efcc88bbdf3bf2af5a0adebfe8063abcede4bbc18dccce1430269603a31d5c82b906a0bace1959210c01a28b2538a3286bb25362dd5a7be17fcf72a47eeafc650061118cc2e1bdbee2cc856d46913a85a5067abb8540a08ccc9b8d88f4745bc7449c24a6cb9945b77c64c0a60027a7ac6ddab7513641aa81f119d048a992d23c128dfadcffac1c49ee354608f5e497c7d894d593780c6a31e4b8e6bec2c13df824eca1482bd529f2b6a7c884b9e3e32e2656d79d5324abefb105e931e94ab15b603fd1da1069de8c087fe0e3184de879e4f80bb7b3aa837b0421aba668952558301de807b46943b2014499d41f97c9a3aaf1e64d66fea00ed4f4c60cb437ce7132a9d493fc3ba8b130c2bc974b8a65c3d38f85b0178f71493dd86c3a229ae46deb293dd736dee1a40e5bf95c15ea00eb57d46b4d25f0af1885ea9a688a517a204a74d9c088b169147e7fe1ef59d57274d1a13f352e18af99e0c26ad9e3105c42e86c385f5ea038c8cf1579607d447410c17bf5cf30cd324eccd1258e7b1ca3f7dffe4d764dc72024a78849cb41d1cd1dd7edd4b0a972c338b66941167a1e00a3438ef6771fc7f695dfebf2056bfd6d50cc1e4da5831d522149d6246c156f2169023de4dd919896196537a5eb124cefb6c6ac5bfabe77425d6e8e798649d1fd8a0e7b0d36bbe326a6699c4fbae07ac82226272b1018ce490d14748feb664cb0e5e0f8d52ca854aacd042523ab973b6d4d3a5a6a115d78cb747a7109de1e3d3d663b2cba664adc12cde7efb1c16b1f506269a6bb47b4e0ed22a66453fbc12ac5e289790874fe496b5903e84eb93da7c71667291dcd9aa194f83c43e30d29cb9097702f3f842cf3eed8527f01bd09609743528f82e4aab4333e5515443582b82cdd8ee3fc89f61635e54cd1d8789c0663d9b41dd900bfe7fe251e40d54941c28856563b2866ef222599342f4cd3ebe157d0314378070cf29f2e1ae7112d765ce09b8cf5a767b1a8dd7d9cd13365a6c7b890394645f0b685287e884e2abd1c7b06145971ce7b2a9adae89aa8870193c5a379d0ed9a630605746304731cd3c952c5acba8717bcc2ec64e40bea6ea53c291b62ad3f6ea0900613731e8e2af0d814041e905921b4180098b17ab3ed28f4b29a5b7bd88c0b0f218fda82284dc6d70d68641f0dc437bc770a234bb86e369e7f7447b1fb330ebf727cfa88d89960674935d204d7c203878a15d8c760d152e0cb524d0faf1b09e9022430fa8d53fb4a058e3eb2f111b5478a650b83788effc04dbe6f931f74906dd7b6eb2335383aa787b57525c96b1e8cc1843d0af4dc5688ccf643cb007e4c7b7eed39c93eab68b261a728e6b4ef1da3ba5328995f569e8a99a62b98614b1265a273e0a03a6893c9af16a76959841c82a3ac1002f209f4d0603896f327330cd337a8e1033c9b3b38565251317eaccde59d682906cd615d8d6aebf4ab33cb58b86a8fc64c90358d4137bc49c54007da1b45b19ba5938dc21ad7b59e8e8111a4f2df29821a2c1db491ae666cff52147fc48e4d35a66d05a6714fa001d69da3c15245d1cd2d464f2681440f153d4dcf293271d1ea33be28c1b2c8f177f6871d7a95adbabb4e5a155b61261e354f0571c8ac96f2db520869a68e6fac715cc297720bd3adf0689ee8f181563d7a38682b1698258c940a2ea920222504bd2fc1df04cf82520488b734f0419aebf46a6eeed221be05ab2013e21e5cebd99b185b9c2b8be72efcb9b1038593fe06e03a1b604253f7673f4f48ddda216f8055727b91fd9f57b3b078d422daf058b9d4676ee216efdd9299e41660b00e6e5d1b82145b1b0f30f6453dae25a3c77553ecdb0d3ab1b79425c395d54baf47b5d7dfa3837e5d4a231ea25adcad0c6b4d55bfaa31119d83f060a4325ce78f454657845cb6b94363d18e3bdf10954eedf18478580b465e4f811ced7684b6cae21859270b26754909518cbeae497699c4fc8f42bba8f0f4afb7e3c370f1fb3175da9069675ba61ac872c8066d8ee45f3a6d0c95556dafe398994946fa7728894cf79c80c4460aad07406acc37783bf6f39944f28abf37558fdd053e9c8cd4b20a521cd350923162137e71bd18b44f126bef96baf849cffe28c867860c7838594162cb2a37b661884f65325a40880857f171652049f9e90d6ff84ebf3633e390aedf2c9eecbec0ef40f4e4a197dd0fd88485ee1808dbe96920d51b08f4a95787a790efe986100088118a9ceee84ad7c9f17d541317b5d79006545e5d1ea698c020fc5c26fc54848537fdfd2ba66a30d17b65e54a55e8072a63de1dba00c63bcc2dccfdfaa357adda62ee6d42b9b8972a1a8d522cdf33a0fcaa55e374fc2aa6cc0f79fd1efd4304b7eb581d40a4154202b26a92578ef2329b908004f78aeb534323e5ac921609983d4a9c7eade2ee8c818081b78b53d6e48a11f6d6bf36bc466688da900adfb75418cb2acbb32f5c1e668ff871619b06d000a7349b99df526b1f31dd6e1011d61235bedd294b6344f239f5ba789cf05f439647c3a733b739e96cd8efd42f6785362e6f73bf03f6f5cebc54807f57d8c6b667689d367bfc121b4fbb7af90772867f17983680a50f50a214b2a58edc24be6c9e99f13f2c44259e09ebb9ca2b9f67193120360f78895aaf29fe61c6c21855e370938b07f163f566f934800ad8e1e945d0863670d35f2e6bd678667dfa082a2dd4455b7dc79b4d546aaa14105de80630bc9d754d02ad769e68e9338d3538c14c2ae555c8d6d8cd0499bc0bef1c78548e54f5428199e50a9ac6fe86c50090d9ba46c020f85a849639607bb74b895964afcde622c0901320466ba2483205960f49f8c6567a7330e9d909094be64622fa1a64e06c3699074a92614ac247a409b08a8efa3d7a22edc3ebf7af67526a9c8f7e10534c5a33d2e6dc3217981de4460c8e4be8758dad1592ee00d84a37ba5018e77b10e2a2c2e4b33ce0dd7dda6df318c0a72085e49ba071748cb5ded0cafc8a4031d0b96b7aa5fdc74b61cbe9d45ea98ee47cc500ea23087d492b0e7fc273de35f1e982789902d2f6cdaadc87b015d32ac29b5af5c3853bcd8ecc804b6985fc62bced70740719720eae3fe22faba5042fa2abcd914882ac8cdb29516916870bbefd799110a365fde34c0ec03332a0eb68f92eba1a10857b1e2a8517a191b354af3d2b802094df462c135891f378e33732c2d37dcae6c96e0293b3457546b4aa5c692ecc71e9f257ea86117b2233bb9979edecdae79bc6002b6e6f0cc2151e75f11dcc148581040a5208cc21c7a71b00fdf70565010807d81fe80cdc77aea5a1c3c68d1cc6a8f974c7a8a34c666769fd4c797152caa503e61f17dd0b31aa1a62e4382c1b2e1788754022968d4bd7fc165c76dc2daade7828b1c90ce8c21155a7b301cddc7a7f6425942af93aafd0d9c6860edd8446fd0fef38d4e465df128d4862986f4b99357eef86d4400fcba9d04949602a1043d940d76e4ece86ccd83d1f826fdea0efa8c0966c7fe7075ce7c022dffba217570e20324fc7be6d7639b53ae41e4725a22a4781c8c0cdb6b352dac342ead5e2714c426606879dbea5c283f85033704c6471282a64a6a6f62a46143376e24d4361dddffeec5a86803b259f048ddd69d76750689ff6380557d12e5fc24dd1e6aebd9836dda5e6cc5a604497c6b413aa0e04691e8d4ea474ddfa90396b225070a299c3b912668338a509840ec1e14a5cf0314e19e859d4fc449490231d8bb7538f2b3098b335578c630fa7c44b785b6b98d5964bd3f711e2b9efa8220a1a3e68106477877c3766eb0e077460fd038944a7e02eb778cac877b42d18b38b3af98706e741e829601b0eac2470d155ce9dcf6e262034b52d364ea0d68c6e14fae982d5d683bacff5b6628163f345b5b50dffbf0869721d1c7802cfa9a2c01fb06654bcf1459f5f557a8382f05209d314d06ae41a32012219af16442b702bd2994a04677ac2cf7fc0cca401c44df685a193126cdb1388de287de05f32d0d2dacdb8336e36fc2a0631c0159ec99f1859a4e934ba9cb84c2305657893170107b905bf3f8f4e8824184d380a03a5d7af529491597c5d85dd59d5cada6b7d4e879caa6e4792b3074be86574af8f9d8937fdb45d0ef56bb10da75451581f1873f84df207a416127919e228b38b00b56f0f98f55ca89794562f86dbb7e5ec4f8e8e9b71ad72768465f7db18f88e56315ecf69e6b3f39972b2e89832e58fb8cef48138b23e09da0fc185acc1db0778a9b24ce674b9c1bc4738c35fe24c1cfd1a95f4ec0d1dadead6b5316e72ddc8ed5fd31c16ae4cb67e9b7fb35d5c63b906d540afee3ba58134b3437f94514cbaa4e492e9dbb56548e668be224db545a0b9be4bc6c0263483fe56921e8e94586bcd8fdd6727bb28117fe892d048dcc9ba1fbcd5a4280d7b45f1b6e17e255b216a403e0f47ff3683556a82b438bc5b3071dbf4cbfce8dc3f1d5db4b87f169021389c32a1c05166f307f3629220423729b637f4169b4dfce16043d1bbb490d20e62b3bd0b34f990a2e36dfdea130f277a0acf08f7668afc920958ad4c18e07f83f83d12a28ab0c14e82dbb66030b9d4140933d7c55b7588b263a545df5983e334509f0a766061d77f67ae93f8e18d9ee83e5aef564b4a39f6957f337f4694418c5808b3835e6616008dd0545eaa61cd063f30a013aa9e16af98b1678bee4dfdfa1804703a7c8c5920fef4cd121221ed7368e389b3334688aee410d30485a8a4db929712ae65e2286c643dd2e87acf2850e5def513a9c166283b00cca4832ba59b867e1947a7c01c17fe0155d19894a1cfd10eb6959f3581fb32f01b569ff89a5d4a7fa508dc67b740455b5c98bd633d20ce8fcb9e3f51a8152c8dceeae670e3c847b24af50a934b1611fa1cb9b5f829081aa7b13d0988c6970982296ae661e0ec74f3b3252ef6df05a5fdb0b5bba8420e08304339ebbc7084c8cb70e11fe19d7540e1fdbba44891b3be4ee42e74d284e5f87c76f93851343bc62f6a62eb47ff1263c8a6f1ddeed6e99b07524100077efe146006eded90c1231434251001fc1d9488ebe8d017c50ec7a6205afc15f7b4a021a995dd95056e47622e653e4f3ec71df090a834a0a8f0424085c065afbcf6f9ca4bab0361d276f1735b999b9ed3066c0a0fa87acd91935270bc3207c4e59abe4aaf0dbf6f3686fe5a16d1b3cf854a30ce3ec14e9905dac0a3f1a06830ec1348d8e1ac9db4f05ccd1349e367211f3e8f21d539df08102a550f6837a8b0aa267527f95e893b28e2c7327d0614fe045fad4dbe3e708058c5675c4e740a883985157c618ead001985f6085babf244c28fb7a705efd7c3954a3abffbf70fb90d82bf982925703e7b21f38c80b98490b88f7854e2c1c13e060ce069efc258cac96c50f853aa2c46bb6099a3b42de255b067cb7979ef844a6a551158ade7bbcf522cdc8ad6a7f79b83339646362c021360a52cdfb4bc5c6431d950c932f7bb46eb31ddb54c485f0ccf7f8df58a67b614d77eace47c658fd18dc76cf165634f4a25cf395416e8f3c70758698b71b07853bc76e3b920b8f1377969fee9e1d43cd8251535c22f683c3c2bfcdc255495fda79143d0b7aa61cacf6b0804c4926fe1e0c6961ef251ef776208b20193b43edba7ab8842e055bf43a398fd87ba3f0a2c4e19501a7f7f4213321929bac4781ce9e204237cfc6e2589a5040d5b5b4f5941ccb3c0548c4564f7d69ba6181a0aaf2721a83b8647176304670a029cf06b376703a44937cd8a6324140c558ffa0dfcd0236eae6fac1650c299f9cb8eb73e2c333848ee228c9c52674a7aeb5c9d491c6a3ccf9d843a60d96e6e1ca8caf524abfd5ad7a0777f4955ce0cf4d019f1eef3d941486e06d60e7889d490366a552714844f1543a0ab01b261b38ac1d1439fc6540c99fb23d72d7fb7b1322a54cdc11ec6eb72b758ae862955481974647adb60cc399279446663925b8a49f38219aaeb4181a6b3adb3bcbfe1e6fb98101823f9246f688f0c40317f728266121ee865b2e0a4632aca78c2bb5a6c10a2deafd1a4124d252908086ae74b35564ecb7ecbc2999196ced3271334d58aca54ee4cca08a8f72200adf09b538d56610ce8d74ae655e7c996defa7607d202c26db404c4a4a4529de5e1cd05a799a3c82e11d0432577ffa3af3dfe3bf277af879660b411681068a18365fb46d361bcc3f4b2c6df8bbb40599d67fa68f27b145011b529ec085b043ce86bde429a989ded1375c2144aaa6bad75a352fdd7a062568be8fb6c3977e8e551fe7961ca4d1fb614f512213b5e8a683a808cdff534f5440bb11c1daac4b4e17b47b1dbbb7478562a68e15ca8f8a5a99f89f65fb9976d3f9a763e61cdc486062ea24826a3aec0d0dce041f61032468cb87af110891c7434fc8f2853ee2a30843409b85f11fc4760a1d3fb2a1e42342ef68ca1aff51aab189a2cd68058346199f36fa5d4331c5d6419b53e97e4815dbb7a53b7022d217dcb6612299eb81124a072fd1cd6cd469f023e1c05d4f3855e21b933d255f2a2908f230959f9be711960a63c5fd6c3aa22ce931c0923d911bcf2b51ecd421e0fbc88d4c2657c8735cd61657d851291459fd840c9aaeb0daf544f401ba4f809af43b4dc66c6278007eeb42182d41e872ad1e0f45228bc345fbff47d92a7e47462c038d7ecb505ab823dca76728d64ed81d21260986f7480ad58864ebf884a243ab64f294661c48be2cb0a05f3e923cf7e58ca062ef6eef72403693bbbd4751ee614f5642d7f5713ed85ceaf3d8ecf6c55277d0cf1649cc4a4c50ab193923fbe67c418270afdb349880948f148f3e7068064f30f18cdf162bf19bc0549e6af387a7f8838b29f0771dd3c086f16725675dc78af8473ad5891cb71ccc22a5f45ec15da090e337bdfde393652bcddf50ea9386bd775b17f8cd9d07c0f185439ccc139ebd85f0c349b222b40a0fe5d374bf5e0a4b8652d65d424b63e42356130c15bd62c379913afdf4c1ebb77f31e7f0d870202456d8ba4dfb131005ebdd12e24c3568fd651ad926558e2031aeab13a9932dac51f0d8222e1ea48d9ef951310c1a85725755757d7f246cb38b1ea8e49d0a14268417795d724540253d19947de37fb6e7a18d209382b1205b25ddac4287823f9658509ee3a7a0ab8cabcc21a5984fc09b91628c302659ec11914dae321acc1409a747a6a169aba6981fc4c2d5c7a362b87e6eab10bbe169f6fb937a7717c6ce75779c8eb7742c09f547e29ac809385220425066e3075adc28cc0c53302c87ca4fb8d39cd32b51c5845fcd821d8555949656286a1adfe1dd55877edee1b53b4df0c3a589083950ad25d8adb84f1a554c66972e1af8f296e141eb22cbed9283358e68a3ff682cc4c52c37194f83fc73cb442a2555afea3be50c903820a4a342102bec93b42d92b2b0ca43bff550e98bc96987b9020766f6648293d6e269cff8501dbea752a2cece0b475ca55c6f31f2b9df72f9ade4c31153d37c061ab88396793ff33a873a1ac43c25df59a0395821ab49287a97ac4afcc3aa766a0d57c667722a0072dac7186e54f9ccaec8c8e47410f27d9e2a61436baa24f3e28989c9145628f219d8579a857ae9a8cf6f726f17564a131a5a2c89334936d9ff1eb7d2e92ebba9306b2c8a5f2b89e7e2684ba854463856c436ea3a34e4a6d29160ad18cffdd341f5773f789788a4950237c47ea074f96e4a038da775b214efe6339b37b3ed60fbc2ae2915d0a2838972569d51e67c653b307acb6a789ff90edb7fb855ad024eae0d69432ef1fe069aa07d198934d66d8292e01f6b21be80346c7999fe6b476df7078a30f185d15d612a0bc89182df530d6cc57d1b1b847f34613bdd08d8f17b4f932f7c610e140b1daff00144c959995f2f7eb79319549556d420914d7588c15d6e913074dc77fa42065ddc4f6e1a977fabb288743c4fa820b6fe03b5b9fbcb6a618806965a2b1052e12be0184abe3a444e88353ec8b39e38b2f0b7f896a7018fe1737d922801d1c3f0c0954f160101dc96d5659c785e20281d3649fc2ef831eaecc7eb4e2cd787c87237e96d93a529f1957ce607711401d315a46e15f207dda1c533f032371c77ea2f8c6127312616bb04461c459bfa63c6f732f37f74e5cc47c9538b9822f4b3689e821acd7074e6d4f2f5c44015d5fbc15374f60b11c59f6e2f2520ca0a3ba1bdd5b898ea65942b035406881da9f9723b560020067fa21e3e9a821531cf19c1350b3ed089ba298dcc1a18f598764349f2e35e0ad85601be70d2ac723944a5b9703948a42834702c7f118adef7c5b54c90d8fd94ae53ea11dee70d0f82fdd9153123f708845c15257a4053927125141332f5e57578a69fb9a506afb61d18d2e33250d5f75942d65d56ed35e82a368e735f839084869fd8f1fd9d4622477e6cc9c108a9861d21bdcb912aac874c223150eb4ea68e3a55d2c6854339c2ec226afea43aad98bac79a19fc3053b6fcd056b3b1cf160f0dc3ec7158eab05f0d3d67e9b064387006c4e86b51ae88f8e265f6eb920e8669e547534965261ad0a9bebb81d1289aa131f486e89c9a3d2642ed4bff43f7a588f5389ff20e32b1e39ee169443190da33a86a4147870995bf6c36c9002915d94446c038d74ca900d1cdc1bc84c73bcbc2f765bdc976d895c6442e14957e69467dfd890254ce36257981d477ba8bb2a9a34d439769acd21b5c4eaf33c4ee6620b44db2555cd3e4af18d7f7700a701e737eee25fa2274b2400a8fdfd38378bd8dc41f8f82c71f3a702433776de27a99fd2b93d4cdd5a0100a24ceba43717c60ad0fec4df56a66e3c10789597493a27226cfe3ae0ac09cec4845b85abe5e65172ba67d7e427749c1cf6900aa93c198be2cc24b6ddaba97dd6cba39bf537daf2123f6c888c1d779e8caafa52a71f70c565f3f9ae212d9359c5a4ba7e0ec1ad0eb2856303d95483a664ecf781671d4a2d8f2231702fa63d3e719094ffcb17db62f8c564cc26d82a6a6067cfbdc23c2c1bb09085ce1e606fc182760b6138c9fe48c4eb31d22b2c1075491a2b0389be94b5094aa356ff2b11de4fd04e1cd0314a3347f859a0e126b44e57bf2ae714965ba198474c6a28fc2aaf9389eaa5dd6bbe088c7c5104e1582093dfd021a4140e784e1bf8b88604ace0533c8a9b48c1105d1a35a11cc5254906c289a5e429db54c1dc7645d874a0809f68c1266cb08422911064150fd444bd522547d46a0b69089c8662f39d369c40d861d127932c33b04fec86b6cc03e973b20af238ac368fe85cb2939ae1e4892cfb58d4d4fac6eb14c0de4f5387138b11469d6007556adab75102b2a6bc69fef3240172f55d2060a014eef5a449d025b05efcd69b161a8b10ca96f3c575d8d7860a060d950b7e6e4f802582af28a8463745649c89ef54f037228418559599dbcd61e37d140c2bb5abb470b4138d28b7d3ae6d65ab6b9068a7b88d3bb58c7cdd879e9a818262e2b2df93a8dd44da88103b87753ff99ac4cc8f8392d0f333e9f652cb83bd16af08c74bfeae6f9f36fc51b1f23672301c882a600429e7a55e193be5d6042b995bd5f05c486d40f80ad5e53be4efdf935ab1fae4cb0f9fd3e35d2e8a448da0b412fa0493714bffc56a40ad790d3fa8c9ab37f17330d116dbc5f248fc54a84a2028d4463b125b7df7410db77bf78b709775e1b27e0ff753bfd5bfa4b0f86d63808316469086acd30c823baab811427e22fcd2efbde447983536ded47f548c28f9a99b53c20f5ca6426249ea89077bf7f138211857f139f6fecc345c91c5dd77be219c021a1f90d7fe7c089666716c99e50574242bd11e808e185f4b87f8b0f406a45f0073fb4b0a3aa58975f333cbc75d510512bdea9cab9189cb40166aa727aa88d4647a56edb033bcbfa4f39ccdf16ff2536aef9be46d517d0f92b5a11a36b72c8c36adc6e14cbcd1ce05635f42afc257c3e4045a15a46331cdc308762d76387faa570e921c208f2311d32b8d3901c32cb8b9e0124c866bdab4be0aabbe833fbec2e2b0b8a6f6229d74a8a835fc28164e513ab4b7f461849abe0a55f67c0216aec32473c999cf2a9ef1f0a41029a560b004ef5f5a285753796956aec0976e4869543a59a91e9c01a2a18104f4e764d9d79bb6f3c750db3ac9102dcb6a4b5689e0a3e7b99c79c1d238fa52cdf4f39a9daa284b20efa838fd373f87cdad9671b7e3ecae839dafc950c94ca19abd209e208902c803799a9eb1a1c26142f63304e3dbb209f8b85138de597a4bee98ae70005e098bca4439382e3428e25c544ef607bfd9cc9fc72b452c2b1a8ad25dcf7e2fc272c584ef57ad126dbe1a43588394ace5ebd87accbf3f268a705d8e77994b8478812451111880ac90afb64b4bcd51ef7efcff3dda319da44ad8bad8f54e1e0643a5762c3c9be5383d85b6b469ad26b872ee7999742e20b02844777ed403802b7a6e589e3f2b9bf2146317d48f43945665a79ef912763019ce5bb47770808b65472cc02dce0d574e7287500c3bb5c2089a64593d6e332e88fd03e0e68c5a835eb8130885d4e1f08f70bdc87b6027fa6bb80778cd1faa8f3c43906b795d4473c0630c70b586076c04eb4840e7c3c3215f60dc8de743ac8f64d003cc89b0cd8062398c794441ffc18aace5b6433e33f80cab63ede2647c3a4f3a9ab204705ea7e16eafce9be62e311be983da96941e13a6df018a0ea1089a332de9237241e2118d6f34bd40bf5a32cd046ec93bc15c8daac03165ab23b318b4446787e17d2ceae7dae98737ecfd12342495325f86b64a02722ce41d42e4bb5a201c4edc44d9f25334bb581336fd9c22a43eb6acdbc4ab18d3139dd4495bdf19f9b62e0767298f45c8f448501e5d9d39f78c44f347644939bcb3b0ad481c015beb190dd0f92832d8c083635de5a7cbf75b987cc6160ca46fd03466204c3fb5d2e4062489f3b6b6e2f310b9789e495454cee3fdf6b0c94a45ca96f4e4bc1435b0188165f10813c9f60ee4142bb8617a92ec1d8ed335a4f8e1e8ee4fa3137347551222a8a6302a4ea700dbc18c57cf6ddc541a1394aadc48f374ea94f9413cd321c957ac5da01905a4a5f3b687a3d5a01fe605fa4c58b2768c00e2e0e54ffeab63bf844b2b374881d5a8e413571a094b4fa4b9fa3f21ba666d2109f00ba1750ddc4851f9ac4f0c5e96abcd06501df5504c093272f9e51b144466f5739f5f88de432c4666e98673baeb13906d7671e72c42b6bfbe4f9bac64ccfd9721363ca0ed31858e20c51dd131b04c84d5ea80145ddf3fe32e6be3da4c5b0416a701c5eb24009e18fe7ab23b16d552f56708bc916a3ef2d032b98950427fefdcc4c9b3a49a643395e5ceeaeb0e01a47b59e77ce212fbb31a9ca46d0bb76bde975d0b69f76152b90c2456905abf81976599e5ec9a16fbe2e4d1c465f8482fe725b7a1c1fd7d35dce3feddda94000d253efbda7f0d817b70fcea19983cacc256591e3638f0aae8902151b4021d3227ee12e5188ff74f87b27388ed1645d337c58d21c3161309204dc54cc1c8cafcf654608fe74fd85de3f9b0a9e842328a60735f85fa99ff4fa0e569b2e73192fb0e2a96cf0ec0c918978ee0405f5426b41115e0c2d80c5ca29b4b1602c96eeb3ecbe6ae0863a9d2d3430885da8b69b0d7a2991b6f579afb8d427cd042b0259933a4d1147afaf945ccea741060a05f61cbada96f256140a455c56da999def374916c97cc92ca1bb6be1e8f8e99ad77473d786da30d033c5409c84560f28cd396ea402592b12c9e722fe412bbe3a2560ada8584c69c22182a1d717d4cfbc0e3e47d8c722fa9a41eef2286177676ddd511688cea7968f9eaf459e881ae43a94062e9132228e4641ba80670029c689197968933ffb4c176797615493c57c3fdd1a9c768240f612df1a94b26045d7e21060c58916aa733027e527f66179efa706555e03a1ddf40cd580de1338b61971643921ba24e1cfacad92e9622a97a516934c2c5b70b444030e83e27eec13eb640143b26cc9f86b9d3b516e0b591135a196671668b4fd195c22756f856561d969542762247cab7e965a6fc29614e3414b0b0f88391aa0789c9ded0de82d824dbf1214937361cf96b715863757566fe2c08c7f4036a3328efe4d2589347748a54588b917bc084e086a642191d50dd1ac3c645989486c6cba42e464ee4d4f0fe5dd1ebd16db13a33a0d35f758ad9cae7af8b0ce8f65a5cfabb5a5f19d365b696f797863fbb66db614debc8cbdffae7ff7de02a561c3506e147c7b41e19a3a12eaba7c6d2ad88f6d360054a72dce5422c8d1d542acd5117ef6760c3c631a073c48332189400e3b2e372d80ebc152c06e9e3ef7919dc034460a76b6e8dd448e3edd247a90babcf6a472f374a495823caabaa9e1111e12a9defc5a6ad24fd4cc0ea229b4b4344b30c23f8cac8a91f531465b7c8c24bbd0ea3e5829b9d06ae9c1b2d3fc543b84771e6983f8ff189aaef718e0b40f06e720bdb1344759b8bb54a8ca630af8056687bfd42666f9f5e4aea6a9193c375253e647bc3bc3f81ecc7f0fbe4390ec9febb20e803f289fae6ccf019e32ef36d6fd82a1a099935bb701fe0e83c11b7405345b354926f603319e888966aee5475875d9253677381332560db1d24e9eed0c241080035d7724f758641877897f256d6ea65b31c2378cdcd546f06f01ad6bfe46cd8ae6cccc6096979b845cd785b0665d65f94946e9384bcd343c733a883a09aeba05bac843644efdb5ac229e20639fad7d9f75b11947c44925bd9787b7ecd571273aab3794a997c1515964a774e7bf1252617924f3ff86f7d753a6c60238df23cf8ec556829b8f40997d04be983372937a985759c4ed62fe67e47c173c753be6c6f9f9279054a4f886195bec986131c613d77d4bb16e2db74cc337226794bef78010e8fb27cdd4125a2b2d4200dd79bf6011f3c8d5d9a031016aaafd36fa8454fa284bb6383977df766d7256ecfa012158e2aeaa28d19485bd23e8edbf5867fec0544e1b49e1c06aee3ce530b5808fa01b9d6342aa0799b5253b4e38eeb43c383d3426e4ae779dc0a1a11d87af5aadfefca10c30c0a79fa50c3e5e516ea9e4073f170c1edb4ff22a7132202e187c6c5983f7552bd5713b1232a9ca431564df80f21b7c5db39570cb959a3c64a103379c01c63e97320778cac143c3aec21f9d632e86bb6a5555b1b5dfe2058022ae3c31a8c5818385390ad84c7317b6ee2fcdd34e3b4e886e0e09cad6abc93c3155c123153b2f262750c715067dc4729acc9bb3a1110013dc64308ce3bfc70ca253bf80400e061ac562b465216c2b8c5617f66d4794e34d51e0e7100d5f0356969e78531fc8f7e59ab131e83fe1ae3515d524a8070ea765a55786f83254939da5a3c1a13598ed88915c30935a5c5b3598195b5cde665e377c2609a4d7f3c7d03364a86f87301c784a278a4b6bd9964e33dc8bb6c5d84368888ac0859f12e229e9787f599cab2b3f97b6d24327bd1fe004fdf2dafdb9af4bf31269c95726abf11f477f6718e2b26940a32440db46b4f9c4271d8495d42ba39e9ff188984654ede58acf8147dbfb0687a438525d11af4c1b1ae4ee8d0d7b5856f26502fcba8163a49b0d0c69f94cca1f6981612ed472559d2b44263024398d30b9df35f996342484a33a2cdae2414a63abfbe7ce85deee71d6e157408cce123dd73432a27795a089adc37868c33976539be0cf80889b44a41f271b88d38767b7543ed9a28ae924ed075a43b9815e7aa574065bce49197c39bb0caad4860bd55877b4aad538a385d7ca9a50c8c3fcc380bfb0a264c1f120f659daa9a36efd06a7d834579c6d58559ef52b09da0609bc6a50dd80a4571c61aabf99b4380d5e1641ff0b68656c9f54d967f95d0593be81652a1156ef34f9bd236b306cc5430d6f91ad7b81b4bc9a5e41fdad5342f6a917aba749e11f5d4103863be2028f52ddcc7d22ac02a8409c10e71e98cda1b7d0cca1e1297297c9581c7495ceb8bfb08f592aa5492d9885caffec394b7fd767b5d3262b0b262c9274a430ab18b0c33ebcf4f9196a43885da6cd0ffb04ba3b9fd9e7217ae356f2681d2efd1e1e4caa8642731de09497b4b72963224f5a83cd7120d33ba9c6714548f04aac7040d2562939ae1186586a57f22234ff4e48a2b443b9a09989a5b63c2287963353782d1ca14ce6af314444b44e2ab9d956a8345b48448b689a299f12757ccae8befa018420116e121c9052dc64b30d5f81686a16df5968da2ebdf351e3f39437ab7a4aabdeeaa380a229130b26d6040d5fbf78d7a00818dca4e3db6556bf183ae5293d9e6abdb7ecf5cd67de70d98226e7e3e67576b2fc94f18599a95d6db21c66cdb3e17ca9eb8ee04799c6e9b25bf426f76b2985ed615c1e55c92802bdfc94d2c9e45a29990fca52387372c94a6ff8b464dc95b58b4368ab82665f61727cdc9f4edf4cce065bb7da7367d21527b1a922b4211433d6743f51e206dd18f15b912aab7d0af342284c89f2ec28309c4778bb776cfcf9767d403852a9634bd7155323e09277ee3ca9e030b1b820f1a85325b2a61c7145f28337094e096a9da9b0a91a0c26b9d1692f2ee749dcd25612226c5673c78d690a04a3af4d8959c38e205ea1c54be0685935d4bc606cb33cb1f90e907b22ef2115f6606a577d7e6609e2a51a9a27959a2331face552a0635bdd07626d55b62c8db129789d50e5d3624adcba08e1b26ad8b8b46f4387dc4828f8de720d1401c2f754ea45e9e98e59d816ea33b93b56ad78f62ecdc844949b638aa39e83403e8e54f6dbe7daa5f4d353188c150eda4b8106e084154d8f794036d43f31345c7e1d59405f460f045efd88e99d962c5b12215e699c0f92a3fc26a9c04a8ab390122ac468a6aade00e1efe8710fb13179f8177b02d5c9cb6106956cdb7d2a8c07af174fe24f6441d9c4efa1f98d7bad7d3c206f86d582f819ae988917edb29446575100d90915f5cd43f6498c61df45a2c1ada548629d06f3cf9bb49d4fc1794c5c70155071acc026de88ab9d82a8ccb735e4270c269709c0453de0fe8e4780ca876412b8460ff42171989236936f4fb718613d5008df7e44f9a6b72584a21154e09e412e64c4218e52766c80ac4e39a04f854fcbb302813044f4a9c93f6dbc41a65f9af04e39674cebc23cda9185dcba29527647d24903b97db32a176724e8036d4f39079a6cb5b15ed9df6462362b94b0ddd9b89b21386a19bccce0ec47c0ccc9f62f58ba54764d9bd94bb3025700ecc6fad91c6bec4017700a0592714d08c8dba6c7aebc1167aad23fef8485712dba4ba926eed54af93eaa85f908a84691f6340cf72a77363788e935b1314d5fe823e87e9c1a80c8e691d4aa5aae5163fbc6c7bcea37b59288c76cc528df69c032545e7420a84f5599e4dbee05058ed605904e25c6b00dfad95d208def8e52d76baacd64848c96cdec469a633109ba376020739b8bbb1ca3f723df6f4e4922fc9a45d3339dcecd679f0d6aa1b493aac40691bcf6d2e3a6bc1d6f9b4b5db860f7857a0729d1524cf8119cc591fd59eef10fd2229a919bfabd680caf5764741889158903c91ebf366272f44a3d6fef86e6849e0f171289b78c9468c3d5df2703b5c637f0bbef2a097f24aa5b4f5ab5136ce9843c08996551399eb759a8936f249612b03aded3330293721a07994bd9bdac5b067357a46fb5a28bc6606e4c9ba6f28ab066e197e785ef8416208efc49872a89d5f30633ca68db0dc223062884566df8c5a6a04a4c9e5a19376a447f21d62580b37695742c280aff3692a26f2d587b06063d73df891fc813963dffea49a4db5a90a6f35b5994554fc53a74d3bbad2288a1f930e85bbfe48b0f2ccb8f955fb34a63da9c77486b3172c219d77a2c57d02bc75e0d45f43ef0682c108cd576678452d686d1fa06367b205f7c15b2e4850dde79b04cf0b3a9fb54dcf40e72211beb44a543f5c3664aeaa8147dc2217e03c674ae038d4a8f22a0e97c5ef407967e444de3361f08fc9319ac1be4380c95e744ded40f8c8da44700d9f86813ad2bdd7084e361088cee6756ae35b9d10f205ce4b17a06cb5262be08bb1070652f91b519a7d03cff4aa61f040ea37a8fbc64f4bc4c5a52e64affcdd993fed4b3134389b9916231c12202e4c0e0ffe88f88f09140e42a99b6c0c89f3c8a4dd47143ae61aa376614bf66adeb27232b01f63515b4c841189d43777333f8a1104a6e3c462a27919a73cceccc9cd271bef1761e9ba943ae9963d5047673183a0fa957c9199cb4d48eb72602ca76530511b5e177afde840ba1ad6a1719853168c538057909430189518d55a8dad35222174e119421e259ee1331640c49863cc83e14085a2ac8478a14a49ff790cfc7a382b8113fb64b16384ba721d06da22f61184b9cf4ed7e37745f9138649df108eef25f4f40a21fee276204f8a312e1da8f9d2872dd6ac3604123c209a09901e5078be04bc1042d6b5886cddc276730de0ee22ce8f353509c433c7e88a560e68aed1e891c3363cbae14bf47217aa1493c168deb4016458fca9bd59d243b9dfc80c29213bfb722cbd7e19ef7771c0d3be21d602f279536f70f5e939cafe3919f324e0a435e2e5b44118bc47c0a787c4e1538f7bb593723785185614cbc48d2461dd0ae25a8a98179d8a8723b4940e6a40a9c3bdc83064bf0b73db2fa7a1e56c878fbd3c04ac3e453d43c41d65fc307e9540674d124f6b06588e3ffe31e4d4f3540d0673dc98952baeb92bc194aacb4bdfe67a861b61e5e3da15942553d28dc4a515c8a515e753956710f135b156d118875d10deea12bf520de5e6971d2c77d1c06e9948a4f0572a85fb0e036e6d07f3e2acfe379f41df7088e6b312933ba35e8b4a62843cd29453ff3461c98072ee05f7f2943154cfd7e3eeb74848a4fa7a6701123c4e33745b94b95b1c03f9f5c02a64b9d4ebf2310c135966f0d7cd737aa210420a95f72651f8873d168c0eb2d20a5f0151b31023e69dd8936b07a65f0f17753938993de58ccc1554e57d65b49af37eaa2210f72daa25bfce3ceae7a9335fed79d3e75163de26ddde2e2fb7f1dacaabed80329546541e039036886b0ba936fdccd2b64bb401c363a0d3a4dee4b443be3a8f108ef22b5e8643250001bc99846dd261a5c73c844a3f4109da13063d96af54534c7a3075b40475ff9457a726c934ce4c8d9a7e9c7f40c631c5c369c50020d8478a3b99d67706f236e1b63d70627c23570b554631453b69ca249f91ec3d44fcfb2fd0285aea385cdceef0709c54a0823b4ce24d538f989cb0236376ff1cf446381a6b32e9efe01f99d9db1f6720b80346b99be39f99877c19df549c16daee34780e78840d2eb90c2277a519ffeb850b37518c3a8b507170c6dc44d047e923e5a592c01d1c6783f87ea129c9f81aeda0c1272fad0cee6439e16b889a03721d5a8cc7a97fc8c125b034d75c961040b03ac241a99f900e8b8fec1b43ffba0461ff9bb5ce0a3f4cc709ae56246fd84402494100d051091545c23dc5a685b5c5bd5e730230db88986a882bf9238ad597a11270541113c17925b0d3afd6ebca56072a8a220659a47c5483d5c7093a7f5ef555d61c5d68db7330ea3a278c121aaf32ac18ebb869fafe3efd58056c32df06c5a25a760b73066d291976e0dc3b04b2f5cf55fd00ee6ca34f8e112d6c5d2c9603efd7bea54cdefbd2fcd349df2b2c14631ede55e759913f5ffe0240923a8ca66d3e962d42379230421d600ae69f564175d21b27bf335c97311399be4a89d88ee8fce8403b7a012cdf97bca0477268644a3af6eeaa270d77250933afbd4558e90cb958f534d47231d520ed9566b3a07ef3f4d5b09fc4b02dc1fe1dd0ebf693ee07ac4ce8e18edb367dfb21d9ee5ab74dbba5419aade8ab4bd339b034a960dab7cefdc265c575a37e4e5b45bd6345f063ba8d1f372317ea4f9a1cd471d243502868193ce1a3492f02ebcbe0cc95e779eb3eb87e9e5e4d0541f9284a1dac1e1da55aa348fd24b7a5d0af7252222a425c1b54acddcc12dff9b492fdd37ef73d003d0ef834845f63cd500ba41d9a46fd8f52edd1e3cd32e6f039e5e9c9891271495ba161c984cd81ad520f036731ba83e3be4105dd568345514071d7ec4add8e252e4211258b0031d01610e1bf77ee19f52d65565b0ad45df739d136e7f2a37acc9a50ceb322b7af45c0b09d514ae7055cd822b0c0407533d2007c46501044ea02bd103568edca0352a2aa3b3904b7120e9967bde4b42be7987ed3c18e90a6ec9568ca5452294f3219cba15a476186322f053b95a75bbfa401233bb1d0905399402e0cb47c464470f438fc6d00469401110a683107f2f7bba0eca4610af5d4f701b2e46494d04d2b102b858654c4e7199ba8b3006fb8123562a800e80c902b975fd8264b128c5172119e8662ec1450179afe5e9fc8b13239c73221776e3a291cd786b99e27e36cea9390746da78cf8715640da3cb62dfc1071380825b2e72a7155372f8fead1d3f4fd698eb8684a1a899c0ad4562b5879f643dc0449eb0cf28412cb5619bbaf3c3ab2a1f94c00fb0f760494343ff6201ec29caa8732a43461a1d501dd81537d71960ceaf80463449282e282ce99171f0ce9eda6772fe5eee3481521e4cb680602ec8f03c12f46ee83dd578df0342fc0ca885b8d016a3377a7d0ec6c325a76b39c4eef5305091cfc4766302f366d2e0533511cd4ff7863de662452cbb1cb561af7700d4417891204304604d8da32c43cc2214d2e3ea47ec7411a6368d607e1f2f1c0b63c0e1fe3c828dce05a77a0e1afad0505214d0502871e8f99884aca5862afc5f39ebabf477124fd9f1a74ab44fcd12d9c1277965312af1ec215ebe1fa2c9e3dd2b38385a0df7fad73ae8d143a18272df288f67df3403745139b232829083a9bc9e316d34707b9a720c1512622cf961e8bd45ce569794fb081a4aa1d13f7ce7021639a9b507c029fb7fc0bba3579fcd941d20f737e938b5196cbd172d3aa0c015de7bd43c45c697bcf16adf125f22ecb933cd8c84b801b2e9d9973a00271e93fc5469f7927999174728851eead693020f469a8d011b16f0426652c1b1b3424344d49526924e73fbe02b93e7e83588fed24ea32eae500e20843b150435d9767ecc759d13d3826e53f604625bbcbede5ef9b949b0b583e450cd39c6687fa81960a7bc01306b2a2eaa3b16a1ff097e696054e247edcdc45527a9339a6d8a8373f5afee248aec8fa72582ede5b741111382c9d0450f18d533520b72d22367e193e6ae3a258087c765d3ceaa82f153b40110c020ec5f4a2d270b82418c2391319239c9943631fc66f8263eca6dd06a54f6691a1ca3be65b6d07121373441339062491cf10519ffead58909d47f2dfea57a997cc6e31b36e1231ff18750c484de7ed6a2b8fb42a8ef90fda59b405d1e020ca88c101df8699ab9549497bb96bf432bc6ba562d1ff738f2d31643a4e2f0ccda51bc6ad3024f79c342df39ba200981100b5bcbd98d84ebfc5dc519827357abbc4912cb9d8d77634d00375e08ee5c3eba32f3c62146d9b4549270f0115a454b68c386d929f80fde8721275fea18bf8c226395ff14126216162e3f14458c57a5901160f72477502878c2b7b5ed12671d14ffc8a088e4bf9befd0bbb1c9dbb6275f67a8ad0d604ef3f3c7489e8c07817cc4df98315f27668901f2d779a7c34b7ab612712291437ee4a8336107ee6cca424ca764fc25974c92ddf4e680c09fc40554657ca639830c5e6ca4c5af85e41affb9b67c43057325db0aeebf6856822c63f731afccd0ca7744b5766b1c99dc97ec629c45d2b68479852db75a35125878b12a188e31f36d266f083da1dc1ee6c23b21c45aff2de412409bbeb9e8294667cd9935420de172974660c99a7eabde36dc6dc29e03728ef0f8a9694ba2b50339205f528fa3fe3382cbea433abe34998ef70d50710ff8e24934a8c35811f3f73ec373fab472eb32bcf2cc7a501a66798226fa86859642a6d35665d0e68b99273ea80e88a09973c21062882df29b32eaf9f5ca3f2ca93c55b06ece69999e3185566e891667cb5cd3050b806ca67891c94a30cd174628a45dce7b4183ea1479bae330de56ca2735b35dacad8ec1ad20ededbee7d9aa8f192722717d90a14433ec80d33d02173cda513b8d2636a4d8342f04909345697ddfb22bf40a332dd74654ee818d840cceb400d8af430bb31a8183522e5ce4e8198d82e29b13345fd9c84bdf1e6d226254b180ae88a849a6d89e51e98391e0a32f6a26374af28c66ddc44b5a1c37fd9585d6a75675a5b4bc1c26befd6b4900d259d394e0bca5ec3b37a2b301e748c0cc1fdacbc8bf8df17b792b37dfdda8fe01ad8ca0999bb74226a3a565c98e5d0f595f0c0f842129c2bceb70f3b004836f2fb09d57d3f49c6300e3eb1b5388f8af236801049659b1dfab00c03cce03b1a715e373f58966eb2a510187baf4f051dc30e270907225433a45542590b63e27340557c292f798e0c85532b79a1c6089dfc9a37d912d2d7efeba9cd8a1304f93df0b69904fe07bac6c6ee70306865d0880357d117b624271fb18496f98b9ebcab6ca7115ae010488a3ed4453a2fc26c23c9ccdab2cdb3ce6e59038cd46b13414cbbc3403304fa281a147ebc3945fc81241f276fafe8f1f96afc074d3b09f38d90c3eeea744a90b911b39fb8bf8d7006ee47079aa31cebda10ccd78ec166cc1175632651bb55c9a2ad6e774673661029eb986115cb39a4bda9708776180efa317a928d299f91ee387e9565790b6a00430c61484b654459e120701f60a03211579dc4fbd1f2f851faefa4ae3751e0bf84bbbd4d8993e9d9a644628f3d81a9ab31de9b22215078648490e266164d11be72909f9ab345e9e5b3fd02539c648fbe618ae70b4237502200c876a275aa96c098205cb4fa84b5514be63cec66550358bb62d3bac5af7e3544edbac8d6185f40a4a5535d940339255ee3c35b7f78b68f5e0099d37e3eda13eaef17b4984f611c1836f0cbde495b8d0f22514c6da47486cfa4270472a12e3acd1e1959b3c99fe28ee7b1ef3c1968be0d5dca964331604a8510558aedf6e63f845faf8da2e0f2a44f6659be4f3685657d5fac8295a52e74c44260763b92c3486fd942bd3ee628c56717b3c096e6b6098aad14a558e70687cb37309f7a47f8bff8c202108a0bfa0abca6cc660d5d9abf21466b449288afa7049d73056371fab93e27558c095f75a040f49e187832b59a9f5300e39dc7955c72b831bfc85012ed77974f300f77ab699f8f7cbbbe7e356e5b56f393ddafa3780d83fbbff2555cef3d17377f8d3f07d3584217817955a11938f8b8ffd20440ce09e0ed746901377e53d918f185be7b9c171438c3360f925f877ffc5ab46cac5ecd3ac8e82914a5fa45e1c9e06dda09dbc1f5ce1de6f52aeb5f92b0730b4c0325c1b02df088bfa77ee55e9d078ab7324167b18f70b72fb7fa61de070fee220bdc4716b7fdc46d3d749cb525dd33f81543031118384293f142901251444e37f4bea2457dbbf916c15914fdde879035c012204ebc4b6f4e75855c42c4a0292eaecaf142a349e42f76e1d71a1487385e135b0fb94bdde05e4ef43520d746aa4f2e49a20178b2fbcdb2e174ecb433252fd8d48ae07544341899b17694e1148237aa1005494388a926bc8b0c828fcab67c5a7c361b615af2917187572b48ed8100136000c9946c2def8232ff73729137652adcdd5445b38543ccea8078d3915a567b9a39993e30ea89298fe331d6bb1f4ffba2c2b56d19b1b091f38dd98380b456e40b9acf9bbe7c57b69eb05227f67b4373f781ef0d92fbe3406194a64b018dcfbef1a2ec01781f29ddf3f6430336c6541423cd74990c76f2699f4b3cf3885753773861e002a42d0d74cf5e108d2efedcf09fc65121d095040d7d7786044c539002a4553c650916ef76dcbf2752a93a43e28400012cbd53509869e83f25fa2389b4195388612459be202525f75da4d8a5bd414e35c67a3b3f1f8a0d4e515c66ce1e4cfaba6f178379b9e695945023ada2ec81abddde807c510e3c337a0e518c97cd6c862f22f826620cd863f949d6020054c394f88e985fbc33659ea396c3d3f49acab3802d146aa79b6256e9b0b75fe1379048ce49deedbf7bc254b42fd9fdba40ea375d858c8e5cbc44d3fd404ad6a659b36c9e66a0dad757c9e4fa3bf0026ce7593bb7311ea973bcb0c23ea4566aa0be1362fc8b713d083588d3228ee5a71131bd2dcc4111a9aff1ecd96ed88b9545a6132bdbbfd23abd11d4e38058b70f503d18d53dac4ae9dfb9cf4d012553a63e81540f4dbef27451124da3e70cb585a6dbbde08f1531c78ff1faa35a9be629c665ef28bcb5dff727ee388a9f625990ac78f7de31e5dbbe708b2cce5280b23be0f36106b7e04047b7b8371e34231ed57dddd958cb37ebf4bc8ede64cd43f77533e27b0c684c94a11546246d5f3d50c1a7b33f2fbaa6b27e23d402b8559797f787e98d58807204a1ce95fc5dd5cfe92149a14e1e32a77b7ad2bdf36e763e79e327425f6a47d0d14b6ef8b11e259e379f6e7f8cc6f68583615b136784af024d815cd58615906d722260d8700781465223595bf6d269d43165a339c53865eaeba72f640c315ef758addf1d4cc4304230bd0b2e9a63ec33b11f2f5ae25c6fb217be1aeab09bf9fe3697a34756d9bae0944863ba5cbab718b7a75eff41a88ca49c2862bff2fd19b44f7963867abfeec535ff758c131a95b2cf2453a1e812f2bb5a5a53a32dc265d47542ada9e528b9f0c7926487928830777848e42ea0afa8b799bd52d008ea096f8333c08873d83ac71e22e2beaaecfc986f8b51a9ba44c4c8b883fca7f7c366a28c140e407a44c58c57e99a5c68fe8107745212882d285f0cf21c460e5988eebe3570e3e450a427f38efb8e1cb5c3f687af92591644fd66f76640d28c26d87f2ee163d6d0291c6cce42aebae2967223eb70cd4005d2dda39f960f5e6a31db929ca3e197eb04c5371f7c3ffe4a1ad5b19b7ce492c438811ae9abf330c00462c4cfcb361957de73a8a9e1c3a05ddeb4c2ee88e6320c1e3a3b9daffe79d9603b83defffb84e94d2058b1a4a431caa3be318bd2950229d4490f7dc08e841c470749144637f36fb934df21418ab621ef53a29ddd13d227245ba8b78a6998d6d321f72109fe50922e47ce260807b44294571ad21a3904da0d16cb06feea6b7f0e207b8607615038003ca34eb7135c14ea03106ffdba6ec630e73ca5a2e146175409715deac4da64d1142b3b3d02dc195f57425784b2f2792b04f6d3adf83d616612bab58b1b2b413c81e11fb0c25c47009e8407c03faf27c8a802958227cb1fe833afe625797e425238f071411bb19bbc212244dd79445f3035111ec0d2f897f4a284da63661527e7d87114f9d0829851b861d2caa55e225c4a163b913bd9bd7a021cfae16873cb22cf47f93660b2c980463817069de15e6becf3228fcae6f386081470e1ed8467319c2bf78ca2fe947dda014c185b56228f019a4107b72c721ff1daf7c13dd501bc32ebe9fc1f029dbbb441adf29fd4914b98ad84b6f5f0332aa61b769962149ea61381bbf1a082e1adabe66395994c80acdc22c54c40eae11125491f7eb0b82309e2e96ae7a8b1d306b12884c7517edf20b471b12e1e03f9e313cc14c011d067a32a2953da5b02a6ec370c39b5e93dab223a1b72b64dd93a08cb8532ff77ed028650c1c188b7f88710e230686eb3a5af354ce3971ae18aaafce001610dd737ca5161d27c38f10030093e835b9288abe07de26d7b8af3460bcd868030735e1cbb8e0ff587dc4a80973b0492c35e184d1cf7c43c845e9c8ac7db306217cde1e72fa84c76bca8c2b778c9b750641475cb4aaaa4fbda023ec8cb1045d85715cc5b0cefb4489764ad2419a33b5d3c61b0c970338f7415f0d1de2b1c800b8d716e40e0934f1c1213844be8d657b8f8c757c0b38385708bf22c1a9c4d215a89901ba26577efd0cc18c2867f5272723ca679eec58a4e2b098300d32a92171f0fc1a46e8b48bb675bf7253b2977d7da5d49e76986ca8d89a4f7d0cb204c582726dd6440ccdd3aea76830ab359a22596848fc482db8b516050db2d445d24ab991e9ab0f2d9e6f70182eceb9eac360b6b4270377ac8eb5691a446d5e6f41194ebdd964aa829c950fd333ef0da4ecaacf0597e0c30b0461ffd3eeab422b8b8ed538009c5e62fe7c5acba9b9ab16767c91e8c0b1353f0a39d49f8c71e83695ddacfb2fc95c5c6b3b96a22d99300dfd84a6c96b238b6d2bd014268170cf4c18f34d3e6d04759ebf59c09c1c712a4f093f1f9c441786963e7470030148242dc2babdae12a5f3cae393fdcf3d44185fcef878c924978570de6d7d0bccd1cee977c4ad2cf7543de52c4990c28e354eaf8207d421a409384895b39ff87a26efb2ce156d03e8d8914951cbda75530d1d6f7162cd9e26983209101ef308a94cf79dd111dae7a31d4fb3397f315f559512d35b3c0426a541d22f41bb45432e622c4b903e054e998ac0c773d7deb2ac07da78053fdcaa8eafe733c6bc4e952b7691c6e14b538f9cfcb27937b0279cd859b2934433f41c479728016dadb85ba720e307cfd12f6b4e2f93882b137ed16cb25e9258b8cfe4836f313435a6a373be781e9ab11b390198ead97c983e663094492eb3c7872950cc1c66b2f2ed4f7be73a503d2b4e1e66aaac0a24109b7d33bbbe190f36e8e331e6d78f4d2ab4ededa2f72365e72eb06ca0629505e158294f3b9c38fa5e33e94f203c632cd6a9027515b0dd3eaf06cb4066b1b15845052aa3d5c886c0d1911bdd94341ea55a8f7cdec482bbeef41e0fb7d185505fa54a201f9aa10e0fbb6f081175da07662dc836979a89183e6e1a017cbbf2b6f2cbd0b0b1d49f13237698eaa5ec0818b74898f073a594f7120a876b49a854ee234f21b76078491a745591d64ae71f6a280e125f4201d0ec485d4e3306b6b227fafb1fa4d8b5a99171fc50b6d5a42c652167d4f691b172dc27c3d2e612b3d37b595182d2ba0b4a7484b398c340458bb9dd9d2333f43b49f68e98f0e3e4c364aa31e0b482ead8f0b422e53e5ef384d2e5b0251aecf2e42f0eecf8827706535bf1d2c2ad1c4189859abf9a767cf01988102e0109be6d75317897298a62f3da8d8483d62501c2e1b0d868435695e61f92c3993cf249312bda3438e9bd024585a64621d4162e3234a75d2665ff49e3e6add0159c17d2ec33bdb17761f4c498aa2dc3f390d88e7386b551dd253a6e158ca8399149f789993c439359c8544508f88759f9f6e0a18001d509b6e00bcb8314121fd95549737736a9378bcc8648c226b25897c8130592c5856849f268118efcba78be481578baa83959883a726589b7d0e497cf004541f11401e65ef1831852e8a9ff4a032a6c966eddbab1b4e708f0271bf97de1028f25ad46730bcda765ca28b2106b2a7d76590d4c7c15a096db8b2eb5bb2341d35e375ff7d4737f1b3006024444fc4731916016759050ebab457fc8eef7ff7bb61312b59c301d7ac41f51561d28fe110f6e7f89abb5fadd2ce696fdde9028bceb9ca5c5a1063eddce0601af5a720979e19ac82732316415143a95fc6d1b28d6668c92632cfafdd25d0960ad04605fc875f8fa8359393ead3c7bd994326908e52cacf54aa32dfd3db443061d250eb39569400482fd8ad1e2c6e87389fdb195576e6932f38d6ade7591d66bfae89d11a61f4bc579eb498e418c1918745bfa9abb072eccf88e5a38bd3c622e529762d3e4833abca98b2d65f3a8378775f70566da09e28fca970afcef9d54d062c5304ea363ba5592010b34a0756c79e4fb4bf5fa9a2fcce53415dc71e4ce73312afc656b43630a736dd4d429e9033a11febe533086cb510ee9950010c0244728e3c9b51ea32091b60ab8f3dff9550b9d59c2822dfdf4a42f6de7b4b29b79449a6030a900a150a1f468f8529831aec64d8de282972d84ab303e2862b165a510d3572d84a393b208805d7adb08bf96b7e68af006cb0d97fd8b0cafe61cdf66b21e223ad433f07e9ba8ba76708cdcb4d3fcc327d9d256e1863148bbbc286a24afd4366daab306e09b638922553fa44a694894c8fc8f43ba443642ab55c1cfab592318629fd21d4726c2f63b97ded7fec6befaf75d83e1087697cb00576e3b6ef6b12c6b8217dcaf4e993663da73b3c5ccfabf9f2594fc4e8d7d8c5dc2d1b5cb7c26efa4827a5d90a0a48769acd2eb2c9899bca394e5c9b23f967835989931d09ca30cf18375cb19eb8f21b0d29a64451a306328a91e3ef64c9d96071e53b95440e3a0db5d23c71cb5df26f8cee2f9d70cd80db59b610cab284c597dcd559b6386549b964f96b44b5505f7bd66421b11b6b6224f5b5af3fb7f81c06026ab2c485188a2c3db593cc6f3ff10f90eab9a82ca7f669bf3d92f81c66c27d7c1c9f098fb73d10d24ae2c81fef3b0ce4616bc12cb555fc8964533df74c647e7b24dc77df6126b0900701de778fb9effef6794ca211aac140f18828e94888868826dd8f2e7dfe6124c2acec9bc06c8e302bc347826e7cc3529f7131f3314ae2c87f2020a4ede3d20c068a4f1828ca442d6d4b9b131755f2531f5dd23e56f695d5427d7f568691d47716122f625409a346512c064bfdcc23fddcd75ed33ef53fdaa7847eb49fc1402e2471e46bd88f88b40f5d48732216c5d991147a8a127aa2383eba64b33d7914f70de33e944235ef4e1247fe0321fd687f1f08e987e6352102c0666a3050cda7fe6220a41aec4b180379127612613358e6434f9281b56cca52aa58141f096a02a34b12474a169208d33ec234560baa97f91fd5cba8927e547f319024c23229858fb6efa961db879268c34c907eb8df8e3c2824145051c510130eb37ebcef36cc42e27df71e0642621dd9fe000d4342a3843083031c588ac56040420ee3fe86ed3bccd2bec3ac23de772fdf470e6ec39ce492c4d243144c4c5f623158c338cce4860dc7b4d730ebc84f2dee00829472739143f9946912d521e57e3e2329873596a51423a31c94322acbe7be59e494e56bd34b967368c6248e9c5a6ec3365894334da6db94e19a40d411ff88832eb86bfe1117d8b8fef31dc7e0a00b2fc0e0608c21babbe330fb171c8c31c0907dce18668ee185e8b027475cc00d3564a5cc19d7cbcd6484d30d6ec7856211c51ffc1668861789d33545189194c37bc3b836379311560c60c43c0137677737cda45dcd39e39c394e6efcf9b1bbbbbbbb632bab32e0b2d933b7a4d0af6f2e6870e70e3766198d3b3cf4bb329b2fb3496b27ff5d07078f7e591cd93fe6ecd093ff65e6452f6eac5d8fb876574f77f7e8ee45425cbb6bd22c739732d5314f77c91885c8fb17ee48ec9f750ee6ccc1a96387fb39fc9df9918d1d700a9739e77c1feea2c9f379821c99734e7c67099d829b1ce67c50841239fbd51791829c3d38a5899c7d179143ce5eea68baa10573fcd54d4c3e72d8ca5953ce888811c194b3ecde5eed7c91830678c0a20921d6a856ccc062da9b2324bbd1c61da26827cf4b71b002e34316ab41b0d1c32442461395b2030f120649990f3e786c4e891289c0004a52294c3c0e6568512d652495a154c6d2aa0b4fae7632716bee2e4a20c5a5ab12588181a8a5c971dac97ab94f2ba8ef12e538bd8298e3344fbef2f5b28b126cd1c0d3a1dc5bc620cab60b1a48a1444ca8d9947bba7422cbbf915966b5888d91e5f53580e29821b45082dd10a1d8c18c2658f7db071473f8027643434181750f4696e16576d1934f42d45dd23f4cc84eb367df4389bb69cf72f221675dad363bb1a0b84be4abbf4cc1812a650c61850da060fef58b58e29598f4c52a0a78725f70c1416f8b4b7103197558018ee33aa2308519414d8c3adc109ec444233433d4e409051639e208481fc96d2e3868c6e57233d9a004398c42371db5eac00d57158aae931dbb63c76e4a69ec7e3e757b0caaa3648cd925b667cf09950667ddd51d637b0f5cf76b4aef965fcb2876f498024e7677c7efeeeeee2902ae55689dd5cada6e9cf3d9d6e8f20bae5bd8ce9e1de377f7b74eec627f6c01275fae6acd3e1c6e7fb893af9452ba8cb30352cadb3a2f5b254e667708d2e32010eb2efbcdccb9673e1cec7f50c68cbe3277a7594e507c8aa175e6ef906ec9f0252d53ba92835784b8f2350d87d9ef0023777ffd92fb29762e73665a5a594669e65870d65d518cd6a1220e91fb7144b9f1c38e8a31fa55cd1f07fb718cb938dab87115bbf13d3029a53d3b3c4fb166a96635db866c1b11dc397138b3cc7e37c392beed577d2ba3b2f78fdd9d7ddd19c5386e70e5873e767a66ade99a0e4e1274dfaca894598d83f3a994f2c61ff15be70ca1186bd9ffe4c6ff40891b9f8873f9317cd6ea1fdcf7c720d4cfe3e6d5f9c78f609ee1da285a0e7e8c44526e96bc7d1657e4ead359a27b77848767672704b70fbfb7adb36091ab4f2b65cbe16e1ded35da4462f0032a566a8ccb11571287fc6cb635aba13475c6ca68775371a9cef3ba14a7daae2663672a0dadc96ce60d8923e6c8b1d2919a53c626d2b92f585a6f92dd10dd8fe0acbbbaef748f71b675e4c82b1c9ee763bb9a35ceb49e4fb5dac675292f73aaee76f2e33227d3d17495a6939fad996699c3d1c96f666e9583bbc1c1d2919278b6ce7cff28dd250bb86e8150c514367eb7003cd6906466a3180b4984651f611916ea60d9b37c5459be2a1b5920c356e2f48fce52760ec0cca928a5945a6b6d5118ab1cbfda23bcc8a2c4e5f8d4c6bf3a50d972fc2c892326951cffc61eb82871452ac7f7b6d67e36fe4521c7a60e3491dd5a2b03982bb67807199d61891d873f932398ad8d292e624c4a897d3a6223ade31f8ae05662b12d9a80f56bfb10486e1f5dcd4d2b77d8a7717cd222377eb83809285f3e8de3be93798ab48e2f99c14bba9dc3869a4170cde393db5ef02537930f5af2f6adfcc6fd01f07463535c5a2de5248ee3efef33643a806019894d72f418e68a1ba31c840293fdbd31ae7f04b3d4b494a397ecbf7ac10d1b8a7bff25ee9a5ac5a12be51a73a1288f41453dd5da54bfd65a3fe4d9a0b2dd70f842debe7e5995bf49a09bb2c561132b59866eaa1427c9f28c77c72804336e3863f689cbbe5b524a29a58c5c8d35d6586394517e11d3cec12cd7e0524ecf468c92f6d97dc7fb5ebbb88883ee60961b74d91e676ae61e9249f9d3c1a74eb3643dbb7b76cb4c6632935996cd0cd7c8960fb7b117391889da3bc0048a4f3f2f8ad3bd03489867606e187d082a8cbbbac1d09bdf5fe7a08f31433662ae8c6f23e67a7c0aa5907cff6c6e57d4c8c8fea57d6bf3abb3ce39ada5f4ad90dad50c71e7afa0298935cbcf9a04eb2eeb27b1396b1df934b4806c3b5b47be9d0fca973d1ccb47e67e2905c0148414333c4073300111c5b5a1250d252d917afbcc6636b339e77c8f39f6db2b1dd710f3ecd8c3874b7128434dce60411c87e53fb3e39e1c64dccf67a89143b0cb53ee6a95a371e277ebc45f6d5132c30339ec2625ae8d1b5e2f950a70e7dd4a293b32191113c10a1307a0ba4bc76164be7e7c925fdf872022bebc7188fa47ea7ffc28a1041144c058878e999c7d3893b79c7db8e5eb0d913985fdc8c1fa32f5bbaf56c9f555f55951dc306e5fbff386c8ec3da8dc2341405c161477abef44db97c433228383f525b0628a650db36ecdbdc9562b165e70b03eeb1dac5ffd48fbd089b4291389ea911c9138f52fab059957fd8f8cf640f3b7fadaab708fc4a9bf03038f13b54efd1efdf2211aa77eddc1c1f9be1c3964647672cd163b1707abadf56dadef6d71fdc3e8b18ecceffc235f46c9f342dbb1737a95524e3ab36c4a49a5ac3ea79d2d07e79452caed3a4597539b19b5e03783297fab9ba59dcadf3a4e6efc69b54cdbe6c649aef36ef3eeeaeffcb7eeffef6da94e6ddc96fa7f25ce899c2771da29abcf29a59cd99c524e97f289ad22ceb0b5e40445adb3ee9add3a339b825651edfbd32f04734eeed6545458e024c697f1cbc1049a990a9c644ea693289c48e9444a27d4099d73cec982289ccc4c054e3227d349144e3c21b8e87266b45a6de33a959674c54ab47265ba96894a490e7e63549ecf0c0d0552794ba262508c45285188c8280e5589473435599219a06cbae92e191c8ca12561782126c9170e57147f377004299245cc314879463d0a70a163ce0e8f9292b6149bbedc1f1b4797a4cef36ce7dd7bc3792b377aa909adb20793c6cc1432a0e172e997a62a27d052850b1b596ac6b832b999aa58996983a9cad1115538d0441521eeb57168a78730d9204c9511786006636031c9034a96e70358e4b095b43497179eaa3cc91f8af90053ae3fdd7542ae2e19468c0f24e181a53b27135f260c261360ecd818b0e155119830ca99053383eeae6ecdea68051953182b02232d72f693e9e805464ce8d0a146110336987e28d2c1da1c5944b93182a1951740d8641185c3053fd42859b941c506506846906443a58da1191124d5f80084cc1651344519b85928cd38a15292a962289525e972d183b744a44201944e8ba754144ec98ba7c40b84d84440d42911d583a6c511a74314ab14db86a4d42dda125484a8905da2ca0f3e24b0b2841519f0e16268092e443e5abc2cf1a5089f2c96183384cf164346443ff86c5992386dff83c14cd24de5ee220d2d425fcc4041814cd29d2c90697a8106d2908109aecddd451a5044dc2d77176944615123872ed6c8329df87097dc715766e377dca7defba6bd94c18803e8a5bb2410e8171843ff986d976cd6f399d90269d41b22b3c4ad33eed76a1cfaaa8ffa0855c8f31ba49f793eb7a580d4771b87bef7f9cc7fa1bbf9ce90e7b5f6887c59cb2d91feaf85155f9c519d45ceaec8a87f596e77d169e56aa53947c54e13471b143c6ce19aa8a22bea21851da23040c490962d6650a7405a434688034861a412b2872fe893115011539f504a69105ad5d24d54a6c8613b752942c5095550ac526998a83041e56ab999a814514ca5881db4cc47c1092e891be49b9b2989189c80f3a2559a4de9b1535ec76d9aada19991b9aa1c2b1c376c3ab861e9f81d1ee02ff7f78f0f3ef088d42c9f6b79f414c7836653a63810b84db333154296e2c0158e1b3635343415de90e5733835c5d9947c6e8408a8b2f0c0273e0d43ee7c4b098ad30a0a940d94282558501efcd60f357181a21daea0c1c61a691851a558a305a935ce489ae2660d2f86a050ba0692132a56fed6e4008b1f96bc7832c61731a2e062c150baab5b736fc64508281651300a6c4d02881568994ac8a4216b54208db29965138a932ccbb21920aecccd94040f39fe9dab9c25b143ce8260adac65469b8325a6bb26b6310bb17d03d7ee6ab0ed1230cba694598c38acfed650d17ac8b22ccb683044520dae88b28124bc6831489d08b0a840a2e004776b9068c10e52ca237106dce7fea17d7f03fa07fd950aee1724c8fdb07efd093dbad1c80ee857cf9c734abb49ab24bf29a5940bc8f4231572affc9e9e9da5091659feff02b27c2f5aac4cd13df775d755c15da9ef3fe22ed5f7ff44317470028314d12187f1a8c582786ba66753caacfa521c973598694a66373516369a5139a546633c8a4772b5e291002338e5964bd480b7a7e2900fce72f8b7dc1d37407077647f9e1dfda38dfc57d3385926674f19da5b4195a7ccc13dda7bb37597f4183f5b2ba394f2043fbc6374777737826bf9a25974f90d2342034ea69456b0289639e6216e4e99e16e9d397b882e5be62634c6a4212a08b41f660dd2289a6908fd30822e9a0d8ae0414341160fc801cacb165ec2a0c19509823574f0d61812a2b57811228c249650e3a9897a450d2a3835445085460927a2f801075fa021544150031926033862c4a658e2891c9acec87c50434a55a3082388a04fec7c2b8bb28217f0f0e0b7fe0df0841529c2a062832435a0c8928d6003501a04291d1efcd6df3f410bbc6861811754a640a33e11c68c6d4b9834a2fc4724b65412acdece39e7444229cff93593c60b1251542d371312557278bf09aeb9dede1f89f61cf614b516b35ae83a8ee39e89fcee1b56412f1747eddecca318d4f65c69938b3b5f22fd70aff9036918c862d0579ae6619e0df3d0d80ddd3329d261a4ecb91c38bc6117240e7dabe1ed91582648b40d5ffb4d7a7eb3517b24f637ed2d66b98659ee340ac5a11c6ddcf884a31707bd222eeb89eb422d04e55e85e0e27f38ad937d905bbe7c736751595954e29f1efc30243c8a9cd83206261f89cb77cc9a482410025ad094148349cc9af8c8f65afc52b9e58bf523df3110527c20895bf60fc7c94c82606b29066d1f98bdcdb22ccb223824d5b0b9e2e6f48760d64de3937b36bd5ffa10f2e582c4993f52b98b70bf2371e63f10d28ffff62effc74e8b813090c4402de50c6fb4b8e5c7ca7efb0cb35a906f1f89ff862d66c271b5165161e6f94b6290bf56503f73ced96268cffc2093c9c6847b560bf6e5236043fa79ed6362711cca8a38fb4de86bff43b5fa1603d9af0fa4bdcd961bae5831c60814bf6bb3e57af69cbb3ce79b554ef6195bc0d5ff205bf96282649f893fc784a5693c40e8f7c490c45fbd642508fda6944279295a23870d26d39a5124a864ea23d3ef03441c23b875ce973f1d90fc0e378165f4332e73fe117cdc72907a19e6d991c40d5b9982556b7fee7ffc39fb13dbcb2d776531363d8b712c1fcc92df3956703f3f0adadb39e2bad533c6d7bfcbeb7845bf73322b479611fbf52b66dd9a2c2d5ead7270e5115d72d84a3df283c822a72cff88fc9f2f593a9076d0478e5f9f7e37f9674ca54f643ec5ac9eb93d21501de0bac501e9951228db23216a47bea6affac70879feb7e6e7b84bcaf0b374c9a738e6c41c75e4ce71307e7c95120c2c2c6e2f1f89f68e993813a41f7fed99c8dfec67a586edd76a1b56a547363887c9a798097da41fcaa4bec4ac888f64b056021d742b7172b4e181607422e6835eae7c6f1e0727eb899b7d679ef9d917bd4cc1752b74a8fee1dd3841981c32b96315395291e37334c789ebc9a7de011a967a4e7e4462fed9a189504f7bce6bf454d8c463b5e0bdea7fb2bfcf65a00b6e7d96fc86ad58125f07eb372c47ec7e971cde5c7fd6af47527807fadeef90fd8d2d777b4ef535ec765eae6a86a3f95ad67cb5a1721caa9d64751df771a87e94523f12d58f4af52397fa714a18ab58c9554bae1d2767a89fa47ef48478e6eacf7c7148e2d497a99fe388bb04cfe060adef55716511f952611657592d78af7a24deab52cf84bef7489864f58fc8d7fd5abf688add93cb7dcbc1c9fd77bfb0bf686fdda1a86b5a8250a02505545233c7895b13aaf9342ad7986797da86cd98bbe8539d4ffa4567064069f4af4d4c9f5ac7dfeb77a514377e68677c9252fc2597e437c929d24952915e6415f924af4828894546492d336693674cbee8d3d3139025f9d2b0388ebf96262e4e6078e9a27dc92ea39edc1b62e3810ff4893ed1a76d481b13ce39673f14dadd3db3c63d250f735a1e1ac75669250f0df64fe92054916328876a4a8250454bec2e305268667366d14a916d93e7901b39fbc8f99c33da39e7c491336c3324eac718638c31c618638c71d639c7cca1d6914ce88d4ca3b531c624219b82ce9b84fa077df90ec6aacb7f01c178982cbfddc58432284b6cedbdab1598ec4fb4e4531cf450fa78a6324a30fea24512e7c95dd91f85490a5282929d2e49a14d53a813d16793a9937c699a53d68ebe1c6847de109b9c61ce03208ee28dfc6a72170edac571a80f0cdc5012492277c97f69551cc7dfb5a38d88db3991d13b1c705ca5b97f4636ee963bef40ccb51fbfcc58ebe77dfc648ede0a137b2b4cdcd52f02d9779824f47e7646e811f975ace79efbee6b39c8fdfc4007b9cf61c6bd0e729f61eb207793b967a1fec43f0edec820a46cfecc382e7b40dcd066eeb95fa1c7411d6d5cff18c4c1c8e108ec1059c9f58d849c48581c7213e87b4e92c02149a9033ec06892529ed18abbc24825270dc5e02e2a3153a629b864fa0af09784d17825d32af251d6d58f48e7faf2bb9187e46b091d52338f2937298b5f6e70f81ec4cadf3f2213ad439ffed7afe88188439f66e1a122d30f7b442832fd10271eb50efdd41a77be90a04cc5263b147f7950e3640f058c94303e454c04e32f183a7271faf1887e5c72d77c311e14464c98d8a54e995cbf9829d5b2a74f57ab7f90ca78d44edf24874d5c90d6ae9c3a0729a516d7d0eff17be3aee956a2b86e85b528bba432b3477ee4477ee455dc8afb15776549585c8b2fb92bce6ec995ba580123771726e1d22fb7d2715dec9e9b7ee447d2332233f7dd713804a2d34e31592c6a88ca8c9a510efa9d56dc8a9596026a51183b3bdf6a28177089424b0684c6c8c10343784b60400b2b2e0e476ca1c1215689b47832438464690a33a0cc08a20a2129c61942db96338010221a89314980051351a2165a589924d0e20a0f28b028c41ec2ac76be75e394c3dfe1c8c8220c0e95034e9604597891c224450a2208b999a47822ebc8cd24c506a4fdfe8ab3f851f3a153331b64cee69c73ce39a70757eead59dd3ca77d604d0866076dd6d262d6acad1f8b7efc4c8bede0f7d1f3e7a6fb470391ce42ee11777ee49ae370e84f43cd99866d90d9e2e960c52c8a7da257275cb7a05c9e22ee8aef60e7803b3038f8a402184d5c7a3a36eec9c96915f1978f9ffe41c3049a34cfddb9ab96410ccbf5c789ad96bbc2beab8937b0e360ccf1a666b5521e4d06541447d99bc26e8ac28a2d8ae20439ec2efe956691931d66e2866076db394e6cf99ab0f668f03c310f1e6eebc40d881b72e390027185f8dd5176c4b1c58d1fb6f26c1d1a94dc905be290066f8519ce1d77ff9cebee3e8c644f6d90597ee733c7305921d43343203f3f0896a23cbfc65f0e739bdaeed7b9c8fd707b99ef87c15f77e67b6436d79da15f4dc3924c7d4ca1eb7db7baf936995a6badb5dad8d4c84c192c53f336d806996b70f7057170ceedc356221244666666e66564645432aaaff38c6a66e65bdd3838ffc6f79b6a7b990fefa652cd5cd536f3a966be1a666666becee008dcb7b9d2fb3d418a6cdbc515dc7170c7d155cd5fdd5c99fbf970f0feb6cd601aaacccac17977dc35b9577d354c21def6d3fbf91fb3102fe696bbbaf7f0fceeebcf7ecc3c9fed81b86bc349e4b49827d3b781663a79f070e78ca764c0f5fbf4d727d27876eceef86dbfc7e717de1cb5f73b5ffbdac1ae8d7b7f627772177035c4afa1ce582c16443f0829be1310a6431cd32487f1fd06274f3d462838db044740fef47800cebaab49466f9226b93d21637293181fc05d7f05052185fef19decc09a64270f439a40939c84869ae737993ba06e88b101dcc775075cd7df9e53b17ce26b5fc364f9441c81afc173fd8a8dc4f9f1230e9be469e50f89d9f200e27adf7df6f100e2f200e2cab71e0d42bc1f12f3f63d8ec304b752916b08b2f7db6fdddddd5db7b76d9bf78d6d90d9fb3a1b9f4b7d3acc108b6d347c4c6dddf6c1e847037d6df52db0d2af94d24a354dcbbe523a3dd788b86e4597d3878ffee1d5cadebcef36cee6cd05488e3e9afb362d3ec5d341708867b075fc71bce03638ef0c5626c972366897c40934b3219e2b5ffc5f5725c66ce5b0c6831cdefcc8612bcf9c6c420e7d405194e71679cebf50c420cf5f45c9f3ff03b9994040c615a4a982c113607c706c48a1b7594b3077176a082dc1c92e4e6078131729a5635f72294472e825142864dcf05f5ef96a49293f172dc1c9cf98e0223f04929bc7afaffc411f3d67927c293408b195d376e46b2a81522639287766d28cd9d4f5ba0f1f9cb199e4cd988332cfa424f9ba32b4da27b138a8543f29971c94bf7d928b83586613cb549a611c9a312fc344524ae9711511378c43b19964eb9d1f7e12986d1cd241c60d67529634324fe5677188064729984a9e1f0f1ce44fa8ce0a2ec5d5d8f80b39c3a1f649b29cfa98ad96edf7b80759aefdf63ee599bba7b294a73219999ab9297b7f4797ab00fb4554f87e5cf85e80418619400ef3782eb89ef5bec73d96fccef339d5ab986231d567efaa2c3bee72556fbfb94db571aa8faa8faaaca3891907587237f485336fbf7d13185cc0a8f16dfe857fe353e1cda9d4dfb0a909fdb5cf3c219ef76d5df6edf9bce3bad1e078c569341cad70116300825c93dd13e2ab2bb2671f9ddcf073401ed0b18f9d940f32aeb6a49edcb093413cd12537d3135c320072333d31e5e626e3d577c9e1ca762b5b7304d7ad702ae5f846a92f03aa4fb86350f9e2e9092879d3bae811d1de71e8233b8f0e6c72bfd7f1bbfee142b70ba10cb24aee6ae49e14df521cda6cba93b41687da5bdb3a6e9ffb5a061fd5b1384e7ceb09b1d8937250113b0781e8869e1495c0b82be4f12b397e0c67929391f7d1dd9fc218c62f79ce8e01124e56e4ec33196415231d03249cb0e4ccaf441972b2c446def7d872e5f768ba12b3e474a71e4c777e354ef146c6a49393939393939314e3d47a1fae0a8e139f47be26ac888e28f2bb1c6e08aa50448bfb1287fee4c349a973cfa889e5df63cb65cd8f39ec5a9f4305f7731407a2cdd13d361991461306c18d36bbf2dfe3e1da1e8772224b6c1d0c27b944ec5aa46391d8931c941fa51492314f8d1bba8b7157d44183fb3d926ecb212fc2765f19c376c51d6cdc29b31ed7f27c032041699dcea135005250e374ce7088b55c8652ae1ea713c4a509e2de08e272b95f2788bbe57edbe90471ab4e1057cbad13c4b542b823baba09b9be17f58f25f9921f027fc299c8a9a31bee7030c410c21077887d2ed5e9047155a9ef4e27884b939b88f63138587f7a444240e37a433cfb3c4b7143277222a129ee0a1d4a51fd1abdb890f7f55d8abb3a56f6f79bc02a2bc39f65f84746e681e6abbe09ac7e084fdcd089f2fc0cfb8ed3a51f950010c6f5af1f0360674ac541498fb22c6a1d599f0ef9530e35a1226da7e9867428473a44a550220e6a7e6145088abfb8583f90bb681687669466c475778cb11ccaa22f541c477e8c922f8fdd33d51bb7d18d6e74a3945afad172eeeeeebec59f1ba7699ab669b2c885a6b85771f0e893521c24a2e260ebe8862ee45086a615ebde946e6aa595fd1b1b0c7772c8d311132047207a8cdf0eda18b1f5ef5086dbb49303b6be045ba49452fe4beab3a3cfd1f7239be6b5713b6e2e89ba94810b6550393ea7434ea12cbff93487a2a6946f0e3928a1244e7ca11c632cc7289dfa294a71304ea21cff93c1ad61e62cc866cc6d1cd085ecdf59be848ad1f1cca07010a3680cca56ed3b2a47cf23962c633b0dcedebe6f0f9edc9d23f9d6d50bd9bff154e2a6984a3426ade0babbcb88424d383621ddfe303615750160433a387792bcb0e24b96fff1b37c598953c695efd92b454024022aae1436931630d97112cf1458e5242b1abc498c35140dc5ec0f5c7d8fd52fa7880cb02a29801942a5e5aefadde3dbc1faae307e7157c53ec5c8e7b56fa3225fbf773c0aa1074f80ef9df72b0e766814e4b56f23d8d76f2321af7d1ba5f0d50b91d7b013190df98abd6800df3f817cbf15c0f7773fbeffe6f8fe9ad5f7af707cffcd8defef39e1fb83f07c7f91d7f7abe0fafe235ef82eb82bb40aa8007812be3f89bbc29cd7f9fe25237c3f90083113827c0805004a094332522e91ea9b7eea1b7c9cef10840f21890796d677e8dff23b9cdfd987dddb7c87dbd77cbf7d9aef8f5ddc1586f83ff80effbfeff0e63bf8fe28c65da18e677d034d9891ef27c087c2e747311c5f8e03bcf679475ef5c918e0693e9b4f4955e157df1759fd4e111c03008243ad88cc0a87401e47919da7c14a6e911d34322da88afc8d03ac7c7010920d65be870702c0218e5ffdb8e1015672af4f8f0f42c281430f2a101cfef81e45766c911d1e3c8e223870e030c7df2882e3c60721fdc0a15191efe1b353e477f8f4d8f920240f7088e30590038737fec7af5af091b13e342aef73f8acb092ce27c7cac8e7695a90f179550b3b3cf820240f87397e001a0e570fe4b98f838910d23c09588985c97c10d209301e1c72afc13ccd0676c2cbc044e091798f050e87de6bb0af47b82038082924e175b0121bdafc082368cfb96ad5f911b0921be48390341c92f0152ba95a7dfd0858893d01872358988b84d7609a8643d55798563f08e9854323d8bb828c007b11b0923b8208dabfeabb9e6341a58343129e27888d0da24323130487aae78278dc0721918043a320afc3824d90a76181049d0f4292c1a1f727a870c83dcfdfefa68083904214c2d6e3602536059b0f4232c1070eaf8c0a2785160e09f03629b47cd8bccc0a178732af4ae15306b84270105268421882c509000e557f4b48a5707088f33c42f82024150e4ba8283c0facc41200873c6c0a0128e15529a87048f3a914f00e4148aa202414706894c20740088f14be258447eb83904ac0a1ea0390fa12feae60c2fb10826385f8102283439abf4264ee07219980432321ef63051c211fc20a38217c10920d0e65de041cdef7f1350518c2c24aac45e1831a9a2136343d1f6025774846c1062ba91987364f33e4673e25351ba95043040721850408bfef002bb11f745093cdcc7cf0e1f083ef002bb9443e1a1ccedaf3d910d667acc40e0942a219424333338466e683907a706834e43b20c21af219910e581f84347148f373e6b3afc14a6a362a802480cd46443eb0445090446c7028bf062be932de2108c9a6e6839008804323228f42013ec0a191d0c388fc8795dcbc4310d207df0721390e6dde250e6b5e7e8f1c001001fc3881e7e532c147004ae8f1ddfccea7e349f800a0f3e54498911fe1bb79113e1d8ff381217c2038ccc8f3f86ebef5e9f80f3efc7d0f2461467010d20eec01f62adf0d362ac0b33e1d05b092fb737cddafbeed717cf66f7cf46dbeee6bbeed693efb331f7d99affb6b3fd56117fa36ec501cecd73e8b87b04b71b0dfe70bf2453ed813f982fc900ff642be2044b93f850f8669183890204c9e5fe54248055064f9c92170589a41f68ff690058a8cdc117b4ca649f66f0559b8e4cea225f7df3497dc3fdf00317e47a12b9fc715195042ba7b1fd2f59c88bc20620b1ba3c549dc1004631673ce2862929558e4d44bade452cabc33e79cfd146fc26e7e8f37895e629ed96c2263c4128944f91dec70e3f748970017bd64d9fc08703b38205c3b6601b71abafddde2bac3e48672b0bf894b95a3293483be7851c222a710458501a3c94a1587e2424f5fb428c5a238458c938f9dd8534b6e2507fba34a7a42765a47c56ae2f6e3e4ee584ddcfe2cf69c724a2ed2965eb318f184926901d7b3684e1113e504c6152b72484201f3b4a4858a17794c4c17a7a42b514a8692a1088f2b2e985b3caeb8337694a9f895098ea39f0ef4eb9cb4d277a7f4e7277168e3a4f1eba7dd38eb9c9cce5a3070c192b59f5900d1a58bf267cea2537dea2b6945e2b810a5e804f238e39a80c38d4e398c354b2a23b8563a499cf97311eaabf458d07e3acdfed146eafc6c88d9e2b01dec2a965ea8703caab8f1edeaa1b4326ef4da9b9f3db63f3d06a18c32c6490217d73f8773b266985e65e1845a5ab98b7e26ffb3644d2daba9e460248104f779405d12bcb8fe9fa7ddf9c0fe4143cc12e7c448532c2331bca1e599ca4a966fc541575de124109baf7c97b775e4ec8fd2d9abae75e473734e2febb24b69adb55aabdd6debb8ee76359e97dad9c97150e2165c55d79d7d7f326c4af2de3bef957ee3ed1ba99acd2abdc65a6bad9f6219b91142207b97ac93e59185e285b46c7ce7c4a1fc96f1a1e24728b799aea892b77fa8acbd3ff7db16c31c396a1fca845a8e1fb5c8233d9e9067fb22d752a673d07ad65e01eeeaacbdf627687fb3acb6d0344e0bfb4bd6721cd43e7e3ba083da673cf2a57958d336acadb2f4887cdeb88f316e5bdc7ad3b22eb86ecd077dc997c3c30d8150098ab2c7b2ff1298ff47e988c5ebd5fd095918a5f3e7c268964d6c44e9d712477ee12a703d3753121adc969b090934f2eac6a3bb096074bf5ef367bf7c2e48c8c2ec6b5f3f210babdf5f78c38fc760f6b5cf09cf8fd08699b367f9c4a29c6189c31b283e52bff3cf85d91f217d96cf9629a64195336c6463b13cc3ac82eb2ad3395823e5ab5be270befc6c9879e296fdfe3db25ba77b9c808b38f40d6ecea00277e6568a22a50237cbad1485284710624c54589971d594fd6fdcffe61a0763962944598a20f2a297f7e85e022ebe0dac786c451b84f9907c7c1eef028da6ec9fe32f09f32e6cf9526e610c702320dd6e565a690e8c085535166396f99dc12e91efb2664fa270da73ab819320ae73536274d39ac45872da3e39467bb2e4e42e192546bb240e96e498faa4ead02f0a45a36a0f146a0c85aa1b6823ec87aa29a3662490b31a255fde38f3fd6726a3dce50dcef7997dac724cbfb427d95fbb42716c9df97966356f9a8dfd23fba6294e54bc5479ba02854546c928f9aab1985097a62f4e60bc84791203156bf2e0871af31b3ab60d65ffd84a376e64a1fe313f3e8dd8041cae14ca472e3a4683feb449f2d02eeac5419f4f1ac7719aa4bb66ccc131da357568502e353da10135a35f5b6c134243c8169bc18d1f6e313aa6eb93ec1fa740a834ed56553d2b99a21900000002c314002020140c8844027150301e1637c13d14000e88963e7c54160cc320ca410a21630c2184106008316460406686b6011f8deb80cafd2f2b5ca4e439c16314d9fc011747c2b2ba0e11fca35ab938ad4cf3ee0b0c0742f380a1701c0c66f6cfe310fd813059532e59a12640bddfdf244164b82d5fafe8c107fc84ae051ba86f952a75b692a07914447b182dc892d00caa35b8d7737ec0fc179a8651c161314d6e87e9cfa586a7dc456e650ec400dcf85b9b95ff3d62cd708eac948d3ab660be82ea529de872b823bf41e382846828dcd014e4e38d8ce16c49ecf387c5b6bcaef2f144e35dacc8bb92c68066e6a47ff8324b6b978d35cbf01605cb335b0b7538b892b0dfde4c328155fecc5971eaa20728afaf92c2710816b4068fe4c1be834eb7b90d25bc2b838b9765273362d337a011e73ee3ee91fab4f781b7ca6e04393341798b430e26c2122d9c03efd554534d9afaf03cbeb69e7aa32c6a9956f294ec83f70ae9dd3c1b57932e5d811a4155c777d05710c7e9b714eb6b0543c2dbe7ff72f36728a7db3a527c4c2b568fd4a5a60c16a4ed5d7fb21c396a8048640fa623bc9ee28bfeb7537c188204582a290cc647e6b2b6bb858d46c3c5f82528402bd08ac9ad99d7928a66fb1719597ce4a81df8de204282040805af07a62edf2d1c78a8a5c061889d0f277599dcc29618d38cdbf15587f1a0f3cfc9824c210699f8b4b9cfffa482a474840a8159e16f9172a5c4cf5e41d488d681ef8a2e062a2a7d9952f088a019118684ae1dbe3a5dd596242dde499dfbfb79bd1eb194797c80ff5d18c65c416fc61725bcb232c7b1e5032ae1d66ec70b128c6caee4d3a828bbc231c5ae243be86b7dcd881dac0efed42fc854918f1d1f7ca8ab9e4fecbf871f5e0042917dad898a224ae55dde60c890c6d60b6e57940fbc78b12d0982976d05cdd6f1c00d166c21aecb549f855ee4b11c4c30ac78762397cb8bf78c6939a20d2f9bdf87e21e6a9b7bcf0e2e7d4f2f7b7b42ae0c28e485ed97c6ad7776c3a67ff2de9ddca809324c4731d89bce4fe53129820fd309b163b3b528d64eda38a29240b2cf6fe1b011ef94256c45ddc036a2c741b53ce6cfa16cebceebf8a3374c7f853d6edfae08f1219c50be1d6f46a9c1fd87506a3080c3b113492b0b932cb10aa580da02682a69a49c7898870732c9b3805558f81a21d9910fc1d7f9a3d10875bf4f4a0a923728572042c728fcfa2ee29cfd713fdd1aafd59afe5ffcd9d37a663aafeed4769ccdb3d5592aef263d4a9a5ba5ab9129433b4391b136c65844259ef6673d6757f18132aa6eb2ab0b7581d621d42505ef612892cfa08ea20c04ad1bba1c4e7bdf58ba8e6b1f929189e0eb7baad7e5d910e767048762bab637d77935810093c2cd6e51a40cb878408297e225789bb8f9b39ed5607b8929ac2b8652360c8ad9600bdf5eda2a7507f3806d73ad0893a5372290bac027cf009d9efdcabb138e4d2ac2ce9201867ef446cabfc81912feddbadceb56c524e81855531a7a9804b8b060ea68a5f437c1ec627ea38376f607372aaaac27f5cac02df9ce068c984cbcb96bac10e341be7888d5de483dd102bd1b7b3c51201334849d28c56aa2411eb9945bc2f9e090297a5044e0ed71420aa9d487bb212029251953bfad6c12f1851a7b8a144ef48316d8d939cd8cb27f629e3f068b871f140fbafa269012b169bed6ffdf11104c5bc45b728518d8b39945e8a148d7e541896ac0a7f966c1a869b165905ab1239701319b869866450ccc882112a7860bad65e3f3376fa0b16b42d3f3f4adbfab5097cf18760e46c5b2aeabfa1afea2d09b00c634703d5917c7c48f289acb059c21eafd65b4e16fb4505c86c7b6192096cb555d145d450e2cfaba1a07479f2696cd1fc7515ab7935e7eb4690b0706bc2ddff79886c8b972cf39b03c6dccdae29214982762fb4ee50edc6f62500716688e2e07ef86222c6c4053e2b00f49d6f537b30a5a6d8fbe92a5071d34037722b52f67727f5b5036ca2e98c4cb6c5fc4e2c805ddb6752c04286aba52e9651e8305d286cd504e8be4a91bdba09f91d7f8af432c9a951055aa890084bc753185ebaf3074921bda71644eeacb9bf53622f8711ebb13c172d6ad589214e040b4cd5fc4246c64102498d734c080456740b755029dfbddcf6d2a1d66a3ffa5000b38a0c5eb5621bcad127b0e0383e8d48262e6d1923e14db32ab665014bab2f420f4c3f3b4cc60316926adc1a411edb72b2a6fc48a0cc3934ee0a38d054206b47fc2bb1a507e985b25209a4d354de42d0d18e86b4ee73678eee2f7799b72d096c153cac872391c4d2bc509d50815652a858557e4d65a42b55edb65df0dad3c865841540787474dc77ce5bb80613841ac6f81b43377db125bda46cd7e3618e099b8ee6e2e7df015bfc048db755203735a747528d771a8c8664f241fa8a0168924a987a715a799c4e671665fe1671359d3a06c1005ef497985a7dd1ca5ad5845f972381010e40dd0f493654ef41624a27a689743d936a6415f657dd7954c8eeee7046f505a4a51f1ef2b5af20812926b92a01b0a5c3c03d2384a7cf9dd31dc180e967973148f0d72f1e4804a6110400ac91d0069a91ed6c0486b4042d82338525c75a0df841764da1db4e5b86110034a74d8951091d00205c8adac323a35f7c5bac92d45ee27287c82efa8ac3cf9d1d1c4442addf8885e365d491ed59c83e9bea3d522879beb1d271665262b9574341cf5f0a0f7629e7587dc7bd09078e39061e1ca86c82e0a7f45d8d04551d963586a3b6dc666fff5c01b1facbd6a43acefd8685263667556ee0e345b23fcb72aee6db482d20e62f5d14a72e274e0c069983e0a24cd885d28ca80c6348df799edeafaac31683ce7828d3bce38a983b5c1814e54efa6f90ef72c4fa4914fdec6e4be85cc0bbb707a80f706b65e4a7bdf61c9bb35cbd8b3df628a6bed40b3deed85b6e74f7db521a75ed12e8d66dddea2f2b525fc4d60bd03eb05c4bdeb78b450538f9122175ece8455a1a00da529a79b2cc4169a3464429ef9fd1209344603ce0d03b726fca8d9ad2e5e73b54a2af52f01a5eeacffe9fe440e85825d476cc12b4dee9140a01a9303bdd98d50eaf06d965d1810a8ef48f1d5aed585a1c076c2ccafa804c66ebc5c03d94462884a4dd4fc5862d61e5fa8f442ab68fa9297518365aca2c5712bd4010eb8d58a94263c6ce76b642a5c485458167be4a7c18a52d187837d3ddc386a067fc62bc4cf88f049df985e4f5164c8642f1c7fe9f49a2d77f635ae65a7ccd1ddadbc6b345df0dbad9787ed29435a929e210e93cd9f526cc121b86d2cc6a3dd35b648e441c8044d2c6a3a0f4c46a74269f62ffa69a185d0ea6f3649cdd0c32d0e360cbfca0375b4ff4995052fd58e796688687d1958c18f8cd604333a15b929d83e6d4374aed197d445f44880743da184ca3bea89e9debd6e51cf5a83c7608ac0cb48d0872cf158b90f0a6697078884a241a9e52a56e1683034b10941074d240dfb38adc73d4dc6c7d2b64f6953dc4ab879caa93e4c937d4adbbea53d71a326f9c034edbbb482db34d9f81414ed5126924182c3b5b07991295a81dbc1381c977901917602cc126d61840f983ef3d074c649042b639f984537c30d64deff0e37b1af00de497eb7d1457728d91ab796f64bc6127ea7b01f20ec5fb9abf0cb50c00256274d65d9eed4783072b18ee4ed6116d425bb1223114d959b3ac519b59c120452e305a23103a7e1ed0ba50c94460a442306a66b6e1f5d18388d1418d3d8deee97bd9a8cf261030faeeb16bc55aaa21c66d776b0c9910ece0ae435db96c78a002f8fa23c65f89cf3796c7f7dba617de357dbfac519a8984fe850e6d548952b3f2ecea83dfc6e3b119a02bcdf787d84e565c3cdc8d4c8c5e9c5c5195582ffd770b4b57f5deee21c70d8c0898c222bace671c98e4f04ad70b150a3083d87e4b82e77de58fa394365f31f25a2e51f999465eea1862e747ee224e92a55d986f4139712aab2b64dc48a02c130b248015a027dd30fbe62972580b0a6d8401b40667e68327a84a0e0e8c9f374ae83867e021bcc3255b927d4524cf31cd98f549ae34805ecc1a8c76c1cb32c9a7b770784979383ff9add91eca753da072fc2a7a989d20f46ad9a15d51271919c31cf4ee42072d7124d02b65e04e900323a8b1a092ff82ea46f44703565596543dc4024889ffaea20acf9c404c2dc8d1612dd276b38f37c9e23cefd95aad19b51f4af100f85883f3fe7339260ba34bd35c7f3014655e6d6178c54f0acb9c8ea2813eb71391d091b57dc5370e039609e69819aae4e9009587a180b418fc9b983997ef45ed824b9811e8ab2e29a3c2c513bc501db432584bc5f00292e2b5c05c601bce72cc0219b8cd8690e4d706181169d3d2f6d48bfddf1dc0fe8155cefff2bced8482a9fbd8245ef602ac638aa30cee4f85a8169adf1fce349500277b7eda5342d750feb8c9f644ad33afd42cb73df68b89bfb8928946d36c933057069553d8f9bac04a7fed716bfb899c51dedd760d1cf8836a206c013d8707e2ca7356c968248cbaa14daa56e97f22f8f46dfbd52fc613ae9ed1077b923ddb308b91049753a0f29c55356a049939a8308ef74636effaf250b5df14896632893684fae089e7ca9c3d98288ebeaa2da876a25c9d2778f5c1e8ee62a021f73263c117f459cd67120d13fbe5eae05c5ce2c4a55dd6fea30204c661d37dac55f2eab9dae39a85144dac1f295e8c8c4c5d94cb6985d23b650dec63b43cd5da22994cf0d4d865b8e332f138482ace00ed062ac2ae541753fc2dc19610259f57e2f945a9be47bcba3efb014e3143a8fc424177e4a8c493824c048e8e7ba7f539acd6d19206939caa18f369258644c145aef2433340f726810e948e53eae8f1830feea522dc522e1f31190aa97cb82449e24c34447919c8da6aea9ae36a2ed665d6d6129f071663821ee9d21ee699a9f98dab8c530c367ab0f5d8f2bf6e0d2132c9326008398465021f29b54dc3e9909ba179ed45cca1878d411b5f67e903f59fd96cdc3f19af0eed487ed21a4b0850c2f4b4c5a414b002077f1730e5516010f12124e719dfe66e08da3640c39241e88c92eb7a080a0875385f12270d55ffcc261449e6c81f8ae0e83cf1f731f2d2b0d4547adaaf2f00ca4578bc1e4605abcd49cb1fac4a7785762a6bf478f0ea1161afc3934d3161b5c52ee2fe342c5e3a2d0e2bec0705d6cc76d760bafa32902022dd20784f7f34d37f23ed0c4c65f8f2ce91b6f2cdfd20ce7bdac9fbfd0a97339db5cb954cb92d37da588c211d4e990b1039a6d8f190676c340a893e43ce7b5a3bb5ec7044d91e6a7beed263c50650a07b76490c67994ccc5ba221858e45544315b3d6f5be2583efaf22df6ff978aaebbc87e9643c5be8358a0b0de8231e807d7526110c24c9255d612228a8666c2effbd945d550891b0a8d40e0016eee80e3a8c03abff0ce34b5e49f4b3aece14c14e5cc1c14dea12b8bfc835835e0e6b3bab129341792710a5110b101e5682ba1b92f1217df8993bed21191ad03fd3d63e59fe2be51d02b53a95e43e82182b425e32c7529e3168cc7055e82c60dfea844634eff60e45f1b6d7127cdbaa989a47edc0a7f5f1e2c7eec440ae32bd55e5c202ded5554dd85d62236420e1818cddb296be330828350becd1c85a621eae2071609fab7df6f3b2645cfd596848362ef6ed054aa194f05e9c74d5be2cc81dd1f32b9194f97511523137940bfbb11d0504da29f4fad5465e28e3913147eac0aecb9413c81a040406b1f8ad5ef2a2d50a8d9a164a2cb3241f35f7b82599fd1f995cf39d67fe30114a68d155547ab68bf8b6164e8c10e19830065c163a85527a6958657206116cef344b3ecd0942cd0f33f18bbbe3f0dab92097855a0edeb2a7d107f98f582b9c0aa8a1275cdf706cece0ad094cf79a13c71aaee10f406c7a62ae1fff716c2651def6688a53f2ca20faf6ea0476fc750a534f58a1a662905df7bd5c9b333544581b4a284127c2ad4b20ac23f209d8a4449e8f955373c6c718f63d6f398e097242263a1fa14c7f542468b16b771fcae81a62ea103cdc23337729ecf770778cd524d7a1ad6e3fa3edb8aaae77388a87a0c43321a646ad239d89dcd1c613f104d12591f1669e07b61868daef17a93a20a2dad379890b77d8c00ca6225bee289f31b8824386b4c9c18ec8276fb067368cace84d605c8b5f2be5318c8c975bce771d39525178360446ea7497d6a3b0a0ca4ef8bac50e876cc2e011a546e9854f1496d7cf8fdc7370947ff81b1783f3c767752c3f45f09bd83d1d6216cf6ec138ba7b6e1ed47608a4cb5a99f26fdef009544781d8e937c2c0ec7b514f84bc8e95f5b9b76976e936ff19d1d1533545f336d0bd6fc0b613a897366baad6841c3961da216447812b736b0e9d7fa4f12cdb4abff3e4b5b41e0f37b2b0f416f6870fa1fad8818b3d0e82bf24dbe37b3e460a69a4c1121feb5f656e8005419f61b683042adad4131230253803c7182b442d7b5caef72c4b2e3213de4b7da704f50165ff050dbc71680c5e11b7eab82378d745cd24dd5f2279fdbe68e2ad0df3cdf3545b81f703f785666f9b69465c953b9f6da76c53bfbfd1db9f93b07c2d16eac37f11b4cc0385efc41d6ffec8528f991d35c4af2feb84b7f3abad14ba9c425958890ce5144ff07454dcc771d8683df02f468957b41ffa8b47a67ada5a72843fd83e275879ae58593b869923de235274a30c23959faf07343bb096b722d71b9a7c7c9b5a4f4924b8e73d5f1089bd18c6f7876e84f9e2b8f4d5968b713f728e8d7109ce74e52814cfb0fa73fbe2c92c57f153ba9310727710bfa9b8aabec793f4431746bf4b3c1de9048905b837cacd37a53b119704dcf840bd864953a0b72c850075694a8c56e211212223da7b2f34e0b78b2621f65513accafa5cc425f12767478a06896d6471648d3dce6c496489aac784cb6441aedaa398b47368a898b15c3f703e631b8e6370a9aeecb4890240f92c3e708026f0170709e9f77c8dfd2ebd2e9eefa6272b42046966dea5c9f3959888ad832e018b31ed0cca6c57fafbcfd206348a72fe35ad39a595cabd45b7b8cf10b1e0f7c16d200ed042b8bd915377cfabd641a89b46822846bf77482fef009736615913a7955741c61cce24c8d297d4c1af13eb01a18799120391e3162f0a6344aea9e54cef15050f8bb83b3d4234c0285018f125579905895abdfd87984fb541b5d21b5aa96c642578e599ae4450485aa3ec39513b5b8ce730bc3abb5487d4ba4eeed88736d69c5a2bed59368da7a825ae579cc4711aa26113939c5905d2363258503c6b5166ea326742f119b6f58a1855697009bfb8c6d48ef844f7096898d0bbf9fddc46b3a09aa9d4747437a1fed9f8d147ed0b209c6981974686e173491c57b07a884d57104258ee06806a7a27068286cef87701b6e7367039c45d021fe8e0fdfa9e5b594c6654be05d7c96e93876c383a3162b6a1846fe7e184c85cf1197e77b930b21cf93b796a2dd53c24c21384023271b0d4f5c9a2a1ba8280a15df9f40c9b653d189e3e9e8bfd1424cb6fa29013871f48064eebdfa1531b5229e422f11ecee3c5c60ab0bb226236a6151b32e6f4ab9918fa16b3de19dffd7d7cb6b85448248901dc44c94526c4ba879d99c9763b68ef407afc615c61253b110b2f0bb0e50d7bd02926765246a8ea282026f2e4d0bdf2f2456362cf0b859a20ecae53a17a905a239b26cb505440af683ace6b6f138e8ce447fde12dc702ac0e95c476c9313be4ded43e9e72a71e48b0224e5fd914893a0bca02c040d53bb3c5b504c4001716261a30e6f200a2eccea93eff9f4c245fcaf7f840a767ffbcd5904e18ec0b2c8ef03843f492bcd2c68ee83742d4fa8d9129617422454a294fec3ba38730fe4423a94c1330d0cdc1419294c0866561b294bcc5870ab1afcca990d26a9f1e9a1da36ec556dec945782c92d7c708303175b858b44db2b530a107456ea60040d905e4d94511a220c5e46b6749f18d797b3d0c64511e618fe1525a0510fd56e7cd57e3f7c8b26a6dda68941ef03e0bdbcad0d60bb4c80fc24ffc54e892401665fe19ecd9128d1d6af3aee77a52af51ba4cbc3eb03c25d1fc73ae20478c6a7760100da002bb055de2057cf0538ff5e2c19999c207296ac88c82648fa405515ea3a57f32761f84de7e59e9031d81996c3d55b8ad8a0b841b68da03f9bf7421d359762eab36db4e7dc4d9e77c12d81a91a2d1b8095385d0ad784001a04f3e958115089a95c6bb33b5ac3a0ce039634be223ac473392772a44cad698d2199fa53b5a4962529904342acc98fc0eef25e24463874f17a6e4573375e67a99c921f12ebe296920a608c2837691ae4d1876febcf0eaa740bfac5fd2d789a97d6a9c68a402a36dc17fb30a4b05a22464d08a25b84bc8e4ac9024827e965be8dc178f0d8b7b0fa59921f8e1660fda1f9efebf29d81c889e2091468396ee909046f78c45d7c1c24bfe6acdfe47c7a702101de8a5ed3fa96c0514ce16cfb4f4924c3902deaca64c026a9864e7fa66f140b960299156ce4619b837447be72a68c19f7d6f8dd29958cc3c8366c5c343b6ff38be69b841f77a4f9b5901e90c56743a2cccbcb39a8d920eca1225e846e613bdcadf99cfba65f0fb87adad9515de8d96c483f2c5ecb143fb12d179461ebee56683070b72c16feea4e3a6130e040f145dcd6899a8e61be7ace84bf405e03a5549f3724dccd089e6892962e89d326976231cd34ef7e33c83f46a7453f4c2cba65b2859662e62201a2c52c0cf028af03985ec5e15f17dd505c04dd143512c83d88e0d6d21cae007c2057bcbb06e61836da1a7d28ae2f67433b7809ae0c2dc73eaa9b38dee20382bdb48f821d36c2e8950b019ef73581e805c5405315585ac32ab601fdc20bcea04136bf71b1d94bf61c42a0e14fc00c38fddeadaa8d65902a28be57c50fa99d62dcd814c524498017d2919d52ee4e82ae0dcdd054efb37a86409ee5d7d296f65ed51ae1faa6c252da7c6171f44ddcc65647b6e0244e1a3ee0a3cbce9e0e1ef9e8d06f93fe12b5a730f0c0c168eb9461e173465c082e314e3210beac0c188914dd506cc0eae22800c5e68ab0a8bd38bfe803b291e4131f4e05ff0988e01390017a77e7354aa3a0ab61c0369be56c8efb86e64997b5048705e69bbb92513694b8484d067bf621039dfd350ee35209e475d4cd869f33ba65c014a796b6ae4890b4be2e7208f3a1232e5007cbcdad25669f5e9d2d03086a09e533400402e44912c9376a28992d68abea97186299160418e6dd714a3843e3881770e9c81c5536ccafd4a333acfdbbfb54615bed7d1f8044e26dfa25c50b5712223b27410182de62d3c4075e0aa0737f642b29127de086a0c25bf3294877a21c4d0f45686ee4d9481f2af7b36ce34590c9e1e6274a580f0bbc2f4d67c7e57db3286fe6edc1bd130f16a0f199a466f0efd779a60fa82cd2fb4cf5b6cf981a2a10e016ec03d67ca758c81f87f4e4683c4377e6bbd005b50512c3d36be6e3edae099228affe2fd205ab2c3ace097125374d9b26eec49ad68e948720e2399c48331adc0feae28bfd4020b4ec78db14126d248caec2ba9dc93c033e56bd95a2d632468b1d92a76ebcab8b1f8a92a9e375f9f7e43d40979ad0757478ac1d0d6def5a91cc08de37db330cf24e725e4e189b03e9fff9007297aa975b8d1389ef8ee0ab2daf2d524c3a104fe9bfcca2f88776b8b9e4aa59a28bb0c1c79b0bc837bb2dc4b66121facddcbc502e93b392c603710bb64c274179c5607765e20f9a27f3ff44d982351ae9f1944c1785b5d44ee46e917c55683f1ef781ed786139b2f2edba6ea9843c4d033d12de250a7abee201f8cf3dd364b8c9541a5d2494d8a1bbc6a591498b02b43bece59465d0873e8a64ed8a24d0448905559c0d3cbf31d2b292e8179f054ff5e175ca54358876b7abb125f4997a59cea9a009ce38cea75420f2aa37ef9b4fc1d3ca9492e3f551e744d73b874f6327e6ce0e3af5aed43f2a0a65cdb0856ed15d99160f9a8723f7dc4f9335578bbde0311bc48782d4d26fd6b692d1dd01c2490d193645f3516ac92ad6025a0e83361eb70c9ab368aebae20abadc05de21141b8fbae547953f2ecef6186401adba0766708302a08e7c4d24ef021b65f38583d2fc94d7573956f4d9e6486c8cfa68b9903d062bbde16c2feb5cddd39184855a0bca5bd274664d7e0b1b227a2ee9dee68e5246eb1066494cb4250054f59f81356e8ede2f5ca8bebdf1cce52710c5abe86c950c7f1a6e286fcef92b78617ea75f0631735274d7eb83ede365002b0b92f2de1a055e1ee7fe750b368eaa3fafae05355d525e1ebbca35b22e4a90a8f41e155d5a7d7f45c98b9ef5282b69b9797891445377c2d35f9556d2c2e2784586b8ec26e1be7085397690f856040facac1b6c28ebe75879fe8a1b539c6061ae413f30f5ae4538dbf56f6baeb4bd99c17b5810aed5c8ff97bdadae1a3917e14099475866634d82a360f6202efd24a9565b14b09704f492b318f9cdfea8bface30ae10e7c11fa8b49c71c87b9bc8e57433688974b70cdc9482c5dcc2609384995fd62102cffa4497f37f6b0b10f6f64fc86da61f5c3240fbbdae361d29b99eefa26647cee1906e8be5db03788de527bdafb5d6e20f6f4d3a0849c2319b55fcac00fe87dc481a9d4d77e9049f143351e5851a3d2650285f704d70319b223cb6ae5b20bc3342aeea4be93996ec5f9e069d36eb57fd4857bfb586a35763059a47da181890da13c82b66d68dcfe7ff4c9e3229bd63bfa509d028eacf99fb94faae9b1295bd101c111254b3ff39ba81c4c032a60e95c59bcaa23e929f5bcac4352f618cf3efe9dc96f70406cff4298cc862dfc54acd98197d4c0ded317fcd694f184957b43b48ca76889cceeafbf328a4c49ec78c2a414d8f6bd8fad63beafa951f539401e658b3bb8f0e74e9a1eecf29ec4c47548b4d9dfc4b56aa3050b9fa12112407f018e211479087f0c4d3632b1954218d5627cccc312be8af286dc3dbea501e8f5f9edf13ef87189485b7e09b4af2f2e05f947c1e25a4937dc22289a37aa19a04ff10ef398af886b2f05c3cd4b55027b278d925a3fad635bab34caa722d9de7500502cb3affca5fd7d1341a9a325f0ffd24ad42df161a8dbc1ce029d64ddfe0963d805b210371170abbcd01f1e5acce13ad3616a6ff6a7de3f5b33a1b8f100d3c9082cfa3c89c9b2160d55c93919b2967a329c56f3b378559390e0607b71ef4a4ccb82d1b05bc197bd53c8c55dc19823cf034c6e497685620388fa402fb9209579721fc8becc8d82ee3e8f911594b2ce26e835bea02228dd85179bccc59bb6f62075ecc98899eea35a90abc5da9baf904904539b3d7c76243ce3ac5ba6199fd34aa677b81983d8bf75ad443b20da408826e4889bdf8ab03e066ffc56a4a806a3b0d2bf33e706553455699f43b3b83c67b53ed8af4f76d4318b661705cce297b877db475eabba93ebf342978304e3767e878b0ec6784df83d326861fd7ee09f4fef2ae5dd521b00674e02cb5b209e5cec8b0ff9810a02f39553d6559550258a07ac75ab04e78356714e6a659ead2b68ff33b69e6725ec44518f420e7e16e615833518420290f7e346f7e76c5239d211fe70681d7d81426f9ba84e14ae5f0d75c5ef79af02a9135f4504d5f0ce569688b588431ee84f586f1866fa06da0008f0b74f089d4c7d49d158ffea2b2c412faf671ce9ba3165226087fe34bbe27b4c413ba304a9477ccaa3a8113587591f98a26cfb468262e362358e515e484ab336a444ef60ac42b1a98907c1ceb56c8a1f12abdd06723925ca5daa5229925110f5d17d58898b6a3f1b038e92b748bc4e6b6cb6883dd4429f29ab6452821814b90fc7917566ac2faad273e2fcc853f457429f57a23bab7c8c539e0518ada96192a5ea043978851c63d3e9a87585dcb5e4a4239d2798b7f286470905448338dd1903cd896a1559013a2dc5ff8b61ad1a0b4308569b05b48f99c731c1698372b4ea4086e70ba07a5f44e30db410fdfcf4698e110066a93c2ce9e9d2075fdd1fab25dfb019188244b0a0641a3e8d66aad2757cd92a93c50d0f9ace4c484c39a74d20e74f617d02440b1c6f10654aeb6a83b2ce1e6bcb88380c3c2f6a81b7761d07a5ad6931242f054173a10a67ded6953d24b7b4bcb12e933803aab6628ee27532730a49cccd017ce6ed0ed001e309c1941071c91ca2ce2f35da0944ab7f027a5ac14384b3aae43a0054e9c32057a3e922c5ffb0de6e856fd032fe2339b735c3b863172816cfa3e86f08de5057d49b4caf155bb6efcfd50a26bb4e56498a98d3bbbc8f7bbd366a8eaf49dbf089525efdc616cf7fa708569aa4a9b355760c5198fc7fadeeaeba1c30dc44bacea2aeba71ea216d97c8dec995ad86c91ee814a85867bd42b6b90b250b290b15164bd294c707f99f0b2930650ae8daa5dad3a73de14785f4f4149b2259c0631b25a11f683b238f9ff827c410f5f1883ef3c55fea8f0ae49d9e4cf1ac47a310f7e98d322570e2851779633ca38f338dc48b00e1d373edad5446019b716c5758c094d64c414e519888f6225707943e574687f64959fec49d50dab95d365692ddd6c76c85961d042cecd2bb658369774ea735481e8f26d598c867f8f93226321b327f7fcbd23e6909c6dba8ee079adc03a5abf9abb2fa4e302795403543e61a18c92363d53eb47de65454a0e9c90b84e1ce40feee220c3417276381f963429266f1da1d211723b30b17fa087d37af35b13814e08e645ca5e33317e445dc7a8f75fadc33b8f09d30676ec0bc5fa1e162b71480afc62d34b24f1e6f69578a233b233e63524dd0eb723eb85ca9f20ff6162f20703f5602caedf532668c2c53afab79a73ab93da7215ed144b84738d19b27e57beb642f97477a944e1e03230704e0f8eb978ab7c1052af265aedca95d153da565c904a20bc2a9b41ac668f5ce750b33e072e7c4b30ad753a352f790245ead043ba3899ebfb627054fbb3c4e785d24418ab6a37541577a4462506276683b35878d908d93e36b54298140fb5c3c3d383fa804cea811145ca9d3284b8f038a5ce01ca7888ff0c20fc344f41e46d511333871c37cec7475e37f01fa6eb30413ad719c04df1bb8cc7343c53087b027c734962de7f2ed6fac94802c77a9284c517a59ced1301e3d94feb0e00d8addbd7399f94eae49467aecdc6d2b9c489f9e2be174ec2a9aeef1aa46541c2e5ca985fe9d53fb5ccddaf61d8942e113368dbb30344bc9060cb53244e8f2f1854f0b7e55accce6c0871884aac85ca20a984b0c38e1124cf448eb5a511936c04475b6bb464624863e2a72368c268f7c01d5b1d2e0e01a115fac79f73877ca1439548448afe39a7a4e312c9f5b8005de4f8373df0771d4109856cfc4cf6e7587ae6cf8447adf594a21cd07706e8cb6f3be73606bebe041ed946a7b8c1425255357bd9042afc01259e31f1594c225d4f90d5d0c2d65dde7d88b2499653fc5aacda382217fd80d2baf1a18c50ba4a8738514dadbd452313528e08095c2401ccd33d51e81f5bb3930fd66a6c283fe8216d6669e476fd52b6a3cd205b6d67695055eb9d716d828dfddb27514dbe2bedd0fecd21f551f4411e20debdb74a577570c522161332ff80f115d1afaeef36ca9ed6bd6b5c161d7aaa075970e80b29848557056d3fd186d2ce37c97223da069077a14756d2989e7151cc4143d2b9117d0fcf25c1e1400810a4a54377505887d521e15aa5884c4ea38681c6217ef6c3ac091f2731e1a027c623806681ec50a1e35aad4638050194f33a999d138d79d4613a427e5b013a1e1093ab43c5671ffc0f67931e95f9b9147357ec29e266487b5e42571804910c50c7c41fd5c24c5069931f81a4cf9a1d732a3c601cfc6aa9698a56be036f787e4869fcda1ae25f04c166d2fd141da998d37723c05abe7f1b315bdda403b068932dc070823f7b272158eed6ba89c410ee22c243dd60d9399908e75fd0a7b483f05ea6e12998a513d3e57b7280056f1a98a92985daa1f77b760ec14b067bf910ad9667535cc0c57b6be402d9b55d905ecdb22eb7b0cf2a640e237b5af396fcd3d9c1b21c7e744d3d05bdf002089a0f6b0f7157731a1198e999cf092e7afd568ba1b05243a09c5518d61a8e2373f4853b644e8758a29abfe06595b06e5872f0450fccdcd052d9a1559d187c8440b5df06234744a5d3a4715943c74a733e88d99340ab9a774a7e7f157124f5f13ed8bdc9dd6de6ef98484876057ac221dd15b23ffb8338f1bf7be4f5a835405245c109863912a67b54c7d9f7a7e5183abcd0f2fc1466866aee8f911838a4fdc41bb3c5df16bdc44a587e8eef2be53feb77a58236010096b4b0e7b9b952d822824be471ba605ec4f914bc536ad9a9c8238839491ebe5bf42f9313f71a81ade08293f127573dbaf58e184cddfa9a4adc66c83e4c94335740b40f13352690a6ea43d9fd196c79fc28d4ec05bb25caf319d0db611825309e09ca2f8246fb1275a26fe8d8ce77099378b72616394e5333551ad39649d044785a551dcb851b3f5a23dc4962af506481c1d7b8b0012bbb82c7a47f853a7a49f34d36a14972bb94396360fffe29bf39fbe0e01f0386662fe3612133c48a2ca0aba55060b43825d53377ff0bd91097629792472db9f0fdcac1e515e9a045721531d5783abcf99ce0a585b01a010752e89896a6002f5e56c4525b7c8039e55c053f6ab257b2db89b254726b87646f539e2de632e11c7180773f8a89963a55de06f7792d3a75bbfcef8e52e91f1e5a14616e6db16e65ce0a3ab0e8dcbcf3a2dfd2f9c46c582b9823f8e53ca6e79e3ecd8bbd6fa141982d9dba3d046e12197b153d75c8d9e8f1f09d970e43fa57cc666210fd0ddd5e2cf90d2e5c07c67490f36a56c55126271b1eb8e92bff348fcd04d942c7de68b55010be4cec13ba80369105b3c5a963914f62234a8d1eacdbb8330aee36e9cc665bc949a5a69d39cec5047cdea6c5b23c79743e7dcb3f9808b4241ea14f7a001448c51c5e51f9e5abc928e35a58836bcf79787089881dfcab5a6031758efd68f76ddb2a1330449dc565e9c42ecfe22332d5c0ca1cbd0317b0971bbcc6c2b93999634b5fe97bdd1b893d3b38cd73242c462d364e23c1fa488a7b1249525ae60e870ea9e703fbbc0e001eeb5b5b24dbf3588c58743eb45a9dc1aada15dffe18a99bcd62a79901319f94e7ead2082597b27965167d7fbc73e4832c4cd920cb882abadfbc016e02d9c014092e109e4fcfbcd850a10a9d938691795fcd1b82a20df4558c0abd47bd3b63376fad2c0951b8366d05858fd0c49e85642579b0ec27591c384e3f0892e16748ddeba3eb97d40932dac22b5515978e92081f6ddd4833d20a82264bd147f415c1c2d39f8ca00d5876ea0b439f48861104a0fe8826f4b26f4069350c61ad35a99a54a74c262c2931938e8ee980cf0375836a32e862103564424755ae7ce9c06d25b36a428235440a707ce355d4c005e3772cda02097df8c6c22c4f7b0697d14674ee104714cf068c87982fd7fd8e335f755d202a42568b5d3924ead02b9fabe8b53f64d62490731fb81349394e37a26eff433d74169731b97eebfc3f3e7703d6d67de385872540aaf5bc58529ae8ac625fe9fc7e70bc3f14d149a5add23b2685a3f22503525c6e3e6f5650d40832600cb1a094e074a304ac62d9d33eb0fd24106bf7e02005c27422210d51cf2e5445df9e2d62b06af91580fe7938d02783ef6f4b72799406cea679e640a8f96b6c816519b890a18aa6eee8a404fa62a083d337b93fb011c4053fce627b895212a7b5d37bfce2104302206e87b3b0abdf47e4a9dce478cc8192077f76c84808425b8bf21b48094962c49df84b39d3fd1df0bc7d54811ccc6c0229bcb366daf840f9b4a8dd99f591bf494665024b3d8e78935db2ce8b0124e42c29e4ba75247e67128ef6251f25702d462970c1ab89dc3866861ef67d9fdcf274754cf8192efd7954e8cc54a48c268bdc5c9223c15ecfe39b5a4838ae57fa585c03bc8022a87c0d9b4ff1bfefc98445c66c0c9e87c7b72622bd953abd283214e812f6a898aed0ec677d8ec841279b9201e6b070ce2ee2da4bc525ac2d605bde8e2f9fa1bcfd78a3b449a458babab5059e341a16627ad30a88019846c7c13bfb228542ba01329571432ebb3af0d51d59196cc1c2e58a3b6084cad51405b6528ded5b5f922ef306d9173c1c7d535984f85487b3396d84f2e5014ccffb131267281e9a078a23ad80abda35a227a9486cd049991c67972470c2cd13ac6156384e8a0591bbb0841e2b8c17ceaef50501516cdbdb332b683a35ac9b765c603b8a3aea105a77c4d9fe9b186f9664c9aa3f90b3665d0d6567c68fc0e2b328f5b2c2eecd644936a469778e0a6408b1429142070bc6b2aa9cab41a873884b854a2c0f07c532de1a9137dfe361dfd95fe72ac615edfb06b03d9fdcaad117a30a9f277863c2745d2d6c2755cc8c3f59069fb0ed6abc5a5171f6fb7b154284d0f35e805209e709277cf1d08dce3232fa00f93a18343508c18b42a914e6b4e9446fdcb11639b5955f44548d463f4af6692dc4c1e3f1319df90c3690ca56f921d95c0058bb392c65b18bade3865f044d99fe76b93e438bb46327ebb73962745ac168ee0598225bfbf31f9b844ee114deba85c2693b64f741f2f4ab4e7b9a231a7f036df1684fa99b11854ab65a7f8b1d27c68ab3ead757dd7c7a0beec22ab6c525d6507e95a5b4a70c38b96acf5c47d6674c173bd65598b5c488a75354d4f239e02d677ad5d6e03005a0d8ad2ada19e71ad88a29d96c0b1a097c29bdd8c859cb3968a77034df40b6266691ac758e097e3425088d3eee0971218a1583837fa283a2b13e2630cf7a2f38326f478ffb5d4fcb4e2c5994101ffc678c88dd68e8c9cc10d77a94de2d09b74e75832cff256dc802fb75a28e532e95dea01b842069a145c603c48ccaa1508159f21a89e3387c88a625dde0a355c6411d515c57caa3bf652588a7f484715aedb0a812028e1d00590e3a690c3d4c13557d7e27be0490cf0afb90a214a4956e1b3ecc317c51e3a043fb85de41c821a53b3bed4eb6de2a61315bdd46ffa522cb674958083b8a4194dccc07720efceec93c73f87da2b22e5ac0861bb618065ce1abdcbff9ad8a8a49038965888cb5c420d330e81987ca8d3e7374c165c456d772fba10132849af596f00741e47455403695a37b984b943d36575dad22fa9cdd4a03808776604b1ae64dbb538ebbcc1e3ab07223fb2c87597d1a66ebc52f9ab5444ceb7e1720df60e04f2ae6543bb64ec6b13282abdc003c845735facabd4394bb5c9745c4691ed2500b30ff6b64dd07b1e7e9a4b80d0a52d1789e8332014fdef6866eed806218d0caec8a9ae6eca0ac134ad8b6f53a23883d722619c057c20d69e05020cac447e2ff32a7849a9811ca372a30ce566ef795ac7c13520590df9c63412d5fb3471ca3defe18344e99f6eebd16777e92b7e7403076625768be9851d5a4d858181c0a8dff246646fe034ae156044f61c215d91896f34fa0a7cdc82aa53bc43a540e8997ac72ad00055dd4c5f1783f8b33cfea4740e3ec4512b00c5cc4c3527aa89f92b358c797f11c7d294b21d94b1f7060e5fc318ee15e12eea1492c82cc541cdd469a4709d76442b85458ce0f89e92a0c9c422061c10ec6297aca9165f99e6560865503cb332b740fcc346cb0ed0d426ed393f089c2aa50f1f3c59a0f543889a1d7b966759c25b3b75084bd4584239096eb551e66d4c207e664ae946c63152e90a5d473f5ac7529842f3867c8f96a8dcbc6a6a4088470beb090a572230d765cebfde684d9ab5cde541a1f1695bd450f02d5928f12b9552987c282f83c12d2c686d1aa5266fd0e1324f97c2c43566f50ce0ca8c3178d5d8ec1f2fb93c0bc811aba529f28277d5440195749e24097c487bbd29598dd548de8eaeb9aae3cf5ae134cfc053227a8482dd458dde3d3aeb24e395ddfdbf25949a4f21128769b747f457a9bdc2a92fcd859e6130f515d0bcf7d568638651ed8eb9f4f2e550c67c05f0dab80bf963e48e9eb2259481031568ccf4f5a86c32f31eda920138bbce589bcd6fb9591ec1eaaa13773f1c5aa72a3ca462a953e453d3fa975ec926aa9e69dafa20481caf58e076142ce9bbdda21795d4c6af5b54c64e8be275628995d0e301a738a42eaf759da06dbf851e3917baf202bcb7a1cece07c26f4e095094330e74d722c988c0392ba0e974e8bad17146eca46a5c3c7ef374073d20abf434f1fd175179b3d215d285fc1b409f4e5b5546b533a96a43abc73232ab9ae4c768d0a94e350d8ad3bc049b74911cec4cfaf426d8f4a6b9593d6b3e1876bc6544acc2aae2ab486b04ac4be28e2ec90ad56abc14b32601b425d1235551c5abc89322cf105857891da5042b32e8ed1a84006515ad223f4552dc9d5a68f2ef58c8865982b785a0a391694502bad0180bcff3f5ed4e54912211a8a4eeabad881efd6d271622720452d4fdba4c4c47ecf4fc746a892745c0b07cdc515e4709ba2d173226a65124d2dc8f8d5c6d93c418468be4fb60e1d8a64cec678a4b56de075688ca042ee70cc4ac5187cd72e91aae0548f22d6a0af4fbc5a58ca001c9bb546ffc271d2a66cb3e80f328f04d77e47c9ad09550b28025132705751b4a1781358fd24c28588b38d9630ebd0e6569d76d699da2600bf81e4f25cf5e8a1d1b865b3abcbd724a859bccce97ec170241db9f128b97fdc7359f3cdebdebe2750b5b67d827d263016970aae77133b3505616d3af60ea72a97d04413f2536d231612eb7837c97aa3a62319e04ee8eb6d938dd347a283fc0f4341626a63c71c2d03ca6b05022d17ba16a7acf298365f07ba6d071da7f9ea1b267a7026026c84e553bb0c271fb3dfebbe13877e789ba81dd7211724c12f665ab005e97876b54162a23a2d3dee0d2f6fca6030a73da1aefae2e1da3a4d057ada5ec7c82eb322b061e240ada2aa42b92f167cb5bfb550af0b02aaeb832023bd921a52b8649922ac94c664d7f9483b813de1a4fda0fc0bf94d257d778bb10879eb38eba2d217ca42c4450c5b298b8fc7fc07b9a1f7fb157a5add4ab12da56757ee9242e67c404e29dce7326c7d59bda7a780961039fcd3e6bafdb44faffb2bca79d808d84c01a8ddd2992405c470ae98c2cd1cda0694d0e66eb8b8a2d05f08658f1f31a1822db29f861890620cd7671aeb115253a30ba6ca9fbb549b1ab43105a55d59c4aaf3a4e2c4e2d8b93c9ca2b79ce84060092eec4d461a9bb5d2eee4c87916817b050cb1af9c593560a55e3e899217cc3eee82cc4bf6a2f7e982a659adcca130c1a0fc303c57799a869168d069f819f4ef5bcad42a12068d0714411d191c6397deac0536d4ec7bf86529b36877bb41aea33d3e650fb38da7010bac97c6d794498141f4b587e02718497890a7b788b24b4f3d86a3989bfb419c85f5c8dbaac9d5c98975e33c4b8647ca89d47b093d083202af02279dbf713c2d82285e4c0edbfab24a4d145deb5015fbc7134ad0cf5a08b0687a5b434c9740d1936e4c00143071b3464e070c3060d1c6ad0b081430d1b3470b84143860e1b6a60c83ab4b9118a981231288f32fd58cdda9345bff7932365b44269bcf3d7407a7def657cd0b456772d70ec537a1010a964817f7f5e44b56aa1dbbfa0cde82e1731d73afebe2949a76693bd2645e377a5e841b04c83d448fe650157c048a1df6beee2628383d8c08b2e81bd734ab31551eb30058d581912ad09a4332ae4d14d4211fbe73b1d2699418f72818b123a00cd542f4e9852bcb831e00aa3051b27cbb4526b12beead53b793715cc781951b46d29d6d2cc1954582cb33b45bf735e2969dec600d2eb998eeaff590e7d32faf8d3c2d9a67358a48a70112b205ad62ca3716664c5fe21c6238a510ca651e0a915020667464cabd8093c54628843287e85cf68577a2e86e92f8d6d20f367ac2774ba047f9fe6efc0821e7f830d8ace153e36c627e9fcf3dd92785ce87466f094c80f169d7803673d51f7d2151ab2e3a8d398d6a622b97ae69aeb05ce48a7ec22b5916fc5db7149211df96e58d5c6f5f10d5a4c0faa1154004953814bc0162f9884215529bb838aba1b44546d96713f2495ddd222da26bda1ad896c970f9462c5080d57d46847fbd17281cf0f36fb1d251a05d0de33b4732424362510f1a4342ce6ff9c68dd314a65d44ac078ab87d10ad5607ff215c156eac01ab145b3f8063457b8841f91083cdcd46f2c023bd34540e859be1534b33d462f89d6b4315e86c224e290887630e9889d1dff65bf73d2cd6012edec237fdda5dd9a5fffbb750fc238973cb2c1edf262898e4017902fe0c2090c8cf9bb49ee22641e99433f351d1421d79fdd1b098fe295f17a5f8fb6e8f4aacd926935eb587f7c83c3595c71c94014086248128046fd7116e6d7dafd27e00336ad5706bcfa2533c5ac97f0b9123d08fd4e43910de6ca089834c6fcda72597b89054b0e5cb347abf6290e5f5860a19ea0a9c1a92168d18265cce1e997e7e13b9e00994f1bb975e468fbe702af435c4d3710354354abb46347362481b8c6b7b5ce292753bd5a359a5653df42602688b3ec99e4f5e72f24f1a0503dfcf5976947665989e812908cb6efc495feb1efaf19736ac9add1a2e19ddf8f36e9f8a0e5f7a73747fe878f29722c5f86094b8852050d2f1ba7ad25dcc9f74a958d7f89c6c6f88fbcb669830013dfcdfd895abe2f4ba9b5880a062cfbd6a94276eb2be64d81afb86e7136be0301aecc52502d57c97d68a1c7dbd76320ec41c80d3b241b77b59c417f190b31b6b72ce54b117f1828b825564252573f39a3f28c54b62e41ae1e652980f40a6af99f0531f6cbdab57c7e6bbf96fbeeeca596beb14d8f15731d84e257422458be2c2434c06e56fcb52e8b1e19804bb72119570afc29deac2ee074a4de71a37e1be7272e8243ab141e5e820249a5652319d00aa90ac99bd1ce15157589027cec452182650419ce5c2b99e01f6b604fcc09458a6f2a986c8d32ad69b3c45833e275826e5d389d2c250769a2bfa9cc1e4a8cad40826a5aa59020c6a9110d862eb78c670abf16eda5c971bda714a81f922b29fa8a24cb73a0be27fb12cc5442f8f516c9d3bcc91b73568f2e87ed7e5506b18c1800e6fe58eae1de022e564d9cbe25d3adfa6755a683a9219f20865014c35bae6bed2451a990cb7de2f2816ebc7477f6d2785677d552dc922aff84591631d36cf120ca245cc1ff11d81ed6d57d8ac487c78fba5096663c80ba7f7d5a3d8c6235e839b94e76c3e6062aa0d73a27bfd5df44e7f7d08d4ab587c193e74e720347af5a571fd10f024d364a48115413c25206283e5ef377855a20cf8ffa01aa0882a01853e019c5f663a3c5029dded948848398db60acf9f02fca6696788609ad96df0cee43e5c96f0a52f2b8314353240a7b09d400602b04d3f06e868155f357ee0f058b43e732632d20053d418ca2084b8433e2704e9201162be0f3517e2365e3084ca47b631ce0ef3e8e48f1da8370875c1355a3a20a798772ac908cda1a3ea5c2fa6e92ee67c9bbb616e1801ba5e9527ee396fd9fca12613f08679d42a2562e20d2843173b34d2b318e440639f6c3edfa052d01d148428430ea7fab8ca5218ae94995505364a0a00bbae083234e7954f5a64c634e4904bccbd6e449797659bdb49ffb184d076357252fff6154158deaf4ebc55de270543d1dd1257432404efc3b1aacb4bfb10c97d7406a83377658d28911c6c4f6f6af48fd87168ecc0616ed3a3cd1f1d42e9b9ad88ea7f88a8b0e489ac640b2c8977770b059174c733b4a9a316f3a576283ce6b398ddb1280032cb3b4a9d6362adcce6188b438cf079eee93fd0a460aef8ca7489d6f8d5874017a7e11fe9b250ad3044b1392b085d37468c4d418640781e57ad6a442611c3bfc28580ad6540a3676e4b4efbf7cef78b43a33ac5653bc638701f3f9215424c63ec800a5074f46840efdc0d5226a0920cb9eccef87426993b8818402379bf12ae1982d0cbcf45020e9a5a42a5c358e48c4ebc141991538201b73519510114729863c3554cb28719d9202564f2a2cee0ae17167c2d8ab0097294bce0fdb75f429367863c74af7da8e6607a7c03d36d2e561728f42143b345b969c655b906464cfee6c9c4dd9849dc51d05ca2c037b529796e2fcf7f5641f37a90651f11b84c3f930f3f80355d1b4e436c520fa85d19785b247c5615b268f51e593f504ffc21f40b1e8fa5dfa1086b267335a09cf42ae367ceddca88252a096085b5800e2b453d295d8905d4da9b52fc51456ae336df35a8e821df14eb32ab9bc6917a712c8ff809d235b4ec9b5d4a5a075728620ef57aa91e0f1a9dd2ef656fb6b6af300b7c3543ef24cd59d6b7d1478ee64d1a62f60740aa5c534f6caffdc0041ed2e71aae5a32e4dace2c87e3128114549912bf9ac4a1d775643cdd2d8509882a304796966aa78958ba106235e62c4715fd6eb6706301d5495b7eb2315b4144723c79982637098a77c96da21602b100c1b99b3fb804f07224beab1847fd36f32b30e9f8b08f3d9d80549fe2218dda3000e2d0c21e0cfab100b33bc01befa6d989a43382608d89c6e0069b61647eb985ef738848e5ad928bf750cf7016d06fdcd6aaa2816f3c5a29fd519ab31e8640e673c9982070b4957e33c7db1376334db16e1fd08b3396afda5e337c8c60a9634b38c58de06451b997fbbb6b93c6864b42869578abf27ba01f27a274515dea6f59dbbe441cfaffe91f2ed41c64de69a9cb46364a0fe58e033919376445fa500640999ace6e3d2bd0f290aec7cc949ba7b881a0a15e44c080f77463c226b8652e49ebeb9654a062ab05ad2e7577a7cc259bacef5ca5439730415bab336f4853558b293b3fbc8bd7c43b7109f20af3abaebd2d0da45779972e397ba5b0ca9bfec7ebee0b0fb0c5e438033695e4f690029c24d041f9101a3c3dcb5feb02b9ecc015c4b13ad71dae63b5614bf69ee61b2950ef1dbe0f20ba6ce7588880a0339d38fcc3d449e319249ccc7de2f4df4d0a3831817a28ad6526a9bebdc44e6e8bebef8661fc22484e51d91bb2385ac528c192ce356c43774b44acace77e6f62564ff3b15b9150b9cde87c36c00ceb45805ccfda320b72668ab4597088efdc87bb7baf4aa71947cab0846bee4614f749e1f6c220e5b6a68a7c9747b5451af74f7ce8838f025023c48cd13697e65122fbe04bed5473e06007904745ba317ec05e64cba5086284ac3faf458ac89ae41fd9482f636821b4f3082b053eba39e0a6d216acaececd6bfff041dd5826d16ee3919b9a1fb026b51b7cb25f6e234449c013da48c34b48ac55911d10c1a3b9a7c0b581252ea99629a7ff9e4cc60be2f80871c063da2b57a12631e5cb40e943aff0da999f489205fc780e60c8f9724bd21ec1a22aec7df6b508c01087af63c870496302bbaed39a1aa7a09451a168c0bcaf557e37671849afa1a36cc6589490f03300d8f0c7b185033be874193edfc193cbdc95107bb7d6d9b5ba7a4ccea7aeaa17a295551efecb4e32f7a30cee0b9dc1a3b2dfa68b7c685dfaad4b2f078a377c0faf55c553895b4c57a136f55321f414f974fead2560b2c312baa116baaa61484a4856483ce23bdcf2a91eab801dece8d4330f55e17b5d43ab85863809b841817188c24f6527c6218c0ac7d548b34c9337a7671f7f796d1f57ebf0b1d0fada7f19b4c5c59507c8dfbf83ca65eea33224e40630da157b42dd1b565c3f962489f276a5a41c46f931c8ceb984098d46457425a68c5cd05219d4a78088ea6fefcfb6defab221a89b3a8f6a48918d5aad851f9846cb9eb7b1d31f4159cb7bb13511e1173d503adc6a8e13f02c2aaf72015c6c864398db807c928eda12f9de887b75a58a543ee438cb6448ab7bfcf75534219ab6a6140c937e4142aa6ec0000e27ce8eba6ec38fefec92fa7dfbb7b8d0b43939e9e7bc51eed9e06496e1546c5bc7527946884b42468132e2e90fcf614faec5200168d0c6ddd75714389963b150174d19f6caf2e67bda707530ea86c9b10e39ed9e908b68e7c1b84465a1f82e65fb106be0a6b4898a83ad93a11510c6c73b0708c319281a9b0afbd16f47841883698494a79df141c9f2e81af1e5b42f2f37b5d5c1053812bac735d7598b486294aeee1cbf11d38a2dda30f37f8c0706db52ff71e02c9f3ce603164fbd8d6f0076b34a88df94b2ffc1e4244509b69ab6cec3ed8bdc8367c0171bcddc204dcedb7ad510c7a346abe9a3e7b5fb7b73c5a34ce79c01997de386e8aa4ff521cbee15f234c4ef6acb659f0cd555eeb5d23840347aeabcdeaea36c8c0cdc705e5266e033040a1ed571d59b15815db244e20f10f3fd1db137955d32d2d238c6c1b863a28c75c7035db2720a7ee5dcc8eb07797593b3cc50144888456f7f6f6989fd06a63e6acc0d04312c7dae1c2fe62bb62b12ba9dab2b09bdece0903ecbd2e9e23983b130a5cafdfe332488bf212233daf270e0141a966922341cc87e82af4e592312cfff432ff79f17c7ef0ebdee1fe313035e9ba76f745c7093a1ef27d3f461974ae886be551f35324e70cf35ed7ca6019bd6e3a576071afbe17a3b2faaf35cc7eccc734a144cfddba1cebfe3c035184a83b0bfa13ecca3a5aac3c959d04de09b5543d7b44ecc3da164bf44c7e3369040dcc42d7e9e0735f21fabf0e37b782c75ab92b4e360f938c79f4e750bef4a697a892cd81a01765d6e8251ff03f7c488e9c25ade6ea1ea2f730ab05188a2719d0ec1e6d8338d1367350f0ff8609acc6f8fe3e672c28a823a03ec69d834942970e26dc21c8b3053787842990b038330337fa5e22ce7c596064cd3454cdc420e7e0a15b0cc2ce4524f6f94cc47a936b398947b05743f89f6baa8f4d3086aad595f48afb8f3bcbff3168b4345229e04834e308ab7d3468e60ddf6b396e7e1e68c04b4c588d2fac9d1db30772e01a02b383c3d1aca03a112f3f13c4726d895590b009d79d61ececc403cc38bd7b488a005d27b1dadea5bd1d2411039f64895534a26e13471fd4ff5fa9c3d5c817c3f304ac19a2d4f4dd58ec5bfc299db42922955d19f1bc8abbc51dd67f151bf7f37a16a59b9279f88ca18fe80bcfbf05a31abced37f3d0a80f2bca996b21f240ebf8e08f883189ca65342360ff115e20f06ff16b1049453fdfd640b6eecfba9052789ab9a500c7121cf708b77342e2a740a6458fecc34b1dc144f0a7b310c15d37348ce35dd7fadd79c976c8b675c3a88eccbe11a7a9368f15399077a81b942b481b253b7324d6db6c4057242afb5f0c37cf2f899d392a20f13bd54d4056c47df29d0559f7e4eae533a6a35a787192db66712c2d3baccb04355a6daa421988692a878d55c363e31bdbd014fa7f61a6388ff7c394366f2c19dc403e3d7d7c9169ddc36c5d6dcc4f62cb1249fd184977aa4bfc756de8248b077a39ba41dd51643b9e60f919444dbcbad87beffca7df2782fe77b56163c38305a676d9fe317724c414506e5404db15dc1381473e7ad11f8cce6819371985c72e5c6f49282007e869e3b1e5356eebab892e85782c5654c6c2fb5cd8ddb68280944d747addbd1155af1fd953e354f9314a955e7fd5cf324681d39a5b963e6fd2cd17499bc71228680ad074f08d989e63937f387b08d3048b206917159ca71d68a63ff92fc8e2d478aa65841ce48b2471b30999f2bcfba624f6ed33a2ef18550276e36323b72427c52c6cf2373a4d358e43720009d28867c2e9d7b7032a2187d053637e9dab200f0954d3458ca2425bd59cd2969015e2e22a82f0759953ce6e118c4969a2b5c83b35ecb20d92c33ffa9b394d2f425fdf669d90d13c320ed6e712e4ab670d933cc26a91dfc2709b4eb2f8e976703453a0af924240fc526277b0317365fe3d1ff568a1d58ec013c99f87e5d5ea7e26428320354723674f27e362f96c133904a89761a8c19ad5adb9b35fb10637aa5cf126eb4298bbd28ef9c7cf05d245141e14e01e9e4d05dcbb54d94c127bafbe375f96eeae7b330f9f3d45b1fc140f8b4a5dbdb1a3c6e1da5f61d1fd4f409f78f70b5361e45732e5eb2dc80726de91970be39a4c9d0a2fbf2e21b23e51c550b4ae79844ae66766c1c402ab4aa0c18246dd1dc3de0efa1be25bc66504d32dff9f01d221dfbb2f03ba3f8daccd75e42b48f92228882d63cb3fb2ddeadb3337e462b93e183d9070a1593a7c68cccb8d530b4bb1a78b9a34c40c543da2c8f35c86d1bf5f3eab0b2e9a974d310c775f6efef1f0cba103f56ffa3f8be572835a2c72fea07af1e17add8cf9e8ec52b2eb06dffc0553cac8aa65ff61fbcd06c1aa7e47443565c784125c90754f3dd1cf18b94cdd6e622ac95036d54c590133c6704c482a6895dbce5b3089a6d81e02eb1ad1a987c44c5e08e8e31cd80f80e50334f04844c1da98d8b753b6285737c54da38a1fd1278ca44d8383e62ec480501bc15306ddc9a86ef33e4fa047c5fc83034ee5a1567dd40c653397da852db80e9734df0c912784dfe516f7ad43082b84e8d653b63d35e26b01a7190e7354014fbade4935adb24ff22b487fb394cdab9f9e82d642c448ec6865d93b4c7f8462d320f04b1a72cad011029d26d7d89126582878ee6c1577de348f772b94edf9afeeb282d091c8c5dce01ba1e7c26d0b04d90ece231f78a62da17d763cb9fad152468dcc8a5ca8057cfd6ecf08e68b5b142d059bcfb2ca9347c889d0c2a20f08cccd39aeb516d79731a4f628a2284bca93875581c0d49bd5e3a7e205341ccb8679ffdac9e3474132b62d351097e5c6ab1bf6d6f4212238d4798e166aadbb6c0391f10ddd7a228eb511adf26a2567fc6202f38065459a608bce65789f01d2a14dd3d66c14d7ef4af540a1d776aabb6a864269969b6059bfcc715748da418e955d0f975e707367d79c26886391572d3ad9c6c6269d4fd72f48bfeb2cb4d0b43d8c7a4149c4931128e4e658bd4e49553f95db6e12aff278f8d4fa7adeaced202ae1cab2961f0b6605084cd816dda4ee5771c6d6957a0ed7aeacc0a95b90f95d055bcb24c0d71dd693f661ce3492fc9c73c615a67b535f57b6d505ed8dc0e8ece8ded3236800e2553b162dad06de90db3a78c37d182232f9968676868f8a4c8d4ce3c5ae628a2c86a797feaffc3f2af0fce7bf7e2980b2ad45a83ff899f713e83d6e2d5630560103fd75eab67964a65b038b1b5a74fd6952fc34065bb133dc75f82231d4810f4e91a8e2eb7b081b48effde17dc79de67b71febef134961ffb25a88614f3ae19282c20b83f9ff224cb4dfdf9f95e2a2e6ca5eeb7c6ca2a0b64fc74684af442bcf7d6874ce8d51e3f4612f9b91abfa0aae092f8bef03b0bce633f22fe90f882ba8dabc124129f3cc4fdb11a1b5c46cb88535bf1c7b991e1f596cc188d8f0219e1770f66556e3c48a4e6f8c12f3a07fe1f02a441b240261a7292f0787208c000a1b52b4a9aa85de9ac19019bb11be9e805ebdf6b967a5ef7cc20a4098711f13723ef251237f2cc6c9746d1c6241e425076a3586568e1895f94dc2961fcd88ae4908d263210073d36ba8b5f1a09e0b48b6db1565c0371068ecd40f650db9952694d301a08f1459e5c6fee5cf8eaccdb6167939f8a580939293cf2da5707ea73e572f244b1d88f6e3a31caaa9c96ac9098102432eb2e7d46a780c629c4a08cdf770f88d7390beb803d6cf033b0da60e32a6a33b87cbc88d913ec338237caf291c128c8bfb8fe18d7168d84294ebd755ff5406916ee038d8b292724b307a21dbe7f706ff6540a072d4e248e0cc123c96b9add46fa43295f359869cb7e73d3d2c5c679ee5ddba03259e2d609a3a212fe67c0feb2103f38be9d89ee7c99693081b29366056a132d63fa515829af32e196fd87b44988764c385968093d0813132235e1e5f8aa86713a79542427764e3540f31d9bacd8bb8b76764585a7bf1ca8971ac16825bca58316130e6f9fd5f25c09064fc30b09e1c9c2b25fbec4c0a57cfedf96c787dc9e0ea52527fd6c0af59c08cdd7d907cfcd55eb67876f51af37334d1b224f2242fadc87cdeed24513e5cd15734063b5440d85a7ad23d95d796373b5d81b00aa3bc021fd3871b906880db6828bde9dd5dd92711e94f0a427010be8d4ee56029454efef13f10667ea918a102585e4ffd80cc338151574e7f2081c33efa3cd185a0b0f97006ad0db631095694988dd621ca0038a9a8c9de47ccbee50d7997ecd50104732e8b63079a2cdfab5031ebef3ccc3914d7011f0a0594cd3d605f30af7a7b17232bb765d3244edb89b128679f4e34458814d38348e2a20fecaedec04d6618788ad74026252015dadc450a205ebcd908a682b1f9dc9cab64ee33aa11cc0ffa8137a351be2dd01989a46b3ae2ef197e8cbaf3bb7de29d700bc28b817da80d55299afff36d909c9e93ddd41b10f2e543bbca872f8624d1c9a3737119b09199575f5413732e8db876346c1c5e6705deec4f4671a4816435588a3d5b54a947327c29425d099a88ac456836ca73f1c3f8fe58cc74493805f0ec531f6610d8c5d86d8b7cb14ca40880ace9779186ac533fd4ce3bab963c079c63c9cd338ec71f1e160676f98af699b52fa690639b7ceea417a192346d602029234eaa6784cbb7fbe9b6a0baaff6bcf3e2369e107a7e0a515f01caa0117867a4c1db38db9e4373918e1e5dbec4c38eab67ca8d1f7945e9d60c5b67caa2ac5045bff230442db1a9c81867ba219b5853c545556a9ab6a01c53cb7804ce54968aab6ac6483879d46513cfda1302c647f79126cbcb85d000252cf65dd4e0a915f7962e8a910bd1b4b880bfba697e3f792dee898120a5f6775e9fe581059f7ca6289031f2d62d4d89fa6b817c62e43ee11f4354834ae68d5dbb7e665851e0aad8712dbea68453da6bacf47ff38e56be3c74cd6a6753ae78d75207bf72d50856a44e356614034c8b7d587da0d7e82d34dbb39b927a782593fb7f6ae93b30fc6f59ac31a69498cab5fbe9c5d04a4febf95c6ba0675c852ef2ab6e2b5051da2caeaaa70cecaab4a9c62f1b81b9f7ebd354f02c690053baccb15d92fd3636375eea5ab4e99b013766834270b5effedc84b220639caf9f513e9a3ab8dd640d1f9e93db2ba44e02f33e7cd93b43bf9efeaf647148b6fe9973276f6e249501502769c4cca0025f6496b19dcf4fb0b0ba57742cf0f01630bc436c3c2236d01deab9ab29f834962ead8d7a4a19ee28b1208770c892b0878e753f8084fd125ba7e1672d936eb7a29ac7a6cf3e1c82836777cdd74cb915ac0fd125a143b7bef75fa2cf35474951c1ee9115a10d564c8752befd19421e138315003afaef9a92381aa3f73bfec854516fd311ee25fbc20f3b448c0562c16f9cd291534713ded166a0b688d83f11c19be1ea558b828fa511ca5dc9f59c5f2d025cae5afdfb99ad5ef54fb3c56bad6fae04c7dccd09a7f2834e3b7745ee6675c9ddf2b4a2b05d4cc6801ef000f6882c0ede2676a3cc036c3fad94c53419de784a5ca3e1dc11ddb7628bbf3dc24491f2f26d22bc262f4e5c4b008e4f28dc985c5299abc0e2f6ca2d0b0c1dbb1a4ba9c7680e6fd540a43c1ab20b94d3687e32d317f10a372be9950146e5fde19bdcd16bb31155d428264859e55efee77edbf59fa87a52e63bc7e738af4f167e22b7d3ddd25180cf579249166019326c6bd3cb5485c1ada50d0e0e4b381356c45fcf8e18eda90971d4e79051d6cb79b2a7175aa9eda1283db7349d3027720614558f0ae72db9cacecf1ba3901da224a166b8c6f984bcef8c8312adab7f35cb5f329429d1e38bedd4735203b9a82473394596875bb75d0df7b0fee2309f72793d46fab4627b7b0535e7fead3b37cf71fa81321a4f3d6bd74f7052b5cfe14af5956a80b3af23f1b5203dc8eec32b85f8750eedf01fb4ded9c6fb2c6b5bc8b8ceb5f49823c40d92b1830f61d9bc1f473325d8374b6b270da10419dcd10861847253a5a38b9199976788b8e06d1b8b585914ef37e334ddbea05e2b77334dca464a280af1ae31a3a55293ad7cb5dd90a1b02851817c32f2a595d5af0dd68d277cb82d0927901e53e196297a6b9c037efa9c7986d1aafcb166525fb94aec0cd3e80b9d5034d873482d64e78ffcc84b53e02a809231b168746067d862c99f41ef2518de479b442a21814bff60d9d49d56bea6cef6ce95704496f7689d29e56e61ab7c38f1ea020577d7c95cfcb2f3206b05839dc194b53a9f6227d4608835a27171cae57b0f84869ba5351e53b0940ea5de66308a12c4e3d949e2b390f1a664ceba555f0630d6d4cea585d6d7e294359287985d0db717391ad76e3df68fa09f71e0b54f1643ebead915956c160c22649592d0675927cb0b3b975ffa86a709081df45d16c274d47ebb6309a41e5b0a880a14d72cb688e534cc8a0f42db33bcf83b7be99df28cca1bf077921f601a184b42a52a37aad4c8979c2f3293c463a1f32b4c8da71d6b064dfd71f4659cf7472bbf5c4579625674ed51218028f9aea2abc2ed7a5ae9cbd33139ea4b8912ff640cce7b3f59c276df3c88d7d8be4d59c3ff38ce8ce7fa31c74647988bf276cc4ad465f0f2828bc89663f2e7286ce34c2974d30eb9751c9233374e92ae76a2014dcb1eab293b8c8566554bd9ef812feb126c7241749f194fad7b6b3a0dd75df534c89ab51d1ce2a81aedce2c82671d48f16c2e204abb10840da053bdf2e0cd069851166d42188829f9205b7ddc109d179e4fc463ad5da7a1087044aba25fdc73715f41a12c13039ff7712320cb93fd460255cd1418851c5110abeb2e56c38c9adc0f9d570a656c2d3f403260b696264599a19ec234ff5eed3249f89b485cdbb9cde35842c4c0d365f2559f6a62e13fabd8943ad4bfd062f73761cee515770cc238f3a83012bf9405b26abca1fc22015891ae141662de2a6d334b9e3e06c28442ceafd13f2eb7b598ee06eedfeb2593e154ae00e0eb8ad10a3f1c6531668f0f7fbcd1d7ae0812e31eaf0198976c2667c6cb7f8096c29da99a718df07b37e86dab16af4baa39b58c8e3c20dd54ee8a4edc9371ece6a3b81be366d547c39102aa81fc9481190c3a72e1ef1fe000cdd0d9c573c49eb8bd863f65afa6174a0ff57935b3d4ac50425418af48e3ec1c2df998df9f3b421676cf63554840e301125cf42fd0b22a769dfb7868b739537723b8f86ce8ba3d531554a78c869d21db89e5d5e922d6bc418400db931cf57e0038fa738f443c68d0618947bf74a3445e0f8d476d9db2c2d3ad58368648a725139b861c6a17f1c52b0fe2dbb36c0f134e4dd4885e7f7b70f5048fa93fcdf99d38f4c4cd67aefd1729d59d9b1c35df793bcf29e176105331bd8f48fd16eeba6089969ce7163f66090d0e6883a68aac1c09dc70f907232e694b6d8971e22299df6355eee76357228da3525f0a8125d8047c1c6dcc1882e4bac948b9ed041f35dc706809ec6b1d4fe8723107580ee39846af61d0743bad6d0013f79456e8a0456880b4faa99dc96f1160ed80c8983944c1c85e609d20e585cff84afecfa6c348673afb7c1c1e230d05db8d0ea9939bb66d8f590ee2bd581592a564b3258a1d0edbfd7ef2bdf5fefdd89ec6ed16d9ff1af160f9b0cf48c5b61d5fb6e88b1b6264d67c2e58ed38139a5896e0e43d29ddf436d77f51782b68cfbafcbfa941d09ca15909171a3d18aeb0b437515e8509fa50200e010f604dff297da946042c5a11ba7e28ee101dd3d8cbf80f3578e381434806075e457f836d346b24c6f0f929e90264d213e5f435b3bc242ea4667093f97f0e1654481b8c3f5d2e7412168968426ebe4ab9cc3d5c6ed09c5122a3f541c6f42222617a5a238fd1e565b1f9eaf5424e6586e2a887b37767e2862215bd852486d4c796784381417052e84c09a9bb9a8d48787663869e15017beb470a80a474a3854852b255caac291123e75e1490b87ba70a5844755f852c2a36e029cf80d4b4323f0eb6b4f386a85c24207100e8c581afc692f3445b9d1d475fb96ccf46543c700107f7365bb3e1782fd17fa15229412414029ee3274b45231792151a099d96fcc0fab001a13e0c9f4cf03ce189d43d26c111a00cc68a9fd26575c569533fa4c6f95af3b6d7a81146655f40e341f7aa51f9174652747f30294bec5b22955dc8be4e71848eb6963642d8b3e0d49eab6cd1e7e47df7cd43658cee935ee12253e9b3d08dd11455a62ee2b8379320ce5b1c9a931d70dde220974ea771efa8f26703bce46b01569b0fef5854593d5444dd4c4fc45ca2b4c83dd3d6b31b7990aeecf3b8ea28eda5bbeeba4726d5381540f3b96f1d7ba71bb840f64d7d9bafe71020a232b5d06e45002d77d2e80b384c5868d23618a6b2b6624f829322e874c87eee564bd41d198ae5d93f18b8a9d892446e739926ee067096c9fbe566f5227993e0d61389181f12a936014ad9b42236a7721cc8371b585700eea18d16cf0b6149b7a2e922926882ccacf2a2afa096563715d3cfca31d9aff49b8cabb72c4a1cf0efa820cd4bc19f85c782c5fb6e45f9cf1bafd9f3366427160822394487b70a2397c82987e2fddb4f2ae6fbd27a991d4e35c03f13c5a52bd78181c1bec843741ed14a16b3887f2fe541cd59da844915bfe5c2355750c7095e2a4e964542fa7fc94f59d581652fc8a7f264e2444c3a21600ce6b3cd1f122d3491f1374ef1a415f7a237b992fcacbdf8d6611fbb36011fcc105cac619e1c07396525b4befa466477de3fe4bed8fe68b40389b312c0775fbf8252a9dd5f53d9e726d5b2466d3800e5cbca2d1e7582a6c9c66d7eccd643207b17285c41f3955ec1b1da52ff2809a582c3385d01c6f9ee4a4e844f223dab60bd1e03d06a82105775f467c137a9f11cc0848fe91c90f78028fb9ddc16584229c75f5e5ebbe75edacfb1a9fce2340d0dc58e184cca6a15a9f89bbdd869c6679ca9a7a877eab8b09cca99a9d142f143700eca6243a776e34217437eb3450c810cdd6658ddae97fa5efaec3f5a0c30a615dbc62586daa1809529993a344ec788d934bca7cc132d5413b4b8273815ef374ce34aa670c71093ae7e099e19861671a98e23ca1da097acb526e9dc53b2ce738798697a167689614648da8dd847bd00ab37bcab6cd1544a6e2e1e9a414d77d4186e79705c71b2a4953f07349cf840af366c9cee48a429fba8f25b827ef23a4264a8f980df1a8ffec828535cd9ccc57a31554899d65332e16744ae25ecc18ec2fea1192281acf8f9b2e74a6674281e6193a2ef1117187f9478a33e1ebd22737d1bc6cb1f738e2b6da2d4438f3a7c5c8e67e4c6b1ec900d6f970165118e6c200628fe2f87f57e7c2f97d3681c1cf1e5b6ed75968ce3ad9edec0153dbf903b917c6f3dd1aeff81c77fefc04e3ab8ecc4863fdc82fa2cb82b5688290d81326ef716bced01d318a4f83498dcac0063561ddb963e4d0fe9394e69c8e283212c2b55251fb0609d10848e528562c9b87aaa2b71700db64bd09c54b1e4c6afc560cc3dcef764724bd0cd294bc5d5fce31926330900994000b7bb10c9c5819b4e414f233687b4d2dec4c0197872e99e9a420a5030fb5abbca72d368c476b0ab8c7cc3a55d54f2b333ee56d90780f6415227b0860b760c9ce60c61daefe6461b7b70b74f10add57c5183868f4b60f4b2f31bfb31cfdcff64cb0672a651fac98b0bbbd369b6a309289a3d354968dbb76a3a35bbe9ddfae82a21fd12db76e24a16ad24aeabb24f163caf5f4eddda9002e24ec3f505a18dc474a22a66028998c257715697ab78c97aa5cad70beaa715b271daa7fdb6fb7b4aa8363dd0ad7549f9456162dda5ac8f96aed8f925f2c9a8c4a27c10ef7ddc0c58397783f45575bb664e5f4dbfc7690a42d596764e985e4d580c0326ba7d1b5573c9069bbaca4611da989f96660ba00085522fdfe597c8a3ed124b65f1f535edabb99834e13547a25531ecfd08591847bf5e69519617b3f8ec344bf5fc13508984864ab95e634d81712a00cfdfdb41a27684440592f3b7b6841ce47047be9a0441564aabfff07811a6b281511b5d13713ee2019df77a3cf7473a9141077f0c9e110833253909e76dc5094b522c384a767eac9d2090fd6d48248078532efa0b01c49a6757385c0b0981de33e304927c34f5da2aeab3c78ac6b6ca59f950ff1aa068da4d1b17e37030ab2c9414fa2a4d8313bd89016734b489b48f0465fab75f4295e613f072d58d364a34d0610a1622ee25e40e85161168de6112a61f4d8d5c69d11c1a06d9ef21a54805e0df03603b38cd450dfbee60cdb3f1a5cbf4d4ee6c1548a92a3cb2e0b364292dde6051bebcec7b50ea7a339e21fa344ff54b125959062501b3a0caa534c2b35edea28e7e3a836c016fcd75d3927971d1bdb540d334e01332b9d8e0ba57ef9108aaf8d9caf196f2ec31349b50159069a15de9f165fa74237c8a68798913966c2bf35a1f8c8b951391d8846f56ec66bd087fd0d47d8aea174ed55c689accf593c0e19d6a57ea60e6723a74cc4af3f92b4c51590a8447a05c271149db0c20e22fd9598611d70fef8337821bd1bae8e184eb139936ad4fe295bfef5434bb9c1d77793270d67e295dd376aa6265b834e5eb7389bb4fcb7e3ad2b73c3c0d467a3ac86cf3fc3d7d0b0ff829d1f916c666bb6525c9ab2479c03940a8d4c5d11fe35008a40493740050fa9c1e103f59a1d1876c714576b0d9e325895abd6c16fba1c3448013f1d8144c5b730c42ade7ca7ad16210688f22d88bbf8ef4f40fb926cc7457a795cb7eb7109300550f7bea94c0354427fb4669b03aa925803bc92d4f54687ac01403d5e49dc05de362057048babe1d30ea014255ea0f0e738432d805933192d17b59dc1466b1ef1aea14b8dee76e32503a923a2fd07d6e0c3fc5c5ce1e657b76cc17ac2a0651b93f7b70bc5470cc5ab350b5f562900ff0074209d0e28351c042d81e87214cb5d1c0394d1034aae72895c3052b29a0ea5f16852a53ac96d48aaf458324b0b1aa515d5e8d1644661a93f04bfad4661c4394587d40632804c7db7331e020000c41de60883e543ee7412d4be2f7d6abe68e1d38f3fc8eb898e0d953631696def6e5bee2da54c32052f086908ca081d8c0a46a55a7974b6a66cb2a1c7c55bfeab998c3773d17321333c227f094999199aa1d2746688a6e0835b7f9c11a293869ec7853c31fbd122343bb8334213d64e67846684fc353f945d1a3e71e9cf34eba6891e86bac6b19170a6f9eb0af0ef4ad38e64b8e05ddd1b012c82b081952a9cf0f112d392b2f617d45ef457e719d8306e90e2879ea32f68082336af6469c20626bee4f14aa5147539b55a467fc73cf1e1e606d88dcb87036c1d8e3f16c9f26db86d1b4f9645793a113367c22412d7a43fedcfef518f038677b63cfc1de6accbd3e9743adda7dac693378dce39e54febb389ce46082ab83308ea3bdb7efa3f61a9bf4f71c0d9a228fcf3c33da5920374ae587beb96e338cfc39ee78df2ede93b9edc85520593a394cafaeda207baaef139e50136ffd3cb203cd9bf1d3ab2299fbe7b9e2cc3f1c722d925b53cd9432535dbfa320c47253ea95333e82cd7ef388dd2a159346da556638910c67045f88e45c41521ac12f314c73cfd98ef2f084f8ef998a7e1f44e1ef557fb4b7a545c3cc9a7396ee910b54f433ae462b56f3195dccb900e512a9e8286dd8f09ef50c574686ca61d17ebc3e0e2fca0229da3334c762be66b7357627e5ce5981ffdc84527dab88ff91ac27b0ac0af6e91b84c3814994bae32b43a403ec7e098af7131e6650ff994c76b5c5b5ce322d7929ba86e1c31d98942b7e262fd11c6b827a69f18dc75be7bcb89aa30d74526b986f021fc30f1d77c1898ee0541a6ea8d7a7905f5d25f2719f3527e07c213912b685fbf019d837e7d07f4ebf455841f2dec7fbc3954dff2557f0a678bf2b5d086f23f7c11421913d63f85de10b7b3e534db594f7e3deccbe7a89c8886864627724ff22b7ee45abc737415e2623542451bca7568dbf2a1529b081bca65ccfb15191627a24642f824326f3288bf6c96258e320a8a53a15c69d06cd5efa0118eb8346cfc7ca7434314f65feb53246ac55f1f965c51f54aae5aead2213a44a9f4f0231e7df04727b24ff1128b83b818026e1743f88d09e66870b9f8abcb8489f0633351fa33612e74aaff21edf9e8c7437b8e06d7881017eb2b913988110ac5c5a009732194d775524dfb2517eb8b104e2fdeaa4339e6c766722c2ef32b2ed677a11ac2098f36e74c0dcf2f4f5cf9344fe367de893a87a4d962b3cc95f96e1891cf7110c21d196a9f1359228a6170b17e8db7e148c8972da05efe24f2d7f7f5dd8a17758e06827ab944668a47efb97c6b84ab708a083f13d20865c224d28b0989926c442a0cbed5fa983011707b8b7ea55dabcb86dff0a1db280c9daf7da7751c0e6ed17d19caa2f9d45fac8979ccbf7f310f9e3c8bfcb55d915a340ce4e2c3c85a19417832ddb64f851d526beffc8eba8864c2643ee6bdeef432a1ecd6fc55b8d556c1f810f817548514e62668ca84a6cbc5ed4fa71accf942d775322be5f9f7bb6d3b55cac5e8886aa15b2897941116190a25efc54b649640fe1ab797402e6aaf6d7894f9be7ced99d892e7abbeb3b6f354a16b6aff20a8ed18218cebfde83f4ee5e62e132683fc67c2625e7b6f22c55fdb7dd56b4059fbe94259d33e26dce42ce8843f1c94c230780473d7b309fb92b7f7e9c2a43661cadadf2f084fbeaf62bab85d4c1da9c92c1d99617e09cdf3755279aabe880a2f9119068f92c7c546247ec145ed95c8acfdc75971b71f21e976288f66cdbe869ab4f69cdbc3f8f86bfbe6c40ff0cf869e85417d675942298fb2887ed1161916d9246baf21f1b44e6be2afd4172691a0a7755a91d7a42e8a9f87d082eebbbe7de2de47e7f0dce5ee61f0d7fcee7f74df7dd775636b91bb2fb693e11257722be5cea20452eee467bdc850baa84bad35cd5a4c975cec3c17bb6f0bd35994b892479caf38feeac01c1f1d9841f99d676fd364f92c22ba771f2e76b65bf2b5203cf983f84bc3f47df8cb7ed7fdaa6b3a0d89ae25703b4b1256b28ddc59920872dab00b936d38101172c77e84a24b5d941c0753a386ece10454be00033593f96bfbf974896271a4a3242db32d79725db24f2ce38ca6b5f782366e6d9d4960c9f3df4952d6bc2d3f5a9926b9c87dd93826cd880423eec819811c53c51c1314d989cb0f15a4ce4bd0156fe63374d4391cd5a473f80849dcf9a8a2d9f2b7f4cbf50199e426b9dfc8c8c8a8685a7b2f088ea8a22d4ebd50254ab9647feeebf139a9bec5b5bef69d731cf3f0dc54964750725efcd59c1109439763f2d7882ae2e2af11ece2af9133cafe1abd2df228cffcd5a08e223e434772a949916fe991623495803a8795cee1aece31abb5f782e0ff28725f5cf44702ef19297065f4097804a54b937cb20a325cba04724cb3e5ff43a0d00d02adbd1704ff93b27f47e337ab3c821af8155f8fbbe2b296d0bcbd4e2a6f98a905fa1b5e32ab91d996633a49a14bfef24959409c037962d48c86930a257f4b927ec9f2a91787cda2252879164dd887c55bfef4cb94f94c232679b4481329d72679b44833492927a1934bb648935a248b346176ce26ac63e08b364ffcc946f0e2dab0ade589db628c5258c7a40879ce74deba357fb638e777a8820c52d2d9dfd28682ae6b58dacfe789d1d05f7e95a93dcbdaadd65a6badb5d64debfa3d6466b9e04f7f883fc53b55b43fe11dfafe1a1e229fc33bf3e5fb966ecab26cead559fe36bc8683ee345cc6555ec47dc75fabf7f692dd3dffbcb1340dcd3b0831ef337ab364e111741b78b435f078f10882781465f0e852e1114726e5f5998747b0c3e38b9c9442ddd3cfc2a3edb781c7db5f038f603f88c7ef9fc1a3d84fa3a75fa627cb5f853b2ef6c7842e9bb309d3d1dd7fb12ccb0f8fe0f8a378c2a3ab9fc3234e082108218d9cb5eca6339ab03f97b1dc93d2d16e1cf4850994e9bf30619c73d1b0493d8c32a60c4c94e21f7b890bb79fd99a9a509e3f81ea17c159dc712bcad303ad127e14b34c5842d8b3d50a7bb6c4701576e388e15eb525730f9394b9bf5a32f729a4cc3d2ae4fe33ca9c2c737f83044ee47e04ee6bb80700f798fb30ecc9fd3365ee45087bb642087b723421f7ac1a2118f66ccd843d5b343c9f308747fbf6a70c0f8c6b5020e5fa36d5eb9c3fb355dfda10057714732dba5fbdaf47708679efebcb21493461c22244c207b8584f5f4fa8aebf16dac52640f207887b12bffa9e475dfc70bbd8758c88bfbea7f1323fda55ccab7efccc61ea2defabe7791eb636d9c3dacb1b26a1e1e821d52ae4bb3a20b05ca13e98690d4cc32cd730fb2ed249808e8d9c8e8e8d5d9edf5d808ec9157cebd68b9dc31b871c696ef58bca19abfd6c6aca1b1e2b6e79c2284fd87ec32da911d714996c45536373a3434777e2eca655776985342a325b17e6479cb961ee53a954cc4e4c77614296e32130af7a7fd57d19bcb3fafb3b310ff32fb837ddf178c495353b018b8158a7ff989075c24986c4fcea9324a063497454bfc23b3b313c3124d62ebd0ceed8eae7b384ac9e75c209e8d8ca355b138fdbd3c913ebe93ae2ca6e3f6df3979e8a1599c00b8eadbdb7730b850f7f5d98efef98d5fc2f325bfe9fb4d2a54229259443928a8b3d61a7d3cbd0e6d3490834e3ca221496444caeccfb87471b33ebf008cae018acc2b3392b65b50997cb86b39b56dde511b343ea62e3e81922ae4f952432ebcc1ff4e4daefb8438bc79adc3c342037cf57628fd4e4f97476937f1a1abbc4c4616013704201a528390c07c7cb977e3f721891d99a6fb55caf4495c89b38944a4425c9add4599e0dc36ccdb75ebee496cd25c3a4b50293b6c985922405943e471c2699883cc0b77f24d0e8bacc83666bca26a5d4b3d69b220a954cbf7e942517f060250f2077135e88b2d1f4dfb64f6747e77d280f9a306b641464146414646485666bbeb5f782a0906cbeb544266c1255a2099b48d6de6bedbd20f8a25889802811459aadf9404c40444144414441484548b402292d2d252d25310149a6f9dd655dccc3f395f94ae42f2b4df2fc5ae4af1a797e9df94bc9619d504f21c44403aa323924a12cf258b954d90bbce4b11ee5f957a6fee8bc7271a210c5bd2e7371ca7a3816ef68d0f84b83c60d479e2c3bd3083b2623f3dd382a51bd61bd5265c26ae5be7ffd4ac44afdea65bebbf7fafd917a995f85acef3c5cb9fbc7780cca8d52a111d58fa9f78709815c6c04e6efcb5049e56a85fa18bc6466954b9810097d1690fbf52b368282919175d14514a010da7aec4f9e0f94e707e5e95db62de1367371e3b275d9945cf4e2e2dc64409b11d086b4056942db2422caf367b6cb5d02917a987ec6b9e164ad68b6b4a0ca0464042403220287ae10518ffdc91329572b2ecea74b1356936ad16ccdaf4b382ebaf4207881f2dc925ae82980e8c902c83d45942b5b928bf3b5242155226b94e75ba3cf2ec5388c05d466f3e1934a5641863ba96491841daeedf1978fbfe87727cebeecc2f147b07b361ffe9adeb474d59266d536df93cf2d332f37d5ec8dc83a611e9e4f20cdfcc7c999ef23080da01a2679be384fdced2c5d86721979bee7f9c7c605fd766c5f109973a6369b434831a349ee2988c83d051143b94b75727b4bf326a56cef73fc6befeec4bdf597f75d3f1ededb16b6a6a6a6a6f0148ed762af3e0c44b1c66e5a7597526a6afe795cca1c6e6e5caeca335d3e81e3ad686a6c6e6e4e9cddb42a9fe81a4b63d191d3ad8d67baeca2dd7d864edcc61a672b6d2459868d8d28529ee9f2884e870e9806db6016c69ddedfc66138cd85ddf2fe18349ee992065dd78cb2dedc2c17ee7fdf80d4a35ebeec1cf2ded44d75ee7521cb05d57fbf80d5a34216cb05d57f3f64f5a8b033cb0598ff7e88cca31ee6bff780cca39e27b35c8079d52f0006b33c1566751ff33cb105c8609607f30bb80f8339c001d5df5f40ea655e06730066756ff73baabfdfe121a91d984f09f151e19d2a43540ff33d5b30309f80be31afc23d5b31b85d85ed550fa38209427da72fe24aa87475118ea7f32a8593a94d04c8267982942b781df5a4b5feaa1a7d1dd9e4a3cdb78d82ff9474881d42b17515fca9f50f0131b3ca94527a4555bcc4b54d4e147d2a7bc2aa6c43a80a94fe10d9c361d92d0e03b6e3c22a9452492925f2534507271b11334f8cfbaebf1ff3756453f5e19e862a3a3859c944a16ab4074ff62d4663c84062e4d7c2ccf3b7f9cddfb8b0ca12dfe69cb6a22c5ee2f4a9bb667df313348b5bd8a1f5070780d49fdd040e49f9ebc1b3fdc4a3670f3b5beb6dafbd9c8f80ba8467843ecd9d47aad9a01eb55a9dac1f599bded9b861baa36723f29bfe463f5441864b718f3975e417fbadb3f50c9d9474c3b2c55a37eadffcba6d8fa5496ad9bf73c8157ae62432dbce41ff07edd931e7a472a226ae357b2d4236752e5790525be23fbc274bac339b9ab2ff5ce25d53531e777a7ab29f50e9562595eed3718991e10303a4583b414495a4d84ef74006455d6231a4aac4da5518bd3398c861281683941440c5e08e9e8792e2d575cd18ceb273d67d3c61c9a7a37cb23f9eb46c9227d675fbd7a3be5b5b6b3d9db2d082699c11aa52b2fc1aa55fb60a758e4a6390e553a4ce315ffe14ea1cda4b1f9a2a25bbdcb64d7e3dac37c5bee58975dfc7fdc006494141de2468ca265064172e59aeb4c852fe49d63960f278c2b29ab515415e01e59916f947d52ccb5adffa9df9c88a41141b428dc3855848de6c4668f684de4cca19a114cc8852b3cea1bd7c4d52a26e18986f87c7c545f766de10fb27303ac0139356145102a41887677862fd25a063331ef2c4c0292e9759b3ff8c942efbcf20cd6c71183743e58a2c3b910ef238b321c1c00666e47146cb0c51f6a7530a551b64197a5d661e974942943bba5075e5fa1718bd596ec18c902bbb50e37017921370111f79a1dbec66eda93b755de5427fc24b12b3980da593a15827e371e1ecc0123ff889d950ee50450c852761748975339ee542d60682621cb6012e1cd1e5547c1a5c1a93299ef2f331ed6538457b239876e3ad223e985e7459713ff7c0885c71107704b3db2a32d002f3a1e989d1f0b12afe3a685efed49b653d1a1962a0ec7e1e69b234238f3634d77590c2ca9dc5cb184d3a202b340ef903e813e47b9578002dcaafee5867e68a71469eccaad9b1f46014b0c551ae79c40171fcd55947962b2777fe54a18402af872da8d88244e7032ec468e2c606f9d53d8b0c4e18752773ce3947b1fe9c5f23a5ac61084c5cfa3ee9fce1fa4b3266378836ae9b895303d230e1c36c045db25de170697e65bd2e3904185cfbb26eafd97049cd46ead3e734f91d17befd06274cc6b81696c825356fd888f6f3250f6d07ba7271d27c8dbfe68b2b2b43b872e96b3f565c5ffe58a9fb20536c44becf12482672b0c2bb97f2bb29a7a39af5e3f4f47b9c70779e5d4ac6e8565cebeca1bde4c9947eb51cad9f3337b16767ff1ccb18b952d7b22954902445d16f6fb7ed7bdb9408a2cb170d824a6a4ee2ca9be3aff9f4a47d75593b9148244b394876f89fc209788bc3b2490e08fa920472e27a766bad3692b934979e6ee19a618cc18473f2014dd3344ddb6eb02e18c3e7e7863cfe910c6cf2fc9e5e4891e7afc07957598e587044258ff73ba059a0a1982189d6a59c7adf3c6d16c569a0ff560d683ad30b47b100e8cb195b986cc1c5f6e5851eacf4e0e0065a7ab8c424524e72461ec1ad0ba333907c9a6c30860d2cf86781197904b38b3bbed8420697232766eca00af01f893290760045892b663060420a2d4c3f5292c0011659be3401022d3d304203104114e144172642c003095c61c696258abac842872ac820b2830f6e40060e3d686146155e784ef08359d11832083241146330110228a6b86188288c88811631da8c1b9481c5d21363006111c6931d3871344490121fd86249e767072678a0b2431541558081840880609a155d79e2072d5f6c33339e10c3878619962abce041d00c335cd1248355ca5b17d979aa5cd154250b2a64800446cca9a0810bb21811021a388122e6ef5ac0baed20d75a8fb832e8a2943d1c9d2083598e8270824a0d6658c83043fe80c309baf44cca0a5214216332814c16933908811355a6608ad1e020ca0f320cc56e00828889580c9a0962ed8414cda5c90ba4ded28109c58a1f9c88c5e01921e110f3df711b40108b212629e6b85d056bc30c475d624e8ae074c002054cb1183e15c4445741fe1423ca8e3c6206298e88c560030431eb2ac8974cc0d89e34b121686699e2402c7d199250a824811485c88633264cc32208273db11e8c88c9a0469e2cab0d5c40208d68a302a23c825108e5d18234a28dd613e3fe6e36f4a10127a6fd8f8d711cb6af4d2429e4fe80b40e72e3292cfa3b2d8b6540fb0c6cf445a7ffd3673516b23dc509e8d8868fe8ba060aa11c3463e57696287ab2404194050a267f9a21de13dbbe67ff940d87469478a61f93fb037223379e9201faa2d3675393777753534cfb29daf7b70003c4347ac59ceeeeee3ebd8694d78860c3952ee9d3d0bb72e763185c1174b33b89e7d9322b71cfb9dd7b8ec0353799bdca12fff92c17b8b74f84b35bf5d93ef4b1dfb3a5c5be51f81fc16a715f6a639df56cd8b1b9e17715fc2b0252c8937e9bfdef1ced1243d6754d15fbdd65af4adb7088acf5d6ae569bbdb063b6c3b255a81ba03624f261ef89f4ab308311253373df5d324f8c27e6dbdfd9f2f7421fef6f7a70759a292b8975a3a0d58e7561c74ee108e64a43ed039242ee34af7ef46d175659e2daac734efaa138bcc4eb7f0b94648b879fae6be82561ca9daa30157447d197666be2b009005702d7e2549cbbbbfb295c0202e1e64ef4578d9f363c9647e31179e037f3c97c4817b5f434e88e2df3251c78b43678290078a904dcc2209188477b038ff749c023a8fa11f0f835aa07001e5d2a3ce6e0cc103fe611caf569e0252211804200e2e9c9f541c04f835944e0ac460c50727d8a349f2ea91e44a2f1334834f018832cd797c1aa25d5920a8f31086189fa42afa3228ff27cce86a353c9f3372e3c2a506e00f004407a53baee860f4032340565ab5d72ff0d1f6e4d514a29bde1c3b65acdaeb59e1da2a101bbbee1c375efad77bfd5fd1b3edcf1e61dde61175762947c9a2c310d23206badb5d67e3be7e48ecdd45d60d881f9fad247e661befe0b1356e3eb2391097374b858bfaba19ab299577d4c38f2ec8801bf0331cb4767f07b74069f27cb84e3afc2d1e6d5cf84a3cc330f8633218defeffe4cc8936fa8a4661a3802f204d5cb8463cf4618b2ea73e6bd38c76b841dc3f117ccfdd4fdeffb7e07732fd652a8cfc3c9381668e5558819d4cef68a263f9560ee429fee69f898f75d373d89bb8ecea7df0f1432c79dbe1d9be7c95c3dff80b832fd3fbdccf2e3fedb5183cc340af4bb9046a3405f0b57f204fa12bc69badc9fb0e722a6177b348aa0a12f4818820421481042fbaed14299bb236188ab699aa67918c43b6aafcddf8f9ee9ce0a35f284f9148f19c89af630dfa99e04216ebba83222285f973e51aa972adc8d03068f30644dac912bb0204f9873a2be1a1e33888bf345eee7b370fa098ed67353478d31641f8d63fe084a977ebbd85ea6328f6d644316909bb7bf79c356bb52fbe9e4acfd3c640bf4ed7f3a3bb6e721f30839dcfe896f441b978ec6389d87101122fa8f3535d2a517f1fd5d2a95ea537f3dbed7c2ef6d28afa436d4b49fd33e554d4d75dfde8ed2d1a652a86f14aef6c465f976fbb2c451714a5223baaea95ca6ffa3611f9640a8948d01ea5ad15a340cf5f3350b340c85e508ba4571b738c6c47cb8bb357679945deece6688bbca49a854e222a55f43ca8501c90bee2881beebfa5a58b5222f79681944a62f9bc81596581d46d03948d02f20b942f7f4650e9d637bfa9a6c16e40a1d967d82f6b446d6fe84471532d5281efb894c93c8209d4366fa547bab7926d29ea073de7b6f27bd54ca4314cae7d4e409a9f79c09f3f7dec94326d1d9f2c9e549763c793cfd0ed477fded40e928b92f33cc4f981b8e4b66d6914c19c5e37b1dd9e4fd9c3adb9289ed0c15d7b60b0997df69e128bb64ee3be98d168f721b2ff8234ede369aad977e933d789341236c72e8f4a3a49237497463fbdfe451deb66ddb7e943331469e6c3c06b9c9f2bb55d6b41737980ffc9d16baa0b8da23b1f86d095989f632f707a466adf3d9cd69a5233e8c2090e5737824f2f1f0ac3d80ca1cc7a9c1022e0910877652f63f39b1f4e4c96c5642f61f4579827f5777d15f88cc05da883728a46082f6f2ebf165894f5952496d962fa467ab62ed3b36c45b2ced6dd6b0102bee23c9eab5425ffef7f3fdf8175f1c659b9f67d8ac308d8e1a9b77490610100f8038633652fb3634b2d55abf47e78a6f9aee122a8f5c53babb697777d31674519a491a57dc98cc6bb26709dce18e6de485343e702f8d37018bbba2f7d280790bf064d5c7b49156ad54abd4f750a9fe7eea79728ec3bc2a8481c198f6773fb0032c68c014134340c903c8cd841042b9d66f87a4e2a29443515cb1116535f37f0a8dcf712983e49085d198016b884866ebc80c8db0674b0ac92028ee945090cc1f654e6129c545d941fe6a8ee6584c0ef9dcef6e643a52f644a5a87f5e2a3c62935be7c7f6fd279bbbf014f6689ca12cdf6e61eb681cf23f7059f569aeddf3e5038abc20537b89e2f1b2e0d570c796c9957f4470e9987c7c9e3c99410e3572670943280725a52698b80e59b858810e6570e14206113740ea9a7af0a56ba21b6b7ead62e3f772b550c99cded5704fa7762aa778dfd9b861828b80fe958b4e021977bc2e216efb448d3c99c5832ed1f83805857b628855e1fe48c7a0061ff464c97da59d4347369d944c22b04071d1c621f16b5fbf1d3cea6fdf199172d2efefd1dda9f614ef6f9abe398c59f0965b9c43821a27778af7d5fb1b87791a0bde72178e8673644a66b82f7145d82cc445224dc45f441a6663ab4b46d074d9affffdd51888ffa4df16b77f7475d760d60f2c5d676cc963cb68240e77043d2093fddcae4f709470d597ff4566e3e607775432c7895d3caa914a2b0c6017f08b2bbf7357adebaf05c7aeb1a9a9c9662533f787808a1b07e36c13a75d134fb97198fd66c15b92ba701ae7d428ba9fc9c8411e7b66236e3cfc408a8cedc8d87c1bc858b2c1e7cb131ea4e0c90c9ae4a0b4440e1e10128a128573373a1d21b928dffd64c4bd6f27a3bcbdfc93d13c21f9ab57f7e61a1e3851ac3ffe92f9645481b2f82fb8f373154b1c653177154a70c9dd746510723f4f6e3ccafa93b7bfe1c31d6b510f1032d7a571c83fc9fc257f6a11027a8b5cdfd5699f820ddbef480187bb11350ef9dbd78f070899b3df0384cc613a6b1cf27980902ddef0d83f8f54a0194e39fde974e42df936bb4e462ecaaf27a36a548bc014d295a3a32e88f2d8327a44a783253f9b9ede869b9c271bb612cf1ad66a38cec72c67f9d31a4d7c07a62c2d3d19e30915b3289dc50b1744912f316bbecc3ef8e0031f00f5d3e789b11ab35cf88977aa341e329f62567fc7b4df995f31abf194ed3b569f27e60e86357aa2e4c6ed35f06f411f21cd6f877c723366b9e03150de1cc5fd8cb4427a92822d4b67702183337abe10bd904964c2b6777db9a30d796276c23cc0130b7962da6f3f44fb0def5499bfd3b3d53d4a3f79bcdd973b8ad37d0b2b42686c62d614ca13f31f45e7019c2dd1cb0c59f63f121319e4e0458b1c668e9b8005ed1d243d3d237d0bf0e4ed6dbfd6daa6d5efa161ed6787ef9273b595cd2150b8c1fd221bcaf5dff1578e8f09b3f782e08b3e84526c87d4a75f3b771ffe37c74693dbd856213066a9f67e5072325e26294dc0a2bb3b4362d1a37d008c2cf2d848dd1952be7cf9a251eb6a212cf8984d6a50c592a963719136d5e939b68c05a3bbba77f6c3c2e96f6ca4041b773f29a5b47fa3dbcfee42d126bce170cde9bbb79ba6f3f45ca8d3a390dc1570e551483642bff11157b6989b78a41cd6347cc312057fee087a9eec6230f3b94e3c27f36b64b85a7ffd5abf7e1657e66a84cfeca6e71f8ffa40da48bfffb4daf7d870bbb8f1e4da93be4825172ea2e4b1919c5425bcca4a5d2db5d780747f15e3cb161a91fd833ef73de8733377e778879a36e5f4ab7bda68d65cef140922222f8a17c584d9fd5c57b7913ceaeeeef3c760b2fdd4ec9cdfa3f3c4f3ebf63e85fb1a87712fffb9964dfa3dc796798da3fb79290499621a70a6e5804b3f0b8864cda72f99ae07ca687abcb93d6044248692093aa09e7780fbd0c87ccd5b9efe5f14ff6598e343883bd2d038c7854ae68fef3620fdfed46bc8828b32a439359ab80f860a5637cbaa0319479f8b4c189d2fbd115c5aa382cb922c896bf4dcf7624a763cdedc8fe38de08e384b4552a46cd92dbb3b8f0cc860205da02c652eca1416b07148f93d6606677bd848724569569ab66d9bb51c783a79377c30f2408fe6fb746489936ff470c7f752d2fdc8a9bbf1ed492165f9b767c2524b28c81f53b3942cf75f204e082b30f25d3fdaee777588d37f435fee9487ae5b1626a13e3beccf917147dcb191840cf197b74a923ec45f49641e2d1128d406180f15b04513173bfbdf19e40a4850038d2b6af862a93ba3c9eabe0e9bec230d0f77b44c50b2801283834f6a87181c7cbe7ca9a1434abdbafbd71c7f1f5cafa768300dcba68c070f74f1622df779c8578288586eb2f428a5476eb2fc19d4704304355001e4e960421732318a9051990e17045f74b516af76eed9645a6badf5ce70e7538aad50a103a5ff1a563740f0178c1df2e8caf261d46ff7954258b0d09653e69730dfc7c7d6e656fbbc20c9dd4958c1d5e19a38603c917d0736d9cb52d248ef59ea760d2ce5fa1d9a50451fa940d9ff79f01a4871450e4053cc40ca144f40d11804b7afd875e3a2cb4529494042c725c11d6d3b095e60d4dddd2d739c0342122a043adcd1cad972b69c2d67cbd97e97727006d18dcbc58b83e3a274c9581734e4f177808124a51402018a3b4aa1217ff5500266cc761ee3863c4a2026222a5e0959b4cd2269c9070c21264a59284def9f3df7730f94f278ffe80b0846e9c205445166abc6cffd2f50d0c417b3268cb2007fb823c884c90b2a84a22c315d0165aadc597af0441e9f0a298edc7ba28154775d9d1445ff1918dc51fe94b155bd52a4ccb193e88f1dc41d6dfd765897f3f8b2cb04f7cba3abc6d539beec7f2306a1bda8844e1475450c15a21900000000f313002030100c8643229150382215cbab7d1400098d9e4086501648b320c96118849031c41800000140001991119a992608973f31f399d7afd1177f736610b44c9dac9fc2fef589614e9146bd6cccad9e2dda689b3d12a949783cc042495026bfe114031899ac033f8c497e3270f8454604da32586981da396319c8043120108a12a6939ce3b16518bce02f4e107c67fea8beee22b260a52e37f0a05d90605d459c2e031d36fbb32e2bdc2d3cc053e924b0cf7bb81c9d1319d83d42087d117f3033540a32eccd702d426f28bb28efe290899c3e4f5c75f7c10d1d2ba4721c83214720051993b6cb52c363e92924306a7d727cac8c70281224639d09550d851bd07d0274f579c117640b60dbbf05349cdcd87d71f3c080a24cbcf8049a24ee3112900e88048f77ca100515fcad10f7b9ca1b789b2f3cd0840add371af23150feaf52576bb23177059762fd521f6cadcf628f41777f375394c0e27b592a68f9f7b04c431717c144971aefc879a2b69955324d0ce1c1c5c53d43fa7c03acc3d93c7280382dde9cff116fadff723507452044a42f825026eb8d5dc037a75fa6f366081138e717053dbf1ab13a4b39abd33bb9a829802683f58efab217ea0536f1c6c4f0c1affc537e1f1613c870fd805751e98790e80432deee0c323c3aaa3219ea12fe5fe4575650516bee442273a2b31905b463d1164b482ff8e6aea13bedf2b2d6045190030217ea964bb0f3c74f5642c9567e08228613d6920b3dae5090ea90cbbfae601b0bf9b3f28d5eb2a1e72ea83304f4ec6194a252f3da2baa2d82e4b04000a9a77c500a62d1d35b15a5e781aa6357290a02b31c703a59a2cac0b8ec888117124b92eb8754ff6cb3bffac2a58a4a4094e5b481db23ecb86769566422e1c784f6ac056b621002226664681268ea41fd19326e7070d5febce3652b67c6c7f1e2e70bccc68a24178870f4f829023e47884fc56e653ed20c3502fcbbd44d553a82421f46eff0da03178265a34a4bcca71be167060427a1ae806b46d845dc825bb2d2894f659f4ea926099dd26e57404f82c9332bdebca30e6c9cc65d770cd88171658e1a9745934b4b37b0638719bd43af362b228564e5e768e4432e0b88a6c2d0cf29ec0908b94aa20261d1879f2352e3ae927bc70d72061a7b5b9dca1425a3e6ad68a31df72e31f5a4f1b2ed1345ab398796b0a84153275f02ced716b37d06693eae93a0d41312ae044ca00787bd7dacc2166ee83ed0f3cc27a16415f07fe52d427ee78eed95594d2b8bf75e9fcfe150e36605d9bc2bbfd59bd737fe022ecd7107abe70ad44ecc3c0f77736ce84d30a50530121f877110dc8a3206706d38bd69660a0eebad91312a1a4c8f7d986d2641a8d0c81644931378aa4aaead1aee80e44a21bb31262a46c00d218422b02d1066e21a91e85219449e8352121e1346e1fb6e2f3c5c00527ba9f63c8a529c75d8dea9ef6b0583692b0e1c641f138507cda24e0fad80b88cb3e76752ce0ef620e1485f23deee88ed8b302e6ad53d34258094ea08b0303fc09b55315632e54993fe35767376a510d1514b3ed253994bdbba53decd876169372f2e543cc871030711285a74dd3070bd440e6c05fa03842216add6a4d6a9e84900f000c709473b11f98bdbc6ded3c1a43abc3c6f049a3311019d351d8943a57ee27f4e123b76497e6850c3780c829e5490315980074531a6350120d6a7b6b6b00e8d45bae72c570089f95b01df7ec64e1b9aad9af5d54b74919a93a6bbe7896802d26519fc00ab9f57f174dfda0fe31f0380bce32bc94635c90b666d1bf4b1698127442847dc4dd57028529e12b63df80e35b8c53e6f5446177616b17e4c7a112514cb955fcacebe7c03b441efe44994bd0f6fd096a3bdc0a0ab4e94647c5a79d57aad6bc95ca425c8fcfbf9222e7a74c281e9557d442986312b7245deec90c1df9aabd5b6c3b51ae89e8995e37d94fddeca22e413854a4cd9441285b70ee3942962b18a769fd746bc137b29b24afa1994abab7c1359ef3600853cfc8c0609d57ce270a9810595248cdb7761cd7ce0dad78a7ed9f827b2f53c4de994a4b12ec5e46bd86ef6bb9a58c033d9c7d040c24b951b9bdfda37f329a3c69980ce1850f04cc600ccaa51808009f09cfca768c2b451be50b3a07c2f57d2d44494c6eeef4d9ea083afc5d8bf38a1d43d5fc53fa8b29ef19ea3086c8ed10fd82023b18f7b04962bb5b14cca40d320f739975b172a982030e19521c722b5b3f8fa06d1965bd55dbc3ab03e3f819d4dcb6dd360ec10e675812085c6316473f93ebf5281952ab6d49c34608e5dd430678d6f98df47d0ada531722a996d18c27114a7f7522220996b549cc8c4db5801b90cdbfc640ac4f75aa71c7527f120ca65baf25310152b672ae7446bde25471aecc0a79c4591c00f44d6b9bb32e53c294e3346614899bb55ebf2ae0d4402a830529158ab31fbee580236e9a0ffa20e7a4328cf0aed3fa0f9b55f97bf357a0b0fac238e098858112d1de994e27e41320d4400a09bde1ff59c8aad08e51baf1d607b9665c43ee125b668a5fa612007bc048a5affbc64cdf118d4f5b608bd12204f4fb48bfed47b3bf47c9413eb6f5d4309ab6daad3acfed9f99d8328629434fcaf7c9feb9e232ea0833e0aac78e9dd8f85dc3cfc9781f65f517ae247a4a43150ac3711ed9be7c1dc0f888a5971e5c4881a88a05921aef3470b48444f9fe0d70e1270e7fea98ce217ac4680e89fe91c864534cd30a0a08858cb4da4d18a81a23590bbf52595acb6a739d5a5b6a6b109d202c8cad68c07852bdb9615175fb80b80cc41970a069db8adf5bf1c5b81724cd33b82de62d9d7843c0574d4e9cc1e35d5da0ae79d21f176f50ae3e4dc868c5338ab9a00649c17a8dd50daf7646ccd142e123302457b78c85964129772288b67f5ea6881c59868785e24cd7e2da54d35a007edf48cb17a7b405f732b5728d647600da92bbd1c6ea289d65ec5c670ac933981970ba16f79ab5f3a0ad55666338e82ca5f80ee09445a3282510ee20c906761291f7add6f8d69a4c34d407cce462d8e3429204c2917a642860c9c34631fa209396a8bb4d192d00fa4166fec8b6b069848a8cb451b09f6672c637d26156716755ba22b89b33a2b8dcc2a55d6259aede558d306c81e75ea95d87d9692ea567bc6177767bddf629989af979910f35201fcda8345ad03b74fa931b521981e770d66811c68c1de9611ca809c4117d0873d105bf4c5a842cb691904ea58a469d8e97b09ef000e243dc78a4ae6829d8c753996f4fe5a310d218a53897be3cc2801d8500c1c1762516b87062acebd23c683729df6c773a8498dfc9241a04f53860b47d416b535c6fcba4404f772506fab89032e6b692be7fba05aad9cf0b784c60cc6cdf94112e1dea024c2d7699e5e6e252a394a307fb98fbae66b89a504ba024f647f43b6c37980654429f6aff33179c06f1963d0e09b8e5a2277c964c4ca791fa516f562bf06da4830114d1616fdfd4e5ce9257d5d5c9f8fbc930922cc8b9b86fd13acf04905362bd5ccb590f058eaf7fc250a81d82003621b4470f6c4b6370488481e6238093395bf465acdf3431133c514bc2f0a769cf92529fb0ca5de868ee3b0678e22922454b172e1ec5c76c528fe802e7908945773219b328e6251982116b41f72042be8e581f8a6536c50d4f934b40d01a13b118ea7fcde3cd52ee80b442b1542d96fb67574f4049ebb3595906856eab2450287e3400a12f2b8fdbbf26bfc4e700b19ec67ce357c2a3d0bcb513c4e9e03883b43fbf1eb96c78aedf5512d70e3fc4cea45519bc37577fff8f439de72814156c3839fc37b3f7242d7ea50a9e7ef799396631dff0948ad2ece2b764168f5ee3f16eecba0e61af09ef9bac64601667fd5ef6fdd7fbc0226b825d10aef7dfe9034aac7d443ce77d18d188280debe035f0fcc190440e2bd32dedb93e4878806ab707601765c870161e66ceaf25f7aa25b0ac9aab38ff191581b2bcf8bd34f3cad6a4661e1b9c4b9110aa53fb7ed12c2f040672bc6e0a76adbd01a8883c9b63de5a22ebf566f8888a852be7bec2ed50887d5208869b3182ae291ebd374b19d2e44da8488246c256871438ea54ed450fe62c39d8f92afbb0a9b79d57f82862691b5aa23525d8aa5c02406bbd8f6cd1d2947267dd01b1188e81265baf2a3376881f7403632976db1f87d5ff127d34d3dff046b62117048e35030900a6648a107eca027f1962e76f03fb9332778b4859e2d7fd763d84dbd9b43d9bccdb720902f69525e36283819ec4ab50be0c853e801159e59d3617c40e7a6f8c01a8867bff93180f65f7cedfe1d7c3e2b3b451cf7012f886b170896bdcdd1b8ac8ff95d308b318989d0baf0da22343b04253fc3e37f262594d7ef7e941713494c0cc1d9844267c46a92697d9c9b47ef637cd85b423ced047644b81fee61cc5d220b88761cda20a0a859eee6f938b40222ca962c30a83f9256d4d0e6afe98da0a80658f6dd470acd0356d53ff1eb6613803ba53124e184c520ffc883b7d96d71db9b01e76b9846ff41539151220c0b123fa23932012b9627c424ed5e04ca148b4a2070f28c50a4132d00e9e06d89441aed8640c5832f67b74d7ec4f1a8ca34160a3f4db68b8d5cd9aab32e802b4e22c9594ad55bd102a07e25dca11b1f3052193c5a80c6f482e5d9128ee11ab92ca17b308b04e186930c375f7a35cf61cb3dd6cce9a3735351fe15fe5b7526573e00a35c7137fe11f076007f2c1c5a186117a6b9d35cc062b08a753f3ba1179af1344a97621c47f9b5da00b2f562b0dfefa157c2ee0843f891c3b788e624dc92d6d0db34c1cb58e2c453c6438d68c8c630515840a453082b283d5c315172608077427e2028f7fa9917259206d992d4ff5400aa634fa75302ff0354a81301be53c10a5a25630fbe42701b43d758eab9df8ab41720b64f2b723e0b5515d1f1a3fb59e6e31ecd671eff90d74fecb54d1bc2c9ec81f1f8669228de5855810f920e7a5185769bcb5bf5a180706aed8a6894a4b94abe6c2a9f32090658d2ebe035088957e50f26d29f3abb24b67a9eb9d5cba49958dd79a014076f7867e4eccdc5d108c98a5f2079b855ed4fbf8074b252af1bba71047d9c5ed55ab82fa571ffeae144815518464f404d44f50480eee8bf50e84d3f5a008f3eca0b1be179b139bcae52ff43121e4d72fc24b12fff08d95be6615744cd73219098b3367632bb942a321479c819ad06dbcd208398ecb96c113e18022c1f9b0f67f2318e7619a551b2a0d7eacf8e2e292a4f40ce82405ce6e76b55af4535ace95939e4c332d452a064a29c90a3b126bbaed214934208aad343e5fac06894de47809006656edab29792a843b2c4af89ffaec58403506b57d525d26b78a0b4447230dbf0bca2f7d680f19f5061768eeee1d204c861778a3125815426d360a8502ee649e7ada94a71376686cf3404a4b89175ec02fa8848fc3fcc19c5a29b3ecc8560c207c13f3941749a8a4a62e3a935ec1bbbb1f6c41d00fbd37bdb80d861f468e78a05e08328c1585a3625fbb5c0b88ede9a2ae0ed8a042694b5e451d938440a12839e381bdc8d3ba015569f420416d4dfe9554c56c167c4f3d11a0040389b67229ce416da8f117b1b43278d1cc306011fe8faf7ce385150a2daee21047d94fb6de5042a62f56667bf6c7d936c7c133bb5b6c1b61ab2c08176271f395fdd2fc09ef93888c2d6cb2d9cb096e7ebb8dd9e499bee7baccc74711cc15e6ded023ccfa8e530c8652e2d7a98e148f8e8c0adf629c4558f842ac07b122d1e731d9317a068a1f3333b2bb356de457b3243229e468adc169610fc1fcc1a6cd8f0f8dff47b3b307871041b39ddb068811e4e0f217f565680d7069bb430fe5eb0e0e67fb17c306bac70386c9b5ff51309b1041fad634a098f9e9aa33c64815ca43114e21d306e4a7b3daf9b6273c4689b58507ecd21c3c1a16d849a8eb04e6f4056375697c8aa0dff3d26b88e4aaeceec56f9cb9a0942f0ac222dab143cb91a56840de0e5b1886489b5c17ccb62083aa17069d953afc3050e9ef4e87e5c908c4dccc2f8c37541d908d532b5bdce095e32c870184e317031a4386f4dcfe8c2f43ccee34106170cb8ad0e6900ccee55734f669a2cb6b9b5aa739e36e4311d1af630298829f23857075701a8699e751c1a610a6bab69f5819cacaa56375224d1ab67d7f2f1a917ce510a92012a041791495cddd1e7f9e6c6efd8f7b1a81213fca253bd03064707a44a4a206fd4eb34c028dbfc14155c614bde9dc3c5f8dfe3588881068ed97cb39e97b38308e1138f65d616301cd5fbf053bafa17440a7829279ca127b666c227ad69336cdfae1015dd7c9756e10af4bcae165b52b784b8e3c217fdf600738ebf0a02d694bceeb0b4879f8d81907e114ddbcb37cf6275e742bd8700e31c4de05e920a2ba18e90e350ebb6e12905504ab17e8d3e5a1df9d0de52d0e590d9d3ed3d4e0e364ad213bd2a4c618144e5e0ac83684cb23321c93feae26d1236b28603e46f20fa370736e8f7852805c19ef31c4ee44fde53ec03b5c404c7fec220cfc7e266581a61f7f504de2ff32df6a6ca274df78531976479c2b36f803405468aacc2dcdc902531fb0bbed69d656db2a532209dbb55256017dcba36e76c6233511776f815808d4b94bc8b8f1d6622314a06d39943b535b1e6e635b1c33bcc42272de7c548abc113ee46cb1ca10c3d4510ea6887d7134297d51c667d087d5e215ed10d4679d060e79a81b14fce999165589030fb3074c89e89cdb072d34ed2f1d0fa5df95669a7aed194ba255240266f3fd63e01705b19fdcae01ae241390e2c0951b0b1fd1adc4ce79ad1ef69890754eb7ea391d691d880eec3a7c1c4c087feb7653107bcf4a495a809460ebb100a9ed05af4c430c0755047627d4ec85188b80c5d318a52581b45375b6e001fe8daaaf3002fe60c97356042761b576dfe0d81a7163671010c9781acb3a7b9ea9427d7b12c558eb4d58cc619142202ac776d81ab401c15bebe1657e059dd0e7f5c6b249722270a7397cefcb8f010406a1eaacf849e2c4ca162db1ff3c088a05eb925ab8115ec38e119ba2e00ef34e77cad7918c611a145b3843543d1597bde7b25242653b4cf18fbabdb5e119642e7f0c81e15ff83f3ad78cc21bcfafc486f46cdc44b6d66f37568580f44a24fd55659a04d7451164708647c6f4686e79095b9f954293fc619b5c683de549de51a231e9d4a530d66aeb192d36246603954ae8b30019b50c683c2bbdd32374ac1932a468e9c8f0c3eee25d0c4adcc9748f9c56403c3c60547bd91a9d455306ea59114cd98514e8100360392720d1884a9d77448b21cef2c85e44c0721dedf4f41161afe5e9b2cb94bc760659e05739c6d212ea4542638596c686588be5612ff7d69915824392d3b01399fe93dcb97fea012b0aaa04b00a4a253c5bb2ffbc741f9d28a79236de0edffc045edb93387078ac77e0a7be936b64fe440527f7c97bda9c0b3c6d09578059553cc3196b09b3d1b30469c4b3eabfa1172f2c34d7338e837faa0e70b6e72bd70568f8a3bec615fd7aa553ff083d47555a40485746d7b5e3ea5c1a7e7da04eb9150c19b7a6903b7e095b00ed83a2b947c5ec02f08ca24d53a7088d87b13f9af97466e2d50b53d26c515387a8a7c852968d4ffd486199297486ec6e6185cef24e52ab4c2ad44a65f4db7971e733c262ebbcd10de25288504e121d7be2f3a5406ecbb56336be878e40e8e49e55391d8edacb325297f73529456d74cf99b416f87a1f92fab82c4536634daa1aa7a08702082e4681939af4428bbcdea84309e3f88dd02562e16d249fc49fbff267181f4f6d8053c579c5897065decce608f146327eb7427074184553176b5dc352e51d0027b36938a356b0324e4ae184e61d85338cda3c77165183ba72a0f66050bead6df3d2f7c5e0d3cd212bef7b3f24054341d2f74641a464e446746ef47f48d31e70cc427e6b69e37dafd000dfa4045fefcf23e1e7c6ba875ff70519ec1d70830a07d943089d414df54611182c35791ca236961b4eb5e52a3b17fb5c96b9fed07b2fcf3cd78e96139399b096d1f16ad2c71957645b0e94e8d9b87182f16c7f927534e70105338a521427486d0b25f251e5796607a0172e91a5196bf3c44274e20d5bf4226b54e5ba4b6c4e30d7062d27fb9baa4f432857ec1a490e84ad36f75caf8cdf7f0c81f394732e6935126f955c9de3cd86f2619a938aed2b42325dca63b8a38b702d3cfd768117b2c2a6300c437777558b0873f5799673404aefc503de01afaadfc9dfd87645bef7990381c6b545b7d236c43580a21680895822628db0e4229c2c922a2b613508e7414f3770e693435b253fcc4b3e14a76d13fe97d062edfd138449598a6949b88b414f391218417ca12ce42d730c6fb61a0088945b12039686c8eaeefe810e98685cf89b59aec5983b9c3b717589d1ab54b4787a1291c6435f07ec769501bda72d588b93f6c2507e5d88b280944d8fe5fdf0b1cf1943113c058416e72dd7764a35666ac96c7ec7f6578bf7c78a0091bc37d4d12c3d9dd36bfc38dfbdefdc4e3577ea8aa08a22519c5eb4ef674fecd2be50843ff4bd421a592c9e978400e39ac34b724a3644c4c39ce14b6cee209f14e45299fb0bdd29c0ff7a24b6d7579ad2104818973d27a5fcc9c708d1e62b37fc0f26d7772645618c2454b300eff86fb8e709f4f331ca69d186ef3d2ce025d2ba6075897ed74799a219d41a3d610ccdcb42043b37fdbe424103c66237490c57e851816bfbf1502e63c2113f401305b924c88f328eb28abd4bc33eb83c5975367167110c83544915124895a4f888709854156606b36d42418840c2a69f7159d0b40d7edee30bf49f10fdee62062e8586d7a133305359b15f1063cf98e0701e14cce0ef0535ce556161ba5ca68d4e51cc4fd0f441926843ab9ce5937377da89c4bb90650e2835211dfa8ecbef5b397f312eac18353106d4a2e6092e8a131685026f4d295f1a0ef219e00bb9e85f6246e34d0266f8df35ba7d3c80c55a3172b1949638a0c7500d6c3a6d6b08d23e11fb81b551fc69a24498f8c18ea17e9bd91c8a1ca26235e94c11681b54dda7358c14e48af74af447bcef11adca939755ba9ce6047878af72b77884fe3dd670af8ceb8fc0182ad6bd3c424d797967c26477cd2898ee67ffb174ed475c84819e83b5e0f3d514932bf67eeb85f7fe2359d6cb213c37a71274eb770af392b16359170e82717ba328f2e225c0df246ac4ce1c25ce792a0a29d908d6a4116c240e78f35c3903fb71688b64f68cc27d976a37954fd2ce91febeb0780f376fa85437a0993a4698ea448404422935fff5f84a339c9455bb02d3c7b43d69889ed9cd82df29305466ef6dade3663fa957b0ff071baec0910f678ef338090472c3002687cf22ab5aaede5268926145f8843bc2da89559822cf988edc8cf9463770293d618d9f95708b0c64873ab69441b835688bbba822e6ea8c3d99a7035a7e57cee8a9c132da0871544b5af5b16390ad8484356eac2dd2c3288abdc5adcc4d24dc0f5a8b504c57a01a4ebaad8ab0c1ce4e16c5f64a5010f31b5f915a41e8a0f065031ee2af75de130f9423553c7ee482273c3c14187fecf0590342976daa522b6c60158a737093754917a0ab56beed57112533152a7cb757fd67ed62c011569c584a4f72282a13a4a04e7e94010b1acfa167ca8ec859da271a96653c53044dbe65238b7f6fdca4df5fbc798d0c076e86542bc46a5db7522e8274523afc29689d48f1161a7d7b7345561aaf6863f6416acde2c3859690b70c45445e93e3d54efb74768533af2821af1df05c63620b07bec492cca29d7d5aabce008c777b8545038302a65cbf09d9985201a73c4966540a4aebf7c6dcacfa89611cdbd8dcb0966ea9d30b03e2f486dc59be198bee293f8a6361a51ee3fcdd4855fc462e25263ea61d623e5fe544617c12604e757f48318b954880b9e29440967dc85c40824550cfd739a463b1d75ac24ef4a8a4d898834cab4a03c2074d5e4e9d116da1672482a92e9a3606f240f73efbd58f909ca2cee76ed09c7a062fa81de24cf61ab38a632cdb48c9e00e843df25c9203af2cb417aa1bd0e95b7303e4d0ea96778484dcc070abc230a6369223954c20d10188ff367568650665f0ec2749b2584993cccf14d6ba8b6550268d46d9a95fa72c791a928904bd018ce27745d4c397ae4231b2d691f3df526b72b8103310428202816b7d5a63072fbdd0452cbe17d1985db5cc2de2e869da7d06e16a208ac34c616a4589487676e7ddf71fc54e28f0e2b9f2200ca889adcf2211d1407c4e1b8159ecf1f32d4e50bdc923ee5dbfbc91385c778e93d83ad4896163e5b9844a54757101f15ab2403648d22651a40a2cd9b26ed1b30b07214c03a99aed0f117f01838464520f4de57f37be852d210db4b83c13098b8f73a67f9c6c14099209b11ce2e30f2281c89d5df1053428a97784cfe4d59f15d34387407e68f84c36955b6e4c28a44fca6f07060b97f74c37b3e36a0b80fb5b921e84d860aaacf347663d588328a1def31f2dc94f4d08328b7beb096da2d78b112f7b3699401e94406f880abb7ddb00124ad3f54d6ce5e8d3dac7bb732f6bc114295fc56493fbd35c2404b045a14dbc7da441dbe5cb9dc8bedc44653d6ac6728fe80e8e98f99003cc384a57c2c1ac1e573ffe8ebf7d589dd77575c47f363d485198682e274bd468c0186ccb4e3c2d6e297f7a2857bf55aa3e846e146c5348f84e5a4cd490d2e0a09bfb829a746ecb3fcdf6e86678cb14e8d2ffa39736709765ee65f9f75c90a6b33558003b50549a9db7a9e7b02f6225e683c297d3cceabfe4ec2af0a5b4fc826130c847c8aab55572cf11fd7afd786046a871e32bd42b864b76286da6bd454d9bdb7ee84af7f39850228d3953e17821ac5720a8810d799042535bebbe763bf706954696af8bc77bce571c2414c0ad3a62b27fd59685f429e509ba9acd360dfc5657d01cb2b3989a304a16977f4ddcf96aaf12521c904754199231bc0b42acccb88eaa0d8b04ec9e6c49838e39ab93f36673a96fb4897cc90f0ba74ff5fb5ed55db247f346e61fefd982ac32103e6fc86887787603f542085e93c083704e2bc0982301e65e6120d347b0e4da19656778895be4d2b36a44cfa940abe70cb43691a6beb33512c6152c1ba5d912168a0c1d5599b2d06178f5ba6e6b7b01721598c4f94e6ec4a4ccf6b5e2603cb2106cb73819d084ab6d9817b1c11888a5d310e8e658d68489c1efd42b3afbafc379eae812cdcabf51629457f691011331a2930c3defb608ddbea2a3b0c7d56b0ecaa1998c26f67f18aae477d368867f0c40ad85c9dba023c8d262203955d606d38d2af62159acee5a8f579dd617c08a003a142838305e91157ce962051b8d10bb13221d69f4445d0afa6a5e4a482a132748982ca9626d3e9d16bb5f7d2e07a0d416ddb2b42c56ed405b526776277d31cd0919c6289d8a351abb04eea2d5a22203ff0959e82c60bffd51362129023aa883d7ac959ea6cc4ed2601629f2460b73a7d6661f2ea722e3a48893c7ec8709d97a90b1a0cfd42f51daca500e3ceedf19b1cb87c646953531149e781378a81d60f2cff01cedc95ff0215afdf940e4fc038b05297f1c14c9423bfeb06d80f9089bc0e43104540516e51f21cf799a1f71d615bf90dfaf895b6846388bc9b5506343fb2de1b5e0cd0c90c82a05c5f4f8875364a3cc249c83864f9708c283fdac413b91b00605ce8da06eaeda981b37f5a4209c4d8bd0291a9dc005840f28548f243d82b1201bb46feea2cd23a8520f66b205ba864848fb24ed7f22d648f29947393379387ffb34ce9d11399c170072b4ad288e6c3540c5b80a8b11312f384633f190ac158a7aef65a7124a959260db9ff0f1ceb30007b89e58ce0faac3008ec9476a331d8f945fd54d243784e7c0a915ec5c6f3acefa80d96cb87d05a78dd6813dd7c20fd19a680340c38aa3f1f65df495331a1638421557e1966472bce569993e3ad7d9cd6350ac146ad923eaf26983644968b7215e1dab99a03f4cbfc186f09fa60c5240a2e4a7cef4430017c9481ea26289b089f3cd6ca06a926ea2ccec756e3d11044b5f47a9586052eec4c3441d79bedc1f42bd1c49705769e4c53883f8614065c4f170d61c04181f38ceb2e97e334c92684895c815647a5889cc1aa19d3480765fb6b809036b7f236e24acf959e577a5ee9bdd2eb4a8f2b3d577a5fe979a5e74a7a5e1f7a386d6d718159038267ac6ab5dc18633bcfafc3e9181f840606337ac2fcd1cc06a9ef04a80ef59ff84f841e575ee099537049531d442ea54f0a9e75ce6d87dd2cd9fed4eb2909a403020b7b1a2fd15433fec110cb5a0e4168942aa636d3a0d16a2fb7c22c28c0a615d2e2b25143bcb14df79f4c58fc04b3f3e07214baa8d2f59de2a08fae8cfd81203e1f6ebbddf0c00ad7c96c8f6db0562ce246f558ad0e778bdc18ac75abc3dde2c660acb0a0b72bb271bf99b6d8f0670595beb925883b4bba81de556b979bb5ee2608c9fd65da3636fc58ac2e779b1b854ed956bc1381ecabcb137fb4d316baf243b362e35c64108bd16aecfef00b4029fc9d01f85e11d26781517fffd79de89b0496cb8cadaa36a16341533e035e97d04a4e635c5404ca971c5286091a9b8b97f4f834248930b9ab388a3600e0d4b4fa65d0278bc6a96bcd071352c071be8cb51683453bbd48c364e8a57c2fd679ad708d191ed0b17205f8c9a34052b7180761c155e6ac7f0ed2d29efbb27c5ff0df2e48c91d519c85f8e17bbd6b1698644ff2e210ac37bae4e0fecc1268136011307e7b68da2a42947954a843e69e5585e986ce85902c0a24709b289aa815516f5e8cc84eb338f390239e12c0be03a6002b27c0d345240217a92b32145012d1e538a2a4de72ccf50da0e64446cb5431428ef24cc0a02a2029638f04feea44cbf8212b62e0f772aa18f7d108dcc49099112dcf9a05a1a4d713afd77618755a87ba84ceaff15f8a64d2badf750e66b9018eeb45040905e4ce65553a42b8c73902520b607639e0618f842a9f77107465a8449530b7471a87feda3e10fcbf32c836fc28f8aa8a8d69b0c574b8c7acf8a9eeafc80f320260292d7bc0ce86b9ec81fbf537e93dfa513deb06a6f73b057735792353575a15fbeda7e4fe01ccaf107c4af6cae8645c7356a85d36b557ba317cb6359e50f76389117240fb696e7ca8c411038dc3149ff0532904e5853655deab8a3edc289463506ae0a3f99d68ad2f11d45dd0676e5ae9004499a980f04080e73cc93fe7f814c45033ad5192e7f1f25bf8278640389e7da261a683b2be03d32dad0a4ae6a0549649a8cec7b02c80ea16930842e0c167c9740a4b82a168c0bb6a6e27a9bcbc3a514edb7fe799fe9f95d033d466c54674fa0ce92702b2fbd86a2e3607948557b061cea3dc8ae75e98f2b2d5679c1083ce8278f1a343c799f7c8ba2fe1c7170818c88503cceb988f3217c968f7361f6d669e4b4a1b0af29995f6638db0283f81a91e3126178d32f2a318cb1b53dfc3da335744d471c27e4fb05fc35ab70f3ca5efa08f09e6335da4d8ba4d30f26379e2e47072515c3863a4632df4f8412d2441ac82c05a71cb2b9a09a03ecac9b07a5cc448735a934e86652b2136c73f61c8874d236952c5b28a9e1b3355a4f8c15ebc99dab1ca62b49f886dcf1e002c8c1eeb9caff07ef65642fe1f0a5bda422b5d1c35ae002b1d5efa12345c157a681ae758f5a4f3c87106946bb4e46bba353ad7c448de02d821d27910d214bbc9eb7316c926415760078899f1ed3bf4c703c5a53dc3e6e008f97fe9169d05fae3830e80832222152919d387f692ef7e0588e5c62266c0078d431867fd9951710b0caea668dc33a4174cc915eca79d0f965a9d3cdbe88e510b0670a1039c2a24514a50f00b0d25e51df134ef7046a0fcf4075fd32f3f7309b930739212ae6bc2505125a5f6286c8fe7990f6c29b605d435eba46984cdf213502d0a070d14f0aa68761625604aef788edce95add0aae7ae120d5be1dcbd77dae86e6ffcf52328b58f50cd0f464004821644c91a7e4638ca9d5cb1a3971534575bf0b99582305d8a9046f2200cca10ce14777ebe60362c62e0a65e0617fc25c0681bfa9a0e2a1f13fb521cf6ae3463be2d023e0720305a42712e53ecb3d45506bf6498d3e4a4874e6b612d83161af87dcd66929ee7886997f8233843e4048b707450c6c1656a24ef24cdb12704680c06c8d5628516c036d26baad78a36713d003531b36b998dc78125554c9990a472ca7139d4bad352468bcc0927fd0871cbba9cd6a977aa16325a364d1ed59c25b8e2afb45911ef663cc78cfe2856239bfb485a839de4724451a8e3a9394d78daeea1a9306d2c251eab473cd4c0bba8c884da352bb7b061c192f74c527b1a17bb88711b7256a1aa79ab699694b135563786dd8020206d4b244d5b155e8c15234a7f544346055b943024356d4634eb7b4feec613f7e6d9d2c1a1189a943aa251191734d91c98e888427a57d077fd6e1e5c3a83baf7ffba375fa6a9247f015bc16477e42244d43c07611476dd05bb3fb3f3ed98ceba6a17e07e9bdf845e552d8a631ab48536060cb7dd48280812d33a0b46051b06507d656186220191aa75a91aec2d2171d442ee48c4cf5e72bf524b96997e64bbef5adb6e9d44683d580448782d2cc61540e800187993e99ef48f4fa756f111b3292865e1e01a32d5ef862064a9c571a16df3fc67d7c562b51532a03bb67390923a364ec92c8b6c2d04b940c07097feead2e8271eb2cd313ee7c581d5ea493881e7dbb899e371abf405c10242bb242721b945a1abe3041617937f7d78e1a27de81ae46ea3dff4b446afefa41e835cd2fcf8138f6c64983c031f5bb7fab077da8343c005859c234c88f77cd8c250726bdc77bf24ff5849e79702103093905bddf36e44a3c349028865dd50aad4aa61bebed8fb23160a34b567cde7687009ceb067db719723d4975698fdd637d74ffb0323aa2b9dfd2e76fef9c7f9a392b14474e12d922e66cbc230f31f038072d01e856cec0b08b0c401208564e24c46c39524e9de39d18fcbbdafde5ec6a9ba3b98bec8e147bc25f22cbd508483ceb8a58c8340193328e51195471e57a0d5717993ce8137b4270f974a3306188fe86ce26aef57d2c6569c21c6a8c9e91e5ad8572a1081000f764bedd877e49def87c1149c7d49df0ac614270e808a2de337c995d6a346057cee0505d47fd4829f6691b91646f7b21f1b3704aef3667222462c8469b424064d1c627ff6f2eb5fa6ed900bd373eef8fe9ac0bdef6abf7f883779dbf405aa68bc586ed8b58ed9078d9cd5673b0276a164608314c8db416913c2acbb87521730b69cf091d10598e2424b103e5d85691a8474b6ebff63d7e0f00754b0614801aeb0f1b7bb98884d779b417a73b5594c6ecba221576925887f31e112bd4f8e485e65932a7ea624ae81863d615426bce8529105342e7cd10f1024d06bae2abaf1ad54af10466d184ae9693c9204f66aa3d67126b58034d62123495dbd329ca01064507225164856dd2ea956ec3f49a44af58442f4526bd01a8d4b7a92c3dc9342789c93a0fc89a0764cd03b2e64159e74159f380ac7940d63c50d63c206b1e94350fca2a0fc88a3c18c0aa74c2c1ac567af119aede0b77ef87abf787abf7c3d5fbe1eafd70f7fe70f77eb87a3f5cbd1fe2ea7f5f5cbeefdedf79cffa818459acdab8fa627194d9f2a78f6713b757fca271521acb273ef76cef3d0b284f328886e4b1f5baa7cf400b44eb01f9a0b583a18c8bba20d8c0e013b2471545cd82f1a1fae4d6787911c43b7800f7675a3208131ae4651a2dc1f0e4b686c5a06ae2d08883909332f64a8c0e3b21bafb73a8c177c7ea764b39296a1dce64eaa3d2cc2b3023539761342985b7e152fdb187ac7069ffdffb9733dfa555cafdc91f805014abeeef8e87fa84f4b875ff540177f53eab2eea0b015ee9ab54aec0f8977d7e0e0f882478e1e32d9333921ed112698cd1936767165288513a54d1fae358a02c98496232492103e742aa7a792da14feee90d94ef78a26833a8f91a55b8f0328f650170ab0eff4f5780d557b8e287147d5619cabd95733a8c671ed58cc0d0d106ccc5529fce6361e379e79f1ef28913b7047275a3c0bf69233b7514204227314f1b3b726b45cfb2421bb8f5fd277e9b240a4544aaf2fa1d227e7d2101369a90df8cbb10ff83d024bd4731072831356c2116554e34de69342760194213a2b10580bc66501d79a81662cec48ebd8e5096c7fa3863d01c4225a95dc63d72b5faa1b9643990741484ade64b8ad2e81123f593f70d1738f7f46b6f922fb9c66c9b34bf38826476272a0cb2a19e7fa8404ea2643cf09d353c21759f02bc7c1fae07c563c06fa3c99640ebddbf44763c0a84f01932cb4e99578cc4c469c8fd598a42d7b4c4b3dba2c4635f07f16fabfed90f7ea8858c3bd89146947ac962112c364e90508b103d4769a636701716193a6d1b7c7e87bf648b167b3a86dc14a8bbafd7108c3210f76c33256d056af790d86233943189cf5e3fa170f708b9a057cdff0eda542d1060d724ee6407947341197d81d033cc0653932ff84d32dd9c51ce729d852eef7f32f79c5c2066aa68af2985934255e1507cdd25d449ed3e8f2930086b2e530e96bea4c3cd4f7c31a0e3b806f4ac0753a96dd3ef66ce3dc8257517ff836ad21fc827028bdd9a1d7cbd1a04b5ca401ddd225cbafc17ac75a0711471c103a28f1f26e9bb32e44412d86f9487c154f3f57ec9b907ea0a2b2b6e6950b98030e248e532cc2a11a4229187aea5e81a9bcb4b9f024e9aeeb006af73083226e84bf0d094c2ee04cf9c5483049e9792dd00df903bc1dff3cd9a3c1a24560528aaa3167691493399f818de6a5089ed1e5f81cdeb8117b1dcba725c308d3433014ddaf10ddf4c4372b9c5c29883f643133c49292f1e4c51837830030fe24038fb602054cc69a9d0942b2c323f1c0dea23e13036aefc6070279e238cc1e035ffe098bfd6898c8ad297e83901e18c598e6483bd305448a93c7c786e01650947c84d1784641ab57fe4ac50f31e18a81de89d9b20bd2b9502fc3a57c633a2b717cec4ea933deddaa8a5a9523cd492126aa79819e61042a821132856ae4e32d3841783856c766f79aafd4d871399c7725a4729116db4ea5035ec0161d9640c53658cefe2e75f523b1c1152a58d8c6e92edae878a13b17af3acec268c7b5a31e5ad66079815a773169f3d8408e3ea5ec34a500377370284434fe2a73d916141c2c478d4dd7c8c976fc29f26e6e7875a80346dd630f1bf1008db5c8c59b786fbd3c4c84f38f237f8698938b61504af1fb60d5a31620e2dcf3562e41335c1da254634c22e2e6035d61aee2c8f091039c9e853dc85ac6d201e03274d21f6525cc30259d5f9a53044b496168802aded96ee2867a244d39a2f22cc3f5a35785548e41d3e11413ce791ed2390d105175d34ab176451e74f9d1f1c79f7062422ad1f9d7af62333fa0ff65c4e33bef56b811f05cb1ff0028a6b39fd94598f4b8fe2581e4516854c70f8bd4391e68d354814e8f2a32ba88072fe6e6822a625c33a7e3b3aa2dea61174e9ca95061d80b60dfd785ea6c188b0cf65ca58840e083c3d547aede6243a1780903fd6b80cc4aded69ae7618a58e8f770499589199b7346be6d235c91a3e89eed8529d4dbc528f490cf4c82a7f4037050d22c1ba4e87d47c9f686f3df69c82c9f1309b264d98e494c7e13bb6abbc4f3e2131c6855285179b82e0237e785e9c0d5674e9a97cf3a0d5cf54324d164519e24c86d46f2e69d5a04ffd2138042e7765cd677596ae16d5c9a47b1ec07b847740f279ce81d72feed92215af141dbf5f0eed6d79266d561bc608d03c5b9ae156d4284b3ef198da83e69d774392e59b1e96f5687cb43487d19c55e9dc9de2abcf1d47f45e83ed8db5f0e167534d9dad4e60ea863b0f4ed5084ae09397ea6adf9bf621cbce2f46e8b402506d25013ad465d56e0fc40a933fc68aebea2d2d13ef9173b2d35f5f7573cbb5a57cfe5a26d278a032e72e782723d64c951a91aa7bebd9a6badd848526107539acc87d8cdb17b00c35d82c6d1001a40ddb34682bd5727a8b85fa030ba3fffc0417e90fa4b20b6b06eb60d4d71f89637e4ad0d3a943dd5a0db7ab935c3a758ebbdf94f20ce676172707eb386d17f70b70285f0ef4c3a457c6cc6c563180c63b0d4e274ca29a7121d8be8cbbba6e3bc4bcaddc5c5a39f0a68eb96b4aebd2079662c337f69c09913d141a0d19235f0119b6066c975714406e23004119b075bf1323fa5122479c0590804c1981ce0ea3df65deb86e305792c2b85f81936ad322daed83a1609730a44bfee79bcbe672cb4cd3709a8083ba0b1c520745b45ed806b0fe17b68c13bf63aa128febcaccd9f4073367871afd1357d394b2a1403496c096daf8aa5e1b05d3cc3a2001c2150a74540d0d963e65c9c3f49bd39f2d12f4baa976c898d39921de7dbeba178147d656f273d1ca9d35e64072d48a15904801afb85e63d3a7dc18e728309e3e1da6770d16d7cd67623a1000c3eade88e67bb019595f3b83ac33efd5ec31765f5ca9f2e8fe16f9b9d26aaa73340c4998431d1d973cb94929c88114f3c4ed686915b4add03ffb74e4ce26ee6ef6070c5343900e0e859920d9d9981fadd3fc2ee30763a8713cb8b9fa2ee747f20c7e5016703351672138b2ac14175000b9d88ae3b49d0b8c2115c0ca6016245683323c36135e13611f3bc88428a2b81da263efdbd8d9db4aa3de2d07cd9453dcce1302f2ae0d39c620a888bd830b3fbca1fdaae5bd15fa8d36efd4ee0ffe41ee4068694ad32339f48c78fbdd0ececf7830066f0c3f02c2b70742f9ba3cf3f609b76831b91db1412ff496f6bdc1b07d3f40ba829bc2a74a7fb026893b8cb9b68f6cc66d5b1eaea6ca4ae18f3a66284658999d8f188071cc7091b40127d7aaef880197367c144154ab6309e24e9b7597704cf1628d2a47d2a57cf4797db049e3b9a01bf5bc60a25fe6897d668bbd205d3b3e9ac4387d8773aa299bf12bf4d1e8c1bc84643e2118cc4e71d36911fd85dca8a258e4cb35f753b5b33564fd0c01007886e98eadd70eb1299cfbc14f7e60b2e47a60846a6b11c639b78b1b0a06a95e48aa8e71f58b34363032ae1cbcdc00486862e9a101fdb3cd18d93d34199cc053d161016af742b57e1aa0c502006227209e639a8786bc86de35bb1e95993fe8996ebb1fedcbd9083890af4d9e9d376734251caa3991c6774113858043989270f08d499385dc9c3f036750bfc95efabaad9535daac52b935f91bf203e1f6c8741d82fc9878874629ce703cc95a13d6721016f0090df771ce678c03930172f4036196ca09af213ff742e77311961a19c532a7117409f13660da2f3f3449283c0361c209b1e7113717a80e77ed6434ca72f7990ad1c43e61180c32686694e072b7c2ddd248bb6f65c9c85cedad5fc275b689458893e40e5f4c9b1f7d14142a97e6fb90568c261f70c348a2d731579130755382b5113ab2369977cbbb642f561e2bb16cfe240ced2e282f7ec7c3471d9e7d04da0cc10c6952784c490349c8efeddd4e52c3a8c9cd25c4f03ded3086efa94afd83537763c2416547d57f429af2c1841d11ee0d6319656d0a21ebd484225382268f28deb6e375929c1f1d6bf8eec4efe96d7672972f533caa5902cb4ca41b8fabd9a4e3c5f4b28dac1cfd37b6d372f7e324036e45fe76033aef2fe613bae432b077efe3f5f8e9d46ce7caa95ae464acd0280f7c3cc20a1e6a8a25b417b881d76e47d1a319c273c21079d2bc914f6b967cea2735916dacb2dec6b048333fb6f57b913daa226649ef756bf58f940c70a92bdbd795c4fe925ab6c21c0e5775827a815c46915bb0add8e03c33ea472c5707cf76f1e5c729a29c493d1bdfc7a9402389d12386381d86fe3228cee0a26c3761fe184114cc23e101479dad3965602e6596d1b8bf1783d032c2e75c7f5ce191802693c5ec8e0cf721291b95d258b12f84db549a71275a58a09e294239c0280cbef7d9c61a1139707c1b122de53e7987d9b7f2dc2e31b512cde74efdd60e94c81263347afb3eea2d9505f874897aade9592727aa20104026407e3f57d7cf1d8daea6aedd47192427b2bebac8b80c4c708db47870e3bbd9ebf8bc14b01cad21db592205216dc199b3064cdb6a988be6e78877abda8a2ffd9d2aa707c79ae952635f7caadad709d27563f41f3b6b7eb097253e2d38eaa9cf9df80c82f5331df54ba28ef2438f3fe2d5d31df86881677ee488cdf8b8545aa63be51f31000b4e987a12d9ecfa17e625d0d7f215ad2d0c54274f802e48a71132a2d374b2caf8ec06a4939f816c96164daadc0cbd9c81b7e716ba7fa4ec77810bfa418f30702bc3c1d4853e96649977ed27ec4ff35aaf97fa32e32d0439dae3089a3977cd27868d27341316c6297618513980861a55cc3132bf456596422a129dc2ce2ae56872cb8c7519c4ddab5d6c17dd7bc2ab49810288a1c257bb6bbad781036603e6e692966477e485e42e61856839c1538500f6d123f6ab8646ae3e502f80b7acfcca2c93c690c6fdbf8d49cd51835b82ab162dafd9a1be94afef409cea1d274f2a4d223155a105a5092a69f89028ee5fc739c92952dbbe7e5be7c69dbca5122971b4e8fdaae24b4c19abaf8a629589d6e07a54484531d67e5d3d45436ebce32621a7f068f530f903bd2afb031bbe5b85e17385b7e86f22b97dd79f49753deb827593732470d05b42a769dab2c271b2c22a5c6193ea2b943039b573d484b8f8d4f089b30d50983826710434907dde7b6b93ab113020d7e6e8945c7f772013218c2e275eb2637acac706ba02857411c161c5fd21c7fdec8842e2f6adbe40f623d82d3f1c2a86a9b4341664ee8cf7a2e31fb9efe5b372fd3463a58ac5f7de99385733e6d5c61d7a52c8ed9aa2e9404fac005b472fbd2f6c31c476359292e29508b355509b9f06a55a5d4c10015c8a653f1e5409b209237ee3b9cf797cb5bef9f36bf0b97b24b659bee7a49c7800884a180b6003ff7e07920222a9d2a311494422d314000c32dcce4151e94b01309ec9e56311047a1e248d86e384389354029eef12175cd27dca133a4bd3cb0581916cc9907a7aeda63ac3c683bb7d41ca69abb304536e9b10a20051f4e91f2ba94edb6d9f44b9f3598194e8862cc3afa374cee84e5e216196274de5bb63ca2167ccaf1fa2e6272d5631e469e25f2364b547f7996febd8b1ee3a767ba77b1ae55a98902a0388edf5e1281536b5706c7efd8006d9c764bcc64729d8f0f34f2da3dabd4ec2c328d7a98179163ca21a349cb03aaae11916c08098a694412d4124d85b7088ebda9daa7958669b105fdadba74bce1ccec1cd156809ff84124f291652d0ca0d33c690a40c0163ac6e7e5d066c342fd66241d2ebb37daf46fe63d74ba438e7c929c880a2772eba68d92b5cdc986d161588214409569c75f6fbf4b1ab6d9b10ee6c6d2d300996ec4552937f6730b514be2d09e5200694cba830aa99c007d8ded9d3073f705ec5ce25ecc839c05b2b160e29d8ef5671b54a81ecf6f5a5a88af8824a47a43dab66694c8ac9b5ff426393f926ec9b13913527470085e4b7f042f612527e8f103b4c6347a395eb1c40517b21a455a4a488dd1290f878f3baf75e9c91eac7792d1996ab2e72df636fa41cda449da4d9a00483d135112a8fb7751675d19c0295e821bf3409d4f5695790a972e227839594dfc27ce662712648a014945113c8f34985750eaf1e24994da7f2676194c91fa755d2cc6111bfc08e8d904b934568b414b9a7535b33df1c18373e87154b7b6a706b9714120d37508c40069d1ec8dadc89117fda4daacc95a4eefd81265031330ada549efb2c0aee5121dcc31fcc003bb45b9d82d23b7d54ecd5c4f2537fe34b4573f021357052352862cdc91955e820ea1a514d483c299e80d06827b6aeb473339265a311b002ea91ea0e32637b858a2b116acc56064f60202c7a67e242c1d224f8aa2c39d9ea59b8e6b4ff6a3090eac2393e4102e9599df2f4ed4f03714f305c455593ffa970be306cfa2a7cd2aa08b6206254d0e678a75584a08c31fd1d437100b847e0f70fbcff4e6df3c0cb9ac4f987f04a0572421fe8946447a275aa363082446c120068d802daa436f287ca329c08deb13400a2b35a2300a5041c59824583546669703f7e2f71c1b5cd0f8036ffc4e44030d2f8a5db927b78b955d0f80a25999cd5bc86e3366bcd5fb2b15a5849a692d3f4e05cf07ab7cb7ddd559cdabf8ce590473dd9b5665c619e1c954e8691cec616ad2c71c8e340cecaa944c9e7437c7245f3ed9d50130b70aac5beb1e03f604f952449144f09ed8bfc7cb0516afb47476b3c35c8d5dfe60cb5c209e3a67b35160181fda6a4c34419b3d5a4648ca0366ca5aad15605c5d1682c9f28fc6a4d5c45d374c656b46b7665a66c51761cdd1b223b5ed0a84d1b9bd37fbf83a429fbf846e0d22e350e18815a95de013d67d87c611eaba6ecee849d97af0ceb9738973feb8b82f17ccefe00362d849a170ade0545201c0accbd5937bfe5e1067d6eb96cd32cf9b2d62c7598f389feaa561af02fc6c49f9947a15dd7d4560d5bdb0f279ab1c619b7b59f14590ec2cc4dd52bcfb658c620b23cdd98e1b95b5c7e9df184201b7b54e763900224bda97e141b13191b8e96a76638d5b8d9a6f9a72e08f3d697fc37654d22eca311caaeb22f33614b88e795f91c721500a922d8d370bebfc66094f2844b1ee03a6f1e8ee76dde266843cd5911670fee20d8c4a5a380554efe2fb51b27380ad7ca4146c03d90d57ede9c811ade0e314039eaf6d7fccb460c7cf9daf9ac9b97a74904e0ef6a697c31ec5efb8878adff3968f401fbef879ce2dcad20411804e09801011cb65bdb5faa047823172f7aaa805c1bc5df704d49c4f6eed7aba5a087869a62a49f432f8d9b433d9ac7a3263c165d105a567d87eb8d48d6e76de2fcf64b1829c5b8720c000c8420f638ccf33a843017ff4152f474e2ce8a7f52999b664a80e081618ac20307ffdeace50a242866ad5df5f838c41f5b3d8816f8fb744126200bc383f7c8988e9ef2a1dfc9bbfb035e798420520159bfb5a97eba6e0c80225a0df7b764b8792d19c162be09b22b093e3f19797b681281642aa64194ba3ec41b838dcfa5c77fd9e9821a72a132297fc70039691c1762ad373fd6779d0649d9631fb6030765068ec7ad4f1c21b1669177cde3c3446973f1e700c0afd7e27b0f0d8155bd983aa3255c299c73aed8de1f14a8bef9d2747652a10bb4889e607331ee06db04d82cabd81114310e9aca5c7882c6c80cf7078f05a949a618b3c9abc20d764c917cf29e6857451a4498a4856acaaaeeb302978c19e1318cfa099c8376d0e17d22f7a21f446f586c3e7450c69c80dda80188d59b4cb31e89ded101c3ea152b2dc1e4646263de5293613551a83c9863e4b6742537a648deb2436c263932b25077f38924afb4a6e5d2c41ae09d0d4c200dd3f5166b41e6d1a30b51c0074256a70076e7514972a8a9c487fce3c586c8e06dd590e9e982110929238607e32738830ce271c1400e93013fcc6ec4b8fc88b0f20ca021310646d0db47dc66e453a8ecb7a8c1d91c49b8608dbb9184a48c929d44b5ee3871a1adb11c7e778383a1dd7ae87018c643402601a643ebe5c268f773032cac33540332a2dee276a2afbe4769ceda4806843bf75a6420d2113e1d01e009b847f60fdd385e8397f9fe5e6146054cb0ebc13c614338886345749419cddefc18d262d32de37000876cf0fe986df392477030b6556e206530095ea14eda1bd223844f08a45c4b6e2d361b4f3d8c4ab25fdc16ce6b6c4e5fb66a1eeff3a80eaa06302a2bfb826ee23091baa11965380505792e2adcca2a05f5143de8dab2a5fbc60626dcb22b418ab7702bc22d758282ba47a6aedcd2110200692fe4d04b73115d60a7dc17d1fa04c5e92e94edb8bdeda2453a596c0e48eeea7d5a1ba6c91e8dcfd692bfbe81822096dd8c6ece35a3cc286f5b73ffc20125693b70a171965568b8c93adaef04dfb8667b2700fd3d5d6f44a69b0ed4e01ca0b703833bca003de6083a1b718a513b57ac37926080157b10c0d588dc84f22d9dce3225a03e14f66f9aa0c662602fa31730fbdee868a838abbae2c466cb14cfafcc81b18a4d711a07dbf391c025a9927766df7eb2022cd6b4814322507c3c70fd8b94fdff5573bd33a696f62fb8da6899b9fe45c50dc819fd90f0dc5a8d48ac9e09fd1d60f8cbc77719a8963c1d69b39e2557eaaf165ed10da60dbff697609e8ae4fdf66eb6c064f9d9a740d9bfa8a59136dfd79febfc6912ec2f5ccf7359461154889d908106b21c42e4ec23394a22a6af954460f0325a3d14369b21e1aae26160e177e2a7ce241b984e70bc85db565f3d88b123cc5880563fbd3f3f55557fd13dd04487ace35f5337b6cfe5e4531a74c67774cb9ad34646503123e1e036d26f6ec97559100911b7b0ec7fd4519b7ee74778b7344af96848c2e45d8b470fae2adc467d2751293cdc6cc775e02b093d267fb89f52de48628b75b864a97da136bb4ca8f76c72cbc44f72e4763cd49fd9fb3e2c605ebfa229191bbe067ac4aa7fb3a2950aa602a7cd9be927430732ec8ab2b438784cc7fb59ee46f3d50324b52f3d61950716ebe6e735cd11b977e69612d84b45456e3cea2e835bd283b9d3cf7039c02f7c395a2c01bdd3119e19bd96bd37e5818e2b2ebd091b71a89ecbab95f4ebddc77bb31cfa51a33efb4115a1c048a3110dc0839be7b961bafd05042142742f0c0dee004d749ac19c233727de7138542eeeb34ba5d5dd809fe586ff3a412e8c7a9785c87c7fda720ee11f8b85a80a4d20ce06d2165e5c2c836d413af174d1c28b5936e60265215f01e7e249307d545875434b1602dbfee2832538e993ccf0f8c4e1bf5accd08ef28227c59c7ff298afac999e0683b13b9995c4f078cc8f80dce518821eb58f5ee64f02de36e99cf68fd027dde032c1a3a7e8213aee99e59cd765e98891f0b154896070894d42275bb14489222e7a24dd7b77ede2bb07e92b94529aaf29648d40063f7ee4dadb99cd0d071939550cd3203091468571e00decfdbcc6f3050dcefa33c2bd153fd9cb78c718b9d943028ab490fba408d7fc10871bd3fdafcd4b3f66ac97bf872ef59bf5cc5645984c85dcc2ff268bb90ea61909341420a14e22da69145ec6b1654ded3d0dea7de148b662ed601c169067f083c05602aa54bdcc224818a33d5bfbb493087660f34fdf5cdd7a8b4945881e781208b2cec1b2a21a4a8f504b5e529d514cd187f0f0b9228d70c8ffaf7501188677f11b0e4531860434644040e4464b29788c86d3f1058c57a8ccaf845429b5900f69e5ada3f3269bbb4acdff6766066be386559d7c835367dbb670356c6e8fa304c711abefa1ae8dd7ccaaab807f268af3298ea86387ce2aa80f2fe95c623a1cc1c5065fdbebcdb433ca1dece1c8ee70904c2e1c7afb4a53a1856557fcba2e5262a02fded1313a44e7254733fca078777ca0401ad8dbac0ca75c71d73d49025536793b92d353cc3d13facba68f9c6378bf6db5f38b63f23a2d393f9ab78d62bb3497b444eeeb705d53b600e590c46ee55f4f554b69c132ca6a6d88702c1ea737eea3bcb09694f2a4a494bbc36d8903969dcc1b63831047a6b0ec9b40bf0d7c197977b41aa056936b6664164df15ca44bfa3e54781f56e4384fb65f087e8d0484eaf7fc6bbcfbddc53e55e8300cb8d422b5e2a2c6d4ff817853fc828faeab4933025f634ace1104cff99b6045e36cc31698e18bd29b17c4827d4d8412e6c10b1eff49e5a66ea55c30346176726a0eb78c0a460d671d086a054ce00181bb30d1d83339148efc78701566a710d79776dd634e21c816a15f7ced515c793133e5e9d2758d15c59e71bab8ad2e06b2f2584bc48727bba30db95a4369ec158a3dd96368a60d906a66f971c4e441ac8828cea7706b519718b26c0801f13a4f8ea1f6193d5db621d00f3d4b769db7b025e0184b8111ac75bf4d76fb90a10474cb7acf7bcf47630bd6d654ae07b059d5c832cd84d6bd3f6e8312083115e1607358b148152dcdbef29e87b769df46d2aefb2c63dce2aacb7f8b2f1d1a552ce2f98958d12d3d31943707b986d46348b3c3ada822d0518a03b329af3bc2b7ba8ba3e9f0e54926cf8736e87268499d30cd7febdf11958cc45f1ad7bac1c363b39b93568de5c58a8ffdf3129066451629aa8514451ffc370995bd781982f5e994e0bb62644742808cce5bc073e3ca85fc557d5052311b9924fbe74b76d71ce5835dac8a548bcbaf35d4a6ec3fc1a35b7d908290354838bd02264ab73bfd534bdb036cd7640e4340ba05454789952fdf4542bd6cc115005c2fcd2b97707b7caabda6237b23527305ff7bd65f304d85a31c2d717b8b071dd22cee6347a1e89138967552ea27955b58a4c68abaa9ad04892a81b10f3cf993e470e7510aceb307a4dcc5acbd33a3ba3904f5340976af1d7db438d988dd610a7c9e4b63c27088e5c2fb6a93cf01a098fddce243d309eadcac1a7ad7d4b1a69bf8be07d3f29333cae5262d245158e9ebf6215edc96e9d36a45e5343845b30afd106a73f3af0d9592f7e19a7027627b4864d2ca17827d9cfc5e1e40cb8467ed0ea2f04ec34fdf9dd73d0c01b684ff9cacf935a4efff28374d152c2c73130583cc6c88fc3391da78bcd3941ea200ce95252c7f3e7c363710cad250e68c8b273b812cf60ed3fee8151676810994229a53f696ca75b8c58db065120b86cdc537259431c9d7fe33652a63078bc5904c39fc5bb48dcdee7c3b8e324d2f6d8555458341104a14184e99855cb77a1b0a1d3565e5579ae72c36365f19691d10be00838659202084ba42246ddb80302cb5228c1149c5414a5dff688828b41597cf809afbef3ad2cd75578605b60d564ef955dc87d76bc39abdccb1e3b4a8cc7b5b6e2cfb6a751d6157dd58d7b7d584074615e174b5d2764deb87a062756c71cd66d09c016b9464ebba735a2b39deb2d5f384bef12a5c386f0d6de696163b7001eceb85a78a312f9870eebe18ea4d9e4c9a23ad4c6e5eafab08b91cb47bdf4c50d4a0c4331b134a9448d9f32858b4900bed55730d01ea0c03e25216c2be843ac327b3b8b0a4ced81fc20532ca9f694013dfd2a393935b5dedaa6d6a5235a7ccc0fc0b2c33a990fa70a304b813fb78007177e3200eaf2ea92a0b0e8585c133d12fe37262df58febd184b59b00452ffa0e0b469cdf425f036d7d68e2846177a8ab9e56f7695a86f014bbd40a551a2cd73d4f1cc94a1f439aeb0f140d2fb29066a36ddbd12aa92befa14600c654cde8ef1cb4c52be85e93160caaab0ee78e1076cfe3dfc73fec4771dec200b47de0c44ee85ce2bf170397680249b844dfd86558bd1ead0d9587e5b65123cfaf73d1ec25c67ad1b3c61f4f38fb6d960bcb482c9c514531f3adcb2f5f0cc91280fd920090a36ea7e526a038c866aafb163399e7b90cbbddb07b9a16897ca3440e04d789d1003dd8e093bfdefae836a0502d1a6a2367ff8a496687eae4160f64bdfaa02bb2eeb5ee627f11ea360fd14ac4dbce3fe4d9d1b492caacbcccdeca6d2574b06626c013f57374b314d96746d2e20d17e85a56e211ef90b1b3c139879252b69dca557c6c2ff93691afeb4576f0f821233916a5bf4416e027aacceba10bee95690222c0acd5b0a936778b257103165202d259889cd9d90116057e96450b4ae356a65ad6941e92d133cb5c63170731414dd65f6f4b78ad04346c25bf88cc2cc3e7e0086cf8ed0e355623cd5872a5c2f0c039071049aa85f551cba45bf542c039b322ccaf42ebf850fd28fca1263763ebfe53663a3f749f8295df1db4bf7956a9ecbaf16fa6a0b3e37096f12f118b5cc465b5581757b2722681ccd46175aa2a47cde469144a7389b05190b51f641445ba7a3024a8ab24b7f2766d179529260d22f1ebfd1a9c2c8d0e01a4550892748f349389b069846eff1daa9910fb1910f7c4768bd040026e5e3fc78b2607a2435e3239a969b94925e5a36f8915b0eea1fab53124afdccfb0478fac3c778ac83369271b245eb3e5fdd2538b6b6a0fa6121b0de85805ff35c656efd5136033c58d12dc0151db5475eb85e857840b090ff99b898c03f368ecc0a7e16f7cf91a6c48619d5033a0cbb56f748755c802363561bc6d7e9271c9a179a0fb3fd823d7f8902972eb2a84941318b6efe00614cc123457f01d4cee96c0881e83dd866d809e36401e503cd25b9c1e84afa371a0151777e79ec8f0b7cbc49f2f322bcf04435e7dde7e325757838cd60b15a67b0c8152a1ee33e19efd64e4281e2a98a53bb4317b7b0e200f78f100e724a878b202f40ea720f6c8612986c3338ab3388412cb3e46e953bc7f5fd6acfd0aa395e5dce83a37ce7eef015c0f961fdd429f2fccfab59086a5f467ab236f92aebbc1c2c86b361ec1dd6960ed6ef435fd65abb398345acd5db80ac766bbf9a4ac1230d1cb73da482cf6c41c703cc776cee281c5d0b1d473821737b7137981de3f9909b9694630afca1366907fb4098185c75abf63716f73b7a63951a8b3b3fcb1a5bf1326a721bb2bcb3bbd8b3b71ac2ad475a843667ad7483f4cc78827783544e63d8fb615daba56cf1caf86745001de0718cc1d5cc87538d96f0df4f7939a693a875b4a84f11ec2652fe9d13ef379a933258a8cfc4b1168b01732271fb0dde330a1e3ede4fb8952e9756a0b3c5f933607e6fb44283018d609b1a2fdc034bea98dba435aa6e93daa0345fbb29a65640916726c5765a2d1a5696967800b5f2605ac0c865e7f3b3d3a67f47b2a5b182ee86bbcf16c7127c4daea165c5f728be8fd9b769f8991e5643433a48fec75a96ed93e136fdbdd0d9bb44b11b5eb1a2921c48ffc90022718c6e69609f95c8a19a0b5c0c41ac24975bac83879a20d592308c6b4b1439b235c302a27f31591f4187dea036cdd0c465da043ac6a587f54a23e978912ec6320ecf620e8cf9b25ee09f3b88b92d98825485d4121ce7c45e441c468b75abd2964607fe2088763a123b766ccb19811c488c3d9f6e832ac068f48b0f00c6ebe1a521276bf3b674a8722982d6d6fd1377270e9d5357b44d11fe28aee789e16cd6c0a6cca854bed278950d0d4a1e318782d926b36d825460a158c7f6ec7d4787d39bf1821b21e9440c212cce280fcb8dc188759ea5e6b48b450d53fe8e2eda8528cbd8555cad6f0eccf9bb6e7eb5ef65a48b9f0a974df47b7d92cd94d0317aa30b9cf850f0a69e4375301542ca5600f25a873dd0d9268b8984f8b37baff3da852a727c7c97eb555145ab507aea91d17a9462a1ce1e9508c9b15029edd16e8fa9625626a4235ef733706d883c110f49e4dce2d7071a50e884278ce2d9ff03037f00aa21140ad8b7cb3804bc458afc82fb6071810b3fa0c0c6cf71a38a16e129eefcee5005ef98df5d50bda36ec82cfa4d16ffe119a8d61cad760143514d5565437781e5d33ff6c9ee43d704760009cd3ae6dc7c931940b1d850c9220a5c446ae97a5d6a1c36b84c4446b651e6cce3abe45c48199dceb241d0cd035dd024289c009fcfb04986671d4a8db99de2eb01b712afc900882e4809831c9f0707f2972a26c1444c02362623ec72fe7ca632f126cf31a676c6566a4594c397a3f447ac8c55089533cf0f0e8d8220d8228cfaf811fd0439ed5f65c1810ec53149173312e07f45e4ebc4e4e9967b3948a41692649a42164381a73f9939eb61bad6d7524ef8ce178848c46de58771ac17b5f42395484b632aaf4e3d20b4370ffdb4b15278f834ffa3a0e384f7ea9d089e5ccbe574201b44ac40ea36a4ef5294327001ff9780854a82e7bf64d0d6e196f2b5fd4e7a0f4ce30be5fc99c622c0c4c0110767d78704b61758820a703dc0ff96998849d31e08b45bb6230f745fbdbc6496679bb7474dcf69cd249e8f4037d0a277a7d856ad7477ed06853aab269a303a723ff1e3f24e3e252d37868f0a504da616553d82d3bcb73b467d648b7087dc8b3d3a90eae60e3b40e8d1efd55bc9f9a5fae3995e2b6d108947e5c5845bd801f97ca522464cb5dc6e6da6d134d4a281a63101f77d8cc40390b765e12422496550a592a3521750cd2b06e3c76ce8a6499b54efb32839a87274406b32bfdea8c20282f8e0d9831c957e21d1d9fadfdb8e342f2906512fc942b5b5ce54ee4b9dc52ce2f586967c6549cfd07bdbc3dc7ad4c3188d8c5d9552211847265c50a869303a5727aa888e7381c97402fef5d786f1c23490447111a685e4dbdc6f17396dceef76cd8fac1f1c933d098b094d694655e11e4fcb4e82efdc443fe5045c5a4aa06be6cd84e7a8c90346568d756b37a76deb61c0e58f672db5775a2ee24c1cdcfe08d4fa985cfb6e346869ded1b55dbddb9df3232d86a17d42e9606345d6a5943d10a0350984525b9af684f252d55a6fdc7411ebb2010eee8fceaef4520f6d0d080e6c8e6391a632c0e6ca8a2acbf6f841b1f2564c51cdf543ccbdc0e5137ab528a143f7857bb781f1cf002aaa0f5dac192c44c57040a7236cd3eaae1eacc5a94c88a7fce5175cb213d80074c882f12267cf864cc5b2cd594524a90caf59d841341cfea653ec7f41de1c798921ae152ea5b218958f61babaf9ff6bf09ea3fc992c26d7e24ad1993503e6af2ad7f4e8194ca501d8f129a4fbdff4c5210d02a6b48c40fe8058646431a429a84259729c9f311c39bcacb96ecd51931b9daa25e79739f4e3e6727eb299b1bb1addde9ecd3c9255a2949b362c007382097c9e9f10e5347cd1e9ac322857b0b428b0f73c3845a424010ee17a766cfc1c47daacc2825829040bbf15042a4833a5df744f84e14458b4d01302ef8abdce1de1ed9e3fd5776a5e3be79da1831c46d098dea12cf672d1f8c10afa151d29918bd2c415f6652807e2e5a7f76e80bf0bb81091dac329f4b4423f566f482b6dcad07559f1c0eea0551400a17c09c811a48d7e7bc8eb1fb6b2eba77e03199872f847764a9e6733534ef690d9080ff408f0ebe19e2fed193fa46ba49aa7f05d2e146a55f9737ff45f48bfed122062e46095f88bbe8887ef90d49f4e1a9cc5c2f8b4f7ac4f7d1c907e642527cfa0f5f8a189f94d40c187b340ccbcb7c2cf3ea60f809443d9f10a121443c182b09e5f837452a8fbb81a49a853a0d15d7d57a922bf942054f01297f6121602c0425b7f5481aa43219bf4dbcb30250a8efff1f92d3f32ceecd06d29d79af70cf96e4f1d8511499e23cc6894ebaf26e6a5452df9e4f9a9be55e8d1a93d4a83dea5e4788bb354b6cf0fc1a9ddf0842f23dfdd13d8a30af198f7fee6cc796969285a5dd815030a8ca48d053151afb9e0ca8f9b492ddb21f13cfa0ce55cc44684bb9b918dc872332d1924893d9e2549646396e1ccbc8ebfb3dde16f195e205a4c5a845d7cb00bb40781259b288174442fc1c774b1bba2ffaab2cac18648992f1ccd119496df951c88f012397cec0598dde7615871cc6c0fe8840aad050a221768bca7a232b9fe80f02bb90f6982743afbdac1e35365fa98e51eaf44496a4d33049c86031a9dea875d30d6594d0789d965f86e0a2e61c18d036c3bd19ba5eea09ea50e04d432953762fea15531d3559d823336e482798b70c68b03300880000886e2b81f6cc624a831ffbad315746a2af3dfe05e3a868d5ddeb558896defb626b794322529034e08bb085208ee9209b1bb6e0c2855e1489353cf72479a179496bf8fd32de0f843226d1144d336b9d573e02a957a97aefbc9f2f8fda57a168861fe7b79172f61313f615ec24f01decb7c8f9877110e617dcccb84439c0cf9de9b38a7521f036277a59e056277a9de0355f8471c7976e00f79a4523d4ebba8c2fb181ce5133925a9e4542af51324a20269a783451576bfb1805fea59bed4b77c0cf8a53ee665de03bfd47befe23ff04bfdf72f9e057ea970c7d401663d0c2af3d4c5d39f46fe7af1d4fb8979daf294e5e9f7f461bc702133ff5520cd1f987a1698fa18507aaa7b9cf6d45c81e3cc1c0e478994f16f1b903ccaa9bf5dc3f25ff7314f04e67dcb637f7d2ca18eec7bd67f5df829c0fb98e7e17d4cb8e384e577bef0eb9e08cc6b097978acf0eb421dd924f242ec2e26b08b9779182fc45df47fc7098f98ff7ec7090fef637ec7098f17ef3d2bdc71c283f530c21d1feea2cffa2fdc71c2e37b56b84343b873b493e32efa2f92887e944594154aa7312176177e09c354c9850c11229926e357813fe411d6eeaf80dab6ddd46f620cebe70b65d9840a7cf238899a50c12c6f93f3e6cc5df43f90f5de138111818144605b731f4804c60289c062c009f374e8c858a1174e2a79ce30c55df9b5507f2ead1aa605a7ef9efbd27117fd19c629eb190fe051ccd4a9f6f4677840d7847e7ad9f5a976ad375b6abb3d48dbfe4d7adbfb227a3fa59003a663873f68e41bfad0f1002e004a22e51733c035cc097198e0512eed7c7701b60d0cd233e62efa960bd22e0aa4451ac3a3b83d45f96b0b71347d54289e5e5c304387a0086248b6da1f3659037fc0a35cca408208d9e99a188f06afb4280e6c37742ccb97394614d71ff5327cc0a2bf869cde1ff58f1aa2fdfd9126a3c24f01f6ef7ff279d4f7af5af8c99087fdfbf62fea3709db9c8bc103ec5d0c13a6bd043b7ff283ccfc71cf43fbd3773ff3053ff9439c6c3fe4f4da0f71d2fd10ed4fefe027c38f0b799c5e0b3ff9b02de4719ffe101a7e32fc16512fba0b45b302fc6dafbd847d9ba6d13f7d8f21fef7254ca2502d5493229444a0fe0faf5225fb77a7a3864d9ac6623077f72a55605da54a952a28008a4093fa367895a6fc434ec94de403cdcec76440f798728a9c32e50894e6d07fa16b5499feb6529bc49ca7bfbdd3f1e98457f6c720611d7ae8b9cbfe0d3f1dfb5f0c1266c31d273c7ec2ee7f3d26ecfe84ddfbfd4386dc50f48a68a10df59fdfa35f767bd0d6609398930874bb30bfc3cf85dc61045c6822734aa008264e98b4306a82053e593621e49391b26c8288a17cbb4636c102992af7e326b1fa9201588631daa386311a7e3d268cfe84d170c7490c72f6983af3a7165bcb1a2c6c521299f300483e6c4747464645454423067226610072c0934936f27964239720e2414f6c12f95432974c2642fde2e49c48a6aca9a9b492f89e1719c1e40c3853a8c5b9b443f7186511bb72729efcd9833ce2e9031e1fea989126b79216fb451b50f40cc0f7dbf80bc703e0fb6900d23535be3f48d7d098f132bec6f535e2c7f89ef9fed9d335287cb74e08c1ff86f1fde2dbc5f74fa13914f3cdfafef904e6fb2794ae79f9fe69349f98472ddf2cdffd33a96bf0f74fa5b934a970df1baa67f2e67e3f8e8ebf68fe868db70100690000aa00d2e0aefe1a201030488bfd3340212df6cb007740119c3270ce66c089046c8153497f08ce252df683e0fc797032e97f01ce206690cc07cea17904383d703e01671138a1b8807305cea32985059c485392fa53e0a4024e267036750cec13d8b34602e2e884a32c92479ea50ce713368c704f2bc91136b1f1b5da08bba79774d3e853948db331051ebd08cca45c037d6ea604f4479b15ba66fc319372d8e47ed750fa14b4c9b1044cdf05aac36316dd8a430783bc41775ab8d5bf470b4ce99eb23a355ad8b255b745eaa0a5395aec1bea84325c7184878508f2287a7987ba4550502c8845c4f2de6af7f4be62f5b07ec5f5b0582e2d2fffc2f22d2c2d302c600b4cf8b900f3aa6701e6a545c502ba7c0be8e2c20a776866ad42215c8f6a47480f12ae67944c3d5c0feb715a88bf58210dac984f4685c3f2b205af6c2c20101a587e94492c2190205d0384e5c79955214b28694875a9ef680022a487eb51b13c4ee3e095969045055259c3512f43a93d06c714934c62c1fb13386ae1ec219a3d4439886c6c609a9b22060ff8f348696b62e0c11df00a8e93b3c9508964c5d20b15f86b05c5162738c3a3249a4d450a031cd279657ce2f80944a1266b06d2e8074efcf8e0c39c53ccc9c1c9215a2127076705fa72e6f9ab21f74d6e9b44a1b64749140ab5a15e6e1bcadd7d3ad52c0de5db0751eeee1beaef76effb751ba44883c3266ff3044416b1ed01d95fd6777094c93f64b20b2af057aec73ba93c01fed38f58c391c9f51d861198d6170e6d95453b6593fb1594714dff293201bf880506f2288d5644983b011eb1d7a24dd23489c043041e2210414af9389f71c09c16e5afd0a2cc6951d67cfd14fce8cb5c6905390aded5e32538074708f86964def3686c69a971401e3cdad87072bcb98804b84915fd5543cf06a1a21058768dbc225f91373902c941e892e53ce78527b9bbdc2f0965a9a3e02088201c346a290a0ea4b49c3638c8fd384f7267af762cc82b9ad4f7ce0579459ef567c7c3090da5ccbb6c76400417e4511ead3ca86200fb807d489244d6b7dfc38738e1314476b36e46c58e1e7ae83144f61822bdf69a284f20ea9cb5d21f5fa7d6b9c2f5561f252528509c10c97b3a695af855a4f0bdf3745a2541c315818194268090650164d904103f36aac04e64fd390ac4d24e870d5bbc1ac0e3e7ae79d189a445d944d3b4f6511a116120043a902571916594a522923c7992244937eb66386de307742ac1329e2b3b88672d1c89f4df7075294b8a944de41f984be015c65cbdb749f7feea42b16eb683c17f9c99d3341947475568f6d46251e19554c7c170880e905314838e11037c1c396006a41fb3a21f1a0fe6c565d5c2a2c22ba98edb282084a9482c7664364bd2d3b3c48708cc3c05a1d9d3e39554c76d2718ce01728a62d03192c1875684c846e68b6179302f2eab1616155e4975dca6a16e9d470c191922fa268a08a9564b354954799e0706cf0b1e173c323c1f4f0c0f8bc7e381e179e171e159f1b4f0b0f0a87830cf0a4f8aa7e3e178361e14cfe5d178a638e239426d8eda5c5aebd6de6b51d46b7b3d692fdd9eee448231bba453d5c4550b8b0aafa43a2e061d2306f83872c00c48230d49370059daf1919a3dc9a43a6e3bd1d0dcd8d8c070885ec83922a6d9935865ab16198b4c25c3b215594ad6c938d9263bc950b22bb33b42669229d632ef0e9a3d55994c2693c96432994c2693c96432994c26939d64329995a14e1435edcf1345518afa79d2aea596d2d3b5d49e50a8d3adb3d812a59adb7a51307eccf13f1600fc5112853fca24e44f592803652f83217b21732193917db218194be6c960642f3217d94ad6226391f5e0e3f3c3cf0f104040430405193134940411d11245454e18194d7174d40324a48945922cc9518f43e7d75a47f9add5a2b45ac31b50da4f79834ed76a45698e428552d6d0bc56a73bd94ffeede10db72d509ffac956a04b00cdf7f457b321385328d85a2c2abc92eab84d43dd2a917ed0a039b1c9d61cc0e9c74b7f44d51fb9fb632a4bacfdc8b2fdb8e27e7ce97ef4523fc6acfc28837f7ca1faf1597e0c5b7e6cad7e9c71f9517cf95106cc8f34bc1f6db07ea489f95185ef471c323fd6b8f8d1e6c58f3793b67c01a2ae0b9005cb80319e177e0c284364812ad0d0dcd8d8c0705ec003e414c5a063c4001f2da003666001471a926e98372820049c45624766497a7af0d9c0f9c3cfcf04026888a02023868692983788e68dfe191eac6d54dba8a6c979baf7e5a4278a43e713ea711a154a59e3d61923d8c5dc8eb4e7a0d2aeb45b7936b79291eeee757a1136ea2da56c49e70df9efeeb4a607e09cb4361ffd68d8637bd4b6811fedba97b0f7b96d8fd3d73b70f31f361cf8d1104715d8fbeb4a577df7478193ebd1b02d94a826a8afe037ffe6f928f09ba19430da2eff1823df8100cf1f5f6b3ac552ee95262a19aeb744e6ee53e00117718ffa1388fa2d852458b678aaf659a8f5bb7aaaa7adfb2d051edcb5eadbcfae037dccb0812f8ec03e68a64184a860f8371a5a14faea9aa1c5f631830c92a60a3cc449f743563ef54358be0b53a10c79b084aa504c82ef49cce32c4a3d4e7748781a85b3e8ed7720c0b509f8874db6ef33bfc5cf7f6d38672df6b448916400d3f0a38308f19b83d4fc629c8b66d19c4191227976510e13b08f195c3e1ac0331ce7516e59246f0090fba791bc915ec30f2f4a2c7b3897b84e1db1252108543869821415fc800537960508565e78a4b9f3e88f5bf4b0d773494a2b6e307483288c28e900a50a169c0d2870ff88fb67d29190cc4ce693208db3842de916718f782eb916db251282a016142105500e3f27d09a3084a1cd69b5da0b9e8b99c6c63b80f30a776620bbd213d9dd7d09ec7def0d470de6ae4483bdff9648238724a400b34c51e980f113ab18d440f582232c8ef02acb284018228f9f04849fdce203202419025ec9320a107880c05b96517ec094c77ff1074b3f88096d7e9a94d28bb3bb7b8ebbbb534a414aa9e6d4bad35a29cdeede6ab1bdbbb57ed9dadd3853da27cec1cf7386dd7d726942a2f5e4d56a577e8e0052c8127bfe21c5f98b3ad913aaa236ba71957aa5cea1babedad53a54ea6a2c1c8c57389745d2289f50f385699b7901d52ee7f461b8bbbb1002cf977f8209f03c82e7d3ff29d8f3fc14d76d1bc7d116a5fbb6752869aba6d55a6b3d597094395d14aad65aebc59b8ad3b854b7b2625964cda3bbfb76e486565569e1b60ccc01dc307390f6db35eea194215d71bebd103922cb9823e413d93249c0d35d7a2b9a16ba1031b9665269a542b432a9b452215a9930a1b5aea032a19a11288c30b9468a30328b3082aac288bbf59e591ad6212298acb80c517fb20bacda95b556bb2d940526ebe9e6a0e162a8d2d183b4f09e542714b699403f9364f92ee400668f0c8e7ca55edb50e0b66d0ad060a71088bb82f0047dc8855a6c3fc2859037faa110d1774371d75f2b30f9277086ed45147876d2c671d25f29981c5a111565a3dc1c8f99596743d3d1d0bc90d3713087c947819e8bfe42c1a4bf4e303984839280ef55b170d2dba779c8f35b07f99a3dee9afd4a7541f390470f9a3eb9df87041e54875cb48065c8f320d183824e288107f9eb05af5423ad5747c0d49b57ce1d0c81eb1e1e3ce1ac684235673d3d28416a0e11155d91441ea7d17c22cfa32925cfb93499a614b52c4f6ebe1c1b499eb37d60642872f7b4fb3b0505b97baed3d1ad32f75ba783e3f2b65d71246f3d10226f8fea746c18f5a74e076a06f9f41249c7494ec9f7b54ec7a5da2a6b52640d090559b3a2080f8c34295f373d2c0144148359ccc40e96208bac586204a300ec6028fbab4b203bc212443158ffe46e9eac10bd1d007d03968842871448017485104c50021128ecc13081c7cfefaf29834a41caf429fd5b0511993e8e5245159952249490695121041b524090e97bef625e91c17c228fdc1432a0e203995222f08869f2b89242897ca7d880677dee4a074f78c04707588800e90803b3d9d1068c1875e0e7a8043c1009cd8e90d000d25114244753f4301d61b1c30e28988e9836302405068eec78e2480a1e8eec7082488a101cd9818323294c30db110592144e663b72002445114976e820e62e1f5c1fc4be24a2fd89c562476ac0c32c656bf00449aeef2c328c020111185502171460cdc910b8135211611512ab2d30ca899512523c80352c5445389565941a14a1c5881a54911329168bed5095e8c9b5d65a7fca1ab0a028d7ef95104a8ca04509e6b28ca2c4095ea0c4101e02a630317124d32ca34c81228ff8b50fc452b0c407cf902c65ba7518c7a42a55ea7ec7f278a6361b51f07dfae011cc3255aadf897955c883f5de7be10e4b158e9228c78072d6cf29d516031281792011588c0e958de8ddeb85a3c71ac5d4d591b9086537dc1d478b6e83365b1dd4b5c2164201ecec02656ea47e42e42f3a2bea1a7a744467ee4f8fe69c3de5be2739c1b6bf85fa25611de4af460ae90c860eb8f36dcd7a0aa5f48909a545a3301c6f8c70c4628c979d8e99c7e99628b6c496d8125bad274459e0d6d324a54973eb717a6b8132cfcead704eb9c18447aa01792381995bf22685dc4f95e48dc458d6a59f99096992143ccea5bc8409d7e9983f4b7aa6cfec49bab33be1e752f81d7e0dc3a7e1883becbf94e6a44b2f0d1b690a55127190d4e212131ac95f74e62ece9b61fff97dc4141a82617e7611c953c9cc4ccb5a3bd36ad518f367c9d77781482cf494166f6ea4ac71f3dbe4acb5744a18bd34e952902acdf76ffdccdf3933a7b7e6ccf48ff1b283c1c31a5a7f730c14c2166e166b27d59999f938b4c48f21de9f9969cdb45a4b4e1c94274541080505f103c4e4c795f812f771251e124fea9aced9912435556a0f5dd662bbcf94a82c29b9775223f98fd265178324e2310052ecc689f8389d927a8a26791e82acd1f4eb7f2d740d3f1ab240f30da9ace1de7a6fb57ef2e6e0d8ba69d99f748277e786d3dddd9f823678be2db0c9cd6f960e6000214d7257e77b0352267735046ca04af4ef537a9dd67ac3bdf7ed0d6bf0bfd9521664c6c16948937aa874e00f998482127c69849f3f8d772199a144972815ca449bba2656652d9a44a9d0251748959a70f82880f74570268c01ce8fc1c184e74b14c0fb159c5904658e01ca3c03cace9fda16fc290d8378ee991307a7fe74d635a75367838e195b6388bfe3d6905b3f3b1d4954892eb5488532b5d8546375764228fbeecb3c694f69b1931aa945edb5ca842a5126dad462accaeaacf65425d5bb8104770713d2a4169b26d1a872d0a4252f3efc85dba57d1fb5842c4c4937d44c9350f7dbd153b2a6d91f8bc4735e586a49d463d33db448f2e9fee9e7240c2df4cb0428e1210441fef8e1b763669186474405288fd20807cd8f3377f8ede83ca750b1b97f3a28c78f8dcd516e221fb46813e4bd187bde3f150a72f98e754dfdd46f96bbbfb2b5d6ba62b195e59d856543d5253a93a4203de121a1d135763ec99129d1148af1622693bf3c8f0906fbf565aae5eddb5066179766b15cba533039c09727df22d95fe6bd4f3d0c389b2693164d2d3ec803e4ffffb7c9ad56acfca0847d16b4e17bd9adc4d58336345aa42d936936c552bf4d2e054e1d303030dfafbebe7d99cf7edfea03a90bb20cfd6d057e37ac56ab4fad522bb0be05bf0f07cfa3b5fed6596f572b6b57a9effb0aa6acfdefb3df0ab4c1f3f7bfb14008d820658d15183601b3b57117d501014aedd7ef77f918975adbe5c11b6aad35aca1bd7701dbf3c2c98452c94308350135210494dc4bb31f265d3381a864fa4d044dc9f4675323c934888c84ad72b25b8523119fbc7a0b8e739412850ee0ff2a784421d78ff98ff5328f41aad8f254a6e5472f3f28765e03850fe0cfe5bdff5ae8ec859f4bc802cd1d4a592346e6efffbfdf58e083de129845c36f91fe17ca8e15baf0a556f62b0a4ab08541c5c5d3174f9f3e8dae89f9fa2cef61fe65f5f473bac6e5e9378c17a06cd105285b94f19a70cbcfadbe07c684413ca75832bf80d90365667de0ec5232b3be5f8132d7ae85d4af566110cfa9ff6aa7be97ba26c582f17ec7ccac2fe677ccfc3d4e4fa6ae49c0e782fd76f90f1469d0b0401c1ecc8b0b98d3225d852bb4b86a6a91766c32b548bd2d704b38e2c9941f40c3a645fa2b600e9c15727478b4482965123b2bb078a958a9c7647a668a0e946c317ab0e907658d1f1162044620c0a38de7f9396e30611b7993429e9f43de749e8ff32f577c8722c0adef981d64918c8d2702a424db9dbf6366293281fb4f7f92b9431c3ccf4721099ea14d8843a44193638524601d37729f46f37568a460ffb195bd65826d9360a5c161038e136549943d1c71b27d99c7dbe4032804fdf1622ab16cc31a6ad84e501b1a3e8c5a6b9552ca3ba994524aa9a3ce916628b64929fd0ee9fc16999c63f256bd0c93b7eb925779db38bc92eab8bce5ad65c5b9bcc078ac986f7321e3e2850b34cf5fe571677e7b0bb6eddeeeee7697d9a9c03377774bf795379552dea6f2e640732771ef59f764f92f66d86ad77f2e908f74853d84ed768dd3d098a72cb3fc9ef402b256292badb45229a594a07b75f7bc5a64d581160c442570f880060e722c4106051e097006be1271a9126cb2e06a0e383970581eea3d00da9336cdbc216988ad97f15698133b40f326658d53a794527797f54777f7eaeeeed49dce4ac5a96a345e5c562d2c2abc92eab84d43dd22b118921e9caae6719b8692218a2ad0d0e4306263a78a2dcfe5a958c6086a5226ac24ea5e168c633c2ffc3f699a466356836ab5547b12820fe3850b992f86e5c1bcb8ac5a92f4f4f4e0e3f3c3cf0f104040430405193134940411d11245454e18194d7174d40324242c9292844e557bbc92eab84d43dd18748c64308244b3278fdb4ea86b4f1475a2284a51274dd372d8d824e154359954c76d324451051a9a1b1b1b180ed10b466ab0d250b7d210ab11b6ce39e79c73ce69ad0d6fd828b5d66ef752bb6df4743a713325cd9e70c4b03c981797550b8b0aafa43aae482c7664364bd2d3d3838fcf0f3f3f41cc947a347ba2f1e2b26a6151e19554c76d27d42d128b2199b9cc7ba4d9938dcc17c3f2605e5c562d2c2abc92eab8edf4c3cf0f1040404304051d316341108e189607f3e2b26a6151e19554c71589c58ecc66497a7a7af0f1f9e1e727082020d63c2541b3271a0fe6c565d5c2a2c22ba98edb4e4562b123b359929e9e25b31f70cd0d3678109cb93ce79cf3decbddbbc5265be3f74b6604d3971979188f57a9027362df080dccc9fdf1e2da12752f0bc632de1f8152cdd6396532994c2693c96432994c2613c25424163b329b25e9e9e9c1c7e7879f1f20808086080a326268280922a2258a8a9c30329a22631305b50d6491ace1326b5f63b9c6fcfdc707f9724997bf24f20420441e4fef9d8e4c0b831a885acfb4dd5b17ac457632d97e9985ace11fa532ff3aab415d7344bf662c55245e9764af42d969ad196916d9fd2915da949d66216f6691bbbcce6acc5f4e1de4cdcf25957c69eff74756f6d145f6fb238cecdeaa505abca006ca8cd8c0eecf2eec54a5e45ae42eaf46d98f5a74a32e6ad1fdbb17e0feb18b564bb451596cb235d29e6c276764c10b4cd82c9229f4b76880a791bba49277a76316c91a4d3ff9f9bff653c3711ed19f0ea2323aa3b116279306ea9f5934c326d6b3a1c450a651b70596fd4339100513283aa9d47f3d1e859f1514c5601aea3ffb55fbe8d7630b3f1bf6e0be86b247f879d8847b0943bd167e1d3639bd8469d75dd30852727bd95d733e0a14c215b973d8d8e0b8ab43197cc8a3d8fdd9366e0b400cd3f7ffad4122b00912816df2c8870c33cc943c804652914c3124a8d335a2c191236b3a3c1af4de5d9a0cfb988186d9af3d8efdfae34dda5f22b00a1281dd17dda53d119805bf1bdec0a3bebfbb34ec2eed6d8841c2ec7bb8b3e3b94b23021b456dbc19e5839a3e4b807e8a9ee8d78b1e8944111f26a1a11564247896c8a8d3d590e0f9c9daef74cd14ca1a11799c3289c4054c90b597a16b50c8dafc7197f65593cd2077b978f06cedefc5d8a6ca70fdf1970512425f8369ef5a58c344aa5336d97a5102f76c8d603281fb471b04d09c42d97dd65649a96bfadda9a5d2a25b26bbd462a705a63fdaa56c7dac528bfeb3f6182fc09b4fab94fdad120b08fc1e64e393e74c0a9eb327d9df89ba86be5542ba5c5e7933ad0c40d6c9a964771742bafc4f2aec41a30dcaee4176488aecd6c76d516e22bb2fb5e81e246bf88b54b4d03ca9fb9007b5e85aecfeb0c9d61cc164c2af5fbf7e5d8925e4d3a61dce252d70ff38978ca6d20b80f0389594bae65940e0f1668ad41d53c2d946deccc8f57168a8f5a5ec17eae45a773bbadc343ae0cedc6fb40a40d6a84f63051895bdabd3abdabfbf3ebbc3fd29b4db9fc2a7271d19b7d100a3c2938dac51bf5584bf16b4b7af556b6bad154bf0f42b48b42d115b0f8dbca99f43a6205fb700f64701106024408e190dccfe68802c3fc6da8cf3a204deec072c9005b241d8207b64a75821eb639fd8226b64a5d821fbd335a3fd406ebbc432b11fe82e06090bc20659202db0ff68817237929cc91a1fa54b41424348af194b474031ca4da94f23796ca53c93377366da447bab3d0902434ae4030defcdb0f9da423ae4ae7efba30d2d07eaa738794e5c4c67f2d84836b44183dcc55a82bba7674ce0f963cfecd29602fbdb8b1278ab49a7296753b10a3c7aac3bc8e2f76a117890374a6a92bc9991edd7257933fd45ad0ed9da1cea12b2b53feff96be5bbaeebde67e89f0a3d6f0d6107eb528734ac4beeb2efcf5495b4ca94eddb9611b82849c77f7635490a1e3ba8a8593821b794c05f0be1775bb4ff692ff39dac2af0d7a4d38f35e974c1693f05e6ccb183b2fd27b27d1f39feaa3a907286e3b5cf92af0ae4c99b599b00f3e8b1f2e8716fadfd0ae4af1a04e9b27f526d1528a912d5a4f179c8f64771ac47d9fecf5879a08516a826c91af6452a5ec2ac80091b926d5daa4944c06ddfa5871b42ca175c21d42d451042cc59f344022285f8ebce1990eea734f4668f4713059630091b6974c0d23e4e07b13dee9aac2660212dce9f34b93b083428420b17002159324bc2405a8c718465a7a30bc2a35c02d23512c95b4382fee6ed80e5bfd00a01d60973e64c6a4fefd39f41f7ce592c460fb0ecba22622777709be519f206757a0e941caea1122e84f183e5dfd79c7e8e8f1774649881864b374a25534c43bd5c9ff4eb4362c209ee07c8672cc75d54b24a20bb2060fbe39c4d24ad2e09cb1fe70c075d1e6cfdb2b5cd396d10a3c0f7afbba6b4321c25150d14a3c03794ee9a3d9bfa257b3271799c4cb8918cbd24cf20f2e6fb537ba894d922d7cfe1d1af9aa0284c0822e3c815db645913753d4c10b22debd70be4fa5276aa055c516548816b7d4ff417fd205cce13df08db94319168685e14268b9ac6ce4a6b0b06f029cb284d4668d2248456130f9ad0d86a6d9540f841e203265600844fcf95298153e2083c4924144650b246b8006eda415315344d36681a8a59a28ba54b111e3d4f94791a3436fe55cedba20c8d30b4ead1d17171e75e7c84634e8bfd04bb3ce1695eb839e151a82c5fb2e49e2f0a4c21b246ff09e0042414a3272dce237ac88eddd354722f99f9a4add3e1b9bb5ff05993e52cf071da41d64f10fc625cc8c078d9e978f1308ed4624f29477302f2e684ef17b2894c60a5cbd12529d80678fed216b4c7e889e6868284baa889fac9921f5607669e4eb9f9eee15e7af7d2e94de8492f42e091cea8129fade7bbbbbb5f7aef0deb92caa4474995d51953938b18c81b9f39ca7d247231b8907b7ae414a92269f1a8c53e9292023cd2d918e49b2a29c2d3018f74e64ca9263cf3a333cdfce7c9b4d8dd1bb5d82b9ff21fe99d4f99744d53241a100d6a9245eea74347f4d327fd5409f5b9d32952d7487121e870147820f77b53d7c840b202cffcd8487d14dec00805570aaf82cc00c081a46b68bac695744d006abebffe30e99ad7f757a0aeb1e91a017cffe6027164ca7948eebd41422dd280f37180361e00e08c5701b4f1298033de410180f36d401bff02677c0d68e30300ce781870a099c685036c3d0040fb2a80ad4f01b42f3b1770a079865d14f6d0dc71bf7eede83bf8d170077d193fdf3f01379c1fa4738dc76918648be10d19329ed2194f6584367805676c978349028b2acc1b369e8236fe82366c7c77337e763a549837ee6ff7de2082a3c58be3b2d33163c65fd0c6df00472279c6cb4ec78cb08974d808fb498d508579e386e34e38ddb65e76b6f5136c6d276e762acca0b7a8748c196a92d4211a1101000020009315000028100c0643028158240d6361780f14800b79a046744e970a63518ec3308c8218cb1103880106008800c8d0d04403e1a4b7c526eb722558ba2e62efda67a563cf65a89dfa358934ee27a84f0bea3e46d05e37fe21b4962b3e359f5665127c65f92b8794f20e42a4cf3abafcf07587011b4f507765ad2cc166037cf89c0c04f2b47ce904f79d2652fb2391a9ac6a4140cc0374d70fc87e8f1d9fda0a1467deafba2fba6b7de5be086cffea4e7378873dc4ca80b0c886e3bc751f69b35a7769d926cd9be8c3bca3821e83bbf70f56e2c4cbc3f3db6e6941761081ba3f1dcca92c7fc3624115e604a6b08fd506f437052fc83e4ac61816ab6f3e56bb7813f21caad608267c1595907bdeb87508f5453f1fa305f740af64fbdd0301badfdee7657e4d5ad764a40aae06b0f79858d61edb8f10068e060ac827f7deda1c123e08eb13a96aa2ddab5077aa16f4a1c36a94fdb6d02c1241da2874bfd0710663696dce262e4aba9e2e5792872bca6fbf154ebae30462d11caaf431865410a18b9163c1bfe717775eabb86d1f8b1da29f96e96f561cbb3816c295b9d64f81170f176c916492192536249c4421c3fc94b2bc6a896f43a5a8a8410287e169070f232290af1f1c2676aa946f869c7350bf4a0ca2747323186ca913b4461831300d68e3843393897a75fb436f16699b003c4cd55e1ce8ef2d1393a7e79d97d3e820f8e012bbc30b89dfde941cab6e778b78c72cb35a01c47ebdfbf4add652770dcc2da9eafb713c275b2e1bcde330bd7c7b24102e252036ddcb834e7d876f154826c77e26478102b7cd35381d3cc2a940f0ed9eca96718bc5fadc25db1cbebfeb1500ffd84888ed8ad7ca82ffedf31a1da877993b3fba0810c5adc8bbde205756e233ba7242906f0ab9768b2cec1dad488216735184a8298db86b1fd182551e45bedeaf19e4ee3a3baabfa0cc75bbb9daaa8e80b1e7620468ab7a1d2c35a0620d0e2c0686d5572ba16a5ab103d8ff2fc55ab7e7f87fc809cc04cb894b09ac65aff91311eb359dacd45c6e816e177fbee9c42f1f17bf3a22871ace5ebbcc5f6aaad2d59790249bff3cf150012549d82aa2abf00b0e793951e538b95e05bfe861308dd85211760cc36ee5aa6438ecd9c92e654e75001cdae40d8be13866b6a4dfdb4b88d20c903b9754d02eb76e5cd70fe60727d3472f33919909cffcdcc140a3fa69842a612686a3e68318ff34267579afe11acd14ed0b6bb28e0241436abe9e219fbbce1a9b964d7ce3b1142b407befb7791501afe578adc61c605bb65734b242069bb41c61614bef453cf3c2f3b95e20e014d7a55644d78f9cff2e805af9396d2acc4695df0a6dd3fd89d27c6dd48bfc6ed5bbdaedc87e44e443e3dd5f8c5a67f384b571fa0c5458a162109cd62f0fce96db4f58d6a09970397dd537cd454949b169c9844b9ae25be2386b57916e2e159f2369f0e1d7833e9236016c6d7c8b0116deaf59a25b250ed4f7ff71ec3410ae353c9412e2f51fae5a491a9a5b4e2fc013937f6867d473f1fc2b46cdbb8a69224833c5eba31a2b3ac911efe8093fae2283a79a2b8b9bc8cdcfcc782720470f616cd60094295b9409a227253d2c99fc12b3001651e689b4e845ef204ecdba4629253d6184fe1b3d97b008d2791627e587dff48a399b0f2778a056f6296179f4fd942a5ee5d28213b8840bd4682e01477c3dc2c200b10743b4a552a461474a004fc00ea8ca3a27d064ad68fe495107a092b7bfd74153e1aa104e1e1ac9404e81fbca6b7f4ef4a019c806c682c9349d17a155394fe804e827d7fd17f9c44f9e8ecb616c059843f4861e44e5bf44e216bbb52db995e0ce52312b53270a292cfbbbe65575c4055ab78a446f032ac7e8a6eba63c8d6874ff5705b8bdb06c00a89a3cff011258facb3a49e0c8b57c3af04457cbb8a407b81a8a86461bec40a62eabd58441196c6e45af41b0379d1890dc065e0520eae6362834f4299b46745abe606dfa30472a463b55b7251a02ab7e4c93f24d93cc02ae6a804466243a201e67bc44c3f02c20f90e20dc21e0638f593340af13e5219c1a2a9480d1b31cb1dc14b21ad7f57aee928d1c6f61b2e1cbf01cc19913de7e4bf96ba17dd6473be1eb9cd32e4eedd0469653245f11a9c057d8f00c99922e7526e15a3e54112e579dd2b4feb36bd290599b362891d3d9d02e0e95d179dca672f212b128fc3f98ca392ac62d3fb59aa6a0c899f18c6b43cdc341479ca008992892f621931acfda271cd3e16fd4ea249cd854291edab523665776b459788ef4061124d83a1205d906d7a077b1acb8f5b38805450e323f6714f7039d8366c8ed06d45e5cebed3adbf86656e0abce3d47837ae367906c86d82a739363713d0b5e5df3b4e5c0870e4be68a71177f39571e78bacfea53e6e042f47146058434d970381f2fb48428837a464cd0de45174e6d88f80e4eae4a21dd0856e00718f5c440bd3c7a66971154587df5ada008b7dea33ddddebdf49f6d1f8fbed5d0f988ba429c8e5274ce0f4c115ce51e524213e982b8e179950d456cc50e31d8582e95b2b9b41ebd81c5a50080562b24bb96b52d34cfc595fca119f21a2b4d44efdf966a8e6114ece1a600aa5c5690fc7a1344d006589119a91b085301e60b928e75e9f13b86c6175679c2517dd5033c687c6ffac82a0f1434559fbd4f2fb4a303652f9b22b88fbbb33671a7c2aea1dde870ad48bd89a1996f1f47ad1e2d4533c375a4203248eaebaf890c7d4cc7a115313075533a7114902b279afcbccec10ef32a2de5877aed0fe676bd436e6fdac55c19714cfc0cf2b23939e54645b133bb4d644720a10670275948a1594b7ce0b3c6882f27cafc4b0714a44364c63cc7ac9fc3bc503661710700df7f7215d9dbcd1dc350da64e576c4096b8f8e7ca0507e1cd82d1bfc337c06e9ee154aaaf299e97d86311a899d47f09528d6ed205a3b9b31ce4e39b1c80b8cb1675748cb99340d732348f891cba326e0c467688e7a5882b937928b8e333d2f24d49f902e17ac10ea14ca75969dcd8a8ae1ae18065ef2093e4632dadbed3e259f9236bcdbff983f2b3f37202705118c636027c65d04f42644df98272855f6ab24777d329615522caa03a048fa7bb0ece945562c80ff29fa4b4aaaf3cc18ca62e3a436b83120a8e2c4b3332c65911d7050ac1e81d1a7971634aa7e2eb81bad7c41c3e61da694856ac2260dd3d0959337499c523c24fd29ad2b12421d857798c204d02d593657c4b57ab61f488ca4b3987f212e02f1fe13c6eae3d4907e1ef91afbc9c9d3d39c14852327e6cf74e0fab969a5b2a53afaf8b3154402e98454ea80b7280d21102eced0facb1cd06cc37334ee99f910930c8114caaadfdb80d5baafa94710d15f002444a794a76cea52893c0c4a0e9d6510ca92204bf0168ab557d612db22ea65c489f852ce924f870cb16409971b3778de7ab29808841c5a0c781233cf63b131a5d01752879f61a639f2dd94b351e5a3b63d47ff89490bd7c280861a27b97fd58103399921bd55295c3207835cd72e57b294adcfc98e9faa156b914f7fdfcb871fac40e22af018da5b84a6461b586bb7ad6c7e675f85f523bfb771f3bd7132dc6526d9f3f881bb54b0354a342dcd4f34c8b245838f47625ad4585a80f5d4d541c9f75e2a14cb42f3722c97ad5d43391787a166eeffb449735c3b586396cdcdf8ab64cd3ca56350735cdd63585d2fd830cfe4c7e2d1e5750fa528011c570ca5a40747e4cba0e0b05202d80130c302179bc9e4455a3b1126e264c2263e4567e07fcaaa8d0b70040f17c68997ba012872f072654b41c91b04becb438ab22a181b9447b741c893b67270378982a265fface043e86ea37a28aca1583fe06f6d4c789701fd7c90e0448bc9ae1da46b2a2a076c873a154f67f5d511dc77b10e1b818672db773159b370acb4ea4efe97c90cc299afa9d3d6a0b844c83c11e79e2103ffe887214453c8fdca296d43c8fb8a7e8977b681a10899b5dd50c7d789a3e0d6a314ad777b57549b4b1ac2282d5c606c8e7187e3b4fe641faf6fe6fad08460b57fcfb9e2956d0ab12f8df6755024f036ce3a960f630705e7e0888e78695ad0b0e4d0c462ad512e1c46fc5bc0864f2a501a56bbb1eaa6f4ba146677524f8bf8e43912ae4989e48da91b0fc08bfad9f120682998b5ce6f820d7528f3d0d59bcfb55aab6d904776f8a71be720da6b97713b6675967d231c531069b42ddcc03dd81786a1c1ab90c32777c85f35fe1acb274d9f7e6cbde1076ec12190b7d8014aff263f7aa28a565f6432a59dc098e177b291da23bada2eb1be068a8d3a065bc40e1c7f0f3ef0f36f94e35d01e2077ca614976af6c0a4f9600b172e146efadb631454a1ba5d8c338e9acf56c0e8d0736d7e3171fbdead3ea9a0ae95252857c289c26d86cf6ed10191862613833443b722d3f528499621aa7ad68a0438416e19947382a01a58f45f281f65599a993f8ec2cafa4ce4df6729711fa043ae8c766081d35e41edca0c9e01aa3640dff1ae67c4a2167f9e0c11744339723d4727b6c344b3c360c7a74064c297d68bde75d8a397730d990023323fe2969038d141ea80049e9e0d627aba6a6a8760e7a6cdde0c3b6c8be5bb89d9acc01a7d719256178352a0955b3e8afe44efe85ebd272905685b426ba87089f7368bef12159fa18d15e519ca639e697c055aab864dd0a802b35bb7b837fb28669220c0e05fa8e86f51fd5ab188ea7a65650b151820c6e10401f9309f7beb201255280bc16f6908cdf091577b6a62b4e5ee69f71a086934906f9d036acd6b7693421f86f39aadbd000273810bca5da56731b2ebd9a12337d7688d10f121f14803d01adb431cadb5178b5e09a8e821af5446dccc455cad6b72699cce56dd9c3791ddb26c02acda6951d49645bce6a936c21b7d6824def2ab719e007307363dcbe39932499fcce2f4d0102220f57ecbd6ef34a930717a7cbf47f9be4c2e854eff53b867b54e8eebc9e8933d50f75a02b43558f8120b90cbaf4b428e035a35b81c44bbcd93e316d5803122f10139ee3fa13c59c7ef37403aeda6b3b3bca2448124a7f80220a11c6f15660fac6a7db7fccc5d6352c969e8a756e97a8dfc8a928bb5e52c709519f1eb4484a81973e1102158d95158859bd0ffb09d3f6f79a6bcec70f497012c52aedd0c14623174fc827a3093cf856611492c52f602cd3695bb9dd911d5a07655e43be73bb5b6a2f076e5d4fd294827831b42d40aed36b13b1c0bec3967b2a1202aba7ba50b3491a18b8b747de28bcbc328ac8c71a33f6e087d9e1b2c08adf084c111bb34dd6d72f2d9442c2a150e6f497929d99f0c83a493c654e4230a5fe14aaaa990e01e58d013cfec7ee73073ccc16d6749263c0497ef467b7b7845c2314e5e8cd8f8457ba7940593021acbac0140bc4cd9a5db6e405167c7139046c43163f706796d75d2541f0dee762271432a33a80375fa06a8ab3a457b16698eb00a7af1c79964a1e8ccc3232e9535b1ff158b04df43bbbdbe72c4f4ac99c9e1ea589ac3f64ff8ce0c484c46fa30cb2e53f3b782eb0e292e384d20ca0659d160270bc2b7110561fea8c55d31d46aa1cdd8944e5629a9c15c77306b9dac12e2d009b6d8816a25d3f491852a10bb740cd93e279535ccd1e8980a0fb9eb1e82e8bb6a049c7d625320d533100fe4004ea4e40196c3aa36ce7e04de55995542bdc7c9d29b41cccdb0092896e716d6148ab913d99c8954b3fd967bd417416858918e3fe306a638f4ba4a7eb9696937af444b7b18f387db6c89d0d0d68aad3466d236c96b04b001152f63ca467f26b84e717819e340aadeaeeda561c17b901f301fdac366beac9ca5b69685c478e85d5a932de9de1a14fe67f03672244c5c466edeaaa11c9b1aaf476617d22a235fbce1244f207196e972dc9ebcc98a49d89441511094630839612485cfb1112803deb35c39816bf521fa8077a39b7d066a42dcc28290646d4afddef69cdfa22ffd7162cf7880b23549c03e7705b9094f98b8504ad09be76d6ef47140ad92d4c0219114682905e4827d922cf9a0cf3d8c2f0b87e6e8b4f18b4781e5cf00158e45db244f6d64fe900dc544527f3e2a9730082fe8a1f157e568e0dd9bb89463b2b35cb2fc48c950d4950c87696af7940f42e4799ac93c0ff6e510b5273af1cf75569295303a177bedb63985d440da3fb866b1a8d1fd483baf786a1eb548ad996d0d4055940fff39765916c3d94d81914ca68474bc3885db4788b81082a330496a3d31ceb19026346cb6b614efd663698ba51c27f303b62580c201dfb6942ff5931cc0fb88a95a3fe6f00bad0fa1bce7a022106902fe34a70404f6e979699f88f845319f221d06c36b7e9c7e0e5246cd7024b68be0089c9e508abd68756fa2bd604279be10a9a5d696b3ce201a16f25ad4009b9e6abc140c4a1c1e74902d61821f0ae3a1d5302d820b7883475661693ab5782645fd910588abe7239ab32b29787a3f6aa0499b2c1f6267a72c2c39395bc05369fd680c4e574bdccf46ff91e838e814561ecdb193662bc9411feb6d1cfb2437f8a83281e8f33a5bb34e5197fc9af9e8bcec9a0bb6588e3789d65e2e948f344f258f4723a601208519a4d5a16e598eafcb54c4bcd602c3425e13895fa7bd95e8e6a48a98b8244a9963bb0b2c6b957509d2532cb919d09dc82a29ecca9d3241e42fa20e9794c944e24b931711e32fcb6f8d323601daa01b9d02faaa048bb721563d72004f8b44af35ca5ab5e6cfe18456a4281d4aaf5990ee5f6f93f5245d263eeb0ff632f5d8b2e036615cdd64ecff9dab65428e8a9086ccff70c4dcb36a0c194d4f2202e1840095ac20d6ddc932f4323ac32af6efacc956464afb05cc735e9d749f7994d3ddaabe0243c09dec75b623c150c17a6f1b017b71350639210745f5f3349efa23b58f064a971950cc6633564d4a08a93a579fd157131f01f2c217d78efad748313de5a424c643f36122e03aaa2db54893d9a37ac665be651f4573fe1af37316df8b5aea0a1fbd95d10b54a11809a4b64de1af7dc1dd19682f2b9bc943b2c9b63c73120396d54c7d97160c9c2ed3b7872cc863be7a08ae3fb10857652dfed85349dd792ab1a29a5002abe29d7821b1e053bcad9d334038adb27310ccee408095b27f42c92bb7265756dd933b937d32bfb27c53c2d96ef686210043c08010fe75b42ab90593002b782df0ec8d6e30d407c4eaa2527f9b41c3a4b2c525834f0816fbff6da9907b9bdfd1c3bb8713e6ed905870454719897a9790d9886fceeac3a394daa6a108e303f172912b69d707c430e8342c26199fb7bfb4ff80d1770407700397d0135c61bc796c8d5fdc3c351c0adca8f012f4ce284bd3c89ec47c3adf113ef765f1ffa0031dd73f0a9712dfb0fae1b8008de6268afa13c035a1d26fb7a5225f98b8ec77c856eaf10f98feab2720513762b756f6cc467f28188abf0d03d9cc9d2ac2b8c853c5930d83a8987558b927f5aabd86cec310732292b96c070de9987804cb8d0ec3112f85b3d136c85a7382d24a6611d921a6831363163ea2477a7919ae1d8be216b39cf0fe852925ebb5d7952aeb0f7938f95123bb9236b01eb6fe255ebae52a11b2e9d7dab9aba9d275fac1ede44ccb8e5498a6f16a729b235d980329dabc0db34537cada7ea9eda38b6f89a651b24c54489b265ca64594e69a698263bb92af5901434e720072803278bd3f18a12b01d5f5d86f0c482523547193474f7ded087c95a83075a5bf4c34d28ceebb074bfe09e1943279d9a06273f8e0dc5346f92757d02173bd99021e79976cb023dfa70b72dc92297a1dbb6b936c50d74ce2511c2a52d1ee74c84e36981aafd307fbe35b11c21a65299c40bb2dc7900bd3fbc5cc96fb147e02850c89c369d6ba615af8f7ba41319760b73084f781f553658ff2c3283a303fc551e43e96d14fd21fcce51ae5dea934a3d782b72fdf6b99f87289beb65ac918119857abc4dcd61a851f64cc86d358fcb1054e3daf8b2be1407222b5595d1cfbd567472b205e820f4b4fc0dabf1ed5fed33598f8136fcbff67bbae9b210124610f632d2a3bca5c63af2cf6ac9bdef68121b2270b5ac71834d598a7130cd64d6c562c9d32d4927196c1ff9ee4d107141f5992041970f49439e364c48cf120ab23cbfcff39f1af97a2a955643fc189a70eeb24521beb029a3688cac646b1d1e769e047fc4d4ace673e1872499648176d0d1dfda638834bc2b977d6b1c7f1170afb99349b6b15100547272a47f45726e2936dd233da4304b0e74f0cc6ccaa400783858c5ff7d7d32729c48523efb58e19ccc36718ced40c8010a2e3dff29db2c18a64dec018c31b11f41a2654743328e78b4544c2c26a24be192844763f0c9f37720c7bc2887e95838c68c838d0b69866bbe14c0f157aa193ced4a337d67c980b9a655af80f2c58da85ee33ab69e1cca75a31b35171566ec35f44701cdbe6ee17d484652266bfe0c2289f5cea4f0e4f832807f3841c6c24e6d561a78b0dfbdbb52b0f99559eaeb003c9e091392a4ea6d76d5d351b7284000e2961e2f534f0ac0f19f858a15e8db7eac6338099d7e49db44bb9b34843f82bb4031b27171642400d46c7e2ce3716ee8d38669c5cfd788c5b7a72280c8124392cb7b124cbb1fa4140bf1e4ae617b88d05ef8dfcba8616171dcf12bfdc1a5dfaaeafe801bdb5f9132f4cb089dd32e20202ab8cdc4d9268ccc2d3bc6c535478068843345ffe3eef2090ff2b8e59be9ae45eb93df55329ef6e39a67b013ae0041db3251833d103c3587cd2c200b7d328c324913820054c3e37a8c1659f3040ae8dcd4ccd031960cc54661f2c2a2b28826f8f079125207252c5e9316b1ecd3b1afbc0660667c648d8d8df649991d0004f7f224e8cb8295eda85703497e1640d3a1279df64d53b924472b2819dfb0408c3f94b4e39b661987eaa95cf6da367ed5d9c1643ca3f15c90a0eb092eb94c404b21772faba447d1ce4fd553c255ea501be6cbf25da5d347e371c6e98a2fe46be87d5897bafe4662e746f0a4afcd7f8ffc2a4aede72abea31b4433c356a6d40012b698d9c3979aad091ce8223424c80edf88850dddde2278e623668bb7a42eacaf5bfc5956ffcde8762664c2d813c6421bf1796c08cf538fe076ba10f5b642b3810561d6001cbd9623ad78452caed5a5b73883d4a22f4ecc41c02d150d714cfb784f633720d21ed79805a458bf327dfdc5c2ab66a35f4cdd6887f73fd8528489b16550390755c37cb058c689457c51b4c35cc9c2585d8602ea7145b074703f5ea92e1970d3ccfae4e14dcb8622cc84c440c0175914110d29f7a9228ebad2deadc6e51ceadda5f22f6a352b075d1bf6902abeffce2f31bcd469c12531e921a486d49a93651e9d729a811a52de0c9b65e7804befa154af575c3f5025d51c499dc935fba6e2d55a02692abe369863265374a1d02d9edd0b55651fef608034ef7b09b0e90ad839289a7a5587623f791dac19610274e50efdbad45bf02883a9a2ecd468031294fe25df008612825c9b1ac66d6e75ebbe543696fd9d4f1c2c03c02b51ef80365716d04df6ed9eda8cdbd89265430a5ebfad086813d82c63bb41c39e9a15deb3ba65b64c520a1f035a25b1a27c75f0610f5ed2ff416ce77616029d8ba6f73bda522ed0c05ab94370812a9eb0d23b8014057e1f0d69d4c10596da613bbaeb47c86ab7b5e1619fc0e128d2ee385253206e8bf7371a0beddf50afdefd05a7e8f81247c32b1e1b160e0c7f926ad886826f3ead1784fbc03d239ac1becb22179babaf71752ed7cb46b00dc4cb388cd159a7b086d5e3da333382b9c3ebc89aee174be7df20e0fcac85a02e1683976eb62bbaed44e6564701aa3177c4ea090efb929880aebbe2f088af7d23a2eb9652b97fc46c9ba8584418df89aee77d83a18fb1ee171ba5c72185505e0385dce0fcc542c3f0029e16a101316916fe8984a5304d78ed30f738a496cc04366250fc958203196e18a445bf596da399e304bb8835a2cca5a57b8bb1f22217013196d899b3d018f7b6aad52bdd4ae839dd1a398f2f943cd2d07d87d1b7cd2d089af3eb2334d31871965baf7d49767f2b9e7654bffc16c1e2934328984fd96162f0089daec00556c7fc7008b16710b329196801dfa3ab2e48f2d0b0e4f8972958d788c1a703764f885152de48e0ea16872516c00a81e79a24b555f217a24912e711bcd69c489ab8ec43ab6c6ca53c38f7a6e655cfb32897d65af07a3753e081885a9a64cb86a2cc3f9386bd370169ca5bf8ef2366fc27dbb8cd4733c4c8908b2f5310096a78baa81a1818aaf1ca11995964454cceab65e7108426e89a8909558d971cca625a72b258a82d5e78abf89d633b7ba1174f7fd87f5b625b7242d213cb9c5bf8445ace0266ed1823d4b47199aa37331362ccc42410e97ec28056bb4b8f97c52470d3e25930300b1189dcc94d6a0ced707e2b36ae49d72f5cbc050908d26a073fce2a423199e05095448c2b5c278ee30f1fb03a35d5ed21170024ddcb9b5fc67c1d09193e32f71353f5f38dcfa82a02f0c59508f01548b5ac067de7e8b21d30deae233acaaa885392c6572a2f97c85bf24a6ba092bb6f0ed053568c293cc4abf32dadf7a6ccc79b7989ebe4005c763f9df3f9e369a2ba66ab0aa52be5120593263e6738053b81418135aedf1f82d53ce19aa8c47ade590dfc8ab48f48959c045f7d8d97ed64b29662401c15d782bcd22399b590cbb88214565b02f0c3d07d4191dbc2d112a16ee959c38082ffcaf468698a22686564b2cec815b6daec485c79c471755bc431a9a84ef170530ca99317affb2f235e218f8afe23d41ea60b3d270b28a9c8bb6fd9a9571a8d69b803c90fb4b5477836eeb726d04d8f9d43127c498ddeecf0e30b3e947c530e01cb022eaab03ab0cb6f4427d9f9b9818561381ab7c47135361232a8e75b14cc166fd449c80f811b6df8815ff478346552e7e2281ed87d0915f387854b680aca2dbb682a28c3ab9a3e565f439e649a0f1cf051fe8d5d7892b236d8e890e7e3faa330696be297fef87947c9f6cb671261766eaeaae63770f67270cfbc92e7277db8d7be2d04e806f09ce8cc712b568a3a743bf9aa76c7078badc79bc43a2dee5864258e47097a3d7fafe60babfebac688e9b4b86c63b288ccaf7703f8d3a11a77b0016e7d1c434d3b7da539d49dc1a72367837987a41639d10daa54fdea9d8df70115c54d8a4d23841cacc329b10604d2773b29131f1ae176d565096def96c863b20a728f12839cd62626dcc681679f9359297a825cea4575856b5bbc4335515f43f2a53fd8e2a4ccbdbb954613b574dcbb7764d50113a6509e79f5645a79ad125aab3964b127fc2645134cda0da0d7effffead94a4229631fd7359c88e7d525f1043000688d3ef853bfdddcd09339e42cc64eda212e08da5ddd96e0fa6221062ab0ba67e70abeb7ee71bb1f575c1b2984555d25e127b507ac9eef5d570beeaaf207c03698266857b1156ce36f03d2b25dc50d3cdca82a598092a09b87f93a6a57d40cdcb62de1a02838bba2b96ff0f6a2207a445020dbbbcc5dc9cf1d8d3349a43e5b15121786d2a52c7573257631847e5600232f60cbcf48d1e1f74478708931c13843f523243235f1e9319f7a6c9d6432d9e061b346207c822a4175ce244edb5c2d6646e57d706dbb27dadc356dafb15a96c2a7e729266e566fdac8768dc534bd60830f601c76a3e3b3bed6678d8eca4ddab49a6ac3b3e3a21258d681ece8ba9ea90cd8f9a3bfa5e687324cc85f131e2a801689bc914e09ec4fd105f33389b755760dd208fcef3ec92957220d2fabd5e7b941fc382ab678445e731d1fe8ca112d8c18650f65e110a310254645195ba6f18a5c2863f908007d301950f0a9b4d59ac471734baf11e469216353d35ac8ba27ff506de9ed1879524c434ec664e8cc61ee43a8f417683e0b299df998ad91db25e67b52f116a71f242560cc7e62bfdf132323124b9a18ba258d97e7970f249191ba5b1839588d6d073b13456d4cb7bda6e0d4d5ffdf29c8f261288a1b9a06e0d537dba5cde85ee4af8e20ca9607f598d4a3e749bb604ed6532d3c0e9f764805ea2612ae1c87054afacb98e03c28f5731535659952cf9c476159e4d533e79159168df5ce7950d7ace868e3e2e042eb359fd19a96d07bcd65f067ed60ed86fb4f2107434a50bb3187e42adfda0a9de7bd731048a9258a995c51ef9c8790b250d43be711ae6621d2cdca757be638e683c5757bce71a4c7592cb6c83e34280144cfdc184368fb4baf397084d339b112048e64a2e9e93b4f64efd9e06c7f8c63ebfe206b882adb6c69044966eb873252f2d5c7331e6497efc3f7e465184577ec7ce3f8f4c15b67a8607cb48605914cac5eea5d182cca396e5932a220472b219d2ce41a2b45c48e899ebadf5b8d8c8e0b960e39d00fc393dc6ca4aec6995de364eac0d25f30587d0858fee61d3cac5e706e93e60105576fae985dbb1fcddec4de211956209ae5ae47af4cd806bd9bc25ec92f013c3bf2f868e84453074dba58237cbc737c06e4fa4845fd832d2ee7b1200476f3c8e4fa12f703fbca7f578081e962f8cdf170b5fadea02c2401e1a516f60579df6e1f0aaa8e34e8912a2b27bb95dcee1ac4a68fb7b729e60e66b3f4ea9dc41b0dd4c4129b13bbcd592eeffe960632df7281544bfb60adf1f2b15610c2f01cd47f13326cba3cfe8a8f010cb926cf8879405ebeddb9fc3673efd47ca7e69d35eed4e8a6e7890350a6cdf19aecc5637e8b3fd227664ffbc399bf45cfe91bb7e63df1c83f77c04130175f0909ee1d1e031909c6a707c24388c7461e8e2db9d685c3ec4c7ffbd67b1b5248a96bc5d919b04c745c81ab35529b147084e4d9cc653e584690439c85e30f63d8f379472ce2e0f8af64db727ad46a93bdfe60d71aed71393bb26579a4e467075b3980418329568875843441b8729fc20f059c106f13bca5b93922ae84ed27fc918d746b3075bbda21ae5e3339a30d1756c5e26178bd47e18f82e9bd1ba2b5ccf0ad996d55eac75b4c686d0c8b96b6ef5d69587e38a7dfcae1848df16a3bd02acee0e79fe5255689f33a20b4ea6accbd171c84d16036d35c8346eee85d4c56a5e4077c2a6033a6c0135e5e6fcf614cad85a3bce6b66b05db719c3b0407101cabddb80a771d68d0917db153f04f00cf961c1e9a74d1d089862ea651322be707245c0e32430e1821f3009c6ab4ca7801cf8d6e5ce8be62d589272717a6de29f82790cf661ebf1a3a6ad65543a7d4186a93c90b98badeb5ec84fa0047251e18c896eba45bf5349293a87d2ce89ec2efc25c3672f269b44bc36e1a7668d805d6b232f62e4a73df72df7c83c27453cbf90c56807539afeb2f0728f908d857f82724c76e0ebe1abad5d055a38ed7609a52860f94b96dfc483ef175de0bd8753d0557a96cbd49c4cefe6bc4b4ea33846922fb287cbf821fe1b8f6f3f2d6dcb1e6ce9a77aec62a1d993e68e275b083339a6a56fb7cc0acc260a6fee131003fb487092e53637a0b6970ab8a22f869bbfd13453f661c587e498a0c374aa2adde37f1c62f8f5b49f44b2ff63f4b4adf3c8c1e5b8b0a8febded67e1c4e3cb21c7bc31c553da9584e16cace8d6eebc3d3faac44e4cb6614e5fe12bb0b5b67ec571d1ed4e8bbf01c68dc466c88ea8e6755d58fc87ffcc0600503d4db7e413c66bbb8ef82967e15e0b595974f935d1a76d2f03d08aaeaf7e126e78d633c133724470c9bf805299f89cc922b8ecc3ca290cfc446c91147c56a1edf0a8263974740460082d14be023c0612b0f67ec9c8151cc70cf72cf3c3ba8cfd61251574f7ee2272226892a41631bc4179c6b3b27bf6dee3b35efae71a734cea1d763a4367b5fdfc080bb317bda1bd0753d0526193a193b7e8e883b2a8bf2d928d2405d72aee6f4f008e2b1c98137fbdc796e26328752cb04b284bdf51b3f435d7f7187c33e39d51d10c1903ec53c0642ead3aef2efc2dc36f3f36bb85be3ce9a776ab84bd6b39af5acb27e592ce83bac155b7c7d8435b8ecdf4967bc91bbd8630bbf3ee94c0f5c5db9fb24be12df844fe22bf14d7806ee922dbf57b787d7d3f1f8de74fc3c61f1ffdc6bdcf49b71bd6c9eb8f877ee356ebe6ffb115d4e24c1fa116c372d0e09502233f85b8501381f993e01250a45b150b7ab9aa51b8263a6353ac9de72e2a1c1c124617b5f9dd78dce05e0ee686c9c3db9017d04ab1e10c3a1b90460a28664c423e404126aeeef2f45c8c50b3413901880d1644ac6bee7ddc0175625704168d316aea746f4b90d3e6ef92a212b1161608c83011a63455e07d9da8269bb0bff0971b4cbc96b1aa1b82ca656eecf9199e739f699935af76ebb6f3e6a6d5554c0e26546fad05e61afd4bff0f2f6fd48f75717355fa57755026c5cd79a0e3d51dd61d909d61c538bf5b852952d7cc80355e03a114a7ab5c2f3b7efa5cfc437d19bf04c7813bd89cf807db2e5f772f3f8323bafa5f8c460a10e261e6131bd1f1b1d4b202c5b92a0a8a27558f41db4d529ea52c91fa13c34edd06c87a69d3498465aa695c654c7f2d232ebca222134400b7eff624d081c3ee455d5e3ebfdd9bfbec282a944781c7529fa6bb3ef2661b82d427fb597d5dbef99bd8617cbeb22ced5d58b891112aa2f81198920f5eb34183e6947182c97fd83f4f25f9ace29b05147b8f2a8d86f46f774d3cc14456e73941fe7c872651c32ae2c57c62db310e9918dc4f564e3223db991589f88d9075316761992d33c2be3f18b31d18f2da365ea4694a54c4485f12813109b4aca54f0624f99c29731061414443c002552a64a4005a4e06b0b4c99b2596380066710330aad4333abbf49268b0f2b97c37a8801395faf08dadb8d922cedd7b4bf743675519e4cfe8f5803b0b21f02af7a9a4ea063d736ce8409f3c412ff2de0d4cdb95a865c9c4c10fae1df737106f2191681e8fedc85824553015f13fe782095958375e60eda942543c88e0c9f930d2c5c130fefa907e0ae2ae3d90f51535739b05c89bb4720713424051234f19a827eb9831902bf8ef9e7c08141d1fde86bd127a9e94ffdee1b033ef6c0509c6474437e7490fcd8d03040850cb11709f957624c409219aa5c1fbc114e019c76b9f26ce6d65143c72b4f503964b536e66e1ca3551f5bfb48db6b44a4d0b1a4eb5a8c45d4a08e26b574662934fa65b2193b74777506da7a9bf1b91704bdb4f1f48dbdce06bdfe66fcaeb7fed82c75e13e78769cff99da11bdb57839ad730e8dad7d3f3a374d582063b41ff17772698181295ac527b00d4912d9b8677abc886cb056a9e94692025e5a40d651c2410f201be31c8f668082d70d78799768ec9706062f77e5c247e0c1fc5a771669369e6968b08a8ef95ad01af8f523f138779c2e00f143630a0542b3c88c7d9026a768b2a59220ee5dd9a7e60995262aad86d18c54d3be61d03524bb07e0bace6745ebf38d8720b0d89ad0e94e06888c766a01c4dfc1acd74c34c6595757aacb448e06ff9b13519e834371608e095ca97c00fa9070f9a3a2656c60de04d9e5160d322cb09f61a601af03e88c116fdf89932de89aa81e4c4f6bfd4c7b35d8b448a1a6509ccc07aa0c8f3feac50d16bee3d8356012e332136e3602d1b0bdba2c476a95de13c55a0e456708c466446ffc246650d0bf7975f1e25ef182c17c21b68dafbac7b5f350723867c68742283cabfda53645220285e7993cb5d42be15ebeb44fb9e20098a940a5e5ba97b88068bf31d21c937228021dd83ddf72806f4dafada517d80cbb2a1ad616a0c8c34c8dcf43b874a6529b8a59b352305d7ba5904cac922438eae232ff51005d5b51feae1da666800ac486a30149daa42be6ff4c67adfa15d4f36951991bdeb543a955502f73415560d2d4656c90585abe3d2e8f5f5087f47ef6f26d841a3923c9394275b8702182aade207e31754c714875c40e000be0410d8bb2b6ef278027c6667f914140c440b2f171208dd46fea3b0bf61484e845a6ae778c34a49cad5a857c371d46c296dd6a1fa088261d2d72ed386a6487229386cf4b766d6547a40993c5e889e498fb6420225e023e70b087bb257dc3f88825c527e43b961c566fef1162186b04e2034ddfe6c46cc94b56786f23268a277e387faf3a1437e7a0e6e9aa89b0e02211d8eb4ed8841871d0dd6bb4b95f2a53cebc8454a60d661e945e7b0eaf4b12e4f72e11e1ca000aec951d39dc192cc0bd10aaea3eed2c8de0e279be0e6806099f63af1cf65815c7eeb0cbc4f0135e5aceea48d7673c7c9230d13a7b63c917a81491aba6c6a95deb6612d1ba4518ea1d192f19d856b7730eafd2233d585cf50c9008af9eaa0438a76f33be977736610262c6c7b92b80b0b79d69313d50bcb128c8c3c006afafae125a0d2e7324eac164b15a7114b50b45256cfb9c9ddb05682352156039170f30275ac54b8a9c7d57d1026b1467286aef638ee62962a953f764f35217bf1959fbf8de20bda8464abe2a1b1fb443a5dd8ac976725cf5f46d610cbd169b5520c5e7e39e35612fe26b51f62dc74e8c4af1700249727356c7a35b98f1819ea6290605df832459761d87900414346c73b1317a0182083d402b3427945cbb70a6fd175c553fff527780b3540f0862d470f08ba0ff0b996df435829a85cb4d88a209aeb211ae51310823be275e782a059dae0490276ec90851815ab2e154f8f4d040d88f00be474ec989bfe5b5646dbf0e9ed6d5adf508d28952fd8709b56ff8b9918c7193c65f4c3a7830c282e8970e39607576edb8269a125b553f20e6228287d43ab777014f916166da2d30f9fade050b3337da80dbe26d78e90c57f1ce6cd1be22146cf8635f27cb36b79ece5c0e1488ad849c475d1e59032a91b7fcb9cf8270be7fcaf6cf547d193e79f0e8e0a53dd62fdc1c2de186b990950cc56364f3fb41387914ea13306f20ebec0e435a080de480f59309e555d629bcdb88731391e901a3c6f89bfd6219bafd83fb93b462a3f8e083b9f4c4e2f099310305b695d07b2beb1488905fa0e7d7f9bed84bdb5fa1ae61a593fc2407d5ab360b80c0d2726452118ad5df256a52f89a121f1a4b21ea7d9126d580cf21ed579cd50462d190f7f1190800534e342e2bcf751d9fce487c31f6a73d801ab2643ac264afcb4193d4c1ce4493aff4ba9cdc09a901552d5cbe1bbd2143e09621cb9e010642eab986dd1119d6ace80a01514e806cf78755a387f329e65f58b9a96b6f58811d4158d443ae722621f50b06cc17fb90402a47231c7743da495d7c6146781bf72741742c9f11a39792bc99236bbb23e56bd624c52e02f93d4d668200349a516afdc3a236fbf6fa2709f371c05e5ca19ed40fc30607aeea624e94500ec5deb793b3a1e6023e39be2bf4902b14a354d45c8c1f90e4b2ba511b8aa37c507fe4dbb6b51b439df3b96f433784cae19acb6dfa58c5ddc5e84c7a16b525293f8334cd59445a0df25542b3c19de38d090db860191e3b403e6fd2d198b1ef205a982ae69407c72a2d6269e8449f0ab005a0cd6e30a362e24ab209802d4227181311ca95c65365811902518046540e9228d0426ee0cb6016906e7d517bf2d291c8d9deb735530cc3858621c07af1d090fd43dc6da8ef32024f350bfc4c066985f490715c44e5ce0dc6c1f2d919630b0a0211cf4dbdfeb50dbe75a9a7a3fb2a8f8bedd073b7e948299cd107d34655c1518c3d68315ee688aceacb0cdf4719b994d61fa26d9f8725bdb8a1e5387ca649262201de892103e2d3a38e930dbc8e9653f6891e5b5a3a5c145c4f9c12fbb433502aaa921f5434526a7ba9ecf1c7d88c70dcb3873bb9048e381af96d8a5d54e101438339673ae82dbf31797881c6826d9cbd986ac74f7f2d76f4913291c6ffe4b5106f1c34d740da01a348faf744afa65c826cf5fc23bd82f570ea0a4aa8656adcee40ac53afd9380c399f8341815789201bc97a89b911a31545c854bd5f4207da1d421135e5efaa5fa65e5e44228e660908577dcb47592f5ba90bc39a8e702851799995c3d1669e06b8efbd205b7e88fe6537cee1d26824ec23ad6a233fc12b6851cb41c832dc38968805a8cbdeb182f886c3b6b0e9fa5bc1fe93ca8b2517c73a32137029c04577b13dcd7687cc7e494caf56eb19e18d6d4c47f991407f021ccfe642aa1b8d5c4fc60213ceac231ede9a69570d986d61ee0919f72199606b3b9b1f3ea9fb83f5fa246063e47154fa243d02099d35a01535d51c0a27fe83bda56bab302fe26daf77966d723b8066232e475227ab8018ae24d387b12782491d82f00432f3135733451a8852b2337e8535f95a0bebf2a81c7b28e22fb6c3a2783c9068681da6e27184d33d1cd7398002fad3a793cf4575ef41b8f924617e2d7a4b92a60f460337d19138a24164abaa4d9a3b05b030616d32371f100c8f388233f81f5d06b48e2354307f25b2eac2693729e36b717d0779501c55f5e671326f084140f2898eaa7dd20efb270f4df36a75ad0a1da25b4dc48f09bcfbf60fca8404be6cdcad4d2e51c707ea673b8c430a1bf7ba569ba28c8021ae89c8bf13185381f11c4e07f75aedd58b0efbbf580ce0bc9e67763a345754806951fd1d9b644c2694eb25ee26a7953e6fb0859a206d5256b318b4de2330c5af8cc7faa28e004e6c8b62e7b18d16d0ac7c206b7fe8d956893973d1c3add8e3572fef0fc4d7b9ec62f4cfeff6e742645605026a18071ab74ac41c86756f818fbc15aebd952a92901f6fe11a85be5e2c39ba4af4d339f2e72d5da3d4f70e666b9efbcdc8c7c9696bc32b32763fb992bc91ebdcde3bda46d61085a65ddbdba91abf2ca13bd9d9e84f0135f107dbf02c8825a23273f29e74bb012e974e5734640a10c56e591295e285cd8ef2db158fb11b9db701ba09e6fbb79328748066dd381e44ba155fbf58f1874444beb241f223f2eaa8a5e0e9e7473692423e49d57d134c5d805ffedeb79079b76f9dcbaf7fa7924bf723ec3d920bf72db9441b09fab8ba402cc38018e79685f78f08c57530f359e796c9623a593217742503d4bfe85291fe3710b955a058f2aecb63d5e617dd3a098f1e19812e94bb692561ff28e32c90e815635a09d06d33ae181b77af95767c941c85fd6b37cfd3b415beef5f4da61fd1bbfe0babf907e5d7f3aa0c6514509b1bfe0b7db0f1a1da1d3b7927bf2a45e9f9d649a954e59307f28c3e997a51e707524ac78a28c3e20d592640f88929584cadd8d3e07267f07fae4ab38ff245789a9315ea54fbfe38be41e22b53ef6a4c1ec048f83d71d000c90f9d5813a0b68dee17493f81e1f0ce88a4511b9dc81042beaf60df6a8c14f53e7414acbe7459458d01fd2494c6d4dd62fe32c0648d94df7038ed48877bcb00134e1daf0c27314d9f63e9f3c062ddb51dcf09d820cfe0fe21bf347f973416f31c1b305a5a22319e57c94dcd2d2e47533cb41cde4ad4cd0de37ee124f0bb48be2eccab57bb39a5a6ce558a965e9da6e1209269494dada75e848c57865f48881782aef44d24c4ca45c23ccc35fc8bd76f4baec8c113e52a858cb8edc96234447ced1bf808d8b4e6d4fa9139ba9ebd7685ca5fa05604101c55dcdde43df3fa89bab1be1e9d8710e65c5197bca642094cf5f909746fee8a5a1079942aca42522bce84755b53e6e38dc8af5083c42ed5c4f0f30ac9f0ffec1efe21b3d437415998d7dd99a6bad0966343b882d87b3a03d65093bbc6d0face1bcf00de70eb9779db84f0450fcceb32ab3aeaa74971833a4376f66435f32f85fa5b3c0d933f8adb03ad333fafafbfbddeb9000a8fc34f949e19e74de33f856c6bdec55ff42264b169d790131d3795e935c3d093a3fb04d7339cf8f88d0384632421461dcb12ac29ba98fed26f3843ebeae5ea3ec26393c05b98cc01e4deb2abd325acd56d3300e05e7f14e1fac0bbfd8d068778c58664ec493e6c28e3fdbbd54026f00b341d55e1e8fc36224217e41c41f7124c68ff296583994b1115019817a6c101aea9a79eaccfb0f53ce50933f1c3037ddcdf9df88277422c088bf787eca8d1c529666ecaf950501a8da4d4077bd2073eb053801da60ca5c0f4e605dfc863abcf85758fcb3707a2a806b15ff7b124b8714260968e43fa5980825c4d4ae0c6ceac494c8f75ec131f10e1ba54098c37f2ae57443a2699ecafee9257e1af7e0dfcc86f226e284cd2d95fef3e1af985f36a355797df2678c600104f6c9572a3b03d2f5bcdf5d8e7233f9469ce76e6f6ac3740b22e4d668daab1064b4727aca5c4866845e590737d217fe414be75a90c275eeb4824c7f9581cd6021d23b4c4434256363beaf0d6cf5e69bd4a83a545fe746940795429369f46df062f9091cb15a0645fc7cdf35f2c5ffa96c66e4e4df3cd4e379cc804d48dca506efe11e1bd1fa083dbdf0814c1e04db0280165589944fed6dc9602d1757c7db0cae629019945f6efbd5117be0517d7a6d32abc182989446278e27dc61f2f15be5daa4d186eeac717cc7c59b991a292e428ce89b54d8ec2dbba933020eeb4691ae16da9e8c8f41131d774200d0412e416e5941d044b86e66a9f91c6263c9c5b047ec7192d2c7e4e4fc5a80b08cd1d2a90b9e2266e45d1d226b7c89b7c83edcccd040492f9e43efaebe36d60cd1f106b92bdd64cc278f7c5f633a3638b4a278fc1d0267d7ff51080b3081a894cc04357ae1a9af24a44e93ad3debf87a33adbe72278d7ddcfb7ec0b0af15b7040981eeb3f4de95197352aeeb5d4d43f31b7c5293356cc2ec9ea6c9d61160a546ce7f2874128501d9b0015b6a888aac12c4f5869e35cdeda204f23c71285b13b3d698880ee047f322df4dc0851892a83f944d8860295d919d061908b549fdd9c6ed458a6c2d82ae3a26adfd349d8c5051541ad3814aa9cb36258a212a9554359953eab0b0b2a965a73711e5b820d383b7191e3cf9f2206cdfb036f0e74e27ff72f166dfc023139db8382e96cfbb7c6bae85714ed525ed7b5e9063f39593d6aa1ac4cdfaceae88e46267855656e5dfa80f02a4a7855d7dcdaba1c4b17389969a91c16f9f7d164200a0492051e0f124b43497a7501bc848f064a3a5bba89b55877abc95d12f955471bdcfe2f8e8868996d85046ef59cf9f7a2499efb359e00988b13540dd05bfab12a82dcc71f6c94e4d2dbb5023466bfa969d2c8d6311c376da973724fe2e5e2a022abb213defb4f5badf49438b2b72ccab7e1741d3d3ff64f684b4f32144e90ac106e9d9eee0b5a1b3f55d5d2f86bf9358986910e9e52b4455502ccf98b3e84ed4f507b10823c250397626601c5e7ef10a6102e2e69c865631757e6ff042e6bd33c817487d14062e8c2fb3ff33b433e81c11eb45d841e8b7f3210f7f401baebb097f167c8cb0f19b3399e1bd16961946cc600b4027094fc5918fc022464de3c6a9f63d8a8d008ce85c3643588d5ddc0a7a9e4251e5c0965f78de5c2af2a1d630a2feebbd2a2c56c743dbaa14623669e4bd84aac10765a0920a0e8e24a0783165f3d7a4dfeeb9c118331548cd6085dd6a1657f914b673a43d382a5c94a1533100c969833211b2cce2f7d4c0e8a2b6ee43e48d7ebde490fa558268c32350b3f37d879e4df06280e1e5209dd02d6228ae1aea0b6a2ba1be722a5b87e54e8e040716c8acb74a05cd0fd828591d64cc8a212222bb48cbb33287d61859900038e28aba3aefa8127d37b331e230c89e950724a320a41d57473ac560ceca8be6d860eb3847570bb218fa4832b8383f1d07c6185cffdf4fbd050cc5bc3772627aa0c6c41dab08aa0d0e6606795c0f9312f8a3454ec4174b923aa4806453b5ba290516b975354f0f7b028fbc1837d4b112aca56a0ef542537e27a097388f30c19e73e0fa5fb5a7d4fc442e37ac9fc100f2c69202e8b0761a81ce2663b996f6537bfd80db0f1def2f6e53cc108a93a878012c09d22e99bfb6e8d6c6014e73bb5a0355255d69157168e366ff6f9dd84a3f91211bbc43578054f8d06aa242e00ebb4ef82810dd9f9f035525e62a06a0f455669a50cb20aa648a0a0a193a64281e0cdcec8e1bf508a5150e8c151876dc17ed5cfde5be063a408588ce1b234cde7e1c70cea13f3a9e91b0244dd6a07566b216101e78fd5d4e2f12e9a25d24c292311f001a348c5499ac580f765a158dc9ccde2b6ec9c2652ba639a7ee2239871e58c823f091b79a35fa9c7507bd62958388e10dc034d8a0aff79bdf919148efd6a943e37af41d02b77d6878120a5407d20ac5f564d3bd56b4b424379570b30c67b9bc76c0dbafa38e0c9dad206ba459710a77d939ad932c89bd9ada06a659b2ddeccabf256411d2201bcf18fc70f6bb7086eee165938051474c0add2e2a7ca88d09322f89dd192d10a9e3812e1b4520aa531abf09a9dd70cd45ce25ed875d875dd7c57fdf601bc9995aa874837b6e362b06ed6a451ecbc7c4e2153315d1b7e8ab006b63b625bf07d3d61e542c1d8d93a1e29f59eb779842fe0f8d53ba9f2050adbf83f55d4a8a2afc2bec688af909d3be772ecac5d7def4db22d562754957bcefe38374333fcca04001d4643a8de12952cc63118ed831af16c5302a8ab1ba86d6a50df16db9bbaef1c9acec01aecea33edcb3935d44ef725d36215acbbfefee97589f24e885746fc17c30823df55662e0b8a8aa5cf7b61a50d4c2d4a01de0b64fb861f5f25c7e6f650dd7594ae53261d5fca1dd0c3697eea9c17169600caa1a4f4720890c99bcb0c881a3914568814794e35ba664d406b1ca5a98482a9636ac5f87a48f8275a582814c580fa7de9196b85d04f99b88f6973f5444ee47426e9f43f7b039cbac09a562c2164d781443dcbc12abc25ef8cf9720199a543940b26f252cc24080edf93d2b9ad0b6946d23beb2c2c4aec216eae43972d4281a903ed104bcd4d329565f33c5b4394cbb05798003a042f92a5614d9c88c6312c2ec9f9191984cffd259aa02fa7514221eae4b4cdd99df5bc65d28e427c822c53ea9124a8632c294a68894b8f9997f1a659a11add9f28f4a067aabebb2b2f6155924ec76e4002f48879390a1bbe4097e639dceb78584dde6c7a1c6e2273bc45385373af6a407c5aa38854ac4dabc52c1ec31cef84ed9b9b9f78a0e72417d433386ad13b54e513c74a2d7c03a536abe3e0d60e0a36bed58a0c57beca67455b21f23d6fa6eabe63d742a39b8db660c02a4ffd2109f9deadc315d2f46ed6947e7a77783c6a9f1e96f4fd17904b8adcee813c2edcfd46baff928d13a26d0d6e70f012fb6c2c558f7b75498b19258b5a54895ed89ef5b0952132a5ede6ef052a0c39d332a3feba6881ebdab30b14646638c944f10b4a8a23fc2acca560eaf703a8be92aab7a374ad7a20493a559ee033e5d7179a3420ab667c23b92e090946356e54413d8a3f9f994d4951664902f087a7289a6526cccc3427d25a329223b7ef7cb5396146589679f0b8b0f9995420d86e42e1be8ab86b5542fda258a22112c38386ece17dff317f24dfcef2391f6fbacdb7eed273bf5eba5078f1adc3cc217213f9f8fbb44be1f7b76807d2fc356a8f12a3af3b4840f82e90f955b6ebd0ba01dfe48fe93182cf46b5a49a8319ae2a1e3d2f976544ca9e49904786e083557c3fe746744a55168afe2b995307b9f3a959cf67aef865143f5ebfe92529fbbb13ba7e4c62df270744817b8dd421bdefb22ccdc022caf0b3e623c0ddb3f26d2540fab3960b928c1dee616aa0943a661d0a00c0d617e829ca59274176ba3b03e376093d4779a3f5e31510f21b22f42e0a78584ab30ff9f46209a7e12ac47365a20e32b3bfb7ffc3021102cec3da550c45cfc3eab4dd773a75f6776fd784dc7c7a3d429c530752ec8665a440c484e5c1006173d9206fe3ced1f346176b0a96471c8f37dac4d9003fcb276e847edee027e111cc48cccb170ca3180eaae9eb064c16cfaf8b95c60688856c923a5fdfdbefa2a36091efc7389c874386fdf96cf074c0a219df3c8c2ee4b7dbbef903c812284a8c1def0e0cd0217a02e7d090a303d7a5b90e60fbd02075997c215d9120a01d96617a41789bc9d444b2ca31f7c02115e3c07d64bbfa66c31ffbf03d87b01d808b6c4aac10ac4144633f0c127254e09314c72b70e591a6bfb3fae21bf1398e2460846e693e940ff6e955fc81c995fbf7dc44024396a20bf9456db6b7178a73e7d6b517a76126a315adedf207f48c3f23c87f715a4774dcb888518cb737328bee0029630b4c2221641a88ecab53c13d53e4a49cd99a523cf084a39c44718a9c39e77abc75cc52518e258000908b4140d1e95155572f9fd10e4da92bfb20fcd5f05b1da861a0ed2d4d8db360064cfac216d9f6c3e852c8445889b7c0c75baad55da3d0bbc6bab11c864a4bd861ac1b2ce33b60a1297901c8e320c7065d915214dc0dbfa353ccf8c9079d627cbff24acd9d606c5a083d99bb4297ce9c8ac03b6702f6336acbcc78229d4e6b8ebf970445b6f13770f67ea19e1e331652135ea579fa83c0d19310a8f1c56410d86270b3c3f34e3d56d6f1b0e729d777719fd100970bc5aca1739e2fe874338448da3ed8ec3b420232e5bbb7e2047af4a1fd2c9a784ccc3bf9efed071d620a73f642e2c10a100cf2fd7a95cabb30683c3d35f01eede3f664be3f233cdb5f9c9ba4532a76ff9c116bc739697b59be0de5d853d373f6abbe6693f6cbe8a3c585663630933bab2c9cb62d1990791a73e9c930a8bcdcd964afd4a8070101ce5036e5c51015f37e763f4fe054f519d52b3af08939a0e1737eb8d1bd0430d64697e8e9eaa766a64176ad72d9c368cd9e43bbf5413b962827988180f7d1d7eaee639f025c6d90c9d38e9a8984edb54f0db9cd130cd04d8e8cad9501b337eb06af41e8f1cf8dca1c6cdaa51e4899483b2d540020a840aef48abb28f921b5fd0a8cc5a7a22705086d2801dbb142c341db6525d9883f0b29a9ec79e25e3f4a91320dafc1c8a89482f14882978dfea24f9a704b90745149931708339d30bacef9dc424315219ebb52473ba00cbb4a2cc3cbbf740bb26eb769b098841f2fa01c12e7ee19fc6bafbba694b5174fa1b158d773f7d3fc43371f40f35926267208d9f358a88f713ef006ec416f56cb48f8d8188ef2482bae7c70b714e67f9f68ea11181a5030eabf0f89076870baef388a7114a4ef1d8e9f6888e1bffecf83c58b8ddfe7f1fb5f4b5b598898affa272e74e756b1d5131cddff09e4c2156d636821abc50228b70e50fc8949de795717c6ad8e32f3666d2d0594c1028be824bf9480438a807266120e81e2f677e16588ba225742039163b980d7068a2319bc84f99e4a5aea44dfb6708016660d4b081fdca35e50aef2023e0d8a377650d676b567f0da3bf88f85b00911076559dd6363b52950ee26ff84e024c4bbf008d85420a60d1d0c072666d15280a29f61860ef73b191d1b9dc353d7da79da1b63763a9684d340a16f816b17fd27e8a6afccd8d9a83629f99f6fdebf92d2bec7429aa5da89b964627271891f98fbc60e6f935ff871ee2d3a691dd96c231130a15e448c34474c0fb22984cc09e95d857e417407b613d713551277c1c2b946e2fe8b49a0ec7bb7722cadd239f3ed34ef3ecefe9d6d1f0e5ff772f762bc7d766fcd5d3eb3f1dd8ca01b0a280470b6932ccde6630e2f9dd3d212a7cda07e1ab8f63218f4422c717bd26b945add588c9944804f4309ea331f0c2a4340cfc462e3e921913d3c30b2634cd845d09a2201e23b8408676af8d8c9e266a7c42d6d262d1e200442211b6876d4b9ef7efb0ae2ce18eb50522ebda62b10e76e8c59a8e549a5d89ff9f3d526354a27186f92828d830a6280739eca68b99421c7c69b041ec415724f940c8fcbdbc0d6964c9b4e24d353685c2fb1bfbd60d8638f8e37591d7ab3a723c7f04b2300a3aa968137a00e8cd4e9a73aa086d2a71ee6c17572bb66cce65d77968037077999338c56ece3f3b1f9d37b27a4109b89771ba8b22694817fba7d3589ae022b51da7dc814475d50d2f25db0b183d77cad673415467f5ae84b5dd488923483d7fed4fc27c6528ad1aadb30a6e5741604adda09e0bb17c9b9c5f2aa694a489f8f5ddd65fbb5a9b5a19eb90bf5fb42bc82d2e58253f4782e5240e7bd21f85b2bc8b082e94e88d7aa17fc488dfdd74f17bcbf3bd1ee77bf900f6ad306bd471318b240ffe8ab8b8de8c63c7c4d0ca54b585c06885973c1579a78e0af4c4b2d1f3a5170cf2ff88a3c528f182a6a4e4d70f109aceda3703ea2d0480f5b4621e6962bd11b92280f4513bf1b8421d77279b1f699b00b9d4cbcbd339394e78558a678ebce52e973180e0cb5f69e49055fca9d348d517963f062c2847c992566b0e258152b0f7660331a5b5539f201ca8400e14fa3c606bcc6b17cfa3b8235d99e81488eb80d7d693eec6a1650de61da495bc6677710bd88ed397fff89df8019e824f311cc46f1b563b24dedf6f9904652b3c036cb8aca9e50aa0b069adc24a819ae7b9ff3919fae778c1209dd9cf033a3856fb247b58fa7290134a70cf347befd96592005b30742d5d6da30220cfd9ef302a9edec22bd2c6108c66ddd5332ac241e895e6da402bbae0793acc3ffe8836e488ae41d4026c5e84fe89b30d48410ddec5208f5163d193d3f53567e0c970d29fddbe8c0f5dc6b5889dc64950062c8d6121ef77a8df466bd0744328ad684af186efc6b503740638dde679ad9075a888e82ba319389396f153c7a06d3e40228ec3edc8829368801892f7c8a4a4fcb64a6838b3eef293b6b93044388c36519903d1585ca869e2698883dcd7e0e736b82503674a41a4db114a29159d2a969c6c4960d5d4968f35056497825fc0b62fe386ff41df154ccde7e9f1608deef5eae44f92d6c0653bd02157f01dc92b3e263989c973076c62eabedf5482de9b6894e07343a5504b43c0a714183603f9a772681691ca06177c4858f0364112aa0202cd0686608f6ef6c2317c71ecb9420c14ce9b613ef37737c38498582c9096e678a6931b63f221ac51a88151049989cfc5e26cf5ff5a985da4765eb9b9aba0935b9aeaa111bf0c0fab80197143fea65fc72b8a20c92ef3e1e95f2f113964c62045d927ef5f8e6d6ecdfe1c1a61d76594b733aaa0d2aa5faf37d34b5419614747d3331019919ad44cb61e9249a64f1963a31961eb73f3fa241dd0b6458efc32ed027ad32001af542f88bbab901090bb050ff3209ae60261800af87a2b48781f1be9e217e73b312f65330199c986f0b410c96084e649927277a5821ca4fc0331d35e5e01cd2f9571e01be71f6e139ccf2955b7022e40aaa0b41231ecb04990b9faa8130f98b85fddf9d0104135d1404d345600c2a957224f54597bf6204f1c1c21ace37f53610a894f7d31b6157a926032ba6f10f196fb234ec92f83c9cd4cbf6564fb87a25373110262e8b1e1f0668039e15d3b87daaf56fffb89d8818e510ceaa04544f699ca41269f05f390dffc5a35bfaa793e1c855ab26e9ee94142d177b21907f16f1aca278622b8ea5cb3ef17e54581ae544affbe98657c34527a33593ccf329a87cccc376934032f810123119b9614706a022ca15c9e123ab099bf403d4360b851d9a1c9934e0d0e036623bf58c84ab038aea1a0f0d7b7a35eb1443d85bbde344d60673d47586833a43049d7936aca593c56912f442743ce9ba38c3f9edb0beabc760e53016774506254fc9066e1d806bf571b59e717df00b1ee9bd68f91ceda0f5e5e545055fc8230389403da48c1917014673d5003702619021c2081b164935cefafd5047804e60881b7f80e53feabb962d78fb45a987f49e78095bf405962e43a480f41fb99e1b5f011bf083c0aeb0c15f0f601104dbe20e704e82fcdfad678f62f63a6e6361ccf9fbf958af7bff86d3836b969e4b698c2b17ac5af258779ba0eb3471df7bf1ed86bc1dbafdd0e14e90b34d350cd01c9756cdb02e2678329ac523bf59aab524921ca61b583d5326c1d6be6866cd1ea0deced545f4483e5fd25cedc7a1e3d6d9c25c9419b52912d525d1c7a59e86dee1e6005727e8ccc3978cf9273f636c57ce75f014ffa255c29f68c2adc6b7d653986b064e5a7e9465e948d732a3d74204f3b5aae9c1814ceb47b15acb23e18aa8d6988fefe35a7e4cfc59e7b2d2de629f88a5eb3e603d2af9710711245d87e6a8da59f5dce253bc5e32db106e94a2d52d1b7b484aa9d4f4a602322a3462d11cd901ebf187b29b08cb62c603de9485b035a7b7cc88538408646033505409f3dea700264ea5c95cd20bdfb0e5f068ec36048f207c39e20c5a15f80e01b646e49299f186c2f3f17887911b72b338c07e6624754e7e03a1bbe95c6b627da6418a4869d3bc07c090dab8d810fb844114eea05d018380a33efbc47ea4fe2e60af20d909fdd3f47f1c6733ed55a14fa1c7ab05c6a5dde27880610484178df3bf7303b8d853e422c01a063613fc2f9d0be43f03ea428e765d3050ba9a0e453711f16fb08caec595276a9f8736024819a0896e708dbc4c207de2da0baf7d146f735f29db8a73098ea78b195740d82a541017424438c8c8586a40170d7e6bbc39bc1da0b0d785e753e5f6adc9bdf74a6b726f2965923268074d07dc0655c4d440777bd666b8d98c72066badd5fdabb5566f159b7ae5d81d8577ec2986911846621899734eeb3fc3e674ee5cadb6d6397b6cad3fb5be7aea9cfee274e95ac75add69adb5babbbb0c30540ca18e90a9577b39b0c3a7af029e7305e76514d622cc79de9c9ca2cb28d445a1bcd3e9240ed5564d6bfb753edb00bbb451f9e4d1b5e53f34c4e1d13ff03d345dfe3160a0a91e3377ede9b96b336a5207eb3884e9f49960d8d50c2de190d3a10b869cb461ea0ea554d6f6cc5431af9f3c545b617d592c23e638ea5f7b264489437ae64f3471c849bb87cad7eb872acf84551f9aae72de86a6cfbc6941a6d618b7d65ad52a96aa74ea5a6f686d4e159a7e053bcf7e39b7db4dd3e766063c3a1e61adcd51410e143950e440a1e9779f1385a68f3f278a4c766269bdea3b73ce69abb5d6666bad2dadb5f6f3e657476bad9d9352fae3ce9f1a7f3d9c9cdf75e56c02394e2c2db927165bafc5be3329a5b8524aadb5d65a4b5a6b73aab0d64e4a29b555acb5768635c55e7be9f50077ba270e45e97827796f8c17642e256a4da7cbedb5578a6b9f68b1578bbd5abe6b6d5bec96bb858ba53366559341183415b798e40dedc5f2e4c9b54fa8d8fbe4c9b54fa8d86befb54fecb597cab54f7cb6eeab505bb5016b474be5b68a70dbf649f1cc9e6f299571299e63052a66cf27e90ed2ca57ef3b061639a5fa2f4fa9554a7c127ec74c195da98ccc0c4da855af12a913d1488fd4c88bd17c6b65e5b30d40803d7fc4279daa117113fa893d1f8b3d9f01f3a8d41efbf2433ff2a39b1dc1ef83ba739cf86278ef3ba71e1fef8627e6533319a023094f68fa25ecf8d123c8f741dd7504529ba9ad984f840a8b19694e0a34fd92c6448765ea6991de7b67be204da757d88df970d4641b80351eb501a1c6a0e9d713123e12bf58ad3e1ce0af3e107c9fbec663793266602034e6cd21d47c08d0db08420f983ea7ce66a3351c23212161a135fcc3307cfa35043f2e1e6af413d7dd6cb7b761242c1d12878445e806e4b13e1cddb33e107c733266743d6c388fe31bb1d6f48022d11a8ec9e9c3d11b175ac33117fd00e7f1a3dd61a47fcad11b173c72e9093f1e5e19070747c60c8e3cfd3c9dec3e7991cbd8f6f30c6bf30cef677c38eccff840f039edabdbf3f5e4f1d89d1ed01bad91125faf568ee454bce5d4038f326bf85fd9fe788c6d2762bbcc7bb8f9d174f91f559fcaa3fa72b5752aa5e2c0a6634ac4540024d000a09919dd063a016c40b1f547c2b5b9a8369d95f376674471ef00ee9d5c76bcc89065635da464df5f15b560df7f12896cdf7f21b162dfeff19d594b22c4beff23c3be0f746b1f1062df9a12d9a6624d0628d87af15aafa8eea1351994844d47b86cee2df7331f0e2e0b9702ce88118f91dae67e7504cae65ee635c77d4f03388ee3381b45be6cfbaa0f8775a24896cd3df7a91a13319c2089224236f75a0b07030c9b9b6273351858b0b90f3f1c9ccd131cd3e66c645e90a25f206235c330758999a58cccc9b6af652d7021896d3fafbae8620645009b0b4d889c404c630b111c5ac0b2add5984b6d3b6b2d54196285b6d6be008bcf5a6b6d7953e674d9bf4b1b9bdcdc630d6673bf426273ff5c4d080b6a438cd8dc95cdf5ccc9c9c04ba61f8e3b806d6fc049382a0306d698c41a5b3ce3860fe03471938164e4260b0de70431b09028baa1d1608345c6ba01151fa8c9d26473832d70586999d5c850438ca4d591110dd013b318b325334036231a2d61a880071999154d754518377061865b93198c8040226b12e208d3172fc880ae2c5971a1698919806a342a8a002531c32e40edaeebba6e89db5d4249d7625e1401a2156121483abc05a9db92d46d99755b68dd161ba9044907c4c5c96907241c9012332cc4916b719622599474576ab047281db1a4a728026262068324404d14d556082309500a906a2c202902a485a90035cc5e74d5552ee6882cb427596eaa1db2c840054683436246a055496656d032505253689d84027d693345b250a1250a2f5abe38a18549860a8df7d4a2e50b15171db382ea07cd29494dd12926334d34cd9e5a6a3d245133e26b4a1449bae2ad890573d15e565f1e0cc985b78092c2786b6e9f6d1517b52764b468bba796da14648e19906c4e9a286dfca5e755b418e33fedf93154ac983e74fbe3afd1a48d98fea500338f62632736ce63e94b6cfc355274f7a527e59fbbf4a2a25c83a4bbb7b1d1b46ba2e8249f394d69e322cc8d45f7bbeecb196591cc162e9a2efca5e36701a11dc991307e95e8b3dac29f124f9f2739893fc7047a6efc81f06d3b7ad274e1cfa9a249d8f103bf0b993e73e327a913d4e3931148cc9deff1d325e68ef7f8e915d3670574077cfcd405d465bed33dd6f869acc680f24c8a45c514d760e32eb2d8f869166a453fd621fc2e5e0a34fe86d8185bfcdd57c344d3cfdd9723fc9a10e850f49cc4f88b1736d64e62cd12a17eea75d44f8db3b674f8afacd0397b93b3a2f728f1fb2cd29dedb4b482747fa215b19317754a297d5764fb389f5055e8fafe27db4befd74be6e7556b74d05f152f12924bc0b4e7d78468da73fce1f36376e9813d573ff5b3ce0f7ac69e32eccf7b69d3a700bbfeb571b935489a367952e561ddfa7e54ff3e0a3f6af4a4ee4f01618411c60db68f285018e43cb37572de68d9303d5f9b1238188d134bbe5a72a8a9638b6badd5d65aabb5a7d3e974aab5d65a6bad5c47bbaee3b8aed66a6badb5d66a6badb6d65a6dad15e3132a67cffbbe39e7e994ed0cf79611fd39fd72ce9e73e5b462cffb3ef0e569ec692c23f37defa54070d5d3d201d4614aab625229952a26464666868666b5aa51a9626264646666686862c458d9d8b05895c562b1626662b474909999a1a189b1aab1b161dddce05c1c1c1c8b835371707070701c0787e2e0e0e0ac563536362cd6cd0d0e8e0c193366fcafbc9a1acf864559363636ac1b1c19335e1c3525bade002679e6284dfadee64197da53aa23559aa2cb8a54912a15af94d699906e97fa8427593c1962852a3fc460d7caf665620643f6bd47c840b6ef5dd5a6ccb0bf3d6b5364bbd4ff84c8a949d6440bb8fe1379d1175ae4a636e7d1de06b2fd6cdd392de32d8b231c6fd0e1b66fff73e8bbcf9cf6c9ef41a0ad632af2ccc9f39df49cb49131455f9bef70f19deeebf7f56dcb484d72b2dab14c81b64bfba8cf4eb6683f1c1cf7dc13ed24ce23dab6044fda9e0d988dfa9ffacd3d3fd4153d19336b5b4774e94df46314e6e57ab46ba542a3093d77e94d4d1837616f2c57388f25e9348c6db575c4f867c516e3b2849e8d31f658f5ebc52dd05f7da0086442db2b2412fad2eecc494a6f598d68910bbafcfdca216556ab22f41f200404837e2773cb88b6329a2e7f7b978608ae46bee3458a9af88e13968fbbca76b5a3cc9539496b1105da97860ab4ff4ff59e2aabb2ca4361f46bd1bc6d7a336934d86beb9ad866f8bd89bdfad2374b1dcb39bb79ad6898b2c5b6654693d5aa43de38496b1de78d93eec5a0cb2e7bac9dfee49e5a605064ef388e409724ebfe6c40d5f9f11dfcc276a11148d0d55e72b6ac84610399e32bb0f316a959d2b7e8ca9cb4b66d1f8441df2fafcc075d5e59d155a2a534ed59eb810b1b7bd67a986da7718fb1d6abd57f75dab65dc7793f1c1e89b6ffa97bee7fdc45a24dfd943fd35a4716d176b1b4412a4739dfdee83f358ba8718aa7fff94ee38d104d975eaef5ddb7687f1ac20c63fbf56cee3bc98d7faf134e697bcb8876d924b368e3c6ae2ba4a379655722f69d3cb8b61ccaab0e7defefd36695a028bd26596decfa8050fc6965b8a05f42fb6a6f4567508f7def75e2b904fa31f9129aab15a774c1a0b100bb70398ea3180083065e7459420a2921c0b22c4ee0c9408b196ce7c21516b25420382dbbece92169977336afd47a38aaffaa349d3d6b2208b3f59e351180e9626ef2c67b745c39e94d1f3f4203bd933aafd94d8ce9f29fb1a2636caf3192b0112dbb437dc8e99a6f9374f6398e83cdddf8ad97bb95b363ce74dd59b99c6e5a1a32396ff634c2b6e612442598ad6d25507737e64c97a5404e05e6b4651372627b8a7373b13d45d67cdf73dbb838e93856f47c9a9a3409e5d1b5528e7a3f290f18bee6e2af1a4bacf558d2b19c496ca756e6cc1b4b6a821ed458d2116cff1315c1f6770a82ed762c2993ed5fc7d94465bea39d1451a05bb335b91896b0c23427075e94ec1054c62466a52e6cfa3ad3676e2c2e74b1eb780314995aafeed4ad739c2d57ea4e6b9dd9d2f1da7a6db52b983912a528e54394584ceb3d6b474c2e30fe48c9061183289cb5d67659acdc5ce1a18887298e72080116cea60b143bece0c40e5d6ceeef15008022c500766061736fa980c2a4622e8e52736ef766505a7882c4b637744670ee6ed71525c14728107ca02b3a9140062e86af24b90e280153074512eb80216cdc5251754092edd640d054833ba00a927d826409d293a31b1c6082251718e0926f017364a557ae3db56c01e3ade451095d4e4a77788f93f9655ef54332e3508c2b5af5310f941de527b746a9cfa9d4833077ea897615cb27418ba930f4be8aa058f6d85ecf78a347c67ae3d3c844d64f48d5a31a9fbe7dd5abde3a8b8e3d603fbce56f43440b01d570a38863c1abbbbbbb1299560e77139fdeffb1f8ddfd27ad9def5f79266c45ddef7ceb3f7de6757b44ced6add320bb79f85b51c7dddd4927dddd6bcd9917076bbd5aeb75de79113049fbf3a9f5a739d5565bed77b6daea95029cadd756af34c8d5c3397d68d7838049de9a0430a0038b807d41388901c0ae7ddf76106ecea7cf320014b8fb5614f49043d1161adcb396030b4a30c5a104d90b8a83950e2c0f0eb19d9596d8b6b5051abb81e9a502af94b9b1cb9b1c1d1b37ecb4d3cb6d97f38b0ab8e0d9e50cb324896ddfdac7da28866d7f65b4c5b6ff640d53b6fd179316b6fd9e1f26406cfb4036bcb0ed0b0dd920c4b64f020cdbd69a5cb1edf798b51b86d8f669cc06366697f400439051d1121220095aa274995c61b31dd9aa4811b169f1395b82f8573db2ff19e9b2bc61e3e6857ca75c8d30c43df7372c9d69fae59c511b2fce9f7b72e67041501eba771c268410f439e88b404bf0a79dd8c9f0986fbf136d882f3bb242b03ff7fd6d4344afcad79e7f455b5fdba0c9121a6692e8195e18e955152a21f40c134bb48c10315c74ee41a64a0e2926fadbb34643125510dadbb34603119514fa6403992ffac445ea0b8dc12cc9228ae6f6accd109e67c52002e8c2760b02a6c20d2fa4b001cc7e158378932e3015646e80d9717a07a5b6e2ace249132dc0aa3853500398165a1349db5fa767fb2b0186e2a9c17bad30214416581565acc0ea15491c6de2529bfb1c72380885617ffb29cc5a3f051f4f8f7a6c471d24e9a7f7510885a1de8e43882c00767a1985a17eb5eaa9ad11a8b63811260cf51db08043c87eae41464b3f8514f038545bdcd3ef8005fcfe3af0e8409af0773f02f6a71df51d321c42a7cfa820534640a13ea16c94476ad4010b2535f2e8375a1d4938b2fdfa320ac39fc711ea1864089105c0f0cb28ac7b3b0aa130ee6dd840e3efac5bd0070d83d26a8bc3df8d2c218c290c857a0a43a1be7e0a29a0467aab2dee6be5e8526dd9aff6fd75d8771c14e631eaa74e69578ff910bceb05e3adbab4851600115d527a99ee526dd5efee7b07367d6f8d13081d6d9898efd057aa3fd43a6badb5d62a525b6bb56e5d48bd077e9e6116b50fbab32bffec44716b71f6a8f9bea6e6cb5fc5ee5b51a268fb397ff9e9e7ba79d8f0e08c43a674a7cf3f2a8893bf10e704a24db4c30743ef432f7c4f7f5f8f301cbfb0c7076483ef7963f9a386dff77d1feb83f03dd0f3f2dbe9c37ad5835eae15bcf1bc5a6b8fd8fe51c11b96e8836ece7bf0f573a29daeea7df8856108e4531fd630280cc3f07f6a48c2063d30f4beef77ea3f71f5e5f8beeffbbeef0bdffbaa9681361fc883e66bde86e6c3ef6b6a80d488e08fe3cf9815f43dcd7f5e0d9fc8c38450d814d5377635fc4e04c72ff5def7e1b07ea29d2ebc81ec39bdefc3e158e295e8e3f41f0fceb73c6f87f204a2ed3dfd1c3e6843714ed737e6ffc459fea8294f83558bdd839ef760f54016a86b0ccf53e52c53bff27bf00b6bf83131c499a711651e0465de9b91a9aa30fc7e3e183e1886ddfe38d8f49f8860e5365f38eeadd2a6cf7d0ef3e6b86efc218edd98837bee6dd771dfcd6ed2d8a67f9936fd0f47bd9fa31bbf7e39e87341d6ef5b13e8ae220a74d3fa459e39397722d106bf70ecfe134b1bf48da9fff19fda89de875c97c227aa3e71be6a2cb9f1870affe3aa3135faf6c409c3ea696a484eb6f72ba48d2d04687ef59c98c3fbd4672a762d1e4ea76ff1a06d97f1a7445650f7f4bb9196348f826f1e359f1f87f3033f1e60a6d413c1cfe20c1f0cc33c3f4f85a0d453d04bfd7cf0a70a1bc8ce4fbf0f78783f774a836349b43d8f077d2f259e40b4e98763e6a727f3f3591f6c1e313f770cd57f9fe97485ff4d577e1abeab38ee0422d558e69fbeb3536399df0420b1fdc3f7f73fd5870a5b3f08beb30fba733e8168a7c6395d610c911594fafca991c7c883be170404cb203bf5a907c56a1d0c02c11efb8b09c792c6be9807c792c6760cf8e1ffb8c5a13f977aa7de13bd8add049aa37b4f25fa08b2657e7e4132ef8974bc5af451b7cc7be3a5cf4366cc2cfd319f5f4664e9cf2f6342d018e49544b19dc7203c64e791c7fcafe665441adb74ee70c78834b63f1e34bffa1a9a077ff5fe05d1fcea734ee54f89d9bd95f8fdfc42a0f9d55812edd5aa0c82fff49fc80af29ee6bda7412adaf8c1cf9312d1b3f6836dcf6c3af2e866f7aa0c7e27e6487562e8d92050e79448412d8a65903dbbe93dcd58d24d73bf1cdd4f27333896767b793c7d27d62f077ef04f621e41f06b8540d30e14cb13f2ce0f4f7df83cbeee274d8da5efd47b9f59ffc2c3a6b7bacfcf12a7b7f2a780ecefc3fdcd2f47dedfff388eef4191d5bdeaf3b3ba4f7d58766350eac3eeb518860f82af43b14c7da95363508c67b1b42a0c3f3f28b2c04e64c97ccc779f5722cdc71067fefbb248f32b317f8c995c8a253806d13c0bfcd47f5f821fbe1659e01894faefe7c7f8b24af4667ee66388ded3cc783f4da0dbfb502c65c620d5b3bef21b83523f3ffc1c23ea30257edf89fa63c4ee5562eae717942a89be39370886c0c1a6df0f5a42dddd8882ef16144d7f72ad23eda37bee4fe27c2cd2ed311a67c5f207a753d0e9e41b2596f81bed09c45ee7a1c43c96166fd4177efd94c8e3f4fede7fe38f7ff9515fcae6407d238f53cefaf4e91cf9bff7443d9641f6d30f47aa0c73e41e1bf5df6b31f55a9c5e4e8d650af5c391bdb75f8ed40fffdea242efb388fa4e1fa0442fc7f7de784205d9de7fe374d21bcb3cce47a14e38c82ee7fff04d4bd813a7a043fe8096e107d483a86f3c620884de6fee33fe1ca0881f25e2c86349048447f7fab593f9b10fba518f12ed74e507c512e7387d414e85ccd0ff7dfe3aa44d3f3f04c03f3d16bfb1044271798200b80307ea830038f2a0ff870abb0b72e7d1e3f456fe50fc9147baf307dd6711e7c8df61b13c05a13e9fc61f75773f9f5e3adf004ef4f9bdc2babbe377d25f50fbc1c90ee7a4d45213e8f6afcffd8f6f73f462cb0aa27f9ffae8461434501271e309f77fdc7dc713eea390da773c81fe8f6f53fa76f4d10538c8b0e08ce86084d8440b530b921b1607f0423667aeacebfe0fe48eb1d8da11b3edfe5e90b8c71bb702bf502fea9a812e5fb4ea4c78d54197af6bbb515c31a0ae7b34c75d71d204d63110caea54c1dd17670de7460d00cdcdaeae81567233c4bb615c4b682049ca9d4b0a0da8ebe8ed761bd2faa66faf5bcf50e559e13a6ffa48c764394597acad936d86903c7328b7e793b13dcbd59e74eaf8744cd1de5508f7fdba73234bc6ceaa0ed9f7ccc9039842d317a18bc0e9710a275c7952e8fe747afca711d716f7a7116bea34062f9ac090628217dbdcfbe6c0056d67d5d22a0f8971a5d1e8d7d1a3d526dc908e0902ddda49f23e698dbab1d4c9e5bc716379e346e378937376498ba0c6a9b34b5a44474699a08374a2c326d9a3daf2ff466b4404d3317508899e886bcbfbec8d137ccf7a6310cfbe7f23809fc720f9934a7b64f445808a4061df48d6d6f46acb3e46fd69d6166754de995d122bc65a1bd1d992ff4a2fd9a6a5ca33619549c78a36cdc8fafb0ffac783eed3bfe36a466b0111bbd4fbd6198201d483d2287aacf13f16ea2e2c5cab6a1626fc28fbd2d1429793e9252ba2956861b48303d3e7ce806837a521b0d81fafeb05d7437a022f2029542f0ea88aac88efec983ef54ab99673a27dc84719c976abe9f2de6c379b4de9968ff2d1cd9691bc59ea6857cf28739683ed7946d4833b7a48668dfaf5bd59e5398d5e6cfc00935db1e7cdbcd89193d593b2ebe56e77470f69d7fff1264ed6e99a4197de2cb6eba76e9e51ca8bef5ccfa8f26427db7372b4eb9167b46b4eda3569d7e7625cec72322ee6cdbc594a49ffa82810d447e0954bb5ae2cae8bc8e5a4e0ba77a9409773f6e58dd207ca2ee7cc877a508f76eef017616e4d941419b25f1ccc1a7f803fe95cd137a525a6db74a235e8cf60eef0252aa34ec307127a2ee0535d50743997fcebd2d2d28e1d4b4b4b3b969696260b3a06a5e8d485047c016e14c24226e444975466c354663ff85f99baa850b1357591e32daa230505ea41e56a6cdef333d21ad469d8809464a3224566b9f71f02425f745a837e156f48994c2693bd349661990c17a14b2a7352df3b9a16ac825198d220425d87065e996acf0e0460d203179096fc8147c75b14572f5cfc0616b73a449049f70b11b32f576e72746cdcc8c08e1e95a77bfdd3e904d1d1e081081decc3690b9c51b7722e35d12b1d285e0c9f0cf4684ce845c6481a2377d09c91af8e928e40da2519db3446871479a40e8d91218d22b8cb5deaa2015e4decd88f74396f5c12e92d262a7326a507ca8bfe12937397bb55270cfae22e77ad6b4893e52e772f8d1866d3bc5e93906d0695ba94e68fcfaa92caf3c2c36e65c2da6c3621219bcd2664b3d958a0cb695baaff433d705ac366b30909d96c36219bcd966ba5b46225277439673f0df039539100b58494a4842e6f12521212d22c09b5845a4a424231e11e540f75118131751a514a1e9ee933ab349834a9d6b50217b6db55e22e77af8e0bf4abe7a266f0638552a16f982fb41f4a63051a0d088846a301d16834265d4eda17279b56a4cbc551ed72e5ca152656c8c20a152a4b9ee05643362f4a230722ac12959b141b94ed94ba16af4ec8d9c2ab02f80adbd594d58b882b58b878d161d38f02c4a67fa7f077626ed92244807a60af5c8ec6142e5cf8cef4a93fc265b229d7e94cd1619d27f8e44ac2bfce3aee07eff1358f784d970e0eba9cb7cbd4642bf19d3a7dec439172a95c228ab6cfdbad61b6af110b0a961fc0ec89c5072e587c30228a21372eb2d2ae444ca5ddf75acf77885a46e8f232fd709256d1205c71fa0e91fdf995f452995f50f73297a9b6301499ded366a48bdd45c99eb62348bbccb75d7fb5a7ed88d12ee9b6df75dd13594c6467ea322d55dcd9ac04025718aa83c5adad77ec7175a93b767d1c25ac7a681cb1ae2357e709cf658356f29db2a78746a371401d108d4603a2d1689dc35c55f46a4f5c238411484f14a171b7309f437556e035914eaeaabd7af6b33b3acc858326ad40d9faa74ad1f66f805093ed7f630825dbdffacc06d9758c792cd03da99f1933a3f30637aab4e4fac267b3d9cf9cfd6ce2d9cf260d1cb4175aff7ed94c78490705141c3c50404701a79f4f5aa0f29cc6a0d9aa3f0d007b0044d1f6b9b7b0fa3d80d058fd21bc69b2b0df510f401417bb63100817e778ab7e0eaba608fddbbb37b7b9746b5d11a08ab6631109ba9f39e54b9cd355835edbe1dbdd7b7dd43ac5ce3b4a5b55f4fcf998d617cc18ccdad47e1478da1a1248ecbc67cd8899d1e98b4a290a2c60f062430d4b47b81778b102a7b9219ab20984ac5093c20a2ab07851f1c4eef6ac5181250c6c64841630e182054c2538e20517b980d2161958b182165808f12b2de04064d6a808e342e2059bbe02b0674d0b1f36b8674d0b27572cd02483152cb684e9f203cc1ffbebff2420129850a2331a107597124de8e564dbbdba90d4a12e96860cd5fe58b94de8a1cbd15882398f924056122a4c078c2ea75218dff19dfab585756a804f349e547a803d39703b3462561754f9d8d5a4ef5fa35d4e25eb2d27334ad4664cb599129b65072e6c464db61a8e86044103145b11b04ca17802a916050cacc0aa38b7e8024667e8e1871a868065eb5971002000a20a0fb02ad2102801cbd70bd9134e7041e4600258eea64081049bb3208a4802cba72850fc80f2ac38c17811032b60559c2e38012c67cf8a02f8e22507236055a4301401cb019ea9a804cd1865358568060000002001a315000020100c088522915014a689268c0f14800c7188426e52381a8623418ec33008821806328618048c014401828c52366403980969f055d0e3f422f42027216e65593d900d1915e9498e863093c76fb43146057df32006757c3fe2ff1780ad5c2cec58f95f59ea5030d6caf9759a8085d1c4671d21c36be5a5ab08616eedbf1f0ebf082508fa549c527e03b9010799b61a9b8bc8ce0b4a1f0a9675cd8b113f42518d27081c91ad4795676894cab8ba98ab11bcd650c82fd9828efb263bade1ae1167f423ebba0a72cac5b67a90e4ef32a4318c05592298751d232e58594073a25e80db4bdda5b64bf834ab20d900a6fef8664112201f9fb80e183dbc9651beb414dd5b59ce37b1dc6ef754c2ad9bf01ef2dadfaaabfa8e6fc7d406bd3468bee0f617b7f6310418443cf654d88c058f3edc657feb309d8d1e11eddd1d12f514bc165a0e58646fd21e9d201bce90ab0d6f9e0268493636d8b1573a559d379a89f851cd2f9a6646b3cef9e482d4834ab2aa7c008f359dc71ffcb8218562cc5bfa437eae15b48013d93a7ef95672d921c7822f89bd2315d21bc9ee5655d68254b1fb1301936373697003c0113a975efde615b434545552bb6fa1ab1dbd3cad9366554d32155ac3bbbfde0a3d9f0add7275c10d2fc1916c2f1e9d3f4876377e15788c9d8179852a2996adb0750e8b4bb861dd276bfddb58d1427f4dc5dfc8d8d0719dbdf7b8d2096f3f2476c8b40aea8eeeed7ae909008c6c7a42609945be9198c7a3471751302a6cad801303d23f26fd3b2436b9a7b239a4917193b4120493f04a295ab351f16b7c7183c40a94084195bf24094594bda3a11f2b3edef197117f1603fd0cebe12346806fc26641588de045d06dec943cb898b432d5d0fceee54d7ce52c1d24cd90fda14654c4d57a03b27a352f5d185794531d98fcc129574c486a1031c86cbf58ae399596702bcec955507d722dc54645481a28341d2415588dc15d70bf5a6c74ff224f913f8ee6bbc82d10947e573daff92765aaad4dddccdc880849bdfd7921a33f26250d9a85a471b3d5eb1f4b4ac92890db32daf8abb565e56beea71af348c1f8febe3a68af5a350d534d8e109b007d58150018f33c1ead6ebc325882d0128cd07054f8183f1ae0e940011ff3fd2df43fda418881dda91009133d912f24cb617c33fa89c87dafb584484950bed81e7d903d4e6c5ca4a324c217e7aa42e9ba848dd386dd87fddaa9c4445954db003081e1bdbb55fff201d030b5a19a7d68c8322c16e768d8564fcd66a74861c31574b4206c9be4be638dab6695184dc4253fba4a756b26bb9f16f9b3acd3b52431978cee9c0a29896d1e26428f5bd6761e18a5e90ac6661af50beb98fde4909e15a1d8f6aa41722c8b69b597ad75ba3ddb0b2314c16801b19336255870827f523bb2e4e823539dd12ada6443e95f63179eaa0f5e44156b2d1153e7b1e9b9ff835d3ddb8949ae7c388a0414862c3fc1bcf9eb1398a7060eed99bc85b685615fb668b4f120eff5ee3a045d2bc4e5ee3c921180b20579029056ff2b57a4287a8433226939a75e8a1364ec822f5dd37d1f8982d67a61d212c9082b4ea0606bc7fb780b92cf7065340040330bc4f031be3ca8f0caf139e0b8208c227d4df909bde7612020095d528b6c13ab238c85ffb3c72e0d89a8fff4631772743f1c1dc06a0ce62fd0b99c7561f95efd7e5d12d170ba45740398ae575247393ef30e8d4299bfe146e2bac74e8b15f8414bdb494312b021de76b8a6da88573b91bcaa09912c6e6420cc09b08289096d1c1f268e30d642f38dd56b3739cdbc606fd263e87d0543302454d7910df1c64efcf05278127b24ed63ae7a53de9134c71aac276c844052922001b60b2ac488eff304e3e551576b8d50763522e3517c7559ea99bac638c03efc4e234b258f985263be6299af4b14e777a61e4b714b3d7cdac4091affb2c08238471a0ce3401500216a28e72dc014f9285292931940013e0fc51eb9aa48848e647d17d949571242673b7ec4447d467fc48c04bb9885e3db3e3a49659123c32a3e277d2e60f19bb2626f8a3c701f65aa4db48778d991997545ba8144a050350d9f650731b30003a218a9089c19772b69a9318dcb15819e405ae46940751cd22355e6d830390249cafc32d699cf11ca7c5af334ab6ffa1f92a2c696dd8760cf5e2b37f02249001d89a4104c52c5f499ef9642336c22f12e697b2d202e415cb25b47b8faa371eb1e76a7c9820c3e4c8506429a72d4dbb5ae795b932cf334dfcc6caf7cf0cc2a4c74ccd53665fa98e1d0b5821d7565f6375d9b01125d774232f31a9f3aa349433ba234a6f067e161ae10f5c5cbc3d6acf115306c4edff1872fd4e233465ca0d3b6ce003d1c53b78352f29cf3a7814ccbf07e1c01edf76d7ac0fe508d8b514d14929751088b3e4096a4b15b58fa35d6fbfe45f1ecf69993b2afdea93c04ecd8292e8c4fca72645f1e956b8cff5ce47408c146f1aed80afbbb8be67d4c5d9e5f90956e965f73dfd49dd3a8736a9c4c8b90b832d44e1eb9d3eeed6d424f9b787e5dfc4bc2383b47d24e1728657941ed793f96c440a357e2fa2eebe9fdedd247f60c8179c3060d49c89fdcb3b6d240ca2d10a5d4473d30b13e4e8d720c5f03385a0cf9174c2e458339dd9058613773dd06b8f885b76bf7c9102fae825bd85da27ec85054a2bd30784177d83916bb807ed5a03c59873f01532e30a5ad4739f44dace4a3a6a1aebc24fe0678940c54b62bf46053bb83b3264c0c2c5b4e9c79b6d06ddbfb3d87bcd91647295a912cd78b1d89800add38954b53652b9e5054796564d9a213b24df92e80cbc4ab4aed8b6d25eefa085687f13663a347a7ea8169034c6b4c953f110614408f725bb90ffa7458ba0d06a39880f001c6aba0ed41b5877fee1e8a41748bebfa3bfa88aceb7138c587aad5dd140a806436ac5c0e1ce808c5a33bf2230a9b501472f0224fa083411b44e024b9308e5882bf1b0ec7d186025691c9c9130728002bd1524331893bc141ace38c1b7d04d17e095da4fbc182068bba071e20a0996fd8f9a1bb85d45412e7a072379384fd6c0813a5f80ba4ce67c02a61e7ced18230d5ed9b373c804244d7cfc7adf53f0de84e18fefba302716e097d0dee686252353fd01421a0d4987e2e3f5cf1b886b06e26c20ac94b2404eb1cdbfa5d7df5707c7324c54f1a1a7a0d01958a50464d86452b11f171ab80f7142db587d7b6052e53f56f1f3d3aaf45eddf00f0d4614e64f20db8808d5e3e8a1b2ed266b4c25889d3b75d2c4a428a41200d603ae53d170c289c028b2d85e2ea77ec8cc33d5e61b72b3891f78aa6b8e9ba1e3982449e7d2966987622339f65ea181b81986dc257c3543a7d6aca873e44625c52676dde01924642e27b3b9316695db14dda187c8c3ba3dd7cc6b7df175333ebbcaab53c42e06319883b91e4aed098e0e26678b3767b7563b7c03daf057ca5d51ee2e9a67dff2579cf0ebaec6c6b9da524328f1cee6ada08dca8176fc81f2ebdff308ed5b7fa47cf80de727ff6598ae2ac6298bd6d0e51706a0f3c1708c355f3d66d94c3d6146d84eaa2a3e850c59a663336d42961b0b0181017ff2280ba38655785a8d0306e4ff870c6f2c5cd7a9f8a68f4acd5805bec0d7809bdfb2383945c2f43f650e8014abdcf2b3f85720cecefd60264c81b4a821914a160bba1166b4ee6b82c4ceddd02fd08037bda34923c62901353c22cfdf74273173bd138c6f837d12e4e58457813732ae6bdb2438978c56a74b55be760c590d1a8a87484f89666a72db50d8b37608c9baac43e3824f9305e6e620da604342ce4ebd98cd6d2e03269997b7189d35983e7e4b02edbe510184d543a9dce5293157d8393ceb7071a8b4ae4bb0e60b56e92ac25da2b0d676d5a274df5b94c6423d202dd8f6e333abd08925c64f8da3b920a2b7c8b1a7c54daf77223cb0797ceda0052d7dcfe00b26d63de70155f2548c44f779c1e953d28bd821ada6bd42e7b133b23bbd0cf7dce2291c41fdc68819e4858861ca6646f98fd539d9886292f6c983d4c7de96404235ad9d9ecde46f6a998f0177ba86afe347a40cdba05120d63b4adcf9e0dc7a99f33b6302b7f212fce10e212a65e8985107635bd3f38bf2af198ae225fc415d2d62abe46446fa76ff8677886738430b305bbc9ed2a6b07875d1c919812da8235d54b5d187be660809ec661e0038b476c25ee3e06c28983f5338304cc40a0e434205407181450829d666fe68cfeed17f438ef64b08cb65a68fb179918096e3677ce3c4fa768b28ea931c1ee95563f13ae56b1f2c2dab378e2382824962c4e82ea239ee08b163d6b8c46ea04977a5ff1c80552cf944c760ff979310fe482cfafa7d35eaf264b0b03d45ff8c8dcdfdceb878153d4d8a956d126903541638f925dda36420a085dd4a2e5401965762dd6da93d91572fee97510b1b6d0f99a72ec00d18fcd07e1202e0b235d9445e4401964742d56db9329bbde2e18e2b88ecbeab8dd3405b3844fe4feb84abb4db739a08237d70e5fedba3221a441cd523f9e8469ca5821258d2aaa15c675045045d9b62b30f13abceea92145add5e88223f2f5a2523e7867214c8cf59168bc9e66928414d941acb370ec4efc5f5f6d06486edd11c2cc6970eafaf334e1051466701c22268cec002f9d85c25520903fbe5a1af9078f06ff30bab46fb94338a265685da5c25521a045d4c5e02b147b2ff47df4b7bc0ae0218c883efb2c6f2c4d88167779f6c3ed3068b53fe7d69d3de52083871fd42c092bbde6b8f4a325b1f4e1fd6a8f1a977891b7e7507f6196b34faf9832bf68952e81dfb9c6e877f47260dde0767569cf1eb21f959c16042716f89fdd52905745f5daca78489875da103d5444e2083262723dacd88b816b5abda6f02c2f6a52ba6a7e8ec376ac239196e0ceafc8a614691538d65abbf7d0b5f81cf4af960420d4daed16d3bb2b3a1f28452835bc74977ad3fdaaf73593ce0cd49803a7ee0c920716b3ccdf665f2390d1d6a9d081a52e18800570c8ed10e198fbac1b31c96cb59899e9a5f6a0cee5faa1f70a1e6edc0b5ad8850c043ce2b5d3a40be8a719ec5a20e6bbd2e12949bafc9732a0702909e3862dde6d8c8a868ebc3b56721d3105af497870971804b40dc7d2a1a4dbfc32406be5dc0cd4982ca014d5da611aba279d649260e602bf1158685944409dc46e8c50a53042edc7995470844f16c459a4796272433e5ebb48820be8ef02ba7f066e43fb82cc7a42910bb927afeac9d5ff1b25a067714a88756e7262acbf4e850dd083ea1fb67de2133613791ac4c6ab9974066ee3f528ed821172927141a1a8f961da67398e1dd2cd59dee2ad6cfa49e5209cf06448c920840ad15777b35382dffa41f6add15929e2e0044c3e7fa8e0e00b4e70cb1141f16cad6dcdc4b02ba969dea89771252acb6afec939f9266d1812d774ab7adf9dbf045b34dc422a048279d8e697d9327c813407d3309c0bf77439b286f4b1b23400a5ccd767430e369900a4b8de8724122b912e751bb93da902cd061effbd5fd4e2db5025d6865acfef8a9f1a9a8d41e52c328c7c9e24d619b58938c775315f20268cc4a31b586fe1588d42a09adf1368cb5cec1043a982b6404538ba78c796ebdea65e0d4f9b661377d8422ab0eaf45e6e3880fd1e2cb2a561dc0044502c0245c34efaa5eb567b64418743b83b348b9ff6e4ff46002f850f14216c311d2bf750b1b2618ac7e794e07f9ce52567c80fb88b9b40b7bca5a40c3aca5d11d8981ec354c4afc15f581051e076f1cdb8f701cfa9513f38d7515e83fc94dac25d3f0b9fc0b0628b758cef05f9a44bae129ef47ae6bb0ce13a3e070e3816629329416c3144cb342292926b81191d4206470d43f2df6c29deda31866abe35055cbca3a09a4693de4b896a00891e3650b7456a661472f503d222fa178b80fd676c8502dbc6ccc089dbc9ec79db5158f8c627a9b7192a9c8858550c604af7d4e8072d1b96c80ec1860f5050aaa5bbb51d95a92eb58d5168a0fbbdec5de552333628366424006cd4db75bd813ad7a10883c8988a135725b8a928e04649f68d2aedd080a01a5e36808341ab7348aa5de1c6d84f8f8647011e8b1e4a5845e25a2067300f1b23a6b64da8ecbba5ca8bf68eab24d67b3f130edbd000a2e39c7dfadb8edd6fd5ad7015691eb12ea5ae97ee526fb8dfac2525c4d30cc58dee79ce897133c4c7f85002b7c1f7cdb9f4231e9b25b9cc6b7e63dbd77d91fc0a879341fcda266f49f8a2c90d95d5b316ab96fb284d85de1e0c3f9627fbf389a49ebc1bb2f5713540ce711253dd0f8e72115d6ba09907d2dbe4d5895a1df1a0e491b96249093339e8858a2c56955df82eda7672b20c85c3d76b3dc4cc693195a11cdd6b676dfa28a26a20fe2d9ef28b9c3bfc383cc055a6cd8a2e863aff8f2ecd051973fcd28a5f484065b23f98b3f0035af38fef141aec70b7ee98fd6b63f52c5fdb02e9a8af5dc172ead926deef8bcfd519df415b3b198682d4dc077249b5905775e6d6c89c64d9e90f31ca7195727c8ed370dd3b68e78eb72e18d6a3837413c0117f3757ef47072c4f653d50a20a1ab489385b3ca5769864517b59516bcd3a0a15cbc2e49ea0b8f02e5d501e3acfa79ac1de86b1539235e395ce3f1446602307087f9286e5c10c8a7bd81b3638bef098cec5de1b4c7b10d1a196d14e28ddeac47aa2ccf8e172d4f580d27b96782cf31ed10b255e301026466c4b7dd3c87bc77c905d2de4cb502669ca4b3bb4e9f4361fa07fc90f123f51ae2b68bcecc75b6a14dccf141fc12958611d501da28f727f6984086c7eb2fa9e2edd6f3508a889112d96c172454744cef7a42f60f34d1f210f08b90c117a8ffa82fb48e7632045f93c422f751904895dc4f635114022b79c32d01947452ce29b56fbc1ffd1af8cf106b38631a36d10be8536e8065ebb242699fb24d4924e6916e0b8067a7c83e3504bf736825ab0c824ffd6d5d0fd5962ade8cd66ce818db9af643c3878a1825234f7161988c421857f26b22eb60a7e56f126bdf9b34d7f1e2f2956fc39c0c6e73e816b589ec00b4b3f5b2a13960ee6aec8d67135946a0434890b434541527776149b13bc68027c7ac87e450e3ce5f9b2ecf7cf6e4957f48279da3c60da8bcab90b7e2be3f20884fff04bc48bcfbfa50ccf275531f23da082efcc799ff3376d08703dff5ab3a45303d7ba1d87e6d0edbe1b022a9e9ba1ce6643942b8ebcfed50ad733c5e4b3faae8050cbc957029a75f2c8222b46d4c7643a77a8427ad24495296a9801ef7f90fb28080063391296ec18df4324412a1f932672b10a3430ae5b258b2245478f4cf405ca4056145f8e1132f92099c0f0136856a150841ad9cdbdc0381da5642e812c9842bc502c8f2c3330e19e33b0007208a7fa56e7726e5028ab3613b913ef03ba21badb89cf3e50d4cb474eef76749676ad8fa7e53bf582eaf9b54b408bd7a0497fe80ebb8f55b2aa8bf8433ac90f1f5d893b1382555d567eff657a91853b18b7c8a0984897bbd46a0a5af70742ef92d3ae5aaf27eea05e58d5c98dd10e31254938911ecc53e0693cc2040544e2b108399454e7af29d539af0078aed7c74e3cf7332e74c008a91725a92dd60e3a7ddb23930a077439f066c445802cd93463b6f7b4e9beef5350b92cb76d25f47ab9329aebb6e5a9cdf34f5504b3fecdbf90ac894bcb11a82fcf10fbfd85cfa43e658bb7e4c7835bb20bfc3ab09a04274408ccaf1ed56e687f4ffdfbc955bd722635caae581a1f47de28a95d5b91c34284c6cbaad1bc4605240749c8f1bc0b024743e393173fc4fc311c4c9306503bd6b5affffb0dfe0ea4a573a749fa5d944d92ddcdb113b6210b27adaf8006761d2f43935ddcb4958f34b6018a42ee01e4c6bfc91ec302fe861711a3a1e55e5554ad1aad8e7e1622f3dca9a85a2800f18f3ff51cba2b1bf92bf07ad54902230b5d513a5f98fde1c418bc0716437189a052b4c068aa7191552eb097b304fac080e29426f9bec0701760d40e6b57bebcc3a31e5f345f3d766f83076e3c6e87ec4e3581010e086866f56d8210a05e780b48b494b1db3159c0133c8ec9d406188abff2e4a7b9ff7ae9af4d2ec9b828a8ef0cfe5e187564adf205540c6720274ea721093bed859bb7b7f04ecf5a2028ae7730828b14711b6c212ba96d6f7c4d6aecda5e4ff3ce769be849f226ece115fba7fca60e73f802b52a46df1149936269527c398d5ae940cdaf66bc24eacfbcf6954d2a87bb472a18e8fe337b2715518f0f64e66e27c2c10e2a9784fd1ab0129640e984cd011b9559504f834b8dd0003a018b050e44026ed2b2cc83302c7cd74fae859cf7b6e950982df05d022a8c4e8cfc9d0b8e5e7ebd61973ff4a09b625a3597328176168ce7874af8ad49711dba1bdabfe1d76a5690ae1032d1f8080c3b65dcbce7e678c034542491b4cb1084e15123b4303bbaaa60c24aaf171d5075bfe12386018f6bdb45679c3e24a14907b382877f47d15ef6ef55b4f270c9bf7e823787b70d5691ad0eb88bef5079a6391344d897a0291bedbda21596d432fb82d16cb612c59cfeb16c831f10fc53e3ce6d4e2c078dcfb374a54918eeb252cabc3da3d4d702767de163e8f84b7708c5bb2c7c6567c07d8a95f97d7f9c39ef5fe7cc498a4e19337a5a470e56855d935ab83993abfcaa8e6c3739d06f73eadac6b0b65e0d0fb9008db2d62058f2b20e6c283ceffc4beef2e980f801f84e1ea154a736ff73f75e4c4d8e64c269fe03b86aba4fe37cfca6b5e09785c386ddf055bc35f99e7c1aa75da0918e1074f1ceb684d5d291bcf7d3344c93a5c1d680980a861525f74713714d3a2cef488a6855eae983513e356c66935c0a55f197ed52ef5c9333b0216f92236c27cafd48bb164d781883e240271581aabddc36298bd1c3ec98057c622680ec7b545a43e68b0801eadf0324192af2ea89591c9c2d4c2be3450483a51a18a0380269cd84621244aa437c76c94b71502a75f460e496a13a55408fafb77abb6d27ec12db8153406d897ddff2a519bed9ccd99de38097da82f996f4d8862ad368f91adea11120a1c1a9582d048009c34bda798d443edee9a3e191851752eb17f730a0c51c0e6f370aabf11dbf05b8e66e4e095a66f9f96e77f0f657d04d8f247c97e868b027d6b62d117838ef89fa31281cfaadc319097c0f4573206c20c8d22ddfbd24666022f3cafb9381c04e609924be3f6af4197c62d377222b2542858d8f674c457086d4ff1526a1afdccf38be631cf5fdb92fda83368bd6dddc039280fa173cc934a79b57ad1cd1f0d878d54b082a05e83477127f206074017a1e4ddb36f7bf48b20a7eb223e57febb57671417b14b0f42e205a705ec9a47b57656f0e71170099c0dcc177b1f09f58c062567536627e8d356b65a0f276a7a016cb3af88e1f070854765243631ea2c0ce2f5f48b16209544625602e1dc47b1d164d90acea281e0127c497fdc1b03f29fba3f3c889b1415ddfe74e2afb0ab44b14a20c7986de7f7eb71672d7df0232b5ece43bdba349b6d6497adee9ce64e658401b1c27dcaa2f1d4dabe152d4609397d4aa91b36174e3aa63532f668b734be5f430b9e9dd8d1e8611a01f213b5e41221bb8ed6e4a4bc752cf5af3418f2ff798d5cc595782395477d0ab303b91ceb4f6d0770a8a228089bb72be6878a9883adea4b5eb3788bc6b124fc9134e19c7cf6e04bea32929cb2be7f2f95d7a1289e4ed1c82a698777fc0b00fa012142c05e1dd09044027137c99915b6319a01bf5b1131e6e83df3d00e94c4ee1e82f775743067d05cd3cc6173497eba57e6ff88b1c1d6eda7a7b77f2ac551fffec5477d3895956870c3a18952db35f9b5d05787dbdf22160564035f996829b879fea8fc9151f18e70b02eb4254be8382ee845b73b4ff618f004c5ab3f711bdab619cffae9b911a2ebcde9761d802462a0597653d0d3661267e3cf70d80d255a18ea8a5d751f7327ea162b52712a785ba51bb41288735e243f12013fbf49dc71ab10a6f3bf449f36c09e6986e08532dde1cada150bebed0edf39bd5f35436eed9a54311850bbcc346cfcb6d192c8f10599e445f3ad859c3e6031acc025c9b4211d61963de5ac558b699acdee43af6cebad9b0af031e4ab923f202fcd145856e5d4faa023c599ceee0f94953811cb7ff6e010d62a92388810379803b0b417709c968e720dfaafd05b04afbd599476fecfcb3732b1140af6f5bd182d24720f39cf9174fcf9f91552fb0a091d197f425fc51cf60b9b8c4263c526c209b46031f31c9d2f391669285f6c7ec23caf7da04ccc9e1bde038fc80cb7a4a6079c3cdd35f9ba91acd0cbbe4cbb070532633b6275ed42e8ec71eb61c9a4c0e77ffeeeb98bdde5fcdc62a35e00102dddf16696db0fedaf7731197e3473bfedbda2c10c89840d84a3b566095e36854a740db09905afcca5aa37a81eaf9f4ce259159381c7235c3583397a6621e27d8c5ce62dc8a43ba641da02d1b750f39e7484af0e1a79a4bb45a88feb4a51199726ee14fe99a2b0a899bbf66b36502a3179cd292b87b07be2aa791120e69604e831c97c33dfc62cc360a2e4fcf443508e44703d1647314bc92faf81871ac5751daa48a0fd7c5dd8125eb7cbb3224c3cdaec8ec3b976b27d4b86f00eaf006dd1e57124a0fbaab37b8332b9f0654bccd1549f7834dd4716bafb5bb7a91da039d68dcab5bfb242a982a5dcbab5da23beb0c98d5c5881ab413543953c47086e032f42c20e6ef66c686c5e0ab1ba7706c085a0860b62c70b7c0ea41fc80c9b70ad2b992da10fa112bb094296b5c44945ed0c187d8b058d9d1e7c41583acd62801c607cbecdd1b0d2bbd978dfb3dd5a608d07ec41728bc8ed571e9897bcd97166719976c6cd6bcb3b8d609caee9073203970a9b218575f8b41da548241abe1a324fa05db08779a1b9b42158642673f54a05fd31e5c00ac5734933ee730e866f95f6c7146871a69a9e92b04211f706f3a8c57cf25335ca5bb8336a885af3c0b3e76b91184b890febfdcc584217e5fe432dd0b4ad090729374df0e4ee3faa508c5842e76c0759c54848020144c72a5d55439b2bf3ecb01c14b4e670f072bacb5c70c690fb374f625e1e2e02eaf7333bb1fcd30cbc42417098dfaabfef599526e5cb40ef22c998b52b158182b99a9d0183b080fa63c5177f7d62c2ad6b2591bf5d84577db205dd1a8202fe2e9d44dd70ba4d05c8ee2b81ccae3aea04cc527d87ad03707412ad2a18cca742c0577c328e4171ce32c581df50c76c7da1b094841b9325f17ab552cb3c0b67c19c7612ea428d76acb3d08e66931433f585ed36f1e232945f50e02808a44bfb1406ff772774d465135c2adf28cc245fc9c48ddba57f573cf1f3231e506b0f6e91fb4c3a58c62f8f930e3835aab242091cda1cdcde64b0a79d25b8dde1ea4483f8c0d3ea517c438220d918d4d9f809a3f70efedb02ffdc741c4e80ed03424bd5ba30b8e68014dfa682da16e70b130f0d223020f0c0e7145785b8427c9e897429d8d9f816896f6c087416dfa1a9a1cbee520437d582dc0c27b452dd5b936f0d96bdcceff821c947719e68731bd25d8dab5de6c7ce961a0a4fb35c53a6a4fb2597ff32bd8420a433895f4638163f0e5a53131248ee3d29b2fc0963572710d947f927c17f1e4c47a9331d646ae86eb66d325eda50bee13df2e315b677e1ed2f3fe642e7924c20f4517832339fe0b02bf13b4cf72a2bce346bdcf561584dfcd4b576fd256c605ae2dc254f78c7af183f8a5783ee63c769c6a93da401fd831ada4479abd0b6de8d55cc0a355eaa7fb363ca32cf6f5d3080cb4c77519a4321205bddb2f1144ea7acd07aa7aa8a52a13796ecd49f9726ea48f2baa6da420160bd48a0701a3193b93d048cff93c4cedd134f6ed29098d2de3b9fe7e4335ed34c03ac3b798851c14ec2a27c0ead9dde7fb37bb2d88a17c287998351f23ede626db5c9fec4e4068d5970be07dbaa2ef71b54a39ff16cfd040d7cc178b6f111d051c03fe2f96f354d3096ed5000cef038da09a5caf03b50411cd6b3e5780f2cf28f23ce0c5680fda19b18a71593a72ed81c2c0a84a0ea3feec2afa20e1b65265ad9a3dafb9dd15a6b3c3b8b669fc8b2e99d55a6a90674060f02613eeb2c0a96f51bee40526a90cef2c70f0194a8a524923e702d1ac5a595f1c7f2585f1cb40faefdebe3759add38f36889c93a2bacb1c0f4bbc0338e73427d1269533e0716a6f6bb13387f395ae85561ce7a8292bcb36f8001a65c4e5f7398c901e7b7256db787af8aec80caa5b24211ff9ace03d07e92a3e83f07b1b6755faf26626c4b64cc63215a81906a948f33d5646d655ea8a62caad80f5ccdb0c505aa81026dc00bb0c0817c9208b7cfb50b89cc7147a2fe8bb71ef3785499632dfb083cb631f49c6ba0351c79b90fe0dd7978e425adc47e60590f2607093f1181684887d37d1630f3574c9c8cacd6d9a2da36998db0f2f4ad63cb175a607e9e0f73f085468a3aeb18817b64d3c702fcb80a063f1ea6a8c4c3d408504b5e7a1a215b8d66edcf4bd3bac290661a8ab3c074d0954df10e81dc00c272ffcfd75005543670dba305286c27c4beaa6c7ba94276a73b140b4862997fe136d9452c3f16a5932d9e0f50398f9f8e51e5dcb6d7d82ca2640f7570419ebb3d1fb5a1e4a191905e110b9be7ec148b0a115cc0b85c5ea5f94e801a8e189caa022bb3234c096e106c5a940846134110822c645ee99615c9893140769f4e58a6d648e3144437167e83786c9c4fb11d00f8c18888c7161dae411985688180d4416c9e74106f9433f0c5eae93b359df1e02723016849728e0ae0ba6652e0f85e15c0b21c619ece3023e3ed27ca9c1e95294280a4cc6467dc6c9e376ec9738042ca29706d35fbe821730f9440ee6b593d812eb963c7b6e261a9915a64904153bf708f63b36c466b2c3a2cc08918078fa40f47a45ee3cc377708809280917071ca6f31db41e0475f03e4674c6ccc0dfdcccf22159ac9e9f259ed924e897a92f774027b59a6c1872ba605b146ffc602850b605ddabac97800451b22059fae3549e8d10929ec8eefc87572f97d2265f342c7e77c96cb4d541da4e9c375859b2e5dd4d5a6f4b5fd9ca9596cc84f38d1eda0d4d8eb5b8fe1525d35cce75bb38384b09045af27592dd52e840585b563dfeac96420402f03048da46107c49bd2d6fe6d30deba74802143b92b05444945e36ccd48a6a612b1528efa3dfab05c5a2ce95b4801b6379e36f6f4e6bba406b2b8a775d780da3065d04b2cb1f7a041d7e104c973c1ef14cae8bd5bd79cf114fab77331a98ef61f01cf12314b81020ecfbca45af2949cf275d26359f752b85240895f61b623f8c4455e97fa04a8e7ab07ad17753bcb28b89f11572c7799698a3a9b2fb27c022fe501007385ec1c02d1360dce9c86682553887ddfd1422403fb4e36415054ffff22d5b99359e72550e82fbc6fab6a7ecfde6561f5910357e72fd31ab5e427cdb1fbd158d2bdb5873803bcaa1f252df5e6f153fc639730c7a79e23e4d685bf0d48483427c18a2554a4e773c6aa32663711ec1ac057050388c3adb446794b6ab989245ac0bd532f9839c41d4ccde94d4a631492596bd2c5b05a00161c7c0f1864bb7f68d9a06e705019cd7975374bbd1c82a29976f322f68fc5b90a210682edfe59f77ba53d6d0d8e6a464e3097966cf480dbc6dbc74d72665a8e3b6d4e3f49a6d334634a30e1041bc6fbe76437b03201e696075e54afd505d3e08107329e3cc55361d1e904aee2c4110dca31f87d026df9937c559bb2246b5e7b2e4b945e1cac4e4d19a05821d4924f7315c4a8abe685c98d633078b97c7f6d2157554620b9720c16c8ddd7f6aba042193e514209ac2a2ce933ed91f17588e29890165005dbed91ccc001451ae29118d08551fbccb883a6a388581f4b381e238c097aee4b8eac3fff80de561ff995b4c2bda127726376cdfd04778cb6840c9ca004558559039eb44cc2ed4689ae8354309591ca8f3dd6af59998458ae9c6f8b1a4447533801b9f55de366e6d4a3051406200fe9d7bc3236784404b5f387dd100e4ca653e848302c64e8e87cce6fdd6ee2d85e515f415672d1504cb86f5d3b7fbbeddcce4099f98497dfd446024f3891841d3fa1e656933847a545e48b153c2029c7c11d3f9fe49f4bf0972c176bfa29629de9562e385ae2ac0b61b561df3f82d15ffec4a9c310c01e333bb606d013ca3dac8b58af69240bf882b56d9fa33e7f965ba4c0e7b04af87087012d1e39bcf9ab84ed71deaf0aeb2ed270a2a36805c768e81f06863816105701f41f1aa3af61f93b46c26e82debd8d35fab3c58dbe705f7feb571f613a63bdd8e0e3cd399207f74c3761f3aedc8f000aafa557530f427f16413f510212a7647621e071ebcb09920caffa8c05208f0cf76bc711caf0fe0de8896a29a6632c998061776da4f65636cf65b51742020d043bc15240a0e359d588b0186d47813f04ffb5951a62010b662f9ecf72fc3b5dc8341930bd4fdd573e2f01e3248bef1783891d3509a6e8aca660c7699f2fba430988b09ff76506a18854d4593b152b7ad3bda530339ebf9cd5915c82c8fa662279701993762f639149013b04fea4ab06b50e894dc47b7a1aeb867e69251b1b81d56a4890aadfbcc84aed1d02f12b4c0bc0cfa14180b974e88eb4e64530bdee451ebebf5c3f2dc93b5e888d6469b64361106a01c32cf93c0aaf68d10e6a01c8da065cf2c56002e19b60be86b100f45ea3cf43f6e43f1c21db26d0859807675b483567c0fa037bb3d42f052e730a0b0c990b47e34a6f53d92e4f32903ffd4b126da3c315f15e9542f1a4d2851f6e1a7b99d37499f0d186685b51abc4cfdcb9be8d15b4a102c7f423d1e696a6e0e49b85e3349b2166d1451aa9a590288b0a003bb29c9409580fb0363a34ccf15e1e254c9240392a6c4e7d820be8e2420ed7dbed25287dc131a21191e339b1cdac307a0d41017331a6f2b4a9943682a0da0990c730ea377cb8fdb5ae5c905b6da29eb64860808b5ba6977a40573990377577bf1d8cefa50327c9b1f3d58a6c51ad4eb893cc9c9870a82808e37ed90fde433821447b1e9b9492c01f2c308b76df9401cf3c217d4ddef7faa9148af9fec08a0326e67e44cdc4c195ed4045b1b40a5aa2170065af523bc0cd8408495255b9fde213e64350604d39590123034686ac22ad6723680fe245f5db1a00d7558f3ec729c286be212a97a6cc96da27e435168a6e9d7bc950071735f46f1f59075c812fbe0fd9324cac2c1328fdac61fcb7b3dae51162df2edc32c59a4c9f40c3ec11048d64addedb1bcedcbd1e2843091c43a049d51dc8cbbc64be0148daddb7b6d154bca4898cd401a45822c842d7c7c76c81923196b2302fe53f8b54a4103c2d342bdb5470edf66a7e8198724abb9ba27d078d703b3e6d22c55e75ecc884d6d4edcd070ab180b0817f0860202fe88438516a774415213c4736e48f5dbcb328e4f3a574bd15ae9497c67405e52354d0be18be9b61d20f52aa462a2e50d3d3f7e018bf20183c70b8601bb434c456c397d2543f512b879acf655adf6f19833f2a4961f310385af9078fc9edcc9ff999f483a8a39007b2e21aa2355bd4c65902e500fadeac94bb5fa350ca41d700e3350895281619765ba7bde20410cafc62a37d10bea8efc32da07f08dfe78e7cd8099e3425b48a1366cec7fef1271a7af4bc211b3861313043b284c24bac96d5ca864d2fadf7d2578811291971376801dd31d4a1a5f9b11a315bf45a2aee7d1e3354f6d9134b9fd13b17ebdf0dcf559474d1bfc6c1fd4e0bcc03a42369d7842767071b596c77e2a0b2eed9d6ff82f0412729164e76da3369ef79adb8e397d39952070e742b219a37e072b13f117c00ec8bdae242585044d548a4baa84369601ef1a21a96207056911753f33160ebe2e6aa41409d7a55aaf04b69681bc06e12e11d35dfc6533e4eba3745b52d1c66ebceba74d7d417069dfffc4e14058000718b42b291fea420829571a2c831c26625e36aeaf6dc071a2643e7ce87b566fc30c1735b80a4e764e0d780688c9de6ca8e76fb4010fc67c1deddc8d80d3be1a18a063a93636a87f9e8b1d651ab06df8b98bd4549d38e099d1735b2839e5834d25f12a55a49dcfb83bca4a05b14fbda35782ae42af1c346125abb6c38c06c4551d0c5d9e38d539242892de0e450aa5edfe3662f61fd459cfd2e52883420fd0cfea8de54f2441ad94271269ce10ec7c2d5e0f3674f4ef8faf97d7412032b96329423fd79a21066d95fc47e78b584d7de89ad97590f838609330a4d6c6403747e930a7ec7053c6a7831268eb31ba4d01ab3cd5583d46c91457032577d7b64583fe9d555506c45ae2fa6332f58e386353a7922d1c28602f4c7b1a8b445d860187608cd635d95cb068adf8ac30e4b2681eb3017d2425bd7f3ed680b54508e4a79351362b7c83d1fa148f4f8aedd8da82079c03125bdc5d390183cce7f52f9260936c646450ef0f444d97e3f5c38d353852a8161d86571f6f51e42054afbe759f6c2605a1685906224b89e7d5484afeb247a2ec790665a0ae381eb5c8d8031818918702f14d924a01a9a21ab81b649482e1d17c0beb447084856018daeb60bd293c4ab17b2b489d6d970391d75b7e96c1d3d4d37cf1380c80c3032e34271a35bd83a237c8e9d64eb94944b8fe2579b8862a1a5069f6d1da3d2f7b0aa21408f53e440708dd6d71d6fea69af8636910a1ba0d290f506eb33bf281dd03ca3ee9d7d7613d2418499310b2240e7d11826e6d9a8ebdaa43518a38e375aa162b48036d1525c3ea63441f4d489191b4f503f41e08e970b8bde000ed10e44c2a749b1cdffa91942113f5a1f069ae2fe53f2006bc8ce17afd47ff6fe5fe1f1fa4af5f012cb1e858d71b465751afe7123fa02485036cc8e9522165a402de274b8d6a2294dab816f5da981b421484be77c07c8c02282c9ad29542d41b329fa41910f5aa0a9a09dbe037acc2c316910970b3438f2710758de2e0436e0f8cb1af0fef2d1d38da6598a390cdd062f5e30e9c64cc2f3bcb013603e230b5425c323790a3c9e1311a9bc67be2015a235524c86cae76111ef33e7f8e74b5efd0c03747ba6532dfcd1ad16f691972c19fb93e7112c3edbc7e975e60d4e4ae82e0393f711d0e3f73c82c20e44da291c81ae64c3a4700c2edc0ae7850bb41b4de670c9f9e7f798620f6a87c1bd4e506cad93c87c4c673755a99f44d6419daccb33baa9ecd3a5072213d4b13aa5abce5ba59b4c71b63f2ae5333707f8c755900daad2e6654c69a4c676a2ba0310e63e745dd4c7a8e4e39fc9ab31accd2ed1418da3dce711ac60f4ce5d0b0dbd71002a325f9422637711c7c424448d7e8e56a2986feb11ae03b914bae975e6e1866840282e330169351032d0815fb982294bc84c5df2fa065a067aa012f341be15e29d9658eaacfac2d698073bb62978a6947dd016a0bf250626fa578d9fcb4d605fbbf9aa1c029a3c174cae34b7978e786a73b2a14eb8206117c4c2a1bbb64c505e323f3ba39401122f3aa4400388fc1add056f07266c6c4c04c4241392951841252f06979c781f8937f2eff0f2060e4d4cd833b5e4680deb032082f91e20dccf22205949fb20cc7163f0efc91702f39004c0fc0c6180e7220f034a9fce6c0f3ac5ab1bd27c24c43c387a3d8229cac53ee723c07304c00988cdaa5687e2f11b2dbee7ee9bcc6e5e67705edc6d6abdd84c85dd3d96c48153a360c99ea49f4f065edc4f5b3e9ddd5ff994b58abf07cd9e4d93d1b9d96344ea3b37949f4825226545c3f36ccebb62acdc9b8fc3c68750173b33b3f11aef81fbb6942ff1e24c1d22976bf5792fbe8a14d07d9ad8d8e7484aa66c3030b5db02d82ae043d1201dabae4cd4546c1d1899473990129e409b44ab31cfbd8317810def864d12a96f9aac5ccf3dd91d76d2de557cb3200388e3a687bf8eb1885324732b914c786cc1ab0cb61e4ef3bc22c1eec1009eaca1b325599458e2c26a29d77328d046e0014e6a3632c949948af3e2d25a59543071081fce973684c37ca302f960905876ec7140d2c2150fd41681be7629340ecd320400511e9158a89543966ff9f0e54877d63c72051983d4aa015a3cc3195830585bf759f0a5596600cc437682c1fe378df1a830311da8b6a66a7f8091452ef4ae5fa054bc35b038e1e2dc8397e7bcde6e733bbf387f9ebf22e4bee8cd4e19f49937be1303dfae259c8745a09b8f7951cca167dae375a01742b0dad87b1882cf9d3eda0de5daaed7c3c81df7a44e2dbda14ca1c8791c60314f4eea6f1d7b51df6234d60a6208186d586d5788832e29b7a6f247f6b47d87bec4f45bc2102f9018e08cef493036174b62f07d82974585a54771f973f0faf8ce55ba4d5072787e1c5a2692ffcfaa1bdf1d3c75f816c07150fcf8561660262766811f95da9dd3e07c69baf18bf49871b5f8b720a968644fffa30a9173fc01596b99da2834c79ccd1248ba5c551164344f454c8654acc45ceb70bef05e425eb210055d89555d2d7128d83bdd30e96aabc04474fe4cbfc0dacabe2d81e8ea3b023ad268872bda7b3db8494c2b9428143e86e3521a0019258b3b09559ee1c6dbe645a0ff786707be61b933026d461cdcc1acf162ffe2adc116ad6106d2a8aeda500a54ba3ce9459d15c8ad76ca4cf12b3305833760914f13e323e7495eee2405c908c658f03c0eb8091d61238202fb8e3125bd69321001ee14811d1af65ecc3d32e569dc02892f9ec7bb04531a6801846b60631b050a5ba29db9cbbef1991a2cf367baf979557780ff60df1f1bba2d3aae49f09c71212d5f0711f4858161d003d0ebe34581dc8125326c5796971bcacaa82801e3ec1cd88497dbb2241d02fc6c7d664f7dd3f26f8dd27a67b98d5dbd9eb00d69bc7a921be8ba311534bc39195a12b0ccfa9934616c1ec143af875bb186c5c0b8ffdb03c29ec18d356f73dd4d236cf61eb826b627a3c6074e1669f9349776e3ce7365aa4ab62f1e9a61e5326a17fa707c5daa13092624bfd4b8e4c6e77f5ff2cd8f4ed88c3eeef04b8358e4c0bbe895ac7ec8f037293445a5b13a9d65c47b35e0e92aba3c391aec96edd63efffb5e1412aafa5d78de7f925a994e4335b9bae0a62498b9349cfd9f26a9519f13eaa2dfe698e3265a8283754e516f75e05bcec45dcc32d32ca62fe6f8b5bef4c8d03088f1625bde0289294ed8ce1b1846e4ee5f1c1a8f0b82b69e4fc837e94a807bed68f6d66d9c85aabcd10847c02c373195bdea9000e21ee9c938bd74c28144fda619a96b06a45bca0773bc4b43b43731cf7ff72bf5ef7b8060df70dfebb469494b7e0b5cc999f00adc23bce913098636055ebca232fa85e7553894bad84e891218f06f60c2b3507cbc9a5dea28e28110bd8466ecbd17e40fe1e08117733ea35b59e86ff592116eb16d8aa2a38f48f994b187027d1c52a90708d3a8c49777e49258ccbb07f0b656d80feec8ac8eb9e6383a2dbb7412ae928e19980c3e1a4313682a722c28cb87ba5e0160136cfe8ba082e1e621379b5f14a0857349950325e0f530caecb314595eacdf1ea4f55a91eb0cd95d76015f8c1396fd0abaf2b5e2f37b86d5589f3b2df604d61bc5507f329fa89377a497141df362669afba960e370ddaf5ba686b57ff6ca490c9fa281a0021419fe17fea5e23814687adfe32b305176f77d2e7b0f852e7d2ed87d36f41e7f6f31c011a0a490de60740759067ffe04c0548b1d7554a5922e3e629802f27b933a233fe481d889edf3441fb6dfac3ec53eed64f5556f647f0e7693a31b0d978ba0d5a8ad41f1d0a3e2f0ae8f8eafbcdf8fa7e80a2e567b3fe13d05c1ca4e94ee6f1b6314e4d972611f2588215425e1831440f8567e2103da97329058014621639964e258cf57893889ef11402e89555a39f7f1affc0c79282f25134012d2e1ba6bb009749fc6b8b703a5331c623fdf4284bc48ca92d603f9ff2e70e72a8e83fe893a4c38c3b1608006f1443faa82b75ec27511471a581841fb49807e3dcf43ef815ca57ad9a606e5875e9aea59c8f69999501ab956edc7f9e2c20bf0bd142ff7c091f6f8a7bd2875ce26b431b61cfd0c2357655e8b405f6e0a4c2cc94feb1c44c4378d55ce53ac09fff5c8c0f3601424809b196cb1e64868ef68c04594892c93e1120893de22b62f88dd833941928d2de61c0c9b82753fea1afea14ce373bb14212c7d258dd29d59f80edefd8481e44c8873ab8b1f5ca29a395ca3c1c2b1e8da8c536dc1c52d23ffbcdcacd23b08fb75281630ce926fcfc91dbc12910d0c0d48585bc951b8e81cb6ceb31fd51b3978d8211916eb0f78c17df5f35edcf5aa759a72df326f1e0b205d40ba58cf0abfa3608810bb464d23c0c0bdc93ea97d5d41358106618fb736594fa27210d63da45396b1cf887722be7a94718e2ac0071551351548ba25427f31916e6d1bb63b3f72163887f11e1c65bb951553da5b52fc6add6e62902e3a519e86898230d74749a50d7ee441a334f888feec49354014956e90488d93d83323d977066881094bc89702727c87c7fb451ca7fa3d5df0e7571c6185ef0a9ff09684062847ba1179491871dec80971f4763f51297f0b462942827a5deb80fe06e9d74c50771b9855a4ac73973f6da51d84e3f295fe283ce4f4de7c4de782b3f0ba0203d2fa8a75b5937b3415449c99bc0ab44e381c2c4edccc04089b1f7280707549ff4144a00dff86df0321e5421cc30c575f02359398efc9234959d812143df5856ebbf93f34e20fe0eba1963a592948d9caa7b491fd9c81040649cc068d880c11b00711f105b5b9c50d7f2eea5fa2b42a16dd94e810437408ced686940ee5b23450c19ed46db11cbdc5dda3776300d729913224946cee72949b8b76ee59b8512e9c9ab505c532c949268d194c9d2a9888a4f9a92484fccad7123fea76087b1a58a781bb0b469385d5a6a611d2fe8b03e0316c492748a225c5dbf24b6760113da3d6d6b18bd3ff2b85dcb23c5c88b495bdf0c117699535b06fe9506f432628d7b2e7b08fd4522d35577c9eadc639b56766843645f2a66c05942b930c75720e10c0b925bb2f7904a387a33a8b274ce7c58a2e656bb02b12320efad4fdfd16491654817f3b7c8ded29b2aff985896431657a8e9c7ce832422501b60bcb98baae363d7017c1ba8184e5d48ef2db5aea99c10c052f7d23ff7d742b52d41ec74891944d0dd34b6d61a14f61fb2323827ec6045957f1eaef86713b06b6b44016bbf6da03ef71cb08e5fe96b01655b7e168e721396b26b369707f8f5700c619245fa60167d42e4f830310edf67c859b8712325a0a08500200b9abaf5f3105bb1e496c00e13b50b44f0716631af6d70bd6c865350a4bb9209ae2a685bad195906f6f2a7558ad06144f38a90d8a9de976df74378efb5c349810ad18cb9c7c5df3a2c162c68881e3201016588d68a6aaad5a7eba87a7a35f8845b67bebc6444b282f11f64c70c8e24da6284340c4cda52645b05628781a8df575b06795bcf23222f1bc2962a149f4b0092409133bc1cb365fa1a911ef819f547798be6997c7ca1a9990b27b30203f5e6b91c0b38be2e5e7f0e795a7eefd0cb67761b52a2220cbda019129877f5602f1fb5c6181f7fd39fd5885dec90716277ad3d8c84b8f92367d63251a7b8899faccc7e4f207fc54b5da60d55aac197a8806c833376491f7ceedcd2a4af6de4a639691c0968388dd78af42e43eeb3c52f2cca8ca7989b05d1d1107ac68c2fec6f39c1d2e188ad3877226668900b25e771781d89a79d7cbeeb2c399ea74b37a3cb60a9d89d939822ebac810796729d8cca51235c4873288f2e246f28f41f3cf33d027d403964969f1aa55acbb77fb1351a4739d90887e798af1882580f39b8e72a09f7b1ffa59d3a1dafa10972c0c9cbed3a45ea1b4feb4f5a8e5e4c25eeb24f49ada306af971430ac84026486893da58305c449d34e24cc0d1da7242a6fe0f4be8d912059af724577ceddc99d149b8762776a284f2ed5d608830829fd0aa9f76e10e6a8da6bb987fe353045cbcaac4db1c6c812637ba699dd1e348f2b6d9796b109c7b9e2b9370573daac45b49ced941849a0c5ae9d00ae75ff4c3cc811728b0d95b4537e6beabec228ccfad92389d48ac1a0a964f0d3ff678c179ef58bbcb1fc275d365d7bccdd212c80e542195fddf219ecaf6a3eaa9ec1b523d6fc2cdd7796a265198042c7324896529f4cdb8c2008b68953cac1e220c913241a8258676b63ebc48b8f26b8a3ea4fb3fa899e828fb8bda032f081d83d2a4b028ff09b99827b7f548d65e82135bf8c2c965286c704377ba058614bb814a56868c0e18430958a516ec25d159f57d8bec85447b1faaa968aaab1a364ffed6fe6db48bd55183c9fddcef83ec9e46c40ae32b5404362eab455f6e7e17c2dfc714c4ed098bc9eb1ad8ee2d43016d86002f4d830f98ac1320346b4019d34fa46b96a5dfc28239bb86832c32dbee252088b61e47402ab39df2ddf45c3d00c339852810a967faffe9c4e2f34703fb24e8673c2c3ab80b258eaf983b4f7f440e39e921b1db012775a7c128e7a0a849affd9db7877c0278a6cd333907ae4cd449d192d2ffaa6a40c5584ebf939e41dc0c895259c543f04c1d9d4d7f2396c05136cdab03080ccd15b4a3d9d5fd6d0c0f0101d7d5deb6f7966f56cefe15fc722003a90b9121b54ef9163dd1a13c6dff0553f4f72cd2adc547391344ad358530b48160e540ba7fd713bea404d7139b7086040566ef5e341ccf299e9611600e1575e151b2acc5631f8812d9419ed57ff9443b30380ce26f30dc6b0019616766cd21fd6b97812086111c1711f850c0b149d9ebff607864f7f9696566e5bb4a74a5a41fca9355d7f1c14f4d423e58dd10747696562ac2457b8b400f6cf0f7d68519e7250e3ad523fa42d445e5b2377ceae92766b65015846c569d94dd1f546c8c35715d76dd4b06c3af71b129c1793c925c3e841e988b9f455afd535d001e208e92f2485b5edd4097308ec60f2c90d69ff563f94f9a7c700c69f6623aa28e8aa4e5c9d814d506407d88040a08097d8657da94442fc00896044b6750395dac08fcc836fd7805187030e0737dd63b7bb009a98927654186cfbfe0327a43c983070f0bde0fb0ed993fa5f20667e77407239d4e6717da75cceb12a169bb228024d6cba1d2b6c7f07f70264e936ec8b286e543f0d02b65c7253db755346881db600908c1cdb74b5307379eca4c4b659a02916f16ccacf6960cf8ad8aab9ccd08869d9ccadc9a7996c79abe2f69b70dcf3ad3f077b95aab0b5d5e69e235160671474f5c05126ca5ba6246071bfaaecc0cbcc48f6b52e493f63a982b67c882adef506541b9b9f1a0c3f7e5d6671139ad265b88d9dae569305e55e1edb31bfb9b2050e02ca9a6d9935f68b1ab0d5781a0acc1256d2653164ccd74db5318ceaa0a13b5d1c43f11534e4e6e59044755089d1a8d62774c1a3b70ca66ee5c38c6ac2168925caff92a6d6ae7bbdf73e91194880e71013273433303c05ba724167155c21587f2567e3e6754e93d2b0830afaf78fc755f8da2b07677ed32a8ef6fae5bde53e93fecc61ab4e6e89cc2619a7b0dd9db7eaf8dbc8f2dce0cc90aad3878293834f1645e0004d2e0c59c1e3272fa0d14b5cc8eceacad07c8af460d5d973a286114fea5caf7a1e28a69005d3d06d843e19d4d2733ceb4e58cdf1dec8d6ea133627cdaa417ff03251be926763a296c6f6b25a11e49f7a2be3f4e5b011e4fcc35ec274bccb37d325ff4245c6e6d6ec107a6d4ca391fd484030844c2570e3e3a41d832181eafcfb6079537f419fd9d7ba21b8e74e042c8c5d77098c387b7b9c7d47595dbd68bb42911652dcbc2266f6ae69d245eae7998082a0238888dbff2350871bcaff2a48ee5aa1f39ebe0f7bc3c81150e507ee329084928fc74d14cbb8f7f1a0f73c366e050010af682de815283cc246b191052ea49ba656f03e4df37a5fe420653048b2408fc5a343eb524c65095a6c9c689aec20c8ab95de4baeafd780261d67a748efeb32eb2e4053a7360009c9190e388e1e59f4118a4ec610ca02840fcae9a9200a3e2cc1588c67a4b582aa8e015bfb522ca216d4197170519264bd6d25946ff8b792db5a1725dbbe24e9525dd0c0c73996939292e7be2b74074a89fd37a78677af59167f612c5de4f4fc86a9c384ddcd49ddbb0aaef7454765070661ba3d1d93741423efcc6bdb1a3ed2b89454838bb5a9a3bb7359e866046f34319d3cdbee2d33665e0f2bd9cff09bfae98ea5facf8fab02097207a7e4a919318d2f56ad11931907366a2e565b936a15c2ae5c99c21f1b15269d831052d9ce3feaa47780c63ccd5e273ee8389fce31b0065fa9dd0e0b328e5b55acf308a04797df7a29f302e19033b44143164848283ca35244f2380ce67ad25ee9f0533a1022067311f7a17c76f8f56b7f2234fa63c5e658833e09ba95210ba6a851311c03e4b12ff91df1cceec66cc625798f8a90451ba5f0debaa4e6dfab16ff4b8bfbc978dad0af896e1067e40c8361d7ee57306572bcccf3bf4ad85349bb20129d3579ce6a6bfa878e0576fd7285c94e06278d200eeb985447ba259216417ecf8b811da2dcd162cb4b3cf5dac7ee0da829e7cafd3bff2435cc697a3045c40c6b9586895a38fdc52cdd4fc842120ab8698e3d3f2371cfb9dbf374db8abf236b1c6b8eb4ae5bb91f1eda41e5b23f8e0833011343dec3ea345ba066ae2e9b9cc5226994b4d188c83dc15e02aeb05de90dad035b13ed7b0a58b7924974d245043a1a3f4679613583c08dd6983f4280887a71d0c7d75f79f0e24504a376b8abed0ee46ead8d085479129b238c3201a02c991bd0452ef2b04571cdd5de96796d3d4757a93aad4a080cc81274cbaa7a6ee8e11794727fc1afda4049516ff7db5de64a2b14580f3e1eeec4b8006c031681a4c4e78e49c860024b84ef8c1542ec62b88f3ead38e80da1812e2c846011a94d7641ea95313f750477fab771906b85e2eacc4ad5fe393254ebf3f3acec8c283bd3c64b929db34157e3bb82ab83ae4df0586a9e0e541426584db7b85a1df32948711bf40abb490baba937478514ab0d3a8575bac555574ff391134891dcc493de679bff7e285773292b95be5f1b8c6d4b92c66974362f895e506a329499f4e0041335041ea590a6765f4d9a29c1a1770cbacce0a62ae3a3e2f4615ad45e2a73ff69e7ab46402fd665f950dff9615d78488963d634762013b8101993760121b89e88642c7950b78aa9abeafcc92762947d5297b9e4f5342921bf801f9897bb4736c03976ff36ca2f9146b47a39557435bd8774f568d202736a397e91f19cb96b339ea65e1400ad8d73a79efdb9e59a3b94137c6b26a9e996a24f387f36df8feebfa04a8c50d5852944c2cad6fd4f9be6f10f0c1adee03ea89c2e059bf2c546c98d114b15ac9d32b0d9c052c5b2f8af935199e407f27daa723fed13137cbd0113bf6ad76bbc8eaf52c36259000b9c811f5e87c0ff924a837e00375301009c2d26a44ae70b7f8bc03f30abfe23d190e777e2c822f26aead3f838ce36d576291964ed648af2c59cc1a12b64a73c7205cac69290f4992ed6af159338bec9a13a0a86eec5ea99298105bca465a16447563ada474a03825861aed434c3bb3f770837e205663a6b2bfb3d34131b36a9ec9df2511efcbc412d892b11a43100b780951fbed464a39d1fee1a55c52deab8c2a4f1f7c4f6949381552568e84911931aa8b28f351fd2cbd7deacbbaa99018f82af15ae3d384ba03776c88b41d45189e58085a867f19ffec1e733d4f3074010db9931c86a0e9667c2014aaf1965fae75667e4776eff551cc9899a9560221ff3a7ced61b03d71b26d211dde0614bee19f69b85217faf3870c5d7d11af1c7c09b23d14dbad9250ad12d7300e97c50e70d357b5ea0c82ba87e18fd9ab69da17d878b61b644e870b35ae8452f628013cc197c5542510e278b1a532cb56cb48c13e94644e036dfe138b38ac414158a91c8ad88ab3365a743be8922f229e74076690acd58598a5e254cb2665755bd503e4132d60743bece57eef2fb4df9bf23c080aa0cb9a8b25a14b4213295a6db0b1945814c76ad356d84067a8f263f54341d898292e9046deaee4d3adc5da4eabe092955be6ac206a89eff4b06d635dfc1a78fb175eb7323f86f43d63f272fec48df3b8353176064cd44004bd2fa37cdc78be2f4f47160c7109cf15ca9ec621da51c93cf144d48492ceeed98b9ac9404d0140839f186eab17020fd94ea6d4cb975b13c95f56d0680ce35e415d2422503523a332087989d3e38cd710e3dca1552a1f825b884ca2076c07b47c1e4277f7251de5f5e77d92ad97182a7bd8acb0436bf9bd081dc4eb28b361575e39377df9b0974cc3e298a6c8fbd7bbf8ab0e72689a31ca6d7d82905c35c1ceb6a9f73a11410ca4473c84fcf084a74b5e29d056d1a679aa3996a292ec08afc6ddef7f9df328d33c5162cec36281c467e60b247a1b478cfad789c07ce6fa0661c67051b349aa83e0242c1ae81682271b00ce8c0d7027cac6aaf7161a8521cf8529db33beb6e0adb916d5939b842b523d04de00914b6b8eedcf9949f4c1fba87054bb964ad04cb685e7c481cdb2f7061b7877854c56e0e057f622294fdc89e3412f7d6b1023ba802a9ce7dca9a30e11a098da8a5ada873dbf7256064a42fbb7d15a7eb5fe88f15ab0a38400c7a1cbb6284f04996f21392a554b0198d0a504184b718b1c5b840804a0e842e02ba64094a5e408cc25107b9321708807f14c49900df8cd5193ba838fa5863869ae0694daef1e535c04aab4088243a65d176f4039b91cbed57e79d11fbf56eceb0bd82c811e4eaf5a46bfa11e66a40dd33b49f9dcea0369d735ca6f72e21522629a59432bf044a04a3043fbe5f307b3daee5baaee3eab594fcc49cf373af661a42eef75e7b35ddc17cf28edad89447e5a27beec2f300c460899d3b6934e23af759776c09358c4035d11d0d9839951739e023c1e3482b1b2c7e76d76790900e345436a9cf20a11b4e2034d620f4424d6b7b7d0609b142a19597561779aae4aec573ceafb8de3a81b0b5fad09cc9fb44d57b4bf75e5bf18b2d68b5585f8cf125d99d5b77b4c604104037cd2d130c4f8f35f5ccabc1efe1244976dd87eea00498a2a9cf9e6a6553ce4992c4d3c39387f330c7ebd8563af90ec337dffa869c0826f64d68040a938b1125e9081c486490181a41488c353c9aeedde85e9099ee795ef9a47ba516bcab4186c2caa7e3683a77a373fc725cd0099d0bf21274c40ea451180e828c00e2fd31bed10b80711163c74548d131172d4450c1f34bca9c606f746db7ccba25c2b505a85b6b8106c8987971346cf9a9224b4247bfbcbe28e276435c4142d642bf9cd66083d69aa4e50c0ba50a2cf0e8e1b544496c86243c62b4272c54718d80355661f1a0220b12028b0a3caad43e2f415d5a1e0ff4cbf6ffb55ff41944e506a3852d4305184bb68c0e98d93a4a4d0c3b78a0ca95cd7f22f9a02a5bfaffdf649955d3a4d56c3c9adc783859c5385325091951aa90a1b16ddc675015a22e2c7e7345887efb0c42824a1779ea476ede8833ca6e79ce6d67299307e3e89667287303d887c1fcaaa0a85fbc2027ca622070c943b75be6684d5eefc06c31bff4ab5b5e397765df4cd4ccb2c5fcca37ccfcca4074bb33956ef99c59ca8cd2fed06c04b145ce893ae637989f30d7a13bac0dfec8bdfb44420c0b7ddf120dbef3879b37dcdcc08a387ace8e8ee65c3e39bd5cf39b130cdb5a6a1f1449e8cf69a9a33b309839945fbe2f8e4eb9e8e2820b48244ee2aaaf7a1de48ec19b89a223fe2513420ecc250a500aaa7a4f9157c37e2fa3904fd12adf47c21726f5d5144a4a4a571df5d3f613fa845233222026944e61ea46b5012daf1cb238822462801c82b8f8182165f11971229cccc708e733a27d3ec68ff9bdf7de7b6de9f21f81b636b179cad633b05b90c7a246515f7ab0d41395d2e228b1d7c5812ea6628bb346472cdbc2bfc5e7130d356bd40e7a74a209417910304d94177184972f6294e8649ce8354c1e70d239b168d9c087a9c10e3347940890707e2acfadecca23902671ddcbf278377ba39a684fa82271382050c2e2ba21b6386bab1fdb795a7eb6921ecfc58987d2da99a5d99a4dccaf1f28cfe292a5aaacd4d494178dab1332c49ae8ea85125c84848686ca94e944445de6840990929b9017abba13c278628bb3b6caaf1e185862e4b0c559fbdb8ad572e1189d087feb7293812a818ccc9a280be6247fcb2c60f9dbdf7030f037cc697a44c05223822dcedaea5f7ad6645b75585b0543d3a2ada21535542d4e0a2b7a420942680a616857c8075e75477c5b5ef529248eeeb01d2f914a235de2f92f46c580511d9836212a95ea75c8538b2d5296aa42a953aa957ea3471980a2ed1e91ca6c91b27c7ca02a8253a17c50f5ca334befaf3b05d17951782ea0d82e5edcf0629fbe6c18526c91b26825c088815221868c106c91b24652060d8e934d5333660b5199998192691a6634423e5ff86a9ca28c3d5c8dae0421738ccd06ca862d52169d26ca6b4ffb592fb39033558f8f59e3a331c3466ed94a80334c04632c368e39a074498001138602797053eb8fcb31c7609d3d2a506da593ce496ba5738a34d5da2375da39e79c74560a060c1820272ca06a3b166f09479f1e08a48e8f74cbafd799a877c55bedec740ab5fc5a9a79e42b50f5c557d4c02eb09956b045528dcb86498b19326cb106d64965b7fc966a5a35f420ec97d7b82c4fcd946ab2dc8a353563b734af6eea17ac81d5d074664f2f4d08914a928c34239535b01a18a924c92eb0919ae01709d380710c8f980ac62fdcc30bbf26ca72540a5bc424695677f05877a68cc42ffcc2240d0cb58f5374877daa01a6ab14ad283016670925e1d4c5616310609d22a5fea4a5f23ce5f1638dc31627397b263ad5b462b538bee921e1d46868d7c61826d8aa182a8ab305ad29eccb6f89ab0320616e7350d8d6c692c8f2c5062b769018f4856db92dd518b6d5258cb1aeadabd6b9049d336bb6da6aabbd3a4f5d33ce696dabc2dd11d20fd5e683ca33afd805d467e9fae93afa2c619875ddd53aefb374c93a7739eb619c6f028e748c17307b55c011ca475c7b21983aa695eb0a8ab4db7c507be5bda786f0bc8262fda072093c68f31cf3077f841df3b0637072295db10ea3cfd2e5a47331f37bf3bc36f76d2e0ffbf30c5a2e5e9ee90d41bc3619fc11f60731382f684ac19600e57a3be1489224f72637b949156979be4d52b2dc222567435cb4d01efaca70a2b0e84cd75a16ba686d628b94bc0da1d22d0acbcc09948b0305bbf012f84f32d4a674ca7d641406b413164db2875016f490894ab2adf46abfdc44bac92c9587d20c2142abc663b715e527339692c2785419aa55a1a8b6ed35433464238b846eb31a462d51e1b22c4510a12c4b11cab224dae22c6be4e7efc1f264b6618bb354e9a0dde910d1d0fbe051c3fc5b05233f39cab2c559bed0cf7e888915aa3bb31faa3baaafb182d32a928a9249e954d40d3a3c5f6dcdde53988bb5d06182f3e77fd415afca9f3f93408749ce9f5f477112aa3b624ae785103d22581d25c5825a3ad2746475c5e1404ad4e995abc8f272f059d397bada593ad214348dc0cfea20e9cf7e27d91667f971e0058fad7b31c658ebd288a7527d869607cba660fcb226cb37b68d403c569f130e5bc436cc9a714aea8e75619ae51971cb71cdf251495b13e6fa73fe9c3fffbf77b9c52e171ef188f1bd185f57cb9b2244922550001e61ca75cddd7d6cefbdf6fe4d063cdc7b6d77efbd95e6d86bedb573d6f0de7b278a8e68071ee46e414a929144df7cb1fe8bf5d37299d7fa9be0abbfc9bdf84718e3cffa7ed64dfefecdba23715c898431c6e0bd9f7513fcfae2c78f39e5f93f5fdde47fe6e0fb1dd3fba0a594ebba8eab99d2129da1d69c3e1ef139c2df64641fd3c7968218d04660ee1abf6da2abcd0f2551ae1275e8602034a7f211ff74680e5733af2312d8c4c62a209306c0c9fe4f079c443af6de8b7f8431feacffff34779ecfd4e451e1e89363e0f1519fd7746fc660fe7c69ad2a6bea46259a9d63444feafda07d7473ce5e8facb1e53994721b3ecfff20d8fe01413f38f9e89783b6d767d010abe3a0211490f05c9f9a85e68c182bc498f9c972c59c600a1a9aba532d4ddda93cdc577774d7397fd6af75d6f9b91e7844346788f59039aa778c05ac189ff882c40b171974cba2c0b50cfd95314920e0c54993da0a5d56d0c26d67f981e5011744804fc057ba13c48cc162bc74197d068921a180d9229dcd59c991bb729b8f30b6d793d9ed71817a410a5c6ea22c1fcd1ccb2d1d4d94e53d3eb8abd5687e40bd5bea317b2d5da0e6ea8160710fdaa7772fc7b487eeb60ba573de209603a13e74f2226fc1112ec71cc8e5984f5e24ca0481fce59817895231e52916a594524a29e5947ff4f27fd066f2cc6daae51981fa7cdca68236235cfe1cc8e54f244a0589649008066d2a3802f599d3e71ef1a1b65a202ee76a87e3cbbed5c59320960709f58962833990a73e369717b9a00d0647a0fc3910caa35c10c8e49917a1fc799128160492c1220fd214cb47e5262d805d790f3e51ac2df23e39e5bd52be2a0189a5a29ad0c6c8483c9b283033470c1699ccca9a6c11d348376ec5b1b856b722e7ea565f7effd69086ad7585e326a353a4144571a96117ad34805cf6087b4f602e46b560afbdf5f59f5080a7743155a17431e524855dfd82784aef7858c5c81c336c11933132169391646c450515afd70bd672c9b6f8ae96abf56a8defea5eddeb5ddfea605dd775ff26d8e2ff7ff7ff5df7df79f1b21a1a8ac562e48c46db22a6cd68b3d8ac468bc56833920b975aad6683c57ed8228ec162b01a8c8cd56a3198cd246412ea5cdd58c5169fd562b15c2d97abf5ac6ec4b4532c45ab996a6ff69e6017a35af849f0f99435137a744a62443af940e24e63b8eef4a4f3504c3c17a7d2c50b94132f44488df0f578d1e0db3dfc3496700b06ca081831502bc4d05a06ce2b85391934a820686aaed4ccc0c9c00c1a58d0f84e4f7c354e45356c9cbad8b099c2e6064e0d372cc071c10200a06a0098f5027d01a7c24b283125f0073000a71d027073821b0cb66090011019e0403dc111e20085262fa61ca82ed3e6d07162c2e630853832d00182113996e45862b3b158ac16292b628b5846ca481659ca582c19d99acd66b49a4d055bc4b69aad36abdd6cb399ad46fb988c12df7bef78eff5c6dbd5bcd6f546cbd3d9ba9a3776356ff458ded8d5acc9fae0b8b6e88d5d0da88bd8257aa337c2620ff35ede18fb98373e4c4699851429441061abd1acb062f67acd4a18e7e24659a9658bb8949532d9ace45c9cab9471e32a4666d9222663642c2623c9d88a6391321fb68865a48c244b19c7e2583252bfc5165b3cb2246eb1ded692401f140f5b0c7b99ba63ed996e75517eadb3d6f973fefb209ed2a3f5ed80f65f793ffccda3652da33b309893cc1ccbed25a2db1bbdc5b6a8656f9b28cb7f467760f0a998395f45981a8d365155b6a86396eb98b6527934f9379bf3e7fcffffb5d7a596fded6f5a6631936af2682e58b7dbad5b6dd9e2af447de36edc8dbbdd3a994c46be8090c18208014c46be8090c18278c16418865a814b368e1acbb08c8e32d46a359bed66668bf866bbd96cab1b57e36a379b4d269391caf175db227e8daf5136c25e32d96b2495a4db2a465ad9222663642c26234937d28d8cadc67184c5a48c3a541947584ccaa8436c1cc7d3150122ef948ba97ec3ebf104730cda549bca69afa00d18c8949d81fb5ca24d28a9e1ec34357cfed5f0f9d79a2d11555bddb1b61d8e8bd556c3f0a9cf7fa75e0f0b5ed89613d526ea6d694dbf9a140665dd952d2d4ff5796ee3fef4e7369db31d474bd883dc29cfa5dc391c442bd36dff17b55dd51d91c3ea8e586b3b1a6806d1a6f4f7f9b116017591c6852eda1f1042983b1d7ffe63eebce0cf4fe10d67c0e4d9a17f16bc8a2f8afe64fa3fc75c9c43501b2da2674a94b462add1b02bbaa332a139cf2f286e0b82f92d0afd3945fd3f7f4177d42766cef34731e9cf2b288640fd957c3fd5d4710b8aba47018a7586fedcf6f0eb4fff5c57acfebca716d5f0e96da8865f1950c6c97317609dd5b00a1fc23cd16924d922b5cd94b42a10b5d59da26f135513cdf26cb01655d3f37aa60654aa630d9f73b97b8ff4b93a9911c57e5e892a0fb7a6af45fd791deb8e138e7a3f2e176dd7e09d61e67386790244bad66058c3e7357c4ebbc847fc39cd472ad5b9236ab0dac07a0116fb769b9e61b5b613c21257cc707691866dd7a669f61096a04dd3ec39dbe4e86153dee13286084a1ecc5ee384ce4dfb29b1c5ef069762ec935f4e85b2e0ee01352432030603c60cd570f61428ee13cb0a4b7418cf5ff4896585327d7a13a0bdeb13cb0a5e3a2ebdd741ed14b4c087cda965711fcd8308a93b934891c913640e99a24f2e7612983c434a46ec123e27f4a6d937be0f8af0f49121d6b1a91208f6cf93378006f5f3f9a00de59cb282d88001734029a87a61e79209d534f92d05a97165de7a6050800f569f93d3c967b9c720c4b4906c550f57551cf404a915e03167fde79653e48033ad622aa5c524d3313fe107f9d7c3ae9cf2ae411735e8996326fd45551f81306ccc7549e752a7d20fe2d52d97205980971eb28c1cd822881c9668a0c8038f04f390dff03f3202590c4950117249c184de4095167e01c923e1824e028521aafc2869a5a09564a19a422ab03c65f99cb2aecf2025a4b0c235be66a86a757b00136a38b915359da4d67b739e4aa95407b83b98db7b2f78cbabafe08a5444a932e11017fab8540eb8ce77da3737288dd6a84d9c37bdda2cdafaffffffc7506688396729cc48391a11e2565ba54aa5c2f0b9783167f1fdc6c75346fb7d7b8f3d96c34cefe36b2d27859b96c7476767f9b870a1702855868846dfd837e289d26c37ba624d2e6e55999285248f51187dd171dafab7fa85d2c13a50345252284684a892a2ee6386cfa7f7c1e432a55fd9e534ec4f822d4d4d7d8e8c8cd4fa31e06b2b9db3d65aebbc5ad49f13d72885dff7fded777e9efbfb3ac0c0fefefbe606f3fe709cabbb9773756336e5f65a6b2de7dad45a7be91b81f5e9c2d6dac93f042bacb0e9c5cf7aad2b196d3b4de9ec9cb0188f48a32b58a4c7d75a5dfb9a6ccdbb4fa5e3898e273a6c408019aee256e76a677340d99667def34c5953684d4a2ec634f772f9d61fd6f611aecf83b82cf3d9769aeea89d7ab8c4202ea3ec5b8d548dbf685c52dc8586e9158a7da76cd21d9f9f96c5500d4a7ad45c3450c4a07169e2c4e6fa0cb23206c60956a4d8c28a153296f89042d82595f5a8840d9af5a444331300100083160000200c0a060502711c88d26096e40314000d59723a6c503e2e8e0542c1381c46511044411c82300cc2300882338421869451f60c5432b44a52010004d5a5a99a47120d6b26d859bc974829dae373c394e11bbb227d1edc0a3b569664840a5967f64a3119466f83a469794528258e2c60492f431b1fae3a7369693178f75f085196852d3ea0779fe6e22eeac563ce10cba943026252b4eb2cbea4444f234a92ea1125716502864ac3b1deea7edb80c876b8e87f269606671e465a6d938843bc65b417cbeff0594303c63945d8ff564e470aa358b0e28d360bc108c88bf1bf73c0fd6c24b4840315f7beedaefe0ebccff50234d9ac9a6432289446b6cef06385b7f3721efc2dd2b88e449e937fc59ecd977c6375f1b46ff4a54bf69f6710769e26d74900f6c339f4e86a6d30986396c30a5062f757f4daaed15890d21fb7782d04b218dc1f96606d7245a195200a9ff0d3a277d5ee41b60798f973ca3162e3802b4b784af104c5c0be47bd1c8c0dbad3ba4cedd378c8c2e7ae07a3a7c33c9779eb0f3499bf14c7700bc14da007eb701b2a178be0ceb28b4530bf8731763b8615ff1434c349962831be724fd2a81390fbcf4a5589f0f617fe35daa74ec97429b1870c5b13bb6ded713a32502913afc9cb19912a3a2d55bd84ed4e067412c1b57545209e21f58afc204437d0644432c7454d6262fd9748310527a659ce1a04b16d0f3fab40e4aa75483c8c638052ad4a051ad474ed90039c34373bef12a8164a6a765b5df47a077349671ac271bc76a0c088424d7f10c518a1022d9802dba3bc93880c6861cc1c3e70b9b8b752d9970c511a6f91cff5b347d11f1fced1190606633563858c836511229d7515cba5137cb02462d43793fc191cd62a102446905f311b6eb6369000cf1abcbbd4c425608d0ac888b993d7c1422f5fe7fca2e5b5bcbd1343106ccd18f0f5bd9f8d8830ee3495674058f380ecb897bdeaaa8f451af54d168074754b8f03d21dfecaa4689630cf6673f1107e9a8ea3ce0f904e3439ed82e40229060a1537b0b94af14c8e686d5b08d93cb95ffdca619910c948aa68983e548d2d98d9f6a4b8d5bca27bc90bb621cc1610150205dd36974114f59b230d298678c8aebf9b1c8b40a9e12b37a825957ce889c143454d7f45042e2c1dda91fbb3872d5675fb5ae50e491e57448c5d0edb191d4db14c70294372170b1d748e1f476ad9894a43b7ddfc6bc4e48a6061981be515972ae865f1ee803128235262701a1c0f181c14967182c50afc4d4d2b289bc586b0b6758a22cec72d9d3670557507e20943d3ccdaa666c04cda9d876391270ad79501dc5e7c72d58580bb184649b045394d3e93baa82f26860ce8404cd387b2b741a0f985270a13ededa39dfd67ae40fd12bc6e1460a019266ebcd5f78d771650de38f4fa74c617bfc5afc181bef862e891c89e4d23fc52fd8e5d34087ff4d80744c32dbf5cd0b75b61463485a2d1727db799867dc9782dbf870c6d134dc4563a38418f7088f9928711e983ccc4db5f8c20abf03c4706fe6613d31ac4095dcf9f7bb8b9489eba82493a5bc7e750f529654b82b5535918be80e4d0c603de5a6373adbed34e877ea96213d168d1abf585cd11030c81b4b6d70b03d8cb7c26029ad55901d700ea8d6de73ac24cd281f0c2b0031d93629d43b8b0c59e98100db075263e9296db4a2a0b35a44308d6d2292da9fe5c156f0b3b1223b380a04d2630bb249192d02e94948abe163455f302595ad034c3c28b14159edb443038d0949aee49be837566087f0ccc6d82be66597b351a886e2823485df346f7e393015b560cacde6a2952a557f28f8d08e6de06f4b807b684a90451ac27ce4bd607b61039d4562c4df912e6c1730117102a2b3e0ea1d081f45563c2ac81edcf791758d0691e3c36097a9ce45db644de6b3e466856925fcb07b033e85fa51734352671c413d0eab49f6544aabd2232ed2bc830cc7036a5ab31bfab16d416838d7925ab16be191d741274b71410d94480a8aa93205efe189f2519364572ef4cf36ba2d22daea51fc307124758ba4f9d4ccfe11619ea9ec0fd2df3a649c27d81d9592755dc64054e5355cf2b740b250d0b7d99cf1a7e015b79ed2c0b35683f50d949260716bf8c8bb181729f5a99ece47bc50e12d5d83f0305d9e9656adf8eb570b1f438842a736b5478e99b51da90a3f1318f36a4421c2140f5db1f867d046a50a6696e5019c40fa11d5b0080cad0838ea511ab3d0b4d066c2d928ed6334a41f4fe2ee014e9f6bbeb0292b01a47bae99e38eaea3c6606680386e3c8c16cbce5cba8c9e431fd4f0090a93e226b41da638ae380c44abbc7a9729f5dc20e4b5d166e3d9600ea54dc67b234ad66cb976987d22c04dbb41d747ea8a2f670170073daa4d12986563d06b18a1d8218cb797407a4e3fe47c11e0b81d226b606e592b5b098736be2a97383e076cd5cf63011823b72f49cc934432364ab95ffe20ca424139f6b9a7949d7906fcf59e874f5a524abd9c855a6b9303e0a250481765e4af907992c0523df3eab85d647b3c123602bf63a2cf13396fafb530ab7ba9e42b501f5b95b451044ee79aa835a4ee140b4337ea647a48f721b82b86628a902e88fdfa022e043df56332443ac6fda14e421809ad8fb425e7dabf2b504b4f045ed841b983a0390b78944bb650d92aa8187b2ed0197c4d0e2f29822411b7a13fb2e8e1e3c3b2da4dc9cd6bc461feb8ef83471f06bc4d42aafbc85b8ad27e798111f6728d98ce3a4768c6a4e786dff6cab084172b068c9a814f6c8790c8517be09b46f64ea7c4abecade6caf5ab18ec02936f060ae81752dc9c9c06103e483dfc64216c10aa8f6d943c080c78c2690837cc0722ece95422558c8069de45cbeb93a660b88e271e5f9c5a2c933bcc2c458386bcb1eac0b6496b6b6484a1cfd52db03f860a857a27813834d0248c77a9cd964a5ea83849a7c2ef236722ec40059c88904564ff708c52b9a0ac62b115654b577b70499bc26de6cce447b0dbad17a80948829730d916559ea240c131ae990014a983ad037966ca94c1f3c4d1be95a117941e1b124be5b9fd68bc281b3495e8126580dd8d274bb0ae4b71709d3e799b8b59fa758e4ae2a71b9e39a2ceb39802a20c0bd798153fdc418230b01b3355064da4e55b70717e8f179747a04a33c52ac3eff8450946a92a2c88b48aca024e6179b3d5825a82f9a0639665680449dfb123ad047d1d98361e45628805623e92d3c24e1f14f078bedc340c482f54d753bb9968447d90cbaa0bc5397711634a21b57eeeb411305b518a1b66e3fa8078dc458bd48d81844f24e99f79f56577849f99e6eb1dc1a8f2236e5750cdaf11b01f5a1451f7527ecc513e498a467d771cf993ff9c65addf2afe22e5e3e8d269b055ad43662853f2a0e09824264d40f1b2b968b32135733be559f2838161af794746cbf7495b2b753b4b819e8df4b16952d881ddfcc507f7217d228e28796fe95e82ac14f88f0e0a1846e36f630ab40afec592574493c929c30af449ebfb3cada7da22adc19e12a4ce074e2314415ade5a435610040326021378257de660492f77d3c0e1e567f0732d987f77e0f333b93828da46b769ff4627144388e2c3d97754f03102aab43d19215f7a9a7b000a3d8ead62614292c0cc3e274e110d33991aecb3f81042e4da67de76ce387e5a0f1d6115ed8221c4b8843064fcb255147028f148f68ceb5f5df06468ca3244ae25bb097d7a0eb9e5e3e05f53d109032b937ebcc191f6e9d95410bf13e00a51de6e9a5543b628dac003d95448a4233cc38f35dd15e96517072da8d199ff68cef852621b3b36b3884081770b9c3359ddac94776d150ef8ba28bea4114d3b87441cc60a2d746c0165d544a5b6ac830ed61e618174a79d9d078e3bd73e2b8d61879b16c344563e07b739b05f431de024ce14ef985e21f4445c5d449fda328bc2a691abbf8bc8995dc0eb398b09376c5b9bae80dad6425c6b2313a92359f36c319936c6dd672da2829599a5d3fa29fbcaa2899331f3cb7ce2cbac53b389960317576755693aef3598f7032d3895e776335027fe5716a64a601fe65e6703135674e0bd328a4c0cb7dfdf181e9a857374a151036c6253ff082a7c78b1513b48a929137cdd2a0ef7d8e131ac7e3b84aefd60b733604bad136d32117427b5d6c29dbcf893d980c37b559083c824c43756419e83136a96a4b08bc8c3f02d97af2eb15cda2a6d209f91c8f2dd2b65fa0eecf7fe57a1010dd75d984392f094ec6b667cdc94ed27ac9b40f5bf8076c7279bc66a7d30d467557fd06ed6a20d61d16187afdc0fca57d2c662bf7da6371b9224a158841567dfbf3d88299808a9115d4b288424b72fa7f64e43a609f0b8d4232afb5385ca0f5e6ba188040f99d00350fd0d9fdfdba55e876940767b6f5be507667d58331c6c33d6e3ec218219e2252c715782b785401068f0a766fdceef191fb73a7da355121b5b1033e6de8a4c0b3a6606cb16b4d5af5dd4c51f0ffcb1a11cfb478648b3e6d3eec0b318e669e6fb769438dee20f0d71d1883e369a251bd3ae57b0f06f8f75cb5d7d59492ac72577408d8a19e13e85a99058b1e39a45ae95bf65a455c21dab92bb45358c29bfdb09c87240e8b2b27832e3bfff1de482b24284a61b80871d9b9206b1940161644f722ca4386cb4030d0868f4a2dc89e5859793d75521b482002cb08479975c1a312aa6670f19dc29d848b7091cec865d89c90da38017c9c3232013ecfa954203a6dca00c9cf9d9d2ddf3de1a17b99d5a7bf0ae77ee7ce2915c7bdce6b5f206038377bb20600cbb161c00c15415366a8751076130a166198d9900908bffca0068d52576469b9facbd1b364c3f5aa5a3e2753f8723c691f72ab31110d20de0e0f5a1b654f7605a221383aef8c827ae5a43422689336e201238506f1943a2e1bd2706d05ad6908798e26dce988b8a5b183d3eec3141289a3c665e0d2b0205f59b66b79b15cc2a16181d14bd2d0f85b576296f3ef852c9f561a4c28583b7502f72844696de010cb7914ac1dda7a9e930cef0a1fd59a8235ef09bfc7d90859e9aa018a6b536bf20c163096066b9dd16a2830cdf2ac0f4b8e5659bbbd3a68a9914be5c295c74ec8158a220ef40a9c07184518e3189820524e599b79439d3cd88a58f3883adc6dbcc2aa3fb0a0e70381145b0cbb26268e56249aece38eb3e675edd5e6e54e83fcc7f6d6f9f09b650f981801d3fa97918ff11fc73f60dd6a5912c2e7dfb1ac67ad72ef16bc98616bda13600b107c6d8c9d15e8a16720d3317c4bf48fe9d6e6cff5fdeb22756a8f8b36a98a0a25fef131f9aedfa87dd940cfc80dfc23df540f8f73c92d15db4e92e79cccd23e7d2a46f3a5f0bcb15cbd9aa689be0a4bf1478c23baa67620cc194b3409cbc3c34d3a9c7f8e84fba13848c308c657c1438268d42091d8f860d5022aa97d8c97da6ada3ec9d94cd5719a71b1ba6184a6a01a25a9536851e3790e679bf8a2980c383bef4f2267e0574be9fad3e7106901fa51b0a5090b2386cc01610e29d9a28a2710b45a6e486b476c01aa2795603df27e7a92ee020050f7147293194981d46b36f4e731e6e47712f6f4e14d5f093c4835b31ba2b504ad8637ff4defb33247bbbc349db6bae23e723b6a07bd8684e8cb628ca3462df9cf1ce2b5b679f656195666d3992d3b0d0c280e9d91246b4e31088446e5d5f5f22cf2380d2160d12794f61580ff150854a636b96110a90e6eb97e7d99f0aed4239d1ab060899e1be8c172c0dc3fb3816522299f13c07cf3d05666ba00e42a0774a43c687b66de87972f3c98582c290f8b60056f5815c0f3fb4b2380aba142019c9db1f4eca7a91d5bb185fb73fddb12ade8de6065ee00ebc441b0350ab4b78345f77bafd51a28498ac8c494b78bb725c7bc0337939c3f378091cb9a853c0b6ac3625ed077d2d7ef40b57f78f11338684591e60809ebb1a57575641802d0d6d75492d9983fe01c38d811fa02d120248e0980dd8445da35e17ba62f4cca830d4c028a3d0f955049fb86e641ba19b421a7e5bd7139eb9c65ff7e16707bcad13993d5f8168992d67c2a42ce8b85d2f042ce09bf2878c6c121052b505e66db01a366b5fe72109e488b7e5165a071921ec6185e961042f4ca47f56d77d03bbbd8da52ffc39600eb080db81f64354cb2b1e6a9ecc355e0d2b157744205b052d6bf47a097ce67f4f8a2d48b89fd9e7cbb681f311c1086d38b872953a0538365541dc7d9f8fb37af08016e4e0e40f08b09f759a3afdb67943ae82ff5029ffd950e0f34a109e33d1c7eb1a343163ceb7491013126fae524b38765ecddaf7ad8b6e9d2c415c2b3398eba5d9625e318ad19d336b72c430fe7c3e837f1c7c15e6d170be7e53e8cfea00e9fdef1d1b772b845d3ba3cba83f1171f3d7c294b8223da334cdb1c5976d51ffae12027602ff8dc5771e840139a1adae1e8171f1d662953d2115b32a69e5cd96476ee794629076938daa75bae345a1f9d45165ee0958031b19d0e87f137b8bb3699c61177cfff08ba77ddbfd01ddacc99ea92b4db35d57c632849bc902be751f80954a12a089ee1764120a6b10b82c8a48a502cd95278ce317ef7f50a9c0d8c5bf06110e8c2b55ecf0ab8f33099027e1f365bc8cbe36505f8f1f0bc2077e3715d9515e0a13fba9dbee0143078f340ef4d1ddb5741d629c0caba006b69908a851f18469fbede83c5980031f4ca0db5732e6441e97105ff5183620d28d061111a3d526a771431f00b7b960f00ac15b869956596086e01173f3c2be0f4785901171f372fe4f5b059215f1e972dc0cfe37305797dbcac80cfc3b2851c7cf8ac803bfd981d0ec4421b28e9ca300799b6c2fc51306803818ade8f96c03bfc8e192596ee8ad9a9912f704bb86981a389772a744bd879a123093617704bf4b9a07bc226054e12362d7463e227054e249ea9c03dd14e850e6c22f1db9f4a85abbcd77cd5e44a5ec7c8b75653676b9b98d6dc414b06d579421ef3f0032160fa26e21b9b4e8e75eb6cf05ccaa0bd57ed033f1c6417ec0d5f7d2986293451d1e33f0c7ea3a3439767c81ca34dcedccda1b1a79301427fea611d9baac2800fe2c7e8f8c0922c12c76c993337b9a5874493a4e44d459ce23c93e57c05d087a9a084be41c66d6d725f85bb2b5538be10847b5b53d83390eb5477ec38c570e83ffc02628bb90f9a9e275d040c696664fbaa50137c5695bf1816988e59ad6a7520dac572eb2a15b70a42cc3a2c35fab17815579f309d7346e308fefed00182204c230f180e2d2c68a9cdee244d07eb85cfa777fb50ea457adeea231fc58db9be4c5164b2e65de09fee03f4424888ea42606b86699b23cbc54e291f981310c6472c8b267a797487f13f3e3e7beecf2493dcb1108f32e55714c931a40856ddc7c82ec28dbc736ac2d5607fb85db6239a923f1a0d1f63edc8dd692eba98f91fd2f134d1af72e5235380c291677f3b93dbd4cebcf0a05c36c7ab1d7f18ccaba16cb047a6d4937ccb603722bb338c735bac5899dec2f5c67a4250104789ee61531508951c144703fad0b2ef1bd64a1fba7827d135da59de7188a0b54d8fa3a51f5be9a75286c66596602b17f66d9ce7189138c5543f9560e02b322c62daedf3aa5ec90de0e3fd4dee0e12b0342497fc61ce7acc8ea40a1a8e4a98b5ee756b62be512b882b087c4b28cbd941b2ef6345f233988d7c12b2e8e79c6bda78a82a6a8d13b734be368ba59c304473ff98bcb11b71d11f664e08d842d75eac8c626ba5d9be6a54c959ff3163e99d5806b8d585de0cb901e8065568692794db49494ab575cf14d527a3880b5ae96de57db32d3821a1c115ca10a1670402321d46feb8a9d728be712b55ba1bc61a5b5e18c77626581469cb39f6852262ee70eb8d7109736b45bb3033a41640645f12bddb58dada14ee3365d1ed7587d08268c3e8ec1a7925031526e354930cba87e743addd7b2c12db91297d101bfd32ddf323287c66c8dddb54dfecbcf3eb8ef539d51531273031043da9fc1499a585189845cb785c4cc716da011b9c1b7df8c7c98732d4f35162716d48d6a02860672a867633d5c1c0390a6b8f99b8d45c453277c286fda485f18eaf4644c7fcb9e64b410d15ea3b808dad73a5f14fbffb5716d57a9a9ee7212773a450447af359be47c542066bfbd02049f3f8859cca7418ccfb2b7e5a77020012985568394001cfab5b676fc6a77e75646b2540c0785611c208d34961854c3d05b98f3a427d74deaecfe0c974571293abd3c664bca25b468933cd6c911bc74fa941cbbd61dfb3fda41a7c185ea2a9ae0ced20fa8d8fcfb6fb3165480427a041beb12713cd09ad4d0d0aed8efb290e9abb89c082d3c3673b1db9b39055d2b12d79a646eec3f774724ab1f591b327614a0cf134652bb43f58a3da71dcc6df9fc94a081af4c994f22447df8b450dd6b7625f99073f6956d2df96523946e25dc73337dc73df38384a7a18d6ec1d4e7e80d097a6aec0da1b937ff45a0192b1dea662c840863d18b50fa12baca18ec9de0af07a11ff5095c22f85d64789004c1002a36487992e9aa0fcbf96123e91bdba7961d9a4c7d94b5ed53e198a514492ffe6d70e5e4f0d2162c1bcd8db7777e1d94a5dc1c69b88df85b9098607d8be341e81bf34004190ad03c72e5d71bc2531965e7a118e50f7d7d7bf2a28eb3f57eefc30fec447e195f2fd064ba19d477ed311bfed41c6d602dbf87a284109c7327f0571c811805a1685464433a512d01f7d1cd03b88f839fcf75d1b6c68ec407176370ff7516ebb1cdc7680c488dc9da51c73bd28706e2dd961f3b518c48fba22f833ca66febec093383a92e3ea68eb262dcf9b28a3965e47d62a601d0312a17b6b1b930c1449e623582e502bbb35742a3046379a56ee9801853e9d7d22c3e4231b5d1396024f987f8f1aab62d15ca784fb3be9c20b9d8581399873360657e38d096c6c17eb3c60b6a32c959b4243bbfc8d1a6a23f91d5b0874d94c00034901b8b6cc7b34b510ebb1c06dde6e7930086120fe5ff631503b725e94ea6904512f22b54b10d90cc605ba71d9427e4a5545b44fa829b84498507c37f147361580b06c7a2dc4db54f33a2a81df17a21d4ac03d1b83935170665f295a6e7f303d90b123e40ff948dadcf4a650cc06b4b7c1abcacbecf984b520971130370dd3a2ee9a15b885821e012647757165138f999ba428a83c0bdd96273404c6924e6257e3c4ad9074506b38e3780dbf7fe88100c31fbfa4fee412115d0477063fc3841f2a860ff7734504dc8fc23c51e7eb0c12e312493eb0c157e98aa2d6bc908865b80970f1d1236eebab4fc6aa7ca95ae65a4b8e35d069a2ab60734b24147f350c3e99b36c90f1a55027f07a12ddd3443ca6d8d6d5083fe00e4699183482a84b30ec6bce0759c02aa247945424fe9e92f494aada27b53e768bd6357d5364ebc5e63312ecd442742942e0790d5efc3f13eb84270e996d9e046085627a15cc09368fcf7805b75d09a9ccac6ccbca7359bce9cbc982b4c5bf3290283865dc8b846d03041f01d82139b577d71bffcf5d405e867235d148a14c515ef09fe31b63628cb3726a3089983685dcbf50d676fe388355e1149db754106a7a607775548fd4453e4868c81be0a0db001920a8f30274524b030c65336d80c0f2a189f6f01040205b59fb6d1919669b919c565aa67dc34d3f0697643868188d50d544c93761b47844455715cf2a71e343380d6ab14f26eeee7411bb9a33b9e00947994aaca5bda2a044818613e59dc0c736a117486d50b4820542db60ee14af25742a5b189d853cd2177d08784b0d4955089be5e948f123ec3b644a19a8a44ec37cdfc1868aa144173f0d4977b99e3bc65a5ba4dbcda8159a1b2f7182676a0b1521deebf0a6bad3bff3e1f5bbd87a318d2e9e04f6c5b82daa5c75134c18d7d1a7a2ea72e0ee3094c60597620c3f5abcd14339cc977df4fe140e8d358403f22384a4b42d7cae70b8cc7d4de217696683450a4794047a226aa35b7189503c57432959015d4bb037a086edc15ab436e4b91e7bcf2cd939a34811c7954d9592f9b559cc245268b818b4d0be34e132bdd0204bb433b19769421a33da0cfc128c3a24c9ae1a9822281748fd8cbef769c51d1314e8ff2741079b2071109c459f248ed963dfa936ea91f2df35bc50bf706ea6f0ecc7f5dd40de220a1bdf61de4a2c50fee4bdafd18be2d6f2737ee6b52240304d54443dea93a65c270cfe8644b23f27a95ddc3a7acefb9a739f304195b29416df7339bbf27c38bcfdd5a3687d0c2322e77889d0115f59a5f07eb6cc564b59e1447f2b51947d2d617226c17db21d3f62753556c02f07dd70bb387c44e0714ff39780a007ea4130ae15884995228410d1ec5a0c832f17ec4aeb822e45317ec239e22389cd26ee4eb06b28ff749dbe524d5c723675c39de37e70e4cfc16a11ba4fa07b1b4c72ec5f29afcbdfab2d7a60608c634521937479eaefb44d790383ab7cf4c01fd00152234a2c6b617c347f8e88ed33e3938c4c3af3a38340adb2c0bdaa136e66ef971a1c5130873845c5bea0aaecc2f5df4809da0ed81e8fe7169f728dafcce8fb13dd87373e281537eaf5907d60d3efed9bd41cea8f42fcddea97016aa728355e6a13cf16e18c033f3b62144d19e1f54c9389dc745fdad0de857a7e52fee7e395afa165b0557fe086e699f82ae677e7aedae872f3c9a151ec8696db992c5b47f4cc7b8c35ebe9346a6d06a2eb517ce6c138c383d5c0e220c5d0325b72498c5084a8f800aee28dc796c921b767b52a8a6ff1b6af4edd6f642396488dc80ad8f8f18f1d94858d23a62de18b1d458a5fc0e096c03ca9c6e7f61b05819d2912295108ee339563b64ba74c9da0146df8c9529763b0e56339272393b37fe2b85138b4f7bd45ca4dbc32e02d1fb9c40a25350d4c27d424e01544d7bc8ffff116e699987d1109bd9d5bad51c5ffcc0ba20a7b9fa1d7cb32a422175dc6d11250cc5df35d4bca2946a8914175e5ede89fa2587d6e28fa34ccdef308beeddc275fb76bbd1ebcfcf9dc45d4c6302b7829e4786b33ae2a6c6dd1c2ecf0d4b17ab99d220c4e04d03c3e5a6fd347fb56b1ee492047c2b4ece1b93d351a12813c84990fdf7e728c9719f86c9b5658f8e505b93c4dd935267f39564b353957ffdd6a2c8478c9b4ac2916fa9a7faf83a59eb8864f200cf56d022e8907ca61a8273d580ec67e070823ce250668a11529d9617ccacb082b5dd4ff04dba28270222beca58405fc8f808600834b8c09a975662daf0531cb504d85ddf7217d3f6f694d104547292a92917b5ed76baa4517369950efa15efc1c5e06433cf0f44dfe105f8c80304097250c7b80a8d4085d67df1fca07fd6c469e572aedd2e83db7114559e9eeaccf12558801be1770057f90ca46af1a8cc21a760cc65b0845b4835248502781e1237fc3a0e12cb85ca757fc3f00ed11c7729fca6ee0dcd8b08e9ab8dddbc370b821eb5819156da44802223814448d8fc1cb82d090c9a03078c540376d2da4c7ff7af92ab9d6b253686e44e82bd8a8066e5697f61ae79a04c43dd9de4510e850238d1d15f7331ee25ecbd1b2d0a5dc3e9561b8eee12cb051d2956cdac188b44ded4efbd6ce027b50eadc148a82b6ec05e724a84dccf6057473aa18d58744ded2095f29df1125ac8c6c0cd5884a11f6faf01cf7616029b98d52e5fec5b23bcdea6150f96350d67c6e5ba7bea65ce719cb8b510751658f336c58012f50d6d80366c56fcd1b6050c7c24b9818e6570dc12bb96350ac2982fb16126912d37847d00dc6e92a02627b0ca8e4a5f6bc14e9f380287080d0de28311d647dfaf3716677dc09979eab1601d33ef90e2810c9652c58be515714ef6b0c6c8ab4fa9ce7bde3217f8e09a37bb2502f1415aeabb7d2d67be5a8cdd73bfc2a528de71dd19a31e38ca5144eb17c02faab909dfa968fd08125d638f340e8bf022cf4c000d163ace5c93e34f84f8800573eec5db3f364a497261be45614c409620a6a60ac6326a9239189b00589de08109630de3bff70111c5bdfb6fee5469028fe168d4c587af6326159f156ed5b8d85395a5be15fedff8088c572a1bc066188771edbb03ea4935e64d0a924986e1f81e5ca358419464112a0321000dfa41c74ad8fdb2f03e8f4de3ad2756e4eb508ceb1d1065971fded747896b3535d317a79a60f049a848ab8f12fd28bea3b2c6b87da832761f3cd1b52bc97f1f0d6985e7fc3b69153109e74e66f4a19a6c58608003109283289278e1f38a0498cf3872e8c72f7673815a7c0d48218e0a51873ca55e434915be3dc3a1241c449c2c589d498189c93f561353e33b5c5ef99acb48268b2af16858a1854eb4e92ec6abe9536ce05130bc0687c30bd02c6127308b76362f48105874d0633f47b5f4d728d6ecc2411da64012576d63adc72e84d906384aa76207f01bfd5ae83d062f4c62df8f4197f2fb4860550af1b88753c32139cf7ccf2b40cfd750b2f92a1e6897e8a45cf50eb1d326cd72dd4ae20eafe344c5209ac7c4847b541f45b190e3110bc89fa2412d7a75327ebfee611fc71781c08469ee0b47db161b8b066a4500d2454eb888b43fca8e89d9ce0368fb08c427534b652f6672df636d8ede10efb7c17ebb203f92c04e206187395a198ff04c5f1e2229427e365bd8da7986aa82db5d60ada27c8096e0fb6bf1e734226c17134d1f95b18f2ab1b110821420fa15a58b729d383d4e2d006b5d1d48a7a4d6c7b3e76c006001c9cc571dd84091c7dc6c17d5bfda754dffcc456f982ca80403c4e767b1a2ecb4473fc93f5c825af0f6db85f47090b6fb28e9aff60b4ca4e5c306727c1ec03e02dab69ef760ee08f4b7ac45061fa9a7377d67c99de6c236aec7514ec85cb9b5d19544b43d01f8fc2c9a759b176df04ea1cdce14e681c3b45f65ac2885811a9da8d36ead0f196c01a577ba58220c354d897dca64df74e604a4bf539e3bc952b07d683b94148ff924dc06553136ab2fccefaaec382e462cf18c9c57519b900d57b1c2211a80b45ed3eaaa71093f160b80dab7d5e5bd3f77def393854800476b096462dc2ab523dc8009f2563b061db5589058bcd0e7558508246f92b9a48c5f796cc7f293004682175379331a23686f2d7218043f648ac412bdbb6b21692aa13bce3789c6a1b2bcbf1582accc822c489fc33613319745226085d8f45e08bfa7956d362dce8c49de01590cfe0e98758d9fc703bf5813031dc56ea12aef5c0fc0d7320213aafcc8f90a1ba478f0dad4d912fadba5ee42be8f5f01551d3daeba55b85e59216d53c47f943d4690fdde0351e9ffa362072cb5e9e178aa359fb3d49ab315e793dbe01cda341fcd545b36cc259c82e15018b89d7fd130da50f5daca11e2f373071d79ecda85b37b8ab000420fabfe089bb3bb5c921f28bf43707fc47396aaa631e5c92f7771e76df3806331444a5bd0de59435348df44033b0dc1a65a12bb63da79ebbc2ecb610b37dfe54c53aa961368356e1fdd94007670fccb07d36e20d3cb594b4011da1a930eb699bb35ee14993ea2abbd48191aecb188cee065f6565a83689a361f6fafb1f0ac299fa38e264d489b0abb83d4859ddc796d18be15cf7ab1858fac442ec12527e8ad5c0ae809f47c523c79b83fdb462424b03811cc639cf7cf61cdda0b53c85273a52d5f5de5ab30ad309139027e9adc4e1b504c4365780aaf5a1fe1f5ac8bc16f6506a0e1788e009a0ce44dac66a05f159c19a850669f9f4f69614172f4092d0355183014bd4fafd270b91546dd710cef167011a15cea157e1b70c50e2713861f67d0456765b26644e408b87eb900be827bb9435013f2aa0bc82bfcb933cdb8279570ad8824bf696492d2b6acb744a5476f35082a1c8874be923550d47342ffa8621020eeb445f53814b4cb41230b4a5f3fae7a17ac6b45f1703ea0119ec909c673285774d809e43233f604e44e4cef5eb4283e581d3318779ddc9fc004d68c7d85b893dc0e13066b871658cc8318e27ab95d4fe8314efcf803fcf4353238b155c32008a41ad590b89aaa3c8668d1ac44eec5bedd9388e198bc842f78dd3def36f59364c79c5f449b7e09efe6f9b118023a5ce7fa419eff46bf8700a54b73d3e084ed2ca1bc49a3d72c5722a1601ad453a77ee1a2d4b80764d6b163d41855f6c90b12b87c4863ce0cc0b4a2974f493d32a7978bbd6f378b4ae529300a550de2df3eee87cae633ab084f6c50b2232339d37ec8dfb73f3f9cdccb13101b7bac739b6f06d83faa111a56b57efe543814a5f1a5ac7f1ab68bf5365b6f62f582ac1cc5517d8cc5810b6d4e278a19008358f04ddc3895a5f0545a16e40cb401ded72fb8c636457123a73b27112e8b1458bbc19506608c967c1f5457f85efff5dedfc87fd08c9c3b56e19d0bf3bb0080fdb27997f116979d7c13b808a664f77921fb2d3fb644e1be04504be85ae8ac5c6cc1a46c8536b62aa9a1874f4f768c5b1048115b5482a606100f7903714191408f995d38296a74dde3b94a5dc4dae57c0708eef38ce68e23c5fa3c7cbefa3d7dd43ee5bf388b26bf76708c6d3d42ac35721cbe32a88d00fdfee73f0ec422c9c754e177382007cdaf92687777da00098185ea655dd67d753f3ef793bd559b5a7e6d4df53a3674bad9deeb83a29cf996b98156ca5f64c430d82ea4c13da8aa5bd8fc4f60ec2dbfbf86eba7f019bdd74efbf6f8a723eca6c4eed15ad9d931ed991134d1023cd0738c3fe29cfcbe2cbab5f5f6ea5921a6c1ab57f6f86728fd35653c1fbe84eee13a2bb7a94d83fc3dd9d4a3270d01d1bd98727e6bfc39beb14a30e762e6233c3878f5cd9aa3481e87a5812f343b9e8b09386410832b2bb028d21b6a281e271ccdee5d126ccdc5b24d8d6c8fecd19073ddd2071870a33b2ab5c578091fd9f705f8fe27feda5cdb0872eb2e70e926eb6c8ae69854413965676e8d26539ed8321b12708bead3b76875406d1c6b193a149470826218e0c2237e292f02131ab417262d0c57e13299fbdafd8e1fd10f951faee815fb1373fdd1d2c760d2b4f789bc836f65b1935f0d32f898e53033b78a2d933bfc31e0ed8e03087c6e6d0fe59629488e178634424875eabd8c8111448c0c615e56f502885aa47c75414ce85ed82f04354a7143c818c51a3f7c0976d22e093bd7503e217e5abec14e016808da0268b5ad88f39849f36c63e938f0a15a961778621fce9b57f95c9f94200e21ec23d823cd78026aab75f301c1e473716cedc684232c84b520c3877035ed1fa302d3225a62ee07527361b219c03fed9df58631d8113e813a473cbb8af822fdea10a0d7cb96ed6eb27b7ed0b970aa678875568f0f0ba59af3fb9b7c581760a3178990a15bcc1e5f4317b94d0ab9d120da286e78a46a387cd8b3f97664ab0442d274dac649caa0e0ea9469d391d87d8dfb788efe810914a25d972a8831962ea3d47f8cac206ba8821a3d6f469efef8179e3c187c2d23c563a1becc2183944c9dbefe1ad55f653854cd308605538284d9b0f37b9bf05db5d3edd4a560d340c691e54702302351a4174f9844c014e020de8722f379e8f9d2bbbe6b50e73985fa06f27deec5d3c0d0e1db2c33decbc08c6f62fe3d27fba56825019c5771b39d1134235215cf4aa12f2c63ab46d93a115958dd4efd14622e01677431fddbb811118a80258414d712bce4f15688737ee43f188e85fff6dc50fbf235f5b75bf295a606c1e5c5c1a27238e2ec47a9176bd224307f5f83ad0b7656702526b31b5b0b53f80386dba0d9752daad9c9463b879ccd95d5c9805348096e71c8380a3bb8c752f3cc018f3fc514b2733c8c42a4d10c31daab39f2299cc5ab2eccee352c2e2219313d635de6ca097e55abac101f5c837ea35afc14be0a2ba8147d6a020a46064b62ba1e71ae4cfdcc6241e584cca15be7d3de6900c9d33c0886363e23398bea6baf811e5a4a3265cbb2a420a19e84cb6965c8a86cfde1edaa3be46f2cd8a6919cdd3ce41b1b0cdbfdd0ec011e0f8e9106ed91182b0de76a3f1e927b25d2af2db80a7f903861f8ea4720e5caf8acd433b4da6a0bcabde2e0035ece75366020dce04b242c7a938ad28c86a4df64383b5d416c07ca796d8486275fde3f6d5f43e08462966cbb5f6149f551f8ea4d5424226cfbf39a971abad5db2ddeb7045b8acec8b2fd09161780accb6e1b493cc2273b4ae1575dc0737cdbb7f088d94ee646d0575da0f58bcccccc4d48e2f955d2c22e335873900c30a25102462e75f31ee1ae0e9d9ba5b3a0b19bbb4a1c97d19ec92128f535ee9701622a3feb6f9ebc3cba32f8568bff1566fc5c30f9c234a3a2796baae0bc07cf15ee4c59f1cf8216983b89cf181da34dcc259c52691e9bc5f4f5980b41cde45217cdfad365f467d8239ac9fed6dfe272274aa1ff7232501f365d27cc969829aaaaf9d7d6d069d971e8bb59438be2a11f8cb549531a85eacc0fe86fa262b912a9887d2d706946168323a9b55a759813a0548d61c570e7c5c1e3dacbbba9e0ed59de3515db708c1a77e4155a571d803a5df0d4a11d8b2af4d40311e67c125aca1674674d989aa180d6a9dd893408d181450709b648902afc72942418794d2a8de30c95c8e0254903b72d5c19d2f645341bec4c321454c961e695c90e932e92ff2006a596e8cc6f93688b2cec34f351649599633460554d237488c3d0e990b1dac2eb867cf019859c3b5a71609fab6cf0a518ad323e3bf8360238a4f468a8b0a18c296b822ef6457e363c2167ad10266d5f43019351f4a0a940095af37ea484b933880c9c5466ec56f42b3f0476bbd2a13c88418ea5415f2fab9935911535f099b202acbd8f18d7865f0479d9cae9b49dec7fd9d14f0ed778c7182f7e48cdc78303b41b6de9b8d41e42c66fef9c5114bc8ae8131119cdecae67a14a7b8bc07f30e3a2fa3c1c1b6dfc57af48f475782828f4721957c2ece8f36577711e414531f0e85589095f96e52bd4fa05c1b01e916c8ce13e7c1005920f47962894b280901c053f9cdf651f9a8406c552cdd4336fd3f13dbbb08be326945909750218b8a5eb0e52a661d0834096280bd6cac20b9316584882eaecc519a2f3144b18667ce1c781ceb7787738718ae318822707e549342b400485249ac643d89ddefcc7b274408ead4e7ec414016b8976e98c20ed3370c4569a606a01bba7b789091d767c5cff85b7330589ef06c6afaf145d8f711fa247b61baa8c9beebffb183ad07e395c656ebfddf6a21c26adfa9c5f70bf95f87e2f00796512aba8d8fb606eb27c72e60057c04242b069c4f6be8dc8ba594a415f8f99fc49eac49a27a3039699afff0492cd61c29fd7dfa2dff317d3d2297796ee81e00a1b318b235561ba7285b26e92aef878bcbf2210e01652cb4d3b8f59104bbc2ea59ac3a06f72457d8e4958af0948bd2acf72e34453d1af57f0fcb63cbe323d850582c19b4097d62e6a142a5819de40a505dec4ad96f39cf5538114fc8498b087ea11b47a4161633207428ef71591228ae7bfc582bd2b82477574cef99576f9f6764f02119d4454ce73ce0ed0f744d89bc4c1e13a1c5cd3fcac8fae4f96f9c01313e0fd73435808778905491161311429f681164d8a1ee4c9008f42fb251112b4da69946d5101616f0584a93c5df5d11c247d6a06012a3a329023d313050223010693e6a2885c878e284260c16c7543639da98c22614d9beccaea5c3fcd0f36b0b403bf7fcadc222017afd7b2a370138a097c6d5c625c541338cb0387ddb7def6da841cf8bfc630638f240add3ed1e9ed9414eaac7288200b18a91f2f02fd128dd00c3c88c1c2123dd296bd0f8bf165f8d2d2630bf0dcf87dad2032111c626fa5be91bdf2775dd0577bb76f29e7ee48e76e7b4055322bfa96ce90f7ccf70425be306913c80c264a98147a1000601486595917dd73d55e48e93bd54337adcca48b586bdefec81bb2c29acfd4a35b9e1aa9dcbc99424d2b964d1d0010a72e39fdf04be1be4da33b23514076fa793e2f38045a4eee06aab94b67f0c407f643d0b83edf4244020ce33e9391832327d2003cc7150a1d5301a6caa2df3c32ddb0a4333d607f8eb5549e9af950c4ae31b485dc162c082a9476651bf77e3bd5eb867b5b1145a8dd7bce7591de455f97fdb856b82e5feb1a1930174bf9e67eaf3d7d58fcfae8729662100dd240ee26c149969500c6f0491f38925b61e86367091db9e02cb25b09f5efdeac98cfd8406dbce4796d06342e52b36753483cd9bd80e049aeac45816d9c82598d566bedf3d848353a9e69f69947f193c7182f6e1c6943927bf57ac89fdfd89340b1c0894045fd43e21551a24419263c8227fec85ba24587d0103752903883600547e15a0cdb980a32aa21bd3e76f7bf961d1a35ded09333865c0d42abec804dd5b0897239efd9c5e35f7e7673586c8307332935b6497e6658fa8850921b844ef16a274841ebadd02866e353146e0c2046ca0e92dee1794eb035a276ba3541058c96a3aa836e327e31ef19c000c842612956a9caeca95c1a3ba5bec5ce91e951f21b8a0c518d17e50e7330ca59b537bc8b18bcae6de513666a39eca7ffce8c7afb7e2191e32e3a916eb467637b9a5942949193b0a490af70953f52fb70a8e5f5d1c1e8e577934f223cdf610f7f3c0c2765e44f262ac09c78d51ba1575e82035bfcafc6081ad89b15e7a3adfa74af20311221996cfa48776b5478364c21f14e1f951355fde0c449c83cd114f48391a12c2f155723c112219c6e17d00c7e3f86e262142248b112192e1d85e9521224432a10fe0f0a2e88f62be131c378aee9777e21ae003e92fc69863128ed1c7077b127a441303e851c210ee18d3b0f051300568dad21fc432b40bbed34cdb1ce83805793dfcee85e66ff3c47a4538fa1c9726498ef7cf81a4718ea7343b7e3e0d12faf3e94f1d4035524aa943870e1d34d26d64fdada2b0d9e605f434407c1faa76d87451e5f578e8af9f977f737df01e6277293feecd11df9f2b85583747ccd2e28469fee63b4f75e7c2435591167a76dc1cf10ea61ba6d0cff3fccfa548de52eaaf9bdd7c77736fbc1d4dfdeda81991ab7761f52d3c843d3c2bee7730c19daee197cf33b41ba4d0cb8f99cd3648e2e77c8e97733ebe134c919c74e236559f6d649d0ceea858dffdb062f36ac8710311dbc03adfc954576b174c12b18ed739dd6e8b119e276b53b3d962e3647306fcd1813f38f0a759888f65de052f3f46c97c0b5e666194ccf778d9f278328f7a185faa5ad7d343d3f3307cd0bf5ec807f9165a787d0eaed7cf07f91eafca9027f2425e55dbf15435165435dcdf05dd183ee7d22c5d9a21c01d8bad8de1875c211f746972ae0a0ff16892845c1a19cdcc5b6d234bc2fd42a3d96226889c17a2629d1712c39018bc26307836b3215e131a1c7e7ec80ff19a10f17e84d0cc10714e846216138a5084f124e44428c8e7958d2cb51272a3200014fa79221a4dea4fc875219d9b818a732e9017725b6886e62f1b99b7fa6bb533b49c1b31908be3290e204fbf8d0cceccd8d024b8937321fef9c6f903b8ca8f4791bcd5412e4dceffe47841e4e0f3423e07d7ffbc907f09f19ab07ebc26b6ab71f99ccf969632c2fd3af7464a7f38c7bb29a2d91b39399e8dac0636e6b38dec850fea8e0bfcc9f9feaea9abb54be7fbbb2dfd1af2fd9d52bb5cf8fe6ed6c9e04fcfcf7cbec1313e62be98564cccc35ea1e214cee7c62a2a91a75ff53c37c8133b619e221179f87a225e93efa12a080b5bd81922d6792310bf90f33ccfc2bb70e7903babe896fc162eecefc7773c77e742dce3cd22b8c3738910c9300b37e29d3ba5e84fbe8efc1cf93f2e0ffe9a5ac09694f2bbc0f25b5eae41c0f23f8f490c2cdf870767e4afe0c957c1938f4305770f2fc6b694e7e588bdf79f9742f27f6e5ec7df7870061eef5d1f6e3ee220f76bf3f742fcf7e63ac13caecd07b934f2857c907f5d1ae90501431a53c840a84990ffc04e13570845e1fff3afe731b359a78600cf9bc2661b998d52bb6a683c9b59bb20c4d24609e8d2b8f7427b0123096b33b391bd2c61e9671b590e3efff3adcac1f542be5538bcfec76bd25c5421f4fa56e110e485784d20ad8b22b02114e4839c7521ce719d601c37d31ce0fe999b6f646e9e4a31377768e09651a44c4304dcfff242cb53497525db3c612a81f38c0d520a671b5990fbba9d17425055437cead14809a43f9b9e179c6d68b86d643e38745e08f978af0b85ba987a2921a245924cc8e505792bc885421d928ed30b7da18021a309331823bbc2104b13a840a209303041139c3041134e7046138a9ad0cdee96ffb9777bb7e73802767777d30e04b15d4e3aa5a45b9515e53b6ee08f2d69fb8e1bf067fe905750d057eb3dad1b9d5ed08cf0474a2e764141fdc9a0a89242d7b9fb6c772925908bd3dee7eedeeedddd32fdc1efce818694dd4dd978a1aa54f73c3c86991caa43715d979a2877771e5c92e145664db6c7a4a4a464d409bfe9a81b37dfe09c54feaa4b171ba22007bbd50b55a5bacbc14c0ed5a1b8ae4b71aa22b30328187883c65c9cf84eb81ecf7186bdf09b77491793d200f6ef4e46a29efbcac16f150485dfe432505130f6aa7a13eecc2252a5d4d3cd0423b44c7f7f4e928accf0011667a4b164021770bf450277cf6e26a400bbbbf0f98be55b94b0f4c10a54d8ccfa7a581d5291f6347e8b1433ce39ad8f81e7ff97051b44a03d52a6e41294c012062cb12c010b962d4f60f93e83a8041de00904d63c7a7919da25f17c52c6a684212846b01829010a099e94c004133f58827581a4841814fde088050423d40b7a2ceb1fe7d3d9605c40042e3a68821374d085e90a98cc93a51862b9c20433505c31c588a6a837af73aab130b3dd49c1f2dbc848b705fe44f88383e5774eede2e203db4edeacd16cee6a5d4d0b2cbf0ba72060293b31b0944a70a707169b3b25b0fcae08eec82b3514e6af49a32f9f45f3d7ac39ccf60ace35357f754ede922f7dd66e9842d4eb6ade5249b1fdb9ab61d915b54bbe7c9aa259a5abdbb6d55a2b7c1666367271f35abf9b5bacf56badb5d6ca7973bb74a8d6aff5bdd65ae753ad0e63c5fcfa3048b5bf46982040982e7085d102d75ae9cf59ddea56b7ba552965e52a57396ee3b6cd6fd7c56b12eeb977ae56ce721fd4b30b4787faab8ff2e6537ff55535b030b5d675b973776f776f2f6360087b3724a57cd91788385d6615c52bbee58011d705a3ba218ce29e300a95ba51a0bebbddf7f5817b0e1861bf999b8ff23ad4fbe5bccc0123ec5fb33f7dcf1c30c29b97e9f464a511da9184657956b584e57a3ab151d8650db8b3a586bcb57da706e4d99afae412792e25ead6f6a3c23d9e6059f389e5dea4970ef5b77ddfd9854b049a6a409eedb735e0cef6d5cb330a787b2afd6db1fe8ee0ce369ffaebd2049042b4c85b3f6636774f30480d2345bbb8df1e2609a60af803f1f63062c01ff95bf75484e7cfd94637bad1cd09104620e63e49c4156256add6426751dab7ebd2dfd65ec7c2ccfaf6dd97a6df09ae1b85e16d8361da845084fd530f85e00f0ef667f28310dd6a0536f53eb58802c367fd64c22895ea55aa5984550f3fa82deb27cf12e0f9530908f480c8e33f7f4a9946aa9fb5d54f2f6761b7cf73ca349a526611dce97f40049a4a409e7e9f25804840ad3e75395cbbcf1d0da77ededcd1f06c32049e1c908227078cf0e440119e292f636008cfdc0de149239e4537cf1aa6f3d24f5d20e27cee53577583cacbf3299e9cc764d6f0f674fba0f62a2fbfbd7caa5f2fef5f5f6e4db7bce27a2166ed48c2fee0f5d09fab96b0fef24e25b8d3bf2aaa3ca8d5d3d0d284b58881297d99a794522333a61943b3301862d17205670bf8ee04eec8018cee113fae95c5eaa494124277e936a0bd70f7c056ff0bf0f5403480f23d42b31eb8d3dfe31ea54b9772a27ca2ea265da2ea466710a4141b74d46f70db36d443196bba72357a4c4bb499cce7bb40faab3a38c090fe82d742031cb5a8a91161afc9b03f4b0b142742dd23e8a9a284a71b68e932c61353aa0988e8d233bb94042e3bc470610d2d9220c3f2e78e21481d240c614102166c6580661cad1185065c1841250742b22e81a594584a29e511ae602035220d0d0d8d60095356a81e74c1831f3f805cc8ca500196f26b194d58be6595c1042cff8d60032cff332304587e4f0fb07c1f690614b0c4d2840596478882e512135a24131c5ccb182a41ac8c121861648b190b082bb082cb7f5f6031d433d4458f920b2c5ab2701a184af33bcf4200f1f17ac6b001ab219b83eb3ec7f5211f32b034a2261c5b80212d04329cc5494585cd3e84c3f371c5c61d57768be7c94ef8c38323158aef317fe1f8f88ee449ac8f3336922a08245b0d17b1fd45fbaccf01e675bc5c1a1c5e7ec7eff09a746838c9845ede89104be6469ba7e3b693b7e2b36e3fbdec17144abdeacafe5232edcab5bf94ccdb76e5f8b9939abc7cd39aacbe97bc157b668384cde1d174ef81d5df7899c7e1d1749e0d44ab7f790fbc7ccccf3c93144e3db4fd759e4d122ce76521228781886d2060f537be550878f9980ff255ed58b638bb912be1f833dfcb5c9af843b049a8678b30a024110323569045e8c6e71e1c619310148af18e780bc6c68742f276acbfe83592d75254d12f282464a3c5e68e61d91637823338732338732338732338732338cb700667b20a37824b1d7443e544d52f4bc8b0d3a0c6844c88e7c9b2e00f0fde84fc3f9d54cc7e61b3ccca0c9b927921f0cd4ea86009853ad8752a4b2b4e9264b0610138c67fc2f2e38560e0b96199c2d3cb426026292ce58b8c0854bcea06ade839b70937a89af18ba6d0880a8d8842512669646df506074e683dba7b7577f76fffa0943c5070d323a59c1142086972680fb46a4a17dbe5a45bb52c944fcf0a23a028aeab3a33540daa74ca82dc721c97a2421bf7af88d4d64776a4421b84843455b4ffc18b8a6a4542faf00d2e1491789c202255a488b47a912f2e6459d2b94ab72026151a92153f5a0890ea4cca28b0e0454a2951aa2254ce10c440f0033c777c11046799274b5880e00458fec69a73eec093fe9c73ce392995a9c1218388f664092c69434f60f9f3fbe69c733a610c3c5578ce39e79c32ae2964c192076db0004b1b4e60421ce23346e7851490bc58e3c91a56ac81461720b03183290c4d6104587ebf38c51fc346e749066a8c00cb67a9a105cbff4f0a44a4bc783a2209246dcc9ea69400c9149c60a6d81b1862d9010f3e0cb1ec60c99d707c2ed0a9084e70867f01275129c95f6ed4bfb444a3cd663219ce363ad96ca3936dcfa5f4179f690519e89048ef804f1a4f32504a297d02d11a4db03104050cc410a2750598521c98524a29a552862155520241430a2144c1882894118526a431c352fac4109c31851ed4e09081b30e102c873c418a27b0018530b8d8000d29676c8182881b4e58158658b448eac1104b10acf8d052b380fe1f4d10dbe0af9e9e207f4def0aef0afbd463720a834527a5132585227891c41152c00222cc30966ab02d514125890b8ab0851345465f84515530c676041b582c677c71861216ddc42d65ad1e4d12fffa46fc6b93592375da1f79c0fd17461d7410a7fa8b52244b7fd67659191655cd57fdbc56e565d5d75a3bbeeab95a3fc851b5d2f9716e8442a15e5e5ede487737cb2658f52f2a959320f27413e6eaa36eedefd7e8204e5ec90b8576f098c2ca7719967ff794d28eb60a21298d51a8d6dfb64df59dcaa349d2af7a23fdaa7782519daaeb54ea72ef04735e3442e4a952dc7c952d2060411337d8c108a2d862cae4428d2352be002307568a38821735054f2011e1193b8867a800bf6088e50c249e20194722902541892c9eb002055c9c306a82043ca8520411b21350349e0793ce4921f0831e5c71c414626461c3a88b0dcc202376840b981082f9c5925ff23bbc921d1c0d61253f86152946b0fe3cca0e576ca66075f81d7cfcc0660906f6f7010327104182d1aef85d13bc82828d4a98947872a9043606f915a2b10e0f554574f0246d872b673fa0606bb0d89481450b5cec58431e35e01df0eec25be0fd7b4abb5a78ff366a57cffbb79476f1bcb3f0fe1da55d3bef3a9ff33fde5bef1f9fdaf5bdfb785fe15d85f78fb5a676f578ffc8d42eeffdb6ebdf6fde3fd2dac5e3fde39576d9bc7f9cb5ab86e677bceb78ff98d42ed67b8e771cef3331ef37de61a4c422d5a7de25f0fe43f82b02ef6f83bf20f0fe47fcf580e7e1336462c067e8d4c3e7964de07327e5cf4df3e173cf10f0590e1de0b377c9e1b33f19e0b373c1e1b33b15e0b36f21c067afddf0d99b6cf8ec4c03f8ec5904f0d9976af8ec60d0f0d96909f8ec57927cf659003ebb1292cf2e3bf2d9ab30f2d99366f8ec5214f9ec48327c762b00f8ec31a1cf4e057ff6a3a0cf3e2586cf6e04c3679742e4b3170df9ec51803e3bece7b31309f9ec433e9fbbcbeb733fb93e3797209fdb0988eb85cf5dabc0e766a2c0e75e62f2b9a5f40b4a2aed0474f9b84b2bd8b3945260ff2ab0cb5e8044593e71f5809ffb021eaa8a28e083da879425eff02ab99907ace4e30de27f0022880bc485b0f5c3dde1a3ef7097bc922bc4c44b962c5125bfc34395921d963cbc3e4a96fc5c1297f830cad22db94b68962cd9c1dddddddd5d72c1fe4b76804b2e845ed0921bdfa362659676d5f04a9e0689005ec90be095bc0eef2f97daa5c3bb92f7974ced5ac0fb775dfc1f6e1327423f2cd1e177f82517f6b783a7c34f550d40bc9297aa0128f9580508ef0351a83004fb831012800fbd2a3f7c90ff70a350f24a76f0b213bcc3c79be30e4b3e46d930c60dc7254a94e8b0c30562c34abe554574f84e87abc385f00854f2539544c9435511251fd43d6b1794755c6769577dff66eaa6aef516275fd2e14a2c97325cc24e64e302ae02ae925b7bdaf5bd005d39f0f347c075e10f705bf81c6ecf1be0f23c0e97852fc0dd79025c9dbfe1e6bc0df7c70fe0b65e00f77b1aee0a9fe4e27c006e8f4772bd3f72ef1bb9ff33dc9b2f7279bc0cd7e601706b5ee8d23cbe3b3ee8eaf8182eeb61b8399ec8c5f143eecc035d99ffb9312fe4de789f0bf3af6bdf755f3ec85d3d90abfa176eea2b7025f014b811f87c19f03e5c077c0f97879fc06dc033b910780bdc077c02ae118855f81aae11887d7c025e3ec9914416b809b882fd112087b0ff01ba60ff1c0c808313f62fc016ec4f801b9ab0bf0d03c882fd05e05fc3f5a7e1c2fe925cff005cd81f12ff2355607f23d77f86eb5f44062bd81f0017f62774fdf185fd055dff18609082fd890c018261ff1f22ec2f6408fbfb5cd8dfebc2fe5c17f617e4fa03b9b0bf172eecaf024cd89f024bd89fc9f521d3b0ff0492b07f0fee04ecd1545cb87d0477fc5bb8dd739be7360bb7776eebdcceb9fde3f6507ffead1b3f2e373add887363ad3fff1e3736f5e7efddfb372ef1b89116afd4dc4873e38e1b65feac1b73dc88e3c6197f19ff981ba9f4e7374ee9cfdffabfdc28a5bf5814a3dc28811b87bce51f813b84b7fc21701f708f78cbdf01b7076ff937e032e0d6506173cd91fe6840c0027878407ffe36633cc13e10e34487fe238c11c509433acc38630a38435995d9ef97490d864dd6fffde503e1f432931aec1e1b2f83b21ec0581dca291386b4584af9d0a753ff5cfda7bf0fd3691482393e5c5ff573d70bbb61adacfecd9393c9d7b021841feeb71d04f9794ecf87e941b1fe5169a33438c1d40b42cab6022a204714eb7f9f587f19973c76951e31ece8b2ba5debad1add7dc28d763115bb63bbfb3c72ff1b176225398bb42c5814cd700b15d52a13387e74f5cb22f61d7aaff206ec85d82b8e2a7f4e62f9830a9bbaf17126e68ff84b87c762b172588b63f55dd7415512e983a45988c9753f6f1763335eae3962bccc822d988f1e0e0f6605f3376e76883b3ae5ecb974238b232c627471c538d4c972cc82634b98141cf10b7a8dc0e8f9f4272511b83387c09d2938572c7728110567ab040c67c0b13fe1fc71d9b033b19056d42431a41531e1ecf33e43e08fff7c22f0073a0ea6e460b30fc647da952da415c9f0fc21787ecfef3d290d3c3d4b121a78c28c43f32750ba6d946e1ba5ae2024a6af78446f10154f8f68d0707fcd67895d538dc4120e9c65a211dd10f605faab7a2bd2873edf7a2bd29eb4bbbdfc03f71baa7e906f57043984a07ce0a134d99ef3aab7e2530f0a842d87d65b30d383b00528342f14eaa7f1cff227a594524a9f081f21ea7dc049953040200518a4e0054fbe10f2a0482f8af668dc4be2a98800618c4334000c5a124a4680886d6cdf3737c1f32bdcd9c93deec19638de44b8c75f40071960e37b96f77798020fca1b1e3635343b74b072e0989189b901635f56aa54c7a1ea46a7f46e29a3d04929259d28cb92927b29b3521a597deadd3dd81fef95fd79db07797785682fc592ef24e83ce5533ef52cd3524cf26805478f5e66126bfd1991d5ff8606367eab8cc878ac88a44a515495f865e5452bfdd1fcf5641feccfdd0c6570162dab57519611ffc9ba177f58b1eed1daa0f39ba5aa489681fdf99d926ed56f9e568aa88c483c63708793aa549752a954125bb8e337a78a58231257b8f30293f22ba5941d8451c5a8607f7332f9b09433ef373f8e9a1c39628e39938ac19192c9919a51b166582c964ccc0d160bc6b258ac17d64ac562b17c70324ec671d1598fb13b469793a6aa1bf951fc1c28ec9c54d22a534738fe262363594cb6d69aaab19a1ad6d758395074100a5bbfeb4ed5982b67735445dcddfe2a950385952f352fef2f2a2335d85a18981beedbe6eefe5ea6cfc4a3478a84a3076fa2f0c30aecaecba162a30d30e2f41775308c1e9216a6898185dfb90f841042086184d055333894a24ab9f22dba6ae507b5138f5104394d74394d44f76eaf3a55d98588284a141ca54c29383ae1259030cc02439a942db8cb69c2e6b88494b3029beb15dc5fafb4cb91ba1e51919fc23de4ad4e7995d65b521f531f531fe9775b58dab4297dd95fa4aa14379fd2344d7b493e1cbfaa284d511a3b1447fbe977ddb4e3a7babbcf123792f849beaef374fa6b8fbb352d65a3c8b090ce2ab290b284bb772933dcbd4ba9e1ee71ba2d6cfc5afd5f1585ca62e9c7cfd1c2e60f6f342a1a9597e4c35ebf6bedbc0d5e096f85c1d91ea5d3037fb8df9c30f7b905bcfdd72ecea349f261550dfe1d7769b897df79349c97e4c3dc739ca75373dd838e2203679b27e836601b43da115ca20f6d818ec4239431acc2f17bda055f3045b4238e9ebee888295243518c700662717487d5231c83ba6d96b01d63ecc15b311a4518f8fde4f16b357e0f317f35cc5bf01b478f4849b0a2d905edd8ddb5c6eebb7d74824c8704b675486023091a771bbc8e6daeb02e858a1fb5aba778015f92c95bfe4d833ff47d32012bf2273035e6d0194f5aca98b11b3fc6dc47d113621eb52b6257ad9ea4f9cba374cbfdd53d9042833ec4841a4567c0b40c95819d69084ed8db0b3f9acdae4c5aa57254abf8d41a76083609d1e4e0011ad701b38f9d988067d1594dd8ccc239c2f6ac6b27e02ba2503030b98f706036f711dc890d9382fbb728dd43a8c934842e889643f21b534ae79431d76ed29f4f9f3eddbd5d763b8c31ca15843bddb0a18c4f9f3e5d464229e58aaa8a00113755912e821042082774afa3f417bb1be6c4c5060ba408649d9c4bbffac869105f8de471c64ef530fecbf791bf9c4bb7e2df88f63a52e489df48fe721ac496d73012545e76233453e76527028e4b282ffb174ddbbcec3ec03d9b5ef62cb0752f3b1548eed45f7c1b14dc2b6c10d2a8615cfa156bdea23010411646e8a1284f44a9f80df357c38a1afe10863d9cdc88312de85cc0e202172f38c1c697170cb11082d2b619a103666b1da0e33814c775b0e3d68010c215a8d8a89c1042082184f5f316747777dfa6808ab25dc64a1b61f74d16856393addf64b76fb2f49becfc262b5337e8a44c94843bf02b8410be854d57c8c184104a192b13e32367e7104208218412fa48296b0f8639451173184a5fe10690260987fd23ce18c0eeb565819d334aa3e6bc1dc7d5212ac7753a6038f98473ef5dc721319f882ac449a74c35c7f4e260836f44d02b43100d47e4101f227c2447040c32bd19638c1d0a725d773fee7f8a875d6a25654febea40c9309141d58d4e8e8b52373a259cc90d1523aa6e7422019d1491decb4c118e27505ce54620a7dc2a8aa342deeaca71dd7316eeb435a2680e015bfd5feb2f4b878571025f91297ab10916133016a1fa47e27064ca8962e3fb1351855254e2094542713ca14828f4369e502494882514ea0111dbc013500111710f70a7ffc50429217e80324407e9df246ef7a0d0bc318382e16eec18c25b1f8b86803b9d52a95236f81902eec42eb5f291b1332a1f7fa56074dc56cf2b22d98a0473311ce23b2f57cecb1615999022135264426262f281734462f2d70660abff494d04b85f095cc40c5c4e14db0dc161d410f0074219e287803fb068e6a8917ae00bf51f0bf690bf545e0402fcfa51d01569dc1de207181dadfb2307eccf65000504c7db370cfcb6f9dd94b2a5945246c8ad24852fa228e8135185502e23ef0210f0001e1ccb4bc3111c36950d3e4486a07981f4f47073e55e9444b084a260d243c3209372407684cdada8176fc8b933bf4325b9698ec5e2e04f9c7066557d6450a5892d2621c34466c6c75fa89eaff597a5c3c2e038045bfdb108f88ab49804134de08e553027035b372aa723d1fca34d8bb448eb700f6d99919dec0fce20cdd84005d6c90afa7bce21f8e32f918688e28c5ebf44823f36516018d290680143da08a6604be384edfa1c2d6e9fb9272f34b8e3f3c9cb10dce15e30b2f3890c8d76648fb29cc18b1741c4f9047b86b48a6990c4974970f601474f3ac1433d8720163cbd2c672077cc1c4915c406cac0472365346ad7942bd80643ec5dc89c260225ec3e9d6822d0824202838663d81fcad8aa1aa2c71444231a9b88eed65de2e84c61a7f7e9d86f667334c2d148290819f4807eb5a00cfd925d5ca7a6464626c3ff7a7c20cd0818604833c2087b7c181c83fd6bf42011364bdc9149081dc49191bec929e7f4640f0fe0e10436d62aadf55714016c350f4c3c4c96c93259261f261f261f2658ab081bbbb819116511ae5c2583ce85606f013b6dc1d7c32b38aafc0a7e7fc513c02370160316193a7db52ca7680477fa5b300b270e2a8ccb1740c0f225134c0638a92a36d7236ab5c2c4c402b6d51d18c92c68b488222ce159c002904551b720ced5d28a70929244548200c88a6830268c02804512978419236c0d8658c288bd30c4124693b5b422a6501dc4705ae38c229e30858bda441a2388a143d8a8448881e54f4b2995c21946a4a0249105142f2c1a48c444170d11975a8960f0b05a2bc3aa99330a99832116252b6ec870b4a919f9ab93926450b320867d8f756b98c0726da5bf08838558c86086fd1b897f23f11abaef006a7fdcdbfe389ea78a94c47d9c538e585082f522cf61fdc50f57b08c8c97650c4b22fc74e386979d09cf92c0b444ab025beb6537c25cf06a15db4296846405a71a8697d270aa3135618ef37294e1380557f88493c2a0cd64944ea31edcc3e9581c0ebb2bf278ed2417f0c302bf2881005b2e99d0904588aa0038c1532ac9198e8ea14a05f0e55380cff209027c9651dcf0594e6143dff87e5703be7818c067f944009fa5086af82c95a0a16382005f1196e4b38b1180cf7e04249f9d0947dace00be229291cf8e83193e3b16453e7b0f646898eff723e02b2e01e0b32f21f4d95590fd89a05f6981af1e8ae1739f01c3e75683c8677f32a45fb480af8ee5eee2e7730721b7183e9f5b09fea4be9bf6fadc2e707dee19e4c6019056b1015ffdf4c2e786d247b4f0b997e8e921c0971f79cb732c8385cf51cbcee77806d6e9eeebd845d422e773b402f7c729e0cb67de729ce802dcd109dc5109f872a7fa8deaf139468182211b100ddc4f01e835b00c1e48e04bca98c01ff8b33dcd67a84424c80e96d101831b85fb55802f89037fe00b55fbf6f1572d80972b01bc1bbc7c231b8097ab00bc6c6bf068804c28a3245eae01f0b245e2a18cb0ff112fdf5427235eae3378d9164172c2fe3278f9c6eb0c005eae425eb6d80bf2f28dd7a318bc5c61f0b225e265d6102fdf6c4f405eae3f5eb642bccc7ac2fe3e319abf36dacbcbd5e5651b840684e65f5ff072b52e782df478f9066f433c5eae76e74867e64efea248def2cff172fde165dbf2326b09fb7fb3eac3cbd5aee065ab028e976f30854999bf26530f2fdf785e665da337baf172e5216536522665d86792a36afe9a345ebe61b1ac95358797ab9df164bc7c8333ea868f675939454e8941c9c858a2a1664f4f31b7f10d9411ea062824540c75e4603818304ea8249e9090e04e6c6c6fe36e5567350995a92e559a47f128f5e88a24a458ed76977aaa4cd4a20aab434a4a3427d49896faa88f54b73b2eb66db1c9b6a40da94b17b813b7a1ad89cd682bda604b4bb7715d4ac3a9c624851428242c3359522c8a4570a77147298c56a147b4680b13b8c3d4c693530dce8ec09dc69d4c0a833693651c8c037722aa0677505a38980cb82731355067e45c61fbe56dfc42d586c047bd30fc944c0d0e104a16791c48f42a18367ed7549397990c407a1d7560fb6bd86b3e2899d3b037eae9051c3fa3b2e8419961670a08fc69225ac02e30cc41308c28336cfc1be20298682460c2b6bdbddd614e0eac0d3dce62c1242fb87fd63d7cc14ec9cf8ea5c36fc26fbee0f9451a1349b8b62b3b1810621d2ce34b272ccd0c930837104e30ec0ff6e761875970ae38eb60f974e2e9e550613d3ab984b1bb5dd5f352150d4e66f8566d2528c2f33b2881f820a05f4260ded2aab5acfc5fcfcb8bbc35ffe53dd62e27dd2a8aeb52aad58b8591e12e466606470e968e1d3435363c6efe6effb8eb918a323cbf870a490bc72afa9b1d4ec44979acd6580c09a993a2bb1552426e47f3d67c98db2d49918484b452410c9b3b58076b57947e09f989047f1c1be998dab5f2aac4264aa90a4fe98fa79452fa4ee93fa594de3ca5de534ae97d4a6b9e524a699e521e4f29a5364f29eb29a534c753bae329a554c7538ae329a554e629a594c62c34e629a5947e4ca2f129a594caa794524a29f7f4c6d3d4534a29a59452fbaba794524ab7a7f4e529a52fa32a427156f87abc0a3ff3c38ad6e7630515703ca41e5eccdfc4bcbfe1ddbf81a949d1cc9a6635339a99e561a35443f3c2cac1cab1da61850e225a63d572d45438aaccac89ce66b3d4ccd631a596a15d8c157588058371d10a3b51ac5ae5b85a6dd65467747603954aa51a2b35ee6c5dd9dac16c1ba54adb6d6c6bcb15ed7a4b3bf9cbe7e7d3da42aec3bcd5317d7c49a55fbeaa49b17a79d42e9c5fbd5401049244adcfb209bc7a3905fe7cbf7a39d42f0983c164d1ca871a104852717df6359ee0cf4d967ec958ac29b625c665a95d3c820081a45290cf1e0618f0e726f54bce66b2d96c350308249b807cf61ae0d53b12fca1f9556d55732aabf7a276edf8d5fb11106812bdf0d993c0abf728f0c7e65f13067b82f910cc615bda55a305024d2a2e7cee25d4e04f8e2bfd9ab11818b12cb1a659bb70680181a6520b9f7b0b25f8a323d6af399b21cd9256df54dac5fad5b713106836f57cee16e055ccafbe89fa356bb58e526b293d05afbe87da55d980409488e7736ca30bfc9961ea1785c16a30a795cc102010a5c2c2e75804bcfab8047fe8af682ca614bb120303affc571fa780405429f2cc9dcff10678f5510af873e3571f8dfa456747b3d8aa7b9dcfb10478f5510908449b84e817ad5d8870149cb37a0a40a08d28feea7f6817f7ab67027fb69a65b2a15f1bcc5bf3c7ea9140a08d4ae4991f1b9281e0d5ebf46b8bd1d02ed4affe08fc91bffaaf5d2fbfc2671f78f52befb3e3d5ab008136251cf8d3bffadaaf6df6b25d30bffa0881b6a6c833edaf7e055591950c4b98bf3a246fcdef21612a4849250275565c52c6fcd575b2939d944a11a88b059172e62f8e939ce4a46c8a405d0744ca9abfb85a67b2461481b8a61788f0e46231580c16835189409c920b54f0e460b1584c290271545a509af90b55abcd6a73367f3645208ea86736e1899a796b7eadb359238a40a8261e2298bf50b1182c068bc1a8442094120b54f044c16031582ca6148150547694f0acb5daac36abcdf07cda148150443ab409cfa7357fd5d98cd666b436a335a208549b7288f0acb1182c068bc1a844a0aab4c5fc556b6cfea614812a95b882129ebfcdfcb5d56ab7da36c3f3716ed7d414812a51e49992c8db9af06796d73c4984e7fbf7e203a9b3b282a78287d3c3f3565e6705cf57e166c50316eb60f371aeea6b1dd14d8d47add611e1f93d68523b664d34b31db359139eefd974355c4c89868bede0625cece6e07070308e4a0e0e868383ad38184705cf7f1d28568d23ca51c3515bd538223cff26a6d65953cc6c35abb3263c9fc7cc26838a29c5a0622b54ac7a31253cdf865247c1a8a06028180a4605cfafb931bb5a4311a5862acbd61a8a08cfa7a9c24a9c7da9ce9aea6ce6b3263cbfe368b61853aa3556635e63782ae1f91d6a87f49f1f69fdcdc61546e5330b8627153c7f63b16c57a4a35b69ab55a208c4cd22cf7c16cb6eb54a84a712cced9a6adc2c027565409ef97388e51d9319706772333c9fa59aa1b1f5727260656398c4e459da156d4c6a17ea5faf9f1f95e22c5ef95212e6adf9b26330d854cdd01d537ff3eded9a3a98bfba2ddd9adfc5765cf084754c70677ee39c1cd81457c3f3b91ab785736ad7f6f33b24871d12ec9090f0fc98d430c8ae95f25891bf3a26e845b55acb62fd841b0c22629efe8ec95f1d6cf5cf8e09311d9391c7dbcb95372621c16edf3f57af9f8f62b2b9ca2b7a41c858454e4a76ccb63ba62e477ac79cb95279fdd4df7c9fdb5dba31e7a1e6bf6eaca25bf3ebfc9ebfcdce8929b3de65c76464c33edfbf65aac8858e45f0c003543a8b0a3a4b672122e285e6d38f41e2cf5b3b48fc0e8cec5670ff4b0a6c841746e9cec4ad6088a5eb58a4310e4e843bf1a5089036ac7faefdd50a1d62271d03a2c6a8b4cc76554d512a433300200082028315000028100e0704e2904824ced34c9a3e14800d779248725c1a0b646990e420c48c41c810400801002000223233430300447929521f7f27b62e95cbc23d10808e96da37eadfff254b85e6c8881868b72b352108cef9e0430bfa35f15529cd0bedc00162a45470e37f2266518a1da101933a57083c29fbd1b89a837fb5c00f885993da36e08b0d96ee0f9b355e525721719e0397b6f81f105f926a82d01e2a20c648911bf7f77f37a4c2cc1adc640edc9b0d907f540c0138cc85b39f196691d58dd8e04b8e5a5ecc5dac0ce1324a9a3ae2cdb81c6b495914c102b57dc5e8928616516ed198f6ef69c2b03f227a10d328e1e54ecf786ae08c93c0096f12584861b62474bae4b86459c42b77748ec474d15af93638391a93c6394862a1491d456602e2d334ec51613daf555ddcb02ecab44436e56e0ac291c8ddef2277657c3d9b23f9cff8e29e38564220ba6a0a882eaf5ec7fda44b1e2d68e00f79eaa9a7b63d63d740593ef0fbbce3fdbbde8f7e80a08c12097e343678fd9aeecf8807a20cae2c05a520b8e790bbee01900a36744137c0afffd17d89d8f537ba0f1f9f602b1142e98db0f6ad2ecf6a90eef3f6c99106edc6819713aabaff5b9969d2fed3d41c20bc0725869a2848aed29e7b71ada6030826425269c510fa1edd5f8b960b398aff5d3647119b73ac2f3c3db4a51e9ca75ebf89d9e9c88d550eea9eac45a1fd41a359104e1b093ee65b6d22c3b728f33192eeddafa4311a9451d052fbaa4bf1cb2e1ab8037ba2b9fa8c7bae8ee12aac05c2e03840fc00ea68f280bcee2cedb2bdd3e602409fa4d042b024e23ccaa207fd6645a0df15b95614944b542843ea89a190b67d0905c30683d5cf0039c8ab5b6a009f4a503ab7b177fd4e0b738bc6080cb4b3f71aba8aa2aba2d5d0f6a0747183b04631ad60222daed93c927f3eea8450573dca223c2cb706d64bd9f550767e81a12c58817a1e2a32e6c6be1cc8657b28edd5dd275ecbc28f5a120bcaed84eb0dba87fa5ee7478d9abf025a7f4ea68ce674a5ce4d99ea487b915edcc268dc0eb5337e043f5130f713d020abb7f3bef0c7c586f91e1cd21754a06cd396f26acdb1347ff9472dcad96e582e4332a008c7111c0e6e832c18d27337a6f8b94528431378dbddcfe7680f184de73510ee0c938ee62817a1a859464d1e5e98cd973fd881c2bb2351260aa7beb11730d54bb3d69a75fe6a339c8eb49dc7a4e6d2ee09c641cee9bbb96bec40064bd92500747db9750dbf341ee8b96dc5d8425146a6331dbb0c6debb75f8795e3d5e09a31875e4b4bc2db2c165a6ebd81cf392052479714e4256db8ee05f23270a4abba8bd823230a4f9fa6c3b4cec57660a392043a84a009f4f039400e7269faadb09e5e76e3e5358f3d4ee831de461f9779f0c1cf6023010707cffe3291720cc5164daf13e01d4570f6b02a78f0acd20daa196d56ddb0a8afd402eb21a500a6588fc0fcf45876f12700803a39b7d5cfda5019131e09cfc2e47898a19d26250cc60497667c1848fa3228ea73dbaae4d3513754e46201f5f00feb777bc5cbbc00d800cf1c89c17778a91779cb1b789319241e9698ac6ac9dda63eab7dc3f695ca94b5b5aa635f8b01af6de65fb5d024b80ed8798768ffe5f728d9a0bea15054e9b88c9655ecaeb05548f8f596d58fc634a9b6325b5799ebbcb13ad7134ffadef6f1445f7196906e7fcd4b8eb1aa2ed036ee704b2ef065e8e22733b1179b55b59c78906a921250c08af0623880d7ee62f1a7466b0134502a9c1c97216bd09e1e124640b7ed881f993954402fcc98a5608c59cf0795a165976749759e6625b57d2c4f5d8b35d7ef163566be4f35d0a556afd354df6534ac82ced5bb23263c088be9675aadd5b07fb72bba10d6c83425c15274ac219b5bca74eaa55515a220240f7f83b3ee0b2db4edfb590ddbf454709fd96931b5bbd635d718b477b453d25b8a2cb4baa80e61b90efd830e883f9703b1b1563b4be51012d79eaea03352074633ef34c3e1363bd7c121d3f9462a06343c01b37beefe8738b6628cf315ca89b48f2391226ae5d95345d9d879645143971e9feef12a73ee12b41ac22f96d9abde959a36e1b5020c3db78a472d28e8ce9295739ec2eda7ef1e2e7444a3363b5944cf09bcc3785c86cb4f93827f95f3af728d6c0f7da15ea2f11ad3f0a9c61221349dcdd903c4e6090f40cbdb09ba3b3a1fda0f02b01b8302a3cfc4fc0cf75b0b3f4dcfe350617b8844cba399b0a345a759145030f45a6433e62938e5917cd4661d485dac55a644421e25004975c4e2c1acd132ee382057cb204ee9e824ac7ce4bd0c3868e1936e9528f4d19b38d4b3b43bac5340327755ddbbab49749db97f7c59252221bb3a0c823b9682074c446773d8752bfb44bb10d977b452018ef7451bd1ec879e40f6ddf2672d07cc87aaba06615a41a55fb95a5de6270a021154f8840e647064a69f02977599a660003e7c9a702c118ad5ace0d5718e68c597e0bc3b9ee9fa1ce634bf7d397ee92e57bae504ffbc698c4c46cb03479469d86832e7e8eec860861a02e0bb1fb33b2180cc887a4b02b05fb201a7a609ea2ff3fca5813293602ac33fce79c57393d8f0a38caedf63ad84d5b44ec5eb82b6c883e344ffd7f0d27a2f35c262fa6197f6951d3a6f985fb2a2fc98fdc5912e281be77998ecd8f7dcdd7902b18f77bd098059a4fb1ef9cdc09bda7c1c20c4d42d83c68d166f3336335aea0a535ce41670e69569a2f5a675f06c7a4e7698ff9c0af32428ae65dc39b6202f47407838912ccdd213327b0b4be4a194c2a9f514ce9b9edb099c4125f83dbe1c39d433affe12cf2dbfb3865553dc0702f583c192591113a09ce4219990ae23337565b6be0cd695c9ba325a57a6aea3e6cb2c31838820221d99d629d37a655ab70ceb9769dd32f4faec64c8437d062935179e8e9967d205bf688f949618d70e49b11ca41c8e007e6cd12c842b0b6255b25103f252b06516e71a161bc479cec759e7f19d566ae42c8bb71f81fad738fd2e91177a300004c21f7f4d2d33de9ded19676d9ea626f4a8160f03444d146018a21f4d9cc34d4d17279f74806adee168687ddf3908fe2951cd301086e1dca383ce07d178fe41e71dfc7bd40ed504ad86a7a4735fd7700eee3fd9d9d1988bb76dd150ec67569be5fc8f193b8672eaf6aad328f9e74e3309ff1e5f872a8774c353d2b1af2b3307f79f6ceb68ccc5dbb66828f6335bcc72fec75c1d4339757bd56994fc737d99847f8fa9439543bae129e9d8d7f59683fb4ff6743486f2a7d011967750ea0c461da24723792295fc172cefe4efb974f85cbcf95e1f8d4e65e23e33bf53195b73b2d1b1d1ab0e719d26c344597e7768eb18da9c57d3d4943739f00126dee1c47759f939a73946bd44c94104e06e3b5cc060512f4eab910bfec66978a524a699360e922999d9b16110e251cc904fe29e433caef3348b8975198b3f3e28f5ab84c05e1eeffde3c3531a2c0044def4bd5fdb292e50e49ab28333fdc5058c8c51f674c847b8c09103ca1c9f000cdc0ac99b0e90e41ded9dbf201501941c4473cc81308a544bdeb10026ef7bef820f490ad0e4e0de93d00493023899dc7b17964952802707f79ec4ad989400cae46e58a3bbdfe8740351de8033c36a390829efffe822d5ebac932371fde3fee2ee9abc4fb0363dc48118f726fc93bd0e507d31d998c34bd9f4698eadc84959a47bc128776546469d9b960c19f04ae82a0f2c2ebbea7b3571e6cf7dd04ebe239f906d9f4699ceb1a11e26847f89aaf4e357c9758ddcb7934fc0008f55da8f72320120a9f728d0998e32e916223d7658255fce347772d06930a18c38d8615fc3d08c40b836d643049a86181c18021e3c722c92ae68d083eb6a85c1c8d4091b21da879af72e4c689477c011bc138747e5825a575360b9f797e9ab3e485968d0baabe891a9013a18fc667a51750a86e9c2cc26f1357041c9dba172df91536405018094251a37db2d6a81b790062e8a3396fd43b9230c42947cb966c186aeb8f30158845be7ca32a22521b86163e980aeef4550ccbfce7c2a1d725b112c91c0d0b8143e0eeb6e169a9167dcc365a50a042feef1ebda2f4d614740ae5fa503067e3197150120796e0bc9b8732814a2d47d82cf72d5651cf01f1da0c5a6f183315716e6b625b4eda0127debca257605dbfb128b84aef7e8a4aff80471ec830d2d9a6a0cd502900622f9fdacea7543977e1c1dc121e77cd2ee78e297cce9a17444d38b58fe90d77010dab24d1b79f51cfda30f2a81dd6cbe2791e555f58ca0de6ea3b1f83548e94f6f5c936e83af0f13909b6355028c126e6763b05b9b5cdf4e2b03061e264f4b5d31a5e222a19481886e2f31dde144348307cfdae847283f58e497a6f5d9b415416149226faa597333ec6a2c7a708de30da89aef5846affdb49ae240454ee05058cb1b280863cc607be510916ab6dcf316d6666e77db32ed1b5c5d23dfe078aa73aee3290e37c3ef5a73bf161cfcd5d136f9c756c25ca36ae4f3262f1845126a988a23f2dc09decd3e72a8f4f0496333814e1e434f1c69a579b179d0459ece56beadc25dcecc98b4321e884a69c0bbd4611ea8c7e7a32cff59abdaf6409ff14c1bc9b637c6b42d393842ef62d39d01a5e38b19879a689410af1860d018cf61d7842ef8e8e1ff13ff873843686f4121dc4ab1fbbb87c7ca3893162c3120e4c525247584c2ff2c600b335b8bfc0cce086179b042fe87a8c7b121a7ffa6d11547ebb530d6a2f4ade20060c5814c48364dd389fa114eaf2f0c32543044a7abdcc36e08e96a8157e80e9541b8dde76148fcf17110bb513ea09b67b0ef0f3c9dc9c374ff2ce23ae21478805c9014cd1002103a801e9c15f3d882e71677ac34319809dcfbdffa5c616d9280fa33c3c514ea723e02a7bd1ad27daf24975ee5779f99f561f69d3116dbd41884bb32ee9f8331ea21ba049078e5394576688ebb29219c3ae91eb59b3cebc408ed6f223d7bc59a0aabab79e2936eee84a7d7ba35f613f52695efd8bb0cdfbcc5a02b4b566be8e5fb0c42d4bf656ba9376bfdac366dbb8af9d4a97e4422f9e9285a3ce35bf45889b16394c0ba321588c35991b5ebf7182618d9e9326a42247a463c238fc471270a3c49e237272dafa765d54bd162e0973229fa3f44a0a5cd8b52719ad4d926c8e2ce68c0b7fb5a786cb2d980c0bdcee2e1feebcfa2696a98428333308cb74f8427c4e64bf7903cd4beac08fe245ead3dfc5d7ba67832da6dff2b97f9c810ddea13191bbbedd5f3b9a2c4a1bc105f12ef520ab5ba74229edaf9307d065a5e77f60cf65e15b16628c9d6552c10c319ddacbbe1ce47b36f3e0b244aac7b3e21c195b4f838d9a003ec4576c381da2a9d3204fa5ea7eb449ec2a2c990b6fa709006138cde7a7bc5a14fafeb7af40cef19c6f3f06cd90d409b800c59976e080b3954681a4a977c395bc7ec9099efd0aa3dbd153e77fc85b7f117ab1e0f859368dd59b27e07a867ccad1eab16fe292726461fee293cc7129ea6f3d267d7d0b34a60f20dd153555de45279a42786960ab89e488359933cf2b50602164f8bc2606c7af3a59348730b319ab04d59b6d986f70e51d099b0401adc954aea8424f065ec951f97ad216b5695a382f670955351090b1c6661c7b32f63130f3e4d9b51232e258778e98fb7813fb79d17945cd8cf45119328b414a66f1332672e27bc7fc1922102e00da7b507daa71c2f2e2ca41a652117993e2373bca933677fe540a08bde23b81f995436ab2b0ce92d75849170274e317b01bcb1b0ee1ef0e47251c6929b15812eb654694cd139f0e949348400f5372c073d91c35abf96868e686efb68f0b59c881d5bbf155daad5923e67c4fe2005ddaf20cdc3bc4b026a789a9cef8b0281ff01f3e3428998609cc8f07ddd772058da115f9a1d4994fc3307bb37eeeae47622df88fd7f7e6023f79462dfe864cb3ad7b56648c0c3a0f61d70b5493c0afd1c70f413d4bf4a3b30282d84d8a8120cbe7c3569d62109aa4ef6be30ee61e7dbc71bd79437092c99b640f117cb839f5bc2ce5c19841a09dae03f637fb1dca612af8e3743a03d061721501e8485ad9195203c1ac96ef611b265742313508a209e1148104104398c2f86b2b523fc911f6a714c2b6c30250c7fc25706b5c139c006d1836b709d7377997d50828c7520ea93c82f5789014b29a11c28a9f09e7959f168b94d0b42a200d3f496884d33514aaa4f793f831e0b9be92ff8d6cc7faa0a0f1c314aa6c4d2cd376c724f0aae2495859cbc1db93f78ebb2dab1ccf9d7f7e04e2fe6b47e562729423281279e259b12d42fd4c22ff28554398b57fd78ecc74c5aa9aacec82c6548a2e7b596533bfb57b36750a1aaaac0375d7527913a2151b63dd47408b669b2270d6c0964ead2e4eca3ff6940137081a1c8b80abe4034bdb994e34f0a24944e12328dc0e7efe2d936ade32dda2427b9f4fb644899c21514b516ddf9965a945b316505766c356e4dfc43bc0d408d6f4d8dd94030e5979797a2e183ddafcce9512ea553760238c614ddc5d581ae2555a1ac5cba07d77371d9950c590b21883eb76764ac2c909d3471bfc8eb4bf4f681c0ae671d09122fc9755bfd7c397fd75b4a60bd88b67af2fdcd1849655aab4d47d9f6dc808ca302e7d160e47577e6ec396b861fb9982adb679264c6b70de038e4b5c87004d88fd59b336e70e5ed533b5f485186ee13311436b521604f231c242fd6d4de001d85dcfa61a711eec5a19e4ac6f363a15156c1e2e0e38b5e590776658b97459c5c77b7395fb6578723999a26f53c3d0588ac363e6074f08279702915711f117fdf5b4951ed7bce3c16c401b00a991ce13f4fb152d781e1a11c3ff1c47947d50ed731881e5485cadf2a14683c2bb7142e876cbf72d449b028d566fa14911acb4f1f2038128ca29c243293447bcf2b11848e904b8a8ca9d966216a37228950485cb2e428182be048ca6eec6ebbf25d145b7ab2190dda64330dc3420bb7466e65b36846dd1572fd26390f343899b0e69e29e7f94724f6ac1cf8e5fed3816239b00f62fb7a20a4cf2be292609e0a929cf45a6dc971b98ffb89e641ba93200a534003a5880668c6f6cf1755518f31435886f548f5437ed11f23486c9707382d4988e16050a3267dd58f73edde690915b970da8a25dca7ed9dd60828824156e7075351c13fe5e093f1682aaf92d27cacdbfaf7930837b36ea1f6defb121d583ab2ff6c8e0537cfdb1d14095dceb732f446ee17b48805b338e639afb48832f3688e184f9c181ef3c8c6b138c45d069050ddad80c2f11e874fa5e64fe5d803207addc5adf8308f7d2859eec69aea0d7fa87c4513345134a7886b65408740e33981905577d5e202dad4e546a077c8b5377944a7a74825fbe20a8a3439f1957696dec7cabb59781fcbde67f57bac7d97a5efb2f25d96becbc2f759fa2e0bdf67e9bbacf567fd3e903962137c94b404933a9c2e411bbf0e7347a71207272a316e45530160ed985ea60b43f6c3e756a87fe73472a0e034b74e5cda45954872fbdcd4b4fd012c473958fc182b03f43c60273ba7b7137122361d7c6f6fd16a22d176c94a416a2f8cf66a42bef945ff9f42a73d7d05d00f0998c5d1d6ebeec506f3bd34acd9443db81ee0690b49a79874ad70accece50e1a252ec2f0cb797e14f84d3215a1b1d4d22966a6aaa282b4b357caab901180424c522632a216d727835dd420e43a263084e9c3230368a1755090f1c52389bcd46fdd448117a1bf7edc2e05d4f1d6a89432d7a15dc13febc8cb419459696cc829682168396a4960f77540cd3cc09cf495e7adc69acf950c069fa3a2c753cd529b15b1fcf253298ddb7836389d77601d4d64b835aba3f8bcbe00896268111c33bc23d6b8567f50549007fb59c6872b3d68846a7910ac27e9c12c54493b68476fa3bec344bc6754e35dffb639256cfd84ea46b4e0c4223323a0842917f0fb9022d37ea97d6af849f4020d77fa3f0e2ca9e574e484061b551155884a2c3c6aa6e71abb75495a7e7481cb3b5d970186665cd2bfa8fcc5afc005a293637fec4908a5850dd4f831ca244790002928484246ad5259ddad0f2c6c447f0628e06e6f5f83896e4a9cb07799d6c5001f2492c295c1fc9f6cf06eeb75063351cc5ead4927474ba6a07e852b2fbad9c478a2a4cb00e420dd38a97d816b14e28aa77a766bd3270cb3bbfd394d3baf62bb60776404a04f4a900ab353e8be294d6a9fd6c4e3dcebb0ac8dfdd009141802e6289aaebcdde61cca6918d7b4cca64d1c7b77390568a2e30025b03beb82a1461e1808a5048e5aa1622de71daa9f31768106feceaf1f00bd205d80f1e24a79ea5dc4bd0da17ef129ecde24ab85f1b708ab5bd54e3b1fd1cb3560a2030f7a7c08926ed9fc5cd106e6b5715083b1c48f28af5131116dd4ae46c8ecc12b19953f3a835129deb96fe61302b976832cb02d6b8dc71be556b0f86ed27aecb4ab7d19b105e6c666ed3f7c3707a581152a773cc31c6ed86aa30a80b8ef39a38160c58b34df427c0fdcb2d2ddf61fa9cc52fa0566c821c903839d11dd36df67e4f1b57f44f64516d5376442b904f7e2b3317ebee7ee06bbcb21f61886a38dd512030c4aff30c8a9160439168cdb1a358c155178580cd6ef4335c142dd8fa88c892fd116015c09ea031306c9821f62aafaae96a7fabbbd8600a17c2814b67bc01fa1ac50d4767ce290cfcdfd713111c414078b7526f5993a3cafdf66453680fd8cb4f393f32f8a1e7d33a0c3ec657ca5862a5badf9c925b9bada08f81f4b1bdbb792b29fb3ec3e239cb959a42140750ff064385ada9da70513a3b005d84c5bfe004c961a881a28a9c32ddb1a4c0df1ac67581f4b2992a615f819ea3b5828ff9a5dee9a0230cab6c967a4f0088e9ae5b72c2b203ba75ce1ca7e95ca7749c8fcee050f4a8ca6e6cb8d453cb68626a0665867811d3376b623a6f45743cd9f1c55e8d9b934af202ffadeb17389e2252b0f82406cbed18a138b150b2cbf1d7f6f6b66f4c1f02b6d9f6b60597ed90f676db066cd423e4f111dc03b764867e6f3b28189c094bae9b533f67e22e958b51078d7071808abe2d1cbe79f80057528ecbd0f3856c4ab5933ee243a4d328fc8906dff4dd42d0eb85c7eac78d1c7cebd88997e1b1a67d392f94665968d8f7f04bad88abb52330cbf5eab44529ae58055dcd6fef82d7ab124dfcddd2da7c805927a48e73732863a05d9f2cd8078e1adfca11f93b53562296ea67aa420235940e008da02c77d83802fb9f11cba499114f1fcf5de9d720b6f1b0123d1c9ac24c78d1d906b76049e8306bf500133d3567136049e9913ebc4892c35024e47588156b0ae429da2da846a40b65123be1482a8ca9f89067f5b5b34894f8edea425924b0dbc7c263ab5ce2190554cb822b76a6dbc963ff06cbd072ec62b888206bfe617d44b5c18c5d569e2c2140b9cdbd9fc858aabbf446c0d58a682d6bed6095170ebe9cde1a2dfc8dc6c809aff550ab107411a9b0d0730c435f7094ed5dc8fb83bc27a1b901d4941524054bd801d28eb3aa992c750226ca2b4f53dba10fec8295273fddd966221853bcbe708bab255882ec3769e85685f81295b4094d1c2d31c45c582da5af1d4bd90723d6f93473552e19c16bc1f7fe27963953739d88cd6998c9c71c519c8971a737427c06f3f94956d78b882333ba8680b4f2580230fc092520d7832b3d23113329829b82891b15543126829e2a322243e756e18632aeaae2327970d07acc3600e8b9fa2b38a241210d0c1f2b7563983ffbb754361078adefd2773a0520b5032abbc56851e8f587b36e9f3198aba8d03c51ee48cef544363394050afdc2b53fe7d6755170bc4b86caf45f462024e6493eef1947a7420c5cce593a45c8fff3300c4f7cf876688a7967356c6c4f0d1f4f12fae5212eca4c742ea3bbb5297a894458c222047014bc8640d6f7ceafeba977b5a1c28b50c4e6f1943ac253e7615f89be934719f66129f7c3d5042dcc38c989c7469eee0477a0dff55f0a833764e5f722eec1f40e45445fa477194e01644b5fe15bd4ac59981fea4813468b025e516d541e3c48c5cfa36bef4a6de498adfca0363175f63bcb8fdd713e4236bd8f5f16f5075ad82dbff6f73ede5f6e355437e4b99717e58021b34989b86a65c10a1eb24f0fc89eaf733e77ef24f80c1827a92b72652966c3cf6c95884e5039d2c26fd2b069c905c362e2ddd4e2f6adbbc99ba7a024e34ca1c25f141ce20a3f846dc0c3f07ae7e13ca9305f7fddb5c50684eab03e930fd9ce35464bf468e086865a51b0bd36f545e3487cc9602b69da7b2c26dda052847937aaa53d552a318734c88fc5d4b70a13d52c653dcdd28b5c02f1454a3d6b6e04606150ed77bca8614c80c8e933c2cf4d52e8690d5664155cc147cf3dca49bfad8a0ca10ae969b720439732784257089ebda73979c03356b36e0f26d872f1c16cf2b82ded0b7ca23c52c00f61a5f650df4188fad2f2abe1972ed6492fc7edf935c311f9962ad4e1f31384d495ea9072ef86307003c0502357f95b39fb3398d9c962f50053e4c32a9f950b035ee5d206231e0183a415b424596846480d4ce72339205224b25e25a558235ee86887866805c2f48b5de450eae680a7a47d167895a0c84a5510dd68312a1270395263faed76a3149c6fd66a36b870e94f6ae0d5be3afe884e8537f044a74a3c8750f5cbeead4bd550be86ec887b27298bc02914ee8755dd334ee5b65524f95817a9b15b7e7d2f4efc7ccecb61649738dfb0e7a837916996af01420e0094e971f1f7f2ee9e326037ba5a10edd2e886839a2b984631aaff8a5a344ed46ef48f5f93d30eeb6336d0a673290b5e8a907c9274ae55dfe6d6bbe4d804a098a9d30389a824e18ca6db729107275e6b51ab16dac451c7b9f699a203c6d055d138312e20be1baf021375deeacf7248bf34b57803dcd8022480d5841ebd3d1bf3c086153d6ea9248944f199fdc6e24cf923570df05452c7bb66dbe7afb67a38b9e3ece5d3abcf0e3088478f631cfd94dc783471202577019361aec8180e1ae821ab6c23ccada2139d787b9e620900829287d9ade7960196318143e11a44ef4ddbde0f88b96bebdd77edfa81bc8cc77104b8c4cc069d2e0db9a1bcd24d5405cf5de7f74e29128217ff5c20ec06a7eb5cd7e9ede30f9cd40e656e159fef2b502270590c6a49a3e75c6914f6c0b647f8514827e6229fca02ed5d8e857e9ac5db74e92c3e777214512a0945b9665a29e937d4bfaa8d312046e733345c88217247aa21d3291f169e2adb2f2543772ca79a275263497aeb07a37aefcd339f48f7c33c4a37dc25a830e050c16f131d7b2641cf0cd70e0abb4e359b740ceffaf1b202f5eb2d49e7178f8e0e61e9a64825e2dac5a1a0ed333073a99b9613305a8f456ec41780f4d5021db01d52a9a7254f81cd00a3cc8694c367c0052c981744e26a13770db82b906103d79f7be8535a57412555914c188b0f2f828f4f5e40b1a8d618b37f3034fbd4f2998bad8b0190aa5191da84a58b1d5c30688bf797c6cceb4145eb0d358d29cdf4af616f8900d3ff2deac9dea2604a5ad20686ace4294ecef0f9ab47863a51ca7503e3c5cb880d21c54c1fe3c917e10d39733ef02373aa44259bee775de0d42639390ec298dbb97ce5015330c070d2b0c69f49390173b4483f6661696ed53884ad91c5faf1e894cff19ea6849be1df0336559704598e89282d7b28e8eb10fbff8e73a78817ebee5fa3169e9efaf02840cc9df0de1786a52ab778d66ed3eac92c4319dec4ecac6ab7a28eaec6fd82a750c1ee2cd9047987d43536282f27a2272507009904c99d83977824d3f07ff448d15cb7f707a8d8ef3c6610b33e8a380a5ea718138974ae80485218e754a21f34e9dc977c7cbabb28e243e1bf957bca2334278a68f06ccdb1043c99965f26691a8c8ecc98f25e50ab9399ebfdc3716840e036ce027cc3e9685c9cdf2c9ee51de72aafaf513631eb98b50f8063a36f60cff1c5e1387be53d17894c5351ebdea86af28954a50d8bf59ebfca75825a594f031168efc0afed18e19c85ad81e588b9799fafe904d1356026bbbd59f56ee00dc6842e362cad2d27e1bbc956f747cfa01bbc36f8bec609b178aa962d86b7c54455a3c4e65ad8d52aa45d06c41329f8d082349e088eebfef028d7205ba08c5233c32ad66823c339c7532fc487ccf229b4df3f79c870c6b9fae56d583e7a9ef213bb06b531d8a5f9a86eb4dab4bd911fe805a2b9552d15953bb6bedc43dde253282ff7781bf030863ead0fa2a3ed54b96d317ae44949efb03e8e33f82a19b6b5d3c91a8f9aaef3336af892c312abbbb73469c67b161e7292c5978948f84a4f20d1e42a19960b30c3cc1eaf1d691867772987370d987a46236206da0e6f497a935d205fb4a1d41ba07fdf0fd58fe0ea9360bdccb84858186b1482fe2217a6680a45373845af1be69ee2c86d0b62ee9491abc5cf5ca62bd365bb86fd12fc4cfeff7df7c987f6c1276d2fd81fbe26439fb9203dbf1611f7bf7dfbc047f7e98b307b17b311cc42f26b9ec0d8c548cf0f8de04590948c6ebf301af01ccfe5c0d1c5fe40eb603422133af4fe6178f8476e8cc8720e5829e33ff6e9339ff9e8e9dcadc3b6b099fc38e7f168677f94f4851212c7b7f3e03511d353d2abd88a515d9167019573e84abbddc804dad78582d760c9cb208871dd1ced7392dce3fc5c978a5c3bed683bd76848677643201233110c09c3be38e0cba435c215c2878f13b45dcf282c5c63ad91c8a965a87c2f17ea077f59330ea1ca456250750f896894b0b441bc2a1aad0170af4e984fc79f8b14e830e4a618f80632c262b2b744201ebcee0fb8c8ff6d1447a90c0cfe8ef97f48d035a1abf58cba7cae71afc712f74d5f4c5826b17489713fc4b21844147282c61eb12e6609603d7b26efffdb4134505a707c23cc1e064a432242cf34184a73519e222aac47425bd79a3bd934339736ad59c7455d9673ff8e7b28519bf9332e054e42e7fbc7aa66f6ae8dd39cbdf015b6ede186a76d3c9f86716a283506393275f4512e9fb15443f70d37fd1e7f0b3660517027c152590921a7ed9b8671322b102ee19e6256db2a22a5acc0b83e946e4ef6a007f570970048c717546ec8a751efa61ad66693f208f932f48279440650021ecf1f04913fdcf88a20ca25c3cf17edb7adcf79a512fc2af74aecd8ea7bd03cb788e41b86a4e7d7c10ea40021eaa6be8c512fe13f26623735430bccfbb7e0bc98c5481078cdc6b73bd72e8c3d7fd26fed2c61a6c47b923df00c66a23e0faa3d49e88580eec8a5d4fc34772468903cb8786776850ad020805571b3a8b3ca371a3c5c12f8a059880219391f5742c2c0ff7c13fae74ef3ecfd5f1dae6dde5175a00e157cb14f1259bfcb466d5e979fcb3527cab04aea88148044e75e07b78c18689e81a7cf2dd2caf878c02a4be02280fa8c8d61d0e1f2401595662556e99148f1d9a57a96477ff8aa6a685aa51d27350cf4ac39d7e40748152c9340d53dd75e4420a6bb399da81dce0e08dd4e91c698044d5cac1faf2e06571a605998b7df3af0d5fd842f792f817eb8c60cb573def5c1dee760eea0138152724e7f9e373d3a592cd3a4ec0d8a1c6333ba8da2f2d4fdf40b7e5c689e09142eb8662ed3479a8ee20c4580f85eaa1aa9ae538529b3dc95ea7bc0ce66679b2bcf921912e8c95ce56db759c29a3cb758320c299f86f74e30336dd3a617ca441b567405e39e19e7f985b08b8581f7946a63341c33d78b459a014e443c0a718d5859bd51f7387399d70c03dd9a30df9a8a5ba960d81b54be24831ed71059728b810cbdb8f90a024877dac226888f430abb2a59637be1202be834f14ca9e3c339a9c277d6af09471382f6708153a4eeed1f2c821c287ca702ce61473619752cc772be0dfe14f703dce621d5e479284b114afd4bf15f4a746a7a8c669008c0fb38a1b3f3aba46d532595061510016c912c3828f060b5808d4cff583484af6f534a0fadc656d54389bc6843b1345a20c8aec6686b36df2ff87e51e127f3fd3ab7e4aa50809825298b9fa3e06ce4e4c9a21f5207259ee29d9045250dbd07dbf4e5af9122490fd5a130dc4cf78463c6f315a84f2786cb5530ebf02102fb0bb30103d49f38bfa40fce497b3c08bdb5b44c6073b25ea61ed8a8467b6ea385e6a7e90a695d55967ea4acc3a8eb405cad7224406731c361a2d1b3ffb1afcffaec637f901864a9923fee4877e466915e87bc505633c1692948385f0059bd290f2c785c5e0bb1a8009d507365eb5e1b2a219bb109a59af833c9a009621d174c9b6229d2a06d5a43088a182120026539ec986950fa53b4ebe090247c2ab1ccae74739a7d9ecadb675bebbd4ffe8b781ee4beaf41eac7d8b072e38efb010172187eb1f483378720c1d82564fb322634b23d35798874ec10988e7851f6fd8dbd45c9f5114b1edae0bf6f0b88cde1475e8877c4e4894c8526b64617dc1162a135a08019fa00ffed1fdca347b9a6da90b99da0079bed41a883272a8c0abaf4ef988bbf2347ae6b823a6d03d65a54d0e3555e0a134c6e499d71cfcbf630ff4419872b685cf7f657680efd10582c751f3a1e1f270873e8177217490fddc4e69910c71b17c64f0976aed206a68e595adb5d7810db0c2252c6b1810000681d9059be4a8896e392e922f7cba7d270b9ef05f8a30633b70a834d8cb9b6860110641ff06065af4e70e490b209da80d7366b5f999f49ec6558010165b7127a85f2edfe33e5cd814e4823903be2475d489c56f87fd7c53d59e211c8d8a3010f823e400cda642123118cbfd8a2f3b0b650671287029684051d28527346086d2d072e8ca5f6b14dbeb9f9c4f80f66875db8a467d6824e1f0557e01cf0b0313b6657474f12af743bc0cfc776162a4d3433e67cfced89b897bda0f233f26c4276c0735745dd03fb847abcd079c7cac4cd873b724408c0216359f292280155c1b5709b49689731d1d895aad28c8a18fb1c857a86c65b8ce77ee6b99030e1ff88c1290c9faaabc870e330e11def1f2f33e3c862c3d3240fe56d1647706b3c7d903b1a10a83724f1bc7105c44002dcec783d6c7151611406929b551c9177c27efe178551788c5de29d33ee0c6edf37752dc12da05a48a20bb226d121c514279de45add3c151bbd04192db74884a9ab48a9b1791a02ebac02f0703fe80f6e065253a1ed43b7d2686cdb626796605c9fc0961718e8cfac2e612d5d0a0bb0e4294f9918fddcc25225b24a6477630ac4a6adb06c2a659c17e40f5a0902e3036977ca800f3c95462de81284ca4ea3de78beda931d21abc61ad86d53c9c442c5cbfe39da2d37bb3841204f030ad834edaa7f53945340d8bd947e558113d7eec970d76ee5a752e87aae594e9702ea64d390a739a33fd5e9b0264250b396da6f853b41197940d5d308db7984db9283f1671d334e2b84a6c5c59a6b0f3ce48b9cc13217bc7b4ef2836fd5c2a727877785e37b14d8c5a2d1938dfe9f56eec04751c9223ca0b5cb4bf4b14d7b4006c9f889e54896282c2d21ec40e840ecfdec458835f8bd9f42a2b181b356580dec7c385667130aa0928f07ce70a03efb0f7ea68654698a05d3a7fce1a38a92714f2bb7f40f80c73aef96c09fcc2fb1589eb9a250cb7054c4d0bb6dbb13080a7c1bd106af8042dda52c651fe314fab1dbb00b57a5719f90c0f3cd00b82fd0c36a823912d0ceca41a0a87761ca6d2e4ccc01d0ad324dc2e7df0cffc74d85276b2ace8c12342d8b942434b7839862b0b240c74fd7642bb0bc02a5bd887af78914acd07a52cc5bae8757c6657949cd7d3aa8ac45d35d306c482fc54b375b6d293046391dab7a30ec446c69309c68f4b7a708c520f2c5478cb7a734d496bbc242a547969e2fe5df8661e604ac4c6e43f0fe7ffae186abd2d8793203fca7f555348b47333b731d9e5f700204fa2ad2cada192d142cff6f77d37367b801183537ea065847420fa80e6b14050df2a87bbeb10b119d30cc87134edb89a13a9b200a05dcecc351e0b049b862490b845fb0d14cad0837d40347a3b2808a3ec1b6f93d0870d47f76a83b7dda7c9ca3d825c461e7fdf068d8bd2a5d7a759fca1ef30c06293664a73a48e2a0ca82867c18349475e679efe0c9dc1bfdfc99cb8a58e8cfa6e4130957edcd89107959f995929864811dea36ae6772e34a61346695cecad7ea371f37c0290ced62c62a4a3918e0ff0a9c2e140ef566be2103d4953a5228818871107ba2cc5a4437cd254938c0c410f129516deae3b03df04d3e60a1e86887031ff42c9c090d478a812c5148c054ef8aab9d34f558264848a7bd2f7796a3cc0d7f04d851c3bffd0acf10a4a0b853e73a3d5509fc3822067742a26a86f2c9e7aed7092cdf74c8d2cb2abb13e3c72f4dc6911b596604887ff689e671b0e6d887a80a0002ada40a62e6b49246fd548e1bc8ee9f5d4d00ae602998eefb71d6ed72178b08c8f3d897dfa4ecd7505f6cd1970b29a0f2a4af3073b10a0b9a6543059fa180b6f91e05e8d413a0f6257d24a255acfe1d955e18f35b1722ae951b9528959fa386fc35af67032652bf0ca0e788877f422306deb687a1a4877da9544c41343004d40a9d422c0b2cc7101ba1b0e3adbd0a8a6227de26bb9210c126c43bdda2edb480ebcbd4186c4976ad4e90011877be0435870a757703deb1caa3b4ddfd783377ac50f8594ae7af3942419501364b9182e640dfcdf577b74e499a1d11c5fffe376b033dc4dd22b03b34601c42b6c253d873396d9c421134701350be45363002cbecc1b7ffeab911211e5092c1082efc824b51b2a1a1a7b1260d44f59a9ba05ed6d39d883f2043ef15413c7cf58844b60ce94409816367b540f0ee4f656a94e666d63b3417d72836576f671f427a4f867ee8cd115b9f770288cfbc832d6f3cf882ff0cc9bb4e85cc40c26d4478e34d500fba9554b82e2351200b362509bfa8eaedaaf03a8236cb1f6648301bebaa430a90121b4cee88570838d2b9cda10b425179c4ac12435f1533c2db071472566d09b88c473b33bb9da65bd777738adc03b81a0d49798e773bdf03b1251802457b5625886bb292e2eab56582bc37aa503057746fd4bb3459f889bcf03313855d0367f95ff445fc07fa669f4b30ea66713077b10292e6a67b03dbef099a8aee53dd056cb62c76550e9b70110790651aa1b0b5297b5feb35cf38c3126b317c98d17031606fef1a1d9e8e117f2549e99a78677268b49c592b03c298c376705a5207ab4094afc44e6ef6036a1588bb593fb7f00de2d308002ae6447713d2693b9dfad5b8296802181a864f14ebe4ddb622373169d373bac601652ada8544a40be0c9ba37af1daf10a47459249c587eda54e22f8a0a4791c1bfe80fbc92d50d5941a38a19ed1e24fc2b56196c2466fe0ae8d2f0c1790a509a21019da3497b9ea518e05e1bd796d0dd2b95d2d2d28d37482cd0f5709507016035554d2d2394732a14e2e444dcf00ec968bdee5b9a4ea2ccb24ca051262ea65ecb9ab36b9bba1ebf3ee99070074f3fadc0f234faaeeab49af5ef48ff743daf30eeb58dd6297d82db734ab4ac460986015b6eac5616681ac9c15ef527e74ab294c0fa921cffd0b3ae004772523303d22fa433cde708a07200c466d7932af550dbe01794daf2075b0e931d7b94d552500415361b41e602c20a00051b27183f43fad79fa3454eedaaf213e18e0d60f5ae1c0c774fc0085cc9597ee03a4538fcc0fe23af43c836b2bd69583f3badd99d9684d63ea38d24c37e862d57830ff7c655d544dc40fa155a01cef294ccd4186ac986a9351f8a13fbeb2308e8407b50e49e14a11dd211852bb823773f77ed595c660883898ad87f194f0541125ad59281cce757775dac1c5810478071766decf58302369c320da8f612572f1b74780c6b6ea6401660bf1b091e89bfa0a3b1cbda740fb4d8e8586030f20f1d208f37d103688f4ae672b9e255e95c16906a0a7261f47632b7254991df1b3f46293cf2ca1f19609db578942c1935309ad9b7af388558a1cf85aec93e1fb6e67d1dd1ee0400bf6f708378d149faebb74ccdfa95e9fee0c17bf24cea5ff57af112af9a2e3ca44b0d1a8722b42c886a7f06d5aed343fc0b275c0e6aef3b0b296f2f4a00e22357c6c1078770fd1498db930d1c18a0e7e46eb23d8148c05a68f36f739cb263a5f1daa0161feeb87a03bea9c9d72de5b6a4435ca8c7e0b591532b8c6300abeb927b9d2a91ab4686aa6f5a072a0858a575aa5285430473462808e0146c6d2081a7a0d9c46d8f4b4be14ae701a9b8923f1ea6320f9396a350b07ec405dcdba2119c310528bdeef162dc1e7c58cb5688c778cc01c76f7f94ea5936f3606ad2e5d2f8a640b89d684c520724e9d8290537b59471b3e02b0d887ce7864c94fe7ff32205588b7cc611d1410511f87b3e85ee315515448e75f3002ac93cb765f6611c4507795e59f666106e1ba7da4419e5a4a7e4ab21dc94282c036a1473e526bcd913bc400b1faa9af4f70af1951c19a81650a727e24c66af06a53a4abd6cbc05706b50046c5075064a5fefee33b4d638c9faddc22bcc9c4c43e80d8a4fe7ef92362f46e650e739c6afcb29b0a5d11d203e224625ce2353ea48a76a842e12dfe5150ef678da9cd22d6c2443a25c9b0781ae10fb7fd187ae65891eb9fe8917063e0f4f0f96dd7aeb14b1834e077ab3a1c15c7966abf8505ee78c526ea68cd05b2d52c1fb39b46875947a026b1a89cce6953d2f60370f6285bbaf775949075d01b8b1f05d0054384bf9e85d4a305293a610a2a345e95dacad1782f9465c5f5b6b7589a8619348a5cc5cc7772432aa79abe8a9b8caee515b4837e75f8724e0706a658a38390027e5500fc60c2f1ef884f77208b3a19a9b47c16e21ad7d922dfee062be5044336871de4c985a943dc2085b2ebef9453ee91738247aaa4c2e2c3081f0398b88b57ae7b6fae4c8a71e21b7fe6b75ed5803b339a8c5477f952d046222e08bc2855d911eba73620d1775a327fd1498e0de1f6f81bb892f0fd16a10c578bf8547c17ac7300d4d73a8715e70b9c5fc87a8f604a54e0cee58cd5168ac7fb0575c44d75706b922740095d1dae1164ee799529a232ebb98772643a0b559dffe3a75200abaeaad936f54fff8650b711f5b1e14ffc1d06dcca2c70cbbc26404acccf153f53d335603f87c99013f7bf1879dd6ce30f83d39917526d79b79bb6c47346a604f850fa201233bcac232575ffa5a91cbb68d9db1bc15d7f60c30782e87d10d60cec840080c7a85816f08d1354fb264a98864e5240b9c1982a830835f4c17bd1f7fa5ddd772ab9821cf192431b79579a6bd33efe612d5300670d27c12863b9fbe17d9afb58bb79a71f3b010c30f57c5549df74186345be1f6e95f55b41ae1dfeb663e56c2eb81f908c7f812dc3fd1e3da7a358a42710c300810cf71465e9d03e2524c9ec418b2fb71a36c0301c6b8aa1980084ce9a21f747f5668d069cf0bcd7f3f7ba5546865db599d5ece889f4ad453ba0efea6faf9ec59f66f46a40e0b6d2cc4637bca5ef2f9547ecf2dd2988bea43b3628cc4fabb196193e80c696227b554244400c843e0d7ce986f8c7bc08108dccba03b581924406bcace1500229abe52395dca8d6379274dbe0fbe765207d9a4da6351fdfa5677996ab1ecf7ad18e6dfd5afc39899317d76f26aa7f31c9ebd8d15f2792cb72c44e85f2e8b43b64098a78ed399d1880ef32b88e5be629de0b3079e5f331193413d6f39cf00842f57810f34e4e42e4919290a58a29b34cf73f559614cee23a7b41d56fc77e7f1dc82d65b38676eff03a5e51970637e4f6c258069a8dd9029b7b6ce73e71a31e53ffd51b8f5e896ba3bb706605d7981485984aaea9f358452209b109663824a4e785c60285493f9fd1b134275a7b8ee3459e1fb70404af731fc9f1d63e00de31c1677b28ead6229ae72c04977af6af4a49e21c07c83a4a8c0a939872ca846b149a2fbc494f9abd82fb9ff68e27f69b9b8b939f819f613dc1b8b6ebae864381573ca0afa1d8dc0d4f71b3c6efb3048dd6b14e99253079b9e0892741531993e2e34a8b1e93f50d4b5fba3601ce9c1ea3a8bead477a77366ff03a0e9b2bb994ab70e36faddd3edd6ef02bc8fbcce4d16c6bcca063152814f2ea4365e8c46876791b2104d9de964e18c7be25cd572a4315504d8cd9b1f5e986ad9e2eb059169ce0402e0af316cc4fd6caf322401f0b8396928866bbd13750362194cc7d659a312695057bda66019b60f872752b99387647376e9d368060cb0ce8ce1cfb0ffcb88f9a15fca45d7d31ae15279163513de85d1d724c716196fa561de9a7ae22953f49876311234c15e6904c4af0687a9192270211e4367c27d5551c0b2f2f51a2a64294d8998f36f84f6417541b72626615e1b159c714541078081fbe2fcb87d156be6ddd45d9d227c9d4b52a31995e4265a8337e547af430081f74f6dbd549928f36bc59756fa0a8d35568b6fab4933ca9a6c19a08c87713eaf2fd3cbf7a49f31fcd1d9f9d19b2fdf7efefac95b4f5f7ffdf5ad674fbf7ffeeac9974fbf7ffee6a9b7cfbf7ffaeab9d7cfbf3ff50eb89d84a4843059c2b0d78ea2dad6aab556f34fb03d5641673d2708a9bc916edd8d122ff2a39662943f2f8a24fda3471372785778b910b27f86837d7bd9aab555d88c38d5429a511a5b33398ac8c5a72082dec3103141d6af797711c23ba44a2359fdf86ac9478456f273b4fc7edf495e7510941dd8879474193c84e5982b9fbec1952eee33e585c26003d5f54256271f728fd8d86f321b53568830fcf298ca9872770d87f4660893ceb6ee069337903f8c5b8cbd51b78f4500cb8b2995795ef0744ddede846edc0b5277e3f65863125297e93e895f76beffe29ad745c215a3f27eb7707ddbeb00f8399da1faf168c2e124d97a03260ed63475f1d220a216d73a56d58e3db9dad0ea19af42a4b2add9bc8f16f96567feecba01774bad856fb2e027d7f5a11d3f04dd50f46614ec02978eb0afe35f10c3a9895a9844c1505264a0a0917fc1036933d4383966232aec8604b87945d8fda8d221403bbc21c589923fd53240f5f9bb0f09a370d5fb5088c761c701bc818ba5a77adedbbe5c4673cbb47a67bd1d45bb6fabad644253167311653468a7b341830e096ab010750a3f7acb2cb814286243eb389ad272d205710f85a1901d9a50c92d2b582d1ec47dcd4af7f4756832e34676c69e52c20fb62c0db6b2ef516d6adc8b871020423eba3853ec7a58a385cb51dd7e1b52528bbb509ba112eb03bf8bac4ce7fb22b20e9d61973c8170797d7b48a1ab73647593b4ce06e42be7ec72cd020e8f5405290c7a3fc9e4c023e04feaf7f0b0b570cd2e08a604159e0da83ca10a8ce5ffbfd5958f7542c57ace6e87e339fbb2eb1e96f1a64df20c9021f9f040fe5101121610be13c8fb7b5213a47adc887aabba27fbfbc2630639a1d55912642e6ce64ddfa74592cf32f7f100695941c8e214853a5b85a60f38c74e483d0f1a280b531c48cafbf712c1cbf954277026a52d9041371e3063da71bb4c483641dd7764c1e4bd2010e44e5eda836949782e559d6b86e79a6067278e1f960fe9fd1389c901e9c992d2ef6bbc735ae30b3d95a91759efb156c83725b7fb54eb972b677781a850e31a43bfcd037080ef0aac175d50aafdaac5c5177d27b7be32965aa80493f593c448695356a3f49c2cc649f18d3b8ee2ea5a71df60376fe615fbe91222e762292508b98f5fa32c1789aad4fa6d0b112ce1b22629f47c10ec11aaa8a72c190c36aa46a50f7353c4c194d18d4a4a09a927c022e7e9be763d8730a93ab73901ce1bcc381b1b047946ca28e7dd9e1a81fe60e32c1a5af45c8244770afcfd0a7ef4074bade1cb2f73ebaf58271d0b6128274b4d92cdfb6a1c28cb3a20d4726c458176d3523c7d6fd3f47ced0db8e5da370616050f6291b9cc1e928c97968f4c66e6531ab89a94688df6c9503d8f960f7f80bdeb107e114aa8e09a8f99c7e58a748d0959f2d10df7df0426c124de734f0ce9b72e91d0dc265fee3c17f88d4e350668a841ce51043fd39f860088e9047e3f1622f9000ea1f72b465adfe6b3bcff8e10ae54b8a4e2aaee0c42802c5e875900b0f9887f4503175607b51471816bf3f4636e1eede6077061ec57543e1a0227c9e62ee2c5cf5ccc201afdf15214187084308dc0992c37bc680ddb083159ddf37161a175d285d0107aa378ba84460cae3e88866634109db3e82442dc7ed5f38f37c8e680a5dec6575de0341a7cadfe9bef33b9bd6e4e70d08c34d8a6edc452d4019b00bd118a56e9d55fa650834f6a9e83502b88d9184053ed7bcbcf436332025ee00120ab3e3c2f317cb02654abb7817f16f0aaf7df6edb5c7b39b93b1f32c19192d61f2daea4c59fa15483f143467a9b5c907434bf177d0649ad8b1369dc8b12f9290aa26ac8944024b52c38f377ac207f702c1b955a6c933fa54dbef5d609a181ed91c4d91bc00af598fc45492cf8ce7e94f790647501730fe6f2842e7b637df61865f08d926be7a95c93d778502a705abf9e474e9967d59074c92e9a5855941d7b91e88d1921c100256ccc3a7e01fd06c35556eaed5aedc544fde5d9532184d11919a8c149c25fd37a54ec808f6c30b6ef3a067efd6076d8ff0bcf7b2b385277119e0e64cf8d92970f8964e36e7caf1b1721cbf09c61cb4364f559582ed162d684e8f2d6a23b68146aa0bdc0038f89b6c05dc604547b527530bce3731a4587803f77daacecca574ebf59a2a45e8623fd55efc920ab93356a83c5eeb0add0925b405c3faff045af70e3d635fa86bb3bf8820e653254ca12009976d36d99fe2a1e6bd09930e040a49beb41e6f93e157f4e32a63e95741a262892be9cac7d9dd98d0c18d89541cae5e2d49efb083802a568c337345888f05cb2cedf61cdbcaf947e5f75caa9159bb14eb23ec9af1f2e6cd31eb33e4fd6cb40f1cab32383c0020a8875a12cf7a14165f9d26b72ca37b30b329aed9ac6a6c113071cc3b554964a90f51147ff99bd0f9ad7e2733556bffcbce549f9b61487d50a037eb0525dc79880ce62760ce9c04d8818530a21b66de3adf4cead08105dcc240e3d8177de4f2f3de529c011cc1009dda8da100fea22121e1cedb67a529c6ce9e622d09f5a57f21827e2d11a9a867338a89290fb7920f5a1c6b74689100a72a7041f70b908f2b710f91d8c51d10c2c32f618b29add0633b1ce1df9ccb68f96bbe7a165117062059aab5952149ed5d761f19a49e042cde7e1c5c463f4207c59923883d3da4f6c54e157a3e2a726408a879d8e55cb6d98866abad44ae46537f054806f37f9f9c9e52a182a840e963545a58c73af0d448a751a70a35d55a768c62abc547cfad18dfe5a308717d5395c111df3bbb801c304817fa84005b4a8892691bdabbb6398e23d7aaa55ab5a075eabfadd4c6d496558e074cdd8287a05386c1749168efa3635ac18e425923593eece5c682cb8443e36c2f9bb5b1e725801803ec047313585f852d90ca56e64a10602b615f130c6e742dedf2143907b8c2307a04dc209745e06043ea4e75bb579cfee16cfc33c08d8055f3f17422e6043ac447183e185080426e60a62da5daf6952b7ecf543ea7ce80353161204d316e62d84192c0b891e883c11e8e7b5a4bbb3d413cd834814f448daea0cb52c00b62ce0b0e46455b2b988e5ab8c3ee9aa407f26e52d2ce1d54806cecbac63ec1d581a2bae749426665292a4f9d53800ff36ec2f666e65136250d3f3fa4d660b03d407e37e13ab743ebcf86b9092323ba166549af3831312a0fd00d4e3e2f83ea90ff058e117987342f04320cc214608b7388f87747d9404bf444f05e2e2c5d498e68720bffa60d37358f42b4d854e7ebc473012f9fe0a19a41c2fb044a22693c7fea0f8cdd293cc63688cf0c1514d18e80ec0f53519ef68ba6076419a91613e00a19acaf9df9645da054393604c5d59b63da98e6277c8334fe8217b8048043a09aabd0be35a08be89306fda4063f728fe7006373cfedc4a5b8a614d24841d1c93d430d8b24ee870b2978f136672d727b00c3ef7a05f820491185ad04ebbf821aeb462e13a44a7a9ca6c393c4ed974156f5ed96d16ef5b2e79d91fbbeff1079be77108b80e0b1e31b0740dbfb2bcc1411400e1b8b700de76fc254d428103b98fd735fe6f4a6f3bf3611afd04efcaef00be5eb588def6900f9e483e44e2dafd47ae708917b7583727ee7c342dc676e9e67934b59f73eabfc7366055e1b5a527a9e7990858297fe55cdfc7e9eaa9e4b8ed4eedcb3aa136e6dabeb48512b92dcf1e6e0e06683bc5d4e416a46cbebb4c74101116b758865cc3eb3219012b5cd4215392684e6641e6e762ca927713aa1bfd436067c86cad18aa58f1e9e44668de47cd79fd50ef642548d747c0a79aa7e3113ed0ad8be2bf42dc6d233634c6d441ab18a917036e27b1429e29d00a76da945dd3900c31f2e4652d6a51165852e73662291117dcf036f50d1cce5f82beea3b80105c65537beb6b0f834c64c865b0cbaaf01f8802434779f79d84560c644630948dec8364725fbb63102128df2dba9558562b30f79e46416c54dc93d9a6415f9fab5907093b2bbccfea6fe1862f7c9e940d246bd8548b93ffb46e7de30d53c1fe50864ffd896d909083176852c4f1c0099c476cf89aee3e88fae4b3fa69bf777c4bd0605ee919408eae02fe8c6c3efc053c615542a6fb5616d18f3cf4896d30d45b25f7124d0f74cbf779eac403f641b410d3f0b9198363e70c4aad5f1af783224da30e3eea2da2f3b960a2863214cbf153ac2231f4aebe8318be1018149182f8f7005cf605eed0112141deef6818308c6ac919185c27a7f45e8ab42ac4afcb6455a5c22cd81b56e5ba88ec869de9a077690928ddfc56f14f2e68a96b173c45cd8d046d3e811eeadfa7dc72fbf35cf918ee9a6c4485a1e1d92feada9ab9fb4c6efac9e4aef6a78ea6865f5d40eae70604289ab4c0e3f143b8d41d7d2447ec6e5577c9c8b95d3a77a7b8cb4943a06bdd79fc620c3aaf98b55fd2409fde2e8021bada2cbbfcc2168da135628ad5ec0a3c3b76acea8581a6424824c0a2925f26e3d1d5db183794fd1198087651af2021b4f3614cf33b59c94bfb5cc4263eb3317e683e3231a534bd8aa53b66f3134df5e94dcf2f0bd80d65f7fcdda6ee70765f9703b9d2b0963cdd5dc0c01483c4d4a3e82e46e904ea1c70715380904dc34a13fa7ae3aa552f84121bcc86bf3afda8a96291788676c70b05340eeccc07bbac466e85808f9a9f4ae05fefe96baeb378a72d30678ab91c770371ba0681ce04724b955ae91e489cfbecdbaac35171091c5de3a40178148c0feaacb77b5b5abf0d9b74a6237df56c08a8d3c1869a3aeaf5272ac6207c00f1db896fca4e9ca71b4f68a231b3a574f3813581188070cd5e8ef59ae404915a7c1cac34ad09b9ea2504011472224e14cfff1b9b370ec5206e366c0b058d775167dfaed0f0968c56dd0041502525c8f4481470c20379c853974ca6d27f70d53906268c3a18dd02ecb2a61ca84bd66dec431a0a7f40460e3ec33fe341e25307abeac09bde500c34c09996f7800c5d69db11f3c199da0097f9fcc41b0b54a0f1db3996b7baa01b6a6db5a9bcd08b5bd46a185a27e01ec0456db6b78439fdf6a079593ec7deac894bdfd15b17aa24ede2b7ef6b7f94b7fad9e9d443649f7c5313f40215026debdcea02a0770d0b1eba148dc68a3eacfe9627b369666284e3753e34ffba6148b538a768c8bce6deff039058c4b780718d9b0751efe8d69813c5203273a7a8a71cd7500e90cdecb951c18f6704eeb78306db3ed9671513a7f97b5932b98c5579c35e059f2552c26196dd29eb820fbd2b6b82a0771c5e78db6f4c1e64df47a42bba135812f89a41414cbdde04984c6ca7c04fca517f934db422b0576c3c390fced62c5bc4d1040b54d9d6907a8136c47884df89ae7730eb52d839206dbc00416a089be98ddb4b091c1117bc83c5f299adf5bff6156b2077b24f6cb0812d19775b68703fc50bc2684a08bdd21056d21fd4cfabd9ad627af37e7c547179226c1b34d871538d98f04023ff7101c0aa463bfa5dd7189467a480b069e016fc25a407ced5e4de819fbf075824754ad966241742b82a2d242140e1a7b86a847b916d0707bcf077b2ccf784f3e7c91d80bcdddb4223663702269bf70a761afe7f97f267ae67dde60a470dcab61bc1f74aa9d10c528df1593ce139970160ea05d07f4d487364460c10e4c0e3fad55fb178d236f8e1b4a4b22bd181d8415b78bce9770e0c313c2bbbc51090c46d158b85412b83b704581a0bef6f5cb49978c155fb08a3589f68328d3a4164ac1d61c4d1e85e4f5fdd05d6dc31490c13f01bb8fce72502f5ab4b18d25ab5fcbc7662a8ce19bf101c19aff7fe8be562f2e526543173b60554394bd38c02597f4645504c5b2ae33edcc2bb5e7a5c6b2822767fe1397408bfbb92687e04daf8983efe6ffac060c37c195ed46c780312dcf9ecc0d07bc162702a9a7502a6c097cb8d142115bdcaa8b3c917e89ab6a11903714e9c85821e6310b8fafa990a2c60d7baec57e73a95061d1c9b4b40d0ba5f9d12c628a9e597ee02551d63ce70481aa0c11ad2be2f53a84a4bd0ff9d13d65ec256bc477305f3a80f9112dd962cc6b14c2a502373ab02efa11961018a23a78d08a9170be4c7f7acdcae0373eb3da43f9b10511090e51194d066901288b7893eee3ae1774b845d3696f48fe82309f633b0722827d0161ea96559364fe7a2771fc893f1b089238b58f2c1aebdfa5f27cdf014ad089775c0d1ca96fd8ea98007a74c8a6d65600a628de4aa9a44806a750a681194ac0b19f3fb9c0d289d64f0f4cb44ef6c1596a3e50d28ae0867b19eadc545dec5d27c3029b3e7d5ded7712d3171282dc2800f27bd00208782516064b467aa0b70be80119e33491b16883b5d28414de6a5c9e2bbc3842b8135247347d7cbdd9decb2c649e896701d2aac742829c4dca12cb6ae35100e265579f926f92298212f3de820f9100b0c1ad67ef6000486c11447411ee1606f947c2788cb813a1f215f264f74ee5767bcb72de8c34cbc82ee19f552f0c13f189ffff63e18b9c9772a38c9fde04b8a3dc3cc11f51912671d06e50356b98488716f9f104632de5cb9a5fefd597de6ff9c9209266033f1c5c4a9d6a0311d2f28ce29a9cc9791af062c6eed17e9fd0e6a66261e6e7538eda6688e7a162b45dcfa4bc8b83337581db8d4459897f5c12ff2d61478fda498bd8f9ba35c926c79129b2329421ea098974efb35dcb76b8ed687a9653b7482c7efb0918c998e41818250b31650836950a6a38c83305a4590ac83aeb1b30fe109502738d31afc8c7250f8a908c9166cc000f2469559548a2a2801845c8e4c1fd0f83d3236d3f6bb209ab5297ba3de59e83e429709862e9947824dee24a88317934b7cffb232a34df9f47eddf68336c037d7255497191b232e5fd00f07e382c7b9fe127c5a9502a812c6454257e491d505aaa916d228f1b2179f85ed2cb91b759d50de3aee59c76b3bbac2da26cdfa35676538b49948da2122a3ff95fbe1671b29dc90e94b40fd732d74f04a2aa1f1061d5058cdedb74a92544fb708fd96f87c83b13be20ff95a1b27ef2c8c9893c10c76ec730f3e3f5699604483d10f64700d92580e64e1c67411db0c73e2f3d242a708fd860808d5940178ba9938c86e184a4e33d3eb5a73dc5272de8d2c2d25a1406964e969bb1cda417671a9b7dcc0f9933b41e2703bdfa018b3b882a0499cf8af620e9bf369f05aff1f76191c3518832ce7a9993e96febd2ed07c91b4d0717aa11d4b5d673f180a8e68596ccd7e3061f92e0ac60e1eb923c6f254aabda92f89c73b612f296359769c75692647ba4dd51465e29c55f655cfa628d27a99c332cd049f072fd8288f218f4ace7727302a36a9be6036110ec2d818748e2be60b34a5294e33f31cbff4448c577c5ba9aaa42674f3c6622f2acf5f6c95333f154b72c828b42281094a282fd526f9b7135cf46563d80ebc6f82eececde787bd7f0ec4a89d0ac406b6f711302f47306fdf073466d6070a37fd469eda76f408f269e1e7ae4089801042f4f936483e08c6ef8e5d2841811626386a18de7cecd95f0fe3b87c00ebd0ce9b0fee891ee9a2901ee27559af687b7ee59a1a30bcdfbc203a0311db8fb715829f7c58e55062f3a30f5e7c4656560feec83fc20b411fda1247e4b59ec6c1494094d85ac19c85689307cd79e35a554ff9ca824d2e938e076c3831a8119e0fba4f1cdfe50effb9ebd1c5462f467d02222fb439d8993408a936df018c2b123be8486e209c43c5038253cc8c7a94d63a6a7a24f9d88655c368f50a2cda8147002757e010b0c9b33291d8742e1925cd0887b0d695b4af7083239734f9cb1e04f033607f3476c11e20b182d5cbc349690d84773c0bc5421f1a5482502ad89a3606ee5f025a50493917673b0d30892c933458d725e89339fc18d463712c7c54252748880fd47637d65851a8c782e23634ca10b0e080901b70a9dc81fb4b6c7154af8ae450269cf96295375c2988f4bc425f8683b4cd7f07939d2adc5e3b9fe686f3d104bfb38f1eea9bd0048f16cd7ab5f97d3ba0969d3d5d70baea9c9561f0c1f37fab02ab0ab5441d82a19c30e853ac971d590328d1f2754e5ea30c703d1f68cec40853f6321887fda5c53f90a551cd41644d1daf2a6b5a2ae46b38d8ee2a735e740fd983410717f67f6251d0512afa4be51a05c49d8377cd0b33fdfe799a4dc13010fc795ee0c3b153aaf10a7a55917e4a29dace858d5d3e8dea32676900135fe2971924f0768ee509fc8dab5a812c0e77c8072f6e01d7d20cc2638da1bb02fabbc7c13e47cd5453612163a14c34c186a22520e6771ca0111e462173eb2ab4e24d592e2e60b04a34f55d9d20122e885df83ac3b711f7742db72103c6c8b0fac5ab04b76259025618fb2ac754d3f7c9ce9b894c591822b87b76246e8b7619ca039443a741ddb06ce07dab0e09cc8cd2a2461e42633f5127526a94f47c13e547cd24453a32d44d103f097a603d642011394e75adb4962b05104d230c3295c021644db6d1b950652103d9f34a670a7ea89c634b77764f33527a67fee52b8cde45145ede39e2a5bd983c26d04b390b97cd586f0840c80445cbd6aede0e022d5e0490b97effe21c51eb5ca0439a98eb76fd6d8d80008ab805fb798682f412d940ba865b4428cce5990a33c389651a2fc702b47475ab81590323f7f57ad9780401b68371388503ee31d5675f92a0dce7333a5f5fb7bcfa4177bf8e8094121825b65f8d564c9fb18dab1f9e29cd8110e0a40b2eb7874d3f8a732bb1edbe42d44c848458c1386f6a3eeb7848f38ab606ec09f7c545d6d4641c1bcaa8531983952be6bd92eaa15c6df44d8f72cc25efb3306cad65aed98c7fa399f8d84b1770958422e241fe4b87c83934dcb21df0a4198678c02967f114b433ec9903cb728946a0cabaf8df0552982ed022a6a3cf3f5df233c1d8c574181d752ce295ad05a7cf93113c84e38c698b60e438f4dfc3c29ffa171e65fa7455533966f24af9fbc238d7e6d4f9cfcdcefc3596738f4ae47c8077d3c4411eaada747c08220bd3e3d7243b65758b4f7786c9938358c4e594d9292331ca90a1c385bd7d1c03250b44e9825fb6c969f0d2cc064aa1cffa6584e034fb3df279a360a0b352ab1622744cfb7fbd819851241df2fc6b3e59eaf07f1f6729660da49de3cb6c93f4a7d7c91cca883d47dac072f7579125756c026731dd7777fbc3485e51477944ddf581e59d896d252547269ca72d490260e7cc24e0393caebc2a5897b000df1366db7e51e152cfd5cdb8666630c3ba055804e2f2fc09cb1e3a521cd19c97d21bf7f3e10abebb73c2077910dd913736fc916725e4d12f2e8425be0c9483c9bfe28de951445fb3f6b5d1ed0f74e4e22c7396b98468379e12e4c67cf10b3b620f82e5d27aa6c90a580e729174df8bcfcbd24c5d62005b85874df179dc98a4bf80e65dd708f9dbc99367c0ceda0f5f600e005b6a3923e81f2a2f6bf02f389a6e6e9ab2a4131c5294706fe2d5aecb0a0a9234f8859143eb3f0a900fc2804cf6e6b80be17a86ea366a571c337facec17a57ab3764f90b50b3fd6ae7fc5aac4ee7e9aad4808faf8117f0e59c5ac27d8795a89fd0a25b8d2d9ef2fc06c9f8e773a2d09bdb39a85966d2037ae37165af1b21d8e831049c03cfe659c05116d458991cbbda61bcfae8c22f14aee7445a7504fc2afdf55dc4d13bd5a6fa5f47eb487358f1d1e16005ef43771ec35f982b7ecbd80075b1a16523ccb30047400f4ed00ec064ae2560a1a067c6564c95f4ec56e05c27ee3b1b0ba8d3465f9065a650470b8f95b9bd8f92a4355e48e3587588c2eb3d61900ebc2515daad207c7d1ec1a22d59ab20ba423bcd5031ade32945c30903a4e072a04cccf4a6966c3b818a3e8f92178c3f5f88fde680c75d4ed499390a4d9747286b761b541205580aca20f210fdc3f296aae4188a21c82a6344f41ef11e364f3e61a981df13edc32daf993bdb4b677b72de596522699029d08cb091709f6e518ad53dbc67c2e1ed9af45b62319c8617b42ea7ba750eb9ddf9c331082ec7340ba5f70c15734beddda240bc826348e7c05740fa7d9e505a1e19e6bcdf47796ef7b279369e3e8e8339f510ecccfe8b3d6cc425d6f646b74e4984553fc8f0bc7cf74431db733bdff78df8b7841ba373d67ea28e54e3f3ee7b98db77389dea7f1a3f832ff5b388a33e19509430ff371618bfbf16e9bf74750d97bef2ebef7de3bbbdfbdf773eebdab7bf11d69beddb790dbdddb9dbef3be0babb49e901bbec03d6d4f883f3741fa1c583729da97b2481acd3c6f71de333a0e9913627af7259d82e9efc5f8fbb6edbb79e98e27f0386341b4753a7b8f8a014296bd7720609900b96540fc64eafd49a3941ee5edb96dfbae28df9ff34ddf2f64b1d24397fb1e8226f93b11bc1f1e1ae94218b890c7b66dbf85b248c6e312e12e3705e8347012f9744d1afbaf2944dfd8afe08c02ce20179fb48dfd0d9c407ee44844640be647693665ad33f3cfbcfd75fbdb6f3179d33add6fa73f82e23a2e94de8f24e4f6bc97de4b0fd5469ef724b88a7baf47f63e01ed7d4eeb74ef799ee779de7b9e870a3d211ecaf3501e0abf45fd18f32811bc203161129951efcd66cc84a7a3dcdddddddddd3d8777829f40103c21defd94f7a3001bd879babb7bcb7d82e009f1e9be6d81fd29e84706e85c829e4de00972d61b16c9e105a16112996b08c30b48d36960dfbd70dd6c7d983b40e747b3185db2bd7dda7215f73dc13fd97e3b6d92edcf32dafe0cd33addafa46c9ee12ab73209cc778467976cbb233fc3e3e9af6d2fc8294c22332a1cef9ffe9e401964f6c217b62d781c612421fb17d95e0a8db7ff82ab52f7fe0d65942018bc841a6fbf0379b44dce5cd50869844132432f0420a1b2a552b6efade4a2ed0e1160023be36cdb9663db36d376c3f41b104bb2aa93e0bcf733819d73a45276366788c31342dfe70d2f051a524aa54ccaa44c26bb187fb325655bc8e574ed16a98647c6914dbaf9b66df79adec324f2c76692f9f722e5d1d9e7e105a1c9b268471198e3d169a0ec6981147c018401890c3857a06dec53a06facfce62a069ff9ecfb680d2fc80c2590fc71bfb247539f3992ec415fc479117f339fdd997b7dc685a31f696192f007a5bcea4cdad3009b35c19975aec9b4e53101734ed98c39c6a921f3e3cf7cf0f313d03d3acf1e79fe07e08c398d7d1b60bbd310f136b37533e66d3f7fec661657713f697841ba3089cc5e3876329e90edb93089f4998b76f4996cdbe286a3bf29dcb62d09cdf78fa0f27dcd14dea2a3eff215cd2011e7c5ef71bbfdf93da7e95bc80c4d53f2e4d9e9001283f4914d7810912335192a9a4bae5dd0077b426698444aa02019248364d0fc51fbf948a8ed6ced7b5e10ade372ba695ffc8f7d311cdbc1027c39e96a1d9974f284dcf7205745912ecf4be1beffe8cd7b319eb9cfe40f2881c06ec662b1fbdd8ffe5d287d8270a004923f3b9ec045bcd53addd99754fb9cd691607f469494a595edcb9e23587fcea68527bf5ba786334bdbd89fe08cb9687f071669dfbdb4ce7dfb7fe3464cccec1b8e31e8c8a92434fb1f4165bf7fffe6f1feec467feca2e987116c0a8d70a10472d1ca9f6d0b1ea54fb6d2c755a9973fd6ca5cb4394ee30fbb312c9bd9d98c878b5606e7f874bc2cdbe77874a6fa13a44f039c42df2f8833577ee434f6c1b4b5d66e333c4efbf6fd680b32431c76a0cc5d4f57e3e1fc08131c7dcff37ee6297a7892e57b2ccfdf01058fb8b725b249a632783f5cc8c56ec714d85b481f7daf4ee42ad3d3f9ba361c3176c105eb2e17bb1fe70bd63a528a2c3dcf01f3bd97ab529a17a634ed354d0b3b300728c3db2b7f350d7dd8161b6f18f1ffdc79f497d4431fe2da6facc0e39cf9d2cc5547bec2ae1a292c6baf7de758ab5bd23ade075ae784fad3e93b70449d3c5066d3c934875c149a2f2b4ef3b233a96da8b5324cba17e3f9cd7ff1e8bb7c45ee599ca80a3cce574efa15fe99e4af23f28c2c05ff6bfa6bbed83758f038936490793e99b309f4e3aa7102654a8196260d7dbb19e17102a5fa490a1de44bf3d6d01be605a1c96e25b5044642b39c3f99bed31f17e9bc021b992fc37cb9329d3323fe2f2bae226a9d2badd3bdf75df71a387ae128a7cb45dad98efed315c4a7509221181e3d2953fa54c85529772b43ae5292afbd9c497226c99924a71557cda3d7f1e57021777927b87daa94c5c5271268f3975b71988b94c869e8d3a3eff2156569c5002bb96a26b54e7dfa138cd6f137325fd2e71c3c82caaf65d7a6e84128cbf7973f933c0153f5e7a76a485f3a7d8e3a954047aeaa99e2fff7614ce7cbd7c2d4a43f93647772f7ed86456865f92fc9ee0ef09fe13ca23d063f6001b6468e68a1911962f7ef6850e7dfc3f71787d4c51bb8c65ff1fd48a881d6a00af41b04e2620d7fd4a74feb0f9e5ce9bcd19a6680a6d24aafa952a6948e3899e2b848bfbbe9534a29ad956a9aec075806a2c237efc5b8418bb72eb4222e760dc32f6e1da9d3fdd7fbe509b8d6c13940cc946bd34ffbdcc86530a6a7a690bae83c441d8ed38231e4640ce4e4114836d2effd08247b395c8d1fdb48c7f4352600593eea246b745f5328c3b1bf7b1f33061c0790ed6310059eda28869cff72b858bfa6b22080205a0787d669227d6a1359e629bde7fcceb2894a9396b88a888b55fcff3e1a253ca6fae5cf9f61aa654f520040c872264b1e59de64f9ea9bfaa34d96a3b7bdc9e459869f04457822877c49f99aa0b4d2f31b94aff9f425ef9523d8804d9f9a8f846e1ea6e60c4517ebd70dac71b1ca7cf59e769a6bbfc11107c7c3cc998512650b33759c17842653ef046badb5d3a5363529ad0ccef151510b7fc8dcf3ebdb2afb31caa33862166a6d3035bfbb3376953fca637ca89aa66955abf5ab875a0d02d75a6bad55d3ea089472cb7c00e3d455e34f2cb7ec0796dd9354bac4f12cdb666b1afa94be53fab76de8ef7882e9370f1d3934d32742e53cd51a36d09d5382c1f58e2570cb7c78e596f9309439efbe2e9d85f636458fdb61991cefc323a775c838a345a9a46966f26492e5f454505a4a72f1032f8d0c220c1811292101164354a03c41bdc0c1dfbf0eb1c66a55d32ad601fe20b7cc6565071847164b18476e99eb0b17ce14b547d3344d6b2da66c4ef444f1f38dc48fd6aac2897c004193b70205b74a4c192e28c10e540c5d21c309ede8c3628a165f5c451031744ff0840c3045e84006463011c62a2140513e4551ab10496c096201d29208907e68c2fa40850939c45e375072420c1098200c9103b020b9b0c860882d624c68b3245a0879a14589a12ead12ece089ecab0b0e40af1a80c0041bc85e5f4ca8c1d26b4904b12a3c3431a10b2015b84c6085167826b7ac8b5717433580e5f1717eee66abb52e6b65aaed2285b55d5ed0035017303c78f1ea42860f4b5cec00e342081d8020c1b8484207203700e2e2091e807831a406af9bdc3221a0d30d335bb55a654e6639c8da444749333ff8e69639b9411e711054e0ff2101891e234cd025064d58a94225ac0007ca0f0a8ea0444b8af8699915556a706ce49661e1419eb9655638218301e49621312577b9654364f9b4aa69758b171df4ecf045139f9f7ac596987605148a3f2c8331920fb58a2d39e061cba38c4c0b594c6e99162ac836b74c8b57163159708ddc32295d84b0965b262507d80b0621b74c8a183486c0a9dc32570f3584302ab7cc356548144b34b72c0ad7c52eef4b0fca09265d17b293143b705e2c7958ec6002ea94b80062eb6288530a4304174966ca8107fbe5b539f141b3c1eb0afd541b20591810465bf0d0801b18d5c049036cd092896146100df0c225db819311340069e95bc1494b1605073215b4668a789c80f2e5cb95227ee84189a1d90315cd022524b01265298c1e88a8cc18100991456c054147566810c612da92971f986859e10227ba68c2e4656700861158e48004508aa67841c4a30409bef8c20352bef0008cd0c745c933d1344dd3b67c69c2e384043d3003882f5410450a585a8b0b914c9433961c600b12378a40c0858c2f8b3058491633c0b10515980b0ebc2d53d0303112e3f4431326272646583ea8c203415c8664946cf183238ba01b4c5c00c6c7c40634b2a8828d66334c8ac4004149092bd011022545430050b265875416586ebcc0071b379051e38612b6d82200215062e444c701b450e22b0281163ddc506294824ec9164b7c4cb41823c5a4288bb0a8898e2c70f840c90b90985112562e4636946c198386122c5c8cf05620450894e412645faa907d11e24b131e3954ad56adf24cb9011058ec20c6183ae8c1fca26ae18982871e0cf1a089104956d01e33b898d9b0c5880c2aa828217b410f13498684908c8b0eb229b78c0bd2152c332aacc822092996701ddd24aafcbf9488ee8f1f07470bb667f8ab04a9835c727da6c48a1b6411c4911322aa70dd028fdcb227b1217e72cb8488e516e4963d09937f89a5ac7d6ba112cb5a2883e70d7c799ca1d56a65193e55b747b5afe1a8d5d0487dcf9daf7deda9083dc0f6b5d048fd998d6832aed2b42c61943580f31111b2fcb1861f76d1592dc0e3f5d1315ffe93d73d4ed06627cdfdd3d4e48fd3ccf9a6cf3873bce1bb4743e913058f3208f7e7dd90c35de8004a337d39ca1bd2cdb523fa6340ac6db5de9f2bc0f0d98119b4522c4843d75a0d8c102c80d24312d554820e4b62ac2dbc74dbbc13e6735cdb7cf9077d1b8332b260ed98be06ef0104605853e6f3b0b80d3481ed9d70ad168e35e0a48898c094691e915afd4dc897e597e5d3af616894b2d91961edc77ba4edac317ca6bb793ae09a43f54873ad3b591a91c185f4d370d4004f6b61df79ab944b604ebaa00c1e72e82fa6a8e04c44703497dc300450ac052c8eca1f58e0828245c1fe620a8bab578e382288c5695984a088c5d98e080c122c6171b7932209252cee76154c11a93b8dc516222d7674cc8055abd8e24b128b823a66e0ee617060f2f439732b432e36a17d7ee9c5fd72b9bb7cadb5d0a384ee4a1870a56cc465c8a3c9ea0412da3252f270dc365a8bdfb7064ff185b6d1de876bbf030b1e71d6dee7476a6f240316e81e2ec89bd6b9af3dd6341f382ed064f9bd2306523ed5ea532c83680feacfa7df55ce39e72cc27386dd0bbe6aef1d50707b42eaec4ebaee50962f19e434f2fdc845293f01b97f34803c81decdee9a34f2b57fd93a6e18ea70b1861368fab89834573ea090500485e3cd419bf664c1e34c8acd2557554b655a51eb6c5ab48e897b93e93570e4c251d2d7465fd7c2acb8982dc541e6ae1dd2176c4efcbd58f3e590c9b267ae682cb6430adc547ef773d58577bc4628cc456985c6e8ab87be640e998ebeb27cfaa2548af752ba972b2fac9994f6b48d34e234e6aa2cae9ad6483f7df9340c2da375b637fdd6bffd05475318769e03faa9d63f97bc20fdf4fd9358f0a5a54d5c94613e572438b44e0e72459328ed610459d222b2b768d25c512c4d235f4b96cfc54bedc9f23b8fc6da46fe8e20ea4fd2532117a5ec3f32f3fca51aa6ba73b1d5e4757c39b04cc7e375fc742f6539fa2c4b2995602e4a1924024df9da6d3571556f30c03d85d436d2affd0591d06cffe650128529177255bba4f384222e1ed1dc4ddf01f4bda9f7fc59ad911ab16d3acc7131c8cdfd31d89c64661c3c96f019356296f33bd0ebeebedf55d30b9574bfc3c3eade87746183dd8156a6dfbd0fbfe08d0bb0f41153e3e2fd9aa2afbd092eca57a5683846eb5ca06d965aa7d61e792363681dfa36749730774918ed992f6b4f960d933ac090d0fe22cb26372ec0e31d3b4bee95cfee5c29e169c1827c7a9678c08616eb033c32727e07f6a459752fbf2bd8b97b09990a4ed3df320f470723270fe73bbae05a87959a6c39e94b972ee7eae3708460846516d9633ea53fc12b6fe6d350b682be1c8c93e928664a5d9cb2556ba52edbc6fd65b565670839be9f93a1af92dcddddddddddddddddddddddddfd5f58c9a449635fce4eea694d1c37be0fdec6cb1999af41e3e5e3b82a06a3fef4f27d9800448521445c75eb4a04151512827be79d8b3269aec2b7612887c070c8e7430bc23b08b405dc3211b8f2975b26029fccd16e0343780a6e2f02f85f412922fc05eb6be07d13a83d079abe034fa79898af51e3674099b761e33ff083bf017e8f03bcf113c4f129707e0eb07308efc3656e5339ec3f28959cc6e6105ecaa6086f5fe608672a1cf1f470bcb44b8121bccc384230c2a3944bdbe7f8711281f0e3bc9253360728330829104000e173a400428ecff11e662ef1f98384665fc2730543d3f8272db55260d3ab751b58730ef004ff1428955cc401ca1ba07ff63fb06f03f419d07d6440af013a903f71d17e0ce841a04701dde542a00fb968bf03bd097fb915d0612e5a0b7a16a729babe348d5cb57da6b7a39986b47e6fa61a1a714adddda9d322bf5df091fb92085a3220926aed524b60eba1e012944a760b93e4a2955fe73571b2b3a560eade0de491e3628cdc1105965fed731dd3c9a49cd43a3f128c5ee14983c7c8d6866cedb59e8381c13d0f71680cf732c9554a4badb37db3591193a9b559a1f9f40c524ab9c477a494d765fbbd57debf2ede1f5ebe2f1bfcda467c01a699cf01a9a467e8004fabe8fd2b99649fabef62143c7bf779a59a77399917aed034fe1d172fd987f4d2b5bd34c3b65dab3d09b2a2f96cb37abbbabbbbbbbb57a9c574940face00ac6656d1d1964fee10968551df297095af4c7df73725bdbf85713e070e83557f762fc852fdef08269585d33ac411e15dc2ebafc5ae5c72b08df577ee22aea9a567d7ee69d330026f86a6bc9489c2c25095f96defdb28b5cec0c4628f7cbed8cec46dcf470d34387f2bcd309851231bedf7d3937558eb8a9226537c7c90dc04c301e9860b3c33e31aac12fd8c391d684c1212c008ca4de62aef26dc9b4642bda9236a42dcc66b41d6dce0975ee9aabc65e16a7f12da6d51081e96fdbb66dd2b308b0cd374b388b965e7103681df99b7dbb34575cb8c5b67044e52d3657d68b4dda62bdc8fe1636697c8b5998bb62631b2d657f2e0abc116d587a8bb58efcbc64dfb8e46c5aa8fc61f3166e60c02de6a23f8d173c755a8701dbfbe7c8b0472fa055d5cb5b38d62859fa3cd9b972249ac6bf04d9bf4a769762dc622723579dbab8ea6eb12d6632559ffa5381dac60a2cc9cba153dd01c8feeef2267cb5b57c0950f6865109ca0ee4c16054aee4d185bca7ade4e164bb9f2467414f28df2ce102380e1ee0f1cb2e9764186f59d85ccd206965c95a6b6d6e0976163428cadcec245212102329b4922ca20ee632cbe4ac5933c828d967481ececf8e9f76f3051ee5926de2256cef6f7b5a8b81000de4d2bb19f986520bae911bb6842b6f60fd719a272e06d528ae98201a7cb2d397fe6a43ab4e482d361625c8fd74288333d93b41fe0e26f0789ad500e17133ca8e82f7a3db663c1965d736fbd44361bef65c6b9b918bcef9c625fb5329e9a5f43473d1a987c266349bb3a1142edc64e1464329db09e9843457a6b7ada3790d41ab6a00a96a04d1a2d780229babef5e8c371990ab2a0e2dcaf7af1144952c7fac0125fb9694fd6de4f1840425fbd75a6be5218f35924e48d9bf46d25c7d509ac6fffffb30ae31d43592b28703c8e307e58365ff5ed99df5b5e6bd187fadec47e419b2e7490af73ebcbd08388b0b776a606d4f5fb582d507ac3f3e82a454320dc74d36c1fbc950660c4086471757a05f3f02f33da421a4610a117f1ae6d330d5375adfe1f7e5662d9961d294262a80f202a19e18105a6eaedc5cb9b97273e5e6cacd959b2b37576eaedc5cb9b97273a505259686904ad09382a0ac819b8fa0945a5062c94bf6d3f3a5880f287846a6333703f6b08209188b93dd0c2867008b818b25033a97315832e04e6f118225f333e04e0d2f66603163d9d8a941ce58325f430844b066de06b85383f7704613ac29618d91c6d7789e1cf33ca8e7c9a7e7c9def3e4ee7932f73cd9f43c797b9e7c9f27dbe7c9daf3e4fa3c993e4fae610b438935f31c0ab0ff0cd8f908f269c4c8e609f43ace1425cfdface4f9162ca2a00c48bbdab011f60ca6e15507ae5b9092454ea72de032ab2fbd39ebd349bfd6494323f43d07f10f6b5fb391fa32557efb701a7ed2a50c63045cb74082a321a02117093d20982c259635450334cbf716acd5fa020b4e83c00cb6afe1ed7e5a3ae989527a9af629c8bdfcc1c9aebd13acac3fe98972940af9e7e67dcf4b00ae18d5b8b53f85e3f6d745cdc8f6dd778a09719fb8af6f7aeeaf7782f71ceda414ffeb5f9fd340fb0e12711acd6d38c4458d07f8cad0878bdac90793d0d413727f8e32597b0fccd17daf4374713e76d5164a79b37c9b8dc8af32d42ed5feb3a67daee12866196aae9decb0d0217fcc2cbfc1115ab543bc616fc49d2bcab0d34a33ecd890e77338e011e33c9f85b94a11b17fff863b6dc3d410fb3cac5b64ae7858468c913929b6cd7c4eb60c7f784ea279be0b45797e1157e1e859c91f925e7055cba210e5f93dc24ead2346905babd487abc62e02e2aa51cc33d5f44d70d5b592672ccb503e1131c66d53b303f6f062cfb21b1eb0e7fe5815af2a5c373ce099db4e1f2ee79c523b2242015a8043982248b62e6cce39430664d8b951057f9ee1bd28d09f4052f45180471c7c0293c3bfef86072c6f78c03777928bf2539f75d488d9e70c533f349cb6f1f7ecc37df80d1b3410b31b034eca218c86d841fe523f4c228644c82d23424683981091831a542528080dca120ac82d0bd262976419941d48c82d8312ebdb397143888d466e5911b310a698d36b1b7f2260d9cb2d33a24977113ac89fcc08275266842b7f2eca546e991144f973518232236410e343a769ad1cdf99ee6f17ecee77b793b2bbdd6f9dea7ed7c9ea9dd009917fdf878fdb731d785db4767bbbbd0fffbe2edcc2eb5e90fb32ec17524386e41b666018c2f17af58143d22fc3cdf2ed7b9eb90dd0d3b2a75e2073f5cd19e6ea7ebd3ff762fc7d5bb5328a05a5cfac3ff87eb57ef52b4feb9bab66d5ce224e4eae0dcb15c8a4a9ac1b6660e992412ed6f782c0f2471924879ec89ebc64417441e28723f99cf7dd3df71130bd17d2e0bd8906d477e17d2ea4a1fbfba690862944babf4f83e9f274ef3d91eebda7ef853b5368e8bd09f55dd8754fbfebfebe27d336da775df75d58030d3e7026cdfc147d1e160a4cd1100991ee51f7e3bc47f1b01e9f42ec2368dfbdf7238e07ba70c8a499f34768716f7a9087650279582fc1146696ad9d229366be299c33c0f0806671a36c99b8076590f409020af201bb37819e7c09d630893ce2128664a7fb2e945ed879ef7db3ba1fe5fc0e6cc96a4f86336da37db3c618b61ad222b2ea7789db3aaae37cf4471c1e4b3895820846e67fee3015244b198a4ff3f85987bfabe61652e4fa63bb14b91b968df453f7ae942ee05e8cbfef5f1467c3165087e8e64cdddb7597eed65dabddd46bb1ff7abbbbec3893778f70e771759359862814aa6ad7b30c4fa793e6597bb30c3d2ffb6bd5d4d96b157591e6d6e91144c9ed9d3acee49d382efbcbe7a6181ca577ce09e6f9d9c89c94522ae794f539da9586b4a911355a82454f006ec8a628409001050f4c808458020b1ca0d0c2a0a2c68c8c97e82229bc145df1822e3aa031d07870c585590e5a98002305e80a14d810104de0882c443f44f1812853dc2a34b832c3206a01b6b9b5384901137c736b7172827facc58994ac25293520294c03c058e235788063726b71028412f8945b8b939e2270aa56aba64505528089a208d7171ea051e61500182919028a15656c112233779a4bc86a2ed9a39337b5d6aa2d691bffad49d67ab29492d33acf3544e0da84d7a12a247bd4da5557db60c163956d5f65d548f3d1a27b6c699d1a46f6a0a1f681bed1d22acd8616fd5b1e6a37b4cdd45a5af07cbfffb1f0fc5ae7a6a04647f61bd9eb8f29570593dc7cb9efb6a15f61d361d9c3d1c1bee6f2ffee7bef2d73b7249b7ed45c1ee82ccd3557f762cde5a61f67a4f73c719d94ee276becb2dc2e787da2b828b921ec054629a59cbb908bad8129cc293e6839234b694dc100fdfa354cc1004d210599e9a3d039c4400af5ab04533845b8befc1645988c6b1d630d1890ab9cc668118d42612f9a25bb8b36a1c96478881005a241340a75513af3d122c610fcb58e4b693df99af7c3cb9fbcd12baf62a9601046eab23605ede5ff6490f23803bfbf8ce53ca475ecd00abdba256c5c64df7e6c19bc8e34da448ae9b76dfb9ecb32bd1005da803617f5a13d2e7a0d9bf60ca1409b6b13a240148802d58069de41518730fe54c8e13235b55699c74aa552691df936725fa103a53cbebb1b49af86b76d3ce31a140563ac6943459a0c75840292fae165dc5d4305835a14551f8e42a15028d4a3c42ef058674452f022cbaf1d53ea58134a5c24aeb49aab75ae4be6d294f86b3670a1e93794d69289a9b3b9ba17e33a73c0e93b14ea04b68b1e15fc79c4a8130a0814b85df434ad045a15cda5b950272018a34e60fd012b507d02561358a3b888da26e66216a4b60193e4a20ce5de4fcbee4a24b6972b8e4b49d334fe202ce1ee3ce3297814efcb8a288aa81ca618818915d59c7f4ee8e7eade278252daf70d50444e8ea6c9b42e5a6b9ad46477b78efc0640ee17c1eed15d3c573b0b6802ca10ac1a342118b0ee4f7141177c047f8e52da69dd3d651fc0111e71708e56adfd017a62a1e0ba852a805e3e2fd913495e603e806c2d135aa965ddc55621598daa91ac6182ce770f8513173c7e96262071a3eee1cf0013dcfed85cacfd6a02101f52e0f1c1b13a89926feb483a6be552bd542f2a18d5c86a30988f7bedcf932017371c9ca04b4fa55a152414cc320d65f09caa91c70e641a4eadc52ab3a6695f3b5b0b8edad7fab48646e8d36c84be0c7dece2e7a258051e51f3042ed81fd53af2270e3664d6e52ab746f6c836e16f856ccc76b12f4b743bae0a3c4aa2eccf7d3b60f93554f0edb82ef4442ee64bca826e50c1575b9a34fef762fc7daf2dfdcd122eb246ae2a41af646b47f6b7437696345734fec382a5a86291b25b97a624fdea5801d6945c74ef7958f7f444f09f5efb53b833450b89a0de0b77683cea7762b42d4cad703abdf7db1612f1be46b8d3bdf73bdc9f9e87459f06d8a20c70880a4fa117f6a4b921126d33030596990060a8b5b6860aa64040eae33dc059b4e70398005f70b3b40fa4c0a3a6942d50f6d7f10496da52cf5c591feb7a6b447be6aa5941403e990a65b7ae49e3456009735105772b645f3554b07c93c9ba800469da130b694f0d46eb4a9500dfef3c6a43d675b10e156029d3945c950a22ff6619e2048179accb457fb10b1ee72b48fbfb5a98d2e1e2e5bee3c2ba46fb7a3f72713687402993a10e2b5806f5ac28bf8ab29c6932d024945e694341ad23b52de00d0bc66e79b4aed1bab016cbae25bb36c4c5ceb2dbe087e71b5a24eb6a1b7f1106587a80c99537c8960f38730564c62a2d32573404d709a6507aac2bfb77afce8aabac7559976bb321db3c55064e9231d9922f294a9252ca2abb674584faa6b2bfd4d22b2dfb7de9454b2e791699ab796735496099c77b5bc7efbd354960c7315305a75670968c65291bc23847349149e09b455ac1e34b59ec35cb4358846c7ff36f2f35c4fbce4402104425d6629960982b1bee4c21727ffb1dfbdd7bb8b3fd7ddfd9e9c254fd49d34ba670e786a9154eef7daa86444e210d532891ae69d8def45c6a05fbf789d8b0befd66d190c816160953351cb2d54d5683c0e317699dea2af9b1b691609c02bd92588a8ab264212281041e35353266050c96b01712786c1896cfbd922c90342ebd18bd20fbbb20d636fed26128e2a2e704a640d3f87b21823146111a8bc5700db86e01074529ed5b0b1f956b641d14ae78424b699a88f3028f9a58971c4d76b7b33850b2b40e4cd5bfcfc3ea9e87c5b50fbf59c2a348845392830c8c58dcfb982b8ec8fd2edc11abb0ee0399ab7b939de59fe31f1698f36e1cd99ad65af5ba4d60cea409d2463542d833f53c1460f710e7457923087c6ae10d6c96067a6eef041c1d707db94ab3e2aa2a8aa2b5da0fd01354e5d17cfb028331213341134c0cdd60d63120436658424405a40e63d4f5e38a640fefc95e46124eebf837573813d724813fc823cebd17e7a35afb4b2f058b1464c4401fe523f38c33720cf48dc4d047b9befc18be8618e8a3d815c3288f6de4375f60a7b118903abb54dcbe970557f5bce0c2926c82a494fd6fb380e4480dadbc99befbee46abb6f6eedd3b81666e33853c5cc4c10177366aa1022c486002f70ba55a6b723340d3f8ff35326241e6202b5756659d93e30306194c20a1c1c46a8eacd7d61c51f0fcf1e79cab6b6ff8f35bc7c32ff09813cb929393a3c93423b86e61054a278039d1738220a4eeeeeeee1988f0c8c1e9dcdddddda7114769350a40d723ad4b5dd29a54a4aa94fd6957a32d2d0332697ce6fbbfb9b2f1fdcf2a9a9c8d4ce995f2658f2d7f36c04ac1bad436fe3360459a34b34953c7689a2411341b6a4880671eeb120dd05931a064d9a06003992bdc348e42e5b1633bf203365c318535f35ccd1158fe0cb863e367be8f6a40fd6004ab7d84b1c16437daa94176d1c39a62fa1a709cdc3003d614fbd54bcffc08e66ad43a60f6b7015630b2572fb2e370816dfccc8f45e6cc88082cb5a7a00c355b1be0ce14223131bf63e377a61091f9987027e6657e87c6d7f829a7e4d9b10156d971a76fd6cece1422367e26dc99791b610a7fb3766c7c0a03a1e4a74ee10a92e7e5a363810b53355c41ce30c1237dc61972db5ec8cc47669ee19105c8b000f6a47130ade35ff46af6acc4f03226114bc67944f64722b3d72cb367ae6c194de3ff011f1db2df1f228cc848e4388f30b26170fc8067586535f6041ec5ec3eff82154c4daa4693c69f0628632a128e16a6e110202eba37041ec5202d6f7080c76a6442328171d5ac46d5a86aa0ccb5fafcccb4504e2387d6ac080c3e4f62f07fa1f5d3491d669b15c9c9992bba75dc166e81c79ebd00c3f6feb2a7752619d9c3c8eee406f96b1857e5cc55e768b55babf61380256796b3dda206a8742d5586452b15d50000009000b3140000301008074462b1683c98679aa63e14800a85a2466e4c1d8bd32487711032c818020c01801002000010012222a30477a81c02289dc2c21340938b65c1c8513a0ab36633fdcd2eacd4ed8a14049942bc417836b9cb2ebefdae51d58aedc14fee08d04028b42f8920e247559246d425512767c73f9ab4ec0f1d6d0c9c112ccbc835cbe46818c7603ed2d60856c288a8cc91d0c754c07b7ec405dae1703e965760ffe64111cdde3577b21be48945f9f1208884ec84a20b1d242a902e6231cfafc0639481f3ea30e440f016f5ba80826fd01b4316289c0ab7ffeda3fa26de71cae6ebdac627ae980038343a7936f53a5f178780c2051fc2c77774668f86ecc2bdac394d8003379358804d4502f2d8cd47c325029df2c89bc7e2063c6d250e60292db7ebb3b41a752cb993c72e07f808bda210705a8059ef5375bbfd2d2250bf2662b07b526436ec668cae820d6603dc5fbd69d8e8b548cfa259cd15bc9712be3fdf91570234d6de2377fc56cb107acd55afe4cdafd79179898d9ec43bb1f5ca42f7dee5687750cb47b2cd3c68e878fdac94ac1dbfb1494155fe1f837bc1fc5914f53b6e16fb71f3268a1561c6c59609920cebb22b051efdb6a827076d83cfab7f173619118e192d5d3316f84d83ffe5fe9e99fa4bba3741f7b0f48fb9965dac39cc08b6b72653a1fd610d62442cb1b294719f3d4332b030fdd12eaf0c0308ecc2ce6fc8d24c5a3b7554ba95625b3583deeaef4653051c73fa9ce6145006445eac5ed31092cd88dca22528ed0ac96aa8fb18e1956054dfae8629bf8797057a37ba4d606eda92d14d2d088b4da1588d3e781b9bdccf4491702bf57934d767313874f415d38771453cc207f4d975ca62e4f7b7452487b8422f4267baacad9ccee75a3a956311bed3a6c6101c6895d75d6bbfec9bb075a32ebcb6980cac33c02d9cdf2f3362f62f55e75074e523fd7609a77749483b2f99a352ddcbbea4f7fc1e2e3f983921677d17eaceff09a2f79376e9c3635c180020f7e937c67526e8477ad491b6f0a85e36abec7c8981760e7c4988b184713ba3e3c63e2a9d806b9f761d53c7dd12930ac0b3e6885c172f971231bbe823b9684d3ab183bea5c07f31c7f43bae1bd1a2e65e8d9ec47fd1ef25e2c5eeaa359cb46dcd81ca85e312cb11b1e88262cbd19e637b6e29801b4c9ddff035b27116350aa5e88a0dcb5d657c0aef59f04a7d4068afc6f718d469b022e3b3696508398274c17be9370f4dc24dbfc173e8cf2e643af888ee20bbf694977e7a094db64b3f060216b083bba5dec948844ca98e40fb2ec70ccd8a6ee761a40744efabaf0590c875d9330b031a16589f54ac974b10491b6d0ac6cb07e386c0d925b52521dc9bdd458cc3f2780ab56792983b98213ceb4ad8d8feeee7cf2a1c161d6ceb155b948cbbf69ae2973953df9cb71c4513e84739446e934d225781b2ca4a84468a0f5fc2f042a7af4c3795e2309be58d710df69bc17df3ff6b6ba0f0d176c40a1db2319ba09b4c0cb4c34585c454b96a53291357802c487707f9373db26498d598698b998e4b98459e5c3578ace592301290e411823e0b3ba4e5dd37c192ac447ad8aec7534ffcab8926554b15d71f076fcd38e6b0e28ef468ff9706ba0ab0cf16af4dfd4a431eebf37c9959da5ac69b140594678021641d2e86c00ca406f608f57314b98646f8f65a71b1bf9e1cb60a588f627ca5c5698dc212f118879629698d1a702b8bb823e16300b30d8fd3e587fe207c127b8e10519f8849a51daf6bcee26bef4741517c0075fe39a5b479855fcd251d6b2ef6f0b4c8f367edc4ec260e1907430b5c91fd424b2b3de536e61c7c7b6e2c9f9328f06cd93105ef50200624e3ed2353b69de34ea89d4d4996a77a85174c6afa46e085ba3d8662408ad717cd19812f35e2bcf1d2148cd39ad53c1e573c0af8b65d3ad4d32af4acc51242ffeca5c35c53a4bfbe1832d92709004094182b090ee02b3b95ea8be22a28c824147cb6088cba3cc6d5c9d665b6a6d50d980866067b25c08c70fc2bc8b0acf660ad62e01c3dd328bd365a4529286d10e4795b20a0f72301f1d4129ad2de1580fc9be08b93cbcc8663dd14d6e8fdce93fad0d794eb019522b559306bd873d53283246a3ec594a6a50c62179bda52930ba537d834ca2df7f5a93789b80f5438553b68959e638c72c821843665959c5fe375dc2fa698c8831c8a94e66297679bc97932827a76bdc22e053303be19161a2468ef45c950a984eabdd87d74e975fa79d7bd53616082e05247c46ff8a8f81a8bb4d6fbd79d742a2cec02ea3270e2e3a169143b275887a61b12b0f88c857c2e71ec8c8a5d2c2253339bcd6c9481fa4613646e4a304f24838d1f91beffd1448ac58d00594fbaf24f9501e59b5a5b877b0cce5e567b13c46fd21b2f687b9b34678e081102751d7abb81c781f463e60c03885092946880e2f3ea7c60f440a997296b0fd30a79e7c9da912087f1ab330c92a479ab920c5ce3df35169108e4e810fd680804b9160f64d5d43a9555928640efb44c4e4b166a2f329cbcd11930593f0e4416dae72a7ac3c04f2c8d5c75904906abcf5d99a158ca82bab9298260c381e9a54d239f97f2b1fcc93cd404c35f74167cc06f277a48aa99997c4b6b2632093546ffaece213492408f28b3ad3a0072d443d4adde8e491aa4a591278c17fba36b12ef39dba8d333ea62af41e7a8cbeb78c9eeca1d5253706f3e0d404ec1b917fc8d6605789d4fbf1046bd5646a601e1204d5d19e8963dc04a8bc62b0a055268da983945005cdad4bdb123ca2891d60e84de085392d6b8e81a43261bdab170e21a1c3b71d277d2a2a4deb9cc68903ff24b7e1f2300e091325e5f27244ca719c47aa0131b5ca51517fbde1dee3ca31f3e5e68a5c1af03198693271d4881aeddec7d588eecf6520a79b1fa42c3bba5efe8966c52842bbec5e3be9a0156c8860e70c72d0b0fff3aa5c89049c88deb9f2429b608497668015bc6e018008fe2901daaeb651e0953c9a6bb21a9db9d114b865afb6399501f90ee885772f829cc1e3b257044e26950932166c994155e3876ea1e9e066c3cb4446786261698370df3b6eaf40b1f23fb04b3ba906726cfee1c0dcd12a94d2eb8f3477709b5391598bbacbff24f1db5f192b1e1c26a705cebc317709a6a3e83931826cd15d33f6daa70fed407e9bc450e30580117dc27f9ef66c1610bc879753119f49ffed43e501498bbe3179372dacb73b6e560663aecd84fc1b9614a7ae62d95e230e05b8e118658ce53449cbbadf7820e08520e82fa7338ba036509853342dcc6ae49ada822673dad5edadbef0d2ceb671f1e3856fd641cb3f8481acbd147743d97131f10af14c7729713715171ede75886037933f7de6560814448bd7b16ea53c1dc7bc01a5c6cfe80a9c9673656d0c106da6f36319436d339af17168f883a47238168d9dd1219301e5fd54c5b257b9c98fc20c61467a51f0f0592ed03ae857c7fcef7aef7ea57efc5b56fe6ac211c282bea9edc07c3c0560d6daab3e2b8d4707ce5907234de7b041b11f2fe45e1d25d304a233f2971f0a35c7d5f108961acc09eba64b740e4833d34c5065b84dd71545213bb1fe6c541dacc70f5022d06b6b12e0c4f9b5f7b21242b38813f355042a6b4a6a91f59d6f94f8a4f31561646522fbffcbf599b9f6a44c38d5315b84ce9cc6ed5e729b37dec39800b4bb89476a87e3e3b4587258107081e0473511a3665cf97c6cebdb761ea803a44940428f68b90ad3b2491132a8519281eda997c63751369db05309ee792335222df662feff5014edde5b018c02b19649c038331f6802a267c73df309251d87b901fe03e446b3a642156791a0684b42dc0826280eafbb4c280be12717c1c8344491f394e2e85111e666c86a815536c9eb9a7c45c5082a1abae5a69046724cb74f3533ab71434d682648e390a99022424c8af05c13612c7a613d812e9851295f6d2b93a073e9fc0bf224f053e888f510a8a51c4b1e1764da09fc63bcfbefbe45fe6839e7b1227b25a12b6a2ac94fb4b59fbd9b61d9c6af1e952265d1b3d9f45b6bd3e041c4e195c9295591444c6c88001afb8db547fd07e46fb70d0e6c3c290d628f3def35529515e0d4d0035508a08d2eb057aa61e497aa868f0ed26e2f71f37c15f5c6eb08ff70cf2a6db0b8f467ba402755d757b86385a0245c1683273ee75d7c675a3666da51a52879e581a3e067b48148b3ec1880852995e7e0ff8ffc9f4c0e4ded58b0a846ee57c0af6c4b6bdef33b629c7fc310ddb286825544f07c44f4e2bab4ce4aed7703314d2380f6a340a877213a51a7d0d6f72f416107797f07c981054ff33a0ea7320b210f9cbc0647aecff2542977873f44cad258e7709fcc05c1264a94bbe48a77e066a00355c5ecd27ea7449361c1460862b708214309068da836afd61ac811b7e1e1be2e3447cef4f35b41103144440c6dcd30094c0dabd61b863b4aae7a4d25a5aab0806f40798aada7bad09ff7b4a3b015214c72c4802849c01c6bab3ff303a97505dcb4101f19bf7db83b7770f319a171a4f58c597e9167f03bbb8604d370b32a36f12af8d2b8d13e53fcc98fd7fcdb02a98a5f6451b39e0dd2035dabf0eccbc03d660aa1e8ac085e5f2014dfa14d94bbb2c2339c15fd25130cd633893d6975b0cfb4ef32b95e2c57be0af4b9bea5960cf81882dd1d2a6a8d1b2b6962f5cc9541d3c6715b501c6401082278742fe106e468d96e7fcae039c778fe6553f52e3c54cdd9a0daa504ee170550caa53e9defd1b640268d822b891f048a4acd57aa13264b137d92f487e711c57e45a286304d2fc155a17e36cd9ec22b98364a27b2ae1a88d0a786b376ccb4cff14e0ae280a2c55e7232b74294f896db7a8b1fd15839f1a46458ced836ef8fafc90abc47ff571b9b92f68b80a2cc3905b13ceaa5fa17ce0d86cb33bdde1aa336db9e36b8c86f735f5574347c3a4c75f86b24520dce5b9c2df9f3b56bea99ad2bb8449aae782b025947f52548eabc02f0010fd69c23eac8302057d8f217f6d3942dfb81713f0fb70e93ca1517692e421230562e02c2c32d932ed7007a0046757d070ee50168d0e4af096e1d01ff9f2939d3eead6d84bdbecda5d37ca1b1c76c2e7388521f1801e1dd6985b876e99592398686b0fba22dad5078f4dfe00ede07e4a0c58d1cc3b82c9f41234a70d1f9a957053c1eabd0af06ec8fa0848f17582feb3737ad27f8a7593421ee9d0bb2aede4bb603c138107407f8b458fd1d0a503718d6d3559c59c77ec224df53fa7e9bd512640df750c583b6a73b77b8efe088ecc1ed064aa8c5db0a445f010c20d42bd0f544d712e550112e99640e2d697d6299c1943163e3276ff27c878c89768fec34e96863092e3ed797fdd5977fea236003fd52848c211242c71f2fac1542c0f49e980e30f1fe74d4576d627a7aab4edd333b5b45c56943fb69f8c277f52cfa351ee1702cc7549f1d21b0df2a283dcaffa2f3aa69645499a76c036ab51c35024ae839f848c927e91c0d9f67f1f0c77c1b9986619fc52d8a6c9812d7430c5a0bd3163383f48ba9c79969a29581a7e60c658d5a32a3b34c9ec4224cf6cc28346ea420234920a5e064a628e9f67ae25825e2ee304f0c6ff818324c2c1e274072b8948a843262c2efa6a1a9a1ffaed2b119af8f1dfafb700853ae3ab9e54fbab2ff79e0707e8a0a72deca34570b0a7f7a38e16022785a24aaa6ccf99dfb25f28b5bdf6489c79c2c413fb805818124d5382cca1cce2be47a6deb13ec947a382b2ad18e08a6544efb1132c309d239ab73618ce7862e73de06d8ff1af02603f700f216f5267e6eeb6df670ed5d2a4f86a5defd053932078c8bb2f278080b8128a01c7dd0136cd26433e26d19e5cfdfb984ad26ba6bdf0f14e13128ba53b6ac54d94e2e0a67386158962b21270fa4a4574567482eca61a6215803dcf4b8605a0b3ee36436e09b563f5bb65f3578828540fedc837cacc354315078199e7869d214fa66e32b25476fcd7aa667195186b2602ad6c5b807c1fcd6ca3ea16a9991f9fb0b429a01f6b5f2e2d42ea8441c384c82e08fd3a271fab6471e9b0a04a6bd969fe7222cb1a2f512cac4a7a1eea9e594c2f94642ea06c7c4c17d10e44d82d4a66fa6bb39e5c5d1c9335d55d60c115d398e91f6c8f149510014cdf6c5d2da8169eadfd6fd15e72ae902769e9d3d7218fc3a7e47ea85f6792d0965a8b17f784e24df7b3d501c99186e1063a14df7561f9eb3813d14ca21a07714e32915347d635e6d7c34bdaf2bbd604e3eec5b431b06c6969cffa5d00fad51ca3c6240d3ad97d3fd8db09ee466d1d809768b98bda39377f0131887c9d439cf196408f5066cd0329fd822a19c16432e2222ae144b76e33d0335082c147566966e5c4e8d62d08e760dbc8808665ad275f9ff2042ca21bde98cb4f815f9c0f644b8219cb9bdf6e2ae6811805a548e65644025885773aa783a861ab7ec761cd112051d1a68c0bcf9aba5b908fc947d64151baad001a72e7c39078272d8b6ab0ef2c8d0309e56ec101fc4db0a25b67bb0f57a9c6515a0f8679dcbf30e25c6f48e0c0c9f6bc9c7f04e5b764609396a1677d5847a422f1dcc6214f72a45d2d10726744e568cbe44a8713ac12df123f4fbca2fd62a781a9e5c941fb42857db45955ea3ed7bf739b408d97271d23fc462e15d73aa73231b382b339cf838606df8dd3662a067d5dc011fc2da95f4dedecfe06b5c0c48cad1d5c8a804b37969b979e6d6d3b847dc8573bb43ad67c1d9848a8e2f1362b9635d7a945e91bdf5eb3c0e88cf794367969da579099a13cde543ab3dc7e0667d1dd05570fcea0906efbe6af40446e2a19011063cfec211ff67e66957b13fcc45818572263006bb6fee47e1e24b418bf7be446a28a8c9e85c1659bb3590719f24d574ac3a02604f214d03fcb828856deb051b980c7dfab1d31d52aeb33b3b002f5ab0f2f68fff746093ce2187734d70a81629358628551d9dc99c323670c17f6656201ec8a8343f072d4d5a58253622fc7d61625bce644912aefcd56d1e0683cb9f5992e907fc0b836d41a78a8d37cb708510a4d11026db6815991d7416f172bdefe88f231485c2f58cacda2324d9e8d3a6a508849688b8f5d6ac6f449aa397c13e63cff40732d61b9397f0ac2ee030705c26b6ac5b27c707c059b3c1d2b934f4aa61cae8306e788015cc6a908561436aa58615330d2e07c0573e3d9f5470fbcd879322114f1fdf21fcaaf258b4800f02f8cedfe3a4fe3be2a95fb6b3ffe60617f89779e1660abcd171c0bf1e019341655bcebc7959d266af11a03045ec871a29debec967ccf23dbb2a3491bdc436fb358b334dd1deefd349bcd028e696a5b9d2a894f134205da346784b9fcb0a881442cc51e4d06b01d92ce1cfa57d106ceaf877b84d8f713b7caa0eb0e273ef755c36789c0d10e2a42d419c807941c74cafecfae17c6675df1ce1cf3569e954eb1cc9cbcc45d3bea49c8b0586157748d04e8c6c138f82465b4a733885bf1e81a8d35fd58ba8ccd1e68d0e6ce65cb55de00892ac14efadd415b50564c9f2e1268667abcf5cfd582e9e4ce923dac1a16854ee2dbada72da3a2440bc494158eecbd52aa57aa573995b2a3013c13b40742e051055dbe48c2e0ede899b7c402e59013dcf1c7001baaf2e6904ccedb063d9e7fba096701459f8326089eead8b9c3943dffa008b624b691f2eb360ab7bfa9eaf3e9a3f7fed8ed592a1be6d0c0034a10149c4416ea066b787e91f7e80235d25b3ef3967b60b584c67933b858c999c98c9a1fc30dcec7f846c3d36346e3b7caacc18677419c19613a076d5331b2f3130a9f4d2aff612e904571e042743ed964fef1ed8cad3c13380601f69d1da3d32d33f10430f34999896a343b40caac604213d18025b31f5ab68d35a980b72f226593f85bf466c9fae612f0d8c0bcceebe1d14a780946ebe7115d04e3a0018c456ef8c9f9e99fbae35fe71884e3425cb7c83bfb004f42843c484a52e892e2694428d4c1c57d3f6ca6e329ce6a62a03ecc46c72bc7269b7a39549bc2e8281fa677eeb0a3608437f6a071c3869837d85899b86c0a89f69c19ee02f45bbac0e5a6f644c81529776900b610b078e8621198087a42008d3afcbf35a6de13b56b7677bcfc42e9ee5895568db84744229cbc01fc2e84144cf7eb71ce36a7d458946b003b1f19824e841beaaf35581070c0a106395782b711469abb3c166a94a63ec3654ed673723a1bf3d406e53df787392985f4ee1ed5966611e0ca3c4d59029a6222f3977f8b3c30475a51fe7fc4245d5ba057f379bcbbf709d7f3e7cb116ea722669abee0b822dfedd05c6552b265a7a2a6f57e9e97797e4727adcc54d0a8bf90d1ced33476020441e2eeb1e2ddba413d1e7b526a0563bc04792a7de82755f6f9bffed1a9966f465701988134189ffe13e6fc58daed97205e23015a3f0218638eb824d7fc0a020b05ad63fb418dff931b0f156003d45b386291d1cdb60ef4b3d2d000bdd0609741bee945925508499b894ba6a6418804774966a1f03494a0f7ec9e1edf1d21865d2f25d3d1d841bfc66bac5d568cf5c8b411e87309de56be08fade1fff95f41f98d78798e3b5728db5617dba7f7378ffb1e053c5929e192e9a5ab703b482a26908aa76b1e4b159f64e8dddcbfd43773fe3d52d4732aa50768070d16966276ea1b95a4eb49e6a1169af5aeb9c1f6d9a5dd97e787f7ae3a5aa83bb97de5de643f6af3493bd0f89e01aff995e4c2a98c8c9a6dffd138a31945618caa5d20107426eb8e3c2c2ad331dd81af212790e7a2f4a2f90ae01752592457872387b702d0593c38b531bf0eae6cd72af1cc6f4befbaebc86e0cc0b0d4aef148f6cbf93df0a7e53fdb6f190a2e9b69f08fb8251f99a5894cfb22dbf8be6511104dc16b44e953bf384210cc0be85a2c74ffaf03c988dcefcaf943265425940d6e722dd235c8543bffee0ac3b09b59241f5e0be019253dee8099350ed93550407f9fea247046b70d748df5da8d6f6c0ca8838584d7a5e885c890049d08ac95df13b2087e7ff0c0f19c13c2c42cce4b0e5ed3229471562809a63a2c0099e48d62532a69b2c0d34b8d748f1477c3a321ca2accad291e07227378160fda0624bbbd48f97411ec49a107aad88d159abfac1880615c365905edd26529ac89dd50b744d5767abb254808be809adb66f38472876f4a060c563f0d4603dd882405cd5ae4e7d0611f737817b8c83874ab6c5177e641b149d61700bf1859b5825d7bb320177d2d8f36fad8e287b3f66189ec006d0fe19b59d3d7a945d1680e423556eb490becc354643be52aac3a5a88c202632f5cc1fd00661289438a6bcbc58842daf60b837f3901a86caa61f5af5be7ae6b8db5ecadd40787cf66d1d2a45523f01a3c2af085dbb2a3f9dd97a93329ac5bcea595b75fd9b4ddf75de9816f311d99cd166db42246afdd29c7f77c3e3d0d1dc0e91fd76cce51fbe499b4a933c21e882b32765e8ffc87158a277138eca73a213d1820f5ac7b7264696c06cd941ceeba21bbb547c7ddd8e3c313d396403a650c4d17b3853e6befbf61d79aecbfe7720692e75daa67ca7daedea95d96ecdf16ad351c7d37e04e36b893ea40a35dbbf08217acb2aa831d4a1658fa8652414b5059c6223df8c0f0f3bf650f91d3d73268101e0fcfbed7292a40a80b13768c14952443538944a70888b6d085e08c2b68991c8f0ef2091c815bc9b055d17880489912e41176415106a41cec9b8332537b9f4223f7b9ad8a937c4cedba8c6a9318cae7b5a1cc0ed6f06385d51750e78f2c7a9f8afd944b14d63aec3d9177f9e28028562e09a31551d2386b5e782861951280814984bb0549a2285e89558d844764317531495b261a2684f77a717a01a7b8048974546c4d1cabacca30a7bb27961ad977dd46a713da3f969b54ae75e6832b1a5590ad32e52c6ad2d6a8613d8c6d8c35f33f3df66389741f2db4cecf50c121173f084e651ebdd5679dc06b2c69000d7b183fe26439e117804b289ff081a37306690beb955ddcb6c6547cc3b4fbc790d152c07408767bb8cc0ca65ee1f74cf33bea57b9ed0c42bf13753408efdd6be2a5f3ab08ac3119751889fa0f77ca7a4898d5d84c928984c24bb95ca05b8a0cfc8392ace8875ea343b903e99716dc9ceb6bc036550988581828a2272adc6a1cc3562dff671f7b3fb8754ce818439e61eeac9d3b7ee85b9c616656b03cd08c87812ca0c4b0dcd0a14bad61dcbc27e157d015a1443f2970e0bdf2189bb2c0a8b95b8807544b5976770d42fa306b1efddd238fe4e92f80c1d234a7aa9fafcab0d31cbcca5cd08f89dbf29d336d77e9fe7d20b4a69ce3ab1aa5c0f1f49d408d328157ac0811881937c46e45641ee18e62e1d9a47507d7dc382408237a2866d601adfe75e9806565e98f9bc8b9e0aac97044c448035441ececd2315787107b8536aa4cbdb3156b080c1efe3731b02a6420739480a8463a9eb0d041399d40d3751f6ea40c484700c5635da2cfdac77a792d31f1f45036e198284e787125c17c720c557fd97fb26d015270b95a593e7e3b4d97cfece714a90e3ceb01b1f856fda6df419cf9070c1e62ad3a7d4bbf4cecd50f0ad531519a6ba21dc3cad65575ed6881b66a2ebc90261c82eb72e031b430b6fbdc9636cea7a4f500cbfd760c1d11b6af7850a9a3fa2c5c71979325bcb4e390e2dca889126c20ad508652c851f83fd4927acc365bdb5850080fd57ae1d4395b63cc69584f5faeeadd5b1c1049159a03b1636166422acd6b7c5588cb1cb4491f16eba5709270ac7dc7961c92105bbc183ebe9feab7833f6856208dfa22ab8bcd2309932f9f3b542558e7fc65703435136766d63893e34036b083be5c1e87f65b6eb3d8488579540af29f4c798d7e4af71109a41b1a097203491931283dd81b881b569702d809ef13066fd8cd9936d96bbedffc242b8f1f190f51a3ba205c1fdf670877d39b1ba50438536fc66d44b9fc98aa8f342991b959079caecec1f8521d97f2a2acc6bec08697842dba4f2c4554091d6c4a3b332ee042c8971020fce78f52680b1db09a971a780a9869a6ebb639d9f5f2fa4443e6a0e424320ba338436734930723e8051728a69c4aa530e4f6cedcda806e906a9e4597a33a34f9a988fa082fa807629b4a6f2535ccac5a5b0d560ec05344c75563ba7739325771c910ce3391ca6fa10a90524976a9a68c598cc94dfbc217550bfd3d45dac34b2920655217a42f66ba12c8e9cdc3402aef7ded87f744a8cbdd24d5699c5d346a42900e4196434b292a3787d6ae0897f75b9c1e1dd11054a515e68064831d0f389e2b2b8755219563f1e24a284a4d939cc704f34f5dd1af86d9a8d3ef52f8f22c38879070ff9252c7eae8c40f68b6aec61176226590a626e27c57e8f211de83d5c8e12bb6e57e8ddf81647571729b15577c1552940113741993ff32b880a9c50495f54109ab1125fbb7774e5a9d675e91fca57db6a2590e90b5a0036d9e129a33e7121509806b6bf9ccabe2c358ae714dd43b1e122ae2b6aefbced5390bed58f8ea18bef081386f68e85d55081c0c8b1ac81bafa9352097d5b1abfe6c04b9bc81a33c66b7b78b43d5307938447fb9553105f9ee29149b5ed534afd558ed51ddbb02cc6bd3decbd996b465ae2106ff5ed33547c4dc8b553b42ccbe3d86f41d988a73b2ab25bce244e59d45cc47938e7131134b08568aac6e2b6064d2ff2eb4330be5c20431c934c1f0a34a12ad5c3096cc42ab3c9a8ac03539246c28c50c7db7535935f2b0f20fcf4d327126290a289bc0cd0d33eaa7dba05aa86cb81884fa8fdf8a28dd250782434a23855e97a35474020d349a36774ae7e4f5d9e50ac4316978186e1a8a26c514eefa6405d30356ca514c91e98b67021850ecd645522e514ed15db612284b8587306c589d50866bb31dde524fd118154f5c49d6443ea08b21897d8d09301cef498618b078f39ae6c6fb580e6b88e404d851e57bcf9b97ee2e34a8c00c895f495de100e26337d5a92895ea729668f01c60cd2eb44ae048b0224576c12c1e5a863524ed53e9bd8e7240a55efc5faf0ce98cb855edcf27ce375f1970ee777929a9f27c35d8057c0a9dc89afe654f7aba951f1cb4ab805eb82d0ec1094a5b0864f411b0a987955e0325e2ac9480c212da8ed7cf899f32a155e284d3966c7a82499c1736619c4fde1a8c1b38112f872020cb282d637ad328624cdf56e2cb052c57fa4711674b12323a34c2fd816e9fcc819a41519ef755e87af0ec481cec63730dc8be3842f60caa9b046bef4157ccb2dff1c36185b464821ea266afa38721eaf4c0395b1c252ccc30270ce5e75b36237494b31492b5041e0f9089bb0ed49412797539f28c5dd4ea5c7694dc0027069d05ad3ffc450aa148f7849d62abd32170536ed0ace24d1baec1c34ba58827e959c64df2c7cc4d9b21c7e27a9175ce07664475a5a4d8895a2b5a422608f20b2f2d36db78f254bf02b479d207deec039f963f7716609f25eab97ed914358d07cb90192c30437e4b0d2f22407a766faf256011a944f35355d4a78086b35785e466cf16191852e587ba70103ea48a8096dd138d34a1cb6e96912db1c84e223e2948e69701ff0ec2c0b632541dbf0f14603f44127aeea05b84ef1a3d9e791deec4b587dee064cefbe532d82d1a409f33d218742a1bf75ab5df32512ce076bd399c216573e4465f2a815986fe42c755ba398d15317e9aef989ee65ca0d8bb5bea41ff88fb690632d6abfd8869a1237d4c8504e16b05655719fb414045c99754383038b926f02d840807a41a5373bcdb3958014c9b6395457fa203ac1482ee2d826fc5f9685aa135e087e8d57c7de749fefcf2070ca9a6d8da057df11dc65fa18bccf0ea908bbc4b0ee75c63c1af54814a07812bce6d8920be8050f1a543c6ef50c27fa68756f3fed970c2a6be397f87a08bc0a42ef564754af6f7dfe250613cbe444d3631c65819edb75c10f402bdbc91aa2d1e1a6ef3a834292c9455f650968c1e2af68718ff31c2e60b3e27c35c997fbfd89fcb04afb6562ab9199abb7f0b11b5a11a5b322ca083edb682fd4d5cf2ce9671c0bfad1751b0951bd6be2f2655fb1525c9cf2c973c254af2d9a5438a9076057540a6dda45932c3b383a17382658cc54a3cf911d179fd7c18adc092ca5dbfdff30268712af7a232b611a73c2178bbbe9bd305c1c0db107638d7bbaec0f0c537277164dc254fc0e7ca01b986ce011919cfb744e094c6de61f1ca8019c30e41ba8afe07a34c3c848114bafd89ab9b5b408097a8255f6cf32b9d2bc4e82312a1005145b0550ee08a27ca456bb933d88e9e31e6b7481d19f5b515d7ca916930b705db0d142056ceb9b6a61ecdb0ab770aa705f9fd1fc01ab936b210c045a8cd581151b94a05c00955f67219f96d24a21a5cf867a325c80fc6a3c522d12ddd417df57464c6929999e8ae71de8a4d73ff7464acaf94a34462cdc3235b67de0fa1659c6f97c943cfa04349c5f01cca4242d2b88206d087df0d484da0c66a6158f411cda80957925b2a02212f274a92cd1297b563f1606ef05d171f572ad3b21fdc4e68aaf2b0fd146655dec7cb4b6c73b2fe9a0d40364584c26c9f146108d526142caec7f9a8229aa42a246995f17bd30ab6130464719513ef7c0d38371d140bc2b75f6de7a2b176efaa1faca6208adb3adccebd1703da5666af23390216aeecf0b451f5845de51db600781a5a62523912d903e55bc36b0e10445664187f0833f6b45044ac3535dfd553342f49a4e31751413e58e9b205583365a2bb34453c2f0d70f9f844389786474d7c90e5f0220b48783198eb4748958460f97d45e5ed55500d99c3a214e5f985ad429c9681407c60606a536c802b83021ab380c50e68a61a2a6f6aa646e96948700072755041394c5cd41398f7550aaaeb2169edb934782f9531084fd93556e81b41f2e5ea4c31600a8e0c49c3a899541961e646640a2ca7aa79d2ccd2f13015ce3923e0f1749c6200f6ea28285e779aa77932b9f2e1964c22f66bf81cea3f68f138ab058a056e056dafdac2d87ad4f0daf33acf0b7ea02a90250c572485c3ef050ad8c2ec60ee1d32b56db80b896711bd2ee0db08a8e8260282efdd042e9a8505b6560da57f89d0693411006b8b773bd43ae11b12f750b7ecfbeef782e50a59c9401966ce035650ef9c0e2f858dd33cd344ce1e226733ea63afa949d871292049ace2c59abc1fb0ca58d5a3ce66d4299eb689172c89b28043652d680a0beaa516cbba864c7de0954008e358b7cb0f099124ff6af14c0f515cb8fcd0eb919fcc40d0054648c0c0ccdf324db24015b70063b3979b2fccd2ad9e32ea8b2af0936c21e5d44c7a164e7440ef5e03f2412eec5218ae1a269b74b8ef99cb8dcc75e4235a1442368caa86bcb0b1cfdba3932258e5ac4e8457c7de5d727c6596786d5a0bb8dbd45fec6c3b06023c3ebc1bcdc6ccb667250a9af030097a7c389debc45cb7f6e6a7970ae39be3b60df6cfcc79308d2c0209fb78936f79f9d7280562640170d62073d3fbff81ccdf18501ef0b89bf12bb9a1f6e70a05e4cc05a08900ce3d1583cbf821f03e0dd37236d5c9bf2977fdff17eb1e1447b1558e24e42480e668d8ae03b0ee4c335cb65fbb102d506af29a915cfa694c33b9b187254e520eccbab3768c771bb5f6a8f3a730209f53d5cbc91b9bf803574f0694740a148c1847a54c7b0a8d1352a645e11f22034561ba5aab7903c26e22d99fb0510916f232c62e9b3fbff444d3cf36e4901660e0647d14404ceed354d7ad48d4df57aa4229afe8342bf512440057fa304a1c695c4eb16017f94184225daef07b9771433f6fa5bcb48d82e5ad899dc6a1d39dd4a0732f4503a61240dc6dab69663ecbe6a8ad12ce0996947fd25a5e5a42673a2a36525b1cde6dcf4756b638cb6cf2e2b8aef1fa71ca33c410926f0f2071185e6642e7970353f0257d91cb16d546dfdcbe7de680b4e5a92fda68c6fda8a785211e905dc80fc7612e83d0d6215406ffd25e5f242bf25b202be96e05f728fdde40d32b4dd91fce291c50e6d0311fe45130851010f9a6220d8235605168c98f0b55e080c4dbc314e3b78ebed0ed029d375506ae0296ab4c95d6c1ab366bf05d305def6953efd40a3284fe692632745247095cd62db28b7296744c24912bb395afeb9edfca043cd71b0808eec10644423e9b3fad08aec6029ac13d00ea90a7ea493e06e234b0941b502baa6c6e256ebe24487debf14c61971a46fe7fdf61305dce3e019c7a7d975ddfed913b75a8a145aa5aa1c3d318dfe837035e84faafe53dd68858289b9661156add0648b45a2784a474094c818e19821ffc9f3d459953fb9364d548a005c656c771cab979da89a27c717374a92f7d5aa72628ce813f9db783cd9f81a4a6e6e74bab281d12e24f48b0d5bfa3a65aa483823dddfa95891cf49caab1252b253066e808904d53ce752a215ecd92583f246469a72cf3f9a680a4b72cac0491092b06eaef6e87019519550f1ecfaf239467af62ac1fd528271bb4b912534a3db5669b93cd0592b7979ddd317187f3c28351688bdcdca904335982c557ab714fe7860602f7882cc8e095728ebbd556be9555d5e5f0a07233fb4a5170136f58744998f61a2f9d506e5e7720e86e17123c5e37e8c5312a0e7d8f643377d2dfa736722387abfb7f92f32f448eb99f8722075dfa3f14437f51c07ae9caa66b9aa43f494072fa785ec95827b3a7b2e64e47b3ea5d9ee1f899b53810f1f3d0e1ed4075dcca1c668cc093181d892369c3a38e4aae2a304e1e3a159836b062c213000f701c964dec9fcb6327d031ccd0a16c8c59ce3c67114a2be6e03bc438f81886af8afbe9fb8137278958738200c667a240adb8bf720d4e776ab523942c4882dd576654ce816e16441134d213eb5a04789f8fcbd5bd4829b91bf0b222e15d5db9d047d69d551a4a8685837da044ea0f05b1952d8f8b0a2b3aac6a95cc2f857b843d110cfcd91394d8f17995da66e94b3cbc4b084e42053229fc4a7f375935bd499bf110a4c8400fbf1317617ba1c946825943ddde66f340032549fdf12074b134ea82b4565d23ac743feade49c9628544bde2779e59cc1ac27259f6e7b485d757876965a930113dac77ea3eb41b7d38fe82786c4688b8ea64fafbacfad97d912d7756d50638f0d277317a50235d2cde6ebabb4b8e73ff61ab9581632a57064b6a93a8834cf488ba1d0b4c1ca5e996980f87b8e489914338da064b7299a106bce888badd8a985206399d08298bb64249d621faaf2ff3509e5838d19d813ee7f63d0620eac9afb0f0fb482178f640da3e6602d185893a7649a2a64f7eda434f8ac76f6e56860ea43094989a34f69a0c3eeda01c1814f35123db05c88d6d427b70c0adefd084f10959df9e7241a35c9b1bba2349fca2be4c58add76721b24b0d3972a88d61715974ac4e0a0533708c72e0612d4e56ac4931b9ec640191d1e9bb780effa2a78be6b5f8e367ffa674e2c3a1be4ce7dfeddb2a33470fe1c8448cee7ba9d67aff9b784dc91dd44fd2cbd9a09139cdfa0ffce18e521aca3ca0e56576d472214a30d6d7f0d94cd366d66fa2ba269a024e28f908d86694db4704049ff0d1055a831a24260ac8ea98839d59961ff8939803d824c1136b9050a5b92a3796188429fc65109c7292a023748989f043b2abfd137c68d50d027b1ad0ac56e053fe8ed550c6e57c7a7f9a7a165c41e9d7efcda8e2ae8103bcaf73359a2baa8267969d30146067172b3497d09b151470bbd5951616336a71bba795432e9b6f074983e9808dc8af5bf4ac6d2a83d3d41a7ae3af78f1fdfe280085cfa5746dbed508c13cff71bf2776b1b9432127a19d6552c6e50e12fb16575287614f865b450b5b81522afd379d8a9f844de57a0b4d1d5d98d56fe655857b1b8958a1f49ab5495ce214a13a4f3053245c79fd1a2dae21684f81c2d55694d7f8861417a1a7e1ebc8118f7ead11e5018a0dc616795792b1a1d5013e0d5ddb45f0a1fd785e328fa8789386050044ba30ac3a0a8249b02fdb97e6e7e6c87f8c5b77cd1428c1b20d947b812ae8749217d2ea9c4e45210ff67305bb9eea1017482113dcc62d37516ae40b10e5d25361597831dc60b6af6d93713ab12a6cb4b105c692ac85a1187e85aa172d8750933d9fc294bfad541cb897b2ae90d22157b17737c3f9a24b5cd1e86482ddc24c050e87d3dcf1141e900556ecbc34723c6d3bd2a951528f60168a1fe6ecfeeed4cf35cc00dc3aaa72b15cba2dcdebb05751a05fa122b61c29c4d8cd7ba841e42203d52af93426eddee45ec4a2b242ec19f39b5828bd7701390cfeb63dabe300688993b6733e4b9d43c203f08548ac641ae2b6b00ea9e4cbb318b37dece5f9b8906c2cd16d7f7ba868042c193ebc72a108cd8b46b2ba6b6730112cf7b245e04d714a378975ff6c8f1929c89629ba27b81955d520c740110ed130a2cb88e2c15bfcb286f38f9ca6e4cae2bce083ca04d225b20fc26047730f7105ede3b7dbb9e445457e2387441ca68bce565e9db4068e7a22eff3e8e25ed52ff75c4afb3473c90ef616dfbb2372077c6a4882291d75f3d0a317dc837d50122c9a34877a1c914e28709d6817e0fb51771e1319301cd43b7574e9c9f73abefa7748a77012bdb893f0a37e268e2fd0dac77bd4eb203217554e8478b59f3a2f031432573c0cb6e1dcf43c5bef015fae73511e6a16e88b80b87f58d32ca5e3187931618cf7d59847a6aa1291c9cdda238f223a79c7e7b9047790b27e2fae2b8b39706fc84f2ffff1e05de980d40568b345daa1b3b00ad4f28a241e20d2fa93293a1f9a3a9d46470daf18e23668f4fdd78326928a196d78c444bde020d1743143cb19430ea71fc3947e957d81620ccb6eeb117933077c20668119a35fad369158ceb0d08295ea3c25b62d1a12383b5967f3849fce9e14e9095cd4281dbfb534a806ab9a16e7b410a1a0a9d3f9701262e305df99d05637a586a5d97533a62605a9e8b0cc1ea0d66c74e4a1f423ade640423bd9a8241a5003646fb584a34a48a59111c6f12a336db6914e0a2e4dc1f87d7399eddee3cab54055c84aafbec284c330cc15c1aae1406828c55047d5e5fba4f6a86e0aff9ecffaec57be51673030156b90f191d4c776610de82af0c5edfbdd925894ab1249344a92a83f93f78c1324cab8cf665325d5e2a3181eeae3c6cf9c0d6f0cb6c7b2a396753634eeb1c567787e554043c16fe2189b54ca7cc1baabe6e3ab1a5abdd5dc9be15ff08288911976d4a5bbb2407304910e9d01bb277489f7eeca0810481e6feba81ec6ec4ce2b4ed6ef32de104324302b063dc9337794915f6a1f9ed051873d04c436d642843b2bc901a8cf151f09d4162c672618bc5d88ef62b2301568d5293048b7a23e6b7bea33789dca604503d07266db3d63f193b3cd13b39c95c37db9e721c55c22adda0a98b2fe5f7533d7e40bce3eec7cdd3301a6ccc587523cdf2fa0d94c9c549bea3067ebfb2101422d928ef5eb23f109ae1ff4e380ad3b4acb6fa24195237a7a4b6c39ccbfe3922c73dca5adc602a95fc8ce3102e0378c32b1e0e163249a14bcc290933685ea8f0c3cb97ecd8370ca5fcb2850f6a9a29577ab5a5cf624fdc9b38162cc9c4529106a6ea2cbee5c27a3ae2b07c37c20b47cc3ba9f7caf91edb62f28b172875d5f05dfb6d417a936078cb46998f848f3885df5052698087b7911729e2e1ecda98df600d9761006ac6323e0238d6727e9cd7adb5a0c519405e9f48aab1259fff10a2408b381aa0902a5f2f9969dbec3e1c0c4b70435863d51a1edad606c655bdd545cdd4d65236283c4e57288e785b6086f2a917759d87ab12dfdc96a02a1bd5599f78768b45549ec09c4ccc570fa72027fef4c1197e34cfa0eb6955cf9e193d67a24cc808530ca860be391c406d9115c07178c979596038295bb8b61dcec2413a7e261cec89b4469feaa4c6a032d79b66f9a7e75f07946963a9ea36df4264cafc9174ca713a4db0d7610a16e75751d756dd86e10588124be023c80050727b8aaea87e16b0bde7154c8c39006d5f89d6620ad0225c25835bb8542941cddd8665cf6d7e26430fcacdff7cd299b5ca1fcc5a09129fafdcaf4538460b806925af4f100820f3a0d87d126ab188b70f540d7ae19f44689c7aaf0205718cd69c0b4c4e515f89efbe1aa22b32c409a832082af46f5bce56fbc48dd6338b85824530c1c0c04f86404bcb33f9337a9fe6a3da6500d51b504aa9e1cc8fdaee169176c30ba00b98e853ce8a971204cc85bf84cc21ad9392a3788cd5c83c2ca53b036a3cdfbbe6798b030ad490cc998b3913789e43ec0463b53a8436d47731cb02051daf53344c12232bed1fee7f48f877616b4ed3c3b9e9df41acea1a8b80811530ac031ad051f15d951c065402ad58b12ed8e4becb0640caa9c4dd37e3bed0b05481820aebe08e6157f89d4b5b42ab15b58de6c61050991d6b4c6071cf1452dbb14481e462554d08d4d010b192c0f6997f7f57bc1eaaae7ae4aba69bd4e232af66cd2769781cfee3dc6e2e74dc41cb91f03f93e161bac773ff5561b67fabb1fbb0db2a5fd8cef7d0ff1d56c5f4670b262f2105e6ce23a8813c1fbcc23fa8b4560aec8cfcd284e30fa9f76a96ea0c60633e04c84a9e1f3014e28b24da02c949cd4137c071666484c859610cf43199224012afbb6509bd4e7008ad10ce0f43484a93abf8081930ecc74ad05987a990f439d5f22777a77855c4244aca314653c016753d001955eac58c0b2e515812710873fc1f57df167b44ff3c24c95dfe69141fe8e24ff8b87ef629d4d99897de88efd7e2e669f43efcd5bd2c4c94368d5901ff9b8e4e45dbc315ece378bd4511c80702ba8da1728b7b5d8b81e4d552b4e6ff50e5cc75bdfeee51677eb7a93cab9a82ebb36e38a56895c5e36296f03fbee35810f05eebd5f8c996179e798f4fe2953b1e64599e0be846c96a98da0a5fc3fafc9cd3e991bf86c327cae3067223860ce4ccc7c3ccb1cd79c4228a2b283813b184fc1c32ba889c92e001f197fbcf166eb736f14a26e9c6074ac33920d538c30aa6fe8c16491d2ec2208016550c392a2d0df2673c9438a7f7ba4058c3fd34da7c026b92569f202e01cc7236bbc8ad9424ca3e196cf326c8db44902a5ea60434f16c80006572283c02f29ade7176a2d48e7eca860152fa29afd3427e7051ff88f7cd1383e448ea332f362fd5a17cc9f31d0e206a70f3357e5d591b66b2f04e34e5524135794eb3846a9921dbdf256d17fc9cc156689f78b1e8a24e36c216c3108bded63bd3aae37ee61ee6b2f72f0a1fd3e609a73575469d5321a34d49c9fc9079209af4e30c36a19472f6c0ea018828db75133d7c5294d12902f8ce313658806c2f334c7696d1ad5c7c3f3f4829636c1f574c673d8a64577f8dbf54e880f230b0c291b94a84f4061c3c97654a75c89134745aa94e62c8d87daf5c41fc981cec4f4807f96da9b8a222f25fc2ef766526f053b986aaf3e3d6e6ef955ef26c35389e04444ee956b6cc5d91e54536b75685db1bdae4b8f13daf7408b6e6c5df39a10c879396b95c66e0b1c4b825b42a16e47a0925d8401b1327c3436ab24265e33ba4e8addb5469d38b24045266f7fdea62d6579e4325aba3e82187d704f7c3b436635a87f8184b90d6263d5ee0709278734451d40f886c8d6342464eb9fa3df8326447ea5348ae8957ac6f706757803842fbd7d2e04be539811f00ab52f6a3ae662682d57f9163c05abcd0fdc8c3542fc931fc91f55530a75db1bc528357b82e682670ccfb7f1f001933d0870407ddfe9e70de13e40076d2fb6631448a58afacfbdabf244151b95a5d681f0aae1323e8eb750410b1ca2f3f970b6b2f068e0cc9652b4e1f3b68ffe7c484f9a9d2b9ce4c2a8d74734df800d95c0dda2f739add32b886efd59ab3d2961833b63a292bcc24669b39a2d47c2f119d12485ba9bf7e2baf2360c58dca10db214249b8cbb4fd430fd8bf808ec8bc92be945633a278301d0cc8e505b7eacdec1994e1d8e1ad529ed48e0331937bd635430d8fa2e2863539eb0b8c05e0ccb22d3570c72465b1f3eb44764ee0f14c057ef09f47c255494c693d663410a006cafd2800d2ee8bdc4aca745eb88428a78a0308afe2168aa5744479495ce9151e5756b62228965a707958d9cd6d4511a095c6d0095e60c84894ce9d0f05e81955d9bb0b64ab1cfe0fb0a446405efe244d6066baa8e340d55e818e53dce9693367ddab88bd48a704e4169e2cf78747b61ee784284749755c75886f7abb3d412382fbc1cf7e3a76ce713e5dd5947717e0bdb8946a15751bae729bfcac102037a19943965cd56cacc3d22415a9bc1222265f8d2dda9b2047d2e82bc664bfa257fd5c328782dbd0a6a60e988c332f7c440a649ab820c3d3ff1f1113090ec5eb30b9117d45b52e211a1f2b0edc8474d0248603cd8f96a018b5465b67db28b2d7ede5799cc534cd498a29a2d0e6cdc49646da5454cf7500dcb6355bb2d710b997bca1c403408a0ee255c80500e195da2f2a5255a998b1b6036c036a4dc3bcd367abd1550d2934cbe75c597bab1cf00cbcffa85e77548d7289de50db88b3f349c3cadce0fdb966ad7394b3593eab3486e5e50868eed4eaa636d42b3152d3040f3ff4205b0d603b5628f7219d5330ace211d8f7239da6d441c25fed0143a99c941f3af4c693090a2add1a3bd40358ebef5d0612ad9135592eeb8ec6ae567a08943cf1257397f1d4c363b56a4b66d2e9953a22e63f5adbcac15ab73a09eb2f3f4aea93e7b7cd644b0a97e3808bf9b83a8d0a7f39087ae01c23f18a03430e32186792ff6788791b7c7d361c6ef3e9e8b31ebccd6ce86306544066d888565eb2ac82bd7e38650712199e9237b062e92c8b67dba904f48c27617c4b1aa6191288d5567bb880017d5d9bc57f555cc0400549c565264df8a419e979ff041bdad00cbebaef1a8bb0b50098fa7a59def8d92675fb4f8ec09855ca5208ed6b2a49eb995b2899307c8e17b9667e007454701b2e3b2bd03979dbff8fa47440b74d25bfd4240ece1e34e32519a54a0083b57920a5900a72028a798c02835edcf698e311a67b033a79d50d20a6e2117dfbc08657eff9926f669f72aa85221f749dbc92967c7ca3a3838dcb42a6db97f28a755c93a5fd906f308088e8140883cb543a2670c46731e32133c5ec47eb4013e9f83873e7f4fdc87aad7b8fea307252ad424c2969c67ace27cc702babe0175b4ec50ca4c648d7b4b77541471ca82775a1b384f9800e55a60ce21ccd111b9739648d436655ddee32f041901cd7265df9004f33274b5bc8e9e08738c2329dc2f27a133015988c1e9f987cbc7b9c750be730df5f883ed6182eae0ecf372a5804a5071289829e97ac6a2f570859cc72187e0ec0d15a713e61097701da86449f7ace1e5308d2b2de3b8d1d542faf310e89afddf1f4ed1a937f338a1ced3c4ff747efc648c1272dd45fe6ee1eb130c609fe0fc9d7ad1d07a3dbdb1811bcfdfaffc7cce4cbc8f1a973958b22ed088221be2e30db12c8decd927848d212e12bdcc3a7c69b460396441d2712f60bd3e93a0e937a3cdb933de889e717b87d186254feedc7ec26e4282441095847f258f7932740d89acbbead72222c8ad05397ceb9c672933be35292c5a8e1c060b83097a601411c0a7b85d1f95f09ea1fa150855036f40d2504550d9151aceaeaf7bb92f38a7a87945d258def95535d1a2a43ecaadfb9abe0745c39a10b12215d7efd5d44f87a77132a513dc786bd703a34bd4537388f8575339ef0ff96546336a37b35505f59acd592f334368a45b70c22650af19d122002cea2f9024729c302cea08e1ac348ce2f4e23159b7bbd5dbac67d4945990d01bd1c716d0b4b76dcbbff8348a9041da312d0b3b56126e852f5ed85e753af80da31bfa44ad582e77c720d0c5bb5c05d97d9a6da81fe66a292ed4b7d5a9f4645512dd19e90d12d89d19fc8be1198b1cacf86193fd225b3f8b5d51980f9d1532e2ace1baad7b7690e99ce378c80873b8ee015e70ab2310c8ef61671661000b8e302b613b5c0f67213209c53af708532301b623864c4ddae168613480b94fb44be6e21158c86d064bac1d0dc9d8abb0e1434bc6c3f194971160344e8b6725d9ad2f1be20ee231f20ad9253865f468d13b280f341929738184070c993b14d60fba13ca7837c2e3ae107559a522e50dc00f7af8d7910264f67ca980779397f30be224a350fa49a13d4835e738164c2f6ed6c5d412c09cd62a0e8a96f344c4d183a8dc992871c34cc495aeee23602da6cdc18d297b4f62b8133b40aa7185854a6015f0cdeaffeb9c4b39c5f4a1ec73990f9eacdaeda935ea024c6f7b69039c1d6856ec7a51fef29f3a54f8cc52e9a2da55a9e9658c6614154152ed981a06cc57750a2c368ca1e97e2a1e002d30c2a7a757d0f8223395a870f002fd0326fc271ca0b4d15d6cd173a80d63e6e185c28151783cde227c2c787962699082ed184def0f3d2ee8b84a2449e922856700a65cd9178b605c9b24e501b88e64a0fe45cc8ee1259f606f20a04bc4dcc5b8560356400961b1e2ea4b48ecebfcf469ab71064e7098f843fd9d5692543e6d25adbc3515e93512a750afccf5654043d9ca3f927e3d1417a6fe584744044307d22292ceb8a571ca3484713bbb20efec17552605877885d8101f0b618989db59b03611be30ba555315165580b63f46d4fae3b0d92004b68d9cadd4c440fc898adca2421ef617bfbbb7e2f697a8310a31a711358afadac360fc601c48a58ed41eea0e059862b73f685a900bac7a0dc7818d02e9e4cacd4d6577b28af21451d961fba4d3c50891ac5ec5ed864bc63fef4a898b529238e01d5728dd5f144c728020a49e3fca31e4c6f93a52e619dcd0bc48a99ea3a3985f2352152bc09409308bd2fc9452cda2320a3a2eb42b17db8841bc4068386712e132658aa863d3dd5036c60458194042fba25cb4eaf0490d357f49b8b612117f4a7e5099eca480876080fb34dd481aa174133e459bd3640da3d209d79dadb1e16bca87cc2c30a966bded620f8f50564cb13b22fb49a63554c73df985e639ef5edda5b522a354eb87bac9493bc6925b702e4194f73090d12bd80a324e69437e4dfaac86703e71d63adf8604135b8875689bf8e62c23f510f1b84193069b7a7fa72bb8a908f2d0ddf653046f957ca9d42809f1dfc9405f2699b77e0579019af04be69003f13ae1d0f217e8dd96c576c35b31903e7df190e2fd12ee1718031e289ed741608231b3535793e6f268c552c298f75aaf56a9eb30597e8a3b50d455d0ed23c1c218a3618ce425c4e1f13ca09600e28afa43ed684a2b1531bc15305155ae344a6b15321fa38c87a764995f64fef7e9f54f1b06d9d909006c9bab3b64cf9a9cfab43c07a54cdc1355b1f3b727a59ce60f5eff1aa90e50e4d0c7c5f26b6b88d79beba9514a1c3b27934517e1e87e02bd101f136fc4b05c81f4b137210f10e3240e872ab9223c6948bf55a4f30450f3a0f3107ac6da2d6fa42155998234fad2b1427721a82044625f4d9575ba8585c57a190a6e2cc5ca4509a0080228e01323cad4d396f5aa635203162341854b3f651edd4e2639fd81baedcfdc5673a605553902a233c3387a1772dba60120b9f48fe6e530c94da062b7513f96388957ac0911b66b815aec711997c30d0024d9cced1f0ea5c4bf90e8b51c65ee9598c43624c375364684ad69946869da2b265acf5f80229ee6c21f77053cf2b2bf9bd353ee174e4bf4418505c30d30e292b3f397f45f3826680a9a7f7bef2051ae0b3864e05177b43a8015fafcdca811d2bb7f950851ec3af56750e5a0c38cc10c4c78751759c07e6346e16bfc1cfcdd86734f284a5a90832b351c2ddbd2395213d43fa312d551931d8dcdb4d8339df158ca650e45e6e907bf4c1a1d08199f83ae59c609a0047467b22706a9a3a9255630fdc1c8d351bd296642832d4b097b2d0b020d33ef5094fbd762700f8a2ae23991be6251b84de697a2c905f066581d781ed2cd4749dc31554a47fda0f93a289af10f35e652e87fe1163c2990f7fa3197505913459d95606ce11b4cb000b28c4214a0441f3710a516cb5032883157c1fc3941b81bed4a72602106118e8a75541ffc8c19abef1f89af29a0367a55481c76aad695463d8eaa92b9420da86206810967309198b9ab31ff900d3b49d0c1cd00e6ee261a65386ccba94b00425349b67c7cae63a6c75b9b09e0d23afcb80840966b1ef4b9e61b26b5442803523c0473799253888517ac24cbec497849be0c81cf08298ec7b8648acdb4a8190a5838e2305398d5d1ceceda7724e5e3e56f745b34470eb82ba988f9884267dc2072726c0e84029bcaf922007358d002d498a35852479aaf411e005bac6218fc2f4232384686fbc6bd76f97f62ee2d75a08128014028a22480ff2f0b40f42b9395fd2161149cc433aff79195d83e343bbee01f5a31c8eda51f2d0f48ad873d3895a91ba0dc510a4b27cb05e92de87b16a8a42ec0b22e1777c448442466f162e9da4c9f97cb03fbf7d16b411aad565258741ae49c5172d600cfd660987e0b651ce92ffc0df8bc25290d1fa64373dce021f49ed07dd0cc86e0f1fede0ec6d43432064af5ef2fa1853eaadfb20203d43b9f5e7a67f1a2a816bb3cdc62279e13bf0077ac5f0414a7d1140b900666bde6a77cc15d191dbcb7147778dd4073ef8abbe0209955676908a17d34395f0ce9690c9533929efa6aff70c83cbbe0e965f2e60a10d8d9cd8481563200d9ec6e03320839eb8194b65c637c3a590025147d1de26b22f44bd7d3f15770eb76fdb7edfb3a789094039ef383153b5ccc8d1e6691419c63931e3ece1f24d27bd8307084da24ee3390d80f7689ebc7abc4c4bfc898dba89133b32a3f63ca79a92878196ac30cf0114b46399ac3436e2032710aa3c25d3a4981122072c9ba92c2830768ee42897576e90c2e294f2c53d36390e5d3d9a52ddc39b98e6135dc1186731926552928be66896506db80258e09cb1e0fafb420b8342dbff64973767bd90b6bb3c38d855cfe6b3ae07ba04cb216c55787899c8a6d8213a82741c52474a24e40ddcbff4957d54c3d7116d3221bf0e863209e6d512547ecd7959d19b62329576873128c849cb9b3a1061f0ec448689bed43605789d4632e422bab784a097ad0b420b42a7c0df72f1034896d2265d7573c9ef7fc34c8711d920a24d2abcd99295748258cd1238278d9c487887459ad5241f3878bfe20eb6ec430dc5909c082482765b48f36000895cc5c2647896ffe5d965979766f576844cd6f3d281a0a5844c21bd875d63d7caf050369fd7a267233e706da976fb749518cfcdda598df90118bb2f3c8e80fe04332d6e8ccf88098835aa5060600a1c2cf53f1861edf98f7a9f1c5cfc4c44fb154e05b14487f9b72a790946d53579b61f94831ad848fc99a8551971fd3793cf50640e2e6a07726fea9d04a817b3a2d1c1a305678f69eaae6df246b06a2291eff857028c42b1a1a1acf2f626b0a862d2172b7767012a19689e8e6400ff7df4ae484915e6d8a5cec5dfdfd4718d1ae74cd614649edf74027b776583330e38dab308fa714c0fc57baf757eccc45ba99a88ee54067a41a4a9a0d0cf0c3e0648d5a3e46194f1ab83a4733fa3aa4594d55e3021d3d0b80e64852cee7dfdc868d4d4b5a6fc34a15edfdafac7bd63f765ab7d331c194934052fd4c5d41358a786d90a039564c041b72c4824220938e50610f540880cb6d40ae54482918a87bbaf1f02bc8f314cc242530048225a9e70009f21b33b411d4af8a2a9843ad708ad0ebc8461c8bbaf175d187c4673249e5a84b74945298e72780ce08548a3c1dd1154a167c2422a6d9ee5997f8f021933feaf87fb9ae1de42edd79a94f2df54b96227d763984723902ab65e7255e46a576b7cb0b6db105fe2d817b6a3138a29748570bedc7f524bde2143b49a29f08e567bc541b20f849608aa20081a67a568cbc6e0eb6c821fe44de8b9e76282ec83a8d0eb8c60ea4ad2de521b45cf0cc9e0f1ba1b083d935b3be0238beeb48c574f8024003badabf008c116e8d3a1e61920d38bc6276d0449575df05cdba3009d56c0254ec39639c68afbe0d2684646cda5c6bfc8eb8e4f300bcd119dc8dcc2e44e2b7402b07c33edc0d8043ccae8bc277fe95f65ecbc4ecede2eb215d51264c8073560667264c198802867b814cb8bab9a88d4b14e62756d94bd12d5a41b7488dc64f337be2b4ef49810a5509b9a3faecde68e1e4f6ad680233cd219fd303cbe33018f0d41c1bb344ec3b8b991f2581e38fa21ef4314d4ae7f0d4a1253d20cfd394419453065bec9d42823dd8239d93a33203f3135229472e359e0f14d9a5502f593cda5b59aedbee9126fb756d3353ef593966c40902e2525181d2ab858c66130963d8c5ca7ffc02f44bdc0e33f10161e285333d9cc5fdf6689dba8bb477ecb2ade55c28e4c4a976b1ee15c9bb00cc850ef4dfd5ae52af83497ed3bbe20230814afea61d61794cb2385ed55773f838be1c504b7738610d8228e6913dfd7b2b05033ce3bf54972d0560ef3cc1ee1df8f75e1111f367f6a7b965390c23efef874dfd935ec6ca240a0bbc6c527dc5693ae8f7c12405b88281b78c899355f2e61963aa9ba9d23b84dc6d80bee6483fd7c86c3d50b72098a5f66b623015904eb9da051ec87023d58351cfc8e06dac3333c510458b241989545b985fc75e6186ab2245e05ce22dcbd2fad62ae1e558c10a43d2f304f740dd44f76714a0941ae0582680293cde36f33579caaec218bc155b1ab598f437a0fb378b787691ad4045197dc240f7d7f1235484a4e8d5181489193a21fe5b208ba4ad9fe6c2d824b2c669e17fab3dbf15ca3b3a59e72382877eeeb8b5358044d1873c1a969620b1226b6df8b80855db63c108519077ff79bf7baf35818872816300b742881dc323f87e9bf9151c0dacf44abae5a6a56616ff9f7ca8654d53d31974cd682608438fdb48a9662406106fe0a769d238f9d0c824a5774560aaff126b133f4a357cc9231ff9c0e85f824f5cde24f2309e0190692c4035625afa504f5cc2f5c816cef2b9e8e911ce37600b668ae8dd97b4fbf842a7a33f30b6f7a02294cffcb08eee356ad84346f94077b5aaaabbe448b6e90291cff4d8a5ae79294228df3de416c0102883404cf81f9d63c51dd7f5a223b4f31e5cf148ca795cfe2b9eff3e6715cb887edcd2e8b06507f833babd845d3356eb460c634acef09aa31ed476505a022205a4a0f7aa770affdee047cc24d151e5c091564aa48456f6d8f21f5a91b087079d6abd260449ab13b8fd8042cd4c4cbe86058175c28b1e77aedbce2ce0d26fa6fc9fea0e2a4b4b09850b8080e679ac5110b5250d8b4cb273babc0b78360142edbf621455a7f9903a23fb33754e8c4528bc7e6d7488c03c7c7a658e34be1ed4ad81b0d07f728027ed205253384cf5576752046a63594640e90b19a054e8d772a77e310ed4561e938c6423c705175f5302284ba9ba59f93481b14bda5208ef5628afe5b7e8d7d7b96e28d9be4652cb2f433d44aa05d80699791ba057d141300476f1b54e60db105a0ecae1580a38628b2ad681907b3df955ec2778f932aca95b21c00398508702905b07038f981251b08812827ea8d6aabfbb1916f6c253e91581068369c91880dd52fb643394dbdd637a7baf68652f4d9cd5d0d535b7b43a7a1b7b888394ebfa5f4c97b01ef1bc6b8f7274c3a174dd9679c1025b08b057659bf6a66d8d0babb1563d00e7e980602526f52a145fffc6d7de8190fb68b25a00dfe8fd85b90a60f88347ed24a0fa45a2a23bdd8a68c51123429457d27f638cbb786cb0c267e665d4394cda117d13ed834c8ce2be439f8bcfc520778cc8614b69328f2f86869817fb43cf4c4efae2ddb565156ae26cdce72fcdfd620163c05042750b1937996bcbb5acbe1001215670b2f2b5176a602867d834650894c620c77198feb37e2767852578cd35471bb0e01459be2844c58ec9fee50df28fb250bf73798f341d090ada43bcd892da8d804011be2327ff579f2f4d0e0be5143913a83827a293f011c16e17150aa1aa5d8fb9036d09ea674374eb86e7ccdf28348dff28cee47bd07a5ad9540b19279879ab07ca8d21428b27b880c587bd3190d17854598c3618e7abeb1e4c1cbd1a456bfd7028da41b3b3f986ec871d86f1020c46bc0af0e0240dbb6c3c6eff190b333f58ef1512c8c76b6cf34abdc79d477ce30fe9d11783d656c212d84f4cf81213cbae413dc9ddc6d65990236e2890b290114c3d129272fce5e42c333045bc83c08551b18c88aa9c6d40c667443f77d6595777e59b7f7c3995453b3fffbba07287011338543cb92c17f3d10f9e7b022b7843a3cf7853e96a91b4fabdbaa304e0cb34398dba7f0ba1ac211b24ba03a0f95f8e8f388ff1c109a6588f9a4ff7715f9b17e18ee0111426cc85f7d877822c3ce0778e6104ca7513b4c044136a84f68957bfb1b2d07f2f6eb72e820d0bc134cb4c6767ac7d0030ab4eb12cd9ba447aa7ca0cceea0c62bd0698e2f3599d770903d8a38ce72ba20c3cba504081a11bcc5e62efd610ad18ee6675429a64f331ee7153291bb9034f2a870da93fa819a8a88db618c3b52f8b91282369315d48a703eb4be6dd9fc45534633e2745f085508d60ca27b6668129932046d93707b59114236128e835c78d39526c838122683f677b353cddfd22dc09b6b307a68452074db7d32db3815422ba5f8bb9773166c8803eb00ffcffba1a1080e57df2b12d056784e8869a7094f967d129e4394cb5a378baf7e8a910002061822e7269c772a35d39486f04fc2e20c2570a82d5166c43ff614cc8033106355c8cff7b343d1829b050b04a277b82e88d14decc6420489dcfb414c654e0bea9e7dd904ce491750b769b7bd271c94a21ea55031592564279dfc0329418ab7b0303576f310741994d433e614199c6543e7f63acf35d6d5098b22eb3336394f326b67dddc1bded7c452119deccdb99916197f2377523f00ea0eb33f7291a0c9d0cedc851466f65b0a36b66c6d13cd812a684f780d54fe43a4e4c575c6415ba2b5cb0a5eab207c53768d521aa8f99221b41097f0b498a3b004bf46bdb0e2df08573352859d81c7443467df67ceb99e847e34e5573d6c565c713e21279361f74ce436a31737b0649aa6821b9fa88fdb34e80544e65333a41637703f24140e3e82dad8486b30cb4b84322d0e4ceebe3d06ca726281ca81472739f65417d03012f6f1bf4b463822a185200aaf55f6ed5a30bd9c429b5b2051ffba7aba44de0f39f1f31872d8272e08be4d54bc86a347f594e7f0e959d22100e7276a94a18e0f6abef0da308cd78171a18e42784ce5f3296e9d3332382ef70e9d4230d5d36e60c44704f2e4a728932e171ac3e5c83b45942d75748849d29a01877bec756e4b55d3200ffba262f9fe8e6435fcad532ec7d1e254b37093d91b0858266a3c631979277caee42c396a46ac485f4c172287bbcbad5e6b270b95aa47c25ee36578a1f3da9c23cbedf7fd9d504fd27d09aa7113ed97993c4a7ddc0e769451478a79b5ae614280184bafa60fb1624553cad08b0bdac40cbe38a309602a533dfca2258a5a9a8354161df316a644ef395a30679bc9e14b7ddcc4edab072f75e47e646d3b37d4b7624072d2b542eada8908207b62e47c56a77e50b4a57278a6eaf87e92b8b6aaaaab195e5977a73c17e051018b2684cbea30bf1305012861a3827c876ab6e7155fb2351115cf7c301d448929553025d8a57da748d034d9eabc57b61c82d42dd33ac27848c4e267fce2e58d0aa4aa79eedd60ff3a88b00da972e167c3a031a1c066c5c30542a520730327d79eacfa1c1627f5d5324c6b21cb3220c49738e953617e9750262a53182e45006dfd1092918320cf442a6d6c40f00287a1fe5140fa47975d520eee10b6a0a32e583f7b51783fbc03fa234b4f7de5b4a29b74c4906af06ae06a10659a3af49929aa669da67051e4158ff0d7d0607730e9641b0044e127dcd7efa7af5197996c0bcf291f66af1061e4c99be7bb7d91fe9cb4bf4354920f8025f207d81d68216b420bd44b65902411b5e540a4eab28a592ca7b5ff831dbbdc87151ee5a554de8e40c4a50deab864fd7f29177b20e4ac7d3f974b1ee49f7ea7aeabd1883e0bf486369ac7b3106c199ae1bc7fa33040dcb778797f7d1229aa087128d77791aef2d1f4a9236d2cc3b3556cbbbbc8be8c3e55b78f0214314193517eb8edaab003ab57ef7350c41c3320dfdb5be1270bfcd6cebc92f4767bbd6fd97d0c70e4108e307a59796acd9f1d184d9e1723f0a7809fc96106647cbc33c8ce803e65bbe875247239c3e21cc8e1a4fe36b88d307667b1a5fe36142981d007898078038614498ed6b88da093563ba04b3893278097c509c3444cdd4795b6ca0ef5c35954aa5ba5497ea525d4a0bacb1ba96a3ba2478d45c3eda5e6ba97c0e37afbcbfc6f2d2472ce2d820f82b2adeabccd28a0a187aa96b4dd2f6ea56421c72c09bbc517f7bb5b460ecbd5440d05192c751a29625979790044b800529e1b06b8dde178e221e58a3f736a8c0a3c82594a50ee9b758d10745f2bc118676e3097ef9a83b978f70c8e176ad496aa1a89696bf96354b3724c1cd56bceeb5845ed25893d4dddbb57437c62dd7ef5a35ac227bfd118f9d2bfb6d6911bb56f66f75d7f059f11a2d21b5d6edd67b5b7062ceed6e97a6d280753aa15a3c37ac20ab9d54cee9e472d9f4b4cd24dd3bbd5b6bfdfa6914a9a7134a13c205766aa7161ea5eb09653c14d94577819469c93631a8f2145558a1e37446c1d1399d52a6393bd539cb3627570ae7743ab138279d9c41c6da61edc87698d4aa7365cdd7e33ac0aeb530e40796f4a3df614aa6610ecfed4deb575aa9ad1f133a6bdfd2b8a8fe48f335406a1f13ea770853c506d8dcdfdfb976a569edc58486862685d345717da66aae266c36993a33c7357b266beedce9dd56ebb66e6b92bc87817bd05f0c762a90d8aa944c66e3083c5bad4e4dd56d5d28401a38d4b26104f71c22657c22d131ae028dbc6691a9ba05388088851ba28b80db9235dd57c8187f2d2ee8a461ac7bc44776aaa66aaab64db5737dae7b5b98a1525570857a6badb5d6f9b5860bf06722f7b5251c218fe0976f8481c75be9b5563563544ee8e40c3dc071cdb5d95076b2c4deddddb5697f7748290c8f3707a1f30ca6f0d8280976dd4d7c622e7a0902fba85f86d314ce5312bc655973bf1f9c29a40082feb3bec4e18a0e8594e997a819d3e30d732b19e592dc4272479192322b6894aae74daa57a12765a35c5405961f8eaf089e736a5a6be22891905bfbc096a59439f9e96364cd69e209ea28408c94916f001e3c8229c8008f379ffca673eb90358d6a01ecbf176310fc51348174355ae858ffbd18779fa4b740159e27938bda0b321d75dac1f28774563649d8ed87b00daf94711b4b70ff783315af6c802c6154a472473dbac926e9ce100e891fc6e29743cb37f419eed119e7e373412064eb472bd62f8ab96cb656dd64773e286e32d0e6141ce051bac6de9ed3a67d074892941f008720aff8809419fbc4fde0832c432ea142c121d1ba668cfde1ee0fe7334916f620882f0d2638233dda4c9ef2a0ef219a747d668c3f3602442256e40649da6432c61f1c5d3636c81afb791db73de733492cbff2e3e57c5a93b4695b0b4f15b11512b756f6df4c3e6a232df2078963db203b90eca314633bdd8b41f1b49d681879c918ff2a6fbdad49aaa619139ba40ade96e9b6fe417c63637dd91fab44922ccabae47d293529779053c4d18a91dd2a713b457671b4575894d502b443c8fed5a2462bb34fd81f29e34282c74d969a651b8cd468b1010f929095e0f179cc2481479a041ebf7e1400b2f22c8f452098ce7c648720456e8bf09a311b0862fc32ddab526d55e53b2ef2f0875b72f9966779ebf21772a7bf10b2bf4b0bcb4a68235bf01ba46c0201be5eff2088b1b441caf8bb5abfb5f2f6c4456e834a98e50bfea110873c803f0e73c816fc4115de7e36998b9cc6b6a05e482ee44808723441303627bfc936d9ed4c29be8212458cb8b1d65ad97d31f8abd9c454f9160abdd4127649c525f4d14bc6407286c6793d4dbe25fcf8028c262526bee761c60853102526ba779796169a19d1ab84b2f4b343a12c6da173afb2b9f8a86613f9c78a0042c0238d01097d4b4b48728155dee5c7b796529a3b1595775151117da87ce87dac3c8bb5945a4ae7addb76eba4d4f32ceddafb44e9221b2f8c81c5f5052d82244d93290c54138af0282a97359d5045f8536affd5719dcaca4a284b5ad82a9a1575d8bce8ff019023edfd092047d3fa8763aac8dbb3149a09c2d62decac5a2941672db601b5a594524a298397baef3e1a1206eeb91f51e07e542177ab8ed53dd185b274044ea9613d855d6a295b18eb090e305613e417fc3b19c891944dc80a44ea89ec5dea9048ca73e1e99652b89217c2ece8de7baffb1e4a5d1752d80eefbbf7440a83e98749113f11a6df1367316335e5e69e13a9ac13e90fdee1defefc7bef953de0968bdcebb8b05bcd98aaea56432489b2644d8334aa8a5563072b22b27fc7a411605f761d4fee1bda53147522a90c456528944c65bfbbf73b6e53adbad5dcbad5f696e33e9c25392f765d074109996cccda8ef9c84f37fd45939907691994ec6fa5fcfff7bf17e37fed27c94b3333221108c6bec5d1fe4ab6df59399a6fa9385221538e6aadd4cecf7af4deef98ac198002c490ff019097057c00c97a2905b803dc2cdb185b1e72c6c3061db449bfbb630f86ff321fd9273eaab158ec5e1c95096406a5dddd578aafd2f6b49af2039e4fefbdb576d1692b685f5e02cffce1b837f75e792578ef8c66c3ed0d354d6b10cc013601d7096edb0d0a92508152a0948f1cf402f580724038a016880968053a72ed0c4b094a6126c8513db9280b89b10832060a0c9417988bec8f8d341642622730c803bc240a70093ce40aa950be3c8252a0d4174eb2312f64cd4cb1fc0882921df424fb83a490a025b286be3f088996352cc89a9bfd4143648df62a3f82b8c80eca42e21f415480a2001919414540a96f05b68823c822c662208843e288b1385e90088252a8140a94b29ded6c922a8bc562d9d9f79aa40a83853277ae28d4a366adac49b271b15c2c174b7b99bb864d924f37edecde7bef97e376db6802d390e6eed6530dc2bab7da6f8585c02e19949235dffb83563e9a818448d28bcb8fa02149b8fc0822f2bd403adb77837c2f3bbba70f257e27177da76cc99031fe3f806865213e979d81522c2cedf2dbd7938f3e938bb43c7ea63cd653febed58cf94ea813ea8402418c572fd8f7827d2f98679b602c4222e0bbb8fc8bcb9f6e09fccfe423cc04293a4d12ce42c6e031b2fff80101e3e4c5898b93132146ad26091be998d724611e2c59e128564b804330fe5ed91fa4034abd80ac8f27da18a813471be3c41b616096958fae9de5647f906a03a526e9e539ee3b97d04b7636492050caceec2c75efc41894da5c5cb67f0967e927890a41c634c541c7f4fb0b644cff185409b98ff003b0073ac84d93c84d99c85d83dce2e83510500a94e23889236590489920489914587c851ffa8a5cb8207867ba006b8030520539d2be2f5716dc6fd85903af054170db2a1555989153051743d4efca51fd76cffdb50604f6a761bd955a9bc20142d378b8fb4c1723ee5ca021067af0808235916646268147dc5a4ce341e9cd0f16f7f6b8f7e2173c6d60803ba37ca4390cdcdb4ae4f4a5ee7b7c2031cb184f112f0cdded7054392b1d17b1668cead640d6c3479e05d03939d4d02d5b5a1daf8d6c92f40129cbb751d0a49f3163936c668c8348602386c83ec83268a800ebe043ca1b60e04dec11042c4b2dc412288aa03295c6bf60172e59adc1dad0a304019b05e4b0596852b3917d95c91e382a94a70448d3eef546e10c3c52d56b6a1f8bc160af97cbd56addabfaa9335923ab4f3d52772a4f65527b6a131aab4054d5447572706a9057ab57b29fd6c4d6a1aa16a9aaa736e98d356d35b0b9c6194d55502ef3512ac7a1a0e669e2f86c02c9fe5776aa533eadfbe3edd033ed54a7f2155d0c49e264cd00b2bf3ff11f5953ef8fa093c1892e4b79a74cb2468d915d96923e353807686749c697a54309411b2cc0a348e44e5679fe9065d903274ef24b13f6b759a29d8875ca65b3283eb22e7399cbb60d47b5a302224203e9a299442767701293561451032e879c3cb16d3adde303ecdad4444c8899decb1aa7d92ec75955ceaa1aa176adb2caee9eb5e5cf5a6bad32c65ff492fd09a0657f9ae5155c08017402ae2291fbe04a070441902b882260ab152bf6c51198ceab498b67b53255934e7ba111fcee3ad4b6b7f1ed2f0e3dcb3b0c2bdd4ef69fb3399bb31933eba8d75ad01497ed9df5aac9544dd5544d9aa7759e5c752b59f33e9a14f0d2ca8742981d21960f7d0f25951066878a0a8b0b0bcbb788d307e63ecbb7bcbccbbfb4dc955f61f1b6b829a0f756563e659640292ba7d3e904da4edd296565c5bbb6a442ff0bc72e77fdea7ba3a732ce1b61e0ae43a15ee5968f6a351d61ad2629f4db4651a10e85422f55d3247577655a55d3ed561d925eb13a14ead5aa6129f00ce1e48404090c491794de7b7bfefe7d59e37792feefdf1c5f7e2b287d5993614c5d9bf6a47931a1a1a1b13e742e41f075ec580100344c442eb406829aa65120648765a7af19e35d4f53da1fa5d40919e3ef2ceaf5cf0fc747a58f68f80529630ff973a034f46bb7e7b6cd6da3c17e3fed6e59a9b672515b7777597be5a2dc9ac872571e672b259425d32479a20f26baf7d7c1fb5b6a94ea8636b9383588c0579572912c7122133c2669422163fc954c71c56cddd4a7e323eda66eeaa66abd49bdb0cc7e64139baa299dd49c335553aea5288147e98ae5da12028f34250aecdfac19a0c3c64574564609664f386396822293c6644d96325a4852a963fcb58861217930041b5b71aaaaa65c24a40ea94ba46cee55fd7442482939c975af7ad5dce77144bcb76104d3a7d408ae79bcf47ede275e2ee546381279f422d9473792fdbb8c266560c66cd1249ba921010e815190fd65bb0f4b68243411a4cfcacab9faf007fc63efc46cc7ea43e0e6fb34d472573d1b46f046432ff70753047bcf7d0d24f00d977be59a244f5c62b5f5138e0910b8619cd82f93ace99894b9f92149adc59bfa898bba73809be51ba04166c0037234e5ece5d3ab496a6d5eeedacab95c6eeb9dc91de3b64eb5e3e2e17c050910abd6da5d43cfee55af3d18eebdd7b282d8d817f6a5439bd45a6b2db7fd9d28a8b015227d7c92eefc8f3579bd74509fdb03ade59d4980d79aa4dd9412c008dca94e512134459b7e539474a29333b44c4ef83801e504847b657cc05aa61a27371b69064704ffa5dabd54abd7aa76b856add80d206b6a5702fc294760cf2161b82b1f5510b4b6de2477d68ebb1d87c2113ca6cfe9022192d61feb91ab9349df4823b47d6215e2559a9aaaa99a8ac5786c9b8b5342698f4e8e21b0d6841d14b400e3278f334fa790a3f94b3089c17e47edd30fc77daafd536d25656c1228a5140533f0fcceddbb56cda773f9d3c77b3cd633baa0d16aaf76eff4bed5dead7af26e5ce7699e37bd3dc975de97a2a5a44cef14e9c92f4c01cd78292947b27c102bcb108721cf51d15454a6b78aab5c247f06c581b809ca0ccacf8ba707c6f3e271758eaa6fb27c2dd5289f3f997e9101e982e4718166c645f2db7d4e4da355ab757ad7f639358dd26a356ba7b7d568add6debb711ac74d6fcede6de3b8aef33eedfba6f7c7759ef77d2929a00681bcc6d8eb50c86b95156d65657aafc870bcb2a5c5932e2fa00682d31b0c752a2b2b5e87234b96cfc292a3c5e5457b7999de2f3705a66f0a0c04f22882dca2c35c249f6500d66dd7eefdda2d657777df1a262b6bde276d44c1f46d44c1f54519aeefa591fddf3658154f4c1a0d4dd32ce10a9a28b79bfb70f48703d348f1831779942f005cb0898f8b288cf6e088a107fdf9913f343fe091c2261259d395e298344d03d2c8fae1d0969cf144ee1918606dd2507a53bb15c77192933dba677a1cc7719c362ff5b8b045c6f8b7cc961a462041206f90a38a0006f48deb131ba2a360a67285be95a3fa34d39792fe9423fb74863d421b943ca0023a1ac904e468be832fca89b238d29cebb6deb7716eeb3a6ebb3b52c6c5c3b5e70312b01878988260604703913e92c6a07282898c9b5a2b16a62b664ba4c05922c5cd9224b18f022603fc94eebb1f81bb57a07fbf451b9a48b4c3c74bab64ff8a520358c2ae486518ec48f6df9101ffa2739166c6f8cc09ff4c184130a0da20010711d091834397301f1fd1d760b0186afde1ca784175612530a8b3a104c679bc35769a3d40154e99527eb0ab72a12df7081bc5891fd844f944cd2df649cad088e1b19a7ca829a827b09505d995c0f5d61e2eb2b1f55e1c5575794c306255f444294afa8dbf07f195ef788aca6e3f39ea4f334564949c31de163c279a948f288e8fb4d3e954ab555516149aa6c3db2a0a02d6a1672d6734756d9541da3aa7ad72c62065fcb5a903a5379adb19c7d1d0bc94526edbb66d311cb771db0df38cbe0206832169edfce83c26ea449240ef1b48a44e404a06587fd0c8b64bf3dea554393a9c7739b07bef6bb3df4be7be5e3d701cc7711dd61cbb4ae2b31b1578a43fd4c7b5ca5ba54d9008a0ef4fc790355c23808a350931e4a80e51fd7062255271aaeaa57a7dddb66d5dbc602f8e7b75ddab7b752f6ef3ac8f0f75b95cf9be70a77cdd7ba9be2609bff0ebbbee3df1fb52c259e23e54070bd980fe63c0c60aae9e9904dc6fb55aad56ab455b2dcfe3d568ab0c4ae2e4f7ac230e43f7ffd64927571871f63ec1ec864161ffeef66e17afc43c9d4451144d51176542716e288bf25015d591f7de7b294a24ea165194524a29a5946a934e22b659c002cf3c8ab26bef798cbfc6b9c6e05c98b3229cc1c859915d83c1a23c6585ac91d9da0803cf1fb926b2b7a40c0e1e298a537139148542dda4382f05d65a2b6d576aadb5d63640646d15dd0bab3b94760f4a9ff69035607633b27f9aa6dd39653d54789432d167517ac420958209ffa093ae2a6bed63e86005ef9fd2047034345a9f5ce0666a04ae8e1703ae8dcb5a8b011eb31db318ae9c715bd7e5ac7454a128405452dab4a5943e8f704da31a95966a56a3fef36597c9a61aea6399a49454d29e99e9992470c65ec85bf6ef80f410a56105bc68db614bfe06f4dbb799daccc133112a655e09f088b3e7509e89c578d48a4ddc12275494527abdb384c9e0264b496aff6aa54d6055d6dab5259df5dc2e8fa2a6b3d96c8643e63a9ba1b3d99c5571047d76a5bc9f8c91a4dc429ed9026b333a58fb6062d8fed2ee400d36af9c52061992742b10f5072543caf8156919ed43458110540999ce9e80671a4b6369300dd3f7c2f426c274157774cf953a11a6c518bc1e4a363a668c57139d69b4d20aead134cd6a76feedd6640d2bace0b1262e72edcb028f7ff38fb74fd514914dac381ef30595c55ce42f6b5a2b82dbab358994f10f8d186b92ecaf4d2d89ac9199c6480ca0504852f8b4353e6d3d6d3d6dbd0d100c856cfa5dc99de28afc55faaa148e0b54c5cfeb059b79407a7e98c8a2c434914847d439bdf7e290247553742fdcbdb0acf962af0e266bbaecdff5f848eb66dd4ff7aa28fbd3bd94e01647ca438385b56e11a6473beb623547cbe37d614e0952c67f9a7ce436cb2e08f7d3e174aa6ed51de9b4e6663e1a3925f873ce45e194d05aa7c2dda93375dd8f05a2a2640dfdf691105e8564af2fc85e8fd85435e7327191d3bb754d54dd6bc67817bbb0fbea5ed4755fd9ab10427e86984e1585638e9bf86876af4c531ed3afcd77b95a7914f528caafecff0a1dc167b0e224ad18020b56329705e8a7d6978eca6d9576208d206ccc8cd72586b51f6724a082944590228c03faedf8e5086c9c4c0bb0cbb40077c581c3043617c4fece2fbf6211d8bf2350e78f366cf8ab521d291cd00fe753dda66c4e85529fd893c5a9337fadde98613124e9beacf94cb37ffdb1a64a46f62abb95de999469327efc746cc6f897ab2cef00cf6ed645a93299b52ea82a3a390312a2e0e095240707ab262196bbbbbbbbbb53f7588c878ef3a0f46b800c0bb6dc55ef4fb2e60594aca1ed271eef446c522341258f9f47cebf6552060b7c8231827b868de322203f31384ca04f32e3a0e2d8aa5025832e8c3dc543d6041965cc4f17902407622626260c654cfa29e627dfa97102dc2df32978f829c4f2ace828a593ce8f8227a59452ba5d90eb4e978b8697052c7097c7cb02161866cb5e8b209778044c5b6e9fdec09338fda99235da5b24dc8e8b6e20475c1229724e8a24dd102ac8a3a70449766ec79f4be2b31b420935499959af9062c810d9a50c0e9dc757f25206854c45ed002a3c82fe8cac3d0eed92359d03854ce787539b09e2ff6bbd49a9b838d94acb356654abb5d65ae5bdb36728bff6c497c8955f4d514a2bad3417e6cac9f7df46114cbb6b58838cd93c3efd701009d56a43ce81731102a7d150e8c386908c651abc1fef836cccd42063b99f7bfa4de0b6387ae26b3de41a4a71d4be1373e01250f28294006f3fd2a484561cb53c1209556a6d288503f37470ee409a66adb5b4d2f95aa85d0d671befb1ae6aaab54d02e89d20ab56f59146552b93c9643299cecc8c480482a2998e0111478e50f2c14302c6e0c28cd2cbf330938202468989ee5f4426ac7d2213c4be7c4d39f8aaaaa2dddddedb30049287a7274b180fcf8d2c613c37b95f777adf6255c99ba969c6e4405da40213b83ecedbb8ac6255b1ac5c7bc44542f0d82dd1b25cf402fda842b660e83a06abacbf43322cba4615f28b8ae3adf9135fcaac5253f59a6cde8bb14c5465a2faa3a8fdde0fc708001d7ad8c9ddc391dca2fd6a5fab9aa67d875d6badf5d6dfe1e55d7e07f05bbe869f45526e6d6a79f07770f997dfa1e5c1af219853fc83e9b7c0cbbbbc05c06ff9fe605a94e1e55dbe8a3bc06fa93c1f7595947117cec055664f40fad4a6da377c096b903c3546f060e5b25f9ff48d8b5226df5a50f5a7ca5c247dbea840dae4222732c6bf8a51c9f8b13f6655d6a62aabd464325daaba49e1d8155781081bc09ad2eea63db5d6eaa294525a2ba594525a002623d8b5e3ee57d16e1dadf53bee7620052948c17bc10b5eb06e6205c16b2f8afba67871a56eeaaaeeebf6dc9c8b735b97c95ddd9652ce56ab355b2d516bb674b00f39058c08224a3e78f02024297dcf030c114bf8e009253a445e95ac91d9bb295913622796c8fe294de0f11ed1c973c7455be0e992b317c646cdd624dd8b3108fe8b44ad992e70f73b789ff23b7cdf3d8c0cdea7bc05be0e7287ac6e6a871d52defb1dbaff9e4a29a54d1258ca929761644879ef2dd0fda7438a7853239dd94802b738de237446837dedfd79c8d7d53d671569a0e298812c5b4a29bb069f97a8e44d77e6bc54440200000553150000200c0a86c30181583c2ed8f5561f14000d6a7a4e7a543c9a08c4418ec3280819648c31060063000122404244580dd11854192e065812155c86e2898cf2be5eb7a861768fca1b921183262839a67f06ed84b55bba4c20ded11a4daf6915b3445f91d245332dedf8d9493a83b74192da0ceeef6f8c2e0e40018993c695061d05ee1878ac108aaccea168f9fe238a4ea040eae9589c700d94b50e074ada848ca330aab7548905f2c22a166cc728b43fd05c8c5549771ade9f25dc2adb7f3326784d2baf77e71415afe5da8f71bb023284fe716a7f66d2697edee2a33822559b3776d4f1a2314dc6d0db3f1b85b2dc5dcc8a750a0daec1b5c0101de58fbc7d5e5466d615cfb2082f2adad8458c685416dbf685d1e55376ed5d7313fcf0a1466559d9d05adfaf6352d94675c959149509b00e2515c6404dcb00ebbd2fabaf474663866ed1cc2fbb2b1a77879dcec73afacba6053575defdaaa03badfe5f13e0864bb06190b5f58c1850dc1109abe87f7808f78f048527879025a49e9aecd812c0fcebe8b7a1645529071792024a50373b00b438d60395fa2418ab4544911a099acae2f489223a8a10e784407822290e0a02460b671c61a23c9e0655922e86f59d51c66e87d46864f5c3946cd529ff5d64b02c719adeaef807097556fce86a0391d5d37729a6afd0f00705e8025a5bfb3ff8ee1c3a8afe6685b4b4a8c1277bd3bdf7993a488cf1edd22f7ce700ca5706e2ed4bec8445377954a459ed82516cb3c927d439c28dacc02b0003d0ffaa1597e4cfb11b2cd22a3061274689bbd6311783a0ad87653082bb5a61b2ca489ca719104aa37ce83e734fe56ef022e84177135aed2f571e13d545cd8dbd13633d8da4e143a027a20f05487300a92619e2aba581ccaef2a32efb1801940759bf37d0a59b873b36eddde748d17b4bcac58c99ccd33caa951efb09c43418486546e27b613a8f81ca161f4544b94cb9e0eb51e13c0b492afe20c9ae1788ffe4ba00cbce52544b5e8a40b42ab7682a1eba60497b2d60fd39c275bf4351c85f251c049300715935dbaf6020f7c34b54f5f6a69fc4bb487a3cad46ff6edadb6551bf158e014049ba60fb41c2ed43c8a23a00a0f51eafa742386513a5406f0661aba0701e445276aca3dcc1c8909c3d5f45ace69742c7e63dae687cb70a77b987448809ebc81532ca02c13b3dbfcdf0f06b1edd6f74bc38a5108820db3889ea97df0a944d47fcc9de2e1de092e1596540cda9d82321baa465ccc8c59ae3e31ac198cc70728fa6a3677935af05d5b52a1e16d6dc6a75d4d0f9b931b24669670f0ae584ae4ec22271c6580d750a753c872bca436286d6c4dabd1829671985467a40e95a8bef86297a212a200f5df2c2c4632e603d63e22308c96c80946186ec17895c6211ec2889aa3264b28ec2f901069dcd5144c102a0e607cd2453acac5c60caac4c872c74ca26a7e3111b7e0adcd91271270f6380be9317d76fa0e79ef125d938c64a51de532efb3d67b05278dc6472b3a66c262ee9aab3163827ae7a8cd43c5e8d0680fee026878cbe0786f1fb27fcd1a0a34ccc19454be78eac2f9695bc44ff44caf9a7a5aa4d4be062a0a04cc6daec271eedd40e2fedfa91f23fb8bb6d7a9bb7d12a3875833c4c3dbdc3c58e8a279d186c18e36e24ec2eb756e6c92eb771338624688cc66ae541d2b5f0bed208d2070e0bacf3c296a50e923cc8fd5e0b4d0724a4d00a879b890fd4ac1baac59d583d9925bbcec3dada29c965be0a3e7dca6a853ff98abfe933dd524d20bec2a6763dc875fe44dd1a4b5cdadfa0344c8047cfc0bfa8daee5de6a4359742000733c01e92a070504b2414e2e60d8b9c76dd968056e0d011c0b6cdd37bef8fdb5dee15b4edc008fdb800203cb1889069e3a121707eadccc97b511ca68c1125ad31622adac513c5b899721b234e62c631828fc33b468cbd028f1183f414b5bac97ae8a6ffbfb494022537f529f0e48db53646848a5dc0458e11c02fbd1e39aef1eb1988bee33ade4c13e98aaf2e227eb60143111be4e0907d4b0eb14841cc601d02dd2fcca2cc4b20071406ad5c40847e14ea860420f8394c1bdbbbfd87cc06d27fd1ff272c8b9e8ccd078dfe24b67852f0973d49c01e5647d0e0b46d35ae9ca28a0455fe2112045b0dc5d3ee2db748e40c415f3cd6796a78e8708b49dc391230301fe1c1832cbbc60fb6ae18b294172f3fecefca7502e41d0b9c78ac2a762c7e9ce134c7e92e5791a6d2e966f971cf966a141aa30cc726339e4032584af567458d0c7a8d4fe7712031387ff4456fd1f1cd2e11f94fedfaa3101037bda7ef93b92a21bc852a436c3457caeb14f496535a5db91db2e0ea404d210505bb849b9de5b17709a0a6851e40190145f55ce8de443277322be4e974eb29418dce240b29a1789dbc2d860d037ee4714c2b5e8720b7327a781dfa9c10f6c434850488474f156c82ce6d01b5369d3a8df03af9b32ab058aa0bf4f3cd3246f1cd8c9a636eb8be87df066df4318803e9c49e15ecb6d6b114e1752cb2dd0ad809258329105e47ec119ddf4ff6eeaba503862c9fdac105d5f850b939e62e92defe69439eecba01883076094bd65490f84cee4e7cbdeb987cf3ae4360bcebf0a93b0997e420394ea1e075cea90afd3266a84f9776a2678cb0bec0f1191505aaf549a09f76f7ac7b3f94dafcfa9910b4309b3be4dc31c36e9de0bd76cedd690196ee7e80bb7ed10182a7dc2cba36fbe134923e2bbf20b92a2a1228b7adec47e9d3c7acc9d5d8284ab191af961cb45dee2edb8e94c951c179831a57e86fcbf5ea17da32f319002843644bc904032085d0824d995ff6ee0462e6734768fde2e3ce806cd2542f4e914d6d81fb83fa0e9db38ba4c39e57e3bca8b7a8d30b7906e658f5f4c4d0eb99d04c0c7522df2d6d16a9f8d59fa7f883ff0998ee903f4a8b01cea1a68e5c6435ba5098b7a7320404b9cbfb62d365a7021b50e5f28499ee111bba9d4d463c792b2eab1c756757761f7bddee0e28db85fd1f8ef6ce17a21f6075ce673f33c38ec3a0673c612b1640d06825335963961654873415bcb59c513d4d9ce43570127675720147781592351b58ff23970fd83b08277905aa28578206070e63c58d48092564f9e40cc662f8b59b8261c22c47a7113119a00cd572900e98ab40252fc1c138f638ed207d449a849c71369ce3dcfe3266cf9e319506e947bb07b933feab710eb38e0f648e82e6250efe7306fae682dac33d0fa8141636031110580bdd94a4be6d12d3284e42208694af78df07a03e44d91626d99d44d9c40515ab8115838f03690949812194d9bf1a527882c741160da055456dc7ad0c1ca724b3ac96a605b9b74069006a168198ac0d917c84231e3f9bb82f108ad9e1a1188f0a8d275b18207de7f68a4e5b2e31436c00318a392ab2091c2046cd9c33e79456d4e107c53f19bb2579f806c6c1567e6f2fc43ecbe51422730562803b2598194518fa5887209d0245e94ecbd25f48c0f87c336f932f90908722e1d77462a3e06d6d87efabd0a3cdb047287be7b16cbbfb1b7aa4b012199b7dbcda0b69057e276898b358cb951d66bd20660824f0891301a0c5745a0f055abfc0ed4507ae07be98fe11972d51d8a84bd5fecac087ec0c1150b61dd2735fcc0d63e1c1357b0797a4a121c809d4cec23f4c306ac1960939a0f0bd3c05c053bea40ad9783f315806b64f14aebe356a20345b8d40bab234a39fbd78e1ea21ebb0ead0a34b1ba3499c6eafdd674e17e4db55c0f6378f11707975163904226c127602c23ea039dac4ed7fb04af0e1cb2ad15acd43e5d9939b098f06932efc583e87befb47f40f1d790aea4cdca5c3a27b0ac848fc414966ea51188b484dd86bd7c30ffc6a5d5bf2107332d4b67ac8657ad4c1e349838a318986d6d814954ead3926ed584edd09ac1bbbcc2340cf76c565796458a955c29539bfd380a5fda0efac3120a769ec38352a0c0075ffbe9b76319d8278e5e1ecd80689950471e00be029f39a5d790ab082b3fecefba30c483f8844cf799539df4ca97cb82a0544c5de42e647bd30ee4605be9cca88a1b0bc02885d5e29f05057e6eb5aae98c07b14195fb606ab5d1cb02b92e187c8adcf83eca710cfe8bceadec5bdb23159d744dda2644d549a57242ed112d5db15d28c3e36262ad4be1e574a0ffe8da5335fc04ba100865293405e640bb75d7b712eca2ed1f9e3a4aef662fa204afd533e33eaf1ef0152b43a2145299852dd7a878fa0e048369c16f28ce35d2b301295000d26415d550e2a49ddd6796a5876b3c30735022f0adb42a67bfc397deb62304488e3b7dd78258ee1e7dd3ad2b689467c19d2acf0b20fa8975f1bbfea28af6a38d04997fc1e0f2d72e6d37f609532ceb74ba1ce0dbb47602ff6d687e19a642bd45c5800253322500b66794f7cb5fda4143e743e6577ab15f7ab0f2b511229ccb37612f9e56b4b19e1f2a1b3f57e288700942eb75b21d6e8d3fd8ece3845bbbb65d911da1f7bd5e3c8ebd5f11540bbfd5f145fb93d7afec53c5c3b60d1d8dc37e84a2fc8bde68b96d05a0a87c00c5fe784bd14d7269ef2e4eedcebe5160d15990de0a9e4d2df62ddf476596a4d2a81035d1b712883ef80bc2854df6f9454eac87e477fb03d7e39ce29005553ad85a6dfa5d940853f5376c16fc07c05a1f502124f5ad446d84b73715982516d0f1c99742ada41531fd3a0423f22b8884babc6cd45087264e0c6af3b0f8601dad4a772e97d1095951e1f7f18a1fab4de84657e18e432ff4af13a3957ebbe693327c10de0c21320ea17dab109cbd421028f6110140ca75f2830349fa809c1c1b1d2c89f9fa55e9a8961ddb7e1a3e83a62630203f7282897f35e895cb9cc147b6eb6a944d7f7431795db8579a9a089b5b264d9819b22727c6f26062322735b0b45f5b9668b5c09c915da4b529bf0881c281bb998eca05af6397a42306228dc8c394c2087568e54a9e001f5417cdf6abd6d8605d097a9b13feee2d8c5433318072d4a37ad2630f22644f2b47ac2ef370f5a044f4e5992a9228f378cd7b7e389d395033a4417403461410f738d91144df98272766536b868d7f9c160c163e5d2b13d7f58318d1eace18733a15b173c56183c7f6c54af96b3dcb43a33f43b9f83ccfb1807ba7c9a4cf2c9255f02f58433a40f11b8d8d2954cdca2708b2913e380a6e52fbabad94d11d657fbbc2961eddf29f3925cf0474bd596bc13795244030a5f83abc3e83fc93ac3fc3fa049359735aa5432996cd6f7df5cb241f4711c7a671a2978ad411dc5e6c9ce4d20595f59f72dfc4aa293c0f3831ec448140583caada00f0cb10411840a673ebc846c16ad78bc86b40640fb12d03b43a088a2105ca9dba1c40c17fd78012ff35990fd53966b1d56d6f1cdfe9bb548614e2c51a5eaca7afe603a95bd8ce572722f231741b75fd025b53746f7574cad687a3368f619a1f542c412303320f28f203ada00e72a4f66e760a1a8a9cc6356dec10960dd0bcf1dc57059bf2d06b4f866a9625d903d40e01ca2655ca6c3c71272dff6eb8c33de79c389eb6c1269ceea770fb11a769483d58d9d84bed749d7720f4ad128f2f52ba4966b77114daed9b9f82fec6321c010059b3215c4e7e2b02cf9fabca74c5c484e111c4adda63b59762804b3f8a4a8a9f500d295729a56444219da83b2b4dbeb7c744f3a88d0c931ef2142bf2b98920869718e50f69921aa49501eb547766483c21f98eff135a8780d62d4acc8bb1ca9ff343fd3d1cae03042736f8107ec4270fb87e0c65d2670308a648614c2e618b930cb8ff7ca7de4bd0db22186787e6a2d26a91c344883330a26d0c7cde771bf26505f8548a4ef6ed713310e7e058f28d19ec8c5087c9de3fed29ce8ec248d902355e2d30dc52235a2c298fc6e6f9006f72f0e988b7b94f5801e89e8d8c8272285553fc64f09293500c25dcf71e58f04d7bc4ebd74d4603dcdfb0b5e75f68fa03ef3766290929436130e34d2e0831087e17fbd49e6b534e17f3522c394e88b4583b391ac0b5d49f75823be4868531e0dbe73d00810d6497d8a6ae6e8dcba84147d4920dfa9382bb0d6cfe6e66c164300bc39adc8aa110fe318d305124489d2d13d85c44aad90d85960d9a52671058da088cbbfbbc92921a0fd1fe98c091ab1fb8fb684b04208715a1e2bfbbcd24ad45b2507c676a72bf33443ab6e3ec8fc430f2b61eb115f0999cec92fc1808918f52b1925c72c6fa2714f684be8d518b47fe18f0260721c4a2370d66a86b4887f2d3885ccca3ca38f984c14de32c0454ce63561e327911c232695b8527a94e40c268e88e911680763f4f8188433b1487614a6cfaf491379bf2955493f394555676e8354a3e580bb997c5787813000fc6f218312726d3f661d2b8384ca9d2318a1b23f8aa8838daa1e3b24157133564f1693edb591fc87624d5944238ea00dcf34973f52e4a81a326a6f3dd3ca675c3ce33043a3c9940553eb0a460aa0b8e82c1b2c367553595447393a8d8bf6a4b26063c6c41dc4548c685cebcd0f33bfa92c4b479700b794651a435cf47bfe884bbe2c30075cefb8309b3c3beed3986357cf227bda18ded69accdc0d876bb42fb8f5d397035a2cda5b81d95546bbb0556a26b137102ecb025a409267b1cfdacb9a490d0b3326ee4793e3deb9f3ad41c96a3e33d9e4d03980021e39df42e3fb42d5ec5a37f9889424944f0f36026cae6a979099500f5840716bec771b5f7c3db33f7bb9ab22db33c20f524555327cd3ff909d290d4151037bd69f43bd9aef1587206b125c8da143a850df22c7448a81bb19fee69cdd746d0ae2174d11d43a60960d3cd37a6ec60e208fabef7068007e5387433215ec979d8b14b62828ca34ae667d4d603406a9f008ad9ea0c43e2422b2a77e75a9a9def30f1ea248cba57029346999cf524d153efb79f3fed3493ca81a741ef72b918b2b089e97177670338df8c397226fa04c6994f05e327efb7368e4277572bbf21d595dca851736222cc6300745f0229a650a634a779060ee711035fc558c2cfb95b22ac9fca7845cd3518b8d2206bbff04940ca1d3a8666a55cdcfa0badec4843fafb99c67bb9449a5e5da51634d3fa695770b71274301013cc4f7c3a0101dd2ea993199fb8ad16d881817c54abb60757e2a4430858666cbbb944ed093be87aea4c8fe5af8d105993ed0156e4e4e3829e59130337842a8d53f60ba2a07b29a69225a72a28a95109fcf85b4274a126f3d8718557da6fc216079a2c48b261a03df6a8391220b64cdf2c4c12c868c1ab9cd604f723b6da80f1581b710dec06c8cf4d09fa421a4215f9c3af3a5546276a8e3e7806613b913d57afa1ba9663778af9007c6105e3b09736c42d7e362c053f0063a04e84f01be6c068cea958119b0e2c24806b8310da8afd5330d6499fc6c7c9a458b24225fb7cac2532c04855b4fd57373adc72025961e4aeb0c1ae39c1d1b6c3b640a22a85126547e981006d77d7901f64a996a91984d60ec27ec667b19cc8d6def8730df384c5103bad2b33db5012692a18fa28df52365b5423e499f0fbd991886a03a6705d8970b623a93d969721ad5f38594941e8c80cdbefb62a2f7202274ad6514f223e6322d19c8ef0fce6f8af3c723e3afbc15d2dccb396d3c533bf746229b7239295d898f59791855fb570247cdf63800a9389a1b7939cb3281c0355aeac26c8e878da19cb534db3d095c16befd4d752f16101bea2081d2bd8f24f6082b23a69822d424be2b450bbd25b005d65a82279ed69b5d0991edae15979232ff277fcaa1911a31fc1c9e5810a8d0bf935915473795b8e4d00ceadbf74725f6c8944ea37de630e7488889d9e49aa23228a4bd5ee0a8197c2be3165f0c81b1e856df32338ef9c6cafb28877229b88edf166302dd5c838fbc5eb5a96df3cc636fc5473b3bc1f3071d93e070b6454a221a6acffc955cfd647ea288bb68d0303d1bf0a13ad149d8426528c2d16f59012414c1538f14214b2ca90f92493f59b877e60d4cda032d8005032dffeb9f6e14e75571e4736eca3da1eead67f1072a9caecc86224aa7356c0e500b6dc80b1560088339f1b2b9ed6e77e0926310239f32d0839613bdb0c58df6794e8b7ddd5d234cf6badafbcfa84f45686531734058f5578c8e1978823da195361a521af561ce7b4a30a8c1a8ccbe31c77e17e90710acc4fc1aa2f04094ff1bcac2842cac70568c41cd86f6f39e8a1b17342cda0c54525a530eec471f9985e58343c33249398f59364827e3a07686f4323c453382ec12a3129d4268a6bc0de30a55665c43656d8242211c11a45e124afe712be862463ba194e49d5cfe26e9b286fdb2d4988254d2a08d2c68ab4ae21cdfb28559f84a429dcfc40f9e65a2e896f93e8b23b371ecb1f499fe237041c615ec84a8425a2ea304b737697c8bbf338c799bafa0950f6b9f139441198a5ea8bbe5427bab2b5b8a2cd7746cfe038d61ff047cc19e803b72239434a5fdce9d8b8e93e183694587f31176d554e07a1e2fbbac1279524e349b4f82fad43647f03238c2d241babbb4fcd2a008a11d6acc5594fa0127947a92bbba510e34a6f38798eb7d5c6c88df7b35bf34429d7aadfc50643e2b0cd2ac7b9e89ec78d32a0496628747adc871611ce460a247145750adc6409ccfc438a41aa13b4b6b374c75b1c4493071ccb3768e23f15816e523e29f225bb178a5933525ce7c1cef306528b09cdc5fc5a9393a16d84702c301b85030201d64dba859f1aec68c34614fb0c0dec0b744ff284e41c514db02c728d052f1fdda8792f9bdb8f6efec31a43753011fb8f8920e4f2b372d5ed0aa7b9a9262df40ffe1095b662a08ab95df0d8d9beaa01152233276d419c37ba8ff521ae1bf2a854fb834d5b8f7352ac4b73c632f457dad6edd4eb5d90e1eb0fe9e6b01221daa16440860054732c1c052035b0388606f6270a1d4bef140113c5c4318ad09d8dc0cfcc4446225e5b795b3adde3b20164f253c74a2d978b773ca4ced6161c5d69b03826e1ce89df84b20e3e32cdd13a4821d878367fc8557db1f0086792a0297a46e7ea6f3586be1a9530b4f462f406df4e9a1324cbbf1024045c7a8fced8249bb9a7b63f4e9a95ec3538e408a74b636be6b9c52b0d982de74c3f417fd065f5c705b30715c042d220e815ecfa06f354fbe931f3454d8affe75c50d77e9e3844733e9dd8d734956f9b47dbaeea509082e395a32d739a287798959fbb796b8ae26585bf62f5ce5ec750508367b780498b4f3e13df38ba15f9c38e77acffe4755a1a4fc0ad8e6a95b4b3bacc69b7b2e3a84b738060a5be9efd65080722dcd20c17eb83e112b449ea7747b3fffa85ea3209fc27601bf856734e1c337f2034e057c72ea6df929eb8982c724155b700d3ef50f5fbd3c2f7f68f21f827ab95847506d65786e211781ad689caf25ac22baa045faa833100f5b8855f30f0d6926a8ffcbe920e2e63280db8448fd0af1a09f1dbbe4533b8763c5ccfa9e9d6d4ddbd3e476e79e0fb2f805848fb9d847d87a1d9dc56206264fd723ffd31c47ad29f2d06fa19d02aa65a32a44a7ee814b41368d49a1a422ee5b6c3a1c8733a101f9c5e6309ca5f36470e94d3253c9abb473362900e656114650d9b8abef5d9f8eb110a991261385a0b031eb1eee5f24da6a0396a4aa59a126406ce917da3f1d12cea13a14eeffc1bb7f81a02c0991172b2e9f78c966f9c45621187b304a5f2305707a34440a7d25c6e8e173e87729c4d01dfae91ac9a1c63f2c769a297fa732c88dcb10344b39e07099aa96fac061636033422568f4cf7cd8ba1d2636a4b3d7ad8071fc9838456bd66065904c5f46372cdc7f628593c5c6a080e320b43dc0cb1142779fbed0ff0595b161ca03c8067912f009ef8b7a7ec38653420e729d8e66343588a0abed2b895b055ad663aba6c2f7b9cb617a70138b8e251173521ebeef7cf43bd04e5479b645f6b5890dbfd27a7a726405a622c7fd01e601292931230d4f2bdfe4ba1d9818bb05f9f7b2b17a9126139bc82cf8b1f92bcb387f1e7dfbd7bf49e0becb263b0f9c0d7ad60057c260a2e058dd52b05e5396b3e4f5e223d2ac9c461810d8ba1f58f0c2324668139a030d75ac4c0a52115111979620252c6c4c2b3c72f0325a8a4ca41128f144dcea183ff2393fccfb20cb012b2990f6d68e42590bb8569d8c1b971aec1110b030465bbe090a365281f5dc542552d117f03c51c072b4a8325662b3f40ce5006e52c8140030f4ddfc1e8b0e5a09d87d60e81599d0b737176b3b5afba144c808472f6eb45037f7e7686d1b72167acf74d9738efbaff8b60a2151f2792368969ee54558263027c0bbeb0b8a1ee85fcf1ea06803ae389eac1249cab2bffc459b4ea206e6b72015c59daf0180187a2321c0b1b6ede650b5668aa922c52488cbc9c823cdb4eedeb446e82d961745b9e4b34cbe287d11ffad66f25f566cb98d53d9ff1701e452fdf081f92a3d57222506740f9ce2322459afec26f265b11ed3612000f75638633fa873902e6eb1ce4157c0247d350a37b0604a5a6a2e80bd3a0eaa1b05128455c9b4b3aa36383700dccd6702e6c3dd8773ba4497359e4cffe915c7416bd13f3f863ccaa48d73e0132cd6a48059d84374792628dc37164396e775876d1e812726cbe2791c2b2c9b803046181ecf1df559146567ea04533e47715d83bf45803091d096a1b65628cbb0f6891e47a003aadb52766b0bfa37fa28f74aed14c0bdb2543c57222150db0287e3687f5d1d127169104b11b862c329ae44289e6e38cb996c2f3eb3316c80ed66d96a95fb0c831abb2504ee221d9e25a8be02d8f799a72896ac0098d892ecc698f75619eff77854a48099084bedffcd6d48a210ded20c743512ad033b46ad38a33a47e104f314f36a2436703f20d95a73c0c8e04bc2caed8ac068b5865cb65047caa127c67934b0003a79748ac8074249ee9d56ae7c12dcde0200a28107a08cd52f68c9ebc8dad4d8c22a1f5624f46537c7c3702dd6d821a6392b74892ff7d41e0743cd98c7484d6fe79ecb79f98827d873e06a928fe064717fb434c791e5b71e01895509a8458e13ff779334ff45b0de52d1865d091abca1f198bf9ad91adccdac70ea51db7bad5707073d4ebde87919ad32092d866d141c2e616943063e02443163806b45e06bae9d369a53cce10838213ad8921cbb29744996a33c12271ea4c7de9df81e5504e4cf65dd9fd10835534467e4de8f2e4759421bf12981272676ebb521c94d41fc5bc4a4b0891de0ac26e578285f64c21f79c35f6d152dd361d8deb098d8a8a9c5c80c3c1d40a6b760e2bac0a170a0a0c24abb6886add8cb11954d89b97ac6bd29b8287fc2677d71fd020e8aa20729a7fea5ef792c65cb624376638a5ad17c420f7a1d012ba5293016c69df989f45ac4656387fa177f83b69c56a4ddb6288415dc18c1052a7b933c644e1949e1cdbfa12d36a10f32b95e9e016588263ac5d104f4f814b3c32387b0f8f9532ee4e9ba8972180f8dd101bc79984245c9e70e305ad80d6b673802812cc733885363c8855f8c6fef576a0c6c02eb3706f711abef2131bb152151bc94839259f15abfa285da4470346058f3d12a206bf1db2a00f105160b57e132d5d6d39631e1610b5bc438aec2923078e5e40063ec73e0cb9ab63cf84cdbf8e54a314cae08f9db0a6e46b17d3fa7bbcb0e09433b122e8d2b64c67315014395c86159b133c5642a22b27a9e4ae902828e1037bde26888cf2f2c79126e5adc4300173c45a886ea72c856e465172689322cdcea2e26a41858c558aa4f11125feaa03e4b8784f1ba86ad375f9dad605d180c20a238ac8471a1e924d47737887e4ff96b6ae3a0250d0e59a5c46ef85852c9b07925013b46186780a650438f1d2924dff3d00d0025bf1e308d22a21740adaee6f310888abe663efa54966c73f5d453c5651a0909fde6051caee34ec235b0f2ef8fedbec5470c23c1d28b77caf928ef260309d3b637c1628ad7daa380413cbeca90cbf9d80cde80fa0763dbaa91f0329f2d3cf8377b26cbbafb404ff75cac79364488c1fb9a7c77de8be18ca797cb49952c0feb693ea1a625e3997a1352375ede97e74a756a47b37f957f3641d00f71377de8b8a715b001db20210eebb97f11a5e430f130761d65ef02f9d893325426213121eba871d0c124368a9bf784688966c1384ea38557c2d265401a09f78afadebe6dc93be54f5cd41c959fe9242fa1c14aa28e8ca82ef2440758152701b46746090c5476dbae33f43d87381b975ff3bad4b0f05d187f3fbf7e25e00576c46efd3aa2c3923afa0a20da1125f1924f01609dbf4f4ed4304d714531cdf0569c5766b604e86dafccdcc2b8318b805cb52851113345fc582aa777eb6111a1340aea2f5dd885bbfa58181520f5e2bc8594fb43d42d635c2a5c8d31b049507db14583aeffc0ad3bba67cd003b093bc91b2f41198c601496ed4721cd1f595c9bf15061614d4e0e3d68aec51115170966d56cf0a7f92adb2a725de541b35f18b77e8f073ea2b0bb94c99e2e29834f4ca6cbfac02f14f3f6a1b02aff861dd4af1b68c56ef2378a39fdd45d025849893604fb58470507f7ed66f7d795f0137553abb10d1705d6b734dd85504faf9d96ea04c48af631e1357dc56be490e0dd00f6157ba3b823a0437924e34ae1c2bf81a62a619bce860984aa64458c1e8a9953506966c83059ac5f29451380753c2e9fe3b7029f58a56b25698d5db089e189a56fcf19f157dd9bb328bc7a29bbf200287474db6c957d2442865e6d01b8f4513442e12706de8a8910b1f9009a17e7792ef70a695f5b08b0b229ea8e4a0c4ed0daff006fc3893293cfb48aa87a6b177aec21020c247d46cd8b4a8c37b930bba23cb410b5527bcc6b38dd3681ef26b15619c849bdf3342bf1934ac1e99a39395851be6d9dbfe867fe7a7e5f3ac5ce79544cc57a8fdf674e9bc5f59b1f85247d2ec06a12c5def23fcd99629a55c3992fad2f3ed7ac33f8059378e949a66c56008551e4e9830e50efebe4b4dcee95dacb3630824b2daa92173b8d53e0a8422cef7267df3a2c1270b2e8317553768d8b2011041ae23276b146eb1f09781e942c1fb8204b2722633b0f60878c4c745b204a52802af754c1a218fea2a9f962d98d300d1be7a9b3aca6deac92da5fc01cdad056bfaa8a337672f6fdb9b3237aba8d2dd5762a797d69197752078984fdd8c7fd904183c44cc6ee1fe0906b495de59072f902c8eab58c29b1a7df8da2eeef5dfdfb3446e8af7bab6148e2ef48b56db9ceb1b4e50df762f3e86305a468ef2639df8daf03677fa0f4256c3943fcd1e79ff81ab22bfa10d492355fe16dcda9ae2e482c1625bba09a02c10dd8056dbca529642de4c2d0fcf3403b1cd06e8ab23496d05c04667850658cab20f6186916f4ae306be0f1720df4fa7ee577adb58703885a06cc50e42902ebe223e6a757674c16f1e0cabcb0f8b263f45458f41a645ead985574b45ca3760b4a416bb8984794aafadf70915e0259b7838b5bca47409968da640db43e2454728e1d38b023cdaa0f5a1e020b167549427603188cbc273daf57497ac40c51b17d881172fd24382394583974b264a22dda0d479b061eb1826da8f97385905263469f1ad86d52f6e8b18201f6e1238f050338a4d9330ca4ec7bc834df6eeff83de48d7ef2987a7d1fe6563a6a2b86fba9261285baccbe3fee51e7308e307dfb85c7e910d08dccf8a797459f8d95a0ef2115ab11fe49f5a55f812e96e38338d8518a8f97e82647167c70c0c59457ff8952f05da6adc887d8428e9a6d85e277a0a14d32ce10fe44789af2c7d42ab1a9054a9c7009d22b344e5a3f79d52eda1ec9741968b67ece52acc8cfc872ea719def49c98066c4adb80eb36dd9f18cd72809e1016bb37554f7440a873ad34fdae50b971e79e80ee82f3574ff2093cdd40c0d214ab8b9233600253b4665ac969e9a9515db952d657d427778795f21b7e761b3162c7d1852ca5ed7e8159910eb5217648ac6da4e4fa9540652df52c3c8bfad80b06625b6229f682f03c4050b5021809f11af1584b4424935b112dcbd63ad092d2b07ed4b3b0e1a65050291278d14629ee98641e4b6611067e3e74980963435071cb1636e28c6a34258e211368db7458ea724027c65510d3ba8caa0bb686398e974d0b69ecb266b15b30795227c0c832e659e2fff3ade844eac1c96311b42ca6a58d6d65206951ed7c7d9d16ca3a023dbfb1af1c3aff0119d759674356156c8728f2bea9f18d803aed69f4c05f6428a397b9a0a5124c48c3a90283ea5983ab2f1d09400c50ea72dc3aae6891208c5a399566bd0126cf984427900f61ab85d552837aec72ff74c48adae454a21f7891a889b7cf4a594c09f31f66123265e54ce90cfc1e7612d4c84a74200979083c78f656cd86b3b97b9bbeb4e4a688afc997ff30844d0d43549f11caf68db999f9107dc42f79d3fbe9a5153ef941781a1ba5d44e189b4d14ad083f1d09089f39ca8c472df1e2286add6a06cb426cbd38a50a219c9f5af57a0f3b835de6d7323beec522e315e005316004bb45345dd2d6251ccdd441be8478a802c5881dbe9256179fc6831f9807baaaeda15132d6b97da68b4e8b95215e8b5321c1ee2379f0ab9409b21bfbfc5a5bdb4bf35d0397a55707d87d634f26cda178202d9cc24992502d18bfc359cc1e459ae44c2444cc4fda283f0c84406e23c0bb6019378e54a019a97046e2f20ff2788f4114a7134cc464e1c60429e4cfc0d95f5e578685e21c38a321e1c1b62d5914cfb07b8136d2e61a022abb2ea93675516ba82c1890c86a61d85e427ff29d3f2643d5044781e54aa051e6ec3befbdf83ba550b241830c2440bdfe5f7314fba541ea5a5bba2f2ad9d8e9b1e9b97029a218c46738798a2b7260d3f8f3903f0bde0401943b28418a2106636b0aefde864f1b52bcc095d9517cfab77285f83e515f34673987b3050e17c3d88c228ba901fdc986c7123983ea6a5b9accc0515975b8aa1c565b011942f70062c1c0016f07c1673bf8138048133febb58eb6a36a7b78769cbb84edacdc9118122241241245c4c71955ee2738dc457d13d380b24f2e1064cb090c414ae343d0f23c835517c25714b0bc29c076affbf7a9250bd48b9dcc36e0e87e2ecae5006753c6106c630eb94f4c91037a833b31a020aad472ead40521474952404e962ac426393a538aeaeb8f70878bd74de715c2fa304e230cf0b8e2d019130c3a7ce59f4f07d2b54a87edb484a922899fa0c2671cdd1d33c4ce95c931f35e667d498ca6868d916a0105d505656c81b204bd05faa3adb4bfadca01de2464f2790438c950f99ca8d4db3f297f9fdd74f77f49964151d54b3ef3f84fd6197b07d26f5dd9663c9fc4f9f49e92e08102e965d5ee8c2af544c3285e3397891187a7f1f285f4be82d34b43f7f59808fae53cfeea077dbfafa03690825799f5665e2b5a6639c0debec3d472b1a3ca12165e729d4b1de835a5896440d3366a549bc0876e3e5b478313e81f381d4c26834221d5875f53264b7d8921efe5e930192e8f5d7e62d095b20241716f0785b433adc84d8f3abe02a333274f1b8071d5fae9cde078de76b27f0ad0984554bc8f814821efacd8a49db0bcb51e1de495f1a5cce76c0b580b1d5499dd1057c55270ee2f10584d3e6c0fe65736af4140ffe0d2b557681a32172e36ba27821a6b3e215164383d5aab81e2463ce1f42ff0bd17ded8ca22f8721603c4c0b18e92b906c0612bd68a8929c556804915289b1f22642fca26a44386dbc5c4e98fa4593db0ae1f9827c9b91bde77d1b459045d1dd4b58eec510b76aa5c8df7d56568022b6d6eea4937d229cce6a14c2c5d3d88d190ff2c8caf766b63f3284853177e5322b2d73d82d49235c5e0640bb95090acc15c4539d3dad4193211a21b3bbae7b682869ca07b855c3639b1d63f857370cb11c3b381cccd027c165bd972f787da50aa2c020e0f52ebd10f66274769c880ce0243bf113cb45111273a9086b06ac311cf650dc824104685799a0390085db8380da6b8417030938760701a028e238917e40e1f546b43c8fd45f906838c3c3d240f17147f6f7d1b083b7df5fa0e4510d27eee25e0d69c0f7aa0a45b3122141818b54f43c16de2904064e76e41df5fc3c76ee926aa80b8cf3e63b38b24b60628a558a4374ad7f6284247153da6ae7d11a0fe6b321f7729feffab455003b20d808c76abb0919dfe02114949480bd98b3fc88e0b55ed6ab5901688704108a7b9f4eddb0f3fde570601af781833ea0ea07163e04dfb07f58c84079511744387b4c3a2bc78f9b74241efd1ea7999f16de4e4a2edd6bdd6c1d80a82d1617c27204a09557d7c31c13f086bf08f22bf3f01546fa5016776cb5702c20b3a36e2b7d279269d770471134b60a3845608d4e86d3b0a0a58e298a9b1da3f0f90cce87495feb23b74cf42d5355aa5ece936ba1d9383970e632e7682630d1f2358cd274b4d1f6262281fa527c2fa363ab415d56e44a438a8b4d89c1e226e49b6e7ff1fdfafc83cd50407c08ef9632312db6a42aadf2616a6594756854d7c38e887b8b8a9e3bc34d204c2e71e39593191a68e0fdcbc66d524316919ec27bc8a72a213bbfd09ce5e1552c53121e740bcbc8429430b9275cd5c81875435b8cf57434b9e8413a930222d84cf9288c3c622c4ffe0245ff868078170969faf7a7a6889b001ade13d70a14dc3122a50615a70b457f00d2206bef5a6c37106cbdb6e014f02bcb380ecf602dfb7ee4c1e0df1b5950b5cd6ec59bba7a9cce23b1d59099daeedfce37c55105c948975a036c5aff42ed2c04b991f45198de1d507dc3df99edbb6c693ffb43aa3f050118c8d878d686cc607be75c10f02cba2b77e8a4b13dff3500a158eda48ce5fa9e9948e35b048c37ecd89211c71f6d492b4b2f9a9c4befac0cc03d6cc6fa67e4a704790d8948a17b710ee85ce85eb5397227909fb27df3ea9af00f59237cab8c656865804d9b52f344bc5f4777eb0e9170a6a0fb390093baa0e96241bf5011487b66efc5498b6e2454b1f58c0e206eec4e098428b8987ea4eb1096141eb961818f716ce955ba53d3f8b57aa2f9e5d90a839cc9aaa57d9aedfd62aad7f662373cdc33648151873ece8df0b7e8b726cdc8b11f687041003f4e82aef2f28d4804ed097f37432e5346b32c5871c6fb31357d0c96b11362ea35db73e4204c541d73f51e85d38e404ba646648d976ea26374beb7fa3faccac86d6033a1c50816af238c84cc571ca5594475932ac8618f1596116e2e7e3e127329878a25cf8b44cbf005c203782c239e6fac4080022291e122cfe178ce8434fcd3ac14ee8949d0e223ad1cef8a6dda2ee788460422a63baaddfe950638051a3d9b92772bdd1db101d0eff330291a68a278224ac93a545b4b846d529b49495837ce2d48540e3549223594033c4be592924d9ca318d4cd5026803d1d7eb65f95a33528842564309883d85f82dbf83097b58a57512c500a90ae2396344c2e7d3c77fa461f34158a79ae8678ab443791cfb793b67694005bdd7159ca6fbeb84b43b7e2023143b2bc3cdb401252466d50b1522ef92fb7197b6cb1c771cf3d9ba71ffae3c679a2129d82d2b611589f27ec36bf0d3a516e972910a89e5ab9d13f8d2a3b182df152ed0b2a6152e0a7bae0c7829b8c4398109e12e65407b1b6a0e0537fe00fbf052a1c11f002116ba82823865136901df2b7340363cf5cccc2632b7647c53aa474557cb64afa76f96a684eab4bce356ab7a46f0626226da8bc46ae0db1130edb4c1d7f6b13f0150650f1f543b9b84a294e9b4b6ba674401907f69c8364e8a9fd418f89d10d53d2ce5b409bf556a3e49f43b10f28e196bb9245158eef8f1220abb059c8c566c5f8dd89d49d9ae1fb435134280990720de1e9866fa39321eff354dc1ad1a9ecf5773640628c390d775482d5cde2818e77d2e1ee9056e00f72f26363a3d0e2ab8ed3879d29e52150b31361e0d3833ad9ae60cf08865face30dde73d2a0035ca8edf5a1d334a7b7cbac2985206d6a1ad296ad8dace65baf8069f3dab2f2692825a6cd4f52a82a3558fe8d7219ff607e887c3e8fc20d9c7e85effa38fa2179e11074df17c8fb8e2b5813fe38cf8b6078575b51263cdd1ddf102726e531696e7fc08023c8f7c712a8dd286c1d303592c1fd6a7593c3a7ce1a787c045b00a7262c686dc2ef9f60093492a7523d5cfc1c07acb5a771f9997972b1bc86851e1e419b9ddd06ac746fb94f51fa56cefb0ec5874bdb93302dd0ede7b91a640f3483c70eb4320edae09409c5331d34a22b78e1a47eb38d77e119a9c7a5d3adc185ead6c8096ac9899f23f1411a0b53ba393156db157c3f27b88511dda9acd811fc75851c4e2aa00e14544c55e0d581de74e8b575a0414c21c944e9381d9049a0e831c065fcb3f84790d09b111598838d9770047b03a6edd27464f6430c3d1d6619415a801facc6ad3b149061582102f570018195c6c39e4a9c9bd3619cb3f230894a5d28bac23104b7092e8e74e99aa99e1fc3c9a15d216a682925d0cc266d6fbe018284b288df27e888b554fedc7f10c8177f7c0bf0023678386f5ce97343e4f1e15e3ac21e3a86bd81e2f865e2469f38a32d2e31011c0aa4d78434ebbb3e0724f846bd5787d98d3bb6046491ed518d9b331a3a2ccca4de4a68b25ef9f73f5617e9fc2e723f8efb66836a893cd53f2dd129e43a268e404518402679eea779b5599e549ebfc8a9abe1d51c653663d7361a10f4602876c97f1ce99ed3a6df9db039e5d284f7b08d9da26db8d1840a2c52cf74bbf45e82752eb926ff3f8041438ad6bf196c9bef2b70091d7e04f193c84a4d8987ba5fcad8f14840e10768f62e5eb3af38c564d0c48befc7d2e6f14a4ba0fb307e9b76f7f2a35049349282ebd7475588c2d96d231b8efd01785f646faa50765eed27d6e38b4a2fd9592a5a7add68d1b3c2baff2b13ab40af698707709b4fde083dd7c38fc70bee15a1086e94f8716f4d032d87c1f6da0c7fd92f1ed205fc5ee40fdd569ee1977ffb8a081a76b78283ac716b691cb8381ae49a97b067359b6f019a4fd53bd04e541379b02b43a3360157e3615e031ab915d1be38817389d376eadd7e52a6562b5c301eb609b59ff55983386ef1536906a5552ff3bf0de0c364a8fbf26f494387af142d2f6e256a0b5cf993759fd1141f649a0586696703f16837fc530017f967d0fdf1e3f00bfb5c82661dff24340786285c896b618a316f2f740a2d0b0b9da15a3918e9e735ee651f5ed814c40332e2823f1916dbc236ba76b4d0ff1fa05fccd902ff48f0aa8111f807155723477fabb7578ad3e85ff8ac018831039249e433c4d06c9c2f45933960fb7d24987b97cba4a3862baf98481efa7beea19c96d0de80dbc5cbd9a87100581b54405f6f53f5da9b254bca8bf1a4bd2b12502b2c91ec01000748102c021a1050c043a10b48781b289834bce6703ca95b7ebe5449c0f1c47547ab79d212e901032adb6eb83a894d98e37158571dc0d614099397c223407fee07b0e76970a01274a8ecb97830c2ed83241f6077b72a8a5ba9854b31390479d73b4ebb4aa8a0c439e686036074b87f6329290b4df0d1e434abb22306a963a31ffa63551e1324ba90739034a3fc39c7d748c451bfbc5107bd97d144d255abf79207572ee6b3e1433195936286a10d1e11ec80381701b7a282674bd66a50d1d283331a27802bdd57e17382866e4c7a7c3e58d212f8529e9f46209573f75fa5cf4e6dd35907f53ab5b21b1ddb208c84107b4ee40005c1cbe28c183fe894c68112dacebe47d325042099eb05453ad6a8e599342944a8f508dea18e3e55ca8c18095596ad82e9451a4170cc8cff887b03dbcb18ca6877a6de60a37f5560788e978099d24672192fba8038269eaa3253e4d70db6a44484b6817b76193b01e6e371439d76d116095a104563adae18e001e86be4980a3bfa0354dc6bf7ffd76b383e77913f00e0bb84cfa71da11545f33f23b9d97e138d6e65e2bd7d3bbaa7a87baa1c05e05816210252ea7240e858b5de33140279d9bf024d099cf2579727afda8c9a2343fc4ac3f56a737d8f076c70b9733f7e1b91980474b03cba91bc7a60d7c05dc4fedd5aaca7507092bd206d57fc97231a2c7a9b019851bebc4032062b1b5dffe29cc6db9b145f5701ad439d3dbc801f13bc0603101e34644abf16ec80479c638e77968e6418edec371932e153ecb6bd7cece0c901b36a9d792799f4425f192a09da8e1e19215d76671bfbcad556dd7c726ea78f7c44908a3a030968e749a51a3d2dacc3b5eea44124b398a5863cab73969ae3a9588db6418f3c6c4d76a166bd76cdf37a059ff4bac3f87cb6ddbbfc231cc5017101bb0024a27afd992652bf059f33bf2f3037acfc206c88721b9dddf3181190c3ac812567c107eef18afe71fedb1c508911a9994a791156ce161721a1837eec75661f21bb2484686b19991b46845fdd73b7bce70ae30a463ff58c989200dbbae1f5fad014a54cc109bf58f42267551a1f695e56c6997f058dfa0b495483ee723e01159c0b3a46d1c219286e66add52e22bbd506c75881676fa8cdb3d7322dd7b9e84d93dd7025e535eec3410b1dec4081cc7cd3cb9174d18a5d415d9f9991396f2fb1493e951ff5fca0bf8a718b1500d14e6c3670413e0ea21841682934702f4ddacbe402cb99b63da8f7cc387d47ecf8c35e3239cca022f9e80c22119979de0e6066dec1a33dddcfebd439b337b66b70c016d54c318b7211949bc77fe212b237c324e53d73a1faf76862818e7cc089d6fa9cb71c13ce7bfa48706a8e1054efa38cbafb7c392dca69fe3ed39aeca2f08f1aaf5d6f09bf093439803ea5aa6c8b0105a7aeaf507707992560b570d2cc9d102667397ba95662e976cf06e694bd5ae70d96159b51ccad0225e9aa46fc37d028eea7232ca21bdef4c7849db546799ffa1f2f58d33088d2e9a7176d4589746865541206864309d4e090ee14b533d2250b2a7fd9d84f9b184e16a653cf93ff028f2ce805822f0863a191122d5488912bd026d4b72db630305906b2ff0b734e35a724b9a2cade11196f03b610b0fe9227864805ccb7cc50d3dfda4591092ce1b205c5496a58ab6d933dabb6cddad9cd0ca848ba681849b7670b01f0225cdb36d10a8b05d8721ab44ed4e89d67b0f965d8aaf843952381de82d8b2e2c86d5fbafadbf6164065d9a9edc2cfca84c6bd3cc16182045493072c06c7228b90bbd4b453a22df9a6cbc1ca27e1bc4a59c1163d921f604017bc24db500a05b6ae801595f776a11f67ffeff2aa206a28ff673510e1aafcf19cffaf5c56c35e4745a807ead146313cf15b694a06c66e8fc139b1acfd3fcbb87d3e9fcf0dff959d9da6331ad6854f7f85622cc6a6e8cc4c9436ae8b1ad9151520351b51f1e36abd864995264acbbe008b580424150cad6f96770f3889386ad32515ef592a1eeb5aafa3688bc386672314e341e4ccb932a9806db33ad5a1227f86e0ebfe02ad3310cb31efa66a0f65649e47a6f7a337965178c0baacf6d01f0b93697bf4e6354569eb77e7d57cfc3e980bc35bd7b6447e4da09289fd5fd7e0b84147e5436b05d82e03edb3d69e5da6a4842f7739e76197bea9ed1638155b8120d50d5bbbef6b53fdf855aab51748586a1ff3ad0a569c699147b8c1b86ccbba510873b179ac7de9544db382c48e2a462b56080da9cd9b58241b23dbd7c1eceada3c539b35fb42c5e26f7ec0aca91bdac522af7befe090b77d3966be3551e3b20ec02235970413698a51fdb20e68a86882746b8cf69e8c08cabf9d4471e06c7e10df808768b64c45972ed623196c0144348fbdc6178a3a04a5e25e23a2a7523aa143236c1428fabdced1c5856e9a2ba97fa408c3ba88b3158f2669c23fa6a5d679d4b2c3177502c189ddeca20c28c15bb3c3ce3bb35255f3b4dce981f47254680d1b5e4eb20f83c1dd605ccce8188dfa24bb9c2e477df4eb99394afd2a13c194213e04952c9995475300d90d973d215be11650e90e0083d78f6fbdd641b48e731f8e14e6d1efef19be7eb8d0648a9cb8ce543920ec9803bfc73ed522509d224d14a380e04e4d6930a91f03e05be548c8ab993f596a358553670550ec2d0b2e5ed0d3be32bc21b4d4ae5d2c79d21ef9c02ef1371737991d1631e417d133efc8e77ac6ae52a9f3ee2713fe428dc412682c081de0d28417e4544ba214395330c70fffe10db494148198b2989fbd5c446be9e9a20a6382c0709cc30ded770ae60053cca092d7849380c241248df3f18d8fdf16b039b4da5c51d2049a3f24c6e294192fc8e8223a8c47bda9a3c2fa82f1a91b7b8eafb2167fd72c60ad6b4e4cab354467bdec2fc8fd68d9c8c3bd6b1f4b1b616166f75e843f7ce4603066fc47d0f857a6bffb5d50ce0c87ca7f240e7fe0a07d2b9fde735a0315165bd30b7234646fb70e1083a2ad44d2421c89f5bc0962133a20dfa7a284054fce7ae90f0e1caa7a72794fe3d66f974b884f7cec4355eb6a45e0873a81598a135c76965fbdc3dca786c13f09c7e37b883e6af2913595a7de62be1f2b7c158a71910a3928d49343d73659602d7228f90329f05483f70e662ad8b570b40f3e689fce7a5041197e6a9289931dd8187ddb39aaf28616f1fd12f040730635a17257537f01efd23e58cd66c5dce2ee1210b408af507b6fff26fdd94b39f01606b9ab98447fbae447bd7e79850b8ef79a323ee52e91d9d156da52f7a71cd369171b86556f2803636c7465110390f732d05f0cf95a241ca69b7b5009ff8587912cda3278513dd0e1ba50d9192e564bb174ccf3fa750c6bca63fd5cafc73ec1c0173e4c330409ac20fee7163694553686b6e5b6959fbb546bd2e031a7a42ea8bceb5eb35889b98cbaa0a4bc0159f65c2faaa1b9ca50f9420a3574085cc800c99b6b18a8969e53e25c22f39b0fa5af7dd7e0c5c9cf6414c73d2f99c08cde61e608fa3834a7fd02c9fb90c501792faeed5e4a3451efc66e4eefd1010e620644a1fec0a8e8db653d714292e55d3f95b08da2bd491efa61dee3217aa9cdf761593a7fbed3bdfd9b22ac7f4a2f8b773ab168cda079cf9dd59446cde1471334a69eb5b2bfdb4fe451381757f4d9f94485ee72b22caca81fdd800bd8be9bdc0ecd93f0e2f0eb574217b11933102b38808729045fc49149054870db8aec1eba49284a5074100d6d368e00eaac315438374c8344dcea87f5eff4b39c33900baf8357252825a8aa8c5ef9e8819ff7e98cee1bc4484197532306d7a9b93fc1561a6a5c103d586935c34396b53987cf20c11dd53796a33b8348aaf13733a9896a9fc95edb7909bf35c8f17fc64268d6200fff085defecb7aecea8ebccea14d5765141d607536f83aa21c52d018dd6335c6fd097e66610baf6bd4cc0cdcc636d697d7256cfc00ec492823c427ba8811ea858722592df5504a972fadab716206abb2ca84c760dcf8b09000ee9288e452989ee2c81baa9d8111976b149d8a5a0cdadc7b2e3433ea881b2010176a23c825d98b4f6a8a7eb91d79285ba1cca826afe8788bc0539c5e205a17fc167743787d242d7ab94b7b5b86bd7b5221abe9fd3576a3792b32a8cd601af4ed1fddd5838be36c04c105d896b5c4812ac29b67abddab5861ec581bba146d581e812161bdeb80842bd29431d3165d41153454cf7d0bdff9a2b384e916856ae62e888df06b953ef7e1de0a9281e0d8431cfef4ac4607c33b065eeb28d186d736b61e5892d1814eeae0aecd8fc8fb13acba860fe99aa2a6619f804f4bbb0e457647b5e0378bf481d84840f3d1838a18b1f8c32637114c612876a52ec13c376a8972c966a37436832b5de134544a0cbdd9a53f980e3a937a9ceedd93e3f27497d1c7f3ec150d43bf2f9e92e105fa791447944de02d23976907e1cf1dab5c4e8e4616a17b6362b731aa857e4fd86b17367a11a37e30839a90513e51228ebcd350cd4dc166f96a58fe0f7d5d30d395c23e279f52fefc49e8c260566ea772025a1822951de00478adaaa4c888e79f4f7de646d8969d635bab9b070c2ac151c7f4d2583457e347e8448052775a25558d94035af8337932a17c1994e42262dfb27c8fbb8a4b74a0eadd60f4a5985f77f4f785ed86506ae720ed2d7ee6a489bda47a93d233ef4b5c4fa33926e185139f98bbd3c458d0c1efe1ed92e756d9f56d78c73593e6200a5fd2e5597373f3b2b501dae3d1fbfd5f679dff9d06dc31dd0c50fc2707316d94713be63536b0f790c176e535982cfb827f0748f722b5e9645f0a92fe86078c55356a65cb17c6e62fca189589d929e226dd7f94245e7dc655c7575e231bd812b2f2bda0ed976006b161a07905e0498484ac96e5351d12b91087c2e88906e6a8f887359356fcdf973b567e74156de347694bdcfc49ed9a6e9994e3ae781d74fd8d63b79304d63bcef33f0d0a33e7e02cac014c932f43f4eed26cd1aa80ffd4c039a13f3998e8e957fa69b1742dfdc3fc95ccd7a0228d4e10fa62a079431c1304a889ec8fedc4298494667789e44c65e7f982501ee35ea2e58c069badcdc6c16cf924fc8eb89b4d3d62bb2f02f8261cdae6c305b39480f9aa9f12fe7d6a62996af1ad532ad52508bd75e1e3085aea2751a08ea28aefb5af9e8b2584ffcf36fe1787da95c814389e17976e3124118a11599a77c70609470de56b3f56e14522ca2befcbe1d439d094b886fc1f5798b5b8817cdbc14b89bf8ee9f5552e1585463db320567e41349cb7ae4f196a88f997a5aa86000cdca9f5cecde1b6d7205b350217acb33c1bcce62327b58e68d850e7ae43616e018fb66cd2d63e66e26f82f11b53ae10dff27d2f29eff7a5d04b2dc7f9cc3030da91c39ae2764078ff5dbe221a8d6e2bdcf60d619c05701f9dc9f5fa8f62299251a11a2853790d7e1cfb452edff732ad00f923cfa84034c9614294111aa3b5e602dc05227fd2b553340117663792dee3ee603664e9eca631c6610fad3910e87c488a41f27e704895047292d8952a8d725c0b6010f936cf493d6be002997cb0c99cd2cb23f73d31f3e991a611fbabb7019c7b1fd61bdb68b5b18fbfafedb1818377c37ee9f984ba5b20653454dccffd78ec70bc27afd70c27a2dd52b854fcb794f11263ae1208d4163c03c08298d3e454fc161336bf50bdddd593750731fdb111c556d055f598923fd45737de6dbc32366a85082c33c339974a994138d8ee9e22945a6d01d4ff36ba275439bfc75c530cd9427affdb7f88f9a358ea9af1d8001586b5ca500f39b48dcc2809cbd281fccc804ba23e319fafa76b23aaaee0e3de7c64219027e0a2508a257d27b3032cbe3141e086a96e3df950b8ef032c322b343905d39eb49cad6022767904f920dd4ab42dbd587de305c56485e00f57bb0828e80f12296860139074e20d5cebd69c58fb3872c5634c7f3fce6064074425ddca5ea2928beaa3e37a1fd7a465b4cc2630ad78c65c09f6515a23017d25809230b21390e1cb8a2043ea240d2f0b2a33e7fbc4a7df8f294f81c31a50927c889b02167b35e372ab49c00026e62bae049150cf216180a18ad05a3a0a1d943073b72793264c937d01bc31ed961007e4ddcb38e1c947c4827795d2b3d2cce731395c66699abc829b15952ee0e558479ddfc761bc99331c28a8ea8a2e3a167020240a73310628c7168fc327c7f0f0444345b2190f662729f2a385608bbd72b752f636ee3b75a9498fb8d58abc6eeddfa81029111b056284287cf897c038f53ac5b5e129c8dd734f6a174adc4f81f0c7ed6894f00bba4189ced47f111451ecd1b66fff3bfa31a91d9953bc9fa70e8656176cdeee30d36e0de408a3280270d42f410d16a160fa8e48a79895624f29dfece2ee6ae1fe5c0f04c63c0322706905d43ae7fd5cbaa85db946e074f27f6f5dc095a8eb16a975262e6ce0221569d0ac54c09a692275c8a6c8f05db52a574f4e1f8aa28c7c3b22228a5b7f46b86da89512a337ff9041f1f50f49ea1ac41a687598532630bb2cfc9c71ff141e09ec2fd66d5aa60628afba6361a375cb2a5758acd8fdec892b1b9657bf627ce9efefa3f55761d6a429ef0b1efacdc06a3027d1aa082e8c3b9fed27c44803dc5abae1b962eaa1e92267fc4a86604e97da56649a8e05e7f53f770ed5d9d58f47ffd32a15907573e4442d49bcd99375267eb38d00deeef8a40223cc991e4cfdf9c456affcd49c4aa87cee39e4738f7cdf77ccdd4c71bc9267562f96489dcf5f86480176ef536c2750400508e3d8c6eed97079ade0ed269a79ef64c2f86d07c92245e4808b344c47976798faa3dfd9cd4f6010d51ac979f63cf36f5c2c7efd3d17883e8d09c00e933e3a60d0e09e34477fe5a13ea9f523c3380f2af1feb3da7be6b0f1a2152999592a2b54670be03f4f2cb26be1bbdfe30ce0b90f11652ba7803fcde7f611f2dcda813edcdc2b4dd5e1fe9f2bea0078bb074bece6e238c46b5464775792680b42913d74e893278a304f54d743e8bb4b2b76d020a6d35d18d40691fbc37d5a292208d87244b93475529dcfa4f32090384e1534935889050bbe93adf17fa04d420219d0aefd7211fc353d88b5c349f100b1f74772ae248e2352339f96007693d4bb09eba6fb972965e24dac95d6ec39e379a338fb1335a55d022358b55da75a6f34c319476c2d1de53e220c3f68c4474ebacaa506b54cc817905285c992309bcd922af8f0259e660e6cf2e3516216ce0deb900c3485d3a309d193ae8b218bc363179a4c9ea971404b0ba2a0bcfddeab4d34b8518c44f8c274bae5bef670d0f8f63921a18c03bf89e66b212623932235f8a15247c24154b1092aa0aa466aaf634ea4533a9a4dbb7083d33a1a2598ca0d7919fcd3ddd5907ebab98c02e0de75ab43139b80b4c4009bd4061647f6ebd4099505a5308f742cd358c6fa40cb55e42b6f9ed50ba45b53d7617abfe1824d99260afad00b62fe2fd080266dc7fb2d5d3035e605177202fcd3959018458d89fd50274552a3fcbf7519f6b496a0ee79b127e965634686014736883914ba039004ed014d30b96f62b4f80d35b77af9e6d7d53e9342293ad6f06b77d674db6bb408dc53bc94d6f600c2aa2de52b93066769cfae7e4dd86a4e7168002eb98d6ac2a5a55abe4a25443915531bbfe2bfaa682552355b96d5faeb9cdff21b81a5124d73bc7f315f0218cfdb181062064d3e4ec2a4012f2dd659c9c6f15cc863da4eb1698c18b61a61a36e47292a4e673f95868d2a21d3f0236b767d725ac4c66186fb896082bccc3ebdaefb8beb157d72f68b8b297348f799a3c8c2b6b9db39e4076d9c4fb819cf3aec84147b32bc312aad2c3cbdfb4e768f03c71bb6ea0bb8ce606f30aa7b436f37b5205f6f7ebba02990c6fe36f9eee50c70bcf463e27345b06bb36251e34f961b29b01552075f497e7165d6693440bd948630c2815b71069337dddba19955bf08bb19c4275c71da9c8ed21eaab3d1bd1e86b45ca30d8efeb83028635bc2f374ec6c68534e5c069cb38f00c0410033ae7c5635c301816a7036902b06c327b48b2adfd54d2124995e030aaaa252c33661150b117f7744f3263896aeaf441c3ce6f8f11c579f5c3003800eb0709e0081e88729994dffd066331e4b97c9ae5ae784c401af11f85b4bdf7de524a29654a322b062f061506b8f678141c9a5e3ee37d6732994623ea52f1ac1cec61b57e8e247141824b43c51829a5af97bfa28d9b96c0a155c5bb24fbbb929c6f0b91e14ab13ed97fe6e09055cd6f287b12584811159d60ad1aca42d1569ab60a40f679f5b89ab462731f39f09aa2e02b63563e037afb78ff781fc9fe55a8d6ae5181bc1f7d5a9cbd9466527a3754f048e499543e7cef08631189e4fdc797e9dac0d1c35dd596cf683370ab55afb64d63f9b47e801c6ccd3930622ad5ead5a4c7c503e44c9ce53fddddddfde33e8f1852c28bd30ed7428b9311181cba022df43513a638b54bd28060830d36d860837de3dd8819041d741074d0c18ed17a314ed59c2a9fe9d7c7eaf19948452b4af69703b9cb99385581f94ca82285ca4b85c91439546992fda98a14d1a66b989faf5c45a9ab5ce52a3a677bf47956ef2b57a9bc84545e2bcff2fd85352ca576c99856791291a6877ce52f49b3f2282fd23321b9a67a37b4aef3de8677147f425bd648224db7b8771faa29d9af94ae5d4ec41887999f237a96cf19b13c2ead744bcbef741fe3459df7ed78bff23bddb3fc5df996d7c1f2e93092837ff43a545ef4315ec7caa703ffe85bbe3f7a30f3d341f42c8f4394d3f22bbfa3f231708c7ee5dbc1dfa2f22b25fc54657b992387f57d20e5edb992ab9c2563dc5d0efacb93f81127fc4793f68ab81a492be91b5891dba562f90cfd99a97295ab6a55896d3c36279d0003cdf9d3b139a76c9229d5316c2f12f448d59cb4afee4e030a51aed9bdfeb9d3b77f1c20ec12c4f842d848e4ee915fa3741cf428707fddb30208701863a42f6253a628c5409b169492dcbdbd2448fd99b6b8f7436c495c6f5ff7cb0753ee9fd4e551a033934422d5cab3a26e7f50aa5d4dbbda9d01064c7f381bfd2534c741d769d40c078ea99b6775a748a5542e263c403e5a49563e5b2ad53454e0ee05c1f175c40c5904310619ba86471bda29120cbfb2e328b4f950a560904a6202f38b1e52339b966a3a8358a33f884a915b86548af3a2055274e0b4a3b680e7c9edeebe30d88d93ee271a484a89b1e72288b7043888a720fbf711220d5845a4f921dab8106d3cba07402830813404dc5bc4d06b2013e0b061edde23fb93dea6f661c3e4508461dbd140024bd06130873dccd6b7216f29661f4098fe59e7d7b0172918e7c6c1380404077ec080068688c0b06b5bc352344d8b5adc9e466ee9ae2b6ddc9783f49938c345f03aa812417f98d479c92fd9df036317d2051e8c3aba79d6a1a37bef75887ef430fd3930fded30fa445f8dbc8182c31a124cc3f40783d37929905a80018056b71bf4edc3b47b9393bc4030d475dd48d48d4428c0318754e814d6c8bff6b59a58d3755dd775df8d442351c7d5785ec76d9acf0fd091ce2f165ce421093eaf98929e17501537b0f686442299482692c94432914c4d60961c9aacb5d69a5e883437c04c2653f69c83e326ca3c014b0f0701340187b7051bc1414f60b01b9e550fcb1a818b317291abe1441e27f26a644d4d0d1874d619d74277f09a7d8da331ea4363523ac7717f1de444decb18bfd633b7755d0febc54468e37102c762ab5b4041508ec47e604f5c1d2f89445ac9349ac09734e7e448349ac04e1c0c7210d6249b9ec061b7aeab8344d067b19e28e3da13970ee090348a02eeffc9dd32c61fdbafb57d303a5818ef5b4f3450b45f8e50f6686a255e572a9976801d7461082c5a62f296d4beee569cb57a6070a2777f2e502bce3640844ee5008f565d7bad09c7b65a2d4deb612589f9b06d6d8ca0f6349c66fba8440752849c7654d511a7ad946b0b0c734ea939e8312a9145fb0430c321680740801a2f4bc11b84527a2dbdb6852608a133bb331db41833dd18b3314629a5f4da6b5b70029d3a3c2bbbe40704417009fe21ca3808fa100cd32fa819b2e7a69c70b0b1001b82a24dcb6439ee6d7191071f4841e24212e301c2fce15a6608963924a5d0341b20c5d9263304d3214370cb0cc13537d93a924824e284883316cc70648cffbd18af208214f0b050a7ad0614cdb484d521020e63cc9bc1021cc69857039c68234494f10dfc9019c145cc4227080e4df2fa0c73319be9509ed91e0954f7de78a397db5ddb3c66c30a1c7e88811c15b080ec82e1324aa9d52b0428d6a48a24539a44616d5bcf74bab76ddbb66d3b821409c40edf2b0509f66e6c52aa2022917a96450e3b7563f349a303035d43fbdceb1d94ce6c06eeefb5b84382527a2dbd5608b34d9bdd999d6ddab5d75edb02eddaaa5d5b6db5754a4aed16d6ce74a6a4d65a6bdd6a9dd564d74af406ba63a8439a04451343486d449b9ee098bb90546323a0d32a21b821b2ffe66510647f8e7e397a70b0861485ec94d6f6e03535b6dedbc3b2ee3d018323499829992df951222382508db576ed28fa38c22a2b2c2da2afc5646201c1957f151209637c6f1439e91314a2789d424c8704e1244272d3c9faf3831ead8b2a2c7824ba9134f1848275b9bfae8bb1f362f47bebe671310526e4189282bf8611b00b4cc02e5081ef8b9480ef5baf06cf75094c818851667e1017b0b9d6bf80cdf3b5978105ce80acef7d5f0e1fccfcb62fc7c18dcb61b338d4cdf5b3396c96a8e6e0e4207b256305d63e6cd6f65a69cb5be902bd654d86105cf34d926740a4e919c418ff1933565821460caf07be5fa8b98262f303386cd79c0ece6863a7124a3ec083ecdf4a228de66ad76c97d7002afafa4d1ca87703fd60401a204b4640bb60fa4b22932907bf5152bfb06132ccae87ca45838df86a57a53cab1ea1ce4944d4994e8d398c33edab76b94dbbdca6dd5a6baddad56ea53ad6ae7aaadb2438c16145c9862c0124afd5349f578c91467a2975a104783a28e3e28cb0c65ced7ac95a0a13a124411d05d64ea6cba552a97e3cd9a9240d0e1587a3d61abc8c23a6458fccc5c47466826404ae7949010edbe592342e6f060b707b37bc1ae0b05533238932fe341d83b6a28583259d842bca9882e017cec1f94148774b97cb45e90605888a4ad6b21e5af9b4948ee229ef99798f8a423941499a9b9be8d3fdb07c6efa8b680333bde845441f22f613b2bfa905d886985330b900d79f72136d7cdbb6267003e8bfb4709764645aeaee6da151865a3605ae5fd8ae8cc37c151c2491b620cd66783b68df0ead92311ebd1b20aa652d6b998fd3b80fef42d62859a3648d6a59a35ad62859a35a26539179ea895050cb82a4ac51b23ac567a84c26b376a5aa421663d4a286425d7c3baec348e0fbf23dafc92e1f3f02c2a83d5aa59a16a4ddcd49adb3e9cbc4c2e11d1aba52eecc67e45b296bb52a70860b8220cbe72ed134dc4e648060db40702703046b39c440300622030453192078ca00c12e0304cb0e63af1fabe00ec7190cf18523b00b45e0fba2170ee3ac57ae7ac4d2936481f2b3ec965d188e05cdcde5074f10d1050a750ac11444b299f6611cfaa1f36edc194ac09c77e305223822208c9b1b84b2b0424785a383d383e1e8dc6bcf893a1920b8e6106b0f53011f483940e56d0b796cdf7d1b5e3d0670dff6f7a77783f6db17daacbdf470b8dfcc04d36e8b35fac08fabba510013188e1081ab4b04034c85603958a96d5f03d7a520055fd22b684684fd07791f14148bc160af5705aa401186baa96813ed1013a7c14d9ca64ff1898d526596c781ac4ad2c45392233f3ed9f6c07275d519cafb3a4a059a5f585f7504d8052af0cca1f6a829077d5455e5a92b1f075b0ea2fae505ed011c5221cab3528d820819c275b9f0053ae24a12052ef25083d70b62304821e1c2ee7598c77c267bb8131f72f7208792fd6f6cef1d068339cc610e3bd15802cb68733fec1229d31cba4c460afc0e138a3631dfa7e91e1ec569763cc66310820346b0d6bbf0295e90a4e1429f7dd15fe36c2bc5ce7b0d3c9d3c2669bc177259a93deebd6f8716e18828a71dd2c9c9487d1e27ae3bef064ee77d366e704a385e39dab0026ce231073d96c2121c7abc42688819271d43e71d07dd648497c36036dc6b408492e7bb77438818e34fcad1d381dd273e63613098a6f95821511615440089547d80e7d740226512c9da554f125c05b13bc618e34f4e46598a3f6b2c0104414a691114cacde39fee99b058d45a25bd21de4a5ff8811373b0fda6f667dc3808a3f3e69ea0043c28c1870634088a45931780ec830c0888b10825be3c9349e4d2f200001de4bae770e6646812ea183dcff33cdb719c271279224fe495228053b2d152fa90e4397c97926b1d26c00d1134c9b870cda21b654cde47ef6588bef0738ca7a228a3c9f8e8674ac918d1674add307284f550cc97482f58854a9a96efef5836fd8f2909f8c7c736bfe87d22d19c9d272a5d4086217a1bbec2f23ed8a021079c9c128e120422e0a083dcfbb79474827821cac2e0c214b3c8563c6c05256aaa4ed1cb1a821b2c19caa94f60b01bad879544f30d0a43b44bd06a0105f94c3cf2e3307fe22e2af2be2ba5eec9fb767ac96947cfe0347a5169a7837e0005d869470f81450874701acd562bdac000146da4057f060b87de72a0d38b46eeba0f01d01fb6803f4efa5ed725072ab94bc6f8cfcea7f01a647f571261f0ef1215d9df6f106bc418e3edac3f91524e9cecda579b776354dae9d3e839973430ddde779f8eeebdc79f0e2339dd7b3abcffa0f4cfc8456d9672b8c0a1ab248df7ddcfcf7954de771ff610ab87f7dd0deaad2fbc5bcb6136a4262573b2f34acdc4633ca69b0651a6c6e8ba0a61c066cc67fa836e141d83a37e601802f7d3067270c614dcdf2fca030833bffb78e2e1650c8e88964699ba8c7157cf869ac5801ce6375bad1cc7b96b611a5a51c68bc418534c4ca9f4f2a282bdc504f66a796b8626f199d96ab56adddc0509edb56227eeb1bb3f76b479f1a649814472130a70d028b70a382f4248d6ae7aee084ef0d30314638c96dbc028e39cc45e0e41b063d034ed8216a40105c639043b6e0329d844d3f470879096740ca0e81612c9c07beae170c3bdbdea540d411a50e051ae5ad594acbac7b768b590702a0a6ca7b4b6c129a990adf7b270939521989ccd1ed2891ccafeb28994c92932688bdd9d4aa552a9d44f2a95ea940edcaa87498f4cff0a1d3c0972dad9212385c8a9fb1dd2074a9c3c98142b62b4a1d94bb06853e2220b98ac53cd72181c06aebbeb7354cba189e3b86de3b66dd356600e46e9803e5dca6d2ab904026ce4be8bde7bc992e9d712bd6f83d5fe76af46dd080683c136587bf4a352052660ee0bdba753d166fb311269e811609d82750ad629584ac26c1421bc75ed03a6ffc2ee07d3a14f396c04236f24ee46d204a5242cdfa0fbc4673409933009e338d68bc9ca90e4b1b98c1ecd9aa939532a17131e201fad242b8b73a37bb469cbe331eec3c16863550f8831d81fb1460cb68858c36d6c08a28c7f68b91cc65813075f4c665370a0c030f7086b5529985e604282a7b525203a57cb93ad8f9bad0592fded8f58c30370050e3b35e59c499a4ecd24ca6762af82606abf4b2c39e3441bd7aa774375e9263c4167bc839e5ad1982058eb124cbfb0066daffd0e9b66aa4035a654a5e99f2e225559a9ca4a5556aab2529595aaac542a657d58954d15b1299a5aa4d27b6308118931ca285128140a85aaa8966779966f87e55bbeaaaaab1437dab6e7a44ba7a9acacc450c11cc7719aa6715a0d365c4ab96d52cadb45cf86edebc75282516929c168a352e799455224ba176312298785e5b5524e4bcb732e31845c5ec6c728c19062c888f1315c3a203ec6afaca8689ca6a2c550c11cc7715a8c185ebd7f6d48fad8ebb652c86d5d4b299e58fcb2b0b0b0b0b05816161616db29541e2a0e0020b58260ad344c297c8161eac75b4b24156a6654f73a89cfa0b94010d4583ed5075d17a39472f42a4f430c098843c83dbf26dad09aa19a4e89adb7ae9ab4f8d32504a152e88c0e757428a42d158c9fc6682c4692c1ab71022679dfaa063f2e511e96cf650283126564515edc0c5e1c277aefc3aaed3898e25e3433578ad9d3e13e93cf399170534c1054dd1fbb86fc2e9140d527e0804424e28ce59e2392fdca6b356e9be9749d83b01713e674423239a594e46181c3f4b2c59d5f88e79cd286b1027aefa41f4be1cd34663c09bc55e6f95f8c4f6be5590dd518638cb55d860c982170f859fecf70bfd6d4d4b07cba5891524a29a59452ca8e31ca643227a61c584692bd0c63197369a400733a58f8586ba544a204c684f7981479e06348c86967870a5f18e124fa1d9dc5134ea30f71f66206a79004452a892557b429612b5880612f94c3e02ff2915dbaa28ce148f4bbeeb1a4197d47ca368726ecb59452761804e5b65a02010ea54bd218a11286034bdebebeadd3d2b741d2ed6d69fbd671d0419a439309cfdc5ff8ee6ddf4854ca116dea37dd22c6f81b31e2021724f124398c32e992ae1f078160303a8c5ef439a22f27ca18ed83a91f5eaf7e3841d45c1bc044ea4fdee783c77c3988e0f0660ac418ff1cd275a9cbe5b2b6674588f31838c04d030a2ca1c00e05b6392469a4196966fa5e46dc0e50f175e5ffdab7eceedb311265ccb7d37d221b5c8103d5fcf0d2800253ef26fe56ba9ffd6c485b92f5068dde0d5b9e6fc303f7f6edaf3398050e6dca89546aa65ce5a0bda92a84043b2b6a96d692ab9ec0617b81c38e75a59da9c409ac9872dad152b84ed16190ffb2021cda94bc76fe8b1338b43345a23685c304873d6453d18600d1664a1a5ca4635648cc26615f36e62a0e8542a15028144ad2dc3b438160afb489642a99b0e972e190a24430fde588bea3288ac225d28c2870e82c1084e9c7cfe3d495ba177d4ef7a2ef17a93cfe7644aff23b31462598f64a30fd2dcfe3c4cd145679eff23b8c1ebbb0bc37572f1f4f2ee349bf83cbbf7c3a44eff2a4cfe575c478191f4f261850d88bb161c23c4e3c529c0f9f894a3c9002ab9234555687648c75e16779d77c491a19afb13cf67e144386cb37bd29f0fc4d54c2997103ccf2f8abe58931ab7210002cdcfa71d075f0bee573bc6f61f1be7e39a3b721bdae144fe164e11c010755463d5054385134d79f434e57794ad9aa0ded405312b41143b82e87bd4a22d2d81f76e5c53cb3a4434419fffad1e99a61030c633f0e0d8536d55af8402691431e5982114b4dd54ccd5414d3355d93073307d306b55f887481ef8755166de644f9ccf53a7727b23fea0898aaa95a53d68725c090e5a128eb635b16c826b137e533b407f5315d9c3321faaee4759fba27d1f7d8543c797655efe72a077db0a9a020fe07959e85f855d0342a9f7596b3f04731fa5e20fab0b320bb00724851dd57801c52148e4dc9989e3d78bebc63d3758516b11dc4991e744c08143543a166a83b9bd15981240d2de2fe480295a2a8554f2a956225710112f1d8dc003d34d513673da82c8793fb736b0e2fa5d40996ffff4f3b0715387c49678e5a8d18cd7be7fce8de5ee700094582608f535e4a59140a8d947647fbd6ebb84dbbd6ebb80d04afe908a694526a499f23edcd64d2e847ba74d6cab35251d75c40298d34ded46e19340ef1848f5fc42e602aa5979f4974de5856ad3880e812105e0ee266ebb7f4cac0cdddf5ef6b4166aba5c9081c73fd82e81ca30c13a93f9966b831e260f9485164c85aab594db3f1b5c8452e7231c678638c97e39ee32217b9c8c518e3b5deb9f65dd234ab69d63ea769d6daf0db5ab7a6b5d67db5bff2ca2bafbcf7729f6d2002ac70488201b17c5a3f40a91c49ad1f1289f4d3020628a57c69f98cbf04bd407939f2f2f3027b79f2e27a6192fde57469b9b45c5a2ead9683fee3a0d7dad2fa5a801cbc02c3b8fce41597560b4a5a60f27d91fdbfa192121694b08024ce5c165e5c92ec2b74116dfa5f90bc148934541563fc1b89172091a6a5155b5adcd7052ebd70d8d262a1489c19f5b0d08a36dc6809167ef288c80a41d146fb91128a449bed5f5ad1c6a240db8e90bdc5489c21755b5a3e431590024fdd872d4f64af39751f722d3f5186ee7d4e2fe69c73ce39e79c734ec1f3399756e8d2a2cfd9aef3eac5f6726921f1e2f299d0a5f57284fef80c5deb05c8a545bb518fcfb4b0464bc49916222dad119196d948092e2d15215be97e0bab85c86ccd0f5b6653cba14b2bfb5d218b96960a45662ab45c5a2f2ded5b2148f5d2a24f55d1469b6f4bb724512b28c81eb938828ae3d97e442540740ea94a7bbdb4b23f0b2e672189cfd097d64beba505c65a888c846a6d82d29a6cca72ae2005cb196b2ab576d53312ea828b3c2899726475e4075584bafa49f1d499cecfbd37e6164449fc28c204846bf65ef43a4810a902074d3889b8799dde6bbd0bc4f641dccc44f7d867362a4a8267ae76bd1a649a803d0371810e23d715b2fd98483d2d31d17d0e91d77d3b1ed39f0e0d3ef3836d2082c0dcb5dc0d2f4d887024f7dbd96fe7a5e146ace17f4b364419b7b1f3f2056e948e83de11f455e94cc7ae7a443a3031791a18fff4bf97698c9146dac4490d3243709dd1bf6b6a35011763d46452b4201c9aa6699aed1b2da84f2b2fdaaa86bb07470f39248d8b8ca0580b4ad69552ccd12a0747a4767db8d539e0dc3818a4dd6df369a9386784524a614ba802c86967870a3c5c9cbcdfe1413c3819e9c774721c95b1ce9bfd0b826a7fb7edbb12a6947a05faa536df86aad99ff7524a67ad3c2b152542dbd0944da88738e7947405388c423dc8198890b27dc41864e81aa85e55179d6d11c8d1c34b68b3abd5a609f5c071429e1426b8f75e6de33aaed336aeeb388ff3bece9837d1ebe0bef35e94237a8f13792621745cf7c94be7bddaac019d8aa8a48dd8f5346404000000000153150000180c060482c1804834269aad637b14000c6382487258381a49844910c43808a218638c32001003000106102022212b00f8db66f517e275de0fa85bb501ababf2bf914dc96a985aa021a7f1f1773ae5f5b44ee3cc4fbbca2bb3ac5218c6c5a2e5b7c0f3387a0e61046969588408ec2ccf244a3d08ef5604a6607153fec40e09fc441c8cbe66d44c4aa8169a134a6608f72a4da0834cd6ecead735a951038200eff1bae794b652e54efc0093619a8e5f9cf9599702d5e5a884dff8ab7a726d709e8e062c6ecd2964fbcab98b061927900ad8b4b73df82719247e41a19b87bbad6b7e1d6ea8ce61a140766811aba6c1df25f4eae688a6a78d7490a260b4f233812512a2ba939d3798b9fcc9b98f6a731d33558953505dda82266d237d12e33e5ef06ac1c1d07ae2d34971d3d77c9c62dbff74c5a1162596cf52334432c4f0080557a1a51aa233433e8b07194e584589568203c027aa2183b767dfd8ab41271a24aa06734e34bc17245938d18112b15ea7fce7563c0009a058b674c28cde843511a25f5759008292fa5024180a7e7e626bce7f640cdbe0611ddb4fed141e3b71250adb2689a2f2f8e498c00729fa019df1bcd8b5ad50245d46fe38f63160a2ff3efbb1cf82cebf15d11a7d1716df020394a3f07561ddc3f36ee6997b661a1e7079adfc0b8aef01c77d70df4fdd1651040f1e4c80db5e6fa323d56b494d45031c99a7d45b80ec310e970606df99c42ade7b4c9b7601b8ca0614f6a743795ada26b22ec2b1700bc7340698b7554e4262900a7565bfe8ee9a2cbec9910d2487494a9f9d16075666f2687d193ef5853a484ebd22a30cd2325ad7145e095e30853b0499f5d1c54760d6e4b16d1f3583f2b52e03d361bfc1539128719fb379b715c5747ea35213d986d224e6cc96655bc0d18a3b8975635d9cf34227eec70c553a4cf870ac9763a897e95446e5b33f9751b5f63240c6d8943cd3671ac11f654ce490f2fbb16370fed3bdaf2cd44036ccaae75a4017389ff4550439273dac59ff4bff3ed1cbbc87b6a82d09b3a4fd53d1f7f1ef2424eba3d06112409ae68cb03c43bcd961af3aebd2b87c74760e2563e6375e87a0267bd9b22ae2a1012b4cb07c1243411989f2ccc2563dc9a871489a6812e30e78310b50414211c51f9f59c178e9894ba3551d18b2481dbfd40cea30dbb374ff1b120c50c15c511c6528db4f3c499bd0a6561129abc0d6712b7c52d1c406efdd223c8c5111014016a64bfa858e8ed3a93e2d1fc48b62c5c1b19a16fa329e622e2143d6f1b3907f7f280662dd9d53de8268a229c436dcc748962c7556e71461768f945594a5358d14787d2e766a0f83f5b3015d28802e60c9017e1a58c9e741601949692c45fba41811b1f4058d202ab7f33907231c7d434c94f1728793854b5ba348eb49142f0494191992789304dcf6cfeb2ba8cc334cb43a6eba47e67121155c9817fbade0cd21896ec920a137b2194874024e824fa122416bbdb53163b0f4749c42c49d52a9c99d9c04264f4e49d6da8848f8ff14985ab02099131253cefa908355c26ff022c06d2e503cd79101acac70815c3145c3c96de0c4695c0598e2f0cc611b8e7e7a7b7da8d7c77abd289cda7250cf4e65eb41b9a730a94a4f94e361962f3a7b54bbe5622ef1adbee4de2b818056c6ae3282ef48d921bb0403192adcf0bb657febca626129ab5c16440753129f45176659e0c6e626a9edcf5706c4e38493c4109504fb1edc2963979952fe746dcdfa9b74af9c598d572f5ed070fd94934650402ba6b06901a8d60a4284294a8f00a1d687153a396bdfbe1a4ef51e864a232dcc60c7504aa8017bedc8dd48e01f88db8f5a55454702626aab3deaf14d8283f5075c01ff0ca84844a873ce9588010e8f46470ceca4051465b560444a68984a84b94ec4f42bed39ae68127cf74f3bdcff22282dbf25cc18a1f4d0596cb0116f8206905916ca8159719b5a7ab4a170eeee387cba37af6686c91845a35bcd72c2fb6ef80b47de2821bdce5a33b174fd5afc75c611093bcf6f1a838869d06af7129ab28646cd3ff95590e12bf0809bd35cbc6d284988046848eb902c18f2ea6be567167f3c372e05013a6b184fb9763d4d2d8ebe825dd65f2d52847179618a14a9288b8b0308eb49ed996ccc1835bd9e9ae588282fc369ac23733587c232ad1c5280a822df94719d5c139c1b4a2dc4fb4dfd93e2e1fbd11366cd8892108eba2c79c057e454823df1d4ee60c16230f20f7082016e778f05c6e33580a66265277e0bcee17696881c748383b1b7126483c373b4da5ffef28f853c662f8e6aed2313efe334777a1f2125690c38d1b289b5e4035a995c92f228ba9fa994f78e8d4351ab6a6cba9062b952992b5d09c3996c661cedba4baaa4bb977bf7569720aca4d3474346bd335adecd993cfc389e5a85b776076164fc0f5948d3720f97be0ae81c24f163e51121fd3ed9f75c16453ac2391f104a30b536085d1e852d12f465811a832d32877e9d1a41047b7dfad91e412f85f1503b0c161bbc63d804406315292e5eb1a136adcdd6fd146262e4b8d9bd337e70803e1cb16cfefcb5682420ab940b0e97131ed8d0289ac5e8fe34fe238902deda4011a8c995ea54c328f21040d7d5d2484d5e3aede24d336ea0a44f09546f94d693e3df91662cf03f6d083b2a36401b75571cd70d548230c274aab6f16adfb0ddacd29f24ef8aff70cb087fe03c29a4609b21a8db022aed55201b72a562f0c8702d61d597dddda25d38c2bc920534f5099c35a854fcc3e70882ccb80026cde794b78daef72bb3ccdec1806c2e86e010227cd67388f142d8ec6bc1aa6506956f6b8beb9df63c8d8b6baeacb9f20885da7f811247d11923ab1304f5b404d8b9591693aea9e3ca7d555bc62fdd8e7098f19b6fde2883f6b1610ca351a4a33e31c4f1ff0ac28c4a5088a6f8d90ce1385cbd44666d946426fd9c36486437b1a3f2c65ee69edb8aa9232ca0008cfd3e4c3cd5b1e7a5a4fa85cb029e17da3e89af16b5ac52184d6f834f52f79f4e6ce4e820d6f57465a5d4e7e69708c1f0f4cf3d36274827c1a4ec316838305644594440b8c48a3ec820a645ac8483feb9c3b8c8ffd5d8a9724a46a1b5518f846f9880565b7e06bbd2ca84e24f3d8719e2db14e4a9a8dc222ed0c342b75f3a1ad0ea58bd081d29d7f3860bcc846e85ac13dbd22c04bd7a978327461c8ee19fb3ac58f51ea88e05c25444f3e8dc9a083dde4e33265bf0a82f967f596cf410f3a7d5f828bc03222d5305f1f4c5254ba83036f5410b175f4dd4dead643daf4838311601fc42b6138fe7d7c63966faeb9865504823b4068e4311d34c9bd1941424052b50192888fe761019933d85b9c0cb0d76e4f1978c204a750ef40aea9c06de2441dc9f8baea3e7b353b3853f003973571ebacac136e2b6f0072c94f220266faccd1fcb942638a7e2a04174c77352e12b90b762d89fd164c6210c27dc18b6c5fa23beec364a74f876207088e0e9d33ab45da61faf8ef1a2c7424ebbe84a889b74f79a741020254511db2f494e343e758d6501025c7a473cbd794320cc4b12a30051cc43db3cc136f0c1d81fe8745260078447a2dad8ba6454234230ea3bd83991a1cf82437321955b4acd4d964fd7235a51bae5e9115db53916d3ac3c6c49678e6c30a11b762e6d114306adbd692b6fcdb2d79b14547f50fc813e056fac38170dffa1696705e0d980c7d9cef8ce6e28892672eab810eadbe2678da5bcb7e4c95b032315750c230ac56f8b358940228e5f47db34b9e25b458b10e8d56b15c688b433d5d1e861fc0237a18e766a46531ac8cecc1f9ad502832ed4d1c04a44262b04c940c91a3ec20bdc2a91cab45f051d8341bac453521b663840e8b04914dff0729ebcc0148369524b735bcc1824f800b7cfa20974f82b45d8bc1f4807fc70c1e0cd0904394fc838c600e173573cbbf165ba56b2e4079168cf86a1a16c4f9bc9b8706ffbdcd6c7794bb93264b179cc8bfb7a1e03a4be79cb8602755bf63a8b1809dacb43190f48dafdb3815733d368e9242958a40b05ed424c9f56b8490300c960daea976092bddfc14a0ba0b76b429f9fd5aaa70835b10ee2b67064f8a1ecfa70f296e27252bfba5cf7ce7c56fdd27d179edc1ea9133d7238ef3658b679cfe548dcefb9c60c0d1164d01e9c3fe8540b7a0d4a4c746ab70a7662caac1b155ac9be195bd7d4befbe377d2aa4aa16dc69006472dd54e2e0518e5fa13e7d0d22281db0722b1a7ff7825f442d7eacd4023acc8d2a3c41c6406e05d96d440dac2f37e71b8604d336a93b78da71cdbfe332333ad6cf787201a992d1652c6829d73e7dc2ed8b64829343d010437e9a8d77b09d964006074d05091b9d00c9ec88d1e76655261923c360a38352b1e2201b01c051aa396601846c2dedf75b81087c5f931629cdb30827df3cb37dd5a82a61702dbf6a369f60877f4a0412a4a65d9fed683aec87d648065df1edd3a1637f4f3a77b6cf55702cab2095d43d5aba9811f85a87b2c2861ef57cc1296914c4b8857fc272e053d170f2a7a18b7692cec7bc30a73eba8d4885253d651ad7035c05db9cbe591b58285b5245546909c6f44ffd1e9c5a0edf55a512c7b9e6d9ae878263046d10e4006c9b7f052cde72aec7a7cfce951bb246e94113a99730238ab39771ce0b10f1cd90c5e03ee8eb1873b7491a78893231e531c5097eb0d28657d36c4c0e306d9755ca910d914143af090145dc18b36adc58e665619195a2bf1e0080e17148871fa197edd54caebb7a890dc38508d256e846b4850a3af575e3637b4123f75ad1cd73cabb4c316e08bc54d3f44b6c0269b3da7f2c803a03ea495b8b152bee0f64261e42bec02884d691cb3ab71fcce489736e0a3a6a5fea1d81bed35a07b01a3713320abbd6da22fc14e05d8085e08f67c0b200db8ca1a879f06b2af3771c1f3182e745d687ef01538c00ff67cc95f097a6f6def45120cdf1d8e0c2623b54220a3037974236ce1a2801862ea3560e4efd8b9e7ad6b80208d7690721a7577fb0e7c8c15d17e576d3c483662b884039d2b33f69e69be2c09da1b63ef4a514bb61ecbaa3b855fdd40ad9bdd4782c048b84163ba6487df6213225fd281443dfc90b5814966a1e0ef37a859a36f57bac006396b698d39283f1acd46bc1c0e2076fd7e17d49acee585bf66e6a59485bb13935d52b66590c8d90aa91014ea0b3b10bdb74996833b8d3c6daea5e8850f96c088b1794aea42256669457b9de71e7ba93eb48e56fcf14068cd71d2e99f9134826f0e704bb72ab9f7f7925cb6f902cad25e1c7dcb01b0329f0b5ca3d3d37cdb37654e39256a0d4bee0691e60762e00f638a86c2c71f0df949b0aa085eefd5f63c6b43a412c972a0bc81aa6ce2153a1047f6e7820dca1ac2215048c79f55d990eb6111a970c800a6d3ee2e5abf3ad38f79dede029154047ecdb3a9a6248b04b54919785dd0f817154e682e7ac2569eeac23258aec681946b90aa5abd92d25bdf0bce9f4371e1d03e53aec2b7dc8f9507e42a6ee7939e9bacb654b972ddf3831d7d9bd7d10dae1a098721701e90c66fc1368015683658706f241663390499bfa39d00e481a1ed9787601b464049f1c184000fa9ca38d26c47f92ec9110b1fa22ee648156782ee236cc961d239f2c85277310c91734a2315356fe8600a95b68e44ffc926b3f5d2fb75938077bbdce4dd15bc8afa00a7331f61f50fd7b3290290b826d97a02ea8fa06e8008cd8c9e6c7d354b0b0895809be6f9f5bd622687c633001eb5e25eaf6c7c56d9b5147522fa99a92fd04d636b334fa24f799108a04fc95ecc9c82919799741fd7811427484d2aeb295a4aa9aa13caeb632a379153e01c065368abe87db73e007a4988862402cf5866261130505244a00374f1e445ba7edea980c686c2acc2127f03393da3893e2005de8bfce2a196421ee43b88a2c43b515e420b97de14718277724229921003669eb297f92dd619f94b741eb057fb4afc325fccfcbb13631fa30ad10778695dedc548bec6e4ee441235d1b393037ac70925355ad5d8deb24aa79d938b10e4f058f336ff55806a72d2b1f0324b01003953382d053deeb3429ed24077721cd0602f782e108ed89d0aa23d28e606c3a0cae7bf2a71f0b40afb0183fe57a0446e968d47c822dc051ba24c046ae5fb5fe1e151f61700f502fa15f948a55d7e860e0e907046d70db062cbed9a12114d6d7007150cff9ac0f624c740bcd7d94789df5281ba9a5e215e83d08a790bb7e5bfba9109c387ebe0b1d1ed9e3f018d533064b0dae350dcb6c63fe13d770cf43a5d87bfdb1d3742d2236a7122f3708edd578b338885b00e7fdca039b046782bd5d790aed9d3a6d70476a3a91eaacdf7488e630081728b257ad89f5643d0e505daf59750e8d520524c9625851f3ddeb65205914754db9239427a4298de2092a432322090b2fc0d140c2b8e6e3fb5655450e9cdef6b140d6eacac1d16160d32897489f2c1bb1f24fb8082c88105a523e0b0532279aff4b1cd16b3e12c877a7e612a55019ebda6d0936794da5c1fa044c5e4bd908d64c2153f36a16ecd99debd4c419c5b979db19fe751fd17a2f3c25496c5e722da074144a7f7ae782a3d2811c83835ac5f1f3f88cba80f84389a93e26f40796e2cd7e597f8c78fba710c5c57110b103e074fd430a075e3268482e84c04f3d608cfe5ef70b73a5855eb085a89f8466969ac21a67a24f933d9ca83a7eeb60ec52b95e33ef875efac605cbd21fdcd95c43068acdb12846aad52e3fd641d89e0cd65cdb5858be74860c9c13a1c99ec1223ee2ec685f2b52f43388936508897422c08653f9dbe41d4e8a03068902a132cf0ba7bc33b4813e10d9c38e54e3f08d79386bf66704363c8604b23c82a9367630602916fc9bd448a76d2a9af1769ebf50d0deae613a3c2ed98221da945d2e9864b43efa54b5751780d678896bd04893ecc59112686791dcc8f444e58333e84f1821905880b3dc3cda28209846b90e959c09932017595711848c8751ac16a607c4e679b30c10d1559cfa4242d0ba7085c21a1b49e8056fc2a815cccdd39f20a8ad8d97a091fa0c39f35243bf2be3e46ee0326e208677048af5c39b68bb344533208bbd0768c3060cb9568825cbb11f1b510525eb80c1d2d5ed997dbfa2d236efe51b1de7a40f5396f8f1e65b7024f161efbbad25ef3a39e613c863ec321db0c634954851c135626d033ff997fe5a50b1d3be8de36669d6232865f3bfbe32e3d49eb8bd0643cab4d543787c55a63a84851550e8fc130ff41f77b6866c2e8a13e8d0701829c2e143845659c8ade5c1328b75058c6420d4c15c6e6d9d2e84441a88d04d6ca48c77d332feabb87dac70be96340887a904caef261dc10af17a20bfa21f540a4b3b23a26c93b9711ccf02f2760886d4d0483235161b563ee0c505dbd49ab55bf3340016f4df921153f39bb9bdfe8ebf5a300148fa008e8a4b4fcaf85b2786e736d1cc6a8c7330fa6a6c7bf30fe4546791d46cd53a16cd93abcc74bd99c4dd9c62d1ff9306f693e932fecf7dd1f883c2ae876535a2b359f7945c8afd8cadb09d5d1718539479b89920e0fd2831bbb0ea8053fdc44887cbcdcbf75aec1b6bef5594fb8a28f8e8a745812ac56feb13572fee5511fc8708d3ae431e19aea9ae30902abc60355bfb143f906eadfdacd4adea2faf69e45929ba538274b74d8de9ea240a499db28a309a575a9ad7c0f300c661d096a1ecc13af40bc6bc946881368c161b0cf981a007a30ba42a7c284c8ebe6f0dac298315a0f7fdf32c19651c2f51081e841e79b8b73a0f5a9e70aa235302a1fe15f9bf244b0534cecdc8f32849e2ec49d4e736118860b849de499004e6f3763ad6e1320047cc356770cc0dbee805442bc8fa842391521221db5453a63f681a04dae1787060a88b2a2ddb83d60c9cc121541e483df0ba8fe7ea4d7e32ab7da20313a77c4537a64f3d055cb1df6c22d602eb7b95c61e5c1a3f238873e63206715be3b336e29a05aff00aa16cab88087fc754a44c68ffaedb9e55ba605ad0eb0d7d217eee1b164e68cc7745415430463389bf8ded6818b0825c89c206295d281025c7fd689fc066c5ae927c3093a3f659bd00fafbf9acbec3e4cc2fc9af6896e74052d354f924b3c395ecc8aea596c569c54ef37b500e4cad4448042ae521427f9021a8724e719adc1f100e917ae0af25c6f5b2bbbd762523b47acefd00afbf6b59d4af65baed563e3040d48fd804b5d20c27f0fa2b482de4972b4511d9f6565720a8890e2ff2d3b162b253c0203d84fd2896eacc4a8228ac68df89e472b517410cf3320aa3c2eb82a43fc1c0658a2fd5970730d47dce2d8f1f703041e52e88f4b4863b719932bc9de5385920c60e519cfde5c0ae2a83f9799cbac4aaf0e3ed277519605647187aab9ea0562a93a6b52c4e5b1c70d9d546a3880d05fdf18125c660e81ac524afe3a7b900360cc0d88a5a0278704f2bf67f0242843819e089b903a72f74f4816b357525ec4352c7d876620141d3c51df926b7234eecd6cc1d4d94e9aa82382d1c2bff801d216481e375f803ecb44129b17711feae76491227f02f14495d70e91f42051c79a804c83e14ed3c8a3cdcd055db24e304d8525301c6c310b6a0d063775653e9ff6db306b0f22ac50f72ed81b32e4db0aaf933a6bb6d06b92f76c59d249d783086b3d5346680d9f240d44f6b6f5b57be63b7b118ec62c3ecc6cc2ce311c41493753928b115505fbc51ad958f2fa5406301f0103c834b821ae0b7e439ffe2ae2285c3eda8e25adea25c337a3ce86428189df40f08d1fb0f3a4edd15cbdf51aca859030d368f836e123d496261667c31587981c93f6230c8148e3088b13232d8d9311c41b2c0451c6a905e1ac3e2bd3bac0c1e3da31962a0ea82356a8070c450830d2c847f7a0c3572ce34dde872cdf0f3163f35e4193818b5b174de618ec26bb621c9b2f2b95bf873cee97840ad30e7cc23207d59a866c8aabfed617ea753cc448ab9b598c2a592671bfe16859c3170b8c45c9e78a7796cb0e0f600404b8d160befa94a2e27aa082559a5e46a53a9407da5870070db0a37557e9e9a7c17dfc21789b6e00f1ded2c0198012add2ca69dab5ce91cd1beb4eb6c2221c650578a2dc205ef6f78133bbfb88f6d8a811dbba1e329dc06696a8437375ed8ee242b402e5e3f30167463f80e54a5dc1be3641af95b055975bdc843c8137a75ac42098caf236e0d72433d9009b6794c48cddd7c7b8685a444043acb2a6c2e3e76510ee9f92235de43e7523e80a2ecd2819d77d0d75cf5a9b2c0a484a9c361b72c5bd6730d53f0b3b1462f75b90f59009855bb52d13d75923330c6b6290c891b5f7a758c2afa6dd93a74b28a0b99c4e24a990e4e47ae6a532cb173af7312b063a915248ad09ea9bbf84f33a16e929323c11b91bec202e36d386f82d2b87d6da1dd42ba9ccfc775004d7f12efcb2ec9894be3bd51ea3a693883e94ec844e8b9e1b98540f37d489e49ae22589f33eb856f7776e700f6fe85377ffa7cafe15932a6758d9827b10b98912c397c9b89ca79d192dfeac8b2da954aa51084b37700f9c7a782df7abf2d82953b3c059e1194df19c9193f7f6d25652e8cf2cf70079129e00a73acd2d5ff41eb2badeccf31e5ce794e1a3c0cff568311077f153451f4832345b24e2b527789337619fd40f0c6a750e2bd5b9c715d73d6666b37fe06309560fdb37279a1346903ea1cfd6f804146c10870a5c386a7af93490e9acd5cf17d322004c240fd5e4c65256a833275289946f1d28144a3cfb183e69760cadc8c45cb100449a1ddd92633af45e4c9321f38bfa41931c25657681860e0b463e864bba7b675b9ffb852cfda7c4d627f7f9d0de29b805c7ca891c64c8fd21c568a78c48179ea0d6fdfe139e250674ad49ece5709993857a78c86db2ef615e6aab5423730e0d1135d8809c3cde7b84a3e648fe68f2913d8860182363a38cedcc382a572159a64d3d8af4271a9907ff2bd0ded025f9a1f3dc90d826b1493a519037eca4a5c838079fe1fc3dee6e66b72649a763ddb0d8a676d7c13995b80ba97e26eaddb07968b9520038eca2e0b07e596083ab2de21657f2c1fc6374c709dabdf67eac2377424076871ebd46f058dfbbd0fb317ba46cf50ff1170b4e7e14a545549db8029b99a29d4a3e51e045bb5f7179d739c37628e664713e5bc8ff7eac18880185efc40c600ccc15462e2e5a22d405e9c2fd7e93c847671f9eb71f1340942267ac2dba3bffec83c58fc19ffdb9f4d7e1f0133c1aa39ad26dd27ee081f758145e4209e6a3bcea2c1844e28598cef15fa6f37e16960f601d1fd3256c013116bfa061e7b43447c99aa0442d96416b7755d249a4dc71540f1c3b85c2982b6445c567e579175dc0fb57bfc8ff3f8d20103418d840184f37e978276f12a24816a007c02a7e319b59717d7899ff2dc9a2e5401a89828da32222cec7a57aea1bf39c702ffba1feb14e40dd6d8a82fc1fa2c0785134aca93bbda13b4fb256929417002f460252e1b2f4bd0eb96245175f365d5095e5a3a362a00f8ff5bd89b24c299a63467a493192f24ef4ac3af87c79916c5154b0f572a2d3b6e0c2b77d6bbd456185e2205ce7f4a120ab1510cbceb614b00605e2d28e0d3fa09865dcd5e54eb411d3344c6db63470be653d3f156e3786d0b2692c84788b52a566d06b8400438e58c94e0685ba13650b4e020481e396cb6f58047ed9998650ec8f6fe8a97ba82c0d11d13ae20e7af01f6397acced2a1d9786d223a2b010e080b6307fbb2617824fba9ce83da9eccf9756cd6e0dd12f21e7c05f3479721947dc5ae4dcb07c68ed51e7061fc64c986db9b99773011aade0cc38695bbdf008509fe76b6009e298a9fc758639fc5311bd6538c5613018a0c84a9953d67158e64449b5c466269129adac176e964070dfb619807e0610087c2d486448078e648c9c9b91e6b29adc11699bf5fd626b618d76481de787f62497d7cae8fb2a016ec46039667cd194cb7cbf4355961e1b52e9401b0308c7f547eaa33d06364e53070d108a2664e4d664c63ff23ffa3cec049ce8f45864cf50d80e0610221e16570e55641806f2ce3218df6ea3ecb216830f367861aa73a6c58ffa986a4d59e05292310423a63ffbaba6b8a066e57b96da86c089794b661da777297b5ef531a15338e7c712eed920687ea7a9a9506d65f7940c0e4861369d67a418bec68b2fd14386cdba522152030af3335c3022ff4e49943bdb1dcf3161c929c610e62523377eef338cd0c20973842a91189c2e08780578224fef7637235d3469671d121439001dc7239a11055f4ed62d2d1bb44912c61c17d225e9ef26fc5a37d1f0b96ede60289a716709680780a5012b79fb5f83b26990e23487f84db12eab607fd0a832d0ac757ed0c61099b85d28a2af18a779f98fa87a20911ed753cd20ccc30c0d56307722f5d2ee0b3b9fd8b3cb5ab4852fc20498d5cbd6ac13b3df4b68b4e378ec5965a445c0c8d240a3a7d33461701511c98f6fd770371d40d0302ae70ded9e2ee7ac7a880c1adeb26be1ab8c8d380b355978fa9207eb0793d2306f1feba0f3857752142f69609aaa944eabba4ee10a47c24403e6cc482d71b78a027879ff5061eff7cd2628f0fbace12989cd8b156163a775153caf124f40d4249dffcace3bb2f501ce333c94d8964af0bf6cc9b23e2d4bdfc9a26f6986d639b3991ed2536060d3c96b25e4188cef8f7dae6c0459242f8afcb4068fc227b0f7265dacc7378f2fd9515b040cb2a86d7b7b7149d2a06ef2dd010a244290b8e877499d05656fdb45e4108be6ec9555b5d37221f70ccb9340266433eab91e3c791a32470715e508fa29eec7feb00145477a9a3d28d773daf3c5a14ce00e351af50ca4a135db0faff082fe4b05864c1c4665639236bf4c9204aa83855f8f65717c2020043655e84ed373e0903046acb3df7fabf5ed4f4164dd523b7da0f64e5bde8bba38ed94bed492f060fad25b65b9de8b874ad1fb1640d8db7c37a5bd0a91664525fd55d7470306e355aacba7477335f3b0597cc61dc8d30221860516fae9c81c2d2d18a590e493246e636ed21f51aa54b5ecf2feb8d8c52577172dfac5e3ad943e0042bb6f81f67e24e5c4ab5e6ee9932cb976c1f7bd081a13bdfd9a5c2136bdc16e448e483d42c0bea896d4e543375a6144863f0184d0ff7b6b735277ada1a1f22b66a33c793c6dc98abdfdfdd2429e9ae0eea616c9670f4de0d4d45ad52aedbdbb4f12b99c70f31d3d5a7b0158087aa5b9976541d3a32d3b6e311f37aea4417a97610aee8e0be9edaf46cdb0e716284123ed0eec435adbf911a6c3323d5cb0ce05fd679a435d7444ce24b0657817c17548bc540f6a425a8a98753df559738b204ae10177cefaead21ddcf78b666885cae10b54808e59f61eff806680b327a9663dc1208b67d5440c05eb95c513af42e117a1b72f60231f4be5d2e648017b407872cdac22ee49a02b7cc42b6619085f8c23e80a33b92976c6c39321c621a9e7e6c1a0c190d8712200672bdf9bfd05d5c87784e9fb0d7388736d7b69ba6c357dab6865b0882bbe1fbd035d99196640f5bb9a9bab08a240aae6114c678eecbda0ab51845d0e7c3eea5998bd090e74f4e3435e750b7a4bdcc03969bc8dfe633c7da268c895706fd0d272cec0af072b4fe16040ad7b96d72c597240813c111aed4cd5e7c746044bde83738c374c8c3334b6bd6d56369ae4b892276a25645c24cad62f2102ec92100f1c3a8ab28a8a4df05937c308a4ee198c0259fa13f4ba70ecfd4486c984bbed89c7ca688d7e41258b0bd6b8cb7ed62b302a705d9e0777f138717a18484f1a04d8de201e245785400a2821a66e9c09e40970b7c3d71b5395dc1c79a73cce75bd1b902ae220b2e3c13f9464d605c1f676412ec4de8e19f12cb63856504a1eee0f05e135a719e84858ce522aecf98121c89d502abbbd69ea749f2f1f17bf51bcb8d61909448198455c8a4de8f7e25d5d48311a4a073c587097c6fea734d53d8ee2a2f51fd8ca2c21bbc2603a33eebc0e144fa1409aae58b25d0ab751a48b909924b24d4b9ea5b0430999065625de1745e98b228fe40829a72ac21182f67a8c3455c856af2e6e98bc8c89790d0ad0694cc14a70278fc3b6e8cbe05743625a3dd6136dcdb78a765a34ed7d9c3130df6f8af1ebfdf53cef22e050287cc902aa066c033abf3a0aad92480f90da344f312ae7597240d71caa8e7ca758ef25213a31692413928cd7956a7b2d6480e4f6db33d8d6e0181f4b93f9b7b62797bc957f8cca6231ef8408134c1b6056421a032cf108c8995fad5289b2512a7326ae2cf0b6cdaeaceb2322a4b50dbfc50270800d224fb89c3f4d1b68065a20c5d725c3683aa1b3d9f895011c0ad249948ad74153f036deec843111f6e8882cfdb95da6a1f6ee8549d81b3dd75013b283283946040d9514d705fc83cdc22c488a9f9295eb2b5de5cc38958e7fcc267c5f40916949356a804858107f3a951c4cd2471734c78171e92e5229200fe14081291dc5539c010413fe4e59b7d33062a5e0a6b6121aad3d9fd29f4a04a4fdee94baae9a4145eb05291406f109a87e558c07a995e8a0b7766f88e615e597c9f0a25f72846ed28026491c751931675f359ed1ac338583dcdde04a7134609545cb4dacd1ba24d66b0f146018ec989a3459e0b516e3a35970c2831cdd4419040a77b9046a3be3f711fb5d6bea1f912f30112030483b323b1a010e1d06f723b9a2784575bfc0515fa2c1b35a0c768fe8564ed523180705b3b3c84484f3291a0b10b4edcbf632ee09cf873574ec39a8b0eaf6c7bc80e8fc8963693b62ef3856003414b532accee84f739d0324b058cdb4f6e9820139147b2165882d90feebe3d8dcf5f09d68d2bcc14f4b45f30e32100276438b8fc2d0e67f4870742092ef6028d35a7a35704be93af56c96434ca49f276ef87f211762ec00e62216136c4a98f922c898dcdf8798fb3a205338492dbce32f4801521a30867f4fdab33b2f448adbf02b31e31bd44cfcb68e677385477ad8f57a0cfa4a3b22489df3f12a6d90bd14baf8cca415015cbf8e9522a99132f5d243f1dbc0748acf6f1e95b3f649cc6bf16217813f42e1ecef0f193ab2b00868ad4ab4286902bd9812a686124c838cc63f70e7474f814c3024ca80d90bce29187d8f39fee56bb153e083541593f601261aafe81a404a17e95fc5eb9119c3de1d013fd6baf85cd0e2235651e9e1ec4c8017fe482287132343d78bd334f18294e26043b772d70a205b6ecee377b4dfe6f0c18466f1458df11f270697ffc490f88442a42241a8ea760624e858b28df513d5d9349f75efe227f87c2e8b9c6a5a61dd8e88c0cf123be50893bc87060ffbdd7a06ae711c94484deabb0ddea99e24c233b7e86ba832f872ae4564d26bce524364e16b9ff060a8818bbe799f6dfe025c1aa3eb4e255f06b233ed39038105e47e163d2c3c270a448942496e2fee29363eabb1e1cc8ef16c0b7b226472e6c8101a66e922296a90c235d4a8f83b5f02c91abbd2d19f9ffc10a0aab19b47c39e5d94a5b20422bb84272caaf000161b837650bc99d1f45ef9f235218911105807dc5d679cdd0b2fd24c5e320778bf9d087ffe5da6c2607ad16a5445030405edb5be0304c2fa89e4982f04712b506aa2ae8f1cc4b5a653891b176fa4792b8c85e81b4764606078577085ed5b18f9e97a48808e915214232321ad53dddf1f2ef9801920e394581977be4de63ad5d9b401078255b012dd84f1047aa299a07155d8934f633f7578ff79988fd550ff9d9a50605df42da5fe9456dcf1e2a3bbd2105157093a6fe3e9859410db65ff81112f517f68ae0f93f451cc02bc932d6d80bf343d29a098c507aa16c0002496c966615dc4f5558d764c1c04aed62726db50269240d5e1d023ccbcfaa1905d10d48e32507104dee0b3bcc37ca155669fb1dd18c351be270bbc5aa0583affd6509e0c7f472f371315786aa887ed8240995c404d500f60934af82fa65cf441fc85fb02c8ab486370d71a2c1550f9f4a772923532d4381e181e29da02c5a0f35f804de6718ebe717c3e5b38189c60e22eb531ce01e010b0276832de96d40c38985754cd6195e10a2a20bf4239ade85616cf49c0ce280f907a748b028e933549615d5b02c690372c8baa97f461b8b06e7f7ebfd5eb1b780df5ac3ec26b6038e452907d696caf621512596f402c9621812caf3307d1c48b95696248b44b39024f42d504d3022456ea120bc5d95441a9f951af873093248cfb46119cac967b45b11ced2d98acbd9cad87f93080b50f55a0f7bef4b9f59ce935b5521b902276494fcf4afe532a67902124849b51f9e552bceb4cd52089d43b50cc9305180c427c6ac7b0435e765b8d65401a46cb0ae98cb335bd6145ec2aecfc7148e132aa88b6e42b14acacc4ec8cb65431f951a930614b884fba2d0c3a88b2cf6902830242dc7c466ac367629965014fe3f10897b0628630b0133f84d5edcc03f1fb4415a7d9ebd0607a674d18a3b0118d0874d4e0e15c54274363cab0a13604291bf3cb4d1ffbe08f2ab0cf706f7ee8af5b66b4cf19801dc66a6f471ac72ec59747ee6daacd1e61d4014d4fbeda2aa00941daeb1d4892450b18cc76f302fec0d4dac79a82372d8e82fb551e7c9c908dae2726ccd851c530b81ac1bdd5fb8239428e56e40734fadf033f1f48f49545d6c6e448075f58f0381dc01158ac2279db4bda7b748662285836561833137318ceb319af41e3a9b49fff59549971c1512684c3a6409e5c4d525860d0b4a3b46e16616a12df47c94d64c8ec8c29925b49da50c79e022d7c87f6695c1cf507d9e35e7f12ca84c7c8292da8f10e5ac4d8825066a3ed09f3e99e28f5282f0d86f060c78fd3c1870f0f00e23b60d4c2510a94bb528dec8263cdca1cf38e4e410a31af1c207f682ebe8a635777e379f2971aab71a9a0a09c1636ca3c9595e93a2273893f7154567ef974260b0184a7ceb5c947a8f677487aaddad5fb034fc1d1b12462c907f1ee2399723598186ea688169a6059b0f298b1ee79a1237022e19f315492266828f4ccdabc351133ea89ed23cc627c50bcb294f01e9f81f2c544476f32ca9804811cbe107125b841993d6743a003c218794abd85a1b8da7218303ba62fb3d6e97d89914db6bf05d2e2b3a98c0f19edc09fc0251eb484ac99ce1ed7e140320f586a1dd4e0fb1fe02e66a5866f2e5848da7281e1eb8a7c34c649356240fad58c0d0c882f3ef15bb72123b1deb3b5a0f3bd417ff672b5bfaca0effccfb152bf6ba861ddeb0092a820063ccfcba60a7c874f552e0ac260ecc11aaf2d26159117c19cb02b1de14fe31a67b1eb422cd81b2d4b4e01303196750730d835bac6e2e95188d3bc5e4d07f6e47a08981dc144f4b8101eec8a91892e2bbebcba9b6498d24065e12e4c0556ff3f4102dab231bdc097b2540b626dce1068094568ba1647576584f1528716fab1e73015c5cc289ca09d95ef819bbaae3d6eeec73ca3c0c04f6477e515d7e36dee90c1af688f9d4ed80fbf076e988748f7d257f487991eac5bea6a906a6d556b6aaf8283b8f4f2e72a7994e7cd8f8bfaec649916b34d8e3c04cbaf49d719e85fa294b9350db941e9baff3474a26ae1e33eeb8b6855730a1c8c46d438dc57b618aa9279266488e0f0a7d757c7e5c52e8e1890119825ea33842aaef2ceb78538cc26f918a89243f4aba14974016932c21ab39a73af668d1fc79d31415828550e896ef3b52e69be04ee46ab75fe052509eab769b4bed361c070cba0f6fb0087f3ed74049ab8894dd4aa7f883fc5bdc564d48b8563efc2f460ad112987daf98f4391779f68e85c63e1c63475ed4a9775e9beb6d2b6cc281bad2b13b961ab3fc7f58bc890d0c4cebbc0e44776eaa4f1fc0b64f35a71260ca892bb35430c8c476572913d7261edb64435c599ea3447a7f5b42de6b5070f611562dbb23b1a2ef8dcb75c68f768581dd334251f4b6803dc5fb6c4c28632586d47cf58cdc87377c4488838094085ba2392b74229bc1f25c4e46441eda020b4ba290bbe2320d40a880470f2e0ba29203f0f3bbeec855555e4b990482e03306bf1e213c3d6a9d911313d45f9d44502178f9691e27c081b4ce3841f5f56d6ad01897ec28488baca7b4a320f8aa4402b9d9a2f3dcf65529b39da9893b0a54520258145a0a4f9088b56d722bf057a64bcbda610f0a569b3f6ea2c0bad29372422915fb6b2bd258989f20604a2bc09aa44522a59cca1b05cf517868da2880f723e21c4dd85b73449ccf61200bb8d9bc4b1afc0c233179af5076349183403ac2ca1a8a3b1fe926faa93bca4b67f037eaad9e819e362b5966b7a51a0998a1318962e92b4d5404946460c42cf5c88a05f3898569d14a94a13c9157ded21876ce5a09fc028883c97d8e738a0a5aca51a9b79d7cab978062121667f0d820ebef562e12b4f494224f77b13c3706b69c7fae241c05ba99c473f152bc202b2522998b9ea1b48eafb5e8c57eb4211a428e1f9e3cbc8f0d0cc876ac08016cd2838e3601e4237b43aef964543b9066a103310db7534a4dc0049b644810769be2d9044bc3cc40dd303eaed46626bf786181454a79a81b54e561992fa264626996b1e4b04247219abb540cc76e1ac90810aff4f8b8272ca1b5877c594396855a6f0fdc6366d223f32e73627707c1d8e3e165a26d3d01cf0d084056c7b4ea7eac822e8d47826842463811ebe3ec3dcabff9bc79c5c2ef61e06176d8808843126928af2360dc4a1db153d0c693922ada0bf2cacf225ae12094fa3fb435e5ee6b7a974e08e1b7bae08bd0ecb1ddedbf6483912721285f8df93746affda5eff5cd4f0ef334887350de08158fca1a2ab036cac7212170e8348033086394a25a3db6e7a44f5946d9bf2dee258c5375d742c59ef37904300636580d0599934d9b8c08b81640e4acc12270c4d001f18a70d0901dce1110c9347b3b81099cc742a09a106c859ab47edd281b937bc7b6e84e6b45bca7d511f365149a583c256f12719be12c6c6620ae77977d260081b48e3cdf8cadf1ca58ce88f48468f1256031a806ce003322a77e76d1dcb29c03572c3c71db33bbfbe5fc583f41c64db13e0d6e4d9d54c48a60cf1c36a12dce38c91f8a32701b926c6dfc944ddb262a7a03b7ff4914577dad6cf64b30d9786068ea510a38f66efbf55e11cb3056f41dd7dd8e8f4d96c61332ebce71cef167a6d7d432e53a7c248f89055bbf2a54f1e710d9aaad8a08c941240726ffd365b3d0034e4a09b0b5e52ef4a35c2206e603a0f4ec2c59a0305aaff84a58d89fb485da8d48498ea960cb9e54b6aa35d921ca5c3077babb0e2ec4a3858aaaff76ee08daa408ba4bf3c463cf6650c1078ab7953c83323258bdacd4e14ab408d7dfa9cba997988508748fa544bf55605e89abe672f0138ba29ed8453bec120163df7b80f265d3ebd0a195ca8f0f3dc0095c2d5124a4e68bf53bdd60db04d1938cc0af00b2cb0848f3caaceea3afa94afd591f7f11a3646ac212ab339e0aad29df3809cf53c3623b49114a6f6235120a1bf2bf7f7a0559204df388a2b7c2405389191b0b202f80b48020b3a5360d1dcdaf8023877ac87e87beedf4799ff293d1952196406dfa786980c6d43ef00bcccf0ae85e633db1899f3c1bf136c32f50c7deaec1d2eee1213a28b3d716e893a7d08c20a863a1407b7bb452fcedea64801a513d7637710205f27f267aae27620c0f2a471b1ae2f8c145250c855e829d231a3db441c4212772d8136600b550c200c258882da9ee7f7ed5f1619600a9bd65dd51d042b02e796390e942d3b18dc2851c0468a403deeda06b0f1485b9d55856072ba1aa7ca8f3ef901160a9bb7fa0c8d08c7d6c663625392ddc8b2bf11f4f9e130e70e0e1d51fdb6da9f4d11e761d45f824bb0e807ce6224a1ac3a16a68801dfebae6d1cbf3208d2a83c1f677e4bbae0b719b3f5043ed4bbdb45af86717d5739b018aae00ee631b579cf89ac2143ef64f471b482669036bbd135b5d3a8ad6ec74407dd3656e03f93ea1a7e88f23bc0448150d9040208dd54704a79fd0353fe21090e3699bdac3afcef823b900ae3d9705696c001b31069cd2860057882798259c18d0e7ae54c81f844bc358aaf6d9aae77708659254d1edf3423391413048438e8b2d21a182d2006fc17c6a9a06e8742e9c9b5b5a0f4cd7607d80d9d46edeafd2bbcecf9337510806571ea1600bc0a3b1db2a8a3f1a67688ab9aeb4befde1eae12057992d228051a470e37b42352f35d756108d5d41704daed98a3d98ccb56eb1955153aeb28df462853ce36da7ac2d75c13ad5f67d00b41419d119a079cfd0c9df968cfe15d81a8bcc0912a5e30e97e6ab8bacdfc45375cc547588237885f4c129459db0839b6804e531d1bacb7610879d5dcd96057d857f47972c664fb6de2a0382b47fa93d2c7ed410493c84d1dab25d6bc0aaafb7cb795fd12caf91f828dac68418350091ca7187c7202223ca03ff70a843e77c22932c5299a8d7921f326682c3edfc10b367c5bca4044a282c705bab4598e3917753b2374787125a7c5fd28223ef617ae4435813da50b3db1072b0f937862202b1b7437fc236a766dd14cb503a409bed1b2d489eae1f29949fe784a356af9d4b638f9057a703ffc94bc9a15906a1c08af8a6095459e44623b24509c1d2880ad3eac8f1b5397003adc56d0c41c176f82dd185fb80969e8b977fa686c6331099f043ce9b13ae6a725adedc4a22ee8e67a18514341d9c5619b2c985ba73cbf97bc9059c56024e13e8db0020ac2caa014e152c3bd7aa01146399a4eec568c23807241be6822db2dabe9b90138244cd187223e464277624fe354ee487b8ecb6818a3ed75af04a7b3a970803dbf7903c911324c9e97b394f896e4ff48ee75d48a3c982d485c3b233a431cddd9f1cf8815632c9b6c675d10018a4dc19d5efd805de23408390c20b8948d7986e18f84cb7003eb98920eaecd1e4be6ced486717fb9d6a1465cfc786b985030d5247905449f25c2b1925e1267bf6f75d03b777ae6f5c446d37d12229996a86ecafece98692401d080aa10a2a14b0d150eb8f05e0142612c7ba92fb51487b608ffb9fb34d66340a8f3254e38155a279b7db1903182c8d68fbbb682de90d1a04dca16f10b830800e82d10f7112fd2bd3bd0a182c019b34e1f72fcae4e5703528be92f1275032e4fe56c58ff56cec3fbaf31ae1c707a888482b42b1c9f89f8c8137409c80277e7ba896d76c21991657eca19d4336a2998915a0b8303ae51c5eaa5cf7c3083b80aa70d0663e79765216ae5f985cc56046dfbe3811c7190cb816f7e31460cf249db5536cd946c45980ff045974a71a0967f70f842a3fc76134f86a12107ba3c186d94d808a8261ec434ede20ca560d8811341c148f0dc7cbee6b7a7db7f7c1c341b1ac3829eca9ad4b969414cacc09a822f568bf79e11c6c32a4216a54ae21e0c8c46a78d13747ffc933271a7cc17e5ca963d533a5dd672be8190d2db39ba8465e142c265756bcb52896e53fafee52e12f043ce19b109754e3138808aedac1e3805d491b1c46c025a14f0e7a2cbcdce4824c377d80164b7c69c4c9c820b026e1d1e553f43e9ff134ae114c79a9c3f83e59c037c31724dcc64584326a8469be890253223a9b52e43c6de4780902b8f4bdea829e6605fc460ee7de840e86afe8e38cd3ec6c228b0f55f9e25ab7d5f26bb36e7985986d4e3e16c09a8af955a94971432b41bdbe96c92281e46ad991012aa7d0fa5494b971f0dd1137ae83bec44c39b361290ac54c3d479210cb33a312c54c797ee64daecadc97a9bf6f3c74b09c0366d01a738f4ae8089e7e6338c28a1df3fd5f6e8c13263027ccc6c6a339f110443820560c3463ef1ac3eb80c881a3ae4bbe0c16eba3e57d94dbd2fea6f94f9a09214323103e55d70234825539dbce7d67f4e90a026f982b82ab3f5c72d3e8a62453a5bbebbcce79288cb086b3ceedcf0a0cd412d189ca4dca3c604ff014a6d362dc64637727a6fdf3a4708c8dcb8293b30005e103ce7680625ef1c8a2d19f9318f5294b16e667401735ff3bd21c6df7abc6ec93756bb977ce45453fcb526ea0b5e6bc0c3a8e0c43b5125bf2475caf82a1c4e92b39e5977a74c38c5e1ff37c66219ca9084b2a6ec835150bb0106b8d6e03ae0b840ea9b6c895fab9a31562abff90154f1a46edfb106855950a0f2a5552e3173a3efb5772f95ff13921ddda3b744a4e6746945d399134ff6f8b0fd7bab8f6d32cf8766decd9dd7a6a6d653e08a385a96f089eb200eeacda4c2e28ca76d12438332f406cc93361f9e504c0f8683d67e28c05791322479c8f53b4b19129d6cc3cdcd4968304af4fa0bdb92c8c18d93ab605b43886cc32187383c9404e4bfd8133971ca46d32fc5e251071b6b4e06e4aeb7a650bb2682b2b9384127848ce5911a14cdf71742c3b1c4968e6b31983a85d6fcee7e119ddb894ce673d45781da98db924a09141696222f57859c2f300f7a0f316771cd84a5420c786cadef25ee6df3371b6762d0a99e7cadcdf39eb23e8727928fed976b2294c9f3107e76bdaa785daffc97736d5d6ad83a7104609c4efbde479f2db3e53c238aa42fbfbbef0fde7db904691aea2289989a982c53b6bdd78a15b0159480abb207ce0c70bb55ca428d62321e4b78d5a7b82a85ae6f26c46ba13d160187b09ed8ae0821a6ea29eb021a226b93a0b264f2bcfa9f608fc2827ba0a43993c0bbd754e8f856b85ee14079d151909b2e900b49d25198d2af95bfc825f03ee1052ecdf381d39ea917b8c1d519bc3ba5b3c1fafb7a69d2b5663e2ebf0c4a9497fca7595ae97303ae051987d152c47a98c8975eb1eb7dff4cdce52bba8ec5eb68aecfeb946369df14cc807405d2e369bbc9a872ddb612b5baeefb8e34ce99af87fa51c7d2c33de8d6e9997375e88743c133eded419326237549794b547c4a5a22d776765adc4f49501d06d60a779f6507a3ee358d2650ad3b22b9f08ee149303038c6caa6225c322312c00ced250f8bf908335cb099c3f4d45fbd007807583b3b06352c54d86dd060530841014e572d80bc3f2fac4d867aaf892bb30b460ab0263d85e5663645dd6b1b47dac151cfffc4efbb08fa5150bc1bc80159fd0af9db5b6d98fdafd859c24d9809640d02b393e8e892d2025f2d6418c7d1630bfe3380dab7d016739607882b903c21717bed143f6f9aeb8d55d63c1b8a790dccd83b7e79cca96840facf2ab5655e35387b8a89180d90ed6625bd6bcdea75bdc381d36864dbccf7c62422d76e24a96dd43640597500da9922de3a4699a4ca25c12c0a9b9c4cd0fd47bb582876744dbba633528536ed68affa96d5647f744c5cdc592e893a784e83cded0422c7a3ff4d58473f4dc4a2283129958383a28c8d115d70e99380842f0e3ea1ad8f7688e99e0682349370c5535f030e33a3a43acd8d97419e5480d244c032c450d9d35c5f479e42de53c32802965fb21530a1b1066936a4a8d246cf1e430bc292029e0dce5af22c822c9f4a0241c60760645180eede7c9044010fa50f792466658fab98d4aafcb41a7e640276acd01c01e09962a6d3c61b27edbd84bc1a15e99630e987031a10c29d29da2468f2dd4bf255c088bcd055d31d98248fea314807b5e2629e1b39c874b0b3f3f4e2b69292b3da6fc2d7b090e4ff9fa7170ac6406c57f14e408826a0f0668d0edc87c95d2490833d839d7ab4353c63d13e05d30c4535df5c057d1286be5768eef903a2a77626d5f1d2ec61d5df32393b395aa437c887a7f68baef2289b5ea2165c9fe8aa768a8af2a380c30795613e0d4c7ca6f3cd5c5304405fa9131cf56e7033b5570356f5da0b964d15e5168870ece30c2e744c92227099c99e190ba0953952fd6f133ec00a5732e3ee06f2027699ecdf265594495c0e37b7a49878cd6a137be32e93502c4e36fbde42fc10fd6164464a8ba7317eae118e5c8994ab4c30d350341bc16c67d42ab2a5efc5a00da5a0f2e2f370132289efc906586648a2ec86568423c67f5d3d20ccc5268c8fe2fdef594bb3f283e3586c36e48eb0ca1751eae000d6a9bdad099f0bbae0349c3f0c6e7b6f5374ac2f5857374c0a531c98c85bb6275c4d7830dacaba15027112187624882fea46837f257d93038b266a0f4cb920e9e04aeb43ed65535a994aea39c6ff6b0d632436af283ba21c7b02d1f117ce6ae48d73645a1a19662bd69e57da869a2207c42f14c129bc39427e0e2ac7744d9040fbba4f9194e734d6f1cba556622bfd3c5207ab2dbe11d94cc15ebe8689e8fba9aa5a86a8e113ced3204ede87d737cf3c2b37cff0380db96ebc5b513df158fcabeab3007ccf395e17a21949a10ac1177b3b51c09ee3a926ccf772d385c47556b088b46428e30551199817505bfb31fe1916a672b9bf63dd83c1e67f31a768dc238eeda74d60337a91c6623bd93525d7e3c483f1bca991bde297cf475c22e22c828aa25e70f85027213c9fe112eb71199b8c8d72298caca5308b063e62c62f63b5c09acee5a7a38278566bda35ebf304264eea8c0100841170728aaffbb9898976a1d5b4acbb4c3bd1b752dbf274d7d0f90523fe559f1d10fadfc890794142412631bdd046a52db9eeea3ff624e7c1c0138b81996b2695fb8caacb8f88aba245619060eaea06f6bb65434e765a036ccf046e6c76cf206f135a8a94b1324584b116d04ff049dd68e50a34f80c788b59dc6b1a8da80c3148919c1d04f577a8f9d5a337edd5dc6632bf159e1556c838c1b686ecca0c99a698d5b04077cef7880809e3ba740e5e4e609dbb64211ce70c03a85369d552e3cf4c05d3dac3eb409774188f67457d71d56cbc41a5e8288a5279034d9420a2a63ec6eb9836d979e7cb0f290bae74817a9310cfe5cd62084228b85653678408a697884ca97c713beaa78a96b7a3e7805f4160646fa95e092e048f7a1ad2d6c4f34763cd2cbf647330e3979cfe70498cf5aab2f700d7dfb8267c65cc6f9478a7781a436dfa3bb0602eec3b9a40d520c71354733e0e1092b3b5c74f57db3f2e2fbc73bd9700e08d3b2ce2914435fc3ba50a157eb22b8c2a43a0ddcbe2217265ac4b5889194c51fc4ab6e6b1da5eee1a644e49a85b0e0bb6a805373aedf6e56230e6bc4cf381396a2c01e75462be4c9a4a4a9439d371ad5ff8d151972266b75cef037208bbdad4e74e72455ecf2d1d9d6c150c208ad557a868f99c7c1bf782c6cdefa80190066198bb6c37858338754bad9aa9f589ddf3f640860594e479d9b76d0b3177c22d0405c524c694a397e02dec86b4976d0c0ab00b97513afd251fca8860b8efd3ec504f52308d83e169a413d664ddb4c40bc0ab5b1669ace816dafc64b7211f3ade0404c99ee6c752d688867e4ed31c53078c19174bab653fb84f30edc4668c90f352989cc521ef219636adf6a36b3904284e85589e292ff84764aec745be6f7d8042d011616a76a22bbca3feaa0234b45816e3e25c8bd05c319471d231a84f631639060aea43f63a658eb61271c352f2ac0a869513fc38762c0566c0bd4c3b16aa02063f2e52010968d7628fd320a96747386aaffd35d6ea3ef5971bc2113683d36a3534719585e4c913c2a589fd4491f49f20b8a84f2c63b5d746a733bc0263dc9f7df2387ee4ff680a93952dd0f7b4bb1d414423bba70183982b09d1f9d9a075a56974b7a2234212c17e56455e6f242304b214d8f59d83b35f257d40a7b094a8835f3e0d3ede43aee96b9668db33948f737191740a820ca6aa4a9cb322e1d995e219f257d2197f5e19db4489b3279e360026ca8d8487dbe72021d64d707c05795ebbfd7babd5ebdb29c098a38d28b06555d7bc7383e4334df80c231a6654cfdf97ef573a96fb86a374d4d64504830699b2d1dcd15d97e880c5ab42b65fb397d4603f9bdf365234869f5171da5153b9e57d975989e9cf94c1f2b3cb2b0a75573b43d3787115014c668c8e6e9f931884966108aed3982bcd31944bf398318a8f00609246026d48474ec75ed4d90e7610d5d00ecab8ede9d17bf8415a0bd01a21b2b04c1052edbc7b1a49faed38f8a834070c3b96e87a6c2c801e9fb82d46da2caf01cc173e4f6d87d81eb6f2e4cf5ed9d33a635eb328cce89f2832d1f4d432746c692f852a91727a220701f5c2c53dc29ceecb812ab02abb7362d8f09c30545e0535fa9a8d5cdef9a591b61dd233415d2df3032ff7a13d95d4db030d9633f96ff8e1b3784a86fb554f2033fbc084e3f394a91dc7da6cee70e075195195e60c4058bbe435e48e4ad3eb5dd26a118f4aa82d109554894c5894c9e1f9672cb0f6b1f3b85fbc9776b0510188c86ae33738b523dcac7c1c822acc04bb0b4b184637136596938ef5341c843b3da3ba1984a1524069c0561f08aa1669aea55748a867445a496e7b02759cacafefa62a0aaf58a2e4e721e0ab701ec3eaa9b06d69a0dc30f84c2cf60e203e0c19f82bdd8213eb982ef380f1ce982e7f247b241121638f3fc81d30d3963dd90b6f3b91fd9cda0b1ebbcf76a01fd5189b38a9bb4a380dd935494a2267f8243abcf106076a3c0a1fd348466b9687e2d10a51aebdfbc182be1e441d1ff7072cf8c6524e8d86e3ebae237e70e05d75f7eb1de242c34394d64838ab098f5e4ac630ae54f34a8e1c646a42dc3ddb3b96550eb913c4d2f4e0c8aa558d5813d68b0352635fc3bd75f7aea65a21bacecfb440ffe18f88be2a288d02fe3b4fad230d33f40a56c76bd20a37f796166e9a19989177715ebad5e1122fe66dd6319aec725857b69b6ca946fd8445aecb339a97afa1324b0c19ab8c1608e695892898891d135d49eff013a6093139e3d861d4b39e046650665a6e29b5433905eb1a95966982c88d0e5c17efa0bc61e4b7b24c8ceb27a20bfe93de263bdaca34de80a7dd717eb871d805f7c9eeac1867df96fe17984279df50b5f9fdc50abff5581021ca9234b9f3eaf9f63fd3e092f83bb79a874e2550470df977089a51267ef5a7d7ec0c2f4eba251d26abc03df26e60bc25a968357e672fe5e84c9f56da8a68e4ec9e752a9cc41311142033ec36343ef43573e675f16009a4a1ff695d0a4a68a2a617f81debcd2dda17e49fbd48f98a68a4300339d5401980cc570bd1eb845fb09e1f5ff3cf95a3054ea4c160c5ecbc157e35139713d5aa1d41e225f1acc2d67a81340923e93ab49452316287a19ca510017d9ce02dd9cc8d38d161fd8ef8bc61760a5ad43db2d88ae13ed22ac3726a517972c40031557e93945d75d6ed4bc291ca7c55574ae035f127d305c1067501e8128fdf13bddd96d1883b83504e4030aabfe885d30145651071934dbdc5660d192412e4326ca329d41600842843330a27ee794a04e7f060d00e080c7363e078f574c2e90676b4346bacff3be5ee26e9b50f15ebe4f3fa4318d5ce7e1e6fdb117530192f5faff8bdab8dbde524a29934c01890891087908b6c48196dd94d2bef1dd784f8df28652ea39b8d15e31f82002d310e67e89966166e026387291ab22dbf0be300a45878676c07142e371e9c1a395d91d1b9b01dee9ee9ef5062a93d5d4eaeda0ebbe99c6d1349d9b065e059e2f5ae1a457ea54e5481926fbed40cf92769c287af5c8ae122ce024c735d752c4c576706c78a847e0818de7813b1c00fede6c0edcb15aa175f84f51885a921ab16c20046108a85a57bb1004cdbaefcc537e87b82217e8fedc7b6f53a92381e445725fafaf69dcf3bc9776a1025ca63415f832b535fb2db9429ea29776b9c8223bcb9bfdccee8ce6a97e5d988b2f8eebec77f6bbd0b2ee7361dbd090cefbf39776672e524aa9e5b9eff6feccfab2483e8adcb7b77699647b6f0e9d3fd99f5a520c24fb0a50806571122ce51e177d362fedca6297756356a7da5ae79df1b8389add19bdb31a34eb993b05ea7402c1973bab41f33e29726f2eebce66ac1a7467a3d19dd1f928140a359bd5eac2528215c828052ae2a2c7b58670f0536e59add55acb707cad67fca94c56f3648b96b22577b97fe1fefd1185dc37c4337c504a69e02183572a83b5b62374adee6efb7a5d9dd75471afea2e9754c3df1748909f5402a82be2e2e501ba3bdd4dc77291e404e6fce77b0cef215f254426ee7dd97a6fd7fd00dbba9b6b4eba5cd2e549d7cc13f8092cb374591715dd859a4d939245ffddcca118177b21c1fbcf3c8117e027ff1f7e72176a681d43340d027ac6df398a5ba8cb4d8aa1498d73a0977d340d273fd63352c668315a8c16a3d16292268447e972b9a44bba7ca40bf25d8216fce122872a822b168bdd6a802975483c5535f4f4111380e8b9582cd675a21c980eb6a960341a39761d2cdb4514ac384a80efd02908235a1d727144e3388c69d9abcd933fd7bda82785f50c169ecb8b8d23561ee57835cf95733ca5a308d83f46088f7568f4a4696a9e13da05ab82d6019b2a94d0b246ef7936ac75a157ab61ed46f66a23186c041bd99c9ef15c31af8919c4ac70a253f6894ed5982d6682148cc2b12a9187e8223ac549cfd593dd7379c78946b051cbc3f1945767e3576979fc72cdf19312ed4215a275c8a6b9a3f79a46f4f509f97d758821f2f8e5effb48a49127eab8eb636b320a1a79ca0c9a6bafbdf6babbb9aeebbaeededbdd4efaecbae62637b9c9711ce7b9cc67f643bb59ad569ed7f28425b8bbbb3b08dcfddddd8d236bd96ddbda8eb338aac033e3900898082cdea6add568b4d94c261bab4cac32b1755d419705c4a48913a1274339fee3d3c3936f5f9bf666da962bbcaffb1add1787e7a9695c560596b29b43c9356e71bcd8709589f7d24076efeddbdc8bddcfd3cecefd170b44374c15caa3845c7801c60d57001dc79de4ebec884a506badb56f59080a6e5ca5d66ae1b5028c802363c95c383cf03cafe49556565ebe182e2d2c2b2f5f0c9716161446803bdb2469e4893a6971ed2888b035a9147237f809cecb7f31c64f44e597f0e3b818e1b842fbb2572a954a5ecad312f5a8473d4a690a9d29b7f4122cc9929412fff5bac6650e8d336024975e827f05167d090ce242991b79e14864277bfd3ca4cca590f41e18a469d9eba629fd984ba003699afeb28f1c426099695d5c9147e94229a1dc3829a1a8a8cce4c37678643eaf148f0103965a6badb5d65a6bb5d2bdba6eea917a0427571cff02e7b1ba6875c13c65753c555d2ed7bd2c3210745dd775316a54806f28a4918712f6144bde3b4e24daf16ad72aabec6e8ec3f8fbfe51536745eb9e880d07c2fce05c47872a59c10ecd86035d47c7a6ae56ad556bd55ab556add56a9efc653b3cb207f930ee423810d9fd03be23bbe60b704fc9272eba48d42d2275db74f62167bd0881e5a394c03119a34f6938daf7e84e12928734c38c2b026b140193defb497950b98347cf2ca2f72a09a606942ffd0f94ff517af92a811fca7f220a4a7c7161f94d9a9625876438ca990f94529801510692fc10bdf7ab2e8fbf5a0da1e4d00a0f3989878f22a370f4a210a60612291c85de8bfe734caafc889fc0186a0db5865a43ada1d6504ba6a4744e826e3767cd93dfc82194921b17bd888b44e8c1db23fef21c1761eec271b58c68c9161d1a1ab236e5f562c509766060c89e519063f33cafbdc62824af5b44aa5efd7171865560b05ce9544d51bc92ebed2b278a7d99eb112ef0601d129de292e8d4f560de4a1e2b2cbbccdced3cd168d431912421d45abbf667bf4c6be7206bb5b59816ecdcdcb4623e383b46603dae1c6badb530503b72794adef48cec46ba546bab937068686805b22e6a58d6ddb6ad57a34ffe337ea4693ae798ae395d413fd951b2fb162f0ff2b9c945ea2b68077994326e87d605cfb4b2081da28e4c48c0f3e504f1ea9349588cebfbb8b7b74e97db968b3748d774855a4c176eb084ccc68fbc248a07b074d954bd7714031a55ecb05aade46aaa1acf8c80254552dbdc07b88c127fc0630f098d6d1b22c76f75b9d50e352808813fcbfbe3ea9b17943d2ece2c755e98a2cef0993474af47c50ea5a53c7256e0b16f403821af9dac97742117238ff6a9cde919087c8dc50c2dccbe5e73926ae8ac15c7ee54516be56ee5aea8e32e8d465b828f90501073c6a07c9f1294c08282310d02064104118495374104216f826012044f50ad3a32cc64e8992b49303578dffd0fa747e02478b4b223b0748c318e23704b1e7bf6d3c6130cf002e3483c5060e01195472067230c3c72f98505789c8185f9f5c7194aa09b04991f8120f583cc90ab618c8b1f13f0f849d28b2b079957390040f8f3005835b2b6b18ad42627c0f6666db248bd91ac2336476675649647967374765aaea9923c7ef2efe17c5c40319f99cc459211d8ff063b645d955a8be30a6282b5b62d103bdfce1c649e80fc69c10f023de3417ac69f24e1ab1100f736cb642f353d436309b602b9f7d6dd2291a8bbebbaae13891cc7c075e49503637d9e2742e2b2c3dd7befbd3b17767bae8b6b39fadab6472894f83f84c31ff048472494124e7109598e93ddfb7ec0a2910665b7213d1292d88cb2255d94eb3a3172d4ee73f290649c6f5311e8f99deb9c3d6648694f40e699c7e32939a5a3cc966fe1b5cc2862655ae316c6daa694524a29a594b65c517bbbc8945edb3c51a0ba240b6b9baa2e51fa5ea54d15cbd3b746a48a257cc14ff4e98f8dd2cfa104cf5c7328c19ec77a47a423789583cc01e8190a455e794774349aad1c87f14d56b0a375b5db01b948bb5ad8d15c84c13a9d6ec7f3fa2b7f2b2e91dea3a31557590153feda3e7b17f444df81dc7b7574c18ee2043d9e9360903fda57969214030c2b2101ec171c6822842cc3285e41d93e90ac726d63d743a68fc191d362e4a0c81449a63d8cc2f1caeeebb2accd5a9a67e977344a613d43bfc63b2117693714e462470bcaf4bb9aa73ed3ef803c35da8e46dfe7c663023d70b65f9f926ae883e56f9322f456c36b1b028f1f174d9f529817f92ee08238804084f40ccdc12ac9742753fa4dbfc665a7e322ed763a981178ec6099feb54d558ca75ffea62a4678b5e8137d172eb8162f32a52c178c4cffda7a8652fa75e6a91f7a6d15b1b0c7b0442ab10c5cb2256200946b7dafd483519d24d7ca515a7532a52a99524ae987c1d255bce451c6892b3ed7e7ea6325e44aeba141ae31c0c955acabe6810f7260c51362202832034ef605cfaa892a327d4a9f73e20499fe652121c9d4298f231c34b1824cbf99a87db5562f903092c1441399fee450f2bdf81d9e12e54a05b5914c0c31c308dd8b52dab46535b688ec6d5b93a3add654d52cb4c0b71c4a30cd028f6f032088a0051142d0e2eedc16a56f83b2ac914fcb40e35f1a10f07fc1812643182f0dc7457f224fc0f3d8b21cd6f48c0f9e61e76ab958835a5ed81ec0c8deb2ef1969eda565a7130cb243cf5ce00e215fdbf50b746b97766936b440f4dbb72ddbea5e18c76b05c47eaa5de2668923d33eb56eed48a4090c5c50647ee739a7f7e8af1f437f0de75740e6ca83c54f92e226f8331748f2881a8d5c762358269db36980c711b8028c4346fee3d363d4f82d9b4994285102a684cfce14d5f33cdac8f26d48176a963ffa01cbf7e68be746839ddc59e4fef101cade1c89349ea0ec92e5db8083ab56827678aa6930d8c9b2c8ed4016b55ef0441e9b4996924e9fa41e5e128264335410cf937c9c024c5f78c65596a3e83f50af336453456ddae59962a698a9679657f27cd3cd24e4aa2fc8e4e3829809c854a32c17f0208f265bcb054dc8a3c989497a8bab6b4bce0b4df6b837cfc481a61e1937af37b4de5423b55563d5c755a29d8a7345939a536175a7f650e92c432d2c96272d45443725f247538e0cc7194c642a838b252fb469cb23cc4d509e2d489a467ebda91f68152ea24ff3a51224490891270b80c873e4514ec6cd142b0599389065e885a5039c9165a81aa9384d9a8166046c21e4d5d634fe2d2f6a53037c7f6c71b5bc9a864bcd60a2c5f9a309499e3d392d3979bed0f234bdf2cb120acba3490673c1c444eb983fb620c926277a66fed892823cbd1c834b966fbaa0a9079c11eb99f91e9d2f4119b7a0525049a834845f58c75578a7f4e40a1dd10d1bc12decc2397962569e5f6f63bd1139110981fe32e65b5006175dab37d36341c2f2ea99590a6a4d95e85b7e579b0834c16aa0361d65228f2dae175acfb4b83ab0c5354fd3143331d1aad1cf37c59a46e6695a92a7690a1314793ecbd0548d4293f77d228c396ebee7a39b0c2ea68c1b0d1e3cb6b8627996825c123429e999f9b5a5d61697a76a9edf72c4534dd3414e9edff2f2944b9edf326b0172556dc9c9a296a729c613cb73da9ac8638b2ccf8e45d3412d8f2d3f79b6c0f234c53c359682649e1a4d4a728fa7ea2dcf27f268f291602928cf3729691aff9f3f4316038c1c082168ce39e79c734e1a3ef8fd4022c02a34bec67be2c9e32ac7fff980c709731ae2b13649993070ba4023789c309724780cb283c6cb7f2cf0386192f3c13ffe3fd69c193f561d193f569ea9ba81d61e3a25873cf5f2fef409ccfbd3db54c95a8df6d0d48c4681688d324179ff5e25391c7ce4cbc9eecef2fe5196cb356d5355e3639f8bd3358f782afc70e4b819e188b18cd087680ed221fa8406d11e90caa8cf8cc6002910486b2065426db409bd71911af1933f0e7704e77c3a3c620d70dac0599ba76f0873dcf73d4c84013b9b40cf2fa0e70f943906d8d945661228730d5006046d4601bb499635796b9bb6a989ff61f1bf1a7309e98f8b9e6b181bc53acb15702442e39dc403d527945f6514945946791aa7d84b134b8841ae1ec02b2cc1348491af5785c94c980e3e718fac098cc3452caef0235dad186009fa008f9225c15601ee1c83c1ae10547dba420df03861a695774aebcaca9f8cc01178c0caca977ec2563ec43f775c36346343fec47bda063f40b3277b1732251c2930337d97239eb2dfb943f92579735705660e37cb503ad1818e84601bdc154b572fc4a0767c220e9697c15fcc08b3a46ab52a7f33cbd738a5206699241e520757c932942a34afa8847367eacc159cc063d36c20c12612d9e8028f1336064165fa5e8d0c9ec77a06e646a0c1340e6846980df9ccea8e118a90c749eb9ccca8f42a6f7f934ea4bcd1f06dc66a1c2381eb73f314a33114b0e685d51783c34fd25a3b613b3595164fad694ad8e66c090ee4713299b6d2099bb039832a91197efab1c562b29b904f4f75526715c8e24802773892c0a46c859364600c0a159a46d62cea14e08b1178acb1779bcb6253c57155d6759d4c8f8b5e67abca841330c84481270964978b9e99b71efc52048e3d177df29f7feb19953cce5b9d5cb4aad679abf316ab6517f48cbf0d4062222d6a4398372713c7fbae7b51e7813526aab3e99a2d17672bbbad3d555663b19a006e9e3ac606518dd5d8bd2c36951778b5d64a0309586ca1790d4bb34c6cccfa74eb58231c41ba708356555992bed134f2db02f7c08a8f0631e8191424f846aa706caa6aadd51ad127ff0ed3d0c250c051c21546b06c60a3d9f28235c6e3a2cc2693559bdd81a9006661f5c72abba93b2e6ad127ff0a86adc276e8ac2f18ac569c16da880755dcf17db53e0ef07c19c2c8279245492af2d9f57b8c2828f30465e64a8047549ed4a5b52112ef3ee7a9ae3b5cecd0da19aa8bb4d6703a48a4f2e03e51e0392c10bde760cf5d3df3028cbb7abf96f27c0e5317c366c15de0f9f247cc926768c30358fa0ee71e4503d1244dad6756fdfe33f391f5c4785cd4c9af232e9c96919b22923564071d5cfc796a4ae9575048734070b8610136fcf041830c037091ae34617aed0ec776bf73d639a7955e84d7cfc91b98577f0e65e060f93d7fb22411978ec51319b96f2a4bd21d29df815c755b2c5a288e4643f268546f9ac67e0d82562db23371d5bca9ddd893bb230473f20a47eeca50a008223b105a905aa3d813980b45468495241e148551133ff93b7109bd464f48454846442d190c2db986231b10a2969c3deb975af747faa4d55eae137923124a09a7a8acb0b4b8c4f85e4c303128c898f1604843a6868d1be26906470b2c80334415be5338e9c8b1024a85d38b27f124de10df460aa793d8020b366a7c31e00a281d396670a4e02b3f782d237dd26a2fd789bc1109a584535456585a5c627c2f2698181464cc7830a42153c3c60d31f77b298c7aaa72ccec68c15e4eb4801b70001264e5fb77f0144bcbb7cb778cefeffbe5fb657fc77c97be5fbef0370adffddf34be656a7cbf9479cac6f78d6ff11bfc9ef1fd12c853a7c7f1fd9249d3a8f0dde5142c807bfc58f8151e07585b4ce14fa06c3185ef1aef143020927ad07cfa1b2403785621ec144ee14824872f7f470b3c421b33c09816c3f00610fc6ac4e020822c321f901b20a6c112c406380ab1901a20078e749001e9873bd0087f061882e13824cf78f90f8e483d689e11521921c7b160db8f16712d635809f40103a41303d260a2edc08032bcfcf098c0017cb3d80b5813c38748cf07bee0d2b29a18e0aae58501b8803b585632b480a8951d34b0803854503e5640b145fc29387eb0007e7dc2bf02881f937a50d10614c8f509bf0e107fe903a202d216f1a3907ae05cfac6f5257e12a907cda56f5cca01ce80a41e34d7909278f8885982d862c591ebf8234f9168439edf62fd8ed483cae4ba803c63b8bf61047e3706070f64b11f105c593a157c450adb1dba9d1d8ea2a493524a6bfd72adb5d6da1ed670a444aea44d30c9bd04095b1ec52548d0f21371091241795a8f919fe4720d452359fa1ff84be19024f287e0b0f48fc321d8238d5042981a563ee513b0f229610632b012262025032a610a2e55127d4982a10b850c7069c8a1ee771f137e3507b8c318aee1fc8942919c52e1719eea2641e3d29d5ed31ad2affb39c37136f68027bf651a061946ce2d734f1cc2008f1f65d9cc97379df33755ddd39f2fae7ccc1ba68af4f3854c15ca4f14f049a296a0f488489f48bc9609a9c08f9fe59311519e82f1bff43fb1746f885dfcaec0d741cea2a8fccaff50f9957048929b128e62ee5ee587cacf53f72a617b28c3081ce3291f51e07c8f65e4f3df53a59f3f5ff4d47d1470d4c14be0488447c9a3218c0c9de9c7d099863bc2c04ffd51704a0aa843cd387421481d28254f35e9470e65721cae99a2fb5dd8e3287b88237b28c72038b2676abb3e78f4b024ae101d5cdc00de41880b05e9bd341dc15224027ff0d84142380e245204860960f42e240c16578448d3c4d019c654826f04e22023023c3aea083c7f8632f37d5ac0b38490917ad3040715c31ca413b27dd31078070a35c3f1b2f2fcd8d04e10ec565f36996c36ab65db8fe2fc1135ae7ad457167d8ddbb8593373f04209485b1685230fafccbd046fcd5d1069cb5d28e5cb13fa825cad00cbcff1022cc31462d8822f4dc0341c256c0052c2c8aa83035b2c4762698bf5782f4927936c240523030d2ec3d1179884652a3e78ad802a8498683191c3dae2eb0f4121f183974d0d61e47b1078f0b219225958c05a363c7441d8d41fb2aa814d0d93f8f7f320a5007cba0fbc69cb3403816683dacd0658c0666dda9a6492a322ca846ae37c4beb8e1be17ab23f1312e451e6c4e451b6382846d993fde69c03e130f08861be985b6d4e97ad295bad9a8bf23520550d90277f1a2d7a4543c3020b2b59ab4da0860c086ba494b77ef946344dcb2156be5030c0d342da7ac66b562ea2bc3b2f0523304318fbf343182a67795226b61edce254c977b72989714048d945fe7ae897cbe572c997add46fbf84628a46f54ccba03ec9e74490e58fdd857416165d72c1715f2c265f9d831a5096c0f308996a9f20902ac99a27f95c10d804597e4b160ab2fcf981a6e142723d64c96529e7cbf7987c8ec3f8fb384e4a2992d207a692c562f5cbfefefe1a7aa6dd45254d72371c39fcfd6b14a74b97a7e61152942f5b90a55c21c762afd82bf68abd62af19fa0dcb15503dd3bfb312f35a669c3b79ba3739ccb9cb5fde8fee9254c8d3b43fa511decff7d9aca3c851d0d0b0c00208bebca4641bca294209f3724481e9fbcff74cd472743a9d1e93455eaea7317cc0fef254db4c070214568ba689b98cc9fa98809140843cb3c873499e3e459e13e8fe5e6b6d0eb2fc1d008108d77acccaf92ef3d4cdf3fdd682cc7de69cee24cf6ae96492e74bd88f2b4fa993e74b3945d37473d2f7989c727a73c43d9d241e720ab0f31d2515397cb07ccb1991a7e8e79b5e5ea74321fb90608af20416fdc8e599250a65093c256b4ec99a74720710bdf7354ea5c820111baf49338011a06ffda34062cb3acc5cbfca59aea09c657f397754c0a094caa0db57d068b323d9dd36a2c2873cce232d3f27fbcbd0a6b815218f382a05739f52c8cd426e1ae9cd1dc89474b9e8fd6c427251602ae4cc41f69f4c4c215a255f7df2ff94804262bf89b83b9ba1cc48e16c14beca7c9ff9ebc661d7cdf554facc537307529c3ef89942b8380ee3ef5f14a7ab675cc25096c0a3dffc66bdfa0d28709348323cbacb5d5385e264b31a9aa6bf297043310600d534f4e5fb8ddea8dfe8ec2a719fe4fbf7a967fa6d25245a0135d05122535263f96006373c349e211e19904f3a435905a612881cfb29a534e43c176029291de50d546b80f9b75f50a711cf0349b0b5379db4b968e5d49c23d0e931d48e19aa5fe80e5714c83cc9973f5f2f4fd52c7fe64853963f6912c6d271d293a54bf96d2b75dad840f235c39802d01d9a667ecfc8f72c7f88a7645e8680e7ce14451c6ea0a183c7961d79bd64f93e7cb234c2666c201aa402172fafdc7fe3881aab1711383468ca9fd406796af498ec22b7a746c7e1845a8a3cd227b96b114e1660043158d00d9dc512245493435d841c21998dcc13b1d11104f647f5008bbffac0516698f768d0e96982e8d0549d441a5084f91a5f811ebb3093e9f1f7347d7d1f8a79d1cb214fad56289428fe7f5f100d476ec8535d0cdddbba33c3a30b50ee7f79a198af3b1cfe5e44cd157dc171b18572ca0d47ee0b470e7f4b38bec8128ee28a4a38aeeecdc59ee18b2b7c695d1060f92e409e92306067af3f05bcb0b17d7c4ce0e82e0747dc85f2898bfd366481f832a8ff453c89005f89fdaba1e3788acb4de50c7ea081980fdcf8c10f563c58519aefd573df3220cc0fb195d004209a8da8c4a6c613b189593983ba85d6cce5937bbcb4dcdf3da90512a475904282dce572c7919baed297d64bcb5330362cc08f788aeba15da7eca269beaf58bdccc3a64ae6fbefce54ad421970871b0c237ff5446c14e0c2cbfc8f00fc2a002fc31f2ebccccb84435c7822363db47a9a1f6550eea053ee6f0a0cc57899c8fd7d83032e1c87f1f7fd8bd3a5068b81d3330dc2fc9019c283fcf971c22609f8375ee689d8d400617ed478f1c570888daff1446c740081e5b5798abef8fdd7c9e9fbefd07de2aa14be9f2bc2dd4c950adfcfb5381c57e1f87eee08f79aaa99efe7609c8ef3e8f87e4ec6f938f7c3d1a66a85efe76a1c1357b1f0fd5c13ee36552d7c3f17e42921e79e74aca902c0f777379d1157a5bebfc3e95c53a5fa8ff67d6133e569844f83c6cbdb9573c7062ce8c224a3117ebf0b932649573f08d60ea7a8acb0b4b8c4f85e4ce0871f8623cc83214c0ffae083e118f3f43f942f17403b344ffd34200d00b80a9a33c291938142387efd3160f571b1c16a022b1317fb5fc07a73b1ff03ab106859a035025a57ff0a6855409b02da998bfd18b4401d686dd6898bfd2ad006cd537f0ab44dfcd40f00d0d6e6a97f07687ffcd4df026865f3d4cf026879fcd4bf026861f3d48f02ed113ff5ebe8cfd13f03d6a17972e2a77e15c06a9ba7fe14c00ae4a7fe135867f3d42f82b5c74ffd1f6daa48e1f8bdf7d1326a446598eefa786a74a9e5fed6e9bf3b9e32cd8a54d9ad5d26ae2a82b2c404b4dc7f9bb80ac5ea5e620223b9ffc6ba4a5d6a9e1abfff689e829130a9f32e4cfa5d6c2e4dfa5d6e2e4e3cc5d520f77fb2963c72c7c5fee9b11ae3b5e5fe0b8e9795fb27385e2572ff0d7008fec18a1bd06c784021811036326f031cb2c28dcd900e5c9b24309487941c1b9930090cfdd15d432ef69f7ec044b2ecfe0bab01b64d05a9100d72b17f82413564286732e08b15f8db4358c3648c04fdb28b530b8f52f6f21191711c607828608147eca212e3239ea2410a699a53f6df6148d3c0c060730e320ccf1386e39a05d3f081312e7e1d6444880e269544a2d54b88f831326d645f08e54953633bc9d8955d9e82f9d124a3562f04ad5805f07ff9e025e94cd48ac07195eb7b600e328a2376d443dc0b251891d99070f080bdb8810d69e4bd68eca02c1289bc1f8d462ff246a1e8a2f45049ee4b295f061789208548be0c1fb027459ee807f03a92a910197cbc8cd0201ac07c51cda29f4e7dca905779a443cd1e38ce6491f722703c65912702c714b2c8f346f53930c80ae63ecdd51649e2df81b749425ffee7975ec8dd088157c42c4f51fc5e5244cc6d70481ae47147963b320736ed0b237986fb1e7845e03b018bbe732f394150f6bcb2dd300322c4534b70c880839cd2a6a30878bee86b5c34c3bd65510883334bd08f4c806f1c816f982af940a6aa699c03719005904934c9f72f6986194618797ed74b8870922f08040996493491d25129c0338c30b2cc63db240b73c0935becd47830cb27a51457b8bc1c5f60520c28e42fb07c9973086159259d6ea3f16041674e8ecf48ca230590364cd50e7d030f2071b3e9c0182bb0c9f105f69fb94931a080043cf610a987e9258f0ec8d64933bc1460b67414981964a643d8810f43361dd85f38b1a152d4a48055b0c22a07f3540e90a97dc08305ac6bb0fadcb52dae6706633b89225b813f57196bc387e92cc51d1255001f4d534318186cae5fc355f6ffe1a9175c1c025ec9180d33c847e560f92e65400d40e5945286a30e0481d2a7f427a53fe9c8458f420af1079cb5b6732e7477ef6e5b2bf5e958605b7b62b087460d6a26a5b49df69440f05ac676d75a6b7d3ae75bfcd55a29f6276af704e9b44332fdc9e3fa9c554ce7b8a7949278d0c939adb4abd4d24b3beeda3a7dee8cd4452291e845a14824128563c7514ae9a493930e044a2947a90cb9abc3a44b3c5ba7bbec999e9452aaba1c9d5eaad5fbeac0e6d802d3efa7593e9550e03ce7945765bfc3915e6b030d29f07c1a44cc95629b89b10866d311e730c70f96ffe10eff678c3ad8dc4fdf29a534c70d30a507681b4ae988fda98c1b2c69add888ad2f69d737e45ca422d971e1f851120f393f5029a596d24a29ad96da5a69a993dddfb03bfa33385b67d7fd7daf03ddbb900b4759be75ca04a5744896df9daad54afa94524b47e0b5fdb17e05b9907371ce9ea7fe183ee09ee2f798c37dea7062295078fc2c235f288479d5c9d177772abba02ececfde610e2bf09c7376cb29e79cf296e5fc415669109e488ec3736887a9aa61ce6fcd99d9c4888b9d47d488cad203f3d8b439e54c99defd716f6dbdf37279ca26a532ee08af5f360c976d86c1b9e69aec4f697f0484a0f2e823cf9f6110549e00c8bd426e39471672ff4a079be77ba0db5cb39cdfe09c303eecb3903dcf1f69f7cf0e1b00204c0fca62d54c73937a48162b07a402a8640dd7553de910c9000000004000c314000028100a86432291502820d15461f70114800c788c48825c1dcbb33088719442c8186088210000000000048646e300638405be2940a1ee31b8c1c48c19156a1e356979ddcc63dcc5681fd1b5bc1d52a9c504741784ef63f6a08c32d038896d122c56fce36af4f92b59dab03bba317d89ad11438f0efc9a852b450a72069b6ca0bdcaafbb2bb20195f3eb8844ead74ccc15194641e29c7006dd5fe02a0d3b2366eec1b8a6c4fa173ebd61b3eae360dcfd06b2f0bb731f1a6bbb60eafa641cde77c167aab1eabe420a31e6daaad5dd26de00e593752a9ea91387280cfd2db6c5b452b127a2f0c595b335e8944fcba5acb48beea289e30855577e5a09ca7733f881501b714dce4856d34cd191b2151546000345bb2f6bdf00b98de8d04b60f7d37014cd4d20530a1376d3334a392151f5d592e4c29762a81e208f1c4181253490ca9a91688e2037b2b10306ddb91ceeafe2ef336ffbdb4cb850219cb8fa17c4e025f9d888711b618426ff307b4f7eec8a1cf0b19d9d94bec091fd10892ee41eb641956737652786432fad9044b78a0d0157d1ed5a10844e9fe043dfb47dd2c69315120b6807841d9e00400946115a0a057307443dad9d845921378d4b423a6f677672ab4cd3b827de78b950601a93c818025d01ae4e90ccf83134e89670589cc893aa4696345b47dd954042d38bec3a007cce0e63f3a9c6bc7f12623346863bfb3badd6ff1ebc7d63a9614657f2a7bda0d3e6cef9ff466c0882aba412e3fc0486301191a86620a895f56361efb431c1a6bbe69961bd68d622aeccba7d94d17cf067148d0e39fb1ed1dbe65c3e99d74cf74c18c4e80089b327e530f483883de20da09577b7300c325ad3ff50140bad12fdd09a78ec81538b59b0dfc28a8e97f02c5257e5a4cd09f4a9fdbfeaaf4450efc450004c6220b25f8256797bb8d97690b01eb222d0bcc058ac0f5f5cd564853c85dc8b93dd944153be0cf2fdae68ffcb40f2a4a691229d2df47f3a40cb3adb4dfac0d3b7bcbf45d8a107c0a55a12690767b7caac237f10e5763fdbcfb3e63c29f06413be089221da79d5c411fd19d4450f724834e9e8796d7ddd89f90622db88c6b403732f057e610bf4abf750e7755ae1936bb32b042930f5528e638651bdb3b01af1fc0aae345f53a1d92c69f9f6623e90e041e5c23a0ce1b51580270538f00607a87c3bc304cc7b8499709880260cc1859880c2345949db94ffc9c9a0a404d7a04067485b8dd655c3a941125f30ba8b10a88438d98a5365a55e951fcea30a43b69cb9a618901f3ea0d7301e4db6dd265bdd12d7e96ea92cdc16202cea790190ca73dec0deeb11bda0da6638eba7cfa154fc472cb3910c14f3fad56810e0a8158ceb425d2628c5815030df20fd13a6922526e36559ae14f057f969fda6833ef07e65c63ffeb5913097c47f386b3bf877179a9b4353ba9ee0d5bf5d4537fc1b942ed27cdd788d7062258a87e730d57aae28f78c3e158ac040fe50838914dce2fc0613bd7a80035145d6271adb50132a14a8316cb2501f9bb4830a16b062bcc6b1d983e79b6372c6a4f49cbd3e81c76f424b866c61d3c9a5a3bf11e736866b0eaf1c38f98d3af5c031b9f9a40b98d8d3f54408d924512e2626aaf594c4889e8c98e86988302d202297857feaa145d74621342e1db62d15c910bb21578f341cea6d0ca7338fd7c744d860970ea97a1e21b7bed1974facca1e391d21cba5a7cb25e9a1cbadb6bdff0226111f1f191d463c9cf311b8064788c6da7b93ca10e8dae4137a7b6a04fd2826b530d1de995fef7062b2577f8fc240842cad7b09e7a790c6cc8f38d90b7d64f820dd42e471fb23c3bf73dc923431c266486a2ad6696d445bb2c9860780aa4afb7db8e3543832451de844eea124f3dba2c6e79706bfb2f56a602021e09e43ea24e3a286344651267ad7a53f812133dcd4ec504aff9bbdbbf56a8bcf04c508fa8c45679d3c51c1889c51a7d31f14351fa9ea90da1b7fdde5705dcb904e1318057c9453bbc99a577fd102dcfbd661c726c28cd7d6a5f36288c2d99119d6e5a12e59aa911f8caf1d00a11c58c74d46549c36fc59e01178a9b444bbb9637b687f9d7e36afd4f6ab60331b7286c040069a0d82d6e9db4bc107fe1fb3b4915cafa9b855d3b7642a6031d1756c7199c020072f4f9319a15a8423ea93372d0c9e71b1936c0cd98abf661559067a091453e6e83c1bf8cad8ddfed9abc62518e6c37110a01cd67075de4346e795461b3e1575069f7fe1df9d401f83c9587b6dd4db2352dfa1d3e78ec5e48496e94b2aa66c2d36b88593ad3b5dec01a0b3c16e223cd8bfb7496c4967796ffea5e537fe407427fb378f70894300ef0d6f58ce83562570842a0d02ca3fdfa168eed06ce7e9c3be06ea8aabc0f57dd70f761fa199134210fa95832ab3b2ceeaac1fc88d6d874916ed3b332c34717c9494be84a1061b454bdc74858e5f4f4086b3fb7d35db8dcf80ae4c4e7c236f9a0fa3491a0b060a05bae67d52c764b856a0491714f07c981956f26c4447cdd6c1b00139d927fb77bb774e14dbde5bf0c24a01c020a0aa1158b05c6a71a9e624ea46e213578cd34fa702f31a8b35a8dee879cb17b5acc0e1168f5d237d3ddd3ea59524ee5910628bf8f83cf54674a50663d58f8b3a5785876c61c9a8cbe4fc2a9c24bfb63a626b59644bc134bc851980108d70ae05f23237f70ed3f03a452a77ab00006a86c6f76f1a593471f3a88b981759d9681f127dc92c3b33db01ea0feacc7f9be296fd4a212ae68a9a4f095f90486d1d11949b046ed98069757c7f36bff6ee3ba19fb380d714c05a0b8f3c66482f30b272456c51c39de1286b043d501c16b42283f16a7d29246b093ba98ae78a146ace4bce173a91222a3990245c62cc00ac82185bd979239aa81c0ea36a3b32147ee0deb7aa26b657e7296da922ffc0661c26c1d1f334006e486ca50e9585297fa9937a3e5ba51d11ea29de1d009c0218f2505640d28ef389973b5d22cb47bf3a543ad650c7c0689c9ba79cd52e6b09c8e23300e879ddca61e142dd51bd15aad68199719bf233859fc40391d38de5d41a07ed891009382312633e77c83a2437c6ba1da2ac17871ac1b7215ae6fe85d2f2d18013db928131fc77892f9ba726599859d4c0ce128a93b01cc0f5300d112b122e375d6d9d228dd45988f763e38a4ffb7291e0e85e5700a6351c825253c4da021ebeba5433a0080d21c85b41e4d65d150b2f8a952bdb1959b2818ed9286c8a9743e88eb40250282bc4b49456b8619a570c9826553b78efde736e9fb69418ccce5529df63079f4d1c4c796868545a699150ece5e92da9c4c47f63a3e741828fb0d8edd2748b205b36bf0bb580b370ce93e69499210adabf04b37447490890fc47f0053668536642c80f9b9a07e01488ab9acc8fc6ab817da9acc1beb36bc3a84e31d2fcb48a0dae3956255566ef3e8426fa3002623e1f3f69a1bd34ff4537be2d7aa218a5f71c19c74342ba8eb76aa5525353803c849ffe823df93ae352d8c29c15e687f9e280a47cf442e4de46e0f036f01f3f81d688a67dbcc6777f1cdfdebbe9436504a49c3c500ea01430a10c5e5cbf4568e2d7e27166188477a0f7f334a4356c78d702ef09da87253018fa140b83c6e02110a44569d786a605138019d55db4b315335aab8005fab427b4e27207f8179fd7cfd90db03f1cd6b74fd8369b05b2a48857a3c5ed99011f957a6b9ca35803151587b07ad80c338ab9a496b9a833d022432e3be890bd4e6de2117bf61426d5d8bc2ce97d7e4fbd5945e2318657f7ed3cfcc254348b0d2b75abbb55f5f3f79062b88739b90dbf24eccab32997e8167785f161fe51dc58240b83ddad9880662ff2f4e5e7e4ff8e2c3c23694a42603fa5e0764c3f8ef894abce88eaaeb25c09a606d6191db39fd320f7d3f3a2be3162f83beaf24641144a0a539187d63e727bbdb607fdf2f2485dfb4317d0b6b60bd41a335a9c824c348ad8443865ee2c47b698ae18054d9da01bc5194fa81d5f0d705df2d679a718315078ee7761229c7e48710554b360eaed716e019f1b36035153060dc84957eb775004333f4561fe48d9630c0ba0bd099ea1e7a9ed29fb736bfaff4c76cda00bb5ba5f6ad4e208f35f30212ea5d475937d3bcf9c40cd570331755c05666f28b7cf774115bce403ad6b1c9ee684b5b870ef4755b0d3152253c66942ac711c3b3a1edb1cec4106d0c7ac654b00b8ba4f7450e8ca4c07f027695b82954e9460c1b9d3326c234ba592c2d131347e603fccc3f296e491c570f61c3a757c22ffe8e827ce88c767e4b0ad7a74c0c67bccb64639a70ad241655473bb78dff6180846fd9fd3c7828612591ae18f0ae501a9009ae227ef1b1a07ccc8ae3bc8dd26f5294f6fd8514c8a0e832056ea4a81eb69b0a0ddf30fdc6468139906c5923e3063aa14f42056271d02bc914c22c1871771b409783d89404dab2694b9fa7019b27d30dc7db39d827edc1e04ea5dbef17117b59e857230b2741aceedaf4d6e9aa2743ea71fe066ab07aee5c011388d651126c2627ac3aa38e3755cbf0614e5ce437812684cbf7f95504dc1568fefe15a2fd08628247c26e714ad90c529b4bfe0f12b22ba34e7ceb2eaa0eacceb0a9348e7501c7b2ed10815a5cce2eeee69607f50f048d7a50a470fd5571790d6aadfd30d3132335fac27774b2b0173d39e998f4b67958ab0502f6909032a33e2f9d1a935206d07397427909b21467312cee66cf4d7d9b7b1e5b96b825064b30aa890c6c0cb77dd3f1644b8a9cb6d1630ab6aebf6b06b13cad46d5e80d8ed85f077ba0ee7eb94cfbbc45b9c773ebf7d1f4db1267f37158c8ddb1e5e7c4785d07a180da99af34382288436bd29bdb48ca4caadc7847997cc59a32f11bf5e235102a4c251be5a36b925216e1155378626327255498df14807f7e432772d1d724173c87932c20388d3536e5539bb02f2b06a4aa9ceba754bbc4091e3c5b89791b1fe18611a358b50714a7eec37fe08c9237ac439896429e9af4e97707a594b6170a9527fd3b815b30658a6f05e257ea695739eb36608b140dabe8fb3df203a00d286b2aa174aa24ba22e6d0a071a232d8e9eeb1c461437e649b9b43f7287da09e8d3d9451608e7261614ba0fecf512f08936023af31dd7c5406079a7bf29c60203af6b85201d84723b782e603667571aa67123ce3bec25a8dcc98b99e54b604e9bd959510ac6004ba4b814a32557e19aabee14f9413707093d1498e7c53b64ba9ee306467e4a4c2eac5177b56486a7549fcd2971296aafb5a4ffc5db1fdad010b183dc855403559bd71ffaa205c15a0363bd35966747bef2aad4924425030e9046df31b738f567f1f3038c8ee75828e8c77251f014794e80f2227f9e256261a113ac6e597bab14e72f7ce217e3dd7c423d5efbeb80444e9416a7c0f2a08aae0526324d14cf7c65adc7db0764a1be785c3151f2031626440e8bb1a31b15ee9cca466085a88ae083f0429c291ff4dc46519f84714427c009e76af5fc6bf47005f50efcc2eac0ead918e75931472f4c11d402ae909d54a1ad713be532f8851b340c26976c9be1f60c2d26926cbf22f79eb94594219f44b4bc2fd26308d6608176422c5268185137391e3e0515db9e9227d3517907338abb42ed551480db5d04eb6cae7932e6b5f93a00b07640b9abb421b2d2626cea30549579433816d7814790f40d5a12d9609db1be8224b2073aedcef986c0f5a039dade364e4acb4c08d484484d8eb531bfbfa7f0113d1e7f18fa5a345e6358183c08511a8a1ad6068a8da10759607c75b8617c3a1630c25579292487b19420e92c744c4032f9e8a0e3e0b608a257520ed574a03a7e07b1205c7109186cca8dc3f4b3be8279da30b51308a9a003c976bb916a48b2f8f9e6ea1cdd88504c7418c8f00ff4ad0678acdd15e39a9bc8e5b68fcca2da478771a94d207051d25dcffee2afaa87d958db50c3ba1b645ad64feba53c79f40db6dc6ce42a435ebda309b37aef0878e65bec9130c8a6d63278a3d0105da19400942ee669054c003cb22cb76c1227c252bb89f14552b6caa3087e3ce24ed578b4410abbbeb1f546c7fb711a4484f286f0b5bbb36efec503b85737832fdb6eaffe361f208df26d951c0f706350dd90baf77b426eabdb8f1e9d26580b08548d0e58bf9b106fdaccdbd5aaf6ae6a084bdbd13c35a42cc9d613f867425ff7c99b1aca2183486e4f8d3b362d7cad2811ab0de5f473d48c1b700ed76401ed5e628fcaf7e02147a8a53f5f2b9121e3df84dec01e942ddecaab18cd181f160963e8d593c6b6300e43dd5d188a2779fc18302750594c01b3c26e4beb3da3986a8db35c5cb932726497eb93479c2ae29e41831f888d36e738346ddc548b8e5e6d540aa587b2c2f43fcff2b421924eeeb2766cdbe37117396e528613ba17b7cd5956a7f14195cf0e7634580a7bec350d5ef8967bdb8b8970cc75db6a3844988079a5a8122bb5c6d81b74921f6c67217ab6134c4f4d377da58bda1839c97de3935dc999ef67c28d48fcc9c174eab0a17867ae5ed0e92e618b81007f02a13a2aab76daa48466a216809a6f3b4d3bb2c85ff02107c4b5c102589b2457cc8a9598ad73611d836a64446700e47b8cf9301e710dfb6dfb1c6584dd0b558792f19b002ed8e3680d6bbfa66f863d05deabb98880f28d06d054fb46fff6753abfd2703d2331a9ff39adb2b8523124179515f2120fcf17222d405d62240ccff2310f46c4a68779862e46862cadb069a8c726fe7dd3a27348e281f2975dd4fb721c3b698368512d26a6c1fa7b4d197ed103dbf0332f79c327ac3b808f89350282db7b511d3f46ed5bf4a42cdd1bdbd43dacda11dadc2f6b946fdc4f68fc67f0a22901cba66388707fcc38c4553c4a607e53f610886e9358d69ad1bdba985a39ebc1e37cfa7e8b309d695364be425d4b305637599b705b4039e865eb4b7fe62c4f381b32ff0db64725455ee1bb21231ba060046cf685b4f5ea3b7d10c22d629ab8859d29e293b560aa2144b150d1e484f009a09319fefa53d057f38e3e3ad64441dd4b4d6ec9b3d2d6d298e4f858d88ebb46ca320564b569d0bc1b4c01a553f226611c1335133b84a41adbcae70ebfb237b6b87df042cf1cf0e3d2dd38c345b6f36fb32f383368e855b3addfb6c42ea00a0efb3df0e98b54bfef1ce1cf50c0528552013d31e116fdee9c6016b24e8f9b9cd2474a1839f7556ed911d956e0de183f096d5cc67ffaa3077f34771b179bd6d2efa3687fc7ab42c534f801f5a340985a44b9648c7eff11bf4b154563b7e019c845a9a11cfaf4ecfd7b569b098e4e14c4185b3291a09308a96080cd0d46a2ede6342672f6c45d1127b7390aaa943c681acb84e4e330471c9910cfbbf957dde3ce3694386aebbbe98cab83f7d5cfca5d2c9312ffa31a51cce4173acd6a9e0f09aafd36771bbcbd2d74472d90997bfa47e3da32d7c7cadd26d312fcc724e2c232f37e38a39bdc652cab46f7555765fb508f7e0393d233d51527afcbc0e6fd0da198167c76bdfb82dcb40aede9362f449f06e14337ea9d2ab08ba0f33a37790cff8b9db51b1ffac1b8b2ccf49172b34cd907316ec31fd75681e90d15a6a0120ede08552848a14a0218a42055fa463fdc6bba6f060b04a529c8187c3e02100a6b461f381e2fef625e06a92d40fdda7372fb4bc0b41835afe9942b624ea7a442c5c5659c5355cab5b1a11697b751f8ee5763d374f2b5d0bf57144b08107f5185ba9cbaa0c3ce2d4d7ab52a082bb18017ea25330045270da1f5cdbc11625854e51301fe4a9f455b829a64552888080cf3c975a6ba342168ea8c3c77428c02afd71df9da7eff37484c5f3b8a4a27c8f4ab85a02322c215976dcaf6ba13c92d00ed022d8afc3ae415a23bce67e139c8a1059aff28afa3a03eac90457780434615e0f9571c7ce95d21026d7ea1533a4e427305d293e68c2996b806605e806cc5a6970a6d5e0f34626c1a9dc9b2195f748e659537d50017d122fb200552bbfe8e643a9d7808b094052f022c41d52e4ece91c24a9f8f3ac48966b1404d2fb21c10d4c0cdb530c1a52c08060086caf8341b8ca195b2b45beb03aa927e2337a87559f51955c13b01f9949e90ef3fe781fded90813a34740a2ad7402a4658efc4e6a2230e991a5a7fb8dc892d12b4cf1e211007ddcb1f0b7bec10776b61e864210acdaaa8c513c94c132b3c1e12b3f7a43fc0008eb3435880a714893a47e9003519103e475180b3a48a649f6c14d1b497892a2cf5b29289258f363a41eacb8a109a7bb34885d23ea36c1a5a12a8f71268cfa048bdf44f47ab92a350a0ad2a587f0c570252c29856ccae89d334c2a09ce628ba5ed16093d8e72a07f55a5fe67c04589727b7425910d3a28a4bbdac4442a9a71d4d20f9672308e6bd2c4ab1accf90262a7b35918918611f945285ca590c4bbf9a26a4fcace788a862dc7d90a228c507d3a020611a0415b8a8b087bbbf2c96777819931c77ef093e1e22f75f1a54fa24e5585446a3ce2bc5aab84b2b23422bf07b00d243c90d0185ab899145e421a529dd51c23605e614748b72679886f7fffcc102c4fcda1a38f4cabb52ff34930e247d0969956e0375ce70ac0256c04d7cbe2d5a0720b0e15c1727c0c65fce7b367a48f8e92928fc6b693620fec84f1817cac87f2d1c1a8cb2777bf329e4d16603a965b725c1b0b4b5c40f54a6cc24b3ae7f24db8562aaf407b7aef81268900004ee353194bcac075487d85f0c079ceb607bce9f8cd23ffce599f0493494d1162708f0b40fafc1e5ced662256810530b74d9d14e7f5c5a5a9278e97a016a73db12b038da1a5fb3a1ed497459bd129b6e973e50a74e165b6ee64bda9ad45356f1ea813c63e2a3366613452962efc3c512e8f3715e1db324ccfd425980baac216fdf56642c8b4d27d0d41d680b88eecbba15ce208d418077074e82fc924eb7a18dd8308caaa4800340558c299845836ac0b3670257da1cb96e1c5a2cf16ac3ddafb16280ad0c40a6c9d05ef982b48289f3411209b280fd73c44a0b102265b987bd8a684bdbb4e6a26772106b3be3f644c6890dbadb9c45c85f24842222f451b570fa870a0af92daff7344a7c773af4db4538a03a2dbe26772652a914c4bc1dbbddd63dcd6e921fcb704488665205d4ef9005ff40cfac60026f3407bc672d356502a55d53fabd7036a9a154e69c52d8a5ad4b81d3a07652861db033dedb81cb74a3f675e0ee49f9298a20a55d2d332526d2541c740122b7710e162d490f8561db7a5bb6566990932292b5f6cc08eb92a11992c682aa037aef631d1870a9552c6e4bce9604aa410cbd3df58d5bf65308e5d25bee97737b7eafcae301e3e464bba3461eb0d2646992d96da3af6386186b79f33148274b279cb71b1bc55f207ce0ad1c2e8ff52bedddfcb01591fa414eb94d67b16140708aebe2fbec07195ece25a891d23b32f550901b367cca9a861aa374fc8fb8b80a8fa1a30dc5196e37dfe5e0bf91f39902cbab60db767420cdc040188c30bcd31c20d3e9bb3036a5c05d08a4f11fca3862be708e15113b82d52e6998fbf087196e79c4967ad97be2b8131cce5724559751c29d2ba0d744de3a597e25f7827b5b537135a7830ab4e9b910a848b1d1ff573b5aa371c4206c1e0e47a9784117cd790e0dcfe5c0db803e270bd199af3be5b73c61b7c041376cf4c67377a870231e3e7baeaca9fee0d85424af057623bf0084154c8f22c84d89d7607a1a8acca27b6252c6c5deed8ceedb5d5d4a684da4c24f7af40d772ce9be1472bc12754bc5915a016cf002020769e1d7d09b4e9bf8650b3a3158760e7da4b90d8b0195d8f383d3fbfd1940512e410f212221cb4bd8d024a2010c9405b78b7f73c202c872c8d32c1863614707741d27525d630da503ced56b591a7c6ef2c3a2a5b49a8686fa712df0dbda603f4807c9a997df102638c0d7a5daabbc6e6da0b36ed9d95059994509656b0109b109b3ac2b57c178ce3e2c034203964c697607f110f070b54e4a9a557b192078739a5a25ed7c5aa490c7439590c3242e0de1a24b01c1836c89d3cb2746dfb2020ac7fa5eaed6150efddd537591a01b9b0619c019d4fbabf09e4f4ad376b657052e3e6fd0695176b5c45d0be0ea67ddd01c2b087fe497737852fb88face464bc29a56914b1e7def6b7a828752fba244749cfa2ca4fd65348acef86215491cddca9858b44119e84949652512cb8c6dc4f4dd140378ce25863b83f1c9f3aaca6ac06cd8d1640bacd371416b1bee971d96649c509a4d8a3a999d29c989ba173953fd6f6176567f3137a3610b88b80db0b0d909e3caae405e5613be0d8ea3d3a5cc67aed517a37d8fd70b3a0204223970b1fead6b41830c556e4a9a1166a18eddeade8971beed0a08a9efbc753c323dbc9f596068960670257f95a30413474574a5f702df5683d7ecfa03e88b44395b14ed04488c205b7d7e245b1f38cd5aaae711bce435311ff08750c709e2ab0367846db3e2f0acae30d1720c4a71612146e225ac0de8b26f898c784095387695daa45911041e67f0869259afaf227e98599c382bd45b8018ef97442154d456f8d171f84b132dc99899cb85ba865356a36ba1c7c8f89783494b012c2a77812006c2342dedd1187c92c6d2ad46aca13d5271b234727f82893b6ae30bbcac1e7d95c01f727102f345056c5084cc6a09d1f8b4c492486f32549519c0fd6e1b21432649fe5c2701c6ecf1f72259239087f8d74e18571adc562393ffdd215510ff9564c2bda7cbc41796c5f728de86bed70feb7ac2992142219b72de49dbfa7899c81fb819ba1db30c16a61b897a29578b7cd41d389442c854b99e215cac9b8c812bd605459befd8b9eaf1772def3be835524b7d29a6758cfe0e8b1dabe2d0cb7c6239273df64b2fdc183707f9c154992c6b20c2080d0e7186c868041f71b1aff13a4783ab47db5db976b4bc4c1c4ed9ada04efb300fc9a021f690ea73d59794685b2cf7edeb6cc64745536450a7fc56747c991550a876d7c858bb31318dad14cee42ff9a826ba0ad92d9db4ff3a46c2ce53db00a45c01c93917750e54030d701b7613c5a7802bf993b716360b8447646a805384e05624214e2ea61d452e027d8c47cadb5efcf6b551b29c4e61490f2468e5683b5c3522f4465b2fe6639e7abfb0cc6a6789b9dff84b20d20cef687001d5194da45e5f8607ff9a31b70c17cc9bedfd6e5cbdf9230892cd6adc9d402198c25d503be2fdadcdcaed5c78fd4adce8b794f9004f3cfad7d7179e3f2ee938e831710004671aa62dea5dc8fc5124c18107413d31b5e28befb9388f532d435369cd1b5043465c388c33ead692e205b5c4c523e5c48efb8dfdf510aafeea96049b839a5a291c4301df5efac8895d2ec3a0d452b84f685d5a207d0c78638d2302296b005e64a101cca57db8ea9bfcdc1fcf036a73c5113142c64266e3d1fd760e30d27fccb763c958eb963df5271327327d6abb867a4a372fc5ff272a6c7dd6f20a205a3c5e06a85af30915ac22aad03b6495511a22f3c5286f16d6b79239e2433aa0e3d824b443bcf42f08606d8bf6194005ff1c37cc0275826e8019bc3a752a99cd6931d15c4ac69069a383fc4bcb34a431666092646960d17201432e94e1c9c760b43853fff61d3f86ef6548070bf8f3751b5e6b1d0984b1ae13fcf67708208a0dc8b7e2135dbd0010d4975f9a309307ac901fa475efeeddf4fd381debb656883e97515e21fa395aec1637fb1b4ce20841d6ade28863da672bbd15a5cef9e8b2504a7aae939e96abd80aefea2124dac15c255be7fd430946f7b5c375913f8574b721fb6fe415700e335e1112a74a25a5036882a97cdc12970d359a3436a5bef7f924c32dc50703aaa5d1a5fccebe8d07a0b7b46f09455dea72af2122d692c13faf2062a577bddc72b36d360750f141fc7aa709185142fdf7d06dca39a5497313a758d3aa84f6db12bb83c9c60d6a33a85c80e04dc0804125499db9c1e10e2b9ca46716c2087c2bdfa3a04aa739954480da2095d7b6b8e4dedec9a01ee1aa06a89faff6395cf58675498ba7a0e85c292399da259614090916a6761f04c52f0507a59d605ba54b05cc19bbb42e8ccf9bd0162958d941f10f295a92c3942d9ac989a4577247969736383429a4a0c2ce15993559e4c32fdecca60f96cde775b3a83bf43233e7ebe6d2f896b11426370718815e90b3b4fb386ac2749b70ffe53ddaea4214d34708574efbfd411d3391d8fe64d6c551ae474e9326c6af10fd9138a1cb35193ebc8622570e41990067950f0de4556044ce9b1c4d90ac04cad799d204558654ed1a57d33496ac2d45388f3916cb2bcde8d243c806be53aa5f35fe57f485822256ff122b0912c2c824483884192ccdb9ed9b058788f7dcfd1bdfdf9b63dda3899bcc3f01ee3b25503b2685da0bf7e4dd82eaa93bd5da08a34fe033e4a47a826bb0bb47263a6ffe48c56b21836da64ac9ccbafce7a6b0bbee1ffb1457c7c4d9a66254aa99fd76425c42805c327ce6f76925e8f5d1e7ac41989e9a6a80311b5c9673a65cb10bc6339491939406ed004a49164adb56c0d0533cdcac03b08d849a8be35f178358576c3c8d40bbc412a66b65caee2ef7f9330f135e149f42ff1af50bb89aa38a51040049d02a933932d1d7ee19c821dda844c79ef6a10855144f5193102709f651d8756f9bf21f267d5d730ad73ff3a3cad5be0629e04b815f8baa4b132043c3425cdf7db6aab9df61edabe48f17f53a27b4b5d00b3e5fae524021e63a56531bc4d1384d2840b896688095d517895886a6dc0c4dbc718fe830f31cf8437fe9a63426a9d3c4a081ce3bc63aaf57884f7642135ba40de1c7ead84ae262a5e708f61d826c6b090006853b3c79c48b4f0dcddcb129547dc78430225b1206d2264dcc4d14eef59e927f6ffda3baa912d631720a47001c0f8469f27205e4252c41778e8836b6102352a7e012a19033179be720e88bb6044a0e416e46447e74c31131aa7fe58ca1507b3407723a6a3ee57f349d24c30199c6d11da60b7c34448eee3be9747ac74e53a13ea386e504cf2aa4b32252c6675a4941cf33fb1c6376d50c3943619293eda0d7cce543a6275b3367d6c2c6a42b364e363a121dd998df19e470e02b725fb430b04d8742ed2e6ded5054ebcca810153b08c6f33fda508884e52e98df56c7d63cab0b142a5b527c339cf5d15330d2eb58a5c034440d4d47d9cde98d08fc60cdadf6e40898150a28c90021d0adbe9be394cbcb34234b751a105bb8c766d9de2d7ee8df06c0e455dd1f4b0d5fbe88ff13654b3e235116c730fac7d92bf85f8422198e348230690abb33ffbb7d5d0dfd2371cdf78861767c6bcc9665b59e8861209d96e240a2ac9d0b7e4d2d9cbb7ce9d62c0768889d9ac47cb24fbf4a13d5087d9df2fe1aebf65511eae29c8ca18f050853af294fef6d07042750cf158f73f3885e9397bf3e219abb61376506e733a77378e4de8b531696b941f43317eb362b8d13844c6337eaa31c2bc1a0fa7d998335c919925ac4db95b836a1006bba453422c341c9d4194450d2a593557e0d554ae7258df17bb55b9c762a3136a329cac2244e48e66e47f35636965c2ded5480a0f258ba3bc1bd6c8c260c88b8211b0502fef1cb61d1a04a845141c82e5802d08bc67bd45b5bc2795fc63b764bb8731ba0a018eff70eb5ef20118264afd64728f611df8517dfe322738e820fb655eccbea4c470a839922cf2913783e8eb2d0c1709b2e40d79191272f7fef02e2f113ac0f888bf03f8e30355d6894aca48cba0ff4972e105a2f5dd8e19fc025c54d2f97d3656a0c5086fed3ab8cc8e1af0e669fecd1afa00124745fc931259f22bedeabf5602848fe7142c7f064813d162f3e1f02c7c992939d4f6ee0f30a593b554b765d58bf8919594b0e4dd82579db2624a0e2e6b8012d414a1052a0bab973fb8b84911fc6d13a9a6db871fd9c7f3622ab811d343bb0628205191e013b3a40915b0c7e871a720cf53a6082d93e4d8810ec5bd8b115ba4e245038f6204f1455a2dba173809a5a34f5bd2db5a0f451d309211588409c45fee7426dedaa2b98c8994bf984a8f663973b40b5d94feb4007670b21c5a40932349b300df0db3c697c31774119ea29d665ec31157d6a5847f1618e4171014046efc2ec5caaa2a4004bcfebe73d7428d2d888691e244de2df5aed7a5da82cd10019494b53ee8ab7d48a60b83a8661208afc80960e2d6e9141d6a9b4fac460fbb5cc182ad40f2e90bdae1ebbb05df3c15412ef56730c401ee38b5a6c8b6ae6aafcd7f9aef4f3e370e6d5f0d294a4810608f5c41e3c557fb735ad7a532e588c4ff04f24648850d9eb14da2aa2f836faa0bd5c479cb45a1f9e22d66a8231fd8e216f4a83a039eb0195d26b90bc338335d9ec56719066770bbc5f9526f33f5ac6029a1491ff1c64cf18a8a2609e881780562ff622f2b7e89665a9e4aa2f087ada6f59fe7aa0bc3b731cb2f6c386b352c94621b9781c016826941d0b0931d4a3b70f5be96e68675aee91d32c6b9aaec5dafd2386f4f8ebf3fca456401cbc13c4e3bc840b750483fbd6065a512fa20f1b09fba8724c3b7b012d41bd43c5ba54e1235842b0d76ca822ab66f8d3edc99507809b2e8ceb465e179ca46104fde0e28453765864202b735166cde58255905c03ba5687388818cf76c5c8fbf31251cd2e67242967d9b35e20f09289c0550f956d4e21f523fc2e109623c71e6fb0318290e100bf0316e348e0bdf4149bd8a277a661122cac82706df9b1a7cd8e9d84efd54d6e4654c1f8636a67d6b178e0c06642a8a511db4d460ec89b76de62a5b914d75f85c7819ac1a466be6a480516bd844400147b701f93ea17bb627a2e3f42a7bfe7ea76c7f5463691d901003ec0f4a5f82774aa2e0b87e23531d5defd209a0fc155816dcd41995d0cf0d982719b1088898c42c52a2a9c118994790583290595a01c40cbb4f5c0d9437dbf22f7a61d8a22306a8a072950d3feb3128792811dc31e27d8854e7dd54d02479553163b6fea32851a9626626b55aaed8abd4e5f084269a8d228384099dc038d8cb024f890a4c54416512591f9c6808c682258def30352f7a889365c8d161269d3f9791df0494c345eff4d0d3abc20d1ef52476afc682f5839cf6386a81daaf6471d57a79525dc08b03fe82c7e3e7f957687f0bb2c5becf185b6e50d0b9dcbbca303124111e590779d43c8cb046211fcadf5f7957e2bb6ac8444c61da195d8aaa37bf916fb5f02a4b1ac0556f0af078104830e66de1d06c5e08c792c60eae2ae88c9901d8ed170fe3778a038ce11f68d11a9d1428bbed2180aa73a5755444231358055d9cd006572f31cb1d9be329046e38d28b0cfb7390a2c5bebe60fff4ed61e39d584e0ce8cff9dcaf711b67ddd95c0083440ed25da74cbe51a427799c48e90f45924a18e9df4f0bcd0a0d71b789268a84f9e8f5fd0010fe3a68a8ec303c40d7bd5d13906d460de30d49f01a670eba30a5578db870762bf6752febb1625a02f0c15615ad828d33ffc93160658a08a17ebc67fe54318bd48ce5e77d9801b8c525f734d765eee897f08992d8703695d9783e5a24f4bef45fe53a81a9da347fb1dc7093ecea6b4ba01093e228db4d0daf0a851cdf4d92d23f34fddf6bfdd07657910c39fc357d8593c2941542b8ef6b64bf62863b32897c9df26a35cbb51a5e1d517249d683a91c9b60150f2b2890573598669cc614e83b93460c26e3f001598416c56a9baf359758514e52eafab3710b513133872c18f148961179c28155df3bf68140d3d0a501d38df95e93a157353bda84dd9604810d1a09f5a7c5a558a46457a4119785be9f9e180da843747266bf46b199d97bbff93945ea2b3bd6a4465a073a8026b3ddd703ec7de066753e9d22a0889924cf8916c88ebcce12550501ffac0cec2ec8c012dc05546075cba05ef79116023df3185e9e76d15318519461b7a42ac5766c84942f47c9aff97905019a175268273fe80bd64b21417f48b6492f6a0240c8ede951193cedee0e9c21a79ad2f066b25e2317655e812082173bd572010c884a481a7904e11cdd1f6dc821890dd963facacff07efce5ced67dd25a54ab43808d0bcc493ad41d0bd1105dc6d785d4b7b6aa40ca16cd9ac78814a52e711554aa078ec078ccd9980bd84a1c2fe01442dce046412ba020443112bbd67f5ed6063f3935df7c8ea24d746bcee3ed8433893fbc2f6088100725d75f8365aff40cfa8a24e0dafe5ccfb1df186a922fc305c059c34a24739bf00b8000bbd706a8ce9b56a83655a5a50459a872a20b2e9c86bf795f46143a9a5a5cfd40b4c90fc02af7be1c05d1f0e6c714439e3f5a1b193c1b7ae0f065f3155bcf0819170a26e44c37c67d2875f2f9eeccc1ccee68ae468b4ed47743c36580492ec0b0b273fc8e6cc339ec2f2ead20bdcd68aa9d33c44f1beacbe661956546e7ff97690e3824cac5f0d0e61acfc90c3f0f35c3a387bca9558c9e94fb05709a528647790454226a5ec65c7a6c7a50d6bf204af0ce463b5198784a580fffd1f20eed9035008625519198733c2b7709560b1dd967a8211280bde8e83ca7b1a2f298699049485594dda878da701d0787cd788494338d7b040b30a41c6571750ae4b0bae959b011462d54eeec3d70abaa5bb5d69613fdb6451322b84f73e818a14d8e90af632ffc7d55295e594e6daf98592f437e1d981f035dc76593eb904d357582bd585e866127d27c4a1ba82be8fd9603dfb53e40a0e026f2d8b6a44126d972bfc9f823e2ef0285f6390e19e36c60e571d1b5c29f7e154413d31ec7a32f7001c95a5b9e280a78250ed71f6cd0024263029b93e27594fa4f8a6589a9a518a35c13e15f3432d9e2d023b1899f2ebbfc09158613db7bfa4805de1c616c61331fe4088616db28fcfcba31055854d120f2c192290846cf6198826f01577f90c8672fea01d6f43af22b7941d98da7f4dd4816e6156af7b2f171bcebec41b2ca10508dfd8802a48aca93f120722b08c0a5f09a9e75ee2dd9dba2cd0c84251f231661540afa64c0415f6c4a4e1c85ebc7f1a2dab020561cf44250b45416331e07e57860a3023f6943f7087726b6e9bb9c44ef243af021f035fc0851d74f677bea4163167be986372ec7d09d50a94666ee08f8715cb15776abb57ac507b265273ffa858c7bbd467feae79ac6d97640b1235d10dbca9e2145abd2b1564c1da17f382654bda569f731b93b8c4029233e4424a936acc797c39032098a12d0250bdcd2c962358671a0190cad2bb69f564f5d357cbd1864ee3c51e33d4e5d2860e560c3b8a4864b81792c388b2a0808c330c0ac089d0f2f916959b32657d2db17698f01efc57bc0abd69992736a0a9e980557d365a5ebad73b08c3469b0e95708c0f33d6801e5787c6546cdb156bfbe78f0ddc32e10e061480c0241b2ab4c16b74f51b7fd609ed90bd5b80d9e85751036d44545974917f59f604a7ea2c6c39a5058c30a25248cc4c6ca905e2c4b684df4d0564a89e3a37256a73969170875b6fcb1a4f3e53a3dc2550cf1f7529f897d2632e528e10915ef78f5345b4f686881e4ba1c0dee5bb3b87213aaedae21f8a3de02c49a504d1bde2be54c153166c3541a0910ce9ff102e145b2d9e5296640fb3a784798da00070be69a113800f1dc2892964e5df46c7c8e09bdca66074a0bf99b7ceed49c850a81b428254218e79d342bafe40487d21056aa556955a5901329d354b92406d5c0b8469f523ffe66ad0e17a0e2084bbaec6d6a0cedcc963e4d5e66622a74f30a94a15e8e1a0f78b44097deeaebb3faf86e3eca131856029aee07c1b9d31c6c076343c3994766eaa4739ac68bc21f942846315700d1688df26b3c9a68ec301ca001ba56d05f401c9a859fd42aa0da1400cb9056d9d60aee4384dc0fc69e49e9f8d5ce09f57d6a0fa893a7a783059de0cb3ca7294ec6a55cf30ece969ca765435ea6770fa87ded971125abe15234bca4c0389d0824adc8d627b5bf4072b6fb08c1005d4acd946c0f315bb49dbc34922ae1fb741ad024e6a17146a0aeb04a0aa2223072d218081ae14c8b64ac9d8f122c4455475b8bd224c8bfe2d2bfd022436863a01824947fc6f1d8abf1c64e8bef2e469cea7e05012780c9515fb6e4e50c7c7bb7193066741d4cc13414d07a7af4f6e546697129f3fb6b48b34066d1a709ff2d9a6dd66956d38dee648b903bc884f0397141ab41a8d4d10ee02bf4b89bfe263c6115a65fdf19e822ee9ff0f1aacb2bc190330104a425873b7434f4356d372aff070630c3788da38837cc84db9236e54f4f092b3192492678d4e3dbf677ce46bf73777b1dc684dca9dce69a4e91973e483f74750750cfb06dc65e142a6e33ab1636481d87b60f068c0ac5481ab2fde8c051b1e4034e05044813ae9f4ad1b6ef240a80a6dee611462c47c2a0277c8726dc65abbb95c0bd88c43ba10a86605783620b58cb04fc4d1cc740a398415e6de4f4d5b6dd375fdc85d29efe35af4fba5d59e61a8d4176ef3e48aeb71a6de21ed14e9d0bf40d7a519becf145edcee28a3bcb0ffec0dc3607844db7e83d86aa15b8174c4e2aeede525b6864446a1a50f24285c77efd9e6e8b3e5ba78003fe44003caf2b5d1e2222a565884ddb616e2e2df0dc24cf21fcfb2d2152c0c621399d99d1052716beb51617e350c66ccb9ebc864275687028f70847e1f024dbc252555028322e6a077b31d1e5cee5d81e20885249dc00a63d8bc08396c6e1e700a20724a0dc1733250741952617e68ac9722b054c4707639417e19925a1d3935f75c432754e511a2045fb537d23e94d20032945dd25512b7f4005c8f201c79949443a9802e3dc43f04cccebb0747a00a88a6a936c131c98e879b9781b24bc579eb5874b8c2a920258bcd8c367a486f16bacb53f50f9402814afef52ea5b4894b18a220e812cf4226bbc7ef4d5d22261b36c269eef404825fdd20384653d61b98cd5ad9220cd4263c376b50a8f6caf5acf84781e10d5053ab65de06e398692cc836e292b5864e12ad4c6cdb25a6eb9f89e514c13c6039e44b9a5412f5440d5d5e59605fb51fd8745bde512b14b5f465b7e0380cea0eb646a29242d5db4dcdbc8322971bc290fc4c6878f92cc8100d1f4b21c9ab22345b54bed3309f2dc2587b22600b925deb2ea54992dac76cee3640f322959dc3676c6eb907e2770708bfadf86350cf89ecdc56ee2873096c16a5de49fd08c8b9b4ce607220f3eb6f1fad8495da0b2758240baecca8d1690f5013395be115ac4c52461484ccdc0214e970682db908c67f0a54808aa0421dd5b54c70c5e0bcd21f82ead93dbddefc7b5f2a7e3a034ae3cbc21d9aaaae5afba54e2046d7b440e6df7f01fc012a65d2ad5923944203b2f1c686b20680e2a10c32dd504027a11680940383fed4564eea6c8edd988724392828831ff8655ab928c4455f802b7831d2498fc55a04d411c11789271434d8853ada5f662aa0f017e258646a925b98ffc4d304695076f69e8875ff88c735e259d0e92f58d94a7af33326caadf7ead1db90fe10260127e9323785ca3183680c916e28f9ec14d304760f1a6c4eb715e01b8e7867403859e8c6245da1f929bd2370850a08a1e841ba0bb7b762f621826974279d915ff56a064bcc7ae5bd76348383df19b61308a7d0fa3611f3244ffc7fda9920c921ef5fa123798319112ce3b21b269b6aed6bef63d3a6fcc5a34201cfad6673ecac0c0c3272ccc714ecc4d0d1faad58174af1b90722784b6441a14916dac92aaa441639dc1dce4685e22e6a71078510d87037530680f035f8e3323857555690fa68b3106079917771bedfb515725bad50686c142f3233ff19a8c020aada3178cd07e2d824d9897ac743a3de5fc64626e3490d241b50e1651409e6d7a1a1bb9047ad5fe8d7b0dc7831f023cc085af6f07e4007f4dabd97900c3ae328134a527602d3cc7ee3d1c405deec6981ae46511dd73997b1a2e21aab9704166ac684d773475c855404525638fdd7bc9f6ccbc2d01e86012b817abba60f0ee8a607165636903bed7be8fce32b2cea3bf1571305dad3cebf4a3d567b19980f093a6538285d56049a709db1ad3d29184962fe5d045f37e704c8f75a331a544dd9b06aae12093041ea302e2e7b504c8b88d521618881c58c9150c87c2794d7962279223e1c1bc828ca2b0fcbf92a0afa62ed68b98f80c5e9aeb7936aab445b795e1c7cb129d06ac012bc3b475d755bb369149b6cf1f3a59dbc791632cd7ae1478497be3b0482877d11c69ec81bec07a962a4a23d265a55d6bad7e214a7b48375c63210067a6a2c67ccfa2c2fd7712a4070cfc50729a5003e854ce993489c54dd0ba367550555462613a177921417972cd143c016ff5648222df2e18ea4a74612775dd4060dc2d8c36814c262eefe82428c15f2ce33f8d31594d1ab3d64eb55a28c6162994e3418fadeb6548ea77dce1083bb4b5a6146b2b76df3c7e7271515d512311c3626f40abd5736b22f3ede4e91dc19023f3d2b6f649598aeb87c9afd86eb51c084a9e19d4d7a5b80bb7041ccb38b27b4e7db5e65e5a3d2d28c34f03f7edcd97cab503e524888a410d3a4f6e33bb27d9135d0fbe7f28a20b17c03ba55d043c057a4101dd79565d9169487743d9d681870df742d1532a538a5293c98d121842961e286471185ade9268dcdc6ebf7e2013717f37d286f3e02cf1b82c9a3794f932ee93184c80f9e00bc4364b9deed645675f6b903cf2f4211502d0a2f67c915c91dd5beeb41c99694332be3cc49f76c74b477ea3b7e39c899fce682eef99d3668276fee85c113123eb607d4c183876b36c8ab77600c1a858295e5bc44f463ffb354e463473db73626644a20e97ee09dc5a860247ede93034aa1a8a6f13d746840257e80b37128e2b2677267e2dd68f2f3a1c66b6e5bd0d97df2e1db84d2f72cd158dc009466dddbc45291c5117e6a739eb09f348304907fbd0d577663f76b427ab79e23e051bcf42584eb24358a84af78b22098b9ae4f4b0879e82f966b1a4177763ce0c46f919b4b13bf33a5367489ca7fb460a1aefe2e8f68e9cde0628dcd50139f39f914a501c595099aecccc4e967c7217510b6580d50afcfa0ae75f9a211ff7adb861990c9c7f1ad995cc5e3091fa54aa788e32d37132028e4390d2d6ec8548f3b53ed6a239008a94eb0630917a9aa58e2dae3a03ab5d84ef81388a14031fa8493b53a30141f2f0e71f2015bceb846cd2bc235f2939e86edd290676951de775c32390269fb26d2f5de71da70da57ff2a1641fe28d9d2e3fc7d437ad14d6f5a078192aa23a729f23fb3cd560267ef40a0805996414b015efb94784e91879642141c9863762a08243a5e10e3b08a3f9fafb7824049ae0033bf8cf8af7b00e1210288cd902b0134cc1edca2e1d37fb462665bfecb7fb75f7d8f5d232474459e085a4005a28d66b9ff3b9e6885f1c59b18c256d5e5d9bc8b0be60e1226f3823de446f84c9ec43e14272313b8c353b42a0b4c64d4feee1d2e0f4af0e7c1920ecf3cb491c20168b10936758b54e019bd61e8096eb45c928d46984908220d7feb50ec2c92d819dce80e730dda5470c90f1e7fb2f36dc4060dd16978c06da10ac847ca8fc91eb122ba8543638894a88cbcccda5d11b27c6bc45836db7f2e84627e96c577dc1f049e9dbc15324eb03cab178467aabb779ad7180818033a38d9a77f867e4286de0629204af4330dd89841385678002094efb3139d3a3532b401416b210fa46a7925bdfa3b3293f7424f37848922513cd169adc32dd5d6dc9cc386d1d231056812d809f846e30383533ab306f5491d7bee821f8ebd91802724d85f545e024df308e1cca27a2ce7b0d3f93c8d9c2cbfa3ce4b4210f320db58b411c05484f6c481373f46c24740b06b9e132d523f7004af81c89f838619c848d2007bea0d96f03a9d6fc84079356a9b468043315c9bf70f61d59b87ea819d97a537bd9b2d7dfe499cd18ddef0fe299bc55b311691378fdc86b148eeb32cf09c38a45c4df5c8599bc08add4e764ecb36a1c9389fc294747f9f91fd255180b31a38545558221d1653f2453a0af8aa5fc9230a728509a2dda2c783c9dfae5788df5cbb8fe4a8b0611097a60519abf19420006e1e493c81a6be9a5f042eeaeb711a442169135c74cb0c2323252668965925868150ab807ba25d9c612e404ab775f079c913bf99267c764abf0e15893468fc43b48f632a9f54a47674de8936c92a892ac1a985651f7086e63f0a1cbd467cff8b43ac0c1ac5b4b2dd52f87a356e492083409cb94357e8f8de15cd3181377259c0cff976c8302fd879cb81ec98476f0a7b84b791108510ac7ef5c54edf678f3e8062b6d831bed6c3151c5820b0a52ce28854260ada5bb2f07fa1783d1b126f00ed223eb03a72c3edf40fa6f5033da1c13cbdb34002027ca3b8faa4a4ae9611ce958ac0a6e6c0425559c69ff18a448dd5842d764ee856171bd30f2bd7a4ec4a5e9d3b5412fdef2fa6d040f698e71a74dd9aed635034b537300705e0d14cc586d3cf12f4981724c8ec86f69b576a7b1ddd6dbae87c247e32ce880076f55bac86ba66265b4519056538e5e67da507829cb2b0a1301d533ccf8a4c0296489d14d1b5c2d5097f385e282773d124efcd35edd579b5dfc932eda3da8459c226eda59c2d70aed02b2654beb735cb5883b489198d6f94594bc0bf00e823f4407569772327dccabe2a132834995fc86f0980b3eabcd12ba23b55f4b8d66d3313751e15b8be13f56e719bcfa34aac5f1176b11fe92da451cd75c063bad783064db0adc96adee0bd94dfd71039289a540c47baf8f74f3b49d978ededed86dec87f9b396804d626f92596a6ea150d074d8ca566307ad0dc8fd9a18876e09840ac2b7cb29e97fbaa60f1e90a8cf4fa9e58cf57faf863a69da09ade76ce41afa9934e473abc7435eaf4b3b7f89139bf00750f41b716bd5df27a99791479402f6fad4356e60bd0900a65d4a885ac1c188002f1d0f861d5a5646a468d7b626069b551656ee46b134445234d88c63d8be945f851ddf2cca405166d5d40940a098ea900e51283f370afd049fbba90f514a41c74a59969f6349a49136e98f3aa2d0b808d4a09943d2cfc9f8692519b0cae0ea79fa158070863473f691bbdf16d6d52f04af8fbcd9c6cdee746ecfa31246211dba8cf9809602b6f8d2fb91e90051e81b211f0cc2cf923cfc0d33ae0fda2a7db94c2b22955510183515c8a314ad743aad3bdc8e00439db2e521dae3ce9f0f98bc4e5e5b03e84392fa5362ac1bcecf83ac8120942e557a08efe410efa18caab41011917313b90c08690cd24126f66d51d10ac4263ea1cdbc0adc0826b4ffc06510fd7403b54f5127457c9b1b97df0591c6286ef941b346051e7620c9420c2393de07c2c4271dd507b57d395ed3c649c14fc57908aa10a7dd19a810112ee0ba4519dd6d2c97233638699ea02c1dde3847e338e80fb823997420c7341174fae0a4c8cc35288c29739ece8d67c5a2a5088ecd7e358c86823caa7687c744264b942222c820e728743e9a3b78db07cef8dd1a130292403cf1d1e442dba8d332ac1108d545e901e44e245ca10d6277f35062ed5ede106b0d02fce4cbe85ae5e64578f0017ff9a0258d5a9041e798e6ad5afa2ff3f7bd447b2c301af7b407d68f6db885600055a65492cb64f2a71f2f72d8352973a962b30ceb184b75a23d3494e3573885126d68a22fb48edaf628264bb9ac86c580e956397bf22e2425b8b3b4e4b25a3e85520835413fdc7be6e403d405613a9d3a8893648e26fcb794d74a2fe40ce7e1d5b00945a84b8565fda904f681f7fbd19139ee36922df86ae689e5f8d35f8203d5a1687e28056433ccf147fd04d959fd144f704c1dba28d5ba73c1345d5078ab1461a6613f4b6709a6372e697a10a3c75dba924134d73721f3da674b49507b915271dea61683817e4bbfb535f5ec6728309b779024e11aab0aa2750701d133e1e9c9733efc90d69c0ce3f12b20e9f31b78b38fdda51b0a7c48738711cd57adbb182d67460cfc137510deb88e2249d9f04b09e59c2eac8a59f3743a7889df9fd5ff5e9d543e3b5b27c03060e4aa01edddb96dcabf0331f0942931c33a2bd06a8497d2bd28dca5092cfad3ea14bca0c941bb448073e5157a85a9d928ff27cb1c2f422f9450aa56b2ab39ba1a8404c7c05a8c4857f99d12265ebfe660ad727d7ae92943ebb0869004f2c4b52d61bced52119456d7c9aaecea0c37493dcaec3912b860b4f0da8349a08ad1030e651b60dbe3ff94ec0618c1db2b1028f51c735b84161449467f1cc20df208717e8cc26a2d93837da1ebb10e0ed754823300b823589709406c42d2c10b7775f8dee84baba9513dc16955efe5b9ab3ba8751f308bd5e459b0a36923ff0567ea19602e24cf598051002f33a12925e66947129d9fb029a3c1bb27d34495825135cd242448f8869adc5336ba6d2c8c1b26cd485528538345bf731a275d052d475fc3ec50652172f834fdae4a3d17c557c21e8805b89ed370193df438a989b23098d2333ff8a28b4241f56098aa9d3f703d3c429dfd02db18dfea39eaca772c9e404b50cf725c18f9aa5369bb75de6e0c6db431709a2239aee3b99ff4b21ad5bfae72d7b1113728e8cbf7a8ae17c57757dc86a80f840f2011c3c8d91408c350919caf2196459b35c161bca746a65999005310ce1285459753f034c5e6fb75112c48ff811cb0c9759715f6cbfcd876f7bc68fc84a6982a3ae7ff9f55e21a4e55b7b6e4a2b38e5a07112b64cdb18cdb50d2e82ccc06dce9d052b3079fa0a81c5f9efd71ae9fe1b2b19fd949382ae5ac659c31bcced304ddd1e87722668a17a536fd92e820053f6d14639331276ad286b942e72eae5db05a45e1154b5b159f8d75fc50ed4728fecb8764e591de14492b04fef8360c4f4867640990e716949121b08f439dac2f3100b6a026826f458841cc69ecada981d9eeead87e21d7580466b6866cc2b39e9a7f470224ef02436cb6d2dc65cb6e57104840973967540518c3bbe143307c39c6ed25d0e2c4c5e9959d9cd3a35ce216f0b84476b65b98697208d4abcdb2c00a8a34463408105d206c3932e8a81462625944b29e2a83a4eb5033b0163223d8a73753558e966cebb6ee858c40863250e7136b4beaf6809cc500133462ebe3c1b8a7e880b0f0bb7946835d412770dc07779e14789a1239738c56557e6a976a73d7104ac3e394307ef600aac74cc61da1dc62b682caf418da65a11c196201a580324ba21ff91e178759f8628b65ab328a266f2eee67638d0effaf6dba8d919a01071eb6e23ba5f79d50d30a9be18b9e25bade2e11f4a1564f488a92c29fdd6345c2e2913ec4e54b04558244f7c06d4178ce6d0c6af88b3b1dafab9ab5a64498990d58cb51901ab5d738a0764d423963378ae26b6f7ef3d2eccaff234f62f3df0f40b9f056d13d3317f4c991a0700dea40b1e3cec27555b60c04719e0c01e99558001a3bd335981935da61373eb721307e88f924e16b29bf2d823d111f1c49917ded8a426afa85002742ffc73777761d9ebbdb2ae19ff973eb7a3f93bc5c4b239e626d4d8e8625f2d2819fe15ed43397134dffe00fd11020409b8100ec542aafc63422e790f140db218bcf32765e4da4defacb91a39cef728c3236c85c877389697d9b6fc7eb9c54e6c30f1bb143be5dc74ab644a4617d0351b600e94dc7fca3896654ea19d5f740892a2ed8c3aed99cdd069d4c5356412200e48b21b5433e043d4ff60907ee809c73d147c0f46ecf5b076080c137ba8fe3b18da144e26f367a9e757750e462b2848fff4c2f65edc0a7a07e530e0e6088fbfd2139db7e1dd3605b4357691e9a01e1d85f3acee18e5e5f659776acee832970ff9af99ab62cff4c19e616b580481238eb1c3788232acf18ab01a43ec50dcff0e07f2c886025a79a419ba3903a7160cc033db68e1b3fe8165b69977e5950cfb9f3826caef7e8ab810614a11fbba2215f25176a02474d08bd7308eb4d634f3cdba53a25993ddeec35cf484fe5c39b936c16db708cb3a2e3a62b05705223928d9254788ec866b63370a463c0f4e20df69fb4511cd42c9c039a6d8eaca7637d9ec649a4ca6350479a8cf6400d54dfd71fa8dac405fbed05e12828b0af1d921de4b12c8128c36cbd98c158c5ec54975f12b7aa27c21598c00286137110dc526fbf51de19d2c9b370f83f552dff8fc10acedeb81a30d27f3842c33a381a041d2516d500b59dd1f53632ce4f94419523be5a5db0b1b6a21038ad5595d2c03acc8d6b437ade943c4ce92a4822d65cf4099f35e6b00b60ab13318c3501f36c426202b4bb8f8a6e0af03a9f62f4703d123248e5468395b92e9e29e0ac54bc1a6f0a5134db4f92119226bac8f7639e0a3048503efa796d48de53f211416e0afdff9ab57a5a58bd36c44ac07ee17961baae861a089f5f95b75ecc96cecd1216f0fccb8c15353d5bf9ac3d317355bb70b1f9efe497fc9bcbc0a2861b76097110b4f1f1f31fdb5f3f3e74c8217045ffa857606363051a6ee113264e88972f60de97fb2778d86c9c91371a22a221d2c0ac54989035c5dfa3a7465ed15704207668eec2789bb65045649f46bce65764612a6e5a15f770b0d501703d113f5e243cbb68fb632ec968059e1c0b05ac393a4c4c772840c153212af59b7a14f320534edf53f1e8e1c3a4510e5c228ba1bcc9072592a4360d9d442f30bbde1cb21b6c73313ee8837dfb5cc7aa69e21544ce4b9f05b0d50b2fbc42ea5e08aed77ae5b9066e0de150702574e5b8c36b0ad6d864d65e5ba8f8de1e96967541ba76a8ea362477a7a6364535c39974b2470a2a558ac5da20168e4918757a769403e21a3737379d384ea64864cf018533648dda2f4742e0f922a421119c4cc0be1ece7ca845673d81b9d62fc781ed621c695aa4c78091f7a080c79293ea1186857d0bd65bee2c7b929c71f8ee77a3e879f2086e447b2089c6ef9080b61cc4db2ada8b1abb01a11d54c772058f1eee730d3934b32a80f3e2c5e4a5d6bda063020e1f8ed65fb103749fc8ed5fe380f82786e1af1d6996e0703bc093249a9b3e65e37dfa2458b25a9dd5a2ce61afd088209390e82e46628c46bcd400935ff5d7fc9b8bc1ea830389c06ce12a02dd233ffc0427e5910e7b0e4b68ce302a582f735de1ddacfa05742f0a022c3aabe8d1eb17a58df4e15fddffffb6fffe37fe82531cb1415519e24cbdd0f0b61c8cc389d104ac0f56d5c39d10c2939a81a58d60ae981b56ecfd70f7b74febd0a8cfcc02d4c62e510059513796fd423402694b32b6d0a361858ffca24af8c1718a51a6e9aa46d44bfc64966e57ea02a1ba4d408cda90390f1908e028d3ab70240e9e8b511c1cd46d1d8bed14217d23e1b7c564bb50e32a20025640298aae230a40b16a71620fc22abd80812025b8070838df69cebd02af60430163d4a3bbe1e3feb0de92b6602b9d948eee3003329ab154bd56f1a826c267db9eddfed9244538b852547665b21626cbea5955d0b8eeff81da122e42723309cb62b01df4de3def404564d95bb5eecd46428913978cd407b2d173b5dbc940829a187d7888267a85921b66bfec16b36dce04af950b8a3fe1232db04e6e1e823a8b213d27c66bddfe2c2b5896f4bdbdbdcd64f9a6c54ea02b92695178f661d5b9c4a65657a075d9f0268191af4e64871763cf963713f3cdb6fef16a868d43a5a3b129516c4694d9c80840bb11822fa63c10c33b0c6347c3f2654a1390011d65498b5034956bb8d2bfd8b85883a46ba6069d5bda803803a34b3510d7bb50e4ba6aa654f1708e8b96fb06a3f6f1f60a92028e738772939185bf956bb0e6ac2a8c22c987cec42c3bf2c5bc33fb31ce2ab3b40868d89ac91316c8f7fdda9b332ff6c164f9bb59a3de2f442f6494e720d95b730afdb0b3c9c05663d3300e2c9334498e530bebcd315e1f06d6c41c296601fe40da9ed31da3b1c53bdb484f9d34943bc2fd4ea8a4a6997ae0a1b7b922c889363fd90683ab5dba62168842dec7a99464ad7b7e9023cd9b072be9399cbe604e9e5aac635ac6ecad69e1783b9cdc951d9a8a89d4a80a399b451c76b1be34e6973a8a4bdc4c92ba7b1d0070c0783cc76211591a7e21ac9f0edef5b578ae41fbbf963f12955626c8ec13b60bf31c3ca36b16da0607bced89c8c448158b0457f69612a5180de6b83a4a8fdd7aa9da832ad75efda8db7bb0c780af87eba0309ac698010127ae82d02c7b0a123ce8053bc6527130430926e84edf5472958ee475bd3958f6508f54491d4eaf1452ed8c69e34302b255264d08fcff497fd74e8d507d61984b05af278bfbe0ae0512dc9fbe41b5de20910ba38e90160e7aa9ac1a793951b2fa8e03b7933e0e58210f4c02438484f3fa2476fe47044311d2f827ca6b10aa2c5088de1761c4d6cd021349412386a2748c379256c689ccaa69dbd90045036c35d2257468f50ae25137be7d90df1e14e18e3d83e5c6b8fefd990b52bb073cbf93cf726fa33c3c2e88938e02af3c6cedaaf3cd70218e836e7f9d16eb3a14cfd28c2183e85a70bc23d347c1d190cb7cd60bd1e6dc202b590b2f38d061f50cd20696bed47395ae9d0594bec192366cdbce64a2246ea7bd1ccb6702d872c57c2c5a40122ad780d720c67c294ec01b2517f233b8e640abd596df631ade72fa4afb086cebac0eac7fd0fb90453ac80ba68c58731b8b1404a83aa4408f407c825f93382d9cb9362d886373c2e1b2533b1877f58c8301855800f8ff6b13a1a4f25e5cfe75ca3889692f0d508ab56862ef1e2a475ee52b064b0a88818c9add319ec5f9235f42eb25814ce3ca657c8b5e76a291ca9909bacbd1ef4d29b9e4ed8b5b2604946aa9ae1207ad410ba231089be4e4badbe0a798d301596905777f535a86b1afd8e71df4053c211aeead9b40deac70ef801ff8aedf77085a6992809c8e37f02c9b52e389cef3505789b26c22397c24160771f77c9a0cbe17bb99114208e2de5ce4015130711e58090cb27e7eeef1cd70b679ed04aea7867c2020386665fe5f7e6955f8373920926fa1c78945e4996955c42ed19a411e356461a420d1b7892f839cf2bbf3ec55b9e2d661301a74e8af69c24a2a2de7628ef1d357bdba136f58ef13a1ac8770b4edfee5e98ea68cc0bcea8ba9a0191afcd5e1a5e3112f82b8cb1442f46878721a1077fe56704ef118d55d19b11c42b76b18b6578ebe74557be5b7f7ff7626714149ced3052fdb9a4c653326c772fc42880aaf7de0effcd893494bef258abdeb1e1aa729c851d93512e4d44bd6e2b6c00b65c3869f1b1bb68326f858eb62810a3ac0e35d829f1c742652c41450234ad0047a26256f55ce663be21785797a7e4457e52cf434818a3a1c8f79b180dc1e0e8341714a9cea8ea281cae15205ddaf7586470262e3f067050938f99f3effe764c2435778e0b280420b2fc09fb93dc9b958d3f7faf707824ccc79b077171cf54fa8d0e00a0f05a31f4b1028ad3f56b95f233a2f02f162685fbb6d0dc36c235d8085e2223b62514ecff898ed16d07946b44b53c64f25d02c197657a3cbd27eb50bd405f49348593ab7af4e76315f66b0d45a65596da4abc29b509878e0129713d6cb69b64ca60292ddf8975fcf4886e7999162299012a03eb379fb9d0fce0d0c4377994181c0e1e270fb8dcbf0d7a7e053773ecaf3c04321feb2be88c63d852e1f44d597f0a736c68004db8027e37dd6170c030fa57ae139358552b7ca1ecdf37341ad1b39f5602e6c19bcc6a2c8cbbd8e994b39fa5eecd3efb12bd721f1c38101e4ecf7e71870b66495dcd65eb7c01586c2a8820f4582cf9e872ced4849ef15282f244e17b7b533f3bcb39f61d84535f07a78b43e45879c0fee99cd211d980875be35532f67bf419e8bf45c6999349141451e2e6602c3965289920e7e106b20cf0a874790e56a153c057c7d5768cce4f2bd298f2af304d7b100e9365ba77361021459dcd7115c2ecaf643e0379530c3db4a30d9344b28a8cbb9cb1b76bb3c209f5672a0d1a438224f711f277e38824e7ea6ccb6143800ec8cdf590d2252a76657cff040e3a128ecb8de4ce4604159f4551f469444beb3ce127ebb9f3d8b4a65a1a5100e910b1558890bb76c36e48673696b31a6a6f5beab3f4ac032dc8b2ec5de94fe2c3708a97ec3cf2714a46ce13efa6409d9a48c7787c50d93d9be33c066d84fe562d4d624bdf5447a309494d0da2b4f006024f46d3089a74ec72a16d5d2417cb571b8323cb012cea44d708cd52e21f8edaa0387083bc2f1f30869bcd6af4e855e304f978cf91284387bb8099295b7275d8a04ea99ead5a2fdaa7e845e646287af229e1c4081dbc50263643a2c42a5553fe12aefa258332136e177beb281b6ec42a2699c00ee798bdef3c30021bcabac4728a5ef2bba081815c7b4429b2a6976efa4e82198fd7d576d7c7038166f7838010973fbd118bf6ae02004393b21339800d131e81e4acf362abebf8c140d077ccfa7b3b37f8c309b4be40bb0b288b0112dcdea31726954b6215979881d29e821b5a8b565aac629b8ddb48c5b78b96e4fc4fd86928bc41f0648c1562a9e3450a14ba44af748465de74256d1dcd6477e91a474b5eb7f4453902114c548ebab60f665c86029dc76f534c40fcb95c6c45562a88452f174f81d4d81a2fc8027e58a0ff3c04c80b26c30702ee838c9397f987a069e9045c272a201139ece1c19d3d48f0c89702bd66a2396c0ad91ee4dfcc51e1db513ae874ae3f20dc900aafbdd36cfac6075839f9105bbab4ba325f1e1ccfb867bb4209e39aa9355f02309708155ecece0b2106e8e10813dece80dad53add212dee8c90435287e8e9af03130b1bc529a752354c3aee5ad4200d017311f70c53a1dca2fd0088d485423f69616d42ac7b3379f2d751535ce3a2126176f87c05e809af00295a3288910a040a5bdb42621b12c2cd552894d515215e951a7fed2d365b26c543c88db7a266eaeaf07ab8a3da6ba43144b079a4c8217da587fc2029fe9a335c4646ceb7c10e822ec11bfd1ec5dda4407ef897ce5994242af0188ce1374cf6e28acf0fe5c39c4e21509471d086bda4852bd4ad45fcce40702e3a9e9688b78285e997b88427533b3e2bde67966c06dd7eb0cfa3229efa643d0816446c7d061267eb9a0b13fb43238c3771c4cdec9e9de32bcd44a255a5f7680438832fda41d58fd89364f8bad6d7458f8658d63feb692b381fb15e9fa1b9db748dfc376e02dd874cbc84d022f6d0e367c8d8e3421b730586cf71e3f27e27ddd1387c34d000ec6758c396ed33eeacbebac6fdcf3e4b4f899bb1645f2ff7b389274a1b1de3402d6e282d31b91d97ce01163b6c5c1431951c349bb994923f7aaf264e89caa6bb50095f0b67186e30047b22137cbe25d8243e8b0a627d85d90ec6bb5d0763692ccdb7b04439cf714238774ec8b785434a2928797ece3570c43927dec523d38e16e003f792ca3ff583baedf2fddfe55ab51ed2a8cea5b6782ec7e1910cb1c93de0aea7a6fcb2ea9500c63835a3c8426d7e93098b0dbd1f9571c2ac5f820e149051f1b076150f01fd225aac3b6e1d66fb2e55532b40505d5afc6f842a04875d2d0c0cbbfb070b0a9761b1d17de4f20d39a5d1241f18e1bbeb87cf9b6a202f272aa489083a2e7d95a0d9f37ee0c452ac4207bb38984456bdbab6544805ba5465817c028112ca63c0538588532d248b8c566139d0448a1129320ea65163a8d42a8c8a28817dd293ca9f0c22e0f4aa6fe2992c7586de4864f771a3b624b68cddbf24c64fc56e258d4cb8862763e80f751ee3c213c9536ba37f38923ff5a719f22768b94fbc6e9216ac42e70ceafdbab6a8e0e022706c2862eb57aa8e16c8a3c727498fe1eb680c2d9bc7355f0ee193451fc53d98902650993921b190ad131dfa48dae085ff3f15a0a99e7bf402bf1a751095c4ec81c82ec6c0be37cf234211e321c19ab1241c651167b0bebf04eb2a971787cda2bad6ad3e6694aa01aeb392b05c101db3ad30f76cf33d77e120a4f3058299731e0607eb34e35bc953d953ce2f99ffe93b94e67af632a094b0867dc4dc796e901b26870fbc41705f4f11c61f7db023b4e3fa5f322dba5d69a92c05ea7376c52a5e06e30dae80d71b04ac50d9d68f02b159f48a2efebf53bf4628ca25809ef426de28cdf8cb138127fc95f2a445b491c2be193b7e11e4c29b50e1eb0a54a45b598ccf909f21c327bf7847392bdb5604c0b85c2f948a5701cc3b2024d6721d77b9f515c83b493e7bb40613375bb52ab1d9389b03dfbc1ee651a7edc5899ea6d06865ae7ae7c36ed05768e8dc5bb5c37929712b7a106ff32af77b8b6328df7541b45dec767c4e1d243a62941e45c1d6d358736ce22c3018bd1d23ca4cd969a2e8a3ee5ef31d3072a4b1e94bc844aba4298648824f2d897b63f8e124e0c29615a0242bda36860d125c2a0b48ab708bb1fbc2efda455c4a98cd6ec4b5631ef96c4ec681db19666aa5b530e1540de25f0acf64f646ed4309fc81e1169bde24b4ac04eaa5dbbd592050ab4a0712100b881da80d8dec03a8036aeac12382cad968adab1f416538f6dfb42f024692019d28a314fb103d630880505630e44a4f3c3c8c768175eff5b5e42edd2b4231be1ff5d4205159d444ecde9b6cb9a594322519b80b770be90b5f9d6b883d44c49e9855a7d5add723e2549f004e14ba475a4748d6f4d4ab03b1d31081f0178311c740b7aa872c6481e8a37ab651a05b75ab9665b5ea4075c757e7a321a71a95865cb55a2d6b6dc9a95edd48ebb02cd7a35ef1ab97fdaa5b97d5392757037287027d53bd86d189fbd8fdb0d0342eb8ace9ac1464972eac1478892c2f9d97c81284fe2ae2d45abd723ed5a76badb56ad7e8858af3e444d88a57bc62a51baae454266ab060a6d65a2b0c6a840f6db4b8c1871b845022e5a75f17dcd04627e654403eb4e1d1e3870d6b7840a424544612152825e9f2d353f7b66ac540124538a140871e72b802562bcc20d2b1fae0439b1eae37b8b0a244831a4a35dce0a70d49fcf497ee67e610b95c2e1cced8a2a6eb225ee1b6c802011fc6297250f1d3577318f1d3ddf5d35b394b7e7611e3a7924b8a1c39fc74aefb991176861a4e949841125c84910411684871b70064f9ea2832b4bc68022800971870b9e1abdb2530de281283054f6b68a154c61436e3b4b0f95afd22c5d1e4ebbd2d686163044e90800438ac01ced8cf49fd87d61a7cf55989e44088d8531b6d1c518209c6cf39e79cd3032d5670bce0ebd77bc98001add5b9ef76c48215aff8b087bd4f6bfcf415eca7fb8cbd11c613ece7057518a104a5c8491209585c96e6cc94236fa45b7bef6ae539595aa7bae45c7a3bd548a1060b3e7321944fe99a67b9d52bfb2417e9923743f246f670d96d3fd53cad7eb82e9b647cead1d49295868d76774629a594524a29f5ce689671dc09c143784a6f9e3ac718c8743d75b622f6c4a7ce6b8890e4f26ae54e73d8f99b30aa054b95dfa20f1f6e8cb921e5439ea7a47703cac7981b4ec878862d35ca470fb7336aab7c4a298d6cb868ece9e829a594da9a4d79ea94b2105dbd969e5aca807eb9c32e7b57deca799d409fa5441c5abb20d38782b4a8ab5b34bcdfd4b91fae2be250ef3182db460b9e7a09ad13c9f0a1e5664ebd62a1306ab3ea943263cb961a384971a4e404c6157b3c78ce43eee38b9655b32ff5d7fd2244714540e2024d284638090204423253c50940184762bf40028c7106e21a9c4020243a2e10c691c826c0982f0a07e34591a1f442a0a16f9f12e8e59ae1e542f20a72c5220ae8d2e5e9e5d3bfbcb87440f65d04040e86f34ea2cf5330c5d6c86239ebe4c789d8f3827fe12912263d869de0c1f7c79cc753f8d550bc88182aea22a32353d8734af058ffe2759e7f13d1871311873de4173cb309dde217372d40a2006bd673bc01171a3ee3bae50e3f1163a5144879f6b8854ec949adc32cc6c1873ce5999b28f0d39c82ccba13f831f0e342b722d3120c4b1a884dcf5c7ae74837c99cc98840b809fb70e22dad93f29077f07cf3eccc147bbe90b18719083371bf70f5946ad243ddea254dc44d9c8599584b33130c84afb4d0b357e6c25c449ce613e8f7d74d224e13d78d709207ba35c2961b79f06d1d4b6cb10535b84c199ac2258a2630d28041e11445132898c2d0e22455458aa62c5aa77b6e436956665de94beaa34720ad13c2ae87c1126197ebfb63a11322895cdfdf906ef14bb722a431d0ad6842b7f8e572fdfc52a9e8b263294e3e2e922bd8620b651cc9c069064b9070c0d17b087e1cdc2ce2b4f767dfce28d85ace5939ca3498a022d852a9d8c3a91422ceac2188ad18979873ce39e79c73cea239e706654e9db711c1e9bebce07294b8e3c2cf2750788a0c5ce0e1f511e0b87404386e7fe8d68e00c7b491e3ce2c50be3f6457686384e588c8c9f45896263b129cd34a9937f850f55950ecb1225871e9c7cf40441c20c18e18c201f44568e3669d741a4ae79137d3ad9d3c3a7e04382e557d00e4b8d4d33ef673f614681d9edc916ade2694b8dd74b2d1647a1eb9c347c4f8c241132b80459c39e7f4d93a506693beaf5492c83176089a4b52404a293d529652423961a33a2f9d022e8890745f6077c753df01b604220bb1672a808c1880382e7bf6b96ba971e93759a02eb0d01511c01b619a3dd7305d173e1650d040c4d940a88097d2c31c1eaaf9e20ee996f46e89c84d548bac954e1714a7df52707cc0f345161fc6579cf20c8b559ead4001830d18289a87cc04bf1250c5078ce5999b3e0c25bfd500e55af7838a81210315cf675b32fad56902be287aca3f259394975167c7b7d3076d9c1da194c40d647777774ba74fd2ab9015299d56915c5e6679e933b6ec0d4a27c7954c46df617bcc8ba797a0745aa57598062f6d48fb6111473acb85ea6fde6852fa0870682268a91927420baeed7ea68bf0e4d6da4456b3ea5389e905f164985fb4f23d715670514cad13d2296d511486f289aff8a2309f884556c374b337ae4e13998d5158b7a4774adc2b29ec975a67ba944e63ad1383740a9bdd8fb559ad12ccaa8fdca1b0992da3302969172f6d60b232fd3e096b9d70a29c84f0428a1951acac02b8ec208aca63e9a9d3367230839f3ea79d73ce27339cc78439e79c92841cecf094a6b07421c5cf2986a89cd560a3b1b91392bda41f479cd93f917c3b5b0d04d236cfd951fa40a923e07a68848280c56aab9a1462969f41cdacc607b6d802962227969ce0b205d28b47796842061198f23a8830c0eaec3dc4931cb73d7e8c8d51e5f96d7cda4a898dd1e4db8db88f00c7a5fe43bf90a723c28fc08050f8e94616d02d9f0faadcafbe00177c546e399ea72e4150e3f2f8f8fc4079d8c3722f65e79ccb2f8481e7a5f374cb0706577ac853427742a4b352984ed9833e64560aecd469d04b675a42eca87ba05bec42a1a23ccd0baf0aa85b4b2e91a6800b3048293d106fda99a3504282979fea4ed086913818b06f3eebfde4379f1445b9c377b5726f310f3f692c4d36127b33b1e3e37fd052b23c5779d65c569db0593e7d589f3cb333d12df611beb848da17f64b6306481ee96342f3d4274deb01d3344d73556b9ab3a6694f3fa5f44e85214d283389232210cdc53e8e624f7b0f1e363cf54973319086a27d3094368b241406329fb00ffab93694d6509ea7bce661236547dd2a92ecf249ee644b38ac4ff249d6279bf9d45f0c3c8be4cebd5f2d2ad2be4de37cf470a73791e4cd1306328f487ae624220e7bd8ecdddf3ca21f958a40fab03ed9a6d661a59c1fe5ec9aa73565475ce3c89cbdbe1181d426ec834beb30aa09b3d3a655136dca9abcfdacaf56df64b514f117aea4bcf685199467afb04f437d30fbc1da7b79cddbd35aebeeaab9ac527ab7578bea16f6c1cef2cb8ebaf51471b884f6eb160652df883ed8398e6fcbb39390c59dcc9a4b13d286acd396d689c1ba11fb9bb35b5fbdb2a11ef5290623175563b54ab55297ba85c5ab36f02a0e220e5758cd53853de5748b3d4723aab1c1e685b6bbbbe9eccffae8e1dae6192773b3c0e9a7af3ed2ea49666ee93954b65704047e4da7514e09f3d639fbc99764da4e2a589231da52cb129396d661c529ad482cb68a0f6516e6983f6be5ebf5cf3e78b83d9dbac572ca297d04015bbdfc588838d3656cbaf4e267b33aab3277fc7416ea1d8f282a15513494256422a269bec4949efcf02173f999552a5f2e5f13cb9c4b3fa7cfe95c6bde6777276e87d99ba4f308a3219ed0295d497152c14566e624b9238fd86804376e1371470ea171c7ee4aca253965b7ab635f1076f190642747ec62d71dc26908262a3e6c2cb263809412958e865e3ab3a41fe0630cd74d3ce1c9a3239e7d05322929f6b03c6872579ffa1839a577e40a585679d9c4b37cfddd54b0e4675212114b568a9d31ce3eca4b29c94972875db632e521768a1ebfb0b57c1d931d1b923b72886834b4faf849a388c3eed3245c71637207e5ec7d25ee6cce30bd147be49194d237bc7df2881f3d7a843c2ccb119e6e2865962daf29723c3f094b776324a620c104e57a485a9b4a35974493a7490875c7002133047a9610eea7cb0f3ff0f32bc25d62e01f9243a96f2d7d25deb04ba129dbe8490e35c92126a87cd8589a883b6d246f38f644efa327be978cd89bbe70e386d2c848ee7413f186fd8967ef254e588dd0c6b51f958e60e2e5d1a6daacbd77b5726fb53a86fa3a46821637ec981130239236e6588ecb4714c5a2d211439289678f0c6829331296aeed7ea20cccf4298de44ec75a1a65e1d256bef8e8c53c7729440f6b8c3748c2f2d1a3cf1d03a2d32fc6d674b9d42d661d7c543222cbf3770e88de2d7639e75724530aaf18f8bb8fabdb372377e66177bfd059e64d5a9b9b9b066e39a54fec912fb34c8b1127b35e91d47711873df342cb12d22f586fa79c750bc7e5b7ae79e6bd60dbadc78e8b381c7d18083f7513acb5591814838375a904d117a2ac4b940cf68bc818da088d2f996f94528f3146ea557b0a53f3b439ead6b3d0fa0cd132eb2e7762bcbe7095ab2f9bdcf9429b7d75fb7cba453f14b42f84a47b42bf4f53fac588c33a96dd5acb6ef905db5ed8ce705c7f815b87ce9871942f5964c6c145390af531370dfde970a30f915e3bb64a72a30f6120fcd58954cd6a5606fef630e8b36e054ab85673ce39e7b4c08d4a515eef324aef30289d8775f2525aa02df208291157699dece318b79d6d0cb347ce8fde41b9ec844cdfdc6efec21d03505f6823bb158d49c9596f3ee719917254caa7570489eb3319737541eedb6f6ed5a3417c36ea63a19c7e1ceab3321449bdf5ce65ec5fc6b0c826b9c5b5a4e80992149815a6a9a5539e50146ebaac3ebff0fbed6528720e372a45717a4eeb84c829dd524958b75eeb0280c30de5ab6308121fe541b85feb7eba2f461fe1e62d6486df1ce6dc373b21f59353ba25037f683d7ea5284cdf9e3120e5ecb68b70a61471a46b5f114fc268e4c894becce999b4e28533fc9c9f8c451cb912baa17cbda6444a64a310bda494520d3523c739e79c9c119caaf3e83cad83dab49ddb791e601790d090cfeb486a871be648d775a175e2e7bc6ee7e83051060314b7a9563eed8b7391e338cea35724d28eddcd71721cbd229267ebc4c8c35dd5caa675506da757424e8f1f3c9e4fb74c584086a5b5e486abd96c5369d8e4a4def526bbdf267797a022717d23d9a70743fd6ee61d652f431850af0450cf62cf0fa5a8571995d9b831368585805e17482963945f08147b89f4d14330baec5122bd9432c618e5d702768fbb1dadcc6136628c73b6a668fd10e38c31c648468c71c618e78c1e00a4b8d2639c3263032157338c255cb7e67767337731639c33c618e79c734629a78c5372b105895f84bbbc92367ed5ada25f752b469888d345b8cb73ddba11c7265c294131fa5065d33ae18f57c1d8d6e1209efc2a4a0f14c4deb8296de58bf64acdb758c4f3b2392b6395d8f215f823eeac644eeb8240c565e7eee7e85f5e74f8aa73fe88cccc7c91bf30e7bbae63c762e8bbee8bdd7af9c2cfa753b2498ca7fc7acd9d17ba0bf3655ebc1f5dae7f07e3b1e394c77841cf301f013a2ffc715fbcf3c2b8f49dbf78e18fef8e7ca4e6f1dda28fd4bcead60b8df4907e555ee42fc69795c116dd0bc1857943fbf844184e1f9f0883e9aff8eee31361b8e0d97e32f80a37b4effd42e369b0814d43528ae203cb88caeeeed84c50e092524a29257f2dc50986469006757db4b160c992454bf767001a6ec7be63f203ea922aa04840320423c9cccc352461669e41921ce6ae819452e64089945206a16469632ec00ca8e40c7132fec8e186bef32329674806bdae808103868c279aa8f24452969d094658e51943a58a0c162afed2e057f2a35212529492406a32361b1c248b870f6266a31b88628c3256a48fcef62a3187f606b356c610f36b48ba6c170d49220f66392e676dc71d55a384b8b143243f63926d9b51fa074a2efbf4b0bbbbc339a56ce570cf39a5742965c7c8ad1c2abbbbbb011071a2ebd861767faca818489fb187569f6e35f5e49c9339763f6fd2a3ef33a5fc8d4e3aabd4acc344495dd2593b63cb3b9a5df1ce86f21647cef191373fdb8d5c63ec618fcefa28f351c69ec99b0f6d335bbf6f66d587638fe69d0df265adceacd1aaf564fe4207435f4a004f8006c4743be20d453d8de9c04be7491b091da2435fd01bb0adfc744ed6eaa7b1a593e5e4a2dca151c9b5e956fbaaee6c5d0ad502afe7c54942e880170fa6abf17e5680f338de4cdfbc899a41cfec3e7ab8d275a6c7fb8510c665df6c56a76ca6b6ebd3167934d7caf1d5472b412befca9b6d052d189d3dc9cccccc2cd9b3ddf2a85cea4550763f43a86f96e37a87eb1be6a7d661276bedbd52ba7aa8eed4b79494cc65d24b985a6fea8628be786629191348508e52d7e57219a9a92e5dba201d3d31faf8f2849b7c7426ea629e50f18127e609951e8a960c7d949cccd20dc058c242ec3ab2e5cc208105124f5ba2c380d291822d4bf4d091822d4a6cd1d2e4820643ab89094b742296874d521a615889a1903905a14b972eb129d14ba83ca8f4a0021b1e547a7852024f0e150016486401c34a00582081054f4e8f968e9caa020051458c1e3c32195f42145065c402092c92f8cc2f0cdc677eb140e2b38c7d6ed672e6f766d39b2f73b4451ea10ea697d4526a9bec163b4532bd742b452ebdd4f292bbe5cec69c0f2657e30f636c29e32cb75a32454a151b93af973edc259b248dac94d6797d124965249fc8251f4c2e1d6293c2883d51c41d0dc81b695f0cc442c1c5cb2b5e56f1d26f78b8d248de48b7f6ded5cac8470ff7f5d26d122a2661d40b129fbf199372727075b8cac3d8b425b54ef4c1e44e8ef3c1e4661fc658520f37ac2e1e625569f4b2d68f080dd0ca673c080c2d51c8472abf21921aa0359e759237d2571e8d37e3d17c2cea2b0f0281663e16fd582acc388d53a7a1f1d50744f3d9d74c1038f333c426c91be9e4caa588235d5e8938d22318a5f76872c32a61232c5d0a4afe0c40c3e527471dd9a4db3e82b8b4e9a5ebf080a2109971d50734e33a9ceb7e588737e3ade48df4408c2b345e0463f02ac1a51edaa41662d7028d977193a4d0ccea35f34c730de5a8cd37ceb9cebb177f4979eafa857198188ff9c25026a659a6634d2fae22295cbb0ab501a4abea5075754b9620797882c8f78938d2262d8065a521de48c741ba74451c09ebaab4ceb44949493a986a84d8220f6631a330e488109105c825db3014ba7bb55a4191567a3beca8d6eeee9c1c1192dc2340d6e54b47a1cb4b9f2102e94f86583339a1107b6e5efa1006625d2ffd011f903dadb767bbbbd93aa5ce5e11eb3e6de352e7c0962a0c8642a79c5e7e90cdea96524a29a594527a4bc9e486d64a2ba594b6d229b570dbab7b2fcfb4d66ff662ea1816a6a2a20f6bddf279589f96644545513ecc8cb2273c3ccc8e3229d54ae5229441c960daa54b0ea5e04349d4e45b16c978289f2025f560a55d4ef150c25c32c9a6a10fa593dc42e3a17cd2e1e114222af2e1890fa7d17cb26a0f2713e184f9c0e5c3199b56be77783897be3d9c59e61309ba782a64d31ed2261dfb901efda0c45326be3da453e8921a1f52a60f6916f790367d7b48b7d4a11270f9b012e900a042a94945397c585f35ec27e9e4a88a5c925c86a0c47062994db4884aa1302d140c6b029056dd435cf4d6b4ab0bb109072d663e3e090d71c96149eb68ad1bd5dd3a50e997deba398b53f3d312a30fdb15f692976ea33e6202c6878dd44c744bf9dbb05ee2c106cfc3bfee87c7fdcfbdeee7a380bd56270c8087e0a11101f85c60081f12300a0fff1c0619c5072b5ccfe3ebc0e70070ef7e00f0c5d6b99e67e5439bf77e78e23d1bbc578211bce7f9aa042778cf41e87ebcace9dd3fe87e1c04129cf120b807dd0f084064972eff81db743f1f3cf71ea8de039f1eb86d22c67be0970452de038f4d98bc07ceea7e3c582d91e56d7c47f763c3f2c18a67794df7c3dae1abee6747eb6b7c00e26b5c47f75323c61570b5729aee67c5e37564791d3ed3fde8a0a17155f743f3fd4c0f2cf81997e97e66546ee34e04551ed3fda8ae142fe311cbcb384cf72313e3b7fb8969c1680183031818bf26781822351e8687271e86491b0f13c2c3c078aafb81e169f1f87065dc8bc5bd08b8f7c75f8f4bfc3dc0dfeb7749d35f5fed2083bfee3ffc1dc1157f9914f1d75fba9fcbe3fa9477dd4fea45072dfec5b9eee765a7f3adfbe93895f7a1cd7338cf717e63cfe970f45c0e44449dfa9b6bddcfe628949647a1dc763fa80fa0307acdb3ee47e3defab43834f1560c9b0dc980521f22fa396b5ecace5ecfa33d12d45a6badb5d65aed915a2b94ea13712acc0793bbbda6691d7b8f8853399fd8d37d7511925cf9d5394ad461a15bd52b7b75867244ee44b07a8c3a1388ad8c281e893d370ca4ba02df81f6f9b1a67f94899c741113464f1a0a1ff1111212130da575589c64619c74798a85b1dc5edf1b4cf860e3c6c931fa6a85c6d14b4ecafe6eab802557e1838d6b421f691dfe5837e4575dd791248bc3e561098831da27deb44760c7e34df431de8b6f498162dfa9232858a89b95b600a33722a52a73f4943e26b48e65fa4ec1f37832d23ad183e0e03b6af1edb12748fc185611673a4cc4c924bb05b241e08d38ec4b5896c62262bf299d253f8d045be41182f07a761f4e570427374c3dbd4090f2ec2084776836a5b4748bdd87d30d79562fa972c23a417e7b7b3f7baa294c6549393d47f76c562e901f0fe6d4d3502a078497194f436e24b111638f41cf512471e2396b9fa0748cab1863f783edf1c3ed4e48a4a2c457ef1f5d6ef6118a13487996f69e8da7651ef5a6b7d271456a81c126bd394e4a2d9d2c841b4f6ec05e6f3c312369d6287f74ff748ec79374c3a08ffe69b1f293eb135cd594527a4eebb0717d8d225e7a98e34e6a0d9899e79c331a41997e404840398508e640c30b3884d4182aaa5214d51328a9b5d65a93284193b4808618861671ac0145ca0d484460f522c9932447b00e5ce450c31363cc20a6851012708032075ce0c0731051bb4882c34cb2830eb5d65a85b6d88658c88d98c4a5e960e89a4185d448d2c51a9bd21667bc0c12a2061c8c5032c404920b94dc47a52dc0f8eda3d2165a7ce839168b6d4b45fb36e274958f1ee3ba20f22d3333f394733ab78c38f6a79bddb235cbac86ea7e48f0e28696e3ba971eb1e311b65e3c322e7fa18d8ddf1b616018863915cb68dec0f36761f0c151ebc88f4e3775943a4a41699de9fca55edfd15348bd934a92a924286eeae8773e951471da39a6cbfea9a46eb5f74c25c92f95944afaf65452ec891f304161660eeaa4b454850956a9b56c628b21488c21486fdc718cd1e3ff61b6521ae1c58fdd0a0dce0f0809c81e7db2e5f847ce89f5eadc5db5b22cd3e249a63a69b1345f4b3a2c2d916161a24d3bbe48023e469c2cb525cbb200c4ae4cc9686f4c2b2243acbca13e7e8b2be2d0317e768bd278292d1fc49ea6c468c518589ebaf54d2e1d79cab47d28267953bd3b22a80f75e40151c601d6a35ba75e887a05413d7f494ff9a917dea114faa78875b96e7823d4f9b913b2fa220a88d4e953dfa897da12bd9453b72cb5366a446e14cdd8486762ad13a6b47c656a6a9d30c565c6cacc4c0af4ada7b4d894536a0b0ac5a57534af9ec292626a9d8e5e3d95a575a8dc614a35b58ef62da05bd5ab4fcfc7e371fafdf861b79efd76b71b8dc59e17a2b384a89ebafd9077f0d4a9971a8a3835f3504311a716c0c90d67e84cac0027b899d3020871a387286e39690da9d55e5842549fb9fdf085cfbef791d18b8f628a3df3aba396628feaaba75043b18786e64ff0e0554811a7fa0094dceb727d686341b61342f8f8593fc1befd2d7a3dbc1cef47b7329a657106c98a31a46491f6e089557ae4fce0f15940f77d71592778309d3ecb3c3e4fb72a6a0985a55b00208ac3c56ebdce50be79f7cd2cc52eca1bbae4cd68d7f5d5599a765d7d85b67cf53bd43add57bf48574aef54a3bba475a292152b5f3d86d715a3e1c37bf4d5098b1f3ebc4c7cbd445f5db3196a26f6d52b5dfaea758caf583e8c58bec62c5f61f0a10dca93ce835a42619989cd2cc99da55675eeaa5aa777c4667b951f2e399840249589a770411c715c110391a4a000b1010b98891cb029354c1da4b8c205b6d739a001f6087e584204b8554e7a3bb43c110613d81e3bf103b86595ce4cdba1250a2c9ec0f6f8091a4024cd8cc576638f0c45b22e5d9efe7545114bcf9f11194de042c4e3d96b9adc302747ca3047f6aa7372ec16575eafd52d5ed550c19ca39232466f1dd5770c8b074450284992eb3cb475ec067d96629e8028a7abd0a04f8f40821bd6a20da975c23af4d389e48ef41a04d7a20a44ecd17c562022ceac41f0cde41bfb8535053f2b123fe717d615e8f869e48807ba35a7d3e62407d73e74706d181ed8228f4deed895f3e821ab479bbcd011b9f3a3b9a87762f740e276c79fd36071e061f57324e2b8c0c2131bcd214888c203930dd6d0c205b627c20d201430a062086c8f751004d843d440e83539e901d524456106884401474e590680048844c715507e740d293fbc0122c911020543544105d1143c5001b6c76c4c81061450bc200bb0bdf8c49318b859aebd49040c40e90149287220b40444d24a5c9b06850d182a0c162891034492e3a48a1b5430068f8d6b20129a78c90122895d9400e48c784152c749cf004ab8f1c2498f84225e8004b617c12dc5498f6590032423b0bdc8850ee07639e94528a6fc7004b607802ae00653e4644611142700b7184945164f4280483a295d885c678048649640f9c536c026c3494fee40448411d81e6b3902dc549cf480e81c538e7e0091e40831841ca0f4f6805a8b1f40250dca6fb601a44723031f8a9480ed512128c08d86939eb47284182fb03dbe320370b33a76d4b0c40e8ec0f6a2530e2092e6c9c60686170a2f7c48aa428714c076804151953988729898e24617af2ab814f104dae000c68ffd84b502154656d3340f126bb535b359952cd8582b0071194820ca3537c2df028b5d471208a42309dc220febd50c5d0f73be9fdc7075135b687d54a2f2a444a5e9a31295a5c7f9a8442589f7e24e4e5291125398c005566c01db256799735a6b3d487ceb417fe499fee87295a810f12cbf7e9c03fb901e8fbc7cc190530a40245d022c3b28a2c70e40240a40430008f8c20d2140240dc795357c005b890a8884999801f617857b6c51ee00d12fb4f80003a5032161264aa47081d38190d4b8202908503a929a28446c014ea058831e4024035002049a5f1476d9dc80cd0621bdfdacc79f4b2ed8ecfcf17443eda773979fecd4b3817a9d935267a694764b8f338e395177c2782af6a4522f2fb1e7e5a5eb624fd7715cece1b86d8b3ddb8642c51e144ad3628fa6451ccd469ce99a5bcf461ccd33a7b1c7faf41a7b5e5cf3197bb6cea7e69d5723901a7d683efdc5ab11673ae7651167fa8c4066f4e1598f461cad4b6864a23814f7c950b3a1b60d8592416d329ba3b6af08376b3532ff3a2ae5851e789491f931f3aa62fc42233334326564647090f1242a954aa552a9542a97919171253ca08c2391f9542a95caadbce90f8ab8ed7647e54241433a78b9646464645cc806948cdbd03292b1511ccdf10454a954ae8407547d4978a0600ba5a1284b78b8028c62bf178c043d11ba81500aaa70092306da12cce08a1180d1c4151cc0760fa05028948c0c0a15136364fe7419e755dda619c166afbfbcf895e9ba3c8c6f7c278c6f9afce9d6519e7c6bfbe787c2b1284ff3170f42fae99d6b5e10d237aaf32254861acd9bde7911ea1f7841f3eb2f681dedac779a6bde695f11548cb35319ef7ae731def5f829d7c788333b4f75be35f78211d539df64f743dfa76f5ec82750df620f438cfd07827eba66352f829af6c5c3e99a6f539b5f11948756e51a6a7aa18f9c2afb1579f98cf8e0cadd3c7ee7eca8187b849b735b1884f4dc97d5ea9d87f63bdf32ae7ae7fc51ee0009c52e34f0020d37e290638e2ea0ca6d60702803930481d63b4dfb52a81e5a0754b76ebf30fb19dfce99a0a73ee3094579413a2b05fb2887217b9474cdfbf17451ce5d3ed47cdbde4801f585d60895cbce882107551f921803a3ccf95bb4c08d2f294ebc5e4e3cf9ee4016f45de6c712627d3a0364e0cf3e5ae717d31c6cb3ed81b7df0b46e6db906d9d5e847a78e4ed27438d94524af915a93b7c5cb9d2a32dc25f8c3d42fb15619f43a8f542fb36b3cf5f8c3761f615a99f11ecf48bdf135be401c8094e19a4b94fd86a3df2edecf347822f1891ff81ed63c92f85969f0cf25f88dd9aec41cc40b605c49edef1638e5b3de86da80e14045667e7e82702d773b27e916176ce3967c489cc5d3e469cf8bd24076e545202c9a23ec6ae3409b9e449a533cb6fce965372d9e201ae538f1b7d814a66fa02a519cda8675ebd17a8db6e359d3f2ba5b65b967a946b56e90b95d6acbe5033cec69efe5969a53483924aa552a9542ae58483f117bf305dd7f9ed522fdded18e6297366470d19bd7f53940bc99c9979c4507dbaf48cf04be99b6bee5a4e7bd66a2f5fa86953d366d4bcc709f2b5cf461cea0330c1cd9c5f18680d6c20f664edd4230820ac56d1da18ef1cc6a9ff681d56cc27a4c5758b73ea9a07e3d96e715dbda3fceb85f3535f0c3fd367e4a2cef4483dcc3edeb8637978393d240f0672137db4f7003e1e2d003c8996b333dc6619cdb2d61c2e1cb034cd81830e5c14cdd124896af5246fa29d0388212e62f226da3986d001cb91bc597571ca1a060cd8132060489ce0ce8f4ab0a42930294a6053946cb0454e8c8955779006d3a450482e34484d96f1618c71c9e1c388258a6183105098e0e00b2a5b4c31e280e3a5ab56414b94288922040e3118c2054a4741ee504009071dae3851451a5d8057d5faa81487151ff351298ed787d79dd8b68f918a94b495736fcb9d2727078500e4705918226f180c400e316e7ef2ca74525a02a1bbbaaa951070f000c70e53261c422aeda3d21b713cb1b28146c3135bcda20a0d4c80d51b59fe7e547ac30a4d638b1e79482965a444b4c91ce3c39b8f1ede3cbbc6ec8535cff49b91267d137187164947523aa2f2fcc473772bcaa64572478b37d13da49aa3901a51a62e44c8e2b66f2a4406e38e0d0d8d873034adc33b236c78888ea77b001e179ef899d34525adda50af9e2377187cf1e94a2dc99be96174babec3c3b8c4aa5965a925b973c4f5d3c314d312538ebc99f4f3d5ca7d9a520f5c87f496fee275d7b5772ebbce3cf88460fc3acbeaf022d825406888076c0fbb47c0d00a6c0f614a186a817dfb05f5d3b7972b9769ce0f1e176028215689b1b814b3d09a7a6a9d30b594724a6d497159923b2b79333db5b45ad53859f6a34e820f0158488983910b1026e0a7473640d8b37d39d5ad57b7df4fc61a2265dbd05496c3b8d00ebfbe49aff3f65ea68f40832b3fcca1711b5f81c66d3e56751b8fe00a3a9c556d7c85ce6d5e7c06e99daff0e2366ef3cd00fa74caafc739cb53f90e4fc6575e8cd778339ea93ce54232ce39f55e585e04777811acf122487fb4189f71212195908c0b71fee23a3c56a5f158d5af47d0b2aa531e79236fa6cb782cc89be9339e06e4cd7ca9bc2e2647decc155e1ce69b81f317a7de0a9d5faf9e0a29efecc60de16804332e4bfda4b0bc78acea3cf266d66fc81c37c5945aca31416a2926c8768c962d82517e2c1532df7c85eaa8ea5bf5193cd6906d73d60a29f4b33777fd3591661e629d3d661a22947564f9d26e3d96f4cd355f6173fbcdf0a17c062d82d77332cd8b20b526608a4edd33055be4e17147ce0574ab4acf16893b31aa1077621c226badf1cad70e85ea1d191eb2518e0e97c663bc3ba7f1249cc7d07c2c1af3b12891ce69649665b47b98e00e913b5ce7dc57bd1b226f6a189750c8d1e1ce1085fa0c9c773e838cc7387bd9c7ce89701f2b731a9fde39fd88c87c2c15689c08cd379d73299dfb62ea8f188f95655a48f339dcfd70db47a4732094cb7804fb867ecdcbbebb1fd610cee31391ddf4ceb78f08e7349fe6d9b71e7fe631e7b1503e04ec3c16ea1b124199989a396eea29e59472ea32efdef09be566eba06e98add343a8d93af6069ad91754f61bcacb7ce3ae0518ef3e966de13acb7e51ac03a5b817e73c4808bff50744fe21348ef2eaa368328b7a4004bb6f8628d66798de71df0cd239a77122d3693e16ca693c8244a4b35034be828cd37c33442142e3313e830e97f115625ce5aa6f061a8ff108661efd8dc66315892185fecc87a8bc4890f82aa771d6101dce7a400475386b051ad7541ecae326d535198f719fa6f15899ab3c823a3c563633a3fa6650398dcfa862543432333a62dc7e44a4731e3fab433c173c0fc89b5a613c14e44dcde956f5ce894cef3e5646443a2bfb2eb802613d5a5fa74bbfae8f0040c205d58679b2c50bdc680517af8f5d7cb4620ba6e7828454e3d137116257eeb0d87778e47cf31536dff1cdc0f9e6fdcd603f167f608debfa5c71b9e1eab98fa5398b08ff10e95bdcc1712c9a83c5cd7c8273f39c970e0f1929c699888bd888c6371d313972875f21cfcfecd3800bed3e9db57744de4ca71ec3ba353d532182ed1b73d28bf280080ab17c876fcd711ecb376f4710c8f9038240ee030a023967399116bc8825d100721f500b5ec4925ce08e181e10c1fe84542ee32c0ddc9813218bfb93b1b20c88991032fae9213789b333caf7a875a65fa3fbe442f9e9829dd48acbced2be21ac1d9ef97624e24cef9c5c76ea9a5f8981baf6fdd897a6cb3e020deefc18266b088bb5c3bbcb9cd2b8ca85665cc65b72a7c6a7bbdc59f9f48de579e03b3c1baff1a4afbc8e33933b717a58f347624fcd4f5f792c1538f7c08970eec10714853a9074cee94764739b0fa8591ee73bbccd6bbc08aebc086e59045943643b17e3a15c8707e334def5991797f13ab7de0322282474fdc58552de79e66d424249500ee34242d7855e9c73a1940b75beb94fc778412ed063188f91268a937e822f1cca0b721da999e3f2746bce9963658890f4be481f01804415541ba6872d9b53ce3967ad52d618c2c0359d1a70a6d5c728e794934e293929a594524a8fdd629fb151682899ec514e568204154a9458c96928c978d8228f58024f474951e8d66742b79a7326ad40dc1ead16ad1495e5f2f09a4d9ba1cc4ad58d51dbf1f179c5d6b19a7de10a8794d9620d25992b6ef751698d29313b5cd447a53562b0c6123af0f0246f64d6b8a98f4a6b18ade0de8f4a6b10ad31e43c983ef41c6a438c52cee9d37bc9544e29254ffe7cce9f53c678d3f21e73ce39e9ca63cd07200673360a06955aedc2d4d81b125607f8e81e6572b95c74d25aa5741e62a2e7a26672d986b88342eb10b20fdfb575e3820b39ad960f0fcf09ddbb1099fcae6a5e7eb1c7e58d0eb4f2174b498c541499743c5d1d4f97cf68b5513b45a698258d2c50a85ed224da8e636c9184890cbcb0010a2532d040230bf98891e8196dbcd4ec758042082c48d810bbd2f42117a12145c84f648c6d1187154c50bc40075b803dc6193fac3260c30e2fb08113521c15812dc6b75fa66f57b50ef7cda9aa7c6863f3210f34da10809391d31969c40088001ee559f783fad5ea06afbdf62aa1c5c3a65b69308146467d138ffa26fe446a25e5bc78607d1341a358dfc4d8174343e8adff586f1dcfc65b677e1b1b4e2f309c9fdb6f15413e6388cfb0c85cfdb266681100af71c38e31cd96cbe5aae1e57259b9d34ccd640694fff13287069ecf6935d3b3af9ad8e8fb0505cc31cb638613ebc51a45bc44d6f7f4d477d8377858d5743f35e643ee29752b8616264f7dd5fdd0cb2e36a298611d57ec40ca53a7e97ee86a3527124f8c9852c692132de0d4011a37cc39277f3468dcb035c6539fd4675aac430a4a64d1061a390421871b20f556ebd82c51247162a3aaee87ca8430e7013eecf1a38418c62a62b8808a18738c4184d2186e2835c16820c3ca4f47a175a2521946fcf498181414cc0862dcf0436f2ba805366ca14214050e2c5a60c3705aa2d254f743a7f8518502c2d8a295a5862c5494a68065914307b09ffe72698e00b03009a1a407208a4800ce2b66b431e79c5a10376ccd137c094f9d6bed10460d3fa76f4f6354d6414d28c078499146ca065f54f1f3675a32a4dc4a63713c75da1a01d3959f3e271a472fd15ecbdd1e1e418929892d37b43d94bc48a3c7dab06075af1d5a424d58b294356132912a113e48137678a22f10a2552b2218518c68e95e1104334962f2ea9bf8a1bd4a393083890923e8d512fda8a446d287d7b7263c25eb4a6ba5334a2ecc4725299862ce90228d1c48c14555c4dd3e2a4d31a43ae3eaf8a834850f5e4c81848c94fbf251690a16c09ce0d68f4a53bc5a2aa881246fe24f964a55caf8f951290da6b9a48ba78f4a5d34b1f7f8a8d48594d7334bfa7ce92c19da55ebfa537cf1708c4c4dec162687c41dca246fd8ab5898dc8931f6c7b02f067b523906eb1657a00bcc6b80698f30b98372728241200b16195c16f50f51b4bdb97a7173d5a3344bdfe8e0f617daa5e727b963955c579efd8b67179185d9f0e0562ba73f1e8e634d94136a0b3beaa97552cfbe19b10a9c505cb058b10143e71f1d4a36ac755827ec6ee2b975c28ec173d84e3c4b7163e809f5c22ef9d609ed14760acb6485b1304c0b6ff9f121c384963401e3c376f2c587fd0485b2302713361fbba4752c119125b24496c812d9224b648f2c924d6247512f32573644999888f437843e691d7a44918ee8113d62a424a6310a48f9b1e046ec1181cb65c56eb18658d7b625625e7f2175c2407f2e48699db05fff923b36266fd857d757fd62eea6670b93370c037bd54093131e3072a463107bda3b10c3f80e48206cf0f2e5d3edc52cbf630ab996cca16e22f648e7796461ecb64aebc03c3b770c680774400ae12e4fbf0e4819b304a8eaa8546dd575490c19aa190100002000e314003028140c88842291482c1e09631e7e14800b859e44804e18cad328c87118420819420801220000002232b409003ccdafc4219f02b14d1a0f36d4410edf7983068824213ca2d100413cfcef8e31097632895c7edb6e627d04f13b4d637bef62e521e01c821f516394898a94d04c6b3a70eb0aba6be0dda8db612d9837ed1096477bd86303318ac65ad9df4e90a17e7a2c94ace5a2b27c62f6e313aada64ac153b2b841e1af8e3094f2603357c7732a61d33a170efbb473231d73ba5e249e57fd60c5adcc4a4fa9c10d09bb047689d4e203ad5552dd176b9df2f9576274874505b859010ac8477fdd93a73fa18a27a54835a0451b529f3cf5f39cd0857d46462030604ed86b8a9defffef3cd8888bc8fce1e1ff64a7412ed012fc46275d40c5919b7c008983be74db20eb6e51fdd2e9df1f146389babbc61a7cf481e33e0eb8f0da7c1e640322e0e753eaa3ade005d322482aa22d8ee732ca63d9492467763c2f1d69d853a57c448f6a6ef740db619964bc2f5c2d3aa9303e84e4600ef67a1daa4057046beb50d69e3a52998ff989f6049063503a6e5cd1bd08e353edc6bf2972e421ec34c1131b7efdefc879d27f6c1a010c77a80e8f871bf49ce649c64cb0cab3e7752d70b1562ce6f35b7a6ab178c7dbf868c05c2ff6700353ee8436b418fd9818b2e39fda06a5386e70822258142d8548e994f72ab407157a2003aca1c87a50c104585a8e1859782f5fe8a3a332fc98f53dc23335be17cfd984558b74d7e96890e31284df59e5eb9b3bf2e19d2de4bb18fba40417a007532fa30e3b448510c229d21be9a2f31907cfa3075d3b82a580d96c41ab1ac55d7b7d8119265ef12b04bfb55dd1d973f05f9d54d10bff6781ca3e50dadfb7a51737fe762ec75bf57590904db495e676b134e9d2e08b08c6244455342ab4837520ab3d9da77e659fdf68bbb0257cd5d21f8cb954b27f8aea07bc9f2aa6a872fbbf0ba1479f528616b1185ddc52b11bc2c5ff898412427010c4a03bf25c668e443a8fdb9a0699fba06464d92f240fc3ee05b1eb36053080ec1a2170852e6f8da17abee0848295a28ef7d0e25753dbf13e6fdaafd226d78530dd98ae7af0eddfa15d261ceb59f69fdab170201796e88d23e4dac002b50a2743e76d419c96ef98af3801a5aed68b689d7e83d49a307abec4a0c6279902eb20602674874a0eb6bf49b2c9a0a036a2cb848f9db9e901df289a1213ff80de9927d56185f9d391993a1b8247495a6eb77e6e1cc900f6174456f55c377b6a533da5a25d3fbd3dab769a091fecee0402c31a1d2263b520612c699b3289053bd407f616e837b4c83e4b53840208dac4b933be7b5cf6392949e5cee4dfd9e6ccabcc50b9ae2fbaed7d37012e06e7729f2a1b38c1b4e78d993ba898eb84ba649ee37a304025ca555ee759c0caa1b74bc709ce664370e262aed4c4d09d8fb8784644a564a6ca50b2c3a621d5fcb6d6123052b118097cbad255d7097c4ca9948e99d54b7fe8176be4969f9a8307b6117c6167120e9a42435aae279417e34f29dc4c5ceacdd7b59f498ff10293987cd7a70555e1ff19f0a039b5373d9915cbc01cf32e21f99a26947ec0aa5137a02d94885d66f88350e3198434cea9026a7a4c1e6e9be9c77daa15702f40a314d60d9f350946a606587d8c0fe0086cf806961dd6ee73d43f4c4c9fb81aad30e3dd3a19705e874a5286cf5ed40d874eeba21d1d52b2a31d1124aae0bfb6772c5b070fc738d4c417e347d8581e4fa2b8d3550206f0793a8809e6f238fa03849cc0f418aa4e317097da2e47faf279009efa2cb84b3e5d4e87fa31fa09344b57bd1b4935683409ef97453db0151cd208aa68ea075079accf6c68166380aaa9c441b3ce48afd9d38276f3eb18b09a0c15e0652a4bcdc4949772661b8c0190e896b20a1e28f154bb0deb1d8d940504546196762e79d087ff69b4abda56abc084f4ec2bad4968be2ac74195e7bf6c56c43cde4223d51eb1fd3250aa617f42e3c6631602e97390a0c20d6b09972cd5a006911ecb64d7ddb2b012ab0c74a8883ab706b53b15d15a02e6b31678089ea1314162788014a73b0cc40cdb2358594ded3d0f845ee30e4616f382c8f9e6cf14a760e9f889eacceee17a91e6ebfe026c8161d2d929e28db6841bb5cd93ac11c5d9e814a2e434d5674a790b29234f192ccf15765f4fafd0cccab0fb0e6ac29dcff74c1d7c5042f85459306505896e0cec17a8d1b57f72de94ffb065a0696c88040d349fc789f394eb6b6049cce4e2df1e136fccc59c21b505bd053642cc6251aefa9ef8ac97789c685579f3c35c29c3e3483a0f18a049e81860d56bd239fd40303e5d0a1e48e3f0646f18045dc41e403a7c562b9d5cd6eea69852fe079ab042ea9fc8bd69696352c3128ca2612814e1f765a4ca4e69cbee7a13201f87dfc6ca6003a9241cdc97be6466f8d51878cdfef5f7ebc197001cf0efe09ab3d5b84026f1e512eec2987a25ff909f11dd1369a35eba5baaaadc352efabda52a59857a9d460319d0b40fe6f1f2806c50f92cf5384fb8b57587d944d58dda839fdbce65079648bf4bb729dd7a49658cc37065e04fa7a9d25ca0eb0bb5cdbcae2b092fc7c299451d5423c67a56091932abfaa0e7e10d9c002be33fe41b0b210e7d3f9fc91b3497acf841cffbe21216a01673a494def49f6b9e186825658a273d68a3f4d9ca2a5fa621d0454d850a6d134173dcb8d6b564d3bdf2c7450da834503dd868a74230b1b54d8fa0d585fa50a69f1a039905670c3beffc144bfb8bddf0622e7d90ebb9f1596126670a4f72e4d97d5188eae38370fbb98b8255d25e2275c030d32ccac467ee4d2410249d7ae368c17ab1ef5f6f30368ff94b961700d533dca69ffa16499d737d51857bc07dd4941e1752d3640d811e784f6c1ac5d5881a08f7357c16006afc3e75ef084c9c9f67986d469368580ed728b64c20d18f1e215e075fda1cb35bf14d2ea8e5ad896731366cec648c8602b1d259fdbcf396114911d764a86658805ea5d2921f0a95aa826fde659b5b9f6e941bb8b77458bdeaf40e090df24c92762433245f594d6890a4de3a0240dc5b2104288e368de53761e5142c37749062953b51a18a6bb3753b6b377d79384e08b746d54926506cac8bab2fbe4be7255212f7e8483920d43d78aea12c3d6c3fedd1f5102360b7cf5c92db9038ba4a35456f45685520c80d27e45b076352ff706da7ccac9fbd9be1a990b81b9c9e8cce1893eb9ef1ffee2124d5db9626589f03b6483cac1e4d1d229dbc506a88fadb6625f71f6d7f7dabe3a300c50ffeeff936a17d42eadd586ad416369b0585abb16c2f71b8fa3e14ce8a9304faa251aafb257d11f9024814f8d15f384d8d881050eee1eedf57e9ecdb1ed50eab8a27dc088c234bfb06d010d7b2ce488fec22635bd32f40545ff8990097748320946a9d4b7819642c05ee980340d98c421d00a53b0c38b85f3e0fc97cc88c00534b809ddc2cd0e1ac6bce56b2618ae9ccf7869577a8e8eb90ed7f4ede01ff3f28e087a17f5db76a87e6ce05ad0c83fd2fc2f60bfa9afefa7521d8b669cb0c4d0cbe674a8134030cbaaa1761ab3184b6ebe91177d894b3c73ef4f22c548bd6350410c22ab3d5d2bd44834c093b0048ad736342564873ed728230400f6746b6dc77defad3a4ab3f28f6869c80bc830daa763097bb5a68b88e026aff64972aa3fbc590f98d33370e144a161404d7da924f6b19b42f70013ca092896de21e77daa6bffed565bbbfc8cee1254fb967028d4cd2646ccc903ff3846d38bbcb754991b982471f8c432938591808efbfbe885322a8977a5ed448d02c261e02233819dbab7ce020c5002965527007de7b2529925e015be0035e967cfd60657d4dbd8472c5f9343ca63ca0896a67dba36b466d2f65a4607aa62372be24288eb32bc694b50dc4f4ad420c2cff850f27c5c291008ab358e0ae06c9b455ddf4dcd48aa9bdfcdedaea86501cc5f3f209750c37a248a74f5843fa339fb39bf8fd5d337dc0b5619771d04ae157b43b788924d8a4c87346c4377d3dcbb0b59269b1fbb9bfb859651e157e85b29e4bf1dea03ae05fd36f3b5ac7e2b46d9005ed57a3312dfc7069b4cad91cabd814e2afb82bf4eb58ed5e2ac2c25cf10594f7712a908070d207b3a7cd190f58306bc92874c907b7360cd925f8eff95e85cbe661cd436d326597918a9bf1460fee3c0ecef96b798feabcd7e72d41159566d88fc91cd0f49c93f1c33ae15334158a1fd03ef29bd93f90ef84a3d95d9791059ff04e1209ca3489c73f0c1ded7decc98e64dd812c1847294591c50305941382cb91d7f8f34e301e55fbdf4f398b542e4ea0e2e2f7a890abbed703281620d0592dcaca6753e78980ab10f21338c2b7ccffd724da2462c1073fc189f1fae6af7f9fb9f749f60d8f585f7fc5f74e2cc9ce1a724e6199d487143eaac40cd0d2f3cefb9f3748ec7bba6f1dabac3e4faad03f200ae1d9ef924dcef8081cdf8905e30e7f42be24ccd8118a780d731148d41b86f10623a87cf15e4db2a234245b2dd40bd12b4aa6e17a8828bcb923f8d8ae56abd65516e0a4a5e48040e6ee5ca848edfbf20ac0fba732c6a3b4ff0c3cd7faafbb36804a515db48735ddc9af4505e4f3198dd4636a895b050311756792b07f608db6d74fd5bb814e8a49c1a3117d2cbaca052e46f032fe09d2553494c9802b22e110965539a8baff54b93bcc303916808d19abe0424a29273b14148059ce0c7bc837b36e2c080d63c20065e954bff001a62b86c5f661b0fc83dbd00a249216a2e0ecfb9a98a9c6a263677eea7418698360aa974eecb10502ee098b078ed22474d7c3a4009a71d545b03ec4b55538819d126a6508de96e545854fc27e5d4ffaf6668921dab7f9ea603c03b182bd64b8af86f018e0587d4aa1ffa1eebf42e4f5c3c74ab13b4c8ea1b7a82941aebdfdf8d5128934e39993b55fdd6de9abacc9b5e078e74cca9e07efec4091ba332565afb41b7a655c9cbeeb89ba0606d41e00943a7030870c3a2867fc02740d1a875eceb35ee6c870a9fe82958a67c46b9a12d6d476f29bdd37467d65e6de79c49b48c7df41222d79a4d5767c9128935e792f30d2cb7626dc477d9fa25c6aa826d6cec88463891a8dd8e74307b298dd4b863c25741bee433e19de8e6928c6e83ff72b7d230276dd79d74d7e1199b5987170faf4a27d4263e4ebc825b727255365dfe9559d137aa1b6ca75c82482e1ccf67cd45dd8cf4b58fa604bfb2035e609d8237b45013171dc0b3ad620316048016268bf62b49a0b810045a2e820f3196b8f562b107f6b0dff19bca89fd89f965875ff08c6c2f86d806d65b8d51b10168ffaa98cc5f862cb56dde864a36646d805ca11fed931b82497038ee77739e664a433fe8ca791828fc11367ce203c703854596be703152c40135e0f945c14eada1a655749c5f61cdf08f326c50c469be98e4a14cc4669db10babb25e392f9d9adad613ee0efdbaed9da61995c230c0d4b75f8e9edece2aa14b1f23382148939f93a670cdfacf1b4148ff3ff03f565d1a7a38db2a19891c65f5c7286d2daefa78007ee07f2620cc4a679aa3105f77a107bc227c6b987b847b3bb6d8e7f0dea3a099fcec70a00fc65af610f494fe3282886268c7feab5b9411436b193193fc1edc9b0b8feb829fb39872bb4b9f93d0ef45ebb36f76ade22afbc638e28112ca743036393633651957fee9b280a1ce1c60d5a2ea2ee9428c8e042f33916afa497d8fbc5109d9574358c8a73613798009bae3130dafa0a2dc49828a9f870ffdf6815cb34117fd034389c856ffcd69d76f919b0f371b1fb288165cc608a9ccc1232c71668f1a5f43cd8dbf0eac54113226ec28d807592bc54a8fa477d58e6c591fc80cb0b0e1cc560f49aaca8f4f81fb475ba6079666a51bccb87c71681c12333fefbeee1684c38be0412eb4b9870d6c59a3a1f1e963bf461ad024eab876683f6880a8a521ed085e3e842506d11be5850e144497ee5d7e33051381275b2771d360cf6e6f597f1c7269051d1463f28671a333a5418e8822b6da03dce8b932ce09f6b265515952d1a2f424c00bbd3baa9f0cc7f8dbafd67b75cd432d667033569f2c1d24fd26d8537619a5c4a18f0da0b5d91d353e44085cace1367fc78f6d0b85d6a69e61f60293f94775d544e9832b77619f08c104606abd32b26dd3bf1b02adc0cce117b9bff0eb9de1136adfcbf84dcfdedc1f685b63d45f31999e00742d47dd2b30edb312318c11e2ad0b92beb2dfaecf9b0f86a189788fe34b208d874579b6339a800e3ceb3cbd40e509ae5b12701984f9a59a5d64a4d67f14a9f6b640640c8d4a2836d3a0584e5743a07a482d008bdd761ffc3d5817afd8ede6a0c30eab56803a1ea002976253500407204f06b4e70bac88b958cf933ec048f496757b5bf2ac9e78535a01a4b286e977f5fc25811e122cf2dafaf611b3ae56a7eb549e8c05a77bb374a3986c16db412f5939f46857e14927014f620c3be98058b9f0e56bad9a741ccd143f7b344b454f05cc534b4be23a638f33c2404f3b3c0698e72fb193a0787822e69d6cd0b56d8e3780c2e42c9692b311c47d23215d71f95c1913f244ccb11d34ac69d2592eca1481e838bebe9b873ac8c15bef26c9673bfa4319be356334601ad69fa69775dda99330e38f51a0c28e73b60d3635cb32de90578ee0ddb37aec2207367ca6d91138087691a340fc4918325bc28b29635a2c0d5e5388afda4aaee4aaee7da7489aac7d12630dac18dc71ea697b5b443344d6f7aad9c8a039ae244aeb4cdcfa73ff5f133933851c159a19ac76ad4abff8fb7d588688cdc97e6753e18d17451f50c5777c4ae073ba2bf79162c318a83ca99cb1c23be3cfc58a07f47232489c06f0b205d5318572a9e8d68a0d7796abeb5e2b8ae59ad00ea1c4ec030c3ed928a342e1d213b668ca4bab545b5b49e9a893d645d0a289513f2f96189ab528a5afbded4de998dbe53c8f7f853fcecf45e910bd3b6241c37b3c02d05b68a4d7b9afe7aa6ff3edab969baa14085aae8a920d661da6ceefe38ff6d1479bcbbded3bebf5856989caec88b125692d84160028e592d5961e96abdf57ed2d78c5c143aad13a0aa032f2f9cc74accea45690482044a3d0a8ce16e42124ad8eed95285d1b6af460c4c5204b07df1dc79fc9e65ea5e3009f5ebb26019c11d127b380534ab50a035647dc19ca70a70284db077796dee450570977b223b0118c4b962f40c22f819d0b5cbb7434252aa7b28f0d54da5b1182c0d97d6b8e211c1672ab2444420643d19eeef35351f408ff34d5c0f18ba89c01fcf26fb57963c4fa70168d88a687ae41e4db5d4fe548046184cc2a1f1c0814b02032ca5e522e9c0b2b1e339453b6b83578b8dd15434ba6c8823d805641874bbbcf857d922abaf85a6219c4f207218e369a56270cc205974890e435075dbf68ee36c464232b8d2bd07aa23cd9cf3f791bdec75c28fdd3728f17d346dfb478908d67515772c9c885bb51142c9b697164d764a8b31cc2b180623ae3f3a506d1417aa14943750449a8c8bbb13b9af5629e772a0273e4cac737cbf2a5e1458e437a07d1c54920d470d63e00ae6f1d0f1c2d8d4cb18ef12fa0a35c9aa940eb525bbf7281fed42e9137cd43beaaaecda197c88442fabc93e5497d14d5b1590b4370e310e2d666974fb94c684ed07873d572639a48a1968daa6f6ad7824de26e555c2d1bee1d75cf4e9192f5dd6da38397545814081704e8b452d52a57b99b63721f37346144005ef2aebf00a3b347248e02b7c7e5c368afad75826ccadda52054a68e8ae882f94e1330f50b98096342aabc0d276e4a8edf523f7cf269a3226ffbee48ba9491f361c658693c6ca9d3086cd98cea6abe027a011fbd7612d795b28531bf874f74e035c6b71713cec8ebb2dabb3a4fcd3e5abf0fe32c8c7270e7c0c51358f3c1fb8dd377cc2d9e970e9fbfe89361593450089f62e406b116ac88cf815cd1393d8a894f899f9cebcbab3afaf38b9486617ad97ceaa77aad6e8234b6a295b8f67cc0754374b2b1c17f6042c4cfcd27c4e47d70b9625c2713f1f140d673bc93eb878262a148db4510b180fe994cc6ef3702da9403d3afa29eee51e7121d39354f6b95f38e913aa1e7541e0c294781b1d6678ad054b06de4c16a0813b43487b0b3e0360926065c552339ac2436fd834fd90efcf2b3ea30128733c32a03263186b827c0ac155f0b35a107cc5a9457418133f7802b21e51ac376c31a93f9d9f461f629cdfbb1b5409b73567fbd3a3e4908f0aa0811867ee922e813ed5a063b19138a9c8ea203cb229b27295e143925877899e9db99aff7f61eead1f68496b53d51adb29a3109e46d523907faf778ef3891a27a4c7ce7d23f32cdc77b32567591cffd74521b36e926ff2e2d3d802fa5eb9209ed8978f6f8e84c479879217fb14ac95e290e4c405166e9e6ce368ea79e68ca7a68d58565dc06ab8a43905fde3d2610605899481cc1ab662a202148ac591c117d732aac6689f71f36f07a6c6e53fb41797a8788f7fa43c7109a41d96b68deda7f999088eafb0e3f60b25ae6dce8b985a065ca2db71864eca1d95e6bb947771b8bfb9c821fc1cc0358c0d6f078830430747037a0a4b58c5dcf98c5cfcce0b964ec1ecda65b87230f8277e7b4d71d0162e0032602f6da0aff663e5cf72c0e46645099e6741523b0c8fad3e6d9ed04b49d29be092285f5ecc6bc9015b25522c2f942b6254d4d91920daf6631b797993d03095958223f6d95c121fae61bf88f055e6a7408a012efd7c731c0c2831e7ba6ca4e952a7780ddf05234477037e288881f1ea149c9d097d930879b325fcecd97cef85b8521bcf432672906ce58bd41d045a8529d99b8e5b772d5c38664422b706c003b9d8e978ca01aec69a9bfac44967a23cf3c45d9a154fd3980f00e1a6738b1ff4eaa524f300377fc30e73a4d8a3e17bcf6f5453d002e8391d13050b44305d520bcf7cb7a2f1d8f7ef2084104bf02271a3c4f35c8c56197edb85a61d00932595f2f80952242665b5a4b0a674949169a8cfbd553433ec8b84c87774cda7e5ce1a7e73de34cd772841e765680593b3ce25ff44bd96dca9cac29d8f4ad0bd667e4651d0b4da90ad53766d47248a0e2c551187a4e0baf8a5166eb4237ab01f4bd622c6a6710fda808445a1d73ac4e4e87b2a92a7028b82fa6177722f6a4dbf704ffe3b422cb4e90c25eae43060b639b2980b2d0cb0afa84a010cab80da34874089b51d44f84e78f8edfd7107517b80dd41f04dd81287773ed45feb1540d2e57228b640676b74636bad47c3a9ace751bbc437874a258b4368c8750701d987006b71544f38795bb55499458eb33b23597ad8ec114092cb167378288584b4047a833e1df168560e7bd0bced35be5a69fc9ca508edd26efd390993524a6e8013d435b99ed56c3213c2dc217268557b69182aa571df04ea9d1f1cbc6dd446c2f27ecba2e42f601aa2433901f4d18bc140495a2182d6b0f4acda769cf7f02f3c5e3e2b05026701f620eb0b594dfb4f941e68c3039693843b3e25e2536bd012b1bae142bdf9a839f6cd6df0b3ef8e3787d420155557def6889a0842ba3c8de26a205f74f9cfc7cb69f21bf48a00628b55e1dc90ddd597a96168ceaecf170103a0c9ffa8b41d5fe04f34479746f492b2bc59aff50371181d0685858a141d560c7b73561396eef8b5a84d12a1e8c01ed84d81932d5668232f31683db3c9810516650dccb10f61aa8064901cfb4d71e700aa884b28a9c3aa85f4892f5be2c2a6983cf86573f07c4464c798a0fb176c94a72c12a6e3a1da2760d2bef5751dec3ebaf7a187195725bd98137a974865f60bd37dca69a3c415508a4d031492c8af0d38a5dcdea2d53d0adcd4e531e0cc7304a5b817e993f8ed1af22435c7220f407d6c07dffab085dec9fcc1031089a9cecf8b81a633860cc40fb5e57908b7e95fb86518452a6ad52cb1913cd0f0684ef8087dc5b28956e6d8678ec144a7ff27a2f89cbac95456ffc7952cc476311c8a7386215aa9c2a00b8017ab8019686ef1199658c2043a77d9e8306699d808089a7e559fdfd774e69f5252f92a25eeb589eb5a776bcd570a9da75ff6821d3af23e7cd00d11ec1cd2aaadc01533ce90f1b9edfc7a117d3b3de69bad67f897ef629ef4949b5657fabebfaf5960cf8e8911451b2249b7721cd082a62d4ac4052d4314d97579b79f7b7c620ee8e9a761e63037ffed606b7409abb81f5dc54a0bd3d4a7bba9e75f9fa6cfb44e13b29a72c81bbb792fa8cf025f989ea847c94a3486bfd39893d34ad6557a8557db49dbfb52b4ac797dd45d7ff297bdbea8e7f4f128c186cdac8d5f7cc91969e0cf4be6468f76dacd611d6ec69e082c1f363a85556aef586f4272a57642fe6d68287729509e56a20f5e05da529228ca40eb2cd2e3a21e210ff4ae7eea4ebfb84be4e71f2f2d82f2cda8273b0ccff732b57f6ce486c8ada3918c2b2d257f6a2ef306669ee92d8620845dacc5eeb480a97b004cbf687da4744a448b821f102d854b342aeeacad9f53cc7e4b1f849adf2c22f81616e6567097eda3212b95f6f2862002c0695e092d47db57515f1370c63f4e8590e0b4a19f82f23f29379b06dff24c9b06ec524e97d0efd4812c2dc3a92e848e6e60d9f295bfbd73b5206286d937783807d0def12e8cdfccb44a173423f312b29e4aacc08d4ce3191806cf29f44c56b45ec0c9cf4e2ff6f40368db3372586eda91a0f388774892912b68309fd91cb7a71645e236ba01233c4610d8650543e46a0c08a57e07fb4d0271f427f1d3fcdd34f1c35dc71ae60d1f360f0c081c5dc69d8cfa77c92e6c2085b3babbefa4b90d661ee9ce316ad42098fa2595b44093351866ea2b05ee5c43ed5820e2398c7f6f9bdf1b9a33cd9503584362285ec2ef3d629bec2ec68e51a99733b6da759aee50e751725689adea4cf1d0a048df97ae847e7b7a797e03eb5492190ee94aad8d3caf912ff4422e72347f62e8a9e61a8eb50ae3719532b918a0a7f9caca0460e2ca6fa7f54dd36d52ab02d21e5ee081de184080ec4f7c52ff28312e840764bc2cf15806b0632e325266952351590c014d3d942221e23db88d788e75842a279cc794adf39df3b7a202955e799f1614d1b2d8445f57d8de53a108a7679f90d3a32200fd8f0ae0b2def41eccc4ed7a1ac75a96369c9dcfba34ddcc60a46fd3839ce0a95e1c630cadbfd574e4cb47f9384743f86a96fba4a798c34b9c5b669cd56d609218864093ac06e2f2a1400c0c0c32a97947f460882a75e863c6c6c0a22971010ff023b90a655a770f24c9f170952a613a7a5a0a04549543629cc687f8a75467bc80803075b757a76c32bbc454a293a9cd393f2ef394cd0f94203cd639182d0645069b418c9c09373099631d4eb4624885dfc4077d68076078cadd48652b8248a8c27ab2ee90f446f5bd1af6619ea70edf5d0289ab39e29091cf25f48815a8b5dec2e71b2a9c9ee3ef136fbcdcfbdbba602cb8146597ed940a904f5a0d7e485bb374754d7ce8515e949a15304d09d6c3038f35839f6271839985fc462bfe776478307b5525a4c45e4dbf3a049b81380bc14e9c0a019d6ac2c91b33f2f9a5294d35f991da19efd9546fb3aaf5a0c289e4ce711ceb96ebc3d131b4afb9b7a2beb8ec13f1c96845f5730db75f02a0119c222cadb4ea41557e1210d1fd4d16100054717bdecb0f1fda006c6c90a17554633b4316929a20e079d344749b0a26095eeb84a293d3441228436a13ca655b470601065caa44977e926adb270da146bdc92407364de277070b070f5f50f11ca4e12ff479ab540b3650a95579951335b9fb3c409d44eb409a2d6c659a38d1054190c52e0cb10feac4f9fbe41c332844f3d61ec5a662acc051036988763d71b9e223fbc5a1df1319182b8935299cf53e97be074194091331340bd90f7b98a957b213d731d0649a09c11150d882870706bf0c447c167c71360745755990c7339a7960d735d6369a6db0de157df5fe2019bb43e076cc5285caf2b02810408d205692701304674d0eebce835302e0563d5bd5f9978e9e794b585f708aef2bdf8dd2f3f8d911246f4249c9a14b24e0355551a60e04d74c91589dba72de2b4aa9ad0d409411617db9985e1f504569885fd7307948ef7f51fa4689ea3bbfc8d6d7cdf1ce0e08aceb5a28bc69fc5cffb85f8451ebecd379b113ff6866c6ef77c3b97c2bda6652d07904095b0baa1453b7f41383b07b92c22b7ba194d5efcc3097ba47f388cb51c58feff61a485f6a83ca99d3a4d3ed854fb01ec84a5bda15b5bd7059ec0252a8a5c1a4499df7785ab296ea2e2caaa0259055cdbfdffb607e0b9fc9ffe6033cac71410734b659dccb0966536dab7554d131b19a0d4656020266f554c8571d305b5187beac947f16af4cbbb44348b82cf433d4f3b41795e400119ff5172c27fa5e6552e6dd30a54bbeca7c93f47f0527d219896df772f1cc0026e20b5ecff3b2de49700da8a0bb4224ab9058c1585bb219632e55bbe099ac05389c7b855a84203ad7261c60b82aa8169e1d6ac15cb25ab4268e88b96159c9f53afe2f47d56a16dfd4f7b9a1121d8306ad7dcfe25fe0371856dd225c5f8b58bd9d38913b688b79ff3f120401cefe66d21004dfc98fcb197e84e69c2b38a82d6a4ed051b767e701d31ebb7b66ae5cdbf7836b6e7fbd10650a4881776402244a4395693170820e20228d5f9775fdd3873ce7fc002b7ab838c8f3f364b9c2021df114017691fe895bd336d4b7d68b37bb00371224ba206f2d4924b0900a3d65cd049eef9a9dc45aaa938e0f64a102d535f9c6a2c29299e86920d2ee9bb454cf084115589aa20b7e580fb288de84b60550dd3939da5695c9f393ddc54d3667661127a58729adf68381bb8a40c1fae7a194635e9be39c9e9df712d979c9c3d0eafc1c3b525092a16fab21b0eb75928123464e00851476264a89897a029d1077de3f39f6f9d8d1b4aa0246dc598fdd45c861d1ab4dabf22083bf8aab844c0232eb0020a1234160dc6a3ccb285ee8b8435dc8ae9e94176a94119226a111f17e9833d6b3bd697d398431fb0cde5022a2974f17f9b384ae6a5d02e22055c384251d8bb55d61e9e26a8f1aca5e092f0404b3c74a4f40027a48495637c1c113dcd0542c8716e5f8d06b2ef38372b9098aedc55d382f449613be2fabd10ec1c34c44b4050de1c265af460bd7319631dca0c0deeaf0cd8e1ee91deebe88ff09b60a437895d789d580a8399cb4cc8a081e3d836a1bde3735e8f1e417db1160b1dc2571dd264f5fe46e77b913e82a90fb80661364182520bcbff2bbf00f7f38799f6228a48a31f8bc6a431e07f40adc616a0e3d1975bb6deebaa560e5f44d89378588ead1f726bf403812a143d0d9a9ce58a8b22ed1be57cf56fb3fab11db9d16efccc0a6c24c01f57ff7e17bbc4df7d051c1f7567d2d3700f685326feeb3138ada41e3debe58d3344a134e3f5a6953ebb32a87942e33b689aa86cb86ddf1ae784b0c405c621fbe37420eaa8f9acbc7aa032c9c3c43097876a0d5dfb523e2422a25bd2cd5801f7eb192d77f589046605a549849cd8d7f2ca4b3be16860d317ebaed97c54b822d4394c37d6777f42f88bfca93d34a95eea4d06c4c966824d43a2ffc74ff4e4042089991f4744554c026e93237f8e82472b62ae7de720e4ffbf017b7155bfd38b1eef2b3126df50fd0aa30139d24bb66a7f98b9a13dae403b7bf86694c2fd037690cfdb803266fa267c7add2950a9bbef00ba0968c96b1fcad1ad7aad31675725107102740f8c0e4e7c20def20ee14042006cb6ab5e2773d5c0c41585712777a0cf460139b1cb3776fbef0d256f671fb3d9621fcb0bfd952da8c065e78ba8ef02b2effca13bfca47b7d59757eceb0cde99c2fa6b2fe25e0f1dac97788f4935951f24d7bdacb1c35e186517e2eb53636eabe19dd2e0c28ca786f545699e0e1f7bfef01c4d7fda121d2d2b3a2c0ab53ea8379f7d56a110cb3fc7422b38102051083860460acb290868b73ae5dd6d2bb0dd40972405a69d6e12ae914423931523727ed7b80a59d0151a97d6bb8c1606bc996d55f85e3c6d420f32bb5018719f2de79926d5ac1e6e5bad2151065e78b79f019294fb9ffc5d0e0a969ecc359013860b28d7c82d66fdfdbff3c596bad9d7925ae6c134ddc6523226c93a7c7c04c5b532d1453921b83875db21b50e1389529d9017a5677d80afd35474a6fb43e6f8574c96af92a62a02402786348bdc99bee2611bfa72605fd943e9a5d190d9816eba95ae39b3014165b468e24ee2cf7a64f10682093bf80724d7c89e9e032df096b29b314257643bee3fc16b1bdafedc3954b6470b9a9bf78920f086dba2dbb7ba0fdf36def4c6c661010fe69c0d4b3dea39697a2d235adfd7766c98c575cd35e4c3d2835443c568e92e2e41d17a7ba894a3d7cf3fa151dd1bd315e4009573b3dc88df60d12f0dd242bc5f808c0ed98b61ab090ffb4756b1fd5e4050b2a8f4ff544dc58c482630d06d41d37dfca3ea623ae7648fc9bf12b2fc515a7dd6d6c1cdfb89e4dff567d1dde92117310897479b460754c6e10c8d6cb4263ab7525e8d61faf2bbae03f7de742e76ed07e66678c242ada085e8321c6c6e39e6f5be2c37a54f449afd47cfe61ba131061e558c35c1981cd69b602663a835a8a50ab7c96b72e8fcd1888aced770bb9b7806b5e1cb64a4ee778d9f9d406aeb9f857b5b6365637b89c44e4bf51d4621947819f9cc8918f2bb821522b4ee239fd7b6ca76050305b28499415a997ae550e840672a4c423613e21c028606da059c75e23c95ebd8d0bde0926b4ab5e8bed57634401bdc9823f480017a54580876f7fb6ed58dd24dd7ca7494c01a9143de1ad6e644674905869cd7488bc28f7f7025feb2a42c02a331c9bdad0f25c7902a0225324b996c47138f7e2a4bc9ef8785202e6d1d0062afa9384ddda16295baaa6c841dbc18bdac15292c709db5bc566d7edfb671d9af6dcdb6dee1907377e4511c5e20c352a19999d04128065c45c2eaac909afa12a1f67b462e363fe1b804cff5d00076ccc3fffd33fc5f340819176c5759e748c717f64fc52638f10681ec285fed13d0ddb23d9b069814d2ff713660e588e6d5ea8b41014d179e81c1475dca0021482d1fc5afd9befeb007a9604be53b284b7ea157ae9d428be2a78c5d9945a047be4b92b8826ac9807f284684bda545209055cbd1fe0f88545771bbacd69e6bcc4d6b813575b15174bad32d676c80d34713204fe06b0066004a414345f88bb6f1d1dac3d9e7028d060ce7765be9388a966a9a1b996b93bb8081ab629553852e1e14ff611ae4e9b4c25b02e9c6b2ad823160f5f73c257330e7cc10b34ce3dc53c5d355632e55bdd1e81959f726cb225b61736555e059cd6b192164e9403ae277410ee5954b38e825bec398ca2cad6a9553b0237e1f25cc03ff7143db1ac64a320fdc9ab4323cc93a49fd2f79c23fc5b9e1dcf98255fd18728812b8428551baae5b96b322c8555db40361c2187b3354efe35996fb45e29ec91f6522c81b5ca4a637fd717e88aa66325c51fa33fcd7216045a1e3a997233d60e0eeae555642731d95e203fbcd22b7644cc49252b8d4f8a588c13cd8a2d1889196ab96b97eb2c9376540fa936a965d9c591a3d87309e59507fdc0607759a94be9cd2dcea2792fd18f9a73b737e93eeaeda4322277ed8b07253b228b7ec857c93c1d0e8fd002fa51a978a6d80b3fbc44da125322da9cc10af5ac4cf01c00a77d476b94498c163bb4b9c37141bf6f15e4839d106f3fee4c204b234d787d53cb6e264f428a41bd21bf228388e6a9061ca3df217754848e2889cf03c4641af379b0680a312a56f8c1c2cde43cc22f7cd0ca0f1a21b01ca73d525ec6b64d806b919f96d7b9244f89de500532f6f19c2b4268cf06355acd210e516bee43007394f4a7d50b8fda591fa2837086fee765079b4889cc9525847dcf8ae10790e7bf76f2794ea8bd0e7349b47e69afd02a46c940d0e686020480526d828225f17a4ab4810cfb8259978968d0fc77450f025ef864ba8910fcbd6e57010009e384c44e6a008429bfe55808a7eb1d533aba44e829681e97ebb3677ce42d81183b3e1e8f449c2da1a3d63ad74856f42786bf29f3194b92a2ab56f99f830abd788de15927e630821d2bfdf950a219252cffdd4d63878313779fc8c5bda8d36b0257ac7a73ec4d07650163bd61a0fd6b560a2af5f40cf9b831612c348ac23487d03609cd02f068a573910308734a811b8347e899959a81b56b83f9ded77b24a657d526832b252911718fc4dffebbbd7ed9db85a9a6b555220ce35470a3ebf5d34bfa999a3ff7317913e7c5d7734d931b28b358a9eae679bc084f5142426da75cf5c0cbd7f3a46362b8b8ea385cd10b2b915de2f5b382c3849355a17ec4d2b5c5af38565502d8802aa886279915bc19211cc51f3e67140c61ca97bbb27041b49355ca7c0c13d50ef232f666578d1430a467f81d5d33f83c8af845889d9547184e88ec575dd572377052c4b666ceda4ad82f69526b99020bf9c73be24c8fc7f31dbf6c309c3a840367c0336688ec9cd802baa88cf2261c6e373a4657a4160987b31cd22c547a235fab325f6737f24efad1c3b514c61806f30951f3e3fead506908574aaa0da4b3721093a66d87c1e539c52fa4f764c0d7db40d71b77e0b28b6bac488ccd936c82088a83865c71b975bd66d89073243334d1731079fe6c9cc26dac8928dfc5034b8ece5424f39670c0617a253555ea61739ad238e0765ac0f0e8a487f5aea5470afcad91b89f02770b195e4115d86583805ae119fa50326dbbe0c29698d948fef38b648f6016d6180ce22d85234a7f388d7a7291031a3fbf7183801d52fa1867064bd84763344ca01771bc99ec1492f774be359da2a51de95cb2821aeba8edd342c06beca3cf5c00b448f95deea16928ae7fcec9d4c6de7dd6978cb0a96a98e27492160d49c68364ee2a8c23269ac81c835ae7a4b3928ee50131331a501c1e6bd21ec8bf67dc02652909023f4e89a12db466050150b569e7609d20129d7322d8d03f6ab1f5e4e78f1049f20ad9f83d789c2ef75ba4801cdb13811ecf3d7c958da6c43f01b302f2fee4f524341cd48ca4c229f9a64019397fc4b7c96ea636af3385b985aa760444e1f881ef0fd4671edd538c42fa4e598fdd30277943677798c66680664619e8a7409d5f6a9a5ea30b20eee7011437063a3a6873cc22dfa8c5c50c8139f0147d805192cc83ccabd9b11f31fef820c270eeee9d14cbbe35aa9ef55887767f4ab0b7c82e3307917cb594f05149979ed289acedd2b01e9aa10ddb503c1cb5465562418099402763c6de2798c5828a523826b13713e014471ae9245c72fe40deec0a0e9c031afc947363de3d872962423c42a915923539dda4cc99370b1af066752a24379df8444c1eab5842c5687b93ab1bb687f8f31b2b7ad24cf9ae0959915036c8b3a25446c575ba384714c1481cefca4e1f79d1fe274bbeb46bbc904529acc19182de472ae5263d0fa20589ac4777b775a4a8bd37b03b2dfdfbd4cc87eb03921fdcabaf54c53c4a672e6b63cf4d711b1b8173a348b4aa118c53749bfd8a207752bfe353b7c1d59434c39f08ef473235c1d50389965b757237558bc823e910903f758fdbc6fed08d5d3f1788cbd272dbb172715c0a332c0a3b0ff1b6094e1d15462bf0293709f7fdc5cf21e16980027d285e63a1de9842a07c0811d19248920ffc6a66a0675513686526b645972d71235e1611d92355d00911e28450a5e2e519cd7e0960b5236cbc65615b8367409b26565714ab4bcda6c9230e1b7d93f37812d2160f114aea3038fc3bb37a6b2f21d1da5a5ffe11531c414ab1aae17c07f2395f4eaa12726e4d24090b5ea8d5bfc17cd7921d29071f8e86770c13b9d21aac447505c9102a675f9736dc9104268776589919aac2ad2c516ec2bbc029107940eb6cde51c654d0fa21d7d41052c182e966b46cb2fe00988d03e8b0043f2a22d2edd0c894300e8a5638c85dddd90db317187a2c21424f3c796a1d6ae0f6b9ef5576af0ed5663c5f32cb8784403dc5e042948ef870dfe81470b0d9eebd2ba6e1e67f2740fbd7cdff84eda151d09110f1821026b6d09727e8ee9e5e87e645a0dee9f056fd0082372ae83c914f57fef820067935150ec28802f43513880ff008f589d015e0fcad58227996a99347b4aca2cd0b8bba4bf7dcf1a8ec394ae99fbc661477de6c7a9fb133e3d7899454d3d7877a6d6086e6b5b1f3c7ebdc0cefa0b9215dfe1af6056171d1075b04147912c77be618efce9404696fa6181e0181f88582e0e979986591fcde6723e1fbc20d213a18e6286e429213220c1a64fa451f545510b31f5b38c5bc4d4f8bf578b95ef1e7db2e5d8a1aa780a2db988a3550e8dd0fee2b51121469a4006badf8a7ec465bb564e8a05de8d656903dea4d321b61277f288d846af9efcf6c3c76140e4591eefc934aa1b48a89e69e959440eb32e88bedc03293cc92e9dd5f4af491c5367a7bd57c56b26faf81b51a999ecb5d76eda16dbdaf2506101687191af888bd91291052e5aa7dd328c1d41e6a3d463079455a916598e09de0f71b89991de0a80d130e7e034738f9537a49f7c5074cebd5e74237082675cf68a6c5a0876d234dcab4202a8e108b82aa28e3ae14fd0a3f880b9643cbb07ae504da7acc246aa54ea442137b9a8a58107fe688c94f12509816093873d697879abaeadf88803873d044bc7d22f0dca59c460a9e70fda88a9482a1c9dd4a8212e1e2940d19217ee65b5f7f99d71147675eb8e9a12bb14dc4d4483b9c87f3e2f47b4c989e89f00b6688de97911204b05d86cd211f09230ae7f0447ad737381fa44d6bec3329a1f95c9bc713e610456d2a7e9ace49a2a1ea35efc1f7ae1314eb5f12ad1d6449994da29f07bf95d86c907f0a71e0aa0633b2218a856c459c478379aabcf900e257a5e00c26b80a320669223863a7a9882a2cb7f555edcb9436159e905c7b95c3311f695bdec1c14c6571e0619d4ccb2a91a1bb436fcd60369b1a15b5b46f7b18930e67feb904e8d393a6fef2fc555dbcb205ae39c07b655191730e939f9d540cab8e1cee72654c1de093d8c7a3da5e69de6fbd7598d9ae70b8924b5b04fa01f24c0bf2cb70fd35191017d41fe907e5446e817dc6dd91e05bcabb0b4c0197272fa0afb120b5b1e139a456c19615fed77ec91e36a8dccd014f581c2068c18d22705471fffb7859c8d29095afd40895d6c0ea1605532db814533d0a9c4d1cce454babf0fae4152b4d50d1c351673bce95c8bacd9c411d597fc21699f3386ef94e8d949cf2dbb69a54d06ac89859fc2019e59ff264b4e17f6ca410a10345b25f4099261295aa1ea5099aed44a485b9587c20466ea4037f6e56db3e4983320cc6a4ffaf3fc13fdcfcc55712645cef0834c77524d2164287c9916b23f1438ae6a93d11887c0cd64d5a675355693fbaec490b79555f10922c786264b030daaa0ff0a7f6f396f38ab34de6944846016d6f3a4f6d05dc1a216e36a3785e2680d50f50b108765d461004ec113284578c4144904db45e999c9b1a13f3ced2fb4d412fc1284f40d4e7be206a6764c31f94dd106922cfa07a421babe2f514b0a96195b2f0e44b62028ef114498aa74ec08d62bc01ae226e896922c0986e82e04bc821a6a39e7cfdc1c0cafeed2896e0058f7204dd4e9f52daac0b39258c6f284f551d4da346f402f74f2133e79d2f3a0f8131bb9f80825cce900070d73bbf15a9225e1ad8b4ca82480be9b1993100d34ed8576d193be3fcfc520032f3f9005a9cb63a12776970d50ffa48fa8ffbba0e39d3b2e5052875fdcf9c0b33d6664003e12f9a804cd59185057591ee91a428bb74445328569cd73e0ee516092d6a2e6a92d6892b5ef496ac76b2b9aaa66c37bf2b0522bbd6e5fdc7c014d0afc85df48743469e5d38b1583100237f5d2efce1c26b027c68f98a9930b66306f5febb17b7dacc1b254c977df2913aaf54b268d94b6c264aa69b45b5d0cbc9eab0853f7c45115206a317b430fcf52b7f0117b2f82a1fc24d308dd34b3e1602f2f18a81b5d44d87de42570904bb2e53c3db6e925c2872fe3c6f7db0f7146e60085508e7dbb10ed74bfcf340933bfe203513942f8e3f1a0c8db0171c7f5a2e2129e94fb55695457fb1e401f2b45ae25ef267102ffeb868fe81d49c145e6d572f87f1cef894a60fff6a1f10e448c6f3b29eb2a9e93c1acee48ff4c0a334830d038605ea4df5c6208150dd72e9109076d1b4b43060a2f471a361901395b926a1a00c61a18f544a6a621d33fbc1b692b3d2a4185793bc7b2ffea5f4a7423d377662eb7b58633486dcbdc0546c5e1d2f664391de59cb413ffae420069c4fdfa73ad924a6d22a20991532c1bc2c4a33a362422cb0a59cf6ab4e3eb1c575eafe689229c47645697e9a8f1b967fec08baf1b9a676c368b6c2c0f7b21d2e21e33c20b69ae1c52a1226216cec41138c513296a366caea6931278aa4b3fab2812fcafc0185b6cfe0fb7d671ff5eeb182801bc7acca5d0420bb182b0a21670d42d2e4de16cfe94dbc071d67b365d8792c85532becf6c773b72929c5dd6bb9168e6fb91b050d10a7393c8a35dd6bd98fc3793673c3e520ee98e29d038f3537cfe696694cdd1baf92f31ca7fac40aec3ec3cc77860a1e21f603b154c19afec0ccf20c61dc0dadc4ebacf16c2842b08b3806bd2fd409269d0c11f5f6bbf169648a94907f960125ee1038cb308e9561ba9756207e4edca298c0cbf9d5900d1c06c3184c79106c115b0c07411821d82b1fef15532138face1e70461aefbdfa07b448c3f655d48a1c0032399ba6cce3e41413861a96b1d9083a1857d6c973dda4d2e1af9037c0e1c6d70550bc0c030f30caa5f1710101969a26634761ecc59e447129965f92b7351d56a1ca957d43357423500d8a2d1fb4889af8de60fd294368101fae2d073049f141c607180fa13d428a4156a230632025264f800680da6237aa20b2c9c01d4b56ce335cb20a3bc822d3ecd0e8bd1aeb0028ad6defa59d9ade4bb35ecc05c6f790d67b79cd5f19a28ee4a91daeb820bbaa2fdfdf2d1f4c5bfe9da1bfadd864098abf095326d039b0894686c920e8f9043682d3cb2bce4546dbe15d4691686044cf56b597726ff4ecaca4ba6314118c43628646489c28ef835aaf468cff8abd0b901860fe1985f389a00fb497411d222d378c77bbc078b4791aa0713050fab7d3ccd81342ca6a0df247c17d58d23cf28ae8159304eb869371aa050d812e6960736048b83ec07a914d7718cf278b7c068a66d0129e263059f4b328966f539a81dd0135c423ae7cdf0b7ba1d938bf28a30633a19cf9cf78e746a1dd9cef4cb1ca9a1d3727c4c8e10376aadde6b89b1691cfbf08df1836a4bdd3ffdb0f37f9e43e784005261118722c58bc27da509db205871d2ce4ebcfc85b4e65c09b0c0fe85a4b6a44efc042b479b4b35e36154bc2260063b20683bb1650eeddbccc16ad504e60bad5d9c92d4edc8fa0fd8ec97d88872ff05a095c98e3c1b09e56032bc70ce03c9d43d4029721aebb8ebe7e3709a444df7354e245250ac15947eaa6e4a37497a3557c28d17b8055649fa0e697ce362d7c25b77c555823cfd752d1fc7d7e00b28be2b8180db588934981cb2b38a24868c935f20cc9a756c99812741386c128e90838ad450a4fad607fd8f20247f948d27d7a4aeb1a14a846d5a71c58c3de65bb1c742028a11a625266681239435e5343912ab0fb0bc681004a2dff0484b5e12fafab0ba1f232e792d80cf547212ee73a26507baf6428ddaf965739c659509e6bb5033deaa5215619d859abedb1e5f950f3f175f459f89966e4932af25991428984d65a019cd5804e6a5fc78eee5d1b3642c02720f5e63f5cbf862199470cbd337b184745f0952b897ffa2a13aebca9670346101aef83208a9dc78507e1e384f819942c2b52d9b666fffa0cd9fe3932d9321a8212d13a477d15a8f626506f389581dec688f253241152e3419b6addc8b71e740b88d16efd5110d49c6d3e10137fd29ac09fcc9c4bcfaad4d21d3eb79b627783e0974b60c6b596155c23dd0ec5f8f460b3aca2cd0a06f1a55d35c8de17283109662a95519ccaa8025dafe66cc8c7f49b8fee8cecf0b391ef5e3479cd78475864e20b4404a4391fad20964ff3d1ea8d71ba54088eae9a8d8fd039af57b822babb57aada52d1082742cdb4737631bb1ca82a9b94a7a178000fc69993bc5986c261cc8e807e959c0c7eab83aa40fdfc5fbefae0d88e548c3ef2865c85da3c66215fae3505b7d2687d41d8508b6abc427405a6c07048b35b5a9ca84b6b6e517eb69b0771afef1799869228bf5cdb9282baf4a360578696c1ee346fc6c0c2ae81670813370ae7799e8c9d49ace6f16324048c744cba0b7c9c464f41a79c6b4a24b7dcbadd149890faa1c5873a04f41a808835851d4793eebdb063089a6bf4e199f513d09c9df6c0794f6b1b21e766d3901b3d20e479bbad39813560f46d04d02815d4878f223558df0d5424bb24c3d53a0734d9422c43b9b8376d8a034c2d4fadfd40923106d2a5c36830ddd8a0ab1fef23b2395661a819c6e7516de828b65a4fa35c44dc4effdcf2ce1b9a0a51ffc0fba4fac38df64340249f3f1143c043434adda06b2d28b7ab7caae591e1627e15dd1146ad823443e26dba753aca6a337db493a2ed4213897844af4c32fcbe43eecab0e48c30f8112ef40e3fc6ce02f4449b19e00965425901b3f37b7fb86379e5696adebdeb281500cbd6d9a2b829199fd1a122811e2debc7db72e6f1daa78436d3f835b8354abc6b61b071ac8855c931e81e294a776000c92ab2b33d444f2e4eb73cc4b3a66529b6dec7330e3b21595c731effee27f52554321aeb655c425955a73bcc8d8bfa1d07fd4c8fefef4282ddd0a31143fd3e585fc2e236413cd0d1348c1990b3434e7701b5cc7dcab996641a61e7792e1aa06d20d7bbdc80f56c5793375425e3883886a3b03adccc30a9ec3e790d39c51b546852df7e574bf6246743061920a58cdd236cd66e9692c1b6b7a75365f9941a595c82e7caf1723f4230ea5a9fc68fb2a3b363b3959dca91e5b6ccbe7acc5a903d59fa8315992ef1950e8a30eef7e6f2a25c686dc4da30cf594d6e0df93a7268cc6aa0c8b72891def188323ca19c49ccd4127012ec13f4b7609bfd058481f10a80783b8adf0672a70e88270ce4ac15fe4f5ef1ca21c3620a21ac3c6b182094f3a0cf0589cfceea8d7a0bd686efb16d447f02f0b0d03f410d80b8c804223a2ca8898e71be342ecb150bd192f94291cea2094b8ea94aae3d09df2caa02c1bbe0f45133a92f56d0c76fd3e4121f208a96f224d7881c8fb35f3eb1cad5c7d3c7090a36b67bba9e1e45ff6a8a931408ea5c3c3e03bb10e2c7edae30736d32f0e61c9a707c448cb001e3cf7348cf365d75ea11143d7ae2383b743bd5cd928a15eacc77ec1d38865d9911e051361185b29634648ecfddef365a17b887417fb8df6ef08dba119558bacfecdfa187f156a15b305cc0ed57a35f539d2a4217b72caa071a6b7b86b9da44a7cbe63ccc5f1689ac242e808a33647a1b88835c888388a75522b9b19c935285ebcab2fc51662044c7b35bed43232ae81d24c0422c345ed2138d28d13d15526ebdc72d40185d7ffead962123319cd2fb8f8178bf2dbe8f5bd57c538fb75ae559aa1b3c3d00c33cfa2a1380f5aab76aee0d3fd7b22a68ac99c036680f9eeb49f4cc55b73b266f15233f309a4b3da474e8e6f3c2b69953e805efd1c00614d1715a6483e29cdceb49ace23348fd9d069b9eb8624d28fa35ad36ab66acce3485acadbf561d6587957f9ec67351444bbf631855471168d16ac50a7505d418f0bba74257c933a5e2c596705698eb2db321ef0bc72936802a16dbe828651d30887f41fba99081b1e008b9a4d7445762174128f6a249688b3a6a740097eb085101b1ea9201aee4782fb4cf19c0a19442715208508000c57662846233ef21002788e51b32c42c14db9c9ed8fbca513fdb7e0c3db195255f707a3a5712ce280a6cb34847f4c46e6bcae017104e8c9295e7fafe514fec82e0525d71c6fbcb8d4958b050ec1d2703558708a3bc8100cd541678ed5d7b3198bcfd848b084d17f02176771a31586aee3e71e233d64b683d3d309998587bb18b222cd5c2264a5f64b2776fc94e8050c383d67fc88139b71f8411fae930848e4d4c36137f765e184179016801ba4200d025d4c774fbd0f1b471615096158d2489a35f1b3d2088811a2778da9c1d254cbad9ed1aa7df2f2307f093e245995f134c24f87fd77ba7f753951ba29d7ad619e928a9ad88cc735eb45c9eeaf847a154dd20522497e5222cf0a401f1a05ae76bc67ab8c1ce4ae9b8ddbf1d3dac7eb640f614591d651ce3bb728c8950b47b881e724fb7f87366dad872e27621955d54b187132cc126a4b2bd9f3e80240bf2b1ab61dccc6810010c4cf806e454c1c97b49e69148968c39949930f9d7b75b97f082afb5d10ef661b635f86c3a1fa9feac439b43812f587714aa837320eb17368fab1f2bc1279d324e1f1035fa5d27dc7797b70f0ca99af853b92507d2eec74a93755b4260bd54057e56eeae2dd85777058010c8b5551828dbd4d91da9809f7a27a7a913097dc0799e7f21d07c440cb5e8f9bae2a3bf69faae66d33a91a33e976bf09d99a5f49cced374fc23d66177f8f2d2fbfa26f6e6cd5d72a2cd236f7d1f8220779635e0c5e430cc3f3d23b9bb6598045c1b80b4906780ea9ea0f7e5c02f76757972b01bf50cbc12b1e3eafd7abc0c8290e6b7a40a495ecb49f84a1357f6f5890ca3fc076f4d78eea8b9c72c4dff2ca3ad1940efac082d66d896bfd83697d000f9fc3112aa2cfc1f7bb4ba1526a52e9a494cd1f3bb39f6a93110a802a939f35249ebef67ad0bdeadb065b5f78d7b7f2572b01d7a96a2d456360f629d07978cef05e927e415319287aa278f584afb4f4a93df21b4289e6ae63ee87c364fdbd16cc7f15d0ccb90260f1177825b18046c6f6e07c0023751022879e58eac4d63937c0f61729c69163d46817907b1389dc50adef6a116b0780780b0b2c4ae304f41b5d57b44aa2cbbeae29ee4203369bae657ce5b309d097fd99ea707ae1244463684050d1da578a9f18a0f81260c738026b97de8040d229466e74b6d36d0ec350b3bcd9356406433f775e6179301e0c09bb375dcfd0c82114186c2a25b8006428421240295b829c403804db08a39b63e07e075d160405310e2e334a7ff40ca532a0f9402842c8478ee891b6e00355c962662bb74e610514f49bf1a81922a008552603d56fdbb84113c4cdfd5af59e4d3b1f03043146e352ebe9a337b8d220d54244cef0fe16e0c44041547547c45068055906b0e7f93704b0d11aeeb34e841fb9a2be65f14ca7a041c9e5646cacea8f57b646b3284f9dd7decef05f13ca3165d581105cf00aae58388b47c385845aab128decb90920ba0a9261cf36f843a14a188954e9badab7a44482c8fcef2253cb0ebb12b5fb27ddc8ab4f67510096a2ff9f9e39a3daa92f10b82f125e7b4260aafc7e9b70c253d8e7f9c913fa3773dda362f358bdb27efb6646be3b98ce499e868f77e37a8e3d73c92828743ae50ed8418e603777f29581e54e1d94d77fea7166891d15962ac6541f2edc022e065bc48912ffe0af884bdd58a8a728edf9320cacae852f3e09813d17eab3ddcbe52074132497c7b53e97f7bedcd6be29553a1e0e100a2ea20526fbd73c77885889415b1ed87b600705a732785d32716a37475a0f44dbebc62a2ccccadf55045aad6ce89d4c39f1a82549dcf18ef3d4d746bfaf4df5e68a5d1ba191e295efadbeed2bf3ae40c1b95d52d75f1c020db847dd5913b72c27a39cf9a5270158b9e8505d827d89eadb697c0a6c30655b7ccf717affc7f28771a33fee3bdd7827f810cd33defcfd34398d8f1b617074c5fe28653541d23c71351a0c34957fd2db64b7c4f61a7d849e729d8ba1af9e3a4bab4832b066c2709bea0dfd787aa9c8ca31ccf4b12b2f12d037575cf998805428f7e4f87d76355c0ebf0dab85ae103f6bb3cb72e5542a94ddc308d8be13abaf0a94d999d231de1f7ebf9e0c00c82e252223456b1efb7df10e5790c0a55f6eacb9ae74b6c75c82ba0b11f4064a2d846ff9c12447908a27ca2151693568b85a160b95a1f18806a2456bc2cc5c8956059f1cb099618c2a993c8c03299ca5ff9ccb223661356a1322608c1328db7d163fd18160fb0da171220072513e70587d4fc43b4fc010700e0568bec4802ae885c483dbe50ec7d765139d8c708e91d97455ddc59025baa5c5d100ed5bba9ea645208b8296633b2e9ffa0630a5bdf4203ee17d4a8ec2fde6aa2446514a91842e164fd64ade9cab81e122b6714d100619039cb0869fef9eef8b40b2425d22272bd67d1b66e8f2aee30a7520c24c31a42a6a1eb0628c3b9f8ca67819251e336f75dbddfa9cc763c5d21ed8f2c46cd57955a432aa15b3003c246b8d18d54592c91f107b936970542183e8b5150df7c7dedee1a92185d09f9c943cd34698b5f5a1a3c8be6e3d5df58eca1184cf96922403aca94f6a279d5a6e433a494747e5ef35ea2e9d84b5c4ead8441f99f2313ce13c360c78896965f9b211c8e118f3dd88c8ac69b3281724564537aac335cfe516c1ce68d4de2a00bc63de83cf81e563e90f5a3ef4e386efbbb55c5330559ff90f137350557d2a9559ef1649dc247088e897080041df087c74ac3a07c3a21fd55bd338f32512a15fe8f4fe5a38cf2287a1556212ad86968e99b7ca86b245f5a3bb24a8159ba024df9e0b761947039cfe7e272fad25a8b70875287a5c82e9271bd6b65dd79281f8699bb3ef07a6073a727f79293bbd9b8486b44242fd79ffe8d6d27fc556dfc2e7f6dfb5b70d8296be2418f999a92f1f16420dcffe1d71dbb5f80296de96073e12744471c300c6b418e0eb550f775d9360d45f7866521e9d1527df0d32a483cadd490bf3a290055ae7c62acb1dc441a4df8a9d1059d53dad215e109053747702b67fb7a22570f05c8f09c35983dea1387b8b413b900e49799102497ad7a377399132060bb59b5e05630b55def81638e6fa7054e0e5c371d4142cd884ec3c0d47016de0d9454ec53b20029c49f1b594cd75a52c5b7d57ddf6c0321fc27872847e66409d39c541fc012e0dfbc4c1374d2b2fe3a9b577d9010e65523ffc17cf654f34b6d5272e2e4637bf89742f97d6bd4901e5058ae91bc44359a3d4e01f8bc22257d6968bc346fdaf590dbaf4747983cc191219bed9ffc8e155184c66fa14753c8328c2f40f575767289067a1837a144adac2a6d3049759a5dbe11f94a809d7479a40d69884a327d999c17756326e7f60d2914f1206835fe5aa21f71d5e3915f77a4e741e1e43e7e6b9d9cfc6eb5293909040d73ad9fe8f6ffb3b883b64342a2f393d9c7f0bf01bfb5a50c1708dab6a1738ac6d3867a1b8289621b18a4e9a550ea4c03e0c570a4c74d2bdca5391dac4e8422535ba777682999572216867496f0a188f393ad408988c854d371f491e6820e9cff1e885ea9662954cd695c740572d4d27447072ef6e6d5774b58c6224f8ece8870c6d8bc54438889812ae4bd07af3202f748238c80df6829f6da7fa0ed746a142db0580a5222335da9dbc567bd705f59f312fc29bfc9e89e0c926e07bf2bf6f18867c5b7ae73a6b4d4d16b88ace5594f6ae32339c392d975a43f9c57e09159a8ef08e7ba00d47f9d043f658dce271603e623906679f907cc490dc7e9fcc017aa6114d7efbb4f38f6c567c416b0eb16be01db0b81fd7d2a40a972680a6fc1a887db77a42ccb9bc36302818d1f46bb69e4842aed1f5f00cfb37716ddbf1fb4508f155fe4c7e962af1bbff4eb5cc9746eebfc8fc2e27dce54490438ea53df536a5451f0864f4d1682e13d8c27c77a240f2746613de3ac297cb7639b5e980c534e24a8de52ca60d4a2a02465c74b5a88bc4b23d2ace3be8b2db87d40101bedba9c21d8a49f35d47d226c6a0bbdecf3f18ca6ab50160344fa06dd3002b3e8bb7929e83da8a8e3f0160dcdd77cd3213d657c9ca42d894521c9685f773f36d1f1b1ddf6cd5bf8b1d13260667c6cba237c6c1f1ba274c3f497fedd849c0997bd78c170348783a3e8eaa6184881143a6132ac3ef75480c15ecd31b828e901de2d9e8f4503ad0cd22d5f9198f523eee7c8fc112c7b7b64dbfcf9429148ecb0d0bf7a7c8081397eb15a5c47e17d1133ccd0d8c24fe636d461d46b9017912153e5c13787dc310d797ee55867da71ec970729ef73149bc496d317179c18c781ef35a47190b8833246e4ce30005846ba21e6f9629a772f42d4a6b8ee7806d314b247a8b2bc38e411d6a5ced764c3343b289eb51fd98f2314ce6b5991029b2045e024f2ca5bff24cbc1a59189277ec0c3f1e0e06325cc84b652ff206876fd73744e2267410c3ca2e2cb747d6a0806faf3fc5268a884c018471e64fee3d9945f1fd0026b922c88bf7574a20f60457e07027feb2947f7216d8b2652bf537be5a628f76b3ba723c2d563008c8db713488415eec5c0c968425aa6c31a8496aa4f2b5eb721f9f3e7e3d9c736bc8dfe7cf1bdd102ee62ecdd105f2000f273d8071e2d02fc00a3c01ecacdddaa833dc16e206e806c7435a03ab28df29163421bb1226e645ded38f1b973335b39672b1d02c4943963daaaf24a5a168d0504c96b75140ce9d5dbfea21b6c55b16ec9172afa9f228ac253e81f7f32cfdc7997598d00a30e0d804a46b343b83a9b78693f69695b47a54524656d0e0482dbad0f75abf237de11be9349ee626a92b324426de5eeed69c205b0764aa0961efc5b2169d351069c252f0e9b390b1334f9fd74bc64556a6ae4090826ba4d13d4ce7120495cfb2506d5a2ab74161eccc97aba68520268c955713440271342a098cf2b720b850aad483eb5610e3962bb35d8b04b55cc5152d2e9fbc346645fdcc9da8a03f439ba4b6adf3ddf41af6483ed363f0b31e7ce0503d80e53cd1514811aab81b19ed2c576082745a884784581747a71ac04bb7b54f11c5b5f6c67623a41f4bbc1517fb255fc6651c961685cfbecdc38c7317cc286701db71ce3acd28c3e63c359623a841973f94d7e7d85888d0ab69f09a1c344760199ccf82fef3e97d1d50f74240767696c0e883d31b634de78a9758c682e37cf1102573fa32a47797e1a8759745f6e168805b44da0e57e0f305cad801e450ea5ea484a5b87ce0021f2e67c8e4a387b0b19a6ad605a7704faf6dfd12d23ab071c4c6359860d3a34d5301dc0ed9bdb22ce71595874f74414f46d502da49797bdfdb731da300a37836515651b90a54607d032929f7cc4294284131eab49a7db55df83a4ca71734e23b1985e9ee85629ed602776aaa6a116586ca7ec90693a1cb4de4dc2b96115dc3117ab73cdfb04ff6ea45109edc5d4f3f861c54b09a79bc0fc1ec0de28559c2b7912628fadd493846eed593a412d30ed074b3c11646cb0f5045eb5b038cc96120cd8deaa33771c732b87fa7856e875abff240f3dbee77ecd24356cccc75c79c265441ad8e4017d90469e1730c8b880c2becf77db454220d6c8440966a1c57a0f9c4260153050a2eb43695cc5be92aa210996f3c020e31d17d258b1bd742cd08671962a0de48d0d0445c83882c725ad8bd56b37c45887b9805c25cf4aac93e44cefcf3c60d21c2cb9c935267250be9103ded41f9cc38845639f87583a06d2462fe603a9c2c81f835f1bfeb130456e8c72e87ce4eb943a464c5ecb5f9dc58655c574e65cc64f7284c7503e3c1871e65c48e5f2259543f42da9ec60562b7d1ddc3d573fb607d5309cb8eeeecbaeb6f26b3fd2737082d57567875027e6d16c7704a58704126ae7f20f4ae28e3637f05a5d1fa0032dccee27a1d920bbba3f31fe21783511455cd49b44281272bbe30d30e72b3dfd5f1a7756c6c0fc0315fad96d48353330e0e92abc77d26b16e6d255f51b566927720686b205fed2c80637c4a5178003d2f8e5dc53ec938d052bedb09dd60d7b051aa0d6810c884921c45788d94cc5bad9f4b6d8a45fe09678053903e267db3b0a73d90bc4514875a9469d621d14488db6448b3af704221198d8103984d855882129d5e02508697c97aae477055ab6796e7dfaaab4b05857dae58c4af80020e47a7b95df6ee7e2635cb764dd90ab1d6610e789db977ecf81bfec1cfcef0d7ca8da6c246634b30702e036b3961bed41bce25026a18e98c00b0e83925c02111d32cd5d2cebcb9f5daa58266ff651382e1f469046e99f4b8a2549820cf51917bdc09dbafa4a6640f6b220eff10ca240a25f0813721b5518a14800b5786bd33cf8e994b9284497453a90df4b4612bd455309418f7035cb3702b0e7d4b06c8a35bdbf8881c609c05da32dc7706a2286d76a64b26a514267beb8ce5b0daea7763b0f28711c8e103b7417148c711212f9d5ed4206898b3da36bfc985ea20c6b223eaca4c221a44aadf5e5bd0cc4de21faad94b26e953db8327276211d4cf806879c1dec0a6bc23e9ce05011c632acdc40276e3abb92b4bc8d86a37933f40889310cc528e87a599c69af31ceb54550c6ec8da65353b77738f570d49ced7a18d1eb61013e5fcb890cc3d8df2d8c02742985075635e30181c571570c4f076e571bc56a3adc95f894b03ae2a6007f31320d712fad3be69de100a5bdb9268143877b0865429f1325b95c94ab15b0b36db40afd0f67d1d977d0f4bd886b9d063dc1b1c984898e4a5f7d5d4428326aa143c63189d93ad1c1ebaf99fb4ee754ee8ca6bc61012f9d6a35f6070d471a027e2b2368648d60120d8fe09fd0413ca168b8574159c777b1ae16c3b4e98c04c390198d5f19a4a0393cefa6a3916d1000a4cd821ee9166b7909f26e285bc5fe1c031e1b919c3f9cfa39d2fa3b8b53565cd3a331b04417c6920c0ab123c90752b5686215e0208a3f6b8127882c612c05e7fe74582ceeb150f6072559a34e8a5fd0b5b118ce7893fd5a2c4ac4826f61e71c763f44720e6829cdbea97190b1b934a24497d7eb37cff6338c2c8db35b1f65a720674f0fdd738c1ed2468d32640530f4dae13a64fc6291f03da9f9033c64a462371f005dd75ac5f5e1123f2f6b009ae00fc33279b0557d8412ff90888e9fc50b09847d600dd2a91b0322792716d7977945857886ca1f76e284b6e78418f1d5ee02b465dfa6d7b7ebd8bd75e430fb56cb81bb9872f3147ce8c25dbbfc0582cc5d75b07f87f92dc6b70695dce6528a03ade3c25958ccd60aecb6b312675bcbe340bf7e109e9ca41e91c00437117c76b92d3b90f9af24b63d66e79e44d0ce2bd4157ff62248c4c26cf84260583e9d355dfcfbe5289244fc64355fce3132ea38e46ad382edfd7a0f13a2e11dda736dca61443f4096bd0ed6990783b782524798da98be52fd6f38a1bdc1c3b0c50d0068ec68dc572478715eefc8a5f802b99401b74d95d74ae19d785ce6daa39559e091568abf41a194c19de035c7335561facc1d0a40938aa84efee824371a5485409da1a9fc9e4be995335ef3fba52ba4cafd1faa505714f5a5c88eae509392ad04b039fdc4d3ec932cdbdc578dfad831b2083b69c595cd26b7aeb12c02a1c660b8605865b99728e6a858e8d5a4ecfdcdbce122862fdd7c411ddcd79ee9dc889ebd3f428bb0a29928e7040aeaae8650ed8f5b6003d6f797eef2167d42e10809184ccf0994a5b2e5886867655e72114f779262cd753c04b482a03b7a76f06c93fa78f3b0472287283efc453b8bf637a142b31c64b292868ffa4b7eca40101b41a7dd6a4bdbf520e8652180634b5c0ed22c0b826ba3311407f6b39332c8210893eddf6b199b8e6c90c3f0e8db2c164a7dca81eaa6c74891a0cfee165e4ead22b3fa89684572af4c739fcc16dff4d18b73ae507dad2429e12d5434e9cabd7f81dd2d48772a2cec0d420eb9cf198105e64d6cc90f42dd581d162273048d2a3ab2ceb8fd937dae37d2cb8c0adefb4780639eaff76a6b85904b09f9a311aaf257e63ef08585ef820ef7c7fe8acab272efd5f4b64a192b5d393054a351f3b0eefb894924a7523f9aa633c959a1f6a7f0d6abc9d487f9b8bdd3886f3377e418c6af6bb1a7de009bf77ea14d280a4c390e01a1d58ec1b9dfc049befe0a1c257af52a7a05cac6bb782323800be0e5cd670d13a07b2fb7c601451ded018072f85c6fc75f77270fd6c6819430025c3e2fa696b072b410027f090823821feb4f168e2e85976851e7e1ffb6aab156b429eefd8c788f8f078fd517084c694cb93b8ec72678ea147f979deada2866d5e7c0c53477733e710c9deed068f8a60c658dc5873bd7d24cd70b8f601920c423dba0193d2e5ed8c69919ed9fef399e41716744cb157d940d07186da87bfc05fa8a2f1c02d5af3d68cd1161ef65449eecf3e38666d03eab9112664e897e64b966e053acac8c1336667d02c0e0e66abb7b9ad09ea7468e0031365cf5cd220d254ce3bdac45317adad99ada7482ae0eaaefc633fbe608e61ae6dfe6d6b57ec645eeed44bb8d9c4a73938fbf480fcdd73ec56ba0776463367ae5bf32542ad8d8da148954b6953715c60ae8662624ff1f9bfcb8c40015c7b12bacbac6044220c61b9edcd249e542612acbc41d72e5a0fcbc67ae797eac80a5d73efed6f9174b87728fc8af01d079068aea4d4708ed2d63bdf95bc198c50e15815da3d533ba9dea1e0c9e2b6e0946a77184fb43cc67627891d70da3d95dc1073022c78ca3622b7245b1974be3218bfd76483e08bd70760f09d6886bbb28aee0e7a82e94ee4755e08f6d19d905d787dc1dc23e28e1ea7bd40b1a02c70669c004018242fa206b19dd7d8a628920fd71b047ad8f3e3150a72c6bd1a6699615219170618e473d4613a32234df9e41338d619eb02b43543ae739f39a7365b8c302b7ce1a98473ee76eeb839f65cce15549c35e587497650555a0a028c80fd24585bd66a7c12de4f6171d872163dc1a5af64e1acf354ce4a0edbf7e94ce5b4a0e931e3917d0d537a2ed90c4b5adab896b566dab288e45c2bd910455d3e5658feb23724e4e0c0985e1235bcc8674ec8a753d2ab969d0991e0b8a60013b78a2715f14dca92626588510167eb71db6f4715ccf534207a6b4cd59b217787c5c4e3f67a884c171529fb37b14f698af9213453b2d5de507e818ecbc9abbfda3fbcfad0d089373e3f063686826dbb40b4c9b7669105ae5f7d63838904f5b8cad77f9733fd19fad268529e3776b65cd9a153ffff6e82e07fa87a2535630a7a4b905ddc521ed9b87b3186b972e4c2447921c6b44be0ac05eae854a82a1bb2e24f68f8ed01a93583dfb6edf6e4f31baf4f46ff8795cfc1e97cdd7cf52397225b31076bac2285eb9cce4664775e8feb1162493b5a463172fd40471e41d39756e2df836ea7130d47f0f1823e8092febcd82371247efc4dee8bb0c20021bd1b92fa4bc25d11fa55d0602d140e3b7975a74840e833780ce11cf40e735bc22b3e3ecf2342027b880911c5a80fc301ef16d965be0a15534baaf34a2076b7640ff45bb3f0d7d8851e0942cfb5dece82e080ac2ab0df7cdda8b727ed7de85d82ff87075efcee7aafc22c3ce185ed17ad8ece773953d9957851ac63ffc212e2ae42eb5a40cd760e27a9b82b7488094b0a641bdfa6bc5b0b2607c9eb3e9c46e2c10bb08e71a4487d45286654a0dbe90bdbf169f3a101da6bb097b047788d7ef41f8419db77351eb6bdf1343bf095bc260ff4d31d0e882fa87a62e3b74f1f73813d9087a72521949d64ba379f80d75b39413684d2b3306ad5c13649254228be124f16e92baa98d30730d57c70716caca574b9db532a8a596f77ce0f2c22bbbe8535b1e291754dc385025f1a7bc2abc791b120ae1ce438db5fdc3189e9fc0e754f9bd3dcdf45fad56cfe233e6008ef9433c8fb924c4623e04b3392005c62a120840204453809f7850b46f83144710c75417d18b742c54cea80c60d8323dfd16ce1905da0154842bb34270d9ced711a5a8e8b86bc2ad648aef6c8b264bd790f42a40927fa4ff45ce7f16e1f290f82aa5113ca9c0ed354db5c85ab5d13905806ee455208ab4bbe57b6398804428d3f30615ded9f6580c71066df57ca8d581cce0849e3d067816322c0be4ca44e5ca5ff1094bb4eb08468738cda036a68b49127380528c229347ae23620b0623ebf4b638919550fa5a4a42774840656841ef47ec3c3b5d3e193e70cb35e6fa2445409baff1fba4154df9363959870429054f612668da4bf67f5f0308aebb39fc3ea3aa6a4e1325a4f7ca6218039bb269fe15c9a133cfd12165db35ee5132ea9f72427cf136a3b55288d44a8371b56efbb01c782b528ae755f324ad713dca7913b81b12e74dd8f6f11dc35e66c22f71e802f422d7cc81dc64d1dbf957c10797e8e38f587d22e92ef848c87083db281518b529061232e97c14cb3bfd584eff7d9182176d9462358c53d0a3c0bce4f91b77caedb5aa098f784a2c145074a18386d36bd288b2fd44d3cd35807addaf8524f3b3a3a2d655b125a2b9fae01711d4ff099251b176a19b45133ce1be9a7cc7e4a591d05569dd04c14b7df13d96163f85f041b94f6f4acecfb95dabd657cd99eb318d540c3aca809ad3e4c2847506d82eb5611d7b0dcee0da3f2270e4203fc7637b8a4a9f125ea4f7354e417845287b63f7bc1e63bc2f133ab28d1e4a54a35bcc1eba9ea867db79b396404d80fb249bcb8619a4bd46b3b7530dd03b5c1e24487f7a58314fbf5a5035276b113b7053be4a12b0aa7f0e1f78417ca63e48e4e68d874c5b0c9bbbb6979358dff7a834adebb2f346e22b43388af6073427baef804c27131fc9f5de5c27a1775c57054537ac30964d177c0902502707f3c7870c47a42078f5ffeaade4773103665aad344f13dcf5d4a9ba053ad1dc4612c83a8b03d056b1942ef192a0dbf318cfad31b41213d9a2dc889f0a64148574457c1378e26d74076b0f73f12b533cf55c4a3e711bd6a8cc6530d14a3f5b95a9f79c221d605452e7449a830260e9542f2a0828607524442f272bb51c239e8f08acb4469ba5af6a49f413517f39e14f869376f2ddd786d57f71fc4a787fce6b943708e3756e7a5e45a1005aa5220b8051744480d564f92ed62c5fb440f205ad875084e18dbe34166cdce5ebb66d04504d87833fe01cd7884b88ad52393c65ad1fab6fe11b9ad2066ab828a805117f4445358b4cfaa8f5b38ab6c67afff9d06d8fb79e28901effbf01cc54856d4b4e23b50eec0f74a9e14ecea2a7ae8b335aee1558de4f54c183a959441b79a5c8900d5ef00998700dac6b8b8a6d941219fd4d1feef0fe8148d5ea523bdcd175d1bcefd8097356f07684cda649ce99939f44baf50ffccd42a7afbd22f68ff932aa45af25701c561ee3e4752dcfc95512568a70642fbd44e047ae43b0a01c6c4c16bf31d702da45ffa324ac215add645fb3dd81cb3026d9584026f0774482a16cad2a1abf7c02c572a0865b6e3c60df5b8e25f063103446847cb37a0fd60550b83ebe0fc8cf34b2c759d6dc8f6186c8b710b0b542f6a8ea5b5dbcec6cbb3514571e3c36e79eaf5b4461b11ceea90ae94c3f2e94e6aefb5a47ebb1f596b5fbdb52f938579898cbec3d3f7308226374d6b1be22d7ebfa4d8107652cbb02e8eed9151f6891cf409fcc2d18f87994edb3fc423bc6a30bd3b5ee37f552f76b189ce944c6dcda316e46e945fea0c965d4b9d2281cf4b2fb68b16111c34ae919b711d62d103b00ba7961019095e88655df625458f2eb46a7cc727235173fb1b2995702c93389109cd45b50ff994051993eda5c48d6c0130c34475e4c3a15443aca8f8526877e9ae2f3ad5764a18e248ba57754c305d99f8b7be4dd11fbdeac99c01233fc72699f81765e1c57f1a28fc0eb220104d008b30d9cc917f05da7ac3fe4c28be73e117809524d26f54bb361969fb93258860d56501da2fff10c15a156a3dc4559865b1b6ec79a25c263e714fb5b764e136b8eb45b70bb2a16a2f2051dde59fe6fad6d12977548cae4ce191635f8e9e4a408cd51f9102e348094418d44f0304356529f66c4366d19189aba9f9df064ca865919d51709f7f055d27443a1e87f86ecfef4c4ce5462117a7315f1c9ac2656bc7e6f62db696b4b8f134184e75bef330b0811b202b937308794d1540757b954ecf6184ce8ad55205cfbabd13d86cee2d035de872f0428274be937233f01addd713293dfbbc10c87194f97269cc04f73051c926a51adbf8b0fb761b611041638898b6399a6a6ea4d335acdc1cc39b88510a91ad63bf71c77f11e18bf1a12389a56a83e84c341b354102ced767d6fbbeaffa96a947ca946872de0fdcbb889dc36f2ea8ee6a2376d8374d919786be7c95a851e09c5e807eaad5d3b4f169e4bd8822c3b7a53e811730f3b65bb89b03248d8dbad99bba1834bca10a113ffab4ceba25e8c5ca3a84cf3777245f4d4e728cfca0a11707ace9dc5b928459b6dd3eec1102550889a75072eb8e1a8aaf09dcf369d71a5468112a2577e80674421cd5f7656d37e0d176e7747e63c130707021581ae2d570edc81a800fd9a5b1784af2056005b10789f23a7ed39bfb160d19db235a15f2cbf688a852877474819b6fbe40db33ef381018ab92d724a0214e192accc0d1e0a13ecf9ec29b57a25aa485c7ceb4a121a2c04d7070c6955319810857050c35041b4231f7c94f386e6c172f0f9f78b0b9aa540fdaaf6a11f10106df41e880eeb13f7c860c7ca66871ec5edcbcb23542704fa30dba45d3012b2d27455f35ac953f0bd3885c5b86bc579ae6571aa745943ac60720c803be3bf61bca3d311c0d0a62c468858379b322987611e036085616731b23835c4f502a4d0071c75bd22f3a311e23148b88ab4d1da6ea346468decbde5de3b2e0a3e0a6d0a193b75d236d6855d06dc264f959ea39f24193b36d2ac33b7d09a2c29c3e27304090666ee2bc23c1586c58454882ea144abd08aca337bc2f9226acc07f74f1c3f18f165121d8638fec31806d22adb314d0b75280be34159d84f50c2944808d3a12c27b4e674ac85d4b1532852c73d46b7ed077e16babc86a30c3229673c688d49a4ef63c7693a522232764e5a196fb24b28c43e1582da7350c721987de6ec324230e6041eb6c50865163178f1b874684d76ec5b8a7e96544e898cd92991f118a10ef66d13c721797b168e43b670cc44212d4304b34b170c94063b2782d96bce441f4e66b24cfc1d60dffd14820dc8be7d935976db71e812166561181115a22cec272e70f616684d2602a12c2c4628bb17302ccbb1cd2e676ba5a0adaec0b2d82067e2489d640c7b86e15016f622d40bc55fd41b634308ec801a5de0294a89430ae92c9570220c9466faa0aca2d9c31591524a21596a405ac931c85351e8841f60b001fdfb2b8e45804c9ab499a134f234cb78d4b135b2c689c43e0b9570c74219fad62a6f90e413d4b7f612902a548461628905cb29e70ea5997f96b921313e988a3f7c80008fb20a0f5ad302652d818744d9a179be055a638308cfef68658974c3924cbb07c815e467a63b9367c837203ea4955681b2e625ad6df39c73ce12f04c1020c9f372ce399b66f921048629e54d5561a3de112868c1375207984f336cbcf00c89635edec89b14708008a1363207413c3c3c36b824d20d47f29c128b6c3ae698916326db228f37f254dde429e7a4333c2544509425d20d577a92b2666a2c4499fef6bd7706f905679d581a414fa6f39c3d4af12ca1b6c860185034f033cf58602b478b1ea580dddcd093133cce293a409225468f6aadd92e7b0a1c7eba514ab406a585d68c73cb92b28ca82d3e7a85039e61e63eb802cdf632cf73507e5c40e2a05f25c15c17059e3d436d9b4513a66d5e504a216a490a3cce284a17ab7d1bce20bf0835208f720c0d1382f5ab1771bd84603dcc8bb86698eaa12cea03aa0ee338850489319524d3a792a48052419f17630a28d3a77a523ea9d4115ae3a1504a3c5e0e7355e8c2cb61bed55016651951e03125949dc8d525a447bc902209f252e28851346807e61f0a0b4c3884451e305f893babc3fc0b77bebf1c86098c5f3fd231058443d4920f1ee78852caf429205a1365a9697e26a5999fd827b1bd8fd991287ecd62b55a73bedcc3c8208fe8910eef614c9b885a0a51584294d20f1e69910c45236a295394120a0bada9b4467639b3452136d719654e2e8880e79b42a12cda222dd2019e3f1501cf7f3ef8b6f88080c5b1c80b6e90a7787282a90d2c30fd8462333879e64b0b943c7b664f46ea39ca12a9a7084a964821d8926951dbcc2c94865e9c4b9445cf92021fd128f7e57d05bc9c9e76104aa96d2650d32ccd1fca5af1d0a229f412d2ae41a9c394e9375a44b1b49145740456327d1055a2596a5394d2114d4297645a44a7138a524229a194e692c4418f820e789c4b2922153de104f668d648810c407d25d8697f146269bddc0f64f0439e6f79c4fd40063ee4215f1137e70ea5a1435a42787e8852cb0558ae30730e102153c7c2cd55e3baf6b53500452f2da6ad20eb860cac0b849e1c0e688e7274f6bbb79a2c915e3fb9863ea7e2ec3971816f5a850aa4c50bccd933f1dc2109e9035932a5b6b6165087870cb387d20bd09af9a31f90cc6f5894907fb091451698b89427b501033c522525bad461cfb8ed33a5b4d4365aea2705a4034c3fa67ef2e444b0277650ea90f9a2c9949d138960cfb06727a2ddb43dcbb2ec994ed34cdc3498b833342ac18e61c7c41ab213695d4ba2ac632de947d3f0ec0cf1d06efad2ced0885d03a6ac04138904691a14ac78c233ca2593f8337f9a669c3f9938d4e76ec2699a394f7801d6ba63a79b0ed6edf20991511e67900d2fb0ec228a2d8cf0b88864077576867860f7aebd88cb137786fad84fe17412ce2ee421ca7346692399d434ad855666871a48c0ab2807912101afa2ac84480d98924b3bf6158f9c3f4981a98829b9a44b13793025172682530be5699a55cff4a13d9435690f769a643f459a65130bfda147e8924895286b9cf98ad488b26814ea6406214a1ea9162414a8ed5203d11a394b5094c5c92bcfd9482813900651569239d2209ca9fc4895a810157200939456e4525c84addf0a02833cf8a713c91288a2802cc8cf4f71045998a012935883d4c23544c106c4203f652e92e58be4221964e1c6404550bbccd9a90862cf36cb7a0257a8d9deae96666de540029e3932eee6c405c658495041a676868aa32caa4f6c1b0e86925b966072c541cbbc9dfb3bd84662217a6991bb832ead3eb208a1353b788019d7aadb3980da186248dbecc8252684805c35745ff4200b97f79d02bc3c71e8b2a03d60479fb6214dc3da318455d831a46d743871cc382df3529e213a3b86481c8d323dfb28b5cc3c431aa629d4c26f6608acc29c2c9ca77e66e6b7ccf4871c94d56f221c218fb3270579631ac206ead01771cdd9f3a0ce9c2e7ab0878b8ab8699aca16745a61df4ecea07ebf650b75bf3322281711e10038bb427fac28c1d8b76a5a6a2024856049eecb51468e734beed79b26fd69892508adf921bb8544ae5e08c5c511b94ea19d69640748488e5c3b350cc00443585ca9d7900021d8e00b176e9b1d19176071edd42075f0852bf51a6411ac70e137cdfd54280335401f8740a8017ab2907da2c04d0b5ad35322d960945ba96da8ab5f7da4a53fb7509b1787644317b97d50d44846aa6ca629d46037d643ee6b937267810201f2fcb54d4ae885b94fa13e89e3274ac971dcbbcbe14e5dd789d3288aad72a810e4813a9681d6c8f0e347f7f146eb348d9aa6efe307129e67000c3f8cda2688e689e39771d80a02cf9f792488eb79d43dc141ba3f203096516111579d464266b6455cb2b36fa9a3331625286338140641b9e250198080555492ba74a17e6f2a4485ada6e9158f0a2d8beafb30fca8b5ed29b5a736ccb2ecac08b2504f5f4f4570be8ae08e16fa7d1dcafa028ff288fe7414c69553130e1e6079d311adc94c55729bace4eeb648cc3e42eaa8ef29b9a7c8bdd93bb336c504180047a697238d4cbbe3e63754a65aa6b4ad139cd64e1f5739fb88ca59f6296d50cf2e51210ea5a4f1c210d3c21916309b76cd143b42e240620acc144f72bfbb1bf3410049f0b31324a68d668fb0f758688f4062d2d04f9a2b8ef649a6760a6aa7644a6758a7461c51a04dce8cdb8d213095925b1c6dfcab80152b56945fbfc7469e52c7066696f43ab9442ec063e6a36982c046b466bc4b72bfef1228f812212f91d431e525d232a9e3122171f4fb9ab4b94588e32a8f97283712d97d422471f4690978fef4cf77c46493333786c033cb0a8bd460a03ea492ece60acbbcd22667702c6185d6d09baed09a9a6e39e52c3aea166d15caa279de04c40f2e40e02d4b249e2f6cc0a62c9178ba40e2517a8127a925b46d54de194a6b95f6d45ab1ed9d7dad95d7562bc46c4a35d4aac353569d4dbb67779dff2b10db56e9a4947653ca9293d2b1d23925a5f607d3de9add5a250e0cb39f2c2c93f576ed6aabcdb06b6b865d16587bad9269ebb4d9e7b4199dac5befebad99ed1a463c04c1091eb39c437691efebbd3c34c9f7bbf7b22eebde7befa512803caa7e56906b15b922fd2421d7e31da6c8f5f6d30205c6d1945cefbdb70b2ba77befbd36721de2071d57329c7da08f93117c4dc4f83cf10363b4a2b2c38bea93a2480aa30ffb68b1030c279c385104837dbaf8514d21f47283085ca02cc1d041879416550e487a504f04b9e490a407fcc42895031226f0682084ca418a0ea7221e9cc3153c74357bf2e3653914e107ee09a3930878d89828ea506062828753a235f1b3e9c0c4275ba2c8549461af9911124a2393e4182dd1bea31d6e1151b6031d2c143d58117c72a00021e9b0841c729c50eac921c804394c242191200539e440b1741a488c9694aebdd65ed5119cca72ca94571038a607940e78234219815d9268c2141b2c3105c9862957b64ce14110a62ce11b8ee0b33ec332ca128a166891edad51462921f9b0039210187f061dfeb0ea5bca128a2125b27d5dca473c3c3c392041c98153ca120aa256cab80a8691e514a429aa2df097e514a41f20815db29c82d404972ff029cb2954764001d6b29c4205084b45285bfb3b23a1804116141070361404952621a0b2440ea8d840b5032a55523d54b09091e5941b28b5b8116013c36e137890a06077fe708a02abb0e3aa40593d77e01431753c16a47c912d75ac72bf0be50e60b66e194fd5324915c25c267c59c9f428354ed3748bc592390e950971fa14babc0b531ecc5f3ebb17a44be6b353a95eae3a8cea45467c818f3978bd49396c62d89d6210e6b9b307f3e057639fdca74a6dc3ca581809cb4bf08a3a300ce87748b0fcfc95f357e6c8ee74f0afee3145372dd64f17d36eb96f1b86d93b83b4a1f63c657e7a17822ec8fc74f08a3d64c4d3bd77077b78efee89c7e953285d1b18c2784ca8fa17a2be0ab1cb61fc95baeaa610d4818179f9364d1beaf8af97cb5f300ef357ea2fd55f2e5ddff5b0bc843b9aa6af0a7d8440f0910baa5161111e1e1e1e170e254ed3b4b7e429515687022c2fdf5da1b202ec170a4a7a41843445094997a650e1c14634650aa23661d7049a4c2a60d2e6e547ca924f673161d79348474ae4be8785d6c0e88e850264aafd8658786a9bee26d387bd62ec975a7b85743e3dfec1454c745545e36c132282cf4d22fa9348eac8b813f1be7d9c493d4e4891dbfbf61e9ed8038b6fa112d14d2772b4591e51d1044a965430b192475c832ca96032828c8fb095e652de514abce3d096131c83e9f6da8be46cdebe578c61be50356b1aef024e6b33945a064e111fb970154ad35f2591158147a965b6966021b9f4c4950a77e6910d920882ab067965896ba889263ec247f8dead507f863294d53add09a76d5e43b5a7f74c94fd806b8306be9c8931d467daa738b6506186698de9591682a6a74cefe13d251249dd3b2a854d229e73d458f8b2e95b168e1f7ead6076efd2854330134117b6e3f7e0ee8944bc73c7221122b86bc07d26620f6be0cb267104923551860f0b55e816284dfffb9aab59729fe18ed6cc842ac63eb4bc85248efe8c124a9be98229f429f42994420a3972dcf83e146a7a8147199c15c968aa8928a80c45f1c97c078f9939a350496b468c9b12933aa4a43219656559cafc9346fce4fe7604d66a9839f0f9c10957f79ddee1c85583dc2284ab1387b0b77c30f7d375723dcdba6f93032f1782f7bef37eface26bdf7e789df3e54d6345feeeef2d4f15883721fe3a28632b432ed23ce461598680b652d515673a7709b6db3fd1916a4a43b0ea544452db660807786768676bcc3f8b6f16c620d2f479deb56c2e96084cd85b491b25f8ff492d4c199c42a82dc36e4ceda8bec43594336968055a2500b354dea188b3a4dc34364888727ee0cf1f0688e128904c1f7c41f4dd3ef3eca232c4e2bd4007def039e289b8613274fd374dd7b10a9e19d58039119e47d8b09f2f1c4211b4a983f1f206a1aeee32492ae367a39026bb8ab304f607a4db412b6952c4e43e0af7b7c7aa339067aed55d3b4f19da5aab3d05eca91e58d9ee931e3b211579eca23754825c9f5a9a544ada5df4add4b6dc511779276abfe5471c338259af4d043ef70e4e5f12ba5c6d24b156826a9b308e5da537f2a92d6d244b466a4442dd4577a90c76642446b324e7bd631403b8fa492b56ff4f51bad956abfa1467d5eb4461363b8c7de35e4ec63cd3108a159ac3108a1e3cc4469c318462134c770df008d3ecb9901a35b288b1efbcc1bc54e296bcb0de511bd92c4d1a759a84df5a1748ab4e920548922a1a4d04a396e7c1f4a6909657513095156772ee8d39f4ea453244dff48e2e86e2c94d549fd6da9975a89b2fa9d0ef0487d727f4915e716ca6a2dddaf56a23c99ba64f4459c8876d86082a534dad157de5bbbbbbbbbbbbbda2872bfbb9b01567bcb561018ccba31acb1eebe5dbbda1d3f29d8e48c7cb98277d0d0060db0ec3a2878945054a035f2d5ee68010b7978504a29edfa197a23d34f15aa00a0c0d9c528bde2ddb6bcbbfbe7fec0657983e70d5b4ecb09aedf61efbd14716a380307b89e012aeca02c6b3371c4d6da7fa10af0787bfb1b3d4315d0b7ffed67b26ced574960d629ab2b02cf7f5207b8c2ccf7e00c33d7cc562d7e36da01d96e7a8fd4b9eda673a9a4a4de44109b79db502e48978b4d01fbe47aec93f40348db98ae7d944b3ca74ddbd13653e868c7d11571ac7873a33a0e6de1c7ed5c902ed55347a56e0a51db86faf6d4460388893aa914ca45569ad7d01469a04108367257bf9ed536af9ebbfa46eec2c20a481cf537b4c044a17e86412e40253580157f7420eb00ce566c01cb34d00ed449a16e3bd05e661ea914eab203ada8b3f5b8c141410bc60010a9e3878becf271957fb84c1b5a9808a03db9bab03d751edb53dbadc8c37494b8436fea74b463ef27b9ddf41628cbc49dc2d9134ea0a6a9dfbc1af443674792d18fa55d159b9c193b28d7f8cc695cce84d3b5d10871d19ba6b67a709fd21f0a34438be63462ab1c8d901a596e26bc61baea52d5420bcaaa4a33a991386924b4a6daf8cc4c4aa206a8a7515f236cc500f7cc6d883b433c689c86b8d397353ef39d3e8d197187bec6e93470542fdf258d012ff3d324fce964cceaf80d39e19c7853786dc665783c278863268a230ec5f1bb387e8638b26488632bc710c79b8cd336540b4ac3d332e248b1e47a501cb31871a45946fc8923dd92eb61c4f17b11c77ee57a18a2aade451c5947723d4a1c5bf5f3c4f10677224fe5aa98f42cd27beb3c42d2d43ba12445ae753a9138eaa5b77646e07b6742e9a22fec6f1bea84a4a997a24a4ca4451247cdab2160da437928abd6a0d3939339afc94cead8f23c2acf303ff188c05ae72b26721ed19c392a6d2f06797ede239af7984c4f893c2f63d0f23874b64744b9dae5f49f60d90de1d123caf31e11ad9151e022206c72663be100cf274860d9e57450f0c862d514ea93cddea4806521d4460c52a12fdb56daa235a38dfe8dce91a50efaa6379cc093b228cb0615bc357763082c0fd00dd1d714706e4e4fe0d98dd319814f1802a63f80c4d1363aa6c8277da3c5626959b018786b6e760c40853b540539f8f0b8627c9b5c8c706712a1c9143cae9d1a86b2c7f8890a6661bbbd2d92e951ccb1d5319817ef230265c0a925cbc86ca742740936a247d8e9156c0e51449928055bfdc87159fd8bcc397f3689b9b0bf857409b4dae93de83d91c876fa93484f847b5fb60d68ef326402a77bef817211679cee89a04589a05d5ab22711d4660e8f433d20dbe6b7e174da61c6c729647d2c0519d23628998f536812f1cc9e9f18323e4e19d4ea6d339d14b1ce0a62b199204d836d930bc321d0057cef3dba9fee12ce382ad46607ba80efdd47f7d3c34c9cc18a3a261b592c0511322468c98ecf0f50128b81dac6070602f26165069338e341964ce28c19bfec641cfc2bc663fe10b4ffa56b46e87dc67b9c7e9148f7d36788b8d53633ced1cf108974a77dd004f2d86e0f6adfe8e989601134893c3ad08a34ccd08099ed5f90aeee34e498aed31dd444b08777fc1ea76f27effdbe868b280b0761202c2384798cf0e5abef5ee8f253887a8685a08e8b0b4a46285d3142e992f1f2e97e7fbd605e2f7fa98e0a37942a9c3d13e80ba7900b8c54285d41fee2be1da5836f677018241ce29d564dc07289ebc314c1a8aa88a20c3e46d2f77b702791087dbf139b9b54044db37b931c37a3dd3e08ade16eacd42223a02021a2ed73c16eda661e5929ba29d244d67f3a0e4d61f658a1ac7a4d86e3388e8371d54153cb84d2e58ac902cfa96113c3660fadf1f97102e50851122122253427b466bc2162a24490a59ba59b204b9a0f6ac27ab49bb49b6ebaa90673187fdb80e2908d11417b22f4dcc5211edce98974ef7327826b671f561f57b967e502276d83b96054dbbf3085ba77ec6e4ffdf542cd9fa04914cea2a6a9ef3c14970aa50ba32825363933e2184b70bf851dddad42db608b401197fda461491da73c579fcfe60d703dfde8256523a52765408ca677946715b397d5e95e2ba5fdfe0c4b07861b4979ce4a958c92b4981e963c2f8db2393750073bc8e39e061670ae6f9dee38b4bbf0a10c94354ffbb55dfbcbf40c02d2658f4369949075b3c3c77cd3cc23cfc833cade52a55a157a812cc4407ea02069d2698352ad7104ee72ea7ccff99e99a335737e9b019a2f2054c9459572bf302cd334d3c6715d773a79dec5185b5c9b4e2cb9d3b8934da60c732a4a9bbe6df70c33f000aae0b1955918a350a95476eebb1940d2aacb0995f09c73ce39e79c73ce39e79c73ce100878004903c8620059b8a2d8e4cc5893329d5c03691bed2888d4c2d3b2c52160fa4c14b0eb027569df6c6b0a1e67bbecc106d43c8b68a8ac9c89fd339465199e3fd92cf29908984a4c14e8e948a96bcb38203ff43d403f414239a89164a03e42593f4a4c04749780095ae94989e9b2eb315d2671a7069776023ccf812f0bf9727f06f945ee3bbb9c9a74ab64350a151f8c7ac2449426584481018ee283094c677d8655c5a8512c88528226b0962552141c8ac0599648513ee08204df2c91a8d8f27ba92042bef7de7bfc41c1aacf06569a26c5041c53052be12e8a5411453f60274f309625121544ccb4302bbabbfb24a594b259df97659dad95ce70dd2dbbbb9aa4b46196dacd302cbbb55a294db27677d76e8e76ac96386bad73ce6f62ef86b2bbbb7b95c5a35e7bf5346d2775cc30f3e48aa8ed6abb7531796ddf1475b57da379269b0164d9926fdbd576356d19aeb65f6d6f1cc675f7e479adeacdd0d9759da4b66b77db15a328ca4bb15a28d6aad5c2975adf6861ab696816b62a76b5509b8b87bd9384f9805c593bc7b6ddfd7d184b29254c3757391b49b0d786163a250ecce1506f284bdee75ae029a7ada3b5d6aabaaf0b96b2dd19ad197bced91565ad6926cbbecfb31c67e5ef002123d775d65a6b378fab310353c55c8d1968594d612e9cb5d65a183064791b9ed0022c431966b2bcca5a185c8d19e4d15a6badb5d65a6badb596d64862662f4ad44842cec4c4a081ad590c4fdda9a6d59c73ce3967b5b79b0adc53e0be569c3d754a81db0877304e4d5c70a88446a5f09592355db806b4916d2fba452971ccd96da4db48779224c22cdd4ecc264d6c4da249b5496240a5adaf6c71c406d6cf26a50e7b9005e9832c4e89a3bef3cc23b8b5d2ce569c4636ac5ddbe46677db1f8aae9de2942c9b71d2b4327d9c9625176ef23bc1e8c0b2dfdfb44d77eef2386de35d5e0b77286b488b06786add0a9db11614784e9f157a3ab7387fa0e0f9d3edc40904349db8d02e93d87a02cf8deb5e3a18a793a702f2c30563d4d134125f7643a1527895511acbd2a0407699465713331113ab8bca033abdc07030df06838f6fc555a9b38a01c25152d3b6f87af0712ed1982cd3ac8420273799189c944cd2502ae4a492291065891fa0403132a9638616248e2752869c31e37dd36ab5b089c9c31366b819a9a3e78db9a36db69b6c686bda50a0a69108984b1305aa63b491a7112acb24a9348da61112782a914cf03cbd0dab2c4d044c29240af45a64fa2b990a234c2331065c7d31b42886090f528ad4154da89ca85614bdc008aa0bdc5922512194046fc9b244a202c9eb6bcd82e811112724ee89124e4f38e14101948a0287cf6ece8944050e33354f737a737227cf9bc9de8d1cd94bc109aff32e87c8deab8714c516d9331aca9e679444f68cbcc89e1447640f490a25646f0a25b2370513b2f75397e33d9fde7539a7aeeb3a2894724703257207c510b228d727b60dca4fdea050c9dbd1e701909ee8a199cc132c32f67bafb497e34cb637b2cd630e7b00ab007b59e58a4580cd22b7bc6af2387da0b0493491ad7d8693e041b6ff9064fb2b8145b6672d0144b66f2d3145b6bf59628b6cbf838924d95e87091e647b1f3f4a902d52135664fb20f64378e029b2a20705d922393185d648a4273d0455c18194c40b1f656f7a5bcc8af1c7b2d29af327ab1ffe9090c82200150441ca0e47d0a1892dae6a85161c9ceebdf716a08a29aa201d41050917e47ae9e48b5c4f802ea77ead5a0700040eba10c1132c28d2c1553322e48a4509b9d65a6bad03e872aae9ce5c9142be509844c94113219a10198183113f988113272670c245be6f753937092a8081429763391d32908111b95e005d4ef581008a38428a0c7af23d0e298ae8e1847c7fc30a114c04a0cbb959104d412222887c0f802ee762acc3aac75dfca0d65a2b4d9753ab0a853ccac44049be3158220a22dfb3ba9c7b0512f2fdbd8d2ee71a0108971a5d8ee56090c41012723d8d2ea7e2eb92efbdf7d6cc54551d5a22d7cb255fe4fa13ba9cfaa58084a22d8284b061881d5cd7ee90ef6d5d9125592d21e4fa2aee208a17282160090bf27d780437449600bc00887c7f6fcba87281109e0cb124dfcfe872ae0e5c2044bebf8c2ee7c65851854d140948018a8c22ab080154430826941c514285100ff9be75232425dfe3ecd089d1e55c2b39660756646e2411ca5746be484968d00215b4c028dfcb7439d7638295148230ca970547588025df8337075b9c085d71040727515cd91082104583795521b3821d6a561045beff824841be8fa9b5d6245831d55a6b5dc9c000e5fb8f8b201bf2fdd7e5dc252d304034c9f52f5d4e8da18206f1f0f0f0f0726566f2bd916f8e146e952bf962d141965be66bfa9cc008f9bede674052f23dfe807c90efef830cf23d4b07a41f809025120a9ec8f7382ac8f73b74906091ef7dd01a89940223f2fd8f1df23d1024152cc917098810cc9e9f24d02075efbd5755eb0998e8c108b9c644e96105a9272650cad75b629ae00453f23dee726e9623bafa3caa7424e1c812b9fef4717144aa83e245aee7928065c346252022d79bb01458b0c0f7de7b65b6b6872b46b57d3d0a95a25d25fe6c269e364e7baf6d532ed305a5927feb0607e76d83f3bf08a7a84816a95029d449ca191e6cd9c46aadb5564beb6d5c6dadb5565b6bad95752d65d548c2ce9b1a4948b9699a1559cd6a56b16b9bd40cbb364956b38a5ddba466d8b549406024c3b29b558c6b865d9b24ab59ad5992ac6615bbb649cdb06b935c8b65f55aac89bdd762496a766d92ac66b549939a25c96a56b16b9bd40cbb36490f3f584629edaa050101013596654744b367f250a2a9a424e716f40839261cf0ced1907b2f965d4d0b92350d3ba6596c5af98ac874ce9a94659966aa5cd63180e6fb8d6b9d1cd4661e1d8d01b8d1a2b92732da421d6a8fa21c1dcd4b6464c52a8e734b3632d1a28ee3ba9cec5c8e92af69e895b26f5c37b16cfbad8e1193b74b746699419028d0df2dd34aa65b481c944849931247bfe92a04154607c30367adb5d66abfafd60af3711c0cedd32e853ab9a85258adb5d6aeb5d2e66aadb59abefa4e21ccbbf0e338ee30dc878116e6dbea0d169ddbeb5bdb8ddcc5b54b4785286dad6ddb4dada803f3f108e1b6d03e7d9f8a39190c8cecbe10260cb17cc3cb6f90efbcbb84a7a342eda9109fce6d9be9f5321d3564f60009350dcf4f1051103b532d45c5a0f310d335ed58bba789e0ecd9aeeeee3689206632756b5a6babeed349a34d3b88f7eeeeeec60dd35ff7edeeee1fdd4d6df76dccc36ec8a4dd0b4da793e9a76b27d38dd156d3a4dda03426f13594fd6504bb4c6fd38d6097f6ce3aae6e6a2dedeeeeeeee6edadddddddd1747e2e8a9996e6790384c7dad3beba6f4989873358d5efb55dadddddddddddd4597a27c70ea8a17d915503613645d98b04841deacd0e00a29523e71850d04b0a9b42601d93e7b72e5064b0003e8e028b70d8da01818612af02c42cac1d192d1927c7f6f7a22073b80f95e1bca8103a57c9390c30a1702c00110f91efbbee6e1e1e101aa8debbffad72b9e34e10a0c7f35c615786cb57eb4ac8c52c9fe7639764a93ec6e4c2379cc916f9550983ab92546cee33c726792c9642a4185c9932a47dc8006f9524142159e7cabe4906f1523c8f7064de47b8327414248556cc95722596145be5656f07d2bc01f567df58911727d7f587a3afc61d5476b20451133146459d6a22d4e0a121db4a44dca103ed90a3f0099a4184102ac6627006a1a29058a11acd08394a3134801420e4ea464e1e1c8480956f0e1c80713ac00a4e508063aac700223cca4455b18553191a2c413aa1f609ce51429354082942b4220054baa0a295a9c404d391ac1eb6805c7e28b1b2a5a3333151e54262aed16c2b6daeaaa75a956d36af57df5ae13c2e180887a400cd436d3495110060ac24e9877f08bf92ae65cb85abd7bcc2a833978d014b3fa74489b979196ab7b0f76719fa292d71c3b4b9afa56df087675c7b28ecb06536859966119a6cd98d5b1cf307968904d904ab0b7624e1613b302bf556e86180bb1508785c81507912b069238ea05d004b631ffee637599db90e6d9e9c4f82a2624b27acc6d0d22f3c3879521ed31e176b999e5ef30e7f11de6bb1579c01c1477e8618e43bf83dd57a126e60ccbb0cb9d70aaf3509b2a05f28888cce99b9e488cb78d21f3930f7c14ea2791083eea1d3e8a3b3ac222a8cd2ce312f02177a083b9f8918e8fb6416901fa49924a7249cbe441819e96b69940415a308f0e7683e960a6cbe0de7147851dc6f81ceeae0a890cf190f9e94462dcbb4b4844e627d3410d87ca9814ffbc8e80b4e01f7c0423c9f5544bdbdcdc68b9d1727363758e348eeb4c22a89dbe752168df59f1d61e75192fe2dab6a34d8707e60383d4f2da4cb6af802ee073e781cf893b43a6cb10776288def14d220fd43b714746043511b4228fbe07f2a03f9dfeb479a1eaa797c78430609e618c5ac1fc85ff82f197cb513e800ce9c1aa9714d639795b747c00e178b450567da5411955015d05d42b4369aef802c1a4bf3d2d57bd11ecea5fef357c1969b94eefc1ae97270ed1809d0621591682737d873dd8f53a893d77d51bb9ab6966391cdc7c3fbd387111849c4a79f726ec060a522911ec64971d87d2199a4a51fac944c134c651347e61fcc2fc161d2d612a1e2a2d549d46e87299ef338f910abfa3c299546ae6a97fa999c708a76b9bb2034d220ef541e8072182a0266a42c2f4f416316926131309fa827481e22be634ae9d5e763498aef352dd45073c3dfdbe99cb0edc449d54ea0b4d31e20bc66988add7ccbf59e33468d8d0a1f942a2239c38f111117dd8957c8fafdce9badfbdbf918c78c2c799742d3cf97e0211614433c7a1da0c16521ecabad7b86f93a3b1f11a31a18d7f618deffb6ee3ab71302472facc65c29913f14e833bd885d265c3077e5e3e1b5126da24263b51c7468d6b349c44481cf7369c454c14ee64a20411d176ecdb313107e3b09087c4714fc308481cf735ec3169ee5bf220dfcb69805162916f9fbcf7a8f7fa5e9f88f77aaa3389e6119d1374a1d67e4a24e2bd3d3123e26a5d81ebfbd6868d1aef4efb76c2c57322b8bd473d8df7e8cf3c6b1b1ad6b16f5907baa052a95c5c8e851a973acd79a44e23ee0ca5ceb130ecf5347e0ac14d74a19e463d0d90477fe6fd996f36c298d708bfd308653e13823f219471318cf187ab87e18c9b64fe8a713026e6b3114a578d50ba6884d235134ad709a17489e1ea33fe7ac9f84be6317fc5f80bfcf710fbc01833626464ac5633680201a95c52a8ebbc6e38cccb61c80e877ea1c47850d6bd7709a7118c705a8109a7922a944df312624c74a94c918cc61862cbf0860aa56babd8f3b048843bbdc7759e37c34b5dc67be0cfc09781539e086e3264789e0c4fba304e81312a182f3231c0548c8631d6d96f93f3665c4638e3a950c6679703eae0bf205d58c6f16734c185c59d222e19f742e98281a5de033fa54a9d66b022a8dd471fdf07bd77da64c8a0e105e97ac5b8cc410dbfd536dc4fa857b1cfbd07fde93911d44417f03deff41e4a04adc803bf8f6784aacb0853c7e1cbbd10c66384df654218305c7d7ff90be63054aad48c50ba64c4c0aecb577fbdbe544c0a06cc4af5f2b9bcf0bda750c7a12714f63aee5404a348465d78b01af6fbdea494d8eb0b610cf517c3c6bb60ba9434d5625a835594ae4a69e85749e00f1771dac5a5f8b23bf71eddb97bdd390cded17581e6bd9d0862e071e826734cd7ad26d00cbaf77e9c49aa7b552e40da665e252077f64c1ea07b6d035f589d0b633ebbd9e9bc6ee877bf819e337d87398881a1746d5401dd5d9a7b56dbb488d3ad6b230b8c2375683aa60d12c7fdbd6bdacc0f4c9afb9b4a4d1e937582c70964354cd459695207f6fb2c6da60f92e6fe62e20492382e1026e69854a0f73250d6fd15370134812d0dd5ae56b6d33e1f3f50d08257407fb3fb19247564190402ef910c199036d3064973efbdbd17401398bab03a781f3197b9ec74624089114e2019d0081715e50beaac565d4cb80a890c61b2c3441e74fb42ef30e1e92f21ea30427c9770bb2a4cdda47d3064568552861934899aa6681e4da12d9c404d738300a1ac234b5d70d8e44ca62da874cc18aa8a542a4423000000006315002020100a874462a1582c0ba551691f14800f6fa04282509aca63599003310a2165083184004080001181991ada0601fd82634adc2824281203f473de42304f27d11339012eacda6bda324f075527db854560cd2a2dafd6ace87109a59fbc5d4644c52b48f0b9ebce3b084059fe1288dfda16b8b1e8d8267c4a0dc133135ff4abb014e23c32e9d45ff23fa2640984400bee8a8d9128a6cc1d82caf4ce3417545e2218fc336ae83a81188c6d94ce4e0dc1c279edcbb3e92c5618edaac168af66c644283548aa99c838aabed494cebc5f5540782f2707f1928eb3fc54744fb0b57c4e7faca1d6b72c86e4656c892019a8a38cf884415589ec814a06cf0a0d382bcc013752ff51c64ebe01cab45c3f8292154fe191ab0b94605c72adc1f08139bbc166d961e2bd6a5b4aa47a774f94f0dd4408b6bd595a29b68a30ca9eb51e8739667f81f2bf67f5617633dae1f3afaf4b871efddf351b20b578cad1eda0b933965a915d86c620d87d3c27c63aa7f06a1c0939d70428a358bb251832fe6acf840d47d56933019c0a21177a1cde1568ead435a2de0ebf8f6811afa61b28ade96fd4256e2ced26918f4bcb4b7a150c85a4f96815dcaff0a1626f4c547226131ebf6493554e9cf09ae507ac54c3bf53a7e33f54740d4b42d030cca261919468fc7b658fdde789209e8b2bd1913774feeadcc9fb51ed6362b9a3320d33b91ecd29603688994b3be4648c728f8007b948be7a66a06b3bf34f8b1a5ad5be075e86e670abf38fce4ebf8e681f2b891b8777069ad4471224663aa0bf1df263e4a9187c5df5b964a49bd43a4888ee7a70cc2c1d9132decc2e4ef97f04dac47eefa582452f575f1581b64bf048ea766e04d246340b32ec81c91b32d20478f1c5681951dd320d3d51f1cf3e416951453c6c40eee16220b49b3d9177fd292b94772f6504cf4d7c01e33b557229dec5ad4b517b7af946d2ee0e0681d91c799a91b815495597dfed5dfc1cd568f925ccaf0a33acbba12d1fda020d555248c090214c050229ef137189822c6cf2c8c9c612388a477c1df0e3450254694e8fc1dee0df771945c397bd7bc9a4590334f266ef45070a2bc4799c7484d8750cc7f82158ee9727b2a768f83ea2f45114bc82f422722bfff6bb3673477ddbd5d704edcd1cd42624b40e697c1b4b3a3adabe3ceb89e16358d8536204716e62daf3c2110f53b3fee5b33a46c91212813f96f75b6c05c7bde29068e3a5f28ee9ca4215d2dfa1a96f900a8d5b15a6a1ee834632ccfc2c9116764d70988ebf49c65d271af59b2d5971e75ae9cb796fd240061cb7c1fb21f09ed595ecef54dca0d29441732b7b64c1cd989bc1b1c148da8d9102cf6c9dd1835f4eed130cd573d56f2e4c34bd2ccecd37b8f32b0fc497d23ed200c30373b98e6a4d1f7b4fea77c28354d19bd8722af74e8df654173f7583a8ad011dd873e763d9a6bbe35696f50d9eab7e3e13c8bec6ecf4cac3bbac32ad96c6e54a4f542f5669b8927574e28cef24eb4b3cb1d5ca907ddee314a6a10b45ff1e7de2ef0b408dce0ec1d37d14b37c9f8ba36d2d62d70a8f62a420ad12e2aa3f7e0f50167001cbc536b9f7da5a06b093f89196b40085d5a432fb7fbc0e0852a259b20322ecad870095da9602f54d0475aea449442b8a7540f59803ee68880aeecf026927ae861628775f2342f2bfa6449d8d3bbef9f33d7a881d411445524397914a95c0f24d60be552265ef2b76b9b8d085d2421a0c78673d92414a0f75fc8128b7a5a0459de5ee38cfff42d7264a34dc1e260691c4fd3349a98043b62cac9c6e88d928c7680102105371e92165c0cb21caa201c46e90eab6a2194e2076e3f76e92d9cb33b99893586dbdaa7916f7dd167ac45b1a7fab370e1ad2c5f528331f1c775162084cc0a87ddfec7cd982215a6b26804365b067a9ea57466b988c358052357d1659cdbfde2410d36105643e6abba3d094abcae8421181bf92831028c50cca4b334667b08055af137f48c1c037078caa1f78e1bce7f1887fe64ac1dace0efc6d52f8130145cae140394648c1f659828ca9b02320fe6caa1a912391b635605c45225400b42a5ef72e279ed93284e0db4188d035dc1dbd0ccd000a07a5975d7f6dfffdf6453e0990693ee1ec91e4fbc59047c2d8a8af5a373dfe0058131d222c690ab0868d4a6f7495e4a133382d8f3e3014d24229cb45fb84238a2df2c811e6b7622ff615acf0f8c63297ec3b738104831462d497d3cbc0465f8fbcdd88827df6e25bc6393ca6b8df87c223e8c8a763b4fe49996367d495ba31868652b2ffb9f691b7afdbc5a8bb935f99bb8eb3bbd336b84a05fa8ba1446e98bd75ed0095abcd90fd95c75e906e9c6aed8e6e1080f5c2c603ea2b31abb49872f57dfb4dec30e48b9197833a6940135a2661d209d0e0e24f69457b507200bb06c4dc1be04a53f52e34cdd42267931647bbca22a40dc227ed7e42a98aea07fc213f64481df03a848d6065041ff2956feeda2d4be27f25ad5231f0622633e48a8ce7cd2ad4e20aec657e75091febef9f4594a52bd76b462b73e9c27e6e6c1f372fa5e6ac7605c00610bf4b607f55c8c8e59d97cdbbf73310ae897d89bff334fb63c55142b6728a7b8ed09c4c5f490f93f786e922df93b93b9a453eacc0b76c1323a3660d91b1fa10f096325cec6bc0c273361820517a0404803d7a98cfcb0e9dbc2d46961263c99e7e8ef37a6f23a71a3cc7ff3d4317e0802ef43bc7e2dcf7775c8154c180c795330f2f601a6e8bf9f430c61e389d36db628b829a860aa457f0b0e7193cdf5c44acba9b1ed4089557fc8b70c18bec098192252b2ced2f195904b14a121f4447a83c98614dd2202e2d2c4e58a10ce06b0f181dc80842ced673ea4878bc4b793fb6f953750bdda4591c1fc4e479cdfa33f94b4ecb622e3a24941dcc5931b64025f2e9523d926bd2146ba40213f98e0888e0e94998ad6360b7d4127d878d1cd01872fa448dd9466a303ac71472ae170b2d1c28e12fb07deb46c2cf46d2da822d616ab8dcb253efd62f2e97d88ccb95d13c1593b7b0bd1b2d37bfe88860f4d0fa92a762c57faf4ea3a17de0edfd73e799d0cd26d0f4fa6eb15d12a0f392689bac1b8f9338e6be46e9b02acd6d48424298736fe255daad5d2dfde3765c30d1a09ffbdde2a466fd2a05d83b3d5ce97afc3577354dd31714d8cb2b5a9bb98bee65d69ac1a9d13fd240935e42d48a57aa8a214bd2984e6d7440046627aced83d0601767d799b7476824118b9be3454a3f863db8fa252ab87859fca89a23018ad2b8ffb18dbd68d6aee574bd721f129b31feecf810ebe9d453ca54fe14c69dd4452c11481cb0626da7ea018035585fd51eb11391ffe679154925fcdbf639f9cf153cb6a6f3aae714fdb67d622544fe27fde8bac0a88c1bc305e400b8326f23af6159862470647e7ff0e755acbf2211929b7be8326a1d0cf5d01337e93d22aef00bd63da977b7a905650e43f815066f411088f7234d5e0cdb397f99778a5147fd405787bf8a3787dc5f71328dcc29aa1d84c3c27fccc110992201078264ac00e650feaabb52cf6ca22045425ee4a6cb284d0c422ab4ebe3873e535f290c94aa2d61d25b8637b5828a7f4479bfe07139c391196815632baa2a5c99f097160ec07e3b97dc312adc4bf9d2a619340baa0f1b9958bf20ed2ca8ea63135e3077ea171f681fa1c80fb2d7ad9f706948b22caf19281b7b999d3e5b29e17e608c6819d876885b00859fd81ee885b2d172f277edded648e22361a77411c7a211f1e660cd5ac40c2a7cc84f97e0c13da92b88b1ab8cadbca65c5e4bc478790c416a0159c15ee86a62747f4126f632db1a2f71f42dfed8429f02e9581e20dba38280a9a5d402cbd139b63aa5b124abdbfb041614ed6ad9bd5fe3fea28bc8337e7b7fa60b864f9c49e567a53359b172e8ef409b4ed0787891602818c81405bde68fbbb0a0c95bb5ac22efbf14d897891fe472ecc5098cb83c796d4f02a3d95b2a63ba2ba035a807f963418e1ca3e3a117707926fb579ee71ad942415e6d24570730c3e058f60879d99a47d0d6d4f6b04f9421282bf1e41cb6953d0a838370ba42e14559f32ddaf30a828db94bd9956b160e98af56aebdb02d5f6c84180a31a524a6d31dfe83bedada17bd0590e70df31f97bf572ba17e0fc650971ce86bdee28de19181ef3a3ea8ec288f74e10c2048cf18214e18d30c81829e0d243e8b9532ff38b3c5f5d2e35e82fd4bb22e9c5f234c53ded9014a1b09b75549b9fdb1a86c21bf5d1830a7c5dc851337def32153af699b38d48024e912c969782f530bd3296538f4ddec4bd0703573aa0f2671c365de12f97b4cec4a4b29793c7be99038df64cd6370d045efed2807c7a075747a84adbbf7b233683dfa900959805a1da6fe1fa4396cb864fc4e37fa8f62da5c8a0e33188e1f6093bdeb83c223d1debe91bd6b10f2aaf4c08af09647638b130b719340595e06c3541f6ad1f172931855b2a7cd84b0304c93effe1d9e014063c00c809089f31a90d7961ce422191a534e0d371e264f5e4b6bd295503ce5fbf6025357898efff21ca739dae1aef29e0b1c732b4946e94d4c57db1d0024e0815094e67f7732460d4a5ece0674960f2b3e529d50370a0c10f808100ff10823b06c1c8d2085f745ebdec7cb4c2858d02107c7425c28dff607d8d1965460b54b8d6f0472522c854153ae784451c67eccec0aefef0d6f81b52408d63202ff81261a8eb420ffe8c365f6573155669faee76b964065edb1570300a203ca1fc541a81c18c9ae97f8c190e0683611cedf632fc69c3b545b0320b82a1087140715c8415d0d4803115ee823ac205ccae1985e557031c087c4e183d08ce819072e16da8d33189c6b25842d68691061193c2f5c8e8bf5cb40383cbec5f441237815711771f34b07f590512dba74c310eec8687c79f6ce99c3b43a3278e6168c0eb69274ef8539f290a2ebb9faa2c5c0d5bd07ad9862edfd1042e844e84bafb8c3bece908592d4089aa365381c8825d176c2088da7e88d6bfee49ae73c0ae63daf2bb6b341230f64aea787c2c0fc01e97eef6b7090e0312ab59b4ef7618720dc5266dad0b7fb709fa20efaba616006080635f732bedd7ed0351abe15c19515b012b6c7f4e3fc4bed3872a17fe5874cadba4807004c577cb6374cc683a0404f2ab08d14cf24522b18e48cadf09766b1a7d69c120c37da772812f78dd57b45fb2beb470bbd2012d2e23031d4e81a2c8fa278381ed54a062f2aabea86ca3672632323e81cf6ec0e08ad0db115293dee081c0508f784f4ae2f4e7763b27c53312d03f8412eb4ffb6c9c1ef441d9e8d0aca2a4d022daf369c3a53e045a27fd1eb33557dce3874b917ae16a6559f63bdf2ea186c1a91de975aadcbb1251f1f4ab0b732a23e679375dd8f398c286fae1cdf61a02d7de1a00da5c940f35244710ca70c86582a8ed9f37f1d756784c8188477f20005363e10080a0c7590bb91aa3db5856b1b3ca33efacb48e9739c66506dcbf315f3c9877aa6f33e94dab09ce20e55b40e899fcf41c795236c3f0c86c7fa4c439d525aef85cb1489a2fad2faa5ef4741ccf5eb61ae43b3e470ef9258c613511e2dc41aa4bb85a027addf106a13a8241abfb0490e81a44199aa9352568017dca71df1986512be286b46e32b7cc0ce601dbcf3106a625f4a1df467fdbd52cbbb71b5d47d859ee403739938f5da80f42d45842d382352d85f2529860aeab45aa5235cf7eea910e586458e645cf97f834af6fd021883d62bec600f72ed6ad7c3909cc9f2cb2d016e256c8bba6cad365e8ba5a926b3690a61bfcb2005eac33a61fcdd0a47d69834870620db92f8528247ef39b0368ddbac509ac725ed1bdb5f801e73855b4342b27fbad3971851312176815481ae499e348a88690b595156f31e8ffea4c8539b404de85028aa814963985072034627698fa3b87b1975bc893eba49440e52208069bf613450dbbc610f253fdd1a63839c506d3f49aff0d66f1a7375c8531616e0954050ccdeb98e092c2d916a1b44dab82f62dc205717c288583ab40efc71959b9d095c0a93e85a2cd1821645245c60d93a3e1d0ab4e2416daa904a49c1fbf983eaea916eb3d3075e2a3efbe9571efcc6da265759a8b866a76ccd1da370f72c78e0536b0de3dd7b9122f477c6382b90ec37ea4740c5161cff3ec99f6facf5b2196959b67c7deabc4946bef553a317c92ed24b0bb76f146da23ac83fde5ab4ba35d73785b55100b39aa2b4b8d92229da926c02dc45c9caa2ee98eb911655a1b6d98e6cd4e483b60030795ff48664e9222440637ae06c55c14ba6f3426434ee276761945a77254af51badc37a19fb9b1ac0442dc756be51b354dd6e52613e646ac75ef7cac456a51182e9cf8ec55c1493bf3f4ec52792040dc246af54b8524f5c24a4b6c40a9f15e268672e9b6531ef726495b355ba35104fe804a6447d2f55ce89c45c24a9e851cca5ee93454b3434584dad10376c682e19a87a499483022dd8fede9330b1c17d04e228be075d6d69f80e1b0f04104a34e8e8958e1b88df0580df1af1b0dfbf5748681e6ce861940e63ca063cec7a4fd1133e2e9e841a6245e0681e466102785fbc372894bb03b51ddcacbfdb971ae806574bf7855f64b46a4515227f72a18b615d86f03d77c782872700f667071bfb221c52ef0fd04e032355ecde2a30222716272bdd0f63cbbc126fd0a2283ac8e5063df6a18eb198ab71f31430438b760c12400a36957fd6410eb2464607ce6079a5a6e68ab53085059a86078d81d0c02124f05948449ae5e081964675f16996e49d832ca4c4ab8ba984d36215cd4979e4789a7f35fcb3e8496158eb615564a5d7fc25466063b6cd232c88d3c502515479c9877bf1bbac02afb351e86c4a58a283e0926866249755a6fb43168dba1ac8f48b3e07c5d562c3e466bfdeb3d8a73668639d1315d99a8d4bbb50cbf802a5b929745144249ee0ca8f40ed469db8021f05c8528634f20aef442f812b196c811a117a7959a88ec8d698a787308e8abacdf81a5491fc439854cd54ff2452e576cc3166baab65887e3cd8fc9b231299f088daf2994b1529a502bb85370641d3f3967784a7e70506c2227c7159aa06aec3e8eb33d0f2db9f985f409e2578e0e1d3d7b11eaee9529e8e5d1d03d61eb9a3e8098484d543b554895046aff22c2cf177153006a98c8dd86becedc904c90e3c4435f7c40209a55eb0263c6db41b59619da67e5ca0e7bc030681cdd11b51d14b9b004cef65bbc7c43ff01ac6f5e6cb58e7094ad3dd750f9412285e593e8c997642dd4664403605ea60053d1597546c63922ae4130f738f31f5c500f583d26a86d6e28c9199b0b443e384b16c2e5b6c170a12a8f369a12e8a2e773c45b14a752d74e35613496b1b92425c396854d79878b249b63b9e067eab1b0da87be0849852ab9c405f92baced4bb97c00bb3807a509251bead6467afe018bc65d7beff41904c7b1ec98141742655a289044d2a551152a9c305dd9e44207a1eee534a02a7017565ca15ca69029abe066ce045c7db8861c16da434a088453414263631e58e527f07a94e22bd19124868f7ab6969b075389d7ededd54226765e739cece81379412d342673b105a8fbd2f9316d55de431197094d589ab908cc11114d06369689c1aac718a280508fd007f2de5a4247b998598f894567c3fbcda8751b97dc1945a7dfa29ab0d217efb55e4bbe581c3d7ba8ace3c11cb7fcdfee29deb705795c7cc23c78f44446488cd94ad923cbda241ab23409ae6559e7abb98d828a037f55d6f024e5e33237b1fd62918dfaf461c98fde26467be9d1f659d2f3fb5117ca971ec4d479a2d554cc8676a826481433c2e7e72cf8506374daf91e28e953fad9e16181f1d040042597220a07d41d241b2f56d920953f76397a7d1833b760acc1a442d6d0ae88c25958e083d86c737416b7465035b232546ae119689550fceb0902fb9bdb3d3919eebfaf3c5159fb30f37ff8f4a8a9125e04d6cbb05f7b3786f778afed1daefd9ba8ae175df0790ce911210ce283679c3c1c08a3594705544724161e23c00508ee21c8e0c9c2e8a3600ecd0458ba5c47eacc35416209b0fd5ac607f71a486fdd13a0a3beef801a6a458e7398a77355c33df48df0a037a541b69f0327a938300c93566ee88643d6e3acd018aa1fc117cbf3a5eb5e43d9dfaf4fe5b5348d8bcedd7720613380c8a58b14360e00d4790ae316f7e59ae45893a1140c8f45f9ad6f75890a9a9503795ae77c51674ffdcf823506b2c48cd4a2141a6b300a569b1ea9a47f563b52676d1c176e840dfc9a9d9219c10b952e8ac00e49a54905645f0128266c5489fc447686f2a3a0205c2246168fe7dd232eb971cd9b7677f554de4995c17647e649e3e6f6b44522366498fe32bf251a038db83255ccc61ab9e0004db90a6b78b7ecdaa317e1200b24200c1ad23cce28b0064685d5d3390e8d927d9a7041bb98f587a58b506c35440d77c613135321986fceb92b566d806e3bd79bdfd5de1c26945d97c39f0458d1cb1b6090977f36f632cbc5a187a5ca396cd6fe1681f8ab22e6d4094fa68dbea28d7ce3d21578ca0ae4bdd359167d2d7b4f43af95bfbf6b1f87815b8086dc606ba158f47463a9fa6935fa324a9bfdbcd961c122d744d0350f9ba824eb72675484de81c01ee84f2561759690fe5fe7d61954f219dbc2832ad1fdab300773d27ff6d4fa6a0b018bd0f9c3cbde52a0273316c4eeafa4f1f033d2c9a6767d8e60588004563bd8be14d4e0ee9edf5ebed5d67288258d6a4484b174af1dd2c311ced6dabf900e58abc0cd9337d994385f9ec1ba98b488194a534aec155993d5287c61d639235271dd50e8465f394eab49a7aeee0a3751d2861f0199e659d519e2e06e30b683185f221a4cc6a4ac6482270d584c5a0fb1f95d60a04f56e4537c1dfdaf04587893dd7307dfa4db2e8a7088c18c362539cd18e79782663dd849a9b40809f0f6ee6a8acfb03f8f77bc1916c5a0e01c6f0386f65e9675f35db96afdedd554d05e4474e5435572c62e823afc894d5850c894f352780fa75f161eeba5a12a2e6abad56a4e3bda768573a2e44e4d4bc6d584db1ca4407acac2a3c54d3c95ae8e05159f32e862bdd11ffd6d1e46004ae9e67c419466186dcb431d4093b31fcf677de1151fdb4d0f397a20dd7182b39f4b7a47c6ea0f3580fad3d67f0e7b38d93d285dc9d4b856ca4739e196d844298f3310e594479a95fb401d3a38fe61bf9dd206a303cf6af7a8c0e55fbd302ff1125b41be4bf50789a500698e23fb0afcf898d5d4066a0c786cc301f82586d1a73bc09bd089ab3dde2fc2f7fd24c2c97b5a707eef90042417c3fabbcad05ec42f9143e1c5225172b775aa2a84def827f0cd8fe3702ebf9b35278065837caea1ede4c46fdffb9808c8cfa9fd3489d6c8ad91061182baff03787a116f40ee87ece79d8721c25040a905a3d9b4e70fbaee914df7276c8be7c1c3167629a909436bd717cb9bfce11382e1512e8eda9ac53ecb3700bf3082cb736405c1074350c795ebd2979dc92371015e402df2656bbb003458162db40ee4533f3dd93a65777ae6c50edae9456b51c0d6842ed11ab6f05c12c43900e83ba959f2ddc97f1a352c26ea38ec9769f2f1373a4abd44a2c948f5c941d6b9d4a2c0ca9d59667291b8c18fd3918111ac6a92d54ea4eda7cffa0b4e6bc13adb04da33407f53281b443e0b6f048a0068ce669641a01f0bc31368fe864e985d76a124b032286293b21622722850c7ca49149989d0f0dd748c1a86bd3188d123be5cf781a3a567a822c7d7ea9895149fae27f846a862bac9a7221ea970476e69c5e2d0ab2aedd07079dda75c968d535db44e614adecbbc617ae6411c52c660a21a81861d44b5b982a7ac9d961496004a743b7db26a051b541f3b11a510af184240020bff7495adcfa239df36b28da5805510e20f5977fe0fecf1e19c5e8c3ed1368d4057171eb85318dbd1e2662538ba24845d889b65ba132fff166c95caac0a580102a7ccc05d99f87680ea8334352055741022d6ca21de5c7bbefb7694b5f39cd07714b8e079728be1dadd50ec56e33abeb94c0f3c0c7227583011ca09ad9995be8a86893e84fbfc2d0bbdf73f3a447a06762ce91ced3fbe3552a53f4a89a0655e018fd06315632d64540e9e8d99b8c02fd28ab51bc4833f19a3c7e36e5171e798d80d0aef70a84bdc4bd6fee6ee5dabe572e5def264d78f86f55d5681bd183705aae738554efd5a6eac73b82e95f8860dfa579babd7e5921f0e6258362edcbb263e1f67ea370e2db296958d1a9fb77c665a28c2e100be7e71aca40b3424cfb54d6ab00ddcc7acfb0f12d68050cc596a915ab0563b13d76105f55b3261c3058729b0871ef4a3a65636549918fb8631f13de9fe78a00e9c50c01c1c6097af56b6193c1bdcf62361746c9914475d92221f13ea0e74c4398042740470cb9cd67f5b56315bb0794059858233af823bf6222e1de3ef77187fb00410055c4f18cc719250381885bfa95184e2a6cf7c529452ead01bb8892fe85e9c32193a11a30ea2685b3add7c6b605432fdf222d25f00ff05fab676b2bd837f1bd47705045020277ccbd4e877f0ac62b96d8839bb49dc697aef148de76fa21109175b6d6904a5c04778cfbaee74f8651c10d4c62b3f612a67eb27e6c75e822d7a87d6aca06d3ab54b8c22e1864722e12630fb5f00c7cc2cbe53e88f66c83bf6a0b23bd9ddce15d46995e51476d2df587ecfb72aa571c949a153874848502a3b79e047deb52b113fa3d38b0c603dde66c33870a3930bd5a4ff1dcbbfa6242dfee2bda3f286623216cd866295939ee2cf26a76650742e8ae3b435644a9b90fb6f461f903ce4a5294111fcb3baef7d86423964e53ecd8b6e7688ac390176ee73111895e16c1535595e70667c7686ae80e76ab31fdb1dc5cd01f2d3059a731e4b33a9902a1fa32a646cd1d9f2e2fe00f2dba588c7c32b3400509a80ca5388fc308feb1e5ccefccbb513e093e7198fe02c6cd468797caed74cf9c3a78ebab3fe8896d40e0dafc818ea703dc31f5206e4cda8bfe46ff3ffbd79c813cf8dd67c6e1dfc5fcbd83bc586448d8bd1c20a3639e3b23de64034cbdb83909d1ff513c762c81427b34ffbb88c5bb855698f4027710f170702c33b5242ccedffa3d7f5dad702d8b7df84d57dcfa72f36035389769515bbb0b5add5e1733b3a549b0d0458a85cd880883990398676b557ad190c40c5d09b553b3b165a9635867ff309b9a582083e6ae013bf14f029add51926f9f5d512c448f82ce207789a3c5bb12d666218224ae9eebcb4ca2af59d5cd15a673634f2726c20a8d663a9ae08a8b6cea5be96c1e3c8f04805313b2e0eb4b54fb86b9578ff8a3148652952924082fed2f172da1d848ce500914c6aa768d86403c1d48b48a7948215db87ea3c2da7463a69268a6cff945a5850570530aefc0ed6131f96302f94d03e9b4255f73e1e95bc1f0905a7218c0315242394e6a2347b8c7f636cb7813efbb3fdad4adb0be902a09ee618f7d524843dd6e98efd92f0e6df4f0c1dd9affa89915ba2028abc9e9fa3f1f58737846b11b6c55582b35d63cfb689f20135a20cc887ded373fb187d502a2aed8c71dc4f7ecdb026a591ac1a87a1093838c9951682419425d2221373a162c60f07d20e6b78a5fd1f669025f217272fd1042f0045f9684d8ac15088908fd890db5ea96fb00c4368949385f1392acc18dac4beb83e32e06ab8dd455838ededa0313a6013ebb9ded9cdc32891a8bbc4707c1c3b380e6f235c79cd1d2dc3f09401720420bbe84694c13290db4341de54c68dad05814b7ba96f11e6585aeae59bb7a949009295631efe3e2d162016435e3e32c04c1ad1dad1b8e8eabcec260a7d0865fbab7c2fcd28b173e6d83e80b92f8db7de02448634bed61b34e07574649e8c6cc81cb6bca4b42e07c62d17fe568cc742ae2cb85d013839857346c46686da435a8fccb5afa63f1313fa25326a3b2a39635f1a410294797f45306058964654d0801a79eedc64c1cabadb54cc844c8b4af50cfec5922c3bbb7fc0e91adaf042d167d6f021b7a1e8335a1e9763f814217cc42933460d178526de09b6f459c13cb1105626b20f7d5a81596d51b1a703a31df3616acc9b51e48779d1fceb29cae003e0626d8533cbcfb56024bcdcce656774212b0e968328c88125dd0593b0d9aa1ebc76606fa78ccb8569bfd43d1e7e31e82bb3f755ac8e4dd27c4db0d2b45607881feebddbc6b87b160c057371a34763190acbd42e68ccd374d1e16e06f472bf940b990147a2a973139a9329d19f19ff7ed094727b3b49e5a656204dd2d2cd7088f97835d47853569161ae0246fdb592eeaae22af2d035d5e884f515f560afb860c6da17d1702be3704895394b6515a4ece2eee862d76bd38484c96f218b89ac907292e342737c7a5d79ae5d0709549286b277d13a1d12b9c39f6f04178ed488a25a674fb0b12921d4a46266152bc53cfbf59508d713b59d0711752f64ed0a28919a4e24840d90eb65d2bbad32dcfc0b475fd2236bcc24253856798b5caa257c13c1bf14fa0f83bfaa28ecaaece37ae341568493786dd4eb8d037db3dedb02df86c66e6775d301f5ed061f482eccae39aea2ddcaea380c58a5685e6fe0ae2f042103ffbc0b470b54e2ae441c4c0806d55be5346f48a438706960103c128f4c42c43c008ad115fc29bf050b494806d815e7f60a3851940d953787fe9fe214d4ad3605d52b3455288eeaf0c408d85ff93582692bbe87f7d8e2d5a633598d4fbb4107f6aa415bb1d1486ce011d88d6c647b092f7c31d21717f40941e7c2e43bb70d2040d734925f00bb05f929141e9dc53c2936af79d51d0aca82cb7ff097d953dd412ae5fd60c32030ce18a16e7110b3906bd883e159b9d5679897a26040714036ac2e3b746e931283df9171b4f66f3e52c9f4093a14fd0b3efd1baf9372b704292c64d18a0875dea3e3537c0dcd83accc22cc6db7f0d04890ecf6673010174655ae219a7867aa1d48032fbc67d4700b4ad77c2fe40be8f5821303a145e61f6598c2b181ed74bc8905e6e3d8540993c640c123fbbd273d8f81949b787f1988b3cf996ef6288786f86f60c0a888fa14413718e2dd9a760fe0668c668ae337c08c1e20019da1148a801fa36d324f8689f38926132cd2a1f08ea0fc224b3381ff02b25229725d41676f29d28e77d290f8d590d9ac8e64207c2339a8a6cccd497e25d7f67b64e0da69d61a737654099e09673d6c7f45e330dc063686a9b24cf44ef3ced2bb82f001263a0044d4643e82b3daf6ac02e7a3c88712bc3b7c3928cfbfea74ff2891a3a04c62c67c8d944bbedb5081b01c04f2010a9b6b5e74c6af0a108c7731e5405de87f76868d587f09ad179414bca77044921a03a283ce2cf412ee7d228c812fad7248990cbcff59ce1dbfa3b8cd2415a8ba35b1270603d9fff2c6801ad9ad98842b3871357168b16c264069a9fc912d3dfc11e1bfcde4bea5de357f551ce7329004bb9c1613e9a10659765cb40661ac7a5bc618d02935c675572c3b5ce39f9caf87f9d600d5dc950c799226f5c24a059c0740bd6daf395b07e30e51b4160e284382704871468ded74ac00a94cc5ff5bf456693da604cf2eab0ebd59cb3df1ac204be84d0f07375609d4a1f96c39b4a49098bc384a3358a2d9845ac7ba029dcd362c7d62fd5743c7dfbbba7b0ae6e2d258ca0ac1016ebf3b8c0a9f75dffb06d3709f64fe1da3f2f497224c4676e84b45c7db5fd8bdc41f0892e73ae790cc99c7d38b2fd27d6f04a8463fd8b4e739ca0c5a969a541aa2205f4341452e325b27f3a869e419c7c7749b33820366d2fee339e1d84bf47472802eedbb605867a9aa17af7881c6259d7b9bffb0f5f4f419b59c2bfe16829c1ad92274b5e1d4afc7896f5bca176a2caa4ddbfa42db5f1c548d0d45fe57d1b35790ccc66a19f18ab08853bf6faf81136914141278231660d2db216dd0338f18e0eb0c9774677c8bfddb34e68cdb0605ae604afdff8469d1e41370f066d2d151cff29c4e11a10f9c947222185c17aab7ea432c2b2e2f428c5913fc4b20523f81ff8812a7cc8ed6538700a439679f5978170b96c1fbf9f8a277bb76f77201a9d39a99cc3d3cf49e9a3d69333bda6ba51aa6c2c44440069994cbdb03efbeb28ef74132b6768741282c167f46ac2bbbf7db823f6659a08890ee87130e2d8290eabc84a8b46eda49523c5583d9c41bb088b7378192db1ea7b32cf10badd52dfe2ad5805459a3d73d6d945560f7050cc7e01e2f73e143ff45e898e0999073845d2a731edfdf90d88378e7e8b21ae672f904631b6dd3cfd9a225917a5cb035a8ce8560cbbc717e38988a1168ecbedbef7a11992d51ff32c12eb0cc6ad583d5ce1bec47e3bc8f78ee9022b1d75bcd228847e81b91d5924b870705f38a1f309ea4ee477f567ab13e3a2a9fffe619fc005dfc424b08e3edb0da028bd2c4c030104ad4f80b2a229bb460c82da2c0d39591357efc1e58891217d33dbcd8936be2b74e3da283091e2c691bc64224578a125a96b2c43a368425125aa011f8fbd89a08642b5a079b1cb1f59c2284011ebc497208bda0512c83867ce3e9f2d4e6085398d164d4aa01fbd8ec4427adc54ea153758effb7a1b462ca4e7805e5232b601b90be90671f874218d78b33adbcbab5680d9757756392035a0f62c2d790ce996a8af3df91c4e0cf94ccc2dac38418307610bf7d9eeb98afd7320168b90218dce9dea615469c2b06d70879745967cf3c904799b991f7f8fdede8b9ae250ac2c9188740ab73174c3386c352688ad96550b1280020d8f7395dacfb98e84b3ee343cfdf81bd840aa96034157b264cb5b6135d347ae036dee257e9147f608b8fcb041599387dae52eabe2836d91c2f2137c45005925d0e64431957932fca927cf5e9d8c25f12384ef5f2c320176bbe0fa5a8da580b48f265ad9062a3cef66e606dc9b85c9be367506d4214ec231669e0c56689f6f300245fc86b7ac3723e111328d6188252a33e21fd7e00506a42a8654752125b445fe0185b6575bb9a5cd0e336429a8e68f284f06291bda852425ed9d53a7ab7d4123d6f9211afea2709d67b94cc93de84273b090901ea7007693425f1ce634e25039e46632241366cd9468502136f307d337749972423f801bda7a5b44a1895d10b361eb162e83b0958bacacb22894412dc3d2fa60d93a37307f27d8a0f823eacf7ab7ef8009b98fb2f9d90a71e81354bba058a32e844187bd4fd3e40c40942d8eeef3ea2e79a86174cc339f9cb84473229d20b32cbecf6389c349a1e60e20da9c52de1d58ca2b9895c6009d470a4013464a460a36de00321c28276f4d69445688c3c6ad1de9906735c52b33c54646acbbb604413d13d30b7b55a299ce9913de96be23d96437cdf6658aaff78968a41c3be0ad196924b4dfb4e44b5591a58467bd1c6ac4d89c288b7ab0b37e7610db464a26c154ed6cfad8bfc691eb6501355f3400b61ce9774540414be58a90787fc2a5aa9a746f684967c18bffd9d6a9ea3ca4f68c23bda359af240e4650e8c1a13bd2198ea162dc1d747e6e3329b9a9a59186763ad99e32c4f1e83bb53d0f7f73067f839c3e48ad66b27950669698dd01090a26fd2dc2ce416224dc7349b0f71ca05778cb44d02b44c310f9985d3a03361abf03e485505455940fa9455e1b47bea217cf62654a127ab9e29febc14696b805aeab0af58aa29bb1de05b144ea96108763e712c5ab1607c7a7803393bbae1517dd67ab2039b0b4e34777c4ee8d58b6e96c52f60892f477c21e287c3a899524c7aa5b78ffe0bd892f364d285c869928f82ec28718cbf264f46ec0b8c962800bb976f864921b3e1c0692e2bb938a7057df4964290ae1198572ba9d43b782c88b83d6e3fa1fa79fcf2a06752ba524bbe5577e2324efb70a26175710506e9c72c4cde18626a1b1fbe33ea809a28a30e79baf17001b3cb93b9ac63db0d0b1f8fa6acc18f8acd66b2b4218738fed7e9eafa43c52a5c299f15a8ebc61882e9397966f12beb1f17aff21eb23e75683cdf204a648e780ef276cc2614e38e7913452b944109844fdd6187e83421fe3d8f8427356efafd8f02a0f82f6839b6903ea8a6342c6a27a3f49596d3547e4fc3e67b015f9acbff5404fa6a504cb45a702210bd0114b436d4fe89af3204750547d2949c1f1e9d9cdb321bdd0f6b1d4df37c3842855380d638a1b0aebcf405e8274bb40c9b01c0f09d6b4ae1857d69757523c0b8e10b1dfa1ac978c35c7e3558183a034b7bd2c48a142300fabd5a84ff222e97866e38f49619bfa4a48d64fa232d5bb8c12f1ff73a3b0eca4f9e39484f396c764b9e0a84b411f7d9314fd2a013b22dad01f09cd0a1382524fa2fe4c0023e341b31668c4cb842a0a8794e2308b1aa3aa3f48cbafa08b21f989f515da64751222e38940464188f7784fae83938d9b639045b17de563119cb65a568e22ac4d30246b3340b4dab44c0957bab5b0e485b073f9a77224881cb6df160adaf6c0310fc64373778858a891e82d0350ec7333a0433286a1522c6a770942fa7678f308c04cbd2dd6c6fed921d674a92bec4d00f111ba6995cebd0b33fb90443c35018c756d7c9adf5d7ee169382547be84f211c4c991f02072859fefaa0d5dab88c2ef0e395ccbaafcec794f658191e0b741565f62414891baac1694fdfe88c3aa4295fe94f98e39fce973c12c79e3437e4399d155494c91e3552db8423aad2bd809e20da4b592e996eb6e5fc93418e4306aa9ca9f7af26e05a8033a8db440c8cb1d45a262e4384e8a4e19d26c47daf376a949f02f154e6895d0a5e54e633253f0a72a0892ebb4d6b112641a85a8133e50e54618381f059cb697c4449a486527bcab39c91b92f9d0fd41d14b2b506dc80acc6eed00741459a96f39fc3e1530642e22e741180449998824092f150efc06a117ec3fa48ee68e9a6e8a04da730622acd3e10ae83dfbacdbcf2ed82298e2420f789a173bb3e864362ce29e68a8ebe682ac1819e24fabb4442b09570b9bd4e622330e4b1d120b8494d4f679af1e9274e508d24cc832ba451f5d46bca5260b2b0425e77381c8e2eda117eb43dc925b8e45a973280d79bf03e64395e960ec013a312ab059686141c0856e8cfa6503c036a7845fa68a2b5c67dc480a72183ca97434dbf908694ab84bf8353691aab9ac1589746afdce1c972de03b13c1052fb1baf35e3fd8c0cc137a4c0c21f77f92ac60c93c1ba60cc334b70b32a4cce9f07c255ffb2531c318d69fd722ede9a262aad8307166a4285e97b3d4d0fc347f3d080f09ebdaf7f234d3772c2da8f526b58bae240bd4f55e140e1fc6b73305bc7c0e57336b784da66ced8361814d768085d2668012bc886029980ac2f3989d7b63add697c8ca255e41dc3f3bb5090cbc4fb85239560d19ef01c8b81d1f8bd5c6791c8e0a21512c5ae2d19c768a1d7a3124016fd62e6d27f7f0ed5c3388ef5e41e0e1c4a82860f09f7dc1cf17b83298a09aa6dd1b329a0aedc8a6def5edadb808ce8ed27c0b4f0d84994408daa4b37d00d01ec531fc404a9cc44f674b49bcbc02361e53a1af0eb805efa02c006991515eecb0c2f9f9420a41f5740856670396e3f1db539cfa6d56f9ffe6dcdd588d7f6f724a2b706def2813a154c00880bab704e8e09dae68e7a08c8f5408f4b70e541748805e9aa83f277a050057fff12964fdf9a2da62f0e179b062469ce179caf0945bb101f92b5d175851d7a505fb9d34e3f91f709b51788dd9c426bcc58f3562a20bfa67783a87ff6fdaa4b19d1408f7860e6598fbe6dcb7e05dfa10c4fa74393926ac0ecbd0b16bcf40ae5358203eeb5870581bc9e37a051d8423d95459488966a9f27c09005ea1788608e0b5e854e004fd96a418b84f9961c0860e01c00339527614d099f493112211fdc421135d7780f6fb10cc3e5b3b309d52b4df365adbae018e011f0ab714313d4e52bc19f913977e3192da8ddf058722ec8e4b86f71221ec0920d38eec4318d918182184cce9366b2bfc287e5fb26e75a55541c4e1e05d178545d7a6ad67af288dcae4a621df1861b9e56c18948dfe7531ac117c7d29064385f2151cba9a3f90f5ff66dc4a4e64a8d877c124adca04f73f56dbed33b610a122a379cd07d856a955c1d3e0aa6faa913662a0ce22446eb5801283fc6dd74c18381a217c8799556a7b2ecee63ee4e37b9a10e0625c34b855879c5c8e24f07854b41878ca8c50ddec38361880d908d3cac62e2aa7e131c8612746f12e92d7eefcc2d3edd172eec9d35b7f411ae5e2a7765d3ac4f0b44e16570d4a916b9d66459b09a89b7aaa2027b95f6e1a11b3d7bd7d4e38c708f87d476dfdb934a8f8f5a99c224b238ced17cdd95ff50d21f9800e832a1f76d9f9fff7ecec940cbaec2494a33c32ea52fed172974158019f433776f97775bedbddf70c37c8d8cb6c8fa741aa0c13a52a2a946587435e4e6b3596f326f86d58e5cba94c3d66328c07fd0064506148dbfe8f62086cd9391c08240742c7116b65188884624fd806d648af82a4d180c3f545e53cb2300bd529a2dc0e78a5f5692c65607e2fbe5332146430c65a42079eb862726af92c85c16a59e61d4ba86a7c7d1791708c0b1b9d5f76a63d1893f16afdd959e6c4ba70307d078dcdecdc6746cf298e452c49a16c1beecce7879a5568256c2f86caa900c7d549ee98ad3d55a79891881915dc49d86760799b680a057082189d2583e9f5766cbb4fa7f85490d6e4d3609448617e6e2be3a9a7fae74b24f913f94835bb368b231fe39caf22d0c51527c123f78df37a96e2e1bf363221778bce916a3115cb94eb81b8003ff23efe347c1820b417ec494a16ca327d52a7ad2415bb650e7e11312cb3f0a273a78a5ccc978a7d7a0030a7d29eb2efd932bdeaa56c22312da8b23357be23ddd3251ab8098cc1f3829bbc15f71f81b03167e3585d274f4b870295e72b3a67befa8471c06d259636057a7fd21fd26b413f4bd076ffe3c0dffef1deb5555fe0b6b9dc01866e66b2ac4a119a02684474bbc65ccb1ddc5b82bba30dc8be9b854a7ac793d5e0e3651962f44da81ddae1bb4573a49cc0385aa56753ad61ba5a61eea9f2d2bd620273be68da02c6b8a8ce027ae4cd245e3df7bc84f7d4c1dad1112992fda2107ea68c81d4ae1a581852b22b8ca08dc6623c605f23032a4842af7c4996450ab2d698dda2d317461dcdc95c902758b63bf8bbadd2f7e9741b91f90a3ac6762eb3fffbcf59d1e3bccb528cd4477d6dd8c7222fb98f0b3ee2475271a13152b52e30c9c1a1438b464c5455ea1df8797575dc081c55b9653c244ce77d485af19d4ea5e341c75488bed5dae052bb19ee49be0d82ee6a74fb2d3211577eed127150a4c2257687a2de6347da9301919249232a6a0d0a0d20e5e73f08873faddd807c100ad45bcaa5016215f98fdc2137f54755282792de45b37f77d421a138c17a74600638539772fcbda017499673a06af6ccc0462978639c4d08d59f2654af1b7721887dd4b50dcc9384d3bc738b34b0ef4eaffa38cfed10c5fe8a647068c59fc3fcf65afa076fcf66f7640e7490b59375823e9aa45a041f6dc1ea1407eec8afb6fdc5428916720f360d7cfab1707260db006c0b9edda7a89c1dc02e0a26fc771b041e8308f61ec27e28272cc9de72303837d0e0655830b637f45df93dea25d2145961fd8012482d32d4e27d45ed5cdc9750c7f0f35705c63e4c5405910c3d1ac07e408c9e688024252226bd3e69debb05aed6ebcf681853835c859b745880c04cb6ec3adb29e7b1c93aa11904e88023c46992816cf9b73e8e4a97e72aff45573adab0ca90a937e7b67766890facd520b50d5c17a5b7ad4b61a8c5ea7d88ffe3294f10f24355c9ca73b2cc0c8e2e31b03e1480739b7d9f38750cd263a94415fa78d07539faf862522bb2b9c6ba8386b3b16e8fe1dcbdf578f6c15d116c39b18703650a3ef3194617dbc08205178e3927431b9b075deb907ae8b6dee5dd60f7cf3f1daaed72246f69c002eb6674b585394c8443a5ea25074d8b45dcf9b31ff6264ffeb8b017b58973e50ca93c8e2d32e0a405a03734d439e3fdc6b6eec8a30427fe13b8bc8a9bebb220af97a21ec8a3a3200419058a759596424ec473b1056f275339a837342f6754e2673f316f3ecfe74bcbccfb2dc2378e4bd98dae033683419ee53163d1c4913aaeccd2619f39966725c249bf3c3c771b1f4deaf6c31a2abe31feb203c2791731f6e2622cda8efa573ade394ab147e207b3666d123de74267d0227dd6f247886c6592d640432d683d7d30435ef8690ae7cce5431659a50d3e223de61f67a7d584cf41600f75aac1e1e23a81342ed061a6e4b25e260d9dae38693ac72694424c2a9a57b6b39516de0fbe06179562cbd362e78c3a39dea3856b58c728f05a3b2f6b8f4969810375210783e3da1598d7281156f7dd8c889bfe974bf0f6fa6a63ac84d69f8605dc6659ce0ace41a056c91f192c984b845719442534d1dc9401e1df10a3924c62affd09d890865ba48f28cb1b305e7c62a2c35e74dfe2d51b38d9a0e61e5fc795351749c1471dc853d744d766e01217e40ccb6e558f005a357507d0e2adc37aa4efc1c19bca040711e1b61c93a56563cb6e38967a23460b7044e2f20fe48a8fc23c2595ed1cdc59a3531a0349114c81501e1988b9c62aae564f8282bca07c45d9f2cbcea0a6f6cd36c6b13742d44f0254bba8e48f6338629b1adb87a1b6a54b7da1516850d183086ef24854d6cb337742eb1304cd20a0333eb02649c85de28acd904a65754214095721e19e4dd222a865ad62aba48aa31e62067e051fb8f4bb42b320b1725a94a699e08295574d11972324779605a1d03d98258a959dd32f318eed3b74e92a709539f1af4088052b80e922c021f09fb32bc42cc8e3747e2e21b1ac2977101f80e5219cf5ffff17a1bcbd38e41a8076924654dad44acb3d91122cb39ef28dd4407b2befe4332341be4354473cac902b3d69416fa3f53c34b12016690a91df54329d9a873129a691a275933f1afc1f3fe7a3ff9f4278c18c81ba6cd690b7b55cea40f3837ae03ef936d2b5792b1d47e52507deb780b5a0d303f009f05e3e8565b4234875c014fe4032d78d7cb0205e18b55645367ccf0d9a999aca766f8d768714ea1b36f936ba4adb54dd3256d0dff2ef0561c549180c63cf0d506b13b9d89f33a9cf042c04b5a184c9268b99583f785448a7d8fc9a490a3dc38147bc16d8d18797f1e40b48a41904b75d5dfc233e068c8d389aef42a6cf5f311d3829a2e4154f58f611dbd7faee6920be7efcb54063c33f7ecff15c259eb9c0db3d23b069a5292d1f60655c482a8d199354c81a18e94a9f2e5ff723caabbb120f249c36336f3fa0a5787c1c0b170867283ca6892228306f6ac7af173eb32a7dfceafdb252114f809ceb8bdfea492803d7fe62a5a8cc3d7c1069fb971ab4142d5bad0af3f94f96238e26d158400afb4caaa1fffd338cb3a0f4864e92b9851827fa4a6ad0b49776dece3049f6623ed37ced2b5d90d62e49f5ba2bcd744e3ccfd0088df3edeaf557433dbd0d799163b4381324cf34c6bdc46cfbcc75c369780848f0d1d03a1459c15e45b41a38fd3eca3afeed13416f45bf839ed0ce6600a21e858ac9d1c4b841229645bf860525a24fa6fb1b1c99e7e64c3b8012a1ce68f2e380c866886b857a2fc7720c61d81ea7cc09d198c047a2d0e51e0aa1ef83a4215194e6d05c83201807a9351629903f4998c15a29d1c9d454fb75342db3ebdd62a6a8e0ca73858672ec1f2829a088247cfa4be11b203be54ed61ee0c551098897ea54e19bdc19976972f96279eb9abb58d524572d6e836a04c721765b6c7c1b0e54dbc4d5658889bdf645db76ddf36a0940a09b4478bb0bb9bb70704cc392f5415291feffbe9ec86f8740c57eaf7f8b24fc3a673d83611aab129df57120aa5a67f250ff0571da7f1e1a90009ebd758b768a7c682c0c9d36d50573b2333f84a30a2079c052598584c07238f0322dc3134b5f36bd4fa141b48114fc7edaa4e267055070829c25ee87b735a16766ee66fe522750dae0eee91a08174b427a769350b9b59d2ae57928180698e03fd9b2ae96692e90490832f12a1ad27707c071d4531a02b824224fdb1306468c4a94103d33d23ea3d0d2ec5c6c07a7cd508159cab06978d386605b9fb63d7a28e32553bdf891a17f841a880d1bc850f5250622edc525d50bb101cf4221e8dae4578e1227fc6be09ed166c806e4b5f53b57f478841f175606d4c3f70c4ec67871c06dea0cd40c81e1908a90fdbcae19222d026d0461c80ac9215767b4943d7503d188554f1f8c3af6186b100d6e11cf868589e45a2e52513f730df59c9afd154dfcce1d4a72144a2915a853664b75e210eeedea77931144dc7254dd1619585f8f76de7b8e805021426f7162646b38fb86fa1ff45504827c978eefcd4c068f9d38c92722030674d0ca17f6e53ef90def4a5a82848d1200d5d8c42e5389cab9830dcb3936cf564add54ce47bc0373f863e1dc85b556a998733aa1aeb8d0ab92e8d8cea406dbb505128752b9dc5a4062baf0fe787d24f2797f0224f052b1041ce4caf44cd446cecd9c68e6111b43afd081c6f5fb71d34e8d36f2a8f91dfd127c9df514d681ed7a9e6989110f115d471d05019e27919b7e172f2e803abef38c1acd73c406331d602e1231146cb723eef322ce89171997bb3428ba7a37ae72e8807d9782647487a7ceb8f4edfcba4156e584b9ee40c04e23fee8190f6389d4233d3ad120a736ec3b53d508225911f3cd431f0699af00a79c3bf55119e4fa139f02133e9a8e0640b4258724f96bc8d23308a3d0626677d13b33da258906dc65c4192de0c13f3f48d0408eddbae4816b1dd60281f04f3d983706d6dc89475a145ed791db41bfb5e538778209e3e0716e97984dd0d551c6c19a5dbf4f861c75ca32335efc508f52e659e2ac6bc9fed345c493d3537e090b866fe1d2cbceb7e581c5fe478843ca19e45e8c1f359b6aadd715a8f5eae2e03a844229b107ce1a3ff621c82568e857c215e24c1685faa4cc8c4d1381b6846cbdbfe325c69f118d0377be13a5caffdeeb53ffc5ce2a5bded5c511e49f1b9b4566971ee4383642f844433ea0df00f53425cce70eef3a0fa61720ced3180d0fa41332d7032266e8e14dc403e007a394495c376b303cf7d7d8696d210db2d1f1cf8841ea15d4fb9941bd26d48a0b5904825a1f2fe37fbf4e21dfb053c3e0b21faf4af31a4e5072851405c50f7bd2f063867dc4eb92c79c87c1dfc30080ee2b2e92ba4ee1ada072e3dfb24875b4b983881ff0309db854cdd804529cc7836ab5f9ce39696c9bfcf714d5554d54b406500a997a55f1af7944e12061f2df401bd39d9e6d53c178e2a3f075214d2b98d695052e2142583ac62dca944ea0a74a08d701e374aaa7d8e48420b17e44c6221aafe60085cd4c17cb32e65099cda205ac53c110f847dcd2f327d0b158322cc5af8e0961149c397b9e73b4d1709cafd4401895a4f3779094d3ec6df84cff5042313ad759dccfee5ba6520aac6eacdc3fa2b9880d937e9e98b68cdfd9ec685dfe0d6277b3ba0a5d5e8d2b6122113bef2af0d92f18ab3d0fe882f1c015cd456ceaaadb459b0c16667f240afc16eaebdd8afdcfa72ef35e9f28bb0960143c86d81c320f54d0981f9013898d3ef85c3216cdd1ec5dcd89d611557f3ef0189c2a8adecc1ba492067161bf3abf9a64dd77a2ce468a6fedbf56fba4be4f746fda7efb8a96eb731e9d8be65415cf353936a3d7e244bb4ce7e0086989596447b54c879afadc88c5118ce3ad20c6334f58a184f620e9ede9d8b734f4902ca01454b36fc79df9092da18087fc0c1f40ec469d2e5bd31e4c360ae26aa2ee2bda9311a067dac299efe3777e93ec1ba1c8c45c5acdf1ae36b3b0cb98808f39548c7ab52ff2cdd65702bdb0f8c4529869ed642940ad0912c984065e1d0d69d7430f3a6683a7f78b057e01967ee0486d348945140d81f4b77c61c2c393d4a202b89603aaef32e08321c1cc0d6d75d8a17cc738eb80cce382278a169374f0e716337df38c1f69dbda2530e75a520a3ec60826d40364fc8c84ffeb7858a18874dd0bb6978681fbd14ec98ce247d1eeb536a75c09cc2d800b0761b6dbb5b4db6b973d6bbcfaa06fbb6b2e03c51f2a54e134665fc3e8c1c6a539476313487a96060184b428cdbb6f5b8bd83fc34043b799ad1a0d1dc9baf175b30768254b52a6b461fe5f640b685eae121f887c114a13e6afd78b7de3ce9436df70f654756b4fa32d50835e0e425de3ca18cb88144aaff308e09f57ab076880783baef055adb8c1a167782e28532735df38c92de259a6f7ae863926a56904e7ff6df2544ddaf1a853a338a8f41dc8ae500cbdb541ed950f2aaf672931b50531a8639672782832ab52aff6e1b892e1a44f8f653a301562f32cbc7ce65c6eacc9fb14e9e35f811e9f06d91eb0900cfe849579773c6e880adc5d91f963c06e0b2f2e8adb3558a6b289f235b1135bcb69cf3461d5f4cd0d7a58a39efd0b318427200e231da14acea0126b48390928292566c40432de6437aac3985cabcc9b4c0100bfca7dc4e560bb981cefc9a329a2c9e8af143ac58e7da723b1ae1671f6243f159c9e51dae8d3c4b9931b7426143e30e60385951bba573f15b0bf593050c671d206a10871d8272d38eba0cb1e3c40c10a1b54b060010d491555208a6003fa60a9ce587cf233da463709693fb96d5a5b5560e208acc966173d45c445c801f1860152bfe12d5cc44143d7d63ce5e79f0020031502dbccf473c2d62101780e974359c0d560b0d52640b1fbf86c5b6f608d274ce2ac97eeb456aeac23282f82e1358760d7750ee6f62c15db16ecaa0e5f52db3fd0248e2a7add542099c6c340f928903fd673bcdfdd07a567df9063446ea4b28f909dd6206d13927e515ef03a11f01d69f5c7c90b76f327f0b2879130723fb01523ca15b2265286cac7792d74d5ca0901a2f09ed1838d5ef732a3af6f4c2f3b7406c581121ebc5c50deb39eed19978bafc066418dca855e879d720c567c9eb7aa3aac26037a72e9b6a36cff13465891f7d9af8281fd9c45bd080bcdaa92e68a5e1db84063c36d1d503992a280e95d5795a07c9dd84401f19310362d0368a809629cf10864b887296813501a17f895cae235cc091e936e8fd594e4499ee3422323fd00fc9ef6e49d687d614efd909485c0a07c98c87128c463113a7fc8a06170482bbe25f27ab2429718f16e05d6729371f1626c8c9d34dca3ef73d889d574a19cddb6eaf7b9cbb2883d89194011149bd54ec65556c56f5bc6d2fa9c36372e6e68b352a822e150baf497d3273d6921ccc7af6b2080e4ca106ca839d3e2c76d05705880ff920f08830b435fa36613d4649a7721f57a0239dff5b35df9f5973f84ada3d8a3758729a44486acdc27f55d53e76384946439bd7574731e0894ea0391b0a6fb430bc130cb594662ca831be459e4c41ab8d1e2a32640fa881c4070e4a9fed4b6b105ff6942d4219e9555d164f660572f611dae10668b53fed97536b319d67566e43f81d73fd1751d0a4479ead63fbf489772eae8017e4ab365744efd038ffb0571a11063d6bf85a41ba11401ea3da21baa2254fce4a075c13ecf45295b1e1e1853c2ad0ca90c57495fed144a2b28413acb91a0774be6764410ad17efd01fdad4ddd98850271110390046a5f77556f6a64afd4ee6df1577ae51e1623345dba957ecb5ba63454a07fb6f38069def8e89fa9c478830fd08547fd544058c9142820524004660f04b2ee646e3a2aa8adfed8fd0f19587b80b8ccca8383dffe072935d3874e7e639d12fc594075bfcdd87898025646a9b9cb5b8481bffc08d894593b02b79639c0a8924ff7c744e4b872c6bc289cb18c9b3116aa6e9bad9dffa65f255aeedde90afc226e0a64ffb261cc8135308b60969373877aa9c9481f4384967c275beea2fa9ea786b23cd72386384aae326ab1ac11450c0b3460d7d0c85148abdc591b32ee84fba78486c57abda9e000e283047562b9adc50245ccecf8cf5191cbe97931011bcd025430f49d7fbfd6204fa42660d0d2d1adee701e81ff5a6ef46b7cc1e96fae9309bb0c10fe6e4bf206e3944fd85c90a71529d43cb6acea5643815c2bfa8347d6148e435570bfc83d754b5fcd32cbdbf33a2a0ad434fde62b09b71716d1d1564f6f6038bfde758d9344a5a01f149d605d2659f8ca37c7dfd036166fd344fbd460064caf33cb69947e7dfb7611b1b199b3217ffd1322cad73a7e0550e44934f2dc890826afd6611f8ada97158e06a7e66cc79bba8cfbc7cee65164d6d3fdf7824690b401ef5ed8a1fc865e002851820dab950e85bf26955ae671673af4c8d2942721503d566988fe8ee229b5377df4e463485b5a0220ccfc5d6186ec4c5ec778eb9d1b0ad8d0b89d5a84ab4e70f270b67b792cf0b0d4f5afc2e474fe283bfc8f84b1bc3a2daefec77b7a063dbc10bdf1949b09d5fe21156cc8a5c69b10b217437a86d31320ead7c4278559cf615f74d8e7cb71474f1aff696587b7407dd1af2ab0b8b263a7c694fae8e6fa6e3606c036abd621b2c90f6bf69c1582753c845055c171b8749bde56f6515219d76d17d73dd6a31a7354dd106056aae65dec90fb944500b58c8d115bb48052f3ef9f59a2304a3dcbd30f3ad41917ebac44d4555261ecb9bf2e6c0eda6bf2b390b120a8bb54568c2aedab1357b8af14ed37f1ef723ce331aea43a62f4907ccbb372751bd92854c4197129b381e7d9005e6eed85f343450fbd38ac01f30bf7e1b2f6672ea97be9311a7ca2481393a64a945096df928e8f899fffdf36de8990433145f3ac3bb5b909b7ae4e432fc02966cc2b0186d37d407d8b37cd6c838853fce51e12805df55766d81716e60ee8f679baab061b43961a40390805e67cd68e8f7078f90ef4712f5f1aec0249151fad89933676a729d5d710981dc0998fbeaf07e2d21b4b84800d484239af10965ee79d9557714b11585fb528caee4e4d21446a986a67dad8df725445e155ef8a652c71e5ae6f857b6692777ca44678b007bfdebcab8d264365b41174bd4b0071016e02d7e44d88abda4d741724bc30beaad26f470ae48684268e02abcb5efe428400080e160445dda883fdca5291d4d94bb8cb9c13f112dba99bc4c694612e09f81268fbb9e6afb18ddd16dd767f72a9a4b7feb8b9449254aad4c1aa8468b42f793e702433b86b1aeaaa7e2db647a69e45fa0dfb81882f6cd1887d53642e70686190b10581b55353f165b8c25a4545467f3cb64e0966ba292b14c4c19fe18e31a240441eeada3da227c30a7220bec0241f8b707d390c3990e95be3c58ecb05261db3f5ea3e5d30d408be36a8d964afd62b9d94eabd74e04eba1bf231ac1ddcbe6c36972c113962b537542e4b51628d5f8623750df22fc8b57348e741757011b3f27bada84c1bd1117be6ead55e9d210d8a0d3d40cd80304e434670710476e8cfe5db12212508d58821ea4335ad38f71e2e391c8b9374615c64c3820fa8fa33b0a8e0c84a68acfb842cb892f7f284b8081f023d93cff7111f5c5fc507543475739317a91e01434b590383179a1b951148ad004797dc4972f39e679498f626317a4853d0342d9c72faa2f54e6f88e2e399398bf026838835cf3db49118a2f4ca133fdf8a4e58d8ad49ad651b416239869f23ca326ac4c30617bd16177fc7f8ba816124247592074ac095b36fe252770f2145e3238d4288e4388b2e1ab3540788f27716881e4e80f1e97c659d1ba4a988cab6ba30fde028510b60a81899757c07bf70c467b02c22af1e271b6e36d2fe83447290ba36c0859e2fb846d8a828e5ece9209bd3f0d39e36c1bb153345999b3750d539a3c833978c90573b0f103daab471dcae8c5ee659a9b637a12813530fd091851f2b39e4bac7b361d93c9ec857266c2e3cdcd2602040f06193b6b56a4f06e955c1790fd44cacade5af946f561ca0fc81df40764b47348ad14f005ac53f48c64b1c10cf68fc2bc421290ca5c0ca51a55dfc69a59f5a16a2e745f6c85ab9a48f84ca2d096dfdb862f69cdc7e6c6f29b8f5d2451d889bcd846bb98113bc6486fcb7b396c1d347b0865dc397948b4ed16b8aef9bde2260bfa3d0190a32d4cfe998054da9f717e85712861c851d41b0916b5acd9891e411e2d2233ba22a8f13b20c41a188515dd5d7b4b27de1369569628268e363f139035ab2f78f777556aa89d30876810521769b76a4cd4e943a1877ea84da434f23b45b73033080614cd4a17a07a539a8b5c100649c70c260ac86351b2db24d900baceaf75ceae506a8a0baae081f865690098e82bf9b6a6947aa8bb0432616c9eea6ca0fbb4c4ace0e29f8a32d311914949dbf4ad844b68c662eba41514f0ac6763491b3423f099552e037c143ba25117434057d00e686f588f58bdb1f0cfc9d59a19270dc9783827842744aaf4400a8eb8a9a275642f281400ecb2a060efb11a57694b870b73090e254af73e627db5064e6cdaac8cf9198e3ba962aef89dc30268a5d4198343c84df57bfdd006310e8c642ee39ced6a370ed5b45165c4ace0b01c26e7fb106d282f3729075a101074d7aa014b625027db1765a1cd7716d1097a88c320f5c4940b787a7263048ef443c43d422ead99ab4178a85159e628da6e80459dd5d6550cb0a6a55752fab18146444be08e2d11a8061936477bee1b5432bc84ab18eabd1e78d8240818255e4e6f7eb10589644e25a8eb10182108eb6af16db440e72f1b1e34c6d74290baa557798c1b92dfaf8580f12d915248f0b8cc589e31804d1403858b9108bec9cd0c50f1407580b12f91ba10d9edf495f586443148765a940cbf3ff0d81f6f54e53292dc740023d40fbd779a145179db6f754ce9ee6246f9d292b97eca8c19a71426565522afbc709452af98bc57fc2e0b930079d1c0096781f0919265bf8b1f7f76ad957d3abc6a2564f43a74cf7d4801f4b8848458ab659451116d3a3a08c1e0371d25b8bf45a42182e7703a580df91075d30764d9839225e871d0fa3c5811f68af9710f4b0acedc9d75cb1da7660d3e2ef14ac9ab246bbe8f0c5391e7a16630938c82898caab2c235ecfde3b86e8df047456f2b5db3832e473ec37da351c69c987c19d1b8810bdd037d5980dd705327c44d4c3505fa261fe60aba8e03c1b8b2e9d5cf13dd8a77d1b273d1c58d09f0d991666ca8fbfc09efac037bb72805f1d8b90582e4ae54d146a26c826f7b1d97d96740fc0b28db8fc96317262b570fbf8ef0a49cc7e98b45be9aa0505391da73d3ef609659d351a9ef7575d13bab893051988accb4d7abee8d864576c576c3cc1b7397979464b07f6d18098cb22e387777c762521cb1d1328ab53810bc5998727ca9a65aa1aad7e94e5624b885032e6846d9786f4720ee5e1ab444d70c230f0091b55661b1d83224b2604acb712134e4222e599fe487381ed2041959c929b5a10d606b8339c3a145d2c336908eb16ac01c2cf5407ad0412e0c529d3e7ef3b40eaf031a4389fb33de4dfe0b75dc61bc3de89916c9b5bac6b3b2a14c4cabd243b428cfaecc2b389f9b210647e75ac84f86cd88c975a7b503cced62df6a0965f0334d6b0766f7d0996d46e1d6513783f33135d4ce423b7a59909bc3b885e0cc3ddde731418220856401ec7afadf2790295f97fd85a43be3861800397c5bab0876efd42eaffb30db404b90547b7a6376a1d095fa8f8a4be6aa960e22d1de7432634e4e897c9e0431981099df2cd24b427a590727e4169a0555a4b359edf549eb29550a4ec9c59476e5e5a065a5efdc8482aa5a9a0300255581ebcc48e01ef515675b8a7cedf446cb11cdb6eaf5d02e4d27df6c5be27c63a74d6069d7d9fee467f7571e62e93ccc6757d2c4c5f67716b309072868a7a458a323330ecf55d2802aed31b31ed47bb01b06738543d4ae927917747896f01f12a68de6ad72e2fb983a2651d433e8e62c93c8a88cda8ddc12f2f0934909a2656bde57e833a758a09eeb82454b2fb2b8008d3c79c06e63ba624ac4fab06e96ad46c27a61e84f92b5db0ae83ef0056b4a7b69e89a87f8e4b627e082a92aa7d471996b0385e2a54b0a9591ed4066b90459d0bd112baa07aca0f31ad0c4373f742a1c2dbe8053792a45b7a5a696631255efb25ce9c1e258d3c62af0a87d7167ab040294466618668236de1161f3c142799fcbde1f4c3908fecd21c3877d4741aea3dcaa83bf1cb1320935d48b427f932662219b6432d3992d903aba33241344cae7ccadf375e34ca9325e95f610ea17a4d29e46712dcd469f1f32cc4dd105652c0ecfc7b11177550a90c6c8518d486ec341b5e84936d24e0e4a7fb7e5b2e79dc91a4ec2d339393811647140c8e2e457461c298b99e170ad2a48e6a01a583c6ba80eb1d316a4037b026e598d5e50312d9432fd2e0389176ad399900009eb01246773a0049aa13d08e77f51263f313ef00b63079f4e998a8f31f66e4cc00aaca00934f15d578b72984254b707d48e5529671f6448cbd78f99a04edfef914ce0e3973907190a98d1d2cf74b6651f8a2142880e1ad13d0f7977fd465aeb26c8002756905a1fc9a52856a8d3e93c604f019f563c5d9ca1d8a7271a6e234783309cb2f5387a3e7a906b115f994b8a720a681a589354b969dcf5addec70e22740070a53c9844b64e31043babf7b58492744b37b7a084238e7819d081188aa4d95d52d4d04d47ed5fd81fef0502c0492d1514a2fe1e23a6557ca44e4a505cde5fe64b32d5072178fe8d8c36674becf90039215f958d1979477876322cae8281f777a9ce0c24494f0a9cd9210feed6bceb245688323fc43cb8e17084425ba470ed86d654d9beebc490ab5d59855dc1c7466175b27c1763bb49a0e9e3f8132c86f6ac58e214da2581185bcb0527e2ea592fecba56373023e585c33518e138d2e00e4bf2337562988e5a0d4c43187de7b06a0b55c1fc45c4f3828670317f8502070aec499a73e8b3569796470402c57e1eac8d5678b119c42aee3023add69a511d4f1c2296b44c70da027bfe1f11bf48efec3be3a9f454bde1aa9db52de4c608c2775b1f549f237a51aefaeeadff7cd6ac2e43360a7f500788310980bcf04974063a122cdb09d70fef772e57328b882cb71573a0223a6cd0d64e652d97465dbe1a185fbf42cc7d5a9391727c6a9f330e019d9563a83ded0f67b989e95b2d269734c8abebf383d7e4cee44a6c23bf234854afec3c6993595e0c7eda62eda537428426a165a3adeb378770d139c7abb50d8dcaecbb346f22e48113c35900d49396d0f467dda8beed6b1fc0b71a1442aab111e8b051e4dcc27d0340e0337636266da05ae6269dda97acad6162cce6b691b8c15f27657341c828a4df49aac0c5dbd170601b6987cb424496d329c64771938f309f2230291bb19f618684ba9d36e382f67458c7c1c9ad3542be1c0af5327688ebef0be37de469d454cb043c977e48be1ea78094e6f044de3aebbe21cae43e764c6daec42c3146adc8f0171e823c99366abe14b4ab1c543025856b483e43a230ba3e59ab7a74c9d651a8a916c72e6cc1d5289759bd618275acba0e815ff8ebe375a53abe69da34820de21270a2f5309f45e118c17abd33c33c49250834a1e432d62e54b4f8d366167ff8790eec86dfd25df9b8045c09ae429290cef010e31ecaa25f18a690328a7b0888a339d3e24086440e7595b93f11e8a2cc98cdf039c76c4dc9018e9199c87eccf94b5f60dda4fdd6136d8796adf8cff26e5300da93fb6ed3445165bd7c42a422718020a2fe782c08c496e86ade4596ed74e271ec946bea80a379f82de8b606a27722b7af3366ee5276d5336d2e6d8dcea639142547676cb4c74b1d41c96b6b7904fd876845203109c5899c26555a8909d29d7cebe32b0f49db60d98813803776e89944e5545fe6baf425544a66d50a24c8152c8e6641c5f04cc6313f75439e8faca1f321ebc8d1ba4992829a104e95327e47d168d501a21cdb7987862f49c84f4660b4d30de36cb75623b7a3b9fcbe61ca403ec1681f1fcb52ff527cd5c93fdc3ea328a35b170ff09690b9aeffe49a29c5ce11edbd287890f0c274dcf3efd4c38798ecb8efb1731a07a4e3224fdd597108eec59ecb2f71e6a97326ef52687d89f63f38a421db1dd0f4a3f8b52cf27759c78e56ffabb5343034e4d25617960bbaf93359f64d84947ff9511ee8e7fcc51b97d9a02289becf7c51adaf0ceade5ebfd211527da8f989ab683d24f4fe0850a0a653185260840a85e1f696ade945023657653524a196b0db8e696f77a2859ecc45d7aaf7bb41eca10ea61813da8b70447492c26f0456636572d96e9e5e993e3c87c1ddb92dc540695940800eec9ad87da402681b31fa99c5529f52268a48f7d6148d20f5316b7438bba6add866e05573c74eae24692d267a79b007f0e892352fca82b1dd214c4add11504686ba1da2b41f958a616ced43dcff65e8bcada86eb74fd3b06019f443c30ad27b67507fe059db8b9caae52840927580585d0292efd8d1b504ba7a41ed6dbb2dc2d2f818b519f6996657af0c73c2419fa3d84eded75cc918923405921dadcad4a6bccbc607cc73d2f899dadafc10a6e9266b911c093b9ae02aa0a7101283486990690c94e68f5e86d7bcca63fb4c2326fccb860bdc62c38ab561c0340d2b2ae1fc9d955cfb06086b0c7a3fc611635c8d4d8b17a1c4a105f2fd604ccd9558d7149eccea3387ce917c4cc2e3eea0653723e128875929b284975cfc78fff1936eb3009a570aeed03c337b36c6995abb7e5cbd5070f7cf065b2095afe4bbf0a8723d257b751c768add73826fab2c1ce559fc0138a4969db6cd6ffc26072094260cbdf5d00a65afda5fb4d8c664080bbfee92e5eb744ae802c8cfeaf9a839b20de91e13021b9e4f4d74e830d9c2be8c36968478356973930de5c10e7a662d1349d1b40b34812049d06ddb49ba1a685707f0221007a1a3b72d350ccebe1c9217e36bae0949042f9c77040a7efbb2592056aa9a0ef1ebd9dbbcb5bd69319f42be06956a18d430f945a0d3fc25aa06bfbf48843a6a19776f22fd7812843ab41b00f8410ffa50b35b97a4e16e60dfd813ad9557f0159fc945fc2c86466351486b7b77db724b29659232510b2c0bec0a9df7f9fd4d078e873c625885e8094658fdcd79c0a0e74b8c31ee3e1eef7319cb677552560b006578282eb583972484b0a7e39433c040e30c200c5d7c46106754a177348a5294ce28018f9831bad89e0e33cc6032a352f8004611343c6320cdf0595a220616a95611c4d015b76a6c59b9248726482c61a2082d5870822240e1f3537388e1a764bae249d7279c6c500413e88a45195c68197c690d2f5912e0a21f3e1e9682f0d0e4b0a8f7c564e4c0872132aa10c111193df8e1898ca5253e4b440822480f4d646001fa2943c9921f2382f80054860a8096caa08192203f04fd04d9a18c278408f253c69425417c6a5edc596dadb60b2daebcc400bff8bc2c31226f81821eea511194747d77e1010fb5a8b1c50c30d3164a84b1c5133c6c81c5cd220da0283a1d52604e47eba2b9872fa542505742a5f421fe2ad495aeca5bfa7fdc5797026f3a4a81513afe4c164cc1251cc310f934484a5ba9ccad3939c7dd1f0698469348772298904a5ca6232dca97394459cb43570ce5a73a6eabf146be2b95b7cc2186f287b46edeadbc3f227581270c80a611dd0ba4b8d44e7e7e67933e0e9b9f7d7c3e055b1ece1d9a29f096b4ee477599fdf9d267be05b5fc5af078c3d2f20750ade33e7c42d327347542d39796ded794e60968fa15d4d5daa7bf7913a4924a5a3f219bb6b45221c49c5a2df7af353a0f1dcd131c5f468f32bafb8cbe030e419a3669696dbc1ea760f9b1c79bb4bdb1e3e2666b541569e97181e3cbce658c51f544cba91af2503e5e3245f82dfc25dc00cfdf9e9e5207b3521a220531b702bc690af0befaad4c212328ecbaf8d55a6bad9e87b5ce3969cb43242708e1edc92b4f10c2f4e357df652710e1f7d0dfcb93c769fe6f7debf5d28f385e027b78b62edeba704a42c1d2142234e998547ba2f74daa31d19daa82467ec8b7cf43935c1bd391cefa43c6998d50bfb9bf26d7bdcd32caeda50a3d596f51e0ed69ccb106fedefbc76e37eae3b71d913a888d207a90a269166223881ea0d0f487c4aeceec89e98b446f6fc12d8dc00f242af2b20bf5a94fb2832413f13e9591c409f31ec9f7497674a605e71b8959b264c1c2f95ff9c120818c4cbdc5b4cd7b88155dcca1288ffb0b76e0e680ee62f776a8efc023545f0f39b027a637eae1143cbfebf29646fac68a7288fedeff6e076e0ba4391c7417d357fe95ff815d3632750d31cde53d44f7c4f446f5d685dbfb52e61ff0cc9205c875adbf5312f098402529c17db23da051747d100c0144266303d9754b9680eec9d2956a9c76f1b37feff6123ce2faeeefa9d194661ff0c6baeb6e8dea970cad22561226d8c9b3e20ee9fa9b6a09021f5d5f7a939e1a8945db0ee85b6b0fa76badd9c8a6b7d45baea146dbbc65d03d359a3e96351f714dfde3477f8b6a6d552277628d77a08b35744269617e73c7dd86973cb0f2c4a7e30cf57e89119424c61863e440cf6f5931621653b4738190578c9fb7d64a9a1838e0a8bcd1425f1e1061c9b4d6da786bc2a0c1a551b4640be80a1fe8961593262b50f4316b90cbeacbeee557266a95127d6c5f48fca489cf7ec2e45721e4d72862bd42cb8fb1f240cbd9519f5f02e88ee785942e2790771d77f87b640a0b30004d054b304e1d5a91d278922f6778c24eb8656cb1b40406238c8146136a383181113bd60b1d10218504155c27213a850eaad45a6bad37b5a12150984209255e40831e04618929042a6e70a7a022074aae9309ba32f48e48610ce95845cfd8b4a3d395296118419584a029091f5031440c72d8991f4bd2440e317842c887a53296eccc2f329ff494307cd09c8e53c2a07147f8e847972ad07c7fe40e2aa20f99ef0ff1664679d4ca914f2f9f681469c2e6cba7f9ffad16c6ad16c646e0ed4b1469ceeed723daa64af7e74380efdc9fd95ead53da5eadf36a73d6cdb781eadd29e95816f8fe48153c14c41df3e7df1fa20f2f8978337f7ab578038477e70dcd599c537edeb14987f395a28f31e2cc8bb025a9828c3bbc880e4694d197d1cfd0330b3d77bc46ef00714a7411b736b7d5daeab2036ed171ca107620021cac5c80b18e5386f0823586904437e7acbace5ce5ae10dddd1d67fad74c7dce293deac0dddddd6194d2c8e9e812f2691bb4e7db0285f449abdd2e87ea5a38aed2d9792e9b57acc840658557acd181638b1489373c70465ca545ecddb6b854a488fd547a6edfb2420aeb75ab60262b3a3e115b296e15a1da978ad425ba34976ef4e172a38fd41301b8b0380375d4227e90388bb4c8b55fc4e92c527f698916f92257b26e30e79c94524aeb56e7cc31aba3661ed94d474d4cc7ec612b0458fe7515e85f70631d5dcf9772477c39bb395b975a4a2d05adb594ceb89368ffac545ae9bdf7a2bc70c7c055ea5dcd790005c6489174af56abe36ca5df8652e3769cd35ba78be234e7c9ceb9ef3e4cadb5956ea8ce7ba9e171dc4bee7297bb4f1ed2fac34ffd4a6594de1030fde1e7ab62cab4c8188d21ea86a63786281492a757abc5cdd4b748a79a50470c27e79f665a5998a2bc581698be4b15e6c77fa56af7f1fbf834443a3d611b2eb044a19e6e4f4314e5b6a71c0a95e482f2895a6f87d3f73cefe3b1efa1cceea748fab72c9de4952727ea14bf3a5d52a39494560c330975f243f9b12faef8865084be62e5175ba8b0b1bef06182c18c8273bef4a4f3af0ff49cdf7ad2d38b223d997c8490839eef31ce70fda2255b588825df1182544804e165214a4121a24302d1b7640404aac95bda810838a00ecbcf92163ff466719ab0b8d405113fd6305aba52c48f233475e1c3103f8c00d4c5114224e1a88b27421475d144103fb038756105d08f24c436b7d5da5a8508a8545102a32a5a80c9a84206483460a2abf3e512354a21d10bcd7d273d6f21d4f8bf2e765e3f1ed577b12ca820c19511147d61c586465ca2efdf1df4ddd6d03c88a978458c2236d12242c45f4420c4cd0abfbf1db94c6a4b34a3882ac4bd31e9bb55a1a2efdda6d0f76344c0f7abd2bdb8a3d3a7dcec0e0cccc36424310ba11d9b5df363be164dd8fd243b1be8220283c4357ffb985c8990c81e7080050d235ad4b40385fb243bddf4bc4041183b4840c0f9b003a5e541308300c50e14aec7113bbba59f566f90d1924adfdf8a5ce631e1b2d4f6fb5b4f7b1bda9af47d19a0f9e32e153563e7c5b2c05fdca11fcf10d7eaed6fb9ee78c2a1be7f7f2e90be41fade9abebf35cd59ecfede9ef475d2f7b7a38d0671b6fd7def6feaef6f51a28f7a37257d59bf5f716b62cbdbd00b2b6f95aef58908bb7f2b92bebf3725f4bdab5a9bb0ab05be3514c459acd6a20fe99277cb0638062cd07722c58080e7cf84ddfb3224b833ef8b7dfb8de86380fd6d68c2ee4f24a48ef3008005af7ecfda6465979c5134df5816786f433074bfa9cba37e841ae5a02bf4f07ed441ba6c64ba5a80e5d35c0bef8d13f847ea661ad4e98e7a7f7fc3742cd2970511c67dd3efeb782c0878cb3623a131810f9078a2ed6cf94ed87df9f5084f35da4edcd16166d9f7874469f9cd6c37fad157c9c3fbad2b31371ae202da40ef7360c8bc7fbdd93267d461f53d257deb7d35bdad4c0ebbbf75d42a0ebb1fa30a558bb8e3fe20dedcdf386e9cc055ff8d1358ea6dc8c35b9526ec7eab85f13de2e1cd1ef0f03e073cec82f08e4efadedfdbd08f50e31c431d9bb36d28c2eefffd991ce0aae4b2386529077dbfbf5f99a40afeb756f1f0be0adc55a9854f7fb9c6c6c07b2229cd1b276a1bd2f727d29421e9fbcebdf4bf990618249a26e9d9a72fa5a455e6df387137d473206a1e406ad06af99c8ea3c0faf648b54e3a87e5d9648d271e9891055f31f47efc4408ab202eb43042524b08b0bde287d50fa200230946605d815d66e05285052d2740c14a072e481083205acee0a2872fb8a005a1ebb06ace9f33ce393f77f73a77477177d3f1c6dd5677a713c884c5b7f129a5b5d64a2975e9ee94babbbb7b4b4b2a0b41fac4b8ce1695c6b5c5a6e8ac484a76fbb6eb5daee350a84a27aae33aef7adfa68144a400f0f828936ac1b1032c3d3a6b4a5577f56dfdd613f875ddf28e4fb6b62c147e39d4cb54b7a926b5bfcd6ddbecf6133412e7a493c6a9e39108065978614807ce0062c0438f33a2b1d554fc19c543772b1e3a5df170f3593489d29844f3b274c4de4a13535caa1273556a62aa4ab54aae4a72ce7927510bb730ab65873cacdd5801deb6668528562cdaa9118d0e78ffd7b87f8eff0ab445279e7352774fa292a9114cb182f442d3d35b624a85df7e47f3baf1b4a2bea35dd7a1baa72890ab1cc7715f6374f73699e8c4d414638cf708a51733452b4a5568a55fb95a6b76252373dee8e97ad6976f79f97d75cb04a24118a686fefc9fef565c86baf1f3fdc965367ebe637119cdcf9f3497bd7e5eed2b6e536dad78b8e4e17c10ac55c0cae4e1fc076b93874e60b501d627b0be404b3b72f991e6321ad747c66bc5b07ed754250fe7a5d13c83629e404a6f27100bb4817e5569ebd0db9584f47c0cee542bd5a4e6e19cb3daff21298abaac5b02775df7f8035b4be06fc6da72ffd66a150fe78355e9fe5c2517e832f130c8c3c9ba3e3ae03d81805c56997893599bf78c5aebad55894241434257af2ae9495f6ec8d515d48b93b69dcb9922e0f89b4a461f37da7a4a55b6a58d292ef5c8c1f33c506a1a24b4aabc3069fb97e60c9cfae8a10958f0ae3f3b47db971b04fccda8f1d76b79fb174711f0be61eafbdddbaf45441f46367df3be7fc3ac40d1c7cdb4cb3622cd99ed5e490909e9e8c8c8a8a8a8cbf6898776d71f6defa7e47fd14315287f932e84eade997cc672293e63b992fcdb99781369455ed9124bfd613949f1d90b90ac22a95821a56cc9848c229fc8265b32914a5a7e472bf1ca8e58a490cf5e8826ccc7672f4833f86c0e1571d9f7b14aa412adc079b13c1a84b70b69c9229a33f77198fc4c041269fb2ca439934c0e93ff79df1b79631b48da0ad1d49f57fd71d59f99fc6385962f937725a2a2e5b7f2ae45317957a739933e3ee50bcb89c572d2f65f80e66c8617a2695779b7ae60f92d40da5e55de3845843467353e87e47b2f48dd0b927cd40b92b6bb85875e9c6c4817a5afa565e512638cf3e557be5a61bd7a59bd8346e2cbc78fe7e575dc63cdf7cec19e8fd1fb086e4ffb4fd0c8f4283d1e36c0f5a9fd6ae4b228e548be4ba9cd995fa14f2e63bd7c8a65f5d2e5e5cbcb96c7aad4cbbe2f921eb771f133c2d3fd0fa9e37c019dc99540a7027a15d097406f92afbaf2e4a16371987c2f3b928c10b04f34bc4af150522c95567d3cfc01ab1220216d03f51c29d3270f6596ffa581f422af181406974a94524a69a4946ac74344b7429548fa9cfe6a4d4929ad2c8cad92cb24923755ea56dcc97a5315d86b92a26da0da9bb010b8736f8a37ee4dde54e526821e18ad6ab845c7241f18ac9cc02a1d937c6a493e403133ac98463a9c40534ac7d022054068e11b6b4839e2c56a5d81e4b0a8e4c2154b363e9eba0617402e29462e4839a2c16b9461c48c144aaf1f2264a278721d11d4aac269660645c420c924e1a8069345532b094d2578d1c1514c0884602139c11001c42a8a171410e182458d154588961d00ada238024751e4926529081515472d53b294c12435c51106faac50e94084a703a7d4089674597c49d45055d43c289870540c75341f045d298250b4328cd88e8cb89f262eecc04908c8852940518470c18aa5288a70018ba72d9270e1282829873556e0c214b5a4102491840b3af831a18a0a001db338430b37c2d1733e1197d5a74fa34c6657907bb3cb7ed4db27917d3c947af7228141169e4aaf385bad1c32c09df476f880eb539eaff1704e1d9186391e4e3d3fa62755a393da484ed8d4b186ae7377f769757818abf1b04ef759dda5f4a2fb74f74d5503ff6dab74c64963a5b3d2b9d969b76d6edb0d8d38fcee4f279d17b5dd2df5f146be51758f8ac7b80455c26e72b32528a1448c314e3c77a0010f3cf0c0030f5262dcbcd6fc56ebcbf1d09fce1fff807897673ed27462d5f4440b16586a6005bbe838058bd0128c62411130c042829714e0948e53b0f8f0d4323a02171e12aa784868f251828434f49635a1212de5132d65142d6f94d568fb95f65801094e8e30064b4f7184202a03234d8d9890e1117eacbdd3b2304509b6583212c2c10f3bd60c6da1e0c10b6dadb5d6c264f19daaeb6f53584f4c949eac683ac50838d0f4edc743554ae0786adce089076dbfb6a6176664d1f9b0b6660068e040db3482d0360d2fb44583a6ad0dc7002245715c01820b9117f772dbb69d7106d0d3151d94105d09e28726239c145d6981104857961022c80a7eae5821441027419e0fad6d5a6bb3e8410f5cca11400e8b41418ea8bd8ca0074c2e69f4000cdcc22c988e592861c16f3473d2f7890d91cb681497d5affabe4dd19c591b237d3f35038cb2c921c2eedff83b7cd8e1c3109650de0e1ff0d4d3c68855c6ad09c345e055cd65332b1edebfbfa2cdd9ece8dea37cb4a2f120c27b6645dfdfb3231b9acbb6b4f171d97deee3f17e66c567477336bb17e32304c49d166e36d4fa6a70eaa5b9d990bef357204b73dc77e075013d87719c8d0fd186167de0e8fb363fd187ebfb364473f6f2f76d8ce68cf5d70605fada18a1efdb08a1af4d91be3647fa7eaa09ecf2ace76c68f1e67e2c0878957180825f320e2fcf7ab97a9727b2caaece25bb6626f2925d5d0e3dbcb0fbacec9a9901dbbbd8c761f5db33c0babcfdb8a3e3df1a78469bb3dabdf89bd17426ec864360f9de751f8f0b01dbbb3c91ed5d32f744ec0f69c947ace674fce23c61179a8e7a4b1a6dc2ee77333d72c02eb40eeff001db1d3ee0aa2313de49f4fc12e03b36347025f303bcfa02a3541ff7137471d9c6c627dedcaf19a3fbd4179a51034ba37e7e305019186954a6613ef7f1f37290d58f2b6a8922c27d1f0f978780324c30868bca33a0eecac7c3558f63a069be8b03473cbcdb0648dffb289487c4fd48451f41598d7a897a14ea6d7c6c7e3cbccf651b9a877745d3f7b70d4ddf5fd15ce67f7fe6c4b94b0ab018c5eb321a7a585a7eaee10a233d5f06467a661ae60eccc842efd84f24f0befef286f9918a6e61d3f363fc8c98a08fcc97fa1352f5fce8d3f3a4e8624de451c3fef3754881b9df576ad7d3a7f6ef3e07769a02f339aea545460604c330cba378e3e12b8376a28f1bb08e54a1bef7f30b107dd8772a9d9775b81c3d44fdec5eb9a16891f85469913993ad960c9ad346d3044df9436a8a4259a2cb835416785f3d93c54573e193a00e29f0d4b1f3cdb31fcff7d9edbd6e5c272d18258d33d582bfe7218682519bb4b5da3a346773a80ee1218b6df06cd57ce38da439a2886a382aa1a0fd65883ea4f62fe2b2d777067ed5b72f73548a4aa911e0fd9a798a724bed2432148941e9459b53b8d03447733ce43efe046313c63dd7e990026fbf43bb75d4f3a226220a9a7b6722fa58fab65fe13cd0dccf1c0157fbdce732efb977292eeb9ebbcfa190305724c7e0a173f4b9a8b9f95ce6a292e6b62e06048c031422f35d1e07fa2d8fc37c9797cf753e36b74184711f5f7a51bce13e16048c0394d4e3d0f2f471706979fa445a9e66d7f648a010593deb3311979fd9b56527feac8c6495f184713fa1105941f17e95a1703f3f21b28987dc7bbf8155cf4f880cfa0da4baa3de067e5aa808bca352c8b52ced1627cdb52c698efb8f20f5c06e7b14783ddc507fef67041c83cbea73a8f7746c01e38e8b174d18f72d46ded469f94ded457316c384717faf126eb5fec370c7f40f8a73230fb947652ff290e3b88f355ca6c185668675bf5c84d98f37fa4041dbf762575b6ba3edaba20401ab8a54441eaa88b4fdd6cc893e6238df7ce38da5d9756776ddecc47fb6b0e9fb564554377c61a05ad275b2d5fabf51a7cb3965b531462a91d23ba78c74733be7a4f5ce39e79c736e2be0c4429d3a29a5f3e59cf34529a59346d50ba8a44e38a448aadd620c299596da1ac3c999b158744a29fd226fa9ad2c0f5b0070c2941a7564dc7bf1666fa45c8d28d4c7433f4a524073e6a3832a4b962c597666b811e432541397792d460ff6f2625410ba4abef40549e1fd5d2e213838f9c8d4373eea1b7fe35d46db17a484eff1f6e3e991b7119c2f618946a3699cec6a025d4ba08b6907f82acdd98b16809c8fb7e5fb1eef8ff3b2df2fda87f37d3da80d74fc707a805f0fd0861e3d7a7c0a3d5240e175bc07e88ff37d287c0af986a951c84e6610a279667b1cdb9f90f79b90776893774c15845f35fdff9f23478e1c05e058e078b81f5c0b5c0e1700ee72ad56ab058220f85d4d4d4d0d0c0c0c4c4c4c4c0c013ef63b1e007ff33afe84bf1f00fa39f75bb8ffe33ecf7d16ee17e0be096ff3dfa7f0381f0df03dbe84bff128bcbfea530f7b1c3ff3f9773c55a19f127259ced0931491cb5478faa9282ef3f1f42d007e0e98f316f8ce7e3c167821890540499403de210f29ad25d6b2a305002d35971b171d2e27b8e4dc045c2297013786fb803bbbf7c357af56ac8b93c25542e1e5c64b092f2f2dedfae8aeab5daed7ebf57a1a84b7d77917adfb0ae47d2f05f2c6b848de2d174dfe04f24dde57025947de3802f9849a0c790681fc80bcb103720c79b71a901990f72f2013c93b54404e0002728ecb88e6876413f2be30e48d35cddb1c20ef96a6f94fe60d90f7d532df23effb42de58cb3c8e90bc5b5ae653b0f12ee47db58d2f21ef1b246fac6dfc0d2079b7b48d470187df4256e5ddfa915379639e0c7bb190774b8705c81beb3000795f1d827509e709f3f8a877be93de0e28f5f7d5ef60a89f733e32358fbac38347adb356cf75c9e6a9eae9094fff1e5d2b5c967afa17c9653d9efef5b93f2ec379fa570990cb6e3cfdcbc465293cfd1be4321c4fff367119ece9dfda1572998ea77f875c66c2d3bf4f5c8682fe485fcf2247bab4be0ae00ce075587e1f20f6307f97f24af8d4c7d3e3539f119ed4ff90ba84b751998002ece3c7037b1defe2c793028ebf616fdcb881e3c65bd048c4f1f1e3c1f13adee16cb5d66dc6ced499b7a091994596a987f927107d1da04bf1b0fe09a033795899d0f7271ed6c7017a9187f571401ff2b01279583f85fa30d09970230feb9b00fa9187f55100dd0a0febdb805ec5c3fa2ad0973cac5f02e84df57bd4ff3e9e99ec421e56ba5d83db91c21a1be08e5264c01d9968c02d7da4ce0537d5fbbaeecb65ddd5bdd7e5b6cc400280db856a769402823b326de9238572805b32f9744a29214de713ad3f4a5c969f724f7f329941b31b00b8a5c6f1b92a79c9364b5724bc42954af4cc072a585f676e604fd5db1bd9740d2c3d93778fee61e9155ec7ad00de3f1e01acf09df40600462d8015e693ddc3d22be449445fd0f7b98d93dab62d561a6ceec1d157e808cb3570d20e489aeeb8b4c38f137a572b0698ba6bb9f12d1f8f8d6ff98cf0d0fc0fa96f7c47a30feed322368939ffc4fbbe1fdeff77e0ff0c2875e7f29944e0f6379e6369fb736812e100cf279368db483d8ea4ed7b933be9c09bdc8a3b79485d2d574dd3d606ee1774cddba8e28aa2fda2e921976d7f3a7299ec845c56447f3241add0b44be2a189bea163d20e58747e0ec7db9f020616172f69072bdafe4d4a42c99eb4298acb62520a8434fdaecaeeac686ac5fef6d6cefcccdf7818b8e3131a86fa0c609fe65f42af9acba2cb665c4302e871631369b47d899fb64a55dbb74fa30f1c6ff376d53c740d1161fbdb557309b9864e00a387337be65d340fe90decb98fc7e620ae9fef94604f618fc247d8a7007b1cd8f780bd0bf631b07fbdf7fd30e161df75294f7e2d84cfbd05bb1970533df319dc52e71b703cc73d8723db23aef3ebcc0c46dd81fb866902b87be2133a3fec3beffb113e178246b86c64d3f6dd8a87ddc3aae8ee33d85d1fbfb14e05e0774ba73efaccb5efec376e69150abf7352f81dc3f91dbec62e1c835f5869c7ef56ec37d62d3c7e5f4dfbe8b357eddefcc600a84b2e0b9fb6f4ca84d5534bb3b0667df4aef6d15f8aba87799751f72ae47dbbee03e032d2f45d4bdde7e47dbb9fe58dbb97b996ba7f751fd3bdabfb1e797ff7387987dda790774c778f42de39fa45eb9e47de9789ee3e9637ee7ec78ba6e9bf6a733683c33a16ba0740dead2bbafb9bbc71f73af2bedd9f90f7edde84ee6179b74230d6e6ac6616754fbbc79177abfb99bc71f7b9ab69da759dabe612720d751b973f7e5c4e3529320348d45cee894742524d8a8aa0adcd3c8c70f7dee48af9785cf537d52b2d3f185c415cdb776db3eb47c665a6d33c9cc02c3d3f9e7804de2e9aa65fc2effbf45d3dec1055c8f1f45d3497cd3c7d974ff451c2d377fdb8ac84cf33709bb55dd4f1cce5fc16dc9eceb99bc901de70750933dfa93e9e99ef66408be3e367710089257cf8099125cc94d0e369eef3c70f866d86ccbdcb3ed5325b3c9940c778f6f680f7d5d4266f24ba86562c4fd769abb5d6ed6ef7627d37d048bc52b76babadb55efbdbdd365b374b8d669187f43f34f0be5452340f993ca4b529572a35bafbbd005d4344d77c2de0f84bc5433ab3715ed0f6bb984e89fb1d4bf0378323effcdb77ad1d4b304ae7cf600f4e0f8eb60f8247a6a6f9ef5e06dc52cbbc7f41eedbf8a86f483df3d1fcf4e40703cd5b9b8f4c6de3e9773597d13cfdefe3b961e3e3c76383e66f989a06064df3b3c8653439864bc565f4633e23525b8de30dfd967843dfe608dcbdcc77b4538a69d5a5392bb2b4346566f4dd2e25067ca94bdb0a6d2bc5326d3e7b93a2e9a7c0bd3169fadfaba6e97bf53bbf9dcae3c0a8955ce6aaf1afe24d4cd88aa6572a952d0af882090f59ba36d5ed6e979b3c28310248d437f7389290c98392206f9a33ba43a92fb96c63ea7c68ce3eda14a2377b583c74e2775d726d1fb91c7a48439c0d8cad60738acc59a74584d1ef4287a1e97745d094764af186b25097b62144e8d7a54ee985df9dd296817848e391109b5f4645fc0259d82b9337cc4b31d22e2eafe32e2e2f2e2e79d3df60c08f79ba7d4c0e7ef9eda36c75528529a51466f5aa17954ab57a1d5fbd8eb35230fff2147cf98e7afae5592c168bf530a09129bffa947a9ad2a7f629a55230a536407f7afa66def8ab51e6c753899ed4e8908756c8439b6bcde8c9c3bf4dafd737e91998bf4d6ecd65a9b77f85eed07d72eddf28452eb37f8d5cd67afb578acb3a9a26d7effa548b244c858179aa585c16937608d2f62dcd652edabe0db2b5a41d5aa0eddb21fbc4673ef74a0fdabe8de2331e40d6c76531e94748dbb73f9609cc0b959f01e4f4e457bf36cdd955b2d7ca0ab44a3cb4ff02a640156831687f3cb47682b605da261ed694ae92b62ff93add260f3f346ed4712c1edab761c296b66f93b67ee536b98c7b1e4618e65f6a2e7b79fb55c865306fbf0ecdd98db75f89e6ecdf862ed8e5a3cf9ef5f6c861f495345d5259a7771fd2db9fa81b398cfe0dd08b1c461f86b610f3929dc846df7ec503b715d2f6695ee0f621176803d52ee0ae45dafe0cb8eb935da5d0e0f27303efcb802c0fedc7d06ce3cbc76f7b91ef9b7ca7da05ac616afa14e5f23477543ae902ee9eee45bee4e861f73a4ebbee5db6eeb9dfbae77af0d1a97f79241128d127e888c51652bad845988769bd914dc37cccc7cf00ad5c6f981a264a86790a6e30f48321f52fdf6d9e7f2d709f8aa23e69d5db772c93367d56eff214dcb617704bfdf23030ef9f11a96f887917d0c8a65df60d53bbe42d754ca6af3361c0dde357f4cb7fab3722b36897a7affa17306a16b86f989abeea572ed9c8a65bfe86a95bbe3eb9ccfb161c752605efa7c016f670d2ed4fd908bcfdc9eefba4ed5b2c9c8ccf20a1be13d474827a1e4d60a9fdab06a24c07030588617d21ded42f800162589f0598be3f06eaad403586f9f56bfd5a2b06e24d05e236d0b53de8b22f5df6e30e2ad6bf1b788dea56ffbef4c1b08edd0446444d44434254bfbd7f2d7423c0db6bf265766def2f85a83ea949153ddf41dac4438a33654283785091d28904401e2e81fdbbf77c83d4dbfef49464fdeab713d64abecb5c433741c9013ddd3b1afc08d5f3e74f8984124d754dd288686d4a97c52413306169aa895ce6123293c4b07a56468203116856ecb090ac9884c082c90e9296fcf831e0e56364124d64f5503c6fe90f78f91821defb7befd23e0c08c55f0506a13f1ca4c1be7c18bcf79f5f10fbf283c17e313060f530445e1eb002810f9125ca0eccdf20f5263d8fd5ca301f034249c16428deafc078278c6a4f0c4c3f050e017528106542833cbcba9bf6270d720229908774cbb2099401de5e7322fa74c7265de432fff94d13ac0979489fc8557621cf34365b7d5c812ee4301a242c89a6dbc002d7e40c4d15d885bce621dd01d6e11e40e2b29d030303bcbd76a4d2a1195c0664888714889e3151d0e964f513ecc991c181bc3024c77334ebe9a424883745c41df42f4899c41b4a832605aa0e4ae9ee534e8f3f4123128c33d6c7b0b2d722acf35aadb94c88be0f39124e12124c103161a429752a9a3a91a65fed247219bd46d0b269ce281387d1a74039b2e0491fdc024bfaa00fb0ff964d15290710467afb2e7a1cc7d1ee5bdfe9ce2913ee7950c15d0462326bb5408fe2b0ed25e85d91342d7158bfa2abd56af5d45b21faa8addb42a124b554825b87f6bca72b441ffead998f1f6a1cb439c3912913876dbf01fd50200f73c0db7b149f4d276f0798b7f13bc83c4dccffef30d37af07770fd4b47531074d5073fff0dd075ff1fc713f9c7f1f7716424506e2672e36b32121c7fe391e4ff4fb2f3025d08003f839f5d445e8fe35f8fe3694057b501da789a2762e369be3e0dcdd76424aefa4976681e89ab661b88d4bccd2720eef8e009db3ebf4d4682c4e66d724d4eb263e393ec7cdcd970e4b893330826d979b156df490f0493ec3c1877e6648147ac9e7902800a6cf3f6deb6d160816b4c6890f72ddf5d4f829d6e8150e8779cd742e9ef07ebef5370f512ccfaaec02355f7642ddfe5250caa9741542f3fea1c5793c9fdd5b3587f3f1e56fd249a82fb8b6829f57d59fd7dc928c81daba7e0c6d1abd7992de0d6a15d5c72cbebcc6e7a4e35d65a6b964e404c521f8f534f8ea61f432969dcb6ff74fc15a28fda02728f41e99402bdf6f17cdb7be0eed131699320ca84021d81b75a63a2c0f4b7dfa1136d3a93eebbc9270f85bcf34d81e60cdf16fe9675a2707b0a3467f762dc6afd53201de97c0aaa340df2507737c03ba6b7ed3e6dce3cfae00c2803b6c01830468d54fe51ba2845434f9a44263e1e460f60e9712ac244440948fc18c389384e2174b1a6db3746a37ce9b663dbce966313f6a2632c9c301c5076a8bd490cb4ff6b8657b47fcc65333a0594540226ed4b4078d2114ce81dab54992bd2574e389d72a0958252cf120869fad2c97d4a7376d110e028ed6694840efc2de724ff9763099682ca01f4ca1b88069252c1f40465b8598d0f97cfd13d389a86faa99fbf71f44cfd487dfdd4d7d7e9024b9d62216ffadb7cfab564666e747c97ea2718a9c3a24bf6e24da4f916b81d54d2fe2e70fbd27630b43fcdd178083804d0f531b80cc3d8e84883e3bf3d1df3eec9d1f187443006257024e2a1a7dc68de60bdfccba773ef5502263a0641c7242384b4fc2e15583e114d43fcd76f1d3af5a9c7a9a76091974e3d98772bf59f37be915f3b95777cee57af33c1a85f608f0e8d5afdb4016e3fe6bfde0648f311747d17bf20f35f397aa84a8136c0ed552a30be0cf87a1a50e6fdfbe1f2f15ba0d433a98fff02772ab7e0f2f165523f03c609b63e82331fe3ca5b878e9fa23f2a8df9969fe04ea263bc9f01edcbe898ed5cec96de6e6494df7b1770f7e0b40c8901ab5235aa4fa5521f53aad44f559ec161fef7e349e5225bb0bec701e278aae367303fc67a061003608f870e468ecb455ed749cd6f35cf82ebd48033e800292273034ed7cce0b019ee7006b0c8cc65482bdafd67ce66d37c252524a3b86414978ce292119347a7159672726aef30424b394880637386739081770c6b8fd86533bf447a8a9f834a8cdf05c92831c618638c31c618639431c628a32f75f3bb09c220df2524fac7dceae40783ccd83df9059199e5617c1d49a4f480114a74fc17930e808e4941444eaecbb6fbd4f34bc70f86f9546fd3695e8142d26d3b7a9790aa3f1ecf528aacb1641115078a3950cc81620e1473a09803c51c28168b12c3ee40d40039900e070ab71d74a476fcb1072cdfbb7109a9498e25610e2de27fdda33ef5405c76b78de36aadb5561b36ed5ffdeb7d47dddd7dcff6a9afdf970ab271ef8146b6efc09efbdcd76ffbbaa968e07e7e37f30a5d4755debb4f9f3e7dfaf4e9f3f7abfbd9bd2a4533108ac1aff324f53b3d8a2bcbb66cd8a6ce5455b0e77eeab78f479532e2017d3b2051a3f21117728100a13b79f778e10d1a60ff5a6081a3a1c7dabfa09dd11debe52002ef88447756c84104bc57d02e21f5e9cf9ff8143cd2f1adb4bcb764161c266796bc4f2f04b8d3e12e9359c743b95483f4da09bb8b922ed66c4a73929a03f7adfc1ae8d360dfbf6e94dedfe8b66ddbd39b74022a5a46c7a42027ed1f9d37e7c9e4b0e9a24f35cd73690b4ae3b4d981680f52e614f270cee9445b94668d40b76ddb362ae596f7945b8b556bc68f6328167eec5deae3a46b5a42b1340a45350a0586152f84aaf7d489f71da5d1317ae8a8bc7b74783250fc8e46dc8ffba8bce76f514f1d34cc9796becf8e46da3f9efab3cb350ef3f7f0298bded128d6b2484c6972026514da14a27dadb416ed34c7418e32f0964ef1a594f82f928e3892b89c3bd0374a0a6a12694e6282249f357ef0850e5828dd27359ae0501f4fcdd2d25b471146c819410c48e013a483a6cfb5eac75a2b11b050aa447b40c320c204449891c4848a228688a2e96f313180c0b2375c004dd15488214d85e081a65f5b39b1488589213879328430c228821a6714d1423f9e8a02928c0e1c4d5758020446d21235806041121046086288a420928072d0f4670844a378898048d3a7224843d324254b68fafe35acb5b29a88f2040c4ad08225493bf5a6a16b55e95a6bad49482d6d1d3f4f7ad0f4b110344df2c129e907a4181ea29394527ab1175991fa9c53ceec60f09082abbb688f3e8b4a4b3189872b4556b4fbc73d7db457badd0a378a4be8ed357fffb1699b2618c743f9b14573998f3c9ab3f9e432fa4a4a48484747464645458ef4f30a7d979dfe6ad935ae2b21399150ae551ce6cfa4dd8af62b61c512d36e7d72642e54ddc9d375d26a37299549939a37f9927b9746650a194393e64f579c3c97a1ea85213a2cfce0fd3a2cbc30e4a2a1e5cbe050f830ddebd9ade8c0c2524a1672c0fb75d75344862adddddda3c7211e4a201e4a777777f7173c942cd0e480a50e79456a3967f5e96e4347287628434fd99aa913743403465ec68fc752b0e60c4c97f186fece408c7adbe466b7ed7e17c10c1871c2e53b612e3360c48b37d7437c5be46f4fffda6da3d606f947a6e691d9c8a669903f330df4af9e92be05f396b38baef3284c448fe2611c7299fc1865f6210fe3cb9f9791a63828d1f64addde82d56bade9fa75bbdb864423ede9eed7a11e569dd49ba6003e7efcf8548987d482483ca06b8fae18d0d5cea06bb6bfbfe7e366e9fa5b0d2c4d43cd13689e70058d3eb6576d89c75fa5ed327afdb86cbb969a5e3e5789d015fdd23149484927b1a4635212483a051d939268a26f159ff940b9a1e457e956b94b9587272d4c1fd9c71950f81f52cb5ec72fd2b5e21e85f41a35c154ba9a053ece07c4f07132e0e3249200261ed20752e221ada67e3ca4291f0f698af6aa39017efdbc6869bc682edb79fa291f971d99a91f97f178faa9a73da804cdb5a5494b95a21900000000a314002030140c07c422716030a087d2247b14000d83964e72521bccc32c49610c1963080104000000010100191a9a46010751aa955ee08f71cf558d18a41b68d123bdc808553a6fc108e42d4cb2b90c891b926198631668706e09ee5c5863284f281c125c424b93f0d34371a06652a2c485e8965b3cb9190656972485a2cc75fa4b560358a533eaa22307b33f8e3b9ef301668acb60516c68765c3ef9657d4f12071e556a48136ea20299b5feaf9cfe6ebf4442ba0a19d4589dab8462a5f6e68c2e7312e638d756e13a0b0cc789a93b53b85a80f1293bc67ee9046e66b11dd1e4cd695518fe4334250998f0b7be080a8f0cc8e894b0e88db2eb2bd660ddc1b6a05ae393a513e708f60edd58c10d68da577ace99950c36c015f83da600a92b505361930d4107197f84bb6674acde6b720ec51dd79b792cd73922364016acb793efa4e8510f1fe90a4a4f54c248e3ccccadf82fdf0b8bdcde5cd05e7a06b01bcfe51ee4711d588d6517e52ed1844a98dd377a27a05a9500439b3000dbdee3fccff79fae360f5e7921b0c2adcea17cfe34d00827e77ec938cc446f2c3a9cec29a72588e8f0e12bf84323f03bd65584fbf70f10f74543653e39b41c9338a816151c54e44b64f95e54aab92d24ac57842a3654f65e29386b5f26a9a046f7f5d6efb254e18438da757d96a05e6b52b7dd2e438bf6768f3700bdad054f3920a82d111a20cd7f4344e33ad0720d85b707a14d4c351e942e4a6fd4c64a062f8a847370332eed8e7d2d6fa6216b28e62eafb6001c1279f02f23a1558c232711b98e2ee2a509c3612a4297f8f4f0bcb644b99e5f82ddd7d23577cfd6e86254b13b7ec04ef95304adbb86022114e22767bab5aa548366fc384c5c5f51bce7931a81967eea48f35c9d89e8342f07a02971b68376d7db110151d27a5f6e9747973527952bd37e1517d73778f22e23d0ca5194c0ca51eb2d5db22fdbb81cb18639a09fd9426b9aa3a21bc2e0a8bd985dbb26e7a24c1e0954371f1801359b375f879d730d85fae09870a6420fc8876162b4052515f57367caf0c562c7c5c6f5608cb43fe7cbd02d5e9ccaf3a55da9cb973932e024661eef8fb1c66b623412a06ed753a4d33ab194e7312064035199f5e49f7a18f71291fb006013f3a78c3615a6952c391bfeb23308b4fdd9d74c23ba26b2a9d8c7c4734e4e895eed6809d15164954ed6b7535d3f3b2c8d81032bfd2edfe05345b6691b2a8a2dd7e05d274bf1b3b01d8a2c1893f2dcf5f2608847e6e73a0390da3a29061c929e65701b9bcb95fdc00baa78c28d9c67b489593024924adcd2c9a0870cc046811ec8de7419d5f6bff35712e244c881d925c01860ea731d4ca09ddc1cf4007f689f690c4dfe5dc416bf397ea5580f361845ff2eb52e4558582eab255a980cd9b3bc75308f13b8fa08a99ae975b6988ee6a1ebb7a988c7c6cf248b39a8ac6bab54bd779546141d4c4786d0091f8b81d86a7b2aaaf5413d0c1ea6859e3bf177c9182858e012c504e8c0dc48d6abf6b5de15c7a3ee04e2788542a754235fefa3cef293e21c8995cd6e82b436daf7f9ecaee43a54f403f49b548566d34c5044c711183fad28f549692e5e1a8dabdfc8adccba7b88eaf1b0dedc084ac620eddbbb3e152a61b68573874f8ca9413d9ad3743720c94634d8ae57ab3438a4e87198542d723a09d71f5452370527ab76c9d4fcb23c5fe4ef87002ae00b6eb814dcdaa4dfccd83772238a9669a8f4b83882a7fc8dc6b702dab0bebc03fc388bea7842cf5c28c1cfc9dbff7e19e89a3fb33afdee323815535224e46feada84fc2db8cfffd4a70a7de0132bd45825a08e6c3fd2ab4603b263b098d854283f8916e62b95c7d50653fc36cef17091665695b662a1719e0765583b82ee2b51c5ecf1ea1cbd6896c75e32c0d04d72da33afbfeca8cb8190e6647f2a470661c210e6f2b90cc64c46aad6cad4d21638312554bb3595c44397d849b8fae80710a06e675972d82b5647f061c7256a9c20ab8e4251908718a0f26bfaa6a588084b83ca63ed5ef7d7792e502df66283f426588984fb0a5b1e750facf4b79852d5dcc58c5d159971fe15d9af76fef1df51303006c2f78e1a4d8a1b9b6d186e2b2ad070ba06a66b21e792221dc8a147ba0c05c0860570a5290ce05d51bfaf55814185999e92e3a5abf2d236dda4c66ee41c88b35083880cd6f151284371fa306db4e43cca99d286c7d05ea55a49e156b09140ca37b597758f5db9a6f953a5230ce1d776fde04968711bea54018fd564ca7e64b99761ccc7b7a9d447c5d01d965b38ac76b1e9c8e936e3b6e363d0b25e29562a15ad6a535ebdda34113afcf4d71604949a25048c15f5011e7a86c3c968a3663d1d9a882134c721c310dc764cc155cde785712ca09c3c87b7b92737a59e0d9efbe25f06c2b822968430f88b5a91cf2cc3c7beea3a828760b1439e877c6793cf46116f105677bdd22572fc17204a2ad0bfa5c5f6848541698d5870d4cc164ebfc0826f5e585e55c93d2ff14d87edc0d8c2f524714c9a3161bdec850325c9195896b33f7a961da65c005e1a711d42d75f0b8c442cdbbbee41b849dc12e4cb13479b59518c41fb899ecdf76262670fadb2daba0eeaa6987b92d2c53a548e4042a58ae70b963798ce0f056f66f194997379e1eab7622b6319069ee6aa01846a95579e71d56752feaccee50f18d9c4c7149898a768b9ee755ba86c439a336a176599b501bc0488731f12457fd7a93168854544f4936ecc37c4bb24434f30467f7a2024c97634920dff8d1a76a445440a199778ec71f7e7cc34ca9479641de293df917b3245b5b85d88cc3176ebce4ac1c75d67527c482ca5e9b5cee6f4cf5b7dc6235e497c7c34f4df4c2fb8bd4747ec7b2502e9176316ff2c3ed324d1dc7872d719947ce8ce9fcafa7381a569cfac98174b17e2d25dd5c15e98c13f998f4ec3e4aa5bb3606a6e722c9ccc0288b8833e3a5152176519253c5a27acf670ab160fc0e117b0df0ddf81e02ec1a6c8d529fcf65f0d8106d0a497dcaa729a74681b130a35dc73379f6d2e8a73d415462ce400f130d5a1c07212156d942bfd1fa1050b0086bbe994f4b85b0c01796aa11d1453d64c312fcdde9ebfbdca615eabb3ba2aad3bad9b3a864243550771d66d879c5ba359eb72a123e7c50f97e1d0d0f9fd52e6a84d525b2f92f311b213e75bba699a9b6afc985cf867c30b6a77b40b16fab0e09f6e68ef098cd1f2215e839454d80be7b1a11140fdc7d27ac475981da850bcc3f435781ac321fe2dab2d729fe53c740e6c765c9acccb81e3c0288aebe197b7967f9117478285e2b387c3daae361d1692b92be3ea42dd3ad235c485fe307f5191f437eca6e82d83d9160f7cd63707c3778f7c6ae581d6b04044e3310a1fb923ad2bc24e6374867ab2ba73e1a0e31ae45e2df8b162a69840cbaeaecea4bd4ca6b8e5b86312e1095c64bac9d01842691e51b085b218d09a996c170f40b35c70c87c3aea29e731b6a97c719175e4b7d6078a935478cb170dd7bd007db40cde7a429d58419c65056b22bedfeeb058e7aebf179908ad5fdb94855c61cca4ea2416f5db87609c022378296fcc335a1b10fca502c1f1b3fb3385ea8f99a6af73277cbd1250fb1d1b3e529e02a4c504717ddc0aafd6ca0a51b9cd1feb9cc9660bf5da407a5b4b8bbbe4952f840f610aed0a3f4ceb024b32f4d39bc7145d12687d3f54d9fd35987ce6695361b229f77540453d42d1210998740be6d62c5f2c9ee15ddbe261ba377ce807eeccec33d11c1e9cb3f799185f872569eb7532d3e366d551a7c6b51c324010d367cc8879d0e8356239be8540bbe8c2d27b6d3939088cbc502d15f5097bf8fa91ee091b6e44728ffc6a9797f2f1b58daa01cfbaddbc1c7286831654bb5e760c2eae4b537c32f8979f7c59ad5470187184fed792a0484ff5828e0288f9965ad1047025257731c190035f89ac0e8dbeb9372d4bf89a11cd8f47634e41b2795ae2fabb4a1e129921852353e39a4e3a58e5d63bf2b6d6ce222c692e1e1c9dc413d3af7cf44806baee5e6ebed812504e0091cc550ee8289046240f613fccf94ebccf38b074d8cc6b9b75ecac2b201fc404d64501f4ed61d76e49fe543b8c7e82d9bd6a32d850f8981fed6e271453367624ea51a52b0f6731ca8a4a3996f17022bb16afd0b0b81da412a6709f0d1031cc4e200eb403e77647b6fead27aab84206a0f6730140468efcff2ca4ff39627e58d102ef1f7749ed41f857fbb712e3b57fe623dd10d7945aa36d98039cfefb3a60325cf4d1256953a88b0eee05843e10d69acbe43ca0bd066b6fb524a2b9c6346b164a9472fb6dc27e490e68c1d70b50d31321811b1217e175e5519c8dd852f2a265eebcfcd59f939241851ad7cf162e4841012f6d43f11531b571650e4283bf9e5add7b25f7dea27d41dfec74261b4ad961e8aec822a5411f16eee561fd9e3ad10023e7eb8949cc1686ac6b36cbbbe6998b06d91d0480f00e533b0e84c4b85e98c0982cfb18ca77e511aeff3d8d52ba1b192e841c57d0c7826d9e5636dc83b36d734d28a6b595c8aa4161ccb21571eb9d31cda2e83fc29fff6e1b768787c62c717b2b9453cc9c5d64b5106e72219f30df4e3d58704b5cbde1a9143535a6e2670506c732f302f34c7b811f60af3bfcdc413d190edb1af73b23671ef082d7474f2d5aa567915566a723c769b080042b0b8972a9a3291a744dc959a32ca7f61e325dfb581fb9189acb3ae981619b5d8b9e55b3c9522bf6e27c28978948814096f117a9015a990442903fbf0d3adb9a5eae01d598a58da0d4dc2ebf8894933d870edccea7e63f3f6d16bae229f592fc78741c6b57372652f35b5ce20041d16c077788edad2978f99bbaec1a3ec73672b5cc0e8bc1c1c06b819c834183cea2c7b13b9ad7d5109fda1d42e2baf397dcc427b711fba1ac6fda7043f69e6bd55d925303657bcf249674f636ea554a0a49463e7fbc5925004f7db0763e79a6483269193969795e66591ac1f2fe9a655423cb10d0fc6f52febda579fbd8972f624b374ef284392620f011adc19b78e21a4e1b6952d38b58c6cea6b5bf942b6086171bb889cda4ac78de03e09b2ad58e987c768ec71526eeea3b9077001fbd29e6da913025d84dffdc2b5a355bccce52c11ffa5e83d67d0a8100a703a3878c497b8b83a05ca17f73ca68d878694bd0f05434684d5044b64bda90d84b47ea37539b8cf20f12e90c031fcb2a3095abda3152abf69a594207d8e4a5c7397822154a3fb5cb22a70f32dff97c3e56e9aac233354103759ae0b3a9ae6d3d16a96343defff56e2100eb15b01422aaebad77d96aab923d1217c6d61dcfce8f6ed1ac8f7d62d91746f6f444fbb68f234ce27969d27dfb8991f6228d467c1f1b2b25836b26b7262d265114cdb803e243a078420ba798c511da6745dfbdb87299503bf6e0752cc1eab6322685e9b82a13b43e700c66d3c97bcbffda844e69fc2f9c4750b95259fd33420bc32e4c1b8573c75997ddd03f502dcc741d2ad22e511d89daa59cfa14b2eb2837d4aae537016081dd076193a8874f8951c764961e6e6437b24bf60581d12e6c8dc71564bf67566904d291c10576d24e9e5baf84e7e597003a18171463320c10f9909705ca5da6a6ddd5bbe5e1c0293d9430434258f3a6be563c7735b7aeb0568a80618ce35b43a8f8be104375b1c3b4c3cebcd06c72601032bc3f4710bd523cc6de830b475da15fbd5203f4b3c4787d5d14d91745e40c9c1e55e217cf5058ee037ebadd1dc84c3014f1b962bd3f3843d0b7a45cee88e667415139503652b0d7dc540236a598d667e360b659dcbbc6f8721a7bc711a5dbab08a8b2647a19df039ea8761ee3465483feb39228ad113ac5dbba415803fdc1440d13511fcceb12efcec516f4fef6974062c9f067835a1718c1ff19996c74126dce9d630a821e9ac3d51ed7759f2da643197e8ef1bb197265409be51b931857de13c7af1b4e82237061fe0685239ed9a0fe3d9d1fd5207bf9e336d56fde9890913fe9666d148cfc1c6c63459a30f111ad4cb9529555b2255eb84f7dbad42b0cd242e7377e5a0bee37a20053568d361cca640af5d038e4e931bbb3ad1acd0cbb4368e621fa4217cd0dd179a9e8e781d836fbb91d489266654b9b7cf236d9c85920f05733443e27b4e1b0710b534f05a3abab89458633148388b3cb1586a483999078f60d69b0dbe23e1e9b961a9e54752a9db5f9716d5cda804361467ca9b50d1b989f6ad110254c318a2d36fbbdba287078edce923f4b866692ffb53bc12bd7a0c3b1ac54f00b50d5b28d5475f4e9357a48f165900b302a0de88afecd1b6e203c69c660b9ea40d5458102a0ca2019fbf5516f1d266ef4b9445934c131e336a4108f8f4b16307594077848e678dfaa95617ac4ca0a56c169620e1e321fea9888b3cc555623a785c277346d1eb51d37ad247c97f59bf20d4aaa8a564cf4842904337871598eebf55a3415c6dc683e805e66c0badeb3b85bc9b0a26fd65ebd6aaa3e7df7ba7e72eb8e87d8785dc83370d5b9caddfcd47f53b801f70c7c90302ad984fabce8daf3ac8da70d7bebc61e32a0756319809445bfd477cbaaadb23790f3cae31bf0b695657531f2b1042b771b8fa079ef625c7b0c114a5c02a4f0785f55593ea2579b0dd25b78985290aab18386dfe0190972aebd8f9ecee957079bf1b92b2341073e1179e3224ca5a5369aff130755314c66722ad068b0410f3bc977941f9f33acbb5decfd2a6ce9d3777cb68a5864bb8d61765057ad2be2eda03dd2532c58b946eaed3d5a325efcd83bda36f895121c68048d4ce91c8dc161ad83d638d41ee02737003a05a01bcbc037003c21e774b80604776f2864c57b8b5b60aa39eb10c760648937ee508d34956bd82f6c9d49764a93b240633a5a3a6251038035e23ad29d33ffc2675fc08dca6ccc96db5794ef8eccc68ac5d882c94334d8703a2c77c32f8cc7191c4880e0a5edad5885dc88450ae84cfbfe04353282ba13b1187a6aa12997fdd1b96f3c1cab2939723e751e6e2b8a28f118b372251649daddd2f6a38cff038845d26277e7dddb3a1c46e4d22069983fce80df93eb0f3a05fed1d6a3ea37a113596c4af33a1f93ffce0cbc41aea8ca15c76ea62790fdc9e3711da43325a69c83df2df4d783ed22152c404eb5afcf51e5d96a9eb5e9527de995b03ada53720f1f5e5cd4a8189d2bfaf088d1389c9540141356a9e2afd552b7297f5c4ec67df2a69fb44001488745532cc7d35816dd6d36e0e81de8e16dd25deb72e0f688b651a2963d6e90b0be88192fb8e4adf5ab2a6e87a6264fd2d6b9a365731a86b29223f77a7bb98cbd2222d34c4538b5f5da7c9a40b68a50701f02b4cd36938737f45a3b936f7c88568570fb1aeaeba538b4fed368871c586b436cd782a82f2578efd1e0aabfe1ba50702cbec0cdeb671d18dd12ff24675acff41d4b757af1264febced42e877d2cc80ef3011bd7c3f5af7b3ae7e4fa310a41cd9af55cff97a36460fa7e1cd6a954a614e71ae603e5ea0a5103c3d5c663ea2d39f7c9b416789e141d8520b4cfd0a749d4185221ca5b8c020ff8948d87c1dbf08c25cc81d3cb7bb625f9f01944917905ac147a479141e02319a893c46d7082bd6bfd7d6ed6984520817e5df7216b4d4cee42d6948607cf62b6ce0ee430e06db679c1081a1922e807b7c03c05355a16ad59196570b0dce89d5374055954240127ca94685da9a4af6ba17237326282c1c0b1652dfa1122c101a5dc1bc5f495f8585b2d15e81b96db144310f45098cbeab25d046550873455852a219375b31466c95320c64b3253bbc87e540aec5b03c64bd224cc3cd50f37bde14b506dfffd4d707c93256c563a08c224093a3c8477d2119f78ce82ec466805bb3e601162a392776ba544ce3c004b34d10ae4a899220fdd8a797c5c55b6773a788d22f61914a9a30ebf28759cb565beebf995f16becca94d408d3730608526a5700a188231db51d30fbbfa35bcf521801c3b067e3b13e9694d68baf75315f2a37afe951b838cd34e96392df041e5173bb2c489bed745a576597e00edd72899f67d34138cbc28a2f87d480fabf017890ad9cde9521f4bb971954e1fe684b1361706a1a3482230c2a3e9b67e05464911534c915a2145393e98a7886e4c02e3e4914ac92c05bc5934af383543e4dfc15530a7b95a7e303bdf17917a1d198dc1553f6ec29a01fd1acfd06d8a4ceb650927ffb335ab5c64cb0423a7dddf6d26aff4b18e59637a2f5fd1c57ddce1f641aa0af290c190fd4a7d94873be9926b6de8675c0e26204a17400a3c88caab926d1d754db0eab1f6d6728d36ee36f5ff34fe9ccceb45a1c331d98f0dd95bf98a40e185818392b3f832b7aab63b624d106a122ae6982339d56544601faf0c593ebd28f1d002197c613f9e5850372a454bad8dd6bfac4ba3cb378852451775e384e372920278f004de0639e4b4c49380c8a6d73088ffaee25dab598e59d0a9fc953bde2b08d30852c834d5f6211b9ce11a8cfe2c91fa48d22376d8fac9b3f40328cc6b09364e8f0be24f3d83ac37e51456bec8d8c9aee06f1afd0b14eafe457b9512c8317524028cef918086be53a641b227acde538952727bad3f456ee16798d867e9618be36fa095381d390989621e871acbb7c3fc9f69ae6b4fdd462b1ca658187afa2e312a9611819bbd74f51eab2c50d50791c140a4e4a4a7cc148c44fb359693b244aa1ad442a7b2d2a72553a8b76a5f3b46e851af20dbdfd29dcfc91a5a14bd912f5091f06331f767844bb740d32281732540c4b6d033767c5d25b5597e2b2c6bc08dc8f464e4affbeac3d5bf7146d92b99bbda21ae8b72799e0ab2be635b8d9c998ddb95d69cc4d10b9b68c39a3a809d8f6e89a126a687b73d9a6b89645d23fc230485055ed3dd794e855c508d2c7d468ec2cf81dd87f1bcf49b26ec64cb05609d7db4bf4d1316468f069cedf74a68a28b40c1c44cfdd0daf3ac2873e707267a958fcd27bff5a19d07bd65293e43f31005d920fb4612a1fac6e6c9011d390f049ebdeebc79f1c4015650974e7954da66fdd03df0777b2c6f8189a8622be6f2051220c745d27abc32da5b12aeb619fb7279d54f04ca7f86216b582511afcb10c15a808b55f031cb20c130ac208321a6b9b24de2aa79b7becec07378ca44a68fbeba8596111d53452116847a7ec434b12441dd90bf89cc710c2067451dfee042afb8f3684241f8d31c380004d04fa40d988681f817906c0f0af28b0c35dc145e6f22b10dc2d1806b93fe991e87190b0269ee6d45432848729e37b2d19d7bf62285e3b2701e8f5d4d5a37f9b4aed4325590b3832a590cf1a62004e380da0c8aea95c7c98983371bb7e03c20dd89252cc216edf43aa8dfe46baad3ca6825885d189b3ac502c6e9cec45dd5c40cb48c1fdd14da411f26ed9735646e6b241dbd50c1250dc1f934019029e8c65aeafbcdbd6f819996ddfedcd5a2ad70d5d8ac614539bcb724944c497a35d96b6f088794fd308d4695d2ad0ec50cea8e639fd8cbd8fd98246e1fa2ba8ffa83615d6dfb77939044d6cbd8bf7bc36516936dc8e1dfc40385ac8e40e597849e4e88a8cbf8c5c6f8e0c0eee40bc8186281dcd26cee3a1793f00671a04df50442e2a84469ab1491ba7d2d62d8df14fcbb8690075c9b5fa70fef4ce5fede7c578c83f3390d58bc87a1fd95a4b56dbc9a64aca9ec81c4b8aaf7949807084db3d44934de8afc48730949435f47114d4512cd89c9a2b2d7c756acb1e40d9d0f26fed3d4f6249f3f3d869bcee987d8ddf636df16660ed293fdb6df287b5472bafc9a1bfcd235e9f548d54d2971715f5f3a832a99dbb748886104193cf8505a631482fd74144c87c1719fedb02aac8bbd93020cfe8d0ee9c7bbb970014d78d972e5b4fddd83b9a8cca4beae91eecdd741a80711d85d357d63318eb9487c58e61d9318e87e5ef569624744dd78f0277cd5ca7cddb95729dd9b230b7ebfbf382f3dd58a2b9c9ab19ed8df5b82002aba907ddc97ba3a7136094750e91ab787e764164e445ff13b0b74a7e883e9eb7d1430c123602a5f3ee40bf4abe8abe4bb4d8558cc5bb3195e1766fed21501f5dddf3493e9ac81ef91e9de5d7b5ca0ba3177fc31346685e6a1eb64407838819c38dc6f7ac69cdb5534c78de2ee646503e0c0c24f3b0f054dc04b24d9b3e6e02634a6c01841b141237ef79d1b71d357aa9d982a57e0bb9284f47c23748e4a8ee0587484d4aabb56d29e9833db0a964c832e8de3dad4638c04582e832866035e79dfde84565d8be58a68dec80d30f87a1ad8ac3d3081f6dd52dd35247e3a8489843a4506a1bb3d99858f845abec920cc31360c537ab5174204dcce06324f8419c187313403e734e83aed819a293a837525f7b4a8e06bf0543a9e6924ec3147b131294130b777e3abb3c819eaa665401f0855c97446726dd41f0d4f544a83b6f9b6d210f8db7d961e608e7cdfe2449c5459527ba876e76e72d5f2bdc558f3f3fab7e1beb9a4bcb9ba612791b403c8be7122a07757c630bb163175862d458a7fe868be1d0f434881d6c5339c7fccb90623584025b8c623fdc8b729e7972297353e8b32995c4699ccda9721295e1608041b723231a985ffff4bfccda49cec39b62f544737100e78033128e6108ff78fd259d331e8a3f49356cbe6503ba061e475acb9dad3c300701c9acfdf03b7350e68ab465467ff7d1d9c04071fd8143ba112aee2f0ffae9cefd77ebedce20b7d3f83b9afeb4fb771c17f31918fdf0ccabdb0abf42a4c5bef158b4c1e77a746a121facf288fe53de432de99bcb7e1636e2be25efa187e6d024b95eeeb76a1822c87c8b029fea4382b53e5e7e4bdce04032419dd782fa0a1278edd46698522228a0c75d8cb9a54ee2915df7e05d030f5ffa2025e7277e659cb880a2c7ee1d9dac38b6969f4726619bf8727512f33842c55c9ab4725fc188fc788f71f0a8302e346b29c20e6703d5ccfd473cacf737885ac5dd304301223183e9fd1704fce2b991075ee32d80563652971bee02b82d59ddc875aa20e39ae19194fe933ea316478c5fd976db9b2eb8a811df787d024ed42bb9e6c7f148a27ab64c1b5ec1d707b6e7f05c3527f945dbf3a17e39d0f5eca4846fe9c1d7726c233fb940ecd0060cbc87e149f3b9f6b105ff5ca06ac981b8084a1a13f1e986ca2754ef096dfacfac80abf67ca848e2951b7d6f77b7a56266b63880378991eb39b4731fdc7b13110d7450b3d8ac9479692e7e52887db42d2adb765b7d0edb1264c39bb3c10561b3199528617d8e81c422c07b5d7df3a423bb1c85e6e07a24d6fb84f8b606d5f1c5ef69ee437a52469fe74aa4dc2005b14ef4647a962f0709bbe6cfc41f3469cd0e1fe7131678beb530d79966e004d4164ee4005c57d5573414824ed8567e89cb241af5f50fec4f755e02d35878226aa0c17e6a6eeaa3e9fdd24e7b53b3714b979cc91662489977fa28110bd5a81a40ba0f91b0e71620697fdf9b81906fa1f6e9ae7eff2c8b70cd92b0147bf7a3d8ae61c3111640208fba9af54fe3e0c45fbf7386730f4eb4e69550a407ad1247fb6dcd6454fb7fc2a819cbad59917a9c9935f35ddc20eaf78ba8584b22a95d88ada05b04788b7e393662d4396c7ba12d41c187850357f51db61cd636621ad0d4835889b1562754623c2fc4b669b4138db649094d54e29779aac498508fadc4d86146f50b68ba42f3aac424eb89a1c1a77885c3e24d630b2c26d7c0fdad782a95ce571507b3246e912772758a97899c55013fc54b859033a17353b954abb053bcad2c5155dd53bc49581c5d4ef1b606e0a778c061531174a7587dd06aaac996f1b2f5fa45798a4503d229feccdce47e3cc59e255500488f4f00f030a30af3142fd192332e8aebc012188423bb816531c9a0ae42f86125452b5444c18bf32d417f852a68f0a1c7bd69c97cf79758a4643b4e152e9f9ce45a11caaaf69af203107f87609a23a77cfa8a14dfbda08651ad244021a7c1c0f6ba95a64504616343dbf207fe4e3660a84dc2454d15d10fd6ec1ceb6c0587c71dbb960193ec93a6da664b1f2cd9cf70f86366046c1af40462cc0640193cb7e89422368705fd45d22f48cd2235e0b998e4b8df2909cfb481f3219f9e377323c930bc300a68e8abc46b511dcad4bccadafb20fbed0e371117c1fff1e675c0848256e70b6ae129af4e3957505edebdb0604c750c262e23f9f3bb5f2b0dd6b2b2253d99b38406814497d3ff54ce736e1b3a67e57466a4eaf24b619e49b5a6eea1a1af0dabe3f7d6833c61d35716e7094805d471441d964134da542f3d3047a9468211d834b5dd806aac98f86e2dde4970edde4e2a8907b85e9bfe77ccf0ed555b95e28ea21a0d359bf6b6f8b47a9dc7075f050de596f4acb537e50bccbb7376bb2bf18a75d05d3bb29bb0f21404f5e469da2684de10149f3646c80be0ca6989859e9e915d3d602be796fe6060f52cd29ae2e7b57b314fb54eea06b38045d3ee3fdad38bd8e140fe47604ccae6d4b604f25d25ad96229429059813a42cc46df39807231c98f3ceb9e722526c6933d7c503011d42717de83b12077d6f49de501cb23f93c37c1da8ad9ebab4534933ef33bf44bca3c02b73883e7970d87819c86f9b5582771a3bd75bf65e2f57c3fb45c8f13eff293367e3c051fa23e1212b320b8dde051bcd40d428ef4c0e2085e74571c395ad1d785258891c28146170c9b80ebec734029b63e8dc98f936e7264a23ee961e859d44b449a26e567b617e0ee55dba02cecab913957ec2a4c6ca73c421230680739a13d1f32107816a8ec7b1de8ff473c808b6d9140787d8cc4765b626c4fce7662bb3a8054cd7eefb40da1efbedca7ecc1d16b45603e7e20c55b04bb06cfa503d4dc0559eae40a0dbc568909c6016aa9d5f9a489e59d9eca498b8777a12945e52a3ec244d4d95c2aa0bfbe7486069bebda379449700753812bb157902be93fcbe55d417fec8b3911155d72d532c1ad382027c8e7c0b666421e0594177773fcbef964c1c59bd76712f4e845c91dbf57269c14b8e78dd89cb5a70d65be3432beb79ba3c0fd9a18d503d3c6fa461369b2eb63dc6be7427fa4217d95a398167bf913031a2910b6436831c64c5388c36037e1af905bbfc80e9ec1d26a763703a5d227b7782bfc300b61d8cf04765f2ed1ca55f4f546513004794e17f68207244b291cd0dc98137a074fd0eb7b50577415b9e15c2d8c7d1b34a38f2f36d51e2f1d322e50cb79a986f0817c3bdf475448c799f401416907980bac66e514c45485499992f2a1ca4cd6f6aa1e38a878a0aba8d29c52d8794bede7547c04f8addf1fc55c49a664e9e1006ba22fb6fbe7c13b0006f46891b96932d7a3f5b1911940e3cd05e34adf4aa3b2d51fdc89378d665515078ab8e931d9c1e9b8cddd8abe4c5d438031f300a47c54b2c1d4583f7caf874ee9e61a22e98132162f2a4a9c4b95301e631ef05db441274365937fa38f1a41467e9ec1fe8ca19265979d440bb5d9e27eb1f4b676c84d157e185ce9e8c4f39cb46efe85b3e0329f3d3825ea0dbb4139f0ce0f13c3dbd77e9e7e0e9ad47fd37a466014dffbcdcd300ae5f45db2b13c3cc8944cac9ef6a3456cc7a054a053240f92715d66e0b5cfdc5557cc436195b4ffdca14e2ef83d38c02c620a97d241442e30c8b35e70963fc4e93af4dd6b231ece5a6cb40f0cdc97ca9b58bc7cc66a89401106505a9570eaf869eba125892cb472f781970c9df024497a69cb5db162eb9e6c5faf16b31c06a3711a05660ac4f98fa87de1dc1ecddc3caf7f414b891061e0c003be97f7e4bc976f7453a526e3e5dcffcd5c0e79ba17a97e7df5a93afeceab22d29461fc06a78a015649da9451507f1c6d35de89e7250d492af745fc0d4a79b30533de177d7db12f0dc7292170b98956dedee1244acc614ce0ac75bc9269433787f442122e402d800e2d2a0a4663f36fa17ea1df47d041f4dcff25c57bed031b73c11b013695bd28ced4943a66b5b27776c2dfbcf1d1d2a5ceb28a5dd52bafcc07f06468bb1faf31113983fc72ed061478c03c433982ec9734eff022155d643604a1c4f1c988e147493f9967582eb0727f4c33d2a85eade01bd28f04401dcc4518c0c57d8c45f7b6baade5aa9cecb276e46d399b5a2b34e2c17d001e28a7636d9c960a4cfa64274d93c0d52aa1f8f6bb7b799183e485413792ca982ba05fd969ad88e6a27ec13efc6c65147c888cb47414d98308593d0175e6604ea8c668c833515b503dbe15738c7e00ba9c02ffa3da11c4f35905723629eb2317c2fbbcc5299467c9fd68991be1b90612dc98e896a9552f1cfbbf8c7cb47a411df5b84ac2f77148433df6c314151e8d4e0a226150001047fa3c9f6c22cd49ad8dd28fc9ac59c9becd2c611c28da19d732f57fafc2bcecc1c2226134c33b8bf78a3f7767223e5615564b6dfd86988eaf541b1e9a7c10a163827988cd741d8321947c140013fc3e5ba017db6137a44daf4d9eb98c3121d39597a17e74327b6edd3c90923bdca3c657d12f0411f7bffde108c9a3735e4e58bf71e7ba82d4f5eb27d04427e5cc8eee4c0e4ea9d3986a299622fea24fd75cf7eca500be99b1574fc5b488e4ed897c6b80f33cd3956d7225947ad4397d7df06ae685ba690a15424f1cb8d0cd3a7b3674de6e40aec8895dcf388c6d006eb14b407369c321cc450700e0f254cb9946c058e6b4463d980bd957a4525f45c151c57131544752d8533874bc41c790591792cc7370f5c2e86ef0cb7d2cd5e0f1b174320e427cfc542983bec7c12f1a6796dd03a32680a596d41adf7654a98e5ca53c17463f741d6b06365acef864186a2b319eee88681568fd00776ef0dcefb5d1015c1f785c999109ab9c6172e3e210cb05436b7dd9f41db9a52651c5bee5f360d61557e0619230c0732d6d4f95880309c1ac5255ba0c01496af72dc022e3e1dd4d1c21607de91215b5253bde7909a575ef09028f59afacf29d509130a8d3d076264323a74cf585a2c69cdf1afb37696d2f955641a330ea3185621c45abc6c32d775d2c5460a813a8e0e63418a7a2c40528d6c27f082096c05a76141eee6d3a101603cf1a581bc9229acf619a85ada69434ee2927e416189b58647e2f2d0b7a56a9c6e7bd71ca30c5207fbf03e289f87aee2fe6f6d2c7626457fcac2a2c1d62715e0ba1609c0ca05abf4ce1bd39e0e0a40757141900a6c5bb4bcc3c3e3549c870f9e1549fb00006168032b36b9e4b7162a99bed16613e980c6ace5f700cacad0978091b3efe209ddea2a6f20b66bbdc6bdf9a827d236df9479a1a4708ae7d442345ff0d9743730f0f2ac2e75b3412074785767ccf331dede0d4c7cecfec249b6995fa12f407c03d3c48e3b7e64a788fe653fa9640af0bb632c750092beb4317297ee0496e069a4477c441d4cbc7410715ba02a5f3342cc0e6531d0cfa2f68362f7dc696b2740e57268031e8d87edcb9daff067ea25f1fc20f7c04720644f482698a994702a70f933711d7399b1350c48ec4f18ff89388acd0f9b828fe0e479fbcdcff23fdb650d3ff2122e34781217041bb4a2d5674416c2244dc0ae5beb10c072e23ffb194cdda2c045dd65392698a0ce958a568d9344239e901b5e2e8569d178a8a7bd631ea6db40ab43dc204798b45d34076fca301633905ffd408b24d3a304cea3aea7bbb3af0fa615e32bbc596dcd125d08b3ea20f7150ea552f1a6b5041aa22bfe9c23601e4a91beead48dc22fb9976da8c9582e214269ced43f4b95cf85e0327abe67d8e98db4889a50b23c7cd63c4f3bae7d684b9865e24b00522acf372dcc0f4ab388862b96dc1ad66387a6100c0949e8f788cc6b233ead658fc60c18f5f5d9800f243e777c89987e428d39173ef24327ad007e721704b4be26ceec77da4eebb17743aedbfd0e959f143f851c0972294771439b1a9d3aa543421208cb73d0737082a54a9255406c4e243ca75f095c5e8b839ae2ac2ec086030b4d84b352c4bf221c20f6e0885be3932605398a07d1854c3bbb704c9426ffbae0e9f68c7f61c68e4b8d9a6b63c2e79d23c82e1572318ec1fafe4c7ade93e1b7e90a03048f09dc5311c863381fca03d6dcf2e612dcc4205d97218c3f8244e7e6fe37ca8afa63b72697694cd29b9e085858ccc59dcc246e89667c29bfced600ba734732e0616bd9e09ab12c8eb6481330af2ee0e3460b15ac97cfcc990db329ee609ae784090905dfd1bb13b742742547f52fc18d916666f4217daacb1214b71801a01c683cd1d7e2c249b1244374d45520c0a503d8f06a569e246b24f3d90f47f008594a68811611cafbd33482eb2ff3d1abbef66d98070e87e78abc86bf53bb416b94654cf74bf0273d778b6792da2ba20680c43426b16d4857c0e69296248a4584b5f2b8a350f7dc087e283cc111418a5ed2d91c76dd72cf0d4ca4218ca9dbffa8c85767a2148ee095564e8d0d55fc3c676dc479345f7b165b3f57d704ca6e4ab95d7dc3d606ef0c2b97bfa4c2bc236128c1ac2a1ea991c28d3307931b0ae1858c46108c9546895d4db3bd53cff5e2992e3cfcae078a0960e2ca6467172cc577a95909e9aac96d2811d6a2951d7a047d4fcd2779673c664b726e591e4eb239187cb77b2acde664d53c17488f81698e35e2ed705d01b492d62643771c9947f76fbbab78559be55c05c5ca314a1ea7c629f9c67bb6966daf764c13ceba15143ad7977ad17da6bb936f550d7e91d237725765f2a129b8ad6bff4d3d0c9bb3768dbe06aa9a6f93be7f859ae129882f343fd0c36cd713ea3a1d786e2415ec6cb3066e96d76986361d1ab6198eb3bba1dd66e015b6ba82e01d5ad87cbe619273da145447904843fdb92383d5a4e034a999ba256f11ea2211198c8329fd545c69939a196544fa09678c327229a6d358982a426aece22f377e9232d8735d428ab5fd4a1f5fed88820ef968a5ba66a0bce61a5015d5950bc0adc2a93410572473df28ceaf3874dc4ad1089c17aea169bd22ab02f86badfe287766bd9c041491d1efba90c1cafccda7968d98ec7575f30486ab03e125131b6b164591ca2fc3d0977b8906e7911aa3e5b6a15cba92da255044344472019003d58ce9c760866936629d307a3350c05fc8f88ff04d70e54fd6f08e76a073a8949c569863bb14b73538d41761ada121bc7b8f458b6c3b13185ed31b81a8b919087ce0e9aece8aa0e5c82cea89853ef705aa6140a51eebdc15ed281d444da84f945b3b4c6ad6898313f9c9c05104480f9570f911cc79e5fbae4cd5e3caafbeea706970414fc2b73238e013a55591e29197112d96ca82cdd5d71e57ee766b2bf70ae60f01971827b1a30f6832e6aa2ca551944e0f0da9d43dc1b9e2c6e0c3181380c1e7059577aa1663929a846b1e1e18ed127e18e809932a6f6931db5d37d1fc0850636b9d0ef0ef90c9bc9a83190c378b567dc32ebf019a384e1f8cc322037d0c4fe0675631eab1bb2fdbb8d24843e5ce486d898a85e285a79fa2ad13d02e638c4c9db106006fe48752aa49e1763328c94709353ea5b64a16882f790c3280009a4b107ae9ba8fa652f382386e4b34c7e6df77f55dc35c4b221ab48cae8bf70fe6649374d956187ae5c29834222807600eed5cc0fae86cce3b51dcd2c6b9dd6df00d06cff4f4e3c57ee1263b1d0354afb32724948fc2f82a61b1a638fec4980b70a9b6d90f178ac47b251c3955752c13df5ab2a610cd435468a8802d43b4cb31e31f40cfaf57706d9f1d8437c0011ace60430c49580db38f11a901a13dad5e7398d8b1dc68cd80b48760e3377c5d08ec24574cbb7372c20056961db5cf4f7bccb124d464ad76e033b72b8f9b6d5ca69aa1f17a4605edf7378c5e2a1093fdd85221629c5f3ce95addd159c3629e0b9f629aa8415a116ff3c649d0aaaca45c91e162abe25b70802fc36f0e6401ef769881d1052a583da84468796069f169f934f04d830aa2cd51af608664eb3511f166bf6957c61f2fa8d9403eeb48c465c2adf9f79c8d44167f94e35301e4438447c8b5f0d6e65015423fe16852f7082b5231962ac093d3cdb1d735a9e47b1443017dbc9a16cabd1e06814680c9247efc8f899e9dea248f95c9aabd78d23227c02438b0b7b43ae56254b360731d10fe6a11364b193695dad8219aae904ba813f638639f4007373cdac4167ef60ed76705bf62716ee83a3044e153547de134db670b8a3a1fd15dc5dbe00ab28a5728d58286bb6df63bd73165bef87f20b1ec53474998cc8b5e70225f22a61cdafbf59205a925b0b21c6fdcafc6403ba38bafe0d3a474b32453bac1722b297803cff6dfc66510f5110236307901fa63d910d25deb3cc9b6542b070c975fb232cd568adb0ba0b2ad8d617343aab703750195ce0a29c1785e1772317deb1954442afa5aa5b36c48700ad5561d60f66ed817bd2dc5a6ba0e74f99bd6a78d37f1d29e2fa6adda6e9dc990c3ddcf5154d615b82fc57e41cea237af93a2d6cf5dedea54d43ae230ed7554f4de0b5817457f416d51abafe3a26ee4be7a3b13bda61cae6ee7a2b6ae807d49f40b721595ee7456b4eb1750b62cf8e5728b66424f6b0607a1dccc48dcc810cbab3fd1dbd5a9e8ef735f7d9d8b58458e57afd3a2bf53c0eab2a8afe82b7ad5db51f4d68d0074d2a057ada005bf4474e6a79468392b89c9bc6a252d794b93337f52ad757969cccc55546afd29bd20c542e752a9486192cb8c4ee34187bf645369982712b3d416c169b0b5edbfac27c8506854ccbb2b78175288f397a704d01ce38ec7e0eb8b58c134cb123502a41c596823123aa668c185f48c1592b382966483e158f32f7894971da91e607807c845f5347a1c0cc5a261622595c6c6f631e359914ab9a8c642ef44b0a14a21bea75964e841a063887447291af279c5b98493e32acdf398fe5dabfb07355e1a2f2db587093f9607f68242e0d9af308a1fd507215194a1ca63acba83a5aefb7d654e6994fea561c5c6b36b61c7a724729cf928e5dcb364590945ab491c61e8328ed5ab2c6c85b3418e3eaf3eb9d689c4b282dab93f5a117a8b7ec1451370f9af1dcf5506676adfdcff3099d200fd1c67d32c3e2c85547580d956b9195d1f09ef7ef078a3fc9ff807ee18de89cb422f28734df487e0c7cf2dc6c49dd00c1783147c2d2a58c8ee824d0c28ef5b9f84accc3bdd0d84a39f2b68d8c06452e39e24d47f034c460c98dfc51dcdd1f34b36a01bbe6f0877270abb4bf463bba8bd920d4ae2cf3460c2e9198dd0ad795f83b7962192ed93447664810cabfe24ce7708d237c50af49ce68746231ffe6f79d82be0388a6f801a2addc3005d87468b3cfc90b0cea943b46a764a197c13dbb6b432d3df112fa788f5525fe2a3f9f6363cff56c026e1c5d4ccfec5d5a3102fea0fe2034d21e86cf0adf289e17f9b3fabdbe2ca533786c6e4609bf404d2a2f108586af9ae7d949ab60e4e3992045ae41d067fc7f39e9a904f2c74df3fe5f093fd3aa8a4f071c09d456037eb3d62cba79d28f0baaa7c7860648d9aa93f908c540c81729722f542d38dbc15101a22c59b586bbf62620b6fb60eaaeddd95831d9654a6979e30c01f0f8c9d32c56df7918ddbb1231b02735cf4de5fa4b68d16a1964cb53399088e41cd548da08731eabbf3d8e6e9fd478ee9faff472b46333489e7621212689fcf7a7d1fbd0866bf1080f3709246e59c97859fcc2530a8de500e11b8497c15149ab1a95235c7ad38aa8a5510085b6b1d5f3f1a81ee07b798bb4eba9aa071029fcf383ce01f98faa9cbad00725eea4b8d5385c6aae9a30fe33249f3b0cf919d3474e855811b988197784e69c9adad9496fdbcc96eee9c6ed20482907b6dc4c463449ded32fb2f9e541c75b09b89e064585f93ee0b74c5e70bfebe53c4590d2f8113a8542eb3d3a3f7fe9df36bebf8f3afdd2d8ac775a2a6c46d38885988a23988958892f195bfc80a14d37566d2407e8e8cfecf25e1582d2e7c5545796d5b00ccacae90e96f92c9ffd935b5970b2a357eb6436cfedabd3fd3b4ee32510a99178b75f9558721e14ab6f0b317392ab68dedf123d673ba64697c77b73d2ccfdc312da0ed11a0a5f3819ce557c41c81d649b601ecb04a4bd7622028aa10fe8a0dfd283f79599fc892cd722ab3e5eb1815771f5acc83f6415bb679a45fe63538de12ae5a846c8bdb65fe661909fa1a318bcbab25d80c9fed6b17d8a0d13092821a54b987680c2f55833b96b4ec3b4b1b940b57357fbb841de46c2ddb33f784d51b2ca5b5140ba083e31c349d4955812686b86512e02f8a406cc1cd57b67ab3ccaea410d3ca4b3003594ba1393a3276b0b7ff52c5df6c6cd44da1a1ff26dea3e7969a797779418dad10bee01a267ef4e5a026484692282d2884ec022f43dc4c2d9498e60544a265b832d81fa283918d2d008908424082e3a700a091e6dca1361040270781105847c02d8236031640a85ac8c41243f3e9bdc0b3531f0617401ef6dfe0b52c736da281e28459ed06a5c86dc1177f282c171a6238d16a9764fa229aa4e2f5db1df2eea474d0926cdbcc7d7f1748da8e98c4bbeaa06658ebbc3c790a692e2cf32ad911a3474ce539c915b32e588674efc44a21fd450ff6147b827891474bb911612c9639b8320e7dcdb0ffc716a5d22be269439fbfc1f1609d6be069ba4bea6ba2f24d47aa3e7bcabfe57a8d51e2b737497e5c55d92908bc848fa3c43f737f31fa91e040ffbf81ff543e78e8e88c276e8dda32b4426981f0c390655928499502d4f777bc1e058083fbbdb9caf1169c4d29f8ceb33a4e24dbc052c69bbb922b1597abe87f88556d7480796f986087b9f634405dc2e713ecd21fb2940b1da90562928ba81bd93dca411fc1c7b0cf220a0c015e656aa1a8a050c7e5575b7d5851d5456f0c8051d52ba8319bed957a8574ac38ddeeb1172ecd44f87020d535ad4ac7075089bda86b9d5193c5cc4ee8fc9b5a327c3bca5f88c6fd96e6890ee9703a4dbcde9dec02014a8c6a041af4f803ae321bf6a420ff4b1c6242d44e75fdd3b0b64e30b3d73004d4b925442dee2c12103f2a72366ce22a98ca9ceee9421135080c0b9774793e2514874b2237d5771c7c4ed8d8ae41b9f3fcb5da972bc817f1f45c2b90f25c5640fb4f4195807ab63620315be4602ad9ddf410be8d8d8e00b75c9d6d0d47da5498568ca1a2f6c814b7a15777264a3d168e71d60e7009bd54910adef79c367a7eb39a67fc1beb990755ad804c67fc7d79e88e544f3228f1e4a6223f425fe87a8e5108b9b5ef81c762259ac3f1bf592f5e6d44f10f3dcd8298f6a5390fe1d7c1893e3f75445044966611169365c878c7fe6be8a24e111ddb4abbc8168837944a7f17a23d6c420b6c74b83b810308d7599bd53a98406a0449eb875f4f2263f60e4323b8ae1b795fc761a53f3c7c717eda41ba8e9d8d58c17b66b9acded34371e7775d353cdaf5975cdbc31b151a87565daf264cc8943a8577554c24792304d9ea42587131eb139fe371c3b602ce4ad3e38edfeaaecb7b248285ffc5ac5ef3c240c77ebca9a00fcc676e3b900425eff372f734e61ede7c11bba2b8173a24cb50c6ff95ff9576f9f8d98c75f40032777333e70876afb5c590ffa471d7e32f795615005e8e2bec1b1b46977062c0597fdaf1bfeeaa2b46022929a3224cbe1634ff27ab4fd1c6e21e152271f5355fa2d950b7c72c4cc08fbc46f1b7e1e73d828845bb934d57a13ce68cc22053f2cd14710fb3f729835b25774bcfe162e291ba2b8e1b4caab7137676e2fb60921d9ece6561e530a8d2c238611cc3369ba8602a9796b565f35e33ce65e9762fd402f008389cf284030b5f000ebb867e497f8fdcb9a4877ec9070b02405692ebea015a97c517b91df09fe6e8531dff0b3f0e0e4192f3f4f4cce00880c457e9e39e5406c8f4a529aac4145c77f72e4344f6e8de7b12cf5a23a029fb98509cc4839eeb31d7edfeac891e8b8eef1f327d4d0b921ebe28623cfe838f4c3494535158a524fda55db99f88ea31a51f7eece94713f844854e981a9160dc3a8bfe804fc0c8dacf2a385eec727be38fdc8f8b2c93edd4d1efc45c15e8dfc5765e23e723e098100c2ac6f377af122cb658af4538a0769b91925bc0350c042e02a0951d3707d40bcc794ba7b42c02b2e3ab8df04d2b8e405c9d3689e12cdb2a4b65228d25214e298f1bc3c7ed41970b661de2929f9e9d6e71b1e0b82b10857c891ffb64e268a699caf039a7215774a47b829c44575a923856040e4a3128dd15ba38d5566497040607a97e44044f01618e8c5cfa1bee7818f1daa9d29acc7f8ad405acb2cbe735ffbdfc385111db1df2df56e0cb097b99ac7782cca64908bc8a6db6da56f338e244e93c80a81561b46870f9951312b6ed74763bd01fda9123b09c10c11f67ac4f13e0dd13b6321693e584f1f19e547a81fb74b39c38bc32a036bdcc11676d39492593edd45934a715de5b1090ace223c64b4d3dfb7f4569e2112b57e347d08f3c04a90cfe27efb80c3b18b7b27cad068c0307133a11bd00fbd886747188edd43d246e10b0ae019bd27d70818aee4f29f7838d5c734a671191ee7ae0ba1af29f12a174c5ceda33b7dcb3dfb636d224a808f67fee633331d7f589e261df889599be33618ca106ccf964bc0ba2b7909df7098f86a04699166ff2dbc95b9576ded9e44ce6a0d9f4a6ac75a73f4b271f06af6fc946d8d3e08b588c79192466770645a40c553ecbcbdd02a423aadd343a328be142c24b990e239496bd2ba4fa79a4def82b01062901761c035d1f0fa0fbce725e6fd4017f4eb7bebf6409e0c47ee7203243460722419e0d3b32395d98958684118c072db6608fad188b41b17b3b17c7ae3239d2e11a36db716e15bbc1973397edd7459ebf0751a109a838909295f911e41891db3142e72d946f664b4cddb61eff0da7136e001ee102f135fa7850edf417ca95a0b4dcea6905c1fc38d0b6480f42391861701803b1c9453eb0cd63321b4317c6efbb812a7b2ddff72d97d084dfefa52ae015a7413ee8f0e07b96748ba4259edffceb1b4e232a6e801cf71843c640df22d57dd60b54fb3b4727575d73d86c650e57bef796b1698f858aa1863cf7566d998d1a6930127fea9a2216741482ecbed6ec3bd7b55772166ddeb54dfa04267b0ee965d9a55160798ba7d0b6d140e74c76bb8914a9d39499780a5f76ed1e161cc33f05b45ebb8c6749a46f0db15a53a1cea9c25149d5cf0092c1e359a5c7d3fe1fefb9c8b13b37804628939b29b95de6a608c62181d3f7f78979c7be722abb65618e9e17790dcacdce999499204b26350535fe7ab340f62c09d00815a99fa4df36d31a2c1b2bec5db47726a3329094cd2be9c3b24d4a35251d5471454479cc44f273173f4a0875cc4827513c61f355cd11f4260c215461e10fe203b3bef2cc98a2e853e1d6b12bb6be39f782f7d30e8a37fa128d72b3e881fce14d7dedf51b11092f4c5c74b0e60a6aef23afbd64851a2d86ed25be6cb1eb2fee5f955d3fafcd4d518816d94daebbb681f4ca85b19957a86af0920c5bf16acf938bf8ba4a0abb111abb1fec47dc56276e7142742c7d831e7b16b9d4c78e18081bcf2ffacbf1cea66b0101b39c7cab16c7e40eb38217222d9536e606eb2e4dc5c03f0eabfad0c320831bfbd60701ed1e37af4b310556142bf6f6ca0937092ab2f4c88e0f21ffdbf2d5498cd553af48fa4aea17beef970c2ddf650ad25f28296a19f8395d248c63c862dcec5b8eb1a1fb8d2fd3d1ded2266da4aac881b9ac507de45e859d41dfd97449050abaec2718da52f5637f8ca6e15891eca1ca83370f59017b67578cccd60267d9fa4fe360edf7bd46afb172bc985266d04d7985b337de1d4129a549edeeb2360eab0e375d3ff9952ad3e6e3c4a6b88eb45658ab4e826642b594262f792e074ca72cb6dc5f667c70a30088ec551e56acbd71b37f0315e25b55156a9bce1abd8c3956b35ab677aaa505486b7704683f6b7db8ca66e72ef44aa11ddafe2c93fa615a6851c7701b4db301635d571c5b4d8d24456ddff792e9aa5b05cde2b51d639935837105d97e01b3780e2c71240c63886010237015bedc39e77bb4fcfe4f3c703dd65f2fcdca1393e35ca2a224e8cdaa3d97b112591dba9775089f875fa832fe92f1c0eddddf9c7dd0818b2a33c0e01c5731a5bdaf6ba3574302faa18b6c5b7820f5c117403b875e9247efe2e224ecbebe35ffafe5ddaa7349c70a695f9a52b68641ef8dfbef0b73e9f27fc9373262b5aa85c3c4dc12b9a3bdd4f20d68ca35db04c70ce84aaef55dfb289ca5bf0873768adf8cd9b557c270f5a362b6e2f6177e3f90dc8dd105baeae4f5d5b1e4713e599ec9c90360324a0c3a5fb90352163dc7d4b584d0bc323d89e2308712995ba35ac7a0cdfc9a3baa7c5e7d691434665f4a98d3ac493b23ad67a92d28618b6605910c30a68785ae184baa750153741f2d40d989af951c089a9820a0ee82405c777bee186b7ba7d62de4f46be94651cdad3c7c548dc122a6c99fe59fcd48a3bda7634b7dc37b282954996fbf29795915463ad9183c056e3c752f0b5edb91f5175fb82bf0b243f41fbce38b05c95ff17979da0446d5875cf7a573ca620d408f7d7a1afc5668a0640effd0ddc43ba4edaa502a751eb2e853e5be53ccebdd0bed293c27de6c5ae765b8eb4fba22b1866c7fd6c3636dd42b7979bec475beaac10ae2b1afb2ad570eadfe98b5a774882fd72d700f7fdc0e29d3c9ddb7da568b30acfc853edecad5c0baaa0f5e75143d20107368b5032492fa26602eb29dab260aac6bb30e87fcf93e3e77793d56e9100e598be80ec21586c463c8b490ae9b5790c1fdc349c642f7dff4411f318f75833934a31f9cf996e4c222630b3b799ec02cc1d286574c60ed211c3c94a5e906fe3bc37c5b74c1b619df5b2792b865192e8ea20530d5e868f7b247b67ed374eafb8fe62837141e728ae8641628369abb31e80e69b32526d6c70da4fb5d85b4a9b90f0dfb3b75c0d7182083dd5fcba8de558eeb65d4e82528056fcbe44706746b8315997c544cb1d89ef5c40e7144c47c0ed531a9dc50a5a0c55ee942ea45610df27e24a05956ace6f43464710c7e528a9172adb1cc890f082917185735ccfcab754278a525d22c2c97c2a28469cefc923a65246af70bfa0e4a1fd5f8ed2efb32d1953f14ffd6a5c9c8d76f350076d16826b7822ac2606cc3bcc3e134b16552a1b9f220a9cb63d7cf16e651d334ff4fcbb66b219ec20a01ba1ff1f70efbc2ee6661ed8ebec2cfea809b74fb6b45edd28724c0f916f25a013410c45fc4b87e794acb0324d89fafe6060fed547f0e75e0c7c49510c1ea6ad71323558a17f9e4d1c110ef2bb3155c54065729a1e7720f45381a95a774855ec9d530d86d242fb5ed182bf32a25db9519b24a5ffd8ac3bf9770a75788a462117761092b55c99db18608ccaeb56a859850250afaa0e2f5d24b99e59b6a23bc588548964f65bcf8bc03965620000e23e814b83859e40c1eaa47481775921419c6bd7d073203d1b31c575a3bea34843a42700e965a58dd39485c72fc8285b2cda97bf588e9b3febf75cbb090ae35f9240fd7306d1e2d0a3d4b9335c1781533cd39da2eab044f8333792d1a502a86bde45558ed9f3bdecf497bae78036c33db2aed59ffdfb17468e6f86516ee9f7e15a708397effb4a67c1156a70ff6c008a1ad13fe0e5cdb8866a43762055f98bce3f7f6c5d667ecfbe3cab342fe91e5de91fce5f3e6e24787142e42e6260353301c7baa1e62dca52db8239e30e0f0481477104102536448639c901ed550e44732bbd3dc6912dd4f9a8b2df93557d805b1c461ad21297f5ccdb8bd950b887d444c0bb8ef57c72cde09a7a12ee646f93c3df32f50275249cf00bc068614c1c477e10c0ef2126404cb4100502764c0ee764158984f1650f1374a71220e180ae65d45c2b58dec38b804320d449e12e9bb38f7c7524ffcf4c1613d465263ccf95d4e23978ba1e1497df3c5ebfafb88353fb568bbbaf23fa6bfa344f2f8ca9e8fd23d92486c9b17d278e9fc71460a9bbee350a87f6317941b04158aa46ad646f31b5e64cbca1fb5926a5825ec44bbafb0d75fd761a807cdc37574b7efdd692020c9cd69fb9c3a8d38cf9a76990a22a0dc9b6c224c77475daf95150a9b1ebcd745bebea5c55d44f8527a5b240d4c38cd0a545359266742db3496ef48e0bc71e5bec406ef689c1502125bc3923de7d0e55924b7f059753e10299e95f6f9c3cb4122c3b8faf141b520b10199196dde4df32dfd9c760375b3ca939481102c228bd39368b9af0e379d952438ac8e20e74cc1c090b459d641e88cf5e4fd37fb37afa893a297415b913256fd661f3c9d44c3f908d1be4317777acd6b79a504de73d421a1ed10094bb6e9e56810af962ee8c60991fb7c275b2cf8b758c15202e13a31714d7a348651f228266be537bbb09eeef1a0d3e58c77d2cb67b9c44d7a32676cec063e0f3860e95d8c5eb90490171c1fb6698b2816aaafed10083a20520f4d3f74517700b3c31d592c109dba97c99e50e4bda35b31e4e63781f28950570162a184380c280c0c9810bf7c387bcdc8ca40aea0856cdff91e02c8ed8b4638d88baa9429575762d8c1357ddddad98d7bb7f7302c289e4642c387310e98be81b067fd088d9f992f8f14ca6b788979cf3062e5b9819a9d0edd21387306beca24e9dfabf58861e2ffe5b1432716f32e0f698961a543dc89e2f85a058408b88037ca42790d81aeb338e9be331c2017c935af22bb4d75522120f07c1672450da5d97f68065c1e9e7d28e64d46a3fc8262d8e413e3d01d54c8c04e879e5b2d399ab002229d48cd8acb03a1d6162bac2e5ee1bacac8cd484421ee1a9093986f9bd6821c8c426d82e8bc76b66df2d22e96200db2b9754be9d4e5485a67659ac3f40e88e491b89d6d5160a5712f6df0c691a28a0a004de098ae74961926d1a90e8fec64f685de380205621dfa2da5e9602a2251fa17fd968ade41685a40e16705a0e3df42f38daac2685be940909147399fd7b051327be64124daca7f9aae3000387cfebf3106f3688da870db57d561ae01c8ff8ef49f4141378b18072b9ee991c7a594854e4280a0c11e48302bb5edcf4538c9e3ff66be0afd85449c8259d048a90d52f24d905498c5b11e507bbc598a26901d13fd33670a71355e9067b9d08cbd64f7c70e1af666f7d17fbf22e9cd5fdd4bb1d977c95054be7a73c1532b9b53bdbd98eb16b9472153bf367b0a0c44fa57eb1b3f60a8c5c8a8d623680affc71e79b289310ba392d32712d731e89d3c6ef0e1b5ff9446e4ccaefb15b43baa1815f43582b199c59bf3f9a36d64bf453e04ce8ba83ab0c97d44948108f51da882381e85abde7ff26ab0bc7d0d3de7ca0a86d411b04f0ad27338e854ddd7c405e86fa8793e81e89c74cf61b2a3c0944cc9ba26832323b35a33f765316308670b70c236ef764f4962f93c74fd7f501293bed0041efc870a4d225c426d10f571fdfebf26c7f61fa57b5073006446669eca4cb39a61ab92c767c8069909def8390397e4f1910a06eb4b34ccc31bfa2e39db04ca859c59446f9db7b3e58973e86e795ff92537b0199c170a81b326f0bd88c8ac4b213f8cf1046fe6d0785b38e043f4048621b0d6b849461a83c51c98cae0d5a1ae855015d44f57d58712a61e071e46e2ea10c4a914e7950314b3309939aa2c7417b3d9ee523cb3c8720959d1f1fb9030b6c9e2be5d522e3d42522ee964689a4ce6ef97681e8ad7e714f534770a3f28afd002d5123358b0e0b9230276b3c9e74f71e2fa2403953eaed16fbe8c82e8e3721df479af948cccd4ff2a2a817faacf2d876a2d493181c286d963915d08d6e7dfd4e6a3b2b54806c7ca6299df9e28c2e01bfa745fec0d20787bcdcf2dfa2dfb9693256d96d178ed1a6d02431673eb5948cb7c34bbc1bdd0064670317f35d179538cfc3828462cf08b6cde7ab477e1f582df88221db1626ab1774ddc560725f867fbea1b0f3e6dd1e1e81a357be9276885a604bd43eb486fbfdf60099aaf10d050b2fbba2cb175570f22ce4c8228b685889adbd5148daafefcf996d7215ef10f7f11289c08de8b0d6b123d1b0b69f3d4b81cd3a971a5e43b9b1173d1709e35a1c04dae47d6578f5c15a91adaec852848990e47f3ef8dbf9819104a60ee44bfb4e14697154718c6d125fca2891ff110f1e2621c002208bc0b14f9ba985f54f3e5dde41df5453b4c3a06fe979fab73e4c26f98fc4ac4cd8d8c788cf2e57b0da45ffadd1045826d114f5fa4722d079e86277ca348f65ab1e74cf4b597819d78fccc3362d4a83f421df6338537ee96e919dbc61bb3fe0bda48c75a182fb48d2d666d66ba47ebae232cf461083d3ed2e74f996b77ccdebf7dd1320cff0312eda9edc5b238af3edfe7fc603afe2bcc6ae64d2327eea87187b5cab36f00f25c7e02392796cba22eb99b25bb2dd846a04c18a5454dbf08b9f6abfeb0835dc33257b980d9c38c06dd71ea416b19d5021e07b688a1c1623a005056f9936f925025b4b98a3f1c7333806cf6d744f78663bcd734a96cbc0a924d0328334ee35a5d0bf787f0e29819cad42414f8883ee0f747951055bd59a8eb06269b0c65b359f6cb8b740725d69ad8dbf3b24590adf21b7867101e97ca787f5e99ddcda2cbe926c39e956b05a08e6c8db0c79cef15d9e3e5d9f9eb18fa005c3b6b47eda20fba9ce46cca837ccdf54a5315ec18aae654d26a8d84c4688ec226115ed6e53bde8a1160e3711d5fb488f2022d55c0a4b45e19d43fe55391b9f57addfe69e2d6829a9c0612c89a6cb2e7e535171a2daf7c9c698234a264c4d8f0207bede3c0ba0d27027427a93fb69a320590a3eba0d34cfb177196e6863285f83c4b0f13d1b4cf297c4611ee60daaa013340d2f822b9db44990d1d1b94855add207204931c37827b3cd96d68cc14ddeaa25d6b6230b039e660be623559d657de1223b0a387b80ba082e6ac703927e74f419ea94778f7592ee3f4367a51d34c16105ac01e826ac4cf6dacf1086eacfb72399a92c33e2bc1ac421b5a742808c993995432725c8ef3c7315f8357389979182b46fd4b4b2b9bd872efdb5802164c2b97a8b77959b1823ebd4c155508ee5a136d287355cf45655f4a880669c6ae98a52c080de924f59969f1104e7cd99e010755034761e6396231625f54c790c9bd01bf1737e363ccd765098a94fc77e97130f4ce9b1c2e86be87173feffef41d919c7f6e2f0ab4b82b39b7e4c4be376917ed5448c79cf5ed7bd58166ebae189cdb2b255cf0f900a44d2a3180150e0d73158b77d8d96a3fa64bcbc346b78bcbd7cc0b3e7c4454d6366905182fde27a523f2da85e5e5489ed66405085087bca4cda09df6a8a53171fd3f5b0132057986642ecc5c0aeeae51e16c265cd24e3bd81e55dcfc81aeaf3e03fd41664be8705be655f1ceac66e867200a861d949cdce43c8d469bf39467539fddb794310f6b16a7c723e70cd198072fffa33d34c8eaf9b32ab87953e80cbff2f3d6a13c220146ea0c1404a97c0d441231d1a79396c76c672b2caca3153e2a4e1a4c1288122eb548dd876771077c787ffd26b7d9ac1097420cd1c7a30c70cd80e37e076e4d6f97d799e738c68aeafb3ec38fc12e40fb3400447217f12de765ea9a0e8c6f83d4e45960b505f0a6b1c368ba0a69f37276d05c822bf6c4dee66422ab272407abe8b3ae4e57f33c42503df376964410e93eac374ab3fe3bd92c5c8da23a4f79c2f5e70dc7d66147fe1b60976fde84b885f5d8aaab166aa42dfec536621f9cee76c60b0eaf70629393c77dc52c7ae480cd82f4538c22b242f99b04d15694fc2e33ef3bb3c15a5c5cd20d1175c71cd2c474b4252b5b0db9e5a29d5a23031f95c4407f7b5f84e2efeced098daa348048ba3301496481e1eb0bf32e9c2ba1ff35e333f2a455072c439a6a01fc250c3b8196c700cdc89647a35dbd6d01af0b452039ee502946e369cf16853d3b24d01d20ada0f65b21a805432b0320ed0e0a60e9d6f4de54b1591bfb0f7b3e10b6046b145e28160246f27c2fcaed7002b39750abebf6abf2878b804f1d411cf73c329fe5c3edf53ce70bbbcdddaad56f67ef03c29962e5030a0e6579ad56e397e57e5c528d258097b4a99630e770493f80d8856b9b007823967ca2d3068db3d4f4253cbfc93fdd5c7bd42e9510e396b2fd60a5a4eba6be1959618eea012e696a775054ecb948cb31066dedcbb210b153f1ebdba4a70abb795a842b324137918e5ef411d41200d7f1fd58bcf1d4ed796d3d0003b9245117c80cb1af84f83ce15477dba983f38ecc976f7d7a095ce9bfd279559ae57a4b3a5b498384de22c6b7057b0970249c479a5c003052fd40ed4778f6f0881b987e0a5c3486b0212039df8f522a8dc5228687978108241d6cdbbe83ae84a825bfed95234a84c30b076448e0e11224f2accf1f49a4992b15271698cd237566fbaf2061fadd729bd5e3d86af352a3b73d58dccd8171296c78cb26916f5ea232da438cccf7672a4a920bf179f45b3a934313446119ef286b8eb4873932d545ab97453292fc1f72cf9f360dbd3838c056a096aaaa75a16505f52a23127de6fb3134554785ce0b23f186c9b50920037dae68d9e816a01e117b7f950b45c2e1730c1272ab32df13c532fc4e91b1bba28e28f85dd026ab52b4447e0fd20010531c490f2568837e0ca164f90488369d06d0058f8842c57ba3e763d06fd14205f98e9e12ff209ee0aae9ae60c6ea43fc2b6c3e3472c927e8796620825a3551b1cc07ad8f96e25c2e70ca806c6ee7e6894b741fb5380a492809dbbac36d4614f4679e023c5918f393787d2d6f75ec5537f4473b57f878da2b2e9a0981f91d7e8c7b57ad6197df2986441256946e0b454ce59dea22cd8fb9700ef03b697dd17a8b6e042164434e4b4624d35da96e2938c0b84507e40490f30a93361533a3b9808c4ff618ed38baecd00ada596e77d1ad215d72745c2ba30179502e8818a66f2923ce3bf51bf6f5c4cd771c714d08c440949129b18d7bd830b2282d45e5074c9aaa36c8e92493408fcfebc5c689d80b37c41e55915f6616ad996c061b08f7c83b5fc70986bf1887cb47626acbde7418bc03e74f6502c162158b85f960998c273219516aa5794f21c91c35b1e01f6bcfffedb96bcb4cae01e880a77fd47ff0c890d8b0ede60c6dff49acdfb7108ef64aa0173e77bcaea6acd8bef3fe6931e1275efe3494706bf8b390e023d7f6870c7748599c20806440d89f1b1e5611d36aa5d1a2564d19238a0e455e4c9da70ea7f324a8d24cd11e8e92c3b160a942239a1f050ae833acd9ffae9b29c0d636d7e7d0904149f8e8e95d94f1ed621ad7a70a605adb45ab363a273113197884da6e928f43aae85654d255a402e36334d7861d64e9386845773fb338d03ce02fc3ab0ccae66c7bb85ab1036f66ce9c29510610bf47728cd9be306152abea4fe173ac9630888d57d4fc77267ecfe42857b6e66a8373f177212d2b1ad87ad825b1a2681c1a2e9a2134a3f260365f50d784f1d9f3afdf9c39d363731ea0b06a49afaddcbc8819875f6b832afb34270c8b7eed73fd2cd38ea9c962e2d7b66d51cdc6f0bd70a424866e191751245c380eb2cdd88ff89a4fd0bd8f0cb7cf6e73565335e22fbfbfd780d19edcf432de058fa152b6668b09338eb8cd9864cf855b0c9f0232b492fad8184fd83244424ff33d489ff371cb6683e9803814e60062e2dd9fd10a14331b2bf209d2c3ec5a93496f60c151a4c0fd8767be078a2418be9b35c5f49156e40e5db8b70118950425f270a1dac3aee53193968d06cfb11378552840f30849ec2359680891d575e7a9a8e213a69f8599e7ae950d6c5b1d660eb519144988f6ad56da47861c2526526dad7491f8303624d44318c7d1b0a742fe57fbc1906d741958d64c24a578464de6c845bb5c9bf8155dd34e2fa362011ba3d3dba6234368d97635c005361824cee0721990c0ff17a426839e25fb6c650622f548103d8ced1ae7d00000afffc3966c9501ea8d1958c1064f6b99f191dfa1b81b7488d52ceaafb41dd2e056b87c4634fe3b097c591db45fc500a567f0644e3b4b4fb5d66eaad2075d0d640118eb8a449973183b50a046a3dca9ecbdea895091970de2f6d89c05442ce345046078ff48480dfc2ad05e072603623be9b77d198f628ab1bde2fa66250ad1f85ee781210c4bdc48aa2027e014b457ebbf5c0a008865b50148c781d75f3cfe38070a06a1478938c7fe381ea71737cb57e593b11370787a87ab50e53db40989c4c43bd327116323c7724577ffb42ceb337d32d37c37d70def2e60b551bee367334c0bb72e8fe58da5b212d9b23e0cfdd2610fff07c27f9e20b931d7bdde2c8b5baf8b9981018e39d3f4580fee817cac3f58dd6a4fa8a53440a7bbe963ced6a073ff2bb613b6363dfd8add7063e2c94576a09faa9ba98931a7e7399a0d8be33f392b76d816d6a2b2f1eb4a95d8573fffd6c2dedf2f5bd3bfc3e3a9d3d060b7f61010b456bb697113cfc729dd125c47b793ae9495342eaa7a9deaefd7876abd2c8ea389fa9d8ea1cc16cd6ddfa6eb8ed5f9c8487df8f94180f537d6170ef297981e7dc92d3552001949191ecb0fde20afb00d860dfc14bc61e6bafc4bd0e94dd95a9865cbd5732230b1abb8d2dda71efa977a4eb872fb7872450f2d177945dd5dce45b14641f445915fe62f5acdedf4a240fe1cc744c31be8397b84c3517045616c2215bfdf8966974ad9f3d9cff2ce7e62116323052d6d4cbf50b73443fa2562f54e4e208c8db09f39c97b48612168e916344ce399bbd8c0e2e6b68b349dc1c10239323381b4217ff4ac45f9c08fc6fcb3a3bbbefbb2523919207e3166f56aef1bfc93f97da7e4d67824c5c150fd57ec6a25f25c6ba711be130588b8af6c9c77ee6ac5ae34945119b37bc698b2bf568a5d87bd98a6f955d46620a71e1a7d545518cf633413104acaed84dc5b3ab54e044e561ccc5da1bbcee71b2172763eb70f7a482facd2c0177181077b1186aea412642c292264b36a497ad9683c86ae6cb127dbc8c587400d6efa3d76dd237a285f1429b4b7dda9e88682908f911ed9e4bb8052711a146c5d7a0efacbebddf540f3b0fdf876dfa3cb78411f1421b6cdb03c928712d41ebd1b651f9b83794130862c1e0a0ea7dd2a3bdcd62be6135a79109c284c2d76419a2c35ca2338f267b62f42644a0d9676481e46f349f899e6805df7d95af1c21493fb5c9e511583747e123151d54179e77e8dea31576372c679abc89b400848b05ba86afb06b3cc3d022ebfe486a6cfc209e93454f7a8cb9236aabcb4ef7818624638d12b207d9aa4267645b382584a22bbfd9df157cd89fc6349608f3a4d874f6ccc32c0370e311bd9afd17b003db0fd70f923c9f4f559e056f6e24f8ff3d3c12970b949a4dbc9244f83c27c1f5f31c35f63ede2fd4f8bd32401a40101093fc3c3823fb4ff2fde7657aa563f1e07d309167612b0a09c1ce4742b3a433f7744e5e3c7a85e1166b8de10a77201122e1313359bf01764f5fcdb02113aac27f0c86498d7f2d267f350b69309bf9a0b46f2ba90e1f0878dc06b7f830cf12198660d699a2920d66f852d04815574aa46e126a0dd0a7cc14c95f4b631406faf01891ea2c907ff999fd6e10c3301e62ac08bba8cea4310b47835e294ae7e542a9bd1b5d71fc88fb1db197a7acf475f0490e280f4b0c9f89bfc59c55e573582cde2b14382775ce2e884db596f11693d6c852e91dbfe9dbc9274689d570742677239b6e8a1fbd0dad270e526535254117122143db4695b89f952c8e42ff9e6ca702f04f81ec5d9f801394b2e62fae16dd9940dcc28c50f7a13b8b8521f589215d555e4ee3488919d0287ca041fe293c904f3c34d21000f9dab14ee562677661934a43dd16ab898c2c78874d0d55ba654ec98080f0c058e9da5d0b99ee663614184ac2d5d14647974bd6fb69cabbcba6c30315ae0fd9c04fa40cd245539b598ae00e69b2410bd5923874aed4cdc4326d3dd2b3e6d3f28cdfef0c0be3937f9636cf722afd20ada3858f447a77b46e5ba3356faf83e58df63c17443720ba8a6b01ffff2d27929316f71d8a8ad14280b195b8d37503c1571ef31b18908e320b538ab89e57ede22af5aa840d4c36357dd28d8bbe9e7b83e40613502ba4c1819eb182305b23484ddaf046ca2528b26bf6ca749d21565b6a1f8aa190769288f0534255d264632877de6e37c4c81c5ce001682bcb3034d7e048f4961e05400221a35f8b06a9262a86fdf4da9e91d437291702abe4a0edc5fac8477e664a3ad6f8af492eec211a0546686f4758b34e0fe7eb775d54208ed5475f8ff59ec32c706aa74301b9fd3d3dabccb6ec4048f33713e5bdedf12e9f5d33e224ea96bcdfbbadeb6ea4c3877a1126d722a65025daea274a6d13c72a0fe597c27c061cab70279f1f7e992380e9ce95f7c4d1e50ab7c2bb3a854eae72eabadbfbe02bc87614cb6abbefd906113d47a9db14f6920408361ff480dc3fb7c4916be26e37a9796e7d4fb53ac521636958dd18fb2c1fab2707ce245c80ceb12e27f69027f675f9341126c937eaf81a7d429c74be437a53e120b96b3e422392ff80c9b1b392f0d5cbc5956913237d171ce429a35941cf093708eccb5c0af53bebb6e749418d0f9c3237b56c6859ce51ad79d20660802b3fc343c014907fa4669129818f8f4aee3a389922df60277015abe9720ed1dcaeec12d6e9a04faa3ed85ddb4b86584f2f738d8f762332fb920666135c792b38490f424c698c560ca53d064f061abbf78d0b1b7076efc45fc78d6cc38da952c4ac8eab7b18941b844864a20b2a75076a7c78843a7f0ee7d76bd9f0c74ad7c9988e8c8a7e53c5ee4060c162fa9eb11592c5e311ddea9b5a9b734017a92343cefbfd14b8a6c9ada2420afbf7afdd7fb1ff54698fcb0982d839bdf1c6c362fe86b0c5641aa7a647a7fa8b7b490f4f25478deffd42b7fc8073c3df88598151905a0307a9bdb2cfed2257fa22e870dbed16bbddeee42b01a5f565d53ff575e132c121b47cd7fe81918b9e37582cbd188fd32f5d8314264f8bbdf387ff309e643adefd990b5697b80841bd811fce8c68368c77ce73783934fbdf438d59be63f4319b3b624c9f80d3f48b7ae227ead9ee4082d20df6def1cadf04d48e6450056a5e795046819b83f9321b6a1643a9806dc838d6002d75e06ccb0722fe88074b2f00da49286373095aa709745d568701ea29eeccf0389a4e10d4ca72a9c9848b35e8279e0b3a58a5d20e1985598861060700f6cadd90a46d309499d642bfb8d9038c5ad9f2764a6713e46360a5478db1ad8c6c2ce78f8d8aa6c9b813e06fafbdef543cccf5f0f3d30bbca8af65c00d50d3d96013c31026aad6d5d5d40cac9db0699a3c2f9e90835cc10252e11e011e96b7dcbe0ccb81a7113f824a0fd814cbede70f3f60b7c1b2e13776888f2db8d30456bd507b4ba52d434c73aeb24dfd233427d8459978b6ac594bc08d40ac3478924b2dd642699524a29c105be05c40541aafebefd5d5a5f0a411f2c41b8fdeb71142e5c3e74f5c4a8bd2f2dba2c5b3d2e5f58d615a72c2b595f9cd11b63ee31f716c07a83dcdc8222a9a3773d6115811e89f4118290b643bae8573ada47283979388163d6a63a5a2b4917de5f75d4260190f38b47ce0d9e184eb0d0d1bbf2f8d0d1bbd6b698b040e7b6a5a3b716a40bd05cb18200a40b0cc4b5a356cd29a1a377955acae53ef73707b0215d84a6064b403301d80601d28507a20c1db55b3c38e0c818e174e2f4aa51a140eae85d498c205d84e607484817b6eb5faf4b1ae9020411878e5a9eb406e4c4119e283840d4a64e1b789e9c88c203e5045447ef8a824647ef9a5303274cc40fe922344538d2510b164493bbb922d2c5d5893774f4ea4e24e9e85d49a08ede75c668b6cbeae8dd299147d8768f96b0edf2b43a7a79a2938cc949542cc5b69b4e61db9512b774f44a61bd6360c863e5adc2b67b55281dbdd74a0bdb6e6f876e41cfb9f69c9b77fd7a51fbdabc60602c88bea8bdd0efd7a10d73ce69fb6e3571fdbcbab0ed7a303776050083784d2c7d753a0a55a716fa8814cb8715bed61ace08d4d15ad656d891dff1794dab40593932820d0552476de1ee8784af57bedc2c20d3d19bd813b3eb69d961458b1517e4f6ba852b966af53020c028a4a356bd60057b587e08793e211a1cc04747ad1557379832a1579a000e6674d4f67276e0ce061bfcb7c403088c461db55b57a26c55e53e9080084447ed2fb603736274bc2726adc00bf1878edaae9dcbe2d4cbe20584108274d4ae2bf0aac460830b0d117e2c9808f5cb4af815960bbab971a7605a88a936f4a02bd275f4ea46fc7e3c70d9198d2874f4eec05d155558d5758faed23daa2a62a15e55056dde55150a2b13af285c784b97478489080f24f224cc11b161881826a71b1dbd4ce29a83919bdea89bde26b9c71baba85253574a5c7b5aaeae7aabc4321dbdaa9572adee158f8e5eab9ea48a949e9638a6a3b737f2b0fa444aee68cb4b0a1dbd5bb31f942c59eeef72e53a7a7fb29c972b4d6ed7f5c2d1d1db453b72b2def57edd35c4efeacb0ebfde05bb61471dbd604e36bd2a5996d62a5b4669152da52a5a3ee482687414e7be462b54bcac54a161a5852f2b54545d11908e625d2ca7c2124f85263ba3a28ee29d4eb178e8faa271a4b4eb283e4acfde95274c57a8c8f0964ae828e6853f1eaf8a1693d338d451cc94a6507b3d987a3da4f492967a4941a4513b3a8a539eabc16aecd57083d4148aa5bc80bfab2f5aba2b197e5c7d21a2568d323a8a559eb3748d3bacd5eaca0a0beb4af7b0b28e7658593cf4b0b014615d15e96989417414f77652cd530187f1a7eeb6bc8c3a3a8ab74aec7e5cf807a6f887e502cf6a4a97578d8ee2ae995cef4a6dc16afd5abdc81e142aae9404d90ae5c90e285452180c87fde8e2513eca4a5a5ad8a7f6cbe57223e07a6cd6d5d54773941446b39aaf32d3bf5e679c75dfe86a5dd6d5091ffb7b83cd9cfb3928e77ea8966877d070fdd7e27bb5bf6097a96f593908dbb22ee7b2fe3a7bb00b34772ff3dc7a1dc56bd7c7182cecf18073b847e62d99c357778bee4ec643f27aa1f64b47619ecc39e731ae4999a9d3738647afd7cf3e368618631cc2d65aeb7bb9d7df26aaabaab4aad3cc933d9829a8d1d1fbd329eec1d7875824eaddb22cce3edf1da2ddb93febc3b0f7ecc5cb536f8efce9fbfbe5aaaa9eadae99d5303d7b9c73d641c43d2ca66100d7d7bdb93a5b6b2d8d6a4df8fad65a6badce78e7db1b6b57d0aeb579cd40a2ddb139e3eca3811afe7bb77a71d6f65cf9d74ac4b75e5c00c7c526f0d1510b26c20feeb9f1fcfbd1db0db1d72d34607dccdf9bcd15d8767508fdcbbc1629d0eae9f5ba5d29935eabd145f266f18aa298019bef8a7bbe70cfcce3f5e6fc8cb7a9598730694cd4fbaefba6d97644985d5e6e575247ad550b1dbd5151551dbd4c4c3174f42a297de9e8dded64e8e80d82d2510b0656a5a3d6cbeb858edadf4f4b47ad96965647ad55948eb668b1d5d1dbafa33b3b593a5afb5e3af7db97543a36d33f63e2a56b625ef6a2cf013582c8489974560d1f20e79c73bef7de7b771d11c5a0b1c5d543121f4e5048d891975448da7285049e1030beaca4ec42071b392260bd1aa68c98c0641c74b128243be8c06146d8900ea629bc37945f8059124358991a3fa6c472241142aa91c439e79cf3bdf7de7bd7c8c2a30a051b5855b250b8348654a9e1e35240861d9e74f1da0191f57b827203496b26312420d5d82c4c5d40487900646c1ce96cb234a16106930f6572803990680ea808856142c310233c700c99d2ddb4dfd1799bdff11b7c41a74b726daf06165ce8bd1074e403b1dd90ad1a600c221b92f4b69aa8a028d15d4e5c36921040820b419c00c583093f70fb0653188f6f70efbdf74512c3bdf7de7b6b9d35c3b4a192c79a0b04eab54eeb7b6b1b9ffbfee567262940bd779f5bd09cfb685c5801108f6d9a73ce39df7befddedde5be7b1cdc63dea77b04dfbec13f3da83be679f98e9350b45c2acc8504fd06104c76b7ceebb8fc894cfb9dfc12f6e7f57b0d4ed477d88d33759d32e2fe881cb3f760a1034b2703074d10bb87f410fdc3631d8553f7459010723ff82c0817e9b3f76ca932b39e060e4325c3810c50db7992706ce679e0e40cf3db86f3ad8a0e7e61e3b4ecda6b8e1000cf38f5d210a8a130e46d6c2813e066ec7862fb0fae6435c7d428e70fab16691bdf534e54fde62040c0743cc8185c248e160b42832a2c2815e973fd6061c0c9b89aa1ce1604cd0c355164da3041507e396f36a61d77e2e90d28a838175c0591bced018df862932b585d3a58885c3295645bb83ae5075bc8ef9837353c293f039668d1741c220c4c389f0dec7c0993e062ec79bf026cc9f9c1c9f63fee4d8d446f810e60d073f341fc2c7c099f03170a58f818b81b3de2c6d50e9cb1e8031ddb633a45f1c96d707722b7ee39853d67a705139d51fb20a8d9e971c0ee7de56fc4efa6cea8ffde9857bb48ecc8e56a4f1d6fae0dda9c2d0b5140c6c0aec41cd42474f23ff346259c4bc6e3475ca9cf29205d8f931d3029b22fb53977d683bcf789a125d71ea74e83965460adbf01975963d7a7aaa4a3dbbf5aba79546e55d365abf7a5a38ca68e39df39acd98176358c830fdf0c73c9923dbca754e5de6c1397e7bd1669f333d75fc4c79794a5de9dca3677a4ad98cfe4ccf28dcc33d1e63e38d024c781ddf81091df3e7e987a7831cf34783cc83bd8e8943fd774e595e3042e79ef4b319e79cf440c7b49927c7f4c0669ed0661e6ea2a71a9ea9d726de8961bf538725cccacf3d6714b6e99f6b3a3e8700fe98f7a0b499473f6d9fe3752ca931c0bbd4523a7aea6ce6c90961ff6c5964f38cb229b03fd31d303d66e633fd18efb2cfc5f5b2cfdda5bd3563a9ad23a9ad0c56bbe19c8ec7a45ae51f6d977d72fc69741e9dbab3e85c330fc6d69e60ff544fdda9c33dbb081be11e7df2704f48f5b13f972c8bfbf1aefdbd77ecd4fae714c10cca85613fd5ab3b75463b1ab08f9e5616470396639e2a9e00ac9faa6561c1c7feb43aaf70cfcdf6feece19e6c6280f641f047e555c2b6b3b774f2782ab69dbf1c18c8487df454cfa29b0db23f771fdf7fea9c984c28b22d440192c3d146bcd069b4ec51daee638f77a79a7930c6477877d2d0d118bb53876d98249b3a7aea3ef638e5a9c33d18a07dd200fa880a98dc209b47621e7170f6e8698254796d38fad521889f4df4ecddab730cd2d17b7bb5f26ed98cb9bf11e5edd2a1dff54ab0a5e3106500a6f4eb27f8c0b982e5c17eca51d6c985e95477741d3dd58ffdf8d5af5f4113a3e3087260cdd0c29a7175f5eb89c96191a9c2d2a57605e1bb33fad78f319260866e3de9a2c40cfafa1d19dd7a71c40d5d4920641fed411f03079a3f4fd78b907db4df668e77e140f3c697d0790bc0db0799e77a4b7280713f317009c02d92c010f9d8240006d81601af5fd205f6d704a2fb7f7707b8390d6686bae565cc9744bb1382a31fb558de7020fe883e06ce8a343705d8fea6832cfaec2deea603ec473f9a1e9079ec2d2e041c6e0f5e323437a0e06683db07fded83de02310ffad0ef9879b301e8430ffad0e480f5053ad8fc22f74772ccaf41c9d03c06510516d625a09666bef541e5e0505a50bd0fa57e348c1aa1b88eec72fb5b1f940f3ea830586bad41f008cf28cc4772e978a42b7feb3bc2353e218b5881dffa90cc3802f66bd8b6c2a6a0bde8d667e4087feb83a2fa2fbaf51dd1fa68f8f1112a1fedb41acb0209d128ce469224c9d90c090f9a27514595eb33b0f9eab00a38f36dfe85875da1b8c68c0051c6bef5a541bf3672d205e79ccce092d66723b0868c7471656406f79319b8652eeddaf202ddfa52a74fb32cecaf5916fc5b5fba74bffed6974efd4d24aa9802824e1df5f6e62ef8d451df5fecfc10db280803a402880c153884a20cf540a462478d8f76ff758382321d7feb2b8203d45afb8ae8f85a7bebfd4eec5b5f91123eea8dc4f1a32dbef5b121ce32a59fe65b9f10ab5f8118edc78c1983c48625ba1b9a70a0905448441dcae68b1b085a505f102402f5411004419d6ff63d85f9e4b7bea71bb016e6c2187f7d1bf6adc73dbc8443a71e85a76eb8ea663de6dd2fecf341d80f07e1226c64fd5d77bbab9dd6cecb8ecbfa6bafaa5e25f52ea9d7498d9abaa1d71a66f9f65e30e6a1f03b359c70ec659e8b838c3ef679a775195c2c1555a54a45711e46c5541999c1fde2388ee348e6bccef4cc45a3696832be3432a3259e3e32ea9dc68827826654a36b6a32be35b158896aab2a15555d1421825655a37b23b495aec2362c8637c4da821a0433be6038bbb9bf461c01067dec49e4b00de75197a806fbd897d05f36dac626e36b338204893197b38f7b780bff700d477dec4ba4d806824c1ffb9b251c100727e38b0382be44897befbd9783b1906f9df50de6f1782b57ffebbac575fd5ad7755dc11de6e48cb9a4f961188661a8aa5895a93b16aeebd7baaeebaaaa55aaaaaa6a18134913b90cf163b1582c16eb71d9fab5aeab6c5d63aa5aa5aaaa0a8ee40c8d88b21c7329e2afaa4c2693c9641f849d1cc331af5febb8e6755dd52a7554b3aaaae2efc7f5fbfd7e3f9a10b411359b1327c69b97b047fb60582c5cd7755dd52a5555d5bddbed76bbdf8febf7fbfd7e691a95a6699a92185554f59a0d8e8e37710ff7d4dc6eb71bc8fbe0f75b7fbbdd6eb75377aaaaaaaadead775db1f9e38de3e8476cf3be56cb341eedd9f3ac5399c71e5ca201791de4cd80bc19c823b7ec17fb5ad7758de59daaab66b43ad32aa9d5ac55598855bd33f25eeff42ef3f5877b3617f9cb3fd92ff60bf11ed3cb4b718f1895a6699a8a26d8d380ceb91c847d4099898e63cc4469b4d04471704c746707c54741141fac80848eee9ced893af6e2acc1cd756233f2f72b8155b4d6da12c4368b79e0de7bef309c73cef9de7befcd39d745d121394971d1e0025219127834d9157828eae255828fa0212249d6236916111dba7a880d01622349a737972b84748d9444b912a602094c09e0e23292831399a12121424f503892b21710c8f2bdf7de5b6806a10b04096f19e1e64092201d990a0253ca41170b92e302f99286f6511317dd45c79b7c5b0c095884291794642889da21089cf1a42b3ae282bbe870e42c05e84784a463e8898703ff9801ec02c2718f9a52d9e73ace973e1f3f070af2913719a2cde39c73cef9de7befcdfd2d66b4b7bff12028e0f101294d84b840c3470a462466201d75d1e90848195a61f5e168ca0fdc5efadbe94910240b8240218265481057b480d41cd112662a6a8c132b28e02e040cd756102849a89ce0d40308520640983140a464a1e2c40510c9935111d2e6555d8ddb38f8dd0af361e9924349d68fa6266157d8802129a5261e32b67a260c41410f0982a2a949892ac817103f7c70c1439466e82f259099c19744244aa9090a8ecc39e79cefbdf7de527c443ec0a0240ce689c90f214d6c20e941c406a3a4bd83eede5efe0602a28477f8f0d970e2a540442a694811a1a4985d53179ab8462852069cea0dedec19d80610d245dad163462a65c9863486c40416748f98bae8a00c39c1a205081c36869430c46be886fd9d00b55445d483912f1e5cfe101a84a65c0ba65aa937b482f0f1648b0c9e3523c61226526608b75ef3de06e69c7d50f53e1597252a19a8bca8c046783283c8931ce913de93a627534fa2829e420d8837bc1e2cf5d7e5f8c732c8aa4fa1176fd093de1a9c7173dc7600e125c39c679c9ba610d1d438df6ce07fe8fd0f65216cc23226fab0142296e1bf1e946d6baf99f9e7e50a6cabfa6199bf181363368ccd50187fffb15c218aa21df10c85f1f7ce40f6b70fcb9b2ca64bfdc1320330723f9bdae21e329cf1bd4df1fb8da52b00cb2f729acb90e4158ce1dd7baf55f3d38e08553a64a96460db10a49c054910cd411054816ac86b193e67aaa09f814c05a1054cd285a668c8a1e7c0bff54df980a7448bc53b42b725f0c559d66a9db34d6129c862b7fcdb47637ceb31c8f687206827571fc5416b61cdb7640a1f99d0c9c1b9b129512331a2862622044d9f9991a3288b6d3254f96a9d5f506948fd9bfb3edf8c330f571219df555512d62fe86d97785912462a9773ce5a4b298d229cc8990a3319d6ccb8a5c766a091794989112115461369d4d0bef531d93142c4b73e2643484cd598589560a2a566a4d168b49a1a265e374cc27066ba9ccee3964d8298a88db789aa33e6e4e4e4e8e834a1628ef8d6d784eb44931f7642dc80e00408c69d9d9d1d32831202084817b989561a01c9826d060b8f932f12d47ceb73720300c65b3a2983a2c4b7be29a214534b01207f6d82f14e4d89e08460e784597a133a39383736256a2446d4d04484b0cd6874882ac02cf18223822561749c523921088aa484ac938e132ad830b168945b8afc0c4326bc68e848899991231566038706d1cd0e239b213a4a30992aaa994cac6c9060a265a7b5d69a82315d7feb63520393309ace86081ebb10416cd04415f5262a8d99265486664db878904d7ed831dec8213a39e186cc49ba23e684851aa1132c37b8932f43dbc90d3b4027658af41411529e5a3ac2535335eed4145defb4292d1ffcd63795f5d1ee23d8023e59e25be2b464e95bdf12a325ba6f7d4ac094e8a0e4031f64b21efb0dac5d896f48fcd6a7c40bf641c99492ababc487858060d207c119200882e08f26a25087900677a8b114d284e11dba091dace7e8503dc91a94e42b4c122466bcf762cc138db4d6201855c453ce79185acd2c91c964a298e497646b4cb2823706b039e0e7d817f176154a5d498ffa2234fbd617d545c443823f01a933f7db8bbc040201f4e2b618e31133cc736dff9219d8b818b18ddb14d857f043476b3c7bf4454ebfe696851d710f8fcd047e36730aa2c5a2f56507fa46e2e5fa10a32d6aaaafb259dd30ba33624f00dc83e260d082a6f4511c9c9da8a28f5a8083a0d8d8826754f75e8ca3aea2bc447dd13db2d805586b1570ab751085e7a1bdcdef78100790531190d308a2f6fa014d4254469078b5d0e584a61774d3179ef7575fee2f3741109402b414ee28255ff6ad2fca298c41b4f65e33ebecbdf7de7befbdf7de7befbdf7de7befbdf7de7befbdf7de7b2fba818fbd05bef501c8e106669d55d4e8257428ae216ea4eb30a2e087944767ea4ffd460791fcd04d80e84e1d878efa5d87922123fcd60745a34a67ad7315adb5d63e1be11721dea5db5a6badb5d65aeb6c0408adb5d65a67adb5a761afb5d62a88ba8a25fdf6740a88dedffa527507df7b8d4714cbba89283cea5b1b06904b48f79acd209819d9863a67af71f634ecb3efb55c5321a4ffe8469a7c281e62987d829e62a1b7d6ca96430d84107eed67e4e86b6258da0f96f6675d0a217dac14a2abe1483f2bc30bf8482396b73b01f6b3d25e1e74001ff75c1eec555c1bf6b5ec53002b1fdb5800d62dc0bdfff6a3dca3376ec6caedc972460df7c4bd088e3e635b08e619c83fea01720485903e24fb17652338f379a96d8ad9ec87a017b1b517eb99ec8f250df3e0fc18085a52cfb62eb729fbb924ff588699272c6d987df815b198afc559cfb6977d3fe652fcbae4403813ee6d86f8321c16112dc6222f8b4d3a7c122a01b76028c205a6880ef95a2344bf48994fab293226c43db1582c261399b81451c59510522ecd0fdb9c6ddae5a75b61956aa824ecd80e5a2ad1d400000000b317000020100806c4912c0992344f7b7a14800a5d72545a462a1e0705b160288e6320866328864100046000844100c42006a1e47410006e8ffb422d49fc08312637e2f9ef1fc0b097c67243cd98517bdb757979380488d44fbbf2bfac67a49e2fd61c73c4f096281ef7e695377fa4502d8c90d91af4f16175627f59503ee554afdc088604a277fd4231aba136b6f69637d4817448e83360478e2681230eb51ff3d06bda109646a40ff044464e099225e80e5efba5e6b0e246c4204dc9b65ae99f1bbd2a268adc7fc1dc7394229717a940b2a944a917d6167b05dbeacb04a91603e49a6e44994c7f55c12837fbc94db18909cbcc5df348d64800fd7469e84af47d0e4b265b15aee96dbdf65106fd470bdb0aa8f537aa839eb7a9843be249d55f3588058f951aff61295f6527fc2fa2aff035885538e4cc491640fcc1dfcb376a73adfb1b30ffe5b2e675b86f830915313c1a2886fb0a1308cfafccff2b30e216372bfd475aa2c94c119cf64bf526615e83ebea53ebd40cfd609c9027d517b4a8a5e6b03e905a2af1511ed63cadee9ddba9f1cb6219bce23de497d3c447416002b438e32d0ac577daf2dacd63e3eb06ff1f7db3d2fa0a300be94bb95aa6e43d21791a438682ed65de976238454bc4b79521b7604bef3850d6c476c16d33edc24ac6784d300fb32342ff55297c9127f563007304f3efc57a180d853f46a7d12b8815e56ea80c095abc6ca08bf07eea123e261379e2e004e0af3a0673dd1ffe9bbfc793ec118d7b51f00790562ea69a474564e6a740dbd1763f608c70b8469ed074eeeee58d3a086f6317871d7ec1db6359a2dbd9461cf62b5fb87f09782308674d94a6e1f13d468fc1f449187c1c51a61e50e711bee1030b27fe322bd347070fc223582793f6a3a3fe07c446201ec1f43fdf4baedcfe1711cf0483218a803787e832123c39df2110db41cf49a81c62d9ee17546e83ac78958dc53b7bb46665315ee6ee73edaba69c84a46dcb18011e9f9018db036645e878c71fbc37c05f947909930f254320d1419d4d5ce38154171bd962910b481f2ed00fdde73920de0a2bbab775df212716a947f55c061cb08bca3272177493d3f558fc150a17a297cb4050c1a0b91d152894b909e6d54f9ee7ad5095eb373ca385cf4b1d0a20afcf5bb81e7944802a383c2d90a7209e846802f224c813104c409a066122e429084f009c454c70acd66d663202815e85c84cdee4a26d9926e5226b0f8bcedbf5e47762b8440ccdb4281c374d665125f8d5440c1187875089661238e33ee86c0665cf67be6ba2a7ea571cdce008a1743e70550beca003863934374ddf502b729eae08013ace2b009e1d29662fb60366c0555236416538a200d0b28086a8cae3844e09cbba78a2f4bce73b1ad156d96606d7478f77c22db57bb8a6d17ba5796ee7bfd4787f6c16e53add3f58442de85efc03a898a29928e26b6c54d5d1bfaddde8100d7ab93a99fba78d03b4b2448dd3cbed30bcbdeabd5efeaf3606ba0d4717bb353211a6e5c6417e0b6ce5dfaffcdb9d7fbff36fd7c2c5057ba5406633dba0695ad2835c4b42c34cb3c872761e213874cce033ae3998618712dc76bf02b956e657b530b58b1cfe395d247973b2c681c353be2088c42a1b4ad7c6cefccf8bb139538818a9661cddd7bca1bb1387fcd9df423bbc710eda7ba05effeac78e44cd8515b9eca9ac171e8494fe32a9f1c1fc162397b9a8dfc300039770d996bf595e1680fbef71084d3c3eff28f34fa2f4d498b18becd03338f7a98bb1138a8e31102d78166d0da3f0c1390307953ffe5bf2d952e1f62dd4f3d035b1db51aef4010b2cbba8f314ecee43a5710069d32585a9616e98c82a8e80e1e1509ab0bfa2a970c9b41626a6362e59ed416a6ce2b3dd1e8adadb78b0691da4e6263e9bed416a6c164e0376483485da727f994bba04223bd4a7052cc4a6202292f4dd3e8d10065a1bae7b3ec215e1512c0e2731c80129911ffa47da7326ab56cc79dcc7644e4d9ccf7d9becdce4e6729f4be6d4cc66b91f4b7ed6cc59fe7adfff710e4735da82404391dc115e39b0605f4b07ba60c4664b0c6f2d925b422b07312f60c74d8c98bd2b23b9eadb1d0c036ffe09561b0824ccdcdc35bc5b2e39d6a7b2d923e04f25802256f6617c71d492ac5cacf1c2e2dab3f29ea645ae3eb985432bc599ca1936d9e9d71b07714a83161737c1c13e8293c7b7a1b5282f062a97621cf52a39fa61a623ed41eb17ada6a13e21fca1530822cf174cc7707aa7ad8adb34ede3db96a8c7c9c3a27e254da4cb3a59014239f008485563fe4acf47f99b55a0afb902dc366651b411301a400562e022c22cc41a507fb30918c75a1eb44b32f5edb47990d9f52a07e6d1e47975b1a32ac7d1a726e3605f21cd82aea302dd37ee70f66b24453597d948131b9ab84ed27934c871c9597681f4221b89e16129182dd16d26f1b55cc230c209ee5b3659e915936713c333d06d38ba385a7472c9556581063b8cf88625932bd5bd73557cc8b4c211dc32c1148323fdaadc0f59e5c257bc305b7d6c7355756676b2ccfd8b2009fd5e6e5293ad84aefc319f991861c74ad4462efd3fb688538fd57093a2c10f68a54d2f1aacd1c7ee4c4787f603edba71d1fcd2ce47e52196cbe3a00e6687635997d78908f52a4e5093a1d2ac2e30d2a30c177dbd561f9b4db804fc43d645491c960de58048f035b28cf07b0717e830f956029f6aca1084f189b1963f8ac8d9d3ec780b17aee82c95c57a560e41083520c9b4ace80e535ed07bbe59bc05043d696e24a68c4d5f61544dbb256c347e979eee10d613fcf4ce10f69bbe83d4e1d79e6274481a21822b8ee24fd4bf722e685f2891e4581d0dbda8947cfe1e036b04e04b77f1e17edcac63607b075f94154d876f112ec59a3ba8ee33e6f5ad5924506c1373964cf91c106d08ebeed1891f81cd4700c2c5b5d7570caa9551c237b7d7a6dcb04a0426418ca169cfb39ecc16d56f8e8406165d1e553c2e6761a0efafd422166cee91e508d50e655afa575b43c0418995a8b9d038abf0d7f46c086f6580d6d93491fb096bdba802a10a2b5ea820f1215219b8dcd9c60a795a2cc0a968e84dc79e60e92d3484fe9c08176b10ac6d2916fac7cf9a59992c343424d13e190635c951319eacf8f8adbb07154bd28f437569ea4ab73a4f732e7877d482405d61d6e549677512626bc257a67ea7b512750898ec9f8388edbed13e37c3650889918bbdd7181bd25fef6241032ecd42c9fd77a536306b586113175b1f47082c1d2a8032caf53241c54756e599355f7ffcbb8c9aad8ffcc6b6ec9a39ce2ffcbfe3f9e5f77fadfb6be498b3f02db722fffaf3df09ef45fce5f7bf67addb96ff5e61e10b134b1188a95a583ff0e1014b99b58d6ca98cea207aca264fcfb6f39144a71b5bb20a916bba5661deede8d5918fc137743fbc1600af0b9e624af47eb737b83e4a24812dce01f519d4eb76c3de085a1ac1dc731f53adf344298bcc50fecae6baf5522903b5c98af35dfd6fbb903123dc28e52103b6129daaa4610b2155f10c7a1dbd56d193ee13313fe68ee30638081c2a863233f6d34802bac0eec83d7c1819c471e0029da0860727603f779ae15f6ee3b04df90202441f3405b051f4dd3026e4bf9582997cbe39eb6c875c7b69ad065ea3ea45803cf1cc16e52b2273c7e3421703f3284d2375831b9d0427941c7fda5fdbd17fb7aed64a549ff2a5437de9323dbf4391b43e178b6c4e03f053787c31dedde486f3ced8e34f77688e86fe9fb059ffa57cc8f84404cce6b712fe4cc5cb838feea377b16a522503311324bdb7013752311eecfb7640e97e406b824a0b4cbd2ed1f70f90c034ae2c37a5561b70dc742f74a6ed2d730933c64fdf7e73ec5c55aa3f79c738c822ef6e4c4a9c15ffab0ca9dd3c55fea3dc78a32f55460ebde5bdf60ebc4ba4bcfeba8e3128f50e6d894e0e52a1e155cad209426bb8abe2262c92d97baf3228342a0365e8f7ff6a23a4f112bdb57f87e2e0f0a88dde70d636203d1cb25e0e5827955f6482abe6263a08ba5a847a346054fb021eaf924795ceb71c3a3c8f2374b7ff2e6ab5520f1abce059dfdd750d2dddef7cd1f120f6dc86a690d23360cb37b559bfed680cf001992321008fa3a84924785f3b643ac2d84626361674f3ff3a09aa5b1c1848c4f711d65a5cd16cf21ed0c2570c59bd8e2b51c8e26abd03a3582a7994ae7c604bfb987edb35d93a8d771e651ea1b211e087def4b9a5213155e0644a3a3c27e36ae324a75d0d604958e4f09aeaf997e09c98bebdcfce32d99f07f69dab13f663cd9cb37a3f948bf4caa38b343566adb6a139661d14323dff528128dcbf735a6deb3036ae1467b69a0f9ae1b7093508c274d506ff5be92ca174b0b79d53b97dc413c9da2d3d2baad3688f2b55a1a3dab33ffbffee94da063bfa05020847a610d49bb05507c096f449d22d7d704c0dd8d271c10af2216a54c44c0d7f3b50c65dddecca13cb7c2b38b2b629d5ffeec4c6c2cc5fd05964707c2dcb81a53c309d42faaa20886117cced9eb237ba2f84540146048e05777ffe1261914def07f9dff33c1fde8d1fbfa561597fef2d6cb537c140cdce2178106a96f2cce732247c2a868baee75eca8a1570d3f68831672934008975aedd6535e299420d25bf618351d2fa307c4330a3c3f632e9c1d1f08d2023b9950c3682e8164791f8668c22e94d66d8647ddefd9eadc487eb34c3a77b990ad5b3e13b0da18308eb68e231d77606b7faf1fa5b6ec309f18e859ca6a59533677706d96a21bc6b3501d9fbefc55c467b679dfda25f2a676fa7ebeaf073cbf6abfae09dcb8e0dbd7c9fca7b658d4d1c653b14733f2bb84c1d292e0f1d801a82e2e1d9298fb02e366e3a18910b4c8163daa1c333527f1018ef7b1816cc2bffac0548c84de58c9f0f081c36abc14cb903d180a3b323b9c7d1f2ee828074fbe9dc0ce37a3f186d16baf45f4e29d9fdfe8d598538538b5a3994f33e95f3ef19367c423bffb17d5f584e562d425de5f1b87429cc6d2dd810681a0ffb4fd9a6d4f8a9fc024c5d6f180d83f3f2b4dd67128df7dae2bd73bd206544ec19bd854b87fa997087287917e638773be80a5a23fc62d16ff81d397180f58c665bbb424a3295b7756a13bf0c12d2ccd14cce58671f37275d56e7f83aa01d73bb67d6fb844e684d9b76725afd9af2baa5705ab390dd95bbf87a1c1515e2351bf564bb4753a080fa69f631b763407e5688f5aa33f754ef7bedfa49d02b6a65fc8890fd9cd3b65bfb65dfb4d8885291af121c9dc489e8e7398666f566395223d372cb08675e7a6e1cf1409f65f2b9b971eb5cc85b052fc93533bf2ff0b14c766be355ff9f05558bae41ed5f82ae74bf8391863e7432cdc9785623fe32fb8e680b4592678715e9af9c1bd97b33b5ab4313f60285e9c273904d620259cdaa836a59900217dcb22aff5b9182a1911a1bd6a5f9383be5f6f11f8273667063bfe078135f9c4e151cb6a5d63d08ede11ae38077c61464af840aaa51985b83acd3194456b29cd8584768a424b694ef60c5580da07f5fc1b557eb70d852e70e46a7ec2fdff5b9277f9d5b3d859e713b52f39956824edaf48f82a1a7fe21069d64e13821fc1243e6ef79d3541983aa9cfd844cd49c965ddec90acb424c88495d88fb259eda191a2359dbdf889cc3187fdce02f3ca86804ee8ab5acf7173099d6b02c842abda5eae394ca34587095aaf1c6034b537085abcbec716197fdc12b2d50ec889fc78ac06f0ee9171d5901166db205fe2f13606136d75c8fb0bd68521cae36c539dbed354c901c48d1a4e6b9ec99f02bde1b8396f144ba16d6ee65e0dcb6dffc4e3c157ad401424d1f669060757b096ed8a6b46766e1ed6750cbfb69b9f90a013561470052f27f032e4cdeb34805ce484cad5e7a2a80549d236677d0907cc8e0764b9c962523f74fbe02804d6e03f51f247136122a17a7596484a75d048e268a31d0cb5dadbbfb8b4cd2f1ac0056d31fddf5bb6e224229491de389c4bb92d57a6c18f956afedb33c967d3ef2579f32757536ca8333c4eb1f7a2748f40881b2e5d10c1d0176ba0857aebf1b97db941e6afa914422c526767c98fb0a60329d2bba62e24d0366bbe4e37fdb13aa0c6a8ad0c237f5038ae3c34256e57ea4f404efb6dab8df55eb99f62d88b91044f1ce7897505070335890e65703f3ea8c973144f92fb5b7167cc772c04ab6eb5a3854ab8778cca87c4b1c4b39a622e642e48ddc7557198b5aad176a3ddbbea021d9fd23c563278443636cb7a642b4fc40f3f9e8071b8364d7f37757f368a490383d903be7d672db021538a44860f064bae91f00d43f14eec002621ce617c178321a7ba967d33424fc201cdec2a5a3303f6bd8e5ba32201c2bc0c7abd6d9c08ec1e2ad0876420228e2653d1d58cc46bd37c2341ed3c41dd85c294d682e00cef89ddda072e1c2f75bd607d57e06dbabc5e68e04cc88f54cb3be648b4443fdd2f47d1a78e69dafd5161a6e4081013abc020b39e610d9ad455ea056ab9c234103c1e7708be0765772d82e67c6fb54b0803a379af00ea5c7169bcc2b1db25b1074f6529c8a92f2e531697ca00a35751a07c0f81c5df7ccffd4e64793c6f057e88616331d91560547641e2916cc703794aabdd67065c2de9ff8cc64f681cb1334d4231ddc41441721fac510d2d949e7017c4a00c04de734536311032d579eca03c487c795c60ae0df45e2840ff7743bb05d474a12e2fc2030ed7ea663b4d05a85e8f92cac732556bc0f4f4d552b0801062f6f3dcd27cb691051a24458e10bf381d23074fd80f18a8dd60226b20187cdcf14f3e32ab9e4b8522f29d03686e1c5acc51f2c4ae82d12fa8684df9f347c98731b4c326472ea6c9ce5b5d40b1990514279598a15f8ad155dccf96091458a2e3164e69779c90843f551681a79183529e2ca7b6ba17fe82c3ed65f29e646770a8c4ba95621be454d65c0b9cb56054eb95e5118d49cf44d0d5b7d08df9c57ab6051ced0acde3e388c066705fd474b25efc2aa6ee816e942c69d034494a6c397bf492fe7cc5457c69bb4854484e21a7e86accdc2597a98092313aa5115bb0a28727d1ce71ed4c583b40e2abf6f560dfd51d64f51e55f19496d538aa6c9f9a9892b3f7929a6ff7cd464974ee2d0ef2587b22cd68a39e1da7d6c5e8335e891627223208401107e0ef377ce94fce3ed253abf13fae146bc51c6c4bd884e31b2278c5886e154331b3b9f51981d4dc1126a70208a3ca781a289145de2f9cda337f14f993db17afd5909d6e53f2608ac1e7e080b2d30fdd59cc391032c6a83f2ce413a265403db96ce68ad3515c2a9f8ff94fbb694c34d54a0714a7e42ccfa0c12a86246e176b816a8418c21083f1bf6355d9496da06881045a42876e44fda61f7746cb79b3a300b19ec06ac043b005bb0eb6222ec641c80dd8d6522e7645755e26f57e462ce838b162d7ac586c8f9db8730b749039b2231c25d0edb336ace2d600decf40d72a2f6787903b4f0973c4dd37e4bf5530629cbccbc8cb4b0ff918dc752d2aa8006e669b5bc080e60fdd591746310e0f5d723d3ecbd7df2d36164ea30eada813762a1c6e385582b6743b937dd558cfc3d1d5a7e30e62c48e8c4e26438e09c9e32b1d790b01370afb9953c87cf9acc0d3b6678cdaf323cedb63153cb2896775a44336580b8a8e05d6b5500e673fd5ca445f8980250ed2dc64414d1991ab4944470193455ead75755a292f919a556456d3c70977fffa95157af631be78d94852a296206d03cc2784013078333c6488e2299ffdd66ff37aa6223c19da462a2b5ff858d83c6cfc1bb092778c6248014fe58171d1531be4278c0f9821603cb8f48e50d9e82046e24fea979df8d4c3e168f91e49f85f15c4b0a1236085ebfffa767672507b663f23de57e92778b7359323393e55fed368f1db819a3ca195f24aa9430b51ff522600667f0184c7f4129f59411f001409144448c3da41b2c3ff8e9982cb57a68b7d0ae45325fe01ab3314e7eaedd41d24a357559908aaa578b191975f7828cbec87839a9222d411c06a10a51157410e12ae42288f22720d3e11e48a74c65a9b81ca72778c23c5fe7d7fc494cf4f51561da494e5efa6e7d4e0089a6fdd8c4450e046145b4738314277b4e69524a46ebf2d5346297ab0588a04a4cd647224aa45cef2669ca1cf1cf234e446254a9ad00d49ee193df4bbabfd7a585e7c63e20fb0a8aef5bb17551b1749dc1c9bfb66f9ee1274b58ec8265e2cd288fb486bbf405a5a2ae2207b81af4772749a5bb2422b12de828e1f9abafa80e2d55492b630acfb76f3f6c610a2d3e7a9565b227df0277d853a4656a4570a1c7035647d0e512968e9e7b88333a1b60037d1adc0d0397aee6ad8f0aecef717f53c03b661d6e12775f16add8ada4d37b9e94f88a07cb726603d691819dfe7eafd050bb5295d330ae7b324bc5965074696f7c1bee4709d623a448d3d517ed15a7600034adc86a32e9dbc2f7a1f9e88527baabb0fbc019dec498808bed2e0e216c1b5151ee5c5bf0e94994a419742f038b39773a214aabe9b90620f90f87f2ba105d51f29d48530f750e29026418fae038c24cb7e129d2bf8d79998f0b4b70048b3b6c5e610356c7356744e5dc4c3fe67a3b19dc5cdf57a0b77de7aca6ad4d11e212489162a44039c40ae97798a0e170be3476e8dac599e8f1f1ff8a3a81bc7435702d7779bb439586b1581319b1d0574c70ccba8689175b714dfc959eed1a86613dbf07c71c5694d6cbe369e667b6f720c8c99dc8d1b1c460f2a68e2824dcc6e26deb1eb5c9d8aacaaf5c0e2a44283d8bdba2e5336973e0ddda6969c05a6bb45a4517aa7dd1c242bdfb06a3dddc254058fe68437f0008b65cbdf9c3ad46817b1b3fa63d4a4f534743d7d65d7d244aa69e79f842e4c728aa3ad5da317a84da03b251a1183d9d7f478bad46c147cf5d8e3eb46eed49eaa76c91484f6b13e1affe0968b1805a17d8335816d455cd6e9980d1a388350797ce662c190d89bf495a116b6b226461f5db21a03d461b424bad123b5a7cc03e7e86893e2022b43284676b7d131755496d41fbb2687b90b9a8459a4b8eb6b8a1f053ec31bf9e11d55d273fcfddfe680b477739b078c0d03e58d04985a6d579016c572ee147642c8439b6434553f284abf0e9366bfdf1543387d87246d233c76f092087b0d90c55c07293928a373392be273713073892efb7b9b212611cd6e663b2127bce39fafccd662ff640ed44e7a1d61b4e3a344a5ccc34b1651874c4dc3b96bf343118b044e7173741357c5a43c141f1d73611cdb7e1c6de6e4e9443d1105fd3b69a4296aba2f2e8c78c93f3c16c1e2dc965798a7243849b4e969303ea2344fb01551a15986e5b662a32f25b022ee72bb4f5b03525bf2671cede714ad62d64963a2eb099cbee5bc24d22560b3c6fff87c9b1ffe39797ae23b0d7288a036275585b887f58e62a3677802fc493a203165c17e8935babdc3ecab7093ae576ce7af805616b0972ccaeb78389663a9568733ca9a84a8a789371e9bf33dc3c3b66a15a988a441f9a893f1ef373cdba8f62e381f1aee775e5d1b1829afdbed6e15c093114a334428be656271514a2675367bfb33801929a9867c9fbaa56a16b22addbb3b5113f3ca2faec193f4d521155665bc3e63a6d8bac229666c50d3b5a4fd74a9d5572d98c982a07a96be110396cd1611001652bb9b62616ebf50fb10628fd91732a3720e83322000f10cf8eeae77a1211255b7c134412d454004fff1cfeeb204cffb20dda4236ec1708edeada2af7ff860e025d43c4940defe22079b0516213bac8cc3015521e8f3eca68819e2338e0bea797890702175d86754a332112b6b97ea80adeb7fcd8ee4ee64d3e6a912def9e70c279abc7da97a7a6a61c0157808b5e6d7cc51482f10508c543c4b062681c4689fa4922803abdacbaa8b1b6256aa9e56c81a18819289cd3be95bce0baa9c878031a94f1b907da908a646c498bdcb75c307f3a465fa010f75eeae118ab5c9485b5519cc8e57e66ca17ac24c539f7836f1c0de0f6b670187cec19d99098dc4c48f6d9d4610a1dbe1c08bb293a51141421cf7af2389d8e716dc034e450602a5b3ab02906ef8cfacb5967d29d9f3d990722091616f4c5195075b74dbe883261b99f8369de27789c2410fa41978c84b5ad86faf66e7da3395e83966877d4030496ca26868ab761252228757ae69e396644581cebcbc6f308c5806e6f058a126620929089c0169168d12a9e6ec9daa5f1bb9c99c1f01d621bb9ce1b8c5d25f05fb65c295e1ef62cf1152e41a7958e953916c0cbd0729afc496ce34373b6143ae771a4ec20e7d4e47bda12bf5586b80f6f2fb18faf644bc85a70e1b21b8795a194cd84d1b8ce4368b5a7223c7496b730b0ab411c04b21b543950e4b8e40b52e42bed8376e33adacd304537a21b55c2db0dc2f5ed86b8ef3528e06be81cbc404f6447528766e3499941c5c3e44ad5398dcc2d6646ac43dc4151cda488f91bdf5a2ded4064811b345d603a88cd04855a73b89b77a025d987d9ccd47c4f7ff8eb5bba1397eb0da658fe7ac7f652e83871730755b9ac42d5506152886132f2e1b1b948a41975948d8377d539f9f692c55f5158a9ee0308e656c331cb0b20b3cfbaa36ee470f7d67381233d94f193c626dfe1bc3e535e5064703906370b143c19c98f73229964fda7200a60df73cf5c4286be8cc471a1dd3b96e38d868d03da97d6f3b60f43aaeaf54b813e2682ad77f53a012ece273980c68ceb7ef33e5b28fe1fe3e10188049e51906d7d8a52fe01ee46b01768aeb3bf4235b55a58f92050a8aa9dd0707516dd1bfa32528b8e297387eaf98289d61120ea6f7114d44ee61ef4a0f45948f75ddd1348164333f3d048c8fa193004758f3c025599729ba9846a4e6d30be8b7b2a4b28928022f24f50bdb6a78278e85455ab614d0cc66e4432f12ec64abec544df43458de14a49a8c818524a296e7955a5e868cc880ad5e0e27ea081fac8a024268aee4d22b16b6ce33280a57652b9bbd7207c946f8267074880f0f408de55192d36cd90fc0807e59184c208cb7f9530745d2b2136adb366fc6019a3c16aee302aabe81f137a9431385e050a24916b4960ac98356b4cd27a3dccb1ca0cec35095532fd843a2f6e22929da897f2c8e7783bc1987ede1c7e9af039652b154d1269396e6ce931c1079a1bcdcd8fc3b36239efa42798dcfba6523086a0dda2911f45595ae6bb43e41ca6cb93d35ce65f1320b2b810483c29e3a3ca5f19ce49c6d708345d85b51bd69020652c66215ddb420dbd0d5fe9aea72e13754d76280f5414dad6db57b972079b93f9d44e59ff16676d66ec182b8db41c08f501327ca02ebcfc86540a99cd24f6d9d1416a863545e421e204049ad500ff3a422012a5f4bd8fa3b6c4a8be864aa803b3cc8eeb6bc4e6a4690b28433456b7f4af32b680e573c9bbffc198e021234b4d1364c482b270194aa9c829ca740e4d81845b57b5d96d4cebb6b7a84c60c214c0429d7fd51090884bfd81bce65233fad1239fe4ea8f7656311b23ebc67b074f83433065c968618638635548e8c0c2cbe12dd3590b1a614d9eebbd47dfea0b5cf8afc2102dfd879d15a150b3742a54666da94c3313298bec36bc9f17f504b0e6c4aefbf2d52965b96babecc9bf7de1fda17639da36115c4e49b9e7040449b450630b7af68d4edb83e6b3fed20f74373af5f46a284c4ae7ad9b0b36367d98a5144eebd74fa556e4c2576e2a6671b790845c335429fb8c3a921cfa7ff185cb02579ac583bd540b38f0bb6f9c4c3c812c604fdd8a29db41ee74fe326cb2e1faabc6857f453b6d63a47626df3ac9be96861b3c0369b8ddcb053a06b28b1a527c3003bd9874d0d30eda80490771af423b838f750d28a055565e53caac817771a6ae4bbc915ae3cc2d0a229895e4da723ccbbfe35e219dc98e86c5572fe90b96f3793814819a7d6e535f51dc11c49fc89161eb5d4197a4cf8c5a2bec838217ce813402e155b2b260880c6dfae570b45042425e093088d3a53ba2b8f658ce30b2833c1bb1ec38a5b4d3f28d8ebb50e21f817215c7b3f8d03cf6d35a4c02bb321c431516419a8018f29e448ed61c26d21f69ed57a2ffdf40ac8f81232775ea41a19e1dd5fc2585b0b41de66cfe55fde106a0c71f827f140eb5d9019ed8d5b261182670605753536cd5db6a411c210147a82416d6e4679cd822561a5942563349a23804840e04b98428ba6a811d931e0a330b1f1ceb949bc5bcce03c5c2136c5b55f26df9652d2519cda952ba878e484860a973e362dfba9050581461a18246e76ace57a1f128ec7198418b37df1d975655d4c37b8485c564b4bf435c84b5f0f3f6a3a2c3ef23d8f6f232afea000d4962443aebd4ba75b6508042c24fffd599056a95964aef3eb455a2ab2ef59128c925b3228ef2729ad4614c73e1e4f1d2ac163cc39ec16289506e079ac9241c00f317b9e5fdbdba10ae93aad6c7d1f3ab15365f7ff22c980bac0d154e9b8d1fe14607eb789f7d7dfa0adefb7a414633bf3e15ad4bede022e0693c5c180abe41b4fd3afef4e96e0570d7d0c791102005f0b1279771fc8878154d639c49cb09373b8733f7c4385939d4d17c0e68c19ba83a702212b4847c1b918fa1cd3eaaeb8475125d212d9b74b938bcd1849e02fc0f2c698c721d54f2a21fed88685f9f737de5e6e0b12d264ef08745e4e6ac22114e90f7d221007a8be2b1af9114a47cb1fd4ad0966c2aa3007108de68946129c92ec1be8bc7de5ad819e1714112797ac15a4b93b869134d3c959e44ed82239e01d4885641900f43c44ead74cc98ddc528a7b8497cfc439db413178ada18f53107bb28702d1378bb0e6435e4f908ea3f0ea338511a88e2c8da015bd772ad1364310a6326d1b181be477cfbf3c69580712385b5caa7c386b9b54601cd6f5fc9c4d19a3719623f3eaca041e8cc6cba0fada6a708c03be4719eabef29fdf83715cfcf2840b633f771d48ec824cd0dfc1a5a5681b47237a8ce384722703387781c60bd40887494785b5054a841f603c2c5bd1550779015127f1f39a8157cd1c315b75343f1dc1b9d982b55f4dd2d2b31c809e366e242f17774412aca3e233781d1f0adb7205840ac90e26c9530307687641a6bdb27133635a3de863f8db1c278e14cfcf9dd9aad3697fcf3a41746150edd7ea4728c52da708599d7dda72ac79cdaa6ebcf558e31b16d77cb93bfa8a431be6cdd6ac79af420d11fc466e2d3302109a5d8484f5d46da134ffe9db3c7ce438d443fc0aea6918fc33743ffb83b0d3ac6a84bd74ed89975499fd4175fd3d0203cb719faedffe9767dc836828dd7787dff24ed9fbffc7e6cc4e2bd429cbc17d6a4f83ae15617d19d8ae6ff90e799be8332c7d310acc9f2b3a45b87bcbad015dbb141f589cdf0ef02de9dd2c384f7055824f3f1ff14adc25a03e2823b91b5dd005ae53ae46b31a691a89aca3ca89ebcdfcca33cee09cad5eb6aaf4978c6ebedf06454a6b5641dc15ca8fbe8662a3ea518ab6286a40483d8210af0bcb88228be070aea2751928926a3fcefedd7ac2bdbfd75d9dd24b788fbfc2ed6f923fe03bd379b056cb2bf093762655e55326e9e10bb12ebe90c1003d1fa101d7f82b5c6f97583139b3f7d5cb8d79681542e6e12c0ac5159527d19eed2967a9fa54d378eea17b188015d9f51731ea7465c4ced306d394e7f75bfa22a6c1cbbd743de4aa9db0735e2bf2f9f7cd05f97d655a9df117aa3af3094d8a193f7eddc39172abc75b6ada8570d2a41ead81888ee706563023d083b310692bc37fe1c334f99737eb40b4d1cf350d7cfc001e9ec7810b3d1dc19369d3b63c06e6cdc0f094d3caf26d9380269d22add88b4677360dc6506af88324c29b15b26b8bbc472e1b44b8f398682460646c03f5280370c0440a382e0236a948a5801a9bb9ab109008185786dff678c6cda7010a919cf6079f2ea6ddb3a5f4dc10a41f5e9cf00deefaf9a2389171db4eedcce667941c8b3d7439b0b37dbb9edbcd8373eb1bd5df4658432ac0445f9642352afae189351197928148d4a02cfdd5f27d0813e2b09195068d38289ac87e951bebf0148042b1d3a8884cd814abc8d4be25cb07525850b92391673b0edf5e87d8b762a4b5a534a0cbbd28ea4603fd0341f30f1732cc20e80232a159d440242e46d53a328f7e3e6460e0761663f2441a06de99bd9635ba6c47a2c0bc5e9dfc91de1dc409712af638638b27a6a08a13d6d1a48421d9e2e682c7c807d2a8b96a666392cab9c19120ed1186624a19afb7542adc35b4884913dca231ca8fadb554df94b528b1b00aa461e2209cf67cd46a366bf5a9e479386e5e705d268ec2387246102e9a0095b0bdd1fa1a773781266ad75ca2894ced15da322d443032886dff68944db2d6a6e593255fbfeb00e2866afd66a9a09b02309d6acfd6145a48b82b4cdd395085b8d3d1586227669ea92ca202c1b5e2b1b5bda4bee51cfd66c0138d062d70390911bb02cfacf0f14b3f7c84919eb6d30fe3a932530eb9f1754c159d37247724a6a7a8608106b76bc784631183bd67a8cfa8f22ef85c79e484346bef320906cedebdb5242c99e30a8f1eadf18117b6d1f1183391d142c8baddaeeda2231b1b083e1196c14e42e6a59d198b6cc40975e0bf0453e0733755d62d967042e82d90cac6d6cd1b728df6bb9a7bd844ccc8165d4a3f97bbc8fcf36e0e1ac55b1cff11e795b3b9656dbd070225e3df0331e405e0382a16673835f0c2a8a75e8361a0b58c7607c4855cc0b2f70e7a61eb35453467f6ee329496911ac00c0226b08ca4625e99a7d22f2ec804794db70d342f975472aa4d015b986a7713e99d44ac2ef876dfc9398487eef0e9dd8da84113e4318590374c2c5dc60e2e767c88b3de91cfd4e2511529bd4c7634956349a550305bb1e65ac4184b7e68caf9ee83a574c0d34b5aeb168799b14e5a5c539b36bf0b694c5b1668f553d3a2b635274ec069a52ebf10b1217b1ff22f8be516628c2985264c852bc7416ac2ab06bac8c1adc026109a91d338d0db4b5c085cff666628278041b1854a9d15f1e245e470a75d0ac64cf54fe8a1fafea14465eb2dfd83eb674a3e9411ef17793adab3b5988f5bd0ff73ee29ab0cc9a1e753d152b5a83633554238457f0ee5b53722033dec41c7f7d3892312f6e10be07ebacab4c9fbab58d6735355344a5a46c5b1bf0b83f4abbd9a5fe6e019881a40ed71402de7d320403ec713141709b970a6885ec7d38358df9d69c096b4c6e79cc59d681d0ccea2328bd199b1e83e7aa208103b1ee4e7e359463e0027fac323332a32cbed6a14cba610e0480385147cd59a64bb0824dc7c1ee0270112aade3760e5b2218ac60bcab7b27485ca4180b32caa1ec350e38da396490c5f8390b7a9a7b8fdc20aada574808e7df0a5d109645542373c03075dd3e449e6b2a4e85130035524c0a500e9e23f5dd2b3049db73392b78c58f156f2b88844d0c8310b1a242732dd58109215ae4d1d0465d4636f928904151ece1531446b08a8abf9a11c416eb745e086796bb925cf00e438a1ce07bd1f1d1c80377a45f0e063e7c05c0e2ae199b49961eef270e9c5f1309c1e66ca7c4172ebb2c9cfabab0c1b8ed4e204416667acbd067364931d04bf63afb44531c7ef5709493fe709e331f668420700c4463cbad318caac7c37f05595edcd196e99d46db361048584f1d48f9601e23609e25831e1b9908a21ea7d14bc33da2e0082cf956e0a591fd95fef3db1dc74df026089a979399969bdfe6fabf09a4a7ff49cbd7ae06eeb182e01341452c883294ddee5a4531ca0497470c43c71658d16e1cbaae7f4cc573f1f585566bdcc98ee47cc40bef2e56b42b3e9874ed8629e83946d749745a8053be1117debde8d94197aa78afd5e820f072c2c650a380b38db98700c2ce3737afc93ef8d39f13bc5570e248a8f14595dd902576daba310c85296a4773bb5d3757be046da5b723c6e359209c13d76beec64edf3a7d44f3d19965784fb0a6a0e7f751bd559b8b54dd9bfa54f28022df43b2083e0762431d757362461b2218ea1d76600a4290394aa5f2499c5c62152d548a59949780680ba7cdb3679b4a7153ba17b5f3b62f71252844d96a0dbc980d8c3b164b2301a30438ff42fda8ff871c0dab451f10052e120856104b0dd3cef3ad2cca4ccdb653b7fd6edddaade8d7c26a88956461477326ba0ed5d85fd8904062c7c3369c4a4135b69528f53a2c87ca67eecabaa035672a6d36410842af8f9177d98edc10c913952671d42660a17c7635dc3ac33ed6cf1f96a0d68d4fa70ac88d01a27124bcda088eb4dbbc5d704443cac00651ea1841fb0d2f158b6a3e1e8b752b981826d83b653ceda04ec35086f65d73a98f4ddb16a4a55e55fdc664e1b4c9553582858ea15b8a7c3fad7b588c32d9719c8d30eec94d55710b1b6cd6418af91b822cd9f47a131097686a9ce335164671879d0f67ffb135e7a1c0b97f1651d16172d741aa82446e4f795f4dbe4ae184ea3fde038927f719bc6b32bc0b2d97806c6fb8791304c76471974a75c8402bf207ee7a6cee4159c7a5a11f7db4df4794f221ec888eb0ce6fc08919d23465799d850e6cfe1faa4f124b6e6087977570b7c55f5758e695eaa45e5b053431a0bf4fcbecd9b175455e4b2333340122c2a2ddd7a21b1bc4f78c198c44816188a106dd44abab91319ca8be7cce96ca8f5b5faf16b44343a4a8ae1a26ba7f9bf7398b76ac19f232c68538a9c8b416e13913f2be6306a3e614ae8cb3a7e7559773ffd74006652f727ede8245de34e25c8268e8555408b176ba37942c038a4556725b409819a7036bc54010534b2f57cb56c8b1321ba08f0af47bdb7bf80848ace1e7810d467412f2a2a4dec6e9e850abb559d80e701fec9ca1233d89a81a1dc2e535ea8006f786e3634b2e191be16724cda2bdf7de726f29a594494a193e07ec061b07326b6aa5472dbad4fa06bc82cf4b1fa01e444c3e403da84fa443344551d4084d022529350511297232051131a554434e5629d550aa1dc3864140c2430c5eb7c21139046b10903a8c01cec02e2f9f945569144b5b974a415954c71444a416a9a76aaa41954835615c5377a83a79e9575d61aa3a5453d6f6b03eb555a7a8536a0bbb0d7c825996655996651984f8de135604a332a122724666599665599665539b10420821c45886d955265db3ac8d732267a45dc919e94237898b3a0915c92084104288337c5bdae6c3dd29ee94ab95467f3986979397ded7aa2fd53574652bbea3883673b118be455efae82467a40dd3050bc28860221666c258188b85b13016c6c258988905bd52980933bd3c66c24c3ef311635d128391432fb5d56ad556a457ea5ea92b75a5703405b54e2da116aa65a4b55aad56ab183361ec3dcdd3e9745bb7d538b6eb5e5edc67663e567abbeee5c59d622cbbb0dbf7744ff7744fa7d36c8fd48a464c1c4ba3bff65a3bed3d65ddcb8b7bbc27acdaae5fe2ad5476dd4bbc5237c6d876b55aad56ad28ec285b61478f6131c66b57766557766557ad9eb627567bd6cc52ecf6ac2dda954e7cad3dd9138bc562b158180583c032e2adf032d50ae2e53d45f1b23d0813a9073dd822fee87bd332ef58b16c95ad62b68ad92acad3eaa563f76ead75e630631c669ad65adf34ebd8957ae95d6f96ad765b96ca9a6134db7b7ae911637cb15bec15679a6d88ff653eab9757ce10f044f76154678ca2a8c6b9b7eb5e5e4ed99ee449a8856a1969a55aaa56f438c59540a6940c4abd8c295535ad9e5413ab9aaa299a9a267ad01562a158a8c691e2b6ba171f0742c1689ae8f2a63a958bcbb5ddd16b3e38a8684da23b0015254e90f2b104027230b4448785ee4887640912212059c1d529b664caf4358b8463f4f0bf8962d1811695cede3e1c5c60ecba73fb3ce02b1db76974fb72f0eaf67d20dbedd30100394fd48b2d7a4f8cf308994862c99886992fad0fc60067c0c019311e3db6e00c924797293843c6a3b709ce60e1d19b0567b4f0783d7618ceb48dbbae118ff9d759e1db5b78cd6ae14d67e1893a2bc860ff5686be9d85375dc61bad0c9132ff5882f8f6e924cf258818cd3f9628be9de44d5fc11205cce630de8bc7b4c0e08573187c5a71ad53ef7a2effe2c5006ba2aff0e209d64477f1620bd6441f7932056ba28bbc36c19ad18a7ff4075bc19a66f12f623eb0069f5a2c0b5d614f4c186c710a1f3df33e79e4eb2197c441e90a9fe88a5bc119179f7a7e13064d45321127c1f0c5477d7c1ab6bec86a31992e3f1d52389a8d848fde2c5813659ef45ecc7b30efbdbcf4563c757cb759848bd63775cdb5a865ac23cb5ffcec8752b7fed19f0d056b46cff9d810d17d22d10aced09c5ecaf2977f31ad9813ace9d1c926a8fb625aa3131317a798d6b747a751e403675087abd5f7e8b9554c2bc604a2d5b75bcf67445db41e02a007c7090463998e24968ce986e9f9d274c5bbf0d3e5eaf2304314f599a8ba045600c21cc09051e3460c0e58e204ca4489baecce18a3944a4c18326adc88618913d669ce0a4c4a274001154cc98061810ea67ce8f19f4fa00211a0404ade4cf472a3009f0fbd8ca8cbaed7f3225a42a6645e449e0e2f432d99970fc830114f32d0cb4185310dceb4e830f366901cf182c66790f02002d043b2e476672ad84688140f41c0a486e8614ed1cb29754ac5208854925413292852494855e1c8cf111f8e88e0c8e9080d8e3871a40847a42cf1b3c40f4b9060092388582235648927967002921d900081a40824228091e252f51c710414522498a2c7116a606487035021a2424b9b8319e347f7751da414c25a2184a3cf1f85d06db00217282cf4075a8fd7f7f4c803fec01be983a24268ca498aece8edd1db07ce8899c5d320a889b2465f9d06619294c562511605920d6fe82579c01b1bc4024909d438d6d434edf776ddcb8b5b09f4ed314d7f200dd23f70264a69dbd45a104b33c19af6ea65ac201a91079237da1038d3de6e4d8d9361d68459933561d6f46d6d4eddca0ac7711c9727ea5e28374a27e5c5a998e9ae79852567da1190a4fb38d3f787a5e00cebfd6143a6b767514c5913f9a6c2a7bc48e9a260a6f7d17458bcad9e5cb50fac914efac7a64717bd4e149cd1423a8b09c8085ea044143bb8410a94e84113258ef0ddc3264a30196203c406880d101b2036406c80d800b1016203c40648b7a94d2b205216c1d231a713652667a04e741833461ae34cd3484d062f612f4306adf693a1c9d664885a294a4d06d9b0bbbbb16f1b3794b0a7b9847d8c1ffdd34a11d6e870f0af0b8335ed989b0186569241839aa6695a94776bef7c610d8c18dba6d36b65c3c33499682a55351b363c70363cd8f0d06aa552363c74d16f1502dbf8e83eb912c9530b4b2ab5d241168bdb1eba8d8f2ecad595e574124b3a25598245609d2c93a145221999ae13fd849cce8ca1870f1f97b351b55266d34307a3a63321bcd7365d27c39e5244ae3d36952fad0d94ce8679611b8ed89df19ab0263a9481a2dbddd938f7fb9a5346447367b3fb32932965c09433b6ce799b200351caee39298d40db79e955313c316e1931bd2a66f1cd2cbed9a561dbbc346cc31de6b24e5bd958288c6c850555048ae49429a6b454434e5643aaa1542c728a4488c8292d18854620468c18a5944db349ab4ebcd7bdae96f1b2f7c2328c319c655ab6711bc7b58cdc96699acec6719a4e97adb0acb0b0b48c2c2e5aa965c58b561a65302e2b9a0ef45a7462369a18ba8c5c5c5a4697bbbd74cffb34f6f6ed4487734e9c2f36e7f4e637edd67456bcbdd865adb598b5d35a6bed75edf4eb4aaae998f9b6e946c843092990844f3a6ae393975716e8cf6c8fc55fded65235ce3645d3480e48b5b5b6d6d6ea161364e1f2b656d3c89693cb907449c99bea2245ba44912e50a48b93974e3efae7b2848a96bca153e4cd9c42de74a576fee5614b248dc4542a39832d59c11a2d74dfd6f29137ad29db141b15f2e65bf1755e9756d5814b0a08bfa4c17f7008d4f412dbcbe23153e36ca92d65dab6ead7bf2d55ab7f5baa4ee1c8cb2d15595ab046b66c445c4cf2a65b522da996544baa25d5927ae92d2d432d2a79533b4eaf81608dcbb74b8afec0d39b529bd81f3555df54adb219a28bdda8875b13df4e297702426219329a26ca905b89239164b86e9d86ead46fce35dfdb349dcc3147aa95566c94d424dd37bfe5a7cd5027fae9525e2ddb24d1c52bca0b5e6c6b27dd90de5e923d5f431281319761fc51d4b7a653dfc87f14e86d6ad055bf016b885570cc3c29b1d7ddd8f1cc326fce39b1639a651ea59452ecb86699576badb5629b1d7498dbcbc4baa936b5de9e5aab78faaa8fcd0ebaead22f4dbb41d2720ed1e38b23f3cb4b198682b08694b9e6a4cb7b5ecb37645987c5e37cb17458fccd14251de766f19ac128de8f919a1a678547fa03def847857c747aca31c688b1fce1b07acc514d2353724652247086b653148433b5d2f8613b284a9ea09b2e47269733487ec09919e9338c3821409aea9c569d73711f2b1cd3918f1c7e3b962b3c52fb8b8baff0f615390717f44b0e2feee23d2fc4c0886ed0d152fdf651ae21561901c119edb2c5041f9dc5e1ca908ff407a489dc133883d5382b5c6699f4ce21bcf1cd6732051279df28e873b132faf1f573f9e384c0b546a7978e791f0eabdf415114f5d6e9e9079cb12e1d00f046c85b97426ffd02a2264863ddc50bd2b1d666085dbbcd50d6da3b28ea5b89234a7c8f50228991c819e19c46584c2f81e04b0cce64cd3f2af4d0e9095221909a204ee6d0e90f48233a0df2d03953e36c99b23267b2e1728af1cc32d8278cfdbad8832f5cd7b30cfef51e76d043037cfde3a3d7201f9d0e5961c1dc4040d663f114d248bf1733cd39e4a5694b99b0282c2aa64c912225caf871a697ce9938225c90bcb93850a9542a954aa552a9542a954aa552a9542a954aa552a9542a954aa552a9542a1b51d3a13eb0a6064e1d162f7bfac5c79d80e00c4b516f13854e7af42b0585eebb36159cc18241789abfedfaf9c1cd66884efa47b1a09f6ee24e47800143768c4d23290db64ae9356469ba9c5396f07575c38011a76629a494522aafe82713dd97f54caae190d7a7cb3c610dac69995a09879c5a69c57a751f975fa71a4906ebd569b8f2a55aa9ab1a8e98a356e2ee35fd0686b51968b075bb1a0d36cbc8329ae6d2269c1eb506c09f594e6d06ebf501366b250be5dda64f69b51b4ad84b2f613f1d7311d640ae143de64b2be19f4e6dc520b6b5b75779a83580ce9921f61a983d536a3f983aa61e9cb7e34837e0432fc187f9c78f87def398d6607f3a8e1a62154ef3cc7d60c73c063d741ab0e9a197ea4f2f553c1dca58a5045fca1ca34ae7f2d6932bde76976646bd3927cc538f524a63ded65a6b2d91de5a6badb5326f276aa258ac562bdb18b9b41ca2b78eb1d8da8d9bbde65a1d73975fbf32bdb0461d166f5d87c5575bbd89d2aa4baf7ab5cacf4c81600da440f575983d57e220501f09838892787a2c3e86c6a13ed2dbdd690effce375e980ff5c180c01be81e5ec7dc6db07156c7560558272fbf74f0e111abbc8039aeb5daad066880e8b3eb63b67b60a8759e99691a48a2e1baf5197a5e6a25fbb3345d7acf4f97de9944bd3aa9bde76bbe81e612f633c312f6325681a184dd90c87f33d35eaac00b9872799004ca0f5050f0ac1d61443205c913628cb285d81e349b734e5a8b1461adbd300735090cc3301e5a3214e57202b641428330312609c23e4018879c2411926d045c82285cc40a18e200e5b3874d98487df42ec24026a230310542d8b08658053ad4463032b1840913133e49aa60820aed874d94f09894076cd30208081121042554474032c40bd489a7bd022904e9eea513751da552093d14b9b5c990169254a1bbbba377476904dab165eceeee467df4ee289148228897fe329372441cb1238ba7294f1701c6880550b400c68bf9024ad474269c26e8be6e0745bdf4e3884a1cd9c1479fde0e8a9a319372cccc13bf3d1a01437903034480e108521c210ad091204e48084267544191cb24c9ca87cbefa52d3252538e41a5a34070c65b4e2f9d005148527cbfe9d52fefebf9cb2795316239837c40858c8bdca757963ca7b7788b7f1de45776f4cfcbc41825144fbd78fc781bd5349ebffb2e72c7227f1da945279361bc160ee3d9ebe1021927b1f046354ea7babb512c3c198ff1eac3c8788bb7707c69365ac8f895692193294c0b219d6a9cd3c7168af1e6b38007f83aa85fc683b006a645867286e4c197dfc283b0a6253e0bcf23c96415e46ced06e330319efdecc1acf044eee28d44dee22f8ea966e3258bbc6a0ce8799128cb1a30055397f417af532df25ae5551395aec53117c9938bd7e22e1e4bca5b791b2adde62e5e07ddc5e3de864ad7b98bb7bd0d952eba48e42e9ef6239f8fa29c6db058afd12c5efbfca014604096e9bffe3ab6feebafbf4ea53a55e3acb19d362a87442209107cca855c2981037ea6c8ccb737118756f8bcbc6aa517ad0e21a53644ae2b7a1768d4ef5c5e1d230008dd28f5d5e565be7ee5cdc65ddd855df8baa85f9b6b2ae8e26ff7621d47e59527aed975e9bd1effe6afe9903b4a7d0d5943de8b3b17ff7557d0bdadf67bbbae5d7cf4cfc5760afa728b100ebaaf65757ae92d2b97bfa6467d2d2b58e193a5404b71dec91b2e7ff139ef3ace37c79b778de8e2733524f54af1a593cba5fc442fafe8d7a6a57e689e4a2b3e5e595bf3646300751f5685e08c485da39e51c7d431ea1716a9d768cdfb5cfce59987bdaffbee5ff9e356f057ce6c9139a7166312d1db415194ce1c9fb031a8b4770118bfd39e75bc3efddb01e3af5f36429d75f8d2e97de9f7c63cf3ce741a0402e106091c618911a440048f253441c50638e4e4678810471821c4112f4022c4c4494930bb9f48c1c10f8050727a2283090516fc9873cee983227e099b223acc67a2b4d284f1b4566d8699a699569b814e194d331d576b6fa51a37356d6a37caecd266909c6bce695e43562e97b0a799bd36bbb4e916d64cc737c3305cca6694d8cc72a695209ca170063ad430ad3427acc159a665da0c3498e658cbb026036c1a1ae8582bd9d71c6730cbb22c8bf26e9a6bf96230629b73dcf44dd3328435990d09ba1dec9232a80fb6baaac0f045931eb01e3659e2e4000f9b2c996243fc0f5b714fd8684344f7333ac119d6e5d55dde44c96ae90fa5d88a7be2db6d88d4c351e56dfeaa471df27348b72e337c8ec33a4cb482336677badde9dbafd5b7cd1ffdf908a5cf7fd54939ac5f7ee59dcbadc3875a8efb241cf723e5b876ec95e1f7bc9452b66a152ca5c71863f476cc442062c7d93901534ba2441f213e10e2831ebf43f6a0072d275017bc410240c56f2728f4a0892d0c010b21b84110a2a0248806551c11e485064217657ca5940aa59e523a638cd1af135ba869e28416a43f6ce2c4109c587d742aa2d9f98baef9db011249fa4e74e8241c19f6c4f802862f3e9a7ae98eb1ce1793bf891d6a38e6f4ce19cb5f8f63ded750011d6ce284e9db599cf0c139e2b767e079fa77361f39d46060f1ce5ce49dbf16bf5e43c6b8f0d3ed603e1d73cd31e6d1d663eec203d6b7b3388ece3b7f3bdaa9a6633a56c25cc3b9310f730664ce7963d9859fceeb1c2b8ec30a06cd3b93386fc77c7a24ceb14f12977530c78e1df3ce31f661afd867d357b20b3f1d752cef64cee51d17cfd9254774970c4799a79fe513e596ccd3bf394bfe74f8c6d88b1a8ec626f09c570dc784e1d31c3b96a79cf978fab7fc7de0bb1fe7296730d73297bfe93c8d7d3d507ecffcf1f477a7a0eb5eadbe351dd8e5b3c35da7d3e5afe7356fef38ceb9cc798dee569cf3a66b5e863bd734cd3bed72f838ef5c8ee1b832066b4818ae761e34e141ea31ef9ed3a7c7791bd8bc3ace3bed18e698e3b0fa155fc91ee77df13bd726f6e969de799cd768cdfbb66c31cc6fe879eced7d38783635e8daa76b7e617e5df3341d9c3f1d1e73e899c3ebf21a7d79186869e084c3aa694f59c50e543ef606e2cb2a76a8828e22e704013118c223e1077ed8a4092700d5296260828eb3410bbaaaf2418f1fa8aad0848f268468a2044da4a00916a84ea06281ca886a072a25aa1fa888a08aa26a820a0a2a1e3cb4ea07909c826a884a081eaa21feeb5c0955c98a472b69f24309149312293ca0602aa14209159ec0bdba9a40a952a28f2ceb1cf01c78e8d3db31f310b6d765305adbdba7c3d301c07ba96ca1caf34c8fdf499a51ad5de746bd36ce6df1d583d5afa633809f2e1ec6fc0cbc5efce9beee657bad1de19c1bcd73e64bfa10d05447a4539252a28488129492391fc21a2154235d900ea853c283c7f6b089121e1a35e99c740a12302822074cf08962c90b73c9cfb9022dfc9cfe4276fd3adf01027e74970707a8e20756caec3993f810248990887bd824090ea0a055e88ce8681294900eba2b74b6874ff743571f36194a02483714e587fb8b21283ce12627fbdbef953ebd24f39cdf2e4d704eccc52865293accd78b55beb394b38894733ae6e69c9392a4973a2e51eda48db5135a25ebce0e1b87738c31ce1fcf8c894125efa0409a9661ce838d659ef957b6393fbc745788e9718ee9e600e508b939262cc72465feac5f380361e7fc7ae779e62b39f3986dcb5f96b98b67becdf28783f30e0af4169b7e5dddfee5fc90f343ce0fd5846d9ced3cf223959f75d7e8b853430e2fbd6be89719cee7915fcc3cf2a311f4ed50a4dee66113288400010504a0c7ad4987628a17c0c32650ac1ecbac6a36a4472d13c919e8c20f457d96ca0e8ac2bebca396be9e9d2b7ff1e69de86ef1d3fc459fcd9a59c0f005d67c3a9d4e93a046680e68ad34549846d063ecd65aa9f3c81d1959eb6dec513a751ee97df265bc52a15a1dc25aab8ccccd304c97f11a32dab81e3d948bc8214b27351d8a92f9bb5dccdfcb4f8a9af9ebc9414a49a308f67630d7e16794893470744ca3082c9377b0633234848821c9e44f3ee6a4fcc9c73482be7693658aa6cb74fa0308ce20f9cc1f861df3afe6c06a26617e9d62cf367f71181cd3699868108934a6d739e7c7233f1a417ff3ce7419c7ea5d317a81f195e9d46f95a5ea3b325f4d47f6d53b13cccb96cd3afd5ad5634cf1f4e69c984c0d396910f93a499ee6311ef6f8e2992c0d939ca1186b451e6551fe5e5e56f2e7c205ce28104d515a69cc5fe45c6aa5aa39c66a9cced4188b3ee130ea773c4f1d91cb24ebd86967ba335f574ddf9d91ead4b1e7c24f47fd72cda340b006fb4e36351dd9639f6921259f8ccec8b7d31022d2c07e390d93bc817fd108fa69e79cd36dcd34848806a077141357482189cf9ec55354bc0b0f9b38197a27449a487927451e7fcc31bde8f4740ba24554a7452a35d5cb57bccd390fbb9ea53f1ac7acc91ca0e892476ade4ee63a7ce4a4ccd1ae6b9a57c9e958c9f921d2905e7734cf78e4b3e858c99ffccc59f2275ff31cd3578f315797de173f8b3546997734cfbcf67f357fb327071deef7819c22feb3ce2377a4ebf0d5fbfa5fbb3958742ce710f9cf5e1d9f7ce91673abddfce598defabd7eaf7fd36fdf7baf432d477579657c123518b2ea377fb16652e6985ba73f60c011bdea885e3d07284748a451fdd6979e0b3f1d8ffcfaf1c81c53a79558bce61d16acb26017c358b69bbfa85dffb821eeadd5b12cf558a39f55f85a9daf79f4abcb68802f47c85fbf6af48ab759bf3ccc5b54730e1036e4296dc10d31e4a9144fa9c778f9878323c5cba85d9a7f393f441a5f6ceff287f30487f535db8f67c6bc53fd7a74ccad0643fcfaed008a997455f86488a752f0f0ccd79cf30fe7c967d9859f6e0705fae89a5bc72ecaf2c7333f9c27a5e83b5afea2d574641f04cdbf4dd3f2b7c352cff921d288f9cb1c7b0e90bcc139a6c768fdab0351ca8dc69b737e8806b099e61dd1693485122b253d564e744dea0d563058d180c7ca064ca4c0440bab134c3a27d531411227f811a404422042142b1128c1a3c35230420bab1d280e9ef0c08445a52082891026262622605202264598b0800912ee2f98189112634720386336160593824dc1a8c03db0104c04c635bd942b79b3e2a1632c39231d07991ac39e1b8435d15dfe8404a694ce19a448d475d396e6bdfc3a0d97d72bdb3a49ed3bd3a9cf09c4bd5d6a2abb679c93b697689632574aab5f6b31b6d6da1d8d6a77e121468fee9238dfd91c3b976354e9362350ce582d6a526adedd9b37e79c9a639a6d1ea594d2cdabb5d65a372f46902ef36b254ff4d46aa5520f59d6ed966304e92ee718c3242ee7c85ed31c670cf39ec7b139f6d29621acf92a0a3bd4b64cda9c7312f69ee7f20d5bd6197dcd50ce5cf9932bd4f42f87d5ef683cc427b4f989f0c58753734e890238331d7a9f244e3535cd0f78534f72c624712eaf48409a693ac99b2a5451006f6a0be0ccf4e9989e705a20343d01428dd3404d93552179d3404854a19f1dc4e6afbfe6e90d04c4b96e03d9a7d7899c0e054a942852a44c99f36ba09fb3337d126387eeab425f157a896b0c1fdae18be8ab183fdd77353a29a5a91435996aad3595b2d65a9bb577efbdedc5e8a1fbe42a460fddfd562b958aa668fa20eb8391758c1e3a68234695ae14db0748a37e83cee849f4e40aca77efbdf7bafccc7265c2a680259d33607e499128765d15895a4a0d0795f7a9637eb98febd6a7469201f3cb69c0ae0b7b1ae3072294daa95f1a8ea6ad95b2a78eb9dc7544c071b5127d4ae3855e43ea307ba8a43ee8eeeeeeeeee26755718bbbbadb5d6ee54976eab8ce163e3ba9b738552ca526bad2dd65a6b45ab953cc953ab954ac5f091e1c86ec86ee81e8d46236bdb664a60d8372f7246877743ce90e04d0e38836550ffb8bfd137e48deb803802f8b89f75abc17e3bf497f6ad777720ab9968b97cbf48e9f68133ba00b1d69c77daddbff367637ad32950a10a53ac70052c4c91052d6c818a2aa8d02a3e3f3bf0e8e183871e807e000922c40758236dc45c7f8035d26d44875acc15084a5b90360e1d9234d297bcb81459808f32d3562b9eb2a8646e543639ea98220100000000531540202818100a0563812448533952dc0314800a8194406854194ac4519423290aa3203084106208000022203023344400bc079fab15c1b8190d47f2efa9d1c9a4b66535c1727dc7b89aba75eadd449990a1590a8447a12eff6c341dc78fa15da0b70c6404e1641f62970fa40081a04aa427462bfa5494886e3305faf8618ac21c4f20bdfed506a76df753fa18009554e93922716c2da9cf3077392b02918dc7408490ef953dce5cb12d64319c31e96c949eae05d4e582c2390ca53364a27b072f1173a695a61bb134be112d7f42086fb8c24ce4c9eac10f1955620045d393f80bb9c45fc65cd69b57676509ead9fd19390b9007592d0d598ce7b98b3a5a83dc3b2cb654fb3bf030ad1b5a052154e932ad7bcd732b377ff0f7d389a9da373ea1d36166356a21ee4b72a53f5b85a06a78bebeb299428b3357070575ff4066db980dab4e278578edbac95e442daeaa3b639cb7d50487e6ee9fc3bd16bad08dc504f5cc973148323d168d2a080ce363d02ae153695a05904375b9b6b61dda26d5b8812674825c944ab1fcf4164a712fad8f78c08f4792eec2ef3fb164d8d54eba26cfbfcb2823aa542723b8e530e22d8fae12bbe282286c86d2423054697842ae92b61a6bfcd7d8dbf0d85c000fd23f9cc5e9e1ab5c82b2186011ddaf77250cf2a3b69ee363d0b329c1a73e4ce86482c6e2f26edcf024f132220d78d9aa1f6f062d06d6418648c2a5653750ac58f0f7304cc4bf8e855f66542d5a70d8cdab3443b5426d86e17e6aa6e89b765b1b5480a619a1f647950bf77560fdd6ce57cdd4f8d66f95ef5b2fb4597103a4482006833bdc9364dca389936b4cc16a173d8a7020122aec89f7ceca002e668a657ce8b877c217332b10d8b4e5d76ee4d5ecf791c0c81a35916d91dc507d6a698020dc54b062e16e874a8b726dd0e4b30346c1ac2511b7ecc2b5d920f7d9318263b20e0fbed6674de9d4c8e7963e2d19d2eb6d26e19ac2a0ac836f4a7eb041c95619b3594f35698139bbcc13a4a5394499967944c16588680a8b16f2c20bedf15ce3839663c5e7aaeae29a52ac5e8e9f318906825f3c364a112570d38835fba95214815a795021b1d87b0d8548fe3ced4b3479f1ba6e36b2b1523b11493673d9213cd04ab6d32e736ddacc69cea32b999b52fe65146ef01bcf86f567ad095be65ad638852c3cd928ca9bf3b46634035e401929bdcce11e19b581b8070f0a25725b8ddd26f13b9ec261f369bfa86a5fdce87bd4fc582e017f88c259f17b1b34bfbd5b44d778123283d58ebdf50ea8a039b83bdfdae18233bc2e3e5c2aa8f7dd7012d6242a254ce620c26e68ddba5b8fef8e05682b48b15756d51fde8f95af43329120f01a070d76b5cff3836aa5d9566e561731c774967d075a4bcb5c89b50ae591a9eb04dc137e88219821d4875bd5009918870fdd5018d7bd9f9323de148e70500306371c865665b9e1506550b4c454d66706a12224946a5398d234fcba1f616acf2dd2cd42b3a9943a24991f7c0730dd224a4cd2fcf28a6343125d2cbd49350ceb7255df9a0b422347fd3cf680a5fd691032d70b5c23fac4394583b6d66f2a677a1845b5a02a3f2da3ec46ad48371b51dda8f7a502b16e21309831b430ad0d080e2c0ffb8a0d400283425d0350cc3cd89ecf0cc04c7048e4043e73df59683f3d54b0ac7c4dc699652158763cbe8dbb2605d7c6b3245f0a42eb44a620f2d5001bff64309ee3f1957c6519580dc6be4f3b9a32f102b80b51ddcd44d5ad06d090e2e08a7ce3e5c2bb1f16fa0f57a1e3574b1c551632ac5983b22c105bdafefff861f2134e001cbd5bb17db6a3ad5ee9a00dac236982ed8b0d09328b82fd0786e686d6d0b1147c6a22eee11b8ea36333c88096384c154542000bec4c5e5a66c72edc8f5a9b77f0be03547dc2041512c64d24ead65787564b6136addfc72ebe163e05b04cbb73788a2e0045f6ee56edf1a13ed2c22ad95bc5a2308f11054605903b91cff3aa303323f82c2792b5f00170535a93d0b3474c16dea028330ad7b1a7e2e982f4b2fa13ba37dc806ab300eac910b81a2ddc30f755b5170be86e1e065a30ef061cc40019d0abc34557c7d43e440350e5f9b8910fa699d45904c5424693227128f0b674fec66459f8dd7d1d7df915de2931af0f0bb8b39e84bb3c389e4a88206f4de4053b241698bd8982b4835f15699e148f98593af81d5e4f85db193589197503ae4fd38589f1c757e5313c062b445009f723d6357f392cbb52818047e7fef0d514e1119166423522805ad0131f89024ac67194cff2698aa36353aac0343ccf56fa3f5ce12f741cb236a60a682840c8fb457804d95422e45540cd825d2861f9ff508785bbc761a8a967e0e095caa65915de6060a88e36490aa5f4eb25af49a57ea153fda646dd1ba13c52f1013b1ef45a67236d8c4c3655269531d46656ef7df469188c2cd4f37e56b31f29f3ffe66554c7a09d1a4dfbd9ff2d5034eeb782646484bf24d94a697fa11b98c762dc5fd74e21368e11e8a6e5bef5a9e71d2489aa734e9325e49f5e833ec8ec67494119232439fa02e4a65647ff436eaa0b512956f8848d72e52ea5a92346bf8ed0312a5b40964b0b3a8a7a1a223307310515ff65c900f575bd7ec10e999d6baa1a8ec46130524f0a0e149768adc132ff7b3645a6c69cb4aea2bb9f9c9da99f1637a03062b8010d9f43c0d5a5fb8a60c22d00f4f40387a7ef0d1b2620b5981d23fe018a0629fd8d6e43442a512336de51b7b69499186453c87dc3edfcdd61cd00158f9ff56510770e478729a0c2b9d4710618ec04fcf0cfb27198493465d9132c5b70927c2b7c421cb0e513cfe77572aec65d2162e2a6d153574fd88ae34e7f8b85c09d734d786dc91b11f12c82bc86f50d1f3a694543c2a6d772a5bd3b3dddb3fca0d3b0a8c75ee72307432bea04dfcffb28f155e200a49680ae43cbcc65e3afabf937b1cf2f05dc6ea3543c043c3687965de42b9ea8faaa5a07de9686722520061b4aa11e128d0fb7db9c5cf2ffe3c02639f16fcb6486d2357e777d7f73f2cb8e08bb757fcec6ef55224ca8c2dba8620c05d43716acf2c77926f5cc23299d3c60d739954bef45028cdd3965ea3279d9f5fe5753c4475c036a4e4353e0004381dba3f7fdb0c82f535753b79adfc24fc8a9f262f46dd26bf668d79ff0bc64f6aa060b090edbe315ea1204046b707930f3e1b1c4136375673a9458a83e2717882a4b4f675f5d75331086e832d7ac1c9441ce7f8207d65603c9f2b622e82e6f63f983494119dba6c36db731065cd1e15280334a48974d2bfac8addb20a4ee5f4256bc88b3d7c4201cb7e23558b9cef0a3ca021cb1bcd0e4539a2a96957ca63229350ad5a5a80ded3831f50907442204aa038356c8749497239d34de3e114d6a88a24cd8bbf708b4349580eb93c4bc9de425e8e6bc601438760038ea77460375b0a783102ba2197763f9861a764d702bc0db53b7b48f7d2d2120dbeb43ec18b5225ad0351d73f408d8e671d25224746b29aab7601d787a9c5d2b6fad73c8a844dc7e06b206338cc0ca073d5379bdc1cbb4d6dd15195c18cc0b8fc2253789a8093f943b69b3f26e363706f6324133f1da439129059c6fc9112080e85d42166d04594b37f39c67910ee97bed58d81bc31086849cc18fad518e45036621951a820e87580d778d931192ab03921296186d40290ab10f0709dda88368b2ab713e5e0469f367880a94d5bc8b116968716d0512326dac13b3501570659427fe29d22cf38a831a0332fe4672a36efdd274673efc690642815fde5f735fb0b826a05ee8449442d175d7c47575e5c6af10a0241edc5e5fd59f9e8001efb777ab91645626baeb36695271568cb0e8090ddee0feb3565113cc33528fa36f5db78b997aee0c4e30e43fdbb382ca160ebc55000ee98d0c17e4bacf82da2719d160e354ca86b283c259c5196d6c9809be4ba07472e2263a92cad16695473562add723a220c178a4740e251ef5671f66f141e1f0a536133f31c4ded8761ebcf7ff9f59cfe316b2c0053c740443d20ae1043d624bee0a1ae4dfc046f963b2ed38cabb68546e6bc4ceafacaf28a11c634eed60149628938e4b1daf025e01eda10db573d56223b8be4af69b3ec47ad2cbab4a1cc6602bafe4154ae5b80768ca1acfcc4d715bf189792b5a68d596ec0ffa30147561e4b036069d41a9153a859887fcd8e1ab79dc956654812b247139456a7cbf995c2c37d1955d4a9642bffa3387af0c5efd57527d39e8dd868466db38c1ab726a9dde42d0561e859e338b6191f0cb6c50ec5dd950cd3a53197574a8c494d618d8fcfd702a4d0b6fa9fd90a40673d2cb00202cff66130cf42a5d6e93cd5b6820251960e7bac0476ffb54c907b9831d81e4430c60a48e1d7f604688ee535d616cd035ffb09af84c0092e67db3be23c3570e8b91ad78f4b60e8fb12ffbcabad5d59fe7b94fecdc97d5bc461011140b7f7adff6615fa07e0569f49e3efa761628d6500026fc021092656e0cfed1b0fd2fc6d8a8ea0962a35e054b1c7cc95a723828d6c0831a19dae0255e7ad9acb160e2f12a4643d8965c06c19877248d875ee87810ed38a4adc953491964acbd02ed0568468106058f7713bcfd842328ead15de3f66d8ae0bcd3bbbcdb093f1fc482a6d7949868f2ab2ac067dd1b3b040fbc531be81623ca763c6b98e459282acfe287cb9104e7d916134de4d75ae5733a26ae8db95e1b8c50d0882489feb5bdc801352c0e092ea7e136d3f75374c44962c067163699b1a08070c8dbc1d597222bfb2ac978b22a64b915081efcc2f18d7bdfc2078c80498b3c5d295b7bc259f1d8fda4119108d8f8708c1be92365d90ad329f0e18176f07148f7000c7e0a6ea636fdf2fc875a49e2a562ea30ea35b1c746ac1c9ea3f13848c5499e568785429055f4c328d3824c6321ee4046e35a81ba8e6aa5fdf6aa27640ccd9a6d7ff953ae474b542961ae0709e903ba7cb12400c00b888ab6450eca3adab2b0c52155222d4918e8ec923eae22fac5c773410152aba671a3bce1cb444643b679752a2fae328458af8af0e14353567382a79c39c72e1c24765a686a039484d71ae29364715852f9c32bd5e69f1aa08e100f87dd87c3715dcc14abcb37c13672d5ea6533ec192025cc177586112ef1249e7d9f9f6157a103ff9802ef3b42c340c748f5b237ecf08f1c0dbb8a069426bb33233df69dd2f05d179bd83d66d235f1ed6550e5343cf6e0cb1ba622e7b07af86f92960a7376338523c2dfe69b5cc7306779c8bce05b77a57c86d54c74c99b00f9910a9d1b56a7d710b1a6860f58f3686f08a34d5b60d015d179a1f1180aeb3c7047e2e687b7a34683da07e0b5176209fccdbd9633cabedb810aa595d7183d63b2f351c26b6c8c8505aaf1a907f06cc27732741eb3ea4b9d81766b73568350c87637004455facc4e2be39c194e62d18171231333ce78339f72fe99e478ce549a99ea4ebf3b1bf52fe3604b062a110e43511334a2ecb21a8fbdf2c635546bcc6240bd9d56dee811cd415d29ec69004128d3021b17343cc39ac2d2f549b21022e9164d2701f962b9bf9a1ae57512e13e71d0dfd72c2223ea187e4b35fdccf4ecebb5c56c783ea46b495916597131e42ddcf61de068966bd4b9147802f3c5ada04295eaaaababaeb2c2ea6aab564387adad366fdbbce9c6ed9b6e6eb0096ea8ea90331c14f6a89a7d3d7c8eee0b1e2101c0b6b4652ebd3a8c1993db12f07f9c2739a18cb2f28ea91167add6f845bf3a7e49729927579130324fa159e897e7cb4599f1889139bd83d83223be707dd1f275299ce86a8a067bda38b5617972460c8e2dfc34ffe38ee42c277ace545795e8afc28e87d5dbc167906fe0e99fe207a67f35f3e67a8e5ffed60ccd844c09fff25e7310f5c269c6a70f490c644a86756544115ae2c2d2e2a15b42cbcdd3970df57cde793c257f456b7181c213783ff04f04295e5984b094427e9892d3c10df65194bae88caca5216b8842da104230c1bfb4e721d54ff0fb9e6ff409425abc01ea2a83beb70b12558ce2a01dc4608ee92ee0d505ad913750a851a714d06064a87700d1e8ea4558bac9092fa5b8b95b6ac88411cd43b66fad766a420ff9cb2a1829a173984a8e73112cfa1f5766d0680b7c7f67124cfb05fb362f7d200ee7939d031e00f28e69950f0012105252cbfc780c73b8858e2c0069b2f0fed2d2efe957464cbb5d3dadc855f67d0168d497f7cd3ac74a64f013d95ebf4570aacb6782eb836db7510bb27d1f46146fb12ac1423bdf02f3030221e002e72a16e46e6afe630d9cb70bf570a088f09b7e9a21d247cac2c85e919bcf9b18d2dff384e80db1f6c827ca1ebc3164a75fbf7e27756de41ab596685d770a139fdf76022b268e5572c9260868da0166999979001d43356177d5f8a165a3f4caf3b82acfb14f67ffd7b1018ea6bb598e5426bf30fca4e4f9593fc395c67cf646655af291ea0ef73f07735852801edbd7b25792775f7257d2c9f681319704beb53de785b085b3041d543a3a1781d4c9c83be47449bf904e91f811820c732158937fc6d47a398ec1d7245e84ce0286522e3e89c6d987983247e0cd11cef499442b0dbab6f529781cfbd08d4bab826947b9080e2679a4b56a3a676353d9a991464a2c674915ff601dda04e74bc9a087640f9b6b34997de65bb270d6089f8cb711cef9a1080c51840479ddf761b21632d2b3f44ed13f393088704cdbcdefb30ef3163154db488f3c3a80bef789d469db92d820344d896470945c80cbe0b90ef51ac4bdfd9b6151bbfde41e3e99d12275701671c8f1a5f2a08cc514356649f6f964270782743258d4133efcb071698d84e1d15097df0aa4edf708be6361b207cc4905315bcaf8d8f59c6d11d93abf28c1baf9e99f50d481070e985876a065e0bae8254d92b515f28b6519408b1f3bc800d4c91677c944f3bbcb23372cc47679e70954205ff2d9c3818cc92b7d62b2a3953b4571c7cd14be18b81d865352aa4911a2110a85fa395d8dcc4641234371e65792b7b630a2d088dcf1a0e7dda004fd5fd0eeedefacf7065ae18bc3f0796d2a47398078c2779676c37047bcba4f852320e68167323773839e58024421d4b721c4e61e7c49c6fca4ff6c1652ea0e46f3b4c116839b6caaa3ccdd485e063b184d0abf028267640d985fd12092cb842fa09a895f14ea8a5809372828fa0100fd6f076e5de7bafc611b3126bd54bf869c4cf6f69f6f8db376c7aeefd34fc18dcee0007ddbc95a1604c99dc8dcc18bae079b0bb1962801141100b4481c3dbc5c030720864c4f504f1e879f82b28f450f38d2c02b0d22e0d321edb84af4a972ee1568936827dd2e614d683031469ab3d1f49b07d29d6c6a6105173c7aeb764e72f21a1abeffdeab28897604317882964ca5f2f7402fd4ad21a0f9a096214884a50a698ea18ac3c32ee9fc9ac3281117652a06f26c0a1dadb87b5e2a30f160e2176358beb8a396693296c29fc85aac5a3cf8b0a80325e2313c8f732a9693f43f2fce3d71944b6d239b22d0ad9cf61974b96e8efb98eb716a8141c182fdc79746bc915bf704702cbf7a655d4c76f48064f0a420d8d913987b53359ffc046d01b7eca485b540ff47e4dac6e1fcbac219008d4f847994b69e0702e93250b535e1c32982a8501aaa014e0f4789c26054ce5213a41ebe4414e91ae2b39d44bca25b736dc0a6a5c007a2bc18a784198559a2a6f0a38e3fd6d0d54385e63c1d44d7c99d9007411ea47aedce6080d86a1d8071066407ef87c0364004fdbe837ea5eef216b148dabeee06ba9863b2025105b21c8135db92fad77e8814094d72437a7c6244c5042469a6b25ea40d5c6ec945944cdcc03f8fc1afc583fd0c49877c3d1a34ab5a3af1d0c2e69435300137583be092536ca998cded924d19505e793934d5381d3a042921103c5e74faf93e236e70de478e6cab0d21462fb18ed171eea2abb07a18bd03d10e21aebc788eef30ef6bbe3d65603bb2675ce8a536f4cffd3715c86a5f35ebfa59f976d61ff0e7b811ce1c5c40adf42df3aa01ec04f6a32b9ad391fdde06ebfd6ee6b0e455fa5a6ffeac9fd27a8c7a94cdc4b4638998ab0faf12d7662b2b1f59a3e937100aafbb121ebe5bcbb2170f08e8ca92e6d7d1b65ea93dce01ace0b049f71a13bd9c8098f1a8416f8483830f8524f6da1cfd9ee480a84348abed8065cc8199468845c07d83e4fe7e073006b5f38d841e5792961fb9498207f045e87e091ed03b9435db7aaa6e0b39d2c3cfb2e6127305f5817357b9e83764f6dd2766abc702d503a1d2e5a28f7fb904a75c9d909a229b5c23e94a5567cc26cdcf1247ee10f891dfd1abe0b0202148df3d650852da3cf061c48b1a175b71bfd2a0dad29d4dce4763e149a380dac8746db84af3369923fd8e419b201f91952d6e967383d86f89cf6ec6297d4cdb3be6e36bf9ef117f261592cd80c57d6d14ac162c98d886ccaa75d5e56a00bcbc866d231aa7493bf3111f0535e720c96d309aee3d1a66ccd5975299ffbd345f0cc97c9dde99620882adf2bfd15d44b32f8a18de521d9d3d6676f8bf667e9328c17e27e8af49f7f6f513b40c0bfe6b1a20c75ec7350bd62ebb1edf398684bbaef6ff98e6d2483f0b26a703b967596e297c0f3329560f74c226c26fcd202ae5e08837d994515b84d4291a126505c319f1d516b1de70950ba98dd0914e9f6b7e5c97da88e036a279f19eb93b7c41bd7f8c6eb743cc71f9024a9537829d01ad5ccb182159ccb76313631090196e4f137826ec9a2a6491e87603c16d4fe8a44f3b54a4a001c6d1147084e5c44ae51018dd0e2739b0cf5eeb9fb65d82507bcfcf368794959a8a46cfc96f0e873086640dc2e96f6df65a6ab120001540e5737470c50dcdc732e673343636dd85b550bed736b66b50cd710866201d7ab91c48eadb5cd1ccb6063d90e22042f789b3d6cca8e61ef99c449e78ffe452a34c2ff2c788b3d6b88c5f94685d62b1e5b01f92258f35d367f325e1ad5d75b297bdd3daba40733ebc360d4ec549dfcf3a26b5eb70b8e653bc56642222d09008e8309501d64a096831d59e4ded63ca49c82bd5bb21bdcb9ee9d9c63a7807efb5e83a9440ffbbaffbf3045c49d88129e3c1813d25cd034385fe7c0406d09f7884557c6fc933dcb4911e42cdfcf08ab9cb21c0b41cc8469412c12084de7a40e82bd809f0ae35d7821d60fdb5db0218f8d99d800586415839892ec06a6a67031b1a7927e0bf4f7745bbfb04e27ab89ffa05ac32f2f8d244fe3d24da11f09163ba05ee0a5ba4635850cf340a451d65f8b7fbb008396bb2a39d5600b6d1e06a488035065f7cd712259a5e885d3a9c95cf052b422f2c5d1d9acdbd578472645c99fa6fcd682f7f347b944e8c1b5c3d8ed6f8150de0b8e906b898c26fe0a42243a0fc1ec023b480162fe318c5361dfd1bf62e233a3640bc163d39e066aa4017bad0d8f7a0e1cd688a9c588d76ac16f716d07fdf0b7f1973f18b841d3ffb53a05039c206410fa75342e9bb4a361d15e00746468eef74af89df1abeb54d6daa709ae44f514d481b78b8c680ad96d579a1982511851f2b181c0deb20ac442bb9005e31aed3765494ad36ab06f02f2bca6fc5dd19f999350a8328cfd55272362c31938d03d9714f0dc38a8135f594efc1ad6d1191f7859b51c6e494953a0e290eef0c4673f2317a6f42415d7504efbe0c360459c3e8a7de9ea30dbd66fba2c9ae230be746165cb111a2d1303added306320e1bb047e5da80b4def5164fe56da12315e7f670961c41c69efa59e161afabbf4bdb066e38f5ae857c1ad3e095d5b2c5bd0368251c6a19041426db9b2401b4a5c0c9a45686d7ecf35413abf2ad62a7d6e3b08d5dcd15570cd755df7ba5b19a3d807727e5f58d7cf198c4c040d2041f225a561304bc52bd53c73a737602052b215788ca3f0a6ba70111faedf85daa987efda5950d38bc47c80465d91a9f8ad4092b899048b88b92629b2fcd5afd1aab98acccd05e0af7cd0f47289f2bbd589b7846d172147a697d46b795040c302f0471498a84f91d0eb11b1e1be50916de373676f1c06d12bd6a719c54cbdc1aebab25841c425d5255857f55f3da31856249ef4d22ac40a0e519d8e94c871225459d16e8994fec04e9196d81f3f058097392aff2f1fbfb0d13cdf7cb89799af05b02760c2eb06f96b402dd1cdc806af3389134da5fc0546883d48cce4a361997c9404a30ad919900f71ebdfe96466c202affaa06c655c3948ac748cedb90007bae6b9da1d622454c410fd3e78eb2f17c09684b89bde4700977d9f672cfa2b0d01997815592cd037f02fd1b7c8c0290d3521b11867bc08e8c91516d04321b59580b386f9bd0637941172336ce028d14f597f48ac6d5a246dcbf7398db8c10bab5c3cb8fbd40a1704ef2c35b71a1f115bb62d6ecce63909958084db749aa31eaceae3159c58416f2c32869de5368471755d689c1b8d4b145f52e8bda8845f2d4a33cf13631a7960f575e7769b81f3c69d7adca2715792ed9a7904c5a04bca71b24ea57c2c15f7703809fb9bede9ac63f3715b4dd72f166a37d912ef3b59142cabd1f2354bd7afddb5ac7d8361f9a736d7c6e1765764548e67e5d22b95023b0e39ceaf566c7698d95acb773a366b676cf4b9bbcb879c83f0505b2ce44e1ea6241db0d5b8bc98f30e0791d0f671290f9d00bf3f2393ab5a288a177b7d706f6e157dafae907841c0e2b5a17d39943cec9463a987e7ccc1b08905075e5198b28822cbf0cf40b4bd0c523155a8ab6210a595d96ce280b07df58e169d5894d6d9196500520f321942c058e9250430f2afca22f31e9217f076087ae483d81252f0c6cbc5a3e0279907142fb30c6a4a35a92733a5cfe71bb4ecc8a773e76600be459441e428e0235230e2ca1d2f3edf7b0e899b9b7a8606db7ef6161199ee822b87242ba603d6563362516ca9e893fa5effc4daa88589cdada56e05b23d454ddbca3cee56018c38116bd395225072c49617d45f07048890a8e8fa8d91cfe247143fa9eca024c3733f41c13e7a55947a448f63b54f2bc4467b8163aac428d07ddf6005c38d708aea6d02772fe1e0ff29b26de989dd014b23ebb2b2d3a83e72099d6bb4b70f46a27ce3bdf8cae50bb481614e532deb77c66e3991080528de336eb845631df172bf0708203892447a13964aa1c9e0ac9bbeb548e64d59fda3259a189dcd35c7429b012f3f762a464315abd380f6e1af4e784a4f041b6fe2e3cdee87b42c4f4d59b705bf9a1293cbe63d683cd29d3eb267965aecdc65db9ef892f6e940cf5bc146cc0675b5f94669473c44d16d08cd936e5bf7bdb1a7005001357d71b7dd1bb7a53a4ae7e5762f26d31f4b77c0c03d8ecad2647929dd7438642aa68ef5521582bbb1e3564aa01df49fbce18ae504090f977da606a04a855dcb190444b7e73f07eb2abc5e1e455defffe063bb882a0938528c8d5d3c2c150ba50236a05031d610583b5bb1d6a95fa8007954dac81a322931c8b559c0b750511966b82c549c008948aaa2d60657807587dce303d0e5d19bf95f01f202fae8e3dbf6385b985b65dd9d07f9e8e1bca17bdf12fc444892674181ec483f9741de1507c3f4645043f921550948275137332262c3a31627d81bc0fb8323c8c7ba536ff5911bc4065a038e66ff909886dd5030ccc2635078ec030640a8c81a0ad69a4fa2e72145ba203820101b8a55d0a76964195ef7b293558f13752f6299a5b7f30133aa96cac547bea1766c6200c28d864ef6e81d516584ef257470ebf8fa65108e20951d50c2cae40fa85822f2b79a15ab4ca2a23a1292f0615d519b370ece57ab9ed9a0dc02287755caf6511f7ed219f2c9fb02b88867218e20aa205e35ba25475388b52258cf5e1c34bad8ba9cf404b300d0058faf58e528fc6b30d0ce7b92e09642bda4140685dc626c0fb209f5b271b100549748d188d52af8f2653395982cbe69be862cee15543d6385165f95330991361f7b1505ba2f36052ad809f30492351c5dbf97e5ef527ad956d3805cb61830318f7db866876dbe9402c0e01f3c3807141013d40edd998e3c295ae88fdcf2862e0bdeccdf3aa27c132c17d3bb124eab242d498354a35534a68009c87ab03e5cccf6631db98f135cfeaec3c9046238b28f9b005692b0e6687c71a2738ab1506be946e6c5f0285c0e1f5622ef502b5dccfba1f523f0f8ca157c6c2a3bb54542ac8afacbb7e1bb15b253f93185d8267dd32c7790191e12c196bedcf476d32ddfd9e67b366692ac6ab5f6dbbf770b02d6df4eab6c923419fa89db94f3f9db13e6b69f8af334537d69c41ae846cf946b41444f50c04ed9fd599d85160aa059bf128e99277c4e4b00ec3deb87fe6d7a7897508cc7a36d01e64727eae42c58cb4c409795e69ec97acb72cc80451d90e2222e868a640d15b38e2c2638d7006799643f319c77c8723afcd9844ad235b2e311d613c48ce3de3d29adb67320d7cb79ba34dc7ac8265b0da041f1bd4c49dc6888d1db667a68e1760996c24c3edec4170c96396c3cb2ada5c8a1f8b64e6954da7a8fedc4021f05d2962d3a464d3a1b6ae80513ed6cc332c80c454076fee644aef142f1ee0c51d59c1b72d60f1e8ec18a2fca0cf2cd1f9f6faba0360163b492658ec7fff6dd7c780e9b8843f3c4e3ccc11d2ae67a6ec812e9172e3e9e5c5ba4dd9442029156dfbb78d9c0f2ee140653586b3ad6cb9f2860d2a2415f1e36035ca19058d530757c805f4443988df4a4f606706ec58389e42936ecbd15a12e49122ba59a2709ddca9a675ab499ce47f4baf674be6b3dae9f8e260eeeee4da2b8531a477b52ac49d2f36d389a637171e851d5d08ac10742dc973c2dc52d30c2ab27d10eba3d88fdf5a6cc774724d8e32a700bd7dad312780c7c61aff7f320d13b4632b9a66b8c1559ac75d32bfb71c4cfacc51e5795bce9518343b920a8903f69578fdeff76752bdd745731bf85de5531682af5ec789f64bbaa0a4b050381ce416326b5483ff49f12ceb5ed24f9724dad173dc59ab793176d29ced170ca06a1fe6b3a5a6ce49dfe323f65c81d8f629c6378760a8877467fe414b79b015ed3aa0bf0291c2d0aa462952b52109970cb2983d2c35f6b3b9786e85cc64f8cb7ec5c9ad241cffff9e24fc8e6794b6beb80b68a3e287346dbb7c05fb184266756bb1cff654071d461107f9fd78e1c8df4558a755677dff1aa2d27e53161f84a932c2527efcaef6e40c996703e2a0cff9de5af052994b7c0ecf9f32bafca0a9f822165401056a078f8a31388bbf253399693078a14be2be529d33003b00e7272ab8b71314a3fc9dc06a4fe575010116a62261baab44160472a7f0bc058dab679f246b2b53739e2becc2e81ffa2517ba20346c138bf031476ffe5c000d1ba0aa7da6ddd7e55d7c17c0e8608102cadd72e7d0bb0400146337b2ddb12ce5c8afedc00a0d94cde77732e141a77789cb334af156a9418f4eda816ffab7459c8318ab704937c8bfc8689e00abcd71d8762d1eca61e11d873f94d49a1ebea922d60e08279d4fece43b5fc27807ee0efc94833a00aeb6c674b89c7f7890f35a8c35ae7c5e59be8171040e94c8832450bb0218ead74eedc67f55da84587d838c3aa22910595aa9a8cb819ec965e7168eb54c50da92a9c09fc572a5057ab8a51c3fea870c085008dd316e785b86da84aadadcbd70e219a10e502a0aa7b9c7a5d0bced6284f1badb4846cd1464f62535fa991bb8233ad73b7eb54f124ab51d45a099b3181f08700abb5a5438b61a127f096466b1daf1fab5fae49869cf269ad2839741cf9e94498a0b5f54001a1a99d1c40b203ad7dc4acb4ae35d5b9c657e85a5ba39a5cc69ff62cf36a388af605b43cbc1aa0a0c7b426bb80a2ac931e2417315ab67ebf272b349ec727f43ae8548a33852d0b0b7a96e7fa0649dd50ffccd5aa6234ac47b626090184ffbd1bd1ae8ca3c8e540cccce36ce7eb9a62d977eb361d6af592cbe319d1fb29575eb379e8557805f5cf76e2c96ab4787324217dd50faf88f1e59efac53002845d799fbd20e6946049f8c8aa99aaba17b6e13bb8a2d6cf60a566c13871a5561c0d91fcc3f19f3c2e9e47d416dae1963f50671d67a4a0403c5d8f710436939282c396ce57f3eb8f94175dde98068b6ea566c5dda3f930793b5d5c3333c45ee5d2273db417be90c49ac74bc754fd066767cd1e5d296c054c6581201de6ca94c11eb66583f8a51aff25f5fd6d0bb5a1f06e42aa9efddf78515876754743b2f49540d79ec9d2a7d2bc5b72aed96814447db7a41d682dab4067aab1f1f82a3a60d32b72238ca6052c2d66d693104f60a47a30a4437da21834d54d24c354066fb8fc25a7eb304c7fdd5bfd91d4cfd4ad845c7873c04e3d149906502ddec80a77d5973851e996a38cf6428b8c6c4bd99b351f0382f47c19bc790484b062eb0e3eb8eef7626d47be3f6c1bcf95a0be4432fc1c80290f55a89c060fe577a59831a12b35358325c0a85650c77f988fede5adda0726a4340072ac99b1bae5519994c4795ce2e35a48a8e02b201acba68b4e5f6d9820c9d06b7858c696f75d98f7d786039b1ca2dd94955abb40dd6b2c3bea5661c4b8c948bec9e5974133a55c0620280ca6798980c6b0f8b6babdd163a635ea49746450267c817db7cf3fa6e727d8f38c03647a504fc8d3e107f88402e3a19bd9d7272a480e5ef4c6ba9b10fa7e1cc28337992f86b22e44f310918280ac4d2aa80bc390233d4439459982c4c1db5b47196fcfb5a5ea7fcb41982fee2ddb8e068ebf6ec96587f634a3b56d763bbca2b3d136e0fc8dde4f0bc7255f90557e18563e3896a35ed55d5aebf6d3018cd13b0c0f494043c54aec9f809595d39d673da7367d556d52a326f0a9e1f1f92293956fca4c224b3066784bb78f99910cfb4267979106e40a5056385c952c40b1d71a90ad954ac44ce6633c1c50c2b927296121b6e2880e1623ab2322776ed89b24f398d824f71e9710690090ccae9cd6baa26698e044b90d3881f6ab81142c7f25c28c42b6c707b24560b879df0f72de9561084cf28a3b21ec1175fa80cb5f13c28550a7d9359f4c2a3514c9a87098220383592f39a82bb2d714fb22f4c0ca3f1cacfe02bd89694da2bc42da0b0990813bea1b6d6e2a23dfb82ae4a9604c213c9ee32a3d6c96100033a567fab677d44bb5b42d107dd661269f6f9bb5cee8d494942b629ac64acbbe078bf0fcee787b53cfd1be14bce7e9dbe6d9d3075991ac5a02be600a76da40b7599546b6da4169de5146026f33db6d7c8a5f9752d072605c2a40058d2e7d00c292faf244b3fc59bc307660e5cd1e56109cea8aa28d1dbae4216b727619bd3a507f076f9294f38a0d545daceb75531629c5adea8ff2762ba2f3832326878a53e29fc7b5438a731601cb3aed7c68326195efdfa6dc34b109fb3516b8b9eac6363218f0771163a8708a18fd606809d7fb4e5106eed5b966847874dd16aae7bd34e1b078867aeb52a6dd38f5c0062e9cb5be5224fef4853054f732640783422473360228a5725fc870e2f43cd76dba33788d931f87ec3852185d50ac551f4932e835f2ac6bb351e8c6c49da2a313deddc0a3a68b142166993df9158361a750c23e3e6cd0b3200bac3648a0d7ccad9bc55ca2b006c28875124fd4324ea8c0b9fd70890b0b46528a2bb9ba2909a8197fbd5d8d4c18050c126f0d74d93a08a778e86510ec2ef419e208cd8778b64ed09ed86b3cd368250da7a15d50b84b230b2212277a9a8e4e16362d7e712fdfe2f983948b44d3899ad75b8d237864da3d6118279f8de9d84418414f056a20e0962e331fe7406c4c459c8ee0b46885377d87fb17dfef83c207f31e344031cd393bf5f143341b2cee717864c8eef604adaa5f885f418bd58942b22dc8406834f8f158f42016fb7a3deee1655019f68d5918c0f07d9fbdb10b5543c08fbf99d499a0217c7c70b2a7509aac8da6799da68cba00dc833330b43e2a6355b5e93942c62f658276e5f99e2e22261755b7faaab9bbffd53b86964f5269290cd2b374f671fc6ffca5871b07466379fc6bbac221006400a127e8cd942a7333066bfe73fe0317a83f11c20a7647abdfaf08d8bdcbef5cf6b73620083af294c60be617f9e04400a73458605802a6f02764af042a61a8ebbf5b2ead95b04a66af0e983e6d710813726488d1324538f2e1e0c66b60198b3a1dea39a2b3dc42f529b3556336953f831b526c13eb695c9ad7ad690255ce39a9f223840faf36d101062a608368bb8fe82394b29876363ee5ae1bc12e454ea6b27d8e5e8057593a1e334d6eea59b5cd9edd265aeee92246fa98c3c99dba9d37dbcdb913e3619da8a3e8e36711b98d3a4f348925fdfee73f92af5293513ef0bb710e42ffa2ea03fecac194395b84b73a5eb908b3402b5961652851b20ee32756cc4c6a82bd8170c8846b4421374b13bb51c3d964faba2b7109a4a9be35b7691c51b4427da620885d225210ab5281e4c4dc8e1f4a35f186a3eb95bb5d4e594ed6914cf152a774451ea032548f3bbfe29fd1af8d9da5996e25680990dceebcfb4d2110b56c870177b64b755f7be45d561d2365c90ff828e2e403fc20f137b2ab063cd0e203ef64f64c1725d2a8b25516adc07cbfc5aaa7a81c7d596f6a94abfeb52fbd651fe750a09a9d828084a195bb6d6d7113e04a2e3a9246803ba81351c62222bea116e4950365e4323436787fca7c168c22b9301d90912c7a8bcc5791cd25fb90f5d3740518f8bb031e38a3c0d1f6aba5702465107b8b2ae144e69cca1b8fbf0d85601f0e2e3470dd210e9df1c8ddca63147c712b6891ce9fb15d8e0ddc55bf4b2f633a4b64591b33ee31e99197eadfc593f244e760ad59966ccf170b8388128f7f6dce89da4a520aedeab88e6ace55a416deaab377b78d0921b351f415b34dc0d973abffa9ab2566fb0a1065ee7bf68558daf636f20ed53f369784b174573a4df931c53a8a0388b1e8e263778e7088167156808c6e025eef6d19811bfcd735ec335fd69c18f7ff79b97f313291dc13d86493571c2ed41fd9b9f7e4336d4b6fda127768aec4adaa46a2a1c0addc0662d2a19ff35924b16bf4de7437cd90950e644b9a6d18586aca6de7b61aed1798fc689c2f900c093ec293d23512ffac7e6232e70cf35ffd61f86bf525beb40a4a431bda0d681b18b698f6dd2ffa3051bd9a367244c670e0c1fa11e3cea68ec7cbfd11d8ab90fd44ac80c3d68519ee5199b1f600eaee6660843b76b86514baf62d6f3de271a39152b7bc043e20913e89e5896fe74309ad4bc1c12e749aa74e71be706a1f9667dc92bad6ee7ccd785e4c0e37d7177e783cd6fcdd5bcc7f2dc45118e1dd6c2951edb4ca597dab966e31da593645ad34bbf3ab2470efa12bca79ff987aa80ca5a2b2724ab517dba568d8b05f8886e9ed7a1027b08c3b6526e9acdbbba6f1bf7078ac3c59a4ca6843accdd14be17bb1dab646f9d585f23aa4c5b123d8f81e5e86b05a0ffade82bb9486db653865495a076cdfb842901cbd5415223a352fadae356e3373ca738ca30dc0eacce09a64efa5fecafebd61e1133b32000831ceefdce1d4091f143e27b12ce2ec8be640dfd30362f2cafd8c228a16816bcfc4f897bae75610c65f19d670c53f27aacfee30b7f0c1d2709c18ae27dcfda41852f483f2a5d7f0ff8d77f54f5468a45ec7056820c69936f4c370e0cc88f5eb0a03e7f18c85e102ac48940a834275397571360d78bbff4a051eff64c919871b0defd81a386c8ad0c29fd2508505f85a4c9cc471f103a37d64aff9ddd42716e210675bad9235698a0a17aa17f5844d4e9aa7db7b152509e682c2117b282dd11ecdb9efcf6c9dbc0cb3e58ac24b57a3a14c09b0f8fdf7d14800096cd0727efc4a7156672d38ff59046d72e1b0d06ee5a4ab0a96c0aa7dbc60ed192420f735ca9b014b2b96549ac0d806eb834e508bacb3eb9684a6e3e45e1c19b455662da03f0af1523578f3ea6307288349078c6ea74da3c586b2438778925a3a0de58021e20d1406ae61848d829790e8dcb64f6a9b939c26681d3fa6b4dfd623e59125302833137e986c1a9151754d00fe24569309294ccaa4115e0689fe4184280d64291151187d623a3ab154ea1b9b47d473da4d60121fb1e0dfc511bab19fbc71665880963a5e5dd77e97a52716c170f1c985fa79f70d5c3718c05d1c2be16f4788737ddc50f92d9128f784a2ab6db8ebccaa07a63d38c276064f16050e11a7def15cff9520801047efab28ca51bfc15dcc96158dc2ca6950c7415623ba5cc7c88a493ca57eaa5174ced1c861d8dbace6af6fef7ae2b829f78b37633966d1d91f45f4369019933a580ad8443225c2484f22c26324e942e05d9466221e495f2fe1492d25bc84ab37fc7f913737463b72b88e9e98982c5abab91e35bb3395b5bf57044eb22e11f8dd0853d352a0ebd3e0e6394096645d7fecdb3d127e3204a60226c38599ad35df3db76812b59817ec74b641322081897eb2701a27bff389a9d7b90b4284e8d008a7d63769eb3494c488987300281915ca5e83e249049334c9b8d759a08180ca2b3d10d61fc1210ea8b689e8f08d3612d0707e8b37e630883f7c6d83d66d81c95a496e412f5c3caccbc4dcbb4c7924b277516dad0ed67f6a9ba92c2468a1678b9c4a2d32214f049b9434d6e3540d8d840e9e13a1e2e0a832082756f011f0d1c211772eedb68d33fb33e3fa39cc6842c30ed425e6ebea8bc60a186273d6a011cf34dfc17e54857a795b3d821e5cc9e6b5acc3120b32a74116e9a2cbab1e8147380cccb62ad30106659b8cb2b316572ed112e03b388e41fdf44f470f9d6c7ddd866661be46d02158c7b38711c05d697261a763094dadcca2c99bf97a6fc31963283aa3b6c58cd02b2a01b3860d7eb0f3f5b7094b9e00634f3dd8151c5dff29c233c15277143a2672d0493589e57e4f6f74ec20f261fe9d0b72ec53839663ae6cbadefe7d0ff4a480ddcf3f9a872646f923d3fc64850f874ca6bb0e89687f5c5a24768518ee24d2f7ba9726ba10e99350e12d7750aeea1d812c0ffb9d10889487788f738b1208dd1ed620530d6e9a3ac44ee377410a5321bb435ba33e331442006cb2a2538d70fb32c968f748af150afe4316b0e926e2f720db6eb823e4f8c28a2417818cff3dfa2e580909dbc4d54479cd9aefff33744f0cb4ee79c1d5d427d1ea97da155e00c3fced8d3d9fc2be9569164fb8c4f66a3256f771ed8a5eab9f03096fe9f2155333a7efb2379bfbf354427375c8405f0d27ee115a8b283e82569f7055ccd4d7c77b073a599382ca94844f3f1db601ab2e9414d725f342af881cfeabda3726386359f1efcd177d1034ce37e450a327aaf840327b0909edc4fe07ab22c87a6633f060196c026ec9ffea085da1eaf8894415822b016a171f1d5bd7db48bc0048468835a38e4fd7d90b50e9d87686ea9a411f9bb0667b5c3e8f7c3b37046c1545f34e2d00c9353be360a9fb91cf8837fe8fd04506c297a4f0046ed08ffab561a583320bf960acb263190cb25b7ad702ed624012525e191f36ef0fdea4f458ec70b1620f45a9a303298b080c271784e6d9ddec71bc22bc0f3284eda955e7c46a3068345a02ed250caaa45337d9de03f05b7a7678addbaf9748979b2f29e225d96bbc0b5067fb918ad8e664e5a4d28719c7a9de7f1c42304784a2c340c93af14e0fc746af1dc34620b5cc389bdb26ed5a232c405d0683f7bdc291b4e4c6ed7fa885c15626e2cd4326b316910525bd987ec65b379733433cd223d7e16c96048e689d3a801a27a431a26e304997e10b30e5c36667d5a4a7c603ae296bb2e289e0d3345affd176dbeab9655a3acdbe225a127e6418c1695967378d8a2ac2fc08ca630465e5f4bf22a16f42420419c533c91648b0a37c12b2972f46f9595cceb857661a33ca1ab45edf37666ac62328e034d624bf45573228567c2f305114deaf9aa62e47ebf9c97987aed0478bf1c3bdf9c727cc45cf6879a353645a8bda67e384eeb7b71c6a043e8dfa3521e2d77f3a5ff1531ec874fe722feb5ba4f83874aae6405a53d42e4821600be6bcddc291442198b44d46399d1a2f9aee3968c1a9d4ea6a0a6ef698bde19bba547be431d969784524fd9d69bf137ebc5b24caedcaf2b4c4220d711deb5ec99e57c6b5d26a0056805c6353fc3dab83a99e0c1b63be602f0ab17a6588eb7aa5a8c49735224e231dd981719730480c847106e1e340c4bc0f251c6a6a0391e9f4f127da470c1a4bfcc350b70043e3123c4ffd3ba0780602b621a82ce9762431aafca6ec6124bd3667667fa07c5fce73ed5b4b7874a2f82934eb137315a2cdcd6f0d1d80f2772b4a3bca0ea2ba79550a4b58850f577afff946d6ee56048ec6d0ff65b7ab8f4576466fa0896ffc2999b50d95e8b2cae1ac7249abe7c474d3e3f0ae1fa2ebefa0275f2b44ee6c40e30c760ebedb5620e096262f3f639e2e85d303821f6765c589457521f3655d98ed8f113e314f96e4bfb65f4b802c2f66c68be34cb9fce5cfc54081997c2cda8b963d200ed672b52fc40058e51b3e366aade56c1fc95a7fd18e2d9f4361d0250ceb8b3c21c967a533f838393f18370bd519368c344e175fed147d052502a530e1b206a31e9df48bdc4b77bd1eaab26e27f3280a1e0df371390f7aeaf68b8f50ccb9377d040689b68494e7255a603041239e494db7492728584c78c7f6307520d64c67d47884497984f652477f16a72e9e2a76061124be6e2a5a1426e42f5c9c63906be7e76a134af2045979fe31449bcc1b46e6187ca6d502bcd7193a1d8025de582c820b60d4382b7c4998952d28efd97fa7aef6626f5369b63f939f395b0c60e9b1fdb209e9cb1f0959b0b394e5d36a23844cfa0ba3052b2255c740271d1513b1ed484d9fe17c2149e9958125e288a76f2b3f99a5b0bc8951a14f477e67a082186749275dc8118c7742a66b380e07e7126311f3af49d8b5810a42ce640d8d1bd458fb1bc72c030cc7f7a62a18b418de4ae9e9b13d79cadee9f3fd8f5438f5a2aa3494835f572947ee71002c732118dcb7f831cc9a2c661e911866763b03fa869b99352ef48ea2fd8359ffe803b6565142acda226f5bfc83887b226ee86945a3271ac6e4319eaedcd16d7abac2aedee2a9c5266bdb231517d5831803f604cefa1a2bc62a06df9f7943e593a74295d43da83e4fe2de36099bc62ae17b92108c6ad8c7ee56775429e7a7aba74fe0f1df084b604d4cf8f6440eb788a14342abba2205e6c8b1caa005f830e7ceecdbe1e108329cdc402ef108e8240b6e7e87d80ce7ec8d0da0cd3c95e9be70594e308acf9de0118e7be863247d42e7155af04a55d8435774164d1c8ab4037b88f6b8fae07d48dae3af4784daa63262d6b69d3bf05c6bd1087ac444f684faf55fec1cde0d764c185c21ae4078e6a7321fa2394b04bb0f2434e7a08b89a79015d21dae244e9d2c19dd61aacd8eb8d61d9df5b39aa27d428ff7b66f66ccbffd6a701fa2551881fbfba403bc6480247959f704c4010bd61f12d5906de87b25942782aee2f0ce5cd848dd8f6fc1b43560efc9941c8d78eff78441c07ebfab7841362f259f3dbe926cef50d64925fbe138c720a1e52f2cf00954d5cb89e9467feb956647a30b4cf0b53d514518c766bfae170af218ab449c15850bb7d4b36b404d2b156a9fe033ce727420e8d53467da936662e851a54b0cd3dfed32e8f3ca62d2007dd8bf09dc13dc656c88293704456938d5260a0221ea6c0e55ac16271f7eb786a708e42af57506856b2f65c5a3305e702e7cde49967a8a9fbd04760c0e5ca294ba679954d7df3ff6122b0eb6ed351b2c73fa1d0fc3b69ac574b3f1673e86c145f8f0f306c6950730fe1116ccd86d7cc534850f19e62e2c64f136e8aa1c1bd5ddadce6f71508a067069dd3a793c282ccfbad2852c781405338a0b45657141040994c0723fcdf6473d1f08ee9b7814152441f7f2c9d92b676e84d4ce8780b1f11fb0b4e771833ad32f5b9a78a1a160ecf9b588a60a074849097527989efd7f8e09fb36d38fc7a59cd86b3b881864454e163b8333d44f68e6e64ffcdcad46fddababa56046587d4d0bdac108ae4c238ab77c695f559af54da3faac0d30bbd4068054ba0514dd28e62795cf9e030be5f8e7d8b6fc2af5836a8e7e704c0bbcd092c4b96cfcc02352db2fa05cd628b9b9275740729e48cdf2758399173fdd2ef47d4631de8022f87106d73809eb5d7dc17cea0395893d5dfae6d5064cf640aebd3471854efd9f6318e36fc948c75d5062a9abe3e7d2541d2adbe59ed13fc6a36441885d342a635e2c825158f5f17d11682b9ab51d887261b84ed55510577584ceed16e09a23bf09e23fc4540774854eccc603deb9a9f1aee80bb6dd2ca801f086c14cf414c2ad456e06a1a5bce391abb0dde1df68da733e08430f5544caa8bfa38ae8178ebb60fc9822e43bf4dd4224f69c3657c41ce604ee98d1395669b109017bb90bb8295ee63a7722eeed855b3ad683e807231751abaed9b4424add97a02c80b4484bf11f748373ca59ced343f0d047fc5821063f3f38e599122754ac51e310022e4903adf91128a4f16785f1c9ac2d203a41e6eb2996267bd955b01ad494854bd5452ec1d30984ba0e2fe40fff7522c1f82511511edbdee55f1b39015152bd74cb4f2df02a48977826e9a80cd77775a4a8d9293bc8fa048aa7c7e55e2d97b374a5a5c7564c298721a656d195a2863f6a91a27ed47b73705ba252af06e794e47309545d750ed67f87a319812de21fe8f7c200bb3e02b9099c08811d3912802e396cb6cbfe28559a98854504e2f37b45d8ba64004ea5aff2715c7b42ca7f33e929b151980717192371ec72f29b0f4a2b2f267abab45c6813ca6472fdb0d4b0bacaaa326c0d2ac90f5a00954997042c287233724b3cb0bccf44482f96f5b8f1893e3c471c763dd21334b14eae1f8e64ea199053c0c4670d92067002773f40c93c11d2a5b52d4e6dd2b20e1b3aa5bc031086159332335f39be9ceccebcfcd269a58f5aa36560944508236590c5a42e9ee8af502ac3e5ae9d03d900da262ae0a4a7237b003c8014e96be6dd8e904c75c4cdb825d031c79044e87a61562878affa2a63a40b0926de5fc83627ae8739790fdde0e7d5505a361134e7a15272ef0271db331bbffe6b9e724a294ef67123f8b023bc78d81f857932dcf3589c0295c907b46cb9f0f706bb5d8eebeb8b8edb31c6476c224080e70cd7fec4be0eab2389be3b0676968de55b2364e50f83239bb39ee23b9dc011610ba7f3747b2a01204e5db83478a00a681db369989cb9f7d253673135ba6a747206080812860dc4ff85bb6907e2e203758aa0d71b75fcae04b444dfe111e4dd1d6867a5c00d178370f5b76b02f1f2196bc392efd96bd9685fd1f63a3ec9a9045f9bc5a2be24ff93d12cdff333271b51a0fe7ae08b10fb50650ade9d0164c41816e7e400505d8ce795a438032b8a5aa4a887bfede9da114802fc6835e68390cd5452a4de5987a1af9f6751b6186f7c5f272407cbaf1d5fd790817c70cbd8b60b0861e30ccf38443c487e5b607a9dc43e5c6c4470f9c7da67f643174c324378644bfd6f775dd1d999a2286a63689d85d9519b21b8d321b38ac0da5d6dfb8072733b576836e74183f209656c7e2ebee9aae4f2c54dcfce8aac5daef5e8d9e3fb6abee7ce956c7f2eb4e4d05f0498cc7526effe9abc562e0cdcc23e8f249cd4a7c43f8535a10fe061f7d8edfed51950a325983184b35b2d0db3f47366733a15b660dc9ac458ed712545965f6e5be5d41e57a277c383a2b1f6d01d43b5c806ddc173f7a112fb920c43867b35801ab6f1d4257410286b102c2880babb66cd82bdf4e23cc72ddd325f5bdbcd2d5e7dd3811d4b4c22138ecb28979f94ef6f33ec381fcf61416dc55994fa11a9b594d5af99b4c18330fdda7cf5d955515369984515246f02b10edd052f2da37df8c9b44e919e92e34a57e4ee6c31d11a596667541daa7f0849bf9915e48949fbea660bd5596d565c3b537879c972a1a455fedbda149cc8e1547f05595d6859125662b412217bc2a1a110063375b4a7dd3d496ca4150b4a50620f9bcb8acc3174da990da40799cf4d948d6c7293f45e62aff96d1af064e91b115e9e9cc935b319b7448ba06e989a4e7cf542ad76cfe40f51190c4d44f9b8cf932372bed1f7fcbff50ce0852211bca2d6d1e2a08b04629bc822e6230e8dc55cf72fc9028fe3892e5304215a4f02482c8e08bea9b3ad1145e236b74f4d26aed3bc149616d4d31416e498d1492467c02577b5c938e76fa1c8b38f248fb856c73a3ff439be19bb1abee98408b48b8a9c684628b50f3ffb967bb76ccac57fba9da16db7efbabddd2056973814b54cedab52dd3b7b31edd4dc4bc14ccecf6b54ec81d50259fbe202361fde38c04ab860d89ecd2b4066f4a9870e2de8afff57688fc7e2a3039e8c919313054dbbf3b7edd4ecdd5d8632e6088715e74a41299f88df1eb7ccf086e9e31469bb4dea7f4572b017129f54d3a2031f72bb53527bc490f3c239c2cf3b496008b6764f47ef661013a62396ba128ee90f5644b65256a6cd97e1c1a0e6c023256e06c668b5109656dd10258a79e989f4cd1d045d0e27e2a7039f698bb1927b1435a0b4cdd0c463a4e1d0db150dfbb61cd4e2b1d9ce996b54f45a55b5a5c42100beef40e62928f1c152b53ea5560b5515a84614133770b1835166d64b248f0f0068bc6168decf3f81a2d2de7c57823950135e9ea19be4de80cee17ebcf9abd1dacedcf7983f35e15d0357c1ac4cdf55e0d97434084ee6f399dda053929cf11297237775190e9a080a83d97b58c4e59f32c6dd4be94850510613292dd9b4bbfe4956cdfdb4e819fb3d7dce263a2d69ad73c88b278f0080b36a48666aba18350f447ec9f11ce02beafe4fd4986aa97bf450914bea0f9c36e37dde23a97142850e7c398f0aa08d52e15a6ce138ad354978294af22535da6833d4e4e60225a07bd078e23f611b054790fd62bca32ef286c0ad648924d589605ce516c48611c66a90ccb2a1c0c60a1d79bb91f4b3f5effe5370bda651b7219504f4f8a983c9c965ba034d2ec8111e3c1924305e010bdb93d926c21b01e6a48ae80ce3267e19d8691fd99adbe3c3108482a97004020b051b62151a7f4bfc6a2bbd0301eb43340cb1b90c80152af0f87c78e8e23b1b82695c35d96ee2c8475508347eedfcc83321fa43792974b9aca09ce123ae80ac96345ac04ac649263ab93f02058307df22f5cec806ea3781b7f732507c970dc9425107c87d67420776f556597fc9a83d98e998f4a890107e8bc9895adf8c1bdea195aaec70c235321b044442cc3cd9aa30156595b61df7a3aa281b83771631c4c139706544712a381bba1092d3aa8159aec5b5a04bdf575b24e19ca5fc9ca86734175c2c859121a3afed18be3a5fd58ca86b3437da2cf23461c6e38a5b14e2d42b181eb2af0a39349cab12ec2b79f25f48963d30a4112c2c60e75e3134ea8f53b3a35e81ba0116be1ba2a342e09dd672ac96ef6618d684667ad0c9582bd041e5227558c70c8b761cad196ea7079e37f62eff7405c4ce94adce06ad25a096e6630d9680069a4518fac54865d4c7715fd64d61d7564e1f8a2ce5d540c52d14f376ff9f564b16c17850feb8e31bd0caa4c5f98177f0fe894c1ac362e84564d11ee28d644928eecdde3de0bca531ee5336da7be280f602978499687081b9b31530c4c12654aab09754d6a08510c34110283a9e1bf33327ef919426fda0d7d5071575ffb1a9066d01f9f91da40ea44dad1a0a55b9b2fab4a075b620fc95e177531cccf569d1cbab8552e56484fd7379685f4beef9faf872abc19238e0e8e3fbe2f2fb73a96e4969680210213f430c29e7d0e07a5010d4f2675c697330224734e4baee276279bccbc1ce8af080261a543ab633121345ed84c0f3c2e9b0d6841b27c3c91d8db0d0076a0408bf09bb2318dc691846e9b2aac0910388f825fb7640fdb408db50dec72de0de58ebf1034a8cca074d226f49b32282c4239905afad8158e11085ae6cc6d68ac60e42074ccbdbbb2278ebe50412a9199b5765b9d060f2802ddb07074ca6409311f953483c25582ab1cab19d6f4d002040d76804d2cd6b6612d4e8d20d79ed359a75df13763856071577c904008b45bf1b3bff9f7479e35797f4ab5d23638544311e4ad0c7b2e2981b71af83d758e87886df2195229947ff8dfb48728f57ab572c6328d90e1cadcbc92bb1e7a6f201cdc9e38deda5b2e3ddaad4db7655f1f694b3c60055eb585ae13581038ca8c7301e5459ab781ea06f1d31cae7e672dae663633b83c8c3ea09e8c15c552f52886969f5aed4e601374dbed4ef9b685cd422c8102601314081fb5f0f7a5725b8a8f943654da366f939fd30236da6ffbbfbbb79429b713dc13a512387c7e4565c3071b55d9a6c19341d4b8e2b7fca417732adb643e7c53876cb3f1e19b54a60f3d04718625e9e50cc7cf31ce72e4c8f13894661ec7e39841c1f133a3cc467e5fd9438d2b8e1376b12133759099543ee49a0cd320c4329afc38643424e499528e9f79259ac7f1348fe3593964387c5c60c9c8dc908def31191b366cf8f2b37e0619e5cc339c84b3d18be3849f434646791793ca9c2ac2f47997f171bcaf958346e63864647e868d9ff13468d020c999548d08737c1916eb6dd8b021ab31ca66bc8d1a339e354366d6f28d52e30c55e10c635559a30267d86ab5c2903513dd731661c60f92c9d1073b7d7ef5e34361d8438ae7c7b08f791862e10fec639e8c3dcc93b08f7925d8c7cc94620f73baf86cbdd83265352bcc714ad80a67be1a11e6eb57f0e212df258b72b658e293e20709a7b0161106a573e0a3d972f5aa37aedcd022deb0cab63cf3feb0fc43f3fe268d0c96675a2d1b573646d80d992a2c9f7b91a9225345a68a4c15992a376c5cd9b8b2c1858d2d36c2b071021b5d6c78f12c343e9a2b54983846d41671c7c9bed699b1e36452d6b322f3d28d276e64c933942432bf9cf9ca99a7c18167687ee695687e66e66974d0bc8e70cc356a68f0bef0b46143ecf13c689ec836f169aa641bcd956ccb31e367b6c854b921fe0d256e54b961c5f4e214bd904ff35186669ee695669ea6c7cfcc946678fccc2cca1059ba64e18cf7d9b89ad9e238228df7b9971ba1ec861237aae41af15fb21b56728d986139427c24a5c7f3f8b27685293a99f91def84e675b0e000251eafe359e831a3217fc7cffc8e6781c78c269c91b063a6b46336f33a9e86e65def72c9c8f7182c0cc3d82bca99d5a55c23ba66689e95568e233e8dac0cad3065beb4f25b95f087e6459235e53734f8902c4b3164c588625235224cf27de49415bc98a70be6a39c19c6052baf5c9e617c2b9c7946565e9dbef0bc316391536e58ddb86135e3c529be4c0c29152323a3a2728d2825d78819071c7ae821889c677e02139351590ae665c437ac68aaf88dfc10263f39a5bcf2d6ec35f3f5b85a829cd15491c2f4b997272852d916d6906d53d94605fb5056a3a645ad0a73e669de474ed1b19a62d47adf9b6bc4a7a912da883f23f3d086e6ca29ce6ca94d71c3ea14bfbccab6ccc5966c0b23dbf209b2ad4be92544d27a5f9eb548cf2ec2907eb664a23bc6220c3ec9190ca5d97223ff07a6a8aa7c264bbdcf908d53b2d28962b122aff75749e3956828cd988d53a1cdf81edac0c0601f939131a298f9a5697e96d58830f3afe0e5c43031a22cca8963600f23f3fd643961e5c364aed16aac325a1561e656cb3c5b3568c8f0cc17241ca560a439db02935d5e4c2b1798e5c34ca91acc295387d7c7589551d92625dba4ca1aa20ce12fcb8729c51e36f3f0431989ff35f3c53c20c6ca71b26be62ba7caa8524a2955d610c2caa8192de5eb0ae72e33df28e525145badd6fbcaa80f65e39351ce59695ab9f2288567317d9e05e31cd63850d36262588b158629be3eca50cbf5af5772fdab3563cd945cfffa976b16656804a31685e9f32c3aa2307b45478a8edc3fdbf299ff6b322b8f2f28336cac828981c744853faecf2e9fab8b6fa4fad64719a2717f28ad77cd7c3f5b945eae7fcd7cc871cd0566eb5def7379097f7c287f8c1498aa2ed92686cb8b8b8c18e02ad6cc0713c499df27c600a6ca71b2ab8bab8b4b0c9797ec8aa91213508e8ff18d1c76e9c245e4c265e4c2850bb9650b172e5cb870d1718a5cb818a9111685e1c8c240ed8bda17b52f6a5fd4bea87d51fba2f645ed8bda17b52f6a5f88400421f0620422f05ca35685493eeb7d315558e496181a61f84d7c1a2420e20c636884e138614c9533c42116b5384ef82e15d698a93af1971d809df95f5562aebcaabcaabcaabcaab43e8797f067940a6df04f4d5151cd58c184e138f87b5060922047d5951c4190e0c435ba9c384795e3e0578d2e27165f2e3c9ba9122550db62a4c21848a20523556d8b0f04490549054d95a5931f0a4d953887e4287e90fc640ec9f0431c230a3f8fe3c8cae3487e903c86e42883e51a1765d8c3700c43320cc5d0c92a56ae963862253131b2fc3f574136c45cae876999af9898d9973fd3325b31b39898b14a88714a16e3cf9598ffd57a568cc8253361ada91c236ab5605998415338f664064ae780035e3a50e5d439bd8a0e489d5d58b1603eb2624aca13632cc3e23f09bf8c38483612da01e7c85e7cc13c767d28b4c3933123af333613da2156e41c634478bc926bc227724d386629f3b3624418872216a610158e8d303c82e131ef5f3aa0088fb32c314bac324e8536ac93f538637c25941253c439f09910d57b8ca887e3e4b1c600d3d703d6c36fde22cf5a1167172ee3c3d45c608a1ffe2bea25e535feab866cc33fbe6b8adff2fbfc4af42292916daea8161ef2f047fc79bd6633d7bf64e5e7d8cce722e21cdf875d531c67ac3a6133df28354ac935e3b3645e922c9fe885152b82bf9c6559be4ff4929f7ce5972cca9959a4e8e51c5f53e10ccaa8da0bccd712fee3e16bcb8bcb2b8cd7095ef9eac58133bf34f0aa408d395659952cd94b0c17e345c638a54b11a66fb43a5f5dc29fd7550f0ee0d0c0ab022d2fa7cfac19ad548951a30248fc240e42684a68871194cec1cc0627c014df5773b2484c7e7e1f346476b5b0cbd59279aec91fa8828b53767a155554394b0cf3571431db5eb320724a6d8ae98b316be0f818bff919fe383e4b168631a21a1126ecf3bf64bed78f1e2bf27ad797b228e78b9cbdb46431b20e2c84a8c56f3daa58b9f2045512260b462d6154d9fa9c6731a295df780449054d9d415782a482a6baa8994b28233fcbc41cc2c2587e51145b201fc7c89cf13986a310e1f865cea218be280bf30b51e550263415231263392b6185e113d699a388d8439830939874c203529ef0fd5c9dbe573c499f3a4a89a2288aa3388a631e49f1b32c7c310749651028f101aab3cce5d3196631cb82a642317b08e38b0281d27194e263ec18635c53a3943f876198652de42861ce0728c5c7f9c59c737e31677214c95114b318e65ccb398759cc39ccf9c59cc31fc91f5f891c5b2c31bf98b39859f945f2c76fc594c819105068d38ad5b298f9c59cc5fca2288a39b38298c9383849e220b0d8827df9394643028c4aa908c6a1582467b155240c479345ca8ab05a2ed68b9f63462c3ce4b1da1326f9aae23839b4b26a71612585e91babb45a5bc629b060c182050b162c58b060c182050b162c58b060c182050b162c58b060c182050b162c58b060c182c5799ce4bb58c56fa34f5ce2c4f959e450e938cc304f98ae2c8e934306b06a5398bef18913e7af4d61b64ecc92592d517a2e7ba630f1c3724d01e314bfb1cefca3162bc6ab6ce3228b32f101b51698becf9ffff38f5bb8f098c0c5390cc3aa2a5114c7711c499224c99a132c168be544cd892e5d5a4e98f8bd9533911866d613a57350fe9114186030c07e9c0a6dca1811f79a9a324682bbcd3883db8826ec1de7702c129be5588d091336a32142be8761187e089bc13cec4b180c068395ef30b01f65e43956c936df68459eb0d2719ce4150993953f5a85ae16cbaafc5cfeab7c57f9adf259ef1baf6225ac24227e391babe41afce528f3f5387d64fe71b472e20f65314cce604a092a54a850a142850a152a54a850a142850a152a54a850a142850a152a54a850a142850a152a54a85079184c9c99f9c6872962fec8549ae2cc57c2647ec2509561f8129f46cc8be3cc77e35b486888b1b7f1e2cc57e35bb9268c297ffc524696e56b20f3a2a964fcb208fe022396b2f2636f7e58ce609e1467fcf83232df0a612081797146638e321a1a7346a47cd8c3be7c998795a20ff6ac3236fe8c59eccb1953eccd1993063f9b6960a4c1cc6fccdef0a4f1e397339f8d995863f68667cc8f3466325f0ba708fbf2cb52062bc5d214611ef6658cf8e3fb6f788ae5986de6eb612f8a62cd03a61896ff82bd287bfd18cec87c292323f33846447c19d37c7366ce7853363e8c2cf6309987f9191f0e989799f946aa1366f682cd985e5fbe663ed84b2c5ffc51e61ba9ced1876394c1fc48d5844f9967c1664ce297e50a619cea884f998719613f9aa62c9f30325fcc4c1da960b2d257be1be55c218c1a044cf17d231591509495ef7a18590d0226aca43a63b117b04a50cb0a6198612800118ce0c454a1004420822c422eacb8a8e2628a8ba898d2c5e5ac76444ced889a07ba78a04b4c8c28c666b1982ce6455124aa1d618a628c07b6d48e20aa1d513b02a68c9a046a11a82d51ab42047f396994ca7f3d0c58ccb9159a188fe2935f449c8d52a314c639144792f5047f1965352b337fe8aa59bd8ac617bfc838463973ebc72fd29ad1b4def534e347395d33b73523b283fc714664478873188aa2388ee348922449b2582c168b5543c2cc1f6ed9d2a50b1266b9e32467a2954fb4f29948d48cd8428c3384d3abd0e2ea2c637c7c1b165b4224995b38f3fba29ce318922fcac62789c65164e11c819c31ce3197cc375a953099ccc33c0ce6c77c08f34a32af877925f3f5315f6251cc632b24cd227ec2602c18f263f80c6346b1d9ab4684f99af95c9f5dfffa20a13a5ae528e1c7c860c219cef05b10a3bc72f84519c6d41c108ae2388e2449922c168bc5aa39a0e60031c78a8c9f3f5634ce32ce312372cc62cea12cca88595aaca87c787dd054d0944bf6b3e51459ac99efe74a4a4a4a2aa88620a9201b849852f59a7a519de302a4826c588094540d5356e30a4a2b2cce9e84a8728d1055ae61b130cec92fca847438c52745a1b0c4302c13da01e7885ccef1b525d78ca11825fc590b676d0833bfcfcf51fc560fa595951255ac7a2881307d6316a9522a4b2995258b0fa56daad421db481fcaaa6c2bcb29a5114253423b084d91e1a58cf24246195595c54b1629b45871c5c596322adbc2aff5e01aabb278c92285162baeb8d8e243962c5268b1e28a8b2d423b08f9504a79c9525279c922c51519a554594396524a6807211db25859596511daa1b42aad942355b6893ffa706298c731570ccf6a9866364089cb6f85e1d77e307d6355f8f3aad20aab54bdaad40e6032b97e2cdff541f2abcacbcaebcaeb896c235f52bcb4645bd9faf0afaeb868952f29d70709493c7b19916ba6e41aabd79497112fab6c63e19755cd00f85de3cc17a5860093f5fe9a7200139fbeb16a0c7f9c321a61a544b655c936d8e9c3a74f8793fc2aa6bca664db2b4bb6f95e53c6aaaa33fc5188f05f46bcaa649b5751859433fc9795fc83877caf2aaed993f0cb97aa707cb95e7166e6737de3ac45fa6b0b4c06160b06cb31180c46041b9f050323638e3131b35a1964d4192a21e28499f9a29c30cf9285368461ec6164b120a9201b60415261d038b24211060bb78c5fd60c60326133dbde173425c26a44f1566ba4f2e1243f48c6679054cca83cc3998c2ca64a6883799cad87d9e2384435264c9c051127b68ae271c668e0c4b3128cd317a301cce3f458915188718bdf6a65980ec689df655a599d6489652f12cb44d3eaf48d5467eb4921666355aee971b6bec7497e8b8cb9621542998db2f1c72749724b2cc35c50925fd67a3099583f9ee478b29efc56141cd9360a5189b01a51ce512cadc8c7ae28a3d014eb3f9c290967be28e3b803d518ca5c98c56279eb85a646511cbfc4e2cc952e2789c571d67a65711cf283e4da044c92bc726228b3202129cac417838451ced1ab5a639864d0948c4c9054900d4153e6b364e6cc1725c88620293f71d054900d3466c47c6b562bc314adce201b4a2282a438e86280992b335a66b2cc5c054dcd3c3123c58c153357826cd0c2847d4d0bf13dc67c808c14f695449ce28da9280eb6788c088f13bbde6763090ea41c479cea9125b4117f86cc439b58f93ef3013cba388e7845ea14bf7601d337ce5c39451f07be1e5a4e51ca297e79a5c715073348a941071f84b072e54ad054900d545553acaa9ce273d0053603cfb60e6439fff42ab49071965e6618141c251361a24cb2be1563cacc60cd94cc9031637c51f0b75a3b50655beb7190308c11c13c4b06d32a3ddb84a6dc4ff285a85a32cf3966549e61c982b5320b8606e3194d97f0a76c955b68baf87a1091e4630fb49caf45162d8c384b0cabf56032b57e1c4f1d514fe325dbc83c13838c6cc3345d6a5726ec6b57a6af9499c2adcc92a932e3cb2de1cf8d2b260e5f8fd3576ef1c1a44e71c6cc83298e23fe159999af8739f3e17039cc8d2ba7e8bb11c67b711cf165a6ca17cb67cd7c3df2cc87c365a61ecfa64e91a64b9553fc1a064cdfb8a5cb2996563e06a7f82f9a2e1e10718a5f5ac962294569c5297261c5e96b85259499b34821aa8fc2f27156a57565cc82c5f7408b79658ab1582c2692b198e8fa58cc28238a315148089919a2187b99989711678832339f1015cbb432655656e20bed20b483d00e423b88e418575b4e8cc3e955642185cf63333326f631313131b18f89897d0c938ffcd6b770c6c89818f18cb162b1568c8b1c59b0d8c7624c33c634633e4818137b1913cc7b8c4c4c4cec67c4fc8c98697e902c0303f331e68c19b1f17d3364bec532636acc781a3364622cd18489f99818a9188c2cc8069c139b8932871929cfd7cc47be2bff381ba7c008c3308419c7cf423bc0e4fcca303139bf1e06f63039c30851598556d92a26b3b2ca2fb483d00e423b08ed305a9155b4f0e104e1f42aaeba903132261febc56f6164c166e3181b3f263e095b3fe3e382eb15c26231582c06fb2021ecf532a612060383bd3e06f631af272113fef23da6a48c817dcccc1734357e0c8cccc3c4c4bc8c2ce6cd570cdfe7c21ee6535be197b299d89af958a718cea658429464283425a4839010423a08f92044256b912fabf2359bf9a2b058423be0305f987c01eccb320cc9307c0fc370c60ac330c427ab2566162b7f9014cb17e403d3f89edf95737e312462cd9e84277e7f676121c6c9737a15585cc9c20a2b0b23b290c262b16a659833f94b9827dfc794673eec7a6101ee4ddcccc1aa6930c743dba1f1e0b0bb35c5d9aad6b92b2f75dddeab8fd8dd6a04d5b2af46578d44d5ed062a51f5af6593b8ae56850ce9e95e0d7453d485d247ee2e814de2b63474f75cdd5b83fb97be6bb0c561020e273886f8d2df96fed5bf5ffdba8fee17186c5b5688bbefe8a85ad53714dcd440c86934da07ef566f5ebd5bdebf6c92a7b9af6aad0675aad67def4d9d9eb7a6e81afcddeadf4d6eea6eda2a4ebbab6edf9be300601ebcc3e38e61f0e8ee3e376ffcdc6d697ab7af9bb379ba655fd7a666dd2a6da76ef77ff87834fdb92dade7ea5e4e0e90ce27a86797cbd9e1d13a3f3dba1adad35303d2d93f38fbd5edebe6826e8efb065926ddeee972de94d77375ef767b5b16080cbab8fdd3edce8611ee6fcbf6dc67d9b7e7eadedfb6dbeea6dd3e9aaaba6935fa717725376d14b92d0d5df7fbb45dc8dd5570b3c695efd7dd2baedd6f8afbb1ed0ea7db403747b79b727a76280f90deedfcf0f4689f5d6ec74717f463dba9f5fcf0e476745ab7fbd139a09d8e8eced1bd6eb0cda961e5c6dd954d5fdd56895c1698c4dd4300c1bddd68dac4b647fad543dc76af6bda046ad5485856c9c3a61c685850a5e6221316102da054cecaa2c80b9c6802721c00f6c503121d70d60baa8c0040a3f9e25f4ee0e30e34fc20b1b380e31f624d7920c2f34f088911a06a7cdc21bc408f073e3e9a50c410142079771b1608348400c49d490a2896a4a00bc7178832950429c559391a908120c850e2a2ab023ae02ad0e4ac18b24ca120832f4e9250ea1f59beb8b3747ead38f16c82225a932a82dc675044540a414a30be6d035f44e08bb38232a0abf2c1848f602460095fb1713ca546c8c6da848748a0a051c293023c07f9fd8081df1727dd1ab0048172f2908a2157c228240f8f8e9a40f8c00a4e1e40070f925841099eabf0148964ec70160c051822ea0810ce52810a3e34015b72bf620164dc80e30727693e1801a6cacac6575eaaeabd42eabe41164a1f11c18cd171ef12b5e4ac1fcb45177f606755617494c12c8fc1e30d0c835b3ce28f873a883c58b9d51ac51063d720d33c3b264114013b4b68f418123bc6781c6bb9cc8e310e7311c62c9c310ec91532ce98956f20614c6296671ca38599246b5e8fc59cc5292c629c5fd80519bb7cc4620e37c6be8c716e2dc934196752ccd8041963326312b0061887b975813164c1e01b18db6885f8079c319931ce210e67f1e5aa651806d2c22307e10fccca59dc095f39639b181b0680b85312098fb28b9e1780c5b0461c330e7d2c00fe027b76d3341d3b1671c629c0ad0c02c679cc39c4b807e7e01686e59c3329047f90b398b14e482b73b43cc0b1193399073ce61c58c421f67c94ff8545cf29e9bf840710baec02d88364208798c4220bc770489573eb1662a010e72c7e86b96af088314b47dce9a183f38b6c658c3166617cc3397b2024b12b8b61139ee520c6210e769519b7583807c93c328fbc23f4d0710c2673c631d10cc59c23e78c492ceeb08b035c8e198f18e607638c314d366295ae8c732d182b6312700be330631117e11d3928ebc86566e5118738e35c669688c331c4f80787b806bb300c632c8479e457c8c22d11678c6f189337720d2c663c8a38289738cc3c7018e62c661107e58c67f20bbb704bcc18e31bde915fac108b2d1163fcf31807082d1f6108230eb10b8b38b0cb752b4fa0e181371071ce64666d6cdad0298b30ce38638c719ce54037210aee52420afc4b08220c2d25c8b89ca089fee288255e6c7125d9169e1404077692a6c29182b0f203928c55424210be2e47ba16e448103c5a50a475e8040581232748c334e0c78b18073fb217b6798167605bfa0284edcb70dad2183c7096a6e49b2d43b037af03420855372ae8b1f4b01eafc10cafa291822f8d38f88b01700fddddddbdc66d69fad5288e94f2ea208a28968be512615a36962bdc01537221c9ac5543be5a40222c068b3012273a85b15046bc0a8001a24898cc728937b11d404744bd98c050f106c6855ca319978898f46181a81f4786c804208b2ff1880d0ca246f002085a5cc0e34da6471017b23813e200b10cc818c2155124d9e3488a8d982118c2222ec81095c3d50456035663052236802df7f582281fafabc812014c962b8c8d4ce20c35b8d01261c81d38a6c7053012e002010b292d2f3cc0e4720c22c30a128dec13ac5ea031d6bc88bc3c205110416005bdb04410455e28e2861ba68e9498ee88009e882a83bb030420c6138399e8088558396c2ebc5841e4ab0513fe18150977bc88bc20eac5328a25b58a688888bc5c45b024d4c712511a8837b61779a311235f25d14c8521a25ead221e391aa2c6d01c314902f91a854810800c9541d707ad22f2c5ca111be2e2404ce2d2a0860e0f666e80d00ac10892072ebc5a34564face7c7e88805135933e32db4b960e2071f84e562bdc49cd1880814048f34a16cbca1e36ae9c22022211cadd70dd78f0b429294b5568dc881c8015944140830e290318491878b035147cc4eb8e385285a0e0e77218e188e0098a9908313ec8ac4115aae58215184100288c2c293cbb4dbba25155030c10993254947474628ba01f5d478d0c1062f6600035f547002304400d2c16c2607ab6a0a29183004094422dc76d88095e4135626e080290c1042014154fdb0a4e6831cda010ca094f4850c317226a8200514c0b13204b821dd01117ab1a6e030f5e3d2e28a8829b7209b0635ca578ba5834602114061a34a928c60448408081b669061c6169f269038a288284d98fcd870d41039a0a5054c80fcb0b958e4036298401209d821071a0080002080b841491220421fc0f018a10cb217b1cba72087218291bd18b7e02d7216190bd715302b32073215d94a7e40884466127d9803dca3b563d411cb9135c836700d191ae38c2c834d112697e32bbb5aadcc628d59c421c6a4c762429ad08928710051e2111284c72c17b8600e11358e3096075bb82006b1236a0013a201a400a2c423ae1b2f1d380210e52a413421e451be24ac45a40544be42211caf318aa85695a81a28b8e0fa097f6001ac8ba8f14bd4089bc1220af6ce9c8872e180c92ca13167d441ca4699838717c41aa2626431b2104726a78543e6cb0b63082d20968f3026f4d112222a44b29343be4823a2c2929c116fca9d108b30f1e645640c8155023943be481fac06ac860b87eb06ab8470062f9033a1d50b6396a80e7ce2ef711dec106fb0185246c4214e28443c826940665613160d2bc96803069365388ab0300c73c80a5ba1eba5430c594b5833628e5e322c9285439658137344dcca11d2b8e0ea3136200ac60817644891a635e405b18b1766b8f72f6580a1c51507b468204b909784c314d190083f7ed8550529c8f9c08c69b14e304149922f1bb0a2033d6ed0a1e2218801bac8820a2c149000120cc8a179b043f0bdc16d5db0822e5c5c610506a844694105144c70a2244911223d786870c30a13519038a20516703b74b06b0f982e40f0e417f5a50c18bc00851334b8219305960c5480021288c0036eb061069c0a25345192c4888a0d64608a26a88a3cb005155830400127861082061802b0822e595c410127ac2421256aa3464432b12e22e0d2850630c0c40494200287016c14a7020a2734312212bad9c8c4a8d880067e38000e5236aac208464488846e3031d145c0858a0d4091012626a0c4038820800d525218c1e887d0eda7b663334346f42f22e00202283490010c303181071041001c6c1880942f7029a020420e8d8c318e188981f9808ea91a6870d244270103a001eac901ae2f61197906e3185906ad06884564223203304fde097530adc4094308ff468fac03d3b066c21c1887a801bee1a2316346688a313018333603cb51ea78c9b8582d929549721cc31a779f717f992e75ab39fdae690bccd928ab4bd7e00e77cc720521dd74cd305d2fb3c5c5e66bfe379aba6bfa6efa6ffa4250ad6e754ff73afd5612ad23cc5695fbd2f3d674f79cdee67ff8f89a1f3e7e4d793bd7029aadedbef43bf6ddf45fa85c0a341aed04a322276774dc47a1d9f881bbf77093b5c5dd96b6b6fb7d3530a77d437093f583c9925ad3d7dde8ffcbaeef6e167d9b9aad6b83ac6bb27c2c2547d7fdea774d560d5db7c9ca6497efbdbc75abbf63ff5b76f7b6fa757ff356ddf6c82b6e4b6b7bbadffee56c5daabaee728187bb6b05f7edaa7ab783edebee33ad168d74e21fb8bb07eedec1631104ed3c906fdba00e84a020a0a09f205b904f502da82788276827482728080808e807c806e4035403ea01e201da01d2010afa01faf9f9b1fdf8fcd47e7a7e787e767e747e826c40b61f9bcde663abd97a6c3cb61d9b8e2dc807c8e7c7c7e6e3e353f3e9f1e1f1d9f1d1f109aa01d57e6ab69a4fad56eba9f1d4766a3ab5a01ea09e9f1e5b8f4f4fada7a787a767a747a727880788e787c7c6e3c353e3e9e1e1e1d9e1d1e109da01daf9d9b1edf8ecd4767a76787676767476827480747e746c3a3e3a359d1e1d1e9d1d1d1d9d9cce93ce76771a37471deeb6b45f70cd797fbbddb2ba144e78ddd4bc6a5878161e00ff37af9b1700feffdf3770f799bb8b2f5efd4d89251b4cf10511e8ee39dc149bb8fbdf9696fef6ba595afadbbbdbeaff6bc9e6edf1d2f6ffdd6db566e96fe85653bd73f58f1f3f64eeeee95efd93bd29cac4ff9b0f3ef8573fba9974e9aa815edc6fe115eefe6e8654fcddf40de6fedd74f7de3788a465db96c8896d5b76c80ca7dc69b4b0c9dd596e86ac190e719bf7e1e3776a7eaf6c92a55fd9f4052a71ddfdc8cdec85bbbb2d2d97aa6fcbe6b4bbdb70a7e1ee1cb88b326238630755ddf44b6d4bb4f4abfe95fd259b1aa06e9dd6e0edf6aa4177232d3dba5555ebdcdd74f71877877198bb97d807774772136b31b11479e6fe37175d836cee77ecbf41f6d917f7a62ad0755b9a0ead46dbc171f70b95cbe9e101d2eda01c1a14c463d3e9f60ece8fd5d172c1de9bf2a0dad74d79bbcd6179add6e5a85ac7aa39ad1bccf91f3ededd5fde9ae126d6c15dd50bd0c04dace4ee35dcc441fc47bbdd3575f798fb0805d7705b1acafe7274bbc9742f5fb8fb0d375d0a775fb7fa3e7ee71e55836ccf7d9b77d39a7fdae89afe6e627fbbc501b593ebee2ef78cba3b0e375d08b7a5bdbf1de9eed83d7bcbb6edfe553fbad736f8eef657367df5bbeedf5681bff4df54b7eff3d6f47df5af6577af33e9ee3adc741cee2ee3a6d7dcdd895577ed6e621fea4d751aa8756e4bd3ed269c936e7374ad9ba3635555e740b5bacda5aabb8fa1e8ee99c61756a55f898a3a3da431021dca982c7f89f1508c8516f2b766514330d8bb582ff15f315996223eecb18c05d7e3ec8abd4b16830843e1bf6452a4f050f831883024e252e643a52c16fb57f95278e8250546820fb9b08c85d763177e16cacf43af5216a321c2ba7232b1660c80ca4fce180015be08a7096b7dd92a6552ca97faa1d7b76452621fc30fbd1ec7665133292cb410fe6b16154a89e187621fc64c30a3a26232165a08bf9c45859f3f540a3fbf645278e8f52e99141e727d299312830843af97c243e59759167b2c837d1823a22141fcd89b103eec5b3229ae26518662f85978bd8c85f225231f0fd560603ef12f2739636200547e0640892f7e7ea5f0287624bec78ec20f923fcce412594a4a8a94388ba115fe4081d242fef05bc02f3e1407409122f543e3c7f0430e5072fdf82dcca0c4f043e30c4a0be4bf66ae590bad99d40fe11f9f356b81c43fcec2f76f81c46b598b41cb6b64d46430beb7ccd0667c1f29a6f8ef83697cd117651625c4464a7f12aa2d292fb880f367d1fd86a988a2283ec69809fba0f8f08f10857c114a53fef10d7ffc20e1188e50468872b6583351c634ce9e40395d2439be380b7df9314bc6d482228a61188aa1288a62188e327f1c3312937f24a2748c7128cbb227a1fb38f389ed2b3e396bca4ffe28ca9ee03899f2ec0d4ff2fd7ccd9e4029c267eb5db327504ed68c89244999387b129e4cfe6e24e6cc515a383751d6c630f397652bfc19cab218700ca21429073c11e1648a419c39e0890867f81837091f972c6f9dd845a129a11dc4f109d97865aa4665e21fb5843fa315ae727ace213ec32c1badf84f852cf8a2ca0b1d4e17eb5def9fbde5ac9993ad6705d56043d0944b361a916ba6e49a307b8c4814ab38ce68956bc2aff160e6c73e4a651bff70ea8a289ee2ff5c853ff83dfb6c1c75c83557ba1c651baeb28280d7e0f7a567fef15b331ff0ccefebadb0e5cc335f58fb80e9c33ee49a271a22e1493e945c73e4858b5cd82ae3c710c0b86a744171e2e4ea74926b70a9e5a5e574c97c4ec6596ae02cb79c384bd191a36cc3277612da601b5680d7d870551ea5c6fc231599b3f8ae8e62fe28594663943ffb13e2670eca2c5a3207b906565e9df89d54655ba901c70a28a538f1e3328c135f29b3380ece1c7070e2137f59d3c27c75711cfcb5294cfc3ef2ca8967f8cb2e2faa98ab0f428eacb135bac6a7711c635fc66440b50f00f97872b997967b61b917d2bd54490102d308d2ca8ab4b2ca02e363245ba4d52885ab306915b658e28fa4d599b7545db5b21906084c9fccd49633ff0d2abf85a37bf9537455e4af99f7f0f59099bac23253e596d90d2a2964a6cefc4f3001d4d8b20508a8a685e9218be55381b830f1493aa903e959c65951ab659a3870f808f1873371cb1983336b9167315bba307d6616922a4b8c17afda1437ad56abd53a811752b6b400b3a08a2d5c6230519ec1a65aae1a9886cce73f9af13ed7629a2d9f49a335c3868dd1c66863b421835d993e73cab49265732adbb23a434d21ce70ca19be87e6d454f8a655b6b96361ba125d5c215931221b33d894158df7f0c7ccdfca3fa30b519eb51a60cabcafd525fcc143648c23b6311b471bb327ac28a518a5171fb187306597a96c0b6b59982fb66032397094256ec95ed83467cc623464a6cc679e38fe2467bed1cb19be4cd9c5c638e6318f7926838de1933125acf7a955e490796833d674720d14262c4a8c6cf302930293cab6a96ca319c32791b0de97dfc68cfc8cc7c7f1396e601c391ec72be5781c383e470e73eaa4b2e16b9d30d87b4c2ce66338727c3c3c3c3c3c3c3c3c3b3b3b3b3b3b3b3b3a3a3a3a3a3a3a3a3a3a3b41414141414141414141404040404040404040403f3f3f3f3f3f3f3f3f3f369bcd66b3d96c369b8f8f8f8f8f8f8f8f8f4fad56abd56ab55aadd6d3d3d3d3d3d3d3d3d3c3c3c3c3c3c3c3c3c3c3b3b3b3b3b3b3b3b3b3a3b31304f463f3a9f5f0ec2051f5dbb2bf9f91bbdfb8f3dc5ca14ab73b4797b67bdd3c287555f20bfed23545e2c4aa7be8ddae7b124155b648c856731a5d37f089e86d77ef55724409aa87dcb110129bc4bdb9e320770cb4b229d29bfed223ed86d2477edcb1cdc7ddb776511d5112919047f59010b7fd05dd95d5b92f30b8919c76c15fd3575af70a6cbb7b83b927f26ed7d5a8caf67e65bfddadee776ea728fb3efa6e8aeed549035da855ff9e6595da0da5d557f5b9d9774d1f18646259f5d774f72fbb0a599dd895695d62e3c4aab9d74fe3d176cdbb7befa034f0d9f757a3dd06dfe04ee7bef7b669eeb5eeb93ff675833adaaf5aef9c5669e87e35bbba2ff46efacf5bd39597baee09dc718f3be631dd2774dc5f76c5b4fd6393dc318ee31b771c823b06c1c695522032c12809378468881221498870484a909088704447960c518204e7e444a25d54b7d7cdc42a21010a02f9edd768088f5551a3bda6bc9548484be38e1ff7705d16b8e2802eba5b1d7ce2e27e4177136d55ffd235fd3d71d9d438cdf114d68b7b0ee7ba1a886360de55afe92f6d756e8afe2fb86ed7dd634c6eb2402af097eaf40e27e62aba7fae7672d922de9a3e6dffefd72ceeeeeba64f44fad543de75b7bbb71ab93b09c20083b7a628abdb43d08da4ea571371f71188c0ddd19465a25d54d70271dddda2a8c63d6dd4a8ddfb084d71eddec0146a3eb69e9eda8f87bb6f209412d436c2be40a856c7d3831ae9ce1645f56ec7de6ebb6d59a77727a9d982aa07eee866aac10786077b3a1648b4737a88081213743d9dd228a877efb4db7375abdb4ebf6bb6e0efd21762d2b1aaee55553b3dfb3e9476fafd63ddd7ea6e6bb6e0bbe9e7b8ee5ebfa763815bf7b2aa4b34740ebd4ffb5d59216f50c96a440489c9cee9a11d262dfb02d356e7ee21bc74b7b242dea79d8408121317755f564d3a9d5855d536355bf06f6ab6608d6e4db57a04d5ead7dd3ca856ffd2f6880812931dcbb640dcae6d52420489c91b04be4116b76b9b5e564d7a59d5bdddda0d0cba3c293ba7733837e8aedb5d59dd0e04ee0e04eadb0d8703ba1804ee0c90a1c9dc851ee4a5b97309db6e13a086700e9818ee341d8bfe568328bba68ffe8e65db2330dc3de7265ac692d2d2afe9bfe9eedf20704d7fc7fe2fe8ae671bd4e9d8f51c02c4b3b304dd4f2991d60fba2b7774c8593d774fddd4b9bbdb7696b4fbe876c301dd77ddba761fe96a70f70e306f4d7b2faeddc09c18ee4ecb9dc0dddd7f6ee66e3ccd01f1ece0dc7603d5dde3e909da76824041209e9da05e62890c2542dc9d8a0db8ab6d4ba46355f52cc0520a1bb4b82a1775f8007035d94835d6a00204dc75a788c2084a051f29d4e07a978296deab813950af14ee2e46f1af1b44fa1f6fbbdf7b35f055f5bcdddc5d27e71ec141775f72330596bb2d0daa7dd79d03d43f1482af7ce48410bc9a9736e9d0881d4426a415af8bfe8f1faf6b5d1a643041f59dc7f9f13ddae6a17ba3ec9ab377fbe67254565782929190f3e814f23efe094d4bf840093edcdde6c35354c28d0f11f585b71c9f8073a2e364097fd259d9d44d9b52946787a68383a36a1d7bc4dd3b70cf276822e5c05fb0491026633009c3dbcd6bb5faff84a6cf64eab7df94499049ebee44dc6472e34ffaf7bbf7ae4e6ccb03fe82bf7b2f13236ccbb67b8554bddd703821e86ef76e171c7247a2a485dbd2da9c9c9c34c7ddb1dfb2ae6e37ba91ceb73dd1fdb24950bd8b9c76922454efa2251b65852405a15b0d04feda9ebb834f369290750395007f6d115218ee5ef3bc87ca7dbb812dfb43a272f7740dd6b848004072e2ee4070de75bf6efab47f65d7d7eb76df4d1f299b248451f30bfed227fd63797b2541879aff27a1258135490042820eb779205ff33a1ebb57277d8404a9c815220e893e70b88b402ee68832f308e8eea31dcbb647afbba69a77b2445c346925427275ef89682f79f5af6589286139f0177479ed5944b7fa68b753b23ab143786b8ab69acdad4a5cdd6e5507a5dbbbf4886ef511922dee3aefa6502ff0b74940320449049050b99be29efcd0fff12eda6aa7d58945712612134c244b9084ee29a07073020aaa0ebe3abd7baf92233c1c79724487bb7b10378d70e17f83a64b4bbfd59c5e6a77138bdbbab4a9ddee1294aaeae0aa7fdfee57abbf73bf6a1d4dd5c135fd55eb9ef6bb796830674409234d9e020abf5748ddbff91bdd6aa0eb6e1ddb027fe7d0a674eb76bb7f6c92bbef70d3c8691a61ed360d3ed9eddfa0db0da2abee7d4f49c9085d8c6095020a7fb37facee0db2233c71dfb1ffee1a448d908c90c41c41c73d05141e4dff041468488438f9475bed045c82a638744d8dec967dda2b10127ce8369377bbbb1d6ba4e4dbad11ada8044edb2fdbf438bb05ee47f7bbdaa9c801454e965056b7f4b48db2ba27c2e4e8752e6d756e2ee87e9b7ebb7b6fba95cc22d35d37f0f7b3efeb560373dc2c72c5ae4e7bfdd32c42a506d1ddd3b9c1d309aa770f35d26d8f9b456096d074e969bad547af350a0ca2ac0e5d53236bca84d3ad06ea704d6eeaee75f3d01ddcc1274050203d5717a17af770ba27ef1a6475389efbc33de91f4f4f70af90bab8cdd3aebb7fb827fdc36d9ec6fdda74b7b23a20a8910fa046b277ab34e4426d26144dd7d408baa64ceefea5efd34687c4a0e54e669cc7511445310cc330c4243992238b1c4516c96291ac18864eb0a62c1087eea774a74bdffdebbd69ab7386a070a8dc6f346d4a51e06fbfaf06e2d9093efbbeaed6b59bb7df35454237ba1241a17ab7eac926799f761274afecefc7ea706fabdfa4a3edea7de465d524b7a5e9dcf497a373d3df939b678dbb83ae9be6f474fbfd17f842e86e378f97b6ef6e83e8ebb3c659c3fd6f40a006023510a871badb6ddd47d03535b26b3a62774c8cfc8cdcfdbe27d49bee24de9a9eba9dc45b53269d1e7a535daa029f88de96cd9da8debd932df2aedbdded51dabeee463a42770a0cea744a76cb0a11f2b451216f3a94618038d9e90e0808407600d101c486bbaf6ccadbedbf690ee7f5ffd876f7bb655f17d56c8672f724377fd47077276efef0c15d53fd63d1dda47baf5f5553a77751bd7b37ee8ddbee77ff74ab81aa0e3ed92ece9be6cc1fbc8db23d9ddeed1a7c74b7fb5df7df54bdb6fb5d53b7a56d5daaeee0b8bf7dbbd59a6e3777f3f4d19346a26a5dca638f3c6924bb297d7512269d7e93a05af6b7913c6924bc35dded11909f6ef551cbbe41dd7ed2ed27a25dd35190ddbf5409b8dba35dd351cd167cdae8faeab62f17745596cdbdc0add36957d54d6c0b046a1cbad1bd57f7655517e7c4f65c1cbbbaabbb55e04fbb38560da2ec0fb76b9bdade8ba64eb8ddd3416975a3b8decbb67b057705020cb26d1160906d793820a8d1bbee1f10d4680814d466c241b5bb89d5b9290a043542b78bb269bb813834059e1e746f17f763d155b740b62e45d714d784d34098f8d06d26406cfea6c6876e3371dd1e4ed77375516ae40d02dd740854fbae44edbb5bbddbdeab5bad438d5af6d5aaca0a418d80e8e4dc23b75bbbd196bddd74bbe94d737ff3a6433c3d412eeb4e81a96eb7ed46d7eda2463c3dc19eab8b8090a046ea5e57dd1302fcb545a851cfd5451b6585ac6c7abbada9eee974ab81a8de453f5adcdddd4de99bfed21688db2dabc3bd2caa995416d87375d17e359aa2ab16d27375d1cf0f1f4c9b154773073627eeb6d3b4bdbbff0f1fffb725de9aaea9feed76e95f57037feb56dd84ae5aa7db282b44000150e1879b3e522edbee159eb7a650eaca3eaab7faecfbee9be63edded82bbb4e73a6ee2eeda4d9f96ae0707c7e6660d8bbba3fbc90be9f4a8c0f3abeebdefba5b855af5bbfbd76a37c8a64dbb3d02020428e843b799f87083ed66d2b1aa8e3d92be6a1009badbbd3ab1403c3bc1770db23f5d939192210677dfeeca02dd8df4d33959535eda2a519540edd213ca6d77ba4b5f3609ba93f6e6e93789972261f2d33da12dabdb485bcda5bc3548e4c87ed9df46da61a2c384d7d4b6444c4a8894dc13ddbffd1afdf66be4be691321fb5d8d7872fc0db63b5b5cedf4aaaa757f9a3b53eeee34732788ebdeeb5fd3df7ed3df2a6f4ddd3fdd1dc7cd9d96bbd3dc60ced4d900bba23c3d411d1cdc7d899b3a3e9ca6aadbc9d57fc35bd3d76dd3ddefd87f1a94aa0677bfdfad027fac0e4dd177f7efd8efe95e3dc4c3e45d77aa5bcf1d26efba7f501a88dbaedeea4672a1b4937aee9575da49b6ae6589f0d654d7b244dcdd6e8f5255336177ab917e75a7bb6a60126f4d6f37de9a32e9d7c5a92c9b43b7aaa6b9761ffd581d909a2de803880fdd66d2b2690bc4b1ed5e616557dceb6a55035ddd12bdad7e5d0d4ca2e5406b3d6d74354ac2f9c1c451427bddcde305dd7f7bbcf4f71b08f5ea6f77ee175c7ff7de9bdd7b7f415eab7befef77da4994e0f0b8d33610ea7df765d5a4fd4b9576f089062a41d7d408baa64ce68d14e64d949b109c96020a2fabba37e89a329d8002cddd3d57abeea3e87e5397056ef4ddb4a7f4b49755dddfbff47d1b5dba4b751be8e2a06bca5483aefa9770db5dda40a0eefd12baa64c4bbfeea694b7631fdd6fcb0277fb5bb7eedd8efd55bf9bfe6e774f094afdedf608ddaa9a1ae9b9fa4d4237bab243aecab247b96012da6aa78da4b2ec91cab247e8cae6d625abd1d66960528f176457a2974db29b5277ddbb3d84b7a6506cfbba7b08bad70d54b25ffdba2acb02713f76fda55a15826e740db2c01f2bc449b768ef4d9ba8eae9ba5b6d370e08d4b7db5e53756571bf959726e95235173cbadd82767c7c785a170411bcddef9b3a296d9efe19b59bb7a64480bc2335d53fa2974d9172bb89aa79fb089456816e3a44e94d8770fad5436c3e3c7bb73d2526add36d23361f1edd4bb2f9f0b04e4a9b49ab433c3d4e1a69a7c74923e9f43869a4201e278d04c4e3a4917e789c34928dc74923f9f03869a41a8f9346eae171d2483c3c4e1a4987c7492305ed386924a01d278df4b3e3a4916c3b4e1ac967c74923d5769c3452cf8e9346e2d971d2483b3b4e1a29c84923013969a41f278d6473d2483e4e1aa9e6a4917a9c34128f9346da71d2483a4e1a69af3629b8594395d6d43e78e20322dc5175a744a0def45d376af441cd918edc9d3aed24a60739b85cae170d2ae772b95ee87edff4b7df26c7c571735c1d8be2aca9fee5b0298de6ea583427e7e602dfb75793c2bbeea67dc2ef9e92cbe57add6e1a88aebae5bdeb6e22323d28721aba7f4177b71eccd0d0ed0635f0df75b76d50c8eba647ce77bb38b3832adc69e84e552050ffba77ee5dddfb77bb0ff5a62ddb7b83fa9d7692770db242782bdb36d1eb12774f82ae4116c9893dc2b24abb657549e87edd60fbae4b94d0dda6ee1eb2b242d08dae4176c8dd4970df2ba449ad121c0e88db6f10f86385aca9ee095995242d61d2e99caa6a2250ba75f3dafd2372dfcd6bf5cbd3136ce26087bb27d96a4eaba96e3ad7252fdb73817a4d5ad9d475d35f2e98a4b22c129749b77bbb3d3a02f5a66a9a4b5dbd89b8bb91119c49c71e4193a8aa161254d9a4ddb26f12ab44c46df7cb32d92d8b844dd2ee26954dbadddabda64836ce1ac4d9aeee0171d2570dfe72a0deb4d54097a75522ba7d9f8854f5d4b92992bd89b81b08f5de6eaebbb2aede48403c3b415917af9baab7dbeba62acee5e91c0ee8a647f6ab75a90a0c0e6977daa477d1cb26e1e9a1fdd349abd1cb2641d7204be465936c944de2ee45457000e2a66c87bf1facee665137edbddfd3a53da7bf5142b71b940a43ff057c0dfeecfb2e4fabbb47376fd53d1d90fd1d5cd3776575cfbe8feeb47d3fc89600a58753a10717771e9a0e6da7896848099252d212763c05bb2d6de7d8f67577683a341e9c9f0e6da7e9d7546d371212c8dd95b8d9e3dd7db7c157efdd2fb96b10d7c4b4f4bbe7061fdd6a4eaffb77ee5f37f87b74d5afa6ede3406da69f1d5a8da683c3635522771fe2ee42dce4e16528aa85a3a25c887a45b94745798ef230ca6788721a68707fb9cbdda5dc0510e5aea82817a26888728f8a128047bde0ee6e82bb97e0e68e2cee4ebaedd9a1e9e0ac4e6c9bebb9bab74388ff9eab8bdcdd77ece0be7f6cd25ed9244aec6e35daa86e79c025afab8fa46fba1aad90aa7a4d891041a940e0b95ddd3b52e2ad69fb2621e180eefee9f5d569e08f1572bbe936caea9a787a824070ed063ee91f938e5573adaa5ff6c53de91f101f4ffaa74bdd356ddfa78ddbaece01e1e909fa68f7d193fefd58a59526096ff7917bbb1d61d2b1ea101074038169fbbab79fab9d7c283153c3abdb6dcbeafe6557f77f973eba5fdd063eed77d3df3da5175ac2b52c6e09dd3d375dd3ddb71b55d3e090dbd2dc37fda56a8eae75737efb75a1dcfd87bb631f8eeea676bb1b69ef9688ce4d7fb79bbb6e26f6bddddce0abdfa4ad5bb5aa8750a377abaa3e5ad9d46d37932477074a01e885808227b850fa68ef1dce85d240d7d5aaaec9dbe3a5dba9090feb49bb41f78daa7f7b600c0f4c79e088d3760ec943e9d4adb4c50ab648c01604d82288d35c28b5d52e12214e1e6ad5ed46d2220a538b02985ac4a045cd69eeebee57ab4b8f4343770e6dd7e0efc7ea7ee774a95655f67d22ba62e2ca862b2077a7f1d6f4a77b5a7a1c9acd0bdc5af7effabaec2f874488939abdd36e52164bb24882c51858a0008b1ab070b190b982892b9c5c017305698595154eac10abe8a28a1adc69dbd53d5d1b04fedebbdd7b57fdbbf46f705a1cda6fbf29fbae290e6df75e77a3ebc6a1edd83fffb604d5ea5497eef6ba734b8f43abd97172636343a3d5fc4d0d8f93a5df395daaa64d9e36e46f4bafbbd1a5c7e1adbf63815a476372d3f65fbd73e8badd5f77464fdbb1bf84bb59faed37c5edbd5bdab1bfe4a2ff7b13d12dbb5b79e96e7713ebaa5ac7baedde3171b30a1b1d20a3036474c008a7b94e4b4b8f437b591609c9df96961ec79df6a6501a891027dfa6ade6edf6dde03fabbe7e26f774df76ab2c9bc3b929101ba0e0df00056b7cac1a48fb0487db3928fd73b3e90b379d70b8e924829b4e25b8e904e5a61301dc74d2805309dc6c9280bbdb663c87cc4608bacdc43940012d5b3c05ea1c257727c14d2d50dcbdc7972f5fb4e4b40c71f707f27f6bd957b79b70de34876ed43d2b41371a54754f44e85e973001717e2bceae6d72f719496e5261c493edfad080683a38e8aa5b2a8e72922224478c8c6054542413d94000dc9636c513ee7f7bd35f2ee87e56624eb181ac4d8d294516f7efed6fd99d7800d100a3894b9089dc4587c203505c91a2c85d0a9b28c800f25b44914514556ef33ebe46f6b7d7d5ea1a857a7377cbea3e85288ea2088a02e87fc8fe6f299c1005e9eeee9be6703f649fc3a1fb0dde8a2c19c28142cadd715c1c746f1727bcb9fb1137a18069c007775b5aceed968500349fa6675fadfe5a76d76e20dbb63b88a6fa686f22596068ae0b4c53a85daa027f7a3b2d2d017fc1dceb7f37ef6fd8a4a72d01d3977d9f7555fd6efaf69cdca5a5a517b209e185369107e143f8bd77b89afff1c0f4c5bd5b15b201a66f8d4ed53adc8fff77ab3939bf5b76e5a537af1b6c9796969ef650b9bfb101a66fcda37b85d4c581525bada6bfd47dfd6bb0c57957b6ddfdaa754baafeb5eecaa65a0df276fbebca02d3f7758e7df6fda5bd77b82515b804f5a64bdfa6bbc7e69e7d7f064cdf366d9afd92aa9776aa02df540dfe7eefdd9fef7803176948917ef5903789bb5f960950b7baa41758546bbaddd0cda4d343bc34f7a6ae6e899a007f6d91d1bb935c9749c71e71d3df8f15e2ea5220abb4429aa4d58da4badb6555d44865d92324214f1b855a3530c96d5997a7875e373dc2a4df2455bfac92ab5b22d7656af5afddaf6e02fcb5453d571725ad4bdc55ffda7d34c4dd6d242217a891ec9765b26bda4112dc8dbaef4e7281da09ea896d5d95dde913db1eb52c91bdc2de2d1157bf7a48cb127181ba557237943eda297173c12425ed060693dc1fab032e69370fdd4f3b897e3712571fb9ba35c8aeebe9be6beaea96e8d4af46f5bb91003512d7dd2971df55a3ebe69d282b84888bea5de4baee6e79ab11f0d716a92c7bd4737511d4d0465921aeeb426d23aefbb62c503b01c5dd9d38c18924276aecfb377fbbf95bbb774ec0dcdda909319ad8e27f637b4e7f836ba20977a3ff06d99feea989013cddfc4d6736b1a38916cdd5e8aadd149501b6c28a112f8595279a3ec225b012c49d96eed276373dceb422d252309978826dbf0913528664254ce0d84d13e0c29d862659da124386bb2f2180256ceebe832a0bd4bfdbadca167787da4cfbd5aa5fc0dd5537aba06e4b03ea5f4ecbbebaddebe655d1f91ba685e15c70907bc12cc700ae818209f700cee11198f5100130fcc726bdcb4b7dbb7d219d07f23d3708fcd70d8e10819ebb19811abdd11c95a007d1ddfe9afeba4409224c25aa9490622a01b451f5cbd6fc6f94d56df4d9f76f4bd8a6ca74b7a5e1fc744f393fdd53bb5fa046350e4a77bbb51bf7d33d991030c21f4addaf4ed1fdfba5bfb483edbbb4b4a638dcaa754b10b881000e045cb9ff2d09205c2ed7cb3d4188cbe57ae156ad0271b82468c07f579db6bae9f7ca0a5152552dc4dde99346f2ee4e77ec9346f2fb972a11d16c760eea05eef697748ff33b4b35bfa45b7a777d59957d74bb3ab79f7d5fd56953dabebaf475736c0b7c37758986ce219e6e77931e42439974699b63dd67d1674f57f7f6bb4379eb5673a9ebb24028f575b5dad3e97db3fbd19eb6d3a3ab661f6a33edd5897db5fe1b77ebd2cd63dfdfe9ab1fdd2d8b03b5995ebfbb9ebcb56d757476cea7d96c57ebf47edd35fda55d9085aa496f762ee969a8ba53281dca5b53a876074978daca3e30f8e85ef556d15543b5efbaee377df64ddf892d7a9a0b0cfe5e53f786c95dd3dd46939e66e3b639f6d9f75d4ddbb4b4e6a1806cfbae2acbea975dd75f53269d6edfdf4d9eb69464e97770922c3d94ba9bd257b781ee6eff06dde8aadddd7bd39ba58d5bd9259b2782c444bf1fac2c5ab3a65a05be1b44d5209005fece7dcbeeda0dfc5dfa4bfbc7262dbd9bdaec5abd555dcd43b5ba6581bfadf43428cdc4beabd7a5a4a5bf1129294982e3444913a51dbba4a3aafadfedee76afecbf4f7bfdedf4376eaa7feb56254f5b2a522a9284e8084e29091192224449b8244a8c94842c41524a4aa244080e8968489225c0e0ef589c7fdd547dd4757fdddbe9ddb9dfed589c8deaf67dfd9b49ff7e3779dacaa68f9444a7d7fd6efaf43477c7f2d0ad3ab12b13ce9ab23a1cfd3d5dd07d77bb2cb00d026fa05aedb6bb09d5bfeaedb23d9d9b6aa79ba7fd2d4be469401b9c95fdedaafba1daddb47f33a56acdbbe96fb7bf57f65dfdbc95559f75ffa6dd4ddb89edfd8dabb23af6655be0ebd21e50b7bc352de144f2afd1d37eed7e5f97aafaf75ab7eea0ba5bd3dfb94fdfd57fc35b5335f7d3bc6fd9ddcebd929224af94b4044950b7937e83a781f0ae5b4ddf35f83aefa6e886527fbffa5f56a5b93b7dda4f5b92e982edebaeac6e0945b5767a77d5fbe70665bd974db23a2981da46c2ed42ba067bafde68cbee76ee6b3615785e55774e012a4df7bc356dd99567949dc857e92fa96acab4f42dfb4b9b49bf4bdfeea6bdfe767aa19c81956d756beaa6b4dfb987d2bfd70dfa507d3eedc65bd3b5d54d6008c2e7ee4b2fab262db5b4aa26771ace9ae6a85af74d69f0c97ede9ab6389b690db2bb7f7645800d775f5ada6e50eb76ee6d35f0ddf4770e078829dc7fcfae0f830f5f00d93fbdbe8f1fe51426eeeea40c0257dcdd6d70e001512468b09a7b0a016a50ef36faaaaad19465d26e5545d23aed8463d1a08b03ba2dce4ddb5e10f843535ceaea58dc667a5dbde3a5ee13db02d120b06d6a7bfacdfd744e56cdaaa813db0a71db0d04eab6098b065b8dc4ee96007f43daedea56eb561fb94155b71ab5ba97e4ea561f6d26fdbaeb92d7d54492bc6e7aa4dd2fcb8497ba2c1a4cda3cfd4b777bd432d948ef12253fa322aad6a52dfbba69910b4c7bee9111f0a75f37a7877efb75a192ac70c75574c01d73c01d6ba16203ee780a29dc711459a070c71ac8e2eec113ea896d8fd4ad5bb5101e1a1cc11d62d160126f4d99f49bd4ee9dbbea5669659340ade7064229413db1edd1e8022fdcdd3b709387c72d14a0276400281cb7f0039050d4113ddc8207aadc1a80c5e3283657411cb09982a3c8400514032a3d1c858c2c403a3a2085a30670f403645605bbf003876dc90257f8050dae7ca25c55c101903dd5aa66d0054b49a97aae3001c2308cd185a70b271ecb10c6ef7c51400b0be0b4daf92fc203f8608b8e083c41f054045a392a34b9820b1073446b2196051b601785f3c36f0bae6241d18d151400e129646c09810b182d3889ab2310c6489ac24eacc00b08333f27780a28aed8043945e12eca08a3c6495518f8054ba67ce094c4962cd34491073b3c71957b4cb8d201952daa64140a00e4c00a2fbd7c030853361003a6284f11b4b4018d0f12e62861ca8c8707286ca2801619093e964212245d3d90889418e402d4e38623ab71a67485c711265c8d2ef0c2774cb11db3f014c4ddfdcb972f5fbe7cf9f2450431a0b9e000363ddf000f576ef8420557bec40e585cc1c1f73e44951cb8c0adc08b170003a8de20862a1e030d270c5d11aae24f1f00820b0644b1e46a0e5cb06082091dd7338cf1147cbd3741000e6601299c7892086ca1c3005ef8e0442290b3e3600a2d2e020c52e450c3011e789029058a1b2a50e53d4290907298529c16e3010315bf731b156e7861053a7b07365b7461920d7159ee87a55a1033df31032a1c100108513eb3020e4a3086891d478f0aa418d90003dc8613a24f1732cc380da90d723812250597a1018a0ee4c004041e738107c200b92a51303521e0220a1f550eab82420f8d2b2b882a7f475e8916047f09e1121546039a7017d01248407c114454ab480242d48ae845b19ee880210626253889842a8221acd8e0245e871855c0c8c7fdb213ba8809225e454083272e1c5c3481003718112587202109ce2f32e0b057c20034250058021251618d9880076ce0e54abe02044308b92180a85cc30c2ac0273257b2119676871119546598201e62285a1f5f2dc1c48d1460d1c1538a28b20495cc16cc020b0e38620c2e8e3013252ab0830f0f8f7b3e40a4050d25bae01a5306c059aa31e55eda011821638a28ef420789ee354017cf12e5f5600322a85c891d212c80e551e3414c91be3664a0e73898f0830a1890227f2187da04608cd0e2c123588e44b1c0114f711e8022628a2a578188232e0d5022ca97b87a48a50829e1475014406801d07d48d58c866aa506ff31c4ab0a9b1c56ffd133b4f08362f39d2b35e30b525dfce6014756cc9e90c26b3cd8e165032a1dce810c8e08511490f2df3d490143110f7c074d4f035c4063023ed3411122183d78e11ab0c08114040425e136726c20891c017ce033c4400008922a9084cbb08830c5608391c71cb1b1a3c444178f35c18227702c8139ccc9082c1713a07979a38c09e0ace003ee7a410a426a844006de6a010d2f6ae0217bebe581075270a206670185413355840b9c84a2022439d470021fad9881012b1690fae843cf50b0010c2e2a41a3430f3500e06290950482f880021e46210118e070457bd843414f081260f1d0a4c586e000099eafbc2d6ce1ea3c3f5d1106f602e719288ad2113b30c7654060c88b2a40388602cb0b02146c8e7d988d0181275870dcfa78b3b07085e32354c19c1a193806210b8f1e51d43886f1b0d8ada081fb0a78743042d9c1fdca004414718102b85ba000395da098b81f21c5164828d9e2fe030bedb2e3e63e00b3690491877b0b4bb42000040bee4fb00001168f70df0d099baac4dc4f00412eaf000c7724194ab062a689fb081bf438508014f7a11eb0e0e1018abb9015c01704d2dd8702443c21d8c25da72895420336f710701848415b86bb072fe0020211a1fb068e45a7c506dc7b3c7962863204e0ae4384251f371ddc732ce58431c60ef71b5138213384cbbdc60a4f963ce8e13e63370de94285bb1943111ee00106ee3057356ce10013b8c798d8948005e85e8e01f3438b07eeaf0bfcf80a50717735a0870d2114e1de4202ca13113cc09d35c5da43141770676514680328813b694405af608cfb382596450dd97dac71c5163086dcc5a90248b9a2ca5dcc81e5e70729dc432462ad1b43b8874a6ea84610847b8e41f9820d38eeb90146d0288012ee7907c24672c33d7fb084950050b9631524801625bbe3268004e084803bbe410649b8b0dc31efc807af07773c94450a1955dcb10c0637f80871c72294246c34e1ee25c038701ab87b076c57a0c0c1ddad64414544c9dd8910a2013688ee4eb57f1420c1dda5c090c99273f715c61003034feebe02408302b8dc5d27c210377cee5e428d009030e2ee4738605737b87b911640e590ba7b900458f1040adcfd661260aae7ee359b265b9eb83bcd0315d035e1ee20e0d059a20177ef408b0f50a2dc5de6c557c509eece434a91cfe6ee343ed7cb0577c7513543101bdcfd468f1c804cb83b8d2eb01051e3ee325bc010c606dcdd746d710030dc1da6f7ab71ba3b6c05ac06a870f7b28a003ea6bbbf9e8891c3cadd5db9199a30dcbdf50b410b65b83b8b0051298880bb9353a84033b83ba963890b22ee3eb2b0440520771753102c42e3eee24a7365c8dd43196cc04ae8ee610e371f3e70f7d04633d2c1ddf3173c3c608abb671ea2806881bb67a5928b6de7d5691698c485da465234a8579eab75ab812bfb4b774ee756ddf268dca0c4c111ee9e6f10e2ee7e830f2003001001053851010e77c7103822e7312b4368e0ee610940a4065c51e34b0bdc3d9c9200228e683091e5c7ddb108043841c9d40c3f5871774c83097ad8b4a38f06b83bb64018a58a11ac58a1e7ee59860d6a2020041d401185bb872e40810a491881811e50e0eed909151db0c2d1c51538b87bb68209408906891218b9bbf8e30724013ce41b24dc3d1f21124104347c40030cdc3db471839614f04065a503ee4e1aa0005eb015b73282b87be6e2021ca090c143087edc5d3cc012ab875e14be2edc1d5df7eb6e271bb60cb9b720e5516c1882515073c729c05b602bb8890154895bcce0ee466e0ea0a65ba15c4c440419cc1aa09451032c9402e4815a8773daeb7801ece3ee98080d1ce8ecd0767074bb4955f50f13e12138c61d014004b9bb0fee329cee0e030c2d90d73d15c1f7bfae56ff8578be03dd9abaaff33f64bf6ed56706284d58468c1f2f70246cdcdd013320867af0a91bdc8f9c8908ae434a03df6a2e97ae3f3c341d9c1c8ab3d3f6cd0dd96fda3aa17ab7355b500627779e26dcede6e280ae0c1bc8604306d1e66f7ff3b8dd6e6b9a2839b142802e5050b799b829f07453a0e6ad2af097c6e02ae2ae731306d7711b65d7b47d812e30edb9edcefd36928db22b6ea7ed2ba506b64d9b562255efa4f648b7fb76bbdd744be4e3e3e3e3e3e353abd56ab55aad56abf5f4f4f4f4f4f4f4f4f4f0f0f0f0f0f0f0f0f0f0ecececececececececece8f0e8f0e8f0e8f0e8f0e8f0e8f0e8f0e8f0e8f00405050505050505050501010101010101010101fdfcfcfcfcfcfcfcfcfcd86c369bcd66b3d96c3e3e3e3e3e3e3e3e3e3eb55aad56abd56ab55a4f4f4f4f4f4f4f4f4f0f0f4fa5f3a8b46db0c6a11ee598526806600400000000d3130030381c140b86c3f1805898d46ced14000787b06a7a441708b32cc861ca18c3003100000000000000603411003bb009f881cdf5c9f9cc01e584428b6dc4a868b92c785e5d0b7d03c9d36afe363b0ef41b93f7f97d7edc28ce6504cea222ea5587a4d67d098d44ba0467a98507382852b74bd386dde2fec3f01470cb9e5b0e2e3fbb6a095fb5563c0042915d82fc1c3da803f77bccc5f9974431dda69e11728d024af61e7ee079076fd04b3b3dee4248ac20bd350a49710f6170807f3e24d626c3642deebfb3bde19a84027fba0335001ee14e046adc8b15bba3ec419df51de8f72ed3f10024fb0d432bfdde748bcbbf3f07cf79588b7d1b0ef06a9d7fbeffa6c2bac88f6fedc5a2bb5baa3b865393a34e6f00d8737a033714a5a3360c4a2c8642b792e3eac76c167070df9e4159af1a40918db014f52ce10c72f67d14a7c4ab734b2719e81efbfa7f467627b37e68322122e291011ff6e4168e0de9f42d2b8ac566287778c2ed2230e7de863e628a559f7aae7eea2348c4219cd34bbe917aeb44cab8798b4d60792828b32eb88a0d88c6fa9a83d9c4db972dbd1d8a32589b0c43b5b4b47a26145e3a07223f2bbf7afdd5744c1b6f686f7f80784c86c478757c49c6fb74b364ea3a9fae43853ae2b7ac0309e40d42d093c6a806c6aaa2d1bbfdcbd238333f89049844e56cc150aa8d275f3fe87e8be50a38b5dc76ff62019d1291e0bec06dfcd7d0d04b54d573db14e44aad3d4f4b66acde7731229e486edd830114a20a80cd485fd8ee73e3bc5f3a4272838be327120a4c5fa8a520defa11918dcb2a49d92c22c8d2f36febf1e100a9db2e05332b88494a7410a7c0520c769ff2bfb9b144a662015c0d5912c4d074169e54a0e1b48483c8b00e958f1154a35a5729cce94568c1a23c0099d468de0052208944d5b75ebc8b7a58a4d342b0b680a5564b4a2c6e52843a1f684f51cb0a3d0a423f9b7c8e5424528a8b53484ab1eb66f9010afc9171915cc30c9d4c03859839620383f6e08d10390ff7c41f20003e3e5003f53916f3cc91bab34d3826855fb566039e049a1e97c1e0d7c513f511b4ec70b4d62134252c322dc24d7ff95f7b561232eb7afcbc6557306afe61e3962f4ce8be12ccc1b04f32d219813c867153e7031ff94453436fa0f3442240d3a7081da26f0ff64923c04fb9fb310a5809a3ee15b7d163a67fc48a2c28a4080109cca3cb3f320a2dce019458ddaff6c2f0acd0b90440481133ace034c219a01ed1ccce00d9cebd3865b8e7a0dc1218dd3ec703781d6a61ea6466695045d0cc245e07863211c8d15b0161de1ee0b23f2e85f14bbc8e03e8a659cd22b71aced200861d723173315636712240ce1941bfe07bff0af7475ac6530f004edc98ee932fc57bc06d74f337c4d38ffd6e27c809401323f2e72026e779c4d265b6aae7f30afebe1facf0d5be5e8975316428e273e504e457f6ed82900fb840924ecb126d47fba8b2d7a28897e3b9ab94fe93b8da704602c9d74525cae2bf0b470894cbc0b7638bb198ce8767354aad04a790bb1e450de7a723eb5dc40c71a61c9e80d11b98e99726283d4a73dfcee2a88fe084c3363a02662c16c00d32cd9ae7b07747a4a835b2243eda76b8741c803e7b8606ca4ca6c160911d949e501dfdffdb5462e354576a5230aef66cc3e6ac50ffbff1699cebc5c44e01c5343cd82965835e3850f9a43628902c58c5c0c9aad5c52726f23f965ca39deac0d17b518d578c056a3383491d6992434d011e22e94e27b78a4f668a9f54c5443a98c32855b20c330f2a2251cf1c422ce4404414c0bfb8964f3771d3a8ba5910937b692fbe1071d1ff180df23eaddfbb97e1af4d35859f06826ee10c54aa7144c70a8a4c06b45e5bb5d2b2ad654db163e3b058cb80afc9b8636d17f6dd059f8b708331ecc17cdc3d8f42604571c5a04c724e6f27490f214ddca0970c9b0aa6d31005decd17f8adfcfc8171fef8e5cca92a916404d68c458d1742a5d28ae6b28adb852612a83d8814e94adbb9dbf8fe896390401468c6bc5fd375e474c075271f8ce6f023be3213a0de1011f8bb141b27dfa9c218be24b653ea13fb575b86a94568ce14d2dd2ba4c4bd3ecc25188b93b714c12db924becca202ce648773752a9c7305159cd208c85c86f7985d2d63707d9de4978c1d7aac38cb8340e28400d1d4004d5aeaa7e884a8db2e2360090e8d036810bd9abbaef47d1ea5c24ec2859b41b0e100a642362c3e0361f847c15bda016ba91eb6b172c0a372194ebe752410bb8a4f20327336312791a57df001edc452ffbdb99ad55abbb794f00ea8a60bff38181a5d54668ca2a9f3e243c2b385f30512b2fd7b0aca8d4a3ce787d802f827458450e7906d370896ee11ae4dc86c64d670306f0976224dbadb3435621e1f53930eb1beba4b2df6da8b0b236c3d696ee74577f22fb07b14d0f0dfd72b510c20ea62dfcf04cbe8b5d5dd740cd54fbe5d0024c0f4f8a82434fc26345e13050e20d431ff791941d1fe96583750e1c1c856920418c8e476e3d21dcb134449de9b3c3843a91be24b4567b352da5da9829b16f6479ac0b51312479bce5a735da236b51223bdc50740e9390c862537c000a4b3e43daa09ee8931a0158ca747b6b370da3b2ab0e4f1d0808f56953ee33464485e923adef02347e910a3620d808a4160fdd30ee35df6a04c53d460db975133dcc64155906a1b0ab139a79f15959fb97f8202de9deba1f32c59b64d9eddf84805f168127631044b23be11b6beef4e131793d53bf4f8fc395bdf5950b77254c37a370aed8eb01f29afda0d53fc2df8f5ffacfb901a516ce2733139fb9119d8854eba549c301439b2fcd1c1aeff2f612c07e77dc481010737840c2a603ac01d025d90764fb8d65bca64353434f7791f5ad8d055037a8386323a1427fe0554ae7118316fc158b73afb8402d5d2d136b4eccb1c2112016e55f810c6ec37b3849ad3f89370ce929bd535a57d03a2e5932562235f99e8f1de4afb23f5d25ec612969c50d3b366acaee7429b24bbade6d92bd8368af90db9495cbe13c2293f558bce95042ec047d7252bfa1cc47078be86b68439228b87c247386f8584b0c24ea756a9993b8fc8eb9a9b51179e723c996d9a5f85e6da008fc1cca0d8eb6381f3c2cd8bb30411b4da07efae024f9c5ddc87e94f509f6064475cf6509bad91d4379f8ed99836da4057ecf8f16f659132fd5f145e017cd4fab094b30272f200d48d02071c15c69716ea3168a2f9b333bfa854a522a68e277678c6897c402b4c1ed23a19c97702cedb55e5c92d4eeafe6358eccd4035c81458e87de100b9495a33a020dbf18bf944d4f0f10dc3d77bbd81ccb1cb6df26fac0a20107e4a886b348369e30d555a8852cd1870ed0319fd66beea5501d84d30c2221194181b8c18226d164d205138d70a35fbf55cc2728510906f83b5f72c1d6387c927e754754053a2c506bf62e572398df2a43bc7999728badae3c461b69b2508d95d285709132d1904e8a2ad83ccffc2d1253e2e69f13b6136b240dc55f3ed5c5377862a4070dca253fb1901cca9e4693817ba5aa3914d149f6d9ba662bec68fdb7b928d13b5c80ea573aad31e3674320d829c000bacae1230b1073d7a6134de672742732099ddbd26336ce10cc69487c5974d96c98d4522923260a4640415a58e39a9030d8e407ad15bc0a68f4b1428428beeb2b8f9c2e18e1d4ecd8e0ddf24ce3999e254a304f88154ba028175448523014793a67a9a7245f42e6b8cdb1d32481d9f172e9d73e21425caffa0db4dd18c4934617e9d795248f8ce4ebd560b6e95fb79ed39a964c0cfa35ed1b717e810c1dd206da5b8492c5b976b22e8732644c36b0c66ab684da48346f2e2ee83d29182bc3019c06be10cb9078718ca738a9d24b85be1d1abc36bc45f24743e0cbd8fa48c4988202216f9bd06af4714e1e79021895f8d509c4d47758a30404d30c2f2bddb63f8c82255cc9470d17f0eef0029400e003bbac196a6e3991918e658465b2333f918a9bb0a0a14491b96a802f077ca16d3092e84fd1fc4ccf433d20feb005ab12bfa8dfeb113531da026eceaec80420af96cb0b876e6206289a336a9c8fb37d63861aef7bc08586bd8ee48e4ba187bc74cf6c5aff28d182054259d09f017fb28dc6481e83fdd871591b5c67e3470381870348ef4f596774701eebfaad2442ff5b6a9f871a5b5838b4c003ad9992142808b6b251356ece725481fe642024360027043c21241ee05dc44d64442c85391bbb2f1819869eba06ba6466e466ae689bdcc560a4807f34fc1f62ffda5bc5a5f299b33ecd600b06d9fba785b37a73b9a82da1e00d27b976c9c6b878c43189a20604fa9529af5363e2110fcf03097661d3bf7d9d0ff47ab190188767187160643fd7eea63bcbac8bf1c1e60a819a5089a98294752cf4e01121103f51415d0e93ca360c055cfff2a3e248f45dacb3a91641e923f0337d4c2a0d4c034b635336ef0add826f161af8ee20b437d55feb063df4e89d828dab8c90053d3645725903dca86019517a992c1f57d9f1cc5237adac4ad9a2cb99d8625d5ab1b94fd85429ce494268227f92f111e6f3264aae486a6befb2c90f696baea55d42456a17a92b486293519929f66a4f825d8e0e420a5afcd336932826c0443357090984cdca7f11c56a207ec5e16b6e75711eb3f1d8cb8ee738fe77b23c193c5d97abfc59d0500657a8a8ba648dc96af3586e2144977dec32fd4e3fb98f4d8860f9221b5d7a1fb31513ba208fc75658f4151411cce58b2411c8de49bb9eb1085c9e86fdbd8fcfe3d73ca7139447671a6389567c51ad600bef826e8ebc1caaaedd69dd11e8abd97c6d2dac5cc177c53df8923c76271160d5be18755db41b9c0d4e90cc3ed40aaf7abf92407d73c5c1babea6efbf1a37e84746f1d787503308bd3af9b2cec96d4587d65f0f5e44fbaf5dcf612673c103f14e5bfa90d8f4967510c1dac67e9c8725acc94030c07261968b31585c6e46a451a484c4be6b8063648e396fe4105022c8f88ca6269b49b473524bc2f465f5bc8f2acac57e3c6b27ba6693a4e8b3ec28aa20119ed7883cd81a36b271c0bccdd99f7d37a0e2f4b6dcbbe261bd4727b592ac268085d1760154db473335430f5c552d03b6c6ccee0da6fd37e59269396b60df7fb54fc3fe17bb0287eff84ff0d532d52c6f93f703edb7ca192d40f2ec2826633db1ce95e62655f9a33d93980a3066f05492997d8dc127604ceea03d8b659a9ec48148d707da7be2b2a9daae37fd48d9e223a3f9600d31793c97f2288aaeae17248431368f460aa1ea0eb3fc16935d430aa02b20bd2fb54b67de60dff556550d1b062b00a1a3933e4e8183bdd0e9b5f9862651185bb7eddc63e804301432da5e8bbdc3e7ee8dbf4c32d1cac8707c839272be7c22535fe4cceca1469b0daf46db23dce6532dd14a45e4e21e1f90f55175d89f37275c3aa904425bc3b6337fa3dfedd63df8b67c8e0350ac847f081382b75f09ef812d08119dcc6549b5ed6a919546f781fd5f05b96e78c1a4296b82105e430cbc23260727788eaa22bf7ffc16ae5d1c51a065f1556963e52088627ae05c7be19c5056eedf1d30a101649cc929ff0675210e75d1b6e1c31e68d9d42071d5125ee6cf7938d53fb1e716428416f189d1c3357beb9fd55616383d2ab09338b2e53c421c22d4176ba9796afb1a6615f2411944908e6528d9c5be19369eeadd0730a9211ab55bb7992835126a6ee3f840b57fea32114a81d151388a410d808a67cc90732bdcdb1c1baadfd03ea6df4f2760afc0fb6e3c79fb1fd0ef3ff3c27db98d8fb46713d7c49d7b56339be68ca8918b2063f9fd31a9986afae62bc4a130d1a5b9a6f103f3bf1f3e2fc932084be3e760729c37be4c0b1bcb9f0c5f440618a5edf00b83e6f2536865e56589e3a46d89be3c0f17054287bd70c20ac9be2dcdf53ba15fac1d476ac2440a6cec0b8e9fe7779bd0f809949f19e3c9b70794f9b9f910395278fc8bff0570b32395464d849f983f1df6319ae2b6ac70cf9111ed8694f3266f7759c323b53713af4181e7eca9e294ee5025c7f60693733513cd8c00db3a55877c8f6b4e9ad37cc1043446a4cbef4aab596ef105972d5f1f1ba9ef8e085cc9d158ecc7c6f0ab7d1781b8d277998edc8c73bbddd1c36359abfa679e205d6a0a026c8c79419655d8c668f600c651a30f58ec18825f3f3a1819ec409ea80bb5afeb701e2e4215d537efdbb2f2a11ca6b575db33f8e51351f3421ee85e76ab88d5174d3c7e7a862f23ab916b9d91cab30315d0813fd45fd95fca9b6c4161d49a9fe16e96b01ec7772aeb69638e8116d4d45a4baaec1de94a723bd6bbe075f38d80958b3ee50bde9be96c2c2b1bb62775aca64011ecb00e5589804e114850f606dbb4e29474e7f9a3a839ba9987929a0301d2afd995f053077842d64fcbf40ed0649adf7756c9e9651a7d6e294b756c163270f208322074ca25617aa8268b9da27a3f285b2be42cd639a4102d4e16f85f7cf49b43074f2a5255c1177a4ab7178066442b1bb4b07a4088544e2aaf880a1ec9891864f05f7d90f48e1f349b11f3af073969d75641576399dec0a647b956ca4da28a6539de15cb79e8bd61ba564ba23ccf56ab958ad514ea73b43f2b404c4d452d23f4e49a1ba97efd573d17aa3944c7705f39d5a365e694ba9545728d7a9e7e3d5562199ea0866ba956cbcd62ca792dda16cbf928bd61b652ad515caf52af978bd594a24bb02d94e3513adb78ae954673857d785ee327acb53dcc8b32daeb673d9c8c716edda796b84558b8edaeba68d04a253f5e9daa9c07aa257db4e05d613bdda7622584bf5ea5aa9807aba5fdb4a04d612dd9a6622b09eead6b792c1b564bfa6950caea4bdba562aa896e8d7b793419564afa659620aa0344bd424e4ff819da366433463a1e5f290ad38227857cf0a5234b6c2e9878ee23c86af97a177c1119eeb581b51d323a528f72945e916370df9b7d70618eb8655a2d2c0e52df33d57926f61fe56d9c377f8fc2c474c28b1d022974a6d717e20a5c7d67c78b7087cbd47b746e404717381d826d744d84eca09c95932096eb5f59bd75dcf5fc59d9de7ea159e40b68ac2788c0c69748ebec261369a5ab55babcc287bd79a404a5a54812347269492276e7f434497dcb5a654b4e113edaaa564b9441fae503d8a8c13ec8130ce77939f153ef8f947daab051dba9e172aeeed1046c30dd02bbc257a87124b4a359a7c9c02ff60396e3e1ab1c4cf20ef51c8ecc1349cc151144b6aeceaedb6799cb28379aae9ee4eae524026ec7e1e52dbf45300103be6b84b1836872834312aa24afe45e8a4c06301070700709089deaedc00d4461cfa490b9f624e84d2c05b94dede8af0b596b6e2dab444ebb635e905636d70a1d7ac9a44dc558d54bd40a1804129f98f1a2230ebd9044a1ecfe1ef85ffe45549b0575ea3ea3b9bd2f3b9ba55cfe4ebaa49bee2b2dc5b9403097530eedb24aacb59824bd4498b7c661300a7ea6a335051da4b0654d8f9c8b9a05988b9e1fa463ec1193da13da1e928e4a617aa70e9847eaa82956243a56039412c4cfc0bbef6604f8b02f148b8e439e627793ccea5f6933d1fbd935ddd09abe1aa82e6ed9344e21ac5cb7ed385b1593746cd68857e710f52b1529e880ca8788cf3a289a5dcfaade3b0d31bebc512229cea5d078481573dd9e1debed283b0c89f4c30f8e7421c80e89d2acf8bc89b849ff36f2a9bfb994489f1f167d0d800841ea4e06a947485b56fb6e01f58a5d4da049bd5eb7d898dab8757a8c4096e8714a582603f96b6cc73fb2d6042b088cb30ee98b1911363c44961493e273db6ca7b88956fa42972709c77d0ddc0876808bf06ba6b99fedbd34d9a799b71053fd78682727488af049f975e240f65510745d63fe2f7f23793cd7ed292733f945f7e9596b22c825363b1b47b3260bd0280d54d76c0bd90387e842043b73ecde1d1c107c0cb3733717841ebde0b87b18cb6a26164474343b4871aee50d34da36ba84c80a9e0f6e6c14f8bb7d20d9015677f98a0199e218083aaa90ca1fae40dd36115b54c15ed6deb1eb280549a58b0bf55289613064882a982a493ce8b6e77424af73039de9dcc15d8ae5b030fb5a0cde110fb4dc39fd26b6edc2733248be7c3583398c1f2148ba58840af06c51f3d90aa6f9c34c400a3060384e28b90e215a5ff287125ad803d1b3b0bb4ea30d3202f4dbdbbf501303f53d16d45703d06cfcf680d1b3b79e6ea1570d08e623146517bbf15bf16378986bb32f1d729f3e7062920965472675f2a5543879d3d95d6b395fade2598ff66423d7d4f16052535bd2b302d01040ef36da10b02dee9409372944ddfd10f486731f6718900748d8cece73156846c614ecb71a772f9d6f5b36947caccbc433aec66a3e997d56a5e5050a074dd69046073efe83280f363db98cbaab0199be0c741a620be46df1fc56192ce04662aa6008dc280933fc8bec74591ca57df01f00ec4b7c4bf8bf61bd5c582a5f36f3e0fb331092377faf1ceebf2de00449f44c64312f219479d2332376a8a6f0b45a0d183a9a1ffad34c4d132b0f0f6793cdab694a36ca8fa9aa5828c53375d1f0195ecb2c213e6a8ed97c494dcd5174f88969cec7ea3fe70f1630d87e86353e1595326413178fb9233f2bd5f9d70b9b5a213efeaeb68308d3c75ec5a40de13ddb43f15b166eaf560f6f36b6b705572c4be615f075efc2ceffbaf8de4ad92992fb9e8c3c1f79e4aeac1e6caa22c9badc9670117e059e6360b80887be63a2723fac0d22fe83d94541a753ee17331e3fbbf1ce24b0d22d409c888c933ba00e2e4f4b099115785289bf06d39d593ebd27cac1466b9382c83e8815ebcc33a646b600c995de658856893fd9777d56b06c025db7ab3c090956422213d6b6d4cf513990e7eae89821089c890ec0c2880441d4d187f62caff93d505c496c4376b6e9473623d19c74d27cc7ed20f4966b7917fe4e6e55819c1ad2e2591367fb6bcf4339a791103cfae2c922b7ae72b7755e52365c12c5df19f92866c87933baa197da2204a392efc8b9e90b1dfa72520ab6924401fb185792676180f5116415b861ee4d19dc4f7a04513e79bbaddb6e28cda1044a5dec14d856e661e2cfb056fe2c4c3ae73b1b68ecd30e8af80dd425390f80748c5150b15d51c91d45f4a006a00c7c3df4bc2b4267fa208a20455c8c9979a1a9cae890435cd429dc72732776ede3fce299c27fa31078a8292c275ca5ffddb49e2837d1b2b6d395ee06cd453cf214c3a93af855b82fc88c28f8f7edc2dbab4b18d68f52067879b69281a64b24e4e25227cd2f40a31542ab770a86c9abc2c275b00d1af956e02f53e4cc647d1597e23d3c98ec89994c12662bb9d1d028c1a570ea42b6ff1f73d50f74ee1a2b0cf83974f52c7573d3ad15a34415b4b378e0193a0a5336fe86e8acc3839175a2659675e0e9769f47fc93fa399e40a2e7e7489f36f0a200f165ca90bae606cf9487f4343f21ad70c05a0f901cc861ac935b69a6a43b9b3ac2872c19dbd2c9996731cbbb8ad82ee82a47e14139b6f3f04f27af9be9c21d9a9a31ffa742c60ddcd9fdd3725e779fc9756a25ac18a0141fccaaeef2a04cfc05ef76886fc5523fdc9cc8a0213ac12dc17c37e2a09626eb5b24442adb3790843f9aaa4d23ae24bc1e310434c776689c2b0bf9a82e46f5ebedb2c8845209ccc7727ea4cc84de6ea630069860db0f983c02435971587fc8151ceb9b82a91604228b0ccad02ff23d3920dec8d1494858dbd635528b0ec5e98ec9dd3071ad5c5a80c1d7ba28d8d6c7cbafba1cc93d59586d19ecd6c141bdabaf9c2ea3853264416379de653d05d42df906e9677aa618d10d3bcad1f740b3ecd34f3c042fb494766832008e69bf18ad5aa27bf4427ce99595d62eaaabed829cb26b4933ab7f716be6e60c5e13251303faa01ccc18b3496d631055d98530a172991c3befd5c6a2d9606d5d2636d1a70e6549511dcb703f12f486151de2f8377cb41233bf07199aa3e4381ca1764f76bdb50fd0c8359d72ab19c94856e88907ebb98665850b9d0c85ae6636a2c2b3907e45c41222810d47782d69dd9013dc52214e461ef6ff0cab0abeca3a7cd64a99934ecb33140deb87e45d63e15121b4e42554b80a43375361060b92ebdeb2596abe5b4561c065c7cbba891f9f48e243e883b1691bd1eed8ad621657f25aa74b3cd9750cd09424bf148a8753ac6c94771c19b5101c834f36737c3d1d461d54c0e413097ed16501c4cb021c80585ff394bd39599e640b4193c4ca25f05365e6ece0246e25fe83751fe876816e57c4316b2e88026826b4c0da5f6b6a77e69e629d515ef029341bf69fe57bff65bd6f64a6fbe1a134a33c8ccb9f0a526d44520ce2316738e7f2206daaf27b01c71dd08015b0a9991c6b5cebb6b4ec53b9235acead35c1c82248c6a84ccbc62d021a84decad265e52da5c3aafa6fb1bf69da8622a254ef2e595bb47930fb8be3df0bff9f8e12181671fa8ff40559c119219ecd4de25706c60b4880d7092c3444da6cb10b83a2b0490d13c37881deaee23d8d7ca27b1a639d627e19b7cbc34ad26699e5f62c31501a64eba4fbcb41d5105da929a137a767f90c2d808d8bd7e83784caff6411b952e562c91cad256a905b5358fa7782fe28035094b5857d6ec6c539274fe6e525057b81a2585e7e3ef505ac90c7924da81b3515f5e2633b9d6968161daa225a45f74429722767797d9a5121af3f3c4f780cb49918ce690ce0b5adb17e5905e4ca09fe81378fdc8d349d35320dca366a8a3531fbccd6af4033241e5065fa60b845231f4dfabeb1d02bf153cf64fe9861274ce133141e9aef0742e6b692e6b3287be47ff18d45ec91aae5e78a8cac169560ccc4242c764122aec98575efe2621ffc341cf681c9d74fa6880fae782776a7e97ad2718e9ae0631c4768cff17b19eaed9eea522f25eee719b9b00cd5102808b61e509cd3c24b303395cf4f77b37ba56eb334db2de23d503bc75cc40082023f1941cd894ba08d9294fa99ad9d077a431c9e3d4dab310e9abd865d741276d364376e34cddcb8ffd60f4cd07167f28ed996d053892cecf1ceb8b6ad48c0cb8f5dd271bf1d79b91437b1cfe816cc16c45051545100b54a68d34d21fc4ff829dade58b4dbab336eb5c878b01ff86cf1d3bc60a1f2a57944405cc65070341ec1521c9337357b1100f7b53e4880bcbe1c2f11ddada23e1302ad6b899db03bb32291766274fcdac9be96e9302351e84bba7805541e5d93231f47d7459c25ed670aaef4bc4bf00040fbc77f432f8cfde28ada07a0092fd6b16753bacbb05f97f8ef6728cd2048bf4b865e7466eb8311fb6fb6f0f32e761b6ad6a5697c0e21fefc1dd0e353b0beb69e72fe336f6403b2293c7f274512357dfae6d3971472b80683ea439b943b8d1fd45969b83f35a2f7f6fec92a23c7ba58123b13182919110a88839115bc990ea5201dd9ff48e8abdf67bcb4757e8385383c8840adf2bef866a9a3729bf92b247385fc83d150dcf1c8f3d230388dc57c3e87f6f67b5652046df4175bfabc59a6d992ada91820d1a9dc0404faa365304a62fad00c7067bd9c75741a9f22544fa1fbeec6fc23f40c2e11bcb3d16f4114f64930d3680ea15d80259284d22fdf6b35d5f83347264176742e31248e3af5c1dee1b48b8b218a7cda2124e486ea2a6c594cc1ca6ea5d0a0f1fb0ffcd0cd6869695e8c112e3c818f202a0283aae71299fd81d5e61deb5de3bcb470ed0a65d099f3e083c0b6e6ded5689a6ff0e0445b3667db15c8e62b896a177e6e915fe065205702dd20b6216ae203c75ae88df41f5b9f5b0e0fd25fb11da7c6b1f74d0a566dfd76e1e1ff0e0370e32c43e97e47580fb2b8fbd7ed941635fd70decf276ad74518ae4f2136d7d5b7d1db95fbb106ae4ffa174a2987b0a423de4391332f362f738b5180c8fff79ab133990e07f3346227049f7127b8db22ae831f8685275f6cb2b13d5ea629496a2f4eb865db29227eba31f25802776ec18a8709da680baa502d9438286dfb48be22a037ad6edbd80c902fec198ad91d9fd43b299320ea39b6c82646425fc74e302eebebeb2388ed88d22bd7df960ee9b311c0effae65780c168c8e8221eb4826c30777e9b4300faedfe86dbccbeae3fb839d36ce538b465c5ca4c0b43700a04951cd9f4b931e699ba349c61f1527187a43af52b0a79f3e745b172d7edc51ac54479a961ddf91656be5f7612c4207421ea70a678d7726deeb2e42e4836d6960358480edfe554862027d5faa19ee8041efb89094258f031b741d825540e12a40d1a4ef30fa6a41a18e633e0f55dd01f6e5aa5430c40a73ff3578382841e276cc68abb371aad8eedfea425d5652de42e72251d1374124b9a48480e6d8b136b267a46389e0d4adaa1d086b71e6f23c7632d770b7a6e0f4f8f2f7e053b084df03944f4f581a9c8433877ec2551742eb740f059f0d5011db31dadd675d3330db1fe5a9cb94bc461c35f9c0a8aeee811f7c4b51473f028030a91e6fb0e2ce0a0ffae393b2679bfd6c5a45103f936c744ae8591df524bfc41eeae01857ca55bafc087966849e5e8b9759a3dfcd70bb9b0b413c743572c3b105f2b997a003275966c9a8df8248687f193c67a4dfa55789cae11da6bbd76ec71511e0b0278075d7ed11d9ddfbeecfc04b14bbfd0183a4a9642a2e7e1ee362e57ee6fac3a4618f08364200a511fdc46a3c2ab56f1673fec009ae2e4873cee80155f3f93b8f9a4bf20d2552bc602c1161d34a07ed9fe56429e906da20c5fc64486d38342e64d8da93d4ea337347e32afa2b4a24400772ae453fa2588c917c4f3bbe50d4d46aeea4a553c1a574f6d6b4fd21333e9b90ff482e3641b8d43ae274f0f54d94db59eba4aca07c7404920bdc79d6c6e724a3d891ae9f61605ee91ed4b2ef35a6c16ca7e18b089d35708439c9b1c9ef5493e8b9fc74eed47e80fca078b191b1967f14434fa29e8aa52ac5859ef3332e2aeee4599da63a63e76da6abfb1f2854edf94be556d24816c58b64769ce037959a5a32761de1c8eb6b991f7b0bdc4cb8dffd289691cb80fc41daffe79b41f80c482d1af7bf0ac4634720e9fa1cb8cc0873922c30d8be3f62dcde5b12e945b5447f47230ae2dcba2cca5e0aef617b9e22516079f58465910a976402bb0cac7a15a34f88360a8d4ecfead96dffb62bd959f963af91cb5b0fc2d5056f31fbc3eab05f6ba79f60f7bac1cb5fd795d77730d6af9712f57a6416dd5d16ec4630b05b8f93676193f930e7178244305f13d81a6220c7a0a750f7f4d0794c97a05fc9c758077ac7e4719f1abebde85a8e49fc0ae954bdf4fdae88a3bb8f0393ed2f5ad88f2971051896dcade57edd480133ea98fc4bbadc6ba27426889b33029f6a026398e635753c32649c429e7b90d53d603b4fac5f8fd86d2bffee25b5f8f377004c3107b5777f730fc7c7aadb94f8628a56e60e3f78d1861ad3b6c5366ab2907e6d1e99f741f45432b70d06473bdcf73236c915b4fcb81ba5123f27547d0d29cf78e886fd13b831818bf5cd99778b58ac2cf0dcdfd86e5244a618685cfeeb7bf6ec66f0e81228b82fa45ec441be46c25b66f212122720395860dc85ffd43d389d97589a55a4d1929bad9b7cbab1e030e4c57479d0c3fdd9646ee93a9722342d13e42f9f7a47d19b384b79676c4f8b07bee8bbb2bfaf3cd1cbc33be0af0fca39fd287f9bfc394ee47f61a1afe2f3f8332a6b5cb31c9b1b6efbb36b38d1929a7eec70fe48ddc17cedc2cace3eba3f44560509e5e47d63b1d9d100f7ff3663235447233f981e3bf5db346b6bf666ce5e0608f51ec7875cd9808499eb388c2a913c010df79d6455308c7c6a19af5e0f54d766ccfecebdcac54efb4748f335a41226656591ddd51eab63b18ed0c4e9586c046bdce91110c574053a4cb039cfff55dc2dd1fedf9598dabdc5e8dc92efa72f7aaab93860547727948372d17ed389e3cc893934f528d00a4567533f7541bde75d51374f1de6c878e86ac06631802a5b2ae1a58e2e1bd5468f922cf63638a1921b1f54f4bf01a15ad27948a91233f76231eb1a7b811751536c0680dbd0fc6ee61924648d0b2bc5b704a1376242111ee4973c57af61ca5eba0479a11263164673af979394662542d11fa6e3a62153b90851988cae16bd57cfa13ddf1a0fbd03b033c3c698e71615704ed7e233450a4e48ce4b386d965312512f54238a5c5264230bd0a9cdca0a864bd3d3bf4637dbf34dc29e53ba58e630c7f5a641cf003d6c0c85f78eb84b01de0b3225eec450698cb3d24eb5276853fe87d069fc2270b6452f0cfeae1998ac6bef93070f30179270e24a67436bc60f17211579c62ceb0933806959b9a8eb77aa07205da4483b96592480340e010f78f3a0ea34d09506e8099fd4b959c8bbaf638cb6fdb84600982e20d2832c0368650d9a6c0a8a3c1b1e8fe8704cb4e906b450d903e11baf2b421d6114f3203c446e5afb68e261e0e36604b1b5b48c712e2dd70b0c2be087bb6efc758a78f05961ac49e697aef1e811e780302198e1cad6d288ca196617f07a666d9b4ac1b86aacb272c54b14859e4dd584cf0d04386d484ebb394017a087587100665be201df52d1b0af6c19e11b9f1035ab52db8e0f32e418feadf6cd818419148c38268c3195c7a94b11075f806907b35d54596989dc829934373fb98123d108c9e1e04def5e3fa079ac71db3fd8632d7e90c46a4d95875c71e2a869b51fcaf73a0ef668a2d7413bc0af48f392b43c0cc688d60e421052461e3b49b78706c5e4a26230cfbbc3e15185a0a000507abb6b1a6a5250df97cfd6d3861c09276285be945c8e544a38027c5d9c34e55650cb1eae0c84aae8480257c784ae4aca58a7c4fc646b77aef6128fcf9b9208d7230bccc666ef24a3fb68f04f2f66c0d82c50a2d6dcc5923fd92bc2c7b2a6889ce86f7e477f0487cc6e73f390310f4c561014090fabc74c8c7ef11b6d3bc04bb1f5a5b7b62679057f8f855cf40f07d786a40438ce702ce649f8feb683abb02689fdaec22689db13f615c83d99a27aad0f1c3f2d5d3d3727ee5e3f2aa3f838241ebdab722b8a04119bf5dfb3abb3596d60b8d189af750cf5644bcb5e6edbba5716ee45415bb202ed0c9c4e09d316a0848325d3190ed99b420cf66bd9ea413143cf0d273e5c63c0d337286ed0a760e99b803679c63ae6b725f8b054a108bab184c0b6b00591c6c3d74d1295a961a77d432eea40347ebd0ca4ee3c185b00d509a60ee53db10f3a54562639ca38082698afcfb9e3486e9c0940c14f6fe69a17d72ac4dabc530badacb505edcc430358f09e8687ddb3404897f8fbaaa81c9175259fd4861d04ea4db0fc4e3c68ca86f425bbd6ea1cc422feb491a3d570604080174984260ae0414441952ba118d794aca2e4fb5a5c08e75866a5a23b9802c2ab5fe4159ed493f23ce3a0d96b29641d2791ce4b58040fd6a3b16aff23df2caa00d1808325a4a8b16d296cfd874ea6499d57842a503c7441fd7907cfe37291aba53619840f669dc875160ffb54676999c8aa6640858670227abefbdccc0ce28eba73aa954df8992656e2b3fab92aa3d17bcf08475242c77a287ab93a791dca35c6cefb1f443fd5264ec941959e8ec97201e6963cbc89403ca5d3e8e11d29ab8c2fe78a29c27f314b35f21397943c1400eac9d4a52c1a54349f6d57e5ccf60fc08ff281778b9f24fd776ed835d1e1124b9a18b487baccc9beb9fb4f7371945d5251357cc38a01ab55948d4c35208a20f24d9471dfe6bee83eea7490d5588a3bacf95b15fbec5288a7db7e70c12bcd7965871b8606a93bb67c84bab7cab455e0d2e33c0d97da497c49331ab98e0a6ab1a909169531ba00360b701403598e79d1f057934b2e62ef64efe38bb8580d779cdee738ffd9ebd8857700e0f88ff90214e0ee581ccd9cdcf8e1b7ab290331c74da9739b510342305eb83c13b9ed67f2f6cd913b68e6cf5478414038051267e2957845711945d9603addc4be182999ce82c149da8150d1557e4dccfed7d80a761ec03fac105f4052a34e3a289fa454f24687eeb7f3abdc3d4bfa0ab5174f5c14638ef5a2cb26f0ea797fb3dc16e734e5fbdea286c37a995d6266c8613d329524d782510a26792218e19caba00e6da5ed0d55362efc800e77c69ceacd4b89cefeceb34cf88f02cb8d3ef1a0b93990faedb582c6457215d83dcea692074c14c10d847f6a3a13567886b372b61e0679a9827d9923cafa4bee3e9872de0b27d03073c5a5fd788a43d968ee639eb1794dea3072c185214d34ab58ffd9574734da5440b1ee596611ab307131e5e8870c18065f25beb8f1486382646ada7c4661d4952f31b664249a51b59fa174c50d2e8c3bc7200df3ca47fdec0baf0dd6de7d282dac24b9c42d7042a9e07b0840a8676a2e7132669c674d9b6bb1c72ceaaa7a776877b1fda9873e150ac5b035e456f5882cb7931d439352e6ca61a20c3d0e63a39c01bae0a8786baece1d8d90a0b6b6834a6af4751b53c8ba592ee4aad93fcb2d16b066961aff5df75f7d1b48f9c7d28a5c50f9813f801c0f3161ecba2a6612f8a31ba45e4e6726954a196c60b05a4cccdfba0000815c2dc59e198e484f9e2beb0acf1c9169f39d5d628350eaae33bf2f0c4f342b01962c4bf9891d97df2bdbb016252a1499d50313f5df0b7e7e7b41b6da477517d81b9cddee9098607631bc0f553f2f77d1bd9e6300f28294e75caeca1a1f7fe860868b52ca864e2bc071114dcf84722edd43e4d600fdd54d029ae1c3353f8ed6b4e288d0fce1c12350100b420160982d89cb81486895161d94603f395a6a23ca0b16280408d7271169b8a7f548e0aace3229d4f63b9b551cd2b1ebb345398b09f77b52070c0e635b39661bc8fdfda998f4498b840471717550d92c447d54269dc41ef6de8da4a3e8fe0f19099a6eeb2986d1c5a748f969cc57829056f7723d03bf73159d4b0d25d0bfcba1a9bca3f08f5776549fda10f99468d9d7ffc531afca15168f8ea41d52229fd07b687fc603247b2b4f97ff51d33ca0cbaa75ecdc1cf0989a60029c142f23fa0fb6d2ada45df77d50d26fa52dd21d6f9d1a5b4629cb8a94b05d2222443b93b4c37e4bb91631c50a85336f5001ffbba5f91d28948e581270f540ef00f9e25f68f4b881cac3590dec4ee289f28f04857fda19973678739e5a51b3d83e01fe44893c3f8565598ab19869e6de533107e6c9deaaa1925490e8b51acd86f4d0ce2cf9938e6af8639685b726ebd6cf6f7d7f8a636915eaeced305b2d39cfd7fe532b714493b84fbd2d3c081173dfd423721cc3094cc47c4a7aa4b1840ff5b214f178d565061e1bc2aca86622d3dc686c839a395c33ffa83e74268b6f7bd34a303b5441d0b912a6f03fbae282623554d43499352a1d8fa0b5cd663d8aa90ef4de1dd5bc4c03a5a23a3d4acdafab732e79a78407fb9932ec464cd69a61ef94086c90d93e8f949a6fe48723b0872386dde8226264fce0cbd47bbc2dca190ae28661b4712125f1a0c05ef3dc107861d5a1bb8c6870fcc3711bda5207d533d5c4c81fa1038abb841609f676b86f7256133e71426f75def8f86c0da66220e2887c07c68407bdf9833221d419bfd3f03827243451f792b03365e24c61c0fea660b2b0a373c67fd48ebc1a7088da04d37460325182fb61a7b9ea5e1f26b66fb8739cee6136b138b2d4aadb294089bdfcc6ca1a3467b1f7bd2fbea506198ad01a46d3c6f62255ea2243bffbda21724c7b46ac50c21bfb160052c0bd403a2c82b969a048771c6b405afc5e96fc2855aa17ed3027225dec33d9256b4de23c5641d564508176651a89309a3433d8e267ec77e25d668f98444e49ce37e229c81e9453dc3ccca2e32e974213c82cd33f0b0f451002281d86157dd22beb5a414828858df46f22d88e28a2d1c5bdc47d94a17da36c302161c3ee549ed0e18ee106e0b4469cc926b92048421bea3b0e8ede51e1c6f26ec46b8289537fdbe8d22a68fd2d09902c4a02f2af68c4e66be4bcdea1262aa62ae0f89b0db734b00441a7600a380ea9820b0a01871c1b287b2f3acbd0f4330203975283ecf30035c4167eaa3f70601c49f5d87c83c29183bd2d49de08323ba6be36e26abc3927721c3f61e2f090dbcc446f9f658b78b97827384835143821b0b69098b520e24038a65eb30678b9ae3acceb268d684be855eff5b426b1a641c582067255fb75209b2646f7f9cc85e180dab9a176d4854af27211507135bc109e8613b0856fd0502ee2636783c4e500a81b737440000e910a052df96d66f67e152d1cc4bbcd09c83dd05a623830f4dc6d780a7d65ec980ab85e2da8d3055dcb77266123d974a665f9b6ba1d5afd34c50e5bce850a4c280ccea0358f34762af5c6f1d055b34406bbe511ae3cdb55ea384adbc0672a0031cc2bd734d27cc0eba53c9e8227a151588ee49474356b8e80788f46146345a96686bc0c1ab086e2dd60dba4c7e5a56b1f7b4763383ea2fb36c740bcd6806e145fa4d603ee4d4de189c3ba71ef3d933ccdd738a9aab868039525e49bee5de95e04d7f8d6a20b20f039eaf03c9066898f18c8db84f2ccdae10932a02711572ce6bf0e794897504297289eab2839c3132a320c392e8a0c10b3c316e6551e09e5f7d5c15d76666023f12f4eef75ae6e372d6ab77171a7acd5f2204b086a3d693fbd39d34dfc353e4c38c212402dfdaba0dd1f7f32df842439f5cc57c9ffbaac1f33e8b936bf4a8a2665035f488fd3db1c032b6412978c3fc408aad8d43b1a8dd2e6cf1efd0c6ea3080b9a8e81a66f5378ccffb4e571093d37da71ed687615d3cbd29e9a7714e47f97c48326796677de894f719dca88a2f84ad9fcd0218592e6b89e520bff803837a4a669c8f9b906562815935ff2050910a21b1cd502b6f22b46da1d4d8c292d32eab2017291ce6fb9888a62ab8a047cf1514cb987a48c3af48ebcd302fac09d429988d7f8a828514ac2bf477f8bea6aa56e32ce6c64f818aa2a96f6f22e7460a5477e6961a10214a0e98f308e61f433e65e952e779b678f2c02ce0146126d9bf8611025dbbae07064e4c75057f1c77c57bf13b5652e480a2b4867c4df0a2b29230a356df3116a2977df4e6b2b9ad656566f69d177c9982b06274098bd66b971a16f4d89e37dadce93e0e8337418ac4766cab5ed06cb17c3ee38f73c354937ef61fad1c985c182726e4a297d93ba14f86dc4fe2eb5175a9479826d4aecd6a2f20a9d2cbf659b17ca3bcdb368e91c9580cdbd2fa0d3eb9fb4076f3ea6780497d48016411df4337cb59ca85e902944c12f83a65c159c66770c673f3a170cf4c7aa33010fbd9fe19d038881ab38f78eecda047103f2f17b6725649fd951cc73ad4d663500029bd7bb2c5ac649713d6d12d20fe6c9e4d66b95c0653c8b14d2224d09ab8ef907d607306c394656d26586db416ca3073bd00689a75701658ce2a77223dc9bafb8f2269a462340717ea25e0810c615ad4e2ca802ec546b6c85aec6ad14042ce52653c03ed7996c2e3c24e7a6a8214657ddb22626e1545bc0902fcc0dde0a810a0eaf279a033e676352e8a9eddfbb5c045c26ca049bf6e36f416b5e225cf3ccac4c7cf8e0de6e08deef47afe8d5aeb82f1dc2d045f17c2fea156189a2ce569e11a16faf623e0353fb551ad0bd3dead7145547fb9d5eb95909e116c07fa59e802f31c1a0cca44cef6239d6fea4d9812ed4d2c5ef901f3930fb321c66a0db4b20c71ff47c3ad4ffd731bdbef8483fef9614a2ba2c96fa12980aef29ed7e0627fc5ed9175217241e555c0404913c8412644b994e53ae0e36efb1b3ad83c8dca478d5929475b52a16b356e481913018f489c4188db0b22e5729f95bd284efc974369ec094ae0958161d4fcaecd9bfd51ebcfd3f79a9ebca4f629e41e89e69240829288e8f10fe63ff77cf805f42a3cab35c7d4cb68a93eacc9a7b728769f290294b73724be9361bc697ffa3414ff5e5ffa8d2c98c4ebfa6f5cc7dcfa037de4921b215c065feaab2cb7988a8d69a9cdfdb79bbd66087d043693385b25557985a1cdccc258bc1bde77e8d3eea07b5617b84bfa6659724fa9d92b8b8ef149684fc900aa78d791f52e2d4edc4d2b9b7b82a6278705a7611b8ba5c0228ea721259185c021cade2fb766609f2afbd27462c312de60b5bbae44299d062c444fc4beb4e68ac8112d75240f47327ab9e65b1a2b79457e9534d77904dbddff65ef3d24297a2b5cba6f9a7ed7af39f807e4e1e557f2792f1168c64348197df735a62bf8e5b5b77293bca9a82fd1d359bbec961d97179eeed3c0785ff27632c3f9124f6c2085b1864065732711cd42853849701d00eafe97c2f44d632534c4728e85b2bd61e55568217318e99d853459d445aea770e323d9959d0a168a868815dd6eb1bf32bd6816b8b818b1a7ef93a03834e51eea1b71442ce8594bca22b4656965aa0609a37ab8b1411c184105a9815f0a87ef7a325167d047f0ad15f06386a3f590061806242b680c2d098321e6e97433125279a77daadcec353fe6657d4ce6c00712faf14d8851f9f5ba7130aa91cbbe05eb33469d18177dfb50d87365c2a420df0067e3375eef9cdc280a92fe20c16cb5f5d5e4d52ed6b448ec300ae54b589d5785382a74bc82cf932678da949e06ca80cc50f31b7afa9af5fb36add06492c433b1a9ea6aae3e6b832cdda243e485c27611b0cee6f9c2a3a3fa0449f8062e84ee4762f4b22dded84dde8735cec93ad14dfa374c1d41e12e0cbcfa2e25ad0165ee2141c088378f426f39d3f3524de114bfdee3f2897307bda74eff8881b18ac60dc1bb9e5b4bc7abf04541e682ccd52829ef098cdcd4a8c168649581d7c9b4a3c1f5041df1cfbbc6943a69d36bcacba09bb0a023809e69c309103aaa0768498461ed694b4f02a9954fee6e00bb10c9249878fb509aa9825342aef59ad461753067c960d4873498566c4efbd67ba7dd29d6e87f00c87374ad6d7e6825c8f4df8b2f4dd6f23d8cf14809faf0ae6866cd948989f5b435fe2f974b12416d086b10f5b43625ae56afc32eb549e8d77afc27988788401a14eae6315cf1b212b85f44518b8abd496d40920404914791461cc1449e977ad01038055bd1111c643a853bcce2cf49e82002251af6084f1fca8f4a9ebd03a28348500204455c708634b8754bf63c32c81ca44aa03001ac3d760648cc45053e44cdb665d6456006ca8193f7402740f18d1db031130412880389e2b0cc6ef408f28c13754c04231ad003f1989ed9308b0ebb7a54153135459b5e8b5ef959c94166aa7aed9384fe58e929ad0fe2be4e06957257abe6a5110f3321a251e7dc04a3e2f837128d38b46e769d6492e4b1d28a694878bbddf07ce9774478ad5a2d7c6bb04166ecca5d7eb85de1ebaa171ef99e300d9d1f754bfab1b96bec6e6b68866e714a7442cb34860e2ddc3ba71016756d3001e8e5c94318753f1c022c4695e6638ad0ae4051a172ec7e2d14b2c7242206cf99cb4132376bc5b281a394578433eef96f4ac100e3552f6246f64addc3aea3278234b6f70b9bc2753a565a03b46ff674c4f50e0f4aa41167d045e604fe9b64f244be88eee4781279078b829f1f84444ff1387863f0dc0e948f6310f5e8c82ec0b776cb1567447f08d1584b702d03b445a5ff4c03feb58fcbe6096900ebf4d665632ec361e1fae1d7957b82e0402bfb84da61bc16f1aca2e23817b19b7153ac24902bf5951fd86675010e3847ef186a110a9f2cf1d70f81396857b4938d9c14b45bfcbd4b811f18185caf165dc4c97f702fd7f79393c8c77fa0180d87159797d5700ef3bfe39df18d8f19fc17dd897981596fe76bc1c110fad6451be6ff824267d2cab87703bdd28019df4321ff99dfe67a1a79d17c90905ca79ae93da162b381580d074cb7f5e160cc42f7ff2e1b76e2460f374054a42e02d9c37a373c70bd87386ed6856277b8455dcfa02c756c8cb957a94f6e031a0ddb568a184b9c92d0dfa122ae3a69d43681b27a1f64a6fad9ff0b7fe5d9d7fe6000301c0a970f428623673d5cceb0b9b031d9f7d531131a2a5ca3a55f64ad94119d7d201fc83d76539428a2fdab897641d593772ea01724134c9cf310baa7b01397511285b8fd433069dcb7adba9db5fe506cf3ace6c8f9386f30829d3e2a650280befc2be5f82606681712f1da8be34aad8a7ea52df44b02eddbeb8cf8d0e9b709b93db97755fb57b813815959641f1b3a9e5ef007da51c7cd81f4497b3c5857ed5b7aa1a7752404c2715cca976f1d0d488938f19a78fb30d0ce2175f86f4e1d26b521b7929e63c253457445b53c529c3934d509e1a9a72715064bfb6e2b36c8af905c4241a819cceb16b98b60023d104303811b1c2740970a84495a896fe4ec5a380d1a2d2b1774b84528584b0ffad06deb46694ad7864f3b372a4f4f7b646436545eef012901f129c07635038aa56d70737f9f413d89d97fb52924a9444d68124baf791180b818814ee235e0b7be5a51d69f75ce20264b9b0f78e99e36b62966cd4d67b6264767b1cb51b56496dbbcc48195952ae23a367e1e77de92ba291e9e36e16c150062b1682ce454e86a47b755ab3c1f60e471835a83ef621e62cb8eda28c34dcdb11ae959a3a8bf00d058553829885517ab4b16b14c4645b14c69e5c827428150666a4f7c6393cb3d115c89e3af23361d586c654a41113c00805ab6f74fc1e232c5f9cca6fb62848b140a842d20974c67e41e14eea2097504fff9259b8522c109bd46d83b84c2ad668cb7296475baa0d624d24a14498fb08d1d6c7c77abe602e701815782d90888f89fa914fa6f7024da75afe468871102f75d85b211c36b48d60e85c41b8990c6cbbc113918b3435f367567c3d566671f90c7e0deb0afc0e8eeacb90e11a73dc6707ac6dd73ba70de6909d81c24f58392a2c960d27568cd67da3fb01a2fa0d3869d27b51ebfc0a40f598cd9c58b9720cb5e6b58303dc2845e2d7af7646315df0466636a756af3333c85924d42d7d7796acad9a3dcc7af16901da104c2c80c29d69ca5a0452287aba4a742b067340d737cbcc59c750ef32c8eaa9e2b42dea321533de695e1c1bfca426bc1e0b66da87680270c90a7102281740404c723a13d4b044b265292a4db82358ed136931d3fd2fef4085ddd0b3e1fb8357595c93d447922f43073c0d39f2dc4a6f5f74ba2b17c492f93162c8092c6c52df48c4468210dd0900c2179f105168e651b495f1c7d21049eaff4201c3d266c280ea5c65583bf0eaad920c342a92b6f4052607fefcfa92e059647cb790842a23b25757d1624b71f4d55523b8ff7c6b1b41bac79ab4ed6a4090f46d06f09c99c53d96216facf16adaa51143d62b496290583e8327fe2ae51ae2beffc0260488d97b46ce6abdb43f4e1e766245b4cfc2373329e3e2c05fc7e0d1843eb6374680857b080a261659e8bc23b63c7cadf97334e8c9374c774799d1cf8d0c6c02d4a8eee140de3b9a15dba4b18ece753b8e8592aa4d9e216145bbc6dfb7391d298652836a875517528705443329793c2ea1300692582b291d97692466e5a7755904befc967618d7569a098a5bc41499b2da2b87700e8b51d1206b6b05791c070a591af98a03e88cf9d0926a617f8fdfefb654ff02aa5916c976d59004619c1cd0df09ab4b3f78ac8ab18cf3ad7b0601f6095c501c43cd23ff967951a2a9cc3a78b7632982335776b71a0677fb9987a01026dc44cfc6241c6f4c36fde3e403b3286e0ced744842a2e743bc6e6cdac9d3c321502112e0d5a4f34347f10c74a774155a22b126600c9a0407d7905d80078fb42b43ad40fc84caef9c0da3dd2c343d6e6897d43588dd3a8ce5f09ae28859d63057f9b8ef9e39ea464cbf571255eecc157700ad2614ea8f4e45d6f679aab5bae34e376c1547f956705e091abaab233e5926c402c1786e86fc1d5221a02234e42e6cd4575d571b359f918e62dd67d8db0fd2ecc1c332fbb2c01678a0f0856173915ad45150e0b62e434d8174ec839077d7528d3352a60b6e41d407fbd3b63bdb420955ca148c550304faf626bd482d26b070d0106e82b49dafe9b7e1d2c47589c265bcf605a70532bbbfa7dce51af921e7f27bf23835a0fa8e78b44e4838f3933756afb696ad3f37bccc69ba8f73a9f40492e1665802d20d88503694cc464957aa123b2be9b592ea81386ee080f9aa620e17f16d7c16e5316b7facc91c6a4c5eb1e5f4f47a5fbd12fb9ad84b3d2c1aaa1dbb9d0be73fa63e299af3895d2af92a23881431ea4a7e9fe07cb76ab60cfcd9de86c36e2b0521b46beb02d5b2ce43cefe1339991ade949e9872b66455482ec278cafba5a5a2e8937147552b406c2da258ccc534560e4f7ad6a65bbcf03d91414e7fb13baef66ab583b16d91190d2aaa6e32c2547b855ff546b8601769f121f569aaa52e2e341ad8081ef616e1989ca019eaa9402c2d514a070f8236cc6b5189d2840e063008303cf73d529b40db8ace45670572cfe7309e2de6fa7610580ede5052a379178e54285932bcf28bd13c86cbd866bfdc56a15f21f91d1cfd5c2af7b738f2de4055a4020f4f603e6f76f3e8cee07b08c217bffa8c90b4b2949f297860450a9a86507f7ad7ad44ddd4fbe3ff7e2be2f52723fae0aa308146a04dfeabb1b3586bda1b5511aaf77da4ed414901c83f53a7bf1ad209c96c28c71a22fa4a4c951342d500a1bf5a32c46c44004b66ccd529e57fc42dbb920cce8853960e40a05cf94c9566c251e69a43a648922c304c8156647ae3212ff36b14c03cfc20abccc6555ba22c8db2bf96f34734e4ec73d18d0402a37bfb1b9247bcdbf6cafc7b1acff21d485bf5443f000eacebbe8a8986b29f329dab4e00fd486f80f39d84e8a373a7bbf3c4b530b7893197dbea98c5323cca6d058948e188a415b8ed5af6cad0a79c8522d17eac5b784892082e02888e27906efc0ca3fd42147c81d8c6b996895fc6d7ecae7c88d31a55e2b48b29a61a1a36d9d9f225aa24bbb1c4ca0b6eab44f3bf1466852cb3c9c902b8ad0cb7bdc7467823a4d55bc355d48282438f283e705bd5d9c223afbc93e90146af53dcb75a33d77b0f681e4b29a3f0a28179532c86031903133dab0039b0670a171b46ccc3fb6a53c3268c12b3dcdef1a6944bac1295b072f1c1647661a54815fb5389c3fa33886de6bdd254df8261756f6b563eb67c95221b541adbbca9a7e9e99c5ac492e6fe82460526204c412a29814acdd71d099b0a676a5bb531559f065058081f6fa84f5c7c4346dac08a45c9d9c125ef5c55a051dc5cb0d98f009b80a87433b5820c120037b1cc36ccb38a6d13440643e64c4f90d4979d947da628d881fc1ef14e5c00da6c927b6d2017145ad034a7046e83a8052db7304a2740041ad4dbb8326299237c2d8796905fc8e6413adf816861b1bf2123a94e8540a708a89c0e11136b9c5533b30e4a936a4c700db206ad493525a8465905a5c935ceba995905a58935ca22a89dc867c2bc7ffbffe06614d6da03c9d37be067883b32a3e1d29945cc37e272f84ae1343283038a5a1d5b20ef255302d199606edca097471a3c876d0fb6779857f7540890965247a4654382d5df5fbb98d94098480f3bc0f235d2120f51cb9d867197fc8e21087f9547ad3b1cd21c2e5f520fea9154388008fa83cb1245a5d5dda05e8211db0ed9b1936f123e8138b563510e776d6db5ee55f1d11814127288e59dd2ee29c0830c6a31ee43c656d848300ae882d15faa09713d6666a243e020194cc48a8d6db7c978681cfda4c3d73ca3622a525dd66aa1fbacd5b9fc9b33bdf907e3855faf9277204b7b996710d963a7eb0d57c271862ea94ca401faddf417c74e838535429d37f3146cdbffad64e82550b794f96377646ec899a9666b666b7faecc8e03d329ddf80c731b2626c54636d6c09a97d59cc9d1a02f3fe023be594f14d78a4ebaa1a1e474efcb113bb110938983a40df70ea6814e4624a10cdbee158547b33064a385d40fa7de158d40b62f128d6c2c54f94e0e574041fa10ad329c2c3ce74ad5cec93359a7aed43913ba1e961b7e96ce29bf1652efca3ee4dbbca929f6c0360c3336f1ac5258976a71ada10ea94182f16d4eee0089b6491c90228f0911eb54993ac1be681ad5da2369c89be7389d9d9177204f3dbd4fea1bc8eb9885690168a88ac29fea012db7a211478a1f8fd8e29f378fcce386fe51ddd1faca20cc5f7a746ed302e22e907efa712f8d060d54fdbb7a4292dec6f1ea5a2919702d8e1267fa328e59d8e93cf17134212f2e1c7b0cc27f0e905ed8b20229e3c0e325fc60ac97798259678866a0d552f821a42e600170641282937f1efcffc3715a2b15d1449e02c8752960d02005ec7efc31d68a3d841093af836d00f6675e615938304e4bd9146ee69df9ee6d42124f5e82486389c930fbe8cf521986e9ae92f55f43cf936920dc0cacadc4cb046830be82091e87f1fbf8f6a8e93d6b53171738043a717dd37efd302b0126a06a6dc9e7bcbe1108694f30fb04fb55d742a05200744e87880935ceb2195906a5893527a8865907ad893527a8865907a54935ccba195983d6a41a6515b44eec26d1c85315a24c44f56cbd005426375168c09bc99c42c9c01e9f8d14ae2a26d41b4356a431ee7b94acf15bac49748539c685c3e0900d53719110ccb6bcfb4945f26c033e8e015ba7ecc907bfaca903a58dd734458d8ea31a2a120aaa90eccf9e705424e5c79340dec247cbaf0394bbf502332fc89a2d154979df2396b016e37b8cacff2e1d1615099d19183142bdc7c122b3266e9df4dbb00654af1223f32e1a5e2915493521c082dfe5d73585e0687f2a9244098757ca89cd8c1cdb7dcb9c4b6f1142ff4f5ecb668466e89226127e584ff1b7da2eb00ab0c7411027ff291733cf4193e7d3ce9270a94812c04ae85c1f79ae3fad6a80811bbd7a6a2f2e47da1b84158012495511759ad254757505da85a54845a273964b1c19d798891084fa11cfa8b080442ae2563d444592942f97c1911d2b46a9c48bb25f0c25481cd8e4ec965f6875f31c4e9e00ecf22ad8eb73d75114c995d3d789761ca7820c97a4d57e6808fdd149b759222c76791945a28148b62e7d7d4dd72fcb2c11580115a69d31deaddebc2540e3e66cfe1e45f23658c27e2ac0339632201f1dcf25229ea628125179dc438d12e30edc34d74a7228c58061c1405246befc305f7c44ad1870d1ebae7c90c4c0f558b913452286abc0689e624e3f6236277bb33472c48c2ca75520c517a035052ee0256d2d4044e525a4a9cd7499ea855a6abb0f86bbb8e7697f7061edf0e09546eaada8252905fa66348140255633440bbc601791e55124339ac4004b258505b47ca6415eef4a723cb79054720b94cc007917da41bcda9b47bbf406947f042e20048922293ded7108b5a3b0e120074b2f6953e4605db61e41e372c91d01cd43f8892e30523622397492e47849f5e04b172f2747f4caba1b1e48e9770a2cbd8d9483458d32f334b272701a7a7f472b60dff5f09bdbdc27bf3acb31969e46df08e2b626f2d5e5e87a1d618eaf2b4a27735c048951b43d8ce6f07d6256cf600f96a85c6a7d4ffbc2ff5be85f2633bd71d0e8e73caf51b08b98af3669c9e70c635a07ad756f1600cf4fb7ac134a91d05c498e1a96e7a03d2c1fdb627e4e9777751c1fb80d11f72ada180c5773629b42b7f5ebe9ba116caab88ea8cbb7598fb24d060d0787aa000efdc958ca6d456e1cf1101ba37b92cab2613cf6117edbb100a2b0580dd7cc86860143a4b4b37dbb206984232ed3725ca5f0ad1686eb4554ea8c2a15d7bd06d7df01eecf737e3ce797e7d9eaa9dd6ecf3b4958ff3dea45cbd780c3ad81093243c6b26994ae2e29d732013e7af861196519d23f076049e3ad6a3b3d394103d4f28fe8832119f5b6fcfffe30f2e0fa13d40d89454a9230227b4a0f7594017209941e50eed5298cfcc971b08bce77b2453df1f2725426b16b73de3f8bac88243e374d041b17d6db40a2933ea2c76cba324c4a401f2481750292b1cc768dc273d247fc98cd4b5717a1c2e6a1cde9bfd7b4aa853379ed40482b556e5f2d7b45471aa0a1ed1d89cf360471217a6c04795742c87160a6eb68f5eb8161e31dcb37f4f22ce965cb652d09a6be47158b204ee2dcf6f2c37f90f6b9006803f68f70c2b1c772d263eb0e5c3008e6ca8b8657861842c31ca7caa8066cd1740fef2864d870386190e1cd5487d9290fb31abd913688cba94b0a75e543a0f6cb4ea77d54419017d0bbd250ce6f44a373a01c941f4cc25cb5929002a20091fd79ba791bfa0a212050b7a1ee705fbe45131caa3503df284593fc988a3ea122acceead9d412bcb185c2b6022d441d89fb9edf2194a8f60804b928109464f3dfd3557b42d6eeb59ab5ca36d0fec8ea59a76278b95ea8211a173114a32699539c66d2211444d92a01301b746e4f25a7e3a8b5e9e5f1129eecb6c61f05c33ff48c4144ba45e00393e2b4e23ad9ffe67c634a5bf6e9c48de5a022899f9b948460a991a87a189da526a30e1f4b904b4d23507cbe81ad2fb5bb1886ff6a229d2331b5ea0b587980dee45ae1656a3a06e0e003ea936fa4598ca676b3933c2999059020cd90b062d319fa58338be25853bb05e5fe2d22cddba013999560218c82d4e16e6aa620468495d4252bb8a93d0fc3131fd8a7c6de155107192676011fac646597f4d53f2c739053a35e05240d3bd8537bb6860b81d515a89d49488dafbc6dc65eed9d7d09f05d09ca402c483a00eb5a34ad939f7ff66f0ad30d366e1bb4962cca5ae7b83434d878fbad95eea6ce7cd405a174bfab39e61b69d582f85ff70f6b15dc8e59dcb003bf27e5523909fddf7320b1b200ff132707a450215591493d922aa404a07cd8b92bcfe3bd054acdae6e41f61f1e8b032ef4c604ea444c3e45f4e7a3e7bb606e27116c0df35a9f3f8b1a4458a4f04ddb3624358460085c355e9a26310e11c5e4d682e9c1e75247e742d7c5306eb23b48541942b14800e3553121bc201fb504b9f613aa1214095077d61de83bf147a7593b4edd5824059bd89b5567a22a7e6884d9bad88ba67c6c258602b632c896d2f9858f394a127288daa4fc82037141d2df83728b537a43a4e4ab6091375f96b98bd6880434064400b0540683f7368840ac78e19cccca8bf756b502796a0f7b64b58f9f7190c169e8ad5554ae01f3015ec27ad3a97ce91cca170de75e4740e16d36474ea198453f0947d7a6ae9339e5f0c0a943b740288790055ee9c3cc9fb5300e7683a6dcce047563814999bb01f804f51d56b34fd401adb4c3020003cbb291aa299e2d643692c1d5853a5de52ffc7bbc38458b21e7274e78e1b5e2526b6e0dc1a7e594502c8ba7c263e5d20497aba625349aa491821daac5a39f9b52c76beaa36f81065f043c8c2a748c6ef5c28893a0822f5d615750b2cb18da82d8c05adb4b83227ef874989cb3cc1e2fc2638b1daa7d2b77d7e20c2d2e8f2f8264c9e0967009bb927a227a114b9aad22af9232403cc1dda1defa8a21da5107390a551df9f19ee2e0791e18b47430b716deaecf107030bcabbcd42df09fc89003f4422c9eb7d00cf44b51b70dd3da1f3e6a9696688939d822138b900b399d39a49182515f7dc195e8492b1f982680dd63f69bdd51a93dfbd836c2ba7a05fef5c4ef078a993e9f5506798dd8edb5742a1c79a8bc9076f19d36cb7b862f842102db2aa12b3c42145179ee59c456dc18eef679f28adff289e31fd89d6c9f2386dfe12d1e13e56d4f34cc0c0869eec1c01e3063ed243e7c6b1b118553c01eef8507d3950a23b56242c45a86f2a6eb7e3b49a40fe39b777fbccfe85c63569b790c2642af9858bd4c119deae86dbc1e4efbce1b933fc96adfcb007cacdb6a954d7e318bb6554a1d8bd7609964d554b21848b8cad06d488a19304cd025b2c6f25d01803204ee719ea7d37b70c3f918a23cb0c8d71c8838826badff2b8f5d8c96ec43861067ff1cdc125a8243093558ee959f1dca4392170f86781bef902423887ae9d663b957fb8392ba755373b194a04384812708c3abbe1dc092317939227b89223a210ab38d3bc881a0067056a2847657f2cd0135bfa8aa8a22c39394e7d74b5a55fbd7f3d05d314fb761f867408aaa3162fc3197ae28dfdd5fd750cc84088fee5c8ab29520607e844377c49381ac0f120342be2bca6a485bd2fa339c3c5e86cba387c2fe688214d156a60d679ee04b6f7c0efa060a30541b07685973df3bbc84db0a8fef3f785f57205e4e196503ee81e41b1e931b43a30578b3fa5bdb0d11e95b8c96a0ac6562f1c5adb59cc6d30b33496ceb4281fb132732a9f16f4be10f151364d31398bcdbc5df948554e5e01f4ea0b476fa6caa7382785b2f0dcadac164870bbca55947f382d3b2420137f0910cdd1c9b7442b7ac80938a7c8f79ac6a41bf074c0982cb74e37dfc98c866785516cd58047204b6ef854dc2b56cb19bafd382ccaae5e2d224467f092145b5136be2c1ea1208925a2f65cb0a86d1431c8a3cd9f40c534494b3af8ac54305c8b661ddcc446f7dffb5fbc4cfe3e8af9c82bc89d84906bcd140de491d1a24eb138a3b331438f030c9082f2e6723a6764f496b5c2620d97e818969a651333a24ac1781e582ff185d9067b61c2a64f1eaceb6c8988569432c862dc32c5d210e542ba8b531882bb5ca5656e8e0c344a9e4248bcb26568f3dadb58cb8cb020ceac40e857b589314243e5761cc5ffb1d4ea8e3433d2b1c2c4d7b66239107a00d2ac8d13d1fcc412576d84244d3207df63b7f7982d55eac514663310213e3e2f5c89c9896054959a8ed6390daf762e0f720be89e13fbf019566698bd20f9672e7744fc142e108844153d928f81c4dd4efa90bc2460573781c95a3664047173eaa5ff4bfe462f6056d17ea797656eb21fe6613a2fd2f16f9bdb5b43458f2a24f81517079e0379dc49456b499be24fd6eaf01a5841142aa21868ff3ac48bc197830b15cd94c1f446da199a479543c802f08fa2100301b32e398f09e55fa95444cbe26c754643cf5443eeddd0e7642563f47bdc2068fc5dec009b84dd2bbd23fe8bfbd4b3743eb95cf800847b1450fb694c05d8aea41f6eadafa157c794f9658cd136ab9f48991c243dcda9a9a3f60beef2b38716ffa71a70cad1c2fd73fd7509664efb0cdfead0938301d2d4aa660a4daba9aefc17bd2beb5925f8805e1e107cb16ad0fdc39c97fc21f8ef32945ea9943eac7783f89319fa00bc52645b24ea583d8ed72fa2c1bb69ed860b6fe36276a6f60b0fae282c4784e04e8caa4822fb10b043c835415950bcf13bbe34af5e713661d695642ccdf2d5c595351c8115ff06f7487e9511b4abc44d99029ef60cfa04cf1f3a7222671b0687d127a43f3482ee9232ba9b08ad4be5f152174a617c38a34dd39487086467358686541072f1cf6c3e8a1a4dd20499f3c5612c01fae01148978d83fc5482c12e27b2a80d7c2d0a578725c797565a19a0187bef5062c86473fe783c54a45ec1914150f389910ccf2bd4b62655a9d68dfa40986fab217bd1bfcf6daffbddd1183cf3a30e2fb2430c539d1c7eee38c69e0101319e48d20c7c0038e3107a6fa0ded221cea0267a781a445ff8c89b1dd6b16988ae3c34245929c40fa16a7a23d3015317122b79ede077d270fd6fc35996b6680240dfd0f3a159f5d3adfa9b8b2e7ff54a1979b3b1e193d3a07b7168054b849d1508221ebf653dee3b845441343c271c811093f16342b5c608b5828844aeb699fef7230edb9b72ef00fe99936172375dacca97dda668320bfe122eaa2e6489536901c7664d4dcc4633ece5ff054c26b12b9bd06d2f15a888d8a0cc060420c2a8db30df24745e334962d79e62913adff036d36e3a4438a649c562938631eda2e3dbfb7f84813a75311e02cd61b2b35efa310371495205f76349ef2c890f2f509485481879e346653074995f139934357497f70eac5462506349521fec789dfdce40eef567a1dd79bc796c0ee4c7f7046cbba717798a9ce0632984423976e2c6d989ec04f0f65b8b5015117e6ea9a46638f224991320ccdcb87d1c35a3194d600951ac508a53da6882f0bf6a7bd666c83322e3e0f88c1b552a98cb03f11d14857639890f69866311e6f0e4c42d5652525c528a7d909c75a950358b983d450ae251f14958ae12f9e8dfdea3a14ff9dae7844c5009cdfe78280e88d2602e29c849c5a653c4d7fcf90c361f21993a02d0ff126661d8e41f77054c4bed052aa0bdbccb5080369e17254fbca854297a6372feca95aab05c0d654f9f4bdf0ca233404d4e4830e09037a86867ed522ad773e95375fd815a27e52d9e49290623479ceb618b39de0df0b7bb4b89cd10993a98b0f6eee6795d42bdbce9e714303ea9a3745e1886505c539a7a6dab1fd283784e284c96c8eb4c38514dc1a459cdcc7cfb36f32a79265026cfe081a68d7dbc47a3b3f6df15e0fe97491bebdf0a6b9874465d9e7393ff98b10a7e934fb3a07fe0fe96b74b72e778a1633ea975d2cd0d1710be10e2a6699dfbafc3d98c653efb322034c274bbe007cfb02636b47ad8bdf83d9fa8bd8c97b3af97b0cfd83e45c09feee739c8c985422fe3a82b0abddd278054d925323cf12a7ccb0cdeff710c9159538e39e56e3e5ba81a15b44419fdbe32b49885b293234f7270b05ffb32f81c77fccbf19f7535ecfeb7a9153a220143033498dc0ab1c0278d02874cd3bca658cdc5cdd2cb06fc825e16133847036476f9cb173b383ac80fe8c983bf9082471deb127cfe134688ba2ed791557a58ee1f0887406cc6995c683f05ea4a38cdef8c5e8690084a77d4625fad604c3d8f91e0aeefc3de70ea40b36e13f515710cad4ccb07a6fa6cffd22699eaf99cdfadd377812118ddb315cfffece6020618eaea66c5a9d8bc100f485a88fec0bc86b4176d63e686a1621d6d66bed35c160c4f60ce97512d4550e299be3927caf295f8be2069586d14ffefe83b2a0bcae88a7d9ae1b5df655cc0a6f2b1c3955a162530842c92a8d1e5ee99f10e4ed3d28be3d968bda2e5942bcb84432198eb0c3562dde58a13ac3294106109697f02225d4342d0728ed28bc94e0d3c61d318aea4b29409e554fb15bdb6924c212cc3de7c4189dae5646b0797d7114e14fa5144dd1c56d7f1463d5e36f7270fa66451399ceb7baf56b8b3f06900637554daada54db947cb6bc2421fa461b5e8645b782ef10828e33c362c9d6bdafde7a6a9c4a35f16d584adb423506e0567aef067b77d669340718b05059cbb30bc568d9991a6a254b34a980213587cdd4bbd2dc038a91492132f1fcf48c998e400ca303601cbe44171168337449dcba4e33d4c8ff88cd25b70a6de3bbdb15817b312b91393b6f50b7dd3ef2a568bcb7e7a0cb6fe4c58767379b9b006bd802d8fa7ef1474a9571f43a21af5bc460eb6c90d7479f1b31daae8a35cd8ce4257f6647f7002eb66cfa8a49b3909762eec5ea06aaeb5d3937e544d3116075a52b62420f6422b824d46ec2feb00567e24e64e2aba14d14b73a2035f1e683d8883d2fb9198c342b8a2f135376681bb64763bbbcc3488dc6a4ed2ef24e316527e243962dcf67e731043c6205da58dbae531048cd361d18e46b2683f0daba910b20117195bfa60cf7c410dcb139327312d954607e4dc46ea9145bcafd1fd9d6d2ee265d3f149c216dfe7fdcded8ec1f8bb66ad4c13f42abf53083ce32017739c08e843505930ada0a0e0bde7c8e12a0474bbe2506b05941c76ba7fb7b9ea7cd4a64a004a6a453c202ca04a827503839e491c314a31c463940c98127490f928c91a495a4851103e06040968e8f31cd137bb6b57b15d9afa616752f6c81de77d5b2dcf37f9e6d8de1fba0e34ab96a6f2acd2f87cd9e787a7c1f87ff727cad6387cdefb1f5367fd9ce31e7d90effed569e473b1ce92aa741dd1fd37bacc9d9cd762fb71e0d2aff47b96a37cfb32dcf9572d2f368b749fc45f56fd89a3cf1ab8dcdfbd9fb964eb4788274a3a0cd33870100c0c10e1c9c18e1c08343dff0811b88483f59e5a7d8a26eaa738b7268ded79474197c4e8bf9c9e77d8d8a9c9725e77de9a7781f88fbea976ad24f71deef9ee5f83e516c02a5e6fd5497c1cf8961eeced75fed53184aca86a8861d0e149beda3b82a4d61d8ec41a1566e02435ca98653a18d4db0944ab9c9e66f882d08d3e1a92f5b834f0d40dd11c79ec83fea9848a65f44bd77d2dd447a548307e9510d1d918ce946b2c391211c49e30811478ad4a431aae9007e3056a9973a3c33f6f2f69796ec9d99e4eeaa1413c9e1f1bc3d4ba826c8881f2323604646b48c8c38c04890ee48c21cd5624f844261f039cfd86e2a8fb27484f413afcdf359194789634f24dd664c8a3d915084a36389f4514b770e4f04bb5b156bbabb8d70cfc234f6498ce7d7694b371e9d667b7333513132615302ba4f7f5bad774c47458628d2a48877e895ca1ad6e9344d8c68724a7894565062a2c443c904eeafc9e61675946c614a1db4274c517e8aa5d35f9296ad0dc237647db47adfddedda11c94a911e91a0905044e22032860812ddf581b0f4546d9d168f88c43022d2349880868e86d10c777447eeff36918e33fd74feb650b1beec7430f4ffdb64062f6600bb6389fb22ea8d66207577835d96a14c576bb15b2c431832dcee58fa22ea3d49954a0df16234c4ca90537743e991103142ae0839c5c0c6046228e2633ac658eed59f683d0f7fe7a21173fefaaa71193f0bb4d9abb545fefa8aaded36af5f9fe5aad4e59adeacf56539ff3cafd7d4d41c71ee5dd79eaa8b62ff2ff4be3571e7a29d94dc6fee79d3e5b1ac02caf15105e93072e9421620c702ace8eeefb2dfcfb6286593317da2f2c953e3523e2b6f7ea55ee5a9ef5dfb37043505b44001593a661a63a9460a00522920484718fa18c120064f150c5dba391c18402f576bb3c9f360707ced2801613091f75834579a002e3e4a4047614209604211a0c7bbdea345f4828ef3dcf0e3e07024ecf0dcf1bc899342a15628d46a740034b01c40c9e8051ebc1046f7c2014641f80832c52808064641988c8204796cbb5796d647cbb0c753fa49d648208f364fd0e62bfd644dcff2be8b5595df1074ec02132e30e9ee58c2a45cffc340b420460b75d4424e470e4f7f03cc61802c2303e4e63e68c482162316aeb050c382afe08215c2d10a52563061947519654e645132201d2bc5a66f6213a64da2ac8979a18df95f22a18d41f15892dd96274bfe3771a56e9aa18bb2be892927fd1461efb56cf697d2bf085b6f7e919d8e831ed3d7e73aef0c5d1dfedbf2e9f53541a1d14802a323a391094d431333b69e7b9e8a86c6e36fdbacb4865f2de72deaf26f084a8fb11ca5830f8f4f904a4aac50bafb841ecd8831c3cdcc50001c8c0a90c5e32cbd0ffa046d96f7146dd088006610c00204c021c0080330c100f26800308c0430a66dbd78de389e2eafcddb6c9223bd65e7579ba784cd1e7b1f0890763118dd2cc6d3253e4d3c12400fddbda44702806114800008310a005077473cbbf9f258494a12ae76fa17d912f714662f4872ef893e83543863a48210231572e8ee00f44885385241480a3488dcfc4e91ab336f13f6428e92da6fd7b6acb7bcd93dcbab8dc1ecf5e65fe9271450941c3725a6d2f3e626f374bdf7dc83b1d583b1ac79dedcbc695b9507cf1d146ad5e1d9692978f728055005040e9404e2a51bcfd70848fd190191ddddaa119012460000d3f25aff6b71a437379d262300748fe7dfef562300e400a063defebbd508052f1dffb31b9e1d47371a0a8d5090ae1b82f63e10a76aab8f50f02ef64e9eb39b0d3884a90fbd1925f64e6cb68f928abd9313c6e88e2dfbd3a3137630c18f91095eba3bfe0db7adc8ded813b1421cbacdd9daa098474343431361b4f517c42313b27477f43cea6464426b64c2936e593a3232018551095fba5bb67e5402964cbf27ba4f347b4625d87483f33bc744de631eeb7b8c55ac97f2ae0976f7931e91208311096074b7cc8f6152a865258e9425212fe238a49fd7d717d2793c3b30b5b2d93e4a9e79e6cc3c398cb76a6beb83f0bc3a28d42a8542ad34ceb3a311c87437952a1e89accdaad10859ba23473d4c39cf2a71eebb2dd317f68c431f7ca8ca7dcb85984a15ff6e351ac1d5eddf65a1d7d7d4caa6f563bb17ccde78dedc46cdc6a8b17434a1478d803f56d031f644afaf25f9fa2a49fe87107fd83f6cfe98e96e273d5384328a00eb583a42faf1532c53980f1912350dd398dbec960b854c9e373799ad0d925c9d960b71aedfe5b0082a229469226ce9ee48842771a2502b2df644a9d47d6efe0e0ab5b2b181d91b3f5e7e20e96e9d9e19021e5a0c418a215019c20166842026ce6e0b7149be2479a631af5a96e7d157972deb66d8cf3a63d7d2a2d93d0bd3cf484c085608a1270841084215317fccebcfaed6e981eeaaf4afedf27f1e18ab959a26dde66a9dbb76f38052f7ff3e779fc3d9da9b43b0c37367a80609ecb9da87148d427ddb94548964f36d535238dc8064274908041600010442023aca929b381e922c9a9dd7b015724aa4e74d1ce9b3863f30e30742443cbb1f0c60868f33f8b8c287a987aab8c994bd1063ba031fc4a087aa987c80419bf7e8c11e507437cef4fb2eab62a6cefdb5d576afb0db03883d76b8b9070b3d00d343554c25f96d53b8fa42f273cb92eabcff72fcdfa40747f4209b4cb93e0fcee86e1e40c18327188738cffb52787042191994b93254c533b69b9bb217e2afd7b2489e298e553553c6a63bea01043daad0e349c71c5ae7fea3395feba6f9cda9c70a79cc9147961eaae2393cde8333e55aebd3b9d9a9f298c9838487197844000f23778cb903cb5015b7d6e531afde2fb22eeff1c83d08dacbc5becb38b4f75d7572259b7dfe25c598076ed3037de68e1a76e0c60eb0f4509589b38318ec88419bbeeb329db18365870e75ec5107143d54c54d60e8cfb2d964bab8d2f9e51fbc8ea23a9e74cbead411840c196ca252059c377b9ddc0b91f9820c13646c3c6f6ed8562dc6cdbf8ea9d351043a66408795ee1eaa62a25225564b73fd22df689e9fcde1accfa22349be5f54292765909ea163668e2fb104c27e0e2dddf1dad6cc1cf27e6b0e20ddfd1f472728470c2afdeee7e7301139b2c891a49bb54d104fcf270667771fc863c972b65a3c5f337274c4b38b434c8cc38a7b1ecdd8ff4eeec39d9e89a326729fff827215870b3af8d2f67acbdaead984e7ebbb950ebaee9e5eb7d579faab03520ee0e8ee38f3e43c0759704873203bdecf1e6dad7200e4da56c5419758aded307d79ace47993e4f5655738984077f7c5a9548994fab629a994e74d9c148e09399ebeb2b1f9b629291b98bd4965fc5ca61f7d2205490d610adbc0eccd1b366ec4d1717a7da2993d6f9ab8af6188ff5b336e4419421b73b491a58d1b66da283246cccc980e7477a4f9cf621a25d5d2fc5ecc8bd1645b69bcb0156257c5e7e6572dad4433ff77b9daaf5483cd4f5a9f18f3bc79adc7bccda50b7fb27b6d015b88f16228200c3fc20813657def04eba187fae37913c78b780f5ec485a4c9f3a824faf9f1288f488f79d4b317be378b68f843bc26ed2514116811818e0800600618140003e80b3fbeb8e28b27bed821047f84808c1078dd4ee35ec41fcf9b96fd16f5ae6db1c2f0f323252197f5483f92e4db9d8f3d7667a594c751f25ac7b2099638f44037c9237ef2169d5e70f1a2025e04e1c5932e43e8c245975817b0cb922e0b00011810740182211000f9801a1f20227e9f29ca4ff18bace988a37e8cd49f96a5c13f0b45173299ea8f9fe2b5a76aa108917e4a24cfd24f25923fbd366bf7bb2cfd14f34ed5bae99452c1664fcae6bbd7b55d88e9ee2eb874778ce1f9eaa2eb22081761ba3d2f5f2e94e06227d3f96d2a2e82fcdd824c473c555b80516d8145b545d7718b9d2d62757a8f1664a2165cb4b092c323b560d2ddd6470b8f116d340b6521a63b5edbcaa24b776791a5b3e8ba3d8b9def5659d434c962860b1c2dc405061d75fe5b3636307bc30a31476285788405998e1e163b60d1e522eff187afe8b962872b1270c58c074ae081ec81ea81177c1024a5fc6fc55822e577c51209bf47c1cf6eb64a2951279b431fe804bfb56a51ef40131f585185153d5811ad28a23a0c49dbaa3c54726859d3a59f72ace2882aaa74cce1c1a1020c152f2a3a2a945011e4546da538cca758ba96e4718bddb26d61c014774ca18129f289f3ac4b20a0d2eb2bc9311156f87da6d7b242213959d7e59a580a3ca44881143f483124c54c146644e125a84271c5e9312f0a20509001451728948082080a295090c0012f58060579112732c58bf894fc42b6f5fa0ada0b821c10c2811736e0c606b244956d557ae77b381b3035c7a99e70c113603cf17ae2478b10b480d11d652988f4f3adfffcdf62cdfb2d2daf65001af843035c340093deb2ac10737fffe609fe6585d83fc6c24432302603626420890cf864a0a69be354ac3b2fc7a99c90e2849026f468e28d2648d044144dec34a102137630f104132a0cf0a09fb3dbc9e594137767278562e9443a5d19747297a5295ec485bc880739a69e51a895c6bde7fd56a378941d9e9e90e4ea949815624c248561b3c74da5d40e6e22f99052c1668f6d55fa2dcc794f9bb8a934f3cc71d3cc3387942dbd9c47e7f7ac7fa55a1ae802bde97a6fe69993b1f6d9dbb279092cb1b4444d37ebb9e7b2f4d11db3b4200b0c5bcf73584d3a3964659912b3d4e42cb9d2ef025ffa025b2eb0d34db9bf9b941e637933db0bb0d0dd4ac040095847256abadbe624cac412479a2fa124b6742c250176ac376425e1dc5f4ac2214682f3ec0ee7d90e092e4858e997f56e9060d2b14c919831724418d5115b3a5a9b8fe8baa3fc663d224977944d8e00124f7fc19bffdbd25e5f51a895c679d608cf88231618d39d1f3f868229114cbb6c01222c80044b1b584680050a2c56ea0dbbcda31efd2e17cdf7fca590dcd61cf1bcd9a24574bec20ab851012c15b8a188378a10a3880a14f12a82060ad080022f0ac470058e0e6f26b9dbec25d2dcdc8467e79e87e57ccfda18b7fc6ec2267c3f56678c05fe05431b33cdfb2c3795be887a5303ff89a4578cb5f75813e77a858b2b563aae7e7e827e635d216102694c000313d099c04cc76b89dcbf4d2447c31fa847c41d4434d1ddd1e4784e980e112409e42181342460250ef1bedcc588ca75ce90430e91680e51f1efb2044856c644dce56a3fafe1a3b0187274562a1c9d20209d9cd37fb37ad524354d4eeaa9ac3861c587ee8e325b7ba563ca43b4d9271c0dbaffb71501383a0e55a152c5ab2a02572250c33da9863942a04c571504b66c10c8618832dd1d71381c623cbb21be0cf18ad3b62a8f10550826dd33082382000a2249f705020a10088895111b2246145340ac04981d300cc06e6026bc82f0aae2f5a40808455b90bceb3e3d229114f112294f2d1e414924250d09678a474853543dd251f273c8a237e7e93d5c9d9bab52d7fd4f2239c23d6d51ce7e353471fefa9a316dcd2f9a9b12aefefd22ebd884ed2bfbbbeef71409294a210c4178a53b4a2e8f19e532b2057294bca423965ccef384e3aaf43e38c242e84ea1502b1b98bd99db47b944b8605e585eae3bce7bc20f67749c3f0c01c974e4eab49eed5edf77f9b379aed02350c7f6c046611de047055393a849469f1d1f139febdb91d23defb9f999a434d13addb4f298f1176e9712fd48c7c1919e37711ecf1b140a8592138cb11c63daad481eedb59f9334ee51d956d8c7eac42caf8a913701cf08911a444510492122c107187494ac106bdce329dd84e4088d03c9231a9ef854a70782f37aaef315da18d136330e6d2cdb7a0a717d9e3aa3ab5a6f3689b16ab9f031e8dcbf10f739b41a472738ef6bda9d9cfd6ecb87208da9fb3885a369968c69476bbaa3cbff4e22a30e8f4e4b63ea2725f97ddeaea8abe94ce7866955b9c694542357182e697aea9aa0ca55e3b9ba3b7e2b8d96958e527a97af7db57628d1bff148fc948e3153cc63cd6c7d7cd2a2d6501513952a1ebfa6fb0babed569c05381bba7d43632b62abe98e46a663225f6db5947ee7aab4e48ab14cefd9ebd7e2b0fa7056185696282bed9111531c162ce6b0bc239e2f156b86e9ee38b98c265077fccf626fd61964ab3cb24cfcf0075a43e7eae491602e9da0e3c8236492881dfc2772554ac4fd571d3cae24900e4f4ff3d28da996ef3f2ba475f16a3b3e1aa93b6a40a2d003c0349edd0336d01debc449e1ea8545956ab0f9a552b9fe96425243eb2f9842524308fe68da17d6541530ddb14472eea9fc41850b2a4c50d1a8b8d0430c463d70d1c3aa3b66ecf95d9a0c35ad24ff63137493e45e92ec6b859fdee4fc7dcef6482965ccd4710cc170c5431f3cac80870bf030a53bc658eea727a6d213d28fa3fc143f68ca988e539648d143ca0ae2571e8fae6afd59ddca562943524c33bead51b888124594a2283274a34ed6c74ff186969648ee363fcb84a9ca23141a408941a9e98ef8feab5bad8258e9ac8aac5e103a432805424542a6ee8ed7d23beb6fd83d8f12e5ce4541ec422521d24fac79762f3b3b5c1fe3bfa10d3ffcc1a58cd52032826c5029c81d0047b439cefbd887c498f799565a5fef802bddd1c43dd108e80fa0138c8030508dbfec8d9f6209574b49bee2e9e1798242e1bccd3ce756f221b5ac37899c2496fe3fdc8f0b3e73f82ce19343cf1e3d51f4ecf4087922832b4f943c998926ec81f64127da226db5b0cd917b1d8ff23d524c74add6f2676934b6bac9eb77523cfee6b67bc1e6b553d32afda4d344c7f4ce22271e70929dfca0d023273c3bf0b10398ee538c3d91143f45cccdbf7e8aa598472dc9635eae33e34a4ba70f22edb0ea6e1e9e3178409eb873831d27563b2be83046c76aab05c30f2ab2ac29254f941c0a8529f761cb9bf8feb78a57fab51e91446fd297f59ee598ca7804f577bacc38e4a4737f6fb646a6ff56ede6f86fe89ed784084dee68724593294d00a023860e918e4dce0e72acc8e17266bae30b45282d69ca3c359cdee289a5161d42a1a8fc47333ed99294fe8f498e896ad86dcefd9c757e45f6e35e936efa1a3d2f6c7125120a15db50a8156bd6f91ee6ea7f0e2df716f4e9231c2eba3be3f8e014b9211387647e2ca9084589a53c9e37b265c1d05beecd4de806bc41806a072a2daa1c9500ba23aed666c72dea8f9f687e3d8efa6c7d220bdbae32e911132b980cc1644a47cfa3446e739ef3be57fa712a146a8513b9bff75b0df8c0a8015334006c000252605239d5a5663ac63285551e8ff8b9fae029ca589d5faaa1a4c255cb42a15ade0b918624572d4b52a95022572d2bb4b11bda9857ea4d6e42891d9e190cb3d4407072aa253758e2654913dd5d3a42fac10173738bfdbff961c9f17c779ac0f93906e70bb3b2c9c1f902e74b48c90c194a8f0c654486428dd0dd4e3acb218c1c8ac872d8e98ed26355e5dcfcdb65232efd14a9f453960488243f242965495e7840670c28010340203b63c04f191ca2c00196d3b298baf42143e2bc13487acbe2ff2646f090213187c75f5f8ddcffecf6377e1a12a1b834c27d7dc50c87ce6e40c1bcf2ad0d8a6136713c3eaddcb0d39dad876776833f774367367c89d7663658e96e1b76ba3b5e0b640358830fbaaa348e67d6008b355c3b1d8a0d28808401f78160b347c6bc96a5d25b99e2a73767bf29eef3cc47b868cfdaec88921a32354cd4f4504363e40646ae18c1c1c84cecb68c7dd210e313f7a5134f67d14bb7472cdd1d2bdd9e157722906e999d64707a65272327201d8f609650916731a6dca5262fd26d6ed9cce4848949777c7d35cda88ac451a4a80850772c559a6be8d9a25984d4ddaa8ca68c8a8605dd3dbb154d161af939a3210dc9fa4f60b3a7a704a63ecba7640536bfac34a5bbdbe66c2bc98d8c1405c9878ce4117f5a687df8ff3222615c9512f1ac053322b08ea5d2d421b2c3512f915c45a423f7190d61a2ad3440d11d1aa141a361c6e6589d9eb56036031cd8facca065869de8e1b9f1b82a9bc19b4c77c479863f3c3765c042063b54a9673204e9f8b215281b026608ac3feacdbf4376fa5acf8674770b8943889688ef739b15c251214cba1d16c30d71b63e31cc208620babb5aebc5e6a7bdbe62edf535f5faba803da05880ce0b9d2d204a8eba02b0c814102a60860c063d3218c6c860a0000c3634f7955a1fea833f2894d3445ac4a59bb86ceb741a1c3d6b13204702509025e04a4757493fb9e4a8749a9675291d4f8ef62043c00910e0650880e100651c60b236217f29087b211ebfc89f68e679fdce77cf2bf9a5955a1f827e48dc5fea590b86aa17f878e18aec8508bc700a32a6e3f43aed4d1c92544c5fb86dd314b96a6fb2204fb2202b642ed891b9e082ee3675e64214990ba10b60e682c721099b3d3a68de6459cfe5590b741fc8b316d460133635d81caad4ee50698fa4e2f985fce4955a1f5ab8226b61b630d3420a0610c3005e18a04a779458cb8fc3967569924b62f0968d72c912e722b52c259db15044c6c2938c051ab2157490ada0816c0528d90a455678215b4177e75aa9ad5976253365a33b3ab2aef50944447e76f4c3e8003365cc70d1ec497a6098656cce028c29c095ac003a590148c8082003024441001e022c19801d03706200b028bd498bf8555279e290749a388442492a8e6962d87ae227a789dcbbabd29f799b2eaba599004001bc9005808c007859005a5021052a0491a910aae0590a45c852b8234b810b003a4b818a149e74f792ce52284096420940b8c88048200302cb80cc90015161041900ba8e4f44bdcdb4e3b1143e2579ec4e8c7ffbea2dea9e37877880a0370f0d31a14a7f39a28d12dd6f6d96d2441c1f12b937553af3fd0d4ae43ed36f5ddb9aff596ceb574bb7cce344a494190a6664288091a190050a2b1400704208b21336d0dd419d9db0d3fd513762c4a39fa4d3c4eff1138dca87486bb1e3f73a37ea811f95a69b88f17b769a90850941990908c84ad84156821b59091628c1ebe85977aed607adf62ccd668dc8e6ff7cf5e3f3a08d484cf1088a4ed7e74cab4b9e8c043b3212329091d075f74f6724bc30827bd616651afef09ebd3c505a34f344d3b5b037b5bcc7999a6836821ad90856b211e40fa0b3114859b391751259ffcb7aa7785f5ad8fcfc9475e98f35fee8d21da7fd32cb8b9eb53efa83e88f27dd1d7dfe38a1085e14818722a4400430e27759feb53dd647fee3fb3874551a63f97f164bcf5a9008980831f811043fb8f881449b86240a25a998b2b5d7b316f4ac05b9d752f3be761f855a713c54f3ac053f7b5bda7b39dbdb65d8fc529f67d0d55225522ab5b281e2263765fa4aad6ce67dcd560d363fadd22f5b7b2f2b65935ad9e0f76cd07bf6de57c17481dc94f271d38e0d47b5fc18889bd68b4db042c1a95450ce8ecd6405e5ecdce7e6e7a97176bbb6a5e197067eb7c33bdeccd5a69eb338afafdcfc0e0c532a94522537a5766c3248b2d93e8a86af65e517caf53f7c8b2c38f3e4b46a29f719537c1ffc1be2799fa3dcfc8edbb22dd26299eed83c1014a7e67d3e5eb63ac787956e3e1ccffb3e08d320ecb147bb97fb20fb60017bf4e191ab4f74e7e7262c577b7c19ed8145665bf700ea3d50e88198eed8032cdcfc1daff454c31edc406dd6f0bccf0322784053c606655e50268832347aa4a147187a687a8cf2f8230f2df2c012f1bdd46326ae7a74b662a64a27f5fe3f5abf7eb7f2d8e1168f87b66269b3e9da3c96e0a1071e4f8cf050e111a4a31462c996f51f1e531cb94abf4ce72bf6f894f16cb5bcc75fad7fce1d5feee0401c9254520f0415eab63c4b19367bb84f457193939dfba952aeffd91e146af5b2750705f8c30e2ceca062070d76b450070bea58a20e9b3a8844991f6bf73daf1485a239d25b96e4712379b5f4889f4ad89b3f92e452c6580e7e33b34c8f6707fe83dc13d9a24a534478ae88f0cc64b6b0e693b1d24d264977d33a373ae0989ee7afaf7f9d08cf3ca2e30b1d5b383a649feae3900e524f90d2318717fa41732481f3dff421c273959aa3660e20728891834b9503ec9e33e6e1d07eb16ae5f021c2337beabf49532894e74d1c2e955ad9bc07862b9beda3c4d14529e320d9aa8330d46bb56cb5261d7489b127721f1d28e1dc4e6a0341154f0edec8810972b0440e94e4e0d42d758ce8389128597242fa9151ba4eccf359f9ceef482e71ec9db8131d2fe24e50f53d0d7739546935a582ca4d97e354a624a18e9b928444f3a568afaf389871f5c05103389270151c40ded0c11b69bcf18137a6bc218334b96697e98bb3f5d4a28e89ae7db5ac733cfe5ddeeee7ce453d49391ec7446e8f1b52b8a1841b366df0d1060fda88a20d1d3a5e696a592ecc16f4589daaf8d5ba262be6dd8f796ec24fb337eb837e2df79872f2afdd70867d45a156f74130d45c9355f417e4e6771e9db924e4a62824ae86b6d3b48cad37264b8f61d29163038eee188b8d2d6c4c894fd8b8b6b58697e69e35bb35ac346c7e6b00ad01a4f37fa31b7ce96e2837c0d2dd7d839d1bccb4983062b2e09615c3a43be674be46628274c7fa44f6b21c5335c474776335b8740c0c3d3f56037777b785b62863ebc191c617691091462b0def8e428468ab9f9fa0d5cf4f101a4ea0a183469033c29c61c41940feac7f218facb701183688d9c0c60c3ecc186386968e9946e96f3261951908a8411ca18de1d42056832361d80883459823c2dc8429d100b33ebb85dbcd365644b310213400a2c1cc0cce98010866e09a010d32c8810c9088a51229c66e3c3a5bd6b1f5361914033131a0c0280641c48006188481c1153058c1808617a0f102235e60039ea03f0bdb98d4a4e7692dab0d711a975e44e22772c11c2eb8800b72c014010c18606031b431f718cb7a72bedc84bfdb425be4d772ef2821427c7eb8e1bf2df8410b3ed082b005475a904277b7ece954edaaa809e6ead3db62c1976e16687916809505350d44197d7497b1c56d2d0374dd598677cf8ca76a0568e4d50aee0a90a8600f158c11ffafcdf7c1f9dde6dd167e3e3de92d9e50053d7da4804b47ccd5d9d109ce2f56a7f55280848c36c8f8d211086a7255ca714f460f6414d1a4fba876562ea4d3c3b3729da09f1ee93e3f9ea3f3e4890ac7756e847876b814921a522592ab5257a55fb47487ae2f5d7f4982823ebadaff2c56a1c04b4714c05000d4f15a47828220fdfad609c0589f13c0589e89545f9de004331d71e9affd481eabd3045e9c621380b67afe989b5426f031cac499e7185a7abe5463482f65a395a0a7040b20c11b24c842029b8e383d907ee2fdd3742f7578a1c24b125ea27819418cdc8f698dc9898e8a81de3b6fc6d8e0a83140d82042b7a34c4d2a459d3eca06096bfc608d3cd690630d34d638a37d899b7cde772f1898a28c0f51e911ea58a3238107dd6d020965b8ffc123debe68e27975f0901b8ca0699af66d30ad6bd0a06a68694705fd0405e9780c31c4a086901a6a2451a3a61d755f33e2282338f4c01b62cd44b54c0d01f892254bba4780401a5ebadbf3bcf747a8693c208da0349274b7d5be0d9629f7dab7c1b4cfd230401a24a0010434cce82e69247794c951953a08cebbfa50285c2dc528d48d191a0100eac9d9a6077a87e70e0ab5d158a6deccd7f6dcab43f4a393d9a0cb6cf024b3c1936e47e10409dd1d2839d872a1d07dd6bc49996cae6de279130707855281a3360a0a854269f5398e761a05a70662ace16c3d7c3bf00bb523bff1582ec43785c39123478e1c3952732487240cc0e1061b6a4072047fb64536363636cf9a3736cf9a2cbb42a164703cdda664425243b803c773ff3ee52c0e2b367bb84a89aef561e63a5fb655796cabd2ff66fd6f56edd332f5c19b1f85d14ffb3cbf6d0af7efb1429bc355eae4c158d62a75d94fcb53fbd617511786cd8fa35aec7ef8c3b5f489369a533514d97cdb14140a491197fa6f69457f535ec05cea05092f3578f17e8e4edc6d3eb1aac488414b250611621809638e309608634916c64cfc6a3f6b2f4bc6587ead945fe9e726495289e04b77144196ee6b5b52707eba9b486722902d826ac31618707483d1a5bb27088615ec604cb9f3c1f02fca74c72fbe48f9024bb7b55f4cb976feeb8b245908f8e8965a2a95ba5f69cf8fcd6b06d9c0ec8df69a17a7f0fd6c8b3498176e78c1c50b21aa6db5a417345dc474f1d21d87e46bf9b1cddef421144a52597519ea920208bc00810441ea03643a9630c96dde2a8dd5196b79ac4e99250a2533ec2bf5b6d999843ec0c30748e8228f2e34d0c5d6854d7724da26eba76732dd78546d1c174b70e103172e6c4146b685175bf4b0c5095a84916971450b2add1dadf50941536cbe3ce679f4958dfc47f37c51bb8a2592635ce77f3ad569686868a8fd6b7b3cc6bc96a67db555d36c4b2885a5601c215caa376ef2ac6591565960e9ce02288b202d87dc245dc5a54cc45cba74472e30e70214c50271e98eb3da0c0b2e58c0b008d21d63e12bc874bce2045764b9a2bbd6845557d4747ba08f1d0f70e9fa5f7829e701a04c410fd4bcebce0ef491435607b80e64e900ce3ae0568421dd58c105c4b14276c796752bdc8a2abe54a1255601547213a98a1a322a64d0b1ab54e3709e1a15413e2a7acb97ee8e5bb27cb7dac2a43bdb12a4a7d081b539d5e1b933853685499a384aa58abc2f90c2082974900288775ff4f7c6a3f062eaf0eca854f11805d85124e98e02484321a6e385424b08423125629cadf581a2a62307fac88fbd42e180976e8aad7300e4400db701325f932840d906bafc06acd81d6f5edbdac0cc743cbb27c4f4135c9e98d2fd04490b192d30d012765ce52f632d41320d1069c0851364e03390394102275a4ec426c4644d7439599c635ea5afa93d910531d17ccf396a8ba6db8cf3bbf2c7f07dbccd56cd7376ab31cf23c7a99ae861a28fee6b7d985fd1d36ecb5e88395b646262cb646295315104037960a04b77c7186fb5167b8c88949843200cc88ef3315064093696a0c012361eed5ececaf5392ec4cfd9ed5d33e767d5782bf5389c38f7b9fb93e98d965d334f2d4f8dfb1a5e30e56383ffa3ab6d7a5982b20089b17c275844ff0260fc0248342d72eef17c6517b0e102415a0e51914a88698e6aa618a7c4968e4ac0942075c44924912589ae23a659123348c0814497e61edb0c092bed4800c51ac71409b7f7e598fa115ffa082bf608efee58dd0832d1082ed10820234e8d391e8e5ac0cb2c50d3dd5185e50d2c23c0c2b9b74d5a432c2e54808cac025d2ac043055628628c589219d327327ad866dc8ab1b62258451020a3800fa0a0404ee4ac7579b5d3e48f69e61e130992527a2a97d2a7f49f2b3bb8a281ec8ad015201308c104b098c02a53c7dfb5dbfb804f2e311124c888b8828816110a908018aece98ad0db2d8c7a3047a90c00b999532566c66c52602428840188cbf66fa723cbd0d63ebcd08c022a04343008c2e20c082401108c8900dd1c610560c8187d8e9f8e32819a5c461d18f14e9a7183a0a4a8ccdcfad1ce2248498385d882e42c084482284b782f0a2bbdbce3c73701049bcea00d107105bba2b10b1d81ab13262e0bcbf4d2f5613e507c8616436181698fbe97587d707ed8b05af2311bf5ea4ee58e9dda46d12144572b8284cc710f45994a53b16d5cc504c777c3cbb96c5212cf4eca5331677cf9f43b065c262c2cd72ce795f2d8e478fa583b974c4df6d186c5cd371e220f74b7c90a345795ebf497e20f303978c2de83f4cf9210489e6839e33704b83a0db2d0f5aeb96d411575afbe80a66c2e657b574f4e64633bd5b56995416a2fbb7c7b725ce0ddfcff9f1160a7da9ae2dfb4050ac69ada24964ada771b6dabf7fed566473d6b45611b8d569c31cdafab29c67eda669e0173e101463aa7d61d5346dab2dcd5e9b3530a4453a353a0b74c49e3759f42b75d5771e90e64ec84584ee186399fe5b26e9c25f9e5aae8f534950a8ed8b50282e630dc42edc247381dd4db977316957092d2fd1f3288e3d91731eedb6dc7aaf85a5e3acd37aee79aad6948ed77e6ba6bbe33f98f87df65a549fa4ffbb3ee6b44c208fdce476bac31a32cef3c7b22dcc86e9c6a45a6afa6c0bd2f15d777ea9a6fc31158bd565ac195cc32cd31d6797ee38ad6473a7bbdf6702e9eea6587e79223bd7b54e045399a4bbb34a33cd8eee8e29fbfda779337b4099077079002867f8caa953f5001928a7930d4161871850c58fee2854e7b45eab9051c5872a33dd314f93cd5e5d7482dfa44206950ec450483ea633e8f53e92c802f570460f5a7a58a2071c321e76c0c3183c8080872a3cdc300508dd4dd4d91424aa4c3175479bb363225c0d5bd7ba12c9d597277794128de52d7b5272423d764daff53495d2b49a78bc34ff7553d89ad7b6400aee1a62eb596f62f799bacf3da6f3864dfe098b82738b9265888af418654a77e45850ca7c129c5fcca0bc000aac3b7a9cf79b4011b20ad3917b0f0c2f37c31ffc399b9d02a9564d74c7975356b61aeaeea1ce56dd910b5974a3af5c6d26f4a53be696d096f6110285a674cc8492740bcd04cde1003c1cc001073871400a4062004501b40350fcb9c18f113f36643f343e623a5ace7bfa728eb2becbf324fd14dd9443cf62d6fc2edf8c2f55e56a6d967e2ab562ac073fbb9972784eac10731cbdf158fa2258f8f9df094eef7e91bd1c655196e3d07e9dd90b3f8aad073e572251964f0f3ea31e13643d5c22f737b42d29e5b3a85722b5c7cb7a7cbaa3bde0a952af5af6c48f3844453e09e3c93f09d21d31917927085a4c9be09f2f27649c84619d34c976b8a33b7e987eee519b5de6d06ad2f3f6b5dacde70e11d881dba1059e327884e88e7fbffbbc979b541ee79dd5ae329e998ed89b96e5d8f376c84430cc76b6f40e9608a4b39d6f8785a639ae4a7ddb1414cadac0eccdb7c1740875f026707447f0b76d7ead0f5a0dfcee8ef43abdaf33e36fd2244bd6447677931a9d385a678aeee6a1339d263ade1d256c7e513e107f41c714f6994e8e96ee48ebfc7260310728cb99c970ca74cf3bc10c0707ecee327de130b921d3dd38eeb991ece64b0341f14d968e1c8f63dadd905af6e684ee1537bfbb13d4325511aa9cc8e48e8cc9144caa749c4c64e8ee9835a08bac014870f3bb06ac90a564909a22d5f9df99c3736285d897ec60490696e8605326b3d980cd8d0d094a62405f6e2a5d22b1efb26d0911622b6d7df7f3c955ad5703b1b2a8f453f6f3a6758a660000280053100000203858301c924a66f301d17d1400005dc49c54cd4696c789cc29648c21c60000000000000000c000287b4bbbf646a6a0db77e8ac574fb741fb4c581aadb6453d9f7dd35a3445814188d4fd87309763c28467ae107a8e6910039e25835cb72ac6fc3cfcbc28bf0b400beb2005cc7afa1fb52db99fb706ea7cc0c3958e61ecc31c08f746b5ffa18c439cd8540799cf07e1042d0eeaef5e23f9a12b1beba05b8f169d6089931c1a5eb6f639b3e11c6df33372ca3cf053bac7e084c609db5802a120e7b4625d38b1acf7ad74047537fe3cfa14ac04eddeff2ae8323125ad4f9c3ce97a7f76c3c6dfca060ae601714900fdaa77a0caad0089927af59442129f412b53b5dfedf275be7a0c75699b0d613810565c6b69ab506e35637dd8aa98e2c54180bafa91184b4e0a90aadae9bf09f65d20d04ee886d4bc515e6db6de97ecd0156989fe4f2f282567d1307dd0b9d66a11e7f5a435d20fb524908a9e5f97d8440980e885a8640d0db39c14a82c372812145f4daca50e2593ef1e9e4f3868bdf3b03d24e12ef02321f7449dc3514764370ccdd1428cd4c04a7c172d85dbe9c4151376c4f556eb88c733e0ba3576791d7d47b8f70399e5f714f0ffadfde4bfeef8452bff5ef88eb2e1627a824592882e769657882b4a5a362f01ef1f050cb7dd7812c599a1d0bc450c0fcec18082691a1f0daa58f77a77a481743d4fcab8d4a4902952b19f00fb39b849c1769d203c846442407b4e9d7418e7f7035dfbf1c7c6b5df201b8fc37f469003f2e55a6c863436013742d741dce05b6d8253744c2b4694760eb489cd8f5ac4a6f34755e70da9ba3a244957e69327435403225d1c37f1c241c98b3aebf36f5cfcf0ba8fe3f2c45082fddbbf51052ef4ca362eb068a9d0ea3a13f157b149da0940ef51e4d44efde96d6af48a10567a9facdd0b2233ec2de6f6d7e8042ddadf6b68f530d021544f29a181d3cc76af22a313885ae8b64f1c5631f31a2e06552ceea2326359b31485447c6853188d23046b0f82a171d6b5ea416d01abe013f004d15e96ac260c05cbf50af80427609c23625665fc89e2fdeebed34609fa79ee2608f85be1d113c53b755a163a8a805fdcac13c474df7c51020d3b7478bcd32c2b7827e0a3e955f409f10afd51c5451131bb8c4d6a49b0a037d1d54ef3fd9f9a017ab4f56ccf2ddac4ea215a8c77dde9d0d1d34a5b0cd954e773fb57f24d7f858ad1a603d361de91be987b24cdd7f65bfb482c0546a2e0cf7df62b2f53d5081ac5a05413987434e2b4625e3399a49bc0d8c6f3157764cc47a927a78a0be9908905dbd68402dc60274979292c1c7c1a53a20a1d2c7fa1feab644888ecc9fc799014378272dd3d96b82662dde3758197fd7c4fff304ea113a73f37a2eee1b1a70eca70aae4c17dfb4f1320aa93187bba2456a7442dde5c569dd8c68c39858f4cd92d82cee834489d3dcb011bc345f4898175794a296d0ff3464c05563152041216713d8327f0579b9d87389b2c0b3ea9fcc040f15b42c0d05e019040a53506002e508017500001cc8002a03f1e8f2a1bfa46f4fc320cd8c64a33051b78cb11e0bf11f1c153dc2d93e951810885109810156a522080ac222ea5037173db868d436c26e587159c070e798d830b12c1303988d97e1ce97dc088cf8bb4fedac31b1a5bd9b88bb84eca8ac6c66afb5265273cfa22e8f82c4c3b50c09c29b6e957975a6e52fc84c2701a17de2beaa8f88510d5da025c3769f2682d1340ec42b33a81d962e60e7cd21244637390c0faa3b6f9cc85b02fa96b8c05d06f842753bf1027dae5adf1fd3488705400a883857497ec6889ba67b02109918ab32d9fac2a58863b67fcf41efc504dc47d9f366dd8786e03844d2cb5477e35cbf082c3ed6d29f868842f4710957a066cf4ce4484b7d22c65208d5938a869f561a17deabbff9b0be5b8e6c0596a6016f9bd8b158532dda9feb460fece624b76fe8e577d4aade2fc4696338dfc80431cade9b88c9134fe115da9e597b92e50268b55248f3c711a5289badaaf583778c7383ae40e894695f7577a76d5d0bfb5d1bfa5cdef93fe88028f0137679a86609b8be4b67f19d337f2197d042fc7b2f0c5c7321449b6bae003d0e635092e190ca29c61836c15bf72c93986afb6968561dc0816809367843a6b17873d83ae959c9e4fd8c49cae84dadcc9888b2ff20e51209d1d63346c24dfdc998f9db487108d60fbbed4ba1c37c083645e0e0879cdb1428c7e8187bc464bb98b05d90c675ebf19349315f1dc6d91dfb7429a98e0b7bc9765181d198c5059677f75d8e229fc0c73ef5345f97f883e6931b8f615c34f04d5d705a26ccf5a8d75941d4005f277affd11f5a7e4df621ed8d115213e0b1a690068bec2090017d221def378537a577f896453901e32b08cf906c5c164062d1fefdc9edc38538143da37bc52fc98d018274277a8c977f1e8c539264bcd526f47646e8194c95c063927369e0bd39da75e5c9c93913b7f276d11981fd9620c1e2aaec6d925e13f1b4232bff62b75e36a756911f88a6b5b80f4b777d4b71e583c6fae9f3bd646dd6526a78944e8280ceb103dc99a944092a4e5928ba1ce3902c7c3246ae7a09d4e34f97d1547363c304671dc01298c582059493fbf1a60b93030e71264094b37694b57747b5a50af1377cfc448df774cb49c5da009d783fbb95c032fd9c1173be315ddc01f3a672b23c29c09c867185742c6c4cf3c16e7682d75b09525cad50a260ea55a67ef74c80056f3b7c84e4ecf10228f22c2612cd796e565d3ee169fd520b3896e30080cd2818d114df7d313c0f600562f32ac2578ad3c941bbf322ecf4d2581b07a53d433436e92e645555471f54bbdcc40b5011dbe848cda144adb9d08b88f3a14aa62d189bcef218dc45eb5d5aee888360891a3723aa961300d3d9e402a5fe8da7ada2c2d07f67729722660ed8d7f4c0f41d77a07e9c4247c9c468c7211d75170a11aaa255df63e2e588f49650b8249f0a73c84c4a5da0c8f853de17c761d85f366776e44fca06dfb0206b5337120a04a805462bbdb99aca3067fb715cccb3962a2fbbe1c585c5cd6e5f1f43b7687cc572e316cd408e7498e07a1f1e9aa7641eab9c945dc29b1e2da3d72569e822c9e7e93d700faa12c38c15a750eac5f21c6739d5c6f7a35f793456f8028405ebde0b76f996cc2ec0a3b6b4f86d737afc11239c4dde06ef61623fbdf9d1e2c81b018eb2eef01e250a5116e55376610e9a8f1c09e3a0008fde113d20ac6c3d2925bd042d77d57897e466f0e9532f05e34ea2c5dc17c15e1e02c942061a70a685c16a4ca1c052106d4218e33f25ec58fcc8443fbcdfda4981eec9c1ba694be4332c87eae674d004fc1267f97e24a073c6a379504b4500562b8f695017577699fdc9ca273b2cfdc85166a236d86752dea452c5ec48680f274a5cb50c6e3c2b0fa7c5d1c8cfab2c5d80ea9e342b029dc96c2c07db7ca3175cb03b4d9c879261620140e357f1c1601c91453091b85d7cff17f8fce48b94dd09a2b3adcb5fdd5c1881dde1c030544421dd75c8bf1c8bdd7b8b66eb98b684b179da1d2e8ecf8a079a38d488aac6207236cf8240ed1858f5c36e418cf949fc70918c7182d4e31628ae7971a8c61c545828923f55afab25a2d6631a3b56a8615174c7d749a54b56defd69479d58f4c0646757be8b87c76fb59154e9f9aa3c7b5e8fe706c8948cbc3ef599d58f3ace5022d9ae6cb40204a4842181f163f8f3b828be161e2019da7b635348f1c13a41ccacc9ccf993508133ae8f9e414ddde7025d572bde692e7c98c1a39acc94014ffb8b08b226801a52a1564a8463bb66f30a115844759fc0ad91eb45018e83e8e02c7038bdcda21fcdc2adb347c97c634c6ef9bfa156004317638b961dac77ca40ee3bc6f5ab85618e2f887393cb266cb8f95dda59f3cb03ab7cbe45dc84506e5105708206ec417ade4efef46129ad481f0d77eec59bea68652f7d5cd73555efe46cfa879781c1d68d198967d043c43ba399a9afac1efa26702e6363606e60b7453c08b0c86ade99cc11682f98400d4b9d81cc3bb2ab400145f863d7a315db40cebaceceffb98ec0fe85b55c670985d5a1a862e5c1bd81d39e1f4cee51ec1e5e81620e8ade4b5c54e662f1e789663bce3108dd1cbdb18648944b4f2a893078a3d7c6b76dedf193e8439eb9ffad62b48941925d578dad68a68dd11af3065bd40a7c02820287f113488d62d4fcb714218d5911453fd1bfcbb1f5376c075eda9c53d2294b58467ae779c8953db32bb797d37d03c7e5386a00a70eacc6d250e346191bcb6b3ffc14a7902586f7b4800320ef7cae5ab624e40c9b551a93441cc9b85d82202688891841a66a13666267ca5438dbb587794865b79e9b129619d97ec21429fbf8774854e1301d3bad4e5586f3ebf768e20e3dcf6ab4eb4f277ba76a1a286b3c081b65ab00164108512a6a913d777734eae84ec13f58df9060fd3681ae36f3245513b8064d1d4d4b4af1a7e44f0e5945c884728bf193a7e29093b09591e6dbb80bea877b4368297495f4f4e8f3d736cd4253bee267f58bee6b008bfea1dd964125f0e1ffe70fa166669bda2dfb080ff2a2c18052030b96293f2e9723f7e022255832ff2ef3bca53d461b17fc89a2f5c7589b2fa1a34b6457bc98ce1829cc2e0ee546873f0cf6fbaab55d1c414d4b582acac502857438d951bf6a30276238815fca3d2cd209d8a5fbb55ebd89205256895b935277a1d4a505869750fc1c5667324b77489811dbffd7d4c391cf2c09e49978e26fb1018d0c54019d33a70fa83463fbb8c9fffaaf136b62cdc80e7ab55d1071753e9bb152942e4a3d068329b3e1be8e7c05b11e4fd37d4e07f8f932e095de0faaa4a49727ae705720da68567b9286e8bd5548a3499d0197215bf927135ef1b0a3c518e93ba712837f44ee538fa8bcfc7cf4618da8be92a6b7aa5156320c6d3a49301b7d09c68736dd7b9e80d4ac0bbc25b6db21c5b8fce67ebd08caf43a11003fc127ab01a18a06e177a0e409377cb85adf0bd1835657af5e26a3ade958bda6c1b4156a4b357f3d3b0fa09226b13154bcf0178f74d8c9267d1a8ee1373c6968bf5176347896a052821dddc27e66e1549d99e1e59cf78c4c0dba3b7e0ae741aa14ee1b278d8628b1fbb7772e2e147674796b3f5da7f1b98c16e135b8d855a311008b354664e07f457ae63f2c973e42758da51eaa79712990c045e75fc8ce82df3c62979534db22eb4fc1979fed33d27b6859b7b41bb8a365f4254434cda5e20f3944ffd55187a74d338bfdf775e2a3a6cbdf0eaee88adb2c45ca13312f9db8f7ce0b07a4236b3591af3cf9743c43caca33a5caa1ab2304966e4a361a1428c4ae6c33dabaad85b9b486c8dd8d397a3b14d818cef2899d4e3cc142d35596bf31d1686642d9d3a946b3905418c397545db2b05886dad491bc1920bbc1428135f1721a50cdc2843444e8cfc0b69fac46c6761f548cbc98f749c1a4584e58899cbf9301b20ae1b67589949a944e006e95a12744df0147ca2cf1539381baeb41cdfebe2880521b9cb6afa82f34690071db4004fbc32f7077c0d975dcaecbe227c0b445692e4b95449659eaef4da711fb46c265832cc450561dd8a0cb494e5ba87de22509c28b5aa426daf66d9760eab81fb2d058d3a7c6b26c4e55e46b251e3db0d28c8715c4c698546545bb91429eca6cf6f53a74fe1dcc505312ba3314e149597e57272b82ade62bebcb1bd691aa7c2a5d9ac014b225c404df58216b3227511061214a07718b836e0ddbbcb2883728f151bbbafb76b5d21acf7ec88173ebcc68eb241ea85cda9e0a1812d2fa7df2dc9e9efe1fb9cf3f0407de1a6e921d7cc7dced0c0c1ee90def96f6bfb2407f6a0bafa4d8f1672e78bd9c4b3e8a18908392d740f9e6cd2d3549a1650fe017789a6440ea81d219edec28554514649539bb310924fe0e3d75b3330bf414b8fe4d177d8a25ae5fcbb22ecd464410fbeffd5e8f9c1ec37ff66cd33a3bdcc6076d1b29a25df640e5200ffbea8f72de7660cfc156dfe1a2616601c2df992c2e4afcd6f5333444b9e16207f0602cdefc667cb2578e88cda8f6e1cdebf76c8a793c3ac0ff93a31a4bb640e1956446f72399f93036daf3160f5aa50ce23e775f6b85a026470c1b9917989c528d8959bf60a598f0a57ae85779f8aadbdddc4cdd8a2d170a46bafbb650eb50cd98157c43e715a8bdb097c031592c10de033944c191277b7488cd130f3b25c75df178be2d3589c3dcec6134ba6944e3d521c74bd31440ba7b1db2b2e42a7935fe6061dbfa07af573356aa6db2c6a8ab8d9468e26d9d86cc08ac1ea3350e6232d6185472b191dbf126f601ee57d84b045e8182c11e08879741137028aaa6df376b601d03288b69929fcfdb091890b3e4e3b9b4403aad2e6f5c33cb138779de711764ac4b085ece66c3ee55e7f35e5179ab1b06625a878a02f0a2860406d81808696bdd40a3b39c6eae514be5e5887b0878a2962e10190b1b330598570fb5c361c5c75a5f60997e30e1ff70bb6376c4c769393479283540501f476f459a5912308920391797db5b43fa317329b636d8f89fd4c0a0fb8474e2afeb6103767f8ca72090c71abb77fe0c76fa36870b82b6f58407cbea1ba2eabe66c8c7c0c6610ad58dcab4e9e822e9be3105fbbd72169f86a407c7b4e81e0fb20f69421ccca3abc53697086171f59a71e2ab899facaa636a407ba997b3cde12e1ea11a9a200a3c2e581ddcda562224b034dce52a23e0a541de8d4e237118e9d75b3dd03dc4d150b4f37d864ec00c26d7f8390d4dc2866f2525c00bb56db00899e771e101dde498c15d44fe9e1c364cfb71bbb7d82761468842b9298fe92f31a795357b543d1aa4007e079a4d63bda794c4b84e7eb58f4d196f0a78dbeb11f2dedbacfca1e7efa224844cd376f9335de3642486d792bb20ddd52ec6e428006d60283c1ba936303c5602687451757bbe1f24675530f654d3db5c07a5b120a736d25fcf365cc0db3bdcfbd336dc6a2f7865a2f88a82bb2e6830858ee0d55956c860db08605a292f8fe37ab7e365d0df07823edd935efc5bfadfecfeeb174b37e36bb60e6090bc85452ac06ba21849405080756272244f5884f5874b2086e2a7afcad5e12d5a41bb9f03b94d5a7414dac9c04c5cfb81b994b46b29790d1a27b5c99ce257bf5bd76bbaa5f38622ee6b1a75f8012a27d7458cfb3ea48737fb61e1256c189dc2693b633e1fd2e360110bd8de7f8a6fde5e025ea4a29e2f3b2242f77d6f6503078ee48c0e0b70c8f0dc3a6561870b86c72e49aeb39c995404f823fa9b6f22dfe1ff321d459bfe4b7b781ce41c7e0c9b3c172748c2cd6094d9297a5e013b54ff4247e73ce51975edd5e4829e8c83dae3a3d01f5b2883186ad02c5fad8e2437157cb24bc74ee70e5eeb84388d5f452446cbf3a5e6c449355fde317fbd8f83daf8a9474d335b12f1ae8bf36699c684241c4bb4aafa2b3aed0d7b1baa886d5fdc1cd1bd931b20bec6ce98264a7baa3e616b3892f304e20625e3e275677e5177f0aec3cc63904bb53a841146947723aa5918ce4ff34fc1cd9f8160ba3132ee020d079419af94f30502a1cd21b53029baaf84151d423faaf83cc78aac3b1329e0a35cd6dfa6e16c01199c00a6a08de2ec86213f8388a3c860571f02c3d42d269ac4616931e15b88a55e2f108fe0dccdaea60b9d45b0fe81997f26be0411480f1444597f4612485bf02305ea0d575b0c84553bb522645e1f2a9b563bef632ce8f9c7587842806a430f1801f6ea386554f46ee03acac45c10bd8b3892b5e601fd0209bdf782e594a966790869676f7952d3843789f57128a8a174c22054820541a3573c03a711e7f4069d240c482224a81e031daecc3fe259063516b6b78bede3f1cda33f8fea95fc3e0835025f47a1032625a2dec60e22340d804167f4be6edcd68817102f0ca9cd87e24f631af887e3a99226fb2a526fb740d54e7e9518155372dca7f8db31828bc29381eecda0107072fc3cbcfbc56c773ca31f0f4821298be704d4fa2da64255689cdc415be86ea326735bd8b8d852dc911a31ff690b09295e7a6e250d8aaf29125ddbbc8060f38b81ac714a090b8a1061c223aed506cf48586335877f0a477a897ee48b7de59449e50157150420a07c0824c1dc8b49bb757627fb6c6c2e408e21c3fa17d88341cc6e1b92c90b7f9bab2b37071ba57ec4d121d9e010f5fce992bfa065b0d75a3cb6f85c204735007978a96f0d5b796eb0230a0f6feb191117bc911c25f8bbd5db928715d53844360663ac56a2e6de98618d31de5f37bd426ab9fa4346d44058088cb8dd4d9bf01971a73580c4c0ea3dd4783172746f403fb90216408566c72d4effab13e481b77e506173a67cd10fe288083b18f9374c14c0c0946bed69164bb963a60f0c079c7c4b4a1babf36d30213096c5ca7919ee6cc4ec0e307b0efa8666821aba1b5c32975559ebc63e1b665fd3c753b247ddc860bdd06e6537b50bd88a0c62234c0dd22b5b01218bc5641d9a992a378be15da51e18a4e77fb877042729575a8beec5a7ed31df2139a9db4d7aa8e32405d30dfc9760483cd1a55a8bcad3845c21aa4298b07057d4f086ddcffb254c4f42e86da704dac56a5031800fb89543d116c25b2ac4f42a5385898d4d5a6f256280608fe3b9e2375ce5167c138b3b71cb85a4547cc6dec8ecfb84e143210e8b06cc324c2d973bf9a2575ff3b3af90b7012c6ce1db2969b40768ea915815565faf033ca4c9166adea763649b0dca1ab76599bd006c2aec3caf998eb507bd46f5b1f1c36ef7aee90ff8b22daf6b645762add6250aaaf3a6b345a3ebb2930723684373f153b1c4cefea9aa03b676413eac30ee38d4c134d36a8fdbf5d7ad9e11a35f57299942f5453ffa6fbfbc7ad2420f89a809201546e43c4ffcffc6104cf5f2449873c8ca4ba22b10b7a8b45ddcc7970c29631b31420b9647f60a0520cd9cb111e218b1be827847053c1222c87598ed149fb8e1636b917e884b75b3cda613e86de86b253d03d99d26558ac11751e03e41bb72939497e65f65f726c63a8004f18a769eba47b61a739d3a1095bd354c586cd3f6453658da2b911b431cebc890a26b2745ebaca92720de18c6bf9070b3d9afdf98d6337e10d81ae99742c36c191a9f7afb5c4ee4187de12fb0a7826821297ec3dc843675d41832da87a924730d9694029b345a7748f779e15aef478cac6af737921824dcb012b6f9ed67e7aae49e838610774d33f4422112a6df4f3cff403493a1f1e487434f61b8b83baf6cfea7470a2188bd01a1541c5dcc0ad5d888ac0100ceacad159c6ca6a7f486915e3ba4262dc686d15c85d3e6b75be2a65cc3cc27a3e741d0fdd448527c9cb60fc6644e8e2c156c45f204b0ef8e975449f5b1a4983cc05f55fbc1309c238ccad80f1292de853361bba539004b3de706f73c30f6946d6b4e03f8c73018d7e3696573c2f29416dc83e1155828e76db4b20b6f462892f2875e2071833e34959dd507d469b2492168d62d0e8d2ff82dee2dd8c449094ecf926cc8346993bf9bc61f67240117829485c8971ef45ecabb7eac6cd26daaa37b4cac58141e38ab8b8705fb978dfd1c1d41664b4b81c1fcf4ed068477cf17bc182bc2b8de3f1a16f38e6fb76d92461695a5fd67ff581f06995a1e9f5a2eb2846673d6626601cdb46c0b541e9fa151b4493e4d74583126ddaf49eaddb4c1308a4e73cdec928bee94c1f889a20e9ef5aee141b89c0efda5a3f75c3b598f43893357c0dd6df18d4e62e6a04f77328e96d313a9868c8dadfe2ddf5413b9088527b491c4b782f02d73ac21fbbc62f9331d5b4f02f9d80f64432e331634b5df5028f3787b881d708bdd4703efdc810a1a08b2f7c8a85b099d1ce7de686a3741ac7f6dbc596e06ff288b95d2166eb4a334266edd8bc16eaae8964b249deeb1fef33ca78d5d0d59ddc9cd7bd99b6ccbf12f317ffd0a22ab91df977e555679f7ca48488cdc0fa71921ffb63e2ccf36908ee3f124c056d5fd6f4c4328fb152a480964c3a483f8b0ad3d8a738917004c1d10a85137709440b1e4aad05659872a6665491c811b0cff94c9c8ab4ff449649190256ddd9ba7ebb3b48d6a97ee5a84b4e172642e505d16e6ac81f29241a1eb353c07cbf353fd96bf656af5af0829aa19242102f4417add93705b734b6466c5d4c187dffa705597e3cfe62b1c6af3dd122306fd40256685f7933b40e3428b6433a0863cdda79badd241d1f5131f359a01c02b6df6ed166c3b2ceb298c3e21e17fed932678a4796d1111b39f96cd0b551ecd14fe6123252f7d4ed7fbe37886df674f7d772d138e0a26b979f83b91d9a59da2b44c45e69cb8f907daa71727c072eb0625e1d8cc6c47bde824c9d725bf0420b46eb9976476ffe45ee06e3f09ce949c3322989138e21ccd3dc7984a752cdedbbed02b465d3ba17b725752c9e3e86b9b94aaf93d873bb778f6829f4a23ac4690078cf8b368ff874c05b69b6a58d9d75b987cdb77c617b67ffc3bd36900e3c5aae3a168e64422c59f4e2206d00b851821bd98b40bbb9927607007c5e039ed3313643b27633349e05386ad7326975cdfc1da0eeb5aecf038975592655eb49ba4257c96a0527cf930dbcee9e78931d387a5437e8c4d4ff0e04b4aee891183ce2112a50afc81eceb31a5c7eb25d76945e23103ac369a40614cbb3a43995a956cd68dc584b44ab83c89b1220585ed86d27d53e15d2afb3e6658a2a05b36b97da99962141ef01d9ac503b9c69484227a8cd80cd6cbfb9cea9fa3294f9775e2cad63a75a5c0c24f0522085b14d6a620c8b63562c5d83c0cd4d087cc9f045d3b4db1e6e79ac9344de48a54763e0883a18d157a67b2bbb0c0ee5dd1d79ada0a74e7a153cfa0a381cb7545cd40b4bd1ed93e90421bab28f93ca5a45f7b23670342588372151b92fc657657f32e90ef77ea93d7801057fec4b481f807a4474320cb36605cf2a2eb78b9a68b4b4dc330bb9cd0a58657e5214cb59c1435712ec836a83236f85588f44602f89c4c11feee3c279b284dc03c925c2ecf5270e3b37c27ff56dd8535f008186f05aa843230ccb794ec430701bd33bcc9a30a59f192244c92af1e82e6bf22153a0641b73708f17fbe1ad24b39a9499d166ea658f57c10f075570df14c251aebb7b838835b0918e139904940d869279db68b65f54640e362254f3e33f00ebb03bc547955556ad74832e6649edaaa5033cc19d1148cf36cbc6362050ae01d6707f20e96ae53ca1c27fd4c310450d25d05549959806db0cc2172ce4a104e76063fa1c08d56d7d2449e37d52afe0f917c6e08d73153f6c3cd33bc0753564ec2d9481e6fc579c915841754b1eea700a2722271e48aec2427791605ebf55606e55362b3a683c1ca6cd9628f4c614e9fb319d634af569ae96882857bb9ebd8b4115dadf1117b4ed9d2c59ecc478f2526570db5efab59320433a3ca25016fc028de681b60108f3464f86f5d9f6b9381fa851f1c423e403457a01a7372c8c49c1f8076b6df0fde364cd4b018b1eef648be14e2b94fc22ce89b64e2d25ede5ca682db9b6349bd64fd87d11034fe419af0586ccbb384bbe58749a277bd4438817914529a0b402e19efc8685be154f5215838270ef989f64dba8d6710c7e7304afd7500fa13f1453982039c0569bcb080d395b76824f58c69c516a13772e989443d524ddee5d5dcf78dd17319207dc0cc7f64417f4f2558d4773d656a933fac4b2b1950bb32d91794093c9457bfbf1e591c3ba2409f0e10a220811e65cf96c39d8de891f9dd545cd7be1c5173a6f2ff431b6de8bd95390303d150a515cfe9ed9f6d1ca3e364e8db059a7e28391e8058c26e88d6a7966d4f52de52327785ab31a8d6843ec0111b8abc5cdd558838223c85c8267d9c0590c96a7e54d65a1b0d4c87f07d41fde8cf8dbb4343a674c222baee81f25f69eb1aa2abc5701d686e0a664f1c6b2fe85cdfaf1334317520ff18268cacc536f94240bbb70c2730d34a29c305172c4eb13525809441bae32123b90cf62ce8d4e926528dbeb46ae031772499c377a96782b58d023bcc53e66efd2c2f388cfb19c3e034083653e63d80285b46c991596675307bc5cb7706cfd51ca4fa423e052e5918880ca6609c5211b6bfa3e181e69aa3e5db954cb8018d6f096831e48760c108c2720657c14f74bf1c5782534045f96be8c8a8e4c7f56b74ba6c6df926e5abff63f9a4434fdbbd77a989d05e0662f60a6d981e262e2cb398fe246149e9dd5a94e4b180b6f75cc4a77a575f4002b80543f7f7fb86e431c14cc1f95bc6f42ba0beccec5eef92b2dd9dbeaa4c0becc9fb8e7cdd177e23d21d987201615df6b306d01a0ca90690e19bd559c2b4e30f0b9a182065fc71e5dbe1f8542ac4a7bd0375210ccffef34e23e7ee1797e742489b1e2873d89fe4d66d0d76c4b7ff4d4418a1fef36e730fa21e76d99504c3adf0c5ec0d7cacdf1ab84d41575477d4e923e2c96215a6d85c77707c2206450970fedd0b0a17ca06c211eab266132083b8c43ad03933f210499438e45d8454b1fdc4b854eb5894fee5faac63604c07280aeb872b4f0ec664d592050774c0353dc93707967310de28193b68bb1154799eb37f28d199505af3e959162255a6481278aa9383f6940cbaa6f4fd2bb0d6e06230f2207a1eb7d5f91c43824e99bb92018f3fe56a7e1e53981e2173dcc93f3d8401581a0594b1284938ec4ead61165c52967e9c80f6c1e2d4bc997565e2d9e249cfcb785a8dca77909db6e2e231ea27fd94d512cdedfa1066001f07cc4bd22caea4d6ae633113b3961761092878e35bf9209605ee6d0d6852b23b717ea1436db8d93afec11056134cdb83ebb109e9cce98f42eb690e6aa8ebb23cc2d9e64481ae3900ef19ac21208dfa3f6419768cf535490b36b0c713d040d96e1fc9e2368390a34baaae333f94e90e0ff7c7ba4e3ddf2534f61fa8af1e845935738bd6095a8803ff75580caaff5c8cf1e540b41af369131006f5f48b1013a5dd206e860e6537bbc35fc6ab827c000e46fe2cf5c6af5f4eda9a85cf772057baeec2e2aca16785850ed0e359512aa1e25fa3cd026b5c3eef1a89110a980aa5708f42e72c13903f278c3143a2d7f87f567ca1e7fb40b0a8aa68fbb7e184116d9053f483445c56221a9be17f73f501fec3785e86c8c75b677d483c613b370a459ac11829066d09ece0c22c3287a381f7b9abafd1c018afa361eb61a6b1ac1b21b229b2b01a2cf90a3c7a65ba726ccb207e20d9610320011d1b997f18dc63bc0281bf11e4dc3573510edf900f14eee1892b8f589adb71288f22be880501c2a5d0f185252bf0aec59b87a4438d4cddf350c5b16475ef82734b6a03ac3902dfb9c79e07c8f3bec53ce2acf73a566e739ceaddf0196b2458617543a0c99980dcd8c19ed49e07c36c639ad618b8b3d3f44634e964c978af285df8c912d909984b827297571f6937a1623c6d83f8f7cc7cf6490dd823a02bdf3593a0b6a587c469b1e7f6a4c296392f278632b54b1cb6de909f15f7e45da1866a28e5392542d9ce59b9374ff553fd7af5776b3ea0bcff35b2d65a9510bd91269ad38851b5bce6ea7a38d3fb0db944664292515282e85b05fdcabf0fa4849f878fcfe9be77fc65862bd0b04587483b38521256a7060f44cf75eacd01f377fc3250c38216714e24ebf03dc43bc04ebd5999a91808fcd9f21d6b34865e01c1b35dc2e0339d574670e4a9c9873aa1c28bd894af9b00b165257ca4178b796731b3c3c8163152ced0b7e885d3f87283ad0284611c9b60d731ab5a8b99bf7555317abf1becb5d03e498f90b41d50c1b1ce7ce7ecd971fe8554678bbf7008bb4ead6b4bdba9856f921438a454726d21c01a3fc86ecdd316a854a15bdf0834b1407362021cf941d8167f8654e7a9432ea272c009c3c76c5101186a7662f16a3724c868ba6692e016666c83af8fc2d423a0370f16c6c426f4f37fcbfa7cab1fcca20006d9a982f6cd449db555bb066b2a785c0f8478be62f94d38df7f01de34abf0a3eb208d6ed71c2e1a62ca37470289a2d54a8419827d5e7477bfe5b83d809be18a3529116d337efdb1cd1e89638c3de35b0efee2ae0957a072bb6bc4db8e1f6e39b84cc52b9d8ca646a69c42474c475793d49f2d4b932411dedff0b25aa02c10d5720f4e34eefd348fba02e2536a071d4cba1805dfd37f0d229fe0726d75d96ff47e4f4009f67d2c363e9138685e436e8514ee0f9f661871bc3f52686d55179ca417d36e69693a4dd1a701881bab4edd79ce1c51763285864d00b730474f880b1f642c2c93b582461c668493017a881ec120b6922009eb15c52fe65677ee3d5110d2e98596175727d427be9d6e6b63b4bbf3db2cabf6afe8023415a3d988715d8deb4bc4f32130ead91bbc6d0eb7e4d5882b9fc1d07bdf6f4fc46aff44743c7d45bff46c6668fecd996b52870bda0a47adadcfd57311568512972830b2a7763af3b4e8e9f0c5aca1063348bd59ee79f4009de46e75281437a4832744f2802fb94e462b9130a9315b70315228503388dc904976b0b7ada0d2f4e1a87e81713dd722e0c0a02bc82df5156a485bfe468dce9e7cf429ff1d37daf841282915fda248ebecc84e0904f45f179731a0e4a128b754f49f07973ca22003b32e8140513f63f87bb07392b66c4a3ab7fe96ae9844994f5f6217a586c7bae1c05376abb8f01e9ddedf4bd60a4a2a8005233d6cabde6b151e641873bc0165e1bc8e0ae8015a822fedbb5d0a81b2f0b92b8337b0c3477dad510df72459fc9e5acfbd0fcbd299dd31cef01ee35fc46bf42b75b66a4c9893f254d17db2ba6113e089278e4c05d60a2cb7352143a3b33b6f2562b30276d4457b0f08245c4381884be8f73db855b521f09b49663d9ecf891be393c72dbf34fae698a6e094070d266b6da594f88ffdcec1df478066a6155b08ea4a3631029fdfed8a5ac7bb878af868242282058b100c93c3b2ca6f83edd412ff9370beab5f2b34d53415b8086d6b96b6a9f0ff78445f05e072204b718111b3cbfe2c937782106fa71ba25125506420e1720f3e3a2313ca2a171001a94b8803eaed5e91e92e650b2058b5635bd5885efad62b6755f7686ebcecc811c090d7fa1e7ae72001bcbef0be7cbf50496bf475e9b0fe3ea9b421e95a25f91d41356b4179b195c7ae3e2ade650b644ccedd1444ee36d208050215e7bb24725ce82f417d829690a3351f44e8e6a8199a1cedfe1a2fafb437ee68aa8f3fcbfa2d08708bf1d0e80118c951c2dba8607d949c48288586fc4e0a436130084d6f97d7c6d22b70be013c8d17ee7835351202ac8cea7ae4c78d2096ba01be5ace292c61bdbe46d544cbfd499458156e0ebeebcb60acafc36a0ba80c04a301bc46fe0d3361f693d49137bad3a2ea72d246bd3f1d220f2865badb1d847de521e9e31d043c836a1e4612e45f146be8f20c42aa0deb766b2677152dc170962dc2e009c3464bf8f69d4dc7f21e50077959d3497b9d0f7fd2d692aa40eba1b63cef7ff71810fc8f79fc6236395b36f4de8a15d22efa74e492bd5dd66c01e8ecff7c7134c0fc120b1324f23446a3b83bcf1086680f43ac6f827a66562b7cccd93cfc68668fafe400ab6338e59bc0b8dc3b8788629dc703e1a19bcbc0ed1acbbf3882e4573b807ab559ba215996e047e6c3e6d1126fe38569081660ec0023c63b07d7c4897191a529fdf2c5d41f5b3053d094dfb80aa0b93792903234f4c46373be30137f03aadd888bbbd605dd5403514020dc3700ef3df39e291c9fe2422301cd01c3037d2577a7456ae4a546afa5eebebad2d7f8e606cd58373e483d1be21d48e4803e8eea7a9166f4a46329757c0e9a4021fe2fd0902c396abffa545a09fc68d7d5d8e798dec4e5e8013fee09ea98fcdf3c92c0283f14ef63f5ff23f426173d49c2e2330684e3f2da87584157b1b6e0203e29b7cd9fbd926a81c684fae20051c9db3dd669f291ac0bfe5ec760315acb2d55dbb87289117b9a95a038c549078f91752067c9bd928b44337b12d04f016c66affcb09b2fdddfc21744da654ed08a829f6a7a60b686a0237e40dec9384ad7ef18a31957e3183b7bdb305b5f7498ada400d0b80a2a3519ca3840aa69547ce78ca28637e82843c9ee43cef6cd5aeb789080871e7757f63a6bcc03e1a0fece844300df834dffa0de4bf99765fc4e1ebda91352e4888d218120fb7c700146d9de99b0352d4bbf3e3f739da1f7420bd95d70731141bab7179c7929bdc78d08cdfc9f60f16bb6d63d62f95c174908033e059895adfaae65380ba38f12e954e7d7c1ac4acd4f7a1f038f5502ddd806293fa96aebcf51b9701b45b664f185e014964c4c004085f8109971664e2a7a4a730854d0cc8cce163b7d50cf12453c49a86b845efeda4621ff63851582ebcbb7def1b78adc5773df09dc23e7fd73f093c257bb0af1ecabfd7b02edaa45b4e8d21ce14238146a46a9e6e139f67d8eb01bd4722a3c3b02c583e5c1e01f2b166eb1ff72ed166fff83873cf08fdc6aa5ac75e95949eb7fa45052e56e1cd49fd5cea78685c068c62e199bf56a4c34f0615e4fefc016e396d37d0fedb6889c21190fa9a92b8a48ac14592b9da00ae7a109299b04d3f8d54641d82951c913d93ff6c8c1d423088a8e42aff1a3d83f6683d07ad3470a29d0d9a896e6808bd68a04625bc0142233b43ce0fb4aaee0a6cccfbe2de12f60d1f3b8ce3ee70cccd29a980cac04ceaef119c12e79fdbd63a7e9022988c1a7948b707c399bcc8e4c9b9a8b46ba0cb0bb94675096c3d6f6cca0a8d301653eac68c8a8d8217ba67cd8d293bc686aba53606ca971edcb23ebfe61696210b27ead5a5fa2f991a9082bd8ddaa1a87007c954a9a5d5151e85fa7da2bfc577b12744165484a32c2824dd011797bbe85d19202655bc303e659a15c26c506d639e949f92a6dd47046b7a783e020f373ff3d61e1a6457c759cbb70e3b206b44a9501bc8a1a428b3128d729c1549e72fd5bff36f7a49eda42d92bb5feae3bec9b508e1d739d8efd6c13486d08703b05f6c30db754a66b4a45e2c3e2fd40b73c569f2979c3dfb971fae08fa14e2b1cf3dbc47a83cddc0aea77f2b2c4dcc3c4f99af2bd100e43e67a4fd4b719eb656c25ea1496e8854beffa97122cd3c69d930cf33c8e5665157ab1520b8e8e17e71adf8df55b60fa9f8880fb299d1dea26edb82295a7cae4bd5c724855dc8dc93fa4f00e6f8e7b0d02c802505780725bc4dce1100deefcfca883a8651534b4728fa347004991efa86495b6f8c1528aa910570c51756b9e1fc54c9598cd2190774e892c9d3f1b703ad40024f833f5319a5eda11f62948c0f0f9383394acec44d818ed59ad64cfc62e17de70b85fdf84775d47fb70424848b34e2bbf6593f3e6e321f87626dedcff01833c7c95b3f27b0425249f5a98c6dd597cf9bfd5ab6f6ef4b87de2e986fe49cb27f4e4f06fa3156f824fc274fefac3590cbe4bacfc65eab1167d506f4a24a8723873bd8b2be2ac0f17b601067567ad4bf23a67e700efcf5fc19875b14c78b0e8ba5695b8a761359217c67cc6d90273df0c70688bb27eb0ad79af947fb868a57b108cf1097e3d2231095de7c65c3ea7fb4aa5442d3524b568029b2cc52e884a9f52b1fe88c365b31815f443fcf76a481db304018d6b3e472f53d33f5bbd2dfb3c47e661fe774ae4d0a05c13821056f6c80aaf3d162755c707952d8763564f120d2a2de3e211aebcf8ccf0ce4c2ccb9dea0bb0b5835982a24b618229b5977404f88377ff8b35231ab5878d40f0c1854538929def8003d1cb617f5e69da7bba85d0efffc4b7f86db1313d2f1637c39b397f078f9c0f1b36ae290515aa56b94d94bf766e886aedb7131f991640d99fbc05fe6e94e2d6997ff7a68b73d662ef9b2ad34e17bdfa4ff00f7cf43d9bea33d07c2634f4d01316fda0e9450f159c0c42dc2bc58c9334af021a03a9f74a66e4dfd2ffd8f39353ce5028d2b396220054a208addeb6547460d69058c71a5a89431ab2e9056113e401a7565cba3557e2801aba216ae6c79a499383e2adf4c1a96e20869b87b21673e987bc33f18dd130972787cca9c4eaabd6d14aaf261e85470b5bf13885e55018784382f270175e6e3ae12060d633ab9922515ef1a2667a83fc14f897b63d847664fe596d4e55c9f7960283cec0ed35162163d8b9a5e4a8ba9a4f6c46178131d44cd1a9a1135a70331ab9369a1002a99d5ac01313bc0ecb39e85e759fe82cc632a262ff2e1194b2993be8875fca36d0716892f86c08ed5587682d056f9bdd8a4b25350c53969185a8113150e16b87ac375bbad83e4af2c0aa82b2e22d4f5c902cdc46c1ded7403506f34a54a13dfb95670087d01a14d10466b6d2bb7bb00a24ce0e0eeca962a210471804c2ba3891b2ae88d36dea5ef9bc37f15e9d91d26adf470624901c0e66484e158c2a34f126e7bf0eb51b82f49ba0d6620b05688903f383ee8c37bcdc51962eb6f4724bfb592988179525732f3b02a60c1da48baaae3b2c702f2efa531d5eca2ac3641d54495d53f2399fc7043f12ecea2ee433a3ea9930406a69731070d18bbb9b98f92587d34643ad46c7284e8607ad1b06e6a0fcc7b9ebfc3f1a3a6e1fd3be3810584656a277d1de47a180e66ebf77080cbe448fc7cec0aa7b9ecc79370faf21ec4cddbf63ead031b0ab6966b9640d8188bc27355c3a941ebbdb94079bd709e149bfac6f3de89398ab76515d5b63ba25348fb4b06e4378a11cbaa4e0ccd811dde97fb8accdf9ea40377fa1542a7768a0515bd4afe0be8c6de0c1b54d215851f14aa4c38d304c9a5ded5a7a394650e37050cb0fd26382686ff61496e919dd17072f177588eded98049cce47c7230a87ff5b94e063387caa82be58f994d1621fe902c04af19d056aeb078f59c53c515980e474811a94a51629ea1c448022906a395f80d3af32820d0d1bf741c3dd53ef67d3a8bf39ef63c65730998efd912a6900585ad6b5e0da43bcda8e184ba527388c2b0ece6e2fb7fef8d13c36c3fdcb954a8d339d308d9aee42bf57258259fcdec81973004b7a63dedcc123464df297570698a0b3bcde030a5ab17f4771e5e0b125d0e7ebf33a1ee7d7c0a824c0d1ab9fa3a63312192bcda6b466bea85554c70dfe718a03e128b8a6c55e0b2420a0238a08f698ebf4ab2d2c8d2ac85f68fdc6c3719e8071847bdc15c6f97d886ffe1438b689febe2bb2a60b932b76534c5ff7fec9f285b5d352c1860a959ac8255f03614d629d1c76cef31ef80a3f48bd636b18eb0e671a86afda40c1ba7a8558e7f3d11f204ea7e4246ef584cd7337fde0e3e4a2f08d82cd41d1e2bf853ecd2ac77e2ae88489df5f860b0a4b41aeb9034c3cb7b0495ab15741849f06548d5d5bdefaf2629c3f4abdded3ff2f4f1d77426dc522fcf7b7aafafc02239361d896625155b87461ba572c6432f98a6dd813c03ff685432567e0865bec92059a57cc8e5beca32ee62354f0794288a89c6abc81f787a77a7faef0ddd88ca4ccc840ac1a843cf5846b3a589c788190fb1f7e44c748aef8182ddf63c4d750f8125a48d41e108cf03f5f6f4e567b367ca2d22ed02e2b347c535fcfdbf5ec775400606760b0b6f74f7c2492eb0388be4caf5be336032ae9d0120793a6fff6eab157fd9a42241be67a634956ef79c676dd3d4fdf4ca4fc88c3d1c7920be8f6781c1a987d0fa69b98ca0c9ed1adc8af9b5ced4e89261fa30f9c9dba1de3e5ceab13c070e4beafb268c16a3a95c9dd8a3b30ed902669bb483c6e6671285e4c44a4a74d765cc9f28951bd62a831d0511d173744f296e652414ab858c467c5fb3a42cd4b3cc0c386d06ae004ae5ed2e41e34840b3ba51933051a3579baa7c5512f5c881c52ace6d1371b7951ae07be075777b760f62cc83d52e16474cbcff08c9e9276f18e15423005536f130079028429b2d0f1253314972c9ac6924b27b000b1a9779e86f8cc8674466a58360b4a2947edf4182fe43b091c7571df4c518b68417e4f154e957913f5a3d3c97fe8551f4cbba9452d790c77a02c0856eb9126c94f99309e4fd248c5d8b88f407c313129f94f9c5dd3bf6f2e4099e079599239693697a8a4f1c78e8194d946cf8e8c8862c046738acea1b8c2470eced5d5f06a23d2da6c8eb8e59658bfb1d46623b3e87d508684759126d428a939c5e7446bdbf865aed029df164b43c4dcb9ae48bc89bcf01bf82eb4f88590e7427a5a8b006d482804cb304c5600a46222303996993ed36ac7360122d0538e86d8a3156d0d7442e5817eae3a72fecf4f7a20089ebedb47d6e29bbbdf1064537829f2573427dfab98d8eaa161f5d3994ef938262a968df82bb1d1c139172af91dd25046091793bf1796a498ac3904f56dd0e33c8bdb2b8c4eac7686ae35aa9afac62dbc8a32ad47f32733a219e2a7aa8ac786e930619745ac125e53dd55c33c979dc24903e4bdf599e59eb606512ee4fd49d3eb39745e5212709966ad6cb69c57796f261b333e054016ee735390b839e6a57a649a36ba988bffcb8881c9e4a4c8d272673a4f4d41903328e05767c8dd2fbd5f7d840a8f30d9760737943b759f18230cdb569a95143ed88bb6a59f277beb726a746d407f84d51cdf7cdc8bf6aac1f1a41c6a3977c0b059593d6c8d11c7217efc7feb56d2849ab2aecf3639fde46a4b3fb1daae25ef2d84c72f997945aef4d113d62e319148f1554770a8b2aa8556f178a782b0e1455b0e69f088a226d83bdbe6a6bc36b8f08dadb328d191c326f79f24a5c979122f81fa4db7589e7ef2a5b37dc197443d90f3cbac851d03ac5b34a03c5963b99b51ee7ad282e129db390d449d06f95d99e26538fa68c8b98a1cd9b69dbecbc40cfdacb3f0cca29b6d5edc95dfd2ccc1e318f91d7af11cd35010d91bf8d1f314ff2ff64b957695a5e74126d09a46a25366b3300ce983bb373934557edc8fd4d45714f089a130208221993647df10830b4bf76c38ac049be218916567a88d452fdae54ffbc733bea4b42d0864de3e87c11cd8d047aefb0f3293c1207ca2fc9c016227fda11c4aa86c8d81474915f2a67efb1172446f8c4f9d1aa1b3d7c293192604e7a49c3ef5e977570e280ccd106672c2c7aa5abc1d2b15b26581510864f94e2e0fcb7dd866a42363511d32319cd1e383c0be55c82b72600a8c369f2da87e8238ac4f4c8186418d4eb6166b10668670bbcdd743d1e810882184442b9c8d38dda6e6fd852532b6849c4d42b1b32d88d403f17a54383cfcd6e65eab87f6373907d53e0f73eaaca3809d5f3215db09298a9aa823e16eed2e16eb196a04d5a789663aa5bc9ed602833c1a13a3d06969a659773e4cae1705d45299957b401aaa65ecb9a8cb79267a5ab2256d0d667747872edc57ccdac2891489fe75b930d602d8788d5c3b4fdedfcb227c3a2d4fe36a0b39fe603df1e3a3e3aacb057b212940635ce1660b1b3a26054a96b3e6bc142c92468d8f41ef138f5bbdf167cde3cfa21286a810a5887343b192a1f7364f582133fbee2bc386b5556f1f42d691986837c79f27415e1ac36cd19aa8584129824146c5d7c041b8ce091d2648a87c7572ddbbe55610544a82c6e2844d54ee7bacdc59b2fa7d3dc3d9c1fcb81dd4c73e73f5a5c483143ea0e87c03e391a9ff2ffd901626a2aa6356728d9b2719f65866485d03d56457fcdec5ef393e7d8f16b3b9212fb024bd55b6d1b644f6b3168512908e1b50c075a149110c5ff5e6500d25d5f64512f6f9d3186809594f08bbd560702b7a8c2cc74be59d5932d4f8d865d904b3d9239f4fba35441042a6f6e978e633a1500f8ceb801065225e4da52c85e84a91fa4adb3d8c672f3f8306026cb9d72ed2a7f201fde9cf8b9555875582f2e7d5ef28f37c56110cddc7e9bab307ae0661febece51655b8f6612027a0db10a50fc39d090889a038d394cae6fef4243693e9353df726f51c1da37cb3bebd7e6f5858437f66df31ca3042da1aefbeea6e331130ebddb9435188ea522311b85e6635b287875566be55574a0245293ee2b1f55a36cb206b19f5866933d26a7f3b68ba2fe8435e1993489b355662faa67c1e49b57ce5e371ea312631311d9e51be339850d5a2355011cd9fbe5935ce6742a930cbb507a6fcf9b5b2e2e1bf7f0684dc0a2b88bcc52f72a1418e307dbf5722d7494516bac66dd88d304ac1fe39e7f621790636961b2cb35c0882c0c514b96ab1373a9f2dfded895510f243fd2f9ad5199beddccc6d1adb9c49ccf4bc72685c65eaf883133302d7692391aa255b0832f9fa288f639909424c59c3c1979c61cf8bc5dfb03efaf821b29377bbdb0b70fc63a6bd988df5001915f2f145a1c630de88444e5c12fe073f7aee34427ed5e3be9be703f5d890cb9beeac504e9efa9212d97d9018e7358c17ea7bc3031288667681547ce996775eb5503d32edf62cd4f56e3d658ea170f7e97bf08584345f064439122fb8b201fd7e6db7be9e43a7b3b8efbb3cb5502cc7de6860adec4e1c15bd02050bd9307b848006bee3f9e27edf711b4036439635b19cf10325646e3472d67836430515be5e967201725e00b902a7c93921a65a806e64137e8a3b100b928156d341a18de3451534a6b7637129fbbb077066fb455b74a569f23b222f4950961ff1ad7b9825276f4af9022ce32070224b23eafb60d0d494ef628a9db74e2ee41f9fe464dfaf2916331990917381fd168dc2dcc04d839ef323e8a700eb621862532d3b81d535babd6c4bae23dae0d6f412e5e9765324b0416d6b73f5909111e0d3edf86422b7a51e5c709b02adef36ca50b9d5dee3d21354bc11febb448d9d02038acfb22add53507685542810621a5ce877b76956546d16ddd814604c2dc6bcaccff186a6a7acf50421b1336c4ed8db254b1d8b051b2cae50833823f5893ef3bd82f74596a68e484eb4fc903a949f59047404334771961a7a8549e361059e03449e759144509fec5cb9014ce2d2b92e90676eac5a3e8cf7663554e829b5b21878c6125213d19b733cca501771a3b2ddd0673870a882e3f835588bedfd8befcc0782f8bc88ab502c3428c6214210cc2976c62d28f95233f9c056f628f39b8238fb7b92afe9fbb68485f701e16559d9a31849f9b9eb8f399c0a34f1b010e1b105b02151f780d2b846732d65a7230333885a1e95308363743a2a5bf234f9a47a2a67a218f0da723c1b981c1569f737fdb5a0bd3442fe53f2fecebf9f145688e57a74417da40af87c2d452e37ecb3e883774fb30870ffdd0d6bd47014b4a0195fda91a44e21b2cca6765f78d2e528553831baa0e03bdaab7b3aca91cbeb562ef7de30e195d9d8b701786cbb0c2f3aceaaa78ac89a46b5c3ab91a91a0a0698a521d60ed09130426af810d389b3a77918d60dfed650bc98a37d48cef73288b75118e887e700818ca8d3bd920057dd1bb3937c433801ffb02e28459a44ba8787d39648c7aebf13260b118e7e2516192404757403b41d2732969ec51500032aa35dcc2d1634d104a471d18961af42a27ce32cf1e22bf04b015caca42ab066d955f7785d864128b95420b47a6833869ecfe8bee64481de508a4198dc0be6bdefe61af52c3c3c1cd8df0d52a9a44d533ba9d4551c00ccfb56300c206fb3912b60b573d6f4eee919494dcee36c7b6911e4f9993ca0e3298facf8af2ff5ff24c4c25e71f3faa2dfe4c850e40b831b221a2fa8e8799d04e74bd687225a437bfa409cd29614b59331b11a3f53cd23a5ea7d53bb9230c6c5932ce4f93d50be5fe414b54d07c38f2d0ec693d076137b8631695ccd9540b698993fe018d55e7f44ebb74f6f93626ac3d09d4c87d5e578ddddc23dfd537858a71426ddfc7dfa8b797b9bb39d0886bb12fd8f4dc8d499dac1cde7fbcc502d76bd7bfaff82ee4b2669150f12763fe3d53e38d0abc3826a92ca19bee9c2b1409d6e53f12b39d8c830540a704112374f94d9913ea4cbf0ffdec9bff9c5e98ee7ce03805ccf3f4fd315863c187fff496c219f9f665d7bb3886577a8b1901435028acedf72d6f5f21ff2333861af36a591bc34f444725c8e739d919b59b2a4f2256ed0626e3b058f23eba20a31f7adc557323e8a63787c457bb3c67b8e94c5ea3cfadf36fe6cb16eeba6e340b1d49def2c1278db820c2089dc1c98303be9d3ef5313ba274987dc4fcfef0666573d0d28336573a206a01c8eabec7524716acad60feed746e2bbe18b7f32e8cde249c76c115e24ed186c957cb452d4c68ae462b8b9b6a9e9441d2d5af033ef2f61a3d6471c06c1dd06723af7284b80985cf0b2ef2cf9742844fb84a5ff787487c91afe7f767bbe7c18a5182ef4f99784eea6e4b3291ebc4e104a8ca71a09e6e297d04d0208dc953008d743cece7121b60d63f15815ac7fab2dda6170c5784be28e93da6770b8e28c483794d6537cb586680cd91e646447972381b60d12b8d924133d21eeb9ffbfb5f7ad489113c3e0759aeb21c34bf370f3f034b5cff953cbbe533d3d5b780a35cc4dd1d53855f4cb68ef174113a4ae85bf69e1246ebae79fa9fc8fd33443b5bd09b2b6623fb4f27c52e7841c2aff86defb3f41aef7ccc48ed334999071047e080372f3844a680497448c5833aa69ea6e55bb8dd8d3ad7b55cc282fcda1e847483235494ebb099ee5d92dfac2dcab2865a6d49b61215b416c5c814ce4d481044b4eea47e5a0a56edac696c46b31fc08ac58e12556f43b779af83bf690f06e4f026a0533412417e241aa3dcc86f202131b679b188a51cecd18b78ed16604114ea8ca056331f7f96556d40cac94928bf47a20800d85218072d83d8fef8acf3edc8d68acf8d9b59aadae4a8ce2369c00c9a86f342a87b8d26195e73a30684d68e9c25da4bade565808ae06c73bdb551ef0c4cf932fe1833dd6a3fc5fb67619727a81db88f24633921a5a5895ae786e495f6c0769262fb98f9b6bda626688230fdfe2a2a85fcdf91c1d4710f06beca78462c3417f1999cd7b536cc724ca482fa42bdc0d6a5a55c8755f5b367914e61a73dbe966c94f9debcd31b39673f6106fb0b5d012d2ed0eda893d2d628ceda2c14ccd2e38e9d898e0037db928697a3169e8dd057188aebaa89a5b498b523ffdbaafb995eecde49d5866d708b1f785ce9e048ba2188c1ffdb67bf44337332d9c4373e55200eba2b5a59948127f250047e2f6d1c3dcbd2d15631a79057b650452c7c9744afb85ff539c011ec4bde030be29922dabe596880bce8fdae453e2ca7a256d95d602cc18bd32def8c8689a48294605a2c1767c20f8332fca9f15a7f635cd001e505176b81aa28bdc16e65910854177b0cef3aa8b5b77a8d3b0968b1b16ace08c3963106d036c01c241b0f70d07c2742f2ed88f0c4a01f01650221aa93c63084a9d88a2f36aed1b58e3169253a9b84ad7ca96f43189accb811b8aafb15dfd14def96f14bc98831cf7dc03e441dab8d7c4f16fa16807ddd06a31af28af6fc5b18355781375c6ccd1bf55b5335778d534229520be1a297f10c27f6ff1db50f4a1a45fd3be69ed0a6cab7ced89f42529bda1037858cdfd6f532e5fc08d8c26385e0da8452a7d815bb401a1a7f506e119b551491f5d9ef337def14e8a5dc4f034dde31d12acf7c637eb63f845467f428de28c99225aeab6159cb9f886471e656ab6ede01ad6857916093c988bbc7c7c91a15d9219fa2feb3c77ad3122495e2eaed34a73f092d7f4ba5da427930fa5cb10dee54494abdd3fafbea4d9201cf8e91cf87ed398e104d26f8c57e7af1b90f9cbee1d3b3946d7497286a0c29ffd5b32bcbf14d4918765c629823c54020e8cb28c2126ac28d99421d8eb0fff75eb8e995b7f78127111988314150102fb89f9d29a5ebe0c52ab629b71eddacc24e5044f869c01938a3729f762258f69a800e75ec68911407ee2469b4dd385c9942f35d1209ccbf083e1c5fe61cf3ada7a8f072e82b9b4b2a91624dbbd005d6454d7fb887b57c3bbe2bd400b9f63e3a70ddef8b86ab5354384739296ca3d04eff42de759006c2f476909ba9a31ae9a10adf1c9e86828b98bba0e60d2e98a287c2053c25f399e2291886219391f895001e5d0b8faba481ab8fd55cd6ef9477efaa30d2c9c82a6eacd23bbd9d348d210db680babb5965f46eaa92fd91b123a441e81510846938440b3f043fd5c59b47e3f0973b7d16fcfcd1f52fe4da420a75aeb96bdeba770a409f25a20a8df6bd3b75387e9530f3d16ad1f68cbdb3bbd609801662af8fed0a9534eb6931c20f6650c4d027d1df2be166d76c97dc287afdd7277b8bf4f4db7a9cf63ad73275e40516464f97707405c003ad6b3e8f9a822a333afbbf7bb079dd22a0d62bf35f33418aed1d44774241fe0b00dd7f7b95cd96655a5bcbfcfaa4ad0847391c49f424c03ee70da2d825442219a0c6f4f5ec1b19ced768f6bad1816787f7e7d9df33903f18ee0dd379afe61e7829a03ee227d136b9b34d73d791c8c2b18745ccceace1ef2fb33925df74cf0c35a01a805e1da0849f39ce0be9875a2716b74c24e8c4f9083ce2e40d6a8c79fc744c5854ef1a61cb92d10ecf6fb46c983d83d48a3836a0d006c5c1d94bb580230d6fb0059c9e0c597857729895a15ebe63d134bba8a9b2a30c46a49d68c2a8bda1492000a4c94ad8593029bb2316b495fa5aae169183a6c03929afcf0bf05b5fc1ea133cc2dfb60bb40bbc2e77fcd5f1dc138540ff76cd474ab617f98c4b906bee3723bf578df26cdfbde641d4be3ae7bdca2c8764a98b1cd6e25a3e0891782412ebbcd53619281596aced082811e33aaeff613f4b495fcd9e9c1e74c26694c0060f03494b9183e51f12a4464097af3b3e07e958159a50b7a0af8e2caf67b7021ce968bdfd120b42e781314ef2b804d051e9ba77aa7399ad6c9403127e4c78ce362532a76b850810c997537b2965ff775d742f8be3825a95655f5b06a3b3361e4e5b49030400ae6fa0565bf2db0e60165fc11d5b827ab85f385889774e23d8860ca171f4366d92f33ae2a62e536553743e5569d8771e4d53170402dd3c5b2e6a5cd313fda0cfaee08d34dfeea1cbfff22b5f097d4d377ef5fff55b8694a33af0498fdf49feb04ebf7b803df2b56f0f2fe8e2cf15e53b713a55e0be75944f13be1b0045a2049089c088e572cad060b8a82e6f6e6027c846a47e273d86d46f8ada053da611577c5c1ae340a4cc605d404fe6c0a92a6552458946597c6c1785d50bf0690af422c77c31ff18254ce4220317952ea0aae7465fe4f39709f8559b52df8b55be8064566a3b4a31e6bbefa29c1b5db825f4eecd45cf48490252c4d09dc1e7e09a1dd314860a71b7988b200cc6f798ac64caffc6cc814a78077b5f925e4141bb1a96622a944f50c8882e4c045d453c06eb1115576e6af8c16c6ef430e2d9872b37a7cae8b2ebcc37b62ad313765c5307e34fb98049e1aade16160933703402ee59a92e45713069f3ab9a25725fa4cc39ad90cd0d5d8e370d65ece03ce7f90f28232e06fcbfa804343c7780860421e2031509ac4f3b022a11b5b80cf9075a3c61be445a11a7dfe0c92bbf341295975f3ff6e330b4c89be1b82a88a3430a49465e07b14a9a2202fc5a8859b4bb05320fabbd039397f52cf361073d7ede594ec06cc5e22e5092d66a242e7bf9668d785fcf26a34706753825918e979cbbacc5aeba2ebbb64ff4067510ae1941e74f5be91bf37d51d7f9702440cfef9ef8a63424660fe399f96cd0dfb3590b2049b579901da763d1f46bb1896abd32ce06a031b19d2cf6b8ccebfbcf3800aff4120630796ad6939e47797cc200f694f8ef564df5f3a7f04404e9fab3c5aeb4ebe8b76938039c88d7de7185be33b0bf9c69e15b251d890376e521bfefee9bf7d980f56a56ab741b1c4fde33eedf83037523400a4de62a2645fe369a32e35067928539c91fbda4ef5d2b3840d45c0987920f1b85aeb25a47c27e04ebe833dde3a16c86f3ef95ec5aa11f2f9067748e563fbda078f2b22483e6e322df80634dd5905beffb8420f0b1cbe384a8e984439313cc75d6b9f4df37219d987d16562cfd549cd69608175088c9fc59ffd62667b28279688c16ecb0f5297a65deede595ae1fd68e1da6f8d27c765091e56741cc407a49ca802fd2237b7232500c806f50f941d61721a015858be1bd0d7afe7457f03f91166f64ef7a79af6269be9ea366c014e64a70e6ffe3e5ed5b8bc339f6f65cc410043ad35b88203f68e2e09e7527ed1898e45a71e9b1fd22d6ce0a0916924e8c51c4070fd2e503966b02c03cd13e08e19aac17758e145aea83257bf68bcdb994ce8948c0bb8a9eafa1c6e8c757e8f085bdebbc28d1ad083f546df4a6d54762ee69b77bfae6c2577f2b93aced1d5a3a867de497c984968c812f8fa8635068fe072ce0b1c41814cb2cb780a8d3c19d322fa2e11295920f1e620a1f9dec75f79966ad1b18dc58533e1d991e20d533caca2a7d40bc16c11e9a43b1fb13f49e05a98ee276d996be432b3b51cae3614af8632c818bb81889992e4e7f74dca46583d8874167e674d6e1fb6b3d3d430d18f66b95920447e7ff336d5d3ed80b8ffa8f7f502c097304914b39bdd00b759564a720e3162217c1c3771127d582c0b97439fc6ea2b9d6265e8677e12e42930e7fb7b92cd686ef3e2f94a1a4c84322764c494b396e8b608ba6b0377a6e0d6504dc4e0bc90d959dba6f9012d7e8e83a94c6257c199e67e5b6f9de0bc97fea8b671e93ae83aaa3088afb035ebc4f7a7def83fcb69de3f9add8b62e115204ccc2bb303af36be86fd1c050fc10ec17f1cbfdea9d0ee7502cf668e55403fe9c57de2b7920769f80fa48dfadb7c3e3784b8737b39801b743aa26c408d533048a608c90e8519399105062914e35446626ca188ef62ef650aa0f10cd7efc94854e2a205e55877b1f862c3cf6753e164a73417f7086a2e81e6c5fb1dcd5b080f22b4a58f7eb7ef10bcebe4a084afc210a7ed693bab77aa2be3aca5af77b44b84bfff1c81dcfcb2139b66d3890e60c03dd23e2fabb46c3f612b0bf91f2861f87504660ef746e862ffa2ed91fec685c56c89ea9c521f1c42aec5faff2ef7a871e50d913996b75118876565c71b6a48d9b835ffa5e5db0a9c57cbec0f48babb661bf4d599d31b46a39e443be1e9cc0dfb15bdd16122f4276aa01cf59be55ce65c97454334104988ec47334a880730ce4dcfc849cf6c15e33e97033c158724dcbb0fbbf24b60e721aea3926ab00d0320f69218ba32fb5243c2ac70e760f4740d7e09b34196c9291dcb6dfae22175dd4bf1e8b79df72db3b9d7fb263917bc9c4103dd80303f57c971168ae23a5ee2d48c6efadfc329c045520b63cc9d226928de03d61d7448293978c1d05f6491a5598a1a28edbcd11e473e44202b0c33f309418c7165ee57adebe0b295de8e362f313f58ffc996fcaa06b0582800349e552e774a4952ac3f13c9dce08de915f2ea08457b5dcb31cc64d2969555de0ef80989badd60ef7176513166cfdb646649714dacbcd6f2c462c54c36521f330fb92873aca87710a71c8e64e04a2798505eedaffb2866dd999c85c52aa0c88ec568bea512523841760e8195e09621186ddf83c6ff3f4e88db8dc74730d1033d8fe090c3b35d54389bc83f7a899121dd27409d621864a6ad9cbfd8e38ea9d4cd4713ac473896235e9aae5e4db787c566c9b62c2c2fd11ae6d655a7991ab5436afe2da336c45a8ea4117a8b19d8edee3612d2f5d9728aa199b630aa5f9fd73b6075db3fc70ad8e47780cb56a46c405ae41f73723fcb4a511ad095433ff02f4f3b1d257a24313a79272055e95f983245a0bf3080cf8789e893c83eae0596ad724cc7a5e1a0f9ee31135ff60588fed9c9d667915d5482d53bbab1df2c8c136853e01120f7d0667f9e289ac99d5f5d8d4dd0e9c94ad9fdf5cf9bccff7d49d0b36327c464fa3ac89ae29f63d6247100cdc6d8f3584a1165419fea726520008a1041cb7214f03223031d99a82039221ef8fdb0f03bfc2f9bf297b6c3f9b621fb763b4541628df17a1beba218a1986f5a59c2531ef8c3a837b67df1300f4331e39b26eb75662632999eb9e45cdbf3159f794405a97883404e1a7c095486652e545c7542d7f948a3e6ce75f17ec6a76be330b27dc6ef85acc378f2fb5f064b7717d7268c40c9a285276deff54339db1303ac5ae40756cae2a4fb05a999388209b2b0c0fef2c06290f6b14554a2c1a805e9a81aeb8ca8095045d7494bb1864b86205485a387a8e2f273a99f8a9e0b30938f3743329dd20c12e8d4751ec8192b567c7a47a62cc87b38176a22bf60c126c6b2872de4c5a6f2b3bce61baf7f8197e0598b0c331a69f32874b94d85f25582c7f909959b1ace6cabc35ad459e241d86e389e7b71cf4e97358c81fa3a40d79266006e7c4807749b4a6088e2d7a3203df39a679b6ea1d46ed530d5e482bac38c2e36f4d4055014106cbbe0a766456b973defb51968ccac69924b383c07f8490c0b0299dacf54efad1fe20f9a7e417967640fa6d61a33ae3b9c7c92baa79d573ef045122214638b6dd5edbb751f551f5141be6b8978bf8f80cc05606ea9e2fe5321474106a3b5cb8d9385cbe2484b100e6eb08c1160c2d962b68218b861d08f009114d7e5f120fd1d5c9197ffe97a41f822a4599d79fa9cf5deaf250aca38c44efa5ea6b91598c2d92e3fb171a0fad61025dd3c9a9332b35759dca7c56162e5b31840a255820d793ac0181fe5568d6a45498ef410548b9cb92a202bd310158c7c73f09711b24d4296c5cbc8ee078b775c2ea92b9b3d58c147a27b9b693ff8c970114b4aa86dd182e9dcadd60806af31337afcdbf389519da5186138b719f9134e073412d39d6fca82831a93aea31f540d767b5c5dcab80ca402b659d8e300c4414ea80fe265bc83868dce3964945e70b6b600b142cf4d2c46c156dfed1da6644b139e5b59418e6629a0565affe3b207b4b9002756744095ff421d7dfadc0b301ee706a71e6e0890b1f4886c87609d8794c6501fb5fc1b3e3acacf7f4ebf50836d2e154e56e81f0de851fbc240b0d8e013518c20dc485b1cb339a844c249fa363e1c6a04301f7e474cc2468ef5f9a9dbc67ebfaa4473fcab5832f341443ab6e1e42cd6526408eb3ba7887c3edc0818cce9b86728fee316662088acc1f35af8fdf3a0480c8222f5c738a897480ab7fdaec306e6c3a44cc8027923beec21134b5bcdd9c38828afd94f933fe1faa951e5c86a2a6f6e47e908cbda213f07fd12bb22136a8a2bdaadc96e3aead047a30250f646ee291ece405e20b850303900267c9543cfea91a6f18fc3d461aea9661535d9bc43801ef5feef85894f017c4d567521508704c18c4a9e2eca4f7162298ff2dbf70263158db93a31171e852996fafb3a52b2b211db6ec177bf196489704a0b3380aa5be3a12f5d65702f577f60b300571430a3b3f93fb3e62b457bab04d7d15aa8e5ef900362bbba4a3168de33f0972e0d0ce3650b9625c1e5adfd4ea790c06d2ed46b43e289f20fe1e6944b5af9df8c09c43d95677388459453e0bbc4bf5dbcb3f7d52ca61eb5a83254ec28119e19bd0ae8cdb0cc8f0369a9f69da99b08d8549a77c4233e2dda0ba73b8b628d1dfb7b65dafe727571c63a0bd9dcf940270add89051e76c63feae840ca8adba770a7713100d70584462e42cd65e5e283e4ddb65a81028d3e56970e2e0f4adc621774a13dc2138153fdb66dd66d853c223bc2bbe07d3908faf04f1f20a6fbd3c7b21eabc2f8a3771c481f16a8a530f6ede23542ec9391c71a106b8b2371eef86cd2f4070bc62994dc5ab96e833e624639c0b82612617ccd91a0bd13c0dddef0c432ed9040007a0c0a717fabbd4c8ba483efa98841b1d89ff13d3f9edf08e64dc3bc8617a025e8ff2da514b49200cf4258cb0670421ef5bce0afdda797bce532ef04bdec98dd9c0d6975a28d00b37a6e864e2afc8a954a8c8ecee51d41e0d08b6fecb6dfe76d22394ed129c61cef996e2aad661e1369c0d84dab2a279289d680305c9fab29e447a33fe57b7104d40a772d2acb69b54295e36bcb28a463babee3c837043d7d18f22c275d4fffb2b0b2e92ea2704bdd3a69f237b8fd4439a9b5709125735f863ce885fa05662a8296e2bb7d0986a5dd3bb8ec67f6c4f09a4745eb49e0e82eedda41e96bb071cdcbca420844d2ca840f1056581bfc55e6c05457194f1041faa35c64ae90e8394cfba6e10c71377c03d9183c1d6d5dbb5418d84c208387ea72575341faf139cf6e955d6d37c799bda88ee871b90a3fb21880cd6fa5686f9790238fe6ffc166eb487bbe90fd7011ba56223883dae9b6d1a8eea8dc533f182470d2c5c156e99c0804e4a3c935872822731a18a34e1fc7072fa11a1f09d88283bae2603e7991075820a5d4f7c22160732b7626aeee5f30afc730076aa0b2f38e53f6c328e98c1a947d9f293c756e1cc58b04742c740a90e12fd86f147ea1ef1c3b8eb74f054ade4aee461ef1d0e2fbaee60ed592987fd6120cd418bd24e165107e919b9067322d33d6a0d9afa0d6835c2c4831c0bd88c0e241b10ce266108b91db5a6a6f9ec1ebbdeb2c9dfb6c73aae3071071f2821067018ee00692782ae8de1f51073b7a6190828edf0446644ec42d72dcce4ae041aafccb75b61b53beedc2e5c2b9ab4dfa5cb22ff9bf4fb435c65af5e2909149e8f61f1599535e68dde7f05bfbc654347ad8993f62f404f77dd9a307f8acfd422f6e59fb8ec0b13fe93f1ed3dc2a945d39d30e6be0eacbc5f33ccb7561dede3706f799b197da4bdd1f61fca77b7855c114f34c3274ce994effd6167248754781de02e25e1efe1dc5810837734a1d508c9e352afa07a0fbfbdf73373822031ba4f12934f8e826bce83c1c9aaafc1e4fb9d3f1d11df8f77a7c34251a0f51bd916c7582994f601a38f6f5c2f22cd0c04029d91c50052ee19e9255c49614e7809e4176728739cf9498681ebff95f538b3fcb643a689e84dcb54261af33148657a18280214f7adc57ad9c7e02d9b61b5c2f2aa9430e670275b5490acdbc20aaabe088a480276a84150bc731a8a63727d0173a58a85c11edd88fa7b75832f85be829b26616c8c2f4b0327e5409ba4a8d689bb23b06280f66585ae7eccf26f1a2644f90b26fb4706e9909cb57295884fc233d187b795a4f3338e1b289bd23196006a7e4dd06eb4d49d2be8053aa9a458f1540d50f002d43e62dc3251e709afbc36b18943d71f6d966a3a6d3013a96f38b30d994f601484a5944e42451c7744b1fd167eb1241ee83a55ae38751bb7a19a84a841d263d4ce76b098c34de6469a75dc0ef2c37214689b6825eac4b598820f6546ea4174381187e3cb5afda660196a11bf002626d7e97bbdfc2e4b8231aebd4aa5d9142f82bcc94eadd2fc74d53a2e78e02583c656e59b46387f24b1d8807e8f1cee5b22d4d4dc404121646d474d5e01711ba051ccc2de28e70188bc760b718c4bbc90b74a100da3c8e0dcb4e7fbd2234564a360ccdf73478eb5f5adeb2c51c0554039c8eb31011b8a3b1afe3583857ee0b534be2fb2063f9a3712bc063228a821919b4ce6bc55b1966b0f7fde83b77f7d7899d820642306b00b1831c4fe0186f462768437f97d4fa3c70e4b1fb834ac6188947e96e519a171f14af7a876281ecc33b12df61ebff86ead196c37043115986d328206723df419f77057972785c2fef110c5d0c5d8d642e6c34cd0601a3a3ec262809839063648eef80badd190db47048ba905d6b2bd2572983afc043e2f86401e537751d8081b2a97056d7087af81ed6e1b0b080fe401566ba7ffc393cb1c8d06177f9a4d5b495c5226b5866042c8eb13dc6fa00156c9dd52ec1ae2432e00b9dbe605612c2bd962067e2d803256726ed1ded0607123d5a7e66701601f7a9e1e6cac1bbf57e3a5905e1c7b90bddead7bbbcb3e15b219be5dc0cf644bed62247d8ea208e90f27a012a2f87449eb98c36de47b2ec6e2971598cceab04220a601496db59cc222efbe4016ef43e4e5a8a581cc469194d357045a9adc4cc0384b738eaacb18100e420e230ec02a8e23d0ef53a9979f64f0ff273b62c4abf1d9f4fc6493ada9104b7d79063b3569126ac7d2a0ac3048dc9b08bed1012e0f7b68719c11bff233d7acedb885a304790433d032e675125615396de5d8b20a191a673f3673c06f786666a1e52d8a880c168e4be1b97243272b6f430879f9dd875aae4e7edf57706b79a53bc346300f61c5ae37f78564bd63be795f1a8fb4fab91a71b2d66f1d07680fcfc98d40585e8e323463b0cb182f07939c7272c836b229cadf1a7d212df9496abe409013efe2bf3a16fe7d7ccd36af9651f0cc000dd079eb984ec8009a6804e11fb17be55bff3ba53bff3dc91afe6345fff03f52569a791c01023b4314995bb3c4cdf678e5b497775d1fca5b81e54ee5123669a11ed0be43b0d07f2a9d805e68f83884484ab70617bc999d29781c3bb289e9b9d28e903480cab0452b009dbd79643271b3cafd3a2b109803ed23d2042dfe42a49c58bc1304af06e0c9187e998939c3d0e92ea15264477f5c6ccc66d990f8d74face478ddc1e22b63244e8a1bae160a1ce01d010e7ef5fb9319e60b6d5ca82555db966a0d0edd9864ad19caf3f96f65a00e74942c291edc947575c1eaeb02517d872827c7c8027d3663dc0905983462096b7f82dacc7519e98869f0baf71800919de328ef4de4b02c7f5186c508d1afca3c1f5b65d3bdb034298d9a239912c9df052659dad509e07624259a917b21bd1312ebebbd2595e01841e68c0330d8db914ef2233dd0f49fa037d70df0380dd6146b7b326428a70594a10f988c9685c2c6fd7aed1ca22aecea35a13ae1024c20d5a69e077b8a7df3c5c2dab4c548fd935c580af108dadb7f88747f5f903cabf8267718ed75b74e76569bac7932174457468ca4d9043a74599abba75d0481e0f888dde6beb0f6bd41a15a2384f9a724cab61e342414e9314264be643a00abb606471c0091365794dbbecd2272fa66ce0bf38246a760caa7c93d3b4f731dac4bcd35e8f4674336abaade333d162bc29e2f3f2519807f8ec2ad88c4844f484ca3ba016dc1e383c18381b7c3c3a5df29e6b71c372daf7cadd59301badf1f7cc48c8a8325eeb51d3546de6d396b51c850601d4e1a9d8616f6e526a2f0bf36da5a9b23b68689d5433f7eca94c336b71b22329df54bbc99af5643f1df1b7374d2f4ceab37cbbceda4602a83d6bb29ef1abe9125384ad9899d7de4c7d5bb840c471a9c1824174cdd5c0cb000dc53aa4721159424eba4c9a5ac9511fb3a719e37711f6902871f62ec51c2dc41214c1d0c04f0b6d0de117480b7fec66f55f4a9bf9f8dd24b3a90f17b1e3c7bf71ff461a8c16dcb96c05462e88b8d9f76daa290da09c54962b29b6151fb443658310569c5aa64728fecbf95e244c9f86a77b45d5ac67ea3fefea98a3d29562d5ae172da373d298746ab8bfab0af62c43b93f88a806ac715dfb72d0364c6c63a37d97ab6c42c6d20834c4b318c67bb56ebb2f55f8cb335fb9724f48332f97db7f2598500a02f0aa52662d65896460fe0ea7d766698734852fbef581e88122258f696890728729da935414710a9d0a7855ed9d068cfac25c912a405043f1c8a517de02207f22974eaab2ff006c5e217ed1353fcab65a848819804622783737c91ee1f37a2f9dd467ef5f6387fd513d397d3c0ecc5317da42b76077ca573229568eccfb150dc2cba060ac88bb8b6ff65c4ab7a43978c5ba01160c585840d34c17b2bb300068a1c8408780a301efa7f8bd192160fe8c908b3162235ca26a071d910489be48c5bc7cc7272f5a9f7761f4cef03f06e091d66b60e2d6828327d0c2384c603e8f247b07361ea10f9b6ec29ffa8bffbc4627c941b898d32f9ea11de2d99ff9242d5c885e76cdf6c09e452cbacd1ff62f648dbcfcf4fce29b6705a466fbe11b1b71b21272452b2b9488a4ba762b76764d1e61500e89e54218b7d43543a554b788600200f802c2d9400a23e3850f4e7b7fcd898be197af4df34bff35325ab74bbbc2009e61a1e3d0e05c6634ba744655ba0bfa09c91669fafe00e5df43c3c7fe8837a73848a827a1bf16abac68bb46301d1b17be8c152948d42e425221a84b63d1910e3e58f24a69b39350ad84c4e3e8e979c0ecc4f08afe1d350d2a8e8c4b11ec0a13191afe287d6cc591ae44071759b8bac06212d9fd6f303ff7c40aae7a2a78a2692f477169a652a0311c0678dcfb0186743b0c69e3b637f44c14b5e8978982a2583c39bdbb852364e3d3a23652920a65ca368600781b3dc30813ab3b0ed49aa5f11face91c08d58bf32d69a502294da5a071fc95ab8199873a2aeba39e43823f134e388209ae4e31ba371e79e93fa4dd768e15c59b4d390e798ed643e405edbe020edfb03c5e541949a0176f14b2d6c519beb22411f4cad5669723196368edca3cc7009224a0ed420242fef631dd5fd54fd9117832a37a95ac2791835ea5e7c210fe7580fe200f5c6b81620e70aecb7cac5d51badd6a810a77d45726788f902631991b97448e55639514bbce489135359460d4efd7491ef3d7c31cfef9361a0c17592c8c0bb64eeb7b0773bb1f49ee7acde52973cd57893b12318220a943330f2cb28003e58248802a5589756e18850e053cd67121e48b3bb334a11369d84960b3f7fab6b584895ef3763630feb2e481d946c631ffae89c3c54f25d73de8087bea896530a1d44f974a2411bf0b3a4809e49257b760081bc82e49bfd3aa1bb67160f4832bec30cb0cdb9ab115b90063f999783b8a7bc9ba5be973923c24557207df26709eac9f1b6a3ed452a96c5fe5c5561f1d8c786ea60a0f5b706fd1af7f03d5d5bd0aaf3860c1a7de7fce35106e94c5b38b3ac4c1967d3135a260de07839bb320210bffe0ada0c0a066ac0bf391181dd405fa813430d2cbfbee3830194855ccd3ad5a1f9c36615185a341801df021be7d0986c75e508443c46516e1257f4f368d26de760690898885d00ed0f71777b309cb2ee3ba79f2a30b085a63ee6b328e7645eabad7dbd3959c775c91d0fbcd5268fd80cae0a6710a8226dc90cc847c66f9e81880df2c389f179ba21baf5081e1017a6f72cb43610d25f36ad8d32dfe297a533bdc966e74d880049211a038ccc6474a99f1fe333f28df2ce4f259ffa6e3aa84f3c34295d2d51a41180cbd682db640b99bc49a8fb65204c3afa02de9744e796d484e6e95ce768a0134c6e336a08958024540c3ac767c2a9ba35f17482641990ebf228d6ef4a40017c275577dec7da01d696ea89fc2711b7bb7be1f71f1dd542297b238dc6e6217d30bb5314a6097ca52524a9a900715d760ddd8410b4ff2495c6fa4963c91c7564c01e78169d221cf952c64b1653ae5a46d4ee97a73e977a76801fab1fb17dba6db4146d806add212b87f2b8867f5a881c65242ad450611b39d48ee64c732cfcfed3cf3897409087f3f348c76bb61768f3c13a1f96cb5053e69dad9297fdd959c297df8002a0e021ff5799f24ac2f7a7c5a7ae0de0324878ad774ca1858a886e974ca6188435532e8978d044c669a2b965781e62f6b5994df51875a3a8a386d434c6894334186364ea2a4bf2c95601a659188dc292efd3055be5f2060e1065fee1320ad39f9dcf7f803f4a2875ffdce85a62826c5e3c4459ae355b28a328777e31d78f1d0b654ce033a5649e412a6a515deb4a19f43e7ff5cf29abe0f3350942a8ca9cc58cea208db18e48a07e900f68d747d688c065ca3364c6dd6ffc6a704e26c987bc2dca16956e59878722ac3a3f53e1dedb1cdee4ffe7369533109838cb32e9a701b99d1dae412782790baed9831a7412ecb6472c3ccc002ec75082a0e22a612abd7dbbe1c133d53d6bacaa0a7b81333591f3faf5cd20ec96feb3a0cd591291b9f0cd6d5e5b81780f2866bd78272a8fe7d3a8f166d40c097365af760d183ae8e4a7621ca6006803fae06bdc81a52d0e70abba313b1a5d40d692471b45b21125732498cd36efe7e8f8b1391538cf3258d6eaa9120baffca76d8b2c460969763b57674e110fbaf4856f7791d7c78e3131ecff67aa64e60ad94404084e4ad95c4d71de8b7fabf1b389381a250d7b99f29af5bf2727aebd31e2be1f0b1c2462dd0db3a20b1998f1d409ce1a15334cfced132be0a5a73a8e08f35d43752efb21072d6720af7b8e2cb8c64620852e1a31b2fafa2c2b3964c31344bc94e7bb23239b42d4cea514bd028d4248eb5f8c2e5621f263b08e75b62680225c4a5f032f857fa1807c5db90fd0a71ff2311c78683df6ff6fefd34040f317af0a16ace124bbb61be9d49648af37aacb9da3c2af852a90bcb4d72c383b1d8eb02b901369f77dd97b19b6c7cafdd0488ed19cf537b79a0b078f9ca5b9baa8c6d29ffaacbe65e1d439893d02e640482b0901c1afc48393b9b1e581d444330ca2893028db489c0edb8fe564417283859122b7d20b8dfc3faa7f1fee80d3cba49c7d2b09592ea5d112db36ae1d4e6d8abdffcf7e304047b89d0e951a5e595d6065ac427452b02c5a3518dc52ed9268fd5580f67596bd2c3512075badc168014aec086d6d38f8e6f300330cfb34bfb9f54fa85d631c1f6b3dede8230f70655434dd880f990a9d2a4c5851c26bebca8847f88b013ccd68c6e40bf8c62a67c599a106bf075b7e4f525238b447437194b8bf06d2814618c305a71ce5c29982da709c3a48e489f06484688cab0fee17cfbfebee8dbdfaeac9ae55f2d6d6fe242cb7df78da1500d8d9f99c1f769f6fe9a99d6adf20d5efe3925357cc9c3cf2b04530d35a25fa65e9586090f43e92bd5441b765d1914aca7b833b2d4da5f62bd88e9ed393a88378d2833f6f224f057040c3e37ba611c6784ec9154204544fa33dad097cc5e748c221b9f7ff9f6edbfde6b3ed22d4bc8c13d5575cfeb624ed0db4deed8878dfd72b55213bb8bf3c3823295faeedecee512a2cee2160c3be7ddf3f9ea04d01f096acb29de9b8ace90e8c8932afe2d0ebe97e341bd2161b23b4b8d2bf2b14c136f54d20c0e168159e3190462c1bc82cda740a2cbcf13641617713748223e0349f554acb3723bed0c70fab319c0232ec768947114265cb9318f1387e158d6e844be22136479e76f569903cf33ca79a2b719bd669ec235d6c2035ce89c82cd32341c3a3629e781d290ad71299244e1a3b4feb5c917aa788735a2e30e282cf1dd5acded54f092217f89decb9bf94b1e84cf19d30d98f39ac82baab7c825f7000db4f7dc8a482fe28413e753e6d88d7afd481218f8e57df81013b749aad946d055e815a78a2ed5e8f6b19e04fcaf6358f1398ee298ec3cb5c649a663e6970515ac626b16a3d329d00f3348781b8668b2ed506ff42dd2d2b7a4290d84d39f91c45a31de1b68068df42136007d200db9c991be0ef70f930c6b2e34b795d4b72a9c41927e4816171627162fd8e97e23e0bb375c1ffc86471b0db081ef4ea3d9b32752267ab6b8c25d0e6bacb2ddda76005345baaf3e93a053ba3dd5dfff423a1072ba3f9703b525f384ee24f5b19caf672d18ccd19cba25526818209e725b446571394ce77d30ad9f93f7871dbdfd7a08b1672277fdbf616de9cb73b458bda1b5133294b7805213d6bdc119aa7b732d3c9b9ecc0d4a68ca1d061eb02f2634f7afa7b05751af3eb056b1992ababd75ed9bbb04bd31b22222398f5a8944779113e20deb9c2253ed78aeac187f7de5f3f429dfcbb2a70de4fc007dc5900a9cc5b3b0032e01dd7288bc2247962f9a3065bc88f44ba3e3164959f6f576a3722ac68a9fdccc1697f25031298c5a08ea469d7730f03d3665a77dc432831146de3de052417df3bf08291c1b5dfbc100102eb7a13f5c00e0ec4e2e4b4d5dd0de7ff5c30ac1c83ab8a20442ae5db75dcdd41e84de2511cef75a8716f29de0f91f088d28ee0c708213cecbdc7254cf1ffaffbf1b54726c235017c5dfdd5968299dd570d3c67088b696d039a3ffbad8853d35cd5245552cd1e8a9851fdbe019ac81b9313de7c7e15cf85bd8d385f75f425d1b014cc213ac677b4c6f74620353cbd2f7ab9e60e3e2b3f9a1e19ff5869898128b639e10115e9dd219f032126fa30d11211a930587f52ec9748528da208df3b58807bf1022fb2f8004f8bd3196dc1f312c9d21132d5ac73fbed09d198d83b989361113f00172ac9a77cb029b94cb5596bdcef87234a62fc0dec4f789df3c41a744d6c723872b025c54c313430713b1fd70a15d679db85944f182629a67fab933864c9d7784260e57808edfeb8e9ef30f1c8437ff8d4e49131613e2f05e9a6aebdd7927d2b133ad5fc38b1741bfdd5c027f90f850569ed1fbe35873d00ccf8ebc4e026dc7beae6102af57ca0433300b45e84cacd7f705ce12f6a5baf8968566b52e95e0b1c6c6654b7a068c86aeda552bed86147aa90ff3aba67e813537b770b471c49b29d2dae67675046e08d212f6b889039180522bfe7a96be8bff0273eb15d4b85b104d17b24c40197355b99e0665efc23a4662d5745cbcf2e90fd439b749b111af54241f45ae374590f0b6184ee104f3569be268277d847fbead83fdc973f4313180894d2dd43431e9a08979c25f185b753ca7fa5d4d2ea8118f83e584ccfd677019f8dab0cd8e629da573c7b631ab12e1f842932d6cbb9a474154800decab19886eb2ae93177d1f870cdb3f7f631e8b1d4a830e136f610ba1422f37d364fe6ce4e288396cc7fe350713f27fa6cdb1cc7c874c3e8ab9198c0da4af8887f62f33933969754b03c5d9a706368a67de1d13c7279750f413c02884349e44d9b05e907ee982e5c5e362b8bbe661cded781cda9b89e90ab1f6cd348d113b5be591ef46eb1d876999e770cf5d0769f449ac687f7d0c9b91d6eaa4a17ec36935bdafb8a57184b4c0d0627811d077135b918e60969b75e6008827b0416388da43d47c6286318fa55e36a12d405532d994f3ea14287399b954a3bb941f5c8f02bf876de5db870588223d18647fc3143d4b381bf98258483c4f9fe39d658a1b5f502eea69dae2fa06916994320d4b562450b233103708bca124065c9feb98c066e3f2426d20b137a0ea0e181a2a5d553ecbac1d448ac9e9e7bfdc655c6f09ab545d35f222d0b26931747d4197ff50f5967f6617949958d9c3a69257e3a7a19c61954569e02451e91dd34a4688291e1bd2bac9217d702d966bf5433d6f95a7f85f810ed044d32585c43e226381a07d4b7a2cff468641fd4dd2cd0bd1b7301709873027f3c243bce2be152033832178897443a476a0cad92337c8b711201cb289724995cf3070000aed9c47352e2b00d4d1190b4d4205b8ce6670cfdc8c4be75bae778e58c9bc0173af3bc458321c5571f70cdcaac409bced61a3372a370df9828530223a7fa27fa823dd239ef04e3b4a1cdef8de9f67c18abe486983f91bced84156e60a44271c2eb3c79524d4611d8e0a2a5f0c38495deb7d61aab87d6191bc6146ef710d5cb9b69e9c937e74eae2ec3c1cd83429c655d3a5e7b64e1f928283f00f13bc773c7807889323a1833b2c888072c6e075c21bc4f9a3b1c6c6ece7845d73518b6b88662b70cf545476616e930bfe2be2be7389432af407fbcdc7b8a9758ae0fdba1f48e9956638b7445ef3a10375cee2b177f982564656962476dcc1c45dd3efff860d08be31c5b627b05f4538ea1458bd07f83e9e9dbec70deca7b8beffa440911ce91c6470981219c250e5d23c94812a869812bf5f24d45980108b79b504949999e99d9b1d79ef286e8766ca4f0a63d866f7dd0646fc0843ffb5d6f5a44eafb8f013df8501821d27db4f9b7257f072f161ff93c6455842ef1624b233f3b247fb7ae2349ff368d2e50eb50060ae08cc48206ed8a31a4035c9530cfcf451407902d499eb144c09005212af88e8dc1a0ced24eec1b5cbe7ef8f2b725e65507182fb01a6f338fb568050adb282873fc6d21b930f1057e6108bd0599d7fca0ee21af4ee2f93a62e42c79ec714851320c0213225b09b157d8392c5b4422418a048378b36cd63fbb1e578c71719885a2ca8fa086914317ea287cd69e6f13a32f52fc59f1df707771ebee460fb24634d2e4a511d81992fc112cf2e1fba79695d27d634640b90c2943d424179cd2481263addcd9ca32aac03390ced9df6b6d97da398bfb76f635552c24de1a51c21b5334b5365f6feec4aafc786c035e2225db6f40415d6cff3e6ed62ba36dd57b7e3c4c0f80f9925ce838157d037a7b47a53befbcebca1973562ed16d634f8e4f9b975eae7fff0b68850c37d8453f0c2994fc4efd82edad2fbe86da68b50d463dea26fb5b4840d8ac40b73cb13dcbd0dad481a607360b3d31d9e29b3fa732416a244753837f1061e042e4c341437b04fcedfd021f7f595034672920b892dd0178503523603774871729f8f8dcbee81e9085eeca8144e97a64d14ccec63e6c0c954303b821e6f8d0385cc00f0b635e468ab4008caa531060f8b11b850633fb1786d6f7bfb13ebcab0ea8b9ea4807d522bf558b84ebd55e179e33e7fca5dcd4b84a97ee8bfc648373a279a9c8d61b3620eff368fd2afda40347e4f6ead0065e0d46d7c6a55eae8f7d649fa965420ddf1d852633ebefafdd5cfceac2e486b2acafa30290a3fca8c51ee06573e7a0ef87a62d544601bee8b4b73467486f3671392b26c2154a2c681c44f00371cad0ccfc72742afb42ac2fa599f8e7bc62043e63af80d886d802c96a98a05563993da32fa643db532f9d6aa06b1cd45effe3fff3c2861306525cb17dad2b40060ac8c5e351abe7d8fc176ad1c7278afa9aeb393afe8485bc55bd1babd767f1b222e2e6470bafc57dfb14e9fede623aab15210e1ef24cceca6fce66735c2520e86f91cd060df5b84be20c9f90ab687461b96d471ca7a84ffc9bbb989c4dbad560367b53c6a6addf69163e24bbb281027c7d53a0c7a9566f970e090e15247edb693d326c85566a285faa0333027afc2694fd7231a7dc0f94dfda1edd790dddd42736ff958f7bd8bf527f541ec393c5a64fc946cdc2f5a7c746b893e8f698323d56b43bbc1ad60cd4b5897e161f4fcb7ff944c6a056710e4cb5b7c435d1abecb9b992affc46a7da01a6ea4b9bf1bd983e689d699b48d3e91060fbd73ff69e2738a431fd4c7dbeb81392756f34e91e672e62a848e5ef6d5bbcf18e753c336c013c4a7293216f3e3905527f3b00a70c46380ebd307b74d1791bc4319b5d30f46df41ab2a2c64afcc8474359f7f71fad85b31e3b7abd7e1f1726f3e8cc05aa9114f5aded3adb8646b71bb7cc4d57d589ae12762472139620098f365d09d49a7ba9859afe78dfc2633c8f3ef45dd0765b015b46fdcd347394be71a566f4a31cda52e8a7ca4816c22ab41156855ec1375b0f41860c876b492d9f948840fe0e31dc262081c6d3b5c1a1f0981bb22c29ba364f7d60c6c38db2beaf1c828dc0875c31aed30f75be8448dedb6384ae836dc1baa9d2a6814280dd41b8b0850a35ca229ed898f794d79f18696f8e1fbcee021d28ee4b439dac9e2b1d3973bc72cf242b75f1b63eb36e5b3125d7d27ac1a6f0fc3cff9a3eda9f9a1f3b57cdcbd3be51e47d20e5db2bb40e12da77bd261bf348fe584c8c2821e1e51e40e325696f7be6b20a4b8cf5000bcea0748507c27ab2f32d4f2ca942935fbbf9f91cb94d00824b909177391e8678958f6e47b3d1fdea6aa4c476d150367e207791137a43151acd69e329fee979247c637be493e66bb702e48a89bfdcc6921376f68bec654101296d0c5075666e06b4dcbb0821f133045b2eb80ca637f7ab7d0a4021c0d7aadc27732bb3a250ef18ee5138467f94343d931a268d3a0bef72846572b1a33d90fe37c3eb9e4af4523005114ad8949423892eb35ee800c13e89f9323caf024fd6452310913b2dd8b202e4ba47521675669149e02298c295f45f5e0e7779333bf5db3a44e96f9caf4e6c7063e73c417fc349f39e12e99c2b29fb3f44afcdfa312bba2ca096c4599de99f5e5ad2a474d4945415db70f506f39c581e09a1ff4dccea008bd4f80807d0539cd697fcaeaeb22d6a0b78182619e2a68df15008a74c61381bcce0bea0b3da332c480404d5b45187538d8015a8d11f5e5be36585a860903153d9f01dc4f028a56d431c0f0ff719b9aef76851c254b1edd8c4c2512ec1f92cac3e7c0a6595dba2a4f3f0b4cee1d24590fabfd9488fb6468443d053b1d15c1ae35f6a948fd26e3e0550f1c6d3a733469f080dbe8e3bd7218137bf6fa1f315800eb308d3f1dbec7a3d73ddced8ac393130daa514de948a799ebbe21a61b39d45d96c744d1a29521d9af10ca5668ffe640767a5b73a91b7a243bd7dddf062b75764bdde084de3f64f9728f658a3914f50b29bfdbe67abfc3c42f297e0ffe7fabc5af21258ea04c5b3668c7362ccad052e150678c584013b8d274278f9d917262496cdb0f03a5abdc6eeaedfb9eab432c8d9c354fbaaee60c0b9ca3eaa05ce2facf73d584bae89c2647deaf309e92f61fc9e07e6dbb0125b45fa871ac74f772a066bf26f8e4a662a810c5f0a0f0029071593eccae35ec2ebf77ed65dcbefbe95f721b3f2a9129adc84d44017f86ee12f6b9817af355d4b3bcc238a4249252e1ff4c780cffc0deb9c5224fbf3f3cd5700362db57f75e1faf9e9c986f235eae737ddfd385060b12af32d42b8f35daf44532407950605caec7c8ff40fe58dc360f37dcd84a8f455896cee7a3a4104891408310be000f8efe23fdb9647082f1cdebb864cba982cd7e33c6f44848ca2a4eacdc2aa519721eca79d701082f8cf0883ce3d644a8417c061067957acdfd4f6539214f2bc2ce3baddc7e1b9f41d97f4e07a3ac00b364beb2a68f6221e0f5c347296da8687f94fa9d4eb0dd0f77cfe0c6ca1fad0664e5b1165962263c9b0bc3d16c09bee04f7a200b2dbd8a7ac2aed9c4f35494e7a8a9eed9b007600fc78261ab018383b535dc7f31ee1ff83c1d5742685044ed81df9f627effb2704022a7a5f2c2ca51bf7d7b6ffbc81ea4bb8b1d0bc7fdddf79ff75fc11245b3032dda40cdfd56df70349b1ae64f4a74aea2dad5c3843eb1a5ec48aad8b393f82b9398f28898d4dd1b507b46e6a0da41b38a6fcfcef45b819495d570f96f20c9ee30322547449e6990054d71bea4fbf11bdd220a18faaab215096d476238afcbbd945835451a7811417a8c2d567cff7c390b90cd5f7e619f561f6f7a0d4f587afe07e78b81f50c44c6c212ce379f1ce7663a4130b8d1fd3a45bbf964b99f57d463cbfa70fc064c964dbac1c9bf5c6d4f7895faab47cfbaaee3ad582296ac5ad454a6311e463298075552c1ae28cde35229c306c21cad1b104ad9f23977dae5a58ea5c6bdd5ede96e97cf8d51c70ce2837503f092b1234767a855ae117461a0d058740af9a9c04e0bba1a03b58a563e8d65bfade3f24cbc8b774b5677fcab7ba15b89545956221f2618760cac89935f604f6d04c87d7c5d0cf508fe270d824cdfc5f229bb46cffb318e8e88143fbf6dd4d59b67ecec0a82801abf1de456e7eca679c2f735ec8ffc4524080fd6315ad75fc884af2d3cbcf92e1e0c77655c9b8fb4e6dedbe00f2ace6eead70c1e88ec41c2b692809510409b10ab2d0affd56abb151c9e7d31fbed3b0fdbd12a5b960073441039ebcbbf6c29b008c1f90ced7eb4e3cb50ae1a4f38a7369b0e5c8d931527f1d5e9040f88233edfc901bba5c5e63fc055bae27662b62f617fe5f11b2e764c99542d0ee0284601db30f79fec23e4f4767cdcb725deaeb93009c61dbbbd952a73fa2666237a51d86d0233d5190b0a408bc2034b070582658d1041f0f99ff33b978850db5aade7a0d94c44e5e387162d219ee34a16070bc757bdbcb598720633efb236a7f251a7e3db05f127b57a8b1b88913c00ac3bfde07e7dba210b79c706e0651e86290577a468eb007638922c5a12bdaba5c66c7d07183b8de0ef59f3ce97425996238438d98893b5bac550ecd9bba63d6a3b7728bcfe3147e652f51a5a7fa593cbff24878b25d1dbf9c2bb2b956b83e4318ee71e330cc0f954d8c3750ab1c70fa3bbaf75c1fea47c1d5624b84172e57ee9a3182c47406d2f123081f4f127928b18ef03d46084179072552ceffbddb9fdf4a0b77e67b6418d0f7e727cd696fd3682788f6e8da46b43538187453dfaa8e6609579a20b9f184f456332faa84c3e290f1835ed460189b31dcf064fdafb0fd5e92734731ae318a43f27464af8b9323ffdf22ec8055d7343e94211f9e71c352236dd7974d8a2403b2a530bc55f3be5e098a2eeb9e378db4a312120fb0aaf6d08362c04be3e17da3b89373ee2161c9ca8994b5acc4615138855010e27b17d2148af37db0ea1d67651670957f64e4ad7d3ba8a8e3f0287d0850f0e92b308be4338bf51081ceb046aeac69a561040cc481db8d531d450e45ad7ad9eb13a96a55f30d2b8bcb2a8fe9731bf82f82314fe5dbf8c246ea9145bca1d292169594b649ad61db0b40591a727e01f71a716b814f8af453ab81d47f9c9cb112b5e7a191d6b9fa45b097809eb094038b1c6110b980f01cada7a86629c50385032c3157668412b63d2b8b8139164d23c983370d6c2ec4577334ac3ffe2546a17b386b56882579c66f7459adcdf1c4a345c1856ab27509e501c5d6855bbf6472c293969d7fe88524a2f0c33505d68656f0b5f86a3d54c832aa8c8c1840e4fa0d45e948522ce91c3132714057a5f44d2a1505225b092c53fc4775a2972ef61cb0ecd99c102830fbe43ac1287bfcdb138e270d414f732ee3498557488a496982f5ca1f3278a4f9b2ed8305d70c31db53453845212677597a394d317ee5efaa462809c9c9c5d585f0acc1d159bd181bba36052b08bc582d56a49170825e42b0d1524e5c6920eee2928967228bd31978eb83b39e69b52143732164b76133e1a1fa57fdbf97789bffc77dfafb729547f4edf6a2b12cdb9f87ed78ab65bc321ba7a0d7d20ce8bae5a2a90b6c09c43599ae2cc54f4bb96e25415fdfe6bd1ef439b3fc9675f2a6d592c8d035f20cda15f7d4b957eca4c417025538139900a08e2c8e88a8a0ab4b792a3fd9af8301c0bc2f95fd0bb9abb4ae2d36ed07d32290ae8dfd6f815e504d7fb35bda591cfd616fe63bd6d3d5b49b4b2b8d32ab375e4423af746e3f1d9dae1ee1328fa61166529029dae284b3fa15664555820e3eeb5ef33ad5e4ba96ebd7e6e86907ea4106d5b29141cb04ea1ba15949394f44e694ae977ad98f14e887ee093ef830d81f4e670786d0e6718fdac7e2be6fa2048f7acc421bdfa6da5377f71ce6e9653abe11b9c7f57269b5baf1faa2d05415010adf32703da016817a4ddfdc6dd63983f647eb4d57c23fd063a8efce890b18ae61edcf7ca7d661465c812f7195a24b2806361a6fa0c3803ce20994112471c271d9c74f0d9ca19b68995824347403f45413f43525c4a54a8e0a729869ca4f0936b0e20e5ee65f85963f226898184618af89ac2836d01e1e5ca103f2041d3df27d6f2620d8254dbb186c3af637987e887db3d8eea3063dacedd71ce43650b397cf4bb0ad59276991462ccc2cde16b84757d29b5db6a09b570347c74da2955419a2ab8a1820754c04bd7227d2002fccbb977d16fbf2862dadad8b57f72f8e815c34c012985957b129bf7b57e652a7183894ed00774fe70edda9cb98456863893315022c63ddf9ec4afb1a8f5d356983d57582cd405664f6af7389e2ddc1d67f738550a07f79eb96382bb8bf0824f9dd7e449eda8e14e5974d70f778f4ba1e60e169d31fa9998f49739dc7081eb200011ee272e2610857a2a0b4f61e00b8f0418c0031411e804d584e58e5241acfbd6dc33599b8ffeafb9cf84a153709f6923b428264b94b8a37a7eacd77da6c81dc583dad119693a77541277d4974f647ca372dc51abd30b50402141e1dcb8a35437a1ade455a259123fec438ca4ff89f6c37e63178e868f76c6625582e512a867debcdcb7ae64f822495a1cec081cee6ebb36fca7e1fca2f3881394f5c4e7111d771f02348d68601ab172ea22635d41961e7f5bca53b5fdd1b6ae72ef82a30669783185a83187c860bf96edd75ab65fcbf66bd97e2d93edd7b2fdfab56cbf4c9651eea930ee5e372e65906921dc5d86b74c88cedd41d7425610a212f2011bfe5f93e12d0380cf205db8bb166ddd1fcafe6b3348933baedd205fdc5d16c4871964f7b819e4c85deb1765417c76ee1e82cf20d47d028183b4372d9b40ac7077205f6ebade26101ddc43dfcd64409cb80351e97d5f86310442e37a9c3fdac0b56b7f60e1de847f8869f387107afed85e770de78f97bbfb787e5cc639d9fc21e447ca3519d7d3c7163e7d34e1bbcadcbdc6a70f2372d04c1fafe9c3c7dda78f0f9ce21d5620ef429b2508a1e4dbf97672c48a223273841a82a238aaab042375e4cf7e58d1c360bb12a84837f4422f0605dd27938272ef22c75c14fe33d997d2e2a1e2ee477c8e1073a7ac1a133947a8f1215bdb23dadde9e961e1ee1fd6e919c0c9727a37d7b5cfd383e2d3ebe134e3da9c47645c0b45f8c02a56c02a746505bce95ab4adfdbe5dcdd516fd9ec9962bd5566b4b3f5ad534db21fae9b2d2cf385575ff733ff4ab75ccb8a6a988cb3feaa15892ab5ce99b9f1b6ddb5cd694ee32ac54db5d65207d0245a8557325c39aeb7dfa55ca64e9ae74b5ba856526a9a6d9fcb666ab5ff41bb75d5901c12a4c6ff30ea23961d5422d51ac766705fc1bad0669b0cab79b519a4ea21f08562b5a5df9536996e95ad66dd14f147f6852ddb05ad26fbc0413c2413347cdfdd3f913ed2e45bb4b164b27f19982c21f8bd5388c63b715b6c8311725ed8b83f44d02fab75ad73ebfc20f75785b2e3148e7c7edc83afee72cc2b7d9eaeca5b4c03f02e9ae53e8f7391431b9d2b408dde159e9505b9d512a8a3b3463aa63e9b5f27d1f5df5d0150f5ded5401a9f651c2f3ede8e8f0f0e432a4e1834a98d8ea4c14411c954cdb0f47b5dfc662695b05022da86d7596a345bbcb24607e1087b34351d0d5f1c39121a156705554f4c9136a6587a2407155569ad26cde3fe2b765bce93862dac5368816a1df4ec4b98d2f5e699cdb7477c35dc35b3f3b448bd0fc4fd25dfeae61962cc78eaf6554afc2b7f452bd6d9de5aa6f424bef8fce366b5b9de11db634d5b2af26be68ebdeb43257209056aa7fe8a76d754635ee5a91bae867abb3fb43b7b6474d69dc400dbfc1163700e394aee8ae9cb16e0572175fdc90e081004cac11866f10e1fe4611a12f8684c6280a14ef8c37add6d54eb4e4f7ed6a6e776b8977b45620fa51f94162b198ec94f533939d92ead725d5761ceb57c3303ff98138aa1a4681f28078479b9de0b36445a29ffe6cf81bbb70fe17b40eef5bd8c6ae8d5d1bbb76557ca1704914ed86851548015e5cb0b3010e3068000ab761e3071b367cb06143179474ba325382cedf1a4eb85316aedd96ec37beb230ac32d9877fb190fec161090b776f14dab084d9c860c86520c4dd7595e1b07c5de2b0d4315000d387c3128622418d2eee78c976edc3e0e88b3484b8adce3ed7b04c76ad6c97e496344eb0b51573b4adce60b0f2f0752993e1300d1fdc1d8d34c2d7e19f0470033265d4914cd3174300992ceedf14fad15dc9bc408692d12153e34eab2f20e3ee37dafeda0bac98d9c18c0ee0c881cd19709c71858e9d33c23380ce706263c6171b33846ccc406263c607333898b9c11ba717d8a006346494e1eece43918422262b4d467ba6219e20a306f719378a66dac051e9fc25e961a2e9f4b63d4c234d271b23021f886fb486a9ad89658de24db5ed56102477fb54820bb8b86aae2f1b3186701b317c44b106edab1f0bfd60b1b0a1a07d377609e1da9d016d5ab0450e0ddc9d66a64cf8abfd5a0fd980d102f7274e4aaaebb6b3276b7807e50b8d21d018814c143e4c4010a42bf0c361b2a3aa5fce8f76d75d71541f4581f2f480babcc52858bfdbecc3d93d4e4577ba71a5773a9cc559bd4ae15572c96432d98e14433cc6543086ebfc859feb4d872e88b95f000c369acc2f8c98628cc0fd6fae309ec8373088705918868dbb0b71c1670bb8ec7294e92a8a55a6ab0c0c19dcbf9f8dbf5b23deb7de5e32d94e7f2ec97d7f0c655fdc402041003e788c0080106a6cc604c0e7181d9f639ef81c53e4734c0c3ec7c8e0730c103ec77cf139a6c9e79814f81cb3039f4d3b7c3609f1d9b4e3130a263ea1e0c1671390cfa696cfa6187c365df1d994f3d99483cf26217c3625e1b369033e9bacf0d90486cfa618f86c8ac3e7140020724388101682d078b042510da8ac30a6e0756bc54092e08e282b50f90906dadeb6ceb43ad226a0ae8e275027144757c71392c8a012edd368122a03b802322a109a5e7c31bdd04d2f62985eace033406c4e6546e023f80205028a87bb0352f072d739096a48aa0719147cf150169a315190729dbf94073eeea91e3f5b8dbf4f49eef964d4459328da2ae5b4c46e46f7ebd56a0afd7e76ed0f507ea6d7b17ddfd26e68613024fd1b7371f176658b189430b1583c4c2c96256bce240102dc6fcebd2b6937a39fdeba8ab39f2f6fd1ae722e0e3399b36b2f5a24fb774868ae35aa35cead2a4d443f7a6f7b77c55ae2ec549adaae75d55c5f22c62fa550e8ee3229b4f5250227864450c331bdaffd8ce8a8402b61f4db61e9ecd01adec938477f97227e9d9d15de9666f75710d4da664c8ef606526c77cd3978e77ca1a537394cf091b86e6bf38774278ad5eaa7d9f2f58f28d6fc4c43b352ff8daec812671bfdb4d5235e812085329be527f1d656d7dcbe25fd44223f56dbb5384d84d630255fb45f921889e2189092ae22edee4f7831104e5b291bbb3676e5a4fc389a35dad8855dbf3776dd1ac4548bb0d0fda39d3896a38d61d77eec9a52f4e0ee40dbbe5c452176817897528e7415c92300822770dc5d026e801b54b041318117499b6338166211469638b350d63c14be766f84a4df6eddbb60b76da7ec5cf7d6bdabe6219e1e251c0bdfa63326aa9968ebdea5776f145e6c330e0a6ae19a7ebd70ac08696351ebbfbfeb91bead8dc5f0df157eb875effa5ad55749efde48877f1f0bce445bdba31c76e99bae452fbd75efaab7d512ebc6b1af555858bfe8957fd77e6a1ed2adcfb5bca4f5d6bdeb8d3226ca5ff3d01f6951fcd9bd91b6a28b59096322033bb8fbcc800beefee92ad2a7a9edda550dd3515791e877b3d91bddedfd994c4ad272d14fa8457e3c3d54972249b350eb66fc6d2f9e95faa3595d8168e0c619a96869b7c434b1da19ddadec8feef5b64466a52632840d6350a3a4ddde76ebb1ba3676856f7b22ecd27da877b722cd4ad8c6ae8d5d4219138971051419e084c82a2ae8244b518cbc42990e25561c01468a390125dcfd96434ab8c0fd4b05befcf005cb97c341a258653b07184934f9c6a2b6f84a6865bfeb9689a2bdd1747594f902582c520bc5f6c366120f9849284d249c8040988904f83a851081363c0263dc3d0cab9e95ff352bdbaf65fbb54c142d2eacb36b4b197e4a0c784db985d7300180c8dd842325a8a0dbf09aed81d7ec16bc66fbe035bb025eb34fe0353b075e5302f19a72c76b4a2a5e531e6004252e80281ec2f600f090e10c77285bdc6336b812b2478a841e59683443c06ac414f9707713aa38a104cfdd5d1566053ab846e5ee0e8405108ab8d3079ca065b66b334ddbb101272b7eb89fbca8c2e711eebb5bb1f8d5ddcc1d85863b8a8ce78e8ae26846061cedb258e1df5cd2873477d40bdc516744b14203ba107d8b6b86d1fdc5f0de6b6703741ae0ee930146306005f79c19375066cc14c5608404dc5d57c7eb53e2a8bef0c3655c7347e5a0881a14b1338b28c3bd88298ac0c02ca2745ae4d2f963b1f402b6dbca996ee120a98a409a43bff081409a93310b04690e888383a42aa22ba09b0aa4ac8abb560c6a01896a4a5dcc47ee8afccb24c2892b614a2292a39559b2ca6429984141026c70af371cc3e32385bf6d38b4450b148ae24ff8afdc87ff8962fd4050ac3844c25bd668497624911912d5fd0199c831fae55aa358539a6dc617db67ca960868eb7ab4757ddd5678876e2b57228c63e11d4a12c53ac2308e85ad4ad6a0fc4c493929b44044fa628b5d4238b60b2b1e1fe9b65eb755cb4baeda10d3bbea6c8bf86124c6aefcf7b60cf0863b5947bcdd6740300d90330008840d063022b3627d2944e14f20b6f804c2e70f6ab8bb53d67731dd1ad3eccde7f4eba32f865676f14aa746bf21a21f18186af2dfc8e6ea48e4cb27e2c30c3ee0e8c9cba8ff14474e081b660f1b50a20722f0877fb201aa0c1419b689d62ff650420f352c1cedbb98a04b08669724ba0cf164459facf4776122b3baa7bb2f3b512c893676a1c67047b9c01d25863b2a0cbdb1a88dbc92a3c0f882052b5081172940c109ba4099c01d5502771417eea82d5051e820c86681308e61c1ae3768404602b79230ff727679538a22813b4a0b774f392a0b771416eea811bccdee2811b8a342e08ebac21d050277d4077c6a81626a0983f2803bca8a14aa0a771fd255a40fe7c77269ad453568e97d5d524b75158968e3dbdaa1a5fbb5de160912a1d60ffdf66b2238fa2dcd8ef862dae70fc7fc9b5eabaad9efc379dbae2acaa21886714cbfb5d919c56f6da8f17172c373cad9d1aaed8f74f84042103ac011941e05e861c01044271bf8cca44e3704508534b841c31734a4618a49830ffe51bcf7c374fe922861cad234b3dc90c506a717d324540a05c289357050c01805b0618c2e409c727071ed85763a7cf1c542cea0e361b86df878fc11874951acb2574d2c3d7276dbee7cf07027c3dcddb95b21a7526d7571e860e022dd37f76db9863828f72e771415fae58e9ac21dd5e48e1a935bc28a4ffb81fb687bc271fe172e76bd2dd75b904df5c904029dbf67ca252d092d6b1d1e9e9e5c9512b6741f5ead7fd4fa4b9c53cb4cd7e6d432139194a59bae454452966ace956e9035e728de41dad697fe27b295483faec65e8798f6bbf05dbbb02aedfbb09f9d43ebe0a5dee17d9d5fc86f7ff82afd76b7daeab695a7a70473f8e272f4ab613afe6de7a8a67dd6397c71f48f740d53a4202a42f4c311c5baa3a2e1b65f665b498a033259c243791e578de8a79588c2fb5bb44457e9a504e90a74145620187685fbb5cee123d8c6aefd481bbbf43f9156baad91a69385f7f514afbbebf029dedc7d846975a0ac1b6da4e992ec92be0b7717f1cd067740ad0dbfa53b4a0a1a77941251b8fbb562585fe8471b0bef4f4e6d02b51e662dcaac05b983201305c124fae120d9379b1d91684d03d30b0abc8ce1d3cb1b3e8340f90ca2c6671027f80c62e53388273e8338f21944e833881a7c0641c408deb401408dc7011ab8f704cabd274470ef891fee3d81c4bd279ab8f7c491bb8b1006146e13c608dc260c2fdc268c32dc268c19b84d1871b88d189edb886164078f0f0020c6c73d3140ee8931724fcc927b6240f7c48cee89c9e29e181fdc13a300f7c424e19e180d8899c23d312570f71f36227c2af7be1ff7be17dcfb44f7be19dcfb7e70ef63807b1f05dcfbc44c1ed4f0c9030e7c76c9e1b34b083ebb08f1d905c786c60df7e9460e9f6e94e0c6e7ee3421f400610665b8cd0cd4709b19bce13665506e53e603b72903c46dca18719b329fdb94f1719b32446e53868adb94a9b94d19ec3665766e538607b72983800fb838f1c9a5c72797229f5c5e3eb958f1c9c546843000702f8c00dc0b93e35e9815dc0be30ae3827b61aa7b61b67b6178702f4c11ee8501e35e9828dc0b8385bb934013020822f49815e8c26705d2f00966874f3022f80433824f30457c8251e213cc8f4f302e9f605ef009e68a4f303a9f60b6f80473009f608af00926099f602ce0138c066a4410a1049b119430c23d2528e09e121b704f090fb8a7c416ee29f102f79468c3bd2f3adcfbf2c3bd2f1f0f9a1d3e46f049f9f4f9e1d367c7a70fcba74f524d4d08248400a406041a2d3eee6909724f0bcc3d2da17b5a4af7b4ece09e961fdcd362847b5a90704fcb13ee6921817b5ac0704fcb1aee6de1e1de9620ee6db9716f8b12f7b6ace0de96977b5baa7b5bb07b5b66706f0b17f7b60ce1de1606f8288184113c1d250881c13d21ef9e109c7b4274ee0901c2039a1adcbd1a78b85783e75e0d3fdcab61c88e1bb8f8bca1013e6fd880cf1bb4f079c3097cde3086cf1b64e0ee1ed0cc31e0f049c60e9f64d8f82443003ec9b8f149068f4f32583ec90072f70f26104ce31ef6dcc345dcc34cdcc330f7f0e8ee9e8f1a24525e83840faf41e2f31a2488dc3dc7ec1182f700c187a8009f62037c8a14f02936e15304814fd1043e45307c8a68f81471e073a4f1394e9f63007c8e487c8e29f81c877c8e517c8e339fe3f63966f1396ef16903801a11524e5016a8923a7970022c3230e08b251d6f0902ae1794947a967896469aceddb55002af256dce034ade77ed48450c0a6f22f68363692a3a0b6dceb90096e583b9005b6247296ce47c26a56421e50bcdb896034f0a096474fe5a20a27a2dc4e8f7b9d7c2072dbc9a786561792f9f27f40b99bca432c4241f7214a1421b9e4d2c0577b7712fc98897f4010d0aa52cd7085c55782e301fb55debdac145a5d206b072f9a82155f1ad90721c9d80b2c28fc1883ee30d3b72e29fbe7628ff4b57913e1047c562c540f0c559cdfbdaa7ad3466a2b6bb7739f95ff4f3d7e867164b4575ad456ba5b2adcb51464bbc2bf16ea5ab2eaf84551793f2e91bb28abf6409d5f6c9f07fef07fa604488505df558c34234e80b160b4a3eb05aa620fabdb8ebcd4d0edefb521dcec5e8774b0dc6e8a7e9f875d7d0e67edb52065dc16b55e0ea87e67258242b92de55d39d488e56eb6df5cdaeb7fdbaa49fce2a897eb326957e5640b00a58056f4abfd56a068274ac9fb726cbcf3fd2ecd6f8daaa0dc39bf314d500af091c9fb1452191325282c003500b98b922a5f431d94c7e48c39d98eb10aefe14512c89eaeb462b91308ed53c248a3528474b4ae1a04213ebf71dfa64ee850a4a8cdcf3f1c2f3d1c187c667865268e2f42385d07d8414487026485096ce794c8a685fab91e12d29012e0ae22df1f19490f12e9e12225cc90a3a7f40783d6466e68d7b3d48f4f4e0f5cc9cb26a1e8f073c9ea2130b3b68b83bfd700d7fe3a7b5fb298b47820f4fa7c63d491a49b070775ae9d1a7f78793e9c6226c63d7c6224c27e907e820e3e54440e77839d31d467b266fd5c451b54bde6aa4daaec2cf388ca36131498eb74aa1e01e0a573c895158808742170f854d592b0f05276409f350f840b4b7a053171c0a30c38435b3e321a1320f877477e91f9e1e21422801881a565a78aa2fee1a8a138b85ca89a74ae11a22f28900372b0f8177440a0fb1ab65ab40de91afe21d49f28e0c1d7972c4c98a866fa50a35e2002917dc5354dc5353dc534bee4fdc33e285bbbfad02d126b63aa3f543ec3aa561040419b4b6e209605cb495e59db0f24ef0e1ee4e593988e8b0d3c10b6a30c60949112759886ce15110094f174021c0dd4f29253d2b35ca03c7828893903764b5460a8cd33cc2dd08aac47334b185674218cf841d3c1356ee620882382dbb18a476938f7ba61bfdd6f6336abb36e32f73ac86716efd60d7ee8dc2dd1b6197eb8827a4c6dd91703f8110c4c7dd0b52e3bec40322e4ee3b71c9128e7dfe92d0bc1f4b783f8470ca5ab14498399dacb857c2ce5ddf4a75497f57db0ac783078f1e9546e3c183470f9dfbcb92c95c492d9894a125d9100b3f52926230998c95031ddecc0f7f7924b0e03a7f399678360b50792334c0fd6d9524ebdf5837f04640a2dbb1a78df94972fc7d4bd96947e913f0660ff76c3cb0e1e1ee94890438800d6aa5489524229cff059cff851cbec1b00be77fe1435d45cab588e915bbf516d3fa8f3416ab7515c97da648c9c7f28ea58b1c7391ae22e96238dad39e843d2d042fdcfd948a3916ab7fa6757f1b3a951e0827782004e17d5046ef1e67cb25c944990414dea90810d8e49b131bd8e632011198c0db5186fb6968e6ce5454149ffefde2872d01a06c5244b491a281edda21a83894524329a470dc532a9a1d4719e710a76edc5347c0783d52ee35f72e07783caeb8f4cb277890dfcf09068f460dd7f9f368a6f0689a60c2a301e39e3292f274ea8453aa486a480ec30ac5a371e2d134f1687c1c0a5d39d916c8db91c5dd3d45c4dd695243860cf17468e19e0e9dce9f9f705e8e2c74fe92f430c15c49ad58904b298a8cca0f12beb2d38d97c389ebfc9523a62df97472b0ec725e4a872f1f05332ed21629c4f94090ea5aa90e2bceb3d6cc1190eaf2b69b80335b13dc3f09ea71dfb1fc70b6fe1c06afa5598360cd9554fd88615be7fdb6b7c128bd7607d61a66851b5b5d3116ff374babc0926afa6918fd92885a44f45afa7db66f887e5ad4f4d35f2b69346c0b1f87b9d6f1b7fda19ff831fa695beab1ac5d6cf34841ba754f411cd57e0deac76247bbc94ca4adcdfbd6f161a1115947198b95bf9d4f2727c70636b08109e89398a6e9a63d930c573fc762c9c2efe200434d449a15c51a147e0d6ae03e96a37d7156c324ac12269a036477a212260a2587f630d5a3550bbb62f0d78ab55ad28f181662cc8228fe847abffea14b3e2831272108c509d8aae15ae99ebdc6e0d6bd0bdcfa2bcd367f6168c79badc4b5cffde01917b011f3828d8f8d22930dd41a39a02a4a59d40a65e93ebcb64a921cd852d8da6d254b80b64c19a4f1c9e003320853a485272723f74f8b62a5d5bfc5eaacd421933d7231c08c038411c18d7ca6f1725591216a34f138995869b8022e811fd408e247338d2fc41aa03161a004015074fe92e4cc316e1e863fe1e755b89335c7867eacae1743b1c4537443627d21ecc2d1f0d1557a71569bfcc559dd25398e56bba7ac58a20af71415ee9eba406a8a914625fcab14be7baa293506d76e4b144b977b4a0af7541429283a907a22fc18ae7120b5040ab8c0420553782a8842054ef88ff5a62ce084f3f4844a98a4c4ec1ee79eda800ecfee8d68ee290dd45d43f75413bb376221c5c4ce95744f6520b4b9145fee290c3ccde225bd7f5eb7de629f3152d8b257c93d15263fce5e8ce49e5a6248ac347b6fb48f89dadafe48ac9666c332caef72a9ebe7d8951d907bea028ecb98680754c7aa4ba32952300dd3f014f79405c0b88fe5d0677cd335e85a4cdb16487ffea21d2a3c547aa828a1b2840a132a2950f1a1c2c3c4628d349de6e96162b1787a98582c1e2616ab8709d76e4be3da6d41b97f94f1953a42b97ff46375e92ad28ff5d65d4328badec2168b753f1c6919e73ca435a943d14e81e284f64bb7ac855698ecf8ec5c5fbb1003ed5cc9b0b50b311014283c3d4e787a94f6caa774e2c3b353fa94bbc72961525a9e9ed2042a488183a0054d1a1ed01a189268683c4081236ab6446183660341506923c601f757c35d02ee2eeee0e9710387cc09777711926802072e5c76d061071fdc533db8a7baf080030f5b78704f71d9a265071ddc5339e090cac13d8583c7051bde82d3193738087a5cf8785c08c0e3c2b720c2dbc209162c1e0974208190bb7f2450799eb15fb8bbcf08fd90c5d6c28b164c5a88b45be64d577ae6260b28b2609285123d03448333c297e1677ae36b837baa861401dcdd8824add70feb67bb9ce5e45656979b5655398ae9077e2045422965e91afe740e6ddff6cd4eb4584767097d4275965024d482785f3cda2afed00fa7b3437765e97d431d9d1daaf3eb34eb553fed5521840f40ffb8a2dd9de6d12388a5c12a6c5538719f2100ed6686042a28400511546c2a5a70f78b29155a7ad11f6d6c032b6bf3ea69f6b15898c6f95f10c59fab9493ae527d6dddc5565ba48b6dfda28c8366b4cce010be0c3b14057a05a44d404b9dd02be00c0d20ecf53aa24ea805430ba4f3bff4c6224cd7302df28482492808d2af866fb1c7797fae4c3b3487823a545b0d83d1d77596a343ffb6f50d597576324eceacd42a22e12562a9d6252e6d765ffc2becda6552f87b0794ffb5abd077cb4cf1b6f2e17cab1f15fdc2bfb9af2bf9059135073bc106d126f336608303b9e26482fba7b1fd59cde50fcd98887efa3e2ee3d7d805fae972578a0449cd3d10fdb46e00e18fd5c5455314619af8c06316d000165710e03171c4ef72db291818c38230e18b48b9cf4ff6e028a919d31a0e1fbb724f84712c27b5b04b29fcf05a29b0f06f2e81c2dfd72249510a353edaba140a3f87c33bd4d2b550fc7415e9bbd85a6008cd9285864f9f40a159b2501c95b6df7fae85807e9ad2cf453f5b85edc49bc90ad2aaad0641aaf54ad76af8c39a1f888260895da08152ba62b1e8cac9686f30b2223da194d21928a540f4c31ad6dbded2b17b5f4ef07daddefca50577a754eb8bedd16e5bd1daaead2fa49de04097999c191e68ccb0b400c192253d064c1b3b39fb61e1dbaec5d1ac51161293c20f6d7dd53c2463d1b29e00eafcb517da6d7b4bbcd2a178af32ae7dcd43f95f5fcdffe5c832cfaa4c66bb9656864f2b052d0d06ce40d0666b170a053f16b8ba6df2858a36b9bbf6cbf8c9261f0e88445791401588246322504575c6b8f62a5d2bb502e6feb25860158af7b5bace72f856491de6da47412bb489cc766d1590b26afe9c12e7b37dafa2bb5c6bde3aad6df8d56a483ab5d6ee9986603889c509cb9bc320d55640b08aa674bf6eb5284e2c67d44acd432b9a43ab80f45ab028f61a1292b9202529066b158156c02a19e7f06d55f3d3f0f75f6c05fcd9d6f5887ef9c78c592c903ea9fa70547af738160bbcd8e63ee733ddad36be7855da15ceee713290e96dded9ea6c05fcdb26ebb81357165fc991383fabf7d69cea679908c509553ae02642e4efd6bf2fcda9a8586fba3eed975af41bed156f3adbbac97148f7a1885d443856732da994c15ea5f0feb4745881f40e6eec40e65a1ad21dc26427bc783becb003b9830a876abb7a1d66a0c3163ea384869fcb2b36b8e17e820116b2e672e822872372e821071a7210355d694c5e4ac31c82e0a0867b0e5faa290b0487965a30d710d190508a862c3f3878d91f0e44353f2be39c87bc1bc6e8e2860fdc0006875e6b84d6f00d3adc40a58a24395a1b1ee0440d6fd46083bb7f7963d7c9fb6206a8c989d28470e14e5866ace0308648030ceeae077073fa8b5d184b0973558a1201c002811d26b664f1e1ee3f8486cba71c50966620fd253ed31f91388602962c4f60d9c1001b9fa4209dbfc7e53ae6de55c2bcddef9ee8caf097cf1218ecb3e58c7ebb4d84c86789d644accf12188c86bf3ffc58a0dfe33013eda66bd18722567d381a16e911fa81dbe7dbde382afa5aac44f403797a4a1004b7c622c5207865a5026b58bfcd8ab41c27383115a559126f5a9128387efdac2bad86737418887e39d7d2ba845fb56bf5bef957f4fb4090ee661fce0d594715d5f4bb21ad7649522dd4fab1ba684fea8a63434117bbc2cf91fdc372d3f5665bb84dbebbba3fee07378737c231be6034632cc3494c718ca868128a6384d29511ca22315d19a1a28a5216b552657c623c42e52e63c586eaae42b876c99ad3381a1665615897703ec424288b48bf0c27c6ddedecabb1e17eb2d1713dbb127465c9952b1fd055d0e36a4c01febfaba46cbfc6bae697c96a7e59eeafecf34dd7cf389ac5e9ff0052d892c944d15623c06bafc6648c376857b8a76670f713161db26e3ddafdb92623eb6b9dad7d0ea1048fa5728fc5c43dd68f7bac24f75856dc6369f7585ddc6329c03d1618f7584deeb150e01ecb0cf7583b70afc90f770f32029019c49bd48790118e4861857b5284e19e146bb83786c6bd3141dc1bd3e3de9822f7c6dcdc1b83837b6382706fcc03dc1bd3847b633ce0de1831dc1bb34613cabd2612dc6b2ae25e53907b4d53dc6bc2ee357171af0909f79a9a70afc903ee35adc0bd261db8374511214180fcf0e1a1c0b887aac23d140bdc43b1e15e2ae55eaa0477b7a929818b1a9f5c10f1c9c5129f5c08f9e462c92717379f5ce87c7271009f5c7ca1d96133840410721ce13307067ce698c2678e14f8cc81864f1d279f3a88f8d4a1824f1d493e75c4e05307cea78e9d4f1d5a4c181244002ec48423454c884284cf2848f88c5285cf2860f88cb2039f4601f069e4e3d328e6d328fb340ac2a7d1123e8d46e0d3280d9f473a7c1ead7c1e45f17934fa3ce2e2f34889106ce61c2ac3e750199f4425f824c2f14904e49348e903113c200104a7f1f1c30b904f2f493ebd549f5eb24f2f04f0e9e5079f5e8ef0e965099f5e3ae0d3cb083cac6ba8a49478d2a2648806040000000000d31000304020160ac562b180583c29f01b1480015b9264b268461aa95118530819628c01002000020000001811038326cf4a8963671d8f61047dd8931adcdcca1d39038d160ec8ee2cfa12e3031b5d8c426fafe237d620a13a521e824cbc6159218dbd21fb32c9c49babe63bf8f1e1cac5d0e7cab9fcfe8774ccec79e4f76b3ee90ff732aa1ba8e76020e7ff5b4d386cc0dd79907f62746abbfc776c4ab946fbd5cf40fad7655b5bc23eb09919f9e355251bf1f4c429b3a3177f81059bdc0184e285bd505084081e2884f67affa15236ac49a8f3cb44f21e405c06817cc25e730a2705aef24e75ff8cc25fd63e169f8af6c63bff763f62c1cc5fe5faeadb6638fc7e5cefff006e116d2917de96daa4d0e7a9b39ea78e6e02af4b9a20a787155cec1675b43e8c885f77b5c4ef719f9cf9c2b9d7d68e2f1b61010ff18a672ee8395b7a80a8201552158a36febcfe932cb586c48607aeb9a5375de91b8b99fbf75e6754a23f07fa57631e48a3a71c74ffeb5c49f38bf4ed24120c7479a35e322e9cfe13c401987221abe16936716f9bb85b02bffb4a8e23cf5e3cbae1d006122583fb7cf42f9ad532520f6f6062ea3ed2baa3a8018a848212acdc6da613e430ccca7de62fce60aa181b024f145ca6eb98e830f7c546377d4d345158336b40c1e752ed9353802523c9fc28c5fc3cccd2303d082f046b6243c727317333d56c7d85c198602a0d30bb1157d3b9ced084dcd95cd9bdf476a6143b79b61942329f4bc03d0cc84c02c686a30b90d6ce9d91d01417c6a1f441134d215e3273d2ab01807197d427b7576edb43cea8ed3333f0f737f771a3848ec4a59ace76161b1c83aa347ac86bba487b064e76f1d99e7a9dd95178674bb78e6048da901c433286a471f2ee690e8d9452c7cd600c3f63f46472e6417007cdfb6267f54fba225b71ec0d24ad6fdeeff6878ebec73a3b129cfae41ff164f74eb9cb123beefda7b7981f0fc33300f283e4a1e7f6b22ce28acdbd03e8b6309114a5d727f5098cc3e071100d7998c67762133c56b59c1d3b8caf7b7b1c856af9482acee7c97504e4966dbff1923077476bac48ac7ea277654600fd513e5e7fe3b5cc0b48dac6014d98e0346a74fa05b586850285edb02a8b8447c8d22e6894bc1948953c2df8c36648fa7f9e82fac0bf68d477af7e059866aced377e9c4c38f2b2d128b9d23ce4865e5f24e9cafc436b77424cd8c6d83cafd316cbe1fdbd0a750fe043b50f94ba038efe5f9e25a0def07ccfc74f55cb22f92c4c150c1e764d56cfb8ec8afe5ec33dd80133cc18a797581256a55fb724c5a465aba58b4c6e5f2622f7501b0c717cdb0bbd9fdb1fffab48ca4c5540e568b73d7bc94cdc09fea139f45f6fa0ba684a47fdb6550b0d1def0d06467cb2f7afa3330c9f32ad5a1b396c0e3363816db87cd3372d34f431b8764dc3aea17e3bcc46706da0fbfb32fac890634abc2f912217c817cb35b70dcf6a562b807f01862bb3cc4597e43a73e32cac8b5c28b1fb2398023ed03f169219ab34684af28d04fbe7417d9a3555f49c2b2b18f18438c4ee14eb595604d4e57c8f79f66e8c5ba3fbb606d4d2e152189fb7475ad85e3466354f29f598e23507bddc710db9d36be40a7755844794265cf1267189cabcc16f2fa8cbc42d3e3fc17c1c37bdcbcaa92b45844352eb2af763524dabc188496b52f59f916fe333726d7d79cb5166aa7c96dd9bc16657eab67c801850ddc6788d7e042a22ca02b3a4fe9ea79b1dd36b7d6d23b115d6f57167c7588b62095e7cd41a62254138bb8a3e573b7b332a1648763faeeb39dbca49c9002211e8b07476d4cb7c479e36906a44cd2104cca48024585df2e32a157c5500b34f04c362428f35914b64715603b05cec24d6658d1fd1f3980339d51a0314407cc144889ee297c0cce7e4af9b0046145836e14616e57ab20c63a62d6bbd01df045c69bf173a4e658c20bf430544ceed86f2920e3a22ddebcaa4dcb8850dc4eb263c56daa000884b492b04f1c9daed1470655bd912130c8826cabb08bc610c79c0af957a6704797ac6e9b4579c74156cc37f98d0a82ed0c1a132f028d9be144c5482d94041d55bb67dbf0c786d2667bdf3eb0f2ac5046608db4f0f56da8cd3451aecb4e76e87ac6876077af68d2084eab291ffa1c0cce9c286cc2f14d0e00ae61f9ffe2ba4d28dbc4ce46e271e42e687c40b9ff02002ec8c93f829c87a2e80aeb13c17b2333b7ddb9cb1331b291760defcf94aa49edc9ff2490bc9ecf068c1fb10bac3ac989e30afd9b799c366d2cea9706ffbe282e024f5d3e9d71d5eb6b9d9dea4fa0b61b0f308992159e6cd544e1360ac3fd690bed1858f43495966e2c3311385f391285308f10fffa0df2335d9ebe2f508651abacaeee1b101712d43778ea7567473b2e1b9b91eedaa748a84306057b80d2416498f56104f1287f93cd1a3e03bbe069846f8fe48492819fb88bd4bdcaff733015996da7a3e0814c5c09aba0937b47f0a49dfb17f8217994c7a0c88befbe22038a00934f91df9b1aa930fcf7cd8f0025166f1648586808c5cf621419eafe1eec5ac1ca9ae0d805e3001e493fa8cdef5303c68c5f51f5d7a9e41503b2427007f222bd0af5775f77c24085cb27f858e007748b9f8356a1189b1975ef9b7fa430b707c59dac851dc68ec1eb5f5e8aadf73b16360d2016bbe7cee9f2049470c8e8cc804703f9e450fb26df38d6c615819d605889f0d684b803f9ad767b7da010d38aa687430ace6044a651b0e7e7d468462d8870587ccc68735dffd44d05003c03d2b0b9eca35f29dc27f633692febd06a0929205f748f2f2727536efcfdd23fe2cce9fb5b2b56175baba519b26e40a649e3bb5062894f1afcb7631c1bee5462909a9c0af433e978c0805c2862bf6971208388643918973314675555b2cf2a0b79e2cdb722bd5a99b7ca137af52f0f78822591eaffad96d2f08f176b970d3070d6c1c28660a92274cac83993dea895066c2bb6185b4eb5825a64e09cffd5b002d037b4ebe59774ba660a50459d0ffb13399546c562fa4b54f6f8c23a59937b930af83d8cd29b2d18dbd7d42c5c7b6149ec767072ce8e16d2b147572cb8a5e0b33efa84b940416a25e984ae1e6cae73d81efd59bc80c7a9652f2fbe1c7b9f236dceb736fe79531c8a3828dbdff13cc1fc01452a4d53f797f7978ee640ffb919fbbacc231ecd5c98ad602481a09aa9cb486a6a18d01a1bf40f69914721d2107ee1cf9d2a5c4e0936e375992ec354fba04cdd7fa2386d3dead855d29ae90e2773d736b1cbd3d1780e9d73e6ff3fe479b5bd862ee00fa89e263ba348b08d4f1937263b1ca579d5c94e4724d2ab23a2f1a3b02cd210474dd96e2df88440482e89f79b914e310548f1f84934dc848b5afd978b508b2dbb01bd1a8b6485f5f7dcdcc1e84346cfb6ad8f84cc0daa4a32bae41fa14fb661f54d5d78afb7d683a25eb9889dd98a4be7204c93890321447bcc1bc1eb45c9f87df10ec92c5b8121559bfdf18ceead18ecb26c5b4f07674d7e8307c44d13f8055a4ba143dc6804f8143f68e95fb98466ccc0ef689fd1e5a980d97b69f06881cfa6c36256c1abe18b3c8c1a721b6409ebebc9139d44b21737b18637d22a86dc4da9e51c5ecfca901e538fb197203b90e15d50d4088160c749789021404e85491f9739b92cf21d7d4104bb7dd7891d77bbbf8a0401a98c55bc9777957b424c19a0f7da93edd65c5300694d90d9fa12594e9b29c1dcc8aa720befcaca965fb83a8a8a59baeb91e0e3e8c7513e77b80874e4f23d2eb75d60f0263c0485c609067d272dec54b980309e33fd39184e2e07fe53f36ef259437c8eebf6844199407a4d3cd0376396a2cea1dc145c4aff369403cd38d27b93efc8bc6de19ece67a4636bc7f116072d04413139ba4d70a5065df01571074d7055b6ff57366987df88735d90fec45afb4100e1bbd82abdf3c5b9b891a88bd57e7d9277bd959c1cc5d46d7e9a3de348c7a11e651660c55d1d4a98eccd85771f0e1f9b8dd53cb6916c9001c13747eca48c133725d887eec5142b1d18fb2a795977bf55f2c47a08d4182f2beb951fb25d375c20c838061314ec2398e1269d2fa98287474ea0a2e35950ac7ba1352820cbaedca904beb7bf062afc36c473a95ba149022eb37ac2233355940c917ab17243159faf399e8f46c6a8421b4e564d9d8381315e0a2813c65478a4e8337913eb2cae2ab28321ed1d41ed9fa8cccf8859c96f82e65a488fa418266344d78ca041ee9bc45b3815d4de036b88a2ae814c0e603b2f02ceb61c39a81f0d274d14366b25f0b017b5cfc062f62fcfc2ec2ae0bcd703ce24ad119fddecd900e0e5531c91727d2934896df6fdf13801b90f8b6f08d8510eb2afa77f9c9aae26c915ed3c1d20bd934b90feee037582be90e6c647df08a018fa390524652ac525360b3686bba0c6376bc2106647b8a944077e5001edb23db51a1fefa94df2a0b0011fdbaade0fa0cc961742594e6fa05db85d88200e2a642746ecdea2c52cd1f14e24976bcada97b8c6114e8a728925057724110f9024f1c01138fb60077f455a6dd440e1682b09dad9026aa263ca238631cd2981a5a40db9c4a67c1e063aedaf4f16aaf7683c48b88f9b88fd9821380041f7ccce832cae80b074d392248fcdd0a484e73418d4b5a5036b976e8d5da8e917456033872dd10d20fb4b5a21a0103214931d2eeed5c27aeae410231d412d9216f1673b86176b931a043eeb82849e4c649e58583be9547d4322ac889bcdaad704b179f61eeb095cdb31a23c475645145f2bbea31063b9147193758d7c37cee7bff4f287d21eeb97144c9066b03abe78423b008bd4ad43212b84395cb45f2a96d44cb1e2cb40ff546750c18583230136392f8ecb7052df99c32d496d3feedcafcd446cbfd4dd5a867f4511cb3b57d4937a7ebaf9149b0fc5e4f090d51020b0b350a44adeb0f573419ece4b6a0b535efc3211698632c093733d318fc77eecf2e9c7564060d1f0bbfd85dd6244c1a0ced80ed433bbe2c4e92cc0f676c55902d98c18a44afb4d7855db836059a02865c42b4b68e573651a8c08908856b5082b877a8dd67e5d1bc2d6c8b69d82d6ca1adc102065cbc5a64f00d022935cc9e44f6b17c73abf7c35523489b61a8b9a015c019ea238bcfc1b7908e42dffbe4d5fe62b26f0035a0834a4c8cc6f803bc3750a97c3ec7953e2265a5effea0aaeda01100e3ce2b415bfa24fe9755860a89f1f1c1bd563ce7e18152bb3b4e76f44c3651768ef1ee8610e6dec7a9809a5b0bc4085e01bf8f177ac0f2a6ea66b35852febb7d6c68498403e789d97bed21c0823ce1823dcbfc234e84e328ac0d1d93d217e89efc68ec2c782072d56ec9ced64e83a4d074674ce3fcb0b50cd8654ffaaef12c2bfaeaef00f48620ad8d3f99fbdc67d6aa95abef6af4ea206d9721b920ffceae710df940e7e842e0f4cef1ddaa50ae1934e66f43dca3236fe2e116c01f17d3ad41f7c06c2567558d3c9b63e7f2531ce3ca853495592d105e15092076a06de8e29962c0c704371baf8fd2026760642459d7d8e49cdccf3b463153c52bc0fdb8601e728773575ad8501998411bd380258748f8d106d09deca6d8b5dca4365c0e308352e63f6159036d42a4d6731e38ea005b437e1adf314cbab326ee9d76799d18102f54b031db8369513cf73aa2b28a139d761d9563445a7bd6564d5f6bb592563ffbbcc0b1170337bf88a3c36a0c12ead9075e19871359b06b5e7c0eab43a334b4a07255850082304198dd301ac31f9656b39959d936657cf6cc9f0082e3549f679cc6c669ef6b8434b0075b0477e829d0bf34cf494925c5f0c91b8763a6098051ae1b0bc1a23665e848a1cd7a73384bca6654e5a753635d427e11de36f721f2f79c56d7b4fc4db1257e127c6f035bc983e115eda5616b95e85059828fc37cfc0e1849f9fd3ea6c7768aa7564389d4858a5648c265f3b874aad032465f30079548662f7914a072f2b195981f6473d7cb32300917d7f33df04f3e6c63467fb9c02818995f8c1b78234e2dcdf86517db96e98998fc7b364953c0f248cce38d344030ebb1fd7f51b6d9f8909448814891b409b58df7b804d1a9ea297a985e5a3dccfa9c97f97c0bb4d4c252c5e858c830da1f0259b8114c6c6c70b2a7e44c40742e9b9e2d312a1403528211747099d8ceedabdedf2dd1eb3601cd46f78db649b0237cef7b6fdea9c157f06b1028e92f64a99b94ca193a3b874d90ea30a34faf11b42d45db58b22e463e06b20982febd616f6fc84c9581db82c60e2dfd3fcd9f2a5e8a17ee435ef8c2dcbc12bc32989166364ef5ac3b698a7e4ff340fa08024eb412c2a02503cb73ef853a25852f6e080b2094a08c6f7a1b1c00b4cb42613170eba8b5a5d1e9b7d378b5218e88b119d6c0442196a981dc41ca90a315484a4a41a7f28fe6c12a50a7b2a3b89ceeae0dd412dfa55227c351182c73bbcb2bce88d4aaebe14082c1ef4e967b70b331eeea88679d9d01e54d43bd2e80640be120fd5f73a09652c533cf0d7454a0cc272bbaa67676749a741d31e30815430116bc9947c2b680d2e9cd5258503d6c1ead6ead820ba5946180761ab112fbcd58de4e0b68062e03b776a78f288d0972b5921ff1c26a37d02d9ef41952c80319312a337bfc81a63166cee040e501c84eb6966ac29df8b56ddf3d3a3e09cbae377b87d21b798eadaee6c32c36e915272e160b6ff3d8d77419fccc3e65cdc7bcfe86155c59e116517ce2ae93f5f0a32429936a03c9952ae68d16200ac779e04837e814ba59b97c41663fbf11f0916e03c50123d82b8a410f365d5f02abc0f5b5dca21a4dac25fbfa96b5a1574191dae80fe654b1a6da037c3b01098ba100c4c232c689479c0f9ea0622340912c9bb8d627bf600ad10d10b30e54401d1786d32a9de5fcec10168ca8ea64141086185f9ad0181f8feeb630c52c40c2bba5fa09c02fcbc0467239c05711311e18709017e69c9ebc630848e5a94b24261b9248475b140b950ac435f8be44a80de4f63be7e53f493eb8debf844e595d9530168436971c22f19a94210abcebcd17c4ae2d115150df61c4da1be26d39f2eae16bdcec59de4cef65f0722fd467a8452789e68e2fba421c05b4d9a00e7d416bdb0814753b873356d388ca7f4cb7f01d9699e5b4a6c2d9c8688805384c4be659740eee6613f67006054b6dfc019a142760e79fa1a73cea278281f67e1097b0a2bfbac7df6269237cbfb61055313bf0d78cca237eef26b30f14525c8642a3e07eeb8ca3f6e1686caa302b54f5d4af4b0aee084cd0561513dd73642e67cd2c61b209309e67c8b5b38d4d05327b0bcf06c33f094fcf8d79b2166e367692ef94d5a18da3a6dc9c185ec55336a0e1b70e7c30cd1de83ee92d488f1c9c5d0bd627c3242c2a8358f830b5943f82c395dfd4498bd9919f7092e97eb42672e8576abafe3d0e28004fc88e3a9be820d914fd31569ba5a4c7de820abee48faf25381bbe5df56960384b248748c1502e4caee6c9c5278990865d33176729560b283c2df36496af4714079d69e370d4e07b480ea8d1fd62f917ac2eaa45761dc5342470dc14a957345bee9cc20708ad4486be81446f0d69d72efcb3f726c563349251d75659f238e7572396a2a6185baf7aba55f3497d876a244961cc4794515f0e656396ee39ae03623ffd7289e4a3cef8d9404f41705966c81297f84f9e7c879af9b48caf665c04d918c1308d9ec014d20128fd363e22568834b1b61d3ed6638a3cf2179ec5fe43850dadf28cdc0a8897b72bd3522a79373a241e353453177e2eb4e203a18133c32393eed24c8abcb60703dbcff6af12a019bfd7c5440c30b9189e1f4aad6338f712fc05f31732aa60827a24696c7ab249c17fed3f66649c7e8973119242803af58d12a4f1b6030695829a3c244bcadef5776a95052bf3136f7d48b232f9c4d496915bdcb70633f39b6934501b64881a5c27b5f583699be0624cd38dc55b10a5750b7a4dc80befd3a30cb357eb1d342a5b9a798cebfb029f6b6da95d4a6cd0a6157e80dc733f4b942ad9e59c59b22d19f9b3d3900b1375d857cb3afb022690efb1df0d173637cfac7ac8187207a78d6b2014f8bede6062494a26fc3cbb470479ab20364b940fd701e7be6dde2d8285689adcb89147609e3dc404fded2313ce354dae199a0d8edb771e9d95ab6cbec3d320da7cb797b9886a6807e1909da0e86586aed5c1de7be0e89d60d419cbf9876de6074cf96305eb674e2017ac587a6ef0d6e3db850d097d6f0ad240845d7be1f50ef1cb4134eec1f6f98807e6ab7f6186ddd17ae7472befcb4be2a1800359c20c0f9ce9ed15c52e6a12e88c579ac9bf554ac6ca91c74c5f4a3036da0518cafc7920dc06059b7695d7287cbd814a1b318637d3b36818a755fd286e3e083316ede9d76cab0308c46d030b21932415e5bbccafab8649fd95aab3c98d2d4974514e5a99498d03e598f27f7b05d5110684e617c5776989df89521a5fc0e9e39cd0d519f34aaaa03c608f2eba8e3e3ace83cb1489ad22aaeed2e5157a863a3cd2c369b26f42b057f4abd914cd92ee7a7956f945edc8db7ec085865e6acb2e6b7a4ff73346a634508ccdebcd1c18e87cc18d406fa49081f002e17a335ffcf212d817a57f489c33413e4253063b9f439f8dfd69eaa1aa2c56a24940d7cb1897355a343476c777a6716a69bf7db93b8d4928821d4ac991545d50daf1294bb305d0fd34cd6502318e094f8ec4bd737e142dd4554c87d4bf5b818beecb0b2e8046bc5870b0f3f55426b9046e3c8c23dad4c11f519a4c93418fd858df045650b91235765f79b6f4b736ff1c5c308cd9dc530148eacad50150164c9ff743e9e47fd3d83ff8bf1c0cbc35328d674ddaa0cdd1c7c30cfe58bc7c238549d90f4a9c2e04e93e82dada2bba1ff8a61d7fa18bb1d1f002030d86511ea14b9b51ff1ec60c506ec6c728655ffabf3a1b063c99f3a878ccf1d05f6dcf775a083823a26cf780cad0dda46836ad0d6ca6d4b5cbdf43b3b036016a4f1482b32d603611bfd043a65de5213e0e088ea41e7ee6a5edb648aef94154236d1091676a5c16e0fd0436cb684edce4d824e837d75375c9b206dc59093ef2265b6ef75b5639afac7a5de080b716a35d1f5f40238e3800dd4e2a4775041829142fde376c0e782117a2e006e14c743e473f0e43633538550b77b3ffb30a64bd4ab1f1855f3b0a90a3dbc76defae00a4456d24a1923f52438026ced0060331c80d1bcf2cd0f6cb7414e6715c932d814d49020a90f00c61e8b16c0035388fdd66c2605de5fa5400968b5274bd568f23249ee279144238887b37e70a5453ddf7bc1359eed056e824b25b9b8315addec775fa1a98cbce35bac0557d9ab30425a7d60be1b3319fa19e0cd6435c90ec1695cb930ba4fc6f705d09d884671526f921b1f36ec97d46e2eef73e0d41c34d07e6ae01a0a7e70c6d1675dc3758effddaafc9de566b899b13ec164b478fed86a20e0b9f0bb7a2596160ba260ac7ac14ffe685f84af6c5233db0738e898f71ae28cff11954cb487dacee8d6abc2f32c41109542c3af13bee61f5ba9173d0acb22a665baa4823e7d5d7fd89d9d172319a37b9562bc64d6f754413036b2df982c69e479e5c5a198fa9f483b1c872653cf33975d7a2990da37d7bf2b89cb91115ed12a267ca53fbeb155098efdbaf546a63fce58993e2a779f4dfd42854e9bd1246f1428a1356e7d8edfaf2c2b755f3a1fb1cb6e124438508022062f338ac053e5775c20f5fb7366a07c7c8f23d5871ab1e9c481bf78b9822a6037cb557ca2b2637113f2922a0a9104dcf5dfd588394836b32a58500e46b3f8be861615521940491d6179b37464b925f75ee4af972c1c7b7fa9df1a70cfaa7278068e93216eedd59d5f9a65ba6b3ea4f15978ba29016ce44d113273361bf5e41c68ecf2c27e342fc73e8cc15b578d8d64a9fd3b0f34285019aec878114ec55b1f05f449ad1121f05195809f30ab9bf7280c871626a967d2ffd0ca3829e1c357710afc7acbab76396691655277e652c3fe09345263d2f2fd1d8d396faf5587fe7dffe6806b28bfae1b01f628ac122a974de12a8a56057d54a8e849cc8e26a62e6d781f4c7a0a98f08f6c4be2620e343cb56e2f895ae3cb2809e38c98788ed372d8e36c2c04a014a49350d15144ae47a29c9410dd767481fc891d6589992c9b561b42bcfc584a264c774b58d8019c0d0c3e27d9a0b7a06125a3cb200187db27b86de012880cd9f6222f269295a48fea712e3db33dd80ddf865f4e21cf9be25edff5e50183eae2a9e0279be1c3720f4ad5087cb187d0350bf8d6899fb54091b459473233c82c1e84e5d46c1add869e665669b4c03830a174d040165915b2c1bb8482e9629f0a9738f3d752ca4b0ae8eedd7aa75083db8bc66b7a4f40ad6aa5b74c77ffb1a8a02a93b17ad00b4761f4150d5343324ec4f8c91b2e66557c4363ef382e8bcbefe69ca113858ac7dfbbf1910486c40e9d83ffea0374ef47d5b6ddfc66029a400a569881b892ae72f030ca56332cf48d8c9a36f3d3a661c8ec64fb3a648e09cf46f4f348ed07ada78653f3a6a238d650a8765876950dc8a4166aef7513047ec73182a46b893296b8996d11b5146edcc4c422efea80e06e6c0b2a842385206b6e238bee21363ce9657f327fcb6fff7af68fa382e2af4a004bd4e154c12a3ba3104c7ff0d70665ecbfa17d9312de8d89a3919281308d309a407980851e713b5e25da6ddc3e55b5aee08fd0eeef85196b7c791b3a4f246847b042f555aa09bd08340f5357ef97bdfe85eb0e08fc6d6365ce5c4264694b98b76be0af36a12580237a271a710398a89876f6aac7a2346416179eb35509c6cd29939303767608ec6f541be6e741f291deaffab839b715fcfb9a60c035644502076adae9b2a06ddc2cf324992d4b74099f22d5d8f1ee818afe928c7477844b9b122694c04287afca10373adced8064975bdc83eec2a6296daf964f716913a697f9c1c3e5f36449fca8c3acae70a767caabe05c6892b0d7e21ab0362072e35dd6b24df13a089813c446442fd6f980f5fdfeb1f3e4824d6662a46fb89493423b88566233862c093848ac47d114e624e8470deaed0e20cddf2cceacc8f7ddc9ad14a254e7b4a547427d88a8651cc2c82ae20a9c5f706cfa4beeb5cfafc180cf2e94e7c77c37595ddd3a547dd7f8c006ef2db6f3c3c0ce98d2c3ac163289cccfae91e03b1e0a2647cd746731ba5e3dcfb3f0bc3e8fc1cf902d2774eca97ff4b9463efa21da3cd7ef0457f8e0f8bd3389e2db0f5efded200e1d2b008bf885ceda22feee1d44d92bd98a77ff28587dde6ab7f7c92f371c4c0cf6896d162e083af1fc3e340ffbb20acf70d90321770f0d20c60b920fad0b182059708ced77779f88c48e7072c7e36d613bee1683c2304992b1cbb3da2b8eac8bf355fdfd74eff78e217a7e683e651f0c79eed40ea7f7b1702f2effeb77b19ed1c9ef7aa7574d5717fda53cd7fe8f0d431bfa459d32d2ff36591562c4db6ad9bc7ebf0145b6e7fb8054faf39467b2ea060bdff461bfda2eb7767f4f89abd376cc57f0509b4a285c633f4f8dba031bca039eb5994427c7c0e8ea666d4fd7172b8b15efce2aeead6bb24835a746e6dff06de60849139582c1c797edff9f588ca79ceda10693322da9a7eccdd7fbe3fe77bbcc741b19776447f59808546bc86ed57f359d40f9f31e825542f5d10daa335b70bf1e4bbcedd8d033e355d0ff61673fe71fc0bc37b7ff1babb427f9dbfaea9e8d1cd804cbc86f82566b32d80e58c1f3153eef49bfd068e7c0bb61cfb6befabea2dcfc8e1dfc7a79c2fcef029f36367fc9ddbce6b6763dbeb6c543cb96a649fd896136352eb3bb1d77f4c474f69787ad8417cefd2e95e63c4193c8f712870fe50195b57729cb9b9df038e08d4d576380e608989ee0e87a92f9fafe9ce00679995a1edc6b7dc2e1a784b17df486bc616503ce075bd0cc0c3dd5c04183acc097676c80ba077cc08fe47456107b7eda81e773b28a301ea5dc80d6076bc870138514d9d0d1eb4cc01f03191198fca07fc0df1f81260a07a6f80d30c1cb140373d15e6d9c24416960666a89282ed3f4f3e247c8326d84fe32041254fb10114b9174ed1d01d48c4f4c6ca9f8c67dd2470cfdc418e66464fa8ff947afa4a28e3361a7fdfc4dd4fc3d4b4b393c89ef38faacdb383b84ff7a07645ab563e01edb49afe0e601bf3fdf0eda75f9d4cc7f9bc8ea74af146fd5b507d06983c737e2312a18e2f094c54cea7b7e8d0275a5de64b7d4a447c0e6ba6f124235b426bff98e7f851141fdfb3d7956a995184929e78f0d89a1e47ecbfd1e3f1522f299a4853408cb8d83db81171b506c54c70c8787ba256ca8c3ed4731ee183ffd73aae3ac24fa2165e47c9b3369a9b5e863fea7dd450f55fc838893b4e73cec6b051fac69614f02e843602c3649cd6d71f2f9789ff8ca52be64e0a8ef3298bb9b3b364bb4c2fdb89a70294eb9a1c859632385d94f505e5b137d5dbcd398b84ddbbbd314d7a2f22df811c306d06375b59f08711d1194edaa77291b103e372fa36c94261e23904dcd0fab01b5eeea0450975e019dba65dd2ce05c763e2de293ef10d94e438418ac7105b862f6b195ffd310338fdcc4b82e79adc107112153914ec0de3bda9e39da3644e5dd1c143a0d52809a1088ce2f0a7db760ed3ad176c6d559ac7eaedaa9e19dd5344c9c7892fce813a18f4b03177a6a6af5c9f41f62424f8f1810ba5be1912d721a4a945ee2e32531095940ed60154f4d61edcd6e299b4af8d112699d0c293797c442dd145dc64ed437d38ad87d0bc6e1a8b015064b09b3074dcede94002d35ac99474cf91203fc9a010608221b9e9b4b478dac3d3963aa76743f92f87705dc1a83d0a0ffbb2a5b1b282d6938e2b350564975f2c26958af8d78f4c916b692af38a8ae35045de1824c57a637240ac6a79ba879f792f15a281995a4476e1f50aa1b8a02bf118e87f3125cef75d52d9561632bd728c650381922519648ad2910e3d554d1ea6b79fb16989c5cbdcb61726321edbc613440fa2a559a93ec2266ecd20e10c9eb580d03e4091e373923d68c4ad37a995fb670e2b19d221dee5f048341c64d636a18d206e5b12094024e970533db5492e95712dd10d9670a6c85b7267f010417e1874106e0a590c415f593747deb9d4148959ba7dd7357370199ec5a2ca5b49ef80b5ca206b7431f1c8aba874902d372a4ef8017c6a02451c7ef717e1bd5b76027a840a547f155a1db6c426da1a1197b01f77831b14b9f286ef6b101c6d38b7829246d3b9f2897c9f45c6c06e68cdcff467918e7afe799d3de8feebaff7678d1975b74db4fbb55c065dd3a003fe2ad62c8eed6a1f2f4852656fc060b2149f1c4ac86644decc05cee1a22cc2dd8dbef4cdfc2b0ea55714e19e799d8b2fe192c8b99b5b30eba6e999262f2e3c09f21b4a87df4aba3babe7df73087fe27ba310ee17ee4c4aa1469e207a56bfba7ae6ee8c9706b9c0ccddce7e749da445112a46feae412b4b2b63a06835fb96b420bde827cfef221f23395cf9ac8403e5545e23d63233ea30ec311821af2816ffeba29ca0c1c02d1a021f30778b3300f7ca46a89963e3034a1e821e6cd82a40eee317f7041360979b9fefcf28163311683951080643848e93ef76fbe093bdea21bb8145a0ea0ad9fdbc299ba2a365f4330a3eac0c7fb7566edf01b073c67d2807852fa1f20700fe392598e6166710d56223f564d28c50ccf780acc9106889573734162624815e16debc24a2b7671220b6741eee74c276cbd938914c3b14e8617012724c00ee666741767c33e571e0d1754d50cfea33399977375b0a4ab95842561b19755919dc80f802a996e0c670844dc66dbf0d50dc806daf2da28411107864e636fd39014113c54245882c9b27bc3b705987fa64c2987a057b264b3663f9e3f9b00f135f0d21b651d88b141b0c89ac0c5dbfaad0d9f4649e69e3a6809ef5899ceb841fba0d62e2c4dd5a72c2b27d3f0f898d241edadc1c273efdf529302b961fcc24a66f411403081301ec7fa240980fd50f1431800a11d780b2aad635a03eba6b7c4b7445547f402eb8169db2e7f1380220468e0f18b253914459dff13066ea646405606c104152700e7bbda3bd8ae9006d6e2bcef7b911d2ed3638c396b6c6a7d4f1cdd317eddd727c22e6cb75a174418650c00b3cae44d933ce2b0ed862ae802d600940ea20dee03f8403330ee887fa595870d94a3da51a6b9a97b0e00671ccacf2c3d8c6f0a9a1ca97729fc88b9b497e24f06d2e342399fb8929fc009f68e4b0f4dfe1ae29e7cf3706fe5547203c9ff04587deb0d5b8c0a3a655c58ec4213f5b4e55df54ab74be0b7b1c2168213c6d9a2249f508e00fb0baa19c7186cb807fd45a102b375ace99023b2d69c692f3a7f8a5fcee93fe91e59dffdaa82a803d7c16fb010c1d6a0fd80e9fe431f95646824ae5b7da206bf04dbae101c5d2825aea36b20ebd00039eacff44811efe7ae1306b2c2160d3fdacc3fe38da91c8b5c1c2172d41b67186fa5789b42f21cbec462a8b4792fbd150f92ec7eff11188104170a2d276409460f8dc9b2727268cd7f40c28a982eeb83d3f0d3dccb15da096f577d9115b05db61bb6bf21b7ac1925b1bc8e9a5f5dda0c2ff5e6fe8d7a660a04a630cadc4fa38e6b25dcc60cc6338e9c41a6c6e17553e63b7849676cb30af4f3c701dbe919ced1fce84cf12112db393d256fd97c2ba697ef29681ef347bcdf527b55fb51b4b79585a0961f303e85cc86cd19909798dae3e98d54401cb1d77a240c66d6d4ebe5400acb271a5f97ecd258e247f7d0436af91803d097f6acddb1cc8d9c49f0958810cb40f1e9faffd9f5033aa8a894747cf494063040dff02c6f9cd96ad1e9755fb59d60f0dd9fb89d7a6e08898d8466c4e7c3202709216ef564f773a5bda2320d710a8de256db11d3d4af74ed55680731a9b074ff2e68f797480321db9795f68be48d5b62255f7d8abd8bf235b0a4b21833d1dbb23a7062075187e9bfa016ebc2ea36163f65b522af49655c65a16471bc85c16f0bf21948724631c9555d25e6449d1d08902f8dbf17a281ebba14292667e6d14f76f1218c6cdbc37603acf744750e2cbabc332a50f63ab630b02f1743050d2be0dfb52cbb4bc3175d27ba0b93e5003b3de5996e2baa2d8bc0b723421018008daecff5b9b5ee03a32e3b4db3e612bf9c10cf8a078bfbb0e4ab51fea8ee4c08653c79e7187f27525f6ffd1b2a1408140e834a876a709f3e37d34b805217e38c7e8e3c03300fbbde3923bfac1cbb475f95989fce9a06366763adfab1fa74b2f9392ce66df309c6ded4a7e995a3cc2b899c900de99e5086c402f657858b0af879f7896b7574ef0afc3267b7d0eda5fc330b30d862a9b138a0c8ea1f7d5ce2eb6f02b1d2e4cc7765f3c4cd12d94bafca388f8954e29939d104f08e5b04db34de0edad07c6be180fb517d74cdf94876ac76cc6a136c7ddabb29faa2d7e177ebe7d007751206f8accdea026b8612784f66221652f22dd8d51b548ad7569b3894ce2a8fe8c79eeee8bb3ff21d236ce0bf5867ae7ff6c7c88da6aa6fa1b16adecc666e92058d50759a13c7fc71cfe94c2a1740ded97fd10ad743de3b3ee07c33fc81dd8046fd6897c64cfa83653a56d9ae2fde0c4b7d77c37e536fcf8f8eb618fb30c4495f2f2cb2c8bd84ebcbecb7db8623c5bbaef835e61b1d56dbeb55f5f8e2ea385cdcf4d6eaf1f58e0ac864b166f6b4d6fb6e5bb6a31936f97b30b1769170700b9bd4278709ef19e64f4147f6881b87e5dbfadd7c75abeff58642d1bf9b6ae572294c9702e4a396e4b025888f39ff93e356ab87d1f3eed062ff37f6ac2c2899e6207142e80360f0fc2787ebd3dcaf6af84a198850214599bf51142b37046a43b48b023b9ac577fe8fa808e0169915c3d336f4c3b718f48b42c353b0916c79a90f836987696054fbd08345ddcdd77f19f6b5d323383d054df554c7c556fcadeb7d1df58e2e9a8f9318789f582a619b786687ba3f174df2c1d8437b4db23979a94ee1462761584f9cb4bbe78468c2b38fd0d18091f932371e1cf879cf89bba1a5b83c4effc6507ab8ca0e10146240915de6d55fdfaea794a556c9c160db8c1642bdfa78d177d1e404152de1631d18eff38fde80514339396d191b1083fc0f28a3867409115a0473d467d3fd91b855ed6714793dded1966b0de620f58ac917395dbe197f937ecb941a879aa5f2ade3ae8958f89ca31fe777eb5c1a138280ca669b08f5028903df33f169ba9d30ca6cffb765f73d0da4aa6bd73f8186b3c984caef09832cdce24f90e93c33c5d673169c304411a1374a388441c1bfa40c9d46f501c01e0b0de64d79fd6bbe2c33bcd88a0f6149b4f83810e363fc07b0d3705affb54b02753f60bf73612f7db983c0ceb20e717b94e4b466df48e42ba1eec6453968a32413d6004c3869b630e9701c19c6d8f4ba67a9bce6b3c1b47c28edf6d33df288d70fcd8d95cc26aaf7a5ebbd8708bc333b75fc3b3ec9aa688b57b568e7d1b041712b866f507bd8a59eff609be5057ece2ec9162432f8d8114bfa1bd42426325c959f929e2f79894bbe16ab133379252f51956d105d09907ffffda274adf1af809c23acbc7ec2e547758a03fa50e4d8eb64f404be8a3a369df9dd1507fc21a7b52c13deb7ec3ad52ab2986c7660f40c9bf73a8c42e6007ffdf8806c7f8151a4632c940f705cd5dc68c75774a65490b349a5e4563c53af52b34f3c9cb1335d78c38081fb667d9748839c50a58b5b30ee4f56f0bed9aeed6e012be160fd5750009ccaacfba58c78485e171c09d46f2d0218731019db0d6b9cf85bba83eb6ee0b8f536a2c70811d297d7622692b21d082e462fa8cb23bad8034d5ec151778cc991464c5555af2aa64087428b3da64bf94e96fa53480198746a337bdb057ef08d71d3e19803dce0d50b2b531e98b7feb5d8c3d20321bb104059e0c055010dc087dd9ee53c345f62357de12649f77feb2054cc82aae062734fe74ac20ce21c3ea24211690410994b022f9e8cc5177e87ff8d43489727f65e5aa04183e48b77d1cfe942bc4fea32996ba296496fa0fd3b547902df175ed81437d84020a28ac7d6c632145b0842ec1e9c1b1268a82ff79c90a1c26d322806f4ca4fb7b7e997dc2093cb34e73092bfd450ab6ecc136283a0b227b5b4f1bb93e3c51e5753bbb9480f9355e12ba2a8d06f5bb6d8edadf123ca1fa0b04c3e595c2ef086c687e43cfb9aa5f23f7da4a96ca0fc3ef086647c3891652f1a840643ce544b861d505b948220d8c127463fbcbfb8a4d617f91b737c69ac6efda71ab7c392d771efb0e49a928d0574fd3c499a143c4981110d8a00b32c491c942165dd04c66ebb9db51f0a435ca7544bacf93b30ca79137c93342acc6dd32ee0340993da5b43b6066def01f4261e63df6ae959c5ee483a1231348be80d3ca7aab849121023e43b485db30e5f849bbd2f683592711b3617367a7f77f8e2060772e3df683bfefbf1c4869d5ad4d88face17702404054cd37d4a59079aadbeebee659753fc849c7bc70b361fbea73353408df0798126f46b377fb10274d1970b37262ad4a5de70daf18a39af9be155509f5b6a3fc13f189af5eecff6a286a113cc355c757efa7f9c8ee27a7c4dbe73edb557a7682bda86524d48c06efbe41d909385e13450a37a584019edd54190eb9e813fe2f799ddaedd2fae11c73d6d563d5d8f3f083d4aa3b6b8cead5327bb3137425162e9943c9e873735f5ca8eaa94c9c21a08ca06800b00677c53558e1a9941ceac37c0e5ddb0b3da749f802b3f6abb7e9680dea5efb847630ee35edfef54cf7217618b18bb535a203593309fea86909f25e7fb52a657174fd887082d24c5891b9de5abc466a1867fd2576ba3c3345ff4c97341b743d2c170780f7b5609ac54c9842744feb487f296c73729a1b0e25aaf8698b1cdfb78f0d783cfc21fb9fde52a174d39963545d1529b6b2833f0ab0602e664b6cad6ddccdc5d9848fe84cee36e46650eadc2c7102a6b322332cc546d4beb770cbb3f854bad1ba8b1da8be1f1dd5c6a4ccc01edddf737055d2a4fc8a3fc6ecbec835fb59a6887917e0d731e59e441c63dff51af2c08bfa6f2e5d049ffd7875ac3ff4678fa7ca9fc603ad01721748dc786aca7da9267bd2de830dfe87fd81998c8bc6579b95f20bd37bc610a9a23d773ef26686d37eeedd778721a9e4a12f1cd3fd4e3bead359d44950691c8b8a87c730c26ce3b8d98e12dceb0634d7b126f022ff4fe7489805065575fd7bed6c07b6a1675a5c8bad8a23d580ca7625ddb826ec487a3e39ac0982c9f65b422f0c1e1f6045acb912f22573d6db9b16967549b7f8c90b50f485bcc463ad6d2fb6ad9a152f58f95ca9e8ab89999975e499a557b47c8e4202079cec9ab11ba71e8535710285e6f80448f05df304d3c44f6afd50f07c437dc32c4fdffb928aacbbc8255cab3a28887765698de058c0396bb23309cb1447bc576525cfaa1fd2025957fd09a2e67379f31952c75e4dd3b255ff878063de5665b54bd52304500450bee84412f8440aa0779b9d42d1e8439cff0bf7a6bcb790befa720c7562655194384bc505acc57e6dce379db50e4f7f1bac693eb415434c2f2bfff87bbec59d370ac0a20225afd3d8b1ec84013f7238d3780f94e0a68fa7a225b6aad2ce2b5cab79e0597aea9ad7028df8d2292ef333d0b1a5f996a43338e6a850d857048aaf9d25a10b65968713fa91602369fb627f6b9a7754e52aae3b73b523eb9d4bddc576aab02707f0016efa3bd098242db2e1e02a7bd45c985aaaa9a49f9939236fa98ee2f07f4fce901ee6ff44d35d3fdffde7eb2412bc5fd41c1ea8149967ee1a5f893ce7a11996fa767c948ddc258b5f0a2631e1051092bd053d5224655270899829b2b3d8257e94a53f259dcdd2cf3505f9a0a704ad5e020651cdff04e48d5a9b9188a533e8ef52f80d0002ae38c9a8dc8465eead50e512a1604b492eb05e63d04b6af08243e5670c12de673d1468b68e97949dfee7a5fd923d8a4241f7942ccca534ffa5cbb23fff44d107f6a2b02e27297be6fdd642a1ca4e9ac01c4e04fdba6c1e6238137fc037d8ae13e7661f5dde6c0435438720b5f2a672086104e7048ac21dcc41eb49bf7adde08501fb0b1fe8f545a11e97b24ec4742ce79eddb802c29a6eacd29a94dd775a90afe39f732fd146659e4a23eafcea5a8f34778650603723f1af12f6d64c998420164ad4d639aa8c03dc0196ad2b3b3a032ec7c18c5f78156fba0eee50105681f60237b200f40cffd015df7375dde7c9a94817b7d53d68ceab5f4e7da8b30ecdc569a42944633bbfca65ec3c754efe565729ca88996c81f6d938fa7ce2dfd799958e466f0dc6194c6bcda42b42e5083c192c9a97bb66a8f60b8d5259a4eecf71a19f8cb214ab105f6b80f9d23678a35a867e22bb4f96bed6cfef82fcf26db48e90b5700e42f78c16770a47d9abfeeeb793a87a05bf45c465539e3fe648b60d153a4d281117fd3d2bdb975a17d9af59b9c6b4ebfe82b99978528ce916838eef16b599101d6126e63ffc7d2016a43d967a129df2595b8e6b4a4ecef545526c97654777bb2821e20658e328d4ad4b506be0fcb5ca2113b086d3a5af666bb8923b2b4983cb37e91328b0fd447580303f6e72071a4cd89317700928872a1650a8195e6050a8fbd9f4e84be8bf6aa0dbd2d34c50af432b91ec41fb80369e15604f35b672d746e96d3fb010b02178c683ec0779829e83174832d4805b1defab92f9449aa963ce5fb9170f8feaeb1da45f4b11487f9adda5dc03d75e1e2f30ca817c0e07e9e0cdba9e14dd4afba7189ffcfd385e9242212823e0e4506acd1bc604a5a73eb15def23e15e96c688e008803fd4124fe499af3e1a3b918e9a135d5a8c62e62b7a3198b70293780d3be1f3d1cd8e791480fd67a79223445edc1f4f24e4aa254be0776dcf3679781140d14ce6a3b457a5af4c18a2db76c840ab3b096f401629843627a6964428f368672f06e216b0f9edca5a75696f8fe4f70961a63497815d2b9b1f56cd451fb401792e6a2f3add102e0be772328374b018e3823cd1285cf7f7e0f6a3c8c81220eca3297734a86880ac8d8a0a9e17f66c8c153eef8a595cdf738c8380e44008102c36e53856e35c20352bee4dfd8427fb1244d1f22d175809ed878ba35afadf45fc0334d306ae38c840df8352504fb32b30b87183b9519309eee053552a1914e77915885dd35823dc1ae26dfeeb6b43bbc7c52134fd8a588b56b5a5a45d576ad45848257fa70fa009442b38078142c378632a823b1f3d55829e8b6ea495cb5898441af7ee4f19979ed1c5dc1026e8b045a090cd1c8e2bc1fe15301481cdee0d5bc068f4c2298055ebab7f7fc9971ca4ad053c1482b2de87090a33da7293bfa1cdb8eb6fdc67954216333ab57db906e349439fcb8e4a36695feb09c92b2dd90e77fe8b6f9c5d1094cafb0a064061a53d474763d97a61e8f71b9494d6b8c34a213ba8edff9afe3885727c62ff2cadfce8f9263a6c31aadaea81e3551ed2601ba23cf609a7cc60dd18e7909246a90886b507ea7231cf983b58f47c4c17ae8c984987bb21991c352707f95c13c30b37e530deba5bb4c4c81ae0bf90adc015f7ed9d0b10fd9fe3cb63f9ec5bc1af233c78e23061b433e1865fde5f2b2d5b80035b3cace68d0c317ee64f308ffc88f337074db1dcec1322a84612f8024c1f68f293144721295a369dbd26ab91b574b7380f3c739ec232af3056b0c8de2fcf9e5177fa48a5a998b741e5ef599730e8ba3ed472534b518ee60f942a839f8bf173ffdd834bdf232c28f0fce7cc8e573bcae2bb93ed29e8ad4ef5bd0516c1b7878e4e47617a9efd44f9d7f89be391370b1646a28942d991c06d2ec4741be470a56fd8308e335616872eb9bc87ce68768b36136bc03472ffac34855123950bec0deb00ddbbec496ac009f5fce0f728276ff808f896576e46f8e9e81fc0e7243a2079331c6a7efd1ad77ae1e9cb4d73cf5fa8e11118850800e2402ffccd10d93a8f79c291e30f63de55ab6a99f9d23b2145df5c15e4ab1bb7b8d76d4843239ba1af706217e24ceea5a89d35265f0ec79718425b5ab97a95a132c8cdc6ea3a8e86df83eb6f9e314cefd5836b1f109d6848714b6d42da057aa451318202a2ec1e4c6645209b68a1a126b2b4ce5728bfc62365076bdf1f6463239dd5bef385e690ef5c66fa3e7d3a89041812c495ac10a2b5811001b838918104fb30c63cd36c66a1815ae4a97de64ac0eb41308cb4282fab0a773265c5b2f25f976a6780a11c37b3d6b90f10576277010357cdea65059d15e206d511be1842b626db23285d3a455f3f6a7c21fb309ebf07c0bbf4b9785807341ae3fb5e0997f71cd5eab81d6f3660f2b94c5505a0bb5ba362d0d94144ec514aeac49afe106e1b49cda109bd6a99ea242379be096176a9ce89ea02c2057b7fe82b4a78e22a67f41dafdee9c17d80944c6a1efd04f93c2499609b445cc2f71cc47c3948bc54bd81aba3f655931db7d113dc7b543dbcdd3a0d23dc5aecfa166e597dc17b8068404197d25847467ad86b1eeb5e7aa23b74a3130956e9379e9767c23f4ae24dfecb28a975f2fbe956b3134d5e48d2075297e7b996ae971c3c04e1b0041bd4d02448cf41df5ee10a955a26ec671b9c915321a3b5bee0a4244a61107638c893a8bc0d739f06291f5107b356f3d12a992a66d45811b18f79bf73e80f3a795d86b673a90de14d3ad01eec951d87d834dd63349f5e21c321a7cc091f349876356bd0cfa0e8edf7d9541944f545b6f5a93112fdf317d2efd7cd38df345d63e43546b0d4d53e0622a6f1d4b0b8eaf40816b8440734a01372863fd9ac9ea73fc5c7d09f5d219acd83105fcabf32ac68db1626e9abce608f35cc3d247e6e7dc7e55f8bc54727af9b498b7d9e769578d69797e897b4c5a1eedaf0c3f24568c3934420a8b31443ddacbf2783d36a75e0e17f760374f156178620917137cef7b0e70f9c668edc564db954f8519d6f0fd6e8b9c9ee0f23e97d6223e79424222e17cb7000a47af7267aca7b272aaeb88ab18c3a560dfc69a56f1dfb5aeb53bb798d317b88bbdf4a128754153c27a8d8ec07e33673a8734168f719a103b009f371ae27f987eb38de869e2fb3c5a2a777580ff896fd64e137e98c3e83fd49325734f5842a157e896c2a5e67c4f5407ee9efc56fe6956e7036dd2eafd1c374a682dd0a5a91e5600ca338f2fb40603e379da30bb80af11438bfb89c8ee7a3fa09ac7387596dfd51c378bee544288c8c444844b3085de144f02ad5ee5407e4a4c935cb7fb8788309845a42d6c10635a900f497bf76bae709e0f160add86771030373ee4f97062903b0ab6ea90e91b96fbdda3f4b2d1d8b8b147c6fb956538c21c2ed8382e92657754ec322b9504580e256825710fcdffe762770aa0183bade97a3c562a32166ec7d22e2df6873582ee275173871bafb40325455df7f2a264d61e01638a622e2d1572a59bccf8d2aad1bbb683ee89404d0095ec081ac96491b88bad80a1de34a5bd5ccc2e9c412d2f4e5046844bd3c0f710c65a416fe4f77a4ab95812699c5896743bf0345226c1a232978560f6f92a4d8cd04fea39bd22180f261a71ee4a1cfb9db72bc47eaadcc309814d2c213c484a8ae4a1a5ef008abe68113fcd453fb2bbc90aad9408c2cf928e408c3fb9ef2092a582e884a7635e7cda592e96bb7a86545800e176aa780324ed5cc73291610aa0764fc059f2a21f5cb91508aed58a26f08656566891291b3d97aaaddca8f91ae8417502778fd3139cf1480cea826ab43ebf2a71437b2a2307f85d8ec308c62cc19bed9806a5ae095afb5293b25c70b307d7a84c0d9a338ae3d4014f3b1d61f6e630a8237ae7bf4601d930d5f3a07a71194b9e326a2f7813b947a2c2384fa714455c4dd24c171f89d19c6fe1bb0f7d3c3214cc0ec71674c8673192971872df4400f71a1b9e1d6a792658c453c8d8cb5fef776c5181e665972f0bdbceea35dad503cc832d0d3cd451e3ed61359ee211af7e095dacfe723fa89808c0ca39bfa21adf87d0831102b04fe1d04f9af69b899e0032c4cb11209513aa565ce64a5e7f82c6cd641130feb830bd4674de33d8d43ad891a90afce85779916117ca548f6d0005b1ed834d11835f3ebe2f7e1835b17879cc07594e65ff75ce9a3494c88a83a7d4200d1b4c304c32358b8a66c6ab35f964a4f2246cfdf2cb88d70a2a318bcfc358ad7d402723e5d273a33cdef113bff309bed509fedb06ae8143b592bf43a0c982f007c9dcae326402d8ec8636fef138abaa78557b69520dd589a70b38c1bfedf06396adb1e435c688a2432981c19129cd9c882950f1c0beb5715ce1c74a5760e055f385ff6e334d67e945c85277d0818debb1f9ac4a6d2e9dc0869664391898cf792d9ac77408e645e9927fc0d69914f7bcab27abf75c5516cca66101ebe562d4e28f5838b373098e6f4411f6b850686664b8c16035b6529a4aaf130bba3af7b6af05da187c4ffcbc4690b634386124cf1c0b374ea4e6580a9cddcb5033d5203c4707f82379b582b51b4fb219a43d9929c09956eb058306bf23a98c4f53b3945ce114a0dba842e2c2053b544ae6525432c395c9dbe9962550eb18e191cc752090c26631b06ce69e5bc575ac38e76b470dbd36390cf6fec7193f7c7a28efa5fd706819a2d41d2589f2627beb4ae0ff22b2ad75349c62d93004b1b84c903bf0d00aff29ed9d6046e21220095613a70c1c4fcd9f5fcf20c2898d88a70126a872139755c38fa7e45a76019a250595378d1e779eb21aad174ee2ca642915b56657bc2b96ead4ef2d62b40b1a643a88fc4a9d42a826c686693cc6b4d064953f034a277d04f01fb430222ea6f1c49b482adf67916a8c94c4e198c0fb925868313c34403a9ec22f5faffc8e0df52ea01c9b343c1a8960ebc8359bf9dd823ae28541faf145976ded17b8d75f8d2b493632433260e72c74cbcc79cafc5b323b35499ae3d537c8b66ebc75a41e8ba598a3b98cf621547d5b331d25d0c7582c17778586bdbbd8557334620f0b01877b20e49f7e19e870a58295f2e8cb48e35efc9caf81e3ec0989b62f7cbbfef87d979ab39e9b8940153aab52695d578229f8e61cd7a03e3b81f313fd931229002320b44a75445ff960ed69f07570fb960e1cdb00c7f34189890f1da1410bf715fd2e4965fef5c3d2a183edee5deca37c97edbb6a213d99a8610d6c112d26338550a789c16069dd1831e23786cf66e2a4a0f1ebaae916001186bc658ba43a2693897f15e08989c0e67dbb2920a6c19be7991a09de668e24ead41a55c1d7ab02827c2b04c6f6c5b951a88b7093316737f21b6b52ec9a7abf0cb9f681486a4e4fe82cbbf18e61e68548a8a7a624b07cf9c51ead2ed072709d0801134b2f25a762ed1041bbfe65e92c19b70276d27bcaf3595844b54d924dc42267ec3d75d26a2c1e9feabb10a255c32cd80ea5644c88bea97ae82cc102c7e1592adfb96eda06597da65652e57da5d44a44a49764543585a322184fa743db2f6d564adf430f6e22599875128558623d00fc36047653e770b685ad52fa73c6b305865ddd7f77e89bc6ca6361a6aac50116e67689bc36fe0a73fbff52193da8bad338593602d31423a17f722b71dd446d806c4ce0926e9fa23ea609560d287244a5c62130c77145bcfafc5811eae476f2dc4bdf98dca7d8e68b72bf8287d602e40fd6230f4f9b683078705d7a59e6becd0943f0efbb9f15f1069b0e5b3dcab0ee23b6c605d45e1fcf78b8434f548afad8d017c97ab599739989c21fa6ce9dd8b882d8068df8af6297c36a5823abdade8889f2317764d9b78d735c2c2c3e0036fc225e9bcc490bd49c0e5cc29f5c2345e20bfd316a100445a377b921a9a2393a941b13ebbec2b729cad1ca67dd92bcf686ca40748c3392398e677102a614ab1d648ce98dfb00baa34bae02258bb8e1b2b47921819f211321b35a8d8258bb6b7fdf517ee37686c9d7a9a08604cf781a085481691e08835734ccd9505cb3186371d194a2dbcdf7b5901d04b16042503422b2545cc2b1f947074258f8923a8502b552e2ccdb7cc5073afd196a7b371f857734e1a6bb160d42871e2334eba8d30fa407a91d8d20b9148d8ed15da63951eab36a5e07f52881fc394bd79dd333e01c1e708a8552177def64f9cac1285439360e4fee7dfeb91685518be968cef8d1c1f045fcfcfe6922893d18eb42c910d642894ae2fd659f2db25373908af79f4976b6302c4c057d7da6fb3b8f7d31b2d8c84d1d1875c197bb4b48b358ec688384fb57483334c3dac7b017a0b93a4dcec967329393fd7158aeb5deb1e7373dcc48b29f5c5b4103914c4af662f45c4cc562b6ed079f13060164a2c9e61eb2ee8c5b6d74794510df1e74b0e10c14189a1524f233fadc336d160be3495e46570a41cb9fbc5637fe0ceb84b089220cade5416862fda4c5d324f1a8c1888722cfba155d218544a4b940b51f7b7bea6464d7df96f6e0fec147a6e714195cecbe7cd45b639f3307ac522615a9ba2bea1faaea58e73753d1fb2b8b9ebbc0a2ebe3d50503c8b12b0f4f90b5321b3d3d0c71a151175977310e53aef4defdbe26d7b7d67456a6d338f8116653ca1fada1f58e4b9348a4e042875096ebc062b4ad67259ba83d5c09c85e6dcf443acb2c52b159203af0a5b781aa6bb95504fb77948530230f7439ddd01d774a990e925c7f76de800baf6c685a219836ef8a1bc4abea0a218301af899a84172f601f49b61118677a5b07a4f79e58020374bf25fd571e6d71b1090544ffb1aac1bdc005766f7da02454d71888a28dc5a30a94d0afa5a16f6898b81d93ce01c88a2b2892886b823f16c4e8ca1de315696330d1eb7101a6bc527724e6ff2f6fc0df957a36739a1663b984f52092a150391c8b58e20a3164343830ab0f8e843f1861e337dc7f5040bbbe052d7dc67d6cd62d8a759275ef00616d3142ea3a90f6496d0702d82078396157b9a8b3e74aaf53fe13873c61d9f8dd7d73b371848134b41a6d6c50c0f0db485fd3d971ee99c5c9402041278433e3b03a094bed28575f92c1443590bd835052fcb1f56b2a49d4a662dc4516ac5f83177ece521dbee3b30c2c117ed00751d3a14c1a165667eb0841fe576b69497a9a1f2d7d40818f0ad1ef2401b82c16c874190f596a12c9b743df1523d4b62dc5f0334391fe03774371d7066bddfd4304c477d046a2ef05a4645fa4801fbb52016a2820a8edd4bbedbba36c7afa2be2e0a60ac8e79ca89f8b7550248e4e1133904bd050167740b12bd957e7c8fab15dea86bbb1e9c02d01a5533196f2581f50fc6b612aefed82593bde3e6c479b6865180fbf47622c9faf041976ca291c76ead7bbc34c8d9292d5f58b04ff14e64081314a3b68bfe16aa649d7d315c244ff604092d93647ce7517446e2b1879a2c83246e13b480c159b193f9a8b79f513935cfe7360948f1433adb6e9839d33f93702a7d9ff22add00ab41a0ff4f35827df22c3ed28ef98f45ccc189aac72c2fd45345ce817999e3f48defb81b267f769fe5e1e401b481f6ab21a65c1c80bcc8bf68bc9891c5bf86782762ea5d948ef007af3eec26df7ced5158722b7e1732c96f19cae559bc07d967868e1bc854920e76470e6945b99812b1944ed5173651b82ac2ea413a80e6f1d7899ce05a8e1f3137b200260c80a6a3c682b6495782c32f5546e1a24a7e6382aab8209ba9e60d3f3ddd835c5924bbe919ad5279537d4823ac5cc5c7e9e68364ff2a846450d53ad1fea9d8e37783a2b3791c4826c04f8160d1ebae25b043aa2807da63063cf26c8cebcbdfbe66dafbd56d9c7428381f3ed293060bfec782a62c49371217546b7233ef4827812f0156f150e0438bba0021ccd940e8236fdfee6d9421974499f03709937b7e97f5f93687d2965911f3f32d75a3600f230b69e2c62060b78b6da16c357139dd87fa7f804d26f9822ff9571ccb758c0edc6bb6600a92e405c822e5c61dfabfd9a825387240225a1bfb002e004462fc6ae686bc6e55136aeee7ac3987fb265bf7ccfbaf82ce2cce0decb7c1f03ef753bf4a407e52f60a15303797481013946b41a199c702cee4182c065ec1c7309856a031c462d02d2c21a2ab45b58fe711eaa024ff61b53f7bb6ee05141c03aa02a5f952e2f8b96c33149effbe0ab06070cb8dd959a03e7f0cf49b48e7f119ad2cc6e7ecd81ff2362c964d169c160c5a1d1b278456d3d299c451e9b8105b948a050240f6534a2e1689ead03d93dd50603716f1fcf292b733ed5334aeae27480813f05387edc9f51631b537ccaa3bd1432b3f2606593c80838d50c34cfb762c245d1cfea9c22af7976b3718f5bbbc2a0f966f69f8c28749492e676f38ad7e141e9611b025206ed908cbedad75d8f4e83211c1ea4016cf25df47a28e98afe8b75c2fd7dd65ead208fdd97e00bf9f367c3024d14988d4161302a499217d33c0b787fc31147879f8b276597b7e454d93f098f772048b05c1a257be12d964885e1e693781439b330cc68db6c994a6a3e53eba570ee3af10236ebeaf465e1f8d874fdaa1a0da49b12e5ac2abcaf0b2bda2546a9ed343662595f5c5a4630bab88368b2c03f680b8b6150fca38135bd2c08727295c1751fc3bce2f0eec8f43ccc000a111b652d3137245e8e4d4f1af5f86103e940b2b23616415af198987f8c5f37bd19075c5454282e4a2772e7082b84e87822c0866e5061c7a836ba8240652312a5d3e9971d1c01eb90edd10cfed13382486c96e738719bc604da0b944c3eb479b87e4fc4042b54b7a6719b9b89b0380e6014ff64efc0a74ea453484a53719ac90997cbb623ea6aa1171c19f92bfbce6b1abcacc071fd563277cfc8d4efc2c5706ef8f0bbff27a7776e647f1f8535b446ce46e7f8c316aa30c43d82e58c81bf0a12b1ff4683b149eb456f83cb7e68b7c71afdbc7e5d1e847aedfb1f431c8775ba99749b116623625c38ec05b97a0c0826ff362057b72c192caf4f60d8bae3085e676b74d4098267889864f0858722dcb8852b9117fc6f243a0fcd329a806d9084959c91c07871f29034b9e4ca91d5ab86450bd6091d9b46bcd5d827b077ccadc900215a0dfa1aea7051e0af341c046f10315c09359a9ef06f3c22a2f05db12c4cad1c987f52c6f01e282053dcb47899c309ea60a53bba1967268ffc519c2734a5d45a64f0cece29972bbc8eeb99c5e8d426104cc50d7e4bd4e445ba1a9d86165a0a2d6cb926989bc4fadfd36b1db3b90841cc0018e5f19706d9310c05cc38c96c6c5fe542b365062abe98c4cdba97a5caedae8c54890dc9c07d9a7858a6972699d89170c4058dcd0e285c80b81a3fb501ec0095132df80c347c87da3158cf581c813c721d74d8bb9cf9d00d2beebad590375b4eb237c25169e0fba5115aab3e051054e2e04f6f0e801a8e455704c330a571443747e706d41238f06f2df9a423dd72ee88d8bc00c9db68aac589ccbd94dfb18d05afe5860dbb753568446054ab8662996623e99153bd9594c820095a56726ea5ef524a267833862dcd29eec93195df3d9ad8506bb725b0f074731d5dd3633808f86a023917817681b56d4ec4b8422c5c41a997babde560b70415989ae9eda62f1e16db2f14a773bb4f794011e5703a7119904ad626ca7d0a00b2edb32f13ef5e841f18fc2bf12481bbdbdac4750383a7fd9e6f25d77e4124c980f16fa7a16d1a090e3c650052d093cc083d36ed6bdd422318bcfc800411cc49bffbd1fd31e370fbf2f8fe1ed5b4f04fdfe30c7a7739a488c2337166855cab9dbee0d60fa1dbd18a55a3d6e4f782c88735a5304b85b33063ca6a8a50365c676ed8e3f32e7d0db3fdd61bc8e521d7ba53822b8021a32305de1d191aceddb8ae35baa86cccfc9e975bb35e92d20c7ad12d93b71258644d88a8702ef46019663c1a1d8f45f134e448166a4920d7454ea81691e8a0ea963bb07d06a77edfb930ee3ab9e54ebe55718dcc9723fc2b656e98d0375b6e61a82920a185fd65d0ca3243b853a66a2436e765352a79043a07507cdf1ad5861cdc848190375f607748b6f49727cb6fe22c085fdc39d16c0df1cdabc2f648d6759b006f8812e44f54605636172bfe7c332f036bb94826e639bad09317a5f4950d3bffc28e05ce5630511e92ef15c550b25732286c339f5cd4b339251f94b4c7d6140e9ae9bac0b75b9c58e7686bc38459dc1b842f8f9adb46e52000cbe778695a8c763b764c093084ac41ff3a43507ceb6cc2752b0c10e158cc400b41cb1dabdeebf167bf6afbb8addb5154488c935c460a90aef7fe5e84d7d300e99bd3512cf193c41eef6756ee4f6ec0fd9de4b3924c20a655cfbabd5c5a3cab1f5875b8121edf3441eb184f231b7c5303c1d6f44dc1b0652d9ece5559127eeb896a6589cf37370ae1c66e28e7f494af3e7d2e3ecbff0df6a97e701ae9c381e28e009f342d76db3b18a5f653c176b3ece6800e65f5773da8649425279a3dab06a98557ce4835d3de4ac17ca4f0a2b02cbe387ef38ec86404030f83de4c0750c56c8a4402702431dee3a64871bbe7e4db4f581ad8abd7d436ad51ae49a473452b8c7c11b929ed3efbe6b1392159fe0833c3a641a2f7f0b373d4569893f3ade7612824f27320b8e323ad48f3cff46d982d552dde12bfe377554d99fe92f6b2aa6a810e2cc45337791a256b2ff5e7c1eefaa67ab72afd2209665b3e4ee849e818628796985e7194b7f8b66fb9e3ad23be316683768105c237f6d915534b7fdbb439844ed71740d85d4cf1e7c0fceb01fefd81e95059fb486f225966897b18d02cba011970a0a2e60032ede86be3657cceff49042211bccab7c9d53eef3cd7033ada5615cb9dd51f7b4ca8fcdcd63aed66f264ad6d489f0122c95eecc605125212429345cf758b2b2b442d6b340e94a703ffa80339d16df1c340c46c4ce6220389f2d9e15572e735b2276bc24d72c2efcb7269da624ccdac8446e737137f9bc8198cea3d5ffa413f0a41645786678a984cf3c0ec85cdfdee49d9764d602d3f0af780c27a4a7a9486939961083b181f442677f01d134daf431f9bc8286d74abe9d63f702cc5a6e820edcfb9515b5655c067607b0a366ad53a8264324a4ece5e45d00ce2f8a81ea3d55f490f1c411184e72ccd81bc8763450726e159714ad205a2c3bc8db08f7f5d9504951f29696855c8a1ba35cb38a807247215a4fe97113aef7b4db73941703a76070704da5d53346d41e26c57fe2f1de1ba7a55a3ceb1e65b06a171acc524f6b133d619c58b6d805a10054371b52ee8da5d33dda6e1ec850da70b95c29d2d3313fb5598f9c5e159c0dc5fa5e923e918ed452e992742b7ab310473e5e2ccfe3dc35a673ec3dd7891a78871e9ed2d80073c4000b44c14baf4e29a8ae21d33d9db969bba8228cb0dc985aefe76d7bf81aa10fb6aeb61042720105fc71bfb7f241eb4c8a63744aa9f4d52d1860120f42f38b4537b1e372cd36c728e7cee9f7f9184acbd74e69a9c3d3fc4b1f0dfeb50efbccd9572c25a0a040a36d744f860922c8b8b1032355bb71b0f741d307a497260b94c95eb655383f803842a44106b95677b8963671230c2876879c1b6287e0786ebd9f49ee1b86e4e8abde24658836271c35ddbbefe11ba3969afb890789b2dd146f91cc5ec1c3c6fdd31efd341be639fe8da13ca81fb766292fc9533555b1d835fbb051c5813d236863cbaf1ce8cf5df1cda3ed63b2840f41ce4c04342ddc5120e3b2440e949102d2ad227816b0a50d3e0badb39159ece46177b1be034de05385b803f690dad27351635857599e1a7e771bdd2b9b006b9b8e130b21946aa183bd3871486976010c3108e76dbd36e9528d884daf923d31ccb3b953a8625331cd7759676d6d25e8e7d8c3987b65fc50ce20ce5a6a74de8f48e2eb65579c9e87eeac58f8d4c7a065c1ac87e055cf75a140775cc1455f9e6d539d8a84d37f77591912c7a3dd88c61a923aa021b2543fc7fa4b876a935f3d8675123ad3b6eea7c0a85bb55dfc8182fdbbd28125d22481e3323a769ddb8e398feca7f4f6d8062eeec6edf047756eff6573ba7f4a6e0783e952640bcc129419665e345dc27b9d4b93de978855055220da55bf6a8431e10f852e2ee41daac2709965a83c589c33feaf2bbefe071ac7ea368370b89c1881898593a89c09c7454d9b415f608dbeeb20bb4553b88bac0a7a2e41c93f61150813c4bbdcb397e2def90020a08daa1c5e696146831afeaa7287fa15d75a90049d2090d478da473013e907d11d0b77e436625efc4284c69fec0e7a41f858a0fd850014ee1aa06ca4b5ad2405ae680cb7e6fe1f3327a5c82cc8b9c498f9ad052655224bccfae591ec615f48029102d1ab8a18278d761d6f27a00af85f932ba758a9919b664c757b0dad18f15cc66bc3977271b92a04252819c691578fe0186bb1f5d090baf4f21e013918f4835399616293feeb41760faf7c4b2e5d5a89cddd48d49ad941796ea0e0d796992fc88781f31fdcd89aafe402888cabcb30b9987d44829d41f29751af233b6ae54a0d5719d1c31041fbfaea9387546d4908a5899efb1dd0ad4a8a81a41fec7293a3a0814a9abda11bf00bea1a237f3ec4181c21591a49e9b0d80b4e9e08c5486d1be9d3f23aa0a72488b13ea3a612f7d073f5132aec63a66a0aafeac29b6591df76bb3a68eb0cc3492d683b3e709d8c18d042043131bd58a8fef78db26a37d158b371bbacaa9d78bb2e035a6a70d74df2b0de1864a391e3a383cf7d23ae88ece3e9bb0c607a80f5f98f1f053fb920ab74a719340d10521ae0e1a1085ccb53ad379e3c77aeeed7539c6d4f4d6a3a58767ca82fb9b9b709c8ad8baa98fec7e67c906734b13d6f15483e1c482e8cc610ddf16d3b7970e65d4ddbd6fdc6aaa7ff5715529c77a85c2d85beb77aad34c67959d9b4f77fe6d23bed55cb34920f91b9f4ce3c98a1b6c069941240bb7ba2b1351eeb3ab7f1ed3f7e5100363d9d0b3d6e8be3b94761674227f02394794158db8ff2583299662128c9c63fe31efb66204909a327a5796df2d7abd412572b33ef7f690cffb949e1bc3b3f3b00ed7fe0eaebb8a977ededf5c9b16bb9f8f325d458b8f4c6fff3fe5bfbc60c3d50c3b61813553c23941acc0d477ed45e2d51d1043f8ba77800ff1ff6879d7f8a88d41fc7008640e2fb15b5f22b88b9c791e26f1d09fb6ad544edf29c707f981ca9bf61be342ede5e41b41cdff3360f38a1934373c904e0a9ffd95f01d9c83e9c278fbd897431d2d378111c182856d0b4f21c64e9b4550e36516ad792070c08cbbc48f0566e83d878fd759b5c0309d653d6894729449f301a1f89930383e3188b48786cd4b609bc94164684f95086531a01d3c6a7f72c1bd27e66a4604ae178103bf860b1473e5e2315fc30fe22e8fcc6a9759ed6ff2e8a83b3a75a8c0c9eb482db5e3f43e06fc57239538a6c187cfa1e93bae8e3a0cf9e039655f446ad628bded924c06cfdb16b20ec7cda6478ce4cb38e7f90518cf2f68f03c9b59cc6ae7f884d3cde11e3f32b47c570101766cdd755041ed40f3b2a44c79334df150f353c842d2ef8025b3e51a424b411c5fa405313325c6e595c792c847f76cf99a98d3cc590af660bb94aac34b595b7e0d8588d135b79969238fba37aced9a036bbdbd65a30835411e1069201356798f5df80e8f8f0117f38df58d8bdbfe8685c0e0248c0bc9a84377168c85010e280bfead31fb48a2b44338efae345f45e36f3710b0c794ac88192c7c1fdeaa232fab590d2e33516f2e2cf1720715c352807a2649898bfe044a70731c5d9bb0f0a16f73aa66f2b106492b3aa41ac63cc7c4ea737961d3dd934c6e8a401bfff4a27d11e8cb5e53fa008ebf1dcb1b6806856bbc3dfec53c60dd15864f020a88a29bab4ec70b9a6116b01bbc8592bfacb9e6e5e43179bc7be919eab4b9276e35e85fe90802a08a8e4b882968cd02a0ba6876088c16c3e9ffe12521cc10a1265bba9a2f7322e04587ee8f76db172aea587116bef2b436a6e02e0d7524cd30bbe2b457f92b0cbdff64d81adb95e19d5b3a645eea078696abd2025b2283ebb442ba4d8c6007cdcaf4fc9e7e3cd460316c6c939cd17976ec3a18a0c7d2547966e3453631ff5e9aacdba5c67490253c14525594d7b0c7ca8f62fe830a65235fc2a832159c734a396a206c49a8a8872633744795e83625a2d28245369fb6772550e97aec0639b8440c87a46c1f8dac73729a22c85bbe65d28115102b675898e4324e660388bbbcab3dba53764871178822512685147f13a466ae1e1cedb318257b061c918c910747f04c2a7a6fa95a8edf2253c4a086526208aff5ad54956cf6c4310e4da6694b75ccf17ae162c73090548f42417db6892d269b810078e0ed0bea93e1195971c1272ad6416b811c67e94e2733bd2cc36e4597b60a7bc504719ccbcc2c588b10a893e76249c22d593ed33bc498252ea1c3b274f929e62e289fb57bd1c48c5bbd49584c0b983f71f08b886da860c089962c3e7093eae3ffe88980b03888ae4d72ffb3106900e08732b4a08e9bf032c84c48d198a638c9a8ca26202023e8754d689f55b6064a216692763eb44044ed6e0b6e4e6a391852210fc1ca61436f70c8b422a6a54a8d6feee581edde7ca25eb90b01382ccda60de743e158fc7dbc0e22c4d15477848465b735f29973d2e0217541665caea8f7d629fa7921fbea0135a4fb6f098f1ac09051563550a2aa71aff2c3c27d1bb5835bd686a577a70536a944919f860efc800f63a66336998c5d566ba36ad8a0352b32eea3da24d6e882ce318a0f2e4848cfc69a7d68aecde5737472d11e04c69daa8f6aebee7b16d060fe2bedce4ee4386568d025dfaf17364e9468e25a86a2378a770170778f6a856ba280870696c41a6287cd64aa89729d3b600c92012e493e28a770e19e49864e5b6d4dec31758659f0535d0562b78548065f9f9a9d780ed4c410cd887552c88f1607820a0722bcad1eab3d0fdfb167a511a7c784b01521a145a69c1e781a0749bed3acb68d09e5e85299dab4d107fabb1494a8f9962c339e49d1e8c4a45a78358d119d46197db9624093ae69e6c220122ae0997d88a15b262425b17eb9e2e3414c33adc4fed9b100356cd87b5862917d2f30d1abe4cea433658c405bf29d659e881e331f8d3137d9a90e084503f861927118d48d34a0926952e4abaa0a691c0c32c906ca3ff2480eb5878ffa9c6868a5c8b88c355afe78e9c93bb7603ca30731f6187a1637c93988eb621bac108c59b643115e82650c58e807e422cad6dc4004a92c45750b7786c02383086b3d22873be98548475ca7fc53b4758f410227db203a789483100dfa1cc284dd9725448d6ffae7828d2690cba24f01279c0528601ce9cd54d98e1e69c558210aca8f26fba3d348ccf62a4b4032ce682f47cf4703d24d69bf01ce15db6562264ff56a2f833517c0725b9ae67ffea9698f655c5947ea7f289ce6131778227a6ffb60c2724eeb043025f13b9a70154ccc45e38d196e8c2df581a25cd5266d4584822c3a962cc563ee08395ab86743f0a7030a31a31a42c0cc97aa421c3dac7ce33c13ae74e29ffbdcc8874b82e359576005e0b8c0417f4e3664662abc801b51d0601ad22e4407e05816d9a04f2555980ef2d5fcade4f08bcbd1147e9b3a7450ae5fdc430660c88bc8d9c533e63e485ad4c325c55fd538caac059f079f192c173a2dbbd939256e12ce09990d8a6824da0c4e13fceb5d539a51042cd15c0029f090ad957b2eca0bb4bfe4cb20e1987b5e65ec4dadd52608dc98ef2d0273bc07dcd3875c6f6c217dd8b6c3a68f4d62924a91c2116f8a8001810d64c67d3457e033d018eac1967d391833d67e5e1797582f01b7cd6e7ef694036971b92f4dcda8e21a0b5b38e0d6143522ac8873d7d024cfe98acb5eee4930fdd563f6ec95af4a4def440f22d3aa869620eba1e5d1c8ea3a4d29200055427dc33af8cf46274f888ae625136aa1b3e5c8aa083ccb1333b9ed35566736377a6659caeb57f1a0076a39e7d9b043d02499836e2508b50f9977508a15204ac18d70cf6b96d8bae7a2f7ff143652843120a83120305d4381b86e012ffcc8e60e829cf88d40625ccc51b5f5ce02e0aea3867c4485861855e5261a7420c6aadd7854faae91c7d3ce5b68cdd13f70fb79e0da63bdeaa64bd01e0e3243a1e68d84fb7cbd4144be5913e49177f11a334a785a16734897609d44ecae847854cfd06617d10f17053540aa7e80d816accb5026c8ff9bbddcd81e22d7d8ff5f3211439ce175123e7791653e6ef3f7a1ff4ef413c405cbc3d6c4d9d0224358243ea850f782f0c372b073e476be90fc2dbe505f567be63337fdb77059ee8bd032781af355ab65bd0f66a36bf5eaf65942424ae07cfddd0f009e856c4fc457c1aae74bdeb2d342338bd1b1ecbbc7b3e20b291fa1880e14db5f41961f50871322ff7f2ee8eee7b39f913378cbcfdc29dd51c5f46967b34455d3d31d2d79e646ecded59079a0a63a8aa6f72f5c7f3dab2b80669635b0e314504e5070d620ef889ad5eb1e3a5bcd51158db0d95d01cfc2b76e8a1b3545158c83a89cea4c373e3adf1d9cbb6de0e72077c08a9d6903fa397b5be308a5d3d4098b9d5b871b0ad6cc0b43565c190666efe4536395f4d755b3284dbd985363a78cb3833c9022a6d34d504cd0d44e183e1e4c42efe39d515cbb2bf9a6211ae324a3f9549bab10a01a0200b21540feadb22ebd34285c45777d81f57e765e8769496e3016b0957fd05d4f44bbee33b466a437b0e8136154bbde0fee0be375b6a180d3eb77744ac0fc9d514229edccc81394ca8323103293810687ae38a693963ead147274007275c6d08c336d2836b5990a46c6676c1a1b8cea4da09f7f2856a1385cabde6345e588024958ff997d161bd7b64aea4f887344a58a2e7770a6556cbe8fe9c3effa6b5b390b688cf0ddd969d9e551f30d350728b5a4b6f1ba3d6cd5ff1a708ba2cb8e85630b7be0f042742a31e4bbc117010777ca19deb146630817a7e6ec0c4172c98b11eb3919bfbb4b50a4cee01a26e3e04990dbf17c8010285f7e26c06fda8a9221f9bdbce7f343c32706e1df84e58649f9c3114818f41bc3874cbb9c6bd9a3b2a282a8fe56b9f3a536a94b3260b6cd10d67743856724beb7fbb6bdc0ee563b9feab52df2aff86a4be1cb281f607106d807a33cd5f920b409d102e58377314ceaa6cb54cc5da9017a7eaa76e62601ad8f275bc391a411e60ba06e1dc19b5f09de974fbf10ea5b42ae00b0434d37ff03f189685bb3a4f9bc24f706ac348da61806747c7d1ad9da77c58763859cca894b940a85d47f4817f940a324a0c21fd6880bc5ec75032c85a0ed06463e408d22901a7ecd4995bcb46bc2e94e87b098ebfe9eeb9e89123d64eece05ede4f51e9f9bfcc4d63ed9e4f1544922161460491c01a0bee3d627d04be76de34610eb9182bd628e2f0f9b742f675103ba4be214ff4a9810df1816d181423742999491f065afe206dcbed2a9710dc8c16240c674261d7187927610ae7a4ded9ca05e1cf973fac9d4b5af73002aa901932b9867b8c806cacd59f671decce839f8c717476644d754e0208d1cbce0d18bfa86c5b23158160ca784649e6c5783960b0ab8eb8e4110f505976da1960cc077841eb6cbde6c3944af516cf723a3c2c7176497d8f5d55b3290a78f9dcc4f45e253eafd511ea5e78ca347d9bde943bffef40d6a86d56674c8fb054b2d0ccf43169b86906c971a8f88751df8fd4de874bd295122db9561a24563bae7cd8edb9ff352b7122e528106aecd31d07a47083b4794b16814dba5bd217b0acc67153fd00a1c696c590490889a014d03e5b4788d7c5eb1dd9ffa6bd2282e2344b02a4a4e60321e77ccec1acba320bb6d1811bd8d6945a14baf2cfad70a5cee62816c5d3a2450b94103446373b07d746c78be8011a85884832dda356635cc63a888c80c0b693a99d6d35468d13d6fa6d0fe10540231978732c11ac87dac58b19a3cc7ba3527f8a61e6eb3479743d2a83152dcfb31fe916154a5f9182bf2bb21df69d85766fcbd0c8623b26824a5aba002406dd8e327019f64e65799dfdeb3ad219bcf036ff62de1b38d8a2ddbe7a4b40b0ff50b25714d67f6e671ec6c84b27ba8b42a2e725fb43fd9803b6591c67f7fdbf2eaf02ef33c61cb6b11ed877ad6c373fbf10ca3f7b92d4619acbea2e06ed304bf02a0f08344969ff77ef311f970fc9fb2f77a5c5c5b2d3dd89d2848c243e3c34b25f23ed68cc69df4dd41d47687a1b225c7f5fb7ffd7e3a86e678e8973e66e2f0d7583d5fd72eb896ecf479bf3462dd59689fb5a7f414c282f6829d6eb599e1b570391394d638d4bc603ad3201ca25272d640c62e04d701288295607ffd0d8c7bb8658cd53f5979138570473d6153858fa90ddf9315cd4844fed0cdd05895bf34f475b2691a6eeab89e019f101e6fa6fba9b7342d347f53e4f8d11759af5ec6d5b60a412f1d49f16ad8f69fef43ca6d0d9c84f6102a7565a6d5e90c97ced33f20360f71f7d525203ce4b79a3e6db4a3c727ea582c7d5b91cc9c236651a32017cbcda15f623c0d87f7614e6b888a7c0c78384af9432becc473b58d8ca8f8de87b8c69a57493c0a4c08319efb4313e7b91bd9d4bd12a38190d33f9534973a08d0301dd06b53e8fe54ddf03d596c3746d923a333ba58bf586f0ed731be218ea1b1689b77fa2f6973c5b101f152fa8f302cf037451d0808486393c3d06b98d62193a1766f4d9ec4e3843ae6f94c40da2c9ec0d6908a6db9bd9ebd3bcf9dff560e7795fe0ab2ab24ce3579847be7a8a8fdbf0cd96d41ddbb6fde64a76de6c9d3a73d59c3e6cf3330d561b2db720042592b29c17b781dc3f5b12918293b2133bd7d1f09d93e63f3f29f57d5661ae245c19047d834586b186eed742f3e1ea037b56d0145cbddbd630c6679c2cc40ae8b8e140c6706b9a77788fdb56f44b6b002211c7356119afd52cda04c6b92c5a613dccd349724dd0551cf45e29ca5e0262098fc4553ee142a26e93a0ed1ef94dd73eae94ad1006c84cdc98f23b01fa03c3e32d5413bc9c7628b0d8cd6f0803410dd0af627c2885b74624338c4e8f1fff9dd753c9c91a1825ac531057a78d0a5756bb2249333fda5f4f64ee0feaadfe1046f35b547f83f7946de6ad4c8d80f4bec8cc6776f8936b53cc11d45c5b954060987e43700afcb16f7337e6b29831302e2df4563c9e688b9c25cbdda5861a0fa021d6ebd7a81c92f4969d3428dba10e732b97e3f11e549bfdc25e4c4af925a0b4386c1512f53b7f4fae1787a7a0d5482952049c61a344a78dce1540158d1e2537cbc21abfa8b68efd47539e67c55c0890e784fed602c8155750134a3037e82d713a9f90407b4bac3828a0ce874ef50b5a1674ca36fc54bf630cb97b823fb8d24faf919e6db2061fe12df5db18d581bf4cd9b51e2175d523838408370f3eb93f5421346ec87ed9d529765502d02028625c6abf44cf9b795dfbf78cb74733ccb465c0274e90ccd6a1d7dd05a22954a3489ea721254bff7c4797a251a06b0217a3e29fe1d2944f447e0368511d125f4865f96c11699abcb7dc6ec5b9437c370ff0597981036292094df30bb54eb97d3faf3e5fe449d0259656342173c97e489448cf84136a86cd914cae24202a1c8f2da0b8161d46cbb9ef179bb38ff5ffc82e9951dac6482b09ee5d072c51bd56a38d0d51c827829b899053ecb7af42de639fd53dcb8a6020cfb6ae169fccccc5186782d1927a8b1d9c305481e52ea3a0df90166970e49f134b7ee85df19c164ad8a562e6cc0f6d304b6f02e6575f187094bf364511178491e08a419803cc199968203db4519f8aa5e0e8ef591a30599a2d4b233201724b9a5efd5840af0f40a446431b0013b9e8f62698d4851c81684747785aa944a8b02672715000f56c915782feec3cd7ea9f0e761cd75ffb8d85d8506ee77583d4614f86a3965837787e1fcc02ea96b4db7716cfe40b72d3cf2cd75eb3438cc4b8f9158c1dd141bf532104d0a7daf768a17225cdff66495f0b3a6494af3d20de7a2a8db44dc81bccff8189454900666461d648518470b69e1ebf747eddf8de0c95f3a352bcef325e04123be242dd237e08525c271885907ddeb61b00653ab536e10fdbcbd49aa94864ca835d7ea8e184b50ba9d7189ade50645b6373dc3d7eef8a375012b64e6e331be37d6e82f264b9e7ebe509d398ac71686d276c29a3464f95f048a0e8f04de6ccea8ff08824fa9c73b00d2a5048aba441689678f7e4e9c4c91762a82076f7500e13b7d9759f2107b893c23c021de13e17220906282dfa9fee71f64cb5238486a56d94efa51fbfff11452a21646c31dfcee3336d0a84649b0ae6d78d076d0c3c8b7db836288a9e02d1181cb576e6f632c4d161d8c06afd6562371c16d67225197a5fa5a71a05d3a91835c080eb5ad80d7c237a1eb1e48f6ffd414ebeb2e8b930f11fc1f321d9813252d565a950fbe0365a6c714ea2d2b901d5c36840bc1c890031cd39c47979df2e38d20ac592350b00c5f98330556ce0148462f975e669dd490d01383a0c7001ba7882658f21363c1961d655e60f42a3646cbbf32f5223e0bffa8664b335c0f241222c564711163d016432a925547401245b77257f625928c009a84b1597926fe49465544f53fe6cc851e3e3b96eb673855657b0b2f3194456044e97d323ee4fa3ff8f875e1b2ec3650164b5c399ccb9be118f2a08157e1d41207f929e7f284ff9f83bb8e1ed3b903119da92ad97d880c4fc394c7a96bffb32b2d3e39c63790851eb0efba3300deafbb4c185c04431321af6a813267a6756d4fe23145daeb4922748934d21a569a323613f1503980094b0f88f4eec736d7bab9a941dd4fb7c8add8db8a905e2ca6894f15ffa650a80c12210a3976a552b4d581f437eaea5dbce05e7eb81fcd5f1388c475b22fcfd45a78f7f240eeb3bab2b9739b88cb5dcd313d047941041cd02d2f8881351a6d5d028149cb9165f00f9ba95dec1b90d4f8f4214c833a189591c849e870cd774da39d80c359c475c31948dd24021f3d887dc5ccf3dfa32dc220bc56257723f0e50b8612d534c2d9d566a9e34d3904f80f4dea5a78fa19ed049ddd68e6dc1d804d7573c9e06aff4567e8153cff1fb06d4c794343f17e7d26fba6532abd7991d1766701c78bd8b4563690b4e43120487036b4c0ccfe37c00a82262ef2f15b6bd77fa88a7113394137602e96d80a7cbadf61eaf5923346240ec56331ad9a68f0388aaaa0e2b0c0b06092e47d02d50f6a4b994aeed9123ab811fe275d9c8110ca63527b5b7b4168dd212d693ebafaedd9f60b872fd84782ef21ab6eb9c19da54dd5f66df8f932b97c3d336f365f36d91f6a7312c768809761889432b2c2db6326f5655336b8b60c3c7b01dfd67ce12c67af36540202d180da833d20eb64ea977607f830e551f1c90260810b631ea692319eeef427aa0b60c12109f2167913e3e3750b936c4b93cbb218f9f452cffcfa6643043bd96bf582fc93f6675cf82ced4458833a92e0ef6b2d49a98898c1ecca1c19ede40008680ff11bdc513fb144aca19ec61a86eabf343e2f94e843cf9f3ea2bc0ea73d126c6db0e20ac41b94bb7258f832d25067110c4dc452f0d25dd8015d3c94121eb4ffe38f510d44c1642cf16ff94771e430656f87f92412203d2bd12bd0bcd6866294822c3beacc60a845be1729fb68e6ae7b4c5b46c0c5e3bc422c22ca574414ee18090743ac04725041afa4cdb86b851081f80732d4f53f1addc8d2be7af5477a355df7bfd05bf2032bcb200aef2f65dda5fe0da1f7db2348485f4232ef6f1b630e7a724a0fe635de18783858b5539fd07cf51fc5221237b04a721c391f5d6e36ecc3d344abebe428ebf0082dbf12987b2ff04688a86610c5a96012e2d4006f3ba981d72bf12ab6895a57061e5655a228a69e45e039a3cf9e7d270fc235f4ea883d1a535ec7d9a76cde0d5dee933c64dbff0e82b1a9c3cb3fa55689daa0f4aa50578351f3db60b08eb8a206163ffd238637a48cbc35cbcbf5deca62656f85194010be02b7e4502bcc38d819b307d3cfaef8ca4165c299c3b888a72d35d2cc801d4019d90ea5321d40ac3a02ba978de3613ca2572d2f9fe33cfa4dcbfcfd22e0d76a650d42c2eaf91afd67ddb4f7e40ecea1fdf54ef51011257eb8e64f0391185bd98554968bdc2bdf2f009c38f1c41bc342f9d2cc3466fbca0a68faaf47f644c30fd9c44115108ea3c3c33a37c7247fcc229e32705f3ac7c94c50884ebcc8efb6723c14cf4cbe0f150f55db46682c779d486c2c0439234a4cd89f1e51d819ae1e46d857daadba88f390a9210204675f949186c2f6623c1e02d845e4ae43ab3388f0cf2dba84f0e92b0c97627eec00ace0eb865c7adb53d730ad066c9bd495660265ebc237d80249786c07f640cb7e831a32f62e1f6b70c9adde3d9de53bca81737b19f8fb8e123fbd0c74aa3bb9687554d553a661155fa72ec8e3fce8a54f9fb0a430b12e5e9a0c364025b202f27a3332522f6aad16a164a07b6e207f24730ef6c574fd0e7b606f8749366208d810c3a8bd15a71e678f9d26a663a2fde6945dd065967d07542d83d0add882b365f146cd753cbd184675ccb1c2b0c1b9c3d1f11eed33d887b35f051faafb8c9e5cfcdd09894436195f0f07540f96e9f8148c18fc0a1f582bb58aacaaaa814156ec6b7ea7fc18e2f7e01904b2b8e20330eccaf797be19e7fcc4b8fdbf7ccd8868d3827a6420fce223e50dd7516bae81781d88a14eb8ad775827366139ea68afe2fa8dbcfda3ef06c9913ae092a4227e11c7be92bbc4862d3914a72f3cc2346c381e375aa0377e55146ac5f1c032d86b5cd5744f601c0b1a395dcfbe84e6cf95f345e2ec399ab63dc7f9feb516e81e2e2c5c54e29984fe44480e32ca2defe8b7502d73020ccec52c1c7b6419af3ab78a3ba3aba5dc6388500f30dcdf8ad1981143cbba40bfdcac5875de18fafc16ce39a7c9a786b5aba7e87da3c4f9c7001c60cc2a329a8a281cb082c3c4690dbea73d588a1a4b35c1a98c5c64a9b647893bf602e7ff2e12e445d2c30a53c61331e25ea2eb24db623c171dfae6ac790e9d027a244c2feb88e69729bc401c4fe85d15e674fc4db948c25a84fc10d78c3abf00e92e3f3d4e628149295116c7b55222fa5a02317d045f97b0e8d916f7183a2641b5b99641c920915a249a0f97a120374c80cbf5cb4a5e1d281ae0d34271fe0cd3d1606f9015b463c6166699e31cab47f693d50376dc6269df87c788a74f1087a7b1a1b12ed97ac476c48544758fab1c314a6b6ac70e899b0ea2173139061a60fa7f0a3d1a0cc01c6b6f8ad6aea95c458ef75fabf29d1e2882ec28a83c47e85688f02942af46b260042115dc734a0b267182a0248ffcd85aa8eac95731f0868f4cd62584afc762f009d56fb1dda6c38e8bdccd8d48aa9b0e8649ba3dcdb82f0a3130c23fddd6b84668a0c25ad415c3057bd9129fcdae4481343350e6b97a7a913cfc0ff238a8aed3d0f0702c50703862fd687e4c2cafb24cef5904caa72c39a083edb97d17e81ed718e3a9ca8a7d5481673081db70aeebe17163917d631743604ab7603dbbd9f28354d3eb09ad887b41b054e7a2badf524ab13978e3979253135b8d83499f97d26e32f97c48768396fc10ffd5b41deb7b2b6e3bd83daab60aefad3f04d6a31ab8ceeb982ae6d754b2c70a1e20aca403eedd06f0112e10c89aa403ffffffffffff0f72a1df9a5bbfa36c43324949d28a155957d4fd4e534a29a59464d2fe41ccdb7ffc36951357119311ebb042f48efc28566bc96141a89dbbf74fa3b590c66151bd8fab7b565d1a6ab3449c0187656f51659a5548b13acf1ec40d12269cf18605a5943c2974e816525b1243419ce18645cdfe6466526850fb31c2196d609368d2cd36bb99e343b45727e1e9a5da95350fce60c392875a19deb3fa7ceae341dc2039c2196b58b6919de9ef56ea0e9b1a16a590dae64a4537669d4aa552691d8638230dc97a6d7a57fb944d22ee3b2ab4e792fd16f1f9e00c342cc676679649b39468fa8c332c665cfd90419fc60d42332c09a1f3cb4de6ac31fd679461d9fdbdb4d61fc36558c9b0186e3cc618d24bc9f68c61515d297b9142c64ef2248625bb97255b88b89abc61d0ee344c4f4fa38a698ba6eb7d6cfdee2055e36058509d1d4a266d37d5793de30b8be1c9947febb7f713173141397f8326314551e3c2195e5850f1169f457bb7feff8c2e2cafbcce19b3ff8a70b507677061392b29cc5cfa9f8aabb6b0fc295a4853edd102b2f16d7334d76d70b773d50e79f6f57965fa6670461616b75f44f8ea8fc73f61a14f4dd1d8cedd0fcd90ed4f33e2aeeea5fe8c2b2c7b16cd68baeeddbc9482226448ae17ceb0c2a29a3fa566b5eb9c5959a9a4a008194282f2463aa30a0ba23cac6e9d35ad92af33a86012d38ca7a16f5ee2eebeb76a75cdba14abd933a6b02619d70ce75deff26a5a08d55a5d874e9f43d26c9c218505a5740c552dff594d7d86035f3c230acb2374fcac5c4edbe6fd378afc9021a724290cce80c2f2471da34cc76aa54a6c5c70c613969526af17775a1a673861514c83ec983ceafaacd3863163db8c656daf7957a8199551c68cc592939f756efb18ba36424212c40d928a8e652cb8cc78277b4a66f2a4214b95b13032bf2bc4f5c45b55c2f686c9104cc6b266c896265fbe0cbb29617bc3640d6470e762b6a6adc935c55bf3a8d39b495b7cd6bff186c91a464c82542a6f980c513cc6d288bdfbb756bb2ba4256cc6ac2d25c249e989b041dc2001a2c3184bdb6922c4a84ba95fa618cbdbf2753ae5b29409173716741063695499dc8e2523bfe74739256c30890d635997d039ec33cabd1106a9542ac25836294b4d8bafcf4a864ad89607aa23188b2f6ae5687d1e6dc7048c65bd15772737298f17572a71d0f18b65dd0dd94c713a676bd9178b9b71c57c99e838cad38b6515bae3460fad5208bd8451da043a78b1ac74d239264e7b3b07d777b1f0eb41755c998cd964aa8be5efa0396c768c7750371874e462e95499e9e668a17e75e5206e9094e8c0c5c2e655de3a49bd2bc7d324b75814fd4966e84f1d5aeab658d02cffdd5422fda3a884adcff66a8197c6d04df3a2bbb19b2d1e6f667f528ad3512a15944ac5f524c5880e5a2cfb27fd9d3368974cd32c96fd73a68f2275ae0621592c7a4b57319b6349573516ec6dda0f978d69dc53346e97baf70da5f5730e8b459ec85fa993f248ca49a9542ac6482ca1082192bbaf58d425c75dad876873a95283c4150b422b213ebb6dcecf722b16c4f76767d23928bdae352b96fba490fbe27fdbecb48ac5a8a3a9678c3287898f2a96e5e384e9d5d8606f2a167784d2b331cbd68da10b80a001a405424e091a6baea003150bf6fdb1f7d4c7c8f42a614b1152440809c9aed10217d4600d186cca11d64eb19cc25595693c29decf0e850e532ccf47cf31eb09f9dd5f76424729964efc9f997e77cccedf408d4aa552c913212466b8091da4586e75e7ca9372a1cb8546b1b0ad994abffe24853cf518748862517b3ee8f8eaf8712d9433040d27838e502cabf72ce2b945c7c6204a1c878d4aa55269126b1d5bd0018a45a54bdbca7b7cf89895b09d9890a4fc1b69fd093edb4334683271f9f4948faa4568b375a0c3138bae835c5da545cb6afc2709a40d18542a954ea4e91a7673edcbb9cce6ec93b237debc4cef4ad8887c8a1092378420b49423468aa87166c66841dc2039d1c189a5ed8c31c99dd0d63259453c08425626e8d8c492686ed3613fa7755d7235da35b1a8dbcc3547351f674f995894ed54233d7bd0e246985810916175d38aaa1661099b1a97588c1d85d6bad654daa312363f394923bd111c305a6241975aa93129a9a42aa54a2cc86eeff05a7b72c2d3031d945872d1a91ecbb3a951fa492cdddba8e96a7ec6fd24616e13d3fbd418a31d4d7bf489ad0f4ad31189e512134a66ecf5fd55572a1dc40d12387440624964ea8e771dba745e8f580e61a21e4aa8f676c8118b49f7ee06ed496afdb2114bf2fd4c99b47d77b5296153434c56a69c3480a001440d206dac01c4c8a141a562e4103153d0c188e551d9aaf5f1d5db774c528aa8a1feac0b3a16b1a459ea18f46678fd61f446e268c349ec0b3a14b198eba1e5bd7ccdcff4891889a38d34e848c4e29d0e2d456388b9da8c640f3a10b1dc75a2eaa3e6294c46f3061d8758124a456fcc0ecd26fe1236236843d049393161a3523150d06188a591f9f842fd474d1e4bd341211653b62b11195e843acd1a1f74106259954eaec56698c7ec6a338865b93775db202f9ed615c4a28ccabc266390eae7c771925264c141a5920647a5624662697060ea4147209644b45c9d85f4395fcf24a5881a958a494a1121293a00b1284d09a56b378aa7fdfc6149a9ccadb7a5dafa9a04d2060c801831397941a562c4e444191d7e58148d6ad7d39c6bf58d9118cac241471f963e7d4ee74a4f26775907954aa512474aa5a2c6482ca508c7917226ab8810c387e577a55a0857baeef6770fe206890e2ae8d8c3b26dc7d9cefa45f20ea40d1800592a95f46afd0d2a1512a3051d7a58d42165aac9d49ee37a7958ec551dd3de9dd68c080f4ba376fdb5be380f75562a241dc40d9222e8b8c3a2b7765a132decd5b5346e00044f528ca01d164b69b7f7cf314ea8084f929214481b30005284e5890869038d4ae587a4a02328954a077183e4888e3a98339b34663b75b3bc88766690ed42f70619df55c2766202c719b248de103264c8efc5a1830ecb619548716e9b7747ec5c43c71c96b437df3e86174daa55c2f6cadc000d379394943f49411172e26de890c3923a1b75aaa2cb4d3d435e489087c3e4848d4a65d95086bc32214272b60c1d7158d8edd979d396fa2b75946a411121266c2c950a8a3a3946504f071d70585a9dbbd5a7bcd8b0a220e878c3a2c7aceaede2567fe712b6214788e0902334a8541a143adcb0a03388cad65a666e4a4bd88870463031a26d580efa4a5b678d5e916d099b10929322a05ca5c25cd0c18685572e36556790d5cf98a4b4694a9127f2431635e485a4913ed0b1862517ab750eb62f9bdb29613376dab9477099073ad4b0a4a156cb26a5e263cd256c4448c7486aa552a9e41ea9542a1555a45249d6f1041d69589cff72e1f1374546a8840d077b72869c84b244f821473d1c2747d25a10374854a0030d8b2d73bb34b7722964ec0ccbbe398fdcfcbca7319b1bc40d1220e8308326349adc8ce765f296c1d52193de5ef71e51ea20c362cc594fb3faf27c1ba5630ccba67585b896f921d6ccd52186853955baa575c6bd6e991e1d6148f7552bb96aa3be2f61cb65234484a01a3ac0b0185ea710bddd6ea3e316e2821fe8f8c2b296fef0d4dee985c91f0942507250a9542a448e14d9206e90201d5e5856bbb23c3588d7d26e4d1d5d58ec936fe2476cbc55a2ebe0c2a28ebf9fe99e6469d92a6133c217c193c7c1490a0eccf82405055dc71616feb647e40ad1f84d112129f243d8e2306292d4941e01d20610a44780f8bd0e2d2ce96f9f10e9da2e324ad1d09185c5d659dae75d5542f4752cd0818505e54a8f4eada37123661d5758d628f3e596ac574a733e5da0c30a8b3b9f3e958f8a68524e481c063aaab0fc5ac91439224a84068bfc1092d317e8a0c2a2ed678fa9a76696b153a2630a8bf342bcfa0a29a4765309dbda490a8a0e292c7d56f3f0f639aba654c2242842ce5c471416c6549692c26c7d672c615b45f244bec4c8397943ed8c65a0030a8b27c3d7f5cafee4327c9282a2e309cbe2853c33fdf61013139441dc2089810e272c8de6091dd97313f16a131685094d233e3b958c5b3261f953dbe7888c1e2b84a8cb121675d8cff4d3b9f5be7dba2861b9f3cfbb6fa55ca9264a1296bcbd377fbe9794d953c28624246f1c41aff690b0a0bae1951442dc6b1d2b6153c70856e25850fadc5fc4d75b7eb084ed8eb09ca2295eabfa31b45ec28662a2d2f42405658db0209a54c6276d2163747200c7b29a5f0f35face35ec66074716eb65061fedfe59a32e65e193687ecae6cc514394e57a2d6ca56c7d2646d314220b4f963ea7de189ad9634d53c236e49cbca18e91b3934f49c3bb08eb97a16944dcc364256c7d76842485c8ea348c78ae75ee30d3607e6f099b494a112124b947481ee5e41849a381c9b64994fd8834292b61d3932f52448d368a2cce8667fda8755017a221b2aca3af544acb77253255c2a63830eb14220b5130643929d7a8376ed49d3d2c6123493b2949449ec8aa136131a70dfde44a6d3c75b6401d362a159493ef0de206c91b4358f8b0ffdcb151cc2625dafe89493a214bdf695ca9c626dbd1710de206090f4a329d5f1a32761e99599741dc2011c21b0b4ac9a84a5d0f27dff113ac5747711bfad6677e0ef2cb1d4f4386f098cbb62c61436913a8e52dd14aebea7b8a7c4ad8e23843d0342d7dc8faf1e8f9735e47801653555b3ddcb57ac89c95aa511d4bdef5db48088bf16568eda7f76a9f1b21220411c97276dd3bcab3edc6bf83b024a5ea6ad9a07498c9a41a4058f43e559d844ed24bcb26ab881a494891c346a5c241dc20593f58fad672ea3fa6892fa5378a0821821b1f2c8d1077ea599686ca34da9e1bcbf1266eee9a293636b83d5856a9e4ea10ae358c58311bc40d921ef060318f6b46cd3227f4e64bd89410923712470a0369030640e228d2824a258e22a78dc5206e902c3b581aa956d785b8c6483ba083059967344acff6f8383a343fc8c19266199494f267a2af3b8252e38d22e706954aa58262c484c438881b24aa8d74cc6c68ad5fed6c4ad84e3e450d342a26a95241a9a471032067ec24c5880e38e0002f8d591bdb50d798367accbe7da354aba784ed531113ff54a40645d2d846316282831b2c78ce42aad8df283a8d256c1bb0819963d432ce44f7cc696ab091d09ca632ea1873ca53c28636831aa86370fd35a9694ec8673208727c2d1ed4d53be8eb6e031a3c9ee5f5ecc7d8495fc2a64c70b0061edefaad9567f19052cae40633306ee92993598e50cddd40068b31c49fd0e3ab45bc56c2d6c66ae84a69d111e1dabc75d7460c96df5b6936fda17bda2d41615b18589fe9e95343b41afb89989c18b1c3411a0b5e2b6367ad215e3f54c266c4e444995916bd95d4ee21834bfda512364941bb172cc9f98809513aa72f623224912421292705e5ccd058545a467dcf49bacc9a54c246c4e46465a5921e0e206dac018488c9491a954a8a0b2a150fe20609125cb0f4d1848a963a359af8256ced821620abe23708fb2c7a2be14aa552413d4931628405bec655d111799bcd5e6685081c43865416468a523722f444c93c02690306404e4cd61b47d0bf60881122424e891c29822944961156b01cd25b4bbb7aa8e6f755b078365a88949df48d27ad1a2958304f2564a77b5d1fd3123612345b4b0f470d50b09cb54cf1f29ce4d367256c679b4264e19e60b1b37cc9535ac5e6884bd8524e9a052658d61cbfef84188da2df218bdb975ce7f4d1e5a8c932a512b636eec520dde55e6697c1a5b084cd0826276fd8f19289d69c2bde5ce8974bd84e52507641b778a5c30be13a895ec2964264790e74e14374ff4d8bd3a9ec1236384ede8e7b908b3a9fc60fe399fe9456095b09ca11232427268b645b00040d206d0069630d20e975c01ac40d923570693f6e0eeaabef5ee94ad84e4e4922319f22e46407954a0a915541393b0062ee0890942344488c0671836407b76c2b6557f545bb74991236206dc000481f0192c60d800069630d2024458490183139517c272946f04f4c520671832405468272e48484440286e40887c8054a60242643484c8644a08201128060041a10019010a07c00041ff8802a7248d20321e880a16c800324abf7424e524200c4031b888006cec4e444021930921060400217b080052250010a50e00313380d48600211300818910718c99f2c9213939443010768a0012be548051840e448910518c99790902414214428a080dc0a24e008264648b10002560f0307e83312445262000c1400850023188091fc1b1e10000909087ea49c9c140f04e0360080226f0841c9800f4389e38940f2811e1ce071ca08119344426244583d23e9517807c907ced8800e232171533df524242947780ee4d029d81d94a1e3899c223c0c0f80c831801ca198852f5ac0231616a800052620810840e0010e68000316a080042080000310c08f000020478e1c3972e4d0b123878e3372e868468e1cccc89123478e1c3972e4c8518c1c3972e418468e1cbfc891c3173972f422470e5ee4c89123472e7220e0023c7220e0024674872306902314b0f0430b7230c0178dc0852d72e4c8a1230bbd7045912f214139a9a0f0b8c5197ec89183872d18d08b36dec08b48f0a8050f5ae42099852c72e4d8851872e400c2805dec60c02db4c0805bf08001b650430e3450941a97bd76884c86b8a016877098c0043c5c418b39f068052b04b10a55e4c0411b3c52c1e314a6c8918301b218461c455a7044120f18f129428a0859a3182264074386b4407d1f72e4e0210af5351832a4053972f008054a8e1c3c4051e4c8ca9183c7279410351af1297f928a98a091925a9023070f4f143109a2069183871fb213399a136b3972f0e0c32672e4388ce39c9cb40025470e1e98b8448e33724eda4051a3155ac8713a20928ea0d4d001ea800f95f8038f465022478e1c391832e4481a3f049d1493206a88490c1e0942709023078f49a49c98184129724eda30392992834fc324c8c32104c5c4a48d1c3978ec213c96b1a0f137c7cc9cf250c6d2d79d8ed5b4cdc27547782463d1f449d1a79fee83582d32963d86ec12fac7bc76ac54d8c0e318cb2b6d4cd374d54565ae889037849c986c21781863b1fd9352e92b166351b9dca831d748a53ba5081ec4587aa5655b87d722642c552a276690e0318c45257464d2ac26dfaba384e0210c4c630c76abc14333ae062d353a9c966e116283c158b80d9fff734c1b3bbc36a8544edc103c80b1a026dac5c4670f32837eb1a4e5398dfacd649e4a34ecc0c3178b617f6f65a8d9462d3642e4a42405c1a3178bff1e94d0a2bdc9737eb2b4a0c283174b4a6b97aa444db3a78c312d018f5d2c9be6579d75cf5d7e74b11cbeafc453eeeabc672e96e5c77022d3d746f91f2e96368350a39496d99d5e5682c72d96a44ea51d646bce4ae94d82872d96e5868cb265544a77743eb92678d462599d52df203a276d29eb4bf0a0c5c209d54a6b7f3d4a87cb089123954a1b21720467b19c7a2ba38cca3ccfc64f5a70b258565393dddcad35942f168be69dfbc6e55e7dd21932e4481a950a0a0f582cb7fc4f2fcd73f48eec9c9c9323f078c562b9d431e9737f6a73ae580e5959a9a2fff3fcd58a458d52833efdb66194162b1647ab2f4def52d2d6b58ae59c59671f2d3dc7552e552c7c6fda983d1f83e93f154b32c3ccd47c9a95dfa362715c4e5d264fafff54a758d61d5d35aef40cba27532c279375f9cae637955029164de5b5e3a7ddfe8c48b1a4bff7615f4ce7d8a1512c88559b5f09255d57eca258ec3a13cd29c308ed6c28963d26b9a6e467907a5d502c6e88c8f7feeb2064fa89e53c99abf2bb43d7293db1bcba6dd36621ef52cc4e2c8dc8ddfa9c6bd2a40c2796c46567d4259e6c85a9073c36b124523beba071569e6e6962492b950de284f4243528130bf274cef04a692d6c574c2c6b89b6caed29219abbc4b29c13a5e24dfcf76496b84b6edeec33c79c8ac6d81b34cac955af71255252ca20bcb4c528031e9458923f2f329d97d25a994ad84e841c232428879d24c564088a09c263120bff2f237e4f89c9ab925854c2fe7b74f2509b94256c79ea8f20248d3533121e91586cd50d277a5ce3fb4e3578406249add2389fe17396d3f278c4620c4aa5d4b5f24eec2b881b244478386239e87bce7c99c9657c2b1512c346f068c4f2299dd35796476f192571338c5834b15af7aeadeaff97c7226e8db39936ea687b768e6dd63f4ae68e41b74b79286299e636e4aade6f193384938837a6db11377b51d22b228408221242e8da7c29cf95b01941e9c3e310c6e6d69a631032b3ac842d8e1423445e83c0c31078fc6d19aa935208366db2d3e4b26d5be3a1ad9526b5df724e6aac846dc839ab011a406800a48d3580bc006f37881b248b072196336731e2a5bf45b6f5071e8358d4626f5b9e6c64ec773ef010c4a287fb24434be53a6db397071e8158dc8c19e7366fccaad3d9071e805814dffe9559c6305597112307ffb01833eebfd0da284cca78f86179473d664c9defc3a2abb99ed5ea2ac5343e2c89ceccd094516aeab41778ec613963c5f37e83cef0530f8b2ab44a71daf407d5681e9646346ece32297fb14934f0c0c3a278d6e82ce25d8492b71878dc6159e7385a66a1633d5cb6c3e2c638ba212b4646cde900081a406600a48d3580b81b78d46159f65febaa47656b4a3a2ce99acdaf9a2bb73c27e571e06ae03187e57dcfaabc83f094b29a030f392cadd45178d0555ea2fc22426610071e7158562a7dfbee368c961c0e0bdff69fb446e3b7d2a6a781c71b96540619bf84e97dd4628f81871b964bc9a46293148d6e771b16e38fa7bbdb8c3234cb0c3cd8b09c947aef7db51d75371b061e6b588e4ae43d758bd5ce130c3cd4b01c470b2d1fb2450ba1b56be0918645572b476ab9abfb3faa848d446d6fc0030dcb51766c0aad593faae47186c53699a3f227ba630c7998616935bf4b597d9deaea1978946149e814ea56081dbae384830c0b4a5efeec08ed3d61a301048d8d83c71816535b434ae5aa4bfdb8071e62588cc2574c5d73f0f4561896ec369a879455b31b836141de8f92e1336355a75f584cb71b32fb67993e8b5e5836d91ea174d06f9e735d58369da36c6c1642cf4bb9b0a0a4169ddd93b0d9a8f1d8c27278cdb1b55d8d788bfc101293222496060f2d2c09ddb9c76590e29185a486187b4f2be5e48185c5db91a3b3bf313347f1b8826e1e1be54b4d7743d745a7d049ce6ed36bb50c3cacb0202f563db77e94d2e51a7c1232040729425cc03ae4c0a30a8b23367afca8a34ea3691e545812532e94962829b3143122784c617936ee43fb88774735917372d8a8542a9584a35259226a09c7f921784861e1831817a5538657322b0a4be6de1e4e9e7d2ea187c272dc6c8f92316e2ed71396764da7ec51dbf192871316766673924f6a4d37aa19cb4a481f21775b87d44acc580e29ccb42aed2d4745cb58927ba35327376d1b3dca5812269e7fd5c57d6c9d8cc5a0a1f38376dfb69e91b1a0a5db4f83d0f4f43196c4764cb47fa7de781a63597e36f129fe7453098bb1a45a2feb1a2f9e1f23c672991a93f9fcfa0e1ac6d2f928dd3194ba4f23250c57c44e4743b39bd9c7e6e076721ea74a8d2718cbb9416cbde977293db473d8018c25e952d9ab2a11ba941c0d3b7eb1f0b9995ed436357cb1b4ee3943dd64838c380f3b7ab1e041e5a56b6911db311d76f062f14f08d7ff39da96da186b37ecd8c5e29754cf6a5386cc13aa547c872e1635a6bdbb8e5af2743817cb31aaeef9559aa575838be530199717951a55866fb1a4b4682d7e5464797e6cd16a9d87d8b69dec899c4a152f454588bf4f6bb1d85194122d4b98b88f2a413931a854f268b1244b09df1452e6a6ba67b1e89a61c7b409e9a263b258eee05de62eb56488170b53ecf355b3b6c9a986768e32a95326dc657783859e2b9eb1df62af311bb623ec78c5e299e639792363a57c5db134a66bd6c36ed4bcab562ce713f39dce3b562c6cea8d8b12b24593b963150b5abc8ebb1fad533a77627272f23b54b158deafaf2a5e6d274f2a301579ad8da71946b54b3f3b4b5d68d2d5f151b1a8ddd32af5bcd26eeb3bc5c229f9d939536bad3f534e0e1b4bd8618a6559d562ba74bf739e24093b4ab130da5bb6c7501fe463282888e2488a45cfea636a73d50f1fa2bd61c72816d7a58cf5f2f720efb543148b4969a952ebbf30a9b73ed32dec08c592b86cd4a85b5ecd4941b1a4437a4ea1312b79a3edf8c47249977965e9dcc688ef0b3b3cb124d47970a163de85cb113bb1a87ee49994bfab737c716249af6e255b5f6a130b26de9bd24e0b7f5dd2c4620c6bfee93a7f36a59958f856f74a6dc38f7b1ec40d12931d9858b6d72e633cc890fbd02516758e6f27d5d9ce98bec312b7bc6746d1cc2c6f4d1a3b6729ea4aff698c6109db8e4a2c9f7eff9819659ef5204a2ca6f454a1a7677db3b563128bde27eb5db35dd7a676486259c94fa6b4abb8c992ad60472496e3e6ac0a8fa259bf5ac28606831d905898cd9ee95b8ad5e29e4aa5523938fae03822e908e8851d8f585097514ff52fa5122bb32316346d345ca598be994f0c3b1ab1a0437e8e1d3f44241dc11823168592aaa34dbee857af2aec58c4f2071d234f9e291d97a01c31e2abed821d8a5892b1eda31acfd1636d89587c553d3a4d06710d3a221664ffe7d55a873be1c9432c6a4e5274659c3e5342432ca7342fd9a5c3ef8eae108be94d9c92327cedfd86107db606d518d6dd4dee5e579594e17163de6b10cbf9f4d48acc53ab418ac404b128d3445ce8d357b283815044a379c779666cbee694d284701fadaaa33001624188adc9fcf9566a7ba9e41576fc61c157d43b9bd4fdb0dca64f668ee52274b5fab01cc7d4ebc384f8b0a0616fa3fd646ed1dac3f2be8e1febaa66436f3d2ce96a9569c4461fdf380f8b1d4496cc696436f31d0fcb2d374be529f11d0ef58cb7ef6233f219a2f935a5eacbb41d9645774c3df2747f4c6ba5b25787054d5af6e269f479cce8b0ca9dd8ba06518faf8d6d9317b3a9b5bf65b4e6b02c6b430ba9f15a094d0e0bfaa5d3c8383a8378b5093be2b0acf32617197578bc762a951d70584c1ec568333d23e3868d0a3bdeb024d478f68edf3c63329529ec70c362083d0dc294942363572a958a939861471b96cfe6fdc36492e2e4aa844dd1de2822a4971d6c588ea7b44e2b53b65def1a16dd53dfdf6cfff586d4b0d85ada3a662df63b7f695892f9f37542ae16b7523bd0b0ac4ef3a59491b14da8cfb098a5a91572f74b2b3dee30831f9f257f3aa8cab0a85634676d5faf3e3c325ceba619c76bbc6d3644db69799fcd946e19cb60c71896476bea18a1994f8878871896fb73f468aa67f793768421cfcf587397d16e315115af56b6846ace656a302c68509af39aeb9439a44c6a61c717164e4d8750e13f7faaf5c262d2192d34e444544759efe8c2a2ab099971b4900976706141eb3d5da9b426f3dc99b0630b8b5acb76ec0d8d69eab4430b8b614caa6aef9cbea49c85e57b4d3f9b3a84cc1516fc34db530dd99def0ecfa0941add7f3a66a52b2c96167e5a7714e3faa33bacb0249f4e4b99f15beeef75d8518585cfa6f455ed4e86b8c3b0830a8babfb95689335dba754217112c30be206898e33ec98c2722ed9495c2a1342b770871418d393394db5cd262ff3a7a3dc505ab776446149c5d3a8ad2eef4e9db13fec80c2b2abf2cf25e30bcd399c840c4147d8a8542a954ac549cc8c9fb024a37ae149b5582df3b7c3090b6e227568ac08f9fa366341cf2ae131a7cdd0b967865d9f269bb1271a64c35de8f039bbc67a4ce72e635967d13198fad9c97d2963b9f3c72c364f7fd666256361e47f0a1de2830b17321685dc15a6e4bd8d38fd3196a3ac0c4268f4af15bf3196748ead5ab7af1ad1be188ba35e54890e21b46629622c798a0e2ef6d27fb486b118c7f7334af99afd0b6331a97bfcd09da7842a188b9921ca63f011fa4b09184b7a664e84eea7d650fac5827ece60da42c6a8c2e48bc591a3730a3991a54ba8170bb6a1dd937bac3c1df16241472bcdad7fbb6a3abb589ad79e32ce4629d3c675b1a0be356d57bc8c2b948b2539df59678f8bcda771b19ca349b5c1c47d8be5d42722e5aef0d05adb62394aa973f62aad5bd65c8b45d171b559635a2cdeb996327bbf6ff32c16a38f9907d7fefa49592ceb142e9a3a9d0935c662e9fb37d6e998a3c9a7b0580ea5a23da6dc2afdc9572c46295db7bbffaf085db124d37487beab8790c1562ca667f8d237ad5f988c158bffa65ef6c94ae1fd2a16f3ca38ee4954cb9ecc19aa58d6599732cd623a09256a319c918a85cd31eba0c5a3db9f4b0d79218c8ac56c23b46754f2d95d5c72c629164667791d4af5bdcee30b82b8417272862916c4fd892d176af50b4de18c522c9bcc5accfc6c4398162916d47f7a2de5b75af973148b659fbf4b4b6dd0fd8962c1ecf7c534eaa8a55a43b1f459c9d3bae14d885041e1b966e6dcdba676ad6928d9294343538de6d02716dc75e82d791a3d8dcb130b3a8954e1a65787145727964cbf3ed531c6def71627963cdf7fc90f3791d36f62d936e58ede285c8ee935b1e09b3ac3bf1622efa432b1bca52e4255be3af1284c2cae6ca6ceee76bab6bac4c2cb88e74d2247b9505962b94f87cca8eb396abf124bae45c9f4bf3a95525162c14696ce9cca4d9ed224167554697676a5fb6592c4d2abbfcc42796a59628ac482cb1c5246ebbce64b9058fe8daeca94a84c9da5472c6a7552d9780c559b428e5816528f6a2da3e1463e8d5890a346758b721dc277462c6ae64eb7bddaa24abd88259d45ca5329e4acf056118ba2be1ee3cbcde71d4dc4924eb71f9d44cb935311b1987baedfec64d8faf4108b2ecb95ce78eda1f4698865ad3e289959284fee6321da8d39cf273d2116a36cd450235f6ea66e10cba37dc67534d9a6844e104b9b5f6f387dfb4ae31788a55dd5cdf8292096b564dea049bbfa8a7f58585d233b47b4956dce0fcb5b424a2935ba6e93f761493dbebddcead73c1f96d388955ad727f91ddec3f287bdf5bc6d39df981e16f4864c9ad456e7ea0e1b3760630d207be48c3c2c7f5ab11b55fd95cc5ea9542a79061e163cc5e895b2d33d6c2638e30ecb1f76aa79648e359bd90967d861615b860cd2f736be9aeab0a8ea5dcae0a756e6937430ee67a9baecbcdbeb896b162f4e65fa90499ec3124b2255eb243666d6a1514e2c7796d058766253bd36b1a86549d3b97a93b85e138b39d6f52bbdc95de933b19c1fa3d09e3b798e8f89e552f17a53c88df9a74b2cba96aa56271fa9aab6c462d817e28348fd61e7acc4826c692fcd14df34272596c6ded36929a54ac83989e59f0f9759e36916da94c4d249fb4e0d5d456231e68d1bc446ffaa2e21b120d3c7f0c1644ea5b97cc4b2ea866e1fcd20bba523964cc91b59ad972ffe462c6a4c7b22d474883d3162e93bbb96276a27565fc472d47753e6522ad9392a62416bb91aa4db9888e5d5ba6432a974ff8b11118bf25447d49e2955150fb11c464aa1b65aa477898658f6af39991f7fb285588845331de6579ff6fc3924c472eb9ff97ded1be6e1209664069deb3107e5ed12c4624eafdd75e80462d14406cfefa893523d20969314523eaac888affeb074267a42449ac620757e58dcaedd9c5169afabebc3e28b93a94c77ee8f51c78705753f57cf2983e9e8f6b09c5d7546cfef223ee7f4b0644a475f74e68eb65f1e16cc755e7daccfbaeac3c38230d9d349b43e7b7d7758509b456ad1f4d96139a9f196313cdffb7c75585e11326f997219577c7458d23ab558fbd3e52edf1c165ccad8f1fe837cfd9e1c16e4e327b9af6344292f0e8bf359f5079d2d1ec583c3c28794f652b90a9532f786e59cd1a67eba5278ccb961416356a3f4698e67af362cdabdc61ff1d3dd2d362c7990c9f47834f7cc5ac39269e97ca9f7852e931a96f4c9e650ee725b8ad2b0bcd137a857a9530b1d342c862cdd39e674567dec0c8b5ae85acd2f21266233c372caa8b59faf5686651dde1a357f5778d4c8b01c93c637f3b81b94d2c6b02864920da9344b88d1c4b02c959c9c98370f0d5a1816ec94ac8bfba0a47b0686250f2a4ce9cbf8b4cdbeb0986d6336776e467b796131e6f471c5e6fb91595d3007d1bbf3252e2c994969a659c83df7b7b0589a3b497fd1aa99d7c27212ba3bb3f6fde3290b8bf946568dd04934762c2c8f1a1125751663f715963575b3c7c67bd36d8545ad934c6265ba0a0b5a636e84faacbfa642f719ab94763b85057b9d54654e4a61b94ccb890e657ab68bc2b27c8ce7a29594b63206005058d04a7bd5c9971ab40e3f61695b556d663ba9d46900e08445bd2963c98e4e33965e2b295688eeed879419cb4296cb579bf7e5f4b60c3ed4444c3698ce9dac6988d0eeba84e68d4e198b9f7fb3d9270d2545c9473216b4de926feaea44664862c85814ed5ede7994569ea5f618d7c7e8bd988ea6ec7e69ca76cef2a95da5ae84cd8db1ecf3ef724deecdfc5c8c0599dbdb3e99d8c64f89b1b8d9c3be799632744e7d0c6379b3462d8d396e3bea7d08c3b9d9e45e5b6aa2b69a55a498d27aecbb77c33e82b1309aa3d2ff3164d0f80c7c006371e5891bb5c95fd6bcbf58d0f5ce764aeb7759225f2c69cd5321f4c5b68b34881b2446f0d18b652f752bde7e32ebbd0f5e2c99dccfdb30fda93b7453f0b18b65139def7b2a3ebb2b5d2c7816a69ff737a80e251fb95834cd332746bc54ffb684cd71b12c4c8eda16fa5b6f7d256c7d8b0519354ee8f4a0fdb5aa84cd6cb174abb50c15a3d163524bd8d0063e6ab12ce3eb4f5e2223a4fc28b458d0ea95de0825575fda3c8bc515b13bf7a685a7777cc86251760affcf60327b2c16467a6971eeea4c63bc840d16cb4a369f365579995453c28627e424255fb1a461d44bcb5d6aeb57c2c6e68ac51f195c099dd63e750c4a2b16659032e7e82fdc5ecbc48415cb2594c828a5f44bd8ccad62c974879c7e5d26a2a354b11cc5b52b1d1f71ae33a958f290fb6c7ad387c7d5e203158b2a5c456a102dfe758a058dc935460a13d2a3bc84ad4db1d852da7ad29a73568a051993dda9fd478a659da19bb30e31a6e4a8846d47f1c7d42ccfdd3d2877b90f5174269e494b4b63345bd5d598e6323ec26c937c846239ea8f4d3eda048a65d58ef91eb3b5964affc4627a293c761ead63c7d380b40183214688a811870b50500e1bca048d4aa5623e3cb1a76bb097cd6ff1205a4911a73e3fe88ae0a3130b4ad4c49abad0dee255c2462264ad3435d2488308ca3142a57242cae083138b711fcda52c25a39b2d6123b29b58581115235f063bf1ff64a56b62f9367574d29da3d4e0d01af8c8c48292ae63fce871bb8489251d7e5a7ae9d5cf881c297289052d764273ecec937b0c7c5862f9b3b810dbc1bca4e8c7e0a3128bfdedba528fd7df8e3e28b174aed36bb54a739cc8580d1f9358cea069f7a9af32e75ac2d69258ac53216afb7184af4c09dbea4562799530cf63326e6ec64a1812444c4c48de38212109808f472c9f988d0b2dba3275658219f0e108554488118a1c49808f4698e195980d1dfdfd95b03dca902739312131728898a91b3e1881ecd6edc8a69df3b8557fe97a37e86c6ab44639eb32f0b188c558b222eed49b7e7ac8102133a85414b1244f2879f5e9d63ebfd268e023110babdd56f87d2b4f234241c4b20b21e44709a951ee432cea16ae2bf5454cbc1c0db178f2e2b4a72afb4fed74e0a3108be3a22f65e7fc4bbe12b66ec3072116d5ebf22ce95ae36774100b9a649aeeea5c9fa53784a05c101f82583a5d9771971e4aec5ec2168805dd5a5f5747b52e27402c9c681de7f4dcdcabb6842d51f9f8c392129eddb5ca5c7fca55c2e67e587ad1da57e7b6d5bba612b664c3471f16fc9496792a3cad945a09db1a0a490a8a4909c99e8910122328ca87e52c4ea7d1eddbba73b58745efd2171d4f7cd4bd7a585cad3e6e275a3dff9f87a5ef24a5a6fd3452d546f1818705253f54e9c94deaa7e3e30ecb69b28332a951cf48d961414f07e15aca6785dc7530dcc98ec876dce98dd641feead99c0e8b31b5b50e9baf6afe3587c5385a6a4f2bbf478f2b87452d74daa0b3361ba447921e0e1f71583a93d15a5ebbc82c49d85ee0030e4b1a4a6c0c7652bbd07ac3924ba14a46a9a342ee46213182b23edcb02874ce3aeac61895baa684adfb48dec0471b163ce6bce9bfbbb3f565c382ae3e7b95779149843ed6b0a8a3663ad551ae37cb1bc7393939aa06e4564bf436666e86bbd996d9542ea73f65d8581a96847833d935a1e33521263a20e2030d4b22b5d660eaef3b3c7ec4c71996b665786808996d820f332c48d9d193fee12f754d62aa041f6558f6dfd6e5779edbe322c392d051937adb0a99343686a5dfa47ca3de58a594db62583e0fb759bd3eed7099302c9bafef67668ebeeef900c3824e793a0b339da53efd1e7c7c61e9ed75e7183caaa708796131ed7c96d431cb5cd7fae8c2e2781673efd8acff1f157c70615906d5f273ac18d7fad9c2a2cce5ef9a59ea6db1b5b0f47df721c32b9d369f5958dc8eb34a6eabdf5a21161644087de76bf2ea5ee3e30acbaf315a97cedffcbdf8093eacb01c42a38bf6d1f96683360a3eaab0a434947b47511f34735458be4deafd637972e9691f5358f04fdd7256c675f9ef430ae64cd16c8dbb71ea35fb4a3f5dff6e55545bc34714166567d4e6d33b25f5018565297d3ef3e7914297aeb9e0e3098bf97f62ffcd43b6e9954aff890f272c99c66cd261dfe23ac94f7a346359c9acb4f0d5f1c6bd45e9c18ce57e71954289cee4590b25c564084a4ba1c73216749b9efbf97eb96dca58f80f539ec9a31e25c4642c09a54ab30c13f74006b679a79add2a9b346b549598cc31fdcb2d8fb160ca5cce699dde540831197a186349948e21f69de76476a5b91ec558b64f4297d0e9b23efc22463021314969206dc00088491b950a3196f467591e1b3a2a947c18cb4a26cd9db1b4857c2f8c651d6fe4743346a551d92318aec86cd29875465537d3ea6a52fd2d602c9eccb56de2f4fd07f3178bf995ccc1420f5f2cadeba8a92ab4379657c2443844d44ed0a3178bbf615ee95542f657ca79b168ab217a4246cdb32533f4d8c592add242537cbccad12d0e3d74b1e0762be3aec6836b66e6d023178b42c6f28ccd41fc948f833af4c0c5b20b2953674f6fdfa1552a282928ff8610c56de8718b45cfbbf9b436ad73e6962d16b3a9870e6ae3970bbd16cb5a6526296e665a2c7face9f5d6e9d4e6b3c72c9647f7492d21576c6a942cd236d7ac9965671a4a75ce3c27af77e62ef488c582d8707a5294cf06b5562a439e08272726454a124aa5b24ee050420f582c481d1eccf3948796517b74106fa1c72b166ba45cf3ff0c1371ba6251b938a932fd5f66946ac5829a67ce302dc30ab3dd6babe6d6b03937a64d527bbff9c89c5fc592f299d1d8faa2939a1aef7aa862795c3cbe4cb34e375b2a966596cd195c66a8ea302a96c48fe9c7a8158fbe9e824d5b6b1b57cf34cba65db9b579c5ae69a542648a05dfa4f557be947ae42e61334224e149eab321e706954a299675da7aa9e3744cd083144b4ae75e8a9b8ebad695b019117292823264538a08c9c11b425cc03c8ac5945949971a8556e266512c6886d0a9f756eb98bd502c6a27539fc6d4c713428210373d40b1a41e548590edd227f51c542a67e8f189e5933a0bd91f7e1e5c26a588902345e8e189e51ed75267daf39ef53ab19c844c3a7869d2b06e0f4e2c6f0e35e56ba71ca4187a6c6231aee798ccbe3fadf6647a6862e964fee9eb9fe80d0e210a5fe07de69989a5173254b85aeda8a41c13cbfa31631237dba6f7bac4621032d7349c29b5224b2c6effebe83f2d43a3a712ab9cdec6186563565bc54ed5dc866a9cdbfb9458dee8a22773deeaf87812cb3207294bcc7f8534690f492ce9d0bbca56e66ae624342a951e9158f6794f522b2db337dc9058ce31eac8484dca84d679c4b206f1e8a5aee7bd85d2a8542a151447f43273295da92292384e52fa0a3d1ab1982d57c6dc7f2ee3cf8845d5253a3ccb983e374bd8d0e238e97e418f452c7b9cdd5b994b5fa7b087221683965aff66bd6486532296560bb1ef2e5577577f420f442cac969b54fd70ed269a2cf438c4722b2da366a735dd6a54c28622a40d65889094238658566d172de4cad810ad37484e4c488a9cbc1a6c27df417a1462514b8e52bb1f1fe39ceb5ff046e2688383b84102470f422c77f816baa4d070b93a8358d0324367542de6f3b63d04b18eb8c8bdccc7b87dc7eaa05dbc7ca53e79842fc2c6102338384941f9428f402cf6071b1d55bf9651fc8058d67cdffe59da7f580ead8f1dcd46e5b558c2a6454c86a051a9700f3f2cbfda98c4ee8577aa911e7af461e9bf64e89c5c8c9b7ef161d93edbb9942e33bf50f7b02453acd2f0c2446e9f7a58d6f93fca0e5b652e5b8f3c2c6694f12232d568bd71206cac01c48df4c0c3829666f7eedaea1e8577580ce662648cf5597554edb0b85a3c544917afe386755890db99c4fecbcffa3b4b22f4a0c3d2ab5399540bf951cc8343e8318745a1b30e6abfb2b5c88c0ee9218705d72e5fea09b5a1a4f0c8903f31c2dd230e0bfbb13cde689a13d196a41c36b224e518c938522e7bc0410dd574f79ebafaad31f77854711b73ee162a612b4949d1931423263ddeb02c84941ae58448e96a2659cbc1a3380e0dcca5082922a4c8119247711c2267449e480f372c46b53226a9e46a79d4e9d186c50c4abe909dd5adea4c0f362c8ff96825b6a783de6f10ca1a954a147aac6171e3b484961ee21d536a58782d4e4bd159fd6a152ae19422271d1df419394490a4471a9653477850ebf2a6d58f86e5fce1a7b35dafac103ac3b2dedcd27d9e49967cccb01c45cb3f3467abd57a191663cc9eed67fe547325c372dbe911bfa2d5547763584e5b9bc58e96502f5c312c7c8e2b56bcb3e8dc6918163eb3d8acb569f92016426230e801066b6f65c3688cece8baf78eecf260aec22f2c86f49c44d3ea4c4a985e58561b333548b197d1378a0841393992fe84c4fe0db59c5f030d3480a41ca141a572f2ca2448e58ddd2b9322a96667a8a1471796464b472f99a37d90b9071796cb3d96964a1b1752ddc272a72d2db2e5eb7ee5f5d0c2b29c0e1bef31757978e1f81b542a70fc89137a646169ed4b963635f6c0c2f28fb6d6fd981e57406c3cd3b9ce6aadec96485b91aadfd58e2e991e565870a157ca0f9a6476ef4c624ce85185452946752ed9f11e54588cb1a23b22b73d8b70f56250a9748f292c871b19b3cce76e49d552d0430a8b41cadca5ee4eb57679099b322142028710c5ab978324d11e515854afb24f9c560d6ded081f83933782c91a3da0b05842cec3ac145a8d9063207a3c6131c8556d9759838ccf19440f272c49e92b3cc73bcaeb4f8ea41c6ec672d21f5b0dad49b53695b0a53c0a89391cb4bdc18319cbfd193cdea4eddac9542a46881c09a058ccbc1a1f75d66c353a9f587ce525640e328afecff1c472d8fa3ab1f3e9c4a26a7ecff2aa3168f97062613e2ae5725229f5966f6261769596e539ea8f51ae894597311df376cc6e936762b9937dccc23f475cc831b1a4f3b61a59cdb52de4975858a9849baa972973905b62e1a45c7bf9bbbeeaf14a2cc8ea6a3299438965f5314dc91cd4beba9358f216aa4f9d5c29ae4a62d94de9d196e323baf4482c661e51552ecfe3c93824163e78d41a95beb6f21fb198364a31527dd399ef8825b532b3721d9531a2462ce8e7e4f2994b463d8c584cf61b1e4d45b9d78b5832198f52a7861e9315b1dc31b8dcf8ccaf344ec48247ad4be7e8cdcd65236249776c539ab13ec492d07ddde967e7c967432cca18b3497d2b2ec46297ebf1d14a99e9141362f15e5f73a37cdb68e2412c7c77d631b2eab4eeb02096cfdf5f55848ebe610ec492ba884b21f24fb79401b118428aed90faf747ca7f58f619617293c8d796ec8725e99f836bcaf8a744eec3628c52a9da9451ae76cc87e5e049c6942b8318d7da3d2ca94d7b5532662dd5b57a58ff795657dada3c2c060d7f32099155e62f1e96a5904168970d23326aefb03c5a85ce8e172f4667edb0a8fb59bdfa6e9929df3a2cea8dfcec4286c6782f1d16fe930afbf4d974b2770ecbbaf327d235556879e5b0a4856a6af5bda542ba7158102e3b64126d59f2a3705814fd4929253b856c5adfb02cfff2c4b77d4e35ed86453d739a45c86d58143a830e6ba3abde65c36299efefea57aa7c5dc3f2a98cb9f4c7dc8753d5b0a0ea75cecbe36fbfd2b0207f427daffed74fa161514fe7d29a36af29d31996c757870a3ffd5a6586e5127de3ab3d8f6ba80c8be2e4bade0efac977322cae0c9929a227e7d53986e5243b68572a743ca72e86e56c676718964c9e1a19942ac1b0a434989677ea7ca6c92f2cb9cc1cf37e68d9e9e5852577d9da3d5ebdfa3c7661a1262e84decf8505d33d5a7b9eb4bcb48505992e464a99fd29a58545216f3eede356cb0f6661e1f3ee861f511b5f266161594c888e9b5cbcc292aa514fb917adb0d826739cac7a6122f52a2cabd5f64995d2302d2a2ca7fe8a931e7593a65358fc4ebbeaa371f7258545257a5264b767a1c5282c7d14b2cd6c95d8ea100acb2ae6aa3c741262f44f587e9d3d5da72e00272c67a1e5680e79d9d16dc69249d75bdb5a48612a33166556356bfd5221c2652c6992aa654cb6fa674e19cb26575de4edefc958d620738a1975994b858c4593593386d3ba4d4ec7585a2d3c99fe3bcf51c7184bfaff9ebdd35fcb783196b430913dab9f6326c6e2be692144cfb8187b188b4a5bccd37c979055184bf2b4d2b06b528a3383b1a46d6bf3adecac4466c0584ca15ea22ee59ebf58fa2e39af47c667b87cb1304a7355f57795ccf762593b6cff5385eb282f165dacfcd135b7ba3cbb58f6a8756309adf3b975b1a8eea14727114f6ee66251d8b98f6ef97d225c2cbbf86426f42b99b3313f6eb1a4956890adbd4147a94bd8e2782290388a11131283821fb658b0f711af3d3e976e45ce10365e9904e9572645127fd462e954a69652dc5decc94b8e16cba765aed4ef93bdcc514e5a70968ac481923a8b36dbaadabe6894d958cd32f4d4ee7eacc9ec0f592c0a5365ae3a37c8dbc7241a6cac01248d1b005103c58f5830ae2a2eb73963eb22a3a11eb56444c610eae13c493182b058d8289514d2b59891a255c216c7498a195b07718384053f5eb1b4c957b4fadf6cb22502a40d1800a9545aa003c41fae58ce9b3a88d82e4bd848549133e41cb662d195f220b73609f71c562cb9f0dc8c7bfb61d45bc592ddc9cccc6196b0191bc20f552ccb49214d4b66d2d175667a383c154baf22746f7dbcd05178e975d0c61a408ab098e9c0c30f542ca891257f62f273922b94532c88d11bfe520bf1639b31f9618aa55159b9a1aacb479cf0e44729964d3665d6ac5d099b5aca0f522c89fed4a7c5bdafae55091b8949904aa55249b31f829090228b148f6239476919b5bc96c9750413444284748620ffc00f512c6a213f85bcaf1afd3a144be2b5124af67678f6068a25995d6dd8f0394eebffc462a86bb966af6ffad713ae464da2595edf3544e75c5a6ece1ebb13cb1f5ff73b3e6ec5e88ce107279636a594c9d59c1bc40d12921f9b583a1d3fcf394fa13754a91c1c248e6204122328260b3f34b1e06af4939642e45f7f463041a3a3f02313cb9aae5f9d89ff7c9eaa6062e985efdc86de97583a9d53e7e4395a5df73f2cb1182a33eb0f79d7d8ed8f4a2c2b59a63cc89895fea0ffa0c4d2eb98a6be37223baf7e4c62c9dd9312e7ae57ab4afd90844193ddc87cdd9e99d6c8519d4f52660effcf8f482c9f566da9e4bf350a1948acfa6db719c7c63679ad4bed3228ad4f6c50063f1eb1f81ff48ed4933fd76e137e386259a696d13909f5a8b4da88850f52e9a9b715cd362316a5bed05166ca70a1d92216c48b8f8c21fb3767fc4311cb61c5fae8d2e2d57e9d88b434d3b8becec6dad3d97a61ab5c95d6c1b71f88580ec27f74897d98da1f0b3f0eb19875fc7ed8129fb55e97c9f0c3100bee9ec3b44cb3aa4c7ba4086f43ce912298422cd8cbcd76a6418777152116432951ae3609b1b2fc12b6bd810f6241e429a1372bc7576c2a951f82585ed5b9b47efdf00c9d93a55239275fa92421427e0462596a9549bcbaf41ca40c22e107209695e63b3fcd7af5ca6d093ffeb0e8a5a5fa0ea14f8fb87e5896531f7757558e5277c8f9d187859739f7d73c1f16558d6bcfab41f88cea33093ff6b038a6bd46940899f0430feeabd35176cef9a9f7c18f3c783a082d2f6e3385876db3b573545ec63c6dd4d744cffbf3e30ecb99359db497babdf3f8c30ecbbf7baaa693bf4829d76161d3cb482947aa7ac9d061518b4eea7a63efe1c71c96ed7d5669a53ee2da92c3b286c9088f2fbaeab43fe2b01cde492b8fb254faad7ec06171544b7fdd1ebdde4ca9c68f372cb7d0352bf5d7a6bbdf0dcbe6ae4f47cd2acb4da90d4bb6312293ea8bd332870d8b2ea3874d99a1e3b6bc06bee33ee633abad66075dd1913965e6fb871a16def4d3e383b91632f5230d8be184145f3d3a28f9d50f342c6b4d97426b6816aeb612b63f2b6222648dc45c0f7e9c61f1c48ee63ef5d1a5d4fe871996d48c7417f16dd1a3ff28c3e26e0b8fab31f259c6dff8418636a8e6cd6cc7eeb606977a5abb68195d5346436e8ff0630ccbd994fc95197b84081d0ede2822e42405a55239f2430c87968b1d5d1896621a326a21ffef223fc070542f27eb4bbece7a233fbe60ebdcc66f53ddcf9871e3a68ca1e5e8c6ff75f0c30bcbdb397b38fff0d516fad185a5919fc306a1e1af7b2e2c6e1ea9dc7fb43e4e5b58ce1ab40621a558d7911696c7565c6dcb6c884f1696852a25437e7b8c2d2cb0ba7d9f3ef72b2c88c9959991da39e85658ec9e4ffa465c85c5a04a9a4b5f25aec3545896ed425caa328f513a85c5da20c4eb98dbd7cf258545f5519ee84ef7afc61585c5a4aabf4a6b416149ce7dce9d95d499f43f9eb06c67ee992f579de77f38613154df484dc273bfcd58d2322a351fdf5f9dcc58fa1c99f97412224eb58c05f32edb1c6bdc334a198b9da9225fce5c9a52321666a52aadc3f43b9e90b1183cebeecbbaf6173ac6a2b77c36dde41942c8180b9e457b899dd1cf9e622cba965c69b255c6df89b1ac6544cc88573d1b1fc6e2acd966d67f42ea5918cbab838bb56d9de16f3016455d7c3495982a293016c39672d1d020645c7fb19cfccc940615e253e98bc511df3264e64b91662f96b588aaececda2e3f5e2cbbe9dfcecdad51cb76b19c231f661e665f64af8b0525befa36b637a9cde762419ce7d8b6932e3b0b170b22559ffdd698c7ac5b2c6ed0fba8566fdc8e2d96348f6caf916db67e2d16bbbe5fcb68a6697b5a2c778c61756b2835be9ac5b2b99062be5ec9f9942c16b4874f8db3f1b24ab1588c31dfc9ee91bfdd81c572f864e27d7514eff62b9693b67ca9a276c5c2bad0efeca832acdb8a25e5a75488334f0058b1fc62466f92a1fb39a602b08a5eebd7e4339b52c592da461dae6a747c52b1f4be225f4793eba1624967cea75f576a999e626964beba8e37dfa12916657c7e78ad5476f7a5585629b39be84dba7c522c7876ddfd2b65a6ac46b1b0f94b98bb8e49bc92289694c8244ea967ea0f85623979e8601bdfe547790a0028963eb33e2b2df654f314804f2c88ad932796eb4f88b8ee18b3efd489e5ec3166ceba99dcb58913cb651e7f940c35b2b46913cb716b749992e1f3479326f22c4b8d9272cdc4c2d8c8ef184d881715138ba7a536d1b0bdbacc7489658fd93cadad50328ac912cbeb2ebef9efb5085daac4828ac7fa4ea234bf9628b120550a1553b9e23c6b124b9a5fc744b8d4215e92582ccfdf4c2a676448456241ccaa2add78b2ad82c492d41cc59a14754a8b1eb118eb34b80e21e25b77c4a2f6997a92d13c576fc492b7fa24b30cee326c462cdf48fdb8f3d332c668118ba9f1e36ad5c6b42a52c4f2ed9bb8172b322aa2442c7af693a74d5f86fa102296d4350b152f93f95e87586cf1b9aec3b80ef50db198ee336bd05d9bd32fc4b2bdd2ece144f67c4588c5a4b33307bbd1f2518358f690d2855e5d1f834a100bd25785befc729d9302b19c25dfbee9ed3f8500b12c2eb35bcad149e8d01f16fb3b5cc8fd26371d3f2ca9562b32dc2bb942de87e5123a3da51825a5ce7c58d4f664a3396b9dce7b583c215ee99c6eabf3ea6139a30995afe5e485340fcba929e3eab51e0fcbf533524b31e5b9c77387459132b986d4328ad48e1d16b7ed5ba833e1a39a5387c5cf9a42dfbee8b0e4ca67837934f9e09ac3823425b370dd742f5f0ecbf32a334da9dcb8290e8be67bea36546cbe101c1647a649194ce959fd79c362544fb54ae6fb1ce56e581835cfd4c1b3d6726fc3c2bcd459bc523b1b96948ffaa8b3f8e47afb1a96a53a99e3c97796d976352c692d368ffc34335b7b1a16a50e1be1da63f0133b1a96d73e4a0ff9f144643fc392d868abd110a7f775332c6915132b46c6cbb0dc59c346eb92f9d3c6c9b0e06965cebd53d5e7c7b09ce483cca132e7245562583e61ffb2b52236288561d1d577fea0c49b3813181633a6cd28b1939a85beb0585a654e7d511e55c70b4babde47da27fbbcbd0b8ba69da21d663784ce85a57fdd5cba1e4e6c7c0bcb3156de768990216a2d2cf7e8aad2163ae5db5958d4b92bcc7eee4dd35858cc2d6eeb542835275f61d983de8aca18b6456885e5d13ad878381dcd835d8525253c6330e97d26c7a6c2622bd11aa74b53a44c4f6171b5147a93d62b946a4961316751adbfb2b3843e0a4b1f3ad5989e32f33e005058122956eff8aff89902f084e5d54a6ba5ba54ca300a80139684e8583a8d1236fa6ec6b28a6688ae8d594a3533165c7cde28a3cbcb58b8f5ccffcddff8282b6331ca937d52eccce4cac958ce2ea1a555898c85d3a419b3a5fedb3cc6c2bc14f23c5b8c7f8c8db12cb5b3d8e6139d8d713196958cd97371253e6d9818cbb1c376c89ee99159c358f0ac4b37eba03247bd3016756a394db23b8bad82b19cf24908539a2b3c058c25d32152ead58f5246fd6251eb9c57fb2a8d1a4abe586c21f74a0a155e5aeac56254f27532b151e58f78b1a419bb4c9f679d5f67178b5198e9a8a514aef7a38b051da4de75ffe869e3b958925b72f4499d377e8c8b65e1274f9ada66e5ef2d169550b2499b966c7d6db1fca1a5bd9ce89c325a8b05a163bbd4502efc95b45894ef1a153a3b9b94b3587eed78a695f89759288b45315a9fd4f020c775b15816fe526650d1a5b46c61b12853baa6d4303207d9be6269c783d0a9b5d6f531eb8ae5d23be739c768ad5bad58cef95ed50b99f455cf8a05ed2e424621b4e6f4ab581223956cf9952a16b3ce79dd4d3a5728150b2a85f89c52e6d39a848ac5d6cdebb9447a8ac5e449cc8a959b624928a121f38a198d7b2996c367eda8b35a316f9362413e87794c3267bcada358b2fd24ea77f4899055140bafa4fe93afc435c61a8a855979eaf37c496daa8262b974d4b2525e536ba17e62598b1d1952d46f99a89e581452d89e6c2164d6463b1190e8a864ed2604b248180a85028130181006a4e977150013140030181426114603128120c89a771480044a503c583426201e18128e04647138180e85c1a050200c0683c180502810840bc1d622362220a81f29614b3a9e12b2c71e101e028790104c8447168cc4bde5d3673996e5cd5799203f93c5e5c5c25aa02e440bae85af1866ac40d400b5b9583bc5b7e80b02ef83f2d030a48404e235168c959110a484042196f8f6f3314f2808f540c7f83fba40de10160e89d1181ec2460c66befd04326273accbabaf3e99fe5f3696173aab83ba11156c043f1918f943144c160194872789a7980283d5640a8a25c299a0151890fb1b9dcbc023113899050136812b2ea13972115564177bd1b014a80d51c19660b78cc4c8360860ded6a1ed57db387b361ab5d56d0ab6024d4157da946e454d7db1a24fd956af29ec2a36155b49538999ad8293470cc5031a83c076044c358b9367fca5f32aa065343599e7f41437607369801dd282d3674ee13571e3876a3c6b97e527392835a8adcb21f3155638f8fa002fd7761d713299a88e7a337b7db92e1fc819aed0c691fc3bf6e4be903558671124d6229caee642f96be60711d2c0e0a8b4a33114264195b39d3a89b0bbdaca7eb4155097fc8361081b8b892733f33850328a811ab42593cee100af0dd40aca0404024be9f446d0ae66f1030d68d3cf827f2fbc398d4af5322e386c83d5ede923716dd7a73d31184b3508752e0b65ac40874e7796f00d4e4575d9493678d0a55bb1e6ab9c427a69313faa8839dae2ed610c8c93248e394023dac83d4f74c5f3fe0db1cfc4d06f171f8fb3d24466d309923895de810b6ffd848a3f42db8c1884b5122954907e572b358dbb93f7622371e61f693d5a69dfa83090ef2c4d79ccb385a4f2b3d03be7a40196786be55347d790cc04a77beab6e8fbbdd7f812f3b4aa1bafffaf8c4bf159b93ad5165b846bbd3e49893e42308838afddcfc521c6f4d9126231cf63ee2dc456c8981a0f2ccab3bb382bfc3cdca1f582232907b1f011aaf8296998d0005a4f33e8c6c661cbc28bd2bd403ca5c5dc149191be4226d3944e424b2b680cc1b6c096fe7e6484810e4608ff8539d24ebdf13b5c672525f8d458eadaeafb0be14015b7a575af82330631c54adfd8bb795c0e607eeee1becf3bd7157788b9657d0a4e81be3f91caa38a984df2eedbff4e264377d439c9735ae0213f5ed17c7a03a1b23e5f2a4e2a39f4a5d71592e25a2906a1e088b89268893a5a12a8002f6c5138481b9cabb39f5a4ca268109492223c1b01e58313dd3cddf4477b77dde9a99aa2bc0cda6d83dc40260c0ba60ea88d815e19b69298468bb8109b4980068261fb0c5d699a23e2683d6ab342cd18b7dee6dc93a3acab5a5b238d56ff45df1f9c489091f78349982b99ffa479afc57bc81660c0349b288abaca603764f8c1cc48d2dc1f3e29ccca8d444b02df4c59387e55dd6064fe548b1305fa845520f96137d48e2374c29c8f7479ca9ae9ac748c1d9407feec012b09d12c25014b17e571768f69fd15c0b37d7577a28b1a9ecf4578f66cad8a8005bd646c7ff0ac444aa011806af659f65597ca150e649b22631ddca2b9c134834e54b3ddeb5a7389f955b3ccdfb200b7e61f67a1b22bab020fbc43d979af0aa36c8670745e9afdde34976c9750611cdb5db8649f64d37a9a62da6c6bbdd0e4dd188de667b669f8a8daa5ae30a8f8cb5e99a98f4855c68d0c5ffb51b060085aca29a4e1ca59f67071aeafe42a9519535b009c3ed34d7eeed6cc873eb192712933d30a409f3ad1374fb92a20596ae5585da9bf43840bab467f736e8206b1a05a315013a7e33cec7c6f5402b329602a58a6fde857646a896cc095e96e2c76a39b2e1aab1b3d13514c19f5c4ee94bc265baaae6884f9a548ca88998872dfb3a3502228f68f090f66c18eb110b507aa495424db0d1a0d14fe329866cc574368806b7a4959239a9b273cc65f2aaed6733f1da7627ecc69b64ed1f49ab9593005b36146d3758a265326b023e226810c54d684cea1993cd9137e324cbd26db343f36ae1200d1d152ba3d73c5b936853f508cd999017115a9782ae3d13de802f4dba8e4bdb3c2269b06700cb088001f0003f87d43d128db28a80834000474213cb8077e0c8e8cc118a031892820c00730c3ff608c8f418dd7411a5f83371e06613c0c6abc0ed2f0b9ba0377fa3498e30e1a59d68dc9f276634fbb758996f3488ffaa170aed6293af806056c4003f20109fc0c41e1e0bd210bfecbacf13708fb6111d66b50c7d3208fefc11f8f83313e828337a087afc11b0fc08117208d5f928e60fe48c101b891a48ce05f38024d83d4900b8552694957d34a2dbf25580c2bb05a0bba885678712ffc1a5c198bb1406b525144a0cfc45f8f2b86d3f5bb044e8b2cac271cf0bdfe750440106d20e8b6da8f9b513fc4c01bd0e00d08f0009cf03008e335a8e36990e20318f001329275bb97973c588419ea26e8225aa12a87b02e180d8119f4c00c5591407d944bf2924485946aac1cae25892f18400728c003425003a801413751092f580776b0000f50c0136ca01ba4c3698447f5d08f868800ce04050a7908a1911bc9e00d06d000c2e0384872f55edb34b4a9f3f9b6f5d3422c0a2ed5a34e0ab1bc881210f4dd9f12ab3e52f45a111b7e0276dd097b9d751f792cabee3ab2fbb3750ad94d04cad0a39a4ac1079b255c0c3da0c86a33f139c9d7516786168b4566ae32f8f55ff0483a24674000720a84b215cca01dd2bbefff6f2924c27e291aab0029d111f494eafc1ad41060d2b272c5f5241f87a378dd0c7e1e2531d626dc986009b0f9aa6e2ec927238ce339e155c065b23128fd4cb4637e1e44e870b499800117eac94a3be3b64e6d956aadebbecfeba88dc72f7468e5752c2a24c7f14f5e62139c3458a10aa9d53e38137bd63f95ac3e88070fa3c3cf536e80b49ceb26a7f080ed9183f3540cfeec0c1f2e9a62619cde6dddb724b85d4cc969ab6587f99af7ff7ae4ec79f80644b5b7dd9998bb529cfeab069c1645e34629bfd6000ff5cfa846e8f07ae161a4a642a0ea73380aa4d06e860fbdb6c953914821e6671e672cdb4bccee6283dd93ea038b0e3f229238a82859860969f848943fa8427a058a0f3c1964a4e244e769001f33e61427f7ab1f3bc16e3627c6a7dd5d2948b4ee1f822730264fe45814e8ee75ed6b5531fe7ddbc8aeaee506cdbbb9f5c25067865250412807799fb3934886550cae31376b94f0ca20e0160de806023e702e3e6190a8f0c54d0bc99fbcac84a1ae2118e3efff7d3d424dc6bc025d0686f9de248c34c7afe0b41561a6533a96187c88470588288f0d7e8323375bfc7420529ca8bf0d82dae0ed845e3ec18e2cb9d2d73a58b8bcfe4e076a6e4b61c6d7e9d8130928263baf0d105e05d6d9e2ad814a2440b92c40af5e98b062995c3ff3345df0cabdd53883e39936431ef552237a80614f3049a351e11029a0963c6d2a89252860aff0e0a105046a899ff4c29b09a322a4520a49af528902118940a63b9d101fd667d7514a757923400f86951731b7737f9e53b1830cd307d3a5a8ed60869467e4ad55893010830325d3dd87dbee9e8eccccabb9cf201c758ccd64c69f0ffd186329d3c8f8bad150d6bb0d3b4c097f07747063ccd3d4fb8ac12fc5126fef6fdf78006d70aeeab7d245ec423bd088352a453b6bcb1d5cc933b3f76cd9a47111498b74ab721e292e5bf448ed51171a6cb19ae24da0b70152da3dddc45a394287f1627621a8cadae89435b5bbab67a3c5bced2d0b352a2f5670d42ee2bb22bb2d6b31597e911196074eccf3f36832a317b5f32a3061248f05320b5e20e48f06ff67c31a27f80d1c1380dea44d474be82f17104baee423de45583fc0e8d704be9099f5460438648cc512b1123d9aa6e2679200988b3e8c102a8f0c9197b2ff60bd3da492d7e8d884a0929f53ebcb7ec9873702bef61e2845942f2526a536669b05d9368c1a92400174192291fdd30c65487d965aa31d5d360df68c05fd0b7a6b70e5c2544abd48b459b7f494ec08d3e2cb320fe7b5ff00600384e6c2d71ea571259830977a82985bc23561c7c2316863cf0d4f0ff6f6fb25da8f46e100aec09c629c8c11591eb9c741f1db54821eea9090d4da42b16936bfec840ae239b82701e2d94487d442b2b7936a7ade560ee985e2a977304b87bdb48835432098f9cbe4dcebd91ce8518f8cf8eb8e54b2106626ec22ac516104e07809deff975dce4e413f4a4ea5c02ef75300d517e322ecc5b1faac85704219de1317e5c7e0a576d86e5d21bc7349cd52e5f5e2366b92a7f47a9e89b08a72eef1effe470081b9dc4a997febefb259315ff3dc1a2bc16d922def83174d6e3a1643a1a2c3d9ad9eb4f3731c1f2cdcc8a5f6e95d188e75ea3a2ec717a3f7f56e2a960a151f74ce570f6a466ecfab39536a2ff3a975b1deda4fe344764c30a34cab3707075ea770b4ffdfcbdfc91cb9d4708850299b8d82c6b081c5de77ac4ab3b725f5d531af107914bceac5946a537cc23b8e527f24dc9ba9b3f8234a6a18196fdfa413784f17a3931be29ceff74e3107273502ecf2469eee45fec180263b19f8e659bd68d36e6482c01ef5167ffeda0edc2f9e6fd54a4df6f9d0e7c1d445a1b108cc0e79cfc915d01e21aaadc97cae919ca203b6abb2286d3df3f39906e86c108c34ca364e9e6deaf24ef06f7a563ea2cbd91c330210b7f73c7ccabd8b00c3b0d8cfbfd91e3a5222c1b8d2f7ba6c2deebf48393966be136c1a3035efa9334c4f516fa273259b1e10bba7d42e30060bc6cbc0ddd28790f800e2ce23e80b408cf2e817e77074e09a442e3911f7af231ccf04b1c4f7470dd269c74d06ac880c2ff69ac298c901100606c59db520d882e581af69862b2a9746bbd2737789eaafa4f83ba46eabb5103dda3a4c34906c126b4ab09f697268ae57a7b00a7bd14f7572cdc9b561bcc289e4bdddb51ea3022c8c91ba6fc1c644650cc1ccb61f6b8c8d656b00fa64f5fde624a0f0abb974951e4e3f086b0de499a723dea87090e27b68b1d6bc3c7200338a8cad853e0a7d5eeacb63067bdf6d329888e423d28b1ae4cea2810ca010de581d7b9f6fc63afd491a3d4f5bb1dd7a60cf0da4a0f873f7a18c91f36da6c5098914e2c901d8507ececfdab7c889b79bbf0ace350339fdbbe3412725058a65e723971b3a1bb5106eb7221755a9b7c9d9b5970ee6d105a8fe36f8ee80d594cbb877287467b8d622f8eab126ad795a2b33f8ad1018381cf5b9675c02e444a39c2ac66b3859d654d3ebde4d28b3f82f6dee1860db0ed968196c3c0cbdc61ad8dbe915c9e979aef36f75a001dffaa3d1c1af17740d35cbe022d0e56a1958cf2c832a4e32de5943eec218d8a5a785563075a3053c219377d7a762dcf4eb14cd4e9602b1136dca39f56610e8aacc2a4f76edaf031165c533162890084e358fc98691934249023b16a4c7e92a093d09ade2792d07abf20c9cba490ee805aeefcda6272b258818da5e59270bb8fae6280796bbfbaab71df81f1f11832651acad8dd3ebaa3b62aa86a13a0800b0a9a08c5838393c2f40535752953ade819ca4ca2d9ac956c444b74493b7da039400be8d8303a0ed7508b60e0434a8535afc3184fc7fea8cee0ea790621b3648c8092a6b517ba0a138d6230ecd7be370737a4ee88149fba6fefddb19e3941c143865e01b6ca041041395c277c517716e266e7469143058dc0117f441a131c17ed1b7b83a6614c6ef4b5ee022eab5f50d1a848149214bd4f922e51dc33646d3289963ad70cc3953059b8758d0573966cc2fd2dabac419473094bc5ac0feb3e6ca910c8252ed70c571ad2bd28195ef60ecb46e722695b7922fe5c92cfa4c5e4d40dac0600760da1a716abaf43edf3b14a52a07a95d6a0dedb204b46906dafd24e360ef3870c870a8731f7ce5b0e34e92d0ac12ab117240dcc0880b18295038441fe8a277c4c99fb041b03092978cc0afd16fef4194dc65607e624173a4e99560bad9b0faed18e9e1ff49e372140629a9b0839e969fe1add64f8298bb2746831d5d024e5d22986364e3cd6f7ebdd0ff68afb398636d1855f6062d63f9c9507dc31f46da736d70d18b33fc663fcee08b466c193cec7787ac05106d701fb07a8b8a20fb806e9af960c96fd81e359837be71bb12d39a835e660f09bc942e40a530adefe3ab2a9ebb549b53dad7e3ce5d7efbe354aa09267cadf87e58036239e7314a98213a77063a70dd091da7d5f2b2254221a6a1099c6e6d00b9b798763d490be66ab7abd46df5b85b60e7f9d287d0f189aad7596d3adb01ae36c36ef4adddfd16615e98b25111b4fd187cb6b79dd092d9d87dd3f716958cdc5084da9ce5a7434f135ecbba93267c0ebb41c8b7d6e5d153f866b39e635643a023fd82ffe1a4605aecc62f7bf52d2aaa0767a87e0715c2e11328d7376ad9123078503c3ce42e746fd22b73c8267e7afa673e0db45c93c97c218621d01d52d3a66417563bda792f0b51c360874ff28d627e10b426c9a887985d98afbfcb41ba2e0f0e6148af029df7af9d519f1439dd8bb895a8b94516b513f4c4a4a4410e47cd051a3377df02fc6fc0617540ba33ddaf999ff8d3c0dd6e6dd32fe2999154c8a830d56ddebbe488c4dbdcbb6ec465e4cccd57eb1bce45abeb481b64fa964add0da3bbf88d6a9cd4347e4aa25239b5c9cc843179daea40cb5bc51bbb3d229d05c135d24959d5e2fa9afddec5dd426f56ad90951b2adbf711634b3a2e485a9e7d793e2a67a7e8ed1e53da7d24b8ba464c84d3fa06fe6c984d4a530461f0afb9219859a05a28aabeb54b6d50d0af16bd0cc2d981990e85d96acfac9ea3498b2e0d15c79515d2a822e6e1a35e369594ab784924ed1765bbdbc6865a76448c297044d2cee221502736333690d1be924012e9a045db30fac04d30e732cb56c1154e135551b631dcd4cadd108079800b47a22b19873e3e3adc7840e7b27bea2f2ca1bf1fc98f269a754c50c88b6e6a1f4124eec373890d8b2e106f7e13baaca032b51e830836021df841022a40011d90011710800972601f77104801099003cc0264b7437af36a3405c28d281a494b21d014894a64f7d39d249131e32a604c969667d2db20cb786a41e329341f83732bdb23e0769784136c0bb445049fb0c37a8d60bc44c20c1c5cd8d617690640f9a23a3292ebaa83494b369de7c407600a11929915103e27cd2f5294b022afca8f39a57fb999b31649975ff37663c114ea596915ba4301b9349e51853c9f4d492b93ea38ed94198ac7c5477ec50818fc088a94c62570ae4b668a62f00d2f01314e00f6db4abe3bd3800cb3d483b0606c10813a500b3449f35da929ee0a3e912d3f4b4ca1a11f4c3229988f8454375156b1c7479a5d97c005b9ecacebbb1d4e0b8e7d9940832ff29765a989ead901c24f888b017fb28e7987c442d7b82324bbfe195ffd944a96c85418f121d0518da685ccf14de99597062a02229ddae0b215b10b493d577b93470d1314ed2bde90e210965bd8a869b7791c913a32b4a119c86bfdb51f572cca450ae010dc3654647f36c5a7a8b91ba64eaf2ff66f1f52047762ea9da6979fc8beea1000683879c32e3d25c2d63da2628dd55932b921b3b8ff42c29747b1f034e1cc6315f9384f4f186cec32bcd9474c498f53d2ed423562eb8845ad264622943b7330527146dd51f3ca21e56a0d9bea97781b0a68949660f93592bb7481d9eacc7c59cea71539ee0ab30153a2bdf93f6c7c4c0ecad7254f9cebfc116e6347cb9945574fe00f5555074d159c9ef9025675a9bdd3ba1b8854bf99548a527c6c64051d36712099c648ab1c842c35ceb4fcbd2d7d8a22323157f44b4677c08e8b2a48272e248be12b5f92df9822e22907c4b2f9ee7d2aa4749aabc7ea9a549eb582d5e0256fca1140e740e97b37d005ab439edabe1d1a90ce12772be6e3b65e8a055ead476cc03239e8beb42c8cd740531fae9e3e7b13eae6bb7ae21d2a74899b3ef2cee055e085ce839b875fde0126eac2ea5b72c0a2db540967a1fe41344bbb551b048956dea0399c989c6e3254e6d707acfc338db6699acf48bc620464b3f2fe24049322eab80d0704ffd4d566762a8e08051284ffdbe57730dec63a21b55de1fc1576b15d602818dd6690c504a63645ea9ccbf011f9d5e8194503c917186ef8bade995225fa5a008afd8eb521e49f75831122dc441891410c38014c1e01a0149e8708bd5251f64d2678f80b01a7d2f06ac0091e62610fd58afa3aca2c389cc0b2de5a33fab00d75fa7023d17e11c2f3230297fff2824c44d2790f53aae892c2a50e52457533fb422e6c8b3ddb2ace21339c30687a841a17b0a4674c9d3d86365ec47839c79866667158117ba1dbda37264faa71efc8343954ffe8dcf88d11ddc4a2d5f7f357e73f9cbbf2420aaf7449aa17d79887bc894bb6194cb7ef785be76681e5b0d3ddaf5209b5ccefe22d3a0c170887c4aba70bdf592a6cbda704e48a2e87eb55fae5b38d469406bbd3ba18cb93deb2a247b90b23ee8f0b4cd3059a7e0bf82f25ac6178598f525b9891ab1d6ebbbe11154b375ff456d3b92d4ef60f72d573c6c69a9400095d15b9ea8b77d83f84ea3802f80ae75994e25096aedfddda1e98a24bca8700598a97c81f660773010ed25de38c05fc92ca7ed3e598962010613e165aef2ef041f0097b701ea59f53850172d415cd02cb029461316d0708d8d572c37d15d41906c06851e1d0d6b37a795646a9c5bef13fc5517eade8250b804f7d925c757d9eb941fc3b6e43e77b8f0b69ab020fbb380cd5f4b3dd894d8721003b06d8245362ae0a220a989e0ec55d22ef76ec46de6e790a9c358ca9a200f580c0258bc1ef7688e7e872e8be06c49554621614806347ff566ecc60ffe5f9a3efb44aeb9f5632c45fadd92a79cb277546ece392e0f5a8d81e34bad1fbd999bb04831d36f69b30eb2b008ccb0da6f10d0b54285909d48c6aa5bf14b7fa9b989c04293c63fe5285e40cce531c8a8d2353f59de22c86cd111e843535e1ce26f38f584323cc7b9ba26ea2bfbf0a1632989680708bbb0ccf31e19cc5182818e2045d0f7afe751fca704afc1a92f43acc8cbae680334571979143be08147908ca5986da23076f44064b0ad4608c0a609e3b445650051b07c14b9b08f5f700be7d7a96694f1cd2e5f51cc247383a1671916ab871dbd8d7f8980374583bf8f28f54ff04703cf2edffa845b7a02b0944a913c31480d92a3f1866344809fd6bacc8405d82248dd58d980fbb424423408370136844e7f67e425c47e5525c28f970edfe9ccc13b343fe10611d7ae27115b237415643e446740c0d080ccbef0f9b154569082de1e0b716397af17900902550a30c192eb624bcb0f0e90de2fe4c6f72c8d59524ac0333bac8f86779c964c9a0c5ab0d06cb15b7a5fb5c6b3f0e00e0e7b9e1d0294baf9a3b502df9963881fc1b4fc1ba43fe10ea605df6c57d16b0d55ba379f66b4a24d185faa8e651f03b5982405d61f08b74f92fcd3bbb8bd0006d2fd1e91e75587d8bee909e904657bc03b7b8d74c637705a9eb3da0148a132baf428f5ec8ee58b918a2fd4d8d80cdaede0c9a25b186368f76434310fb98f726fce938b13593d1c141b863feb9eecd1b1f66898450856aa9afe10e41049fdea089699321fea67d34db17f495c78b332e72830ef4a81e8e579d2b676a6e39df7e794c9e17c8a1eca66e8c898e7171d2feccf8bec321b7e2af84b5f81bae1a69833748b3abfc1774a52fd74ea7a76caf03aeac830a87a940d55088c3a87a9aa5580f6968877103741c31e5834a6537f3560cc2e232f3477b1ad30e433486b3ecccb78a6da1d7e084f189318d6187211ac31923c38c720dd11933342605fdc6ce8a71c1b28610f56a855fa6a56db136dc35b26197c942a1c515e333e3691a81ac2ff63ac285038db766b250d9e84a1f23991a2eee3cbca47d783cd5ecbeac27cee215632b4a7283bb821ce3b4052ea7bb7088e9d1ed97aa9e2895216793b3a35cd83ef6beffe99482210edb116df791b77b1f0253239216aa5d1267022e50443fb4a206e9c88f62a97c09564345c8893462a3187510844828421c84a1294a507cee0d32bdc98a0f1c517b224ee5ed22d20f508dc2b3501766017002d2390a3fc4e419b0b46879a3c100144e150b837016685c5aa4a2a408112a55c261f3219618040def3c0f141bc2281a33aea2febe190d8f3ca91b58490b39be36401a501cb069c32cad4c6143078935dde526b7331e2eec38266716051b6f2cf8d5ca411ca5875b7dae5837edab0185744e27c4741cb1ca02c7e4331815af6e9560b1d714cc3a96d76b970f60df429b74a59bef34fa5b5b3d96e3a009d7f11bbab0880ae6e1eb0a00c5f6ee6655d4e13e1848d859411fa48738ae8df70ba2914202f8aa7b81079eddc84af1eb88f6f20aa18e7addc6813594be364f4f761b1b9ca5e348be0f499d467057bb342b4b8ca41d180a7e3d5438f9bc6b843e2a8c552864265804ccd3bb74416a158c66176e32a8f752afb3cf0e8f30b7f7bc8a9b2e97deb98edeb4ca4852266a258548a0c1b139341b8c0d8783eaa4e5e36754f0c011fc32e3a945deb416b0a8d7e0415854b2a88502c5ab2864c4439e54cb8d23737b09512eb63c6a4a13e30bf158dfab9ce28b6c468008761484323bfe77682ecddc63a8de5b40253db99fd23f68f296662f37031253d7346368498cf29ea12d9b685bbd158d6309921ae0002ea0467eecf837615888f64778941c2c4ab3ba609e023827b07c06a2e69bba392864b3554782a0420b8152e2dd6c13300b36e91182e4dbeebd0b3d88f9d9fce2e9d29da1fc1ca29c1692ee3ff7542eab20d2b0239b4c1709b2c2c63477f07ff7cfc84551f4c7e56a4718109a00d64a36605817d0397314f09f1dacfb32c144242cacc5c551c9fea2d025bee5e69d979ead6a8acaf2cd1df57f573b66bfce31786ab9056910c529436dc845a202bdea26b2e038c044e3f6b9e98ef42ef13a3630383004fe8854b33102e800492400083e7ecce9fa7f774861885f48457a6c1175f10023bdafa0f003de49e779ae890e1b8bf29eb71cf5b10413191b134110a7908409a97b01a09385b472506caad8ee691ba997da42e6f1a5911fd64986282ecc7ce221727aed5c996612c242c0fd396fd623494288992cfe9058a255d71c06d91771b4cc89d51c2abb12d6a1d14f48d880b8e05db7931cd52868c704a40e24a06f034876bfa635239b4489e35fc3cf4f311f93bd97327f6a8d6f6badcf51961909f6cbbc31303097d711495bca08d681314b95c4f417075018a3813e313e738e4014b402aec28715c864fde8862bd8298ebb1653d71ce835f79b2c23ad72cb8b8826e88a84184aadb8cdaa71526ec56b566191be5dff1cce35bb4db1ef5b96a4cf4c9577fb191ffbc0b437721b481d1ac70bad49dbecaf570e272b4e3aebaadf2254d6a492cb592d869b41bc950154f78e1fa74398be78479bcb9063722039f67cdbc7414e5031c99f3bfeefdfaa2c4cbe721a273f89b020f193437b70331940852af11f56c6b959fc79fbee784d3b34ca36ceeb712bca58470d3d6db0e792245de08d0a4f40fdf016189cbb3efcc7ed05477dac35bdcccdcb0b2a94ae1158f044bf731d541f0793d63ce67251a0d920126c796a016f5567beff370fcfbebf5808b46ef2bbf7d36799eaf223b6573b5bd64cc358303d38cc6a382b745944bab4c1c471c96b76f5626e0042e9ba8122c058ddd1069ba7f4156f88796b647aaa8b46a79c40f6957f33dbc4006c6793e24edf14464aa53625a5d49a0d0b3608d2f634ceaa2adb3d693a83a1ee5f6a8b93e8c0080c0d9adb286cb2ee542f9e4c29f2cdeb22d1ccd1e916f3196b58db72405f7b7a5dfdee43df09b41ec9c72489a9dc29e79fb4f8563600d44a58131252ab36f1fcf269188e60831097f19915cebc2447061d540ee87c9b94b1a31f2debb803ac8140b51d826d0d39d50fe4816cd0ab034a4f96b4b72edabe4f7ae1f01e73ecc8ee7e87b0740442a1f87543238e235fc357048520491e1c22c71678adb017997be72a45184f3be27cbeac2d9b9325b401f668f28d87153f549e9ea20ec42160b0b82427ec8bd81e783e9b2dfea2b45cea886dab0f6bdbb7e1a80412af54bac1e447102cb6db24c85223fbb71894e6f09a0eb679dd5af6b1333b8d12b5b2010a87ae87d41dca6a04f0d7a02db81c53a993864f4fa1fa0499f16de38cb5d826a15dd9fa9edd009f747a01abe2628bb030d5bcb0ce5c0ce4d8ce871c6f06000d65897a4d860c621c728e2a6066499cc723d71650798d48295089a79a3263718ad0146a3d089ad6b5e1809b6cb4285fc0cd06d9fe66d2a52056bb0730bef3e3d79f281922ccbd46861b37301f1b09e4a11d7f9b9398fbc62d85a7089c0c551bf4141abc36da1859e61f4d875d65e81c5ff4a3e55672810677af69aaea86acad3f016d9aff60d025fd4b8882f428b99b7bf3fe7e0954fefa0548b5ca12c68b9abdded58209483373a083cc058805513767ce0c956b701f0184a3ae8fa3db2c0e39110b826494c8f19263bfe1a16a29d6cc3fc4b2bed15e325f051f3df80e814658eed6ff9e55a60473807066062d80ce1a7cdbd597ca8470d88f6a31304fb4fdc2838b832f4e81840b485817faa8d3837099263a00a0735ea17ec91e0d8c6a9e7899941a66d0dae8c6766f6fa3a6ae9a3a8604cff33c29923b79fde21b5079f16fc0c8749b2f02f2f2543d82cbd283ad2a08aec23769aea46fef42981ab2464134540c6e0778adfb5c128b03b88be472021a6c80210470b371171c49d91d668fc0821411b70b5abb68c2974ac5e521b9a304c6d505963c0cdb4e804ae12c5033693a4722765e713111cdcc6741c6aa1560cb5cecfda7d96a927e4f57b98e4cf0f4043fb4050fba059138e5adda5be1f56cfb8c6f135678a2411badf0e35a1d162edcd1a4974a3347a7177cc5df7cf11e2a362d5f385166f6408df509ee35802d6aeb0e6f0e718c7bbe534d4a998354c9c8c89899a8c1208d15e49b415023d8696eb183877a31de98b3636c813881082c0bd799be2de90f275e9cb3d9ac24cf84b05a72e7a6889a14650232b30c57eb943813928d1e1d1e70c40dea3cd771e1858eb6fac5a21e6751e0ecbcfa11e93f13a10f287be1c99a6f5a327588761153f8727d0e32268482857721a68bcd35e1042a4d49484d797d02a72b007ecb4c52d4a451fb41201a31780aee9518539e923859953bd06f08fe5964b4c3a1433c16953f273ba2dde305063590ed13fe44d79714575c5281e08ac8f3566901f0cce75525880258c0bd39cd26cf21da274fc088c93264dbae6835f3c7b9282416f4b0f5b9c5460f927bf4d6166bf90c7e871a95e0d9494843d26d52c871e46f2d60c94567070394153bc99100c1122841d6c1257cdce73e2176fe0fab525eea9367296e6033beacd6994a0bd2cbfaceb0c1f83977062e57906a9e6abc2472e4123bc84b56b4d1d7456013437d2f252fad8e443f98272cf344ea7955c1fc040b58500ae46c9393d378e3238f9ff87ee3e1254fc161c46229e96f981d30d9428f359630a95aa06f7107297df57e106c402c15278de9cf23bcf3ee9c3f3a45c5bbfad173bbb5f6caee230cc6111d0f9aaaa781d1496563fe694edd8438df738febefb90454fe6a790c0db03b338527f0d3942f175c19c0f5c2609fa62acf15ee14aa5b56a097c78bcd945d01b139c2806fde3bd92b81e87cd48251dc260e0f93ba795ff3d0e2faf4dc106956b8b5c10c3764837bac46e02b52e6a4a556fd39a382ec5e8498b7dc0e8a71189081dd430eb262f934d5b6747e032cd69d1e0c1491acb5e3d43a46a6518c1cceee7b5a8f49f25c86c07564e8cc14d857638431112067f0b9f5fe846e4f6c675eb958528f1081305a436d00f78f168c6be91139a0c67eaa63083722d3951cf0afe7a47ce76167ad704f727e47899c8cc61a7a7fd363d36eedf585823fda0b4179bc846810d0d932be3d56979ad0fbd2367b0e5c6650336ad8adf7b5caea927a3260ed53e632b2cc9d18d3fdb304fa9749710846302a44bb60ab8404e72a1a338dd359712b388e787eecd5a9191d67fad7ded4b2c6805f2b1f5e876ec63b895e4a1b0c2b8e3996429347f685a160ff018d37602526c6e3160391839efa17dec1447a7558d39d65595d87e889329e88328e6e023f01f97107d001d0c6163f389b501e94f013f454fe10f14c138a998d52ace1965113f38fcf2f71e64c719412f9eba54d8188c90a84a9c5ef09871bd49c4e602bc3821019d637e9b9eaf9f8eb081986e811ac3a7cc067c92858340e33a90f09d430c41b685af94037e619a96bdc5446d7acc6fdbcbd7a356e45b0ca1417f37f21038ae392cf4d689d393a43ea7acf21035acc4c382f15f603be67a3b8ba80384f75597d217b2ab18321394ad745d4ddce186e98726654ce269f9e4a16c0e1ef3623c353be2689fda4f3fce391c26bc410217987581b7db5aa7931e5bce58db22950d551c0663d027c8c87d467726a0f54e7b493d0c64b029e190e2d8ada52b659b5a3370000e7223d3fc8eb47ce7b7161408908888405925bc3932fe93515c62845c8a57c49065a124c20ae14fc6a7be0ad7ad3d90440232098d82a8f8fc0215fb7652cf779ed6529fc7fc4e9b53b23374f9661901fcd54e2dd55a8cffa600790f232ea513812e66382daf7e2af5038d615bb68e83051c5fe4420dfd8cec1521432a311095d2bc60204ad26387e300e014c422fb89387539b96f264701507101d9758de3d9a81e04849ea270562a8a2491c0893efc1b914fa491097510aee4619e5d50232b1c59a32fc4d1ae7cd098a33ee001103ba34e9400a126abcac9e6524cecb246897a216f2f6e27b5f1977c490ce5a3432a1a6eb25ca699e4c986eb2d713254efa72c2e4923d9a3869ca879376d1c00c068b1390b639c5d77010aebbf74f13649730f28732184069bbc6738392683e39466824e98008124133b2313210b54ab9a0321ab1171afc4a684cb828351ab84878710c901e943c5cbd3e66b71f57c1b2e5b49b2d65b8ffafbf988156a722f18ef8037506040f1a3a7a8d5f1e92f94d09d6182552bce4de9268b42a1a5049f079e965c8a29876a04bb54c18f8ff25def0d165e13467603b7afb2e1f2ebf738bdca4fcd182fc541418263dc362ad731d3c417c76f2499f7f67b7b4607d09ea6caaa31e18b59bced1341c1cc766f464d08e021c57e6cbd2079526ad23914c2823028709560f84372ebfca87cdef06c2896f99d88d3238bb94800f0491921dc03b41d01cf4225ba495b642d504927cfbaa996b508311caa3f32ac63ab2f820eba909469c36338e5cfc63e334352efc9cd518c58a250ce4d733655fd8b238d4dab53a8a1d65a94a3d95a532b2b122aa9dc538c50b4a38281012106b668f697bf746fe0ec8322e0306b8deb5eb1a1ecfe1dd6ce71801d636d83f6ebabef82217ef8cf1dfd2ed398511e596b8751ce3afee815bc1e2fc189fe39e848e7468e58918880e03632d6f2b9e1979e9df44c695e1f159a67d0ae4f711fa5914ba1b0c7d425a742ad6e8b372c3d2262b14b662c11495fa1ba5b9e35ab9b1dc84b883b3eca6e9d339f9f47da498e8795298b98ae07662895ab726ae81122de11e9b2978ef6c67e25398a8e4ba5f923c32fce782ef02514816c3868317d76acb83697b68ae9f43c46de1307fe8c28cb042922ec6ec87e20e70770c224da9380fe9793141ec490a6f4a8533eb2c2ba068643872245ed19f8f413a2a492a52b44d44919748550f7e622f13d486964075e2806f9580a70922c421a59f5ba0eb1f93b0a461ec62dcd4c00867b516c43231cc3a2977cb33290db7587832525875ccd416963aeb58b13e5eae83ed5a875f93fab067f700d81be3f08f57c91089347f79ae61aea21f9249a80c71ac4fa5dbe75ebd36c04239d6300610516c2c5064d63131c0279b1bd3008553384a7df4314e9bac7e8de32f3f0059a785cb8cad352e68e2cd5c8b6956cc305a1a700576eba866cb18ed54311b95f745ae5113503382938d301b179ab1f3adb85e5c246b37c56848011f435ba8372acace4c68dd109c39026e6244fe0a277078e70ffc0ffc0ffc0f0cf36f9a7253f41a70969bc07393ac8854f024073dc9911c492febc040e40a160b370b661d49d9647ecd61c7118610558bb7d1eefe9e4698a23dee96a49cead8c308b3876ce220e5971cd545987365143e67f97d7cb622cc9ebd8d539474228c9d3a634f21a334a93222ccd02c7bc78a7c085384ec3997df64bbba86303d9eedf8ca17c2605a97b2e9b0b4d28430a66789473e0fc2144288c94e5b49951584793b2cfb51b018be6a204c21480c71106c4018f5bb7c25aee50fe61ccd89ebb8d8f14f3f181de50951a1cbade5ec83e155228aa7c639594af2c11022724a3d71d2ec8c7b303b58958d8cbf5672a807d3c5aec9b70af360883a9fa2526c2c4d8207e345d2cfef29fad1237730ed9a68569cf5f7d80ee6e8aaea9fc8d5c10c424f18cb0f72ac8ca383214a4e4e6ed9e6608e69114246b927e5c5e4a054ea49df5589831955edbea48f1d720e0e664dc71945cafff715bdc1f440fdcf3f94e488dd60b230b1a5d223cabb6d305f3eb14e122e495dd860d4b56871b234ec8cacc1fc7ae29375d2996d6a30dc4c32d14d2feb290da6980a6b1d7e5a2363ac208908004128001a4cc1c457fc42667bf80c86ce312b799c3f6e143398f42bef3b2c1f3beccb6016d752fffd7cd635190ca3df8d62c231decbc7600c0be5d520470ce638f76d1fcd1306935b9ca507e60183313a3baac13678ffce170c756e5a5b298549d2f18279445fd2fc7cbce99d4b2573ed10a9b96096fdcd0db15dcb7f0b46096175fbc1ac05e356dcbfea9c6ceac259307da4e4f0a7df73238c05f346789c75627d458faf60d29d7ad78fad158cdeddf20f237aee8bab600639f5198edf436da582412f42d60a977f44d52998545c7f6246cfd4a85230a5f171145cdf51eb250ac6afb43d2be90205c36ee7309d81b8bf5c9e60ae693d8baf473b392798553d23b6f7cb5fd70453789cb3a452ae9c5b32818af4472ec19c2fa5989fea2f750c9560dccc7d2cc127493068aa7e8e8548308bb8b785c7c142fa8e60eafef865b9b2563288114cb993846efcafeb5e04a38497aa0f1193cb12c11441233b0eed7d1b6f0806b7b9eb8b7c9fb9108cf69bb9e127a1831f0473a7c9d925f5e488a70040309a7e0a6f59554e56fb8541eb524bcc9fc48c7d61fe49e720e46fffa1d50bb3443893f4695e183a6dde85d9272f1dc47ce059d1851975920f09b9917c47b93074793d984871ddca172c0c6e0704c085d1abff1e5efc6f61f6748d1da5a8976b7e5b9872f28c480e91ac77af05492ea6ae45da69619471c7368d33de8ff82ccca5222ea11d4e1fe7c8c238f12ff28f3cc85939b130cc64e81844c98185c1c31a85ae38194b9d571843acddcdfa3f87bc2b0cab9197e3e41879f66e853956e6a343553892342b4c32fe293f63bdf1be0a635e7665e491d4c9556190b1e8581b87536148fd9cb1b1934b928c8a5ca2566d720a73579830fbf9e64a5318f633c6fdd1eedef196c2e85052aeb0eb125d521876eeb43be318c9ca5198c6e2fc416d4c38504561080d2fd487e87c4e28cc96a912112dcb970714a6baca2d9978589efe84e953dccea7638fea8a27cc79e219b754affbdd0993a514353ae4f1ac4b72c20cfa63bcf549aba33761daf9c61d5b3ce5859a306d668b7fc8312d6f98098396dc9ba5799cf7c184217574f7240f6adabd8419a6ff4aadf9f75b2c618c9a14ae3a924a181ac589f9b841286198e065b9365ba2e593309875ccf55b12865c927148952f12c6b3898daba555c2789030e420a9fea2e41e61061f7c3e58b0988ee20853d85699e49f1a49b34698dbe5d4662a54cfcf08530e529d9f23fa4faf4598231d955d63eb93b8228cf5f218ea3412618aecbe0e268b8676b482240684a0023c2000220c8e53d68fa9f51d81008730469e79b12c29bc7e5c8bc04000431cbf46a2a56c54276d20085008f34ba83c59a2b27d5647a9820084783c4462c4c43c7ef42088bae3a1c26a6ff54510f69c46ca87eecfc70a9252506a0f040884b9a35fc683fc6b231e4018a75b32fac7b50fc2e50fe62c96ba3a42fbdf4156903488007e30973f18c97d91f3394320401f4cad8ef16bbdca23fd9b40003e185d43eca1cf6be5990d01093080066e83188ca0010d8081bf80c90d76408310900d98200502c00301f660c6dee87e2eca4e62bb82a4149c1608a00753e6a4fa88afffe85108900763f5475f1d5d2f8f1e0fa60db58c2558145b99dcc110af6fa7755f3b984384fccff5d82b7cb80e268b8cea7262495785e8608e9ed1597b3cc67d3f0753459c9b207ea923a3e46069300a03049083f172f89c1ebb1fa758881c408038181c36bc74ef90aa0910000e26ad10527564984f0e4941051ad00002bcc11cdf29673f8c8c8ce31524d5c059402a6840031af00302b8c17469a24904ad48cff509046883a9a15d92b476d960fadaf724f143cc9d2328045883215423577dedec91b71a0c2ae218ce65e9dd504080349841721b4fa10e6210810634e0b80e2ed1e05ecee670cfbf2cbf82241710e00ca65fcbbd1d24228019b0ea14a7c3227e8ac1b181bbc005db81063460073a88c1a7e09c6d400358b03408c169054014042883966f262c5ed6abc90ab2010d78410c68f00d68800460c00215b080061c204f10800ca6141bb53f9edc296a40038a0830067cb2262a578747bb1870f0285d4ec3803b30b1cbfbff9e0403af923db979bee4be808e8709496e4208034ede80012f60c10a6ad0007701af0d70c000ce1b30605c0d08e08557c33b496ef08262831b68000614f00001ba50ce3013ebca29c9325211d5304b312e25bd5db07d30a04011800b668c937e8bd8630549640b04d88221bcfe2bb936beff4f0cba06a4063190c1068a6c032648010808a005a383b88aad974188e7c982293bbb62781e3b099f14088005e387be8e5fad9d974208700553706c6715adae20e99415ccba697a711a03c7c05c09100254c1e839c5bead6c895c710250c1fc29b62633a2bd27dd04049882513e486587624ac11cb93c69d8fce4c9d20d08100543348b7d67f1ebf17b37c01210000ae6fa9c2dff5d4d9eec4f3097cbdbc734b73c6e911410c009478662c1c243323323aad66c1d665f037743500119d48092004d303cf858f13544cbbbc304435ac465281fd33ded7646327d2e4d22400025981fcd5af48ec5ee8cf1000224c1dcf6ed8d63a7ece86824183a77e8b4dcf93fe51ec114d24c6ca5b7b6fc680453083a295d575ad68b45503c7d3a22987ce6373d8c4e7e670886fdb4962096de7ca40a104008c65dbbd41644a31a864130669735862f0f5f3b45002098fcda3a23a5b4d5bf30d7d99e7fed4ca5b82f8c5f16af9dfe6b2cdd0b636e38be14f6365fe585a962c8fb8a97bd85bb309c45c7133ec7db8fe8c210e3b18eadb930557ccfbef21e17669473b8a485fe16665429a5f3f030563bb6307b90f49effb1bf836b61ca18a75c07939d724a0b43b4bd91bc0fb1149c85494ac5bdec3e3325b2305638aa90133d1e5f2cccde511a86440e0b633c6875e0f05718ce1d58766d8b49c35d619e8c90957676a13ddf0ac325bd9c996a8f31feb0c2944d52eeef0f12f39e55984c2457ee8410721c8f2a8cda8eeb1d44a66c785261d4911091bf424e750715e6c7f9ede748b3f1744e61fc08958f7ea643d8db14a6065211afb397c26ca76395d1d4689e4d0ac3679cf3ea7ee558668fc29ce63c5285e4da48b6284c3712c2e46a7cf9c80e85e1a3ba1f4fd5ed4f1a14a6ca8cbb99496182a43f619edcd032b6762c1b694f986f2754cf55d599df0943ce719751a7efde0f73c2ecc8efb37dd6c17a7813868bfa7e36296da5963561088dd16464933361c858c26560bb396932260cf1730e9d22126b227c09c35834aaf4907c428a2d610a17f6b42e663f565c09b3bdfba4a824318f9812e69fb3fd5991b868f024cc515e95b195b1b7be1e49fc10e6f3d56ff9faac1f3d8630967f64700da5c3a985a02c43f78627f3394210be4caf23e5c9741066d3eab309fdca218230543c780caa715c6a74208cf71f725df21910944fa354ae1e76237f304b0821dee998e5e57e30cdcceb8d87e0e0d307a356dc47b62ea39b321f8c6db11c7e3657cf760fe6897428da13a3ddad1e4cd32b2b71745f2fac7930c555e5da477b469be2e1c6a042bab4e1a8b27107431eb324f31adf73297630ade78fa349e13abf1a6238401d5095ac0d655ba24d0763dbe3f449f9df56953918552cc5f7ab143250530e4c5c928e162b9e38182b4feed686e56865c2a146491f8a45db49ae208946180ef006938c571c6de91ceba21bcc1887fb5d4d8f9f2c69c3ef9ead753c4f54d860f6bb64b1df3cfb37c81a8cfdd959afca27633e35f01627397ebae86ba461147325dee3d0a1c1947fd2735547eef2cf6054d70a3231713318bb415bc9c4d55d489581d5e9db47ddfd1baf200969600319f0e00064307b4adf5c46765eee188c33d3a76e41bc5f138349a52e7f4e2e699141612898a77851d2bf78e3c1904855c3896421fd8251ac24a47ee9054348921c557434b95f72802e1862be6195ad4c8c6a70c1142f5aaac670291c600bb6e3ec4a61f3bd168adc25251d26bd16c9c2f071b8496942e2818566e2872df94bfd197705a35f6fa38c72c7bde85ac16cff187cd66547322955b0ad2cd742e7fa97a8a088379824e9b35f53d872bbd42363dc7dba140cf12578dcf95bb03638281c200aa4aa8c4d82ff5e83a1a0c5afada89b4a97f2278c523f5db7f969e4ad86fe9dd7fab2be4d783ea4ad745339e41c8009a6e0288ada6479f0f05fc2f1ef2045e790d47628e1e4e9cdb3d844679360ee9db4615ea51e4b47028eff62eea6a8a49e47b0363e8aa78e44ce08af6789973571a10314c12067561321ba5ba5fb0044289cbf25b1eac6c8fa21d439d78710cf412442184faf798ec67bbc1784da71f898f8bfeb197f0020943da5fc1eea2af78b2b9bbe6f5975aaee7c6148c92da2db799bc4db8b3e4b4ee5387ed7c68b62d756ca33d740ebdd45e7571d923fbe8f82ba481e34869dcb8595d21c95a5608ec7055641622ac5254b836f716c758cb2e655a8b660f734e651c6f239856aa124178facbfd738b4304de742545f3b3e6316797e5d5fcadc364f165dcea8815b9474dd1a0bae42168f56919ef2b078ede26a1fc4ec5e31cadb713c67e407ba62e9ee9ee0eb51713dad2059639897b4d33b4d58f1f7e4776c5f76045761ce919eee8f43483a892a88d10f8970a1329053917968cf20ac45560fa2c2a91f954e6db51eca5318f284109b91d2b4774ca1348aa6b38e32ab52181c39ca39dd871439c828fd3e7c0e3ed151585ef2a1cad2279d4461cea132d668c6819b5b8128182014c6c919954b1ea2a70e0a834ae98e7c4ccf9e439fd8c124070f2a82ccb99e38bea70b8d45b4f64e18a45fdd752c27e43b9e60004eb497ae425073ad47eb26ca1fefb7cbe6acd304a2a7d5e7c8842185facf5956ec4e764c186592edc4079af70ebf8451c42eb767d496304c4acaabc378fd70254c161ffd747c90912053c29c1cf6e978b08e8aee240cdaa981c56c5937874ac2b0924e3e4f4689049e356374bc41d6b12161c829788f4a368bf32a05e905033ce21c7ef13be4b73fe488457bbced2dd7db082394e5c3c7a0d1ca88e2a3c66e5ad27b2ec2a052951cc8564a5751447f2b591dfea5d4de27c2f4ebb7597e1e440cc2b8ac84f4be790e6176fd97eebce15da621cc8ea41a4978a71066f95453a1f2c7cfa943083a2b2f48ca6dbc0da28e9c0ed5a1e5b2ba20fcd3cc7b903ebb1508e3c788141a7c0c08634e8e9c54dbebbbef1f9493ca22df9e2cc50f76b8df4c59484615efc33dbafb71563d72ca873bf3a25d5c72bdec1e4c1b7165d7fef13a8aeac19194e00d1f8eecb97940a5c5dcd4ef623d878741766d8716c92f4dce1d0a4982fb6e353c0b713b18c3735c99dc2aabbe0ea62cfed93e5ec6f5391db27c19e438fbd28ede39e4a937773737ff9783b173f0bc61596d72ca3898711eb3bffacf1febc1c16a2413ff401d7fde50486be319bd6e30a8850f2d19f12ca9b5e1c7da216ed9e59b0d26a99c2cd954ad93576b30e518d33e11cb991e3550fb7b227feab9224f8379a71aa3b2600034982786e78b1d34e5527f06737a0c3dca7a96cf8f37c3d054c6310ec9df3298c187d8af2269fb2b4e06d3458be9b00fe1ffee3118238bcf58c68be1cbe89bf084c11c7f735af2e41f59c0f0a4f353f5f20b65bdb3dd19d1a8bd60ba183953742423a4b60ba60e6fabf095e3c2285acaf6e12d181a6455e44b650e2ea505d2af5eb677d4b964c16c5b6ad31fa34b44c282a1d5442ae3b993d7320506b8825992a42393d0db50b78262eadf393e6891dbab600e26a9636d6db27ec60a9252b0031ad8800af785b43d139a82b1f55dcc75f42f348e14465a0e631bc3fa8b76144cdeef56eb6d211d83a1700e19e9c53dd2b49f6094f91ce751ce39e18a90e76c2e477ffd4d307985643aeadf1d0f638297a63b5cb2822418b0e0532083a51a24837d81d120063658d58101966012e98fd0198baaa4d0201dd840062f3030e0000324c0000930c06fe0821d010324c0801d3806565083b5000324c000068420030c90c15291c1bec08801946092af757496f62f65d38c0d900453aef4d1b7a53ccfcd152419005661002418643d48f63dc93140020c90000324c0001a688001126000034290810634a0010d6085018e608e7df8d937792f59358011cc78723807df5f5e25af200f0246570628021f1d54a52bafd669600310d0c05db06d002298a3db6b441a3137ef94160c300473925e499d9fbe87fb160c2004e39c554af9dbd2874483608894968cac35ebe1c0820180605e0f2139a6566ad0458202fcc214ca73a3f0b8e85fc90a9290c8060ae00b73ce203356bf62bb1dd80b53a7cba0cc66f45c6a2714801786959812a6c361b50b6305f11cd153c838a63328802e0c5142863b66154b7eb61614201766f3f3bd3c8df49fd32b485a41c74006be8202e0c2105cdac14b5a465ee70a926490028b42016e61aa9cfad2877c27f3fa0a9286190a80130a600b8347713c293c3994edc0200647030b380c38d08006902c0a508ba2a39021555661c941e48a9d759797e6133f2b481ab430e4e9180dbd10034250810303d38006008014059885e9ca1ae407e5cac2281fee725245ff1f5c44140b7383f058bb301e62a70d64c0026fff8004740c0580851987a34e212532ea9c6101068420030c1043015e61f29cf2fc08731b512d802b4c77e3df5be1b01a550ad00aa3a7b9787f7275b39102b0c20cae52bcbb5c56905483183c049c9750805598e2bcf5a48e7dfff0b32e50005518c3d61a05bb759cf35d4112394f142015e66029e460fb2b17695b4192d50858200308f03a2f0f3f14001586a924d5972b3d7f031ae00d8318c0c0060d68400d1ad000093040023b7818a88005201881061ad0000930a0ec06388208c4e053e082f643031f034844014e614a7b9f9f1ae3ea0905308551b2f3995aceb5725f290c11d9f6fe73496cbca430a4e31033b9d3280c9ed3b6ef3afe5d7888c22833735f11a19dd7344201426148491a49ce11bfc76e28140014c6778c36eaee234c265328c027cc59c7d27f44ce1f245f4152f6a0009e304f96e04025fcb25f7605494e0319940e0ad009330ee1c349c706d971423841febcd9936c58f70a92720405d8842147d8444c6ba4200555301041031a50004d1824ec27c75521be2c54804c187ccf723d90d2c619b482a4183c0c58a082241b78054a110a8009935545e7f078bfd2f397307ed01ad7a990cfd4b784e9d6a3c465985dac7c254c0fda7214efe8f68fa784d1772c6ae8445e89fa244c296d35f248ae23a64bc270296d5d42cb62871e096343d5cbfe97cec973481c92ff5aa9781e112a802424800fa413b00773946832312f5ec40af560f628165dfdea2c03290179309bf64843877139b58c07f3bc456964bf0d3f7c77308eef8d59fdd967745121017630accf5f6fba5af413af90803a981bb5564bf2d0c170717dedea2dfff736076366cd45dacb3edeb91c0c3da29b922d458ff08983e1372c1d46aa3cd91238984f625b5a7f74a8f45148c01bcc1865c718e3fd8a8e221b780d6a30680b097083c91e47ab9c43420d24a00d667bc9efca11b9c4ad1524917902d860facff1e372a3cbf1dc3598835cb66790e17755c80a926cc08217b0c005b909508331e2fc43242d57a6bc831aac551a4c691aa1722ccfc61eb7e3c07c900034987de5513c587d073dde064c90020e24e00c265d2bc995380cd6062c6084800d5810810634c0062ce00d9820051d4880198c1d0eb561e7cb6e562b48721eb0c0062c80c1d6a00ca6f151cf895b165beb0a9252b00317c840065be84a90003298e2a4ab9ef6b76c213830d21324600c86b89ca8725c5ffd6831985bbce1e448179b74e321016130cfcd84bf7790bdd31d020283d9f53d83947aa1813e3e015f3097bec3bf92d50a61424102bc6068107d2cfa6fdac1ba82a461840474e19c4a7699fb78ae5b41520e96062970ff149021015c3079d6daf0e0a22ba5c0ca0209d88239c9a590ae6a1bda578783024302b460b628c16b5692e8a9e9850464c15cff212a5dc638c4bc3b406a0f09c08269fa25f34aca2b983cafea56c3a3bebe3924c00ae6928eb69b0a6a123b5530e94a048d0b96a482517a6f3ac752e475255330ebac3e8e5e69f52f1d1c12200573f48a480f6ef352582f0151305a75c8d0f4318a52900028982be579cbd1e14f30b59a4c6984ade038ed843f2c775b37301b0b69600319f420014d307d84b9a87cb9723a478943029860ec8f5efd8f0db3d1684b309f954a774e58daf2dd9000259851fc90b0549e336126c110d1dfd27a0cebaec90726608135240009e671d41949638c3424e0082689aab10d3bee5888450d09308221d64ec655e1423424a0088608b992fbb5554a1d13c1dc219ae433717970e12118f55ec527f2830e9b9e19122004d3c57674d1dd967b520e8c21014130639cfb3de3ad6d7c27b1210140304defa38f9ca3c3a58c2f20e017e68e5d8e333a3d091f5790d48006b4e320041960c00e681002a3761c10a92f0c77622a8fa203ffcff7c21c433b4549d9f3c2942d57faa9cf29a8f4bb3065a5bbd020ab57f8e8c2e4f9ba758e920b73ee5648907aad0ce2b830e4d63e73fcddc2a4d928f3d1bcd7576e0b837607b3d3add3caa985212adab6e24d0bb38446ff8d1ba5ac55c91910300be3bdc553bdf49185e937a342a2fee3994f2c0c391bc4978ca4d3ed8185f9ab3b05edca7985d9e5a23eb0e8b74e5d616af0797774247fec5a618888fa603aa39bd45861b617558d1cb2c15cab3084e49692c36ed0dd685598c54d24e8e43d943f158614362e675cfd60755498c48245ec0cc7b167a7303fdc9caf643615265314213bed5cc8e05218e493e5ecf493c2d4c0a35e847f4761c65221b8bae4c8a05e5118462a392e472a16f386c2d87fd9a2cab3fd5f1614c6cb93fc84396517ddc70ee473e20973a66d8460e39522a413064b5ae559328828dd72c234118ee28f397c94b19b30834bed592a57ac64551386898cce0fa962d267c228a99e54e4d3aa6c4c18ede62704e9cdbcf0250c9e82de390a8991a296305d87e4f7dfaf7c965209934e4a8f73a30b25ccc0cb77f5d2586b9e933063cba9d69e5212c6cfb6f0b12ab7f73212a6c893b7a2786c18718484a9d7d2a274e8ccfae023cc57de395c7f0e963ae80833aefcda92e5d0638a8d3085f0174f613ba5136484218f7f449dd391f17011c6ce71ddf1ce3fc7a1220ca7fdd1a9e367f90d1361588b101a574c29cb134418cca2ce3c66e7c99e1cc2f83956e70adb10e64f6321c2e43cad7321cc5ddb595daa5159268430845422194467ef996410e6be498f099d5d5b24823048a36d9cd23bd281241086d46c997b8feffc08204cf963a5c5d0889c0ef20773e5e8f9a711bbfbfbc13c59dc7374148d71be0fc6fbcba858a936463f3e18acd24bd72da5f8fcf660b8fccd4923973fa44f0f86e9797d6c7a9164be3c98a3ee41a74e9d4dc2c38339575a474dcff2cadd1dccd1f05383c919e556cd0ea6c828957a86a7944eab83e9342292fa7779b68c0e86ff776dd47f9a91d71c0c153caee7cf64945a723845b4c7d21312075394fc5b47fa695e8383d12dc457caf13baa566f30adb57894746aa38c7283e92a8c8479bdc4f5db60b238898d68d9cc3e3698c3a79ebad7680d866f8bac16cf1e32911a8c1e3a0daa34182f6c866499d6c61634985348ab9fbb7b1f79673045c48b8bd56217323398b72d4a7cae71e80a0494c110b2c75dcac8d3f2545320800c861819578c9b47c44a29103006c34b3ada6d954be921314080188cb532dfd2285520200c8614641fc897dae7ddc160ced99c98b895bbba7ec1a86e21bf2cd9bee5d10ba69334e1547feb41b420a00bc6fdd0f750f52d4b084100170cffa1ab3d54454d3a37c00009d800044e83184800015b30648f3adbb1aa97e868c124413f654f777b336a16cc495566dd63e3f89760c168e9310a92ca43c0154cfd75eaa8c7bb53dccf3a0d944000015630cb686535f9602149af82b9e3f566b278a58a53c19053bae4e29e77000c089882713d7a4aa7b5121fbf523047fc95d4be12ec721a05935ffa489a34718e52a060c838d4c42f74c593c7f8000324c080e101067420030c70810c5ec08214b80b6470031ba400c90b087882c93f560676592a55bcba800027982bb79fc78a907168e0c7200634e840031a408317bc200532a84122a009a60c87765e951afed91524612310c004a3eb84f6efea7093ee14d4c04f0c605037e0c00a6ab02bc880b3800621c8403b0d2a47818025500310a004d39fa47d05492980410c88d011084882f1d5b5ce4a2e4f5cb482a46440082a70011870a0010da8410b62108315181602014840c0114cdda37f72edad918e4a03028c604aa65a6ef1f416d0200639288259525013b3991c8d010144303f48fdfdf921040cc1fb067525777b0b62108315a4c00587055584408010cc2727eab971f56410322020086633fff94b639e11342c0302806094f31cfbcfb15eee06030e4860053660010920c000093080011260000324c000064880012060400832c0801b447e61c8db2167ffd10ed2a52f0c92b916d3b0b61e5507e885a13d845c93ede2f4745e18c2477bd949ef96e72e4c939af91dba1577b12e8c2d252712f3d9729ee4c254eb392eccff21bd85ae6415e73bc02d8c63f9d304eb3789ebae2049ab70005b28ba93bb2fe58adfb5307a12b7d78be3b87b9bc2016861ee8be968a7b37b247f168648bb901fd22e0b2ad6d2681b0bd37712f9fcc8d724d5b0305a187764f9234a96fb0a53c87d8c1d489ca5495d619c4a0e5afe3edadc5698d24895ee5e921566cbedad22d9adc2106e62c45ab479f1c70a5215869810263df74bd8cfa7c278f11979cc93c7e71f54186b92a73da88ebfd43985b973a6f765e74d4a1d5398f365742fc75d0aa37b0c7520a91dbbd9a430696c3a2a0f2e9d7e1d85297efdf927159368a9288c1e972b48834a28ccfddddbb8e1ded60714c6bda8dbecd05179929f3078527f189d2557f89e18ae5de8e420219d583672be2c51e4843be9bf22dd8421ad2bb5758e922be534611075cf403adca794970973d7573b06d1aef3c530614ee1a1adef3336bbeb12a6b0f34022ad650993e388a974de21afac4a9841565edd8a73b1aca284b127ca51c74fd6185493303b0ad239c44927d7491286defb495524cc95637f83f01c1a0c09d396c76f71afb028e911864eaada79792c65768439f5efa4fbc8b8f3d408b3a5d889faa713428311a6fadeecaddcf0ef7f115ca7b71c37df2269400390080e075004729d5d62bf62123b8930f73bd4d69e499f538f08435d843febdcd6059730e4640ff9b2254c8d326abc1271f6522a619a127fec5adff1154a1872747fa6caa4aa3209b344fcd94b3179a948c21c2e27efb8d1f82b190953aa8c72fe0aa171242161c6af16a562ee8c898f30c5dbd8ea5e879c42471882ab3f8a32698439b6743b86aa7de131c2f0e9a139d2bae8d02dc2e47f95d1f5cd5ead08633ac89e114d84e13cc4c96a25efa521c2ec12546b5d3b8439079551b392eba90ce17cb80cc25f0833f0ba10e6a867744298c4aa246689db58681086efcbe14e4241985ae6d3b4e718611f1808438e8cbf550702c218179722b687faf6ff6086298d4fc28412dbfd6070ec51ab410a29b7de07933a5249099d0f6690f7d431063f613c7b306dadbee77390726f3d98322e6c664887ac771e4c73d96aeb49cb77c683513ff7350e3d5651be83213fc65253723215dbc118f972238f708e515c07b3d8942309b16e194c0743eaded6c9d3ce78e76094aad0a235194865e560b4c9d1b137360e061f8df686161d54858349552a7c46f99125f50de69736ffffd16b98bac1a00d426d6b461aa66d30bfff4b598c07e21736987327358f9b90d89735985cd3c2a5f0dcb152d4609c8c499d3d2d42b0a4c1a41971737f52c44fa2c1d466276e2138c6769ec1d4d93ea78cbe7c759ac15039b5d393f5c52fcb5074644b295f4906f3c50e0f17a1a1f73806e3dc65b750a933ec89188c1549c28e657db891309872d7a6e7778454c1604e5267e19b3a9e7ec1249f62794e7954b6f382d145ecbdee8251577e3b7ab24a765c308639d6b2ff88796dc1148f42c628a405a3594e59af615930ebabb687d90916cca0d723aa55c415895cc114d53e9b85f14615112b98572a875bce7e2519a40a062ff59b0915cc713cf75218cd6c0c3205e37fd07fe85e96752918ffe1875bd1cb157d14cc9db32ac9f145c50e05c3afc965eb1025749e60d4926958b5134c22b9dc2d7c9a6094c995ad7d33c1d49b79532125bbcb124c162988ece5ae7cac047368d6abbe051d0727c1a413222496e7cc8f0493e34871943f827127d24ddf76878e110c1feec245ca16c1e8297f12f128118c0dbb1bc565014330dfbca97d6789e0960508c1701f34373fdf985a16100453f0546625faf1c2b900201846c7a30395cdb8e42f4c793d39f09026560efac2e45172aaa0622fcca13f5dd97734db9017e6cc6d155b51db89ecc2949523f161f338447461e8ae1017cc8529784d050fd1f51e5c1822b5a578f4ca856f6130b11eb320afbe9e2d4c6b169d9bd7c2e8e3a0de3f460b63a84cf9d8360b43c79e779c43a4e4992ccc511ac70829e3e38985f9dd9264576cfa0d0be35e5fc658e2a8ddf40a439a947ca942ae303fd4f7a46321de48ad3088674d945769c9b2c2f47eda96ea2acc934c2f245263bc8d538519adc8787e7ca930540a9151ebc64ee751617c7cd136b25398c7f6472f66a2546f0a9334fc90f17cea4a97c230ff1d331da4304fbcdcc7d04761e82025bed11185197e84328792b34c1e0a934c6bbd5b5b8e7883c2188efb71e6fd0963f74c4de5e839a3b4270cba0d5cb6ec2b3bba13c6ecbc15fa27ce3bcc09b35caacbbfcd7ebe9b30839a64961d74e5753561ba18e2e9153ff7b2993044abec66197698473161860d1aa4cae5a55f2e61f6f0f03ad7c51226bbaf9c6f2476cd5209b3a34eed78d9331da484d16f22e29493307b32bb47d171f2a1248cfd12d342484f9e23611cf9b1f5742b5242c2e4482fc44a1e618cf5e821c3d26e8d98021c61ce13d2b89d8bd8444c01658fb5095e21620a60c49f41ec7010ba4598752c5c485b11e69bbc6967c9c65d4b8429e7753017ded129438429be8448163292903f84c93f44b6ffeae8784318c244b45e9ab6f10a61ee9caebfb532429843be88d721fa8746d920cc392d4d444710a68f5607e9fceddf4018bc42a38c62bd68840061cab063a4e4102cd27f3063db37c933dd670df38339e47ca7e396da751fcc38279d5c89388f9e0f8674313f9defc19095723d18d2a3dcffed20789e0753e8e9758d1c3c9845628ea7eddcc17815ccfd439cc61f3b18efc7a452bcc87dd7c11cab2a72a84acada743069a4ac06b1c347da7330c8e4a852a932b249cbc11c1fdc8b6e778fd47130d884f49165c2f2633898e274dea7b30a91f70d663191d01a6929a4d50d6690652655febc16d63698f751ead8e0db3ea96c307ece778e4fd760ca6b1de63188f97f5183393f3ef2ef68469f067350b390fb2dd172120d46472edb95d16730b67547f0b6b3f6d10ce6bc171da98b65307a6cc7abc617134132982552fc4848b89043c6605cb34ac15266058d88c19c7a921be747913b1706939e854a29447a990f0c860e8e7d32f0862ede17cc6a5ebfdf25393dce0ba69447252d6ed50573ecd19bdad9d7dc8a0ba64fe7a525e72885565b306d58063b2e133967a50593e5106c2c9b390e5965c1d8b223f3293ff44815168cd1355b39ee2b985e2d7b8e5fea37a9ac6054079572889ce2034755c1fc1e2fa37af76419a3a8606efc30d351c67b979f82a125db7b10998cdca5604629a2faebffe7f32898b45278a40e52c27aa0609a0821d3249a95759e60724b4fe943630fd2718251dc1c4a3f4cabb89b606a282622eb19e6a599608a20692b6b99494a2fc154daa11b445909a60acf21d597a50f71128ce136973b9dddbb8b04439609be1ec1101e66523db4faac463045cec152e8b7ba9c16c1343afa1a3d11cc915274fe7031a196211825d885ed76b5d44230c5d4914e5f3339190463a472dc1b21fa47140004839f383899ca59bcfc8529bf4ef4b7a02f0cd2359f7bc45e98371f79a989fc4ac80b536af50349a99a38d985792ed8e6a5e3ae90e8c2d4fb686d7fde76be5c18cf51e5f8b91d6fe570616c64db0f4294ca92bb85a923fe4fe5982d0c49fb6cdee2c3eaab16a668d8b967bca1d4450b53636ccdc2ec0fe3645c259a2f0ba3fa5a701cd5f29d5818928458687d20f9c2c2389626921d4409788569afbe533a69899c1f097085e93fcf8590f8931d3f12d00aa3f75eb213af2cff19096005b962deab304d6e75b47d6b445b15a6c965ed91bc92073b15869056263af6176f1915869071bf7bc8a730e5a892162cd64a8a4d61eeb0f0ef713e470697c2fca1b6515e5897baa430b68597ee4721bfb2a33035f6468e234f3faba23064774ac6b962594e4361967f60b99a9d525a40610a91a1cfebf909439ebda9740ee2bcf484d12b4908a5e3f9b2d80973a8f5e9f83cec2872c268a1536d7cb896876cc2ec3ed697454d183a5abe8c1f4c86db65c29011f24df98a09535e744b572e6186958199a5bc36cf1286341d75c23ac8cb2a6108693e1a78a789a82861ac4fd9bbfb6f9d69128694e63d5db5f9684918a5c36765d70e138d8459a662eb3a1ebe080943909249ff38ab446324e011c692afb20cce778431f294e5f851234a5e8d30553cc6ff25d9d3c703070960c4feb8c16d768ed3007511a690423acb78bdaa244f80220c26dbc81a5df8e94f4984d12dd794a4f55b9e8c12901424001186addbf9f23891d3d84318d2d4ea785ccf9d8636b041d9010d4240820418c21ccdd5752bf9e5aee80a924450086a000c12408816246010647c9dc28390410881a0010de840068a68600319e82101823079e898103957ee309f4098b5e2acc13d3ef10fbea0d8e0061a70810c5ec00292840400c28caeebdb5b24ab4a16fcc198e97ff7a7e60dfc60980cc72242b0bf08199790803e98ef622603cfad3b69f5c3021cf835a00112300d6840031a2001067480011260c00718208106342089eaa8b4ebe62262b148201087c3c150481486eb5701531508201834208f469218854365de01140003542c22362828101a160c0e1410081c0a0a03c280d0401c0682016120180804855c21226808b42700287841f6c3b01d0bcc63764bcdc8fe11e6ddc7250a8c79786bcab1db7b1e4c57ed62b3fab18b467c476e3acb4d080967724a37f50f9fefd9c7901577e50962849fa8d5e620264a442a7e2b0cb8d0b95739d809af7f81656c87c5f1a5a3db3007b7d3800c3cdf2a975db20da73bed259fabc6fa3bd9de6a533ba21f81fa0ed2611afef27c27569e00c2ee378323d0a29b8e24f2eee4f772d731042f5c3b71a2598bfbe31d24b77e022a6002e49a327666052ca21066f54653dcbe10d9aae9d884940f7b172d0b38426f5bc07e04222424c082884647ee01110b8ff5460a8021009de53cdda3d323696c6a4d4b10ba079f37f3e22a869a1001a6db76d07305284335991a464303555fc6502942b1daa5742d6bb51bb2859a617b2bec8a8488e6e85749e81e0a7ba50bc21d1d29ae540681d58a28ea2c6065b5b316bb7474f2d3c6c358110c452e5628ce16b5a649ad64bd652d9b9b385d50c67adec96e62efae17652154552b4e0624003337ad7d24907737eeadf88884b03e62f057cdacb85a55fd5fafa797044d3083dadb55980d1e083aae47e8bd5cf1ac2ce1080f6b1b4e0138b9018763aaef5ae7510d402ba07a1704a615eebbea7d0bd9bd8f379232bb9acbce79f1e318c0186bd80bda1d2dbf34ddca44d87fbbbeb700247194d53a6a33257b22832d6bd7126d36775271ba14f2988a43646281356a5c7044f8bc7cf41860008773da456f7c1d0b4c630e4bc1d2a520887ab305aba074fdb92d49ec0e6ba246a3f8f262dba52ae69f1eaa6601641aff91f2e44a2df72cd6e16e37cb395d78869f8e9f8c1535ce637ae81e054f6cb75913ade2e8c54708d4e8f2133798d163f41861aa134794ee4592ce1063d803da7a18a2570d33bfc130b658c50ee6804623ac2878c50526b6d74273fd89f434faaf98175051fa6301b55eb33661297589eefb2644400c9241a0fc9dd6c0a81aaf2542d28ef01b0f759324a59267a7c973fc9dc2e50763373ca60df23b22b0845654ac899a92782d30a0cf506228dcc615378eba1f73068d23ef22eab8352e83bca252fdaf9163d4a50ba257ade72d1bcd81636700d515b91be9c4ee6e461b5820b0fa824d362bdade87a12def168add8e643945019b61ad5d3eaaa3fd3d0738a4c52882eaf3724299d305789b87ddbc1ae6dc7c5d00328473ecccab033e2844f0c9e708d60a8e76c6c6d1f8a9941ed0b4406c8dc190459a016016296bb4652345550ee2ae28b346162a4302d8b34605e2711090c61dab0b6ecc121504c65eba63d4833cb43bc4f661acae9f2c0ea0cfbf84a42da283423b7208fb1fdb7111f03efac1e256a1026db353e5e5c32a5d4e064ecf238e93f1b0a9a54bf10b66811a4c7181a57d587fc99bcda26919e949ee4d14e74309e1303aa549184b701974e0f421a74bbe74754e80fe37884531a48170e077b15e93db17ca3756410049bc896bd4087729a5555023aec7410977930c0704a90076cc99351702d506526931b16ad053834da840c8c6afdd030078360850528186a397b8261dbd40a8ca9903fbd512d5c7422c642e44c45969c82d9f39731501c0a428084025c54d062224cfbb5b8d038b42c77ba206a997e54d94547ed8b08fcfc424b5acae54e142eb5c5d61234e1ba8197ff46afa0d72752a23c52225a02088ca27533a2ea06d100a4c984fc79a4fbcee002a066b256b096af74fe41e4dae758f7f3373d3368ea73711fde215ad83b447beed43a62977d18437f09443fff8e5becb68182962a57aef700a0e84720e2cbf29602d3bb8c2911574eb2c0f96754a702eeac47a56efdeca3d251f153e21e9773f1849fe13ed4f53a01e4b382c7abc7b32315b456a333c41d7278625ab83d958b4a24ffdd55dc8532bc403e9e1fa125edf934c4765cd33fcced471b783b7ec513939099be1f001007a3d82692d13f6056d17caeb510ab4db0ec9460357334114ac96c17d0d1269c27384a362726542b413f8c632cea233661d86557853239c7d032bf14a205dd902675f10a418c3ddb36fde84d60f85b9ab67234a6767c5767e562a42f9560fe068f069a654ef06f0fcd73f4d8212cc0d07a598cd0ca6e530bc57d648340a8145e5ec0721f5a7374d124b089cbc57c9e6ea1d08a31178d4b4cbfea1872f7554eae8aa07f9fe39dce8aeea727d2d97afdd4961be4074ccf9b87bd3c9b67ae3332a41da82001c8de85cc2864680ad531df6161e9e699a6028cd346e769b0165447e81f46f81206dafdd42635ca2c00182352ead06d28d4259e3244ac371088c0070d3369382b8e7d88965c662d07f0c029bd2885780cc941a840dc8971b74bdaf81c270667c60a28727dea1327b10257924a92d92f290e01207bf439c6502406b0ab95b1b144dc0a426b8066f37bd882494b90677e8b8681e459690259b2402aeaad41fec18eb6fc6f25e74234612bbbf2277257879d8371b15109338593cd0e0f7407da2af98c421948868403fde2890b565ea040b6c35005688cbe4b0d5f3297845a716a4d6e9fa2bf7c016e99de310c2624dfc864846c83e5ba14f158d5d79de49bcd530d072eeb836fa117582db540ce9d3a00eee805691b19299c10c5cf6ea3b57323cedc5ea54d769e3820532db77c496543e9e2af9c74fe21153568aa5296b226efa2ad5d3e1d6b5068b292965e725ca1bb45becbdf8ad7e8dbeb33e23ec795c6c47a57481b3c3d02cf26f8d7923e061e8123423e73e61db17821f305210d01b67c61e5ea56eb6102e62a2eaaedf2516ef3920511f05c24bf777b3411ef6e798d7d7eed0e5ad4b0062a87b780987ed79eed96dc7d8623e2393641ea5da692acf2705e654b46e74ddea5ff72e55bd695fdaade985e905e98dc3cbb94bd75bd55bea8d8ab72e8fc626f0f36efdd622ab69b177f90c60eb94ebbed6cdd1cdf2bb69728f60edba6639defbd9fb94774bf7421767b7ba37be37a05706af7ded9e5b0950c54797a0d9ddda2ad5b0907409bad8ba847535bac1757774bb74ef747d74d5e866b3ddee811429155d7b73780b75317509ea2aba8175b7773b7b6f79fd75d5e8b674cde9267579ea32d465eab6d46dea0665f7b503243dcdea9dccebd055b3abdd9bf978cfc5ce000ca1eeccdc5d9e34921a2598b237b6b767e2ad538315679499a046d3bc6b42ad9d98bce807710b3f01271842255314c461359397ccd3c6c32e005433aec874fe686238b151636bea4731b2474b32230ac296da19c1b326459a19977a256930e40568d10790053d88f529381a5d6c50738b0abd51f7f922d7b9bd44327b776744132967d951c8e1108a494724ca29f726ba43a2e0c36104fdd1f85a9e93a0c0ba9be235831517d3ae49e453f154b1dfc6205c3d7624ab8b908cc38fa1d19b91f7e23f1b7b3f28717ab606938ed9f6869c2cfd7591c81c8bc78724c802d2007c0cb3860f2b2302d388d190a4614ea94a70036a0c5de861c52e4178b27393982e3ecd639c49a523237280a0233f444347c0434303441366943899632aaa99335f86beaf9c9d99e347585092fa6b126231bcc70ca676e95201bf36416da94920baee4fb92fd34e443562a18505ed480abb3efe42bffb0a9ce9422d2e4696e449d7374d784e71bcc930a9e6e28bb14ef6807d80749026d7d3b2a17f9023c8103412a60d075181d06b298a56b2037dd71100ee36c6446439b2d84474ab5f4b3d3ac3f396f45e05aac51429e801aa30db8c4c5eea54a94d7b18a89a23ba6081f25328a9087ab8ae0609fc2a5078131ad6ed45ccedabbd6574b0f9b68bef3df2362f20453817c294f7711a7aa5ac0f847051e2b422fcee337536fe8c1663df9f54be3c75f6cd904e78ae3a9564e45eb1d9719d5ff95ab0e88e16312d9fa2d27c66f121537fcfa1950a1cd383781bac21da21006427f030181348ea64b7f7f4ce3a29daa70e70ebdf314da84a8d9573f4b902a7ca536b627f827d5caec45705c00bd801140e00f3159d5a337ff78b91008610c5877e6033b46873d44c3c49ed0641790e84c90969891555dba181ccd29cf97c16b0c628f59675640f2d4b35e85605940a2806fa0b820cb394b42135814c9606d62acec0191ccfd0fb7ca160c5026e815581ea1bd8824af15b1b02cf023304f4d1c08be4d358ff06064590f97640758ce0f8fd4d5d47dfa69029e6737f3d608d5dd20c268f92327e327ae8d1929a238024a027f04204e285ecfb05a3c09c0098003c01f81488e7fa9ccd0b01d4603d00b20046193fc62fae4c8575b0d404bc30a63427cd2cb2ee6b6298aa227a1c5a2b23652ac323bd8bddb40d95fb489231ed696b0702f611787934b0bea004ac587e2cb4b60f1f0acb4d8db91a486c031e319089f5eac8a106aca9ed71bb9fcc4d80371836f00c82cc7d0240d409580788175dff4f206cdc795e78354fb0e0a15687702a333720c75a632d8d5ab8e19f4b39a05695081c0a24b57b96347ba1cb7155803a27f234d8496ab6f598162fa97aae82d518af42ee663596163d8f4c06e05d14184d20aa5714b736e9b46a82009c804f2759baa27b0e0d171a78340b5e6827545100a50156e75fafb109d24c12c9472fd745303d1612836d437b02e7fa28e0294106aac0a3220551fbfc31cdbe569b5c2b34544d3401118cf72a2dbfacc4484f6e38936456b0ee7c889a9d862d4ff0582190236018188faf4094570d6c16714a128f98882b92e25fdafc23c88cf74afbe0cf1ba3d1fe18d6c896784e15273e77160450e3ffba6cf9fb84045f4412bb4d1c2a251ba2640408613f02f30ccf68ce69fad23a0bcf75d493f107a02361ed536c41b80c42fbc80621f364e771d5fbe763ac7516e91093a16e3368b065c766b01f9b6374adb6c7b7b9b649cf9355154c39d33ab9d2eda8d79bfafb46c5a264292501de00330370886d43510dfb1d99472ded6c3834eb7980d19e776bef8492fafdff3ebc1f36dcb03e864ecc44ac011deec3c090cf5178d982a2c870630a5af7e19873caf9035546e3b6df35d5d68c93bcdaa0f911d708c85de8ea88b2c5e1eee043641b8fdbd7e41791e7c8ef5eac38369a10bf798013274628953710b4352727e2b302103c341eeb5bb4db3306c052daff95e1c4fdbcd2111c7dfc2703155774aed5f9937b5d340d65a7aec9b62e3304324a5fba4a3b765b83e96a2cfaadd085e50d6c8e018d6e344a0c82867077aacb63230daaaf413c4d938b7aa19b6c76d4432dcea8d48fd19f99e64d472d80948d6dd1abe8efb8c4f72c8ede70ea744c6b88e39be4ecccbd34af49801ce567c06910d4d921404f02846d0dde9279d345945ee3c36060dfb433c21742212011a94c6503be0b23a55dad4ab7a37b393796aac78460442ba0fd96a33a1b6cd43ef39eb471eb35019257c292029bdebe9f1a0f8aef0b8d7b2404a8f1235c8d2fb24bcddbef1d63dd6b99dd30bbde78408c165e99277859e3f21a019aa1c4860a1b5d47d93447f335a1404bdc4533b7c1a7ea58cf4d0cb41f0cf9e256ef51cee665b9cae051ea0cb4b109b1df7b183b03539d97df0b25a2da2d1f2869b0efabeda0416d424875fdc288d7971cb2a4d1af41d9f3ef0eca361c9591a6abd4c60e1b5adcea0c3bc34bd2827a8a9ada428c000a2b0b71fa72c78ccf39a983c02e3a4641802e5cfee22de65157bbb46186c4dadd40187bfae38617e62df31f11f48bf5c7eb18371c8395965643c6eaa0eba40e39d94341e3f4c0987ce3d11a4c1cda08c9d61a9dda85993c4ab5a44849a19adc3522ea9f66a88d79db9aa4dea089712e03e641c85779f00b72b1b4794443243a496cc764b01c06938a7b2ec9a7904649896e3b06416c606f17ce2fcf0ab26679ebf9c502c5bcdf976fc78a45ce9c08669818cebe07f314be3ed78145b410f7542173465789327994d06798e7758d59fa38a2bb208e1dd321d98bda6b7111257a90e645ff50d26629e465c621aa8da2a8289ccaee0aa4a4097c47151e545102189f200aad1f269a5143da2cc12b71520fe2ae32d05889831560be84c603632be47f049f288e4c0f54daf3b72d57e49576b3f82d9e912ce1b7b5f824edf962fe8270855383df2b6f8c58b8e01cb3a385183219aac3c7e771ba3fc11174950f1dd09c70726e9086e2cc404fc43af88264b16e6b0a0d8b9d1ac037915abe80fc3673edf873227916fc02ebaa621c956e1d3a59f1abad86101566099c9134682223d537aa812b8eeb6dcb61deca71e3cd3697920f739a576b1b8b78888826fab4fa1ed48f995e91589bb153ce20f862ee3c2fce36271d2a614672e2ccae9739efb6ad8bcfd9f76c4576c2a6886ced4602d067abc9038b0e4295c30191e7027725536029295f7a2bace2ec287e1d3fa5b0ee082aa7a68be185cdbf24a04e421144772b777d5f37c18a6393016cad56c51d10780933fb8b73c5e13b5b8de768f0d96deba3f8d3deec81241192a786c7159477c9a80768d789f5d671b4e6f3f27ac2e6419448df8cb41e491365b621f35761f5a6123ba417b1f7cdf906187c2048ff75ae33fdd92009bf4c11e990603f2b4db30f51cec65faeb416729f00e190d46faa93085cedda2faed2e173a76cba0d7899d94f5ad1aa096c29d0ca02701cc68d426c6a8c40be28d902aa40c637ac859ca0d01c2fe426ff9b622ee9245592cd45fe2bb0389436b3675055062c79dd213bb71d06e2b57b303f5442866342989349b8c95bb26a08144ec628aee0db8c8aaaaf6b4c9dc3991c45fc67889675c1d3c8d0e7d50c77f14505d5360fb61f062a38e90d3aab5ad0f0a593e9dbaa7ada40ba8331ee8da8b7fcbd5a8c1f5e9cb51e96a7f3daa92b2cccac84ab4f4732020bc573ead62021fa73f86219f9227475711bab08c9b0c5967e10cb87d180eef90010d949a87589eb6aa7313220bd278137403338f8745c61da42aec64de35196e7f9fc796c5e93169f969f2e3d42d80ba01f38388dd357292b4e796ce073c4dfbe17f77b9dc2d78b774527a0faf5a0695e095d9892eaa5a8a8af9aab16354218d4e54a07509f809b5aabff96fc97ce1ca9f359b11077f5bf59d9f79b57614d9c3a09287066582d8ae95db69a04410b1344442e20a24f193f91efe28cb4f1b260e8fc947928e62ba5b7abac2127c2800358856c7dc91a2831c03ce542cc35864b3ded9a9fa12b6ad10c89c9b9cf286edf90891397a966612226e81cd19ca3895264d95cd921bf825bc4851308148974a20c2701d4d98213ce8cfbdf043a996b62c6beecabb85710f5e5bdd0a5ee3e491625f8a0e0dfa1ad416390a44df223c575448634195d68cb568eb7dd86822b1af542132e7ba763372e7d75889426bf69f9f0e2e25e793054113a11fa2092830c59d6ac276bf988a1bb9a5e9704cff65a042ce8f66a712a453cec007ed792cccf4bd3676ae881d027a216130eca1326eb368b33b56b539a05f9a445f5709d916092296869a80decd12bfcbb6c5b53e2dbc9607816516d78266b321ac979844008500d3805ee81c4c03b307430e4dcbf730c188fe27e707478297273d8500cb8c620e02c810d6cb4afe2014c0c870d620f8118638b1c2509fa930c6e2414993f8860679444efc190b032511a0227c1852012fc085612f002ce4c958c8227207148c963c36e1bbde1057d3c6c304f40eb373070212ced11099835168b12e1eb587a3c1f25460f6326181121b9939832320de7736406413fb650867d7e2f9d892643a1dfe1ec70af336f95a1a6bdce4a07e9d00e72ecdfb0fc78cb685e18742a2074d20e67e7d371d605f43f529d30679d8373be3995f3bc4136f4da3427ec591121a0bb9d3d85429fed618cadec0cc19d1fa8aa4823f57ca12008aac1a80102f6388b52905d6734b18ae81ee6a720f41c3d7fe72879608bee7e29c2d0eb70b46bc40e55fbfcf5b0b54d8051ebe9e3fb3309140de901dd6bf6c63623f40291816c58e416e87e02b6418ea29444171a8dda4cdb31f5c13d4e160c9d356a060d342d7eb3f2a94b6848352341422d5590e940928e6ca5e1c8e5b49530444427eca9fb4a00c64b9d3822a8ef04fa72fc28092f0142e675637ba6bee4e47fc821a91dc54b621db87e11bd141695907251d89e003204c72967020897437d2c487f94191d895816663207892f50f5c3c53069ff5583b82cc21fc877fc853142b002897777718c2c9a205b028d223fe2cd25b741321ec3312a688a651cd96bda10869846e372ee316602656142b4a27a2efa832c83748fe2c4acb634a371c1393146f56118773454f86261a3de881a1a029fbd213700921b347bfa0b2c3b27493844a2147b4428519cfde78a153870698bc9393581ea5c6c1cd234dcdfcf0712dabb8d784d700dd86439101778b4527f680586c35d502a7de44cfdb575a51dcdf4707720066aca2fb17f65f3082f69bf46c907b851498398c4aca980a205ab91fd023986a130ac00e0a975ae6c27506ae0aac428a954aacec2a00204069f5401fb28c7972783d69c658cd4ed003ee83edfcf1d74bd9c1001fea02380f4bb018a72ad4f094efe05d1b78209bd8c8eea88cd9f83a60d17ff41def2b8f8990a51f0a14756dc7a8a1f83ff4ec3624b2aa19604dcb0868cd765db24e644894812dd59294d1e42846d0fd0996ba8c4dfa24acd69997258866fdc108e7801d0bd06cf97af9237c185949fdfb347095f1e1fc5f7e13cfd60e3306568f5e034be7c988a627bb7b5a03a2a518b76ba0c1a93efaf28ffa18e197be41fcb8b515340bf952e03a15babc2e71f6081add9699a2180ab9a785285f12df2fe1e2f830e4f5f71d0b5d466cffbe5118bd9df1b99c8452b50942da03765d91b08de96b537ecc6b2d24861af7232bb696da9b8e0162d06a03edc743e8dea2089d329c4806b3f9143b3628fb887a07ce72847029a7940f683b59572284363afc1825aeefa546c7796f1ac6eab9e8a0d228e8cbc329381da2d233620aff87013ddd3f5fa1308e8501cdb6cb688702aa97276b8bffc7cb6b61326dc256b635835cdaad5f3a8408258229cbe53c07ed5c986e1c9824a444e891c635d8a402065517491648d7904332da6131a0136f8b5d423cfd9c7d777b9bbd548546b6e758d559b5ec5357528e2c10b5393e90d8f1e3ff142f4e4ad746e64a1fcea294816d42adaa5060d86101b479b66357c6408b5bca45dade32c506e9429dd0bc08c80bbb39d300c38f9f812fd4e3763f0f36914562be493185823ab9b0ad625b5091ea57411d5afcd13f783e130db6bf60486e6473ebf0e63654fd3083c54dafa1571f828e82513669f2ec7256cb41f956392e32ddc50e03dcddfce9ea13ce29285533a6f9b96a2530c2121da4036d785fdc56f868ca8bc78a520ce8f90664666edfdc0ce04288acb83ecf87347483a62f280fb67bb9237d609d61bb1fa9de7c2ea35b79aeaad7a47876c5dc2c6e9ab13db2b15765886d84d585103c3c954def2a77899d243c87a8211e439b36c36821a26110878f19bb03b043e56f00649bd6d451b26b862d751feaa89be512fe7f5718f7180b8da6d366eccc8838a028aec9051894469aa3e5447b4c956c24dba949865ceaff2c6bb8819bcff44960a0a58efdbe79dbc7655aff57bc5ae5856813228d1d5625064b72145407025dec3c7f0313c860ce3616c88c7d18d1fdf6af93d648c9ee133e40c1f63c7f8889ea747fa003dc63a18bbe19728de5a580957402ab81256429410a5fd0407037cab022e0945d30c2e30f44c07c0300cc3300cda01482723ea275a262953bd917049fbe029a594649229aaf3856eb3b7b8781306c0ec0d030cef0c990c6bccc0a48e3f1e3c83bed294819310b42575eaab774b64e0b425dddd1abd17b43406c6ac7582defc295e2c89810bbea321b7745222248581d3eda1b282aba8094960e02745d024727d8b9ebec00995938ac81b5276c90b4cfea8a671439267495de09365483c2b7181778fe973a6243fe6fc1698982c724e217b8ef1d7029727fda79c449e907d165813f1d34cd5c5ba1e0b9c274b5e810d292491df9ede222b704987a6ceceec243f55e0daba73104bae993915f80e66426e2ae96bf514d031e4a5c0df0449599d8d026b22eb96ac160aac6f7c513944ec20ee1338bf51294b595576d4098cc6a4ba82d7046eef47ab6b52a222ca043eb3ae77aaa4325f02972bd2bccb74106e2a810f19946a0b39bcb449e094dec58cfb691e3290c09b89a09e3976a4143c06ab7df7adda71ab640c7e84ab6f0473cdf96270e6f927e4fe536f1383fd53f2f482f2ccd80d8317ebd1d14b04af9230b82e0db223b7c6206382c1a690c94ac8d4a94cc0e04d4b4dd2a43653fc177c998ee8a8eee611f305af9aad74647ac1c9dcaf3fabf082f1be4b757d179c6735355d962ed85c9a3309a956dfd5e782f5bc96e28237299262a6102ce4c8df82d38d41e4d415296ac8db8253a1272515f9d335f2b5e04534a54fa243544db4d0453bb36024e99bd8953a485216ec05cdbba539787a1f0b26063f931137b38960c188084f4adf2ea7f2bc82d399fcd4c44da9992b185dfbb1f094a023de0a4668767eab9d7c7956b012e2e73821c65cdf2a38d77c6d916a5594425e4d1e940a465f8558c51715ec7607539fdc53b039a6d469d3712bafa6607b7563454c0b9aa3a5e0b54abd9668b67e9514ec5ede10743b0adef55424bd59535da260729db0a4269a864443c1a7491972c9fda849a0604f6672d141d8a59c4fb01dd3baf5588ca07b821fbdcea349fbe59d9de03fc8309d5382b61539c1a595143d65c9f4eb6d827d2ddb94538a0a71a309ee92e4cab66a67f94cb0f1236fceb969b3334cf063322b678fb14de8126c868918aa2b8778154bb0b1eb92dfa75025f8dfcf1b47a41025b82c3a94a687d41443d02418b3d19833ae95984912bc5a3271bb14963429126c7a7928ef8a41d40f0946a9f014235da8d0fd23b8983caafb49d09bbd23b8bceddcbe135522df08c6c642fc8ea0f47fc40836a96707b10a39e5a045b0a74549bbed76ef902238efcbcaa7b35a5f28117c90f62ad2434d731c119cda9299ea2be27e7e083e57bd07171982abf41ebe9f7d93ae0bc196e73fffe8e32989108c07ad5e291efaf941b095dba3d65e0ca11404e3ff9a4b734e4d6f03c18bf2e02f2242d40408c627572695eb1f3875d1204ff503377a59aac634fbed0327e27a8bf4732d11f9c08b1695175c62ce31b80776eba4077624fe991c15cf511e58d32a39b978e0925a69b97777403533c60edc48aaf209eeaa2953073e644e971444f0fbe9c0c94d1e938b6af091cd81bf8d3957abaf9d6b72e02dad23a7cbe97bb438b065a2c181add4f5a53a5ed0aebc8189eb254b6b2425a63a6ee072355489324badd769036b227a760b62036331a70b5a2dc8ec92b306b6b3a84f963fbf29c951031731c6dca7abe4a7a434f0266388219eff65484203a73e9f921efa33709ed3c3abfcd2a7df0c8c488a1062eecbc0e613a5b53c3d6a069181f3af1b9d2a2a92058d81c99fb2e717fb10e111031b3cf577163d0c6cb01211aa93fa2f0e065623c61c62a6da4bfb05d6bb42b254773bc9f5027b9dd72ef04125ddfd5791d5572eb079bbd1c34f5be03f9ace0949827237b5c0a6dd9861a636be995960c543e40aeb4875251638a1fb2ebe05afc0c62bd9d12afeab67053e9b55889ed4b847ae0aacd99806770d153cd95ba7f29e02fb976731433785ac9102d79f3541e5fcaaa489026b57212b9e5e51218302bb296fa6c79ec0e7d89afe39bdbfa8b813b8f4aaeb6b294de03c96aa18b4a5a47b26b0df7ed1f73693a8e54b605395573e253fa95f096cbe85a77852401238912176f03c22d7961480045e735547da1fd1fd8fc1e6afba96161983c9aa7942278fe9e58bc1060f33cb8c264a44c460cd3ff865ef87c1e56c7929054d31a52061704945758c5d9dd99f607021887bcac9fcd27980c1dbc7b4aa7f6dbd9c5ff0f9ebcc7f33462eb92fb8112abd66ab4f5ef15e70da75f725e478c1771cf11a99a7b2d9bbe0ce644af9e2eac9af75c1f9282d79335e69a473c1d5a58a3caa6d927071c167105da545d9e5b6b7603fe97a336dda828ffd262c45e224356bc14aac93a2b682789c169c69266517eef5599c0527e26dd0d4a641c650169cde50d5d593bce2c5824b23430c792c491fc182efa0469675cc2bd84fa362901b4ca5a87105232f5de50d31dd0ab67c4fd684245459ac59c1c490360795bea4254baf82b30a2106192fe69c52ad0a6ed3eba78b68db24d2a9e0f4e3be6fa729a5218d0aee56947ed091371ae953f0125325517e320557a12b7adf4e474b2a05f723443bc716f52a9182d315abf43ccbac4aa3604b445688c12dfef9a2604d08e9a6f25a48c20f0593bf6d7276fe0eb90705a35a2bc7f491bf3efa04a794ee90214fb031788879c334e8d53bc16fe6b6dc3a32a4d539c109adb5316766accc37c1870e9a36794a6bead6045bb75766522be798ce04bf571d6697f3fbb398e0839d59d273f74bee25385d9d94f45e8dea5b824fb50ec9eaed52502538b9c127e91825e587127c5a7ad795d06d4f27c1664a6c9b14950497a1d62a84c50e291a095635e44dab97eaf484042754aa8f353e8253eb3b29a62b933982bbd839a458d9da62b2115cbb47c95bf9a7828c60b25ede360d17c1987774eb8f22d8a034dcd293ddee26116cb06cda93aaa0e46b10c18dce88a94e08891d3b049f3244724dcc10ac057d5aa2975e8da5107c7674112dd512a216bdcfa3fdda41b0263bc8979833c95cad20f8c9494dbbfb3ce56a03c15bd29b629937640f5940b0db4954c8dd183f4ef60fdc5e66e7f8b771a364fdc0db56ce932f047df9d407be2c9be81c3dd976121fd889a369d54cb2c9d31ef89520c2275dce514f7a6093caecca8fa09fa63cf01b29c894f9c703f79f76f4fdff2fe6efc0a74a954f56b251526407ae724e2a996d291583eac09e9d342d2934a40bd18155b3a03249d38e1134076e37ec3a88305da72307feb472e4d3afaca41307de73a974a33588fc3a1cf8b4d1425f820eadf91b389d2d94474ea615d36e603f66affd966bf8db06367b07730d394e122d1bb8bf88e943661731770dac0799d93a79303fa51ad8e03fc27f4dcffe4c039f134789dce02979120d8c9550a6d267e07376483a5494bf976660b54d721a5367da6519d8f6ccf5ca37f164920c8cb75587b797c8791c032f297a5041796891a318d81042fe1129775fbb30b067afd12595cae93760e052142d49b896b4fe0bfc69e8b7c6161ddd0b5ca624b25a4c5d60d3eb2787fe8ef91a17d8a0420a522db345d3b6c0757f7aeb8eb4c065b6cc9e274b3734cd0217e298523a6a4c9d53140b9ca766bdf368138445afc07d6a9a4e31e6d1a3522b70655182bf264f0d12ad02ef2b2ae9db1c3a1e512a30c2437f69d34f81d7a8a93599764fbe14589139855665d38e4781094a089dea9d3c68a1c08ae88c5127895e143d81d1c13bd47c4fc5e4042ec8102a4bce2b3adb04aef25772ee18325487098ceadb324d3a4e3097c0eae9edf7e80950022331782c551d32783a014960edb36ae9ccc9b5b30940021f546eaea04a95eaf6185ca924628690bd63516370124ffdad697cf16431f8c827c1748d12b64962b0312cab4ecaa376c961f0373978e57f26e1a330b84939948e1083af2718ac45dab2d171b73bc0e035fd4a4c8929237bbfe0e48b29f9ff49a6ccf9823ba126c77cb97964d70b36a6959716cf0ad5f082338b31448e0eba3bb60b26fe48522ab787654d174c1211c1f6b55cf0ea9ec5ce74f89f70c1a5e062e941b847f3168c90394753aba42d1893346a5c2dc74c95ac055b2a5be8cf49d28253b5dba347335696e42cb8a0a2764487caa49705577a53f4f190fe3bb1e027e74e7bcaa4086d820523926d25849457f05f95c6a4a5a9cf2557f0b691c23b4d63ea5bc19dbee5fc2bb182af3e8da0761d94c8af82115b31692152052f71437e0c69fc64502a38694906114d4d9d205430ea2e6ebcbce942e99c82cd1837546ace27a2c7146c2c79f9ba544c13724ac1568dba8da89382dff05d8d172e3ae2a3e056549d55d9eaed2d0a2e67dd9eb421294f772898e02d16226332dd1a14bcfb958f127a54dd8b4698ba09d2dd2d6a895c0e3ea4d3df670896e9233b234bdb70838d2be4d843c9a107d63e59d2c1f3ea251dcb1c79c0b49db8ef85d2cfc822c10b34071e380df93958c9cbe4b8035f2fa9c67eff1c76e052db06cd0d3a877cf91c75e0f3b7a913225e7a5242746083c48d2021f5b5d83d07c6e49969ae38ba64a939e4c066298b962ce418724673c481ddf40edaa39aa49895030ebc896c6f956d3a39a6e47803dba66aba9a9b1b1613e4700397f9aae63922a8944f6764f9e004a1ca07a7064fea0339dac0b7da85bd5994a03d31482d3863c10d7c08600939d8c06e985f4e9e5f21f86f06a936dc60430a39d6c0c4503299f69494d098c9a1062e5f0e5df9374a47093923ebdd5803083e7ef419ba468e34986f1f32c7539adb33b27ebc1a6bf82055a573a08175cb8be4224bf3c11b6dacf1c60fd00739ce6006377fd24b95d44644ffe1c61a69a4d1df0629c5f0214719f814aa4ea787d29519e420039fd35dce18dac2418e31f0b9a7fe3d45ede0a532258718f81c74de56c8d4ba1e623891230c7c0aed9c7c6d3ad37f4891030c7c6fc4902a629e81c8f10536051bb3dca2eebe792fb0b1eca35b577bce910b458e2e7096f559c3f4735fe6cec8b2f2460e2e309641c81c838fcee944c5448e2d304a984e6b25416b798e5e22871698782b7a255528b12b0d1f6ae4c80293eb34fd456db5b43158e063f55a26bd4ebb529121c71538614af4ab7bf2bb9eacc0da47cd69397455e0ab7436e14174c897c91c54e0b743da78987e0aace8acdfb7bae831e7a5c09d67d985e93e0a8c34fd8ef71662e7eaa1c0995039869043f3f6839ec0c61c9537b132e8582327f0a631761226d52bc94f13381973905c26e4e5c80f13b89129e48d77dbd7b62f81cdb14a62a45191eb5702fb39afe74b7be531d33992c0e8f518e48aaa1c91750e24b05be72a4295f6188c6ef5a8f1f4c6e054a444896161133c5b0cce563572c62d75ff2b31b8d166f9e9df47897c2294d24f6d61be30f88bda31a83011330f06a3d094f5844c2160f0934fe76aa6f70bdea3c9cd79359d47f37cc109378ba7136b3508af175e1ea5e993a98617dce77429877e8d21b4b60b267d90db7551f7c4325db0a251b4a511a973cc4b2e18b59eb137c471c15b052b9119928eff965bb0417e56c8ce9c1b715b3039e25d486aa337cf5a70a9f7b53eba9b6c5320c0410bf66fb38bdca07365f9b3e05bc74688decbf31c248beb4efac45c009e802316ac464dde3d1252091cb0e0d63e620eba27e6e6fb0c385ec17f2595bc4ca718f2775770213da36d57bc15fc0711a38e055119496505ff5d6a4d2611d4242757c177088d5c317b0c1d93aae024673291e36e59d61a472ab83555329679f55a0a51c1fe5bb0ebe4962776e614fce78570d3da925b1953702f1a9e1ed2941c5529051bdd53c89734450aae62cc371a7257d71e0537d62316592d578a105170d263e85dd0f96ae2474b8123149c7db9d6e84e5b891a5266bb800314bc8ec689e2d92757868001c727f8119e2a2a07bd1b707882fbfa1eeb2455947bde093ef78d891cbae10427644cfdb46a2262fe2638a12257768d29f69aa3092689143d6f3fa57c9d6682cb31a8f68e12d318494c70233a6ed01ff53fe5ff12dcb88a4a59eb5b82915fd2d4739b3ea16b2538b1324dbb9d39e61e08fd8312ac464bfadd2448fc0d4d8217e16e6a39e7d4299592602547cd9bff46c41cb148f0233a77094dea676b59230dd3230625e8d182203c2afbb10624b8bcbaaff7af3d820d3a3b67499db1462b1c8ee0fb3679bcb2246358d8021c8d38739021652edd703082bd4f2aa8bfd469cd2b05c722d858b1dc3fd2a664d32b82cb6b95c34af5cef39a08f6c42a28bd31a7931ac4020e44b01d826a9041241d55ea21b80d8dd143f7890a3186e03afd8a0a5a45c78d771c701482cb9321aea5f897fb070e3808c1b5c4fea0a1c34ac77b10bc9650ad1739e23db136e010c439479b8e95750c04fb92bf9479d0b13257c28003108cbde86cdfba9f29df7f60ec73cc9f5c37e7a6710d871fca142d68129d31fd81d478630df7e1c61b6ca881868ff369bc95fc81a30fec470b223ef0f6bb1a4b4fb34e74f6c06d4eed98a457f4c0694c0b1559724c9a3979e092ea5ec490f2261d723cb05f4a26153ceb77e0cb4786473ecd22bfb403e31ba2e8a471720af25207ae3ae6edfe943747a50c071d0ef24c86e690691f9c1a20351e0666f42069f0a7c1c61b31e8e10215f4581c73684d04d59e3b38e4c0e43bcdbc23e346b63a17471c385d9245858ce8c15f8503f77a1be39d9ef8624938dec028e5956e1d93af664f0738dcc09be86e9b763a6ded3a8e36c000071bd8d2a642baaaa4ac556d0d5cc5135da39b995496fc0fc338d4c087a0eee717b4d8c6ff8cac32caa8c5910636568550b2be4fafca33b2b48c4f0d8332caf0e183130483030da6bd1c3d6b77720d367e7c1b69021c6760fc933cb7bce9e149e4c60f355ca0821e6efc0f337afc70e3cb28e3c70f37be87193dccb836dc60630061c061862f7268ac0c709481513925fe869c4565d3c9c089b9e4fe204fab4b63e0525fdcdc1ea915fae16ffc30c50538c4c08b891011f4ba6abcb000471818dd9362d9dee778908f35dc000367a7fd157307959d6f83940f4e0daea800c717b88b7be937521e871758cb482146dd495b4988a30b7c3699ab4ed856ae45896102071798b89f73d54d9b05c71618cda9a693444fd2eb734696e2d042e111433a9d459f8c5e8d1da00183f2061c596082c827bc36443469212cf092264e3a318921aa73053ebe5fa6241642468e15f8ee94a5d512342bdd2a709ff9224c945aa8880a9c5645ecd41f44852aa7c06e9ed893464adc37498189bb291ea26b8a8ac128b076d22c7a484a37f70705468be7d5cad021dbfb04c6bc6cf2c930a55248700b389cc09f36b9ba9ea993a6df04266bf09c646deafd424c00028e25b092264ffec871a3faa704b6b4b76385d812646db4f13ed8681f64158e24f0257b93ba5d15081c4860d2ad9f86a4e516553c06a75392a23b2ba5fff491e207328cd1e7b3fd9c2de535ad8719a709328ac176101237e8ea85d0103288c19ace9b7ae49f0ca219710419c3e0bf4442dcd28d38a6148230184b71572b88a44b2579910419c1e0b3377d8a1d2466061d185e6b6a8cf96cff718320e317aca9a4f1b2d65414ad8719a67c94410519be60a4fe7e75685a46196a05327ac168b9047d398bc640062f388b7875f94fb34d62659451ca479d25c8d805d7b5fd3da269da13a40bbe4a5269f85d0c19c8c805e3934aaefec8b738e91f3dcc28a3948f3a3c90810b3e68bd4b2ad364abde7a98d163076aa0d1c38c1e583e0a4741c62df8d8973f48d552113351810c5bf0513d644eac4e5af2050c64d482cd227314a134e46e510d2df88a6d51af2e52a6e467c16e6ad0fad3df651a9205a7217ed25d623285f0c4824bba1e7bcfb64fe50e2cd80f953f9ad0ff92d2fe0a4e65ef919d9bba72ee0a46d3a68a1ecbebb56e05ab1d4c6bc692413dd8ace024e52cb9d99e47c67a157c16eb903f2a37544955c15aa9d76ff732159cbe8fd6ba955c534ca2820ba946988e123c055f99f6d635e7d2b49c291819d3ffe2674592201a6bb0f13e4023066594e105324ac1e579909c62e8da242978f3a41f44e6890c9e1905a39ae2efd97753c88b824bdb1d5248c1fab3b550b0aee9bd23ed4f2f4840c156758e243b25fb88ef2758ef8f29ca4c98c88e7b82d35be953d74fa49b38199d604d4d0e9226e59c23d1139451861bc8e004a374c6a44cfbde049f6aecd2fa4a7b454b13559ba5c4cae0d94c70164a66c5f5d8256aec61468f32ca50031998e0afb7828841477efdd825d858ff13a4c9bd04820c4b7097714b978c71c592a7470c54d0630b116454823b95ca76f3e428c17ed0aba9be920ab19d04a3d299a5acd53d1b8f24f8fedad21b53ce0e4a6d063222c1feae8e25351ae69983049742cef311267d043f71fc949af4273c6d47b067a2ec844cf972096523b8d6b13079415a5b14d5e02b90c1085e7c47f2afe9641e25642c8233694975e78abac0076bb441022b3a90a1087e44d475d7a02b2a3f3f84f0292023119ccc3475a22506119c088f54b293e9108c8c755bba762b537b48a0061addc608d010fce9bedcf0e4bddd27295f20a3107c882749a52b47084e93a7fd51df4d25a91eace123065a2063109c10318724b6179a562c4d2043105cd09e4ec8ca91b5731e08fe547bdb86ef89849433b2a240062008ee1252a8b9998b866c1622e30fbc69168f5d79fa640c9e91755820c30f8c70178d9bede49b521ff89084a8b39492fc58e203a725afe7babea855da03af76428fa8ef17fdf5c00611da952e520a269407c653aece65aa4479101e181534e516fd1df85013347b9262be213b7057ab3776ea533cd581b77c59cbb64785acd3818b97deb7f3a4c898039f839936a58248d9ec44861c18bbec8ca3d27160b3ae4b9a680cc95c3870aa31445159a3fab86fe02dbbba5a27bba7d60d6ccea2c5f4bfa856b50d4c5a8f413bcf06beee834c59e93796b90626d5ede98ff7967935709721a67d926960a3460dd14797d0c05a884197929b3370294632ab7beb32e5e9901305e3596428a546e91c53283825d307b7fd78f97a030a3e8b4e72f7ec2973c77c820da1fb2f44fff3b18d2778df10112328d5d0a5e9045b3afa7324fdc1448ce1045f5dfe49d44b5d0e319be0bdeca38aca9b26386d422925bb35d73c99c8ba7d635025c704a3c6d6ff638e2be9be047b1b4904752247cf194b301a393dc653418e6e5689b5f7b3bda894e06b74dccc79a22e9c041b634e22e56ddbc44c125c4553a5af47876c2a8b041bd40813957bccf604094e861ea1377d1ec1e5dbceeea33a828b16fb63c468eea92b8de054f618d57a46304a66d122f8bfdb497a7245302a449294a73b87cc5622f82cd2b4eadb3b6c2c4470664164a4b4eb10ece98ab4963d4c6b5886605c47a874ab27357655086e4dc468764289ec2142b0d6ae294faa589e4283e0dffa3b8fbc05c197ae18dc6272f73b10ac78a552d91744574070394dc7f8a222e470ffc0c4a8f71916294810fa81dd184ab7e9d19fe286f481bd9035fdb87e7bbc950fecd968d22afa9fff92122c600f8cf2b47c9f22f3a64aaf61017ae0921297e81f2aac529507c63bbb0791738f9aa4c6039bd63ac7245a3705d1de81f38e95cbfc3fdf946a0726efc694573159072ea80cfad143493ffde8c0c8a8218920e29ec4dce6c0dda80aa64268d21c39b0779e9f374ad4b08038f01ab2c81aa99b9ae3c381516e96d79644ce90f537f093ce2b9508d11d217b46a661016ee04c62c42cc949c83c69342ca00d8c0896475f4ceffb33b1814bfbebfa2b663939bf06767412b3d148fa4474d4c0e49077fb94b7b7779506364531bd25ef4403eb212d37f37c64c89b33b013eb2ae7d04cda259a810fa6a92142779610b1332ca00cbc49eda43e6f0c19383f5dd264b94e4a311b03b7b14b6f8724d7449218383f1192822c57d38a8781d51d1952c4d366a8b2d13ef081bee2b0003030d6e95466254bf26aff02135d47c895d8bf5d322f709a72e70ed94f4e0efa2e70fa5da66f6495a5a0c2054684ab5f1276dac296649688efbd5ae022a41bbd49080d9adb2cf01d1eab62b0ec76495860d287d0499ab7ba48f50a9c8ee962caa6d1bb64d20a5cb0ce9aba61153cbd9a2d444fb73b1510f9efd2e5208350ad4d810b76b916e4b8e47e8d0f0b900296821215921610053e5ebbbb96360962211458f3effc0cb5ea61014f7044248ffb9af6960538c1f0d0eed87d63a326701fd1a359e6601fb38909aec5530ba2ce4c6eb78025f07a42fc5d439012f8d22d7d5224060dd5594012f8709341284f2a0b400231a668fc94478fc81c83f5e0a37e951eaf4a6b8c7e92f60a7631b88eab12b33f4b8ce368b41029e5ccf2866170d57c1274b583c23082d039c41f25841b0c2ecbd6bf9398b21c638051e5f5a4ea764231d0f10bc62ea70ba54cc51756ccc943baca0d29bde05ca3468fa35379d0c10b6c2f95e93c5d2165d32e76df8b4164754b9173bae0ce34d59d89d6d86f6764fdd044e8c805974247da889f3f8485e082cb2354d34d75b0fa112274dc82cba0833ee5b14f4fdfb6e0b43b87fc192af604752dd8b4bda0cd532aebec68c185143773525a6bb4c6cc82b1bc1edc42fefda98a2cd888e97eec94af98ba6251cca764a6281b16bcee9a9f473cbf4bd52bd8e47ed9d15ac52dc8158cc58f7cb1528e97a3c408a1a315a8124965c9aebb531356309a557692a388dc745ac5a6a9323c644a115505e39a3f9bc7c8b944d0a88146b7815281bd4557bfa8da222a4abfae2f4d9f4a7b8a3e6a47b22f0f915953acab59523ff5c74a6929bccd4b2dbfeb94d7523a48c176ca6af1c5474bcc33081da3787392c80cea3de5d2e810059bef675e91c2a2086529213c9212848e50e021a667ca11d7aa8d77038daa1e3128418f129414f430a347a5e0d3f06ee3dd38011bed03523a40c19e781072fd6b1d9fa8320c053a3cc19727a144d24a9e8e4ee496797bda54678f137cf6493555b936dc6043c726d88ef9e97ebbd194b2e8d004a36bb925d267649971061d99b0926e8e9e3ad665ab7d58010012746062f18a242b08e167ada087193430c3aa4b709a4f44db686faa35b564d06109468f4a9653ab0b838e4a7022a92ca9644a398f327550821d9de9b3a97b8a219626613a24c128bf68e25722547dae23127c4aaa7c93a20409466a0e7a820cd623189da7c73594d69455a2c3119ca66c2a42e64f216f4e4723d8fcbed947c9aecfdad7406ffcb0820e46306935448d1e3b6b124a3a16c1e660a2d2c93cf6ed271d8ae0db35547617991df329117ca76bfd9f7c26624204bb155fb2e48f9ba36b8760b426d5fda0d1c4842c0d3a0cc1a73f7d4bb2c4f385c8051d8560f2a52a0d0d32d907f30a3a08c125114d875892932ce828021d8360d3be6e8cd1af3d84f48c2c1f56b6063a04c18bac24cd26de66c531108cde5cddf3542aa80d083686483aa54d11b5ed1fb820736d67d1fe5e59e4073e980ae93507bf8997f781539d3e846421256a7ef9c058929efd2a46f7c0c49042cc2427e64df7f4809e6a4b2a962e79e062ad862599c52afb06f1c024ad3d7ae944eec067eb343a45cf5f59b303a7e4de7ee954d781312ff54a2fba1f74d081fbb698f775b1937aaa7ffcf8151039e898033f6a17dad64f6f1a4d8f18a8a0871374c881495faa529da8a44fdf38f09b2f97074b9a42ed6594618a1e74c081714d13f2043715e2e674bc81f588f7a934c5e86e313adcc096aa8b218eaedac0568eae623152ded0c1067ef2aeb75590b132660df4c60f32051d6b602bf333254b7c9149ed820e35b09a37e27d4c7fdb77a7812dcf753968487540071a3477b1ce182da5f654cd5a9b941fbee2f79f19749c8135994da9e9e4e425c121e8300313278f4c4ba23d45ccc90a0237117494813f3b13727f2d3e7c70a5470c4a10833fe820c3679741b4a9ae05668c41c718f87eaf5832350479ca2406de4d624ed2d29ddcf330f031a8ec8281915bb2e37566bfc078b77be652d96e42f5029ba964aadfbd0be91c4a763e991f2ef0319ebd8b16dd1618adc1da2a4f694d11d7029bb9429a8a9644a7a0cd021f3b44d5dc1025242bb1c08585db974e1bb6a7bb02a3448deeedf3244f6e0526483e4d6de94cb3b52a701da3692e13e9473b440715d8e8e997934eaa6fad2441c714d830db18f1e35a071ba50e29683d6697924e9efc828e28e49a2984360b21e9b7a1f0798e59b5e4c40d7fe3870f83838e2770229daa6e8fa9c3096ca96822be7787c8e80a414713584b1f4b42dfd6c1043e64fe9074b4bb94a16fa30dbe36742c811fbbda94cb4ff8279312f86cd93fc6932244484e1238d531e5deccdf2593ae03097c9a0c6229ff3c06a72fa8fdf86756abd318fcae88975de724fe6231d84a0d295695480c4eff93b6e02582ca21330c4eb8e80ea1d2688eba1106a34c475bb4bb44099560b0bf56baa2a409cd6d010663b253546ac97fc16b5604257aa164fabee0839806df9d98363fd20bd6da4b4373da286a7d5e3039554410f5d8b9416817bcbbe667c918316d8574c1dd250fd7169d5cf096459992db1d3fc7830bbebd7454b38ba342e5dc82bbcad6fa313bb56bb705376ab2c33dedd6d35a0b7efc74365d3bc293a7b4e035f965299114e27ece824d93f4ee95530c2a93b2e0723635d9926f2cdd632cb8cd418504ed65754284059b310955d16465ec0cbe8213a9c7bcd38daee07e77eb3b7f5e09a15670371aabbd32a5bc946305ff2793642dcf1383e456c1e5ef31a51b9e273b46158c6951b3d3d61c726252c1f825fd1854d0dcec182a18bdfaaa74a64207f529b8789353caff4f63b629f8133f117b7f4f575b0a36a79351639fa4e093e57831e858954ca546c1a47a11dbfacd508fe410055bd74159c6f554af9f8d1ca160dc745b289be03992aa6ae31fada146d5a3d1861b9b03147c75c8addc93ff04af66f235794c97cd523cc17ad67fe4a8e51bba57030d5c418e4edc19379d4551af914602e0093938c17a50f74f55a333b2d858c30d34dc071b3fce083936c18b6b26d1ffc819593fda4023f960e307f11f6998f36cfc3045841c9ae07ab456074ba24cf07ddb954b9f12138ce5182d8252d7f21d2fc1fd86074f7fa352d3b504272b5f8a3cf9df3c69a4d1597ac4a0043ece70838d4741c851094ed9fdbe7bee4ffa254a7021c88ce49db43526951c93783d4acecb3b046ffce047707e065596073924c1284befb14eff049df748b02dca930ea612fbaa83046b19262c7dcefcb6ed115c6c93a53b47529afb76045f974a869ce2d9d8441bc1f79a4e4af3ed7a7918c15a4d4c59c274aad0390c722c82cd9bdafdf36a3f3a0d9343118cfea5b4a782d54db2e4204722384fca549b57be94ed470497f2e9f3d26a77ebfb2198dcb95363d5e854b13604976d1f34b7430e21bf85e03d5eee7b06330b4984e0ff3f8aae5e0dd2f28360d35a34119d7d4f7229083694505716cb43c66c20386d37cdb01073008253f172d2a47a8399a7e4f843499d0a76fa69f20357aa92e74922c95415ef036777e04452ad693b444dd4b81db85bdbbc27ca4209b3ebc047f05ccd2f5a3a70514fbf8d0ca18359760eecbb6b121a72c7ebc9ca8117cd11237fc80ffd6b1c8a233b706035fe6da70a3965eafa06de63bafc39fb3e249d1bb818454adab798d224d9066e93ca9dd9ec4799101b58f7dd4eb9f135b039c64db943836ae026474a1e2c8269603d491042c7aaddf8a381b1bdd310a4e67e1edd19f878fa19beee41fbe8ccc08f5031dd86ec19d12f033faa729514afd4eac8c07b7dcebdebd4749b1903e7c1b62b3b481eb18c18b8e0292c977af7f4a030b01d51837e5f090cec87c79f186a17f12a5f606b633093a7e4053e6b2fe71449c9beb70b7c04cd79628af4e7738153df4c162496b6c09ff012a9ae6981d52ca9bc6388ffe9226764f928a5470c4aa007066481eb3c0b49ee792915f4a26000165039430aef13ff0a7cbc18feefda7e820156e03a28cb49c43d195005ee3e75529d92252dc1002ab07eab39e5b3f813bb5209064c814f77c1544c4f5630400a7cd6c689d1deb792c49831200a7cc50c42a71ced1c2a04052ec51c5b3f9ddbdd9aa8c08027f03b7a3f5e2396938c33c0097cd61e3d41e8cfdc15a9c981014d60f5753cc44aa78c3218c0047e828a56ddcf10f34c4ba83218a0040624813d4b66d5a9e4850148e06ad72ce40b25449e760c366e2c19cdc7524a217d041ec640838d10c080013c8a418c1e3c86f1a30d34d808c18f34d658c30512e0218c606c800730088f5fa491469be18b5e6080072f2cc063173ed06023042e80000f5dfc51630d358e1a6bb0a0013c72f1001eb8f82184ef410478dc020d364210011eb6381eb5e8c1831608e0310b59f0e0110b1f68b01182f71601193c6061011eaf501eae58008f56fc7834d06023042570000f56fcf8f169bc1bedc60f1f0de0b18a04f050452a527f1a64f040851b6bacd1868f367ce0031facc0003c4ee1c38710be0714e0610ae4510a37fe870f520821fdbb0f34d8088110d2bf0870f018050e1ea238e3479f21011ea160c3c78f04043626c0031417e0f1091f6aacc1001e9ee8041b3e68e30c4e6c42136ba41d3c32f1c30d351e088f091f1ae0718906f0b0044a038d334e252ac0831214e031090bf090c40f08f088c40f36de58a38d1df870000f48fc68630d377eac61021d3c1ee1030d3642d0c6f7a08d356810011e8ef0a1c61a68c420033c1af1831110e0b1081e3c14e183013c12f163070f44b40f06f038848f03f03004193c0a81001e8428008f41f0317848a2ea93f2bebf020f41302a5a925497f7e5110836a98bbbd72504083ef2d58bc6ac1653f6f0f8032b761f4f8e6f5af9ee073ea59259734506c941b6861b6c08a18d833cfac07b2e7929b4d7eec8d5073cf8c0a8673af32e399ed6da0327967ddc6e5c846ba9072eaa7ef5a44ddad355fec60f7482cc039f526d25adada9a44ecd030fecffa81a9944c4e30e6cc6ecd151f275ec537180871df8102986d0f9b779f1af03fb2bee7ea9273d955883f3c1830e5c46c92109d1146b47c7630eec776fb20cca3e7bc669a3ff870f21b4d16aa0e1762ee021077f2ff69b529b1df0884395a1071e70302575d32f3966edc60f34540083476bb0a08d357efcf873def851c6efa07eeca04560460f646ff4e841ca1b3dbcbca18610900f373a0d129451861aef861b9d46aa2a027481c71bb8aa728fa7cf2da52bb981750feab446dcdbc0aae520bc2fe488954e36b06a3a8a4d50bb962cb306cedbee5275686bd44929010f3530ba3282e93125ac94d6682391fab11fe091063e99f76bee789a37da2df04003ffe31f5f824ca292483a03ab9a4727d8c574a33d33f0272be7d02054f49c2c1e65e047887c91d4a5f4f49c0c9c0c49e8d13e216a5f19039b5d1fa6deb3ebdaa515fbcbe6c1bd0a031f3c27a52f42464bd38a117880810b993a4b5f4a216668bec005c92bba54752ff023e297975e5d60340615e5393c5ce043945e9a1cb49f8d108f2d30da7e2bdb8b540bdcf6480f5d62423b8d2a0b9cb2f3ac9326a75352c2051e586082125d59748e4c41b55760540c29a9a02d6b055662069dffeb23664a5681937ed29366fb5259192ab01723be04dbf414f88c13530e115f44a9500a8c0c65b6e952b288138c0217440e79aa153f236f063ca0c055fc3c425f88f1b34a86c713f84c4b2afd8d7bce9f5911783881b1d4a7da64c510bf727c706a7050e0d104b6f30565b24d2b72481713f06002fb9af2b386e686c063099c679668bf53151e4a60e2faa9144d49891b396764a5d13e88c5814712d89ce2668ba6926764211ffa061e48e024784e89db9736687a46d631d8f70c91dc744cb602a14fba61873176148309f227478b787d779333b288c1e4eb7cebd1a64753f7d1c630b8eb0ca6a6631421728c1106272bf5c7b89f538d57cec8f2d14629c1602dbe57fe3ae11959c0e0624ab94b622ea17caf33b2aaa061c72f88ba79b4bab74b690d1f3d3868f4c00f3b7cc1ff7dbccad3fe8cac1dbd60548ee9acdfa4c8b1dc4702021b3edc38adc62b2f78b3131de28de7269d9c9195be0d1fb90b4e73f0eb47b361c2b043175cd60912bb5bf4fdab33b272c105b9a7d6394ac4c70f340c0c3b70c1aaa6bce4f974a9f9688d53f2167c9946b0d73bdbb6a048ecb005572131d9abe6207ca3b56093484c29ff9f3a19738ed8410b3697683e19764ac48e5930b14ce8994ca32176c8824be5a35a7a92f67d0eed88052783ceadbaeece9fb12dc40e5870a63eda8698e5ada7a484d8f10a3ea53755b1cb3e234b0d34fe0710d070e3b41b75831daee05a2b6b05a513e2c5c7f7a07d402c0d34dc78c3d860472b38c941624e573982a70b1f76b082cbdb28923fe8dfb17f156c96e6cad194af0ade4406b9a9562b7c47a9e075553f7f3508155c5cbb8b59a28d8bbfa7602d24ddda27f263d66b0a762b9f6f6f921d2497a5e0df840475a12505a73d4946cb29a7ed1047c1c9647d2288720d726444c14e0adea3b932f5b3150aee5744505dba25da8382ab94ef8256d23d71f40946898955ce1e62bcda13ac0795b427e6c13a5876748211511ad3290b31abc909562c9a98daa0dd3a256d820da9a24648d2ccca9234c1e778521d236711294399e0a386a4d55b3998e07f376687ebe813e55ec2743aa9d051444bb029cb644611424b245a092ec72033c83a213d8d45097e2d73d0ee793a9ea48c324a31764c82b31c9e694c9fe52404c10e49ec8804974d07bdf6c88104d72543ae5d87a830f923b8f7b46229a6db11dc464d37823725df9390b7c1b455463039f2a824ad4d49cb17c1058f6c29ed8ac55b4b115ce7d115f9d184d43849041796fee347f5c939428860f3f2f23bab7a0836c85c49994fcc1a7618829394cdbdff4db7e9a8106cef9985dece494a758460837a50ea72ddfa488b9a61c7203821f2421251da7e4a1dc30e417025aff24070a9723e750e3b00c16736cdd5d94e56fd49e1b0e30f5cc6985cf74f33db327ee0d647ff484ee3a554826fd8d107de72c454596342725a75b0830fac29534ae6f66469b0630fece9f6915b67631976e8811555a7f62e6af795bb64d891073e57085da2dd967376d0ae3d6250821d786043d606d591edb8039fe3d9ea496d52dff0366280d490811a2d30a3470f337afc93608d18f488810a7a202576d8812bad7e6da9f4e66477471d187d6ae26731addfe574e073e706e1fd93938c493607d64dfa859894ccd4b0430eac6889b6412b7b901363c38e38f03fd23f3764111cd88e29bdbc267e69d8f1065ea4a6e5865c1eb4e705073bdcc08a4af2c694a7520bfd0b76b4a16c12534e1792f4851d6c60948e560dc9ad59b2f4c28e35f025936da50e16b2fa038ded118312a8b1430df95de49bd88e34ec3726ef3744a8dab0030d6c8c24ad6db3e876297960c719981882f214b45c33f0b984469012246fc5b70c9c96681f4f7c32f06a63d9d9ad6234618d8155d1edc95b317061223fa70ecb1b6c8481915d1736b1b31d60e042e68ceafa495fe05f92054f9b17f5dae4057e7cd2fe7b8e1564307581fb1259b3554ce2029b524ca5b2b583992c6d81892554cd357a957eb4c058854aca23bf879093055e5448428fb6bdcd712c709da36a5e89d9cdec2bb0fe6e41dd48adc0d908cf1aefba92955f0576b4646a85f0a9c0ef28172172041593ca4f810d95c9bc944c13c924053e9b66ff8a6e14b8cc39f89f9857c5288202132c06cb6d2572080f3d81493adb43de9f08962127b01152529325869ac0e851bb2b3f51954a4ce035a4dfad1eff1a7309fcb54beecd56e93125b0b7d136bb82674712f8dc2f296af65eefecec4002e3416fe9fa6cefeffb3178ebcdd71625e48fb71b833fbd7f3a4ae2c5e0a39f486e26d9724e3731d8f292277c9247116d0f834f7af29916353956dac2e0b6821c91747bccae7530f8d050b1369d8d483a0b0c36a79eee5bc839e4777fc1ee2525434aca085a5d5f30b23e7e86e8509ad2ed05e71d9a9457baac132e2f381996dfd22fbb0bae2467ca9dfd21a56a75c1c74bc29224ef949c930b563f8552e2dd1a348d0bae744704b35119317d0b46e5c97a97ee62d66a0bbe4b959212453dccd45a70f929a83b2f559f374a0b3ed6aec7539da2c9340b76cda3760eb92efe4816fcf875946859f2a5642cb85c2a6f093d163e4958302a8229992c423a1d7b05934be77b8919aaf395aee02e28dd6d3f229e0ac95670a1ad2f462db1823d35d12969d4697856c1fee9f4d21e4755f041d4681665632a98a0ef4ae70ded54a643057741a5a0a308e91693ee145c109e64e63cd24787ce149c758a9336678cc1f72b055fa1d994b6d14c953e52706b95c193d5370a46b9fe5db6308c3fa844cd6402e3b150240e89c30141200c6eea3d00b3140830282c240ec501a12c4c24b51e148003482a1e3a2e2c161a1a160e16100e120e0c8542815030100683c180302010060331a53112a3fb4421b4e90d6d43561a106c0879174a10c20842915f23cfe3901193e826aa7d081122ee21a64894ab9873c7d62bca81cb6e7cc2209466b86036f4378412d1a310e4a1508aac130913c23604934ef4364f091c17fa1e874a68c83c8e550487c81d3cca75da617bdc50ce1131d4bee745b9bf9297d075f14187bc460b0375a33fc64e3fbcbb29b0700df6bf9269890428d6276f83cec94d2c3a18bd04563f54e6b0af676b266ad4c6b5d2e18685c8f77b7278b885b5cd9d4c6ca504db47f542d2c5f1c2946eb11a90002b806454d890e2176089bcef6139dc3d0840c0d9a3cbc4eba78aae45bbfaad6a7a39aa8014c7a9a2da54509de4d8e36bb6b435506b95a576e1db1b006e77f7990178acc7d4755dde8c32193d417ac886cda1da3d888040ab1f9920061826fd450740097ce6f6a5abb1e7a55497822e1310e12d60b8810b5d8303bec1847a43abcd00c1c43cfc087eb01ec9ebafe0265d4b1fed2b38210e1b04b1cc820aa769846a05412a238dcab082d00a15e691cdcb727e956d84df47c0e68cc61e8d1dfb3d47b78ba8095fab768f66fa9eeb195034bd7bceed5dd4ab9f59d8bd84afda56cdcd42bf88b3517c08898ccdb43a2ade3dea629e33bc594ed3d7d54e9ebba77b6ffb781eb55c53c26fe70a2a1b06157f039eb43824ff8768a47c523dde04c3140c5f82e7d395e85de83c2d10f0bcb238291917777652a314cef02ef9960e6e6ff8c9d1dbb950d5cff5b87078947fc70e3ab968dd3abea85ae8b8b7f914c137dcb058fbe3ac512cf80e489591b4382a8ab36144f6725162a230ad7206d5de4374f186a71a567f2a17aea33f8a9a058494fb21e67f2a7689302e561b0e2ae40acd0d36ce57b415463deccf3c0c7b14760d0b39367261ab62e02ed84940d737b10f922aa2899d3cd9cac4a1649c02c561a81144abdd7567197280a02ec54d37d70d0167e366f873f37ec7017671a5cb7d8a1acc2c37bf07884c1111246fc351bcf1ff469aa56c36392a760f69be62e56810e6471bcf1d4ebaad8f9bf61ccd94ec916c2e3a689103c7e69c40ad457b9a8e06cc895a040b295ff1a4a4a9e9dc8b32d269b6a15ab2a7d5499382872680d21644fc1826cb99b9fd9a829914f6ce604dc19bb2c446940248219446ad1a857f2099eca978ba4245d07665f94aac9ec16f3ee356d0eaf1dcb8829e73a8a7c5df50a64a5d2fa5079216236daf0a2a1bd06625f89444d57805b4ec4d4cb0c0f634b506adeccf438ed37e9f01cba1901896aa8bc826d1ccbcd354b539b0810777b40655e74ff86a5b1e02148ac0f1478f029ce92e7807577c45074bb4dd951ba23197111e3b6d4b3be664525b39d5e1a98b4da7fa31a8ced02524a513e635680d4e60fc3d7cfcde9112b89e58efdde46249217a37bd42cb293890f01ecf838a5095b9bc4de79d8c393c694915f0c2cf509e9ea2e35d7eac38babfe780b92e07b4c6db16e075ee1e9b3b99757d008cee2194cf53f4dd7ecedea0e152a38e708bd076aaa836521f6f6e4619e1fc2e69b92b0023cd7f4df48f4b14cad5c374d7ea3c6274d6346b0f4c4a3c6a766f8e3f6d90e8d09ba95073af919849aa5fa960c0451ba9f96bed6971913b8a56624792e07d5aa2e1dfc87c4dec2b5ed2fb731a3cc9c3693337167c2b960f7270c5821c320681c0e780e0f10382bff024f6d74038e80f4f6ae125aa31cd043f977016c1c93a60af0fd70bec969339b39ba3a8e8936a8c0c1812fa14b1b574915882ec933a69953d70ea454a3de5aa3fd6ad43768d0b172fdb9bd4e95302a27569df98ccd24be7cfb4092bfd36a6d76e3bb61a1e3e2df30eb5a6ba14b5c1d7022dc445a9c5421c6f5ec886db90206a527298f4ac923a407bcda27627f6c1c4d3fba5fa170a25891aa9366ca46fbd40d734db4246a504e9405d6b6d46b88245996a182c91f6a7544f4dc6f9b66e9911d4d889c0e89fe4b02fceb6416d27ead9472d1051bf5b3b8487006e7aec80f59a00d67cbaa0e70809482c162942eba72776f5f194c1c6f906cfdef8a567206cde8068da443b8534379ee4e7ffff2ed5b3009d300f80412f40e5275e7a12ad989d16a81100a05303dd73b1bd39b18fed41644c2327ede8f8ffdab2c7084b9ef82f657ae2ad8292084d37dae30bded57f89c5c1ed9c742dd1675779680f52722d2c914904d6636f6587c21dd6a57425e7f5314f97c6009037b85c480b3250ef57d117e032d3db99f1e26f814114f57238f3a3e3bb68665c43ea80f48fd0a25542265611bd9fae64a8950df2d865b775bc20bd5a296d0db82e77716108736eaba557f2225f237be19c107710a8d054919ef830c72500d840daba77e6b22af810c797762fa241958e403b52c961929df05721ea8d9a823968fca77771ceed9972727455930e3ec7fc40ed20f0ce65fc805329effe43523a7b46a06a0aecd087dd2b03aa72a9620c12b85a497e19182c687aa980bb9b741d2c37d24a2fbb95303832f04b9fac643a03fdeb0f4938c745e10a99d52f14bcbfd3b4b11354bda71f83b367ce09753f8e93d256ae6a38fca01e2d0a6e27bc654427cf0a35146a6b41cdf83e6213274ebbf1dac8985f33c925facbc5576be691cc93bfe390d2d1d1a8b120e8b129d231d7afef0d1b79e4a6444387238b6ec61bf775ccc6baae54f0f09b9b6c91655a8ecae40a27eca275ee8805f3e06912df5616d1094e281e4ec0084aad0d0db1db845870d7dc811fd0e74ba0c13b86b456d3a00574da76315b35bfcc2058d78405c4ef659b5ccc8df2d906261964236b1cd0ef84d1f69127e228a561b1f1a1e6a72c02c9094d9729046287a9368e63ffc4f494dd8740dffaaabfc4b049c8fad55f55ffb3fea2473ff047ab7bb3c28f419f397db77d9afacbea374b7f859d580ff9cd4313576d1de32f31f4858792a0be74e6515e42abfcb26a078d7d1f1b0a845d603d61930fb0511fdba0c60a78c040db157b13b30984025413523449fbedfaa30a6b644096a026a63ef86407131120ae89391f2c5dd5eb2a73d6c15881ba35ee9df15d2d47dffeb6b220dbc8348979e3bb2bc1fd3c26bf4eeff253257775c69fb4f949a527832b516da46f931a2751a961e8d595bab753a5806a944e51d51ae0d528ddaa7ce1610a5046105de695bf009fa4bf3d134660506b14706dba1efd28408c5839ee191ee99f253138d453128cd1439714aeccb3fd82c879c53b0c5500db6da45e44eba8fc2daa33d6b0028b0bafa44dc38dc4166511dedadb4a5ec9e2a7f0031b1526f662fd39125e7fb110c40e9fd0adc4c60fd727d9a4360237f7918ebdacc9e5a4ed120bea8fe03619d64883c15edeaa9f2fdf21f653b29a274f245eb1132d9a546a1dbcc901596352e86b566944cf800c9c7bb2892ea0f7ae965f811a03022115622df13c733addcd3e9147a776b63d7652eaf5cda6697a59ce469f260781234dd0c0e6ee1804d5fe93ab58a2480e5f7ad51a84216d9480cf58a383b4f816c8713156d79ceec6d1b3b6a108a06dbc38dacd8944b30f75c60a4992d752744a312bca0f01864dd77bcc47ea0ecabdd80b61d9e6aa51ebbdc480495c5697c5ab958bd1d46f36d287026d30c9010541418cb8d3920f9a34cdd747dbc6a4579945cd5094b5b854a9285b7867c402fb03462ec63e13e656c7769cdd7dee0be2cbb59c5aadd3123b0e03d4f00fef51534061572383805ddcc32a8845548b518ab4c1ee539970c56c213c4b06200f60f57a83f2374ff063f3eeb7a6d928addfdc06619c543b1e6a26509f1b376d0f2db640a1223f4720df1d7f082432db639c0464b156d15c5a53f4ad6a35513fc439dac0c24dca0ed25a0e3b1827adffa09043cf07ee21717c0dc9c56b5e79c6d9d254b2ddc191e804bdb473145d4705137fa007ce2070f4d4a30b08eb50d5bd11b98f56e85f6c5ed417d0da1fdd5a5214536a67e67af009b5116132c882d6686f74db45e08ba49170e4932544f1c42a8dac34d563bc61eb71fdb24b551e5b659c50f81217af0e6b22adfb0b06ee0ae07eb04168a1af2c25bbd334acf35c8ade049253c8a118f46a970eaa6c3be7262c01c5134a69be3b89f169d90cca0d54812ccbd59c382d2715cde26f9afb0f99678c06f8c18b1a53fee6a97bed68bb823d914db07d18d43bd20f5f3ecaed8b3daf5aa52a132defa91c6dec5ca4e0f5d892a69a39b0514353cc83c567aa6018fc2d82ad546d6022fe0f9c8162c359ea29601bb955e8a18327430810c920922712d914c734d93424c223c28a083c6f9272e2e867de0e8e2557e80649ea202845eb7694092a18072b0f8455c12b50d071645d2225936d55515a1c1b45c6fb0e763d2964670f8139cd1464e674682fe3ad60601a8b125e86fcb79cfece40db53f0e3c652ddf0b26b23cd5ca11aaf19689868b40b02c7838ed5b020484b40168fb0ce90a5537019c2c2f64795900a369afda35dcc1a220c2834ea2ee293a892730583e258a08131038b58e5ef179c7232a095b41659cc829aeed44f8ecc6cdbb5983a957fbe6ced3c6e8988b059a248dc14203d1594c437006b82c76d9e0b85624038a3d44fb3b89013d83887c4a7fa21e2080025c7878bd16985ffc99d18498a373c801f08f4bb908c9ea5edb3fd276459a048f71074b658304c411bfa2a50762764263eaf0f53ff87b37f1815345e0c32ea3704471217ea4aaeb66ef3c00762f86a276c7eb013c1fdf4a05658dcc15c7bab7b7546b11d321dc8b2c5d79ba3693cccaa561dada05941b5a2689583574362ee2c6cf158a9571a8f35d49ceb935aa8111dc6430b61663012dda0f7c4ce22f48195fd5d813797232c1db9ef79339037c8a35170c028516f81e7958ce9929941d1e0536196db7f211c02313e80191d0422b1b3a195540a71f965baebc60d714c342f928769027ea24287c6b8da0d55d0b211ff636c2ceef03fdf04ff0331e3a3e73f2f5fc903a965623400742eff6f7722928ffb4000da3bba24e3ec30b3bb7a387e6af9333564e34f3e204f53a0bc7897aa3ecb31a6af0642524ffadafdd414011963761bd936329904b4b65e2ffdef1a9d59cdf41891a8dedde485e7bd8916ba38ae76a5590323927df5ac010b5836e94422c22bf57e56054fb5e3910b80fe2a2a648a5ea0ddc5cb8ac5ece999a32fb54d6594bda0ca65ee83e29861c4f546192dd9f6ee84059295e440ab1b3e142d0c332908aa78face80c877eacfa77793a7314b91795660025ea6f0cff98b7dd6ab37a0ce81ed50865ce868bf28fddb0b71a99bca4fb41af540966f4619de037d9a777356f2e15c29484fe72340e2af1b5cadd5875fefd8670ff876f88849e58d94e3bd2d1fcdd840fe807e16481d1e03fee75b721e2ea39a9ee0babeda53b69587413dc7654872022b7c94a89064bd1683ab25b0653dba6a7d0ed10182cbaf1aa337962d263a3b9a80b9e6f3515ca289813595bb6e4049b96ad0582f8e348c6ee508c4a6933a572ebd830ca505bb2ac8c7d64a64f51809be905c018538549a0fb291ba50f5349e4f38e08818bd57fed5ba74d9fa00d8a062b0a6225e3778409cd969eaed7e076f1344e8bb4d95f1c045bb84758020c4b806f51391af68318ba88b0762d9e33f10d7840d5f2d6d964003dfab8772a98415cbe1d312d27e179c561d169c298c64ab50215d4efc0aa06c02a13f1d641826c51e320883162a0b34f92984e82f53e8691a238a919e1873f52c976f630c82b1d72e29010c1a775fe287c1c5f74cdf5b11c4b42f9bf3da182a1c79408f1d1920f77ad6167025a9f561c59383e73618978d5c7ecf3d11a242b24fcc9c19b714938e7310fe02845cb68d28ebebddc783bd261c6b1dca7de002b0e83b46a1e79f1b2b75c4dd26f38867a0098f18db72354c5670ea9923e222dd335332fafffa3220a8a34e3b2ff84de076076b1694876bec50413e9d4abef21612aaa0a31fb291509ed43ccbbb6bc2a5713db695809660d6fd0a9fc1fc02dd6c58a85daed986045b6380096a1f9a2393682383b560bf00ee9f9757e329ae74bbd6057972ef6752ee0dd6355531f64d010090ffe1669400403356168d63dd53f276f64880444c76ac38a26977510f633818c459d6d45fc43c6ccb8129358c1aa606e5f8f5e035cada82a08a36b568ebae11d0f15421656a245c962af1ee3c59c397bc55dc6ecb9015ffc8cfe903befb5421e21573bbf6e497ad38a3af7c7c66c96cd4333290714bbb306147825f1922401969a740ac20e3800d67474c0329484540c42db40e5c9a86db44bca76a91703834ad20e8646399a637804e408aa01d9828e454d4c2b308711011633fc82b59cd1bfdacf1fb80088829aeb4c2f4006a899dcf44cca4d5030851583f36a0382c1ea4e00b8f4446b59b9169400b31d5e96af4e355fab1dfe45b7f1993eb7683ffea13918492b7ca7556a5742d78a673aa6ec1b7fe5a1a5b349e89931dc25614ba11c5caf9e1ec7ef66800293c18394914c59ff8f8e1efc8de432acfcb85ed099b273a6b8a0ce7f41d8fc970a319049908a8f8f52be850a623b408ac7ef01e5692da04628ef80009187021824e91ac11aa0802497d2775252086fcc9482a7c7940bee4829020a40a4245137ea420af2029fc265012240a740251814b819ea02aa44b874f46c83c3c0f66088e0556a1a2c02a283d705230145cb478777abdc873041809f94341d161bd1ed9e4498354d2a0512c721c866a8e18d5de379326b5c4bc4ce57775cefc9af6781ba758a41c4b9121b3e633205e65b440a2e595d41e66220973af23048bc625cd0871f5d6327ff54ba79261e1419cccd27617d032eab6a44152ea92097840bb45736dad1fbc04c917f0676113b7883af10f463474097a0a013bb22e2743bd773f667cde8ee1d7138be55660ec88b263abc1141f8f05e6b17d7921f69e4e248266ad42ef1ba9e8525e1cc4a78a31e5ed4cbca39bb05e3a858dc467f8df2f3ccce9af869ddbd934d1611c42d8561fea52135752a457be1a2ed46d7c80581ca59e13256c8f5dae7d01f0b988914148d4fb651813085dd0db1a28bc8475694f0f7503b5d2348936a09d873c03b6e9c34b945e8142f56c46e7a10b9651fa6e256cfa75930e5133a0ffedd51d7fc87ad1299bb8b5f0668694e1f67a14292a12b2e304ca4f1bac870bea86a53d09c9459d86304b40f1e1a256979b3b9a12a96a196ae0623c24ad6a6743c348d36217f42987fc488b768f38d0946534a551a53733afaf856714ac68134d2e4f6a748ce82bc428ef7792391a31decc44a8a61c35036bb5e5be212625587f4ed487921df0989864313576d06a1131820ef1d4a2e98626613f21499f7071905f65d83c0d1402cbc5d84eb0a783f284c95488c0064411274e1560e05739b96c3b047e741ba08eecb300a6d882df07167489a5acf32790a1a8ece46b218adbbad546195753531f4201ceddb22f5263f0b672dd42bb5d061ad1ad892758dbfd97710dbca31b9e58ef458cb058fd9c53c6258ad34f040e08534521aacdd1f31293c51698b039e0a827061dbaf69ebed0bed15a912a6a62627e1a304a3552e0cc32bcd4c18f5a53780255f533e54e91be95b0f166b4ac99bb262290b03297e1bf0fee77bc5bc1407ca98a523fd0eca75ad0cd94c467493a8cb04143ecc2928dcecba3227a61dc0cd354703dfbb898aec4a0ac597c591aa7a3eaac451ae0e60438640f2438b910c8092f145ff82f2c13171a45ac4255f965205d25d9417e7c3cde6abde548148d85ced59a63be31f3f12df0dedb742e3f30bc749e58cdc6255ada5b2f52faa8722d191f5d345655cd78c37478ba472e1ee19ceb3a7b56b0d7e60d35d518705b16a3fc06fc08a3dbac1593a0220f5396177aa39c3b8cf0993123a8acef5c3d9ca066aad2ffd6f461d47215a1f02778854c453cfd10bea05c7318bc41448d5b0be11e9d3050f942207cca4884eb2f38772bb8c42490df9ec9c717adebaddc13d7da822fc3311983902b0825beb71008e42b7f4b7bb376f34b160fe089f215047a6d96e532036b56288cb59ec2439a3fba457780e3e83e5aefb7513be87c99ea2b3c74178b22404a628de7a258ccfabd65b91d46242e255de97a4ed714b055f9b9a97063777d4f7defaec51b07d5e45d3591808442bb2268db138ff5064d5de5ab92727c186a8f5db3a02f68a2c05e9273d598b054911bfe419f74265e93f07a57f4b88f176444bf44870270bf1be51ffbad7be5d26ce4dab2bd82972f54b94b25e696cd3b8e2d8edb7117e613deb944a50689f10b1c5b31500aa1aaa2d3d9532c2475eae504b390fb478cc8d4517b7857f8e3612b09202f93361cfb55d566e99dbb9ea6164ba356203d121e079009aae2380d5c772005a104a3407c8531430bd8c6f16f85c33f11d13318771ac2bfa961b5cb1803bafe5b3789a8accbfc2560b67cf83011c75b0d2c3e5dc04e36a5c3511349c75b9dd21c4520f98c68b3ade8b02b743f3bcaa7c9bd837f353e75fccb554a0f03f21f25f6679795f1c63678b867e413105105061a982d18fc80f8c95ab7e5060c35aeba01ceb65b1cecb9d3f6ebb4041e6bc4d6c3af0b9cae154ddf6b6490d18aa8683589110dc1cd1985653e86a4ba95ee47c8591e275c72389fb2f10496b4a4d8088bcdd3fe66766dbf81815b8a66aac6e3f9818c2f20d149f0504d5857cc7441509f5fcd81e7a03077b4b97577a87a0344f182328b4cd4624437ff5008e6d9329f140ea84da2e96682b0f9e36b36cc367ede9b31bdd61f60dc86fc3633baf06673fe11723e338098e94defe159de1a319a88f3081f10f7d164f17c81201e49c002f1b857a56f06e4c2a3ec0a430f32e87227c02fcd521ab9df4509916b2d1195b1744fe36379f0f52d8b6fbb766f814b8c7b2e0598d160c4cd5fd4091a2db0a84545f0ca5fec66e6829403e06972147c109a4be63357edd473f509b69a8cc2bd5300f52e1f4305baac2c12be639276965be6f9f61f092583228c925721ce08996423f29bce2d50a8670000fd34fb99d35e7dd8d8d25d52b4794b753ea815c1c5820f29b681bc072ecac42a2a1b8a57b309201b20f694eef5aa325140f7c9c53ac5c638285a57a62beeb6a940864d875178e81454a4c39febf17bd742d59597c46b201a874ce3357907529dd7be6d4110b29e90b3a3aa778671cb499d418dea68c8c6a01bb5cbb0688b27049d909ae73e83bf98a6d1ba9caaa691458ebb801de100a26e1de1db95235f2f5220b6b19c64ecd043c48820309ed752399c6af4889a7ba8cde1b20b3910de738f0940dcbf72a5c11c7f556ca5d13999c7b3ab3fba4cd273218d87de11e8b7d89dfef642e89bfb322ed89dd59e6edfcc9dfe919fb9a4a6586a3270bdd7749e60db66008418f2c891f427d04d0f6f19e81384e94a90fdb15aafa7bbd4bbe3d0b97f33034d452546dc190db665a3171437351d0c657639527b5bc133fa1ce1ce3a7f192c479654309f020a63321198c70d46152400b55e3df5bd6f42ae3de1e121ba127c16403720ae00a7810505bd0c20088c5f002e03cd4dd6e4402423bf0da6c49a4d15598cae65c1f1c5b48f8e88aa3a1c728b566831b2b43388f97942e52f77e72cd6201c57ac4d6de9360f067e4b14cbe8099f4b85b254a9d7d8d534c3820d54cb80fd05f2e1910fe4066843bbdd136e8572650dbacc896185c255dd8ce8dbffbe4b7c0c5d55e73c997863c357c9932aa9931e028af97c5422bbd6c94dcca73e5ab0ff62f0cd7f5132ce2195afbb3959f229b2be1f9ba8fac3ba9bc04018639ccde0ee91614f95946c89b3d9e414c9411535b0362484cebacfb4e096e03a2ef62117ad52d821b185b9954c3f55517dc292e307cc995c862bf42318b922225216d39601f06456c16c3268fef828e76dd7b29c2bc65aa847daaba69519d0f89495e2746eaf9e2fa525314f79ece0045d6b32ee6ee7e90428df9b0e05f05213a14e95ab525b0abbf1bd818b58170c8f3d99dec43db92051ffa3dbf5ce44659ee4beb1419af24128014845ab737b52ae56d8416a291763b666484d3561c7a5743a0a0f59a8fd2fe3486f52cd36b179bac2c15925834f5fc939a3f1903463984eec490af812ad5e55467e49eabfe959b45ce6a87e8817f7e88bf7ebc7da59f266a070447542b9c325458f7b7a50a1e747becd0898e4172f9125bae641c8f0dbfedde824afa3f374f738e087e09374104b973a840b00b6973f6b7b467787c92deea2ddd2f0a95bcf289339c9cc1605799d508ab36476e4f304d71f1d436f4c9098bcbf89cbc773fb47bd2864f41c45818290e2baeaa8f150a4a8004d2b68a39a2e39b49dd10a2e7371c01a6dc434189a35c9bf7f3ca481164443c5f73e2025f386376f3dcf6a4ca9a9b9451926a0203c9b05be6d58b1d4fa7903948c7ad05b52ae7d8bed5539360a2c64fc14542fda9b088441845de5348849fea00c2642918588d813756472276a5c974bc280aad5b88e654ef5922a79ec1550adf89242cd1840fef0c5341fa1a01f7d47752b6cb53a8391ce25596e69365b13d2e93663e1d790498c9836e81fc575f686f2d3f46597060e4a013ec79b9f7c853ecce5993e98df105c1fc51e1c2e83eadac9cd05d9a84501717adcb714b927f61c8fa551d2d1dd2c92f29dcbec1710952fcdf203dc5a22e61725e0864b67becc800f79eff2166712ae665c6ee01b90c781dfdd2a879afa73f2de212f132c7dd830b3a324d8f950c014ddab5f513ef1b5c467e7eb02f50bf890f0eab380297e5503dd7da6657be7a05f660e0aac28306186a7f69a1d5d105b84ffa61ad165a0ea59ec403c9656273930c4af4abf65543fd0edb2bbc75eccb79cd419e038adeb218264faca500d90c7c20532285d397667eaa9235917880b606072ec34c0f7cf750a1a51a8bf434112f20c45ab8e3298aecc9f8b0b15801996d5e920b5bb8abb7faeca189bf2895c8d903199b5d8b4c51822733e6c6dc3e4d7d9bb93d5954357680158ca077bba248aa1455f58f28003d8eb7029735a65f8a1c7ac7547c78ab34ec9ac7024004a8aa8ffdec87f83da14f296dd0f3c2b950ead5da6264bcb8f2ad03704876a01c797f698aa216ade644a8173bb89dec882013ab0432d62443b1a753b86ca4e624f5b5a9ad14083ed0ae12a91d2d7ba9c994500664718444519a99580981e6481e0a4234cf807f855df2c8b4b8b9cf6d6d999f2a4923029cc968836a1972594124a265c8551c0abccb5aa9440d20339d6d00056888c41b7ecd1bcf8872bdb199a6c3a57965443371810c020fc090554341846d6fa5adc694815358b5416d1c9b6020069ad838400a9f10bdf1606b8a158e73f75ffd1db2cdd0966bcec51d6c03db7f5915e2751e820e06243597fbab2142fe8b26a01c0b34ddc2eb2ba661b1b3ed0584631e1d14d197e6d67993fe0418db573f5096276dfe2c5960af90d96a11680e26c92b5de7de3c6bd3a08aa6044bebebc6f4ec4478cc79608d888f4f7d41660ba65e96714c71acccd554d5e57b48d3a0a3469dcbc7b41e946197050308b0b0fb242936fab5b9574a8821e64afb76f6eb9e533d83d71ba38b15c6e9741deeeef261e493bc58c9695f51f43b2529c16560fb97787dd3b9a0f9f99e5423a8b6138e005da611bb0f40883bcee0ec54f20b9f0642fabea7fb3d9db2e8110f704c927182bc333209100aae2cb064416533addb35be76f5fe73057257d4876f22dda6b971106318a1b0b3ba25d7f4f439a333e3c57e5aeedc24fa4edbeef1a8ae0b8bdc78cec72cf84e51aa8cb158f9060881a740102efb31e7ca2e24d23ef3692d9c733d0e070df407b92d7a0722d1bccfa933b647451138aba66693ab5a197002c6fbbdc1d4b57f171bdb943c02f00d2f953b9bdfb5a41404774b4ecc17e3d3ca7715237bd02e0f085877311e5c1cd81b4a001111c98508c7fb0830c7d6c2f086e2e7ec7ba89e5371ad12ad31878bcc18c3fda65869ff4b026ddca14e8797672bb5fcd88260c7b41f3084fdc56375d7336b7802d7fae569a3e5e410e0414002000232c28f4701b25164379a66b4b182145177b28806b87f60708c88037a6b14659638c6523a5f8d12e67fec9c3d6d20d3ff73ba60a17b13d82cf68e76091d08412dad205d6087150de4e57ad7377370001bf3d142110d786b0b8f7650a7c5679df20831fbf1667c63b6c5e833bcaf863af162d32b21a2456023e15447c0a3960b024850330333333333333333333a3bf52bf8dffff636c516eb2ef3620640612209599999939e517c205fcf6635bd399894f67263e1d7409a20a480a0f0a6ee78929446b848f6367087ea78b5dc48f87d6e2118267f9d6728f479a24c8088293c7e766d5f10308be07d79c3972df03193f70634a9aebc6e5ccc7df95c9f0811f621d1b19d647c8e8816b7393ee038dccdba391113278e026d10c314df64d9b4645c8d8812373f5e993a64bd963f0042143075e46da8f2bb45848390608193970d2a6ab2398e4a011627690818314c8b881a3a216dd93bc8a830c1b7861829cc7642ef21fd940a741460ddc1ef8206bd7f4464ab73268e0e6987dc37fec6d697f170b0083d8c0808c1978a92eedeab591210327e639b1ff49460c9c0ef729667d985c9001032f0fc587363769564b72f05ee174e5d1a87a1ef8a654395ce147b21e85889df46204619cc08b1a374e184fd25ae1e78890c73ef848791459133858e1bd4628190bf7d1a3b60a27e56041ad625b44cbd8d852859bdec736e695c7a749852f217fe72f95ae4817e70b2f6a34133850516edbf461f26c6c31c729bc54e12996744b0831c2e430859fb9657e9046628c3162636b4be14f46d230f3691b5b67040e52b8b94cbaafc27d3ba78b13dcf8826a081ca370720eff23f59ce4dde31285b7398f3bddc7594a33571d3842e1c84a89bf774baf4b77e000851f2e4acac3f6984f60a12d669994493a7078c2efcca8511aa25530b7135e857091826434ed9c87e1e084572d6335639b834886d7c0b109577cb03977e5520d1c9a702666b35e8071e3040ce84c782d3e417b2c79606fdf7260823c1e747f9ffb28fc20360c3157c786210ca0fa828b131cc7253ceb4dad7f2ffb92c97258c249f2b17c2ae70ecb6922382ae1441f240ba721d8050725bcd03eaa1ecf8519101c93f046fb44642494849b3da8a7f81ec81109bfe7526bca1f3de14adb8203127e8ff7c5f1083fc9a61f7c5f49b86fbd1881026c182fbef0d3c505b8f0e286039cc0e1087f24d56b75e3c398336d84b39e3ea43f4f3ff2f459fb030723fcd8e43f3dbe1f5d6c1f8bf0c735659d613c42844611fe47ea911f8944d9bf89f02ee688e9246f0eb5220722fcf179ca10f2721cc2f174e6e3d6b4ebb18673c06108a73624958bf263935829849fc5e76c345a0e42f89b255aacb05aed23eb3806518270a22fd27c70a505bae04b7004c295ce83bff1b9319b1f5e8401082f04ebe81ad27f70ce63f7776ced07af5f7b90915d8dc72901a9b1b71fe0e883e7294597f470f0c13fd3cedc834b759939f6e08f3475c9657661e0d08373dd7d1624f23005a9e4c119f9b3183c630e3cb8315da650f37f954fd93bb839e2fc424e559b6348f1c061076f6343fb24fb20988497c051076f425a0e3d68b5d61c2470d0c1fb414879dcb93e82a90f8e39f895ba327ef345f10ccbc1e9c15606b909e3e087b09db279f44846ab1a6068a16ae080836f7942b3533a25e6ce1b7c8f90b3a71ca6b5730f37b83efae0d182ab86a70dbe8fbbf2dd0fc2fb829505071e9016003470b0c1eb711e661e5a1e6bbb8860a8805400f3c0b1065f354d5d84ceac0d1c6a78fc3d8ef01f4896230d4e1e664d99e12d1a98249a9369d09833303eb08bbe31461e5938cce0c677c7ab8f63488f7519bc9447217b1c1123e5b78d830c5e9e8d59c3c7233b8df018d008f5f5a907036e90e2108317176a837c5ab5a86c1738c2707c49df60f0c3b5e7f06969f39d66a5058e2ff861b9cc3d69f644eaadc0e10537e7ce3153a70fea1976c169f53c5723f9fba3e60b1c5c7042ade74d3e95c7d7291c5b705d7238edf2aa5cdf6bc11fd8df87c6a4b1297064c1574fa97cb482646ad7a0c081052ffb20fc9002c7151c2f916816da59426bace06b64b5ca8a1fa53c9a3c81a30a8efa48a366f51c0838a8e0f78fb7d56436ce079a0c704cc1574f1d9baaf375871f2d05673284d8f6bfd23e9089c0110567637765c859127040c1891ef9678b9a1e5c7abc170f4601c713dc8ee9bf7b2d6c4d8c392a38e90587137cffa1869a29dfc6568dc7839065c18107d0a04183c60a0c590078c0d104370f7d3cec089130c1b7ac21e7144e51702cc1a96c0f3e48a79ae6b70664c180a24a058712fcac937c5935b38516364c36802309fcd88731b1add563090712bc3cf0358d50977ce8e12d701cc1ebf4242169d57ef4af11504faf96cf302b492da9388ae0a8f8a6c550411b5b6b1be063303888e0f9f4403d5aeeecc16d6c6c710c41f9d598027008c193cb79e439698a6121dbd8521a6b068e20b8612c6ae40cc966d2a0d15ce000827767397a1ce1caaa35c6f10337591ea5aa5ff651f9581b5b5f70d1010674a1c506bee0e20460e0f08117a2b9e6b160d3a0a1048e1eb8e2e30ef36c612acce781db923a85109e1df8a9e436e72813967ea303a77cc54aeccf81933a84d9ec74c1819bc6f2860a5ae9432a37f0bee27f368f2636f07ad0a38a32c9fd34a5066ecae639dcde57a3a934f0a3a79c471ad2879d6370064e5c46899210b6328f93815b21657a33d23f248b811355b307d263b1c8546d4150e0051747035d5c200b06148103065ee561241f85be0fb182478b1b1c786ca1850d060821c62b8ac623862bce297d14669e2b37b42018a856b839524f76448a1f664b0c56f81721117c26de6359a87c1063154e88a5268979fca92c07093154e1ffe0529aecc195cdc9a9f03b468ccb3e4a7f31a6a8f03d798538cf1872cc6711629cc29b3cb4caeda9c3df078f2186293c0b93bfa42ce4f1307c4ae1e74c21ef9eedf615b6b1f55eac0d300804706390c21fd697c71c62875f1e1863146e4d96f071beec093144e19be698e9935078eb833ea930a13ae7b1a0f072d5e577c96bd7aff984a3f11b346795c852315e50208627fc3cb0dcdd9fff52e5f71181189d70aaa2fa7883d53138e1888795b8c82ed7a6a141430b0cace04c6013ce660d898d31347a9aad6b1043136e18911f6b8e983d48f03c608b1899f0d24a74b3083d88299f8dfd189870436cce937531e754951897f0376cb667e754ef6a896189f3e87325ab347725fc51f644f164ae6673c6a0c4b59d9132ae2d53d5d5af2995ec7b45a23a2f624cc2f791dc8d46388b8b1649f8aae933f4a74a24fcac31fa7388f41f230d0947b2bee6b753528bf908376f082934e4ec957fa023fcd4633b7b8fd6565989d108a7ee4355cd765d8f633118e1e40dc1734f5d8fec47c5588427777945f8e3fe8148fa4ff4716f12e146a648497945849b7b9083f838a887b0188670b53f65c6a679fa486b0e6214c2ed3489ec32de21a686106e1ef4209c8ae5b0b941b8f92de7ce13b425b54c338821882e6d0cc1626b38107eb220399b950f08cf5be5ffd268d6543ffee04d141953d7c89ecef683132bf75862979c440bfbe0af0499c80ae7c9e4e318c4e0839ba3c77bb81ef7e0786b8a394df841859cd783df115eb2d4da0fd35ff2e045c66facf7306ae9463cb8e152688ef97d1ce1e371072ffb072edd954db33d3b7817b5b3adfa78d0e38dce0531eae0a43cb692b246ef0531e8e00f636a788da231738ee6e0884f34b7f4919c5f1e72702c5cfa78a75849138b835766353eb67049248f8683a3f94f0383186f70e4cc37b95dca82186ef03c2acd6986d3d0d5a30d8ec498611a42a37bb2a420061bfcf1ca560861977f58a68dadb303c45883f7ade111cd7c52ac8e1962a8c1510bda11f30fd2c7fe34f8752b9a278fba6da28f062f6b5676985e4d21f967b036a65c3f0ee59bc15b1f4aa827eb94c43f6570ee42fce822d705ff1c3278d162920937daa539670ccea9cf6db8bab6d9470cfe45d0f4c17fa6bbec230c6e1ee4d82ed2ebe38f1e60f036d2a7c8101f4f92cb8d3270328f448435915b8bb0888113438f3efd52f63a9b6001033fa7b50d3df9c7dda31faff03c936695abd015fe8a6a8fb2438c98529a6020805638bda15e63975ff08904c00a535a4beb34acce4db37e74db121a32db390f2d6cd4382cd0028c06e37471811b278c08086015ce5f58c952cb51856fed438959ac7e62e5a4c2d9f6f0f158da0779fd820a3f85ad8b58296415af9cc2b799bb1fd3f023c921a6702ba4187da8757e9ed3a5f0eedf63ec339914de6c5d964d5e3956b4a370b4b37556736f715f14fe49f2e879a8d150b81d39061f595a41e10f6e738f731e8934577ec2111f75121f8f2d86f8e109e77a5b32870976c2b94b3f8ccbd82ec1454eb8197a98247767a42b71137ec4b8ea51901fcd216ac2b34a6a95aebf4c3892a3a4d58f0f34bec3849763ecfad28f44d573092787c817965c43e58e25fc61c46829e7a41934a612bebbff4545c775968c12c56944360937450c79fc9d25322c25093f8cc4aef47d24bc983179e5411e7c6407127e92c9eebb6d37993cc29388417da8227eb6239c9cc1fe47f73ed0781be1add824fb71d88f6e6584172d327ace70116ec8e13187ecd10704a00837fa507aa816325c784f229cb48fb12c5410e1d5d90fab7a3687707a23e7eb8dc810be4d8a957c985de522a4106e55f2ece38308219c29eba1cb85a4bf24198493e6830e8b1e86f18e20fc0e2b39c57c04c2d7f0d321cdcaa7cb00c2eb2ca129331bfb92fd07b75342f8a7b3fde0d468e51f8b364926dc873f250fb6c456e6833f98ccd46112eb0729efc18d4bcb3fa137e4b2560fbeca66da853063f3c983132115ed7b90e987321ebcd0e67d16be6617dfc1e998d17bd03d883539b58393529692854cebe0a50fbea47ad6c7792c1dfcc8f441f8a8ca39b82e3596dc559483bf1eac2cf6944dfa5c1cfc488bb1ba520e0ebe868a72519fc27c907b831ff2978f524cb307ebb9c1db58956c6abbbf52d7063f68ca615ea22a6b3a36389a8739848e9bf08ab1353892de7d3c0e57d192626af0627acaed69b434f8e38b593ea4081b3d6668f02a95e4cc57ae31fd67f0a67dbe724a48f9376670d3754930c9de25923278339a244cb21e454a17199ccc6ceae30bbd726b8dc11f45c6a09e43478e6189c18fd8a33673efdc7109833f5315d721f64a4883c18ba9b3f3fce02fb825e3834e75717dad171c4b96f17717624c77c1bbe8e9b242ca05bff27c7a892509600b6e4e3953f34948b19d09400bd56dbd9c57a847324f5799bccc4d2b02c8823fa18752c9c7c3141dc358f0c36b30956895248571022eb8888071000621802bb81ac32d6e3a6abbf3b0821333f3c0dcb30f3573a60ade740f93c45b4d05bfbe07f9d3b73a05efa2fa5f3c4596cba7143c0fe703ed61555170d4eb2452c84849d91a55e78b1570600100288400a0e087a514bdffa6436de5094e88a924dda7fffa459ce048cc6069520ab339a69be07ff060d9471d62ac0d33c14b1783fd78c74b70d25bfa6ca7d049e35582d3e3cabf8d2d14948075110248829787b1071e2a6b4ead3e90e06b9f99c64efea391f808be4dd43cf89ed708ae9a471e48fa615df8b408fe306d2d48c5985131480437a34ff27c1e5585b00dc18b357f89151af3a847d9d84241096a3c395813480b401f04200427a57f72c8e358c9e307c1c9833cfa98d2493b8f870182b33e13c6ff26e355f303e72f48688b9bbc21293ef0eb342bcf8fef81b7f5e1da57a23158080fbcf1f1f1f578d856a1911d38a1c652a7b15807cee590e347129b29979b032fa5cd9b33638a034ff3fbfae44a16c907dec04b1f76f4896c1fbaff459b420036f02247caee3e8aef5c750d5c49a925bd7da1c7bdd2c04f29c9e7d1e5f29852390327ffc55ce69195c7bfc9c00b9e3ed7f43831f0630c539962c347a58de0716a3cb6d0c286492f0400833d47f8285c1e5a5ee1e4f1e47513b5719bda15fecf49f9f7385274865be1654de96d36b1494392154efaf1484e32a5cf53de2afc9cabc73f4c935785efa1828f2fd548b43f154ef6d8f60d793c96743e50e19a87fb18ebff711639856f7d2979d77f9030b229fc41884b0fc36350d9be14de69b5786c7ac91f6248e14dd894b47e22212e19851bf2859c9456886f5114aed9fc98c5ce42e16d488996db6653f80f0ab76b4cec22dd2e3aff0937e6de1cef616924329e702425471ec73cb25349e984e7b73e2226d9197d104e78171b3252ca924df817b2d2fc4cd27c6a6bc2489dbf33c42533e15ab498fa4b0be21962c2999fbf10cd1e49085fc28faf14c57b94dd23f32ce176956ff6309570dd3dbd67e898c7121e257c93109fee3cd6df9f842b977e10bcbe8724fc7ca63e1ea9252312d9fc4584d9e4cf16594706241c4d2f6ed3f661e7173546d08516253023b871b8287b84df79e42d69eb4759e5be51630b72c200a34609b63841d11a41175af4cb7084a361d2e3458dc709c39c7fa2022dbe8edcb8800ab4f8c3818c46b8d6a13eea2ec3dc256d6c25256430c239cb219be7f06163cb2e123216e18f452a794899c743cbb38dadb32c38f0801a8a08198a70632799aab1ce9d63b5d189f02dc20f7d20a3492c5612908108a7d3c4d083143fd2ccbf8dad0d64c100ee43f8293dcc83cfa3fb1fe6db104e590a69b1e683cb63554621bc6895349845dac9679b10fe784542889cda769563021983f022f2674f361f2d6e700009204310454303e1e7c8f031b74a559e2c20bc4e59ce4731ff7ff0a6b5a77efe6386f77e7082664edb963b4590be0faefd054919b30f3eb8e19249beebbb4f17b30737a7654b85cc5d51357af0d29aab6466f9bb0ec98393925b7d184265fb94e0c19f97b8e853ef1dbcc8a32413da7bf031cd76f02267f5d6e7b40e9e7a67fa54a9a27970d2c149f9f3fdd053cae832e7e059a5f21c234239f81dd38f3a44aadf6717072f325d089e1a1c9cb58df9473efafca3cadee0b57c8f23edcd47997783b7927c181562a57a4f1b9c1c6d7c3cf00c3965910daef4b0a2074f392eb306ff926bca3771d143576af033ff65e989982ddd0739368a64a4c1c9f21f7fd63ece16520164a0c1c93d214506b9c8dae39cc149a98e791cb6524ae6318317cac7e390f3b95bb81f6570536e9ff99cd1f25b850cde66cdf0d3e162882a8fc14be6e3f17c9c6c7e4bc5e084c8a3ba6c32f28336c3e08498ecbc7ed083891bc1e09bc488325bde173c19914db16925fc6a5e7035a60f2c0fa4479343aa0b5e68856a9b903dae262e78d9a3398f738ea69932356e788145c6169ccae33c793cb4e88dd96bc14b992b260b2125d6a85416021959f0b35df609e9099d3f050432b0e0ab6cd096e4e12d3694710522bbe6946247f8898d2daa13a00d30880d84810c2ba08c2a3cca979344ef6c218b32a8e087ba0f355d4ef96205617471821a678b2fb8d0028c1a5bdc3828f0a20b2fba401953f047e2ef1b2d861e0d3e300232a4e0b77f2e51f7e077799c283841625a88f68182bf926ac3fdf82c744a798213cd07a16743e441e7e1047fd8c33c4c162ca73ccca18c2638dae3184952c41e5b763298e09cf6a035a63f1b5b4c43c612fc41c43c1ea787b86c8478073294e0ddf738ea627617601cc84882a6beb9e3a20519487052b74a96689a2378329acb635af9b8b86d6cc930823316f2c03b4c2c8213e5ea3be5a898208308016b309b8fd2a306be4f674852e663af99065ec9c576fff561867706defff8785c1365b37246065e1e0f5e7e183613035f2447f518c42ef766060cfcab1ebaaf70b6a7674254fb817c882b1c4f4125bb3c748f7b90563897473f4c39cb6c9a346185db79d8ab1524e63dd8ab705a635544a3075655abc2cbe0a366a92977ffa9704ef24056c3f8c7ee282adcd618bd534c1ea7f0259da53cd8209b37aba6f032ff7a1e4bc6aa8b6a29fcacc13a260dfd9b9992c21f479e9cfa7e1c21131c859f7d1c5af5e289c2abf6cc5e2f67bd7528dc8a8bb1c7bcc7a15603852f52d616b426dcf89ff062c56c518f9dc71fe2097ff3206cf4c822bbc75627fc2dd7fca3d59a07c089d6a4b24b6acf6fc2cd0c96da62fbd0841f2c5ec3c4f9f5e02d99f092ff202d8698f623b531e1a7da9b85b2774b8f2fe126cf29f8e087ad25bc98563bba27f74044ad84d777b691dffd3b5639004ab8e9e3b3c8ea3e08b1fe24fcd165fc5f4cddfe9a2309a772660cd1ebc72f93130937bce572befc03127ec83f88aaca9df3fa783cc2c91033e5f1a05b423ac611fea86b7dca337d8f479b4610fbbbc7a3ed9c6184b77962e69cc35aac345984a319ec3626250fd2268af0ffe3334c089244382ae97be4c3ac929683082fb3bf8714fb1fc9e7106efaa9ce3cfa1ea70bb621bcf3a4c92d7f47fbaa0be19d8cafc6f483cce39b09e18a858a792b5bfc0479107e44fbceaadd83ce694138271272e81093845007c2efa18fc2667df0d99301849b72c8101fd6ea6fd33f78c1a422a6fa20a507a71fbc103284bf7c4e31ceec839f075152247e201f7cf9614a452b3f4b37eec195d37c11838a7a702cc66497589e265398076742f2eca13b25edfff0e0e6c18fc223e53c52c9dc1d7cab904b635ede47edd9c18b6f1fa4fb61570737c2c769b97bb53e6274f0224afa60a28fbf6be6e0645a49e2a3eac8615272702a7fdd6fb771a82bcbbac427e170f4a872f2d78a06f086a26106e006bf72cc69260d15f3b40368837799926565a84bb4a82a18001b9c9e9041b23c5becbaacc189298fae320f3578e7e38a91c4a3c796471a7cededa154f09ae888a0c14b9595efbe538ec9de33f83dce10e5073dcc36213583ab311d3c69f88b96d000cae06af430644a6ff52164f06fc2c2d94fc6e06b3013b77c1d31b8290fe24b33e5cfe3f17884c18d6411c6c35fd4250f30389a72b2bb13959aeff1056fa687d1e1152ff811e5fe724ed754d574c14d163287cdf7e3ee70c14b9e23e5f1e61593ac5e172db8e263d236165d530cc9826b21a5cc63cbce232ec182f7e3107b24295c84f2d15770647bb4d1a11ab92f6d054736a465bb90ae823f0ccb2e992261f20fa6822be12e9b2b67aa973c053fb3656b0bfe43ed1c4be1db6c9fcbde63a3e067ad9210ddc9a2c540c149ab3aadfaf1c02cf80447cb56c2f80f23b8a64ef093ff207cce93aa43cb26783f3f50dfcae34f1961829fe97d2aead2a5ea12fc8dab1c238450095ea73c8ee6fe5d129c1f26cbc9273cb9e70e094e0cd15254538ee0cdadd6f83058b4481ac1df1c34560ac94e7bb222f8d353d987dd9ed3f92082d35d7d9d3547bfe00ec12d8f9dc716eec3f228044f25c5929f942b0f421504ff5444c2c9e714daa10100c1db7016ea3db96d1e4d5c18c00f9ceaf61fc67c317d3ec707beca667ff99052c7d9037f826769f549e156191e38bda6e2a37435e13fca0efcd7c81aca5ef2e07e101d789b1d39fba4f90c5f0e4c4bb93b67f4cf0d1c38d34136fd5f0815f1dfc0f3893d96ab9279919401d8c0e998ccc7b9938f1a7851eefa7f3ad6c796d0c0f798cd73f6e8f0bf9981b7f65d3f8ea9fcdf6c19f8a308c90062e0754a35f3f1c801c0c08d169e7cfc7914452df60a825458b5455f1ec0c2157eb6f790ba10bb7b4f2b3c33cfd9e39826ac70fb247dfb4f74159e56b8d274792e7c94aaf0c7f9d2d48a8fc322157ecd6cf4e50f3ee84b63810a55528945da6acabb38c0e21446834d9157b8448839fb44ad144e487d2e4dbe6a1e73b624c082144963bbc06214ae4c758f3632788859ba065888c24b1f29e5af74e6c394eb0d45912a192267ccd9d8aad5020b50f416587c62045878c2eda8289f70b5c8a3c9c63ad927b0e88489010b4e103965bfc46c6ca109b0d844185868a268d4c0221346e30421c002137e4dc766feb14ccae3125e8819621e2709675b371696f0c52b8f2b867545890a8b4ab8f279bc4135ab4b7c8205255ccfa62194c7e8410e9b04603109a7442ea98f64a347174bc2cd56ae592e620e7d2b169170d4ecb5cecd0724ca2199f70827d5d847844feaa39c61e1087fd0036be94c37c2cf308de623738f950c233c8d51e7513ef9abb98bf0267f748e756e23732e010b45f8a97fe89ed9f3ddb675018b44382115c2c4da0f1b5b551c30e78b0dd0a0f1e8e284a182f3052c10e1c96af8c1446bb294f6218ce1c9a2cda754808521bceb939096d63e5f6f21d2b2bfd498935a90302a8ca381c72928604108e726bccd864ee5213d42c062105e48e6b4e8d79b073d04e1e718491a7afc2ca604c2adc95929487ad78401e1fb0f3dbaa8f87ff0ce34fca0c7792cd37b3f78167ad05124aa4abbef83f7661262657b3ef896928758d94943f2f7e0899c54f058777eb61e3ccbf56e41734e1eb37970dbee356aaa6c554523c4f530a4f20ebe670c3db04c4172ce8e1d9c8aec19d2c7a98367f37fa92b74f0071192c875faf1e07eccc19f0df5e951f09490918323e9c783fe41b4345112077ff340a33eadfc5c0b076f2be70e957f9c57a28f3738dff711ea738c1b1ccbdc23311fc4b4c1b9b9f4d698cb917db0c19b9851eb2c8f47216fca1a1c1fa7d7983c839ac6450dcee51482c76ef1ce68498373ed9dc137fa487e2c68f0e325aa6c87ca19bc947012d2fe354d6f06af373c552e4f474d19dc3c8ada941072b8ee4106b752a898073fb4ada8198313da6452a79288c18dc89ed83912063f72b6452cf3516f8e80c189f083ab0ee1a3b4395ff0c2d945c8f8682f7852217bbe50d11643ba0bae5636690b413b0fd3cd052779a5f5586d6ed1ed2d3821e64d2e7e939ab6d68213dc6662d63e0b6eacf3501f7605f560c11ff964ab1ee641f2cab9829f65f38dc4369990ade05790891f8dd1b9625b05b7c355a464fa66f2a082231ba6636a264c3d0527a5987d2811535896a5e0d506ff415ca5cf1846c1ad19afb49ae3ea3f283876311bce37fbe47a8227290f2353d8c7bda44ef03588864d704246c904efed8357b2b1cec3602ec157e96e8db6397279a904b747561633e74ebf7d123c11979cc77122c11f8acf058d29634e161ec14dd1de525923385266533ef615c1cbe34cfef6dd11c10fd5fcd172ccb9c7a91b822396c731b312a2c86f42707ef4d17de4df9fce6241f0a3fde71a0d01821bad42bee81e5d7dd87ee0468d7868b128392a3e70d2a764ad193cfde21e38fd962ed335d279ca0392a805d79ee00e7c917591f7b174e06de474755e29f5872a077e4fda9c1d1f1c78999ad2c46c4f212537f0f3c69c5ff34bc8dddac0cb90145a34b78fecac819f428f3226695591200d9cb7b454ae5155f398819b9255b21cbc4a0fb5b145e6808e4ef89ea1dc6c72ac0ef73e6e78618e06ba205f6ca00a1d9cf033434a299aad7505061d9bf0cab77bec430d99993e5d902fc0a8d106461711401b41175ab0269cacec9b49f5dd9ecc8e4cf87f93c4be42c4e6dce32a011d98c073e893bfdf8b076fa0e3126e0c4bb36111d558c1165f7c410a011d96703552e51c6bdeb96264636b0537ce094c5009bfcc335f46f717e8a0c424fcf8c1f8a8bb7204f1f05b6971830323e882cc031d9220bbc2aa5fc798f3f56040160e98c0021a50011a34caa3c60a6e78c1c5e18223e1f9c46a3e19b73a1fa703127e284b6123743cc2b7137f79ff91fafddae108274c3ac5ce8acb7037c277970ce132683a18e154f8f82073943b16e1e68d69f5691f1d8a70a2d38f345df645b64f4722dc8c0951794b298f3aa50311be640f235ddf86f7d6790867338f242c8f47fdf3cea0c3107e66ac569bf437512c347414c2e90b9a21997f0c4d96a183109e5bb66baa8f6cadcb163a06e16579f264b944aa2f3a04e15469478dfa20d2bbc5c6569e1b5e84b181403831850a9624caec2ce77c8118b802a327d0010847424eeeda117a94796363ab86178fb21674fcc1b3b4ac29c7f4ced0e1072f5fa52cee6b51c98e3e385222b669baeb42071f9c921cb62115434c986d6c65c18107908e3d5c871ebc8f1e7c8ae84d0790068d3c78f29b520efeb10e3c78ddb26973cea91e1b3aeee0fa559dfa3846860e3b782159cb6485afcf9bb28e3a38162aab8f6492cf18b4b155e30b2ecc71c180156cd1c505ce08ce165aa0408b1b18a042071d7cd1cee51e41d231075fa3ad271f67700a1d72f05d3a5b45c44467ad90d0110727daab6863eb9cd00107c7873ea8de2cbec1510d5e176bf287dce30ca1c30dbe6f46f113fb3c61aa6c839f37c59e6cc1c20647b5677bd285d7e0e54bf1b1e3c7e1b45a3578e929ca0f62cf59bc9a063f0f34bf86e951ee71f6d0e0456b69c8e3fb18724b7906bf071f911a42aedc1b43207498c17fe9719e187e351a422a081d65703dcc36bc6ca71f772b840e3278663f0e5172cc3d4c510dc38ed03106cf66427d948bf6d14b1d62f024a22f5d24a4230c5e1e46bee8715afe199f071d6070b2b25448c6d495f3f82fc0c841c717c2d0e105b7a3ad63fafc47035914e8e882131682bf7fead6e8210a985507177c4b39ff67882629def0e23c6a8071b8d0228c1a37bec60d2fbe06e10d72bad08202070c2d5270ccd1020359f9e8d882ff69dd96c75f6bc197cd9dba376678c969169cd4357962b2346f12dad802e3d105b13a13410716fcb19567a42f9b902aaf200c2de4d07105cf7fbaaca387ff8f731ca041430d1d5670ae6a32575c92530db1434715bcca9432871f7c56f87850c1cfbd7943feacd9bba273e898829766ece65565e23d34248c30c85637a0430a8e6fd21ce6437becab151fa0230a6eca4a781ff738e7915b1472b6d8827051e36fa080eb6fa0e00cd001052ff37978bb4a2936453a9ee067b57f3bb9920df79519a2c309be4b4c412589848607812c1890c5033c0b0664d1802c18d0838e2678f5df9eaf2fbfe6f499e0b6975f4c51930ff2384bf0364358c7bca9a7c84af023848f1dfbb349f03c86f65c3d0e127ccfc3ecadf7ff181f47f04ca6325da8444d598de0c4ea1452140f69219f45f083a40b61c43df90491086ee50b9542bfb4cb6a43208fba6baa7c1021789fa145bebef2288a1404a763ee51c66c9dc74307084e321f64f3717794b6941f389add7f25f92be78ff9e090c713ad72aa66470f7c9bbfe429557cf48cf2c0dba8340966e30e9c0fa1722b830e1df8e314dfdede289aa5d2a061d5868e1c783d981cb2ed2b11a26fd58103efa5235aa53cea0a1d37f0de723ad1d075d8c08b9ecfb136d858694a470d9c90aad38f24c5b4102e1a785759dd225d5c2daa5930200b1a341005346828a2c0ba0e1d33f05b2698e6f70ab73efd698075c8c09b174b39c4349c74ae23066ee6c8137169d527783a60e08f63ea185afb7e85db294d76862b9ccd6fa132450f177d9dd10a7f3c0a96439d65440f7a4d2e66b0c275ab2c49eb3d36b6acbe20373cb00adf3f584d0cb5b1d273ac8419aaf025b3f99846aefcf51d61462abccd8354339729f58a3f03157e8e1ee5fcb3f94a5d730a27fdfcf8d3e7978c98628aa2518aa23183147eb758adfa7f925069c6289c3caea499f2f8643cda6d8419a2f0c4d3fcb007a9c43b653f98110a3f7bcb3d6a5bcc3a060abfc42a67c8c3f868326161c6279c091a628eae1fd8269d30c3135e12efacdc155e923f1598d109a7b2f5a8c948d983199c68346d44a948c583199b70abefb3ce2d6a0ad5787cc1c5e36668c2e951cab1a256fef1208f343332e1a88fa4d67c17240386a26006265c4b397adc16e52e4e8061ccb8843f4a69be7d46a3e45650c3ecc60c4bf8ed030f953dc8aaecaf8d2d2f1e5c097fe8d2296bce779a156650c2f31fc534953a6b63eb0b2fc200036f5815664cc2a9981252e5a07e1b6c2eb4205b189384af99c2d7f25869c59c2f5470380b3322416640e2689c04663ca26874608623fcf185cb83f01f676f473d30a3113318e19dd78c670e4b29aaddc28c45b8608622dcf08390ad6e1f8f3eccc6561717a041a30b2f12e1bc87d78acae3d2f6cbc616185f7871a301529881082f76a7149253d6c6623a9871085f24c51ef98fa7198670c644d37dfe1e0bc8820314c8a2060d6614c217cb9c3ed45656f2cd0c42b8bd31d9db3dce682fcf18841346bcebbaef5b43d0c656175e30116608a2720927ea51614620a834af98ee7651895a97d8fe26b1439e3003107e5dfd5fd2b0f161ff7ff07ea266cb1add835c793ff811fb72cae3f3f17cf77df053cfcaa64bd2e341fef0c10b9e329d56ccd9839799c7e361b4ec673df8e8c1b1ca434bc959b47ce8c983e369e224548f2a42fe81077fd2798718d51d7cb3cf21392cda46da0ece0f33fc53f378e81ed6c14f93d635876a8c907ad0c1efecb2217a62fa28f49883a715717ef946233a460eaebba4f4583fa1cc3671f0c7e93fa58d1e0ebe270d2113c5737fe70d7e90cca621a7c50dfe78642d04b510dc72a50dae241f57c874d8e07576da90b25a84c7acc1d7d4223fc851eda3a206ff2cf727f5b169f0d394b7cd0473d588a0c16f1f67b710cd3ec3233379741f7db0199c9c25aa215e633be532f8f39124f5584a06ff2c56fed174b4eb713b0637c47caa7dd1a28fb31583133cc5b8a4d986c10b2949d5f2ffd9f66070b4c2fb62c65667f80b5eeaba907f3b7d99e9055ffae6524586bb4975c10d3e2adbf61e48f69072c11189b616de913ae6b105d7828f7c70deb16ea2057f5652cc83c9ede3d764c1f91e674ee9535f112c78211d7a1cb36fd0b65cc14fcb9b7e2bb7b644ade0866069eedeadc15305b7bc454d62f2618fa482677990ec876e1e6a654ec1b38e9d225e2a54f448c171db1c6a62c3fbca1805c7abd5e2dbe4830f0205bff230254f36d1298f7c827fd992e4a10f3c2794cb2e694df0369a87185b66e1aa31611f7687a908932dc1bf9c2a9fdd1999234af05462878b3ef693182d5d3e538947b090e0675665cc8f23389631ca5d309fd7a88ce0585cf4a1bd4d564a11bcb5510d1a7d22f8f31ee23b1c82b36e97d7c71eba2d2304bf262df2a5faa07f942038214797bb20a799672038ee2999e71eab851e4b337ee0af4d889c3464c8cad10c1f38f7e38b19e1223d7072a8d75c8fc2e4d4290f1c55b5cc03bf16fb3b7bc60e3c1fab88a7dd26cbb70e3c9f8cd942983a0b0e3c80055a58000361ccc88173ef79a5366a78f5781cf8a53dff0dbcbabfb68f9d53b0946303bf8b2ea824efc402b260280e87c40181300882869c0be3130800182c240e0622a1682c4f767d07148003592820362c2c162426161418188505a250200c088502814020180a074281504014160e4bb5e603032aa4c10de1c855b8b926580d5be3ed2a3f4ef429b151ca449976672408728391edc5211e40d83d8a2d3a8e4e9d90a29c90f1921c1843e3dfe23b38a14ed27a94960d9b62a766be6d28e8aca873406b304db64ff6d95d6c6035541605cce0cab57a1f7f627fc225f5ab415b167fed0efde13dc362c54e12a9e09455542c52ff82000fbf5551e43f7b48100e8a29f94443eb5933c8bcd502df3450fe00c6da5b1f2107394fe3c2f7f7f4a83a63ec5992522c28ad0050f1eef0ff6933287b4778203d3a162d4bfd65504d0dace403757aea8890ffe8dcb358b57ead935a6f09a81245ba588289d9809408b913ad662865cdae37152466f08cfe6d2f7c982ad178c2ceddbb754e24087f90fab72e57667928a2f64a4beadf22c7267385da110f18b6809bda8c904c54939abd4e4eb2d5bf8dad71debf39487229f7a9d85b1f7f5be8445d26e6be4c695fa0fc6641f460988b88ab746c47d9480e2b0e1d0cd3c295102a0aeb7ea15f8cf13e0fdd78fd6548f2015ae643e87c6af238873dcb5f17a2012e00e2a329c43687970f3b181531decbc4ab29c6d169c757a31f0d10e6069f119cc5f688f77ddd3afe101962a26bfba9d6808e67186fbcacb89d6670e5a1ef68ef7bcb9cd0643f3aa173107e064d8eaa3630e6fd452cd62b2f000a22c748acac756a21cfbc5d6c0b3607e794810fb970507e7da5dcbf703084c69c72b3f0511dc3982c6aa589d871f26f1b2343d35f5bcfe0b08f1f837b985276eecacef7277f4a1800410f76b84e90271b5eca421e4ea0744eb052dc8ab14f165eafa5c611836a9c769a86110c6751ec114434bfac699c4eec811bc28272c963b8098b28730311bc725585234c202e8023d217223aaf364b19a8ee930ea4cb8d19ab3c355afff072a21c21395b76e460d312057561acabdf761c9ac29aafefb4a720b74b7f167ed0604a79926409ce90b23819e0b33f452a3223f0c3a7494018c1dda9e8c5d5dd0b691de1a6ab7208c99cf59dbd42f4e1a4dce76125a03a6741ebb010646b360cc171471f4e8603f62f3ed625573ec3c4e5602c358039ec0acf430ba1711e6e46205dc40969244c328e7b65ee4c1120bb86995c7adbfa46a7d8b1ab6a9bb04fa449a32335691022962ed6688b7ff1b912da6da93ee9eaef5bb1c26968c49c9f8d2d72fb2634c5813d03d21d4eac9c671186e490ebf057455b784e74d6efe9812d40a4fc8d79025c045c10909240a6a1b69c0dc8035a0243842f09795d6d234bc47edc0eba902be4901076669a73fe3595618142f2c085aaa21a54288ab2bd241a95b8127a549749f86146b624b5436891f2b89c3071673627a18d9217620d104c618ec03271a3f0c2fdff23fa661fd445059be985cb550882a2b0fd269f40e8a1984a23266f50ba3229f31a58e1145d466dcf5fc251fab8270b890f3994aa310dcaf4afb81586486796c8e37481dae0be6ce9b8e2f0f5c38ca767d7e2aa15ffb551de31dbcb89d85d74113c4d116dda0e5b73f8e91e19fbd71d439f3f548f34dc5ccabcb93089db9c3c22e05238dcfcbc8a973305f3a43f52629201b2dfe4b524ea952e17d30b8fd065e7239212760449d5b17207048a451f56c88275a8ec93500d034a63c01a0a6f7d3777d88921327764ca5f24adbeab322dfee9d35ab83010aa06834032a109e953274e0c243b3a0f00bad58794f96e8493a18ecc28f19609005e81b12ef7fa05d24d17f24644490e9c675111bb36064203122ea9a2b84053258d0f569638934ca51dd3b408d6abf24895317fe31f4abbe5bd5b46c152a5811afb88b4b50bdc33286d829c3e7c49fc620c2b5e58068d491fef9313352632b3c4f2a2e249220fe7105b3222e54411515614b39868c15195c1c844c18b9d25b09a304296601ee8e894edb721ccb6d2993b2a5d24879585d295d3fbc5c817c6eea74e392bd850590361cce19d00f2d01e26dbe628c33e3e3d82bb8f098ca26cef804465c9ffc6ef1f75b2c6cc7e3d1462c88baa454234b23fae589bcc6b6ef086760bff8b51970a5206552f20e20110ca629e0eaa63d8fb28167b33525a56b80b1dbae97c36c5f9f81e673bb47673d9c5802cadfc2b3da05ca6c915752c1e30f01e5be3de1828c29877488a673411c58073a381028b4eef14c958546fc2c20135109425db636817f12e3bd2d3c26946982534cacc5130787e6da52b7f7f4276c7d906d187d84168e98fc59986b3d466ef3fc1cf92429a18524ba55f130c2ad4cf69257a6d8c59979f8d384262a9b4d0093907e2bac8a1a8059a49e2b6f2ddc7094a77c4269fdb605138dbba51a29d20a561a6f8e9c5af6b46223e3cc691266e0dfc9ae6712a3bfbca36a0177b100ce009a74507869026927462f23829fc8194d466e770ad0177f9f0de087b538eea02fc6d3bfedf523430d5126f865a0580d59f2e5610cf2da5f19f2fe874f6f97b146b39d840489d304b3476fbfda43da24d428404b7c4634931a46a6aaa5c86062988a101cd00c1462108ac6a18575d3496956e521de2a31a83938b2985b92003344cf8ae26187f8713d7d12d890b787605074e3e021a04ebb5811ab0b59172324b815f2399231eae69b63d5d917241fdf600ce1b0a380653c795981fe75d810e6fc3a1f2324357a263acf063d82e3d049047bb95d16278a97de275b861243480339f1892de4ec6f938a4e9f80ee13bb30b89f1dc037a19c273e2f51f5e9143d7362501c9259d9af76fd0527b8fedecd82a36252af2412f628dacc81276581306639dea68f4152865b29938c0e8c86b24cc5aa7134266c88a7dab9f484bced5d2c9af41bddc44e8841d8935f9323c993048793c13a6c7d1fe17e22c0254ee916e60ee79101d591ee3ef30ccf62d753703b2ed78b8808e9b8fe7e552c5300bff2d332e25682d8d2131b7249d400fd018c8f7097d236ff8859158d24b557beb24a58612f7017f0a398abe7e0b8ef6a3d8e087015c818834432954c27edd6901b15a417d56ea3175c7b7922593aebbea9445d0b3c39ce147ce910058cd8cc7fcbda71a82241e7d06c24d5ca76151661ec7efc2170ae107c12bde1889ad211881521027c1452d6361c78021f93b5bb38a02ac990070eaa7225b21f88d19be289c3c92c759dc5243b913eb1678612fe358462b2a4135fb6b1b532ef330a88c3f0b1b18c0bd6fc892d540c4fc250ed73d066245397f94e8a22f30b3aa83cccfaaca5019d4a44ab6312d081ab85f8361c38e2c4ac893ce34d7fe1e5e941b1a131d6ef404d454df304585a162a1e08d0a777d497bde1ae83a43dd4157a177e83e432bfe2703bcbb7a20b522b227a18b882a55ea79a7aa677abdbabb4244b5c2686dd793aa3e0a65047d41f91cb4a20cee4062a3a259eae98c8b0a859aa1e0ded44503465b08556adc7ae73ae43a5bdd91ae5cbddafeaf3ebcbdba1eaf4ece0e62df4d37d441d3d7d639b5322c5413d0f7b4c2c13a19cc5cdb1613ad23f5444ff6d4369dc25c455569aae64612f58c951df786b1a644978b65dcd611c681ba9e88491467e395b5abde55212ae3e9e44d53164c0bd3b573633dc33a269f47f53035e03aa0d40a3c51e53cafc24328cec8456161256aaba5f8fdd0c5630f5cbea38db200b8977ccb32b481f6e2d4a924cae9a3629872d1f9272e2bae55e0a754ac376337693e7e33517e59cf316e7fb58ac8e5b3dc7ca7f52c4b62fa7be8c1e095b33ec41de3c5f17cfc175e649eabf7a719470dede5535848ec16a789019639956f667c1298cbd16ee7d8df64ea5904d3e9052ee2d12088308fe6e1630c0a785d4f1edcb46525a23209f4e4939cbb7fee4d22c9a764844ba2266c876c41cc8aa8dc3228e8708015506dc3e581d50f480db4036482bd0599b85ac10f1e402298a70092d0c65c7e8a10dcc146dc0486032e415a05208cb7e21630047805d602680611042dd501b121f07b7ac2d104e007240a6683f31f7019af117b9800a10f6fc4b402f481f7edb39a406dc3af7af309b4d8cce91c029f14248a6196410aa0c4698ad2e4846f12b6a36b74fb24f2a2dcbc0fc1b999b4f8d473c8110953187d90f8e6bbed8e753618198e5c980c999ae3057c3d1f43085267b6ca942ea87333ec96893adbda64b221cf6613663301a31818b5c2c1a28de4e9cc4a444db15e96f4a1dda86f6a52539b1b3621d93863a3366f866db4630e05da70b271d146946ea631c8103a7677e3b71ca57052da00cbcd3b73e69e334b89f60e24669b312230adbc938df568338d28c35b09c9290f0742377fe42c5d8e359c84d8ccdaaf997bf8d48b44166a6a71b4aab86071703208c060c29bc17bbcbfb95a6ab14a8665482700c92f9ee2cecb647266e4ee01e73dfe859d89d8ca911cc1e4cea00ed8b287f038c9998d4d6f26262b5fdd9c53c79010ebd6951025baa4c9604409dac7d033c634502313bac4174c1f2695f961b6edd3772c371f4774c74be9a8452463ab7071b3fa425f2a4e08fa48ac03f946e55a4c0858d3422c2107ca31051c7b136f1b6aa3122de08501cbb82d03c8cfff2ee13e0ee747281852df694b1a8020924b4351c4bdfbf457f001ad572a78ecae2e250f9517c356bb657f12dd7f65bcf48ea27ff3717f236da259dbe597ffe5f6873888e69ba52db5057a4be9ed11cb6ac7536741daf7cd3077cb2420e065dcf47a4dc6c08eeeae8d463b0cef0b2c81be4b38f8320b76f8e4d1c4a1cf44c44b38b9fa4caad5955bca240614da3072a6e0699a4984006bb69ddd0a7ac5018dee3fd3e03e5bce8d2b49d271a18c622c62ea627bf26f06b2c0de4c3fe5569aa9163904c55e085f40b15989d89e5774a1d6ac3613277651970c0e81cb4629ae908c808fa05166a451220c31ecd1e394f108c162190aa9518816fa69d56c75dec920050d300924558cc4992b6c02fdf649a6660df816649c0bef88a97622511d54ece9fd3a48f7891914c8fe5fd6c892f65d0ddf72c6a980cd5750d1f2765d416a576ace05299564c843e44f8a7792e9147fb63629d64da11c8f2f73de40a09605822f90af7e9c2c39599417e1cb2b6c44a8d59c669e709e99fb1b4096bf08556661c382cd3e70bfd91215e6385bae26aa52e0f28cc48c6ee16a2a8f0ae8ab64862a7c784638bce0a3ed5d090de6d9f2009277c9f7281a3ee36b35dc180e257ce05ef32387079dfc3bd4d46685000f37de32f5dc688c50c301c3b78458e082f90ef30524c78cb7b156a2825bfda5042fbc1fde036faabd4b14475073e8ef1330be51935055e1e1bbb4b27de23d9681e197c43cc17ec3195eb25c429238bd7b0f9037c7fb9edf8c5c32a0fd0c08d0718e701f5cf5bb04fc757c3ebc3c3c349c0717a477f84e1db6b3757b3df82634b0d63e0d3a8065f0b3d7d2a78ee002e1ce6b8e810ed33bccd0cb0cb48987e865216312be09073cb5af781ebc149e12bc4b1b31247e08fafa2533a41946383c2c193dc411172e7ce5fe5aa0e7927c1b8fbfdacc1ba618d2121e12ce030548f1b003f95ba18387f51d7dc12fbcff9f2485b730fe151757163b05c28d052ea54aa2f722917c65e400e6c075313b6641577940b73dd901859762342a8f95e84f9754cdd31fb6d8566f290504e7f6c791edd8392e5d5a77b13b89b88b6c2b78e5ce29f1c614f07b5dee45db781786d4fb8a3ad7bd6f55c5b9d7666b04ccc5e2fc7242865b21cb879a8075727d231b9f8ddbc011b99d499fdcca756962ae8b2fde0aa296f9691d4f13e30a45389d129515f033997ae56af8750f11ed70b0febc6601e62415c4ecfeaa9ad4aee523ca4614baa1c3b38864c304770aef9d6d5aa20abc069f054749053e1f6d2d5228a2fef36b01c946b170362d5ab0414b12b86a7ff746ce20cbb3150a673d0789be5c754d92d6794e174321ef63ce319aa47589050983e8504a6c2583c903499e542001a132aa639e3794891335596e2507bc5d8996233157bc1fb679f0dcb2bfb0eb96c5121628d948b3220bc2247c202c48d5716ca1647354366eefae48685cb0c84125a4e50a21148e8a0ca11f0908082af557ab3969a80e690126464948df9139a0e9710add1033be46947711b93bfd3110b526d69a8cae282210137244ee8705e59d2f915b250f7335ca512fba334f755bd01dcf71c30b1a71c02bac6ffcb0b5dd03a5e85232664ac23d948289dade01d194a04795c444ee934c7370f0bed9699c06d31185b060f756df8daada31a860d97886dbe7392a3493b61481c7fe78a4071eb8b1ce43e5fe043c33c43279ebc74bdad6dbee6ec4a8750d373a03f00ee5329421508effcaf122e2464321f3f9ca4104e57b79867a68ad0ce694ef1bbb411761ca77b1dce7ab2ca882545ffed36953b445cd06ccbd08139cae0a853915157d0754f852585973a4d64f2b50c3ca5da731e225faee35e6940387dde2f0aeb64411432d6991c249b594b13539ddcd40c14f443d61adcc416287d8b6424ad53cd3d26c6b26efb36696a896f76dd6046516d26381bb16ee6de1ebdfc210bceb7493670bd61d0b8348db60896f0bb97901ba18762c2d8c8523976f17d7a04b17147d6118b964e6afad1646aeb90c684d4fe1b8b113e7adc346ec8925b7765668dc9d12dc92d78037f0d0dc05b33529a7dcbf1692ff1eca40da66123d70ec6bc316534c0f1993bb686cf3cb8c7c63f4a455e4d2e68873c280e2bfbd814ff74e758d76e78176b2e73551fe2db6cae4d37cb02fcce613b9146b601e315dc8fc51a1bf14f545c5056a5b0d2cfb4f3ef59dd2cc08c74e7022d2a11a098c6dbbd5305f8dcc355f7a1cf8016fa93c7c80c7e3e9fca6d301054e201d8b1d5b42e9bd9f131f623bfbd939c5165ecab3c9894c79fa12d3c1103582272273ad7f78a5f58a6969a45da394eb0344090a1b7e4b1726e49710e576d62b79d655c0860cf8ecbce16cec8f208238749ba722195bccdadd636bbeb7c11c661606e77b49a88fe1517dda834545105e2c3a8b6e90f66d93d745b144038a047e46c758e7f295b8cf5e3dac51caaa90273e14417e1a55b64f2a27cb67fbff8045b97117abdf16dce1034fde6558a35491331b678f72a35834f5dfa788b19b36c4e2df5e2aedb74990d2a1ffd923f6883040acea6e88f62f6e04a0dc5ab5988aead19aa8817634623dbf1e493bd1b1d348f470a5530cdee1101add85162a5d7689eeb1caacd334d91bb82bc3db82c9c6887fd9f416d515b873ca55355c83122bd7e952004aac6e2e114a0e3f0a7346852685bea6d85edb8b7e1d61c0b6602cb6fd7b51cebcd57aac0fcf140e56eb91cf3c16815dce60400a62aaba007f153b1b338b2d3b6874e7a30d2f0df810d24ece117ccae9c5f3dbcd107f49dc3b9525a913cd54905d36896fd86d0e8b9cc10ebca8073700cc15252166336405ed03b1fe5520919a70467db8ddde77ba4f294599565acf003cd89806aa2d642fd7054c26539d34471a4e0afe43637431a06acf28563e029c837c48160a583553ece5f5d787e4e9c1cf44878146d6469636b24eb95c76a3909f42800a953dcc1004f3bdb87319223dd610fad4f4f35d03b3de156c57beb688675c560c681e7b9fe42595afca3c81376c604ac3ac4491265951681294e66c391bf16c9812db6c687b639e01c36f6b4e3609800f8b643575f7fd89743e3254cff51ac8a1cc1d9351c4e37ed8a4fddc40dea32acae7ff51cc7a742d55eb2fb1f8b6497aa080cd081fb24dc3528bf430343740ff7575319623898123442bb1395108e03ac5004d57b249a2305f35e977e6a35363c0c42a885b308a2bfe2cf9c60ef7b36ff00f317e00469271e030d46068c045020eeaebdf3e8d0edcd1f47712b7a83510e7a3756e14dbad61bc80e70d3aab95388ea6072d6bf84082725f0caae61748c41be76c872d9e89c104faec1db28ad1ba46ef7172499040a02e4a1129b0c5d49f8397f69410a7053f36a2468845ae41a9d19500a6c86c2a79d7923c49d9380679f28a177afe1db9f9ce6b305225a8202c221d83de4b1639c320afa45248191f0c119c2e108b3ae91fe548ffcd1a340c607dd4550772c8ae745943408b90d26962064db78e86767b608adf09567644a11c5e1351d883a64177eb581d5dbf32f2682b3b458c8624d2b236c23244bd9294c334cd4cd9e6244e9690301fb9c334b9a12c810a550c306377471a3a2a5d2914a266a9717f59dc215400f414a8416b67ebd96faefd0960cd2fa2d949d5237f88d590dc07ef0bbb9333aa72f8c4179394994bdba5cf5b5398949326f802404c4f2a2b297b5c4d9496cebcecbdaeccaea21232b854190f52ecd46c19ab1034b17d97009635115779f6193eb0944fd737a95672c635770c14a624162ae21cf0cb4bf3ce14cae00d4746f8573485c00a0490da33020023a343c3f12e2b6a1efa6056e5c8f8ab5b5d2a42ba8f66e3686c606cbefd7db6d0121ae8a2bd1b05b03bb33c9f74997ff2ba2c590622348b5b816e6e1ed4a5fec280714a0a260ce16801862bbefb33d6ffb45ff9db3461ce2d3fcd24a5f0359088c24cc14535ad1fac56ca1cbad5662f6a90e0e216abf71ef168116dc20b0102483dffb44addd7c3cbba1e5aae791484dc32066a96a9a66beb7de236f17d207eb9f8cde5e3fd365b7cfc8c6e9fe804107ce7e010f7c1d68f11a8d41a885e9eb03aa3f56309cc153670163afcfd6e058ff692952f69487935b0aa332624806a31943bee50e54eb80e3b112fed4a1dfa600404a2e43fa7322b20791c01554e1bd28559fbfd97b52266b5000c8a17a0ce4c93416d0c9079f36b8c186ee06b5eb266e1926e3b0cced82c4bfd7c6339b895425438c221109c095aafff79461e001376132cefc7b7df66a3041859670629d5184d673252d584eb1905b647766cdbfc72d00c54956bcf2e943d085093746b785711b4c4060a169b66b4ac310b0cbd5b0194f016c80bd620242d0eae6cfdccc09526f32f7e1086f3afc4fb3e28778ad222e6cab4a4a4696c90b900684d1e94114a26c952f69ea071a0a6d1d79f010556d96410fec1718bd2bb3b406c010cca5de736b36903606dd9d7611b7d07bb0a5961466b06863f45dc2b3135b95252046065f8f0602478b2f0f915104a120d44795ff4225c69ac51176aa563ed2390ec469b1abaa0972371d229c56b18debb1e88265c6bf09aaf74495dd14a52511532be852a854796cdd0075e5d88c93025f2880d68121b5b1d6ece2a53c8d6a8e796c1b34ee8398f5972f2497657cc814f091a8d076031b19420a6d743cb5e205fd64cd8c7c9fae864ee91f2e3f2a5237ed405fcae8b134af981ea985d5a3b70eacb3403ef47a1d14253b882e323961ab4ed5fb778ad8c77e6cf724dc9284d49f3ad49825b00cf663e3c8dadc0f09f22a973b448ac04b1312b5243b6662cf8afd52a80ffeee609d8f3cd0f913481ddbf5203c0409c29e162409e81b6cb8fe0021a6269cad359e76a3424e9317a9254a4884018c0a9ded8bff37c8edc849c76c2a2e0acfdb6643d79eab10f8bfc16d9dac101b860f1303040939dea2f6511c25b92ba0c759f21561e73edb6e83374c52d7f26eec92e37713295b14a9143938ebc5f10d515cf43be9da7c1fc6a625bc6dbb06231184adf818cca6da892891d82d284c04920ced850899e44975ab580cb60882f682712a2ffac264b683947cb005e04c71230d35661ca51ae6d01be4d5828bd3d1447f5a3a4545f61c77c042d28e4c77d408ccd10c210fce9f5cfe9944e4ea1e90fb82153034f8390d2ad3670b901109ca9cde7d5d204dba03fb4a42b879dad17b3470e13a9c8bbfaf9bed8aeba075e03630146c2dc54119529985b190ce7e292d07aafc65d33fd21fb6a3e81dcb83ee1dd5fe28816fa3c93afb46d80b0d084798fa41af0055baeb86913b644ce9c7badfd88badd33dcd95f4290d0f45ffdfbec0a37aa53f076b85c83389488ade83024e28648f9604027a6cadaaffa1e241a2602cdaca451ac5a8d1c0ee516bac5607aa40ac4c9ce98dddf3503e8ca793b2e04d7d07c5de43e2822e6fa945eac71226b0ee79f439f980b133d523513676605c8252c3723b20d15360717422cbdf171d052adec1404f571a58bd088804206181b2486d0e7d6789372026c7814b03b37d67126e3fc7de9ff7d1bbc8aea620403dbb3b7809f4e2d54116e61041b3d19daecc007826cd938660929b008eade8607f26bee9541634469521516641cbac2aee5031932e0c336e41daac68d9ecfe851b48ea4afcb82f657cda0e6595c57caf865f2ff7d210d0f56a0d5e71e877f9fdc1536ca1f5f4fd83995b9e96fa9b0f05da4218f6545b369d01f0ad2c2808ed2f28aa35f52f8ca8e05f5ba1317b9ed55d0eb9a5915b15941ba34a0e42d2d6611c2efe651de5a0853b55e21d377ba4eb25a8301de87194b887a8c74d915693a63fda02b41b304e65fc453f076c2addff8999f38d5f03219219f740d734e460637b5889a42ff783e57b48a2b154befb87dc527079280e6198ccef00465289da783c5efc7a4d20e4d8298c3689d08ed2bfaa2e6b1ffe110672de18f6e5119461dfab385be86333447619f2e1913f03ba166d038fa4f3c042f99c009387409d0fd851abe13b89c3502da44c8bcb1438d0549e0ace00a820504a110641381d37cce5b2019dd452748f9cb3432f312aad502aeaeb4d5c1f6603c818bff92328f53d25a13c755b3ca0083b9bab6017864e4a543270847a37cffea2ba7a83d4a886df2100788624d9047f440ce20020d32bc3ff5e382a7271ed4d3b233fad7c3438bb202249ee30207751ff1dd9e2921f7732e2c404062e379356b8c28842917428aa061089c40180f20e49dc02826642aae9269606d297762b770a6ec7cb62dc0b2593c81122b29bf6cb1950360ad841a66390df9eabfa63059264295778d3eac77db4470e609a288bc60b41f4a8bbda57f565fc630cdf88b00d7ef7196ed526b18dcdd2d9546cd146fa7fe73d9be9caffbf1f0fc217affe40de7d91f48c9513b92dd0f43bc726982ac54c0b0b502aab434ca4e11457d05141049e5a21c8c02b0d909a51f226187affcc8243962a2975c3947ce50e5105a4316155068f56548c482b5214fb518579fbcde7a0e6b96d3b7398d427fd466d3363cabfde8d856e3db3d92a0fe065f15cf02b55809c7e191c64fcc0b0be46aed077de1c9f4e670f2727fb3d5beb9dfea2c38b33600f041006bcb9ec6c496cf7cf0d4dd1409ac395d9b60f9d85bee7aa1a79e36c094666c7452c63f526bcc3d80d9b96ad51622f65c62e591359a12ad56697931bcf50d9d7d84f6606ad8acd7b024588d56c5a08d18776503148dd3627f3c3cc94e6742d20158e0d7f975ca44c8bd7794254941b98021be18b2d2f15c29982142d08417c55d1f7f7875173d7b29a15224764f58e4d0a8826371837cbcc2608b251560ad0b1c30c48c5d604edf9ecfa1786d13813c4045a03620e223c5f55ec0358fa00af9463da09c40f66c845b9630f506bb218d08045b2c7a598335eb5771ff8a89664ed4698a0d250860c57017e41002e521a2859cff6c5340f38cbb083c92c5af4079aa7af0a04d6fb42002af3464b74c6405a17e885332846207c8b77ef84f574b10411285ff3345225aa2c8e81abe705e2929a4390aa5b798685215dd4c21145712c20b97b50cda2ed8d5d98f8081baf526ab732c93bf86adfa9082ca423585186ae8e3dacf521b9510c242960b7910725dc892903042ee5921624302948d026e16847b0a3760b46439330f0f0f0f0f0f0f0f6f706b4968db1892209394924a6e3c99e26d4929c994644aa201daf7227b35fc45daf01769c35f88f106bf0a260a660ae8e694d51c3306a24d79e35bbc0e205acb312771cfa67e62a3a1b6caf1877d4d663fb431cc4fe5ab129a7feec33232b3f6c7b57c58935213fe1a6164a4031c7be8841eeb94a4dc3b3b3d5cee31cb43a71f6442d6a474bc535e900370e0e10ad9344528028e3bb4e7793e29fa66c61f2f1c05c8cb7639ecd075a9c435315b253e4543f76c8cad4395667a2162fef6a735be580062c00c2db40043035b54e002650065e0a0c30938e65038e4d0cba8860c93153c046884e10dd8420b1f38e2d05f8a9d49e4d216d999861a071cba6849f5b48632d714dbc840044c8eb151c46a8801c61538ded0cc46cca124a9145ac434d46a0311d0428b1a5f040e37f4f132ecffe71c37bdcc00638c1925f8e28b2ed4021b5b6c4460230311d88800a380a30d6df410632296841ccc67431fda638831f5645ae41fe05843abc174f40a498586da176294ad3dfac20b0570a8a1fd8b5fdadd44ce07d3d078cf04003be040437bb1795248d1945209318e337466f9634eaaac359a85861a0752031c66e84447ac7c412425ae7e19daec164d574c4964e8a37576fe470bb96534867ea286f0963a460c5d5072c34f66f998141386beff24c9d3d4893f5a30744a787cfb89cb3c26fd42977dc172cebc29c247bd90e4e42245c5a076a1b7982d942c39135753b8d04fca29222c754f26650bbae4941a2279d442937cf467f1340d224bb2d0e6bd4ca22dcc69fe170b5d442ffd0bdfaed2c815fa14b34cf848cc621e8715da6ce23ac54408be21aa429ba406b358154374c854e8b357448d3a97b46568048e29f441d44d26a614f37e34091c52e87c435297785984d60f1438a2d0c7cf62c963a65e781f0a5dcc9cd068328236a572c7f184f633b446430d55c0e1847ee3efcfe906ab3cb92229e0684223b1ba44343fd196c9467030a1ff770939c5ce27a083e0584233a3994152f8a0f363dc070e25341ed52c55c345129a981d2bb72d494842c5041c4868ab4fe65b5065157e81bc18034bc071842b6446cb3b40014c48c061842677ca21c9440d5a9c31022fb840781ce02802115af3f852393b2567353986d06e90a3624cb173616a5060021c42d86487c924729a100902dab356fa84181d430542422c33c8f7833a280d3283ca77c1858b91387cf0a8ec317ee3e841952d63a31c3c6877cfcd5ffb42432d8c1178170b581070eca0d11a296bcc162903870eda896b2a4c4e34079dd0d312964b069553a720a2060e1c74524e7ceccca4ce71311ccd0023396ed05ac8d13f899ce1b0412729ef054d53564a475ccc5881185c78e128f82ebec61d470d9aa01a13e3dd32838306a4a43cf5e98e865add0a386660454e0d5b399868a86d083864d05fe868a6442bf9c4a2197f3d0f17468652d9804597d93fd1adb292102f0d5170c5365ed1c7f292212e5cd1c6f0cd2534fa59650e0db5195f2055c2462bda9c25e3c47a76f64bace852f88826622c79d858457f3d7f21ffb344ce962a5a37195f249c4e2a9aebd2151ac206156d082969e69f9e0e397b8a4697fcd25c1df17367136c98a2cb9c2a4fcf748a6165b4518af6bc73b7af760ce71829ed6c90a26f534d161e64deb8308fa251d18f24aecf15459f33750c1d5783b94b42d16bd01fbe537bce912191d80045bb5fb96362afe7202d49feb0f1894e7952f2212159870d4f747e32bc85bd136d92a94b97b6f988ec2e273a29615535e86bd1fb6fa28d14f16318fd51a22835d147d15da57e29a7ffcd44eb99e35b3c638a91c7446f39c9259964061d3dbf44633a85fa76d6fc2eaa25fa929f10c37e961c8357893eac8baa8a311e3c5d4ab47dd9e3b924e9868d49b41eb36a257574ee0b5d20c9b021894ecfe5d22db21489bf42d73bf4e62521d1fc5588794212bad9c9473479b382ea333709ff3ba2b7cad14995b2ca0f2a8d68f3a726cd929f534aab850d46f4d163325529a7919416d178862da5c2fe569b47117d756ec9a8f325546412d1ccc6ac29090b2a2ca90511fdc7ec1339593a44eb3106b95749690d8d3144bf49c7e429c80f2d3153886672a7fcd71427449f3f9ff227914b6aca41742a73e916e42888b64bbd730812c2784c02d1492e95fc937e050b1b80e87d3f4589d9b08b2fbea8c05d61e30fada408bfd797c423cb1b7ee8633ffe7e94e0f7a1bde899b993f64ce15b3ef415a2b3a6ca90f2c3dc437b3977cc907221d8d043a7af2b89d3a81e57611e1ab96d7a942ed9ade48fa17868945b4bcea49741258cbc43ff49540e3ac61873de858bf1851887810d3bf47ffe39684ff620be44436d0535c428b3c0461d1a4bde15528fae12d8a043e77162489e5b0f6ccca1dfa4db212869ea7115b809e4d0ffc5b7f079f2647827b011875e3676543fdf9c3d35ec40b00187e6a4e8d6ea0d7deae925d1f21762f7620cedc0861b4ccf8a79b6a1d17e89979ba634630535c4400d6cb0a193d728295a5ac477c6796417d85843a7acb2c4f60ed15023ac862e93bcfe19cf3d5135330606340d9dee895a9245dfa2031b5b6c4860630b0c24e07f01e847b1818626650d115e32630535c4288688828d33345ab39eaa4ea5d9f268862ed5937b86d01aa729fdc246193a1127be42679e49ae36c8d08fee6865a231f888908d31747263865f195d0c6d65f30ced294bbb886c61230c9d9012f725f6a8143b250c2e34021b6068f4570c6d60e30b9dccef9b9de3261d6412a3b0e18526efac8e7ad03c494a36bad09bb545308c0f43035a6851890d2ef4b1f144fc93d201bc898d2d20731ebf30e33127b9b1c537600f1b5ae87cad424ffe53becefd2f000f1b59e862abe28276070b8dbfe7899e9025b6728556925e917965746766acd05ae44d7193971964b60a9db22c67b93d89e636a9d0864e139ed22253e8834c10ea13d332ce047931c602d8131b52e84788d39e3465a2d0e9903f9c483619f673283421c9089729c256a9ef85170e10c3c613da7c223189985427b497e36bae0a8fec7969425f22448289e9539a2513ba1c49fcc8e9e49a3ac2808d2534395e52dd915425595a850d25f4b13fe8064d6561e35701296c24a18f2242cee6a4fe417e24f41355b28ed2958d23f4714f865f2c913bd7dc3042571a4f6eac64a93c4c2cd0848d2234bbc9744fd774d221b2c5465160638b8d9ac0c6161b25818d2d362a021b5b6c1404362a10818d376c10a18927517f47641a6a5b6c25b131843ee9b09033960e62c6d2c2890d21341e4568c8d77b79cf7280185f7c718117e38b093c622308adfc6ca96c3e7225836603085d490d0d41b926213e51c0c60fda0fdb5fa692b42ed7fc182770302840ee011b3e584772de1c739e1e34c1748b76796689568db0c183642768ccc87ecac0c60efa0bc243f8563632c0802abaa183bed2cd7cc6ca2fac5d380d74c0460eda3ca17c72beb0290e1a8961fd4278aef406edf5c91cb13d558e1b43436d86175f10b3416cd8a0dfdc5151297274c511ffb051835c23e7ac1c43bb41833e67d80b29e5f84f6dd1504b402239c2878d1974e245538ee9c9b12de4ecb02183b63d2de8d8b214825eb28845234aa8e6f2cff2d67c16b038dd3a89b2784557aab334982e21e39559b8a28d49964412f17e4996452bdab84158999988bc04b360453f96179e327ab2584567d631ee84f1f5187216aa6853949d3fab6865d69851e38b1978a96852e72a8d9d0ab240459f7da278b5c5f9e88a218b53b4622a550ca1ef173dccc2146dc8967f338c454a626aa14515b228451bb38652ed2943438d579005291a19fc63f75e6b9cc5281af7687199628ed1ee31200b517471e3274f5a4e34d4b0025ea393105984a2919f644f65848a0a1a3e64018aaef36e8978d94ac6cf175af274e8e55faef2cc0691cf1cba14577a44cca46723874684a7dc1d92981034250e5d95f60ab1281e379be0d09af99bb824f768ba3734f2aa66f2f39f0e5a71439b59deb4eea9f84d6a4327f5ad4ff733e712b1a1cfa7276a5211314a6be87379c59c9ad28210a71abafc5a2dbb6d26433e0d9db9eff89e5910a3a1d77811eaa14b650b9da18f33327898bddc1c33b4f227db5b62484b192c43bf9257d74bc6890b91a1978fd13b8aaca8b142c6d068970c66b221abf562e82be40d91e730b4edb1224abb60d02f3439c51cb443968f92e285cef3e62e15329b78b10b5dc8cfe193f210628c1c17ba9c573e2adb6da18d4964bc8aaa26d14f5f48c265a4ea59684686d916110b8d8ad39b3aa449d1ccaed088c490132fb342e3598390182b7fe8bf0a5d9eb8eb7b890acd76aa145250d6c18253e854126d218f69d11c2929349b5762b4bc4987f3390aa6c50708000524c0135a8b257294dcf7099b75425772f4db540a95eed126741db354ec9d99d0a61cc5fdf4a5345a40802594dcc2ffb24b9212facff01b77469f8436bfc8474f991a4b7524741653bc98fcba6254f708cd5fce7d42668b11badc9ae76405e93e9b5284b62ae5514a3648842e89043f1171937f121942bb6d1e2c478742e8f325953e2ae711e29220349663e7247229ffa01f03080084dea3c6d62039a490dafb4117ba544dcf645329a53e0c1fb4e1437c311dbf22c4a01ef49373475f4e0bf9e4e1412bb2b4340711a2fc66078d5f5e10e235f306a9d1417f3994550eabe7a08b15b2c63995e3a0f3ac3d3204f752f2dd1bb4ada3640a23378789890ddacdea4e59218b10f91a341e75b453b508d3b9d1a0cb172733a662f61cff0c1aef4e22b865ad303902c8a0cd39860bf229138b2e4592d52958862961d1c659e593eb896bb1bea2b75442e205a53c665071453f17feb4b2a99224c35634ba3b25879c8415cdc791a3cbf20993a28c5520f26e2966ae1855b46142d8355d2afad24df720fc47279c50d106612a49b427b93153326520e3145df613e143bf24490c09b9820c442ca8a05962ac40414629fa1cf55d2f4e481efd438a4e6852151e3556599c47d177c6d8d93bb6fb89a2e844bf42c498722eed48287a119243851c178fa081a29f985fe545356912eb13cd9afabf5a5658d0ae0c4f741e746abd8a6dda82298c1a5fac08323ad17e852d6daa72b30519464106273a0d4931531279e793888c4d74c2f3cf42cc87d81baa418626fa70f19470799272cf973b838c4cf4e366498f6e5e1265d9d8e2b5ac0b323071d06ee25d927b892e825716b15a5264042dd1f7be8a0ed77a251a917d620c61bb7f43a344eb32b1cff3b509f3ca24bac8f99364b588c94356126df5e4b051241a89c65a3b8428d310368a90e8c325b99ba3f223da58fa2be9889f23ba4a7132a3e9a9117d5ed2ee3029618497187138f9395944a333c23c9f056141744534a79efe97aff54fc444b4d6496bc81ef3e6181a221af191295890dd215a511de3d462d64c863090618846e445f49953cb199142b4339f2b56dc144ae610a2cd2e1e46e4a457df4174b17a84070db2209a94b21a4f09d79c491988b6c3e8f5734b01449f448a7b4a792471faf387fe2d244f254e8e63c8f04317f445739719c94992fad0b6f8e8cd91f48e328d0f5d4510e626dedf432b1ae5544e65d143bbe6beda9784567c501e5aff0967fa5478e87388561419f39265f60e7d32cd41751cd9a1cde15386d88e6b396b75687df39f488ee9d0252d298c8cbb113f9ae6d04e929d8fec9a29fb91436fa62d77cf5adeaf3e0e7d653453cbff15c23232e0d024a173cc61cd2a28113241c61b5a373d324ecaa7d73d54820c3774d2fccf444cf4ea1cce1fc868439731cf299d4abf53ec65b0a1af701982704f1d1da38c3534674a9cc778212187ec63c00932d4d0a830af6d9a3c9872d3d005d116277a4ad1d0249d5208197333a855ced025df36113ddf5e0e6498a18ddf953ba7aa5611a232e43988cfd7a77299ce9561410619da121944d8a0c306c999316851e486ec21fbc94e4e0cad57ac67f74c593a7a183af3d011db73ddb50543b3a1739d53f6936e5fe8b443b0247762e5b4d80b8dccce945972b5ffb85db012c8e0826921630bfd8490c309195532aa163a4b2a84a6f8cd39a94643cd0b2e66a047637471bcf8e2a82b6464a1f7e095f258fcbfe8140b9dde08e233fb8f694dafd0f5aa4c38dfd20a9d1e4da662fe2cbba52af429680f3249cb494e16a9a086b8631529e8981e1a9a5b430c308620630abd596a954939a9925ced4086143a8ba753d2edf49890140535c4205d826486163ac88042334ac8111b25c7d2fa131a71551253e2a9202e4e684fb689533a7a3d254d131a79ea338191626d427f04194b68dc440c9f62cf4ae867f2450cb316da194e42a3ae2149c49d91d0f685d392375a1219b2476894e69cc276921dbf4d2334aa33e524634a4bc22274b979561e84564a9c08bd28159fab4a4c100b0ea1ff34f3b188ee298888101a2554d2781e2f376a178466354d68ea1c27e809844665848e31d9595734193f6873263797981e1ff4a54dce4a4abe6b3ad983362c58fbc9242b6388f0a0bda45f517662a74ace0edaf1b78e9dafb4f2373ae874789b65121fb17334078d6eb028223fc3012275a4ac92d5c2818c1ba4c38345923b96639836b04a3c3d62c7b8c904b10cadbc71734c460dfa983b5b56fd5d060dba1416a2b1a49e8230c0f01460c00b0ad040c60c9aa412929c94bdfa3387792043067dee17592a397de5ac1870c4a27189bdd62f423e23a5051cb0e8f5f27a24adf9bbb59281f417e078452751f3653f3e56458a3b030e5734d625e35b64b6d29d4443cda0196178591809d81770b4a2b18ab9373e7a6b86112bfaeeb0417ce4bdce2f59459b67dbf79455250bc9aaa2176bad0ebf705926e14885a5152ac5891aa2c7bb00c304af43e04045a36244b648fa533fe4148e5374593b66563dcf533669a86dc1618ad673cc98a232d253975b8a3e674a195e35778849478a46256542c9cd29fb3446178e8262a368e4426b8f8e906b84e1db00bc0b7088a29f1877454a1e1984e484a25392accf734c2923555074397ae584d9b0d06bd150628fbe98002ec727163911f2c40e8f86d298915609e0f044a3e5db674b473b640431a09dd8625e6408030e4edc26fa4b724a8eee20630e319a68df7d92577ec99189d6b26efb26971c3b781c986883c8159d8299e5cbd0251af1c947a80e2ae5529cc312bd856a27158448def1af447fed9664d6ab50a26f13655237c44ca291d57122a414f3b5bc92687d230915ad4ca87c950e2447243a75796df990a37b1621d1c6d3b9563956dbe4f3883e6f24cbcecd107f2758010e47f4ad93728913173c685023da2b2ddd25c2f9c70c35b05181087030c2ff4849ef59f28e8c51e38b2d46e0c50546e0c505ea9cc6164e630b2db4701ac7699c19c80bafc1b1884675a876914146b66ac760037028a28d3974c7b026938846678841cb48de5e13d6482d3810d1e5e8ab1c3d9679c13f441f622a632ecd3cce86687c4d93ec7e0add092a44a79d33b39ca49452cc09d16f4c31ea482df5d87910eda414477abe108316a920fad63cfbe67c125c444c96f2e842632a46d8ce2b4fba835c38ccafb54af95fa76ca11fb196bbaa2734140c7f1a5b1e5a303dc54a5eb932ca53077864a1936d2123ba9b328d1f0bbd448d331f41e28ce95c1c023caed0ca7ed2a9af19d7e2c70a9d2acbb313f5636638a304fe350c9a1835be0803d5a30aa6d4e472f263c8d0501ba3920aea3105529829392bf2904213f2e768228849c192360a5d049f49923265886128f4b923982935997d528ac7131a212321e5f844916f7938c1a3094d9024733a75855eced99d053c98d08e124188d4b87c9ae3b184f64b6f76159557426fb27f3246d1a1b145496836e82097af5246181109cd48b314fde462ca7972844e54782e9d496c5f05cd044705840a1e46209e18d11e56c28504f00c1e4568a3e9f8868a4468e24c90f35e9563653e061843e83bc92cca62ac3895230f21f4f1a5156244675ff462d8a5c023088669ea982d1a1322c6185fb085512e081e401883c70ffad22d29276fcb3d2b4de1e183ae42b6986e3abf87e936b6e084470fdaefccfa392733080f1ef45772c63da7a7d49d2484c70efae07ff1623ed5415b16c53463941e39e83a789e84ada467544e073c70d0e8eeef5c2104994b2578dca0ff53aa3709990ac2c306fdc44a2512b73584354fffe05183b6d46492cdf690d1471a34b1359a8ab36e041e33e853ee1713a66377cafdd5c14306edc5e599d6f8d7923bb61a8bbefb3cdc53fcac84a58b2e5c8dd0018b463bf5f5041dc1cd64960e1dafe84b365676488d713c270e1dae502b8705195278e6d0d18a3ec6d3159697c38a2eb56b0c22590a00e0d0b18a563be4a05490336632a98a2e06092a238fcb45472a3a31bab179c14477eea0a25377b150e163d4723c4fd107cdf1634c25739cf7d0146d6a1016636ef66ff4cce000e0a0a3148d889c32254b527964320cff2e66a00f3a48d1e8609dcf52236698d028da0da37166639268fadb193a44d1659325439a6ace734b051da1e882b058a6a478f4588d91a103148d8edf9ec96da9a562868e4fb456e571d4d43fc68f3cd1b6c6e9ffe4b8f2788da1a3135d1415838ac154e9c99ed3c1893e649f2715c722eeeb1fe8d844972f9899129741869cb459e8d044ab419586183b842c5aa358e8c804424f99f6157dd250834107265a51b91db2173fe85668a8f9257a9df114a654febd3ceab0441b2244fc6e52537d255aff1232e5ce123ae6130d35e71d7450a22b51b2933211a72b29d1505b1d744ca23725cdb2766ad39d241a6a92683d5e359a5684cd4185865a24bacea4de5971e213355fa0bc410724fa784b9a4197cc4156d018603ca2ab9c71720e7aae4c5f1a6a60180e3a1cd14c506119d7f93e568d37267813cc10e34760d7838e4634f9527c6bef91869ad7104305ff05239a1ca9334d5cfcca2dd25023566ad0b1887673680d224e76092b7eb11b1960c0c6165f021703035e50400b2d8800ead0a188f62abfe41c4e9f887f47f3e5927ca588a003119df7282509741ca251a9741c29c134c34e0c1d86682e6577ccd10b7d2a9dc0133a0ad127add7174394fece3184e834d6520e27456ee554c720fa8f9e2f3a458b17ca14801f7408a2499e933da836a5e3840270838e40981614e800846951818e3f749a5cac72e60e99e48661d1e107b3644ecfa0a71916fcd748351e055fa3a30f9d0e37a574ac5db47f68a8a9a2830f8dca99f164fe988e3d742277ce3929afb861f47a68f3759ee54e5995e39c873a72b2ec9b710474e0a12b397931fb9b4a89f93bf4a942aee6093713a2d7e03a74d8a1ddc897a1bf7f3647d5518726269349c850396b0a4a74e864c25c0cda4b73e8254efe146304cb174d72e862f65ea4d4d372d3c7a1fd32a159b307c1a1b768a5642839ebdc794327f9ff5d35e95c458f1b7af5399f2bb5cc39316de8644b6cd9fcde193f674317465a59525a53d0c95d439f75528fd231f732ae6ae8573c27dddd9ba29a34b421b6a256675d1d33d1d08686529d84521d4a7567e83c73a7aad06186b652f6c549b00ccd06eb8b99f927439b3b77f2eb498b31f93174fafe8ba1919647a8d60eff210585a1d9bc92aa93f534081118da8c15434624e80bbd8f100de2344f7cd3f14223bd555c36ea7ccc7b17faec49a6987424f924c48576645c95b0fa164cb157d6429fb3e36cb894b3d079ca0e55539164b43016fa1d9311b44fc6ad70afd04696386135e35e45d60afd8e90f7a4bf748544abd08889e6a353259d4d5fa8d0974cd627c25445cd32852ecb34e8d37ea13d2b526894d69c21e5bf28ffc928742664082621040a6d0a59a55f720e97b37c42bfd921491f59cac2864e68e545758855de49e59bd06b8a3a966487666b98d099ec16333fb910df25749623d973c994d0c867864a427549e84f4d8c142174895199143a90d0bf87b21c2182e5544147e8b5423ed349ad0e233496dc43cce5a93cad3a8ad084984290f0eb4944ac8ba711867f042c79d041844664664eb14dc91f9dd94b740ca191d1eb519e5384d05b89e78fa8c9a4856410baa4631e9d33983cfd11085d6faaf7860bd14474fca0d19d93ec988c5b55bcd81e3a7cd0e74eaa914ace9a89680fba343159d7248859065da18307bd5e90983aff6ade20a742c70efaed92fd25e45cce57b142870e9ae8232349ab92837e7e44e52037d6818376c44ccede9d21c3bd8e1bf42156653b6cd0674e5953e6988bdcb18e1a349f1b5ad9f26ac88fe9a04113d62ce2c8733b66d08e9e447d8f271a6a334ef061a8ed023a64d0e6deff0d614f983051d0c5171808e30b302630022fb858c1f11a6378f1261881d7a051016b030162d1c952c2622e5e9c20ba44818d2d36d208008bfe5fbb94d06b39cffe2b3a1d9496379daff72f2886177f5cd1080fb160e169322c1e81175cd0c8408dafc0b6a2099692d0b1e75f474859d1eba41421a60e20c02a1a19832c19b3b98820e5a38aae63d4f74cb921450a622a102015cd242d2f5d624a06714145a3a485e8173a6ebc89a7e88376c6a6088f254233459347466cec90b21bab1f010386408052b4d9536eec4f94146d4aa154c4128d00a3e882e82023a5205134faf22fc5d85c3223a1e8646c176d265a43e512289aa02e6e290613a152e913ad27b50af9f2e70b274f34da532cb95945a8ca74a2d54f5a548ca72432ce89bebcb4df42d6df8c6fa26b535af4f5ab89c6f3cabd456a55ce7d26daea2033c6f408139d5ece6a42f5e812fdc8593795a63b660659a2958fa1e4c5dce821a24a7416f3e8243c73c6c8214a742a421c97b85eddf93389d6946835a1f4fb67cf914473b919b3591646869c48347b1e83dca4a23b2707126d9fb6eccca355b13a8f68bc4b5b5a92a0470739a24979fa3d5b10a55b348d68b4bbc7a472181d63c430a2cf0c9d32245db592994574c1742f68550a1535a388fe227a0ee2b46ace299388ce6268b01063a798841c119d8e4156f52771cfd60fd18cc6ca6939e4eaceb921fa492545a24abc108d67c965cd721183e584e84275e624e3697fe60ca20b25b2460a1f3a9b1444fba2133d654f22a3c681e83af67b9f751810ad9ab064b12b6f12417fe8bda29888d9cdc762fcd068d36b1649585f69fbd07f2ea92364521135b504e043f3af7a9d91a45870f7d05e85ea5129d45dc7d543a784d4b8cad8e6a1499553c44ba2c9a249f170e999f19c2de90e6d18f9c14396b8b9233b34628484f0a62dab995a8736b586164be854901fa325f5054959015ca2004ae8940871d67341cb855c00952840121a9d3f584e737dcfa984842e24e127bbd7591dff113abf9420840ee531f9c8088d8fb470ed95a6742c8bd06f24edf124a80a40842e3f2ff9fc8987d068ea9e4f3a7c9e66ff86500021b4418a10163e8442284010ba98a921557c0c844eb9fc6b2c7d2d12827e8089385396820e0805f0419f34c7c827223de84cab6acc4146f60705e041abae39db53a6fc09a31db49644d6f87a3e294ed5413b22e72063ca9b265205c8419ba7f307f1c94b478e3c210e28000e3aa1e43ac58b7ddedd29c00dda5826824cd2bc0d3a77cf2e1205a841dbfd29a4f828132faf601480065df8c58bc57c96534e0f11059841a39ee4829c104ec2890f510019b461ba93f40ac9820a2aff118baef3b67c8ad02523665560d126e95ad65d9a37f28a762e357e6f66d2e0160db5306a9031a0f0e18ace4d43d0ec504db2a78f5674f9a2f65c4c7156743a88291d66397faca209c22a95d09d4badab0f557421f165e246980d1fa96892c7901e49e4594f96f9404523927f5f3661b249a7740d1fa768c782d2d9b47990e2da186af830451f72c5109390efbe9f82c347291aa5ca52ce512529daca0f398b5b1239150bf1318a4633cefc58507e88a231eb18ddce18f3a9e42314edbfe9ea31e95d00bcf0018afe4410c9e72bbe53d05c7d7ca24daf8e21553301171f9ee854b2fec9102fc7c5a4138de8554b1ae339a6d01d514e744a5b5f9c9ace31a7f0265a899fb2c41c160c68a1451847f7f0a189aefdb7c467e61025fa99683b2b97574a5aa14310136d882185f8ee8bc997ba44d2f425e71cdb6237b33a7c58a249912546d3e196ed9f4e7c54a215b1952c7e9279656428d1cfcbe49cb359774b7612ad78c8254c7c8cb39d93442f328ff09131168946364bce21944adaea42a27393e182d2bb3ea217cbb191aad7b235c5117da4a44d859cc455df3e1ad189204907fd97f43cc77c30a211a9d5d5aef2c7225af1b06cbaa694faa188465c97cec13b826ab1a07e24a22b4d9d135d15118d4ca93587b6e610bd8660ba459c95c82363884ea614c3988998698b3e0ad1acc8082244d7a72207f149cca8f820da31193c64cce8e14c2288f693f60bda1dbe625803d1ba494d95525736d91e10cde53ecf49b6475612fda1d1fa9da7e3c4cdd2c1f8f04393636ab13c5d392de94de05eccd0157cf4a109dea2dc3fc85d8b232f7e043e0351f0c1873e566c889b0aa2a1661f7b68e74cba7fb094af0461b81833ca6a7ce861bfa024a74eb93c341abe9276533af881874e2cb5778892d4537c68858f3b74662afc6b902f7a7eb3437f4274848855952a7cd4a1b5d2a264a6f0b8a9e220f8a0435f4a8952593b6638dfe6d00819b2e6a4abdb64b660e0430e9d4aea334245ccbf1163250e8d07eb11d3bf29965af8030ef7f18626c81c1e83e5e7b03e9202840f37b025e35cc65e8a79f0d18646c324219799f15d8cc00b2ec2b07bc1071bdacc1c454c57c1183e230c2ec8181f6b686499e72427e6ec99facff81a5fa437f7a1864654ff6810424c764ef691863e898a7af1a182867eb4c2568e9d64a4c5848f33343ba73a998e500b1f66687757e4f46f5a862e98e54a2b599317d506aef041864ee470f25f445c3ec6709c8c1fb46eec430c4df0cb3129b53ec2d0c6dc69498769e4030c6d5810ca2fb6cd44f95ff8f0429fa95af22b4ab0d4330a1f5d4833e814ceddbb0f2e3822478b1fb3de1ae1630b9d52b96c4105f9418b0e7df0a1855e26fa4ff4d53019b72c7c64a14fa56dc2fc471f58e89330d318445636b678c2c715bae416eb1b72966abc8b2e9c86185c7000cdf06185f64db589c81946fb8318868f2a74ad412d66f6d1905946808d021f5468c2b8e409b2a652aa58828f29f46b2a35695892cb3ad2d0b3314cc00593e0430a6dc50acd1a46ff1185de2d488d78da25cac60e121f50684d5a4b94d3d9fce44f6864189f7cda6304933ba1cba7a2673719cc74b609cd6ee6dc414499d0a4123a4da35f9f597e096d4e9ee2098f1e1ae62ba1ebd4377d25f249683be69057693284543948e84f5e720a494dcfb2e6089dcfc8edcac8d9f4334668bea4cfcaebc694a917a1f710e38928173ee28308cd9608d9a1fd4c87d287d08645bf6e99989a1a0ba15349f57b9c3783d0f7e5ac90dd0742632a47798d97d354f2833fa305211ff45b6eb224aa3de872ced54ecd1667c4833e78bcdc2bf9a12a2b3be824b75634d9491d741696f7627672d0e964c257f54270d069d5d894e3456ed0c58b1983c7cf6c9d6dd06b8a4d31872082b5a5066d858ca3dda5ca443b1af426ad4d37c6d8c70cdab1603a29f9116263fb90417f4994ce913a985a1c0b0f5834ca4f6829bf98577442889988a482e7841c57746ae2b299a956089bb5a22b1dbc738c14ef59b3ace86228d5a3e62937c26315fd66feab4a113d43ee8cf05045a3a72792f87a523247450b1ea9e867bfd2820c42ea6c9924111ea8307fae5c9623ff893fe0718a2ee78ad9fe1e61c1c314fd88d251437896a63c96a2fd0f2a4b959aa6c52039840729ba58da3af2aae690aa1c45a74509cd6f667988a2b718476786e885a25dbda02df1a4e86b388d30aae0018aae528a61cef39fca2bfa443b5e2da32fe99e68dc73ee0ded1ff31761824727fa32b55cf13563a2ab9ce843d65b3f3586ee3d0a1e9be8b36c0acd1db4268ba9133c34d15fcc399c88b9db158432d17c90a364322d3ab94a010f4cf491b453cbfc7a09f64d3797e4105d4bf49d44469016749aec180db5ad445f32fc249d924d5f630c30ae0b0f4a3452d42fddd3f2259dd0505b810ab84824f098449fe7babad57292e84465d038597b57747ea5e01189ce430797131f1b4b6cce3c20d178e5d01ae272f8e0f188de7ac3e7cdf70d22766c0f1e8ee82da378f966deb7886944172c2f7c45bf90e48519d1f6c994a26eb78be8325382f80c29a2b31cc763e82ce22fc744349bb22c660a1344b4399a56c9a7cafd35b5048f43343a7f7c5664cea51e433441c8b5e0922cb6e5268cc5834721d0a3e43de90b642ef02044674a4555e8d03fc16310cc85696c4bb0b0229334f33767abd6f6603a33be40697532f010446b3986ce73994ad3f740342296506d1a93889539207a49415bea8c9c3f7422cdf3524e914408a31f9adc2133f89970cbbfe943db2352e6b7cbf3c4c987763367bb7dbe2f69687be83b04f352e14354d1e8a14999e4e5f0d4300d6e1e7a399d4a786874c7880f1363c26ed21dfa28f99212526432070f3bf4e5f1a2b45d5b729cebd08b5011d473ad4329131dfa7822cb76eafca1f19f43ab31e7f698e25c9624ecc2cf1711505bc19780bbf00678c8a1095bca53fe240e1e71e87775c73f5aca030ebd7a6e92b73821fe346fe862e7d893e22955b1440f3774b1621016fe5494cb590682471bdacc18fd2433e991612c0ff060439b93deb2b04957498bb935f4a7a192757691a0c4440c2278a8a151b2e2a1af52b3443f0d9d8e514325af9c152df7050abe4605b4d0a208c1030d5d07091f4fe4f8199a9042be3b6fcc4bf898a1f1109de31bf2a1235ce8e051864694bcb8c162e84186d6fb734cc1abaf37788ca1d7a41554d6982f6f38d7430cade55a72d9a062e86a30a0c50e3cc2d0c8881d2f31b344887c0dc1030ced6739f127d6ae6b418f2ff421f4e7acef1e3b688c2f6ae8d1c88087179a038da6a8249165f2582c1288c3a1300e82181cdb1e00731308001838260e8562d1783c0e85dd0714000556301c46322e121c201a101c188985c2703010088602815020140e8682c160402c54b220a90750ec386e472a6aabeafa50d23831aa616191d6e96e4d392166cd79a1a9a40e8e8344c9a1f2dd5e6c7d119fc22020c62de6e276372b0ce63a91a585b50d4d787b24ecf877cb990642baec91219bd774fa128ad2a874537d90b5410e0669a0d6a0b0217e9e3876b91ece1b4c139990918b3e5e1a01b9a30176f9818a5f13a10d9bac4bbf6ad60d0b3741bc302414abb0d3f734a484adac51beaddf44b2754a24a8fd7d5b112b4dcdacc42916c4fbfb854ad8eb265f97089da8b4c689b9517a29905b344e041faf922df3be6e4234f2f96330878d796cf6e58ab32d8fccb73d5bba1cc197dd2f8ccc6649f6e014ccd477a3de545076a759a8ea2149c159d35bc40da68d0a4be05be8862f1ce4a752a01fbf6d9f3224563a8d4b40c5c05b1375538ca85d5a1ba0c2715cab1da79e08d1a4f4c4cc882070f8c57e3a02b3880ef265d6a7256eaa83547112a8185e9758cf9632c75ef7e356a9efa579973b185cdc68027fbaf913c5d41297129074062173f4589aaa465058e647680af0bc213df906e358f227578bfbfddb810e066cdbcd7520165689b08a4d0a2f2d0deb36ea685b3a6882601d22684a95b4099f35b63b551089971472ec59827b485051a47ef47355d2a73ef3e44f9e213010ddc62e8f024038871a4a0eaefd42a62508c39615ddd6cd8f1a70f883f6603b3fa09f50db054e9fe44bd8044bf3c73de01eae0f8068754750d98adeb374485f1850ea0b8f58e2a55f45a80778e683d929a3aa7b4059911cae67d65f71b1fafa30152e1bf83c2f04a5d2fe6d5595f754b1cba67066b11c4157d267e82ee806fff0f40d10dce8d2dc57aedaf2ce713c9c0492e8d81a6da84431ae9d413a301b94c32e3dc7092d8af0f4276b1b21dc2d1cd42244fa05419240e47117c756d67a479dbb7ff45546b0d08b5849430698e28455eab30dc96591d87e8b25c362ec62d6992df4564e9ce7a4a01575de356f0e0e430232d85d4ef107f2b15bb0a567c7cdf39f9663b433d79448299e30905e2a6fe12aff088455e1ad3d8126ba6aa39d7a59a3ea2509af36a14d74a76927e33c851b2f92e5c3957e64c1d39df94bf82f5ac55bf90e6cf6474b3a6f6704a0df3fdc57fe3f17a1ea87bdc6dd98f312cdce8ecddf6a41bd5364d87638ee11c9b951bd8d2464253c77f703d1dc74660b646f2c40642ff97530bc25631aa7f00273d21b0e96a25a2eb060d36c7910ddb59d2795a8a47a08a1a43028fd6debeb0764a86fa39c8e049fa9e483ed358dd868416cc12b4b0d10fcd51e2a77c61ad4a6b719ab685498e335060cadb8b1542498c860b03a07019c77fd3f7c784eaf916f31c083b561c3c2a4d901033870bf80682f78de86f4ca393c20cca1711b85a8f2a3b2111648041ff451d98ff34d077168e52f29816d41873ec18ee2e5a81a2850e6984db40a1bf339ab220493ff82864da4dbc8e7cbe7a1dc5c25aa2fbda50914de7d7b14f3ed2b25f36703196d70b7b8e262c79509fca843317cf03771ade9b69d03852bebd9f6078e66fc2771bab3df922a0e451905fb2f216c8803e177746364c27a2d452796c6fbfdfe322a66ff4a59f9a6665ce6587761d3241b64531bf0e04a859955acada6dc0ec8adecf14291af4835a000f1b7dcaf5ffc36a105c12412ffe68dbb8b88bd2a082e9c68607fc7d8c21a93914ed7db738552c37a59369fcbb79ec350ba5b19dd4af8f6edfea5035aff8b750858bb1ef326be57c9973241f42cc742b9e6896d9621ccc057d61548fc1071a8fe8b34b7fade00264297f510c62974af217078293edcfe522d5bf6ba978525edb810ca04cb01ffc2032f28ac908f87e6d4336cec40bb2c5e4fda21c1eb6fc1c36bcfc1e01ac1df6be789df73a5e09958940f4c97cddd7ac0455f17380cd7936e7cfca3837c7f1c3684f9cbd03e838a9228a59cb7dcf04a877eee97dce6fa71f8511d167689b484283626a99c617d2114aaacc90eeb4f0016c93942bc345e10cdb47cedc2376d4aedf15ebba71b2d14fe7f99aaa2430da088902694bbd0951b6bf5c8f23df10b74db639f61d46b1e2e4d6eed3f790eec3b3d92cdb6b5b01962a573a186146c873bce48a1670ac6c5b1b3d0b5e5fbc361506b36a80682a2156e278f98a9d0910a39a8d08d053afa8af0ab638ec682017a0062a4cc22ed4ecbc459f37b4a8549cf5aaf5845d29fe0b5785e096ee9cb8f83a786dceb30506a8692bb1f95a4f588ce7031502d0c89bf1b7ff992cdcb25c8eb3ba408904ebdb80decdab18c8d071138b954cdabaf99feed19685dc186153443161d92da43072ebf543b0ac17cff3fbc003e36ebf9e99af77c15a124f1666c789a65c4ea43861dacb7ff85bfe43ff9ff8dcfae1bc8ab80c942fdb5a9aef0b20c14ce6c8e39e0ac07f9c23abe0aa9aa7496b8f70efb3541c4a4e70d143b14e1345f156706b6047f94770a77ee9bd2deddf14f3c994c30b6be5928970a60fbecc07517a872443bc14fff7ccfdf4105cc7acd6cfba5ffa5a5f6d496de63d839e876ec23215a8d57b74a4a1f770740fa09094d200b4ecf89446af7c0fc96bf8a9d71b1de1f3c6e6433453c1c34981a87678b54604b1a373601cf6043e42278f8ab60582c1406a8b488a86b83793af5ca3494378c28e206eeed07bc5b70126d1b9447382b1e5b0b396167c5c323f6eca56b41b668ef381c58e97d8ac3772fcbe1db9f118fe15ae4220836434b3f46ba439b4ca85670f9a6797ab0246afd37a728b6ff8ddd2294584a79b6c691ec2a16f7c469bde28aa39579e1107b2408a86912315ac2066ca534e0e68e1e63df2f1b0713a43ede41e440aa8e4d5f64a724fcc470168dede661909d541c5122b40f828a5a68710f6dd543f7e279c1b4a281b3d62910558f577133cfc85a1c184d1d4834a41af039e5f318ca0471c34f65bf265e525be65fc641409780dfaad4a063c3d353c073f3801532be65009a217320a333fc17d02f38c47968c152fce5d9f8269d1c2dedbaf83521e28f54746894195b0bc1eb8954a3beb5f84bac90d6cc9a2a9b7ae18316e4f1ce82680823fdde5e8fd9a6cafa32bc5129919187e26111ab2480d60024780ad25af9a5134b368c1041f83e2319c69d4b1cb37d22c825fbeeadf561c541372c213faaf453cbee5a42a9d1786c8109dac898c459bfe62070c85fcc9b48960a134b90b9ef486ea9261063c1f410422c0408123a1b442b85e881cce45a049610641020a5dd91071504280853ab89c04e95cbe784618ad60849486e47b2046c8aa5f98014c45625558427f69042ab94084c2945b742ef538e746b095a99a2eac9622478f56ed852fc2428c0218464307d64ab69ac215c38c018025bc7956a43bca3f250da735d445f27de227916a31243bb949030819b0b10ca18a62b685772623b8762db36318667ca09d17e82f7f4107bb9d51fadbe2940fb1f2a9592afafa7bef390338e0e9e84b7ff5499d60a13dcd043211c328342acb138729bc8ac0861e9be0431c5c331a8a81597a49970b93a9d37214bd2cdb083c49fc71842bcf3da8dfb6c1877ac4bd6f92af45fbecef336691fbce7c2ae7447711bcb5245c4a0879a39022753abb10ff452144b38d1cc27501ac35c75615237912007f3b0420351a898a46c2d32cb2f09c914987750ab412334071895892786c93a082a3ea0a871f84b2aa7ea093b57dbb12b53b4865c310bc67fa80f75adc647894763b9639c280652608a1e5e35e467cd86d590e7358cb9c2f143580af6659e0803bb10aabf9f91ba74b73af4917c3b3551ea6fca084404a0247b12610b8729d92b07aec348db78b00a04a96fbe72e47206c2884b6024f5aac9effc0508e6fa91c48d9d004690184202ab72228d4bbb3e256c54c21f1a8da119b4183b422e268af373a9e011037ec56dfe239b12e181b39b4fc7a8493373fb68f6e9a5748ee8df4c5784587f6ecc84bb5bf31c3a7be367687e93060f22e18222d22e69c7240d3b8e1e80fd7f9a92c1a49b9552624a2091fedd614aff7af6ad02de3aba81cf088bc3e2e56cb48f60ea4abb78c848020cea4ad6cda683922a5e4b4428978bb11e11412e852670a1bb6f5a6b4a6a8ca01b0868cda4a1209f2207116073ae7aff78a1f7c1c92a804082c3723a1ba94fd1357093d742ef458490deb2a02705fdf58977f9201a8cacab9cfc5a40892952266229620e8fb671749468d3509d084e72b39ae33e3e97fa7044e500286367603345ab0613c30183821143fa546c67c98d75c95731f6e02917c62ec9bc93885721e96ca2b0b3e684c3328b6e3743c14e6396f1c598591760c886c978ff410b4f8319ee52b26711a950848bc8cc6b2485a5dd6db421baf58cf409e2d93064056e1d3dc35b89b037f9be5660293e88fa5183430c16d967f809b421e183985842769d0e9ff6e8d26724f009aba063b57157cdc82e57a42d99ac1e423283e7c81e7d983622acc34cdd9a315c6b889e465684b2cc9fa48c8cda646abbc1c563e1de6f2b72e3ee57e56393a627768c945f1a6162f29122ae3560d26f10bfa721760c1e8641f1c0b42ca7ae6ed04d600dd9b9889661e10e9be6e0fde9be0ac4942aa60d80e5a47f5fb04b1ac61305360c73eed7953e54f39106e564886f6e0ce9682ab224cda9a302047b83bbdd9205b7eec0ae5f2e8ccffa5cc5422b69449408da3870fb81ff94564239de6df6fee3e9ca05c4cfee87ecf9655c4dcf8c8036ed32157be99281f0374e71d2dfd6fe2cd62ebc6762a30a8ed697727ee0818e1672ca59d077a8980ecd6df160de88b92701f36165634aa18986c63f5191b83d2309602038386f3325e92028d760003d318688f0dbbe78d7d1f1c91a02ee8670af2d20cc3711c14acd75acd641f7d6a37e217784ba4ef31a8d185b6c6c16169b54b7e6504d76151a980a69af9ae183a1e83f47f1e0a2ac5debc23673a266360851407d2545002f7555e4e340015d5eabf8ec519be47c7bae9d3094f17d1648b891c08f832b251671c667584c794852a588a4989f6e703924f4eeaa871361228458832ad1aebdc821834cc213b5a751f95936a78051294e922d4a9f5018df50e3989982581a2053eb9457a9b40ad525cb79566e2ce91e8c4c00595aa473f5969b7b407ec27dbdd73e449a64c23e2142750df5affa950f1782f451d390e7499281146f6689699f099cdb7e5faaa5728962d6649b28beac8466067e91403878772869a788ce28f560d5f187b321ae653507c0e6044c3b8678d093ad775cc1c30f8cdd772b1c4d6f3e3423cb6d1793c8c4e0f00c7bb15c09d31104f8424bfc4196f4ab2a5113e93cacfd01521fc0bd5c228927df9806442cc205c380d864379b7ecb3bb4d77a61edcb84d85c745540c676c56ed57cf21f5775066014a08a5d9d7f234be218b6d7ee017145e32fe685a161a214ca4631e28a6067f3549217171c48506c8dee31909e8e54ab7789e4e8827d50593f3916b9e23b766c65295b0e503914e7a185f05cd3682d61a7b1869cbdbcc056206cd87037490e2db8cc3fdf64ff90113e1d9a16ce503c21b27008407266a143feaa32193be781b043762e2cab94e487016c2506752fe9ee745d0867321fdbde50c0f56dc9a0ce4d49c18f34c98770ab8c06a541945327576bd12ed3663bc36af068c6c8d48f71eec939d7f6e9ab1b8a308e642826d964ae5d27abbfb93758118851213de837e0e36b296138a101a804b2940b05e1251edbb5165216c766f550b915d988b1e6f21c107d8f46d8d7c055740f60d7f463b3e9bfe0b197e89a339e0114e698cbfb1f3a9545f1d801c6374e292c12fa55a31c03e94855078134817c4bd90332f52fd198d08abd46a82b4428d9a7cb4ca244043129f601b8ee896836f9445c565255624a77361e268937cd7a6022e0bbcb4caff85ad8a82327e4503bd368ccacb46b92a2a97ecc06764934b435318cee234ae39d256b5e9039ac8890e1c20c33dfd3a5dbd80aa5b81b01af2c8a17b8a60e7a0d2ba007f0b4c35c946738918cae775183b36b542649f083a563491d092224afbc2589eaf88c745f2e91c9842012ba917024798e4496961868721f099e2a51754e1abb880faa74930c7139a68c10723c6daba71e005982442da755f2506478bce35f5a90e4e54ccf7b2ceab18be831a986cdf1f6e8b1bd07a17c50c7434e7a0ef299f790d36d1227c49364058bef719c42063aa5ab87de3c58f5c0e5418ec7fd3d74f570c6e3d8fdb11de7b83a6054102f1ecceb8326430a30e47b0339fd262dbe1e0d3989ba3cc64dc4e3743d40aa6b87420709b8c06ac89ddb2851730bd6b466182f06e1803c1c5cadf4568bb183acf24580c28c7d04a95b78e99d3233670071a5e248b1caa70e5addaff92e3b3f3a24234034dfe19525669361f544ebb0e71362a03594d2ddf0d5c58ce966d46d462f5c50deb074d9adcb6ed0713a2a061a9227768616a06e1b79cf30114677858eac01f172056b70eae2dbf1b6f3a128c2658719c344a3354192642d6596ec8ac2495219e08815f5c929b83bab853fb942f04e6e19464fa154213c4be41ae2e0b3fdb3b8df4e2202cca11a605360ea20289106ae11080c340ce2bef8a9b8030ba624fcddbc92bf03ee1264193a41a98b07b90e30c2089a37ca68a20fc31431b316cc35c02261f7a3a14921530b5dfdfec568e5e66155e177b15ca4647aaca803d8739417546000472eb8914ccb559d80d71899f27e83f8891d5a51b4f89e0e8e46056880fb4aa482585cee629f764de49c9ac013e98ac3734528aace492ea7d255317260d100bff2912b329b284b7da01330e6263d76feee41fecedcf21408614b55ba770ee4513971ace73f7adcbbfe89c5af5cfeb180e33a328a1b16279b8bf9a815f154f0d8420ba6b057c93783fae1778cd40d221751fb50b1c41e448b5f405d03006a80b9016010519725d5442aef34c3cd77654f31e9aa32d6c16e938d9a7ff95c73af321feca56dca8095cc6784b28ab529c7bbf24859f9bd4ae1798a86df17111056d4296c124fa60156c2a652657503a9ff492b5866a704931450a17566418f4bdf104714f298a5c2d23a89814476a99a7449da20f17c22880225a1263194e649aa94ac4a7b12fe44ca73ac901883a4c89da65de1a1d8dff4974f3396424dbf4df58f64150e2a6cdd63b11a572aa2a73fecbbd066587afe742d7d5470eb972b06d4fc4fd7ae70a0f86e0194403aa1a71e6f12cbca82d4d51666131f5f05291b0f6aa5c5ac890c8afa27a85d056c390fac084205789c8c4a3656fb360e0dac166a915d948884542aa91851cc9af497be02395427b2427a3cc90e85fa8ffb301a95a77e50e95f0e64c9c4cb94f4ab3f4e716ba82a7581f7803f682d0146013894520fcf074b823cd2ac0eaffb8a55be934cb814e079864a0b411578e5a98efcb81c062ded932885396782a18b8cd0c429985dcc1b6ef2ff02172ac4c8758d6fa8dfddc7cb9c387508417f58c633730f5d25f03288c177f0cad94c8bfc0b6c31fcd910e610cec69f40f3574c7ae61dd11daed9cc48ccb8bc39ca8f3840173b40586be07f9b99b2f11b14f4a1c030f28634997f0c666a7c08b7475c406ffc708430d43d12722fd0e76440fdc1fb1d2e3db170e445ae08da3a398240d404c279876257285a99469448d4514863280a2587a25939d437486d9a2ae8d10ab06a277e3bb7b2582dec850e811846254b4995b62a4491be3f036accbe57c6afc60ca607e66d019917fc315866314e8b11e759d471f5687ad2ea5f5ae46513f79ac0697ae20e8dac531dc126131d82e36d4812ed0b1e526ff038ef0cbe33d5ac8967279494322077ba9df19802bdd80c9f22bf875c38a674516d91f5c464d75617e46fb4d37943afc799471489bf896ebcb09c27475416e8fcf697a7d57a04405825495d364ceb94a1acad6003269448b683a30f7ece280759c54a780325fc9038a82193090bd7702df524cb03b121baf6193299938e341f6f103c0a1038a5dd994643e0c84504d57760c46d5680b2a46176309f312310d162dd558cf0588d2475c458413cf0ab5bec01b2038ed18eb64579598d3131d8f2b16cc70838f4d1d3748c407ecdc3500b0e21cf4ec73ca7c77e610728098cc804d97795d2da905be09a1781f33ae1eafbda39ec337d967f101335447e941c0a40225743cb03d698ec6c9735ee55e4ccd9b50e86f577b096b31fc92a1da2f6fcf329a5cf1c77f62135c2cc69e71b3f7bb2f0553fb1e39c9d7dc399f29a0141a4337f4ad05e5fe1fc70487746ccf9ad6a9a1401732de0699315192fcf3b22e4c28d8a1a076078e2c8048792a05efd7a9286c7dd01d6818f9dc8dc2f326e065e288b48382cbdf5958dbffe7a69b4ba10c9da59c30eee5be2899dc46e16285cd66a882f31c82ad9d68560b4de412abe808618e6c09848dd9315f2a4bf305c0caa1801bd9af4d0555daec46ec0fe0914cc9e97d6d1fe482dee9483e721f2ed98a8663c00a896d4f48fa1fe385777eef90dd94a92a8aea8959a2d8d625f218cc36518e530a28610c0b8b36739f2d5c3902fc76f369f436a809d24f76b434eb04b64f11b1f9b58a549b962f5c5079ccb7307d47e4e29d5823feea5284802fa1c9f6dac5b8034f611703263a80dfdd16d6b3f2708037edcb18906697b6d6c4419c23e5c325cd7e0d6d85924370bfa3aa1f0ba2d7c2ed4f4e268a74ead5401e9007a3b6d541af48dda45da5f311ebfb84fe36d170079e671caae6de67e3d3c3a9a12f639f35e3a1ab573a71e2f381da5a3d070eb402a51a30c94f35de10cde1aae56d792e79f4864d44cc87a1200101f506805896f05ee80b5894992f1df3e9a244aa9690bb37e249a8bf61925f86d48751dfa08859f946a2a434b62b247542b31c9ee79c8997107ba070ffc448d4c75fb654263d5c1c5d48bea7ba98dfa0b8894abdaf81119b9a81c54bb90801aadb348d07a4254de36c7036c843a11fb3debd84e8b1f7ef0a4898a59317fc7190a22f82efed3ff97f7b9d78af6253dc172e902e6faf9aa0bb99c0449130ba1ba907cdf74d7403cd5941e0c621c14e62c54e95ebe488ea7ef7501ae0832e5f1b96054f3d4e913e4bc538186662eda8a09028acf3aaae205cb2d3f6afeaf71312974b630f01122e8b4bce79b6c46947613ce93e869fe33d87f72440a03645c70b86ea46fa1084f785a44c88f173da83bb4fece5d341e2f9a3a7ff71058f13508efb1ddc1eb9477ecb2d7d9b0a3a501b776fab7f62481626b1af45b4f6362d20e6827a80157d96d1cdb038a61064727d7ee8895db29b008aae6d83447ab2539ae02111d0b1089e8002f54a1a752a79ed606aacf40a08b885054c9edc592ad3aa0ce857dc48f7019992d604754d53c403b1e5afb2428161790b712f63c4f9b54d3e25c02a9c11d3e46dc614d63dc8b830f1f071b929e18d706f06142308ec6aab9b0862146605546269c31594ef92d1eff5ad258d210d92b7deb1c066540f7330fdc12f2bb8c9094d7f8870487147d96f10c901d4baf7e902e1d4d7603b8fb5e9dc228af3e73ddbe0f25650554d2a73aa2f3042b6a5a10f37cf321babeb46f4a68738bd7c17c78cfd67d6df08565842f1d4b7851b8a94403f8cf02a1554f21331bb92df5a2f8cf85e57e11b28a527b87994fb77e31d13d8721d2e5eeed34d77bf2dedc2e4dfdf74f99ea53b30a968edd04c735313dbf7f953ee44375b792f6fe24a860d13e5063bf10ae20a1324f823461c80daf9e56936c7b47a21f9ac0625de469feaca073d0a58cf419178c70ab5ae866867ae03f215024f9c22dc7e7220a56b91aabaa46848b9d9115c1135cec0084819b24b669fe633857e0501497166b8391e0321c0de4bd58b8e8660d179f7a2b69b1b7d2dcc8c0a93aa200fa08c1e9a94437cae0547f426c3f40bc86be1b72f5f42b603d628b95d39ec2a01e1af02009caee4f78fa4c8b68c13418bc216bdea02fefa41d740c19baf3087b96f79237edd2c2824a01a1c5b8715a2c9dd4b46a7e960c0194ff29d323f8fa63fcbe963084f480af51123a78a54b5f63c1fdc03722f804fd349fe3fae93ffd69985b5b0e6d8d24fc6342d927f67fda830f4d8211aa7546ec649be996c5f7c65608104a5bda31c2eb3cc49b083c66454e40e0794fc788cdf1445cae2ad717aa865f893cdcf0e1c3750d0876c2d00ceb64feee7e628a3cb23c4bc68823a103a6e71a0eeb49bd37be4e27b4d8cace7cfaadbec30e4bcfb4e5f14e63c77aa890a80d31b443bdfdf0cb22fbdb2dfa6be06d64c705b5f0c48bd713147d34a6de0d6029097efe6abdddbc86ef410157dc5d209658d9fbfc7ea5c8815603013a82e0f4a05669f6de31c03859758447b28b846d83952e9c1aface06fd0767886f5246ec7705bb0b93b140673c963e2d95cdda911ea4f46845b56c143488df76c4bbb89329521831e49d450143efc52b94625e0aa96ddd7f4a43c88616a66be8cc0a870d6f5eff9b27e95e0a00bbb212d15f6f566f2b8a4a676cd3bdb6a10cd7c04e069b262cd0bd8a69b5d08ba11e5857990485144b0f8f1f806735d900278e94ca363b0e9264140ad177230cc8a185cf62a457cc9bfe9820cd75a8e855c2dec5e3c2b567e0087d71e830e21f5ae65b7496e53c5cb06cc311f4a20c09b2771384159f34e603d6634913f15f898febba299d84988730b2bfd004d2c1de574a8bcb359cad78422b19ba5eea528095fefcfb99aeb93daf598b0f22111ff58e72ce5919652c0335e838142554833c2ff3a8c85241f5f6de7f90e37a04d6de211062e5a856c6734009bda6fc67bb01d09db3582a2517233cfc15a7fd749c33dbd6c863ef7e051368f7787fc087c15e4422231a9c1c3441925d5936c62563dd64c1c92b3614bcfec534cddefa5e5966213559ec7ce9b2c81eccf6f7b4c58d49a84272f59e361e4b4d24b15307addac65d879e25df2a95b1b7db975ad5dfeca0dbc9f9b46ac74c2bec643dae93e51a3fbf1975a40def506ad397898e0b92a72a8e55af5b236d5e4247dadefc9e3668659a8e2490e9a1c62f70d8d10c28207518126ee4aa15c93dffa83519a39d0d02a99fcccdcb538af3c8c37ad9556a403649b0ff924e657798167423904f68b298fc7d64c342e6626683738e4a64f512eb86fb1b9a29ba1827d250d43217142aba17962664b8396c4a0376e3e44fd984a10151ea5dde63dd8bc5c95bf11b2dcb8177ab3065bf8ce270ff9fe11f56fc55e11c0a3a269241b2d950b99d4dcf8f50a307618c3c1922dd67ea2c6451a272a7b28f006c9ff6328725624d7817c0873c411a18b0d87bea27ed7663438b38ccec4b087b11b3f74a96b8d8a236b367bf590b8f22ad3ddecff1e9d44c281220346ea2f0e25e0796343697f14b5598b0096781d873d3c438ed49f7b481a7853746141055997a5e24b41f1926914a1463640a5a4d9c57905135d49afd341cace552a740622cb2a82f84a8355f3d154ca930014f1f63f8aba8809e8225251883a53ad52f2ad98423801f3e58f12da67ab2f80c5c600aec6123aa9359c033e4677ac11e0ac3e379a76e80fd930c2895a10b82f5106f75b31ce035db43925b5ea2ac68078006d6e33918f09752a75323820187d0603653ab211c53a4b2e5f96e44af8ecbe657cbb49637a922e2a37f57b1c01caf42ef2bbd5999323639bc786ecc2b80384bdb0bdb4d5fa2f272cb5bdba4121aa94ad6d2af4ae48edb55599731c79a467daa8d89c4996018833bec627a05fb6e9e2576a7351065e84b0c1310698dfb18266890b89815c798362baada62911422a310c11fe0a5f2cd7b5b931b05f88f6a9dcc028e5743c92093103ec2d21851d761d6aa0ba3839a10833c14875fb5085db2d7f8b7d552b7dd5b99b8c3dd101bb72f0d35fa451f3856b08425c7c637f9661a5265f6b577ebf59a6075b4020b0d2e10161580c39aad306d083860d8d53825815d0254ca455ab7364908ce9e1b50e63098ef0803ab032650d8e198d8fc53e93a1a5a5fe1fc4c46b6ed1442650d952f5ee5aa8ba937ec45858d55ae1ceff44ea351c9919516ac0a5dadb20d2abaf60764bb8633a292558fbc46e9119676ddeadd256e478e0214688145094d41792de5d8cb4153cbb36ffe70c2a06daa4a840bcdff51d55462a5b5bd961339ad82ae368a25ac6cc54bc146dfbb44aa408a2de3e5b837cf688a283b21751b0fb15f4a6d6e1d3148ace6876ec82e34b5baf19920c5f521970131cbd9284dc0ef080b9449b9410706649ec5a6c73a87a3b4a3628ccc8d23ba346612af38bfb5cb1fdc47ec8f43859beb9bebd742fa1ac0faf79552e47c0a3ec7a3773da1a1f43362d7ef59895fd9accaab5116f346e3d0d98c0afbd0297c96100e086888cc0ef9fa31711bc59629e4f2317af42357bf97d5b500f530738871bbcfc130713ac1bbcb43f4dce650e05e0ee6419a8b3227804e106dd388262a5eb1f101dedfc08e36719f0d941b9f64924ff40f63d834b0db8d16ccdf833577246a38ffcf0eccb337f4875a4eb819314f77783ee8a8e23da71c927a6386c9d064539c3261a163c527bd2522993d9e4a365cb3f8b7fa64696f7ab1242c4d4f1ec14f595e8597d134841a362cf8e04fc6def6fee0ae626fb44eb399ee45ea6532ce896211a0a1faed716531dbcde3c98c332785a3946cb39705bca5a962f862395d5839694b82d5c19f814cb6abc15bfc3a91bfba51b80daed8d57f48c2af2cae346032a1e5ba000c555c86ab446cbc3c343c30b0ef8b4c953f8cdc5718374ea74b9b62c5fd0e017fd23c1b23c531aff13ff2f90750e8dbe2cc8fbfa71073520f0c175bb2a5a194e46127d9f359e4aa4b973ec64c4486aee98c96cb5e3808e4078c8674dc5001501e92acd3ebd7921eed3907ec5d01a62b3e916d440a3f582a1db28a068d87bda80a29f2ba147512755bf706e0e80c9b4a421bb91b6146fba20100debd239486a15096446c15a7f004c57db9a7dc83c296148ca24a2300bbd96473a6f07f9f3c6998879f7db18030bb6c7119a66a0d10d016f0e5b182c10894b91343598052bb665e9f22b84f990d5c1a619a8b895df11c15c6dfd51b099f95edd47d87a46d26ad350a4506637f146a8ade715003aa86271cf016af97b1ab0a27ee3b8b533beb1e6d33ddb21aa377d80ca536d3cad13de0c9db42fa8add3ea1846e0767c220165827ce3722149a0596e7a8abeb66d3c607ccb093841ac836d86766f14ddb1a5f39b139a558cc6923ede80044856ae413746209b78d92f3d188449c61563358c4e08499f1e89c1f8f2dc9d8a8d0cb792e20ac3b331327a97ce4435adc93d83c35b5199f3115217eff80f7efa60d0b99c6974195a730cc1611482c741643fcc24f02cb8af1deba893da11c099e40b0f8421e47f4c1fb018752621b93855f7881fdc1d494524c4c85d4ca6d052deb526380732bc495b9f5a971689866f69085a1e215b728538f668c5c334a35fa498ad0a560a99bed1f7faba6bc2ef60a1006687e34dc527a6dcf96a494f12fa2b31d061bb39525f22a68e771ae06c5af3d982a25edbb6bb5c71c5eb6480a830d2665f5661e81df31091f0b5db55fb7cd88388983499b7c6fea22861aec29b349a2960014778bff94a95b55928f66b360fd1769e09a2462c18c9fd270aac8e0738c3343705bc4ab9ddff16243707b9415a4120209f4fb485835e1c32a34ffc214622839761c6b31269e2d45274df063ea2ad8d946c634552dab13f6bec0e1fff4b026aced9a32b9fd975302db979cb391e225e138d6919cd67ee4934cb28736068e54d0aecd975cc440e5409b2f825f66ac87a9d42603585a186648e2f4f9918c8c5e703ccf636ae0686d1827a23055ba6fb5b73c36d2196ec3d16298d1884b433740d3646895c25a7ea4a9577c835dca19d88e4c453c4bd5306ccc794c89ca2a09cb84c23a23d0f6cc308dde34471c9fa018b39e41ceeb65133aa5a604a6af97c683676442419cdc961d3001ae2816f27fb4aeb9b5264d50039472fbe168d60c241b4634b34f3c2bf9195e5925009d34968c9d8558d9651c1c9aa0b1e5fa342eb6ba75b39112af205ebc115908c07b1cd88b82c08ddc5ef4c005122f47824de923ebbc7cd6abbb7570e1144a2a0e1928ca74d12c0f0f13b8fc21f6e3468fa7a9ceb7eca5f067abbd38f7cd1521ba6c83e7f6d6209dc46d6a8ad08615d17cfdb572d48f18160c9fade36a3506d31c9d3ab4702040471235d49e18d3f612f5fdd9cd036cc302fe59c4170b3460b25472ff000000000000000000c098d5946d51a5e9a624e402a80f858057a4ac8848b9093778cd3b7807efe01dbc832d460504390bf90a3c0b495ec7f13c5b42a25a30a71695c833bdfa51b360f41cd747be8ec3b35549002c18724a0cef9f90b38b7a05d3549af81244b58239cb05f578ede8efa982b14b522c7a5430b95f9e488ed76c3b5330a7648f24d9aeea722998a287a9193a12c78e82e9543aec473514ccedf9224dad85b2399f60debde853edb71b0f2718d3d3ca871dd6cf2fd90453f214a472c8293acd67692000138c6f919ff3e5079225b90453c7e77b93efbaf65782297f3a5965497b590a19418024183d8d5b78fa4830c7af152ec4fd0886d02a9f23cf8d60b0c88c96969036ef22983aa40e62caa58c9508e6f8ea6285b6698f780886100f9f24729c63f8ee000208c1a85f612e58dd447804c1a4399a123a403056f037c966d9425e7e60f2b07591a51adfaf0fcc217fba551792ff1e18b743c57f492edd260fcc3979ff63497660bc943c694142001d183e08f92484656d8a8400393085641f677abaf4142502e0c0a8bebb691621c00d4cae13ceee3d8a2f1721800d8c1d7f4708d767953eb5305da41016232f9d7d68619addaac91ea2d4f3b3c02d6461f02fd3b5900e46f2b13079dada894a291da4616172efaec9eee4b3f32bcc41df9e89a956ea0e5798e62cd9b4f5e5945b61d4d2cb7233d2972cac3077f2dcaa93ff0eb2ab30569f57fcfbf03c5215269b20293edde5eb3415e6387e5eb508d1b30515a68e72a8321f75ca71790a73c4fabbfcf9be9b98c2101e27f6f57176bb1406dd8e3e963c29ccd9638e887ed816a330470e3bda3c148549a33de7ae2f14a60b2613452e54c50d14c6d0ccb4b4147e8220c92677afef09e3c95c149b79f9d077c2e01e3e549670412bce0973b8fc2872ed5dc7de84e1b5ccf3634bdf7f68c294531fee5b454ddf4c18afb365c45131616c4f217a4729472fc9254cdef15896a8a3250c1f47ea5d1e2b610a36e99235d299659430ba755c792efac53e0943a4f0f92f9b8fb77492305aad870ee3e27e942e1286fe487b152f64b51f128acc64e9f4f9230c57b136dc7e3b3c8e3045e409ee7f37c2e8d166c8990723cc9d93dc7fc81d7d14da2dc214e6267faa993c63518439ba4ca4368b12d126c250fda15ff8e41de724224c71aaaa1a512bfa4398ece4d4e388863087b81c5369d25b14c22cb1d472cb5c7cca11c2e0a66f314e3b47e5d9208cbf9683a9becb6f9d09c228365f29baa3e8c03f65d8221086b1886f926b9d3f00616abdbda9911c86f90773aeccfcc9c1c5f1fc600ee257b454d607835cbb445dae9e330992cac4167c30f707cf8b1f1d6e93edc11c29e9a9ee63f2e47a3047e1953be7fb5fdb7930670fb6a39c113f79a7dbb0051e4c1fd7fa546424eb90c28504f4015bdcc160214e9944db0ee61042ca952ccb52751dcc66af7dd1b3d773a68361f5c38e3dc77330476fa313cfe5fee3908321c7fb78a392844e471ccc1d7b050ec6ba0b36361dead2bec158592a1f7f965b59dd60ca3904cb716cae69d8065399b47c7b8e193a1b8ce9336e5241fa33aa3518f7f4762b470d46cbff1c44928c3cf93418e4aa24564a231accf1f6747cf73398ca3fb263c5f3a86306739c3d2c4931bdc316653054e538e510f1f66793c16ce11f2b4cbac8378fc164391ecbe3436e3b6705b785184c216946927784c114d5ba237e98bfbe6c74d8020c0693c8aa74f70be6ec39ad7e687ac1f051ae52f3bb60705dc921b992336cc105aa2db660b4f071143a9248f9205a30c7510af1382f0be6fad877e663c114428fa78997cf17bb82d1caf36c07e9e85159618b2a18a524dde78a4d0f2a98627f9ef7587e1042f482a30b1a35c2e0a2055b5d1c615c5d608b29983a2d89fea7764f5e19cbc0165230e9770ec43feb778790ffa2020358c000beb07183015e5460000b18000464c89021e3bb602e4ce0c50a4e148cf3293cc4e6a582b7a40b5b40c1e061648b1dfe22ed07edb4b0c51362b08513cc71b494e9f1b209a68faaffd22bbe8794230c5b30c1641176eb83fae86205c7799fc2164b30a5d78966569f27156f610b2518d5264775093d09c7122e1d5fcecff3c15fd8020906b7dce61237fe2687d4d0a2f161ace08be783778b23e0164668f5d22fece5300b5b146171cb213ee272d406a0010710620b2298c6bf83b5101de70e72c2f0e2c300830b30bcb0d15e4020095b0c810f72847f095b08c1d8b29f3a4831e27b8e83c2164130845c28eb91ec41ee3811b6008241f4a3c71e22a8876b61c16317071729b0e1c50bbab06181ff06ac81d1802d7e90cf4618f71ce56ce103532e6b29bbc9d9256e41d8a207e68deb20625677fad82e40d88207468fe3f073d4d212f5293dd86207e6c8a3f0dcf519bb1cfb600b1df456793d871a72da29ed850d2f2a115be4e08ca83877b7c0c1163cbc8bebff28de4e6c71833b3dcf637bedeb0edec206a87cac4b926389752d901c99e67b103f8d69719a6019b29f6265167f7d9aef70ac7d95851e8844980db7e9b762d1c71bfee993443a0a2ceef99734dd9b2ed9739688f18a27c97c36afc8514dc8ce10c31584b41b31625cc444a7156787a8b86e072be838fe0829fce4f0744118b90a2a66b4f5aaa788aaf073f6b493c8b9e0a5025f978e5b2dbe24c941c5371f7a36a5f724e753289652557c5d7d1453bc17c97d3bb090214629f6f9702a3b342c5d52d0da7160f9be448c5190b324044b9ba81ea2289ac6c6e408c5f17e781d821030c1a18030c0e0a20b2c33c4004591cfa3f0f2b9fe937b8f88f1892c566ee5d4a3b36b952186275ebffc09ee91952648c4e844151e3b45ca3e5e2d12313841d6ddd9fb78e6e64d3c6baf9ebe42469489261ab5949649f51f57993032b363ff52f7ac9848774cc4729c4b98f7a6f4e3b4d812ead427ab88ee215e09a2c40b1f84e5a051a48841893b6e7a0a9e1ed60e22c6241af9b8d24935e4639504af1bf7a972d26d2691306e5df2e0eb3a9d4ba79f05a586189038942cc95348d80d1ff1e55cefde71a84b9f23f81c215de769a68b84188d38c7510e92e3b0ae3a8c60c205e9e0928a5b16615a671b97a022cca84a9de291dd4d2776d81e27c2e01dedc30f7e1271af14d8b8d1802ec44084213d8ea27df2bab00f740f428c4398f2c7f1357e62734c8c610872b023fec1c6c506310a61ac7871ba3b96da44b7c42084714e3eec9ed864cd8e1c84c93b274f79278f87d986114310669b8e363c47a417b704c230d926c16ec2b6e3d82e6cfc0d05445b29f97ecc3e738f72c4f88329fe6d3f0832f77f0186175c5420861fc8113fe6e2cb27461f4cb93d2877a9b9460c3e9c9d7371ab3d18267cc4978f9b1eac9f5ced0862e4c1349f7dc26989a758a38fa6c28339be465eec2cbd1c79773069a5f839a4761cf964566807b3f4f5a4d78e915120461dccb11fd54e7d48fa1a113a982ee8747ccad17a2c360793dc67c9dda15fd72487d6639bec0d1b5c601ccc95f4bc3d7cc7f695b5ba06c48083313652848c77f059d53718ff434f0e19953b4e5edc5841187f83861787460ac0385a10868d30ee1410c30d265bf370f696b315724c10a30da6ecd0a1ffec44ca4b712ffe461e5f64200c2e1480e6c5f105180f88c1064ff27ed9487684d30b2e6c58e00531d6f0d4bcbf7f2426710584918206c0e0052d70410c3598badfd7239329ab06a00107a4c1143472ce92474583a963d9b4bc547c7818e30c268dcbf6ee2fa0b9e8028c23410c339893b647e99f7797634f8c32940c03c420837962533fa7384b5b7d8c3198c385d4b18cd4623056eac0febdcb72f47261408c30d093e9361d421b030c6c94dcfd765fe85eaca1953588e1858dd105e3c21dc45c4e498bb185e3f373b1ed17701e03189021a36f68e16264c160fde551e7f1d6a5b586ee186260c134ed39525f89edaf1f85ae60ca418eccc3f48bfcfa5ac16254c11c4fde8f2a9dd3c85a16f05cdcd00052c114a62c59aa8fe4a5a618534024f476cc0a6693d730b848410c29ec9ef2256fbfcf3025b08209288001cfc50d0d6c1519c48882c78082d13edeeecb6515cc3a0610e7cb608897f3e3a43fd1bf4d3298e473ccf3ecf4bda96e30630ca693ce71d8b1b23b454ac1c10c319863fb4e57be130693dda889bb9de4e83cc060ae8abeab1c962f184c42655f4bd2a1730e2f1827769e4e1d077b92ad0d6674c17c2176f46817463f5433b860b2ea388c8ad0b7602c8fba1c68840833b460d6daaef22887ecd1e5c88229afccddd97eaef02158300769776aaaae17227805937a88a567f3cdb082219ee5884f7760216654c19436a6f2c707b9aa43501033a8602c3df5fe701f16a2650ac6a81017eefd713c340331430a861c4908fd81fd8c87c46146144cd9b36cf7e3efeb08ea0c28982accc22e5659434b04339e6014c9eaf97a632b29aea1559c60b41c767071cbefc3f5c68c2618527e0ff593759634da173398600e2687b4e913cb9f820777d1c571689413cc5882692d745acb1e77dfe76628a164e88c24cc4082e1c4e378d34143af30e308e66a95991c9d52c5fb6a689133c308a64ab3fc9a2c7f12db456246114ce3b169f97f882f920168c0011648c40c2218e268e4c95b1f42891943305a0829428ad50fac3f5012338460eac0232c9d981104b34ca9c4cf396fe7396b68bda06968e5c9600610cc9683a073f9ef8184687f60160d4f7aa3e92266f8c0dc711ea767a58898d1034378cae1722c9e2266f0c010f9eda179f5efe7750786d8a1fe29c2de47617288193a30577f102547e5c050792721aabf14dcf80c63060ecc51d6b162223fcaf560ccb881052430c306469509e99cb35343cb8b159ca940462d4ce9fa514efde40964d0c254f1f427ea429d5e9d0719b3305c4e99d2792be23f3c2b2490210b73db76d2f934378bd31d64c4c2d86e6a973dc751062ccc1e751ccce484f515a6d8a8b614cd3ac8dec5d1ef0517295030c87085a1e38f53a86b4fe31d5b4365b4c268e6db593cf0682ec038d86a0b325861ae28cb13a9a372105e19ab304c7bca1d56a4d3889e1a5a302813830c55185654a2ae471397acd7d0f2028ca3c617990a737798292968a586162a0ce5b1488ea867b1d50f858c5318274d0e2b64fb386ab5a630fcc7c5f071cbec3856186494c228973eda8c78ea511d5298a3dcd94356728cc2aa924c14c6f86cf9539a381466adbb1cbeae0285c9c2c2e688bb8f6fc7274cd9574c5e27f67ef784f9db43cdff8fd20973acdaf53957faf452e184f13df2502d7f30056014199b30777496ad9bfe41fc9a307f0e1ef677c6781c2513a68febf34be5fbad2d268c1de63d88be189b1f4d43c6254cd2b9c2c37fbb9f485ac27c7d49bc3a142b619eed18e99e2dfa3da1842942d9641246cb297cdc58f49fed2461f8f01ee4345bb3795924cca925e5384b7efc512f4898d2e3bdac7bf3c82492f108c36ccd95ec24c311c6358fe53efc3052b0310b321a61b624611eb4fb549c560623d0f7d8a2accbb190b108b357f88b1f5829431146f150de632b09d5eec948846132247a0e15958188c7fe2b5bfa8628e310e6d097a2e7f6c4bba840d128c2022bb49061887daba3560bbdce4146210c15dfa2030fc35f321b1c309341085390bcfc2562b993e90419833079059f903fab166408c26cba71339f1e739d0ce399068db3c3808c4098aa269be9cf7fc3c6175f94ca0084d1e3e4b5f89e5b848c3f9883e71ce7ee2865f8c19083079523afb3eae2f0820b13dc3826b0d907c37f59b66462b9cd8364f0c1e47156e914c1a33812eec13021f5616dd352f0482eda059f32f460ce1dedcb85856d6dee850d2e4a7140461e8c2b1397923eca56d1b5ec800c3c183b8e139f3e5fe6e7957107e374f03bdbef90610743e545c4d0dc0b651a59838c3a98d327eb841c78b75a44061d4c2355da390efa111e2f630ec63889f46178c8c1fcb97f54b2c4f6d14c461c4c79b267ad905d527c30f60132e060941e97c871f294cd426a68e51bccd3bfb16e39e560a708c8708329e6bbeee4fd6883d13d5be8ed20df7e2e185cdcb0d1051f6890c106a37dc6bfa5bcebc108038c722e630da61c4773ff9185d8e5b00c3598fbe3dcc1c769aa6d2784a08c341856d75f3b9e6029a686a501098471e359e0858d94830c3498feee63fa2297869d32ce60f4e47174c9afa67d4086190c12c383e9e05306d36f7eca7e8456eec96088eb28a47d0a0b790ca61c7ab4c82945ed8718cce75bf51fa47b5d1d0653de55a77e3af71c07180cd142656544a473ec17cc162fce5f4aecc3d40be6c9f591e3b2bc6c66174c1fe48bf97ab960dc8e2d983ec77d66a1d4b4afa30543487e168c6921d3276d2c18c2756d34dfb37bf0150cf1b2fc87ed68f66305739214274ac7510553c497fb38b73ae40a15cc1693a6927bd27e85c89882f14c353f64f76548c11c2a313cae6c3ad2be8c2898c3ccec38be7b4f1ee46540c1b4feb199ac764b84308d4fc87882d142eca495ee9d602c8f733f0a93cefe34c16cff5fc9d633a2e7996076eff65c8f3dfbe3976016cd9576d9cb15b7128cbfb3df1e73124cfe765f219dc7e13112ccb9720e739623987debd6ec423a6a690473189b92c8412f8279cefcb244e84430dbd47cb4581d543c1f82a94d23467ecfc9552e04e3fd66c7e6f484fc04c1609d7b1ecae54fec4906104c395927e1237a94e3fcc07c2be75ec9373dd207e6c0030b15297a74ae07c64ab9f915ef1e4ece03a3a6c7dcce1d3b30e89da79b6cd181397adce99e6b0e0c2339f8f051c9c681612375246597e31e3730e5c90e39bda465d8c01c9d526bf5d6b530b6deb430f54795132d751ca7ccc2fcc17efc1f6a779847168620d172b695b68f8d8529c79125c9c18785713a07d1d4c3fc0a634d5e54995859f5b22bcca9444ba7cb554ce55618d4b7b4a77527c713c20ae3441fa930ead3b955984ebf42b8bd760f535598e67ce3dfc3e754692a0c9b3fb1b3127e69820a73f493c2edcf851c484e61f6fa28c63afad8595398d45a721c6cc71eb7580a435ca98a36968314e6b01f7b0e2dae5b5f3c0a93b8e7dc56cea230e7c4f5a449db71421c0a837a740b2efe923a0714e69bbefcdfc1774a399f305545e7b5e0f184296fb59484be9fed3b61ca619d5396b42c7a4e9883f9283ab116e6c33761900b1d94e78ff352ae0973f5a5081fd599305dce13df225a7a0ec684c9524c979cf541bde312a7e41f6efc7558c220294cbbb6b784eaa884e953983191303bc1a58439fa3821d557b2f49193307e1c050f2356766012499854c27dbe6b0f257c91306f64d317fd0ed276903067eb87f74d0b0f628f30f9c78f9453e60873e4e71f1e3cc73d698421fd8ea7ed1861f8f012b9f4e0aad522cc95d633f75184394a845cdb750f3e1d893044f30a71443a1061a895ce716ea99a528f0087304577e93c5e663bb721cc7a1d4d8f648530eb74e471ec10c21cff477aa62231da1c8429ee3aa7ef485cb71484a13d36b34a92c33d2410c68f3f4bc6a47d460410e64995bce7c33b0bf10fe68fdd27a63a3f98c4a2aae558a454b73e9872da4e93d3c60783841046ae2ddf47d983e183cd5ee8ae1056e9c1f069e2e84af0d419953c98bf5b357f7cc2e267098007a3a478dc9ffda3aa7f097007a3fe05f9683aebf32f01ec609ed7df93cb2221894a803a98e3f83b4ff623c1723a9824e4a0c2caabe54fcfc16839dfdbf6450ea6b493ef903fbabcc7c114c2876ca9cfeceb8583f1e6f306737c713fee94fe5210c00de670971869c27c7bda60d68ee711125b27ad6c6892c7f16b30eea73bf71ed5608ab157f1f3360d8629a9d21b0f0da68b1f7a8a0a57f1d59ec110b32b49b3bd623a66304c9876d49d9741698b6e913f9a0cc64af10ae21f6330ef79fa143dda90481183293d9a7d14be72a3120643fcb7e50bab97f2e2040083c1a2cd99d5a85f30eb7fb4913ef2a05356bd60f0b760a9afa3325bb50b468baefa134644ff542e98e2b493c4c484bf44b76030c7a9fa0f7d7a3a238928271852b228d1e379770a6e13cc816aec79e49fb8b017130cf79a16526a42c77d2dc1683152e33d0aea361e02885282a9d358ac685c8af1619504b3c54fa1ad563fce7b6ed4088354a290600a4bcb3d194b1e414ad411cc6312cbbaf2c974878d60482e31c2c3a5c5c81e24aa0886f08fba6db299021b371ae05e70d1001932445144307b2441c672c87c3ef3c3b0c1011932720d3504a36709be5ad17639ee1082a1c3e4ed6d7f8360f675bd12fd38dcf71c100cee16bf83f3fc39320901f503d3e6071f8778faddeb7d609290fa9f731c6b47f5f6c090b331eaee761e9d583c30e6a84769970517503b30770ee22d5d2a51bd1c30b848004aa074608ecae72ce7df0e5a5e0abc30011704059503f3fb9c8f4974f8a3e0082303c745e1c03036a96e2698e430923730979bdec48d89bc9f05eac0a83241d9c0d87942b05fcb41b530e7be47a9420853261e440b53ae7ae8796d274ca559184ffd52f2bc33c9c25cea21215dbcd309140b83a6a499186d6d2d2e8d4a5818625afa0edd59bf8264000b18c0a7e0a0400664c8a05179a057985344a7cfaef482a65fac0005366c48205d618e362b3cf4b9ed5ac8817ca0569892c47964565773d1215618faf3855f8eeb2dc2a25518b2ed8ec712f25a5a23551853427a0e453a7cdc1fa5c23c35b13ea99af31d8c0a43e518aa91737d053a85f9a2ba1090294cdf973e8c1ffd961442a5305e8ebfabfd425ee71091c2d8ae8551bca05198fc2a664d7e94272ac78dffc2868ac2a02e3988f4101a5028cc417b904d35cf25ec07288c1ee7b0e3e439ab5fa04f985dc3d34e46aebeb92817c813068bec38c6428e4e18b4d2ee59a4fad4d1e584f9726d8b88acb409e3498894238d10a2676e77481366c9d973301dbe3c98b286561860acc06998c00b2e962913c62f9d94b303111366b124c1f4e2ae4c90037409d34692d5df71310a640943b8df5ff197ea1c5d953007ffee6a69b289c4094409e34dfcb8e6b52c4d050a3409e348e8ff1c6325d5a3650b2409f3fc7cb48458d2c0c2801100588122618c9437fcdd2d5aa7bdea090409738a337fb14225956a35b41e618e5a5feb3e9adcc44e0ec81146fdd0c7a27585c9351100d8801a610e3d451bc9b08ee6631a66046284d147d7e5c277a998498b304ca476fefca02d8728a294621f5243cbca079408b36e4b0e3b5fb0778f22820e610ef395d4c4262d7c28990319c21ceeef513a8fa35e290b61c8ca37a939e998e12142182cca7fcae6e791ff85ca40833069697a34a16e3c965c10e688316a93e621761805c29cdf96fde3b1c89c0c10860eb33edecfc37a747f3086b4f86db4bf84d47e30a975fa997f5697bd0fa60faea7fb64c73db6f9600a12d3efc1902ec13df04897e2133d9872ca397aa98a06ca83295b4578c7d68ec3ed82075394f4f76d359f78f51d0c954bae26690763fcabfecde76ccfb93a98b5de2a8795a31f9bbc407430f4a67ce8a0567f953e680ea608c9252efa5776ae1c0cda35971ee9e3af0b691ccc56d9935b479df348100ee6fefda05ee9c63d9a107a03933c1ab5df8675416e30a49e903bfc28525c09a161c3c986406d307cfce8b6ca75bf27ca06b3d9e7f8ace7301d44346cf81a4c39da7e9ba80f4605e3c6db700502a9c13011ed738e4d2687d260b4ebd9b89cbe180f743f68d8f0fc01a1c11c244ba5bae7166e1713f8777106730e76627ab2bc10611b5d48c0cc60ee388a8ea3a4c8efde6b6871e1822350198c9f27a5cfe929ec420e190cf3d9e14348e81c51aed1a5031a8329f85a85646ece8aa40b2e50402406f3e4d8dcb2488ec2c8594369786183aa8b2e56d0806f2fe24061307668698f36b235b42e0066203018d4c6d5dea36df38fe378405f30b748f2d0ee5144f1ce8d378109b8b811068d17dc08a31479c11042d6e32323471ec25d30ec55e867ddfe40ab5317003210174c21782cad8b30357d16b405730c198f746dece2fd1a5a5cf8dff8c214415a307a6487ae59ececf474109485523e3377b6b354681c1b34ec8bc482e123c9514b4a99466890aa0b045dc1b86ee1e3fd783eacfc8281ac608a2a7926a4e3cb7f175405d387e838b63a3331f9dc0b1bac06a282d13f8f8b47f5dfb58b1b61348d230c30a660baa096b7d7c2bc4aa886d649a178d9e1cbe760fe2a325014cc6a1fb78510b7671f47503007a296e2788a316b2e3dc14916732368759815c80986b3e41d87b50f2c5013ccd164957ddc9fa8d09860ae8e52f547f7414b30ebdcc5ced1c7f9b7626509a404a3d487a6e79c94404930c72e91a3655c47785006909060ca694bbd13d743d38eba33d011cc79bf3bae94a84cb3c7404630c67edcc17d799bceaa052a8229fda744c831725db0b2404430c7d9a93c8e52a12198a45afa3c924908e6b379f77d15d749a930b8b8d1823b2550104c61facbdf2db7e3acb9e815f8df5019326c090404538eb36529e4844e4e3b81bc7174b10213fc0ddd0dd00fcc3943c2e46446a38b2f52c0c5150084403e304ebb78fcf214a807a68f429c091f6ca7902b0f4c531fcbe37c25146807a68b9be51d5d987460f0f06ec6b3ec3fb75505ca81f143c27b78ce1a957429100ecc9d3c4827b1f2a59205c6d1c5026ebc05cc403730b78edb459eca0364039376dccf66b1db3e592a30a316a67c9e2fe5e02cd56a575a182e564ef73895a7c9c92c8cd9fbbb1fa28b245716861cb95f08b52a9344b458cc8085c1542c54d49f35b48c05335ef1e7b826d4f2e46c292bc20c5718ecdb420ab9225e3c0bfe462b8cbf923fc48dc759697871b4b1c278f92e8574ade7729219ab30eef58aa87a96c7bfd5d0a25285c1a3e9f5ec462c15476f9f545f2a21cc4085217885a41e824ee50e5f14669c62bbf83cb9ef363a630d2d53182ad75a77b41f9ffb430312184029cc512686a9458ccfe10c529845dca3db25f507d7868d1ba3307bf47c59ec43a4d5ee4595cd108539ec346b160e85c1bbe683c4ec7b10a51a5af86633406156c9619b4d4acdf884a9e311c99e4c2cccf0c4a71663425be7555298d1098384ab0f0d30b8e084397fea385744985e98b109f3999c6c5f49bae86205c72a4b1356ecb2e8551e924c2a444aad942af8c7295dca16c83bccc884693aca431fbd08f760c26c2fd1ed2935bd5b42332e619a141d6787de29d4796a681d6186250c16d6732813afa18546985109836ca9a48a170b0fc11a5a288719943044b9ff0f23679330de87e19103fb54d2510dadac0168c0013264a8618624cc1e63245cea5c95cc120943e8e710cda3089e3b88026640c2143f2ef2c57a26c5f511a61c55b0bfccabf095bf812581198e307baa509fa7340db4e0c040590b8e1b0b98d18852089752ba53610623d80ee4425c3a4f98b1883edd7ddf7f080127cc50c493e2f1b2c28c44182e96e7b9a7e6e5c8e306035a70dc902183063310614a396e73ad83df4ed17208b394b9c5e8f03006330c610e3eb4fc98a89c2a7d210c73a9f54cebc37489cef15d9419cc2084396de551fb951af3388328277ff1f3b35910e6145f2b7ff4396a68398de28319813085ef935fd9c83b3d01611e4f7561ab2347a1e3300733fe60d8a8ec683d6de3627e30c7c588719f392122d20783c9eadc440a9583af35b4de6db48d05bcdba040a12ac00c3edcf28c3d9843da79cfab1c5a0e827a30054f09d172c78f486b461e0cff73d66198a586da08c30b08e0c11c7ab9c7710a1121b504c18c3b983aac9063a4d4396ebb1dcc992775923bb80e5e0757b12b39e8603e89d0d5aa36d9213b0753c4ea0b963de7725e0e46c9e192912c84c68c38183d7cfa38dcf6e46fde0206c0810158400203a00008686460061cca8c3798fd42429e74f7cf699ee1063e9a9310e771ed35a30da69d7f30122b2b8d54434b6f0f33d8f089743c9798f38c3518236de24fba5f0dc68ffab5721c466c879c06f37b58d57144d674d0a2c164a9e641f4d0d1c2a4669cc1e4e9f34dbcce7efdb90e6698c1905a1e2e74b8172d0120a071807382b12c072d7ab237000303b82698c623564c3a3a9ff698600af9e71f1dec4efde5805b42991255615d5119c9dbbce3546127b78405a70443c6e5c6e7b0def134b34b825923072124448f438239e46b4fb48a8f1ce772057704e3bcaa86df87a838df9f114c6d39753e5de98ae0a4d073edec99e088604a9d3f08398ebf9e7e43307e8a0e1d21797cec92aae08420830b021e10b8a87dee51f0f9cbb91f9c0f4cfb3b937268a16fa91b07ae07a6d8816ce48f3fce1ef9450a7a4b703cb0dbc1962a92a418512c976b250b8f326c3bba713a306a47071d98256d2d079703f36705f9f05f4bb23e6787037387be1f9d9287f33fdf0d8c7912fb4ebb0c9c0d8c23ebd69e729ec8b56b61faa99cb56bc13b6f4c0b731cc761b330fcf489e7b46ec995928521bbe791a90fcd144f2c4c9fd5a3840e75e4dc020b436ea5aef8f6d8a5a2dd81bdc29c6dfdbeebd269c485d0c05c61d6eb099522841edde800d60a63664627bbac91aa6c5618e23c22e5bebcaf925761caf1f75a5ec897af8a2acc514893d8908da4b7a6c218fe5b15af23adb7890a733cc13a1649eb173d4e61b22c1e8794831ced4789290c6f23e5b186570ad386c78f9472470a43f08e733ef73874906c14e6bf8eb3b2924267bd44618ed4ccdd72750e62b15018cc3df4f408f1dbe310280cc1839fff9eb97c3157fb04a135c3baa324494472899c3ebf965ccd85e609739cd37cc4b4f868a61bb04e98f672aa3e304f1fb1a3140ce384219f449aafcaf238961cb04d98bde5c366e7a22f304d982bfbeec743f9579ae4179609b3852c9dbd8e364c985385fcd7396f1d471959609730c7bb64affd7b8a3a530004343e6096306dde9e5f05914d9510ab84ed651a9153d77b09b8e098c0aea00118184015188c1226c99e7235f737d9e39330c7da2fcbaefea91c25e1cd6ce7a4146eec348b84d135c4e28e7fdafe09244c21b5e3f45ab7dca1f808b378ad4ebcf4b83c9d39c2903a8e6f3d56eed27fa55137ac11a5c42a8976b5c84821faa92aecff1a5a7b238c2f82608c30b4fea7c8adba08f36b5b49da6e4fb192354598363e70d7b33fad8852db1e91c262883007398aecaed39b1d2275f7f8f78cb40feb8019c2581f4b9af170b74298a3891eac84f60961a8547a1da77c1065d30ed3ff5210e6f5993699348d2c1f23b04018cc738ccfd1e58bddd20061cc0ffb336352f607338284c830f5a8685ad153fc0539fdb2201ee607d3a73c39ea14d751bf5304eb83c1abf42a27755c128c0fe610b43775532edfd22fb8a10125d81e0c314ed3d3e7363d18dea223847413b63c7930c771f08e53d6120f061befa87adab98339ec09e2416d4dea388e1d4c1eea3bc5ab5ddaaf83218667c5b5a8b9e1d2c1d075e9ff92763469cec114f282e59b7230ab6795b9749d2c483e0ea61c7b7c747e3898f73bc8219efe06f34b99e428a4b81b0c3af3575d6b1bcc96bdbeaa1e76cbc90643f7e4e5d4d3db9ef91a8ce11f7eaeab90ce8f1a0c761de4b2e0a7c1fc3a971dce8c06b2dae3fa8fd233983dc85fdb6a159f3683a93b4e39858edb72342983d9653c7d54e397354206a3d8a76a93e8205cd763304a67b58f546a311873b4e63d66a8661f0673071d42881983c13caedfe16a55b68bf117ccafa2baea29f682b1ad62ffc8d7e7a647170c97334423480717cce5361f32553d295fb6608e2c92e51f450ba6c859e452d75cc7a364c154712a2eca7d48b3162c98f543e59492761c4b7f0543ee74207a177175b782397deef0a32fc3e3bb0ac6ec0b9e6b96438ef652c1f4a2174f5ee2071fef149e1c5968a560a834c9c1b78e48e46814ccf13d4523522f99af5030a5b06a6d1ffa047359c717795e7de2552798c23c3a8f4f22dc45b509a6cf08b17ad483ed3d99608e0f3ae894f3c30e3d720926aff496e2944a305fa5d0e53f2955c59260ce8ef6ee672a645e8904d364558ffb117142fe0806add8d1bf7860ddb911cc9fa7fe238f9fe32c17c1389ffbfe2e4a04b4f2c4896c12320473e5b434322142305c0e35f6a3e7ff0a49104cbb13daf253e8ec39020473e039d28f7beec8e23f3025c929e4263ece15f681c1d259258d8f1e98673d55229e86eb0479600ed2577e9e77600a9fa32a0b93dde3203a308a45eb943f96186ae5c0649e52e7aea96d48880363bd8bb9fba7f58e7203b3ae87753d9b0dccfd777596db5e3fb416a6f9b579bb8c8c28d2c2ecee9de27c6e16a610e52bf1dac9c2b06d967e2b6eb130c7c1d7a68795e338cb060b835a87d87e49d29db5579873ac62ad26952cfeae30eb579470eb358fe75618ff4227b7cf3fe40f5698c76276fcfc1da4b9d42acc17d623ca3a946e5baa3069e59e728f71de51a5c2a0f78178bed78f2407158650e11d9227ed10fe294c717624477b9bc2104267911cc78ba71486ab2cd13bf592742185d923071b7927aa511845429a7417bc42a5578a50a23059e750bcbc52d84a9708150a83c4fab72e9d58698f100a14860b9715359f3bec8f1e17ea13a6f5be8f9d6763ff2558284f1842ff79ca4c3f0f33e913aa13e699bf949053bc48284e98e320d84c969011111111efeeeeeeeeccccccccccaaaaaaaa8aaaace652000434042085da8449e6a38f23f7f0e2f5ac09f357fead68f72a13c6da34f90e22294c98837f7c6449df13d425a86494256c355095a0c49611bae4bfdc23845e9370247ecc2d4306312a942488ae78fbc9f7f5b48a84214fd6b71cf8bcdee80509f3458afb615979ce71af47983a791ca69fd582b6a11f28479823ee6457dd32e97eff467aa01a61a8dabdf0b8157f35308a11c68e14b9d62e3f947817515a508a304bcabb99933d0e391acf82038cab0d54220caddbe7f1b5e595cbf1376c74894221c21cc874aadeffc5cff94318352582e4929ca30a1bc26ca9911a5a36b60a61300bd1e1e15f4556534258271f1d6f3508c35c4c7d3c99a8fbd1305660e353c0451882305c4a1e8d57dcae453710868a7349dda3f9322f6d84610508e3a465f5af10724821eb42fde120b247c5f59a3668786183c6172bf08329ca638bea1c7b20d15243c3b0718346e142f5c11c87ba2129dbc495a93485e283695ece72daf0d4b2ef1e68507a307bc8d9e396fb2077b8d6d0e2e28b83300f86bbf0d1274ba778305b8a8dd189f62aadeb0e76304bba8971ad9f37afd6d0052ca00e26d17f0b4b316662ba557430cb847a88a2bd39a1e161bc09146b0ea614febb428e3a1d6ea95a283998a62fe59c53cc8a83a9d43b2d73be2f48c161dd53f98e3d272172a8376cb5a19e53eec38b2a3718d3426acfd9fc2778a822a2da602eab10f2295267c448b1c13c29d7d6b51e6b309d79f4edb97d8bd8a9516a306507afd4ea1f576930544e578df7aa1c47586830d55aa85e54a786960dc726d419ccdd213f76b0cbcbf999c12c72ba5d162f3f54190cfa913a9fc5cd62646b0f450673241693a320f93caefa5063307a10ba42ee4f1d68878bc1dcd17739f6dc764ab61e2a0ca654c96ffe3fb6b4456030e4897596d3d65dd4174c398a0739555a49177997174c3293aafda23b74c7c6a1ba60d40eff39c3a307a13e3814178c1f39cdcdc5f528c75c300daa2d9826eeee5eb6490d2d3068e027c098505a30bb9956f66d85fc49b54065c114a5a562562776deb861c3ce505830c7b3547793763fe51cf7c286a3d515888f3eec1829446a68d108c3868d1b250c6505f3efa795f8713c899cf34ec386df16aa0aa6ad9024e639ff5592555430fce5c4acbb58a82998efd268e7ca55234a0aa6d7b58f26d6768fad8aa828182a2287243139f92d85110505b3e77e91ef684a26d74f301e8f34a8c47086226128128803c2380ac22034730f73130000000c1a91c742c158341c4bb2ea0314800457281c48382e14201c1c14221e8906a2502014088603816020000a0642a160901ccf731ed50053456744eecb71fdfe283c69334f00b71b1efc7be8fa4c361d6e4621451e02331d47eaef91c860c022b7775379f0d4087f37bc3a929f0691469de313f9773f095580ab2d4ea20c0a50417a7acaa58403b9ea9710c7e058bf383c5c7180d0045c3434b820ba42ba79d500a333ad52750175e7233d477a671ec8e7411a2db6f53204991a5825890458bf07d2b7ae2a6ba1a39a21065efc45d5380fa34c0d0fc72ca0e5d68ecc902e2dac632594c1b4408416f41c37c4a7ca8f77088229dc55f092b3006096e2ab1bfe20080698a876b4fac709752786795b49210e6044a272f13db13c75a48b5de94ee2308c1925a054b2510b2f9b396ca53d002b921d8ad010ca709b1c96ef2e22f93c275827fed9905c9c71adb22decf5530a85bf9e0153f35b20081a9ebb0d52d917c55ae8fc8830ae402bb4dceafbd37cc4cb2b06eb35bb177e0d8a99c20dad56db6335e3ba509a8a3214199235ddeb450130fa3daa0ef045a134550187840398f563ef67098489d9dc50f4a68f8f7e121a5e6754945621a394ac85d25fed7532244b1bfb295acd58f5f2d01ff62fa3df8336cdca45f90e442c91a8191e04af2050e412e321a52c353b82b08e7bff92a235378ab1495ff7efca3590c903d8a34956ebc0942217dd27bd20d5412fa76e3fd6a09426702ec1a0dfee3524c4fd5d76c8ae4b5bb52fac00ffebdaac58c403647fb601ccd38ca330c85cc1d2dc1ad3f67d29d90d12b0f372a5fb2f4daa53f251213bf9a2e2a5f0c0f777bdfa68bb4db2870fc3b9a68b6d310f15b73106dd50574f0e70829c6ba2fe7813601649d81d862d0405798aa3c2ecf25697ec0dc7c2024139ce20b96c489b45d8725332a598d8c2f415d1ca112446044e963c982c19d1d21877b8970ab8470193f106b1e7f3499f0c5a45e3ba72b12abba4107c98c9d057883f58cf36e27ed54ae052994c16a2cb6560855942f5e4bb421c67c99eb34f35e44b11924fe135a7d842a5fbebe28862416881829045900f8e0a93aa30b97bff655f3bb470572bf16bfe652a0f880920ca8c1eee8bb3d1ef2222c012b134a4febb40400de945b08dea75e289a8e273b5bde93910d3a358ebdaa10f1d8db8fe927a3ebaf4ee655152e31e6811bc45383eb1792c0b30f038844836f38cecfab6295a9ab4f45ae5428aa76b387812c918405ec41d6d301fb0fa754c1df0284ba60b75285fe80c112b83c1aa59bc992b2f0df19b20f16105cb255256660ff42cb81201634aa45a4862bdcb3d5f0105a08fc5caf7dc36efe1e5031de0fceb4d581938d9ff46f7a12789512a49a290dfaad56cae251bd301395327017455dc18f5abe11b2e2e95fd5680dab6f20d84d0b858b0b0fdd0e2f06944b180cdfa49a3a131834630802c1d586e3c1de066807ddf3401d6040d3ae451eedb633f6ced7779d134d4f501e4098422400ab0c402bbbe15cd58f57f04705a50a7626eeb284c9f9ccf22598f8b8f7a17a450b7952d323d4bbe32633a64eee8d5f31cec76f016c77affbf806bbbb128a02a5c1df40d32a218d31e9d32b10b8f0a46d19001a01ca061a129372cb2d54058582c900b1057e28ac12859f33d3f0b5421d415df3ecf802828a800df0808c74d500500d070cd2b5fbd4d5a059f43741790a99f5070a127ca490b8d2806def257332cd1a23a895d0306e309360567333e02585220936959c0163df7267a4794090e04805bc042a075e87259b7ff6a54000d8c27e41d08349d6995eec17bf2d32112030c86777968a82beaee6a4471df97b5853dc26ae5cb19341bc810bdbf56d0fcd8b0e3f4f43f94dffaf54d39d5849aef9d5c62433a7756c784aa93ccb3a6ee523b34d4f4a3b0dd14d2b6f8a0ef1355ef71231d045aaa3927d1cb7074f6363bda9c3143a8addf3303705a547816f6ce9f0275c4026196927dea78fdd232fa1bcba7d71a4d41f8e48252895f6ac53474bb2b4d5d8a2b591545aa44113514ae2a35d5ad8a12d5a5b8925551a44a145143e1aa5253752d5765b9d2cad5a95c3165250ea94ff5148a2654c9136a955e40b3858454e1e9016cb4270db53baaa01bac0912cd20f21d92f88defa41d639cd9a23ebb72350911110ec99c10b5db6e4061f783cf9b39d484ccee6a2913c8fcdb51ec1ae2d106bfd3ab6e3885a243b1974dc63b5036724ec1d032581b56acdec0b44d2221c2b4142fd08fc71d17cf61ebbab3e48f55c330458238f99967e39fc27d2cb24c477dd4d0c7e1a3efdb602b75de9317aaea0f62a515056229d3db6a67a54c8ffdd4459e815625f587a254b13a6efb3d6232acf617135d92041ffd8eae48193cd3ba98fd4f9f78404e7179e5ff80c088dacbecb58eeb2f70e017f9ec004255d5c6e781d39ea6a43b08a349d6316cdf1bfc63a88ac434a094b13f639b1b227a0da5db5a8b9399ed3d7aaa6025ec0e4d8cae75ec3ade87ec850461c5afda53b20b18f01b5df4fa7ed6d475ecd10bc0e576e1bf15e5007617a019a7961c24f663c8708397f667b0541919e0fc4f2d703d6bffb5fc710fead1b85c7b51bc6db38bc3fbc5bbdcc9c79c2a342505228609e46498360c94329480c70d177d7a0db7409fb8143748b78aa9a7206cb98c8db38e134334e7680f4576091851692c31188e74c751acd031bfb5a4d6761db5ac82ed6773879e62d638845b0d42e1acf4d28805be93056910f6becbaf5195a4bba991aa513d8bc9f37d11c0fb44a31297b854ef8c4f9886807aaee2b06e37b0fa4dcef331a82c9c4cb3febf7ceebd64a3b4c3d8e541948cb1fef6ef1781b496cbb6a3a88ea87aa86924921d4c8ee988082d30488226cc9fd5b4e4ae3404c8bd7194dc36f0fb43f1312fe6b1b1dfd48ec78e3c233e95b1d4bcc5455b932083d0ec291d39d0bdfcbaa6b50d3ade66af3ae3a2b857e07d716f558a70198af61d81fae9378ae86ce62a2c49da146c9488b171ee9f086dca3e393192b5a42b4b8aa4b0bde51f90985bf6809818c9384984ddd71da52cf122422ed0f87e9e394aaf4e2eba88220dee224a9da985f9e82798f0c308ee7140b431b228596d615257834af58164745346d33a8b540f1280841424ff9159ab4918ba1f4bec8901d80131108d208b10b832f36c84e5a6dcf01f0071f7d9ed9e251b04f377625cf5cbf4fc78b5aebc6f8d6ab2a7fa56e5579f463cd764f1929915de9ce46e5f5ec5d2a7d7a06a4339fc03a2342ed0f5359f9b742ddead5b2fa97eb17bec3fe323a61f98acee92bb15930cf34bbd35d126241dbd64b4e107ae37c855e28e08763066cb50a41aa50b9977d66f7533918e213301e15e58fbf1312e1189da560082ab06a46438560faf9881e1f50660f9e3aa165489253341dfc75e006c58b76cc889414c73dc5d69577ae1d6c41b4764585d7a1e95ff18ef702ed9b2ffa77651cf7bf59be2db32d14325d04dbfdbc962e7c0d4b542817f81ddcc449086b0ee905d28b32f74d3b2b1293bdb57827de1c5c3c15f47b331471f5fff360544668c7437197e4d4f6ad8c3c2b4836c86c76452f1f983ac91a719200bb3269bba2df8a8fe66010bfedd6d64c317122fe9f67dd480a8cb02d5672ccd8f4aed5d71d8f1720bb05b4f35299664d0f16292b07c95b867e449b55eb9e88312e3c235ae1686e847b5ce75d3e9674b960f26385b387da43672780bdf595064972dea15d83568fca2e4eb1f383202861be16d06d91a02284208b5ea6b80ecee35bf99011c63519e138da2107628259d8d6da727e906c6e37717deaf9c123192cc0d6af301c9406c70f15d8dafa1e5afb70f950b5c09494d581aee1a8879cfc59c4551f87085224638472856c720a16955bce4f6cbd0d88472bdfccf8e93ac8d7361026d54b53eb329179e97b2f3b1f11227bcb08b1efdeba9a8b93ac92d1316c56a93cc9d335d42eaa47fe9a5a34885bcfa25e0b7726918f3e00890ab010439f7cd71eb741ff97295d5426de6b320bf467ad6035645d771af440699c6423480a58529153a54492a285275e03fc4e5b7c6225086c9c5f952568e0414ae4f630bd8b2b5861d7e2bc580e7b6875625056cf5b8df01478e3f73b9ce68150d2e89424453d660579bd92411c23091a12cba2ed434c9552bdd5c3333cdaa61bbb19a969f58a741337296032b8e2382aa210be0b995c4abb730bb25e374d1dd677d7377157ce2fdaa4df1f2d997e5c69fdaaf8bb9834f7ddf26933ca811dc378b5b6f45914f0deb5bc90c4ac7bc211c6c51427a4c17ba51a84cf3fd8e15b39ff59b525288007c522bce02f5101c15fe3d9b79284dfcbc1cc5fd804497d96bccd4c8eb811bf088a3ae954a84ee8c4c030ce690c26f41e95f8c3bd19cc651e88573ab18f88070e3c40cc65983c0beab6de963dc4b359432e6af033610408d9764b4ceeae0c37bce2205d0b1596101bb7a5c47f64e23de7d1444539f6bceac4a4408efc98d3d04c95e60f6e0013524c4294f3137514cd958a232af1eb76a34f4d26b26ac54d252c60ea33d78342a1fd13b80b76e5dfc552e7ec34150783b3b66830ec09583cb742e03b6d368c59fce35bff97653d8c0af916672df4a3815ef789391b16ddd9456326624f3e59964f51f4017c92f006c81e3c7ac6b59b23ee003a7e585d594034b21c0502ed73ac85dcbde136486d7d166e35e92bb54f6415408f299b234ce607547fabf1d326d1401d614cf8b465a1dd8dab6ac238d619afe0298e7061bd59c05d5d2c16b263dc22f9e91ea8bdff14209cf502f12695b4678634c61f9f19f6ed24a0980bf865643fd902354febb3bc0a5ce342df4ca1475c306d6154d61b73a34bb4f72126f9a3312d5901b294b7a46c0268d3c6051e670029ea0b01a7ab8c58b7709cc9bc4fb506f68cfa1ce29a7a8f203ae672d65847c3a7590a22650019031cc9be5c8340d33820af3befa2beb30aac8f1e22dc4bbf03bb7b941a2a0579256cefcd215d8f31ffa17593a8b749aefa4fb9303df28c6149b613955f9b82f6819b65a0afdfe6735d4a915a024d6dc0d8b320c0b345fdf87a1a5d8242327d561761faf00f4e8cce8ce0a7831664d8b7c405a6cad043dd3f121d2f0863a30aeabbee20c7c6ac24f1672f290cb565cd7f76c538ee031a84942d37bc95c7182d2b2cef92bbe165db0d50bb0892061a4910ebda48e5de0d82577d6c946777368d777ab0d181a2fe4f8011f8524a61914dbf601fd090496062b3365b6c366115d64a50c2debd6d970ff735b563e6082404bfdda87aa4c7d8b6d21369db1a61781414fba38999206d926f868cce260f3e4a4e8b73f0e0475ab7df5d842de177a4da16f18822b791ae3f5a1b79ac0575c5c23919e85ce8d506eb5c9746192c361b9e65ca34bf5c5ad99ab992ac7f6c3e033daf524da6b1f6709fd14995e33c6972d58e35de4e8ca45fb7d793dfaf8c1e35e027e6215cc42dba130ff3acd1e6714d5f9f5072a82a9ee2ccca558b77b1b4eaa6203d7a98294a085d0fabc153243c914a743c0cc092198b695e3c09fb329a0b5a4e233c6e786a34ae3a356c60e889f8b6448e4736d0179c96609ede424df6dfb25fd4a0acdcae3622285cb6377046fa24a571d7ba96ec72987da201bb6cfe74e79b68015e524a8ae76229af3f02712795a3088891711097fcb4a64100d11676a7fac4188cc2ec513324ab41ea3911683f5a715aaa3ea0679231b07f1e5306238e572f83a4a2ea0c220467f32067cf595775e250e1786a764ef851d0a22ca4376d1240ec9c7c497a2ad951d4157afc505896d9a5969cc65f3b3d522bcb1c0a605e1ae73eebc0346d0ee7b7def4834b170ac377b0391c1c264a2f0e37e9739cb34882295893e8bbcd6b83b1ccb308c2a5ccae8aef0d7d0a77c7619f0b1b75863b2bdc92bdc5bec4d0421546a167307cd5a20f39feedf70d0db25c475f41e93342670a09b4c37c03403b204011ef1628120e8875f898e1e6bdd373652c4b11ab5d059555351fec5e60c82374dc38cd18cf8291628fe4894d156ee311154b5a157df0ef6820c87b76b2f2d24cef676db146524ffafc6676d7c8a2ec936b7556f5a895193ec04aefda891a8b9d63f84d50fc41b2151e84987bf939a8cb66bdc7cef4586daf82e3b780e78bfbefdab827d1444327ea26e3be1c7b6c5931e5cad1e098031fbb63c1ccf7bb1232fb4b0052e9653cad4d15bd5e3427ec45f1bf916bd334e462f9f0667a415b0d858b0f451565f0d490fe7e1df53fe807329c6cd379649f3c21b7f4afd716bb18aa48be950cb94cf236fb10203a6749e6c416d16e1c55c9dce664acb992e302a5bdbd0b652286b12f3415e47a1ae523c0afeda01ae82213e3013bc423c7a38ba0ad482f244b252d11da67d9220c76385b44674cec5bca9bb97848b25796dae4af9896524b3108e2dd6ed00d7c289d57493874dd5d5501048b1afa2c261a7ef5a929316d8994a71b0e308765ae2736c46822961e71a04aa5e11bd66b23eee428d4b2b5308019260a504c2832e2750950b752ec78265822e3f0b1c3dc28b0b296f225550ae1ce8b72c3dd44735af620439b878cb4bde34ad384fee4eaf7117a1c2b406a9a88e619b7997223206b69ce6c700f9ec4576d425bd081cb7bb6bc507feffb1441ace94fde9c8112056790f01cfc943162b99d03a80fc4d4a4d4ba2690f49de42d6681fdf5d614d8913e1dc2243b55695a1393c6ea2ed3adf28539f57296fde3ad0dceba47be1e1701b56376afa91a27dbc4a080900f9b263107134d8c9ddc19cbf0dd4bb44d8ba1d67c03ee3e0edfbd1c348ec1fadd6f2dfce54c7bf83b13356c3ad9108c90903373bf6045c5b71da2e855c7d1d7ed51fbf1d89d59d76dd37ff5103cf6ac783868c3eb95c0724547a7b3f58b5c03b636ee8e22f796532c0996d4a9869c5a9f73a0ef99086f3b6e0d3d93bdcee6ed3f41699ebdd5509a22f7a06e0bf42a756a138bc51db98548b891a067b6d3bafc06e969e6298c3a87f2d92f372d682fdb36a20de906073a094d58546ebb56d2c43b01fc6a835f93ab29afccbcc9be48fabdcf617d5dd6fd87ab36426c1d662da22d73f8249a67c133bf1d20a94c0ed5bbbeb171c7f45d5495191f5494ed41848ffe468b84e8d455f27d6cbcde676a6d8a1332616b58f6a2a36ba583d5f6e654e1442a2f756720d4bd71106eee02e54572af4781a1366fcefd7e18f65815cd57b601249b2245aa12112fffa1b0a174b2a72f0f8358687bc3524819e2c216b22d8a024d1578bb1c478cd8cb4385ddfc329b654f97658adad0568f2e6e8bf00da4321668453510188313411af758a4e4414ac64ab6cf0c79a980f4ab4e1c2addb24faf73e0be83407c1befe121dcaabcaa24395bebb6e688d9ba2781ab3192caf3be95ba243f66cb93141ba46ca7a9092ecd458abd25ffb0e12e251f436f960cb0f35e4ddf359aeaae9e15214dcfa8c99a23dfcc1d2b71d00d19c003494717fe8bc308813efd0fe6580681299228128d401c264efb3f62b657a1b9e0fe18a5376ef68d4fc72f16bc3ed7e474ac763146aca026ec6fa2382226aef4cf4feb1b0898a2639bbfd62664e01672adfd10d942747109046e724408eacd77eefa210cdd3d90096a08e1cdbd1b5ed159eee8010c08b7538c14b6d7775b8fb77234a9093f01f5ab2b23196f3cfb3c49680a53ae31b23e623e14760c5f12bf56488f78a03e5f81a7e1f43747f8f5a8f067472f1781df93e98565c746092defecae82c188561b57b1840032ffc5bdead56e4bc85672b08d4abeec09688b94c469570cf4f270f405817990e8257a409efa60227b5c1f230e9b03c57c4e6b0f420ef181e36b604a7eecf7e3209f5487cc81c690879ecbf1e0d6e2eebb52359d0fb00d1c303767b4acdd0ae28f0f74d92b9ca2a97dd14c126133f1008db671cfdf2a78d859d821792b7cb2386640e5a708d75b7804b65357b78129655f181a6978a3296e4b352681e7f888efca94e8243411711593a53e2b7077b118e0b7bd5a86e39a0ca0783ce99db48205facf141bc58641063ae0e8caf7c325151e3ad91a436aca42894d469bc9a6485aa67756c819ec75791bb6fd37e0ba8e0975351effc9954a488de5971255928d0c06d8e9bcd0959de2c5cab555d87314686140f9f014c1472c8de66b1003530a922f9c6f823807c55f2b23cdb9986fcad91bd670946183d680deea0abc2706c3ab5ef032fb2750717e07d34b7917754484224eb50061c9f976f52b1d06133618841db7d67f0203deffdde917bba0b1ac7b76de7ad97a64d854529a8a4b5ca2f958016bee72715080b5e5b3dfa57a6889e40588f8131424706343ae455594216bb15f0a7d9240f4f0fcef891794b38e20f03351fbe5a099ef836b59a63fa2895b2cdb486c339d84be11a328f26a5f8f2b12a56aa862ec9603636c3eb941fa300075ed7aa6cc2d2cbd1c4ab0975da7026184849ac2d80565b0c1868ff02d148166a5affca9ddf3a2c2fc0b822754e65bcb2604335d490c312649812462ccfdbaae171fa4db4f6012f9a065898cac752ef38cfd05b54d750d37209979e13e34de30f7f9025d55306573d7fe960bb91411977d36b5328f027d7bb1b05a3e2496d5213794cf9aea1ac6aa5d4672a0ea0360822dc4a6062b3505942c8e1c40d60e6aaac648516296a32145d4e50b7c1c3842573368db9652b3806916626385694b61dbefd01dcb3d85863e97a07ca640b6edc667ba0dbcee1d112b22e1092423ac981127bc97d0facc68a2c4e77a03932134977807ba5e79507685ed19cd8a574af641c4e8f2b4d8dad2577a2f03cecb2dfb65aca1153df839d810f115cc650624b910749fdc7e98afc7c6a071296b4b8680c51179b533668c9e9a48f5a28e1ca3af25a13202097301710dffd374fe70e6c79f1b3865977d98daba9387d93b293640762a88f1b74b74aec0be38f63b5002557c819f3bbec13cd7bd3ccdfd653b686b36b23ee74e0b5f589c31a1c75eab6a30031d6090c33742843b21dd5b0decda1e4afebda5b18eabdfff6bf7350c12b799974cb4da78787c7be3c97b0b6f0b4a39709b4aa77bf99e4ea99e3b6fc27ca52b2162161bf6151441f85980c97c9dc1a17c8b7a1bded8aac4ad1e02a2e61d259f3c418be7f590446214aa4c80f88f33e796a90ebfac952aeddc9f38f2b86ae2c57985088689f89ec556dd0ef0d32c7f77e703dde127ad74cf8415c77851c46a80555341a6dfc857581844041fe32f252937142addae6cd5f7992abd6b0f6b5f57a07843c30b11899e241225f12b7e6b42450816fad4e1b4b14483077450c8c49a0544d9645d9ad88dd7bf4a2efdc4aee18946a8805a1135a569fb1850d9e1c36562e5e84174a4745c4976f406c5ed51abafb6363df320a7b92c7863a249a960f6daf860185f53a128d4b3920e71a1776523a0e26eba030d394d2ec890e9cc721b9e60a99d5e154641221cec5352e4e85b928a088434ae26125e75eaf1bb39cf39a9876f9207705293c6002ac2411fdbf23541793ac5da8e13ba362940b24faf88767dd6e28cf42d87d4a72a4efc8b523224de302030f8ba254c7e22e7a60489dcceb504bf728ccdf202c56af11ae4ada15e2577074453512452ce438469569086c088d3ca708a5232ae7bab8b05f1f5b11a4bb35aacdb388f7e87de549e1409df375e5264be418bcd7e5674d476d76e01f91ee50111c1c56575a9d2488fa7e0aebffe096936034c5788cfd23d35c06e52e5c955cad41cfacd427eea36631b374442a8ff23e362dc5929f2ab4e02abcdb057d5693246c7a720f30ecd6243bdde889a39f35247795ca76a6a8940d8e57df82f9361373c1ffba288ad7c8413e8446567afe9183ba0175ba432084138be444765ff2306bf32413261c08d0f5ac0a9da348deaea9883bda08b8999ecd59d37cf4e932a58f540dcf389f7251b72ca8b22f8470a823336aca6b18c3eff3b3a770bab40dfcd4be6618e3f54c504629b77df4af562059e0715e17c9505604cbfe1a60dda7921b87573ffed8030e1a8f81741d7dbdd6932dcb2a122a8ec889017eb7173f258994e804a12b7510c5b3e045c09d7576f4cdfaaab401b8204d547fdc6a45819ac52e59565b9007bb90e04c01c88d7e17185539822e4f2f08575f40a8c1d86f4581b222a77332bf285b1e6044b00ac998556350a3625cabb2372551c50adc4064029bcd0d3a3c78e03595c8d6631d955a7fbea54cc974a8819e0b83efcbc190e652f29ab612a47b8a7b7a431b16989d2652a1c204775c8bdbf09d343855273e2171b380b5c93d8d2e7c5c32dcdd55971861570d47091f18542d5956e28237c6c0c7fe3948896815eaf6ab9c9cc7ca0f90b79595f8b871dbeaef3b988be8384efacf7f59f86be2452ee45910c13709fde45fb18ade7631a9c651d0d0731748c2c009106cd090e897199592969b85007bb5e844ff1cbef2faf7624e237bfff131355ac1d9e704cd4194d0f4540ae6710af1cbf97ecb2ab8d35c7173b0674710ee252dd640c029f4b190e083d0a2eb2bb30237042b265c9b068d739ca838317a1fd300418a0812c6ffbfe6522fe59e67d1d05f406a42343c1f61c754e5a5e2a7e411d83d1351c617614fe98b0f79022611d178cb0c0485c11f2aa97d3b34a08ef3d4cb6a7cf73122a74e7ced77e1e0ae3e47fe49ee91ab5c6b50294608267c8f6a0bb3296933a51137b893448214b1ff2518dd02ea61ba9e02bc530a7394aa1ab8ff2172fd2177e62fe55ab0ad4e512e0a9ca2558448e9e8cf780b717ef416ee7841514e9942cd5cf3b4c6a206c2da0ac49cc34c3841d35531ed53540b354bd6a386f7118c87e37f115492c43566a7fc235d0ceb334e80797071c4bfd340151a6b952ea1e39fb969f28541dcbd25a356b856c21e4eff17443a403e71ce0bdefc14973fb3f62f0370c12f3ad8c75f688a3dc30435f3dfa512dbba7e6a9e25cba0bbb903c3e4a47409cb68bfde7b3b3eab2c97102e1dfba297db18667d9329899cb2e7dfc4d6098159b8bc07d23d9f04cc2fb779b44054ef9e8c68e08ecf200200930062a02dcc13d413440a2771b812e50cab0e583540891fe767dd856492fc6bae82698608031162f8fbd43cd71b0970c13b1a57046e7919570c57f84166e01ad6d0cff072ef2e3bab3dbfc649bc035f4cd7213d449cb478c7d62c7ce05ce58726bdca001afd3fc3c40569c3198515836c8f0423781958f578b645840acc16b8743fae41e7cda76e3b873e67f662fa3f3c3a41e76a474e548e803a620c83dac335f0a72052547069c376b7ed13f857499bc140422fe60418579239db89f67bc50017b055339fbaeb9911ca5393ac148736d201cf27ae884fed1eba0dfef35e0e5ba82e1aee1a5f05321c158c7f04de8bd908713a730825ac1ade1328015e0cd26658fd68380dd913ed7eeadf2468d5fa9c9f81304fc3a906f7b91465a01b4614160e403afc27e8fb41fe036303e760d43b7f53ea699026e0130814d008700b74c2e0012b55834b4a0cb84db1929cbfc8d7c30cc0e5a04b481e6871595b5e88dbf651aaf7e1f0c83fbbed7ba9eca88d39c95e266e2e21607641861ab840da44f403ec079f0c69ea0dc10bec040a01eb61508195d28d7ec105fd074c712934692c049a0cf2009fe257519bcf9d5b4dde78c16a53c3008cf6367384418081bfe535c0ec20e8629c36b872190553c338991a303c8718038aa4a113848458fe094b1c02ba3fe2e1aa32c6a14cb8b2491633b58779780857e6d868a66d830bced5d920fcff5f00857c0de0151f220d9781a7e710035c8ea6a6aff417d07495d673d025bc1ca8027ae2a4691b61e8ee73d81649b7bdcd997c86b175555005563e37b82c35f2b9317c7f3c977d5efe2c47d4690c2e7ac0ee5db35c309eb8e99dd4961757c6efab450878d39cefa2c35457e5b4ce792c2d4003a88f71d1607d1870ad568fb3059013870d317a474b7a04bafa2826a272ecb2d57177b2bf1900d098101cb3ebaf132aec702add330205ca7afbd4d03fa80fc170ad748bd20a3afcfd93dec522c34d0057c675a02094cbdb3fdb51900a4e2191e9c888f169000e7869a8c670ad1b3a1905142e1d15cf3262d2af363a88816937cca07976b57695c3d903bdebb44583dd09db040c835a40cda4fa850d153815810d8e8f661e8df9157044d5fa32b81caa06de03e98c17c2ed80d3413d80a54123700d0c1de2ea1ee05ea8221492553fc0f83378f2b053804608ac1436118dc8a005c295dfa4a482f5fb0cf78480c1a0d9004ea040807a002b606548e956020ed9015cfa74f77156f8fd4b72c123a0d0133842ccc689fcf400ab54b7840e08cc5be26a7d435f2cf590073e897add0d64871863bf2760e719b2b1493e2a61067d28c475c1adcf9c605300028036e342fe57474a3d1075bc2a672cfb3e5fddda4626c02026fb74e92dae3ec5c78ac4058a032d24f496e94a41aefbdb1fb7fbde09651811b221869f622713650a1837ae63ce164f74e3e68bf2d689827a6f3517d9e29046a2ea364bff5e235b7832af00c3ffaf86f783021b7ebcca41d4fca5ce1a2ef421e0a9ecdd7a0d51cd418570e0405a7db309e3990fe8e5155c26373865832be19f0002e13f14c649a22d001c7813d88fe12a11412d1b3b078b02c781aaa07df12dff8a774ef4f0dae38a1c8061a8b2b7e83520481beebb32eb536da4d75385697039d083e3cbf8b9ce08b87054fdd2f8363d7d8e9942b58f68ca340f81bf008cd1c26cb09af46dfb4e56dbcb8533fce49ea5ac8f6c313d3e60664086380194d060ecbd2404546dbb4d9afa46ea3928c336c07c047538c3e5b737d0ef089703fd42074281a9bfeb64de5370e564b7fb37437ffed50ea9771abae76a32260ca84b1e481834246666f21d0e2b5c8a3a71b4860d564def9cca3f47ce844b0930ae773ab354189b306c02dece43ae0b63eea9081b8a324835e71a9b916f2cbb799f1e384f5b0f4a6dc4999585ebb041250c453d8440f4a50bfdf5a73d771eaca869d6ff45d356b3c15eb896a597a73a05382c06fcbbf4e9a5a4195d77c7583f86573c60598d342592d820f7ca7959d24f685c94fe652a133432591b242b7f9c97dd0218df1b24ac5a514af62af8ff1d300121921554366596c2409ccd2cb0595a6c1e80a373a7b0c57b3b47efef57afb45d99a70db2f060c02b5f29060da88d9b328787e74edffc8731f73bf74b14b4366a901637de2d30ae2f9180250309fb534371a4be955acff8b901665c5d127b931a1b5c74428fb2dc50099abe2081de8c6b0319fc9d526a6c97aeec5a162367958a5f3525132df5430a732e06a8b1347ab53487edb9e4912b9f63617ab1de5f7312006e73537cf6e88425e94a1bd4fa2c6617aeca6f4116db88d69ba1c8823dc1555dea62b62b34ad6b674b14e90fb24c5ea6616fca482ae440dcce5d8f33434268d29c7f184219e735d9657fc4f7a832f79180984be20964574b5366a4209446967e01c90b2d523f9208613294d078d8fd5f5133b6f5e7d2f0ab7cac61720a51978636bc49a780c50181234d05a101884ad0fe51971983cb2274a8f7283b4c98ea8b193a4f0efd8b1a23313a78bd6324bb507a696c9c5b12a0e182594c2cc96157f7f77d6c15e4b7ba87cde754c2200a9847bb8e77431ae97eb68d1aca4f2f85eb2926239ee8d5f623b192a57bbc954e776d47f05a3df1d154879242f7b08d809eb203c6b223118e053af00fa3f4ce197044a8557a8037342b78e88e4c54c558b784d22d5b23bfb21a8ea82f0b3a66d406f76b517aa6b12e7ea0a35233987d494d54cc246876594c6d8b9ae896f57664b6a81e29f2d692b219fb7df4e8ed5cf1f69a385860779e3c530458d57c1dba2b70e0b77f072ae31ea704c5697e2aa5405b75ce51971b95c2a7ad3b773ef22baf65546ae9e06e34c574d2b4a7510056ffeec2c12625e2525338e13b3f901ac24429042182212e030efab18687c398b922b64724e24c1d49182ac7e340814462b2d9956636fb63e704f90fd06295a61af2ad917b30e3a3ba4db4fbe2a2cb30295107881b99268e48113d1addd90ea5c64e0f0c65944fb58e2c032268d89cca178b2cc00903520c75e81fd25f7cf9d2039c6ae330dd01528b62ba976b1b997564c424f94c92c313c88d86e6e683d96d59a7778466394c1b12c40c68d924f8c8140cb7aab15070371004fe9edad9111925b1dff765a8bd57fccea8e45a9599709f5b7dedbf179d7ed9136da0d5606473623ce9b509a7ec588cfc1cb48d5804606c080d39a2e29008c4203efa4874bd4378dd28a0e15d902667b56c89bf8c68204a883a8d00ba8fa80ba27acd2ff143ed84c5ee1712b25c2617afa0bcfe55df32e254aa439ffd9be1cf0210ead62fc4d7a6397142ce01e49badfabe9d7e37aa66c6df7893570d42a781ba69e5f7876b3de0e0dae35144a7eed2db970c798276c67ad5ef81b21f3814a2250fcc602f7429e74a8a533c59a41c91a662e46b3152ec4bf0c516a9c84536f214c7a57847bd3338a2c38cfc6491c89ed3823912960cb37b760d134159576e4b99a247ac059b0364c21b68f01d3028a00258e17ad254323b4ddeedf9ba28705bae04766f24f3b97b2e0fe41004cb635d221fa7d8a0d8e5dd241b32d88a033043eca759832481c9725233298b5d057278cf30fccf929ed800259c00d729c0956ad41f664c44cbe31345582591f66ee79a5d2dcbe32cc351868421b7c4e758b54af4152ff6ac07f0e98baa8f2ed7f5b514a647a1092490dde3f299b5d4e4a990e3d2438ab1dd51370183a4365bfb709c7ad5b42517f3f0e8e937a0a4b42ca06c91793582033cdcec727e08c3eab5be9b2eefb687329168d131ea19710391948d98aaacb28853d3ee9c09d3b9f35932c3716f0129680e5af428c5fa55d0765ec55aaeaf0ccd675a3640a17febfc731e0b5e7e4b32907fc159f5d7dc0cb23dd3150f2852226a5e631a6c1c684a706d2e19600eefe717f25dc6a0c3a1e3d01f44f71183669c4f68873e1fa46b9951a1a551c03c6ce7d893e6a2c6bdf24abc548683ba68cb6507f7675ed9b94ae8b92e46c11295994e2d9eb9270af8fda2fbc696d18aa255c9ad351388e2be524d1d9c6128ec7ce3ad32cf5d152168c90dd251cb7e71e25ee4514304b7a16c741064d4e911f45ca66353bb6e7a9b4159a6d2fc9b4657756acedaed3d7cf8a0bc9102fd99e6702404063886a43980d9f2b4327d259cca0f1cf190aa6560726476f342acd27cb51fe5eb1ce049eb76a3ab96e4192c429b8476440c0f140c774cf7cd1136c0a99cb1b37c970c6bacd2cfb3acf5950ac3cfbeea080069eab4e045d07f41a49e0403548c5048daabff589c2391c9567ac4252d5afe964707a2438dff23251ef46fc9b87ecd36394a43c604f407d9c369389e835a09194078603836c3a0e077e1f0cfe299528710cf0af624af069f581c54b508d4c8aade5d6a28c6ecc4461f46b09b17eac5fe4f6e87cbb454280e3a370581b9bc7f8ec04ed7c3355e2460ebcdf1520130b660f33701e6d445ab4dd544fb1f09a890a4c90cd02ca990f412e6314b56c725ef5ba3bb520c36b041b397a8ed6f7e7dcdabf49a465a58a7b2a2863122610d3788a2d667e435cb0b968bb75e5b3984e5ee2763942538b439c23472f011c857dc707bf010e08d7cc4c41978482c95c33a957cf2354332431bc4944df110561ce5986d581dec90b24a0cf42dc56828491836ac0ae3a7ee0ff56c084219287df999ea96ce67029b6baf62ab1939d00fd4405686f90958bff3bf5a71df7de038d42733bb49a5eb2a48473a0d16fe1fd7057da000eafb64293260b62c07ffffffffffffff7f3fa51fb2b79140a278a2a494523245a9a939bfc3654a49a694d2c12d3d67a463ddd29cfa21017402690281026d7573af3922293fb5fb29f2745cc7de439243a7a97475587b8aa921095f32bbb5f05f6d21e97dcb57ada896f5fbfb610949aaa9a6af50b3ef20a97d61a7b7fedd1524293262cb5dbfb3811c40123b75e49e3d2a6e77ce1fc91dfdb7bd6243c6f9fc48cfbcd89e29978e9dd9ccf691543eb75aea7e1ee683bc2c1fe99dab866c57f59d3b5533768f96cd9b3ffe3787c5ea919e43c656b7a5989b477aebbd7376ae159dc5bc8be75fb078a4cc5c471fe6d2861c7231ec1d49357de7eda1a3f828fa2e9ecdb5579b3c61ed488fa1624f9b4389da3b6aeb486725b63afd4df9d95c74a4c7ca6d764375efe63947ebfbe75ea3c6c49537ac1ce99dbe964b257bdeda6a98c1c6911833536ab585bf0b476be7debaedcaf5467aa7ed5bf8dab675232d66897a9fc34e3d4c39b16d2475f99ad79f7364bca8cc1a1172d938afac992fc796ff2c985d23699fdda30a99aa91dc6ad7cdac9edaf5f6692477dcc71bf3f78fed381aa91d297cab15730b3dd6f68cf41e4a85cdfab95febda0cc478dab9771989dfc273c848fd966aab4db3db35d43e862ba2bf9e7c5d84faca8dd0c3c59abc6224f6d8f4ade4739ae266982bc48691d8fbea8291d66137e57c66566d5f24a48da1b51e1dd78b848e21545dd7ad8bb4de2573ce3c1917e9e5c1f5dc3b63ceaa6b8bf48d8b59d35eaa85fae9f38cf0b34f37d7506b47b9f3f983cd82e942c773b14808d75a6ff79e355ad47bc5af1569b1c59ab1913a236f4acf569158d17bf1a2a5ca1967cdcc4b0b960ac6f8fc52a699bf64f00283cc97992e3733344c81bb07557191f3fafa54746fb92d62ce5f5829d26b96c77757e25a37b3186c14295b5f7333a5ae5a5b4a7991b999f102c606d306d9a04b5f2d5b65221272c57b1e7aac290f915c277a7775fba956a5b210c913157b66ee74958348e799c3b6e8301ec6b80c44c62cfe216deedb863c15aad3bebb78dacc1ce5807d48cbf0553a660ef5f63a5516b887e45e4b6f6745ec658a9887b4fc7bb8a166737538577987a4dd9a3a6e2bd6f97d56590784f6f0ea7be261e71c92539efcefa0a5ca38a486eebd5c6ebe4e99a95bd696c4dfc9729ff2f673bad7d69294fa3ad67616d192945f7f3baff68fe6e39d25bdf59a43ecdbad2c29175beabfdde3b26abdb11c6e3fed0d4b6287bb9753afadff5df44a4ac650735407f1ef59b4aea4cfcceda6d4e15652af760e1172a7f02c645692f676fbed5fdcced8ae92de233c9947335795c45c33b48baf9b8b4d25f93aa610626cdfee128b4a42ebac3bddb231e4faec298953f520fade8654d9f0663ead29e9ad7daede1f3d8514bdab01a31e6c29e9214fc656fbb4252539b5ce7ba8cfa21de5a0edfeed3ef8a8aa487945d10d25b93e27d7bd83eca741492c9bfdea3b8d3e49a8d235e7795d9e24f51873f4b8dcea2431a4ce419a98271f9fd3633949ad54327ac7595bffb037498cbd479b9fcbf5f3d2d524bdf7468a316ede87b965929e1bbd3fad4a7df7314c12f343b67c3957d8fe7a493ac82dc2d330dd5a92d0fe3bfb0c296afc0fb792a4cfedf9e61e5a8f9867104b49628baa946aeaaba1f43349c2954dd15a889ccad34fc14a92d86bca177eaa6e2489ed5acce834b7633d084962a88f5d262ab67059ea3e92dcc367d89e1f62ac1cea2feb48ca862adf7a54ddd4ff37927a39cfc63811eb0cca48eab6ec385f7f527791d40b1d5dabab484a657fea612be44c1b3d129b485acf54737e54ef9c75da5a8524e68ba16afdbc29769918302149652fe5d6c4ff601085ac9df32c487268356763cb7539908489faac528e310664ff583fd23b7fa265ac38b3fbf4c147eab687b11e749ae1ead6e0be9c0e6adc4d070e861a07c26a8dbb99d9a3196922dcc3cdce2ad57bcb31f4e956b6a2c07a24c4d071ca8feb570b17cb233d65ff1c991f5eb5bef0480edd1d913b1ddee176a4a38833791f393bcb5d474287e821b6a8cb747c1e948f1e3df2b32227236ab7b8bf280243c3e648cfd5357dc6eeed2ec44a0013a6b90d96e5c8982f1c47e247c63dea919fb7620c47c6bc91316e681b1ac3460bbc466aaed3add7563b6a2467d6efbd7bb6103f434e23639686d1e83d23a9976fb5a5075fae4cf66c46c69491312e301909b1e43d4f99ebfb8792c7484d21b6b89527bad54b35c26238f3223ad8ac9db8c7d1596875f772cf57ad30527b88eca16ae6188ce4faed71dfbf97af2dee49f88bd4cc8f56bb667fe5cfb1170973ed6aafddda45ec1f4021dc45e264dea99cbf513bb7b848ff8d9d43bbda798bb434177b479ece2adf6991d6b9c37e903aecdef17633c3dadc171a44f081b3480fa9d50f9d6bbe0d9d61915e5b6e8f182bc577dcf2156965b6d778bd6d56246f6ba9852e75ab488da965bede4a5f8ba9464542c8fcd858632ad551dd8fa748ead67a36ff86dc6b3329d22f624ef725274791f03bffdde25a8c0f1f8aa4d6da7dc850732af809d698a9f7aaec445a7cd8719deab063478f2a3791d82b96a711af5d4da6ae473013a999428ffccddf9babb344d2c590fd34b5eb9bab2a2b918e42ecdaf27348f5074e22f5f27577dc51b58e8f541989f4dc39ab3da5bdd88947f1118917eaa3dcef428ef0f784b082cc086c44c6a4c04524ec7fd70f5b0b129c9c20730255d4079eabf50f5145f0800d5e6ebac030f365028cf18e4908d88d4d981a0200b0614dd05c025c6e6cc2903000000e061506862fcacb0101d8d43816a6c95003068113b800c0850b1706de0105904d020044c2c5c666c50c1080ba512d860000d48d6a32d4c418c045800610a871313075632323a300170016302025a25d78fe1c55d43d5dfc35c368d2b81a9919669a4c73286fbecc8c7078d8999b9a2e365e646410383baccc014787024e0e3b7333c34c0b232383c0c1010c0dbbc500b665676e9a0c3461646410e05ad2e1ddb5eadc1752e94d4b42fd6ddd71a56f5bae6649eba8cb4507a9546c9e348ec69b6549ebcb164bf4368061348ea6691ccdda7c99b9f1321366c9d4382f33606864608763b97136348ea6008665676ebc74b151323208f02b09f51e75e83974256573a45e33a71ab779732ba9a1852c593fd458497ad6f9d5e36bb95f25a9b5acd669cb5d913542aaa466c7a9d695ba5f280fa7925c43e8bc750c17a392beed42aabdbfa7176f17cf2f3334eb7c4ada97ec2c6ea2ebe2b9c2d8e4056c8a3aaeee653eb794aec0a5a45c3ccfb9d78fcb96e6064c4a7a3e08196a47eee2e915f028e9308610b766cea66d346051d2e347baf9123f94d7b0a163e5d8435e8741e14fd4938ce9449b13dde435d14cb23191676f33ea57f978b29b63675d0cca04c624355e6cfdee7d9edbf625c9799762dbd052b624b9fffae7581e57496299fb0d559b1d765e5355604ad25be6eb47bda3ca83cc93fc663e3f62e4e72077e4db08d5236a7e164b921443abeab1556a21d58b23498e976ae7b5902b3ad5500e0cc9739ddd16b32f55defc11a4cd99d9fbac9d8daca9d4ba86f068738f123992f617bd7ccf75abcb6c8d24f7de7bb7db773db99091b4dcb2cf6a87d022893547847d14ab228931a58ab1c5ae1349aff998a8df91db316444125a8e9b3acb8e968fa20c1f925af66e72e25bad1de46c487ac48326a8a2830e53c61863883146449a490772091404e4308e03499873926a371240303108c4380c632008a2288a184208218410a308218410326264d40db36806b60fb9d042b1cf85de9d9bdb3d958d62f61ac029fd71a1756e58e49e76de1d385c2b243d8fc511abc6d0e6bb45ce053d7f03ebcad03157c9e33dc3ae8655b2a788764dc2b86023eeda3ccceb90a804aa551a5ee590644848d025f962f1e8c0f5fb0858109d3d97c1b4f0de8b2ef86df02d4d6fb8ea253ed19df785b08b4eed065b32480759c96f71159ab84d06a2bcce77c21f24da1604f9450c81861aef61b8c05e883879cc43360ea86d55236f6e8bc70107d38b28c54b58655bbc2c94fec83a058250c9cd83238bec06260a61244dbe6bf54a7fa24e897f6f9c0a6ef3d71565c936f98a294e0bd8c75d5c81057008c5d25bc0db6e25c053360a61b61fc65c4fea7b6956bf1b729bf3d898a8950e58dbf7218b1d6f91b5601bc39aa6588d696b802cb50507e6078fafa8d0e7481dea2f1407dfd6acac665c129a33f39978974426361597b99c23607a72f714284d18c3960faf571ba6df6c1d3bb26565e7c1676a738ca0dd55bb5595c01098fb35f94de05695195935887ff7219b13102fa8b2ea11987e0de9790f21b68f51ed1429a865c29760feb97ef24a8ec2e284d75f205492a5e6826aeccc85766dd90ae328ca48f437a982b82527041fa3715eb8ab5cd367e2c22892a25db476fb5fc7050d93128183b8b5a8f834c925da776b33e9a6ba9049f07cffbefe1dfc678c951619d082e66f1e2f33bccd81a9515c5b293ecaf2de43a0b193e594e4d73b819f3f3dbb1eb89d0a88948d924fb0cf993ee94f284db453659fe6aa2a02a12dd032faafca08e52fbe31e8a9164b671b0da6e5fb7ae39b50d089ff296b6d40037dfc39231f8daf1ef17757e460a5386ede16b828a276ecb5d5cc15995360d29b769ae463b6630a988b29e09c4d143896dba059ca697a2e6647f65db433f03cf100f310f336084d0fbb68f121f5f979a836e7a64ac9490fbd5d1694a82b7c5dac1a1f2e574080340b571b943a671c7fb208612f9460921aef1c0892749cf547af2c086924c00fd114929a1c7fb2b2b229e3f2bfad945cd24827108224f78019ca1693009e947d7bc95c0d540494b854d9893aed2812e5db9bf316236921737a606fdce3c231d3d2f51c5563d565b61fb9bc8dd68bfc669a6b81e783abd5c41b146a9ff7df6a949484d664a9d8172a667556201aedaa7c2362ec85654edf3f026b2b69a46e3003630507b196c5364958ad7bd8541b470ff89dc6f0c0f1dbc06c9205f5c11fc49188e09bd788aaa8309b94064be202ca235aa519bcf3560eddbbdeb2117194fbf3e41453ac930c6cd7d0a8de503a30f49b0485245a84c950740661a5d13650765c81a3f01325bff8ada9ae155bfeae1040a86b0983f1fdea5a7a1febc819073d22f039cc59667aa540f9094fc406ea8f595d8f8e515566554cf8b84c539e061b155a1869a3dede11863d27321fbafe54892ec05a973d929635882fccbe0ea452168ad37c83bb841742a048beba325f994009083fdee8925466a990dd9dc3da54027253273104d8f3c3205e2e97253f14f4029d6843eaf8fda626cdc41880fc0fa9023ef000a18121dca938f0aab9263988207da514b0edd2e48eaf7a5cb4b06376e44ba544bb25cafcc8e78f6e25d07625ad8ebfb19595b0f5ca4393215bcb5bfc5378d04624a1eb134e810463c741af4e4285596668629bea0d0dc34c44d2168e14fca44f439ffb5186b9d240ddde5f07a0fd0e8b717a9d6011f44fc43a80a20ac1e94a21a9aa638449e3bb68629029ef7c8bf90d26831371018b1ed224b8f3e23d0ad7c4a3dbe48d8e4b439c4c4e25277b0dea7aba17965dafbeda1b6d40ed31395ad0f25e36bda3f13b009e05557cc411e39374332c73be6859eb781eadbfd1fcbece553fbf58729eda8946864a0a164f32bd5efc5d823906b7f0c1f404df50639be89b451b81bd0e0816c8adf695f92bddaf6cba4128980b8c112f600ca1676290810f1381908aa147b0e6daf51b7260aae120715c595e22bc6bdb64d85d5a034bee11e0262338d8a8918cbcdd54de1b7f3dac8e07da040235b5dc72db6c5075ebab0e99de4a8329b5d17c66b4f3d1455a027a84e91e936e64842a5284e45e112eb92cd9892314b31e90d503e09d45a64431c509eae2b11332698d1a5e2114b7ce2b26ec0a47cb65893a39610c63889b94928bd880a39bf6e811f1fe2b07658b1f70279474ee4f7de8016db1575e06951b2710b8147f6551b6709a78c68ff5f499c011e098209d3d8b3a18673e04968bfb25a6b87c631e54e22267c961cd7b15fffaab5d385a9d2692222f320d1d56b8a39724f34fc7ea6032a79117e1e0062b585f3526906411bb4a42e124aa2a01c38e949c26363985e87cf0e24375bb5e7ab474050847d3311549420461d5e0713795b5b0851fdc2dfab322cd23bb51a0a8e2132533009aad5e6b2b99b40c5d0511eac836a8054fa291249f132ccc3a92fd5f1d02c70723cd6a1ef60d0099d0de09cfb1f17eda6cd1686343051cb619f8aed9d071a19538cd7885c81b24b78a12ad52562b509c1e3f7592e0408f8d58441e3d0a0cf0c5c65bcd8e348b10c0feb6119e9ae0acdc9985755cc92793ded212caa01749082d7eff5f2152f2c341015cfe5588380ba58319fcdd2348f6f0e11b69f6c080e4184b35d6c11924ab0e068e7acd2c15ef23f7ced68e1ceaecff99243aa0b4093c03fc63544116d4e512bd60f09bffc23edb9919da6a2f1d76f48ee0df7c79178a9a495f78b1820724bd19f930232042266a490d8acda1fcf6a56d87749a3564221d8b588eea63601606cd8b87c7f03ba979b69499f98e44a7d9bbace3ef5e240ad01213937960043facd69616509e05e379de488855f77291aa1523c5c65766a5840a38ac256de93ad64e423e951dd23f3b90122d2a6a1b7f2eb2c2eb2d0177dc22e01adec7cfd669218efc69055bfc7f161b5c0a38e0fbe72ff89a668306e0c86a01427cb1ed1be9a4ce7914f5c0b4415c467404f7e013a78d7a503f966aae841513c23d3a0e2e3f814055b1d6f517c6881845602e7d19fbf8d13ac59058170f5e73bd10bd0601afcca7b2b100b8411a9aad2c45ce841a433ba943572e26a24ffc314c7f71034642b8092a8a9fc54b8ef62b415e6583e77484293a7ab5042ccf7bca1e7e3343aa39bb37dc817853bd5d31ad10c08410e287b827249d5c678e89033997cf0049284877d750075e3fae2295435d26d3581a19ea43d1d4931cb597e06d49ad1afe2a22248580b4a6785240680900ab5efe80ba30b7c6918097682ae9296a3447e40f4071f0129c63558c1ed198cfcf0fc186ef62de2e0bddb25c8f136cfe3765c37e75d140f56a89ab69e784f762310da4bfe7081537378116e40887288c8852e9a8bbad37848e2b48a4139a019339d835a22c583008", - "0x3a65787472696e7369635f696e646578": "0x00000000", - "0x3a6772616e6470615f617574686f726974696573": "0x0110094f736c315addde86b7cd5adac7984cd10b1dc187364e92f7ac901a5447609f0100000000000000cee75bb8d02be946f52be595adfd9e4a8ce0343a9894c5e2471429193926765301000000000000005e7084c57d9f04eaa7c22a86d33757cdef9bbcb6607dab1a7c2262dd1293d7ce01000000000000009ee080484f0429022dda72f19bc76cd0b142689d2782c0a68682bba5c5fb156e0100000000000000", - "0x3d9cad2baf702e20b136f4c8900cd8024e7b9012096b41c4eb3aaf947f6ea429": "0x0100", - "0x3db7a24cfdc9de785974746c14a99df94e7b9012096b41c4eb3aaf947f6ea429": "0x0400", - "0x3f1467a096bcd71a5b6a0c8155e208104e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x3fba98689ebed1138735e0e7a5a790ab4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x3fba98689ebed1138735e0e7a5a790abee99a84ccbfb4b82e714617e5e06f6f7": "0xd0070000", - "0x42b50b77ef717947e7043bb52127d6654e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x4da2c41eaffa8e1a791c5d65beeefd1f028685274e698e781f7f2766cba0cc8300000000": "0x1003000000010000000000000002000000abc3f086f5ac20eaab792c75933b2e196307835a61a955be82aa63bc0ff9617a0600000010ce68cabd54aaa5c1e9870f89645ee0f2b3cfafc58089b15387b1e87f59ec3d7e701aa8e4ebae70f627b5cca9726c5ac67133b9295eacdfd5f22a3e44297c4e3b866bd4b14f3f67a056b09c6834375bdc6d0b2d7ae387f8568f67afd1db9b8a1bacf21938aa46cda6a2eca3134629bfb201bf45cc62514672daeb4c55f6b2f332000000000000000000000000000000000000000100000000000000", - "0x4da2c41eaffa8e1a791c5d65beeefd1f4e5747352ae927817a9171156fb3da7f00000000": "0x00", - "0x4da2c41eaffa8e1a791c5d65beeefd1f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", - "0x4da2c41eaffa8e1a791c5d65beeefd1f5762b52ec4f696c1235b20491a567f8500000000": "0x00", - "0x4da2c41eaffa8e1a791c5d65beeefd1fff4a51b74593c3708682038efe5323b5": "0x00000000", - "0x50e709b04947c0cd2f04727ef76e88f64e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x5c0d1176a568c1f92944340dbfed9e9c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x5c0d1176a568c1f92944340dbfed9e9c530ebca703c85910e7164cb7d1c9e47b": "0x9ed7705e3c7da027ba0583a22a3212042f7e715d3c168ba14f1424e2bc111d00", - "0x5f27b51b5ec208ee9cb25b55d8728243308ce9615de0775a82f8a94dc3d285a1": "0x01", - "0x5f27b51b5ec208ee9cb25b55d87282434e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x5f3e4907f716ac89b6347d15ececedca0b6a45321efae92aea15e0740ec7afe7": "0x00000000", - "0x5f3e4907f716ac89b6347d15ececedca138e71612491192d68deab7e6f563fe1": "0x0a000000", - "0x5f3e4907f716ac89b6347d15ececedca28dccb559b95c40168a1b2696581b5a7": "0x00000000000000000000000000000000", - "0x5f3e4907f716ac89b6347d15ececedca3ed14b45ed20d054f05e37e2542cfe705e5f82ad672e896be4c1d81c7e8df384e1ae9667a668825c56e95b0f7d3b1ba2f7539d4c470abb1b": "0x388f7ac281acf72b7782ada96bf0c0d3c09f9276c6f4b7c6271c375fa3a28716", - "0x5f3e4907f716ac89b6347d15ececedca3ed14b45ed20d054f05e37e2542cfe70ad47afdd1ab6146118caf23bb6b8cda7f3f90c1dc2b2f6c2b7143b13c956ef0a12dfe486e83cb758": "0xb2858dfa47e91328dc2f41334228a288d19a853ce0e981cd0115c406f001225f", - "0x5f3e4907f716ac89b6347d15ececedca3ed14b45ed20d054f05e37e2542cfe70ca9d64ddf2c4bc4afa6a6474ec1a9234888f7b20d3978a706d386d0f16344765faa48cb376db331c": "0x18484954a9f3547cf962d6dec822c6353042b56776ec58316a5558d75e304f31", - "0x5f3e4907f716ac89b6347d15ececedca3ed14b45ed20d054f05e37e2542cfe70dd959ae783e3505c005203395fd92c2b411136466e88ef74dd6327c0a6f32b3be7e38d56a2e1324f": "0xf628104fc1f6314effd92cd12cfdfb5ee5c913605174e76ec501797254c61d19", - "0x5f3e4907f716ac89b6347d15ececedca422adb579f1dbf4f3886c5cfa3bb8cc410675ed593218347060fc977d4c87a2318484954a9f3547cf962d6dec822c6353042b56776ec58316a5558d75e304f31": "0xfa6a6474ec1a9234888f7b20d3978a706d386d0f16344765faa48cb376db331c0b00407a10f35a0b00407a10f35a0000", - "0x5f3e4907f716ac89b6347d15ececedca422adb579f1dbf4f3886c5cfa3bb8cc43c9981354ec1409d0ef80e92fad06bf6388f7ac281acf72b7782ada96bf0c0d3c09f9276c6f4b7c6271c375fa3a28716": "0xe4c1d81c7e8df384e1ae9667a668825c56e95b0f7d3b1ba2f7539d4c470abb1b0b00407a10f35a0b00407a10f35a0000", - "0x5f3e4907f716ac89b6347d15ececedca422adb579f1dbf4f3886c5cfa3bb8cc488021e4d172831d344e0aa9a1b9bc22ab2858dfa47e91328dc2f41334228a288d19a853ce0e981cd0115c406f001225f": "0x18caf23bb6b8cda7f3f90c1dc2b2f6c2b7143b13c956ef0a12dfe486e83cb7580b00407a10f35a0b00407a10f35a0000", - "0x5f3e4907f716ac89b6347d15ececedca422adb579f1dbf4f3886c5cfa3bb8cc4b5b6969754a268a0612ebdf3fad88e97f628104fc1f6314effd92cd12cfdfb5ee5c913605174e76ec501797254c61d19": "0x005203395fd92c2b411136466e88ef74dd6327c0a6f32b3be7e38d56a2e1324f0b00407a10f35a0b00407a10f35a0000", - "0x5f3e4907f716ac89b6347d15ececedca42982b9d6c7acc99faa9094c912372c2b4def25cfda6ef3a000000005e5f82ad672e896be4c1d81c7e8df384e1ae9667a668825c56e95b0f7d3b1ba2f7539d4c470abb1b": "0x0b00407a10f35a0b00407a10f35a00", - "0x5f3e4907f716ac89b6347d15ececedca42982b9d6c7acc99faa9094c912372c2b4def25cfda6ef3a00000000ad47afdd1ab6146118caf23bb6b8cda7f3f90c1dc2b2f6c2b7143b13c956ef0a12dfe486e83cb758": "0x0b00407a10f35a0b00407a10f35a00", - "0x5f3e4907f716ac89b6347d15ececedca42982b9d6c7acc99faa9094c912372c2b4def25cfda6ef3a00000000ca9d64ddf2c4bc4afa6a6474ec1a9234888f7b20d3978a706d386d0f16344765faa48cb376db331c": "0x0b00407a10f35a0b00407a10f35a00", - "0x5f3e4907f716ac89b6347d15ececedca42982b9d6c7acc99faa9094c912372c2b4def25cfda6ef3a00000000dd959ae783e3505c005203395fd92c2b411136466e88ef74dd6327c0a6f32b3be7e38d56a2e1324f": "0x0b00407a10f35a0b00407a10f35a00", - "0x5f3e4907f716ac89b6347d15ececedca487df464e44a534ba6b0cbb32407b587": "0x0000000000", - "0x5f3e4907f716ac89b6347d15ececedca4e7b9012096b41c4eb3aaf947f6ea429": "0x0d00", - "0x5f3e4907f716ac89b6347d15ececedca5579297f4dfb9609e7e4c2ebab9ce40a": "0x10fa6a6474ec1a9234888f7b20d3978a706d386d0f16344765faa48cb376db331c005203395fd92c2b411136466e88ef74dd6327c0a6f32b3be7e38d56a2e1324fe4c1d81c7e8df384e1ae9667a668825c56e95b0f7d3b1ba2f7539d4c470abb1b18caf23bb6b8cda7f3f90c1dc2b2f6c2b7143b13c956ef0a12dfe486e83cb758", - "0x5f3e4907f716ac89b6347d15ececedca666fdcbb473985b3ac933d13f4acff8d": "0x00000000000000000000000000000000", - "0x5f3e4907f716ac89b6347d15ececedca682db92dde20a10d96d00ff0e9e221c0b4def25cfda6ef3a000000005e5f82ad672e896be4c1d81c7e8df384e1ae9667a668825c56e95b0f7d3b1ba2f7539d4c470abb1b": "0x0000", - "0x5f3e4907f716ac89b6347d15ececedca682db92dde20a10d96d00ff0e9e221c0b4def25cfda6ef3a00000000ad47afdd1ab6146118caf23bb6b8cda7f3f90c1dc2b2f6c2b7143b13c956ef0a12dfe486e83cb758": "0x0000", - "0x5f3e4907f716ac89b6347d15ececedca682db92dde20a10d96d00ff0e9e221c0b4def25cfda6ef3a00000000ca9d64ddf2c4bc4afa6a6474ec1a9234888f7b20d3978a706d386d0f16344765faa48cb376db331c": "0x0000", - "0x5f3e4907f716ac89b6347d15ececedca682db92dde20a10d96d00ff0e9e221c0b4def25cfda6ef3a00000000dd959ae783e3505c005203395fd92c2b411136466e88ef74dd6327c0a6f32b3be7e38d56a2e1324f": "0x0000", - "0x5f3e4907f716ac89b6347d15ececedca6ddc7809c6da9bb6093ee22e0fda4ba8": "0x04000000", - "0x5f3e4907f716ac89b6347d15ececedca88dcde934c658227ee1dfafcd6e169035e5f82ad672e896be4c1d81c7e8df384e1ae9667a668825c56e95b0f7d3b1ba2f7539d4c470abb1b": "0x0000", - "0x5f3e4907f716ac89b6347d15ececedca88dcde934c658227ee1dfafcd6e16903ad47afdd1ab6146118caf23bb6b8cda7f3f90c1dc2b2f6c2b7143b13c956ef0a12dfe486e83cb758": "0x0000", - "0x5f3e4907f716ac89b6347d15ececedca88dcde934c658227ee1dfafcd6e16903ca9d64ddf2c4bc4afa6a6474ec1a9234888f7b20d3978a706d386d0f16344765faa48cb376db331c": "0x0000", - "0x5f3e4907f716ac89b6347d15ececedca88dcde934c658227ee1dfafcd6e16903dd959ae783e3505c005203395fd92c2b411136466e88ef74dd6327c0a6f32b3be7e38d56a2e1324f": "0x0000", - "0x5f3e4907f716ac89b6347d15ececedca8bde0a0ea8864605e3b68ed9cb2da01bb4def25cfda6ef3a000000005e5f82ad672e896be4c1d81c7e8df384e1ae9667a668825c56e95b0f7d3b1ba2f7539d4c470abb1b": "0x0b00407a10f35a0b00407a10f35a00", - "0x5f3e4907f716ac89b6347d15ececedca8bde0a0ea8864605e3b68ed9cb2da01bb4def25cfda6ef3a00000000ad47afdd1ab6146118caf23bb6b8cda7f3f90c1dc2b2f6c2b7143b13c956ef0a12dfe486e83cb758": "0x0b00407a10f35a0b00407a10f35a00", - "0x5f3e4907f716ac89b6347d15ececedca8bde0a0ea8864605e3b68ed9cb2da01bb4def25cfda6ef3a00000000ca9d64ddf2c4bc4afa6a6474ec1a9234888f7b20d3978a706d386d0f16344765faa48cb376db331c": "0x0b00407a10f35a0b00407a10f35a00", - "0x5f3e4907f716ac89b6347d15ececedca8bde0a0ea8864605e3b68ed9cb2da01bb4def25cfda6ef3a00000000dd959ae783e3505c005203395fd92c2b411136466e88ef74dd6327c0a6f32b3be7e38d56a2e1324f": "0x0b00407a10f35a0b00407a10f35a00", - "0x5f3e4907f716ac89b6347d15ececedca9220e172bed316605f73f1ff7b4ade985e5f82ad672e896be4c1d81c7e8df384e1ae9667a668825c56e95b0f7d3b1ba2f7539d4c470abb1b": "0x00", - "0x5f3e4907f716ac89b6347d15ececedca9220e172bed316605f73f1ff7b4ade98ad47afdd1ab6146118caf23bb6b8cda7f3f90c1dc2b2f6c2b7143b13c956ef0a12dfe486e83cb758": "0x00", - "0x5f3e4907f716ac89b6347d15ececedca9220e172bed316605f73f1ff7b4ade98ca9d64ddf2c4bc4afa6a6474ec1a9234888f7b20d3978a706d386d0f16344765faa48cb376db331c": "0x00", - "0x5f3e4907f716ac89b6347d15ececedca9220e172bed316605f73f1ff7b4ade98dd959ae783e3505c005203395fd92c2b411136466e88ef74dd6327c0a6f32b3be7e38d56a2e1324f": "0x00", - "0x5f3e4907f716ac89b6347d15ececedcaa141c4fe67c2d11f4a10c6aca7a79a04b4def25cfda6ef3a00000000": "0x0000e941cc6b01000000000000000000", - "0x5f3e4907f716ac89b6347d15ececedcaad811cd65a470ddc5f1d628ff0550982b4def25cfda6ef3a00000000": "0x00000000", - "0x5f3e4907f716ac89b6347d15ececedcab49a2738eeb30896aacb8b3fb46471bd": "0x04000000", - "0x5f3e4907f716ac89b6347d15ececedcac0d39ff577af2cc6b67ac3641fa9c4e7": "0x01000000", - "0x5f3e4907f716ac89b6347d15ececedcac29a0310e1bb45d20cace77ccb62c97d": "0x00e1f505", - "0x5f3e4907f716ac89b6347d15ececedcaea07de2b8f010516dca3f7ef52f7ac5a": "0x040000000000000000", - "0x5f3e4907f716ac89b6347d15ececedcaed441ceb81326c56263efbb60c95c2e4": "0x00000000000000000000000000000000", - "0x5f3e4907f716ac89b6347d15ececedcaf7dad0317324aecae8744b87fc95f2f3": "0x02", - "0x5f3e4907f716ac89b6347d15ececedcafab86d26e629e39b4903db94786fac74": "0xffffffffffffffff0000000000000000", - "0x5f9cc45b7a00c5899361e1c6099678dc4e7b9012096b41c4eb3aaf947f6ea429": "0x0400", - "0x5f9cc45b7a00c5899361e1c6099678dc8a2d09463effcc78a22d75b9cb87dffc": "0x0000000000000000", - "0x5f9cc45b7a00c5899361e1c6099678dcd47cb8f5328af743ddfb361e7180e7fcbb1bdbcacd6ac9340000000000000000": "0x00000000", - "0x63f78c98723ddc9073523ef3beefda0c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x6a0da05ca59913bc38a8630590f2627c2a351b6a99a5b21324516e668bb86a57": "0x00", - "0x6a0da05ca59913bc38a8630590f2627c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x6ac983d82528bf1595ab26438ae5b2cf4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x6cf4040bbce30824850f1a4823d8c65f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x7474449cca95dc5d0c00e71735a6d17d4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", - "0x74dd702da46f77d7acf77f5a48d4af7d4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x74dd702da46f77d7acf77f5a48d4af7d62556a85fcb7c61b2c6c750924846b155e5f82ad672e896be4c1d81c7e8df384e1ae9667a668825c56e95b0f7d3b1ba2f7539d4c470abb1b": "0xe4c1d81c7e8df384e1ae9667a668825c56e95b0f7d3b1ba2f7539d4c470abb1b01005203395fd92c2b411136466e88ef74dd6327c0a6f32b3be7e38d56a2e1324f0118caf23bb6b8cda7f3f90c1dc2b2f6c2b7143b13c956ef0a12dfe486e83cb758e7f10262de5b000000407a10f35a0000", - "0x74dd702da46f77d7acf77f5a48d4af7d62556a85fcb7c61b2c6c750924846b15ad47afdd1ab6146118caf23bb6b8cda7f3f90c1dc2b2f6c2b7143b13c956ef0a12dfe486e83cb758": "0x18caf23bb6b8cda7f3f90c1dc2b2f6c2b7143b13c956ef0a12dfe486e83cb75801e4c1d81c7e8df384e1ae9667a668825c56e95b0f7d3b1ba2f7539d4c470abb1b00e7f10262de5b000000407a10f35a0000", - "0x74dd702da46f77d7acf77f5a48d4af7d62556a85fcb7c61b2c6c750924846b15ca9d64ddf2c4bc4afa6a6474ec1a9234888f7b20d3978a706d386d0f16344765faa48cb376db331c": "0xfa6a6474ec1a9234888f7b20d3978a706d386d0f16344765faa48cb376db331c0001005203395fd92c2b411136466e88ef74dd6327c0a6f32b3be7e38d56a2e1324fe7f10262de5b000000407a10f35a0000", - "0x74dd702da46f77d7acf77f5a48d4af7d62556a85fcb7c61b2c6c750924846b15dd959ae783e3505c005203395fd92c2b411136466e88ef74dd6327c0a6f32b3be7e38d56a2e1324f": "0x005203395fd92c2b411136466e88ef74dd6327c0a6f32b3be7e38d56a2e1324f01fa6a6474ec1a9234888f7b20d3978a706d386d0f16344765faa48cb376db331c01e4c1d81c7e8df384e1ae9667a668825c56e95b0f7d3b1ba2f7539d4c470abb1be7f10262de5b000000407a10f35a0000", - "0x74dd702da46f77d7acf77f5a48d4af7d7a6dc62e324093ba1331bf49fdb2f24a": "0x04000000", - "0x74dd702da46f77d7acf77f5a48d4af7de5c03730c8f59f00941607850b6633d8dec683721ac60452e7f10262de5b0000": "0x01fa6a6474ec1a9234888f7b20d3978a706d386d0f16344765faa48cb376db331c0118caf23bb6b8cda7f3f90c1dc2b2f6c2b7143b13c956ef0a12dfe486e83cb758", - "0x7a6d38deaa01cb6e76ee69889f1696272be9a4e88368a2188d2b9100a9f3cd43": "0x00000000000000000000000000000000", - "0x7a6d38deaa01cb6e76ee69889f16962730256ea2c545a3e5e3744665ffb2ed28": "0x00020000", - "0x7a6d38deaa01cb6e76ee69889f1696273f0d64e1907361c689834a9c1cb0fbe0": "0x20000000", - "0x7a6d38deaa01cb6e76ee69889f16962749d67997de33812a1cc37310f765b82e": "0x00000000000000000000000000000000", - "0x7a6d38deaa01cb6e76ee69889f1696274e7b9012096b41c4eb3aaf947f6ea429": "0x0400", - "0x7a6d38deaa01cb6e76ee69889f169627ba93302f3b868c50785e6ade45c6a1d8": "0x10000000", - "0x94eadf0156a8ad5156507773d0471e4a16973e1142f5bd30d9464076794007db": "0x00", - "0x94eadf0156a8ad5156507773d0471e4a1e8de4295679f32032acb318db364135": "0x00", - "0x94eadf0156a8ad5156507773d0471e4a4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0x94eadf0156a8ad5156507773d0471e4a64fb6e378f53d72f7859ad0e6b6d8810": "0x0000000000", - "0x94eadf0156a8ad5156507773d0471e4a9ce0310edffce7a01a96c2039f92dd10": "0x01000000", - "0x94eadf0156a8ad5156507773d0471e4ab8ebad86f546c7e0b135a4212aace339": "0x00", - "0xa2ce73642c549ae79c14f0a671cf45f94e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0xb341e3a63e58a188839b242d17f8c9f82586833f834350b4d435d5fd269ecc8b": "0x1003000000010000000000000002000000", - "0xb341e3a63e58a188839b242d17f8c9f84e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0xb341e3a63e58a188839b242d17f8c9f87a50c904b368210021127f9238883a6e": "0x10ce68cabd54aaa5c1e9870f89645ee0f2b3cfafc58089b15387b1e87f59ec3d7e701aa8e4ebae70f627b5cca9726c5ac67133b9295eacdfd5f22a3e44297c4e3b866bd4b14f3f67a056b09c6834375bdc6d0b2d7ae387f8568f67afd1db9b8a1bacf21938aa46cda6a2eca3134629bfb201bf45cc62514672daeb4c55f6b2f332", - "0xb341e3a63e58a188839b242d17f8c9f8b5cab3380174032968897a4c3ce57c0a": "0x00000000", - "0xc2261276cc9d1f8598ea4b6a74b15c2f218f26c73add634897550b4003b26bc69a0d9ba64d584162e7d1fc85d6d19ad1005203395fd92c2b411136466e88ef74dd6327c0a6f32b3be7e38d56a2e1324f": "0x047374616b696e672000407a10f35a0000000000000000000002", - "0xc2261276cc9d1f8598ea4b6a74b15c2f218f26c73add634897550b4003b26bc6a1e0293801ecda3bccddad286cfce679fa6a6474ec1a9234888f7b20d3978a706d386d0f16344765faa48cb376db331c": "0x047374616b696e672000407a10f35a0000000000000000000002", - "0xc2261276cc9d1f8598ea4b6a74b15c2f218f26c73add634897550b4003b26bc6e39abd9d6d25130391c9ff6fc64a35ef18caf23bb6b8cda7f3f90c1dc2b2f6c2b7143b13c956ef0a12dfe486e83cb758": "0x047374616b696e672000407a10f35a0000000000000000000002", - "0xc2261276cc9d1f8598ea4b6a74b15c2f218f26c73add634897550b4003b26bc6f4c6172605184c65d6c162727408dc0be4c1d81c7e8df384e1ae9667a668825c56e95b0f7d3b1ba2f7539d4c470abb1b": "0x047374616b696e672000407a10f35a0000000000000000000002", - "0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", - "0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x0040c7f9727de20d0000000000000000", - "0xca32a41f4b3ed515863dc0a38697f84e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0xcd710b30bd2eab0352ddcc26417aa1944e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0xcd710b30bd2eab0352ddcc26417aa1949f4993f016e2d2f8e5f43be7bb259486": "0x00", - "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb35e5f82ad672e896be4c1d81c7e8df384e1ae9667a668825c56e95b0f7d3b1ba2f7539d4c470abb1b": "0x5e7084c57d9f04eaa7c22a86d33757cdef9bbcb6607dab1a7c2262dd1293d7cee6b8162c3e767f8e61892f7fcd06d27041d806e5e0335c59dcdafa5c8e181c5bded28f03696a0c9f9dec223f3cbc44c4895d8b243ebe5cee12f9f02bf0c5043cacf21938aa46cda6a2eca3134629bfb201bf45cc62514672daeb4c55f6b2f332b2174a8685bb3c874484978b71c55b45c4057e290c57c0a076ba9aeb7b6618025ed9fdbd8dffeb5324935a7fafc536de96d62abee0a05d7eefa961c1cf3de266", - "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3ad47afdd1ab6146118caf23bb6b8cda7f3f90c1dc2b2f6c2b7143b13c956ef0a12dfe486e83cb758": "0xcee75bb8d02be946f52be595adfd9e4a8ce0343a9894c5e2471429193926765322371e9715d00b3a21c9a899ba3eafd11f5143b821b159b864025ba1eabdb631ce83a2b5c733f98b4018856a1fb0bdf0138dd883cc93a883f97de48b762d6b12701aa8e4ebae70f627b5cca9726c5ac67133b9295eacdfd5f22a3e44297c4e3bd815b1a9dc0077cdf10a4cd3bedc7dd0b5de4b873f9932ae8f8b9d147f43d3000e93248544c963f34bb9cde63c97f85ef7a1939d3c9075907b26edf368fe846e", - "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3ca9d64ddf2c4bc4afa6a6474ec1a9234888f7b20d3978a706d386d0f16344765faa48cb376db331c": "0x9ee080484f0429022dda72f19bc76cd0b142689d2782c0a68682bba5c5fb156e585a72774ca9465ba0e7407e4e66d239febbe906cbf090169b6cfa15dd44e5779e3e67bfc0daed31db022fce484b2cf0d757e9aafded1988293da74301275b38ce68cabd54aaa5c1e9870f89645ee0f2b3cfafc58089b15387b1e87f59ec3d7e62f0e85adce6f9782769ae007691df98557e3a04452ac0be90309f88f513f55dca24971e2ec596d510c673f4f8d36d0a8a407b59ffd0643f621369973a335656", - "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3dd959ae783e3505c005203395fd92c2b411136466e88ef74dd6327c0a6f32b3be7e38d56a2e1324f": "0x094f736c315addde86b7cd5adac7984cd10b1dc187364e92f7ac901a5447609f006078f6e6a00db1f40097f0d07953008b04cda71ad831e70f37e93eb2b404314a611c52c43142e11767e4443eb56b908babae266b4f446271d11ffaaafbb16e866bd4b14f3f67a056b09c6834375bdc6d0b2d7ae387f8568f67afd1db9b8a1bca5ff4e343aa58559db1467ab84f5241f95baf8fd4bbc4d90856089e74d32669b691bfd2cd584abd1531b7deff6d0e34893960b59ae550348c33abd76af4cb49", - "0xcec5070d609dd3497f72bde07fc96ba04e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195005b6dcd704a27908696d6f6e804a611c52c43142e11767e4443eb56b908babae266b4f446271d11ffaaafbb16e": "0x005203395fd92c2b411136466e88ef74dd6327c0a6f32b3be7e38d56a2e1324f", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19500a7d72b76c2ec9b06173676e8062f0e85adce6f9782769ae007691df98557e3a04452ac0be90309f88f513f55d": "0xfa6a6474ec1a9234888f7b20d3978a706d386d0f16344765faa48cb376db331c", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950110db7b3540f726061756469805ed9fdbd8dffeb5324935a7fafc536de96d62abee0a05d7eefa961c1cf3de266": "0xe4c1d81c7e8df384e1ae9667a668825c56e95b0f7d3b1ba2f7539d4c470abb1b", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195014a98987c6f4654c6261626580e6b8162c3e767f8e61892f7fcd06d27041d806e5e0335c59dcdafa5c8e181c5b": "0xe4c1d81c7e8df384e1ae9667a668825c56e95b0f7d3b1ba2f7539d4c470abb1b", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950270bb04a2a9e106e696d6f6e80ce83a2b5c733f98b4018856a1fb0bdf0138dd883cc93a883f97de48b762d6b12": "0x18caf23bb6b8cda7f3f90c1dc2b2f6c2b7143b13c956ef0a12dfe486e83cb758", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195031063675260bb8076261626580006078f6e6a00db1f40097f0d07953008b04cda71ad831e70f37e93eb2b40431": "0x005203395fd92c2b411136466e88ef74dd6327c0a6f32b3be7e38d56a2e1324f", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950413ab9d61fa646a76772616e80094f736c315addde86b7cd5adac7984cd10b1dc187364e92f7ac901a5447609f": "0x005203395fd92c2b411136466e88ef74dd6327c0a6f32b3be7e38d56a2e1324f", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19505404aed8a9c40e507061726180866bd4b14f3f67a056b09c6834375bdc6d0b2d7ae387f8568f67afd1db9b8a1b": "0x005203395fd92c2b411136466e88ef74dd6327c0a6f32b3be7e38d56a2e1324f", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950597968238adfa9af6772616e80cee75bb8d02be946f52be595adfd9e4a8ce0343a9894c5e24714291939267653": "0x18caf23bb6b8cda7f3f90c1dc2b2f6c2b7143b13c956ef0a12dfe486e83cb758", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19505b639c79d4e8330c696d6f6e809e3e67bfc0daed31db022fce484b2cf0d757e9aafded1988293da74301275b38": "0xfa6a6474ec1a9234888f7b20d3978a706d386d0f16344765faa48cb376db331c", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195061ccbc794cd1e95c6175646980b691bfd2cd584abd1531b7deff6d0e34893960b59ae550348c33abd76af4cb49": "0x005203395fd92c2b411136466e88ef74dd6327c0a6f32b3be7e38d56a2e1324f", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19507f0621f339620f26696d6f6e80ded28f03696a0c9f9dec223f3cbc44c4895d8b243ebe5cee12f9f02bf0c5043c": "0xe4c1d81c7e8df384e1ae9667a668825c56e95b0f7d3b1ba2f7539d4c470abb1b", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950949420096ce6b2176772616e805e7084c57d9f04eaa7c22a86d33757cdef9bbcb6607dab1a7c2262dd1293d7ce": "0xe4c1d81c7e8df384e1ae9667a668825c56e95b0f7d3b1ba2f7539d4c470abb1b", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19509af54e7103657a4c7061726180701aa8e4ebae70f627b5cca9726c5ac67133b9295eacdfd5f22a3e44297c4e3b": "0x18caf23bb6b8cda7f3f90c1dc2b2f6c2b7143b13c956ef0a12dfe486e83cb758", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950a359a745f65c1e456261626580585a72774ca9465ba0e7407e4e66d239febbe906cbf090169b6cfa15dd44e577": "0xfa6a6474ec1a9234888f7b20d3978a706d386d0f16344765faa48cb376db331c", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950af162637344b36a96173676e80d815b1a9dc0077cdf10a4cd3bedc7dd0b5de4b873f9932ae8f8b9d147f43d300": "0x18caf23bb6b8cda7f3f90c1dc2b2f6c2b7143b13c956ef0a12dfe486e83cb758", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950b229c28236e354a26173676e80b2174a8685bb3c874484978b71c55b45c4057e290c57c0a076ba9aeb7b661802": "0xe4c1d81c7e8df384e1ae9667a668825c56e95b0f7d3b1ba2f7539d4c470abb1b", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950b6b8ce596a13561b7061726180acf21938aa46cda6a2eca3134629bfb201bf45cc62514672daeb4c55f6b2f332": "0xe4c1d81c7e8df384e1ae9667a668825c56e95b0f7d3b1ba2f7539d4c470abb1b", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950bee8d7df4d460d9d61756469800e93248544c963f34bb9cde63c97f85ef7a1939d3c9075907b26edf368fe846e": "0x18caf23bb6b8cda7f3f90c1dc2b2f6c2b7143b13c956ef0a12dfe486e83cb758", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950c02fd5bdeb0ec6f06175646980ca24971e2ec596d510c673f4f8d36d0a8a407b59ffd0643f621369973a335656": "0xfa6a6474ec1a9234888f7b20d3978a706d386d0f16344765faa48cb376db331c", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950d9ebe2452f14a591626162658022371e9715d00b3a21c9a899ba3eafd11f5143b821b159b864025ba1eabdb631": "0x18caf23bb6b8cda7f3f90c1dc2b2f6c2b7143b13c956ef0a12dfe486e83cb758", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950e322099b0a5bb5836772616e809ee080484f0429022dda72f19bc76cd0b142689d2782c0a68682bba5c5fb156e": "0xfa6a6474ec1a9234888f7b20d3978a706d386d0f16344765faa48cb376db331c", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950e6ddf4dc42f9b1b66173676e80ca5ff4e343aa58559db1467ab84f5241f95baf8fd4bbc4d90856089e74d32669": "0x005203395fd92c2b411136466e88ef74dd6327c0a6f32b3be7e38d56a2e1324f", - "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950f8bc7112b190dae97061726180ce68cabd54aaa5c1e9870f89645ee0f2b3cfafc58089b15387b1e87f59ec3d7e": "0xfa6a6474ec1a9234888f7b20d3978a706d386d0f16344765faa48cb376db331c", - "0xcec5070d609dd3497f72bde07fc96ba088dcde934c658227ee1dfafcd6e16903": "0x10005203395fd92c2b411136466e88ef74dd6327c0a6f32b3be7e38d56a2e1324f18caf23bb6b8cda7f3f90c1dc2b2f6c2b7143b13c956ef0a12dfe486e83cb758e4c1d81c7e8df384e1ae9667a668825c56e95b0f7d3b1ba2f7539d4c470abb1bfa6a6474ec1a9234888f7b20d3978a706d386d0f16344765faa48cb376db331c", - "0xcec5070d609dd3497f72bde07fc96ba0e0cdd062e6eaf24295ad4ccfc41d4609": "0x10005203395fd92c2b411136466e88ef74dd6327c0a6f32b3be7e38d56a2e1324f094f736c315addde86b7cd5adac7984cd10b1dc187364e92f7ac901a5447609f006078f6e6a00db1f40097f0d07953008b04cda71ad831e70f37e93eb2b404314a611c52c43142e11767e4443eb56b908babae266b4f446271d11ffaaafbb16e866bd4b14f3f67a056b09c6834375bdc6d0b2d7ae387f8568f67afd1db9b8a1bca5ff4e343aa58559db1467ab84f5241f95baf8fd4bbc4d90856089e74d32669b691bfd2cd584abd1531b7deff6d0e34893960b59ae550348c33abd76af4cb4918caf23bb6b8cda7f3f90c1dc2b2f6c2b7143b13c956ef0a12dfe486e83cb758cee75bb8d02be946f52be595adfd9e4a8ce0343a9894c5e2471429193926765322371e9715d00b3a21c9a899ba3eafd11f5143b821b159b864025ba1eabdb631ce83a2b5c733f98b4018856a1fb0bdf0138dd883cc93a883f97de48b762d6b12701aa8e4ebae70f627b5cca9726c5ac67133b9295eacdfd5f22a3e44297c4e3bd815b1a9dc0077cdf10a4cd3bedc7dd0b5de4b873f9932ae8f8b9d147f43d3000e93248544c963f34bb9cde63c97f85ef7a1939d3c9075907b26edf368fe846ee4c1d81c7e8df384e1ae9667a668825c56e95b0f7d3b1ba2f7539d4c470abb1b5e7084c57d9f04eaa7c22a86d33757cdef9bbcb6607dab1a7c2262dd1293d7cee6b8162c3e767f8e61892f7fcd06d27041d806e5e0335c59dcdafa5c8e181c5bded28f03696a0c9f9dec223f3cbc44c4895d8b243ebe5cee12f9f02bf0c5043cacf21938aa46cda6a2eca3134629bfb201bf45cc62514672daeb4c55f6b2f332b2174a8685bb3c874484978b71c55b45c4057e290c57c0a076ba9aeb7b6618025ed9fdbd8dffeb5324935a7fafc536de96d62abee0a05d7eefa961c1cf3de266fa6a6474ec1a9234888f7b20d3978a706d386d0f16344765faa48cb376db331c9ee080484f0429022dda72f19bc76cd0b142689d2782c0a68682bba5c5fb156e585a72774ca9465ba0e7407e4e66d239febbe906cbf090169b6cfa15dd44e5779e3e67bfc0daed31db022fce484b2cf0d757e9aafded1988293da74301275b38ce68cabd54aaa5c1e9870f89645ee0f2b3cfafc58089b15387b1e87f59ec3d7e62f0e85adce6f9782769ae007691df98557e3a04452ac0be90309f88f513f55dca24971e2ec596d510c673f4f8d36d0a8a407b59ffd0643f621369973a335656", - "0xd57bce545fb382c34570e5dfbf338f5e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0xd5c41b52a371aa36c9254ce34324f2a54e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0xd5e1a2fa16732ce6906189438c0a82c64e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0xd8bbe27baf3aa64bb483afabc240f68e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0xd8f314b7f4e6b095f0f8ee4656a448254e7b9012096b41c4eb3aaf947f6ea429": "0x0100", - "0xede8e4fdc3c8b556f0ce2f77fc2575e34e7b9012096b41c4eb3aaf947f6ea429": "0x0100", - "0xf0c365c3cf59d671eb72da0e7a4113c44e7b9012096b41c4eb3aaf947f6ea429": "0x0000", - "0xf5207f03cfdce586301014700e2c25934e7b9012096b41c4eb3aaf947f6ea429": "0x0100", - "0xf5a4963e4efb097983d7a693b0c1ee454e7b9012096b41c4eb3aaf947f6ea429": "0x0100" - }, - "childrenDefault": {} - } - } -} diff --git a/polkadot/node/service/src/chain_spec.rs b/polkadot/node/service/src/chain_spec.rs index d377a75f1069b52edbcd02a3c73f67d15be3d3a4..fe360e7b8c769204972af5fceb2b0c788dc29d08 100644 --- a/polkadot/node/service/src/chain_spec.rs +++ b/polkadot/node/service/src/chain_spec.rs @@ -16,16 +16,6 @@ //! Polkadot chain configurations. -#[cfg(feature = "westend-native")] -use pallet_staking::Forcing; -use polkadot_primitives::{AccountId, AccountPublic, AssignmentId, ValidatorId}; -use sc_consensus_grandpa::AuthorityId as GrandpaId; -use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; -use sp_consensus_babe::AuthorityId as BabeId; -use sp_consensus_beefy::ecdsa_crypto::AuthorityId as BeefyId; - -#[cfg(feature = "westend-native")] -use polkadot_primitives::SchedulerParams; #[cfg(feature = "rococo-native")] use rococo_runtime as rococo; use sc_chain_spec::ChainSpecExtension; @@ -34,14 +24,12 @@ use sc_chain_spec::ChainType; #[cfg(any(feature = "westend-native", feature = "rococo-native"))] use sc_telemetry::TelemetryEndpoints; use serde::{Deserialize, Serialize}; -use sp_core::{sr25519, Pair, Public}; -use sp_runtime::traits::IdentifyAccount; -#[cfg(feature = "westend-native")] -use sp_runtime::Perbill; #[cfg(feature = "westend-native")] use westend_runtime as westend; -#[cfg(feature = "westend-native")] -use westend_runtime_constants::currency::UNITS as WND; + +use polkadot_primitives::{AccountId, AccountPublic}; +use sp_core::{Pair, Public}; +use sp_runtime::traits::IdentifyAccount; #[cfg(feature = "westend-native")] const WESTEND_STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/"; @@ -110,269 +98,6 @@ pub fn rococo_config() -> Result { RococoChainSpec::from_json_bytes(&include_bytes!("../chain-specs/rococo.json")[..]) } -/// This is a temporary testnet that uses the same runtime as rococo. -pub fn wococo_config() -> Result { - RococoChainSpec::from_json_bytes(&include_bytes!("../chain-specs/wococo.json")[..]) -} - -/// The default parachains host configuration. -#[cfg(feature = "westend-native")] -fn default_parachains_host_configuration( -) -> polkadot_runtime_parachains::configuration::HostConfiguration -{ - use polkadot_primitives::{ - node_features::FeatureIndex, ApprovalVotingParams, AsyncBackingParams, MAX_CODE_SIZE, - MAX_POV_SIZE, - }; - - polkadot_runtime_parachains::configuration::HostConfiguration { - validation_upgrade_cooldown: 2u32, - validation_upgrade_delay: 2, - code_retention_period: 1200, - max_code_size: MAX_CODE_SIZE, - max_pov_size: MAX_POV_SIZE, - max_head_data_size: 32 * 1024, - max_upward_queue_count: 8, - max_upward_queue_size: 1024 * 1024, - max_downward_message_size: 1024 * 1024, - max_upward_message_size: 50 * 1024, - max_upward_message_num_per_candidate: 5, - hrmp_sender_deposit: 0, - hrmp_recipient_deposit: 0, - hrmp_channel_max_capacity: 8, - hrmp_channel_max_total_size: 8 * 1024, - hrmp_max_parachain_inbound_channels: 4, - hrmp_channel_max_message_size: 1024 * 1024, - hrmp_max_parachain_outbound_channels: 4, - hrmp_max_message_num_per_candidate: 5, - dispute_period: 6, - no_show_slots: 2, - n_delay_tranches: 25, - needed_approvals: 2, - 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) | - 1u8 << (FeatureIndex::EnableAssignmentsV2 as usize), - ), - scheduler_params: SchedulerParams { - lookahead: 2, - group_rotation_frequency: 20, - paras_availability_period: 4, - ..Default::default() - }, - approval_voting_params: ApprovalVotingParams { max_approval_coalesce_count: 5 }, - ..Default::default() - } -} - -#[cfg(feature = "westend-native")] -#[test] -fn default_parachains_host_configuration_is_consistent() { - default_parachains_host_configuration().panic_if_not_consistent(); -} - -#[cfg(feature = "westend-native")] -fn westend_session_keys( - babe: BabeId, - grandpa: GrandpaId, - para_validator: ValidatorId, - para_assignment: AssignmentId, - authority_discovery: AuthorityDiscoveryId, - beefy: BeefyId, -) -> westend::SessionKeys { - westend::SessionKeys { - babe, - grandpa, - para_validator, - para_assignment, - authority_discovery, - beefy, - } -} - -#[cfg(feature = "westend-native")] -fn westend_staging_testnet_config_genesis() -> serde_json::Value { - use hex_literal::hex; - use sp_core::crypto::UncheckedInto; - - // Following keys are used in genesis config for development chains. - // DO NOT use them in production chains as the secret seed is public. - // - // SECRET_SEED="slow awkward present example safe bundle science ocean cradle word tennis earn" - // subkey inspect -n polkadot "$SECRET_SEED" - let endowed_accounts = vec![ - // 15S75FkhCWEowEGfxWwVfrW3LQuy8w8PNhVmrzfsVhCMjUh1 - hex!["c416837e232d9603e83162ef4bda08e61580eeefe60fe92fc044aa508559ae42"].into(), - ]; - // SECRET=$SECRET_SEED ./scripts/prepare-test-net.sh 4 - let initial_authorities: Vec<( - AccountId, - AccountId, - BabeId, - GrandpaId, - ValidatorId, - AssignmentId, - AuthorityDiscoveryId, - BeefyId, - )> = vec![ - ( - //5EvydUTtHvt39Khac3mMxNPgzcfu49uPDzUs3TL7KEzyrwbw - hex!["7ecfd50629cdd246649959d88d490b31508db511487e111a52a392e6e458f518"].into(), - //5HQyX5gyy77m9QLXguAhiwjTArHYjYspeY98dYDu1JDetfZg - hex!["eca2cca09bdc66a7e6d8c3d9499a0be2ad4690061be8a9834972e17d13d2fe7e"].into(), - //5G13qYRudTyttwTJvHvnwp8StFtcfigyPnwfD4v7LNopsnX4 - hex!["ae27367cb77850fb195fe1f9c60b73210409e68c5ad953088070f7d8513d464c"] - .unchecked_into(), - //5Eb7wM65PNgtY6e33FEAzYtU5cRTXt6WQvZTnzaKQwkVcABk - hex!["6faae44b21c6f2681a7f60df708e9f79d340f7d441d28bd987fab8d05c6487e8"] - .unchecked_into(), - //5FqMLAgygdX9UqzukDp15Uid9PAKdFAR621U7xtp5ut2NfrW - hex!["a6c1a5b501985a83cb1c37630c5b41e6b0a15b3675b2fd94694758e6cfa6794d"] - .unchecked_into(), - //5DhXAV75BKvF9o447ikWqLttyL2wHtLMFSX7GrsKF9Ny61Ta - hex!["485051748ab9c15732f19f3fbcf1fd00a6d9709635f084505107fbb059c33d2f"] - .unchecked_into(), - //5GNHfmrtWLTawnGCmc39rjAEiW97vKvE7DGePYe4am5JtE4i - hex!["be59ed75a72f7b47221ce081ba4262cf2e1ea7867e30e0b3781822f942b97677"] - .unchecked_into(), - //5DA6Z8RUF626stn94aTRBCeobDCYcFbU7Pdk4Tz1R9vA8B8F - hex!["0207e43990799e1d02b0507451e342a1240ff836ea769c57297589a5fd072ad8f4"] - .unchecked_into(), - ), - ( - //5DFpvDUdCgw54E3E357GR1PyJe3Ft9s7Qyp7wbELAoJH9RQa - hex!["34b7b3efd35fcc3c1926ca065381682b1af29b57dabbcd091042c6de1d541b7d"].into(), - //5DZSSsND5wCjngvyXv27qvF3yPzt3MCU8rWnqNy4imqZmjT8 - hex!["4226796fa792ac78875e023ff2e30e3c2cf79f0b7b3431254cd0f14a3007bc0e"].into(), - //5CPrgfRNDQvQSnLRdeCphP3ibj5PJW9ESbqj2fw29vBMNQNn - hex!["0e9b60f04be3bffe362eb2212ea99d2b909b052f4bff7c714e13c2416a797f5d"] - .unchecked_into(), - //5FXFsPReTUEYPRNKhbTdUathcWBsxTNsLbk2mTpYdKCJewjA - hex!["98f4d81cb383898c2c3d54dab28698c0f717c81b509cb32dc6905af3cc697b18"] - .unchecked_into(), - //5CZjurB78XbSHf6SLkLhCdkqw52Zm7aBYUDdfkLqEDWJ9Zhj - hex!["162508accd470e379b04cb0c7c60b35a7d5357e84407a89ed2dd48db4b726960"] - .unchecked_into(), - //5DkAqCtSjUMVoJFauuGoAbSEgn2aFCRGziKJiLGpPwYgE1pS - hex!["4a559c028b69a7f784ce553393e547bec0aa530352157603396d515f9c83463b"] - .unchecked_into(), - //5GsBt9MhGwkg8Jfb1F9LAy2kcr88WNyNy4L5ezwbCr8NWKQU - hex!["d464908266c878acbf181bf8fda398b3aa3fd2d05508013e414aaece4cf0d702"] - .unchecked_into(), - //5DtJVkz8AHevEnpszy3X4dUcPvACW6x1qBMQZtFxjexLr5bq - hex!["02fdf30222d2cb88f2376d558d3de9cb83f9fde3aa4b2dd40c93e3104e3488bcd2"] - .unchecked_into(), - ), - ( - //5E2cob2jrXsBkTih56pizwSqENjE4siaVdXhaD6akLdDyVq7 - hex!["56e0f73c563d49ee4a3971c393e17c44eaa313dabad7fcf297dc3271d803f303"].into(), - //5D4rNYgP9uFNi5GMyDEXTfiaFLjXyDEEX2VvuqBVi3f1qgCh - hex!["2c58e5e1d5aef77774480cead4f6876b1a1a6261170166995184d7f86140572b"].into(), - //5Ea2D65KXqe625sz4uV1jjhSfuigVnkezC8VgEj9LXN7ERAk - hex!["6ed45cb7af613be5d88a2622921e18d147225165f24538af03b93f2a03ce6e13"] - .unchecked_into(), - //5G4kCbgqUhEyrRHCyFwFEkgBZXoYA8sbgsRxT9rY8Tp5Jj5F - hex!["b0f8d2b9e4e1eafd4dab6358e0b9d5380d78af27c094e69ae9d6d30ca300fd86"] - .unchecked_into(), - //5CS7thd2n54WfqeKU3cjvZzK4z5p7zku1Zw97mSzXgPioAAs - hex!["1055100a283968271a0781450b389b9093231be809be1e48a305ebad2a90497e"] - .unchecked_into(), - //5DSaL4ZmSYarZSazhL5NQh7LT6pWhNRDcefk2QS9RxEXfsJe - hex!["3cea4ab74bab4adf176cf05a6e18c1599a7bc217d4c6c217275bfbe3b037a527"] - .unchecked_into(), - //5CaNLkYEbFYXZodXhd3UjV6RNLjFGNLiYafc8X5NooMkZiAq - hex!["169faa81aebfe74533518bda28567f2e2664014c8905aa07ea003336afda5a58"] - .unchecked_into(), - //5ERwhKiePayukzZStMuzGzRJGxGRFpwxYUXVarQpMSMrXzDS - hex!["03429d0d20f6ac5ca8b349f04d014f7b5b864acf382a744104d5d9a51108156c0f"] - .unchecked_into(), - ), - ( - //5H6j9ovzYk9opckVjvM9SvVfaK37ASTtPTzWeRfqk1tgLJUN - hex!["deb804ed2ed2bb696a3dd4ed7de4cd5c496528a2b204051c6ace385bacd66a3a"].into(), - //5DJ51tMW916mGwjMpfS1o9skcNt6Sb28YnZQXaKVg4h89agE - hex!["366da6a748afedb31f07902f2de36ab265beccee37762d3ae1f237de234d9c36"].into(), - //5CSPYDYoCDGSoSLgSp4EHkJ52YasZLHG2woqhPZkdbtNQpke - hex!["1089bc0cd60237d061872925e81d36c9d9205d250d5d8b542c8e08a8ecf1b911"] - .unchecked_into(), - //5ChfdrAqmLjCeDJvynbMjcxYLHYzPe8UWXd3HnX9JDThUMbn - hex!["1c309a70b4e274314b84c9a0a1f973c9c4fc084df5479ef686c54b1ae4950424"] - .unchecked_into(), - //5D8C3HHEp5E8fJsXRD56494F413CdRSR9QKGXe7v5ZEfymdj - hex!["2ee4d78f328db178c54f205ac809da12e291a33bcbd4f29f081ce7e74bdc5044"] - .unchecked_into(), - //5GxeTYCGmp1C3ZRLDkRWqJc6gB2GYmuqnygweuH3vsivMQq6 - hex!["d88e40e3c2c7a7c5abf96ffdd8f7b7bec8798cc277bc97e255881871ab73b529"] - .unchecked_into(), - //5DoGpsgSLcJsHa9B8V4PKjxegWAqDZttWfxicAd68prUX654 - hex!["4cb3863271b70daa38612acd5dae4f5afcb7c165fa277629e5150d2214df322a"] - .unchecked_into(), - //5G1KLjqFyMsPAodnjSRkwRFJztTTEzmZWxow2Q3ZSRCPdthM - hex!["03be5ec86d10a94db89c9b7a396d3c7742e3bec5f85159d4cf308cef505966ddf5"] - .unchecked_into(), - ), - ]; - - const ENDOWMENT: u128 = 1_000_000 * WND; - const STASH: u128 = 100 * WND; - - serde_json::json!({ - "balances": { - "balances": endowed_accounts - .iter() - .map(|k: &AccountId| (k.clone(), ENDOWMENT)) - .chain(initial_authorities.iter().map(|x| (x.0.clone(), STASH))) - .collect::>(), - }, - "session": { - "keys": initial_authorities - .iter() - .map(|x| { - ( - x.0.clone(), - x.0.clone(), - westend_session_keys( - x.2.clone(), - x.3.clone(), - x.4.clone(), - x.5.clone(), - x.6.clone(), - x.7.clone(), - ), - ) - }) - .collect::>(), - }, - "staking": { - "validatorCount": 50, - "minimumValidatorCount": 4, - "stakers": initial_authorities - .iter() - .map(|x| (x.0.clone(), x.0.clone(), STASH, westend::StakerStatus::::Validator)) - .collect::>(), - "invulnerables": initial_authorities.iter().map(|x| x.0.clone()).collect::>(), - "forceEra": Forcing::ForceNone, - "slashRewardFraction": Perbill::from_percent(10), - }, - "babe": { - "epochConfig": Some(westend::BABE_GENESIS_EPOCH_CONFIG), - }, - "sudo": { "key": Some(endowed_accounts[0].clone()) }, - "configuration": { - "config": default_parachains_host_configuration(), - }, - "registrar": { - "nextFreeParaId": polkadot_primitives::LOWEST_PUBLIC_ID, - }, - }) -} - /// Westend staging testnet config. #[cfg(feature = "westend-native")] pub fn westend_staging_testnet_config() -> Result { @@ -383,7 +108,7 @@ pub fn westend_staging_testnet_config() -> Result { .with_name("Westend Staging Testnet") .with_id("westend_staging_testnet") .with_chain_type(ChainType::Live) - .with_genesis_config_patch(westend_staging_testnet_config_genesis()) + .with_genesis_config_preset_name("staging_testnet") .with_telemetry_endpoints( TelemetryEndpoints::new(vec![(WESTEND_STAGING_TELEMETRY_URL.to_string(), 0)]) .expect("Westend Staging telemetry url is valid; qed"), @@ -442,148 +167,6 @@ pub fn versi_staging_testnet_config() -> Result { .build()) } -/// Helper function to generate a crypto pair from seed -pub fn get_from_seed(seed: &str) -> ::Public { - TPublic::Pair::from_string(&format!("//{}", seed), None) - .expect("static values are valid; qed") - .public() -} - -/// Helper function to generate an account ID from seed -pub fn get_account_id_from_seed(seed: &str) -> AccountId -where - AccountPublic: From<::Public>, -{ - AccountPublic::from(get_from_seed::(seed)).into_account() -} - -/// Helper function to generate stash, controller and session key from seed -pub fn get_authority_keys_from_seed( - seed: &str, -) -> ( - AccountId, - AccountId, - BabeId, - GrandpaId, - ValidatorId, - AssignmentId, - AuthorityDiscoveryId, - BeefyId, -) { - let keys = get_authority_keys_from_seed_no_beefy(seed); - (keys.0, keys.1, keys.2, keys.3, keys.4, keys.5, keys.6, get_from_seed::(seed)) -} - -/// Helper function to generate stash, controller and session key from seed -pub fn get_authority_keys_from_seed_no_beefy( - seed: &str, -) -> (AccountId, AccountId, BabeId, GrandpaId, ValidatorId, AssignmentId, AuthorityDiscoveryId) { - ( - 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), - ) -} - -#[cfg(feature = "westend-native")] -fn testnet_accounts() -> Vec { - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ] -} - -/// Helper function to create westend runtime `GenesisConfig` patch for testing -#[cfg(feature = "westend-native")] -pub fn westend_testnet_genesis( - initial_authorities: Vec<( - AccountId, - AccountId, - BabeId, - GrandpaId, - ValidatorId, - AssignmentId, - AuthorityDiscoveryId, - BeefyId, - )>, - root_key: AccountId, - endowed_accounts: Option>, -) -> serde_json::Value { - let endowed_accounts: Vec = endowed_accounts.unwrap_or_else(testnet_accounts); - - const ENDOWMENT: u128 = 1_000_000 * WND; - const STASH: u128 = 100 * WND; - - serde_json::json!({ - "balances": { - "balances": endowed_accounts.iter().map(|k| (k.clone(), ENDOWMENT)).collect::>(), - }, - "session": { - "keys": initial_authorities - .iter() - .map(|x| { - ( - x.0.clone(), - x.0.clone(), - westend_session_keys( - x.2.clone(), - x.3.clone(), - x.4.clone(), - x.5.clone(), - x.6.clone(), - x.7.clone(), - ), - ) - }) - .collect::>(), - }, - "staking": { - "minimumValidatorCount": 1, - "validatorCount": initial_authorities.len() as u32, - "stakers": initial_authorities - .iter() - .map(|x| (x.0.clone(), x.0.clone(), STASH, westend::StakerStatus::::Validator)) - .collect::>(), - "invulnerables": initial_authorities.iter().map(|x| x.0.clone()).collect::>(), - "forceEra": Forcing::NotForcing, - "slashRewardFraction": Perbill::from_percent(10), - }, - "babe": { - "epochConfig": Some(westend::BABE_GENESIS_EPOCH_CONFIG), - }, - "sudo": { "key": Some(root_key) }, - "configuration": { - "config": default_parachains_host_configuration(), - }, - "registrar": { - "nextFreeParaId": polkadot_primitives::LOWEST_PUBLIC_ID, - }, - }) -} - -#[cfg(feature = "westend-native")] -fn westend_development_config_genesis() -> serde_json::Value { - westend_testnet_genesis( - vec![get_authority_keys_from_seed("Alice")], - get_account_id_from_seed::("Alice"), - None, - ) -} - /// Westend development config (single validator Alice) #[cfg(feature = "westend-native")] pub fn westend_development_config() -> Result { @@ -594,7 +177,7 @@ pub fn westend_development_config() -> Result { .with_name("Development") .with_id("westend_dev") .with_chain_type(ChainType::Development) - .with_genesis_config_patch(westend_development_config_genesis()) + .with_genesis_config_preset_name(sp_genesis_builder::DEV_RUNTIME_PRESET) .with_protocol_id(DEFAULT_PROTOCOL_ID) .build()) } @@ -609,7 +192,7 @@ pub fn rococo_development_config() -> Result { .with_name("Development") .with_id("rococo_dev") .with_chain_type(ChainType::Development) - .with_genesis_config_preset_name("development") + .with_genesis_config_preset_name(sp_genesis_builder::DEV_RUNTIME_PRESET) .with_protocol_id(DEFAULT_PROTOCOL_ID) .build()) } @@ -624,36 +207,11 @@ pub fn versi_development_config() -> Result { .with_name("Development") .with_id("versi_dev") .with_chain_type(ChainType::Development) - .with_genesis_config_preset_name("development") + .with_genesis_config_preset_name(sp_genesis_builder::DEV_RUNTIME_PRESET) .with_protocol_id("versi") .build()) } -/// Wococo development config (single validator Alice) -#[cfg(feature = "rococo-native")] -pub fn wococo_development_config() -> Result { - const WOCOCO_DEV_PROTOCOL_ID: &str = "woco"; - Ok(RococoChainSpec::builder( - rococo::WASM_BINARY.ok_or("Wococo development wasm not available")?, - Default::default(), - ) - .with_name("Development") - .with_id("wococo_dev") - .with_chain_type(ChainType::Development) - .with_genesis_config_preset_name("development") - .with_protocol_id(WOCOCO_DEV_PROTOCOL_ID) - .build()) -} - -#[cfg(feature = "westend-native")] -fn westend_local_testnet_genesis() -> serde_json::Value { - westend_testnet_genesis( - vec![get_authority_keys_from_seed("Alice"), get_authority_keys_from_seed("Bob")], - get_account_id_from_seed::("Alice"), - None, - ) -} - /// Westend local testnet config (multivalidator Alice + Bob) #[cfg(feature = "westend-native")] pub fn westend_local_testnet_config() -> Result { @@ -665,7 +223,7 @@ pub fn westend_local_testnet_config() -> Result { .with_name("Westend Local Testnet") .with_id("westend_local_testnet") .with_chain_type(ChainType::Local) - .with_genesis_config_patch(westend_local_testnet_genesis()) + .with_genesis_config_preset_name(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET) .with_protocol_id(DEFAULT_PROTOCOL_ID) .build()) } @@ -680,22 +238,7 @@ pub fn rococo_local_testnet_config() -> Result { .with_name("Rococo Local Testnet") .with_id("rococo_local_testnet") .with_chain_type(ChainType::Local) - .with_genesis_config_preset_name("local_testnet") - .with_protocol_id(DEFAULT_PROTOCOL_ID) - .build()) -} - -/// Wococo local testnet config (multivalidator Alice + Bob + Charlie + Dave) -#[cfg(feature = "rococo-native")] -pub fn wococo_local_testnet_config() -> Result { - Ok(RococoChainSpec::builder( - rococo::WASM_BINARY.ok_or("Rococo development wasm (used for wococo) not available")?, - Default::default(), - ) - .with_name("Wococo Local Testnet") - .with_id("wococo_local_testnet") - .with_chain_type(ChainType::Local) - .with_genesis_config_preset_name("wococo_local_testnet") + .with_genesis_config_preset_name(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET) .with_protocol_id(DEFAULT_PROTOCOL_ID) .build()) } @@ -714,3 +257,18 @@ pub fn versi_local_testnet_config() -> Result { .with_protocol_id("versi") .build()) } + +/// Helper function to generate a crypto pair from seed +pub fn get_from_seed(seed: &str) -> ::Public { + TPublic::Pair::from_string(&format!("//{}", seed), None) + .expect("static values are valid; qed") + .public() +} + +/// Helper function to generate an account ID from seed +pub fn get_account_id_from_seed(seed: &str) -> AccountId +where + AccountPublic: From<::Public>, +{ + AccountPublic::from(get_from_seed::(seed)).into_account() +} diff --git a/polkadot/node/service/src/fake_runtime_api.rs b/polkadot/node/service/src/fake_runtime_api.rs index cdef39d5bdf1805d1066fdeba9cb09f0037261b1..d8f147a9cf7b51571a48336108b054ef45639f57 100644 --- a/polkadot/node/service/src/fake_runtime_api.rs +++ b/polkadot/node/service/src/fake_runtime_api.rs @@ -21,12 +21,16 @@ use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo}; use polkadot_primitives::{ - runtime_api, slashing, AccountId, AuthorityDiscoveryId, Balance, Block, BlockNumber, - CandidateCommitments, CandidateEvent, CandidateHash, CommittedCandidateReceipt, CoreState, - DisputeState, ExecutorParams, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, - InboundHrmpMessage, Nonce, OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, - ScrapedOnChainVotes, SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash, - ValidatorId, ValidatorIndex, ValidatorSignature, + runtime_api, slashing, + vstaging::{ + CandidateEvent, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + ScrapedOnChainVotes, + }, + AccountId, AuthorityDiscoveryId, Balance, Block, BlockNumber, CandidateCommitments, + CandidateHash, DisputeState, ExecutorParams, GroupRotationInfo, Hash, Id as ParaId, + InboundDownwardMessage, InboundHrmpMessage, Nonce, OccupiedCoreAssumption, + PersistedValidationData, PvfCheckStatement, SessionIndex, SessionInfo, ValidationCode, + ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, }; use sp_consensus_beefy::ecdsa_crypto::{AuthorityId as BeefyId, Signature as BeefySignature}; use sp_consensus_grandpa::AuthorityId as GrandpaId; @@ -49,6 +53,7 @@ sp_api::decl_runtime_apis! { } } +#[allow(dead_code)] struct Runtime; sp_api::impl_runtime_apis! { diff --git a/polkadot/node/service/src/lib.rs b/polkadot/node/service/src/lib.rs index a907d310c10529ffc91056e8fbb5b100f557bbcb..9515dd23113805448831d25467c21e3cab89c406 100644 --- a/polkadot/node/service/src/lib.rs +++ b/polkadot/node/service/src/lib.rs @@ -59,10 +59,10 @@ use { sc_client_api::BlockBackend, sc_consensus_grandpa::{self, FinalityProofProvider as GrandpaFinalityProofProvider}, sc_transaction_pool_api::OffchainTransactionPoolFactory, - sp_core::traits::SpawnNamed, }; use polkadot_node_subsystem_util::database::Database; +use polkadot_overseer::SpawnGlue; #[cfg(feature = "full-node")] pub use { @@ -75,15 +75,12 @@ pub use { sp_consensus_babe::BabeApi, }; -#[cfg(feature = "full-node")] -use polkadot_node_subsystem::jaeger; - use std::{collections::HashMap, path::PathBuf, sync::Arc, time::Duration}; use prometheus_endpoint::Registry; #[cfg(feature = "full-node")] use sc_service::KeystoreContainer; -use sc_service::RpcHandlers; +use sc_service::{build_polkadot_syncing_strategy, RpcHandlers, SpawnTaskHandle}; use sc_telemetry::TelemetryWorker; #[cfg(feature = "full-node")] use sc_telemetry::{Telemetry, TelemetryWorkerHandle}; @@ -221,9 +218,6 @@ pub enum Error { #[error(transparent)] Telemetry(#[from] sc_telemetry::Error), - #[error(transparent)] - Jaeger(#[from] polkadot_node_subsystem::jaeger::JaegerError), - #[cfg(feature = "full-node")] #[error(transparent)] Availability(#[from] AvailabilityError), @@ -289,9 +283,6 @@ pub trait IdentifyVariant { /// Returns if this is a configuration for the `Rococo` network. fn is_rococo(&self) -> bool; - /// Returns if this is a configuration for the `Wococo` test network. - fn is_wococo(&self) -> bool; - /// Returns if this is a configuration for the `Versi` test network. fn is_versi(&self) -> bool; @@ -315,9 +306,6 @@ impl IdentifyVariant for Box { fn is_rococo(&self) -> bool { self.id().starts_with("rococo") || self.id().starts_with("rco") } - fn is_wococo(&self) -> bool { - self.id().starts_with("wococo") || self.id().starts_with("wco") - } fn is_versi(&self) -> bool { self.id().starts_with("versi") || self.id().starts_with("vrs") } @@ -331,7 +319,7 @@ impl IdentifyVariant for Box { Chain::Kusama } else if self.is_westend() { Chain::Westend - } else if self.is_rococo() || self.is_versi() || self.is_wococo() { + } else if self.is_rococo() || self.is_versi() { Chain::Rococo } else { Chain::Unknown @@ -370,25 +358,6 @@ pub fn open_database(db_source: &DatabaseSource) -> Result, Er Ok(parachains_db) } -/// Initialize the `Jeager` collector. The destination must listen -/// on the given address and port for `UDP` packets. -#[cfg(any(test, feature = "full-node"))] -fn jaeger_launch_collector_with_agent( - spawner: impl SpawnNamed, - config: &Configuration, - agent: Option, -) -> Result<(), Error> { - if let Some(agent) = agent { - let cfg = jaeger::JaegerConfig::builder() - .agent(agent) - .named(&config.network.node_name) - .build(); - - jaeger::Jaeger::new(cfg).launch(spawner)?; - } - Ok(()) -} - #[cfg(feature = "full-node")] type FullSelectChain = relay_chain_selection::SelectRelayChain; #[cfg(feature = "full-node")] @@ -416,7 +385,6 @@ struct Basics { #[cfg(feature = "full-node")] fn new_partial_basics( config: &mut Configuration, - jaeger_agent: Option, telemetry_worker_handle: Option, ) -> Result { let telemetry = config @@ -437,15 +405,16 @@ fn new_partial_basics( .transpose()?; let heap_pages = config + .executor .default_heap_pages .map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |h| HeapAllocStrategy::Static { extra_pages: h as _ }); let executor = WasmExecutor::builder() - .with_execution_method(config.wasm_method) + .with_execution_method(config.executor.wasm_method) .with_onchain_heap_alloc_strategy(heap_pages) .with_offchain_heap_alloc_strategy(heap_pages) - .with_max_runtime_instances(config.max_runtime_instances) - .with_runtime_cache_size(config.runtime_cache_size) + .with_max_runtime_instances(config.executor.max_runtime_instances) + .with_runtime_cache_size(config.executor.runtime_cache_size) .build(); let (client, backend, keystore_container, task_manager) = @@ -467,8 +436,6 @@ fn new_partial_basics( telemetry }); - jaeger_launch_collector_with_agent(task_manager.spawn_handle(), &*config, jaeger_agent)?; - Ok(Basics { task_manager, client, backend, keystore_container, telemetry }) } @@ -641,7 +608,6 @@ pub struct NewFullParams { /// Whether to enable the block authoring backoff on production networks /// where it isn't enabled by default. pub force_authoring_backoff: bool, - pub jaeger_agent: Option, pub telemetry_worker_handle: Option, /// The version of the node. TESTING ONLY: `None` can be passed to skip the node/worker version /// check, both on startup and in the workers. @@ -664,6 +630,8 @@ pub struct NewFullParams { #[allow(dead_code)] pub malus_finality_delay: Option, pub hwbench: Option, + /// Enable approval voting processing in parallel. + pub enable_approval_voting_parallel: bool, } #[cfg(feature = "full-node")] @@ -744,7 +712,6 @@ pub fn new_full< is_parachain_node, enable_beefy, force_authoring_backoff, - jaeger_agent, telemetry_worker_handle, node_version, secure_validator_mode, @@ -757,14 +724,16 @@ pub fn new_full< execute_workers_max_num, prepare_workers_soft_max_num, prepare_workers_hard_max_num, + enable_approval_voting_parallel, }: NewFullParams, ) -> Result { use polkadot_availability_recovery::FETCH_CHUNKS_THRESHOLD; use polkadot_node_network_protocol::request_response::IncomingRequest; use sc_network_sync::WarpSyncConfig; + use sc_sysinfo::Metric; let is_offchain_indexing_enabled = config.offchain_worker.indexing_enabled; - let role = config.role.clone(); + let role = config.role; let force_authoring = config.force_authoring; let backoff_authoring_blocks = if !force_authoring_backoff && (config.chain_spec.is_polkadot() || config.chain_spec.is_kusama()) @@ -775,7 +744,6 @@ pub fn new_full< let mut backoff = sc_consensus_slots::BackoffAuthoringOnFinalizedHeadLagging::default(); if config.chain_spec.is_rococo() || - config.chain_spec.is_wococo() || config.chain_spec.is_versi() || config.chain_spec.is_dev() { @@ -788,18 +756,24 @@ pub fn new_full< Some(backoff) }; + // Running approval voting in parallel is enabled by default on all networks except Polkadot and + // Kusama, unless explicitly enabled by the commandline option. + // This is meant to be temporary until we have enough confidence in the new system to enable it + // by default on all networks. + let enable_approval_voting_parallel = (!config.chain_spec.is_kusama() && + !config.chain_spec.is_polkadot()) || + enable_approval_voting_parallel; + let disable_grandpa = config.disable_grandpa; let name = config.network.node_name.clone(); - let basics = new_partial_basics(&mut config, jaeger_agent, telemetry_worker_handle)?; + let basics = new_partial_basics(&mut config, telemetry_worker_handle)?; let prometheus_registry = config.prometheus_registry().cloned(); let overseer_connector = OverseerConnector::default(); let overseer_handle = Handle::new(overseer_connector.handle()); - let chain_spec = config.chain_spec.cloned_box(); - let keystore = basics.keystore_container.local_keystore(); let auth_or_collator = role.is_authority() || is_parachain_node.is_collator(); @@ -812,6 +786,7 @@ pub fn new_full< overseer_handle.clone(), metrics, Some(basics.task_manager.spawn_handle()), + enable_approval_voting_parallel, ) } else { SelectRelayChain::new_longest_chain(basics.backend.clone()) @@ -1022,9 +997,20 @@ pub fn new_full< dispute_coordinator_config, chain_selection_config, fetch_chunks_threshold, + enable_approval_voting_parallel, }) }; + let syncing_strategy = build_polkadot_syncing_strategy( + config.protocol_id(), + config.chain_spec.fork_id(), + &mut net_config, + Some(WarpSyncConfig::WithProvider(warp_sync)), + client.clone(), + &task_manager.spawn_handle(), + config.prometheus_config.as_ref().map(|config| &config.registry), + )?; + let (network, system_rpc_tx, tx_handler_controller, network_starter, sync_service) = sc_service::build_network(sc_service::BuildNetworkParams { config: &config, @@ -1034,7 +1020,7 @@ pub fn new_full< spawn_handle: task_manager.spawn_handle(), import_queue, block_announce_validator_builder: None, - warp_sync_config: Some(WarpSyncConfig::WithProvider(warp_sync)), + syncing_strategy, block_relay: None, metrics, })?; @@ -1079,13 +1065,31 @@ pub fn new_full< if let Some(hwbench) = hwbench { sc_sysinfo::print_hwbench(&hwbench); - match SUBSTRATE_REFERENCE_HARDWARE.check_hardware(&hwbench) { + match SUBSTRATE_REFERENCE_HARDWARE.check_hardware(&hwbench, role.is_authority()) { Err(err) if role.is_authority() => { - log::warn!( - "โš ๏ธ The hardware does not meet the minimal requirements {} for role 'Authority' find out more at:\n\ - https://wiki.polkadot.network/docs/maintain-guides-how-to-validate-polkadot#reference-hardware", - err - ); + if err + .0 + .iter() + .any(|failure| matches!(failure.metric, Metric::Blake2256Parallel { .. })) + { + log::warn!( + "โš ๏ธ Starting January 2025 the hardware will fail the minimal physical CPU cores requirements {} for role 'Authority',\n\ + find out more when this will become mandatory at:\n\ + https://wiki.polkadot.network/docs/maintain-guides-how-to-validate-polkadot#reference-hardware", + err + ); + } + if err + .0 + .iter() + .any(|failure| !matches!(failure.metric, Metric::Blake2256Parallel { .. })) + { + log::warn!( + "โš ๏ธ The hardware does not meet the minimal requirements {} for role 'Authority' find out more at:\n\ + https://wiki.polkadot.network/docs/maintain-guides-how-to-validate-polkadot#reference-hardware", + err + ); + } }, _ => {}, } @@ -1296,7 +1300,7 @@ pub fn new_full< runtime: client.clone(), key_store: keystore_opt.clone(), network_params, - min_block_delta: if chain_spec.is_wococo() { 4 } else { 8 }, + min_block_delta: 8, prometheus_registry: prometheus_registry.clone(), links: beefy_links, on_demand_justifications_handler: beefy_on_demand_justifications_handler, @@ -1402,11 +1406,10 @@ pub fn new_full< #[cfg(feature = "full-node")] macro_rules! chain_ops { - ($config:expr, $jaeger_agent:expr, $telemetry_worker_handle:expr) => {{ + ($config:expr, $telemetry_worker_handle:expr) => {{ let telemetry_worker_handle = $telemetry_worker_handle; - let jaeger_agent = $jaeger_agent; let mut config = $config; - let basics = new_partial_basics(config, jaeger_agent, telemetry_worker_handle)?; + let basics = new_partial_basics(config, telemetry_worker_handle)?; use ::sc_consensus::LongestChain; // use the longest chain selection, since there is no overseer available @@ -1422,22 +1425,18 @@ macro_rules! chain_ops { #[cfg(feature = "full-node")] pub fn new_chain_ops( config: &mut Configuration, - jaeger_agent: Option, ) -> Result<(Arc, Arc, sc_consensus::BasicQueue, TaskManager), Error> { config.keystore = sc_service::config::KeystoreConfig::InMemory; - if config.chain_spec.is_rococo() || - config.chain_spec.is_wococo() || - config.chain_spec.is_versi() - { - chain_ops!(config, jaeger_agent, None) + if config.chain_spec.is_rococo() || config.chain_spec.is_versi() { + chain_ops!(config, None) } else if config.chain_spec.is_kusama() { - chain_ops!(config, jaeger_agent, None) + chain_ops!(config, None) } else if config.chain_spec.is_westend() { - return chain_ops!(config, jaeger_agent, None) + return chain_ops!(config, None) } else { - chain_ops!(config, jaeger_agent, None) + chain_ops!(config, None) } } @@ -1480,6 +1479,7 @@ pub fn revert_backend( backend: Arc, blocks: BlockNumber, config: Configuration, + task_handle: SpawnTaskHandle, ) -> Result<(), Error> { let best_number = client.info().best_number; let finalized = client.info().finalized_number; @@ -1500,7 +1500,7 @@ pub fn revert_backend( let parachains_db = open_database(&config.database) .map_err(|err| sp_blockchain::Error::Backend(err.to_string()))?; - revert_approval_voting(parachains_db.clone(), hash)?; + revert_approval_voting(parachains_db.clone(), hash, task_handle)?; revert_chain_selection(parachains_db, hash)?; // Revert Substrate consensus related components sc_consensus_babe::revert(client.clone(), backend, blocks)?; @@ -1523,7 +1523,11 @@ fn revert_chain_selection(db: Arc, hash: Hash) -> sp_blockchain::R .map_err(|err| sp_blockchain::Error::Backend(err.to_string())) } -fn revert_approval_voting(db: Arc, hash: Hash) -> sp_blockchain::Result<()> { +fn revert_approval_voting( + db: Arc, + hash: Hash, + task_handle: SpawnTaskHandle, +) -> sp_blockchain::Result<()> { let config = approval_voting_subsystem::Config { col_approval_data: parachains_db::REAL_COLUMNS.col_approval_data, slot_duration_millis: Default::default(), @@ -1535,6 +1539,7 @@ fn revert_approval_voting(db: Arc, hash: Hash) -> sp_blockchain::R Arc::new(sc_keystore::LocalKeystore::in_memory()), Box::new(sp_consensus::NoNetwork), approval_voting_subsystem::Metrics::default(), + Arc::new(SpawnGlue(task_handle)), ); approval_voting diff --git a/polkadot/node/service/src/overseer.rs b/polkadot/node/service/src/overseer.rs index add5d239364dfa8b66aaa33cc825512a59b244f4..279b6ff80704dc52c00b440b096ea87d18ea4235 100644 --- a/polkadot/node/service/src/overseer.rs +++ b/polkadot/node/service/src/overseer.rs @@ -20,7 +20,7 @@ use polkadot_overseer::{DummySubsystem, InitializedOverseerBuilder, SubsystemErr use sp_core::traits::SpawnNamed; use polkadot_availability_distribution::IncomingRequestReceivers; -use polkadot_node_core_approval_voting::Config as ApprovalVotingConfig; +use polkadot_node_core_approval_voting::{Config as ApprovalVotingConfig, RealAssignmentCriteria}; use polkadot_node_core_av_store::Config as AvailabilityConfig; use polkadot_node_core_candidate_validation::Config as CandidateValidationConfig; use polkadot_node_core_chain_selection::Config as ChainSelectionConfig; @@ -58,6 +58,9 @@ pub use polkadot_network_bridge::{ }; pub use polkadot_node_collation_generation::CollationGenerationSubsystem; pub use polkadot_node_core_approval_voting::ApprovalVotingSubsystem; +pub use polkadot_node_core_approval_voting_parallel::{ + ApprovalVotingParallelSubsystem, Metrics as ApprovalVotingParallelMetrics, +}; pub use polkadot_node_core_av_store::AvailabilityStoreSubsystem; pub use polkadot_node_core_backing::CandidateBackingSubsystem; pub use polkadot_node_core_bitfield_signing::BitfieldSigningSubsystem; @@ -139,9 +142,16 @@ pub struct ExtendedOverseerGenArgs { /// than the value put in here we always try to recovery availability from backers. /// The presence of this parameter here is needed to have different values per chain. pub fetch_chunks_threshold: Option, + /// Enable approval-voting-parallel subsystem and disable the standalone approval-voting and + /// approval-distribution subsystems. + pub enable_approval_voting_parallel: bool, } /// Obtain a prepared validator `Overseer`, that is initialized with all default values. +/// +/// The difference between this function and `validator_with_parallel_overseer_builder` is that this +/// function enables the standalone approval-voting and approval-distribution subsystems +/// and disables the approval-voting-parallel subsystem. pub fn validator_overseer_builder( OverseerGenArgs { runtime_client, @@ -174,6 +184,7 @@ pub fn validator_overseer_builder( dispute_coordinator_config, chain_selection_config, fetch_chunks_threshold, + enable_approval_voting_parallel, }: ExtendedOverseerGenArgs, ) -> Result< InitializedOverseerBuilder< @@ -203,6 +214,7 @@ pub fn validator_overseer_builder( CollatorProtocolSubsystem, ApprovalDistributionSubsystem, ApprovalVotingSubsystem, + DummySubsystem, GossipSupportSubsystem, DisputeCoordinatorSubsystem, DisputeDistributionSubsystem, @@ -223,7 +235,8 @@ where let spawner = SpawnGlue(spawner); let network_bridge_metrics: NetworkBridgeMetrics = Metrics::register(registry)?; - + let approval_voting_parallel_metrics: ApprovalVotingParallelMetrics = + Metrics::register(registry)?; let builder = Overseer::builder() .network_bridge_tx(NetworkBridgeTxSubsystem::new( network_service.clone(), @@ -241,6 +254,7 @@ where peerset_protocol_names, notification_services, notification_sinks, + enable_approval_voting_parallel, )) .availability_distribution(AvailabilityDistributionSubsystem::new( keystore.clone(), @@ -309,14 +323,241 @@ where Metrics::register(registry)?, rand::rngs::StdRng::from_entropy(), )) - .approval_distribution(ApprovalDistributionSubsystem::new(Metrics::register(registry)?)) + .approval_distribution(ApprovalDistributionSubsystem::new( + approval_voting_parallel_metrics.approval_distribution_metrics(), + approval_voting_config.slot_duration_millis, + Arc::new(RealAssignmentCriteria {}), + )) .approval_voting(ApprovalVotingSubsystem::with_config( - approval_voting_config, + approval_voting_config.clone(), parachains_db.clone(), keystore.clone(), Box::new(sync_service.clone()), + approval_voting_parallel_metrics.approval_voting_metrics(), + Arc::new(spawner.clone()), + )) + .approval_voting_parallel(DummySubsystem) + .gossip_support(GossipSupportSubsystem::new( + keystore.clone(), + authority_discovery_service.clone(), + Metrics::register(registry)?, + )) + .dispute_coordinator(DisputeCoordinatorSubsystem::new( + parachains_db.clone(), + dispute_coordinator_config, + keystore.clone(), + Metrics::register(registry)?, + enable_approval_voting_parallel, + )) + .dispute_distribution(DisputeDistributionSubsystem::new( + keystore.clone(), + dispute_req_receiver, + authority_discovery_service.clone(), + Metrics::register(registry)?, + )) + .chain_selection(ChainSelectionSubsystem::new(chain_selection_config, parachains_db)) + .prospective_parachains(ProspectiveParachainsSubsystem::new(Metrics::register(registry)?)) + .activation_external_listeners(Default::default()) + .active_leaves(Default::default()) + .supports_parachains(runtime_client) + .metrics(metrics) + .spawner(spawner); + + let builder = if let Some(capacity) = overseer_message_channel_capacity_override { + builder.message_channel_capacity(capacity) + } else { + builder + }; + Ok(builder) +} + +/// Obtain a prepared validator `Overseer`, that is initialized with all default values. +/// +/// The difference between this function and `validator_overseer_builder` is that this +/// function enables the approval-voting-parallel subsystem and disables the standalone +/// approval-voting and approval-distribution subsystems. +pub fn validator_with_parallel_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, + ExtendedOverseerGenArgs { + keystore, + parachains_db, + candidate_validation_config, + availability_config, + pov_req_receiver, + chunk_req_v1_receiver, + chunk_req_v2_receiver, + statement_req_receiver, + candidate_req_v2_receiver, + approval_voting_config, + dispute_req_receiver, + dispute_coordinator_config, + chain_selection_config, + fetch_chunks_threshold, + enable_approval_voting_parallel, + }: ExtendedOverseerGenArgs, +) -> Result< + InitializedOverseerBuilder< + SpawnGlue, + Arc, + CandidateValidationSubsystem, + PvfCheckerSubsystem, + CandidateBackingSubsystem, + StatementDistributionSubsystem, + AvailabilityDistributionSubsystem, + AvailabilityRecoverySubsystem, + BitfieldSigningSubsystem, + BitfieldDistributionSubsystem, + ProvisionerSubsystem, + RuntimeApiSubsystem, + AvailabilityStoreSubsystem, + NetworkBridgeRxSubsystem< + Arc, + AuthorityDiscoveryService, + >, + NetworkBridgeTxSubsystem< + Arc, + AuthorityDiscoveryService, + >, + ChainApiSubsystem, + CollationGenerationSubsystem, + CollatorProtocolSubsystem, + DummySubsystem, + DummySubsystem, + ApprovalVotingParallelSubsystem, + GossipSupportSubsystem, + DisputeCoordinatorSubsystem, + DisputeDistributionSubsystem, + ChainSelectionSubsystem, + ProspectiveParachainsSubsystem, + >, + Error, +> +where + RuntimeClient: RuntimeApiSubsystemClient + ChainApiBackend + AuxStore + 'static, + Spawner: 'static + SpawnNamed + Clone + Unpin, +{ + use polkadot_node_subsystem_util::metrics::Metrics; + + let metrics = ::register(registry)?; + let notification_sinks = Arc::new(Mutex::new(HashMap::new())); + + let spawner = SpawnGlue(spawner); + + let network_bridge_metrics: NetworkBridgeMetrics = Metrics::register(registry)?; + let approval_voting_parallel_metrics: ApprovalVotingParallelMetrics = + 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.clone(), + 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, + enable_approval_voting_parallel, + )) + .availability_distribution(AvailabilityDistributionSubsystem::new( + keystore.clone(), + IncomingRequestReceivers { + pov_req_receiver, + chunk_req_v1_receiver, + chunk_req_v2_receiver, + }, + req_protocol_names.clone(), + Metrics::register(registry)?, + )) + .availability_recovery(AvailabilityRecoverySubsystem::for_validator( + fetch_chunks_threshold, + available_data_req_receiver, + &req_protocol_names, + Metrics::register(registry)?, + )) + .availability_store(AvailabilityStoreSubsystem::new( + parachains_db.clone(), + availability_config, + Box::new(sync_service.clone()), Metrics::register(registry)?, )) + .bitfield_distribution(BitfieldDistributionSubsystem::new(Metrics::register(registry)?)) + .bitfield_signing(BitfieldSigningSubsystem::new( + keystore.clone(), + Metrics::register(registry)?, + )) + .candidate_backing(CandidateBackingSubsystem::new( + keystore.clone(), + Metrics::register(registry)?, + )) + .candidate_validation(CandidateValidationSubsystem::with_config( + candidate_validation_config, + keystore.clone(), + Metrics::register(registry)?, // candidate-validation metrics + Metrics::register(registry)?, // validation host metrics + )) + .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(_) | 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(), + metrics: Metrics::register(registry)?, + }, + }; + CollatorProtocolSubsystem::new(side) + }) + .provisioner(ProvisionerSubsystem::new(Metrics::register(registry)?)) + .runtime_api(RuntimeApiSubsystem::new( + runtime_client.clone(), + Metrics::register(registry)?, + spawner.clone(), + )) + .statement_distribution(StatementDistributionSubsystem::new( + keystore.clone(), + statement_req_receiver, + candidate_req_v2_receiver, + Metrics::register(registry)?, + rand::rngs::StdRng::from_entropy(), + )) + .approval_distribution(DummySubsystem) + .approval_voting(DummySubsystem) + .approval_voting_parallel(ApprovalVotingParallelSubsystem::with_config( + approval_voting_config, + parachains_db.clone(), + keystore.clone(), + Box::new(sync_service.clone()), + approval_voting_parallel_metrics, + spawner.clone(), + overseer_message_channel_capacity_override, + )) .gossip_support(GossipSupportSubsystem::new( keystore.clone(), authority_discovery_service.clone(), @@ -327,6 +568,7 @@ where dispute_coordinator_config, keystore.clone(), Metrics::register(registry)?, + enable_approval_voting_parallel, )) .dispute_distribution(DisputeDistributionSubsystem::new( keystore.clone(), @@ -337,7 +579,6 @@ where .chain_selection(ChainSelectionSubsystem::new(chain_selection_config, parachains_db)) .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) @@ -402,6 +643,7 @@ pub fn collator_overseer_builder( DummySubsystem, DummySubsystem, DummySubsystem, + DummySubsystem, >, Error, > @@ -434,6 +676,7 @@ where peerset_protocol_names, notification_services, notification_sinks, + false, )) .availability_distribution(DummySubsystem) .availability_recovery(AvailabilityRecoverySubsystem::for_collator( @@ -476,13 +719,13 @@ where .statement_distribution(DummySubsystem) .approval_distribution(DummySubsystem) .approval_voting(DummySubsystem) + .approval_voting_parallel(DummySubsystem) .gossip_support(DummySubsystem) .dispute_coordinator(DummySubsystem) .dispute_distribution(DummySubsystem) .chain_selection(DummySubsystem) .prospective_parachains(DummySubsystem) .activation_external_listeners(Default::default()) - .span_per_active_leaf(Default::default()) .active_leaves(Default::default()) .supports_parachains(runtime_client) .metrics(Metrics::register(registry)?) @@ -532,9 +775,15 @@ impl OverseerGen for ValidatorOverseerGen { "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()) + if ext_args.enable_approval_voting_parallel { + validator_with_parallel_overseer_builder(args, ext_args)? + .build_with_connector(connector) + .map_err(|e| e.into()) + } else { + validator_overseer_builder(args, ext_args)? + .build_with_connector(connector) + .map_err(|e| e.into()) + } } } diff --git a/polkadot/node/service/src/parachains_db/mod.rs b/polkadot/node/service/src/parachains_db/mod.rs index 59af30dceeb90f47ca6a4263e2c1428fc74ac436..887db80a3034860585ce343f8394c13d0bef3a5f 100644 --- a/polkadot/node/service/src/parachains_db/mod.rs +++ b/polkadot/node/service/src/parachains_db/mod.rs @@ -100,18 +100,11 @@ pub struct CacheSizes { pub availability_meta: usize, /// Cache used by approval data. pub approval_data: usize, - /// Cache used by session window data - pub session_data: usize, } impl Default for CacheSizes { fn default() -> Self { - CacheSizes { - availability_data: 25, - availability_meta: 1, - approval_data: 5, - session_data: 1, - } + CacheSizes { availability_data: 25, availability_meta: 1, approval_data: 5 } } } diff --git a/polkadot/node/service/src/relay_chain_selection.rs b/polkadot/node/service/src/relay_chain_selection.rs index c0b1ce8b0ebe1e38db56c3692ca473d7b31d13c4..e48874f01ca6fe0af410b7b65feb64945a69c757 100644 --- a/polkadot/node/service/src/relay_chain_selection.rs +++ b/polkadot/node/service/src/relay_chain_selection.rs @@ -39,8 +39,8 @@ use super::{HeaderProvider, HeaderProviderProvider}; use futures::channel::oneshot; use polkadot_node_primitives::MAX_FINALITY_LAG as PRIMITIVES_MAX_FINALITY_LAG; use polkadot_node_subsystem::messages::{ - ApprovalDistributionMessage, ApprovalVotingMessage, ChainSelectionMessage, - DisputeCoordinatorMessage, HighestApprovedAncestorBlock, + ApprovalDistributionMessage, ApprovalVotingMessage, ApprovalVotingParallelMessage, + ChainSelectionMessage, DisputeCoordinatorMessage, HighestApprovedAncestorBlock, }; use polkadot_node_subsystem_util::metrics::{self, prometheus}; use polkadot_overseer::{AllMessages, Handle}; @@ -169,6 +169,7 @@ where overseer: Handle, metrics: Metrics, spawn_handle: Option, + approval_voting_parallel_enabled: bool, ) -> Self { gum::debug!(target: LOG_TARGET, "Using dispute aware relay-chain selection algorithm",); @@ -179,6 +180,7 @@ where overseer, metrics, spawn_handle, + approval_voting_parallel_enabled, )), } } @@ -230,6 +232,7 @@ pub struct SelectRelayChainInner { overseer: OH, metrics: Metrics, spawn_handle: Option, + approval_voting_parallel_enabled: bool, } impl SelectRelayChainInner @@ -244,8 +247,15 @@ where overseer: OH, metrics: Metrics, spawn_handle: Option, + approval_voting_parallel_enabled: bool, ) -> Self { - SelectRelayChainInner { backend, overseer, metrics, spawn_handle } + SelectRelayChainInner { + backend, + overseer, + metrics, + spawn_handle, + approval_voting_parallel_enabled, + } } fn block_header(&self, hash: Hash) -> Result { @@ -284,6 +294,7 @@ where overseer: self.overseer.clone(), metrics: self.metrics.clone(), spawn_handle: self.spawn_handle.clone(), + approval_voting_parallel_enabled: self.approval_voting_parallel_enabled, } } } @@ -448,13 +459,25 @@ where // 2. Constrain according to `ApprovedAncestor`. let (subchain_head, subchain_number, subchain_block_descriptions) = { let (tx, rx) = oneshot::channel(); - overseer - .send_msg( - ApprovalVotingMessage::ApprovedAncestor(subchain_head, target_number, tx), - std::any::type_name::(), - ) - .await; - + if self.approval_voting_parallel_enabled { + overseer + .send_msg( + ApprovalVotingParallelMessage::ApprovedAncestor( + subchain_head, + target_number, + tx, + ), + std::any::type_name::(), + ) + .await; + } else { + overseer + .send_msg( + ApprovalVotingMessage::ApprovedAncestor(subchain_head, target_number, tx), + std::any::type_name::(), + ) + .await; + } match rx .await .map_err(Error::ApprovedAncestorCanceled) @@ -476,13 +499,23 @@ where // task for sending the message to not block here and delay finality. if let Some(spawn_handle) = &self.spawn_handle { let mut overseer_handle = self.overseer.clone(); + let approval_voting_parallel_enabled = self.approval_voting_parallel_enabled; let lag_update_task = async move { - overseer_handle - .send_msg( - ApprovalDistributionMessage::ApprovalCheckingLagUpdate(lag), - std::any::type_name::(), - ) - .await; + if approval_voting_parallel_enabled { + overseer_handle + .send_msg( + ApprovalVotingParallelMessage::ApprovalCheckingLagUpdate(lag), + std::any::type_name::(), + ) + .await; + } else { + overseer_handle + .send_msg( + ApprovalDistributionMessage::ApprovalCheckingLagUpdate(lag), + std::any::type_name::(), + ) + .await; + } }; spawn_handle.spawn( diff --git a/polkadot/node/service/src/tests.rs b/polkadot/node/service/src/tests.rs index 195432bcb75d84a3fa1a5428efdb2d50fd76a910..78bbfcd5444f0413707cf3e35c9347ad65302794 100644 --- a/polkadot/node/service/src/tests.rs +++ b/polkadot/node/service/src/tests.rs @@ -63,9 +63,6 @@ struct TestHarness { finality_target_rx: Receiver>, } -#[derive(Default)] -struct HarnessConfig; - fn test_harness>( case_vars: CaseVars, test: impl FnOnce(TestHarness) -> T, @@ -83,6 +80,7 @@ fn test_harness>( context.sender().clone(), Default::default(), None, + false, ); let target_hash = case_vars.target_block; diff --git a/polkadot/node/subsystem-bench/Cargo.toml b/polkadot/node/subsystem-bench/Cargo.toml index ae798cf2640a727d36c79a56019337792a1485b6..293df9f6e6d5cdb370e33b4d36ab6c7b3c0ba647 100644 --- a/polkadot/node/subsystem-bench/Cargo.toml +++ b/polkadot/node/subsystem-bench/Cargo.toml @@ -80,6 +80,7 @@ serde_yaml = { workspace = true } serde_json = { workspace = true } polkadot-node-core-approval-voting = { workspace = true, default-features = true } +polkadot-node-core-approval-voting-parallel = { workspace = true, default-features = true } polkadot-approval-distribution = { workspace = true, default-features = true } sp-consensus-babe = { workspace = true, default-features = true } sp-runtime = { workspace = true } diff --git a/polkadot/node/subsystem-bench/examples/approvals_no_shows.yaml b/polkadot/node/subsystem-bench/examples/approvals_no_shows.yaml index cae1a30914da78c32a54ad035b8870f3a4412548..1423d324df3f05e911465f62d09bbad38084a408 100644 --- a/polkadot/node/subsystem-bench/examples/approvals_no_shows.yaml +++ b/polkadot/node/subsystem-bench/examples/approvals_no_shows.yaml @@ -9,6 +9,7 @@ TestConfiguration: coalesce_tranche_diff: 12 num_no_shows_per_candidate: 10 workdir_prefix: "/tmp/" + approval_voting_parallel_enabled: false n_validators: 500 n_cores: 100 min_pov_size: 1120 diff --git a/polkadot/node/subsystem-bench/examples/approvals_throughput.yaml b/polkadot/node/subsystem-bench/examples/approvals_throughput.yaml index 7edb48e302a46b464e86d5b3535ab71d1585a408..87c6103a5d0a64a6b8a0a22348b84feb1ff26f4d 100644 --- a/polkadot/node/subsystem-bench/examples/approvals_throughput.yaml +++ b/polkadot/node/subsystem-bench/examples/approvals_throughput.yaml @@ -9,6 +9,7 @@ TestConfiguration: coalesce_tranche_diff: 12 num_no_shows_per_candidate: 0 workdir_prefix: "/tmp" + approval_voting_parallel_enabled: true n_validators: 500 n_cores: 100 min_pov_size: 1120 diff --git a/polkadot/node/subsystem-bench/examples/approvals_throughput_best_case.yaml b/polkadot/node/subsystem-bench/examples/approvals_throughput_best_case.yaml index 7c24f50e6af5531ab652e838f362733f0fda6707..5e2ea3817d171206c8294475cbd8ac38e0cf2042 100644 --- a/polkadot/node/subsystem-bench/examples/approvals_throughput_best_case.yaml +++ b/polkadot/node/subsystem-bench/examples/approvals_throughput_best_case.yaml @@ -8,6 +8,7 @@ TestConfiguration: stop_when_approved: true coalesce_tranche_diff: 12 num_no_shows_per_candidate: 0 + approval_voting_parallel_enabled: false workdir_prefix: "/tmp/" n_validators: 500 n_cores: 100 diff --git a/polkadot/node/subsystem-bench/src/lib/approval/helpers.rs b/polkadot/node/subsystem-bench/src/lib/approval/helpers.rs index ca58875c81393eebfea236e4fee0c25ca9d3a023..a3a475ac6b983dbc7981120e24acc8196df46df1 100644 --- a/polkadot/node/subsystem-bench/src/lib/approval/helpers.rs +++ b/polkadot/node/subsystem-bench/src/lib/approval/helpers.rs @@ -16,13 +16,16 @@ use crate::configuration::TestAuthorities; use itertools::Itertools; -use polkadot_node_core_approval_voting::time::{Clock, SystemClock, Tick}; use polkadot_node_network_protocol::{ grid_topology::{SessionGridTopology, TopologyPeerInfo}, View, }; +use polkadot_node_primitives::approval::time::{Clock, SystemClock, Tick}; +use polkadot_node_subsystem::messages::{ + ApprovalDistributionMessage, ApprovalVotingParallelMessage, +}; use polkadot_node_subsystem_types::messages::{ - network_bridge_event::NewGossipTopology, ApprovalDistributionMessage, NetworkBridgeEvent, + network_bridge_event::NewGossipTopology, NetworkBridgeEvent, }; use polkadot_overseer::AllMessages; use polkadot_primitives::{ @@ -121,6 +124,7 @@ pub fn generate_topology(test_authorities: &TestAuthorities) -> SessionGridTopol pub fn generate_new_session_topology( test_authorities: &TestAuthorities, test_node: ValidatorIndex, + approval_voting_parallel_enabled: bool, ) -> Vec { let topology = generate_topology(test_authorities); @@ -129,14 +133,29 @@ pub fn generate_new_session_topology( topology, local_index: Some(test_node), }); - vec![AllMessages::ApprovalDistribution(ApprovalDistributionMessage::NetworkBridgeUpdate(event))] + vec![if approval_voting_parallel_enabled { + AllMessages::ApprovalVotingParallel(ApprovalVotingParallelMessage::NetworkBridgeUpdate( + event, + )) + } else { + 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 { +pub fn generate_peer_view_change_for( + block_hash: Hash, + peer_id: PeerId, + approval_voting_parallel_enabled: bool, +) -> AllMessages { let network = NetworkBridgeEvent::PeerViewChange(peer_id, View::new([block_hash], 0)); - - AllMessages::ApprovalDistribution(ApprovalDistributionMessage::NetworkBridgeUpdate(network)) + if approval_voting_parallel_enabled { + AllMessages::ApprovalVotingParallel(ApprovalVotingParallelMessage::NetworkBridgeUpdate( + network, + )) + } else { + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::NetworkBridgeUpdate(network)) + } } /// Helper function to create a a signature for the block header. diff --git a/polkadot/node/subsystem-bench/src/lib/approval/message_generator.rs b/polkadot/node/subsystem-bench/src/lib/approval/message_generator.rs index 6d3e7dd92db151d067c9385dfdb4d39419152b76..807afb0438c99c7277f264ccd2b7ced1231aea33 100644 --- a/polkadot/node/subsystem-bench/src/lib/approval/message_generator.rs +++ b/polkadot/node/subsystem-bench/src/lib/approval/message_generator.rs @@ -28,16 +28,15 @@ use crate::{ use codec::Encode; use futures::SinkExt; use itertools::Itertools; -use polkadot_node_core_approval_voting::{ - criteria::{compute_assignments, Config}, - time::tranche_to_tick, -}; +use polkadot_node_core_approval_voting::criteria::{compute_assignments, Config}; + use polkadot_node_network_protocol::{ grid_topology::{GridNeighbors, RandomRouting, RequiredRouting, SessionGridTopology}, v3 as protocol_v3, }; use polkadot_node_primitives::approval::{ self, + time::tranche_to_tick, v2::{CoreBitfield, IndirectAssignmentCertV2, IndirectSignedApprovalVoteV2}, }; use polkadot_primitives::{ @@ -402,7 +401,7 @@ impl PeerMessagesGenerator { /// 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. +/// test. fn random_samplings_to_node( node_under_test: ValidatorIndex, num_validators: usize, @@ -475,8 +474,7 @@ fn issue_approvals( coalesce_approvals_len(options.coalesce_mean, options.coalesce_std_dev, rand_chacha); let result = assignments .iter() - .enumerate() - .map(|(_index, message)| match &message.msg { + .map(|message| match &message.msg { protocol_v3::ApprovalDistributionMessage::Assignments(assignments) => { let mut approvals_to_create = Vec::new(); diff --git a/polkadot/node/subsystem-bench/src/lib/approval/mock_chain_selection.rs b/polkadot/node/subsystem-bench/src/lib/approval/mock_chain_selection.rs index 77ba80d4b2bbcbc7616c00ad6f5069ceb22979bc..709d56d52f0b8a2c3781372be5cb6a44b6739831 100644 --- a/polkadot/node/subsystem-bench/src/lib/approval/mock_chain_selection.rs +++ b/polkadot/node/subsystem-bench/src/lib/approval/mock_chain_selection.rs @@ -16,7 +16,7 @@ use crate::approval::{ApprovalTestState, PastSystemClock, LOG_TARGET, SLOT_DURATION_MILLIS}; use futures::FutureExt; -use polkadot_node_core_approval_voting::time::{slot_number_to_tick, Clock, TICK_DURATION_MILLIS}; +use polkadot_node_primitives::approval::time::{slot_number_to_tick, Clock, TICK_DURATION_MILLIS}; use polkadot_node_subsystem::{overseer, SpawnedSubsystem, SubsystemError}; use polkadot_node_subsystem_types::messages::ChainSelectionMessage; diff --git a/polkadot/node/subsystem-bench/src/lib/approval/mod.rs b/polkadot/node/subsystem-bench/src/lib/approval/mod.rs index 404df2f8c65365e66d8bf318ce8c33ba08c5b3b6..29ebc4a419aee60e0d715c11dfddef06af8caa00 100644 --- a/polkadot/node/subsystem-bench/src/lib/approval/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/approval/mod.rs @@ -49,16 +49,21 @@ use itertools::Itertools; use orchestra::TimeoutExt; use overseer::{metrics::Metrics as OverseerMetrics, MetricsTrait}; use polkadot_approval_distribution::ApprovalDistribution; +use polkadot_node_core_approval_voting_parallel::ApprovalVotingParallelSubsystem; +use polkadot_node_primitives::approval::time::{ + slot_number_to_tick, tick_to_slot_number, Clock, ClockExt, SystemClock, +}; + use polkadot_node_core_approval_voting::{ - time::{slot_number_to_tick, tick_to_slot_number, Clock, ClockExt, SystemClock}, - ApprovalVotingSubsystem, Config as ApprovalVotingConfig, Metrics as ApprovalVotingMetrics, + ApprovalVotingSubsystem, Config as ApprovalVotingConfig, RealAssignmentCriteria, }; 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::{ + messages::{ApprovalDistributionMessage, ApprovalVotingMessage, ApprovalVotingParallelMessage}, + 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, ValidatorId, @@ -134,6 +139,9 @@ pub struct ApprovalsOptions { /// The number of no shows per candidate #[clap(short, long, default_value_t = 0)] pub num_no_shows_per_candidate: u32, + /// Enable approval voting parallel. + #[clap(short, long, default_value_t = true)] + pub approval_voting_parallel_enabled: bool, } impl ApprovalsOptions { @@ -268,7 +276,7 @@ pub struct ApprovalTestState { /// Total unique sent messages. total_unique_messages: Arc, /// Approval voting metrics. - approval_voting_metrics: ApprovalVotingMetrics, + approval_voting_parallel_metrics: polkadot_node_core_approval_voting_parallel::Metrics, /// The delta ticks from the tick the messages were generated to the the time we start this /// message. delta_tick_from_generated: Arc, @@ -326,7 +334,10 @@ impl ApprovalTestState { 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) + approval_voting_parallel_metrics: + polkadot_node_core_approval_voting_parallel::Metrics::try_register( + &dependencies.registry, + ) .unwrap(), delta_tick_from_generated: Arc::new(AtomicU64::new(630720000)), configuration: configuration.clone(), @@ -452,6 +463,14 @@ impl ApprovalTestState { }) .collect() } + + fn subsystem_name(&self) -> &'static str { + if self.options.approval_voting_parallel_enabled { + "approval-voting-parallel-subsystem" + } else { + "approval-distribution-subsystem" + } + } } impl ApprovalTestState { @@ -593,13 +612,16 @@ impl PeerMessageProducer { // 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; + let msg = if self.options.approval_voting_parallel_enabled { + AllMessages::ApprovalVotingParallel( + ApprovalVotingParallelMessage::GetApprovalSignatures(HashSet::new(), tx), + ) + } else { + AllMessages::ApprovalDistribution( + ApprovalDistributionMessage::GetApprovalSignatures(HashSet::new(), tx), + ) + }; + self.send_overseer_message(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 "); @@ -739,7 +761,11 @@ impl PeerMessageProducer { 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); + let view_update = generate_peer_view_change_for( + block_info.hash, + *peer_id, + self.state.options.approval_voting_parallel_enabled, + ); self.send_overseer_message(view_update, validator, None).await; } @@ -804,19 +830,12 @@ fn build_overseer( 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 keystore = Arc::new(keystore); + let db = Arc::new(db); - 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_chain_selection = + MockChainSelection { state: state.clone(), clock: system_clock.clone() }; let mock_runtime_api = MockRuntimeApi::new( config.clone(), state.test_authorities.clone(), @@ -831,11 +850,14 @@ fn build_overseer( network_interface.subsystem_sender(), state.test_authorities.clone(), ); - let mock_rx_bridge = MockNetworkBridgeRx::new(network_receiver, None); + let mock_rx_bridge = MockNetworkBridgeRx::new( + network_receiver, + None, + state.options.approval_voting_parallel_enabled, + ); 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) + let task_handle = spawn_task_handle.clone(); + let dummy = dummy_builder!(task_handle, overseer_metrics) .replace_chain_api(|_| mock_chain_api) .replace_chain_selection(|_| mock_chain_selection) .replace_runtime_api(|_| mock_runtime_api) @@ -844,8 +866,45 @@ fn build_overseer( .replace_availability_recovery(|_| MockAvailabilityRecovery::new()) .replace_candidate_validation(|_| MockCandidateValidation::new()); - let (overseer, raw_handle) = - dummy.build_with_connector(overseer_connector).expect("Should not fail"); + let (overseer, raw_handle) = if state.options.approval_voting_parallel_enabled { + let approval_voting_parallel = ApprovalVotingParallelSubsystem::with_config_and_clock( + TEST_CONFIG, + db.clone(), + keystore.clone(), + Box::new(TestSyncOracle {}), + state.approval_voting_parallel_metrics.clone(), + Arc::new(system_clock.clone()), + SpawnGlue(spawn_task_handle.clone()), + None, + ); + dummy + .replace_approval_voting_parallel(|_| approval_voting_parallel) + .build_with_connector(overseer_connector) + .expect("Should not fail") + } else { + let approval_voting = ApprovalVotingSubsystem::with_config_and_clock( + TEST_CONFIG, + db.clone(), + keystore.clone(), + Box::new(TestSyncOracle {}), + state.approval_voting_parallel_metrics.approval_voting_metrics(), + Arc::new(system_clock.clone()), + Arc::new(SpawnGlue(spawn_task_handle.clone())), + ); + + let approval_distribution = ApprovalDistribution::new_with_clock( + state.approval_voting_parallel_metrics.approval_distribution_metrics(), + TEST_CONFIG.slot_duration_millis, + Arc::new(system_clock.clone()), + Arc::new(RealAssignmentCriteria {}), + ); + + dummy + .replace_approval_voting(|_| approval_voting) + .replace_approval_distribution(|_| approval_distribution) + .build_with_connector(overseer_connector) + .expect("Should not fail") + }; let overseer_handle = OverseerHandleReal::new(raw_handle); (overseer, overseer_handle) @@ -934,11 +993,18 @@ pub async fn bench_approvals_run( // 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(|e| { - AllMessages::ApprovalDistribution(ApprovalDistributionMessage::NetworkBridgeUpdate(e)) + if state.options.approval_voting_parallel_enabled { + AllMessages::ApprovalVotingParallel(ApprovalVotingParallelMessage::NetworkBridgeUpdate( + e, + )) + } else { + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::NetworkBridgeUpdate(e)) + } }); initialization_messages.extend(generate_new_session_topology( &state.test_authorities, ValidatorIndex(NODE_UNDER_TEST), + state.options.approval_voting_parallel_enabled, )); for message in initialization_messages { env.send_message(message).await; @@ -1003,7 +1069,14 @@ pub async fn bench_approvals_run( 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")), + Some(( + "subsystem_name", + if state.options.approval_voting_parallel_enabled { + "approval-voting-parallel-subsystem" + } else { + "approval-distribution-subsystem" + }, + )), |value| { gum::debug!(target: LOG_TARGET, ?value, ?at_least_messages, "Waiting metric"); value >= at_least_messages as f64 @@ -1020,11 +1093,22 @@ pub async fn bench_approvals_run( 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 msg = if state.options.approval_voting_parallel_enabled { + AllMessages::ApprovalVotingParallel( + ApprovalVotingParallelMessage::GetApprovalSignaturesForCandidate( + receipt_fetch.hash(), + tx, + ), + ) + } else { + AllMessages::ApprovalVoting( + ApprovalVotingMessage::GetApprovalSignaturesForCandidate( + receipt_fetch.hash(), + tx, + ), + ) + }; + env.send_message(msg).await; let result = rx.await.unwrap(); @@ -1048,7 +1132,7 @@ pub async fn bench_approvals_run( 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")), + Some(("subsystem_name", state.subsystem_name())), |value| { gum::debug!(target: LOG_TARGET, ?value, ?at_least_messages, "Waiting metric"); value >= at_least_messages as f64 @@ -1089,5 +1173,8 @@ pub async fn bench_approvals_run( state.total_unique_messages.load(std::sync::atomic::Ordering::SeqCst) ); - env.collect_resource_usage(&["approval-distribution", "approval-voting"]) + env.collect_resource_usage( + &["approval-distribution", "approval-voting", "approval-voting-parallel"], + true, + ) } diff --git a/polkadot/node/subsystem-bench/src/lib/availability/mod.rs b/polkadot/node/subsystem-bench/src/lib/availability/mod.rs index 32dc8ae2c8dca329df20277111ef79a036a82bb2..a99f013195fa87a9be3bd439d4e80e57f5c4ba54 100644 --- a/polkadot/node/subsystem-bench/src/lib/availability/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/availability/mod.rs @@ -49,10 +49,7 @@ use polkadot_node_subsystem::{ messages::{AllMessages, AvailabilityRecoveryMessage}, Overseer, OverseerConnector, SpawnGlue, }; -use polkadot_node_subsystem_types::{ - messages::{AvailabilityStoreMessage, NetworkBridgeEvent}, - Span, -}; +use polkadot_node_subsystem_types::messages::{AvailabilityStoreMessage, NetworkBridgeEvent}; use polkadot_overseer::{metrics::Metrics as OverseerMetrics, Handle as OverseerHandle}; use polkadot_primitives::{Block, CoreIndex, GroupIndex, Hash}; use sc_network::request_responses::{IncomingRequest as RawIncomingRequest, ProtocolConfig}; @@ -210,7 +207,7 @@ pub fn prepare_test( state.test_authorities.clone(), ); let network_bridge_rx = - network_bridge::MockNetworkBridgeRx::new(network_receiver, Some(chunk_req_v2_cfg)); + network_bridge::MockNetworkBridgeRx::new(network_receiver, Some(chunk_req_v2_cfg), false); let runtime_api = MockRuntimeApi::new( state.config.clone(), @@ -372,7 +369,7 @@ pub async fn benchmark_availability_read( ); env.stop().await; - env.collect_resource_usage(&["availability-recovery"]) + env.collect_resource_usage(&["availability-recovery"], false) } pub async fn benchmark_availability_write( @@ -421,7 +418,7 @@ pub async fn benchmark_availability_write( // 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)) + NetworkBridgeEvent::OurViewChange(OurView::new(vec![relay_block_hash], 0)) ); env.send_message(AllMessages::BitfieldDistribution(message)).await; @@ -506,9 +503,8 @@ pub async fn benchmark_availability_write( ); env.stop().await; - env.collect_resource_usage(&[ - "availability-distribution", - "bitfield-distribution", - "availability-store", - ]) + env.collect_resource_usage( + &["availability-distribution", "bitfield-distribution", "availability-store"], + false, + ) } diff --git a/polkadot/node/subsystem-bench/src/lib/display.rs b/polkadot/node/subsystem-bench/src/lib/display.rs index b153d54a7c36f43bc110dde9443f2413a34d9af6..c47dd9a07900ebe795abdb74fceac1f2c006c05d 100644 --- a/polkadot/node/subsystem-bench/src/lib/display.rs +++ b/polkadot/node/subsystem-bench/src/lib/display.rs @@ -96,6 +96,23 @@ pub struct TestMetric { value: f64, } +impl TestMetric { + pub fn name(&self) -> &str { + &self.name + } + + pub fn value(&self) -> f64 { + self.value + } + + pub fn label_value(&self, label_name: &str) -> Option<&str> { + self.label_names + .iter() + .position(|name| name == label_name) + .and_then(|index| self.label_values.get(index).map(|s| s.as_str())) + } +} + impl Display for TestMetric { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( diff --git a/polkadot/node/subsystem-bench/src/lib/environment.rs b/polkadot/node/subsystem-bench/src/lib/environment.rs index a63f90da50b3aabcacca38874d723e7166173842..4de683ad6487b2156d0a2dff7d289f029662ed05 100644 --- a/polkadot/node/subsystem-bench/src/lib/environment.rs +++ b/polkadot/node/subsystem-bench/src/lib/environment.rs @@ -351,10 +351,14 @@ impl TestEnvironment { } } - pub fn collect_resource_usage(&self, subsystems_under_test: &[&str]) -> BenchmarkUsage { + pub fn collect_resource_usage( + &self, + subsystems_under_test: &[&str], + break_down_cpu_usage_per_task: bool, + ) -> BenchmarkUsage { BenchmarkUsage { network_usage: self.network_usage(), - cpu_usage: self.cpu_usage(subsystems_under_test), + cpu_usage: self.cpu_usage(subsystems_under_test, break_down_cpu_usage_per_task), } } @@ -378,7 +382,11 @@ impl TestEnvironment { ] } - fn cpu_usage(&self, subsystems_under_test: &[&str]) -> Vec { + fn cpu_usage( + &self, + subsystems_under_test: &[&str], + break_down_per_task: bool, + ) -> Vec { let test_metrics = super::display::parse_metrics(self.registry()); let mut usage = vec![]; let num_blocks = self.config().num_blocks as f64; @@ -392,6 +400,22 @@ impl TestEnvironment { total: total_cpu, per_block: total_cpu / num_blocks, }); + + if break_down_per_task { + for metric in subsystem_cpu_metrics.all() { + if metric.name() != "substrate_tasks_polling_duration_sum" { + continue; + } + + if let Some(task_name) = metric.label_value("task_name") { + usage.push(ResourceUsage { + resource_name: format!("{}/{}", subsystem, task_name), + total: metric.value(), + per_block: metric.value() / num_blocks, + }); + } + } + } } let test_env_cpu_metrics = diff --git a/polkadot/node/subsystem-bench/src/lib/mock/dummy.rs b/polkadot/node/subsystem-bench/src/lib/mock/dummy.rs index 8783b35f1c04a9c59bf415340689b1a9ae29ee6a..092a8fc5f4c12e5742f3ef7abed7372d295d0cd9 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/dummy.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/dummy.rs @@ -96,5 +96,6 @@ mock!(NetworkBridgeTx); mock!(ChainApi); mock!(ChainSelection); mock!(ApprovalVoting); +mock!(ApprovalVotingParallel); mock!(ApprovalDistribution); mock!(RuntimeApi); diff --git a/polkadot/node/subsystem-bench/src/lib/mock/mod.rs b/polkadot/node/subsystem-bench/src/lib/mock/mod.rs index da4ac05e33b7c9af76ab6804961a53cfbe68f9b0..00c19fe62cc48663c6efb235d5379513fe3189f0 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/mod.rs @@ -47,6 +47,7 @@ macro_rules! dummy_builder { // All subsystem except approval_voting and approval_distribution are mock subsystems. Overseer::builder() .approval_voting(MockApprovalVoting {}) + .approval_voting_parallel(MockApprovalVotingParallel {}) .approval_distribution(MockApprovalDistribution {}) .availability_recovery(MockAvailabilityRecovery {}) .candidate_validation(MockCandidateValidation {}) @@ -70,7 +71,6 @@ macro_rules! dummy_builder { .dispute_distribution(MockDisputeDistribution {}) .prospective_parachains(MockProspectiveParachains {}) .activation_external_listeners(Default::default()) - .span_per_active_leaf(Default::default()) .active_leaves(Default::default()) .metrics($metrics) .supports_parachains(AlwaysSupportsParachains {}) diff --git a/polkadot/node/subsystem-bench/src/lib/mock/network_bridge.rs b/polkadot/node/subsystem-bench/src/lib/mock/network_bridge.rs index d70953926d130eabc7be096cab71ff59d6e91bc8..f5474a61e3dca3e8f0d571d5326c5b8710cc3e37 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/network_bridge.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/network_bridge.rs @@ -24,13 +24,13 @@ use crate::{ 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, - StatementDistributionMessage, + ApprovalDistributionMessage, ApprovalVotingParallelMessage, NetworkBridgeTxMessage, }, + overseer, SpawnedSubsystem, SubsystemError, +}; +use polkadot_node_subsystem_types::{ + messages::{BitfieldDistributionMessage, NetworkBridgeEvent, StatementDistributionMessage}, OverseerSignal, }; use sc_network::{request_responses::ProtocolConfig, RequestFailure}; @@ -57,6 +57,8 @@ pub struct MockNetworkBridgeRx { network_receiver: NetworkInterfaceReceiver, /// Chunk request sender chunk_request_sender: Option, + /// Approval voting parallel enabled. + approval_voting_parallel_enabled: bool, } impl MockNetworkBridgeTx { @@ -73,8 +75,9 @@ impl MockNetworkBridgeRx { pub fn new( network_receiver: NetworkInterfaceReceiver, chunk_request_sender: Option, + approval_voting_parallel_enabled: bool, ) -> MockNetworkBridgeRx { - Self { network_receiver, chunk_request_sender } + Self { network_receiver, chunk_request_sender, approval_voting_parallel_enabled } } } @@ -199,9 +202,15 @@ impl MockNetworkBridgeRx { 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; + if self.approval_voting_parallel_enabled { + ctx.send_message( + ApprovalVotingParallelMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerMessage(peer_id, polkadot_node_network_protocol::Versioned::V3(msg))) + ).await; + } else { + ctx.send_message( + ApprovalDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerMessage(peer_id, polkadot_node_network_protocol::Versioned::V3(msg))) + ).await; + } } Versioned::V3( polkadot_node_network_protocol::v3::ValidationProtocol::StatementDistribution(msg) diff --git a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs index bd47505f56aeabc201f0d57bec57fa6a3f3380d4..dd7095d3b00c9dc353ca95d89f388c0a683ea3dd 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs @@ -114,14 +114,14 @@ fn build_overseer( state.pvd.clone(), state.own_backing_group.clone(), ); - let (statement_req_receiver, statement_req_cfg) = IncomingRequest::get_config_receiver::< - Block, - sc_network::NetworkWorker, - >(&ReqProtocolNames::new(GENESIS_HASH, None)); - let (candidate_req_receiver, candidate_req_cfg) = IncomingRequest::get_config_receiver::< - Block, - sc_network::NetworkWorker, - >(&ReqProtocolNames::new(GENESIS_HASH, None)); + let (statement_req_receiver, statement_req_cfg) = + IncomingRequest::get_config_receiver::>( + &ReqProtocolNames::new(GENESIS_HASH, None), + ); + let (candidate_req_receiver, candidate_req_cfg) = + IncomingRequest::get_config_receiver::>( + &ReqProtocolNames::new(GENESIS_HASH, None), + ); let keystore = make_keystore(); let subsystem = StatementDistributionSubsystem::new( keystore.clone(), @@ -135,7 +135,8 @@ fn build_overseer( network_interface.subsystem_sender(), state.test_authorities.clone(), ); - let network_bridge_rx = MockNetworkBridgeRx::new(network_receiver, Some(candidate_req_cfg)); + let network_bridge_rx = + MockNetworkBridgeRx::new(network_receiver, Some(candidate_req_cfg), false); let dummy = dummy_builder!(spawn_task_handle, overseer_metrics) .replace_runtime_api(|_| mock_runtime_api) @@ -445,5 +446,5 @@ pub async fn benchmark_statement_distribution( ); env.stop().await; - env.collect_resource_usage(&["statement-distribution"]) + env.collect_resource_usage(&["statement-distribution"], false) } diff --git a/polkadot/node/subsystem-bench/src/lib/usage.rs b/polkadot/node/subsystem-bench/src/lib/usage.rs index 883e9aa7ad0a0cec210ef49ccd7542f17d7256ee..5f691ae2db3992ac60f0461e101416e0ae2e02a5 100644 --- a/polkadot/node/subsystem-bench/src/lib/usage.rs +++ b/polkadot/node/subsystem-bench/src/lib/usage.rs @@ -32,14 +32,14 @@ impl std::fmt::Display for BenchmarkUsage { write!( f, "\n{}\n{}\n\n{}\n{}\n", - format!("{:<32}{:>12}{:>12}", "Network usage, KiB", "total", "per block").blue(), + format!("{:<64}{:>12}{:>12}", "Network usage, KiB", "total", "per block").blue(), self.network_usage .iter() .map(|v| v.to_string()) .sorted() .collect::>() .join("\n"), - format!("{:<32}{:>12}{:>12}", "CPU usage, seconds", "total", "per block").blue(), + format!("{:<64}{:>12}{:>12}", "CPU usage, seconds", "total", "per block").blue(), self.cpu_usage .iter() .map(|v| v.to_string()) @@ -134,7 +134,7 @@ pub struct ResourceUsage { impl std::fmt::Display for ResourceUsage { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{:<32}{:>12.4}{:>12.4}", self.resource_name.cyan(), self.total, self.per_block) + write!(f, "{:<64}{:>12.4}{:>12.4}", self.resource_name.cyan(), self.total, self.per_block) } } diff --git a/polkadot/node/subsystem-test-helpers/src/lib.rs b/polkadot/node/subsystem-test-helpers/src/lib.rs index bdb0647fee6f5aec1dd6322b68c9a38f6e493b98..5b1f8d3223d1afd5122702c999ac2711722324b0 100644 --- a/polkadot/node/subsystem-test-helpers/src/lib.rs +++ b/polkadot/node/subsystem-test-helpers/src/lib.rs @@ -294,6 +294,9 @@ pub struct TestSubsystemContextHandle { /// Message counter over subsystems. pub message_counter: MessageCounter, + + /// Intermediate buffer for a message when using `peek`. + message_buffer: Option, } impl TestSubsystemContextHandle { @@ -323,12 +326,30 @@ impl TestSubsystemContextHandle { /// Receive the next message from the subsystem, or `None` if the channel has been closed. pub async fn try_recv(&mut self) -> Option { + if let Some(msg) = self.message_buffer.take() { + return Some(msg) + } + self.rx .next() .timeout(Self::TIMEOUT) .await .expect("`try_recv` does not timeout") } + + /// Peek into the next message from the subsystem or `None` if the channel has been closed. + pub async fn peek(&mut self) -> Option<&AllMessages> { + if self.message_buffer.is_none() { + self.message_buffer = self + .rx + .next() + .timeout(Self::TIMEOUT) + .await + .expect("`try_recv` does not timeout"); + } + + self.message_buffer.as_ref() + } } /// Make a test subsystem context with `buffer_size == 0`. This is used by most @@ -392,6 +413,7 @@ pub fn make_buffered_subsystem_context( tx: overseer_tx, rx: all_messages_rx, message_counter: message_counter.clone(), + message_buffer: None, }, ) } diff --git a/polkadot/node/subsystem-test-helpers/src/mock.rs b/polkadot/node/subsystem-test-helpers/src/mock.rs index 14026960ac13ec285c0dc972c26fbf7dd1b9b4b9..f73b4b573ff5f99a38a54e022ac4648a51bdb608 100644 --- a/polkadot/node/subsystem-test-helpers/src/mock.rs +++ b/polkadot/node/subsystem-test-helpers/src/mock.rs @@ -16,7 +16,7 @@ use std::sync::Arc; -use polkadot_node_subsystem::{jaeger, ActivatedLeaf, BlockInfo}; +use polkadot_node_subsystem::{ActivatedLeaf, BlockInfo}; use sc_client_api::UnpinHandle; use sc_keystore::LocalKeystore; use sc_utils::mpsc::tracing_unbounded; @@ -52,12 +52,7 @@ pub fn dummy_unpin_handle(block: Hash) -> UnpinHandle { /// Create a new leaf with the given hash and number. pub fn new_leaf(hash: Hash, number: BlockNumber) -> ActivatedLeaf { - ActivatedLeaf { - hash, - number, - unpin_handle: dummy_unpin_handle(hash), - span: Arc::new(jaeger::Span::Disabled), - } + ActivatedLeaf { hash, number, unpin_handle: dummy_unpin_handle(hash) } } /// Create a new leaf with the given hash and number. diff --git a/polkadot/node/subsystem-types/Cargo.toml b/polkadot/node/subsystem-types/Cargo.toml index c8fc324699e1754cb0d476a97ef29c8fea4ede74..b8bad8f8a2953a8327ff79f656f3af83ddb638ea 100644 --- a/polkadot/node/subsystem-types/Cargo.toml +++ b/polkadot/node/subsystem-types/Cargo.toml @@ -17,7 +17,6 @@ polkadot-primitives = { workspace = true, default-features = true } polkadot-node-primitives = { workspace = true, default-features = true } polkadot-node-network-protocol = { workspace = true, default-features = true } polkadot-statement-table = { workspace = true, default-features = true } -polkadot-node-jaeger = { workspace = true, default-features = true } orchestra = { features = ["futures_channel"], workspace = true } sc-network = { workspace = true, default-features = true } sc-network-types = { workspace = true, default-features = true } @@ -33,3 +32,4 @@ prometheus-endpoint = { workspace = true, default-features = true } thiserror = { workspace = true } async-trait = { workspace = true } bitvec = { features = ["alloc"], workspace = true } +strum = { features = ["derive"], workspace = true, default-features = true } diff --git a/polkadot/node/subsystem-types/src/errors.rs b/polkadot/node/subsystem-types/src/errors.rs index 8e1b515c8db0acc4ff30727ed0986c6322d941ce..8770f3a3d9a1affa2ca1a211ac434e260f08389f 100644 --- a/polkadot/node/subsystem-types/src/errors.rs +++ b/polkadot/node/subsystem-types/src/errors.rs @@ -16,7 +16,6 @@ //! Error types for the subsystem requests. -use crate::JaegerError; use ::orchestra::OrchestraError as OverseerError; use fatality::fatality; @@ -109,9 +108,6 @@ pub enum SubsystemError { #[error(transparent)] Prometheus(#[from] prometheus_endpoint::PrometheusError), - #[error(transparent)] - Jaeger(#[from] JaegerError), - #[error("Failed to {0}")] Context(String), diff --git a/polkadot/node/subsystem-types/src/lib.rs b/polkadot/node/subsystem-types/src/lib.rs index cd39aa03e56736399ab491705cc9568a52b2784e..cde6bba18e7aae23d57e70dc1c78212ffdfc9431 100644 --- a/polkadot/node/subsystem-types/src/lib.rs +++ b/polkadot/node/subsystem-types/src/lib.rs @@ -23,7 +23,7 @@ #![warn(missing_docs)] use smallvec::SmallVec; -use std::{fmt, sync::Arc}; +use std::fmt; pub use polkadot_primitives::{Block, BlockNumber, Hash}; @@ -42,9 +42,6 @@ pub mod messages; mod runtime_client; pub use runtime_client::{ChainApiBackend, DefaultSubsystemClient, RuntimeApiSubsystemClient}; -pub use jaeger::*; -pub use polkadot_node_jaeger as jaeger; - /// How many slots are stack-reserved for active leaves updates /// /// If there are fewer than this number of slots, then we've wasted some stack space. @@ -60,11 +57,6 @@ pub struct ActivatedLeaf { pub number: BlockNumber, /// A handle to unpin the block on drop. pub unpin_handle: UnpinHandle, - /// An associated [`jaeger::Span`]. - /// - /// NOTE: Each span should only be kept active as long as the leaf is considered active and - /// should be dropped when the leaf is deactivated. - pub span: Arc, } /// Changes in the set of active leaves: the parachain heads which we care to work on. diff --git a/polkadot/node/subsystem-types/src/messages.rs b/polkadot/node/subsystem-types/src/messages.rs index d067ca468011d6f196efef42b012f6c8f1459cb7..0017adb4556818aaf887c7a3a8cca191e870b372 100644 --- a/polkadot/node/subsystem-types/src/messages.rs +++ b/polkadot/node/subsystem-types/src/messages.rs @@ -24,6 +24,7 @@ use futures::channel::oneshot; use sc_network::{Multiaddr, ReputationChange}; +use strum::EnumIter; use thiserror::Error; pub use sc_network::IfDisconnected; @@ -33,7 +34,7 @@ use polkadot_node_network_protocol::{ }; use polkadot_node_primitives::{ approval::{ - v1::BlockApprovalMeta, + v1::{BlockApprovalMeta, DelayTranche}, v2::{CandidateBitfield, IndirectAssignmentCertV2, IndirectSignedApprovalVoteV2}, }, AvailableData, BabeEpoch, BlockWeight, CandidateVotes, CollationGenerationConfig, @@ -47,9 +48,10 @@ use polkadot_primitives::{ CandidateReceipt, CollatorId, CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState, ExecutorParams, GroupIndex, GroupRotationInfo, Hash, HeadData, Header as BlockHeader, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, MultiDisputeStatementSet, - NodeFeatures, OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, PvfExecKind, - SessionIndex, SessionInfo, SignedAvailabilityBitfield, SignedAvailabilityBitfields, - ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, + NodeFeatures, OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, + PvfExecKind as RuntimePvfExecKind, SessionIndex, SessionInfo, SignedAvailabilityBitfield, + SignedAvailabilityBitfields, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, + ValidatorSignature, }; use polkadot_statement_table::v2::Misbehavior; use std::{ @@ -142,28 +144,6 @@ pub enum PreCheckOutcome { /// or `Ok(ValidationResult::Invalid)`. #[derive(Debug)] pub enum CandidateValidationMessage { - /// Validate a candidate with provided parameters using relay-chain state. - /// - /// This will implicitly attempt to gather the `PersistedValidationData` and `ValidationCode` - /// from the runtime API of the chain, based on the `relay_parent` - /// of the `CandidateReceipt`. - /// - /// This will also perform checking of validation outputs against the acceptance criteria. - /// - /// If there is no state available which can provide this data or the core for - /// the para is not free at the relay-parent, an error is returned. - ValidateFromChainState { - /// The candidate receipt - candidate_receipt: CandidateReceipt, - /// The proof-of-validity - pov: Arc, - /// Session's executor parameters - executor_params: ExecutorParams, - /// Execution kind, used for timeouts and retries (backing/approvals) - exec_kind: PvfExecKind, - /// The sending side of the response channel - response_sender: oneshot::Sender>, - }, /// Validate a candidate with provided, exhaustive parameters for validation. /// /// Explicitly provide the `PersistedValidationData` and `ValidationCode` so this can do full @@ -204,6 +184,45 @@ pub enum CandidateValidationMessage { }, } +/// Extends primitives::PvfExecKind, which is a runtime parameter we don't want to change, +/// to separate and prioritize execution jobs by request type. +/// The order is important, because we iterate through the values and assume it is going from higher +/// to lowest priority. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, EnumIter)] +pub enum PvfExecKind { + /// For dispute requests + Dispute, + /// For approval requests + Approval, + /// For backing requests from system parachains. + BackingSystemParas, + /// For backing requests. + Backing, +} + +impl PvfExecKind { + /// Converts priority level to &str + pub fn as_str(&self) -> &str { + match *self { + Self::Dispute => "dispute", + Self::Approval => "approval", + Self::BackingSystemParas => "backing_system_paras", + Self::Backing => "backing", + } + } +} + +impl From for RuntimePvfExecKind { + fn from(exec: PvfExecKind) -> Self { + match exec { + PvfExecKind::Dispute => RuntimePvfExecKind::Approval, + PvfExecKind::Approval => RuntimePvfExecKind::Approval, + PvfExecKind::BackingSystemParas => RuntimePvfExecKind::Backing, + PvfExecKind::Backing => RuntimePvfExecKind::Backing, + } + } +} + /// Messages received by the Collator Protocol subsystem. #[derive(Debug, derive_more::From)] pub enum CollatorProtocolMessage { @@ -879,7 +898,7 @@ pub enum CollationGenerationMessage { SubmitCollation(SubmitCollationParams), } -/// The result type of [`ApprovalVotingMessage::CheckAndImportAssignment`] request. +/// The result type of [`ApprovalVotingMessage::ImportAssignment`] request. #[derive(Debug, Clone, PartialEq, Eq)] pub enum AssignmentCheckResult { /// The vote was accepted and should be propagated onwards. @@ -892,7 +911,7 @@ pub enum AssignmentCheckResult { Bad(AssignmentCheckError), } -/// The error result type of [`ApprovalVotingMessage::CheckAndImportAssignment`] request. +/// The error result type of [`ApprovalVotingMessage::ImportAssignment`] request. #[derive(Error, Debug, Clone, PartialEq, Eq)] #[allow(missing_docs)] pub enum AssignmentCheckError { @@ -912,7 +931,7 @@ pub enum AssignmentCheckError { InvalidBitfield(usize), } -/// The result type of [`ApprovalVotingMessage::CheckAndImportApproval`] request. +/// The result type of [`ApprovalVotingMessage::ImportApproval`] request. #[derive(Debug, Clone, PartialEq, Eq)] pub enum ApprovalCheckResult { /// The vote was accepted and should be propagated onwards. @@ -921,7 +940,7 @@ pub enum ApprovalCheckResult { Bad(ApprovalCheckError), } -/// The error result type of [`ApprovalVotingMessage::CheckAndImportApproval`] request. +/// The error result type of [`ApprovalVotingMessage::ImportApproval`] request. #[derive(Error, Debug, Clone, PartialEq, Eq)] #[allow(missing_docs)] pub enum ApprovalCheckError { @@ -955,6 +974,103 @@ pub struct BlockDescription { pub candidates: Vec, } +/// Message to the approval voting parallel subsystem running both approval-distribution and +/// approval-voting logic in parallel. This is a combination of all the messages ApprovalVoting and +/// ApprovalDistribution subsystems can receive. +/// +/// The reason this exists is, so that we can keep both modes of running in the same polkadot +/// binary, based on the value of `--approval-voting-parallel-enabled`, we decide if we run with two +/// different subsystems for approval-distribution and approval-voting or run the approval-voting +/// parallel which has several parallel workers for the approval-distribution and a worker for +/// approval-voting. +/// +/// This is meant to be a temporary state until we can safely remove running the two subsystems +/// individually. +#[derive(Debug, derive_more::From)] +pub enum ApprovalVotingParallelMessage { + /// Gets mapped into `ApprovalVotingMessage::ApprovedAncestor` + ApprovedAncestor(Hash, BlockNumber, oneshot::Sender>), + + /// Gets mapped into `ApprovalVotingMessage::GetApprovalSignaturesForCandidate` + GetApprovalSignaturesForCandidate( + CandidateHash, + oneshot::Sender, ValidatorSignature)>>, + ), + /// Gets mapped into `ApprovalDistributionMessage::NewBlocks` + NewBlocks(Vec), + /// Gets mapped into `ApprovalDistributionMessage::DistributeAssignment` + DistributeAssignment(IndirectAssignmentCertV2, CandidateBitfield), + /// Gets mapped into `ApprovalDistributionMessage::DistributeApproval` + DistributeApproval(IndirectSignedApprovalVoteV2), + /// An update from the network bridge, gets mapped into + /// `ApprovalDistributionMessage::NetworkBridgeUpdate` + #[from] + NetworkBridgeUpdate(NetworkBridgeEvent), + + /// Gets mapped into `ApprovalDistributionMessage::GetApprovalSignatures` + GetApprovalSignatures( + HashSet<(Hash, CandidateIndex)>, + oneshot::Sender, ValidatorSignature)>>, + ), + /// Gets mapped into `ApprovalDistributionMessage::ApprovalCheckingLagUpdate` + ApprovalCheckingLagUpdate(BlockNumber), +} + +impl TryFrom for ApprovalVotingMessage { + type Error = (); + + fn try_from(msg: ApprovalVotingParallelMessage) -> Result { + match msg { + ApprovalVotingParallelMessage::ApprovedAncestor(hash, number, tx) => + Ok(ApprovalVotingMessage::ApprovedAncestor(hash, number, tx)), + ApprovalVotingParallelMessage::GetApprovalSignaturesForCandidate(candidate, tx) => + Ok(ApprovalVotingMessage::GetApprovalSignaturesForCandidate(candidate, tx)), + _ => Err(()), + } + } +} + +impl TryFrom for ApprovalDistributionMessage { + type Error = (); + + fn try_from(msg: ApprovalVotingParallelMessage) -> Result { + match msg { + ApprovalVotingParallelMessage::NewBlocks(blocks) => + Ok(ApprovalDistributionMessage::NewBlocks(blocks)), + ApprovalVotingParallelMessage::DistributeAssignment(assignment, claimed_cores) => + Ok(ApprovalDistributionMessage::DistributeAssignment(assignment, claimed_cores)), + ApprovalVotingParallelMessage::DistributeApproval(vote) => + Ok(ApprovalDistributionMessage::DistributeApproval(vote)), + ApprovalVotingParallelMessage::NetworkBridgeUpdate(msg) => + Ok(ApprovalDistributionMessage::NetworkBridgeUpdate(msg)), + ApprovalVotingParallelMessage::GetApprovalSignatures(candidate_indicies, tx) => + Ok(ApprovalDistributionMessage::GetApprovalSignatures(candidate_indicies, tx)), + ApprovalVotingParallelMessage::ApprovalCheckingLagUpdate(lag) => + Ok(ApprovalDistributionMessage::ApprovalCheckingLagUpdate(lag)), + _ => Err(()), + } + } +} + +impl From for ApprovalVotingParallelMessage { + fn from(msg: ApprovalDistributionMessage) -> Self { + match msg { + ApprovalDistributionMessage::NewBlocks(blocks) => + ApprovalVotingParallelMessage::NewBlocks(blocks), + ApprovalDistributionMessage::DistributeAssignment(cert, bitfield) => + ApprovalVotingParallelMessage::DistributeAssignment(cert, bitfield), + ApprovalDistributionMessage::DistributeApproval(vote) => + ApprovalVotingParallelMessage::DistributeApproval(vote), + ApprovalDistributionMessage::NetworkBridgeUpdate(msg) => + ApprovalVotingParallelMessage::NetworkBridgeUpdate(msg), + ApprovalDistributionMessage::GetApprovalSignatures(candidate_indicies, tx) => + ApprovalVotingParallelMessage::GetApprovalSignatures(candidate_indicies, tx), + ApprovalDistributionMessage::ApprovalCheckingLagUpdate(lag) => + ApprovalVotingParallelMessage::ApprovalCheckingLagUpdate(lag), + } + } +} + /// Response type to `ApprovalVotingMessage::ApprovedAncestor`. #[derive(Clone, Debug)] pub struct HighestApprovedAncestorBlock { @@ -969,21 +1085,68 @@ pub struct HighestApprovedAncestorBlock { pub descriptions: Vec, } +/// A checked indirect assignment, the crypto for the cert has been validated +/// and the `candidate_bitfield` is correctly claimed at `delay_tranche`. +#[derive(Debug)] +pub struct CheckedIndirectAssignment { + assignment: IndirectAssignmentCertV2, + candidate_indices: CandidateBitfield, + tranche: DelayTranche, +} + +impl CheckedIndirectAssignment { + /// Builds a checked assignment from an assignment that was checked to be valid for the + /// `claimed_candidate_indices` at the give tranche + pub fn from_checked( + assignment: IndirectAssignmentCertV2, + claimed_candidate_indices: CandidateBitfield, + tranche: DelayTranche, + ) -> Self { + Self { assignment, candidate_indices: claimed_candidate_indices, tranche } + } + + /// Returns the indirect assignment. + pub fn assignment(&self) -> &IndirectAssignmentCertV2 { + &self.assignment + } + + /// Returns the candidate bitfield claimed by the assignment. + pub fn candidate_indices(&self) -> &CandidateBitfield { + &self.candidate_indices + } + + /// Returns the tranche this assignment is claimed at. + pub fn tranche(&self) -> DelayTranche { + self.tranche + } +} + +/// A checked indirect signed approval vote. +/// +/// The crypto for the vote has been validated and the signature can be trusted as being valid and +/// to correspond to the `validator_index` inside the structure. +#[derive(Debug, derive_more::Deref, derive_more::Into)] +pub struct CheckedIndirectSignedApprovalVote(IndirectSignedApprovalVoteV2); + +impl CheckedIndirectSignedApprovalVote { + /// Builds a checked vote from a vote that was checked to be valid and correctly signed. + pub fn from_checked(vote: IndirectSignedApprovalVoteV2) -> Self { + Self(vote) + } +} + /// Message to the Approval Voting subsystem. #[derive(Debug)] pub enum ApprovalVotingMessage { - /// Check if the assignment is valid and can be accepted by our view of the protocol. - /// Should not be sent unless the block hash is known. - CheckAndImportAssignment( - IndirectAssignmentCertV2, - CandidateBitfield, - oneshot::Sender, - ), - /// Check if the approval vote is valid and can be accepted by our view of the - /// protocol. + /// Import an assignment into the approval-voting database. + /// + /// Should not be sent unless the block hash is known and the VRF assignment checks out. + ImportAssignment(CheckedIndirectAssignment, Option>), + /// Import an approval vote into approval-voting database /// - /// Should not be sent unless the block hash within the indirect vote is known. - CheckAndImportApproval(IndirectSignedApprovalVoteV2, oneshot::Sender), + /// Should not be sent unless the block hash within the indirect vote is known, vote is + /// correctly signed and we had a previous assignment for the candidate. + ImportApproval(CheckedIndirectSignedApprovalVote, Option>), /// Returns the highest possible ancestor hash of the provided block hash which is /// acceptable to vote on finality for. /// The `BlockNumber` provided is the number of the block's ancestor which is the diff --git a/polkadot/node/subsystem-types/src/runtime_client.rs b/polkadot/node/subsystem-types/src/runtime_client.rs index e5e1e4d24ef96e8fa7689a41cd7897832b5aa271..a8af8b7996f9fb5a7d362c4a592c007cd86e29d3 100644 --- a/polkadot/node/subsystem-types/src/runtime_client.rs +++ b/polkadot/node/subsystem-types/src/runtime_client.rs @@ -380,7 +380,10 @@ where &self, at: Hash, ) -> Result>, ApiError> { - self.client.runtime_api().availability_cores(at) + self.client + .runtime_api() + .availability_cores(at) + .map(|cores| cores.into_iter().map(|core| core.into()).collect::>()) } async fn persisted_validation_data( @@ -433,7 +436,10 @@ where at: Hash, para_id: Id, ) -> Result>, ApiError> { - self.client.runtime_api().candidate_pending_availability(at, para_id) + self.client + .runtime_api() + .candidate_pending_availability(at, para_id) + .map(|maybe_candidate| maybe_candidate.map(|candidate| candidate.into())) } async fn candidates_pending_availability( @@ -441,11 +447,19 @@ where at: Hash, para_id: Id, ) -> Result>, ApiError> { - self.client.runtime_api().candidates_pending_availability(at, para_id) + self.client + .runtime_api() + .candidates_pending_availability(at, para_id) + .map(|candidates| { + candidates.into_iter().map(|candidate| candidate.into()).collect::>() + }) } async fn candidate_events(&self, at: Hash) -> Result>, ApiError> { - self.client.runtime_api().candidate_events(at) + self.client + .runtime_api() + .candidate_events(at) + .map(|events| events.into_iter().map(|event| event.into()).collect::>()) } async fn dmq_contents( @@ -476,7 +490,10 @@ where &self, at: Hash, ) -> Result>, ApiError> { - self.client.runtime_api().on_chain_votes(at) + self.client + .runtime_api() + .on_chain_votes(at) + .map(|maybe_votes| maybe_votes.map(|votes| votes.into())) } async fn session_executor_params( @@ -588,7 +605,12 @@ where at: Hash, para_id: Id, ) -> Result, ApiError> { - self.client.runtime_api().para_backing_state(at, para_id) + self.client + .runtime_api() + .para_backing_state(at, para_id) + .map(|maybe_backing_state| { + maybe_backing_state.map(|backing_state| backing_state.into()) + }) } async fn async_backing_params( @@ -643,7 +665,8 @@ where fn number( &self, hash: Block::Hash, - ) -> sc_client_api::blockchain::Result::Header as HeaderT>::Number>> { + ) -> sc_client_api::blockchain::Result::Header as HeaderT>::Number>> + { self.client.number(hash) } diff --git a/polkadot/node/subsystem-util/Cargo.toml b/polkadot/node/subsystem-util/Cargo.toml index a7157d1b5b7f46627665fb96d97f28c95fb105ec..d12daa57205521243251200fabc4aead5286e023 100644 --- a/polkadot/node/subsystem-util/Cargo.toml +++ b/polkadot/node/subsystem-util/Cargo.toml @@ -27,7 +27,6 @@ schnellru = { workspace = true } polkadot-erasure-coding = { workspace = true, default-features = true } polkadot-node-subsystem = { workspace = true, default-features = true } polkadot-node-subsystem-types = { workspace = true, default-features = true } -polkadot-node-jaeger = { workspace = true, default-features = true } polkadot-node-metrics = { workspace = true, default-features = true } polkadot-node-network-protocol = { workspace = true, default-features = true } polkadot-primitives = { workspace = true, default-features = true } @@ -48,7 +47,6 @@ assert_matches = { workspace = true } futures = { features = ["thread-pool"], workspace = true } log = { workspace = true, default-features = true } polkadot-node-subsystem-test-helpers = { workspace = true } -lazy_static = { workspace = true } polkadot-primitives-test-helpers = { workspace = true } kvdb-shared-tests = { workspace = true } tempfile = { workspace = true } diff --git a/polkadot/node/subsystem/Cargo.toml b/polkadot/node/subsystem/Cargo.toml index 8edfea9e26bf5d2176b672c9d24bb1e0251b33ae..ce4bceec7336e59efdee79ecc2c88293d66bfa67 100644 --- a/polkadot/node/subsystem/Cargo.toml +++ b/polkadot/node/subsystem/Cargo.toml @@ -12,4 +12,3 @@ workspace = true [dependencies] polkadot-overseer = { workspace = true, default-features = true } polkadot-node-subsystem-types = { workspace = true, default-features = true } -polkadot-node-jaeger = { workspace = true, default-features = true } diff --git a/polkadot/node/subsystem/src/lib.rs b/polkadot/node/subsystem/src/lib.rs index 8b407c75a0c82722419920e3288e2f83d8fb8419..bde5a623c476fbbae50732b96ba1cfbae955699a 100644 --- a/polkadot/node/subsystem/src/lib.rs +++ b/polkadot/node/subsystem/src/lib.rs @@ -21,9 +21,6 @@ #![deny(missing_docs)] #![deny(unused_crate_dependencies)] -pub use jaeger::*; -pub use polkadot_node_jaeger as jaeger; - pub use polkadot_overseer::{self as overseer, *}; pub use polkadot_node_subsystem_types::{ diff --git a/polkadot/node/test/client/src/block_builder.rs b/polkadot/node/test/client/src/block_builder.rs index 71bcdaffac4e7e69c7614abe4c51a50bd1e8556e..9375aca6ed7340bce68dc8b74da6a01708c24d29 100644 --- a/polkadot/node/test/client/src/block_builder.rs +++ b/polkadot/node/test/client/src/block_builder.rs @@ -16,7 +16,7 @@ use crate::Client; use codec::{Decode, Encode}; -use polkadot_primitives::{Block, InherentData as ParachainsInherentData}; +use polkadot_primitives::{vstaging::InherentData as ParachainsInherentData, Block}; use polkadot_test_runtime::UncheckedExtrinsic; use polkadot_test_service::GetLastTimestamp; use sc_block_builder::{BlockBuilder, BlockBuilderBuilder}; diff --git a/polkadot/node/test/service/src/chain_spec.rs b/polkadot/node/test/service/src/chain_spec.rs index 8add51b07521f4bbde67009d081b8fec0fd421ee..00d62af0b2734088a6f99503a3ae034d90c65c6b 100644 --- a/polkadot/node/test/service/src/chain_spec.rs +++ b/polkadot/node/test/service/src/chain_spec.rs @@ -20,7 +20,8 @@ use pallet_staking::Forcing; use polkadot_primitives::{ AccountId, AssignmentId, SchedulerParams, ValidatorId, MAX_CODE_SIZE, MAX_POV_SIZE, }; -use polkadot_service::chain_spec::{get_account_id_from_seed, get_from_seed, Extensions}; +use polkadot_service::chain_spec::Extensions; +pub use polkadot_service::chain_spec::{get_account_id_from_seed, get_from_seed}; use polkadot_test_runtime::BABE_GENESIS_EPOCH_CONFIG; use sc_chain_spec::{ChainSpec, ChainType}; use sc_consensus_grandpa::AuthorityId as GrandpaId; diff --git a/polkadot/node/test/service/src/lib.rs b/polkadot/node/test/service/src/lib.rs index a4e58253bb1706c9face2ded9dbbfb66a93ebb40..aa7295dddc5d2f6b904f86ed1c646ad871db6815 100644 --- a/polkadot/node/test/service/src/lib.rs +++ b/polkadot/node/test/service/src/lib.rs @@ -68,6 +68,7 @@ use substrate_test_client::{ pub type Client = FullClient; pub use polkadot_service::{FullBackend, GetLastTimestamp}; +use sc_service::config::{ExecutorConfiguration, RpcConfiguration}; /// Create a new full node. #[sc_tracing::logging::prefix_logs_with(config.network.node_name.as_str())] @@ -87,7 +88,6 @@ pub fn new_full( is_parachain_node, enable_beefy: true, force_authoring_backoff: false, - jaeger_agent: None, telemetry_worker_handle: None, node_version: None, secure_validator_mode: false, @@ -100,6 +100,7 @@ pub fn new_full( execute_workers_max_num: None, prepare_workers_hard_max_num: None, prepare_workers_soft_max_num: None, + enable_approval_voting_parallel: false, }, ), sc_network::config::NetworkBackendType::Litep2p => @@ -109,7 +110,6 @@ pub fn new_full( is_parachain_node, enable_beefy: true, force_authoring_backoff: false, - jaeger_agent: None, telemetry_worker_handle: None, node_version: None, secure_validator_mode: false, @@ -122,6 +122,7 @@ pub fn new_full( execute_workers_max_num: None, prepare_workers_hard_max_num: None, prepare_workers_soft_max_num: None, + enable_approval_voting_parallel: false, }, ), } @@ -200,35 +201,37 @@ pub fn node_config( state_pruning: Default::default(), blocks_pruning: BlocksPruning::KeepFinalized, chain_spec: Box::new(spec), - wasm_method: WasmExecutionMethod::Compiled { - instantiation_strategy: WasmtimeInstantiationStrategy::PoolingCopyOnWrite, + executor: ExecutorConfiguration { + wasm_method: WasmExecutionMethod::Compiled { + instantiation_strategy: WasmtimeInstantiationStrategy::PoolingCopyOnWrite, + }, + ..ExecutorConfiguration::default() }, wasm_runtime_overrides: Default::default(), - rpc_addr: Default::default(), - rpc_max_request_size: Default::default(), - rpc_max_response_size: Default::default(), - rpc_max_connections: Default::default(), - rpc_cors: None, - rpc_methods: Default::default(), - rpc_id_provider: None, - rpc_max_subs_per_conn: Default::default(), - rpc_port: 9944, - rpc_message_buffer_capacity: Default::default(), - rpc_batch_config: RpcBatchRequestConfig::Unlimited, - rpc_rate_limit: None, - rpc_rate_limit_whitelisted_ips: Default::default(), - rpc_rate_limit_trust_proxy_headers: Default::default(), + rpc: RpcConfiguration { + addr: Default::default(), + max_request_size: Default::default(), + max_response_size: Default::default(), + max_connections: Default::default(), + cors: None, + methods: Default::default(), + id_provider: None, + max_subs_per_conn: Default::default(), + port: 9944, + message_buffer_capacity: Default::default(), + batch_config: RpcBatchRequestConfig::Unlimited, + rate_limit: None, + rate_limit_whitelisted_ips: Default::default(), + rate_limit_trust_proxy_headers: Default::default(), + }, prometheus_config: None, telemetry_endpoints: None, - default_heap_pages: None, offchain_worker: Default::default(), force_authoring: false, disable_grandpa: false, dev_key_seed: Some(key_seed), tracing_targets: None, tracing_receiver: Default::default(), - max_runtime_instances: 8, - runtime_cache_size: 2, announce_block: true, data_path: root, base_path, diff --git a/polkadot/node/zombienet-backchannel/Cargo.toml b/polkadot/node/zombienet-backchannel/Cargo.toml index a9bf1f5ef093a905de7ca96c515a5a1b2c9cd17b..56c49a1ec305e17e949c71104bd659bdb5c379a6 100644 --- a/polkadot/node/zombienet-backchannel/Cargo.toml +++ b/polkadot/node/zombienet-backchannel/Cargo.toml @@ -16,7 +16,6 @@ tokio = { features = ["macros", "net", "rt-multi-thread", "sync"], workspace = t url = { workspace = true } tokio-tungstenite = { workspace = true } futures-util = { workspace = true, default-features = true } -lazy_static = { workspace = true } codec = { features = ["derive"], workspace = true, default-features = true } reqwest = { features = ["rustls-tls"], workspace = true } thiserror = { workspace = true } diff --git a/polkadot/node/zombienet-backchannel/src/lib.rs b/polkadot/node/zombienet-backchannel/src/lib.rs index 9068b03399ca5180ec17a03669be3ea25fc3c5db..080dcf1c2b75dce91b9fd38af59fadfbd7dc63bb 100644 --- a/polkadot/node/zombienet-backchannel/src/lib.rs +++ b/polkadot/node/zombienet-backchannel/src/lib.rs @@ -21,7 +21,6 @@ use codec; use futures_util::{SinkExt, StreamExt}; -use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; use std::{env, sync::Mutex}; use tokio::sync::broadcast; @@ -30,9 +29,7 @@ use tokio_tungstenite::{connect_async, tungstenite::protocol::Message}; mod errors; use errors::BackchannelError; -lazy_static! { - pub static ref ZOMBIENET_BACKCHANNEL: Mutex> = Mutex::new(None); -} +pub static ZOMBIENET_BACKCHANNEL: Mutex> = Mutex::new(None); #[derive(Debug)] pub struct ZombienetBackchannel { diff --git a/polkadot/parachain/test-parachains/adder/collator/src/main.rs b/polkadot/parachain/test-parachains/adder/collator/src/main.rs index e8588274df27aad40820d0a73e70ec5d24a89fb6..416e58b0a8ac6868213a2b0f165a934ad5357c66 100644 --- a/polkadot/parachain/test-parachains/adder/collator/src/main.rs +++ b/polkadot/parachain/test-parachains/adder/collator/src/main.rs @@ -82,7 +82,6 @@ fn main() -> Result<()> { ), enable_beefy: false, force_authoring_backoff: false, - jaeger_agent: None, telemetry_worker_handle: None, // Collators don't spawn PVF workers, so we can disable version checks. @@ -98,6 +97,7 @@ fn main() -> Result<()> { execute_workers_max_num: None, prepare_workers_hard_max_num: None, prepare_workers_soft_max_num: None, + enable_approval_voting_parallel: false, }, ) .map_err(|e| e.to_string())?; diff --git a/polkadot/parachain/test-parachains/undying/collator/src/main.rs b/polkadot/parachain/test-parachains/undying/collator/src/main.rs index 7198a831a4771b1a70de4180149ce81ffe6f5412..017eefe5ee31e1695272ac63ad43567604152f03 100644 --- a/polkadot/parachain/test-parachains/undying/collator/src/main.rs +++ b/polkadot/parachain/test-parachains/undying/collator/src/main.rs @@ -84,7 +84,6 @@ fn main() -> Result<()> { ), enable_beefy: false, force_authoring_backoff: false, - jaeger_agent: None, telemetry_worker_handle: None, // Collators don't spawn PVF workers, so we can disable version checks. @@ -100,6 +99,7 @@ fn main() -> Result<()> { execute_workers_max_num: None, prepare_workers_hard_max_num: None, prepare_workers_soft_max_num: None, + enable_approval_voting_parallel: false, }, ) .map_err(|e| e.to_string())?; diff --git a/polkadot/primitives/Cargo.toml b/polkadot/primitives/Cargo.toml index 8f7ec314ecffe6d7ca7f1f943a47971b4a1e9558..a8cd6cb5f4e00c36a3236f58c3ec72922d8b4d43 100644 --- a/polkadot/primitives/Cargo.toml +++ b/polkadot/primitives/Cargo.toml @@ -28,10 +28,14 @@ sp-consensus-slots = { features = ["serde"], workspace = true } sp-io = { workspace = true } sp-keystore = { optional = true, workspace = true } sp-staking = { features = ["serde"], workspace = true } +sp-std = { workspace = true, optional = true } polkadot-core-primitives = { workspace = true } polkadot-parachain-primitives = { workspace = true } +[dev-dependencies] +polkadot-primitives-test-helpers = { workspace = true } + [features] default = ["std"] std = [ @@ -54,9 +58,11 @@ std = [ "sp-keystore?/std", "sp-runtime/std", "sp-staking/std", + "sp-std/std", ] runtime-benchmarks = [ "polkadot-parachain-primitives/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "sp-staking/runtime-benchmarks", ] +test = [] diff --git a/polkadot/primitives/src/runtime_api.rs b/polkadot/primitives/src/runtime_api.rs index b4816ad15075dcc8d739259b779bfda1a9b5c84a..3c90c050baed1d9ff6b8239b0d69fa3dc14b16f5 100644 --- a/polkadot/primitives/src/runtime_api.rs +++ b/polkadot/primitives/src/runtime_api.rs @@ -36,7 +36,7 @@ //! //! Let's see a quick example: //! -//! ```rust(ignore) +//! ```nocompile //! sp_api::decl_runtime_apis! { //! #[api_version(2)] //! pub trait MyApi { @@ -114,11 +114,15 @@ //! separated from the stable primitives. use crate::{ - async_backing, slashing, ApprovalVotingParams, AsyncBackingParams, BlockNumber, - CandidateCommitments, CandidateEvent, CandidateHash, CommittedCandidateReceipt, CoreIndex, - CoreState, DisputeState, ExecutorParams, GroupRotationInfo, Hash, NodeFeatures, - OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, ScrapedOnChainVotes, - SessionIndex, SessionInfo, ValidatorId, ValidatorIndex, ValidatorSignature, + slashing, + vstaging::{ + self, CandidateEvent, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + ScrapedOnChainVotes, + }, + ApprovalVotingParams, AsyncBackingParams, BlockNumber, CandidateCommitments, CandidateHash, + CoreIndex, DisputeState, ExecutorParams, GroupRotationInfo, Hash, NodeFeatures, + OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, SessionIndex, SessionInfo, + ValidatorId, ValidatorIndex, ValidatorSignature, }; use alloc::{ @@ -260,7 +264,7 @@ sp_api::decl_runtime_apis! { /// Returns the state of parachain backing for a given para. #[api_version(7)] - fn para_backing_state(_: ppp::Id) -> Option>; + fn para_backing_state(_: ppp::Id) -> Option>; /// Returns candidate's acceptance limitations for asynchronous backing for a relay parent. #[api_version(7)] diff --git a/polkadot/primitives/src/v8/metrics.rs b/polkadot/primitives/src/v8/metrics.rs index 1d66c9848a7c4016a86f5ed1eb85e880b35bba91..409efc86bc9b9787e53e642dc925798c91c8c078 100644 --- a/polkadot/primitives/src/v8/metrics.rs +++ b/polkadot/primitives/src/v8/metrics.rs @@ -91,18 +91,6 @@ pub type RuntimeMetricLabelValue = RuntimeMetricLabel; /// A set of metric label values. pub type RuntimeMetricLabelValues = RuntimeMetricLabels; -/// Trait for converting Vec to `&str`. -pub trait AsStr { - /// Return a str reference. - fn as_str(&self) -> Option<&str>; -} - -impl AsStr for RuntimeMetricLabel { - fn as_str(&self) -> Option<&str> { - alloc::str::from_utf8(&self.0).ok() - } -} - impl From<&'static str> for RuntimeMetricLabel { fn from(s: &'static str) -> Self { Self(s.as_bytes().to_vec()) diff --git a/polkadot/primitives/src/v8/mod.rs b/polkadot/primitives/src/v8/mod.rs index b6928e372064621e64dbac2af346491f6bb10afc..a51ee0bd99bfe9d2c8db17b1db5cdfadce489277 100644 --- a/polkadot/primitives/src/v8/mod.rs +++ b/polkadot/primitives/src/v8/mod.rs @@ -15,7 +15,6 @@ // along with Polkadot. If not, see . //! `V7` Primitives. - use alloc::{ vec, vec::{IntoIter, Vec}, @@ -1157,7 +1156,7 @@ pub enum OccupiedCoreAssumption { Free, } -/// An even concerning a candidate. +/// An event concerning a candidate. #[derive(Clone, Encode, Decode, TypeInfo, RuntimeDebug)] #[cfg_attr(feature = "std", derive(PartialEq))] pub enum CandidateEvent { @@ -2130,7 +2129,7 @@ impl> Default for SchedulerParams } #[cfg(test)] -mod tests { +pub mod tests { use super::*; use bitvec::bitvec; use sp_core::sr25519; diff --git a/polkadot/primitives/src/vstaging/async_backing.rs b/polkadot/primitives/src/vstaging/async_backing.rs new file mode 100644 index 0000000000000000000000000000000000000000..8706214b5a0109b779140c4ffba7d52cbaada62e --- /dev/null +++ b/polkadot/primitives/src/vstaging/async_backing.rs @@ -0,0 +1,76 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +use super::*; + +use alloc::vec::Vec; +use codec::{Decode, Encode}; +use scale_info::TypeInfo; +use sp_core::RuntimeDebug; + +/// A candidate pending availability. +#[derive(RuntimeDebug, Clone, PartialEq, Encode, Decode, TypeInfo)] +pub struct CandidatePendingAvailability { + /// The hash of the candidate. + pub candidate_hash: CandidateHash, + /// The candidate's descriptor. + pub descriptor: CandidateDescriptorV2, + /// The commitments of the candidate. + pub commitments: CandidateCommitments, + /// The candidate's relay parent's number. + pub relay_parent_number: N, + /// The maximum Proof-of-Validity size allowed, in bytes. + pub max_pov_size: u32, +} + +impl From> + for crate::v8::async_backing::CandidatePendingAvailability +{ + fn from(value: CandidatePendingAvailability) -> Self { + Self { + candidate_hash: value.candidate_hash, + descriptor: value.descriptor.into(), + commitments: value.commitments, + relay_parent_number: value.relay_parent_number, + max_pov_size: value.max_pov_size, + } + } +} + +/// The per-parachain state of the backing system, including +/// state-machine constraints and candidates pending availability. +#[derive(RuntimeDebug, Clone, PartialEq, Encode, Decode, TypeInfo)] +pub struct BackingState { + /// The state-machine constraints of the parachain. + pub constraints: Constraints, + /// The candidates pending availability. These should be ordered, i.e. they should form + /// a sub-chain, where the first candidate builds on top of the required parent of the + /// constraints and each subsequent builds on top of the previous head-data. + pub pending_availability: Vec>, +} + +impl From> for crate::v8::async_backing::BackingState { + fn from(value: BackingState) -> Self { + Self { + constraints: value.constraints, + pending_availability: value + .pending_availability + .into_iter() + .map(|candidate| candidate.into()) + .collect::>(), + } + } +} diff --git a/polkadot/primitives/src/vstaging/mod.rs b/polkadot/primitives/src/vstaging/mod.rs index 1429b0c326aceef4b9088bd4ddef6828f8dcfbd8..bc687f7e2fbe751d89362070406e46d2e6a5202f 100644 --- a/polkadot/primitives/src/vstaging/mod.rs +++ b/polkadot/primitives/src/vstaging/mod.rs @@ -15,5 +15,1060 @@ // along with Polkadot. If not, see . //! Staging Primitives. +use crate::{ValidatorIndex, ValidityAttestation}; // Put any primitives used by staging APIs functions here +use super::{ + async_backing::Constraints, BlakeTwo256, BlockNumber, CandidateCommitments, + CandidateDescriptor, CandidateHash, CollatorId, CollatorSignature, CoreIndex, GroupIndex, Hash, + HashT, HeadData, Header, Id, Id as ParaId, MultiDisputeStatementSet, ScheduledCore, + UncheckedSignedAvailabilityBitfields, ValidationCodeHash, +}; +use alloc::{ + collections::{BTreeMap, BTreeSet, VecDeque}, + vec, + vec::Vec, +}; +use bitvec::prelude::*; +use codec::{Decode, Encode}; +use scale_info::TypeInfo; +use sp_application_crypto::ByteArray; +use sp_core::RuntimeDebug; +use sp_runtime::traits::Header as HeaderT; +use sp_staking::SessionIndex; +/// Async backing primitives +pub mod async_backing; + +/// A type representing the version of the candidate descriptor and internal version number. +#[derive(PartialEq, Eq, Encode, Decode, Clone, TypeInfo, RuntimeDebug, Copy)] +#[cfg_attr(feature = "std", derive(Hash))] +pub struct InternalVersion(pub u8); + +/// A type representing the version of the candidate descriptor. +#[derive(PartialEq, Eq, Clone, TypeInfo, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Hash))] +pub enum CandidateDescriptorVersion { + /// The old candidate descriptor version. + V1, + /// The new `CandidateDescriptorV2`. + V2, + /// An unknown version. + Unknown, +} + +/// A unique descriptor of the candidate receipt. +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Hash))] +pub struct CandidateDescriptorV2 { + /// The ID of the para this is a candidate for. + para_id: ParaId, + /// The hash of the relay-chain block this is executed in the context of. + relay_parent: H, + /// Version field. The raw value here is not exposed, instead it is used + /// to determine the `CandidateDescriptorVersion`, see `fn version()`. + /// For the current version this field is set to `0` and will be incremented + /// by next versions. + version: InternalVersion, + /// The core index where the candidate is backed. + core_index: u16, + /// The session index of the candidate relay parent. + session_index: SessionIndex, + /// Reserved bytes. + reserved1: [u8; 25], + /// The blake2-256 hash of the persisted validation data. This is extra data derived from + /// relay-chain state which may vary based on bitfields included before the candidate. + /// Thus it cannot be derived entirely from the relay-parent. + persisted_validation_data_hash: Hash, + /// The blake2-256 hash of the PoV. + pov_hash: Hash, + /// The root of a block's erasure encoding Merkle tree. + erasure_root: Hash, + /// Reserved bytes. + reserved2: [u8; 64], + /// Hash of the para header that is being generated by this candidate. + para_head: Hash, + /// The blake2-256 hash of the validation code bytes. + validation_code_hash: ValidationCodeHash, +} + +impl From> for CandidateDescriptor { + fn from(value: CandidateDescriptorV2) -> Self { + Self { + para_id: value.para_id, + relay_parent: value.relay_parent, + collator: value.rebuild_collator_field(), + persisted_validation_data_hash: value.persisted_validation_data_hash, + pov_hash: value.pov_hash, + erasure_root: value.erasure_root, + signature: value.rebuild_signature_field(), + para_head: value.para_head, + validation_code_hash: value.validation_code_hash, + } + } +} + +#[cfg(any(feature = "runtime-benchmarks", feature = "test"))] +impl From> for CandidateDescriptorV2 { + fn from(value: CandidateDescriptor) -> Self { + Decode::decode(&mut value.encode().as_slice()).unwrap() + } +} + +impl CandidateDescriptorV2 { + /// Constructor + pub fn new( + para_id: Id, + relay_parent: H, + core_index: CoreIndex, + session_index: SessionIndex, + persisted_validation_data_hash: Hash, + pov_hash: Hash, + erasure_root: Hash, + para_head: Hash, + validation_code_hash: ValidationCodeHash, + ) -> Self { + Self { + para_id, + relay_parent, + version: InternalVersion(0), + core_index: core_index.0 as u16, + session_index, + reserved1: [0; 25], + persisted_validation_data_hash, + pov_hash, + erasure_root, + reserved2: [0; 64], + para_head, + validation_code_hash, + } + } + + /// Set the PoV size in the descriptor. Only for tests. + #[cfg(feature = "test")] + pub fn set_pov_hash(&mut self, pov_hash: Hash) { + self.pov_hash = pov_hash; + } + + /// Set the version in the descriptor. Only for tests. + #[cfg(feature = "test")] + pub fn set_version(&mut self, version: InternalVersion) { + self.version = version; + } +} + +/// A candidate-receipt at version 2. +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Hash))] +pub struct CandidateReceiptV2 { + /// The descriptor of the candidate. + pub descriptor: CandidateDescriptorV2, + /// The hash of the encoded commitments made as a result of candidate execution. + pub commitments_hash: Hash, +} + +/// A candidate-receipt with commitments directly included. +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(Hash))] +pub struct CommittedCandidateReceiptV2 { + /// The descriptor of the candidate. + pub descriptor: CandidateDescriptorV2, + /// The commitments of the candidate receipt. + pub commitments: CandidateCommitments, +} + +/// An event concerning a candidate. +#[derive(Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(PartialEq))] +pub enum CandidateEvent { + /// This candidate receipt was backed in the most recent block. + /// This includes the core index the candidate is now occupying. + #[codec(index = 0)] + CandidateBacked(CandidateReceiptV2, HeadData, CoreIndex, GroupIndex), + /// This candidate receipt was included and became a parablock at the most recent block. + /// This includes the core index the candidate was occupying as well as the group responsible + /// for backing the candidate. + #[codec(index = 1)] + CandidateIncluded(CandidateReceiptV2, HeadData, CoreIndex, GroupIndex), + /// This candidate receipt was not made available in time and timed out. + /// This includes the core index the candidate was occupying. + #[codec(index = 2)] + CandidateTimedOut(CandidateReceiptV2, HeadData, CoreIndex), +} + +impl From> for super::v8::CandidateEvent { + fn from(value: CandidateEvent) -> Self { + match value { + CandidateEvent::CandidateBacked(receipt, head_data, core_index, group_index) => + super::v8::CandidateEvent::CandidateBacked( + receipt.into(), + head_data, + core_index, + group_index, + ), + CandidateEvent::CandidateIncluded(receipt, head_data, core_index, group_index) => + super::v8::CandidateEvent::CandidateIncluded( + receipt.into(), + head_data, + core_index, + group_index, + ), + CandidateEvent::CandidateTimedOut(receipt, head_data, core_index) => + super::v8::CandidateEvent::CandidateTimedOut(receipt.into(), head_data, core_index), + } + } +} + +impl CandidateReceiptV2 { + /// Get a reference to the candidate descriptor. + pub fn descriptor(&self) -> &CandidateDescriptorV2 { + &self.descriptor + } + + /// Computes the blake2-256 hash of the receipt. + pub fn hash(&self) -> CandidateHash + where + H: Encode, + { + CandidateHash(BlakeTwo256::hash_of(self)) + } +} + +impl CommittedCandidateReceiptV2 { + /// Transforms this into a plain `CandidateReceipt`. + pub fn to_plain(&self) -> CandidateReceiptV2 { + CandidateReceiptV2 { + descriptor: self.descriptor.clone(), + commitments_hash: self.commitments.hash(), + } + } + + /// Computes the hash of the committed candidate receipt. + /// + /// This computes the canonical hash, not the hash of the directly encoded data. + /// Thus this is a shortcut for `candidate.to_plain().hash()`. + pub fn hash(&self) -> CandidateHash + where + H: Encode, + { + self.to_plain().hash() + } + + /// Does this committed candidate receipt corresponds to the given [`CandidateReceiptV2`]? + pub fn corresponds_to(&self, receipt: &CandidateReceiptV2) -> bool + where + H: PartialEq, + { + receipt.descriptor == self.descriptor && receipt.commitments_hash == self.commitments.hash() + } +} + +impl PartialOrd for CommittedCandidateReceiptV2 { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for CommittedCandidateReceiptV2 { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + self.descriptor + .para_id + .cmp(&other.descriptor.para_id) + .then_with(|| self.commitments.head_data.cmp(&other.commitments.head_data)) + } +} + +impl From> for super::v8::CommittedCandidateReceipt { + fn from(value: CommittedCandidateReceiptV2) -> Self { + Self { descriptor: value.descriptor.into(), commitments: value.commitments } + } +} + +impl From> for super::v8::CandidateReceipt { + fn from(value: CandidateReceiptV2) -> Self { + Self { descriptor: value.descriptor.into(), commitments_hash: value.commitments_hash } + } +} + +/// A strictly increasing sequence number, typically this would be the least significant byte of the +/// block number. +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +pub struct CoreSelector(pub u8); + +/// An offset in the relay chain claim queue. +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +pub struct ClaimQueueOffset(pub u8); + +/// Signals that a parachain can send to the relay chain via the UMP queue. +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +pub enum UMPSignal { + /// A message sent by a parachain to select the core the candidate is committed to. + /// Relay chain validators, in particular backers, use the `CoreSelector` and + /// `ClaimQueueOffset` to compute the index of the core the candidate has committed to. + SelectCore(CoreSelector, ClaimQueueOffset), +} +/// Separator between `XCM` and `UMPSignal`. +pub const UMP_SEPARATOR: Vec = vec![]; + +impl CandidateCommitments { + /// Returns the core selector and claim queue offset the candidate has committed to, if any. + pub fn selected_core(&self) -> Option<(CoreSelector, ClaimQueueOffset)> { + // We need at least 2 messages for the separator and core selector + if self.upward_messages.len() < 2 { + return None + } + + let separator_pos = + self.upward_messages.iter().rposition(|message| message == &UMP_SEPARATOR)?; + + // Use first commitment + let message = self.upward_messages.get(separator_pos + 1)?; + + match UMPSignal::decode(&mut message.as_slice()).ok()? { + UMPSignal::SelectCore(core_selector, cq_offset) => Some((core_selector, cq_offset)), + } + } + + /// Returns the core index determined by `UMPSignal::SelectCore` commitment + /// and `assigned_cores`. + /// + /// Returns `None` if there is no `UMPSignal::SelectCore` commitment or + /// assigned cores is empty. + /// + /// `assigned_cores` must be a sorted vec of all core indices assigned to a parachain. + pub fn committed_core_index(&self, assigned_cores: &[&CoreIndex]) -> Option { + if assigned_cores.is_empty() { + return None + } + + self.selected_core().and_then(|(core_selector, _cq_offset)| { + let core_index = + **assigned_cores.get(core_selector.0 as usize % assigned_cores.len())?; + Some(core_index) + }) + } +} + +/// CandidateReceipt construction errors. +#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +pub enum CandidateReceiptError { + /// The specified core index is invalid. + InvalidCoreIndex, + /// The core index in commitments doesn't match the one in descriptor + CoreIndexMismatch, + /// The core selector or claim queue offset is invalid. + InvalidSelectedCore, + /// The parachain is not assigned to any core at specified claim queue offset. + NoAssignment, + /// No core was selected. The `SelectCore` commitment is mandatory for + /// v2 receipts if parachains has multiple cores assigned. + NoCoreSelected, + /// Unknown version. + UnknownVersion(InternalVersion), +} + +macro_rules! impl_getter { + ($field:ident, $type:ident) => { + /// Returns the value of $field field. + pub fn $field(&self) -> $type { + self.$field + } + }; +} + +impl CandidateDescriptorV2 { + impl_getter!(erasure_root, Hash); + impl_getter!(para_head, Hash); + impl_getter!(relay_parent, H); + impl_getter!(para_id, ParaId); + impl_getter!(persisted_validation_data_hash, Hash); + impl_getter!(pov_hash, Hash); + impl_getter!(validation_code_hash, ValidationCodeHash); + + /// Returns the candidate descriptor version. + /// The candidate is at version 2 if the reserved fields are zeroed out + /// and the internal `version` field is 0. + pub fn version(&self) -> CandidateDescriptorVersion { + if self.reserved2 != [0u8; 64] || self.reserved1 != [0u8; 25] { + return CandidateDescriptorVersion::V1 + } + + match self.version.0 { + 0 => CandidateDescriptorVersion::V2, + _ => CandidateDescriptorVersion::Unknown, + } + } + + fn rebuild_collator_field(&self) -> CollatorId { + let mut collator_id = Vec::with_capacity(32); + let core_index: [u8; 2] = self.core_index.to_ne_bytes(); + let session_index: [u8; 4] = self.session_index.to_ne_bytes(); + + collator_id.push(self.version.0); + collator_id.extend_from_slice(core_index.as_slice()); + collator_id.extend_from_slice(session_index.as_slice()); + collator_id.extend_from_slice(self.reserved1.as_slice()); + + CollatorId::from_slice(&collator_id.as_slice()) + .expect("Slice size is exactly 32 bytes; qed") + } + + /// Returns the collator id if this is a v1 `CandidateDescriptor` + pub fn collator(&self) -> Option { + if self.version() == CandidateDescriptorVersion::V1 { + Some(self.rebuild_collator_field()) + } else { + None + } + } + + fn rebuild_signature_field(&self) -> CollatorSignature { + CollatorSignature::from_slice(self.reserved2.as_slice()) + .expect("Slice size is exactly 64 bytes; qed") + } + + /// Returns the collator signature of `V1` candidate descriptors, `None` otherwise. + pub fn signature(&self) -> Option { + if self.version() == CandidateDescriptorVersion::V1 { + return Some(self.rebuild_signature_field()) + } + + None + } + + /// Returns the `core_index` of `V2` candidate descriptors, `None` otherwise. + pub fn core_index(&self) -> Option { + if self.version() == CandidateDescriptorVersion::V1 { + return None + } + + Some(CoreIndex(self.core_index as u32)) + } + + /// Returns the `core_index` of `V2` candidate descriptors, `None` otherwise. + pub fn session_index(&self) -> Option { + if self.version() == CandidateDescriptorVersion::V1 { + return None + } + + Some(self.session_index) + } +} + +impl CommittedCandidateReceiptV2 { + /// Checks if descriptor core index is equal to the committed core index. + /// Input `cores_per_para` is a claim queue snapshot stored as a mapping + /// between `ParaId` and the cores assigned per depth. + pub fn check_core_index( + &self, + cores_per_para: &TransposedClaimQueue, + ) -> Result<(), CandidateReceiptError> { + match self.descriptor.version() { + // Don't check v1 descriptors. + CandidateDescriptorVersion::V1 => return Ok(()), + CandidateDescriptorVersion::V2 => {}, + CandidateDescriptorVersion::Unknown => + return Err(CandidateReceiptError::UnknownVersion(self.descriptor.version)), + } + + if cores_per_para.is_empty() { + return Err(CandidateReceiptError::NoAssignment) + } + + let (offset, core_selected) = + if let Some((_core_selector, cq_offset)) = self.commitments.selected_core() { + (cq_offset.0, true) + } else { + // If no core has been selected then we use offset 0 (top of claim queue) + (0, false) + }; + + // The cores assigned to the parachain at above computed offset. + let assigned_cores = cores_per_para + .get(&self.descriptor.para_id()) + .ok_or(CandidateReceiptError::NoAssignment)? + .get(&offset) + .ok_or(CandidateReceiptError::NoAssignment)? + .into_iter() + .collect::>(); + + let core_index = if core_selected { + self.commitments + .committed_core_index(assigned_cores.as_slice()) + .ok_or(CandidateReceiptError::NoAssignment)? + } else { + // `SelectCore` commitment is mandatory for elastic scaling parachains. + if assigned_cores.len() > 1 { + return Err(CandidateReceiptError::NoCoreSelected) + } + + **assigned_cores.get(0).ok_or(CandidateReceiptError::NoAssignment)? + }; + + let descriptor_core_index = CoreIndex(self.descriptor.core_index as u32); + if core_index != descriptor_core_index { + return Err(CandidateReceiptError::CoreIndexMismatch) + } + + Ok(()) + } +} + +/// A backed (or backable, depending on context) candidate. +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] +pub struct BackedCandidate { + /// The candidate referred to. + candidate: CommittedCandidateReceiptV2, + /// The validity votes themselves, expressed as signatures. + validity_votes: Vec, + /// The indices of the validators within the group, expressed as a bitfield. May be extended + /// beyond the backing group size to contain the assigned core index, if ElasticScalingMVP is + /// enabled. + validator_indices: BitVec, +} + +/// Parachains inherent-data passed into the runtime by a block author +#[derive(Encode, Decode, Clone, PartialEq, RuntimeDebug, TypeInfo)] +pub struct InherentData { + /// Signed bitfields by validators about availability. + pub bitfields: UncheckedSignedAvailabilityBitfields, + /// Backed candidates for inclusion in the block. + pub backed_candidates: Vec>, + /// Sets of dispute votes for inclusion, + pub disputes: MultiDisputeStatementSet, + /// The parent block header. Used for checking state proofs. + pub parent_header: HDR, +} + +impl BackedCandidate { + /// Constructor + pub fn new( + candidate: CommittedCandidateReceiptV2, + validity_votes: Vec, + validator_indices: BitVec, + core_index: Option, + ) -> Self { + let mut instance = Self { candidate, validity_votes, validator_indices }; + if let Some(core_index) = core_index { + instance.inject_core_index(core_index); + } + instance + } + + /// Get a reference to the committed candidate receipt of the candidate. + pub fn candidate(&self) -> &CommittedCandidateReceiptV2 { + &self.candidate + } + + /// Get a mutable reference to the committed candidate receipt of the candidate. + /// Only for testing. + #[cfg(feature = "test")] + pub fn candidate_mut(&mut self) -> &mut CommittedCandidateReceiptV2 { + &mut self.candidate + } + /// Get a reference to the descriptor of the candidate. + pub fn descriptor(&self) -> &CandidateDescriptorV2 { + &self.candidate.descriptor + } + + /// Get a mutable reference to the descriptor of the candidate. Only for testing. + #[cfg(feature = "test")] + pub fn descriptor_mut(&mut self) -> &mut CandidateDescriptorV2 { + &mut self.candidate.descriptor + } + + /// Get a reference to the validity votes of the candidate. + pub fn validity_votes(&self) -> &[ValidityAttestation] { + &self.validity_votes + } + + /// Get a mutable reference to validity votes of the para. + pub fn validity_votes_mut(&mut self) -> &mut Vec { + &mut self.validity_votes + } + + /// Compute this candidate's hash. + pub fn hash(&self) -> CandidateHash + where + H: Clone + Encode, + { + self.candidate.to_plain().hash() + } + + /// Get this candidate's receipt. + pub fn receipt(&self) -> CandidateReceiptV2 + where + H: Clone, + { + self.candidate.to_plain() + } + + /// Get a copy of the validator indices and the assumed core index, if any. + pub fn validator_indices_and_core_index( + &self, + core_index_enabled: bool, + ) -> (&BitSlice, Option) { + // This flag tells us if the block producers must enable Elastic Scaling MVP hack. + // It extends `BackedCandidate::validity_indices` to store a 8 bit core index. + if core_index_enabled { + let core_idx_offset = self.validator_indices.len().saturating_sub(8); + if core_idx_offset > 0 { + let (validator_indices_slice, core_idx_slice) = + self.validator_indices.split_at(core_idx_offset); + return ( + validator_indices_slice, + Some(CoreIndex(core_idx_slice.load::() as u32)), + ); + } + } + + (&self.validator_indices, None) + } + + /// Inject a core index in the validator_indices bitvec. + fn inject_core_index(&mut self, core_index: CoreIndex) { + let core_index_to_inject: BitVec = + BitVec::from_vec(vec![core_index.0 as u8]); + self.validator_indices.extend(core_index_to_inject); + } + + /// Update the validator indices and core index in the candidate. + pub fn set_validator_indices_and_core_index( + &mut self, + new_indices: BitVec, + maybe_core_index: Option, + ) { + self.validator_indices = new_indices; + + if let Some(core_index) = maybe_core_index { + self.inject_core_index(core_index); + } + } +} + +/// Scraped runtime backing votes and resolved disputes. +#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)] +#[cfg_attr(feature = "std", derive(PartialEq))] +pub struct ScrapedOnChainVotes { + /// The session in which the block was included. + pub session: SessionIndex, + /// Set of backing validators for each candidate, represented by its candidate + /// receipt. + pub backing_validators_per_candidate: + Vec<(CandidateReceiptV2, Vec<(ValidatorIndex, ValidityAttestation)>)>, + /// On-chain-recorded set of disputes. + /// Note that the above `backing_validators` are + /// unrelated to the backers of the disputes candidates. + pub disputes: MultiDisputeStatementSet, +} + +impl From> for super::v8::ScrapedOnChainVotes { + fn from(value: ScrapedOnChainVotes) -> Self { + Self { + session: value.session, + backing_validators_per_candidate: value + .backing_validators_per_candidate + .into_iter() + .map(|(receipt, validators)| (receipt.into(), validators)) + .collect::>(), + disputes: value.disputes, + } + } +} + +/// Information about a core which is currently occupied. +#[derive(Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(PartialEq))] +pub struct OccupiedCore { + // NOTE: this has no ParaId as it can be deduced from the candidate descriptor. + /// If this core is freed by availability, this is the assignment that is next up on this + /// core, if any. None if there is nothing queued for this core. + pub next_up_on_available: Option, + /// The relay-chain block number this began occupying the core at. + pub occupied_since: N, + /// The relay-chain block this will time-out at, if any. + pub time_out_at: N, + /// If this core is freed by being timed-out, this is the assignment that is next up on this + /// core. None if there is nothing queued for this core or there is no possibility of timing + /// out. + pub next_up_on_time_out: Option, + /// A bitfield with 1 bit for each validator in the set. `1` bits mean that the corresponding + /// validators has attested to availability on-chain. A 2/3+ majority of `1` bits means that + /// this will be available. + pub availability: BitVec, + /// The group assigned to distribute availability pieces of this candidate. + pub group_responsible: GroupIndex, + /// The hash of the candidate occupying the core. + pub candidate_hash: CandidateHash, + /// The descriptor of the candidate occupying the core. + pub candidate_descriptor: CandidateDescriptorV2, +} + +/// The state of a particular availability core. +#[derive(Clone, Encode, Decode, TypeInfo, RuntimeDebug)] +#[cfg_attr(feature = "std", derive(PartialEq))] +pub enum CoreState { + /// The core is currently occupied. + #[codec(index = 0)] + Occupied(OccupiedCore), + /// The core is currently free, with a para scheduled and given the opportunity + /// to occupy. + /// + /// If a particular Collator is required to author this block, that is also present in this + /// variant. + #[codec(index = 1)] + Scheduled(ScheduledCore), + /// The core is currently free and there is nothing scheduled. This can be the case for + /// parathread cores when there are no parathread blocks queued. Parachain cores will never be + /// left idle. + #[codec(index = 2)] + Free, +} + +impl From> for super::v8::OccupiedCore { + fn from(value: OccupiedCore) -> Self { + Self { + next_up_on_available: value.next_up_on_available, + occupied_since: value.occupied_since, + time_out_at: value.time_out_at, + next_up_on_time_out: value.next_up_on_time_out, + availability: value.availability, + group_responsible: value.group_responsible, + candidate_hash: value.candidate_hash, + candidate_descriptor: value.candidate_descriptor.into(), + } + } +} + +impl From> for super::v8::CoreState { + fn from(value: CoreState) -> Self { + match value { + CoreState::Free => super::v8::CoreState::Free, + CoreState::Scheduled(core) => super::v8::CoreState::Scheduled(core), + CoreState::Occupied(occupied_core) => + super::v8::CoreState::Occupied(occupied_core.into()), + } + } +} + +/// The claim queue mapped by parachain id. +pub type TransposedClaimQueue = BTreeMap>>; + +/// Returns a mapping between the para id and the core indices assigned at different +/// depths in the claim queue. +pub fn transpose_claim_queue( + claim_queue: BTreeMap>, +) -> TransposedClaimQueue { + let mut per_para_claim_queue = BTreeMap::new(); + + for (core, paras) in claim_queue { + // Iterate paras assigned to this core at each depth. + for (depth, para) in paras.into_iter().enumerate() { + let depths: &mut BTreeMap> = + per_para_claim_queue.entry(para).or_insert_with(|| Default::default()); + + depths.entry(depth as u8).or_default().insert(core); + } + } + + per_para_claim_queue +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + v8::{ + tests::dummy_committed_candidate_receipt as dummy_old_committed_candidate_receipt, + CommittedCandidateReceipt, Hash, HeadData, ValidationCode, + }, + vstaging::{CandidateDescriptorV2, CommittedCandidateReceiptV2}, + }; + + fn dummy_collator_signature() -> CollatorSignature { + CollatorSignature::from_slice(&mut (0..64).into_iter().collect::>().as_slice()) + .expect("64 bytes; qed") + } + + fn dummy_collator_id() -> CollatorId { + CollatorId::from_slice(&mut (0..32).into_iter().collect::>().as_slice()) + .expect("32 bytes; qed") + } + + pub fn dummy_committed_candidate_receipt_v2() -> CommittedCandidateReceiptV2 { + let zeros = Hash::zero(); + let reserved2 = [0; 64]; + + CommittedCandidateReceiptV2 { + descriptor: CandidateDescriptorV2 { + para_id: 0.into(), + relay_parent: zeros, + version: InternalVersion(0), + core_index: 123, + session_index: 1, + reserved1: Default::default(), + persisted_validation_data_hash: zeros, + pov_hash: zeros, + erasure_root: zeros, + reserved2, + para_head: zeros, + validation_code_hash: ValidationCode(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]).hash(), + }, + commitments: CandidateCommitments { + head_data: HeadData(vec![]), + upward_messages: vec![].try_into().expect("empty vec fits within bounds"), + new_validation_code: None, + horizontal_messages: vec![].try_into().expect("empty vec fits within bounds"), + processed_downward_messages: 0, + hrmp_watermark: 0_u32, + }, + } + } + + #[test] + fn is_binary_compatibile() { + let old_ccr = dummy_old_committed_candidate_receipt(); + let new_ccr = dummy_committed_candidate_receipt_v2(); + + assert_eq!(old_ccr.encoded_size(), new_ccr.encoded_size()); + + let encoded_old = old_ccr.encode(); + + // Deserialize from old candidate receipt. + let new_ccr: CommittedCandidateReceiptV2 = + Decode::decode(&mut encoded_old.as_slice()).unwrap(); + + // We get same candidate hash. + assert_eq!(old_ccr.hash(), new_ccr.hash()); + } + + #[test] + fn invalid_version_descriptor() { + let mut new_ccr = dummy_committed_candidate_receipt_v2(); + assert_eq!(new_ccr.descriptor.version(), CandidateDescriptorVersion::V2); + // Put some unknown version. + new_ccr.descriptor.version = InternalVersion(100); + + // Deserialize as V1. + let new_ccr: CommittedCandidateReceiptV2 = + Decode::decode(&mut new_ccr.encode().as_slice()).unwrap(); + + assert_eq!(new_ccr.descriptor.version(), CandidateDescriptorVersion::Unknown); + assert_eq!( + new_ccr.check_core_index(&BTreeMap::new()), + Err(CandidateReceiptError::UnknownVersion(InternalVersion(100))) + ) + } + + #[test] + fn test_ump_commitment() { + let mut new_ccr = dummy_committed_candidate_receipt_v2(); + new_ccr.descriptor.core_index = 123; + new_ccr.descriptor.para_id = ParaId::new(1000); + + // dummy XCM messages + new_ccr.commitments.upward_messages.force_push(vec![0u8; 256]); + new_ccr.commitments.upward_messages.force_push(vec![0xff; 256]); + + // separator + new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR); + + // CoreIndex commitment + new_ccr + .commitments + .upward_messages + .force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode()); + + let mut cq = BTreeMap::new(); + cq.insert( + CoreIndex(123), + vec![new_ccr.descriptor.para_id(), new_ccr.descriptor.para_id()].into(), + ); + + assert_eq!(new_ccr.check_core_index(&transpose_claim_queue(cq)), Ok(())); + } + + #[test] + fn test_invalid_ump_commitment() { + let mut new_ccr = dummy_committed_candidate_receipt_v2(); + new_ccr.descriptor.core_index = 0; + new_ccr.descriptor.para_id = ParaId::new(1000); + + new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR); + new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR); + + let mut cq = BTreeMap::new(); + cq.insert(CoreIndex(0), vec![new_ccr.descriptor.para_id()].into()); + + // The check should not fail because no `SelectCore` signal was sent. + // The message is optional. + assert!(new_ccr.check_core_index(&transpose_claim_queue(cq)).is_ok()); + + // Garbage message. + new_ccr.commitments.upward_messages.force_push(vec![0, 13, 200].encode()); + + // No `SelectCore` can be decoded. + assert_eq!(new_ccr.commitments.selected_core(), None); + + let mut cq = BTreeMap::new(); + cq.insert( + CoreIndex(0), + vec![new_ccr.descriptor.para_id(), new_ccr.descriptor.para_id()].into(), + ); + cq.insert( + CoreIndex(100), + vec![new_ccr.descriptor.para_id(), new_ccr.descriptor.para_id()].into(), + ); + + assert_eq!( + new_ccr.check_core_index(&transpose_claim_queue(cq.clone())), + Err(CandidateReceiptError::NoCoreSelected) + ); + + new_ccr.commitments.upward_messages.clear(); + new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR); + + new_ccr + .commitments + .upward_messages + .force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode()); + + // Duplicate + new_ccr + .commitments + .upward_messages + .force_push(UMPSignal::SelectCore(CoreSelector(1), ClaimQueueOffset(1)).encode()); + + // Duplicate doesn't override first signal. + assert_eq!(new_ccr.check_core_index(&transpose_claim_queue(cq)), Ok(())); + } + + #[test] + fn test_version2_receipts_decoded_as_v1() { + let mut new_ccr = dummy_committed_candidate_receipt_v2(); + new_ccr.descriptor.core_index = 123; + new_ccr.descriptor.para_id = ParaId::new(1000); + + // dummy XCM messages + new_ccr.commitments.upward_messages.force_push(vec![0u8; 256]); + new_ccr.commitments.upward_messages.force_push(vec![0xff; 256]); + + // separator + new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR); + + // CoreIndex commitment + new_ccr + .commitments + .upward_messages + .force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode()); + + let encoded_ccr = new_ccr.encode(); + let decoded_ccr: CommittedCandidateReceipt = + Decode::decode(&mut encoded_ccr.as_slice()).unwrap(); + + assert_eq!(decoded_ccr.descriptor.relay_parent, new_ccr.descriptor.relay_parent()); + assert_eq!(decoded_ccr.descriptor.para_id, new_ccr.descriptor.para_id()); + + assert_eq!(new_ccr.hash(), decoded_ccr.hash()); + + // Encode v1 and decode as V2 + let encoded_ccr = new_ccr.encode(); + let v2_ccr: CommittedCandidateReceiptV2 = + Decode::decode(&mut encoded_ccr.as_slice()).unwrap(); + + assert_eq!(v2_ccr.descriptor.core_index(), Some(CoreIndex(123))); + + let mut cq = BTreeMap::new(); + cq.insert( + CoreIndex(123), + vec![new_ccr.descriptor.para_id(), new_ccr.descriptor.para_id()].into(), + ); + + assert_eq!(new_ccr.check_core_index(&transpose_claim_queue(cq)), Ok(())); + + assert_eq!(new_ccr.hash(), v2_ccr.hash()); + } + + // Only check descriptor `core_index` field of v2 descriptors. If it is v1, that field + // will be garbage. + #[test] + fn test_v1_descriptors_with_ump_signal() { + let mut ccr = dummy_old_committed_candidate_receipt(); + ccr.descriptor.para_id = ParaId::new(1024); + // Adding collator signature should make it decode as v1. + ccr.descriptor.signature = dummy_collator_signature(); + ccr.descriptor.collator = dummy_collator_id(); + + ccr.commitments.upward_messages.force_push(UMP_SEPARATOR); + ccr.commitments + .upward_messages + .force_push(UMPSignal::SelectCore(CoreSelector(1), ClaimQueueOffset(1)).encode()); + + let encoded_ccr: Vec = ccr.encode(); + + let v1_ccr: CommittedCandidateReceiptV2 = + Decode::decode(&mut encoded_ccr.as_slice()).unwrap(); + + assert_eq!(v1_ccr.descriptor.version(), CandidateDescriptorVersion::V1); + assert!(v1_ccr.commitments.selected_core().is_some()); + + let mut cq = BTreeMap::new(); + cq.insert(CoreIndex(0), vec![v1_ccr.descriptor.para_id()].into()); + cq.insert(CoreIndex(1), vec![v1_ccr.descriptor.para_id()].into()); + + assert!(v1_ccr.check_core_index(&transpose_claim_queue(cq)).is_ok()); + + assert_eq!( + v1_ccr.commitments.committed_core_index(&vec![&CoreIndex(10), &CoreIndex(5)]), + Some(CoreIndex(5)), + ); + + assert_eq!(v1_ccr.descriptor.core_index(), None); + } + + #[test] + fn test_core_select_is_optional() { + // Testing edge case when collators provide zeroed signature and collator id. + let mut old_ccr = dummy_old_committed_candidate_receipt(); + old_ccr.descriptor.para_id = ParaId::new(1000); + let encoded_ccr: Vec = old_ccr.encode(); + + let new_ccr: CommittedCandidateReceiptV2 = + Decode::decode(&mut encoded_ccr.as_slice()).unwrap(); + + let mut cq = BTreeMap::new(); + cq.insert(CoreIndex(0), vec![new_ccr.descriptor.para_id()].into()); + + // Since collator sig and id are zeroed, it means that the descriptor uses format + // version 2. Should still pass checks without core selector. + assert!(new_ccr.check_core_index(&transpose_claim_queue(cq)).is_ok()); + + let mut cq = BTreeMap::new(); + cq.insert(CoreIndex(0), vec![new_ccr.descriptor.para_id()].into()); + cq.insert(CoreIndex(1), vec![new_ccr.descriptor.para_id()].into()); + + // Should fail because 2 cores are assigned, + assert_eq!( + new_ccr.check_core_index(&transpose_claim_queue(cq)), + Err(CandidateReceiptError::NoCoreSelected) + ); + + // Adding collator signature should make it decode as v1. + old_ccr.descriptor.signature = dummy_collator_signature(); + old_ccr.descriptor.collator = dummy_collator_id(); + + let old_ccr_hash = old_ccr.hash(); + + let encoded_ccr: Vec = old_ccr.encode(); + + let new_ccr: CommittedCandidateReceiptV2 = + Decode::decode(&mut encoded_ccr.as_slice()).unwrap(); + + assert_eq!(new_ccr.descriptor.signature(), Some(old_ccr.descriptor.signature)); + assert_eq!(new_ccr.descriptor.collator(), Some(old_ccr.descriptor.collator)); + + assert_eq!(new_ccr.descriptor.core_index(), None); + assert_eq!(new_ccr.descriptor.para_id(), ParaId::new(1000)); + + assert_eq!(old_ccr_hash, new_ccr.hash()); + } +} diff --git a/polkadot/primitives/test-helpers/src/lib.rs b/polkadot/primitives/test-helpers/src/lib.rs index d43cf3317e573adffb21b8fdc077eeebf993d40d..b0f78717dd97538758e735eb61f21821c17ed7e6 100644 --- a/polkadot/primitives/test-helpers/src/lib.rs +++ b/polkadot/primitives/test-helpers/src/lib.rs @@ -23,9 +23,10 @@ //! Note that `dummy_` prefixed values are meant to be fillers, that should not matter, and will //! contain randomness based data. use polkadot_primitives::{ + vstaging::{CandidateDescriptorV2, CandidateReceiptV2, CommittedCandidateReceiptV2}, CandidateCommitments, CandidateDescriptor, CandidateReceipt, CollatorId, CollatorSignature, - CommittedCandidateReceipt, Hash, HeadData, Id as ParaId, PersistedValidationData, - ValidationCode, ValidationCodeHash, ValidatorId, + CommittedCandidateReceipt, CoreIndex, Hash, HeadData, Id as ParaId, PersistedValidationData, + SessionIndex, ValidationCode, ValidationCodeHash, ValidatorId, }; pub use rand; use sp_application_crypto::sr25519; @@ -42,6 +43,14 @@ pub fn dummy_candidate_receipt>(relay_parent: H) -> CandidateRece } } +/// Creates a v2 candidate receipt with filler data. +pub fn dummy_candidate_receipt_v2>(relay_parent: H) -> CandidateReceiptV2 { + CandidateReceiptV2:: { + commitments_hash: dummy_candidate_commitments(dummy_head_data()).hash(), + descriptor: dummy_candidate_descriptor_v2(relay_parent), + } +} + /// Creates a committed candidate receipt with filler data. pub fn dummy_committed_candidate_receipt>( relay_parent: H, @@ -52,6 +61,16 @@ pub fn dummy_committed_candidate_receipt>( } } +/// Creates a v2 committed candidate receipt with filler data. +pub fn dummy_committed_candidate_receipt_v2>( + relay_parent: H, +) -> CommittedCandidateReceiptV2 { + CommittedCandidateReceiptV2 { + descriptor: dummy_candidate_descriptor_v2::(relay_parent), + commitments: dummy_candidate_commitments(dummy_head_data()), + } +} + /// Create a candidate receipt with a bogus signature and filler data. Optionally set the commitment /// hash with the `commitments` arg. pub fn dummy_candidate_receipt_bad_sig( @@ -124,6 +143,23 @@ pub fn dummy_candidate_descriptor>(relay_parent: H) -> CandidateD descriptor } +/// Create a v2 candidate descriptor with filler data. +pub fn dummy_candidate_descriptor_v2>(relay_parent: H) -> CandidateDescriptorV2 { + let invalid = Hash::zero(); + let descriptor = make_valid_candidate_descriptor_v2( + 1.into(), + relay_parent, + CoreIndex(1), + 1, + invalid, + invalid, + invalid, + invalid, + invalid, + ); + descriptor +} + /// Create meaningless validation code. pub fn dummy_validation_code() -> ValidationCode { ValidationCode(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]) @@ -134,16 +170,16 @@ pub fn dummy_head_data() -> HeadData { HeadData(vec![]) } -/// Create a meaningless collator id. -pub fn dummy_collator() -> CollatorId { - CollatorId::from(sr25519::Public::default()) -} - /// Create a meaningless validator id. pub fn dummy_validator() -> ValidatorId { ValidatorId::from(sr25519::Public::default()) } +/// Create a meaningless collator id. +pub fn dummy_collator() -> CollatorId { + CollatorId::from(sr25519::Public::default()) +} + /// Create a meaningless collator signature. pub fn dummy_collator_signature() -> CollatorSignature { CollatorSignature::from(sr25519::Signature::default()) @@ -232,6 +268,34 @@ pub fn make_valid_candidate_descriptor>( descriptor } +/// Create a v2 candidate descriptor. +pub fn make_valid_candidate_descriptor_v2>( + para_id: ParaId, + relay_parent: H, + core_index: CoreIndex, + session_index: SessionIndex, + persisted_validation_data_hash: Hash, + pov_hash: Hash, + validation_code_hash: impl Into, + para_head: Hash, + erasure_root: Hash, +) -> CandidateDescriptorV2 { + let validation_code_hash = validation_code_hash.into(); + + let descriptor = CandidateDescriptorV2::new( + para_id, + relay_parent, + core_index, + session_index, + persisted_validation_data_hash, + pov_hash, + erasure_root, + para_head, + validation_code_hash, + ); + + descriptor +} /// After manually modifying the candidate descriptor, resign with a defined collator key. pub fn resign_candidate_descriptor_with_collator>( descriptor: &mut CandidateDescriptor, diff --git a/polkadot/roadmap/implementers-guide/src/node/approval/approval-distribution.md b/polkadot/roadmap/implementers-guide/src/node/approval/approval-distribution.md index c987b7fe5beaa366c23c2fc58a179a916bbb8a21..e09418c7d5ab091078b642234c7d72a8b13255a8 100644 --- a/polkadot/roadmap/implementers-guide/src/node/approval/approval-distribution.md +++ b/polkadot/roadmap/implementers-guide/src/node/approval/approval-distribution.md @@ -66,8 +66,8 @@ Input: * `OverseerSignal::BlockFinalized` Output: - * `ApprovalVotingMessage::CheckAndImportAssignment` - * `ApprovalVotingMessage::CheckAndImportApproval` + * `ApprovalVotingMessage::ImportAssignment` + * `ApprovalVotingMessage::ImportApproval` * `NetworkBridgeMessage::SendValidationMessage::ApprovalDistribution` ## Functionality @@ -253,8 +253,30 @@ The algorithm is the following: boost, add the fingerprint to the peer's knowledge only if it knows about the block and return. Note that we must do this after checking for out-of-view and if the peers knows about the block to avoid being spammed. If we did this check earlier, a peer could provide data out-of-view repeatedly and be rewarded for it. - * Dispatch `ApprovalVotingMessage::CheckAndImportAssignment(assignment)` and wait for the response. + * Check the assignment certificate is valid. + * If the cert kind is `RelayVRFModulo`, then the certificate is valid as long as `sample < + session_info.relay_vrf_samples` and the VRF is valid for the validator's key with the input + `block_entry.relay_vrf_story ++ sample.encode()` as described with + [the approvals protocol section](../../protocol-approval.md#assignment-criteria). We set + `core_index = vrf.make_bytes().to_u32() % session_info.n_cores`. If the `BlockEntry` causes + inclusion of a candidate at `core_index`, then this is a valid assignment for the candidate + at `core_index` and has delay tranche 0. Otherwise, it can be ignored. + * If the cert kind is `RelayVRFModuloCompact`, then the certificate is valid as long as the VRF + is valid for the validator's key with the input `block_entry.relay_vrf_story ++ relay_vrf_samples.encode()` + as described with [the approvals protocol section](../../protocol-approval.md#assignment-criteria). + We enforce that all `core_bitfield` indices are included in the set of the core indices sampled from the + VRF Output. The assignment is considered a valid tranche0 assignment for all claimed candidates if all + `core_bitfield` indices match the core indices where the claimed candidates were included at. + * If the cert kind is `RelayVRFDelay`, then we check if the VRF is valid for the validator's key with the + input `block_entry.relay_vrf_story ++ cert.core_index.encode()` as described in [the approvals protocol + section](../../protocol-approval.md#assignment-criteria). The cert can be ignored if the block did not + cause inclusion of a candidate on that core index. Otherwise, this is a valid assignment for the included + candidate. The delay tranche for the assignment is determined by reducing + `(vrf.make_bytes().to_u64() % (session_info.n_delay_tranches + session_info.zeroth_delay_tranche_width)).saturating_sub(session_info.zeroth_delay_tranche_width)`. + * We also check that the core index derived by the output is covered by the `VRFProof` by means of an auxiliary signature. + * If the delay tranche is too far in the future, return `AssignmentCheckResult::TooFarInFuture`. * If the result is `AssignmentCheckResult::Accepted` + * Dispatch `ApprovalVotingMessage::ImportAssignment(assignment)` to approval-voting to import the assignment. * If the vote was accepted but not duplicate, give the peer a positive reputation boost * add the fingerprint to both our and the peer's knowledge in the `BlockEntry`. Note that we only doing this after making sure we have the right fingerprint. @@ -293,10 +315,12 @@ Imports an approval signature referenced by block hash and candidate index: boost, add the fingerprint to the peer's knowledge only if it knows about the block and return. Note that we must do this after checking for out-of-view to avoid being spammed. If we did this check earlier, a peer could provide data out-of-view repeatedly and be rewarded for it. - * Dispatch `ApprovalVotingMessage::CheckAndImportApproval(approval)` and wait for the response. - * If the result is `VoteCheckResult::Accepted(())`: + * Construct a `SignedApprovalVote` using the candidates hashes and check against the validator's approval key, + based on the session info of the block. If invalid or no such validator, return `Err(InvalidVoteError)`. + * If the result of checking the signature is `Ok(CheckedIndirectSignedApprovalVote)`: + * Dispatch `ApprovalVotingMessage::ImportApproval(approval)` . * Give the peer a positive reputation boost and add the fingerprint to both our and the peer's knowledge. - * If the result is `VoteCheckResult::Bad`: + * If the result is `Err(InvalidVoteError)`: * Report the peer and return. * Load the candidate entry for the given candidate index. It should exist unless there is a logic error in the approval voting subsystem. diff --git a/polkadot/roadmap/implementers-guide/src/node/approval/approval-voting-parallel.md b/polkadot/roadmap/implementers-guide/src/node/approval/approval-voting-parallel.md new file mode 100644 index 0000000000000000000000000000000000000000..84661b7bf9b35fe698d37beb809836117ed4696f --- /dev/null +++ b/polkadot/roadmap/implementers-guide/src/node/approval/approval-voting-parallel.md @@ -0,0 +1,30 @@ +# Approval voting parallel + +The approval-voting-parallel subsystem acts as an orchestrator for the tasks handled by the [Approval Voting](approval-voting.md) +and [Approval Distribution](approval-distribution.md) subsystems. Initially, these two systems operated separately and interacted +with each other and other subsystems through orchestra. + +With approval-voting-parallel, we have a single subsystem that creates two types of workers: +- Four approval-distribution workers that operate in parallel, each handling tasks based on the validator_index of the message + originator. +- One approval-voting worker that performs the tasks previously managed by the standalone approval-voting subsystem. + +This subsystem does not maintain any state. Instead, it functions as an orchestrator that: +- Spawns and initializes each workers. +- Forwards each message and signal to the appropriate worker. +- Aggregates results for messages that require input from more than one worker, such as GetApprovalSignatures. + +## Forwarding logic + +The messages received and forwarded by approval-voting-parallel split in three categories: +- Signals which need to be forwarded to all workers. +- Messages that only the `approval-voting` worker needs to handle, `ApprovalVotingParallelMessage::ApprovedAncestor` + and `ApprovalVotingParallelMessage::GetApprovalSignaturesForCandidate` +- Control messages that all `approval-distribution` workers need to receive `ApprovalVotingParallelMessage::NewBlocks`, + `ApprovalVotingParallelMessage::ApprovalCheckingLagUpdate` and all network bridge variants `ApprovalVotingParallelMessage::NetworkBridgeUpdate` + except `ApprovalVotingParallelMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerMessage)` +- Data messages `ApprovalVotingParallelMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerMessage)` which need to be sent + just to a single `approval-distribution` worker based on the ValidatorIndex. The logic for assigning the work is: + ``` + assigned_worker_index = validator_index % number_of_workers; + ``` diff --git a/polkadot/roadmap/implementers-guide/src/node/approval/approval-voting.md b/polkadot/roadmap/implementers-guide/src/node/approval/approval-voting.md index 9b4082c49e2f003f0b7d0fd317bc2e8ce06580e4..40394412d81b0d47d1cc96b7a399662b63611176 100644 --- a/polkadot/roadmap/implementers-guide/src/node/approval/approval-voting.md +++ b/polkadot/roadmap/implementers-guide/src/node/approval/approval-voting.md @@ -39,8 +39,8 @@ been approved. ## Protocol Input: - * `ApprovalVotingMessage::CheckAndImportAssignment` - * `ApprovalVotingMessage::CheckAndImportApproval` + * `ApprovalVotingMessage::ImportAssignment` + * `ApprovalVotingMessage::ImportApproval` * `ApprovalVotingMessage::ApprovedAncestor` Output: @@ -266,39 +266,17 @@ On receiving an `OverseerSignal::ActiveLeavesUpdate(update)`: 0-tranche assignment, kick off approval work, and schedule the next delay. * Dispatch an `ApprovalDistributionMessage::NewBlocks` with the meta information filled out for each new block. -#### `ApprovalVotingMessage::CheckAndImportAssignment` +#### `ApprovalVotingMessage::ImportAssignment` -On receiving a `ApprovalVotingMessage::CheckAndImportAssignment` message, we check the assignment cert against the block -entry. The cert itself contains information necessary to determine the candidate that is being assigned-to. In detail: +On receiving a `ApprovalVotingMessage::ImportAssignment` message, we assume the assignment cert itself has already been +checked to be valid we proceed then to import the assignment inside the block entry. The cert itself contains +information necessary to determine the candidate that is being assigned-to. In detail: * Load the `BlockEntry` for the relay-parent referenced by the message. If there is none, return `AssignmentCheckResult::Bad`. * Fetch the `SessionInfo` for the session of the block * Determine the assignment key of the validator based on that. * Determine the claimed core index by looking up the candidate with given index in `block_entry.candidates`. Return `AssignmentCheckResult::Bad` if missing. - * Check the assignment cert - * If the cert kind is `RelayVRFModulo`, then the certificate is valid as long as `sample < - session_info.relay_vrf_samples` and the VRF is valid for the validator's key with the input - `block_entry.relay_vrf_story ++ sample.encode()` as described with - [the approvals protocol section](../../protocol-approval.md#assignment-criteria). We set - `core_index = vrf.make_bytes().to_u32() % session_info.n_cores`. If the `BlockEntry` causes - inclusion of a candidate at `core_index`, then this is a valid assignment for the candidate - at `core_index` and has delay tranche 0. Otherwise, it can be ignored. - * If the cert kind is `RelayVRFModuloCompact`, then the certificate is valid as long as the VRF - is valid for the validator's key with the input `block_entry.relay_vrf_story ++ relay_vrf_samples.encode()` - as described with [the approvals protocol section](../../protocol-approval.md#assignment-criteria). - We enforce that all `core_bitfield` indices are included in the set of the core indices sampled from the - VRF Output. The assignment is considered a valid tranche0 assignment for all claimed candidates if all - `core_bitfield` indices match the core indices where the claimed candidates were included at. - - * If the cert kind is `RelayVRFDelay`, then we check if the VRF is valid for the validator's key with the - input `block_entry.relay_vrf_story ++ cert.core_index.encode()` as described in [the approvals protocol - section](../../protocol-approval.md#assignment-criteria). The cert can be ignored if the block did not - cause inclusion of a candidate on that core index. Otherwise, this is a valid assignment for the included - candidate. The delay tranche for the assignment is determined by reducing - `(vrf.make_bytes().to_u64() % (session_info.n_delay_tranches + session_info.zeroth_delay_tranche_width)).saturating_sub(session_info.zeroth_delay_tranche_width)`. - * We also check that the core index derived by the output is covered by the `VRFProof` by means of an auxiliary signature. - * If the delay tranche is too far in the future, return `AssignmentCheckResult::TooFarInFuture`. * Import the assignment. * Load the candidate in question and access the `approval_entry` for the block hash the cert references. * Ignore if we already observe the validator as having been assigned. @@ -309,14 +287,12 @@ entry. The cert itself contains information necessary to determine the candidate * [Schedule a wakeup](#schedule-wakeup) for this block, candidate pair. * return the appropriate `AssignmentCheckResult` on the response channel. -#### `ApprovalVotingMessage::CheckAndImportApproval` +#### `ApprovalVotingMessage::ImportApproval` -On receiving a `CheckAndImportApproval(indirect_approval_vote, response_channel)` message: +On receiving a `ImportApproval(indirect_approval_vote, response_channel)` message: * Fetch the `BlockEntry` from the indirect approval vote's `block_hash`. If none, return `ApprovalCheckResult::Bad`. * Fetch all `CandidateEntry` from the indirect approval vote's `candidate_indices`. If the block did not trigger inclusion of enough candidates, return `ApprovalCheckResult::Bad`. - * Construct a `SignedApprovalVote` using the candidates hashes and check against the validator's approval key, - based on the session info of the block. If invalid or no such validator, return `ApprovalCheckResult::Bad`. * Send `ApprovalCheckResult::Accepted` * [Import the checked approval vote](#import-checked-approval) for all candidates diff --git a/polkadot/roadmap/implementers-guide/src/node/utility/candidate-validation.md b/polkadot/roadmap/implementers-guide/src/node/utility/candidate-validation.md index 1a3ff1c6aff061ad845dcaaba4258129eb1673bb..aad77de0aded2cf87827c463c7f981a52bc990d2 100644 --- a/polkadot/roadmap/implementers-guide/src/node/utility/candidate-validation.md +++ b/polkadot/roadmap/implementers-guide/src/node/utility/candidate-validation.md @@ -85,7 +85,7 @@ state. Once we have all parameters, we can spin up a background task to perform the validation in a way that doesn't hold up the entire event loop. Before invoking the validation function itself, this should first do some basic checks: - * The collator signature is valid + * The collator signature is valid (only if `CandidateDescriptor` has version 1) * The PoV provided matches the `pov_hash` field of the descriptor For more details please see [PVF Host and Workers](pvf-host-and-workers.md). diff --git a/polkadot/roadmap/implementers-guide/src/runtime/inclusion.md b/polkadot/roadmap/implementers-guide/src/runtime/inclusion.md index 5031433cf5a1d7a5517458d5f0a5b31a024c4d49..48909db07ba567234efbf813816147ac575dec4a 100644 --- a/polkadot/roadmap/implementers-guide/src/runtime/inclusion.md +++ b/polkadot/roadmap/implementers-guide/src/runtime/inclusion.md @@ -109,7 +109,7 @@ All failed checks should lead to an unrecoverable error making the block invalid 1. Ensure that any code upgrade scheduled by the candidate does not happen within `config.validation_upgrade_cooldown` of `Paras::last_code_upgrade(para_id, true)`, if any, comparing against the value of `Paras::FutureCodeUpgrades` for the given para ID. - 1. Check the collator's signature on the candidate data. + 1. Check the collator's signature on the candidate data (only if `CandidateDescriptor` is version 1) 1. check the backing of the candidate using the signatures and the bitfields, comparing against the validators assigned to the groups, fetched with the `group_validators` lookup, while group indices are computed by `Scheduler` according to group rotation info. diff --git a/polkadot/roadmap/implementers-guide/src/types/overseer-protocol.md b/polkadot/roadmap/implementers-guide/src/types/overseer-protocol.md index c82d89d2d8799eff6a68b49bdb01ee15bbe43d84..6e24d969dde4b6f2d6dfca9a001fac3e81519e7d 100644 --- a/polkadot/roadmap/implementers-guide/src/types/overseer-protocol.md +++ b/polkadot/roadmap/implementers-guide/src/types/overseer-protocol.md @@ -111,21 +111,15 @@ pub enum ApprovalCheckError { } enum ApprovalVotingMessage { - /// Check if the assignment is valid and can be accepted by our view of the protocol. - /// Should not be sent unless the block hash is known. - CheckAndImportAssignment( - IndirectAssignmentCert, - CandidateIndex, // The index of the candidate included in the block. - ResponseChannel, - ), - /// Check if the approval vote is valid and can be accepted by our view of the - /// protocol. - /// - /// Should not be sent unless the block hash within the indirect vote is known. - CheckAndImportApproval( - IndirectSignedApprovalVote, - ResponseChannel, - ), + /// Import an assignment into the approval-voting database. + /// + /// Should not be sent unless the block hash is known and the VRF assignment checks out. + ImportAssignment(CheckedIndirectAssignment, Option>), + /// Import an approval vote into approval-voting database + /// + /// Should not be sent unless the block hash within the indirect vote is known, vote is + /// correctly signed and we had a previous assignment for the candidate. + ImportApproval(CheckedIndirectSignedApprovalVote, Option>), /// Returns the highest possible ancestor hash of the provided block hash which is /// acceptable to vote on finality for. Along with that, return the lists of candidate hashes /// which appear in every block from the (non-inclusive) base number up to (inclusive) the specified @@ -907,22 +901,6 @@ const APPROVAL_EXECUTION_TIMEOUT: Duration = 6 seconds; /// or `Ok(ValidationResult::Invalid)`. #[derive(Debug)] pub enum CandidateValidationMessage { - /// Validate a candidate with provided parameters using relay-chain state. - /// - /// This will implicitly attempt to gather the `PersistedValidationData` and `ValidationCode` - /// from the runtime API of the chain, based on the `relay_parent` - /// of the `CandidateDescriptor`. - /// - /// This will also perform checking of validation outputs against the acceptance criteria. - /// - /// If there is no state available which can provide this data or the core for - /// the para is not free at the relay-parent, an error is returned. - ValidateFromChainState( - CandidateDescriptor, - Arc, - Duration, // Execution timeout. - oneshot::Sender>, - ), /// Validate a candidate with provided, exhaustive parameters for validation. /// /// Explicitly provide the `PersistedValidationData` and `ValidationCode` so this can do full diff --git a/polkadot/runtime/common/src/assigned_slots/mod.rs b/polkadot/runtime/common/src/assigned_slots/mod.rs index dd39789e10cfdbb481615f728b6cd223ec64f037..96c98c45954d3fd29e6ab64fab1e24c829f2d39f 100644 --- a/polkadot/runtime/common/src/assigned_slots/mod.rs +++ b/polkadot/runtime/common/src/assigned_slots/mod.rs @@ -186,6 +186,7 @@ pub mod pallet { pub struct GenesisConfig { pub max_temporary_slots: u32, pub max_permanent_slots: u32, + #[serde(skip)] pub _config: PhantomData, } diff --git a/polkadot/runtime/common/src/claims.rs b/polkadot/runtime/common/src/claims.rs index 162bf01c38432f95ba25216478205c4046448c98..32686d1a0bfa46720db1aa14088b7e14f68624bb 100644 --- a/polkadot/runtime/common/src/claims.rs +++ b/polkadot/runtime/common/src/claims.rs @@ -1,18 +1,18 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Polkadot. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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 Substrate. If not, see . +// along with Polkadot. If not, see . //! Pallet to process claims from Ethereum addresses. diff --git a/polkadot/runtime/common/src/purchase.rs b/polkadot/runtime/common/src/purchase.rs index d650548b8ac39a4059a7a9721e5926ae5ab528a9..9cbb907536d94769afbc5825ead2942c71c811ca 100644 --- a/polkadot/runtime/common/src/purchase.rs +++ b/polkadot/runtime/common/src/purchase.rs @@ -1,18 +1,18 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Polkadot. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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 Substrate. If not, see . +// along with Polkadot. If not, see . //! Pallet to process purchase of DOTs. diff --git a/polkadot/runtime/common/src/xcm_sender.rs b/polkadot/runtime/common/src/xcm_sender.rs index dace785a535b914b6da3ca322ea11cfe94f5dbfd..37fe7f0b59e979a7812bbbf4d7f9e7cb7541c386 100644 --- a/polkadot/runtime/common/src/xcm_sender.rs +++ b/polkadot/runtime/common/src/xcm_sender.rs @@ -141,6 +141,11 @@ where } impl InspectMessageQueues for ChildParachainRouter { + fn clear_messages() { + // Best effort. + let _ = dmp::DownwardMessageQueues::::clear(u32::MAX, None); + } + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { dmp::DownwardMessageQueues::::iter() .map(|(para_id, messages)| { diff --git a/polkadot/runtime/parachains/Cargo.toml b/polkadot/runtime/parachains/Cargo.toml index cfe373e8cba2e6c186ea62d7121aff5f3d4cafa7..a3eec3f9d961ac2119df82c430178855b212cbb0 100644 --- a/polkadot/runtime/parachains/Cargo.toml +++ b/polkadot/runtime/parachains/Cargo.toml @@ -59,6 +59,8 @@ polkadot-runtime-metrics = { workspace = true } polkadot-core-primitives = { workspace = true } [dev-dependencies] +polkadot-primitives = { workspace = true, features = ["test"] } + futures = { workspace = true } hex-literal = { workspace = true, default-features = true } sp-keyring = { workspace = true, default-features = true } diff --git a/polkadot/runtime/parachains/src/builder.rs b/polkadot/runtime/parachains/src/builder.rs index 65e56881c315f141b4b2898617cb1bfa7b1d4841..1654590d109e97aecc3de062ebb57489274c5026 100644 --- a/polkadot/runtime/parachains/src/builder.rs +++ b/polkadot/runtime/parachains/src/builder.rs @@ -30,32 +30,38 @@ use bitvec::{order::Lsb0 as BitOrderLsb0, vec::BitVec}; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use polkadot_primitives::{ - node_features::FeatureIndex, AvailabilityBitfield, BackedCandidate, CandidateCommitments, - CandidateDescriptor, CandidateHash, CollatorId, CollatorSignature, CommittedCandidateReceipt, - CompactStatement, CoreIndex, DisputeStatement, DisputeStatementSet, GroupIndex, HeadData, - Id as ParaId, IndexedVec, InherentData as ParachainsInherentData, InvalidDisputeStatementKind, + node_features::FeatureIndex, + vstaging::{ + BackedCandidate, CandidateDescriptorV2, ClaimQueueOffset, + CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreSelector, + InherentData as ParachainsInherentData, UMPSignal, UMP_SEPARATOR, + }, + AvailabilityBitfield, CandidateCommitments, CandidateDescriptor, CandidateHash, CollatorId, + CollatorSignature, CompactStatement, CoreIndex, DisputeStatement, DisputeStatementSet, + GroupIndex, HeadData, Id as ParaId, IndexedVec, InvalidDisputeStatementKind, PersistedValidationData, SessionIndex, SigningContext, UncheckedSigned, ValidDisputeStatementKind, ValidationCode, ValidatorId, ValidatorIndex, ValidityAttestation, }; -use sp_core::{sr25519, ByteArray, H256}; +use sp_core::{ByteArray, H256}; use sp_runtime::{ generic::Digest, traits::{Header as HeaderT, One, TrailingZeroInput, Zero}, RuntimeAppPublic, }; - -/// Create a null collator id. -pub fn dummy_collator() -> CollatorId { - CollatorId::from_slice(&vec![0u8; 32]).expect("32 bytes; qed") +fn mock_validation_code() -> ValidationCode { + ValidationCode(vec![1, 2, 3]) } -/// Create a null collator signature. -pub fn dummy_collator_signature() -> CollatorSignature { - CollatorSignature::from_slice(&vec![0u8; 64]).expect("64 bytes; qed") +/// Create a dummy collator id suitable to be used in a V1 candidate descriptor. +pub fn junk_collator() -> CollatorId { + CollatorId::from_slice(&mut (0..32).into_iter().collect::>().as_slice()) + .expect("32 bytes; qed") } -fn mock_validation_code() -> ValidationCode { - ValidationCode(vec![1, 2, 3]) +/// Creates a dummy collator signature suitable to be used in a V1 candidate descriptor. +pub fn junk_collator_signature() -> CollatorSignature { + CollatorSignature::from_slice(&mut (0..64).into_iter().collect::>().as_slice()) + .expect("64 bytes; qed") } /// Grab an account, seeded by a name and index. @@ -136,9 +142,16 @@ pub(crate) struct BenchBuilder { fill_claimqueue: bool, /// Cores which should not be available when being populated with pending candidates. unavailable_cores: Vec, + /// Use v2 candidate descriptor. + candidate_descriptor_v2: bool, + /// Apply custom changes to generated candidates + candidate_modifier: Option>, _phantom: core::marker::PhantomData, } +pub type CandidateModifier = + fn(CommittedCandidateReceipt) -> CommittedCandidateReceipt; + /// Paras inherent `enter` benchmark scenario. #[cfg(any(feature = "runtime-benchmarks", test))] pub(crate) struct Bench { @@ -167,6 +180,8 @@ impl BenchBuilder { code_upgrade: None, fill_claimqueue: true, unavailable_cores: vec![], + candidate_descriptor_v2: false, + candidate_modifier: None, _phantom: core::marker::PhantomData::, } } @@ -275,6 +290,21 @@ impl BenchBuilder { self } + /// Toggle usage of v2 candidate descriptors. + pub(crate) fn set_candidate_descriptor_v2(mut self, enable: bool) -> Self { + self.candidate_descriptor_v2 = enable; + self + } + + /// Set the candidate modifier. + pub(crate) fn set_candidate_modifier( + mut self, + modifier: Option>, + ) -> Self { + self.candidate_modifier = modifier; + self + } + /// Get the maximum number of validators per core. fn max_validators_per_core(&self) -> u32 { self.max_validators_per_core.unwrap_or(Self::fallback_max_validators_per_core()) @@ -310,18 +340,35 @@ impl BenchBuilder { HeadData(vec![0xFF; max_head_size as usize]) } - fn candidate_descriptor_mock() -> CandidateDescriptor { - CandidateDescriptor:: { - para_id: 0.into(), - relay_parent: Default::default(), - collator: CollatorId::from(sr25519::Public::from_raw([42u8; 32])), - persisted_validation_data_hash: Default::default(), - pov_hash: Default::default(), - erasure_root: Default::default(), - signature: CollatorSignature::from(sr25519::Signature::from_raw([42u8; 64])), - para_head: Default::default(), - validation_code_hash: mock_validation_code().hash(), + fn candidate_descriptor_mock(candidate_descriptor_v2: bool) -> CandidateDescriptorV2 { + if candidate_descriptor_v2 { + CandidateDescriptorV2::new( + 0.into(), + Default::default(), + CoreIndex(200), + 2, + Default::default(), + Default::default(), + Default::default(), + Default::default(), + mock_validation_code().hash(), + ) + } else { + // Convert v1 to v2. + CandidateDescriptor:: { + para_id: 0.into(), + relay_parent: Default::default(), + collator: junk_collator(), + persisted_validation_data_hash: Default::default(), + pov_hash: Default::default(), + erasure_root: Default::default(), + signature: junk_collator_signature(), + para_head: Default::default(), + validation_code_hash: mock_validation_code().hash(), + } + .into() } + .into() } /// Create a mock of `CandidatePendingAvailability`. @@ -331,17 +378,19 @@ impl BenchBuilder { candidate_hash: CandidateHash, availability_votes: BitVec, commitments: CandidateCommitments, + candidate_descriptor_v2: bool, ) -> inclusion::CandidatePendingAvailability> { inclusion::CandidatePendingAvailability::>::new( - core_idx, // core - candidate_hash, // hash - Self::candidate_descriptor_mock(), // candidate descriptor - commitments, // commitments - availability_votes, // availability votes - Default::default(), // backers - Zero::zero(), // relay parent - One::one(), // relay chain block this was backed in - group_idx, // backing group + core_idx, // core + candidate_hash, // hash + Self::candidate_descriptor_mock(candidate_descriptor_v2), // candidate descriptor + commitments, // commitments + availability_votes, // availability votes + Default::default(), // backers + Zero::zero(), // relay parent + One::one(), /* relay chain block this + * was backed in */ + group_idx, // backing group ) } @@ -356,6 +405,7 @@ impl BenchBuilder { group_idx: GroupIndex, availability_votes: BitVec, candidate_hash: CandidateHash, + candidate_descriptor_v2: bool, ) { let commitments = CandidateCommitments:: { upward_messages: Default::default(), @@ -371,6 +421,7 @@ impl BenchBuilder { candidate_hash, availability_votes, commitments, + candidate_descriptor_v2, ); inclusion::PendingAvailability::::mutate(para_id, |maybe_candidates| { if let Some(candidates) = maybe_candidates { @@ -530,6 +581,7 @@ impl BenchBuilder { // No validators have made this candidate available yet. bitvec::bitvec![u8, bitvec::order::Lsb0; 0; validators.len()], CandidateHash(H256::from(byte32_slice_from(current_core_idx))), + self.candidate_descriptor_v2, ); if !self.unavailable_cores.contains(¤t_core_idx) { concluding_cores.insert(current_core_idx); @@ -632,18 +684,35 @@ impl BenchBuilder { let group_validators = scheduler::Pallet::::group_validators(group_idx).unwrap(); - let candidate = CommittedCandidateReceipt:: { - descriptor: CandidateDescriptor:: { + let descriptor = if self.candidate_descriptor_v2 { + CandidateDescriptorV2::new( + para_id, + relay_parent, + core_idx, + self.target_session, + persisted_validation_data_hash, + pov_hash, + Default::default(), + head_data.hash(), + validation_code_hash, + ) + } else { + CandidateDescriptor:: { para_id, relay_parent, - collator: dummy_collator(), + collator: junk_collator(), persisted_validation_data_hash, pov_hash, erasure_root: Default::default(), - signature: dummy_collator_signature(), + signature: junk_collator_signature(), para_head: head_data.hash(), validation_code_hash, - }, + } + .into() + }; + + let mut candidate = CommittedCandidateReceipt:: { + descriptor, commitments: CandidateCommitments:: { upward_messages: Default::default(), horizontal_messages: Default::default(), @@ -655,6 +724,27 @@ impl BenchBuilder { }, }; + if self.candidate_descriptor_v2 { + // `UMPSignal` separator. + candidate.commitments.upward_messages.force_push(UMP_SEPARATOR); + + // `SelectCore` commitment. + // Claim queue offset must be `0` so this candidate is for the very + // next block. + candidate.commitments.upward_messages.force_push( + UMPSignal::SelectCore( + CoreSelector(chain_idx as u8), + ClaimQueueOffset(0), + ) + .encode(), + ); + } + + // Maybe apply the candidate modifier + if let Some(modifier) = self.candidate_modifier { + candidate = modifier(candidate); + } + let candidate_hash = candidate.hash(); let validity_votes: Vec<_> = group_validators @@ -674,12 +764,15 @@ impl BenchBuilder { }) .collect(); - // 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::ActiveConfig::::get() - .node_features - .get(FeatureIndex::ElasticScalingMVP as usize) - .map(|_the_bit| core_idx); + // Don't inject core when it is available in descriptor. + let core_idx = if candidate.descriptor.core_index().is_some() { + None + } else { + configuration::ActiveConfig::::get() + .node_features + .get(FeatureIndex::ElasticScalingMVP as usize) + .and_then(|the_bit| if *the_bit { Some(core_idx) } else { None }) + }; BackedCandidate::::new( candidate, @@ -732,6 +825,7 @@ impl BenchBuilder { group_idx, Self::validator_availability_votes_yes(validators.len()), candidate_hash, + self.candidate_descriptor_v2, ); let statements_len = diff --git a/polkadot/runtime/parachains/src/configuration.rs b/polkadot/runtime/parachains/src/configuration.rs index 30fe95883e77f2cf09ada2a14ced20b93a7d1988..36888247580e75c51238afc127f6ab81c233bdd1 100644 --- a/polkadot/runtime/parachains/src/configuration.rs +++ b/polkadot/runtime/parachains/src/configuration.rs @@ -29,7 +29,7 @@ use polkadot_parachain_primitives::primitives::{ use polkadot_primitives::{ ApprovalVotingParams, AsyncBackingParams, Balance, ExecutorParamError, ExecutorParams, NodeFeatures, SessionIndex, LEGACY_MIN_BACKING_VOTES, MAX_CODE_SIZE, MAX_HEAD_DATA_SIZE, - MAX_POV_SIZE, ON_DEMAND_MAX_QUEUE_MAX_SIZE, + ON_DEMAND_MAX_QUEUE_MAX_SIZE, }; use sp_runtime::{traits::Zero, Perbill, Percent}; @@ -46,6 +46,10 @@ use polkadot_primitives::SchedulerParams; const LOG_TARGET: &str = "runtime::configuration"; +// This value is derived from network layer limits. See `sc_network::MAX_RESPONSE_SIZE` and +// `polkadot_node_network_protocol::POV_RESPONSE_SIZE`. +const POV_SIZE_HARD_LIMIT: u32 = 16 * 1024 * 1024; + /// All configuration of the runtime with respect to paras. #[derive( Clone, @@ -310,7 +314,7 @@ pub enum InconsistentError { MaxCodeSizeExceedHardLimit { max_code_size: u32 }, /// `max_head_data_size` exceeds the hard limit of `MAX_HEAD_DATA_SIZE`. MaxHeadDataSizeExceedHardLimit { max_head_data_size: u32 }, - /// `max_pov_size` exceeds the hard limit of `MAX_POV_SIZE`. + /// `max_pov_size` exceeds the hard limit of `POV_SIZE_HARD_LIMIT`. MaxPovSizeExceedHardLimit { max_pov_size: u32 }, /// `minimum_validation_upgrade_delay` is less than `paras_availability_period`. MinimumValidationUpgradeDelayLessThanChainAvailabilityPeriod { @@ -377,7 +381,7 @@ where }) } - if self.max_pov_size > MAX_POV_SIZE { + if self.max_pov_size > POV_SIZE_HARD_LIMIT { return Err(MaxPovSizeExceedHardLimit { max_pov_size: self.max_pov_size }) } diff --git a/polkadot/runtime/parachains/src/configuration/tests.rs b/polkadot/runtime/parachains/src/configuration/tests.rs index dad8b6458e10ae16868d4354054ad80923f548d0..0d20399e471ba7b66f93e0f46dfe7f1a3677ef84 100644 --- a/polkadot/runtime/parachains/src/configuration/tests.rs +++ b/polkadot/runtime/parachains/src/configuration/tests.rs @@ -210,7 +210,7 @@ fn invariants() { ); assert_err!( - Configuration::set_max_pov_size(RuntimeOrigin::root(), MAX_POV_SIZE + 1), + Configuration::set_max_pov_size(RuntimeOrigin::root(), POV_SIZE_HARD_LIMIT + 1), Error::::InvalidNewValue ); diff --git a/polkadot/runtime/parachains/src/hrmp.rs b/polkadot/runtime/parachains/src/hrmp.rs index b149404b41b8716173d57b0dfa37a777618946db..220543f00ec33535dbbf2eef7da61c396a182092 100644 --- a/polkadot/runtime/parachains/src/hrmp.rs +++ b/polkadot/runtime/parachains/src/hrmp.rs @@ -945,7 +945,7 @@ impl Pallet { outgoing_paras.len() as u32 )) .saturating_add(::WeightInfo::force_process_hrmp_close( - outgoing_paras.len() as u32 + outgoing_paras.len() as u32, )) } diff --git a/polkadot/runtime/parachains/src/inclusion/benchmarking.rs b/polkadot/runtime/parachains/src/inclusion/benchmarking.rs index 978ef718ea4007e142340b0a4088b4c70ad8043d..1dac3c92cf166cd30c2170d9d8fb142fad6af54f 100644 --- a/polkadot/runtime/parachains/src/inclusion/benchmarking.rs +++ b/polkadot/runtime/parachains/src/inclusion/benchmarking.rs @@ -25,10 +25,9 @@ use bitvec::{bitvec, prelude::Lsb0}; use frame_benchmarking::benchmarks; use pallet_message_queue as mq; use polkadot_primitives::{ - CandidateCommitments, CollatorId, CollatorSignature, CommittedCandidateReceipt, HrmpChannelId, - OutboundHrmpMessage, SessionIndex, + vstaging::CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CandidateCommitments, + HrmpChannelId, OutboundHrmpMessage, SessionIndex, }; -use sp_core::sr25519; fn create_candidate_commitments( para_id: ParaId, @@ -95,8 +94,8 @@ benchmarks! { } enact_candidate { - let u in 1 .. 32; - let h in 1 .. 32; + let u in 0 .. 2; + let h in 0 .. 2; let c in 0 .. 1; let para = 42_u32.into(); // not especially important. @@ -124,17 +123,17 @@ benchmarks! { let core_index = CoreIndex::from(0); let backing_group = GroupIndex::from(0); - let descriptor = CandidateDescriptor:: { - para_id: para, - relay_parent: Default::default(), - collator: CollatorId::from(sr25519::Public::from_raw([42u8; 32])), - persisted_validation_data_hash: Default::default(), - pov_hash: Default::default(), - erasure_root: Default::default(), - signature: CollatorSignature::from(sr25519::Signature::from_raw([42u8; 64])), - para_head: Default::default(), - validation_code_hash: ValidationCode(vec![1, 2, 3]).hash(), - }; + let descriptor = CandidateDescriptor::::new( + para, + Default::default(), + CoreIndex(0), + 1, + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ValidationCode(vec![1, 2, 3]).hash(), + ); let receipt = CommittedCandidateReceipt:: { descriptor, diff --git a/polkadot/runtime/parachains/src/inclusion/migration.rs b/polkadot/runtime/parachains/src/inclusion/migration.rs index 36a810d341c655cf9309b9b8fec87032a8bc7486..2a215d5d595cf44dfdc8d3701ff021e9e8ac1600 100644 --- a/polkadot/runtime/parachains/src/inclusion/migration.rs +++ b/polkadot/runtime/parachains/src/inclusion/migration.rs @@ -20,8 +20,8 @@ pub mod v0 { use frame_support::{storage_alias, Twox64Concat}; use frame_system::pallet_prelude::BlockNumberFor; use polkadot_primitives::{ - AvailabilityBitfield, CandidateCommitments, CandidateDescriptor, CandidateHash, CoreIndex, - GroupIndex, Id as ParaId, ValidatorIndex, + vstaging::CandidateDescriptorV2 as CandidateDescriptor, AvailabilityBitfield, + CandidateCommitments, CandidateHash, CoreIndex, GroupIndex, Id as ParaId, ValidatorIndex, }; use scale_info::TypeInfo; @@ -219,7 +219,7 @@ mod tests { use frame_support::traits::UncheckedOnRuntimeUpgrade; use polkadot_primitives::{AvailabilityBitfield, Id as ParaId}; use polkadot_primitives_test_helpers::{ - dummy_candidate_commitments, dummy_candidate_descriptor, dummy_hash, + dummy_candidate_commitments, dummy_candidate_descriptor_v2, dummy_hash, }; #[test] @@ -235,7 +235,7 @@ mod tests { let mut expected = vec![]; for i in 1..5 { - let descriptor = dummy_candidate_descriptor(dummy_hash()); + let descriptor = dummy_candidate_descriptor_v2(dummy_hash()); v0::PendingAvailability::::insert( ParaId::from(i), v0::CandidatePendingAvailability { @@ -285,7 +285,7 @@ mod tests { ParaId::from(6), v0::CandidatePendingAvailability { core: CoreIndex(6), - descriptor: dummy_candidate_descriptor(dummy_hash()), + descriptor: dummy_candidate_descriptor_v2(dummy_hash()), relay_parent_number: 6, hash: CandidateHash(dummy_hash()), availability_votes: Default::default(), diff --git a/polkadot/runtime/parachains/src/inclusion/mod.rs b/polkadot/runtime/parachains/src/inclusion/mod.rs index b1d22996fd1228f07ebb74cf5659efdc1eb2e1dc..36f874b8db1e9bb015657cc7b2628e64ee75443b 100644 --- a/polkadot/runtime/parachains/src/inclusion/mod.rs +++ b/polkadot/runtime/parachains/src/inclusion/mod.rs @@ -44,11 +44,15 @@ use frame_support::{ use frame_system::pallet_prelude::*; use pallet_message_queue::OnQueueChanged; use polkadot_primitives::{ - effective_minimum_backing_votes, supermajority_threshold, well_known_keys, BackedCandidate, - CandidateCommitments, CandidateDescriptor, CandidateHash, CandidateReceipt, - CommittedCandidateReceipt, CoreIndex, GroupIndex, Hash, HeadData, Id as ParaId, - SignedAvailabilityBitfields, SigningContext, UpwardMessage, ValidatorId, ValidatorIndex, - ValidityAttestation, + effective_minimum_backing_votes, supermajority_threshold, + vstaging::{ + BackedCandidate, CandidateDescriptorV2 as CandidateDescriptor, + CandidateReceiptV2 as CandidateReceipt, + CommittedCandidateReceiptV2 as CommittedCandidateReceipt, + }, + well_known_keys, CandidateCommitments, CandidateHash, CoreIndex, GroupIndex, Hash, HeadData, + Id as ParaId, SignedAvailabilityBitfields, SigningContext, UpwardMessage, ValidatorId, + ValidatorIndex, ValidityAttestation, }; use scale_info::TypeInfo; use sp_runtime::{traits::One, DispatchError, SaturatedConversion, Saturating}; @@ -436,6 +440,11 @@ pub(crate) enum UmpAcceptanceCheckErr { TotalSizeExceeded { total_size: u64, limit: u64 }, /// A para-chain cannot send UMP messages while it is offboarding. IsOffboarding, + /// The allowed number of `UMPSignal` messages in the queue was exceeded. + /// Currenly only one such message is allowed. + TooManyUMPSignals { count: u32 }, + /// The UMP queue contains an invalid `UMPSignal` + NoUmpSignal, } impl fmt::Debug for UmpAcceptanceCheckErr { @@ -464,6 +473,12 @@ impl fmt::Debug for UmpAcceptanceCheckErr { UmpAcceptanceCheckErr::IsOffboarding => { write!(fmt, "upward message rejected because the para is off-boarding") }, + UmpAcceptanceCheckErr::TooManyUMPSignals { count } => { + write!(fmt, "the ump queue has too many `UMPSignal` messages ({} > 1 )", count) + }, + UmpAcceptanceCheckErr::NoUmpSignal => { + write!(fmt, "Required UMP signal not found") + }, } } } @@ -764,7 +779,7 @@ impl Pallet { let mut backers = bitvec::bitvec![u8, BitOrderLsb0; 0; validators.len()]; let signing_context = SigningContext { - parent_hash: backed_candidate.descriptor().relay_parent, + parent_hash: backed_candidate.descriptor().relay_parent(), session_index: shared::CurrentSessionIndex::::get(), }; @@ -880,7 +895,7 @@ impl Pallet { let now = frame_system::Pallet::::block_number(); paras::Pallet::::schedule_code_upgrade( - receipt.descriptor.para_id, + receipt.descriptor.para_id(), new_code, now, &config, @@ -890,19 +905,19 @@ impl Pallet { // enact the messaging facet of the candidate. dmp::Pallet::::prune_dmq( - receipt.descriptor.para_id, + receipt.descriptor.para_id(), commitments.processed_downward_messages, ); Self::receive_upward_messages( - receipt.descriptor.para_id, + receipt.descriptor.para_id(), commitments.upward_messages.as_slice(), ); hrmp::Pallet::::prune_hrmp( - receipt.descriptor.para_id, + receipt.descriptor.para_id(), BlockNumberFor::::from(commitments.hrmp_watermark), ); hrmp::Pallet::::queue_outbound_hrmp( - receipt.descriptor.para_id, + receipt.descriptor.para_id(), commitments.horizontal_messages, ); @@ -914,7 +929,7 @@ impl Pallet { )); paras::Pallet::::note_new_head( - receipt.descriptor.para_id, + receipt.descriptor.para_id(), commitments.head_data, relay_parent_number, ); @@ -931,6 +946,27 @@ impl Pallet { para: ParaId, upward_messages: &[UpwardMessage], ) -> Result<(), UmpAcceptanceCheckErr> { + // Filter any pending UMP signals and the separator. + let upward_messages = if let Some(separator_index) = + upward_messages.iter().position(|message| message.is_empty()) + { + let (upward_messages, ump_signals) = upward_messages.split_at(separator_index); + + if ump_signals.len() > 2 { + return Err(UmpAcceptanceCheckErr::TooManyUMPSignals { + count: ump_signals.len() as u32, + }) + } + + if ump_signals.len() == 1 { + return Err(UmpAcceptanceCheckErr::NoUmpSignal) + } + + upward_messages + } else { + upward_messages + }; + // Cannot send UMP messages while off-boarding. if paras::Pallet::::is_offboarding(para) { ensure!(upward_messages.is_empty(), UmpAcceptanceCheckErr::IsOffboarding); @@ -985,11 +1021,12 @@ impl Pallet { pub(crate) fn receive_upward_messages(para: ParaId, upward_messages: &[Vec]) { let bounded = upward_messages .iter() + // Stop once we hit the `UMPSignal` separator. + .take_while(|message| !message.is_empty()) .filter_map(|d| { BoundedSlice::try_from(&d[..]) - .map_err(|e| { + .inspect_err(|_| { defensive!("Accepted candidate contains too long msg, len=", d.len()); - e }) .ok() }) @@ -1250,28 +1287,28 @@ impl CandidateCheckContext { backed_candidate_receipt: &CommittedCandidateReceipt<::Hash>, parent_head_data: HeadData, ) -> Result, Error> { - let para_id = backed_candidate_receipt.descriptor().para_id; - let relay_parent = backed_candidate_receipt.descriptor().relay_parent; + let para_id = backed_candidate_receipt.descriptor.para_id(); + let relay_parent = backed_candidate_receipt.descriptor.relay_parent(); // Check that the relay-parent is one of the allowed relay-parents. - let (relay_parent_storage_root, relay_parent_number) = { + let (state_root, relay_parent_number) = { match allowed_relay_parents.acquire_info(relay_parent, self.prev_context) { None => return Err(Error::::DisallowedRelayParent), - Some(info) => info, + Some((info, relay_parent_number)) => (info.state_root, relay_parent_number), } }; { let persisted_validation_data = make_persisted_validation_data_with_parent::( relay_parent_number, - relay_parent_storage_root, + state_root, parent_head_data, ); let expected = persisted_validation_data.hash(); ensure!( - expected == backed_candidate_receipt.descriptor().persisted_validation_data_hash, + expected == backed_candidate_receipt.descriptor.persisted_validation_data_hash(), Error::::ValidationDataHashMismatch, ); } @@ -1280,12 +1317,12 @@ impl CandidateCheckContext { // A candidate for a parachain without current validation code is not scheduled. .ok_or_else(|| Error::::UnscheduledCandidate)?; ensure!( - backed_candidate_receipt.descriptor().validation_code_hash == validation_code_hash, + backed_candidate_receipt.descriptor.validation_code_hash() == validation_code_hash, Error::::InvalidValidationCodeHash, ); ensure!( - backed_candidate_receipt.descriptor().para_head == + backed_candidate_receipt.descriptor.para_head() == backed_candidate_receipt.commitments.head_data.hash(), Error::::ParaHeadMismatch, ); diff --git a/polkadot/runtime/parachains/src/inclusion/tests.rs b/polkadot/runtime/parachains/src/inclusion/tests.rs index 95fd66bf8e4fbcd139198955fec3e2eff0b42801..87d21e209a49e5141aba6a7edf30e08e4addc573 100644 --- a/polkadot/runtime/parachains/src/inclusion/tests.rs +++ b/polkadot/runtime/parachains/src/inclusion/tests.rs @@ -26,7 +26,12 @@ use crate::{ shared::AllowedRelayParentsTracker, }; use polkadot_primitives::{ - effective_minimum_backing_votes, AvailabilityBitfield, SignedAvailabilityBitfields, + effective_minimum_backing_votes, + vstaging::{ + CandidateDescriptorV2, CandidateDescriptorVersion, ClaimQueueOffset, CoreSelector, + UMPSignal, UMP_SEPARATOR, + }, + AvailabilityBitfield, CandidateDescriptor, SignedAvailabilityBitfields, UncheckedSignedAvailabilityBitfields, }; @@ -34,14 +39,13 @@ use assert_matches::assert_matches; use codec::DecodeAll; use frame_support::assert_noop; use polkadot_primitives::{ - BlockNumber, CandidateCommitments, CandidateDescriptor, CollatorId, + BlockNumber, CandidateCommitments, CollatorId, CollatorSignature, CompactStatement as Statement, Hash, SignedAvailabilityBitfield, SignedStatement, ValidationCode, ValidatorId, ValidityAttestation, PARACHAIN_KEY_TYPE_ID, }; -use polkadot_primitives_test_helpers::{ - dummy_collator, dummy_collator_signature, dummy_validation_code, -}; +use polkadot_primitives_test_helpers::dummy_validation_code; use sc_keystore::LocalKeystore; +use sp_core::ByteArray; use sp_keyring::Sr25519Keyring; use sp_keystore::{Keystore, KeystorePtr}; use std::sync::Arc; @@ -84,7 +88,7 @@ fn default_allowed_relay_parent_tracker() -> AllowedRelayParentsTracker, pub(crate) validation_code: ValidationCode, pub(crate) hrmp_watermark: BlockNumber, + /// Creates a v2 descriptor if set. + pub(crate) core_index: Option, + /// The core selector to use. + pub(crate) core_selector: Option, } impl std::default::Default for TestCandidateBuilder { @@ -296,14 +286,28 @@ impl std::default::Default for TestCandidateBuilder { new_validation_code: None, validation_code: dummy_validation_code(), hrmp_watermark: 0u32.into(), + core_index: None, + core_selector: None, } } } impl TestCandidateBuilder { pub(crate) fn build(self) -> CommittedCandidateReceipt { - CommittedCandidateReceipt { - descriptor: CandidateDescriptor { + let descriptor = if let Some(core_index) = self.core_index { + CandidateDescriptorV2::new( + self.para_id, + self.relay_parent, + core_index, + 0, + self.persisted_validation_data_hash, + self.pov_hash, + Default::default(), + self.para_head_hash.unwrap_or_else(|| self.head_data.hash()), + self.validation_code.hash(), + ) + } else { + CandidateDescriptor { para_id: self.para_id, pov_hash: self.pov_hash, relay_parent: self.relay_parent, @@ -311,16 +315,40 @@ impl TestCandidateBuilder { validation_code_hash: self.validation_code.hash(), para_head: self.para_head_hash.unwrap_or_else(|| self.head_data.hash()), erasure_root: Default::default(), - signature: dummy_collator_signature(), - collator: dummy_collator(), - }, + signature: CollatorSignature::from_slice( + &mut (0..64).into_iter().collect::>().as_slice(), + ) + .expect("64 bytes; qed"), + collator: CollatorId::from_slice( + &mut (0..32).into_iter().collect::>().as_slice(), + ) + .expect("32 bytes; qed"), + } + .into() + }; + let mut ccr = CommittedCandidateReceipt { + descriptor, commitments: CandidateCommitments { head_data: self.head_data, new_validation_code: self.new_validation_code, hrmp_watermark: self.hrmp_watermark, ..Default::default() }, + }; + + if ccr.descriptor.version() == CandidateDescriptorVersion::V2 { + ccr.commitments.upward_messages.force_push(UMP_SEPARATOR); + + ccr.commitments.upward_messages.force_push( + UMPSignal::SelectCore( + CoreSelector(self.core_selector.unwrap_or_default()), + ClaimQueueOffset(0), + ) + .encode(), + ); } + + ccr } } @@ -1244,7 +1272,7 @@ fn candidate_checks() { // Check candidate ordering { - let mut candidate_a = TestCandidateBuilder { + let candidate_a = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -1253,7 +1281,7 @@ fn candidate_checks() { ..Default::default() } .build(); - let mut candidate_b_1 = TestCandidateBuilder { + let candidate_b_1 = TestCandidateBuilder { para_id: chain_b, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(2), @@ -1265,7 +1293,7 @@ fn candidate_checks() { .build(); // Make candidate b2 a child of b1. - let mut candidate_b_2 = TestCandidateBuilder { + let candidate_b_2 = TestCandidateBuilder { para_id: chain_b, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(3), @@ -1281,10 +1309,6 @@ fn candidate_checks() { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); - collator_sign_candidate(Sr25519Keyring::Two, &mut candidate_b_1); - collator_sign_candidate(Sr25519Keyring::Two, &mut candidate_b_2); - let backed_a = back_candidate( candidate_a, &validators, @@ -1356,7 +1380,7 @@ fn candidate_checks() { // candidate does not build on top of the latest unincluded head - let mut candidate_b_3 = TestCandidateBuilder { + let candidate_b_3 = TestCandidateBuilder { para_id: chain_b, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(4), @@ -1371,7 +1395,6 @@ fn candidate_checks() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::Two, &mut candidate_b_3); let backed_b_3 = back_candidate( candidate_b_3, @@ -1396,7 +1419,7 @@ fn candidate_checks() { // candidate not backed. { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -1405,7 +1428,6 @@ fn candidate_checks() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); // Insufficient backing. let backed = back_candidate( @@ -1459,7 +1481,7 @@ fn candidate_checks() { let wrong_parent_hash = Hash::repeat_byte(222); assert!(System::parent_hash() != wrong_parent_hash); - let mut candidate_a = TestCandidateBuilder { + let candidate_a = TestCandidateBuilder { para_id: chain_a, relay_parent: wrong_parent_hash, pov_hash: Hash::repeat_byte(1), @@ -1468,7 +1490,7 @@ fn candidate_checks() { } .build(); - let mut candidate_b = TestCandidateBuilder { + let candidate_b = TestCandidateBuilder { para_id: chain_b, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(2), @@ -1478,10 +1500,6 @@ fn candidate_checks() { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); - - collator_sign_candidate(Sr25519Keyring::Two, &mut candidate_b); - let backed_a = back_candidate( candidate_a, &validators, @@ -1531,10 +1549,9 @@ fn candidate_checks() { .build(); assert_eq!(CollatorId::from(Sr25519Keyring::Two.public()), thread_collator); - collator_sign_candidate(Sr25519Keyring::Two, &mut candidate); // change the candidate after signing. - candidate.descriptor.pov_hash = Hash::repeat_byte(2); + candidate.descriptor.set_pov_hash(Hash::repeat_byte(2)); let backed = back_candidate( candidate, @@ -1597,7 +1614,7 @@ fn candidate_checks() { // interfering code upgrade - reject { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -1608,8 +1625,6 @@ fn candidate_checks() { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -1648,7 +1663,7 @@ fn candidate_checks() { // Bad validation data hash - reject { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -1658,8 +1673,6 @@ fn candidate_checks() { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -1685,7 +1698,7 @@ fn candidate_checks() { // bad validation code hash { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -1696,8 +1709,6 @@ fn candidate_checks() { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -1723,7 +1734,7 @@ fn candidate_checks() { // Para head hash in descriptor doesn't match head data { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -1734,8 +1745,6 @@ fn candidate_checks() { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -1826,7 +1835,7 @@ fn backing_works() { let chain_b_assignment = (chain_b, CoreIndex::from(1)); let thread_a_assignment = (thread_a, CoreIndex::from(2)); - let mut candidate_a = TestCandidateBuilder { + let candidate_a = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -1835,9 +1844,8 @@ fn backing_works() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); - let mut candidate_b = TestCandidateBuilder { + let candidate_b = TestCandidateBuilder { para_id: chain_b, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(2), @@ -1846,9 +1854,8 @@ fn backing_works() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_b); - let mut candidate_c = TestCandidateBuilder { + let candidate_c = TestCandidateBuilder { para_id: thread_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(3), @@ -1857,7 +1864,6 @@ fn backing_works() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::Two, &mut candidate_c); let backed_a = back_candidate( candidate_a.clone(), @@ -1978,7 +1984,7 @@ fn backing_works() { Vec<(ValidatorIndex, ValidityAttestation)>, )>| { candidate_receipts_with_backers.sort_by(|(cr1, _), (cr2, _)| { - cr1.descriptor().para_id.cmp(&cr2.descriptor().para_id) + cr1.descriptor().para_id().cmp(&cr2.descriptor().para_id()) }); candidate_receipts_with_backers }; @@ -2121,7 +2127,7 @@ fn backing_works_with_elastic_scaling_mvp() { let allowed_relay_parents = default_allowed_relay_parent_tracker(); - let mut candidate_a = TestCandidateBuilder { + let candidate_a = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -2130,9 +2136,8 @@ fn backing_works_with_elastic_scaling_mvp() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); - let mut candidate_b_1 = TestCandidateBuilder { + let candidate_b_1 = TestCandidateBuilder { para_id: chain_b, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(2), @@ -2141,10 +2146,9 @@ fn backing_works_with_elastic_scaling_mvp() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_b_1); // Make candidate b2 a child of b1. - let mut candidate_b_2 = TestCandidateBuilder { + let candidate_b_2 = TestCandidateBuilder { para_id: chain_b, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(3), @@ -2158,7 +2162,6 @@ fn backing_works_with_elastic_scaling_mvp() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_b_2); let backed_a = back_candidate( candidate_a.clone(), @@ -2396,7 +2399,7 @@ fn can_include_candidate_with_ok_code_upgrade() { let allowed_relay_parents = default_allowed_relay_parent_tracker(); let chain_a_assignment = (chain_a, CoreIndex::from(0)); - let mut candidate_a = TestCandidateBuilder { + let candidate_a = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -2406,7 +2409,6 @@ fn can_include_candidate_with_ok_code_upgrade() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); let backed_a = back_candidate( candidate_a.clone(), @@ -2535,18 +2537,21 @@ fn check_allowed_relay_parents() { allowed_relay_parents.update( relay_parent_a.1, Hash::zero(), + Default::default(), relay_parent_a.0, max_ancestry_len, ); allowed_relay_parents.update( relay_parent_b.1, Hash::zero(), + Default::default(), relay_parent_b.0, max_ancestry_len, ); allowed_relay_parents.update( relay_parent_c.1, Hash::zero(), + Default::default(), relay_parent_c.0, max_ancestry_len, ); @@ -2556,7 +2561,7 @@ fn check_allowed_relay_parents() { let chain_b_assignment = (chain_b, CoreIndex::from(1)); let thread_a_assignment = (thread_a, CoreIndex::from(2)); - let mut candidate_a = TestCandidateBuilder { + let candidate_a = TestCandidateBuilder { para_id: chain_a, relay_parent: relay_parent_a.1, pov_hash: Hash::repeat_byte(1), @@ -2569,10 +2574,9 @@ fn check_allowed_relay_parents() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); let signing_context_a = SigningContext { parent_hash: relay_parent_a.1, session_index: 5 }; - let mut candidate_b = TestCandidateBuilder { + let candidate_b = TestCandidateBuilder { para_id: chain_b, relay_parent: relay_parent_b.1, pov_hash: Hash::repeat_byte(2), @@ -2585,10 +2589,9 @@ fn check_allowed_relay_parents() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_b); let signing_context_b = SigningContext { parent_hash: relay_parent_b.1, session_index: 5 }; - let mut candidate_c = TestCandidateBuilder { + let candidate_c = TestCandidateBuilder { para_id: thread_a, relay_parent: relay_parent_c.1, pov_hash: Hash::repeat_byte(3), @@ -2601,7 +2604,6 @@ fn check_allowed_relay_parents() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::Two, &mut candidate_c); let signing_context_c = SigningContext { parent_hash: relay_parent_c.1, session_index: 5 }; let backed_a = back_candidate( @@ -2823,7 +2825,7 @@ fn para_upgrade_delay_scheduled_from_inclusion() { let allowed_relay_parents = default_allowed_relay_parent_tracker(); let chain_a_assignment = (chain_a, CoreIndex::from(0)); - let mut candidate_a = TestCandidateBuilder { + let candidate_a = TestCandidateBuilder { para_id: chain_a, relay_parent: System::parent_hash(), pov_hash: Hash::repeat_byte(1), @@ -2833,7 +2835,6 @@ fn para_upgrade_delay_scheduled_from_inclusion() { ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_a); let backed_a = back_candidate( candidate_a.clone(), diff --git a/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs b/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs index 1742e91276d4b40bdb0b263dc97f87f962ee2592..266860061bed8830311237721deda12b2b0e9356 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/benchmarking.rs @@ -17,7 +17,7 @@ use super::*; use crate::{inclusion, ParaId}; use alloc::collections::btree_map::BTreeMap; -use core::cmp::min; +use core::cmp::{max, min}; use frame_benchmarking::{benchmarks, impl_benchmark_test_suite}; use frame_system::RawOrigin; @@ -107,7 +107,7 @@ benchmarks! { // of a single backed candidate. enter_backed_candidates_variable { let v in (BenchBuilder::::fallback_min_backing_votes()) - ..(BenchBuilder::::fallback_max_validators_per_core()); + .. max(BenchBuilder::::fallback_min_backing_votes() + 1, BenchBuilder::::fallback_max_validators_per_core()); let cores_with_backed: BTreeMap<_, _> = vec![(0, v)] // The backed candidate will have `v` validity votes. @@ -144,8 +144,8 @@ benchmarks! { // Traverse candidates and assert descriptors are as expected for (para_id, backing_validators) in vote.backing_validators_per_candidate.iter().enumerate() { let descriptor = backing_validators.0.descriptor(); - assert_eq!(ParaId::from(para_id), descriptor.para_id); - assert_eq!(header.hash(), descriptor.relay_parent); + assert_eq!(ParaId::from(para_id), descriptor.para_id()); + assert_eq!(header.hash(), descriptor.relay_parent()); assert_eq!(backing_validators.1.len(), votes); } @@ -203,8 +203,8 @@ benchmarks! { for (para_id, backing_validators) in vote.backing_validators_per_candidate.iter().enumerate() { let descriptor = backing_validators.0.descriptor(); - assert_eq!(ParaId::from(para_id), descriptor.para_id); - assert_eq!(header.hash(), descriptor.relay_parent); + assert_eq!(ParaId::from(para_id), descriptor.para_id()); + assert_eq!(header.hash(), descriptor.relay_parent()); assert_eq!( backing_validators.1.len(), votes, diff --git a/polkadot/runtime/parachains/src/paras_inherent/mod.rs b/polkadot/runtime/parachains/src/paras_inherent/mod.rs index bd8d08a842c31ec7db3bba8ad677de79c0f92b97..2aca0f2c728a8bf8b962d116de1b572af0fe42d9 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/mod.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/mod.rs @@ -45,15 +45,21 @@ use frame_support::{ pallet_prelude::*, traits::Randomness, }; + use frame_system::pallet_prelude::*; use pallet_babe::{self, ParentBlockRandomness}; use polkadot_primitives::{ - effective_minimum_backing_votes, node_features::FeatureIndex, BackedCandidate, CandidateHash, - CandidateReceipt, CheckedDisputeStatementSet, CheckedMultiDisputeStatementSet, CoreIndex, - DisputeStatementSet, HeadData, InherentData as ParachainsInherentData, - MultiDisputeStatementSet, ScrapedOnChainVotes, SessionIndex, SignedAvailabilityBitfields, - SigningContext, UncheckedSignedAvailabilityBitfield, UncheckedSignedAvailabilityBitfields, - ValidatorId, ValidatorIndex, ValidityAttestation, PARACHAINS_INHERENT_IDENTIFIER, + effective_minimum_backing_votes, + node_features::FeatureIndex, + vstaging::{ + BackedCandidate, CandidateDescriptorVersion, CandidateReceiptV2 as CandidateReceipt, + InherentData as ParachainsInherentData, ScrapedOnChainVotes, + }, + CandidateHash, CheckedDisputeStatementSet, CheckedMultiDisputeStatementSet, CoreIndex, + DisputeStatementSet, HeadData, MultiDisputeStatementSet, SessionIndex, + SignedAvailabilityBitfields, SigningContext, UncheckedSignedAvailabilityBitfield, + UncheckedSignedAvailabilityBitfields, ValidatorId, ValidatorIndex, ValidityAttestation, + PARACHAINS_INHERENT_IDENTIFIER, }; use rand::{seq::SliceRandom, SeedableRng}; use scale_info::TypeInfo; @@ -327,22 +333,6 @@ impl Pallet { let now = frame_system::Pallet::::block_number(); let config = configuration::ActiveConfig::::get(); - // Before anything else, update the allowed relay-parents. - { - let parent_number = now - One::one(); - let parent_storage_root = *parent_header.state_root(); - - shared::AllowedRelayParents::::mutate(|tracker| { - tracker.update( - parent_hash, - parent_storage_root, - parent_number, - config.async_backing_params.allowed_ancestry_len, - ); - }); - } - let allowed_relay_parents = shared::AllowedRelayParents::::get(); - let candidates_weight = backed_candidates_weight::(&backed_candidates); let bitfields_weight = signed_bitfields_weight::(&bitfields); let disputes_weight = multi_dispute_statement_sets_weight::(&disputes); @@ -588,12 +578,41 @@ impl Pallet { METRICS.on_candidates_processed_total(backed_candidates.len() as u64); + // After freeing cores and filling claims, but before processing backed candidates + // we update the allowed relay-parents. + { + let parent_number = now - One::one(); + let parent_storage_root = *parent_header.state_root(); + + shared::AllowedRelayParents::::mutate(|tracker| { + tracker.update( + parent_hash, + parent_storage_root, + scheduler::ClaimQueue::::get() + .into_iter() + .map(|(core_index, paras)| { + (core_index, paras.into_iter().map(|e| e.para_id()).collect()) + }) + .collect(), + parent_number, + config.async_backing_params.allowed_ancestry_len, + ); + }); + } + let allowed_relay_parents = shared::AllowedRelayParents::::get(); + let core_index_enabled = configuration::ActiveConfig::::get() .node_features .get(FeatureIndex::ElasticScalingMVP as usize) .map(|b| *b) .unwrap_or(false); + let allow_v2_receipts = configuration::ActiveConfig::::get() + .node_features + .get(FeatureIndex::CandidateReceiptV2 as usize) + .map(|b| *b) + .unwrap_or(false); + let mut eligible: BTreeMap> = BTreeMap::new(); let mut total_eligible_cores = 0; @@ -610,6 +629,7 @@ impl Pallet { concluded_invalid_hashes, eligible, core_index_enabled, + allow_v2_receipts, ); let count = count_backed_candidates(&backed_candidates_with_core); @@ -787,7 +807,7 @@ pub(crate) fn apply_weight_limit( let mut current_para_id = None; for candidate in core::mem::take(candidates).into_iter() { - let candidate_para_id = candidate.descriptor().para_id; + let candidate_para_id = candidate.descriptor().para_id(); if Some(candidate_para_id) == current_para_id { let chain = chained_candidates .last_mut() @@ -960,20 +980,101 @@ pub(crate) fn sanitize_bitfields( bitfields } +/// Perform required checks for given candidate receipt. +/// +/// Returns `true` if candidate descriptor is version 1. +/// +/// Otherwise returns `false` if: +/// - version 2 descriptors are not allowed +/// - the core index in descriptor doesn't match the one computed from the commitments +/// - the `SelectCore` signal does not refer to a core at the top of claim queue +fn sanitize_backed_candidate_v2( + candidate: &BackedCandidate, + allowed_relay_parents: &AllowedRelayParentsTracker>, + allow_v2_receipts: bool, +) -> bool { + if candidate.descriptor().version() == CandidateDescriptorVersion::V1 { + return true + } + + // It is mandatory to filter these before calling `filter_unchained_candidates` to ensure + // any v1 descendants of v2 candidates are dropped. + if !allow_v2_receipts { + log::debug!( + target: LOG_TARGET, + "V2 candidate descriptors not allowed. Dropping candidate {:?} for paraid {:?}.", + candidate.candidate().hash(), + candidate.descriptor().para_id() + ); + return false + } + + let Some(session_index) = candidate.descriptor().session_index() else { + log::debug!( + target: LOG_TARGET, + "Invalid V2 candidate receipt {:?} for paraid {:?}, missing session index.", + candidate.candidate().hash(), + candidate.descriptor().para_id(), + ); + return false + }; + + // Check if session index is equal to current session index. + if session_index != shared::CurrentSessionIndex::::get() { + log::debug!( + target: LOG_TARGET, + "Dropping V2 candidate receipt {:?} for paraid {:?}, invalid session index {}, current session {}", + candidate.candidate().hash(), + candidate.descriptor().para_id(), + session_index, + shared::CurrentSessionIndex::::get() + ); + return false + } + + // Get the claim queue snapshot at the candidate relay parent. + let Some((rp_info, _)) = + allowed_relay_parents.acquire_info(candidate.descriptor().relay_parent(), None) + else { + log::debug!( + target: LOG_TARGET, + "Relay parent {:?} for candidate {:?} is not in the allowed relay parents.", + candidate.descriptor().relay_parent(), + candidate.candidate().hash(), + ); + return false + }; + + // Check validity of `core_index`. + if let Err(err) = candidate.candidate().check_core_index(&rp_info.claim_queue) { + log::debug!( + target: LOG_TARGET, + "Dropping candidate {:?} for paraid {:?}, {:?}", + candidate.candidate().hash(), + candidate.descriptor().para_id(), + err, + ); + + return false + } + true +} + /// Performs various filtering on the backed candidates inherent data. /// Must maintain the invariant that the returned candidate collection contains the candidates /// sorted in dependency order for each para. When doing any filtering, we must therefore drop any /// subsequent candidates after the filtered one. /// /// Filter out: -/// 1. any candidates which don't form a chain with the other candidates of the paraid (even if they +/// 1. Candidates that have v2 descriptors if the node `CandidateReceiptV2` feature is not enabled. +/// 2. any candidates which don't form a chain with the other candidates of the paraid (even if they /// do form a chain but are not in the right order). -/// 2. any candidates that have a concluded invalid dispute or who are descendants of a concluded +/// 3. any candidates that have a concluded invalid dispute or who are descendants of a concluded /// invalid candidate. -/// 3. any unscheduled candidates, as well as candidates whose paraid has multiple cores assigned +/// 4. any unscheduled candidates, as well as candidates whose paraid has multiple cores assigned /// but have no injected core index. -/// 4. all backing votes from disabled validators -/// 5. any candidates that end up with less than `effective_minimum_backing_votes` backing votes +/// 5. all backing votes from disabled validators +/// 6. any candidates that end up with less than `effective_minimum_backing_votes` backing votes /// /// Returns the scheduled /// backed candidates which passed filtering, mapped by para id and in the right dependency order. @@ -983,13 +1084,20 @@ fn sanitize_backed_candidates( concluded_invalid_with_descendants: BTreeSet, scheduled: BTreeMap>, core_index_enabled: bool, + allow_v2_receipts: bool, ) -> BTreeMap, CoreIndex)>> { // Map the candidates to the right paraids, while making sure that the order between candidates // of the same para is preserved. let mut candidates_per_para: BTreeMap> = BTreeMap::new(); + for candidate in backed_candidates { + if !sanitize_backed_candidate_v2::(&candidate, allowed_relay_parents, allow_v2_receipts) + { + continue + } + candidates_per_para - .entry(candidate.descriptor().para_id) + .entry(candidate.descriptor().para_id()) .or_default() .push(candidate); } @@ -1008,7 +1116,7 @@ fn sanitize_backed_candidates( target: LOG_TARGET, "Found backed candidate {:?} which was concluded invalid or is a descendant of a concluded invalid candidate, for paraid {:?}.", candidate.candidate().hash(), - candidate.descriptor().para_id + candidate.descriptor().para_id() ); } keep @@ -1182,24 +1290,26 @@ fn filter_backed_statements_from_disabled_validators< // 1. Core index assigned to the parachain which has produced the candidate // 2. The relay chain block number of the candidate retain_candidates::(backed_candidates_with_core, |para_id, (bc, core_idx)| { + // `CoreIndex` not used, we just need a copy to write it back later. 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.acquire_info(bc.descriptor().relay_parent, None) { - Some((_, block_num)) => block_num, - None => { - log::debug!( - target: LOG_TARGET, - "Relay parent {:?} for candidate is not in the allowed relay parents. Dropping the candidate.", - bc.descriptor().relay_parent - ); - return false - }, - }; + let relay_parent_block_number = match allowed_relay_parents + .acquire_info(bc.descriptor().relay_parent(), None) + { + Some((_, block_num)) => block_num, + None => { + log::debug!( + target: LOG_TARGET, + "Relay parent {:?} for candidate is not in the allowed relay parents. Dropping the candidate.", + bc.descriptor().relay_parent() + ); + return false + }, + }; // Get the group index for the core let group_idx = match scheduler::Pallet::::group_assigned_to_core( @@ -1339,8 +1449,8 @@ fn filter_unchained_candidates= 1 && core_index_enabled { - // We must preserve the dependency order given in the input. - let mut temp_backed_candidates = Vec::with_capacity(scheduled_cores.len()); - - for candidate in backed_candidates { - if scheduled_cores.len() == 0 { - // We've got candidates for all of this para's assigned cores. Move on to - // the next para. - log::debug!( - target: LOG_TARGET, - "Found enough candidates for paraid: {:?}.", - candidate.descriptor().para_id - ); - break; - } - let maybe_injected_core_index: Option = - get_injected_core_index::(allowed_relay_parents, &candidate); - - if let Some(core_index) = maybe_injected_core_index { - if scheduled_cores.remove(&core_index) { - temp_backed_candidates.push((candidate, core_index)); - } else { - // if we got a candidate for a core index which is not scheduled, stop - // the work for this para. the already processed candidate chain in - // temp_backed_candidates is still fine though. - log::debug!( - target: LOG_TARGET, - "Found a backed candidate {:?} with injected core index {}, which is not scheduled for paraid {:?}.", - candidate.candidate().hash(), - core_index.0, - candidate.descriptor().para_id - ); - - break; - } - } else { - // if we got a candidate which does not contain its core index, stop the - // work for this para. the already processed candidate chain in - // temp_backed_candidates is still fine though. - - log::debug!( - target: LOG_TARGET, - "Found a backed candidate {:?} with no injected core index, for paraid {:?} which has multiple scheduled cores.", - candidate.candidate().hash(), - candidate.descriptor().para_id - ); - - break; - } - } + if let Some(core_index) = + get_core_index::(core_index_enabled, allowed_relay_parents, &candidate) + { + if scheduled_cores.remove(&core_index) { + temp_backed_candidates.push((candidate, core_index)); + } else { + // if we got a candidate for a core index which is not scheduled, stop + // the work for this para. the already processed candidate chain in + // temp_backed_candidates is still fine though. + log::debug!( + target: LOG_TARGET, + "Found a backed candidate {:?} with core index {}, which is not scheduled for paraid {:?}.", + candidate.candidate().hash(), + core_index.0, + candidate.descriptor().para_id() + ); - if !temp_backed_candidates.is_empty() { - backed_candidates_with_core - .entry(para_id) - .or_insert_with(|| vec![]) - .extend(temp_backed_candidates); + break; } } else { - log::warn!( + // No core index is fine, if para has just 1 core assigned. + if scheduled_cores.len() == 1 { + temp_backed_candidates + .push((candidate, scheduled_cores.pop_first().expect("Length is 1"))); + break; + } + + // if we got a candidate which does not contain its core index, stop the + // work for this para. the already processed candidate chain in + // temp_backed_candidates is still fine though. + + log::debug!( target: LOG_TARGET, - "Found a paraid {:?} which has multiple scheduled cores but ElasticScalingMVP feature is not enabled: {:?}", - para_id, - scheduled_cores + "Found a backed candidate {:?} without core index information, but paraid {:?} has multiple scheduled cores.", + candidate.candidate().hash(), + candidate.descriptor().para_id() ); + + break; } - } else { - log::debug!( - target: LOG_TARGET, - "Paraid: {:?} has no entry in scheduled cores but {} candidates were supplied.", - para_id, - backed_candidates.len() - ); + } + + if !temp_backed_candidates.is_empty() { + backed_candidates_with_core + .entry(para_id) + .or_insert_with(|| vec![]) + .extend(temp_backed_candidates); } } backed_candidates_with_core } +// Must be called only for candidates that have been sanitized already. +fn get_core_index( + core_index_enabled: bool, + allowed_relay_parents: &AllowedRelayParentsTracker>, + candidate: &BackedCandidate, +) -> Option { + candidate.candidate().descriptor.core_index().or_else(|| { + get_injected_core_index::(core_index_enabled, allowed_relay_parents, &candidate) + }) +} + fn get_injected_core_index( + core_index_enabled: bool, allowed_relay_parents: &AllowedRelayParentsTracker>, candidate: &BackedCandidate, ) -> 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(true); + 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) { + 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.", - candidate.descriptor().relay_parent, + candidate.descriptor().relay_parent(), candidate.candidate().hash(), ); return None diff --git a/polkadot/runtime/parachains/src/paras_inherent/tests.rs b/polkadot/runtime/parachains/src/paras_inherent/tests.rs index e34055bfa9f25ce480c78a04e7e22f387db742c1..f5c3d507776439d0b69e2b694c8eaad9b6d85933 100644 --- a/polkadot/runtime/parachains/src/paras_inherent/tests.rs +++ b/polkadot/runtime/parachains/src/paras_inherent/tests.rs @@ -44,8 +44,11 @@ fn default_config() -> MockGenesisConfig { #[cfg(not(feature = "runtime-benchmarks"))] mod enter { use super::{inclusion::tests::TestCandidateBuilder, *}; + use polkadot_primitives::vstaging::{ClaimQueueOffset, CoreSelector, UMPSignal, UMP_SEPARATOR}; + use rstest::rstest; + use crate::{ - builder::{Bench, BenchBuilder}, + builder::{junk_collator, junk_collator_signature, Bench, BenchBuilder, CandidateModifier}, mock::{mock_assigner, new_test_ext, BlockLength, BlockWeights, RuntimeOrigin, Test}, scheduler::{ common::{Assignment, AssignmentProvider}, @@ -58,7 +61,10 @@ mod enter { use core::panic; use frame_support::assert_ok; use frame_system::limits; - use polkadot_primitives::{AvailabilityBitfield, SchedulerParams, UncheckedSigned}; + use polkadot_primitives::{ + vstaging::{CandidateDescriptorV2, CommittedCandidateReceiptV2, InternalVersion}, + AvailabilityBitfield, CandidateDescriptor, UncheckedSigned, + }; use sp_runtime::Perbill; struct TestConfig { @@ -70,6 +76,8 @@ mod enter { fill_claimqueue: bool, elastic_paras: BTreeMap, unavailable_cores: Vec, + v2_descriptor: bool, + candidate_modifier: Option::Hash>>, } fn make_inherent_data( @@ -82,6 +90,8 @@ mod enter { fill_claimqueue, elastic_paras, unavailable_cores, + v2_descriptor, + candidate_modifier, }: TestConfig, ) -> Bench { let extra_cores = elastic_paras @@ -99,7 +109,9 @@ mod enter { .set_backed_and_concluding_paras(backed_and_concluding.clone()) .set_dispute_sessions(&dispute_sessions[..]) .set_fill_claimqueue(fill_claimqueue) - .set_unavailable_cores(unavailable_cores); + .set_unavailable_cores(unavailable_cores) + .set_candidate_descriptor_v2(v2_descriptor) + .set_candidate_modifier(candidate_modifier); // Setup some assignments as needed: mock_assigner::Pallet::::set_core_count(builder.max_cores()); @@ -121,15 +133,25 @@ mod enter { } } - #[test] + #[rstest] + #[case(true)] + #[case(false)] // Validate that if we create 2 backed candidates which are assigned to 2 cores that will be // freed via becoming fully available, the backed candidates will not be filtered out in // `create_inherent` and will not cause `enter` to early. - fn include_backed_candidates() { + fn include_backed_candidates(#[case] v2_descriptor: bool) { let config = MockGenesisConfig::default(); assert!(config.configuration.config.scheduler_params.lookahead > 0); new_test_ext(config).execute_with(|| { + // Enable the v2 receipts. + configuration::Pallet::::set_node_feature( + RuntimeOrigin::root(), + FeatureIndex::CandidateReceiptV2 as u8, + v2_descriptor, + ) + .unwrap(); + let dispute_statements = BTreeMap::new(); let mut backed_and_concluding = BTreeMap::new(); @@ -142,9 +164,11 @@ mod enter { backed_and_concluding, num_validators_per_core: 1, code_upgrade: None, - fill_claimqueue: false, + fill_claimqueue: true, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor, + candidate_modifier: None, }); // We expect the scenario to have cores 0 & 1 with pending availability. The backed @@ -165,9 +189,6 @@ mod enter { .put_data(PARACHAINS_INHERENT_IDENTIFIER, &expected_para_inherent_data) .unwrap(); - // The current schedule is empty prior to calling `create_inherent_enter`. - assert!(scheduler::Pallet::::claim_queue_is_empty()); - // Nothing is filtered out (including the backed candidates.) assert_eq!( Pallet::::create_inherent_inner(&inherent_data.clone()).unwrap(), @@ -206,8 +227,14 @@ mod enter { }); } - #[test] - fn include_backed_candidates_elastic_scaling() { + #[rstest] + #[case(true, false)] + #[case(true, true)] + #[case(false, true)] + fn include_backed_candidates_elastic_scaling( + #[case] v2_descriptor: bool, + #[case] injected_core: bool, + ) { // ParaId 0 has one pending candidate on core 0. // ParaId 1 has one pending candidate on core 1. // ParaId 2 has three pending candidates on cores 2, 3 and 4. @@ -220,7 +247,15 @@ mod enter { configuration::Pallet::::set_node_feature( RuntimeOrigin::root(), FeatureIndex::ElasticScalingMVP as u8, - true, + injected_core, + ) + .unwrap(); + + // Enable the v2 receipts. + configuration::Pallet::::set_node_feature( + RuntimeOrigin::root(), + FeatureIndex::CandidateReceiptV2 as u8, + v2_descriptor, ) .unwrap(); @@ -237,9 +272,11 @@ mod enter { backed_and_concluding, num_validators_per_core: 1, code_upgrade: None, - fill_claimqueue: false, + fill_claimqueue: true, elastic_paras: [(2, 3)].into_iter().collect(), unavailable_cores: vec![], + v2_descriptor, + candidate_modifier: None, }); let expected_para_inherent_data = scenario.data.clone(); @@ -256,9 +293,6 @@ mod enter { .put_data(PARACHAINS_INHERENT_IDENTIFIER, &expected_para_inherent_data) .unwrap(); - // The current schedule is empty prior to calling `create_inherent_enter`. - assert!(scheduler::Pallet::::claim_queue_is_empty()); - assert!(pallet::OnChainVotes::::get().is_none()); // Nothing is filtered out (including the backed candidates.) @@ -344,6 +378,8 @@ mod enter { fill_claimqueue: true, elastic_paras: [(2, 4)].into_iter().collect(), unavailable_cores: unavailable_cores.clone(), + v2_descriptor: false, + candidate_modifier: None, }); let mut expected_para_inherent_data = scenario.data.clone(); @@ -600,6 +636,8 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, + candidate_modifier: None, }); let expected_para_inherent_data = scenario.data.clone(); @@ -673,6 +711,8 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, + candidate_modifier: None, }); let expected_para_inherent_data = scenario.data.clone(); @@ -744,6 +784,8 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, + candidate_modifier: None, }); let expected_para_inherent_data = scenario.data.clone(); @@ -831,6 +873,8 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, + candidate_modifier: None, }); let expected_para_inherent_data = scenario.data.clone(); @@ -918,6 +962,8 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, + candidate_modifier: None, }); let expected_para_inherent_data = scenario.data.clone(); @@ -977,6 +1023,8 @@ mod enter { fill_claimqueue: true, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, + candidate_modifier: None, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1063,6 +1111,8 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, + candidate_modifier: None, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1170,6 +1220,8 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, + candidate_modifier: None, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1238,6 +1290,8 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, + candidate_modifier: None, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1304,6 +1358,8 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, + candidate_modifier: None, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1370,6 +1426,15 @@ mod enter { ccr.commitments.processed_downward_messages = idx as u32; let core_index = start_core_index + idx; + // `UMPSignal` separator. + ccr.commitments.upward_messages.force_push(UMP_SEPARATOR); + + // `SelectCore` commitment. + // Claim queue offset must be `0`` so this candidate is for the very next block. + ccr.commitments.upward_messages.force_push( + UMPSignal::SelectCore(CoreSelector(idx as u8), ClaimQueueOffset(0)).encode(), + ); + BackedCandidate::new( ccr.into(), Default::default(), @@ -1382,8 +1447,10 @@ mod enter { // 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() { + #[rstest] + #[case(true)] + #[case(false)] + fn test_backed_candidates_apply_weight_works_for_elastic_scaling(#[case] v2_descriptor: bool) { 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, @@ -1394,6 +1461,14 @@ mod enter { // Create an overweight inherent and oversized block let mut backed_and_concluding = BTreeMap::new(); + // Enable the v2 receipts. + configuration::Pallet::::set_node_feature( + RuntimeOrigin::root(), + FeatureIndex::CandidateReceiptV2 as u8, + v2_descriptor, + ) + .unwrap(); + for i in 0..30 { backed_and_concluding.insert(i, i); } @@ -1407,6 +1482,8 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor, + candidate_modifier: None, }); let mut para_inherent_data = scenario.data.clone(); @@ -1440,7 +1517,7 @@ mod enter { // The chained candidates are not picked, instead a single other candidate is picked assert_eq!(backed_candidates.len(), 1); - assert_ne!(backed_candidates[0].descriptor().para_id, ParaId::from(1000)); + assert_ne!(backed_candidates[0].descriptor().para_id(), ParaId::from(1000)); // All bitfields are kept. assert_eq!(bitfields.len(), 150); @@ -1461,9 +1538,9 @@ mod enter { // Only the chained candidates should pass filter. assert_eq!(backed_candidates.len(), 3); // Check the actual candidates - assert_eq!(backed_candidates[0].descriptor().para_id, ParaId::from(1000)); - assert_eq!(backed_candidates[1].descriptor().para_id, ParaId::from(1000)); - assert_eq!(backed_candidates[2].descriptor().para_id, ParaId::from(1000)); + assert_eq!(backed_candidates[0].descriptor().para_id(), ParaId::from(1000)); + assert_eq!(backed_candidates[1].descriptor().para_id(), ParaId::from(1000)); + assert_eq!(backed_candidates[2].descriptor().para_id(), ParaId::from(1000)); // All bitfields are kept. assert_eq!(bitfields.len(), 150); @@ -1496,6 +1573,8 @@ mod enter { fill_claimqueue: false, elastic_paras: BTreeMap::new(), unavailable_cores: vec![], + v2_descriptor: false, + candidate_modifier: None, }); let expected_para_inherent_data = scenario.data.clone(); @@ -1524,6 +1603,502 @@ mod enter { assert_eq!(dispatch_error, Error::::InherentOverweight.into()); }); } + + #[test] + fn v2_descriptors_are_filtered() { + let config = default_config(); + assert!(config.configuration.config.scheduler_params.lookahead > 0); + new_test_ext(config).execute_with(|| { + // Set the elastic scaling MVP feature. + configuration::Pallet::::set_node_feature( + RuntimeOrigin::root(), + FeatureIndex::ElasticScalingMVP as u8, + true, + ) + .unwrap(); + + let mut backed_and_concluding = BTreeMap::new(); + backed_and_concluding.insert(0, 1); + backed_and_concluding.insert(1, 1); + backed_and_concluding.insert(2, 1); + + let unavailable_cores = vec![]; + + let scenario = make_inherent_data(TestConfig { + dispute_statements: BTreeMap::new(), + dispute_sessions: vec![], // No disputes + backed_and_concluding, + num_validators_per_core: 5, + code_upgrade: None, + fill_claimqueue: true, + elastic_paras: [(2, 8)].into_iter().collect(), + unavailable_cores: unavailable_cores.clone(), + v2_descriptor: true, + candidate_modifier: None, + }); + + let mut unfiltered_para_inherent_data = scenario.data.clone(); + + // Check the para inherent data is as expected: + // * 1 bitfield per validator (5 validators per core, 10 backed candidates) + assert_eq!(unfiltered_para_inherent_data.bitfields.len(), 50); + // * 10 v2 candidate descriptors. + assert_eq!(unfiltered_para_inherent_data.backed_candidates.len(), 10); + + // Make the last candidate look like v1, by using an unknown version. + unfiltered_para_inherent_data.backed_candidates[9] + .descriptor_mut() + .set_version(InternalVersion(123)); + + let mut inherent_data = InherentData::new(); + inherent_data + .put_data(PARACHAINS_INHERENT_IDENTIFIER, &unfiltered_para_inherent_data) + .unwrap(); + + // We expect all backed candidates to be filtered out. + let filtered_para_inherend_data = + Pallet::::create_inherent_inner(&inherent_data).unwrap(); + + assert_eq!(filtered_para_inherend_data.backed_candidates.len(), 0); + + let dispatch_error = Pallet::::enter( + frame_system::RawOrigin::None.into(), + unfiltered_para_inherent_data, + ) + .unwrap_err() + .error; + + // We expect `enter` to fail because the inherent data contains backed candidates with + // v2 descriptors. + assert_eq!(dispatch_error, Error::::CandidatesFilteredDuringExecution.into()); + }); + } + + #[test] + fn too_many_ump_signals() { + let config = default_config(); + assert!(config.configuration.config.scheduler_params.lookahead > 0); + new_test_ext(config).execute_with(|| { + // Set the elastic scaling MVP feature. + configuration::Pallet::::set_node_feature( + RuntimeOrigin::root(), + FeatureIndex::CandidateReceiptV2 as u8, + true, + ) + .unwrap(); + + let mut backed_and_concluding = BTreeMap::new(); + backed_and_concluding.insert(0, 1); + backed_and_concluding.insert(1, 1); + backed_and_concluding.insert(2, 1); + + let unavailable_cores = vec![]; + + let scenario = make_inherent_data(TestConfig { + dispute_statements: BTreeMap::new(), + dispute_sessions: vec![], // No disputes + backed_and_concluding, + num_validators_per_core: 5, + code_upgrade: None, + fill_claimqueue: true, + elastic_paras: [(2, 8)].into_iter().collect(), + unavailable_cores: unavailable_cores.clone(), + v2_descriptor: true, + candidate_modifier: Some(|mut candidate: CommittedCandidateReceiptV2| { + if candidate.descriptor.para_id() == 2.into() { + // Add an extra message so `verify_backed_candidates` fails. + candidate.commitments.upward_messages.force_push( + UMPSignal::SelectCore(CoreSelector(123 as u8), ClaimQueueOffset(2)) + .encode(), + ); + } + candidate + }), + }); + + let unfiltered_para_inherent_data = scenario.data.clone(); + + // Check the para inherent data is as expected: + // * 1 bitfield per validator (5 validators per core, 10 backed candidates) + assert_eq!(unfiltered_para_inherent_data.bitfields.len(), 50); + // * 10 v2 candidate descriptors. + assert_eq!(unfiltered_para_inherent_data.backed_candidates.len(), 10); + + let mut inherent_data = InherentData::new(); + inherent_data + .put_data(PARACHAINS_INHERENT_IDENTIFIER, &unfiltered_para_inherent_data) + .unwrap(); + + let dispatch_error = Pallet::::enter( + frame_system::RawOrigin::None.into(), + unfiltered_para_inherent_data, + ) + .unwrap_err() + .error; + + // We expect `enter` to fail because the inherent data contains backed candidates with + // v2 descriptors. + assert_eq!(dispatch_error, Error::::CandidatesFilteredDuringExecution.into()); + }); + } + + #[test] + fn invalid_ump_signals() { + let config = default_config(); + assert!(config.configuration.config.scheduler_params.lookahead > 0); + new_test_ext(config).execute_with(|| { + // Set the elastic scaling MVP feature. + configuration::Pallet::::set_node_feature( + RuntimeOrigin::root(), + FeatureIndex::CandidateReceiptV2 as u8, + true, + ) + .unwrap(); + + let mut backed_and_concluding = BTreeMap::new(); + backed_and_concluding.insert(0, 1); + backed_and_concluding.insert(1, 1); + backed_and_concluding.insert(2, 1); + + let unavailable_cores = vec![]; + + let scenario = make_inherent_data(TestConfig { + dispute_statements: BTreeMap::new(), + dispute_sessions: vec![], // No disputes + backed_and_concluding, + num_validators_per_core: 5, + code_upgrade: None, + fill_claimqueue: true, + elastic_paras: [(2, 8)].into_iter().collect(), + unavailable_cores: unavailable_cores.clone(), + v2_descriptor: true, + candidate_modifier: Some(|mut candidate: CommittedCandidateReceiptV2| { + if candidate.descriptor.para_id() == 1.into() { + // Drop the core selector to make it invalid + candidate + .commitments + .upward_messages + .truncate(candidate.commitments.upward_messages.len() - 1); + } + candidate + }), + }); + + let unfiltered_para_inherent_data = scenario.data.clone(); + + // Check the para inherent data is as expected: + // * 1 bitfield per validator (5 validators per core, 10 backed candidates) + assert_eq!(unfiltered_para_inherent_data.bitfields.len(), 50); + // * 10 v2 candidate descriptors. + assert_eq!(unfiltered_para_inherent_data.backed_candidates.len(), 10); + + let mut inherent_data = InherentData::new(); + inherent_data + .put_data(PARACHAINS_INHERENT_IDENTIFIER, &unfiltered_para_inherent_data) + .unwrap(); + + let dispatch_error = Pallet::::enter( + frame_system::RawOrigin::None.into(), + unfiltered_para_inherent_data, + ) + .unwrap_err() + .error; + + // We expect `enter` to fail because the inherent data contains backed candidates with + // v2 descriptors. + assert_eq!(dispatch_error, Error::::CandidatesFilteredDuringExecution.into()); + }); + } + #[test] + fn v2_descriptors_are_accepted() { + let config = default_config(); + assert!(config.configuration.config.scheduler_params.lookahead > 0); + new_test_ext(config).execute_with(|| { + // Set the elastic scaling MVP feature. + configuration::Pallet::::set_node_feature( + RuntimeOrigin::root(), + FeatureIndex::ElasticScalingMVP as u8, + true, + ) + .unwrap(); + + // Enable the v2 receipts. + configuration::Pallet::::set_node_feature( + RuntimeOrigin::root(), + FeatureIndex::CandidateReceiptV2 as u8, + true, + ) + .unwrap(); + + let mut backed_and_concluding = BTreeMap::new(); + backed_and_concluding.insert(0, 1); + backed_and_concluding.insert(1, 1); + backed_and_concluding.insert(2, 1); + + let unavailable_cores = vec![]; + + let scenario = make_inherent_data(TestConfig { + dispute_statements: BTreeMap::new(), + dispute_sessions: vec![], // No disputes + backed_and_concluding, + num_validators_per_core: 1, + code_upgrade: None, + fill_claimqueue: true, + elastic_paras: [(2, 3)].into_iter().collect(), + unavailable_cores: unavailable_cores.clone(), + v2_descriptor: true, + candidate_modifier: None, + }); + + let inherent_data = scenario.data.clone(); + + // Check the para inherent data is as expected: + // * 1 bitfield per validator (2 validators per core, 5 backed candidates) + assert_eq!(inherent_data.bitfields.len(), 5); + // * 5 v2 candidate descriptors. + assert_eq!(inherent_data.backed_candidates.len(), 5); + + Pallet::::enter(frame_system::RawOrigin::None.into(), inherent_data).unwrap(); + }); + } + + // Test when parachain runtime is upgraded to support the new commitments + // but some collators are not and provide v1 descriptors. + #[test] + fn elastic_scaling_mixed_v1_v2_descriptors() { + let config = default_config(); + assert!(config.configuration.config.scheduler_params.lookahead > 0); + new_test_ext(config).execute_with(|| { + // Set the elastic scaling MVP feature. + configuration::Pallet::::set_node_feature( + RuntimeOrigin::root(), + FeatureIndex::ElasticScalingMVP as u8, + true, + ) + .unwrap(); + + // Enable the v2 receipts. + configuration::Pallet::::set_node_feature( + RuntimeOrigin::root(), + FeatureIndex::CandidateReceiptV2 as u8, + true, + ) + .unwrap(); + + let mut backed_and_concluding = BTreeMap::new(); + backed_and_concluding.insert(0, 1); + backed_and_concluding.insert(1, 1); + backed_and_concluding.insert(2, 1); + + let unavailable_cores = vec![]; + + let scenario = make_inherent_data(TestConfig { + dispute_statements: BTreeMap::new(), + dispute_sessions: vec![], // No disputes + backed_and_concluding, + num_validators_per_core: 1, + code_upgrade: None, + fill_claimqueue: true, + elastic_paras: [(2, 3)].into_iter().collect(), + unavailable_cores: unavailable_cores.clone(), + v2_descriptor: true, + candidate_modifier: None, + }); + + let mut inherent_data = scenario.data.clone(); + let candidate_count = inherent_data.backed_candidates.len(); + + // Make last 2 candidates v1 + for index in candidate_count - 2..candidate_count { + let encoded = inherent_data.backed_candidates[index].descriptor().encode(); + + let mut decoded: CandidateDescriptor = + Decode::decode(&mut encoded.as_slice()).unwrap(); + decoded.collator = junk_collator(); + decoded.signature = junk_collator_signature(); + + *inherent_data.backed_candidates[index].descriptor_mut() = + Decode::decode(&mut encoded.as_slice()).unwrap(); + } + + // Check the para inherent data is as expected: + // * 1 bitfield per validator (2 validators per core, 5 backed candidates) + assert_eq!(inherent_data.bitfields.len(), 5); + // * 5 v2 candidate descriptors. + assert_eq!(inherent_data.backed_candidates.len(), 5); + + Pallet::::enter(frame_system::RawOrigin::None.into(), inherent_data).unwrap(); + }); + } + + // Mixed test with v1, v2 with/without `UMPSignal::SelectCore` + #[test] + fn mixed_v1_and_v2_optional_commitments() { + let config = default_config(); + assert!(config.configuration.config.scheduler_params.lookahead > 0); + new_test_ext(config).execute_with(|| { + // Set the elastic scaling MVP feature. + configuration::Pallet::::set_node_feature( + RuntimeOrigin::root(), + FeatureIndex::ElasticScalingMVP as u8, + true, + ) + .unwrap(); + + // Enable the v2 receipts. + configuration::Pallet::::set_node_feature( + RuntimeOrigin::root(), + FeatureIndex::CandidateReceiptV2 as u8, + true, + ) + .unwrap(); + + let mut backed_and_concluding = BTreeMap::new(); + backed_and_concluding.insert(0, 1); + backed_and_concluding.insert(1, 1); + backed_and_concluding.insert(2, 1); + backed_and_concluding.insert(3, 1); + backed_and_concluding.insert(4, 1); + + let unavailable_cores = vec![]; + + let candidate_modifier = |mut candidate: CommittedCandidateReceiptV2| { + // first candidate has v2 descriptor with no commitments + if candidate.descriptor.para_id() == ParaId::from(0) { + candidate.commitments.upward_messages.clear(); + } + + if candidate.descriptor.para_id() > ParaId::from(2) { + let mut v1: CandidateDescriptor = candidate.descriptor.into(); + + v1.collator = junk_collator(); + v1.signature = junk_collator_signature(); + + candidate.descriptor = v1.into(); + } + candidate + }; + + let scenario = make_inherent_data(TestConfig { + dispute_statements: BTreeMap::new(), + dispute_sessions: vec![], // No disputes + backed_and_concluding, + num_validators_per_core: 1, + code_upgrade: None, + fill_claimqueue: true, + elastic_paras: Default::default(), + unavailable_cores: unavailable_cores.clone(), + v2_descriptor: true, + candidate_modifier: Some(candidate_modifier), + }); + + let inherent_data = scenario.data.clone(); + + // Check the para inherent data is as expected: + // * 1 bitfield per validator (2 validators per core, 5 backed candidates) + assert_eq!(inherent_data.bitfields.len(), 5); + // * 5 v2 candidate descriptors. + assert_eq!(inherent_data.backed_candidates.len(), 5); + + Pallet::::enter(frame_system::RawOrigin::None.into(), inherent_data).unwrap(); + }); + } + + // A test to ensure that the `paras_inherent` filters out candidates with invalid + // session index in the descriptor. + #[test] + fn invalid_session_index() { + let config = default_config(); + assert!(config.configuration.config.scheduler_params.lookahead > 0); + new_test_ext(config).execute_with(|| { + // Set the elastic scaling MVP feature. + configuration::Pallet::::set_node_feature( + RuntimeOrigin::root(), + FeatureIndex::ElasticScalingMVP as u8, + true, + ) + .unwrap(); + + // Enable the v2 receipts. + configuration::Pallet::::set_node_feature( + RuntimeOrigin::root(), + FeatureIndex::CandidateReceiptV2 as u8, + true, + ) + .unwrap(); + + let mut backed_and_concluding = BTreeMap::new(); + backed_and_concluding.insert(0, 1); + backed_and_concluding.insert(1, 1); + backed_and_concluding.insert(2, 1); + + let unavailable_cores = vec![]; + + let scenario = make_inherent_data(TestConfig { + dispute_statements: BTreeMap::new(), + dispute_sessions: vec![], // No disputes + backed_and_concluding, + num_validators_per_core: 1, + code_upgrade: None, + fill_claimqueue: true, + elastic_paras: [(2, 3)].into_iter().collect(), + unavailable_cores, + v2_descriptor: true, + candidate_modifier: None, + }); + + let mut inherent_data = scenario.data.clone(); + + // Check the para inherent data is as expected: + // * 1 bitfield per validator (2 validators per core, 5 backed candidates) + assert_eq!(inherent_data.bitfields.len(), 5); + // * 5 v2 candidate descriptors passed, 1 is invalid + assert_eq!(inherent_data.backed_candidates.len(), 5); + + let index = inherent_data.backed_candidates.len() - 1; + + // Put invalid session index in last candidate + let backed_candidate = inherent_data.backed_candidates[index].clone(); + + let candidate = CommittedCandidateReceiptV2 { + descriptor: CandidateDescriptorV2::new( + backed_candidate.descriptor().para_id(), + backed_candidate.descriptor().relay_parent(), + backed_candidate.descriptor().core_index().unwrap(), + 100, + backed_candidate.descriptor().persisted_validation_data_hash(), + backed_candidate.descriptor().pov_hash(), + backed_candidate.descriptor().erasure_root(), + backed_candidate.descriptor().para_head(), + backed_candidate.descriptor().validation_code_hash(), + ), + commitments: backed_candidate.candidate().commitments.clone(), + }; + + inherent_data.backed_candidates[index] = BackedCandidate::new( + candidate, + backed_candidate.validity_votes().to_vec(), + backed_candidate.validator_indices_and_core_index(false).0.into(), + None, + ); + + let mut expected_inherent_data = inherent_data.clone(); + expected_inherent_data.backed_candidates.truncate(index); + + let mut create_inherent_data = InherentData::new(); + create_inherent_data + .put_data(PARACHAINS_INHERENT_IDENTIFIER, &inherent_data) + .unwrap(); + + // 1 candidate with invalid session is filtered out + assert_eq!( + Pallet::::create_inherent_inner(&create_inherent_data).unwrap(), + expected_inherent_data + ); + + Pallet::::enter(frame_system::RawOrigin::None.into(), inherent_data).unwrap_err(); + }); + } } fn default_header() -> polkadot_primitives::Header { @@ -1540,9 +2115,7 @@ mod sanitizers { use super::*; use crate::{ - inclusion::tests::{ - back_candidate, collator_sign_candidate, BackingKind, TestCandidateBuilder, - }, + inclusion::tests::{back_candidate, BackingKind, TestCandidateBuilder}, mock::new_test_ext, }; use bitvec::order::Lsb0; @@ -1556,7 +2129,6 @@ mod sanitizers { use crate::mock::Test; use polkadot_primitives::PARACHAIN_KEY_TYPE_ID; use sc_keystore::LocalKeystore; - use sp_keyring::Sr25519Keyring; use sp_keystore::{Keystore, KeystorePtr}; use std::sync::Arc; @@ -1826,6 +2398,7 @@ mod sanitizers { shared::Pallet::::add_allowed_relay_parent( default_header().hash(), Default::default(), + Default::default(), RELAY_PARENT_NUM, 1, ); @@ -1940,7 +2513,7 @@ mod sanitizers { .into_iter() .map(|idx0| { let idx1 = idx0 + 1; - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(idx1), relay_parent, pov_hash: Hash::repeat_byte(idx1 as u8), @@ -1957,8 +2530,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -1991,7 +2562,7 @@ mod sanitizers { let mut expected_backed_candidates_with_core = BTreeMap::new(); for candidate in backed_candidates.iter() { - let para_id = candidate.descriptor().para_id; + let para_id = candidate.descriptor().para_id(); expected_backed_candidates_with_core.entry(para_id).or_insert(vec![]).push(( candidate.clone(), @@ -2016,18 +2587,12 @@ mod sanitizers { // Para 6 is not scheduled. One candidate supplied. // Para 7 is scheduled on core 7 and 8, but the candidate contains the wrong core index. // Para 8 is scheduled on core 9, but the candidate contains the wrong core index. - fn get_test_data_multiple_cores_per_para(core_index_enabled: bool) -> TestData { + fn get_test_data_multiple_cores_per_para( + core_index_enabled: bool, + v2_descriptor: 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); @@ -2146,6 +2711,21 @@ mod sanitizers { ), ])); + // 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( + relay_parent, + Default::default(), + scheduler::ClaimQueue::::get() + .into_iter() + .map(|(core_index, paras)| { + (core_index, paras.into_iter().map(|e| e.para_id()).collect()) + }) + .collect(), + RELAY_PARENT_NUM, + 1, + ); + // Set the on-chain included head data and current code hash. for id in 1..=8u32 { paras::Pallet::::set_current_head(ParaId::from(id), HeadData(vec![id as u8])); @@ -2175,9 +2755,17 @@ mod sanitizers { let mut backed_candidates = vec![]; let mut expected_backed_candidates_with_core = BTreeMap::new(); + let maybe_core_index = |core_index: CoreIndex| -> Option { + if !v2_descriptor { + None + } else { + Some(core_index) + } + }; + // Para 1 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(1), relay_parent, pov_hash: Hash::repeat_byte(1 as u8), @@ -2191,12 +2779,11 @@ mod sanitizers { hrmp_watermark: RELAY_PARENT_NUM, head_data: HeadData(vec![1, 1]), validation_code: ValidationCode(vec![1]), + core_index: maybe_core_index(CoreIndex(0)), ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed: BackedCandidate = back_candidate( candidate, @@ -2208,14 +2795,14 @@ mod sanitizers { core_index_enabled.then_some(CoreIndex(0 as u32)), ); backed_candidates.push(backed.clone()); - if core_index_enabled { + if core_index_enabled || v2_descriptor { expected_backed_candidates_with_core .entry(ParaId::from(1)) .or_insert(vec![]) .push((backed, CoreIndex(0))); } - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(1), relay_parent, pov_hash: Hash::repeat_byte(2 as u8), @@ -2229,12 +2816,12 @@ mod sanitizers { .hash(), hrmp_watermark: RELAY_PARENT_NUM, validation_code: ValidationCode(vec![1]), + core_index: maybe_core_index(CoreIndex(1)), + core_selector: Some(1), ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2245,7 +2832,7 @@ mod sanitizers { core_index_enabled.then_some(CoreIndex(1 as u32)), ); backed_candidates.push(backed.clone()); - if core_index_enabled { + if core_index_enabled || v2_descriptor { expected_backed_candidates_with_core .entry(ParaId::from(1)) .or_insert(vec![]) @@ -2255,7 +2842,7 @@ mod sanitizers { // Para 2 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(2), relay_parent, pov_hash: Hash::repeat_byte(3 as u8), @@ -2268,12 +2855,11 @@ mod sanitizers { .hash(), hrmp_watermark: RELAY_PARENT_NUM, validation_code: ValidationCode(vec![2]), + core_index: maybe_core_index(CoreIndex(2)), ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2284,7 +2870,7 @@ mod sanitizers { core_index_enabled.then_some(CoreIndex(2 as u32)), ); backed_candidates.push(backed.clone()); - if core_index_enabled { + if core_index_enabled || v2_descriptor { expected_backed_candidates_with_core .entry(ParaId::from(2)) .or_insert(vec![]) @@ -2294,7 +2880,7 @@ mod sanitizers { // Para 3 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(3), relay_parent, pov_hash: Hash::repeat_byte(4 as u8), @@ -2307,12 +2893,11 @@ mod sanitizers { .hash(), hrmp_watermark: RELAY_PARENT_NUM, validation_code: ValidationCode(vec![3]), + core_index: maybe_core_index(CoreIndex(4)), ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2331,7 +2916,7 @@ mod sanitizers { // Para 4 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(4), relay_parent, pov_hash: Hash::repeat_byte(5 as u8), @@ -2344,12 +2929,11 @@ mod sanitizers { .hash(), hrmp_watermark: RELAY_PARENT_NUM, validation_code: ValidationCode(vec![4]), + core_index: maybe_core_index(CoreIndex(5)), ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed = back_candidate( candidate, @@ -2366,7 +2950,7 @@ mod sanitizers { .or_insert(vec![]) .push((backed, CoreIndex(5))); - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(4), relay_parent, pov_hash: Hash::repeat_byte(6 as u8), @@ -2380,12 +2964,11 @@ mod sanitizers { .hash(), hrmp_watermark: RELAY_PARENT_NUM, validation_code: ValidationCode(vec![4]), + core_index: maybe_core_index(CoreIndex(5)), ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2402,7 +2985,7 @@ mod sanitizers { // Para 6. { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(6), relay_parent, pov_hash: Hash::repeat_byte(3 as u8), @@ -2415,12 +2998,11 @@ mod sanitizers { .hash(), hrmp_watermark: RELAY_PARENT_NUM, validation_code: ValidationCode(vec![6]), + core_index: maybe_core_index(CoreIndex(6)), ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2435,7 +3017,7 @@ mod sanitizers { // Para 7. { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(7), relay_parent, pov_hash: Hash::repeat_byte(3 as u8), @@ -2448,12 +3030,11 @@ mod sanitizers { .hash(), hrmp_watermark: RELAY_PARENT_NUM, validation_code: ValidationCode(vec![7]), + core_index: maybe_core_index(CoreIndex(6)), ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2468,7 +3049,7 @@ mod sanitizers { // Para 8. { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(8), relay_parent, pov_hash: Hash::repeat_byte(3 as u8), @@ -2481,12 +3062,11 @@ mod sanitizers { .hash(), hrmp_watermark: RELAY_PARENT_NUM, validation_code: ValidationCode(vec![8]), + core_index: maybe_core_index(CoreIndex(7)), ..Default::default() } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2497,7 +3077,7 @@ mod sanitizers { core_index_enabled.then_some(CoreIndex(7 as u32)), ); backed_candidates.push(backed.clone()); - if !core_index_enabled { + if !core_index_enabled && !v2_descriptor { expected_backed_candidates_with_core .entry(ParaId::from(8)) .or_insert(vec![]) @@ -2558,13 +3138,6 @@ mod sanitizers { let header = default_header(); let relay_parent = header.hash(); - shared::Pallet::::add_allowed_relay_parent( - relay_parent, - Default::default(), - RELAY_PARENT_NUM, - 1, - ); - let session_index = SessionIndex::from(0_u32); let keystore = LocalKeystore::in_memory(); @@ -2676,6 +3249,19 @@ mod sanitizers { ), ])); + shared::Pallet::::add_allowed_relay_parent( + relay_parent, + Default::default(), + scheduler::ClaimQueue::::get() + .into_iter() + .map(|(core_index, paras)| { + (core_index, paras.into_iter().map(|e| e.para_id()).collect()) + }) + .collect(), + RELAY_PARENT_NUM, + 1, + ); + // Set the on-chain included head data and current code hash. for id in 1..=4u32 { paras::Pallet::::set_current_head(ParaId::from(id), HeadData(vec![id as u8])); @@ -2707,7 +3293,7 @@ mod sanitizers { // Para 1 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(1), relay_parent, pov_hash: Hash::repeat_byte(1 as u8), @@ -2725,8 +3311,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let prev_backed: BackedCandidate = back_candidate( candidate, @@ -2738,7 +3322,7 @@ mod sanitizers { core_index_enabled.then_some(CoreIndex(0 as u32)), ); - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(1), relay_parent, pov_hash: Hash::repeat_byte(2 as u8), @@ -2756,8 +3340,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2773,7 +3355,7 @@ mod sanitizers { // Para 2. { - let mut candidate_1 = TestCandidateBuilder { + let candidate_1 = TestCandidateBuilder { para_id: ParaId::from(2), relay_parent, pov_hash: Hash::repeat_byte(3 as u8), @@ -2791,8 +3373,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_1); - let backed_1: BackedCandidate = back_candidate( candidate_1, &validators, @@ -2811,7 +3391,7 @@ mod sanitizers { .push((backed_1, CoreIndex(2))); } - let mut candidate_2 = TestCandidateBuilder { + let candidate_2 = TestCandidateBuilder { para_id: ParaId::from(2), relay_parent, pov_hash: Hash::repeat_byte(4 as u8), @@ -2829,8 +3409,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_2); - let backed_2 = back_candidate( candidate_2.clone(), &validators, @@ -2842,7 +3420,7 @@ mod sanitizers { ); backed_candidates.push(backed_2.clone()); - let mut candidate_3 = TestCandidateBuilder { + let candidate_3 = TestCandidateBuilder { para_id: ParaId::from(2), relay_parent, pov_hash: Hash::repeat_byte(5 as u8), @@ -2860,8 +3438,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate_3); - let backed_3 = back_candidate( candidate_3, &validators, @@ -2876,7 +3452,7 @@ mod sanitizers { // Para 3 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(3), relay_parent, pov_hash: Hash::repeat_byte(6 as u8), @@ -2894,8 +3470,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed: BackedCandidate = back_candidate( candidate, @@ -2914,7 +3488,7 @@ mod sanitizers { .push((backed, CoreIndex(5))); } - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(3), relay_parent, pov_hash: Hash::repeat_byte(6 as u8), @@ -2932,8 +3506,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -2954,7 +3526,7 @@ mod sanitizers { // Para 4 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(4), relay_parent, pov_hash: Hash::repeat_byte(8 as u8), @@ -2972,8 +3544,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed: BackedCandidate = back_candidate( candidate.clone(), &validators, @@ -3077,6 +3647,7 @@ mod sanitizers { shared::Pallet::::add_allowed_relay_parent( prev_relay_parent, Default::default(), + Default::default(), RELAY_PARENT_NUM - 1, 2, ); @@ -3084,6 +3655,7 @@ mod sanitizers { shared::Pallet::::add_allowed_relay_parent( relay_parent, Default::default(), + Default::default(), RELAY_PARENT_NUM, 2, ); @@ -3091,6 +3663,7 @@ mod sanitizers { shared::Pallet::::add_allowed_relay_parent( next_relay_parent, Default::default(), + Default::default(), RELAY_PARENT_NUM + 1, 2, ); @@ -3210,7 +3783,7 @@ mod sanitizers { // Para 1 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(1), relay_parent, pov_hash: Hash::repeat_byte(1 as u8), @@ -3228,8 +3801,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed: BackedCandidate = back_candidate( candidate, @@ -3248,7 +3819,7 @@ mod sanitizers { .push((backed, CoreIndex(0))); } - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(1), relay_parent: prev_relay_parent, pov_hash: Hash::repeat_byte(1 as u8), @@ -3267,8 +3838,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed = back_candidate( candidate, @@ -3281,7 +3850,7 @@ mod sanitizers { ); backed_candidates.push(backed.clone()); - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(1), relay_parent, pov_hash: Hash::repeat_byte(1 as u8), @@ -3300,8 +3869,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -3316,7 +3883,7 @@ mod sanitizers { // Para 2 { - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(2), relay_parent: prev_relay_parent, pov_hash: Hash::repeat_byte(2 as u8), @@ -3334,8 +3901,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed: BackedCandidate = back_candidate( candidate, @@ -3354,7 +3919,7 @@ mod sanitizers { .push((backed, CoreIndex(3))); } - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(2), relay_parent, pov_hash: Hash::repeat_byte(2 as u8), @@ -3373,8 +3938,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let prev_candidate = candidate.clone(); let backed = back_candidate( candidate, @@ -3393,7 +3956,7 @@ mod sanitizers { .push((backed, CoreIndex(4))); } - let mut candidate = TestCandidateBuilder { + let candidate = TestCandidateBuilder { para_id: ParaId::from(2), relay_parent, pov_hash: Hash::repeat_byte(2 as u8), @@ -3412,8 +3975,6 @@ mod sanitizers { } .build(); - collator_sign_candidate(Sr25519Keyring::One, &mut candidate); - let backed = back_candidate( candidate, &validators, @@ -3486,7 +4047,8 @@ mod sanitizers { &shared::AllowedRelayParents::::get(), BTreeSet::new(), scheduled, - core_index_enabled + core_index_enabled, + false, ), expected_backed_candidates_with_core, ); @@ -3494,15 +4056,20 @@ mod sanitizers { } #[rstest] - #[case(false)] - #[case(true)] - fn test_with_multiple_cores_per_para(#[case] core_index_enabled: bool) { + #[case(false, false)] + #[case(true, false)] + #[case(false, true)] + #[case(true, true)] + fn test_with_multiple_cores_per_para( + #[case] core_index_enabled: bool, + #[case] v2_descriptor: bool, + ) { new_test_ext(default_config()).execute_with(|| { let TestData { backed_candidates, expected_backed_candidates_with_core, scheduled_paras: scheduled, - } = get_test_data_multiple_cores_per_para(core_index_enabled); + } = get_test_data_multiple_cores_per_para(core_index_enabled, v2_descriptor); assert_eq!( sanitize_backed_candidates::( @@ -3510,7 +4077,8 @@ mod sanitizers { &shared::AllowedRelayParents::::get(), BTreeSet::new(), scheduled, - core_index_enabled + core_index_enabled, + v2_descriptor, ), expected_backed_candidates_with_core, ); @@ -3535,6 +4103,7 @@ mod sanitizers { BTreeSet::new(), scheduled, core_index_enabled, + false, ), expected_backed_candidates_with_core ); @@ -3567,6 +4136,7 @@ mod sanitizers { BTreeSet::new(), scheduled, core_index_enabled, + false, ), expected_backed_candidates_with_core ); @@ -3607,6 +4177,7 @@ mod sanitizers { BTreeSet::new(), scheduled, core_index_enabled, + false, ); if core_index_enabled { @@ -3677,6 +4248,7 @@ mod sanitizers { BTreeSet::new(), scheduled, core_index_enabled, + false, ); if core_index_enabled { @@ -3693,17 +4265,22 @@ mod sanitizers { // nothing is scheduled, so no paraids match, thus all backed candidates are skipped #[rstest] - #[case(false, false)] - #[case(true, true)] - #[case(false, true)] - #[case(true, false)] + #[case(false, false, true)] + #[case(true, true, true)] + #[case(false, true, true)] + #[case(true, false, true)] + #[case(false, false, false)] + #[case(true, true, false)] + #[case(false, true, false)] + #[case(true, false, false)] fn nothing_scheduled( #[case] core_index_enabled: bool, #[case] multiple_cores_per_para: bool, + #[case] v2_descriptor: bool, ) { new_test_ext(default_config()).execute_with(|| { let TestData { backed_candidates, .. } = if multiple_cores_per_para { - get_test_data_multiple_cores_per_para(core_index_enabled) + get_test_data_multiple_cores_per_para(core_index_enabled, v2_descriptor) } else { get_test_data_one_core_per_para(core_index_enabled) }; @@ -3715,6 +4292,7 @@ mod sanitizers { BTreeSet::new(), scheduled, core_index_enabled, + false, ); assert!(sanitized_backed_candidates.is_empty()); @@ -3751,6 +4329,7 @@ mod sanitizers { set, scheduled, core_index_enabled, + false, ); assert_eq!(sanitized_backed_candidates.len(), backed_candidates.len() / 2); @@ -3758,8 +4337,14 @@ mod sanitizers { } // candidates that have concluded as invalid are filtered out, as well as their descendants. - #[test] - fn concluded_invalid_are_filtered_out_multiple_cores_per_para() { + #[rstest] + #[case(false, true)] + #[case(true, false)] + #[case(true, true)] + fn concluded_invalid_are_filtered_out_multiple_cores_per_para( + #[case] core_index_enabled: bool, + #[case] v2_descriptor: bool, + ) { // Mark the first candidate of paraid 1 as invalid. Its descendant should also // be dropped. Also mark the candidate of paraid 3 as invalid. new_test_ext(default_config()).execute_with(|| { @@ -3768,14 +4353,14 @@ mod sanitizers { scheduled_paras: scheduled, mut expected_backed_candidates_with_core, .. - } = get_test_data_multiple_cores_per_para(true); + } = get_test_data_multiple_cores_per_para(core_index_enabled, v2_descriptor); let mut invalid_set = std::collections::BTreeSet::new(); for (idx, backed_candidate) in backed_candidates.iter().enumerate() { - if backed_candidate.descriptor().para_id == ParaId::from(1) && idx == 0 { + if backed_candidate.descriptor().para_id() == ParaId::from(1) && idx == 0 { invalid_set.insert(backed_candidate.hash()); - } else if backed_candidate.descriptor().para_id == ParaId::from(3) { + } else if backed_candidate.descriptor().para_id() == ParaId::from(3) { invalid_set.insert(backed_candidate.hash()); } } @@ -3787,7 +4372,8 @@ mod sanitizers { &shared::AllowedRelayParents::::get(), invalid_set, scheduled, - true, + core_index_enabled, + v2_descriptor, ); // We'll be left with candidates from paraid 2 and 4. @@ -3806,12 +4392,12 @@ mod sanitizers { scheduled_paras: scheduled, mut expected_backed_candidates_with_core, .. - } = get_test_data_multiple_cores_per_para(true); + } = get_test_data_multiple_cores_per_para(core_index_enabled, v2_descriptor); let mut invalid_set = std::collections::BTreeSet::new(); for (idx, backed_candidate) in backed_candidates.iter().enumerate() { - if backed_candidate.descriptor().para_id == ParaId::from(1) && idx == 1 { + if backed_candidate.descriptor().para_id() == ParaId::from(1) && idx == 1 { invalid_set.insert(backed_candidate.hash()); } } @@ -3823,7 +4409,8 @@ mod sanitizers { &shared::AllowedRelayParents::::get(), invalid_set, scheduled, - true, + core_index_enabled, + v2_descriptor, ); // Only the second candidate of paraid 1 should be removed. @@ -4034,7 +4621,7 @@ mod sanitizers { // Disable Bob, only the second candidate of paraid 1 should be removed. new_test_ext(default_config()).execute_with(|| { let TestData { mut expected_backed_candidates_with_core, .. } = - get_test_data_multiple_cores_per_para(true); + get_test_data_multiple_cores_per_para(true, false); set_disabled_validators(vec![1]); @@ -4056,7 +4643,7 @@ mod sanitizers { for disabled in [vec![0], vec![0, 1]] { new_test_ext(default_config()).execute_with(|| { let TestData { mut expected_backed_candidates_with_core, .. } = - get_test_data_multiple_cores_per_para(true); + get_test_data_multiple_cores_per_para(true, false); set_disabled_validators(disabled); diff --git a/polkadot/runtime/parachains/src/runtime_api_impl/mod.rs b/polkadot/runtime/parachains/src/runtime_api_impl/mod.rs index ed2e95b3cfa989f0c00f32af63f5f3d015b175ae..ad80856e2393e288c07ebc32a6ed16f0b7bde1bd 100644 --- a/polkadot/runtime/parachains/src/runtime_api_impl/mod.rs +++ b/polkadot/runtime/parachains/src/runtime_api_impl/mod.rs @@ -26,5 +26,5 @@ //! 2. Move methods from `vstaging` to `v3`. The new stable version should include all methods from //! `vstaging` tagged with the new version number (e.g. all `v3` methods). -pub mod v10; +pub mod v11; pub mod vstaging; diff --git a/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs b/polkadot/runtime/parachains/src/runtime_api_impl/v11.rs similarity index 87% rename from polkadot/runtime/parachains/src/runtime_api_impl/v10.rs rename to polkadot/runtime/parachains/src/runtime_api_impl/v11.rs index 697890232211315af749a365c95fa8cc23425450..a0996d5df0eec2eade802bf0980e1b4c49c3ce17 100644 --- a/polkadot/runtime/parachains/src/runtime_api_impl/v10.rs +++ b/polkadot/runtime/parachains/src/runtime_api_impl/v11.rs @@ -14,7 +14,7 @@ //! A module exporting runtime API implementation functions for all runtime APIs using `v5` //! primitives. //! -//! Runtimes implementing the v10 runtime API are recommended to forward directly to these +//! Runtimes implementing the v11 runtime API are recommended to forward directly to these //! functions. use crate::{ @@ -22,20 +22,28 @@ use crate::{ scheduler::{self, CoreOccupied}, session_info, shared, }; -use alloc::{collections::btree_map::BTreeMap, vec, vec::Vec}; +use alloc::{ + collections::{btree_map::BTreeMap, vec_deque::VecDeque}, + vec, + vec::Vec, +}; use frame_support::traits::{GetStorageVersion, StorageVersion}; use frame_system::pallet_prelude::*; use polkadot_primitives::{ async_backing::{ - AsyncBackingParams, BackingState, CandidatePendingAvailability, Constraints, - InboundHrmpLimitations, OutboundHrmpChannelLimitations, + AsyncBackingParams, Constraints, InboundHrmpLimitations, OutboundHrmpChannelLimitations, + }, + slashing, + vstaging::{ + async_backing::{BackingState, CandidatePendingAvailability}, + CandidateEvent, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + OccupiedCore, ScrapedOnChainVotes, }, - slashing, ApprovalVotingParams, AuthorityDiscoveryId, CandidateEvent, CandidateHash, - CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState, ExecutorParams, GroupIndex, - GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, - NodeFeatures, OccupiedCore, OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, - ScrapedOnChainVotes, SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash, - ValidatorId, ValidatorIndex, ValidatorSignature, + ApprovalVotingParams, AuthorityDiscoveryId, CandidateHash, CoreIndex, DisputeState, + ExecutorParams, GroupIndex, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, + InboundHrmpMessage, NodeFeatures, OccupiedCoreAssumption, PersistedValidationData, + PvfCheckStatement, SessionIndex, SessionInfo, ValidationCode, ValidationCodeHash, ValidatorId, + ValidatorIndex, ValidatorSignature, }; use sp_runtime::traits::One; @@ -447,8 +455,22 @@ pub fn backing_state( // // Thus, minimum relay parent is ensured to have asynchronous backing enabled. let now = frame_system::Pallet::::block_number(); - let min_relay_parent_number = shared::AllowedRelayParents::::get() - .hypothetical_earliest_block_number(now, config.async_backing_params.allowed_ancestry_len); + + // Use the right storage depending on version to ensure #64 doesn't cause issues with this + // migration. + let min_relay_parent_number = if shared::Pallet::::on_chain_storage_version() == + StorageVersion::new(0) + { + shared::migration::v0::AllowedRelayParents::::get().hypothetical_earliest_block_number( + now, + config.async_backing_params.allowed_ancestry_len, + ) + } else { + shared::AllowedRelayParents::::get().hypothetical_earliest_block_number( + now, + config.async_backing_params.allowed_ancestry_len, + ) + }; let required_parent = paras::Heads::::get(para_id)?; let validation_code_hash = paras::CurrentCodeHash::::get(para_id)?; @@ -543,3 +565,38 @@ pub fn node_features() -> NodeFeatures { pub fn approval_voting_params() -> ApprovalVotingParams { configuration::ActiveConfig::::get().approval_voting_params } + +/// Returns the claimqueue from the scheduler +pub fn claim_queue() -> BTreeMap> { + let now = >::block_number() + One::one(); + + // This is needed so that the claim queue always has the right size (equal to + // scheduling_lookahead). Otherwise, if a candidate is backed in the same block where the + // previous candidate is included, the claim queue will have already pop()-ed the next item + // from the queue and the length would be `scheduling_lookahead - 1`. + >::free_cores_and_fill_claim_queue(Vec::new(), now); + let config = configuration::ActiveConfig::::get(); + // Extra sanity, config should already never be smaller than 1: + let n_lookahead = config.scheduler_params.lookahead.max(1); + + scheduler::ClaimQueue::::get() + .into_iter() + .map(|(core_index, entries)| { + // on cores timing out internal claim queue size may be temporarily longer than it + // should be as the timed out assignment might got pushed back to an already full claim + // queue: + ( + core_index, + entries.into_iter().map(|e| e.para_id()).take(n_lookahead as usize).collect(), + ) + }) + .collect() +} + +/// Returns all the candidates that are pending availability for a given `ParaId`. +/// Deprecates `candidate_pending_availability` in favor of supporting elastic scaling. +pub fn candidates_pending_availability( + para_id: ParaId, +) -> Vec> { + >::candidates_pending_availability(para_id) +} diff --git a/polkadot/runtime/parachains/src/runtime_api_impl/vstaging.rs b/polkadot/runtime/parachains/src/runtime_api_impl/vstaging.rs index 4aa381e33b1bc2246abd3c16044cc56290597b12..d01b543630c31c80747d2d1e80480b931ae86886 100644 --- a/polkadot/runtime/parachains/src/runtime_api_impl/vstaging.rs +++ b/polkadot/runtime/parachains/src/runtime_api_impl/vstaging.rs @@ -15,46 +15,3 @@ // along with Polkadot. If not, see . //! Put implementations of functions from staging APIs here. - -use crate::{configuration, inclusion, initializer, scheduler}; -use alloc::{ - collections::{btree_map::BTreeMap, vec_deque::VecDeque}, - vec::Vec, -}; -use polkadot_primitives::{CommittedCandidateReceipt, CoreIndex, Id as ParaId}; -use sp_runtime::traits::One; - -/// Returns the claimqueue from the scheduler -pub fn claim_queue() -> BTreeMap> { - let now = >::block_number() + One::one(); - - // This is needed so that the claim queue always has the right size (equal to - // scheduling_lookahead). Otherwise, if a candidate is backed in the same block where the - // previous candidate is included, the claim queue will have already pop()-ed the next item - // from the queue and the length would be `scheduling_lookahead - 1`. - >::free_cores_and_fill_claim_queue(Vec::new(), now); - let config = configuration::ActiveConfig::::get(); - // Extra sanity, config should already never be smaller than 1: - let n_lookahead = config.scheduler_params.lookahead.max(1); - - scheduler::ClaimQueue::::get() - .into_iter() - .map(|(core_index, entries)| { - // on cores timing out internal claim queue size may be temporarily longer than it - // should be as the timed out assignment might got pushed back to an already full claim - // queue: - ( - core_index, - entries.into_iter().map(|e| e.para_id()).take(n_lookahead as usize).collect(), - ) - }) - .collect() -} - -/// Returns all the candidates that are pending availability for a given `ParaId`. -/// Deprecates `candidate_pending_availability` in favor of supporting elastic scaling. -pub fn candidates_pending_availability( - para_id: ParaId, -) -> Vec> { - >::candidates_pending_availability(para_id) -} diff --git a/polkadot/runtime/parachains/src/shared.rs b/polkadot/runtime/parachains/src/shared.rs index 154b7cfefc3aca68b3773b49194dba7038feccc5..f582bf0d90b5e400ac6ff558653bc69cd68930df 100644 --- a/polkadot/runtime/parachains/src/shared.rs +++ b/polkadot/runtime/parachains/src/shared.rs @@ -20,12 +20,14 @@ //! dependent on any of the other pallets. use alloc::{ - collections::{btree_map::BTreeMap, vec_deque::VecDeque}, + collections::{btree_map::BTreeMap, btree_set::BTreeSet, vec_deque::VecDeque}, vec::Vec, }; use frame_support::{pallet_prelude::*, traits::DisabledValidators}; use frame_system::pallet_prelude::BlockNumberFor; -use polkadot_primitives::{SessionIndex, ValidatorId, ValidatorIndex}; +use polkadot_primitives::{ + vstaging::transpose_claim_queue, CoreIndex, Id, SessionIndex, ValidatorId, ValidatorIndex, +}; use sp_runtime::traits::AtLeast32BitUnsigned; use rand::{seq::SliceRandom, SeedableRng}; @@ -43,16 +45,28 @@ pub(crate) const SESSION_DELAY: SessionIndex = 2; #[cfg(test)] mod tests; -/// Information about past relay-parents. +pub mod migration; + +/// Information about a relay parent. +#[derive(Encode, Decode, Default, TypeInfo, Debug)] +pub struct RelayParentInfo { + // Relay parent hash + pub relay_parent: Hash, + // The state root at this block + pub state_root: Hash, + // Claim queue snapshot, optimized for accessing the assignments by `ParaId`. + // For each para we store the cores assigned per depth. + pub claim_queue: BTreeMap>>, +} + +/// Keeps tracks of information about all viable relay parents. #[derive(Encode, Decode, Default, TypeInfo)] pub struct AllowedRelayParentsTracker { - // The past relay parents, paired with state roots, that are viable to build upon. + // Information about past relay parents that are viable to build upon. // // They are in ascending chronologic order, so the newest relay parents are at // the back of the deque. - // - // (relay_parent, state_root) - buffer: VecDeque<(Hash, Hash)>, + buffer: VecDeque>, // The number of the most recent relay-parent, if any. // If the buffer is empty, this value has no meaning and may @@ -70,13 +84,17 @@ impl &mut self, relay_parent: Hash, state_root: Hash, + claim_queue: BTreeMap>, number: BlockNumber, max_ancestry_len: u32, ) { + let claim_queue = transpose_claim_queue(claim_queue); + // + 1 for the most recent block, which is always allowed. let buffer_size_limit = max_ancestry_len as usize + 1; - self.buffer.push_back((relay_parent, state_root)); + self.buffer.push_back(RelayParentInfo { relay_parent, state_root, claim_queue }); + self.latest_number = number; while self.buffer.len() > buffer_size_limit { let _ = self.buffer.pop_front(); @@ -96,8 +114,8 @@ impl &self, relay_parent: Hash, prev: Option, - ) -> Option<(Hash, BlockNumber)> { - let pos = self.buffer.iter().position(|(rp, _)| rp == &relay_parent)?; + ) -> Option<(&RelayParentInfo, BlockNumber)> { + let pos = self.buffer.iter().position(|info| info.relay_parent == relay_parent)?; let age = (self.buffer.len() - 1) - pos; let number = self.latest_number - BlockNumber::from(age as u32); @@ -107,7 +125,7 @@ impl } } - Some((self.buffer[pos].1, number)) + Some((&self.buffer[pos], number)) } /// Returns block number of the earliest block the buffer would contain if @@ -127,8 +145,11 @@ impl pub mod pallet { use super::*; + const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); + #[pallet::pallet] #[pallet::without_storage_info] + #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); #[pallet::config] @@ -263,11 +284,12 @@ impl Pallet { pub(crate) fn add_allowed_relay_parent( relay_parent: T::Hash, state_root: T::Hash, + claim_queue: BTreeMap>, number: BlockNumberFor, max_ancestry_len: u32, ) { AllowedRelayParents::::mutate(|tracker| { - tracker.update(relay_parent, state_root, number, max_ancestry_len) + tracker.update(relay_parent, state_root, claim_queue, number, max_ancestry_len) }) } } diff --git a/polkadot/runtime/parachains/src/shared/migration.rs b/polkadot/runtime/parachains/src/shared/migration.rs new file mode 100644 index 0000000000000000000000000000000000000000..ae0412c6e26cb879374de2a4ba9dc7ff303b6fa4 --- /dev/null +++ b/polkadot/runtime/parachains/src/shared/migration.rs @@ -0,0 +1,196 @@ +// 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. + +use super::*; +use codec::{Decode, Encode}; +use frame_support::{ + pallet_prelude::ValueQuery, traits::UncheckedOnRuntimeUpgrade, weights::Weight, +}; + +#[cfg(feature = "try-runtime")] +const LOG_TARGET: &str = "runtime::shared"; + +pub mod v0 { + use super::*; + use alloc::collections::vec_deque::VecDeque; + + use frame_support::storage_alias; + + /// All allowed relay-parents storage at version 0. + #[storage_alias] + pub(crate) type AllowedRelayParents = StorageValue< + Pallet, + super::v0::AllowedRelayParentsTracker<::Hash, BlockNumberFor>, + ValueQuery, + >; + + #[derive(Encode, Decode, Default, TypeInfo)] + pub struct AllowedRelayParentsTracker { + // The past relay parents, paired with state roots, that are viable to build upon. + // + // They are in ascending chronologic order, so the newest relay parents are at + // the back of the deque. + // + // (relay_parent, state_root) + pub buffer: VecDeque<(Hash, Hash)>, + + // The number of the most recent relay-parent, if any. + // If the buffer is empty, this value has no meaning and may + // be nonsensical. + pub latest_number: BlockNumber, + } + + // Required to workaround #64. + impl + AllowedRelayParentsTracker + { + /// Returns block number of the earliest block the buffer would contain if + /// `now` is pushed into it. + pub(crate) fn hypothetical_earliest_block_number( + &self, + now: BlockNumber, + max_ancestry_len: u32, + ) -> BlockNumber { + let allowed_ancestry_len = max_ancestry_len.min(self.buffer.len() as u32); + + now - allowed_ancestry_len.into() + } + } + + impl From> + for super::AllowedRelayParentsTracker + { + fn from(value: AllowedRelayParentsTracker) -> Self { + Self { + latest_number: value.latest_number, + buffer: value + .buffer + .into_iter() + .map(|(relay_parent, state_root)| super::RelayParentInfo { + relay_parent, + state_root, + claim_queue: Default::default(), + }) + .collect(), + } + } + } +} + +mod v1 { + use super::*; + + #[cfg(feature = "try-runtime")] + use frame_support::{ + ensure, + traits::{GetStorageVersion, StorageVersion}, + }; + + pub struct VersionUncheckedMigrateToV1(core::marker::PhantomData); + + impl UncheckedOnRuntimeUpgrade for VersionUncheckedMigrateToV1 { + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { + log::trace!(target: LOG_TARGET, "Running pre_upgrade() for shared MigrateToV1"); + let bytes = u32::to_ne_bytes(v0::AllowedRelayParents::::get().buffer.len() as u32); + + Ok(bytes.to_vec()) + } + + fn on_runtime_upgrade() -> Weight { + let mut weight: Weight = Weight::zero(); + + // Read old storage. + let old_rp_tracker = v0::AllowedRelayParents::::take(); + + super::AllowedRelayParents::::set(old_rp_tracker.into()); + + weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); + + weight + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + log::trace!(target: LOG_TARGET, "Running post_upgrade() for shared MigrateToV1"); + ensure!( + Pallet::::on_chain_storage_version() >= StorageVersion::new(1), + "Storage version should be >= 1 after the migration" + ); + + let relay_parent_count = u32::from_ne_bytes( + state + .try_into() + .expect("u32::from_ne_bytes(to_ne_bytes(u32)) always works; qed"), + ); + + let rp_tracker = AllowedRelayParents::::get(); + + ensure!( + relay_parent_count as usize == rp_tracker.buffer.len(), + "Number of allowed relay parents should be the same as the one before the upgrade." + ); + + Ok(()) + } + } +} + +/// Migrate shared module storage to v1. +pub type MigrateToV1 = frame_support::migrations::VersionedMigration< + 0, + 1, + v1::VersionUncheckedMigrateToV1, + Pallet, + ::DbWeight, +>; + +#[cfg(test)] +mod tests { + use super::{v1::VersionUncheckedMigrateToV1, *}; + use crate::mock::{new_test_ext, MockGenesisConfig, Test}; + use frame_support::traits::UncheckedOnRuntimeUpgrade; + use polkadot_primitives::Hash; + + #[test] + fn migrate_to_v1() { + new_test_ext(MockGenesisConfig::default()).execute_with(|| { + let rp_tracker = v0::AllowedRelayParentsTracker { + latest_number: 9, + buffer: (0..10u64) + .into_iter() + .map(|idx| (Hash::from_low_u64_ne(idx), Hash::from_low_u64_ne(2 * idx))) + .collect::>(), + }; + + v0::AllowedRelayParents::::put(rp_tracker); + + as UncheckedOnRuntimeUpgrade>::on_runtime_upgrade(); + + let rp_tracker = AllowedRelayParents::::get(); + + assert_eq!(rp_tracker.buffer.len(), 10); + + for idx in 0..10u64 { + let relay_parent = Hash::from_low_u64_ne(idx); + let state_root = Hash::from_low_u64_ne(2 * idx); + let (info, block_num) = rp_tracker.acquire_info(relay_parent, None).unwrap(); + + assert!(info.claim_queue.is_empty()); + assert_eq!(info.relay_parent, relay_parent); + assert_eq!(info.state_root, state_root); + assert_eq!(block_num as u64, idx); + } + }); + } +} diff --git a/polkadot/runtime/parachains/src/shared/tests.rs b/polkadot/runtime/parachains/src/shared/tests.rs index e47d1fd9cfe0d43adaf9b6c3f9befd2d604d2c8c..6da84e254f051f393612adb2f7f6a5e9a60cfc7c 100644 --- a/polkadot/runtime/parachains/src/shared/tests.rs +++ b/polkadot/runtime/parachains/src/shared/tests.rs @@ -36,22 +36,71 @@ fn tracker_earliest_block_number() { // Push a single block into the tracker, suppose max capacity is 1. let max_ancestry_len = 0; - tracker.update(Hash::zero(), Hash::zero(), 0, max_ancestry_len); + tracker.update(Hash::zero(), Hash::zero(), Default::default(), 0, max_ancestry_len); assert_eq!(tracker.hypothetical_earliest_block_number(now, max_ancestry_len), now); // Test a greater capacity. let max_ancestry_len = 4; let now = 4; for i in 1..now { - tracker.update(Hash::zero(), Hash::zero(), i, max_ancestry_len); + tracker.update(Hash::zero(), Hash::zero(), Default::default(), i, max_ancestry_len); assert_eq!(tracker.hypothetical_earliest_block_number(i + 1, max_ancestry_len), 0); } // Capacity exceeded. - tracker.update(Hash::zero(), Hash::zero(), now, max_ancestry_len); + tracker.update(Hash::zero(), Hash::zero(), Default::default(), now, max_ancestry_len); assert_eq!(tracker.hypothetical_earliest_block_number(now + 1, max_ancestry_len), 1); } +#[test] +fn tracker_claim_queue_remap() { + let mut tracker = AllowedRelayParentsTracker::::default(); + + let mut claim_queue = BTreeMap::new(); + claim_queue.insert(CoreIndex(0), vec![Id::from(0), Id::from(1), Id::from(2)].into()); + claim_queue.insert(CoreIndex(1), vec![Id::from(0), Id::from(0), Id::from(100)].into()); + claim_queue.insert(CoreIndex(2), vec![Id::from(1), Id::from(2), Id::from(100)].into()); + + tracker.update(Hash::zero(), Hash::zero(), claim_queue, 1u32, 3u32); + + let (info, _block_num) = tracker.acquire_info(Hash::zero(), None).unwrap(); + assert_eq!( + info.claim_queue.get(&Id::from(0)).unwrap()[&0], + vec![CoreIndex(0), CoreIndex(1)].into_iter().collect::>() + ); + assert_eq!( + info.claim_queue.get(&Id::from(1)).unwrap()[&0], + vec![CoreIndex(2)].into_iter().collect::>() + ); + assert_eq!(info.claim_queue.get(&Id::from(2)).unwrap().get(&0), None); + assert_eq!(info.claim_queue.get(&Id::from(100)).unwrap().get(&0), None); + + assert_eq!( + info.claim_queue.get(&Id::from(0)).unwrap()[&1], + vec![CoreIndex(1)].into_iter().collect::>() + ); + assert_eq!( + info.claim_queue.get(&Id::from(1)).unwrap()[&1], + vec![CoreIndex(0)].into_iter().collect::>() + ); + assert_eq!( + info.claim_queue.get(&Id::from(2)).unwrap()[&1], + vec![CoreIndex(2)].into_iter().collect::>() + ); + assert_eq!(info.claim_queue.get(&Id::from(100)).unwrap().get(&1), None); + + assert_eq!(info.claim_queue.get(&Id::from(0)).unwrap().get(&2), None); + assert_eq!(info.claim_queue.get(&Id::from(1)).unwrap().get(&2), None); + assert_eq!( + info.claim_queue.get(&Id::from(2)).unwrap()[&2], + vec![CoreIndex(0)].into_iter().collect::>() + ); + assert_eq!( + info.claim_queue.get(&Id::from(100)).unwrap()[&2], + vec![CoreIndex(1), CoreIndex(2)].into_iter().collect::>() + ); +} + #[test] fn tracker_acquire_info() { let mut tracker = AllowedRelayParentsTracker::::default(); @@ -65,20 +114,20 @@ fn tracker_acquire_info() { ]; let (relay_parent, state_root) = blocks[0]; - tracker.update(relay_parent, state_root, 0, max_ancestry_len); + tracker.update(relay_parent, state_root, Default::default(), 0, max_ancestry_len); assert_matches!( tracker.acquire_info(relay_parent, None), - Some((s, b)) if s == state_root && b == 0 + Some((s, b)) if s.state_root == state_root && b == 0 ); let (relay_parent, state_root) = blocks[1]; - tracker.update(relay_parent, state_root, 1u32, max_ancestry_len); + tracker.update(relay_parent, state_root, Default::default(), 1u32, max_ancestry_len); let (relay_parent, state_root) = blocks[2]; - tracker.update(relay_parent, state_root, 2u32, max_ancestry_len); + tracker.update(relay_parent, state_root, Default::default(), 2u32, max_ancestry_len); for (block_num, (rp, state_root)) in blocks.iter().enumerate().take(2) { assert_matches!( tracker.acquire_info(*rp, None), - Some((s, b)) if &s == state_root && b == block_num as u32 + Some((s, b)) if &s.state_root == state_root && b == block_num as u32 ); assert!(tracker.acquire_info(*rp, Some(2)).is_none()); @@ -87,7 +136,7 @@ fn tracker_acquire_info() { for (block_num, (rp, state_root)) in blocks.iter().enumerate().skip(1) { assert_matches!( tracker.acquire_info(*rp, Some(block_num as u32 - 1)), - Some((s, b)) if &s == state_root && b == block_num as u32 + Some((s, b)) if &s.state_root == state_root && b == block_num as u32 ); } } diff --git a/polkadot/runtime/parachains/src/ump_tests.rs b/polkadot/runtime/parachains/src/ump_tests.rs index d914bf8b666120fba1c3db2f85f3420427fb9186..cd7951ac9aa90cf15821ee60e9d624dbe4058ade 100644 --- a/polkadot/runtime/parachains/src/ump_tests.rs +++ b/polkadot/runtime/parachains/src/ump_tests.rs @@ -31,7 +31,10 @@ use frame_support::{ traits::{EnqueueMessage, ExecuteOverweightError, ServiceQueues}, weights::Weight, }; -use polkadot_primitives::{well_known_keys, Id as ParaId, UpwardMessage}; +use polkadot_primitives::{ + vstaging::{ClaimQueueOffset, CoreSelector, UMPSignal, UMP_SEPARATOR}, + well_known_keys, Id as ParaId, UpwardMessage, +}; use sp_crypto_hashing::{blake2_256, twox_64}; use sp_runtime::traits::Bounded; @@ -141,12 +144,12 @@ mod check_upward_messages { configuration::ActiveConfig::::get().max_upward_message_num_per_candidate; for sent in 0..permitted + 1 { - check(P_0, vec![msg(""); sent as usize], None); + check(P_0, vec![msg("a"); sent as usize], None); } for sent in permitted + 1..permitted + 10 { check( P_0, - vec![msg(""); sent as usize], + vec![msg("a"); sent as usize], Some(UmpAcceptanceCheckErr::MoreMessagesThanPermitted { sent, permitted }), ); } @@ -161,7 +164,7 @@ mod check_upward_messages { let max_per_candidate = configuration::ActiveConfig::::get().max_upward_message_num_per_candidate; - for msg_size in 0..=max_size { + for msg_size in 1..=max_size { check(P_0, vec![vec![0; msg_size as usize]], None); } for msg_size in max_size + 1..max_size + 10 { @@ -185,18 +188,18 @@ mod check_upward_messages { let limit = configuration::ActiveConfig::::get().max_upward_queue_count as u64; for _ in 0..limit { - check(P_0, vec![msg("")], None); - queue(P_0, vec![msg("")]); + check(P_0, vec![msg("a")], None); + queue(P_0, vec![msg("a")]); } check( P_0, - vec![msg("")], + vec![msg("a")], Some(UmpAcceptanceCheckErr::CapacityExceeded { count: limit + 1, limit }), ); check( P_0, - vec![msg(""); 2], + vec![msg("a"); 2], Some(UmpAcceptanceCheckErr::CapacityExceeded { count: limit + 2, limit }), ); }); @@ -462,10 +465,11 @@ fn verify_relay_dispatch_queue_size_is_externally_accessible() { fn assert_queue_size(para: ParaId, count: u32, size: u32) { #[allow(deprecated)] - let raw_queue_size = sp_io::storage::get(&well_known_keys::relay_dispatch_queue_size(para)).expect( - "enqueuing a message should create the dispatch queue\ + let raw_queue_size = sp_io::storage::get(&well_known_keys::relay_dispatch_queue_size(para)) + .expect( + "enqueuing a message should create the dispatch queue\ and it should be accessible via the well known keys", - ); + ); let (c, s) = <(u32, u32)>::decode(&mut &raw_queue_size[..]) .expect("the dispatch queue size should be decodable into (u32, u32)"); assert_eq!((c, s), (count, size)); @@ -641,6 +645,42 @@ fn cannot_offboard_while_ump_dispatch_queued() { }); } +/// Test UMP signals are filtered out and don't consume `max_upward_message_num_per_candidate`. +#[test] +fn enqueue_ump_signals() { + let para = 100.into(); + + new_test_ext(GenesisConfigBuilder::default().build()).execute_with(|| { + register_parachain(para); + run_to_block(5, vec![4, 5]); + + let config = configuration::ActiveConfig::::get(); + let mut messages = (0..config.max_upward_message_num_per_candidate) + .into_iter() + .map(|_| "msg".encode()) + .collect::>(); + let expected_messages = messages.iter().cloned().map(|msg| (para, msg)).collect::>(); + + // `UMPSignals` and separator do not count as XCM messages. The below check must pass. + messages.append(&mut vec![ + UMP_SEPARATOR, + UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(0)).encode(), + ]); + + ParaInclusion::check_upward_messages( + &configuration::ActiveConfig::::get(), + para, + &messages, + ) + .unwrap(); + + // We expect that all messages except UMP signal and separator are processed + ParaInclusion::receive_upward_messages(para, &messages); + MessageQueue::service_queues(Weight::max_value()); + assert_eq!(Processed::take(), expected_messages); + }); +} + /// A para-chain cannot send an UMP to the relay chain while it is offboarding. #[test] fn cannot_enqueue_ump_while_offboarding() { diff --git a/polkadot/runtime/rococo/build.rs b/polkadot/runtime/rococo/build.rs index 7aae84cd5e0fedcb29265f98d40bcd9ccd83ae43..aab666b0f11c15e9bdb4124767220126abf6e680 100644 --- a/polkadot/runtime/rococo/build.rs +++ b/polkadot/runtime/rococo/build.rs @@ -1,18 +1,18 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Polkadot. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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 Substrate. If not, see . +// along with Polkadot. If not, see . #[cfg(all(not(feature = "metadata-hash"), feature = "std"))] fn main() { diff --git a/polkadot/runtime/rococo/constants/src/weights/mod.rs b/polkadot/runtime/rococo/constants/src/weights/mod.rs index 23812ce7ed0528c394f84042fb9842eb617a834b..2648608a2f8af79f3830dee845f3079315eb001f 100644 --- a/polkadot/runtime/rococo/constants/src/weights/mod.rs +++ b/polkadot/runtime/rococo/constants/src/weights/mod.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Polkadot. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/polkadot/runtime/rococo/constants/src/weights/paritydb_weights.rs b/polkadot/runtime/rococo/constants/src/weights/paritydb_weights.rs index 25679703831a13b8d1bb7fb7dd4d92fa84b1f255..67d5286022ee0f19bc86f7fea2ac997c31f87cfa 100644 --- a/polkadot/runtime/rococo/constants/src/weights/paritydb_weights.rs +++ b/polkadot/runtime/rococo/constants/src/weights/paritydb_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Polkadot. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/polkadot/runtime/rococo/constants/src/weights/rocksdb_weights.rs b/polkadot/runtime/rococo/constants/src/weights/rocksdb_weights.rs index 3dd817aa6f137085b0e5fdf2b11b7f50e5c8b002..57f49e1202c16b7afd578fecc0ad63697a32e983 100644 --- a/polkadot/runtime/rococo/constants/src/weights/rocksdb_weights.rs +++ b/polkadot/runtime/rococo/constants/src/weights/rocksdb_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Polkadot. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/polkadot/runtime/rococo/src/genesis_config_presets.rs b/polkadot/runtime/rococo/src/genesis_config_presets.rs index 17792cff18678623f373621e7206b609340f2bb6..c237dfd967f6780061c6801d37f7ef01b758cc5c 100644 --- a/polkadot/runtime/rococo/src/genesis_config_presets.rs +++ b/polkadot/runtime/rococo/src/genesis_config_presets.rs @@ -16,10 +16,13 @@ //! Genesis configs presets for the Rococo runtime -use crate::{SessionKeys, BABE_GENESIS_EPOCH_CONFIG}; +use crate::{ + BabeConfig, BalancesConfig, ConfigurationConfig, RegistrarConfig, RuntimeGenesisConfig, + SessionConfig, SessionKeys, SudoConfig, BABE_GENESIS_EPOCH_CONFIG, +}; #[cfg(not(feature = "std"))] use alloc::format; -use alloc::vec::Vec; +use alloc::{vec, vec::Vec}; use polkadot_primitives::{AccountId, AccountPublic, AssignmentId, SchedulerParams, ValidatorId}; use rococo_runtime_constants::currency::UNITS as ROC; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; @@ -27,6 +30,7 @@ use sp_consensus_babe::AuthorityId as BabeId; use sp_consensus_beefy::ecdsa_crypto::AuthorityId as BeefyId; use sp_consensus_grandpa::AuthorityId as GrandpaId; use sp_core::{sr25519, Pair, Public}; +use sp_genesis_builder::PresetId; use sp_runtime::traits::IdentifyAccount; /// Helper function to generate a crypto pair from seed @@ -178,12 +182,12 @@ fn rococo_testnet_genesis( const ENDOWMENT: u128 = 1_000_000 * ROC; - serde_json::json!({ - "balances": { - "balances": endowed_accounts.iter().map(|k| (k.clone(), ENDOWMENT)).collect::>(), + let config = RuntimeGenesisConfig { + balances: BalancesConfig { + balances: endowed_accounts.iter().map(|k| (k.clone(), ENDOWMENT)).collect::>(), }, - "session": { - "keys": initial_authorities + session: SessionConfig { + keys: initial_authorities .iter() .map(|x| { ( @@ -200,13 +204,12 @@ fn rococo_testnet_genesis( ) }) .collect::>(), + ..Default::default() }, - "babe": { - "epochConfig": Some(BABE_GENESIS_EPOCH_CONFIG), - }, - "sudo": { "key": Some(root_key.clone()) }, - "configuration": { - "config": polkadot_runtime_parachains::configuration::HostConfiguration { + babe: BabeConfig { epoch_config: BABE_GENESIS_EPOCH_CONFIG, ..Default::default() }, + sudo: SudoConfig { key: Some(root_key.clone()) }, + configuration: ConfigurationConfig { + config: polkadot_runtime_parachains::configuration::HostConfiguration { scheduler_params: SchedulerParams { max_validators_per_core: Some(1), ..default_parachains_host_configuration().scheduler_params @@ -214,10 +217,14 @@ fn rococo_testnet_genesis( ..default_parachains_host_configuration() }, }, - "registrar": { - "nextFreeParaId": polkadot_primitives::LOWEST_PUBLIC_ID, - } - }) + registrar: RegistrarConfig { + next_free_para_id: polkadot_primitives::LOWEST_PUBLIC_ID, + ..Default::default() + }, + ..Default::default() + }; + + serde_json::to_value(config).expect("Could not build genesis config.") } // staging_testnet @@ -439,44 +446,32 @@ fn rococo_staging_testnet_config_genesis() -> serde_json::Value { const ENDOWMENT: u128 = 1_000_000 * ROC; const STASH: u128 = 100 * ROC; - serde_json::json!({ - "balances": { - "balances": endowed_accounts + let config = RuntimeGenesisConfig { + balances: BalancesConfig { + balances: endowed_accounts .iter() .map(|k: &AccountId| (k.clone(), ENDOWMENT)) .chain(initial_authorities.iter().map(|x| (x.0.clone(), STASH))) .collect::>(), }, - "session": { - "keys": initial_authorities + session: SessionConfig { + keys: initial_authorities .into_iter() - .map(|x| { - ( - x.0.clone(), - x.0, - rococo_session_keys( - x.2, - x.3, - x.4, - x.5, - x.6, - x.7, - ), - ) - }) + .map(|x| (x.0.clone(), x.0, rococo_session_keys(x.2, x.3, x.4, x.5, x.6, x.7))) .collect::>(), + ..Default::default() }, - "babe": { - "epochConfig": Some(BABE_GENESIS_EPOCH_CONFIG), - }, - "sudo": { "key": Some(endowed_accounts[0].clone()) }, - "configuration": { - "config": default_parachains_host_configuration(), - }, - "registrar": { - "nextFreeParaId": polkadot_primitives::LOWEST_PUBLIC_ID, + babe: BabeConfig { epoch_config: BABE_GENESIS_EPOCH_CONFIG, ..Default::default() }, + sudo: SudoConfig { key: Some(endowed_accounts[0].clone()) }, + configuration: ConfigurationConfig { config: default_parachains_host_configuration() }, + registrar: RegistrarConfig { + next_free_para_id: polkadot_primitives::LOWEST_PUBLIC_ID, + ..Default::default() }, - }) + ..Default::default() + }; + + serde_json::to_value(config).expect("Could not build genesis config.") } //development @@ -512,28 +507,12 @@ fn versi_local_testnet_genesis() -> serde_json::Value { ) } -/// Wococo is a temporary testnet that uses almost the same runtime as rococo. -//wococo_local_testnet -fn wococo_local_testnet_genesis() -> serde_json::Value { - rococo_testnet_genesis( - Vec::from([ - get_authority_keys_from_seed("Alice"), - get_authority_keys_from_seed("Bob"), - get_authority_keys_from_seed("Charlie"), - get_authority_keys_from_seed("Dave"), - ]), - get_account_id_from_seed::("Alice"), - None, - ) -} - /// Provides the JSON representation of predefined genesis config for given `id`. -pub fn get_preset(id: &sp_genesis_builder::PresetId) -> Option> { +pub fn get_preset(id: &PresetId) -> Option> { let patch = match id.try_into() { - Ok("local_testnet") => rococo_local_testnet_genesis(), - Ok("development") => rococo_development_config_genesis(), + Ok(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET) => rococo_local_testnet_genesis(), + Ok(sp_genesis_builder::DEV_RUNTIME_PRESET) => rococo_development_config_genesis(), Ok("staging_testnet") => rococo_staging_testnet_config_genesis(), - Ok("wococo_local_testnet") => wococo_local_testnet_genesis(), Ok("versi_local_testnet") => versi_local_testnet_genesis(), _ => return None, }; @@ -543,3 +522,13 @@ pub fn get_preset(id: &sp_genesis_builder::PresetId) -> Option Vec { + vec![ + PresetId::from(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET), + PresetId::from(sp_genesis_builder::DEV_RUNTIME_PRESET), + PresetId::from("staging_testnet"), + PresetId::from("versi_local_testnet"), + ] +} diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index dfc41b15bb1dc4468ecab0e0cceb4c09d3f6efd8..1b5f7b5157d83479e9004bfcb4c3a5a31bdd8464 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -33,14 +33,19 @@ use frame_support::{ dynamic_params::{dynamic_pallet_params, dynamic_params}, traits::FromContains, }; +use pallet_balances::WeightInfo; use pallet_nis::WithMaximumOf; use polkadot_primitives::{ - slashing, AccountId, AccountIndex, ApprovalVotingParams, Balance, BlockNumber, CandidateEvent, - CandidateHash, CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState, ExecutorParams, - GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, Moment, - NodeFeatures, Nonce, OccupiedCoreAssumption, PersistedValidationData, ScrapedOnChainVotes, - SessionInfo, Signature, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, - PARACHAIN_KEY_TYPE_ID, + slashing, + vstaging::{ + CandidateEvent, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + ScrapedOnChainVotes, + }, + AccountId, AccountIndex, ApprovalVotingParams, Balance, BlockNumber, CandidateHash, CoreIndex, + DisputeState, ExecutorParams, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, + InboundHrmpMessage, Moment, NodeFeatures, Nonce, OccupiedCoreAssumption, + PersistedValidationData, SessionInfo, Signature, ValidationCode, ValidationCodeHash, + ValidatorId, ValidatorIndex, PARACHAIN_KEY_TYPE_ID, }; use polkadot_runtime_common::{ assigned_slots, auctions, claims, crowdloan, identity_migrator, impl_runtime_weights, @@ -62,9 +67,7 @@ use polkadot_runtime_parachains::{ initializer as parachains_initializer, on_demand as parachains_on_demand, origin as parachains_origin, paras as parachains_paras, paras_inherent as parachains_paras_inherent, - runtime_api_impl::{ - v10 as parachains_runtime_api_impl, vstaging as vstaging_parachains_runtime_api_impl, - }, + runtime_api_impl::v11 as parachains_runtime_api_impl, scheduler as parachains_scheduler, session_info as parachains_session_info, shared as parachains_shared, }; @@ -166,11 +169,11 @@ 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_015_000, + spec_version: 1_016_001, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 26, - state_version: 1, + system_version: 1, }; /// The BABE epoch configuration at genesis. @@ -398,6 +401,7 @@ impl pallet_balances::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type MaxFreezes = ConstU32<1>; + type DoneSlashHandler = (); } parameter_types! { @@ -1232,6 +1236,7 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<1>; + type DoneSlashHandler = (); } parameter_types! { @@ -1597,6 +1602,8 @@ pub mod migrations { pub const TechnicalMembershipPalletName: &'static str = "TechnicalMembership"; pub const TipsPalletName: &'static str = "Tips"; pub const PhragmenElectionPalletId: LockIdentifier = *b"phrelect"; + /// Weight for balance unreservations + pub BalanceUnreserveWeight: Weight = weights::pallet_balances_balances::WeightInfo::::force_unreserve(); } // Special Config for Gov V1 pallets, allowing us to run migrations for them without @@ -1652,6 +1659,7 @@ pub mod migrations { pallet_elections_phragmen::migrations::unlock_and_unreserve_all_funds::UnlockAndUnreserveAllFunds, pallet_democracy::migrations::unlock_and_unreserve_all_funds::UnlockAndUnreserveAllFunds, pallet_tips::migrations::unreserve_deposits::UnreserveDeposits, + pallet_treasury::migration::cleanup_proposals::Migration, // Delete all Gov v1 pallet storage key/values. @@ -1675,6 +1683,7 @@ pub mod migrations { // permanent pallet_xcm::migration::MigrateToLatestXcmVersion, parachains_inclusion::migration::MigrateToV1, + parachains_shared::migration::MigrateToV1, ); } @@ -2032,7 +2041,7 @@ sp_api::impl_runtime_apis! { parachains_runtime_api_impl::minimum_backing_votes::() } - fn para_backing_state(para_id: ParaId) -> Option { + fn para_backing_state(para_id: ParaId) -> Option { parachains_runtime_api_impl::backing_state::(para_id) } @@ -2053,11 +2062,11 @@ sp_api::impl_runtime_apis! { } fn claim_queue() -> BTreeMap> { - vstaging_parachains_runtime_api_impl::claim_queue::() + parachains_runtime_api_impl::claim_queue::() } fn candidates_pending_availability(para_id: ParaId) -> Vec> { - vstaging_parachains_runtime_api_impl::candidates_pending_availability::(para_id) + parachains_runtime_api_impl::candidates_pending_availability::(para_id) } } @@ -2578,13 +2587,7 @@ sp_api::impl_runtime_apis! { } fn preset_names() -> Vec { - vec![ - PresetId::from("local_testnet"), - PresetId::from("development"), - PresetId::from("staging_testnet"), - PresetId::from("wococo_local_testnet"), - PresetId::from("versi_local_testnet"), - ] + genesis_config_presets::preset_names() } } } diff --git a/polkadot/runtime/rococo/src/tests.rs b/polkadot/runtime/rococo/src/tests.rs index 464a8c4f5454dce32ec43388061c83056f0a260c..01eaad87e342479bec6341944ebc911ab3c73cdd 100644 --- a/polkadot/runtime/rococo/src/tests.rs +++ b/polkadot/runtime/rococo/src/tests.rs @@ -19,8 +19,11 @@ use crate::*; use std::collections::HashSet; +use crate::xcm_config::LocationConverter; use frame_support::traits::WhitelistedStorageKeys; -use sp_core::hexdisplay::HexDisplay; +use sp_core::{crypto::Ss58Codec, hexdisplay::HexDisplay}; +use sp_keyring::AccountKeyring::Alice; +use xcm_runtime_apis::conversions::LocationToAccountHelper; #[test] fn check_whitelist() { @@ -61,3 +64,76 @@ mod encoding_tests { assert_eq!(RuntimeHoldReason::Nis(pallet_nis::HoldReason::NftReceipt).encode(), [38, 0]); } } + +#[test] +fn location_conversion_works() { + // the purpose of hardcoded values is to catch an unintended location conversion logic change. + struct TestCase { + description: &'static str, + location: Location, + expected_account_id_str: &'static str, + } + + let test_cases = vec![ + // DescribeTerminus + TestCase { + description: "DescribeTerminus Child", + location: Location::new(0, [Parachain(1111)]), + expected_account_id_str: "5Ec4AhP4h37t7TFsAZ4HhFq6k92usAAJDUC3ADSZ4H4Acru3", + }, + // DescribePalletTerminal + TestCase { + description: "DescribePalletTerminal Child", + location: Location::new(0, [Parachain(1111), PalletInstance(50)]), + expected_account_id_str: "5FjEBrKn3STAFsZpQF4jzwxUYHNGnNgzdZqSQfTzeJ82XKp6", + }, + // DescribeAccountId32Terminal + TestCase { + description: "DescribeAccountId32Terminal Child", + location: Location::new( + 0, + [Parachain(1111), AccountId32 { network: None, id: AccountId::from(Alice).into() }], + ), + expected_account_id_str: "5EEMro9RRDpne4jn9TuD7cTB6Amv1raVZ3xspSkqb2BF3FJH", + }, + // DescribeAccountKey20Terminal + TestCase { + description: "DescribeAccountKey20Terminal Child", + location: Location::new( + 0, + [Parachain(1111), AccountKey20 { network: None, key: [0u8; 20] }], + ), + expected_account_id_str: "5HohjXdjs6afcYcgHHSstkrtGfxgfGKsnZ1jtewBpFiGu4DL", + }, + // DescribeTreasuryVoiceTerminal + TestCase { + description: "DescribeTreasuryVoiceTerminal Child", + location: Location::new( + 0, + [Parachain(1111), Plurality { id: BodyId::Treasury, part: BodyPart::Voice }], + ), + expected_account_id_str: "5GenE4vJgHvwYVcD6b4nBvH5HNY4pzpVHWoqwFpNMFT7a2oX", + }, + // DescribeBodyTerminal + TestCase { + description: "DescribeBodyTerminal Child", + location: Location::new( + 0, + [Parachain(1111), Plurality { id: BodyId::Unit, part: BodyPart::Voice }], + ), + expected_account_id_str: "5DPgGBFTTYm1dGbtB1VWHJ3T3ScvdrskGGx6vSJZNP1WNStV", + }, + ]; + + for tc in test_cases { + let expected = + AccountId::from_string(tc.expected_account_id_str).expect("Invalid AccountId string"); + + let got = LocationToAccountHelper::::convert_location( + tc.location.into(), + ) + .unwrap(); + + assert_eq!(got, expected, "{}", tc.description); + } +} diff --git a/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_inclusion.rs b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_inclusion.rs index 4c6ce8835573b9c256d3a723bc7213a7f7d8f563..5824658383becd3dd9502883e4e048a80f29102b 100644 --- a/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_inclusion.rs +++ b/polkadot/runtime/rococo/src/weights/polkadot_runtime_parachains_inclusion.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `polkadot_runtime_parachains::inclusion` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-29, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-svzsllib-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("rococo-dev")`, DB CACHE: 1024 // Executed Command: @@ -70,13 +70,13 @@ impl polkadot_runtime_parachains::inclusion::WeightInfo /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(55), added: 2530, mode: `MaxEncodedLen`) - /// Storage: `MessageQueue::Pages` (r:1 w:32) + /// Storage: `MessageQueue::Pages` (r:1 w:10) /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(32818), added: 35293, mode: `MaxEncodedLen`) - /// Storage: `Hrmp::HrmpChannelDigests` (r:33 w:33) + /// Storage: `Hrmp::HrmpChannelDigests` (r:11 w:11) /// Proof: `Hrmp::HrmpChannelDigests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Hrmp::HrmpChannels` (r:32 w:32) + /// Storage: `Hrmp::HrmpChannels` (r:10 w:10) /// Proof: `Hrmp::HrmpChannels` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Hrmp::HrmpChannelContents` (r:32 w:32) + /// Storage: `Hrmp::HrmpChannelContents` (r:10 w:10) /// Proof: `Hrmp::HrmpChannelContents` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Paras::FutureCodeUpgrades` (r:1 w:0) /// Proof: `Paras::FutureCodeUpgrades` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -96,28 +96,31 @@ impl polkadot_runtime_parachains::inclusion::WeightInfo /// Proof: `Paras::UpgradeRestrictionSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: UNKNOWN KEY `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) /// Proof: UNKNOWN KEY `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) - /// The range of component `u` is `[1, 32]`. - /// The range of component `h` is `[1, 32]`. + /// The range of component `u` is `[0, 10]`. + /// The range of component `h` is `[0, 10]`. /// The range of component `c` is `[0, 1]`. fn enact_candidate(u: u32, h: u32, c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `33353 + c * (16114 ยฑ0) + h * (75 ยฑ0)` - // Estimated: `36818 + c * (26467 ยฑ0) + h * (2551 ยฑ0)` - // Minimum execution time: 4_829_551_000 picoseconds. - Weight::from_parts(1_892_697_027, 0) - .saturating_add(Weight::from_parts(0, 36818)) - // Standard Error: 793_993 - .saturating_add(Weight::from_parts(126_698_671, 0).saturating_mul(u.into())) - // Standard Error: 793_993 - .saturating_add(Weight::from_parts(144_116_038, 0).saturating_mul(h.into())) - .saturating_add(T::DbWeight::get().reads(7)) + // Measured: `33352 + c * (16115 ยฑ0) + h * (76 ยฑ0)` + // Estimated: `36283 + c * (19327 ยฑ403) + h * (3057 ยฑ59) + u * (1314 ยฑ59)` + // Minimum execution time: 1_334_017_000 picoseconds. + Weight::from_parts(5_805_317, 0) + .saturating_add(Weight::from_parts(0, 36283)) + // Standard Error: 282_194 + .saturating_add(Weight::from_parts(128_332_196, 0).saturating_mul(u.into())) + // Standard Error: 282_194 + .saturating_add(Weight::from_parts(146_910_684, 0).saturating_mul(h.into())) + // Standard Error: 1_905_330 + .saturating_add(Weight::from_parts(91_514_854, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(h.into()))) .saturating_add(T::DbWeight::get().reads((8_u64).saturating_mul(c.into()))) - .saturating_add(T::DbWeight::get().writes(10)) + .saturating_add(T::DbWeight::get().writes(8)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(u.into()))) .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(h.into()))) .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(c.into()))) - .saturating_add(Weight::from_parts(0, 26467).saturating_mul(c.into())) - .saturating_add(Weight::from_parts(0, 2551).saturating_mul(h.into())) + .saturating_add(Weight::from_parts(0, 19327).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 3057).saturating_mul(h.into())) + .saturating_add(Weight::from_parts(0, 1314).saturating_mul(u.into())) } } diff --git a/polkadot/runtime/test-runtime/constants/src/weights/block_weights.rs b/polkadot/runtime/test-runtime/constants/src/weights/block_weights.rs index e7fdb2aae2a01ec06076de83d94817e540e205dd..07316759104f3998ce3c36e0adff9aece0261407 100644 --- a/polkadot/runtime/test-runtime/constants/src/weights/block_weights.rs +++ b/polkadot/runtime/test-runtime/constants/src/weights/block_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Polkadot. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/polkadot/runtime/test-runtime/constants/src/weights/extrinsic_weights.rs b/polkadot/runtime/test-runtime/constants/src/weights/extrinsic_weights.rs index 1a4adb968bb7195428ea00d59cd92dcd3b6eea5f..d0af4ec8921d90cbdcd09172c2497210caf2e7bd 100644 --- a/polkadot/runtime/test-runtime/constants/src/weights/extrinsic_weights.rs +++ b/polkadot/runtime/test-runtime/constants/src/weights/extrinsic_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Polkadot. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/polkadot/runtime/test-runtime/constants/src/weights/mod.rs b/polkadot/runtime/test-runtime/constants/src/weights/mod.rs index 30fa2c4060689ff98cc427c84f81866172845e52..d9087d7f057ed788d7b7d835d14e46955730e727 100644 --- a/polkadot/runtime/test-runtime/constants/src/weights/mod.rs +++ b/polkadot/runtime/test-runtime/constants/src/weights/mod.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Polkadot. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/polkadot/runtime/test-runtime/constants/src/weights/paritydb_weights.rs b/polkadot/runtime/test-runtime/constants/src/weights/paritydb_weights.rs index 25679703831a13b8d1bb7fb7dd4d92fa84b1f255..67d5286022ee0f19bc86f7fea2ac997c31f87cfa 100644 --- a/polkadot/runtime/test-runtime/constants/src/weights/paritydb_weights.rs +++ b/polkadot/runtime/test-runtime/constants/src/weights/paritydb_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Polkadot. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/polkadot/runtime/test-runtime/constants/src/weights/rocksdb_weights.rs b/polkadot/runtime/test-runtime/constants/src/weights/rocksdb_weights.rs index 3dd817aa6f137085b0e5fdf2b11b7f50e5c8b002..57f49e1202c16b7afd578fecc0ad63697a32e983 100644 --- a/polkadot/runtime/test-runtime/constants/src/weights/rocksdb_weights.rs +++ b/polkadot/runtime/test-runtime/constants/src/weights/rocksdb_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Polkadot. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/polkadot/runtime/test-runtime/src/lib.rs b/polkadot/runtime/test-runtime/src/lib.rs index 8e34320d38f2f26edc800343cf37217977e8bad1..6ba8e6ad31825c733c287d9ec8f08d5dc71ba162 100644 --- a/polkadot/runtime/test-runtime/src/lib.rs +++ b/polkadot/runtime/test-runtime/src/lib.rs @@ -33,13 +33,11 @@ use pallet_transaction_payment::FungibleAdapter; use polkadot_runtime_parachains::{ assigner_parachains as parachains_assigner_parachains, configuration as parachains_configuration, - configuration::ActiveConfigHrmpChannelSizeAndCapacityRatio, - disputes as parachains_disputes, - disputes::slashing as parachains_slashing, - dmp as parachains_dmp, hrmp as parachains_hrmp, inclusion as parachains_inclusion, - initializer as parachains_initializer, origin as parachains_origin, paras as parachains_paras, - paras_inherent as parachains_paras_inherent, - runtime_api_impl::{v10 as runtime_impl, vstaging as vstaging_parachains_runtime_api_impl}, + configuration::ActiveConfigHrmpChannelSizeAndCapacityRatio, disputes as parachains_disputes, + disputes::slashing as parachains_slashing, dmp as parachains_dmp, hrmp as parachains_hrmp, + inclusion as parachains_inclusion, initializer as parachains_initializer, + origin as parachains_origin, paras as parachains_paras, + paras_inherent as parachains_paras_inherent, runtime_api_impl::v11 as runtime_impl, scheduler as parachains_scheduler, session_info as parachains_session_info, shared as parachains_shared, }; @@ -59,10 +57,14 @@ use pallet_session::historical as session_historical; use pallet_timestamp::Now; use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo}; use polkadot_primitives::{ - slashing, AccountId, AccountIndex, Balance, BlockNumber, CandidateEvent, CandidateHash, - CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState, ExecutorParams, - GroupRotationInfo, Hash as HashT, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, - Moment, Nonce, OccupiedCoreAssumption, PersistedValidationData, ScrapedOnChainVotes, + slashing, + vstaging::{ + CandidateEvent, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + ScrapedOnChainVotes, + }, + AccountId, AccountIndex, Balance, BlockNumber, CandidateHash, CoreIndex, DisputeState, + ExecutorParams, GroupRotationInfo, Hash as HashT, Id as ParaId, InboundDownwardMessage, + InboundHrmpMessage, Moment, Nonce, OccupiedCoreAssumption, PersistedValidationData, SessionInfo as SessionInfoData, Signature, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex, PARACHAIN_KEY_TYPE_ID, }; @@ -121,7 +123,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 1, + system_version: 1, }; /// The BABE epoch configuration at genesis. @@ -232,6 +234,7 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<0>; + type DoneSlashHandler = (); } parameter_types! { @@ -978,7 +981,7 @@ sp_api::impl_runtime_apis! { runtime_impl::minimum_backing_votes::() } - fn para_backing_state(para_id: ParaId) -> Option { + fn para_backing_state(para_id: ParaId) -> Option { runtime_impl::backing_state::(para_id) } @@ -999,11 +1002,11 @@ sp_api::impl_runtime_apis! { } fn claim_queue() -> BTreeMap> { - vstaging_parachains_runtime_api_impl::claim_queue::() + runtime_impl::claim_queue::() } fn candidates_pending_availability(para_id: ParaId) -> Vec> { - vstaging_parachains_runtime_api_impl::candidates_pending_availability::(para_id) + runtime_impl::candidates_pending_availability::(para_id) } } diff --git a/polkadot/runtime/westend/Cargo.toml b/polkadot/runtime/westend/Cargo.toml index 83c0eb037f4a15de4634ccce14e1c7573e2671c1..28ffd9fb150a01ae1786d6938c6d32ad300a4e36 100644 --- a/polkadot/runtime/westend/Cargo.toml +++ b/polkadot/runtime/westend/Cargo.toml @@ -17,11 +17,13 @@ scale-info = { features = ["derive"], workspace = true } log = { workspace = true } serde = { workspace = true } serde_derive = { optional = true, workspace = true } +serde_json = { features = ["alloc"], workspace = true } smallvec = { workspace = true, default-features = true } sp-authority-discovery = { workspace = true } sp-consensus-babe = { workspace = true } sp-consensus-beefy = { workspace = true } +sp-consensus-grandpa = { workspace = true } binary-merkle-tree = { workspace = true } sp-inherents = { workspace = true } sp-offchain = { workspace = true } @@ -103,7 +105,7 @@ pallet-election-provider-support-benchmarking = { optional = true, workspace = t pallet-nomination-pools-benchmarking = { optional = true, workspace = true } pallet-offences-benchmarking = { optional = true, workspace = true } pallet-session-benchmarking = { optional = true, workspace = true } -hex-literal = { optional = true, workspace = true, default-features = true } +hex-literal = { workspace = true, default-features = true } polkadot-runtime-common = { workspace = true } polkadot-primitives = { workspace = true } @@ -116,7 +118,6 @@ xcm-builder = { workspace = true } xcm-runtime-apis = { workspace = true } [dev-dependencies] -hex-literal = { workspace = true, default-features = true } tiny-keccak = { features = ["keccak"], workspace = true } sp-keyring = { workspace = true, default-features = true } serde_json = { workspace = true, default-features = true } @@ -203,6 +204,7 @@ std = [ "scale-info/std", "serde/std", "serde_derive", + "serde_json/std", "sp-api/std", "sp-application-crypto/std", "sp-arithmetic/std", @@ -210,6 +212,7 @@ std = [ "sp-block-builder/std", "sp-consensus-babe/std", "sp-consensus-beefy/std", + "sp-consensus-grandpa/std", "sp-core/std", "sp-genesis-builder/std", "sp-inherents/std", @@ -236,7 +239,6 @@ runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system-benchmarking/runtime-benchmarks", "frame-system/runtime-benchmarks", - "hex-literal", "pallet-asset-rate/runtime-benchmarks", "pallet-babe/runtime-benchmarks", "pallet-bags-list/runtime-benchmarks", diff --git a/polkadot/runtime/westend/build.rs b/polkadot/runtime/westend/build.rs index 55ccd36401293342d2dff4ccca14b674f6b1aeec..cf4097a2da6c109a5279bb3b53a1d555d515558b 100644 --- a/polkadot/runtime/westend/build.rs +++ b/polkadot/runtime/westend/build.rs @@ -6,7 +6,7 @@ // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/polkadot/runtime/westend/constants/src/weights/mod.rs b/polkadot/runtime/westend/constants/src/weights/mod.rs index 23812ce7ed0528c394f84042fb9842eb617a834b..2648608a2f8af79f3830dee845f3079315eb001f 100644 --- a/polkadot/runtime/westend/constants/src/weights/mod.rs +++ b/polkadot/runtime/westend/constants/src/weights/mod.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Polkadot. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/polkadot/runtime/westend/constants/src/weights/paritydb_weights.rs b/polkadot/runtime/westend/constants/src/weights/paritydb_weights.rs index 25679703831a13b8d1bb7fb7dd4d92fa84b1f255..67d5286022ee0f19bc86f7fea2ac997c31f87cfa 100644 --- a/polkadot/runtime/westend/constants/src/weights/paritydb_weights.rs +++ b/polkadot/runtime/westend/constants/src/weights/paritydb_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Polkadot. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/polkadot/runtime/westend/constants/src/weights/rocksdb_weights.rs b/polkadot/runtime/westend/constants/src/weights/rocksdb_weights.rs index 3dd817aa6f137085b0e5fdf2b11b7f50e5c8b002..57f49e1202c16b7afd578fecc0ad63697a32e983 100644 --- a/polkadot/runtime/westend/constants/src/weights/rocksdb_weights.rs +++ b/polkadot/runtime/westend/constants/src/weights/rocksdb_weights.rs @@ -1,4 +1,4 @@ -// This file is part of Substrate. +// This file is part of Polkadot. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 diff --git a/polkadot/runtime/westend/src/genesis_config_presets.rs b/polkadot/runtime/westend/src/genesis_config_presets.rs new file mode 100644 index 0000000000000000000000000000000000000000..f59bacce31bd11f0d46f72a06865fd55f11846c5 --- /dev/null +++ b/polkadot/runtime/westend/src/genesis_config_presets.rs @@ -0,0 +1,459 @@ +// 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 . + +//! Genesis configs presets for the Westend runtime + +use crate::{ + BabeConfig, BalancesConfig, ConfigurationConfig, RegistrarConfig, RuntimeGenesisConfig, + SessionConfig, SessionKeys, StakingConfig, SudoConfig, BABE_GENESIS_EPOCH_CONFIG, +}; +#[cfg(not(feature = "std"))] +use alloc::format; +use alloc::{vec, vec::Vec}; +use pallet_staking::{Forcing, StakerStatus}; +use polkadot_primitives::{AccountId, AccountPublic, AssignmentId, SchedulerParams, ValidatorId}; +use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; +use sp_consensus_babe::AuthorityId as BabeId; +use sp_consensus_beefy::ecdsa_crypto::AuthorityId as BeefyId; +use sp_consensus_grandpa::AuthorityId as GrandpaId; +use sp_core::{sr25519, Pair, Public}; +use sp_genesis_builder::PresetId; +use sp_runtime::{traits::IdentifyAccount, Perbill}; +use westend_runtime_constants::currency::UNITS as WND; + +/// Helper function to generate a crypto pair from seed +fn get_from_seed(seed: &str) -> ::Public { + TPublic::Pair::from_string(&format!("//{}", seed), None) + .expect("static values are valid; qed") + .public() +} + +/// Helper function to generate an account ID from seed +fn get_account_id_from_seed(seed: &str) -> AccountId +where + AccountPublic: From<::Public>, +{ + AccountPublic::from(get_from_seed::(seed)).into_account() +} + +/// Helper function to generate stash, controller and session key from seed +fn get_authority_keys_from_seed( + seed: &str, +) -> ( + AccountId, + AccountId, + BabeId, + GrandpaId, + ValidatorId, + AssignmentId, + AuthorityDiscoveryId, + BeefyId, +) { + let keys = get_authority_keys_from_seed_no_beefy(seed); + (keys.0, keys.1, keys.2, keys.3, keys.4, keys.5, keys.6, get_from_seed::(seed)) +} + +/// Helper function to generate stash, controller and session key from seed +fn get_authority_keys_from_seed_no_beefy( + seed: &str, +) -> (AccountId, AccountId, BabeId, GrandpaId, ValidatorId, AssignmentId, AuthorityDiscoveryId) { + ( + 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), + ) +} + +fn testnet_accounts() -> Vec { + Vec::from([ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ]) +} + +fn westend_session_keys( + babe: BabeId, + grandpa: GrandpaId, + para_validator: ValidatorId, + para_assignment: AssignmentId, + authority_discovery: AuthorityDiscoveryId, + beefy: BeefyId, +) -> SessionKeys { + SessionKeys { babe, grandpa, para_validator, para_assignment, authority_discovery, beefy } +} + +fn default_parachains_host_configuration( +) -> polkadot_runtime_parachains::configuration::HostConfiguration +{ + use polkadot_primitives::{ + node_features::FeatureIndex, ApprovalVotingParams, AsyncBackingParams, MAX_CODE_SIZE, + MAX_POV_SIZE, + }; + + polkadot_runtime_parachains::configuration::HostConfiguration { + validation_upgrade_cooldown: 2u32, + validation_upgrade_delay: 2, + code_retention_period: 1200, + max_code_size: MAX_CODE_SIZE, + max_pov_size: MAX_POV_SIZE, + max_head_data_size: 32 * 1024, + max_upward_queue_count: 8, + max_upward_queue_size: 1024 * 1024, + max_downward_message_size: 1024 * 1024, + max_upward_message_size: 50 * 1024, + max_upward_message_num_per_candidate: 5, + hrmp_sender_deposit: 0, + hrmp_recipient_deposit: 0, + hrmp_channel_max_capacity: 8, + hrmp_channel_max_total_size: 8 * 1024, + hrmp_max_parachain_inbound_channels: 4, + hrmp_channel_max_message_size: 1024 * 1024, + hrmp_max_parachain_outbound_channels: 4, + hrmp_max_message_num_per_candidate: 5, + dispute_period: 6, + no_show_slots: 2, + n_delay_tranches: 25, + needed_approvals: 2, + 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) | + 1u8 << (FeatureIndex::EnableAssignmentsV2 as usize), + ), + scheduler_params: SchedulerParams { + lookahead: 2, + group_rotation_frequency: 20, + paras_availability_period: 4, + ..Default::default() + }, + approval_voting_params: ApprovalVotingParams { max_approval_coalesce_count: 5 }, + ..Default::default() + } +} + +#[test] +fn default_parachains_host_configuration_is_consistent() { + default_parachains_host_configuration().panic_if_not_consistent(); +} + +/// Helper function to create westend runtime `GenesisConfig` patch for testing +fn westend_testnet_genesis( + initial_authorities: Vec<( + AccountId, + AccountId, + BabeId, + GrandpaId, + ValidatorId, + AssignmentId, + AuthorityDiscoveryId, + BeefyId, + )>, + root_key: AccountId, + endowed_accounts: Option>, +) -> serde_json::Value { + let endowed_accounts: Vec = endowed_accounts.unwrap_or_else(testnet_accounts); + + const ENDOWMENT: u128 = 1_000_000 * WND; + const STASH: u128 = 100 * WND; + + let config = RuntimeGenesisConfig { + balances: BalancesConfig { + balances: endowed_accounts.iter().map(|k| (k.clone(), ENDOWMENT)).collect::>(), + }, + session: SessionConfig { + keys: initial_authorities + .iter() + .map(|x| { + ( + x.0.clone(), + x.0.clone(), + westend_session_keys( + x.2.clone(), + x.3.clone(), + x.4.clone(), + x.5.clone(), + x.6.clone(), + x.7.clone(), + ), + ) + }) + .collect::>(), + ..Default::default() + }, + staking: StakingConfig { + minimum_validator_count: 1, + validator_count: initial_authorities.len() as u32, + stakers: initial_authorities + .iter() + .map(|x| (x.0.clone(), x.0.clone(), STASH, StakerStatus::::Validator)) + .collect::>(), + invulnerables: initial_authorities.iter().map(|x| x.0.clone()).collect::>(), + force_era: Forcing::NotForcing, + slash_reward_fraction: Perbill::from_percent(10), + ..Default::default() + }, + babe: BabeConfig { epoch_config: BABE_GENESIS_EPOCH_CONFIG, ..Default::default() }, + sudo: SudoConfig { key: Some(root_key) }, + configuration: ConfigurationConfig { config: default_parachains_host_configuration() }, + registrar: RegistrarConfig { + next_free_para_id: polkadot_primitives::LOWEST_PUBLIC_ID, + ..Default::default() + }, + ..Default::default() + }; + + serde_json::to_value(config).expect("Could not build genesis config.") +} + +// staging_testnet +fn westend_staging_testnet_config_genesis() -> serde_json::Value { + use hex_literal::hex; + use sp_core::crypto::UncheckedInto; + + // Following keys are used in genesis config for development chains. + // DO NOT use them in production chains as the secret seed is public. + // + // SECRET_SEED="slow awkward present example safe bundle science ocean cradle word tennis earn" + // subkey inspect -n polkadot "$SECRET_SEED" + let endowed_accounts = vec![ + // 15S75FkhCWEowEGfxWwVfrW3LQuy8w8PNhVmrzfsVhCMjUh1 + hex!["c416837e232d9603e83162ef4bda08e61580eeefe60fe92fc044aa508559ae42"].into(), + ]; + // SECRET=$SECRET_SEED ./scripts/prepare-test-net.sh 4 + let initial_authorities: Vec<( + AccountId, + AccountId, + BabeId, + GrandpaId, + ValidatorId, + AssignmentId, + AuthorityDiscoveryId, + BeefyId, + )> = Vec::from([ + ( + //5EvydUTtHvt39Khac3mMxNPgzcfu49uPDzUs3TL7KEzyrwbw + hex!["7ecfd50629cdd246649959d88d490b31508db511487e111a52a392e6e458f518"].into(), + //5HQyX5gyy77m9QLXguAhiwjTArHYjYspeY98dYDu1JDetfZg + hex!["eca2cca09bdc66a7e6d8c3d9499a0be2ad4690061be8a9834972e17d13d2fe7e"].into(), + //5G13qYRudTyttwTJvHvnwp8StFtcfigyPnwfD4v7LNopsnX4 + hex!["ae27367cb77850fb195fe1f9c60b73210409e68c5ad953088070f7d8513d464c"] + .unchecked_into(), + //5Eb7wM65PNgtY6e33FEAzYtU5cRTXt6WQvZTnzaKQwkVcABk + hex!["6faae44b21c6f2681a7f60df708e9f79d340f7d441d28bd987fab8d05c6487e8"] + .unchecked_into(), + //5FqMLAgygdX9UqzukDp15Uid9PAKdFAR621U7xtp5ut2NfrW + hex!["a6c1a5b501985a83cb1c37630c5b41e6b0a15b3675b2fd94694758e6cfa6794d"] + .unchecked_into(), + //5DhXAV75BKvF9o447ikWqLttyL2wHtLMFSX7GrsKF9Ny61Ta + hex!["485051748ab9c15732f19f3fbcf1fd00a6d9709635f084505107fbb059c33d2f"] + .unchecked_into(), + //5GNHfmrtWLTawnGCmc39rjAEiW97vKvE7DGePYe4am5JtE4i + hex!["be59ed75a72f7b47221ce081ba4262cf2e1ea7867e30e0b3781822f942b97677"] + .unchecked_into(), + //5DA6Z8RUF626stn94aTRBCeobDCYcFbU7Pdk4Tz1R9vA8B8F + hex!["0207e43990799e1d02b0507451e342a1240ff836ea769c57297589a5fd072ad8f4"] + .unchecked_into(), + ), + ( + //5DFpvDUdCgw54E3E357GR1PyJe3Ft9s7Qyp7wbELAoJH9RQa + hex!["34b7b3efd35fcc3c1926ca065381682b1af29b57dabbcd091042c6de1d541b7d"].into(), + //5DZSSsND5wCjngvyXv27qvF3yPzt3MCU8rWnqNy4imqZmjT8 + hex!["4226796fa792ac78875e023ff2e30e3c2cf79f0b7b3431254cd0f14a3007bc0e"].into(), + //5CPrgfRNDQvQSnLRdeCphP3ibj5PJW9ESbqj2fw29vBMNQNn + hex!["0e9b60f04be3bffe362eb2212ea99d2b909b052f4bff7c714e13c2416a797f5d"] + .unchecked_into(), + //5FXFsPReTUEYPRNKhbTdUathcWBsxTNsLbk2mTpYdKCJewjA + hex!["98f4d81cb383898c2c3d54dab28698c0f717c81b509cb32dc6905af3cc697b18"] + .unchecked_into(), + //5CZjurB78XbSHf6SLkLhCdkqw52Zm7aBYUDdfkLqEDWJ9Zhj + hex!["162508accd470e379b04cb0c7c60b35a7d5357e84407a89ed2dd48db4b726960"] + .unchecked_into(), + //5DkAqCtSjUMVoJFauuGoAbSEgn2aFCRGziKJiLGpPwYgE1pS + hex!["4a559c028b69a7f784ce553393e547bec0aa530352157603396d515f9c83463b"] + .unchecked_into(), + //5GsBt9MhGwkg8Jfb1F9LAy2kcr88WNyNy4L5ezwbCr8NWKQU + hex!["d464908266c878acbf181bf8fda398b3aa3fd2d05508013e414aaece4cf0d702"] + .unchecked_into(), + //5DtJVkz8AHevEnpszy3X4dUcPvACW6x1qBMQZtFxjexLr5bq + hex!["02fdf30222d2cb88f2376d558d3de9cb83f9fde3aa4b2dd40c93e3104e3488bcd2"] + .unchecked_into(), + ), + ( + //5E2cob2jrXsBkTih56pizwSqENjE4siaVdXhaD6akLdDyVq7 + hex!["56e0f73c563d49ee4a3971c393e17c44eaa313dabad7fcf297dc3271d803f303"].into(), + //5D4rNYgP9uFNi5GMyDEXTfiaFLjXyDEEX2VvuqBVi3f1qgCh + hex!["2c58e5e1d5aef77774480cead4f6876b1a1a6261170166995184d7f86140572b"].into(), + //5Ea2D65KXqe625sz4uV1jjhSfuigVnkezC8VgEj9LXN7ERAk + hex!["6ed45cb7af613be5d88a2622921e18d147225165f24538af03b93f2a03ce6e13"] + .unchecked_into(), + //5G4kCbgqUhEyrRHCyFwFEkgBZXoYA8sbgsRxT9rY8Tp5Jj5F + hex!["b0f8d2b9e4e1eafd4dab6358e0b9d5380d78af27c094e69ae9d6d30ca300fd86"] + .unchecked_into(), + //5CS7thd2n54WfqeKU3cjvZzK4z5p7zku1Zw97mSzXgPioAAs + hex!["1055100a283968271a0781450b389b9093231be809be1e48a305ebad2a90497e"] + .unchecked_into(), + //5DSaL4ZmSYarZSazhL5NQh7LT6pWhNRDcefk2QS9RxEXfsJe + hex!["3cea4ab74bab4adf176cf05a6e18c1599a7bc217d4c6c217275bfbe3b037a527"] + .unchecked_into(), + //5CaNLkYEbFYXZodXhd3UjV6RNLjFGNLiYafc8X5NooMkZiAq + hex!["169faa81aebfe74533518bda28567f2e2664014c8905aa07ea003336afda5a58"] + .unchecked_into(), + //5ERwhKiePayukzZStMuzGzRJGxGRFpwxYUXVarQpMSMrXzDS + hex!["03429d0d20f6ac5ca8b349f04d014f7b5b864acf382a744104d5d9a51108156c0f"] + .unchecked_into(), + ), + ( + //5H6j9ovzYk9opckVjvM9SvVfaK37ASTtPTzWeRfqk1tgLJUN + hex!["deb804ed2ed2bb696a3dd4ed7de4cd5c496528a2b204051c6ace385bacd66a3a"].into(), + //5DJ51tMW916mGwjMpfS1o9skcNt6Sb28YnZQXaKVg4h89agE + hex!["366da6a748afedb31f07902f2de36ab265beccee37762d3ae1f237de234d9c36"].into(), + //5CSPYDYoCDGSoSLgSp4EHkJ52YasZLHG2woqhPZkdbtNQpke + hex!["1089bc0cd60237d061872925e81d36c9d9205d250d5d8b542c8e08a8ecf1b911"] + .unchecked_into(), + //5ChfdrAqmLjCeDJvynbMjcxYLHYzPe8UWXd3HnX9JDThUMbn + hex!["1c309a70b4e274314b84c9a0a1f973c9c4fc084df5479ef686c54b1ae4950424"] + .unchecked_into(), + //5D8C3HHEp5E8fJsXRD56494F413CdRSR9QKGXe7v5ZEfymdj + hex!["2ee4d78f328db178c54f205ac809da12e291a33bcbd4f29f081ce7e74bdc5044"] + .unchecked_into(), + //5GxeTYCGmp1C3ZRLDkRWqJc6gB2GYmuqnygweuH3vsivMQq6 + hex!["d88e40e3c2c7a7c5abf96ffdd8f7b7bec8798cc277bc97e255881871ab73b529"] + .unchecked_into(), + //5DoGpsgSLcJsHa9B8V4PKjxegWAqDZttWfxicAd68prUX654 + hex!["4cb3863271b70daa38612acd5dae4f5afcb7c165fa277629e5150d2214df322a"] + .unchecked_into(), + //5G1KLjqFyMsPAodnjSRkwRFJztTTEzmZWxow2Q3ZSRCPdthM + hex!["03be5ec86d10a94db89c9b7a396d3c7742e3bec5f85159d4cf308cef505966ddf5"] + .unchecked_into(), + ), + ]); + + const ENDOWMENT: u128 = 1_000_000 * WND; + const STASH: u128 = 100 * WND; + + let config = RuntimeGenesisConfig { + balances: BalancesConfig { + balances: endowed_accounts + .iter() + .map(|k: &AccountId| (k.clone(), ENDOWMENT)) + .chain(initial_authorities.iter().map(|x| (x.0.clone(), STASH))) + .collect::>(), + }, + session: SessionConfig { + keys: initial_authorities + .iter() + .map(|x| { + ( + x.0.clone(), + x.0.clone(), + westend_session_keys( + x.2.clone(), + x.3.clone(), + x.4.clone(), + x.5.clone(), + x.6.clone(), + x.7.clone(), + ), + ) + }) + .collect::>(), + ..Default::default() + }, + staking: StakingConfig { + validator_count: 50, + minimum_validator_count: 4, + stakers: initial_authorities + .iter() + .map(|x| (x.0.clone(), x.0.clone(), STASH, StakerStatus::::Validator)) + .collect::>(), + invulnerables: initial_authorities.iter().map(|x| x.0.clone()).collect::>(), + force_era: Forcing::ForceNone, + slash_reward_fraction: Perbill::from_percent(10), + ..Default::default() + }, + babe: BabeConfig { epoch_config: BABE_GENESIS_EPOCH_CONFIG, ..Default::default() }, + sudo: SudoConfig { key: Some(endowed_accounts[0].clone()) }, + configuration: ConfigurationConfig { config: default_parachains_host_configuration() }, + registrar: RegistrarConfig { + next_free_para_id: polkadot_primitives::LOWEST_PUBLIC_ID, + ..Default::default() + }, + ..Default::default() + }; + + serde_json::to_value(config).expect("Could not build genesis config.") +} + +//development +fn westend_development_config_genesis() -> serde_json::Value { + westend_testnet_genesis( + Vec::from([get_authority_keys_from_seed("Alice")]), + get_account_id_from_seed::("Alice"), + None, + ) +} + +//local_testnet +fn westend_local_testnet_genesis() -> serde_json::Value { + westend_testnet_genesis( + Vec::from([get_authority_keys_from_seed("Alice"), get_authority_keys_from_seed("Bob")]), + get_account_id_from_seed::("Alice"), + None, + ) +} + +/// Provides the JSON representation of predefined genesis config for given `id`. +pub fn get_preset(id: &PresetId) -> Option> { + let patch = match id.try_into() { + Ok(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET) => westend_local_testnet_genesis(), + Ok(sp_genesis_builder::DEV_RUNTIME_PRESET) => westend_development_config_genesis(), + Ok("staging_testnet") => westend_staging_testnet_config_genesis(), + _ => return None, + }; + Some( + serde_json::to_string(&patch) + .expect("serialization to json is expected to work. qed.") + .into_bytes(), + ) +} + +/// List of supported presets. +pub fn preset_names() -> Vec { + vec![ + PresetId::from(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET), + PresetId::from(sp_genesis_builder::DEV_RUNTIME_PRESET), + PresetId::from("staging_testnet"), + ] +} diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index e8fe11615d743a914aceaaa3445d9c6f1940054d..fe1777bc94eef9ee0e0fde69f758cd1bd18aaa61 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -49,12 +49,16 @@ use pallet_identity::legacy::IdentityInfo; use pallet_session::historical as session_historical; use pallet_transaction_payment::{FeeDetails, FungibleAdapter, RuntimeDispatchInfo}; use polkadot_primitives::{ - slashing, AccountId, AccountIndex, ApprovalVotingParams, Balance, BlockNumber, CandidateEvent, - CandidateHash, CommittedCandidateReceipt, CoreIndex, CoreState, DisputeState, ExecutorParams, - GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage, Moment, - NodeFeatures, Nonce, OccupiedCoreAssumption, PersistedValidationData, PvfCheckStatement, - ScrapedOnChainVotes, SessionInfo, Signature, ValidationCode, ValidationCodeHash, ValidatorId, - ValidatorIndex, ValidatorSignature, PARACHAIN_KEY_TYPE_ID, + slashing, + vstaging::{ + CandidateEvent, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, CoreState, + ScrapedOnChainVotes, + }, + AccountId, AccountIndex, ApprovalVotingParams, Balance, BlockNumber, CandidateHash, CoreIndex, + DisputeState, ExecutorParams, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, + InboundHrmpMessage, Moment, NodeFeatures, Nonce, OccupiedCoreAssumption, + PersistedValidationData, PvfCheckStatement, SessionInfo, Signature, ValidationCode, + ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, PARACHAIN_KEY_TYPE_ID, }; use polkadot_runtime_common::{ assigned_slots, auctions, crowdloan, @@ -79,9 +83,7 @@ use polkadot_runtime_parachains::{ initializer as parachains_initializer, on_demand as parachains_on_demand, origin as parachains_origin, paras as parachains_paras, paras_inherent as parachains_paras_inherent, reward_points as parachains_reward_points, - runtime_api_impl::{ - v10 as parachains_runtime_api_impl, vstaging as vstaging_parachains_runtime_api_impl, - }, + runtime_api_impl::v11 as parachains_runtime_api_impl, scheduler as parachains_scheduler, session_info as parachains_session_info, shared as parachains_shared, }; @@ -116,8 +118,6 @@ use xcm_runtime_apis::{ pub use frame_system::Call as SystemCall; pub use pallet_balances::Call as BalancesCall; pub use pallet_election_provider_multi_phase::{Call as EPMCall, GeometricDepositBase}; -#[cfg(feature = "std")] -pub use pallet_staking::StakerStatus; use pallet_staking::UseValidatorsMap; pub use pallet_timestamp::Call as TimestampCall; use sp_runtime::traits::Get; @@ -133,6 +133,7 @@ use westend_runtime_constants::{ }; mod bag_thresholds; +mod genesis_config_presets; mod weights; pub mod xcm_config; @@ -167,11 +168,11 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("westend"), impl_name: create_runtime_str!("parity-westend"), authoring_version: 2, - spec_version: 1_015_000, + spec_version: 1_016_001, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 26, - state_version: 1, + system_version: 1, }; /// The BABE epoch configuration at genesis. @@ -398,6 +399,7 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = RuntimeFreezeReason; type MaxFreezes = VariantCountOf; + type DoneSlashHandler = (); } parameter_types! { @@ -1109,7 +1111,8 @@ impl InstanceFilter for ProxyType { matches!( c, RuntimeCall::Staking(..) | - RuntimeCall::Session(..) | RuntimeCall::Utility(..) | + RuntimeCall::Session(..) | + RuntimeCall::Utility(..) | RuntimeCall::FastUnstake(..) | RuntimeCall::VoterList(..) | RuntimeCall::NominationPools(..) @@ -1782,6 +1785,7 @@ pub mod migrations { Runtime, MaxAgentsToMigrate, >, + parachains_shared::migration::MigrateToV1, ); } @@ -2064,7 +2068,7 @@ sp_api::impl_runtime_apis! { parachains_runtime_api_impl::minimum_backing_votes::() } - fn para_backing_state(para_id: ParaId) -> Option { + fn para_backing_state(para_id: ParaId) -> Option { parachains_runtime_api_impl::backing_state::(para_id) } @@ -2085,11 +2089,11 @@ sp_api::impl_runtime_apis! { } fn claim_queue() -> BTreeMap> { - vstaging_parachains_runtime_api_impl::claim_queue::() + parachains_runtime_api_impl::claim_queue::() } fn candidates_pending_availability(para_id: ParaId) -> Vec> { - vstaging_parachains_runtime_api_impl::candidates_pending_availability::(para_id) + parachains_runtime_api_impl::candidates_pending_availability::(para_id) } } @@ -2752,57 +2756,11 @@ sp_api::impl_runtime_apis! { } fn get_preset(id: &Option) -> Option> { - get_preset::(id, |_| None) + get_preset::(id, &genesis_config_presets::get_preset) } fn preset_names() -> Vec { - vec![] - } - } -} - -mod clean_state_migration { - use super::Runtime; - #[cfg(feature = "try-runtime")] - use super::Vec; - use frame_support::{pallet_prelude::*, storage_alias, traits::OnRuntimeUpgrade}; - use pallet_state_trie_migration::MigrationLimits; - - #[storage_alias] - type AutoLimits = StorageValue, ValueQuery>; - - // Actual type of value is `MigrationTask`, putting a dummy - // one to avoid the trait constraint on T. - // Since we only use `kill` it is fine. - #[storage_alias] - type MigrationProcess = StorageValue; - - #[storage_alias] - type SignedMigrationMaxLimits = StorageValue; - - /// Initialize an automatic migration process. - pub struct CleanMigrate; - - impl OnRuntimeUpgrade for CleanMigrate { - #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { - Ok(Default::default()) - } - - fn on_runtime_upgrade() -> frame_support::weights::Weight { - MigrationProcess::kill(); - AutoLimits::kill(); - SignedMigrationMaxLimits::kill(); - ::DbWeight::get().writes(3) - } - - #[cfg(feature = "try-runtime")] - fn post_upgrade(_state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { - frame_support::ensure!( - !AutoLimits::exists() && !SignedMigrationMaxLimits::exists(), - "State migration clean.", - ); - Ok(()) + genesis_config_presets::preset_names() } } } diff --git a/polkadot/runtime/westend/src/tests.rs b/polkadot/runtime/westend/src/tests.rs index dc8103ab52c474a8f24b288add5b0cd9880265fc..eef95006bce45b30abc16cb8064cda6a21cce65f 100644 --- a/polkadot/runtime/westend/src/tests.rs +++ b/polkadot/runtime/westend/src/tests.rs @@ -18,9 +18,11 @@ use std::collections::HashSet; -use crate::*; +use crate::{xcm_config::LocationConverter, *}; use frame_support::traits::WhitelistedStorageKeys; -use sp_core::hexdisplay::HexDisplay; +use sp_core::{crypto::Ss58Codec, hexdisplay::HexDisplay}; +use sp_keyring::AccountKeyring::Alice; +use xcm_runtime_apis::conversions::LocationToAccountHelper; #[test] fn remove_keys_weight_is_sensible() { @@ -236,3 +238,76 @@ mod remote_tests { }); } } + +#[test] +fn location_conversion_works() { + // the purpose of hardcoded values is to catch an unintended location conversion logic change. + struct TestCase { + description: &'static str, + location: Location, + expected_account_id_str: &'static str, + } + + let test_cases = vec![ + // DescribeTerminus + TestCase { + description: "DescribeTerminus Child", + location: Location::new(0, [Parachain(1111)]), + expected_account_id_str: "5Ec4AhP4h37t7TFsAZ4HhFq6k92usAAJDUC3ADSZ4H4Acru3", + }, + // DescribePalletTerminal + TestCase { + description: "DescribePalletTerminal Child", + location: Location::new(0, [Parachain(1111), PalletInstance(50)]), + expected_account_id_str: "5FjEBrKn3STAFsZpQF4jzwxUYHNGnNgzdZqSQfTzeJ82XKp6", + }, + // DescribeAccountId32Terminal + TestCase { + description: "DescribeAccountId32Terminal Child", + location: Location::new( + 0, + [Parachain(1111), AccountId32 { network: None, id: AccountId::from(Alice).into() }], + ), + expected_account_id_str: "5EEMro9RRDpne4jn9TuD7cTB6Amv1raVZ3xspSkqb2BF3FJH", + }, + // DescribeAccountKey20Terminal + TestCase { + description: "DescribeAccountKey20Terminal Child", + location: Location::new( + 0, + [Parachain(1111), AccountKey20 { network: None, key: [0u8; 20] }], + ), + expected_account_id_str: "5HohjXdjs6afcYcgHHSstkrtGfxgfGKsnZ1jtewBpFiGu4DL", + }, + // DescribeTreasuryVoiceTerminal + TestCase { + description: "DescribeTreasuryVoiceTerminal Child", + location: Location::new( + 0, + [Parachain(1111), Plurality { id: BodyId::Treasury, part: BodyPart::Voice }], + ), + expected_account_id_str: "5GenE4vJgHvwYVcD6b4nBvH5HNY4pzpVHWoqwFpNMFT7a2oX", + }, + // DescribeBodyTerminal + TestCase { + description: "DescribeBodyTerminal Child", + location: Location::new( + 0, + [Parachain(1111), Plurality { id: BodyId::Unit, part: BodyPart::Voice }], + ), + expected_account_id_str: "5DPgGBFTTYm1dGbtB1VWHJ3T3ScvdrskGGx6vSJZNP1WNStV", + }, + ]; + + for tc in test_cases { + let expected = + AccountId::from_string(tc.expected_account_id_str).expect("Invalid AccountId string"); + + let got = LocationToAccountHelper::::convert_location( + tc.location.into(), + ) + .unwrap(); + + assert_eq!(got, expected, "{}", tc.description); + } +} diff --git a/polkadot/runtime/westend/src/weights/mod.rs b/polkadot/runtime/westend/src/weights/mod.rs index 1e7b01bc472b2227ce489b4477018d7b4fabde53..ae11f6ccba41852cb16a7dddb911cdd4cdcab19f 100644 --- a/polkadot/runtime/westend/src/weights/mod.rs +++ b/polkadot/runtime/westend/src/weights/mod.rs @@ -33,7 +33,6 @@ pub mod pallet_nomination_pools; pub mod pallet_parameters; pub mod pallet_preimage; pub mod pallet_proxy; -pub mod pallet_referenda_fellowship_referenda; pub mod pallet_referenda_referenda; pub mod pallet_scheduler; pub mod pallet_session; diff --git a/polkadot/runtime/westend/src/weights/pallet_referenda_fellowship_referenda.rs b/polkadot/runtime/westend/src/weights/pallet_referenda_fellowship_referenda.rs deleted file mode 100644 index a4ac066791168a67b17289a1c87b1ede681f92ad..0000000000000000000000000000000000000000 --- a/polkadot/runtime/westend/src/weights/pallet_referenda_fellowship_referenda.rs +++ /dev/null @@ -1,525 +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_referenda` -//! -//! 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: `[]` -//! 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 - -// Executed Command: -// ./target/production/polkadot -// benchmark -// pallet -// --chain=kusama-dev -// --steps=50 -// --repeat=20 -// --no-storage-info -// --no-median-slopes -// --no-min-squares -// --pallet=pallet_referenda -// --extrinsic=* -// --execution=wasm -// --wasm-execution=compiled -// --header=./file_header.txt -// --output=./runtime/kusama/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_referenda`. -pub struct WeightInfo(PhantomData); -impl pallet_referenda::WeightInfo for WeightInfo { - /// Storage: FellowshipCollective Members (r:1 w:0) - /// Proof: FellowshipCollective Members (max_values: None, max_size: Some(42), added: 2517, mode: MaxEncodedLen) - /// Storage: FellowshipReferenda ReferendumCount (r:1 w:1) - /// Proof: FellowshipReferenda 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(38963), added: 41438, mode: MaxEncodedLen) - /// Storage: FellowshipReferenda ReferendumInfoFor (r:0 w:1) - /// Proof: FellowshipReferenda ReferendumInfoFor (max_values: None, max_size: Some(900), added: 3375, mode: MaxEncodedLen) - fn submit() -> Weight { - // Proof Size summary in bytes: - // Measured: `327` - // Estimated: `42428` - // Minimum execution time: 28_969_000 picoseconds. - Weight::from_parts(30_902_000, 0) - .saturating_add(Weight::from_parts(0, 42428)) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) - } - /// Storage: FellowshipReferenda ReferendumInfoFor (r:1 w:1) - /// 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) - fn place_decision_deposit_preparing() -> Weight { - // Proof Size summary in bytes: - // Measured: `404` - // Estimated: `83866` - // Minimum execution time: 53_500_000 picoseconds. - Weight::from_parts(54_447_000, 0) - .saturating_add(Weight::from_parts(0, 83866)) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) - } - /// Storage: FellowshipReferenda ReferendumInfoFor (r:1 w:1) - /// Proof: FellowshipReferenda ReferendumInfoFor (max_values: None, max_size: Some(900), added: 3375, mode: MaxEncodedLen) - /// Storage: FellowshipReferenda DecidingCount (r:1 w:0) - /// Proof: FellowshipReferenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: FellowshipReferenda TrackQueue (r:1 w:1) - /// 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) - fn place_decision_deposit_queued() -> Weight { - // Proof Size summary in bytes: - // Measured: `2042` - // Estimated: `42428` - // Minimum execution time: 114_321_000 picoseconds. - Weight::from_parts(122_607_000, 0) - .saturating_add(Weight::from_parts(0, 42428)) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(3)) - } - /// Storage: FellowshipReferenda ReferendumInfoFor (r:1 w:1) - /// Proof: FellowshipReferenda ReferendumInfoFor (max_values: None, max_size: Some(900), added: 3375, mode: MaxEncodedLen) - /// Storage: FellowshipReferenda DecidingCount (r:1 w:0) - /// Proof: FellowshipReferenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: FellowshipReferenda TrackQueue (r:1 w:1) - /// 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) - fn place_decision_deposit_not_queued() -> Weight { - // Proof Size summary in bytes: - // Measured: `2083` - // Estimated: `42428` - // Minimum execution time: 113_476_000 picoseconds. - Weight::from_parts(120_078_000, 0) - .saturating_add(Weight::from_parts(0, 42428)) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(3)) - } - /// Storage: FellowshipReferenda ReferendumInfoFor (r:1 w:1) - /// Proof: FellowshipReferenda ReferendumInfoFor (max_values: None, max_size: Some(900), added: 3375, mode: MaxEncodedLen) - /// Storage: FellowshipReferenda DecidingCount (r:1 w:1) - /// Proof: FellowshipReferenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: FellowshipCollective MemberCount (r:1 w:0) - /// 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) - fn place_decision_deposit_passing() -> Weight { - // Proof Size summary in bytes: - // Measured: `774` - // Estimated: `83866` - // Minimum execution time: 194_798_000 picoseconds. - Weight::from_parts(208_378_000, 0) - .saturating_add(Weight::from_parts(0, 83866)) - .saturating_add(T::DbWeight::get().reads(5)) - .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) - /// Storage: FellowshipReferenda DecidingCount (r:1 w:1) - /// Proof: FellowshipReferenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: FellowshipCollective MemberCount (r:1 w:0) - /// 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) - fn place_decision_deposit_failing() -> Weight { - // Proof Size summary in bytes: - // Measured: `639` - // Estimated: `83866` - // Minimum execution time: 69_502_000 picoseconds. - Weight::from_parts(71_500_000, 0) - .saturating_add(Weight::from_parts(0, 83866)) - .saturating_add(T::DbWeight::get().reads(5)) - .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) - fn refund_decision_deposit() -> Weight { - // Proof Size summary in bytes: - // Measured: `317` - // Estimated: `4365` - // Minimum execution time: 30_561_000 picoseconds. - Weight::from_parts(31_427_000, 0) - .saturating_add(Weight::from_parts(0, 4365)) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) - } - /// Storage: FellowshipReferenda ReferendumInfoFor (r:1 w:1) - /// 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: `167` - // Estimated: `4365` - // Minimum execution time: 14_535_000 picoseconds. - Weight::from_parts(14_999_000, 0) - .saturating_add(Weight::from_parts(0, 4365)) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) - } - /// Storage: FellowshipReferenda ReferendumInfoFor (r:1 w:1) - /// 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) - fn cancel() -> Weight { - // Proof Size summary in bytes: - // Measured: `349` - // Estimated: `83866` - // Minimum execution time: 38_532_000 picoseconds. - Weight::from_parts(39_361_000, 0) - .saturating_add(Weight::from_parts(0, 83866)) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(3)) - } - /// Storage: FellowshipReferenda ReferendumInfoFor (r:1 w:1) - /// 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: FellowshipReferenda MetadataOf (r:1 w:0) - /// Proof: FellowshipReferenda MetadataOf (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) - fn kill() -> Weight { - // Proof Size summary in bytes: - // Measured: `450` - // Estimated: `83866` - // Minimum execution time: 78_956_000 picoseconds. - Weight::from_parts(80_594_000, 0) - .saturating_add(Weight::from_parts(0, 83866)) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(3)) - } - /// Storage: FellowshipReferenda TrackQueue (r:1 w:0) - /// Proof: FellowshipReferenda TrackQueue (max_values: None, max_size: Some(812), added: 3287, mode: MaxEncodedLen) - /// Storage: FellowshipReferenda DecidingCount (r:1 w:1) - /// 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: `140` - // Estimated: `4277` - // Minimum execution time: 9_450_000 picoseconds. - Weight::from_parts(9_881_000, 0) - .saturating_add(Weight::from_parts(0, 4277)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) - } - /// Storage: FellowshipReferenda TrackQueue (r:1 w:1) - /// Proof: FellowshipReferenda TrackQueue (max_values: None, max_size: Some(812), added: 3287, mode: MaxEncodedLen) - /// Storage: FellowshipReferenda ReferendumInfoFor (r:1 w:1) - /// Proof: FellowshipReferenda ReferendumInfoFor (max_values: None, max_size: Some(900), added: 3375, mode: MaxEncodedLen) - /// Storage: FellowshipCollective MemberCount (r:1 w:0) - /// Proof: FellowshipCollective MemberCount (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(38963), added: 41438, mode: MaxEncodedLen) - fn one_fewer_deciding_failing() -> Weight { - // Proof Size summary in bytes: - // Measured: `2376` - // Estimated: `42428` - // Minimum execution time: 98_126_000 picoseconds. - Weight::from_parts(102_511_000, 0) - .saturating_add(Weight::from_parts(0, 42428)) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(3)) - } - /// Storage: FellowshipReferenda TrackQueue (r:1 w:1) - /// Proof: FellowshipReferenda TrackQueue (max_values: None, max_size: Some(812), added: 3287, mode: MaxEncodedLen) - /// Storage: FellowshipReferenda ReferendumInfoFor (r:1 w:1) - /// Proof: FellowshipReferenda ReferendumInfoFor (max_values: None, max_size: Some(900), added: 3375, mode: MaxEncodedLen) - /// Storage: FellowshipCollective MemberCount (r:1 w:0) - /// Proof: FellowshipCollective MemberCount (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(38963), added: 41438, mode: MaxEncodedLen) - fn one_fewer_deciding_passing() -> Weight { - // Proof Size summary in bytes: - // Measured: `2362` - // Estimated: `42428` - // Minimum execution time: 99_398_000 picoseconds. - Weight::from_parts(104_045_000, 0) - .saturating_add(Weight::from_parts(0, 42428)) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(3)) - } - /// Storage: FellowshipReferenda ReferendumInfoFor (r:1 w:0) - /// Proof: FellowshipReferenda ReferendumInfoFor (max_values: None, max_size: Some(900), added: 3375, mode: MaxEncodedLen) - /// Storage: FellowshipReferenda TrackQueue (r:1 w:1) - /// 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: `1807` - // Estimated: `4365` - // Minimum execution time: 43_734_000 picoseconds. - Weight::from_parts(46_962_000, 0) - .saturating_add(Weight::from_parts(0, 4365)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) - } - /// Storage: FellowshipReferenda ReferendumInfoFor (r:1 w:0) - /// Proof: FellowshipReferenda ReferendumInfoFor (max_values: None, max_size: Some(900), added: 3375, mode: MaxEncodedLen) - /// Storage: FellowshipReferenda TrackQueue (r:1 w:1) - /// 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: `1774` - // Estimated: `4365` - // Minimum execution time: 42_863_000 picoseconds. - Weight::from_parts(46_241_000, 0) - .saturating_add(Weight::from_parts(0, 4365)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) - } - /// Storage: FellowshipReferenda ReferendumInfoFor (r:1 w:1) - /// Proof: FellowshipReferenda ReferendumInfoFor (max_values: None, max_size: Some(900), added: 3375, mode: MaxEncodedLen) - /// Storage: FellowshipReferenda DecidingCount (r:1 w:0) - /// Proof: FellowshipReferenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: FellowshipReferenda TrackQueue (r:1 w:1) - /// 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: `1790` - // Estimated: `4365` - // Minimum execution time: 57_511_000 picoseconds. - Weight::from_parts(64_027_000, 0) - .saturating_add(Weight::from_parts(0, 4365)) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(2)) - } - /// Storage: FellowshipReferenda ReferendumInfoFor (r:1 w:1) - /// Proof: FellowshipReferenda ReferendumInfoFor (max_values: None, max_size: Some(900), added: 3375, mode: MaxEncodedLen) - /// Storage: FellowshipReferenda DecidingCount (r:1 w:0) - /// Proof: FellowshipReferenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: FellowshipReferenda TrackQueue (r:1 w:1) - /// 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: `1831` - // Estimated: `4365` - // Minimum execution time: 56_726_000 picoseconds. - Weight::from_parts(61_962_000, 0) - .saturating_add(Weight::from_parts(0, 4365)) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(2)) - } - /// Storage: FellowshipReferenda ReferendumInfoFor (r:1 w:1) - /// Proof: FellowshipReferenda ReferendumInfoFor (max_values: None, max_size: Some(900), added: 3375, 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_no_deposit() -> Weight { - // Proof Size summary in bytes: - // Measured: `301` - // Estimated: `42428` - // Minimum execution time: 24_870_000 picoseconds. - Weight::from_parts(25_837_000, 0) - .saturating_add(Weight::from_parts(0, 42428)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(2)) - } - /// Storage: FellowshipReferenda ReferendumInfoFor (r:1 w:1) - /// Proof: FellowshipReferenda ReferendumInfoFor (max_values: None, max_size: Some(900), added: 3375, 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_preparing() -> Weight { - // Proof Size summary in bytes: - // Measured: `349` - // Estimated: `42428` - // Minimum execution time: 25_297_000 picoseconds. - Weight::from_parts(26_086_000, 0) - .saturating_add(Weight::from_parts(0, 42428)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(2)) - } - /// Storage: FellowshipReferenda ReferendumInfoFor (r:1 w:1) - /// 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: `208` - // Estimated: `4365` - // Minimum execution time: 16_776_000 picoseconds. - Weight::from_parts(17_396_000, 0) - .saturating_add(Weight::from_parts(0, 4365)) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) - } - /// Storage: FellowshipReferenda ReferendumInfoFor (r:1 w:1) - /// Proof: FellowshipReferenda ReferendumInfoFor (max_values: None, max_size: Some(900), added: 3375, mode: MaxEncodedLen) - /// Storage: FellowshipReferenda DecidingCount (r:1 w:1) - /// Proof: FellowshipReferenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: FellowshipCollective MemberCount (r:1 w:0) - /// Proof: FellowshipCollective MemberCount (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(38963), added: 41438, mode: MaxEncodedLen) - fn nudge_referendum_begin_deciding_failing() -> Weight { - // Proof Size summary in bytes: - // Measured: `584` - // Estimated: `42428` - // Minimum execution time: 37_780_000 picoseconds. - Weight::from_parts(38_626_000, 0) - .saturating_add(Weight::from_parts(0, 42428)) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(3)) - } - /// Storage: FellowshipReferenda ReferendumInfoFor (r:1 w:1) - /// Proof: FellowshipReferenda ReferendumInfoFor (max_values: None, max_size: Some(900), added: 3375, mode: MaxEncodedLen) - /// Storage: FellowshipReferenda DecidingCount (r:1 w:1) - /// Proof: FellowshipReferenda DecidingCount (max_values: None, max_size: Some(14), added: 2489, mode: MaxEncodedLen) - /// Storage: FellowshipCollective MemberCount (r:1 w:0) - /// Proof: FellowshipCollective MemberCount (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(38963), added: 41438, mode: MaxEncodedLen) - fn nudge_referendum_begin_deciding_passing() -> Weight { - // Proof Size summary in bytes: - // Measured: `719` - // Estimated: `42428` - // Minimum execution time: 85_265_000 picoseconds. - Weight::from_parts(89_986_000, 0) - .saturating_add(Weight::from_parts(0, 42428)) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(3)) - } - /// Storage: FellowshipReferenda ReferendumInfoFor (r:1 w:1) - /// Proof: FellowshipReferenda ReferendumInfoFor (max_values: None, max_size: Some(900), added: 3375, mode: MaxEncodedLen) - /// Storage: FellowshipCollective MemberCount (r:1 w:0) - /// Proof: FellowshipCollective MemberCount (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(38963), added: 41438, mode: MaxEncodedLen) - fn nudge_referendum_begin_confirming() -> Weight { - // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `42428` - // Minimum execution time: 143_283_000 picoseconds. - Weight::from_parts(158_540_000, 0) - .saturating_add(Weight::from_parts(0, 42428)) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(2)) - } - /// Storage: FellowshipReferenda ReferendumInfoFor (r:1 w:1) - /// Proof: FellowshipReferenda ReferendumInfoFor (max_values: None, max_size: Some(900), added: 3375, mode: MaxEncodedLen) - /// Storage: FellowshipCollective MemberCount (r:1 w:0) - /// Proof: FellowshipCollective MemberCount (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(38963), added: 41438, mode: MaxEncodedLen) - fn nudge_referendum_end_confirming() -> Weight { - // Proof Size summary in bytes: - // Measured: `755` - // Estimated: `42428` - // Minimum execution time: 143_736_000 picoseconds. - Weight::from_parts(162_755_000, 0) - .saturating_add(Weight::from_parts(0, 42428)) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(2)) - } - /// Storage: FellowshipReferenda ReferendumInfoFor (r:1 w:1) - /// Proof: FellowshipReferenda ReferendumInfoFor (max_values: None, max_size: Some(900), added: 3375, mode: MaxEncodedLen) - /// Storage: FellowshipCollective MemberCount (r:1 w:0) - /// Proof: FellowshipCollective MemberCount (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(38963), added: 41438, mode: MaxEncodedLen) - fn nudge_referendum_continue_not_confirming() -> Weight { - // Proof Size summary in bytes: - // Measured: `770` - // Estimated: `42428` - // Minimum execution time: 139_021_000 picoseconds. - Weight::from_parts(157_398_000, 0) - .saturating_add(Weight::from_parts(0, 42428)) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(2)) - } - /// Storage: FellowshipReferenda ReferendumInfoFor (r:1 w:1) - /// Proof: FellowshipReferenda ReferendumInfoFor (max_values: None, max_size: Some(900), added: 3375, mode: MaxEncodedLen) - /// Storage: FellowshipCollective MemberCount (r:1 w:0) - /// Proof: FellowshipCollective MemberCount (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(38963), added: 41438, mode: MaxEncodedLen) - fn nudge_referendum_continue_confirming() -> Weight { - // Proof Size summary in bytes: - // Measured: `776` - // Estimated: `42428` - // Minimum execution time: 78_530_000 picoseconds. - Weight::from_parts(83_556_000, 0) - .saturating_add(Weight::from_parts(0, 42428)) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(2)) - } - /// Storage: FellowshipReferenda ReferendumInfoFor (r:1 w:1) - /// Proof: FellowshipReferenda ReferendumInfoFor (max_values: None, max_size: Some(900), added: 3375, mode: MaxEncodedLen) - /// Storage: FellowshipCollective MemberCount (r:1 w:0) - /// 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 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: `776` - // Estimated: `83866` - // Minimum execution time: 174_165_000 picoseconds. - Weight::from_parts(188_496_000, 0) - .saturating_add(Weight::from_parts(0, 83866)) - .saturating_add(T::DbWeight::get().reads(5)) - .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) - /// Storage: FellowshipCollective MemberCount (r:1 w:0) - /// Proof: FellowshipCollective MemberCount (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(38963), added: 41438, mode: MaxEncodedLen) - fn nudge_referendum_rejected() -> Weight { - // Proof Size summary in bytes: - // Measured: `772` - // Estimated: `42428` - // Minimum execution time: 142_964_000 picoseconds. - Weight::from_parts(157_257_000, 0) - .saturating_add(Weight::from_parts(0, 42428)) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(2)) - } - /// Storage: FellowshipReferenda ReferendumInfoFor (r:1 w:0) - /// 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: 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: `352` - // Estimated: `4365` - // Minimum execution time: 20_126_000 picoseconds. - Weight::from_parts(20_635_000, 0) - .saturating_add(Weight::from_parts(0, 4365)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) - } - /// Storage: FellowshipReferenda ReferendumInfoFor (r:1 w:0) - /// Proof: FellowshipReferenda ReferendumInfoFor (max_values: None, max_size: Some(900), added: 3375, mode: MaxEncodedLen) - /// Storage: FellowshipReferenda MetadataOf (r:1 w:1) - /// Proof: FellowshipReferenda MetadataOf (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) - fn clear_metadata() -> Weight { - // Proof Size summary in bytes: - // Measured: `285` - // Estimated: `4365` - // Minimum execution time: 17_716_000 picoseconds. - Weight::from_parts(18_324_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/westend/src/weights/polkadot_runtime_parachains_inclusion.rs b/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_inclusion.rs index 36a4c5c24c9a18787e7a9d68393ef6c3fcbff2cb..28d8aa8ea32335b77320b5bc249aa2d8386e740e 100644 --- a/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_inclusion.rs +++ b/polkadot/runtime/westend/src/weights/polkadot_runtime_parachains_inclusion.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `polkadot_runtime_parachains::inclusion` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-08-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-svzsllib-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -80,13 +80,13 @@ impl polkadot_runtime_parachains::inclusion::WeightInfo /// Proof: `Dmp::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `MessageQueue::BookStateFor` (r:1 w:1) /// Proof: `MessageQueue::BookStateFor` (`max_values`: None, `max_size`: Some(55), added: 2530, mode: `MaxEncodedLen`) - /// Storage: `MessageQueue::Pages` (r:1 w:32) + /// Storage: `MessageQueue::Pages` (r:1 w:2) /// Proof: `MessageQueue::Pages` (`max_values`: None, `max_size`: Some(131122), added: 133597, mode: `MaxEncodedLen`) - /// Storage: `Hrmp::HrmpChannelDigests` (r:33 w:33) + /// Storage: `Hrmp::HrmpChannelDigests` (r:3 w:3) /// Proof: `Hrmp::HrmpChannelDigests` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Hrmp::HrmpChannels` (r:32 w:32) + /// Storage: `Hrmp::HrmpChannels` (r:2 w:2) /// Proof: `Hrmp::HrmpChannels` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// Storage: `Hrmp::HrmpChannelContents` (r:32 w:32) + /// Storage: `Hrmp::HrmpChannelContents` (r:2 w:2) /// Proof: `Hrmp::HrmpChannelContents` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Paras::FutureCodeUpgrades` (r:1 w:0) /// Proof: `Paras::FutureCodeUpgrades` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -104,28 +104,32 @@ impl polkadot_runtime_parachains::inclusion::WeightInfo /// Proof: `Paras::UpgradeRestrictionSignal` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: UNKNOWN KEY `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) /// Proof: UNKNOWN KEY `0xf5207f03cfdce586301014700e2c2593fad157e461d71fd4c1f936839a5f1f3e` (r:0 w:1) - /// The range of component `u` is `[1, 32]`. - /// The range of component `h` is `[1, 32]`. + /// The range of component `u` is `[0, 2]`. + /// The range of component `h` is `[0, 2]`. /// The range of component `c` is `[0, 1]`. fn enact_candidate(u: u32, h: u32, c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `132737 + c * (15992 ยฑ0) + h * (75 ยฑ0)` - // Estimated: `136202 + c * (76098 ยฑ0) + h * (2551 ยฑ0)` - // Minimum execution time: 18_868_930_000 picoseconds. - Weight::from_parts(6_899_601_016, 0) - .saturating_add(Weight::from_parts(0, 136202)) - // Standard Error: 1_952_665 - .saturating_add(Weight::from_parts(467_810_135, 0).saturating_mul(u.into())) - // Standard Error: 1_952_665 - .saturating_add(Weight::from_parts(551_226_340, 0).saturating_mul(h.into())) - .saturating_add(T::DbWeight::get().reads(11)) + // Measured: `1447 + c * (15992 ยฑ0) + h * (92 ยฑ0) + u * (131259 ยฑ0)` + // Estimated: `134587 + c * (25419 ยฑ939) + h * (29985 ยฑ511) + u * (82828 ยฑ511)` + // Minimum execution time: 1_065_780_000 picoseconds. + Weight::from_parts(192_328_221, 0) + .saturating_add(Weight::from_parts(0, 134587)) + // Standard Error: 1_263_527 + .saturating_add(Weight::from_parts(454_948_671, 0).saturating_mul(u.into())) + // Standard Error: 1_263_527 + .saturating_add(Weight::from_parts(527_131_211, 0).saturating_mul(h.into())) + // Standard Error: 2_093_815 + .saturating_add(Weight::from_parts(11_112_489, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(8)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(u.into()))) .saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(h.into()))) - .saturating_add(T::DbWeight::get().reads((8_u64).saturating_mul(c.into()))) - .saturating_add(T::DbWeight::get().writes(11)) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(u.into()))) - .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(h.into()))) - .saturating_add(T::DbWeight::get().writes((7_u64).saturating_mul(c.into()))) - .saturating_add(Weight::from_parts(0, 76098).saturating_mul(c.into())) - .saturating_add(Weight::from_parts(0, 2551).saturating_mul(h.into())) + .saturating_add(T::DbWeight::get().reads((9_u64).saturating_mul(c.into()))) + .saturating_add(T::DbWeight::get().writes(6)) + .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(u.into()))) + .saturating_add(T::DbWeight::get().writes((4_u64).saturating_mul(h.into()))) + .saturating_add(T::DbWeight::get().writes((8_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_parts(0, 25419).saturating_mul(c.into())) + .saturating_add(Weight::from_parts(0, 29985).saturating_mul(h.into())) + .saturating_add(Weight::from_parts(0, 82828).saturating_mul(u.into())) } } diff --git a/polkadot/scripts/list-syscalls/execute-worker-syscalls b/polkadot/scripts/list-syscalls/execute-worker-syscalls index 349af783cf1a1340d9467187fde21e1829ec6248..4b22f67878648235b03ac641fc3f05cba5aece0e 100644 --- a/polkadot/scripts/list-syscalls/execute-worker-syscalls +++ b/polkadot/scripts/list-syscalls/execute-worker-syscalls @@ -20,6 +20,7 @@ 24 (sched_yield) 25 (mremap) 28 (madvise) +34 (pause) 39 (getpid) 41 (socket) 42 (connect) diff --git a/polkadot/scripts/list-syscalls/prepare-worker-syscalls b/polkadot/scripts/list-syscalls/prepare-worker-syscalls index 05281b61591a7f9e45efa7d7bb6514fbe118f130..fd46a788537dc324fb3801443bb6bea459d9a977 100644 --- a/polkadot/scripts/list-syscalls/prepare-worker-syscalls +++ b/polkadot/scripts/list-syscalls/prepare-worker-syscalls @@ -20,6 +20,7 @@ 24 (sched_yield) 25 (mremap) 28 (madvise) +34 (pause) 39 (getpid) 41 (socket) 42 (connect) diff --git a/polkadot/statement-table/src/generic.rs b/polkadot/statement-table/src/generic.rs index 1e90338a0f18a803e572c8174de4460a3b888e33..e3c470fcdeec94ceb792268dd845cf74774e2d53 100644 --- a/polkadot/statement-table/src/generic.rs +++ b/polkadot/statement-table/src/generic.rs @@ -245,7 +245,8 @@ impl CandidateData { pub fn attested( &self, validity_threshold: usize, - ) -> Option> { + ) -> Option> + { let valid_votes = self.validity_votes.len(); if valid_votes < validity_threshold { return None @@ -321,7 +322,8 @@ impl Table { digest: &Ctx::Digest, context: &Ctx, minimum_backing_votes: u32, - ) -> Option> { + ) -> Option> + { self.candidate_votes.get(digest).and_then(|data| { let v_threshold = context.get_group_size(&data.group_id).map_or(usize::MAX, |len| { effective_minimum_backing_votes(len, minimum_backing_votes) diff --git a/polkadot/tests/common.rs b/polkadot/tests/common.rs index dbee2d3650347e228db0b3663a696f67f5a2240a..c13bb8cd14329c0d1a5b2a2564c992b059bf205a 100644 --- a/polkadot/tests/common.rs +++ b/polkadot/tests/common.rs @@ -1,18 +1,18 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Substrate. +// This file is part of Polkadot. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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 Substrate. If not, see . +// along with Polkadot. If not, see . use polkadot_core_primitives::{Block, Hash, Header}; use std::{ diff --git a/polkadot/tests/invalid_order_arguments.rs b/polkadot/tests/invalid_order_arguments.rs index 8b5f4e31c17a7fcf5429bfb0131f81390c7e0834..f9213bbdc30805129795b92430297fe5bf5e3ace 100644 --- a/polkadot/tests/invalid_order_arguments.rs +++ b/polkadot/tests/invalid_order_arguments.rs @@ -1,18 +1,18 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Substrate. +// This file is part of Polkadot. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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 Substrate. If not, see . +// along with Polkadot. If not, see . use assert_cmd::cargo::cargo_bin; use std::process::Command; diff --git a/polkadot/tests/purge_chain_works.rs b/polkadot/tests/purge_chain_works.rs index f5a73e232e0cb22ee18c7a6100e21f44d208d78f..bc36097b8d21a1e5c7c0992bb33f37404871246f 100644 --- a/polkadot/tests/purge_chain_works.rs +++ b/polkadot/tests/purge_chain_works.rs @@ -1,18 +1,18 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Substrate. +// This file is part of Polkadot. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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 Substrate. If not, see . +// along with Polkadot. If not, see . #![cfg(unix)] diff --git a/polkadot/tests/running_the_node_and_interrupt.rs b/polkadot/tests/running_the_node_and_interrupt.rs index 85c073d3023a8e28417a2f0eb5047e2f04100820..053acc9667964e77796d254311a9e202a651ff52 100644 --- a/polkadot/tests/running_the_node_and_interrupt.rs +++ b/polkadot/tests/running_the_node_and_interrupt.rs @@ -1,18 +1,18 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Substrate. +// This file is part of Polkadot. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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 Substrate. If not, see . +// along with Polkadot. If not, see . use assert_cmd::cargo::cargo_bin; use std::process::{self, Command}; diff --git a/polkadot/xcm/docs/src/cookbook/relay_token_transactor/parachain/xcm_config.rs b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/parachain/xcm_config.rs index 99f17693093e7f0472d78caf54f842847a8a3e84..7cb230f6e0066fa775732ff5feebffddbc46194e 100644 --- a/polkadot/xcm/docs/src/cookbook/relay_token_transactor/parachain/xcm_config.rs +++ b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/parachain/xcm_config.rs @@ -152,7 +152,7 @@ impl pallet_xcm::Config for Runtime { // We turn off sending for these tests type SendXcmOrigin = EnsureXcmOrigin; type XcmRouter = super::super::network::ParachainXcmRouter; // Provided by xcm-simulator - // Anyone can execute XCM programs + // Anyone can execute XCM programs type ExecuteXcmOrigin = EnsureXcmOrigin; // We execute any type of program type XcmExecuteFilter = Everything; diff --git a/polkadot/xcm/docs/src/cookbook/relay_token_transactor/relay_chain/xcm_config.rs b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/relay_chain/xcm_config.rs index 987bb3f9ab6649bc299edafa97dc1d06166db440..a31e664d8216006982ccb0be1625c6a1ecc80a27 100644 --- a/polkadot/xcm/docs/src/cookbook/relay_token_transactor/relay_chain/xcm_config.rs +++ b/polkadot/xcm/docs/src/cookbook/relay_token_transactor/relay_chain/xcm_config.rs @@ -125,7 +125,7 @@ impl pallet_xcm::Config for Runtime { // No one can call `send` type SendXcmOrigin = EnsureXcmOrigin; type XcmRouter = super::super::network::RelayChainXcmRouter; // Provided by xcm-simulator - // Anyone can execute XCM programs + // Anyone can execute XCM programs type ExecuteXcmOrigin = EnsureXcmOrigin; // We execute any type of program type XcmExecuteFilter = Everything; diff --git a/polkadot/xcm/pallet-xcm-benchmarks/src/lib.rs b/polkadot/xcm/pallet-xcm-benchmarks/src/lib.rs index 4a12bb7f47c665ed6f302d5e665cf9c2b0e911a2..210b8f65637797966261e69dc2e5453a6fccb467 100644 --- a/polkadot/xcm/pallet-xcm-benchmarks/src/lib.rs +++ b/polkadot/xcm/pallet-xcm-benchmarks/src/lib.rs @@ -72,7 +72,7 @@ pub fn generate_holding_assets(max_assets: u32) -> Assets { let fungibles_amount: u128 = 100; let holding_fungibles = max_assets / 2; let holding_non_fungibles = max_assets - holding_fungibles - 1; // -1 because of adding `Here` asset - // add count of `holding_fungibles` + // add count of `holding_fungibles` (0..holding_fungibles) .map(|i| { Asset { diff --git a/polkadot/xcm/pallet-xcm/src/lib.rs b/polkadot/xcm/pallet-xcm/src/lib.rs index 6451901279b166e116dd8dcf1d204da74db98d35..ee448c3df60660701cc0564b5de1452cbdaf3ff1 100644 --- a/polkadot/xcm/pallet-xcm/src/lib.rs +++ b/polkadot/xcm/pallet-xcm/src/lib.rs @@ -1939,6 +1939,10 @@ impl Pallet { ) -> Result<(Xcm<::RuntimeCall>, Xcm<()>), Error> { let value = (origin, vec![fees.clone()]); ensure!(T::XcmReserveTransferFilter::contains(&value), Error::::Filtered); + ensure!( + ::IsReserve::contains(&fees, &dest), + Error::::InvalidAssetUnsupportedReserve + ); let context = T::UniversalLocation::get(); let reanchored_fees = fees @@ -1973,6 +1977,12 @@ impl Pallet { let value = (origin, assets); ensure!(T::XcmReserveTransferFilter::contains(&value), Error::::Filtered); let (_, assets) = value; + for asset in assets.iter() { + ensure!( + ::IsReserve::contains(&asset, &dest), + Error::::InvalidAssetUnsupportedReserve + ); + } // max assets is `assets` (+ potentially separately handled fee) let max_assets = @@ -2079,6 +2089,10 @@ impl Pallet { ) -> Result<(Xcm<::RuntimeCall>, Xcm<()>), Error> { let value = (origin, vec![fees.clone()]); ensure!(T::XcmTeleportFilter::contains(&value), Error::::Filtered); + ensure!( + ::IsTeleporter::contains(&fees, &dest), + Error::::Filtered + ); let context = T::UniversalLocation::get(); let reanchored_fees = fees @@ -2134,6 +2148,12 @@ impl Pallet { let value = (origin, assets); ensure!(T::XcmTeleportFilter::contains(&value), Error::::Filtered); let (_, assets) = value; + for asset in assets.iter() { + ensure!( + ::IsTeleporter::contains(&asset, &dest), + Error::::Filtered + ); + } // max assets is `assets` (+ potentially separately handled fee) let max_assets = @@ -2457,10 +2477,14 @@ impl Pallet { ::RuntimeOrigin: From, { crate::Pallet::::set_record_xcm(true); - frame_system::Pallet::::reset_events(); // To make sure we only record events from current call. + // Clear other messages in queues... + Router::clear_messages(); + // ...and reset events to make sure we only record events from current call. + frame_system::Pallet::::reset_events(); let result = call.dispatch(origin.into()); crate::Pallet::::set_record_xcm(false); let local_xcm = crate::Pallet::::recorded_xcm(); + // Should only get messages from this call since we cleared previous ones. let forwarded_xcms = Router::get_messages(); let events: Vec<::RuntimeEvent> = frame_system::Pallet::::read_events_no_consensus() diff --git a/polkadot/xcm/procedural/tests/ui.rs b/polkadot/xcm/procedural/tests/ui.rs index b3469b520eb77cbe1329fc5428932e3467757991..4d0c8af450053e0bf4c20aa183ff84d7a1774b8e 100644 --- a/polkadot/xcm/procedural/tests/ui.rs +++ b/polkadot/xcm/procedural/tests/ui.rs @@ -16,7 +16,6 @@ //! UI tests for XCM procedural macros -#[cfg(not(feature = "disable-ui-tests"))] #[test] fn ui() { // Only run the ui tests when `RUN_UI_TESTS` is set. diff --git a/polkadot/xcm/src/lib.rs b/polkadot/xcm/src/lib.rs index 1f5191c23407d7b88c52f872025583e4f701674d..0b916c87f549c6cfc8bbc50a43d0e6e488142e4a 100644 --- a/polkadot/xcm/src/lib.rs +++ b/polkadot/xcm/src/lib.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Polkadot. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/polkadot/xcm/src/v2/mod.rs b/polkadot/xcm/src/v2/mod.rs index 1afc120f500c65253e462762e2e69e6e3338210a..e3358f08d4101ecf1f98a4e69d324a4991d60e80 100644 --- a/polkadot/xcm/src/v2/mod.rs +++ b/polkadot/xcm/src/v2/mod.rs @@ -1,18 +1,18 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Cumulus. +// This file is part of Polkadot. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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 Cumulus. If not, see . +// along with Polkadot. If not, see . //! # XCM Version 2 //! diff --git a/polkadot/xcm/src/v2/multiasset.rs b/polkadot/xcm/src/v2/multiasset.rs index 7090ef138ca2d3ef71ec9743722b85d9459bd3dc..218f21b63b0a9bc40a64146c1d0515e13a99f88f 100644 --- a/polkadot/xcm/src/v2/multiasset.rs +++ b/polkadot/xcm/src/v2/multiasset.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Polkadot. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/polkadot/xcm/src/v2/traits.rs b/polkadot/xcm/src/v2/traits.rs index 4dcb4c50c68cd7d727588988d32b0f514a15dfb5..815495b812710adfe3197d7c929f8a1a9e8161d5 100644 --- a/polkadot/xcm/src/v2/traits.rs +++ b/polkadot/xcm/src/v2/traits.rs @@ -1,18 +1,18 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Cumulus. +// This file is part of Polkadot. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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 Cumulus. If not, see . +// along with Polkadot. If not, see . //! Cross-Consensus Message format data structures. diff --git a/polkadot/xcm/src/v3/mod.rs b/polkadot/xcm/src/v3/mod.rs index 880520cfedc2a82d94d01d936cfc7a00b77ade14..ff64c98e15b3fa3a99af3ca6f426ed2641da5c6d 100644 --- a/polkadot/xcm/src/v3/mod.rs +++ b/polkadot/xcm/src/v3/mod.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Polkadot. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/polkadot/xcm/src/v3/multiasset.rs b/polkadot/xcm/src/v3/multiasset.rs index 7db0fa7369028f94fe080437299835c7537f0f56..56b46b1d921e7673abddf2b4af73c8ffa6a6a357 100644 --- a/polkadot/xcm/src/v3/multiasset.rs +++ b/polkadot/xcm/src/v3/multiasset.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Polkadot. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/polkadot/xcm/src/v3/traits.rs b/polkadot/xcm/src/v3/traits.rs index 7fa8824c3568ac0a16571c826648af013da48b26..34c46453b9a8c0928722bc2abccb97a301a536f2 100644 --- a/polkadot/xcm/src/v3/traits.rs +++ b/polkadot/xcm/src/v3/traits.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Polkadot. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/polkadot/xcm/src/v4/asset.rs b/polkadot/xcm/src/v4/asset.rs index a081b595adb1b77085dc232034ccc7f961922d1a..41f1f82f828cfc822ee721d63623625773c611df 100644 --- a/polkadot/xcm/src/v4/asset.rs +++ b/polkadot/xcm/src/v4/asset.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Polkadot. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/polkadot/xcm/src/v4/mod.rs b/polkadot/xcm/src/v4/mod.rs index 2a279f989e9b23084925c1484f7f366c08325080..a2b12dcc54cee3882f4dc7b28182b1da8d8163f1 100644 --- a/polkadot/xcm/src/v4/mod.rs +++ b/polkadot/xcm/src/v4/mod.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Polkadot. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/polkadot/xcm/src/v4/traits.rs b/polkadot/xcm/src/v4/traits.rs index 351de92c80edd169cfa1498a76597ef0b976e6ef..f32b26fb163d357b5f2f025dd43abd3887685b2a 100644 --- a/polkadot/xcm/src/v4/traits.rs +++ b/polkadot/xcm/src/v4/traits.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Polkadot. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/polkadot/xcm/xcm-builder/src/asset_conversion.rs b/polkadot/xcm/xcm-builder/src/asset_conversion.rs index 16ae05c20795e27a9aa6b4d2ec78d47b7665bbb2..6d090b04886c9510b3df61d4da386dc28f178e16 100644 --- a/polkadot/xcm/xcm-builder/src/asset_conversion.rs +++ b/polkadot/xcm/xcm-builder/src/asset_conversion.rs @@ -137,7 +137,13 @@ impl< ConvertClassId: MaybeEquivalence, ConvertInstanceId: MaybeEquivalence, > MatchesNonFungibles - for MatchedConvertedConcreteId + for MatchedConvertedConcreteId< + ClassId, + InstanceId, + MatchClassId, + ConvertClassId, + ConvertInstanceId, + > { fn matches_nonfungibles(a: &Asset) -> result::Result<(ClassId, InstanceId), MatchError> { let (instance, class) = match (&a.fun, &a.id) { diff --git a/polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/adapter.rs b/polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/adapter.rs index fa94ee5f1caaa1fa16a7568086a42edc82aafee1..3108068686f9b40ac35347df13005568b9bd543e 100644 --- a/polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/adapter.rs +++ b/polkadot/xcm/xcm-builder/src/asset_exchange/single_asset_adapter/adapter.rs @@ -136,7 +136,7 @@ where give.clone() })?; - (credit_out, Some(credit_change)) + (credit_out, if credit_change.peek() > 0 { Some(credit_change) } else { None }) }; // We create an `AssetsInHolding` instance by putting in the resulting asset diff --git a/polkadot/xcm/xcm-builder/src/barriers.rs b/polkadot/xcm/xcm-builder/src/barriers.rs index 5d95005eb663097b8130cd01640d6dd29a4f2307..c995361ea8a3c73208701903518e89817b896e58 100644 --- a/polkadot/xcm/xcm-builder/src/barriers.rs +++ b/polkadot/xcm/xcm-builder/src/barriers.rs @@ -57,8 +57,9 @@ const MAX_ASSETS_FOR_BUY_EXECUTION: usize = 2; /// Allows execution from `origin` if it is contained in `T` (i.e. `T::Contains(origin)`) taking /// payments into account. /// -/// Only allows for `TeleportAsset`, `WithdrawAsset`, `ClaimAsset` and `ReserveAssetDeposit` XCMs -/// because they are the only ones that place assets in the Holding Register to pay for execution. +/// Only allows for `WithdrawAsset`, `ReceiveTeleportedAsset`, `ReserveAssetDeposited` and +/// `ClaimAsset` XCMs because they are the only ones that place assets in the Holding Register to +/// pay for execution. pub struct AllowTopLevelPaidExecutionFrom(PhantomData); impl> ShouldExecute for AllowTopLevelPaidExecutionFrom { fn should_execute( @@ -81,9 +82,9 @@ impl> ShouldExecute for AllowTopLevelPaidExecutionFrom instructions[..end] .matcher() .match_next_inst(|inst| match inst { + WithdrawAsset(ref assets) | ReceiveTeleportedAsset(ref assets) | ReserveAssetDeposited(ref assets) | - WithdrawAsset(ref assets) | ClaimAsset { ref assets, .. } => if assets.len() <= MAX_ASSETS_FOR_BUY_EXECUTION { Ok(()) @@ -92,7 +93,10 @@ impl> ShouldExecute for AllowTopLevelPaidExecutionFrom }, _ => Err(ProcessMessageError::BadFormat), })? - .skip_inst_while(|inst| matches!(inst, ClearOrigin))? + .skip_inst_while(|inst| { + matches!(inst, ClearOrigin | AliasOrigin(..)) || + matches!(inst, DescendOrigin(child) if child != &Here) + })? .match_next_inst(|inst| match inst { BuyExecution { weight_limit: Limited(ref mut weight), .. } if weight.all_gte(max_weight) => diff --git a/polkadot/xcm/xcm-builder/src/matcher.rs b/polkadot/xcm/xcm-builder/src/matcher.rs index eae43b290fb2c6d569f6fe0db3506e92e021e3e1..ab515f180527da4a3ff0d4c68bf9ab4a337ac33f 100644 --- a/polkadot/xcm/xcm-builder/src/matcher.rs +++ b/polkadot/xcm/xcm-builder/src/matcher.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Polkadot. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/polkadot/xcm/xcm-builder/src/nonfungibles_adapter.rs b/polkadot/xcm/xcm-builder/src/nonfungibles_adapter.rs index b111a05a4f1fc2a9c0559c42f672a9734fd110fe..006c28954bcee6b5635de86082841ce812e63497 100644 --- a/polkadot/xcm/xcm-builder/src/nonfungibles_adapter.rs +++ b/polkadot/xcm/xcm-builder/src/nonfungibles_adapter.rs @@ -270,7 +270,14 @@ impl< CheckAsset: AssetChecking, CheckingAccount: Get>, > TransactAsset - for NonFungiblesAdapter + for NonFungiblesAdapter< + Assets, + Matcher, + AccountIdConverter, + AccountId, + CheckAsset, + CheckingAccount, + > { fn can_check_in(origin: &Location, what: &Asset, context: &XcmContext) -> XcmResult { NonFungiblesMutateAdapter::< diff --git a/polkadot/xcm/xcm-builder/src/routing.rs b/polkadot/xcm/xcm-builder/src/routing.rs index 03ef780ef0325c24e4f53b1013e558a3e0d3990b..fc2de89d21285076fa2db281108ae9c98bacc7bb 100644 --- a/polkadot/xcm/xcm-builder/src/routing.rs +++ b/polkadot/xcm/xcm-builder/src/routing.rs @@ -62,6 +62,10 @@ impl SendXcm for WithUniqueTopic { } } impl InspectMessageQueues for WithUniqueTopic { + fn clear_messages() { + Inner::clear_messages() + } + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { Inner::get_messages() } @@ -149,12 +153,21 @@ impl EnsureDelivery for Tuple { /// Inspects messages in queues. /// Meant to be used in runtime APIs, not in runtimes. pub trait InspectMessageQueues { + /// Clear the queues at the beginning of Runtime API call, so that subsequent + /// `Self::get_messages()` will return only messages generated by said Runtime API. + fn clear_messages(); /// Get queued messages and their destinations. fn get_messages() -> Vec<(VersionedLocation, Vec>)>; } #[impl_trait_for_tuples::impl_for_tuples(30)] impl InspectMessageQueues for Tuple { + fn clear_messages() { + for_tuples!( #( + Tuple::clear_messages(); + )* ); + } + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { let mut messages = Vec::new(); diff --git a/polkadot/xcm/xcm-builder/src/tests/barriers.rs b/polkadot/xcm/xcm-builder/src/tests/barriers.rs index 665b5febc61fec2d549cf707af55e4ba872d21e2..cd2b6db66efcfed828139311d482700faaa02484 100644 --- a/polkadot/xcm/xcm-builder/src/tests/barriers.rs +++ b/polkadot/xcm/xcm-builder/src/tests/barriers.rs @@ -283,6 +283,56 @@ fn allow_paid_should_work() { assert_eq!(r, Ok(())) } +#[test] +fn allow_paid_should_deprivilege_origin() { + AllowPaidFrom::set(vec![Parent.into()]); + let fees = (Parent, 1).into(); + + let mut paying_message_clears_origin = Xcm::<()>(vec![ + ReserveAssetDeposited((Parent, 100).into()), + ClearOrigin, + BuyExecution { fees, weight_limit: Limited(Weight::from_parts(30, 30)) }, + DepositAsset { assets: AllCounted(1).into(), beneficiary: Here.into() }, + ]); + let r = AllowTopLevelPaidExecutionFrom::>::should_execute( + &Parent.into(), + paying_message_clears_origin.inner_mut(), + Weight::from_parts(30, 30), + &mut props(Weight::zero()), + ); + assert_eq!(r, Ok(())); + + let mut paying_message_aliases_origin = paying_message_clears_origin.clone(); + paying_message_aliases_origin.0[1] = AliasOrigin(Parachain(1).into()); + let r = AllowTopLevelPaidExecutionFrom::>::should_execute( + &Parent.into(), + paying_message_aliases_origin.inner_mut(), + Weight::from_parts(30, 30), + &mut props(Weight::zero()), + ); + assert_eq!(r, Ok(())); + + let mut paying_message_descends_origin = paying_message_clears_origin.clone(); + paying_message_descends_origin.0[1] = DescendOrigin(Parachain(1).into()); + let r = AllowTopLevelPaidExecutionFrom::>::should_execute( + &Parent.into(), + paying_message_descends_origin.inner_mut(), + Weight::from_parts(30, 30), + &mut props(Weight::zero()), + ); + assert_eq!(r, Ok(())); + + let mut paying_message_fake_descends_origin = paying_message_clears_origin.clone(); + paying_message_fake_descends_origin.0[1] = DescendOrigin(Here.into()); + let r = AllowTopLevelPaidExecutionFrom::>::should_execute( + &Parent.into(), + paying_message_fake_descends_origin.inner_mut(), + Weight::from_parts(30, 30), + &mut props(Weight::zero()), + ); + assert_eq!(r, Err(ProcessMessageError::Overweight(Weight::from_parts(30, 30)))); +} + #[test] fn suspension_should_work() { TestSuspender::set_suspended(true); diff --git a/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs b/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs index 18bde3aab485a9660892ab1f5dc7c3de4b508338..9f2146fa30e86bc9a4c3e54280105c5cebfbbe09 100644 --- a/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs +++ b/polkadot/xcm/xcm-builder/src/tests/pay/mock.rs @@ -77,6 +77,7 @@ impl pallet_balances::Config for Test { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = (); type MaxFreezes = ConstU32<0>; + type DoneSlashHandler = (); } parameter_types! { diff --git a/polkadot/xcm/xcm-builder/src/universal_exports.rs b/polkadot/xcm/xcm-builder/src/universal_exports.rs index 8aa9602fcc297e9a402f7df11188773466b6ac90..5c754f01ec0ad2b5ebdd29a4ab4b3cc4649ea393 100644 --- a/polkadot/xcm/xcm-builder/src/universal_exports.rs +++ b/polkadot/xcm/xcm-builder/src/universal_exports.rs @@ -337,11 +337,15 @@ impl InspectMessageQueues +impl InspectMessageQueues for SovereignPaidRemoteExporter { + fn clear_messages() {} + + /// This router needs to implement `InspectMessageQueues` but doesn't have to + /// return any messages, since it just reuses the `XcmpQueue` router. fn get_messages() -> Vec<(VersionedLocation, Vec>)> { - Router::get_messages() + Vec::new() } } diff --git a/polkadot/xcm/xcm-builder/tests/scenarios.rs b/polkadot/xcm/xcm-builder/tests/scenarios.rs index ee1aeffbb4e71d5c42cb92376395961d4d95de81..99c14f5bba1bc8fad67605ce8eddbf8185fa7caf 100644 --- a/polkadot/xcm/xcm-builder/tests/scenarios.rs +++ b/polkadot/xcm/xcm-builder/tests/scenarios.rs @@ -22,7 +22,7 @@ use mock::{ }; use polkadot_parachain_primitives::primitives::Id as ParaId; use sp_runtime::traits::AccountIdConversion; -use xcm::latest::prelude::*; +use xcm::latest::{prelude::*, Error::UntrustedTeleportLocation}; use xcm_executor::XcmExecutor; pub const ALICE: AccountId = AccountId::new([0u8; 32]); @@ -217,7 +217,7 @@ fn teleport_to_asset_hub_works() { ]; let weight = BaseXcmWeight::get() * 3; - // teleports are allowed to community chains, even in the absence of trust from their side. + // teleports are not allowed to other chains, in the absence of trust from their side let message = Xcm(vec![ WithdrawAsset((Here, amount).into()), buy_execution(), @@ -235,16 +235,7 @@ fn teleport_to_asset_hub_works() { weight, Weight::zero(), ); - assert_eq!(r, Outcome::Complete { used: weight }); - let expected_msg = Xcm(vec![ReceiveTeleportedAsset((Parent, amount).into()), ClearOrigin] - .into_iter() - .chain(teleport_effects.clone().into_iter()) - .collect()); - let expected_hash = fake_message_hash(&expected_msg); - assert_eq!( - mock::sent_xcm(), - vec![(Parachain(other_para_id).into(), expected_msg, expected_hash,)] - ); + assert_eq!(r, Outcome::Incomplete { used: weight, error: UntrustedTeleportLocation }); // teleports are allowed from asset hub to kusama. let message = Xcm(vec![ @@ -274,10 +265,7 @@ fn teleport_to_asset_hub_works() { let expected_hash = fake_message_hash(&expected_msg); assert_eq!( mock::sent_xcm(), - vec![ - (Parachain(other_para_id).into(), expected_msg.clone(), expected_hash,), - (Parachain(asset_hub_id).into(), expected_msg, expected_hash,) - ] + vec![(Parachain(asset_hub_id).into(), expected_msg, expected_hash,)] ); }); } diff --git a/polkadot/xcm/xcm-executor/integration-tests/Cargo.toml b/polkadot/xcm/xcm-executor/integration-tests/Cargo.toml index e669e5d2b2312fbba56a86a6b8518506ba350543..7e6bfe967b9049f729c91f49329b1e75940332ec 100644 --- a/polkadot/xcm/xcm-executor/integration-tests/Cargo.toml +++ b/polkadot/xcm/xcm-executor/integration-tests/Cargo.toml @@ -13,14 +13,12 @@ workspace = true [dependencies] codec = { workspace = true, default-features = true } frame-support = { workspace = true } -frame-system = { workspace = true, default-features = true } futures = { workspace = true } pallet-transaction-payment = { workspace = true, default-features = true } pallet-xcm = { workspace = true, default-features = true } polkadot-test-client = { workspace = true } polkadot-test-runtime = { workspace = true } polkadot-test-service = { workspace = true } -polkadot-service = { workspace = true, default-features = true } sp-consensus = { workspace = true, default-features = true } sp-keyring = { workspace = true, default-features = true } sp-runtime = { workspace = true } diff --git a/polkadot/xcm/xcm-executor/integration-tests/src/lib.rs b/polkadot/xcm/xcm-executor/integration-tests/src/lib.rs index 7683c6025392a824200afc779f4e5ec1a700dc53..6f44cc0a75d55267426f3044d369ec30fce8b550 100644 --- a/polkadot/xcm/xcm-executor/integration-tests/src/lib.rs +++ b/polkadot/xcm/xcm-executor/integration-tests/src/lib.rs @@ -18,13 +18,12 @@ 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 polkadot_test_service::{chain_spec::get_account_id_from_seed, construct_extrinsic}; use sp_core::sr25519; use sp_runtime::traits::Block; use sp_state_machine::InspectState; diff --git a/polkadot/xcm/xcm-executor/src/config.rs b/polkadot/xcm/xcm-executor/src/config.rs index 63b113bc250fa34db10336f7575771bbdbf76b01..5bcbbd3466e8e3b4a674d1085ebd573994cff376 100644 --- a/polkadot/xcm/xcm-executor/src/config.rs +++ b/polkadot/xcm/xcm-executor/src/config.rs @@ -33,6 +33,10 @@ pub trait Config { type RuntimeCall: Parameter + Dispatchable + GetDispatchInfo; /// How to send an onward XCM message. + /// + /// The sender is tasked with returning the assets it needs to pay for delivery fees. + /// Only one asset should be returned as delivery fees, any other will be ignored by + /// the executor. type XcmSender: SendXcm; /// How to withdraw and deposit an asset. @@ -74,6 +78,9 @@ pub trait Config { type AssetLocker: AssetLock; /// Handler for exchanging assets. + /// + /// This is used in the executor to swap the asset wanted for fees with the asset needed for + /// delivery fees. type AssetExchanger: AssetExchange; /// The handler for when there is an instruction to claim assets. diff --git a/polkadot/xcm/xcm-executor/src/lib.rs b/polkadot/xcm/xcm-executor/src/lib.rs index 74561e931e7e14931aeac42e73b0cd9cb8790b6b..14920f36445b8c99ca5f56a76750b8b894ca1c0b 100644 --- a/polkadot/xcm/xcm-executor/src/lib.rs +++ b/polkadot/xcm/xcm-executor/src/lib.rs @@ -83,6 +83,9 @@ pub struct XcmExecutor { appendix_weight: Weight, transact_status: MaybeErrorCode, fees_mode: FeesMode, + /// Asset provided in last `BuyExecution` instruction (if any) in current XCM program. Same + /// asset type will be used for paying any potential delivery fees incurred by the program. + asset_used_for_fees: Option, _config: PhantomData, } @@ -269,7 +272,7 @@ impl ExecuteXcm for XcmExecutor XcmExecutor { appendix_weight: Weight::zero(), transact_status: Default::default(), fees_mode: FeesMode { jit_withdraw: false }, + asset_used_for_fees: None, _config: PhantomData, } } @@ -469,31 +473,112 @@ impl XcmExecutor { Ok(()) } - fn take_fee(&mut self, fee: Assets, reason: FeeReason) -> XcmResult { + fn take_fee(&mut self, fees: Assets, reason: FeeReason) -> XcmResult { if Config::FeeManager::is_waived(self.origin_ref(), reason.clone()) { return Ok(()) } tracing::trace!( target: "xcm::fees", - ?fee, + ?fees, origin_ref = ?self.origin_ref(), fees_mode = ?self.fees_mode, ?reason, "Taking fees", ); - let paid = if self.fees_mode.jit_withdraw { + // We only ever use the first asset from `fees`. + let asset_needed_for_fees = match fees.get(0) { + Some(fee) => fee, + None => return Ok(()), // No delivery fees need to be paid. + }; + // If `BuyExecution` was called, we use that asset for delivery fees as well. + let asset_to_pay_for_fees = + self.calculate_asset_for_delivery_fees(asset_needed_for_fees.clone()); + tracing::trace!(target: "xcm::fees", ?asset_to_pay_for_fees); + // We withdraw or take from holding the asset the user wants to use for fee payment. + let withdrawn_fee_asset = if self.fees_mode.jit_withdraw { let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; - for asset in fee.inner() { - Config::AssetTransactor::withdraw_asset(&asset, origin, Some(&self.context))?; - } - fee + Config::AssetTransactor::withdraw_asset( + &asset_to_pay_for_fees, + origin, + Some(&self.context), + )?; + tracing::trace!(target: "xcm::fees", ?asset_needed_for_fees); + asset_to_pay_for_fees.clone().into() + } else { + let assets_taken_from_holding_to_pay_delivery_fees = self + .holding + .try_take(asset_to_pay_for_fees.clone().into()) + .map_err(|_| XcmError::NotHoldingFees)?; + tracing::trace!(target: "xcm::fees", ?assets_taken_from_holding_to_pay_delivery_fees); + let mut iter = assets_taken_from_holding_to_pay_delivery_fees.fungible_assets_iter(); + let asset = iter.next().ok_or(XcmError::NotHoldingFees)?; + asset.into() + }; + // We perform the swap, if needed, to pay fees. + let paid = if asset_to_pay_for_fees.id != asset_needed_for_fees.id { + let swapped_asset: Assets = Config::AssetExchanger::exchange_asset( + self.origin_ref(), + withdrawn_fee_asset, + &asset_needed_for_fees.clone().into(), + false, + ) + .map_err(|given_assets| { + tracing::error!( + target: "xcm::fees", + ?given_assets, + "Swap was deemed necessary but couldn't be done", + ); + XcmError::FeesNotMet + })? + .into(); + swapped_asset } else { - self.holding.try_take(fee.into()).map_err(|_| XcmError::NotHoldingFees)?.into() + // If the asset wanted to pay for fees is the one that was needed, + // we don't need to do any swap. + // We just use the assets withdrawn or taken from holding. + withdrawn_fee_asset.into() }; Config::FeeManager::handle_fee(paid, Some(&self.context), reason); Ok(()) } + /// Calculates the amount of `self.asset_used_for_fees` required to swap for + /// `asset_needed_for_fees`. + /// + /// The calculation is done by `Config::AssetExchanger`. + /// If `self.asset_used_for_fees` is not set, it will just return `asset_needed_for_fees`. + fn calculate_asset_for_delivery_fees(&self, asset_needed_for_fees: Asset) -> Asset { + if let Some(asset_wanted_for_fees) = &self.asset_used_for_fees { + if *asset_wanted_for_fees != asset_needed_for_fees.id { + match Config::AssetExchanger::quote_exchange_price( + &(asset_wanted_for_fees.clone(), Fungible(0)).into(), + &asset_needed_for_fees.clone().into(), + false, // Minimal. + ) { + Some(necessary_assets) => + // We only use the first asset for fees. + // If this is not enough to swap for the fee asset then it will error later down + // the line. + necessary_assets.get(0).unwrap_or(&asset_needed_for_fees.clone()).clone(), + // If we can't convert, then we return the original asset. + // It will error later in any case. + None => { + tracing::trace!( + target: "xcm::calculate_asset_for_delivery_fees", + ?asset_wanted_for_fees, + "Could not convert fees", + ); + asset_needed_for_fees.clone() + }, + } + } else { + asset_needed_for_fees + } + } else { + asset_needed_for_fees + } + } + /// Calculates what `local_querier` would be from the perspective of `destination`. fn to_querier( local_querier: Option, @@ -878,11 +963,23 @@ impl XcmExecutor { message_to_weigh.extend(xcm.0.clone().into_iter()); let (_, fee) = validate_send::(dest.clone(), Xcm(message_to_weigh))?; - // set aside fee to be charged by XcmSender - let transport_fee = self.holding.saturating_take(fee.into()); - - // now take assets to deposit (excluding transport_fee) + let maybe_delivery_fee = fee.get(0).map(|asset_needed_for_fees| { + tracing::trace!( + target: "xcm::DepositReserveAsset", + "Asset provided to pay for fees {:?}, asset required for delivery fees: {:?}", + self.asset_used_for_fees, asset_needed_for_fees, + ); + let asset_to_pay_for_fees = + self.calculate_asset_for_delivery_fees(asset_needed_for_fees.clone()); + // set aside fee to be charged by XcmSender + let delivery_fee = + self.holding.saturating_take(asset_to_pay_for_fees.into()); + tracing::trace!(target: "xcm::DepositReserveAsset", ?delivery_fee); + delivery_fee + }); + // now take assets to deposit (after having taken delivery fees) let deposited = self.holding.saturating_take(assets); + tracing::trace!(target: "xcm::DepositReserveAsset", ?deposited, "Assets except delivery fee"); self.deposit_assets_with_retry(&deposited, &dest)?; // Note that we pass `None` as `maybe_failed_bin` and drop any assets which // cannot be reanchored because we have already called `deposit_asset` on all @@ -890,8 +987,10 @@ impl XcmExecutor { let assets = Self::reanchored(deposited, &dest, None); let mut message = vec![ReserveAssetDeposited(assets), ClearOrigin]; message.extend(xcm.0.into_iter()); - // put back transport_fee in holding register to be charged by XcmSender - self.holding.subsume_assets(transport_fee); + // put back delivery_fee in holding register to be charged by XcmSender + if let Some(delivery_fee) = maybe_delivery_fee { + self.holding.subsume_assets(delivery_fee); + } self.send(dest, Xcm(message), FeeReason::DepositReserveAsset)?; Ok(()) }); @@ -903,13 +1002,18 @@ impl XcmExecutor { InitiateReserveWithdraw { assets, reserve, xcm } => { let old_holding = self.holding.clone(); let result = Config::TransactionalProcessor::process(|| { + let assets = self.holding.saturating_take(assets); + // Must ensure that we recognise the assets as being managed by the destination. + #[cfg(not(feature = "runtime-benchmarks"))] + for asset in assets.assets_iter() { + ensure!( + Config::IsReserve::contains(&asset, &reserve), + XcmError::UntrustedReserveLocation + ); + } // 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 assets = Self::reanchored(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)?; @@ -925,6 +1029,14 @@ impl XcmExecutor { let result = (|| -> Result<(), XcmError> { // We must do this first in order to resolve wildcards. let assets = self.holding.saturating_take(assets); + // Must ensure that we have teleport trust with destination for these assets. + #[cfg(not(feature = "runtime-benchmarks"))] + for asset in assets.assets_iter() { + ensure!( + Config::IsTeleporter::contains(&asset, &dest), + XcmError::UntrustedTeleportLocation + ); + } 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 @@ -969,6 +1081,10 @@ impl XcmExecutor { // should be executed. let Some(weight) = Option::::from(weight_limit) else { return Ok(()) }; let old_holding = self.holding.clone(); + // Save the asset being used for execution fees, so we later know what should be + // used for delivery fees. + self.asset_used_for_fees = Some(fees.id.clone()); + tracing::trace!(target: "xcm::executor::BuyExecution", asset_used_for_fees = ?self.asset_used_for_fees); // pay for `weight` using up to `fees` of the holding register. let max_fee = self.holding.try_take(fees.into()).map_err(|_| XcmError::NotHoldingFees)?; diff --git a/polkadot/xcm/xcm-executor/src/traits/export.rs b/polkadot/xcm/xcm-executor/src/traits/export.rs index 78aa68ce2644a8bd6d4ab1075d9d93923b31c3cc..b356e0da7df7801b52cb63652fa321507223a824 100644 --- a/polkadot/xcm/xcm-executor/src/traits/export.rs +++ b/polkadot/xcm/xcm-executor/src/traits/export.rs @@ -20,7 +20,7 @@ use xcm::latest::prelude::*; /// spoofed origin. This essentially defines the behaviour of the `ExportMessage` XCM instruction. /// /// This is quite different to `SendXcm`; `SendXcm` assumes that the local side's location will be -/// preserved to be represented as the value of the Origin register in the messages execution. +/// preserved to be represented as the value of the Origin register during the message's execution. /// /// This trait on the other hand assumes that we do not necessarily want the Origin register to /// contain the local (i.e. the caller chain's) location, since it will generally be exporting a @@ -44,8 +44,8 @@ pub trait ExportXcm { /// The `destination` and `message` must be `Some` (or else an error will be returned) and they /// may only be consumed if the `Err` is not `NotApplicable`. /// - /// If it is not a destination which can be reached with this type but possibly could by others, - /// then this *MUST* return `NotApplicable`. Any other error will cause the tuple + /// If it is not a destination that can be reached with this type, but possibly could be with + /// others, then this *MUST* return `NotApplicable`. Any other error will cause the tuple /// implementation (used to compose routing systems from different delivery agents) to exit /// early without trying alternative means of delivery. fn validate( diff --git a/polkadot/xcm/xcm-executor/src/traits/weight.rs b/polkadot/xcm/xcm-executor/src/traits/weight.rs index 72de3e0f433b96ed1718be70e3c0feb20340453a..61545c330621326e85884014d87cc3bfec674bd1 100644 --- a/polkadot/xcm/xcm-executor/src/traits/weight.rs +++ b/polkadot/xcm/xcm-executor/src/traits/weight.rs @@ -29,13 +29,6 @@ pub trait WeightBounds { fn instr_weight(instruction: &Instruction) -> Result; } -/// A means of getting approximate weight consumption for a given destination message executor and a -/// message. -pub trait UniversalWeigher { - /// Get the upper limit of weight required for `dest` to execute `message`. - fn weigh(dest: impl Into, message: Xcm<()>) -> Result; -} - /// Charge for weight in order to execute XCM. /// /// A `WeightTrader` may also be put into a tuple, in which case the default behavior of diff --git a/polkadot/xcm/xcm-runtime-apis/src/conversions.rs b/polkadot/xcm/xcm-runtime-apis/src/conversions.rs index e5eeac013fee6e8b7222429dfd0ed93afa9384c9..22f0809ea5f926929bad7183f341727391522b52 100644 --- a/polkadot/xcm/xcm-runtime-apis/src/conversions.rs +++ b/polkadot/xcm/xcm-runtime-apis/src/conversions.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Polkadot. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/polkadot/xcm/xcm-runtime-apis/src/dry_run.rs b/polkadot/xcm/xcm-runtime-apis/src/dry_run.rs index 2a1a0daf0d5d5d0bae868e973df56f7e35b41592..f0a70b0dacfe11773437f8e361d032f237f432bb 100644 --- a/polkadot/xcm/xcm-runtime-apis/src/dry_run.rs +++ b/polkadot/xcm/xcm-runtime-apis/src/dry_run.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Polkadot. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. @@ -57,7 +57,12 @@ sp_api::decl_runtime_apis! { /// Calls or XCMs might fail when executed, this doesn't mean the result of these calls will be an `Err`. /// In those cases, there might still be a valid result, with the execution error inside it. /// The only reasons why these calls might return an error are listed in the [`Error`] enum. - pub trait DryRunApi { + pub trait DryRunApi + where + Call: Encode, + Event: Decode, + OriginCaller: Encode + { /// Dry run call. fn dry_run_call(origin: OriginCaller, call: Call) -> Result, Error>; diff --git a/polkadot/xcm/xcm-runtime-apis/src/fees.rs b/polkadot/xcm/xcm-runtime-apis/src/fees.rs index 3445d42ecab3bda1a212e60f2be5583a31e04b58..9500a7f7281f42e0c1648a38100300dc3b2e35f4 100644 --- a/polkadot/xcm/xcm-runtime-apis/src/fees.rs +++ b/polkadot/xcm/xcm-runtime-apis/src/fees.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Polkadot. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/polkadot/xcm/xcm-runtime-apis/src/lib.rs b/polkadot/xcm/xcm-runtime-apis/src/lib.rs index b106836c1132b253acdc01e8b9fd63a7c4ff828b..44e518e8e7ab0919f12c5a73eff3e4a6b38afdc4 100644 --- a/polkadot/xcm/xcm-runtime-apis/src/lib.rs +++ b/polkadot/xcm/xcm-runtime-apis/src/lib.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Polkadot. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/polkadot/xcm/xcm-runtime-apis/tests/conversions.rs b/polkadot/xcm/xcm-runtime-apis/tests/conversions.rs index 7f0f0923b092116e0ad10f4b287dda8c93f5357f..c7a1dda0169c875bada9138516d334bb4833c56c 100644 --- a/polkadot/xcm/xcm-runtime-apis/tests/conversions.rs +++ b/polkadot/xcm/xcm-runtime-apis/tests/conversions.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Polkadot. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/polkadot/xcm/xcm-runtime-apis/tests/fee_estimation.rs b/polkadot/xcm/xcm-runtime-apis/tests/fee_estimation.rs index e5dac7c7a04edaed341274d79690b09e7c987b42..2d14b4e571c6724574d0751a12ea1f470c125da5 100644 --- a/polkadot/xcm/xcm-runtime-apis/tests/fee_estimation.rs +++ b/polkadot/xcm/xcm-runtime-apis/tests/fee_estimation.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Polkadot. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. @@ -197,7 +197,7 @@ fn fee_estimation_for_teleport() { fn dry_run_reserve_asset_transfer() { sp_tracing::init_for_tests(); let who = 1; // AccountId = u64. - // Native token used for fees. + // Native token used for fees. let balances = vec![(who, DeliveryFees::get() + ExistentialDeposit::get())]; // Relay token is the one we want to transfer. let assets = vec![(1, who, 100)]; // id, account_id, balance. diff --git a/polkadot/xcm/xcm-runtime-apis/tests/mock.rs b/polkadot/xcm/xcm-runtime-apis/tests/mock.rs index c76b26fcd2a337545450da4d77cb0103c3e8eb8c..6575feccf8a3a77e1e64e01531be67eca81842fe 100644 --- a/polkadot/xcm/xcm-runtime-apis/tests/mock.rs +++ b/polkadot/xcm/xcm-runtime-apis/tests/mock.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Polkadot. -// Substrate is free software: you can redistribute it and/or modify +// 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. -// Substrate is distributed in the hope that it will be useful, +// 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. diff --git a/polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/mod.rs b/polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/mod.rs index c5d5fa66732b939b9a03e1da2cf9c658c4e61acb..6218915cd12d632bfc4415d88bbe3f3784f8baa0 100644 --- a/polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/mod.rs +++ b/polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/mod.rs @@ -19,6 +19,7 @@ pub mod barrier; pub mod constants; pub mod location_converter; pub mod origin_converter; +pub mod teleporter; pub mod weigher; use crate::relay_chain::{RuntimeCall, XcmPallet}; @@ -36,7 +37,7 @@ impl Config for XcmConfig { type AssetTransactor = asset_transactor::AssetTransactor; type OriginConverter = origin_converter::OriginConverter; type IsReserve = (); - type IsTeleporter = (); + type IsTeleporter = teleporter::TrustedTeleporters; type UniversalLocation = constants::UniversalLocation; type Barrier = barrier::Barrier; type Weigher = weigher::Weigher; diff --git a/polkadot/node/jaeger/src/errors.rs b/polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/teleporter.rs similarity index 65% rename from polkadot/node/jaeger/src/errors.rs rename to polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/teleporter.rs index adedda34c7fc46ec6303b65bbfa328e1a960967f..92e5065044e63a75b2c2fc84bb4d3535dc8dc538 100644 --- a/polkadot/node/jaeger/src/errors.rs +++ b/polkadot/xcm/xcm-simulator/example/src/relay_chain/xcm_config/teleporter.rs @@ -14,15 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -//! Polkadot Jaeger error definitions. +use frame_support::parameter_types; +use xcm::latest::prelude::*; -/// A description of an error during jaeger initialization. -#[derive(Debug, thiserror::Error)] -#[allow(missing_docs)] -pub enum JaegerError { - #[error("Already launched the collector thread")] - AlreadyLaunched, - - #[error("Missing jaeger configuration")] - MissingConfiguration, +parameter_types! { + pub NftCollectionOnRelay: AssetFilter + = Wild(AllOf { fun: WildNonFungible, id: AssetId(GeneralIndex(1).into()) }); + pub NftCollectionForChild: (AssetFilter, Location) + = (NftCollectionOnRelay::get(), Parachain(1).into()); } +pub type TrustedTeleporters = xcm_builder::Case; diff --git a/polkadot/zombienet-sdk-tests/Cargo.toml b/polkadot/zombienet-sdk-tests/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..4eac7af49f8a9ea4ae775491b3aaed9fb436fd60 --- /dev/null +++ b/polkadot/zombienet-sdk-tests/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "polkadot-zombienet-sdk-tests" +version = "0.1.0" +description = "Zomebienet-sdk tests." +authors.workspace = true +edition.workspace = true +license.workspace = true +publish = false + +[dependencies] +env_logger = { workspace = true } +log = { workspace = true } +subxt = { workspace = true, features = ["substrate-compat"] } +subxt-signer = { workspace = true } +tokio = { workspace = true, features = ["rt-multi-thread"] } +anyhow = { workspace = true } +zombienet-sdk = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +codec = { workspace = true, features = ["derive"] } + +[features] +zombie-metadata = [] + +[build-dependencies] +substrate-build-script-utils = { workspace = true, default-features = true } +subwasmlib = { git = "https://github.com/chevdor/subwasm", rev = "v0.21.3" } diff --git a/polkadot/zombienet-sdk-tests/build.rs b/polkadot/zombienet-sdk-tests/build.rs new file mode 100644 index 0000000000000000000000000000000000000000..240d86386af2ee831f887d83a2ff9ced16c9e175 --- /dev/null +++ b/polkadot/zombienet-sdk-tests/build.rs @@ -0,0 +1,151 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +use std::{ + env, fs, path, + path::{Path, PathBuf}, + process::Command, +}; + +use subwasmlib::{source::Source, OutputFormat, Subwasm}; + +macro_rules! debug_output { + ($($tokens: tt)*) => { + if env::var("ZOMBIE_METADATA_BUILD_DEBUG").is_ok() { + println!("cargo:warning={}", format!($($tokens)*)) + } + } +} + +fn replace_dashes(k: &str) -> String { + k.replace('-', "_") +} + +fn make_env_key(k: &str) -> String { + replace_dashes(&k.to_ascii_uppercase()) +} + +fn find_wasm(chain: &str) -> Option { + const PROFILES: [&str; 2] = ["release", "testnet"]; + let manifest_path = env::var("CARGO_WORKSPACE_ROOT_DIR").unwrap(); + let manifest_path = manifest_path.strip_suffix('/').unwrap(); + debug_output!("manifest_path is : {}", manifest_path); + let package = format!("{chain}-runtime"); + let profile = PROFILES.into_iter().find(|p| { + let full_path = format!( + "{}/target/{}/wbuild/{}/{}.wasm", + manifest_path, + p, + &package, + replace_dashes(&package) + ); + debug_output!("checking wasm at : {}", full_path); + matches!(path::PathBuf::from(&full_path).try_exists(), Ok(true)) + }); + + debug_output!("profile is : {:?}", profile); + profile.map(|profile| { + PathBuf::from(&format!( + "{}/target/{}/wbuild/{}/{}.wasm", + manifest_path, + profile, + &package, + replace_dashes(&package) + )) + }) +} + +// based on https://gist.github.com/s0me0ne-unkn0wn/bbd83fe32ce10327086adbf13e750eec +fn build_wasm(chain: &str) -> PathBuf { + let package = format!("{chain}-runtime"); + + let cargo = env::var("CARGO").unwrap(); + let target = env::var("TARGET").unwrap(); + let out_dir = env::var("OUT_DIR").unwrap(); + let target_dir = format!("{}/runtimes", out_dir); + let args = vec![ + "build", + "-p", + &package, + "--profile", + "release", + "--target", + &target, + "--target-dir", + &target_dir, + ]; + debug_output!("building metadata with args: {}", args.join(" ")); + Command::new(cargo) + .env_remove("SKIP_WASM_BUILD") // force build to get the metadata + .args(&args) + .status() + .unwrap(); + + let wasm_path = &format!( + "{target_dir}/{target}/release/wbuild/{}/{}.wasm", + &package, + replace_dashes(&package) + ); + PathBuf::from(wasm_path) +} + +fn generate_metadata_file(wasm_path: &Path, output_path: &Path) { + let source = Source::from_options(Some(wasm_path.to_path_buf()), None, None, None).unwrap(); + let subwasm = Subwasm::new(&source.try_into().unwrap()).unwrap(); + let mut output_file = std::fs::File::create(output_path).unwrap(); + subwasm.write_metadata(OutputFormat::Scale, None, &mut output_file).unwrap(); +} + +fn fetch_metadata_file(chain: &str, output_path: &Path) { + // First check if we have an explicit path to use + let env_key = format!("{}_METADATA_FILE", make_env_key(chain)); + + if let Ok(path_to_use) = env::var(env_key) { + debug_output!("metadata file to use (from env): {}\n", path_to_use); + let metadata_file = PathBuf::from(&path_to_use); + fs::copy(metadata_file, output_path).unwrap(); + } else if let Some(exisiting_wasm) = find_wasm(chain) { + debug_output!("exisiting wasm: {:?}", exisiting_wasm); + // generate metadata + generate_metadata_file(&exisiting_wasm, output_path); + } else { + // build runtime + let wasm_path = build_wasm(chain); + debug_output!("created wasm: {:?}", wasm_path); + // genetate metadata + generate_metadata_file(&wasm_path, output_path); + } +} + +fn main() { + if env::var("CARGO_FEATURE_ZOMBIE_METADATA").is_err() { + debug_output!("zombie-metadata feature not enabled, not need to check metadata files."); + return; + } + + // Ensure we have the needed metadata files in place to run zombienet tests + let manifest_path = env::var("CARGO_MANIFEST_DIR").unwrap(); + const METADATA_DIR: &str = "metadata-files"; + const CHAINS: [&str; 2] = ["rococo", "coretime-rococo"]; + + let metadata_path = format!("{manifest_path}/{METADATA_DIR}"); + + for chain in CHAINS { + let full_path = format!("{metadata_path}/{chain}-local.scale"); + let output_path = path::PathBuf::from(&full_path); + + match output_path.try_exists() { + Ok(true) => { + debug_output!("got: {}", full_path); + }, + _ => { + debug_output!("needs: {}", full_path); + fetch_metadata_file(chain, &output_path); + }, + }; + } + + substrate_build_script_utils::generate_cargo_keys(); + substrate_build_script_utils::rerun_if_git_head_changed(); + println!("cargo:rerun-if-changed={}", metadata_path); +} diff --git a/polkadot/zombienet-sdk-tests/metadata-files/.gitkeep b/polkadot/zombienet-sdk-tests/metadata-files/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/polkadot/zombienet-sdk-tests/src/lib.rs b/polkadot/zombienet-sdk-tests/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..fe0aa995d77ad4cb25fa7ced8f3be2f4fa0a1eae --- /dev/null +++ b/polkadot/zombienet-sdk-tests/src/lib.rs @@ -0,0 +1,2 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 diff --git a/polkadot/zombienet-sdk-tests/tests/lib.rs b/polkadot/zombienet-sdk-tests/tests/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..74cdc0765600f52688c0bf255763c60c822c798a --- /dev/null +++ b/polkadot/zombienet-sdk-tests/tests/lib.rs @@ -0,0 +1,4 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +mod smoke; diff --git a/polkadot/zombienet-sdk-tests/tests/smoke/coretime_revenue.rs b/polkadot/zombienet-sdk-tests/tests/smoke/coretime_revenue.rs new file mode 100644 index 0000000000000000000000000000000000000000..7880dc782d05d0550c4b178c14362c897b1bcb08 --- /dev/null +++ b/polkadot/zombienet-sdk-tests/tests/smoke/coretime_revenue.rs @@ -0,0 +1,505 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +//! Binaries for this test should be built with `fast-runtime` feature enabled: +//! `cargo build -r -F fast-runtime -p polkadot-parachain-bin && \` +//! `cargo build -r -F fast-runtime --bin polkadot --bin polkadot-execute-worker --bin +//! polkadot-prepare-worker` +//! +//! Running with normal runtimes is possible but would take ages. Running fast relay runtime with +//! normal parachain runtime WILL mess things up. + +use anyhow::anyhow; +#[subxt::subxt(runtime_metadata_path = "metadata-files/rococo-local.scale")] +pub mod rococo {} + +#[subxt::subxt(runtime_metadata_path = "metadata-files/coretime-rococo-local.scale")] +mod coretime_rococo {} + +use rococo::runtime_types::{ + staging_xcm::v4::{ + asset::{Asset, AssetId, Assets, Fungibility}, + junction::Junction, + junctions::Junctions, + location::Location, + }, + xcm::{VersionedAssets, VersionedLocation}, +}; +use serde_json::json; +use std::{fmt::Display, sync::Arc}; +use subxt::{events::StaticEvent, utils::AccountId32, OnlineClient, PolkadotConfig}; +use subxt_signer::sr25519::dev; +use tokio::sync::RwLock; +use zombienet_sdk::NetworkConfigBuilder; + +use coretime_rococo::{ + self as coretime_api, + broker::events as broker_events, + runtime_types::{ + pallet_broker::types::{ConfigRecord as BrokerConfigRecord, Finality as BrokerFinality}, + sp_arithmetic::per_things::Perbill, + }, +}; + +use rococo::{self as rococo_api, runtime_types::polkadot_parachain_primitives::primitives}; + +type CoretimeRuntimeCall = coretime_api::runtime_types::coretime_rococo_runtime::RuntimeCall; +type CoretimeUtilityCall = coretime_api::runtime_types::pallet_utility::pallet::Call; +type CoretimeBrokerCall = coretime_api::runtime_types::pallet_broker::pallet::Call; + +// On-demand coretime base fee (set at the genesis) +const ON_DEMAND_BASE_FEE: u128 = 50_000_000; + +async fn get_total_issuance( + relay: OnlineClient, + coretime: OnlineClient, +) -> (u128, u128) { + ( + relay + .storage() + .at_latest() + .await + .unwrap() + .fetch(&rococo_api::storage().balances().total_issuance()) + .await + .unwrap() + .unwrap(), + coretime + .storage() + .at_latest() + .await + .unwrap() + .fetch(&coretime_api::storage().balances().total_issuance()) + .await + .unwrap() + .unwrap(), + ) +} + +async fn assert_total_issuance( + relay: OnlineClient, + coretime: OnlineClient, + ti: (u128, u128), +) { + let actual_ti = get_total_issuance(relay, coretime).await; + log::debug!("Asserting total issuance: actual: {actual_ti:?}, expected: {ti:?}"); + assert_eq!(ti, actual_ti); +} + +type ParaEvents = Arc)>>>; + +macro_rules! trace_event { + ($event:ident : $mod:ident => $($ev:ident),*) => { + match $event.variant_name() { + $( + stringify!($ev) => + log::trace!("{:#?}", $event.as_event::<$mod::$ev>().unwrap().unwrap()), + )* + _ => () + } + }; +} + +async fn para_watcher(api: OnlineClient, events: ParaEvents) +where + ::Number: Display, +{ + let mut blocks_sub = api.blocks().subscribe_finalized().await.unwrap(); + + log::debug!("Starting parachain watcher"); + while let Some(block) = blocks_sub.next().await { + let block = block.unwrap(); + log::debug!("Finalized parachain block {}", block.number()); + + for event in block.events().await.unwrap().iter() { + let event = event.unwrap(); + log::debug!("Got event: {} :: {}", event.pallet_name(), event.variant_name()); + { + events.write().await.push((block.number().into(), event.clone())); + } + + if event.pallet_name() == "Broker" { + trace_event!(event: broker_events => + Purchased, SaleInitialized, HistoryInitialized, CoreAssigned, Pooled, + ClaimsReady, RevenueClaimBegun, RevenueClaimItem, RevenueClaimPaid + ); + } + } + } +} + +async fn wait_for_para_event bool + Copy>( + events: ParaEvents, + pallet: &'static str, + variant: &'static str, + predicate: P, +) -> E { + loop { + let mut events = events.write().await; + if let Some(entry) = events.iter().find(|&e| { + e.1.pallet_name() == pallet && + e.1.variant_name() == variant && + predicate(&e.1.as_event::().unwrap().unwrap()) + }) { + let entry = entry.clone(); + events.retain(|e| e.0 > entry.0); + return entry.1.as_event::().unwrap().unwrap(); + } + drop(events); + tokio::time::sleep(std::time::Duration::from_secs(6)).await; + } +} + +async fn ti_watcher(api: OnlineClient, prefix: &'static str) +where + ::Number: Display, +{ + let mut blocks_sub = api.blocks().subscribe_finalized().await.unwrap(); + + let mut issuance = 0i128; + + log::debug!("Starting parachain watcher"); + while let Some(block) = blocks_sub.next().await { + let block = block.unwrap(); + + let ti = api + .storage() + .at(block.reference()) + .fetch(&rococo_api::storage().balances().total_issuance()) + .await + .unwrap() + .unwrap() as i128; + + let diff = ti - issuance; + if diff != 0 { + log::info!("{} #{} issuance {} ({:+})", prefix, block.number(), ti, diff); + } + issuance = ti; + } +} + +#[tokio::test(flavor = "multi_thread")] +async fn coretime_revenue_test() -> Result<(), anyhow::Error> { + env_logger::init_from_env( + env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"), + ); + + let images = zombienet_sdk::environment::get_images_from_env(); + let config = NetworkConfigBuilder::new() + .with_relaychain(|r| { + r.with_chain("rococo-local") + .with_default_command("polkadot") + .with_default_image(images.polkadot.as_str()) + .with_genesis_overrides( + json!({ "configuration": { "config": { "scheduler_params": { "on_demand_base_fee": ON_DEMAND_BASE_FEE }}}}), + ) + .with_node(|node| node.with_name("alice")) + .with_node(|node| node.with_name("bob")) + .with_node(|node| node.with_name("charlie")) + }) + .with_parachain(|p| { + p.with_id(1005) + .with_default_command("polkadot-parachain") + .with_default_image(images.cumulus.as_str()) + .with_chain("coretime-rococo-local") + .with_collator(|n| n.with_name("coretime")) + }) + .build() + .map_err(|e| { + let errs = e.into_iter().map(|e| e.to_string()).collect::>().join(" "); + anyhow!("config errs: {errs}") + })?; + + let spawn_fn = zombienet_sdk::environment::get_spawn_fn(); + let network = spawn_fn(config).await?; + + let relay_node = network.get_node("alice")?; + let para_node = network.get_node("coretime")?; + + let relay_client: OnlineClient = relay_node.wait_client().await?; + let para_client: OnlineClient = para_node.wait_client().await?; + + // Get total issuance on both sides + let mut total_issuance = get_total_issuance(relay_client.clone(), para_client.clone()).await; + log::info!("Reference total issuance: {total_issuance:?}"); + + // Prepare everything + let alice = dev::alice(); + let alice_acc = AccountId32(alice.public_key().0); + + let bob = dev::bob(); + + let para_events: ParaEvents = Arc::new(RwLock::new(Vec::new())); + let p_api = para_node.wait_client().await?; + let p_events = para_events.clone(); + + let _subscriber = tokio::spawn(async move { + para_watcher(p_api, p_events).await; + }); + + let api: OnlineClient = para_node.wait_client().await?; + let _s1 = tokio::spawn(async move { + ti_watcher(api, "PARA").await; + }); + let api: OnlineClient = relay_node.wait_client().await?; + let _s2 = tokio::spawn(async move { + ti_watcher(api, "RELAY").await; + }); + + log::info!("Initiating teleport from RC's account of Alice to PC's one"); + + // Teleport some Alice's tokens to the Coretime chain. Although her account is pre-funded on + // the PC, that is still neccessary to bootstrap RC's `CheckedAccount`. + relay_client + .tx() + .sign_and_submit_default( + &rococo_api::tx().xcm_pallet().teleport_assets( + VersionedLocation::V4(Location { + parents: 0, + interior: Junctions::X1([Junction::Parachain(1005)]), + }), + VersionedLocation::V4(Location { + parents: 0, + interior: Junctions::X1([Junction::AccountId32 { + network: None, + id: alice.public_key().0, + }]), + }), + VersionedAssets::V4(Assets(vec![Asset { + id: AssetId(Location { parents: 0, interior: Junctions::Here }), + fun: Fungibility::Fungible(1_500_000_000), + }])), + 0, + ), + &alice, + ) + .await?; + + wait_for_para_event( + para_events.clone(), + "Balances", + "Minted", + |e: &coretime_api::balances::events::Minted| e.who == alice_acc, + ) + .await; + + // RC's total issuance doen't change, but PC's one increases after the teleport. + + total_issuance.1 += 1_500_000_000; + assert_total_issuance(relay_client.clone(), para_client.clone(), total_issuance).await; + + log::info!("Initializing broker and starting sales"); + + // Initialize broker and start sales + + para_client + .tx() + .sign_and_submit_default( + &coretime_api::tx().sudo().sudo(CoretimeRuntimeCall::Utility( + CoretimeUtilityCall::batch { + calls: vec![ + CoretimeRuntimeCall::Broker(CoretimeBrokerCall::configure { + config: BrokerConfigRecord { + advance_notice: 5, + interlude_length: 1, + leadin_length: 1, + region_length: 1, + ideal_bulk_proportion: Perbill(100), + limit_cores_offered: None, + renewal_bump: Perbill(10), + contribution_timeout: 5, + }, + }), + CoretimeRuntimeCall::Broker(CoretimeBrokerCall::set_lease { + task: 1005, + until: 1000, + }), + CoretimeRuntimeCall::Broker(CoretimeBrokerCall::start_sales { + end_price: 45_000_000, + extra_cores: 2, + }), + ], + }, + )), + &alice, + ) + .await?; + + log::info!("Waiting for a full-length sale to begin"); + + // Skip the first sale completeley as it may be a short one. Also, `request_code_count` requires + // two session boundaries to propagate. Given that the `fast-runtime` session is 10 blocks and + // the timeslice is 20 blocks, we should be just in time. + + let _: coretime_api::broker::events::SaleInitialized = + wait_for_para_event(para_events.clone(), "Broker", "SaleInitialized", |_| true).await; + log::info!("Skipped short sale"); + + let sale: coretime_api::broker::events::SaleInitialized = + wait_for_para_event(para_events.clone(), "Broker", "SaleInitialized", |_| true).await; + log::info!("{:?}", sale); + + // Alice buys a region + + log::info!("Alice is going to buy a region"); + + para_client + .tx() + .sign_and_submit_default(&coretime_api::tx().broker().purchase(1_000_000_000), &alice) + .await?; + + let purchase = wait_for_para_event( + para_events.clone(), + "Broker", + "Purchased", + |e: &broker_events::Purchased| e.who == alice_acc, + ) + .await; + + let region_begin = purchase.region_id.begin; + + // Somewhere below this point, the revenue from this sale will be teleported to the RC and burnt + // on both chains. Let's account that but not assert just yet. + + total_issuance.0 -= purchase.price; + total_issuance.1 -= purchase.price; + + // Alice pools the region + + log::info!("Alice is going to put the region into the pool"); + + para_client + .tx() + .sign_and_submit_default( + &coretime_api::tx().broker().pool( + purchase.region_id, + alice_acc.clone(), + BrokerFinality::Final, + ), + &alice, + ) + .await?; + + let pooled = wait_for_para_event( + para_events.clone(), + "Broker", + "Pooled", + |e: &broker_events::Pooled| e.region_id.begin == region_begin, + ) + .await; + + // Wait until the beginning of the timeslice where the region belongs to + + log::info!("Waiting for the region to begin"); + + let hist = wait_for_para_event( + para_events.clone(), + "Broker", + "HistoryInitialized", + |e: &broker_events::HistoryInitialized| e.when == pooled.region_id.begin, + ) + .await; + + // Alice's private contribution should be there + + assert!(hist.private_pool_size > 0); + + // Bob places an order to buy insta coretime as RC + + log::info!("Bob is going to buy an on-demand core"); + + let r = relay_client + .tx() + .sign_and_submit_then_watch_default( + &rococo_api::tx() + .on_demand_assignment_provider() + .place_order_allow_death(100_000_000, primitives::Id(100)), + &bob, + ) + .await? + .wait_for_finalized_success() + .await?; + + let order = r + .find_first::()? + .unwrap(); + + // As there's no spot traffic, Bob will only pay base fee + + assert_eq!(order.spot_price, ON_DEMAND_BASE_FEE); + + // Somewhere below this point, revenue is generated and is teleported to the PC (that happens + // once a timeslice so we're not ready to assert it yet, let's just account). That checks out + // tokens from the RC and mints them on the PC. + + total_issuance.1 += ON_DEMAND_BASE_FEE; + + // As soon as the PC receives the tokens, it divides them half by half into system and private + // contributions (we have 3 cores, one is leased to Coretime itself, one is pooled by the + // system, and one is pooled by Alice). + + // Now we're waiting for the moment when Alice may claim her revenue + + log::info!("Waiting for Alice's revenue to be ready to claim"); + + let claims_ready = wait_for_para_event( + para_events.clone(), + "Broker", + "ClaimsReady", + |e: &broker_events::ClaimsReady| e.when == pooled.region_id.begin, + ) + .await; + + // The revenue should be half of the spot price, which is equal to the base fee. + + assert_eq!(claims_ready.private_payout, ON_DEMAND_BASE_FEE / 2); + + // By this moment, we're sure that revenue was received by the PC and can assert the total + // issuance + + assert_total_issuance(relay_client.clone(), para_client.clone(), total_issuance).await; + + // Alice claims her revenue + + log::info!("Alice is going to claim her revenue"); + + para_client + .tx() + .sign_and_submit_default( + &coretime_api::tx().broker().claim_revenue(pooled.region_id, pooled.duration), + &alice, + ) + .await?; + + let claim_paid = wait_for_para_event( + para_events.clone(), + "Broker", + "RevenueClaimPaid", + |e: &broker_events::RevenueClaimPaid| e.who == alice_acc, + ) + .await; + + log::info!("Revenue claimed, waiting for 2 timeslices until the system revenue is burnt"); + + assert_eq!(claim_paid.amount, ON_DEMAND_BASE_FEE / 2); + + // As for the system revenue, it is teleported back to the RC and burnt there. Those burns are + // batched and are processed once a timeslice, after a new one starts. So we have to wait for + // two timeslice boundaries to pass to be sure the teleport has already happened somewhere in + // between. + + let _: coretime_api::broker::events::SaleInitialized = + wait_for_para_event(para_events.clone(), "Broker", "SaleInitialized", |_| true).await; + + total_issuance.0 -= ON_DEMAND_BASE_FEE / 2; + total_issuance.1 -= ON_DEMAND_BASE_FEE / 2; + + let _: coretime_api::broker::events::SaleInitialized = + wait_for_para_event(para_events.clone(), "Broker", "SaleInitialized", |_| true).await; + + assert_total_issuance(relay_client.clone(), para_client.clone(), total_issuance).await; + + log::info!("Test finished successfuly"); + + Ok(()) +} diff --git a/polkadot/zombienet-sdk-tests/tests/smoke/mod.rs b/polkadot/zombienet-sdk-tests/tests/smoke/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..a3fe153826740dbd5d1f92e641fce21fc31cebeb --- /dev/null +++ b/polkadot/zombienet-sdk-tests/tests/smoke/mod.rs @@ -0,0 +1,5 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#[cfg(feature = "zombie-metadata")] +mod coretime_revenue; diff --git a/polkadot/zombienet_tests/functional/0003-beefy-and-mmr.zndsl b/polkadot/zombienet_tests/functional/0003-beefy-and-mmr.zndsl index 8300ef051f09a8903b2469b3f36330c729c66125..4fc066a13b07e9622b8aa605bd9fda27bfe1c1a6 100644 --- a/polkadot/zombienet_tests/functional/0003-beefy-and-mmr.zndsl +++ b/polkadot/zombienet_tests/functional/0003-beefy-and-mmr.zndsl @@ -18,22 +18,22 @@ validator-unstable: reports substrate_beefy_best_block is at least 1 within 60 s validator-unstable: pause # Verify validator sets get changed on new sessions. -validator: reports substrate_beefy_validator_set_id is at least 1 within 70 seconds +validator: reports substrate_beefy_validator_set_id is at least 1 within 180 seconds # Check next session too. -validator: reports substrate_beefy_validator_set_id is at least 2 within 130 seconds +validator: reports substrate_beefy_validator_set_id is at least 2 within 180 seconds # Verify voting happens and blocks are being finalized for new sessions too: # since we verified we're at least in the 3rd session, verify BEEFY finalized mandatory #21. -validator: reports substrate_beefy_best_block is at least 21 within 130 seconds +validator: reports substrate_beefy_best_block is at least 21 within 180 seconds # Custom JS to test BEEFY RPCs. -validator-0: js-script ./0003-beefy-finalized-heads.js with "validator-0,validator-1,validator-2" return is 1 within 5 seconds +validator-0: js-script ./0003-beefy-finalized-heads.js with "validator-0,validator-1,validator-2" return is 1 within 60 seconds # Custom JS to test MMR RPCs. -validator: js-script ./0003-mmr-leaves.js with "21" return is 1 within 5 seconds -validator: js-script ./0003-mmr-generate-and-verify-proof.js with "validator-0,validator-1,validator-2" return is 1 within 5 seconds +validator: js-script ./0003-mmr-leaves.js with "21" return is 1 within 60 seconds +validator: js-script ./0003-mmr-generate-and-verify-proof.js with "validator-0,validator-1,validator-2" return is 1 within 60 seconds # Resume validator-unstable and verify it imports all BEEFY justification and catches up. validator-unstable: resume -validator-unstable: reports substrate_beefy_validator_set_id is at least 2 within 30 seconds -validator-unstable: reports substrate_beefy_best_block is at least 21 within 30 seconds +validator-unstable: reports substrate_beefy_validator_set_id is at least 2 within 60 seconds +validator-unstable: reports substrate_beefy_best_block is at least 21 within 60 seconds diff --git a/polkadot/zombienet_tests/functional/0009-approval-voting-coalescing.toml b/polkadot/zombienet_tests/functional/0009-approval-voting-coalescing.toml index 19c7015403d7d86b3ece2e7006995e86fc9c0ab7..113de0e73aa1028519abeb8aa3f1da020e8e5f7f 100644 --- a/polkadot/zombienet_tests/functional/0009-approval-voting-coalescing.toml +++ b/polkadot/zombienet_tests/functional/0009-approval-voting-coalescing.toml @@ -18,7 +18,7 @@ requests = { memory = "2G", cpu = "1" } [[relaychain.node_groups]] name = "alice" - args = [ "-lparachain=trace,runtime=debug" ] + args = [ "-lparachain=debug,runtime=debug" ] count = 13 [[parachains]] diff --git a/polkadot/zombienet_tests/functional/0016-approval-voting-parallel.toml b/polkadot/zombienet_tests/functional/0016-approval-voting-parallel.toml new file mode 100644 index 0000000000000000000000000000000000000000..c035e23639c1421beeea6e95713b66bb4dec654c --- /dev/null +++ b/polkadot/zombienet_tests/functional/0016-approval-voting-parallel.toml @@ -0,0 +1,120 @@ +[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 = 2 + +[relaychain.genesis.runtimeGenesis.patch.configuration.config.approval_voting_params] + max_approval_coalesce_count = 5 + +[relaychain.default_resources] +limits = { memory = "4G", cpu = "2" } +requests = { memory = "2G", cpu = "1" } + + [[relaychain.node_groups]] + name = "alice" + args = ["-lparachain=debug,runtime=debug", "--enable-approval-voting-parallel"] + count = 8 + + [[relaychain.node_groups]] + name = "bob" + args = ["-lparachain=debug,runtime=debug"] + count = 7 + +[[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 +addToGenesis = true +genesis_state_generator = "undying-collator export-genesis-state --pov-size=100000 --pvf-complexity=10" + + [parachains.collator] + name = "collator02" + image = "{{COL_IMAGE}}" + command = "undying-collator" + args = ["-lparachain=debug", "--pov-size=100000", "--parachain-id=2001", "--pvf-complexity=10"] + +[[parachains]] +id = 2002 +addToGenesis = true +genesis_state_generator = "undying-collator export-genesis-state --pov-size=100000 --pvf-complexity=100" + + [parachains.collator] + name = "collator03" + image = "{{COL_IMAGE}}" + command = "undying-collator" + args = ["-lparachain=debug", "--pov-size=100000", "--parachain-id=2002", "--pvf-complexity=100"] + +[[parachains]] +id = 2003 +addToGenesis = true +genesis_state_generator = "undying-collator export-genesis-state --pov-size=20000 --pvf-complexity=300" + + [parachains.collator] + name = "collator04" + image = "{{COL_IMAGE}}" + command = "undying-collator" + args = ["-lparachain=debug", "--pov-size=20000", "--parachain-id=2003", "--pvf-complexity=300"] + +[[parachains]] +id = 2004 +addToGenesis = true +genesis_state_generator = "undying-collator export-genesis-state --pov-size=100000 --pvf-complexity=300" + + [parachains.collator] + name = "collator05" + image = "{{COL_IMAGE}}" + command = "undying-collator" + args = ["-lparachain=debug", "--pov-size=100000", "--parachain-id=2004", "--pvf-complexity=300"] + +[[parachains]] +id = 2005 +addToGenesis = true +genesis_state_generator = "undying-collator export-genesis-state --pov-size=20000 --pvf-complexity=400" + + [parachains.collator] + name = "collator06" + image = "{{COL_IMAGE}}" + command = "undying-collator" + args = ["-lparachain=debug", "--pov-size=20000", "--pvf-complexity=400", "--parachain-id=2005"] + +[[parachains]] +id = 2006 +addToGenesis = true +genesis_state_generator = "undying-collator export-genesis-state --pov-size=100000 --pvf-complexity=300" + + [parachains.collator] + name = "collator07" + image = "{{COL_IMAGE}}" + command = "undying-collator" + args = ["-lparachain=debug", "--pov-size=100000", "--pvf-complexity=300", "--parachain-id=2006"] + +[[parachains]] +id = 2007 +addToGenesis = true +genesis_state_generator = "undying-collator export-genesis-state --pov-size=100000 --pvf-complexity=300" + + [parachains.collator] + name = "collator08" + image = "{{COL_IMAGE}}" + command = "undying-collator" + args = ["-lparachain=debug", "--pov-size=100000", "--pvf-complexity=300", "--parachain-id=2007"] + +[types.Header] +number = "u64" +parent_hash = "Hash" +post_state = "Hash" \ No newline at end of file diff --git a/polkadot/zombienet_tests/functional/0016-approval-voting-parallel.zndsl b/polkadot/zombienet_tests/functional/0016-approval-voting-parallel.zndsl new file mode 100644 index 0000000000000000000000000000000000000000..d7070774747418b1e038e725ce81018e90147367 --- /dev/null +++ b/polkadot/zombienet_tests/functional/0016-approval-voting-parallel.zndsl @@ -0,0 +1,35 @@ +Description: Check finality works with approval voting parallel enabled +Network: ./0016-approval-voting-parallel.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 +alice: parachain 2002 is registered within 60 seconds +alice: parachain 2003 is registered within 60 seconds +alice: parachain 2004 is registered within 60 seconds +alice: parachain 2005 is registered within 60 seconds +alice: parachain 2006 is registered within 60 seconds +alice: parachain 2007 is registered within 60 seconds + +# Ensure parachains made progress. +alice: parachain 2000 block height is at least 10 within 300 seconds +alice: parachain 2001 block height is at least 10 within 300 seconds +alice: parachain 2002 block height is at least 10 within 300 seconds +alice: parachain 2003 block height is at least 10 within 300 seconds +alice: parachain 2004 block height is at least 10 within 300 seconds +alice: parachain 2005 block height is at least 10 within 300 seconds +alice: parachain 2006 block height is at least 10 within 300 seconds +alice: parachain 2007 block height is at least 10 within 300 seconds + +alice: reports substrate_block_height{status="finalized"} is at least 30 within 180 seconds +bob: reports substrate_block_height{status="finalized"} is at least 30 within 180 seconds + +alice: reports polkadot_parachain_approval_checking_finality_lag < 3 +bob: reports polkadot_parachain_approval_checking_finality_lag < 3 + +alice: reports polkadot_parachain_approvals_no_shows_total < 3 within 10 seconds +bob: reports polkadot_parachain_approvals_no_shows_total < 3 within 10 seconds diff --git a/prdoc/pr_2923.prdoc b/prdoc/1.16.0/pr_2923.prdoc similarity index 100% rename from prdoc/pr_2923.prdoc rename to prdoc/1.16.0/pr_2923.prdoc diff --git a/prdoc/1.16.0/pr_3049.prdoc b/prdoc/1.16.0/pr_3049.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..9cead8e2a4e5c6b0232f66c96a87f5efe41dd18f --- /dev/null +++ b/prdoc/1.16.0/pr_3049.prdoc @@ -0,0 +1,11 @@ +title: "Fix treasury benchmarks when `SpendOrigin` being `None`" + +doc: + - audience: Runtime Dev + description: | + Fix treasury benchmarks when `SpendOrigin` not returning any succesful origin. + This is for example the case when `SpendOrigin` is set to `NeverOrigin`. + +crates: + - name: pallet-treasury + bump: patch diff --git a/prdoc/pr_3786.prdoc b/prdoc/1.16.0/pr_3786.prdoc similarity index 100% rename from prdoc/pr_3786.prdoc rename to prdoc/1.16.0/pr_3786.prdoc diff --git a/prdoc/1.16.0/pr_3996.prdoc b/prdoc/1.16.0/pr_3996.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..7590d8992368d61936df1f5a82f4cdddb9dc115a --- /dev/null +++ b/prdoc/1.16.0/pr_3996.prdoc @@ -0,0 +1,20 @@ +title: asset-hub-rococo - genesis config presets added + +doc: + - audience: Node Dev + description: | + `asset-hub-rococo` genesis state was moved to runtime. + - audience: Runtime Dev + description: | + `asset-hub-rococo` genesis state was moved to runtime. + + +crates: + - name: parachains-common + bump: minor + - name: testnet-parachains-constants + bump: minor + - name: asset-hub-rococo-runtime + bump: minor + - name: polkadot-parachain-bin + bump: minor diff --git a/prdoc/pr_4129.prdoc b/prdoc/1.16.0/pr_4129.prdoc similarity index 100% rename from prdoc/pr_4129.prdoc rename to prdoc/1.16.0/pr_4129.prdoc diff --git a/prdoc/pr_4424.prdoc b/prdoc/1.16.0/pr_4424.prdoc similarity index 100% rename from prdoc/pr_4424.prdoc rename to prdoc/1.16.0/pr_4424.prdoc diff --git a/prdoc/pr_4460.prdoc b/prdoc/1.16.0/pr_4460.prdoc similarity index 100% rename from prdoc/pr_4460.prdoc rename to prdoc/1.16.0/pr_4460.prdoc diff --git a/prdoc/pr_4487.prdoc b/prdoc/1.16.0/pr_4487.prdoc similarity index 100% rename from prdoc/pr_4487.prdoc rename to prdoc/1.16.0/pr_4487.prdoc diff --git a/prdoc/pr_4488.prdoc b/prdoc/1.16.0/pr_4488.prdoc similarity index 100% rename from prdoc/pr_4488.prdoc rename to prdoc/1.16.0/pr_4488.prdoc diff --git a/prdoc/pr_4527.prdoc b/prdoc/1.16.0/pr_4527.prdoc similarity index 100% rename from prdoc/pr_4527.prdoc rename to prdoc/1.16.0/pr_4527.prdoc diff --git a/prdoc/pr_4564.prdoc b/prdoc/1.16.0/pr_4564.prdoc similarity index 100% rename from prdoc/pr_4564.prdoc rename to prdoc/1.16.0/pr_4564.prdoc diff --git a/prdoc/pr_4586.prdoc b/prdoc/1.16.0/pr_4586.prdoc similarity index 100% rename from prdoc/pr_4586.prdoc rename to prdoc/1.16.0/pr_4586.prdoc diff --git a/prdoc/pr_4613.prdoc b/prdoc/1.16.0/pr_4613.prdoc similarity index 100% rename from prdoc/pr_4613.prdoc rename to prdoc/1.16.0/pr_4613.prdoc diff --git a/prdoc/pr_4640.prdoc b/prdoc/1.16.0/pr_4640.prdoc similarity index 100% rename from prdoc/pr_4640.prdoc rename to prdoc/1.16.0/pr_4640.prdoc diff --git a/prdoc/pr_4665.prdoc b/prdoc/1.16.0/pr_4665.prdoc similarity index 100% rename from prdoc/pr_4665.prdoc rename to prdoc/1.16.0/pr_4665.prdoc diff --git a/prdoc/pr_4706.prdoc b/prdoc/1.16.0/pr_4706.prdoc similarity index 100% rename from prdoc/pr_4706.prdoc rename to prdoc/1.16.0/pr_4706.prdoc diff --git a/prdoc/1.16.0/pr_4739.prdoc b/prdoc/1.16.0/pr_4739.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..9ca230e3e763ea3aca0ca598187d8e380c038607 --- /dev/null +++ b/prdoc/1.16.0/pr_4739.prdoc @@ -0,0 +1,15 @@ +title: parachain-template - genesis config presets added + +doc: + - audience: Node Dev + description: | + - common DEV_RUNTIME_PRESET ("development") const added to sp-genesis-builder. + - parachain-templates are now using presets. + +crates: + - name: sp-genesis-builder + bump: minor + - name: parachain-template-node + bump: patch + - name: parachain-template-runtime + bump: patch diff --git a/prdoc/pr_4751.prdoc b/prdoc/1.16.0/pr_4751.prdoc similarity index 100% rename from prdoc/pr_4751.prdoc rename to prdoc/1.16.0/pr_4751.prdoc diff --git a/prdoc/pr_4792.prdoc b/prdoc/1.16.0/pr_4792.prdoc similarity index 100% rename from prdoc/pr_4792.prdoc rename to prdoc/1.16.0/pr_4792.prdoc diff --git a/prdoc/pr_4822.prdoc b/prdoc/1.16.0/pr_4822.prdoc similarity index 100% rename from prdoc/pr_4822.prdoc rename to prdoc/1.16.0/pr_4822.prdoc diff --git a/prdoc/pr_4845.prdoc b/prdoc/1.16.0/pr_4845.prdoc similarity index 100% rename from prdoc/pr_4845.prdoc rename to prdoc/1.16.0/pr_4845.prdoc diff --git a/prdoc/1.16.0/pr_4928.prdoc b/prdoc/1.16.0/pr_4928.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..9935652dc511a47ccd0b4cedbd14090822f1eb00 --- /dev/null +++ b/prdoc/1.16.0/pr_4928.prdoc @@ -0,0 +1,28 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Move assignment VRF check and vote signature in approval-distribution + +doc: + - audience: Node Dev + description: | + This PR moves the assignment VRF check and approval vote signature from approval-voting into approval-distribution. + This optimization creates a better pipelining for processing new messages, because in this way approval-distribution + does not have to wait after approval-voting anymore and it will just notify it when it received a valid message that + is ready to be imported. + +crates: + - name: polkadot-node-subsystem-types + bump: major + - name: polkadot-approval-distribution + bump: major + - name: polkadot-node-core-approval-voting + bump: major + - name: polkadot-node-primitives + bump: major + - name: polkadot-service + bump: major + - name: polkadot-subsystem-bench + bump: major + - name: polkadot-overseer + bump: patch \ No newline at end of file diff --git a/prdoc/pr_4930.prdoc b/prdoc/1.16.0/pr_4930.prdoc similarity index 100% rename from prdoc/pr_4930.prdoc rename to prdoc/1.16.0/pr_4930.prdoc diff --git a/prdoc/pr_4936.prdoc b/prdoc/1.16.0/pr_4936.prdoc similarity index 100% rename from prdoc/pr_4936.prdoc rename to prdoc/1.16.0/pr_4936.prdoc diff --git a/prdoc/pr_4938.prdoc b/prdoc/1.16.0/pr_4938.prdoc similarity index 100% rename from prdoc/pr_4938.prdoc rename to prdoc/1.16.0/pr_4938.prdoc diff --git a/prdoc/1.16.0/pr_4949.prdoc b/prdoc/1.16.0/pr_4949.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..4a5c09c6fa8de62972bf193d5796e0b36b01b878 --- /dev/null +++ b/prdoc/1.16.0/pr_4949.prdoc @@ -0,0 +1,78 @@ +title: "[bridges-v2] Permissionless lanes" + +doc: +- audience: Runtime Dev + description: | + This PR adds support for opening and closing dynamic, also known as permissionless, lanes. + This means that authorized origins (relay chain, sibling parachains) + can open and close bridges with other bridged (substrate-like) consensuses supported by Bridge Hubs. + The Bridge Hubs, particularly the `pallet-xcm-bridge-hub`, introduce new extrinsics `open_bridge` and `close_bridge`, + which can be called using `xcm::Transact`. + +crates: +- name: bridge-runtime-common + bump: major +- name: bp-bridge-hub-rococo + bump: minor +- name: bp-bridge-hub-westend + bump: minor +- name: pallet-bridge-grandpa + bump: major +- name: pallet-bridge-messages + bump: major +- name: pallet-bridge-parachains + bump: major +- name: pallet-bridge-relayers + bump: major +- name: pallet-xcm-bridge-hub + bump: major +- name: pallet-xcm-bridge-hub-router + bump: major +- name: bp-header-chain + bump: patch +- name: bp-messages + bump: major +- name: bp-parachains + bump: major +- name: bp-polkadot-core + bump: none +- name: bp-relayers + bump: major +- name: bp-runtime + bump: minor +- name: bp-xcm-bridge-hub-router + bump: patch +- name: bp-xcm-bridge-hub + bump: major +- name: relay-substrate-client + bump: none +- name: substrate-relay-helper + bump: major +- name: messages-relay + bump: major +- name: parachains-relay + bump: none +- name: cumulus-pallet-xcmp-queue + bump: patch +- name: parachains-relay + bump: none +- name: asset-hub-rococo-runtime + bump: major +- name: asset-hub-westend-runtime + bump: major +- name: bridge-hub-rococo-runtime + bump: major +- name: bridge-hub-westend-runtime + bump: major +- name: emulated-integration-tests-common + bump: minor +- name: asset-test-utils + bump: patch +- name: parachains-runtimes-test-utils + bump: minor +- name: bridge-hub-common + bump: minor +- name: bridge-hub-test-utils + bump: major +- name: xcm-emulator + bump: major diff --git a/prdoc/pr_4956.prdoc b/prdoc/1.16.0/pr_4956.prdoc similarity index 100% rename from prdoc/pr_4956.prdoc rename to prdoc/1.16.0/pr_4956.prdoc diff --git a/prdoc/pr_4959.prdoc b/prdoc/1.16.0/pr_4959.prdoc similarity index 100% rename from prdoc/pr_4959.prdoc rename to prdoc/1.16.0/pr_4959.prdoc diff --git a/prdoc/pr_4962.prdoc b/prdoc/1.16.0/pr_4962.prdoc similarity index 100% rename from prdoc/pr_4962.prdoc rename to prdoc/1.16.0/pr_4962.prdoc diff --git a/prdoc/pr_4963.prdoc b/prdoc/1.16.0/pr_4963.prdoc similarity index 100% rename from prdoc/pr_4963.prdoc rename to prdoc/1.16.0/pr_4963.prdoc diff --git a/prdoc/pr_4967.prdoc b/prdoc/1.16.0/pr_4967.prdoc similarity index 100% rename from prdoc/pr_4967.prdoc rename to prdoc/1.16.0/pr_4967.prdoc diff --git a/prdoc/pr_4970.prdoc b/prdoc/1.16.0/pr_4970.prdoc similarity index 100% rename from prdoc/pr_4970.prdoc rename to prdoc/1.16.0/pr_4970.prdoc diff --git a/prdoc/pr_4973.prdoc b/prdoc/1.16.0/pr_4973.prdoc similarity index 100% rename from prdoc/pr_4973.prdoc rename to prdoc/1.16.0/pr_4973.prdoc diff --git a/prdoc/pr_4976.prdoc b/prdoc/1.16.0/pr_4976.prdoc similarity index 100% rename from prdoc/pr_4976.prdoc rename to prdoc/1.16.0/pr_4976.prdoc diff --git a/prdoc/pr_4993.prdoc b/prdoc/1.16.0/pr_4993.prdoc similarity index 100% rename from prdoc/pr_4993.prdoc rename to prdoc/1.16.0/pr_4993.prdoc diff --git a/prdoc/pr_4998.prdoc b/prdoc/1.16.0/pr_4998.prdoc similarity index 100% rename from prdoc/pr_4998.prdoc rename to prdoc/1.16.0/pr_4998.prdoc diff --git a/prdoc/pr_4999.prdoc b/prdoc/1.16.0/pr_4999.prdoc similarity index 100% rename from prdoc/pr_4999.prdoc rename to prdoc/1.16.0/pr_4999.prdoc diff --git a/prdoc/pr_5029.prdoc b/prdoc/1.16.0/pr_5029.prdoc similarity index 100% rename from prdoc/pr_5029.prdoc rename to prdoc/1.16.0/pr_5029.prdoc diff --git a/prdoc/pr_5036.prdoc b/prdoc/1.16.0/pr_5036.prdoc similarity index 100% rename from prdoc/pr_5036.prdoc rename to prdoc/1.16.0/pr_5036.prdoc diff --git a/prdoc/pr_5055.prdoc b/prdoc/1.16.0/pr_5055.prdoc similarity index 100% rename from prdoc/pr_5055.prdoc rename to prdoc/1.16.0/pr_5055.prdoc diff --git a/prdoc/pr_5065.prdoc b/prdoc/1.16.0/pr_5065.prdoc similarity index 100% rename from prdoc/pr_5065.prdoc rename to prdoc/1.16.0/pr_5065.prdoc diff --git a/prdoc/pr_5067.prdoc b/prdoc/1.16.0/pr_5067.prdoc similarity index 100% rename from prdoc/pr_5067.prdoc rename to prdoc/1.16.0/pr_5067.prdoc diff --git a/prdoc/pr_5074.prdoc b/prdoc/1.16.0/pr_5074.prdoc similarity index 100% rename from prdoc/pr_5074.prdoc rename to prdoc/1.16.0/pr_5074.prdoc diff --git a/prdoc/pr_5078.prdoc b/prdoc/1.16.0/pr_5078.prdoc similarity index 100% rename from prdoc/pr_5078.prdoc rename to prdoc/1.16.0/pr_5078.prdoc diff --git a/prdoc/pr_5082.prdoc b/prdoc/1.16.0/pr_5082.prdoc similarity index 100% rename from prdoc/pr_5082.prdoc rename to prdoc/1.16.0/pr_5082.prdoc diff --git a/prdoc/pr_5113.prdoc b/prdoc/1.16.0/pr_5113.prdoc similarity index 100% rename from prdoc/pr_5113.prdoc rename to prdoc/1.16.0/pr_5113.prdoc diff --git a/prdoc/pr_5114.prdoc b/prdoc/1.16.0/pr_5114.prdoc similarity index 100% rename from prdoc/pr_5114.prdoc rename to prdoc/1.16.0/pr_5114.prdoc diff --git a/prdoc/pr_5124.prdoc b/prdoc/1.16.0/pr_5124.prdoc similarity index 100% rename from prdoc/pr_5124.prdoc rename to prdoc/1.16.0/pr_5124.prdoc diff --git a/prdoc/1.16.0/pr_5127.prdoc b/prdoc/1.16.0/pr_5127.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..c08f4e7fb8fa5b55c98683ed53076be80f1ef21b --- /dev/null +++ b/prdoc/1.16.0/pr_5127.prdoc @@ -0,0 +1,25 @@ +title: Add benchmark to check upcoming minimum required hw cores + +doc: + - audience: Node Operator + description: | + Add benchmark that checks hardware satisifies the minimum required hardware cores + for a validators. The new minimum requirements are schedule to come into effect + in January 2025, for more details see: https://polkadot.subsquare.io/referenda/1051. + + +crates: + - name: sc-sysinfo + bump: major + - name: frame-benchmarking-cli + bump: major + - name: staging-node-cli + bump: patch + - name: polkadot-service + bump: patch + - name: polkadot-parachain-lib + bump: patch + - name: polkadot-cli + bump: patch + - name: parachain-template-node + bump: patch diff --git a/prdoc/pr_5129.prdoc b/prdoc/1.16.0/pr_5129.prdoc similarity index 100% rename from prdoc/pr_5129.prdoc rename to prdoc/1.16.0/pr_5129.prdoc diff --git a/prdoc/pr_5130.prdoc b/prdoc/1.16.0/pr_5130.prdoc similarity index 100% rename from prdoc/pr_5130.prdoc rename to prdoc/1.16.0/pr_5130.prdoc diff --git a/prdoc/1.16.0/pr_5131.prdoc b/prdoc/1.16.0/pr_5131.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..db1003ab40338875891cade7258011c3d4f9fd9d --- /dev/null +++ b/prdoc/1.16.0/pr_5131.prdoc @@ -0,0 +1,42 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Swap for paying delivery fees in different assets + +doc: + - audience: Runtime User + description: | + If the `AssetExchanger` is configured on a runtime, the XCM executor is now able to swap assets + to pay for delivery fees. + This was already possible for execution fees via the `SwapFirstAssetTrader`. + A runtime where this will be possible is Asset Hub. + That means reserve asset transfers from Parachain A to Parachain B passing through Asset Hub no + longer need to have any DOT to pay for fees on AssetHub. + They can have any asset in a pool with DOT on Asset Hub, for example USDT or USDC. + - audience: Runtime Dev + description: | + Using the `AssetExchanger` XCM config item, the executor now swaps fees to use for delivery fees, + if possible. + If you want your runtime to support this, you need to configure this new item. + Thankfully, `xcm-builder` now has a new adapter for this, which lets you use `pallet-asset-conversion` + or any type that implements the `SwapCredit` and `QuotePrice` traits. + It's called `SingleAssetExchangeAdapter`, you can read more about it in its rust docs. + This item is already configured in Asset Hub. + + IMPORTANT: The executor now only takes the first asset for delivery fees. If you have configured a custom router + that returns more than one asset for delivery fees, then only the first one will be taken into account. + This is most likely not what you want. + +crates: + - name: staging-xcm-executor + bump: minor + - name: asset-hub-westend-runtime + bump: minor + - name: asset-hub-rococo-runtime + bump: minor + - name: staging-xcm-builder + bump: patch + - name: assets-common + bump: patch + - name: penpal-runtime + bump: minor diff --git a/prdoc/pr_5132.prdoc b/prdoc/1.16.0/pr_5132.prdoc similarity index 100% rename from prdoc/pr_5132.prdoc rename to prdoc/1.16.0/pr_5132.prdoc diff --git a/prdoc/pr_5142.prdoc b/prdoc/1.16.0/pr_5142.prdoc similarity index 100% rename from prdoc/pr_5142.prdoc rename to prdoc/1.16.0/pr_5142.prdoc diff --git a/prdoc/pr_5155.prdoc b/prdoc/1.16.0/pr_5155.prdoc similarity index 100% rename from prdoc/pr_5155.prdoc rename to prdoc/1.16.0/pr_5155.prdoc diff --git a/prdoc/pr_5173.prdoc b/prdoc/1.16.0/pr_5173.prdoc similarity index 100% rename from prdoc/pr_5173.prdoc rename to prdoc/1.16.0/pr_5173.prdoc diff --git a/prdoc/pr_5174.prdoc b/prdoc/1.16.0/pr_5174.prdoc similarity index 100% rename from prdoc/pr_5174.prdoc rename to prdoc/1.16.0/pr_5174.prdoc diff --git a/prdoc/pr_5188.prdoc b/prdoc/1.16.0/pr_5188.prdoc similarity index 100% rename from prdoc/pr_5188.prdoc rename to prdoc/1.16.0/pr_5188.prdoc diff --git a/prdoc/pr_5195.prdoc b/prdoc/1.16.0/pr_5195.prdoc similarity index 100% rename from prdoc/pr_5195.prdoc rename to prdoc/1.16.0/pr_5195.prdoc diff --git a/prdoc/pr_5196.prdoc b/prdoc/1.16.0/pr_5196.prdoc similarity index 100% rename from prdoc/pr_5196.prdoc rename to prdoc/1.16.0/pr_5196.prdoc diff --git a/prdoc/pr_5197.prdoc b/prdoc/1.16.0/pr_5197.prdoc similarity index 100% rename from prdoc/pr_5197.prdoc rename to prdoc/1.16.0/pr_5197.prdoc diff --git a/prdoc/pr_5204.prdoc b/prdoc/1.16.0/pr_5204.prdoc similarity index 100% rename from prdoc/pr_5204.prdoc rename to prdoc/1.16.0/pr_5204.prdoc diff --git a/prdoc/pr_5205.prdoc b/prdoc/1.16.0/pr_5205.prdoc similarity index 100% rename from prdoc/pr_5205.prdoc rename to prdoc/1.16.0/pr_5205.prdoc diff --git a/prdoc/pr_5214.prdoc b/prdoc/1.16.0/pr_5214.prdoc similarity index 100% rename from prdoc/pr_5214.prdoc rename to prdoc/1.16.0/pr_5214.prdoc diff --git a/prdoc/pr_5240.prdoc b/prdoc/1.16.0/pr_5240.prdoc similarity index 100% rename from prdoc/pr_5240.prdoc rename to prdoc/1.16.0/pr_5240.prdoc diff --git a/prdoc/pr_5250.prdoc b/prdoc/1.16.0/pr_5250.prdoc similarity index 100% rename from prdoc/pr_5250.prdoc rename to prdoc/1.16.0/pr_5250.prdoc diff --git a/prdoc/pr_5252.prdoc b/prdoc/1.16.0/pr_5252.prdoc similarity index 100% rename from prdoc/pr_5252.prdoc rename to prdoc/1.16.0/pr_5252.prdoc diff --git a/prdoc/pr_5257.prdoc b/prdoc/1.16.0/pr_5257.prdoc similarity index 100% rename from prdoc/pr_5257.prdoc rename to prdoc/1.16.0/pr_5257.prdoc diff --git a/prdoc/pr_5262.prdoc b/prdoc/1.16.0/pr_5262.prdoc similarity index 100% rename from prdoc/pr_5262.prdoc rename to prdoc/1.16.0/pr_5262.prdoc diff --git a/prdoc/pr_5269.prdoc b/prdoc/1.16.0/pr_5269.prdoc similarity index 100% rename from prdoc/pr_5269.prdoc rename to prdoc/1.16.0/pr_5269.prdoc diff --git a/prdoc/pr_5270.prdoc b/prdoc/1.16.0/pr_5270.prdoc similarity index 100% rename from prdoc/pr_5270.prdoc rename to prdoc/1.16.0/pr_5270.prdoc diff --git a/prdoc/pr_5284.prdoc b/prdoc/1.16.0/pr_5284.prdoc similarity index 100% rename from prdoc/pr_5284.prdoc rename to prdoc/1.16.0/pr_5284.prdoc diff --git a/prdoc/pr_5288.prdoc b/prdoc/1.16.0/pr_5288.prdoc similarity index 100% rename from prdoc/pr_5288.prdoc rename to prdoc/1.16.0/pr_5288.prdoc diff --git a/prdoc/pr_5293.prdoc b/prdoc/1.16.0/pr_5293.prdoc similarity index 100% rename from prdoc/pr_5293.prdoc rename to prdoc/1.16.0/pr_5293.prdoc diff --git a/prdoc/pr_5316.prdoc b/prdoc/1.16.0/pr_5316.prdoc similarity index 100% rename from prdoc/pr_5316.prdoc rename to prdoc/1.16.0/pr_5316.prdoc diff --git a/prdoc/pr_5326.prdoc b/prdoc/1.16.0/pr_5326.prdoc similarity index 100% rename from prdoc/pr_5326.prdoc rename to prdoc/1.16.0/pr_5326.prdoc diff --git a/prdoc/1.16.0/pr_5327.prdoc b/prdoc/1.16.0/pr_5327.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..a3821790590b11b3791caa56786e393b5ac83010 --- /dev/null +++ b/prdoc/1.16.0/pr_5327.prdoc @@ -0,0 +1,43 @@ +# 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: "Moved presets to the testnet runtimes" + +doc: + - audience: Runtime Dev + description: | + This PR migrates the genesis config presets from `polkadot-parachain-bin` to the relevant runtimes. + +crates: + - name: polkadot-runtime-common + bump: patch + - name: rococo-runtime + bump: patch + - name: westend-runtime + bump: patch + - name: parachains-common + bump: patch + - name: testnet-parachains-constants + bump: patch + - name: asset-hub-rococo-runtime + bump: patch + - name: asset-hub-westend-runtime + bump: patch + - name: bridge-hub-rococo-runtime + bump: patch + - name: bridge-hub-westend-runtime + bump: patch + - name: collectives-westend-runtime + bump: patch + - name: polkadot-parachain-bin + bump: patch + - name: polkadot-runtime-parachains + bump: none + - name: polkadot-service + bump: major + - name: polkadot-cli + bump: patch + - name: sc-chain-spec + bump: none + - name: sp-genesis-builder + bump: none diff --git a/prdoc/pr_5339.prdoc b/prdoc/1.16.0/pr_5339.prdoc similarity index 100% rename from prdoc/pr_5339.prdoc rename to prdoc/1.16.0/pr_5339.prdoc diff --git a/prdoc/pr_5344.prdoc b/prdoc/1.16.0/pr_5344.prdoc similarity index 100% rename from prdoc/pr_5344.prdoc rename to prdoc/1.16.0/pr_5344.prdoc diff --git a/prdoc/pr_5348.prdoc b/prdoc/1.16.0/pr_5348.prdoc similarity index 100% rename from prdoc/pr_5348.prdoc rename to prdoc/1.16.0/pr_5348.prdoc diff --git a/prdoc/pr_5352.prdoc b/prdoc/1.16.0/pr_5352.prdoc similarity index 100% rename from prdoc/pr_5352.prdoc rename to prdoc/1.16.0/pr_5352.prdoc diff --git a/prdoc/pr_5354.prdoc b/prdoc/1.16.0/pr_5354.prdoc similarity index 100% rename from prdoc/pr_5354.prdoc rename to prdoc/1.16.0/pr_5354.prdoc diff --git a/prdoc/pr_5356.prdoc b/prdoc/1.16.0/pr_5356.prdoc similarity index 100% rename from prdoc/pr_5356.prdoc rename to prdoc/1.16.0/pr_5356.prdoc diff --git a/prdoc/pr_5359.prdoc b/prdoc/1.16.0/pr_5359.prdoc similarity index 100% rename from prdoc/pr_5359.prdoc rename to prdoc/1.16.0/pr_5359.prdoc diff --git a/prdoc/pr_5360.prdoc b/prdoc/1.16.0/pr_5360.prdoc similarity index 100% rename from prdoc/pr_5360.prdoc rename to prdoc/1.16.0/pr_5360.prdoc diff --git a/prdoc/1.16.0/pr_5364.prdoc b/prdoc/1.16.0/pr_5364.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..35a72f65fb1e20997dca27cec035b2b5aa9e7fb0 --- /dev/null +++ b/prdoc/1.16.0/pr_5364.prdoc @@ -0,0 +1,39 @@ +title: Improve `sc-service` API + +doc: + - audience: Node Dev + description: | + This improves `sc-service` API by not requiring the whole `&Configuration`, using specific configuration options + instead. `RpcConfiguration` and `ExecutorConfiguration` were also extracted from `Configuration` to group all RPC + and executor options together. + If `sc-service` is used as a library with lower-level APIs, `Configuration` can now be avoided in most cases. + + This mainly impacts you on your node implementation. There you need to change this: + ``` + with_execution_method(config.wasm_method) + ``` + + to this: + ``` + with_execution_method(config.executor.wasm_method) + ``` + + There are similar changes required as well, but all are around the initialization of the wasm executor. + +crates: + - name: sc-service + bump: major + - name: sc-network-common + bump: patch + - name: sc-cli + bump: major + - name: polkadot-service + bump: patch + - name: cumulus-relay-chain-minimal-node + bump: none + - name: polkadot-parachain-bin + bump: major + - name: polkadot-parachain-lib + bump: major + - name: staging-node-inspect + bump: major diff --git a/prdoc/pr_5369.prdoc b/prdoc/1.16.0/pr_5369.prdoc similarity index 100% rename from prdoc/pr_5369.prdoc rename to prdoc/1.16.0/pr_5369.prdoc diff --git a/prdoc/pr_5376.prdoc b/prdoc/1.16.0/pr_5376.prdoc similarity index 100% rename from prdoc/pr_5376.prdoc rename to prdoc/1.16.0/pr_5376.prdoc diff --git a/prdoc/pr_5380.prdoc b/prdoc/1.16.0/pr_5380.prdoc similarity index 100% rename from prdoc/pr_5380.prdoc rename to prdoc/1.16.0/pr_5380.prdoc diff --git a/prdoc/pr_5384.prdoc b/prdoc/1.16.0/pr_5384.prdoc similarity index 100% rename from prdoc/pr_5384.prdoc rename to prdoc/1.16.0/pr_5384.prdoc diff --git a/prdoc/pr_5392.prdoc b/prdoc/1.16.0/pr_5392.prdoc similarity index 100% rename from prdoc/pr_5392.prdoc rename to prdoc/1.16.0/pr_5392.prdoc diff --git a/prdoc/pr_5393.prdoc b/prdoc/1.16.0/pr_5393.prdoc similarity index 100% rename from prdoc/pr_5393.prdoc rename to prdoc/1.16.0/pr_5393.prdoc diff --git a/prdoc/pr_5396.prdoc b/prdoc/1.16.0/pr_5396.prdoc similarity index 100% rename from prdoc/pr_5396.prdoc rename to prdoc/1.16.0/pr_5396.prdoc diff --git a/prdoc/pr_5407.prdoc b/prdoc/1.16.0/pr_5407.prdoc similarity index 100% rename from prdoc/pr_5407.prdoc rename to prdoc/1.16.0/pr_5407.prdoc diff --git a/prdoc/pr_5410.prdoc b/prdoc/1.16.0/pr_5410.prdoc similarity index 100% rename from prdoc/pr_5410.prdoc rename to prdoc/1.16.0/pr_5410.prdoc diff --git a/prdoc/pr_5411.prdoc b/prdoc/1.16.0/pr_5411.prdoc similarity index 100% rename from prdoc/pr_5411.prdoc rename to prdoc/1.16.0/pr_5411.prdoc diff --git a/prdoc/pr_5424.prdoc b/prdoc/1.16.0/pr_5424.prdoc similarity index 100% rename from prdoc/pr_5424.prdoc rename to prdoc/1.16.0/pr_5424.prdoc diff --git a/prdoc/pr_5430.prdoc b/prdoc/1.16.0/pr_5430.prdoc similarity index 100% rename from prdoc/pr_5430.prdoc rename to prdoc/1.16.0/pr_5430.prdoc diff --git a/prdoc/pr_5431.prdoc b/prdoc/1.16.0/pr_5431.prdoc similarity index 100% rename from prdoc/pr_5431.prdoc rename to prdoc/1.16.0/pr_5431.prdoc diff --git a/prdoc/pr_5436.prdoc b/prdoc/1.16.0/pr_5436.prdoc similarity index 100% rename from prdoc/pr_5436.prdoc rename to prdoc/1.16.0/pr_5436.prdoc diff --git a/prdoc/pr_5439.prdoc b/prdoc/1.16.0/pr_5439.prdoc similarity index 100% rename from prdoc/pr_5439.prdoc rename to prdoc/1.16.0/pr_5439.prdoc diff --git a/prdoc/pr_5442.prdoc b/prdoc/1.16.0/pr_5442.prdoc similarity index 100% rename from prdoc/pr_5442.prdoc rename to prdoc/1.16.0/pr_5442.prdoc diff --git a/prdoc/pr_5443.prdoc b/prdoc/1.16.0/pr_5443.prdoc similarity index 100% rename from prdoc/pr_5443.prdoc rename to prdoc/1.16.0/pr_5443.prdoc diff --git a/prdoc/pr_5450.prdoc b/prdoc/1.16.0/pr_5450.prdoc similarity index 100% rename from prdoc/pr_5450.prdoc rename to prdoc/1.16.0/pr_5450.prdoc diff --git a/prdoc/pr_5465.prdoc b/prdoc/1.16.0/pr_5465.prdoc similarity index 100% rename from prdoc/pr_5465.prdoc rename to prdoc/1.16.0/pr_5465.prdoc diff --git a/prdoc/pr_5466.prdoc b/prdoc/1.16.0/pr_5466.prdoc similarity index 100% rename from prdoc/pr_5466.prdoc rename to prdoc/1.16.0/pr_5466.prdoc diff --git a/prdoc/pr_5467.prdoc b/prdoc/1.16.0/pr_5467.prdoc similarity index 100% rename from prdoc/pr_5467.prdoc rename to prdoc/1.16.0/pr_5467.prdoc diff --git a/prdoc/1.16.0/pr_5509.prdoc b/prdoc/1.16.0/pr_5509.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..154146034e6fd8dad28c3d0cb299154886bd6449 --- /dev/null +++ b/prdoc/1.16.0/pr_5509.prdoc @@ -0,0 +1,17 @@ +title: Add `pallet_proxy` to People Chain and Coretime Chain testnet runtimes. + +doc: + - audience: Runtime User + description: | + Proxies can now be used on `coretime-rococo`, `coretime-westend`, `people-rococo` and + `people-westend` in the same way as they can be on Kusama and Polkadot chains. + +crates: + - name: coretime-rococo-runtime + bump: major + - name: coretime-westend-runtime + bump: major + - name: people-rococo-runtime + bump: major + - name: people-westend-runtime + bump: major diff --git a/prdoc/pr_5513.prdoc b/prdoc/1.16.0/pr_5513.prdoc similarity index 100% rename from prdoc/pr_5513.prdoc rename to prdoc/1.16.0/pr_5513.prdoc diff --git a/prdoc/1.16.0/pr_5527.prdoc b/prdoc/1.16.0/pr_5527.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..38eb75affe44730319ed2d5b4f68292f59790639 --- /dev/null +++ b/prdoc/1.16.0/pr_5527.prdoc @@ -0,0 +1,17 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Report BestBlock events only for newBlock reports + +doc: + - audience: Node Dev + description: | + This PR ensures that the chainHead_v1_follow method of the RPC-v2 API is always + reporting a `BestBlock` event after a `NewBlock`. + There was a race condition in the chainHead follow logic which led to the `BestBlock` + event to be emitted without an associated `NewBlock` event. + +crates: + - name: sc-rpc-spec-v2 + bump: minor + diff --git a/prdoc/1.16.0/pr_5538.prdoc b/prdoc/1.16.0/pr_5538.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..5924f97890403fe2003912363241a1fa0ff5c7a3 --- /dev/null +++ b/prdoc/1.16.0/pr_5538.prdoc @@ -0,0 +1,11 @@ +title: "collator-protocol: Remove race condition" + +doc: + - audience: Node Dev + description: | + Remove a race condition in the collator protocol that could lead + to collations not being announced to a validator. + +crates: + - name: polkadot-collator-protocol + bump: patch diff --git a/prdoc/1.16.0/pr_5546.prdoc b/prdoc/1.16.0/pr_5546.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..95f02dbe13b25c5e66149a0b7bbe99bdea7f4302 --- /dev/null +++ b/prdoc/1.16.0/pr_5546.prdoc @@ -0,0 +1,36 @@ +title: "Transfer polkadot native assets to Ethereum through snowbridge" + +doc: + - audience: Runtime Dev + description: | + Transfer polkadot native asset to Ethereum through snowbridge. + +crates: + - name: snowbridge-pallet-inbound-queue + bump: patch + - name: snowbridge-pallet-inbound-queue + bump: patch + - name: snowbridge-pallet-outbound-queue + bump: patch + - name: snowbridge-pallet-system + bump: minor + validate: false + - name: snowbridge-core + bump: minor + validate: false + - name: snowbridge-router-primitives + bump: minor + validate: false + - name: bridge-hub-westend-runtime + bump: patch + - name: bridge-hub-rococo-runtime + bump: patch + - name: bridge-hub-westend-emulated-chain + bump: patch + - name: bridge-hub-westend-integration-tests + bump: minor + - name: asset-hub-westend-emulated-chain + bump: patch + - name: emulated-integration-tests-common + bump: patch + diff --git a/prdoc/1.16.0/pr_5563.prdoc b/prdoc/1.16.0/pr_5563.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..cbf436125bb5e1125dae8340963721eb314a1392 --- /dev/null +++ b/prdoc/1.16.0/pr_5563.prdoc @@ -0,0 +1,14 @@ +title: "snowbridge: improve destination fee handling to avoid trapping fees dust" + +doc: + - audience: Runtime User + description: | + On Ethereum -> Polkadot Asset Hub messages, whether they are a token transfer + or a `Transact` for registering a new token, any unspent fees are deposited to + Snowbridge's sovereign account on Asset Hub, rather than trapped in AH's asset trap. + +crates: + - name: snowbridge-router-primitives + bump: patch + - name: snowbridge-pallet-inbound-queue + bump: patch diff --git a/prdoc/1.16.0/pr_5580.prdoc b/prdoc/1.16.0/pr_5580.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..e03b946070aa25ad4bbbd448d21c89bdbf7db6fa --- /dev/null +++ b/prdoc/1.16.0/pr_5580.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Fix error message on pallet macro + +doc: + - audience: Runtime Dev + description: | + Improve error message for pallet macro generated code. + +crates: + - name: frame-support-procedural + bump: patch diff --git a/prdoc/1.16.0/pr_5581.prdoc b/prdoc/1.16.0/pr_5581.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..e33690939d8ba54554c4a72f6345246915874f51 --- /dev/null +++ b/prdoc/1.16.0/pr_5581.prdoc @@ -0,0 +1,30 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Clear other messages before dry-running + +doc: + - audience: Runtime Dev + description: | + The DryRunApi.dry_run_call and DryRunApi.dry_run_xcm functions used to populate + `forwarded_xcms` with all the existing messages in the queues at the time. + Now, existing (irrelevant) messages are cleared when dry-running, meaning only the + messages produced by the dry-run call (or xcm) will be returned in `forwarded_xcms`. + +crates: + - name: pallet-xcm + bump: minor + - name: staging-xcm-builder + bump: major + - name: pallet-xcm-bridge-hub-router + bump: minor + - name: cumulus-pallet-parachain-system + bump: minor + - name: cumulus-pallet-xcmp-queue + bump: minor + - name: cumulus-primitives-utility + bump: minor + - name: polkadot-runtime-common + bump: minor + - name: pallet-xcm-bridge-hub + bump: minor diff --git a/prdoc/1.16.0/pr_5594.prdoc b/prdoc/1.16.0/pr_5594.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..dbdc7937b73d33be17c7cf6acf6fd1258a5a8ba2 --- /dev/null +++ b/prdoc/1.16.0/pr_5594.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "Add debugging info for `StorageWeightReclaim`" + +doc: + - audience: Runtime Dev + description: | + - Includes extrinsic index to be displayed in the logs when the consumed weight is higher than the measured one. + +crates: + - name: cumulus-primitives-storage-weight-reclaim + bump: patch diff --git a/prdoc/1.16.0/pr_5632.prdoc b/prdoc/1.16.0/pr_5632.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..f76428bbc8f64db05e2fe2b9df2786a8afebf72b --- /dev/null +++ b/prdoc/1.16.0/pr_5632.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Fix `alloc` not found error in `format_runtime_string!` + +doc: + - audience: Runtime Dev + description: | + Fixes the macro hygiene in the `format_runtime_string!` macro to fix the `alloc` not found build error. + +crates: + - name: sp-runtime + bump: patch diff --git a/prdoc/1.16.0/pr_5644.prdoc b/prdoc/1.16.0/pr_5644.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..3300d557fce40f274120fb76b449e675eccb2a2f --- /dev/null +++ b/prdoc/1.16.0/pr_5644.prdoc @@ -0,0 +1,8 @@ +title: 'pallet-utility: Improve weight annotations' +doc: +- audience: Runtime Dev + description: |- + Prevent allocations when calculating the weights. +crates: +- name: pallet-utility + bump: patch diff --git a/prdoc/1.16.0/pr_5649.prdoc b/prdoc/1.16.0/pr_5649.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..1f4c97aa1753a924c87050d8d08a904042f66ac5 --- /dev/null +++ b/prdoc/1.16.0/pr_5649.prdoc @@ -0,0 +1,49 @@ +title: "Bridges lane id agnostic for backwards compatibility" + +doc: +- audience: Runtime Dev + description: | + This PR improves support for handling `LaneId` backwards compatibility with the previously merged [PR](https://github.com/paritytech/polkadot-sdk/pull/4949). + If `pallet_bridge_messages` or `pallet_bridge_relayers` used `LaneId([u8; 4])` previously, they should now set `type LaneId = LegacyLaneId;`. + +crates: +- name: bridge-runtime-common + bump: patch +- name: bp-runtime + bump: patch +- name: staging-xcm-executor + bump: none +- name: parachains-runtimes-test-utils + bump: patch +- name: bp-messages + bump: major +- name: bp-relayers + bump: major +- name: bp-xcm-bridge-hub + bump: major +- name: pallet-bridge-messages + bump: patch +- name: pallet-bridge-relayers + bump: patch +- name: pallet-xcm-bridge-hub + bump: major +- name: emulated-integration-tests-common + bump: patch +- name: bp-bridge-hub-kusama + bump: patch +- name: bp-bridge-hub-polkadot + bump: patch +- name: bp-bridge-hub-rococo + bump: patch +- name: bp-bridge-hub-westend + bump: patch +- name: bp-polkadot-bulletin + bump: patch +- name: bridge-hub-rococo-runtime + bump: major +- name: bridge-hub-westend-runtime + bump: patch +- name: polkadot-parachain-bin + bump: none +- name: bridge-hub-test-utils + bump: major diff --git a/prdoc/1.16.0/pr_5655.prdoc b/prdoc/1.16.0/pr_5655.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..bfa2e295d1571ebfb11bb1a917e62242efc1ad2f --- /dev/null +++ b/prdoc/1.16.0/pr_5655.prdoc @@ -0,0 +1,15 @@ +title: '[benchmarking] Reset to genesis storage after each run' +doc: +- audience: Runtime Dev + description: |- + The genesis state is currently partially provided via `OverlayedChanges`, but these changes are reset by the runtime after the first repetition, causing the second repitition to use an invalid genesis state. + + Changes: + - Provide the genesis state as a `Storage` without any `OverlayedChanges` to make it work correctly with repetitions. + - Add `--genesis-builder-preset` option to use different genesis preset names. + - Improve error messages. +crates: +- name: frame-benchmarking-cli + bump: major +- name: frame-benchmarking-pallet-pov + bump: patch diff --git a/prdoc/1.16.0/pr_5660.prdoc b/prdoc/1.16.0/pr_5660.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..fce791cebb65a5a11c37a85587b4afc9174a2cb8 --- /dev/null +++ b/prdoc/1.16.0/pr_5660.prdoc @@ -0,0 +1,30 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "xcm-executor: validate destinations for ReserveWithdraw and Teleport transfers" + +doc: + - audience: + - Runtime User + - Runtime Dev + description: | + This change adds the required validation for stronger UX guarantees when using + `InitiateReserveWithdraw` or `InitiateTeleport` XCM instructions. Execution of + the instructions will fail if the local chain is not configured to trust the + "destination" or "reserve" chain as a reserve/trusted-teleporter for the provided + "assets". + With this change, misuse of `InitiateReserveWithdraw`/`InitiateTeleport` fails on + origin with no overall side-effects, rather than failing on destination (with + side-effects to origin's assets issuance). + The commit also makes the same validations for pallet-xcm transfers, and adds + regression tests. + +crates: + - name: staging-xcm-executor + bump: patch + - name: staging-xcm-builder + bump: patch + - name: pallet-xcm + bump: patch + - name: xcm-simulator-example + bump: patch diff --git a/prdoc/1.16.0/pr_5671.prdoc b/prdoc/1.16.0/pr_5671.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..364165ec820edf980b6a49e0a1ff5246e98385ed --- /dev/null +++ b/prdoc/1.16.0/pr_5671.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: Snowbridge free consensus updates border condition fix + +doc: + - audience: Runtime Dev + description: | + A fix for a border condition introduced with the Ethereum client free consensus updates. A malicious relayer could + spam the Ethereum client with sync committee updates that have already been imported for the period. This PR adds + a storage item to track the last imported sync committee period, so that subsequent irrelevant updates are not free. + No impact for users or relayers, since the feature introducing the border condition has not been released. + +crates: + - name: snowbridge-pallet-ethereum-client + bump: patch diff --git a/prdoc/1.16.0/pr_5678.prdoc b/prdoc/1.16.0/pr_5678.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..ebb5e5a0d79facf64f35008a3a48f65cdce56d20 --- /dev/null +++ b/prdoc/1.16.0/pr_5678.prdoc @@ -0,0 +1,14 @@ +title: 'rpc server: fix deny unsafe on RpcMethods::Auto' +doc: +- audience: Node Operator + description: |- + Close #5677 + + I made a nit when I moved this code: https://github.com/paritytech/polkadot-sdk/blob/v1.14.0-rc1/substrate/client/service/src/lib.rs#L379-#L385 in https://github.com/paritytech/polkadot-sdk/pull/4792 + + Thus: + - (ip.is_loopback(), RpcMethods::Auto) -> allow unsafe + - (!ip.is_loopback(), RpcMethods::Auto) -> deny unsafe +crates: +- name: sc-rpc-server + bump: patch diff --git a/prdoc/1.16.0/pr_5688.prdoc b/prdoc/1.16.0/pr_5688.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..88e712fcba68ca2b1f7c2d63cfe6a4e951f3a987 --- /dev/null +++ b/prdoc/1.16.0/pr_5688.prdoc @@ -0,0 +1,10 @@ +title: "Fix `paras_inherent` benchmark" + +doc: + - audience: Runtime Dev + description: | + Fixes the benchmark for relay chains like rococo-dev with `max_validators_per_core` value lower than 2. + +crates: +- name: polkadot-runtime-parachains + bump: patch diff --git a/prdoc/1.16.0/pr_5695.prdoc b/prdoc/1.16.0/pr_5695.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..202eafa26ebd8db98e3a759530b581b652cca18a --- /dev/null +++ b/prdoc/1.16.0/pr_5695.prdoc @@ -0,0 +1,15 @@ +title: 'pallet-migrations: fix index access for singluar migrations' +doc: +- audience: Runtime Dev + description: |- + Discovered a bug in the migrations pallet while debugging https://github.com/paritytech/try-runtime-cli/pull/90. + It only occurs when a single MBM is configured - hence it did not happen when Ajuna Network tried it... + + Changes: + - Check len of the tuple before accessing its nth_id + - Make nth_id return `None` on unary tuples and n>0 +crates: +- name: pallet-migrations + bump: patch +- name: frame-support + bump: patch diff --git a/prdoc/1.16.0/pr_5712.prdoc b/prdoc/1.16.0/pr_5712.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..321ed12f3135e6ad7f4055b00a8dba7e26bd26c9 --- /dev/null +++ b/prdoc/1.16.0/pr_5712.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: Better logs for XCM emulator + +doc: + - audience: Runtime Dev + description: | + Now the XCM emulator has a log every time `execute_with` is called, to know + which chain is being used. + Also, the logs for UMP, DMP, HRMP processing were included in the `xcm` log filter + and changed from showing the message as an array of bytes to a hex string. + This means running the tests with `RUST_LOG=xcm` should give you everything you need, + you can always filter by `RUST_LOG=xcm::hrmp` or any other if you need it. + +crates: + - name: xcm-emulator + bump: patch diff --git a/prdoc/1.16.0/pr_5713.prdoc b/prdoc/1.16.0/pr_5713.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..54d3619cdcaff17dbd8795ad9ab05491ce72c99f --- /dev/null +++ b/prdoc/1.16.0/pr_5713.prdoc @@ -0,0 +1,10 @@ +title: "pallet-treasury: Improve `remove_approval` benchmark" + +doc: + - audience: Runtime Dev + description: | + Fix the `remove_approval` benchmark when `SpendOrigin` doesn't return any `succesful_origin`. + +crates: + - name: pallet-treasury + bump: patch diff --git a/prdoc/1.16.0/pr_5747.prdoc b/prdoc/1.16.0/pr_5747.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..ee786db658c8634e0a2b6abf89edddc16d6e20b7 --- /dev/null +++ b/prdoc/1.16.0/pr_5747.prdoc @@ -0,0 +1,13 @@ +title: "Snowbridge runtime migration on Westend" + +doc: + - audience: Runtime Dev + description: | + This is a backport for https://github.com/paritytech/polkadot-sdk/pull/5074 which missed + the runtime migration to initialize channels of the bridge. + +crates: + - name: bridge-hub-westend-runtime + bump: patch + + diff --git a/prdoc/1.9.0/pr_1378.prdoc b/prdoc/1.9.0/pr_1378.prdoc index 6533dcb663030718fdcd3ffc353b2ae8b2a92407..03427cdba99d9a79d2feec7e28f7ec391ad63c4e 100644 --- a/prdoc/1.9.0/pr_1378.prdoc +++ b/prdoc/1.9.0/pr_1378.prdoc @@ -10,7 +10,7 @@ doc: 3. `#[runtime::pallet_index]` must be attached to a pallet to define its index 4. `#[runtime::disable_call]` can be optionally attached to a pallet to disable its calls 5. `#[runtime::disable_unsigned]` can be optionally attached to a pallet to disable unsigned calls - 6. A pallet instance can be defined as `TemplateModule: pallet_template` + 6. A pallet instance can be defined as `Template: pallet_template` An optional attribute can be defined as `#[frame_support::runtime(legacy_ordering)]` to ensure that the order of hooks is same as the order of pallets (and not based on the pallet_index). This is to support legacy runtimes and should be avoided for new ones. diff --git a/prdoc/pr_3151.prdoc b/prdoc/pr_3151.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..5e43b86a97533c53cb18404e4023751b27d2f556 --- /dev/null +++ b/prdoc/pr_3151.prdoc @@ -0,0 +1,49 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Dynamic deposit based on number of proposals + +doc: + - audience: + - Runtime User + - Runtime Dev + description: | + Introduce a dynamic proposal deposit mechanism influenced by the total number of active + proposals, with the option to set the deposit to none. + + The potential cost (e.g., balance hold) for proposal submission and storage is determined + by the implementation of the `Consideration` trait. The footprint is defined as `proposal_count`, + representing the total number of active proposals in the system, excluding the one currently + being proposed. This cost may vary based on the proposal count. The pallet also offers various + types to define a cost strategy based on the number of proposals. + + Two new calls are introduced: + - kill(origin, proposal_hash): the cancellation of a proposal, accompanied by the burning + of the associated cost/consideration ticket. + - release_proposal_cost(origin, proposal_hash): the release of the cost for a non-active proposal. + + New config parameters: + - DisapproveOrigin: origin from which a proposal in any status may be disapproved without + associated cost for a proposer; + - KillOrigin: Origin from which any malicious proposal may be killed with associated cost + for a proposer; + - Consideration: mechanism to assess the necessity of some cost for publishing and storing + a proposal. Set to unit type to have not submission cost; + + Additionally change: + - benchmarks have been upgraded to benchmarks::v2 for collective pallet; + - `ensure_successful` function added to the `Consideration` under `runtime-benchmarks` feature. + +crates: + - name: pallet-collective + bump: major + - name: collectives-westend-runtime + bump: major + - name: kitchensink-runtime + bump: major + - name: pallet-alliance + bump: patch + - name: pallet-balances + bump: patch + - name: pallet-utility + bump: patch diff --git a/prdoc/pr_3881.prdoc b/prdoc/pr_3881.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..4cf6425e73a562d0a1482bba45c7d2d0d18d95ea --- /dev/null +++ b/prdoc/pr_3881.prdoc @@ -0,0 +1,15 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Introduce a Generic Proving Trie + +doc: + - audience: Runtime Dev + description: | + This PR introduces a Proving Trie object which can be used inside the runtime. This can allow + for things like airdrops where a single hash is stored on chain representing the whole airdrop + and individuals present a proof of their inclusion in the airdrop. + +crates: + - name: sp-runtime + bump: major diff --git a/prdoc/pr_4251.prdoc b/prdoc/pr_4251.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..4d4fcd73469232939c8f25e8861847d5f61bbb1b --- /dev/null +++ b/prdoc/pr_4251.prdoc @@ -0,0 +1,79 @@ +title: MBM `try-runtime` support +doc: +- audience: Runtime Dev + description: | + # MBM try-runtime support + + This MR adds support to the try-runtime + trait such that the try-runtime-CLI will be able to support MBM testing [here](https://github.com/paritytech/try-runtime-cli/pull/90). + It mainly adds two feature-gated hooks to the `SteppedMigration` hook to facilitate + testing. These hooks are named `pre_upgrade` and `post_upgrade` and have the + same signature and implications as for single-block migrations. + + ## Integration + + To make use of this in your Multi-Block-Migration, just implement the two new hooks and test pre- and post-conditions in them: + + ```rust + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, frame_support::sp_runtime::TryRuntimeError> + { + // ... + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(prev: Vec) -> Result<(), frame_support::sp_runtime::TryRuntimeError> { + // ... + } + ``` + + You may return an error or panic in these functions to indicate failure. + This will then show up in the try-runtime-CLI and can be used in CI for testing. + + + Changes: + - Adds `try-runtime` gated methods `pre_upgrade` and `post_upgrade` + on `SteppedMigration` + - Adds `try-runtime` gated methods `nth_pre_upgrade` + and `nth_post_upgrade` on `SteppedMigrations` + - Modifies `pallet_migrations` + implementation to run pre_upgrade and post_upgrade steps at the appropriate times, and panic in the event of migration failure. +crates: +- name: asset-hub-rococo-runtime + bump: minor +- name: asset-hub-westend-runtime + bump: minor +- name: bridge-hub-rococo-runtime + bump: minor +- name: bridge-hub-westend-runtime + bump: minor +- name: collectives-westend-runtime + bump: minor +- name: contracts-rococo-runtime + bump: minor +- name: coretime-rococo-runtime + bump: minor +- name: coretime-westend-runtime + bump: minor +- name: people-rococo-runtime + bump: minor +- name: people-westend-runtime + bump: minor +- name: penpal-runtime + bump: minor +- name: polkadot-parachain-bin + bump: minor +- name: rococo-runtime + bump: minor +- name: westend-runtime + bump: minor +- name: frame-executive + bump: minor +- name: pallet-migrations + bump: minor +- name: frame-support + bump: minor +- name: frame-system + bump: minor +- name: frame-try-runtime + bump: minor diff --git a/prdoc/pr_4257.prdoc b/prdoc/pr_4257.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..860b85a4888e5039aa4785fc5f18c4538dbd21b1 --- /dev/null +++ b/prdoc/pr_4257.prdoc @@ -0,0 +1,76 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Rename `state_version` in `RuntimeVersion` to `system_version`. + +doc: + - audience: Runtime Dev + description: | + This PR renames `state_version` in `RuntimeVersion` to `system_version`. `system_version=2` signifies + that extrinsic root derivation uses `StateVersion::V1`. + + - audience: Runtime User + description: | + `RuntimeVersion`'s `state_version` is renamed to `system_version`. Applications using that type and its field + must update their code to reflect the changes. For easier migration serde serialization produces both new + `systemVersion` and old `stateVersion` fields and deserialization supports `stateVersion` as an alias as too. + +crates: + - name: frame-system + bump: major + - name: sp-api + bump: none + - name: sp-version + bump: major + - name: sp-storage + bump: minor + - name: sp-version-proc-macro + bump: minor + - name: sc-block-builder + bump: major + - name: sc-executor + bump: major + - name: sc-rpc + bump: none + - name: sc-rpc-spec-v2 + bump: none + - name: cumulus-pallet-parachain-system + bump: none + - name: cumulus-client-pov-recovery + bump: none + - name: cumulus-client-network + bump: none + - name: rococo-runtime + bump: major + - name: westend-runtime + bump: major + - name: asset-hub-rococo-runtime + bump: major + - name: asset-hub-westend-runtime + bump: major + - name: bridge-hub-rococo-runtime + bump: major + - name: bridge-hub-westend-runtime + bump: major + - name: collectives-westend-runtime + bump: major + - name: coretime-rococo-runtime + bump: major + - name: coretime-westend-runtime + bump: major + - name: people-rococo-runtime + bump: major + - name: people-westend-runtime + bump: major + - name: penpal-runtime + bump: major + - name: contracts-rococo-runtime + bump: major + - name: glutton-westend-runtime + bump: major + - name: seedling-runtime + bump: major + - name: shell-runtime + bump: major + - name: rococo-parachain-runtime + bump: major diff --git a/prdoc/pr_4837.prdoc b/prdoc/pr_4837.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..55c12cc92a1c10206f37ec3e94a5d955b52976d4 --- /dev/null +++ b/prdoc/pr_4837.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: Add PVF execution priority + +doc: + - audience: Node Dev + description: | + The new logic optimizes the distribution of execution jobs for disputes, approvals, and backings. + The main goal is to create back pressure for backing in the presence of disputes or numerous approval jobs. + +crates: + - name: polkadot-node-core-pvf + bump: major + - name: polkadot-overseer + bump: patch + - name: polkadot-node-subsystem-types + bump: patch + - name: polkadot-node-core-approval-voting + bump: patch + - name: polkadot-node-core-backing + bump: patch + - name: polkadot-node-core-candidate-validation + bump: patch + - name: polkadot-node-core-dispute-coordinator + bump: patch diff --git a/prdoc/pr_4846.prdoc b/prdoc/pr_4846.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..eb18301b1010e7ce8988808dfcc04fe84550f1e6 --- /dev/null +++ b/prdoc/pr_4846.prdoc @@ -0,0 +1,13 @@ +title: "Make approval-voting runnable on a worker thread" + +doc: + - audience: Node Dev + description: | + Make approval-voting subsystem runnable on a separate worker thread without having to + to always pass to it an orchestra context. It achieves that by refactoring existing functions + to require only the minimal set of traits needed in the function instead of the general + `Context` + +crates: + - name: polkadot-node-core-approval-voting + bump: major diff --git a/prdoc/pr_4849.prdoc b/prdoc/pr_4849.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..1852951510680423906321ceadac35e8b5749032 --- /dev/null +++ b/prdoc/pr_4849.prdoc @@ -0,0 +1,47 @@ +title: Introduce approval-voting-parallel subsystem + +doc: + - audience: Node Dev + description: | + This introduces a new subsystem called approval-voting-parallel. It combines the tasks + previously handled by the approval-voting and approval-distribution subsystems. + + The new subsystem is enabled by default on all test networks. On production networks + like Polkadot and Kusama, the legacy system with two separate subsystems is still in use. + However, there is a CLI option --enable-approval-voting-parallel to gradually roll out + the new subsystem on specific nodes. Once we are confident that it works as expected, + it will be enabled by default on all networks. + + The approval-voting-parallel subsystem coordinates two groups of workers: + - Four approval-distribution workers that operate in parallel, each handling tasks based + on the validator_index of the message originator. + - One approval-voting worker that performs the tasks previously managed by the standalone + approval-voting subsystem. + +crates: + - name: polkadot-overseer + bump: major + - name: polkadot-node-primitives + bump: major + - name: polkadot-node-subsystem-types + bump: major + - name: polkadot-service + bump: major + - name: polkadot-approval-distribution + bump: major + - name: polkadot-node-core-approval-voting + bump: major + - name: polkadot-node-core-approval-voting-parallel + bump: major + - name: polkadot-network-bridge + bump: major + - name: polkadot-node-core-dispute-coordinator + bump: major + - name: cumulus-relay-chain-inprocess-interface + bump: major + - name: polkadot-cli + bump: major + - name: polkadot + bump: major + - name: polkadot-sdk + bump: minor diff --git a/prdoc/pr_4851.prdoc b/prdoc/pr_4851.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..2110a68d401ca0332b679a266cc6fce102b8d675 --- /dev/null +++ b/prdoc/pr_4851.prdoc @@ -0,0 +1,40 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Add support for deprecation metadata in `RuntimeMetadataIr` entries. + +doc: + - audience: + - Runtime Dev + - Runtime User + description: | + Changes introduced are listed below. + Adds `DeprecationStatusIR` enum to sp_metadata_ir. + - Is a deprecation info for simple items. + Adds `DeprecationInfoIR` enum to sp_metadata_ir. + - It is a deprecation info for an enums/errors/calls. Contains `DeprecationStatusIR`. + Also denotes full/partial deprecation of the type or its variants/calls. + Adds `deprecation_info` field to + - `RuntimeApiMetadataIR` + - `RuntimeApiMethodMetadataIR` + - `StorageEntryMetadataIR` + - `PalletConstantMetadataIR` + - `PalletCallMetadataIR` + - `PalletMetadataIR` + - `PalletEventMetadataIR` + - `PalletErrorMetadataIR` + Examples of the deprecation info produced can be seen inside + - Tests for `frame-support` + - hackmd link https://hackmd.io/@Zett98/Bys0YgbcR + +crates: + - name: frame-support-procedural + bump: patch + - name: frame-support + bump: major + - name: sp-api-proc-macro + bump: patch + - name: sp-api + bump: patch + - name: sp-metadata-ir + bump: major diff --git a/prdoc/pr_4889.prdoc b/prdoc/pr_4889.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..dfcdcd4f15bcd875d664dc74273695eeeef95d08 --- /dev/null +++ b/prdoc/pr_4889.prdoc @@ -0,0 +1,15 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Add CLI options for parachain chain specifications + fix bug for swallowing custom fields + +doc: + - audience: Node Operator + description: | + Parachain ID and relay chain can be specified via the CLI arguments for when creating a chain spec. + A bug that also swallowed custom fields outside of the default config has also been fixed. + + +crates: + - name: staging-chain-spec-builder + bump: major diff --git a/prdoc/pr_4974.prdoc b/prdoc/pr_4974.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..f764ea3f46fd762f1c7b89689d680163539f79c1 --- /dev/null +++ b/prdoc/pr_4974.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: "Remove libp2p dependency from sc-network-sync" + +doc: + - audience: Node Dev + description: | + This PR removes `libp2p::request_response::OutboundFailure` from `substrate/client/network/sync/src/engine.rs`. + +crates: + - name: sc-network + bump: patch + - name: sc-network-sync + bump: patch diff --git a/prdoc/pr_5038.prdoc b/prdoc/pr_5038.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..2bab8ef69f8999e34ed35f2ff43fa57d6c739530 --- /dev/null +++ b/prdoc/pr_5038.prdoc @@ -0,0 +1,15 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Plumb RPC listener up to caller + +doc: + - audience: Node Dev + description: + This PR allows the RPC server's socket address to be returned when initializing the server. + This allows the library consumer to easily programmatically determine which port the RPC server is listening on. +crates: + - name: sc-rpc-server + bump: major + - name: sc-service + bump: major diff --git a/prdoc/pr_5198.prdoc b/prdoc/pr_5198.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..417b0b5a4fd9efd338d37f95545accaba24f2ce0 --- /dev/null +++ b/prdoc/pr_5198.prdoc @@ -0,0 +1,13 @@ +title: "MQ processor should be transactional" + +doc: + - audience: [Runtime User, Runtime Dev] + description: | + Enforce transactional processing on pallet Message Queue Processor. + + Storage changes that were done while processing a message will now be rolled back + when the processing returns an error. `Ok(false)` will not revert, only `Err(_)`. + +crates: + - name: pallet-message-queue + bump: major \ No newline at end of file diff --git a/prdoc/pr_5201.prdoc b/prdoc/pr_5201.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..a0c1bbfd2e413784bdddab0fcd27e1ad586ff8d9 --- /dev/null +++ b/prdoc/pr_5201.prdoc @@ -0,0 +1,23 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Snowbridge free consensus updates + +doc: + - audience: Runtime Dev + description: | + Allow free consensus updates to the Snowbridge Ethereum client if the headers are more than a certain + number of headers apart. Relayers providing valid consensus updates are refunded for updates. Bridge + users are not affected. + +crates: + - name: snowbridge-pallet-ethereum-client + bump: patch + - name: snowbridge-pallet-inbound-queue + bump: patch + - name: snowbridge-runtime-test-common + bump: patch + - name: bridge-hub-rococo-runtime + bump: major + - name: bridge-hub-westend-runtime + bump: major diff --git a/prdoc/pr_5322.prdoc b/prdoc/pr_5322.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..b4cf261f33a4d7cabea2b5e22529451aa79ca856 --- /dev/null +++ b/prdoc/pr_5322.prdoc @@ -0,0 +1,30 @@ +title: Elastic scaling - introduce new candidate receipt primitive + +doc: + - audience: [Runtime Dev, Node Dev] + description: | + Introduces `CandidateDescriptorV2` primitive as described in [RFC 103](https://github.com/polkadot-fellows/RFCs/pull/103). + Updates parachains runtime, Westend, Rococo and test runtimes to use the new primitives. + This change does not implement the functionality of the new candidate receipts. + +crates: +- name: polkadot-primitives + bump: minor +- name: polkadot-primitives-test-helpers + bump: minor +- name: polkadot-runtime-parachains + bump: major +- name: rococo-runtime + bump: major +- name: westend-runtime + bump: major +- name: polkadot-test-runtime + bump: major +- name: polkadot-service + bump: patch +- name: polkadot-node-subsystem-types + bump: patch +- name: polkadot-test-client + bump: major +- name: cumulus-relay-chain-inprocess-interface + bump: patch diff --git a/prdoc/pr_5343.prdoc b/prdoc/pr_5343.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..3cec70de93cba97e93ef7efcfb1ccc712bda2ab0 --- /dev/null +++ b/prdoc/pr_5343.prdoc @@ -0,0 +1,19 @@ +title: Allow to disable gap creation during block import + +doc: + - audience: Node Dev + description: | + New property `BlockImportParams::create_gap` allows to change whether to create block gap in case block + has no parent (defaults to `true` keeping existing behavior), which is helpful for sync protocols that do not need + to sync the gap after this happens. `BlockImportOperation::create_gap()` method was also introduced, though in + most cases `BlockImportParams::create_gap` will be used. + +crates: + - name: sc-client-api + bump: major + - name: sc-consensus + bump: minor + - name: sc-client-db + bump: minor + - name: sc-service + bump: minor diff --git a/prdoc/pr_5372.prdoc b/prdoc/pr_5372.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..fec856b3c0d6a86a79f8118fef3c7659c82819ae --- /dev/null +++ b/prdoc/pr_5372.prdoc @@ -0,0 +1,71 @@ +title: "elastic scaling: add core selector to cumulus" + +doc: + - audience: [Node Dev, Runtime Dev] + description: | + Adds a runtime API for querying the core selector of a parachain. + Also use the core selector API and the claim queue relay chain runtime API in the slot based collator (if available) + to determine which cores to build on. + Part of implementing https://github.com/polkadot-fellows/RFCs/pull/103. + +crates: + - name: cumulus-client-consensus-aura + bump: major + - name: cumulus-relay-chain-inprocess-interface + bump: patch + - name: cumulus-relay-chain-interface + bump: major + validate: false + - name: cumulus-relay-chain-minimal-node + bump: none + - name: cumulus-relay-chain-rpc-interface + bump: patch + - name: cumulus-pallet-parachain-system + bump: major + validate: false + - name: asset-hub-rococo-runtime + bump: patch + - name: asset-hub-westend-runtime + bump: patch + - name: bridge-hub-rococo-runtime + bump: patch + - name: bridge-hub-westend-runtime + bump: patch + - name: collectives-westend-runtime + bump: patch + - name: contracts-rococo-runtime + bump: patch + - name: coretime-rococo-runtime + bump: patch + - name: coretime-westend-runtime + bump: patch + - name: glutton-westend-runtime + bump: patch + - name: people-rococo-runtime + bump: patch + - name: people-westend-runtime + bump: patch + - name: seedling-runtime + bump: patch + - name: shell-runtime + bump: patch + - name: penpal-runtime + bump: patch + - name: rococo-parachain-runtime + bump: patch + - name: polkadot-parachain-lib + bump: major + validate: false + - name: cumulus-primitives-core + bump: minor + validate: false + - name: cumulus-test-runtime + bump: minor + - name: cumulus-client-consensus-common + bump: none + - name: cumulus-client-pov-recovery + bump: none + - name: cumulus-client-network + bump: none + - name: cumulus-pallet-xcmp-queue + bump: none diff --git a/prdoc/pr_5423.prdoc b/prdoc/pr_5423.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..dbd685d73dc3d849e61e3b655f021d8d880bd4af --- /dev/null +++ b/prdoc/pr_5423.prdoc @@ -0,0 +1,20 @@ +title: Runtime support for candidate receipt v2 (RFC103) + +doc: + - audience: [Runtime Dev, Node Dev] + description: | + Implementation of [RFC103](https://github.com/polkadot-fellows/RFCs/pull/103) in the relay chain runtime. + The runtime will accept and validate the new receipts only if the `FeatureIndex::CandidateReceiptV2` + feature bit is enabled. + +crates: + - name: polkadot-primitives + bump: major + - name: polkadot-runtime-parachains + bump: patch + - name: rococo-runtime + bump: patch + - name: westend-runtime + bump: patch + - name: polkadot + bump: patch diff --git a/prdoc/pr_5469.prdoc b/prdoc/pr_5469.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..1e6aa3c0c0726f9af6da31c1d3f89c66d917edd8 --- /dev/null +++ b/prdoc/pr_5469.prdoc @@ -0,0 +1,11 @@ +title: Syncing strategy refactoring + +doc: + - audience: Node Dev + description: | + Mostly internal changes to syncing strategies that is a step towards making them configurable/extensible in the + future. It is unlikely that external developers will need to change their code. + +crates: + - name: sc-network-sync + bump: major diff --git a/prdoc/pr_5515.prdoc b/prdoc/pr_5515.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..60f43b922c7ff36391133f073f9edd35eb8108ec --- /dev/null +++ b/prdoc/pr_5515.prdoc @@ -0,0 +1,15 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Add retry logic in relay chain rpc interface + +doc: + - audience: [ Node Dev, Node Operator ] + description: | + Added a basic retry logic for collators connecting to external RPC servers. The collator + will try for 5 times to connect to each RPC server from the provided list. In between + each iteration will wait a duration which will increase exponentailly by a factor of two. + The maximum time a collator can spend in the retry logic is 1 + 2 + 4 + 8 + 16 = 31 seconds. +crates: + - name: cumulus-relay-chain-rpc-interface + bump: minor diff --git a/prdoc/pr_5521.prdoc b/prdoc/pr_5521.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..564d9df58ceb7ff57f861706f982de2e61331a8c --- /dev/null +++ b/prdoc/pr_5521.prdoc @@ -0,0 +1,24 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Allow to call arbitrary runtime apis using RelayChainInterface + +doc: + - audience: Node Dev + description: | + This PR adds a `call_runtime_api` method to RelayChainInterface trait, and a separate function also named `call_runtime_api` + which allows the caller to specify the input and output types, as opposed to having to encode them. + +crates: + - name: cumulus-relay-chain-interface + bump: patch + - name: cumulus-client-consensus-common + bump: patch + - name: cumulus-client-pov-recovery + bump: patch + - name: cumulus-client-network + bump: patch + - name: cumulus-relay-chain-inprocess-interface + bump: patch + - name: cumulus-relay-chain-rpc-interface + bump: patch diff --git a/prdoc/pr_5526.prdoc b/prdoc/pr_5526.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..0c0a4b055f6a4ebb90f9b486ca4e276952b71504 --- /dev/null +++ b/prdoc/pr_5526.prdoc @@ -0,0 +1,13 @@ +title: "Fix enact_candidate weight generation" +doc: + - audience: Runtime Dev + description: | + This PR works around an issue in multivariate linear regression of weight generation. + +crates: + - name: polkadot-runtime-parachains + bump: patch + - name: rococo-runtime + bump: patch + - name: westend-runtime + bump: patch diff --git a/prdoc/pr_5540.prdoc b/prdoc/pr_5540.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..2c00714c4f861641b216689e3819bc155f7e9c5c --- /dev/null +++ b/prdoc/pr_5540.prdoc @@ -0,0 +1,12 @@ +title: Avoid unnecessary block gap updates + +doc: + - audience: Node Dev + description: | + Previously, the block gap storage in database and state in `BlockchainDb` could be updated even if no changes occurred. + This commit refines the logic to ensure updates only occur when the block gap value actually changes, reducing unnecessary + writes and enhancing overall efficiency. + +crates: + - name: sc-client-db + bump: none diff --git a/prdoc/pr_5548.prdoc b/prdoc/pr_5548.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..69e79213fa294864068af09692e729a89c5df281 --- /dev/null +++ b/prdoc/pr_5548.prdoc @@ -0,0 +1,16 @@ +title: Use H160 when interfacing with contracts + +doc: + - audience: Runtime Dev + description: | + When interfacing with a contract we now use the native ethereum address + type and map it to AccountId32 when interfacing with the rest + of substrate. + +crates: + - name: pallet-revive + bump: major + - name: pallet-revive-fixtures + bump: major + - name: pallet-revive-mock-network + bump: major diff --git a/prdoc/pr_5555.prdoc b/prdoc/pr_5555.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..630345b9b5a1f710489a14235f7ec2a0a179ff50 --- /dev/null +++ b/prdoc/pr_5555.prdoc @@ -0,0 +1,15 @@ +title: Make salt optional + +doc: + - audience: Runtime Dev + description: | + Remove address_len and salt_len from uapi as both are now fixed size + +crates: + - name: pallet-revive + bump: patch + - name: pallet-revive-uapi + bump: patch + - name: pallet-revive-fixtures + bump: patch + diff --git a/prdoc/pr_5556.prdoc b/prdoc/pr_5556.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..4865ec1e338d652af4c37f9f271c5f1869871212 --- /dev/null +++ b/prdoc/pr_5556.prdoc @@ -0,0 +1,11 @@ +title: Make salt optional + +doc: + - audience: Runtime Dev + description: | + By making salt optional we allow clients to use CREATE1 semantics + when deploying a new contract. + +crates: + - name: pallet-revive + bump: major diff --git a/prdoc/pr_5572.prdoc b/prdoc/pr_5572.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..c0707e4b7eba4cd18716d96be96fe6f4192070b8 --- /dev/null +++ b/prdoc/pr_5572.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: added RPC metrics for the collator + +doc: + - audience: [ Node Dev, Node Operator ] + description: | + The metric is named `relay_chain_rpc_interface` and can be scraped by prometheus agents from the parachain prometheus exporter. The metric provide information about `count`, `sum` and `duration` in seconds (with exponential buckets with parameters as start = 0.001, factor = 4, count = 9) for all RPC requests made with the `relay-chain-rpc-interface`. +crates: + - name: cumulus-relay-chain-rpc-interface + bump: major + - name: cumulus-relay-chain-minimal-node + bump: major + - name: cumulus-test-service + bump: patch + - name: substrate-prometheus-endpoint + bump: patch + - name: cumulus-client-service + bump: patch + diff --git a/prdoc/pr_5592.prdoc b/prdoc/pr_5592.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..9d51917db7b15fa4e03bf2551fe019d82451a4e4 --- /dev/null +++ b/prdoc/pr_5592.prdoc @@ -0,0 +1,26 @@ +title: Introduce `BlockGap` + +doc: + - audience: Node Dev + description: | + This is the first step towards https://github.com/paritytech/polkadot-sdk/issues/5406, + refactoring the representation of block gap. This refactor converts the existing + `(NumberFor, NumberFor)` into a dedicated `BlockGap>` + struct. This change is purely structural and does not alter existing logic, but lays + the groundwork for the follow-up PR. The compatibility concern in the database caused + by the new structure transition is addressed as well. + + The `BlockGap` refactoring results in breaking changes in the `Info` structure returned + in `client.info()`. + +crates: + - name: sc-consensus-babe + bump: none + - name: sc-client-db + bump: none + - name: sc-network-sync + bump: none + - name: sc-service + bump: none + - name: sp-blockchain + bump: major diff --git a/prdoc/pr_5601.prdoc b/prdoc/pr_5601.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..3a0ec9ee87147f0e61255cef28a97e3060d86410 --- /dev/null +++ b/prdoc/pr_5601.prdoc @@ -0,0 +1,12 @@ +title: Introduce `RpcParams` in sc-cli + +doc: + - audience: Node Dev + description: | + Refactors and consolidates all RPC-related parameters in the run command into a dedicated `RpcParams` struct. This change allows downstream users to build custom run command without duplicating code. + +crates: + - name: cumulus-client-cli + bump: none + - name: sc-cli + bump: major diff --git a/prdoc/pr_5606.prdoc b/prdoc/pr_5606.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..46883c5722cdef4cb517b5a1c124f334be79061b --- /dev/null +++ b/prdoc/pr_5606.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Fix PVF precompilation for Kusama + +doc: + - audience: Node Operator + description: | + Tweaks the PVF precompilation on Kusama to allow prepare PVFs when the node is an authority but not a validator. + +crates: + - name: polkadot-node-core-candidate-validation + bump: patch diff --git a/prdoc/pr_5608.prdoc b/prdoc/pr_5608.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..9a0748e46babd1dfd379de99ab1744257c6b13fc --- /dev/null +++ b/prdoc/pr_5608.prdoc @@ -0,0 +1,16 @@ +title: "[pallet-revive] update runtime types" + +doc: + - audience: Runtime Dev + description: | + Refactor the Ext trait to use U256 instead of BalanceOf or MomentOf + +crates: + - name: pallet-revive + bump: major + - name: pallet-revive-uapi + bump: patch + - name: pallet-revive-fixtures + bump: patch + + diff --git a/prdoc/pr_5609.prdoc b/prdoc/pr_5609.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..799071f04c1e370c0ea3f69de77226e2ee99e673 --- /dev/null +++ b/prdoc/pr_5609.prdoc @@ -0,0 +1,21 @@ +title: Update litep2p network backend to v0.7.0 + +doc: + - audience: [ Node Dev, Node Operator ] + description: | + This PR updates the Litep2p network backend to version 0.7.0. + This new release introduces several new features, improvements, and fixes to the litep2p library. + Key updates include enhanced error handling propagated through metrics, configurable connection limits, + and a new API for managing public addresses. + + The Identify protocol no longer includes public addresses in its configuration. + Instead, we rely on the `litep2p.public_addresses` interface to propagate external addresses of the node. + + Litep2p uses hickory DNS resolver (formerly known as trust DNS). + Similarly to the trust DNS, the hickory logs are silenced. + +crates: + - name: sc-network + bump: patch + - name: sc-tracing + bump: minor diff --git a/prdoc/pr_5623.prdoc b/prdoc/pr_5623.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..c0701e0e1b518f90869ae6cc04979150abcd587e --- /dev/null +++ b/prdoc/pr_5623.prdoc @@ -0,0 +1,89 @@ +# 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: Generic slashing side-effects + +doc: + - audience: Runtime Dev + description: | + What? + Make it possible for other pallets to implement their own logic when a slash on a balance occurs. + + How? + First we abstract the done_slash function of holds::Balanced to it's own trait that any pallet can implement. + Then we add a config type in pallet-balances that accepts a callback tuple of all the pallets that implement this trait. + Finally implement done_slash for pallet-balances such that it calls the config type. + Integration + The default implementation of done_slash is still an empty function, and the new config type of pallet-balances can be set to an empty tuple, so nothing changes by default. + +crates: + - name: frame-support + bump: major + + - name: pallet-balances + bump: major + + - name: pallet-broker + bump: minor + + - name: rococo-runtime + bump: minor + + - name: pallet-nis + bump: minor + + - name: westend-runtime + bump: minor + + - name: pallet-assets-freezer + bump: minor + + - name: pallet-contracts-mock-network + bump: minor + + - name: pallet-revive-mock-network + bump: minor + + - name: asset-hub-rococo-runtime + bump: minor + + - name: asset-hub-westend-runtime + bump: minor + + - name: bridge-hub-rococo-runtime + bump: minor + + - name: bridge-hub-westend-runtime + bump: minor + + - name: collectives-westend-runtime + bump: minor + + - name: coretime-rococo-runtime + bump: minor + + - name: coretime-westend-runtime + bump: minor + + - name: people-rococo-runtime + bump: minor + + - name: people-westend-runtime + bump: minor + + - name: penpal-runtime + bump: minor + + - name: contracts-rococo-runtime + bump: minor + + - name: rococo-parachain-runtime + bump: minor + + - name: staging-xcm-builder + bump: minor + + - name: polkadot-sdk + bump: minor + + diff --git a/prdoc/pr_5630.prdoc b/prdoc/pr_5630.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..bafb9e746d40a9ac72ce1a8a30c33855e2cb2f8c --- /dev/null +++ b/prdoc/pr_5630.prdoc @@ -0,0 +1,15 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Introduce and Implement the `VestedTransfer` Trait + +doc: + - audience: Runtime Dev + description: | + This PR introduces a new trait `VestedTransfer` which is implemented by `pallet_vesting`. With this, other pallets can easily introduce vested transfers into their logic. + +crates: + - name: frame-support + bump: minor + - name: pallet-vesting + bump: minor diff --git a/prdoc/pr_5635.prdoc b/prdoc/pr_5635.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..168d65970c955e522a719f6473c7378362efa2a9 --- /dev/null +++ b/prdoc/pr_5635.prdoc @@ -0,0 +1,13 @@ +title: Fix edge case where state sync is not triggered + +doc: + - audience: Node Dev + description: | + There is an edge case where the finalized block notification is received, but the conditions required to initiate the + state sync are not fully met. In such cases, state sync would fail to start as expected and remain stalled. + This patch addresses it by storing the pending attempt and trying to start the state sync later when the conditions + are satisfied. + +crates: + - name: sc-network-sync + bump: patch diff --git a/prdoc/pr_5640.prdoc b/prdoc/pr_5640.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..fdd7f5e1b893b8291acc3b0eed8dd77a6a091bed --- /dev/null +++ b/prdoc/pr_5640.prdoc @@ -0,0 +1,10 @@ +title: "[pallet-revive] Move event's topics" + +doc: + - audience: Runtime Dev + description: | + Move event's topics inside body + +crates: + - name: pallet-revive + bump: major diff --git a/prdoc/pr_5664.prdoc b/prdoc/pr_5664.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..7fbe15006da360aad837f958525feb628677d739 --- /dev/null +++ b/prdoc/pr_5664.prdoc @@ -0,0 +1,11 @@ +title: Calling an address without associated code is a balance transfer + +doc: + - audience: Runtime Dev + description: | + This makes pallet_revive behave like EVM where a balance transfer + is just a call to a plain wallet. + +crates: + - name: pallet-revive + bump: patch diff --git a/prdoc/pr_5665.prdoc b/prdoc/pr_5665.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..2b2f0547dc01312b4d5a8abcbd1dee774fe1612b --- /dev/null +++ b/prdoc/pr_5665.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: "[pallet-contracts] remove riscv support" + +doc: + - audience: Runtime Dev + description: | + RISC-V support is now being built inside the new fork pallet-revive + +crates: + - name: pallet-contracts + bump: patch + - name: pallet-contracts-fixtures + bump: patch + - name: pallet-contracts-uapi + bump: patch diff --git a/prdoc/pr_5666.prdoc b/prdoc/pr_5666.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..08bd9815cdd475491dd8250696d9223b31115609 --- /dev/null +++ b/prdoc/pr_5666.prdoc @@ -0,0 +1,19 @@ +title: Make syncing strategy an argument of the syncing engine + +doc: + - audience: Node Dev + description: | + Syncing strategy is no longer implicitly created when building network, but needs to be instantiated explicitly. + Previously default implementation can be created with new function `build_polkadot_syncing_strategy` or custom + syncing strategy could be implemented and used instead if desired, providing greater flexibility for chain + developers. + +crates: + - name: cumulus-client-service + bump: patch + - name: polkadot-service + bump: patch + - name: sc-service + bump: major + - name: sc-network-sync + bump: major diff --git a/prdoc/pr_5675.prdoc b/prdoc/pr_5675.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..ceae446dab1ba67f6b6ce133811c2e4846a292d8 --- /dev/null +++ b/prdoc/pr_5675.prdoc @@ -0,0 +1,14 @@ +title: "[pallet-revive] Add balance_of syscyall for fetching foreign balances" + +doc: + - audience: Runtime Dev + description: | + This adds an API method balance_of, corresponding to the BALANCE EVM opcode. + +crates: + - name: pallet-revive + bump: minor + - name: pallet-revive-uapi + bump: minor + - name: pallet-revive-fixtures + bump: minor diff --git a/prdoc/pr_5676.prdoc b/prdoc/pr_5676.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..dfe23e120b4b1c6d6a3310df25a288add203f585 --- /dev/null +++ b/prdoc/pr_5676.prdoc @@ -0,0 +1,174 @@ +title: '[ci] Update CI image with rust 1.81.0 and 2024-09-11' +doc: +- audience: [Runtime Dev, Node Dev, Node Operator] + description: |- + cc https://github.com/paritytech/ci_cd/issues/1035 + + close https://github.com/paritytech/ci_cd/issues/1023 +crates: +- name: pallet-xcm-bridge-hub + bump: patch +- name: snowbridge-router-primitives + bump: patch +- name: snowbridge-runtime-common + bump: patch +- name: cumulus-pallet-parachain-system + bump: patch +- name: asset-hub-rococo-runtime + bump: patch +- name: asset-hub-westend-runtime + bump: patch +- name: asset-test-utils + bump: patch +- name: bridge-hub-test-utils + bump: patch +- name: cumulus-primitives-utility + bump: patch +- name: polkadot-node-core-approval-voting + bump: patch +- name: polkadot-node-core-pvf-common + bump: patch +- name: polkadot-approval-distribution + bump: patch +- name: polkadot-availability-recovery + bump: patch +- name: polkadot-node-subsystem-types + bump: patch +- name: polkadot-runtime-parachains + bump: patch +- name: westend-runtime + bump: patch +- name: polkadot-statement-table + bump: patch +- name: pallet-xcm-benchmarks + bump: patch +- name: staging-xcm-builder + bump: patch +- name: xcm-runtime-apis + bump: patch +- name: sc-cli + bump: patch +- name: sc-consensus-grandpa + bump: patch +- name: sc-network + bump: patch +- name: sc-network-sync + bump: patch +- name: sc-rpc-spec-v2 + bump: patch +- name: pallet-bags-list + bump: patch +- name: pallet-balances + bump: patch +- name: pallet-bounties + bump: patch +- name: pallet-child-bounties + bump: patch +- name: pallet-nis + bump: patch +- name: pallet-referenda + bump: patch +- name: pallet-revive-proc-macro + bump: patch +- name: pallet-society + bump: patch +- name: pallet-staking + bump: patch +- name: frame-support-procedural + bump: patch +- name: frame-support + bump: patch +- name: pallet-transaction-payment + bump: patch +- name: pallet-utility + bump: patch +- name: pallet-vesting + bump: patch +- name: substrate-wasm-builder + bump: patch +- name: snowbridge-outbound-queue-merkle-tree + bump: patch +- name: shell-runtime + bump: patch +- name: polkadot-parachain-lib + bump: patch +- name: polkadot-cli + bump: patch +- name: polkadot-node-core-pvf + bump: patch +- name: polkadot-service + bump: patch +- name: polkadot-primitives + bump: patch +- name: staging-xcm-executor + bump: patch +- name: sc-consensus-beefy + bump: patch +- name: sc-consensus-slots + bump: patch +- name: frame-benchmarking-pallet-pov + bump: patch +- name: pallet-contracts + bump: patch +- name: frame-election-provider-support + bump: patch +- name: pallet-revive-mock-network + bump: patch +- name: frame-benchmarking-cli + bump: patch +- name: sc-utils + bump: patch +- name: pallet-beefy-mmr + bump: patch +- name: sp-state-machine + bump: patch +- name: fork-tree + bump: patch +- name: sc-transaction-pool + bump: patch +- name: pallet-delegated-staking + bump: patch +- name: sc-executor-wasmtime + bump: patch +- name: cumulus-pallet-xcmp-queue + bump: patch +- name: xcm-procedural + bump: patch +- name: sp-application-crypto + bump: patch +- name: sp-core + bump: patch +- name: sp-keyring + bump: patch +- name: polkadot-availability-distribution + bump: patch +- name: sp-runtime + bump: patch +- name: sc-authority-discovery + bump: patch +- name: frame-system + bump: patch +- name: sc-network-gossip + bump: patch +- name: pallet-authorship + bump: patch +- name: pallet-election-provider-multi-phase + bump: patch +- name: sp-runtime-interface + bump: patch +- name: pallet-bridge-grandpa + bump: patch +- name: pallet-elections-phragmen + bump: patch +- name: frame-executive + bump: patch +- name: bp-header-chain + bump: patch +- name: polkadot-overseer + bump: patch +- name: polkadot + bump: patch +- name: bridge-hub-westend-runtime + bump: major +- name: bp-messages + bump: patch diff --git a/prdoc/pr_5682.prdoc b/prdoc/pr_5682.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..2b05d73ef5525ad9b0cfc8d7f0c0997d3c5b429d --- /dev/null +++ b/prdoc/pr_5682.prdoc @@ -0,0 +1,15 @@ +title: Introduces `VerifyExistenceProof` trait + +doc: + - audience: Runtime Dev + description: | + Introduces `VerifyExistenceProof` trait for verifying proofs in the runtime. + An implementation of the trait for binary and 16 patricia merkle tree is provided. + +crates: + - name: binary-merkle-tree + bump: major + - name: sp-runtime + bump: patch + - name: frame-support + bump: minor diff --git a/prdoc/pr_5684.prdoc b/prdoc/pr_5684.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..9800c85de2ae431e97a3b9546274d3e4d4c7e1ce --- /dev/null +++ b/prdoc/pr_5684.prdoc @@ -0,0 +1,19 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "[pallet-revive]" + +doc: + - audience: Runtime Dev + description: | + Update xcm runtime api, and fix pallet-revive xcm tests + +crates: + - name: pallet-revive + bump: patch + - name: pallet-revive-fixtures + bump: patch + - name: pallet-revive-mock-network + bump: patch + - name: polkadot-sdk + bump: patch diff --git a/prdoc/pr_5687.prdoc b/prdoc/pr_5687.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..f84f8e72226917296a67f0b15facbd717eaba8dc --- /dev/null +++ b/prdoc/pr_5687.prdoc @@ -0,0 +1,24 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "Westend/Rococo Asset Hub: auto incremented asset id for trust backed assets" + +doc: + - audience: Runtime User + description: | + Setup auto incremented asset id to `50_000_000` for trust backed assets. + + ### Migration + This change does not break the API but introduces a new constraint. It implements + an auto-incremented ID strategy for Trust-Backed Assets (50 pallet instance indexes on both + networks), starting at ID 50,000,000. Each new asset must be created with an ID that is one + greater than the last asset created. The next ID can be fetched from the `NextAssetId` + storage item of the assets pallet. An empty `NextAssetId` storage item indicates no + constraint on the next asset ID and can serve as a feature flag for this release. + + +crates: + - name: asset-hub-rococo-runtime + bump: major + - name: asset-hub-westend-runtime + bump: major diff --git a/prdoc/pr_5701.prdoc b/prdoc/pr_5701.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..3d237eb34e749b114f68bff8933510b19e576ef1 --- /dev/null +++ b/prdoc/pr_5701.prdoc @@ -0,0 +1,14 @@ +title: "[pallet-revive] uapi: allow create1 equivalent calls" + +doc: + - audience: Runtime Dev + description: | + The salt argument should be optional to allow create1 equivalent calls. + +crates: + - name: pallet-revive + bump: minor + - name: pallet-revive-uapi + bump: major + - name: pallet-revive-fixtures + bump: patch diff --git a/prdoc/pr_5707.prdoc b/prdoc/pr_5707.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..11136b3c3626441d5500cbf4b79ef469c2fa2ea1 --- /dev/null +++ b/prdoc/pr_5707.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: Remove ValidateFromChainState + +doc: + - audience: Node Dev + description: | + Removed the `CandidateValidationMessage::ValidateFromChainState`, which was previously used by backing, but is no longer relevant since initial async backing implementation + +crates: + - name: polkadot-node-subsystem-types + bump: major + - name: polkadot-node-core-candidate-validation + bump: major + - name: polkadot + bump: patch + - name: polkadot-overseer + bump: patch diff --git a/prdoc/pr_5716.prdoc b/prdoc/pr_5716.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..a986662337295d7f1350422efb8c705cb59b6b1c --- /dev/null +++ b/prdoc/pr_5716.prdoc @@ -0,0 +1,37 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Replace `lazy_static` with `LazyLock` + +doc: + - audience: Node Dev + description: | + Replace all lazy_static usages with LazyLock from the Rust standard library. This will bring us less dependencies. + +crates: + - name: sp-core + bump: patch + - name: sp-panic-handler + bump: patch + - name: sp-trie + bump: patch + - name: sc-utils + bump: major + - name: cumulus-pallet-parachain-system + bump: patch + - name: sp-consensus-beefy + bump: patch + - name: polkadot-node-primitives + bump: patch + - name: polkadot-node-jaeger + bump: patch + - name: frame-benchmarking-cli + bump: major + - name: sc-offchain + bump: patch + - name: polkadot-dispute-distribution + bump: patch + - name: polkadot-gossip-support + bump: patch + - name: xcm-emulator + bump: patch diff --git a/prdoc/pr_5726.prdoc b/prdoc/pr_5726.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..ce666647bad36b965be618fa00b648e11c46d665 --- /dev/null +++ b/prdoc/pr_5726.prdoc @@ -0,0 +1,14 @@ +title: "revive: Limit the amount of static memory" + +doc: + - audience: Runtime Dev + description: | + Limit the amount of static memory a contract can declare. + +crates: + - name: pallet-revive + bump: major + - name: pallet-revive-fixtures + bump: minor + - name: pallet-revive-uapi + bump: patch diff --git a/prdoc/pr_5741.prdoc b/prdoc/pr_5741.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..5eafbc90ee85e07c8fb230a5db6cb8a73f5c4e07 --- /dev/null +++ b/prdoc/pr_5741.prdoc @@ -0,0 +1,25 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: make RPC endpoint `chainHead_v1_storage` faster + +doc: + - audience: Node Operator + description: | + The RPC endpoint `chainHead_v1_storage` now relies solely on backpressure to + determine how quickly to serve back values instead of handing back a fixed number + of entries and then expecting the client to ask for more. This should improve the + throughput for bigger storage queries significantly. + + Benchmarks using subxt on localhost: + - Iterate over 10 accounts on westend-dev -> ~2-3x faster + - Fetch 1024 storage values (i.e, not descedant values) -> ~50x faster + - Fetch 1024 descendant values -> ~500x faster + +crates: + - name: sc-rpc-spec-v2 + bump: major + - name: sc-rpc-server + bump: patch + - name: sc-service + bump: major diff --git a/prdoc/pr_5743.prdoc b/prdoc/pr_5743.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..0059cbaf790c49f3fc4ac62dadecc4af36238b50 --- /dev/null +++ b/prdoc/pr_5743.prdoc @@ -0,0 +1,22 @@ +title: "[pallet-revive] write sandbox output according to the provided output buffer length" + +doc: + - audience: Runtime Dev + description: | + Instead of error out if the provided output buffer is smaller than what we want to write, + we can just write what fits into the output buffer instead. + We already write back the actual bytes written to the in-out pointer, + so contracts can check it anyways. + + This in turn introduces the benefit of allowing contracts to implicitly request only a portion + of the returned data from calls and incantations. + Which is especially beneficial for YUL as the call family opcodes have a return data size + argument and this change removes the need to work around it in contract code. + +crates: + - name: pallet-revive + bump: major + - name: pallet-revive-fixtures + bump: patch + - name: pallet-revive-uapi + bump: patch diff --git a/prdoc/pr_5745.prdoc b/prdoc/pr_5745.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..7463589378a0bcd543b2e6f18a39a02672e8472f --- /dev/null +++ b/prdoc/pr_5745.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: Implement `try_append` for `StorageNMap` + +doc: + - audience: Runtime Dev + description: | + This PR introduces the `try_append` api which is available on other storage map types, + but missing on `StorageNMap`. + +crates: + - name: frame-support + bump: minor diff --git a/prdoc/pr_5753.prdoc b/prdoc/pr_5753.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..dca181ff5c40fb1f953b4b90454139d9926dfa7a --- /dev/null +++ b/prdoc/pr_5753.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: Use maximum allowed response size for request/response protocols + +doc: + - audience: Node Dev + description: | + Increase maximum PoV response size to 16MB which is equal to the default value used in the substrate. + +crates: + - name: sc-network + bump: patch + - name: sc-network-light + bump: patch + - name: sc-network-sync + bump: patch + - name: polkadot-node-network-protocol + bump: patch + - name: sc-network-transactions + bump: patch diff --git a/prdoc/pr_5756.prdoc b/prdoc/pr_5756.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..525f955d3ac122cb73957deccfa1afb6a20c66d8 --- /dev/null +++ b/prdoc/pr_5756.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: Improve APIs for Tries in Runtime + +doc: + - audience: Runtime Dev + description: | + This PR introduces a trait `ProvingTrie` which has all the function you need to use tries in the runtime. + This trait includes the ability to create, query, and prove data in a trie. Another trait `ProofToHashes` + allows developers to express the computational complexity of proof verification using the proof data. +crates: + - name: sp-runtime + bump: major + - name: frame-support + bump: major diff --git a/prdoc/pr_5762.prdoc b/prdoc/pr_5762.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..730b3a46df84f8247401164c90bd60f20d8517fd --- /dev/null +++ b/prdoc/pr_5762.prdoc @@ -0,0 +1,10 @@ +title: Fast return for invalid request of node health + +doc: + - audience: Node Dev + description: | + Return directly when invalid request for node health api + +crates: + - name: sc-rpc-server + bump: patch diff --git a/prdoc/pr_5765.prdoc b/prdoc/pr_5765.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..e8ecca8ba0fffa4dd3333a27fbcfaf2f9b66d05d --- /dev/null +++ b/prdoc/pr_5765.prdoc @@ -0,0 +1,42 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "Added foreign locations to local accounts converter to all the parachains." + +doc: + - audience: Runtime Dev + description: | + Added foreign locations to local accounts converter to all the parachains. + i.e. added HashedDescription> to LocationToAccountId + + - audience: Runtime User + description: | + Now any user account can have a sovereign account on another chain controlled by the original account. + +crates: + - name: asset-hub-westend-runtime + bump: patch + - name: bridge-hub-rococo-runtime + bump: patch + - name: bridge-hub-westend-runtime + bump: patch + - name: collectives-westend-runtime + bump: patch + - name: contracts-rococo-runtime + bump: patch + - name: coretime-rococo-runtime + bump: patch + - name: coretime-westend-runtime + bump: minor + - name: people-rococo-runtime + bump: patch + - name: people-westend-runtime + bump: patch + - name: penpal-runtime + bump: patch + - name: rococo-runtime + bump: patch + - name: westend-runtime + bump: patch + - name: asset-hub-rococo-runtime + bump: patch diff --git a/prdoc/pr_5768.prdoc b/prdoc/pr_5768.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..5c6060065618d0d47bb709ebabee7e85096d9cca --- /dev/null +++ b/prdoc/pr_5768.prdoc @@ -0,0 +1,10 @@ +title: "export NodeHealthProxyLayer" + +doc: + - audience: Node Dev + description: | + This PR export `NodeHealthProxyLayer` from sc-rpc-server. + +crates: + - name: sc-rpc-server + bump: patch diff --git a/prdoc/pr_5774.prdoc b/prdoc/pr_5774.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..15aa64f541046f252378d97262ff22e4b90e32be --- /dev/null +++ b/prdoc/pr_5774.prdoc @@ -0,0 +1,13 @@ +title: Avoid unnecessary state reset of allowed_requests when no block requests are sent + +doc: + - audience: Node Dev + description: | + Previously, the state of `allowed_requests` was always reset to the default + even if there were no new block requests. This could cause an edge case + because `peer_block_request()` will return early next time when there are no ongoing block requests. + This patch fixes it by checking whether block requests are empty before updating the state. + +crates: + - name: sc-network-sync + bump: patch diff --git a/prdoc/pr_5779.prdoc b/prdoc/pr_5779.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..659a3a19f695492c0dc2f9a03b6c64a0f528cd93 --- /dev/null +++ b/prdoc/pr_5779.prdoc @@ -0,0 +1,38 @@ +title: "[pallet-revive] last call return data API" + +doc: + - audience: Runtime Dev + description: | + This PR introduces 2 new syscall: `return_data_size`ย and `return_data_copy`, + resembling the semantics of the EVM `RETURNDATASIZE` and `RETURNDATACOPY` opcodes. + + The ownership of `ExecReturnValue` (the return data) has moved to the `Frame`. + This allows implementing the new contract API surface functionality in ext with no additional copies. + Returned data is passed via contract memory, memory is (will be) metered, + hence the amount of returned data can not be statically known, + so we should avoid storing copies of the returned data if we can. + By moving the ownership of the exectuables return value into the `Frame` struct we achieve this. + + A zero-copy implementation of those APIs would be technically possible without that internal change by making + the callsite in the runtime responsible for moving the returned data into the frame after any call. + However, resetting the stored output needs to be handled in ext, since plain transfers will _not_ affect the + stored return data (and we don't want to handle this special call case inside the `runtime` API). + This has drawbacks: + - It can not be tested easily in the mock. + - It introduces an inconsistency where resetting the stored output is handled in ext, + but the runtime API is responsible to store it back correctly after any calls made. + Instead, with ownership of the data in `Frame`, both can be handled in a single place. + Handling both in `fn run()` is more natural and leaves less room for runtime API bugs. + + The returned output is reset each time _before_ running any executable in a nested stack. + This change should not incur any overhead to the overall memory usage as _only_ the returned data from the last + executed frame will be kept around at any time. + +crates: + - name: pallet-revive + bump: major + - name: pallet-revive-fixtures + bump: minor + - name: pallet-revive-uapi + bump: minor + \ No newline at end of file diff --git a/prdoc/pr_5787.prdoc b/prdoc/pr_5787.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..59d4118f1905063ae79dd7bdf1f10ec66b8d65dd --- /dev/null +++ b/prdoc/pr_5787.prdoc @@ -0,0 +1,13 @@ +title: "Move bitfield_distribution to blocking task pool and set capacity to 8192" + +doc: + - audience: Node Dev + description: | + This is moving bitfield_distribution to the blocking task pool because it does cpu + intensive work and to make it snappier. Additionally, also increase the message + capacity of the subsystem to make sure the queue does not get full if there is a + burst of messages. + +crates: + - name: polkadot-overseer + bump: patch diff --git a/prdoc/pr_5789.prdoc b/prdoc/pr_5789.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..9a808fc89d595d51deba7a240e2fb3f4bfdb0ce0 --- /dev/null +++ b/prdoc/pr_5789.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: Prevents EthereumBlobExporter from consuming parameters when returning NotApplicable + +doc: + - audience: Node Dev + description: | + When the EthereumBlobExporter returned a NotApplicable error, it consumed parameters `universal_source`, + `destination` and `message`. As a result, subsequent exporters could not use these values. This PR corrects + this incorrect behaviour. It also changes error type from `Unroutable` to `NotApplicable` when the global consensus + system cannot be extracted from the `universal_source`, or when the source location cannot be converted to an agent + ID. Lastly, it changes the error type from `MissingArgument` to `NotApplicable` when the parachain ID cannot be + extracted from the location. These changes should have no effect - it is purely to correct behvaiour should + multiple exporters be used. + +crates: + - name: snowbridge-router-primitives + bump: patch diff --git a/prdoc/pr_5796.prdoc b/prdoc/pr_5796.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..76958e3db4f396e70a1460950ac972f3115b3ef1 --- /dev/null +++ b/prdoc/pr_5796.prdoc @@ -0,0 +1,8 @@ +title: "Fix RPC relay chain interface" + +doc: + +crates: + - name: cumulus-relay-chain-rpc-interface + bump: none + validate: false diff --git a/prdoc/pr_5807.prdoc b/prdoc/pr_5807.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..3447ea64e439274ff9545536fb0ef365d5c7affa --- /dev/null +++ b/prdoc/pr_5807.prdoc @@ -0,0 +1,16 @@ +title: "[pallet-revive] last call return data API" + +doc: + - audience: Runtime Dev + description: | + This PR adds the EVM chain ID to Config as well as a corresponding runtime API so contracts can query it. + + Related issue: https://github.com/paritytech/revive/issues/44 + +crates: + - name: pallet-revive + bump: major + - name: pallet-revive-fixtures + bump: patch + - name: pallet-revive-uapi + bump: minor diff --git a/prdoc/pr_5811.prdoc b/prdoc/pr_5811.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..103fef4bb8b09707346942576aa3b232ea9b28be --- /dev/null +++ b/prdoc/pr_5811.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: "Improve `import_notification_stream` documentation" + +doc: + - audience: Node Dev + description: | + "Updates the doc comment on the `import_notification_stream` to make its behaviour clearer. Now it specifically states that this notification stream is fired on every import notification after the initial sync, and only when there are re-orgs in the initial sync." + +crates: + - name: sc-client-api + bump: patch diff --git a/prdoc/pr_5824.prdoc b/prdoc/pr_5824.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..136cd6bfee8432e5ae99e7829a105a5a5a3265f0 --- /dev/null +++ b/prdoc/pr_5824.prdoc @@ -0,0 +1,17 @@ +title: "Bump parachains runtime API to v11" + +doc: + - audience: [ Node Dev, Runtime Dev ] + description: | + This PR promotes all staging methods in v10 to stable and releases v11 stable runtime + APIs. + +crates: + - name: polkadot-runtime-parachains + bump: major + - name: rococo-runtime + bump: patch + - name: westend-runtime + bump: patch + - name: polkadot-test-runtime + bump: patch diff --git a/prdoc/pr_5830.prdoc b/prdoc/pr_5830.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..10b586e4a4afad0fa3f180f5e06764b4e3554357 --- /dev/null +++ b/prdoc/pr_5830.prdoc @@ -0,0 +1,13 @@ +title: "Remove jaeger from approval-voting and approval-distribution" + +doc: + - audience: Node Dev + description: | + Jaeger was remove from approval-voting and approval-distribution because + it did not prove to improve the debugging and it wasted precious cpu cycles. + +crates: + - name: polkadot-approval-distribution + bump: none + - name: polkadot-node-core-approval-voting + bump: none diff --git a/prdoc/pr_5838.prdoc b/prdoc/pr_5838.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..f6ce091a12dec27e59912d84729da998de714434 --- /dev/null +++ b/prdoc/pr_5838.prdoc @@ -0,0 +1,20 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: enable wasm builder diagnostics propagation + +doc: + - audience: Runtime Dev + description: | + `substrate-wasm-builder` is used as a build dependency by crates that implement FRAME runtimes. + Errors that occur in these crates can not be detected by IDEs that use rust-analyzer as a language + server because rust-analyzer needs the errors to be reported as diagnostic message in json format to + be able to publish them to language server clients. This PR adds `WASM_BUILD_CARGO_ARGS` environment + variable, which can hold a space separated list of args that will be parsed and passed to the `cargo` + command that it is used for building against wasm target. It can be used for the stated initial case, + but it is also flexible enough to allow passing other arguments or formatting the messages using another + available type. +crates: + - name: substrate-wasm-builder + bump: patch + diff --git a/prdoc/pr_5839.prdoc b/prdoc/pr_5839.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..1dc95fe5c333ffeae9f67f636d5c995079405a3f --- /dev/null +++ b/prdoc/pr_5839.prdoc @@ -0,0 +1,21 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Remove internal workaround for compiler bug + +doc: + - audience: + - Runtime Dev + - Node Dev + description: | + Remove a workaround we had in the `impl_runtime_apis` macro for a compiler bug that has been long fixed. + No impact on downstream users is expected, except relaxed trait bounds in a few places where the compiler + is now able to deduce more type info itself. + +crates: + - name: sp-api-proc-macro + bump: patch + - name: frame-support-procedural + bump: patch + - name: polkadot-parachain-lib + bump: patch diff --git a/prdoc/pr_5845.prdoc b/prdoc/pr_5845.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..6b214d7599b5b59ffc7ad294a44db78f74605fe1 --- /dev/null +++ b/prdoc/pr_5845.prdoc @@ -0,0 +1,15 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Fix compilation after renaming some of benchmarks in pallet_revive. + +doc: + - audience: Runtime Dev + description: | + Changed the "instr" benchmark so that it should no longer return to little weight. It is still bogus but at least benchmarking should not work. + +crates: + - name: pallet-revive + bump: patch + - name: pallet-revive-fixtures + bump: major \ No newline at end of file diff --git a/prdoc/pr_5856.prdoc b/prdoc/pr_5856.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..383e95e3da88cf7192043fa54118043e5a4f07ab --- /dev/null +++ b/prdoc/pr_5856.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: Extend state tracking of chainHead to capture notification gaps + +doc: + - audience: Node Dev + description: | + This PR extends the state tracking of the RPC-v2 chainHead methods. + ChainHead tracks the reported blocks to detect notification gaps. + This state tracking ensures we can detect `NewBlock` events for + which we did not report previously the parent hash. + +crates: + - name: sc-rpc-spec-v2 + bump: minor + diff --git a/prdoc/pr_5857.prdoc b/prdoc/pr_5857.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..00ee0a8cc70478766940ce33e04fb66f7ec2dc61 --- /dev/null +++ b/prdoc/pr_5857.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: "Beefy equivocation: check all the MMR roots" + +doc: + - audience: + - Runtime Dev + - Runtime User + description: | + This PR adjusts the logic for `report_fork_voting` exposed by `pallet-beefy`. + Normally, the BEEFY protocol only accepts a single MMR Root entry in a commitment's payload. But, in order to + be extra careful, now, when validating equivocation reports, we check all the MMR roots, if there are more. + +crates: + - name: sp-consensus-beefy + bump: patch + - name: pallet-beefy-mmr + bump: patch diff --git a/prdoc/pr_5859.prdoc b/prdoc/pr_5859.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..edb3008238b30d29e22531a8677d7c1a7765eba3 --- /dev/null +++ b/prdoc/pr_5859.prdoc @@ -0,0 +1,11 @@ +title: Add number of live peers available for requests + +doc: + - audience: [Node Operator, Node Dev] + description: | + This PR adds a new metric for the number of live peers available for beefy requests. + The metric is exposed under the name `substrate_beefy_on_demand_live_peers`. + +crates: + - name: sc-consensus-beefy + bump: minor diff --git a/prdoc/pr_5861.prdoc b/prdoc/pr_5861.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..e2187dc1bddecd56c84250405ba676a576e7baa5 --- /dev/null +++ b/prdoc/pr_5861.prdoc @@ -0,0 +1,37 @@ +title: "[pallet-revive] immutable data storage" + +doc: + - audience: Runtime Dev + description: | + This PR introduces the concept of immutable storage data, used for + [Solidity immutable variables](https://docs.soliditylang.org/en/latest/contracts.html#immutable). + + This is a minimal implementation. Immutable data is attached to a contract; to + `ContractInfo` fixed in size, we only store the length there, and store the immutable + data in a dedicated storage map instead. Which comes at the cost of requiring an + storage read (costly) for contracts using this feature. + + We discussed more optimal solutions not requiring any additional storage accesses + internally, but they turned out to be non-trivial to implement. Another optimization + benefiting multiple calls to the same contract in a single call stack would be to cache + the immutable data in `Stack`. However, this potential creates a DOS vulnerability (the + attack vector is to call into as many contracts in a single stack as possible, where + they all have maximum immutable data to fill the cache as efficiently as possible). So + this either has to be guaranteed to be a non-issue by limits, or, more likely, to have + some logic to bound the cache. Eventually, we should think about introducing the concept + of warm and cold storage reads (akin to EVM). Since immutable variables are commonly + used in contracts, this change is blocking our initial launch and we should only + optimize it properly in follow-ups. + + This PR also disables the `set_code_hash` API (which isn't usable for Solidity contracts + without pre-compiles anyways). With immutable storage attached to contracts, we now want + to run the constructor of the new code hash to collect the immutable data during + `set_code_hash`. This will be implemented in a follow up PR. + +crates: + - name: pallet-revive + bump: major + - name: pallet-revive-fixtures + bump: patch + - name: pallet-revive-uapi + bump: minor diff --git a/prdoc/pr_5872.prdoc b/prdoc/pr_5872.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..cf4f0b24f8db3f1b0728dbfba4da405d1d984c81 --- /dev/null +++ b/prdoc/pr_5872.prdoc @@ -0,0 +1,13 @@ +title: '[omni-bencher] Make all runtimes work' +doc: +- audience: Runtime Dev + description: |- + Changes: + - Add `--exclude-pallets` to exclude some pallets from runtimes where we dont have genesis presets yet + - Make `--genesis-builder-policy=none` work with `--runtime` + - CI: Run the frame-omni-bencher for all runtimes +crates: +- name: frame-benchmarking-cli + bump: major +- name: contracts-rococo-runtime + bump: patch diff --git a/prdoc/pr_5875.prdoc b/prdoc/pr_5875.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..fb308c02dde5685878778f307a3617f86f579626 --- /dev/null +++ b/prdoc/pr_5875.prdoc @@ -0,0 +1,47 @@ +title: "Remove jaeger from polkadot" + +doc: + - audience: [ Node Dev, Node Operator ] + description: | + Jaeger was remove from the codebase because it was not used by anyone + and it did not help with the debugging. + +crates: + - name: polkadot-sdk + bump: patch + - name: polkadot-overseer + bump: major + - name: polkadot-node-subsystem + bump: patch + - name: polkadot-node-subsystem-types + bump: major + - name: polkadot-node-network-protocol + bump: major + - name: polkadot-service + bump: major + - name: polkadot-availability-distribution + bump: patch + - name: polkadot-availability-recovery + bump: patch + - name: polkadot-node-core-av-store + bump: patch + - name: polkadot-statement-distribution + bump: patch + - name: polkadot-collator-protocol + bump: patch + - name: polkadot-availability-bitfield-distribution + bump: patch + - name: polkadot-network-bridge + bump: patch + - name: polkadot-node-collation-generation + bump: patch + - name: polkadot-node-core-bitfield-signing + bump: patch + - name: polkadot-node-core-candidate-validation + bump: patch + - name: polkadot-node-core-provisioner + bump: patch + - name: cumulus-relay-chain-inprocess-interface + bump: patch + - name: polkadot-cli + bump: major diff --git a/prdoc/pr_5880.prdoc b/prdoc/pr_5880.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..b246bff11f8d712138dc0efb56c9753ffe0a9a86 --- /dev/null +++ b/prdoc/pr_5880.prdoc @@ -0,0 +1,11 @@ +title: Fix prospective parachains test to use shuffled candidate list + +doc: + - audience: Node Dev + description: | + Fix prospective parachains test to use shuffled candidate list. + Resolves https://github.com/paritytech/polkadot-sdk/issues/5617. + +crates: + - name: polkadot-node-core-prospective-parachains + bump: none diff --git a/prdoc/pr_5886.prdoc b/prdoc/pr_5886.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..f5e59728119783a894aaf61c594bb9a5e275e335 --- /dev/null +++ b/prdoc/pr_5886.prdoc @@ -0,0 +1,18 @@ +title: Bump some dependencies +doc: +- audience: Runtime Dev + description: |- + This bumps `ethbloom`, `ethereum-types`, `primitive-types` and `rlp` to their latest version. + + Fixes: https://github.com/paritytech/polkadot-sdk/issues/5870 +crates: +- name: sc-consensus-babe + bump: patch +- name: pallet-babe + bump: patch +- name: pallet-revive + bump: patch +- name: sp-runtime + bump: patch +- name: bp-polkadot-core + bump: major diff --git a/prdoc/pr_5887.prdoc b/prdoc/pr_5887.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..3ee6ac05a11a40d6cde8b7a69d2e684d4fefd03e --- /dev/null +++ b/prdoc/pr_5887.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: "Set reasonable hard limit for PoV size config value" + +doc: + - audience: + - Runtime Dev + - Runtime User + description: | + Sets the hard limit of the `max_pov_size` host configuration parameter to correspond to the + actual network-related limit rather than to a random constant. + +crates: + - name: polkadot-runtime-parachains + bump: patch + diff --git a/prdoc/pr_5888.prdoc b/prdoc/pr_5888.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..9552eada691582bc3561300e9189ed620f62444f --- /dev/null +++ b/prdoc/pr_5888.prdoc @@ -0,0 +1,16 @@ +title: 'parachain-system: send core selector ump signal' + +doc: + - audience: Runtime Dev + description: | + Send the core selector ump signal in cumulus. Guarded by a compile time feature called `experimental-ump-signals` + until nodes are upgraded to a version that includes https://github.com/paritytech/polkadot-sdk/pull/5423 for + gracefully handling ump signals. + +crates: + - name: cumulus-client-consensus-aura + bump: minor + - name: cumulus-pallet-parachain-system + bump: major + - name: cumulus-primitives-core + bump: minor diff --git a/prdoc/pr_5892.prdoc b/prdoc/pr_5892.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..b909e443328b5caf5231ee5f29d535cd5f3208c2 --- /dev/null +++ b/prdoc/pr_5892.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: "Treasury: add migration to clean up unapproved deprecated proposals" + +doc: + - audience: Runtime Dev + description: | + It is no longer possible to create `Proposals` storage item in `pallet-treasury` due to migration from + governance v1 model but there are some `Proposals` whose bonds are still on hold with no way to release them. + The purpose of this migration is to clear `Proposals` which are stuck and return bonds to the proposers. + +crates: + - name: pallet-treasury + bump: patch + - name: rococo-runtime + bump: patch diff --git a/prdoc/pr_5901.prdoc b/prdoc/pr_5901.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..4d3bce7f45a286564f8a7256adee8652a5ba8585 --- /dev/null +++ b/prdoc/pr_5901.prdoc @@ -0,0 +1,3 @@ +crates: + - name: polkadot-node-core-dispute-coordinator + bump: none diff --git a/prdoc/pr_5911.prdoc b/prdoc/pr_5911.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..8b063242f24f883057e43f9826c513e36b2a8f6b --- /dev/null +++ b/prdoc/pr_5911.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: Removed the possibility to start a shell parachain node + +doc: + - audience: Node Dev + description: | + Removed the possibility to start a shell parachain node using the polkadot-parachain-lib or + polkadot-parachain-bin. + +crates: + - name: polkadot-parachain-lib + bump: minor + - name: polkadot-parachain-bin + bump: minor diff --git a/prdoc/pr_5913.prdoc b/prdoc/pr_5913.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..f50cd722c71469a128fed5f5bdb59d2fc25ca362 --- /dev/null +++ b/prdoc/pr_5913.prdoc @@ -0,0 +1,20 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Remove redundant XCMs from dry run's forwarded xcms + +doc: + - audience: Runtime User + description: | + The DryRunApi was returning the same message repeated multiple times in the + `forwarded_xcms` field. This is no longer the case. + +crates: + - name: pallet-xcm-bridge-hub-router + bump: patch + - name: cumulus-pallet-parachain-system + bump: patch + - name: staging-xcm-builder + bump: patch + - name: emulated-integration-tests-common + bump: minor diff --git a/prdoc/pr_5915.prdoc b/prdoc/pr_5915.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..a9303e2563d115bbb908a5f8cd05f6a04c717ab0 --- /dev/null +++ b/prdoc/pr_5915.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: Omni-Node renamings + +doc: + - audience: Node Dev + description: | + This PR renames the `polkadot-parachain-lib` crate to `polkadot-omni-node-lib` and introduces a new + `polkadot-omni-node` binary. + +crates: + - name: polkadot-omni-node-lib + bump: patch + - name: polkadot-parachain-bin + bump: patch + - name: polkadot-sdk + bump: patch diff --git a/prdoc/pr_5917.prdoc b/prdoc/pr_5917.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..54b2e42ed9c3eb1aa7469bdee43fd0d40354d825 --- /dev/null +++ b/prdoc/pr_5917.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: "XCM paid execution barrier supports more origin altering instructions" + +doc: + - audience: Runtime Dev + description: | + Updates the `AllowTopLevelPaidExecutionFrom` barrier to also support messages that + use `DescendOrigin` or `AliasOrigin` for altering the computed origin during execution. + +crates: + - name: staging-xcm-builder + bump: patch diff --git a/prdoc/pr_5924.prdoc b/prdoc/pr_5924.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..26bde8eec0def19bda308ebf046e5744dfbb85de --- /dev/null +++ b/prdoc/pr_5924.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: Bump PoV request timeout + +doc: + - audience: Node Dev + description: | + With asynchronous backing and PoV size 10MB, we can increase the PoV request timeout from 1.2s to 2s. + +crates: + - name: polkadot-node-network-protocol + bump: patch diff --git a/prdoc/pr_5939.prdoc b/prdoc/pr_5939.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..babb26281ecdeb6b78ea724818c1a3f82b1f4b80 --- /dev/null +++ b/prdoc/pr_5939.prdoc @@ -0,0 +1,14 @@ +title: "[pallet-revive] Bump PolkaVM and add static code validation" + +doc: + - audience: Runtime Dev + description: | + Statically validate basic block sizes and instructions. + +crates: + - name: pallet-revive + bump: major + - name: pallet-revive-fixtures + bump: minor + - name: pallet-revive-uapi + bump: patch diff --git a/prdoc/pr_5941.prdoc b/prdoc/pr_5941.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..4e88400f4ef07920c62379b4f666e8a27bc973a6 --- /dev/null +++ b/prdoc/pr_5941.prdoc @@ -0,0 +1,16 @@ +title: "`SolochainDefaultConfig`: Use correct `AccountData`" + +doc: + - audience: Runtime Dev + description: | + `SolochainDefaultConfig` by default was setting `AccountData` to `AccountInfo`. + Thus, the actual account data was recursively nested the same type. By default + it should be set `()`, because this is the only reasonable `AccountData`. + + If you have used `SolochainDefaultConfig` before and did not overwrite, `AccountData` + you should now overwrite it to `AccountInfo` or you will need to write a migration to + change the data. + +crates: + - name: frame-system + bump: patch diff --git a/prdoc/pr_5946.prdoc b/prdoc/pr_5946.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..9a858c980a195e9d8edd2eefb255a4d4aca7afe9 --- /dev/null +++ b/prdoc/pr_5946.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: "[FRAME] fix: Do not emit `Issued { amount: 0 }` event" + +doc: + - audience: + - Runtime Dev + - Runtime User + description: | + Filter out `Issued` events in `pallet-balances` module when its balance amount is zero. + +crates: + - name: pallet-balances + bump: patch diff --git a/prdoc/pr_5994.prdoc b/prdoc/pr_5994.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..425653e52646d612e39f5b9eeccd1c85bd2ccbf9 --- /dev/null +++ b/prdoc/pr_5994.prdoc @@ -0,0 +1,3 @@ +crates: + - name: sc-consensus-babe + bump: none diff --git a/scripts/getting-started.sh b/scripts/getting-started.sh index 57806280914bd8289b253a69c669f941bbc8dabf..d5fd545481d1ea3d582126eedf00ac3b4e2d8b87 100755 --- a/scripts/getting-started.sh +++ b/scripts/getting-started.sh @@ -4,30 +4,41 @@ set -e prompt() { while true; do - echo "$1 [y/N]" + printf "$1 [y/N]\n" read yn case $yn in [Yy]* ) return 0;; # Yes, return 0 (true) [Nn]* ) return 1;; # No, return 1 (false) "" ) return 1;; # Default to no if user just presses Enter - * ) echo "Please answer yes or no.";; + * ) printf "Please answer yes or no.\n";; esac done } prompt_default_yes() { while true; do - echo "$1 [Y/n]" + printf "$1 [Y/n]\n" read yn case $yn in [Yy]* ) return 0;; # Yes, return 0 (true) [Nn]* ) return 1;; # No, return 1 (false) "" ) return 0;; # Default to yes if user just presses Enter - * ) echo "Please answer yes or no.";; + * ) printf "Please answer yes or no.\n";; esac done } +clone_and_enter_template() { + template="$1" # minimal, solochain, or parachain + if [ -d "${template}-template" ]; then + printf "\nโœ…๏ธŽ ${template}-template directory already exists. -> Entering.\n" + else + printf "\nโ†“ Let's grab the ${template} template from github.\n" + git clone --quiet https://github.com/paritytech/polkadot-sdk-${template}-template.git ${template}-template + fi + cd ${template}-template +} + cat </dev/null 2>&1; then - echo "\nโœ…๏ธŽ๐Ÿบ Homebrew already installed." + printf "\nโœ…๏ธŽ๐Ÿบ Homebrew already installed.\n" else - if prompt_default_yes "\n๐Ÿบ Homebrew is not installed. Install it?"; then - echo "๐Ÿบ Installing Homebrew." + if prompt_default_yes "\n๐Ÿบ Homebrew is not installed. Install it?\n"; then + printf "๐Ÿบ Installing Homebrew.\n" /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" else - echo "โŒ Cannot continue without homebrew. Aborting." + printf "โŒ Cannot continue without homebrew. Aborting.\n" exit 1 fi fi brew update if command -v git >/dev/null 2>&1; then - echo "\nโœ…๏ธŽ๐Ÿบ git already installed." + printf "\nโœ…๏ธŽ๐Ÿบ git already installed.\n" else - if prompt_default_yes "\n๐Ÿบ git seems to be missing but we will need it; install git?"; then + if prompt_default_yes "\n๐Ÿบ git seems to be missing but we will need it; install git?\n"; then brew install git else - echo "โŒ Cannot continue without git. Aborting." + printf "โŒ Cannot continue without git. Aborting.\n" exit 1 fi fi @@ -75,73 +86,94 @@ if [ "$os_name" = "Darwin" ]; then if prompt "\n๐Ÿบ Install cmake, openssl and protobuf?"; then brew install cmake openssl protobuf else - echo "๐Ÿบ Assuming cmake, openssl and protobuf are present." + printf "๐Ÿบ Assuming cmake, openssl and protobuf are present.\n" fi elif [ "$os_name" = "Linux" ]; then # find the distro name in the release files distro=$( cat /etc/*-release | tr '[:upper:]' '[:lower:]' | grep -Poi '(debian|ubuntu|arch|fedora|opensuse)' | uniq | head -n 1 ) if [ "$distro" = "ubuntu" ]; then - echo "\n๐Ÿง Detected Ubuntu. Using apt to install dependencies." - sudo apt install --assume-yes git clang curl libssl-dev protobuf-compiler + printf "\n๐Ÿง Detected Ubuntu. Using apt to install dependencies.\n" + sudo apt -qq update + sudo apt -qq install --assume-yes git clang curl libssl-dev protobuf-compiler make elif [ "$distro" = "debian" ]; then - echo "\n๐Ÿง Detected Debian. Using apt to install dependencies." - sudo apt install --assume-yes git clang curl libssl-dev llvm libudev-dev make protobuf-compiler + printf "\n๐Ÿง Detected Debian. Using apt to install dependencies.\n" + sudo apt -qq update + sudo apt -qq install --assume-yes git clang curl libssl-dev llvm libudev-dev make protobuf-compiler elif [ "$distro" = "arch" ]; then - echo "\n๐Ÿง Detected Arch Linux. Using pacman to install dependencies." + printf "\n๐Ÿง Detected Arch Linux. Using pacman to install dependencies.\n" pacman -Syu --needed --noconfirm curl git clang make protobuf elif [ "$distro" = "fedora" ]; then - echo "\n๐Ÿง Detected Fedora. Using dnf to install dependencies." - sudo dnf update - sudo dnf install clang curl git openssl-devel make protobuf-compiler + printf "\n๐Ÿง Detected Fedora. Using dnf to install dependencies.\n" + sudo dnf update --assumeyes + sudo dnf install --assumeyes clang curl git openssl-devel make protobuf-compiler perl elif [ "$distro" = "opensuse" ]; then - echo "\n๐Ÿง Detected openSUSE. Using zypper to install dependencies." - sudo zypper install clang curl git openssl-devel llvm-devel libudev-devel make protobuf + printf "\n๐Ÿง Detected openSUSE. Using zypper to install dependencies.\n" + sudo zypper install --no-confirm clang gcc gcc-c++ curl git openssl-devel llvm-devel libudev-devel make awk protobuf-devel else - if prompt "\n๐Ÿง Unknown Linux distribution. Unable to install dependencies. Continue anyway?"; then - echo "\n๐Ÿง Proceeding with unknown linux distribution..." + if prompt "\n๐Ÿง Unknown Linux distribution. Unable to install dependencies. Continue anyway?\n"; then + printf "\n๐Ÿง Proceeding with unknown linux distribution...\n" else exit 1 fi fi else - echo "โŒ Unknown operating system. Aborting." + printf "โŒ Unknown operating system. Aborting.\n" exit 1 fi # Check if rust is installed +[ -f "$HOME/.cargo/env" ] && . "$HOME/.cargo/env" if command -v rustc >/dev/null 2>&1; then - echo "\nโœ…๏ธŽ๐Ÿฆ€ Rust already installed." + printf "\nโœ…๏ธŽ๐Ÿฆ€ Rust already installed.\n" else if prompt_default_yes "\n๐Ÿฆ€ Rust is not installed. Install it?"; then - echo "๐Ÿฆ€ Installing via rustup." + printf "๐Ÿฆ€ Installing via rustup.\n" curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + . "$HOME/.cargo/env" else - echo "Aborting." + printf "Aborting.\n" exit 1 fi fi # Ensure that we have wasm support if prompt_default_yes "\n๐Ÿฆ€ Setup the Rust environment (e.g. WASM support)?"; then - echo "๐Ÿฆ€ Setting up Rust environment." + printf "๐Ÿฆ€ Setting up Rust environment.\n" rustup default stable rustup update rustup target add wasm32-unknown-unknown rustup component add rust-src fi -if [ -d "minimal-template" ]; then - echo "\nโœ…๏ธŽ minimal-template directory already exists. -> Entering." -else - echo "\nโ†“ Let's grab the minimal template from github." - git clone https://github.com/paritytech/polkadot-sdk-minimal-template.git minimal-template +if ! prompt "\nWould you like to start with one of the templates?"; then + printf "โšก All done, the environment is ready for hacking.\n" + exit 0 +fi + +while true; do + printf "\nWhich template would you like to start with?\n" + printf "1) minimal template\n" + printf "2) parachain template\n" + printf "3) solochain template\n" + printf "q) cancel\n" + read -p "#? " template + case $template in + [1]* ) clone_and_enter_template minimal; break;; + [2]* ) clone_and_enter_template parachain; break;; + [3]* ) clone_and_enter_template solochain; break;; + [qQ]* ) printf "Canceling, not using a template.\n"; exit 0;; + * ) printf "Selection not recognized.\n";; + esac +done + +if ! prompt_default_yes "\nโš™๏ธ Let's compile the node? It might take a while."; then + printf "โšก Script finished, you can continue in the ${template}-template directory.\n" + exit 0 fi -cd minimal-template -echo "\nโš™๏ธ Let's compile the node." cargo build --release -if prompt_default_yes "\n๐Ÿš€ Everything ready to go, let's run the node?"; then +if prompt_default_yes "\n๐Ÿš€ Everything ready to go, let's run the node?\n"; then cargo run --release -- --dev fi diff --git a/scripts/update-ui-tests.sh b/scripts/update-ui-tests.sh new file mode 100755 index 0000000000000000000000000000000000000000..c25b22fa7f75a1d713854ffd486622ce0f417fdf --- /dev/null +++ b/scripts/update-ui-tests.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +# Script for updating the UI tests for a new rust stable version. +# Exit on error +set -e + +# by default current rust stable will be used +RUSTUP_RUN="" +# check if we have a parameter +# ./scripts/update-ui-tests.sh 1.70 +if [ ! -z "$1" ]; then + echo "RUST_VERSION: $1" + # This will run all UI tests with the rust stable 1.70. + # The script requires that rustup is installed. + RUST_VERSION=$1 + RUSTUP_RUN="rustup run $RUST_VERSION" + + + echo "installing rustup $RUST_VERSION" + if ! command -v rustup &> /dev/null + then + echo "rustup needs to be installed" + exit + fi + + rustup install $RUST_VERSION + rustup component add rust-src --toolchain $RUST_VERSION +fi + +# Ensure we run the ui tests +export RUN_UI_TESTS=1 +# We don't need any wasm files for ui tests +export SKIP_WASM_BUILD=1 +# Let trybuild overwrite the .stderr files +export TRYBUILD=overwrite +# Warnings are part of our UI and the CI also sets this. +export RUSTFLAGS="-C debug-assertions -D warnings" + +# ./substrate +$RUSTUP_RUN cargo test -q --locked --manifest-path substrate/primitives/runtime-interface/Cargo.toml ui +$RUSTUP_RUN cargo test -q --locked -p sp-api-test ui +$RUSTUP_RUN cargo test -q --locked -p frame-election-provider-solution-type ui +$RUSTUP_RUN cargo test -q --locked -p frame-support-test --features=no-metadata-docs,try-runtime,experimental ui +$RUSTUP_RUN cargo test -q --locked -p xcm-procedural ui diff --git a/substrate/bin/node/bench/Cargo.toml b/substrate/bin/node/bench/Cargo.toml index 88ea908abc23a9f270268c6c7911837c2547256b..8c6556da682c109545b8004d2b346018344f7b3b 100644 --- a/substrate/bin/node/bench/Cargo.toml +++ b/substrate/bin/node/bench/Cargo.toml @@ -40,7 +40,6 @@ hash-db = { workspace = true, default-features = true } tempfile = { workspace = true } fs_extra = { workspace = true } rand = { features = ["small_rng"], workspace = true, default-features = true } -lazy_static = { workspace = true } parity-db = { workspace = true } sc-transaction-pool = { workspace = true, default-features = true } sc-transaction-pool-api = { workspace = true, default-features = true } diff --git a/substrate/bin/node/bench/src/trie.rs b/substrate/bin/node/bench/src/trie.rs index 09ab405c03b23baed9217e50e8fb19e50fb18baf..402a186767eeb3203c045c89a58bc3bc27311b26 100644 --- a/substrate/bin/node/bench/src/trie.rs +++ b/substrate/bin/node/bench/src/trie.rs @@ -20,11 +20,14 @@ use hash_db::Prefix; use kvdb::KeyValueDB; -use lazy_static::lazy_static; use rand::Rng; use sp_state_machine::Backend as _; use sp_trie::{trie_types::TrieDBMutBuilderV1, TrieMut as _}; -use std::{borrow::Cow, collections::HashMap, sync::Arc}; +use std::{ + borrow::Cow, + collections::HashMap, + sync::{Arc, LazyLock}, +}; use node_primitives::Hash; @@ -57,10 +60,8 @@ pub enum DatabaseSize { Huge, } -lazy_static! { - static ref KUSAMA_STATE_DISTRIBUTION: SizePool = - SizePool::from_histogram(crate::state_sizes::KUSAMA_STATE_DISTRIBUTION); -} +static KUSAMA_STATE_DISTRIBUTION: LazyLock = + LazyLock::new(|| SizePool::from_histogram(crate::state_sizes::KUSAMA_STATE_DISTRIBUTION)); impl DatabaseSize { /// Should be multiple of SAMPLE_SIZE! diff --git a/substrate/bin/node/cli/benches/block_production.rs b/substrate/bin/node/cli/benches/block_production.rs index 9ac9d8b4f67dbe5f1dd825c34372c1594a3508de..de883d1051f50e1cf8a24cf83eef4f48d771073a 100644 --- a/substrate/bin/node/cli/benches/block_production.rs +++ b/substrate/bin/node/cli/benches/block_production.rs @@ -22,6 +22,7 @@ use criterion::{criterion_group, criterion_main, BatchSize, Criterion, Throughpu use kitchensink_runtime::{constants::currency::*, BalancesCall}; use node_cli::service::{create_extrinsic, FullClient}; +use polkadot_sdk::sc_service::config::{ExecutorConfiguration, RpcConfiguration}; use sc_block_builder::{BlockBuilderBuilder, BuiltBlock}; use sc_consensus::{ block_import::{BlockImportParams, ForkChoiceStrategy}, @@ -73,34 +74,36 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase { state_pruning: Some(PruningMode::ArchiveAll), blocks_pruning: BlocksPruning::KeepAll, chain_spec: spec, - wasm_method: WasmExecutionMethod::Compiled { - instantiation_strategy: WasmtimeInstantiationStrategy::PoolingCopyOnWrite, + executor: ExecutorConfiguration { + wasm_method: WasmExecutionMethod::Compiled { + instantiation_strategy: WasmtimeInstantiationStrategy::PoolingCopyOnWrite, + }, + ..ExecutorConfiguration::default() + }, + rpc: RpcConfiguration { + addr: None, + max_connections: Default::default(), + cors: None, + methods: Default::default(), + max_request_size: Default::default(), + max_response_size: Default::default(), + id_provider: Default::default(), + max_subs_per_conn: Default::default(), + port: 9944, + message_buffer_capacity: Default::default(), + batch_config: RpcBatchRequestConfig::Unlimited, + rate_limit: None, + rate_limit_whitelisted_ips: Default::default(), + rate_limit_trust_proxy_headers: Default::default(), }, - rpc_addr: None, - rpc_max_connections: Default::default(), - rpc_cors: None, - rpc_methods: Default::default(), - rpc_max_request_size: Default::default(), - rpc_max_response_size: Default::default(), - rpc_id_provider: Default::default(), - rpc_max_subs_per_conn: Default::default(), - rpc_port: 9944, - rpc_message_buffer_capacity: Default::default(), - rpc_batch_config: RpcBatchRequestConfig::Unlimited, - rpc_rate_limit: None, - rpc_rate_limit_whitelisted_ips: Default::default(), - rpc_rate_limit_trust_proxy_headers: Default::default(), prometheus_config: None, telemetry_endpoints: None, - default_heap_pages: None, offchain_worker: OffchainWorkerConfig { enabled: true, indexing_enabled: false }, force_authoring: false, disable_grandpa: false, dev_key_seed: Some(Sr25519Keyring::Alice.to_seed()), tracing_targets: None, tracing_receiver: Default::default(), - max_runtime_instances: 8, - runtime_cache_size: 2, announce_block: true, data_path: base_path.path().into(), base_path, diff --git a/substrate/bin/node/cli/benches/transaction_pool.rs b/substrate/bin/node/cli/benches/transaction_pool.rs index 9a71a4ec585d4621f5cfd141f55ad9fb2a5ab76a..efec081427f4b6a4eab597b955e56e75eba9972a 100644 --- a/substrate/bin/node/cli/benches/transaction_pool.rs +++ b/substrate/bin/node/cli/benches/transaction_pool.rs @@ -24,6 +24,7 @@ use futures::{future, StreamExt}; use kitchensink_runtime::{constants::currency::*, BalancesCall, SudoCall}; use node_cli::service::{create_extrinsic, fetch_nonce, FullClient, TransactionPool}; use node_primitives::AccountId; +use polkadot_sdk::sc_service::config::{ExecutorConfiguration, RpcConfiguration}; use sc_service::{ config::{ BlocksPruning, DatabaseSource, KeystoreConfig, NetworkConfiguration, OffchainWorkerConfig, @@ -70,32 +71,31 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase { state_pruning: Some(PruningMode::ArchiveAll), blocks_pruning: BlocksPruning::KeepAll, chain_spec: spec, - wasm_method: Default::default(), - rpc_addr: None, - rpc_max_connections: Default::default(), - rpc_cors: None, - rpc_methods: Default::default(), - rpc_max_request_size: Default::default(), - rpc_max_response_size: Default::default(), - rpc_id_provider: Default::default(), - rpc_max_subs_per_conn: Default::default(), - rpc_port: 9944, - rpc_message_buffer_capacity: Default::default(), - rpc_batch_config: RpcBatchRequestConfig::Unlimited, - rpc_rate_limit: None, - rpc_rate_limit_whitelisted_ips: Default::default(), - rpc_rate_limit_trust_proxy_headers: Default::default(), + executor: ExecutorConfiguration::default(), + rpc: RpcConfiguration { + addr: None, + max_connections: Default::default(), + cors: None, + methods: Default::default(), + max_request_size: Default::default(), + max_response_size: Default::default(), + id_provider: Default::default(), + max_subs_per_conn: Default::default(), + port: 9944, + message_buffer_capacity: Default::default(), + batch_config: RpcBatchRequestConfig::Unlimited, + rate_limit: None, + rate_limit_whitelisted_ips: Default::default(), + rate_limit_trust_proxy_headers: Default::default(), + }, prometheus_config: None, telemetry_endpoints: None, - default_heap_pages: None, offchain_worker: OffchainWorkerConfig { enabled: true, indexing_enabled: false }, force_authoring: false, disable_grandpa: false, dev_key_seed: Some(Sr25519Keyring::Alice.to_seed()), tracing_targets: None, tracing_receiver: Default::default(), - max_runtime_instances: 8, - runtime_cache_size: 2, announce_block: true, data_path: base_path.path().into(), base_path, diff --git a/substrate/bin/node/cli/src/service.rs b/substrate/bin/node/cli/src/service.rs index 8fdcc7261b55fc00c4c69a726eac967b7a8f072e..69e953f54e42ee63262a8c96381e58d24ed34078 100644 --- a/substrate/bin/node/cli/src/service.rs +++ b/substrate/bin/node/cli/src/service.rs @@ -32,6 +32,7 @@ use frame_system_rpc_runtime_api::AccountNonceApi; use futures::prelude::*; use kitchensink_runtime::RuntimeApi; use node_primitives::Block; +use polkadot_sdk::sc_service::build_polkadot_syncing_strategy; use sc_client_api::{Backend, BlockBackend}; use sc_consensus_babe::{self, SlotProportion}; use sc_network::{ @@ -208,7 +209,7 @@ pub fn new_partial( }) .transpose()?; - let executor = sc_service::new_wasm_executor(&config); + let executor = sc_service::new_wasm_executor(&config.executor); let (client, backend, keystore_container, task_manager) = sc_service::new_full_parts::( @@ -404,7 +405,7 @@ pub fn new_full_base::Hash>>( ), ) -> Result { let is_offchain_indexing_enabled = config.offchain_worker.indexing_enabled; - let role = config.role.clone(); + let role = config.role; let force_authoring = config.force_authoring; let backoff_authoring_blocks = Some(sc_consensus_slots::BackoffAuthoringOnFinalizedHeadLagging::default()); @@ -416,7 +417,7 @@ pub fn new_full_base::Hash>>( let hwbench = (!disable_hardware_benchmarks) .then_some(config.database.path().map(|database_path| { let _ = std::fs::create_dir_all(&database_path); - sc_sysinfo::gather_hwbench(Some(database_path)) + sc_sysinfo::gather_hwbench(Some(database_path), &SUBSTRATE_REFERENCE_HARDWARE) })) .flatten(); @@ -506,6 +507,16 @@ pub fn new_full_base::Hash>>( Vec::default(), )); + let syncing_strategy = build_polkadot_syncing_strategy( + config.protocol_id(), + config.chain_spec.fork_id(), + &mut net_config, + Some(WarpSyncConfig::WithProvider(warp_sync)), + client.clone(), + &task_manager.spawn_handle(), + config.prometheus_config.as_ref().map(|config| &config.registry), + )?; + let (network, system_rpc_tx, tx_handler_controller, network_starter, sync_service) = sc_service::build_network(sc_service::BuildNetworkParams { config: &config, @@ -515,7 +526,7 @@ pub fn new_full_base::Hash>>( spawn_handle: task_manager.spawn_handle(), import_queue, block_announce_validator_builder: None, - warp_sync_config: Some(WarpSyncConfig::WithProvider(warp_sync)), + syncing_strategy, block_relay: None, metrics, })?; @@ -553,7 +564,7 @@ pub fn new_full_base::Hash>>( if let Some(hwbench) = hwbench { sc_sysinfo::print_hwbench(&hwbench); - match SUBSTRATE_REFERENCE_HARDWARE.check_hardware(&hwbench) { + match SUBSTRATE_REFERENCE_HARDWARE.check_hardware(&hwbench, false) { Err(err) if role.is_authority() => { log::warn!( "โš ๏ธ The hardware does not meet the minimal requirements {} for role 'Authority'.", @@ -717,7 +728,7 @@ pub fn new_full_base::Hash>>( name: Some(name), observer_enabled: false, keystore, - local_role: role.clone(), + local_role: role, telemetry: telemetry.as_ref().map(|x| x.handle()), protocol_name: grandpa_protocol_name, }; diff --git a/substrate/bin/node/cli/tests/benchmark_pallet_works.rs b/substrate/bin/node/cli/tests/benchmark_pallet_works.rs index 8441333429bea6ed3cee7d33a6edac9ccb8d4586..d913228881a4e93b78fc03b51d3050c26a5f617a 100644 --- a/substrate/bin/node/cli/tests/benchmark_pallet_works.rs +++ b/substrate/bin/node/cli/tests/benchmark_pallet_works.rs @@ -33,6 +33,31 @@ fn benchmark_pallet_works() { benchmark_pallet(20, 50, true); } +#[test] +fn benchmark_pallet_args_work() { + benchmark_pallet_args(&["--list", "--pallet=pallet_balances"], true); + benchmark_pallet_args(&["--list", "--pallet=pallet_balances"], true); + benchmark_pallet_args( + &["--list", "--pallet=pallet_balances", "--genesis-builder=spec-genesis"], + true, + ); + benchmark_pallet_args( + &["--list", "--pallet=pallet_balances", "--chain=dev", "--genesis-builder=spec-genesis"], + true, + ); + + // Error because the genesis runtime does not have any presets in it: + benchmark_pallet_args( + &["--list", "--pallet=pallet_balances", "--chain=dev", "--genesis-builder=spec-runtime"], + false, + ); + // Error because no runtime is provided: + benchmark_pallet_args( + &["--list", "--pallet=pallet_balances", "--chain=dev", "--genesis-builder=runtime"], + false, + ); +} + fn benchmark_pallet(steps: u32, repeat: u32, should_work: bool) { let status = Command::new(cargo_bin("substrate-node")) .args(["benchmark", "pallet", "--dev"]) @@ -51,3 +76,13 @@ fn benchmark_pallet(steps: u32, repeat: u32, should_work: bool) { assert_eq!(status.success(), should_work); } + +fn benchmark_pallet_args(args: &[&str], should_work: bool) { + let status = Command::new(cargo_bin("substrate-node")) + .args(["benchmark", "pallet"]) + .args(args) + .status() + .unwrap(); + + assert_eq!(status.success(), should_work); +} diff --git a/substrate/bin/node/cli/tests/fees.rs b/substrate/bin/node/cli/tests/fees.rs index 9f82338b4fb03bbe36d09894add142027e419f7e..b49af4c1055cd9774905c0c7f1c44c6f90b25ac3 100644 --- a/substrate/bin/node/cli/tests/fees.rs +++ b/substrate/bin/node/cli/tests/fees.rs @@ -188,135 +188,3 @@ fn transaction_fee_is_correct() { assert_eq!(Balances::total_balance(&alice()), balance_alice); }); } - -#[test] -#[should_panic] -#[cfg(feature = "stress-test")] -fn block_weight_capacity_report() { - // Just report how many transfer calls you could fit into a block. The number should at least - // be a few hundred (250 at the time of writing but can change over time). Runs until panic. - use node_primitives::Nonce; - - // execution ext. - let mut t = new_test_ext(compact_code_unwrap()); - // setup ext. - let mut tt = new_test_ext(compact_code_unwrap()); - - let factor = 50; - let mut time = 10; - let mut nonce: Nonce = 0; - let mut block_number = 1; - let mut previous_hash: node_primitives::Hash = GENESIS_HASH.into(); - - loop { - 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))), - function: RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { - dest: bob().into(), - value: 0, - }), - }) - .collect::>(); - - xts.insert( - 0, - CheckedExtrinsic { - signed: None, - function: RuntimeCall::Timestamp(pallet_timestamp::Call::set { now: time * 1000 }), - }, - ); - - // NOTE: this is super slow. Can probably be improved. - let block = construct_block( - &mut tt, - block_number, - previous_hash, - xts, - (time * 1000 / SLOT_DURATION).into(), - ); - - let len = block.0.len(); - print!( - "++ Executing block with {} transfers. Block size = {} bytes / {} kb / {} mb", - num_transfers, - len, - len / 1024, - len / 1024 / 1024, - ); - - let r = executor_call(&mut t, "Core_execute_block", &block.0).0; - - println!(" || Result = {:?}", r); - assert!(r.is_ok()); - - previous_hash = block.1; - nonce += num_transfers; - time += 10; - block_number += 1; - } -} - -#[test] -#[should_panic] -#[cfg(feature = "stress-test")] -fn block_length_capacity_report() { - // Just report how big a block can get. Executes until panic. Should be ignored unless if - // manually inspected. The number should at least be a few megabytes (5 at the time of - // writing but can change over time). - use node_primitives::Nonce; - - // execution ext. - let mut t = new_test_ext(compact_code_unwrap()); - // setup ext. - let mut tt = new_test_ext(compact_code_unwrap()); - - let factor = 256 * 1024; - let mut time = 10; - let mut nonce: Nonce = 0; - let mut block_number = 1; - let mut previous_hash: node_primitives::Hash = GENESIS_HASH.into(); - - loop { - // NOTE: this is super slow. Can probably be improved. - let block = construct_block( - &mut tt, - block_number, - previous_hash, - vec![ - CheckedExtrinsic { - signed: None, - function: RuntimeCall::Timestamp(pallet_timestamp::Call::set { - now: time * 1000, - }), - }, - CheckedExtrinsic { - signed: Some((charlie(), signed_extra(nonce, 0))), - function: RuntimeCall::System(frame_system::Call::remark { - remark: vec![0u8; (block_number * factor) as usize], - }), - }, - ], - (time * 1000 / SLOT_DURATION).into(), - ); - - let len = block.0.len(); - print!( - "++ Executing block with big remark. Block size = {} bytes / {} kb / {} mb", - len, - len / 1024, - len / 1024 / 1024, - ); - - let r = executor_call(&mut t, "Core_execute_block", &block.0).0; - - println!(" || Result = {:?}", r); - assert!(r.is_ok()); - - previous_hash = block.1; - nonce += 1; - time += 10; - block_number += 1; - } -} diff --git a/substrate/bin/node/inspect/src/command.rs b/substrate/bin/node/inspect/src/command.rs index e0e25707e31b06b7d53f31bde9a0b809211d8dce..b9e5e55be8ef3b273e6d05b46be8490b70838f4b 100644 --- a/substrate/bin/node/inspect/src/command.rs +++ b/substrate/bin/node/inspect/src/command.rs @@ -36,7 +36,7 @@ impl InspectCmd { B: Block, RA: Send + Sync + 'static, { - let executor = sc_service::new_wasm_executor::(&config); + let executor = sc_service::new_wasm_executor::(&config.executor); let client = sc_service::new_full_client::(&config, None, executor)?; let inspect = Inspector::::new(client); diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index ef5c52bf6e6ec88fa7db507e8900e988871ba209..c81921f844bd51e01cb95c89025c7192dc055505 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -49,8 +49,8 @@ use frame_support::{ imbalance::ResolveAssetTo, nonfungibles_v2::Inspect, pay::PayAssetFromAccount, GetSalary, PayFromAccount, }, - AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32, Contains, Currency, - EitherOfDiverse, EnsureOriginWithArg, EqualPrivilegeOnly, Imbalance, InsideBoth, + AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32, ConstU64, Contains, + Currency, EitherOfDiverse, EnsureOriginWithArg, EqualPrivilegeOnly, Imbalance, InsideBoth, InstanceFilter, KeyOwnerProofSystem, LinearStoragePrice, LockIdentifier, Nothing, OnUnbalanced, VariantCountOf, WithdrawReasons, }, @@ -91,7 +91,7 @@ use sp_consensus_beefy::{ mmr::MmrLeafVersion, }; use sp_consensus_grandpa::AuthorityId as GrandpaId; -use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; +use sp_core::{crypto::KeyTypeId, OpaqueMetadata, H160}; use sp_inherents::{CheckInherentsResult, InherentData}; use sp_runtime::{ create_runtime_str, @@ -171,7 +171,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 2, - state_version: 1, + system_version: 1, }; /// The BABE epoch configuration at genesis. @@ -507,8 +507,7 @@ impl pallet_babe::Config for Runtime { type WeightInfo = (); type MaxAuthorities = MaxAuthorities; type MaxNominators = MaxNominators; - type KeyOwnerProof = - >::Proof; + type KeyOwnerProof = sp_session::MembershipProof; type EquivocationReportSystem = pallet_babe::EquivocationReportSystem; } @@ -547,6 +546,7 @@ impl pallet_balances::Config for Runtime { type WeightInfo = pallet_balances::weights::SubstrateWeight; type FreezeIdentifier = RuntimeFreezeReason; type MaxFreezes = VariantCountOf; + type DoneSlashHandler = (); } parameter_types! { @@ -1114,6 +1114,9 @@ parameter_types! { pub const CouncilMotionDuration: BlockNumber = 5 * DAYS; pub const CouncilMaxProposals: u32 = 100; pub const CouncilMaxMembers: u32 = 100; + pub const ProposalDepositOffset: Balance = ExistentialDeposit::get() + ExistentialDeposit::get(); + pub const ProposalHoldReason: RuntimeHoldReason = + RuntimeHoldReason::Council(pallet_collective::HoldReason::ProposalSubmission); } type CouncilCollective = pallet_collective::Instance1; @@ -1128,6 +1131,18 @@ impl pallet_collective::Config for Runtime { type WeightInfo = pallet_collective::weights::SubstrateWeight; type SetMembersOrigin = EnsureRoot; type MaxProposalWeight = MaxCollectivesProposalWeight; + type DisapproveOrigin = EnsureRoot; + type KillOrigin = EnsureRoot; + type Consideration = HoldConsideration< + AccountId, + Balances, + ProposalHoldReason, + pallet_collective::deposit::Delayed< + ConstU32<2>, + pallet_collective::deposit::Linear, ProposalDepositOffset>, + >, + u32, + >; } parameter_types! { @@ -1189,6 +1204,9 @@ impl pallet_collective::Config for Runtime { type WeightInfo = pallet_collective::weights::SubstrateWeight; type SetMembersOrigin = EnsureRoot; type MaxProposalWeight = MaxCollectivesProposalWeight; + type DisapproveOrigin = EnsureRoot; + type KillOrigin = EnsureRoot; + type Consideration = (); } type EnsureRootOrHalfCouncil = EitherOfDiverse< @@ -1392,21 +1410,17 @@ impl pallet_revive::Config for Runtime { type WeightPrice = pallet_transaction_payment::Pallet; type WeightInfo = pallet_revive::weights::SubstrateWeight; type ChainExtension = (); - type AddressGenerator = pallet_revive::DefaultAddressGenerator; - type MaxCodeLen = ConstU32<{ 123 * 1024 }>; + type AddressMapper = pallet_revive::DefaultAddressMapper; type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>; type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>; type UnsafeUnstableInterface = ConstBool; type UploadOrigin = EnsureSigned; type InstantiateOrigin = EnsureSigned; type RuntimeHoldReason = RuntimeHoldReason; - #[cfg(not(feature = "runtime-benchmarks"))] - type Migrations = (); - #[cfg(feature = "runtime-benchmarks")] - type Migrations = pallet_revive::migration::codegen::BenchMigrations; type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; type Debug = (); type Xcm = (); + type ChainId = ConstU64<420_420_420>; } impl pallet_sudo::Config for Runtime { @@ -1516,7 +1530,7 @@ impl pallet_grandpa::Config for Runtime { type MaxAuthorities = MaxAuthorities; type MaxNominators = MaxNominators; type MaxSetIdSessionEntries = MaxSetIdSessionEntries; - type KeyOwnerProof = >::Proof; + type KeyOwnerProof = sp_session::MembershipProof; type EquivocationReportSystem = pallet_grandpa::EquivocationReportSystem; } @@ -2027,6 +2041,9 @@ impl pallet_collective::Config for Runtime { type WeightInfo = pallet_collective::weights::SubstrateWeight; type SetMembersOrigin = EnsureRoot; type MaxProposalWeight = MaxCollectivesProposalWeight; + type DisapproveOrigin = EnsureRoot; + type KillOrigin = EnsureRoot; + type Consideration = (); } parameter_types! { @@ -2572,7 +2589,6 @@ type Migrations = ( pallet_nomination_pools::migration::versioned::V6ToV7, pallet_alliance::migration::Migration, pallet_contracts::Migration, - pallet_revive::Migration, pallet_identity::migration::versioned::V0ToV1, ); @@ -2593,7 +2609,7 @@ impl pallet_beefy::Config for Runtime { type OnNewValidatorSet = MmrLeaf; type AncestryHelper = MmrLeaf; type WeightInfo = (); - type KeyOwnerProof = >::Proof; + type KeyOwnerProof = sp_session::MembershipProof; type EquivocationReportSystem = pallet_beefy::EquivocationReportSystem; } @@ -2988,11 +3004,11 @@ impl_runtime_apis! { } } - impl pallet_revive::ReviveApi for Runtime + impl pallet_revive::ReviveApi for Runtime { fn call( origin: AccountId, - dest: AccountId, + dest: H160, value: Balance, gas_limit: Option, storage_deposit_limit: Option, @@ -3015,10 +3031,10 @@ impl_runtime_apis! { value: Balance, gas_limit: Option, storage_deposit_limit: Option, - code: pallet_revive::Code, + code: pallet_revive::Code, data: Vec, - salt: Vec, - ) -> pallet_revive::ContractInstantiateResult + salt: Option<[u8; 32]>, + ) -> pallet_revive::ContractInstantiateResult { Revive::bare_instantiate( RuntimeOrigin::signed(origin), @@ -3037,7 +3053,7 @@ impl_runtime_apis! { origin: AccountId, code: Vec, storage_deposit_limit: Option, - ) -> pallet_revive::CodeUploadResult + ) -> pallet_revive::CodeUploadResult { Revive::bare_upload_code( RuntimeOrigin::signed(origin), @@ -3047,8 +3063,8 @@ impl_runtime_apis! { } fn get_storage( - address: AccountId, - key: Vec, + address: H160, + key: [u8; 32], ) -> pallet_revive::GetStorageResult { Revive::get_storage( address, diff --git a/substrate/bin/utils/chain-spec-builder/Cargo.toml b/substrate/bin/utils/chain-spec-builder/Cargo.toml index 070cf13091756449239894ba6c811a9c21456330..f2fe8cb7e166c7b5f8a70e73582be0c9c173e2a1 100644 --- a/substrate/bin/utils/chain-spec-builder/Cargo.toml +++ b/substrate/bin/utils/chain-spec-builder/Cargo.toml @@ -28,4 +28,8 @@ clap = { features = ["derive"], workspace = true } log = { workspace = true, default-features = true } sc-chain-spec = { features = ["clap"], workspace = true, default-features = true } serde_json = { workspace = true, default-features = true } +serde = { workspace = true, default-features = true } sp-tracing = { workspace = true, default-features = true } + +[dev-dependencies] +substrate-test-runtime = { workspace = true } diff --git a/substrate/bin/utils/chain-spec-builder/bin/main.rs b/substrate/bin/utils/chain-spec-builder/bin/main.rs index 39fa054b4806d396acd557947a957c6cfdd519f7..9898a582dd146a1f57e4021245c1cbca98f7c6f7 100644 --- a/substrate/bin/utils/chain-spec-builder/bin/main.rs +++ b/substrate/bin/utils/chain-spec-builder/bin/main.rs @@ -16,19 +16,9 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use chain_spec_builder::{ - generate_chain_spec_for_runtime, AddCodeSubstituteCmd, ChainSpecBuilder, ChainSpecBuilderCmd, - ConvertToRawCmd, DisplayPresetCmd, ListPresetsCmd, UpdateCodeCmd, VerifyCmd, -}; +use chain_spec_builder::ChainSpecBuilder; use clap::Parser; -use sc_chain_spec::{ - set_code_substitute_in_json_chain_spec, update_code_in_json_chain_spec, GenericChainSpec, - GenesisConfigBuilderRuntimeCaller, -}; use staging_chain_spec_builder as chain_spec_builder; -use std::fs; - -type ChainSpec = GenericChainSpec<(), ()>; //avoid error message escaping fn main() { @@ -42,99 +32,5 @@ fn inner_main() -> Result<(), String> { sp_tracing::try_init_simple(); let builder = ChainSpecBuilder::parse(); - let chain_spec_path = builder.chain_spec_path.to_path_buf(); - - match builder.command { - ChainSpecBuilderCmd::Create(cmd) => { - let chain_spec_json = generate_chain_spec_for_runtime(&cmd)?; - fs::write(chain_spec_path, chain_spec_json).map_err(|err| err.to_string())?; - }, - ChainSpecBuilderCmd::UpdateCode(UpdateCodeCmd { - ref input_chain_spec, - ref runtime_wasm_path, - }) => { - let chain_spec = ChainSpec::from_json_file(input_chain_spec.clone())?; - - let mut chain_spec_json = - serde_json::from_str::(&chain_spec.as_json(false)?) - .map_err(|e| format!("Conversion to json failed: {e}"))?; - update_code_in_json_chain_spec( - &mut chain_spec_json, - &fs::read(runtime_wasm_path.as_path()) - .map_err(|e| format!("Wasm blob file could not be read: {e}"))?[..], - ); - - let chain_spec_json = serde_json::to_string_pretty(&chain_spec_json) - .map_err(|e| format!("to pretty failed: {e}"))?; - fs::write(chain_spec_path, chain_spec_json).map_err(|err| err.to_string())?; - }, - ChainSpecBuilderCmd::AddCodeSubstitute(AddCodeSubstituteCmd { - ref input_chain_spec, - ref runtime_wasm_path, - block_height, - }) => { - let chain_spec = ChainSpec::from_json_file(input_chain_spec.clone())?; - - let mut chain_spec_json = - serde_json::from_str::(&chain_spec.as_json(false)?) - .map_err(|e| format!("Conversion to json failed: {e}"))?; - - set_code_substitute_in_json_chain_spec( - &mut chain_spec_json, - &fs::read(runtime_wasm_path.as_path()) - .map_err(|e| format!("Wasm blob file could not be read: {e}"))?[..], - block_height, - ); - let chain_spec_json = serde_json::to_string_pretty(&chain_spec_json) - .map_err(|e| format!("to pretty failed: {e}"))?; - fs::write(chain_spec_path, chain_spec_json).map_err(|err| err.to_string())?; - }, - ChainSpecBuilderCmd::ConvertToRaw(ConvertToRawCmd { ref input_chain_spec }) => { - let chain_spec = ChainSpec::from_json_file(input_chain_spec.clone())?; - - let chain_spec_json = - serde_json::from_str::(&chain_spec.as_json(true)?) - .map_err(|e| format!("Conversion to json failed: {e}"))?; - - let chain_spec_json = serde_json::to_string_pretty(&chain_spec_json) - .map_err(|e| format!("Conversion to pretty failed: {e}"))?; - fs::write(chain_spec_path, chain_spec_json).map_err(|err| err.to_string())?; - }, - ChainSpecBuilderCmd::Verify(VerifyCmd { ref input_chain_spec }) => { - let chain_spec = ChainSpec::from_json_file(input_chain_spec.clone())?; - let _ = serde_json::from_str::(&chain_spec.as_json(true)?) - .map_err(|e| format!("Conversion to json failed: {e}"))?; - }, - ChainSpecBuilderCmd::ListPresets(ListPresetsCmd { runtime_wasm_path }) => { - let code = fs::read(runtime_wasm_path.as_path()) - .map_err(|e| format!("wasm blob shall be readable {e}"))?; - let caller: GenesisConfigBuilderRuntimeCaller = - GenesisConfigBuilderRuntimeCaller::new(&code[..]); - let presets = caller - .preset_names() - .map_err(|e| format!("getting default config from runtime should work: {e}"))?; - let presets: Vec = presets - .into_iter() - .map(|preset| { - String::from( - TryInto::<&str>::try_into(&preset) - .unwrap_or_else(|_| "cannot display preset id") - .to_string(), - ) - }) - .collect(); - println!("{}", serde_json::json!({"presets":presets}).to_string()); - }, - ChainSpecBuilderCmd::DisplayPreset(DisplayPresetCmd { runtime_wasm_path, preset_name }) => { - let code = fs::read(runtime_wasm_path.as_path()) - .map_err(|e| format!("wasm blob shall be readable {e}"))?; - let caller: GenesisConfigBuilderRuntimeCaller = - GenesisConfigBuilderRuntimeCaller::new(&code[..]); - let preset = caller - .get_named_preset(preset_name.as_ref()) - .map_err(|e| format!("getting default config from runtime should work: {e}"))?; - println!("{preset}"); - }, - }; - Ok(()) + builder.run() } diff --git a/substrate/bin/utils/chain-spec-builder/src/lib.rs b/substrate/bin/utils/chain-spec-builder/src/lib.rs index 6c679f109a002401bb6b25718979c2e8fc58d2a9..629edcf685685d73312fefe1a967a8585465cfaf 100644 --- a/substrate/bin/utils/chain-spec-builder/src/lib.rs +++ b/substrate/bin/utils/chain-spec-builder/src/lib.rs @@ -117,11 +117,18 @@ //! [sp-genesis-builder-list]: ../sp_genesis_builder/trait.GenesisBuilder.html#method.preset_names //! [sp-genesis-builder-get-preset]: ../sp_genesis_builder/trait.GenesisBuilder.html#method.get_preset -use std::{fs, path::PathBuf}; - use clap::{Parser, Subcommand}; -use sc_chain_spec::{ChainType, GenericChainSpec, GenesisConfigBuilderRuntimeCaller}; +use sc_chain_spec::{ + json_patch, set_code_substitute_in_json_chain_spec, update_code_in_json_chain_spec, ChainType, + GenericChainSpec, GenesisConfigBuilderRuntimeCaller, +}; +use serde::{Deserialize, Serialize}; use serde_json::Value; +use std::{ + borrow::Cow, + fs, + path::{Path, PathBuf}, +}; /// A utility to easily create a chain spec definition. #[derive(Debug, Parser)] @@ -158,9 +165,15 @@ pub struct CreateCmd { /// The chain type. #[arg(value_enum, short = 't', default_value = "live")] chain_type: ChainType, + /// The para ID for your chain. + #[arg(long, value_enum, short = 'p', requires = "relay_chain")] + pub para_id: Option, + /// The relay chain you wish to connect to. + #[arg(long, value_enum, short = 'c', requires = "para_id")] + pub relay_chain: Option, /// The path to runtime wasm blob. - #[arg(long, short)] - runtime_wasm_path: PathBuf, + #[arg(long, short, alias = "runtime-wasm-path")] + runtime: PathBuf, /// Export chainspec as raw storage. #[arg(long, short = 's')] raw_storage: bool, @@ -170,6 +183,10 @@ pub struct CreateCmd { verify: bool, #[command(subcommand)] action: GenesisBuildAction, + + /// Allows to provide the runtime code blob, instead of reading it from the provided file path. + #[clap(skip)] + code: Option>, } #[derive(Subcommand, Debug, Clone)] @@ -220,7 +237,8 @@ pub struct UpdateCodeCmd { /// Please note that the file will not be updated in-place. pub input_chain_spec: PathBuf, /// The path to new runtime wasm blob to be stored into chain-spec. - pub runtime_wasm_path: PathBuf, + #[arg(alias = "runtime-wasm-path")] + pub runtime: PathBuf, } /// Add a code substitute in the chain spec. @@ -237,7 +255,8 @@ pub struct AddCodeSubstituteCmd { /// Chain spec to be updated. pub input_chain_spec: PathBuf, /// New runtime wasm blob that should replace the existing code. - pub runtime_wasm_path: PathBuf, + #[arg(alias = "runtime-wasm-path")] + pub runtime: PathBuf, /// The block height at which the code should be substituted. pub block_height: u64, } @@ -253,16 +272,16 @@ pub struct ConvertToRawCmd { #[derive(Parser, Debug, Clone)] pub struct ListPresetsCmd { /// The path to runtime wasm blob. - #[arg(long, short)] - pub runtime_wasm_path: PathBuf, + #[arg(long, short, alias = "runtime-wasm-path")] + pub runtime: PathBuf, } /// Displays given preset #[derive(Parser, Debug, Clone)] pub struct DisplayPresetCmd { /// The path to runtime wasm blob. - #[arg(long, short)] - pub runtime_wasm_path: PathBuf, + #[arg(long, short, alias = "runtime-wasm-path")] + pub runtime: PathBuf, /// Preset to be displayed. If none is given default will be displayed. #[arg(long, short)] pub preset_name: Option, @@ -279,18 +298,146 @@ pub struct VerifyCmd { pub input_chain_spec: PathBuf, } -/// Processes `CreateCmd` and returns JSON version of `ChainSpec`. -pub fn generate_chain_spec_for_runtime(cmd: &CreateCmd) -> Result { - let code = fs::read(cmd.runtime_wasm_path.as_path()) - .map_err(|e| format!("wasm blob shall be readable {e}"))?; +#[derive(Deserialize, Serialize, Clone)] +pub struct ParachainExtension { + /// The relay chain of the Parachain. + pub relay_chain: String, + /// The id of the Parachain. + pub para_id: u32, +} - let chain_type = &cmd.chain_type; +type ChainSpec = GenericChainSpec<()>; - let builder = GenericChainSpec::<()>::builder(&code[..], Default::default()) - .with_name(&cmd.chain_name[..]) - .with_id(&cmd.chain_id[..]) - .with_chain_type(chain_type.clone()); +impl ChainSpecBuilder { + /// Executes the internal command. + pub fn run(self) -> Result<(), String> { + let chain_spec_path = self.chain_spec_path.to_path_buf(); + + match self.command { + ChainSpecBuilderCmd::Create(cmd) => { + let chain_spec_json = generate_chain_spec_for_runtime(&cmd)?; + fs::write(chain_spec_path, chain_spec_json).map_err(|err| err.to_string())?; + }, + ChainSpecBuilderCmd::UpdateCode(UpdateCodeCmd { + ref input_chain_spec, + ref runtime, + }) => { + let mut chain_spec_json = extract_chain_spec_json(input_chain_spec.as_path())?; + + update_code_in_json_chain_spec( + &mut chain_spec_json, + &fs::read(runtime.as_path()) + .map_err(|e| format!("Wasm blob file could not be read: {e}"))?[..], + ); + + let chain_spec_json = serde_json::to_string_pretty(&chain_spec_json) + .map_err(|e| format!("to pretty failed: {e}"))?; + fs::write(chain_spec_path, chain_spec_json).map_err(|err| err.to_string())?; + }, + ChainSpecBuilderCmd::AddCodeSubstitute(AddCodeSubstituteCmd { + ref input_chain_spec, + ref runtime, + block_height, + }) => { + let mut chain_spec_json = extract_chain_spec_json(input_chain_spec.as_path())?; + + set_code_substitute_in_json_chain_spec( + &mut chain_spec_json, + &fs::read(runtime.as_path()) + .map_err(|e| format!("Wasm blob file could not be read: {e}"))?[..], + block_height, + ); + let chain_spec_json = serde_json::to_string_pretty(&chain_spec_json) + .map_err(|e| format!("to pretty failed: {e}"))?; + fs::write(chain_spec_path, chain_spec_json).map_err(|err| err.to_string())?; + }, + ChainSpecBuilderCmd::ConvertToRaw(ConvertToRawCmd { ref input_chain_spec }) => { + let chain_spec = ChainSpec::from_json_file(input_chain_spec.clone())?; + + let mut genesis_json = + serde_json::from_str::(&chain_spec.as_json(true)?) + .map_err(|e| format!("Conversion to json failed: {e}"))?; + + // We want to extract only raw genesis ("genesis::raw" key), and apply it as a patch + // for the original json file. However, the file also contains original plain + // genesis ("genesis::runtimeGenesis") so set it to null so the patch will erase it. + genesis_json.as_object_mut().map(|map| { + map.retain(|key, _| key == "genesis"); + map.get_mut("genesis").map(|genesis| { + genesis.as_object_mut().map(|genesis_map| { + genesis_map + .insert("runtimeGenesis".to_string(), serde_json::Value::Null); + }); + }); + }); + + let mut org_chain_spec_json = extract_chain_spec_json(input_chain_spec.as_path())?; + json_patch::merge(&mut org_chain_spec_json, genesis_json); + + let chain_spec_json = serde_json::to_string_pretty(&org_chain_spec_json) + .map_err(|e| format!("Conversion to pretty failed: {e}"))?; + fs::write(chain_spec_path, chain_spec_json).map_err(|err| err.to_string())?; + }, + ChainSpecBuilderCmd::Verify(VerifyCmd { ref input_chain_spec }) => { + let chain_spec = ChainSpec::from_json_file(input_chain_spec.clone())?; + let _ = serde_json::from_str::(&chain_spec.as_json(true)?) + .map_err(|e| format!("Conversion to json failed: {e}"))?; + }, + ChainSpecBuilderCmd::ListPresets(ListPresetsCmd { runtime }) => { + let code = fs::read(runtime.as_path()) + .map_err(|e| format!("wasm blob shall be readable {e}"))?; + let caller: GenesisConfigBuilderRuntimeCaller = + GenesisConfigBuilderRuntimeCaller::new(&code[..]); + let presets = caller + .preset_names() + .map_err(|e| format!("getting default config from runtime should work: {e}"))?; + let presets: Vec = presets + .into_iter() + .map(|preset| { + String::from( + TryInto::<&str>::try_into(&preset) + .unwrap_or_else(|_| "cannot display preset id") + .to_string(), + ) + }) + .collect(); + println!("{}", serde_json::json!({"presets":presets}).to_string()); + }, + ChainSpecBuilderCmd::DisplayPreset(DisplayPresetCmd { runtime, preset_name }) => { + let code = fs::read(runtime.as_path()) + .map_err(|e| format!("wasm blob shall be readable {e}"))?; + let caller: GenesisConfigBuilderRuntimeCaller = + GenesisConfigBuilderRuntimeCaller::new(&code[..]); + let preset = caller + .get_named_preset(preset_name.as_ref()) + .map_err(|e| format!("getting default config from runtime should work: {e}"))?; + println!("{preset}"); + }, + } + Ok(()) + } + + /// Sets the code used by [`CreateCmd`] + /// + /// The file pointed by `CreateCmd::runtime` field will not be read. Provided blob will used + /// instead for chain spec generation. + pub fn set_create_cmd_runtime_code(&mut self, code: Cow<'static, [u8]>) { + match &mut self.command { + ChainSpecBuilderCmd::Create(cmd) => { + cmd.code = Some(code); + }, + _ => { + panic!("Overwriting code blob is only supported for CreateCmd"); + }, + }; + } +} +fn process_action( + cmd: &CreateCmd, + code: &[u8], + builder: sc_chain_spec::ChainSpecBuilder, +) -> Result { let builder = match cmd.action { GenesisBuildAction::NamedPreset(NamedPresetCmd { ref preset_name }) => builder.with_genesis_config_preset_name(&preset_name), @@ -310,7 +457,7 @@ pub fn generate_chain_spec_for_runtime(cmd: &CreateCmd) -> Result { let caller: GenesisConfigBuilderRuntimeCaller = - GenesisConfigBuilderRuntimeCaller::new(&code[..]); + GenesisConfigBuilderRuntimeCaller::new(&code); let default_config = caller .get_default_config() .map_err(|e| format!("getting default config from runtime should work: {e}"))?; @@ -330,3 +477,59 @@ pub fn generate_chain_spec_for_runtime(cmd: &CreateCmd) -> Result chain_spec.as_json(false), } } + +impl CreateCmd { + /// Returns the associated runtime code. + /// + /// If the code blob was previously set, returns it. Otherwise reads the file. + fn get_runtime_code(&self) -> Result, String> { + Ok(if let Some(code) = self.code.clone() { + code + } else { + fs::read(self.runtime.as_path()) + .map_err(|e| format!("wasm blob shall be readable {e}"))? + .into() + }) + } +} + +/// Processes `CreateCmd` and returns string represenataion of JSON version of `ChainSpec`. +pub fn generate_chain_spec_for_runtime(cmd: &CreateCmd) -> Result { + let code = cmd.get_runtime_code()?; + + let chain_type = &cmd.chain_type; + + let mut properties = sc_chain_spec::Properties::new(); + properties.insert("tokenSymbol".into(), "UNIT".into()); + properties.insert("tokenDecimals".into(), 12.into()); + + let builder = ChainSpec::builder(&code[..], Default::default()) + .with_name(&cmd.chain_name[..]) + .with_id(&cmd.chain_id[..]) + .with_properties(properties) + .with_chain_type(chain_type.clone()); + + let chain_spec_json_string = process_action(&cmd, &code[..], builder)?; + + if let (Some(para_id), Some(ref relay_chain)) = (cmd.para_id, &cmd.relay_chain) { + let parachain_properties = serde_json::json!({ + "relay_chain": relay_chain, + "para_id": para_id, + }); + let mut chain_spec_json_blob = serde_json::from_str(chain_spec_json_string.as_str()) + .map_err(|e| format!("deserialization a json failed {e}"))?; + json_patch::merge(&mut chain_spec_json_blob, parachain_properties); + Ok(serde_json::to_string_pretty(&chain_spec_json_blob) + .map_err(|e| format!("to pretty failed: {e}"))?) + } else { + Ok(chain_spec_json_string) + } +} + +/// Extract any chain spec and convert it to JSON +fn extract_chain_spec_json(input_chain_spec: &Path) -> Result { + let chain_spec = &fs::read(input_chain_spec) + .map_err(|e| format!("Provided chain spec could not be read: {e}"))?; + + serde_json::from_slice(&chain_spec).map_err(|e| format!("Conversion to json failed: {e}")) +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/add_code_substitute.json b/substrate/bin/utils/chain-spec-builder/tests/expected/add_code_substitute.json new file mode 100644 index 0000000000000000000000000000000000000000..b957b09f5646b15028bd9790db2c09b85ba94d0c --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/add_code_substitute.json @@ -0,0 +1,42 @@ +{ + "bootNodes": [], + "chainType": "Live", + "codeSubstitutes": { + "100": "0x040506" + }, + "custom_field": "custom_value", + "genesis": { + "runtimeGenesis": { + "code": "0x010203", + "config": { + "babe": { + "authorities": [], + "epochConfig": { + "allowed_slots": "PrimaryAndSecondaryVRFSlots", + "c": [ + 1, + 4 + ] + } + }, + "balances": { + "balances": [] + }, + "substrateTest": { + "authorities": [] + }, + "system": {} + } + } + }, + "id": "custom", + "name": "Custom", + "para_id": 10101, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "protocolId": null, + "relay_chain": "rococo-local", + "telemetryEndpoints": null +} \ No newline at end of file diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/convert_to_raw.json b/substrate/bin/utils/chain-spec-builder/tests/expected/convert_to_raw.json new file mode 100644 index 0000000000000000000000000000000000000000..5b1b4e2f04cfe0aeea8383bd541bebbdc2705e0e --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/convert_to_raw.json @@ -0,0 +1,38 @@ +{ + "name": "Custom", + "id": "custom", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "relay_chain": "rococo-local", + "para_id": 10101, + "custom_field": "custom_value", + "codeSubstitutes": {}, + "genesis": { + "raw": { + "childrenDefault": {}, + "top": { + "0x00771836bebdd29870ff246d305c578c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x00771836bebdd29870ff246d305c578c5e0621c4869aa60c02be9adcc98a0d1d": "0x00", + "0x1cb6f36e027abb2091cfb5110ab5087f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x1cb6f36e027abb2091cfb5110ab5087f66e8f035c8adbe7f1547b43c51e6f8a4": "0x00000000", + "0x1cb6f36e027abb2091cfb5110ab5087fdc6b171b77304263c292cc3ea5ed31ef": "0x0100000000000000040000000000000002", + "0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746bb1bdbcacd6ac9340000000000000000": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x0000", + "0x3a636f6465": "0x010203", + "0x3a65787472696e7369635f696e646578": "0x00000000", + "0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x0000000000000000" + } + } + } +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/create_default.json b/substrate/bin/utils/chain-spec-builder/tests/expected/create_default.json new file mode 100644 index 0000000000000000000000000000000000000000..ac67aef93345fdf6738b2b6fde3e4ab6b7df1a86 --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/create_default.json @@ -0,0 +1,37 @@ +{ + "name": "Custom", + "id": "custom", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "codeSubstitutes": {}, + "genesis": { + "runtimeGenesis": { + "code": "0x010203", + "config": { + "babe": { + "authorities": [], + "epochConfig": { + "allowed_slots": "PrimaryAndSecondaryVRFSlots", + "c": [ + 1, + 4 + ] + } + }, + "balances": { + "balances": [] + }, + "substrateTest": { + "authorities": [] + }, + "system": {} + } + } + } +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/create_parachain.json b/substrate/bin/utils/chain-spec-builder/tests/expected/create_parachain.json new file mode 100644 index 0000000000000000000000000000000000000000..7106b4b50dc593a0efcb77edc74d67acd5121b92 --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/create_parachain.json @@ -0,0 +1,39 @@ +{ + "name": "test_chain", + "id": "100", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "relay_chain": "rococo-local", + "para_id": 10101, + "codeSubstitutes": {}, + "genesis": { + "runtimeGenesis": { + "code": "0x010203", + "config": { + "babe": { + "authorities": [], + "epochConfig": { + "allowed_slots": "PrimaryAndSecondaryVRFSlots", + "c": [ + 1, + 4 + ] + } + }, + "balances": { + "balances": [] + }, + "substrateTest": { + "authorities": [] + }, + "system": {} + } + } + } +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/create_raw_storage.json b/substrate/bin/utils/chain-spec-builder/tests/expected/create_raw_storage.json new file mode 100644 index 0000000000000000000000000000000000000000..0501d6cbe45b645e9b34bec58537063d264b3a67 --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/create_raw_storage.json @@ -0,0 +1,38 @@ +{ + "name": "Custom", + "id": "custom", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "codeSubstitutes": {}, + "genesis": { + "raw": { + "top": { + "0x00771836bebdd29870ff246d305c578c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x00771836bebdd29870ff246d305c578c5e0621c4869aa60c02be9adcc98a0d1d": "0x0cd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d1cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c186e1bafbb1430668c95d89b77217a402a74f64c3e103137b69e95e4b6e06b1e", + "0x1cb6f36e027abb2091cfb5110ab5087f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x1cb6f36e027abb2091cfb5110ab5087f66e8f035c8adbe7f1547b43c51e6f8a4": "0x00000000", + "0x1cb6f36e027abb2091cfb5110ab5087fdc6b171b77304263c292cc3ea5ed31ef": "0x0100000000000000040000000000000002", + "0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746bb1bdbcacd6ac9340000000000000000": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da92c2a60ec6dd16cd8ab911865ecf7555b186e1bafbb1430668c95d89b77217a402a74f64c3e103137b69e95e4b6e06b1e": "0x00000000000000000000000001000000000000000080e03779c311000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da94f9aea1afa791265fae359272badc1cf8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48": "0x00000000000000000000000001000000000000000080c6a47e8d03000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9b0edae20838083f2cde1c4080db8cf8090b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22": "0x00000000000000000000000001000000000000000080c6a47e8d03000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x0000", + "0x3a636f6465": "0x010203", + "0x3a65787472696e7369635f696e646578": "0x00000000", + "0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x00806d8176de1800" + }, + "childrenDefault": {} + } + } +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/create_with_full.json b/substrate/bin/utils/chain-spec-builder/tests/expected/create_with_full.json new file mode 100644 index 0000000000000000000000000000000000000000..6d127b6c0aca09fce0924291b5a5615b302b443f --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/create_with_full.json @@ -0,0 +1,58 @@ +{ + "name": "Custom", + "id": "custom", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "codeSubstitutes": {}, + "genesis": { + "runtimeGenesis": { + "code": "0x010203", + "config": { + "babe": { + "authorities": [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", + "5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b" + ], + "epochConfig": { + "allowed_slots": "PrimaryAndSecondaryVRFSlots", + "c": [ + 2, + 4 + ] + } + }, + "balances": { + "balances": [ + [ + "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + 2000000000000000 + ], + [ + "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", + 2000000000000000 + ], + [ + "5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b", + 5000000000000000 + ] + ] + }, + "substrateTest": { + "authorities": [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", + "5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b" + ] + }, + "system": {} + } + } + } +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/create_with_named_preset.json b/substrate/bin/utils/chain-spec-builder/tests/expected/create_with_named_preset.json new file mode 100644 index 0000000000000000000000000000000000000000..2bf84281c59eb37e05677ed482035133918cf91d --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/create_with_named_preset.json @@ -0,0 +1,38 @@ +{ + "name": "Custom", + "id": "custom", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "codeSubstitutes": {}, + "genesis": { + "runtimeGenesis": { + "code": "0x010203", + "patch": { + "balances": { + "balances": [ + [ + "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + 1000000000000000 + ], + [ + "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", + 1000000000000000 + ] + ] + }, + "substrateTest": { + "authorities": [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL" + ] + } + } + } + } +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/create_with_params.json b/substrate/bin/utils/chain-spec-builder/tests/expected/create_with_params.json new file mode 100644 index 0000000000000000000000000000000000000000..5aedd5b5c18baab559757196295bd05dd586af9e --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/create_with_params.json @@ -0,0 +1,37 @@ +{ + "name": "test_chain", + "id": "100", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "codeSubstitutes": {}, + "genesis": { + "runtimeGenesis": { + "code": "0x010203", + "config": { + "babe": { + "authorities": [], + "epochConfig": { + "allowed_slots": "PrimaryAndSecondaryVRFSlots", + "c": [ + 1, + 4 + ] + } + }, + "balances": { + "balances": [] + }, + "substrateTest": { + "authorities": [] + }, + "system": {} + } + } + } +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/create_with_patch.json b/substrate/bin/utils/chain-spec-builder/tests/expected/create_with_patch.json new file mode 100644 index 0000000000000000000000000000000000000000..f98be3d7cfbe807f019893bb433cbe98a3a4ff41 --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/create_with_patch.json @@ -0,0 +1,43 @@ +{ + "name": "Custom", + "id": "custom", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "codeSubstitutes": {}, + "genesis": { + "runtimeGenesis": { + "code": "0x010203", + "patch": { + "balances": { + "balances": [ + [ + "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + 1000000000000000 + ], + [ + "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", + 1000000000000000 + ], + [ + "5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b", + 5000000000000000 + ] + ] + }, + "substrateTest": { + "authorities": [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", + "5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b" + ] + } + } + } + } +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/update_code.json b/substrate/bin/utils/chain-spec-builder/tests/expected/update_code.json new file mode 100644 index 0000000000000000000000000000000000000000..dde561a594ffefde6e068af58e88ad775ccd8af2 --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/update_code.json @@ -0,0 +1,40 @@ +{ + "name": "Custom", + "id": "custom", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "relay_chain": "rococo-local", + "para_id": 10101, + "custom_field": "custom_value", + "codeSubstitutes": {}, + "genesis": { + "runtimeGenesis": { + "code": "0x040506", + "config": { + "babe": { + "authorities": [], + "epochConfig": { + "allowed_slots": "PrimaryAndSecondaryVRFSlots", + "c": [ + 1, + 4 + ] + } + }, + "balances": { + "balances": [] + }, + "substrateTest": { + "authorities": [] + }, + "system": {} + } + } + } +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/update_code_raw.json b/substrate/bin/utils/chain-spec-builder/tests/expected/update_code_raw.json new file mode 100644 index 0000000000000000000000000000000000000000..d8c558a0ccbbc20b7471631a938447b1650daf22 --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/update_code_raw.json @@ -0,0 +1,38 @@ +{ + "name": "Custom", + "id": "custom", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "codeSubstitutes": {}, + "genesis": { + "raw": { + "top": { + "0x00771836bebdd29870ff246d305c578c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x00771836bebdd29870ff246d305c578c5e0621c4869aa60c02be9adcc98a0d1d": "0x0cd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d1cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c186e1bafbb1430668c95d89b77217a402a74f64c3e103137b69e95e4b6e06b1e", + "0x1cb6f36e027abb2091cfb5110ab5087f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x1cb6f36e027abb2091cfb5110ab5087f66e8f035c8adbe7f1547b43c51e6f8a4": "0x00000000", + "0x1cb6f36e027abb2091cfb5110ab5087fdc6b171b77304263c292cc3ea5ed31ef": "0x0100000000000000040000000000000002", + "0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746bb1bdbcacd6ac9340000000000000000": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da92c2a60ec6dd16cd8ab911865ecf7555b186e1bafbb1430668c95d89b77217a402a74f64c3e103137b69e95e4b6e06b1e": "0x00000000000000000000000001000000000000000080e03779c311000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da94f9aea1afa791265fae359272badc1cf8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48": "0x00000000000000000000000001000000000000000080c6a47e8d03000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9b0edae20838083f2cde1c4080db8cf8090b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22": "0x00000000000000000000000001000000000000000080c6a47e8d03000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x0000", + "0x3a636f6465": "0x040506", + "0x3a65787472696e7369635f696e646578": "0x00000000", + "0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x00806d8176de1800" + }, + "childrenDefault": {} + } + } +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/input/chain_spec_conversion_test.json b/substrate/bin/utils/chain-spec-builder/tests/input/chain_spec_conversion_test.json new file mode 100644 index 0000000000000000000000000000000000000000..6a390c0d38b1cc27500294287c03f265cd6ca118 --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/input/chain_spec_conversion_test.json @@ -0,0 +1,40 @@ +{ + "name": "Custom", + "id": "custom", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "relay_chain": "rococo-local", + "para_id": 10101, + "custom_field": "custom_value", + "codeSubstitutes": {}, + "genesis": { + "runtimeGenesis": { + "code": "0x0061736d0100000001b4033b60017f0060017f017f60037f7f7f017f60027f7f017f60027f7f0060037f7f7f0060047f7f7f7f0060057f7f7f7f7f0060000060047f7f7f7f017f60067f7f7f7f7f7f0060087f7f7f7f7f7f7f7f006000017f60047f7e7e7e0060027e7e0060017e0060027e7e017e60017e017e60037e7e7f017e60017f017e60017e017f60027f7e017f60037f7f7e017e60037f7e7f017f60037f7e7e0060047e7e7e7f017e60037e7e7e0060027e7f017f60047f7f7e7e0060037e7f7f0060047e7e7f7f017f60067f7f7f7f7f7f017f60057f7f7f7f7f017f600f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f017f60077f7f7f7f7f7f7f0060047f7c7f7f017f60037f7c7f017f60037e7f7f017f60077f7f7f7f7f7f7f017f60097f7f7f7f7f7f7e7e7e0060067f7f7f7e7e7f0060097f7f7f7e7e7f7f7f7f0060027e7f0060027c7f017f60067f7f7e7e7f7f0060027f7e00600a7f7f7f7f7f7f7f7f7f7f006000017e60067f7f7e7f7f7f0060057f7f7f7e7f0060047f7f7f7e0060027f7f017e60057f7f7e7f7f0060037f7f7e0060027f7e017e600b7f7f7f7f7f7f7f7f7f7f7f0060037f7f7c0060057f7e7e7e7e0060047f7e7e7f0002800d2a03656e76066d656d6f727902001203656e761c6578745f73746f726167655f617070656e645f76657273696f6e5f31000e03656e761b6578745f73746f726167655f636c6561725f76657273696f6e5f31000f03656e76226578745f73746f726167655f636c6561725f7072656669785f76657273696f6e5f32001003656e76286578745f73746f726167655f636f6d6d69745f7472616e73616374696f6e5f76657273696f6e5f31000803656e76196578745f73746f726167655f6765745f76657273696f6e5f31001103656e761e6578745f73746f726167655f6e6578745f6b65795f76657273696f6e5f31001103656e761a6578745f73746f726167655f726561645f76657273696f6e5f31001203656e762a6578745f73746f726167655f726f6c6c6261636b5f7472616e73616374696f6e5f76657273696f6e5f31000803656e761a6578745f73746f726167655f726f6f745f76657273696f6e5f32001303656e76196578745f73746f726167655f7365745f76657273696f6e5f31000e03656e76276578745f73746f726167655f73746172745f7472616e73616374696f6e5f76657273696f6e5f31000803656e76206578745f68617368696e675f626c616b65325f3132385f76657273696f6e5f31001403656e76206578745f68617368696e675f626c616b65325f3235365f76657273696f6e5f31001403656e761e6578745f68617368696e675f74776f785f3132385f76657273696f6e5f31001403656e761d6578745f68617368696e675f74776f785f36345f76657273696f6e5f31001403656e76226578745f6f6666636861696e5f696e6465785f636c6561725f76657273696f6e5f31000f03656e76206578745f6f6666636861696e5f696e6465785f7365745f76657273696f6e5f31000e03656e76236578745f63727970746f5f65636473615f67656e65726174655f76657273696f6e5f31001503656e76266578745f63727970746f5f65636473615f7075626c69635f6b6579735f76657273696f6e5f31001303656e761f6578745f63727970746f5f65636473615f7369676e5f76657273696f6e5f31001603656e76216578745f63727970746f5f65636473615f7665726966795f76657273696f6e5f32001703656e76256578745f63727970746f5f656432353531395f67656e65726174655f76657273696f6e5f31001503656e76286578745f63727970746f5f656432353531395f7075626c69635f6b6579735f76657273696f6e5f31001303656e76216578745f63727970746f5f656432353531395f7369676e5f76657273696f6e5f31001603656e76236578745f63727970746f5f656432353531395f7665726966795f76657273696f6e5f31001703656e76256578745f63727970746f5f737232353531395f67656e65726174655f76657273696f6e5f31001503656e76286578745f63727970746f5f737232353531395f7075626c69635f6b6579735f76657273696f6e5f31001303656e76216578745f63727970746f5f737232353531395f7369676e5f76657273696f6e5f31001603656e76236578745f63727970746f5f737232353531395f7665726966795f76657273696f6e5f32001703656e76296578745f6f6666636861696e5f7375626d69745f7472616e73616374696f6e5f76657273696f6e5f31001103656e76256578745f7472616e73616374696f6e5f696e6465785f696e6465785f76657273696f6e5f31000503656e76196578745f6c6f6767696e675f6c6f675f76657273696f6e5f31001803656e761f6578745f6c6f6767696e675f6d61785f6c6576656c5f76657273696f6e5f31000c03656e761c6578745f616c6c6f6361746f725f667265655f76657273696f6e5f31000003656e761e6578745f616c6c6f6361746f725f6d616c6c6f635f76657273696f6e5f31000103656e76286578745f64656661756c745f6368696c645f73746f726167655f726561645f76657273696f6e5f31001903656e76276578745f64656661756c745f6368696c645f73746f726167655f7365745f76657273696f6e5f31001a03656e762a6578745f747269655f626c616b65325f3235365f6f7264657265645f726f6f745f76657273696f6e5f32001b03656e761c6578745f6d6973635f7072696e745f6865785f76657273696f6e5f31000f03656e761d6578745f6d6973635f7072696e745f757466385f76657273696f6e5f31000f03656e76226578745f6d6973635f72756e74696d655f76657273696f6e5f76657273696f6e5f31001103e40de20d0403000203080506040400000303040402031c0a00030406060105060a060605060605010606010508061d1e0300050302031f20020302210302030303030304030302222323240303070100000405050507220503250303030300030303000007020320020203030607030505050707260103030607270005050405040505050404040405040505040404040304040500040404040404040406050504040404000004040428050404040404040404010505040504000029050505010404040400060406040404050505050000000003030c00070004040404000000040400030100010004040400000000060404050505060604000304000703000604050707060404000000030605040205030004040506002a2b2c07040404040404040404060405050504040407060000000703220303031c03050500000000000000030007040303030300030303030203020000030303020c03030301010203020c0604000504030401060405030003020303030400000604050000000000000000000404040604040505050004040006040505000604040505050000040404040000000000000000000000000000060606060000000604000000000604060405050505000000000400000005000404040000000000000006040505050604042d2d2d040003030300040506010300040504070805050a080406080505050504060504070905040709050407090104020405070c040404040400010b0a05040405040404040000000405040404060606060606060606060606060404040505050505000000040404040004000003030300000304000004040500000000000000000000000607060405060707000403030300030003000605020605040000000404040604040505050400060000000000060400030000040607060707050505060505050505050505050505050505050506050505220a220a040000040404000000000307060505070605050505050506070706070704040a0a040606222206222206060705070707070700000a0a06060605040404040404040404040404040404040505050505050509030304040404040404040404040404040404040404040404040404050404040403030004092d09090209090909090300060000000000000000000000000000000000000000000005050004040404040400040404050407030305050404040400000000070404050504040404040404040404000000000000000004040400000304000000061400000000030404010506000705050504050400050400000406050f2d03030303030303000000030506040404060a0004030000000001050600040400000004040504050404042d042a040404040400030f000400070602030204050606050505000404040004040400040004040000000003030a000003030305031313130405030505050504040000030404000000000000000000000004040003050304030403060501000a0304050303040504030504040404050000000c0000000000000000080404040000040004040404040405000303030303030303030303030303030303030303030303000000000000070707070700070303030303040300040404040502040004030303030303030303030000000000000403030b0b2e2e06070506020404040406010911040000040400062f0000000804050800030003040500000000000500030504030404040004000000040000010500000000300008060506050508040308040000000401040000000404000000000000030d03000000050304040f050404040404040404000606040a030302053131313105320101070502030103010106070305000000000301090203090401040404040401040404040a200a0a0a220606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606060606040305050505050505053333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333330400000000000000000000000334353130360100000503040401040104000000040404040404040404030303030b0404040404000537040a07040404050607070707060404000400000000000000040404060404040606050707050506060203030400040406020400040f050001040008000506000004000600070406050504040403030000000306050505040206020404030000000000000000000000040138063535051c1c03030303030303030000000703060300010406151c3300040103332d040305050304000704050304040303040404090400030403050303030300000800080306050500000304393a39023902020202023a3a390239020407017001c404c4040619037f01418080c0000b7f004198a4c6000b7f0041a0a4c6000b078b0c33195f5f696e6469726563745f66756e6374696f6e5f7461626c6501000c436f72655f76657273696f6e00de0b12436f72655f657865637574655f626c6f636b00df0b15436f72655f696e697469616c697a655f626c6f636b00e00b114d657461646174615f6d6574616461746100e10b1c4d657461646174615f6d657461646174615f61745f76657273696f6e00e20b1a4d657461646174615f6d657461646174615f76657273696f6e7300e30b2b5461676765645472616e73616374696f6e51756575655f76616c69646174655f7472616e73616374696f6e00e40b1c426c6f636b4275696c6465725f6170706c795f65787472696e73696300e50b1b426c6f636b4275696c6465725f66696e616c697a655f626c6f636b00e60b20426c6f636b4275696c6465725f696e686572656e745f65787472696e7369637300e70b1c426c6f636b4275696c6465725f636865636b5f696e686572656e747300e80b1d4163636f756e744e6f6e63654170695f6163636f756e745f6e6f6e636500e90b12546573744150495f62616c616e63655f6f6600ea0b19546573744150495f62656e63686d61726b5f6164645f6f6e6500eb0b20546573744150495f62656e63686d61726b5f766563746f725f6164645f6f6e6500ec0b22546573744150495f66756e6374696f6e5f7369676e61747572655f6368616e67656400ed0b10546573744150495f7573655f7472696500ee0b1f546573744150495f62656e63686d61726b5f696e6469726563745f63616c6c00ef0b1d546573744150495f62656e63686d61726b5f6469726563745f63616c6c00f00b19546573744150495f7665635f776974685f636170616369747900f10b18546573744150495f6765745f626c6f636b5f6e756d62657200f20b1b546573744150495f746573745f656432353531395f63727970746f00f30b1b546573744150495f746573745f737232353531395f63727970746f00f40b19546573744150495f746573745f65636473615f63727970746f00f50b14546573744150495f746573745f73746f7261676500f60b14546573744150495f746573745f7769746e65737300f70b1f546573744150495f746573745f6d756c7469706c655f617267756d656e747300f80b14546573744150495f646f5f74726163655f6c6f6700f90b16546573744150495f7665726966795f6564323535313900fa0b17546573744150495f77726974655f6b65795f76616c756500fb0b15417572614170695f736c6f745f6475726174696f6e00fc0b13417572614170695f617574686f72697469657300fd0b15426162654170695f636f6e66696775726174696f6e00fe0b1b426162654170695f63757272656e745f65706f63685f737461727400ff0b15426162654170695f63757272656e745f65706f636800800c12426162654170695f6e6578745f65706f636800810c35426162654170695f7375626d69745f7265706f72745f65717569766f636174696f6e5f756e7369676e65645f65787472696e73696300820c24426162654170695f67656e65726174655f6b65795f6f776e6572736869705f70726f6f6600830c214f6666636861696e576f726b65724170695f6f6666636861696e5f776f726b657200840c2153657373696f6e4b6579735f67656e65726174655f73657373696f6e5f6b65797300850c1f53657373696f6e4b6579735f6465636f64655f73657373696f6e5f6b65797300860c1e4772616e6470614170695f6772616e6470615f617574686f72697469657300870c194772616e6470614170695f63757272656e745f7365745f696400880c384772616e6470614170695f7375626d69745f7265706f72745f65717569766f636174696f6e5f756e7369676e65645f65787472696e73696300890c274772616e6470614170695f67656e65726174655f6b65795f6f776e6572736869705f70726f6f66008a0c1a47656e657369734275696c6465725f6275696c645f7374617465008b0c1947656e657369734275696c6465725f6765745f707265736574008c0c1b47656e657369734275696c6465725f7072657365745f6e616d6573008d0c0a5f5f646174615f656e6403010b5f5f686561705f62617365030209e208010041010bc3042b2c2d2a800134393a3335364041424344464c48494a4b3d3e4d4e4f509301568a018b018f0167655a75687466860185018301840163b905f501f401f901f801fd01da02e302900282029102840288028a028302fa049d029e029f02a00255a302a202e502af02bb02e102d502d902db02e802e7026182018101f10264eb0271ec02ed02ef02f402f502ee02fe02fa02970384039603f802fc02fb029203f702f902930395039403a403a503a303a603b903cb03e402cd03c003ce03c703c103df03c803cc03d803f003cf03d003d103da03d903ea03f203ee03ef03f703fa03f803f603f903e002fb0389048a048b048d048c04900491048f049204950496049704980499049a049b049c049d049e049f04a604a704a804a904aa04ab04ac04ad04ae04af04b004b104b704b804c004c104c204c304c404c504a004a104a204a304a404a504be04bf04b604b204fe04f104e80483058605ff04a7038405ee04e704f204f5049d05f304f404f604f704f804f904e9049b019c01960595059405980593059905ae05a305a205b105b205b005b305b705e308e605dd05fb05e105fa05e005fe05d106dd06e206e903db0ca307aa0aa407a707850d830db403e106b602a90ad70cc508e306ff068c0987079b039a03dd03a0039f03ae078d09d303900c940ca607ff02db07d907d707da078003d807d2078709d5079709e602850582058005e607a708820df6099202ef069908ea068509f0079d08f0069f08ee06a208e706e506a508e906b508a908ad08aa08900999098e09dd0a9809c308de03a203a103c708c908ca08b109cb08c20ae901cf08d108de08df08d508db08b805c208c608fd08f206f106f004a308eb06ab09a0098607a209af089e09ad09b0099d099209b80c8a0988098b098609c10996099b09ab08ba0cd50bb90c8f0994099f099c09a109aa099109ac099509a40aa50aa30ab807b707c108ac0cad0cab0cda0cd50c8705b405e606930ceb038105e707fc03e406d60ce907a107e006a105e307a507a207c408ec06e003d90cc903970c960cca03c603920c950ca4088805910ce8068f0cf406e807f409c808f509f709cc09ed0cc10dc709b409b509e30cec0ce60cea0ce80cc509be09d407d909da09d209d609d309d509d409c809f30cf40cc609d709d809e40c9b0de70ceb0ce90ccd099c0d9d0dc70ddb09c209c60ddc09b209c409c309f109e50793099a0a7f9a09f00c9302f2078c02ba058702e202fa09ae0ab30ab50aaf0ab20af302910de00abc09df0af2028909bd09bb09d006c009a608bf09cf06b70cb00ad107f601ae02ed06b103f506f306e009fb08d80cec03fa08d203f701a00db502900d9a0cd607c10cc00cc40cbe0cbf0cfe09ee0cad02a807ac02940d8b088a0889088c088e088d08930dd307b70db80dbe0d70b90dba0dbf0db50dbb0dbc0dbd0dae0db00db10db30db40db20dc20daf0db60d58c40dcb0dcc0dd20dd50dcd0dca0dd30dd40dce0dd60dcf0dd00dc80dd10dc90dd70dc30de50de60dec0def0df30dee0ded0dea0df80deb0df90d0afd9e49e20d0d002000200110b780808000000b12002000418080c08000200110d9808080000b220002402000280200450d00200028020441002802c0a3c68000118080808000000b0b4b01017f02402000280200200028020822036b20024f0d0020002003200210af80808000200028020821030b200028020420036a2001200210848e8080001a2000200320026a36020841000bf20201027f23808080800041106b220224808080800002400240024002402001418001490d002002410036020c2001418010490d0102402001418080044f0d0020022001413f71418001723a000e20022001410c7641e001723a000c20022001410676413f71418001723a000d410321010c030b20022001413f71418001723a000f20022001410676413f71418001723a000e20022001410c76413f71418001723a000d2002200141127641077141f001723a000c410421010c020b0240200028020822032000280200470d002000200310b180808000200028020821030b200028020420036a20013a00002000200028020841016a3602080c020b20022001413f71418001723a000d2002200141067641c001723a000c410221010b02402000280200200028020822036b20014f0d0020002003200110af80808000200028020821030b200028020420036a2002410c6a200110848e8080001a2000200320016a3602080b200241106a24808080800041000b4e01017f23808080800041206b2200248080808000200041146a42003702002000410136020c2000419c81c080003602082000419880c08000360210200041086a41a481c0800010f680808000000be20101027f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024108200241084b1b2202417f73411f7621040240024020010d00200341003602180c010b2003200136021c20034101360218200320002802043602140b200341086a20042002200341146a10b080808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000b860201017f024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d0020020d02410121010c030b20032802002103200241002802c8a3c68000118180808000002201450d0320012003200410848e8080001a200341002802c0a3c68000118080808000000c020b20020d00410121010c010b41002d00fca3c680001a200241002802c8a3c68000118180808000002201450d010b20002001360204200041086a2002360200200041003602000f0b20004101360204200041086a2002360200200041013602000f0b20004100360204200041086a2002360200200041013602000f0b20004100360204200041013602000be00101037f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014108200141084b1b2201417f73411f7621040240024020030d00200241003602180c010b2002200336021c20024101360218200220002802043602140b200241086a20042001200241146a10b080808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000b0d002001200010a980808000000b02000b220002402000280200450d00200028020441002802c0a3c68000118080808000000b0b2100200128021441b481c080004105200141186a28020028020c118280808000000b2100200128021441b981c08000410b200141186a28020028020c118280808000000bc60101017f23808080800041306b22022480808080002002200036020c024041002d00fda3c680000d002002411c6a420137020020024102360214200241e881c08000360210200241858080800036022c2002200241286a36021820022002410c6a360228200241106a410041e882c0800010f780808000000b2002411c6a420137020020024102360214200241e881c08000360210200241858080800036022c2002200241286a36021820022002410c6a360228200241106a41f882c0800010f680808000000bb80301077f23808080800041106b220224808080800002400240024002400240024020012802042203450d00200128020021042003410371210502400240200341044f0d0041002103410021060c010b2004411c6a21072003417c712108410021034100210603402007280200200741786a280200200741706a280200200741686a28020020036a6a6a6a2103200741206a21072008200641046a2206470d000b0b02402005450d00200641037420046a41046a21070340200728020020036a2103200741086a21072005417f6a22050d000b0b02402001410c6a280200450d0020034100480d012003411049200428020445710d01200341017421030b20030d010b41012107410021030c010b2003417f4c0d0141002d00fca3c680001a200341002802c8a3c68000118180808000002207450d020b2002410036020820022007360204200220033602002002418883c08000200110d980808000450d0241a083c0800041332002410f6a41d483c0800041d484c08000108981808000000b10ae80808000000b4101200310b280808000000b20002002290200370200200041086a200241086a280200360200200241106a2480808080000b4b01017f02402000280200200028020822036b20024f0d0020002003200210af80808000200028020821030b200028020420036a2001200210848e8080001a2000200320026a36020841000bf20201027f23808080800041106b220224808080800002400240024002402001418001490d002002410036020c2001418010490d0102402001418080044f0d0020022001413f71418001723a000e20022001410c7641e001723a000c20022001410676413f71418001723a000d410321010c030b20022001413f71418001723a000f20022001410676413f71418001723a000e20022001410c76413f71418001723a000d2002200141127641077141f001723a000c410421010c020b0240200028020822032000280200470d002000200310b180808000200028020821030b200028020420036a20013a00002000200028020841016a3602080c020b20022001413f71418001723a000d2002200141067641c001723a000c410221010b02402000280200200028020822036b20014f0d0020002003200110af80808000200028020821030b200028020420036a2002410c6a200110848e8080001a2000200320016a3602080b200241106a24808080800041000b892e07027e017f087e017f0a7e017f157e200020002903102204200129002022057c200041306a220629030022077c2208200129002822097c200820028542ebfa86dabfb5f6c11f85422089220a42abf0d3f4afeebcb73c7c220b200785422889220c7c220d200129006022027c2000290318220e200129003022087c200041386a220f29030022107c2211200129003822127c201120038542f9c2f89b91a3b3f0db0085422089220342f1edf4f8a5a7fda7a57f7c221120108542288922137c2214200385423089221520117c221620138542018922177c2218200129006822037c201820002903082219200129001022117c200041286a221a290300221b7c221c200129001822137c201c429fd8f9d9c291da829b7f85422089221c42bbceaaa6d8d0ebb3bb7f7c221d201b85422889221e7c221f201c85423089222085422089222120002903002222200129000022187c200029032022237c22242001290008221c7c200029034020248542d1859aeffacf9487d100854220892224428892f39dffccf984ea007c222520238542288922267c2227202485423089222820257c22257c2229201785422889222a7c222b200129004822177c201f200129005022247c200d200a85423089220d200b7c221f200c85420189220b7c220c2001290058220a7c200c202885422089220c20167c2216200b85422889220b7c2228200c85423089222c20167c2216200b85420189222d7c222e2001290078220b7c202e20142001290070220c7c202520268542018922147c2225200b7c2025200d85422089220d2020201d7c221d7c222020148542288922147c2225200d85423089222685422089222e20272001290040220d7c201d201e85420189221d7c221e20177c201e2015854220892215201f7c221e201d85422889221d7c221f2015854230892215201e7c221e7c2227202d85422889222d7c222f200a7c202520037c202b202185423089222120297c2225202a8542018922297c222a20087c202a201585422089221520167c221620298542288922297c222a201585423089221520167c221620298542018922297c222b20127c202b202820057c201e201d85420189221d7c221e200d7c201e202185422089221e202620207c22207c2221201d85422889221d7c2226201e85423089221e854220892228201f200c7c202020148542018922147c221f20247c201f202c85422089221f20257c222020148542288922147c2225201f85423089221f20207c22207c222b20298542288922297c222c20097c202620187c202f202e85423089222620277c2227202d85420189222d7c222e20117c202e201f85422089221f20167c2216202d85422889222d7c222e201f85423089221f20167c2216202d85420189222d7c222f20117c202f202a20097c202020148542018922147c222020137c20202026854220892220201e20217c221e7c222120148542288922147c2226202085423089222085422089222a2025201c7c201e201d85420189221d7c221e20027c201e201585422089221520277c221e201d85422889221d7c22252015854230892215201e7c221e7c2227202d85422889222d7c222f20127c2026200b7c202c2028854230892226202b7c222820298542018922297c222b20037c202b201585422089221520167c221620298542288922297c222b201585423089221520167c221620298542018922297c222c201c7c202c202e20027c201e201d85420189221d7c221e20187c201e202685422089221e202020217c22207c2221201d85422889221d7c2226201e85423089221e85422089222c2025200a7c202020148542018922147c2220200d7c2020201f85422089221f20287c222020148542288922147c2225201f85423089221f20207c22207c222820298542288922297c222e20037c202620137c202f202a85423089222620277c2227202d85420189222a7c222d20087c202d201f85422089221f20167c2216202a85422889222a7c222d201f85423089221f20167c2216202a85420189222a7c222f20027c202f202b20177c202020148542018922147c222020057c20202026854220892220201e20217c221e7c222120148542288922147c2226202085423089222085422089222b202520247c201e201d85420189221d7c221e200c7c201e201585422089221520277c221e201d85422889221d7c22252015854230892215201e7c221e7c2227202a85422889222a7c222f20057c2026200a7c202e202c85423089222620287c222820298542018922297c222c200c7c202c201585422089221520167c221620298542288922297c222c201585423089221520167c221620298542018922297c222e20187c202e202d20137c201e201d85420189221d7c221e201c7c201e202685422089221e202020217c22207c2221201d85422889221d7c2226201e85423089221e85422089222d202520127c202020148542018922147c222020177c2020201f85422089221f20287c222020148542288922147c2225201f85423089221f20207c22207c222820298542288922297c222e20117c202620097c202f202b85423089222620277c2227202a85420189222a7c222b20247c202b201f85422089221f20167c2216202a85422889222a7c222b201f85423089221f20167c2216202a85420189222a7c222f20057c202f202c200b7c202020148542018922147c2220200d7c20202026854220892220201e20217c221e7c222120148542288922147c2226202085423089222085422089222c202520117c201e201d85420189221d7c221e20087c201e201585422089221520277c221e201d85422889221d7c22252015854230892215201e7c221e7c2227202a85422889222a7c222f20087c202620247c202e202d85423089222620287c222820298542018922297c222d200b7c202d201585422089221520167c221620298542288922297c222d201585423089221520167c221620298542018922297c222e200d7c202e202b20097c201e201d85420189221d7c221e20127c201e202685422089221e202020217c22207c2221201d85422889221d7c2226201e85423089221e85422089222b202520177c202020148542018922147c222020187c2020201f85422089221f20287c222020148542288922147c2225201f85423089221f20207c22207c222820298542288922297c222e20187c2026200a7c202f202c85423089222620277c2227202a85420189222a7c222c20027c202c201f85422089221f20167c2216202a85422889222a7c222c201f85423089221f20167c2216202a85420189222a7c222f200a7c202f202d20137c202020148542018922147c222020037c20202026854220892220201e20217c221e7c222120148542288922147c2226202085423089222085422089222d2025200c7c201e201d85420189221d7c221e201c7c201e201585422089221520277c221e201d85422889221d7c22252015854230892215201e7c221e7c2227202a85422889222a7c222f200b7c2026200d7c202e202b85423089222620287c222820298542018922297c222b20137c202b201585422089221520167c221620298542288922297c222b201585423089221520167c221620298542018922297c222e200c7c202e202c20087c201e201d85420189221d7c221e20247c201e202685422089221e202020217c22207c2221201d85422889221d7c2226201e85423089221e85422089222c202520117c202020148542018922147c222020027c2020201f85422089221f20287c222020148542288922147c2225201f85423089221f20207c22207c222820298542288922297c222e200c7c202620127c202f202d85423089222620277c2227202a85420189222a7c222d20097c202d201f85422089221f20167c2216202a85422889222a7c222d201f85423089221f20167c2216202a85420189222a7c222f20037c202f202b201c7c202020148542018922147c222020177c20202026854220892220201e20217c221e7c222120148542288922147c2226202085423089222085422089222b202520057c201e201d85420189221d7c221e20037c201e201585422089221520277c221e201d85422889221d7c22252015854230892215201e7c221e7c2227202a85422889222a7c222f20177c202620057c202e202c85423089222620287c222820298542018922297c222c20247c202c201585422089221520167c221620298542288922297c222c201585423089221520167c221620298542018922297c222e20117c202e202d201c7c201e201d85420189221d7c221e200b7c201e202685422089221e202020217c22207c2221201d85422889221d7c2226201e85423089221e85422089222d202520027c202020148542018922147c222020097c2020201f85422089221f20287c222020148542288922147c2225201f85423089221f20207c22207c222820298542288922297c222e20027c202620087c202f202b85423089222620277c2227202a85420189222a7c222b20137c202b201f85422089221f20167c2216202a85422889222a7c222b201f85423089221f20167c2216202a85420189222a7c222f201c7c202f202c200d7c202020148542018922147c2220200a7c20202026854220892220201e20217c221e7c222120148542288922147c2226202085423089222085422089222c202520187c201e201d85420189221d7c221e20127c201e201585422089221520277c221e201d85422889221d7c22252015854230892215201e7c221e7c2227202a85422889222a7c222f200d7c202620137c202e202d85423089222620287c222820298542018922297c222d20177c202d201585422089221520167c221620298542288922297c222d201585423089221520167c221620298542018922297c222e20087c202e202b20127c201e201d85420189221d7c221e200c7c201e202685422089221e202020217c22207c2221201d85422889221d7c2226201e85423089221e85422089222b202520037c202020148542018922147c2220200a7c2020201f85422089221f20287c222020148542288922147c2225201f85423089221f20207c22207c222820298542288922297c222e200a7c2026200b7c202f202c85423089222620277c2227202a85420189222a7c222c20057c202c201f85422089221f20167c2216202a85422889222a7c222c201f85423089221f20167c2216202a85420189222a7c222f20137c202f202d20117c202020148542018922147c222020247c20202026854220892220201e20217c221e7c222120148542288922147c2226202085423089222085422089222d202520097c201e201d85420189221d7c221e20187c201e201585422089221520277c221e201d85422889221d7c22252015854230892215201e7c221e7c2227202a85422889222a7c222f201c7c202620187c202e202b85423089222620287c222820298542018922297c222b200d7c202b201585422089221520167c221620298542288922297c222b201585423089221520167c221620298542018922297c222e20057c202e202c200c7c201e201d85420189221d7c221e20177c201e202685422089221e202020217c22207c2221201d85422889221d7c2226201e85423089221e85422089222c202520087c202020148542018922147c2220200b7c2020201f85422089221f20287c222020148542288922147c2225201f85423089221f20207c22207c222820298542288922297c222e20127c202620037c202f202d85423089222620277c2227202a85420189222a7c222d20127c202d201f85422089221f20167c2216202a85422889222a7c222d201f85423089221f20167c2216202a85420189222a7c222f20087c202f202b20247c202020148542018922147c222020097c20202026854220892220201e20217c221e7c222120148542288922147c2226202085423089222085422089222b202520027c201e201d85420189221d7c221e20117c201e201585422089221520277c221e201d85422889221d7c22252015854230892215201e7c221e7c2227202a85422889222a7c222f20137c2026201c7c202e202c85423089222620287c222820298542018922297c222c20097c202c201585422089221520167c221620298542288922297c222c201585423089221520167c221620298542018922297c222e20027c202e202d200d7c201e201d85420189221d7c221e20057c201e202685422089221e202020217c22207c2221201d85422889221d7c2226201e85423089221e85422089222d202520247c202020148542018922147c222020117c2020201f85422089221f20287c222020148542288922147c2225201f85423089221f20207c22207c222820298542288922297c222e20057c202620177c202f202b85423089222620277c2227202a85420189222a7c222b200c7c202b201f85422089221f20167c2216202a85422889222a7c222b201f85423089221f20167c2216202a85420189222a7c222f20097c202f202c20037c202020148542018922147c222020187c20202026854220892220201e20217c221e7c222120148542288922147c2226202085423089222085422089222c2025200b7c201e201d85420189221d7c221e200a7c201e201585422089221520277c221e201d85422889221d7c22252015854230892215201e7c221e7c2227202a85422889222a7c222f20027c202620087c202e202d85423089222620287c222820298542018922297c222d20127c202d201585422089221520167c221620298542288922297c222d201585423089221520167c221620298542018922297c222e20037c202e202b20117c201e201d85420189221d7c221e20137c201e202685422089221e202020217c22207c2221201d85422889221d7c2226201e85423089221e85422089222b202520187c202020148542018922147c2220201c7c2020201f85422089221f20287c222020148542288922147c2225201f85423089221f20207c22207c222820298542288922297c222e20177c202620247c202f202c85423089222620277c2227202a85420189222a7c222c200a7c202c201f85422089221f20167c2216202a85422889222a7c222c201f85423089221f20167c2216202a85420189222a7c222f200b7c202f202d200c7c202020148542018922147c2220200b7c2020202685422089220b201e20217c221e7c222020148542288922147c2221200b85423089220b8542208922262025200d7c201e201d85420189221d7c221e20177c201e201585422089221720277c2215201d85422889221d7c221e201785423089221720157c22157c2225202a8542288922277c222a200a7c202120037c202e202b85423089220320287c220a20298542018922217c222820087c2028201785422089220820167c221720218542288922167c2221200885423089220820177c221720168542018922167c222820127c2028202c20057c2015201d8542018922057c2212200d7c20122003854220892212200b20207c22037c220b20058542288922057c220d2012854230892212854220892215201e200c7c200320148542018922037c220c20247c200c201f854220892224200a7c220a20038542288922037c220c2024854230892224200a7c220a7c221420168542288922167c221d200485200c201c7c2012200b7c221220058542018922057c221c20027c201c2008854220892202202a202685423089220820257c221c7c220b20058542288922057c220c2002854230892202200b7c220b853703102000200e2013202120097c200a20038542018922097c22037c2003200885422089220820127c221220098542288922097c2203852011200d20187c201c20278542018922137c22187c2018202485422089221120177c221820138542288922137c221c201185423089221120187c2218853703182000201c2019852003200885423089220820127c2212853703082000200c202285201d201585423089220320147c221c85370300201a201b200b20058542018985200385370300200f2010201c2016854201898520028537030020062007201820138542018985200885370300200020232012200985420189852011853703200b8b0601097f0240024002400240024020020d00410021060c010b200120026a210720052d008001210841002106200121094100210a03400240024020092c0000220b4100480d002005200b41ff0171220b6a2d0000220c41ff01470d012000200a3602042000200b3602000f0b2000200a3602042000418280c4003602000f0b02400240200620044b0d00200320066a210d2006450d01024002402006410371220e0d002003210b0c010b2003210b0340200b200b2d0000413a6c200c6a220c3a0000200b41016a210b200c410876210c200e417f6a220e0d000b0b20064104490d010340200b200b2d0000413a6c200c6a220c3a0000200b41016a220e200e2d0000413a6c200c4108766a220c3a0000200b41026a220e200e2d0000413a6c200c4108766a220c3a0000200b41036a220e200e2d0000413a6c200c4108766a220c3a0000200c410876210c200b41046a220b200d470d000c020b0b20062004418c88c08000109581808000000b0240200c450d00200620044f0d04200d200c3a0000200641016a21060b200a41016a210a200941016a22092007470d000b20062004200620044b1b210e200841ff0171210c0240034020012d0000200c470d01200320066a410020062004491b210b200e2006460d03200141016a2101200b41003a0000200641016a21062002417f6a22020d000b0b200620044b0d0320064102490d00200320066a200641017622046b210a4100210b024020044101460d002006417f6a210c200441feffffff077121014100210b03402003200c6a220d2d00002109200d2003200b6a220e2d00003a0000200e20093a0000200a2004200b417e736a6a220d2d00002109200d200e41016a220e2d00003a0000200e20093a0000200c417e6a210c2001200b41026a220b470d000b0b2006410271450d002003200b6a220c2d0000210e200c200a2004200b417f736a6a220b2d00003a0000200b200e3a00000b2000418380c400360200200020063602040f0b2000200b3602042000418080c4003602000f0b2000428080c4003702000f0b2006200441fc87c08000109581808000000b02000b21002001280214419c88c08000410b200141186a28020028020c118280808000000bd20101037f200128020421020240024002402001280208220320012802002204460d0041002d00fca3c680001a410c41002802c8a3c68000118180808000002201450d0220014101360208200120043602042001200236020041908ac0800021040c010b024020030d00418489c08000210441002101419c88c0800021020c010b41a489c08000210402402002410171450d00200221010c010b20024101722101419489c0800021040b2000200136020c2000200336020820002002360204200020043602000f0b4104410c10b280808000000b22002000410036020c20002003360208200020023602042000418489c080003602000b7801017f024002400240024020030d00410121040c010b2003417f4c0d0141002d00fca3c680001a200341002802c8a3c68000118180808000002204450d020b20042002200310848e80800021022000200336020820002002360204200020033602000f0b10ae80808000000b4101200310b280808000000b040041000b02000b6b01017f024020012802002204410171450d002000200120042004417e712002200310c5808080000f0b20042004280208220141016a36020802402001417f4c0d002000200436020c2000200336020820002002360204200041908ac080003602000f0b10d180808000000be00101017f41002d00fca3c680001a02400240410c41002802c8a3c68000118180808000002206450d0020064102360208200620033602002006200420036b20056a360204200120062001280200220320032002461b360200024020032002470d002000200636020c2000200536020820002004360204200041908ac080003602000f0b20032003280208220241016a3602082002417f4c0d012000200336020c2000200536020820002004360204200041908ac08000360200200641002802c0a3c68000118080808000000f0b4104410c10b280808000000b10d180808000000b4d00024020012802002201410171450d002001417e712002200310fe8d808000210120002003360208200020013602042000200220036a20016b3602000f0b200020012002200310c7808080000bdb0201037f23808080800041106b220424808080800041012105200141002001280208220620064101461b3602080240024002400240024020064101470d002001280204210620012802002105200141002802c0a3c6800011808080800000200020052002200310fe8d808000360204200020063602000c010b02402003450d002003417f4c0d0241002d00fca3c680001a200341002802c8a3c68000118180808000002205450d030b20052002200310848e8080002102200120012802082206417f6a360208024020064101470d00200141046a280200417f4c0d04200128020041002802c0a3c6800011808080800000200141002802c0a3c68000118080808000000b20002002360204200020033602000b20002003360208200441106a2480808080000f0b10ae80808000000b4101200310b280808000000b41b489c08000412b2004410f6a41e089c0800041808ac08000108981808000000bea0101017f23808080800041106b2203248080808000024002400240024020002802002200410171450d00200120026a2000417e7122006b417f4c0d02200041002802c0a3c68000118080808000000c010b200020002802082202417f6a36020820024101470d00200041046a280200417f4c0d02200028020041002802c0a3c6800011808080800000200041002802c0a3c68000118080808000000b200341106a2480808080000f0b41b489c08000412b2003410f6a41e089c0800041f089c08000108981808000000b41b489c08000412b2003410f6a41e089c0800041808ac08000108981808000000b6801017f024020012802002204410171450d0020002001200420042002200310c5808080000f0b20042004280208220141016a36020802402001417f4c0d002000200436020c2000200336020820002002360204200041908ac080003602000f0b10d180808000000b4a00024020012802002201410171450d0020012002200310fe8d808000210120002003360208200020013602042000200220036a20016b3602000f0b200020012002200310c7808080000be50101017f23808080800041106b2203248080808000024002400240024020002802002200410171450d00200120026a20006b417f4c0d02200041002802c0a3c68000118080808000000c010b200020002802082202417f6a36020820024101470d00200041046a280200417f4c0d02200028020041002802c0a3c6800011808080800000200041002802c0a3c68000118080808000000b200341106a2480808080000f0b41b489c08000412b2003410f6a41e089c0800041f089c08000108981808000000b41b489c08000412b2003410f6a41e089c0800041808ac08000108981808000000b2301017f410121010240200028020022004101710d00200028020841014621010b20010b4901017f200128020022012001280208220441016a36020802402004417f4a0d0010d180808000000b2000200136020c2000200336020820002002360204200041908ac080003602000b1300200020012802002002200310c7808080000b0d0020002802002802084101460b960101027f23808080800041106b22032480808080002000280200220020002802082204417f6a3602080240024020044101470d00200041046a280200417f4c0d01200028020041002802c0a3c6800011808080800000200041002802c0a3c68000118080808000000b200341106a2480808080000f0b41b489c08000412b2003410f6a41e089c0800041808ac08000108981808000000b170041a08ac08000410541808bc0800010f880808000000bd10805017f027e017f017e027f23808080800041e0006b22042480808080002004200336023c02400240024002402003417e6a41234f0d0020020d01200041003a00010c020b200441cc006a420137020020044101360244200441cc8bc08000360240200441858080800036025c2004200441d8006a36024820042004413c6a360258200441c0006a41c48cc0800010f680808000000b0240024002400240024020012d000041556a0e03010200020b20024101460d03200141016a21012003ad2105024002400240200241104b0d0020034111490d010b024002402003410a4b0d002002417f6a21024200210603402002450d07200441286a20062006423f872005420010878e80800020012d000041506a220720034f0d08200429033020042903282208423f87520d04200141016a21012002417f6a21022007ad2206420055200820067d220620085373450d000c020b0b2002417f6a21024200210603402002450d06200441186a20062006423f872005420010878e8080002004290318210820042903202106024020012d0000220941506a2207410a490d00417f2009412072220741a97f6a220920092007419f7f6a491b220720034f0d080b20062008423f87520d03200141016a21012002417f6a21022007ad2206420055200820067d220620085373450d000b0b200041033a00010c060b02402003410a4b0d002002417f6a210242002106034020012d000041506a220720034f0d06200141016a2101200620057e2007ad7d21062002417f6a22020d000c050b0b2002417f6a2107420021060340024020012d0000220941506a2202410a490d00417f2009412072220241a97f6a220920092002419f7f6a491b220220034f0d060b200141016a2101200620057e2002ad7d21062007417f6a22070d000c040b0b200041033a00010c040b2002417f6a2202450d02200141016a21010b2003ad210802400240200341104b0d0020024110490d010b2003410b49210a42002106024003402002450d03200441086a20062006423f872008420010878e80800020012d0000220941506a2107200429030821052004290310210602400240200a0d002007410a490d01417f2009412072220741a97f6a220920092007419f7f6a491b21070b200720034f0d050b20062005423f87520d01200141016a21012002417f6a21022007ad2206420053200520067c220620055373450d000b200041023a00010c040b200041023a00010c030b02402003410a4b0d0042002106034020012d000041506a220720034f0d03200141016a2101200620087e2007ad7c21062002417f6a22020d000c020b0b420021060340024020012d0000220941506a2207410a490d00417f2009412072220741a97f6a220920092007419f7f6a491b220720034f0d030b200141016a2101200620087e2007ad7c21062002417f6a22020d000b0b20002006370308410021010c020b41012101200041013a00010c010b410121010b200020013a0000200441e0006a2480808080000bc50605027f017e017f017e027f02402002280200220341134d0d00024002400240200042808084fea6dee111540d002002200341706a2204360200200120046a2000200042808084fea6dee11180220542808084fea6dee1117e7d2200428080e983b1de1680a741017441da8dc080006a2f00003b0000200320016a2206417c6a200042e40080220742e40082a741017441da8dc080006a2f00003b00002006417a6a20004290ce008042e40082a741017441da8dc080006a2f00003b0000200641786a200042c0843d8042e40082a741017441da8dc080006a2f00003b0000200641766a20004280c2d72f80a741e4007041017441da8dc080006a2f00003b0000200641746a20004280c8afa02580a741e4007041017441da8dc080006a2f00003b0000200641726a20004280a094a58d1d80a741ffff037141e4007041017441da8dc080006a2f00003b00002000200742e4007e7da721060c010b024020004280c2d72f5a0d0020032104200021050c020b2001200341786a22046a200020004280c2d72f8022054280c2d72f7e7da7220641c0843d6e41017441da8dc080006a2f00003b0000200320016a2208417c6a200641e4006e220941e4007041017441da8dc080006a2f00003b00002008417a6a20064190ce006e41ffff037141e4007041017441da8dc080006a2f00003b00002006200941e4006c6b21060b200320016a417e6a200641017441da8dc080006a2f00003b00000b024002402005a722084190ce004f0d0020042103200821060c010b20012004417c6a22036a200820084190ce006e22064190ce006c6b220841ffff037141e4006e220941017441da8dc080006a2f00003b0000200420016a417e6a2008200941e4006c6b41ffff037141017441da8dc080006a2f00003b00000b02400240200641ffff0371220441e4004f0d00200621040c010b20012003417e6a22036a2006200441e4006e220441e4006c6b41ffff037141017441da8dc080006a2f00003b00000b0240200441ffff0371410a490d0020012003417e6a22036a200441ffff037141017441da8dc080006a2f00003b0000200220033602000f0b20012003417f6a22036a200441306a3a0000200220033602000f0b41a28fc08000411c41c08fc0800010f880808000000bd10403017f027e017f2380808080004190016b22042480808080002004412736028c0102400240200142808020540d00200441306a2000420042f3b2d8c19e9ebdcc957f420010878e808000200441206a2000420042d2e1aadaeda7c987f600420010878e808000200441d0006a2001420042f3b2d8c19e9ebdcc957f420010878e808000200441c0006a2001420042d2e1aadaeda7c987f600420010878e808000200441c0006a41086a290300200441206a41086a290300200441306a41086a290300220520042903207c2201200554ad7c220620042903407c2205200654ad7c2005200441d0006a41086a290300200120042903507c200154ad7c7c2201200554ad7c2206423e8821052001423e8820064202868421010c010b20004213882001422d868442bda282a38eab04802101420021050b200441106a20012005428080e0b0b79fb79cf500420010878e808000200429031020007c200441e5006a2004418c016a10d380808000200428028c012107024020012005844200510d00200441e5006a41146a41302007416c6a108a8e8080001a2004411436028c01200420014213882005422d8684220542bda282a38eab048022002001428080e0b0b79fb79cf500420010878e808000200429030020017c200441e5006a2004418c016a10d380808000200428028c012107200542bda282a38eab04540d00200441e6006a41302007417f6a108a8e8080001a20042000a74130723a0065410021070b2003200241d08fc080004100200441e5006a20076a412720076b10db80808000210720044190016a24808080800020070ba60101037f2380808080004180016b220224808080800020002d00002103410021000340200220006a41ff006a20034101714130723a00002000417f6a2100200341ff017122044101762103200441024f0d000b024020004180016a22034180014d0d00200341800141c88dc08000109481808000000b2001410141d88dc080004102200220006a4180016a410020006b10db80808000210020024180016a24808080800020000b02000bb10701017f23808080800041106b2203248080808000024002400240024002400240024002400240024020010e2805080808080808080801030808020808080808080808080808080808080808080808060808080807000b200141dc00460d030c070b20004180043b010a20004200370102200041dce8013b01000c070b20004180043b010a20004200370102200041dce4013b01000c060b20004180043b010a20004200370102200041dcdc013b01000c050b20004180043b010a20004200370102200041dcb8013b01000c040b20004180043b010a20004200370102200041dce0003b01000c030b20024180800471450d0120004180043b010a20004200370102200041dcc4003b01000c020b200241800271450d0020004180043b010a20004200370102200041dcce003b01000c010b024002400240024002402002410171450d00200110f3808080000d010b2001109a81808000450d012000200136020420004180013a00000c040b200341066a41026a41003a0000200341003b0106200341fd003a000f20032001410f714190bdc080006a2d00003a000e20032001410476410f714190bdc080006a2d00003a000d20032001410876410f714190bdc080006a2d00003a000c20032001410c76410f714190bdc080006a2d00003a000b20032001411076410f714190bdc080006a2d00003a000a20032001411476410f714190bdc080006a2d00003a0009200141017267410276417e6a2201410b4f0d01200341066a20016a220241002f008fbec080003b0000200241026a41002d0091bec080003a00002000410a3a000b200020013a000a20002003290106370000200041086a200341066a41086a2f01003b00000c030b200341066a41026a41003a0000200341003b0106200341fd003a000f20032001410f714190bdc080006a2d00003a000e20032001410476410f714190bdc080006a2d00003a000d20032001410876410f714190bdc080006a2d00003a000c20032001410c76410f714190bdc080006a2d00003a000b20032001411076410f714190bdc080006a2d00003a000a20032001411476410f714190bdc080006a2d00003a0009200141017267410276417e6a2201410b4f0d01200341066a20016a220241002f008fbec080003b0000200241026a41002d0091bec080003a00002000410a3a000b200020013a000a20002003290106370000200041086a200341066a41086a2f01003b00000c020b2001410a4194bec08000109481808000000b2001410a4194bec08000109481808000000b200341106a2480808080000b17002001280214200141186a280200200010d9808080000be105010b7f23808080800041306b2203248080808000200341246a2001360200200341033a002c2003412036021c410021042003410036022820032000360220200341003602142003410036020c02400240024002400240200228021022050d002002410c6a2802002200450d012002280208220120004103746a21062000417f6a41ffffffff017141016a2104200228020021004100210703400240200041046a2802002208450d00200328022020002802002008200328022428020c118280808000000d040b20012802002003410c6a200141046a280200118380808000000d03200741016a2107200041086a2100200141086a22012006470d000c020b0b200241146a2802002201450d00200141057421092001417f6a41ffffff3f7141016a21042002280208210a20022802002100410021074100210b03400240200041046a2802002201450d00200328022020002802002001200328022428020c118280808000000d030b2003200520076a220141106a28020036021c20032001411c6a2d00003a002c2003200141186a2802003602282001410c6a28020021064100210c41002108024002400240200141086a2802000e03010002010b2006410374210d41002108200a200d6a220d280204419d80808000470d01200d28020028020021060b410121080b200320063602102003200836020c200141046a280200210802400240024020012802000e03010002010b20084103742106200a20066a2206280204419d80808000470d01200628020028020021080b4101210c0b200320083602182003200c360214200a200141146a2802004103746a22012802002003410c6a200141046a280200118380808000000d02200b41016a210b200041086a21002009200741206a2207470d000b0b200420022802044f0d012003280220200228020020044103746a22012802002001280204200328022428020c11828080800000450d010b410121010c010b410021010b200341306a24808080800020010b17002001280214200141186a280200200010d9808080000bad0601077f0240024020010d00200541016a2106200028021c2107412d21080c010b412b418080c400200028021c220741017122011b2108200120056a21060b0240024020074104710d00410021020c010b0240024020034110490d002002200310fd8080800021010c010b024020030d00410021010c010b2003410371210902400240200341044f0d00410021014100210a0c010b2003417c71210b410021014100210a034020012002200a6a220c2c000041bf7f4a6a200c41016a2c000041bf7f4a6a200c41026a2c000041bf7f4a6a200c41036a2c000041bf7f4a6a2101200b200a41046a220a470d000b0b2009450d002002200a6a210c03402001200c2c000041bf7f4a6a2101200c41016a210c2009417f6a22090d000b0b200120066a21060b0240024020002802000d00410121012000280214220c2000280218220a20082002200310dc808080000d01200c20042005200a28020c118280808000000f0b02402000280204220920064b0d00410121012000280214220c2000280218220a20082002200310dc808080000d01200c20042005200a28020c118280808000000f0b02402007410871450d002000280210210b2000413036021020002d0020210741012101200041013a00202000280214220c2000280218220a20082002200310dc808080000d01200920066b41016a2101024003402001417f6a2201450d01200c4130200a28021011838080800000450d000b41010f0b41012101200c20042005200a28020c118280808000000d01200020073a00202000200b360210410021010c010b200920066b210602400240024020002d002022010e0402000100020b20062101410021060c010b20064101762101200641016a41017621060b200141016a2101200041186a280200210c200028021021092000280214210a024003402001417f6a2201450d01200a2009200c28021011838080800000450d000b41010f0b41012101200a200c20082002200310dc808080000d00200a20042005200c28020c118280808000000d00410021010340024020062001470d0020062006490f0b200141016a2101200a2009200c28021011838080800000450d000b2001417f6a2006490f0b20010b4a01017f0240024002402002418080c400460d0041012105200020022001280210118380808000000d010b20030d01410021050b20050f0b200020032004200128020c118280808000000bd20701087f0240200028020022032000280208220472450d0002402004450d00200120026a21052000410c6a28020041016a2106410021072001210802400340200821042006417f6a2206450d0120042005460d020240024020042c00002209417f4c0d00200441016a2108200941ff017121090c010b20042d0001413f71210a2009411f71210802402009415f4b0d002008410674200a722109200441026a21080c010b200a41067420042d0002413f7172210a0240200941704f0d00200a2008410c74722109200441036a21080c010b200a41067420042d0003413f71722008411274418080f00071722209418080c400460d03200441046a21080b200720046b20086a21072009418080c400470d000c020b0b20042005460d00024020042c00002208417f4a0d0020084160490d0020084170490d0020042d0002413f7141067420042d0001413f71410c747220042d0003413f7172200841ff0171411274418080f0007172418080c400460d010b024002402007450d00024020072002490d004100210420072002460d010c020b41002104200120076a2c00004140480d010b200121040b2007200220041b21022004200120041b21010b024020030d00200028021420012002200041186a28020028020c118280808000000f0b200028020421050240024020024110490d002001200210fd8080800021040c010b024020020d00410021040c010b2002410371210602400240200241044f0d0041002104410021090c010b2002417c712107410021044100210903402004200120096a22082c000041bf7f4a6a200841016a2c000041bf7f4a6a200841026a2c000041bf7f4a6a200841036a2c000041bf7f4a6a21042007200941046a2209470d000b0b2006450d00200120096a21080340200420082c000041bf7f4a6a2104200841016a21082006417f6a22060d000b0b02400240200520044d0d00200520046b21074100210402400240024020002d00200e0402000102020b20072104410021070c010b20074101762104200741016a41017621070b200441016a2104200041186a2802002108200028021021062000280214210903402004417f6a2204450d0220092006200828021011838080800000450d000b41010f0b200028021420012002200041186a28020028020c118280808000000f0b410121040240200920012002200828020c118280808000000d004100210402400340024020072004470d00200721040c020b200441016a210420092006200828021011838080800000450d000b2004417f6a21040b200420074921040b20040f0b200028021420012002200041186a28020028020c118280808000000b9d05010a7f23808080800041106b2202248080808000024002400240024002402000280200450d00200028020421032002410c6a2001410c6a280200220436020020022001280208220536020820022001280204220636020420022001280200220136020020002d002021072000280210210820002d001c4108710d0120062101200821092007210a0c020b20002802142000280218200110df8080800021050c030b200028021420012006200041186a28020028020c118280808000000d014101210a200041013a002041302109200041303602104100210120024100360204200241e88fc080003602004100200320066b2206200620034b1b21030b02402004450d002004410c6c21040340024002400240024020052f01000e03000201000b200541046a28020021060c020b200541086a28020021060c010b0240200541026a2f0100220b41e807490d0041044105200b4190ce00491b21060c010b41012106200b410a490d0041024103200b41e400491b21060b2005410c6a2105200620016a2101200441746a22040d000b0b024002400240200320014d0d00200320016b2104024002400240200a41ff017122050e0402000100020b20042105410021040c010b20044101762105200441016a41017621040b200541016a2105200041186a28020021012000280214210603402005417f6a2205450d0220062009200128021011838080800000450d000c040b0b20002802142000280218200210df8080800021050c010b20062001200210df808080000d014100210502400340024020042005470d00200421050c020b200541016a210520062009200128021011838080800000450d000b2005417f6a21050b200520044921050b200020073a0020200020083602100c010b410121050b200241106a24808080800020050b9f0501087f23808080800041106b22032480808080000240024020022802042204450d0041012105200020022802002004200128020c118280808000000d010b02402002410c6a2802002205450d00200228020822062005410c6c6a2107200341086a41046a21080340024002400240024020062f01000e03000201000b024002402006280204220241c100490d002001410c6a280200210503400240200041d890c0800041c000200511828080800000450d00410121050c090b200241406a220241c0004b0d000c020b0b2002450d032001410c6a28020021050b200041d890c080002002200511828080800000450d02410121050c050b20002006280204200641086a2802002001410c6a28020011828080800000450d01410121050c040b20062f01022102200841003a00002003410036020802400240024002400240024020062f01000e03020100020b200641086a21050c020b024020062f0102220541e807490d004104410520054190ce00491b21090c030b410121092005410a490d0241024103200541e400491b21090c020b200641046a21050b02402005280200220941064f0d0020090d01410021090c020b20094105419891c08000109581808000000b200341086a20096a21040240024020094101710d00200221050c010b2004417f6a22042002200241ffff0371410a6e2205410a6c6b4130723a00000b20094101460d002004417e6a210203402002200541ffff03712204410a6e220a410a704130723a0000200241016a2005200a410a6c6b4130723a0000200441e4006e21052002200341086a4621042002417e6a21022004450d000b0b2000200341086a20092001410c6a28020011828080800000450d00410121050c030b2006410c6a22062007470d000b0b410021050b200341106a24808080800020050b820201017f23808080800041106b220f248080808000200028021420012002200041186a28020028020c118280808000002102200f41003a000d200f20023a000c200f2000360208200f41086a2003200420052006108c81808000200720082009200a108c81808000200b200c200d200e108c818080002101200f2d000c210202400240200f2d000d0d00200241ff017141004721000c010b41012100200241ff01710d000240200128020022002d001c4104710d0020002802144187a5c080004102200028021828020c1182808080000021000c010b20002802144186a5c080004101200028021828020c1182808080000021000b200f41106a24808080800020000b2d00024020002d00000d00200141a891c08000410510dd808080000f0b200141ad91c08000410410dd808080000bd107030d7f017e017f23808080800041206b22032480808080004101210402400240200228021422054122200241186a28020022062802102207118380808000000d000240024020010d0041002102410021010c010b200020016a210841002102200021094100210a024002400340024002402009220b2c0000220c417f4c0d00200b41016a2109200c41ff0171210d0c010b200b2d0001413f71210e200c411f71210f0240200c415f4b0d00200f410674200e72210d200b41026a21090c010b200e410674200b2d0002413f7172210e200b41036a21090240200c41704f0d00200e200f410c7472210d0c010b200e41067420092d0000413f7172200f411274418080f0007172220d418080c400460d03200b41046a21090b200341046a200d4181800410d7808080000240024020032d0004418001460d0020032d000f20032d000e6b41ff01714101460d00200a2002490d0302402002450d00024020022001490d0020022001460d010c050b200020026a2c00004140480d040b0240200a450d000240200a2001490d00200a2001460d010c050b2000200a6a2c000041bf7f4c0d040b024002402005200020026a200a20026b200628020c118280808000000d00200341106a41086a220f200341046a41086a28020036020020032003290204221037031002402010a741ff0171418001470d00418001210e034002400240200e41ff0171418001460d0020032d001a220c20032d001b4f0d052003200c41016a3a001a200c410a4f0d07200341106a200c6a2d000021020c010b4100210e200f410036020020032802142102200342003703100b20052002200711838080800000450d000c020b0b20032d001a2202410a2002410a4b1b210c20032d001b220e2002200e20024b1b2111034020112002460d022003200241016a220e3a001a200c2002460d04200341106a20026a210f200e21022005200f2d0000200711838080800000450d000b0b410121040c070b410121020240200d418001490d0041022102200d418010490d0041034104200d41808004491b21020b2002200a6a21020b200a200b6b20096a210a20092008470d010c030b0b200c410a41a4bec0800010f980808000000b200020012002200a41c491c08000109781808000000b024020020d00410021020c010b0240200120024b0d0020012002470d03200120026b210c20012102200c21010c010b200020026a2c000041bf7f4c0d02200120026b21010b2005200020026a2001200628020c118280808000000d002005412220071183808080000021040b200341206a24808080800020040f0b200020012002200141b491c08000109781808000000be40201077f23808080800041106b22022480808080004101210302400240200128021422044127200141186a2802002802102205118380808000000d002002200028020041810210d7808080000240024020022d0000418001470d00200241086a21064180012107034002400240200741ff0171418001460d0020022d000a220020022d000b4f0d042002200041016a3a000a2000410a4f0d06200220006a2d000021010c010b410021072006410036020020022802042101200242003703000b20042001200511838080800000450d000c030b0b20022d000a2201410a2001410a4b1b210020022d000b22072001200720014b1b2108034020082001460d012002200141016a22073a000a20002001460d03200220016a210620072101200420062d0000200511838080800000450d000c020b0b2004412720051183808080000021030b200241106a24808080800020030f0b2000410a41a4bec0800010f980808000000bbb0201017f23808080800041106b220224808080800020002802002100024002402001280200200128020872450d002002410036020c02400240024002402000418001490d002000418010490d012000418080044f0d0220022000413f71418001723a000e20022000410c7641e001723a000c20022000410676413f71418001723a000d410321000c030b200220003a000c410121000c020b20022000413f71418001723a000d2002200041067641c001723a000c410221000c010b20022000413f71418001723a000f2002200041127641f001723a000c20022000410676413f71418001723a000e20022000410c76413f71418001723a000d410421000b20012002410c6a200010dd8080800021010c010b20012802142000200141186a2802002802101183808080000021010b200241106a24808080800020010b180020002802002001200028020428020c118380808000000bf80202027f017e2380808080004180016b22022480808080002000280200210002400240024002400240200128021c22034110710d0020034120710d0120002903004101200110fe8080800021000c020b20002903002104410021000340200220006a41ff006a413041d7002004a7410f712203410a491b20036a3a00002000417f6a210020044210542103200442048821042003450d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000c010b20002903002104410021000340200220006a41ff006a413041372004a7410f712203410a491b20036a3a00002000417f6a210020044210542103200442048821042003450d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000b20024180016a24808080800020000f0b200341800141c88dc08000109481808000000b200341800141c88dc08000109481808000000b140020012000280200200028020410dd808080000b2000200042d7e8a9deef8fddde6a370308200042f0c6e287c69783ad393703000bb30301057f23808080800041c0006b22022480808080004101210302402001280214220441d491c08000410c200141186a280200220528020c2206118280808000000d00200028020c2101200241106a410c6a42033702002002413c6a418580808000360200200241286a410c6a41858080800036020020024103360214200241f49fc0800036021020022001410c6a3602382002200141086a360230200241a28080800036022c200220013602282002200241286a36021820042005200241106a10d9808080000d000240024020002802082201450d00200441e091c0800041022006118280808000000d02200241286a41106a200141106a290200370300200241286a41086a200141086a2902003703002002200129020037032820042005200241286a10d9808080000d020c010b2002200028020022012000280204410c6a28020011848080800000200229030042c1f7f9e8cc93b2d14185200241086a29030042e4dec78590d085de7d858450450d00200441e091c0800041022006118280808000000d012004200128020020012802042006118280808000000d010b410021030b200241c0006a24808080800020030b8a0401077f0240024002400240200141800a4f0d00200141057621020240024020002802a0012203450d00200341284b0d032002417f6a2104200341027420006a417c6a2105200320026a41027420006a417c6a21060340200420036a41274b0d02200620052802003602002006417c6a21062005417c6a21052003417f6a22030d000b0b2001411f712104024020014120490d002000410020024101200241014b1b410274108a8e8080001a0b20002802a00120026a2105024020040d00200020053602a00120000f0b2005417f6a220341274b0d0320052107200020034102746a2802002206410020016b2208762203450d040240200541274b0d00200020054102746a2003360200200541016a21070c050b2005412841d892c0800010f980808000000b200220036a417f6a412841d892c0800010f980808000000b418293c08000411d41d892c0800010f880808000000b2003417f6a412841d892c0800010f980808000000b2003412841d892c0800010f980808000000b02400240200241016a220120054f0d002008411f712108200541027420006a41786a210303402005417e6a41284f0d02200341046a200620047420032802002206200876723602002003417c6a210320012005417f6a2205490d000b0b200020024102746a22032003280200200474360200200020073602a00120000f0b417f412841d892c0800010f980808000000bb806030b7f027e017f23808080800041a0016b22032480808080002003410041a001108a8e80800021040240024002400240024020002802a00122052002490d00200541294f0d01200120024102746a21060240024002402005450d00200541016a21072005410274210241002108410021090340200420084102746a210a03402008210b200a210320012006460d09200341046a210a200b41016a21082001280200210c200141046a220d2101200c450d000b200cad210e4200210f2002210c200b21012000210a0340200141284f0d042003200f20033502007c200a350200200e7e7c220f3e0200200f422088210f200341046a2103200141016a2101200a41046a210a200c417c6a220c0d000b200521030240200fa72201450d00200b20056a220341284f0d03200420034102746a2001360200200721030b20092003200b6a2203200920034b1b2109200d21010c000b0b4100210941002103034020012006460d07200341016a21032001280200210a200141046a22082101200a450d0020092003417f6a2201200920014b1b2109200821010c000b0b2003412841d892c0800010f980808000000b2001412841d892c0800010f980808000000b200541294f0d0120024102742107200241016a2110200020054102746a210d4100210b2000210a4100210903402004200b4102746a21080340200b210c20082103200a200d460d05200341046a2108200c41016a210b200a2802002106200a41046a2205210a2006450d000b2006ad210e4200210f20072106200c210a2001210802400340200a41284f0d012003200f20033502007c2008350200200e7e7c220f3e0200200f422088210f200341046a2103200a41016a210a200841046a21082006417c6a22060d000b200221030240200fa7220a450d00200c20026a220341284f0d05200420034102746a200a360200201021030b20092003200c6a2203200920034b1b21092005210a0c010b0b200a412841d892c0800010f980808000000b2005412841d892c08000109581808000000b2005412841d892c08000109581808000000b2003412841d892c0800010f980808000000b2000200441a00110848e808000220320093602a001200441a0016a24808080800020030bc203000240024002402002450d0020012d000041304d0d01200641034d0d02200541023b01000240024002400240200341107441107522064101480d0020052001360204200341ffff0371220320024f0d01200541023b0118200541023b010c20052003360208200541206a200220036b22023602002005411c6a200120036a360200200541146a4101360200200541106a41c895c0800036020041032101200420024d0d03200420026b21040c020b200541023b0118200541003b010c20054102360208200541c995c08000360204200541206a20023602002005411c6a2001360200200541106a410020066b220336020041032101200420024d0d02200420026b220220034d0d02200220066a21040c010b200541003b010c20052002360208200541106a200320026b360200024020040d00410221010c020b200541023b0118200541206a41013602002005411c6a41c895c080003602000b200541003b0124200541286a2004360200410421010b20002001360204200020053602000f0b41b294c08000412141d494c0800010f880808000000b41e494c08000411f418495c0800010f880808000000b419495c08000412241b895c0800010f880808000000b970809017f017e017f017e017f037e027f017e017f23808080800041f0086b22042480808080002001bd21050240024020012001610d00410221060c010b200542ffffffffffffff0783220742808080808080800884200542018642feffffffffffff0f832005423488a741ff0f7122081b2209420183210a4103210602400240024041014102410420054280808080808080f8ff0083220b50220c1b200b4280808080808080f8ff00511b41034104200c1b2007501b417f6a0e0403000102030b410421060c020b200841cd776a210d200a5021064201210e0c010b428080808080808020200942018620094280808080808080085122061b21094202420120061b210e41cb7741cc7720061b20086a210d200a5021060b2004200d3b01e8082004200e3703e008200442013703d808200420093703d008200420063a00ea080240024002400240024002402006417e6a41ff01712206410320064103491b2208450d0041f095c0800041ef95c0800020021b41ef95c080002005423f87a7220f417f4a1b210c410121064101200f4180017141077620021b21022008417f6a0e03010203010b2004410336029808200441f195c0800036029408200441023b0190084101210620044190086a210d4100210241ef95c08000210c0c040b2004410336029808200441f495c0800036029408200441023b01900820044190086a210d0c030b41022106200441023b0190082003450d01200441a0086a2003360200200441003b019c082004410236029808200441c995c080003602940820044190086a210d0c020b024041744105200d41107441107522064100481b20066c220641c0fd004f0d0020044190086a200441d0086a200441106a200641047641156a220d410020036b4180807e200341808002491b2206109e8180800020064110744110752106024002402004280290080d00200441c0086a200441d0086a200441106a200d20061092818080000c010b200441c0086a41086a20044190086a41086a28020036020020042004290290083703c0080b024020042e01c808220d20064c0d00200441086a20042802c00820042802c408200d200320044190086a410410ec80808000200428020c21062004280208210d0c030b41022106200441023b019008024020030d00410121062004410136029808200441f795c080003602940820044190086a210d0c030b200441a0086a2003360200200441003b019c082004410236029808200441c995c080003602940820044190086a210d0c020b41fb95c08000412541a096c0800010f880808000000b410121062004410136029808200441f795c080003602940820044190086a210d0b200441cc086a20063602002004200d3602c808200420023602c4082004200c3602c0082000200441c0086a10de808080002106200441f0086a24808080800020060bac0608017f017e017f017e017f037e027f017e2380808080004180016b22042480808080002001bd21050240024020012001610d00410221060c010b200542ffffffffffffff0783220742808080808080800884200542018642feffffffffffff0f832005423488a741ff0f7122081b2209420183210a4103210602400240024041014102410420054280808080808080f8ff0083220b50220c1b200b4280808080808080f8ff00511b41034104200c1b2007501b417f6a0e0403000102030b410421060c020b200841cd776a210d200a5021064201210e0c010b428080808080808020200942018620094280808080808080085122061b21094202420120061b210e41cb7741cc7720061b20086a210d200a5021060b2004200d3b01782004200e3703702004420137036820042009370360200420063a007a02400240024002402006417e6a41ff01712206410320064103491b220c450d0041f095c0800041ef95c080002005423f87a72208417f4a22061b41ef95c0800041ef95c0800020061b20021b210d41012106410120084180017141077620021b21020240200c417f6a0e03020300020b200441206a200441e0006a2004410f6a4111109d818080000240024020042802200d00200441d0006a200441e0006a2004410f6a41111091818080000c010b200441d0006a41086a200441206a41086a280200360200200420042902203703500b20042004280250200428025420042f01582003200441206a410410ec80808000200428020421062004280200210c0c030b20044103360228200441f195c08000360224200441023b012041012106200441206a210c4100210241ef95c08000210d0c020b20044103360228200441f495c08000360224200441023b0120200441206a210c0c010b41022106200441023b012002402003450d00200441306a4101360200200441003b012c20044102360228200441c995c08000360224200441206a210c0c010b4101210620044101360228200441f795c08000360224200441206a210c0b200441dc006a20063602002004200c360258200420023602542004200d3602502000200441d0006a10de80808000210620044180016a24808080800020060bf10709017f017e017f017e017f037e027f017e017f2380808080004190016b22032480808080002001bd21040240024020012001610d00410221050c010b200442ffffffffffffff0783220642808080808080800884200442018642feffffffffffff0f832004423488a741ff0f7122071b220842018321094103210502400240024041014102410420044280808080808080f8ff0083220a50220b1b200a4280808080808080f8ff00511b41034104200b1b2006501b417f6a0e0403000102030b410421050c020b200741cd776a210c20095021054201210d0c010b428080808080808020200842018620084280808080808080085122051b21084202420120051b210d41cb7741cc7720051b20076a210c20095021050b2003200c3b0188012003200d370380012003420137037820032008370370200320053a008a01024002400240024002400240024002402005417e6a41ff01712205410320054103491b220b450d0041f095c0800041ef95c0800020021b41ef95c080002004423f87a72207417f4a1b210c41012105410120074180017141077620021b21020240200b417f6a0e03020300020b200341186a200341f0006a200341076a4111109d818080000240024020032802180d00200341e0006a200341f0006a200341076a41111091818080000c010b200341e0006a41086a200341186a41086a280200360200200320032902183703600b20032802642207450d032003280260220e2d000041304d0d0420032e0168210b200341013602202003200e36021c200341023b011841012105200741014b0d050c060b20034103360220200341f195c0800036021c200341023b0118410121054100210241ef95c08000210c0c060b20034103360220200341f495c0800036021c200341023b01180c050b20034103360220200341f895c0800036021c200341023b01180c040b41b294c08000412141cc95c0800010f880808000000b41e494c08000411f41dc95c0800010f880808000000b200341386a2007417f6a360200200341346a200e41016a3602002003412c6a4101360200200341286a41c895c08000360200200341023b0130200341023b0124410321050b02400240200b4101480d00200341186a2005410c6c6a22074101360208200741ec95c08000360204200741023b0100200b417f6a21070c010b200341186a2005410c6c6a22074102360208200741ed95c08000360204200741023b01004101200b6b21070b2005410c6c200341186a6a220b410e6a20073b0100200b410c6a41013b0100200541026a21050b200341ec006a2005360200200320023602642003200c3602602003200341186a3602682000200341e0006a10de80808000210520034190016a24808080800020050b950103017f017e027c200128021c410171210202402001280208450d00200120002b030020022001410c6a28020010ed808080000f0b20002903002203bf21040240200342ffffffffffffffffff0083bf2205440080e03779c34143660d002005440000000000000000622005442d431cebe2361a3f63710d00200120042002410110ee808080000f0b20012004200210ef808080000b4502017f017c200128021c410171210220002b0300210302402001280208450d002001200320022001410c6a28020010ed808080000f0b200120032002410010ee808080000bb00c03097f017e017f024020040d002000200336023820002001360230200041003a000e20004181023b010c20002002360208200042003703002000413c6a4100360200200041346a20023602000f0b41012105410021060240024002400240024002400240024002400240024020044101470d0041002107410121080c010b41012109410121054100210a4101210b410021060340200b210c2006200a6a220b20044f0d0202400240200320096a2d000041ff017122092003200b6a2d0000220b4f0d00200c20066a41016a220b200a6b2105410021060c010b02402009200b460d0041012105200c41016a210b41002106200c210a0c010b4100200641016a220b200b20054622091b2106200b410020091b200c6a210b0b200b20066a22092004490d000b4101210941012108410021074101210b410021060340200b210c200620076a220b20044f0d0302400240200320096a2d000041ff017122092003200b6a2d0000220b4d0d00200c20066a41016a220b20076b2108410021060c010b02402009200b460d0041012108200c41016a210b41002106200c21070c010b4100200641016a220b200b20084622091b2106200b410020091b200c6a210b0b200b20066a22092004490d000b200a21060b200420062007200620074b220b1b220d490d0220052008200b1b220b200d6a2206200b490d03200620044b0d040240024020032003200b6a200d10888e808000450d002004410371210c024002402004417f6a41034f0d004100210b4200210e0c010b2004417c7121094100210b4200210e034042012003200b6a220641036a310000864201200641026a310000864201200641016a310000864201200631000086200e84848484210e2009200b41046a220b470d000b0b2004200d6b21090240200c450d002003200b6a210603404201200631000086200e84210e200641016a2106200c417f6a220c0d000b0b200d2009200d20094b1b41016a210b417f210a200d2105417f21060c010b41012107410021064101210941002105024003402009220c20066a220820044f0d01200420066b200c417f736a220920044f0d082006417f7320046a20056b220a20044f0d0902400240200320096a2d000041ff017122092003200a6a2d0000220a4f0d00200841016a220920056b2107410021060c010b02402009200a460d00200c41016a21094100210641012107200c21050c010b4100200641016a22092009200746220a1b210620094100200a1b200c6a21090b2007200b470d000b0b41012107410021064101210941002108024003402009220c20066a220f20044f0d01200420066b200c417f736a220920044f0d0a2006417f7320046a20086b220a20044f0d0b02400240200320096a2d000041ff017122092003200a6a2d0000220a4d0d00200f41016a220920086b2107410021060c010b02402009200a460d00200c41016a21094100210641012107200c21080c010b4100200641016a22092009200746220a1b210620094100200a1b200c6a21090b2007200b470d000b0b200420052008200520084b1b6b210502400240200b0d004200210e4100210b4100210a0c010b200b41037121094100210a02400240200b41044f0d004200210e4100210c0c010b200b417c7121074100210c4200210e034042012003200c6a220641036a310000864201200641026a310000864201200641016a310000864201200631000086200e84848484210e2007200c41046a220c470d000b0b2009450d002003200c6a210603404201200631000086200e84210e200641016a21062009417f6a22090d000b0b200421060b2000200336023820002001360230200020063602282000200a360224200020023602202000410036021c2000200b360218200020053602142000200d3602102000200e370308200041013602002000413c6a2004360200200041346a20023602000f0b200b200441c497c0800010f980808000000b200b200441c497c0800010f980808000000b200d200441a497c08000109581808000000b200b200641b497c08000109681808000000b2006200441b497c08000109581808000000b2009200441d497c0800010f980808000000b200a200441e497c0800010f980808000000b2009200441d497c0800010f980808000000b200a200441e497c0800010f980808000000bf00201057f2000410b742101410021024121210341212104024002400340200341017620026a2203410274419499c080006a280200410b7422052001460d0120032004200520014b1b2204200341016a200220052001491b22026b2103200420024b0d000c020b0b200341016a21020b0240024002400240200241204b0d0020024102742203419499c080006a280200411576210120024120470d01411f210241d70521050c020b2002412141f498c0800010f980808000000b2003419899c080006a2802004115762105024020020d00410021020c020b2002417f6a21020b2002410274419499c080006a28020041ffffff007121020b0240024020052001417f736a450d00200020026b2104200141d705200141d7054b1b21032005417f6a210541002102034020032001460d022002200141989ac080006a2d00006a220220044b0d012005200141016a2201470d000b200521010b20014101710f0b200341d705418499c0800010f980808000000b02000b02000b4c01017f23808080800041206b2202248080808000200241013b011c20022001360218200220003602142002418ca0c08000360210200241f09fc0800036020c2002410c6a109384808000000b7d01017f23808080800041306b2203248080808000200341106a200041106a290200370300200341086a200041086a29020037030020032000290200370300200320013a002d200341003a002c200320023602282003418ca0c08000360220200341f09fc0800036021c200320033602242003411c6a109384808000000b5401017f23808080800041206b22032480808080002003410c6a420037020020034101360204200341f09fc080003602082003200136021c200320003602182003200341186a3602002003200210f680808000000b870101017f23808080800041306b22032480808080002003200136020420032000360200200341086a410c6a4202370200200341206a410c6a4185808080003602002003410236020c200341d0a0c0800036020820034185808080003602242003200341206a360210200320033602282003200341046a360220200341086a200210f680808000000b4601017f23808080800041106b22052480808080002005200236020c200520013602082000200541086a41e0a0c080002005410c6a41e0a0c080002003200410fb80808000000bd90301017f23808080800041f0006b22072480808080002007200236020c2007200136020820072004360214200720033602100240024002400240200041ff01710e03000102000b200741f0a0c08000360218410221020c020b200741f2a0c08000360218410221020c010b200741f4a0c08000360218410721020b2007200236021c024020052802000d00200741cc006a41a380808000360200200741386a410c6a41a380808000360200200741d8006a410c6a42033702002007410336025c200741aca1c08000360258200741a28080800036023c2007200741386a3602602007200741106a3602482007200741086a3602402007200741186a360238200741d8006a200610f680808000000b200741206a41106a200541106a290200370300200741206a41086a200541086a29020037030020072005290200370320200741d8006a410c6a4204370200200741d4006a41a380808000360200200741cc006a41a380808000360200200741386a410c6a41a4808080003602002007410436025c200741e0a1c08000360258200741a28080800036023c2007200741386a3602602007200741106a3602502007200741086a3602482007200741206a3602402007200741186a360238200741d8006a200610f680808000000bf90503057f027e017f02402002450d004100200241796a2203200320024b1b2104200141036a417c7120016b21054100210303400240024002400240200120036a2d0000220641187441187522074100480d00200520036b4103710d01200320044f0d020340200120036a220641046a280200200628020072418081828478710d03200341086a22032004490d000c030b0b428080808080202108428080808010210902400240024002400240024002400240024002400240024020064180a2c080006a2d0000417e6a0e030001020a0b200341016a22062002490d0242002108420021090c090b42002108200341016a220a2002490d02420021090c080b42002108200341016a220a2002490d02420021090c070b4280808080802021084280808080102109200120066a2c000041bf7f4a0d060c070b2001200a6a2c0000210a024002400240200641a07e6a0e0e0002020202020202020202020201020b200a41607141a07f460d040c030b200a419f7f4a0d020c030b02402007411f6a41ff0171410c490d002007417e71416e470d02200a4140480d030c020b200a4140480d020c010b2001200a6a2c0000210a0240024002400240200641907e6a0e050100000002000b2007410f6a41ff017141024b0d03200a41404e0d030c020b200a41f0006a41ff017141304f0d020c010b200a418f7f4a0d010b0240200341026a22062002490d00420021090c050b200120066a2c000041bf7f4a0d0242002109200341036a220620024f0d04200120066a2c000041bf7f4c0d05428080808080e00021080c030b4280808080802021080c020b42002109200341026a220620024f0d02200120066a2c000041bf7f4c0d030b428080808080c00021080b42808080801021090b200020082003ad84200984370204200041013602000f0b200641016a21030c020b200341016a21030c010b200320024f0d000340200120036a2c00004100480d012002200341016a2203470d000c030b0b20032002490d000b0b20002001360204200041086a2002360200200041003602000b800701097f024002402001200041036a417c71220220006b2203490d00200120036b22044104490d002004410371210541002106410021010240200220004622070d00410021010240024020022000417f736a41034f0d00410021080c010b4100210803402001200020086a22092c000041bf7f4a6a200941016a2c000041bf7f4a6a200941026a2c000041bf7f4a6a200941036a2c000041bf7f4a6a2101200841046a22080d000b0b20070d00200020026b2102200020086a21090340200120092c000041bf7f4a6a2101200941016a2109200241016a22020d000b0b200020036a210802402005450d0020082004417c716a22092c000041bf7f4a210620054101460d00200620092c000141bf7f4a6a210620054102460d00200620092c000241bf7f4a6a21060b20044102762103200620016a21020340200821062003450d02200341c001200341c001491b220441037121052004410274210702400240200441fc0171220a0d00410021090c010b2006200a4102746a21004100210920062101034020012802002208417f7341077620084106767241818284087120096a200141046a2802002209417f734107762009410676724181828408716a200141086a2802002209417f734107762009410676724181828408716a2001410c6a2802002209417f734107762009410676724181828408716a2109200141106a22012000470d000b0b200320046b2103200620076a2108200941087641ff81fc0771200941ff81fc07716a418180046c41107620026a21022005450d000b2006200a4102746a22092802002201417f734107762001410676724181828408712101024020054101460d0020092802042208417f7341077620084106767241818284087120016a210120054102460d0020092802082209417f7341077620094106767241818284087120016a21010b200141087641ff811c71200141ff81fc07716a418180046c41107620026a21020c010b024020010d0041000f0b2001410371210802400240200141044f0d0041002102410021090c010b2001417c712103410021024100210903402002200020096a22012c000041bf7f4a6a200141016a2c000041bf7f4a6a200141026a2c000041bf7f4a6a200141036a2c000041bf7f4a6a21022003200941046a2209470d000b0b2008450d00200020096a21010340200220012c000041bf7f4a6a2102200141016a21012008417f6a22080d000b0b20020be90203027f017e037f23808080800041306b2203248080808000412721040240024020004290ce005a0d00200021050c010b412721040340200341096a20046a2206417c6a200020004290ce008022054290ce007e7da7220741ffff037141e4006e220841017441da8dc080006a2f00003b00002006417e6a2007200841e4006c6b41ffff037141017441da8dc080006a2f00003b00002004417c6a2104200042ffc1d72f5621062005210020060d000b0b02402005a7220641e3004d0d00200341096a2004417e6a22046a2005a72206200641ffff037141e4006e220641e4006c6b41ffff037141017441da8dc080006a2f00003b00000b024002402006410a490d00200341096a2004417e6a22046a200641017441da8dc080006a2f00003b00000c010b200341096a2004417f6a22046a200641306a3a00000b200220014180a4c080004100200341096a20046a412720046b10db808080002104200341306a24808080800020040b110020003100004101200110fe808080000b110020003502004101200110fe808080000b2301027e200029030022022002423f8722038520037d2002427f55200110fe808080000b110020002903004101200110fe808080000b02000b850601047f2380808080004180016b22022480808080000240024002400240024002400240200128021c22034110710d00024020034120710d004101210320003502004101200110fe80808000450d020c030b20002802002103410021040340200220046a41ff006a413041372003410f712205410a491b20056a3a00002004417f6a210420034110492105200341047621032005450d000b20044180016a22034180014b0d03410121032001410141c48dc080004102200220046a4180016a410020046b10db80808000450d010c020b20002802002103410021040340200220046a41ff006a413041d7002003410f712205410a491b20056a3a00002004417f6a210420034110492105200341047621032005450d000b20044180016a22034180014b0d03410121032001410141c48dc080004102200220046a4180016a410020046b10db808080000d010b2002410c6a4200370200410121032002410136020420024184a4c0800036020020024180a4c080003602082001280214200141186a280200200210d9808080000d0002400240200128021c22034110710d0020034120710d0120003502044101200110fe8080800021030c020b20002802042103410021040340200220046a41ff006a413041d7002003410f712205410a491b20056a3a00002004417f6a210420034110492105200341047621032005450d000b20044180016a22034180014b0d042001410141c48dc080004102200220046a4180016a410020046b10db8080800021030c010b20002802042103410021040340200220046a41ff006a413041372003410f712205410a491b20056a3a00002004417f6a210420034110492105200341047621032005450d000b20044180016a22034180014b0d042001410141c48dc080004102200220046a4180016a410020046b10db8080800021030b20024180016a24808080800020030f0b200341800141c88dc08000109481808000000b200341800141c88dc08000109481808000000b200341800141c88dc08000109481808000000b200341800141c88dc08000109481808000000b21002001280214418ca4c08000410b200141186a28020028020c118280808000000b210020012802144197a4c08000410e200141186a28020028020c118280808000000b5e01017f23808080800041306b2201248080808000200141186a420137020020014101360210200141b8a4c0800036020c200141a9808080003602282001200141246a36021420012001412f6a3602242001410c6a200010f680808000000b5e01017f23808080800041306b2201248080808000200141186a420137020020014101360210200141dca4c0800036020c200141aa808080003602282001200141246a36021420012001412f6a3602242001410c6a200010f680808000000b990101017f23808080800041c0006b22052480808080002005200136020c200520003602082005200336021420052002360210200541186a410c6a4202370200200541306a410c6a41a3808080003602002005410236021c200541e8a4c08000360218200541a2808080003602342005200541306a3602202005200541106a3602382005200541086a360230200541186a200410f680808000000bc404010b7f2000280204210320002802002104200028020821054100210641002107410021084100210902400340200941ff01710d0102400240200820024b0d000340200120086a210a02400240024002400240200220086b220b4108490d00200a41036a417c712200200a460d012000200a6b2200450d014100210c0340200a200c6a2d0000410a460d052000200c41016a220c470d000b2000200b41786a220d4b0d030c020b024020022008470d00200221080c060b4100210c0340200a200c6a2d0000410a460d04200b200c41016a220c470d000b200221080c050b200b41786a210d410021000b0340200a20006a220c41046a2802002209418a94a8d0007341fffdfb776a2009417f7371200c280200220c418a94a8d0007341fffdfb776a200c417f737172418081828478710d01200041086a2200200d4d0d000b0b02402000200b470d00200221080c030b03400240200a20006a2d0000410a470d002000210c0c020b200b200041016a2200470d000b200221080c020b200c20086a220041016a21080240200020024f0d00200120006a2d0000410a470d00410021092008210d200821000c030b200820024d0d000b0b410121092007210d2002210020072002460d020b0240024020052d0000450d00200441f8a4c080004104200328020c118280808000000d010b200120076a210c200020076b210a4100210b024020002007460d00200a200c6a417f6a2d0000410a46210b0b2005200b3a0000200d21072004200c200a200328020c11828080800000450d010b0b410121060b20060b5801027f20002802042102200028020021030240200028020822002d0000450d00200341f8a4c080004104200228020c11828080800000450d0041010f0b20002001410a463a0000200320012002280210118380808000000be40302057f017e23808080800041c0006b220524808080800041012106024020002d00040d0020002d0005210702402000280200220828021c22094104710d0041012106200828021441ffa4c0800041fca4c08000200741ff017122071b4102410320071b200841186a28020028020c118280808000000d0141012106200828021420012002200828021828020c118280808000000d0141012106200828021441e4a4c080004102200828021828020c118280808000000d0120032008200428020c1183808080000021060c010b0240200741ff01710d004101210620082802144181a5c080004103200841186a28020028020c118280808000000d01200828021c21090b41012106200541013a001b200541346a41d08fc080003602002005200829021437020c20052005411b6a360214200520082902083702242008290200210a200520093602382005200828021036022c200520082d00203a003c2005200a37021c20052005410c6a3602302005410c6a20012002108a818080000d002005410c6a41e4a4c080004102108a818080000d0020032005411c6a200428020c118380808000000d0020052802304184a5c080004102200528023428020c1182808080000021060b200041013a0005200020063a0004200541c0006a24808080800020000bf70202057f017e23808080800041c0006b22032480808080002000280200210441012105024020002d00080d0002402000280204220628021c22074104710d0041012105200628021441ffa4c080004189a5c0800020041b4102410120041b200641186a28020028020c118280808000000d0120012006200228020c1183808080000021050c010b024020040d00410121052006280214418aa5c080004102200641186a28020028020c118280808000000d01200628021c21070b41012105200341013a001b200341346a41d08fc080003602002003200629021437020c20032003411b6a3602142003200629020837022420062902002108200320073602382003200628021036022c200320062d00203a003c2003200837021c20032003410c6a36023020012003411c6a200228020c118380808000000d0020032802304184a5c080004102200328023428020c1182808080000021050b200020053a00082000200441016a360200200341c0006a24808080800020000bf90202047f017e23808080800041c0006b220324808080800041012104024020002d00040d0020002d00052104024002402000280200220528021c22064104710d00200441ff0171450d0141012104200528021441ffa4c080004102200541186a28020028020c11828080800000450d010c020b0240200441ff01710d00410121042005280214418ea5c080004101200541186a28020028020c118280808000000d02200528021c21060b41012104200341013a001b200341346a41d08fc080003602002003200529021437020c20032003411b6a3602142003200529020837022420052902002107200320063602382003200528021036022c200320052d00203a003c2003200737021c20032003410c6a36023020012003411c6a200228020c118380808000000d0120032802304184a5c080004102200328023428020c1182808080000021040c010b20012005200228020c1183808080000021040b200041013a0005200020043a0004200341c0006a24808080800020000b120020004194a5c08000200110d9808080000ba00705027f017e027f017e017f024020014107712202450d000240024020002802a001220341294f0d00024020030d00200041003602a0010c030b200241027441aca5c080006a35020021042003417f6a41ffffffff0371220241016a220541037121060240200241034f0d0042002107200021020c020b200541fcffffff07712105420021072000210203402002200235020020047e20077c22073e0200200241046a2208200835020020047e20074220887c22073e0200200241086a2208200835020020047e20074220887c22073e02002002410c6a2208200835020020047e20074220887c22073e020020074220882107200241106a21022005417c6a22050d000c020b0b2003412841d892c08000109581808000000b02402006450d0003402002200235020020047e20077c22073e0200200241046a2102200742208821072006417f6a22060d000b0b024002402007a72202450d00200341274b0d01200020034102746a2002360200200341016a21030b200020033602a0010c010b4128412841d892c0800010f980808000000b024002402001410871450d0002400240024020002802a001220341294f0d00024020030d00410021030c030b2003417f6a41ffffffff0371220241016a220541037121060240200241034f0d0042002104200021020c020b200541fcffffff0771210542002104200021020340200220023502004280c2d72f7e20047c22043e0200200241046a220820083502004280c2d72f7e20044220887c22043e0200200241086a220820083502004280c2d72f7e20044220887c22043e02002002410c6a220820083502004280c2d72f7e20044220887c22043e020020044220882104200241106a21022005417c6a22050d000c020b0b2003412841d892c08000109581808000000b02402006450d000340200220023502004280c2d72f7e20047c22043e0200200241046a2102200442208821042006417f6a22060d000b0b2004a72202450d00200341274b0d02200020034102746a2002360200200341016a21030b200020033602a0010b02402001411071450d00200041fca5c08000410210eb808080001a0b02402001412071450d0020004184a6c08000410410eb808080001a0b0240200141c00071450d0020004194a6c08000410710eb808080001a0b0240200141800171450d00200041b0a6c08000410e10eb808080001a0b0240200141800271450d00200041e8a6c08000411b10eb808080001a0b20000f0b4128412841d892c0800010f980808000000bc53103017f047e1c7f23808080800041a00a6b2204248080808000024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240200129030022054200510d00200129030822064200510d01200129031022074200510d02200520077c22082005540d0320052006540d04200341104d0d0520012c001a210920012f01182101200420053e0200200441014102200542808080801054220a1b3602a001200441002005422088a7200a1b360204200441086a4100419801108a8e8080001a200420063e02a401200441014102200642808080801054220a1b3602c402200441002006422088a7200a1b3602a801200441a4016a41086a4100419801108a8e8080001a200420073e02c802200441014102200742808080801054220a1b3602e803200441002007422088a7200a1b3602cc02200441c8026a41086a4100419801108a8e8080001a200441f0036a4100419c01108a8e8080001a200441013602ec032004410136028c052001ad4230864230872008427f7c797d42c29ac1e8047e4280a1cda0b4027c422088a7220a411074411075210b024002402001411074411075220c4100480d002004200110ea808080001a200441a4016a200110ea808080001a200441c8026a200110ea808080001a0c010b200441ec036a4100200c6b41107441107510ea808080001a0b02400240200b417f4a0d0020044100200b6b41107441107522011090818080001a200441a4016a20011090818080001a200441c8026a20011090818080001a0c010b200441ec036a200a41ffff03711090818080001a0b20042802a001210d200441fc086a200441a00110848e8080001a2004200d36029c0a200d20042802e803220e200d200e4b1b220f41294f0d060240200f0d004100210f0c090b200f41017121100240200f4101470d0041002111410021120c080b200f417e71211341002111200441fc086a2101200441c8026a210a410021120340200120012802002214200a2802006a220c20114101716a2215360200200141046a221120112802002216200a41046a2802006a2211200c2014492015200c49726a220c3602002011201649200c201149722111200a41086a210a200141086a21012013201241026a2212470d000c080b0b41d8a8c08000411c41f4a8c0800010f880808000000b4184a9c08000411d41a4a9c0800010f880808000000b41b4a9c08000411c41d0a9c0800010f880808000000b4198abc08000413641d0abc0800010f880808000000b41d0aac0800041374188abc0800010f880808000000b41e0a9c08000412d4190aac0800010f880808000000b200f412841d892c08000109581808000000b02402010450d00200441fc086a201241027422016a220a200a280200220a200441c8026a20016a2802006a220120116a220c3602002001200a49200c2001497221110b2011410171450d00200f41274b0d01200441fc086a200f4102746a4101360200200f41016a210f0b2004200f36029c0a200428028c052212200f2012200f4b1b220141294f0d01200141027421010240024003402001450d01417f2001417c6a2201200441fc086a6a280200220a2001200441ec036a6a280200220c47200a200c4b1b220a450d000c020b0b417f410020011b210a0b0240200a2009480d000240200d0d004100210d0c050b200d417f6a41ffffffff0371220141016a220c410371210a0240200141034f0d0020042101420021050c040b200c41fcffffff0771210c2004210142002105034020012001350200420a7e20057c22053e0200200141046a22112011350200420a7e20054220887c22053e0200200141086a22112011350200420a7e20054220887c22053e02002001410c6a22112011350200420a7e20054220887c22053e020020054220882105200141106a2101200c417c6a220c0d000c040b0b200b41016a210b0c0b0b4128412841d892c0800010f980808000000b2001412841d892c08000109581808000000b0240200a450d00034020012001350200420a7e20057c22053e0200200141046a210120054220882105200a417f6a220a0d000b0b2005a72201450d00200d41274b0d012004200d4102746a2001360200200d41016a210d0b2004200d3602a00120042802c402221441294f0d0141002115410021012014450d032014417f6a41ffffffff0371220141016a220c410371210a0240200141034f0d00200441a4016a2101420021050c030b200c41fcffffff0771210c200441a4016a210142002105034020012001350200420a7e20057c22053e0200200141046a22112011350200420a7e20054220887c22053e0200200141086a22112011350200420a7e20054220887c22053e02002001410c6a22112011350200420a7e20054220887c22053e020020054220882105200141106a2101200c417c6a220c0d000c030b0b4128412841d892c0800010f980808000000b2014412841d892c08000109581808000000b0240200a450d00034020012001350200420a7e20057c22053e0200200141046a210120054220882105200a417f6a220a0d000b0b02402005a722010d00201421010c010b201441274b0d01200441a4016a20144102746a2001360200201441016a21010b200420013602c402200e450d02200e417f6a41ffffffff0371220141016a220c410371210a0240200141034f0d00200441c8026a2101420021050c020b200c41fcffffff0771210c200441c8026a210142002105034020012001350200420a7e20057c22053e0200200141046a22112011350200420a7e20054220887c22053e0200200141086a22112011350200420a7e20054220887c22053e02002001410c6a22112011350200420a7e20054220887c22053e020020054220882105200141106a2101200c417c6a220c0d000c020b0b4128412841d892c0800010f980808000000b0240200a450d00034020012001350200420a7e20057c22053e0200200141046a210120054220882105200a417f6a220a0d000b0b02402005a722010d002004200e3602e8030c020b200e41274b0d02200441c8026a200e4102746a2001360200200e41016a21150b200420153602e8030b20044190056a200441ec036a41a00110848e8080001a200420123602b00620044190056a410110ea808080002117200428028c052101200441b4066a200441ec036a41a00110848e8080001a200420013602d407200441b4066a410210ea808080002118200428028c052101200441d8076a200441ec036a41a00110848e8080001a200420013602f808200441d8076a410310ea8080800021190240024020042802a001221220042802f808221a2012201a4b1b221041284b0d0020044190056a417c6a210e200441b4066a417c6a210d200441d8076a417c6a210f200428028c05211b20042802b006211c20042802d407211d4100211e0340201e211f201041027421010240024003402001450d01417f200f20016a280200220a2001417c6a220120046a280200220c47200a200c4b1b220a450d000c020b0b417f410020011b210a0b410021200240024002400240024002400240024002400240024002400240024002400240024002400240024002400240200a41014b0d0002402010450d00410121112010410171212141002112024020104101460d002010417e712113410021124101211120042101200441d8076a210a0340200120012802002214200a280200417f736a220c20114101716a2215360200200141046a221120112802002216200a41046a280200417f736a2211200c2014492015200c49726a220c3602002011201649200c201149722111200a41086a210a200141086a21012013201241026a2212470d000b0b02402021450d002004201241027422016a220a200a280200220a201920016a280200417f736a220120116a220c3602002001200a49200c2001497221110b2011410171450d020b200420103602a00141082120201021120b2012201d2012201d4b1b221341294f0d01201341027421010240024003402001450d01417f200d20016a280200220a2001417c6a220120046a280200220c47200a200c4b1b220a450d000c020b0b417f410020011b210a0b02400240200a41014d0d00201221130c010b02402013450d00410121112013410171212141002112024020134101460d002013417e712110410021124101211120042101200441b4066a210a0340200120012802002214200a280200417f736a220c20114101716a2215360200200141046a221120112802002216200a41046a280200417f736a2211200c2014492015200c49726a220c3602002011201649200c201149722111200a41086a210a200141086a21012010201241026a2212470d000b0b02402021450d002004201241027422016a220a200a280200220a201820016a280200417f736a220120116a220c3602002001200a49200c2001497221110b2011410171450d040b200420133602a001202041047221200b2013201c2013201c4b1b222141294f0d03202141027421010240024003402001450d01417f200e20016a280200220a2001417c6a220120046a280200220c47200a200c4b1b220a450d000c020b0b417f410020011b210a0b02400240200a41014d0d00201321210c010b02402021450d00410121112021410171211041002112024020214101460d002021417e71211341002112410121112004210120044190056a210a0340200120012802002214200a280200417f736a220c20114101716a2215360200200141046a221120112802002216200a41046a280200417f736a2211200c2014492015200c49726a220c3602002011201649200c201149722111200a41086a210a200141086a21012013201241026a2212470d000b0b02402010450d002004201241027422016a220a200a280200220a201720016a280200417f736a220120116a220c3602002001200a49200c2001497221110b2011410171450d060b200420213602a001202041026a21200b2021201b2021201b4b1b221041294f0d05201041027421010240024003402001450d01417f2001417c6a2201200441ec036a6a280200220a200120046a280200220c47200a200c4b1b220a450d000c020b0b417f410020011b210a0b02400240200a41014d0d00202121100c010b02402010450d00410121112010410171212141002112024020104101460d002010417e712113410021124101211120042101200441ec036a210a0340200120012802002214200a280200417f736a220c20114101716a2215360200200141046a221120112802002216200a41046a280200417f736a2211200c2014492015200c49726a220c3602002011201649200c201149722111200a41086a210a200141086a21012013201241026a2212470d000b0b02402021450d002004201241027422016a220a200a280200220a200441ec036a20016a280200417f736a220120116a220c3602002001200a49200c2001497221110b2011410171450d080b200420103602a001202041016a21200b201f2003460d0b2002201f6a202041306a3a0000201020042802c4022222201020224b1b220141294f0d07201f41016a211e200141027421010240024003402001450d01417f2001417c6a2201200441a4016a6a280200220a200120046a280200220c47200a200c4b1b2213450d000c020b0b417f410020011b21130b200441fc086a200441a00110848e8080001a2004201036029c0a201020042802e8032223201020234b1b222041294f0d080240024020200d00410021200c010b202041017121244100211141002112024020204101460d002020417e71212141002111200441fc086a2101200441c8026a210a410021120340200120012802002214200a2802006a220c20114101716a2215360200200141046a221120112802002216200a41046a2802006a2211200c2014492015200c49726a220c3602002011201649200c201149722111200a41086a210a200141086a21012021201241026a2212470d000b0b02402024450d00200441fc086a201241027422016a220a200a280200220a200441c8026a20016a2802006a220120116a220c3602002001200a49200c2001497221110b2011410171450d00202041274b0d0a200441fc086a20204102746a4101360200202041016a21200b2004202036029c0a201b2020201b20204b1b220141294f0d0a200141027421010240024003402001450d01417f2001417c6a2201200441fc086a6a280200220a2001200441ec036a6a280200220c47200a200c4b1b220a450d000c020b0b417f410020011b210a0b024002400240201320094822010d00200a20094e0d010b200a20094e0d1c20010d010c1b0b41002114410021122010450d0f2010417f6a41ffffffff0371220141016a220c410371210a0240200141034f0d0020042101420021050c0f0b200c41fcffffff0771210c2004210142002105034020012001350200420a7e20057c22053e0200200141046a22112011350200420a7e20054220887c22053e0200200141086a22112011350200420a7e20054220887c22053e02002001410c6a22112011350200420a7e20054220887c22053e020020054220882105200141106a2101200c417c6a220c0d000c0f0b0b2004410110ea808080001a20042802a0012201200428028c05220a2001200a4b1b220141294f0d0c200141027421012004417c6a2111200441ec036a417c6a21120240024003402001450d01201120016a210a201220016a210c2001417c6a2101417f200c280200220c200a280200220a47200c200a4b1b220a450d000c020b0b417f410020011b210a0b200a4102490d190c1a0b41e892c08000411a41d892c0800010f880808000000b2013412841d892c08000109581808000000b41e892c08000411a41d892c0800010f880808000000b2021412841d892c08000109581808000000b41e892c08000411a41d892c0800010f880808000000b2010412841d892c08000109581808000000b41e892c08000411a41d892c0800010f880808000000b2001412841d892c08000109581808000000b2020412841d892c08000109581808000000b4128412841d892c0800010f980808000000b2001412841d892c08000109581808000000b2003200341a0aac0800010f980808000000b2001412841d892c08000109581808000000b0240200a450d00034020012001350200420a7e20057c22053e0200200141046a210120054220882105200a417f6a220a0d000b0b02402005a722010d00201021120c010b201041274b0d01200420104102746a2001360200201041016a21120b200420123602a0012022450d022022417f6a41ffffffff0371220141016a220c410371210a0240200141034f0d00200441a4016a2101420021050c020b200c41fcffffff0771210c200441a4016a210142002105034020012001350200420a7e20057c22053e0200200141046a22112011350200420a7e20054220887c22053e0200200141086a22112011350200420a7e20054220887c22053e02002001410c6a22112011350200420a7e20054220887c22053e020020054220882105200141106a2101200c417c6a220c0d000c020b0b4128412841d892c0800010f980808000000b0240200a450d00034020012001350200420a7e20057c22053e0200200141046a210120054220882105200a417f6a220a0d000b0b02402005a722010d00202221140c010b202241274b0d01200441a4016a20224102746a2001360200202241016a21140b200420143602c402024020230d00410021230c030b2023417f6a41ffffffff0371220141016a220c410371210a0240200141034f0d00200441c8026a2101420021050c020b200c41fcffffff0771210c200441c8026a210142002105034020012001350200420a7e20057c22053e0200200141046a22112011350200420a7e20054220887c22053e0200200141086a22112011350200420a7e20054220887c22053e02002001410c6a22112011350200420a7e20054220887c22053e020020054220882105200141106a2101200c417c6a220c0d000c020b0b4128412841d892c0800010f980808000000b0240200a450d00034020012001350200420a7e20057c22053e0200200141046a210120054220882105200a417f6a220a0d000b0b2005a72201450d00202341274b0d03200441c8026a20234102746a2001360200202341016a21230b200420233602e8032012201a2012201a4b1b221041284d0d000b0b2010412841d892c08000109581808000000b4128412841d892c0800010f980808000000b4128412841d892c0800010f980808000000b2002201e6a2112201f2101417f210a024003402001417f460d01200a41016a210a200220016a210c2001417f6a22112101200c2d00004139460d000b200220116a220c41016a220120012d000041016a3a0000201141026a201e4f0d01200c41026a4130200a108a8e8080001a0c010b02400240201e0d00413121010c010b200241313a00000240201f0d00413021010c010b41302101200241016a4130201f108a8e8080001a0b0240201e20034f0d00201220013a0000200b41016a210b201f41026a211e0c010b201e200341b0aac0800010f980808000000b0240201e20034b0d002000200b3b01082000201e36020420002002360200200441a00a6a2480808080000f0b201e200341c0aac08000109581808000000b872a03017f037e1a7f23808080800041c0066b22052480808080000240024002400240024002400240024002400240024002400240024002400240024002400240200129030022064200510d00200129030822074200510d01200129031022084200510d02200620087c2006540d0320062007540d0420012f01182101200520063e020c20054101410220064280808080105422091b3602ac01200541002006422088a720091b360210200541146a4100419801108a8e8080001a200541b4016a4100419c01108a8e8080001a200541013602b001200541013602d0022001ad4230864230872006427f7c797d42c29ac1e8047e4280a1cda0b4027c422088a72209411074411075210a024002402001411074411075220b4100480d002005410c6a200110ea808080001a0c010b200541b0016a4100200b6b41107441107510ea808080001a0b02400240200a417f4a0d002005410c6a4100200a6b4110744110751090818080001a0c010b200541b0016a200941ffff03711090818080001a0b20052802d002210c2005419c056a200541b0016a41a00110848e8080001a2005200c3602bc062003210d02402003410a490d0002400240200c41284d0d00200c21010c010b2005419c056a41786a210e2003210d200c2101034002402001450d002001417f6a41ffffffff0371220941016a220b410171210f200141027421010240024020090d002005419c056a20016a2101420021060c010b200b41feffffff07712109200e20016a2101420021060340200141046a220b2006422086200b350200842206428094ebdc038022073e0200200120062007428094ebdc037e7d4220862001350200842206428094ebdc038022073e020020062007428094ebdc037e7d2106200141786a21012009417e6a22090d000b200141086a21010b200f450d002001417c6a22012006422086200135020084428094ebdc03803e02000b200d41776a220d41094d0d0220052802bc0622014129490d000b0b2001412841d892c08000109581808000000b200d41027441d4a5c080006a2802002209450d0520052802bc06220141294f0d060240024020010d00410021010c010b2001417f6a41ffffffff0371220b41016a220f410171210d200141027421012009ad210602400240200b0d002005419c056a20016a2101420021070c010b200f41feffffff0771210920012005419c056a6a41786a2101420021070340200141046a220b2007422086200b35020084220720068022083e020020012007200820067e7d422086200135020084220720068022083e02002007200820067e7d2107200141786a21012009417e6a22090d000b200141086a21010b0240200d450d002001417c6a220120074220862001350200842006803e02000b20052802bc0621010b200120052802ac012210200120104b1b221141294f0d07024020110d00410021110c0a0b20114101712112024020114101470d004100210d4100210f0c090b2011417e7121134100210d2005419c056a21012005410c6a21094100210f034020012001280200220e20092802006a220b200d4101716a2214360200200141046a220d200d2802002215200941046a2802006a220d200b200e492014200b49726a220b360200200d201549200b200d4972210d200941086a2109200141086a21012013200f41026a220f470d000c090b0b41d8a8c08000411c41e0abc0800010f880808000000b4184a9c08000411d41f0abc0800010f880808000000b41b4a9c08000411c4180acc0800010f880808000000b4198abc08000413641f0acc0800010f880808000000b41d0aac08000413741e0acc0800010f880808000000b419f93c08000411b41d892c0800010f880808000000b2001412841d892c08000109581808000000b2011412841d892c08000109581808000000b02402012450d002005419c056a200f41027422016a2209200928020022092005410c6a20016a2802006a2201200d6a220b3602002001200949200b20014972210d0b200d410171450d00201141274b0d012005419c056a20114102746a4101360200201141016a21110b200520113602bc062011200c2011200c4b1b220141294f0d01200141027421010240024003402001450d01417f2001417c6a2201200541b0016a6a280200220920012005419c056a6a280200220b472009200b4b1b2209450d000c020b0b417f410020011b21090b0240200941014b0d00200a41016a210a0c050b024020100d00410021100c040b2010417f6a41ffffffff0371220141016a220b41037121090240200141034f0d002005410c6a2101420021060c030b200b41fcffffff0771210b2005410c6a210142002106034020012001350200420a7e20067c22063e0200200141046a220d200d350200420a7e20064220887c22063e0200200141086a220d200d350200420a7e20064220887c22063e02002001410c6a220d200d350200420a7e20064220887c22063e020020064220882106200141106a2101200b417c6a220b0d000c030b0b4128412841d892c0800010f980808000000b2001412841d892c08000109581808000000b02402009450d00034020012001350200420a7e20067c22063e0200200141046a2101200642208821062009417f6a22090d000b0b2006a72201450d00201041274b0d022005410c6a20104102746a2001360200201041016a21100b200520103602ac010b4100210e02400240200a411074411075220120044110744110752209480d00200a20046b4110744110752003200120096b2003491b220d0d014100210e0b4100210d0c020b200541d4026a200541b0016a41a00110848e8080001a2005200c3602f403200541d4026a410110ea80808000211620052802d0022101200541f8036a200541b0016a41a00110848e8080001a2005200136029805200541f8036a410210ea80808000211720052802d00221012005419c056a200541b0016a41a00110848e8080001a200520013602bc06200541b0016a417c6a2113200541d4026a417c6a2115200541f8036a417c6a21142005419c056a417c6a210e2005419c056a410310ea80808000211820052802d002210c20052802f4032119200528029805211a20052802bc06211b20052802ac0121104100211c02400340201c211d0240024002400240024002400240024002400240024002400240201041294f0d00201d41016a211c2010410274210b410021010240024002400340200b2001460d012005410c6a20016a2109200141046a21012009280200450d000b2010201b2010201b4b1b221e41294f0d04201e41027421010240024003402001450d01417f200e20016a28020022092001417c6a22012005410c6a6a280200220b472009200b4b1b2209450d000c020b0b417f410020011b21090b4100211f0240200941024f0d000240201e450d004101210f201e410171211f410021100240201e4101460d00201e417e712120410021104101210f2005410c6a21012005419c056a210903402001200128020022112009280200417f736a220b200f4101716a2212360200200141046a220f200f2802002221200941046a280200417f736a220f200b2011492012200b49726a220b360200200f202149200b200f4972210f200941086a2109200141086a21012020201041026a2210470d000b0b0240201f450d002005410c6a201041027422016a220920092802002209201820016a280200417f736a2201200f6a220b3602002001200949200b20014972210f0b200f410171450d080b2005201e3602ac014108211f201e21100b2010201a2010201a4b1b222041294f0d072020410274210103402001450d02417f201420016a28020022092001417c6a22012005410c6a6a280200220b472009200b4b1b2209450d000c030b0b200d20034b0d04200d201d460d132002201d6a4130200d201d6b108a8e8080001a0c130b417f410020011b21090b02400240200941014d0d00201021200c010b02402020450d004101210f2020410171212241002110024020204101460d002020417e71211e410021104101210f2005410c6a2101200541f8036a210903402001200128020022112009280200417f736a220b200f4101716a2212360200200141046a220f200f2802002221200941046a280200417f736a220f200b2011492012200b49726a220b360200200f202149200b200f4972210f200941086a2109200141086a2101201e201041026a2210470d000b0b02402022450d002005410c6a201041027422016a220920092802002209201720016a280200417f736a2201200f6a220b3602002001200949200b20014972210f0b200f410171450d070b200520203602ac01201f410472211f0b20202019202020194b1b221e41294f0d06201e41027421010240024003402001450d01417f201520016a28020022092001417c6a22012005410c6a6a280200220b472009200b4b1b2209450d000c020b0b417f410020011b21090b02400240200941014d0d002020211e0c010b0240201e450d004101210f201e4101712122410021100240201e4101460d00201e417e712120410021104101210f2005410c6a2101200541d4026a210903402001200128020022112009280200417f736a220b200f4101716a2212360200200141046a220f200f2802002221200941046a280200417f736a220f200b2011492012200b49726a220b360200200f202149200b200f4972210f200941086a2109200141086a21012020201041026a2210470d000b0b02402022450d002005410c6a201041027422016a220920092802002209201620016a280200417f736a2201200f6a220b3602002001200949200b20014972210f0b200f410171450d090b2005201e3602ac01201f41026a211f0b201e200c201e200c4b1b221041294f0d08201041027421010240024003402001450d01417f201320016a28020022092001417c6a22012005410c6a6a280200220b472009200b4b1b2209450d000c020b0b417f410020011b21090b02400240200941014d0d00201e21100c010b02402010450d004101210f2010410171212241002111024020104101460d002010417e71211e410021114101210f2005410c6a2101200541b0016a210903402001200128020022122009280200417f736a220b200f4101716a2221360200200141046a220f200f2802002220200941046a280200417f736a220f200b2012492021200b49726a220b360200200f202049200b200f4972210f200941086a2109200141086a2101201e201141026a2211470d000b0b02402022450d002005410c6a201141027422016a220920092802002209200541b0016a20016a280200417f736a2201200f6a220b3602002001200949200b20014972210f0b200f410171450d0b0b200520103602ac01201f41016a211f0b0240201d2003460d002002201d6a201f41306a3a0000201041294f0d0b024020100d00410021100c0e0b2010417f6a41ffffffff0371220141016a220b41037121090240200141034f0d002005410c6a2101420021060c0d0b200b41fcffffff0771210b2005410c6a210142002106034020012001350200420a7e20067c22063e0200200141046a220f200f350200420a7e20064220887c22063e0200200141086a220f200f350200420a7e20064220887c22063e02002001410c6a220f200f350200420a7e20064220887c22063e020020064220882106200141106a2101200b417c6a220b0d000c0d0b0b2003200341c0acc0800010f980808000000b2010412841d892c08000109581808000000b201e412841d892c08000109581808000000b200d200341d0acc08000109581808000000b41e892c08000411a41d892c0800010f880808000000b2020412841d892c08000109581808000000b41e892c08000411a41d892c0800010f880808000000b201e412841d892c08000109581808000000b41e892c08000411a41d892c0800010f880808000000b2010412841d892c08000109581808000000b41e892c08000411a41d892c0800010f880808000000b2010412841d892c08000109581808000000b02402009450d00034020012001350200420a7e20067c22063e0200200141046a2101200642208821062009417f6a22090d000b0b2006a72201450d00201041274b0d022005410c6a20104102746a2001360200201041016a21100b200520103602ac01201c200d470d000b4101210e0c020b4128412841d892c0800010f980808000000b4128412841d892c0800010f980808000000b0240024002400240024002400240200c41294f0d000240200c0d004100210c0c030b200c417f6a41ffffffff0371220141016a220b41037121090240200141034f0d00200541b0016a2101420021060c020b200b41fcffffff0771210b200541b0016a21014200210603402001200135020042057e20067c22063e0200200141046a220f200f35020042057e20064220887c22063e0200200141086a220f200f35020042057e20064220887c22063e02002001410c6a220f200f35020042057e20064220887c22063e020020064220882106200141106a2101200b417c6a220b0d000c020b0b200c412841d892c08000109581808000000b02402009450d0003402001200135020042057e20067c22063e0200200141046a2101200642208821062009417f6a22090d000b0b2006a72201450d00200c41274b0d01200541b0016a200c4102746a2001360200200c41016a210c0b2005200c3602d0022010200c2010200c4b1b220141294f0d0120014102742101024002400240024003402001450d01417f2001417c6a2201200541b0016a6a280200220920012005410c6a6a280200220b472009200b4b1b2209450d000b200941ff01714101460d010c070b200e20014571450d06200d417f6a220120034f0d01200220016a2d0000410171450d060b200d20034b0d042002200d6a210f410021012002210902400340200d2001460d01200141016a21012009417f6a2209200d6a220b2d00004139460d000b200b200b2d000041016a3a0000200d20016b41016a200d4f0d06200b41016a41302001417f6a108a8e8080001a0c060b02400240200d0d00413121010c010b200241313a000041302101200d4101460d0041302101200241016a4130200d417f6a108a8e8080001a0b200a411074418080046a411075220a20044110744110754a0d010c050b200120034190acc0800010f980808000000b200d20034f0d03200f20013a0000200d41016a210d0c030b4128412841d892c0800010f980808000000b2001412841d892c08000109581808000000b200d200341a0acc08000109581808000000b200d20034b0d010b2000200a3b01082000200d36020420002002360200200541c0066a2480808080000f0b200d200341b0acc08000109581808000000b0d0020002802001a037f0c000b0b870101017f23808080800041306b22032480808080002003200036020020032001360204200341086a410c6a4202370200200341206a410c6a4185808080003602002003410236020c200341b4adc0800036020820034185808080003602242003200341206a3602102003200341046a36022820032003360220200341086a200210f680808000000b870101017f23808080800041306b22032480808080002003200036020020032001360204200341086a410c6a4202370200200341206a410c6a4185808080003602002003410236020c200341d4adc0800036020820034185808080003602242003200341206a3602102003200341046a36022820032003360220200341086a200210f680808000000b870101017f23808080800041306b22032480808080002003200036020020032001360204200341086a410c6a4202370200200341206a410c6a4185808080003602002003410236020c20034188aec0800036020820034185808080003602242003200341206a3602102003200341046a36022820032003360220200341086a200210f680808000000b130020002001200220032004109881808000000bf70901057f23808080800041f0006b22052480808080002005200336020c20052002360208024002402001418102490d004180022106024020002c00800241bf7f4a0d0041ff01210620002c00ff0141bf7f4a0d0041fe01210620002c00fe0141bf7f4a0d0041fd0121060b2005200636021420052000360210410521064198aec0800021070c010b2005200136021420052000360210410021064180adc0800021070b2005200636021c2005200736021802400240024002400240200220014b22060d00200320014b0d00200220034b0d01024002402002450d00200220014f0d00200020026a2c00004140480d010b200321020b20052002360220200121030240200220014f0d0041002002417d6a2203200320024b1b2203200241016a22064b0d03024020032006460d00200020066a200020036a22086b21060240200020026a22092c000041bf7f4c0d002006417f6a21070c010b20032002460d0002402009417f6a22022c000041bf7f4c0d002006417e6a21070c010b20082002460d0002402009417e6a22022c000041bf7f4c0d002006417d6a21070c010b20082002460d0002402009417d6a22022c000041bf7f4c0d002006417c6a21070c010b20082002460d002006417b6a21070b200720036a21030b02402003450d0002400240200120034b0d0020012003460d010c070b200020036a2c000041bf7f4c0d060b200120036b21010b2001450d030240024002400240200020036a22012c00002202417f4a0d0020012d0001413f7121002002411f7121062002415f4b0d01200641067420007221010c020b2005200241ff0171360224410121020c020b200041067420012d0002413f717221000240200241704f0d0020002006410c747221010c010b200041067420012d0003413f71722006411274418080f00071722201418080c400460d050b20052001360224410121022001418001490d00410221022001418010490d0041034104200141808004491b21020b200520033602282005200220036a36022c200541306a410c6a4205370200200541ec006a41a280808000360200200541e4006a41a280808000360200200541dc006a41ac80808000360200200541c8006a410c6a41ad8080800036020020054105360234200541a0afc08000360230200541858080800036024c2005200541c8006a3602382005200541186a3602682005200541106a3602602005200541286a3602582005200541246a3602502005200541206a360248200541306a200410f680808000000b20052002200320061b360228200541306a410c6a4203370200200541dc006a41a280808000360200200541c8006a410c6a41a28080800036020020054103360234200541e0afc08000360230200541858080800036024c2005200541c8006a3602382005200541186a3602582005200541106a3602502005200541286a360248200541306a200410f680808000000b200541e4006a41a280808000360200200541dc006a41a280808000360200200541c8006a410c6a418580808000360200200541306a410c6a420437020020054104360234200541c0aec08000360230200541858080800036024c2005200541c8006a3602382005200541186a3602602005200541106a36025820052005410c6a3602502005200541086a360248200541306a200410f680808000000b2003200641e8b0c08000109681808000000b200410a081808000000b20002001200320012004109781808000000b8a0301077f41012107024002402002450d00200120024101746a210820004180fe037141087621094100210a200041ff0171210b0340200141026a210c200a20012d000122026a210d024020012d000022012009460d00200120094b0d02200d210a200c2101200c2008460d020c010b024002400240200a200d4b0d00200d20044b0d012003200a6a210103402002450d032002417f6a210220012d0000210a200141016a2101200a200b470d000b410021070c050b200a200d4184b2c08000109681808000000b200d20044184b2c08000109581808000000b200d210a200c2101200c2008470d000b0b2006450d00200520066a210b200041ffff0371210a200520064100476a21024101210703400240024020052d00002201411874411875220d4100480d00200221050c010b02402002200b460d00200241016a2105200d41ff007141087420022d00007221010c010b41f4b1c0800010a081808000000b200a20016b220a4100480d012007410173210720052005200b4722016a210220010d000b0b20074101710ba10201017f0240200041204f0d0041000f0b4101210102400240200041ff00490d00200041808004490d0102400240200041808008490d000240200041d0b8736a41d0ba2b4f0d0041000f0b0240200041b5d9736a41054f0d0041000f0b0240200041e28b746a41e20b4f0d0041000f0b02402000419fa8746a419f184f0d0041000f0b0240200041dee2746a410e4f0d0041000f0b02402000417e71419ef00a470d0041000f0b200041607141e0cd0a470d0141000f0b20004194b2c08000412c41ecb2c0800041c40141b0b4c0800041c2031099818080000f0b41002101200041c691756a4106490d002000418080bc7f6a41f083744921010b20010f0b200041f2b7c08000412841c2b8c08000419f0241e1bac0800041af021099818080000bed0201037f2380808080004180016b220224808080800002400240024002400240200128021c22034110710d0020034120710d0120003502004101200110fe8080800021000c020b20002802002100410021030340200220036a41ff006a413041d7002000410f712204410a491b20046a3a00002003417f6a210320004110492104200041047621002004450d000b20034180016a22004180014b0d022001410141c48dc080004102200220036a4180016a410020036b10db8080800021000c010b20002802002100410021030340200220036a41ff006a413041372000410f712204410a491b20046a3a00002003417f6a210320004110492104200041047621002004450d000b20034180016a22004180014b0d022001410141c48dc080004102200220036a4180016a410020036b10db8080800021000b20024180016a24808080800020000f0b200041800141c88dc08000109481808000000b200041800141c88dc08000109481808000000be40201037f2380808080004180016b22022480808080002000280200210002400240024002400240200128021c22034110710d0020034120710d012000ad4101200110fe8080800021000c020b410021030340200220036a41ff006a413041d7002000410f712204410a491b20046a3a00002003417f6a210320004110492104200041047621002004450d000b20034180016a22004180014b0d022001410141c48dc080004102200220036a4180016a410020036b10db8080800021000c010b410021030340200220036a41ff006a413041372000410f712204410a491b20046a3a00002003417f6a210320004110492104200041047621002004450d000b20034180016a22004180014b0d022001410141c48dc080004102200220036a4180016a410020036b10db8080800021000b20024180016a24808080800020000f0b200041800141c88dc08000109481808000000b200041800141c88dc08000109481808000000bb01107017f037e027f127e027f027e037f23808080800041306b22042480808080000240024002400240024002400240024002400240024002400240200129030022054200510d00200129030822064200510d01200129031022074200510d02200520077c22072005540d0320052006540d04200341104d0d052007428080808080808080205a0d06200420012f011822013b01082004200520067d22063703002001200141606a200120074280808080105422081b220941706a20092007422086200720081b220742808080808080c0005422081b220941786a20092007421086200720081b2207428080808080808080015422081b2209417c6a20092007420886200720081b2207428080808080808080105422081b2209417e6a20092007420486200720081b2207428080808080808080c0005422081b2007420286200720081b220a427f556b22086b4110744110752209417f4c0d07200420062009ad220786220b200788220c370310200c2006520d08200420013b010820042005370300200420052007423f832206862207200688220637031020062005520d0941a07f20086b41107441107541d0006c41b0a7056a41ce106d220141d1004f0d0a200141047422014188c0c080006a290300220642ffffffff0f8322052007422088220d7e220c422088220e2006422088220f200d7e7c200f200742ffffffff0f8322077e220642208822107c2111200c42ffffffff0f83200520077e4220887c200642ffffffff0f837c4280808080087c422088211242014100200820014190c0c080006a2f01006a6b413f71ad2207862213427f7c21142005200b42208822067e220c42ffffffff0f832005200b42ffffffff0f83220b7e4220887c200f200b7e220b42ffffffff0f837c4280808080087c4220882115200f20067e2106200b422088210b200c422088210c20014192c0c080006a2f010021010240200f200a200a427f85423f8886220a42208822167e2217200520167e221842208822197c200f200a42ffffffff0f83220a7e221a422088221b7c201842ffffffff0f832005200a7e4220887c201a42ffffffff0f837c4280808080087c422088221a7c42017c2218200788a722094190ce00490d00200941c0843d490d0c024020094180c2d72f490d00410841092009418094ebdc034922081b211c4180c2d72f418094ebdc0320081b21080c0e0b4106410720094180ade2044922081b211c41c0843d4180ade20420081b21080c0d0b0240200941e400490d0041024103200941e8074922081b211c41e40041e80720081b21080c0d0b410a4101200941094b221c1b21080c0c0b41accbc08000411c41c8cbc0800010f880808000000b41d8cbc08000411d41f8cbc0800010f880808000000b4188ccc08000411c41a4ccc0800010f880808000000b41d4cec080004136418ccfc0800010f880808000000b418ccec08000413741c4cec0800010f880808000000b41b4ccc08000412d41e4ccc0800010f880808000000b41f4ccc08000412d41a4cdc0800010f880808000000b41b4bec08000411d41c8bfc0800010f880808000000b200441003602184100200441106a2004200441186a41d8bfc0800010fa80808000000b200441003602184100200441106a2004200441186a41d8bfc0800010fa80808000000b200141d100419ccbc0800010f980808000000b41044105200941a08d064922081b211c4190ce0041a08d0620081b21080b201120127c211120182014832105201c20016b41016a211d2018200c20067c200b7c20157c221e7d221f42017c220c201483210a41002101024002400240024002400240024002400340200920086e212020032001460d02200220016a2221202041306a22223a000002400240200c2009202020086c6b2209ad200786220620057c220b560d00201c2001470d01200141016a21014201210603402006210b200a210c200120034f0d06200220016a2005420a7e2205200788a741306a22083a0000200141016a2101200b420a7e2106200c420a7e220a20052014832205580d000b2006201820117d7e220720067c2112200a20057d20135422090d07200720067d22142005560d030c070b200c200b7d220c2008ad2007862207542108201820117d220a42017c2115200a427f7c2213200b580d05200c2007540d05200520077c220b200e7c20107c20127c200f200d20167d7e7c20197d201b7d201a7d210c2019201b7c201a7c20177c210a42002011200620057c7c7d210d4202201e200b20067c7c7d2118034002402006200b7c22142013540d00200d200a7c2006200c7c5a0d00200620057c210b410021080c070b20212022417f6a22223a0000200520077c21052018200a7c210f0240201420135a0d00200b20077c210b200c20077c210c200a20077d210a200f20075a0d010b0b200f2007542108200620057c210b0c050b200141016a21012008410a4921202008410a6e21082020450d000b41d0cdc08000411941b4cdc0800010f880808000000b200220016a417f6a2120200c420a7e201320057c7d210f201320147d2118201420057d210d4200210703400240200520137c22062014540d00200d20077c201820057c5a0d00410021090c050b20202008417f6a22083a0000200f20077c220c2013542109200620145a0d05200720137d210720062105200c2013540d050c000b0b2003200341eccdc0800010f980808000000b2001200341fccdc0800010f980808000000b02402015200b580d0020080d000240200b20077c22052015540d002015200b7d200520157d540d010b200041003602000c040b02400240200b4202540d00200b201f427d7c580d010b200041003602000c040b2000201d3b01082000200141016a3602040c020b200521060b024020122006580d0020090d000240200620137c22052012540d00201220067d200520127d540d010b200041003602000c020b02400240200b42147e2006560d002006200b42587e200a7c580d010b200041003602000c020b2000201d3b0108200020013602040b200020023602000b200441306a2480808080000b9e0906017e017f047e027f017e057f0240024002400240024002400240200129030022054200510d002005428080808080808080205a0d012003450d0241a07f20012f0118220141606a200120054280808080105422061b220141706a20012005422086200520061b220542808080808080c0005422061b220141786a20012005421086200520061b2205428080808080808080015422061b2201417c6a20012005420886200520061b2205428080808080808080105422061b2201417e6a20012005420486200520061b2205428080808080808080c0005422061b2005420286200520061b2205427f556b22066b41107441107541d0006c41b0a7056a41ce106d220141d1004f0d03200141047422014188c0c080006a290300220742ffffffff0f83220820052005427f85423f8886220542208822097e220a4220882007422088220720097e7c2007200542ffffffff0f8322057e22074220887c200a42ffffffff0f83200820057e4220887c200742ffffffff0f837c4280808080087c4220887c22054140200620014190c0c080006a2f01006a6b220b413f71ad220888a7210c20014192c0c080006a2f01002101024020054201200886220d427f7c22098322074200520d002003410a4b0d0720034102744190d0c080006a280200200c4b0d070b0240200c4190ce00490d00200c41c0843d490d050240200c4180c2d72f490d0041084109200c418094ebdc034922061b210e4180c2d72f418094ebdc0320061b21060c070b41064107200c4180ade2044922061b210e41c0843d4180ade20420061b21060c060b0240200c41e400490d0041024103200c41e8074922061b210e41e40041e80720061b21060c060b410a4101200c41094b220e1b21060c050b41accbc08000411c41c0cfc0800010f880808000000b41d0cfc08000412441f4cfc0800010f880808000000b419ccfc0800041214184d0c0800010f880808000000b200141d100419ccbc0800010f980808000000b41044105200c41a08d064922061b210e4190ce0041a08d0620061b21060b02400240024002400240200e20016b411074418080046a411075220f200441107441107522014c0d00200b41ffff03712110200f20046b4110744110752003200f20016b2003491b2211417f6a2112410021010340200c20066e210b20032001460d03200c200b20066c6b210c200220016a200b41306a3a000020122001460d04200e2001460d02200141016a21012006410a49210b2006410a6e2106200b450d000b41d0cdc08000411941bcd0c0800010f880808000000b2000200220034100200f20042005420a802006ad200886200d109f818080000f0b200141016a21012010417f6a413f71ad210a42012105034002402005200a88500d00200041003602000f0b200120034f0d03200220016a2007420a7e2207200888a741306a3a00002005420a7e2105200720098321072011200141016a2201470d000b2000200220032011200f20042007200d2005109f818080000f0b2003200341ccd0c0800010f980808000000b2000200220032011200f2004200cad20088620077c2006ad200886200d109f818080000f0b2001200341dcd0c0800010f980808000000b200041003602000bae0301047f024002400240024002400240024020072008580d00200720087d2008580d01024002400240200720067d2006580d00200720064201867d20084201865a0d010b20062008560d010c080b200320024b0d030c060b2007200620087d22087d2008560d06200320024b0d03200120036a21094100210a2001210b024003402003200a460d01200a41016a210a200b417f6a220b20036a220c2d00004139460d000b200c200c2d000041016a3a00002003200a6b41016a20034f0d05200c41016a4130200a417f6a108a8e8080001a0c050b0240024020030d004131210a0c010b200141313a00004130210a20034101460d004130210a200141016a41302003417f6a108a8e8080001a0b2004411074418080046a4110752104200320024f0d04200420054110744110754c0d042009200a3a0000200341016a21030c040b200041003602000f0b200041003602000f0b20032002418cd1c08000109581808000000b2003200241ecd0c08000109581808000000b200320024d0d002003200241fcd0c08000109581808000000b200020043b010820002003360204200020013602000f0b200041003602000b1300419cd1c08000412b200010f880808000000b6c01017f23808080800041306b22032480808080002003200136020c200320003602082003411c6a420137020020034101360214200341c8d1c08000360210200341a28080800036022c2003200341286a3602182003200341086a360228200341106a200210f680808000000b870101017f23808080800041306b22032480808080002003200136020420032000360200200341086a410c6a4202370200200341206a410c6a4185808080003602002003410336020c20034194d2c0800036020820034185808080003602242003200341206a360210200320033602282003200341046a360220200341086a200210f680808000000b840602087e0a7f200020013502102202421a8820013502147c220342198820013502187c2204421a88200135021c7c220542198820013502207c2206421a8820013502247c220742198842137e2001350200220842ffffff1f837c2209a741ffffff1f71220a41136a411a762009421a882008421a8820013502047c220842ffffff0f837ca7220b6a411976200842198820013502087c2208a741ffffff1f71220c6a411a762008421a88200135020c7c2208a741ffffff0f71220d6a4119762008421988200242ffffff1f837c2202a741ffffff1f71220e6a411a762002421a88200342ffffff0f837ca7220f6a4119762004a741ffffff1f7122106a411a762005a741ffffff0f7122116a4119762006a741ffffff1f7122126a411a762007a741ffffff0f7122136a41197641136c200a6a22013a0000200020014110763a0002200020014108763a000120002001411a76200b6a220a410e763a00052000200a4106763a00042000200a4102742001411876410371723a00032000200a411976200c6a2201410d763a0008200020014105763a000720002001410374200a418080800e71411676723a000620002001411a76200d6a220a410b763a000b2000200a4103763a000a2000200a4105742001411576411f71723a00092000200a411976200e6a22014112763a000f20002001410a763a000e200020014102763a000d20002001411a76200f6a220b3a001020002001410674200a411376413f71723a000c2000200b4110763a00122000200b4108763a00112000200b41197620106a2201410f763a0015200020014107763a001420002001410174200b411876410171723a001320002001411a7620116a220a410d763a00182000200a4105763a00172000200a4103742001411776410771723a00162000200a41197620126a2201410c763a001b200020014104763a001a20002001410474200a411576410f71723a001920002001411a7620136a220a410a763a001e2000200a4102763a001d2000200a418080f00f714112763a001f2000200a4106742001411476413f71723a001c0b832404017f067e0f7f027e23808080800041f0076b2203248080808000200341a0076a200210a581808000200320032903d00720032903c80720032903c0072204421a887c22054219887c2206a741ffffff1f7136029007200320032903b00720032903a80720032903a0072207421a887c22084219887c2209a741ffffff1f7136028007200320032903d8072006421a887c2206a741ffffff0f7136029407200320032903b8072009421a887c2209a741ffffff0f71360284072003200642198820032903e0077c2206a741ffffff1f713602980720032009421988200442ffffff1f837c2204421a88200542ffffff0f837c3e028c0720032004a741ffffff1f713602880720032006421a8820032903e8077c2204a741ffffff0f7136029c072003200442198842137e200742ffffff1f837c2204421a88200842ffffff0f837c3e02fc0620032004a741ffffff1f713602f806200341086a200341f8066a200210a681808000200341a0076a200341086a10a581808000200320032903d00720032903c80720032903c0072204421a887c22054219887c2206a741ffffff1f7136029007200320032903b00720032903a80720032903a0072207421a887c22084219887c2209a741ffffff1f7136028007200320032903d8072006421a887c2206a741ffffff0f7136029407200320032903b8072009421a887c2209a741ffffff0f71360284072003200642198820032903e0077c2206a741ffffff1f713602980720032009421988200442ffffff1f837c2204421a88200542ffffff0f837c3e028c0720032004a741ffffff1f713602880720032006421a8820032903e8077c2204a741ffffff0f7136029c072003200442198842137e200742ffffff1f837c2204421a88200842ffffff0f837c3e02fc0620032004a741ffffff1f713602f806200341306a200341f8066a200210a681808000200341d8006a2001200341086a10a68180800020034180016a2001200341306a10a681808000200341a0076a20034180016a10a581808000200320032903d00720032903c80720032903c0072204421a887c22054219887c2206a741ffffff1f713602c001200320032903b00720032903a80720032903a0072207421a887c22084219887c2209a741ffffff1f713602b001200320032903d8072006421a887c2206a741ffffff0f713602c401200320032903b8072009421a887c2209a741ffffff0f713602b4012003200642198820032903e0077c2206a741ffffff1f713602c80120032009421988200442ffffff1f837c2204421a88200542ffffff0f837c3e02bc0120032004a741ffffff1f713602b80120032006421a8820032903e8077c2204a741ffffff0f713602cc012003200442198842137e200742ffffff1f837c2204421a88200842ffffff0f837c3e02ac0120032004a741ffffff1f713602a801200341a0076a200341a8016a10a581808000200320032903d00720032903c80720032903c0072204421a887c22054219887c2206a741ffffff1f7136029007200320032903b00720032903a80720032903a0072207421a887c22084219887c2209a741ffffff1f7136028007200320032903d8072006421a887c2206a741ffffff0f7136029407200320032903b8072009421a887c2209a741ffffff0f71360284072003200642198820032903e0077c2206a741ffffff1f713602980720032009421988200442ffffff1f837c2204421a88200542ffffff0f837c3e028c0720032004a741ffffff1f713602880720032006421a8820032903e8077c2204a741ffffff0f7136029c072003200442198842137e200742ffffff1f837c2204421a88200842ffffff0f837c3e02fc0620032004a741ffffff1f713602f806200341a0076a200341f8066a10a581808000200320032903d00720032903c80720032903c0072204421a887c22054219887c2206a741ffffff1f713602e801200320032903b00720032903a80720032903a0072207421a887c22084219887c2209a741ffffff1f713602d801200320032903d8072006421a887c2206a741ffffff0f713602ec01200320032903b8072009421a887c2209a741ffffff0f713602dc012003200642198820032903e0077c2206a741ffffff1f713602f00120032009421988200442ffffff1f837c2204421a88200542ffffff0f837c3e02e40120032004a741ffffff1f713602e00120032006421a8820032903e8077c2204a741ffffff0f713602f4012003200442198842137e200742ffffff1f837c2204421a88200842ffffff0f837c3e02d40120032004a741ffffff1f713602d001200341f8016a20034180016a200341d0016a10a681808000200341a0026a200341a8016a200341f8016a10a681808000200341a0076a200341a0026a10a581808000200320032903d00720032903c80720032903c0072204421a887c22054219887c2206a741ffffff1f713602e002200320032903b00720032903a80720032903a0072207421a887c22084219887c2209a741ffffff1f713602d002200320032903d8072006421a887c2206a741ffffff0f713602e402200320032903b8072009421a887c2209a741ffffff0f713602d4022003200642198820032903e0077c2206a741ffffff1f713602e80220032009421988200442ffffff1f837c2204421a88200542ffffff0f837c3e02dc0220032004a741ffffff1f713602d80220032006421a8820032903e8077c2204a741ffffff0f713602ec022003200442198842137e200742ffffff1f837c2204421a88200842ffffff0f837c3e02cc0220032004a741ffffff1f713602c802200341f0026a200341f8016a200341c8026a10a68180800020034198036a200341f0026a410510a781808000200341c0036a20034198036a200341f0026a10a681808000200341e8036a200341c0036a410a10a78180800020034190046a200341e8036a200341c0036a10a681808000200341b8046a20034190046a411410a781808000200341e0046a200341b8046a20034190046a10a68180800020034188056a200341e0046a410a10a781808000200341b0056a20034188056a200341c0036a10a681808000200341d8056a200341b0056a413210a78180800020034180066a200341d8056a200341b0056a10a681808000200341a8066a20034180066a41e40010a781808000200341d0066a200341a8066a20034180066a10a681808000200341f8066a200341d0066a413210a781808000200341a0076a200341f8066a200341b0056a10a681808000200341f8066a41206a200341a0076a41206a290200370300200341f8066a41186a200341a0076a41186a290200370300200341f8066a41106a200341a0076a41106a290200370300200341f8066a41086a200341a0076a41086a290200370300200320032902a0073703f806200341a0076a200341f8066a410210a781808000200341d0066a20034180016a200341a0076a10a681808000200341a8066a200341d8006a200341d0066a10a681808000200341a0076a200341a8066a10a581808000200320032903d00720032903c80720032903c0072204421a887c22054219887c2206a741ffffff1f7136029007200320032903b00720032903a80720032903a0072207421a887c22084219887c2209a741ffffff1f7136028007200320032903d8072006421a887c2206a741ffffff0f7136029407200320032903b8072009421a887c2209a741ffffff0f71360284072003200642198820032903e0077c2206a741ffffff1f713602980720032009421988200442ffffff1f837c2204421a88200542ffffff0f837c3e028c0720032004a741ffffff1f713602880720032006421a8820032903e8077c2204a741ffffff0f7136029c072003200442198842137e200742ffffff1f837c2204421a88200842ffffff0f837c3e02fc0620032004a741ffffff1f713602f806200341d0066a2002200341f8066a10a681808000200341f8066a200341d0066a10a381808000200341a0076a200110a381808000410021024101210a0340200341f8066a20026a2d0000200341a0076a20026a2d00004610ad8d808000200a71210a200241016a22024120470d000b200a10ad8d808000210b200341f0ffffff0320012802106bad2204421a8841f0ffffff0120012802146bad7c220542198841f0ffffff0320012802186bad7c2206a741ffffff1f71220c3602b807200341d0fdffff0320012802006bad2207421a8841f0ffffff0120012802046bad7c220842198841f0ffffff0320012802086bad7c2209a741ffffff1f71220d3602a80720032006421a8841f0ffffff01200128021c6bad7c2206a741ffffff0f71220e3602bc0720032009421a8841f0ffffff01200128020c6bad7c2209a741ffffff0f71220f3602ac072003200642198841f0ffffff0320012802206bad7c2206a741ffffff1f7122103602c00720032009421988200442ffffff1f837c2204a741ffffff1f7122113602b00720032004421a88200542ffffff0f837ca722123602b40720032006421a8841f0ffffff0120012802246bad7c2204a741ffffff0f7122013602c4072003200442198842137e200742ffffff1f837c2204a741ffffff1f7122133602a00720032004421a88200842ffffff0f837ca722143602a40720034180066a200341d0066a10a381808000200341f8066a200341a0076a10a381808000410021024101210a034020034180066a20026a2d0000200341f8066a20026a2d00004610ad8d808000200a71210a200241016a22024120470d000b200a10ad8d8080002115200320013602c407200320103602c0072003200e3602bc072003200c3602b807200320123602b407200320113602b0072003200f3602ac072003200d3602a807200320143602a407200320133602a007200341f8066a200341a0076a41d4d2c0800010a681808000200341d8056a200341d0066a10a38180800020034180066a200341f8066a10a381808000410021024101210a0340200341d8056a20026a2d000020034180066a20026a2d00004610ad8d808000200a71210a200241016a22024120470d000b200a10ad8d8080002102200341a0076a41d4d2c08000200341a8066a10a681808000200220157210ad8d8080002102200341b0066a220a20032802a807200a2802002201734100200241ff01716b2202712001732201360200200341b8066a220c20032802b007200c280200220d73200271200d73220d360200200341c0066a220e20032802b807200e280200220f73200271200f73220f360200200320032802ac0720032802b40622107320027120107322103602b406200320032802a40720032802ac0622117320027120117322113602ac06200320032802a00720032802a80622127320027120127322123602a806200320032802b40720032802bc0622137320027120137322133602bc06200320032802bc0720032802c40622147320027120147322143602c406200341c8066a221620032802c00720162802002217732002712017732217360200200320032802c40720032802cc0622187320027120187322183602cc06200341f8066a200341a8066a10a381808000201641f0ffffff03200d6bad2204421a8841f0ffffff0120136bad7c220542198841f0ffffff03200f6bad7c2206421a8841f0ffffff0120146bad7c220742198841f0ffffff0320176bad7c2208a741ffffff1f71201773410020032d00f80641017110ad8d80800041ff01716b220271201773360200200e2006a741ffffff1f71200f73200271200f73360200200c200442ffffff1f8341d0fdffff0320126bad2204421a8841f0ffffff0120116bad7c220642198841f0ffffff0320016bad7c2209421a8841f0ffffff0120106bad7c22194219887c221aa741ffffff1f71200d73200271200d73360200200a2009a741ffffff1f7120017320027120017336020020032008421a8841f0ffffff0120186bad7c2208a741ffffff0f712018732002712018733602cc0620032007a741ffffff0f712014732002712014733602c40620032013200542ffffff0f83201a421a887ca7732002712013733602bc0620032019a741ffffff0f712010732002712010733602b4062003200842198842137e200442ffffff1f837c2204a741ffffff1f712012732002712012733602a806200320112004421a88200642ffffff0f837ca7732002712011733602ac0620002015200b7210ad8d8080003a0000200041246a20162902003702002000411c6a200e290200370200200041146a200c2902003702002000410c6a200a290200370200200020032902a806370204200341f0076a2480808080000bcd0412017f017e017f017e017f017e017f017e017f017e017f017e017f017e017f017e017f0c7e2000200128020c2202ad220320012802002204410174ad22057e20012802042206410174ad220720012802082208ad22097e7c2001280220220a41136cad220b2001280214220c410174ad220d7e7c2001280224220e41136cad220f20012802102210ad22117e200128021c221241136cad221320012802182201ad22147e7c4201867c3703182000200141136cad2215200d7e20052006ad22167e7c200b2002410174ad22177e7c200f20097e201320117e7c4201867c3703082000201420177e2010410174ad2218200cad22197e7c2012ad221a2008410174ad221b7e7c200aad221c20077e7c200ead221d20057e7c37034820002019201b7e201720117e7c201420077e7c201a20057e7c201c200f7e4201867c3703382000201120077e201b20037e7c201920057e7c200b2012410174ad221e7e7c2014200f7e4201867c3703282000201720077e200920097e7c201120057e7c200b2001410174ad7e7c200f200d7e2013201a7e7c4201867c3703202000200920057e200720167e7c201520147e7c200b20187e7c200f20177e2013200d7e7c4201867c3703102000201520187e2004ad220920097e7c200b201b7e7c201320177e200c41136cad20197e7c200f20077e7c4201867c37030020002014201b7e201120117e7c200d20177e7c201e20077e7c201c20057e7c201d200f7e4201867c3703402000201720037e2011201b7e7c200d20077e7c201420057e7c200b201c7e7c201e200f7e4201867c3703300bc7081a017f017e017f017e017f017e017f017e017f017e017f017e017f017e017f027e017f017e017f017e017f027e017f027e017f147e2000200128020c2203410174ad2204200228020c2205ad22067e20012802042207410174ad220820022802142209ad220a7e7c2001280214220b410174ad220c2002280204220dad220e7e7c200128021c220f410174ad22102002280224221141136cad22127e7c2001350200221320022802182214ad22157e7c20012802242216410174ad2217200228021c221841136cad22197e7c2001350208221a2002280210221bad221c7e7c2001350210221d2002280208221ead221f7e7c20013502182220200235020022217e7c200135022022222002280220220241136cad22237e7c2003ad2224201f7e2007ad2225201c7e7c200fad222620237e7c2016ad2227201441136cad22287e7c2013200a7e7c2021200bad22297e7c201a20067e7c201d200e7e7c202020127e7c202220197e7c2004200e7e200820067e7c200c20127e7c201020197e7c2013201c7e7c2017200941136cad222a7e7c201a201f7e7c201d20217e7c202020237e7c202220287e7c222b421a887c222c4219887c222da741ffffff1f713602182000200420127e2008200e7e7c200c20197e7c2010202a7e7c2013201f7e7c2017200541136cad222e7e7c201a20217e7c201d20237e7c202020287e7c2022201b41136cad222f7e7c202920287e202420237e7c2026202f7e7c2027201e41136cad22307e7c2013200e7e7c202120257e7c201a20127e7c201d20197e7c2020202a7e7c2022202e7e7c200420197e200820127e7c200c202a7e7c2010202e7e7c2017200d41136cad7e7c202120137e7c201a20237e7c201d20287e7c2020202f7e7c202220307e7c2230421a887c22314219887c2232a741ffffff1f7136020820002024201c7e202520157e7c2029201f7e7c202720237e7c20132018ad222e7e7c202120267e7c201a200a7e7c201d20067e7c2020200e7e7c202220127e7c202d421a887c222da741ffffff0f7136021c2000202920237e2025201f7e7c202620287e7c2027202f7e7c201320067e7c202120247e7c201a200e7e7c201d20127e7c202020197e7c2022202a7e7c2032421a887c2223a741ffffff0f7136020c20002004200a7e2008202e7e7c200c20067e7c2010200e7e7c20132002ad22197e7c201720127e7c201a20157e7c201d201c7e7c2020201f7e7c202220217e7c202d4219887c2212a741ffffff1f7136022020002023421988202b42ffffff1f837c2223421a88202c42ffffff0f837c3e021420002023a741ffffff1f713602102000202420157e202520197e7c2029201c7e7c2026201f7e7c20132011ad7e7c202120277e7c201a202e7e7c201d200a7e7c202020067e7c2022200e7e7c2012421a887c2213a741ffffff0f713602242000201342198842137e203042ffffff1f837c2213421a88203142ffffff0f837c3e020420002013a741ffffff1f713602000be20502017f067e2380808080004180016b2203248080808000200341306a200110a58180800020032003290360200329035820032903502204421a887c22054219887c2206a741ffffff1f7136022020032003290340200329033820032903302207421a887c22084219887c2209a741ffffff1f71360210200320032903682006421a887c2206a741ffffff0f71360224200320032903482009421a887c2209a741ffffff0f713602142003200642198820032903707c2206a741ffffff1f7136022820032009421988200442ffffff1f837c2204421a88200542ffffff0f837c3e021c20032004a741ffffff1f7136021820032006421a8820032903787c2204a741ffffff0f7136022c2003200442198842137e200742ffffff1f837c2204421a88200842ffffff0f837c3e020c20032004a741ffffff1f71360208024020024102490d002002417f6a21020340200341306a200341086a10a58180800020032003290360200329035820032903502204421a887c22054219887c2206a741ffffff1f7136022020032003290340200329033820032903302207421a887c22084219887c2209a741ffffff1f71360210200320032903682006421a887c2206a741ffffff0f71360224200320032903482009421a887c2209a741ffffff0f713602142003200642198820032903707c2206a741ffffff1f7136022820032009421988200442ffffff1f837c2204421a88200542ffffff0f837c3e021c20032004a741ffffff1f7136021820032006421a8820032903787c2204a741ffffff0f7136022c2003200442198842137e200742ffffff1f837c2204421a88200842ffffff0f837c3e020c20032004a741ffffff1f713602082002417f6a22020d000b0b20002003290208370200200041206a200341086a41206a290200370200200041186a200341086a41186a290200370200200041106a200341086a41106a290200370200200041086a200341086a41086a29020037020020034180016a2480808080000be103020e7f067e20022802242103200128022421042002280220210520012802202106200228020c2107200128020c2108200228021c2109200128021c210a2002280208210b2001280208210c2002280204210d2001280204210e2002280200210f200128020021102000200128021020022802106b41f0ffffff036aad2211421a88200128021420022802146b41f0ffffff016aad7c2212421988200128021820022802186b41f0ffffff036aad7c2213a741ffffff1f7136021820002010200f6b41d0fdffff036aad2214421a88200e200d6b41f0ffffff016aad7c2215421988200c200b6b41f0ffffff036aad7c2216a741ffffff1f7136020820002013421a88200a20096b41f0ffffff016aad7c2213a741ffffff0f7136021c20002016421a88200820076b41f0ffffff016aad7c2216a741ffffff0f7136020c20002013421988200620056b41f0ffffff036aad7c2213a741ffffff1f7136022020002016421988201142ffffff1f837c2211421a88201242ffffff0f837c3e021420002011a741ffffff1f7136021020002013421a88200420036b41f0ffffff016aad7c2211a741ffffff0f713602242000201142198842137e201442ffffff1f837c2211421a88201542ffffff0f837c3e020420002011a741ffffff1f713602000beb0201067e200041f0ffffff0320012802106bad2202421a8841f0ffffff0120012802146bad7c220342198841f0ffffff0320012802186bad7c2204a741ffffff1f71360218200041d0fdffff0320012802006bad2205421a8841f0ffffff0120012802046bad7c220642198841f0ffffff0320012802086bad7c2207a741ffffff1f7136020820002004421a8841f0ffffff01200128021c6bad7c2204a741ffffff0f7136021c20002007421a8841f0ffffff01200128020c6bad7c2207a741ffffff0f7136020c2000200442198841f0ffffff0320012802206bad7c2204a741ffffff1f7136022020002007421988200242ffffff1f837c2202421a88200342ffffff0f837c3e021420002002a741ffffff1f7136021020002004421a8841f0ffffff0120012802246bad7c2202a741ffffff0f713602242000200242198842137e200542ffffff1f837c2202421a88200642ffffff0f837c3e020420002002a741ffffff1f713602000be00302187e017f20013100052102200131000421032001310015210420013100142105200131000821062001310007210720013100062108200131000b2109200131000a210a2001310009210b200131000f210c200131000e210d200131000d210e200131000c210f200131001821102001310017211120013100162112200131001b2113200131001a211420013100192115200131001f2116200131001e2117200131001d2118200131001c21192001280000211a20002001280010220141ffffff0f713602142000201a41ffffff1f7136020020002016421286428080f00f8320184202862019420688842017420a8684843e022420002019421486428080c01f8320144204862015420488842013420c8684843e022020002015421586428080800f8320114205862012420388842010420d8684843e021c2000200e420286200f42068884200d420a8684200c421286843e02102000200f421386428080e00f83200a420386200b420588842009420b8684843e020c2000200b421586428080801f8320074205862008420388842006420d8684843e020820002012421786428080801c8320054207862001411976ad842004420f8684843e021820002008421686428080800e832003420686201a411a76ad842002420e8684843e02040bfe0c0a017f027e017f017e017f017e017f027e017f157e23808080800041f0006b2202248080808000200241046a200110ae8180800020022002350214220342ecf3b78a037e20022802082201ad220442e7e2e4b3017e20022802042205ad220642eecaf5ff017e7c200228020c2207ad2208428c93f0fb007e7c20022802102209ad220a4283e685d3017e7c200342edf3b78a017e7c220b7d2002280218220c20056aad220d42eecaf5ff017e7c200228021c220520016aad220e42e6e2a4b4017e7c2002280220220120076aad220f428b93f0fb027e7c20022802242207ad221042ffffffff017e22112001ad221242ffffffff017e22137c22142005ad221542ffff3f7e7c22167d200720096aad22174282e685d3037e7c200642ff037e42ffffffff0183221842d2b1cc047e200442edf3b78a017e20064283e685d3017e7c22197c201842eda7d7e7017e200642edf3b78a017e221a7c421d887c221b429bfcd192017e42ffffffff0183221c4214867c200842e7e2e4b3017e200442eecaf5ff017e7c200a428c93f0fb007e7c20034283e685d3017e7c200cad221d42ffffffff017e221e7d221f201a7d200d42ecf3b78a037e7c201c42cd027e7c20044283e685d3017e2006428c93f0fb007e7c200842edf3b78a017e7c222020184296eb9cef017e7c201c42d2b1cc047e7c201c42eda7d7e7017e201b7c421d887c221b429bfcd192017e42ffffffff0183221a42c5faceef017e7c2004428c93f0fb007e200642e7e2e4b3017e7c20084283e685d3017e7c200a42edf3b78a017e7c2221201842c5faceef017e7c201c4296eb9cef017e7c201a42d2b1cc047e7c201a42eda7d7e7017e201b7c421d887c2204429bfcd192017e42ffffffff018322064296eb9cef017e7c200b201842cd027e7c201c42c5faceef017e7c201a4296eb9cef017e7c200642d2b1cc047e7c200642eda7d7e7017e20047c421d887c2204429bfcd192017e42ffffffff0183221c42d2b1cc047e7c201c42eda7d7e7017e20047c421d887c220b429bfcd192017e42ffffffff0183220442cd027e7c200d4282e685d3037e20197d200e42ecf3b78a037e7c200a42e7e2e4b3017e200842eecaf5ff017e7c2003428c93f0fb007e7c201542ffffffff017e2215201e7c22197d221b7c201a42cd027e7c200642c5faceef017e7c201c4296eb9cef017e7c200442d2b1cc047e7c200442eda7d7e7017e200b7c421d887c220b429bfcd192017e42ffffffff0183220842c5faceef017e7c200d428b93f0fb027e20207d200e4282e685d3037e7c200f42ecf3b78a037e7c200342e7e2e4b3017e200a42eecaf5ff017e7c201320197c7d22137c200642cd027e7c201c42c5faceef017e7c20044296eb9cef017e7c200842d2b1cc047e7c200842eda7d7e7017e200b7c421d887c220b429bfcd192017e42ffffffff0183220a4296eb9cef017e7c201842148620217d200342eecaf5ff017e7c200d42e6e2a4b4017e7c200e428b93f0fb027e7c200f4282e685d3037e7c2014201d42ffff3f7e7c20157c220d7d201742ecf3b78a037e7c201c42cd027e7c200442c5faceef017e7c20084296eb9cef017e7c200a42d2b1cc047e7c200a42eda7d7e7017e200b7c421d887c220b429bfcd192017e42ffffffff0183221842d2b1cc047e7c201842eda7d7e7017e200b7c421d887c220ba741ffffffff017136024c200220034282e685d3037e201f7d200e42eecaf5ff017e7c200f42e6e2a4b4017e7c2011201242ffff3f7e7c220e7d2017428b93f0fb027e7c201a4214867c200842cd027e7c200a42c5faceef017e7c20184296eb9cef017e7c200b421d887c221aa741ffffffff017136025020022003428b93f0fb027e201b201042ffff3f7e220b7c7d200f42eecaf5ff017e7c201742e6e2a4b4017e7c20064214867c200a42cd027e7c201842c5faceef017e7c201a421d887c2206a741ffffffff01713602542002200342e6e2a4b4017e20137d201742eecaf5ff017e7c201c4214867c201842cd027e7c2006421d887c2203a741ffffffff017136025820022004421486200d7c2003421d887c2203a741ffffffff017136025c2002200842148620167c2003421d887c2203a741ffffffff01713602602002200a421486200e7c2003421d887c2203a741ffffffff017136026420022018421486200b7c2003421d887c2203421d883e026c20022003a741ffffffff0171360268200241286a200241cc006a41fcd2c0800010ad818080002000200241286a10ac81808000200241f0006a2480808080000b950301077f2000200128022022023a001d2000200128020022033a0000200020024110763a001f200020024108763a001e2000200128021c22044115763a001c20002004410d763a001b200020044105763a001a2000200128021822024112763a001820002002410a763a0017200020024102763a0016200020012802142205410f763a0014200020054107763a00132000200128021022064114763a001120002006410c763a0010200020064104763a000f2000200128020c22074111763a000d200020074109763a000c200020074101763a000b200020012802082208410e763a0009200020084106763a00082000200128020422014113763a000620002001410b763a0005200020014103763a0004200020034110763a0002200020034108763a0001200020044103742002411a76723a0019200020024106742005411776723a0015200020054101742006411c76723a0012200020064104742007411976723a000e200020074107742008411676723a000a200020084102742001411b76723a0007200020014105742003411876723a00030bcd04010e7f23808080800041106b2203200128022020022802206b200128021c200228021c6b200128021820022802186b200128021420022802146b200128021020022802106b200128020c200228020c6b200128020820022802086b200128020020022802006b2204411f7520012802046a20022802046b2201411f756a2205411f756a2206411f756a2207411f756a2208411f756a2209411f756a220a411f756a220b411f75220236020c200328020c210c2003200236020c200328020c210d2003200236020c200328020c210e2003200236020c200328020c210f2003200236020c200328020c21102003200236020c200328020c1a2003200236020c200328020c1a2003200236020c200328020c1a2003200236020c200328020c21032000200c41eda7d7e70171200441ffffffff01716a2202411d76200141ffffffff01716a200d41d2b1cc04716a2201411d76200541ffffffff01716a200e4196eb9cef01716a2204411d76200641ffffffff01716a200f41c5faceef01716a2205411d76200741ffffffff01716a201041cd02716a2206411d76200841ffffffff01716a2207411d76200941ffffffff01716a2208411d76200a41ffffffff01716a220941ffffffff017136021c2000200841ffffffff01713602182000200741ffffffff01713602142000200641ffffffff01713602102000200541ffffffff017136020c2000200441ffffffff01713602082000200141ffffffff01713602042000200241ffffffff017136020020002009411d76200b6a2003418080c000716a41ffffffff01713602200bd50301187f20012f0004210220012d0006210320012d0018210420012d0016210520012d0017210620012f0008210720012d0007210820012f000c210920012d000b210a20012d000a210b20012f0010210c20012d000f210d20012d000e210e20012d0014210f20012d0015211020012d0013211120012d0012211220012d001c211320012d0019211420012d001a211520012d001b211620012f0000211720012d0002211820012d00032119200020012f001d20012d001f41107472360220200020172018411074722019411874220141808080f80171723602002000201341157420154110742016411874722014410874221372410b767236021c2000200f2010410874221072410f7420114118742012411074220f724111767241ffffffff01713602142000200c200f72410c74200d411874200e411074220c724114767241ffffffff017136021020002009200c72410974200a411874200b4110742209724117767241ffffffff017136020c2000200720097241067420084118742207411a767241ffffffff0171360208200020132004724112742005411074200641187472201072410e767241ffffffff0171360218200020022003411074722007724103742001411d767241ffffffff01713602040bcf08011b7f23808080800041a0026b22032480808080002001412c6a2802002104200141306a2802002105200141346a2802002106200141386a28020021072001413c6a2802002108200141c0006a2802002109200141c4006a280200210a200141c8006a280200210b200141cc006a280200210c2001280204210d2001280208210e200128020c210f200128021021102001280214211120012802182112200128021c211320012802202114200128022421152003200128020020012802286a36020820032015200c6a36022c20032014200b6a36022820032013200a6a3602242003201220096a3602202003201120086a36021c2003201020076a3602182003200f20066a3602142003200e20056a3602102003200d20046a36020c200341306a200141286a200110a881808000200341d8006a200341086a200210a68180800020034180016a200341306a200241286a10a681808000200341a8016a200141f8006a200241f8006a10a681808000200341d0016a200141d0006a200241d0006a10a681808000200320032802d00141017422013602f801200320032802d40141017422023602fc01200320032802d801410174220436028002200320032802dc01410174220536028402200320032802e001410174220636028802200320032802e401410174220736028c02200320032802e801410174220836029002200320032802ec01410174220936029402200320032802f001410174220a36029802200320032802f401410174220b36029c022000200341d8006a20034180016a10a881808000200328028001210c2003280258210d200328028401210e200328025c210f200328028801211020032802602111200328028c012112200328026421132003280290012114200328026821152003280294012116200328026c2117200328029801211820032802702119200328029c01211a2003280274211b20032802a001211c2003280278211d200041cc006a20032802a401200328027c6a360200200041c8006a201c201d6a360200200041c4006a201a201b6a360200200041c0006a201820196a3602002000413c6a201620176a360200200041386a201420156a360200200041346a201220136a360200200041306a201020116a3602002000412c6a200e200f6a3602002000200c200d6a36022820032802a801210c20032802ac01210d20032802b001210e20032802b401210f20032802b801211020032802bc01211120032802c001211220032802c401211320032802c8012114200041f4006a20032802cc01200b6a360200200041f0006a2014200a6a360200200041ec006a201320096a360200200041e8006a201220086a360200200041e4006a201120076a360200200041e0006a201020066a360200200041dc006a200f20056a360200200041d8006a200e20046a360200200041d4006a200d20026a3602002000200c20016a360250200041f8006a200341f8016a200341a8016a10a881808000200341a0026a2480808080000ba40401137f23808080800041c0026b22032480808080002002412c6a2802002104200241306a2802002105200241346a2802002106200241386a28020021072002413c6a2802002108200241c0006a2802002109200241c4006a280200210a200241c8006a280200210b2002280200210c2002280228210d2002280204210e2002280208210f200228020c2110200228021021112002280214211220022802182113200228021c21142002280220211520032002280224200241cc006a2802006a3602c40120032015200b6a3602c00120032014200a6a3602bc012003201320096a3602b8012003201220086a3602b4012003201120076a3602b0012003201020066a3602ac012003200f20056a3602a8012003200e20046a3602a4012003200c200d6a3602a001200341a0016a41286a200241286a200210a881808000200341a0016a41f0006a200241f0006a290200370200200341a0016a41e8006a200241e8006a290200370200200341a0016a41e0006a200241e0006a290200370200200341a0016a41d8006a200241d8006a290200370200200320022902503702f001200341a0016a41f8006a200241f8006a41a0d3c0800010a68180800020032001200341a0016a10af8180800020002003200341f8006a220210a681808000200041286a200341286a2201200341d0006a220410a681808000200041d0006a2004200210a681808000200041f8006a2003200110a681808000200341c0026a2480808080000bae0201047f23808080800041f0026b2202248080808000200241cc016a200110b681808000200241086a200241d8016a290200370300200241106a200241e0016a290200370300200241186a200241e8016a290200370300200241206a200241f0016a290200370300200220022902d00137030041002101024020022d00cd0120022d00cc01417f7341017110ad8d8080007210ad8d80800041ff01710d00200241cc016a200210b78180800020022d00ce01210320022d00cd01210420022d00cc012105200241286a200241cc016a41046a41a00110848e8080001a200320042005417f7341017110ad8d8080007210ad8d8080007210ad8d80800041ff01710d00200041046a200241286a41a00110848e8080001a410121010b20002001360200200241f0026a2480808080000bb1160a067f017e017f017e017f017e017f027e0d7f017e23808080800041e0046b2202248080808000200241086a41206a2203200141206a290200370300200241086a41186a2204200141186a290200370300200241086a41106a2205200141106a290200370300200241086a41086a2206200141086a29020037030020022001290200370308200241306a41206a2207200141c8006a2902002208370300200241306a41186a2209200141c0006a290200220a370300200241306a41106a220b200141386a290200220c370300200241306a41086a220d200141306a290200220e37030020022001290228220f370330200141d4006a2802002110200141d8006a2802002111200141dc006a2802002112200141e0006a2802002113200141e4006a2802002114200141e8006a2802002115200141ec006a2802002116200141f0006a28020021172001280250211820022802342119200228023c211a2002280244211b200228024c211c20022002280254200141f4006a2802006a36028c04200220172008a76a360288042002201c20166a3602840420022015200aa76a360280042002201b20146a3602fc0320022013200ca76a3602f8032002201a20126a3602f40320022011200ea76a3602f0032002201920106a3602ec0320022018200fa76a3602e80320024190046a200141d0006a2214200241306a10a881808000200241d8006a200241e8036a20024190046a10a68180800020024180016a200241086a200241306a10a68180800020024190046a20024180016a10a581808000200220022903c00420022903b80420022903b0042208421a887c220a4219887c220ca741ffffff1f7136028004200220022903a004200229039804200229039004220e421a887c220f4219887c221da741ffffff1f713602f003200220022903c804200c421a887c220ca741ffffff0f7136028404200220022903a804201d421a887c221da741ffffff0f713602f4032002200c42198820022903d0047c220ca741ffffff1f71360288042002201d421988200842ffffff1f837c2208421a88200a42ffffff0f837c3e02fc0320022008a741ffffff1f713602f8032002200c421a8820022903d8047c2208a741ffffff0f7136028c042002200842198842137e200e42ffffff1f837c2208421a88200f42ffffff0f837c3e02ec0320022008a741ffffff1f713602e803200241c0036a200241d8006a200241e8036a10a68180800020024190046a41acd2c08000200241c0036a10a481808000200241a8016a41206a200241b4046a290200370300200241a8016a41186a200241ac046a290200370300200241a8016a41106a200241a4046a290200370300200241a8016a41086a2002419c046a29020037030020022002290294043703a801200241d0016a200241a8016a200241d8006a10a681808000200241f8016a200241a8016a20024180016a10a68180800020024190046a200241f8016a200141f8006a220110a681808000200241a0026a200241d0016a20024190046a10a681808000200241c8026a41206a2210200241f8016a41206a290200370300200241c8026a41186a2211200241f8016a41186a290200370300200241c8026a41106a2212200241f8016a41106a290200370300200241c8026a41086a2213200241f8016a41086a290200370300200220022902f8013703c802200241f0026a200241086a4198d4c0800010a68180800020024198036a200241306a4198d4c0800010a681808000200241c0036a200241d0016a41c0d4c0800010a68180800020024190046a2001200241a0026a10a681808000200241e8036a20024190046a10a38180800020022d00e80341017110ad8d8080002101200620022802a00320062802002215734100200141ff01716b220171201573360200200520022802a8032005280200220673200171200673360200200220022802980320022802082205732001712005733602082002200228029c03200228020c22057320017120057336020c200220022802a4032002280214220573200171200573360214200220022802ac03200228021c22057320017120057336021c200420022802b0032004280200220573200171200573360200200220022802b4032002280224220473200171200473360224200320022802b8032003280200220473200171200473360200200220022802bc03200228022c22037320017120037336022c200220022802f0022002280230220373200171200373360230200220022802f4022002280234220373200171200373360234200d20022802f802200d280200220373200171200373360200200220022802fc02200228023c22037320017120037336023c200b200228028003200b28020022037320017120037336020020022002280284032002280244220373200171200373360244200920022802880320092802002203732001712003733602002002200228028c03200228024c22037320017120037336024c2007200228029003200728020022037320017120037336020020022002280294032002280254220373200171200373360254200220022802c00320022802c8022203732001712003733602c802200220022802c40320022802cc022203732001712003733602cc02201320022802c8032013280200220373200171200373360200200220022802cc0320022802d4022203732001712003733602d402201220022802d0032012280200220373200171200373360200200220022802d40320022802dc022203732001712003733602dc02201120022802d8032011280200220373200171200373360200200220022802dc0320022802e4022203732001712003733602e402201020022802e0032010280200220373200171200373360200200220022802e40320022802ec022203732001712003733602ec02200241e8036a200241086a200241a0026a10a68180800020024190046a200241e8036a10a38180800020022d00900441017110ad8d808000210120024190046a200241306a10a981808000200d200228029804200d2802002203734100200141ff01716b220171200373360200200b20022802a004200b280200220d73200171200d73360200200920022802a8042009280200220b73200171200b7336020020022002280290042002280230220973200171200973360230200220022802940420022802342209732001712009733602342002200228029c04200228023c22097320017120097336023c200220022802a4042002280244220973200171200973360244200220022802ac04200228024c22097320017120097336024c200720022802b0042007280200220973200171200973360200200220022802b404200228025422077320017120077336025420024190046a2014200241306a10a881808000200241e8036a200241c8026a20024190046a10a68180800020024190046a200241e8036a10a38180800020022d00900441017110ad8d808000210120024190046a200241e8036a10a981808000200220022802900420022802e8032207734100200141ff01716b2201712007733602e803200220022802940420022802ec032207732001712007733602ec03200220022802980420022802f0032207732001712007733602f0032002200228029c0420022802f4032207732001712007733602f403200220022802a00420022802f8032207732001712007733602f803200220022802a40420022802fc032207732001712007733602fc03200220022802a80420022802800422077320017120077336028004200220022802ac0420022802840422077320017120077336028404200220022802b00420022802880422077320017120077336028804200220022802b404200228028c0422077320017120077336028c042000200241e8036a10a381808000200241e0046a2480808080000bee1803097f067e0e7f2380808080004180066b2202248080808000200241086a41206a22034100290290d4c08000370300200241086a41186a22044100290288d4c08000370300200241086a41106a22054100290280d4c08000370300200241086a41086a220641002902f8d3c08000370300200241002902f0d3c08000370308200241306a41206a220741002902e8d3c08000370300200241306a41186a220841002902e0d3c08000370300200241306a41106a220941002902d8d3c08000370300200241306a41086a220a41002902d0d3c08000370300200241002902c8d3c0800037033020024190046a200110a581808000200220022903c00420022903b80420022903b004220b421a887c220c4219887c220da741ffffff1f7136028803200220022903a004200229039804200229039004220e421a887c220f4219887c2210a741ffffff1f713602f802200220022903c804200d421a887c220da741ffffff0f7136028c03200220022903a8042010421a887c2210a741ffffff0f713602fc022002200d42198820022903d0047c220da741ffffff1f713602900320022010421988200b42ffffff1f837c220b421a88200c42ffffff0f837c3e0284032002200ba741ffffff1f71360280032002200d421a8820022903d8047c220ba741ffffff0f71360294032002200b42198842137e200e42ffffff1f837c220b421a88200f42ffffff0f837c3e02f4022002200ba741ffffff1f713602f002200241d8006a4198d4c08000200241f0026a10a681808000200a280200210a2009280200210920082802002108200728020021072002280258211120022802302112200228025c2113200228023421142002280260211520022802642116200228023c211720022802682118200228026c21192002280244211a2002280270211b2002280274211c200228024c211d2002280278211e20022002280254200228027c6a3602b40420022007201e6a3602b0042002201d201c6a3602ac0420022008201b6a3602a8042002201a20196a3602a4042002200920186a3602a0042002201720166a36029c042002200a20156a360298042002201420136a360294042002201220116a3602900420024180016a20024190046a41e8d4c0800010a681808000200241f0026a4190d5c08000200241d8006a10a681808000200241d8056a200241086a200241f0026a10a8818080002002200228027c41b39ba00a6a3602b4042002200228027841ffed8a176a3602b004200220022802744198e3b90e6a3602ac042002200228027041bbf9801d6a3602a8042002200228026c4198d1e70b6a3602a4042002200228026841a980076a3602a00420022002280264418e94a8036a36029c042002200228026041bdddd5186a360298042002200228025c4184e5cd066a360294042002200228025841a3f1e51a6a36029004200241a8016a200241d8056a20024190046a10a68180800020024190046a20024180016a200241a8016a10a48180800020022d0090042111200241d0016a41206a2207200241b4046a290200370300200241d0016a41186a2208200241ac046a290200370300200241d0016a41106a2209200241a4046a290200370300200241d0016a41086a220a2002419c046a29020037030020022002290294043703d001200241f8016a200241d0016a200110a68180800020024190046a200241f8016a10a38180800020022d00900441017110ad8d808000417f7341017110ad8d808000210120024190046a200241f8016a10a981808000200220022802900420022802f8012212734100200141ff01716b2201712012733602f801200220022802940420022802fc012212732001712012733602fc012002200228029804200228028002221273200171201273360280022002200228029c0420022802840222127320017120127336028402200220022802a00420022802880222127320017120127336028802200220022802a404200228028c0222127320017120127336028c02200220022802a80420022802900222127320017120127336029002200220022802ac0420022802940222127320017120127336029402200220022802b00420022802980222127320017120127336029802200220022802b404200228029c0222127320017120127336029c022011417f73410171221110ad8d8080002101200a200228028002200a2802002212734100200141ff01716b2201712012733602002009200228028802200928020022127320017120127336020020082002280290022008280200221273200171201273360200200220022802f80120022802d0012212732001712012733602d001200220022802fc0120022802d4012212732001712012733602d401200220022802840220022802dc012212732001712012733602dc012002200228028c0220022802e4012212732001712012733602e401200220022802940220022802ec012212732001712012733602ec01200720022802980220072802002212732001712012733602002002200228029c0220022802f4012212732001712012733602f401201110ad8d80800021012006200228026020062802002211734100200141ff01716b2201712011733602002005200228026820052802002206732001712006733602002004200228027020042802002205732001712005733602002002200228025820022802082204732001712004733602082002200228025c200228020c22047320017120047336020c2002200228026420022802142204732001712004733602142002200228026c200228021c22047320017120047336021c2002200228027420022802242204732001712004733602242003200228027820032802002204732001712004733602002002200228027c200228022c22037320017120037336022c20024190046a200241d8006a200241306a10a881808000200241f0026a200241086a20024190046a10a681808000200241d8056a200241f0026a41b8d5c0800010a681808000200241a0026a200241d8056a200241a8016a10a88180800020024190046a200241d0016a10a581808000200220022903c00420022903b80420022903b004220b421a887c220c4219887c220da741ffffff1f713602e002200220022903a004200229039804200229039004220e421a887c220f4219887c2210a741ffffff1f713602d002200220022903c804200d421a887c220da741ffffff0f713602e402200220022903a8042010421a887c2210a741ffffff0f713602d4022002200d42198820022903d0047c220da741ffffff1f713602e80220022010421988200b42ffffff1f837c220b421a88200c42ffffff0f837c3e02dc022002200ba741ffffff1f713602d8022002200d421a8820022903d8047c220ba741ffffff0f713602ec022002200b42198842137e200e42ffffff1f837c220b421a88200f42ffffff0f837c3e02cc022002200ba741ffffff1f713602c802200220022802f4014101743602fc05200220072802004101743602f805200220022802ec014101743602f405200220082802004101743602f005200220022802e4014101743602ec05200220092802004101743602e805200220022802dc014101743602e4052002200a2802004101743602e005200220022802d4014101743602dc05200220022802d0014101743602d805200241b0056a200241d8056a200241a8016a10a681808000200241f0026a200241a0026a41e0d5c0800010a68180800020024190046a41286a220141c8d3c08000200241c8026a10a881808000200241ac056a20022802ec02360200200241a4056a20022902e4023702002002419c056a20022902dc0237020020024194056a20022902d4023702002002418c056a20022902cc0237020020024190046a41086a200241b0056a41086a29020037030020024190046a41106a200241b0056a41106a29020037030020024190046a41186a200241b0056a41186a29020037030020024190046a41206a200241b0056a41206a290200370300200220022902b00537039004200220022802c80241016a3602880520024180056a200241f0026a41206a290200370300200241f8046a200241f0026a41186a290200370300200241f0046a200241f0026a41106a290200370300200241e8046a200241f0026a41086a290200370300200220022902f0023703e004200241f0026a20024190046a20024190046a41f8006a220710a681808000200241f0026a41286a200120024190046a41d0006a220810a681808000200241f0026a41d0006a2008200710a681808000200241f0026a41f8006a20024190046a200110a6818080002000200241f0026a41a00110848e8080001a20024180066a2480808080000bf90101017f23808080800041d0036b2202248080808000200241186a200141186a290000370300200241106a200141106a290000370300200241086a200141086a29000037030020022001290000370300200241206a200210aa81808000200241c8006a200241206a10b381808000200241e8016a41186a200141386a290000370300200241e8016a41106a200141306a290000370300200241e8016a41086a200141286a290000370300200220012900203703e80120024188026a200241e8016a10aa81808000200241b0026a20024188026a10b3818080002000200241c8006a200241b0026a10b081808000200241d0036a2480808080000bbf0201037f23808080800041e0016b220224808080800020022000200141286a220310a681808000200241286a200041286a2204200110a681808000200241d0006a2000200110a681808000200241f8006a2004200310a681808000200241a0016a200210a381808000200241c0016a200241286a10a38180800041002101410121000340200241a0016a20016a2d0000200241c0016a20016a2d00004610ad8d8080002000712100200141016a22014120470d000b200010ad8d8080002103200241a0016a200241d0006a10a381808000200241c0016a200241f8006a10a38180800041002101410121000340200241a0016a20016a2d0000200241c0016a20016a2d00004610ad8d8080002000712100200141016a22014120470d000b200010ad8d80800020037210ad8d8080002101200241e0016a24808080800020010bf80101037f23808080800041f0006b2202248080808000200241086a200110aa81808000200241306a200241086a10a38180800041002103410121040340200241306a20036a2d0000200120036a2d00004610ad8d8080002004712104200341016a22034120470d000b200410ad8d8080002103200241d0006a200241086a10a38180800020022d005041017110ad8d8080002104200041246a200241286a2902003702002000411c6a200241206a290200370200200041146a200241186a2902003702002000410c6a200241106a29020037020020002002290208370204200020043a0001200020033a0000200241f0006a2480808080000bd51503067f067e0e7f23808080800041a0066b220224808080800041002103200241206a220441002902a8d6c08000370300200241186a220541002902a0d6c08000370300200241106a22064100290298d6c08000370300200241086a22074100290290d6c0800037030020024100290288d6c0800037030020024180056a200110a581808000200220022903b00520022903a80520022903a0052208421a887c22094219887c220aa741ffffff1f713602402002200229039005200229038805200229038005220b421a887c220c4219887c220da741ffffff1f71360230200220022903b805200a421a887c220aa741ffffff0f713602442002200229039805200d421a887c220da741ffffff0f713602342002200a42198820022903c0057c220aa741ffffff1f713602482002200d421988200842ffffff1f837c2208421a88200942ffffff0f837c3e023c20022008a741ffffff1f713602382002200a421a8820022903c8057c2208a741ffffff0f7136024c2002200842198842137e200b42ffffff1f837c2208421a88200c42ffffff0f837c3e022c20022008a741ffffff1f71360228200241d0006a2002200241286a10a881808000200728020021072006280200210620052802002105200428020021042002280200210e2002280228210f20022802042110200228022c211120022802302112200228020c2113200228023421142002280238211520022802142116200228023c211720022802402118200228021c21192002280244211a2002280248211b2002200228024c20022802246a36029c012002201b20046a360298012002201a20196a360294012002201820056a360290012002201720166a36028c012002201520066a360288012002201420136a360284012002201220076a360280012002201120106a36027c2002200f200e6a36027820024180056a200241f8006a10a581808000200220022903b00520022903a80520022903a0052208421a887c22094219887c220aa741ffffff1f713602b8012002200229039005200229038805200229038005220b421a887c220c4219887c220da741ffffff1f713602a801200220022903b805200a421a887c220aa741ffffff0f713602bc012002200229039805200d421a887c220da741ffffff0f713602ac012002200a42198820022903c0057c220aa741ffffff1f713602c0012002200d421988200842ffffff1f837c2208421a88200942ffffff0f837c3e02b40120022008a741ffffff1f713602b0012002200a421a8820022903c8057c2208a741ffffff0f713602c4012002200842198842137e200b42ffffff1f837c2208421a88200c42ffffff0f837c3e02a40120022008a741ffffff1f713602a001200241b8036a41b0d6c0800010a98180800020024180056a200241d0006a10a581808000200220022903b00520022903a80520022903a0052208421a887c22094219887c220aa741ffffff1f713602f8032002200229039005200229038805200229038005220b421a887c220c4219887c220da741ffffff1f713602e803200220022903b805200a421a887c220aa741ffffff0f713602fc032002200229039805200d421a887c220da741ffffff0f713602ec032002200a42198820022903c0057c220aa741ffffff1f71360280042002200d421988200842ffffff1f837c2208421a88200942ffffff0f837c3e02f40320022008a741ffffff1f713602f0032002200a421a8820022903c8057c2208a741ffffff0f71360284042002200842198842137e200b42ffffff1f837c2208421a88200c42ffffff0f837c3e02e40320022008a741ffffff1f713602e00320024180056a200241b8036a200241e0036a10a681808000200241c8016a20024180056a200241a0016a10a881808000200241e0036a200241c8016a200241a0016a10a68180800020024180056a41acd2c08000200241e0036a10a48180800020022d0080052104200241f0016a41206a200241a4056a290200370300200241f0016a41186a2002419c056a290200370300200241f0016a41106a20024194056a290200370300200241f0016a41086a2002418c056a29020037030020022002290284053703f00120024198026a200241f0016a200241f8006a10a68180800020024180056a20024198026a200241c8016a10a681808000200241c0026a200241f0016a20024180056a10a681808000200220012802244101743602a405200220012802204101743602a0052002200128021c41017436029c052002200128021841017436029805200220012802144101743602940520022001280210410174360290052002200128020c41017436028c05200220012802084101743602880520022001280204410174360284052002200128020041017436028005200241e8026a20024180056a20024198026a10a68180800020024180056a200241e8026a10a38180800020022d00800541017110ad8d808000210120024180056a200241e8026a10a981808000200220022802800520022802e8022205734100200141ff01716b2201712005733602e802200220022802840520022802ec022205732001712005733602ec02200220022802880520022802f0022205732001712005733602f0022002200228028c0520022802f4022205732001712005733602f402200220022802900520022802f8022205732001712005733602f802200220022802940520022802fc022205732001712005733602fc022002200228029805200228028003220573200171200573360280032002200228029c0520022802840322057320017120057336028403200220022802a00520022802880322057320017120057336028803200220022802a405200228028c0322057320017120057336028c0320024190036a200241d0006a200241c0026a10a681808000200241b8036a200241e8026a20024190036a10a68180800020024180056a200241b8036a10a38180800020022d00800541017110ad8d8080002105200241e0036a41186a4200370300200241e0036a41106a4200370300200241e0036a41086a4200370300200242003703e00320024180056a20024190036a10a38180800041012101034020024180056a20036a2d0000200241e0036a20036a2d00004610ad8d8080002001712101200341016a22034120470d000b200110ad8d808000210320024180056a41206a200241e8026a41206a29020037030020024180056a41186a200241e8026a41186a29020037030020024180056a41106a200241e8026a41106a29020037030020024180056a41086a200241e8026a41086a290200370300200241b0056a20024190036a41086a290200370300200241b8056a20024190036a41106a290200370300200241c0056a20024190036a41186a290200370300200241c8056a20024190036a41206a290200370300200220022902e8023703800520022002290290033703a805200241f0056a200241206a290300370300200241e8056a200241186a290300370300200241e0056a200241106a290300370300200241d8056a200241086a29030037030020024198066a200241b8036a41206a29020037030020024190066a200241b8036a41186a29020037030020024188066a200241b8036a41106a29020037030020024180066a200241b8036a41086a290200370300200220022903003703d005200220022902b8033703f805200241e0036a20024180056a41a00110848e8080001a200020033a0002200020053a0001200020043a0000200041046a200241e0036a41a00110848e8080001a200241a0066a2480808080000bde0101047f23808080800041106b220324808080800002402001450d00200020014104746a21040340200028020021052003200028020422013602082003200341086a36020c2003410c6a200210cb8180800002402002280200200228020822066b20014f0d0020022006200110b182808000200228020821060b200228020420066a2005200110848e8080001a2002200620016a3602082003200041086a36020c2003410c6a200210cb8180800020032000410c6a36020c2003410c6a200210cb81808000200041106a22002004470d000b0b200341106a2480808080000b9506010e7f20002802042101024020002802082202450d0041002103034002402001200341386c6a2204280200450d00200428020441002802c0a3c68000118080808000000b0240200428020c450d00200441106a28020041002802c0a3c68000118080808000000b0240024002400240024020042d00240e050001040402040b2004412c6a21050240200441306a2802002206450d00200528020021072006410171210841002109024020064101460d002006417e71210a4100210920072106034002402006280200450d00200641046a28020041002802c0a3c68000118080808000000b0240200641206a280200450d00200641246a28020041002802c0a3c68000118080808000000b200641c0006a2106200a200941026a2209470d000b0b2008450d00200720094105746a2206280200450d00200628020441002802c0a3c68000118080808000000b20042802280d020c030b2004412c6a21050240200441306a280200220b450d002005280200210c4100210703400240200c200741246c6a22082802082206450d002008280204210d2006410171210e41002109024020064101460d002006417e71210a41002109200d2106034002402006280200450d00200641046a28020041002802c0a3c68000118080808000000b0240200641206a280200450d00200641246a28020041002802c0a3c68000118080808000000b200641c0006a2106200a200941026a2209470d000b0b200e450d00200d20094105746a2206280200450d00200628020441002802c0a3c68000118080808000000b02402008280200450d00200828020441002802c0a3c68000118080808000000b0240200828020c450d002008410c6a28020441002802c0a3c68000118080808000000b200741016a2207200b470d000b0b20042802280d010c020b200441286a280200450d012004412c6a21050b200528020041002802c0a3c68000118080808000000b02402004280218450d002004411c6a28020041002802c0a3c68000118080808000000b200341016a22032002470d000b0b02402000280200450d00200141002802c0a3c68000118080808000000b0bb703010a7f23808080800041e0016b2202248080808000200128020c2203200128020422046b220541077621062001280208210720012802002108024002400240024020032004470d002002200336020c200220073602082002200836020041002101410421090c010b2005418099b3e67c4b0d01200641d0006c220a417f4c0d01200128021021054100210141002d00fca3c680001a200a41002802c8a3c68000118180808000002209450d022002200336020c20022007360208200220043602042002200836020020024184016a220a41086a210b200921070240034020042802702208418080808078460d01200241106a200441f00010848e8080001a200b200441fc006a280200360200200a2004290274370200200220083602800120024190016a200241106a200510e381808000200720024190016a41d00010848e80800041d0006a2107200141016a210120044180016a2208210420082003470d000b200321040c010b20044180016a21040b20022004360204200210ca81808000200020013602082000200936020420002006360200200241e0016a2480808080000f0b10ae80808000000b4104200a10b280808000000bc204020b7f027e23808080800041c0006b220224808080800020012802082103200128020c220421052001280200220621070240200128020422082004460d002001280210210920062107024003402008220a280200220b418080808078460d01200a28020c210c200a290218210d200a290210210e200a2802042108200a28020821052002200936023c20022008200541386c6a3602382002200b360234200220083602302002200836022c200241086a2002412c6a10bc8180800020072002290308370200200741106a200e370200200741186a200d3702002002200c41ffffffff0171360214200741086a200241086a41086a290300370200200741206a2107200a41206a22082004470d000b0b200a41206a21050b20014284808080c00037020020014280808080c000370208024020042005460d00200420056b41057621044100210c034002402005200c4105746a220b2802082208450d00200b28020441306a210a03400240200a41706a280200450d00200a41746a28020041002802c0a3c68000118080808000000b0240200a417c6a280200450d00200a28020041002802c0a3c68000118080808000000b200a41386a210a2008417f6a22080d000b0b0240200b280200450d00200b28020441002802c0a3c68000118080808000000b0240200b28020c450d00200b410c6a28020441002802c0a3c68000118080808000000b200c41016a220c2004470d000b0b200020063602042000200341ffffff3f713602002000200720066b410576360208200241c0006a2480808080000b8505020d7f017e23808080800041e0006b2202248080808000200128020c2203200128020422046b220541386e21062001280208210720012802002108024002400240024020032004470d00410021094104210a0c010b200541a8e3f1b87c4b0d01200641246c2209417f4c0d012001280210210b4100210c41002d00fca3c680001a200941002802c8a3c6800011818080800000220a450d02200541486a210d200a210541002109024003402004200c6a2201412c6a280200220e418080808078460d01200141306a290300210f200241286a200141286a280200360200200241206a200141206a290300370300200241186a200141186a290300370300200241106a200141106a290300370300200241086a200141086a2903003703002002200f3703302002200e36022c20022001290300370300200241386a2002200b10e281808000200541206a200241386a41206a280200360200200541186a200241386a41186a290300370200200541106a200241386a41106a290300370200200541086a200241386a41086a29030037020020052002290338370200200541246a2105200941016a21092004200c41386a220c6a2003470d000c020b0b200141386a2003460d00200141e8006a2101200d200c6b41386e210503400240200141706a280200450d00200141746a28020041002802c0a3c68000118080808000000b02402001417c6a280200450d00200128020041002802c0a3c68000118080808000000b200141386a21012005417f6a22050d000b0b02402007450d00200841002802c0a3c68000118080808000000b200020093602082000200a36020420002006360200200241e0006a2480808080000f0b10ae80808000000b4104200910b280808000000bac0201087f23808080800041306b2202248080808000200128020c2203200128020422046b220541e8006e2106200128021021072001280208210820012802002109024002400240024020032004470d00410421010c010b200541d0b6dbed7e4b0d01200641386c2205417f4c0d0141002d00fca3c680001a200541002802c8a3c68000118180808000002201450d020b200241046a41086a220541003602002002200136020820022006360204200220073602202002200336021c2002200836021820022004360214200220093602102002200136022c2002410036022820022005360224200241106a200241246a10c881808000200041086a200528020036020020002002290204370200200241306a2480808080000f0b10ae80808000000b4104200510b280808000000bb703010a7f23808080800041d0016b2202248080808000200128020c2203200128020422046b220541f8006e21062001280208210720012802002108024002400240024020032004470d002002200336020c200220073602082002200836020041002101410421090c010b200541f8c2878f7e4b0d01200641c4006c220a417f4c0d01200128021021054100210141002d00fca3c680001a200a41002802c8a3c68000118180808000002209450d022002200336020c200220073602082002200436020420022008360200200241f8006a220a41086a210b200921070240034020042802642208418080808078460d01200241106a200441e40010848e8080001a200b200441f0006a290300370300200a20042903683703002002200836027420024188016a200241106a200510cf81808000200720024188016a41c40010848e80800041c4006a2107200141016a2101200441f8006a2208210420082003470d000b200321040c010b200441f8006a21040b20022004360204200210c981808000200020013602082000200936020420002006360200200241d0016a2480808080000f0b10ae80808000000b4104200a10b280808000000bab0201087f23808080800041306b2202248080808000200128020c2203200128020422046b220541386e2106200128021021072001280208210820012802002109024002400240024020032004470d00410421010c010b200541a8e3f1b87c4b0d01200641246c2205417f4c0d0141002d00fca3c680001a200541002802c8a3c68000118180808000002201450d020b200241046a41086a220541003602002002200136020820022006360204200220073602202002200336021c2002200836021820022004360214200220093602102002200136022c2002410036022820022005360224200241106a200241246a10c581808000200041086a200528020036020020002002290204370200200241306a2480808080000f0b10ae80808000000b4104200510b280808000000b850703077f017e027f2380808080004190016b2202248080808000200241386a200110db81808000024002400240024002402002280258418080808078460d00200228023c210320022802382104200241e8006a41206a200241e0006a290300370300200241e8006a41186a2205200241386a41206a290300370300200241e8006a41106a200241386a41186a290300370300200241f0006a200241386a41106a290300370300200220022903403703682001280224200241e8006a10dd82808000210620052802002205418080808078470d010b2000410036020820004280808080c000370200200110df818080000c010b200128022041016a2207417f20071b22074104200741044b1b220741d5aad52a4b0d01200741186c2208417f4c0d0120024184016a290200210941002d00fca3c680001a200841002802c8a3c6800011818080800000220a450d02200a2006360214200a200937020c200a2005360208200a2003360204200a20043602002002410136020c2002200a36020820022007360204200241106a41206a200141206a290200370300200241106a41186a200141186a290200370300200241106a41106a200141106a290200370300200241106a41086a200141086a29020037030020022001290200370310200241386a200241106a10db8180800002402002280258418080808078460d00200241386a41086a2101412c2104410121030340200228023c210620022802382107200241e8006a41206a200141206a290300370300200241e8006a41186a2205200141186a290300370300200241e8006a41106a200141106a290300370300200241e8006a41086a200141086a290300370300200220012903003703682002280234200241e8006a10dd8280800021082005280200220b418080808078460d012002290284012109024020032002280204470d00200241046a2003200228023041016a2205417f20051b10c3818080002002280208210a0b200a20046a22052008360200200541786a2009370200200541746a200b360200200541706a20063602002005416c6a20073602002002200341016a220336020c200441186a2104200241386a200241106a10db818080002002280258418080808078470d000b0b200241106a10df8180800020002002290204370200200041086a200241046a41086a2802003602000b20024190016a2480808080000f0b10ae80808000000b4104200810b280808000000bd10c01077f2000200110d781808000024002400240024002400240024002400240200128020022004188808080786a410f2000418780808078481b41786a0e0701020304050607000b200110b9818080000240200141146a2802002202450d00200141106a280200210341002104034002402003200441d0006c6a22052802302200418080808078460d00200541306a21060240200541386a2802002207450d00200541346a280200210003400240200041186a2802002208418080808078460d002008450d002000411c6a28020041002802c0a3c68000118080808000000b02402000280200450d00200041046a28020041002802c0a3c68000118080808000000b02402000410c6a280200450d00200041106a28020041002802c0a3c68000118080808000000b200041386a21002007417f6a22070d000b200628020021000b2000450d00200628020441002802c0a3c68000118080808000000b0240200541206a2802002207450d002005411c6a2802002100034002402000280200450d00200041046a28020041002802c0a3c68000118080808000000b02402000410c6a280200450d00200041106a28020041002802c0a3c68000118080808000000b200041246a21002007417f6a22070d000b0b02402005280218450d00200541186a28020441002802c0a3c68000118080808000000b02402005280224450d00200541246a28020441002802c0a3c68000118080808000000b200441016a22042002470d000b0b0240200128020c450d00200128021041002802c0a3c68000118080808000000b02402001280218450d002001411c6a28020041002802c0a3c68000118080808000000b0240200141c0006a2802002204450d002001413c6a28020021064100210503400240200620054105746a22082802082207450d0020082802042100034002402000280200450d00200041046a28020041002802c0a3c68000118080808000000b02402000410c6a280200450d00200041106a28020041002802c0a3c68000118080808000000b200041246a21002007417f6a22070d000b0b02402008280200450d00200828020441002802c0a3c68000118080808000000b0240200828020c450d002008410c6a28020441002802c0a3c68000118080808000000b200541016a22052004470d000b0b02402001280238450d00200128023c41002802c0a3c68000118080808000000b200141d4006a10de818080000f0b2001280204450d06200141086a28020041002802c0a3c68000118080808000000f0b2001280204450d05200141086a28020041002802c0a3c68000118080808000000f0b2001280204450d04200141086a28020041002802c0a3c68000118080808000000f0b2001280204450d03200141086a28020041002802c0a3c68000118080808000000f0b2001280204450d02200141086a28020041002802c0a3c68000118080808000000f0b2001280204450d01200141086a28020041002802c0a3c68000118080808000000f0b200141046a10b9818080000240200141186a2802002202450d00200141146a280200210341002104034002402003200441c4006c6a22052802242200418080808078460d00200541246a210602402005412c6a2802002207450d00200541286a280200210003400240200041186a2802002208418080808078460d002008450d002000411c6a28020041002802c0a3c68000118080808000000b02402000280200450d00200041046a28020041002802c0a3c68000118080808000000b02402000410c6a280200450d00200041106a28020041002802c0a3c68000118080808000000b200041386a21002007417f6a22070d000b200628020021000b2000450d00200628020441002802c0a3c68000118080808000000b0240200541206a2802002207450d002005411c6a2802002100034002402000280200450d00200041046a28020041002802c0a3c68000118080808000000b02402000410c6a280200450d00200041106a28020041002802c0a3c68000118080808000000b200041246a21002007417f6a22070d000b0b02402005280218450d00200541186a28020441002802c0a3c68000118080808000000b200441016a22042002470d000b0b0240200141106a280200450d00200128021441002802c0a3c68000118080808000000b2001411c6a280200450d00200141206a28020041002802c0a3c68000118080808000000b0ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241186c2104200241d6aad52a4941027421050240024020010d00200341003602180c010b200341043602182003200141186c36021c200320002802043602140b200341086a20052004200341146a10c281808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bb21208067f017e027f017e017f017e057f017e23808080800041c0006b220324808080800020002802002104024020002802042205450d0002400240200541037122060d00200521070c010b2005210703402007417f6a2107200420042f018e024102746a4190026a28020021042006417f6a22060d000b0b20054104490d000340200420042f018e024102746a4190026a280200220420042f018e024102746a4190026a280200220420042f018e024102746a4190026a280200220420042f018e024102746a4190026a28020021042007417c6a22070d000b0b200341086a41206a200141206a290200370300200341086a41186a200141186a290200370300200341086a41106a200141106a290200370300200341086a41086a200141086a29020037030020032001290200370308200341146a21080340200329030821092003280224210120032802102106200328022c210a0240024003402003418180808078360210200341086a2105024002402006418180808078460d00200121072006210b2009210c0c010b2001200a460d022003200141186a22073602242001280208210b2001290200210c200121050b0240200b418080808078460d00200528020c210d2005290210210e200c422088a72105200ca7210f02402007200a470d0020034180808080783602100c040b2003200741186a22013602242007290200210920072802082106200729020c210c200841086a200741146a2802003602002008200c370200200320063602102006418080808078460d0320052009422088a7470d03200f2009a7200510888e8080000d03200b450d01200d41002802c0a3c68000118080808000000c010b0b20032009370308200a2007460d00200a20076b220441186e22014101712105410021060240200441686a4118490d00200741246a2104200141feffffff007121014100210603400240200441646a280200450d00200441686a28020041002802c0a3c68000118080808000000b02402004417c6a280200450d00200428020041002802c0a3c68000118080808000000b200441306a21042001200641026a2206470d000b0b2005450d002007200641186c6a2204280208450d00200441086a28020441002802c0a3c68000118080808000000b0240200341286a280200450d00200328022041002802c0a3c68000118080808000000b02402000280204220b450d00200028020021060340024002400240024020062f018e022207450d0020064190026a220520074102746a28020022042f018e02220141054f0d0320052007417f6a220d4102746a28020022052f018e022207410520016b220f490d0120052007200f6b22083b018e02200441053b018e02200441b0016a2200200f4103746a2000200141037410fe8d8080001a2004200f4104746a2004200141047410fe8d8080001a2007200841016a220a6b2207410420016b470d022000200541b0016a2202200a4103746a2007410374221010848e808000210020042005200a4104746a2007410474221110848e8080002107200341086a41086a200520084104746a221241086a290200220c3703002006200d4103746a41b0016a2213290200210e201229020021092013200220084103746a290200370200200320093703082006200d4104746a2206290200211420062009370200200641086a220629020021092006200c370200200020106a200e370200200720116a22062014370200200641086a2009370200200b4101460d0320074190026a2206200f410274220f6a2006200141027441046a10fe8d8080001a20062005200a4102746a4190026a200f10848e8080001a200728029002220641003b018c02200620073602880220074194026a280200220641013b018c02200620073602880220074198026a280200220641023b018c0220062007360288022007419c026a280200220641033b018c022006200736028802200741a0026a280200220641043b018c022006200736028802200741a4026a280200220641053b018c0220062007360288020c030b41f8d8c0800041194194d9c0800010f880808000000b41c0d8c08000412741e8d8c0800010f880808000000b4188d8c08000412841b0d8c0800010f880808000000b20042106200b417f6a220b0d000b0b200341c0006a2480808080000f0b20032009370308024002400240024002400240024020042f018e022207410b490d004100210a0240024003402004280288022204450d01200a41016a210a20042f018e02410b4f0d000c020b0b200028020421062000280200210741002d00fca3c680001a41c00241002802c8a3c68000118180808000002204450d022004200736029002200441003b018e02200441003602880220002004360200200741003b018c0220072004360288022000200641016a220a3602040b41002d00fca3c680001a41900241002802c8a3c68000118180808000002206450d02200641003b018e022006410036028802200a417f6a2201450d04034041002d00fca3c680001a41c00241002802c8a3c68000118180808000002207450d042007200636029002200741003b018e022007410036028802200641003b018c022006200736028802200721062001417f6a2201450d050c000b0b2004200741016a3b018e02200420074103746a220641b0016a200f360200200641b4016a2005360200200420074104746a2207200b3602002007200d3602042007200e3702080c040b410441c00210b280808000000b410441900210b280808000000b410441c00210b280808000000b20042f018e022207410b4f0d012004200741016a22013b018e02200420074103746a221041b0016a200f360200201041b4016a2005360200200420074104746a2207200b3602002007200d3602042007200e370208200420014102746a4190026a2006360200200620013b018c022006200436028802200a450d0002400240200a41037122060d00200a21070c010b200a210703402007417f6a2107200420042f018e024102746a4190026a28020021042006417f6a22060d000b0b200a4104490d000340200420042f018e024102746a4190026a280200220420042f018e024102746a4190026a280200220420042f018e024102746a4190026a280200220420042f018e024102746a4190026a28020021042007417c6a22070d000b0b2002200228020041016a3602000c010b0b41d8d6c08000412041f8d7c0800010f880808000000bc804020d7f037e23808080800041c0006b22022480808080002001280204210320012802002104200028020821052000280200210602400240024020002802042207200028020c2208460d00200028021021092001280208200341246c6a2101200820076b41486a210a200241086a41206a210b200241086a41086a210c4100210d03402007200d6a2200412c6a280200220e418080808078460d02200041306a290300210f200241086a41286a200041286a280200360200200b200041206a290300370300200241086a41186a200041186a290300370300200241086a41106a200041106a290300370300200c200041086a2903003703002002200029030022103703082002200f3703382002200e3602342009200c10dd828080002100200b290200210f2002280234210e20022903382111200141086a200b41086a2802003602002001200f370200200141206a2000360200200141186a2010370200200141106a20113702002001410c6a200e41ffffffff0171360200200141246a2101200341016a21032007200d41386a220d6a2008470d000b0b200420033602000c010b20042003360200200041386a2008460d00200041e8006a2100200a200d6b41386e210103400240200041706a280200450d00200041746a28020041002802c0a3c68000118080808000000b02402000417c6a280200450d00200028020041002802c0a3c68000118080808000000b200041386a21002001417f6a22010d000b0b02402005450d00200641002802c0a3c68000118080808000000b200241c0006a2480808080000bab02010d7f23808080800041206b220224808080800020012802042103200128020021042000280208210520002802002106024020002802042207200028020c2208460d002000280210210920012802082003410c6c6a2100200241086a210a2002410472220b41186a210c200b41106a210d034020072802002201450d01200b2007290204370200200c2007411c6a280200360200200d200741146a290200370200200b41086a2007410c6a290200370200200220013602002002280204210e200041086a2009200a10dd82808000360200200041046a200e360200200020013602002000410c6a2100200341016a2103200741206a22072008470d000b0b2004200336020002402005450d00200641002802c0a3c68000118080808000000b200241206a2480808080000b870301107f23808080800041c0006b220224808080800020012802042103200128020021042000280208210520002802002106024020002802042207200028020c2208460d0020002802102109200128020820034104746a2100200241086a41206a210a200241086a41086a210b200241086a410472220141306a210c200141286a210d200141186a210e03402007280200220f450d0120012007290204370200200c200741346a280200360200200d2007412c6a290200370200200141206a200741246a290200370200200e2007411c6a290200370200200141106a200741146a290200370200200141086a2007410c6a2902003702002002200f360208200228020c21102009200b10dd8280800021112000410c6a2009200a10dd82808000360200200041086a2011360200200041046a20103602002000200f360200200041106a2100200341016a2103200741386a22072008470d000b0b2004200336020002402005450d00200641002802c0a3c68000118080808000000b200241c0006a2480808080000b9a0605097f027e087f027e017f23808080800041c0006b2202248080808000200128020421032001280200210420002802082105200028020021060240024020002802042207200028020c2208460d00200028021021092001280208200341386c6a2100200241086a2101024003402007280200220a4102460d012001200741d0006a28020036020020022007290248370300200741106a290200210b2007290208210c20072d0060210d200728025c210e2007280258210f2007280254211020072802442111200728024021122007280218211302400240200a0d002002200c370328200220133602382002200b3703302009200241286a10dd82808000210a41808080807821140c010b200741306a2902002115200729022821162007280238210a200728021c211720072802042114200220072802203602202002201736021c200220133602182002200b37031020022015370330200220163703282002200a3602382009200241106a10dd8280800021172009200241286a10dd828080002113200ca7210a0b20002002290300370200200041346a200d3a0000200041306a20113602002000412c6a2012360200200041286a2013360200200041246a2017360200200041206a200c422088a73602002000411c6a200a360200200041186a2014360200200041146a200e360200200041106a200f3602002000410c6a201041ffffffff0171360200200041086a2001280200360200200041386a2100200341016a2103200741e8006a22072008470d000b200420033602000c020b200741e8006a21070b20042003360200200820076b41e8006e210020082007460d00034002402007280200450d00200741046a280200450d00200741086a28020041002802c0a3c68000118080808000000b0240200741c8006a280200450d00200741cc006a28020041002802c0a3c68000118080808000000b0240200741d4006a280200450d00200741d8006a28020041002802c0a3c68000118080808000000b200741e8006a21072000417f6a22000d000b0b02402005450d00200641002802c0a3c68000118080808000000b200241c0006a2480808080000be00301077f200028020c2201200028020422026b41f8006e2103024020012002460d0041002104034002402002200441f8006c6a22052802502201418080808078460d000240200541d0006a22062802082207450d0020062802042101034002402001280200450d00200141046a280200450d00200141086a28020041002802c0a3c68000118080808000000b0240200141c8006a280200450d00200141cc006a28020041002802c0a3c68000118080808000000b0240200141d4006a280200450d00200141d8006a28020041002802c0a3c68000118080808000000b200141e8006a21012007417f6a22070d000b200628020021010b2001450d00200628020441002802c0a3c68000118080808000000b0240200541ec006a2802002207450d00200541e8006a28020041306a210103400240200141706a280200450d00200141746a28020041002802c0a3c68000118080808000000b02402001417c6a280200450d00200128020041002802c0a3c68000118080808000000b200141386a21012007417f6a22070d000b0b0240200541e4006a2201280200450d00200128020441002802c0a3c68000118080808000000b200441016a22042003470d000b0b02402000280208450d00200028020041002802c0a3c68000118080808000000b0b820401077f0240200028020c220120002802042202460d00200120026b41077621034100210403400240200220044107746a22052802502201418080808078460d000240200541d0006a22062802082207450d0020062802042101034002402001280200450d00200141046a280200450d00200141086a28020041002802c0a3c68000118080808000000b0240200141c8006a280200450d00200141cc006a28020041002802c0a3c68000118080808000000b0240200141d4006a280200450d00200141d8006a28020041002802c0a3c68000118080808000000b200141e8006a21012007417f6a22070d000b200628020021010b2001450d00200628020441002802c0a3c68000118080808000000b0240200541ec006a2802002207450d00200541e8006a28020041306a210103400240200141706a280200450d00200141746a28020041002802c0a3c68000118080808000000b02402001417c6a280200450d00200128020041002802c0a3c68000118080808000000b200141386a21012007417f6a22070d000b0b0240200541e4006a2201280200450d00200128020441002802c0a3c68000118080808000000b02402005280270450d00200541f0006a28020441002802c0a3c68000118080808000000b200441016a22042003470d000b0b02402000280208450d00200028020041002802c0a3c68000118080808000000b0b950301037f0240200028020022022802002200413f4b0d00200041027421020240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a20023a00000f0b0240200041ffff004b0d002000410274410172210202402001280200200128020822006b41014b0d0020012000410210b182808000200128020821000b2001200041026a360208200128020420006a20023b00000f0b0240200041ffffffff034b0d002000410274410272210202402001280200200128020822006b41034b0d0020012000410410b182808000200128020821000b2001200041046a360208200128020420006a20023600000f0b02402001280200220320012802082200470d0020012000410110b18280800020012802002103200128020821000b2001280204220420006a41033a00002001200041016a2200360208200228020021020240200320006b41034b0d0020012000410410b18280800020012802042104200128020821000b2001200041046a360208200420006a20023600000bd80301057f23808080800041106b22022480808080002000280218210320022000411c6a28020022043602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822056b20044f0d0020012005200410b182808000200128020821050b200128020420056a2003200410848e8080001a2001200520046a3602082002200041206a36020c2002410c6a200110cb81808000200028020421032002200028020822043602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822056b20044f0d0020012005200410b182808000200128020821050b200128020420056a2003200410848e8080001a2001200520046a360208200041106a28020021042002200041146a28020022003602082002200241086a36020c2002410c6a200110cb8180800002402000450d00200420004103746a21060340200428020021032002200428020422003602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822056b20004f0d0020012005200010b182808000200128020821050b200128020420056a2003200010848e8080001a2001200520006a360208200441086a22042006470d000b0b200241106a2480808080000bdf0601067f23808080800041106b2202248080808000200028022c21032002200041306a28020022043602082002200241086a36020c2002410c6a200110cb81808000024020012802002205200128020822066b20044f0d0020012006200410b18280800020012802002105200128020821060b2001280204220720066a2003200410848e8080001a2001200620046a220436020820002d00342106024020052004470d0020012005410110b18280800020012802042107200128020821040b200720046a20063a00002001200441016a2204360208024002402000280218418080808078470d00024020012802002004470d0020012004410110b18280800020012802042107200128020821040b2000411c6a21062001200441016a360208200720046a41003a00000c010b024020012802002004470d0020012004410110b18280800020012802042107200128020821040b2001200441016a360208200720046a41013a0000200028021c21062002200041206a28020022073602082002200241086a36020c2002410c6a200110cb8180800002402007450d00034020062d000021050240200128020020012802082204470d0020012004410110b182808000200128020821040b200641016a21062001200441016a360208200128020420046a20053a00002007417f6a22070d000b0b2002200041246a36020c2002410c6a200110cb81808000200041286a21060b2002200636020c2002410c6a200110cb81808000200028020421072002200028020822043602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822066b20044f0d0020012006200410b182808000200128020821060b200128020420066a2007200410848e8080001a2001200620046a360208200041106a28020021042002200041146a28020022063602082002200241086a36020c2002410c6a200110cb8180800002402006450d00200420064103746a21000340200428020021052002200428020422063602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822076b20064f0d0020012007200610b182808000200128020821070b200128020420076a2005200610848e8080001a2001200720066a360208200441086a22042000470d000b0b200241106a2480808080000bcf0701117f23808080800041b0016b220624808080800020062004370310200620033703082006200536021841002107200641206a41206a41003602002006420037023420064200370228200642808080808001370220200620012802003602800120062001280204220536027c2006200536027820062005200128020841f8006c6a360284012006200641206a36028801200641c4006a200641f8006a10be81808000200641206a200210dd8280800021082002411c6a28020021092002280218210a20022d0024210b024002400240200241206a280200220c0d004104210d0c010b4100210741002d00fca3c680001a200c410474220241002802c8a3c6800011818080800000220d450d012009200c41386c6a210e200641f8006a41206a210f200641f8006a41086a2110200641f8006a410472220541306a2111200541286a2112200541186a2113200d210120092102034020022802002214450d01200520022902043702002011200241346a28020036020020122002412c6a290200370200200541206a200241246a29020037020020132002411c6a290200370200200541106a200241146a290200370200200541086a2002410c6a29020037020020062014360278200628027c2115200641206a201010dd8280800021162001410c6a200641206a200f10dd82808000360200200141086a2016360200200141046a201536020020012014360200200141106a2101200741016a2107200241386a2202200e470d000b0b0240200a450d00200941002802c0a3c68000118080808000000b200641206a200641086a10dd828080002101200641d0006a41206a200641206a41206a2802002205360200200641d0006a41186a2214200641206a41186a2902002204370300200641d0006a41106a200641206a41106a290200370300200641d0006a41086a200641206a41086a290200370300200620062902203703502006200541002004a722021b360298012006200641d0006a411c6a28020022053602940120062002360290012006410036028c0120062002410047221536028801200620053602840120062002360280012006410036027c200620153602782000200641f8006a10c882808000200641dc006a10d38280800002402006280250450d00200628025441002802c0a3c68000118080808000000b201410d482808000200041146a200641c4006a41086a2802003602002000200629024437020c2000200136022c200041286a200b3a0000200041246a2008360200200041206a20073602002000411c6a200d3602002000200c360218200641b0016a2480808080000f0b4104200210b280808000000b900503057f017e067f23808080800041c0006b2203248080808000200141cc006a2802002104200128024821054180808080782106024020012802502207418080808078460d00200141dc006a2902002108200141d4006a2802002109200141d8006a28020021062003200236023820032009200641e8006c6a360234200320073602302003200936022c20032009360228200341146a200341286a10bd81808000200341086a200837030020032003290218370300200328021421060b4100210702400240200128021022090d004100210a0c010b200129030021082003200141086a29030037033020032008370328200320093602382002200341286a10dd82808000210b4101210a0b02400240200141286a28020022090d000c010b200129031821082003200141206a29030037033020032008370328200320093602382002200341286a10dd82808000210c410121070b200141ec006a280200210d200141e8006a28020021092001280264210e200320023602382003200e3602302003200936022c2003200936022820032009200d41386c6a360234200341146a200341286a10bf8180800002400240200141c0006a28020022090d00410021020c010b200129033021082003200141386a29030037033020032008370328200320093602382002200341286a10dd828080002109410121020b2000200536023820002006360224200020073602082000200b3602042000200a36020020002003290214370218200020023602102000413c6a2004360200200041286a20032903003702002000410c6a200c360200200020012d00703a0040200041146a2009360200200041306a200341086a290300370200200041206a200341146a41086a280200360200200341c0006a2480808080000b850301037f23808080800041106b2202248080808000200028020421032002200028020822043602082002200241086a36020c2002410c6a200110cb8180800002402004450d00200441386c210403402003200110d681808000200341386a2103200441486a22040d000b0b200041106a28020021032002200041146a28020022043602082002200241086a36020c2002410c6a200110cb8180800002402004450d00200441c4006c210403402003200110d181808000200341c4006a2103200441bc7f6a22040d000b0b2002200041246a36020c2002410c6a200110cb81808000200041286a2d000021040240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a20043a00002001200341016a3602082000411c6a28020021032002200041206a28020022043602082002200241086a36020c2002410c6a200110cb8180800020032004200110b88180800020022000412c6a36020c2002410c6a200110cb81808000200241106a2480808080000bb00701057f23808080800041106b22022480808080002000280238210320022000413c6a28020022043602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822056b20044f0d0020012005200410b182808000200128020821050b2001280204220620056a2003200410848e8080001a2001200520046a2204360208024002402000280224418080808078470d00024020012802002004470d0020012004410110b18280800020012802042106200128020821040b2001200441016a360208200620046a41003a00000c010b200041246a2105024020012802002004470d0020012004410110b18280800020012802042106200128020821040b2001200441016a360208200620046a41013a00002005200110d2818080000b0240024020002802000d000240200128020020012802082204470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a41003a00000c010b0240200128020020012802082204470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a41013a00002002200041046a36020c2002410c6a200110cb818080000b0240024020002802080d000240200128020020012802082204470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a41003a00000c010b0240200128020020012802082204470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a41013a000020022000410c6a36020c2002410c6a200110cb818080000b2000411c6a28020021042002200041206a28020022053602082002200241086a36020c2002410c6a200110cb8180800002402005450d00200541246c210503402004200110cc81808000200441246a21042005415c6a22050d000b0b0240024020002802100d000240200128020020012802082205470d0020012005410110b182808000200128020821050b2001200541016a2204360208200128020420056a41003a00000c010b0240200128020020012802082204470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a41013a00002002200041146a36020c2002410c6a200110cb81808000200128020821040b20002d00402105024020012802002004470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a20053a0000200241106a2480808080000be80101047f23808080800041106b2202248080808000200028020c21032002200041106a28020022043602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822056b20044f0d0020012005200410b182808000200128020821050b200128020420056a2003200410848e8080001a2001200520046a360208200028020421042002200028020822003602082002200241086a36020c2002410c6a200110cb8180800002402000450d00200041386c210003402004200110cd81808000200441386a2104200041486a22000d000b0b200241106a2480808080000ba80201047f23808080800041106b22022480808080002000280208210320022000410c6a28020022043602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822056b20044f0d0020012005200410b182808000200128020821050b200128020420056a2003200410848e8080001a2001200520046a22043602080240024020002802000d00024020012802002004470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a41003a00000c010b024020012802002004470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a41013a00002002200041046a360204200241046a200110cb818080000b200241106a2480808080000bdb0501057f23808080800041106b220224808080800002400240200028020c22030d000240200128020020012802082204470d0020012004410110b182808000200128020821040b200128020420046a41003a0000200441016a21040c010b0240200128020020012802082204470d0020012004410110b182808000200128020821040b200128020420046a41013a00002001200441016a3602082002200041106a28020022043602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822056b20044f0d0020012005200410b182808000200128020821050b200128020420056a2003200410848e8080001a200520046a21040b200120043602082002200041146a36020c2002410c6a200110cb8180800002400240200028021822030d000240200128020020012802082204470d0020012004410110b182808000200128020821040b200128020420046a41003a0000200441016a21040c010b0240200128020020012802082204470d0020012004410110b182808000200128020821040b200128020420046a41013a00002001200441016a36020820022000411c6a28020022043602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822056b20044f0d0020012005200410b182808000200128020821050b200128020420056a2003200410848e8080001a200520046a21040b20012004360208200028020421042002200028020822003602082002200241086a36020c2002410c6a200110cb8180800002402000450d00200420004103746a21060340200428020021032002200428020422003602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822056b20004f0d0020012005200010b182808000200128020821050b200128020420056a2003200010848e8080001a2001200520006a360208200441086a22042006470d000b0b200241106a2480808080000beb0301057f23808080800041106b22022480808080002000280218210320022000411c6a28020022043602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822056b20044f0d0020012005200410b182808000200128020821050b200128020420056a2003200410848e8080001a2001200520046a360208200028020421042002200028020822053602082002200241086a36020c2002410c6a200110cb8180800002402005450d002005410574210503402004200110d481808000200441206a2104200541606a22050d000b0b20002d002021050240200128020020012802082204470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a20053a0000200041106a28020021042002200041146a28020022053602082002200241086a36020c2002410c6a200110cb8180800002402005450d00200420054103746a21060340200428020021032002200428020422053602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822006b20054f0d0020012000200510b182808000200128020821000b200128020420006a2003200510848e8080001a2001200020056a360208200441086a22042006470d000b0b200241106a2480808080000bb70c01067f23808080800041106b22022480808080002002200041346a36020c2002410c6a200110cb81808000200028020421032002200028020822043602082002200241086a36020c2002410c6a200110cb8180800002402004450d00200320044103746a21050340200328020021062002200328020422043602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822076b20044f0d0020012007200410b182808000200128020821070b200128020420076a2006200410848e8080001a2001200720046a360208200341086a22032005470d000b0b200041106a28020021032002200041146a28020022043602082002200241086a36020c2002410c6a200110cb8180800002402004450d002004410474210403402003200110d381808000200341106a2103200441706a22040d000b0b02400240024002400240024002400240024020002d00240e080001020304050607000b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41003a00002001200341016a3602082000412c6a28020021032002200041306a28020022043602082002200241086a36020c2002410c6a200110cb818080002004450d072004410574210403402003200110d481808000200341206a2103200441606a22040d000c080b0b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41013a00002001200341016a3602082000412c6a28020021032002200041306a28020022043602082002200241086a36020c2002410c6a200110cb818080002004450d06200441246c210403402003200110d581808000200341246a21032004415c6a22040d000c070b0b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41023a00002002200041286a36020c2002410c6a200110cb818080000c050b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41033a00002001200341016a2203360208200041286a28020021040240200128020020036b41034b0d0020012003410410b182808000200128020821030b2001200341046a360208200128020420036a200436000020022000412c6a36020c2002410c6a200110cb818080000c040b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41043a00002001200341016a3602082000412c6a28020021032002200041306a28020022043602082002200241086a36020c2002410c6a200110cb818080002004450d032004410274210403402002200336020c2002410c6a200110cb81808000200341046a21032004417c6a22040d000c040b0b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41053a00002001200341016a2203360208200041256a2d00002104024020012802002003470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a20043a00000c020b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41063a00002002200041286a36020c2002410c6a200110cb818080000c010b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41073a00002002200041286a36020c2002410c6a200110cb8180800020022000412c6a36020c2002410c6a200110cb818080000b2000411c6a28020021032002200041206a28020022043602082002200241086a36020c2002410c6a200110cb8180800002402004450d00200320044103746a21050340200328020021062002200328020422043602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822076b20044f0d0020012007200410b182808000200128020821070b200128020420076a2006200410848e8080001a2001200720046a360208200341086a22032005470d000b0b200241106a2480808080000bf40b01037f23808080800041206b220224808080800002400240417f200110d881808000220341046a220420042003491b2203417f4c0d0041002d00fca3c680001a200341002802c8a3c68000118180808000002204450d01200220043602102002200336020c200420012802603600004104210420024104360214024002400240024002400240024002400240200128020022034188808080786a410f2003418780808078481b41786a0e080001020304050607000b410421030240200228020c4104470d002002410c6a4104410110b182808000200228021421030b200228021020036a41083a00002002200341016a360214200141086a280200210420022001410c6a28020022013602182002200241186a36021c2002411c6a2002410c6a10cb818080000240200228020c200228021422036b20014f0d002002410c6a2003200110b182808000200228021421030b200228021020036a2004200110848e8080001a2002200320016a3602140c070b410421030240200228020c4104470d002002410c6a4104410110b182808000200228021421030b200228021020036a41093a00002002200341016a360214200141086a280200210420022001410c6a28020022013602182002200241186a36021c2002411c6a2002410c6a10cb818080000240200228020c200228021422036b20014f0d002002410c6a2003200110b182808000200228021421030b200228021020036a2004200110848e8080001a2002200320016a3602140c060b410421030240200228020c4104470d002002410c6a4104410110b182808000200228021421030b200228021020036a410a3a00002002200341016a360214200141086a280200210420022001410c6a28020022013602182002200241186a36021c2002411c6a2002410c6a10cb818080000240200228020c200228021422036b20014f0d002002410c6a2003200110b182808000200228021421030b200228021020036a2004200110848e8080001a2002200320016a3602140c050b410421030240200228020c4104470d002002410c6a4104410110b182808000200228021421030b200228021020036a410b3a00002002200341016a360214200141086a280200210420022001410c6a28020022013602182002200241186a36021c2002411c6a2002410c6a10cb818080000240200228020c200228021422036b20014f0d002002410c6a2003200110b182808000200228021421030b200228021020036a2004200110848e8080001a2002200320016a3602140c040b410421030240200228020c4104470d002002410c6a4104410110b182808000200228021421030b200228021020036a410c3a00002002200341016a360214200141086a280200210420022001410c6a28020022013602182002200241186a36021c2002411c6a2002410c6a10cb818080000240200228020c200228021422036b20014f0d002002410c6a2003200110b182808000200228021421030b200228021020036a2004200110848e8080001a2002200320016a3602140c030b410421030240200228020c4104470d002002410c6a4104410110b182808000200228021421030b200228021020036a410d3a00002002200341016a360214200141086a280200210420022001410c6a28020022013602182002200241186a36021c2002411c6a2002410c6a10cb818080000240200228020c200228021422036b20014f0d002002410c6a2003200110b182808000200228021421030b200228021020036a2004200110848e8080001a2002200320016a3602140c020b410421030240200228020c4104470d002002410c6a4104410110b182808000200228021421030b200228021020036a410e3a00002002200341016a360214200141046a2002410c6a10d0818080000c010b0240200228020c4104470d002002410c6a4104410110b182808000200228021421040b200228021020046a410f3a00002002200441016a36021420012002410c6a10e5818080000b2000200229020c370200200041086a2002410c6a41086a280200360200200241206a2480808080000f0b10ae80808000000b4101200310b280808000000b840301057f024002400240024002400240024002400240200028020022014188808080786a410f2001418780808078481b41786a0e080001020304050607000b2000410c6a28020041046a21000c070b2000410c6a28020041046a21000c060b2000410c6a28020041046a21000c050b2000410c6a28020041046a21000c040b2000410c6a28020041046a21000c030b2000410c6a28020041046a21000c020b200041186a21022000410c6a2103410621010240200041286a280200220441c000490d0041072101200441808001490d004109410a2004418080808004491b21010b2002280200210420032802002103200041246a2802002105410121020240200041306a280200220041c000490d0041022102200041808001490d00410441052000418080808004491b21020b417f417f417f200441c4006c200341386c41047222006a41046a220320032000491b220020054104742001726a220120012000491b220020026a220120012000491b21000c010b200010e48180800021000b200041016a0b931003137f017e047f23808080800041206b2203248080808000024002400240024020014115490d0041002d00fca3c680001a0240200141017641186c41002802c8a3c68000118180808000002204450d0041002d00fca3c680001a41800141002802c8a3c68000118180808000002205450d04200041686a2106200041346a210741002108410021094110210a0240034020002008220b41186c220c6a210d0240024002402001200b6b220e4102490d000240200d41186a280200220f200d280200200d411c6a2802002210200d41046a280200221120102011491b10888e8080002212201020116b20121b4100480d0041022112200e4102460d022007200c6a21114102211203402011417c6a2802002213200f20112802002214201020142010491b10888e808000220f201420106b200f1b4100480d03201141186a2111201421102013210f200e201241016a2212460d020c000b0b410221120240200e4102460d002007200c6a21114102211203402011417c6a2802002213200f20112802002214201020142010491b10888e808000220f201420106b200f1b417f4a0d01201141186a2111201421102013210f200e201241016a2212470d000b200e21120b024002402012200b6a22082012490d00200820014b0d0120124102490d042012410176210e200c201241186c6a2115200621142000210f0340200f200c6a221041086a221329020021162013201420156a221141086a221729020037020020172016370200201141146a2802002113201141106a221728020021182017201041106a221929020037020020102902002116201020112902003702002011201637020020192018360200201041146a2013360200201441686a2114200f41186a210f200e417f6a220e0d000c050b0b200b200841c8dbc08000109681808000000b2008200141c8dbc08000109581808000000b200e21120b2012200b6a21080b024002402008200b490d00200820014d0d010b41b8dcc08000412c41e4dcc0800010f880808000000b024002400240200820014f0d002012410a490d010b2008200b6b21100c010b200b410a6a2210200120102001491b2208200b490d02200d2008200b6b221020124101201241014b1b10da818080000b024002402009200a470d0041002d00fca3c680001a200941047441002802c8a3c68000118180808000002211450d012009410174210a20112005200941037410848e8080002111200541002802c0a3c6800011808080800000201121050b200520094103746a2211200b36020420112010360200200941016a22152109024020154102490d0003400240024002400240200520152213417f6a22154103746a2209280200221020092802046a2001460d00201341037420056a221441706a280200221220104d0d0041022109201341024d0d0520052013417d6a22184103746a2802002211201220106a4d0d0141032109201341034d0d05201441606a280200201120126a4d0d01201321090c050b20134103490d0120052013417d6a22184103746a28020021110b20112010490d010b2013417e6a21180b024002400240024002400240201320184d0d002013201841016a22104d0d01200520104103746a22192802042019280200221a6a2211200520184103746a220d280204220c490d02201120014b0d032000200c41186c6a2209200d280200221741186c22126a2110201141186c211402402011200c6b220f20176b221120174f0d0020042010201141186c221210848e808000220e20126a211220174101480d0520114101480d05200620146a2111034020112012201241686a2214280200201041686a220f280200201441046a2802002214200f41046a280200220f2014200f491b10888e808000220b2014200f6b200b1b2214411f75220f417f7341186c6a22122010200f41186c6a22102014417f4a1b2214290200370200201141106a201441106a290200370200201141086a201441086a290200370200201020094d0d06201141686a21112012200e4d0d060c000b0b20042009201210848e808000221120126a21120240201741014e0d00201121110c060b0240200f20174a0d00201121110c060b200020146a210e20112111034020092011201020102802002011280200201041046a2802002214201141046a280200220f2014200f491b10888e808000220b2014200f6b200b1b220b417f4a220f1b2214290200370200200941106a201441106a290200370200200941086a201441086a290200370200200941186a21092011200f41186c6a221120124f0d062010200b411f7641186c6a2210200e490d000c060b0b200341146a42003702002003410136020c200341ccdac08000360208200341d4dac08000360210200341086a41d8dbc0800010f680808000000b200341146a42003702002003410136020c200341ccdac08000360208200341d4dac08000360210200341086a41e8dbc0800010f680808000000b200c201141f8dbc08000109681808000000b2011200141f8dbc08000109581808000000b20102109200e21110b20092011201220116b10848e8080001a2019200c3602042019201a20176a360200200d200d41086a20132018417f736a41037410fe8d8080001a41012109201541014b0d000b0b200820014f0d050c010b0b41a8dcc0800010a081808000000b200b200841f4dcc08000109681808000000b4188dcc0800010a081808000000b200141014d0d0120002001410110da818080000c010b200541002802c0a3c6800011808080800000200441002802c0a3c68000118080808000000b200341206a2480808080000f0b4198dcc0800010a081808000000bba0303097f017e017f23808080800041106b220324808080800002402002417f6a20014f0d000240200220014f0d00200241186c20006a41506a2104034002402000200241186c6a22052802002206200541686a2207280200200541046a2802002208200741046a280200220920082009491b10888e808000220a200820096b200a1b417f4a0d0020052007290200370200200341086a220b200541106a2209290200370300200541086a2205290200210c2005200741086a2902003702002009200741106a2902003702002003200c370300024020024101460d004101210d200421050340200541186a2107200620052802002008200541046a280200220920082009491b10888e808000220a200820096b200a1b417f4a0d0120072005290200370200200741106a200541106a290200370200200741086a200541086a290200370200200541686a21052002200d41016a220d470d000b200021070b200720083602042007200636020020072003290300370208200741106a200b2903003702000b200441186a2104200241016a22022001470d000b0b200341106a2480808080000f0b4184ddc08000412e41b4ddc0800010f880808000000bef0602077f017e0240200128022022020d00200128020021022001410036020002402002450d000240200128020422020d0020012802082102200128020c2203450d0002400240200341077122040d00200321050c010b2003210503402005417f6a210520022802980421022004417f6a22040d000b0b20034108490d000340200228029804280298042802980428029804280298042802980428029804280298042102200541786a22050d000b0b03402002280290042105200241002802c0a3c68000118080808000002005210220050d000b0b20004180808080783602200f0b20012002417f6a360220200128020421020240024002400240024020012802002205450d002002450d010b2005450d022001410c6a2802002104200141086a28020021030c010b200141086a280200210202402001410c6a2802002203450d0002400240200341077122040d00200321050c010b2003210503402005417f6a210520022802980421022004417f6a22040d000b0b20034108490d000340200228029804280298042802980428029804280298042802980428029804280298042102200541786a22050d000b0b20014200370208200120023602042001410136020041002104410021030b0240200420022f0196044f0d00200221050c020b024003402002280290042205450d0120022f0194042104200241002802c0a3c6800011808080800000200341016a210320052102200420052f019604490d030c000b0b200241002802c0a3c680001180808080000041a8dac0800010a081808000000b41c8dec0800010a081808000000b200441016a21060240024020030d00200521020c010b200520064102746a4198046a2802002102410021062003417f6a2207450d002003417e6a2108024020074107712203450d0003402007417f6a210720022802980421022003417f6a22030d000b0b20084107490d000340200228029804280298042802980428029804280298042802980428029804280298042102200741786a22070d000b0b2001200636020c2001410036020820012002360204200520044103746a290200210920002005200441286c6a220241d8006a290300370308200041106a200241e0006a290300370300200041186a200241e8006a290300370300200041206a200241f0006a290300370300200041286a200241f8006a290300370300200020093703000be50101047f23808080800041106b2203248080808000200320013602082003200341086a36020c2003410c6a200210cb8180800002402001450d0020002001410c6c6a21040340200028020021052003200028020422013602082003200341086a36020c2003410c6a200210cb8180800002402002280200200228020822066b20014f0d0020022006200110b182808000200228020821060b200228020420066a2005200110848e8080001a2002200620016a3602082003200041086a36020c2003410c6a200210cb818080002000410c6a22002004470d000b0b200341106a2480808080000bfb0201057f23808080800041106b22022480808080002000280218210320022000411c6a28020022043602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822056b20044f0d0020012005200410b182808000200128020821050b200128020420056a2003200410848e8080001a2001200520046a36020820002802042000280208200110dc818080002002200041206a36020c2002410c6a200110cb81808000200041106a28020021042002200041146a28020022003602082002200241086a36020c2002410c6a200110cb8180800002402000450d00200420004103746a21060340200428020021032002200428020422003602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822056b20004f0d0020012005200010b182808000200128020821050b200128020420056a2003200010848e8080001a2001200520006a360208200441086a22042006470d000b0b200241106a2480808080000ba70501077f024020002802002201450d00200028020421020240024020002802082203450d00410021000340024002402000450d002001210420022105200021010c010b4100210402402002450d0020022100024020024107712205450d0003402000417f6a210020012802900221012005417f6a22050d000b0b20024108490d000340200128029002280290022802900228029002280290022802900228029002280290022101200041786a22000d000b0b410021050b024002400240200520012f018e02490d0003402001280288022200450d0220012f018c022105200141002802c0a3c6800011808080800000200441016a210420002101200520002f018e024f0d000b200021010b200541016a2102024020040d00200121000c020b200120024102746a4190026a2802002100410021022004417f6a2206450d012004417e6a2107024020064107712204450d0003402006417f6a210620002802900221002004417f6a22040d000b0b20074107490d010340200028029002280290022802900228029002280290022802900228029002280290022100200641786a22060d000c020b0b200141002802c0a3c680001180808080000041a8dac0800010a081808000000b0240200120054104746a2201280200450d00200128020441002802c0a3c68000118080808000000b410021012003417f6a22030d000c020b0b024020020d00200121000c010b02400240200241077122050d0020012100200221010c010b200121002002210103402001417f6a210120002802900221002005417f6a22050d000b0b20024108490d000340200028029002280290022802900228029002280290022802900228029002280290022100200141786a22010d000b0b03402000280288022101200041002802c0a3c68000118080808000002001210020010d000b0b0ba00601097f2000280200210102400240024020002802202202450d0020002802042103034020002002417f6a220236022002400240024002400240024002402001450d0020030d0020002802082104200028020c2205450d03200541077122060d01200521030c020b2001450d04200028020c210620002802082105200321040c030b2005210303402003417f6a210320042802980421042006417f6a22060d000b0b20054108490d000340200428029804280298042802980428029804280298042802980428029804280298042104200341786a22030d000b0b2000420037020820002004360204410121012000410136020041002106410021050b02400240200620042f019604490d0003402004280290042203450d0220042f0194042106200441002802c0a3c6800011808080800000200541016a210520032104200620032f0196044f0d000b200321040b200641016a2107024020050d00200421030c030b200420074102746a4198046a2802002103410021072005417f6a2208450d022005417e6a2109024020084107712205450d0003402008417f6a210820032802980421032005417f6a22050d000b0b20094107490d020340200328029804280298042802980428029804280298042802980428029804280298042103200841786a22080d000c030b0b200441002802c0a3c680001180808080000041a8dac0800010a081808000000b41c8dec0800010a081808000000b2000200736020c200041003602082000200336020402402004200641286c6a41f0006a2204280200450d00200428020441002802c0a3c68000118080808000000b20020d000b200041003602000c010b200041003602002001450d01200028020422030d0020002802082103200028020c2205450d0002400240200541077122060d00200521040c010b2005210403402004417f6a210420032802980421032006417f6a22060d000b0b20054108490d000340200328029804280298042802980428029804280298042802980428029804280298042103200441786a22040d000b0b03402003280290042104200341002802c0a3c68000118080808000002004210320040d000b0b0be70801097f23808080800041d0016b2209248080808000200920043703102009200337030820092005360218200941206a41206a220a41003602002009420037023420094200370228200942808080808001370220200920012802003602b4012009200128020422053602b001200920053602ac012009200520012802084107746a3602b8012009200941206a3602bc01200941c4006a200941ac016a10ba8180800020022d006c210b200941206a200210dd82808000210c200941206a200241186a10dd82808000210d200941206a200241306a10dd82808000210e200941206a200241c8006a10dd82808000210f200241e4006a280200210120022802602110024002400240200241e8006a28020022020d00410421050c010b41002d00fca3c680001a2002410474221141002802c8a3c68000118180808000002205450d010b200941a0016a41086a22114100360200200920053602a401200920023602a00120092001200241386c6a3602b801200920103602b401200920013602b001200920013602ac012009200941206a3602bc0120092005360280012009410036027c20092011360278200941ac016a200941f8006a10c781808000200941d0006a41086a22012011280200360200200920092902a001370350200941206a200941086a10dd828080002105200920062802003602b4012009200628020422023602b001200920023602ac012009200220062802084105746a3602b8012009200941206a3602bc01200941e0006a200941ac016a10bb81808000200941206a200710dd828080002106200941206a200741186a10dd828080002111200941206a200741306a10dd828080002107200941ec006a2008200941206a10e181808000200941f8006a41206a200a2802002208360200200941f8006a41186a220a200941206a41186a2902002204370300200941f8006a41106a200941206a41106a290200370300200941f8006a41086a200941206a41086a290200370300200920092902203703782009200841002004a722021b3602cc01200920094194016a28020022083602c801200920023602c401200941003602c0012009200241004722103602bc01200920083602b801200920023602b401200941003602b001200920103602ac012000200941ac016a10c88280800020094184016a10d38280800002402009280278450d00200928027c41002802c0a3c68000118080808000000b200a10d482808000200041146a200941c4006a41086a2802003602002000200929024437020c20002009290350370218200041206a200128020036020020002005360244200041346a200b3a0000200041306a200f3602002000412c6a200e360200200041286a200d360200200041246a200c36020020002009290260370238200041c0006a200941e0006a41086a280200360200200041d0006a2007360200200041cc006a2011360200200020063602482000200929026c370254200041dc006a200941ec006a41086a280200360200200941d0016a2480808080000f0b4104201110b280808000000bb80301037f23808080800041d0006b22032480808080002001280204210420012802082105200128020021012003200236024420032005410020011b3602402003200436023c20032001360238200341003602342003200141004722023602302003200436022c200320013602282003410036022420032002360220200341086a200341206a10c081808000200328020c2101024002400240200328021022020d0002402003280208450d00200141002802c0a3c68000118080808000000b41002101410021020c010b2003200341cf006a36022020012002200341206a10d9818080002003280208210541002d00fca3c680001a41900241002802c8a3c68000118180808000002204450d01200441003b018e02200441003602880220034100360218200320043602142003410036021c20032001200241186c6a360244200320053602402003200136023c200320013602382003418180808078360228200341146a200341206a2003411c6a10c4818080002003280214210120032802182104200328021c21020b200020023602082000200436020420002001360200200341d0006a2480808080000f0b410441900210b280808000000bcd0201087f23808080800041306b2203248080808000200141246a280200210420012802202105200128020421062001280200210702400240200141286a28020022080d00410421090c010b41002d00fca3c680001a2008410c6c220a41002802c8a3c680001181808080000022090d004104200a10b280808000000b200341046a41086a220a41003602002003200936020820032008360204200320023602202003200420084105746a36021c2003200536021820032004360214200320043602102003200936022c200341003602282003200a360224200341106a200341246a10c681808000200041086a200a2802003602002000200329020437020020002002200141086a10dd828080003602202000411c6a200636020020002007360218200041106a200141306a2903003702002000200128022c41ffffffff017136020c200341306a2480808080000bb30503057f017e067f23808080800041c0006b2203248080808000200141cc006a2802002104200128024821054180808080782106024020012802502207418080808078460d00200141dc006a2902002108200141d4006a2802002109200141d8006a28020021062003200236023820032009200641e8006c6a360234200320073602302003200936022c20032009360228200341146a200341286a10bd81808000200341086a200837030020032003290218370300200328021421060b4100210702400240200128021022090d004100210a0c010b200129030021082003200141086a29030037033020032008370328200320093602382002200341286a10dd82808000210b4101210a0b02400240200141286a28020022090d000c010b200129031821082003200141206a29030037033020032008370328200320093602382002200341286a10dd82808000210c410121070b200141ec006a280200210d200141e8006a28020021092001280264210e200320023602382003200e3602302003200936022c2003200936022820032009200d41386c6a360234200341146a200341286a10bf8180800002400240200141c0006a28020022090d00410021020c010b200129033021082003200141386a29030037033020032008370328200320093602382002200341286a10dd828080002109410121020b2000200536024420002006360230200020073602082000200b3602042000200a36020020002003290214370218200041c8006a2004360200200041346a20032903003702002000410c6a200c360200200041286a200141f4006a2902003702002000200128027041ffffffff01713602242000413c6a200341086a290300370200200041206a200341146a41086a280200360200200020012d007c3a004c200041146a200936020020002002360210200341c0006a2480808080000bfa04010d7f410221010240200041246a280200220241c000490d0041032101200241808001490d00410541062002418080808004491b21010b41012102410121030240200041286a280200220441c000490d0041022103200441808001490d00410441052004418080808004491b21030b02402000412c6a280200220441c000490d0041022102200441808001490d00410441052004418080808004491b21020b41012104410121050240200041306a280200220641c000490d0041022105200641808001490d00410441052006418080808004491b21050b02402000280244220641c000490d0041022104200641808001490d00410441052006418080808004491b21040b410121064101210702402000280248220841c000490d0041022107200841808001490d00410441052008418080808004491b21070b200041146a2108200041206a2109200041c0006a210a0240200041cc006a280200220b41c000490d0041022106200b41808001490d0041044105200b418080808004491b21060b2008280200210b2000280208210c20092802002109200a280200210a410121080240200041d0006a280200220d41c000490d0041022108200d41808001490d0041044105200d418080808004491b21080b417f417f417f417f417f417f200c41386c410472220c200b41d0006c6a41046a220b200b200c491b220b417f200320016a20026a20056a220120094104746a41046a220220022001491b6a22012001200b491b220120046a220220022001491b2201200a4105746a41046a220220022001491b2201200620076a20086a6a220220022001491b2201200041dc006a280200220041047420004103746a6a41046a220020002001491b0bec0a01097f23808080800041106b2202248080808000200028020421032002200028020822043602082002200241086a36020c2002410c6a200110cb8180800002402004450d00200441386c210403402003200110d681808000200341386a2103200441486a22040d000b0b200041106a28020021032002200041146a28020022043602082002200241086a36020c2002410c6a200110cb8180800002402004450d00200441d0006c210403402003200110e681808000200341d0006a2103200441b07f6a22040d000b0b200041346a2d000021040240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a20043a00002002200041246a36020c2002410c6a200110cb818080002002200041286a36020c2002410c6a200110cb8180800020022000412c6a36020c2002410c6a200110cb818080002002200041306a36020c2002410c6a200110cb818080002000411c6a28020021032002200041206a28020022043602082002200241086a36020c2002410c6a200110cb8180800020032004200110b8818080002002200041c4006a36020c2002410c6a200110cb818080002000413c6a28020021032002200041c0006a28020022043602082002200241086a36020c2002410c6a200110cb8180800002402004450d002004410574210403402003200110e781808000200341206a2103200441606a22040d000b0b2002200041c8006a36020c2002410c6a200110cb818080002002200041cc006a36020c2002410c6a200110cb818080002002200041d0006a36020c2002410c6a200110cb818080002002200041dc006a28020022053602082002200241086a36020c2002410c6a200110cb81808000024002402005450d0020002802542206450d004100210320064100472107200041d8006a280200210003400240024002402007450d002003450d010b20070d0141e8dec0800010a081808000000b410121072006210302402000450d0020002104024020004107712206450d0003402004417f6a210420032802900221032006417f6a22060d000b0b20004108490d000340200328029002280290022802900228029002280290022802900228029002280290022103200441786a22040d000b0b41002106410021000b0240200020032f018e02490d0003402003280288022204450d04200641016a210620032f018c02210020042103200020042f018e024f0d000b0b200041016a21080240024020060d00200321040c010b200320084102746a4190026a2802002104410021082006417f6a2209450d002006417e6a210a024020094107712206450d0003402009417f6a210920042802900221042006417f6a22060d000b0b200a4107490d000340200428029002280290022802900228029002280290022802900228029002280290022104200941786a22090d000b0b200320004103746a220641b0016a28020021092002200641b4016a28020022063602082002200241086a36020c2002410c6a200110cb81808000200320004104746a210302402001280200200128020822006b20064f0d0020012000200610b182808000200128020821000b200128020420006a2009200610848e8080001a2001200020066a36020820022003410c6a36020c2002410c6a200110cb81808000200328020421062002200328020822033602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822006b20034f0d0020012000200310b182808000200128020821000b200128020420006a2006200310848e8080001a2001200020036a3602084100210620082100200421032005417f6a22050d000b0b200241106a2480808080000f0b41d8dec0800010a081808000000be70801057f23808080800041106b2202248080808000200028024421032002200041c8006a28020022043602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822056b20044f0d0020012005200410b182808000200128020821050b200128020420056a2003200410848e8080001a2001200520046a2204360208024002402000280230418080808078470d00024020012802002004470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a41003a00000c010b200041306a2105024020012802002004470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a41013a00002005200110d2818080000b0240024020002802000d000240200128020020012802082204470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a41003a00000c010b0240200128020020012802082204470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a41013a00002002200041046a36020c2002410c6a200110cb818080000b0240024020002802080d000240200128020020012802082204470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a41003a00000c010b0240200128020020012802082204470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a41013a000020022000410c6a36020c2002410c6a200110cb818080000b2000411c6a28020021042002200041206a28020022053602082002200241086a36020c2002410c6a200110cb8180800002402005450d00200541246c210503402004200110cc81808000200441246a21042005415c6a22050d000b0b0240024020002802100d000240200128020020012802082205470d0020012005410110b182808000200128020821050b2001200541016a2204360208200128020420056a41003a00000c010b0240200128020020012802082204470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a41013a00002002200041146a36020c2002410c6a200110cb81808000200128020821040b20002d004c2105024020012802002004470d0020012004410110b182808000200128020821040b200128020420046a20053a00002001200441016a360208200041286a280200210420022000412c6a28020022053602082002200241086a36020c2002410c6a200110cb8180800002402005450d00200420054103746a21060340200428020021032002200428020422053602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822006b20054f0d0020012000200510b182808000200128020821000b200128020420006a2003200510848e8080001a2001200020056a360208200441086a22042006470d000b0b200241106a2480808080000ba80301057f23808080800041106b22022480808080002000280218210320022000411c6a28020022043602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822056b20044f0d0020012005200410b182808000200128020821050b200128020420056a2003200410848e8080001a2001200520046a360208200028020421042002200028020822053602082002200241086a36020c2002410c6a200110cb8180800002402005450d00200541246c210503402004200110dd81808000200441246a21042005415c6a22050d000b0b200041106a28020021042002200041146a28020022053602082002200241086a36020c2002410c6a200110cb8180800002402005450d00200420054103746a21060340200428020021032002200428020422053602082002200241086a36020c2002410c6a200110cb8180800002402001280200200128020822006b20054f0d0020012000200510b182808000200128020821000b200128020420006a2003200510848e8080001a2001200020056a360208200441086a22042006470d000b0b200241106a2480808080000bc00302057f017e0240024002400240024020012d00000d004100210241002d00fca3c680001a412041002802c8a3c68000118180808000002203450d0241ffdfc080002101034002402001417f6a2d0000412072220441506a220541ff0171410a490d00200441997f6a41ff017141f9014d0d05200441a97f6a21050b024020012d0000412072220641506a220441ff0171410a490d00200641997f6a41ff017141f9014d0d06200641a97f6a21040b200320026a20042005410474723a0000200141026a2101200241016a2205210220054120470d000b200020032f00003b0001200041106a200329000f370000200041036a200341026a2d00003a0000200041186a200341176a290000370000200041206a2003411f6a2d00003a000020032900032107200328000b2101200341002802c0a3c68000118080808000002000410c6a2001360000200041046a20073700000c010b20002001290001370001200041196a200141196a290000370000200041116a200141116a290000370000200041096a200141096a2900003700000b200041013a00000f0b4101412010b280808000000b418087c0800010a081808000000b419087c0800010a081808000000bf20305027f017e027f017e017f23808080800041c0006b2201248080808000200141246a41bee0c08000410441c2e0c08000411d41fcdfc08000410010d882808000200141086a410036020020014280808080c00037030020012802242102200129022821032001410c6a41086a2204410036020020014280808080c00037020c2001410c6a410010eb818080002001280210200428020041246c6a220541003a00202005410836021c200541dfe0c080003602182005420437021020054200370208200542808080808001370200200141306a41086a200428020041016a22053602002001200129020c2206370330024020052006a7470d00200141306a200510eb81808000200128023821050b2001280234200541246c6a220541013a00202005410736021c200541e7e0c08000360218200542043702102005420037020820054280808080800137020002402002418080808078470d0041f8dec08000411141ecdfc0800010a181808000000b20012802342105200128023021042001280238210720002001290300370350200042808080808001370244200020023602382000200536020820002004360204200041013a0000200041cc006a41003602002000413c6a20033702002000200741016a36020c200041d8006a200141086a280200360200200141c0006a2480808080000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141246c2104200141e4f1b81c4941027421050240024020030d00200241003602180c010b200241043602182002200341246c36021c200220002802043602140b200241086a20052004200241146a10ea81808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000be00101037f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014108200141084b1b2201417f73411f7621040240024020030d00200241003602180c010b2002200336021c20024101360218200220002802043602140b200241086a20042001200241146a10ec81808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a10ec81808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141246c2104200141e4f1b81c4941027421050240024020030d00200241003602180c010b200241043602182002200341246c36021c200220002802043602140b200241086a20052004200241146a10ec81808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241386c210420024193c9a4124941037421050240024020010d00200341003602180c010b200341083602182003200141386c36021c200320002802043602140b200341086a20052004200341146a10ec81808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b22024105742104200241808080204941037421050240024020010d00200341003602180c010b200341083602182003200141057436021c200320002802043602140b200341086a20052004200341146a10ec81808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241246c2104200241e4f1b81c4941027421050240024020010d00200341003602180c010b200341043602182003200141246c36021c200320002802043602140b200341086a20052004200341146a10ec81808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000be20101027f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024108200241084b1b2202417f73411f7621040240024020010d00200341003602180c010b2003200136021c20034101360218200320002802043602140b200341086a20042002200341146a10ec81808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bba0504057f017e027f017e23808080800041c0006b2201248080808000200141246a41a0e2c08000410441a4e2c08000411741a0e2c08000410010d8828080002001411c6a42043702002001420037021420014280808080800137020c2001428880808080013702302001428080808080013702382001410c6a200141306a10fe81808000200141086a200141206a28020036020020012001290218370300200128020c2102200128021021032001280214210420012802242105200129022821062001410c6a41086a2207410036020020014280808080c00037020c2001410c6a410010ef818080002001280210200728020041246c6a220841003a00202008410336021c2008419ae2c080003602182008420437021020084200370208200842808080808001370200200141306a41086a200728020041016a22083602002001200129020c2209370330024020082009a7470d00200141306a200810ef81808000200128023821080b2001280234200841246c6a220841013a00202008410236021c2008419de2c0800036021820084204370210200842003702082008428080808080013702002001280238210720012802342108200120012802303602142001200836020c20012008200741246c6a41246a36021820012008360210200141306a2001410c6a10808280800002402005418080808078470d0041eee0c08000411141e0e1c0800010a181808000000b200120023602142001200336020c200120033602102001200320044105746a360218200041c4006a2001410c6a10fe81808000200141176a200141306a41086a2802003600002000413c6a200637020020002005360238200041013a000020002001290300370350200041d8006a200141086a2802003602002001200129023037000f2000200129000c370001200041086a200141136a290000370000200141c0006a2480808080000bbf0604057f017e037f017e23808080800041d0006b2201248080808000200141306a41bbe2c08000410d41a4e2c08000411741a0e2c08000410010d882808000200141286a420437020020014200370220200142808080808001370218200142888080808001370240200142808080808001370248200141186a200141c0006a10fe81808000200141086a41086a2001412c6a2802003602002001200129022437030820012802182102200128021c2103200128022021042001280230210520012902342106200141186a41086a2207410036020020014280808080c000370218200141186a410010ef81808000200128021c200728020041246c6a220841003a00202008410636021c200841c8e2c080003602182008420437021020084200370208200842808080808001370200200141c0006a41086a2209200728020041016a220836020020012001290218220a37034002402008200aa7470d00200141c0006a200810ef81808000200128024821080b2001280244200841246c6a220841013a00202008410b36021c200841cee2c0800036021820084204370210200842003702082008428080808080013702002007200928020041016a220836020020012001290340220a37031802402008200aa7470d00200141186a200810ef81808000200128022021080b200128021c200841246c6a220841023a00202008410936021c200841d9e2c08000360218200842043702102008420037020820084280808080800137020020012802202107200128021c2108200120012802183602202001200836021820012008200741246c6a41246a3602242001200836021c200141c0006a200141186a10808280800002402005418080808078470d0041eee0c08000411141e0e1c0800010a181808000000b20012002360220200120033602182001200336021c2001200320044105746a360224200041c4006a200141186a10fe81808000200141236a200141c0006a41086a2802003600002000413c6a200637020020002005360238200041013a000020002001290308370350200041d8006a200141086a41086a2802003602002001200129024037001b20002001290018370001200041086a2001411f6a290000370000200141d0006a2480808080000b9f0704057f017e037f017e23808080800041d0006b2201248080808000200141286a41e2e2c08000410c41a4e2c08000411741a0e2c08000410010d882808000200141206a420437020020014200370218200142808080808001370210200142888080808001370240200142808080808001370248200141106a200141c0006a10fe81808000200141086a200141246a2802003602002001200129021c37030020012802102102200128021421032001280218210420012802282105200129022c2106200141106a41086a22074100360200200142808080808001370210200141106a410010ee818080002001280214200728020041386c6a2208420437022c20084206370224200841f6e1c080003602202008410636021c200841f0e1c08000360218200841ae808080003602102008428a96cd9bb2e69e8d723703082008429b99b4f08dc8ccdea77f370300200141c0006a41086a2209200728020041016a220836020020012001290210220a37034002402008200aa7470d00200141c0006a200810ee81808000200128024821080b2001280244200841386c6a2208420437022c2008420d37022420084181e2c080003602202008410536021c200841fce1c08000360218200841af80808000360210200842a3e3bbd692f2b9f9c000370308200842b79f92f0eda6f987be7f3703002007200928020041016a220836020020012001290340220a37031002402008200aa7470d00200141106a200810ee81808000200128021821080b2001280214200841386c6a2208420437022c2008420437022420084196e2c080003602202008410836021c2008418ee2c08000360218200841b080808000360210200842d58ec2d7bbaa93feab7f370308200842b3e2d6a8e7e4a79f77370300200128021821072001280214210820012001280210360248200120083602402001200836024420012008200741386c6a41386a36024c200141346a200141c0006a10ff8180800002402005418080808078470d0041eee0c08000411141e0e1c0800010a181808000000b2001200236021820012003360210200120033602142001200320044105746a36021c200041c4006a200141106a10fe818080002001411b6a200141346a41086a2802003600002000413c6a200637020020002005360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000bba0504057f017e027f017e23808080800041c0006b2201248080808000200141246a41f0e2c08000410d41fde2c08000412341f0e2c08000410010d8828080002001411c6a42043702002001420037021420014280808080800137020c2001428880808080013702302001428080808080013702382001410c6a200141306a10fe81808000200141086a200141206a28020036020020012001290218370300200128020c2102200128021021032001280214210420012802242105200129022821062001410c6a41086a2207410036020020014280808080c00037020c2001410c6a410010ef818080002001280210200728020041246c6a220841003a00202008410436021c200841a0e3c080003602182008420437021020084200370208200842808080808001370200200141306a41086a200728020041016a22083602002001200129020c2209370330024020082009a7470d00200141306a200810ef81808000200128023821080b2001280234200841246c6a220841013a00202008410836021c200841a4e3c0800036021820084204370210200842003702082008428080808080013702002001280238210720012802342108200120012802303602142001200836020c20012008200741246c6a41246a36021820012008360210200141306a2001410c6a10808280800002402005418080808078470d0041eee0c08000411141e0e1c0800010a181808000000b200120023602142001200336020c200120033602102001200320044105746a360218200041c4006a2001410c6a10fe81808000200141176a200141306a41086a2802003600002000413c6a200637020020002005360238200041013a000020002001290300370350200041d8006a200141086a2802003602002001200129023037000f2000200129000c370001200041086a200141136a290000370000200141c0006a2480808080000b140020012000280204200028020810dd808080000b2100200128021441ace3c080004105200141186a28020028020c118280808000000bc40403027f017e047f2380808080004190016b220024808080800041002101200041086a41b1e3c08000411341002802b8a1c680001185808080000002402000280208450d00200041186a41086a200041086a41086a290200220237030020002000290208370318200028021c210302402002a7220441034b0d0002400240417f4100280284a4c680002201410147200141014b1b2201417f460d00200141ff01710d010b2000413c6a41e4e5c08000410241b1e3c08000411310fc818080002000412c6a410c6a41b180808000360200200041b28080800036023020002000418f016a36023420002000413c6a36022c4100280290a1c680002101410028028ca1c6800021054100280280a4c68000210620004180016a4202370200200041f8006a4102360200200041f0006a4110360200200041ec006a41e6e5c08000360200200041e0006a41f6e5c08000ad4280808080900d84370200200041c8006a410c6a41dfe6c08000ad4280808080800484370200200041fc006a2000412c6a360200200041d4e5c08000360274200041013602682000410036025c2000410036025020004281808080c003370248200541ecf2c08000200641024622061b200041c8006a200141d4f2c0800020061b28021011848080800000200028023c450d00200028024041002802c0a3c68000118080808000000b200041246a20032004200028021828020c11858080800000410021010c010b20032800002101200041246a20032004200028021828020c118580808000000b20004190016a24808080800020010b890301047f23808080800041d0006b2201248080808000024002400240024010fa8180800022020e020001020b0240417f4100280284a4c680002202410247200241024b1b2202417f460d00200241ff01710d030b4100280290a1c680002102410028028ca1c6800021034100280280a4c680002104200141c4006a4200370200200141c0006a41ace3c080003602002001413c6a4101360200200141346a4125360200200141306a41a8e4c08000360200200141246a41cde4c08000ad4280808080e00d84370200200141186a41a8e4c08000ad4280808080d00484370200200141a0e4c08000360238200141003602202001410036021420014281808080800937020c2001410236022c200341ecf2c08000200441024622041b2001410c6a200241d4f2c0800020041b280210118480808000000c020b41b1e3c08000411341002802a0a1c68000118480808000000c010b20012002417f6a36020c41b1e3c0800041132001410c6a410441002802e0a1c68000118680808000000b200141d0006a2480808080000beb0601057f23808080800041106b220524808080800041012106024002400240200441017420026a2207450d002007417f4c0d0241002d00fca3c680001a200741002802c8a3c68000118180808000002206450d010b4100210820054100360208200520063602042005200736020002402002450d00200120026a210741002108034002400240024020012c00002202417f4c0d00200141016a2101200241ff017121020c010b20012d0001413f7121062002411f712109024002402002415f4b0d0020094106742006722102200141026a21010c010b200641067420012d0002413f717221060240200241704f0d0020062009410c74722102200141036a21010c010b200641067420012d0003413f71722009411274418080f00071722202418080c400460d04200141046a21010b2002418001490d002005410036020c024002402002418010490d0002402002418080044f0d0020052002413f71418001723a000e20052002410c7641e001723a000c20052002410676413f71418001723a000d410321020c020b20052002413f71418001723a000f2005200241127641f001723a000c20052002410676413f71418001723a000e20052002410c76413f71418001723a000d410421020c010b20052002413f71418001723a000d2005200241067641c001723a000c410221020b0240200528020020086b20024f0d0020052008200210f381808000200528020821080b200528020420086a2005410c6a200210848e8080001a200820026a21080c010b024020082005280200470d002005200810ed81808000200528020821080b200528020420086a20023a0000200528020841016a21080b2005200836020820012007470d000b0b02402004450d000340413041d70020032d0000220141a001491b20014104766a2102024020082005280200470d002005200810ed81808000200528020821080b200528020420086a20023a00002005200528020841016a2208360208024020082005280200470d002005200810ed81808000200528020821080b200341016a2103200528020420086a413041d7002001410f712208410a491b20086a3a00002005200528020841016a22083602082004417f6a22040d000b0b20002005290200370200200041086a200541086a280200360200200541106a2480808080000f0b4101200710b280808000000b10ae80808000000b130020004200370200200041086a42043702000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b22064105762207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710f18180800020022802082109200228020c21080b200920084105746a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b4105762107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b220641386e2207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710f08180800020022802082109200228020c21080b2009200841386c6a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b41386e2107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b220641246e2207200128020822014101764f0d01410021082002410036020c20024280808080c00037020441042109024020052004460d00200241046a4100200710f28180800020022802082109200228020c21080b2009200841246c6a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b41246e2107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000b9c07010b7f23808080800041306b22022480808080002001280208210302400240024002400240024002400240024002400240200128020022040d0020030d01200242808080801037020c410021050c080b200128020420046b210520030d01200521060c020b2001410c6a28020020036b21060c010b20052001410c6a28020020036b6a22062005490d010b0240024020060d00410121070c010b2006417f4c0d0641002d00fca3c680001a200641002802c8a3c68000118180808000002207450d070b4100210520024100360214200220073602102002200636020c200128020c21082001280204210102400240024020040d002003450d07200820036b21090c010b200120046b2105024020030d00200521090c010b2005200820036b6a22092005490d010b410021050240200620094f0d002002410c6a4100200910f38180800020022802102107200228021421050b2004450d0420042001460d04200120046b2206410371210a2004417f7320016a41034f0d02410021010c030b200241246a42003702002002410136021c20024190e7c0800036021820024198e7c08000360220200241186a41a0e9c0800010f680808000000b200241186a410c6a42003702002002410136021c20024190e7c0800036021820024198e7c08000360220200241186a419ce8c0800010f680808000000b200720056a210b2006417c71210c410021010340200b20016a2206200420016a22092d00003a0000200641016a200941016a2d00003a0000200641026a200941026a2d00003a0000200641036a200941036a2d00003a0000200c200141046a2201470d000b200520016a21050b200a450d00200420016a21010340200720056a20012d00003a0000200141016a2101200541016a2105200a417f6a220a0d000b0b2003450d0020032008460d00200820036b2201410371210a024002402003417f7320086a41034f0d00410021010c010b200720056a21042001417c71210b410021010340200420016a2206200320016a22092d00003a0000200641016a200941016a2d00003a0000200641026a200941026a2d00003a0000200641036a200941036a2d00003a0000200b200141046a2201470d000b200520016a21050b200a450d00200320016a21010340200720056a20012d00003a0000200141016a2101200541016a2105200a417f6a220a0d000b0b2000200229020c370200200041086a2005360200200241306a2480808080000f0b10ae80808000000b4101200610b280808000000bf20503037f017e037f23808080800041c0006b220124808080800020014106360210200141b0e9c0800036020c41002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241b0e9c08000360200200241046a410636020041b0e9c08000410610d782808000450d0141002d00fca3c680001a412041002802c8a3c68000118180808000002203450d022003429b99b4f08dc8ccdea77f370308200341ae8080800036021820034101360204200341b6e9c08000360200200341106a428a96cd9bb2e69e8d7237030020014101360220200120033602182001200341206a3602242001200336021c200141306a200141186a108f828080002001290230210420012802382105200141186a41086a2206410036020020014280808080c000370218200141186a4100109582808000200128021c200628020041246c6a220341003a00202003410436021c200341b7e9c080003602182003420437021020034200370208200342808080808001370200200141306a41086a2207200628020041016a360200200120012902183703302001410c6a200141306a41bbe9c080004104109a828080002001200128020c36022020012001280210220336021820012003200128021441246c6a3602242001200336021c200141306a200141186a108d82808000200141236a2007280200360000200041cc006a2005360200200020043702442000413c6a2002ad4280808080108437020020004101360238200041013a0000200041d8006a410036020020004280808080c0003703502001200129023037001b20002001290018370001200041086a2001411f6a290000370000200141c0006a2480808080000f0b4104410810b280808000000b200241002802c0a3c6800011808080800000200141246a42013702002001410236021c20014180ddc18000360218200141b4808080003602342001200141306a36022020012001410c6a360230200141186a4190ddc1800010f680808000000b4108412010b280808000000b6e00200042d7c9cb8fc1cf97db3e37030820004280808080c00037033820004280808080c000370350200041b580808000360218200041063a0000200041106a42e88488d0c0e3aebc13370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b02000bd70809017e027f027e017f077e017f037e017f037e200141386a29030021022001290330502103200141c0006a2104427f200141286a2903002205200129031822065022071b2108200141d0006a290300210920014190026a290300210a2001290348210b02400240427f200141206a290300220c20071b220d200129038802220e580d002008200a580d00200d200b580d004100210f20082009560d010b200d200884420052210f0b20042903002110200141a0026a2107427f200220031b21022001290310211120012903082112410121040240024020012802002213450d002012500d012011500d010b20134520124200200d200b7d22142014200d561b5820114200200820097d220d200d2008561b587172410173200f7221040b427f201020031b21122007290300210d2001290398022108024002402002200b580d0020122009560d010b200220128442005220047221040b2008200b200e7c58200d2009200a7c58722006a7410047200c2008562005200d56727172200472210320014190016a290300211120014188016a29030050210720014198016a2104427f20014180016a290300220c200141f0006a290300221050220f1b210b200141a8016a2903002102200141a0016a290300210902400240427f200141f8006a2903002214200f1b2212200e580d00200b200a580d0020122009580d00200b2002560d010b2012200b8442005220037221030b20042903002115427f201120071b2111200141e8006a2903002106200141e0006a290300210541012104024002402001280258220f450d002005500d012006500d010b200f4520054200201220097d221620162012561b5820064200200b20027d22122012200b561b58717241017320037221040b427f201520071b210b0240024020112009580d00200b2002560d010b2011200b8442005220047221040b20082009200e7c58200d2002200a7c58722010a74100472014200856200c200d567271722004722103200141e8016a2903002111200141e0016a290300502107200141f0016a2104427f200141d8016a2903002210200141c8016a290300221450220f1b210b20014180026a2903002102200141f8016a290300210902400240427f200141d0016a2903002215200f1b2212200e580d00200b200a580d0020122009580d00200b2002560d010b2012200b8442005220037221030b2004290300210c427f201120071b2111200141c0016a2903002106200141b8016a2903002105410121040240024020012802b001220f450d002005500d012006500d010b200f4520054200201220097d221620162012561b5820064200200b20027d22122012200b561b58717241017320037221040b427f200c20071b210b0240024020112009580d00200b2002560d010b2011200b8442005220047221040b024020082009200e7c580d00200d2002200a7c580d002014a74520152008582010200d587172450d0020040d002000200141a80210848e8080001a0f0b20004202370300200041013a00080bfe0606017f027e017f017e037f027e23808080800041b0026b2202248080808000200241086a200141a80210848e8080001a200241a8026a290300210320022903a0022104024020022903205022050d00200241306a2903002206200320062003561b2103200241286a2903002206200420062004561b21040b0240200241f8006a2903005022070d0020024188016a2903002206200320062003561b210320024180016a2903002206200420062004561b21040b200141ac026a210820012802a80221010240200241d0016a2903005022090d00200241e0016a2903002206200320062003561b2103200241d8016a2903002206200420062004561b21040b20083502002106200220033703a802200220043703a00202402001450d0020032003428094ebdc0380220a428094ebdc037e7d20067e2203428094ebdc0380220b200a20067e7c2003200b428094ebdc037e7d4280cab5ee0156ad7c210b20042004428094ebdc03802203428094ebdc037e7d20067e2204428094ebdc0380220a200320067e7c2004200a428094ebdc037e7d4280cab5ee0156ad7c21060240200229030850450d00024002402005450d004200210a0c010b42004200200241306a2903002203200b7d220420042003561b2203200241d8006a2903007d220420042003561b210442004200200241286a290300220320067d220a200a2003561b220320022903507d220a200a2003561b21034201210a0b20022004370318200220033703102002200a3703080b024020022903604200520d004200210a024020070d004200420020024188016a2903002203200b7d220420042003561b2203200241b0016a2903007d220420042003561b21044200420020024180016a290300220320067d220a200a2003561b2203200241a8016a2903007d220a200a2003561b21034201210a0b200241f0006a2004370300200241e8006a20033703002002200a3703600b20022903b8014200520d00024002402009450d00420021060c010b42004200200241e0016a2903002203200b7d220420042003561b220320024188026a2903007d220420042003561b210442004200200241d8016a290300220320067d220620062003561b220320024180026a2903007d220620062003561b2103420121060b200241c8016a2004370300200241c0016a2003370300200220063703b8010b2000200241086a108582808000200241b0026a2480808080000bc40403057f017e037f23808080800041d0006b2201248080808000200141286a41afebc08000410b41baebc08000411441c0e9c08000410010d882808000200141206a42043702002001420037021820014280808080800137021020014288808080800137023420014280808080800137023c200141106a200141346a108f82808000200141086a2202200141106a41146a2802003602002001200129021c370300200128021021032001280214210420012802182105200129022c21062001280228210720014100360218200142808080808001370210200141106a41001096828080002001280214200128021841386c6a2208420437022c2008421537022420084182eec080003602202008410336021c200841ffedc08000360218200841b680808000360210200842a5968aeabda0f18d34370308200842a2c6d1e2c3c0eaf01f370300200128021821092001280214210820012001280210360218200120083602102001200836021420012008200941386c6a41386a36021c200141c4006a200141106a108e8280800002402007418080808078470d0041e3ecc08000411141d4edc0800010a181808000000b200141346a410b6a200141c4006a41086a2802003600002000200336024420002007360238200041003a000020002001290300370350200041cc006a2005360200200041c8006a20043602002000413c6a20063702002001200129024437003720002001290034370001200041d8006a2002280200360200200041086a2001413b6a290000370000200141d0006a2480808080000b2100200128021441a0ebc08000410f200141186a28020028020c118280808000000bd20403027f017e047f410121014101210202402000290348220342c000540d0041022102200342808001540d00410421022003428080808004540d004109200379a74103766b21020b0240200041d0006a290300220342c000540d0041022101200342808001540d00410421012003428080808004540d004109200379a74103766b21010b410121044101210502402000290300500d004102210502402000290308220342c000540d0041032105200342808001540d00410521052003428080808004540d00410a200379a74103766b21050b410121060240200041106a290300220342c000540d0041022106200342808001540d00410421062003428080808004540d004109200379a74103766b21060b200520066a21050b02402000290318500d00410221040240200041206a290300220342c000540d0041032104200342808001540d00410521042003428080808004540d00410a200379a74103766b21040b410121060240200041286a290300220342c000540d0041022106200342808001540d00410421062003428080808004540d004109200379a74103766b21060b200420066a21040b02400240200029033050450d00410121000c010b410221060240200041386a290300220342c000540d0041032106200342808001540d00410521062003428080808004540d00410a200379a74103766b21060b410121070240200041c0006a290300220342c000540d0041022107200342808001540d00410421072003428080808004540d004109200379a74103766b21070b200620076a21000b200120026a20056a20046a20006a0bbf0804057f017e037f017e23808080800041e0006b2201248080808000200141306a41ceebc08000410f41baebc08000411441c0e9c08000410010d882808000200141286a420437020020014200370220200142808080808001370218200142888080808001370240200142808080808001370248200141186a200141c0006a108f82808000200141086a41086a200141186a41146a2802003602002001200129022437030820012802182102200128021c2103200128022021042001280230210520012902342106200141186a41086a22074100360200200142808080808001370218200141186a4100109682808000200128021c200728020041386c6a2208420437022c20084206370224200841a5eec080003602202008410e36021c20084197eec08000360218200841ae808080003602102008428a96cd9bb2e69e8d723703082008429b99b4f08dc8ccdea77f370300200141c0006a41086a2209200728020041016a220836020020012001290218220a37034002402008200aa7470d00200141c0006a2008109682808000200128024821080b2001280244200841386c6a2208420437022c2008420e370224200841b8eec080003602202008410d36021c200841abeec08000360218200841b7808080003602102008428caaf9b7c8a1dba0c900370308200842e3cce7c1b8e587e3ba7f3703002007200928020041016a220836020020012001290340220a37031802402008200aa7470d00200141186a2008109682808000200128022021080b200128021c200841386c6a2208420437022c2008420e370224200841b8eec080003602202008410936021c200841c6eec08000360218200841b7808080003602102008428caaf9b7c8a1dba0c900370308200842e3cce7c1b8e587e3ba7f370300200141c0006a41086a200141186a41086a28020041016a220836020020012001290318220a37034002402008200aa7470d00200141c0006a2008109682808000200128024821080b2001280244200841386c6a2208420437022c2008420e370224200841b8eec080003602202008410836021c200841cfeec08000360218200841b7808080003602102008428caaf9b7c8a1dba0c900370308200842e3cce7c1b8e587e3ba7f370300200128024821072001280244210820012001280240360220200120083602182001200836021c20012008200741386c6a41386a360224200141d4006a200141186a108e8280800002402005418080808078470d0041e3ecc08000411141d4edc0800010a181808000000b200141cb006a200141d4006a41086a2802003600002000200236024420002005360238200041003a000020002001290308370350200041cc006a2004360200200041c8006a20033602002000413c6a20063702002001200129025437004320002001290040370001200041d8006a200141086a41086a280200360200200041086a200141c7006a290000370000200141e0006a2480808080000bd40203027f017e027f41012101410121020240200029038802220342c000540d0041022102200342808001540d00410421022003428080808004540d004109200379a74103766b21020b024020004190026a290300220342c000540d0041022101200342808001540d00410421012003428080808004540d004109200379a74103766b21010b41012104410121050240200029039802220342c000540d0041022105200342808001540d00410421052003428080808004540d004109200379a74103766b21050b0240200041a0026a290300220342c000540d0041022104200342808001540d00410421042003428080808004540d004109200379a74103766b21040b417f200120026a20056a20046a2201417f417f20001089828080002202200041d8006a1089828080006a220420042002491b2202200041b0016a1089828080006a220020002002491b6a220020002001491b0b940704057f017e037f017e23808080800041e0006b2201248080808000200141306a41ddebc08000410c41baebc08000411441c0e9c08000410010d882808000200141286a420437020020014200370220200142808080808001370218200142888080808001370240200142808080808001370248200141186a200141c0006a108f82808000200141086a41086a200141186a41146a2802003602002001200129022437030820012802182102200128021c2103200128022021042001280230210520012902342106200141186a41086a22074100360200200142808080808001370218200141186a4100109682808000200128021c200728020041386c6a2208420437022c20084206370224200841a5eec080003602202008410a36021c200841d7eec08000360218200841ae808080003602102008428a96cd9bb2e69e8d723703082008429b99b4f08dc8ccdea77f370300200141c0006a41086a2209200728020041016a220836020020012001290218220a37034002402008200aa7470d00200141c0006a2008109682808000200128024821080b2001280244200841386c6a2208420437022c20084206370224200841a5eec080003602202008410936021c200841e1eec08000360218200841ae808080003602102008428a96cd9bb2e69e8d723703082008429b99b4f08dc8ccdea77f3703002007200928020041016a220836020020012001290340220a37031802402008200aa7470d00200141186a2008109682808000200128022021080b200128021c200841386c6a2208420437022c20084221370224200841f3eec080003602202008410936021c200841eaeec08000360218200841b8808080003602102008429de3c1c199ed89c7f200370308200842b6a3f7ac9ac487f47237030020012802202107200128021c210820012001280218360220200120083602182001200836021c20012008200741386c6a41386a360224200141d4006a200141186a108e8280800002402005418080808078470d0041e3ecc08000411141d4edc0800010a181808000000b200141cb006a200141d4006a41086a2802003600002000200236024420002005360238200041003a000020002001290308370350200041cc006a2004360200200041c8006a20033602002000413c6a20063702002001200129025437004320002001290040370001200041d8006a200141086a41086a280200360200200041086a200141c7006a290000370000200141e0006a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b220641246e2207200128020822014101764f0d01410021082002410036020c20024280808080c00037020441042109024020052004460d00200241046a4100200710988280800020022802082109200228020c21080b2009200841246c6a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b41246e2107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b220641386e2207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710998280800020022802082109200228020c21080b2009200841386c6a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b41386e2107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b22064105762207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710978280800020022802082109200228020c21080b200920084105746a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b4105762107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000be50604047f017e027f017e23808080800041d0006b2201248080808000200141306a41ecebc08000411041fcebc08000411741ecebc08000410010d8828080002001412c6a410036020020014280808080c00037022441002d00fca3c680001a02400240412041002802c8a3c68000118180808000002202450d00200242d7c9cb8fc1cf97db3e370308200241b5808080003602182002410136020420024193ecc08000360200200241106a42e88488d0c0e3aebc13370300200141086a41086a200141246a220341086a280200360200200120032902003703082001280230210420012902342105200141186a41086a22064100360200200142808080808001370218200141186a4100109682808000200128021c200628020041386c6a2203420437022c20034201370224200341ededc080003602202003410636021c200341eeedc08000360218200341b580808000360210200342e88488d0c0e3aebc13370308200342d7c9cb8fc1cf97db3e370300200141c0006a41086a2207200628020041016a2203360200200120012902182208370340024020032008a7470d00200141c0006a2003109682808000200128024821030b2001280244200341386c6a2203420437022c20034201370224200341ededc080003602202003410b36021c200341f4edc08000360218200341b580808000360210200342e88488d0c0e3aebc13370308200342d7c9cb8fc1cf97db3e3703002006200728020041016a2203360200200120012903402208370318024020032008a7470d00200141186a2003109682808000200128022021030b200128021c200341386c6a2203420437022c20034201370224200341ededc080003602202003410936021c200341e4edc08000360218200341b580808000360210200342e88488d0c0e3aebc13370308200342d7c9cb8fc1cf97db3e3703002004418080808078460d012001280220210320012802182106200128021c210720004101360244200020043602382000200736020820002006360204200041003a000020002001290308370350200041cc006a4101360200200041c8006a20023602002000413c6a20053702002000200341016a36020c200041d8006a200141106a280200360200200141d0006a2480808080000f0b4108412010b280808000000b41e3ecc08000411141d4edc0800010a181808000000be90604047f017e027f017e23808080800041d0006b2201248080808000200141306a41ecebc08000411041fcebc08000411741ecebc08000410010d8828080002001412c6a410036020020014280808080c00037022441002d00fca3c680001a02400240412041002802c8a3c68000118180808000002202450d0020024296a2989994a79f81d800370308200241bb808080003602182002410136020420024193ecc08000360200200241106a4295f8a9f7f1dbffef28370300200141086a41086a200141246a220341086a280200360200200120032902003703082001280230210420012902342105200141186a41086a22064100360200200142808080808001370218200141186a4100109682808000200128021c200628020041386c6a2203420437022c20034201370224200341ededc080003602202003410636021c200341eeedc08000360218200341bb8080800036021020034295f8a9f7f1dbffef2837030820034296a2989994a79f81d800370300200141c0006a41086a2207200628020041016a2203360200200120012902182208370340024020032008a7470d00200141c0006a2003109682808000200128024821030b2001280244200341386c6a2203420437022c20034201370224200341ededc080003602202003410b36021c200341f4edc08000360218200341bb8080800036021020034295f8a9f7f1dbffef2837030820034296a2989994a79f81d8003703002006200728020041016a2203360200200120012903402208370318024020032008a7470d00200141186a2003109682808000200128022021030b200128021c200341386c6a2203420437022c20034201370224200341ededc080003602202003410936021c200341e4edc08000360218200341bb8080800036021020034295f8a9f7f1dbffef2837030820034296a2989994a79f81d8003703002004418080808078460d012001280220210320012802182106200128021c210720004101360244200020043602382000200736020820002006360204200041003a000020002001290308370350200041cc006a4101360200200041c8006a20023602002000413c6a20053702002000200341016a36020c200041d8006a200141106a280200360200200141d0006a2480808080000f0b4108412010b280808000000b41e3ecc08000411141d4edc0800010a181808000000b9e0405027f017e017f017e027f23808080800041c0006b2201248080808000200141286a4194ecc0800041054199ecc08000410c41ecebc08000410010d882808000200141086a410036020020014280808080c00037030020012802282102200129022c21032001410036021820014280808080c000370210200141346a200141106a41a5ecc08000410e109b828080000240200128023c22042001280234470d00200141346a2004109582808000200128023c21040b2001280238200441246c6a220441013a00202004410c36021c200441b3ecc080003602182004420437021020044200370208200442808080808001370200200141106a41086a200141346a41086a28020041016a2204360200200120012902342205370310024020042005a7470d00200141106a2004109582808000200128021821040b2001280214200441246c6a220441023a00202004410e36021c200441bfecc08000360218200442043702102004420037020820044280808080800137020002402002418080808078470d0041e3ecc08000411141d4edc0800010a181808000000b200128021421042001280210210620012802182107200042808080808001370244200020023602382000200436020820002006360204200041013a000020002001290300370350200041cc006a41003602002000413c6a20033702002000200741016a36020c200041d8006a200141086a280200360200200141c0006a2480808080000bb80405027f017e027f017e017f23808080800041c0006b2201248080808000200141246a41cdecc0800041164199ecc08000410c41ecebc08000410010d882808000200141086a410036020020014280808080c00037030020012802242102200129022821032001410c6a41086a2204410036020020014280808080800137020c2001410c6a41001096828080002001280210200428020041386c6a2205420437022c20054213370224200541a3efc080003602202005410c36021c20054197efc08000360218200541bc80808000360210200542b2b8a880edaaf0a1c000370308200542e1ffbd85d68ecce4b97f370300200141306a41086a200428020041016a22053602002001200129020c2206370330024020052006a7470d00200141306a2005109682808000200128023821050b2001280234200541386c6a2205420437022c20054219370224200541bfefc080003602202005410936021c200541b6efc08000360218200541bd80808000360210200542efc9c9edb5e7b3a6c700370308200542acf6debeefe0d9c8d30037030002402002418080808078470d0041e3ecc08000411141d4edc0800010a181808000000b200128023821052001280230210420012802342107200042808080808001370244200020023602382000200736020820002004360204200041003a000020002001290300370350200041cc006a41003602002000413c6a20033702002000200541016a36020c200041d8006a200141086a280200360200200141c0006a2480808080000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141246c2104200141e4f1b81c4941027421050240024020030d00200241003602180c010b200241043602182002200341246c36021c200220002802043602140b200241086a20052004200241146a109482808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a109482808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b22024105742104200241808080204941037421050240024020010d00200341003602180c010b200341083602182003200141057436021c200320002802043602140b200341086a20052004200341146a109482808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241246c2104200241e4f1b81c4941027421050240024020010d00200341003602180c010b200341043602182003200141246c36021c200320002802043602140b200341086a20052004200341146a109482808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241386c210420024193c9a4124941037421050240024020010d00200341003602180c010b200341083602182003200141386c36021c200320002802043602140b200341086a20052004200341146a109482808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bbf0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a41001096828080002004280208200428020c220541386c6a2206410036023020064280808080c0003703282006410036022020064100360218200641ae808080003602102006428a96cd9bb2e69e8d723703082006429b99b4f08dc8ccdea77f37030020042802042107200428020821080240200128020822062001280200470d0020012006109582808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bbd0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a41001096828080002004280208200428020c220541386c6a2206420437022c2006420337022420064194efc0800036022020064100360218200641b580808000360210200642e88488d0c0e3aebc13370308200642d7c9cb8fc1cf97db3e37030020042802042107200428020821080240200128020822062001280200470d0020012006109582808000200128020821060b2001280204200641246c6a220641003a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000be708012d7e0240200141184b0d000240411820016b41037441e8efc080006a41a8f1c08000460d00410020014103746b210120002903c0012102200029039801210320002903702104200029034821052000290320210620002903b80121072000290390012108200029036821092000290340210a2000290318210b20002903b001210c200029038801210d2000290360210e2000290338210f2000290310211020002903a8012111200029038001211220002903582113200029033021142000290308211520002903a00121162000290378211720002903502118200029032821192000290300211a03402017201685201885201985201a85221b200d200c85200e85200f85201085221c42018985221d201485211e201b4201892008200785200985200a85200b85221f85221b2002852120201d2011854202892221201c2003200285200485200585200685222242018985221c200a8542378922232012201185201385201485201585220a201f42018985221f201085423e892224427f8583852102200a42018920228522102017854229892222201b2004854227892225427f85832023852111201d201385420a892226201c2007854238892227201f200d85420f892228427f858385210d202620102019854224892229427f8583201b200685421b89222a852117201f200f85420689222b201d201585420189222c427f858320102016854212892216852104201b200385420889222d201c200985421989222e427f8583202b852113201b200585421489221b201c200b85421c89220b427f8583201f200c85423d89220f852105201d201285422d89221d200b200f427f858385210a200f201d427f85832010201885420389221585210f201d2015427f8583201b8521142015201b427f8583200b8521192010201a85221d2020420e89221b427f8583201c200885421589221c85210b201b201c427f8583201f200e85422b89221f852110201e422c892206201c201f427f8583852115200141a8f1c080006a290300201f2006427f858385201d85211a2029202a427f8583202785221c21032006201d427f8583201b85221d210620242021427f8583202285221b2107202a2027427f8583202885221f2108202c2016427f8583202d852227210920212022427f85832025852221210c2016202d427f8583202e852222210e20282026427f85832029852226211220252023427f858320248522232116202c202e202b427f85838522242118200141086a22010d000b200020233703a001200020173703782000202437035020002019370328200020113703a8012000202637038001200020133703582000201437033020002015370308200020213703b0012000200d37038801200020223703602000200f370338200020103703102000201b3703b8012000201f37039001200020273703682000200a3703402000200b370318200020023703c0012000201c3703980120002004370370200020053703482000201d3703202000201a3703000b0f0b4181f2c0800041c10041c4f2c0800010f880808000000b02000b040041000b02000b02000bc00202057f057e23808080800041d0006b220524808080800002402003450d00200541186a420037020020054101360210200541b8f3c0800036020c200541d4f2c080003602142005410c6a41a4f4c0800010f680808000000b4100280290a1c680002103410028028ca1c6800021064100280280a4c6800021072002280210220828020821092000290200210a2002290208210b2008290200210c2002290200210d2000290208210e200541c8006a2000290210370200200541c0006a200e370200200541306a200d370200200541246a200c370200200541186a200b3702002005200a3702382005200136022c2005410036022020054100360214200520093602102005410136020c200641ecf2c08000200741024622021b2005410c6a200341d4f2c0800020021b28021011848080800000200541d0006a2480808080000bf60201037f2380808080004180016b22022480808080002000280200210002400240024002400240200128021c22034110710d0020034120710d0120003100004101200110fe8080800021000c020b20002d00002103410021000340200220006a41ff006a413041d7002003410f712204410a491b20046a3a00002000417f6a2100200341ff017122044104762103200441104f0d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000c010b20002d00002103410021000340200220006a41ff006a413041372003410f712204410a491b20046a3a00002000417f6a2100200341ff017122044104762103200441104f0d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000b20024180016a24808080800020000f0b200341800141c88dc08000109481808000000b200341800141c88dc08000109481808000000b02000b4601017f23808080800041106b22042480808080002004200136020c200420003602084100200441086a41b4f4c080002004410c6a41b4f4c080002002200310fb80808000000ba70401037f23808080800041206b22022480808080002002200141087122033a000702400240024020030d00200020013a00ca0120002d00c9012104200020002d00c801220341016a3a00c9010240200341c7014b0d00200020036a220320032d00002004733a0000200020002d00c80141016a22033a00c80102400240200341ff0171220341a601470d00200020002d00a60120002d00c901733a00a601200020002d00a701418401733a00a70120004118109c8280800041002103200041003b01c8010c010b200341c7014b0d010b200020036a220320032d00002001733a0000200020002d00c80141016a22033a00c801024002400240200341ff017141a601470d00200020002d00a60120002d00c901733a00a60141840121030c010b2001412471450d01200341ff01712203450d01200341c7014b0d04200020036a220320032d000020002d00c901733a000020002d00c80141016a41ff0171220341c8014f0d05200020036a220320032d00004104733a000041800121030b200020002d00a7012003733a00a70120004118109c82808000200041003b01c8010b200241206a2480808080000f0b200341c80141d4f5c0800010f980808000000b20024200370214200241b4f4c080003602102002410136020c200241b4f6c08000360208200241076a41bcf6c08000200241086a41c0f6c0800010a482808000000b200341c80141b4f5c0800010f980808000000b200341c80141c4f5c0800010f980808000000b890d01027f23808080800041a0036b2203248080808000200341002f00a4f5c080003b0104200341002800a0f5c08000360200200341126a410041b601108a8e8080001a2003410e6a41002800aef5c08000360100200341002900a6f5c0800037010620034118109c82808000200341d0016a200341c80110848e8080001a200341003a009a03200341003b019803200341d0016a411210a582808000024020032d009803220441c7014b0d00200341d0016a20046a220420042d000041cd00733a0000200320032d00980341016a22043a00980302400240200441ff0171220441a601470d00200320032d00f60220032d009903733a00f602200320032d00f702418401733a00f702200341d0016a4118109c8280800041002104200341003b0198030c010b200441c7014b0d010b200341d0016a20046a220420042d000041e500733a0000200320032d00980341016a22043a00980302400240200441ff0171220441a601470d00200320032d00f60220032d009903733a00f602200320032d00f702418401733a00f702200341d0016a4118109c8280800041002104200341003b0198030c010b200441c7014b0d010b200341d0016a20046a220420042d000041f200733a0000200320032d00980341016a22043a00980302400240200441ff0171220441a601470d00200320032d00f60220032d009903733a00f602200320032d00f702418401733a00f702200341d0016a4118109c8280800041002104200341003b0198030c010b200441c7014b0d010b200341d0016a20046a220420042d000041ec00733a0000200320032d00980341016a22043a00980302400240200441ff0171220441a601470d00200320032d00f60220032d009903733a00f602200320032d00f702418401733a00f702200341d0016a4118109c8280800041002104200341003b0198030c010b200441c7014b0d010b200341d0016a20046a220420042d000041e900733a0000200320032d00980341016a22043a00980302400240200441ff0171220441a601470d00200320032d00f60220032d009903733a00f602200320032d00f702418401733a00f702200341d0016a4118109c8280800041002104200341003b0198030c010b200441c7014b0d010b200341d0016a20046a220420042d000041ee00733a0000200320032d00980341016a22043a00980302400240200441ff0171220441a601470d00200320032d00f60220032d009903733a00f602200320032d00f702418401733a00f702200341d0016a4118109c8280800041002104200341003b0198030c010b200441c7014b0d010b200341d0016a20046a220420042d00004120733a0000200320032d00980341016a22043a00980302400240200441ff0171220441a601470d00200320032d00f60220032d009903733a00f602200320032d00f702418401733a00f702200341d0016a4118109c8280800041002104200341003b0198030c010b200441c7014b0d010b200341d0016a20046a220420042d000041f600733a0000200320032d00980341016a22043a00980302400240200441ff0171220441a601470d00200320032d00f60220032d009903733a00f602200320032d00f702418401733a00f702200341d0016a4118109c8280800041002104200341003b0198030c010b200441c7014b0d010b200341d0016a20046a220420042d00004131733a0000200320032d00980341016a22043a00980302400240200441ff0171220441a601470d00200320032d00f60220032d009903733a00f602200320032d00f702418401733a00f702200341d0016a4118109c8280800041002104200341003b0198030c010b200441c7014b0d010b200341d0016a20046a220420042d0000412e733a0000200320032d00980341016a22043a00980302400240200441ff0171220441a601470d00200320032d00f60220032d009903733a00f602200320032d00f702418401733a00f702200341d0016a4118109c8280800041002104200341003b0198030c010b200441c7014b0d010b200341d0016a20046a220420042d00004130733a0000200320032d00980341016a22043a0098030240200441ff017141a601470d00200320032d00f60220032d009903733a00f602200320032d00f702418401733a00f702200341d0016a4118109c82808000200341003b0198030b2003200341d0016a41d00110848e808000220341a0f7c0800041072001200210a7828080002000200341d00110848e8080001a200341a0036a2480808080000f0b200441c80141d4f5c0800010f980808000000b8c0901027f23808080800041f0006b22052480808080002000411210a582808000024002400240024002402002450d0020002d00c80121060340200641ff0171220641c7014b0d02200020066a220620062d000020012d0000733a0000200020002d00c80141016a22063a00c8010240200641ff017141a601470d00200020002d00a60120002d00c901733a00a601200020002d00a701418401733a00a70120004118109c8280800041002106200041003b01c8010b200141016a21012002417f6a22020d000b0b200541123a000720002d00ca014112470d0120002d00c801220641c7014b0d02200020066a220620062d00002004733a0000200020002d00c80141016a22063a00c80102400240200641ff0171220641a601470d00200020002d00a60120002d00c901733a00a601200020002d00a701418401733a00a70120004118109c8280800041002106200041003b01c8010c010b200641c7014b0d030b200020066a220620062d00002004410876733a0000200020002d00c80141016a22063a00c80102400240200641ff0171220641a601470d00200020002d00a60120002d00c901733a00a601200020002d00a701418401733a00a70120004118109c8280800041002106200041003b01c8010c010b200641c7014b0d030b200020066a220620062d00002004411076733a0000200020002d00c80141016a22063a00c80102400240200641ff0171220641a601470d00200020002d00a60120002d00c901733a00a601200020002d00a701418401733a00a70120004118109c8280800041002106200041003b01c8010c010b200641c7014b0d030b200020066a220620062d00002004411876733a0000200020002d00c80141016a22063a00c8010240200641ff017141a601470d00200020002d00a60120002d00c901733a00a601200020002d00a701418401733a00a70120004118109c82808000200041003b01c8010b2000410210a58280800002402004450d0020002d00c80121060340200641ff0171220641c7014b0d05200020066a220620062d000020032d0000733a0000200020002d00c80141016a22063a00c8010240200641ff017141a601470d00200020002d00a60120002d00c901733a00a601200020002d00a701418401733a00a70120004118109c8280800041002106200041003b01c8010b200341016a21032004417f6a22040d000b0b200541f0006a2480808080000f0b200641c80141d4f5c0800010f980808000000b2005412c6a41c280808000360200200541c2808080003602242005200041ca016a22003602202005200541076a360228200541ec006a41033a0000200541e8006a4104360200200541e0006a42a080808010370200200541d8006a41023602002005410236021c200541023602142005410236020c20054180f7c0800036020820054102360250200541033a004c200541043602482005422037024020054102360238200541023602302005200541306a3602182005200541206a3602102000200541076a200541086a4190f7c0800010a482808000000b200641c80141d4f5c0800010f980808000000b200641c80141d4f5c0800010f980808000000b910901027f23808080800041f0006b22052480808080002000411210a582808000024002400240024002402002450d0020002d00c80121060340200641ff0171220641c7014b0d02200020066a220620062d000020012d0000733a0000200020002d00c80141016a22063a00c8010240200641ff017141a601470d00200020002d00a60120002d00c901733a00a601200020002d00a701418401733a00a70120004118109c8280800041002106200041003b01c8010b200141016a21012002417f6a22020d000b0b200541123a000720002d00ca014112470d0120002d00c801220641c7014b0d02200020066a220620062d00002004733a0000200020002d00c80141016a22063a00c80102400240200641ff0171220641a601470d00200020002d00a60120002d00c901733a00a601200020002d00a701418401733a00a70120004118109c8280800041002106200041003b01c8010c010b200641c7014b0d030b200020066a220620062d00002004410876733a0000200020002d00c80141016a22063a00c80102400240200641ff0171220641a601470d00200020002d00a60120002d00c901733a00a601200020002d00a701418401733a00a70120004118109c8280800041002106200041003b01c8010c010b200641c7014b0d030b200020066a220620062d00002004411076733a0000200020002d00c80141016a22063a00c80102400240200641ff0171220641a601470d00200020002d00a60120002d00c901733a00a601200020002d00a701418401733a00a70120004118109c8280800041002106200041003b01c8010c010b200641c7014b0d030b200020066a220620062d00002004411876733a0000200020002d00c80141016a22063a00c8010240200641ff017141a601470d00200020002d00a60120002d00c901733a00a601200020002d00a701418401733a00a70120004118109c82808000200041003b01c8010b2000410710a58280800002402004450d0020002d00c80121060340200641ff0171220641c8014f0d05200020066a22062d00002101200641003a0000200320013a0000200020002d00c80141016a22063a00c8010240200641ff017141a601470d00200020002d00a60120002d00c901733a00a601200020002d00a701418401733a00a70120004118109c8280800041002106200041003b01c8010b200341016a21032004417f6a22040d000b0b200541f0006a2480808080000f0b200641c80141d4f5c0800010f980808000000b2005412c6a41c280808000360200200541c2808080003602242005200041ca016a22003602202005200541076a360228200541ec006a41033a0000200541e8006a4104360200200541e0006a42a080808010370200200541d8006a41023602002005410236021c200541023602142005410236020c20054180f7c0800036020820054102360250200541033a004c200541043602482005422037024020054102360238200541023602302005200541306a3602182005200541206a3602102000200541076a200541086a4190f7c0800010a482808000000b200641c80141d4f5c0800010f980808000000b200641c80141e4f5c0800010f980808000000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141246c2104200141e4f1b81c4941027421050240024020030d00200241003602180c010b200241043602182002200341246c36021c200220002802043602140b200241086a20052004200241146a10a982808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a10a982808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000be10405027f017e037f017e017f23808080800041d0006b2201248080808000200141306a41a8f7c08000410741aff7c08000411641a8f7c08000410010d882808000200141086a41086a410036020020014280808080c0003703082001280230210220012902342103200141186a41086a2204410036020020014280808080c000370218200141186a410010aa82808000200141c0006a41086a2004280200220541016a2206360200200128021c200541246c6a220541003a00202005410336021c200541c5f7c080003602182005420437021020054200370208200542808080808001370200200120012902182207370340024020062007a7470d00200141c0006a200610aa82808000200128024821060b2004200641016a22083602002001280244200641246c6a220541013a00202005410436021c200541c8f7c080003602182005420437021020054200370208200542808080808001370200200120012903402207370318024020082007a72206470d00200141186a200810aa8280800020012802182106200128022021080b200128021c2204200841246c6a220541023a00202005410336021c200541ccf7c08000360218200542043702102005420037020820054280808080800137020002402002418080808078470d0041fcf7c08000411141f0f8c0800010a181808000000b200042808080808001370244200020023602382000200436020820002006360204200041013a000020002001290308370350200041cc006a41003602002000413c6a20033702002000200841016a36020c200041d8006a200141106a280200360200200141d0006a2480808080000bf20203027f017e047f23808080800041306b2201248080808000200141246a41cff7c08000410a41aff7c08000411641a8f7c08000410010d882808000200141086a2202410036020020014280808080c00037030020012902282103200128022421042001410036021420014280808080800137020c2001410c6a410010ab82808000200128021022052001280214220641386c6a220742e987d8bed5a3d0fbfb00370308200742e2e68fceaa92ce9c7b3703002007420437022c2007420437022420074180f9c0800036022020074100360218200741c58080800036021002402004418080808078470d0041fcf7c08000411141f0f8c0800010a181808000000b200128020c2107200042808080808001370244200020043602382000200536020820002007360204200041003a000020002001290300370350200041cc006a41003602002000413c6a20033702002000200641016a36020c200041d8006a2002280200360200200141306a2480808080000be70305027f017e027f017e027f23808080800041c0006b2201248080808000200141246a41d9f7c08000411341aff7c08000411641a8f7c08000410010d882808000200141086a410036020020014280808080c00037030020012802242102200129022821032001410c6a41086a2204410036020020014280808080c00037020c2001410c6a410010aa82808000200141306a41086a2004280200220441016a22053602002001280210200441246c6a220441003a00202004410836021c200441ecf7c0800036021820044204370210200442003702082004428080808080013702002001200129020c2206370330024020052006a72207470d00200141306a200510aa8280800020012802302107200128023821050b20012802342208200541246c6a220441013a00202004410836021c200441f4f7c08000360218200442043702102004420037020820044280808080800137020002402002418080808078470d0041fcf7c08000411141f0f8c0800010a181808000000b200042808080808001370244200020023602382000200836020820002007360204200041013a000020002001290300370350200041cc006a41003602002000413c6a20033702002000200541016a36020c200041d8006a200141086a280200360200200141c0006a2480808080000bed0201037f2380808080004180016b220224808080800002400240024002400240200128021c22034110710d0020034120710d0120003502004101200110fe8080800021000c020b20002802002100410021030340200220036a41ff006a413041d7002000410f712204410a491b20046a3a00002003417f6a210320004110492104200041047621002004450d000b20034180016a22004180014b0d022001410141c48dc080004102200220036a4180016a410020036b10db8080800021000c010b20002802002100410021030340200220036a41ff006a413041372000410f712204410a491b20046a3a00002003417f6a210320004110492104200041047621002004450d000b20034180016a22004180014b0d022001410141c48dc080004102200220036a4180016a410020036b10db8080800021000b20024180016a24808080800020000f0b200041800141c88dc08000109481808000000b200041800141c88dc08000109481808000000b860201017f024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d0020020d02410121010c030b20032802002103200241002802c8a3c68000118180808000002201450d0320012003200410848e8080001a200341002802c0a3c68000118080808000000c020b20020d00410121010c010b41002d00fca3c680001a200241002802c8a3c68000118180808000002201450d010b20002001360204200041086a2002360200200041003602000f0b20004101360204200041086a2002360200200041013602000f0b20004100360204200041086a2002360200200041013602000f0b20004100360204200041013602000be20101027f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024108200241084b1b2202417f73411f7621040240024020010d00200341003602180c010b2003200136021c20034101360218200320002802043602140b200341086a20042002200341146a10b082808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bc10601067f23808080800041306b220224808080800002400240024002400240024002402001280208220320012802102204460d0002400240024002400240200441016a2205450d0020032005490d0120012802042106200120053602100240024002400240200620046a2d000022074103710e0400020103000b200741027621040c070b200241096a20073a0000200241013a0008200220013602042002410036021c200241046a2002411c6a410410b3828080000d07200228021c220541808004490d07200541027621040c050b200241096a20073a0000200241013a000820022001360204200241003b011c200241046a2002411c6a410210b3828080000d0620022f011c2205418002490d06200541027621040c040b20074104490d020c050b417f20054190fbc08000109681808000000b200520034190fbc08000109581808000000b200320056b4104490d02200441056a21042005417b4b0d04200420034b0d0520012004360210200620056a2800002204418080808004490d020b20012802082103200128021021050b2002200536020020032005490d042001200320056b22033602082001200128020420056a220536020420014100360210200420034d0d01200041003602000c060b200041003602000c050b20032004460d03024020040d00200042003702082000419c88c080003602042000418489c080003602000c050b200241046a2001410c6a20052003200128020028020011868080800000200241046a41086a22052004360200200020022902043702002001200128020820046b3602082001200128020420046a360204200041086a20052902003702000c040b200520044190fbc08000109681808000000b200420034190fbc08000109581808000000b2002411c6a410c6a41c680808000360200200241046a410c6a420237020020024102360208200241acf9c08000360204200241c6808080003602202002200336022c20022002411c6a36020c20022002412c6a3602242002200236021c200241046a4198fac0800010f680808000000b200020012902003702002001419c88c080003602042001418489c08000360200200041086a200141086a2201290200370200200142003702000b200241306a2480808080000ba10201037f20002d00042103200041003a0004024002400240024002400240024020030d0041012103200028020022002802082204200028021022056b2002490d02200520026a22032005490d03200320044b0d042001200028020420056a200210848e8080001a200020033602100c010b2001200041056a2d00003a000041012103200028020022002802082204200028021022056b2002417f6a2202490d01200520026a22032005490d04200320044b0d05200141016a200028020420056a200210848e8080001a200020033602100b410021030b20030f0b200520034190fbc08000109681808000000b200320044190fbc08000109581808000000b200520034190fbc08000109681808000000b200320044190fbc08000109581808000000bc40301047f23808080800041106b2203248080808000024002400240024002400240200241046a2204450d002004417f4c0d0241002d00fca3c680001a200441002802c8a3c68000118180808000002205450d03200320053602082003200436020402402002413f4b0d00200520024102743a0000410121060c060b200241ffff004b0d0141022106200520024102744101723b00000c050b2003410036020c2003428080808010370204200341046a4100410110b1828080002003280208210520032802042104200328020c21060c030b41002106200241ffffffff034b0d0220052002410274410272360000410421060c030b10ae80808000000b4101200410b280808000000b200520066a41033a00002003200641016a220636020c0240200420066b41034b0d00200341046a2006410410b1828080002003280204210420032802082105200328020c21060b200520066a2002360000200641046a21060b2003200636020c0240200420066b20024f0d00200341046a2006200210b18280800020032802082105200328020c21060b200520066a2001200210848e8080001a200041086a200620026a36020020002003290204370200200341106a2480808080000b2100200128021441a0fbc08000410b200141186a28020028020c118280808000000b840303027f017e047f23808080800041306b2201248080808000200141246a41bbfbc08000410441acfbc08000410f41acfbc08000410010d882808000200141086a2202410036020020014280808080c00037030020012902282103200128022421042001410036021420014280808080800137020c2001410c6a410010b782808000200128021022052001280214220641386c6a220742a4d2928ecac0faa432370308200742c4ccab9c81ebb4b8db003703002007420437022c20074208370224200741c0fcc0800036022020074100360218200741c78080800036021002402004418080808078470d0041bffbc08000411141b0fcc0800010a181808000000b200128020c210720014288808080800137020c200142808080808001370214200041c4006a2001410c6a10b8828080002000413c6a2003370200200020043602382000200641016a36020c2000200536020820002007360204200041003a000020002001290300370350200041d8006a2002280200360200200141306a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a10ba82808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b22064105762207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710b98280800020022802082109200228020c21080b200920084105746a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b4105762107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b22024105742104200241808080204941037421050240024020010d00200341003602180c010b200341083602182003200141057436021c200320002802043602140b200341086a20052004200341146a10ba82808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000b7600200042dbd791d5c2919eaecd0037030820004280808080c00037033820004280808080c00037035020004120360220200041c880808000360218200041033a0000200041106a42e6ed8d82cc91adcb05370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000bda0303017f017e047f0240024020004280808080105a0d0020012102200021030c010b200141786a220220004280c2d72f8022034280bea8d00f7e20007ca722044190ce006e22054190ce0070220641ffff037141e4006e22074101744188d0c180006a2f00003b00002001417c6a200420054190ce006c6b220441ffff037141e4006e22054101744188d0c180006a2f00003b00002001417a6a2006200741e4006c6b41ffff03714101744188d0c180006a2f00003b00002001417e6a2004200541e4006c6b41ffff03714101744188d0c180006a2f00003b00000b024002402003a722014190ce004f0d00200121040c010b2002417c6a21020340200220014190ce006e220441f0b17f6c20016a220541e4006e22064101744188d0c180006a2f00003b0000200241026a2005200641e4006c6b4101744188d0c180006a2f00003b00002002417c6a2102200141ffc1d72f4b21052004210120050d000b200241046a21020b02400240200441e3004b0d00200421010c010b2002417e6a22022004200441ffff037141e4006e220141e4006c6b41ffff03714101744188d0c180006a2f00003b00000b0240200141094b0d002002417f6a200141306a3a00000f0b2002417e6a20014101744188d0c180006a2f00003b00000bb51509017f027e037f017e027f037e017f027e017f23808080800041a0026b22022480808080002000bd220342ffffffffffffff078321042003423488a7210541002106024020034200590d002001412d3a0000410121060b200541ff0f712105024002400240024002400240024020044200520d002005450d010b20044200522005410249722107200442808080808080800884200420051b22034202862104200342018321080240200541cb776a41cc7720051b2205417f4a0d0020024190026a41a8a7c1800020054185a2536c4114762005417f476b220920056a220a41047422056b290300220342002004420284220b420010878e80800020024180026a41b0a7c1800020056b290300220c4200200b420010878e808000200241f0016a20024190026a41086a290300220b2002290380027c220d20024180026a41086a290300200d200b54ad7c2009200a41b1d9b51f6c4113766b413c6a41ff0071220510fc8d808000200241b0016a2003420020042007ad427f857c220b420010878e808000200241a0016a200c4200200b420010878e80800020024190016a200241b0016a41086a290300220b20022903a0017c220d200241a0016a41086a290300200d200b54ad7c200510fc8d808000200241e0016a200342002004420010878e808000200241d0016a200c42002004420010878e808000200241c0016a200241e0016a41086a290300220320022903d0017c220c200241d0016a41086a290300200c200354ad7c200510fc8d80800020022903c001210b200229039001210d20022903f001210c024020094102490d002009413e4b0d032004427f2009ad86427f858350450d030c040b200c20087d210c200720085071210e410121090c040b20024180016a200541c1e8046c411276200541034b6b220a410474220941c8fcc080006a290300220c420020044202842203420010878e808000200241f0006a200941d0fcc080006a290300220b42002003420010878e808000200241e0006a20024180016a41086a290300220d20022903707c220f200241f0006a41086a290300200f200d54ad7c200a20056b200a41cfa6ca006c4113766a413d6a41ff0071220510fc8d808000200241206a200c420020042007ad2210427f857c220d420010878e808000200241106a200b4200200d420010878e8080002002200241206a41086a290300220d20022903107c220f200241106a41086a290300200f200d54ad7c200510fc8d808000200241d0006a200c42002004420010878e808000200241c0006a200b42002004420010878e808000200241306a200241d0006a41086a290300220c20022903407c220b200241c0006a41086a290300200b200c54ad7c200510fc8d8080002002290330210b2002290300210d2002290360210c200a41164f0d0102402004420580a7417b6c41002004a76b470d00417f210503402004a72107200541016a210520044205802204a7417b6c410020076b460d000b2005200a4f0d030c020b02402008500d00417f210503402003a72107200541016a210520034205802203a7417b6c410020076b460d000b200c2005200a4fad7d210c0c020b2010427f8520047c2104417f210503402004a72107200541016a210520044205802204a7417b6c410020076b460d000b2005200a490d014101210e410021090c030b200120066a220541002f00d0d1c180003b0000200541026a41002d00d2d1c180003a00002003423f88a741036a21050c040b4100210702400240200c42e400802203200d42e40080220f560d0041002105200d210f200c2103200b21040c010b200b42e400802204a7419c7f6c200ba76a41314b2107410221050b02402003420a802203200f420a80220c580d000340200541016a21052004220b420a8021042003420a802203200c220f420a80220c560d000b2004a741766c200ba76a41044b21070b2004200f5120077221070c020b4100210e410121090b4100210702400240200c420a802204200d420a80220f560d0041002105200d2103200b210c0c010b41002105410021070340200e200f2203a741766c4100200da76b4671210e200541016a21052009200741ff017145712109200b420a80220ca741766c200ba76a2107200c210b2003210d2004420a8022042003420a80220f560d000b0b024002400240200e450d002003420a80220ba741766c41002003a76b460d010b200c21040c010b0340200ba72111200541016a21052009200741ff017145712109200c420a802204a741766c200ca76a2107200b2103200b420a80220d210b2004210c200da741766c410020116b460d000b0b2008420052200e417f7372200420035171410441052004420183501b2007200741ff01714105461b200720091b41ff017141044b7221070b2005200a6a210941112105024020042007ad7c220442ffff83fea6dee111560d0041102105200442ffff99a6eaafe301560d00410f2105200442ffffe883b1de16560d00410e2105200442ffbfcaf384a302560d00410d2105200442ff9f94a58d1d560d00410c2105200442ffcfdbc3f402560d00410b2105200442ffc7afa025560d00410a2105200442ff93ebdc03560d0041092105200442ffc1d72f560d0041082105200442fface204560d0041072105200442bf843d560d00410621052004429f8d06560d00410521052004428fce00560d0041042105200442e707560d0041032105200442e300560d004102410120044209561b21050b200520096a2107024002400240024002400240024002400240024020094100480d0020074111480d010b2007417f6a22094110490d01200741046a4105490d02200620016a221141016a210e20054101470d05200e41e5003a000020112004a741306a3a00002001200641027222066a210e20094100480d03200921050c040b20042001200520066a6a220e10bc828080000240200520074e0d00200e41302009108a8e8080001a0b2001200720066a22056a41aee0003b0000200541026a21050c080b200420012005200641016a22096a22056a10bc82808000200120066a200120096a200710fe8d8080001a2001200720066a6a412e3a00000c070b200120066a220e41b0dc003b0000410220076b210902402007417f4a0d00200e41026a413020094103200941034a1b417e6a108a8e8080001a0b20042001200520066a20096a22056a10bc828080000c060b200e412d3a0000410120076b2105200e41016a210e0b200541e3004a0d010240200541094a0d00200e200541306a3a00002009411f7641016a20066a21050c050b200e20054101744188d0c180006a2f00003b00002009411f7641027220066a21050c040b2004200520066a220520016a41016a220610bc828080002011200e2d00003a0000200e412e3a0000200641e5003a00002001200541026a22066a210e20094100480d01200921050c020b200e200541e4006e220741306a3a0000200e2005200741e4006c6b4101744188d0c180006a2f00003b00012009411f7641036a20066a21050c020b200e412d3a0000410120076b2105200e41016a210e0b0240200541e3004a0d000240200541094a0d00200e200541306a3a00002009411f7641016a20066a21050c020b200e20054101744188d0c180006a2f00003b00002009411f7641027220066a21050c010b200e200541e4006e220741306a3a0000200e2005200741e4006c6b4101744188d0c180006a2f00003b00012009411f7641036a20066a21050b200241a0026a24808080800020050b8414030b7f027e057f0240024002400240024002400240024002400240024002400240024002400240024002400240024002400240200128020022062f01e2012207410b490d004101210841042107200128020822094105490d03200921072009417b6a0e020302010b2006200128020822094104746a210a2001280204210b200941016a220120074d0d03200a2002370300200a20033703080c040b200941796a210941002108410621070c010b4100210841052107410021090b2001280204210a41002d00fca3c680001a41e80141002802c8a3c6800011818080800000220c450d05200c41003b01e201200c41003602b001200c20062f01e201220d2007417f736a22013b01e2012001410c4f0d06200d200741016a220e6b2001470d07200641b4016a220d20074102746a280200210f200620074104746a22102903002111201041086a2903002112200c2006200e4104746a200141047410848e808000221041b4016a200d200e4102746a200141027410848e8080001a200620073b01e2012006201020081b221320094104746a210120132f01e201220720094b0d0220012002370300200120033703080c030b200620014104746a200a200720096b220841047410fe8d8080001a200a2003370308200a2002370300200641b4016a220a20014102746a200a20094102746a200841027410fe8d8080001a0b200620094102746a41b4016a20043602002006200741016a3b01e2010c020b2013200941016a220e4104746a2001200720096b220d41047410fe8d8080001a2001200337030820012002370300201341b4016a2201200e4102746a200120094102746a200d41027410fe8d8080001a0b200a410020081b210b201320094102746a41b4016a20043602002013200741016a3b01e20102400240024020062802b00122010d00410021100c010b410021100340200621072012210320112102200f2114200c211520012106200a2010470d0720072f01e001210102400240024020062f01e2012210410b490d004101210e200141054f0d012001210d410421010c020b200141016a2107201041016a2104200620014104746a210a0240024020012010490d00200a2002370300200a2003370308200620014102746a41b4016a20143602000c010b200620074104746a200a201020016b220841047410fe8d8080001a200a2003370308200a2002370300200641b4016a220a2007410274220e6a200a2001410274220d6a220a2008410274220810fe8d8080001a200a2014360200200d200641e8016a220a6a41086a200a200e6a200810fe8d8080001a0b200620043b01e201200620074102746a41e8016a20153602002007201041026a22084f0d040240201020016b220e41016a410371220a450d00200620014102746a41ec016a210103402001280200220420073b01e001200420063602b001200141046a2101200741016a2107200a417f6a220a0d000b0b200e4103490d04200741027420066a41f4016a21010340200141746a280200220a20073b01e001200a20063602b001200141786a280200220a200741016a3b01e001200a20063602b0012001417c6a280200220a200741026a3b01e001200a20063602b0012001280200220a200741036a3b01e001200a20063602b001200141106a21012008200741046a2207470d000c050b0b2001210d024002402001417b6a0e020201000b200141796a210d4100210e410621010c010b4100210e410521014100210d0b41002d00fca3c680001a41980241002802c8a3c6800011818080800000220c450d08200c41003b01e201200c41003602b001200c20062f01e20122042001417f736a22073b01e2012007410c4f0d092004200141016a22086b2007470d0a200641b4016a221620014102746a280200210f200620014104746a22042903002111200441086a2903002112200c200620084104746a200741047410848e808000220441b4016a2016200841027422176a200741027410848e8080001a200620013b01e20120042f01e201220741016a21082007410c4f0d0b201020016b22012008470d0c200a41016a2110200441e8016a200620176a41e8016a200141027410848e80800021084100210102400340200820014102746a280200220a20013b01e001200a20043602b001200120074f0d01200120012007496a220120074d0d000b0b20062004200e1b2207200d4104746a210402400240200d41016a220120072f01e201220a4d0d0020042002370300200420033703080c010b200720014104746a2004200a200d6b220841047410fe8d8080001a2004200337030820042002370300200741b4016a220420014102746a2004200d4102746a200841027410fe8d8080001a0b200a41016a21082007200d4102746a221641b4016a2014360200200741e8016a21040240200d41026a2214200a41026a220e4f0d00200420144102746a200420014102746a200a200d6b41027410fe8d8080001a0b200420014102746a2015360200200720083b01e20102402001200e4f0d000240200a200d6b220d41016a4103712204450d00201641ec016a210a0340200a280200220820013b01e001200820073602b001200a41046a210a200141016a21012004417f6a22040d000b0b200d4103490d00200720014102746a41f4016a210a0340200a41746a280200220420013b01e001200420073602b001200a41786a2802002204200141016a3b01e001200420073602b001200a417c6a2802002204200141026a3b01e001200420073602b001200a2802002204200141036a3b01e001200420073602b001200a41106a210a200e200141046a2201470d000b0b2010210a20062802b00122010d000b0b20052802002207280200220a450d0b2007280204210441002d00fca3c680001a41980241002802c8a3c68000118180808000002201450d0c2001200a3602e801200141003b01e201200141003602b00120072001360200200a41003b01e001200a20013602b0012007200441016a36020420042010470d0d20012f01e2012207410b4f0d0e2001200741016a220a3b01e201200120074104746a2204201237030820042011370300200141e8016a200a4102746a200c360200200120074102746a41b4016a200f360200200c200a3b01e001200c20013602b0010b201321060b200020093602082000200b360204200020063602000f0b410841e80110b280808000000b2001410b4190d5c18000109581808000000b41d8d4c1800041284180d5c1800010f880808000000b41b0d5c18000413541e8d5c1800010f880808000000b410841980210b280808000000b2007410b4190d5c18000109581808000000b41d8d4c1800041284180d5c1800010f880808000000b2008410c41a0d5c18000109581808000000b41d8d4c1800041284180d5c1800010f880808000000b41d8d2c1800010a081808000000b410841980210b280808000000b4188d4c18000413041b8d4c1800010f880808000000b41e8d2c18000412041c8d4c1800010f880808000000bf51f011a7f23808080800041c0016b220524808080800002400240024002400240024002400240024002400240024002400240024002400240024002400240200128020022062f01ee042207410b490d0041012108410421092001280208220a4105490d03200a2109200a417b6a0e020302010b200641046a22092001280208220a4102746a210b2001280204210c02400240200a41016a220120074d0d00200b20023602000c010b200920014102746a200b2007200a6b220941027410fe8d8080001a200b2002360200200641306a220b200141346c6a200b200a41346c6a200941346c10fe8d8080001a0b2006200a41346c6a220141e0006a200341306a280200360200200141d8006a200341286a290200370200200141d0006a200341206a290200370200200141c8006a200341186a290200370200200141c0006a200341106a290200370200200141386a200341086a290200370200200141306a20032902003702002006200741016a3b01ee040c030b200a41796a210a41002108410621090c010b41002108410521094100210a0b2001280204210b41002d00fca3c680001a41f00441002802c8a3c68000118180808000002207450d03200741003b01ee0420074100360200200720062f01ee04220d2009417f736a220e3b01ee0420054198016a200641306a220f200941346c6a2201410c6a290200370300200541a0016a200141146a290200370300200541a8016a2001411c6a290200370300200541b0016a200141246a290200370300200541b8016a2001412c6a2902003703002005200129020437039001200e410c4f0d04200d200941016a22106b200e470d0520012802002111200641046a220120094102746a2802002112200741046a200120104102746a200e41027410848e8080001a200741306a200f201041346c6a200e41346c10848e8080001a200620093b01ee04200541e0006a41086a20054190016a41086a290300370300200541e0006a41106a20054190016a41106a290300370300200541e0006a41186a20054190016a41186a290300370300200541e0006a41206a20054190016a41206a290300370300200541e0006a41286a20054190016a41286a29030037030020052005290390013703602006200720081b221341046a200a4102746a21010240024020132f01ee042209200a4b0d00200120023602000c010b200141046a20012009200a6b220e41027410fe8d8080001a200120023602002013200a41346c6a220141e4006a200141306a200e41346c10fe8d8080001a0b200b410020081b210c2013200a41346c6a220141e0006a200341306a280200360200200141d8006a200341286a290200370200200141d0006a200341206a290200370200200141c8006a200341186a290200370200200141c0006a200341106a290200370200200141386a200341086a290200370200200141306a2003290200370200200541086a2203200541e0006a41086a290300370300200541106a2201200541e0006a41106a290300370300200541186a220e200541e0006a41186a290300370300200541206a2202200541e0006a41206a290300370300200541286a2208200541e0006a41286a2903003703002013200941016a3b01ee04200520052903603703002011418080808078470d01201321060b2000200a3602082000200c360204200020063602000c010b200541306a41286a2008290300370300200541306a41206a2002290300370300200541306a41186a200e290300370300200541306a41106a2001290300370300200541306a41086a200329030037030020052005290300370330024002400240200628020022030d00410021140c010b20054190016a41086a210820054190016a41106a211020054190016a41186a210d20054190016a41206a210f20054190016a41286a21154100211420072116201221172011211803402003210e200b2014470d0720062f01ec042106024002400240200e2f01ee042214410b490d0041012119200641054f0d0120062109410421060c020b200e41046a220b200641027422096a2101200641016a2103201441016a21070240024020062014490d0020012017360200200e200641346c6a220141306a2018360200200141346a20052903303702002001413c6a200541386a290300370200200141c4006a200541c0006a290300370200200141cc006a200541c8006a290300370200200141d4006a200541d0006a290300370200200141dc006a200541d8006a2903003702000c010b200b200341027422026a2001201420066b220b410274220810fe8d8080001a20012017360200200e41306a2201200341346c6a2001200641346c6a2201200b41346c10fe8d8080001a20012018360200200120052903303702042001410c6a200541306a41086a290300370200200141146a200541c0006a2903003702002001411c6a200541c8006a290300370200200141246a200541d0006a2903003702002001412c6a200541d8006a2903003702002009200e41f0046a22016a41086a200120026a200810fe8d8080001a0b200e20073b01ee04200e20034102746a41f0046a20163602002003201441026a220b4f0d040240201420066b220941016a4103712201450d00200e20064102746a41f4046a210603402006280200220720033b01ec042007200e360200200641046a2106200341016a21032001417f6a22010d000b0b20094103490d042003410274200e6a41fc046a21060340200641746a280200220120033b01ec042001200e360200200641786a2802002201200341016a3b01ec042001200e3602002006417c6a2802002201200341026a3b01ec042001200e36020020062802002201200341036a3b01ec042001200e360200200641106a2106200b200341046a2203470d000c050b0b20062109024002402006417b6a0e020201000b200641796a210941002119410621060c010b4100211941052106410021090b41002d00fca3c680001a41a00541002802c8a3c68000118180808000002207450d08200741003b01ee04200741003602002007200e2f01ee0422112006417f736a22013b01ee042008200e41306a221a200641346c6a2203410c6a2902003703002010200341146a290200370300200d2003411c6a290200370300200f200341246a29020037030020152003412c6a29020037030020052003290204370390012001410c4f0d092011200641016a22026b2001470d0a20032802002111200e41046a220320064102746a2802002112200741046a20032002410274221b6a200141027410848e8080001a200741306a201a200241346c6a200141346c10848e8080001a200e20063b01ee04200541e0006a41086a22022008290300370300200541e0006a41106a221a2010290300370300200541e0006a41186a221c200d290300370300200541e0006a41206a221d200f290300370300200541e0006a41286a221e2015290300370300200520052903900137036020072f01ee04220341016a21012003410c4f0d0b201420066b22062001470d0c200b41016a2114200741f0046a200e201b6a41f0046a200641027410848e808000210b4100210602400340200b20064102746a280200220120063b01ec0420012007360200200620034f0d01200620062003496a220620034d0d000b0b2015201e290300370300200f201d290300370300200d201c2903003703002010201a290300370300200820022903003703002005200529036037039001200e200720191b220141046a22022009410274221e6a210302400240200941016a220620012f01ee04220b4d0d00200320173602000c010b200220064102746a2003200b20096b220241027410fe8d8080001a20032017360200200141306a2203200641346c6a2003200941346c6a200241346c10fe8d8080001a0b200b41016a21192001200941346c6a220341306a2018360200200341346a20052903303702002003413c6a200541306a41086a2218290300370200200341c4006a200541306a41106a2217290300370200200341cc006a200541306a41186a221a290300370200200341d4006a200541306a41206a221b290300370200200341dc006a200541306a41286a221c290300370200200141f0046a21030240200941026a221d200b41026a22024f0d002003201d4102746a200320064102746a200b20096b41027410fe8d8080001a0b200320064102746a2016360200200120193b01ee040240200620024f0d000240200b20096b221941016a410371220b450d002001201e6a41f4046a210303402003280200220920063b01ec0420092001360200200341046a2103200641016a2106200b417f6a220b0d000b0b20194103490d00200120064102746a41fc046a21030340200341746a280200220b20063b01ec04200b2001360200200341786a280200220b200641016a3b01ec04200b20013602002003417c6a280200220b200641026a3b01ec04200b20013602002003280200220b200641036a3b01ec04200b2001360200200341106a21032002200641046a2206470d000b0b200541286a22062015290300370300200541206a2203200f290300370300200541186a2201200d290300370300200541106a220b2010290300370300200541086a2209200829030037030020052005290390013703002011418080808078460d02201c2006290300370300201b2003290300370300201a20012903003703002017200b290300370300201820092903003703002005200529030037033020072116201221172014210b200e210620112118200e28020022030d000b0b200428020022032802002201450d0b2003280204210b41002d00fca3c680001a41a00541002802c8a3c68000118180808000002206450d0c200620013602f004200641003b01ee042006410036020020032006360200200141003b01ec04200120063602002003200b41016a360204200b2014470d0d20062f01ee042201410b4f0d0e2006200141016a220b3b01ee042006200141346c6a220341346a20052903303702002003413c6a200541386a290300370200200341c4006a200541c0006a290300370200200341cc006a200541c8006a290300370200200341d4006a200541d0006a290300370200200341dc006a200541d8006a290300370200200341306a2011360200200620014102746a41046a2012360200200641f0046a200b4102746a20073602002007200b3b01ec04200720063602000b2000200a3602082000200c360204200020133602000b200541c0016a2480808080000f0b410441f00410b280808000000b200e410b4190d5c18000109581808000000b41d8d4c1800041284180d5c1800010f880808000000b41b0d5c18000413541e8d5c1800010f880808000000b410441a00510b280808000000b2001410b4190d5c18000109581808000000b41d8d4c1800041284180d5c1800010f880808000000b2001410c41a0d5c18000109581808000000b41d8d4c1800041284180d5c1800010f880808000000b41d8d2c1800010a081808000000b410441a00510b280808000000b4188d4c18000413041b8d4c1800010f880808000000b41e8d2c18000412041c8d4c1800010f880808000000bc909030f7f017e057f4100210202400240024002400240024002402001280200220341024622040d00024020012d00490d004100200120041b210520012802302106024002400240024020030d002001410e6a2d00000d032001410c6a2d00002107200141346a2802002204210802400240024020012802042203450d0002400240200420034b0d0020042003460d010c030b200620036a2c00004140480d020b200420036b21080b2008450d0302400240200620036a22092c00002208417f4a0d0020092d0001413f71210a2008411f71210b0240200841604f0d00200b410674200a7221080c020b200a41067420092d0002413f7172210a0240200841704f0d00200a200b410c747221080c020b200a41067420092d0003413f7172200b411274418080f000717221080c010b200841ff017121080b200741ff01710d0a2008418080c400460d014101210702402008418001490d00410221072008418010490d0041034104200841808004491b21070b2001200720036a22033602042003450d0902400240200420034b0d0020042003470d010c0a0b200620036a2c000041bf7f4a0d090b410121070b200120074101733a000c200620042003200441b0d8c18000109781808000000b200141013a000c0c020b02402001411c6a28020022082001413c6a280200220c417f6a220d6a2203200141346a28020022094f0d002001280238210a200c200141186a280200220e6b210f200141106a280200211020012903082111200141246a2802002212417f46211320122114034002400240024002402011200620036a31000088a74101710d0020012008200c6a220836021c0c010b201020102014201020144b1b20131b2215200c2015200c4b1b210b200620086a21162015210302400240024003400240200b2003470d004100201420131b2107201021030340024020072003490d0020012008200c6a220336021c2012417f460d15200141003602240c150b2003417f6a2203200c4f0d05200320086a220420094f0d03200a20036a2d0000200620046a2d0000460d000b20012008200e6a220836021c200f21032013450d060c070b200820036a20094f0d02201620036a2104200a20036a2107200341016a210320072d000020042d0000460d000b200820106b20036a21080c030b200420094190d8c1800010f980808000000b2009201520086a2203200920034b1b200941a0d8c1800010f980808000000b2003200c4180d8c1800010f980808000000b4100210320130d010b20012003360224200321140b2008200d6a22032009490d000b0b2001200936021c0c020b200120074101733a000c20032108200741ff01710d080b200141013a000e0b200141013a00490240024020052d0048450d0020052802442103200528024021040c010b2005280244220320052802402204460d010b200320046b2103200620046a21020c070b200141023602000b024020012802500d000c060b200141d4006a2203280200210220034100360200200141d8006a28020021030c050b200420036b21040b024020040d00410021040c020b41012107200620036a2c00002204417f4a0d0020044160491a0b200741017321040b200120043a000c200321080b2005280240210120052003360240200820016b2103200620016a21020b20002003360204200020023602000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b22064105762207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710cb8280800020022802082109200228020c21080b200920084105746a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b4105762107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041306b2202248080808000200128020c2203200128020422046b220541386e2106200128021021072001280208210820012802002109024002400240024020032004470d00410421010c010b200541c8ffffff7d4b0d0120064105742205417f4c0d0141002d00fca3c680001a200541002802c8a3c68000118180808000002201450d020b200241046a41086a220541003602002002200136020820022006360204200220073602202002200336021c2002200836021820022004360214200220093602102002200136022c2002410036022820022005360224200241106a200241246a10ce82808000200041086a200528020036020020002002290204370200200241306a2480808080000f0b10ae80808000000b4104200510b280808000000bbd05030a7f027e027f23808080800041c0006b220224808080800020012802082103200128020c220421052001280200220621070240200128020422082004460d0020012802102105200621070240034020082209280200220a418080808078460d01200928020c210b2009290218210c2009290210210d20092d0020210e200928020421082009280208210f2002200536023c20022008200f41386c6a3602382002200a360234200220083602302002200836022c200241086a2002412c6a10c282808000200241086a41206a2208200e3a000020072002290308370200200741106a200d370200200741186a200c3702002002200b41ffffffff0171360214200741086a200241086a41086a290300370200200741206a2008280200360200200741246a2107200941246a22082004470d000b0b200941246a21050b20014284808080c00037020020014280808080c000370208200420056b41246e2101024020042005460d004100210e034002402005200e41246c6a220b280208220a450d00200b2802042104200a410171210f410021080240200a4101460d00200441e4006a2109200a417e71210a4100210803400240200941446a280200450d00200941486a28020041002802c0a3c68000118080808000000b02402009417c6a280200450d00200928020041002802c0a3c68000118080808000000b200941f0006a2109200a200841026a2208470d000b0b200f450d002004200841386c6a2209280228450d00200941286a28020441002802c0a3c68000118080808000000b0240200b280200450d00200b28020441002802c0a3c68000118080808000000b0240200b28020c450d00200b410c6a28020441002802c0a3c68000118080808000000b200e41016a220e2001470d000b0b200020063602042000200720066b41246e3602082000200341246c41246e360200200241c0006a2480808080000b9d03010e7f02400240024002400240200128020822020d00410421030c010b200241ffffff1f4b0d0220024105742204417f4c0d022001280204210141002d00fca3c680001a200441002802c8a3c68000118180808000002203450d014100210520022106034020042005460d012001411c6a2802002107200141106a2802002108200128020421092001280218210a2001280214210b200128020c210c024002402001280208220d0d004104210e4100210f0c010b200d41ffffffff004b0d04200d410374220f417f4c0d0441002d00fca3c680001a200f41002802c8a3c6800011818080800000220e450d050b200141206a2101200e2009200f10848e8080002109200320056a220f200d360200200f411c6a2007360200200f41186a200a360200200f41146a200b360200200f41106a2008360200200f410c6a200c360200200f41086a200d360200200f41046a2009360200200541206a21052006417f6a22060d000b0b2000200236020820002003360204200020023602000f0b4104200410b280808000000b10ae80808000000b4104200f10b280808000000b860601177f0240024002400240024002400240200128020822020d00410421030c010b200241e3f1b81c4b0d04200241246c2204417f4c0d042001280204210541002d00fca3c680001a200441002802c8a3c68000118180808000002203450d012005200241246c6a21062002210741002108034020052006460d012005411c6a28020021092005280218210a4104210b4104210c02402005280208220d450d00200d41ffffff1f4b0d06200d410574220e417f4c0d062005280204210141002d00fca3c680001a200e41002802c8a3c6800011818080800000220c450d044100210f200d21100340200e200f460d012001411c6a2802002111200141106a2802002112200128020421132001280218211420012802142115200128020c211602400240200128020822170d0041042118410021040c010b201741ffffffff004b0d0820174103742204417f4c0d0841002d00fca3c680001a200441002802c8a3c68000118180808000002218450d070b200141206a210120182013200410848e8080002113200c200f6a220420173602002004411c6a2011360200200441186a2014360200200441146a2015360200200441106a20123602002004410c6a2016360200200441086a2017360200200441046a2013360200200f41206a210f2010417f6a22100d000b0b200541106a280200211720052d0020210f02400240200541146a28020022040d00410021010c010b200441ffffffff004b0d0620044103742201417f4c0d0641002d00fca3c680001a200141002802c8a3c6800011818080800000220b450d070b200541246a2105200b2017200110848e80800021172003200841246c6a2201200f3a00202001200936021c2001200a36021820012004360214200120173602102001200436020c2001200d3602082001200c3602042001200d360200200841016a21082007417f6a22070d000b0b2000200236020820002003360204200020023602000f0b4104200410b280808000000b4104200e10b280808000000b4104200410b280808000000b10ae80808000000b4104200110b280808000000b880301067f2380808080004190016b2202248080808000200241186a200110c082808000024002400240200228021822030d002000410036020820004280808080c0003702000c010b200228021c210441002d00fca3c680001a412041002802c8a3c68000118180808000002205450d0120052003360200200520043602042002410136022c2002200536022820024104360224200241306a200141e00010848e8080001a200241106a200241306a10c082808000024020022802102204450d0020022802142106410c2103410121010340024020012002280224470d00200241246a2001410241012002280284011b41012002280280011b10cd82808000200228022821050b200520036a220720063602002007417c6a20043602002002200141016a220136022c200341086a2103200241086a200241306a10c082808000200228020c2106200228020822040d000b0b20002002290224370200200041086a200241246a41086a2802003602000b20024190016a2480808080000f0b4104412010b280808000000bcc0401077f2380808080004190016b2202248080808000200241106a200141086a10c0828080000240024002400240024020022802102203450d002002280214210420012802042205450d0220012802002106200541047421050340024020062802042004470d0020032006280200200410888e808000450d030b200641106a2106200541706a22050d000c030b0b2000410036020820004280808080c0003702000c020b2006410c6a2802002104200641086a28020021030b41002d00fca3c680001a412041002802c8a3c68000118180808000002207450d01200720043602042007200336020020024101360224200220073602202002410436021c200241286a200141e80010848e8080001a200241086a200241286a41086a220810c082808000024020022802082203450d00200228020c21044101210103400240200228022c2205450d00200228022821062005410474210502400340024020062802042004470d0020032006280200200410888e808000450d020b200641106a2106200541706a22050d000c020b0b2006410c6a2802002104200641086a28020021030b02402001200228021c470d002002411c6a2001410241012002280284011b41012002280280011b10cd82808000200228022021070b200720014103746a22062004360204200620033602002002200141016a22013602242002200810c08280800020022802042104200228020022030d000b0b2000200229021c370200200041086a2002411c6a41086a2802003602000b20024190016a2480808080000f0b4104412010b280808000000bbf0801117f23808080800041d0006b22022480808080002002200110d08280800002400240024002400240024020022802002203450d0020032802002103200241186a200228020410cf828080002002200336024c2002280218418080808078470d010b2000410036020820004280808080c0003702000c010b2001280220220441016a2203417f20031b22034104200341044b1b22034192c9a4124b0d03200341386c2205417f4c0d0341002d00fca3c680001a200541002802c8a3c68000118180808000002206450d0220062002290218370200200641306a200241186a41306a2207290200370200200641286a200241186a41286a2208290200370200200641206a200241186a41206a2209290200370200200641186a200241186a41186a220a290200370200200641106a200241186a41106a220b290200370200200641086a200241186a41086a220c29020037020020024101360214200220063602102002200336020c02402004450d00200128020c210d20012802082103200128020421052001280200210e4101210f0340024002400240200e450d002005450d010b200e0d0141f8dac1800010a081808000000b4101210e0240200d450d00200d21050240200d4107712201450d0003402005417f6a210520032802f00421032001417f6a22010d000b0b200d4108490d00034020032802f0042802f0042802f0042802f0042802f0042802f0042802f0042802f0042103200541786a22050d000b0b4100210d20032105410021030b0240200d20052f01ee04490d00034020052802002201450d05200341016a210320052f01ec04210d20012105200d20012f01ee044f0d000b0b200d41016a21100240024020030d00200521010c010b200520104102746a41f0046a2802002101410021102003417f6a2211450d002003417e6a2112024020114107712203450d0003402011417f6a211120012802f00421012003417f6a22030d000b0b20124107490d00034020012802f0042802f0042802f0042802f0042802f0042802f0042802f0042802f0042101201141786a22110d000b0b2005200d4102746a41046a2802002103200241186a2005200d41346c6a41306a10cf828080002002200336024c2002280218418080808078460d012004417f6a21040240200f200228020c470d002002410c6a200f200441016a2203417f20031b10cc82808000200228021021060b2006200f41386c6a22032002290218370200200341306a2007290200370200200341286a2008290200370200200341206a2009290200370200200341186a200a290200370200200341106a200b290200370200200341086a200c2902003702002002200f41016a220f36021441002103200121052010210d20040d000b0b2000200229020c370200200041086a2002410c6a41086a2802003602000b200241d0006a2480808080000f0b41e8dac1800010a081808000000b4104200510b280808000000b10ae80808000000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bee0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b22014104742104200141808080c0004941037421050240024020030d00200241003602180c010b200241083602182002200341047436021c200220002802043602140b200241086a20052004200241146a10c982808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b22024105742104200241808080204941037421050240024020010d00200341003602180c010b200341083602182003200141057436021c200320002802043602140b200341086a20052004200341146a10c982808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241386c210420024193c9a4124941027421050240024020010d00200341003602180c010b200341043602182003200141386c36021c200320002802043602140b200341086a20052004200341146a10c982808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bf00101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b2202410374210420024180808080014941027421050240024020010d00200341003602180c010b200341043602182003200141037436021c200320002802043602140b200341086a20052004200341146a10c982808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000b9505020c7f037e23808080800041c0006b22022480808080002001280204210320012802002104200028020821052000280200210602400240024020002802042207200028020c2208460d00200241086a412c6a21092000280210210a200128020820034105746a21014100210b03402007200b6a220041286a280200220c418080808078460d02200241086a41206a220d200041206a290300370300200241086a41186a200041186a290300220e370300200241086a41106a200041106a290300370300200241086a41086a200041086a2903003703002002200029030037030820092000412c6a290200370200200941086a200041346a2802003602002002200c360230200a200241086a10dd828080002100200d290300210f2002280230210c20022902342110200141146a2000360200200141186a200f3702002001410c6a200e370200200141046a20103702002001200c41ffffffff0171360200200141206a2101200341016a21032007200b41386a220b6a2008470d000b0b200420033602000c010b20042003360200200041386a2008460d00200820076b200b6b220341486a220c41386e4101712109410021010240200341907f6a4138490d002000419c016a2100200c41f0006e41017421034100210103400240200041446a280200450d00200041486a28020041002802c0a3c68000118080808000000b02402000417c6a280200450d00200028020041002802c0a3c68000118080808000000b200041f0006a21002003200141026a2201470d000b0b2009450d002007200141386c6a200b6a220041e0006a280200450d00200041e4006a28020041002802c0a3c68000118080808000000b02402005450d00200641002802c0a3c68000118080808000000b200241c0006a2480808080000ba90703097f017e047f23808080800041106b220224808080800020012802042103410421040240024002400240024002400240200128020822050d0041002106410421070c010b200541ffffffff004b0d0420054103742206417f4c0d0441002d00fca3c680001a200641002802c8a3c68000118180808000002207450d010b20072003200610848e80800021080240200141146a2802002209450d00200941ffffff3f4b0d042009410474220a417f4c0d04200141106a28020021034100210641002d00fca3c680001a200a41002802c8a3c68000118180808000002204450d02200921070340200a2006460d012003290208210b200420066a220c2003290200370200200c41086a200b370200200641106a2106200341106a21032007417f6a22070d000b0b0240024002400240024002400240024002400240024020012d00240e080001020304050607000b2002200141286a10c482808000200228020821072002280204210c2002280200210a4100210d0c080b2002200141286a10c582808000200228020821072002280204210c2002280200210a4101210d0c070b200141286a280200210a4102210d0c060b2001412c6a280200210c200141286a280200210a4103210d0c040b2001412c6a28020021034104210d02400240200141306a28020022070d00410021064104210c0c010b200741ffffffff014b0d0a20074102742206417f4c0d0a41002d00fca3c680001a200641002802c8a3c6800011818080800000220c450d090b200c2003200610848e8080001a2007210a0c040b200141256a2d0000210e4105210d0c040b200141286a280200210a4106210d0c020b2001412c6a280200210c200141286a280200210a4107210d0b0b0b2001411c6a280200210f02400240200141206a28020022060d0041042101410021030c010b200641ffffffff004b0d0420064103742203417f4c0d0441002d00fca3c680001a200341002802c8a3c68000118180808000002201450d050b2001200f200310848e8080002103200041206a20063602002000411c6a200336020020002006360218200041306a20073602002000412c6a200c360200200041286a200a360200200041256a200e3a00002000200d3a0024200041146a2009360200200041106a20043602002000200936020c200020053602082000200836020420002005360200200241106a2480808080000f0b4104200610b280808000000b4104200a10b280808000000b4104200610b280808000000b10ae80808000000b4104200310b280808000000bbb0401077f0240024002400240200128022022020d00410021020c010b20012002417f6a3602202001280204210202400240024020012802002203450d002002450d010b2003450d032001410c6a2802002104200141086a28020021050c010b200141086a280200210202402001410c6a2802002205450d0002400240200541077122040d00200521030c010b2005210303402003417f6a210320022802f00421022004417f6a22040d000b0b20054108490d00034020022802f0042802f0042802f0042802f0042802f0042802f0042802f0042802f0042102200341786a22030d000b0b20014200370208200120023602042001410136020041002104410021050b02400240200420022f01ee044f0d00200221030c010b034020022802002203450d04200541016a210520022f01ec04210420032102200420032f01ee044f0d000b0b200441016a21060240024020050d00200321020c010b200320064102746a41f0046a2802002102410021062005417f6a2207450d002005417e6a2108024020074107712205450d0003402007417f6a210720022802f00421022005417f6a22050d000b0b20084107490d00034020022802f0042802f0042802f0042802f0042802f0042802f0042802f0042802f0042102200741786a22070d000b0b2001200636020c20014100360208200120023602042003200441346c6a41306a2105200320044102746a41046a21020b20002005360204200020023602000f0b41f8dac1800010a081808000000b41e8dac1800010a081808000000bd009030e7f017e087f23808080800041c0006b22052480808080002005200120022003200410f28080800020052802302106200541346a2802002107024002400240024020052802000d00200221032005410e6a2d00000d032006200528020422086a21092005410c6a2d00002104024020080d00024020070d0020022103200441ff0171450d0503402004410173220441ff01710d000b200221030c050b024020092c0000220a4100480d00200441ff0171450d0403402004410173220441ff01710d000c050b0b0240200a4160490d000240200a4170490d0020092d0002413f7141067420092d0001413f71410c74722107200441ff0171450d0403402004410173220441ff01710d000c050b0b200441ff0171450d0403402004410173220441ff01710d000c050b0b200441ff0171450d0303402004410173220441ff01710d000c040b0b02400240200820074f0d0020092c0000220a4140480d01200441017321030240200a4100480d00200441ff0171450d050340200341ff017121042003410173210320040d000c060b0b0240200a4160490d000240200a4170490d0020092d0002413f7141067420092d0001413f71410c74722107200441ff0171450d050340200341ff017121042003410173210320040d000c060b0b200441ff0171450d050340200341ff017121042003410173210320040d000c060b0b200441ff0171450d040340200341ff017121042003410173210320040d000c050b0b20082007470d0020022103200441ff0171450d0403402004410173220441ff01710d000b200221030c040b20062007200820074188dbc18000109781808000000b200221032005411c6a28020022092007460d022005413c6a280200220b200541186a280200220c6b210d200b417f6a210e200541246a280200210f2005280238210a200620096a2110200541106a280200221120096b211220052903082113034002402009200e6a22032007490d00200921030c040b410020126b21142009200b6a21152009200c6a2116200f2117200f2118200921040340024020092004460d00200921030c050b0240024002402013200620036a31000088a74101710d004100210320152104200f417f460d020c010b201120112018201120184b1b200f417f4622191b221a200b201a200b4b1b211b201a210402400240024003400240201b2004470d004100201820191b2108201121040340024020082004490d002017410020191b210f2010200b6a21102012200b6b21122015210920152007470d0a200221030c0d0b2004417f6a2204200b4f0d03200420096a220320074f0d04200a20046a2d0000200620036a2d0000460d000b200d2103201621042019450d050c060b200920046a20074f0d03201020046a2103200a20046a2108200441016a210420082d000020032d0000460d000b201420046a2104410021032019450d030c040b2004200b41b4d9c1800010f980808000000b2003200741c4d9c1800010f980808000000b2007201a20096a2204200720044b1b200741d4d9c1800010f980808000000b20032117200321180b2004200e6a22032007490d000b0b200921030c020b20022103200720092d0003413f7172200a411274418080f0007172418080c400460d010b200821030b2000200220036b3602042000200120036a360200200541c0006a2480808080000bda0501097f23808080800041306b220424808080800002400240024002400240200128020022050d0041002d00fca3c680001a41f00441002802c8a3c680001181808080000022060d01410441f00410b280808000000b2001280204210703402005417c6a210820052f01ee042209410274210a41002106417f210b024003400240200a2006470d002009210b0c020b200520066a210c200641046a2106200b41016a210b200841346a2108417f200c41046a280200220c200247200c20024b1b220c4101460d000b200c41ff0171450d040b02402007450d002007417f6a21072005200b4102746a41f0046a28020021050c010b0b200441003602102004200536020c20042002360208200420013602042004200b360214200441206a200b3602002004200429020c370318200441246a200441186a20022003200441046a10bf8280800020042802042206200628020841016a3602080c010b200641013b01ee04200641003602002006200236020420062003290200370230200641386a200341086a290200370200200641c0006a200341106a290200370200200641c8006a200341186a290200370200200641d0006a200341206a290200370200200641d8006a200341286a290200370200200641e0006a200341306a2802003602002001428080808010370204200120063602000b20004180808080783602000c010b20002008290200370200200041306a200841306a2206280200360200200041286a200841286a220b290200370200200041206a200841206a2202290200370200200041186a200841186a220c290200370200200041106a200841106a2205290200370200200041086a200841086a220a29020037020020082003290200370200200a200341086a2902003702002005200341106a290200370200200c200341186a2902003702002002200341206a290200370200200b200341286a2902003702002006200341306a2802003602000b200441306a2480808080000bff0401057f024020002802002201450d00200028020421020240024020002802082203450d00410021000340024002402000450d002002210420012105200021010c010b4100210402402002450d0020022100024020024107712205450d0003402000417f6a210020012802e80121012005417f6a22050d000b0b20024108490d00034020012802e8012802e8012802e8012802e8012802e8012802e8012802e8012802e8012101200041786a22000d000b0b410021050b024002400240200420012f01e201490d00034020012802b0012200450d0220012f01e0012104200141002802c0a3c6800011808080800000200541016a210520002101200420002f01e2014f0d000b200021010b200441016a2102024020050d00200121000c020b200120024102746a41e8016a2802002100410021022005417f6a2201450d012005417e6a2104024020014107712205450d0003402001417f6a210120002802e80121002005417f6a22050d000b0b20044107490d01034020002802e8012802e8012802e8012802e8012802e8012802e8012802e8012802e8012100200141786a22010d000c020b0b200141002802c0a3c680001180808080000041fcd6c1800010a081808000000b410021012003417f6a22030d000c020b0b024020020d00200121000c010b02400240200241077122050d0020012100200221010c010b200121002002210103402001417f6a210120002802e80121002005417f6a22050d000b0b20024108490d00034020002802e8012802e8012802e8012802e8012802e8012802e8012802e8012802e8012100200141786a22010d000b0b034020002802b0012101200041002802c0a3c68000118080808000002001210020010d000b0b0bc50a010c7f024020002802002201450d00200028020421020240024020002802082203450d00410021040340024002402004450d002002210520012106200421010c010b4100210502402002450d0020022100024020024107712204450d0003402000417f6a210020012802f00421012004417f6a22040d000b0b20024108490d00034020012802f0042802f0042802f0042802f0042802f0042802f0042802f0042802f0042101200041786a22000d000b0b410021060b024002400240200520012f01ee04490d00034020012802002200450d0220012f01ec042105200141002802c0a3c6800011808080800000200641016a210620002101200520002f01ee044f0d000b200021010b200541016a2102024020060d00200121040c020b200120024102746a41f0046a2802002104410021022006417f6a2200450d012006417e6a2107024020004107712206450d0003402000417f6a210020042802f00421042006417f6a22060d000b0b20074107490d01034020042802f0042802f0042802f0042802f0042802f0042802f0042802f0042802f0042104200041786a22000d000c020b0b200141002802c0a3c680001180808080000041fcd6c1800010a081808000000b02402001200541346c6a41306a2208280200450d00200828020441002802c0a3c68000118080808000000b0240200828020c450d00200841106a28020041002802c0a3c68000118080808000000b0240024002400240024020082d00240e050001040402040b2008412c6a28020021090240200841306a2802002201450d002001410171210641002100024020014101460d002001417e7121054100210020092101034002402001280200450d00200141046a28020041002802c0a3c68000118080808000000b0240200141206a280200450d00200141246a28020041002802c0a3c68000118080808000000b200141c0006a21012005200041026a2200470d000b0b2006450d00200920004105746a2201280200450d00200128020441002802c0a3c68000118080808000000b20082802280d020c030b2008412c6a28020021090240200841306a280200220a450d0041002107034002402009200741246c6a22062802082201450d002006280204210b2001410171210c41002100024020014101460d002001417e71210541002100200b2101034002402001280200450d00200141046a28020041002802c0a3c68000118080808000000b0240200141206a280200450d00200141246a28020041002802c0a3c68000118080808000000b200141c0006a21012005200041026a2200470d000b0b200c450d00200b20004105746a2201280200450d00200128020441002802c0a3c68000118080808000000b02402006280200450d00200628020441002802c0a3c68000118080808000000b0240200628020c450d002006410c6a28020441002802c0a3c68000118080808000000b200741016a2207200a470d000b0b20082802280d010c020b200841286a280200450d012008412c6a28020021090b200941002802c0a3c68000118080808000000b02402008280218450d002008411c6a28020041002802c0a3c68000118080808000000b410021012003417f6a22030d000c020b0b024020020d00200121040c010b02400240200241077122000d0020012104200221010c010b200121042002210103402001417f6a210120042802f00421042000417f6a22000d000b0b20024108490d00034020042802f0042802f0042802f0042802f0042802f0042802f0042802f0042802f0042104200141786a22010d000b0b034020042802002101200441002802c0a3c68000118080808000002001210420010d000b0b0b02000bd00201027f23808080800041f0006b2205248080808000200541106a200320044198dbc18000410210f280808000200520023602682005200136026420054101360260200541013b0158200520043602544100210120054100360250200541046a200541106a10c682808000024002400240200528020c22060d000c010b2005280208220120064103746a21024100210320012104034002402004280200200441046a28020010d7828080000d00410121010c020b200341016a2103200441086a22042002470d000b024020052802042204418080808078470d00200621030c020b200020063602082000200136020420002004360200200541f0006a2480808080000f0b2005280204450d00200528020841002802c0a3c68000118080808000000b2005200336021420052001360210419adbc180004132200541106a41ccdbc1800041c0dcc18000108981808000000ba90301057f23808080800041106b2202248080808000024002400240024020014104490d00200041036a417c71220320006b220420014d0d010b2000417f6a21042001210303402003450d02200420036a21052003417f6a210320052c0000417f4a0d000b410021050c020b410021052000280000418081828478710d0102404104200420032000461b22032001417c6a22044f0d000340200020036a280200418081828478710d03200341046a22032004490d000b0b200020046a280000418081828478710d010b200241086a2000200141a0ddc18000410210d18280800041002105200228020c2204450d0020022802082203450d0041012106024020032d0000220541df00460d00410121062005419f7f6a41ff0171411a490d00200541bf7f6a41ff0171411a4921060b200341016a21032004417f6a21040240034020042200450d012000417f6a210420032d00002105200341016a22012103200541506a41ff0171410a490d0020012103200541ff017141df00460d00200121032005415f7141bf7f6a41ff0171411a490d000b0b20062000457121050b200241106a24808080800020050be10201017f2380808080004180016b2207248080808000200741186a41086a200320044198dbc18000410210f280808000200720023602782007200136027420074101360270200741013b01682007200436026441002101200741003602602007200636021c200720053602182007410c6a200741186a10c782808000024002400240200728021422060d000c010b2007280210220120064103746a21024100210320012104034002402004280200200441046a28020010d7828080000d00410121010c020b200341016a2103200441086a22042002470d000b0240200728020c2204418080808078470d00200621030c020b20002006360208200020013602042000200436020020074180016a2480808080000f0b200728020c450d00200728021041002802c0a3c68000118080808000000b2007200336021c20072001360218419adbc180004132200741186a41ccdbc1800041d0dcc18000108981808000000bac0201027f23808080800041106b22022480808080000240024020002802000d00200128021441a2ddc18000410f200141186a28020028020c1182808080000021010c010b2002200041046a360204200128021441b1ddc180004111200141186a28020028020c118280808000002100200241003a000d200220003a000c20022001360208200241086a41c2ddc180004107200241046a41ccddc18000108c81808000210320022d000c2100024020022d000d0d00200041ff017141004721010c010b41012101200041ff01710d000240200328020022012d001c4104710d0020012802144187a5c080004102200128021828020c1182808080000021010c010b20012802144186a5c080004101200128021828020c1182808080000021010b200241106a24808080800020010b140020002802002000280204200110e2808080000bf40201037f2380808080004180016b22022480808080002000280200210002400240024002400240200128021c22034110710d0020034120710d0120003502004101200110fe8080800021000c020b20002802002100410021030340200220036a41ff006a413041d7002000410f712204410a491b20046a3a00002003417f6a210320004110492104200041047621002004450d000b20034180016a22004180014b0d022001410141c48dc080004102200220036a4180016a410020036b10db8080800021000c010b20002802002100410021030340200220036a41ff006a413041372000410f712204410a491b20046a3a00002003417f6a210320004110492104200041047621002004450d000b20034180016a22004180014b0d022001410141c48dc080004102200220036a4180016a410020036b10db8080800021000b20024180016a24808080800020000f0b200041800141c88dc08000109481808000000b200041800141c88dc08000109481808000000b8505020a7f027e23808080800041c0006b2204248080808000200128020821050240024002400240200128020c22060d00410021060c010b200141106a28020021070340200641b0016a210820062f01e2012209410474210a4100210b417f210c0240024003400240200a200b470d002009210c0c020b2006200b6a210d200b41106a210b200c41016a210c200841046a2108417f200d290300220e200285200d41086a290300220f20038584420052200e200256200f200356200f2003511b1b220d4101460d000b200d41ff0171450d010b2007450d022007417f6a21072006200c4102746a41e8016a28020021060c010b0b200828020021054100210b0c010b20042001410c6a3602242004200c3602202004410036021c20042006360218200420023e0208200420024220883e020c200420033e0210200420034220883e02140240024020060d0041002d00fca3c680001a41e80141002802c8a3c6800011818080800000220b450d03200b41013b01e201200b41003602b001200b20053602b401200b2004290308370300200b200441106a290300370308200141106a4280808080103702002001200b36020c0c010b200441286a41086a200441186a220b41086a2802003602002004200b290200370328200441346a200441286a2004290308200441086a41086a2903002005200441246a10be828080002004280224220b200b28020841016a3602080b02402001280208220b2001280200470d002001200b10ca828080002001280208210b0b2001280204200b4104746a220b2003370308200b20023703004101210b2001200128020841016a3602080b200020053602042000200b3a0000200441c0006a2480808080000f0b410841e80110b280808000000bf409030c7f017e017f23808080800041c0016b2202248080808000200241086a20002001290300200141086a29030010dc82808000200228020c21030240024020022d0008450d00200241c8006a200128021011808080800000200220024184016a290200370218200220022802800141ffffffff017136021420024190016a2802002104200228028c0121050240024020024194016a28020022060d0041002107410421080c010b4100210741002d00fca3c680001a2006410474220141002802c8a3c68000118180808000002208450d02200420064105746a21092008210a2004210103402001280200220b450d012001280204210c024002402001280218220d0d004100210d0c010b2001290208210e2002200141106a2902003703b0012002200e3703a8012002200d3602b8012000200241a8016a10dd82808000210f4101210d0b200a200d360200200a410c6a200c360200200a41086a200b360200200a41046a200f360200200a41106a210a200741016a2107200141206a22012009470d000b0b02402005450d00200441002802c0a3c68000118080808000000b200241286a2007360200200241246a200836020020022006360220200241386a200241c8006a200010de82808000200241306a2002419c016a290200370200200220022802980141ffffffff017136022c200241c8006a200041186a2003200241146a10d28280800020022802482201418080808078460d0002402001450d00200228024c41002802c0a3c68000118080808000000b02402002280254450d00200241d8006a28020041002802c0a3c68000118080808000000b0240024002400240024020022d006c0e050001040402040b200241f4006a280200210d0240200241f8006a2802002201450d002001410171210b4100210a024020014101460d002001417e712107200d21014100210a034002402001280200450d00200141046a28020041002802c0a3c68000118080808000000b0240200141206a280200450d00200141246a28020041002802c0a3c68000118080808000000b200141c0006a21012007200a41026a220a470d000b0b200b450d00200d200a4105746a2201280200450d00200128020441002802c0a3c68000118080808000000b20022802700d020c030b200241f4006a280200210d0240200241f8006a2802002209450d004100210c03400240200d200c41246c6a220b2802082201450d00200b280204210f200141017121004100210a024020014101460d002001417e7121074100210a200f2101034002402001280200450d00200141046a28020041002802c0a3c68000118080808000000b0240200141206a280200450d00200141246a28020041002802c0a3c68000118080808000000b200141c0006a21012007200a41026a220a470d000b0b2000450d00200f200a4105746a2201280200450d00200128020441002802c0a3c68000118080808000000b0240200b280200450d00200b28020441002802c0a3c68000118080808000000b0240200b28020c450d00200b410c6a28020441002802c0a3c68000118080808000000b200c41016a220c2009470d000b0b20022802700d010c020b200241f0006a280200450d01200241f4006a280200210d0b200d41002802c0a3c68000118080808000000b2002280260450d00200241e4006a28020041002802c0a3c68000118080808000000b200241c0016a24808080800020030f0b4104200110b280808000000bab0603027f017e077f23808080800041d0006b2203248080808000024002400240024002400240024002400240024020012d00000e080001020304050607000b200041046a2002200141046a10df82808000410021010c070b20032002360248200320012802043602402003200141086a280200220436023c20032004360238200320042001410c6a28020041246c6a360244200041046a200341386a10c382808000410121010c060b200141186a2802002104200129030821052003200141106a290300370340200320053703382003200436024820002002200341386a10dd82808000360204410221010c050b200141206a2802002104200041086a2002200141086a10dd8280800036020020002004360204410321010c040b200141086a280200210620012802042107024002402001410c6a28020022080d00410021094104210a0c010b41002d00fca3c680001a2008410274220141002802c8a3c6800011818080800000220a450d052006200841186c6a210b200841037441786a41037641016a2109200a21042006210103402001280210210c200129030021052003200141086a290300370340200320053703382003200c36024820042002200341386a10dd82808000360200200441046a2104200141186a2201200b470d000b0b02402007450d00200641002802c0a3c68000118080808000000b200020083602042000410c6a2009360200200041086a200a360200410421010c030b200020012d00013a0001410521010c020b200141186a2802002104200129030821052003200141106a290300370340200320053703382003200436024820002002200341386a10dd82808000360204410621010c010b200341086a41286a200141306a290300370300200341086a41206a200141286a290300370300200341086a41186a2204200141206a290300370300200341086a41106a200141186a290300370300200341086a41086a200141106a290300370300200320012903083703082002200141086a10dd828080002101200041086a2002200410dd8280800036020020002001360204410721010b200020013a0000200341d0006a2480808080000f0b4104200110b280808000000b950201057f23808080800041306b220324808080800020022802042104200228020021050240024002400240200228020822020d00410421060c010b200241ffffff1f4b0d0120024105742207417f4c0d0141002d00fca3c680001a200741002802c8a3c68000118180808000002206450d020b200341046a41086a2207410036020020032006360208200320023602042003200136022020032004200241386c6a36021c2003200536021820032004360214200320043602102003200636022c2003410036022820032007360224200341106a200341246a10ce82808000200041086a200728020036020020002003290204370200200341306a2480808080000f0b10ae80808000000b4104200710b280808000000b420020004280808080c00037033820004280808080c000370350200041053b0100200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b430020004280808080c00037033820004280808080c00037035020004185063b0100200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b430020004280808080c00037033820004280808080c00037035020004185083b0100200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b430020004280808080c00037033820004280808080c000370350200041850a3b0100200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b430020004280808080c00037033820004280808080c000370350200041850c3b0100200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b430020004280808080c00037033820004280808080c000370350200041850e3b0100200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b550020004280808080c00037033820004280808080c0003703502000410036020c200042808080808001370204200041043a0000200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000bf60201037f2380808080004180016b22022480808080002000280200210002400240024002400240200128021c22034110710d0020034120710d0120003100004101200110fe8080800021000c020b20002d00002103410021000340200220006a41ff006a413041d7002003410f712204410a491b20046a3a00002000417f6a2100200341ff017122044104762103200441104f0d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000c010b20002d00002103410021000340200220006a41ff006a413041372003410f712204410a491b20046a3a00002000417f6a2100200341ff017122044104762103200441104f0d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000b20024180016a24808080800020000f0b200341800141c88dc08000109481808000000b200341800141c88dc08000109481808000000b02000b4601017f23808080800041106b22052480808080002005200236020c200520013602082000200541086a41dcddc180002005410c6a41dcddc180002003200410fb80808000000bd90201047f23808080800041c0006b220224808080800020012c001f417f4a10ad8d8080002103200241186a200141186a290000370300200241106a200141106a290000370300200241086a200141086a29000037030020022001290000370300200241206a200210ab8180800041002104410121050340200220046a2d0000200241206a20046a2d00004610ad8d8080002005712105200441016a22044120470d000b4100210402400240200510ad8d80800020037110ad8d8080002205417f7341017110ad8d80800041ff01710d00200220053a0000200541ff01714101470d0120002001290000370001200041196a200141186a290000370000200041116a200141106a290000370000200041096a200141086a290000370000410121040b200020043a0000200241c0006a2480808080000f0b200241003602204100200241ecddc18000200241206a41c8dec1800010e982808000000b140020002802002000280204200110e2808080000b1900200120002802002200280200200028020410dd808080000b140020012000280200200028020410dd808080000b1200200041d8dec18000200110d9808080000b02000b800902017f017c23808080800041306b2202248080808000024002400240024002400240024002400240024002400240024002400240024002400240024020002d00000e12000102030405060708090a0b0c0d0e0f1011000b200220002d00013a00082002411c6a42013702002002410236021420024190dfc18000360210200241ce8080800036022c200141186a28020021002002200241286a3602182002200241086a36022820012802142000200241106a10d98080800021010c110b200220002903083703082002411c6a420137020020024102360214200241acdfc18000360210200241cf8080800036022c200141186a28020021002002200241286a3602182002200241086a36022820012802142000200241106a10d98080800021010c100b200220002903083703082002411c6a420137020020024102360214200241acdfc18000360210200241d08080800036022c200141186a28020021002002200241286a3602182002200241086a36022820012802142000200241106a10d98080800021010c0f0b20002b030821032002411c6a420137020020024102360214200241ccdfc18000360210200241d18080800036020c20022003390328200141186a28020021002002200241086a3602182002200241286a36020820012802142000200241106a10d98080800021010c0e0b200220002802043602082002411c6a420137020020024102360214200241e8dfc18000360210200241d28080800036022c200141186a28020021002002200241286a3602182002200241086a36022820012802142000200241106a10d98080800021010c0d0b200220002902043702082002411c6a42013702002002410136021420024180e0c18000360210200241d38080800036022c200141186a28020021002002200241286a3602182002200241086a36022820012802142000200241106a10d98080800021010c0c0b200128021441fcdec18000410a200141186a28020028020c1182808080000021010c0b0b20012802144188e0c18000410a200141186a28020028020c1182808080000021010c0a0b20012802144192e0c18000410c200141186a28020028020c1182808080000021010c090b2001280214419ee0c18000410e200141186a28020028020c1182808080000021010c080b200128021441ace0c180004108200141186a28020028020c1182808080000021010c070b200128021441b4e0c180004103200141186a28020028020c1182808080000021010c060b200128021441b7e0c180004104200141186a28020028020c1182808080000021010c050b200128021441bbe0c18000410c200141186a28020028020c1182808080000021010c040b200128021441c7e0c18000410f200141186a28020028020c1182808080000021010c030b200128021441d6e0c18000410d200141186a28020028020c1182808080000021010c020b200128021441e3e0c18000410e200141186a28020028020c1182808080000021010c010b20012802142000280204200041086a280200200141186a28020028020c1182808080000021010b200241306a24808080800020010baf0201027f23808080800041306b220224808080800002400240200029030042ffffffffffffffffff0083bf44000000000000f07f630d002002411c6a420137020020024101360214200241a8e2c18000360210200241d48080800036022c20022000360228200141186a28020021002002200241286a36021820012802142000200241106a10d98080800021030c010b200241003a000c200220013602082002411c6a42013702004101210320024101360214200241a8e2c18000360210200241d48080800036022c200220003602282002200241286a360218200241086a41d8dec18000200241106a10d9808080000d00024020022d000c0d00200128021441b0e2c180004102200141186a28020028020c118280808000000d010b410021030b200241306a24808080800020030b2300200128021420002802002000280204200141186a28020028020c118280808000000bbc0401067f23808080800041306b220224808080800020002802002103024002400240024002400240200028020422040e03030201000b4101210020012802142205419ce2c180004107200141186a280200220628020c2207118280808000000d0420022003360214200241246a42013702002002410236021c200241ece1c18000360218200241d5808080003602082002200241046a3602202002200241146a36020420052006200241186a10d9808080000d03200341086a2101200441037441786a2103034020022001360214200541a3e2c1800041022007118280808000000d042002410236021c200241ece1c1800036021820024201370224200241d5808080003602082002200241046a3602202002200241146a36020420052006200241186a10d9808080000d04200141086a210141002100200341786a22030d000c050b0b200241046a410c6a41d680808000360200200241186a410c6a42023702002002410336021c20024184e2c18000360218200241d680808000360208200220033602042002200341086a36020c200141186a28020021002002200241046a36022020012802142000200241186a10d98080800021000c030b200241246a42013702002002410236021c200241ece1c18000360218200241d68080800036020820022003360204200141186a28020021002002200241046a36022020012802142000200241186a10d98080800021000c020b41f1e0c18000410e41dce1c1800010f880808000000b410121000b200241306a24808080800020000bc90301047f0240024002400240024020024108490d00200141036a417c7122032001460d01200320016b2204450d01200120036b21054101210620012103034020032d0000412e460d05200341016a2103200541016a22050d000b2004200241786a22064b0d030c020b024020020d00410021060c040b20012d0000412e4622060d0320024101460d0320012d0001412e4622060d0320024102460d0320012d0002412e4622060d0320024103460d0320012d0003412e4622060d0320024104460d0320012d0004412e4622060d0320024105460d0320012d0005412e4622060d0320024106460d0320012d0006412e4621060c030b200241786a2106410021040b0340200120046a220341046a280200220541aedcb8f1027341fffdfb776a2005417f73712003280200220341aedcb8f1027341fffdfb776a2003417f737172418081828478710d01200441086a220420064d0d000b0b4100210620042002460d00200120046a21032004417f7320026a210503402005210420032d0000412e4622060d01200341016a21032004417f6a210520040d000b0b2000200620002d0004410047723a00042000280200220328021420012002200341186a28020028020c118280808000000b330020002001412e4620002d0004410047723a0004200028020022002802142001200041186a280200280210118380808000000bfa0103017f017c017e23808080800041106b22032480808080000240024002400240024020002802000e0400010203000b20002b03082104200341033a00002003200439030820032001200210838380800021020c030b20002903082105200341013a00002003200537030820032001200210838380800021020c020b20002903082105200341023a00002003200537030820032001200210838380800021020c010b200341086a4106360200200341b5e2c18000360204200341113a000020032001200210838380800021022000280204450d00200041086a28020041002802c0a3c68000118080808000000b200341106a24808080800020020b02000b220002402000280200450d00200028020441002802c0a3c68000118080808000000b0b2100200128021441fcf8c180004105200141186a28020028020c118280808000000b140020002802042000280208200110e2808080000bf20201027f23808080800041106b220224808080800002400240024002402001418001490d002002410036020c2001418010490d0102402001418080044f0d0020022001413f71418001723a000e20022001410c7641e001723a000c20022001410676413f71418001723a000d410321010c030b20022001413f71418001723a000f20022001410676413f71418001723a000e20022001410c76413f71418001723a000d2002200141127641077141f001723a000c410421010c020b0240200028020822032000280200470d0020002003109083808000200028020821030b200028020420036a20013a00002000200028020841016a3602080c020b20022001413f71418001723a000d2002200141067641c001723a000c410221010b02402000280200200028020822036b20014f0d00200020032001109183808000200028020821030b200028020420036a2002410c6a200110848e8080001a2000200320016a3602080b200241106a24808080800041000b4b01017f02402000280200200028020822036b20024f0d00200020032002109183808000200028020821030b200028020420036a2001200210848e8080001a2000200320026a36020841000b4201017f41002d00fca3c680001a0240411441002802c8a3c680001181808080000022000d004104411410b280808000000b2000420037020c2000410136020020000b810700024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024020002802000e19000102030405060708090a0b0c0d0e0f101112131415161718000b20012802142000280204200041086a280200200141186a28020028020c118280808000000f0b200041046a20011098838080001a000b200128021441d4f9c180004118200141186a28020028020c118280808000000f0b200128021441ecf9c18000411b200141186a28020028020c118280808000000f0b20012802144187fac18000411a200141186a28020028020c118280808000000f0b200128021441a1fac180004119200141186a28020028020c118280808000000f0b200128021441bafac18000410c200141186a28020028020c118280808000000f0b200128021441c6fac180004113200141186a28020028020c118280808000000f0b200128021441d9fac180004113200141186a28020028020c118280808000000f0b200128021441ecfac18000410e200141186a28020028020c118280808000000f0b200128021441fafac18000410e200141186a28020028020c118280808000000f0b20012802144188fbc18000410c200141186a28020028020c118280808000000f0b20012802144194fbc18000410e200141186a28020028020c118280808000000f0b200128021441a2fbc18000410e200141186a28020028020c118280808000000f0b200128021441b0fbc180004113200141186a28020028020c118280808000000f0b200128021441c3fbc18000411a200141186a28020028020c118280808000000f0b200128021441ddfbc18000413e200141186a28020028020c118280808000000f0b2001280214419bfcc180004114200141186a28020028020c118280808000000f0b200128021441affcc180004134200141186a28020028020c118280808000000f0b200128021441e3fcc18000412c200141186a28020028020c118280808000000f0b2001280214418ffdc180004124200141186a28020028020c118280808000000f0b200128021441b3fdc18000410e200141186a28020028020c118280808000000f0b200128021441c1fdc180004113200141186a28020028020c118280808000000f0b200128021441d4fdc18000411c200141186a28020028020c118280808000000f0b200128021441f0fdc180004118200141186a28020028020c118280808000000bc80101017f23808080800041306b2202248080808000024002402000280200220028020c0d002000200110fe8280800021000c010b2002412c6a418580808000360200200241186a410c6a4185808080003602002002410c6a4203370200200241033602042002419cfec1800036020020022000410c6a360220200241db8080800036021c200220003602182002200041106a360228200141186a28020021002002200241186a36020820012802142000200210d98080800021000b200241306a24808080800020000bfb0201017f23808080800041f0006b220224808080800020002802002100200241003602482002428080808010370240200241cc006a41186a41e8f5c18000360200200241033a006c2002412036025c20024100360268200241003602542002410036024c2002200241c0006a36026002402000200241cc006a10fe828080000d00200241306a41086a200241c0006a41086a2802003602002002412c6a418580808000360200200241186a410c6a4185808080003602002002410c6a420337020020022002290240370330200241dc8080800036021c20024104360204200241d0fec180003602002002200041106a36022820022000410c6a360220200141186a28020021002002200241306a3602182002200241186a36020820012802142000200210d980808000210002402002280230450d00200228023441002802c0a3c68000118080808000000b200241f0006a24808080800020000f0b4180f6c180004137200241186a41b8f6c1800041b8f7c18000108981808000000b850201037f23808080800041106b22012480808080002000410c6a28020021020240024002400240024002400240024020002802040e020001020b20020d01410121034100210041c8f7c1800021020c030b2002450d010b200141046a200010b8808080000c020b2000280200220028020021020240200028020422000d0041012103410021000c010b2000417f4c0d0241002d00fca3c680001a200041002802c8a3c68000118180808000002203450d030b20032002200010848e80800021022001200036020c20012002360208200120003602040b200141046a1082838080002100200141106a24808080800020000f0b10ae80808000000b4101200010b280808000000bdd1303157f017e027f23808080800041c0006b2201248080808000200120002802042202200028020822034188fec18000410910f28080800002400240024002400240024020012802000d002001410e6a2d00000d032001410d6a2d00002104200141086a2802002205450d0120012802302106024002402005200141346a2802002207490d0020052007460d010c070b200620056a2c00004140480d060b0240200620056a2208417f6a2d00002209411874411875220a417f4a0d00024002402008417e6a2d00002209411874411875220b41bf7f4c0d002009411f7121090c010b024002402008417d6a2d00002209411874411875220c41bf7f4c0d002009410f7121090c010b2008417c6a2d0000410771410674200c413f717221090b2009410674200b413f717221090b2009410674200a413f717221090b200441ff01710d022009418080c400460d03417f210402402009418001490d00417e21042009418010490d00417d417c200941808004491b21040b0240200420056a22050d00410021050c030b0240024020052007490d0020052007470d070c010b200620056a2c000041bf7f4c0d060b200620056a2204417f6a2c0000417f4a0d022004417e6a2c000041bf7f4a1a0c020b200141206a280200220d2001413c6a280200220e6b2205200141346a280200220a4f0d02200141246a280200210f2001280230210b200141146a2802002210200e2010200e4b1b211120012802382212417f6a2113200141286a2802002114200141186a28020021152001290308211603400240024002402016200b20056a220c31000088a74101710d00200e21042005210d200f417f470d010c020b02400240024002400240024020102010201420102014491b200f417f4622171b2204417f6a2206200e4f0d00201320046a2109410020046b2106200420056a417f6a210403402006450d022004200a4f0d03200641016a2106200b20046a210720092d000021082004417f6a21042009417f6a2109200820072d0000460d000b200d20106b20066b210d200e210420170d070c060b20040d020b200e201420171b22042010200420104b1b210720102104034020072004460d0920112004460d03200520046a200a4f0d04200c20046a2106201220046a2109200441016a210420092d000020062d0000460d000b200d20156b210d201521042017450d040c050b2004200a41ecf8c1800010f980808000000b2006200e41dcf8c1800010f980808000000b2011200e41bcf8c1800010f980808000000b200a200520106a2204200a20044b1b200a41ccf8c1800010f980808000000b200421140b200d200e6b2205200a490d000c030b0b41002105200441ff0171450d010b200220056a210b200541096a220e20036b211241002104417720056b22112109200e21070240024002400240024002400340200520046a210a2003210602402011200446220c0d000240200a41096a2003490d00201220046a0d03200320096a21060c010b200b20046a41096a2c000041bf7f4c0d02200320076b21060b200b20046a2108024002402006450d00200841096a2d000041506a41ff0171410a490d010b200541096a2206200220046a22126a210d200620046a2114200620036b20046a2117200321070240200c0d000240024020142003490d002017450d010c0a0b200d2c000041bf7f4c0d090b200320096a21070b4101210620074108490d05200d29000042a0c6bde3d6ae9bb720520d05200441116a2109200320046b416f6a2111201241116a210641002112410020056b210f416f20056b2115200a20036b41116a2118200a41116a2213210a034020032107024002400240200520096a2210450d00024020102003490d0020052011470d02200f20116a21070c010b200620056a2c000041bf7f4c0d012003200a6b21070b02402007450d00200620056a2d000041506a41ff0171410a490d020b4101210620102003490d08200e20144b0d090240200e450d000240200e2003490d00200e2003460d010c0b0b2002200e6a2c00004140480d0a0b0240200c0d00024020142003490d0020170d0b0c010b200d2c000041bf7f4c0d0a0b2004450d080240024002402002200e6a22072d000041556a0e03010002000b2004210a0c080b20044101460d082004417f6a210a200741016a21070c070b2004210a20044101470d060c070b200220032010200341e880c28000109781808000000b200641016a2106200941016a2109201241016a21122011417f6a2111200a41016a210a0c000b0b2009417f6a2109200441016a2104200741016a21070c000b0b20022003200a41096a200341c880c28000109781808000000b02400240200a4109490d00410021090340200a450d0220072d000041506a220c41094b0d032009ad420a7e2216422088a74100470d03200741016a2107200a417f6a210a2016a7220e200c6a2209200e4f0d000c030b0b41002109034020072d000041506a220c41094b0d02200741016a2107200c2009410a6c6a2109200a417f6a220a0d000b0b0240201320104b0d00024020152004460d00024020132003490d002018450d010c020b200841116a2c00004140480d010b02402010450d0020052011470d010b2012450d02024002400240200841116a22042d000041556a0e03000201020b4101210620124101460d042012417f6a2112200841126a21040c010b4101210620124101460d030b0240024020124109490d004100210803402012450d024101210620042d000041506a220741094b0d052008ad420a7e2216422088a74100470d05200441016a21042012417f6a21122016a7220a20076a2208200a490d050c000b0b4100210841012106034020042d000041506a220741094b0d04200441016a210420072008410a6c6a21082012417f6a22120d000b0b4100210620032005490d06024002402005450d00200320054d0d00200b2c000041bf7f4c0d010b20002005360208200521030c070b4181f9c18000413041b4f9c1800010f880808000000b2002200320132010418881c28000109781808000000b410121060c040b0c030b20022003200e201441f880c28000109781808000000b200220032014200341d880c28000109781808000000b410121060b02400240024002402000280200220420034b0d00200221070c010b0240024020030d00410121070c010b200341002802c8a3c68000118180808000002207450d02200720022004200320042003491b10848e8080001a0b200241002802c0a3c68000118080808000000b41002d00fca3c680001a411441002802c8a3c68000118180808000002204450d0120042003360208200420073602042004410036020020044100200820061b36021020044100200920061b36020c200141c0006a24808080800020040f0b4101200310b280808000000b4104411410b280808000000b200620074100200541c4f9c18000109781808000000bb20101017f23808080800041c0006b22032480808080002003200236020420032001360200200341086a410c6a4202370200200341206a410c6a41dd80808000360200200341306a41086a200041086a2903003703002003410236020c2003418cffc18000360208200341de80808000360224200320002903003703302003200341206a360210200320033602282003200341306a360220200341086a1081838080002100200341c0006a24808080800020000bf70204017f017c017e027f23808080800041c0006b2202248080808000024002400240024020002d0000417d6a0e050100000002000b200241286a41086a200041086a29030037030020022000290300370328200241286a200110f08280800021000c020b0240024020002b03082203bd22044280808080808080f8ff00834280808080808080f8ff00510d00200241286a21002003200241286a10bd8280800021050c010b41a887c2800041ab87c280002004427f5522051b41af87c28000200442ffffffffffffff07835022061b21004103410420051b410320061b21050b2002410c6a42013702002002200536022420022000360220200241df8080800036021c20024102360204200241d0ffc18000360200200141186a28020021002002200241206a3602182002200241186a36020820012802142000200210d98080800021000c010b200128021441e0ffc180004104200141186a28020028020c1182808080000021000b200241c0006a24808080800020000bb20101017f23808080800041c0006b22032480808080002003200236020420032001360200200341086a410c6a4202370200200341206a410c6a41dd80808000360200200341306a41086a200041086a2903003703002003410236020c200341acffc18000360208200341de80808000360224200320002903003703302003200341206a360210200320033602282003200341306a360220200341086a1081838080002100200341c0006a24808080800020000b4201017f41002d00fca3c680001a0240411441002802c8a3c680001181808080000022000d004104411410b280808000000b2000420037020c2000410d36020020000bbd0401057f024002400240024002400240200320024b0d00410121044100210520034101480d04200120036a21060240200341034b0d000340200620014d0d062006417f6a22062d0000410a470d000c050b0b02402006417c6a2800002207417f732007418a94a8d0007341fffdfb776a7141808182847871450d000340200620014d0d062006417f6a22062d0000410a470d000c050b0b200320064103716b210720034109490d010340200722064108480d03200120066a220841786a2802002207417f732007418a94a8d0007341fffdfb776a71418081828478710d03200641786a21072008417c6a2802002208417f732008418a94a8d0007341fffdfb776a7141808182847871450d000c030b0b20032002418884c28000109581808000000b200120076a21060340200620014d0d032006417f6a22062d0000410a470d000c020b0b200120066a21060340200620014d0d022006417f6a22062d0000410a470d000b0b200620016b220641016a2105200620024f0d010b0240200120056a20014d0d0020054103712104024002402005417f6a41034f0d00410021060c010b2005417c712102410021060340200620012d0000410a466a20012d0001410a466a20012d0002410a466a20012d0003410a466a2106200141046a21012002417c6a22020d000b0b02402004450d000340200620012d0000410a466a2106200141016a21012004417f6a22040d000b0b200641016a21040b200020043602002000200320056b3602040f0b20052002419884c28000109581808000000b880501077f02402000280208220220002802042203460d000240024002400240024002400240200220034f0d002000280200220420026a2d000022054122460d07200541dc00460d0702402005411f4b0d0020010d080b2000200241016a2206360208200320066b2107024020010d0020074101480d070240200420036a2208200420066a22056b41034b0d002007210120052102034020022d000022034122460d08200341dc00460d08200241016a21022001417f6a22010d000c090b0b200721032005210220052800002201417f73418081828478712204200141a2c48891027341fffdfb776a71450d030c040b200441016a210541002007417c7122046b210103402001450d02200520026a2103200241046a2102200141046a210120032800002203417f73200341a2c48891027341fffdfb776a200341e0bffffe7d6a72200341dcb8f1e2057341fffdfb776a7271418081828478712203450d000b200020036841037620026a417d6a3602080f0b2002200341a884c2800010f980808000000b2000200420066a36020820001089838080000f0b20072103200521022004200141dcb8f1e2057341fffdfb776a710d002005417c7141046a22022008417c6a22044b0d01034020022802002203417f73418081828478712201200341a2c48891027341fffdfb776a710d022001200341dcb8f1e2057341fffdfb776a710d02200241046a220220044d0d000c020b0b034020022d000022014122460d02200141dc00460d02200241016a21022003417f6a22030d000c030b0b200220084f0d01200820026b2101034020022d000022034122460d01200341dc00460d01200241016a21022001417f6a22010d000c020b0b200220056b21070b2000200720066a3602080b0b5301047f024020002802082201200028020422024f0d00200028020021030340200320016a2d000022044122460d01200441dc00460d0120044120490d012000200141016a220136020820022001470d000b0b0bcc1501087f23808080800041e0006b2203248080808000200128020821042001410110888380800002400240024002402001280208220520012802042206460d000340024020052006490d002005200641b884c2800010f980808000000b02400240024002400240024002400240024002400240024002400240024002402001280200220720056a2d0000220841dc00460d0020084122460d012001200541016a2202360208200341106a20072006200210878380800041002d00fca3c680001a2003280214210120032802102105411441002802c8a3c68000118180808000002202450d032002200536020c2002411036020020002002360204200220013602100c120b024020052004490d00200720046a210902402002280200200228020822086b200520046b22044f0d00200220082004109183808000200228020821080b200228020420086a2009200410848e8080001a2001200541016a22093602082002200820046a220836020802400240024002400240024002400240024002400240200920064f0d002001200541026a2204360208200720096a2d0000415e6a0e5402010101010101010101010101040101010101010101010101010101010101010101010101010101010101010101010101010101010101010101030101010101050101010601010101010101070101010801090a010b200341c0006a20072006200910878380800041002d00fca3c680001a2003280244210120032802402105411441002802c8a3c68000118180808000002202450d0e2002200536020c20024104360200200220013602100c160b200341286a20072006200410878380800041002d00fca3c680001a200328022c210120032802282105411441002802c8a3c68000118180808000002202450d0e2002200536020c2002410c360200200220013602100c150b024020082002280200470d0020022008109083808000200228020821080b200228020420086a41223a00002002200228020841016a3602080c170b024020082002280200470d0020022008109083808000200228020821080b200228020420086a41dc003a00002002200228020841016a3602080c160b024020082002280200470d0020022008109083808000200228020821080b200228020420086a412f3a00002002200228020841016a3602080c150b024020082002280200470d0020022008109083808000200228020821080b200228020420086a41083a00002002200228020841016a3602080c140b024020082002280200470d0020022008109083808000200228020821080b200228020420086a410c3a00002002200228020841016a3602080c130b024020082002280200470d0020022008109083808000200228020821080b200228020420086a410a3a00002002200228020841016a3602080c120b024020082002280200470d0020022008109083808000200228020821080b200228020420086a410d3a00002002200228020841016a3602080c110b024020082002280200470d0020022008109083808000200228020821080b200228020420086a41093a00002002200228020841016a3602080c100b200341cc006a2001108b8380800020032f014c0d020240024002400240024020032f014e22054180f8037122044180b003460d0020044180b803460d0420054180b0bf7f73418090bc7f4f0d01419885c2800010a081808000000b2001280208220420012802042208490d02200341386a20012802002008200410878380800041002d00fca3c680001a200328023c210120032802382105411441002802c8a3c680001181808080000022020d014104411410b280808000000b200341003602542005418001490d09024020054180104f0d0020032005413f71418001723a00552003200541067641c001723a0054410221050c120b20032005413f71418001723a005620032005410c7641e001723a005420032005410676413f71418001723a0055410321050c110b2002200536020c20024104360200200220013602100c0e0b2001200441016a360208200128020020046a2d000041dc00460d0c200341173602542001200341d4006a108c8380800021020c0d0b200341306a20012802002001280204200128020810878380800041002d00fca3c680001a2003280234210120032802302105411441002802c8a3c68000118180808000002202450d072002200536020c20024114360200200220013602100c0c0b2004200541e884c28000109681808000000b20022802082208450d0820052004490d06200720046a210a0240200228020020086b200520046b22094f0d00200220082009109183808000200228020821080b200228020420086a200a200910848e8080001a410121042001200541016a22053602082002200820096a2201360208200341d4006a2002280204200110fc80808000024020032802540d00200020032902583702040c120b200341206a20072006200510878380800041002d00fca3c680001a2003280224210120032802202105411441002802c8a3c68000118180808000002202450d072002200536020c2002410f36020020022001360210200020023602040c100b200328025021020c090b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b200320053a0054410121050c070b4104411410b280808000000b2004200541d884c28000109681808000000b4104411410b280808000000b0240024020052004490d002001200541016a2202360208200341d4006a200720046a200520046b10fc80808000024020032802540d0020002003290258370204410021040c0b0b200341186a20072006200210878380800041002d00fca3c680001a200328021c210120032802182105411441002802c8a3c68000118180808000002202450d012002200536020c2002410f36020020022001360210200020023602040c090b2004200541c884c28000109681808000000b4104411410b280808000000b200341d4006a2001108d83808000024020032d00540d0020032d005521082001200441026a3602080240200841f500470d00200341d4006a2001108b8380800020032f01540d01024020032f015622044180c0006a41ffff03714180f8034f0d00200341143602542001200341d4006a108c8380800021020c030b024020054180d0006a41ffff0371410a7420044180c8006a41ffff0371722208418080046a2205418080c400460d0020054180b00373418080bc7f6a41ff8fbc7f4b0d040b2003410f3602542001200341d4006a108c8380800021020c020b200341173602542001200341d4006a108c8380800021020c010b200328025821020b200020023602040c050b20032004413f71418001723a005720032008410676413f71418001723a005620032005410c76413f71418001723a00552003200541127641077141f001723a0054410421050b02402002280200200228020822046b20054f0d00200220042005109183808000200228020821040b200228020420046a200341d4006a200510848e8080001a2002200420056a360208200128020821040b200141011088838080002001280208220520012802042206470d000b0b200341086a20012802002005200510878380800041002d00fca3c680001a200328020c210120032802082105411441002802c8a3c68000118180808000002202450d022002200536020c2002410436020020002002360204200220013602100b410221040b20002004360200200341e0006a2480808080000f0b4104411410b280808000000bd00401097f23808080800041106b2202248080808000024002400240024002400240024002402001280208220341046a2204200128020422054b0d00200520034d0d03200128020021062001200341016a2207360208200620036a2d000041a885c280006a2d0000220841ff01470d01200721040c040b20012005360208200220012802002005200510878380800041002d00fca3c680001a2002280204210520022802002103411441002802c8a3c68000118180808000002201450d012001200336020c2001410436020020002001360204200120053602100c040b02404100200520036b2209200920054b1b22094101470d00200721030c020b2001200341026a220a3602080240200620076a2d000041a885c280006a2d0000220741ff01470d00200a21040c030b024020094102470d00200a21030c020b2001200341036a220336020802402006200a6a2d000041a885c280006a2d0000220a41ff01470d00200321040c030b20094103460d0120012004360208200620036a2d000041a885c280006a2d0000220141ff01460d022000200741047420084108746a200a6a41047420016a3b0102410021010c040b4104411410b280808000000b20032005418885c2800010f980808000000b200241086a20062005200410878380800041002d00fca3c680001a200228020c210520022802082103411441002802c8a3c68000118180808000002201450d022001200336020c2001410c36020020002001360204200120053602100b410121010b200020013b0100200241106a2480808080000f0b4104411410b280808000000ba00101037f23808080800041106b2202248080808000200241086a20002802002000280204200028020810878380800041002d00fca3c680001a200228020c2103200228020821040240411441002802c8a3c680001181808080000022000d004104411410b280808000000b2000200436020c2000200129020037020020002003360210200041086a200141086a280200360200200241106a24808080800020000bc60101037f23808080800041106b22022480808080000240024002402001280208220320012802042204490d00200241086a20012802002004200310878380800041002d00fca3c680001a200228020c210320022802082104411441002802c8a3c68000118180808000002201450d022001200436020c200141043602002000200136020420012003360210410121010c010b2000200128020020036a2d00003a0001410021010b200020013a0000200241106a2480808080000f0b4104411410b280808000000bef0501057f23808080800041306b2201248080808000200041011088838080000240024002402000280208220220002802042203460d000340024020022003490d002002200341f884c2800010f980808000000b0240024002400240024002402000280200220420026a2d0000220541dc00460d0020054122460d01200141106a20042003200210878380800041002d00fca3c680001a2001280214210220012802102103411441002802c8a3c68000118180808000002200450d022000200336020c20004110360200200020023602100c080b2000200241016a2205360208024002400240200520034f0d002000200241026a2202360208200420056a2d0000415e6a0e54070101010101010101010101010701010101010101010101010101010101010101010101010101010101010101010101010101010101010101010701010101010701010107010101010101010701010107010702010b200141206a20042003200510878380800041002d00fca3c680001a2001280224210220012802202103411441002802c8a3c68000118180808000002200450d042000200336020c20004104360200200020023602100c090b200141186a20042003200210878380800041002d00fca3c680001a200128021c210220012802182103411441002802c8a3c68000118180808000002200450d042000200336020c2000410c360200200020023602100c080b200141286a2000108b8380800020012f0128450d04200128022c21000c070b2000200241016a360208410021000c060b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b200041011088838080002000280208220220002802042203470d000b0b200141086a20002802002002200210878380800041002d00fca3c680001a200128020c210220012802082103411441002802c8a3c68000118180808000002200450d012000200336020c20004104360200200020023602100b200141306a24808080800020000f0b4104411410b280808000000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000be00101037f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014108200141084b1b2201417f73411f7621040240024020030d00200241003602180c010b2002200336021c20024101360218200220002802043602140b200241086a20042001200241146a108f83808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000be20101027f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024108200241084b1b2202417f73411f7621040240024020010d00200341003602180c010b2003200136021c20034101360218200320002802043602140b200341086a20042002200341146a108f83808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000b1200200041b487c28000200110d9808080000b220002402000280200450d00200028020441002802c0a3c68000118080808000000b0bf20201027f23808080800041106b220224808080800002400240024002402001418001490d002002410036020c2001418010490d0102402001418080044f0d0020022001413f71418001723a000e20022001410c7641e001723a000c20022001410676413f71418001723a000d410321010c030b20022001413f71418001723a000f20022001410676413f71418001723a000e20022001410c76413f71418001723a000d2002200141127641077141f001723a000c410421010c020b0240200028020822032000280200470d0020002003109083808000200028020821030b200028020420036a20013a00002000200028020841016a3602080c020b20022001413f71418001723a000d2002200141067641c001723a000c410221010b02402000280200200028020822036b20014f0d00200020032001109183808000200028020821030b200028020420036a2002410c6a200110848e8080001a2000200320016a3602080b200241106a24808080800041000b4b01017f02402000280200200028020822036b20024f0d00200020032002109183808000200028020821030b200028020420036a2001200210848e8080001a2000200320026a36020841000b140020012000280200200028020410dd808080000b180020002802002001200028020428020c118380808000000b170041cc87c28000412841d888c2800010f880808000000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b22064105762207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a41002007109e8380800020022802082109200228020c21080b200920084105746a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b4105762107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bfc0203027f017e047f23808080800041306b2201248080808000200141246a41e888c28000410641ee88c28000412341e888c28000410010d882808000200141086a2202410036020020014280808080c00037030020012902282103200128022421042001410036021420014280808080800137020c2001410c6a4100109d838080002001280210200128021441386c6a2205420437022c2005420d370224200541c38ac2800036022020054100360218200541e980808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db0037030002402004418080808078470d00419a89c280004111418c8ac2800010a181808000000b20012802142105200128020c210620012802102107200042808080808001370244200020043602382000200736020820002006360204200041003a000020002001290300370350200041cc006a41003602002000413c6a20033702002000200541016a36020c200041d8006a2002280200360200200141306a2480808080000bfc0203027f017e047f23808080800041306b2201248080808000200141246a419189c28000410941ee88c28000412341e888c28000410010d882808000200141086a2202410036020020014280808080c00037030020012902282103200128022421042001410036021420014280808080800137020c2001410c6a4100109d838080002001280210200128021441386c6a2205420437022c20054210370224200541d98ac2800036022020054100360218200541ea80808000360210200542f0a3fcacaeba9c956f370308200542e3beec81d3e996af917f37030002402004418080808078470d00419a89c280004111418c8ac2800010a181808000000b20012802142105200128020c210620012802102107200042808080808001370244200020043602382000200736020820002006360204200041003a000020002001290300370350200041cc006a41003602002000413c6a20033702002000200541016a36020c200041d8006a2002280200360200200141306a2480808080000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a109c83808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b22024105742104200241808080204941037421050240024020010d00200341003602180c010b200341083602182003200141057436021c200320002802043602140b200341086a20052004200341146a109c83808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bf60203027f017e057f23808080800041306b2201248080808000200141246a419c8ac28000410641a28ac280004121419c8ac28000410010d882808000200141086a2202410036020020014280808080c00037030020012902282103200128022421042001410036021420014280808080800137020c2001410c6a4100109d8380800020012802102205200128021441386c22066a2207429ae5abc8c1f8c7ab997f370308200742cfdf8096d7fe8c90c100370300200128020c21082007420437022c2007420d370224200741c38ac2800036022020074100360218200741eb8080800036021002402004418080808078470d00419a89c280004111418c8ac2800010a181808000000b200042808080808001370244200020043602382000200536020820002008360204200041003a000020002001290300370350200041cc006a41003602002000413c6a2003370200200041d8006a20022802003602002000200641386a41386e36020c200141306a2480808080000bf40203027f017e057f23808080800041306b2201248080808000200141246a41d08ac28000410941a28ac280004121419c8ac28000410010d882808000200141086a2202410036020020014280808080c00037030020012902282103200128022421042001410036021420014280808080800137020c2001410c6a4100109d8380800020012802102205200128021441386c22066a220742c4decdf7cda6c89430370308200742aad9ebaed398dbfa41370300200128020c21082007420437022c20074210370224200741d98ac2800036022020074100360218200741ec8080800036021002402004418080808078470d00419a89c280004111418c8ac2800010a181808000000b200042808080808001370244200020043602382000200536020820002008360204200041003a000020002001290300370350200041cc006a41003602002000413c6a2003370200200041d8006a20022802003602002000200641386a41386e36020c200141306a2480808080000bf50203027f017e057f23808080800041306b2201248080808000200141246a419c8ac28000410641e98ac280004123419c8ac28000410010d882808000200141086a2202410036020020014280808080c00037030020012902282103200128022421042001410036021420014280808080800137020c2001410c6a4100109d8380800020012802102205200128021441386c22066a220742a4d2928ecac0faa432370308200742c4ccab9c81ebb4b8db00370300200128020c21082007420437022c2007420d370224200741c38ac2800036022020074100360218200741e98080800036021002402004418080808078470d00419a89c280004111418c8ac2800010a181808000000b200042808080808001370244200020043602382000200536020820002008360204200041003a000020002001290300370350200041cc006a41003602002000413c6a2003370200200041d8006a20022802003602002000200641386a41386e36020c200141306a2480808080000bf50203027f017e057f23808080800041306b2201248080808000200141246a41d08ac28000410941e98ac280004123419c8ac28000410010d882808000200141086a2202410036020020014280808080c00037030020012902282103200128022421042001410036021420014280808080800137020c2001410c6a4100109d8380800020012802102205200128021441386c22066a220742f0a3fcacaeba9c956f370308200742e3beec81d3e996af917f370300200128020c21082007420437022c20074210370224200741d98ac2800036022020074100360218200741ea8080800036021002402004418080808078470d00419a89c280004111418c8ac2800010a181808000000b200042808080808001370244200020043602382000200536020820002008360204200041003a000020002001290300370350200041cc006a41003602002000413c6a2003370200200041d8006a20022802003602002000200641386a41386e36020c200141306a2480808080000baf0101017f23808080800041106b2201248080808000200142888080808001370200200142808080808001370208200041c4006a2001109983808000200041106a42e6ed8d82cc91adcb05370300200042dbd791d5c2919eaecd00370308200041c0006a410036020020004280808080c000370338200041d8006a410036020020004280808080c00037035020004121360220200041c880808000360218200041033a0000200141106a2480808080000baf0101017f23808080800041106b2201248080808000200142888080808001370200200142808080808001370208200041c4006a2001109983808000200041106a42e6ed8d82cc91adcb05370300200042dbd791d5c2919eaecd00370308200041c0006a410036020020004280808080c000370338200041d8006a410036020020004280808080c00037035020004120360220200041c880808000360218200041033a0000200141106a2480808080000bb00101017f23808080800041106b2201248080808000200142888080808001370200200142808080808001370208200041c4006a2001109983808000200041106a42e6ed8d82cc91adcb05370300200042dbd791d5c2919eaecd00370308200041c0006a410036020020004280808080c000370338200041d8006a410036020020004280808080c000370350200041c000360220200041c880808000360218200041033a0000200141106a2480808080000bb00101017f23808080800041106b2201248080808000200142888080808001370200200142808080808001370208200041c4006a2001109983808000200041106a42e6ed8d82cc91adcb05370300200042dbd791d5c2919eaecd00370308200041c0006a410036020020004280808080c000370338200041d8006a410036020020004280808080c000370350200041c100360220200041c880808000360218200041033a0000200141106a2480808080000bbf0604057f017e037f017e23808080800041d0006b2201248080808000200141306a418c8bc28000410f419b8bc28000410d418c8bc28000410010d882808000200141286a420437020020014200370220200142808080808001370218200142888080808001370240200142808080808001370248200141186a200141c0006a10a883808000200141086a41086a2001412c6a2802003602002001200129022437030820012802182102200128021c2103200128022021042001280230210520012902342106200141186a41086a2207410036020020014280808080c000370218200141186a410010ad83808000200128021c200728020041246c6a220841003a00202008410936021c200841a88bc280003602182008420437021020084200370208200842808080808001370200200141c0006a41086a2209200728020041016a220836020020012001290218220a37034002402008200aa7470d00200141c0006a200810ad83808000200128024821080b2001280244200841246c6a220841013a00202008410836021c200841b18bc2800036021820084204370210200842003702082008428080808080013702002007200928020041016a220836020020012001290340220a37031802402008200aa7470d00200141186a200810ad83808000200128022021080b200128021c200841246c6a220841023a00202008410e36021c200841b98bc28000360218200842043702102008420037020820084280808080800137020020012802202107200128021c2108200120012802183602202001200836021820012008200741246c6a41246a3602242001200836021c200141c0006a200141186a10a98380800002402005418080808078470d0041e88bc28000411141dc8cc2800010a181808000000b20012002360220200120033602182001200336021c2001200320044105746a360224200041c4006a200141186a10a883808000200141236a200141c0006a41086a2802003600002000413c6a200637020020002005360238200041013a000020002001290308370350200041d8006a200141086a41086a2802003602002001200129024037001b20002001290018370001200041086a2001411f6a290000370000200141d0006a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b22064105762207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710ae8380800020022802082109200228020c21080b200920084105746a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b4105762107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b220641246e2207200128020822014101764f0d01410021082002410036020c20024280808080c00037020441042109024020052004460d00200241046a4100200710af8380800020022802082109200228020c21080b2009200841246c6a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b41246e2107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b220641386e2207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710b08380800020022802082109200228020c21080b2009200841386c6a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b41386e2107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a10ab83808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141246c2104200141e4f1b81c4941027421050240024020030d00200241003602180c010b200241043602182002200341246c36021c200220002802043602140b200241086a20052004200241146a10ab83808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b22024105742104200241808080204941037421050240024020010d00200341003602180c010b200341083602182003200141057436021c200320002802043602140b200341086a20052004200341146a10ab83808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241246c2104200241e4f1b81c4941027421050240024020010d00200341003602180c010b200341043602182003200141246c36021c200320002802043602140b200341086a20052004200341146a10ab83808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241386c210420024193c9a4124941037421050240024020010d00200341003602180c010b200341083602182003200141386c36021c200320002802043602140b200341086a20052004200341146a10ab83808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bcc0403057f017e037f23808080800041d0006b2201248080808000200141246a41e18bc28000410741c88bc28000411941c88bc28000410010d8828080002001411c6a42043702002001420037021420014280808080800137020c2001428880808080013702402001428080808080013702482001410c6a200141c0006a10a883808000200141086a2202200141206a28020036020020012001290218370300200128020c2103200128021021042001280214210520012902282106200128022421072001410036021420014280808080800137020c2001410c6a410010ac838080002001280210200128021441386c6a2208420437022c20084203370224200841ec8cc2800036022020084100360218200841b580808000360210200842e88488d0c0e3aebc13370308200842d7c9cb8fc1cf97db3e37030020012802142109200128021021082001200128020c360248200120083602402001200836024420012008200941386c6a41386a36024c200141306a200141c0006a10aa8380800002402007418080808078470d0041e88bc28000411141dc8cc2800010a181808000000b200120033602142001200436020c200120043602102001200420054105746a360218200041c4006a2001410c6a10a883808000200141176a200141306a41086a2802003600002000413c6a200637020020002007360238200041003a000020002001290300370350200041d8006a20022802003602002001200129033037000f2000200129000c370001200041086a2001410c6a41076a290000370000200141d0006a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b22064105762207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710b78380800020022802082109200228020c21080b200920084105746a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b4105762107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b220641386e2207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710b88380800020022802082109200228020c21080b2009200841386c6a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b41386e2107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bca0403057f017e037f23808080800041d0006b2201248080808000200141246a41808ec28000410641868ec28000412741808ec28000410010d8828080002001411c6a42043702002001420037021420014280808080800137020c2001428880808080013702402001428080808080013702482001410c6a200141c0006a10b283808000200141086a2202200141206a28020036020020012001290218370300200128020c2103200128021021042001280214210520012902282106200128022421072001410036021420014280808080800137020c2001410c6a410010b6838080002001280210200128021441386c6a2208420437022c2008420f370224200841f08dc2800036022020084100360218200841ed80808000360210200842a4d2928ecac0faa432370308200842c4ccab9c81ebb4b8db0037030020012802142109200128021021082001200128020c360248200120083602402001200836024420012008200941386c6a41386a36024c200141306a200141c0006a10b38380800002402007418080808078470d0041ef8cc28000411141e08dc2800010a181808000000b200120033602142001200436020c200120043602102001200420054105746a360218200041c4006a2001410c6a10b283808000200141176a200141306a41086a2802003600002000413c6a200637020020002007360238200041003a000020002001290300370350200041d8006a20022802003602002001200129033037000f2000200129000c370001200041086a200141136a290000370000200141d0006a2480808080000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a10b583808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b22024105742104200241808080204941037421050240024020010d00200341003602180c010b200341083602182003200141057436021c200320002802043602140b200341086a20052004200341146a10b583808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241386c210420024193c9a4124941037421050240024020010d00200341003602180c010b200341083602182003200141386c36021c200320002802043602140b200341086a20052004200341146a10b583808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000baf0101017f23808080800041106b2201248080808000200142888080808001370200200142808080808001370208200041c4006a200110b283808000200041106a42e6ed8d82cc91adcb05370300200042dbd791d5c2919eaecd00370308200041c0006a410036020020004280808080c000370338200041d8006a410036020020004280808080c00037035020004120360220200041c880808000360218200041033a0000200141106a2480808080000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141246c2104200141e4f1b81c4941027421050240024020030d00200241003602180c010b200241043602182002200341246c36021c200220002802043602140b200241086a20052004200241146a10ba83808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a10ba83808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241386c210420024193c9a4124941037421050240024020010d00200341003602180c010b200341083602182003200141386c36021c200320002802043602140b200341086a20052004200341146a10ba83808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241246c2104200241e4f1b81c4941027421050240024020010d00200341003602180c010b200341043602182003200141246c36021c200320002802043602140b200341086a20052004200341146a10ba83808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b22024105742104200241808080204941037421050240024020010d00200341003602180c010b200341083602182003200141057436021c200320002802043602140b200341086a20052004200341146a10ba83808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000b6e00200042e5f0b3f4e8a9b1b12a37030820004280808080c00037033820004280808080c000370350200041ee80808000360218200041023a0000200041106a4281ebc5ecd497b09a0a370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6f00200042dbd791d5c2919eaecd0037030820004280808080c00037033820004280808080c000370350200041c880808000360218200041023a0000200041106a42e6ed8d82cc91adcb05370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b220641246e2207200128020822014101764f0d01410021082002410036020c20024280808080c00037020441042109024020052004460d00200241046a4100200710be8380800020022802082109200228020c21080b2009200841246c6a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b41246e2107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b22064105762207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710bf8380800020022802082109200228020c21080b200920084105746a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b4105762107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b220641386e2207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710bd8380800020022802082109200228020c21080b2009200841386c6a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b41386e2107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bc90103057f027e017f20012802082102200128020022032104024020012802042205200128020c2206460d0020032104034002402005290300220742efb7e9de94adbae42685200541086a290300220842c785eb8b8b8eddc0618584500d0020052802102109200420083703082004200737030020042009360210200441186a21040b200541186a22052006470d000b0b200142888080808001370200200142808080808001370208200020033602042000200420036b41186e3602082000200241186c41186e3602000b920a04027f017e037f017e23808080800041d0006b2201248080808000200141286a41cc90c28000411141dd90c28000411141cc90c28000410010d882808000200141086a410036020020014280808080c00037030020012802282102200129022c2103200141106a41086a22044100360200200142808080808001370210200141106a410010bc838080002001280214200428020041386c6a2205420437022c200542033702242005419596c280003602202005410d36021c2005418896c28000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c370300200141c0006a41086a2206200428020041016a2205360200200120012902102207370340024020052007a7470d00200141c0006a200510bc83808000200128024821050b2001280244200541386c6a2205420437022c200542033702242005419596c280003602202005410c36021c2005419896c28000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c3703002004200628020041016a2205360200200120012903402207370310024020052007a7470d00200141106a200510bc83808000200128021821050b2001280214200541386c6a2205420437022c2005420a370224200541d695c280003602202005410136021c200541d595c28000360218200541f080808000360210200542e0df9add94b69bd2ff00370308200542bcf58bd0d59aa7c9db00370300200141c0006a41086a2204200141106a41086a220628020041016a2205360200200120012903102207370340024020052007a7470d00200141c0006a200510bc83808000200128024821050b2001280244200541386c6a2205420437022c20054227370224200541af96c280003602202005410b36021c200541a496c28000360218200541f180808000360210200542c6be9491c9dc9cca59370308200542c7e49fb5d2d4f6ebb47f3703002006200428020041016a2205360200200120012903402207370310024020052007a7470d00200141106a200510bc83808000200128021821050b2001280214200541386c6a2205420437022c2005420a370224200541e096c280003602202005410a36021c200541d696c28000360218200541f280808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200141c0006a41086a200141106a41086a28020041016a2205360200200120012903102207370340024020052007a7470d00200141c0006a200510bc83808000200128024821050b2001280244200541386c6a2205420437022c2005420c370224200541ed95c280003602202005410d36021c200541e095c28000360218200541f380808000360210200542edb3cdfa95de98ad10370308200542cde181a596cd91e354370300200128024821042001280244210520012001280240360218200120053602102001200536021420012005200441386c6a41386a36021c200141346a200141106a10c48380800002402002418080808078470d0041d293c28000411141c494c2800010a181808000000b200042808080808001370244200041cc006a4100360200200141cb006a200141346a41086a2802003600002000413c6a200337020020002002360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437004320002001290040370001200041086a200141c7006a290000370000200141d0006a2480808080000bc40504027f017e037f017e23808080800041d0006b2201248080808000200141306a41ee90c28000410c41dd90c28000411141cc90c28000410010d882808000200141086a41086a410036020020014280808080c0003703082001280230210220012902342103200141186a41086a2204410036020020014280808080c000370218200141186a410010bb83808000200128021c200428020041246c6a220541003a00202005410c36021c200541fa90c280003602182005420437021020054200370208200542808080808001370200200141c0006a41086a2206200428020041016a2205360200200120012902182207370340024020052007a7470d00200141c0006a200510bb83808000200128024821050b2001280244200541246c6a220541013a00202005411d36021c2005418691c2800036021820054204370210200542003702082005428080808080013702002004200628020041016a2205360200200120012903402207370318024020052007a7470d00200141186a200510bb83808000200128022021050b200128021c200541246c6a220541023a00202005411b36021c200541a391c28000360218200542043702102005420037020820054280808080800137020020012802202104200128021c2105200120012802183602202001200536021820012005200441246c6a41246a3602242001200536021c200141c0006a200141186a10c28380800002402002418080808078470d0041d293c28000411141c494c2800010a181808000000b200042808080808001370244200041cc006a4100360200200141236a200141c0006a41086a2802003600002000413c6a200337020020002002360238200041013a000020002001290308370350200041d8006a200141086a41086a2802003602002001200129024037001b20002001290018370001200041086a2001411f6a290000370000200141d0006a2480808080000b880504027f017e027f017e23808080800041d0006b2201248080808000200141286a41d091c28000411641dd90c28000411141cc90c28000410010d882808000200141086a410036020020014280808080c00037030020012802282102200129022c2103200141106a41086a22044100360200200142808080808001370210200141106a410010bc838080002001280214200428020041386c6a2205420437022c2005420a370224200541d695c280003602202005410136021c200541d595c28000360218200541f080808000360210200542e0df9add94b69bd2ff00370308200542bcf58bd0d59aa7c9db00370300200141c0006a41086a200428020041016a2205360200200120012902102206370340024020052006a7470d00200141c0006a200510bc83808000200128024821050b2001280244200541386c6a2205420437022c2005420c370224200541ed95c280003602202005410d36021c200541e095c28000360218200541f380808000360210200542edb3cdfa95de98ad10370308200542cde181a596cd91e354370300200128024821042001280244210520012001280240360218200120053602102001200536021420012005200441386c6a41386a36021c200141346a200141106a10c48380800002402002418080808078470d0041d293c28000411141c494c2800010a181808000000b200042808080808001370244200041cc006a4100360200200141cb006a200141346a41086a2802003600002000413c6a200337020020002002360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437004320002001290040370001200041086a200141c7006a290000370000200141d0006a2480808080000bd00303027f017e037f23808080800041d0006b2201248080808000200141286a418392c28000411741dd90c28000411141cc90c28000410010d882808000200141086a2202410036020020014280808080c000370300200129022c21032001280228210420014100360218200142808080808001370210200141106a410010bc838080002001280214200128021841386c6a2205420437022c20054207370224200541ea96c2800036022020054100360218200541f480808000360210200542b891b68c98adebcf61370308200542e7b0a091f3ed9c85c500370300200128021821062001280214210520012001280210360218200120053602102001200536021420012005200641386c6a41386a36021c200141346a200141106a10c48380800002402004418080808078470d0041d293c28000411141c494c2800010a181808000000b200042808080808001370244200041cc006a4100360200200141cc006a200141346a41086a2802003600002000413c6a200337020020002004360238200041003a000020002001290300370350200041d8006a20022802003602002001200129023437004420002001290041370001200041086a200141c8006a290000370000200141d0006a2480808080000b930a04027f017e037f017e23808080800041d0006b2201248080808000200141286a419a92c28000410541dd90c28000411141cc90c28000410010d882808000200141086a410036020020014280808080c00037030020012802282102200129022c2103200141106a41086a22044100360200200142808080808001370210200141106a410010bc838080002001280214200428020041386c6a2205420437022c200542033702242005419596c280003602202005410b36021c200541f196c28000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c370300200141c0006a41086a2206200428020041016a2205360200200120012902102207370340024020052007a7470d00200141c0006a200510bc83808000200128024821050b2001280244200541386c6a2205420437022c20054204370224200541fc94c280003602202005410a36021c200541fc96c28000360218200541f580808000360210200542b8b6d386cdcbfab1a07f370308200542e3a4fae3cee3d18d723703002004200628020041016a2205360200200120012903402207370310024020052007a7470d00200141106a200510bc83808000200128021821050b2001280214200541386c6a2205420437022c200542033702242005419596c280003602202005410836021c2005418697c28000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c370300200141c0006a41086a2204200141106a41086a220628020041016a2205360200200120012903102207370340024020052007a7470d00200141c0006a200510bc83808000200128024821050b2001280244200541386c6a2205420437022c20054227370224200541af96c280003602202005410b36021c200541a496c28000360218200541f180808000360210200542c6be9491c9dc9cca59370308200542c7e49fb5d2d4f6ebb47f3703002006200428020041016a2205360200200120012903402207370310024020052007a7470d00200141106a200510bc83808000200128021821050b2001280214200541386c6a2205420437022c2005420a370224200541e096c280003602202005410a36021c200541d696c28000360218200541f280808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200141c0006a41086a200141106a41086a28020041016a2205360200200120012903102207370340024020052007a7470d00200141c0006a200510bc83808000200128024821050b2001280244200541386c6a2205420437022c200542163702242005419497c280003602202005410636021c2005418e97c28000360218200541f680808000360210200542f2b3a2a5fbe78f9ac600370308200542b7e2f1ac9bf5d4dc827f370300200128024821042001280244210520012001280240360218200120053602102001200536021420012005200441386c6a41386a36021c200141346a200141106a10c48380800002402002418080808078470d0041d293c28000411141c494c2800010a181808000000b200042808080808001370244200041cc006a4100360200200141cb006a200141346a41086a2802003600002000413c6a200337020020002002360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437004320002001290040370001200041086a200141c7006a290000370000200141d0006a2480808080000bec0201027f23808080800041206b220124808080800041002d00fca3c680001a0240413041002802c8a3c680001181808080000022020d004108413010b280808000000b200242a5e9e3ab9e929adc2c370318200242e9b494c69bdbc4d608370308200242ead283debcdebd93d800370300200241f780808000360210200241206a4293888c8f89fdc6ec9e7f370300200241286a41ef808080003602002001200241306a36021c200141023602182001200236021420012002360210200141046a200141106a10c583808000200142888080808001370210200142808080808001370218200041c4006a200141106a10c3838080002001411b6a200141046a41086a280200360000200041c0006a410036020020004280808080c000370338200041043a0000200041d8006a410036020020004280808080c0003703502001200129020437001320002001290010370001200041086a200141176a290000370000200141206a2480808080000bca0403057f017e037f23808080800041d0006b2201248080808000200141286a41b693c28000410641bc93c28000411641a092c28000410010d882808000200141206a420437020020014200370218200142808080808001370210200142888080808001370240200142808080808001370248200141106a200141c0006a10c383808000200141086a2202200141246a2802003602002001200129021c370300200128021021032001280214210420012802182105200129022c21062001280228210720014100360218200142808080808001370210200141106a410010bc838080002001280214200128021841386c6a2208420437022c2008420f370224200841f995c2800036022020084100360218200841f880808000360210200842a4d2928ecac0faa432370308200842c4ccab9c81ebb4b8db00370300200128021821092001280214210820012001280210360248200120083602402001200836024420012008200941386c6a41386a36024c200141346a200141c0006a10c48380800002402007418080808078470d0041d293c28000411141c494c2800010a181808000000b2001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10c3838080002001411b6a200141346a41086a2802003600002000413c6a200637020020002007360238200041003a000020002001290300370350200041d8006a20022802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000bec0201027f23808080800041206b220124808080800041002d00fca3c680001a0240413041002802c8a3c680001181808080000022020d004108413010b280808000000b200242a5e9e3ab9e929adc2c37031820024293888c8f89fdc6ec9e7f370308200242a5e9e3ab9e929adc2c370300200241ef80808000360210200241206a4293888c8f89fdc6ec9e7f370300200241286a41ef808080003602002001200241306a36021c200141023602182001200236021420012002360210200141046a200141106a10c583808000200142888080808001370210200142808080808001370218200041c4006a200141106a10c3838080002001411b6a200141046a41086a280200360000200041c0006a410036020020004280808080c000370338200041043a0000200041d8006a410036020020004280808080c0003703502001200129020437001320002001290010370001200041086a200141176a290000370000200141206a2480808080000baf0101017f23808080800041106b2201248080808000200142888080808001370200200142808080808001370208200041c4006a200110c383808000200041106a42e6ed8d82cc91adcb05370300200042dbd791d5c2919eaecd00370308200041c0006a410036020020004280808080c000370338200041d8006a410036020020004280808080c00037035020004120360220200041c880808000360218200041033a0000200141106a2480808080000b9f0704057f017e037f017e23808080800041d0006b2201248080808000200141286a41a092c28000411041b092c28000411a41a092c28000410010d882808000200141106a41106a420437020020014200370218200142808080808001370210200142888080808001370240200142808080808001370248200141106a200141c0006a10c383808000200141086a200141246a2802003602002001200129021c37030020012802102102200128021421032001280218210420012802282105200129022c2106200141106a41086a22074100360200200142808080808001370210200141106a410010bc838080002001280214200728020041386c6a2208420437022c20084215370224200841e394c280003602202008410f36021c200841d494c28000360218200841b580808000360210200842e88488d0c0e3aebc13370308200842d7c9cb8fc1cf97db3e370300200141c0006a41086a2209200728020041016a220836020020012001290210220a37034002402008200aa7470d00200141c0006a200810bc83808000200128024821080b2001280244200841386c6a2208420437022c20084204370224200841fc94c280003602202008410436021c200841f894c28000360218200841f580808000360210200842b8b6d386cdcbfab1a07f370308200842e3a4fae3cee3d18d723703002007200928020041016a220836020020012001290340220a37031002402008200aa7470d00200141106a200810bc83808000200128021821080b2001280214200841386c6a2208420437022c2008420c3702242008418d95c280003602202008410d36021c2008418095c28000360218200841f980808000360210200842cd86adf4d1ba8a89763703082008428ad5e2d7bdbcbdc342370300200128021821072001280214210820012001280210360248200120083602402001200836024420012008200741386c6a41386a36024c200141346a200141c0006a10c48380800002402005418080808078470d0041d293c28000411141c494c2800010a181808000000b2001200236021820012003360210200120033602142001200320044105746a36021c200041c4006a200141106a10c3838080002001411b6a200141346a41086a2802003600002000413c6a200637020020002005360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000b810604057f017e027f017e23808080800041d0006b2201248080808000200141286a41ca92c28000411741b092c28000411a41a092c28000410010d882808000200141206a420437020020014200370218200142808080808001370210200142888080808001370240200142808080808001370248200141106a200141c0006a10c383808000200141086a200141246a2802003602002001200129021c37030020012802102102200128021421032001280218210420012802282105200129022c2106200141106a41086a22074100360200200142808080808001370210200141106a410010bc838080002001280214200728020041386c6a2208420437022c20084215370224200841e394c280003602202008410f36021c200841d494c28000360218200841b580808000360210200842e88488d0c0e3aebc13370308200842d7c9cb8fc1cf97db3e370300200141c0006a41086a200728020041016a2208360200200120012902102209370340024020082009a7470d00200141c0006a200810bc83808000200128024821080b2001280244200841386c6a2208420437022c20084204370224200841fc94c280003602202008410436021c200841f894c28000360218200841f580808000360210200842b8b6d386cdcbfab1a07f370308200842e3a4fae3cee3d18d72370300200128024821072001280244210820012001280240360248200120083602402001200836024420012008200741386c6a41386a36024c200141346a200141c0006a10c48380800002402005418080808078470d0041d293c28000411141c494c2800010a181808000000b2001200236021820012003360210200120033602142001200320044105746a36021c200041c4006a200141106a10c3838080002001411b6a200141346a41086a2802003600002000413c6a200637020020002005360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000b9c0704057f017e037f017e23808080800041d0006b2201248080808000200141286a41e192c28000411541b092c28000411a41a092c28000410010d882808000200141206a420437020020014200370218200142808080808001370210200142888080808001370240200142808080808001370248200141106a200141c0006a10c383808000200141086a200141246a2802003602002001200129021c37030020012802102102200128021421032001280218210420012802282105200129022c2106200141106a41086a22074100360200200142808080808001370210200141106a410010bc838080002001280214200728020041386c6a2208420437022c20084215370224200841e394c280003602202008410f36021c200841d494c28000360218200841b580808000360210200842e88488d0c0e3aebc13370308200842d7c9cb8fc1cf97db3e370300200141c0006a41086a2209200728020041016a220836020020012001290210220a37034002402008200aa7470d00200141c0006a200810bc83808000200128024821080b2001280244200841386c6a2208420437022c20084204370224200841fc94c280003602202008410436021c200841f894c28000360218200841f580808000360210200842b8b6d386cdcbfab1a07f370308200842e3a4fae3cee3d18d723703002007200928020041016a220836020020012001290340220a37031002402008200aa7470d00200141106a200810bc83808000200128021821080b2001280214200841386c6a2208420437022c2008420c3702242008418d95c280003602202008410d36021c2008418095c28000360218200841f980808000360210200842cd86adf4d1ba8a89763703082008428ad5e2d7bdbcbdc342370300200128021821072001280214210820012001280210360248200120083602402001200836024420012008200741386c6a41386a36024c200141346a200141c0006a10c48380800002402005418080808078470d0041d293c28000411141c494c2800010a181808000000b2001200236021820012003360210200120033602142001200320044105746a36021c200041c4006a200141106a10c3838080002001411b6a200141346a41086a2802003600002000413c6a200637020020002005360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000baa0403057f017e027f23808080800041d0006b2201248080808000200141286a41f692c28000410941b092c28000411a41a092c28000410010d882808000200141206a420437020020014200370218200142808080808001370210200142888080808001370240200142808080808001370248200141106a200141c0006a10c383808000200141086a2202200141246a2802003602002001200129021c370300200128021021032001280214210420012802182105200129022c2106200128022821072001410036021820014280808080c000370210200141c0006a200141106a41ff92c28000410710d483808000200141106a200141c0006a418693c28000410e10d683808000200141346a200141106a419493c28000410c10d7838080002001200128023436021820012001280238220836021020012008200128023c41246c6a36021c20012008360214200141c0006a200141106a10c28380800002402007418080808078470d0041d293c28000411141c494c2800010a181808000000b2001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10c3838080002001411b6a200141c0006a41086a2802003600002000413c6a200637020020002007360238200041013a000020002001290300370350200041d8006a20022802003602002001200129024037001320002001290010370001200041086a200141106a41076a290000370000200141d0006a2480808080000bf80303057f017e027f23808080800041d0006b2201248080808000200141286a41a093c28000411441b092c28000411a41a092c28000410010d882808000200141206a420437020020014200370218200142808080808001370210200142888080808001370240200142808080808001370248200141106a200141c0006a10c383808000200141086a2202200141106a41146a2802003602002001200129021c370300200128021021032001280214210420012802182105200129022c2106200128022821072001410036021820014280808080c000370210200141346a200141106a41b493c28000410210d5838080002001200128023436021820012001280238220836021020012008200128023c41246c6a36021c20012008360214200141c0006a200141106a10c28380800002402007418080808078470d0041d293c28000411141c494c2800010a181808000000b2001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10c3838080002001411b6a200141c0006a41086a2802003600002000413c6a200637020020002007360238200041013a000020002001290300370350200041d8006a20022802003602002001200129024037001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010bc838080002004280208200428020c220541386c6a2206420437022c200642103702242006419995c2800036022020064100360218200641fa80808000360210200642e1eab2d29ed8a59afd003703082006428b91acbbbb91f6d30b37030020042802042107200428020821080240200128020822062001280200470d002001200610bb83808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bf20303037f017e027f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010bc8380800020042802182005280200220641386c6a2205420437022c2005420a370224200541d695c280003602202005410136021c200541d595c28000360218200541f080808000360210200542e0df9add94b69bd2ff00370308200542bcf58bd0d59aa7c9db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610bc83808000200428021021060b200428020c200641386c6a2205420437022c2005420c370224200541ed95c280003602202005410d36021c200541e095c28000360218200541f380808000360210200542edb3cdfa95de98ad10370308200542cde181a596cd91e35437030020042802082108200428020c21090240200128020822052001280200470d002001200510bb83808000200128020821050b2001280204200541246c6a220541013a00202005200336021c200520023602182005410036021420054280808080c00037020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010bc838080002004280208200428020c220541386c6a2206420437022c20064217370224200641a995c2800036022020064100360218200641fb80808000360210200642beb6e3c9cf8cb1be2137030820064290e384e9e5949acd8f7f37030020042802042107200428020821080240200128020822062001280200470d002001200610bb83808000200128020821060b2001280204200641246c6a220641023a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bbd0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010bc838080002004280208200428020c220541386c6a2206420437022c20064215370224200641c095c2800036022020064100360218200641fc80808000360210200642a8f8d28adee2a2ca163703082006429e8f89a5d7f0a0bf7d37030020042802042107200428020821080240200128020822062001280200470d002001200610bb83808000200128020821060b2001280204200641246c6a220641033a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000baf0101017f23808080800041106b2201248080808000200142888080808001370200200142808080808001370208200041c4006a200110c383808000200041106a42e6ed8d82cc91adcb05370300200042dbd791d5c2919eaecd00370308200041c0006a410036020020004280808080c000370338200041d8006a410036020020004280808080c00037035020004120360220200041c880808000360218200041033a0000200141106a2480808080000b7700200042dbd791d5c2919eaecd0037030820004280808080c00037033820004280808080c000370350200041c000360220200041c880808000360218200041033a0000200041106a42e6ed8d82cc91adcb05370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b7600200042dbd791d5c2919eaecd0037030820004280808080c00037033820004280808080c00037035020004120360220200041c880808000360218200041033a0000200041106a42e6ed8d82cc91adcb05370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a10db83808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bf50203027f017e057f23808080800041306b2201248080808000200141246a41ac98c28000410641b298c28000411941ac98c28000410010d882808000200141086a2202410036020020014280808080c00037030020012902282103200128022421042001410036021420014280808080800137020c2001410c6a410010dc8380800020012802102205200128021441386c22066a220742a4d2928ecac0faa432370308200742c4ccab9c81ebb4b8db00370300200128020c21082007420437022c2007420f370224200741cb98c2800036022020074100360218200741fd8080800036021002402004418080808078470d0041aa97c280004111419c98c2800010a181808000000b200042808080808001370244200020043602382000200536020820002008360204200041003a000020002001290300370350200041cc006a41003602002000413c6a2003370200200041d8006a20022802003602002000200641386a41386e36020c200141306a2480808080000bf50203027f017e057f23808080800041306b2201248080808000200141246a41da98c28000410941b298c28000411941ac98c28000410010d882808000200141086a2202410036020020014280808080c00037030020012902282103200128022421042001410036021420014280808080800137020c2001410c6a410010dc8380800020012802102205200128021441386c22066a220742f0a3fcacaeba9c956f370308200742e3beec81d3e996af917f370300200128020c21082007420437022c20074212370224200741e398c2800036022020074100360218200741fe8080800036021002402004418080808078470d0041aa97c280004111419c98c2800010a181808000000b200042808080808001370244200020043602382000200536020820002008360204200041003a000020002001290300370350200041cc006a41003602002000413c6a2003370200200041d8006a20022802003602002000200641386a41386e36020c200141306a2480808080000bf20203027f017e057f23808080800041306b2201248080808000200141246a41f899c28000410441fc99c28000411241f899c28000410010d882808000200141086a2202410036020020014280808080c00037030020012902282103200128022421042001410036021420014280808080800137020c2001410c6a410010e283808000200128021022052001280214220641386c6a22074293888c8f89fdc6ec9e7f370308200742a5e9e3ab9e929adc2c370300200128020c21082007420437022c200742033702242007418e9ac2800036022020074100360218200741ef8080800036021002402004418080808078470d0041f598c28000411141e899c2800010a181808000000b20002001290300370350200042808080808001370244200020043602382000200536020820002008360204200041003a0000200041cc006a41003602002000413c6a20033702002000200641016a36020c200041d8006a2002280200360200200141306a2480808080000bf20203027f017e057f23808080800041306b2201248080808000200141246a41919ac28000410c41fc99c28000411241f899c28000410010d882808000200141086a2202410036020020014280808080c00037030020012902282103200128022421042001410036021420014280808080800137020c2001410c6a410010e283808000200128021022052001280214220641386c6a22074293888c8f89fdc6ec9e7f370308200742a5e9e3ab9e929adc2c370300200128020c21082007420437022c200742033702242007418e9ac2800036022020074100360218200741ef8080800036021002402004418080808078470d0041f598c28000411141e899c2800010a181808000000b20002001290300370350200042808080808001370244200020043602382000200536020820002008360204200041003a0000200041cc006a41003602002000413c6a20033702002000200641016a36020c200041d8006a2002280200360200200141306a2480808080000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a10e183808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a10e383808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241386c210420024193c9a4124941037421050240024020010d00200341003602180c010b200341083602182003200141386c36021c200320002802043602140b200341086a20052004200341146a10e383808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b22024105742104200241808080204941037421050240024020010d00200341003602180c010b200341083602182003200141057436021c200320002802043602140b200341086a20052004200341146a10e383808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241246c2104200241e4f1b81c4941027421050240024020010d00200341003602180c010b200341043602182003200141246c36021c200320002802043602140b200341086a20052004200341146a10e383808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bf10803067f057e027f23808080800041a0056b220324808080800020034200370340200342f9c2f89b91a3b3f0db00370338200342ebfa86dabfb5f6c11f3703302003429fd8f9d9c291da829b7f370328200342d1859aeffacf9487d100370320200342f1edf4f8a5a7fda7a57f370318200342abf0d3f4afeebcb73c370310200342bbceaaa6d8d0ebb3bb7f370308200342c892f795ffccf984ea00370300200341cf006a410041f900108a8e8080002104200341c8016a41073a0000200341cb006a41002800a39ac28000360000200341002800a09ac2800036024802400240200241fa00490d002004200141f90010848e8080001a20034280013703402003200341c8006a22054200420010bb80808000200141f9006a2201200241877f6a2202410776200241ff00712202456b22064107746a2107200241800120021b210402402006450d00200641077421020340200320032903404280017c370340200320014200420010bb8080800020014180016a2101200241807f6a22020d000b0b20052007200410848e8080001a0c010b20042001200210848e8080001a200241076a21040b200320043a00c80120034190026a200341d00110848e8080001a200320032903d002200341d8036a2d00002201ad7c3703d002200341d8026a210202402001418001460d00200220016a410041800120016b108a8e8080001a0b200341003a00d80320034190026a2002427f420010bb80808000200341a0046a41086a220120034190026a41086a290300370300200341a0046a41106a220220034190026a41106a290300370300200341a0046a41186a220420034190026a41186a290300370300200341a0046a41206a220620032903b002370300200341a0046a41286a220520034190026a41286a290300370300200341a0046a41306a220720034190026a41306a290300370300200341a0046a41386a220820034190026a41386a29030037030020032003290390023703a004200341e0036a41106a20022903002209370300200341e0036a41186a2004290300220a370300200341e0036a41206a2006290300220b370300200341e0036a41286a2005290300220c370300200341e0036a41306a2007290300220d370300200341d0016a41086a22022001290300370300200341d0016a41106a22042009370300200341d0016a41186a2206200a370300200341d0016a41206a2205200b370300200341d0016a41286a2207200c370300200341d0016a41306a220e200d370300200341d0016a41386a220f2008290300370300200320032903a0043703d00141002d00fca3c680001a024041c00041002802c8a3c680001181808080000022010d00410141c00010b280808000000b200120032903d001370000200041c00036020820002001360204200041c000360200200141386a200f290300370000200141306a200e290300370000200141286a2007290300370000200141206a2005290300370000200141186a2006290300370000200141106a2004290300370000200141086a2002290300370000200341a0056a2480808080000bca0403057f017e037f23808080800041d0006b2201248080808000200141286a41b69ac28000410941a79ac28000410f41a09ac28000410010d882808000200141206a420437020020014200370218200142808080808001370210200142888080808001370240200142808080808001370248200141106a200141c0006a10f483808000200141086a2202200141246a2802003602002001200129021c370300200128021021032001280214210420012802182105200129022c21062001280228210720014100360218200142808080808001370210200141106a410010e4838080002001280214200128021841386c6a2208420437022c20084207370224200841809cc2800036022020084100360218200841ff80808000360210200842d886fac2c186f9c46f3703082008429cfcf2b9cfebc9bfa37f370300200128021821092001280214210820012001280210360248200120083602402001200836024420012008200941386c6a41386a36024c200141346a200141c0006a10f58380800002402007418080808078470d0041fd9ac28000411141f09bc2800010a181808000000b2001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10f4838080002001411b6a200141346a41086a2802003600002000413c6a200637020020002007360238200041003a000020002001290300370350200041d8006a20022802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000baf0101017f23808080800041106b2201248080808000200142888080808001370200200142808080808001370208200041c4006a200110f483808000200041106a42e6ed8d82cc91adcb05370300200042dbd791d5c2919eaecd00370308200041c0006a410036020020004280808080c000370338200041d8006a410036020020004280808080c00037035020004104360220200041c880808000360218200041033a0000200141106a2480808080000bcd0403057f017e037f23808080800041d0006b2201248080808000200141286a41c09ac28000410e41ce9ac28000410741c09ac28000410010d882808000200141206a420437020020014200370218200142808080808001370210200142888080808001370240200142808080808001370248200141106a200141c0006a10f483808000200141086a2202200141246a2802003602002001200129021c370300200128021021032001280214210420012802182105200129022c21062001280228210720014100360218200142808080808001370210200141106a410010e4838080002001280214200128021841386c6a2208420437022c20084207370224200841879cc28000360220200841003602182008418081808000360210200842b891b68c98adebcf61370308200842e7b0a091f3ed9c85c500370300200128021821092001280214210820012001280210360248200120083602402001200836024420012008200941386c6a41386a36024c200141346a200141c0006a10f58380800002402007418080808078470d0041fd9ac28000411141f09bc2800010a181808000000b2001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10f4838080002001411b6a200141346a41086a2802003600002000413c6a200637020020002007360238200041003a000020002001290300370350200041d8006a20022802003602002001200129023437001320002001290010370001200041086a200141106a41076a290000370000200141d0006a2480808080000bb50303057f017e017f23808080800041c0006b2201248080808000200141246a41d59ac28000410441ce9ac28000410741c09ac28000410010d8828080002001411c6a42043702002001420037021420014280808080800137020c2001428880808080013702302001428080808080013702382001410c6a200141306a10f483808000200141086a2202200141206a28020036020020012001290218370300200128020c21032001280210210420012802142105200129022821062001280224210720014284808080c00037020c20014280808080c000370214200141306a2001410c6a10f38380800002402007418080808078470d0041fd9ac28000411141f09bc2800010a181808000000b200120033602142001200436020c200120043602102001200420054105746a360218200041c4006a2001410c6a10f483808000200141176a200141306a41086a2802003600002000413c6a200637020020002007360238200041013a000020002001290300370350200041d8006a20022802003602002001200129023037000f2000200129000c370001200041086a2001410c6a41076a290000370000200141c0006a2480808080000b950301037f0240200028020022022802002200413f4b0d00200041027421020240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a20023a00000f0b0240200041ffff004b0d002000410274410172210202402001280200200128020822006b41014b0d0020012000410210b182808000200128020821000b2001200041026a360208200128020420006a20023b00000f0b0240200041ffffffff034b0d002000410274410272210202402001280200200128020822006b41034b0d0020012000410410b182808000200128020821000b2001200041046a360208200128020420006a20023600000f0b02402001280200220320012802082200470d0020012000410110b18280800020012802002103200128020821000b2001280204220420006a41033a00002001200041016a2200360208200228020021020240200320006b41034b0d0020012000410410b18280800020012802042104200128020821000b2001200041046a360208200420006a20023600000b7600200042dbd791d5c2919eaecd0037030820004280808080c00037033820004280808080c00037035020004120360220200041c880808000360218200041033a0000200041106a42e6ed8d82cc91adcb05370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b7700200042dbd791d5c2919eaecd0037030820004280808080c00037033820004280808080c000370350200041c000360220200041c880808000360218200041033a0000200041106a42e6ed8d82cc91adcb05370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b880504027f017e027f017e23808080800041d0006b2201248080808000200141286a41dc9ac28000410c41e89ac28000411541dc9ac28000410010d882808000200141086a410036020020014280808080c00037030020012802282102200129022c2103200141106a41086a22044100360200200142808080808001370210200141106a410010e4838080002001280214200428020041386c6a2205420437022c2005420c370224200541989cc280003602202005410a36021c2005418e9cc280003602182005418181808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200141c0006a41086a200428020041016a2205360200200120012902102206370340024020052006a7470d00200141c0006a200510e483808000200128024821050b2001280244200541386c6a2205420437022c20054208370224200541a99cc280003602202005410536021c200541a49cc280003602182005418281808000360210200542f0a3fcacaeba9c956f370308200542e3beec81d3e996af917f370300200128024821042001280244210520012001280240360218200120053602102001200536021420012005200441386c6a41386a36021c200141346a200141106a10f58380800002402002418080808078470d0041fd9ac28000411141f09bc2800010a181808000000b200042808080808001370244200041cc006a4100360200200141cb006a200141346a41086a2802003600002000413c6a200337020020002002360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437004320002001290040370001200041086a200141c7006a290000370000200141d0006a2480808080000b810201037f23808080800041206b22032480808080000240024002400240200241046a22040d00410121050c010b2004417f4c0d0141002d00fca3c680001a200441002802c8a3c68000118180808000002205450d020b20034100360214200320053602102003200436020c200320023602182003200341186a36021c2003411c6a2003410c6a10ed838080000240200328020c200328021422046b20024f0d002003410c6a2004200210b182808000200328021421040b200328021020046a2001200210848e8080001a200041086a200420026a3602002000200329020c370200200341206a2480808080000f0b10ae80808000000b4101200410b280808000000b6f00200042dbd791d5c2919eaecd0037030820004280808080c00037033820004280808080c000370350200041c880808000360218200041023a0000200041106a42e6ed8d82cc91adcb05370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b220641246e2207200128020822014101764f0d01410021082002410036020c20024280808080c00037020441042109024020052004460d00200241046a4100200710e78380800020022802082109200228020c21080b2009200841246c6a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b41246e2107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b22064105762207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710e68380800020022802082109200228020c21080b200920084105746a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b4105762107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b220641386e2207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710e58380800020022802082109200228020c21080b2009200841386c6a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b41386e2107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000b6f002000428d9abfc787899e9d7f37030820004280808080c00037033820004280808080c0003703502000418381808000360218200041023a0000200041106a42aeabccc7cea8c39bcc00370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000be20101017f41002d00fca3c680001a0240413041002802c8a3c680001181808080000022010d004108413010b280808000000b200142e7b0a091f3ed9c85c500370318200142a8c7daf8defb9a8fc400370308200142a7b98ffce8cbcbd38b7f370300200141848180800036021020004280808080c00037033820004280808080c0003703502000410236020c2000200136020820004102360204200041043a0000200141206a42b891b68c98adebcf61370300200141286a418581808000360200200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6f00200042dbd791d5c2919eaecd0037030820004280808080c00037033820004280808080c000370350200041c880808000360218200041023a0000200041106a42e6ed8d82cc91adcb05370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b950501077f23808080800041306b22012480808080002001410836020c200141b49dc2800036020841002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241b49dc28000360200200241046a410836020041b49dc28000410810d782808000450d0141002d00fca3c680001a41c00041002802c8a3c68000118180808000002203450d02200342a7b98ffce8cbcbd38b7f370308200341bd9dc28000360220200341848180800036021820034101360204200341bc9dc28000360200200341306a42b891b68c98adebcf61370300200341286a42e7b0a091f3ed9c85c500370300200341106a42a8c7daf8defb9a8fc400370300200341386a418581808000360200200341246a410136020020014100360218200142808080808001370210200141106a410010fe8380800020012802142204200128021841386c22056a220642bcc8e6aaf4e393df6c3703082006429de7d5c2a3c6909862370300200128021021072006410036023020064280808080c00037032820064100360220200641003602182006418681808000360210200041cc006a4102360200200041c8006a2003360200200041023602442000413c6a2002ad4280808080108437020020004101360238200041d8006a410036020020004280808080c0003703502000200541386a41386e36020c2000200436020820002007360204200041003a0000200141306a2480808080000f0b4104410810b280808000000b200241002802c0a3c68000118080808000002001411c6a42013702002001410236021420014180ddc18000360210200141b48080800036022c2001200141286a3602182001200141086a360228200141106a4190ddc1800010f680808000000b410841c00010b280808000000b7600200042dbd791d5c2919eaecd0037030820004280808080c00037033820004280808080c00037035020004108360220200041c880808000360218200041033a0000200041106a42e6ed8d82cc91adcb05370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b800303027f017e057f23808080800041306b2201248080808000200141246a41c09dc28000410c41cc9dc28000410c41c09dc28000410010d882808000200141086a2202410036020020014280808080c00037030020012902282103200128022421042001410036021420014280808080800137020c2001410c6a410010fe8380800020012802102205200128021441386c22066a2207428485acdad4b59be240370308200742f4d89fc7ac8bcaf8db00370300200128020c21082007420437022c20074225370224200741dc9dc280003602202007410436021c200741d89dc28000360218200741878180800036021002402004418080808078470d0041b19cc28000411141a49dc2800010a181808000000b200042808080808001370244200020043602382000200536020820002008360204200041003a000020002001290300370350200041cc006a41003602002000413c6a2003370200200041d8006a20022802003602002000200641386a41386e36020c200141306a2480808080000bcc0505027f017e037f017e017f23808080800041d0006b2201248080808000200141306a41819ec28000411441cc9dc28000410c41c09dc28000410010d882808000200141086a41086a410036020020014280808080c0003703082001280230210220012902342103200141186a41086a22044100360200200142808080808001370218200141186a410010fe83808000200128021c2004280200220541386c6a2206420437022c20064204370224200641999ec280003602202006410436021c200641959ec28000360218200641888180800036021020064298848fa1dab08ba174370308200642febac4ad81b6fafcb37f370300200141c0006a41086a200541016a2205360200200120012902182207370340024020052007a7470d00200141c0006a200510fe83808000200128024821050b2001280244200541386c6a2206420437022c20064204370224200641999ec280003602202006410b36021c2006419d9ec28000360218200641888180800036021020064298848fa1dab08ba174370308200642febac4ad81b6fafcb37f3703002004200541016a2206360200200120012903402207370318024020062007a72205470d00200141186a200610fe8380800020012802182105200128022021060b200128021c2204200641386c22086a220642aeb899d38addfa91213703082006429cc4c6fe96f1ecf5e0003703002006420437022c2006420c370224200641c09dc280003602202006410636021c200641a89ec28000360218200641898180800036021002402002418080808078470d0041b19cc28000411141a49dc2800010a181808000000b200042808080808001370244200020023602382000200436020820002005360204200041003a000020002001290308370350200041cc006a41003602002000413c6a2003370200200041d8006a200141106a2802003602002000200841386a41386e36020c200141d0006a2480808080000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a10fd83808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000b8f04010a7f23808080800041c0006b220324808080800002400240024002402001280204220441216e2205200220052002491b22050d00410121060c010b200541bef0831f4b0d01200541216c2207417f4c0d0141002d00fca3c680001a200741002802c8a3c68000118180808000002206450d020b4100210720034100360214200320063602102003200536020c0240024002402002450d0020012802002105034020044121490d0220012004415f6a22043602042001200541216a2208360200200341186a41086a2209200541086a290000370300200341186a41106a220a200541106a290000370300200341186a41186a220b200541186a290000370300200341186a41206a220c200541206a2d00003a00002003200529000037031802402007200328020c470d002003410c6a200710838480800020032802102106200328021421070b2006200741216c6a22052003290318370000200541206a200c2d00003a0000200541186a200b290300370000200541106a200a290300370000200541086a20092903003700002003200741016a2207360214200821052002417f6a22020d000b0b2000200329020c370200200041086a2003410c6a41086a2802003602000c010b2000418080808078360200200328020c450d00200641002802c0a3c68000118080808000000b200341c0006a2480808080000f0b10ae80808000000b4101200710b280808000000beb0301097f23808080800041306b22032480808080000240024002400240200128020422044105762205200220052002491b22050d00410121060c010b200541ffffff1f4b0d0120054105742207417f4c0d0141002d00fca3c680001a200741002802c8a3c68000118180808000002206450d020b410021072003410036020c20032006360208200320053602040240024002402002450d0020012802002105034020044120490d022001200441606a22043602042001200541206a2208360200200341106a41086a2209200541086a290000370300200341106a41106a220a200541106a290000370300200341106a41186a220b200541186a29000037030020032005290000370310024020072003280204470d00200341046a200710848480800020032802082106200328020c21070b200620074105746a22052003290310370000200541186a200b290300370000200541106a200a290300370000200541086a20092903003700002003200741016a220736020c200821052002417f6a22020d000b0b20002003290204370200200041086a200341046a41086a2802003602000c010b20004180808080783602002003280204450d00200641002802c0a3c68000118080808000000b200341306a2480808080000f0b10ae80808000000b4101200710b280808000000beb0301097f23808080800041306b22032480808080000240024002400240200128020422044105762205200220052002491b22050d00410121060c010b200541ffffff1f4b0d0120054105742207417f4c0d0141002d00fca3c680001a200741002802c8a3c68000118180808000002206450d020b410021072003410036020c20032006360208200320053602040240024002402002450d0020012802002105034020044120490d022001200441606a22043602042001200541206a2208360200200341106a41086a2209200541086a290000370300200341106a41106a220a200541106a290000370300200341106a41186a220b200541186a29000037030020032005290000370310024020072003280204470d00200341046a200710848480800020032802082106200328020c21070b200620074105746a22052003290310370000200541186a200b290300370000200541106a200a290300370000200541086a20092903003700002003200741016a220736020c200821052002417f6a22020d000b0b20002003290204370200200041086a200341046a41086a2802003602000c010b20004180808080783602002003280204450d00200641002802c0a3c68000118080808000000b200341306a2480808080000f0b10ae80808000000b4101200710b280808000000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bea0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141bff0831f492104200141216c21050240024020030d00200241003602180c010b200241013602182002200341216c36021c200220002802043602140b200241086a20042005200241146a108284808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bea0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b22014180808020492104200141057421050240024020030d00200241003602180c010b200241013602182002200341057436021c200220002802043602140b200241086a20042005200241146a108284808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000ba90301057f23808080800041206b22022480808080000240024002402001422088a722030d002000410036020820004280808080103702000c010b20022003417f6a220436021820022001a7220541016a3602140240024002400240024020052d000022064103710e0400030102000b200641027621030c030b20034104490d0420022003417c6a3602182002200541046a360214200541036a2d000041187420052f000141087472200672220341808004490d04200341027621030c020b200641044f0d0320034105490d0320022003417b6a3602182002200541056a360214200528000122034180808080044f0d010c030b2004450d0220022003417e6a3602182002200541026a36021420052d000141087420067241ffff03712203418002490d02200341027621030b200241086a200241146a20031081848080002002280208418080808078460d0120002002290208370200200041086a200241086a41086a280200360200200541002802c0a3c68000118080808000000b200241206a2480808080000f0b41ae9ec28000412e2002411f6a41dc9ec2800041dc9fc28000108981808000000ba90301057f23808080800041206b22022480808080000240024002402001422088a722030d002000410036020820004280808080103702000c010b20022003417f6a220436021820022001a7220541016a3602140240024002400240024020052d000022064103710e0400030102000b200641027621030c030b20034104490d0420022003417c6a3602182002200541046a360214200541036a2d000041187420052f000141087472200672220341808004490d04200341027621030c020b200641044f0d0320034105490d0320022003417b6a3602182002200541056a360214200528000122034180808080044f0d010c030b2004450d0220022003417e6a3602182002200541026a36021420052d000141087420067241ffff03712203418002490d02200341027621030b200241086a200241146a200310ff838080002002280208418080808078460d0120002002290208370200200041086a200241086a41086a280200360200200541002802c0a3c68000118080808000000b200241206a2480808080000f0b41ae9ec28000412e2002411f6a41dc9ec2800041dc9fc28000108981808000000ba90301057f23808080800041206b22022480808080000240024002402001422088a722030d002000410036020820004280808080103702000c010b20022003417f6a220436021820022001a7220541016a3602140240024002400240024020052d000022064103710e0400030102000b200641027621030c030b20034104490d0420022003417c6a3602182002200541046a360214200541036a2d000041187420052f000141087472200672220341808004490d04200341027621030c020b200641044f0d0320034105490d0320022003417b6a3602182002200541056a360214200528000122034180808080044f0d010c030b2004450d0220022003417e6a3602182002200541026a36021420052d000141087420067241ffff03712203418002490d02200341027621030b200241086a200241146a20031080848080002002280208418080808078460d0120002002290208370200200041086a200241086a41086a280200360200200541002802c0a3c68000118080808000000b200241206a2480808080000f0b41ae9ec28000412e2002411f6a41dc9ec2800041dc9fc28000108981808000000ba80301057f23808080800041206b22022480808080002001280204210302400240024002400240024020012802082201410c6c41046a22040d00410121050c010b2004417f4c0d0141002d00fca3c680001a200441002802c8a3c68000118180808000002205450d020b20024100360214200220053602102002200436020c200220013602182002200241186a36021c2002411c6a2002410c6a10b5848080002001450d022001410c6c2106200341086a210403402004417c6a28020021032002200428020022013602182002200241186a36021c2002411c6a2002410c6a10b5848080000240200228020c200228021422056b20014f0d002002410c6a2005200110b182808000200228021421050b200228021020056a2003200110848e8080001a2002200520016a22013602142004410c6a2104200641746a22060d000c040b0b10ae80808000000b4101200410b280808000000b200228021421010b200228020c2105200228021021042000410c6a2001360200200041086a2004360200200020053602042000410136020020002001ad4220862004ad84370310200241206a2480808080000b02000b2100200128021441ec9fc280004105200141186a28020028020c118280808000000b0f002000280200200110e9808080000b120020014198a2c28000410210dd808080000b02000bd30101027f2001410c6a2802002102024002400240024002400240024020012802040e020001020b20020d01410121034100210141b8a1c2800021020c030b2002450d010b2000200110b8808080000f0b2001280200220128020021020240200128020422010d0041012103410021010c010b2001417f4c0d0141002d00fca3c680001a200141002802c8a3c68000118180808000002203450d020b20032002200110848e80800021022000200136020820002002360204200020013602000f0b10ae80808000000b4101200110b280808000000b02000b22002000410036020c2000200336020820002002360204200041b8a1c280003602000b040041000b21002001280214419aa2c280004105200141186a28020028020c118280808000000b8a0101017f23808080800041306b2201248080808000200120003602002001411c6a420137020020014101360214200141a0a2c280003602102001418c8180800036022c2001200141286a36021820012001360228200141046a200141106a108e84808000410141a8a2c2800041072001280208200128020c41002802d0a2c680001187808080000000000bb20201067f41022102024002402001280208220320012802102204470d000c010b024002400240024002400240200441016a2205450d0020032005490d01200128020421062001200536021002400240200620046a2d00000e020001080b200320056b4104490d07200441056a21072005417b4b0d0341002102200720034b0d040c060b200320056b4104490d06200441056a21072005417b4b0d0441012102200720034d0d05200720034190fbc08000109581808000000b417f20054190fbc08000109681808000000b200520034190fbc08000109581808000000b200520074190fbc08000109681808000000b200720034190fbc08000109581808000000b200520074190fbc08000109681808000000b20012007360210200620056a28000021070b20002007360204200020023602000b40002001ad4220862000ad84200235020842208620022802042201ad8410808080800002402002280200450d00200141002802c0a3c68000118080808000000b0b12002001ad4220862000ad841081808080000b9d0302047f027e23808080800041206b22052480808080004100210641002d00fca3c680001a024002404105410120031b220741002802c8a3c68000118180808000002208450d002002ad42208621090240024020030d00428080808010210a0c010b200820043600014280808080d000210a410121060b200820063a00000240024020092001ad84200a2008ad841082808080002209422088a722020d0041b8a1c28000210341b8a1c280002106410021010c010b41a489c08000210602402009a72203410171450d00200321010c010b20034101722101419489c0800021060b2005410036021c2005200136021820052002360214200520033602102005200636020c20052005410c6a1094848080002005280204210620052802002103200541186a20052802102005280214200528020c28020c1185808080000020034102460d01200841002802c0a3c68000118080808000002000200636020420002003360200200541206a2480808080000f0b4101200710b280808000000b41c8a1c28000412e2005410c6a41f8a1c280004188a2c28000108981808000000b08001083808080000b800303017f017e027f23808080800041306b22032480808080000240024002402002ad4220862001ad841084808080002204422088a72202450d00410021052003410036021c2003200236021420032004a722023602102003410136021c200320022002410172200241017122011b360218200341a489c08000419489c0800020011b36020c200341186a21060240024020022d00000e020001040b41002101410121050c030b200341206a2003410c6a10b28280800020032802202201450d01200341086a2003412c6a28020036020020032003290224370300410121050c020b2003420037021820032002360214200341b8a1c28000360210200341b8a1c2800036020c200341186a2106410021050b0b200620032802102003280214200328020c28020c11858080800000024020050d0041c8a1c28000412e2003410c6a41f8a1c280004188a2c28000108981808000000b20002001360200200020032903003702042000410c6a200341086a280200360200200341306a2480808080000b9e0203017f017e027f23808080800041206b2203248080808000024002402002ad4220862001ad841085808080002204422088a722050d0041b8a1c28000210241b8a1c280002101410021060c010b41a489c08000210102402004a72202410171450d00200221060c010b20024101722106419489c0800021010b2003410036021c2003200636021820032005360214200320023602102003200136020c20032003410c6a10bc84808000200341186a20032802102003280214200328020c28020c1185808080000002402003280200418180808078470d0041c8a1c28000412e2003410c6a41f8a1c280004188a2c28000108981808000000b20002003290200370200200041086a200341086a280200360200200341206a2480808080000ba30202017f017e23808080800041206b2206248080808000024002402002ad4220862001ad842004ad4220862003ad8420051086808080002207422088a722020d0041b8a1c28000210441b8a1c280002103410021010c010b41a489c08000210302402007a72204410171450d00200421010c010b20044101722101419489c0800021030b2006410036021c2006200136021820062002360214200620043602102006200336020c20062006410c6a10ba848080002006280204210320062802002104200641186a20062802102006280214200628020c28020c11858080800000024020044102470d0041c8a1c28000412e2006410c6a41f8a1c280004188a2c28000108981808000000b2000200336020420002004360200200641206a2480808080000b08001087808080000b4101017e024020011088808080002202422088a722010d002000410036020820004280808080103702000f0b20002001360208200020023e0204200020013602000b1c002001ad4220862000ad842003ad4220862002ad841089808080000b0800108a808080000b380020002002ad4220862001ad84108b80808000220229000837000820002002290000370000200241002802c0a3c68000118080808000000b5e0020002002ad4220862001ad84108c808080002202290000370000200041186a200241186a290000370000200041106a200241106a290000370000200041086a200241086a290000370000200241002802c0a3c68000118080808000000b380020002002ad4220862001ad84108d80808000220229000837000820002002290000370000200241002802c0a3c68000118080808000000b2e0020002002ad4220862001ad84108e808080002202290000370000200241002802c0a3c68000118080808000000b12002001ad4220862000ad84108f808080000b1c002001ad4220862000ad842003ad4220862002ad841090808080000bf70101037f23808080800041106b220324808080800020032001360200200341046a200210b9848080002003280204210420002003200335020c42208620032802082205ad841091808080002201290000370000200041206a200141206a2d00003a0000200041186a200141186a290000370000200041106a200141106a290000370000200041086a200141086a290000370000200141002802c0a3c680001180808080000002402004450d00200541002802c0a3c68000118080808000000b024020022802002200418080808078460d002000450d00200228020441002802c0a3c68000118080808000000b200341106a2480808080000b3a01017f23808080800041106b22022480808080002002200136020c20002002410c6a109280808000108684808000200241106a2480808080000bac0202017f017e23808080800041f0006b22052480808080002005200136020002400240200520022004ad4220862003ad841093808080002206422088a722010d002005420037020c200541b8a1c28000360208200541b8a1c280003602040c010b2005200136021c200520063e021820052001360214200541046a200541146a10bf808080000b200541d8006a41086a2201200541046a41086a2902003703002005200529020437035820054100360268200541146a200541d8006a10bb84808000200541e4006a200528025c2001280200200528025828020c11858080800000024020052d00144102470d0041c8a1c28000412e200541d8006a41f8a1c280004188a2c28000108981808000000b2000200541146a41c20010848e8080001a200541f0006a2480808080000b190020002002ad4220862001ad8420031094808080004101460be70101037f23808080800041106b220324808080800020032001360200200341046a200210b9848080002003280204210420002003200335020c42208620032802082205ad841095808080002201290000370000200041186a200141186a290000370000200041106a200141106a290000370000200041086a200141086a290000370000200141002802c0a3c680001180808080000002402004450d00200541002802c0a3c68000118080808000000b024020022802002200418080808078460d002000450d00200228020441002802c0a3c68000118080808000000b200341106a2480808080000b3a01017f23808080800041106b22022480808080002002200136020c20002002410c6a109680808000108784808000200241106a2480808080000bac0202017f017e23808080800041f0006b22052480808080002005200136020002400240200520022004ad4220862003ad841097808080002206422088a722010d002005420037020c200541b8a1c28000360208200541b8a1c280003602040c010b2005200136021c200520063e021820052001360214200541046a200541146a10bf808080000b200541d8006a41086a2201200541046a41086a2902003703002005200529020437035820054100360268200541146a200541d8006a10bd84808000200541e4006a200528025c2001280200200528025828020c11858080800000024020052d00144102470d0041c8a1c28000412e200541d8006a41f8a1c280004188a2c28000108981808000000b2000200541146a41c10010848e8080001a200541f0006a2480808080000b190020002002ad4220862001ad8420031098808080004101460be70101037f23808080800041106b220324808080800020032001360200200341046a200210b9848080002003280204210420002003200335020c42208620032802082205ad841099808080002201290000370000200041186a200141186a290000370000200041106a200141106a290000370000200041086a200141086a290000370000200141002802c0a3c680001180808080000002402004450d00200541002802c0a3c68000118080808000000b024020022802002200418080808078460d002000450d00200228020441002802c0a3c68000118080808000000b200341106a2480808080000b3a01017f23808080800041106b22022480808080002002200136020c20002002410c6a109a80808000108584808000200241106a2480808080000bac0202017f017e23808080800041f0006b22052480808080002005200136020002400240200520022004ad4220862003ad84109b808080002206422088a722010d002005420037020c200541b8a1c28000360208200541b8a1c280003602040c010b2005200136021c200520063e021820052001360214200541046a200541146a10bf808080000b200541d8006a41086a2201200541046a41086a2902003703002005200529020437035820054100360268200541146a200541d8006a10bd84808000200541e4006a200528025c2001280200200528025828020c11858080800000024020052d00144102470d0041c8a1c28000412e200541d8006a41f8a1c280004188a2c28000108981808000000b2000200541146a41c10010848e8080001a200541f0006a2480808080000b190020002002ad4220862001ad842003109c808080004101460bdd0203027f017e047f23808080800041306b220124808080800002400240200035020842208620002802042202ad84109d808080002203422088a722040d0020014200370210200141b8a1c2800036020c200141b8a1c280003602080c010b20012004360220200120033e021c20012004360218200141086a200141186a10bf808080000b200141186a41086a200141086a41086a2902002203370300200120012902083703184100210420014100360228200128021c210502402003a72206450d0020052d0000210720014101360228200741ff01712207450d004101410220074101461b21040b200141246a20052006200128021828020c1185808080000002402006450d0020044102460d0002402000280200450d00200241002802c0a3c68000118080808000000b200141306a24808080800020044100470f0b41c8a1c28000412e200141186a41f8a1c280004188a2c28000108981808000000bd60301077f23808080800041106b22022480808080000240024002400240024002402001280208220320012802102204470d00410121050c010b200441016a2206450d0120032006490d02200128020421072001200636021002400240024002400240200720046a2d000022084103710e0400020301000b20084102762108410021050c040b4101210520084104490d020c030b200241096a20083a000041012105200241013a000820022001360204200241003b010c0240200241046a2002410c6a410210b4848080000d0020022f010c2201410276210820014180024921050c030b0c020b200241096a20083a000041012105200241013a0008200220013602042002410036020c0240200241046a2002410c6a410410b4848080000d00200228020c220141027621082001418080044921050c020b0c010b200320066b4104490d00200441056a21052006417b4b0d03200520034b0d0420012005360210200720066a28000022084180808080044921050b2000200836020420002005360200200241106a2480808080000f0b417f20064190fbc08000109681808000000b200620034190fbc08000109581808000000b200620054190fbc08000109681808000000b200520034190fbc08000109581808000000bad0201037f20002d00042103200041003a0004024002400240024002400240024002402002450d00200341ff01710d010b41012103200028020022002802082204200028021022056b2002490d02200520026a22032005490d03200320044b0d042001200028020420056a200210848e8080001a200020033602100c010b2001200041056a2d00003a000041012103200028020022002802082204200028021022056b2002417f6a2202490d01200520026a22032005490d04200320044b0d05200141016a200028020420056a200210848e8080001a200020033602100b410021030b20030f0b200520034190fbc08000109681808000000b200320044190fbc08000109581808000000b200520034190fbc08000109681808000000b200320044190fbc08000109581808000000b950301037f0240200028020022022802002200413f4b0d00200041027421020240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a20023a00000f0b0240200041ffff004b0d002000410274410172210202402001280200200128020822006b41014b0d0020012000410210b182808000200128020821000b2001200041026a360208200128020420006a20023b00000f0b0240200041ffffffff034b0d002000410274410272210202402001280200200128020822006b41034b0d0020012000410410b182808000200128020821000b2001200041046a360208200128020420006a20023600000f0b02402001280200220320012802082200470d0020012000410110b18280800020012802002103200128020821000b2001280204220420006a41033a00002001200041016a2200360208200228020021020240200320006b41034b0d0020012000410410b18280800020012802042104200128020821000b2001200041046a360208200420006a20023600000b0e00200020012002109e808080000b25002000417f6a41ff01712002ad4220862001ad842004ad4220862003ad84109f808080000b5601027f23808080800041106b2200248080808000024010a080808000220141ff01714106490d0041f19fc2800041342000410f6a41a8a0c2800041a8a1c28000108981808000000b200041106a24808080800020010bbb0301057f23808080800041206b2202248080808000024002400240024002402001280200418080808078460d00024002402001280208220341056a2204450d002004417f4c0d044100210541002d00fca3c680001a200441002802c8a3c68000118180808000002206450d05200220063602102002200436020c0c010b20024100360214200242808080801037020c2002410c6a4100410110b18280800020022802102106200228021421050b200620056a41013a00002002200541016a36021420012802042104200220033602182002200241186a36021c2002411c6a2002410c6a10b5848080000240200228020c200228021422016b20034f0d002002410c6a2001200310b182808000200228021421010b200228021020016a2004200310848e8080001a2002200120036a3602140c010b41002d00fca3c680001a410141002802c8a3c68000118180808000002201450d03200220013602102002410136020c200141003a0000200241013602140b2000200229020c370200200041086a2002410c6a41086a280200360200200241206a2480808080000f0b10ae80808000000b4101200410b280808000000b4101410110b280808000000bec0101067f410221020240024002400240024002402001280208220320012802102204470d000c010b200441016a2205450d0120032005490d02200128020421062001200536021002400240200620046a2d00000e020001020b410021020c010b200320056b4104490d00200441056a21072005417b4b0d03200720034b0d0420012007360210200620056a2800002107410121020b20002007360204200020023602000f0b417f20054190fbc08000109681808000000b200520034190fbc08000109581808000000b200520074190fbc08000109681808000000b200720034190fbc08000109581808000000bef0101057f41022102024002400240024002402001280208220320012802102204460d00200441016a2205450d0120032005490d02200128020421062001200536021002400240200620046a2d00000e020001020b200041003a00000f0b200320056b41c100490d00200441c2006a2104200541be7f4b0d03200420034b0d042001200436021041012102200041016a200620056a41c10010848e8080001a0b200020023a00000f0b417f20054190fbc08000109681808000000b200520034190fbc08000109581808000000b200520044190fbc08000109681808000000b200420034190fbc08000109581808000000ba80301067f23808080800041106b2202248080808000024002400240024002400240024002402001280208220320012802102204460d00200441016a2205450d02200520034b0d032001280204210320012005360210024002400240200320046a2d00000e020102000b20004181808080783602000c030b20004180808080783602000c020b200241086a200110b38480800020022802080d0020012802082205200128021022036b200228020c2204490d000240024020040d00410121060c010b2004417f4c0d05200441002802c8a3c68000118180808000002206450d06200641002004108a8e8080001a0b200320046a22072003490d06200720054b0d072006200128020420036a200410848e8080002103200020043602002001200736021020002004ad4220862003ad843702040c010b20004181808080783602000b200241106a2480808080000f0b417f20054190fbc08000109681808000000b200520034190fbc08000109581808000000b10ae80808000000b4101200410b280808000000b200320074190fbc08000109681808000000b200720054190fbc08000109581808000000bda0201057f41022102024002400240024002402001280208220320012802102204460d00200441016a2205450d0120032005490d02200128020421062001200536021002400240200620046a2d00000e020001020b200041003a00000f0b200320056b41c000490d00200441c1006a2104200541bf7f4b0d03200420034b0d04200120043602102000200620056a2201290000370001200041096a200141086a290000370000200041116a200141106a290000370000200041196a200141186a290000370000200041216a200141206a290000370000200041296a200141286a290000370000200041316a200141306a290000370000200041396a200141386a290000370000410121020b200020023a00000f0b417f20054190fbc08000109681808000000b200520034190fbc08000109581808000000b200520044190fbc08000109681808000000b200420034190fbc08000109581808000000b0a00200010a1808080000b0a00200010a2808080000bb60202017f017e23808080800041306b2208248080808000024002402002ad4220862001ad842004ad4220862003ad842006ad4220862005ad84200710a3808080002209422088a722060d0020084200370210200841b8a1c2800036020c200841b8a1c280003602080c010b20082006360220200820093e021c20082006360218200841086a200841186a10bf808080000b200841186a41086a2205200841086a41086a29020037030020082008290208370318200841003602282008200841186a10ba848080002008280204210420082802002106200841246a200828021c2005280200200828021828020c11858080800000024020064102470d0041c8a1c28000412e200841186a41f8a1c280004188a2c28000108981808000000b2000200436020420002006360200200841306a2480808080000b26002001ad4220862000ad842003ad4220862002ad842005ad4220862004ad8410a4808080000b9a0303017f017e037f23808080800041206b2203248080808000200341086a20011088848080002000200329031820032903102204200328020822051b200210a5808080002202290000370000200041186a200241186a290000370000200041106a200241106a290000370000200041086a200241086a290000370000200241002802c0a3c680001180808080000002402005450d00200328020c450d002004a741002802c0a3c68000118080808000000b20012802042106024020012802082200450d002000410171210741002102024020004101460d002000417e7121052006210041002102034002402000280200450d00200041046a28020041002802c0a3c68000118080808000000b02402000410c6a280200450d00200041106a28020041002802c0a3c68000118080808000000b200041186a21002005200241026a2202470d000b0b2007450d0020062002410c6c6a2200280200450d00200028020441002802c0a3c68000118080808000000b02402001280200450d00200641002802c0a3c68000118080808000000b200341206a2480808080000b12002001ad4220862000ad8410a6808080000b12002001ad4220862000ad8410a7808080000bad0202017f017e23808080800041c0006b2203248080808000024002402002ad4220862001ad8410a8808080002204422088a722020d0020034200370214200341b8a1c28000360210200341b8a1c2800036020c0c010b20032002360230200320043e022c200320023602282003410c6a200341286a10bf808080000b200341286a41086a22022003410c6a41086a2902003703002003200329020c370328200341003602382003411c6a200341286a10bc84808000200341346a200328022c2002280200200328022828020c118580808000000240200328021c418180808078470d0041c8a1c28000412e200341286a41f8a1c280004188a2c28000108981808000000b2000200329021c370200200041086a2003411c6a41086a280200360200200341c0006a2480808080000b8103010c7f2380808080004180026b220224808080800020012802082203410774220441f8006e2105200128020022062107024020012802042208200128020c2209460d00200241f4016a220a41086a210b200621070340200120084180016a220c3602042008280270220d418080808078460d0120024180016a200841f00010848e8080001a200b200841fc006a280200360200200a20082902743702002002200d3602f001200241086a20024180016a10d0848080002007200241086a41f80010848e80800041f8006a2107200c2108200c2009470d000b0b200110cb8480800020062108024002402003450d00200621082004200541f8006c220c460d00024020040d00410821080c010b200c41002802c8a3c68000118180808000002208450d0120082006200c10848e8080001a200641002802c0a3c68000118080808000000b20002008360204200020053602002000200720066b41f8006e360208200110cc8480800020024180026a2480808080000f0b4108200c10b280808000000be70304077f017e057f017e200128020c220221032001280200220421050240200128020422062002460d002004210503402006220741386a21060240200728022c2208418080808078470d00200621030c020b200741106a29030021092007280224220a210302402007280228220b450d00200a200b410574220b6a210c200a210303402003280200450d01200341206a2103200b41606a220b0d000b200c21030b2007280220210b2007280218210c2007280204210d2007280200210e2007290330210f200520072903083703082005200f3703302005200836022c2005200a3602242005200c3602182005200d3602042005200e360200200541106a20093703002005200b41ffffff3f7136022020052003200a6b410576360228200541386a210520062002470d000b200221030b20012802082107200142888080808001370200200142808080808001370208200220036b41386e210b024020022003460d00200341306a210303400240200341706a280200450d00200341746a28020041002802c0a3c68000118080808000000b02402003417c6a280200450d00200328020041002802c0a3c68000118080808000000b200341386a2103200b417f6a220b0d000b0b200020043602042000200520046b41386e3602082000200741386c41386e3602000be704020a7f017e23808080800041c0006b220224808080800020012802082103200128020c220421052001280200220621070240200128020422082004460d002002411c6a210920062107024003402008220a280200220b418080808078460d01200241086a2205200a41146a2802003602002002200a29020c370300200a290218210c2002200a2802042208200a28020841386c6a36023c2002200b3602382002200836023420022008360230200241106a200241306a10c784808000200241106a41186a2208200c370300200941086a200528020036020020092002290300370200200741186a2008290300370200200741106a200241106a41106a290300370200200741086a200241106a41086a29030037020020072002290310370200200741206a2107200a41206a22082004470d000b0b200a41206a21050b20014284808080c00037020020014280808080c000370208024020042005460d00200420056b41057621044100210b034002402005200b4105746a22092802082208450d00200928020441306a210a03400240200a41706a280200450d00200a41746a28020041002802c0a3c68000118080808000000b0240200a417c6a280200450d00200a28020041002802c0a3c68000118080808000000b200a41386a210a2008417f6a22080d000b0b02402009280200450d00200928020441002802c0a3c68000118080808000000b0240200928020c450d002009410c6a28020441002802c0a3c68000118080808000000b200b41016a220b2004470d000b0b200020063602042000200341ffffff3f713602002000200720066b410576360208200241c0006a2480808080000bc503010b7f23808080800041e0016b220224808080800020012802082103200128020c220421052001280200220621070240200128020422082004460d00200241146a2109200241106a210a4100210502400340200620056a2107200820056a220b280200220c4102460d012009200b41046a41e40010848e8080001a2002200c3602102002200736020c20022006360208200241f8006a200a10d1848080002007200241f8006a41e80010848e8080001a200541e8006a2105200b41e8006a2004470d000b200620056a21070b200b41e8006a21050b200142888080808001370200200142808080808001370208200420056b41e8006e210b024020042005460d00034002402005280200450d00200541046a280200450d00200541086a28020041002802c0a3c68000118080808000000b0240200541c8006a280200450d00200541cc006a28020041002802c0a3c68000118080808000000b0240200541d4006a280200450d00200541d8006a28020041002802c0a3c68000118080808000000b200541e8006a2105200b417f6a220b0d000b0b200020063602042000200720066b41e8006e3602082000200341e8006c41e8006e360200200241e0016a2480808080000be00301067f024020002802082201450d00200028020421024100210303400240200220034107746a22042802502200418080808078460d00200441d0006a21050240200441d8006a2802002206450d00200441d4006a2802002100034002402000280200450d00200041046a280200450d00200041086a28020041002802c0a3c68000118080808000000b0240200041c8006a280200450d00200041cc006a28020041002802c0a3c68000118080808000000b0240200041d4006a280200450d00200041d8006a28020041002802c0a3c68000118080808000000b200041e8006a21002006417f6a22060d000b200528020021000b2000450d00200528020441002802c0a3c68000118080808000000b0240200441ec006a2802002206450d00200441e8006a28020041306a210003400240200041706a280200450d00200041746a28020041002802c0a3c68000118080808000000b02402000417c6a280200450d00200028020041002802c0a3c68000118080808000000b200041386a21002006417f6a22060d000b0b02402004280264450d00200441e4006a28020441002802c0a3c68000118080808000000b02402004280270450d00200441f0006a28020441002802c0a3c68000118080808000000b200341016a22032001470d000b0b0bfe0301067f200028020c210120004280808080800137020820002802042102200042888080808001370200024020012002460d00200120026b41077621034100210403400240200220044107746a22052802502200418080808078460d000240200541d0006a22062802082201450d0020062802042100034002402000280200450d00200041046a280200450d00200041086a28020041002802c0a3c68000118080808000000b0240200041c8006a280200450d00200041cc006a28020041002802c0a3c68000118080808000000b0240200041d4006a280200450d00200041d8006a28020041002802c0a3c68000118080808000000b200041e8006a21002001417f6a22010d000b200628020021000b2000450d00200628020441002802c0a3c68000118080808000000b0240200541ec006a2802002201450d00200541e8006a28020041306a210003400240200041706a280200450d00200041746a28020041002802c0a3c68000118080808000000b02402000417c6a280200450d00200028020041002802c0a3c68000118080808000000b200041386a21002001417f6a22010d000b0b0240200541e4006a2200280200450d00200028020441002802c0a3c68000118080808000000b02402005280270450d00200541f0006a28020441002802c0a3c68000118080808000000b200441016a22042003470d000b0b0b820401077f0240200028020c220120002802042202460d00200120026b41077621034100210403400240200220044107746a22052802502201418080808078460d000240200541d0006a22062802082207450d0020062802042101034002402001280200450d00200141046a280200450d00200141086a28020041002802c0a3c68000118080808000000b0240200141c8006a280200450d00200141cc006a28020041002802c0a3c68000118080808000000b0240200141d4006a280200450d00200141d8006a28020041002802c0a3c68000118080808000000b200141e8006a21012007417f6a22070d000b200628020021010b2001450d00200628020441002802c0a3c68000118080808000000b0240200541ec006a2802002207450d00200541e8006a28020041306a210103400240200141706a280200450d00200141746a28020041002802c0a3c68000118080808000000b02402001417c6a280200450d00200128020041002802c0a3c68000118080808000000b200141386a21012007417f6a22070d000b0b0240200541e4006a2201280200450d00200128020441002802c0a3c68000118080808000000b02402005280270450d00200541f0006a28020441002802c0a3c68000118080808000000b200441016a22042003470d000b0b02402000280208450d00200028020041002802c0a3c68000118080808000000b0baa070b057f017e017f027e017f027e0f7f017e017f017e037f23808080800041c0006b2202248080808000200141cc006a2802002103200128024821044180808080782105024020012802502206418080808078460d00200141dc006a29020021072002200141d4006a2802002208200141d8006a28020041e8006c6a36023c200220063602382002200836023420022008360230200241186a200241306a10c984808000200241106a20073703002002200229021c370308200228021821050b200141206a2903002109200141086a290300210a200141286a280200210b2001290318210c2001290300210d2001280264210e2001280210210f200141e8006a280200221021110240200141ec006a2802002206450d002010200641386c22126a2111200241306a41046a2108200241186a41046a21134100211402400340201020146a2206412c6a22152802002216418080808078460d01200641306a22172903002107200641046a22182802002119200641186a221a280200211b200641086a221c290300211d200641106a221e290300211f20062802002120200241186a41086a200641246a222129020037030020022006411c6a2222290200370318200841086a201341086a28020036020020082013290200370200201e201f370300201c201d370300201a201b3602002018201936020020062020360200202220022902303702002021200241306a41086a29020037020020152016360200201720073702002012201441386a2214470d000c020b0b0240201241486a2014460d00200641e8006a2108201220146b41486a41386e211403400240200841706a280200450d00200841746a28020041002802c0a3c68000118080808000000b02402008417c6a280200450d00200828020041002802c0a3c68000118080808000000b200841386a21082014417f6a22140d000b0b200621110b2000200d3703002000200c37031820002005360250200020043602482000200f3602102000200a370308200041206a200937030020002001290330370330200041e8006a201036020020002001290370370370200041cc006a2003360200200041d4006a2002290308370200200041286a200b360200200020012d007c3a007c200041386a200141386a290300370300200041ec006a201120106b41386e3602002000200e41386c41386e360264200041f8006a200141f8006a280200360200200041dc006a200241086a41086a290300370200200041c0006a200141c0006a280200360200200241c0006a2480808080000b870c03067f107e137f2380808080004190046b22032480808080000240024002400240200241726a0e020102000b2000418780808078360200200141e8016a10ca84808000024020012802e801450d00200141ec016a28020041002802c0a3c68000118080808000000b0240200141d8016a280200450d00200141dc016a28020041002802c0a3c68000118080808000000b200141f8016a28020021040240200141fc016a2802002205450d004100210603400240200420064105746a22072802082208450d00200728020441306a210203400240200241706a280200450d00200241746a28020041002802c0a3c68000118080808000000b02402002417c6a280200450d00200228020041002802c0a3c68000118080808000000b200241386a21022008417f6a22080d000b0b02402007280200450d00200728020441002802c0a3c68000118080808000000b0240200728020c450d002007410c6a28020441002802c0a3c68000118080808000000b200641016a22062005470d000b0b20012802f401450d02200441002802c0a3c68000118080808000000c020b2003200141800210848e80800022024194036a200210cf84808000200241868080807836029003200241edcad18b063602f003200020024190036a41e40010848e8080001a0c010b200141c8016a2903002109200141b0016a290300210a20014198016a290300210b20014180016a290300210c200141d0006a290300210d200141386a290300210e200141206a290300210f200141086a290300211020012903c001211120012903a80121122001290390012113200129037821142001290348211520012903302116200129031821172001290300211820012802fc01211920012802f801211a20012802f401211b20012d00e401211c20012802e001211d20012802dc01211e20012802d801211f20012802d001212020012802b801212120012802a001212220012802880121232001280258212420012802402125200128022821262001280210212720012802e8012128200320012802ec01220720012802f001220841077422046a222936029802200320283602940220032007360290022003200736028c022007210202402008450d00200341086a210541002108200341fc006a222a41086a212b024002400340200720086a220241f0006a2802002201418080808078460d012005200241f00010848e808000210620032007360200202b200241fc006a280200360200202a200241f4006a290200370200200320013602782003200236020420034190036a200610cd84808000200220034190036a41800110848e8080001a200420084180016a2208470d000b200720086a21020c010b20024180016a21290b20032029360290020b2003418c026a10cb848080002003202841ffffff0f713602800220032007360284022003200220076b410776360288022003418c026a10cc84808000201e21020240201d450d00201e201d41386c6a2107201d41386c2108201e210203402002280200450d01200241386a2102200841486a22080d000b200721020b200341d0006a2009370300200341386a200a370300200341206a200b370300200341e4006a201e360200200341d8006a2020360200200341c0006a2021360200200341286a2022360200200341e8006a2002201e6b41386e360200200320113703482003201237033020032013370318200320143703002003201c3a006c200320233602102003200c3703082003201f41386c41386e3602602003201b3602b0022003201a3602ac022003201a3602a8022003201a20194105746a3602b4022003419c026a200341a8026a10c884808000200341b8026a41386a200d370300200341b8026a41206a200e370300200341b8026a41c0006a2024360200200341b8026a41286a2025360200200320153703e802200320163703d0022003200f3703c002200320173703b802200320263602c8022003410036028c03200341003602840320034190036a20034180026a20032018201020272003419c026a200341b8026a20034184036a10e081808000200341edcad18b063602f003200020034190036a41e40010848e8080001a0b20034190046a2480808080000be40405027f017e037f017e037f23808080800041d0006b2202248080808000200220012802e8013602202002200141ec016a280200220336021c2002200336021820022003200141f0016a2802004107746a3602242002410c6a200241186a10c684808000200141e8006a2903002104200141e4016a2d00002105200141d8016a2802002106200141f0006a280200210720012903602108200141dc016a280200220921030240200141e0016a280200220a450d002009200a41386c220a6a210b2009210303402003280200450d01200341386a2103200a41486a220a0d000b200b21030b200241c4006a2009360200200241c8006a200320096b41386e36020020022008370328200220053a004c20022007360238200220043703302002200641386c41386e36024020002002410c6a200241286a2001290300200141086a290300200128021010ce81808000200141f8016a28020021050240200141fc016a2802002206450d004100210003400240200520004105746a2209280208220a450d00200928020441306a210303400240200341706a280200450d00200341746a28020041002802c0a3c68000118080808000000b02402003417c6a280200450d00200328020041002802c0a3c68000118080808000000b200341386a2103200a417f6a220a0d000b0b02402009280200450d00200928020441002802c0a3c68000118080808000000b0240200928020c450d002009410c6a28020441002802c0a3c68000118080808000000b200041016a22002006470d000b0b024020012802f401450d00200541002802c0a3c68000118080808000000b200241d0006a2480808080000bb2070b057f017e017f027e017f027e0f7f017e017f017e037f23808080800041c0006b2202248080808000200141cc006a2802002103200128024821044180808080782105024020012802502206418080808078460d00200141dc006a29020021072002200141d4006a2802002208200141d8006a28020041e8006c6a36023c200220063602382002200836023420022008360230200241186a200241306a10c984808000200241106a20073703002002200229021c370308200228021821050b200141206a2903002109200141086a290300210a200141286a280200210b2001290318210c2001290300210d2001280264210e2001280210210f200141e8006a280200221021110240200141ec006a2802002206450d002010200641386c22126a2111200241306a41046a2108200241186a41046a21134100211402400340201020146a2206412c6a22152802002216418080808078460d01200641306a22172903002107200641046a22182802002119200641186a221a280200211b200641086a221c290300211d200641106a221e290300211f20062802002120200241186a41086a200641246a222129020037030020022006411c6a2222290200370318200841086a201341086a28020036020020082013290200370200201e201f370300201c201d370300201a201b3602002018201936020020062020360200202220022902303702002021200241306a41086a29020037020020152016360200201720073702002012201441386a2214470d000c020b0b0240201241486a2014460d00200641e8006a2108201220146b41486a41386e211403400240200841706a280200450d00200841746a28020041002802c0a3c68000118080808000000b02402008417c6a280200450d00200828020041002802c0a3c68000118080808000000b200841386a21082014417f6a22140d000b0b200621110b2000200c3703182000200d37030020002005360250200020043602482000200f36021020002001290330370330200041206a20093703002000200a370308200041e8006a2010360200200041cc006a2003360200200041d4006a2002290308370200200020012d007c3a0070200041286a200b360200200041386a200141386a290300370300200041ec006a201120106b41386e3602002000200e41386c41386e360264200041dc006a200241086a41086a290300370200200041c0006a200141c0006a28020036020002402001280270450d00200141f4006a28020041002802c0a3c68000118080808000000b200241c0006a2480808080000bb80205067f017e027f017e027f200141c4006a280200210220012d00602103200128024021040240024020012802000d00200141186a280200210520012802082106410021070c010b200141306a2903002108200141086a2802002106200141386a2802002109200141206a280200210a2001290328210b200128021c210c200128021821052001280204210d410121070b2000200b370328200020033a006020002004360240200020093602382000200a3602202000200c36021c20002005360218200020063602082000200d36020420002007360200200041306a20083703002000200129034837034820002001290254370254200041c4006a2002360200200020012903103703102000200128020c36020c200041d0006a200141d0006a280200360200200041dc006a200141dc006a2802003602000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010e1848080002004280208200428020c220541386c6a2206420437022c200642123702242006418ba5c2800036022020064100360218200641bc81808000360210200642c9a784c5fedbb0fa25370308200642efa8a2ace59194fdb37f37030020042802042107200428020821080240200128020822062001280200470d002001200610e084808000200128020821060b2001280204200641246c6a220641003a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010e1848080002004280208200428020c220541386c6a2206420437022c20064202370224200641e0a4c2800036022020064100360218200641c880808000360210200642e6ed8d82cc91adcb05370308200642dbd791d5c2919eaecd0037030020042802042107200428020821080240200128020822062001280200470d002001200610e084808000200128020821060b2001280204200641246c6a220641073a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bdc0303037f017e027f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010e18480800020042802182005280200220641386c6a2205420437022c20054211370224200541b0a4c2800036022020054100360218200541bd81808000360210200542d886fac2c186f9c46f3703082005429cfcf2b9cfebc9bfa37f370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610e184808000200428021021060b200428020c200641386c6a2205420437022c20054207370224200541c1a4c2800036022020054100360218200541be81808000360210200542b891b68c98adebcf61370308200542e7b0a091f3ed9c85c50037030020042802082108200428020c21090240200128020822052001280200470d002001200510e084808000200128020821050b2001280204200541246c6a220541053a00202005200336021c200520023602182005410036021420054280808080c00037020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000bbd0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010e1848080002004280208200428020c220541386c6a2206420437022c2006420b370224200641e7a4c2800036022020064100360218200641bf81808000360210200642878ff9fb82dfe1a82a370308200642949ecee4d5a9a0866437030020042802042107200428020821080240200128020822062001280200470d002001200610e084808000200128020821060b2001280204200641246c6a220641033a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010e1848080002004280208200428020c220541386c6a2206420437022c20064207370224200641c1a4c2800036022020064100360218200641be81808000360210200642b891b68c98adebcf61370308200642e7b0a091f3ed9c85c50037030020042802042107200428020821080240200128020822062001280200470d002001200610e084808000200128020821060b2001280204200641246c6a220641003a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bbf0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010e1848080002004280208200428020c220541386c6a2206420437022c2006420a370224200641f2a4c2800036022020064100360218200641c0818080003602102006428d999faad5ccf2dbca00370308200642a7cde9f3c9fe9ebfe30037030020042802042107200428020821080240200128020822062001280200470d002001200610e084808000200128020821060b2001280204200641246c6a220641073a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bbd0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010e1848080002004280208200428020c220541386c6a2206420437022c200642123702242006419da5c2800036022020064100360218200641c18180800036021020064283e8c09cb8d3f4c01f370308200642dcffb3b186d2b1f50637030020042802042107200428020821080240200128020822062001280200470d002001200610e084808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bdc0303037f017e027f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010e18480800020042802182005280200220641386c6a2205420437022c20054211370224200541b0a4c2800036022020054100360218200541bd81808000360210200542d886fac2c186f9c46f3703082005429cfcf2b9cfebc9bfa37f370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610e184808000200428021021060b200428020c200641386c6a2205420437022c20054207370224200541c1a4c2800036022020054100360218200541be81808000360210200542b891b68c98adebcf61370308200542e7b0a091f3ed9c85c50037030020042802082108200428020c21090240200128020822052001280200470d002001200510e084808000200128020821050b2001280204200541246c6a220541063a00202005200336021c200520023602182005410036021420054280808080c00037020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010e1848080002004280208200428020c220541386c6a2206420437022c2006420f370224200641fca4c2800036022020064100360218200641c281808000360210200642f68af3a9c7b285af3c37030820064296b2f4978ff1ee81bf7f37030020042802042107200428020821080240200128020822062001280200470d002001200610e084808000200128020821060b2001280204200641246c6a220641083a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010e1848080002004280208200428020c220541386c6a2206420437022c20064202370224200641e0a4c2800036022020064100360218200641c880808000360210200642e6ed8d82cc91adcb05370308200642dbd791d5c2919eaecd0037030020042802042107200428020821080240200128020822062001280200470d002001200610e084808000200128020821060b2001280204200641246c6a220641023a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bbf0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010e1848080002004280208200428020c220541386c6a2206420437022c20064212370224200641baa6c2800036022020064100360218200641c381808000360210200642c69182ab97bfe490ac7f37030820064286a3ebac9a8ef1cfee0037030020042802042107200428020821080240200128020822062001280200470d002001200610e084808000200128020821060b2001280204200641246c6a220641093a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bdc0303037f017e027f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010e18480800020042802182005280200220641386c6a2205420437022c20054211370224200541b0a4c2800036022020054100360218200541bd81808000360210200542d886fac2c186f9c46f3703082005429cfcf2b9cfebc9bfa37f370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610e184808000200428021021060b200428020c200641386c6a2205420437022c20054207370224200541c1a4c2800036022020054100360218200541be81808000360210200542b891b68c98adebcf61370308200542e7b0a091f3ed9c85c50037030020042802082108200428020c21090240200128020822052001280200470d002001200510e084808000200128020821050b2001280204200541246c6a220541043a00202005200336021c200520023602182005410036021420054280808080c00037020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141146c2104200141e7cc99334941027421050240024020030d00200241003602180c010b200241043602182002200341146c36021c200220002802043602140b200241086a20052004200241146a10de84808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141246c2104200141e4f1b81c4941027421050240024020030d00200241003602180c010b200241043602182002200341246c36021c200220002802043602140b200241086a20052004200241146a10de84808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a10de84808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bf00101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b2202410c6c2104200241abd5aad5004941027421050240024020010d00200341003602180c010b2003410436021820032001410c6c36021c200320002802043602140b200341086a20052004200341146a10de84808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241246c2104200241e4f1b81c4941027421050240024020010d00200341003602180c010b200341043602182003200141246c36021c200320002802043602140b200341086a20052004200341146a10de84808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241386c210420024193c9a4124941037421050240024020010d00200341003602180c010b200341083602182003200141386c36021c200320002802043602140b200341086a20052004200341146a10de84808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b22024105742104200241808080204941037421050240024020010d00200341003602180c010b200341083602182003200141057436021c200320002802043602140b200341086a20052004200341146a10de84808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000b810201037f23808080800041206b22032480808080000240024002400240200241046a22040d00410121050c010b2004417f4c0d0141002d00fca3c680001a200441002802c8a3c68000118180808000002205450d020b20034100360214200320053602102003200436020c200320023602182003200341186a36021c2003411c6a2003410c6a10fb848080000240200328020c200328021422046b20024f0d002003410c6a2004200210b182808000200328021421040b200328021020046a2001200210848e8080001a200041086a200420026a3602002000200329020c370200200341206a2480808080000f0b10ae80808000000b4101200410b280808000000b70002000428ca3c7fa85a49cf2a17f37030820004280808080c00037033820004280808080c000370350200041c481808000360218200041023a0000200041106a42e0f4e1e1b7dafaaf8d7f370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6f00200042dbd791d5c2919eaecd0037030820004280808080c00037033820004280808080c000370350200041c880808000360218200041023a0000200041106a42e6ed8d82cc91adcb05370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6f00200042e7b0a091f3ed9c85c50037030820004280808080c00037033820004280808080c000370350200041be81808000360218200041023a0000200041106a42b891b68c98adebcf61370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b220641246e2207200128020822014101764f0d01410021082002410036020c20024280808080c00037020441042109024020052004460d00200241046a4100200710e38480800020022802082109200228020c21080b2009200841246c6a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b41246e2107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b220641386e2207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710e48480800020022802082109200228020c21080b2009200841386c6a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b41386e2107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b22064105762207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710e58480800020022802082109200228020c21080b200920084105746a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b4105762107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bf80701047f23808080800041206b220224808080800020024100360214200242808080801037020c02400240024002400240024020012802000e050001020304000b2001410c6a280200210320012802082104200128020421052002410c6a4100410110b1828080002002280210200228021422016a41063a00002002200141016a22013602140240200228020c20016b41034b0d002002410c6a2001410410b182808000200228021421010b200228021020016a20052800003600002002200141046a360214200220033602182002200241186a36021c2002411c6a2002410c6a10fb848080000240200228020c200228021422016b20034f0d002002410c6a2001200310b182808000200228021421010b200228021020016a2004200310848e8080001a200120036a21010c040b2001410c6a280200210320012802082104200128020421052002410c6a4100410110b1828080002002280210200228021422016a41043a00002002200141016a22013602140240200228020c20016b41034b0d002002410c6a2001410410b182808000200228021421010b200228021020016a20052800003600002002200141046a360214200220033602182002200241186a36021c2002411c6a2002410c6a10fb848080000240200228020c200228021422016b20034f0d002002410c6a2001200310b182808000200228021421010b200228021020016a2004200310848e8080001a200120036a21010c030b2001410c6a280200210320012802082104200128020421052002410c6a4100410110b1828080002002280210200228021422016a41053a00002002200141016a22013602140240200228020c20016b41034b0d002002410c6a2001410410b182808000200228021421010b200228021020016a20052800003600002002200141046a360214200220033602182002200241186a36021c2002411c6a2002410c6a10fb848080000240200228020c200228021422016b20034f0d002002410c6a2001200310b182808000200228021421010b200228021020016a2004200310848e8080001a200120036a21010c020b200141086a2802002103200128020421042002410c6a4100410110b1828080002002280210200228021422016a41003a00002002200141016a360214200220033602182002200241186a36021c2002411c6a2002410c6a10fb848080000240200228020c200228021422016b20034f0d002002410c6a2001200310b182808000200228021421010b200228021020016a2004200310848e8080001a200120036a21010c010b2002410c6a4100410110b1828080002002280210200228021422016a41083a0000200141016a21010b2000200229020c370200200041086a2001360200200241206a2480808080000bbc0402037f017e23808080800041c0006b2201248080808000200141186a41cca6c28000410a41d6a6c28000411b10d682808000200141146a410036020020014280808080c00037020c2001410036023820014280808080c000370230200141246a200141306a41f1a6c28000410a10d984808000200141306a200141246a41fba6c28000410910dd84808000200141246a200141306a4184a7c28000410410d484808000200141306a200141246a4188a7c28000410510d6848080000240200128023822022001280230470d00200141306a200210e084808000200128023821020b2001280234200241246c6a220241083a00202002411936021c2002418da7c2800036021820024204370210200242003702082002428080808080013702002001280238210320012802342102200120012802303602382001200236023020012002200341246c6a41246a36023c20012002360234200141246a200141306a10ea84808000024020012802182202418080808078470d0041afa3c28000411141a0a4c2800010a181808000000b2001411c6a2902002104200142888080808001370230200142808080808001370238200041c4006a200141306a10ec848080002001413b6a200141246a41086a2802003600002000413c6a200437020020002002360238200041013a000020002001410c6a2202290200370250200041d8006a200241086a2802003602002001200129022437003320002001290030370001200041086a200141376a290000370000200141c0006a2480808080000bdd0701047f23808080800041206b2202248080808000024002400240024002400240024020002d0000220320012d00002204470d000240024002400240024020030e04000102030b0b20030d0a20002800012001280001460d0341a6a7c2800041144100280280a3c68000118480808000000c080b20034101470d09024020002800012001280001460d0041a6a7c2800041144100280280a3c68000118480808000000c070b0240200041106a2802002203200141106a280200470d002000410c6a2802002001410c6a280200200310888e808000450d0a0b41a6a7c2800041144100280280a3c68000118480808000000c060b20034102470d08024020002800012001280001460d0041a6a7c2800041144100280280a3c68000118480808000000c050b0240200041106a2802002203200141106a280200470d002000410c6a2802002001410c6a280200200310888e808000450d090b41a6a7c2800041144100280280a3c68000118480808000000c040b20034103470d07200041086a280200210302402000410c6a28020022052001410c6a280200470d002003200141086a280200200510888e808000450d080b41a6a7c2800041144100280280a3c68000118480808000000c020b0240200041106a2802002203200141106a280200470d002000410c6a2802002001410c6a280200200310888e808000450d070b41a6a7c2800041144100280280a3c68000118480808000000c040b41a6a7c2800041144100280280a3c6800011848080800000024020030e050403020005040b2000410c6a2802002105200041086a28020021030b200241186a200536020020022003360214410321030c030b2002200041016a36021420022000410c6a290200370218410221030c020b410121032002200041016a36021420022000410c6a2902003702180c010b2002200041016a36021420022000410c6a290200370218410021030b20022003360210200241046a200241106a10ed8480800020022802082203200228020c41002802f8a2c680001184808080000002402002280204450d00200341002802c0a3c68000118080808000000b0240024002400240024020040e050001020304000b2002200141016a36021420022001410c6a2902003702180c030b2002200141016a36021420022001410c6a2902003702180c020b2002200141016a36021420022001410c6a2902003702180c010b2002200141086a2902003702140b20022004360210200241046a200241106a10ed8480800020022802082203200228020c41002802f8a2c68000118480808000002002280204450d00200341002802c0a3c68000118080808000000b200241206a2480808080000bd40403057f017e037f23808080800041d0006b2201248080808000200141286a41baa7c28000410641d6a6c28000411b41cca6c28000410010d882808000200141206a420437020020014200370218200142808080808001370210200142888080808001370240200142808080808001370248200141106a200141c0006a10ec84808000200141086a2202200141246a2802003602002001200129021c370300200128021021032001280214210420012802182105200129022c21062001280228210720014100360218200142808080808001370210200141106a410010e1848080002001280214200128021841386c6a2208420437022c2008420f370224200841cca4c280003602202008410436021c200841c8a4c28000360218200841c581808000360210200842dfbeb7dcc08ee1ef70370308200842d1c6e9d6b09296e819370300200128021821092001280214210820012001280210360248200120083602402001200836024420012008200941386c6a41386a36024c200141346a200141c0006a10eb8480800002402007418080808078470d0041afa3c28000411141a0a4c2800010a181808000000b2001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10ec848080002001411b6a200141346a41086a2802003600002000413c6a200637020020002007360238200041003a000020002001290300370350200041d8006a20022802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000b7600200042dbd791d5c2919eaecd0037030820004280808080c00037033820004280808080c00037035020004104360220200041c880808000360218200041033a0000200041106a42e6ed8d82cc91adcb05370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b1a002001280214200141186a280200200028020010d9808080000bbd0201027f23808080800041106b22022480808080002002410036020c02400240024002402001418001490d002001418010490d012001418080044f0d0220022001413f71418001723a000e20022001410c7641e001723a000c20022001410676413f71418001723a000d410321010c030b200220013a000c410121010c020b20022001413f71418001723a000d2002200141067641c001723a000c410221010c010b20022001413f71418001723a000f20022001410676413f71418001723a000e20022001410c76413f71418001723a000d2002200141127641077141f001723a000c410421010b02402000280200200028020822036b20014f0d00200020032001109c85808000200028020821030b200028020420036a2002410c6a200110848e8080001a2000200320016a360208200241106a24808080800041000b1200200041c0a7c28000200110d9808080000b220002402000280200450d00200028020441002802c0a3c68000118080808000000b0b02000b040041010bda0101017f23808080800041306b2202248080808000200242808080801037020020024100360208200241186a420137020020024101360210200241f0a7c2800036020c200241c68180800036022820022001412c6a36022c2002200241246a36021420022002412c6a360224200241c0a7c280002002410c6a10d9808080001a2001280220200141246a280200200141286a2802002002280204200228020841002802d0a2c680001187808080000002402002280200450d00200228020441002802c0a3c68000118080808000000b200241306a2480808080000b02000b7c01017f23808080800041106b2201248080808000200142888080808001370200200142808080808001370208200041c4006a200110c182808000200041c0006a410036020020004280808080c000370338200041d8006a410036020020004280808080c00037035020004185043b0100200141106a2480808080000b950301037f0240200028020022022802002200413f4b0d00200041027421020240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a20023a00000f0b0240200041ffff004b0d002000410274410172210202402001280200200128020822006b41014b0d0020012000410210b182808000200128020821000b2001200041026a360208200128020420006a20023b00000f0b0240200041ffffffff034b0d002000410274410272210202402001280200200128020822006b41034b0d0020012000410410b182808000200128020821000b2001200041046a360208200128020420006a20023600000f0b02402001280200220320012802082200470d0020012000410110b18280800020012802002103200128020821000b2001280204220420006a41033a00002001200041016a2200360208200228020021020240200320006b41034b0d0020012000410410b18280800020012802042104200128020821000b2001200041046a360208200420006a20023600000b7601017f20014180feff077141087621020240024020014101710d002002411874411875410274220241d0afc280006a2101200241a4afc280006a21020c010b200241187441187541027422024188b0c280006a2101200241fcafc280006a21020b20002001280200360204200020022802003602000ba90303027f027e037f200141106a2103200241146a2802002104200229030021052001290300210602402001280210200141186a28020022076b200241186a28020022084f0d0020032007200810e284808000200128021821070b200141146a2802002007410c6c6a20042008410c6c10848e8080001a2001200720086a3602182002410036021820002003290300370310200041186a200341086a2802003602002001411c6a2103200241206a28020021090240200128021c200141246a28020022076b200241246a28020022084f0d0020032007200810e284808000200128022421070b200141206a2802002007410c6c6a20092008410c6c10848e8080001a2001200720086a360224200241003602242000427f200620057c220520052006541b3703002000200329020037021c200041246a200341086a280200360200200020012d002841004720022d0028410047713a00282000200129030822062002290308220520062005541b37030802402002280210450d00200441002802c0a3c68000118080808000000b0240200228021c450d00200941002802c0a3c68000118080808000000b0be60d04057f017e037f017e23808080800041d0006b2201248080808000200141306a4181adc2800041124193adc2800041204188a8c28000410010d882808000200141286a420437020020014200370220200142808080808001370218200142888080808001370240200142808080808001370248200141186a200141c0006a10ec84808000200141086a41086a2001412c6a2802003602002001200129022437030820012802182102200128021c2103200128022021042001280230210520012902342106200141186a41086a2207410036020020014280808080c000370218200141186a410010e084808000200128021c200728020041246c6a220841003a00202008410436021c200841b3adc280003602182008420437021020084200370208200842808080808001370200200141c0006a41086a2209200728020041016a220836020020012001290218220a37034002402008200aa7470d00200141c0006a200810e084808000200128024821080b2001280244200841246c6a220841013a00202008410736021c200841b7adc2800036021820084204370210200842003702082008428080808080013702002007200928020041016a220836020020012001290340220a37031802402008200aa7470d00200141186a200810e084808000200128022021080b200128021c200841246c6a220841023a00202008410636021c200841beadc280003602182008420437021020084200370208200842808080808001370200200141c0006a41086a2207200141186a41086a220928020041016a220836020020012001290318220a37034002402008200aa7470d00200141c0006a200810e084808000200128024821080b2001280244200841246c6a220841033a00202008410536021c200841c4adc2800036021820084204370210200842003702082008428080808080013702002009200728020041016a220836020020012001290340220a37031802402008200aa7470d00200141186a200810e084808000200128022021080b200128021c200841246c6a220841043a00202008410836021c200841c9adc280003602182008420437021020084200370208200842808080808001370200200141c0006a41086a2207200141186a41086a220928020041016a220836020020012001290318220a37034002402008200aa7470d00200141c0006a200810e084808000200128024821080b2001280244200841246c6a220841053a00202008411136021c200841d1adc2800036021820084204370210200842003702082008428080808080013702002009200728020041016a220836020020012001290340220a37031802402008200aa7470d00200141186a200810e084808000200128022021080b200128021c200841246c6a220841063a00202008411136021c200841e2adc280003602182008420437021020084200370208200842808080808001370200200141c0006a41086a2207200141186a41086a220928020041016a36020020012001290318370340200141186a200141c0006a41f3adc28000410610d3848080000240200128022022082001280218470d00200141186a200810e084808000200128022021080b200128021c200841246c6a220841083a00202008410c36021c200841f9adc2800036021820084204370210200842003702082008428080808080013702002007200928020041016a220836020020012001290218220a37034002402008200aa7470d00200141c0006a200810e084808000200128024821080b2001280244200841246c6a220841093a00202008411336021c20084185aec280003602182008420437021020084200370208200842808080808001370200200141186a41086a200141c0006a41086a28020041016a220836020020012001290340220a37031802402008200aa7470d00200141186a200810e084808000200128022021080b200128021c200841246c6a2208410a3a00202008410936021c20084198aec28000360218200842043702102008420037020820084280808080800137020020012802202107200128021c2108200120012802183602202001200836021820012008200741246c6a41246a3602242001200836021c200141c0006a200141186a10ea8480800002402005418080808078470d0041afa3c28000411141a0a4c2800010a181808000000b20012002360220200120033602182001200336021c2001200320044105746a360224200041c4006a200141186a10ec84808000200141236a200141c0006a41086a2802003600002000413c6a200637020020002005360238200041013a000020002001290308370350200041d8006a200141086a41086a2802003602002001200129024037001b20002001290018370001200041086a2001411f6a290000370000200141d0006a2480808080000be70504057f017e037f017e23808080800041d0006b2201248080808000200141286a41a1aec2800041124193adc2800041204188a8c28000410010d882808000200141206a420437020020014200370218200142808080808001370210200142888080808001370240200142808080808001370248200141106a200141c0006a10ec84808000200141086a200141246a2802003602002001200129021c37030020012802102102200128021421032001280218210420012802282105200129022c2106200141106a41086a2207410036020020014280808080c000370210200141106a410010e0848080002001280214200728020041246c6a220841003a00202008410c36021c200841f5acc280003602182008420437021020084200370208200842808080808001370200200141c0006a41086a2209200728020041016a220836020020012001290210220a37034002402008200aa7470d00200141c0006a200810e084808000200128024821080b2001280244200841246c6a220841013a00202008411336021c200841b3aec2800036021820084204370210200842003702082008428080808080013702002007200928020041016a36020020012001290340370310200141346a200141106a41f3adc28000410610db848080002001200128023436021820012001280238220836021020012008200128023c41246c6a36021c20012008360214200141c0006a200141106a10ea8480800002402005418080808078470d0041afa3c28000411141a0a4c2800010a181808000000b2001200236021820012003360210200120033602142001200320044105746a36021c200041c4006a200141106a10ec848080002001411b6a200141c0006a41086a2802003600002000413c6a200637020020002005360238200041013a000020002001290300370350200041d8006a200141086a2802003602002001200129024037001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000b950403057f017e027f23808080800041d0006b2201248080808000200141106a41186a41c6aec2800041184193adc2800041204188a8c28000410010d882808000200141206a420437020020014200370218200142808080808001370210200142888080808001370240200142808080808001370248200141106a200141c0006a10ec84808000200141086a2202200141246a2802003602002001200129021c370300200128021021032001280214210420012802182105200129022c2106200128022821072001410036021820014280808080c000370210200141c0006a200141106a41deaec28000410710d284808000200141346a200141c0006a41e5aec28000410710d8848080002001200128023436021820012001280238220836021020012008200128023c41246c6a36021c20012008360214200141c0006a200141106a10ea8480800002402007418080808078470d0041afa3c28000411141a0a4c2800010a181808000000b2001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10ec848080002001411b6a200141c0006a41086a2802003600002000413c6a200637020020002007360238200041013a000020002001290300370350200041d8006a20022802003602002001200129024037001320002001290010370001200041086a200141106a41076a290000370000200141d0006a2480808080000bbf0604057f017e037f017e23808080800041d0006b2201248080808000200141306a41ecaec2800041114193adc2800041204188a8c28000410010d882808000200141286a420437020020014200370220200142808080808001370218200142888080808001370240200142808080808001370248200141186a200141c0006a10ec84808000200141086a41086a2001412c6a2802003602002001200129022437030820012802182102200128021c2103200128022021042001280230210520012902342106200141186a41086a2207410036020020014280808080c000370218200141186a410010e084808000200128021c200728020041246c6a220841003a00202008410736021c200841fdaec280003602182008420437021020084200370208200842808080808001370200200141c0006a41086a2209200728020041016a220836020020012001290218220a37034002402008200aa7470d00200141c0006a200810e084808000200128024821080b2001280244200841246c6a220841013a00202008410536021c20084184afc2800036021820084204370210200842003702082008428080808080013702002007200928020041016a220836020020012001290340220a37031802402008200aa7470d00200141186a200810e084808000200128022021080b200128021c200841246c6a220841023a00202008410836021c20084189afc28000360218200842043702102008420037020820084280808080800137020020012802202107200128021c2108200120012802183602202001200836021820012008200741246c6a41246a3602242001200836021c200141c0006a200141186a10ea8480800002402005418080808078470d0041afa3c28000411141a0a4c2800010a181808000000b20012002360220200120033602182001200336021c2001200320044105746a360224200041c4006a200141186a10ec84808000200141236a200141c0006a41086a2802003600002000413c6a200637020020002005360238200041013a000020002001290308370350200041d8006a200141086a41086a2802003602002001200129024037001b20002001290018370001200041086a2001411f6a290000370000200141d0006a2480808080000be70904057f017e037f017e23808080800041d0006b2201248080808000200141286a4191afc2800041104193adc2800041204188a8c28000410010d882808000200141106a41106a420437020020014200370218200142808080808001370210200142888080808001370240200142808080808001370248200141106a200141c0006a10ec84808000200141086a200141246a2802003602002001200129021c37030020012802102102200128021421032001280218210420012802282105200129022c2106200141106a41086a22074100360200200142808080808001370210200141106a410010e1848080002001280214200728020041386c6a2208420437022c20084213370224200841b7a5c280003602202008410836021c200841afa5c28000360218200841ef8080800036021020084293888c8f89fdc6ec9e7f370308200842a5e9e3ab9e929adc2c370300200141c0006a41086a2209200728020041016a220836020020012001290210220a37034002402008200aa7470d00200141c0006a200810e184808000200128024821080b2001280244200841386c6a2208420437022c20084213370224200841d2a5c280003602202008410836021c200841caa5c28000360218200841cf81808000360210200842aac2d79da4bfd9a04e370308200842e6afce95f6a1ffa6c3003703002007200928020041016a220836020020012001290340220a37031002402008200aa7470d00200141106a200810e184808000200128021821080b2001280214200841386c6a2208420437022c20084213370224200841d2a5c280003602202008410836021c200841e5a5c28000360218200841cf81808000360210200842aac2d79da4bfd9a04e370308200842e6afce95f6a1ffa6c300370300200141c0006a41086a2207200141106a41086a220928020041016a220836020020012001290310220a37034002402008200aa7470d00200141c0006a200810e184808000200128024821080b2001280244200841386c6a2208420437022c20084214370224200841f6a5c280003602202008410936021c200841eda5c28000360218200841ef8080800036021020084293888c8f89fdc6ec9e7f370308200842a5e9e3ab9e929adc2c3703002009200728020041016a220836020020012001290340220a37031002402008200aa7470d00200141106a200810e184808000200128021821080b2001280214200841386c6a2208420437022c2008420437022420084193a6c280003602202008410936021c2008418aa6c28000360218200841888180800036021020084298848fa1dab08ba174370308200842febac4ad81b6fafcb37f370300200128021821072001280214210820012001280210360248200120083602402001200836024420012008200741386c6a41386a36024c200141346a200141c0006a10eb8480800002402005418080808078470d0041afa3c28000411141a0a4c2800010a181808000000b2001200236021820012003360210200120033602142001200320044105746a36021c200041c4006a200141106a10ec848080002001411b6a200141346a41086a2802003600002000413c6a200637020020002005360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000b820604057f017e027f017e23808080800041d0006b2201248080808000200141286a41abb0c28000410b41a1b0c28000410a4194b0c28000410010d882808000200141206a420437020020014200370218200142808080808001370210200142888080808001370240200142808080808001370248200141106a200141c0006a10ec84808000200141086a200141246a2802003602002001200129021c37030020012802102102200128021421032001280218210420012802282105200129022c2106200141106a41086a22074100360200200142808080808001370210200141106a410010e1848080002001280214200728020041386c6a2208420437022c20084202370224200841e0a4c280003602202008410536021c200841dba4c28000360218200841c880808000360210200842e6ed8d82cc91adcb05370308200842dbd791d5c2919eaecd00370300200141c0006a41086a200728020041016a2208360200200120012902102209370340024020082009a7470d00200141c0006a200810e184808000200128024821080b2001280244200841386c6a2208420437022c2008422337022420084197a6c280003602202008410536021c200841e2a4c28000360218200841bd81808000360210200842d886fac2c186f9c46f3703082008429cfcf2b9cfebc9bfa37f370300200128024821072001280244210820012001280240360248200120083602402001200836024420012008200741386c6a41386a36024c200141346a200141c0006a10eb8480800002402005418080808078470d0041afa3c28000411141a0a4c2800010a181808000000b2001200236021820012003360210200120033602142001200320044105746a36021c200041c4006a200141106a10ec848080002001411b6a200141346a41086a2802003600002000413c6a200637020020002005360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000bbd0504057f017e027f017e23808080800041c0006b2201248080808000200141246a41b6b0c28000411241a1b0c28000410a4194b0c28000410010d8828080002001411c6a42043702002001420037021420014280808080800137020c2001428880808080013702302001428080808080013702382001410c6a200141306a10ec84808000200141086a200141206a28020036020020012001290218370300200128020c2102200128021021032001280214210420012802242105200129022821062001410c6a41086a2207410036020020014280808080c00037020c2001410c6a410010e0848080002001280210200728020041246c6a220841003a00202008410c36021c200841c8b0c280003602182008420437021020084200370208200842808080808001370200200141306a41086a200728020041016a22083602002001200129020c2209370330024020082009a7470d00200141306a200810e084808000200128023821080b2001280234200841246c6a220841013a00202008410736021c200841d4b0c2800036021820084204370210200842003702082008428080808080013702002001280238210720012802342108200120012802303602142001200836020c20012008200741246c6a41246a36021820012008360210200141306a2001410c6a10ea8480800002402005418080808078470d0041afa3c28000411141a0a4c2800010a181808000000b200120023602142001200336020c200120033602102001200320044105746a360218200041c4006a2001410c6a10ec84808000200141176a200141306a41086a2802003600002000413c6a200637020020002005360238200041013a000020002001290300370350200041d8006a200141086a2802003602002001200129023037000f2000200129000c370001200041086a2001410c6a41076a290000370000200141c0006a2480808080000bb40e04057f017e037f017e23808080800041d0006b2201248080808000200141306a4194b0c28000410d41a1b0c28000410a4194b0c28000410010d882808000200141286a420437020020014200370220200142808080808001370218200142888080808001370240200142808080808001370248200141186a200141c0006a10ec84808000200141086a41086a2001412c6a2802003602002001200129022437030820012802182102200128021c2103200128022021042001280230210520012902342106200141186a41086a2207410036020020014280808080c000370218200141186a410010e084808000200128021c200728020041246c6a220841003a00202008410536021c200841dbb0c280003602182008420437021020084200370208200842808080808001370200200141c0006a41086a2209200728020041016a220836020020012001290218220a37034002402008200aa7470d00200141c0006a200810e084808000200128024821080b2001280244200841246c6a220841013a00202008410c36021c200841e0b0c2800036021820084204370210200842003702082008428080808080013702002007200928020041016a220836020020012001290340220a37031802402008200aa7470d00200141186a200810e084808000200128022021080b200128021c200841246c6a220841023a00202008410936021c200841ecb0c280003602182008420437021020084200370208200842808080808001370200200141c0006a41086a2207200141186a41086a220928020041016a36020020012001290318370340200141186a200141c0006a41f5b0c28000410610d5848080000240200128022022082001280218470d00200141186a200810e084808000200128022021080b200128021c200841246c6a220841043a00202008411136021c200841fbb0c2800036021820084204370210200842003702082008428080808080013702002007200928020041016a220836020020012001290218220a37034002402008200aa7470d00200141c0006a200810e084808000200128024821080b2001280244200841246c6a220841053a00202008410b36021c2008418cb1c280003602182008420437021020084200370208200842808080808001370200200141186a41086a2207200141c0006a41086a220928020041016a220836020020012001290340220a37031802402008200aa7470d00200141186a200810e084808000200128022021080b200128021c200841246c6a220841063a00202008411036021c20084197b1c2800036021820084204370210200842003702082008428080808080013702002009200728020041016a36020020012001290318370340200141186a200141c0006a41a7b1c28000410510d784808000200141c0006a200141186a41acb1c28000410a10da84808000200141186a200141c0006a41b6b1c28000410d10dc848080000240200128022022082001280218470d00200141186a200810e084808000200128022021080b200128021c200841246c6a2208410a3a00202008410936021c200841c3b1c280003602182008420437021020084200370208200842808080808001370200200141c0006a41086a2207200141186a41086a220928020041016a220836020020012001290218220a37034002402008200aa7470d00200141c0006a200810e084808000200128024821080b2001280244200841246c6a2208410b3a00202008410a36021c200841ccb1c2800036021820084204370210200842003702082008428080808080013702002009200728020041016a220836020020012001290340220a37031802402008200aa7470d00200141186a200810e084808000200128022021080b200128021c200841246c6a2208410c3a00202008410b36021c200841d6b1c280003602182008420437021020084200370208200842808080808001370200200141c0006a41086a200141186a41086a28020041016a220836020020012001290318220a37034002402008200aa7470d00200141c0006a200810e084808000200128024821080b2001280244200841246c6a2208410d3a00202008410e36021c200841e1b1c2800036021820084204370210200842003702082008428080808080013702002001280248210720012802442108200120012802403602202001200836021820012008200741246c6a41246a3602242001200836021c200141c0006a200141186a10ea8480800002402005418080808078470d0041afa3c28000411141a0a4c2800010a181808000000b20012002360220200120033602182001200336021c2001200320044105746a360224200041c4006a200141186a10ec84808000200141236a200141c0006a41086a2802003600002000413c6a200637020020002005360238200041013a000020002001290308370350200041d8006a200141086a41086a2802003602002001200129024037001b20002001290018370001200041086a2001411f6a290000370000200141d0006a2480808080000bd10d04057f017e037f017e23808080800041d0006b2201248080808000200141306a41efb1c28000410a41a1b0c28000410a4194b0c28000410010d882808000200141186a41106a420437020020014200370220200142808080808001370218200142888080808001370240200142808080808001370248200141186a200141c0006a10ec84808000200141086a41086a2001412c6a2802003602002001200129022437030820012802182102200128021c2103200128022021042001280230210520012902342106200141186a41086a2207410036020020014280808080c000370218200141186a410010e084808000200128021c200728020041246c6a220841003a00202008411036021c200841f9b1c280003602182008420437021020084200370208200842808080808001370200200141c0006a41086a2209200728020041016a220836020020012001290218220a37034002402008200aa7470d00200141c0006a200810e084808000200128024821080b2001280244200841246c6a220841013a00202008410c36021c20084189b2c2800036021820084204370210200842003702082008428080808080013702002007200928020041016a220836020020012001290340220a37031802402008200aa7470d00200141186a200810e084808000200128022021080b200128021c200841246c6a220841023a00202008410c36021c20084195b2c280003602182008420437021020084200370208200842808080808001370200200141c0006a41086a2207200141186a41086a220928020041016a220836020020012001290318220a37034002402008200aa7470d00200141c0006a200810e084808000200128024821080b2001280244200841246c6a220841033a00202008410c36021c200841a1b2c2800036021820084204370210200842003702082008428080808080013702002009200728020041016a220836020020012001290340220a37031802402008200aa7470d00200141186a200810e084808000200128022021080b200128021c200841246c6a220841043a00202008410c36021c200841adb2c280003602182008420437021020084200370208200842808080808001370200200141c0006a41086a2207200141186a41086a220928020041016a220836020020012001290318220a37034002402008200aa7470d00200141c0006a200810e084808000200128024821080b2001280244200841246c6a220841053a00202008410636021c200841b9b2c2800036021820084204370210200842003702082008428080808080013702002009200728020041016a220836020020012001290340220a37031802402008200aa7470d00200141186a200810e084808000200128022021080b200128021c200841246c6a220841063a00202008410b36021c200841bfb2c280003602182008420437021020084200370208200842808080808001370200200141c0006a41086a2207200141186a41086a220928020041016a220836020020012001290318220a37034002402008200aa7470d00200141c0006a200810e084808000200128024821080b2001280244200841246c6a220841073a00202008411036021c200841cab2c2800036021820084204370210200842003702082008428080808080013702002009200728020041016a220836020020012001290340220a37031802402008200aa7470d00200141186a200810e084808000200128022021080b200128021c200841246c6a220841083a00202008410d36021c200841dab2c280003602182008420437021020084200370208200842808080808001370200200141c0006a41086a200141186a41086a28020041016a220836020020012001290318220a37034002402008200aa7470d00200141c0006a200810e084808000200128024821080b2001280244200841246c6a220841093a00202008410736021c200841e7b2c2800036021820084204370210200842003702082008428080808080013702002001280248210720012802442108200120012802403602202001200836021820012008200741246c6a41246a3602242001200836021c200141c0006a200141186a10ea8480800002402005418080808078470d0041afa3c28000411141a0a4c2800010a181808000000b20012002360220200120033602182001200336021c2001200320044105746a360224200041c4006a200141186a10ec84808000200141236a200141c0006a41086a2802003600002000413c6a200637020020002005360238200041013a000020002001290308370350200041d8006a200141086a41086a2802003602002001200129024037001b20002001290018370001200041086a200141186a41076a290000370000200141d0006a2480808080000bba0504057f017e027f017e23808080800041c0006b2201248080808000200141246a41eeb2c28000411641a1b0c28000410a4194b0c28000410010d8828080002001411c6a42043702002001420037021420014280808080800137020c2001428880808080013702302001428080808080013702382001410c6a200141306a10ec84808000200141086a200141206a28020036020020012001290218370300200128020c2102200128021021032001280214210420012802242105200129022821062001410c6a41086a2207410036020020014280808080c00037020c2001410c6a410010e0848080002001280210200728020041246c6a220841003a00202008410d36021c20084184b3c280003602182008420437021020084200370208200842808080808001370200200141306a41086a200728020041016a22083602002001200129020c2209370330024020082009a7470d00200141306a200810e084808000200128023821080b2001280234200841246c6a220841013a00202008410d36021c20084191b3c2800036021820084204370210200842003702082008428080808080013702002001280238210720012802342108200120012802303602142001200836020c20012008200741246c6a41246a36021820012008360210200141306a2001410c6a10ea8480800002402005418080808078470d0041afa3c28000411141a0a4c2800010a181808000000b200120023602142001200336020c200120033602102001200320044105746a360218200041c4006a2001410c6a10ec84808000200141176a200141306a41086a2802003600002000413c6a200637020020002005360238200041013a000020002001290300370350200041d8006a200141086a2802003602002001200129023037000f2000200129000c370001200041086a200141136a290000370000200141c0006a2480808080000bcd0403057f017e037f23808080800041d0006b2201248080808000200141286a419eb3c28000410b41a1b0c28000410a4194b0c28000410010d882808000200141206a420437020020014200370218200142808080808001370210200142888080808001370240200142808080808001370248200141106a200141c0006a10ec84808000200141086a2202200141246a2802003602002001200129021c370300200128021021032001280214210420012802182105200129022c21062001280228210720014100360218200142808080808001370210200141106a410010e1848080002001280214200128021841386c6a2208420437022c20084207370224200841c1a4c2800036022020084100360218200841be81808000360210200842b891b68c98adebcf61370308200842e7b0a091f3ed9c85c500370300200128021821092001280214210820012001280210360248200120083602402001200836024420012008200941386c6a41386a36024c200141346a200141c0006a10eb8480800002402007418080808078470d0041afa3c28000411141a0a4c2800010a181808000000b2001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10ec84808000200141106a410b6a200141346a41086a2802003600002000413c6a200637020020002007360238200041003a000020002001290300370350200041d8006a20022802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000bc611050a7f017e027f017e027f024002400240024002400240024002400240024002400240024002400240024002400240200128020022042f018a012205410b490d004101210641042105200128020822074105490d03200721052007417b6a0e020302010b200441046a21082001280204210902402001280208220741016a220120054b0d0020082001410c6c6a20082007410c6c6a200520076b410c6c10fe8d8080001a0b20082007410c6c6a22012002290200370200200141086a200241086a2802003602002004200541016a3b018a010c030b200741796a210741002106410621050c010b4100210641052105410021070b2001280204210a41002d00fca3c680001a418c0141002802c8a3c68000118180808000002208450d01200841003b018a0120084100360200200820042f018a01220b2005417f736a22013b018a012001410c4f0d02200b200541016a220c6b2001470d03200441046a220b2005410c6c6a220d290204210e200d280200210d200841046a200b200c410c6c6a2001410c6c10848e8080001a200420053b018a012004200820061b220f41046a21010240200f2f018a01220520074d0d0020012007410c6c6a220b410c6a200b200520076b410c6c10fe8d8080001a0b200a410020061b210920012007410c6c6a22012002290200370200200141086a200241086a280200360200200f200541016a3b018a010240200d418080808078460d00410021010240034020082110200e2111200d2112200422022802002204450d01200a2001470d0720022f018801210102400240024020042f018a012206410b490d004101210c200141054f0d012001210b410421010c020b200441046a220b2001410c6c6a2105200141016a2102200641016a21080240024020012006490d0020052011370204200520123602000c010b200b2002410c6c6a2005200620016b220b410c6c10fe8d8080001a200520113702042005201236020020014102742004418c016a22056a41086a200520024102746a200b41027410fe8d8080001a0b200420083b018a01200420024102746a418c016a20103602002002200641026a220b4f0d040240200620016b220641016a4103712205450d00200420014102746a4190016a210103402001280200220820023b01880120082004360200200141046a2101200241016a21022005417f6a22050d000b0b20064103490d04200241027420046a4198016a21010340200141746a280200220520023b01880120052004360200200141786a2802002205200241016a3b018801200520043602002001417c6a2802002205200241026a3b0188012005200436020020012802002205200241036a3b01880120052004360200200141106a2101200b200241046a2202470d000c050b0b2001210b024002402001417b6a0e020201000b200141796a210b4100210c410621010c010b4100210c410521014100210b0b41002d00fca3c680001a41bc0141002802c8a3c68000118180808000002208450d08200841003b018a0120084100360200200820042f018a01220d2001417f736a22023b018a012002410c4f0d09200d200141016a22056b2002470d0a200441046a22132001410c6c6a220d290204210e200d280200210d200841046a20132005410c6c6a2002410c6c10848e8080001a200420013b018a0120082f018a01220241016a21132002410c4f0d0b200620016b22012013470d0c200a41016a210a2008418c016a200420054102746a418c016a200141027410848e80800021064100210102400340200620014102746a280200220520013b01880120052008360200200120024f0d01200120012002496a220120024d0d000b0b20042008200c1b220541046a21060240200b41016a220120052f018a0122024b0d0020062001410c6c6a2006200b410c6c6a2002200b6b410c6c10fe8d8080001a0b200241016a21132006200b410c6c6a22062011370204200620123602002005418c016a21060240200b41026a2212200241026a220c4f0d00200620124102746a200620014102746a2002200b6b41027410fe8d8080001a0b200620014102746a2010360200200520133b018a0102402001200c4f0d0002402002200b6b221041016a4103712206450d002005200b4102746a4190016a210203402002280200220b20013b018801200b2005360200200241046a2102200141016a21012006417f6a22060d000b0b20104103490d00200520014102746a4198016a21020340200241746a280200220620013b01880120062005360200200241786a2802002206200141016a3b018801200620053602002002417c6a2802002206200141026a3b0188012006200536020020022802002206200141036a3b01880120062005360200200241106a2102200c200141046a2201470d000b0b200a2101200d418080808078470d000c020b0b200328020022052802002208450d0b2005280204210641002d00fca3c680001a41bc0141002802c8a3c68000118180808000002202450d0c2002200836028c01200241003b018a012002410036020020052002360200200841003b018801200820023602002005200641016a36020420062001470d0d20022f018a012201410b4f0d0e2002200141016a22053b018a0120022001410c6c6a220141086a2011370200200141046a20123602002002418c016a20054102746a2010360200201020053b018801201020023602000b200f21040b2000200736020820002009360204200020043602000f0b4104418c0110b280808000000b2001410b41e8b6c28000109581808000000b41b0b6c28000412841d8b6c2800010f880808000000b4188b7c28000413541c0b7c2800010f880808000000b410441bc0110b280808000000b2002410b41e8b6c28000109581808000000b41b0b6c28000412841d8b6c2800010f880808000000b2013410c41f8b6c28000109581808000000b41b0b6c28000412841d8b6c2800010f880808000000b41b0b4c2800010a081808000000b410441bc0110b280808000000b41e0b5c2800041304190b6c2800010f880808000000b41c0b4c28000412041a0b6c2800010f880808000000b8d17050c7f017e027f017e027f2380808080004180076b220524808080800002400240024002400240024002400240024002400240024002400240024002400240024002400240200128020022062f01aa142207410b490d004101210841042107200128020822094105490d03200921072009417b6a0e020302010b200641a4136a220820012802082209410c6c6a210a2001280204210b02400240200941016a220120074d0d00200a2002290200370200200a41086a200241086a2802003602000c010b20082001410c6c6a200a200720096b2208410c6c10fe8d8080001a200a41086a200241086a280200360200200a20022902003702002006200141e0016c6a2006200941e0016c6a200841e0016c10fe8d8080001a0b2006200941e0016c6a200341e00110848e8080001a2006200741016a3b01aa140c030b200941796a210941002108410621070c010b4100210841052107410021090b2001280204210a41002d00fca3c680001a41ac1441002802c8a3c6800011818080800000220c450d03200c41003b01aa14200c41003602a013200c20062f01aa14220d2007417f736a22013b01aa14200641a4136a220e2007410c6c6a220f2802002110200f2902042111200541a0056a2006200741e0016c6a41e00110848e8080001a2001410c4f0d04200d200741016a220f6b2001470d05200c41a4136a200e200f410c6c6a2001410c6c10848e8080001a200c2006200f41e0016c6a200141e0016c10848e808000210f200620073b01aa14200541c0036a200541a0056a41e00110848e8080001a2006200f20081b221241a4136a2009410c6c6a21010240024020122f01aa14220720094b0d0020012002290200370200200141086a200241086a2802003602000c010b2001410c6a2001200720096b220d410c6c10fe8d8080001a200141086a200241086a280200360200200120022902003702002012200941e0016c6a220141e0016a2001200d41e0016c10fe8d8080001a0b200a410020081b210b2012200941e0016c6a200341e00110848e8080001a2012200741016a3b01aa142005200541c0036a41e00110848e808000210d2010418080808078470d01201221060b200020093602082000200b360204200020063602000c010b200d41e0016a200d41e00110848e8080001a0240024020062802a01322010d004100210e0c010b4100210e200f21132011211420102115034020012108200a200e470d0620062f01a81421060240024002400240024020082f01aa14220e410b490d004101210f200641054f0d0120062103410421060c020b200841a4136a220a2006410c6c6a2107200641016a2101200e41016a2102024002402006200e490d0020072014370204200720153602002008200641e0016c6a200d41e0016a41e00110848e8080001a0c010b200a2001410c6c6a2007200e20066b220a410c6c10fe8d8080001a20072014370204200720153602002008200141e0016c6a2008200641e0016c6a2207200a41e0016c10fe8d8080001a2007200d41e0016a41e00110848e8080001a2006410274200841ac146a22076a41086a200720014102746a200a41027410fe8d8080001a0b200820023b01aa14200820014102746a41ac146a20133602002001200e41026a220a4f0d020240200e20066b220341016a4103712207450d00200820064102746a41b0146a210603402006280200220220013b01a814200220083602a013200641046a2106200141016a21012007417f6a22070d000b0b20034103490d02200141027420086a41b8146a21060340200641746a280200220720013b01a814200720083602a013200641786a2802002207200141016a3b01a814200720083602a0132006417c6a2802002207200141026a3b01a814200720083602a01320062802002207200141036a3b01a814200720083602a013200641106a2106200a200141046a2201470d000c030b0b20062103024002402006417b6a0e020201000b200641796a21034100210f410621060c010b4100210f41052106410021030b41002d00fca3c680001a41dc1441002802c8a3c6800011818080800000220c450d09200c41003b01aa14200c41003602a013200c20082f01aa1422022006417f736a22013b01aa14200841a4136a22162006410c6c6a2207280200211020072902042111200d41a0056a2008200641e0016c6a41e00110848e8080001a2001410c4f0d0a2002200641016a22076b2001470d0b200c41a4136a20162007410c6c6a2001410c6c10848e8080001a200c2008200741e0016c6a200141e0016c10848e8080002102200820063b01aa14200d41c0036a200d41a0056a41e00110848e8080001a20022f01aa14220141016a21162001410c4f0d0c200e20066b22062016470d0d200a41016a210e200241ac146a200820074102746a41ac146a200641027410848e808000210a4100210602400340200a20064102746a280200220720063b01a814200720023602a013200620014f0d01200620062001496a220620014d0d000b0b200d41a0056a200d41c0036a41e00110848e8080001a20082002200f1b220141a4136a220f2003410c6c6a210a02400240200341016a220620012f01aa1422074d0d00200a2014370204200a20153602000c010b200f2006410c6c6a200a200720036b220f410c6c10fe8d8080001a200a2014370204200a20153602002001200641e0016c6a2001200341e0016c6a200f41e0016c10fe8d8080001a0b200741016a21152001200341e0016c6a200d41e0016a41e00110848e8080001a200141ac146a210a0240200341026a2216200741026a220f4f0d00200a20164102746a200a20064102746a200720036b41027410fe8d8080001a0b200a20064102746a2013360200200120153b01aa1402402006200f4f0d000240200720036b221341016a410371220a450d00200120034102746a41b0146a210703402007280200220320063b01a814200320013602a013200741046a2107200641016a2106200a417f6a220a0d000b0b20134103490d00200120064102746a41b8146a21070340200741746a280200220a20063b01a814200a20013602a013200741786a280200220a200641016a3b01a814200a20013602a0132007417c6a280200220a200641026a3b01a814200a20013602a0132007280200220a200641036a3b01a814200a20013602a013200741106a2107200f200641046a2206470d000b0b200d200d41a0056a41e00110848e80800021062010418080808078470d010b200020093602082000200b360204200020123602000c030b200641e0016a200641e00110848e8080001a20022113200e210a20082106201121142010211520082802a01322010d000b0b200428020022012802002207450d0a2001280204210241002d00fca3c680001a41dc1441002802c8a3c68000118180808000002206450d0b200620073602ac14200641003b01aa14200641003602a01320012006360200200741003b01a814200720063602a0132001200241016a3602042002200e470d0c20062f01aa142201410b4f0d0d2006200141016a22073b01aa1420062001410c6c6a220241a8136a2011370200200241a4136a20103602002006200141e0016c6a200d41e0016a41e00110848e8080001a200641ac146a20074102746a200c360200200020093602082000200b36020420002012360200200c20073b01a814200c20063602a0130b20054180076a2480808080000f0b410441ac1410b280808000000b2001410b41e8b6c28000109581808000000b41b0b6c28000412841d8b6c2800010f880808000000b4188b7c28000413541c0b7c2800010f880808000000b410441dc1410b280808000000b2001410b41e8b6c28000109581808000000b41b0b6c28000412841d8b6c2800010f880808000000b2016410c41f8b6c28000109581808000000b41b0b6c28000412841d8b6c2800010f880808000000b41b0b4c2800010a081808000000b410441dc1410b280808000000b41e0b5c2800041304190b6c2800010f880808000000b41c0b4c28000412041a0b6c2800010f880808000000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bee0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b2201410274210420014180808080024941027421050240024020030d00200241003602180c010b200241043602182002200341027436021c200220002802043602140b200241086a20052004200241146a108b85808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000be20101027f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024108200241084b1b2202417f73411f7621040240024020010d00200341003602180c010b2003200136021c20034101360218200320002802043602140b200341086a20042002200341146a108b85808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bf20301027f23808080800041c0006b22042480808080000240024020010d00410021010c010b41012101200241c000490d0041022101200241808001490d00410441052002418080808004491b21010b200420033602182004200441186a36021c2004410c6a2004411c6a10928580800002402000280200220228020822052001490d0020024100360208200228020421032004413c6a2004280210220020042802146a360200200441386a200428020c360200200441346a2000360200200420023602242004200336021c20042000360230200420013602282004200520016b36022c2004200320016a3602202004411c6a10918580800020042802202102200441d0b7c28000360220200428021c2103200441d0b7c2800036021c200428022c210102400240024020022003470d002001450d022004280224220041086a21032004280228220520002802082202460d012000280204220020026a200020056a200110fe8d8080001a0c010b2001450d012004280224220041086a21032004280228220520002802082202460d002000280204220020026a200020056a200110fe8d8080001a0b2003200220016a3602000b02402004280238450d00200428023041002802c0a3c68000118080808000000b200441c0006a2480808080000f0b2001200541dcbcc28000109581808000000be60701097f410341022001280200418080808078461b21052000280200210602400240024020020d00200041086a2802002006200641054b22071b22020d010b200041086a2102024002402000200641054b22074103746a28020022082006410520071b460d002002200020071b21022000280204200041046a20071b21060c010b2000109a8580800020002802082108200028020421060b20062008412c6c6a2206200129020037020c200620053602082006410036022820064280808080c000370220200641146a200141086a2802003602002002200228020041016a3602000c010b024002402002412c6c200041046a2202280200200220071b6a41546a220628020822084101460d00410221070c010b200641186a2207280200210920074100360200200641146a280200210a2006280210210b20064280808080103702102006410c6a280200210c2006280204210d200628020021070b02400240024002402008417e6a2208410220084102491b0e020103000b200641106a21080c010b2006410c6a21080b2008280200450d00200828020441002802c0a3c68000118080808000000b200620053602082006200129020037020c200641146a200141086a28020036020020074102460d0002400240200028020820002802002201200141054b22011b220641014d0d0002402006412c6c2002280200200220011b6a41a87f6a220128020841014b0d0041002106410021022001280200450d02410121022001280204220541c000490d0241022102200541808001490d02410441052005418080808004491b21020c020b200b450d02200a41002802c0a3c68000118080808000000c020b4198bbc2800041c00041d8bbc2800010a181808000000b02402007450d0041012106200d41c000490d0041022106200d41808001490d0041044105200d418080808004491b21060b2001200d360204200120073602002009200220066b200620026b20062002491b2207410020076b20022006491b200c6a220620092006491b210602402001280210450d00200141146a28020041002802c0a3c68000118080808000000b2001200b360210200141186a2006360200200141146a200a3602000b0240024020034101470d00200041086a28020020002802002201200141054b22011b2206450d012006412c6c2000280204200041046a20011b6a220141746a210002402001417c6a2802002201450d00200141027420002802046a417c6a2802002004460d010b024020012000280200470d0020002001108c85808000200028020821010b200028020420014102746a20043602002000200028020841016a3602080b0f0b41fcb8c2800041fc004188bbc2800010a181808000000b8c07010c7f23808080800041b0026b2205248080808000200128020421060240024002400240024002400240200128020822070d00410121080c010b2007417f4c0d0141002d00fca3c680001a200741002802c8a3c68000118180808000002208450d020b20082006200710848e808000210902402000280200220a0d004100210a0c030b2000280204210b024002400340200a41a4136a2108200a2f01aa14220c410c6c210d417f210e0240024003400240200d0d00200c210e0c020b200841086a210f200841046a2110200d41746a210d200e41016a210e2008410c6a2108417f200920102802002007200f280200220f2007200f491b10888e80800022102007200f6b20101b220f410047200f4100481b220f4101460d000b200f41ff0171450d010b200b450d02200b417f6a210b200a200e4102746a41ac146a280200210a0c010b0b2007450d01200941002802c0a3c68000118080808000000c010b2007418080808078470d032009210a2000210e0b200a200e41e0016c6a21080c030b10ae80808000000b4101200710b280808000000b2005200e360224200541003602202005200a36021c2005200036021820052007360214200520093602102005200736020c200541003602280240200a0d0041002d00fca3c680001a41ac1441002802c8a3c68000118180808000002208450d02200841013b01aa14200841003602a013200820073602a413200841ac136a2007360200200841a8136a20093602002008200541286a41e00110848e80800021072000428080808010370204200020073602000c010b20054188026a41086a2005411c6a220841086a2802003602002005200829020037038802200520073602ac02200520093602a802200520073602a40220054198026a20054188026a200541a4026a200541286a200541186a108a8580800020052802182208200828020841016a36020820052802980220052802a00241e0016c6a21080b02400240200041146a280200200028020c2207200741054b22071b220d450d00200041106a220f280200210e200541286a41086a200141086a28020036020020052001290200370328200d410c6c200e200f20071b6a41746a200541286a10978580800041017321070c010b024020012802000d00410021070c010b41002107200641002802c0a3c68000118080808000000b20082002200720032004108f85808000200541b0026a2480808080000f0b410441ac1410b280808000000bf608010c7f200041e8bbc28000360204200041e8bbc280003602000240200028021022010d00024020002802082202280200200228020822036b200041206a2802002204200041186a28020022056b22064f0d00200220032006108d85808000200228020821030b024020052004460d00200228020421072005417f7320046a2108024020064103712206450d000340200720036a20052d00003a0000200341016a2103200541016a21052006417f6a22060d000b0b024020084103490d00200720036a2109410021070340200920076a2208200520076a22062d00003a0000200841016a200641016a2d00003a0000200841026a200641026a2d00003a0000200841036a200641036a2d00003a0000200741046a2107200641046a2004470d000b200320076a21030b200020043602180b200220033602080f0b024002400240200028020c220a200028020822032802082205470d00200041206a2802002109200041186a28020021070c010b200a20056b2106200328020420056a2105200041186a2802002107200041206a2802002109034020072009460d02200520072d00003a00002000200741016a22073602182003200328020841016a360208200541016a21052006417f6a22060d000b0b0240024020092007470d0020092107200921060c010b02402003280200200a20016a22066b200920076b22054f0d00200320062005108d858080000b200328020422062005200a6a22046a2006200a6a200110fe8d8080001a2000200436020c0240200328020822062004470d002004210a200721060c010b200328020420066a2105200a20096a20066b210820072106034020062009460d02200520062d00003a00002000200641016a22063602182003200328020841016a360208200541016a21052008200741016a2207470d000b2004210a0b0240024020092006470d00410121020c010b02400240200920076b2208417f4c0d004100210541002d00fca3c680001a200841002802c8a3c68000118180808000002202450d01200920066b2208410371210b02402006417f7320096a4103490d002008417c71210c410021050340200220056a2208200620056a22042d00003a0000200841016a200441016a2d00003a0000200841026a200441026a2d00003a0000200841036a200441036a2d00003a0000200c200541046a2205470d000b200620056a21060b0240200b450d000340200220056a20062d00003a0000200541016a2105200641016a2106200b417f6a220b0d000b0b200020093602182005450d0202402003280200200a20016a22066b20054f0d00200320062005108d858080000b20032802042208200a20056a22066a2008200a6a200110fe8d8080001a2000200636020c200328020822082006460d02200328020420086a21002005200a20086b6a210841002106034020052006460d03200020066a200220066a2d00003a00002003200328020841016a3602082008200641016a2206460d030c000b0b10ae80808000000b4101200810b280808000000b20092007460d00200241002802c0a3c68000118080808000000b0bcb0301047f23808080800041106b220224808080800041012103024020012802002204280200220141c000490d0041022103200141808001490d00410441052001418080808004491b21030b41002d00fca3c680001a024002400240200341002802c8a3c68000118180808000002201450d002002410036020c2002200136020820022003360204024020042802002205413f4b0d00200120054102743a0000410121030c030b0240200541ffff004b0d0020054102744101722104410021050240200341014b0d00200241046a4100410210b18280800020022802082101200228020c21050b200120056a20043b0000200541026a21030c030b200541ffffffff034b0d0120054102744102722104410021050240200341034b0d00200241046a4100410410b18280800020022802082101200228020c21050b200120056a2004360000200541046a21030c020b4101200310b280808000000b200141033a0000410121052002410136020c200428020021040240200341044b0d00200241046a4101410410b18280800020022802082101200228020c21050b200120056a2004360000200541046a21030b20002002290204370200200041086a2003360200200241106a2480808080000b9a0201027f23808080800041106b2202248080808000200220002802002200360204200128021441ecbcc280004106200141186a28020028020c118280808000002103200241003a000d200220033a000c20022001360208200241086a41f2bcc280004104200041046a41f8bcc28000108c818080004188bdc280004105200241046a4190bdc28000108c81808000210320022d000c21000240024020022d000d0d00200041ff017141004721010c010b41012101200041ff01710d000240200328020022012d001c4104710d0020012802144187a5c080004102200128021828020c1182808080000021010c010b20012802144186a5c080004101200128021828020c1182808080000021010b200241106a24808080800020010bae0101017f23808080800041306b2202248080808000200028020021002002410c6a4202370200200241186a410c6a41d08180800036020020022000280200220036022820024103360204200241f0bfc08000360200200241d18180800036021c200220006836022c200141186a28020021002002200241186a36020820022002412c6a3602202002200241286a36021820012802142000200210d9808080002101200241306a24808080800020010bed0201037f2380808080004180016b220224808080800002400240024002400240200128021c22034110710d0020034120710d0120003502004101200110fe8080800021000c020b20002802002100410021030340200220036a41ff006a413041d7002000410f712204410a491b20046a3a00002003417f6a210320004110492104200041047621002004450d000b20034180016a22004180014b0d022001410141c48dc080004102200220036a4180016a410020036b10db8080800021000c010b20002802002100410021030340200220036a41ff006a413041372000410f712204410a491b20046a3a00002003417f6a210320004110492104200041047621002004450d000b20034180016a22004180014b0d022001410141c48dc080004102200220036a4180016a410020036b10db8080800021000b20024180016a24808080800020000f0b200041800141c88dc08000109481808000000b200041800141c88dc08000109481808000000b02000bed04020c7f017e23808080800041d0006b22022480808080000240024002400240200028020022030d00200141046a2104410021030c010b200141046a210420012802082105200128020421062000280204210702400340200341046a210820032f018a012209410c6c210a417f210b0240024003400240200a0d002009210b0c020b200841086a210c200841046a210d200a41746a210a200b41016a210b2008410c6a2108417f2006200d2802002005200c280200220c2005200c491b10888e808000220d2005200c6b200d1b220c410047200c4100481b220c4101460d000b200c41ff0171450d010b2007450d022007417f6a21072003200b4102746a418c016a28020021030c010b0b410121082001280200450d02200641002802c0a3c68000118080808000000c020b200bad422086210e0b410121082001280200220a418080808078460d002002200e3702202002200336021c200220003602182002200a36020c200220042902003702100240024020030d0041002d00fca3c680001a418c0141002802c8a3c68000118180808000002208450d0320084100360200200841013b018a01200241146a280200210a2008200229020c3702042008410c6a200a3602002000428080808010370204200020083602000c010b200241286a41086a2002411c6a220841086a28020036020020022008290200370328200241c0006a41086a2002410c6a41086a2802003602002002200229020c370340200241346a200241286a200241c0006a200241186a10898580800020022802182208200828020841016a3602080b410021080b200241d0006a24808080800020080f0b4104418c0110b280808000000b02000ba90201027f23808080800041106b22022480808080000240024020002802000d00200128021441a0bdc280004110200141186a28020028020c1182808080000021010c010b20022000360204200128021441b0bdc280004108200141186a28020028020c118280808000002100200241003a000d200220003a000c20022001360208200241086a41b8bdc280004106200241046a41c0bdc28000108c81808000210320022d000c2100024020022d000d0d00200041ff017141004721010c010b41012101200041ff01710d000240200328020022012d001c4104710d0020012802144187a5c080004102200128021828020c1182808080000021010c010b20012802144186a5c080004101200128021828020c1182808080000021010b200241106a24808080800020010bbd0402077f017e23808080800041106b2201248080808000024002400240024002400240200041086a28020020002802002202200241054b1b220341016a2204450d004100417f2004417f6a677620044102491b41016a2204450d00200320044b0d0120024105200241054b22051b21062000280204200041046a220720051b2105024020044106490d0020062004460d060240024002402004ad422c7e2208422088a70d002008a7220741fcffffff074b0d0020024106490d012006ad422c7e2208422088a70d002008a7220641fcffffff074b0d00200741002802c8a3c68000118180808000002202450d02200220052006200720062007491b10848e8080001a200541002802c0a3c68000118080808000000c080b41f8bec280004111418cbfc2800010f880808000000b41002d00fca3c680001a200741002802c8a3c680001181808080000022020d050b4104200710b280808000000b200241064f0d020c050b41f8bec280004111419cbfc2800010a181808000000b41acbfc28000412041ccbfc2800010f880808000000b200720052003412c6c10848e8080001a200020033602002006ad422c7e2208a7210002402008422088a70d00200041fdffffff074f0d00200541002802c0a3c68000118080808000000c030b2001200036020c2001410036020841acbec28000412b200141086a41d8bec2800041e8bec28000108981808000000b200220052003412c6c10848e8080001a0b2000200336020820002002360204200020043602000b200141106a2480808080000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000be20101027f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024108200241084b1b2202417f73411f7621040240024020010d00200341003602180c010b2003200136021c20034101360218200320002802043602140b200341086a20042002200341146a109b85808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000b4b01017f02402000280200200028020822036b20024f0d00200020032002109c85808000200028020821030b200028020420036a2001200210848e8080001a2000200320026a36020841000b860201017f024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d0020020d02410121010c030b20032802002103200241002802c8a3c68000118180808000002201450d0320012003200410848e8080001a200341002802c0a3c68000118080808000000c020b20020d00410121010c010b41002d00fca3c680001a200241002802c8a3c68000118180808000002201450d010b20002001360204200041086a2002360200200041003602000f0b20004101360204200041086a2002360200200041013602000f0b20004100360204200041086a2002360200200041013602000f0b20004100360204200041013602000be20101027f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024108200241084b1b2202417f73411f7621040240024020010d00200341003602180c010b2003200136021c20034101360218200320002802043602140b200341086a20042002200341146a109e85808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000be50201057f23808080800041106b220224808080800020012802042103024002400240024002402001280208220141176a22040d002002410036020c20024280808080103702040c010b2004417f4c0d024100210541002d00fca3c680001a200441002802c8a3c68000118180808000002206450d032002410036020c200220063602082002200436020420014169490d010b200241046a41004117109f8580800020022802082106200228020c21050b200620056a220441002900dcbfc280003700002004410f6a41002900ebbfc28000370000200441086a41002900e4bfc280003700002002200541176a220436020c0240200228020420046b20014f0d00200241046a20042001109f8580800020022802082106200228020c21040b200620046a2003200110848e8080001a200041086a200420016a36020020002002290204370200200241106a2480808080000f0b10ae80808000000b4101200410b280808000000b800303027f017e057f23808080800041306b2201248080808000200141246a4192c2c28000410c41fcc1c28000411641fcc1c28000410010d882808000200141086a2202410036020020014280808080c00037030020012902282103200128022421042001410036021420014280808080800137020c2001410c6a410010a98580800020012802102205200128021441386c22066a220742d1beacaad083ca85ad7f370308200742fbd1a5f0d984c58f51370300200128020c21082007420437022c20074211370224200741a8c2c280003602202007410a36021c2007419ec2c28000360218200741d88180800036021002402004418080808078470d0041f3bfc28000411141e4c0c2800010a181808000000b200042808080808001370244200020043602382000200536020820002008360204200041003a000020002001290300370350200041cc006a41003602002000413c6a2003370200200041d8006a20022802003602002000200641386a41386e36020c200141306a2480808080000b6f00200042e7b0a091f3ed9c85c50037030820004280808080c00037033820004280808080c000370350200041d981808000360218200041023a0000200041106a42b891b68c98adebcf61370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6f00200042dbd791d5c2919eaecd0037030820004280808080c00037033820004280808080c000370350200041c880808000360218200041023a0000200041106a42e6ed8d82cc91adcb05370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b22064105762207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710ac8580800020022802082109200228020c21080b200920084105746a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b4105762107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b220641386e2207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710ab8580800020022802082109200228020c21080b2009200841386c6a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b41386e2107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000ba707010d7f200141186a28020021022001410d6a2d00002103200141096a2d00002104200141146a28020021052001280210210620012d000c21072001280204210820012d000821092001280200210a0340024002400240024002400240024002400240024002400240024002400240024002400240200a417e6a0e020102000b0240200941ff0171220b4102460d002001200b454101743a000841002109200b450d002004210c0c030b0240200a450d002008450d0002402008418002490d002001200841817e6a220836020441ff01210c410221090c040b200141003602042008417f6a210c41022109410021080c030b41022109200141023602000b200741ff0171210a410221070240200a4102460d0041002107200141003a000c200a0d030b200141033602000b2006450d072005450d07200120052005200220052002491b220a6b220536021420012006200a6a220b360210024002400240200a0e020001020b4100410041c8c4c2800010f980808000000b4101410141d8c4c2800010f980808000000b20062d000041047420062d000172210c4103210a200b21060b2000280208220b2000280200470d0d200a4103470d024100210d4103210a2006450d0c2005450d0c2002450d01200520026e220d2005200d20026c6b4100476a210d0c0c0b410021072000280208220b2000280200460d024102210a2003210c0c0c0b41c0c3c28000411941acc3c2800010f880808000000b2006450d06200a4102470d024102210a200741ff01714102470d014100210d410221070c050b2003210c2006450d060b200741ff0171410047210d4102210a0c030b200741ff0171220d4102460d012009410171200d4100476a210d0c020b0f0b410221074100210d0240200941ff0171220e4102470d00410221090c010b200e410047210d0b4100210e024002402005450d002002450d01200520026e220e2005200e20026c6b4100476a210e0b417f200d200e6a220e200e200d491b210d0c040b41c0c3c28000411941acc3c2800010f880808000000b200a4102470d014102210a410021064100210d200741ff01714102460d020b41002106200741ff0171410047210d4102210a0c010b0240200741ff0171220d4102460d00410021062009410171200d4100476a210d0c010b410021060240200941ff0171220d4102470d00410221094100210d0c010b41002106200d410047210d0b2000200b200d41016a220d417f200d1b10aa858080000b2000200b41016a3602082000280204200b6a200c3a00000c000b0ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000be00101037f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014108200141084b1b2201417f73411f7621040240024020030d00200241003602180c010b2002200336021c20024101360218200220002802043602140b200241086a20042001200241146a10a785808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a10a785808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000be20101027f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024108200241084b1b2202417f73411f7621040240024020010d00200341003602180c010b2003200136021c20034101360218200320002802043602140b200341086a20042002200341146a10a785808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241386c210420024193c9a4124941037421050240024020010d00200341003602180c010b200341083602182003200141386c36021c200320002802043602140b200341086a20052004200341146a10a785808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b22024105742104200241808080204941037421050240024020010d00200341003602180c010b200341083602182003200141057436021c200320002802043602140b200341086a20052004200341146a10a785808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000b950301037f0240200028020022022802002200413f4b0d00200041027421020240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a20023a00000f0b0240200041ffff004b0d002000410274410172210202402001280200200128020822006b41014b0d0020012000410210b182808000200128020821000b2001200041026a360208200128020420006a20023b00000f0b0240200041ffffffff034b0d002000410274410272210202402001280200200128020822006b41034b0d0020012000410410b182808000200128020821000b2001200041046a360208200128020420006a20023600000f0b02402001280200220320012802082200470d0020012000410110b18280800020012802002103200128020821000b2001280204220420006a41033a00002001200041016a2200360208200228020021020240200320006b41034b0d0020012000410410b18280800020012802042104200128020821000b2001200041046a360208200420006a20023600000bfe0501077f23808080800041c0006b220124808080800020014108360224200141d9c3c2800036022041002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241d9c3c28000360200200241046a410836020041d9c3c28000410810d782808000450d0141002d00fca3c680001a412041002802c8a3c68000118180808000002203450d02200342e7b0a091f3ed9c85c500370308200341d98180800036021820034101360204200341e1c3c28000360200200341106a42b891b68c98adebcf6137030020014101360210200120033602082001200341206a3602142001200336020c200141306a200141086a10a48580800020012802382104200128023421052001280230210620014100360210200142808080808001370208200141086a410010a985808000200128020c200128021041386c6a2203410036023020034280808080c0003703282003410036022020034100360218200341da81808000360210200342aac2d79da4bfd9a04e370308200342e6afce95f6a1ffa6c30037030020012802102107200128020c210320012001280208360238200120033602302001200336023420012003200741386c6a41386a36023c200141206a200141306a10a58580800020012006360210200120053602082001200520044105746a3602142001200536020c200041c4006a200141086a10a485808000200141136a200141206a41086a2802003600002000413c6a2002ad4280808080108437020020004101360238200041003a0000200041d8006a410036020020004280808080c0003703502001200129032037000b20002001290008370001200041086a2001410f6a290000370000200141c0006a2480808080000f0b4104410810b280808000000b200241002802c0a3c6800011808080800000200141146a42013702002001410236020c20014180ddc18000360208200141b4808080003602342001200141306a3602102001200141206a360230200141086a4190ddc1800010f680808000000b4108412010b280808000000b9d0401057f23808080800041306b2204248080808000200241017121050240024020032802002206418080808078470d002002413e2002413e491b210602402005450d0020012d000021070b200441246a4102360200200441206a2002417e71360200200420073a0019200441013a00142004410136020c2004200220066b360210200420053a00182004200120056a36021c200441ff00200641c000722002413e4b1b3a001520002004410c6a10a6858080002004200341086a28020022023602282004200441286a36022c2004412c6a200010ad858080002003280204210502402000280200200028020822036b20024f0d0020002003200210aa85808000200028020821030b200028020420036a2005200210848e8080001a2000200320026a3602080c010b2002411e2002411e491b210702402005450d0020012d000021080b200441246a4102360200200441206a2002417e71360200200420083a0019200441013a00142004410136020c2004200220076b360210200420053a00182004200120056a36021c2004413f20074120722002411e4b1b3a001520002004410c6a10a6858080002003280204210502402000280200200028020822026b200328020822034f0d0020002002200310aa85808000200028020821020b200028020420026a2005200310848e8080001a2000200220036a3602082006450d00200541002802c0a3c68000118080808000000b200441306a2480808080000b6f00200042f9f59bcab3e385eb9e7f37030820004280808080c00037033820004280808080c000370350200041db81808000360218200041023a0000200041106a42e4a581ceab969ef15c370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000be10101017f41002d00fca3c680001a0240413041002802c8a3c680001181808080000022010d004108413010b280808000000b200142d7c9cb8fc1cf97db3e370318200142a8c7daf8defb9a8fc400370308200142a7b98ffce8cbcbd38b7f370300200141dc8180800036021020004280808080c00037033820004280808080c0003703502000410236020c2000200136020820004102360204200041043a0000200141206a42e88488d0c0e3aebc13370300200141286a41b580808000360200200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b7600200042dbd791d5c2919eaecd0037030820004280808080c00037033820004280808080c00037035020004108360220200041c880808000360218200041033a0000200041106a42e6ed8d82cc91adcb05370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000bcd0401077f23808080800041306b22012480808080002001410336020c200141dcc6c2800036020841002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241dcc6c28000360200200241046a410336020041dcc6c28000410310d782808000450d0141002d00fca3c680001a412041002802c8a3c68000118180808000002203450d02200342cec69c9ba88e87eade00370308200341dd8180800036021820034101360204200341dfc6c28000360200200341106a428e89b0b185a4c6d5fa0037030020014100360218200142808080808001370210200141106a410010b68580800020012802142204200128021841386c22056a2206428e89b0b185a4c6d5fa00370308200642cec69c9ba88e87eade00370300200128021021072006410036023020064280808080c0003703282006410036022020064100360218200641dd81808000360210200041cc006a4101360200200041c8006a2003360200200041013602442000413c6a2002ad4280808080108437020020004101360238200041d8006a410036020020004280808080c0003703502000200541386a41386e36020c2000200436020820002007360204200041003a0000200141306a2480808080000f0b4104410810b280808000000b200241002802c0a3c68000118080808000002001411c6a42013702002001410236021420014180ddc18000360210200141b48080800036022c2001200141286a3602182001200141086a360228200141106a4190ddc1800010f680808000000b4108412010b280808000000bdf0b05027f017e037f017e017f23808080800041d0006b2201248080808000200141306a41e0c6c28000410e41eec6c28000410a41e0c6c28000410010d882808000200141086a41086a410036020020014280808080c0003703082001280230210220012902342103200141186a41086a22044100360200200142808080808001370218200141186a410010b685808000200128021c2004280200220541386c6a2206420437022c2006420d37022420064181c7c280003602202006410936021c200641f8c6c28000360218200641bd80808000360210200642efc9c9edb5e7b3a6c700370308200642acf6debeefe0d9c8d300370300200141c0006a41086a200541016a2205360200200120012902182207370340024020052007a7470d00200141c0006a200510b685808000200128024821050b2001280244200541386c6a2206420437022c2006420d37022420064181c7c280003602202006410936021c2006418ec7c28000360218200641bd80808000360210200642efc9c9edb5e7b3a6c700370308200642acf6debeefe0d9c8d3003703002004200541016a2205360200200120012903402207370318024020052007a7470d00200141186a200510b685808000200128022021050b200128021c200541386c6a2206420437022c20064203370224200641a8c7c280003602202006411136021c20064197c7c28000360218200641b580808000360210200642e88488d0c0e3aebc13370308200642d7c9cb8fc1cf97db3e370300200141c0006a41086a200541016a2205360200200120012903182207370340024020052007a7470d00200141c0006a200510b685808000200128024821050b2001280244200541386c6a2206420437022c20064203370224200641a8c7c280003602202006410c36021c200641abc7c28000360218200641b580808000360210200642e88488d0c0e3aebc13370308200642d7c9cb8fc1cf97db3e370300200141186a41086a200541016a2205360200200120012903402207370318024020052007a7470d00200141186a200510b685808000200128022021050b200128021c200541386c6a2206420437022c20064203370224200641a8c7c280003602202006410c36021c200641b7c7c28000360218200641b580808000360210200642e88488d0c0e3aebc13370308200642d7c9cb8fc1cf97db3e370300200141c0006a41086a200541016a2205360200200120012903182207370340024020052007a7470d00200141c0006a200510b685808000200128024821050b2001280244200541386c6a2206420437022c20064207370224200641c7c7c280003602202006410436021c200641c3c7c28000360218200641de81808000360210200642abd8aafbe989a3f863370308200642ccc2e78eb1d4fbf0bc7f370300200141186a41086a200541016a2205360200200120012903402207370318024020052007a7470d00200141186a200510b685808000200128022021050b200128021c200541386c6a2206420437022c20064203370224200641a8c7c280003602202006411336021c200641cec7c28000360218200641b580808000360210200642e88488d0c0e3aebc13370308200642d7c9cb8fc1cf97db3e370300200141c8006a200541016a2205360200200120012903182207370340024020052007a72204470d00200141c0006a200510b68580800020012802402104200128024821050b20012802442208200541386c6a220642e6ed8d82cc91adcb05370308200642dbd791d5c2919eaecd003703002006420437022c20064202370224200641eec7c280003602202006410d36021c200641e1c7c28000360218200641c88080800036021002402002418080808078470d0041d8c5c28000411141ccc6c2800010a181808000000b200042808080808001370244200020023602382000200836020820002004360204200041003a000020002001290308370350200041cc006a41003602002000413c6a2003370200200041d8006a200141106a2802003602002000200541386c41386a41386e36020c200141d0006a2480808080000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a10b585808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000b6f00200042a5e9e3ab9e929adc2c37030820004280808080c00037033820004280808080c000370350200041ef80808000360218200041063a0000200041106a4293888c8f89fdc6ec9e7f370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b960101017f23808080800041306b2202248080808000200241206a410c6a41cf80808000360200200241086a410c6a42023702002002410336020c20024190c8c28000360208200241cf80808000360224200220003602202002200041086a360228200141186a28020021002002200241206a36021020012802142000200241086a10d9808080002100200241306a24808080800020000bab0405027f017e027f017e027f23808080800041c0006b2201248080808000200141246a41a8c8c28000410641aec8c28000411541f0c7c28000410010d882808000200141086a410036020020014280808080c00037030020012802242102200129022821032001410c6a41086a2204410036020020014280808080800137020c2001410c6a410010bb85808000200141306a41086a2004280200220441016a22053602002001280210200441386c6a220442899ac8f29d8ce69ac300370308200442e78dcee4d0becc97573703002004420437022c20044203370224200441e8c9c280003602202004410836021c200441e0c9c28000360218200441df818080003602102001200129020c2206370330024020052006a72207470d00200141306a200510bb8580800020012802302107200128023821050b20012802342208200541386c6a220442899ac8f29d8ce69ac300370308200442e78dcee4d0becc97573703002004420437022c20044203370224200441e8c9c280003602202004410a36021c200441ebc9c28000360218200441df8180800036021002402002418080808078470d0041dcc8c28000411141d0c9c2800010a181808000000b200042808080808001370244200020023602382000200836020820002007360204200041003a000020002001290300370350200041cc006a41003602002000413c6a20033702002000200541016a36020c200041d8006a200141086a280200360200200141c0006a2480808080000bab0405027f017e027f017e027f23808080800041c0006b2201248080808000200141246a41c3c8c28000410f41d2c8c28000410a41f0c7c28000410010d882808000200141086a410036020020014280808080c00037030020012802242102200129022821032001410c6a41086a2204410036020020014280808080800137020c2001410c6a410010bb85808000200141306a41086a2004280200220441016a22053602002001280210200441386c6a22044293888c8f89fdc6ec9e7f370308200442a5e9e3ab9e929adc2c3703002004420437022c20044203370224200441e8c9c280003602202004410436021c200441f5c9c28000360218200441ef808080003602102001200129020c2206370330024020052006a72207470d00200141306a200510bb8580800020012802302107200128023821050b20012802342208200541386c6a22044293888c8f89fdc6ec9e7f370308200442a5e9e3ab9e929adc2c3703002004420437022c20044203370224200441e8c9c280003602202004410536021c200441f9c9c28000360218200441ef8080800036021002402002418080808078470d0041dcc8c28000411141d0c9c2800010a181808000000b200042808080808001370244200020023602382000200836020820002007360204200041003a000020002001290300370350200041cc006a41003602002000413c6a20033702002000200541016a36020c200041d8006a200141086a280200360200200141c0006a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a10bc85808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000ba02001177f23808080800041c0016b220524808080800002400240024002400240024002400240024002400240024002400240024002400240024002400240200128020022062f0196042207410b490d0041012108410421092001280208220a4105490d03200a2109200a417b6a0e020302010b20062001280208220a4105746a21092001280204210b02400240200a41016a220120074d0d0020092002290000370000200941186a200241186a290000370000200941106a200241106a290000370000200941086a200241086a2900003700000c010b200620014105746a20092007200a6b220c41057410fe8d8080001a200941186a200241186a290000370000200941106a200241106a290000370000200941086a200241086a29000037000020092002290000370000200641e0026a220220014104746a2002200a4104746a200c41047410fe8d8080001a0b2006200a4104746a220241e8026a200341086a290200370200200241e0026a20032902003702002006200741016a3b0196040c030b200a41796a210a41002108410621090c010b41002108410521094100210a0b2001280204210741002d00fca3c680001a41980441002802c8a3c6800011818080800000220d450d03200d41003b019604200d41003602900420054190016a41086a200620094105746a220c41086a29000037030020054190016a41106a200c41106a29000037030020054190016a41186a200c41186a290000370300200541c0006a41086a200641e0026a220e20094104746a220f410c6a280200360200200d20062f01960422102009417f736a22013b0196042005200c290000370390012005200f2902043703402001410c4f0d042010200941016a220c6b2001470d05200f2802002111200d2006200c4105746a200141057410848e808000220f41e0026a200e200c4104746a200141047410848e8080001a200620093b019604200541f0006a41086a20054190016a41086a290300370300200541f0006a41106a20054190016a41106a290300370300200541f0006a41186a20054190016a41186a290300370300200541b0016a41086a200541c0006a41086a2802003602002005200529039001370370200520052903403703b0012006200f20081b2212200a4105746a21010240024020122f0196042209200a4b0d0020012002290000370000200141186a200241186a290000370000200141106a200241106a290000370000200141086a200241086a2900003700000c010b2012200a41016a220c4105746a20012009200a6b221041057410fe8d8080001a200141186a200241186a290000370000200141106a200241106a290000370000200141086a200241086a29000037000020012002290000370000201241e0026a2202200c4104746a2002200a4104746a201041047410fe8d8080001a0b2007410020081b210b2012200a4104746a220241e8026a200341086a290200370200200241e0026a2003290200370200200541106a41086a2202200541f0006a41086a290300370300200541106a41106a2201200541f0006a41106a290300370300200541106a41186a2203200541f0006a41186a290300370300200541e0006a41086a220c200541b0016a41086a2802003602002012200941016a3b01960420052005290370370310200520052903b0013703602011418080808078470d01201221060b2000200a3602082000200b360204200020063602000c010b200541c0006a41186a2003290300370300200541c0006a41106a2001290300370300200541c0006a41086a200229030037030020052005290310370340200541306a41086a200c2802003602002005200529036037033002400240024020062802900422020d00410021130c010b41002113200f21142011211503402002210c20072013470d0720062f0194042106024002400240200c2f0196042213410b490d0041012108200641054f0d0120062103410421060c020b200641016a2102201341016a2109200c20064105746a21010240024020062013490d0020012005290340370000200141186a200541c0006a41186a290300370000200141106a200541c0006a41106a290300370000200141086a200541c0006a41086a290300370000200c20064104746a220141e0026a2015360200200141e4026a2005290330370200200141ec026a200541306a41086a2802003602000c010b200c20024105746a2001201320066b220741057410fe8d8080001a200141186a200541c0006a41186a290300370000200141106a200541c0006a41106a290300370000200141086a200541c0006a41086a29030037000020012005290340370000200c41e0026a220120024104746a200120064104746a2201200741047410fe8d8080001a20012015360200200120052903303702042001410c6a200541306a41086a2802003602002006410274200c4198046a22016a41086a200120024102746a200741027410fe8d8080001a0b200c20093b019604200c20024102746a4198046a20143602002002201341026a22074f0d040240201320066b220341016a4103712201450d00200c20064102746a419c046a210603402006280200220920023b0194042009200c36029004200641046a2106200241016a21022001417f6a22010d000b0b20034103490d042002410274200c6a41a4046a21060340200641746a280200220120023b0194042001200c36029004200641786a2802002201200241016a3b0194042001200c360290042006417c6a2802002201200241026a3b0194042001200c3602900420062802002201200241036a3b0194042001200c36029004200641106a21062007200241046a2202470d000c050b0b20062103024002402006417b6a0e020201000b200641796a210341002108410621060c010b4100210841052106410021030b41002d00fca3c680001a41c80441002802c8a3c6800011818080800000220d450d08200d41003b019604200d41003602900420054190016a41086a220f200c20064105746a220141086a29000037030020054190016a41106a2210200141106a29000037030020054190016a41186a220e200141186a290000370300200541b0016a41086a2216200c41e0026a221720064104746a2209410c6a280200360200200d200c2f01960422112006417f736a22023b0196042005200129000037039001200520092902043703b0012002410c4f0d092011200641016a22016b2002470d0a20092802002111200d200c20014105746a200241057410848e808000220941e0026a201720014104746a200241047410848e8080001a200c20063b019604200541f0006a41086a2218200f290300370300200541f0006a41106a22192010290300370300200541f0006a41186a221a200e290300370300200541e0006a41086a221b20162802003602002005200529039001370370200520052903b00137036020092f019604220241016a21172002410c4f0d0b201320066b22062017470d0c200741016a211320094198046a200c20014102746a4198046a200641027410848e80800021074100210602400340200720064102746a280200220120063b0194042001200936029004200620024f0d01200620062002496a220620024d0d000b0b200e201a29030037030020102019290300370300200f20182903003703002016201b2802003602002005200529037037039001200520052903603703b001200c200920081b220220034105746a210102400240200341016a220620022f01960422074d0d0020012005290340370000200141186a200541c0006a41186a290300370000200141106a200541c0006a41106a290300370000200141086a200541c0006a41086a2903003700000c010b200220064105746a2001200720036b220841057410fe8d8080001a200141186a200541c0006a41186a290300370000200141106a200541c0006a41106a290300370000200141086a200541c0006a41086a29030037000020012005290340370000200241e0026a220120064104746a200120034104746a200841047410fe8d8080001a0b200741016a2117200220034104746a220141e0026a2015360200200141e4026a2005290330370200200141ec026a200541306a41086a221528020036020020024198046a21010240200341026a2218200741026a22084f0d00200120184102746a200120064102746a200720036b41027410fe8d8080001a0b200120064102746a2014360200200220173b0196040240200620084f0d000240200720036b221441016a4103712207450d00200220034102746a419c046a210103402001280200220320063b0194042003200236029004200141046a2101200641016a21062007417f6a22070d000b0b20144103490d00200220064102746a41a4046a21010340200141746a280200220720063b0194042007200236029004200141786a2802002207200641016a3b01940420072002360290042001417c6a2802002207200641026a3b019404200720023602900420012802002207200641036a3b0194042007200236029004200141106a21012008200641046a2206470d000b0b200541106a41186a2206200e290300370300200541106a41106a22022010290300370300200541106a41086a2201200f290300370300200541086a220720162802003602002005200529039001370310200520052903b0013703002011418080808078460d02200541c0006a41186a2006290300370300200541c0006a41106a2002290300370300200541c0006a41086a20012903003703002015200728020036020020052005290310370340200520052903003703302009211420132107200c210620112115200c2802900422020d000b0b200428020022022802002201450d0b2002280204210941002d00fca3c680001a41c80441002802c8a3c68000118180808000002206450d0c2006200136029804200641003b019604200641003602900420022006360200200141003b01940420012006360290042002200941016a36020420092013470d0d20062f0196042202410b4f0d0e2006200241016a22093b019604200620024105746a22012005290340370200200141086a200541c0006a41086a290300370200200141186a200541c0006a41186a290300370200200141106a200541c0006a41106a290300370200200620024104746a220241e0026a2011360200200241e4026a2005290330370200200241ec026a200541306a41086a28020036020020064198046a20094102746a200d360200200d20093b019404200d2006360290040b2000200a3602082000200b360204200020123602000b200541c0016a2480808080000f0b410441980410b280808000000b2001410b41bccdc28000109581808000000b4184cdc28000412841accdc2800010f880808000000b41dccdc2800041354194cec2800010f880808000000b410441c80410b280808000000b2002410b41bccdc28000109581808000000b4184cdc28000412841accdc2800010f880808000000b2017410c41cccdc28000109581808000000b4184cdc28000412841accdc2800010f880808000000b4184cbc2800010a081808000000b410441c80410b280808000000b41b4ccc28000413041e4ccc2800010f880808000000b4194cbc28000412041f4ccc2800010f880808000000ba61601137f23808080800041d0026b220424808080800002400240024002400240024002400240024002400240024002400240024002400240024002400240200128020022052f01f6062206410b490d004101210741042106200128020822084105490d03200821062008417b6a0e020302010b2001280204210902402001280208220841016a220120064b0d002005200141d0006c6a2005200841d0006c6a200620086b41d0006c10fe8d8080001a0b2005200841d0006c6a200241d00010848e8080001a2005200641016a3b01f6060c030b200841796a210841002107410621060c010b4100210741052106410021080b2001280204210a41002d00fca3c680001a41f80641002802c8a3c6800011818080800000220b450d03200b41003b01f606200b41003602f006200b20052f01f606220c2006417f736a22013b01f60620044180026a2005200641d0006c6a220d41cc0010848e8080001a200441e2006a200d41cf006a2d00003a00002004200d2f004d3b01602001410c4f0d04200c200641016a220e6b2001470d05200d2d004c210f200b2005200e41d0006c6a200141d0006c10848e808000210d200520063b01f606200441b0016a20044180026a41cc0010848e8080001a200441fc016a41026a2206200441e0006a41026a2d00003a0000200420042f01603b01fc0102402005200d20071b22102f01f606220120084d0d002010200841d0006c6a220c41d0006a200c200120086b41d0006c10fe8d8080001a0b200a410020071b21092010200841d0006c6a200241d00010848e8080001a2010200141016a3b01f606200441106a200441b0016a41cc0010848e8080001a200441ac016a41026a20062d00003a0000200420042f01fc013b01ac01200f41ff01714102470d01201021050b2000200836020820002009360204200020053602000c010b200441e0006a200441106a41cc0010848e8080001a200441dc006a41026a2211200441ac016a41026a22122d00003a0000200420042f01ac013b015c0240024020052802f00622010d00410021130c010b200441fc016a41026a210e41002113200d2114200f2115034020012107200a2013470d0620052f01f40621050240024002400240024020072f01f6062213410b490d0041012116200541054f0d012005210d410421050c020b200541016a21010240200520134f22020d002007200141d0006c6a2007200541d0006c6a201320056b41d0006c10fe8d8080001a0b201341016a210d2007200541d0006c6a200441e0006a41cc0010848e808000220620153a004c200620042f015c3b004d200641cf006a200441dc006a41026a2d00003a0000200741f8066a2106201341026a210a024020020d00200541027420066a41086a200620014102746a201320056b41027410fe8d8080001a0b2007200d3b01f606200620014102746a20143602002001200a4f0d020240201320056b220d41016a4103712206450d00200720054102746a41fc066a210503402005280200220220013b01f406200220073602f006200541046a2105200141016a21012006417f6a22060d000b0b200d4103490d02200141027420076a4184076a21050340200541746a280200220620013b01f406200620073602f006200541786a2802002206200141016a3b01f406200620073602f0062005417c6a2802002206200141026a3b01f406200620073602f00620052802002206200141036a3b01f406200620073602f006200541106a2105200a200141046a2201470d000c030b0b2005210d024002402005417b6a0e020201000b200541796a210d41002116410621050c010b41002116410521054100210d0b41002d00fca3c680001a41a80741002802c8a3c6800011818080800000220b450d09200b41003b01f606200b41003602f006200b20072f01f60622022005417f736a22013b01f60620044180026a2007200541d0006c6a220641cc0010848e8080001a200e200641cf006a2d00003a0000200420062f004d3b01fc012001410c4f0d0a2002200541016a220c6b2001470d0b20062d004c210f200b2007200c41d0006c6a200141d0006c10848e8080002102200720053b01f606200441b0016a20044180026a41cc0010848e8080001a2012200e2d00003a0000200420042f01fc013b01ac0120022f01f606220141016a21062001410c4f0d0c201320056b22052006470d0d200a41016a2113200241f8066a2007200c4102746a41f8066a200541027410848e808000210a4100210502400340200a20054102746a280200220620053b01f406200620023602f006200520014f0d01200520052001496a220520014d0d000b0b20044180026a200441b0016a41cc0010848e8080001a200e20122d00003a0000200420042f01ac013b01fc010240200d41016a22052007200220161b22062f01f60622014b0d002006200541d0006c6a2006200d41d0006c6a2001200d6b41d0006c10fe8d8080001a0b200141016a21162006200d41d0006c6a200441e0006a41cc0010848e808000220a20153a004c200a20042f015c3b004d200a41cf006a20112d00003a0000200641f8066a210a0240200d41026a2215200141026a220c4f0d00200a20154102746a200a20054102746a2001200d6b41027410fe8d8080001a0b200a20054102746a2014360200200620163b01f60602402005200c4f0d0002402001200d6b221641016a410371220a450d002006200d4102746a41fc066a210103402001280200220d20053b01f406200d20063602f006200141046a2101200541016a2105200a417f6a220a0d000b0b20164103490d00200620054102746a4184076a21010340200141746a280200220a20053b01f406200a20063602f006200141786a280200220a200541016a3b01f406200a20063602f0062001417c6a280200220a200541026a3b01f406200a20063602f0062001280200220a200541036a3b01f406200a20063602f006200141106a2101200c200541046a2205470d000b0b200441106a20044180026a41cc0010848e8080001a2004410c6a41026a2205200e2d00003a0000200420042f01fc013b010c200f41ff01714102470d010b2000200836020820002009360204200020103602000c030b200441e0006a200441106a41cc0010848e8080001a201120052d00003a0000200420042f010c3b015c200221142013210a20072105200f211520072802f00622010d000b0b200328020022012802002206450d0a2001280204210241002d00fca3c680001a41a80741002802c8a3c68000118180808000002205450d0b200520063602f806200541003b01f606200541003602f00620012005360200200641003b01f406200620053602f0062001200241016a36020420022013470d0c20052f01f6062201410b4f0d0d2005200141016a22063b01f6062005200141d0006c6a200441e0006a41cc0010848e8080002201200f3a004c200120042f015c3b004d200141cf006a200441dc006a41026a2d00003a0000200541f8066a20064102746a200b360200200020083602082000200936020420002010360200200b20063b01f406200b20053602f0060b200441d0026a2480808080000f0b410441f80610b280808000000b2001410b41bccdc28000109581808000000b4184cdc28000412841accdc2800010f880808000000b41dccdc2800041354194cec2800010f880808000000b410441a80710b280808000000b2001410b41bccdc28000109581808000000b4184cdc28000412841accdc2800010f880808000000b2006410c41cccdc28000109581808000000b4184cdc28000412841accdc2800010f880808000000b4184cbc2800010a081808000000b410441a80710b280808000000b41b4ccc28000413041e4ccc2800010f880808000000b4194cbc28000412041f4ccc2800010f880808000000b9718050b7f017e037f017e037f23808080800041c0006b220524808080800002400240024002400240024002400240024002400240024002400240024002400240024002400240200128020022062f01ba022207410b490d004101210841042107200128020822094105490d03200921072009417b6a0e020302010b200641b4016a220820012802082209410c6c6a210a2001280204210b02400240200941016a220120074d0d00200a2002290200370200200a41086a200241086a2802003602000c010b20082001410c6c6a200a200720096b2208410c6c10fe8d8080001a200a41086a200241086a280200360200200a2002290200370200200620014104746a200620094104746a200841047410fe8d8080001a0b200620094104746a220141086a200341086a290200370200200120032902003702002006200741016a3b01ba020c030b200941796a210941002108410621070c010b4100210841052107410021090b2001280204210a41002d00fca3c680001a41bc0241002802c8a3c6800011818080800000220c450d03200c41003b01ba02200c41003602b001200c20062f01ba02220d2007417f736a22013b01ba02200541306a41086a200620074104746a220e41086a2902003703002005200e2902003703302001410c4f0d04200d200741016a220e6b2001470d05200641b4016a220d2007410c6c6a220f2902042110200f280200210f200c41b4016a200d200e410c6c6a2001410c6c10848e8080001a200c2006200e4104746a200141047410848e808000210e200620073b01ba02200541206a41086a200541306a41086a290300370300200520052903303703202006200e20081b221141b4016a220d2009410c6c6a21010240024020112f01ba02220720094b0d0020012002290200370200200141086a200241086a2802003602000c010b200d200941016a2212410c6c6a2001200720096b220d410c6c10fe8d8080001a200141086a200241086a28020036020020012002290200370200201120124104746a201120094104746a200d41047410fe8d8080001a0b200a410020081b210b201120094104746a220141086a200341086a29020037020020012003290200370200200541086a2201200541206a41086a2903003703002011200741016a3b01ba0220052005290320370300200f418080808078470d01201121060b200020093602082000200b360204200020063602000c010b200541106a41086a2001290300370300200520052903003703100240024020062802b00122010d00410021120c010b41002112200e211320102114200f2115034020012108200a2012470d0620062f01b80221060240024002400240024020082f01ba022212410b490d004101210e200641054f0d0120062102410421060c020b200841b4016a220a2006410c6c6a2107200641016a2101201241016a21030240024020062012490d002007201437020420072015360200200820064104746a22072005290310370200200741086a200541106a41086a2903003702000c010b200a2001410c6c6a2007201220066b220a410c6c10fe8d8080001a2007201437020420072015360200200820014104746a200820064104746a2207200a41047410fe8d8080001a200741086a200541106a41086a290300370200200720052903103702002006410274200841bc026a22076a41086a200720014102746a200a41027410fe8d8080001a0b200820033b01ba02200820014102746a41bc026a20133602002001201241026a220a4f0d020240201220066b220241016a4103712207450d00200820064102746a41c0026a210603402006280200220320013b01b802200320083602b001200641046a2106200141016a21012007417f6a22070d000b0b20024103490d02200141027420086a41c8026a21060340200641746a280200220720013b01b802200720083602b001200641786a2802002207200141016a3b01b802200720083602b0012006417c6a2802002207200141026a3b01b802200720083602b00120062802002207200141036a3b01b802200720083602b001200641106a2106200a200141046a2201470d000c030b0b20062102024002402006417b6a0e020201000b200641796a21024100210e410621060c010b4100210e41052106410021020b41002d00fca3c680001a41ec0241002802c8a3c6800011818080800000220c450d09200c41003b01ba02200c41003602b001200c20082f01ba0222032006417f736a22013b01ba02200541306a41086a220d200820064104746a220741086a290200370300200520072902003703302001410c4f0d0a2003200641016a22076b2001470d0b200841b4016a22032006410c6c6a220f2902042110200f280200210f200c41b4016a20032007410c6c6a2001410c6c10848e8080001a200c200820074104746a200141047410848e8080002103200820063b01ba02200541206a41086a2216200d2903003703002005200529033037032020032f01ba02220141016a21172001410c4f0d0c201220066b22062017470d0d200a41016a2112200341bc026a200820074102746a41bc026a200641027410848e808000210a4100210602400340200a20064102746a280200220720063b01b802200720033602b001200620014f0d01200620062001496a220620014d0d000b0b200d20162903003703002005200529032037033020082003200e1b220141b4016a220e2002410c6c6a210a02400240200241016a220620012f01ba0222074d0d00200a2014370204200a20153602000c010b200e2006410c6c6a200a200720026b220e410c6c10fe8d8080001a200a2014370204200a2015360200200120064104746a200120024104746a200e41047410fe8d8080001a0b200741016a2115200120024104746a220a2005290310370200200a41086a200541106a41086a2217290300370200200141bc026a210a0240200241026a2216200741026a220e4f0d00200a20164102746a200a20064102746a200720026b41027410fe8d8080001a0b200a20064102746a2013360200200120153b01ba0202402006200e4f0d000240200720026b221341016a410371220a450d00200120024102746a41c0026a210703402007280200220220063b01b802200220013602b001200741046a2107200641016a2106200a417f6a220a0d000b0b20134103490d00200120064102746a41c8026a21070340200741746a280200220a20063b01b802200a20013602b001200741786a280200220a200641016a3b01b802200a20013602b0012007417c6a280200220a200641026a3b01b802200a20013602b0012007280200220a200641036a3b01b802200a20013602b001200741106a2107200e200641046a2206470d000b0b200541086a2206200d29030037030020052005290330370300200f418080808078470d010b200020093602082000200b360204200020113602000c030b2017200629030037030020052005290300370310200321132012210a2008210620102114200f211520082802b00122010d000b0b200428020022012802002207450d0a2001280204210341002d00fca3c680001a41ec0241002802c8a3c68000118180808000002206450d0b200620073602bc02200641003b01ba02200641003602b00120012006360200200741003b01b802200720063602b0012001200341016a36020420032012470d0c20062f01ba022201410b4f0d0d2006200141016a22073b01ba0220062001410c6c6a220341b8016a2010370200200341b4016a200f360200200620014104746a220141086a200541106a41086a29030037020020012005290310370200200641bc026a20074102746a200c360200200020093602082000200b36020420002011360200200c20073b01b802200c20063602b0010b200541c0006a2480808080000f0b410441bc0210b280808000000b2001410b41bccdc28000109581808000000b4184cdc28000412841accdc2800010f880808000000b41dccdc2800041354194cec2800010f880808000000b410441ec0210b280808000000b2001410b41bccdc28000109581808000000b4184cdc28000412841accdc2800010f880808000000b2017410c41cccdc28000109581808000000b4184cdc28000412841accdc2800010f880808000000b4184cbc2800010a081808000000b410441ec0210b280808000000b41b4ccc28000413041e4ccc2800010f880808000000b4194cbc28000412041f4ccc2800010f880808000000b9718050b7f017e037f017e037f23808080800041c0006b220524808080800002400240024002400240024002400240024002400240024002400240024002400240024002400240200128020022062f01ba022207410b490d004101210841042107200128020822094105490d03200921072009417b6a0e020302010b200641b4016a220820012802082209410c6c6a210a2001280204210b02400240200941016a220120074d0d00200a2002290200370200200a41086a200241086a2802003602000c010b20082001410c6c6a200a200720096b2208410c6c10fe8d8080001a200a41086a200241086a280200360200200a2002290200370200200620014104746a200620094104746a200841047410fe8d8080001a0b200620094104746a220141086a200341086a290200370200200120032902003702002006200741016a3b01ba020c030b200941796a210941002108410621070c010b4100210841052107410021090b2001280204210a41002d00fca3c680001a41bc0241002802c8a3c6800011818080800000220c450d03200c41003b01ba02200c41003602b001200c20062f01ba02220d2007417f736a22013b01ba02200541306a41086a200620074104746a220e41086a2902003703002005200e2902003703302001410c4f0d04200d200741016a220e6b2001470d05200641b4016a220d2007410c6c6a220f2902042110200f280200210f200c41b4016a200d200e410c6c6a2001410c6c10848e8080001a200c2006200e4104746a200141047410848e808000210e200620073b01ba02200541206a41086a200541306a41086a290300370300200520052903303703202006200e20081b221141b4016a220d2009410c6c6a21010240024020112f01ba02220720094b0d0020012002290200370200200141086a200241086a2802003602000c010b200d200941016a2212410c6c6a2001200720096b220d410c6c10fe8d8080001a200141086a200241086a28020036020020012002290200370200201120124104746a201120094104746a200d41047410fe8d8080001a0b200a410020081b210b201120094104746a220141086a200341086a29020037020020012003290200370200200541086a2201200541206a41086a2903003703002011200741016a3b01ba0220052005290320370300200f418080808078470d01201121060b200020093602082000200b360204200020063602000c010b200541106a41086a2001290300370300200520052903003703100240024020062802b00122010d00410021120c010b41002112200e211320102114200f2115034020012108200a2012470d0620062f01b80221060240024002400240024020082f01ba022212410b490d004101210e200641054f0d0120062102410421060c020b200841b4016a220a2006410c6c6a2107200641016a2101201241016a21030240024020062012490d002007201437020420072015360200200820064104746a22072005290310370200200741086a200541106a41086a2903003702000c010b200a2001410c6c6a2007201220066b220a410c6c10fe8d8080001a2007201437020420072015360200200820014104746a200820064104746a2207200a41047410fe8d8080001a200741086a200541106a41086a290300370200200720052903103702002006410274200841bc026a22076a41086a200720014102746a200a41027410fe8d8080001a0b200820033b01ba02200820014102746a41bc026a20133602002001201241026a220a4f0d020240201220066b220241016a4103712207450d00200820064102746a41c0026a210603402006280200220320013b01b802200320083602b001200641046a2106200141016a21012007417f6a22070d000b0b20024103490d02200141027420086a41c8026a21060340200641746a280200220720013b01b802200720083602b001200641786a2802002207200141016a3b01b802200720083602b0012006417c6a2802002207200141026a3b01b802200720083602b00120062802002207200141036a3b01b802200720083602b001200641106a2106200a200141046a2201470d000c030b0b20062102024002402006417b6a0e020201000b200641796a21024100210e410621060c010b4100210e41052106410021020b41002d00fca3c680001a41ec0241002802c8a3c6800011818080800000220c450d09200c41003b01ba02200c41003602b001200c20082f01ba0222032006417f736a22013b01ba02200541306a41086a220d200820064104746a220741086a290200370300200520072902003703302001410c4f0d0a2003200641016a22076b2001470d0b200841b4016a22032006410c6c6a220f2902042110200f280200210f200c41b4016a20032007410c6c6a2001410c6c10848e8080001a200c200820074104746a200141047410848e8080002103200820063b01ba02200541206a41086a2216200d2903003703002005200529033037032020032f01ba02220141016a21172001410c4f0d0c201220066b22062017470d0d200a41016a2112200341bc026a200820074102746a41bc026a200641027410848e808000210a4100210602400340200a20064102746a280200220720063b01b802200720033602b001200620014f0d01200620062001496a220620014d0d000b0b200d20162903003703002005200529032037033020082003200e1b220141b4016a220e2002410c6c6a210a02400240200241016a220620012f01ba0222074d0d00200a2014370204200a20153602000c010b200e2006410c6c6a200a200720026b220e410c6c10fe8d8080001a200a2014370204200a2015360200200120064104746a200120024104746a200e41047410fe8d8080001a0b200741016a2115200120024104746a220a2005290310370200200a41086a200541106a41086a2217290300370200200141bc026a210a0240200241026a2216200741026a220e4f0d00200a20164102746a200a20064102746a200720026b41027410fe8d8080001a0b200a20064102746a2013360200200120153b01ba0202402006200e4f0d000240200720026b221341016a410371220a450d00200120024102746a41c0026a210703402007280200220220063b01b802200220013602b001200741046a2107200641016a2106200a417f6a220a0d000b0b20134103490d00200120064102746a41c8026a21070340200741746a280200220a20063b01b802200a20013602b001200741786a280200220a200641016a3b01b802200a20013602b0012007417c6a280200220a200641026a3b01b802200a20013602b0012007280200220a200641036a3b01b802200a20013602b001200741106a2107200e200641046a2206470d000b0b200541086a2206200d29030037030020052005290330370300200f418080808078470d010b200020093602082000200b360204200020113602000c030b2017200629030037030020052005290300370310200321132012210a2008210620102114200f211520082802b00122010d000b0b200428020022012802002207450d0a2001280204210341002d00fca3c680001a41ec0241002802c8a3c68000118180808000002206450d0b200620073602bc02200641003b01ba02200641003602b00120012006360200200741003b01b802200720063602b0012001200341016a36020420032012470d0c20062f01ba022201410b4f0d0d2006200141016a22073b01ba0220062001410c6c6a220341b8016a2010370200200341b4016a200f360200200620014104746a220141086a200541106a41086a29030037020020012005290310370200200641bc026a20074102746a200c360200200020093602082000200b36020420002011360200200c20073b01b802200c20063602b0010b200541c0006a2480808080000f0b410441bc0210b280808000000b2001410b41bccdc28000109581808000000b4184cdc28000412841accdc2800010f880808000000b41dccdc2800041354194cec2800010f880808000000b410441ec0210b280808000000b2001410b41bccdc28000109581808000000b4184cdc28000412841accdc2800010f880808000000b2017410c41cccdc28000109581808000000b4184cdc28000412841accdc2800010f880808000000b4184cbc2800010a081808000000b410441ec0210b280808000000b41b4ccc28000413041e4ccc2800010f880808000000b4194cbc28000412041f4ccc2800010f880808000000b8d11030b7f037e047f23808080800041206b220324808080800020002802002104024020002802042205450d0002400240200541037122060d00200521070c010b2005210703402007417f6a2107200420042f01e2014102746a41e4016a28020021042006417f6a22060d000b0b20054104490d000340200420042f01e2014102746a41e4016a280200220420042f01e2014102746a41e4016a280200220420042f01e2014102746a41e4016a280200220420042f01e2014102746a41e4016a28020021042007417c6a22070d000b0b20012802202108200128021c2109200128021821062001280214210a2001280210210b200128020c210c2001280208210d2001290200210e0340200bad422086200cad84210f02400240034002400240200d418180808078460d0020062107200d2105200e21100c010b20062008460d02200641146a2107200629020c210f20062802082105200629020021100b02402005418080808078460d00200fa72111024020072008470d00418080808078210d200821060c040b200741146a21062007280210210b200728020c210c2007290200210e02402007280208220d418080808078470d00418080808078210d0c040b2010200e520d03200729020c210f2010210e2005450d01201141002802c0a3c68000118080808000002010210e0c010b0b20082007460d00200820076b220441146e220141017121054100210602402004416c6a4114490d00200741206a2104200141feffffff007121014100210603400240200441686a280200450d002004416c6a28020041002802c0a3c68000118080808000000b02402004417c6a280200450d00200428020041002802c0a3c68000118080808000000b200441286a21042001200641026a2206470d000b0b2005450d002007200641146c6a2204280208450d00200441086a28020441002802c0a3c68000118080808000000b02402009450d00200a41002802c0a3c68000118080808000000b02402000280204220d450d00200028020021060340024002400240024020062f01e2012207450d00200641e4016a220520074102746a28020022042f01e201220141054f0d0320052007417f6a22084102746a28020022052f01e2012207410520016b2212490d012005200720126b22133b01e201200441053b01e201200420124103746a2004200141037410fe8d8080001a200441dc006a220b2012410c6c6a200b2001410c6c10fe8d8080001a2007201341016a22116b220c410420016b470d022004200520114103746a200c410374220010848e8080002107200b200541dc006a22022011410c6c6a200c410c6c220c10848e808000210b200341086a20022013410c6c6a220241086a28020022143602002002290200210e200620084103746a220229000021102002200520134103746a2900003700002003200e37030020062008410c6c6a220641dc006a2213290200210f2013200e370200200641e4006a2206280200211320062014360200200720006a2010370000200b200c6a220641086a20133602002006200f370200200d4101460d03200741e4016a2206201241027422126a2006200141027441046a10fe8d8080001a2006200520114102746a41e4016a201210848e8080001a20072802e401220641003b01e00120062007360258200741e8016a280200220641013b01e00120062007360258200741ec016a280200220641023b01e00120062007360258200741f0016a280200220641033b01e00120062007360258200741f4016a280200220641043b01e00120062007360258200741f8016a280200220641053b01e001200620073602580c030b41dccec28000411941f8cec2800010f880808000000b41a4cec28000412741cccec2800010f880808000000b4184cdc28000412841accdc2800010f880808000000b20042106200d417f6a220d0d000b0b200341206a2480808080000f0b200f422088a72114024002400240024002400240024020042f01e2012207410b490d004100211302400240034020042802582204450d01201341016a211320042f01e201410b4f0d000c020b0b200028020421012000280200210741002d00fca3c680001a41940241002802c8a3c68000118180808000002204450d02200420073602e401200441003b01e2012004410036025820002004360200200741003b01e001200720043602582000200141016a22133602040b41002d00fca3c680001a41e40141002802c8a3c68000118180808000002201450d02200141003b01e201200141003602582013417f6a2212450d04034041002d00fca3c680001a41940241002802c8a3c68000118180808000002207450d04200720013602e401200741003b01e20120074100360258200141003b01e00120012007360258200721012012417f6a2212450d050c000b0b2004200741016a3b01e201200420074103746a201037000020042007410c6c6a220741e4006a2014360200200741e0006a2011360200200741dc006a20053602000c040b410441940210b280808000000b410441e40110b280808000000b410441940210b280808000000b20042f01e2012207410b4f0d012004200741016a22123b01e201200420074103746a201037000020042007410c6c6a220741e0006a2011360200200741dc006a2005360200200741e4006a2014360200200420124102746a41e4016a2001360200200120123b01e001200120043602582013450d0002400240201341037122010d00201321070c010b2013210703402007417f6a2107200420042f01e2014102746a41e4016a28020021042001417f6a22010d000b0b20134104490d000340200420042f01e2014102746a41e4016a280200220420042f01e2014102746a41e4016a280200220420042f01e2014102746a41e4016a280200220420042f01e2014102746a41e4016a28020021042007417c6a22070d000b0b2002200228020041016a3602000c010b0b4194cbc28000412041f4ccc2800010f880808000000bc513020f7f057e23808080800041c0016b220324808080800020002802002104024020002802042205450d0002400240200541037122060d00200521070c010b2005210703402007417f6a2107200420042f01e6024102746a41e8026a28020021042006417f6a22060d000b0b20054104490d000340200420042f01e6024102746a41e8026a280200220420042f01e6024102746a41e8026a280200220420042f01e6024102746a41e8026a280200220420042f01e6024102746a41e8026a28020021042007417c6a22070d000b0b200341306a200141306a280200360200200341286a200141286a290200370300200341206a200141206a290200370300200341186a200141186a290200370300200341106a200141106a290200370300200341086a200141086a2902003703002003200129020037030020034198016a41016a2108200341116a21090240034020032802042106200328020c210a0240024020032d001022074102460d0020082009290000370000200841186a200941186a290000370000200841106a200941106a290000370000200841086a200941086a29000037000020070d010c030b2006200a460d0220082006290000370000200841086a200641086a290000370000200841106a200641106a290000370000200841186a200641186a2900003700002003200641206a22063602040b200341f8006a41186a220b200841186a290000370300200341f8006a41106a220c200841106a290000370300200341f8006a41086a220d200841086a29000037030020032008290000370378024002402006200a460d0020092006290000370000200941086a2201200641086a220e290000370000200941106a2205200641106a220f290000370000200941186a2210200641186a22112900003700002003200641206a220736020402400240200341f8006a2009412010888e8080000d00200d200e290000370300200c200f290000370300200b2011290000370300200320062900003703782007200a460d0102400340200920072900003700002001200741086a2900003700002005200741106a2900003700002010200741186a290000370000200341f8006a2009412010888e8080000d01200d2001290000370300200c2005290000370300200b201029000037030020032009290000370378200741206a2207200a460d030c000b0b2003200741206a3602040b200341013a00100c020b2003200a3602040b2009200329039801370000200941086a20034198016a41086a290300370000200941106a20034198016a41106a290300370000200941186a20034198016a41186a290300370000200341003a00100b200341386a41186a2210200b290300370300200341386a41106a220a200c290300370300200341386a41086a220b200d29030037030020032003290378370338024002400240024002400240024020042f01e6022207410b490d004100210502400240034020042802e0022204450d01200541016a210520042f01e602410b4f0d000c020b0b200028020421062000280200210741002d00fca3c680001a41980341002802c8a3c68000118180808000002204450d02200420073602e802200441003b01e602200441003602e00220002004360200200741003b01e402200720043602e0022000200641016a22053602040b41002d00fca3c680001a41e80241002802c8a3c68000118180808000002206450d02200641003b01e602200641003602e0022005417f6a2201450d04034041002d00fca3c680001a41980341002802c8a3c68000118180808000002207450d04200720063602e802200741003b01e602200741003602e002200641003b01e402200620073602e002200721062001417f6a2201450d050c000b0b2004200741016a3b01e602200420074105746a22072003290338370000200741186a2010290300370000200741106a200a290300370000200741086a200b2903003700000c040b410441980310b280808000000b410441e80210b280808000000b410441980310b280808000000b20042f01e6022207410b4f0d012004200741016a22013b01e602200420074105746a22072003290338370000200741186a2010290300370000200741106a200a290300370000200741086a200b290300370000200420014102746a41e8026a2006360200200620013b01e402200620043602e0022005450d0002400240200541037122060d00200521070c010b2005210703402007417f6a2107200420042f01e6024102746a41e8026a28020021042006417f6a22060d000b0b20054104490d000340200420042f01e6024102746a41e8026a280200220420042f01e6024102746a41e8026a280200220420042f01e6024102746a41e8026a280200220420042f01e6024102746a41e8026a28020021042007417c6a22070d000b0b2002200228020041016a3602000c010b0b4194cbc28000412041f4ccc2800010f880808000000b02402003280208450d00200328020041002802c0a3c68000118080808000000b024020002802042205450d00200028020021060340024002400240024020062f01e6022204450d00200641e8026a220920044102746a28020022072f01e602220141054f0d0320092004417f6a220b4102746a28020022092f01e6022204410520016b2210490d012009200420106b22083b01e602200741053b01e602200720104105746a2007200141057410fe8d8080001a2004200841016a220a6b2204410420016b470d0220072009200a4105746a2004410574220c10848e80800021042006200b4105746a22062900002112200920084105746a220841086a2900002113200841106a2900002114200841186a290000211520062008290000370000200641186a2208290000211620082015370000200641106a2208290000211520082014370000200641086a22062900002114200620133700002004200c6a220641186a2016370000200641106a2015370000200641086a20143700002006201237000020054101460d05200441e8026a2206201041027422086a2006200141027441046a10fe8d8080001a20062009200a4102746a41e8026a200810848e8080001a20042802e802220641003b01e402200620043602e002200441ec026a280200220641013b01e402200620043602e002200441f0026a280200220641023b01e402200620043602e002200441f4026a280200220641033b01e402200620043602e002200441f8026a280200220641043b01e402200620043602e002200441fc026a280200220641053b01e402200620043602e0020c030b41dccec28000411941f8cec2800010f880808000000b41a4cec28000412741cccec2800010f880808000000b4184cdc28000412841accdc2800010f880808000000b200721062005417f6a22050d000b0b200341c0016a2480808080000b9e0f03087f017e027f23808080800041c0006b220324808080800020002802002104024020002802042205450d0002400240200541037122060d00200521070c010b2005210703402007417f6a2107200420042f018a014102746a418c016a28020021042006417f6a22060d000b0b20054104490d000340200420042f018a014102746a418c016a280200220420042f018a014102746a418c016a280200220420042f018a014102746a418c016a280200220420042f018a014102746a418c016a28020021042007417c6a22070d000b0b200341086a41186a2208200141186a280200360200200341086a41106a2209200141106a290200370300200341086a41086a200141086a29020037030020032001290200370308200341246a200341086a10b38980800002402003280224418080808078460d00200341246a41086a210a0240034002400240024002400240024020042f018a012207410b490d004100210502400240034020042802002204450d01200541016a210520042f018a01410b4f0d000c020b0b200028020421062000280200210741002d00fca3c680001a41bc0141002802c8a3c68000118180808000002204450d022004200736028c01200441003b018a012004410036020020002004360200200741003b018801200720043602002000200641016a22053602040b41002d00fca3c680001a418c0141002802c8a3c68000118180808000002206450d02200641003b018a01200641003602002005417f6a2201450d04034041002d00fca3c680001a41bc0141002802c8a3c68000118180808000002207450d042007200636028c01200741003b018a0120074100360200200641003b01880120062007360200200721062001417f6a2201450d050c000b0b2004200741016a3b018a012003290224210b20042007410c6c6a2207410c6a200a280200360200200741046a200b3702000c040b410441bc0110b280808000000b4104418c0110b280808000000b410441bc0110b280808000000b20042f018a012207410b4f0d022004200741016a22013b018a0120042007410c6c6a2207410c6a200a280200360200200741046a2003290224370200200420014102746a418c016a2006360200200620013b018801200620043602002005450d0002400240200541037122060d00200521070c010b2005210703402007417f6a2107200420042f018a014102746a418c016a28020021042006417f6a22060d000b0b20054104490d000340200420042f018a014102746a418c016a280200220420042f018a014102746a418c016a280200220420042f018a014102746a418c016a280200220420042f018a014102746a418c016a28020021042007417c6a22070d000b0b2002200228020041016a360200200341246a200341086a10b3898080002003280224418080808078470d000c020b0b4194cbc28000412041f4ccc2800010f880808000000b02402008280200220420092802002201460d00200420016b2204410c6e22064101712105410021070240200441746a410c490d00200641feffffff017121062001210441002107034002402004280200450d00200441046a28020041002802c0a3c68000118080808000000b02402004410c6a280200450d00200441106a28020041002802c0a3c68000118080808000000b200441186a21042006200741026a2207470d000b0b2005450d0020012007410c6c6a2204280200450d00200428020441002802c0a3c68000118080808000000b02402003411c6a280200450d00200328021441002802c0a3c68000118080808000000b024020032802082204418280808078480d002004450d00200328020c41002802c0a3c68000118080808000000b024020002802042201450d00200028020021070340024002400240024020072f018a012205450d002007418c016a220220054102746a28020022042f018a01220641054f0d0320022005417f6a220c4102746a28020022022f018a012200410520066b2205490d012002200020056b220a3b018a01200441053b018a01200441046a22082005410c6c6a20082006410c6c10fe8d8080001a2000200a41016a22096b2200410420066b470d022008200241046a220d2009410c6c6a2000410c6c220010848e8080002108200d200a410c6c6a220a41086a280200210d2007200c410c6c6a220741046a220c290200210b200c200a2902003702002007410c6a2207280200210a2007200d360200200820006a220741086a200a3602002007200b37020020014101460d052004418c016a2207200541027422056a2007200641027441046a10fe8d8080001a2007200220094102746a418c016a200510848e8080001a200428028c01220741003b0188012007200436020020044190016a280200220741013b0188012007200436020020044194016a280200220741023b0188012007200436020020044198016a280200220741033b018801200720043602002004419c016a280200220741043b01880120072004360200200441a0016a280200220741053b018801200720043602000c030b41dccec28000411941f8cec2800010f880808000000b41a4cec28000412741cccec2800010f880808000000b4184cdc28000412841accdc2800010f880808000000b200421072001417f6a22010d000b0b200341c0006a2480808080000be805010d7f200341246a280200200341c8006a2802002204200441284b22041b21052003280220200341206a20041b2106024002400240200341cc006a2d000041ff01712207450d00200341cd006a2d000041ff01712108034020012f01f606220941d0006c210a41002104417f210b4100210c024002400340200b210d200a2004460d010240417f2003200120046a220b412010888e808000220e410047200e4100481b220e0d00417f2006200b41206a220e280200200e200b41c8006a280200220f41284b22101b2005200b41246a280200200f20101b220e2005200e491b10888e808000220f2005200e6b200f1b220e410047200e4100481b220e0d0002402007200b41cc006a2d0000220f4f0d00200c21090c030b4101210e2007200f470d0002402008200b41cd006a2d000041ff0171220e4f0d00200c21090c030b2008200e47210e0b200c41016a210c200441d0006a2104200d41016a210b200e4101460d000b200e41ff0171450d01200d41016a21090b2002450d032002417f6a2102200120094102746a41f8066a28020021010c010b0b41002104200b21090c020b034020012f01f606220f41d0006c210c41002104417f2109024002400240024003400240200c2004470d00200f21090c030b0240417f2003200120046a220b412010888e808000220e410047200e4100481b220e0d00417f2006200b41206a220e280200200e200b41c8006a280200220d41284b220a1b2005200b41246a280200200d200a1b220e2005200e491b10888e808000220d2005200e6b200d1b220e410047200e4100481b220e450d020b200441d0006a2104200941016a2109200e4101460d000b200e41ff01710d01410021040c060b200b41cc006a2d0000450d01200941016a21090b20020d010c030b200941016a2109410021040c030b2002417f6a2102200120094102746a41f8066a28020021010c000b0b41012104410021020b20002001360204200020043602002000410c6a2009360200200041086a20023602000ba90402097f017e23808080800041306b22032480808080000240024002400240024002402001280204220441286e2205200220052002491b22050d00410821060c010b200541b3e6cc194b0d01200541286c2207417f4c0d0141002d00fca3c680001a200741002802c8a3c68000118180808000002206450d020b410021072003410036020c200320063602082003200536020402402002450d00200128020041206a2105034002402004411f4b0d00200328020421080c050b200120053602002001200441606a22063602042003280204210820064108490d042001200641786a22043602042001200541086a360200200341106a41086a2209200541606a220641086a290000370300200341106a41106a220a200641106a290000370300200341106a41186a220b200641186a290000370300200320062900003703102005290000210c024020072008470d00200341046a2007109a86808000200328020c21070b2003280208200741286c6a220620032903103703002006200c370320200641086a2009290300370300200641106a200a290300370300200641186a200b2903003703002003200741016a220736020c200541286a21052002417f6a22020d000b0b20002003290204370200200041086a200341046a41086a2802003602000c030b10ae80808000000b4108200710b280808000000b20004180808080783602002008450d00200328020841002802c0a3c68000118080808000000b200341306a2480808080000beb0301097f23808080800041306b22032480808080000240024002400240200128020422044105762205200220052002491b22050d00410121060c010b200541ffffff1f4b0d0120054105742207417f4c0d0141002d00fca3c680001a200741002802c8a3c68000118180808000002206450d020b410021072003410036020c20032006360208200320053602040240024002402002450d0020012802002105034020044120490d022001200441606a22043602042001200541206a2208360200200341106a41086a2209200541086a290000370300200341106a41106a220a200541106a290000370300200341106a41186a220b200541186a29000037030020032005290000370310024020072003280204470d00200341046a200710a18680800020032802082106200328020c21070b200620074105746a22052003290310370000200541186a200b290300370000200541106a200a290300370000200541086a20092903003700002003200741016a220736020c200821052002417f6a22020d000b0b20002003290204370200200041086a200341046a41086a2802003602000c010b20004180808080783602002003280204450d00200641002802c0a3c68000118080808000000b200341306a2480808080000f0b10ae80808000000b4101200710b280808000000b980501067f23808080800041c0006b2203248080808000410421040240024002400240200128020041046a28020041146e2205200220052002491b2205450d00200541e6cc99334b0d02200541146c2206417f4c0d0241002d00fca3c680001a200641002802c8a3c68000118180808000002204450d010b20012001280204220741016a2206360204200341003602102003200436020c2003200536020802400240200620012802084d0d0020004180808080783602000c010b024002402002450d00200341146a41017221040340200341146a2001109f8d80800020032d001422064105460d02200341286a410f6a22072004410f6a280000360000200341286a41086a2208200441086a290000370300200320042900003703280240200328021022052003280208470d00200341086a200510a586808000200328021021050b200328020c200541146c6a22052003290328370001200520063a0000200541096a2008290300370000200541106a20072800003600002003200328021041016a3602102002417f6a22020d000b2001280204417f6a21070b2001200736020420002003290208370200200041086a200341086a41086a2802003602000c040b200041808080807836020020032802102201450d00200328020c220621054100210403402006200441146c6a21020240024002400240024020052d00000e0400010102040b200541086a21020c020b200241086a21020c010b200241046a21020b2002280200450d00200228020441002802c0a3c68000118080808000000b200441016a2104200541146a21052001417f6a22010d000b0b2003280208450d02200328020c41002802c0a3c68000118080808000000c020b4104200610b280808000000b10ae80808000000b200341c0006a2480808080000ba204010a7f23808080800041306b2203248080808000410121040240024002402001280200220541046a2802004105762206200220062002491b2206450d00200641ffffff1f4b0d0120064105742207417f4c0d0141002d00fca3c680001a200741002802c8a3c68000118180808000002204450d020b20012001280204220841016a22073602042003410036020c200320043602082003200636020402400240200720012802084b0d00024002402002450d00200541046a220928020022064120490d0103402009200641606a36020020052005280200220641206a360200200341106a41086a220a200641086a290000370300200341106a41106a220b200641106a290000370300200341106a41186a220c200641186a290000370300200320062900003703100240200328020c22062003280204470d00200341046a200610a186808000200328020c21060b2003280208220420064105746a22072003290310370000200741086a200a290300370000200741106a200b290300370000200741186a200c2903003700002003200641016a36020c2002417f6a2202450d01200928020022064120490d020c000b0b2001200836020420002003290204370200200041086a200341046a41086a2802003602000c020b200328020421060b20004180808080783602002006450d00200441002802c0a3c68000118080808000000b200341306a2480808080000f0b10ae80808000000b4101200710b280808000000bda0401067f23808080800041c0006b220324808080800002400240024002400240200128020441146e2204200220042002491b22040d00410421050c010b200441e6cc99334b0d02200441146c2206417f4c0d0241002d00fca3c680001a200641002802c8a3c68000118180808000002205450d010b200341003602102003200536020c20032004360208024002402002450d00200341146a41017221050340200341146a2001109e8d80800020032d001422064105460d02200341286a410f6a22072005410f6a280000360000200341286a41086a2208200541086a290000370300200320052900003703280240200328021022042003280208470d00200341086a200410a586808000200328021021040b200328020c200441146c6a22042003290328370001200420063a0000200441096a2008290300370000200441106a20072800003600002003200328021041016a3602102002417f6a22020d000b0b20002003290208370200200041086a200341086a41086a2802003602000c030b2000418080808078360200024020032802102206450d00200328020c220721044100210503402007200541146c6a21020240024002400240024020042d00000e0400010102040b200441086a21020c020b200241086a21020c010b200241046a21020b2002280200450d00200228020441002802c0a3c68000118080808000000b200541016a2105200441146a21042006417f6a22060d000b0b2003280208450d02200328020c41002802c0a3c68000118080808000000c020b4104200610b280808000000b10ae80808000000b200341c0006a2480808080000beb0301097f23808080800041306b22032480808080000240024002400240200128020422044105762205200220052002491b22050d00410121060c010b200541ffffff1f4b0d0120054105742207417f4c0d0141002d00fca3c680001a200741002802c8a3c68000118180808000002206450d020b410021072003410036020c20032006360208200320053602040240024002402002450d0020012802002105034020044120490d022001200441606a22043602042001200541206a2208360200200341106a41086a2209200541086a290000370300200341106a41106a220a200541106a290000370300200341106a41186a220b200541186a29000037030020032005290000370310024020072003280204470d00200341046a200710a18680800020032802082106200328020c21070b200620074105746a22052003290310370000200541186a200b290300370000200541106a200a290300370000200541086a20092903003700002003200741016a220736020c200821052002417f6a22020d000b0b20002003290204370200200041086a200341046a41086a2802003602000c010b20004180808080783602002003280204450d00200641002802c0a3c68000118080808000000b200341306a2480808080000f0b10ae80808000000b4101200710b280808000000bb80302067f017e23808080800041106b2203248080808000024002400240024002400240200128020422044104762205200220052002491b22060d00410821070c010b200641ffffff3f4b0d0120064104742205417f4c0d0141002d00fca3c680001a200541002802c8a3c68000118180808000002207450d020b410021052003410036020c200320073602082003200636020402402002450d002001280200210603400240200441074b0d00200328020421080c050b2001200441786a22043602042001200641086a22073602002003280204210820044104490d042006290000210920012004417c6a22043602042001200741046a220636020020072800002107024020052008470d00200341046a200510a286808000200328020c21050b200328020820054104746a22052007360208200520093703002003200328020c41016a220536020c2002417f6a22020d000b0b20002003290204370200200041086a200341046a41086a2802003602000c030b10ae80808000000b4108200510b280808000000b20004180808080783602002008450d00200328020841002802c0a3c68000118080808000000b200341106a2480808080000bd70301057f23808080800041e0036b22032480808080000240024002400240200128020041046a28020041e8016e2204200220042002491b22040d00410821050c010b200441cbfbb4044b0d01200441e8016c2206417f4c0d0141002d00fca3c680001a200641002802c8a3c68000118180808000002205450d020b20012001280204220741016a22063602042003410036020c200320053602082003200436020402400240200620012802084b0d0002402002450d00200341106a41047221060340200341106a200110f58c80800020032802102205450d02200341fc016a200641e40110848e8080001a0240200328020c22042003280204470d00200341046a2004109786808000200328020c21040b2003280208200441e8016c6a22042005360200200441046a200341fc016a41e40110848e8080001a2003200328020c41016a36020c2002417f6a22020d000b2001280204417f6a21070b2001200736020420002003290204370200200041086a200341046a41086a2802003602000c010b2000418080808078360200200341046a108f878080002003280204450d00200328020841002802c0a3c68000118080808000000b200341e0036a2480808080000f0b10ae80808000000b4108200610b280808000000b9f0602077f027e23808080800041206b22032480808080004104210402400240024002400240024002400240200128020041046a280200410c6e2205200220052002491b2205450d00200541aad5aad5004b0d022005410c6c2206417f4c0d0241002d00fca3c680001a200641002802c8a3c68000118180808000002204450d010b20012001280204220741016a22063602042003410036021c2003200436021820032005360214200620012802084b0d032002450d050340200341086a200110bc8a80800020032802080d042001280200220641046a22072802002204200328020c2205490d040240024020050d00410121080c010b2005417f4c0d03200541002802c8a3c68000118180808000002208450d04200841002005108a8e8080001a200728020021040b0240200420054f0d00200841002802c0a3c68000118080808000000c050b200820062802002209200510848e80800021082007200420056b3602002006200920056a3602002005ad210a2008ad210b0240200328021c22042003280214470d00200341146a2004109986808000200328021c21040b20032802182004410c6c6a2204200a422086200b84370204200420053602002003200328021c41016a36021c2002417f6a2202450d050c000b0b4104200610b280808000000b10ae80808000000b4101200510b280808000000b20004180808080783602000240200328021c2205450d00200328021821022005410171210641002101024020054101460d002005417e7121042002210541002101034002402005280200450d00200541046a28020041002802c0a3c68000118080808000000b02402005410c6a280200450d00200541106a28020041002802c0a3c68000118080808000000b200541186a21052004200141026a2201470d000b0b2006450d0020022001410c6c6a2205280200450d00200528020441002802c0a3c68000118080808000000b2003280214450d02200328021841002802c0a3c68000118080808000000c020b2001280204417f6a21070b2001200736020420002003290214370200200041086a200341146a41086a2802003602000b200341206a2480808080000bb104010e7f23808080800041106b220324808080800002400240024002400240024020012802042204410c6e2205200220052002491b22050d00410421060c010b200541aad5aad5004b0d012005410c6c2207417f4c0d0141002d00fca3c680001a200741002802c8a3c68000118180808000002206450d020b410021072003410036020c200320063602082003200536020402402002450d002001280200210503400240200441074b0d00200328020421080c050b2001200441786a22043602042001200541086a22063602002003280204210820044104490d04200541076a2d00002109200541066a2d0000210a200541056a2d0000210b200541036a2d0000210c20052d0004210d20052d0002210e20052d0001210f20052d0000211020012004417c6a22043602042001200641046a220536020020062800002106024020072008470d00200341046a2007109986808000200328020c21070b20032802082007410c6c6a2207200e3a00022007200f3a0001200720103a000020072006360208200741036a200c3a00002007200d3a0004200741056a200b3a0000200741066a200a3a0000200741076a20093a00002003200328020c41016a220736020c2002417f6a22020d000b0b20002003290204370200200041086a200341046a41086a2802003602000c030b10ae80808000000b4104200710b280808000000b20004180808080783602002008450d00200328020841002802c0a3c68000118080808000000b200341106a2480808080000bf90401067f23808080800041c0006b220324808080800041042104024002400240200128020041046a28020041186e2205200220052002491b2205450d00200541d5aad52a4b0d01200541186c2206417f4c0d0141002d00fca3c680001a200641002802c8a3c68000118180808000002204450d020b20012001280204220741016a22063602042003410036020c2003200436020820032005360204024002400240200620012802084d0d0020004180808080783602000c010b024002402002450d00200341146a21040340200341106a200110fd8680800020032802102206418080808078460d02200341286a41106a2207200441106a280200360200200341286a41086a2208200441086a290200370300200320042902003703280240200328020c22052003280204470d00200341046a200510a686808000200328020c21050b2003280208200541186c6a22052003290328370204200520063602002005410c6a2008290300370200200541146a20072802003602002003200328020c41016a36020c2002417f6a22020d000b2001280204417f6a21070b2001200736020420002003290204370200200041086a200341046a41086a2802003602000c020b2000418080808078360200200328020c2204450d0020032802082105034002402005280200450d00200541046a28020041002802c0a3c68000118080808000000b02402005410c6a280200450d00200541106a28020041002802c0a3c68000118080808000000b200541186a21052004417f6a22040d000b0b2003280204450d00200328020841002802c0a3c68000118080808000000b200341c0006a2480808080000f0b10ae80808000000b4104200610b280808000000be10101037f0240024002402001280200220341046a28020022012002490d000240024020020d00410121040c010b2002417f4c0d02200241002802c8a3c68000118180808000002204450d03200441002002108a8e8080001a200341046a28020021010b024020012002490d00200420032802002205200210848e8080002104200341046a200120026b3602002003200520026a3602002000200236020020002002ad4220862004ad843702040f0b200441002802c0a3c68000118080808000000b20004180808080783602000f0b10ae80808000000b4101200210b280808000000bfb0101047f02400240200241ffffffff014b0d002001280200220341046a280200220420024103742201490d0002400240024002402002450d00200241ffffffff004b0d052001417f4c0d05200141002802c8a3c680001181808080000022050d014108200110b280808000000b410821050c010b200541002001108a8e8080002106200341046a28020022042001490d010b200520032802002206200110848e8080002105200341046a200420016b3602002003200620016a3602002000200236020020002002ad4220862005ad843702040f0b200641002802c0a3c68000118080808000000b20004180808080783602000f0b10ae80808000000bf90101047f23808080800041106b220324808080800002402001450d002001410c6c21040340024002402000280200418080808078470d00200341046a200041046a280200200041086a28020010b4828080000c010b200341046a2000280204200041086a28020010e6848080000b2003280208210502402002280200200228020822016b200328020c22064f0d0020022001200610b182808000200228020821010b200228020420016a2005200610848e8080001a2002200120066a36020802402003280204450d00200541002802c0a3c68000118080808000000b2000410c6a2100200441746a22040d000b0b200341106a2480808080000b8a0202057f017e23808080800041106b220324808080800002402001450d0020014103742104200228020821010340200341046a10b989808000200328020821050240200228020020016b200328020c22064f0d0020022001200610b182808000200228020821010b2002280204220720016a2005200610848e8080001a2002200120066a220636020802402003280204450d00200541002802c0a3c68000118080808000000b200029030021080240200228020020066b41074b0d0020022006410810b18280800020022802042107200228020821060b200041086a21002002200641086a2201360208200720066a2008370000200441786a22040d000b0b200341106a2480808080000bfd0101047f23808080800041106b220324808080800002402001450d00200141047421042000410c6a21010340200141786a280200210520032001417c6a28020022003602082003200341086a36020c2003410c6a200210c08a80800002402002280200200228020822066b20004f0d0020022006200010b182808000200228020821060b200228020420066a2005200010848e8080001a2002200620006a22003602080240200228020020006b41034b0d0020022000410410b182808000200228020821000b2002200041046a360208200228020420006a2001280200360000200141106a2101200441706a22040d000b0b200341106a2480808080000bab0201037f23808080800041206b22042480808080000240024002400240200141046a22050d00410121060c010b2005417f4c0d0141002d00fca3c680001a200541002802c8a3c68000118180808000002206450d020b20044100360214200420063602102004200536020c200420013602182004200441186a36021c2004411c6a2004410c6a10c08a8080000240200428020c200428021422056b20014f0d002004410c6a2005200110b182808000200428021421050b200428021020056a2000200110848e8080001a200428020c21002002200320042802102206200520016a41002802e0a1c680001186808080000002402000450d00200641002802c0a3c68000118080808000000b200441206a2480808080000f0b10ae80808000000b4101200510b280808000000b810201037f23808080800041206b22032480808080000240024002400240200241046a22040d00410121050c010b2004417f4c0d0141002d00fca3c680001a200441002802c8a3c68000118180808000002205450d020b20034100360214200320053602102003200436020c200320023602182003200341186a36021c2003411c6a2003410c6a10c08a8080000240200328020c200328021422046b20024f0d002003410c6a2004200210b182808000200328021421040b200328021020046a2001200210848e8080001a200041086a200420026a3602002000200329020c370200200341206a2480808080000f0b10ae80808000000b4101200410b280808000000bf60101047f23808080800041206b2203248080808000024002402002417f4c0d0041002d00fca3c680001a20024103742204410472220541002802c8a3c68000118180808000002206450d0120034100360214200320063602102003200536020c200320023602182003200341186a36021c2003411c6a2003410c6a10c08a8080000240200328020c200328021422026b20044f0d002003410c6a2002200410b182808000200328021421020b200328021020026a2001200410848e8080001a200041086a200220046a3602002000200329020c370200200341206a2480808080000f0b10ae80808000000b4101200510b280808000000b860201047f23808080800041206b220324808080800002400240024002402002410274220441046a22050d00410121060c010b2005417f4c0d0141002d00fca3c680001a200541002802c8a3c68000118180808000002206450d020b20034100360214200320063602102003200536020c200320023602182003200341186a36021c2003411c6a2003410c6a10c08a8080000240200328020c200328021422056b20044f0d002003410c6a2005200410b182808000200328021421050b200328021020056a2001200410848e8080001a200041086a200520046a3602002000200329020c370200200341206a2480808080000f0b10ae80808000000b4101200510b280808000000bc80401077f23808080800041c0006b2207248080808000200120042005200610e38d80800021060240024002400240024020032d00004102470d00200341086a28020021052002280200220841d4006a2802002109200328020421042008280250210a200128022c220841017621030240024020084101710d00200128020420012802282208200841284b22081b220b2003490d042001280200200120081b21084100210b0c010b200128020420012802282208200841284b1b220b2003490d04200b20034d0d05200741296a20012802002001200841284b1b220820036a2d000041f001713a00004101210b0b2007200b3a002820072003360224200720083602202007200a200741206a20042005200928021c1187808080000020022802002102200128020021082001280204210920012802282103200741206a41186a200741186a220a290000370300200741206a41106a200741106a220b290000370300200741206a41086a200741086a220c29000037030020072007290000370320200220082001200341284b220d1b20092003200d1b20042005200741206a10fd858080002001200610e08d808000200041003a0000200041196a200a290000370000200041116a200b290000370000200041096a200c290000370000200020072900003700010c010b200020022802002003200110ff858080002001200610e08d8080000b200741c0006a2480808080000f0b2003200b41ac95c68000109581808000000b2003200b41bc95c68000109581808000000b2003200b41cc95c6800010f980808000000beb0401097f23808080800041c0006b22062480808080002001280200220728020020032004200510e38d80800021050240024002400240024020022d00004102470d00200241086a280200210420012802042208280200220141d4006a2802002109200228020421032001280250210a2007280200220228022c220b410176210102400240200b4101710d0020022802042002280228220b200b41284b220b1b220c2001490d0420022802002002200b1b21024100210b0c010b20022802042002280228220b200b41284b1b220c2001490d04200c20014d0d05200641296a20022802002002200b41284b1b220220016a2d000041f001713a00004101210b0b2006200b3a002820062001360224200620023602202006200a200641206a20032004200928021c118780808000002008280200210b2007280200220228020021082002280204210920022802282101200641206a41186a200641186a220a290000370300200641206a41106a200641106a220c290000370300200641206a41086a200641086a220d29000037030020062006290000370320200b20082002200141284b220e1b20092001200e1b20032004200641206a10fc858080002007280200200510e08d808000200041003a0000200041196a200a290000370000200041116a200c290000370000200041096a200d290000370000200020062900003700010c010b20002001280204280200200220072802001080868080002007280200200510e08d8080000b200641c0006a2480808080000f0b2001200c41ac95c68000109581808000000b2001200c41bc95c68000109581808000000b2001200c41cc95c6800010f980808000000bc80401077f23808080800041c0006b2207248080808000200120042005200610e38d80800021060240024002400240024020032d00004102470d00200341086a28020021052002280200220841d4006a2802002109200328020421042008280250210a200128022c220841017621030240024020084101710d00200128020420012802282208200841284b22081b220b2003490d042001280200200120081b21084100210b0c010b200128020420012802282208200841284b1b220b2003490d04200b20034d0d05200741296a20012802002001200841284b1b220820036a2d000041f001713a00004101210b0b2007200b3a002820072003360224200720083602202007200a200741206a20042005200928021c1187808080000020022802002102200128020021082001280204210920012802282103200741206a41186a200741186a220a290000370300200741206a41106a200741106a220b290000370300200741206a41086a200741086a220c29000037030020072007290000370320200220082001200341284b220d1b20092003200d1b20042005200741206a10fc858080002001200610e08d808000200041003a0000200041196a200a290000370000200041116a200b290000370000200041096a200c290000370000200020072900003700010c010b20002002280200200320011080868080002001200610e08d8080000b200741c0006a2480808080000f0b2003200b41ac95c68000109581808000000b2003200b41bc95c68000109581808000000b2003200b41cc95c6800010f980808000000beb0401097f23808080800041c0006b22062480808080002001280200220728020020032004200510e38d80800021050240024002400240024020022d00004102470d00200241086a280200210420012802042208280200220141d4006a2802002109200228020421032001280250210a2007280200220228022c220b410176210102400240200b4101710d0020022802042002280228220b200b41284b220b1b220c2001490d0420022802002002200b1b21024100210b0c010b20022802042002280228220b200b41284b1b220c2001490d04200c20014d0d05200641296a20022802002002200b41284b1b220220016a2d000041f001713a00004101210b0b2006200b3a002820062001360224200620023602202006200a200641206a20032004200928021c118780808000002008280200210b2007280200220228020021082002280204210920022802282101200641206a41186a200641186a220a290000370300200641206a41106a200641106a220c290000370300200641206a41086a200641086a220d29000037030020062006290000370320200b20082002200141284b220e1b20092001200e1b20032004200641206a10fd858080002007280200200510e08d808000200041003a0000200041196a200a290000370000200041116a200c290000370000200041096a200d290000370000200020062900003700010c010b200020012802042802002002200728020010ff858080002007280200200510e08d8080000b200641c0006a2480808080000f0b2001200c41ac95c68000109581808000000b2001200c41bc95c68000109581808000000b2001200c41cc95c6800010f980808000000ba00301047f23808080800041f0086b2202248080808000200241e8016a2001280200220128020022032001280204220110c28c8080000240024002400240024020022802e80122044105470d002002410c6a410c6a200241e8016a410c6a290200370200200220022902ec013702102002410536020c0c010b200241e0086a41086a2205200241e8016a410c6a290200370300200220022902ec013703e00820024184076a41146a200241e8016a41146a41c80110848e8080001a20024184076a410c6a20052903003702002002200436028407200220022903e008370288072002410c6a20024184076a2003200110f48d808000200228020c22014105470d010b200241106a10de858080000c010b20024184076a41046a2002410c6a41046a41d80110848e8080001a2002200136028407200241e8016a20024184076a10b68a80800020022802e80122014108470d0120022802ec0110df858080000b41c0d3c2800041c2004184d4c2800010a181808000000b200041046a200241e8016a41046a41980510848e8080001a20002001360200200241f0086a2480808080000bc50101027f024002400240024020002802002201418080808078732202410220024104491b0e03030301000b0240024002402000280204220028020041fcffffff076a2202410320024105491b0e0404040102000b2000280204450d03200041086a28020041002802c0a3c68000118080808000000c030b2000280204450d02200041086a28020041002802c0a3c68000118080808000000c020b200010de858080000c010b2001450d01200028020421000b200041002802c0a3c68000118080808000000b0b8b0101017f0240024002400240200028020041fcffffff076a2201410320014105491b0e0403030102000b2000280204450d02200041086a28020041002802c0a3c68000118080808000000c020b2000280204450d01200041086a28020041002802c0a3c68000118080808000000c010b200010de858080000b200041002802c0a3c68000118080808000000b860101037f20012802042102200128020022012802042103200128020022012001280200220441016a36020002402004417f4a0d0000000b2000200336020820002001360204200041073602002000200229000037000c200041246a200241186a2900003700002000411c6a200241106a290000370000200041146a200241086a2900003700000ba00301047f23808080800041f0086b2202248080808000200241e8016a2001280200220128020022032001280204220110c28c8080000240024002400240024020022802e80122044105470d002002410c6a410c6a200241e8016a410c6a290200370200200220022902ec013702102002410536020c0c010b200241e0086a41086a2205200241e8016a410c6a290200370300200220022902ec013703e00820024184076a41146a200241e8016a41146a41c80110848e8080001a20024184076a410c6a20052903003702002002200436028407200220022903e008370288072002410c6a20024184076a2003200110f48d808000200228020c22014105470d010b200241106a10de858080000c010b20024184076a41046a2002410c6a41046a41d80110848e8080001a2002200136028407200241e8016a20024184076a10b78a80800020022802e80122014108470d0120022802ec0110df858080000b41c0d3c2800041c2004184d4c2800010a181808000000b200041046a200241e8016a41046a41980510848e8080001a20002001360200200241f0086a2480808080000be80201047f23808080800041106b220224808080800020012802082103200128020421040240024002400240024020012802000d00024020034100480d00200341f5ffffff074f0d0202402003410b6a417c7122050d00410421010c050b41002d00fca3c680001a200541002802c8a3c680001181808080000022010d044104200510b280808000000b41fc9bc68000412b2002410f6a41a89cc6800041b89cc68000108981808000000b20034120470d0120002004290000370001200041196a200441186a290000370000200041116a200441106a290000370000200041096a200441086a290000370000410121010c030b41e484c08000412b2002410f6a419085c08000419086c08000108981808000000b412020034190d1c2800010a281808000000b2001428180808010370200200141086a2004200310848e8080001a200041086a200336020020002001360204410021010b200020013a0000200241106a2480808080000b810401027f0240024002400240024020002d00002201417c6a41ff01712202410420024104491b0e0404010203000b0240200041dc006a2802004129490d00200041346a28020041002802c0a3c68000118080808000000b200028022c41002802c0a3c680001180808080000020014103460d0302400240024020010e020106000b2000280224220220022802002202417f6a36020020024101470d05200041246a21000c010b2000280204220220022802002202417f6a36020020024101470d04200041046a21000b200010e28a8080000c030b0240200041dc006a2802004129490d00200041346a28020041002802c0a3c68000118080808000000b02400240024020002d00040e020105000b200041286a2200280200220220022802002202417f6a36020020024101470d040c010b200041086a2200280200220220022802002202417f6a36020020024101470d030b200010e28a8080000f0b200041d4006a2802004129490d012000412c6a28020041002802c0a3c68000118080808000000f0b200028023041002802c0a3c680001180808080000020002d000422024103460d0002400240024020020e020103000b200041286a2200280200220220022802002202417f6a36020020024101470d020c010b200041086a2200280200220220022802002202417f6a36020020024101470d010b200010e28a8080000f0b0b6801017f024002400240024020002d00000e020103000b2000280224220120012802002201417f6a36020020014101470d02200041246a21000c010b2000280204220120012802002201417f6a36020020014101470d01200041046a21000b200010e28a8080000b0b7101017f024020002d000022014103460d0002400240024020010e020103000b2000280224220120012802002201417f6a36020020014101470d02200041246a21000c010b2000280204220120012802002201417f6a36020020014101470d01200041046a21000b200010e28a8080000b0b02000bdc0101017f0240024002400240024020002d00000e03000201040b20012d00000d03200041086a2802002202200141086a280200470d03200028020441086a200128020441086a200210888e808000450f0b20012d00004102460d010c020b20012d00004101470d01200041016a200141016a412010888e808000450f0b0240024020002d0001450d0020012d000141ff01710d010b200041286a2802002202200141286a280200470d01200028022441086a200128022441086a200210888e808000450f0b200041026a200141026a412010888e808000450f0b41000ba97407047f027e037f017e027f017e1b7f23808080800041d00f6b22052480808080002005200436020c200541e00d6a2002200310c28c80800002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024020052802e00d22064105470d00200541106a410c6a200541e00d6a410c6a290200370200200520052902e40d3702140c010b200541a0036a2207200541e00d6a410c6a290200370300200520052902e40d37039803200541800c6a41146a200541e00d6a41146a41c80110848e8080001a200541800c6a410c6a2007290300370200200520063602800c20052005290398033702840c200541106a200541800c6a2002200310f48d808000200528021022034105470d010b200541e00d6a41086a22032005411c6a290200370300200541e00d6a41186a2202200141086a290000370300200541e00d6a41206a2204200141106a290000370300200541e00d6a41286a2206200141186a290000370300200520052902143703e00d200520012900003703f00d41002d00fca3c680001a413041002802c8a3c680001181808080000022010d014104413010b280808000000b2005280228210220052802242106200528022021082005290218210920052802142107200541f0016a2005412c6a41a80110848e8080001a20052902d801210a20052802d401210b4104210c0240024002400240024002400240024020030e052d000301022d0b200541003602880e200541e00d6a200720072009a76a10f58d808000200541800c6a41186a200541e00d6a41186a290200370300200541800c6a41106a200541e00d6a41106a290200370300200541800c6a41086a200541e00d6a41086a290200370300200520052902e00d3703800c20052802800e210d20052902840e210e024020080d00024020024100480d00200241f5ffffff074f0d0a02402002410b6a417c7122010d004104210f0c2d0b41002d00fca3c680001a200141002802c8a3c6800011818080800000220f0d2c4104200110b280808000000b41fc9bc68000412b200541e00d6a41a89cc6800041b89cc68000108981808000000b20024120470d09200541106a41026a200641026a2d00003a0000200541e80d6a200641136a290000370300200541ed0d6a200641186a290000370000200520062f00003b01102005200629000b3703e00d200628000721022006280003210f410121100c2b0b200520023602f40d200520063602f00d200520083602ec0d200520093702e40d200520073602e00d200541e00d6a41186a200541f0016a41a80110848e8080002103200520013602d0074102210220052005410c6a3602d4072005200541e00d6a3602cc070240024020074102470d000c010b200541c00f6a41086a200541e00d6a41086a280200360200200520052902e00d3703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200410e98580800020052d00800c22024102460d0a200541fe0b6a20052d00830c3a0000200541f8086a41086a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c210620052802ec0d2108200221020b4102210420054184046a41026a200541fc0b6a41026a2d00003a0000200541e8036a41086a200541f8086a41086a290300370300200541e8036a41106a200541f8086a41106a290300370300200541e8036a41186a200541f8086a41186a280200360200200520052f01fc0b3b018404200520052903f8083703e80320084102470d020c030b20052902e401211120052802e001210f200520023602e80d200520063602e40d200520083602e00d200541ec0d6a200541f0016a41a80110848e80800021032005200a3702980f2005200b3602940f200520013602d0074102210220052005410c6a3602d4072005200541e00d6a3602cc070240024020084102470d000c010b200541c00f6a41086a200541e00d6a41086a280200360200200520052902e00d3703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200410e98580800020052d00800c22024102460d19200541fe0b6a20052d00830c3a0000200541f8086a41086a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c2106200221020b4102210420054194086a41026a200541fc0b6a41026a2d00003a0000200541f8076a41086a200541f8086a41086a290300370300200541f8076a41106a200541f8086a41106a290300370300200541f8076a41186a200541f8086a41186a280200360200200520052f01fc0b3b019408200520052903f8083703f80720052802ec0d4102470d030c040b200520023602e403200520063602e003200520083602dc03200541003602880e200541e00d6a200720072009a76a10f58d808000200541106a41086a2203200541e00d6a41106a290200370300200541106a41106a2202200541e00d6a41186a290200370300200541106a41186a2204200541800e6a290200370300200520052902e80d37031020052802e40d211220052802e00d211320052802880e210d200541800c6a41186a200141186a290000370300200541800c6a41106a200141106a290000370300200541800c6a41086a200141086a290000370300200520012900003703800c200541e00d6a200541800c6a200541dc036a200528020c10e985808000024020052d00e00d22104102460d00200541d4036a41026a20052d00e30d3a0000200541b8036a41086a200541f40d6a290200370300200541b8036a41106a200541fc0d6a29020037030020054198036a41086a200329030037030020054198036a41106a200229030037030020054198036a41186a2004290300370300200520052f00e10d3b01d403200520052902ec0d3703b80320052005290310370398032009422088a7211420052802e40d210f20052802e80d21024106210c0c2a0b20052802e40d2101200041083a000020002001360204200d4129490d2a201341002802c0a3c68000118080808000000c2a0b200541c00f6a41086a200541ec0d6a220441086a280200360200200520042902003703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200528020c10e98580800020052d00800c22044102460d07200541fe0b6a20052d00830c3a000020054180096a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c2108200421040b41022107200541a4046a41026a200541fc0b6a41026a2d00003a000020054188046a41086a200541f8086a41086a29030037030020054188046a41106a200541f8086a41106a29030037030020054188046a41186a200541f8086a41186a280200360200200520052f01fc0b3b01a404200520052903f808370388040240024020052802f80d4102470d000c010b200541c00f6a41086a200341086a280200360200200520032902003703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200528020c10e98580800020052d00800c22034102460d08200541fe0b6a20052d00830c3a000020054180096a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c210d200321070b41022103200541c4046a41026a200541fc0b6a41026a2d00003a0000200541a8046a41086a200541f8086a41086a290300370300200541a8046a41106a200541f8086a41106a290300370300200541a8046a41186a200541f8086a41186a280200360200200520052f01fc0b3b01c404200520052903f8083703a8040240024020052802840e4102470d000c010b200541c00f6a41086a200541840e6a220341086a280200360200200520032902003703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200528020c10e98580800020052d00800c22014102460d09200541fe0b6a20052d00830c3a000020054180096a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c2113200121030b200541e4046a41026a200541fc0b6a41026a2d00003a0000200541c8046a41086a200541f8086a41086a290300370300200541c8046a41106a200541f8086a41106a290300370300200541c8046a41186a200541f8086a41186a280200360200200520052f01fc0b3b01e404200520052903f8083703c804200541800c6a200541cc076a410410ea8580800020052d00800c22014103460d0920054184056a41026a20052d00830c3a0000200541e8046a41086a200541800c6a41106a290200370300200541e8046a41106a200541800c6a41186a290200370300200541e8046a41186a200541a00c6a220f280200360200200520052f00810c3b018405200520052902880c3703e80420052802840c210c200541800c6a200541cc076a410510ea8580800020052d00800c22144103460d0a200541a4056a41026a20052d00830c3a000020054188056a41086a200541800c6a41106a221229020037030020054188056a41106a200541800c6a41186a221029020037030020054188056a41186a200f280200360200200520052f00810c3b01a405200520052902880c3703880520052802840c210f200541800c6a200541cc076a410610ea8580800020052d00800c22154103460d0b200541c4056a41026a20052d00830c3a0000200541a8056a41086a2012290200370300200541a8056a41106a2010290200370300200541a8056a41186a200541a00c6a2212280200360200200520052f00810c3b01c405200520052902880c3703a80520052802840c2116200541800c6a200541cc076a410710ea8580800020052d00800c22174103460d0c200541e4056a41026a20052d00830c3a0000200541c8056a41086a200541800c6a41106a2210290200370300200541c8056a41106a200541800c6a41186a2218290200370300200541c8056a41186a2012280200360200200520052f00810c3b01e405200520052902880c3703c80520052802840c2119200541800c6a200541cc076a410810ea8580800020052d00800c221a4103460d0d20054184066a41026a20052d00830c3a0000200541e8056a41086a2010290200370300200541e8056a41106a2018290200370300200541e8056a41186a200541a00c6a2212280200360200200520052f00810c3b018406200520052902880c3703e80520052802840c2118200541800c6a200541cc076a410910ea8580800020052d00800c221b4103460d0e200541a4066a41026a20052d00830c3a000020054188066a41086a200541800c6a41106a221029020037030020054188066a41106a200541800c6a41186a221c29020037030020054188066a41186a2012280200360200200520052f00810c3b01a406200520052902880c3703880620052802840c211d200541800c6a200541cc076a410a10ea8580800020052d00800c221e4103460d0f200541c4066a41026a20052d00830c3a0000200541a8066a41086a2010290200370300200541a8066a41106a201c290200370300200541a8066a41186a200541a00c6a2212280200360200200520052f00810c3b01c406200520052902880c3703a80620052802840c211c200541800c6a200541cc076a410b10ea8580800020052d00800c221f4103460d10200541e4066a41026a20052d00830c3a0000200541c8066a41086a200541800c6a41106a2210290200370300200541c8066a41106a200541800c6a41186a2220290200370300200541c8066a41186a2012280200360200200520052f00810c3b01e406200520052902880c3703c80620052802840c2121200541800c6a200541cc076a410c10ea8580800020052d00800c22224103460d1120054184076a41026a20052d00830c3a0000200541e8066a41086a2010290200370300200541e8066a41106a2020290200370300200541e8066a41186a200541a00c6a2212280200360200200520052f00810c3b018407200520052902880c3703e80620052802840c2120200541800c6a200541cc076a410d10ea8580800020052d00800c22234103460d12200541a4076a41026a20052d00830c3a000020054188076a41086a200541800c6a41106a221029020037030020054188076a41106a200541800c6a41186a222429020037030020054188076a41186a2012280200360200200520052f00810c3b01a407200520052902880c3703880720052802840c2125200541800c6a200541cc076a410e10ea8580800020052d00800c22264103460d13200541c8076a41026a20052d00830c3a0000200541a8076a41086a2010290200370300200541a8076a41106a2024290200370300200541a8076a41186a200541a00c6a2212280200360200200520052f00810c3b01c807200520052902880c3703a80720052802840c2124200541800c6a200541cc076a410f10ea8580800002400240024020052d00800c22274103460d00200541f4076a41026a222820052d00830c3a0000200541d8076a41086a2229200541800c6a41106a290200370300200541d8076a41106a222a200541800c6a41186a290200370300200541d8076a41186a222b2012280200360200200520052f00810c3b01f407200520052902880c3703d80720052802840c212c41002d00fca3c680001a41c00441002802c8a3c68000118180808000002212450d17201220023a0000201220052f0184043b000120122006360204201220052903e803370208201220043a0024201220052f01a4043b002541032110201241036a20054184046a41026a2d00003a0000201241106a200541e8036a41086a290300370200201241186a200541e8036a41106a290300370200201241206a200541e8036a41186a280200360200201241276a200541a4046a41026a2d00003a000020122008360228201220073a00482012200d36024c201220052903880437022c201241346a20054188046a41086a2903003702002012413c6a20054188046a41106a290300370200201241c4006a20054188046a41186a280200360200201220052f01c4043b0049201241cb006a200541c4046a41026a2d00003a0000201220052903a804370250201241d8006a200541a8046a41086a290300370200201241e0006a200541a8046a41106a290300370200201241e8006a200541a8046a41186a280200360200201220033a006c20122013360270201220013a009001201220052f01e4043b006d201241ef006a200541e4046a41026a2d00003a00002012418c016a200541c8046a41186a28020036020020124184016a200541c8046a41106a290300370200201241fc006a200541c8046a41086a290300370200201220052903c804370274201220052f0184053b00910120124193016a20054184056a41026a2d00003a00002012200c36029401201241b0016a200541e8046a41186a280200360200201241a8016a200541e8046a41106a290300370200201241a0016a200541e8046a41086a290300370200201220052903e80437029801201220143a00b401201241b7016a200541a4056a41026a2d00003a0000201220052f01a4053b00b5012012200f3602b801201241d4016a20054188056a41186a280200360200201241cc016a20054188056a41106a290300370200201241c4016a20054188056a41086a29030037020020122005290388053702bc01201220153a00d801201241db016a200541c4056a41026a2d00003a0000201220052f01c4053b00d901201220163602dc01201241f8016a200541a8056a41186a280200360200201241f0016a200541a8056a41106a290300370200201241e8016a200541a8056a41086a290300370200201220052903a8053702e001201220173a00fc01201241ff016a200541e4056a41026a2d00003a0000201220052f01e4053b00fd0120122019360280022012419c026a200541c8056a41186a28020036020020124194026a200541c8056a41106a2903003702002012418c026a200541c8056a41086a290300370200201220052903c805370284022012201a3a00a002201241a3026a20054184066a41026a2d00003a0000201220052f0184063b00a102201220183602a402201241c0026a200541e8056a41186a280200360200201241b8026a200541e8056a41106a290300370200201241b0026a200541e8056a41086a290300370200201220052903e8053702a8022012201b3a00c402201241c7026a200541a4066a41026a2d00003a0000201220052f01a4063b00c5022012201d3602c802201241e4026a20054188066a41186a280200360200201241dc026a20054188066a41106a290300370200201241d4026a20054188066a41086a29030037020020122005290388063702cc022012201e3a00e802201241eb026a200541c4066a41026a2d00003a0000201220052f01c4063b00e9022012201c3602ec0220124188036a200541a8066a41186a28020036020020124180036a200541a8066a41106a290300370200201241f8026a200541a8066a41086a290300370200201220052903a8063702f0022012201f3a008c032012418f036a200541e4066a41026a2d00003a0000201220052f01e4063b008d032012202136029003201241ac036a200541c8066a41186a280200360200201241a4036a200541c8066a41106a2903003702002012419c036a200541c8066a41086a290300370200201220052903c80637029403201220223a00b003201241b3036a20054184076a41026a2d00003a0000201220052f0184073b00b103201220203602b403201241d0036a200541e8066a41186a280200360200201241c8036a200541e8066a41106a290300370200201241c0036a200541e8066a41086a290300370200201220052903e8063702b803201220233a00d403201241d7036a200541a4076a41026a2d00003a0000201220052f01a4073b00d503201220253602d803201241f4036a20054188076a41186a280200360200201241ec036a20054188076a41106a290300370200201241e4036a20054188076a41086a29030037020020122005290388073702dc03201220263a00f803201241fb036a200541c8076a41026a2d00003a0000201220052f01c8073b00f903201220243602fc0320124198046a200541a8076a41186a28020036020020124190046a200541a8076a41106a29030037020020124188046a200541a8076a41086a290300370200201220052903a80737028004201220273a009c042012419f046a20282d00003a0000201220052f01f4073b009d042012202c3602a004201241bc046a202b280200360200201241b4046a202a290300370200201241ac046a2029290300370200201220052903d8073702a404200b4102470d010c020b20052802840c2101200041083a0000200020013602040c2a0b2005200a3702fc082005200b3602f808200541800c6a200541f8086a10e285808000200541da076a20052d00830c3a0000200541186a200541940c6a290200370300200541206a2005419c0c6a290200370300200520052f00810c3b01d8072005200529028c0c37031020052d00800c211020052802840c210f20052802880c210220052802a40c211420052802a80c21130b200541d4036a41026a200541d8076a41026a2d00003a0000200541b8036a41086a200541106a41086a290300370300200541b8036a41106a200541106a41106a290300370300200520052f01d8073b01d403200520052903103703b8034107210c0c270b200541c00f6a41086a200341086a280200360200200520032902003703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200528020c10e98580800020052d00800c22034102460d15200541fe0b6a20052d00830c3a000020054180096a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c2108200321040b41022103200541b4086a41026a200541fc0b6a41026a2d00003a000020054198086a41086a200541f8086a41086a29030037030020054198086a41106a200541f8086a41106a29030037030020054198086a41186a200541f8086a41186a280200360200200520052f01fc0b3b01b408200520052903f808370398080240024020052802f80d4102470d000c010b200541c00f6a41086a200541e00d6a41186a220341086a280200360200200520032902003703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200528020c10e98580800020052d00800c22034102460d16200541fe0b6a20052d00830c3a000020054180096a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c2110200321030b4102210d200541d4086a41026a200541fc0b6a41026a2d00003a0000200541b8086a41086a200541f8086a41086a290300370300200541b8086a41106a200541f8086a41106a290300370300200541b8086a41186a200541f8086a41186a280200360200200520052f01fc0b3b01d408200520052903f8083703b8080240024020052802840e4102470d000c010b200541c00f6a41086a200541840e6a220d41086a2802003602002005200d2902003703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200528020c10e98580800020052d00800c220d4102460d17200541fe0b6a20052d00830c3a000020054180096a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c210b200d210d0b41022114200541f4086a41026a200541fc0b6a41026a2d00003a0000200541d8086a41086a200541f8086a41086a290300370300200541d8086a41106a200541f8086a41106a290300370300200541d8086a41186a200541f8086a41186a280200360200200520052f01fc0b3b01f408200520052903f8083703d8080240024020052802900e4102470d000c010b200541c00f6a41086a200541900e6a221341086a280200360200200520132902003703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200528020c10e98580800020052d00800c22134102460d18200541fe0b6a20052d00830c3a000020054180096a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c2101201321140b200541b4096a41026a200541fc0b6a41026a2d00003a000020054198096a41086a200541f8086a41086a29030037030020054198096a41106a200541f8086a41106a29030037030020054198096a41186a200541f8086a41186a280200360200200520052f01fc0b3b01b409200520052903f80837039809200541800c6a200541cc076a410510eb8580800020052d00800c22154103460d18200541d4096a41026a20052d00830c3a0000200541b8096a41086a200541800c6a41106a290200370300200541b8096a41106a200541800c6a41186a290200370300200541b8096a41186a200541a00c6a2213280200360200200520052f00810c3b01d409200520052902880c3703b80920052802840c2116200541800c6a200541cc076a410610eb8580800020052d00800c22174103460d19200541f4096a41026a20052d00830c3a0000200541d8096a41086a200541800c6a41106a2212290200370300200541d8096a41106a200541800c6a41186a220c290200370300200541d8096a41186a2013280200360200200520052f00810c3b01f409200520052902880c3703d80920052802840c2118200541800c6a200541cc076a410710eb8580800020052d00800c22194103460d1a200541940a6a41026a20052d00830c3a0000200541f8096a41086a2012290200370300200541f8096a41106a200c290200370300200541f8096a41186a200541a00c6a2213280200360200200520052f00810c3b01940a200520052902880c3703f80920052802840c211a200541800c6a200541cc076a410810eb8580800020052d00800c221b4103460d1b200541b40a6a41026a20052d00830c3a0000200541980a6a41086a200541800c6a41106a2212290200370300200541980a6a41106a200541800c6a41186a220c290200370300200541980a6a41186a2013280200360200200520052f00810c3b01b40a200520052902880c3703980a20052802840c211c200541800c6a200541cc076a410910eb8580800020052d00800c221d4103460d1c200541d40a6a41026a20052d00830c3a0000200541b80a6a41086a2012290200370300200541b80a6a41106a200c290200370300200541b80a6a41186a200541a00c6a2213280200360200200520052f00810c3b01d40a200520052902880c3703b80a20052802840c211e200541800c6a200541cc076a410a10eb8580800020052d00800c221f4103460d1d200541f40a6a41026a20052d00830c3a0000200541d80a6a41086a200541800c6a41106a2212290200370300200541d80a6a41106a200541800c6a41186a220c290200370300200541d80a6a41186a2013280200360200200520052f00810c3b01f40a200520052902880c3703d80a20052802840c2120200541800c6a200541cc076a410b10eb8580800020052d00800c22214103460d1e200541940b6a41026a20052d00830c3a0000200541f80a6a41086a2012290200370300200541f80a6a41106a200c290200370300200541f80a6a41186a200541a00c6a2213280200360200200520052f00810c3b01940b200520052902880c3703f80a20052802840c2122200541800c6a200541cc076a410c10eb8580800020052d00800c22234103460d1f200541b40b6a41026a20052d00830c3a0000200541980b6a41086a200541800c6a41106a2212290200370300200541980b6a41106a200541800c6a41186a220c290200370300200541980b6a41186a2013280200360200200520052f00810c3b01b40b200520052902880c3703980b20052802840c2124200541800c6a200541cc076a410d10eb8580800020052d00800c22254103460d20200541d40b6a41026a20052d00830c3a0000200541b80b6a41086a2012290200370300200541b80b6a41106a200c290200370300200541b80b6a41186a200541a00c6a2213280200360200200520052f00810c3b01d40b200520052902880c3703b80b20052802840c2126200541800c6a200541cc076a410e10eb8580800020052d00800c22274103460d21200541f80b6a41026a20052d00830c3a0000200541d80b6a41086a200541800c6a41106a2212290200370300200541d80b6a41106a200541800c6a41186a220c290200370300200541d80b6a41186a2013280200360200200520052f00810c3b01f80b200520052902880c3703d80b20052802840c2128200541800c6a200541cc076a410f10eb8580800002400240024020052d00800c22294103460d00200541f4076a41026a20052d00830c3a0000200541d8076a41086a2012290200370300200541d8076a41106a200c290200370300200541d8076a41186a200541800c6a41206a280200360200200520052f00810c3b01f407200520052902880c3703d80720052802840c212a41002d00fca3c680001a41c00441002802c8a3c68000118180808000002213450d252009422088a72112201320023a0000201320052f0194083b000120132006360204201320052903f807370208201320043a0024201320052f01b4083b00254103210c201341036a20054194086a41026a2d00003a0000201341106a200541f8076a41086a290300370200201341186a200541f8076a41106a290300370200201341206a200541f8076a41186a280200360200201341276a200541b4086a41026a2d00003a000020132008360228201320033a00482013201036024c201320052903980837022c201341346a20054198086a41086a2903003702002013413c6a20054198086a41106a290300370200201341c4006a20054198086a41186a280200360200201320052f01d4083b0049201341cb006a200541d4086a41026a2d00003a0000201320052903b808370250201341d8006a200541b8086a41086a290300370200201341e0006a200541b8086a41106a290300370200201341e8006a200541b8086a41186a2802003602002013200d3a006c2013200b360270201320143a009001201320052f01f4083b006d201341ef006a200541f4086a41026a2d00003a00002013418c016a200541d8086a41186a28020036020020134184016a200541d8086a41106a290300370200201341fc006a200541d8086a41086a290300370200201320052903d808370274201320052f01b4093b00910120134193016a200541b4096a41026a2d00003a00002013200136029401201341b0016a20054198096a41186a280200360200201341a8016a20054198096a41106a290300370200201341a0016a20054198096a41086a290300370200201320052903980937029801201320153a00b401201341b7016a200541d4096a41026a2d00003a0000201320052f01d4093b00b501201320163602b801201341d4016a200541b8096a41186a280200360200201341cc016a200541b8096a41106a290300370200201341c4016a200541b8096a41086a290300370200201320052903b8093702bc01201320173a00d801201341db016a200541f4096a41026a2d00003a0000201320052f01f4093b00d901201320183602dc01201341f8016a200541d8096a41186a280200360200201341f0016a200541d8096a41106a290300370200201341e8016a200541d8096a41086a290300370200201320052903d8093702e001201320193a00fc01201341ff016a200541940a6a41026a2d00003a0000201320052f01940a3b00fd012013201a360280022013419c026a200541f8096a41186a28020036020020134194026a200541f8096a41106a2903003702002013418c026a200541f8096a41086a290300370200201320052903f809370284022013201b3a00a002201341a3026a200541b40a6a41026a2d00003a0000201320052f01b40a3b00a1022013201c3602a402201341c0026a200541980a6a41186a280200360200201341b8026a200541980a6a41106a290300370200201341b0026a200541980a6a41086a290300370200201320052903980a3702a8022013201d3a00c402201341c7026a200541d40a6a41026a2d00003a0000201320052f01d40a3b00c5022013201e3602c802201341e4026a200541b80a6a41186a280200360200201341dc026a200541b80a6a41106a290300370200201341d4026a200541b80a6a41086a290300370200201320052903b80a3702cc022013201f3a00e802201341eb026a200541f40a6a41026a2d00003a0000201320052f01f40a3b00e902201320203602ec0220134188036a200541d80a6a41186a28020036020020134180036a200541d80a6a41106a290300370200201341f8026a200541d80a6a41086a290300370200201320052903d80a3702f002201320213a008c032013418f036a200541940b6a41026a2d00003a0000201320052f01940b3b008d032013202236029003201341ac036a200541f80a6a41186a280200360200201341a4036a200541f80a6a41106a2903003702002013419c036a200541f80a6a41086a290300370200201320052903f80a37029403201320233a00b003201341b3036a200541b40b6a41026a2d00003a0000201320052f01b40b3b00b103201320243602b403201341d0036a200541980b6a41186a280200360200201341c8036a200541980b6a41106a290300370200201341c0036a200541980b6a41086a290300370200201320052903980b3702b803201320253a00d403201341d7036a200541d40b6a41026a2d00003a0000201320052f01d40b3b00d503201320263602d803201341f4036a200541b80b6a41186a280200360200201341ec036a200541b80b6a41106a290300370200201341e4036a200541b80b6a41086a290300370200201320052903b80b3702dc03201320273a00f803201341fb036a200541f80b6a41026a2d00003a0000201320052f01f80b3b00f903201320283602fc0320134198046a200541d80b6a41186a28020036020020134190046a200541d80b6a41106a29030037020020134188046a200541d80b6a41086a290300370200201320052903d80b37028004201320293a009c042013419f046a200541f4076a41026a2d00003a0000201320052f01f4073b009d042013202a3602a004201341bc046a200541d8076a41186a280200360200201341b4046a200541d8076a41106a290300370200201341ac046a200541d8076a41086a290300370200201320052903d8073702a404200541003602a80c200541800c6a200720072009a76a10f58d808000200541106a41186a2201200541800c6a41186a2202290200370300200541106a41106a2203200541800c6a41106a290200370300200541106a41086a2204200541800c6a41086a290200370300200520052902800c37031020052802a00c210d20052902a40c210e200f4102470d010c020b20052802840c2101200041083a0000200020013602040c280b200520113702dc072005200f3602d807200541800c6a200541d8076a10e285808000200541c00f6a41026a20052d00830c3a0000200541fc0b6a41026a200541870c6a2d00003a0000200541f8086a41086a2002290200370300200541f8086a41106a200541a00c6a290200370300200520052f00810c3b01c00f200520052f00850c3b01fc0b200520052902900c3703f80820052d00800c210c20052d00840c211020052802880c210f200528028c0c210220052802a80c21140b20054198036a41186a200129030037030020054198036a41106a200329030037030020054198036a41086a2004290300370300200541d8036a41026a200541c00f6a41026a2d00003a0000200541d4036a41026a200541fc0b6a41026a2d00003a0000200541b8036a41106a200541f8086a41106a290300370300200541b8036a41086a200541f8086a41086a2903003703002005200529031037039803200520052f01c00f3b01d803200520052f01fc0b3b01d403200520052903f8083703b8030c250b200120052903e00d370200200141286a2006290300370200200141206a2004290300370200200141186a2002290300370200200141106a200541e00d6a41106a290300370200200141086a2003290300370200200041083a0000200020013602040c250b41e484c08000412b200541e00d6a419085c08000419086c08000108981808000000b412020024190d1c2800010a281808000000b20052802840c2101200041083a0000200020013602040c220b20052802840c2101200041083a0000200020013602040c210b20052802840c2101200041083a0000200020013602040c200b20052802840c2101200041083a0000200020013602040c1f0b20052802840c2101200041083a0000200020013602040c1e0b20052802840c2101200041083a0000200020013602040c1d0b20052802840c2101200041083a0000200020013602040c1c0b20052802840c2101200041083a0000200020013602040c1b0b20052802840c2101200041083a0000200020013602040c1a0b20052802840c2101200041083a0000200020013602040c190b20052802840c2101200041083a0000200020013602040c180b20052802840c2101200041083a0000200020013602040c170b20052802840c2101200041083a0000200020013602040c160b20052802840c2101200041083a0000200020013602040c150b20052802840c2101200041083a0000200020013602040c140b410441c00410b280808000000b20052802840c2101200041083a0000200020013602040c120b20052802840c2101200041083a0000200020013602040c110b20052802840c2101200041083a0000200020013602040c100b20052802840c2101200041083a0000200020013602040c0f0b20052802840c2101200041083a0000200020013602040c0e0b20052802840c2101200041083a0000200020013602040c0d0b20052802840c2101200041083a0000200020013602040c0c0b20052802840c2101200041083a0000200020013602040c0b0b20052802840c2101200041083a0000200020013602040c0a0b20052802840c2101200041083a0000200020013602040c090b20052802840c2101200041083a0000200020013602040c080b20052802840c2101200041083a0000200020013602040c070b20052802840c2101200041083a0000200020013602040c060b20052802840c2101200041083a0000200020013602040c050b20052802840c2101200041083a0000200020013602040c040b410441c00410b280808000000b200f428180808010370200200f41086a2006200210848e8080001a410021100b2009422088a7211220054198036a41186a200541800c6a41186a29030037030020054198036a41106a200541800c6a41106a29030037030020054198036a41086a200541800c6a41086a290300370300200541d4036a41026a200541106a41026a2d00003a0000200541b8036a41086a200541e00d6a41086a290300370300200541b8036a41106a200541e00d6a41106a290300370300200520052903800c37039803200520052f01103b01d403200520052903e00d3703b8034105210c0b200020052f01d8033b0001200020052f01d4033b0005200020052903b8033702102000200529039803370234200041036a200541d8036a41026a2d00003a0000200041076a200541d4036a41026a2d00003a0000200041186a200541b8036a41086a290300370200200041206a200541b8036a41106a2903003702002000413c6a20054198036a41086a290300370200200041c4006a20054198036a41106a290300370200200041cc006a20054198036a41186a2903003702002000200e3702582000200d3602542000200f3602082000200236020c200020143602282000201336022c20002012360230200020103a00042000200c3a00000b200541d00f6a2480808080000ba30801057f23808080800041e0016b2204248080808000200241086a28020021052002280204210602400240024002400240024020022802000d00024002400240024020050d00410121020c010b20054120460d012005417f4c0d0441002d00fca3c680001a200541002802c8a3c68000118180808000002202450d050b20022006200510848e808000210341002d00fca3c680001a413041002802c8a3c680001181808080000022020d014104413010b280808000000b2004411c6a41026a200641026a2d00003a0000200441086a2006410f6a290000370300200441106a200641176a290000370300200441186a2006411f6a2d00003a0000200420062f00003b011c2004200629000737030020062800032102410121060c050b2002200536020c2002200336020820022005360204200241888080807836020020022001290000370010200241186a200141086a290000370000200241206a200141106a290000370000200241286a200141186a2900003700000c030b20044180016a41186a200141186a29000037030020044180016a41106a200141106a29000037030020044180016a41086a200141086a2900003703002004200129000037038001200441206a20044180016a20062005200310e885808000024020042d002022054108460d00200441dc016a41026a20042d00233a0000200420042f00213b01dc012004280224210720044180016a200441286a41d80010848e8080001a02400240200341186a2802002201450d0020032001417f6a36021841002106200341146a22012001280200220141016a22024100200328020c220820022008491b6b360200200341106a28020020014102746a2802002202200328020822014f0d01200328020420024107746a220141046a200120012d00004108461b10e385808000200120053a0004200141083a000020012007360208200120042f01dc013b0005200141076a200441de016a2d00003a00002001410c6a20044180016a41d80010848e8080001a0c060b0240200328020822012003280200470d0020032001109e86808000200328020821010b200328020420014107746a220120053a0004200141083a0000200120042f01dc013b000520012007360208200141076a200441de016a2d00003a00002001410c6a20044180016a41d80010848e8080001a20032003280208220241016a360208410021060c050b2002200141d4d7c2800010f980808000000b200428022421020c020b10ae80808000000b4101200510b280808000000b200041023a0000200020023602040c010b200020042f011c3b00012000200429030037020820002002360204200020063a0000200041036a2004411e6a2d00003a0000200041106a200441086a290300370200200041186a200441106a290300370200200041206a200441186a2802003602000b200441e0016a2480808080000bd40201017f23808080800041d0006b220324808080800002402002410f4b0d000240024020012802002002410c6c6a22022802004102470d00200041023a00000c010b200341086a200241086a28020036020020032002290200370300200341306a41086a2001280204220241086a290000370300200341306a41106a200241106a290000370300200341306a41186a200241186a290000370300200320022900003703302003410c6a200341306a2003200128020828020010e985808000024020032d000c4102460d002000200329020c370200200041206a2003410c6a41206a280200360200200041186a2003410c6a41186a290200370200200041106a2003410c6a41106a290200370200200041086a2003410c6a41086a2902003702000c010b20002003280210360204200041033a00000b200341d0006a2480808080000f0b2002411041b0d1c2800010f980808000000bd40201017f23808080800041d0006b220324808080800002402002410f4b0d000240024020012802002002410c6c6a22022802004102470d00200041023a00000c010b200341086a200241086a28020036020020032002290200370300200341306a41086a2001280204220241086a290000370300200341306a41106a200241106a290000370300200341306a41186a200241186a290000370300200320022900003703302003410c6a200341306a2003200128020828020010e985808000024020032d000c4102460d002000200329020c370200200041206a2003410c6a41206a280200360200200041186a2003410c6a41186a290200370200200041106a2003410c6a41106a290200370200200041086a2003410c6a41086a2902003702000c010b20002003280210360204200041033a00000b200341d0006a2480808080000f0b2002411041a0d1c2800010f980808000000ba97407047f027e037f017e027f017e1b7f23808080800041d00f6b22052480808080002005200436020c200541e00d6a2002200310c28c80800002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024020052802e00d22064105470d00200541106a410c6a200541e00d6a410c6a290200370200200520052902e40d3702140c010b200541a0036a2207200541e00d6a410c6a290200370300200520052902e40d37039803200541800c6a41146a200541e00d6a41146a41c80110848e8080001a200541800c6a410c6a2007290300370200200520063602800c20052005290398033702840c200541106a200541800c6a2002200310f48d808000200528021022034105470d010b200541e00d6a41086a22032005411c6a290200370300200541e00d6a41186a2202200141086a290000370300200541e00d6a41206a2204200141106a290000370300200541e00d6a41286a2206200141186a290000370300200520052902143703e00d200520012900003703f00d41002d00fca3c680001a413041002802c8a3c680001181808080000022010d014104413010b280808000000b2005280228210220052802242106200528022021082005290218210920052802142107200541f0016a2005412c6a41a80110848e8080001a20052902d801210a20052802d401210b4104210c0240024002400240024002400240024020030e052d000301022d0b200541003602880e200541e00d6a200720072009a76a10f58d808000200541800c6a41186a200541e00d6a41186a290200370300200541800c6a41106a200541e00d6a41106a290200370300200541800c6a41086a200541e00d6a41086a290200370300200520052902e00d3703800c20052802800e210d20052902840e210e024020080d00024020024100480d00200241f5ffffff074f0d0a02402002410b6a417c7122010d004104210f0c2d0b41002d00fca3c680001a200141002802c8a3c6800011818080800000220f0d2c4104200110b280808000000b41fc9bc68000412b200541e00d6a41a89cc6800041b89cc68000108981808000000b20024120470d09200541106a41026a200641026a2d00003a0000200541e80d6a200641136a290000370300200541ed0d6a200641186a290000370000200520062f00003b01102005200629000b3703e00d200628000721022006280003210f410121100c2b0b200520023602f40d200520063602f00d200520083602ec0d200520093702e40d200520073602e00d200541e00d6a41186a200541f0016a41a80110848e8080002103200520013602d0074102210220052005410c6a3602d4072005200541e00d6a3602cc070240024020074102470d000c010b200541c00f6a41086a200541e00d6a41086a280200360200200520052902e00d3703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200410ed8580800020052d00800c22024102460d0a200541fe0b6a20052d00830c3a0000200541f8086a41086a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c210620052802ec0d2108200221020b4102210420054184046a41026a200541fc0b6a41026a2d00003a0000200541e8036a41086a200541f8086a41086a290300370300200541e8036a41106a200541f8086a41106a290300370300200541e8036a41186a200541f8086a41186a280200360200200520052f01fc0b3b018404200520052903f8083703e80320084102470d020c030b20052902e401211120052802e001210f200520023602e80d200520063602e40d200520083602e00d200541ec0d6a200541f0016a41a80110848e80800021032005200a3702980f2005200b3602940f200520013602d0074102210220052005410c6a3602d4072005200541e00d6a3602cc070240024020084102470d000c010b200541c00f6a41086a200541e00d6a41086a280200360200200520052902e00d3703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200410ed8580800020052d00800c22024102460d19200541fe0b6a20052d00830c3a0000200541f8086a41086a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c2106200221020b4102210420054194086a41026a200541fc0b6a41026a2d00003a0000200541f8076a41086a200541f8086a41086a290300370300200541f8076a41106a200541f8086a41106a290300370300200541f8076a41186a200541f8086a41186a280200360200200520052f01fc0b3b019408200520052903f8083703f80720052802ec0d4102470d030c040b200520023602e403200520063602e003200520083602dc03200541003602880e200541e00d6a200720072009a76a10f58d808000200541106a41086a2203200541e00d6a41106a290200370300200541106a41106a2202200541e00d6a41186a290200370300200541106a41186a2204200541800e6a290200370300200520052902e80d37031020052802e40d211220052802e00d211320052802880e210d200541800c6a41186a200141186a290000370300200541800c6a41106a200141106a290000370300200541800c6a41086a200141086a290000370300200520012900003703800c200541e00d6a200541800c6a200541dc036a200528020c10ed85808000024020052d00e00d22104102460d00200541d4036a41026a20052d00e30d3a0000200541b8036a41086a200541f40d6a290200370300200541b8036a41106a200541fc0d6a29020037030020054198036a41086a200329030037030020054198036a41106a200229030037030020054198036a41186a2004290300370300200520052f00e10d3b01d403200520052902ec0d3703b80320052005290310370398032009422088a7211420052802e40d210f20052802e80d21024106210c0c2a0b20052802e40d2101200041083a000020002001360204200d4129490d2a201341002802c0a3c68000118080808000000c2a0b200541c00f6a41086a200541ec0d6a220441086a280200360200200520042902003703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200528020c10ed8580800020052d00800c22044102460d07200541fe0b6a20052d00830c3a000020054180096a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c2108200421040b41022107200541a4046a41026a200541fc0b6a41026a2d00003a000020054188046a41086a200541f8086a41086a29030037030020054188046a41106a200541f8086a41106a29030037030020054188046a41186a200541f8086a41186a280200360200200520052f01fc0b3b01a404200520052903f808370388040240024020052802f80d4102470d000c010b200541c00f6a41086a200341086a280200360200200520032902003703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200528020c10ed8580800020052d00800c22034102460d08200541fe0b6a20052d00830c3a000020054180096a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c210d200321070b41022103200541c4046a41026a200541fc0b6a41026a2d00003a0000200541a8046a41086a200541f8086a41086a290300370300200541a8046a41106a200541f8086a41106a290300370300200541a8046a41186a200541f8086a41186a280200360200200520052f01fc0b3b01c404200520052903f8083703a8040240024020052802840e4102470d000c010b200541c00f6a41086a200541840e6a220341086a280200360200200520032902003703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200528020c10ed8580800020052d00800c22014102460d09200541fe0b6a20052d00830c3a000020054180096a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c2113200121030b200541e4046a41026a200541fc0b6a41026a2d00003a0000200541c8046a41086a200541f8086a41086a290300370300200541c8046a41106a200541f8086a41106a290300370300200541c8046a41186a200541f8086a41186a280200360200200520052f01fc0b3b01e404200520052903f8083703c804200541800c6a200541cc076a410410ee8580800020052d00800c22014103460d0920054184056a41026a20052d00830c3a0000200541e8046a41086a200541800c6a41106a290200370300200541e8046a41106a200541800c6a41186a290200370300200541e8046a41186a200541a00c6a220f280200360200200520052f00810c3b018405200520052902880c3703e80420052802840c210c200541800c6a200541cc076a410510ee8580800020052d00800c22144103460d0a200541a4056a41026a20052d00830c3a000020054188056a41086a200541800c6a41106a221229020037030020054188056a41106a200541800c6a41186a221029020037030020054188056a41186a200f280200360200200520052f00810c3b01a405200520052902880c3703880520052802840c210f200541800c6a200541cc076a410610ee8580800020052d00800c22154103460d0b200541c4056a41026a20052d00830c3a0000200541a8056a41086a2012290200370300200541a8056a41106a2010290200370300200541a8056a41186a200541a00c6a2212280200360200200520052f00810c3b01c405200520052902880c3703a80520052802840c2116200541800c6a200541cc076a410710ee8580800020052d00800c22174103460d0c200541e4056a41026a20052d00830c3a0000200541c8056a41086a200541800c6a41106a2210290200370300200541c8056a41106a200541800c6a41186a2218290200370300200541c8056a41186a2012280200360200200520052f00810c3b01e405200520052902880c3703c80520052802840c2119200541800c6a200541cc076a410810ee8580800020052d00800c221a4103460d0d20054184066a41026a20052d00830c3a0000200541e8056a41086a2010290200370300200541e8056a41106a2018290200370300200541e8056a41186a200541a00c6a2212280200360200200520052f00810c3b018406200520052902880c3703e80520052802840c2118200541800c6a200541cc076a410910ee8580800020052d00800c221b4103460d0e200541a4066a41026a20052d00830c3a000020054188066a41086a200541800c6a41106a221029020037030020054188066a41106a200541800c6a41186a221c29020037030020054188066a41186a2012280200360200200520052f00810c3b01a406200520052902880c3703880620052802840c211d200541800c6a200541cc076a410a10ee8580800020052d00800c221e4103460d0f200541c4066a41026a20052d00830c3a0000200541a8066a41086a2010290200370300200541a8066a41106a201c290200370300200541a8066a41186a200541a00c6a2212280200360200200520052f00810c3b01c406200520052902880c3703a80620052802840c211c200541800c6a200541cc076a410b10ee8580800020052d00800c221f4103460d10200541e4066a41026a20052d00830c3a0000200541c8066a41086a200541800c6a41106a2210290200370300200541c8066a41106a200541800c6a41186a2220290200370300200541c8066a41186a2012280200360200200520052f00810c3b01e406200520052902880c3703c80620052802840c2121200541800c6a200541cc076a410c10ee8580800020052d00800c22224103460d1120054184076a41026a20052d00830c3a0000200541e8066a41086a2010290200370300200541e8066a41106a2020290200370300200541e8066a41186a200541a00c6a2212280200360200200520052f00810c3b018407200520052902880c3703e80620052802840c2120200541800c6a200541cc076a410d10ee8580800020052d00800c22234103460d12200541a4076a41026a20052d00830c3a000020054188076a41086a200541800c6a41106a221029020037030020054188076a41106a200541800c6a41186a222429020037030020054188076a41186a2012280200360200200520052f00810c3b01a407200520052902880c3703880720052802840c2125200541800c6a200541cc076a410e10ee8580800020052d00800c22264103460d13200541c8076a41026a20052d00830c3a0000200541a8076a41086a2010290200370300200541a8076a41106a2024290200370300200541a8076a41186a200541a00c6a2212280200360200200520052f00810c3b01c807200520052902880c3703a80720052802840c2124200541800c6a200541cc076a410f10ee8580800002400240024020052d00800c22274103460d00200541f4076a41026a222820052d00830c3a0000200541d8076a41086a2229200541800c6a41106a290200370300200541d8076a41106a222a200541800c6a41186a290200370300200541d8076a41186a222b2012280200360200200520052f00810c3b01f407200520052902880c3703d80720052802840c212c41002d00fca3c680001a41c00441002802c8a3c68000118180808000002212450d17201220023a0000201220052f0184043b000120122006360204201220052903e803370208201220043a0024201220052f01a4043b002541032110201241036a20054184046a41026a2d00003a0000201241106a200541e8036a41086a290300370200201241186a200541e8036a41106a290300370200201241206a200541e8036a41186a280200360200201241276a200541a4046a41026a2d00003a000020122008360228201220073a00482012200d36024c201220052903880437022c201241346a20054188046a41086a2903003702002012413c6a20054188046a41106a290300370200201241c4006a20054188046a41186a280200360200201220052f01c4043b0049201241cb006a200541c4046a41026a2d00003a0000201220052903a804370250201241d8006a200541a8046a41086a290300370200201241e0006a200541a8046a41106a290300370200201241e8006a200541a8046a41186a280200360200201220033a006c20122013360270201220013a009001201220052f01e4043b006d201241ef006a200541e4046a41026a2d00003a00002012418c016a200541c8046a41186a28020036020020124184016a200541c8046a41106a290300370200201241fc006a200541c8046a41086a290300370200201220052903c804370274201220052f0184053b00910120124193016a20054184056a41026a2d00003a00002012200c36029401201241b0016a200541e8046a41186a280200360200201241a8016a200541e8046a41106a290300370200201241a0016a200541e8046a41086a290300370200201220052903e80437029801201220143a00b401201241b7016a200541a4056a41026a2d00003a0000201220052f01a4053b00b5012012200f3602b801201241d4016a20054188056a41186a280200360200201241cc016a20054188056a41106a290300370200201241c4016a20054188056a41086a29030037020020122005290388053702bc01201220153a00d801201241db016a200541c4056a41026a2d00003a0000201220052f01c4053b00d901201220163602dc01201241f8016a200541a8056a41186a280200360200201241f0016a200541a8056a41106a290300370200201241e8016a200541a8056a41086a290300370200201220052903a8053702e001201220173a00fc01201241ff016a200541e4056a41026a2d00003a0000201220052f01e4053b00fd0120122019360280022012419c026a200541c8056a41186a28020036020020124194026a200541c8056a41106a2903003702002012418c026a200541c8056a41086a290300370200201220052903c805370284022012201a3a00a002201241a3026a20054184066a41026a2d00003a0000201220052f0184063b00a102201220183602a402201241c0026a200541e8056a41186a280200360200201241b8026a200541e8056a41106a290300370200201241b0026a200541e8056a41086a290300370200201220052903e8053702a8022012201b3a00c402201241c7026a200541a4066a41026a2d00003a0000201220052f01a4063b00c5022012201d3602c802201241e4026a20054188066a41186a280200360200201241dc026a20054188066a41106a290300370200201241d4026a20054188066a41086a29030037020020122005290388063702cc022012201e3a00e802201241eb026a200541c4066a41026a2d00003a0000201220052f01c4063b00e9022012201c3602ec0220124188036a200541a8066a41186a28020036020020124180036a200541a8066a41106a290300370200201241f8026a200541a8066a41086a290300370200201220052903a8063702f0022012201f3a008c032012418f036a200541e4066a41026a2d00003a0000201220052f01e4063b008d032012202136029003201241ac036a200541c8066a41186a280200360200201241a4036a200541c8066a41106a2903003702002012419c036a200541c8066a41086a290300370200201220052903c80637029403201220223a00b003201241b3036a20054184076a41026a2d00003a0000201220052f0184073b00b103201220203602b403201241d0036a200541e8066a41186a280200360200201241c8036a200541e8066a41106a290300370200201241c0036a200541e8066a41086a290300370200201220052903e8063702b803201220233a00d403201241d7036a200541a4076a41026a2d00003a0000201220052f01a4073b00d503201220253602d803201241f4036a20054188076a41186a280200360200201241ec036a20054188076a41106a290300370200201241e4036a20054188076a41086a29030037020020122005290388073702dc03201220263a00f803201241fb036a200541c8076a41026a2d00003a0000201220052f01c8073b00f903201220243602fc0320124198046a200541a8076a41186a28020036020020124190046a200541a8076a41106a29030037020020124188046a200541a8076a41086a290300370200201220052903a80737028004201220273a009c042012419f046a20282d00003a0000201220052f01f4073b009d042012202c3602a004201241bc046a202b280200360200201241b4046a202a290300370200201241ac046a2029290300370200201220052903d8073702a404200b4102470d010c020b20052802840c2101200041083a0000200020013602040c2a0b2005200a3702fc082005200b3602f808200541800c6a200541f8086a10e285808000200541da076a20052d00830c3a0000200541186a200541940c6a290200370300200541206a2005419c0c6a290200370300200520052f00810c3b01d8072005200529028c0c37031020052d00800c211020052802840c210f20052802880c210220052802a40c211420052802a80c21130b200541d4036a41026a200541d8076a41026a2d00003a0000200541b8036a41086a200541106a41086a290300370300200541b8036a41106a200541106a41106a290300370300200520052f01d8073b01d403200520052903103703b8034107210c0c270b200541c00f6a41086a200341086a280200360200200520032902003703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200528020c10ed8580800020052d00800c22034102460d15200541fe0b6a20052d00830c3a000020054180096a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c2108200321040b41022103200541b4086a41026a200541fc0b6a41026a2d00003a000020054198086a41086a200541f8086a41086a29030037030020054198086a41106a200541f8086a41106a29030037030020054198086a41186a200541f8086a41186a280200360200200520052f01fc0b3b01b408200520052903f808370398080240024020052802f80d4102470d000c010b200541c00f6a41086a200541e00d6a41186a220341086a280200360200200520032902003703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200528020c10ed8580800020052d00800c22034102460d16200541fe0b6a20052d00830c3a000020054180096a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c2110200321030b4102210d200541d4086a41026a200541fc0b6a41026a2d00003a0000200541b8086a41086a200541f8086a41086a290300370300200541b8086a41106a200541f8086a41106a290300370300200541b8086a41186a200541f8086a41186a280200360200200520052f01fc0b3b01d408200520052903f8083703b8080240024020052802840e4102470d000c010b200541c00f6a41086a200541840e6a220d41086a2802003602002005200d2902003703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200528020c10ed8580800020052d00800c220d4102460d17200541fe0b6a20052d00830c3a000020054180096a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c210b200d210d0b41022114200541f4086a41026a200541fc0b6a41026a2d00003a0000200541d8086a41086a200541f8086a41086a290300370300200541d8086a41106a200541f8086a41106a290300370300200541d8086a41186a200541f8086a41186a280200360200200520052f01fc0b3b01f408200520052903f8083703d8080240024020052802900e4102470d000c010b200541c00f6a41086a200541900e6a221341086a280200360200200520132902003703c00f200541106a41186a200141186a290000370300200541106a41106a200141106a290000370300200541106a41086a200141086a29000037030020052001290000370310200541800c6a200541106a200541c00f6a200528020c10ed8580800020052d00800c22134102460d18200541fe0b6a20052d00830c3a000020054180096a200541800c6a41106a290200370300200541f8086a41106a200541800c6a41186a290200370300200541f8086a41186a200541a00c6a280200360200200520052f00810c3b01fc0b200520052902880c3703f80820052802840c2101201321140b200541b4096a41026a200541fc0b6a41026a2d00003a000020054198096a41086a200541f8086a41086a29030037030020054198096a41106a200541f8086a41106a29030037030020054198096a41186a200541f8086a41186a280200360200200520052f01fc0b3b01b409200520052903f80837039809200541800c6a200541cc076a410510ef8580800020052d00800c22154103460d18200541d4096a41026a20052d00830c3a0000200541b8096a41086a200541800c6a41106a290200370300200541b8096a41106a200541800c6a41186a290200370300200541b8096a41186a200541a00c6a2213280200360200200520052f00810c3b01d409200520052902880c3703b80920052802840c2116200541800c6a200541cc076a410610ef8580800020052d00800c22174103460d19200541f4096a41026a20052d00830c3a0000200541d8096a41086a200541800c6a41106a2212290200370300200541d8096a41106a200541800c6a41186a220c290200370300200541d8096a41186a2013280200360200200520052f00810c3b01f409200520052902880c3703d80920052802840c2118200541800c6a200541cc076a410710ef8580800020052d00800c22194103460d1a200541940a6a41026a20052d00830c3a0000200541f8096a41086a2012290200370300200541f8096a41106a200c290200370300200541f8096a41186a200541a00c6a2213280200360200200520052f00810c3b01940a200520052902880c3703f80920052802840c211a200541800c6a200541cc076a410810ef8580800020052d00800c221b4103460d1b200541b40a6a41026a20052d00830c3a0000200541980a6a41086a200541800c6a41106a2212290200370300200541980a6a41106a200541800c6a41186a220c290200370300200541980a6a41186a2013280200360200200520052f00810c3b01b40a200520052902880c3703980a20052802840c211c200541800c6a200541cc076a410910ef8580800020052d00800c221d4103460d1c200541d40a6a41026a20052d00830c3a0000200541b80a6a41086a2012290200370300200541b80a6a41106a200c290200370300200541b80a6a41186a200541a00c6a2213280200360200200520052f00810c3b01d40a200520052902880c3703b80a20052802840c211e200541800c6a200541cc076a410a10ef8580800020052d00800c221f4103460d1d200541f40a6a41026a20052d00830c3a0000200541d80a6a41086a200541800c6a41106a2212290200370300200541d80a6a41106a200541800c6a41186a220c290200370300200541d80a6a41186a2013280200360200200520052f00810c3b01f40a200520052902880c3703d80a20052802840c2120200541800c6a200541cc076a410b10ef8580800020052d00800c22214103460d1e200541940b6a41026a20052d00830c3a0000200541f80a6a41086a2012290200370300200541f80a6a41106a200c290200370300200541f80a6a41186a200541a00c6a2213280200360200200520052f00810c3b01940b200520052902880c3703f80a20052802840c2122200541800c6a200541cc076a410c10ef8580800020052d00800c22234103460d1f200541b40b6a41026a20052d00830c3a0000200541980b6a41086a200541800c6a41106a2212290200370300200541980b6a41106a200541800c6a41186a220c290200370300200541980b6a41186a2013280200360200200520052f00810c3b01b40b200520052902880c3703980b20052802840c2124200541800c6a200541cc076a410d10ef8580800020052d00800c22254103460d20200541d40b6a41026a20052d00830c3a0000200541b80b6a41086a2012290200370300200541b80b6a41106a200c290200370300200541b80b6a41186a200541a00c6a2213280200360200200520052f00810c3b01d40b200520052902880c3703b80b20052802840c2126200541800c6a200541cc076a410e10ef8580800020052d00800c22274103460d21200541f80b6a41026a20052d00830c3a0000200541d80b6a41086a200541800c6a41106a2212290200370300200541d80b6a41106a200541800c6a41186a220c290200370300200541d80b6a41186a2013280200360200200520052f00810c3b01f80b200520052902880c3703d80b20052802840c2128200541800c6a200541cc076a410f10ef8580800002400240024020052d00800c22294103460d00200541f4076a41026a20052d00830c3a0000200541d8076a41086a2012290200370300200541d8076a41106a200c290200370300200541d8076a41186a200541800c6a41206a280200360200200520052f00810c3b01f407200520052902880c3703d80720052802840c212a41002d00fca3c680001a41c00441002802c8a3c68000118180808000002213450d252009422088a72112201320023a0000201320052f0194083b000120132006360204201320052903f807370208201320043a0024201320052f01b4083b00254103210c201341036a20054194086a41026a2d00003a0000201341106a200541f8076a41086a290300370200201341186a200541f8076a41106a290300370200201341206a200541f8076a41186a280200360200201341276a200541b4086a41026a2d00003a000020132008360228201320033a00482013201036024c201320052903980837022c201341346a20054198086a41086a2903003702002013413c6a20054198086a41106a290300370200201341c4006a20054198086a41186a280200360200201320052f01d4083b0049201341cb006a200541d4086a41026a2d00003a0000201320052903b808370250201341d8006a200541b8086a41086a290300370200201341e0006a200541b8086a41106a290300370200201341e8006a200541b8086a41186a2802003602002013200d3a006c2013200b360270201320143a009001201320052f01f4083b006d201341ef006a200541f4086a41026a2d00003a00002013418c016a200541d8086a41186a28020036020020134184016a200541d8086a41106a290300370200201341fc006a200541d8086a41086a290300370200201320052903d808370274201320052f01b4093b00910120134193016a200541b4096a41026a2d00003a00002013200136029401201341b0016a20054198096a41186a280200360200201341a8016a20054198096a41106a290300370200201341a0016a20054198096a41086a290300370200201320052903980937029801201320153a00b401201341b7016a200541d4096a41026a2d00003a0000201320052f01d4093b00b501201320163602b801201341d4016a200541b8096a41186a280200360200201341cc016a200541b8096a41106a290300370200201341c4016a200541b8096a41086a290300370200201320052903b8093702bc01201320173a00d801201341db016a200541f4096a41026a2d00003a0000201320052f01f4093b00d901201320183602dc01201341f8016a200541d8096a41186a280200360200201341f0016a200541d8096a41106a290300370200201341e8016a200541d8096a41086a290300370200201320052903d8093702e001201320193a00fc01201341ff016a200541940a6a41026a2d00003a0000201320052f01940a3b00fd012013201a360280022013419c026a200541f8096a41186a28020036020020134194026a200541f8096a41106a2903003702002013418c026a200541f8096a41086a290300370200201320052903f809370284022013201b3a00a002201341a3026a200541b40a6a41026a2d00003a0000201320052f01b40a3b00a1022013201c3602a402201341c0026a200541980a6a41186a280200360200201341b8026a200541980a6a41106a290300370200201341b0026a200541980a6a41086a290300370200201320052903980a3702a8022013201d3a00c402201341c7026a200541d40a6a41026a2d00003a0000201320052f01d40a3b00c5022013201e3602c802201341e4026a200541b80a6a41186a280200360200201341dc026a200541b80a6a41106a290300370200201341d4026a200541b80a6a41086a290300370200201320052903b80a3702cc022013201f3a00e802201341eb026a200541f40a6a41026a2d00003a0000201320052f01f40a3b00e902201320203602ec0220134188036a200541d80a6a41186a28020036020020134180036a200541d80a6a41106a290300370200201341f8026a200541d80a6a41086a290300370200201320052903d80a3702f002201320213a008c032013418f036a200541940b6a41026a2d00003a0000201320052f01940b3b008d032013202236029003201341ac036a200541f80a6a41186a280200360200201341a4036a200541f80a6a41106a2903003702002013419c036a200541f80a6a41086a290300370200201320052903f80a37029403201320233a00b003201341b3036a200541b40b6a41026a2d00003a0000201320052f01b40b3b00b103201320243602b403201341d0036a200541980b6a41186a280200360200201341c8036a200541980b6a41106a290300370200201341c0036a200541980b6a41086a290300370200201320052903980b3702b803201320253a00d403201341d7036a200541d40b6a41026a2d00003a0000201320052f01d40b3b00d503201320263602d803201341f4036a200541b80b6a41186a280200360200201341ec036a200541b80b6a41106a290300370200201341e4036a200541b80b6a41086a290300370200201320052903b80b3702dc03201320273a00f803201341fb036a200541f80b6a41026a2d00003a0000201320052f01f80b3b00f903201320283602fc0320134198046a200541d80b6a41186a28020036020020134190046a200541d80b6a41106a29030037020020134188046a200541d80b6a41086a290300370200201320052903d80b37028004201320293a009c042013419f046a200541f4076a41026a2d00003a0000201320052f01f4073b009d042013202a3602a004201341bc046a200541d8076a41186a280200360200201341b4046a200541d8076a41106a290300370200201341ac046a200541d8076a41086a290300370200201320052903d8073702a404200541003602a80c200541800c6a200720072009a76a10f58d808000200541106a41186a2201200541800c6a41186a2202290200370300200541106a41106a2203200541800c6a41106a290200370300200541106a41086a2204200541800c6a41086a290200370300200520052902800c37031020052802a00c210d20052902a40c210e200f4102470d010c020b20052802840c2101200041083a0000200020013602040c280b200520113702dc072005200f3602d807200541800c6a200541d8076a10e285808000200541c00f6a41026a20052d00830c3a0000200541fc0b6a41026a200541870c6a2d00003a0000200541f8086a41086a2002290200370300200541f8086a41106a200541a00c6a290200370300200520052f00810c3b01c00f200520052f00850c3b01fc0b200520052902900c3703f80820052d00800c210c20052d00840c211020052802880c210f200528028c0c210220052802a80c21140b20054198036a41186a200129030037030020054198036a41106a200329030037030020054198036a41086a2004290300370300200541d8036a41026a200541c00f6a41026a2d00003a0000200541d4036a41026a200541fc0b6a41026a2d00003a0000200541b8036a41106a200541f8086a41106a290300370300200541b8036a41086a200541f8086a41086a2903003703002005200529031037039803200520052f01c00f3b01d803200520052f01fc0b3b01d403200520052903f8083703b8030c250b200120052903e00d370200200141286a2006290300370200200141206a2004290300370200200141186a2002290300370200200141106a200541e00d6a41106a290300370200200141086a2003290300370200200041083a0000200020013602040c250b41e484c08000412b200541e00d6a419085c08000419086c08000108981808000000b412020024190d1c2800010a281808000000b20052802840c2101200041083a0000200020013602040c220b20052802840c2101200041083a0000200020013602040c210b20052802840c2101200041083a0000200020013602040c200b20052802840c2101200041083a0000200020013602040c1f0b20052802840c2101200041083a0000200020013602040c1e0b20052802840c2101200041083a0000200020013602040c1d0b20052802840c2101200041083a0000200020013602040c1c0b20052802840c2101200041083a0000200020013602040c1b0b20052802840c2101200041083a0000200020013602040c1a0b20052802840c2101200041083a0000200020013602040c190b20052802840c2101200041083a0000200020013602040c180b20052802840c2101200041083a0000200020013602040c170b20052802840c2101200041083a0000200020013602040c160b20052802840c2101200041083a0000200020013602040c150b20052802840c2101200041083a0000200020013602040c140b410441c00410b280808000000b20052802840c2101200041083a0000200020013602040c120b20052802840c2101200041083a0000200020013602040c110b20052802840c2101200041083a0000200020013602040c100b20052802840c2101200041083a0000200020013602040c0f0b20052802840c2101200041083a0000200020013602040c0e0b20052802840c2101200041083a0000200020013602040c0d0b20052802840c2101200041083a0000200020013602040c0c0b20052802840c2101200041083a0000200020013602040c0b0b20052802840c2101200041083a0000200020013602040c0a0b20052802840c2101200041083a0000200020013602040c090b20052802840c2101200041083a0000200020013602040c080b20052802840c2101200041083a0000200020013602040c070b20052802840c2101200041083a0000200020013602040c060b20052802840c2101200041083a0000200020013602040c050b20052802840c2101200041083a0000200020013602040c040b410441c00410b280808000000b200f428180808010370200200f41086a2006200210848e8080001a410021100b2009422088a7211220054198036a41186a200541800c6a41186a29030037030020054198036a41106a200541800c6a41106a29030037030020054198036a41086a200541800c6a41086a290300370300200541d4036a41026a200541106a41026a2d00003a0000200541b8036a41086a200541e00d6a41086a290300370300200541b8036a41106a200541e00d6a41106a290300370300200520052903800c37039803200520052f01103b01d403200520052903e00d3703b8034105210c0b200020052f01d8033b0001200020052f01d4033b0005200020052903b8033702102000200529039803370234200041036a200541d8036a41026a2d00003a0000200041076a200541d4036a41026a2d00003a0000200041186a200541b8036a41086a290300370200200041206a200541b8036a41106a2903003702002000413c6a20054198036a41086a290300370200200041c4006a20054198036a41106a290300370200200041cc006a20054198036a41186a2903003702002000200e3702582000200d3602542000200f3602082000200236020c200020143602282000201336022c20002012360230200020103a00042000200c3a00000b200541d00f6a2480808080000ba30801057f23808080800041e0016b2204248080808000200241086a28020021052002280204210602400240024002400240024020022802000d00024002400240024020050d00410121020c010b20054120460d012005417f4c0d0441002d00fca3c680001a200541002802c8a3c68000118180808000002202450d050b20022006200510848e808000210341002d00fca3c680001a413041002802c8a3c680001181808080000022020d014104413010b280808000000b2004411c6a41026a200641026a2d00003a0000200441086a2006410f6a290000370300200441106a200641176a290000370300200441186a2006411f6a2d00003a0000200420062f00003b011c2004200629000737030020062800032102410121060c050b2002200536020c2002200336020820022005360204200241888080807836020020022001290000370010200241186a200141086a290000370000200241206a200141106a290000370000200241286a200141186a2900003700000c030b20044180016a41186a200141186a29000037030020044180016a41106a200141106a29000037030020044180016a41086a200141086a2900003703002004200129000037038001200441206a20044180016a20062005200310ec85808000024020042d002022054108460d00200441dc016a41026a20042d00233a0000200420042f00213b01dc012004280224210720044180016a200441286a41d80010848e8080001a02400240200341186a2802002201450d0020032001417f6a36021841002106200341146a22012001280200220141016a22024100200328020c220820022008491b6b360200200341106a28020020014102746a2802002202200328020822014f0d01200328020420024107746a220141046a200120012d00004108461b10e385808000200120053a0004200141083a000020012007360208200120042f01dc013b0005200141076a200441de016a2d00003a00002001410c6a20044180016a41d80010848e8080001a0c060b0240200328020822012003280200470d0020032001109e86808000200328020821010b200328020420014107746a220120053a0004200141083a0000200120042f01dc013b000520012007360208200141076a200441de016a2d00003a00002001410c6a20044180016a41d80010848e8080001a20032003280208220241016a360208410021060c050b2002200141d4d7c2800010f980808000000b200428022421020c020b10ae80808000000b4101200510b280808000000b200041023a0000200020023602040c010b200020042f011c3b00012000200429030037020820002002360204200020063a0000200041036a2004411e6a2d00003a0000200041106a200441086a290300370200200041186a200441106a290300370200200041206a200441186a2802003602000b200441e0016a2480808080000bd40201017f23808080800041d0006b220324808080800002402002410f4b0d000240024020012802002002410c6c6a22022802004102470d00200041023a00000c010b200341086a200241086a28020036020020032002290200370300200341306a41086a2001280204220241086a290000370300200341306a41106a200241106a290000370300200341306a41186a200241186a290000370300200320022900003703302003410c6a200341306a2003200128020828020010ed85808000024020032d000c4102460d002000200329020c370200200041206a2003410c6a41206a280200360200200041186a2003410c6a41186a290200370200200041106a2003410c6a41106a290200370200200041086a2003410c6a41086a2902003702000c010b20002003280210360204200041033a00000b200341d0006a2480808080000f0b2002411041b0d1c2800010f980808000000bd40201017f23808080800041d0006b220324808080800002402002410f4b0d000240024020012802002002410c6c6a22022802004102470d00200041023a00000c010b200341086a200241086a28020036020020032002290200370300200341306a41086a2001280204220241086a290000370300200341306a41106a200241106a290000370300200341306a41186a200241186a290000370300200320022900003703302003410c6a200341306a2003200128020828020010ed85808000024020032d000c4102460d002000200329020c370200200041206a2003410c6a41206a280200360200200041186a2003410c6a41186a290200370200200041106a2003410c6a41106a290200370200200041086a2003410c6a41086a2902003702000c010b20002003280210360204200041033a00000b200341d0006a2480808080000f0b2002411041a0d1c2800010f980808000000bb09e0101617f23808080800041800c6b2203248080808000024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024020012802002204417e6a2205410420054106491b0e06050001020304050b200341980b6a200141046a10fa8d808000200141386a21050240024020012802342202450d002005280200210520022002280200220441016a360200410021012004417f4a0d010c1f0b200341026a200541026a2d00003a0000200341c00a6a200141cb006a290000370300200341c50a6a200141d0006a290000370000200320052f00003b0100200320012900433703b80a200128003f2105200128003b2102410121010b200020032902980b370230200020013a0004200041056a20032f01003b0000200041d8006a200341980b6a41286a290200370200200041d0006a200341980b6a41206a290200370200200041c8006a200341980b6a41186a290200370200200041c0006a200341980b6a41106a290200370200200041386a200341980b6a41086a290200370200200041076a200341026a2d00003a00002000410c6a2005360200200041086a2002360200200041053a0000200041106a20032903b80a370200200041186a200341b80a6a41086a290300370200200041206a200341b80a6a41106a290300370200200041286a200341b80a6a41186a2903003702000c1c0b2003200141286a10fa8d8080000240024020012d00040d00200341ba0a6a200141076a2d00003a0000200341980b6a41086a200141146a290200370300200341a80b6a2001411c6a290200370300200341b00b6a200141246a2d00003a00002003200141056a2f00003b01b80a20032001410c6a2902003703980b200141086a2802002101410121020c010b200341b80a6a200141086a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002201450d0020022001417f6a360218200241146a22012001280200220141016a22054100200228020c220420052004491b6b360200200241106a28020020014102746a2802002201200228020822054f0d0b200228020420014107746a220241046a200220022d00004108461b10e385808000200241083a0000200241016a200341980b6a41e30010848e8080001a0c010b0240200228020822012002280200470d0020022001109e86808000200228020821010b200228020420014107746a220141083a0000200141016a200341980b6a41e30010848e8080001a20022002280208220141016a3602080b410021020b20002003290200370228200020023a0004200041056a20032f01b80a3b0000200041086a2001360200200041d0006a200341286a290200370200200041c8006a200341206a290200370200200041c0006a200341186a290200370200200041386a200341106a290200370200200041306a200341086a290200370200200041076a200341ba0a6a2d00003a0000200041063a0000200041246a200341980b6a41186a2802003602002000411c6a200341980b6a41106a290300370200200041146a200341980b6a41086a2903003702002000410c6a20032903980b3702000c1b0b4102210641022107024020012d003022044102460d000240024020040d00200341026a200141336a2d00003a0000200341a00b6a200141c0006a290200370300200341a80b6a200141c8006a290200370300200341b00b6a200141d0006a2d00003a00002003200141316a2f00003b01002003200141386a2902003703980b200141346a2802002105410121070c010b200341b80a6a200141346a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002205450d0020022005417f6a360218200241146a22052005280200220541016a22044100200228020c220820042008491b6b360200200241106a28020020054102746a2802002205200228020822044f0d0c200228020420054107746a220441046a200420042d00004108461b10e385808000200441083a0000200441016a200341980b6a41e30010848e8080001a0c010b0240200228020822052002280200470d0020022005109e86808000200228020821050b200228020420054107746a220541083a0000200541016a200341980b6a41e30010848e8080001a20022002280208220541016a3602080b410021070b200341f0006a41026a200341026a2d00003a0000200341e0056a41086a200341980b6a41086a290300370300200341e0056a41106a200341980b6a41106a290300370300200341e0056a41186a200341980b6a41186a280200360200200320032f01003b0170200320032903980b3703e0050b0240200141d4006a2d000022084102460d000240024020080d00200341026a200141d7006a2d00003a0000200341a00b6a200141e4006a290200370300200341a80b6a200141ec006a290200370300200341b00b6a200141f4006a2d00003a00002003200141d5006a2f00003b01002003200141dc006a2902003703980b200141d8006a2802002104410121060c010b200341b80a6a200141d8006a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002204450d0020022004417f6a360218200241146a22042004280200220441016a22084100200228020c220920082009491b6b360200200241106a28020020044102746a2802002204200228020822084f0d0d200228020420044107746a220841046a200820082d00004108461b10e385808000200841083a0000200841016a200341980b6a41e30010848e8080001a0c010b0240200228020822042002280200470d0020022004109e86808000200228020821040b200228020420044107746a220441083a0000200441016a200341980b6a41e30010848e8080001a20022002280208220441016a3602080b410021060b200341f4006a41026a200341026a2d00003a000020034188066a41086a200341980b6a41086a29030037030020034188066a41106a200341980b6a41106a29030037030020034188066a41186a200341980b6a41186a280200360200200320032f01003b0174200320032903980b370388060b4102210a4102210b0240200141f8006a2d000022094102460d000240024020090d00200341026a200141fb006a2d00003a0000200341a00b6a20014188016a290200370300200341a80b6a20014190016a290200370300200341b00b6a20014198016a2d00003a00002003200141f9006a2f00003b0100200320014180016a2902003703980b200141fc006a28020021084101210b0c010b200341b80a6a200141fc006a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002208450d0020022008417f6a360218200241146a22082008280200220841016a22094100200228020c220c2009200c491b6b360200200241106a28020020084102746a2802002208200228020822094f0d0e200228020420084107746a220941046a200920092d00004108461b10e385808000200941083a0000200941016a200341980b6a41e30010848e8080001a0c010b0240200228020822082002280200470d0020022008109e86808000200228020821080b200228020420084107746a220841083a0000200841016a200341980b6a41e30010848e8080001a20022002280208220841016a3602080b4100210b0b200341f8006a41026a200341026a2d00003a0000200341b0066a41086a200341980b6a41086a290300370300200341b0066a41106a200341980b6a41106a290300370300200341b0066a41186a200341980b6a41186a280200360200200320032f01003b0178200320032903980b3703b0060b02402001419c016a2d0000220c4102460d0002400240200c0d00200341026a2001419f016a2d00003a0000200341a00b6a200141ac016a290200370300200341a80b6a200141b4016a290200370300200341b00b6a200141bc016a2d00003a000020032001419d016a2f00003b01002003200141a4016a2902003703980b200141a0016a28020021094101210a0c010b200341b80a6a200141a0016a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002209450d0020022009417f6a360218200241146a22092009280200220941016a220c4100200228020c220d200c200d491b6b360200200241106a28020020094102746a28020022092002280208220c4f0d0f200228020420094107746a220c41046a200c200c2d00004108461b10e385808000200c41083a0000200c41016a200341980b6a41e30010848e8080001a0c010b0240200228020822092002280200470d0020022009109e86808000200228020821090b200228020420094107746a220941083a0000200941016a200341980b6a41e30010848e8080001a20022002280208220941016a3602080b4100210a0b200341fc006a41026a200341026a2d00003a0000200341d8066a41086a200341980b6a41086a290300370300200341d8066a41106a200341980b6a41106a290300370300200341d8066a41186a200341980b6a41186a280200360200200320032f01003b017c200320032903980b3703d8060b4102210e4102210f0240200141c0016a2d0000220d4102460d0002400240200d0d00200341026a200141c3016a2d00003a0000200341a00b6a200141d0016a290200370300200341a80b6a200141d8016a290200370300200341b00b6a200141e0016a2d00003a00002003200141c1016a2f00003b01002003200141c8016a2902003703980b200141c4016a280200210c4101210f0c010b200341b80a6a200141c4016a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a280200220c450d002002200c417f6a360218200241146a220c200c280200220c41016a220d4100200228020c2210200d2010491b6b360200200241106a280200200c4102746a280200220c2002280208220d4f0d102002280204200c4107746a220d41046a200d200d2d00004108461b10e385808000200d41083a0000200d41016a200341980b6a41e30010848e8080001a0c010b02402002280208220c2002280200470d002002200c109e868080002002280208210c0b2002280204200c4107746a220c41083a0000200c41016a200341980b6a41e30010848e8080001a20022002280208220c41016a3602080b4100210f0b20034180016a41026a200341026a2d00003a000020034180076a41086a200341980b6a41086a29030037030020034180076a41106a200341980b6a41106a29030037030020034180076a41186a200341980b6a41186a280200360200200320032f01003b018001200320032903980b370380070b0240200141e4016a2d000022104102460d000240024020100d00200341026a200141e7016a2d00003a0000200341a00b6a200141f4016a290200370300200341a80b6a200141fc016a290200370300200341b00b6a20014184026a2d00003a00002003200141e5016a2f00003b01002003200141ec016a2902003703980b200141e8016a280200210d4101210e0c010b200341b80a6a200141e8016a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a280200220d450d002002200d417f6a360218200241146a220d200d280200220d41016a22104100200228020c221120102011491b6b360200200241106a280200200d4102746a280200220d200228020822104f0d112002280204200d4107746a221041046a201020102d00004108461b10e385808000201041083a0000201041016a200341980b6a41e30010848e8080001a0c010b02402002280208220d2002280200470d002002200d109e868080002002280208210d0b2002280204200d4107746a220d41083a0000200d41016a200341980b6a41e30010848e8080001a20022002280208220d41016a3602080b4100210e0b20034184016a41026a200341026a2d00003a0000200341a8076a41086a200341980b6a41086a290300370300200341a8076a41106a200341980b6a41106a290300370300200341a8076a41186a200341980b6a41186a280200360200200320032f01003b018401200320032903980b3703a8070b4102211241022113024020014188026a2d000022114102460d000240024020110d00200341026a2001418b026a2d00003a0000200341a00b6a20014198026a290200370300200341a80b6a200141a0026a290200370300200341b00b6a200141a8026a2d00003a0000200320014189026a2f00003b0100200320014190026a2902003703980b2001418c026a2802002110410121130c010b200341b80a6a2001418c026a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002210450d0020022010417f6a360218200241146a22102010280200221041016a22114100200228020c221420112014491b6b360200200241106a28020020104102746a2802002210200228020822114f0d12200228020420104107746a221141046a201120112d00004108461b10e385808000201141083a0000201141016a200341980b6a41e30010848e8080001a0c010b0240200228020822102002280200470d0020022010109e86808000200228020821100b200228020420104107746a221041083a0000201041016a200341980b6a41e30010848e8080001a20022002280208221041016a3602080b410021130b20034188016a41026a200341026a2d00003a0000200341d0076a41086a200341980b6a41086a290300370300200341d0076a41106a200341980b6a41106a290300370300200341d0076a41186a200341980b6a41186a280200360200200320032f01003b018801200320032903980b3703d0070b0240200141ac026a2d000022144102460d000240024020140d00200341026a200141af026a2d00003a0000200341a00b6a200141bc026a290200370300200341a80b6a200141c4026a290200370300200341b00b6a200141cc026a2d00003a00002003200141ad026a2f00003b01002003200141b4026a2902003703980b200141b0026a2802002111410121120c010b200341b80a6a200141b0026a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002211450d0020022011417f6a360218200241146a22112011280200221141016a22144100200228020c221520142015491b6b360200200241106a28020020114102746a2802002211200228020822144f0d13200228020420114107746a221441046a201420142d00004108461b10e385808000201441083a0000201441016a200341980b6a41e30010848e8080001a0c010b0240200228020822112002280200470d0020022011109e86808000200228020821110b200228020420114107746a221141083a0000201141016a200341980b6a41e30010848e8080001a20022002280208221141016a3602080b410021120b2003418c016a41026a200341026a2d00003a0000200341f8076a41086a200341980b6a41086a290300370300200341f8076a41106a200341980b6a41106a290300370300200341f8076a41186a200341980b6a41186a280200360200200320032f01003b018c01200320032903980b3703f8070b41022116410221170240200141d0026a2d000022154102460d000240024020150d00200341026a200141d3026a2d00003a0000200341a00b6a200141e0026a290200370300200341a80b6a200141e8026a290200370300200341b00b6a200141f0026a2d00003a00002003200141d1026a2f00003b01002003200141d8026a2902003703980b200141d4026a2802002114410121170c010b200341b80a6a200141d4026a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002214450d0020022014417f6a360218200241146a22142014280200221441016a22154100200228020c221820152018491b6b360200200241106a28020020144102746a2802002214200228020822154f0d14200228020420144107746a221541046a201520152d00004108461b10e385808000201541083a0000201541016a200341980b6a41e30010848e8080001a0c010b0240200228020822142002280200470d0020022014109e86808000200228020821140b200228020420144107746a221441083a0000201441016a200341980b6a41e30010848e8080001a20022002280208221441016a3602080b410021170b20034190016a41026a200341026a2d00003a0000200341a0086a41086a200341980b6a41086a290300370300200341a0086a41106a200341980b6a41106a290300370300200341a0086a41186a200341980b6a41186a280200360200200320032f01003b019001200320032903980b3703a0080b0240200141f4026a2d000022184102460d000240024020180d00200341026a200141f7026a2d00003a0000200341a00b6a20014184036a290200370300200341a80b6a2001418c036a290200370300200341b00b6a20014194036a2d00003a00002003200141f5026a2f00003b01002003200141fc026a2902003703980b200141f8026a2802002115410121160c010b200341b80a6a200141f8026a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002215450d0020022015417f6a360218200241146a22152015280200221541016a22184100200228020c221920182019491b6b360200200241106a28020020154102746a2802002215200228020822184f0d15200228020420154107746a221841046a201820182d00004108461b10e385808000201841083a0000201841016a200341980b6a41e30010848e8080001a0c010b0240200228020822152002280200470d0020022015109e86808000200228020821150b200228020420154107746a221541083a0000201541016a200341980b6a41e30010848e8080001a20022002280208221541016a3602080b410021160b20034194016a41026a200341026a2d00003a0000200341c8086a41086a200341980b6a41086a290300370300200341c8086a41106a200341980b6a41106a290300370300200341c8086a41186a200341980b6a41186a280200360200200320032f01003b019401200320032903980b3703c8080b4102211a4102211b024020014198036a2d000022194102460d000240024020190d00200341026a2001419b036a2d00003a0000200341a00b6a200141a8036a290200370300200341a80b6a200141b0036a290200370300200341b00b6a200141b8036a2d00003a0000200320014199036a2f00003b01002003200141a0036a2902003703980b2001419c036a28020021184101211b0c010b200341b80a6a2001419c036a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002218450d0020022018417f6a360218200241146a22182018280200221841016a22194100200228020c221c2019201c491b6b360200200241106a28020020184102746a2802002218200228020822194f0d16200228020420184107746a221941046a201920192d00004108461b10e385808000201941083a0000201941016a200341980b6a41e30010848e8080001a0c010b0240200228020822182002280200470d0020022018109e86808000200228020821180b200228020420184107746a221841083a0000201841016a200341980b6a41e30010848e8080001a20022002280208221841016a3602080b4100211b0b20034198016a41026a200341026a2d00003a0000200341f0086a41086a200341980b6a41086a290300370300200341f0086a41106a200341980b6a41106a290300370300200341f0086a41186a200341980b6a41186a280200360200200320032f01003b019801200320032903980b3703f0080b0240200141bc036a2d0000221c4102460d0002400240201c0d00200341026a200141bf036a2d00003a0000200341a00b6a200141cc036a290200370300200341a80b6a200141d4036a290200370300200341b00b6a200141dc036a2d00003a00002003200141bd036a2f00003b01002003200141c4036a2902003703980b200141c0036a28020021194101211a0c010b200341b80a6a200141c0036a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002219450d0020022019417f6a360218200241146a22192019280200221941016a221c4100200228020c221d201c201d491b6b360200200241106a28020020194102746a28020022192002280208221c4f0d17200228020420194107746a221c41046a201c201c2d00004108461b10e385808000201c41083a0000201c41016a200341980b6a41e30010848e8080001a0c010b0240200228020822192002280200470d0020022019109e86808000200228020821190b200228020420194107746a221941083a0000201941016a200341980b6a41e30010848e8080001a20022002280208221941016a3602080b4100211a0b2003419c016a41026a200341026a2d00003a000020034198096a41086a200341980b6a41086a29030037030020034198096a41106a200341980b6a41106a29030037030020034198096a41186a200341980b6a41186a280200360200200320032f01003b019c01200320032903980b370398090b4102211e4102211f0240200141e0036a2d0000221d4102460d0002400240201d0d00200341026a200141e3036a2d00003a0000200341a00b6a200141f0036a290200370300200341a80b6a200141f8036a290200370300200341b00b6a20014180046a2d00003a00002003200141e1036a2f00003b01002003200141e8036a2902003703980b200141e4036a280200211c4101211f0c010b200341b80a6a200141e4036a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a280200221c450d002002201c417f6a360218200241146a221c201c280200221c41016a221d4100200228020c2220201d2020491b6b360200200241106a280200201c4102746a280200221c2002280208221d4f0d182002280204201c4107746a221d41046a201d201d2d00004108461b10e385808000201d41083a0000201d41016a200341980b6a41e30010848e8080001a0c010b02402002280208221c2002280200470d002002201c109e868080002002280208211c0b2002280204201c4107746a221c41083a0000201c41016a200341980b6a41e30010848e8080001a20022002280208221c41016a3602080b4100211f0b200341a0016a41026a200341026a2d00003a0000200341c0096a41086a200341980b6a41086a290300370300200341c0096a41106a200341980b6a41106a290300370300200341c0096a41186a200341980b6a41186a280200360200200320032f01003b01a001200320032903980b3703c0090b024020014184046a2d000022204102460d000240024020200d00200341026a20014187046a2d00003a0000200341a00b6a20014194046a290200370300200341a80b6a2001419c046a290200370300200341b00b6a200141a4046a2d00003a0000200320014185046a2f00003b010020032001418c046a2902003703980b20014188046a280200211d4101211e0c010b200341b80a6a20014188046a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a280200221d450d002002201d417f6a360218200241146a221d201d280200221d41016a22204100200228020c222120202021491b6b360200200241106a280200201d4102746a280200221d200228020822204f0d192002280204201d4107746a222041046a202020202d00004108461b10e385808000202041083a0000202041016a200341980b6a41e30010848e8080001a0c010b02402002280208221d2002280200470d002002201d109e868080002002280208211d0b2002280204201d4107746a221d41083a0000201d41016a200341980b6a41e30010848e8080001a20022002280208221d41016a3602080b4100211e0b200341a4016a41026a200341026a2d00003a0000200341e8096a41086a200341980b6a41086a290300370300200341e8096a41106a200341980b6a41106a290300370300200341e8096a41186a200341980b6a41186a280200360200200320032f01003b01a401200320032903980b3703e8090b41022122410221230240200141a8046a2d000022214102460d000240024020210d00200341026a200141ab046a2d00003a0000200341a00b6a200141b8046a290200370300200341a80b6a200141c0046a290200370300200341b00b6a200141c8046a2d00003a00002003200141a9046a2f00003b01002003200141b0046a2902003703980b200141ac046a2802002120410121230c010b200341b80a6a200141ac046a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002220450d0020022020417f6a360218200241146a22202020280200222041016a22214100200228020c222320212023491b6b360200200241106a28020020204102746a2802002220200228020822214f0d1a200228020420204107746a222141046a202120212d00004108461b10e385808000202141083a0000202141016a200341980b6a41e30010848e8080001a0c010b0240200228020822202002280200470d0020022020109e86808000200228020821200b200228020420204107746a222041083a0000202041016a200341980b6a41e30010848e8080001a20022002280208222041016a3602080b410021230b200341a8016a41026a200341026a2d00003a0000200341900a6a41086a200341980b6a41086a290300370300200341900a6a41106a200341980b6a41106a290300370300200341900a6a41186a200341980b6a41186a280200360200200320032f01003b01a801200320032903980b3703900a0b0240200141cc046a2d000022244102460d000240024020240d00200341ba056a200141cf046a2d00003a0000200341a00b6a200141dc046a290200370300200341a80b6a200141e4046a290200370300200341b00b6a200141ec046a2d00003a00002003200141cd046a2f00003b01b8052003200141d4046a2902003703980b200141d0046a2802002121410121220c010b200341b80a6a200141d0046a280200200210f0858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002221450d0020022021417f6a360218200241146a22212021280200222141016a22224100200228020c222420222024491b6b360200200241106a28020020214102746a2802002221200228020822224f0d1b200228020420214107746a220241046a200220022d00004108461b10e385808000200241083a0000200241016a200341980b6a41e30010848e8080001a0c010b0240200228020822212002280200470d0020022021109e86808000200228020821210b200228020420214107746a222141083a0000202141016a200341980b6a41e30010848e8080001a20022002280208222141016a3602080b410021220b200341ac016a41026a200341b8056a41026a2d00003a0000200341086a200341980b6a41086a290300370300200341106a200341980b6a41106a290300370300200341186a200341980b6a41186a280200360200200320032f01b8053b01ac01200320032903980b3703000b200341ec006a41026a2224200341f0006a41026a2d00003a0000200341980b6a41086a2225200341e0056a41086a290300370300200341980b6a41106a2226200341e0056a41106a290300370300200341980b6a41186a2227200341e0056a41186a280200360200200341e8006a41026a2228200341f4006a41026a2d00003a0000200341b80a6a41186a222920034188066a41186a280200360200200341b80a6a41106a222a20034188066a41106a290300370300200341b80a6a41086a222b20034188066a41086a290300370300200320032f01703b016c200320032903e0053703980b200320032f01743b016820032003290388063703b80a200341e4006a41026a222c200341f8006a41026a2d00003a0000200341b8056a41086a222d200341b0066a41086a290300370300200341b8056a41106a222e200341b0066a41106a290300370300200341b8056a41186a222f200341b0066a41186a280200360200200341e0006a41026a2230200341fc006a41026a2d00003a000020034190056a41086a2231200341d8066a41086a29030037030020034190056a41106a2232200341d8066a41106a29030037030020034190056a41186a2233200341d8066a41186a280200360200200320032f01783b0164200320032903b0063703b805200320032f017c3b0160200320032903d80637039005200341dc006a41026a223420034180016a41026a2d00003a0000200341e8046a41186a223520034180076a41186a280200360200200341e8046a41106a223620034180076a41106a290300370300200341e8046a41086a223720034180076a41086a290300370300200341d8006a41026a223820034184016a41026a2d00003a0000200341c0046a41186a2239200341a8076a41186a280200360200200341c0046a41106a223a200341a8076a41106a290300370300200341c0046a41086a223b200341a8076a41086a290300370300200320032f0180013b015c20032003290380073703e804200320032f0184013b0158200320032903a8073703c004200341d4006a41026a223c20034188016a41026a2d00003a0000200320032f0188013b015420034198046a41186a223d200341d0076a41186a28020036020020034198046a41106a223e200341d0076a41106a29030037030020034198046a41086a223f200341d0076a41086a290300370300200320032903d00737039804200341d0006a41026a22402003418c016a41026a2d00003a0000200320032f018c013b0150200341f0036a41186a2241200341f8076a41186a280200360200200341f0036a41106a2242200341f8076a41106a290300370300200341f0036a41086a2243200341f8076a41086a290300370300200320032903f8073703f003200341cc006a41026a224420034190016a41026a2d00003a0000200320032f0190013b014c200341c8036a41186a2245200341a0086a41186a280200360200200341c8036a41106a2246200341a0086a41106a290300370300200341c8036a41086a2247200341a0086a41086a290300370300200320032903a0083703c803200341c8006a41026a224820034194016a41026a2d00003a0000200320032f0194013b0148200341a0036a41186a2249200341c8086a41186a280200360200200341a0036a41106a224a200341c8086a41106a290300370300200341a0036a41086a224b200341c8086a41086a290300370300200320032903c8083703a003200341c4006a41026a224c20034198016a41026a2d00003a0000200320032f0198013b0144200341f8026a41186a224d200341f0086a41186a280200360200200341f8026a41106a224e200341f0086a41106a290300370300200341f8026a41086a224f200341f0086a41086a290300370300200320032903f0083703f802200341c0006a41026a22502003419c016a41026a2d00003a0000200320032f019c013b0140200341d0026a41186a225120034198096a41186a280200360200200341d0026a41106a225220034198096a41106a290300370300200341d0026a41086a225320034198096a41086a29030037030020032003290398093703d0022003413c6a41026a2254200341a0016a41026a2d00003a0000200320032f01a0013b013c200341a8026a41186a2255200341c0096a41186a280200360200200341a8026a41106a2256200341c0096a41106a290300370300200341a8026a41086a2257200341c0096a41086a290300370300200320032903c0093703a802200341386a41026a2258200341a4016a41026a2d00003a0000200320032f01a4013b013820034180026a41186a2259200341e8096a41186a28020036020020034180026a41106a225a200341e8096a41106a29030037030020034180026a41086a225b200341e8096a41086a290300370300200320032903e80937038002200341346a41026a225c200341a8016a41026a2d00003a0000200320032f01a8013b0134200341d8016a41186a225d200341900a6a41186a280200360200200341d8016a41106a225e200341900a6a41106a290300370300200341d8016a41086a225f200341900a6a41086a290300370300200320032903900a3703d801200341306a41026a2260200341ac016a41026a2d00003a0000200320032f01ac013b0130200341b0016a41186a2261200341186a280200360200200341b0016a41106a2262200341106a290300370300200341b0016a41086a2263200341086a290300370300200320032903003703b00141002d00fca3c680001a41c00441002802c8a3c68000118180808000002202450d18200220073a0000200220032f016c3b000120022005360204200220032903980b370008200220063a0024200220032f01683b002541032107200241036a20242d00003a0000200241106a2025290300370000200241186a2026290300370000200241206a2027280200360000200241276a20282d00003a0000200220043602282002200b3a00482002200836024c200220032903b80a37002c200241346a202b2903003700002002413c6a202a290300370000200241c4006a2029280200360000200220032f01643b0049200241cb006a202c2d00003a0000200220032903b805370050200241d8006a202d290300370000200241e0006a202e290300370000200241e8006a202f2802003600002002200a3a006c200220093602702002200f3a009001200220032f01603b006d200241ef006a20302d00003a00002002418c016a203328020036000020024184016a2032290300370000200241fc006a20312903003700002002200329039005370074200220032f015c3b00910120024193016a20342d00003a00002002200c36029401200241b0016a2035280200360000200241a8016a2036290300370000200241a0016a2037290300370000200220032903e804370098012002200e3a00b401200241b7016a20382d00003a0000200220032f01583b00b5012002200d3602b801200241d4016a2039280200360000200241cc016a203a290300370000200241c4016a203b290300370000200220032903c0043700bc01200220133a00d801200241db016a203c2d00003a0000200220032f01543b00d901200220103602dc01200241f8016a203d280200360000200241f0016a203e290300370000200241e8016a203f29030037000020022003290398043700e001200220123a00fc01200241ff016a20402d00003a0000200220032f01503b00fd0120022011360280022002419c026a204128020036000020024194026a20422903003700002002418c026a2043290300370000200220032903f00337008402200220173a00a002200241a3026a20442d00003a0000200220032f014c3b00a102200220143602a402200241c0026a2045280200360000200241b8026a2046290300370000200241b0026a2047290300370000200220032903c8033700a802200220163a00c402200241c7026a20482d00003a0000200220032f01483b00c502200220153602c802200241e4026a2049280200360000200241dc026a204a290300370000200241d4026a204b290300370000200220032903a0033700cc022002201b3a00e802200241eb026a204c2d00003a0000200220032f01443b00e902200220183602ec0220024188036a204d28020036000020024180036a204e290300370000200241f8026a204f290300370000200220032903f8023700f0022002201a3a008c032002418f036a20502d00003a0000200220032f01403b008d032002201936029003200241ac036a2051280200360000200241a4036a20522903003700002002419c036a2053290300370000200220032903d002370094032002201f3a00b003200241b3036a20542d00003a0000200220032f013c3b00b1032002201c3602b403200241d0036a2055280200360000200241c8036a2056290300370000200241c0036a2057290300370000200220032903a8023700b8032002201e3a00d403200241d7036a20582d00003a0000200220032f01383b00d5032002201d3602d803200241f4036a2059280200360000200241ec036a205a290300370000200241e4036a205b29030037000020022003290380023700dc03200220233a00f803200241fb036a205c2d00003a0000200220032f01343b00f903200220203602fc0320024198046a205d28020036000020024190046a205e29030037000020024188046a205f290300370000200220032903d80137008004200220223a009c042002419f046a20602d00003a0000200220032f01303b009d04200220213602a004200241bc046a2061280200360000200241b4046a2062290300370000200241ac046a2063290300370000200220032903b0013700a40420012802040d030c040b4102210541022108024020012d002c4102460d00200341980b6a2001412c6a200210f18580800020034190066a200341a10b6a29000037030020034198066a200341a90b6a290000370300200341a0066a200341b10b6a290000370300200341a7066a200341b80b6a280000360000200320032900990b3703880620032d00980b21080b0240200141d0006a22092d00004102460d00200341980b6a2009200210f185808000200341b8066a200341a10b6a290000370300200341c0066a200341a90b6a290000370300200341c8066a200341b10b6a290000370300200341cf066a200341b80b6a280000360000200320032900990b3703b00620032d00980b21050b410221094102210c0240200141f4006a220d2d00004102460d00200341980b6a200d200210f185808000200341e0066a200341a10b6a290000370300200341e8066a200341a90b6a290000370300200341f0066a200341b10b6a290000370300200341f7066a200341b80b6a280000360000200320032900990b3703d80620032d00980b210c0b024020014198016a220d2d00004102460d00200341980b6a200d200210f18580800020034188076a200341a10b6a29000037030020034190076a200341a90b6a29000037030020034198076a200341b10b6a2900003703002003419f076a200341b80b6a280000360000200320032900990b3703800720032d00980b21090b4102210d410221100240200141bc016a22112d00004102460d00200341980b6a2011200210f185808000200341b0076a200341a10b6a290000370300200341b8076a200341a90b6a290000370300200341c0076a200341b10b6a290000370300200341c7076a200341b80b6a280000360000200320032900990b3703a80720032d00980b21100b0240200141e0016a22112d00004102460d00200341980b6a2011200210f185808000200341d8076a200341a10b6a290000370300200341e0076a200341a90b6a290000370300200341e8076a200341b10b6a290000370300200341ef076a200341b80b6a280000360000200320032900990b3703d00720032d00980b210d0b4102211141022114024020014184026a22152d00004102460d00200341980b6a2015200210f18580800020034180086a200341a10b6a29000037030020034188086a200341a90b6a29000037030020034190086a200341b10b6a29000037030020034197086a200341b80b6a280000360000200320032900990b3703f80720032d00980b21140b0240200141a8026a22152d00004102460d00200341980b6a2015200210f185808000200341a8086a200341a10b6a290000370300200341b0086a200341a90b6a290000370300200341b8086a200341b10b6a290000370300200341bf086a200341b80b6a280000360000200320032900990b3703a00820032d00980b21110b41022115410221180240200141cc026a22192d00004102460d00200341980b6a2019200210f185808000200341d0086a200341a10b6a290000370300200341d8086a200341a90b6a290000370300200341e0086a200341b10b6a290000370300200341e7086a200341b80b6a280000360000200320032900990b3703c80820032d00980b21180b0240200141f0026a22192d00004102460d00200341980b6a2019200210f185808000200341f8086a200341a10b6a29000037030020034180096a200341a90b6a29000037030020034188096a200341b10b6a2900003703002003418f096a200341b80b6a280000360000200320032900990b3703f00820032d00980b21150b410221194102211c024020014194036a221d2d00004102460d00200341980b6a201d200210f185808000200341a0096a200341a10b6a290000370300200341a8096a200341a90b6a290000370300200341b0096a200341b10b6a290000370300200341b7096a200341b80b6a280000360000200320032900990b3703980920032d00980b211c0b0240200141b8036a221d2d00004102460d00200341980b6a201d200210f185808000200341c8096a200341a10b6a290000370300200341d0096a200341a90b6a290000370300200341d8096a200341b10b6a290000370300200341df096a200341b80b6a280000360000200320032900990b3703c00920032d00980b21190b4102211d410221200240200141dc036a22212d00004102460d00200341980b6a2021200210f185808000200341f0096a200341a10b6a290000370300200341f8096a200341a90b6a290000370300200341800a6a200341b10b6a290000370300200341870a6a200341b80b6a280000360000200320032900990b3703e80920032d00980b21200b024020014180046a22212d00004102460d00200341980b6a2021200210f185808000200341980a6a200341a10b6a290000370300200341a00a6a200341a90b6a290000370300200341a80a6a200341b10b6a290000370300200341af0a6a200341b80b6a280000360000200320032900990b3703900a20032d00980b211d0b41022121410221070240200141a4046a22062d00004102460d00200341980b6a2006200210f185808000200341086a200341a10b6a290000370300200341106a200341a90b6a290000370300200341186a200341b10b6a2900003703002003411f6a200341b80b6a280000360000200320032900990b37030020032d00980b21070b0240200141c8046a22062d00004102460d00200341980b6a2006200210f185808000200341c00a6a200341a10b6a290000370300200341c80a6a200341a90b6a290000370300200341d00a6a200341b10b6a290000370300200341d70a6a200341b80b6a280000360000200320032900990b3703b80a20032d00980b21210b200341980b6a411f6a220620034188066a411f6a280000360000200341980b6a41186a220b20034188066a41186a290300370300200341980b6a41106a220a20034188066a41106a290300370300200341980b6a41086a220f20034188066a41086a290300370300200341e0056a41086a220e200341b0066a41086a290300370300200341e0056a41106a2213200341b0066a41106a290300370300200341e0056a41186a2212200341b0066a41186a290300370300200341e0056a411f6a2217200341b0066a411f6a28000036000020032003290388063703980b200320032903b0063703e005200341b8056a411f6a2216200341d8066a411f6a280000360000200341b8056a41186a221b200341d8066a41186a290300370300200341b8056a41106a221a200341d8066a41106a290300370300200341b8056a41086a221f200341d8066a41086a29030037030020034190056a41086a221e20034180076a41086a29030037030020034190056a41106a222320034180076a41106a29030037030020034190056a41186a222220034180076a41186a29030037030020034190056a411f6a222420034180076a411f6a280000360000200320032903d8063703b805200320032903800737039005200341e8046a411f6a2225200341a8076a411f6a280000360000200341e8046a41186a2226200341a8076a41186a290300370300200341e8046a41106a2227200341a8076a41106a290300370300200341e8046a41086a2228200341a8076a41086a290300370300200341c0046a411f6a2229200341d0076a411f6a280000360000200341c0046a41186a222a200341d0076a41186a290300370300200341c0046a41106a222b200341d0076a41106a290300370300200341c0046a41086a222c200341d0076a41086a290300370300200320032903a8073703e804200320032903d0073703c00420034198046a411f6a222d200341f8076a411f6a28000036000020034198046a41186a222e200341f8076a41186a29030037030020034198046a41106a222f200341f8076a41106a29030037030020034198046a41086a2230200341f8076a41086a290300370300200320032903f80737039804200341f0036a411f6a2231200341a0086a411f6a280000360000200341f0036a41186a2232200341a0086a41186a290300370300200341f0036a41106a2233200341a0086a41106a290300370300200341f0036a41086a2234200341a0086a41086a290300370300200320032903a0083703f003200341c8036a411f6a2235200341c8086a411f6a280000360000200341c8036a41186a2236200341c8086a41186a290300370300200341c8036a41106a2237200341c8086a41106a290300370300200341c8036a41086a2238200341c8086a41086a290300370300200320032903c8083703c803200341a0036a411f6a2239200341f0086a411f6a280000360000200341a0036a41186a223a200341f0086a41186a290300370300200341a0036a41106a223b200341f0086a41106a290300370300200341a0036a41086a223c200341f0086a41086a290300370300200320032903f0083703a003200341f8026a411f6a223d20034198096a411f6a280000360000200341f8026a41186a223e20034198096a41186a290300370300200341f8026a41106a223f20034198096a41106a290300370300200341f8026a41086a224020034198096a41086a29030037030020032003290398093703f802200341d0026a411f6a2241200341c0096a411f6a280000360000200341d0026a41186a2242200341c0096a41186a290300370300200341d0026a41106a2243200341c0096a41106a290300370300200341d0026a41086a2244200341c0096a41086a290300370300200320032903c0093703d002200341a8026a411f6a2245200341e8096a411f6a280000360000200341a8026a41186a2246200341e8096a41186a290300370300200341a8026a41106a2247200341e8096a41106a290300370300200341a8026a41086a2248200341e8096a41086a290300370300200320032903e8093703a80220034180026a411f6a2249200341900a6a411f6a28000036000020034180026a41186a224a200341900a6a41186a29030037030020034180026a41106a224b200341900a6a41106a29030037030020034180026a41086a224c200341900a6a41086a290300370300200320032903900a37038002200341d8016a411f6a224d2003411f6a280000360000200341d8016a41186a224e200341186a290300370300200341d8016a41106a224f200341106a290300370300200341d8016a41086a2250200341086a290300370300200320032903003703d801200341b0016a411f6a2251200341b80a6a411f6a280000360000200341b0016a41186a2252200341b80a6a41186a290300370300200341b0016a41106a2253200341b80a6a41106a290300370300200341b0016a41086a2254200341b80a6a41086a290300370300200320032903b80a3703b00141002d00fca3c680001a41c00441002802c8a3c68000118180808000002202450d18200220083a0000200220032903980b370001200220053a0024200220032903e005370025200241096a200f290300370000200241116a200a290300370000200241196a200b290300370000200241206a20062800003600002002412d6a200e290300370000200241356a20132903003700002002413d6a2012290300370000200241c4006a20172800003600002002200c3a0048200220093a006c200220032903b805370049200241d1006a201f290300370000200241d9006a201a290300370000200241e1006a201b290300370000200241e8006a2016280000360000200220032903900537006d200241f5006a201e290300370000200241fd006a202329030037000020024185016a20222903003700002002418c016a2024280000360000200220103a0090012002200d3a00b401200220032903e8043700910120024199016a2028290300370000200241a1016a2027290300370000200241a9016a2026290300370000200241b0016a2025280000360000200220032903c0043700b501200241bd016a202c290300370000200241c5016a202b290300370000200241cd016a202a290300370000200241d4016a2029280000360000200220143a00d801200241f8016a202d280000360000200241f1016a202e290300370000200241e9016a202f290300370000200241e1016a203029030037000020022003290398043700d901200220113a00fc012002419c026a203128000036000020024195026a20322903003700002002418d026a203329030037000020024185026a2034290300370000200220032903f0033700fd01200220183a00a002200241c0026a2035280000360000200241b9026a2036290300370000200241b1026a2037290300370000200241a9026a2038290300370000200220032903c8033700a102200220153a00c402200241e4026a2039280000360000200241dd026a203a290300370000200241d5026a203b290300370000200241cd026a203c290300370000200220032903a0033700c5022002201c3a00e80220024188036a203d28000036000020024181036a203e290300370000200241f9026a203f290300370000200241f1026a2040290300370000200220032903f8023700e902200220193a008c03200241ac036a2041280000360000200241a5036a20422903003700002002419d036a204329030037000020024195036a2044290300370000200220032903d00237008d03200220203a00b003200241d0036a2045280000360000200241c9036a2046290300370000200241c1036a2047290300370000200241b9036a2048290300370000200220032903a8023700b1032002201d3a00d403200241f4036a2049280000360000200241ed036a204a290300370000200241e5036a204b290300370000200241dd036a204c29030037000020022003290380023700d503200220073a00f80320024198046a204d28000036000020024191046a204e29030037000020024189046a204f29030037000020024181046a2050290300370000200220032903d8013700f903200220213a009c04200241bc046a2051280000360000200241b5046a2052290300370000200241ad046a2053290300370000200241a5046a2054290300370000200220032903b00137009d04200341980b6a200141ec046a10fa8d80800020040d04410321010c050b200341a40b6a42013702002003410136029c0b200341fcd1c280003602980b200341e0818080003602bc0a200341c4d2c280003602b80a2003200341b80a6a3602a00b200341980b6a41ccd2c2800010f680808000000b200041043a00000c170b2001410c6a210402400240200141086a2802002205450d002004280200210420052005280200220141016a360200410021072001417f4c0d190c010b200341026a200441026a2d00003a0000200341980b6a41086a2001411f6a290000370300200341a50b6a200141246a290000370000200320042f00003b0100200320012900173703980b20012800132104200128000f2105410121070b200341900a6a41026a200341026a2d00003a0000200341b80a6a41086a200341980b6a41086a290300370300200341b80a6a41106a200341980b6a41106a290300370300200341b80a6a41186a200341980b6a41186a290300370300200320032f01003b01900a200320032903980b3703b80a0b200020073a000420002002360230200041073a0000200041056a20032f01900a3b00002000410c6a2004360200200041086a2005360200200041106a20032903b80a370000200041076a200341920a6a2d00003a0000200041186a200341b80a6a41086a290300370000200041206a200341b80a6a41106a290300370000200041286a200341b80a6a41186a2903003700000c150b200141086a21040240024020012802042205450d002004280200210420052005280200220841016a360200410021012008417f4c0d170c010b200341900a6a41026a200441026a2d00003a0000200341b80a6a41086a2001411b6a290000370300200341c50a6a200141206a290000370000200320042f00003b01900a200320012900133703b80a200128000f2104200128000b2105410121010b200341e8096a41026a200341900a6a41026a2d00003a0000200341086a200341b80a6a41086a290300370300200341106a200341b80a6a41106a290300370300200341186a200341b80a6a41186a290300370300200320032f01900a3b01e809200320032903b80a3703000b200020032902980b3702302000200236022c200020013a0000200020032f01e8093b0001200041d8006a200341c00b6a290200370200200041d0006a200341980b6a41206a290200370200200041c8006a200341980b6a41186a290200370200200041c0006a200341980b6a41106a290200370200200041386a200341980b6a41086a290200370200200041036a200341ea096a2d00003a000020002004360208200020053602042000200329030037000c200041146a200341086a2903003700002000411c6a200341106a290300370000200041246a200341186a2903003700000c130b2001200541d4d7c2800010f980808000000b2005200441d4d7c2800010f980808000000b2004200841d4d7c2800010f980808000000b2008200941d4d7c2800010f980808000000b2009200c41d4d7c2800010f980808000000b200c200d41d4d7c2800010f980808000000b200d201041d4d7c2800010f980808000000b2010201141d4d7c2800010f980808000000b2011201441d4d7c2800010f980808000000b2014201541d4d7c2800010f980808000000b2015201841d4d7c2800010f980808000000b2018201941d4d7c2800010f980808000000b2019201c41d4d7c2800010f980808000000b201c201d41d4d7c2800010f980808000000b201d202041d4d7c2800010f980808000000b2020202141d4d7c2800010f980808000000b2021202241d4d7c2800010f980808000000b410441c00410b280808000000b410441c00410b280808000000b200341800c6a2480808080000f0b00000bb50301037f23808080800041d0016b220324808080800002400240024020012d00000d0020002001290001370001200041196a200141196a290000370000200041116a200141116a290000370000200041096a200141096a290000370000410121020c010b2003410c6a2001280204200210f085808000200341f0006a2003410c6a41e00010848e8080001a02400240200241186a2802002201450d0020022001417f6a360218200241146a22012001280200220141016a22044100200228020c220520042005491b6b360200200241106a28020020014102746a2802002201200228020822044f0d03200228020420014107746a220241046a200220022d00004108461b10e385808000200241083a0000200241016a200341ed006a41e30010848e8080001a0c010b0240200228020822012002280200470d0020022001109e86808000200228020821010b200228020420014107746a220141083a0000200141016a200341ed006a41e30010848e8080001a20022002280208220141016a3602080b20002001360204410021020b200020023a0000200341d0016a2480808080000f0b2001200441d4d7c2800010f980808000000bb09e0101617f23808080800041800c6b2203248080808000024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024020012802002204417e6a2205410420054106491b0e06050001020304050b200341980b6a200141046a10fa8d808000200141386a21050240024020012802342202450d002005280200210520022002280200220441016a360200410021012004417f4a0d010c1f0b200341026a200541026a2d00003a0000200341c00a6a200141cb006a290000370300200341c50a6a200141d0006a290000370000200320052f00003b0100200320012900433703b80a200128003f2105200128003b2102410121010b200020032902980b370230200020013a0004200041056a20032f01003b0000200041d8006a200341980b6a41286a290200370200200041d0006a200341980b6a41206a290200370200200041c8006a200341980b6a41186a290200370200200041c0006a200341980b6a41106a290200370200200041386a200341980b6a41086a290200370200200041076a200341026a2d00003a00002000410c6a2005360200200041086a2002360200200041053a0000200041106a20032903b80a370200200041186a200341b80a6a41086a290300370200200041206a200341b80a6a41106a290300370200200041286a200341b80a6a41186a2903003702000c1c0b2003200141286a10fa8d8080000240024020012d00040d00200341ba0a6a200141076a2d00003a0000200341980b6a41086a200141146a290200370300200341a80b6a2001411c6a290200370300200341b00b6a200141246a2d00003a00002003200141056a2f00003b01b80a20032001410c6a2902003703980b200141086a2802002101410121020c010b200341b80a6a200141086a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002201450d0020022001417f6a360218200241146a22012001280200220141016a22054100200228020c220420052004491b6b360200200241106a28020020014102746a2802002201200228020822054f0d0b200228020420014107746a220241046a200220022d00004108461b10e385808000200241083a0000200241016a200341980b6a41e30010848e8080001a0c010b0240200228020822012002280200470d0020022001109e86808000200228020821010b200228020420014107746a220141083a0000200141016a200341980b6a41e30010848e8080001a20022002280208220141016a3602080b410021020b20002003290200370228200020023a0004200041056a20032f01b80a3b0000200041086a2001360200200041d0006a200341286a290200370200200041c8006a200341206a290200370200200041c0006a200341186a290200370200200041386a200341106a290200370200200041306a200341086a290200370200200041076a200341ba0a6a2d00003a0000200041063a0000200041246a200341980b6a41186a2802003602002000411c6a200341980b6a41106a290300370200200041146a200341980b6a41086a2903003702002000410c6a20032903980b3702000c1b0b4102210641022107024020012d003022044102460d000240024020040d00200341026a200141336a2d00003a0000200341a00b6a200141c0006a290200370300200341a80b6a200141c8006a290200370300200341b00b6a200141d0006a2d00003a00002003200141316a2f00003b01002003200141386a2902003703980b200141346a2802002105410121070c010b200341b80a6a200141346a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002205450d0020022005417f6a360218200241146a22052005280200220541016a22044100200228020c220820042008491b6b360200200241106a28020020054102746a2802002205200228020822044f0d0c200228020420054107746a220441046a200420042d00004108461b10e385808000200441083a0000200441016a200341980b6a41e30010848e8080001a0c010b0240200228020822052002280200470d0020022005109e86808000200228020821050b200228020420054107746a220541083a0000200541016a200341980b6a41e30010848e8080001a20022002280208220541016a3602080b410021070b200341f0006a41026a200341026a2d00003a0000200341e0056a41086a200341980b6a41086a290300370300200341e0056a41106a200341980b6a41106a290300370300200341e0056a41186a200341980b6a41186a280200360200200320032f01003b0170200320032903980b3703e0050b0240200141d4006a2d000022084102460d000240024020080d00200341026a200141d7006a2d00003a0000200341a00b6a200141e4006a290200370300200341a80b6a200141ec006a290200370300200341b00b6a200141f4006a2d00003a00002003200141d5006a2f00003b01002003200141dc006a2902003703980b200141d8006a2802002104410121060c010b200341b80a6a200141d8006a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002204450d0020022004417f6a360218200241146a22042004280200220441016a22084100200228020c220920082009491b6b360200200241106a28020020044102746a2802002204200228020822084f0d0d200228020420044107746a220841046a200820082d00004108461b10e385808000200841083a0000200841016a200341980b6a41e30010848e8080001a0c010b0240200228020822042002280200470d0020022004109e86808000200228020821040b200228020420044107746a220441083a0000200441016a200341980b6a41e30010848e8080001a20022002280208220441016a3602080b410021060b200341f4006a41026a200341026a2d00003a000020034188066a41086a200341980b6a41086a29030037030020034188066a41106a200341980b6a41106a29030037030020034188066a41186a200341980b6a41186a280200360200200320032f01003b0174200320032903980b370388060b4102210a4102210b0240200141f8006a2d000022094102460d000240024020090d00200341026a200141fb006a2d00003a0000200341a00b6a20014188016a290200370300200341a80b6a20014190016a290200370300200341b00b6a20014198016a2d00003a00002003200141f9006a2f00003b0100200320014180016a2902003703980b200141fc006a28020021084101210b0c010b200341b80a6a200141fc006a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002208450d0020022008417f6a360218200241146a22082008280200220841016a22094100200228020c220c2009200c491b6b360200200241106a28020020084102746a2802002208200228020822094f0d0e200228020420084107746a220941046a200920092d00004108461b10e385808000200941083a0000200941016a200341980b6a41e30010848e8080001a0c010b0240200228020822082002280200470d0020022008109e86808000200228020821080b200228020420084107746a220841083a0000200841016a200341980b6a41e30010848e8080001a20022002280208220841016a3602080b4100210b0b200341f8006a41026a200341026a2d00003a0000200341b0066a41086a200341980b6a41086a290300370300200341b0066a41106a200341980b6a41106a290300370300200341b0066a41186a200341980b6a41186a280200360200200320032f01003b0178200320032903980b3703b0060b02402001419c016a2d0000220c4102460d0002400240200c0d00200341026a2001419f016a2d00003a0000200341a00b6a200141ac016a290200370300200341a80b6a200141b4016a290200370300200341b00b6a200141bc016a2d00003a000020032001419d016a2f00003b01002003200141a4016a2902003703980b200141a0016a28020021094101210a0c010b200341b80a6a200141a0016a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002209450d0020022009417f6a360218200241146a22092009280200220941016a220c4100200228020c220d200c200d491b6b360200200241106a28020020094102746a28020022092002280208220c4f0d0f200228020420094107746a220c41046a200c200c2d00004108461b10e385808000200c41083a0000200c41016a200341980b6a41e30010848e8080001a0c010b0240200228020822092002280200470d0020022009109e86808000200228020821090b200228020420094107746a220941083a0000200941016a200341980b6a41e30010848e8080001a20022002280208220941016a3602080b4100210a0b200341fc006a41026a200341026a2d00003a0000200341d8066a41086a200341980b6a41086a290300370300200341d8066a41106a200341980b6a41106a290300370300200341d8066a41186a200341980b6a41186a280200360200200320032f01003b017c200320032903980b3703d8060b4102210e4102210f0240200141c0016a2d0000220d4102460d0002400240200d0d00200341026a200141c3016a2d00003a0000200341a00b6a200141d0016a290200370300200341a80b6a200141d8016a290200370300200341b00b6a200141e0016a2d00003a00002003200141c1016a2f00003b01002003200141c8016a2902003703980b200141c4016a280200210c4101210f0c010b200341b80a6a200141c4016a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a280200220c450d002002200c417f6a360218200241146a220c200c280200220c41016a220d4100200228020c2210200d2010491b6b360200200241106a280200200c4102746a280200220c2002280208220d4f0d102002280204200c4107746a220d41046a200d200d2d00004108461b10e385808000200d41083a0000200d41016a200341980b6a41e30010848e8080001a0c010b02402002280208220c2002280200470d002002200c109e868080002002280208210c0b2002280204200c4107746a220c41083a0000200c41016a200341980b6a41e30010848e8080001a20022002280208220c41016a3602080b4100210f0b20034180016a41026a200341026a2d00003a000020034180076a41086a200341980b6a41086a29030037030020034180076a41106a200341980b6a41106a29030037030020034180076a41186a200341980b6a41186a280200360200200320032f01003b018001200320032903980b370380070b0240200141e4016a2d000022104102460d000240024020100d00200341026a200141e7016a2d00003a0000200341a00b6a200141f4016a290200370300200341a80b6a200141fc016a290200370300200341b00b6a20014184026a2d00003a00002003200141e5016a2f00003b01002003200141ec016a2902003703980b200141e8016a280200210d4101210e0c010b200341b80a6a200141e8016a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a280200220d450d002002200d417f6a360218200241146a220d200d280200220d41016a22104100200228020c221120102011491b6b360200200241106a280200200d4102746a280200220d200228020822104f0d112002280204200d4107746a221041046a201020102d00004108461b10e385808000201041083a0000201041016a200341980b6a41e30010848e8080001a0c010b02402002280208220d2002280200470d002002200d109e868080002002280208210d0b2002280204200d4107746a220d41083a0000200d41016a200341980b6a41e30010848e8080001a20022002280208220d41016a3602080b4100210e0b20034184016a41026a200341026a2d00003a0000200341a8076a41086a200341980b6a41086a290300370300200341a8076a41106a200341980b6a41106a290300370300200341a8076a41186a200341980b6a41186a280200360200200320032f01003b018401200320032903980b3703a8070b4102211241022113024020014188026a2d000022114102460d000240024020110d00200341026a2001418b026a2d00003a0000200341a00b6a20014198026a290200370300200341a80b6a200141a0026a290200370300200341b00b6a200141a8026a2d00003a0000200320014189026a2f00003b0100200320014190026a2902003703980b2001418c026a2802002110410121130c010b200341b80a6a2001418c026a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002210450d0020022010417f6a360218200241146a22102010280200221041016a22114100200228020c221420112014491b6b360200200241106a28020020104102746a2802002210200228020822114f0d12200228020420104107746a221141046a201120112d00004108461b10e385808000201141083a0000201141016a200341980b6a41e30010848e8080001a0c010b0240200228020822102002280200470d0020022010109e86808000200228020821100b200228020420104107746a221041083a0000201041016a200341980b6a41e30010848e8080001a20022002280208221041016a3602080b410021130b20034188016a41026a200341026a2d00003a0000200341d0076a41086a200341980b6a41086a290300370300200341d0076a41106a200341980b6a41106a290300370300200341d0076a41186a200341980b6a41186a280200360200200320032f01003b018801200320032903980b3703d0070b0240200141ac026a2d000022144102460d000240024020140d00200341026a200141af026a2d00003a0000200341a00b6a200141bc026a290200370300200341a80b6a200141c4026a290200370300200341b00b6a200141cc026a2d00003a00002003200141ad026a2f00003b01002003200141b4026a2902003703980b200141b0026a2802002111410121120c010b200341b80a6a200141b0026a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002211450d0020022011417f6a360218200241146a22112011280200221141016a22144100200228020c221520142015491b6b360200200241106a28020020114102746a2802002211200228020822144f0d13200228020420114107746a221441046a201420142d00004108461b10e385808000201441083a0000201441016a200341980b6a41e30010848e8080001a0c010b0240200228020822112002280200470d0020022011109e86808000200228020821110b200228020420114107746a221141083a0000201141016a200341980b6a41e30010848e8080001a20022002280208221141016a3602080b410021120b2003418c016a41026a200341026a2d00003a0000200341f8076a41086a200341980b6a41086a290300370300200341f8076a41106a200341980b6a41106a290300370300200341f8076a41186a200341980b6a41186a280200360200200320032f01003b018c01200320032903980b3703f8070b41022116410221170240200141d0026a2d000022154102460d000240024020150d00200341026a200141d3026a2d00003a0000200341a00b6a200141e0026a290200370300200341a80b6a200141e8026a290200370300200341b00b6a200141f0026a2d00003a00002003200141d1026a2f00003b01002003200141d8026a2902003703980b200141d4026a2802002114410121170c010b200341b80a6a200141d4026a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002214450d0020022014417f6a360218200241146a22142014280200221441016a22154100200228020c221820152018491b6b360200200241106a28020020144102746a2802002214200228020822154f0d14200228020420144107746a221541046a201520152d00004108461b10e385808000201541083a0000201541016a200341980b6a41e30010848e8080001a0c010b0240200228020822142002280200470d0020022014109e86808000200228020821140b200228020420144107746a221441083a0000201441016a200341980b6a41e30010848e8080001a20022002280208221441016a3602080b410021170b20034190016a41026a200341026a2d00003a0000200341a0086a41086a200341980b6a41086a290300370300200341a0086a41106a200341980b6a41106a290300370300200341a0086a41186a200341980b6a41186a280200360200200320032f01003b019001200320032903980b3703a0080b0240200141f4026a2d000022184102460d000240024020180d00200341026a200141f7026a2d00003a0000200341a00b6a20014184036a290200370300200341a80b6a2001418c036a290200370300200341b00b6a20014194036a2d00003a00002003200141f5026a2f00003b01002003200141fc026a2902003703980b200141f8026a2802002115410121160c010b200341b80a6a200141f8026a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002215450d0020022015417f6a360218200241146a22152015280200221541016a22184100200228020c221920182019491b6b360200200241106a28020020154102746a2802002215200228020822184f0d15200228020420154107746a221841046a201820182d00004108461b10e385808000201841083a0000201841016a200341980b6a41e30010848e8080001a0c010b0240200228020822152002280200470d0020022015109e86808000200228020821150b200228020420154107746a221541083a0000201541016a200341980b6a41e30010848e8080001a20022002280208221541016a3602080b410021160b20034194016a41026a200341026a2d00003a0000200341c8086a41086a200341980b6a41086a290300370300200341c8086a41106a200341980b6a41106a290300370300200341c8086a41186a200341980b6a41186a280200360200200320032f01003b019401200320032903980b3703c8080b4102211a4102211b024020014198036a2d000022194102460d000240024020190d00200341026a2001419b036a2d00003a0000200341a00b6a200141a8036a290200370300200341a80b6a200141b0036a290200370300200341b00b6a200141b8036a2d00003a0000200320014199036a2f00003b01002003200141a0036a2902003703980b2001419c036a28020021184101211b0c010b200341b80a6a2001419c036a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002218450d0020022018417f6a360218200241146a22182018280200221841016a22194100200228020c221c2019201c491b6b360200200241106a28020020184102746a2802002218200228020822194f0d16200228020420184107746a221941046a201920192d00004108461b10e385808000201941083a0000201941016a200341980b6a41e30010848e8080001a0c010b0240200228020822182002280200470d0020022018109e86808000200228020821180b200228020420184107746a221841083a0000201841016a200341980b6a41e30010848e8080001a20022002280208221841016a3602080b4100211b0b20034198016a41026a200341026a2d00003a0000200341f0086a41086a200341980b6a41086a290300370300200341f0086a41106a200341980b6a41106a290300370300200341f0086a41186a200341980b6a41186a280200360200200320032f01003b019801200320032903980b3703f0080b0240200141bc036a2d0000221c4102460d0002400240201c0d00200341026a200141bf036a2d00003a0000200341a00b6a200141cc036a290200370300200341a80b6a200141d4036a290200370300200341b00b6a200141dc036a2d00003a00002003200141bd036a2f00003b01002003200141c4036a2902003703980b200141c0036a28020021194101211a0c010b200341b80a6a200141c0036a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002219450d0020022019417f6a360218200241146a22192019280200221941016a221c4100200228020c221d201c201d491b6b360200200241106a28020020194102746a28020022192002280208221c4f0d17200228020420194107746a221c41046a201c201c2d00004108461b10e385808000201c41083a0000201c41016a200341980b6a41e30010848e8080001a0c010b0240200228020822192002280200470d0020022019109e86808000200228020821190b200228020420194107746a221941083a0000201941016a200341980b6a41e30010848e8080001a20022002280208221941016a3602080b4100211a0b2003419c016a41026a200341026a2d00003a000020034198096a41086a200341980b6a41086a29030037030020034198096a41106a200341980b6a41106a29030037030020034198096a41186a200341980b6a41186a280200360200200320032f01003b019c01200320032903980b370398090b4102211e4102211f0240200141e0036a2d0000221d4102460d0002400240201d0d00200341026a200141e3036a2d00003a0000200341a00b6a200141f0036a290200370300200341a80b6a200141f8036a290200370300200341b00b6a20014180046a2d00003a00002003200141e1036a2f00003b01002003200141e8036a2902003703980b200141e4036a280200211c4101211f0c010b200341b80a6a200141e4036a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a280200221c450d002002201c417f6a360218200241146a221c201c280200221c41016a221d4100200228020c2220201d2020491b6b360200200241106a280200201c4102746a280200221c2002280208221d4f0d182002280204201c4107746a221d41046a201d201d2d00004108461b10e385808000201d41083a0000201d41016a200341980b6a41e30010848e8080001a0c010b02402002280208221c2002280200470d002002201c109e868080002002280208211c0b2002280204201c4107746a221c41083a0000201c41016a200341980b6a41e30010848e8080001a20022002280208221c41016a3602080b4100211f0b200341a0016a41026a200341026a2d00003a0000200341c0096a41086a200341980b6a41086a290300370300200341c0096a41106a200341980b6a41106a290300370300200341c0096a41186a200341980b6a41186a280200360200200320032f01003b01a001200320032903980b3703c0090b024020014184046a2d000022204102460d000240024020200d00200341026a20014187046a2d00003a0000200341a00b6a20014194046a290200370300200341a80b6a2001419c046a290200370300200341b00b6a200141a4046a2d00003a0000200320014185046a2f00003b010020032001418c046a2902003703980b20014188046a280200211d4101211e0c010b200341b80a6a20014188046a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a280200221d450d002002201d417f6a360218200241146a221d201d280200221d41016a22204100200228020c222120202021491b6b360200200241106a280200201d4102746a280200221d200228020822204f0d192002280204201d4107746a222041046a202020202d00004108461b10e385808000202041083a0000202041016a200341980b6a41e30010848e8080001a0c010b02402002280208221d2002280200470d002002201d109e868080002002280208211d0b2002280204201d4107746a221d41083a0000201d41016a200341980b6a41e30010848e8080001a20022002280208221d41016a3602080b4100211e0b200341a4016a41026a200341026a2d00003a0000200341e8096a41086a200341980b6a41086a290300370300200341e8096a41106a200341980b6a41106a290300370300200341e8096a41186a200341980b6a41186a280200360200200320032f01003b01a401200320032903980b3703e8090b41022122410221230240200141a8046a2d000022214102460d000240024020210d00200341026a200141ab046a2d00003a0000200341a00b6a200141b8046a290200370300200341a80b6a200141c0046a290200370300200341b00b6a200141c8046a2d00003a00002003200141a9046a2f00003b01002003200141b0046a2902003703980b200141ac046a2802002120410121230c010b200341b80a6a200141ac046a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002220450d0020022020417f6a360218200241146a22202020280200222041016a22214100200228020c222320212023491b6b360200200241106a28020020204102746a2802002220200228020822214f0d1a200228020420204107746a222141046a202120212d00004108461b10e385808000202141083a0000202141016a200341980b6a41e30010848e8080001a0c010b0240200228020822202002280200470d0020022020109e86808000200228020821200b200228020420204107746a222041083a0000202041016a200341980b6a41e30010848e8080001a20022002280208222041016a3602080b410021230b200341a8016a41026a200341026a2d00003a0000200341900a6a41086a200341980b6a41086a290300370300200341900a6a41106a200341980b6a41106a290300370300200341900a6a41186a200341980b6a41186a280200360200200320032f01003b01a801200320032903980b3703900a0b0240200141cc046a2d000022244102460d000240024020240d00200341ba056a200141cf046a2d00003a0000200341a00b6a200141dc046a290200370300200341a80b6a200141e4046a290200370300200341b00b6a200141ec046a2d00003a00002003200141cd046a2f00003b01b8052003200141d4046a2902003703980b200141d0046a2802002121410121220c010b200341b80a6a200141d0046a280200200210f2858080002003419b0b6a200341b80a6a41e00010848e8080001a02400240200241186a2802002221450d0020022021417f6a360218200241146a22212021280200222141016a22224100200228020c222420222024491b6b360200200241106a28020020214102746a2802002221200228020822224f0d1b200228020420214107746a220241046a200220022d00004108461b10e385808000200241083a0000200241016a200341980b6a41e30010848e8080001a0c010b0240200228020822212002280200470d0020022021109e86808000200228020821210b200228020420214107746a222141083a0000202141016a200341980b6a41e30010848e8080001a20022002280208222141016a3602080b410021220b200341ac016a41026a200341b8056a41026a2d00003a0000200341086a200341980b6a41086a290300370300200341106a200341980b6a41106a290300370300200341186a200341980b6a41186a280200360200200320032f01b8053b01ac01200320032903980b3703000b200341ec006a41026a2224200341f0006a41026a2d00003a0000200341980b6a41086a2225200341e0056a41086a290300370300200341980b6a41106a2226200341e0056a41106a290300370300200341980b6a41186a2227200341e0056a41186a280200360200200341e8006a41026a2228200341f4006a41026a2d00003a0000200341b80a6a41186a222920034188066a41186a280200360200200341b80a6a41106a222a20034188066a41106a290300370300200341b80a6a41086a222b20034188066a41086a290300370300200320032f01703b016c200320032903e0053703980b200320032f01743b016820032003290388063703b80a200341e4006a41026a222c200341f8006a41026a2d00003a0000200341b8056a41086a222d200341b0066a41086a290300370300200341b8056a41106a222e200341b0066a41106a290300370300200341b8056a41186a222f200341b0066a41186a280200360200200341e0006a41026a2230200341fc006a41026a2d00003a000020034190056a41086a2231200341d8066a41086a29030037030020034190056a41106a2232200341d8066a41106a29030037030020034190056a41186a2233200341d8066a41186a280200360200200320032f01783b0164200320032903b0063703b805200320032f017c3b0160200320032903d80637039005200341dc006a41026a223420034180016a41026a2d00003a0000200341e8046a41186a223520034180076a41186a280200360200200341e8046a41106a223620034180076a41106a290300370300200341e8046a41086a223720034180076a41086a290300370300200341d8006a41026a223820034184016a41026a2d00003a0000200341c0046a41186a2239200341a8076a41186a280200360200200341c0046a41106a223a200341a8076a41106a290300370300200341c0046a41086a223b200341a8076a41086a290300370300200320032f0180013b015c20032003290380073703e804200320032f0184013b0158200320032903a8073703c004200341d4006a41026a223c20034188016a41026a2d00003a0000200320032f0188013b015420034198046a41186a223d200341d0076a41186a28020036020020034198046a41106a223e200341d0076a41106a29030037030020034198046a41086a223f200341d0076a41086a290300370300200320032903d00737039804200341d0006a41026a22402003418c016a41026a2d00003a0000200320032f018c013b0150200341f0036a41186a2241200341f8076a41186a280200360200200341f0036a41106a2242200341f8076a41106a290300370300200341f0036a41086a2243200341f8076a41086a290300370300200320032903f8073703f003200341cc006a41026a224420034190016a41026a2d00003a0000200320032f0190013b014c200341c8036a41186a2245200341a0086a41186a280200360200200341c8036a41106a2246200341a0086a41106a290300370300200341c8036a41086a2247200341a0086a41086a290300370300200320032903a0083703c803200341c8006a41026a224820034194016a41026a2d00003a0000200320032f0194013b0148200341a0036a41186a2249200341c8086a41186a280200360200200341a0036a41106a224a200341c8086a41106a290300370300200341a0036a41086a224b200341c8086a41086a290300370300200320032903c8083703a003200341c4006a41026a224c20034198016a41026a2d00003a0000200320032f0198013b0144200341f8026a41186a224d200341f0086a41186a280200360200200341f8026a41106a224e200341f0086a41106a290300370300200341f8026a41086a224f200341f0086a41086a290300370300200320032903f0083703f802200341c0006a41026a22502003419c016a41026a2d00003a0000200320032f019c013b0140200341d0026a41186a225120034198096a41186a280200360200200341d0026a41106a225220034198096a41106a290300370300200341d0026a41086a225320034198096a41086a29030037030020032003290398093703d0022003413c6a41026a2254200341a0016a41026a2d00003a0000200320032f01a0013b013c200341a8026a41186a2255200341c0096a41186a280200360200200341a8026a41106a2256200341c0096a41106a290300370300200341a8026a41086a2257200341c0096a41086a290300370300200320032903c0093703a802200341386a41026a2258200341a4016a41026a2d00003a0000200320032f01a4013b013820034180026a41186a2259200341e8096a41186a28020036020020034180026a41106a225a200341e8096a41106a29030037030020034180026a41086a225b200341e8096a41086a290300370300200320032903e80937038002200341346a41026a225c200341a8016a41026a2d00003a0000200320032f01a8013b0134200341d8016a41186a225d200341900a6a41186a280200360200200341d8016a41106a225e200341900a6a41106a290300370300200341d8016a41086a225f200341900a6a41086a290300370300200320032903900a3703d801200341306a41026a2260200341ac016a41026a2d00003a0000200320032f01ac013b0130200341b0016a41186a2261200341186a280200360200200341b0016a41106a2262200341106a290300370300200341b0016a41086a2263200341086a290300370300200320032903003703b00141002d00fca3c680001a41c00441002802c8a3c68000118180808000002202450d18200220073a0000200220032f016c3b000120022005360204200220032903980b370008200220063a0024200220032f01683b002541032107200241036a20242d00003a0000200241106a2025290300370000200241186a2026290300370000200241206a2027280200360000200241276a20282d00003a0000200220043602282002200b3a00482002200836024c200220032903b80a37002c200241346a202b2903003700002002413c6a202a290300370000200241c4006a2029280200360000200220032f01643b0049200241cb006a202c2d00003a0000200220032903b805370050200241d8006a202d290300370000200241e0006a202e290300370000200241e8006a202f2802003600002002200a3a006c200220093602702002200f3a009001200220032f01603b006d200241ef006a20302d00003a00002002418c016a203328020036000020024184016a2032290300370000200241fc006a20312903003700002002200329039005370074200220032f015c3b00910120024193016a20342d00003a00002002200c36029401200241b0016a2035280200360000200241a8016a2036290300370000200241a0016a2037290300370000200220032903e804370098012002200e3a00b401200241b7016a20382d00003a0000200220032f01583b00b5012002200d3602b801200241d4016a2039280200360000200241cc016a203a290300370000200241c4016a203b290300370000200220032903c0043700bc01200220133a00d801200241db016a203c2d00003a0000200220032f01543b00d901200220103602dc01200241f8016a203d280200360000200241f0016a203e290300370000200241e8016a203f29030037000020022003290398043700e001200220123a00fc01200241ff016a20402d00003a0000200220032f01503b00fd0120022011360280022002419c026a204128020036000020024194026a20422903003700002002418c026a2043290300370000200220032903f00337008402200220173a00a002200241a3026a20442d00003a0000200220032f014c3b00a102200220143602a402200241c0026a2045280200360000200241b8026a2046290300370000200241b0026a2047290300370000200220032903c8033700a802200220163a00c402200241c7026a20482d00003a0000200220032f01483b00c502200220153602c802200241e4026a2049280200360000200241dc026a204a290300370000200241d4026a204b290300370000200220032903a0033700cc022002201b3a00e802200241eb026a204c2d00003a0000200220032f01443b00e902200220183602ec0220024188036a204d28020036000020024180036a204e290300370000200241f8026a204f290300370000200220032903f8023700f0022002201a3a008c032002418f036a20502d00003a0000200220032f01403b008d032002201936029003200241ac036a2051280200360000200241a4036a20522903003700002002419c036a2053290300370000200220032903d002370094032002201f3a00b003200241b3036a20542d00003a0000200220032f013c3b00b1032002201c3602b403200241d0036a2055280200360000200241c8036a2056290300370000200241c0036a2057290300370000200220032903a8023700b8032002201e3a00d403200241d7036a20582d00003a0000200220032f01383b00d5032002201d3602d803200241f4036a2059280200360000200241ec036a205a290300370000200241e4036a205b29030037000020022003290380023700dc03200220233a00f803200241fb036a205c2d00003a0000200220032f01343b00f903200220203602fc0320024198046a205d28020036000020024190046a205e29030037000020024188046a205f290300370000200220032903d80137008004200220223a009c042002419f046a20602d00003a0000200220032f01303b009d04200220213602a004200241bc046a2061280200360000200241b4046a2062290300370000200241ac046a2063290300370000200220032903b0013700a40420012802040d030c040b4102210541022108024020012d002c4102460d00200341980b6a2001412c6a200210f38580800020034190066a200341a10b6a29000037030020034198066a200341a90b6a290000370300200341a0066a200341b10b6a290000370300200341a7066a200341b80b6a280000360000200320032900990b3703880620032d00980b21080b0240200141d0006a22092d00004102460d00200341980b6a2009200210f385808000200341b8066a200341a10b6a290000370300200341c0066a200341a90b6a290000370300200341c8066a200341b10b6a290000370300200341cf066a200341b80b6a280000360000200320032900990b3703b00620032d00980b21050b410221094102210c0240200141f4006a220d2d00004102460d00200341980b6a200d200210f385808000200341e0066a200341a10b6a290000370300200341e8066a200341a90b6a290000370300200341f0066a200341b10b6a290000370300200341f7066a200341b80b6a280000360000200320032900990b3703d80620032d00980b210c0b024020014198016a220d2d00004102460d00200341980b6a200d200210f38580800020034188076a200341a10b6a29000037030020034190076a200341a90b6a29000037030020034198076a200341b10b6a2900003703002003419f076a200341b80b6a280000360000200320032900990b3703800720032d00980b21090b4102210d410221100240200141bc016a22112d00004102460d00200341980b6a2011200210f385808000200341b0076a200341a10b6a290000370300200341b8076a200341a90b6a290000370300200341c0076a200341b10b6a290000370300200341c7076a200341b80b6a280000360000200320032900990b3703a80720032d00980b21100b0240200141e0016a22112d00004102460d00200341980b6a2011200210f385808000200341d8076a200341a10b6a290000370300200341e0076a200341a90b6a290000370300200341e8076a200341b10b6a290000370300200341ef076a200341b80b6a280000360000200320032900990b3703d00720032d00980b210d0b4102211141022114024020014184026a22152d00004102460d00200341980b6a2015200210f38580800020034180086a200341a10b6a29000037030020034188086a200341a90b6a29000037030020034190086a200341b10b6a29000037030020034197086a200341b80b6a280000360000200320032900990b3703f80720032d00980b21140b0240200141a8026a22152d00004102460d00200341980b6a2015200210f385808000200341a8086a200341a10b6a290000370300200341b0086a200341a90b6a290000370300200341b8086a200341b10b6a290000370300200341bf086a200341b80b6a280000360000200320032900990b3703a00820032d00980b21110b41022115410221180240200141cc026a22192d00004102460d00200341980b6a2019200210f385808000200341d0086a200341a10b6a290000370300200341d8086a200341a90b6a290000370300200341e0086a200341b10b6a290000370300200341e7086a200341b80b6a280000360000200320032900990b3703c80820032d00980b21180b0240200141f0026a22192d00004102460d00200341980b6a2019200210f385808000200341f8086a200341a10b6a29000037030020034180096a200341a90b6a29000037030020034188096a200341b10b6a2900003703002003418f096a200341b80b6a280000360000200320032900990b3703f00820032d00980b21150b410221194102211c024020014194036a221d2d00004102460d00200341980b6a201d200210f385808000200341a0096a200341a10b6a290000370300200341a8096a200341a90b6a290000370300200341b0096a200341b10b6a290000370300200341b7096a200341b80b6a280000360000200320032900990b3703980920032d00980b211c0b0240200141b8036a221d2d00004102460d00200341980b6a201d200210f385808000200341c8096a200341a10b6a290000370300200341d0096a200341a90b6a290000370300200341d8096a200341b10b6a290000370300200341df096a200341b80b6a280000360000200320032900990b3703c00920032d00980b21190b4102211d410221200240200141dc036a22212d00004102460d00200341980b6a2021200210f385808000200341f0096a200341a10b6a290000370300200341f8096a200341a90b6a290000370300200341800a6a200341b10b6a290000370300200341870a6a200341b80b6a280000360000200320032900990b3703e80920032d00980b21200b024020014180046a22212d00004102460d00200341980b6a2021200210f385808000200341980a6a200341a10b6a290000370300200341a00a6a200341a90b6a290000370300200341a80a6a200341b10b6a290000370300200341af0a6a200341b80b6a280000360000200320032900990b3703900a20032d00980b211d0b41022121410221070240200141a4046a22062d00004102460d00200341980b6a2006200210f385808000200341086a200341a10b6a290000370300200341106a200341a90b6a290000370300200341186a200341b10b6a2900003703002003411f6a200341b80b6a280000360000200320032900990b37030020032d00980b21070b0240200141c8046a22062d00004102460d00200341980b6a2006200210f385808000200341c00a6a200341a10b6a290000370300200341c80a6a200341a90b6a290000370300200341d00a6a200341b10b6a290000370300200341d70a6a200341b80b6a280000360000200320032900990b3703b80a20032d00980b21210b200341980b6a411f6a220620034188066a411f6a280000360000200341980b6a41186a220b20034188066a41186a290300370300200341980b6a41106a220a20034188066a41106a290300370300200341980b6a41086a220f20034188066a41086a290300370300200341e0056a41086a220e200341b0066a41086a290300370300200341e0056a41106a2213200341b0066a41106a290300370300200341e0056a41186a2212200341b0066a41186a290300370300200341e0056a411f6a2217200341b0066a411f6a28000036000020032003290388063703980b200320032903b0063703e005200341b8056a411f6a2216200341d8066a411f6a280000360000200341b8056a41186a221b200341d8066a41186a290300370300200341b8056a41106a221a200341d8066a41106a290300370300200341b8056a41086a221f200341d8066a41086a29030037030020034190056a41086a221e20034180076a41086a29030037030020034190056a41106a222320034180076a41106a29030037030020034190056a41186a222220034180076a41186a29030037030020034190056a411f6a222420034180076a411f6a280000360000200320032903d8063703b805200320032903800737039005200341e8046a411f6a2225200341a8076a411f6a280000360000200341e8046a41186a2226200341a8076a41186a290300370300200341e8046a41106a2227200341a8076a41106a290300370300200341e8046a41086a2228200341a8076a41086a290300370300200341c0046a411f6a2229200341d0076a411f6a280000360000200341c0046a41186a222a200341d0076a41186a290300370300200341c0046a41106a222b200341d0076a41106a290300370300200341c0046a41086a222c200341d0076a41086a290300370300200320032903a8073703e804200320032903d0073703c00420034198046a411f6a222d200341f8076a411f6a28000036000020034198046a41186a222e200341f8076a41186a29030037030020034198046a41106a222f200341f8076a41106a29030037030020034198046a41086a2230200341f8076a41086a290300370300200320032903f80737039804200341f0036a411f6a2231200341a0086a411f6a280000360000200341f0036a41186a2232200341a0086a41186a290300370300200341f0036a41106a2233200341a0086a41106a290300370300200341f0036a41086a2234200341a0086a41086a290300370300200320032903a0083703f003200341c8036a411f6a2235200341c8086a411f6a280000360000200341c8036a41186a2236200341c8086a41186a290300370300200341c8036a41106a2237200341c8086a41106a290300370300200341c8036a41086a2238200341c8086a41086a290300370300200320032903c8083703c803200341a0036a411f6a2239200341f0086a411f6a280000360000200341a0036a41186a223a200341f0086a41186a290300370300200341a0036a41106a223b200341f0086a41106a290300370300200341a0036a41086a223c200341f0086a41086a290300370300200320032903f0083703a003200341f8026a411f6a223d20034198096a411f6a280000360000200341f8026a41186a223e20034198096a41186a290300370300200341f8026a41106a223f20034198096a41106a290300370300200341f8026a41086a224020034198096a41086a29030037030020032003290398093703f802200341d0026a411f6a2241200341c0096a411f6a280000360000200341d0026a41186a2242200341c0096a41186a290300370300200341d0026a41106a2243200341c0096a41106a290300370300200341d0026a41086a2244200341c0096a41086a290300370300200320032903c0093703d002200341a8026a411f6a2245200341e8096a411f6a280000360000200341a8026a41186a2246200341e8096a41186a290300370300200341a8026a41106a2247200341e8096a41106a290300370300200341a8026a41086a2248200341e8096a41086a290300370300200320032903e8093703a80220034180026a411f6a2249200341900a6a411f6a28000036000020034180026a41186a224a200341900a6a41186a29030037030020034180026a41106a224b200341900a6a41106a29030037030020034180026a41086a224c200341900a6a41086a290300370300200320032903900a37038002200341d8016a411f6a224d2003411f6a280000360000200341d8016a41186a224e200341186a290300370300200341d8016a41106a224f200341106a290300370300200341d8016a41086a2250200341086a290300370300200320032903003703d801200341b0016a411f6a2251200341b80a6a411f6a280000360000200341b0016a41186a2252200341b80a6a41186a290300370300200341b0016a41106a2253200341b80a6a41106a290300370300200341b0016a41086a2254200341b80a6a41086a290300370300200320032903b80a3703b00141002d00fca3c680001a41c00441002802c8a3c68000118180808000002202450d18200220083a0000200220032903980b370001200220053a0024200220032903e005370025200241096a200f290300370000200241116a200a290300370000200241196a200b290300370000200241206a20062800003600002002412d6a200e290300370000200241356a20132903003700002002413d6a2012290300370000200241c4006a20172800003600002002200c3a0048200220093a006c200220032903b805370049200241d1006a201f290300370000200241d9006a201a290300370000200241e1006a201b290300370000200241e8006a2016280000360000200220032903900537006d200241f5006a201e290300370000200241fd006a202329030037000020024185016a20222903003700002002418c016a2024280000360000200220103a0090012002200d3a00b401200220032903e8043700910120024199016a2028290300370000200241a1016a2027290300370000200241a9016a2026290300370000200241b0016a2025280000360000200220032903c0043700b501200241bd016a202c290300370000200241c5016a202b290300370000200241cd016a202a290300370000200241d4016a2029280000360000200220143a00d801200241f8016a202d280000360000200241f1016a202e290300370000200241e9016a202f290300370000200241e1016a203029030037000020022003290398043700d901200220113a00fc012002419c026a203128000036000020024195026a20322903003700002002418d026a203329030037000020024185026a2034290300370000200220032903f0033700fd01200220183a00a002200241c0026a2035280000360000200241b9026a2036290300370000200241b1026a2037290300370000200241a9026a2038290300370000200220032903c8033700a102200220153a00c402200241e4026a2039280000360000200241dd026a203a290300370000200241d5026a203b290300370000200241cd026a203c290300370000200220032903a0033700c5022002201c3a00e80220024188036a203d28000036000020024181036a203e290300370000200241f9026a203f290300370000200241f1026a2040290300370000200220032903f8023700e902200220193a008c03200241ac036a2041280000360000200241a5036a20422903003700002002419d036a204329030037000020024195036a2044290300370000200220032903d00237008d03200220203a00b003200241d0036a2045280000360000200241c9036a2046290300370000200241c1036a2047290300370000200241b9036a2048290300370000200220032903a8023700b1032002201d3a00d403200241f4036a2049280000360000200241ed036a204a290300370000200241e5036a204b290300370000200241dd036a204c29030037000020022003290380023700d503200220073a00f80320024198046a204d28000036000020024191046a204e29030037000020024189046a204f29030037000020024181046a2050290300370000200220032903d8013700f903200220213a009c04200241bc046a2051280000360000200241b5046a2052290300370000200241ad046a2053290300370000200241a5046a2054290300370000200220032903b00137009d04200341980b6a200141ec046a10fa8d80800020040d04410321010c050b200341a40b6a42013702002003410136029c0b200341fcd1c280003602980b200341e0818080003602bc0a200341c4d2c280003602b80a2003200341b80a6a3602a00b200341980b6a41ccd2c2800010f680808000000b200041043a00000c170b2001410c6a210402400240200141086a2802002205450d002004280200210420052005280200220141016a360200410021072001417f4c0d190c010b200341026a200441026a2d00003a0000200341980b6a41086a2001411f6a290000370300200341a50b6a200141246a290000370000200320042f00003b0100200320012900173703980b20012800132104200128000f2105410121070b200341900a6a41026a200341026a2d00003a0000200341b80a6a41086a200341980b6a41086a290300370300200341b80a6a41106a200341980b6a41106a290300370300200341b80a6a41186a200341980b6a41186a290300370300200320032f01003b01900a200320032903980b3703b80a0b200020073a000420002002360230200041073a0000200041056a20032f01900a3b00002000410c6a2004360200200041086a2005360200200041106a20032903b80a370000200041076a200341920a6a2d00003a0000200041186a200341b80a6a41086a290300370000200041206a200341b80a6a41106a290300370000200041286a200341b80a6a41186a2903003700000c150b200141086a21040240024020012802042205450d002004280200210420052005280200220841016a360200410021012008417f4c0d170c010b200341900a6a41026a200441026a2d00003a0000200341b80a6a41086a2001411b6a290000370300200341c50a6a200141206a290000370000200320042f00003b01900a200320012900133703b80a200128000f2104200128000b2105410121010b200341e8096a41026a200341900a6a41026a2d00003a0000200341086a200341b80a6a41086a290300370300200341106a200341b80a6a41106a290300370300200341186a200341b80a6a41186a290300370300200320032f01900a3b01e809200320032903b80a3703000b200020032902980b3702302000200236022c200020013a0000200020032f01e8093b0001200041d8006a200341c00b6a290200370200200041d0006a200341980b6a41206a290200370200200041c8006a200341980b6a41186a290200370200200041c0006a200341980b6a41106a290200370200200041386a200341980b6a41086a290200370200200041036a200341ea096a2d00003a000020002004360208200020053602042000200329030037000c200041146a200341086a2903003700002000411c6a200341106a290300370000200041246a200341186a2903003700000c130b2001200541d4d7c2800010f980808000000b2005200441d4d7c2800010f980808000000b2004200841d4d7c2800010f980808000000b2008200941d4d7c2800010f980808000000b2009200c41d4d7c2800010f980808000000b200c200d41d4d7c2800010f980808000000b200d201041d4d7c2800010f980808000000b2010201141d4d7c2800010f980808000000b2011201441d4d7c2800010f980808000000b2014201541d4d7c2800010f980808000000b2015201841d4d7c2800010f980808000000b2018201941d4d7c2800010f980808000000b2019201c41d4d7c2800010f980808000000b201c201d41d4d7c2800010f980808000000b201d202041d4d7c2800010f980808000000b2020202141d4d7c2800010f980808000000b2021202241d4d7c2800010f980808000000b410441c00410b280808000000b410441c00410b280808000000b200341800c6a2480808080000f0b00000bb50301037f23808080800041d0016b220324808080800002400240024020012d00000d0020002001290001370001200041196a200141196a290000370000200041116a200141116a290000370000200041096a200141096a290000370000410121020c010b2003410c6a2001280204200210f285808000200341f0006a2003410c6a41e00010848e8080001a02400240200241186a2802002201450d0020022001417f6a360218200241146a22012001280200220141016a22044100200228020c220520042005491b6b360200200241106a28020020014102746a2802002201200228020822044f0d03200228020420014107746a220241046a200220022d00004108461b10e385808000200241083a0000200241016a200341ed006a41e30010848e8080001a0c010b0240200228020822012002280200470d0020022001109e86808000200228020821010b200228020420014107746a220141083a0000200141016a200341ed006a41e30010848e8080001a20022002280208220141016a3602080b20002001360204410021020b200020023a0000200341d0016a2480808080000f0b2001200441d4d7c2800010f980808000000bb60301017f2380808080004180016b22042480808080000240024002400240024020012d00000e03010200020b200441003a005420042002360250200441023a002c2004200141286a2802003602342004200128022441086a360230200441046a20032004412c6a20024100200410da8580800020042d00040d03200441f0006a22022004411d6a290000370300200441e8006a2203200441156a290000370300200441d8006a41086a2004410d6a29000037030020042004290005370358024020012d00010d00200141013a0001200141026a20042903583700002001411a6a2002290300370000200141126a20032903003700002001410a6a200441e0006a2903003700000b200141026a210241202101410121030c020b200128020441086a2102200141086a2802002101410021030c010b41012103200141016a2102412021010b20002001360208200020023602042000200336020020044180016a2480808080000f0b200441386a420137020020044101360230200441fcd1c2800036022c200441e08180800036027c20044180d3c280003602782004200441f8006a3602342004412c6a4188d3c2800010f680808000000bb80301017f2380808080004180016b22052480808080000240024002400240024020012d00000e03010200020b200541003a005420052002360250200541023a002c2005200141286a2802003602342005200128022441086a360230200541046a200320042005412c6a20024100200510db8580800020052d00040d03200541f0006a22022005411d6a290000370300200541e8006a2204200541156a290000370300200541d8006a41086a2005410d6a29000037030020052005290005370358024020012d00010d00200141013a0001200141026a20052903583700002001411a6a2002290300370000200141126a20042903003700002001410a6a200541e0006a2903003700000b200141026a210241202101410121040c020b200128020441086a2102200141086a2802002101410021040c010b41012104200141016a2102412021010b20002001360208200020023602042000200436020020054180016a2480808080000f0b200541386a420137020020054101360230200541fcd1c2800036022c200541e08180800036027c20054180d3c280003602782005200541f8006a3602342005412c6a4188d3c2800010f680808000000bb80301017f2380808080004180016b22052480808080000240024002400240024020012d00000e03010200020b200541003a005420052002360250200541023a002c2005200141286a2802003602342005200128022441086a360230200541046a200320042005412c6a20024100200510d98580800020052d00040d03200541f0006a22022005411d6a290000370300200541e8006a2204200541156a290000370300200541d8006a41086a2005410d6a29000037030020052005290005370358024020012d00010d00200141013a0001200141026a20052903583700002001411a6a2002290300370000200141126a20042903003700002001410a6a200541e0006a2903003700000b200141026a210241202101410121040c020b200128020441086a2102200141086a2802002101410021040c010b41012104200141016a2102412021010b20002001360208200020023602042000200436020020054180016a2480808080000f0b200541386a420137020020054101360230200541fcd1c2800036022c200541e08180800036027c20054180d3c280003602782005200541f8006a3602342005412c6a4188d3c2800010f680808000000bb60301017f2380808080004180016b22042480808080000240024002400240024020012d00000e03010200020b200441003a005420042002360250200441023a002c2004200141286a2802003602342004200128022441086a360230200441046a20032004412c6a20024100200410dc8580800020042d00040d03200441f0006a22022004411d6a290000370300200441e8006a2203200441156a290000370300200441d8006a41086a2004410d6a29000037030020042004290005370358024020012d00010d00200141013a0001200141026a20042903583700002001411a6a2002290300370000200141126a20032903003700002001410a6a200441e0006a2903003700000b200141026a210241202101410121030c020b200128020441086a2102200141086a2802002101410021030c010b41012103200141016a2102412021010b20002001360208200020023602042000200336020020044180016a2480808080000f0b200441386a420137020020044101360230200441fcd1c2800036022c200441e08180800036027c20044180d3c280003602782004200441f8006a3602342004412c6a4188d3c2800010f680808000000be31303057f047e077f23808080800041d0016b220524808080800020052003360218200520023602140240024002400240200028026822060d00410021000c010b200041ec006a280200220728021421002005200541146a360258200541086a20062001200541d8006a4198d3c28000200011878080800000200528020c210802402005280208450d000240024002400240200828020041fcffffff076a2200410320004105491b0e0403030102000b2008280204450d02200841086a28020041002802c0a3c68000118080808000000c020b2008280204450d01200841086a28020041002802c0a3c68000118080808000000c010b200810de858080000b200841002802c0a3c68000118080808000002004280200450d030c020b20054280808080c00037021c20054100360224024020042802002209450d00200541286a41286a2004412c6a290200370300200541286a41206a200441246a290200370300200541286a41186a2004411c6a290200370300200541286a41106a200441146a290200370300200541286a41086a2004410c6a29020037030020052004290204370328024002400240024002400240024020082802002203417e6a2200410420004106491b0e06060206000103060b2008280204450d05200841086a2203280200450d05200841106a21000c040b2003450d042008280204450d04200841046a21032008410c6a21000c030b20082802340d010c030b2008410c6a2100200841046a21030c010b200841346a21032008413c6a21000b20054190016a41186a200041186a290000220a37030020054190016a41106a200041106a290000220b37030020054190016a41086a200041086a290000220c37030020052000290000220d37039001200541d8006a41186a200a370300200541d8006a41106a200b370300200541d8006a41086a200c3703002005200d370358200528022821014101210002400240024002400240200528022c20052802502202200241284b22021b220e450d00200e417f4c0d0141002d00fca3c680001a200e41002802c8a3c68000118180808000002200450d020b20002001200541286a20021b200e10848e808000210f200341046a2802002101200328020022022002280200220041016a3602002000417f4c0d022005200136029401200520023602900103402002280204210003402000417f460d012000417f4c0d052002200041016a2002280204220320032000461b360204200320004721012003210020010d000b0b2005280294012103200528029001220020002802002200417f6a360200024020004101470d0020054190016a10f18d8080000b024020052802242200200528021c470d002005411c6a2000109f86808000200528022421000b2005280220200041386c6a220041023a000c2000200e3602082000200f3602042000200e3602002000200529035837000d2000200336023420002002360230200041156a200541e0006a2903003700002000411d6a200541e8006a290300370000200041256a200541f0006a2903003700002005200528022441016a3602240c040b10ae80808000000b4101200e10b280808000000b00000b10f08d808000000b20052802282103200528022c2102200528025021004100211020054190016a41286a2201410036020020054190016a2003200541286a200041284b220e1b2203200320022000200e1b6a10f686808000200541d8006a41286a2001280200360200200541d8006a41206a20054190016a41206a290200370300200541d8006a41186a20054190016a41186a290200370300200541d8006a41106a20054190016a41106a290200370300200541d8006a41086a20054190016a41086a290200370300200520052902900137035820052005280254360284012008280200210320052005411c6a3602c00141002111024002400240024002402003417e6a2203410420034106491b0e06040403000104040b200841306a21120c010b2008412c6a21120b41022111410021000c010b200841046a2100410121110b2012415c6a21132005200541c0016a3602c8012005200541d8006a3602c40103400240024002400240024020110e03010200010b20004110200041104b1b210e2012200041246c22026a2103201320026a2102200041087441807e6a21010340200e2000460d03200241246a210220014180026a2101200041016a210020032d00002108200341246a210320084102460d000b20014180fe0371410172210f200221140c030b200f41807e71410272210f0c020b200f41807e71210f0240201041ff01710d0041012110200021140c020b200f410272210f0c010b200f41807e71410272210f200e21000b0240200f41ff01714102460d0020142d0000450d01201428020421032005200541c4016a3602cc01200fad220a42ff01834202510d0120052003ad422086200a42ffff03838437039001200541cc016a20054190016a10bc8c8080000c010b0b02402005280280014129490d00200528025841002802c0a3c68000118080808000000b024020052802504129490d00200528022841002802c0a3c68000118080808000000b2005280220220f2005280224220341386c6a2108200528021c2114200f2100024002402003450d00200541d8006a41086a210320054188016a210e200f21000240034020002802002202418080808078460d01200028020421012000290204210a200e200041346a280200360200200541d8006a41286a2000412c6a290200370300200541d8006a41206a200041246a290200370300200541d8006a41186a2000411c6a290200370300200541d8006a41106a200041146a29020037030020032000410c6a29020037030020052000290204370358200541286a41286a200341286a280200360200200541286a41206a200341206a290200370300200541286a41186a200341186a290200370300200541286a41106a200341106a290200370300200541286a41086a200341086a290200370300200520032902003703282006200aa7200a422088a7200541286a20072802101186808080000002402002450d00200141002802c0a3c68000118080808000000b200041386a22002008470d000c030b0b200041386a21000b200820006b41386e210320082000460d00034002402000280200450d00200041046a28020041002802c0a3c68000118080808000000b02402000410c6a2d00004102490d00200041306a2802002202417f460d00200220022802042201417f6a36020420014101470d00200041346a280200410b6a4104490d00200241002802c0a3c68000118080808000000b200041386a21002003417f6a22030d000b0b2014450d00200f41002802c0a3c68000118080808000000b200941004721000b20000d012004280200450d010b2004412c6a2802004129490d00200428020441002802c0a3c68000118080808000000b200541d0016a2480808080000be31303057f047e077f23808080800041d0016b220524808080800020052003360218200520023602140240024002400240200028026822060d00410021000c010b200041ec006a280200220728021421002005200541146a360258200541086a20062001200541d8006a41acd3c28000200011878080800000200528020c210802402005280208450d000240024002400240200828020041fcffffff076a2200410320004105491b0e0403030102000b2008280204450d02200841086a28020041002802c0a3c68000118080808000000c020b2008280204450d01200841086a28020041002802c0a3c68000118080808000000c010b200810de858080000b200841002802c0a3c68000118080808000002004280200450d030c020b20054280808080c00037021c20054100360224024020042802002209450d00200541286a41286a2004412c6a290200370300200541286a41206a200441246a290200370300200541286a41186a2004411c6a290200370300200541286a41106a200441146a290200370300200541286a41086a2004410c6a29020037030020052004290204370328024002400240024002400240024020082802002203417e6a2200410420004106491b0e06060206000103060b2008280204450d05200841086a2203280200450d05200841106a21000c040b2003450d042008280204450d04200841046a21032008410c6a21000c030b20082802340d010c030b2008410c6a2100200841046a21030c010b200841346a21032008413c6a21000b20054190016a41186a200041186a290000220a37030020054190016a41106a200041106a290000220b37030020054190016a41086a200041086a290000220c37030020052000290000220d37039001200541d8006a41186a200a370300200541d8006a41106a200b370300200541d8006a41086a200c3703002005200d370358200528022821014101210002400240024002400240200528022c20052802502202200241284b22021b220e450d00200e417f4c0d0141002d00fca3c680001a200e41002802c8a3c68000118180808000002200450d020b20002001200541286a20021b200e10848e808000210f200341046a2802002101200328020022022002280200220041016a3602002000417f4c0d022005200136029401200520023602900103402002280204210003402000417f460d012000417f4c0d052002200041016a2002280204220320032000461b360204200320004721012003210020010d000b0b2005280294012103200528029001220020002802002200417f6a360200024020004101470d0020054190016a10f18d8080000b024020052802242200200528021c470d002005411c6a2000109f86808000200528022421000b2005280220200041386c6a220041023a000c2000200e3602082000200f3602042000200e3602002000200529035837000d2000200336023420002002360230200041156a200541e0006a2903003700002000411d6a200541e8006a290300370000200041256a200541f0006a2903003700002005200528022441016a3602240c040b10ae80808000000b4101200e10b280808000000b00000b10f08d808000000b20052802282103200528022c2102200528025021004100211020054190016a41286a2201410036020020054190016a2003200541286a200041284b220e1b2203200320022000200e1b6a10f686808000200541d8006a41286a2001280200360200200541d8006a41206a20054190016a41206a290200370300200541d8006a41186a20054190016a41186a290200370300200541d8006a41106a20054190016a41106a290200370300200541d8006a41086a20054190016a41086a290200370300200520052902900137035820052005280254360284012008280200210320052005411c6a3602c00141002111024002400240024002402003417e6a2203410420034106491b0e06040403000104040b200841306a21120c010b2008412c6a21120b41022111410021000c010b200841046a2100410121110b2012415c6a21132005200541c0016a3602c8012005200541d8006a3602c40103400240024002400240024020110e03010200010b20004110200041104b1b210e2012200041246c22026a2103201320026a2102200041087441807e6a21010340200e2000460d03200241246a210220014180026a2101200041016a210020032d00002108200341246a210320084102460d000b20014180fe0371410172210f200221140c030b200f41807e71410272210f0c020b200f41807e71210f0240201041ff01710d0041012110200021140c020b200f410272210f0c010b200f41807e71410272210f200e21000b0240200f41ff01714102460d0020142d0000450d01201428020421032005200541c4016a3602cc01200fad220a42ff01834202510d0120052003ad422086200a42ffff03838437039001200541cc016a20054190016a10bd8c8080000c010b0b02402005280280014129490d00200528025841002802c0a3c68000118080808000000b024020052802504129490d00200528022841002802c0a3c68000118080808000000b2005280220220f2005280224220341386c6a2108200528021c2114200f2100024002402003450d00200541d8006a41086a210320054188016a210e200f21000240034020002802002202418080808078460d01200028020421012000290204210a200e200041346a280200360200200541d8006a41286a2000412c6a290200370300200541d8006a41206a200041246a290200370300200541d8006a41186a2000411c6a290200370300200541d8006a41106a200041146a29020037030020032000410c6a29020037030020052000290204370358200541286a41286a200341286a280200360200200541286a41206a200341206a290200370300200541286a41186a200341186a290200370300200541286a41106a200341106a290200370300200541286a41086a200341086a290200370300200520032902003703282006200aa7200a422088a7200541286a20072802101186808080000002402002450d00200141002802c0a3c68000118080808000000b200041386a22002008470d000c030b0b200041386a21000b200820006b41386e210320082000460d00034002402000280200450d00200041046a28020041002802c0a3c68000118080808000000b02402000410c6a2d00004102490d00200041306a2802002202417f460d00200220022802042201417f6a36020420014101470d00200041346a280200410b6a4104490d00200241002802c0a3c68000118080808000000b200041386a21002003417f6a22030d000b0b2014450d00200f41002802c0a3c68000118080808000000b200941004721000b20000d012004280200450d010b2004412c6a2802004129490d00200428020441002802c0a3c68000118080808000000b200541d0016a2480808080000ba00301047f23808080800041f0086b2202248080808000200241e8016a2001280200220128020022032001280204220110c28c8080000240024002400240024020022802e80122044105470d002002410c6a410c6a200241e8016a410c6a290200370200200220022902ec013702102002410536020c0c010b200241e0086a41086a2205200241e8016a410c6a290200370300200220022902ec013703e00820024184076a41146a200241e8016a41146a41c80110848e8080001a20024184076a410c6a20052903003702002002200436028407200220022903e008370288072002410c6a20024184076a2003200110f48d808000200228020c22014105470d010b200241106a10de858080000c010b20024184076a41046a2002410c6a41046a41d80110848e8080001a2002200136028407200241e8016a20024184076a10b78a80800020022802e80122014108470d0120022802ec0110df858080000b41c0d3c2800041c2004184d4c2800010a181808000000b200041046a200241e8016a41046a41980510848e8080001a20002001360200200241f0086a2480808080000ba00301047f23808080800041f0086b2202248080808000200241e8016a2001280200220128020022032001280204220110c28c8080000240024002400240024020022802e80122044105470d002002410c6a410c6a200241e8016a410c6a290200370200200220022902ec013702102002410536020c0c010b200241e0086a41086a2205200241e8016a410c6a290200370300200220022902ec013703e00820024184076a41146a200241e8016a41146a41c80110848e8080001a20024184076a410c6a20052903003702002002200436028407200220022903e008370288072002410c6a20024184076a2003200110f48d808000200228020c22014105470d010b200241106a10de858080000c010b20024184076a41046a2002410c6a41046a41d80110848e8080001a2002200136028407200241e8016a20024184076a10b68a80800020022802e80122014108470d0120022802ec0110df858080000b41c0d3c2800041c2004184d4c2800010a181808000000b200041046a200241e8016a41046a41980510848e8080001a20002001360200200241f0086a2480808080000b9a0801047f23808080800041f0006b220624808080800002400240024020002802682207450d0002400240024020044100480d00200441f5ffffff074f0d0102402004410b6a417c7122080d00410421090c030b41002d00fca3c680001a200841002802c8a3c680001181808080000022090d024104200810b280808000000b41fc9bc68000412b200641186a41a89cc6800041b89cc68000108981808000000b41e484c08000412b200641186a419085c08000419086c08000108981808000000b2009428180808010370200200941086a2003200410848e8080001a2006200436021420062009360210200041ec006a2802002103200641186a41186a200541186a290000370300200641186a41106a200541106a290000370300200641186a41086a200541086a290000370300200620052900003703182006200536026c200328021421042006200641106a360268200641086a2007200641186a200641e8006a4194d4c28000200411878080800000200628020c220428020021000240024002400240024020062802080d00024002402000417e6a2209410420094106491b0e06060006030104060b20042802342200450d05200441346a21090c040b2000450d0420042802042200450d04200441046a21090c030b0240024002400240200041fcffffff076a2205410320054105491b0e0403030102000b2004280204450d02200441086a28020041002802c0a3c68000118080808000000c020b2004280204450d01200441086a28020041002802c0a3c68000118080808000000c010b200410de858080000b200441002802c0a3c68000118080808000000c030b2004280204450d02200441086a220928020022000d010c020b200441046a2109200428020421000b200941046a280200210920002000280200220441016a3602002004417f4c0d02200641c8006a41086a200541086a290000370300200641c8006a41106a200541106a290000370300200641c8006a41186a200541186a290000370300200620052900003703482006200936026c2006200036026803402000280204210503402005417f460d012005417f4c0d052000200541016a2000280204220420042005461b360204200420054721092004210520090d000b0b200628026c21052006280268220420042802002204417f6a360200024020044101470d00200641e8006a10f18d8080000b200641c0006a2005360200200641216a200641d0006a290300370000200641296a200641d8006a290300370000200641316a200641e0006a290300370000200620062903483700192006200036023c200641023a0018200720012002200641186a2003280210118680808000000b2006280210220520052802002205417f6a36020020054101470d00200641106a10e28a8080000b200641f0006a2480808080000f0b00000b10f08d808000000b9a0801047f23808080800041f0006b220624808080800002400240024020002802682207450d0002400240024020044100480d00200441f5ffffff074f0d0102402004410b6a417c7122080d00410421090c030b41002d00fca3c680001a200841002802c8a3c680001181808080000022090d024104200810b280808000000b41fc9bc68000412b200641186a41a89cc6800041b89cc68000108981808000000b41e484c08000412b200641186a419085c08000419086c08000108981808000000b2009428180808010370200200941086a2003200410848e8080001a2006200436021420062009360210200041ec006a2802002103200641186a41186a200541186a290000370300200641186a41106a200541106a290000370300200641186a41086a200541086a290000370300200620052900003703182006200536026c200328021421042006200641106a360268200641086a2007200641186a200641e8006a4194d4c28000200411878080800000200628020c220428020021000240024002400240024020062802080d00024002402000417e6a2209410420094106491b0e06060006030104060b20042802342200450d05200441346a21090c040b2000450d0420042802042200450d04200441046a21090c030b0240024002400240200041fcffffff076a2205410320054105491b0e0403030102000b2004280204450d02200441086a28020041002802c0a3c68000118080808000000c020b2004280204450d01200441086a28020041002802c0a3c68000118080808000000c010b200410de858080000b200441002802c0a3c68000118080808000000c030b2004280204450d02200441086a220928020022000d010c020b200441046a2109200428020421000b200941046a280200210920002000280200220441016a3602002004417f4c0d02200641c8006a41086a200541086a290000370300200641c8006a41106a200541106a290000370300200641c8006a41186a200541186a290000370300200620052900003703482006200936026c2006200036026803402000280204210503402005417f460d012005417f4c0d052000200541016a2000280204220420042005461b360204200420054721092004210520090d000b0b200628026c21052006280268220420042802002204417f6a360200024020044101470d00200641e8006a10f18d8080000b200641c0006a2005360200200641216a200641d0006a290300370000200641296a200641d8006a290300370000200641316a200641e0006a290300370000200620062903483700192006200036023c200641023a0018200720012002200641186a2003280210118680808000000b2006280210220520052802002205417f6a36020020054101470d00200641106a10e28a8080000b200641f0006a2480808080000f0b00000b10f08d808000000b870101037f2001280200220241046a2802002103200228020022022002280200220441016a36020002402004417f4a0d0000000b20002003360208200020023602042000410736020020002001280204220129000037000c200041146a200141086a2900003700002000411c6a200141106a290000370000200041246a200141186a2900003700000bc32103097f017e027f23808080800041b0046b22042480808080002004200336021420042001360210024002400240024002400240024002400240024020022d00000d00200228020421020240200141286a28020022052001411c6a22062802002203470d00200610a889808000200128021c2103200128022821050b200141206a280200200141246a28020020056a22054100200320052003491b6b4102746a20023602002001200128022841016a360228200141186a280200220320024d0d02200441186a200141146a28020020024107746a220141800110848e8080001a200141043a0004200141083a00000240024020042d00184108470d0020044198016a200441186a41046a41e00010848e8080001a02400240200428021028026822070d00410021080c010b2004280214220128020021032001280204210520012802282102200441d8036a41286a22064100360200200441d8036a20032001200241284b22091b220320032005200220091b6a10f686808000200441f0026a41286a2006280200360200200441f0026a41206a200441d8036a41206a290200370300200441f0026a41186a200441d8036a41186a290200370300200441f0026a41106a200441d8036a41106a290200370300200441f0026a41086a200441d8036a41086a290200370300200420042902d8033703f0022004200128022c220a36029c030240024002400240024020042d009801417c6a41ff01712201410420014104491b0e050400010402040b200441c8016a21010c020b20044198016a41286a21010c010b200441c8016a21010b200141086a2802002001412c6a2802002202200241284b22031b21022001280204200141046a20031b21052001280200220341017621010240024020034101710d00410021060240200120024b0d0020012103410021010c020b2001200241d492c68000109481808000000b200120024f0d0841012106200141016a2103200520016a2d0000410f7121010b200441e0036a200220036b360200200420013a00d903200420063a00d8032004200520036a3602dc03200441f0026a200441d8036a10e28d808000200428029c03210a0b200441f8016a41086a200441fc026a290200370300200441f8016a41106a20044184036a290200370300200441f8016a41186a2004418c036a290200370300200441f8016a41206a20044194036a280200360200200420042902f4023703f80120042802f002210b2004280298032106410121080b200441a8026a41086a20044198016a41096a290000370300200441a8026a41106a20044198016a41116a290000370300200441a8026a41186a20044198016a41196a290000370300200441a8026a411f6a20044198016a41206a29000037000020042004290099013703a80220042d009801210320042802c001210c20042802c401210520042802c80121022004200441106a3602ac032004200441146a3602a803200441cc016a210102400240024002400240024002400240024002402003417c6a41ff01712209410420094104491b0e050001020304000b41002d00fca3c680001a410141002802c8a3c68000118180808000002201450d0f200141003a0000200441013602a402200420013602a0022004410136029c020c080b200441fc026a200141086a29020037020020044184036a200141106a2902003702002004418c036a200141186a290200370200200441f0026a41246a200141206a2902003702002004419c036a2203200141286a280200360200200420023602f002200420012902003702f402200441d8036a41206a200441cb026a280000360200200441d8036a41186a200441c3026a290000370300200441d8036a41106a200441bb026a290000370300200441d8036a41086a200441b3026a290000370300200420042900ab023703d80320042005360280042004200c3602fc03200420023602ac04200420042802f80220032802002201200141284b22011b3602a804200420042802f402200441f0026a41046a20011b3602a40420044194046a200441d8036a200441a4046a200441a8036a10f78580800020042802ac042203410176210220042802a804210120042802a40421090240024020034101710d004100210c0240200220014b0d0020022105410021020c020b2002200141d492c68000109481808000000b200220014f0d104101210c200241016a2105200920026a2d0000410f7121020b200420023a00bd032004200c3a00bc03200441003602b8032004200120056b3602b4032004200920056a3602b0032004419c026a200441b0036a200141017420036b20044194046a10d08c808000024002400240024020042d00d8030e020103000b20042802fc03220120012802002201417f6a36020020014101470d02200441d8036a41246a21010c010b20042802dc03220120012802002201417f6a36020020014101470d01200441d8036a41047221010b200110e28a8080000b200428029c034129490d0720042802f40241002802c0a3c68000118080808000000c070b20044184036a200141086a2902003702002004418c036a200141106a29020037020020044194036a200141186a2902003702002004419c036a200141206a280200360200200420023602f802200420053602f4022004200c3602f002200420012902003702fc02200441d8036a41206a200441cb026a280000360200200441d8036a41186a200441c3026a290000370300200441d8036a41106a200441bb026a290000370300200441d8036a41086a200441b3026a290000370300200420042900ab023703d803200441086a200441f0026a41046a41c0d1c280001096878080002004200c3602ac04200420042903083702a40420044194046a200441a4046a10dc8d808000200441003a0080042004200441a4046a3602fc03200441b0036a200441a8036a200441d8036a200441a4046a4100200410dc858080002004419c026a20044194046a20042802a80441017420042802ac046b200441b0036a10cb8c808000000b200441f0026a41206a200441cb026a280000360200200441f0026a41186a200441c3026a290000370300200441f0026a41106a200441bb026a290000370300200441f0026a41086a200441b3026a290000370300200420042900ab02220d3703f00220042005360298032004200c36029403200da741ff01714103470d01410221000c020b200441fc026a200141086a29020037020020044184036a200141106a2902003702002004418c036a200141186a29020037020020044194036a200141206a2902003702002004419c036a2209200141286a280200360200200420023602f002200420012902003702f402200441d8036a41096a20044198016a410172220141086a290000370000200441d8036a41116a200141106a290000370000200441d8036a41196a200141186a290000370000200441d8036a41206a2001411f6a2900003700002004200c36028004200420012900003700d903200420033a00d8032004200236029004200420042802f80220092802002201200141284b22091b220136028c04200420042802f402200441f0026a41046a20091b220936028804200341ff01714103470d024102210e0c030b200441d8036a200441f0026a4100200441a8036a10f78580800020042902dc03210d20042802d80321000b2004200d3702b403200420003602b0032004419c026a200441d8036a200441b0036a10ca8c808000000b200441b0036a200441d8036a20044188046a200441a8036a10f78580800020042902b403210d20042802b003210e200428028c042101200428028804210920042802900421020b200241017621030240024020024101710d004100210f0240200320014b0d002003210c410021030c020b2003200141d492c68000109481808000000b200320014f0d0a4101210f200341016a210c200920036a2d0000410f7121030b200420033a00a1042004200f3a00a0042004410036029c0420042001200c6b3602980420042009200c6a36029404200441c0036a200441a8036a360200200441003602b803200420053602b0032004200541c0046a3602b4032004200441f0026a3602bc032004200d3702a8042004200e3602a4042004419c026a20044194046a200141017420026b200441b0036a200441a4046a10ce8c808000024020042d00d80322014103460d0002400240024020010e020103000b20042802fc03220120012802002201417f6a36020020014101470d02200441fc036a21010c010b20042802dc03220120012802002201417f6a36020020014101470d01200441dc036a21010b200110e28a8080000b200541002802c0a3c6800011808080800000200428029c034129490d0020042802f40241002802c0a3c68000118080808000000b0240024020042802a4022201411f4b0d0020042802a0022102200441f0026a20016a4100412020016b108a8e8080001a200441f0026a2002200110848e8080001a20002001360204200041206a200441f0026a41186a290000370000200041186a200441f0026a41106a290000370000200041106a200441f8026a290000370000200020042900f002370008410121030c010b2004280210220241d4006a28020021092002280250210c2004280214220228022c220541017621030240024020054101710d00200228020420022802282205200541284b22051b220e2003490d0c2002280200200220051b2102410021050c010b200228020420022802282205200541284b1b220e2003490d0c200e20034d0d0d200441f9026a20022802002002200541284b1b220220036a2d000041f001713a0000410121050b200420053a00f802200420033602f402200420023602f002200441d0026a200c200441f0026a20042802a00222022001200928021c1187808080000020042802102103200441f0026a41106a200441f8016a41086a290300370200200441f0026a41186a200441f8016a41106a290300370200200441f0026a41206a200441f8016a41186a29030037020020044198036a200441f8016a41206a2802003602002004200b3602f402200420083602f002200420042903f8013702f8022004200a3602a0032004200636029c03200441d8036a41186a200441d0026a41186a2205290000370300200441d8036a41106a200441d0026a41106a2209290000370300200441d8036a41086a200441d0026a41086a220a290000370300200420042900d0023703d8032003200441d8036a20022001200441f0026a10f985808000200041196a2005290000370000200041116a2009290000370000200041096a200a290000370000200020042900d002370001410021030b200020033a00000240200428029c02450d00200241002802c0a3c68000118080808000000b02402007450d002001411f4b0d0020064129490d00200b41002802c0a3c68000118080808000000b20042d00184108470d010c030b20002004290278370001200041003a0000200041196a20044190016a290200370000200041116a20044188016a290200370000200041096a20044180016a2902003700000b200441186a10e3858080000c010b200041003a000020002002290001370001200041196a200241196a290000370000200041116a200241116a290000370000200041096a200241096a2900003700000b200441b0046a2480808080000f0b2002200341e4d7c2800010f980808000000b2001200241e492c6800010f980808000000b4101410110b280808000000b2002200141e492c6800010f980808000000b2003200141e492c6800010f980808000000b2003200e41ac95c68000109581808000000b2003200e41bc95c68000109581808000000b2003200e41cc95c6800010f980808000000bc32103097f017e027f23808080800041b0046b22042480808080002004200336021420042001360210024002400240024002400240024002400240024020022d00000d00200228020421020240200141286a28020022052001411c6a22062802002203470d00200610a889808000200128021c2103200128022821050b200141206a280200200141246a28020020056a22054100200320052003491b6b4102746a20023602002001200128022841016a360228200141186a280200220320024d0d02200441186a200141146a28020020024107746a220141800110848e8080001a200141043a0004200141083a00000240024020042d00184108470d0020044198016a200441186a41046a41e00010848e8080001a02400240200428021028026822070d00410021080c010b2004280214220128020021032001280204210520012802282102200441d8036a41286a22064100360200200441d8036a20032001200241284b22091b220320032005200220091b6a10f686808000200441f0026a41286a2006280200360200200441f0026a41206a200441d8036a41206a290200370300200441f0026a41186a200441d8036a41186a290200370300200441f0026a41106a200441d8036a41106a290200370300200441f0026a41086a200441d8036a41086a290200370300200420042902d8033703f0022004200128022c220a36029c030240024002400240024020042d009801417c6a41ff01712201410420014104491b0e050400010402040b200441c8016a21010c020b20044198016a41286a21010c010b200441c8016a21010b200141086a2802002001412c6a2802002202200241284b22031b21022001280204200141046a20031b21052001280200220341017621010240024020034101710d00410021060240200120024b0d0020012103410021010c020b2001200241d492c68000109481808000000b200120024f0d0841012106200141016a2103200520016a2d0000410f7121010b200441e0036a200220036b360200200420013a00d903200420063a00d8032004200520036a3602dc03200441f0026a200441d8036a10e28d808000200428029c03210a0b200441f8016a41086a200441fc026a290200370300200441f8016a41106a20044184036a290200370300200441f8016a41186a2004418c036a290200370300200441f8016a41206a20044194036a280200360200200420042902f4023703f80120042802f002210b2004280298032106410121080b200441a8026a41086a20044198016a41096a290000370300200441a8026a41106a20044198016a41116a290000370300200441a8026a41186a20044198016a41196a290000370300200441a8026a411f6a20044198016a41206a29000037000020042004290099013703a80220042d009801210320042802c001210c20042802c401210520042802c80121022004200441106a3602ac032004200441146a3602a803200441cc016a210102400240024002400240024002400240024002402003417c6a41ff01712209410420094104491b0e050001020304000b41002d00fca3c680001a410141002802c8a3c68000118180808000002201450d0f200141003a0000200441013602a402200420013602a0022004410136029c020c080b200441fc026a200141086a29020037020020044184036a200141106a2902003702002004418c036a200141186a290200370200200441f0026a41246a200141206a2902003702002004419c036a2203200141286a280200360200200420023602f002200420012902003702f402200441d8036a41206a200441cb026a280000360200200441d8036a41186a200441c3026a290000370300200441d8036a41106a200441bb026a290000370300200441d8036a41086a200441b3026a290000370300200420042900ab023703d80320042005360280042004200c3602fc03200420023602ac04200420042802f80220032802002201200141284b22011b3602a804200420042802f402200441f0026a41046a20011b3602a40420044194046a200441d8036a200441a4046a200441a8036a10f48580800020042802ac042203410176210220042802a804210120042802a40421090240024020034101710d004100210c0240200220014b0d0020022105410021020c020b2002200141d492c68000109481808000000b200220014f0d104101210c200241016a2105200920026a2d0000410f7121020b200420023a00bd032004200c3a00bc03200441003602b8032004200120056b3602b4032004200920056a3602b0032004419c026a200441b0036a200141017420036b20044194046a10d08c808000024002400240024020042d00d8030e020103000b20042802fc03220120012802002201417f6a36020020014101470d02200441d8036a41246a21010c010b20042802dc03220120012802002201417f6a36020020014101470d01200441d8036a41047221010b200110e28a8080000b200428029c034129490d0720042802f40241002802c0a3c68000118080808000000c070b20044184036a200141086a2902003702002004418c036a200141106a29020037020020044194036a200141186a2902003702002004419c036a200141206a280200360200200420023602f802200420053602f4022004200c3602f002200420012902003702fc02200441d8036a41206a200441cb026a280000360200200441d8036a41186a200441c3026a290000370300200441d8036a41106a200441bb026a290000370300200441d8036a41086a200441b3026a290000370300200420042900ab023703d803200441086a200441f0026a41046a41c0d1c280001096878080002004200c3602ac04200420042903083702a40420044194046a200441a4046a10dc8d808000200441003a0080042004200441a4046a3602fc03200441b0036a200441a8036a200441d8036a200441a4046a4100200410da858080002004419c026a20044194046a20042802a80441017420042802ac046b200441b0036a10cb8c808000000b200441f0026a41206a200441cb026a280000360200200441f0026a41186a200441c3026a290000370300200441f0026a41106a200441bb026a290000370300200441f0026a41086a200441b3026a290000370300200420042900ab02220d3703f00220042005360298032004200c36029403200da741ff01714103470d01410221000c020b200441fc026a200141086a29020037020020044184036a200141106a2902003702002004418c036a200141186a29020037020020044194036a200141206a2902003702002004419c036a2209200141286a280200360200200420023602f002200420012902003702f402200441d8036a41096a20044198016a410172220141086a290000370000200441d8036a41116a200141106a290000370000200441d8036a41196a200141186a290000370000200441d8036a41206a2001411f6a2900003700002004200c36028004200420012900003700d903200420033a00d8032004200236029004200420042802f80220092802002201200141284b22091b220136028c04200420042802f402200441f0026a41046a20091b220936028804200341ff01714103470d024102210e0c030b200441d8036a200441f0026a4100200441a8036a10f48580800020042902dc03210d20042802d80321000b2004200d3702b403200420003602b0032004419c026a200441d8036a200441b0036a10ca8c808000000b200441b0036a200441d8036a20044188046a200441a8036a10f48580800020042902b403210d20042802b003210e200428028c042101200428028804210920042802900421020b200241017621030240024020024101710d004100210f0240200320014b0d002003210c410021030c020b2003200141d492c68000109481808000000b200320014f0d0a4101210f200341016a210c200920036a2d0000410f7121030b200420033a00a1042004200f3a00a0042004410036029c0420042001200c6b3602980420042009200c6a36029404200441c0036a200441a8036a360200200441003602b803200420053602b0032004200541c0046a3602b4032004200441f0026a3602bc032004200d3702a8042004200e3602a4042004419c026a20044194046a200141017420026b200441b0036a200441a4046a10cf8c808000024020042d00d80322014103460d0002400240024020010e020103000b20042802fc03220120012802002201417f6a36020020014101470d02200441fc036a21010c010b20042802dc03220120012802002201417f6a36020020014101470d01200441dc036a21010b200110e28a8080000b200541002802c0a3c6800011808080800000200428029c034129490d0020042802f40241002802c0a3c68000118080808000000b0240024020042802a4022201411f4b0d0020042802a0022102200441f0026a20016a4100412020016b108a8e8080001a200441f0026a2002200110848e8080001a20002001360204200041206a200441f0026a41186a290000370000200041186a200441f0026a41106a290000370000200041106a200441f8026a290000370000200020042900f002370008410121030c010b2004280210220241d4006a28020021092002280250210c2004280214220228022c220541017621030240024020054101710d00200228020420022802282205200541284b22051b220e2003490d0c2002280200200220051b2102410021050c010b200228020420022802282205200541284b1b220e2003490d0c200e20034d0d0d200441f9026a20022802002002200541284b1b220220036a2d000041f001713a0000410121050b200420053a00f802200420033602f402200420023602f002200441d0026a200c200441f0026a20042802a00222022001200928021c1187808080000020042802102103200441f0026a41106a200441f8016a41086a290300370200200441f0026a41186a200441f8016a41106a290300370200200441f0026a41206a200441f8016a41186a29030037020020044198036a200441f8016a41206a2802003602002004200b3602f402200420083602f002200420042903f8013702f8022004200a3602a0032004200636029c03200441d8036a41186a200441d0026a41186a2205290000370300200441d8036a41106a200441d0026a41106a2209290000370300200441d8036a41086a200441d0026a41086a220a290000370300200420042900d0023703d8032003200441d8036a20022001200441f0026a10f885808000200041196a2005290000370000200041116a2009290000370000200041096a200a290000370000200020042900d002370001410021030b200020033a00000240200428029c02450d00200241002802c0a3c68000118080808000000b02402007450d002001411f4b0d0020064129490d00200b41002802c0a3c68000118080808000000b20042d00184108470d010c030b20002004290278370001200041003a0000200041196a20044190016a290200370000200041116a20044188016a290200370000200041096a20044180016a2902003700000b200441186a10e3858080000c010b200041003a000020002002290001370001200041196a200241196a290000370000200041116a200241116a290000370000200041096a200241096a2900003700000b200441b0046a2480808080000f0b2002200341e4d7c2800010f980808000000b2001200241e492c6800010f980808000000b4101410110b280808000000b2002200141e492c6800010f980808000000b2003200141e492c6800010f980808000000b2003200e41ac95c68000109581808000000b2003200e41bc95c68000109581808000000b2003200e41cc95c6800010f980808000000b807703067f027e087f23808080800041f0066b2207248080808000200741086a41086a200341086a28020036020020072003290200370308024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024020022d0000417c6a41ff01712208410420084104491b0e050001020304000b200728021022084101762203200728020c22014b0d04200728020821024100210920074188066a41286a220a410036020020074188066a200220036a200220016a10f58d808000200741186a41286a200a280200360200200741186a41206a20074188066a41206a290200370300200741186a41186a20074188066a41186a290200370300200741186a41106a20074188066a41106a290200370300200741186a41086a20074188066a41086a29020037030020072007290288063703182008410171210a4105210341002106200521084100210b0c310b200741f0036a41286a200241d8006a290200370300200741f0036a41206a200241d0006a290200370300200741f0036a41186a200241c8006a290200370300200741f0036a41106a200241c0006a290200370300200741f0036a41086a2208200241386a290200370300200720022902303703f003200741a8026a41286a2002412c6a280200360200200741a8026a41206a200241246a290200370300200741a8026a41186a2002411c6a290200370300200741a8026a41106a200241146a290200370300200741a8026a41086a2002410c6a290200370300200720022902043703a802200720072802f0033602dc0220072008280200200741f0036a412c6a2802002208200841284b22081b3602d802200720072802f403200741f0036a410472220920081b3602d402200241046a2108200741086a200741d4026a10db8d808000220a20072802d802220b41017420072802dc02220c6b2202460d210c220b200741f0036a41286a200241d0006a29020037030020074190046a200241c8006a29020037030020074188046a200241c0006a29020037030020074180046a200241386a290200370300200741f0036a41086a200241306a290200220d37030020072002290228220e3703f003200241086a280200210f20022d000421102007200e3e02b0022007200da72007419c046a2802002208200841284b22081b3602ac02200720072802f403200741f0036a41047220081b3602a802200241046a210a200741086a200741a8026a10db8d808000210820072802ac02220c410174210920072802b002210b024002402008450d0020082009200b6b470d012003200328020820086a36020820074188066a2001200a2003200420052006108286808000200728028806210420072d008c0622024102460d1e20072802b0022208410176220120072802ac0222054b0d0b20072802a802210341002106200741b0066a220a410036020020074188066a200320016a200320056a10f58d808000200741206a20074188066a41106a290200370300200741186a41106a20074188066a41186a290200370300200741186a41186a20074188066a41206a290200370300200741186a41206a200a28020036020020072007290290063703182008410171210f200728028c06210a2007280288062108200245210b410621030c200b2009200b460d20200b4101762208200c4f0d0520072802a80220086a2d0000210c41002d00fca3c680001a41c00441002802c8a3c68000118180808000002208450d04200841023a0000200841023a009c04200841023a00f803200841023a00d403200841023a00b003200841023a008c03200841023a00e802200841023a00c402200841023a00a002200841023a00fc01200841023a00d801200841023a00b401200841023a009001200841023a006c200841023a0048200841023a00240240024020072802ac02220941017420072802b00222116b4101470d00200741c8046a41026a200241056a220a41026a2d00003a000020074188066a41086a2002410c6a220241086a29020037030020074188066a41106a200241106a29020037030020074188066a41186a200241186a2802003602002007200a2f00003b01c80420072002290200370388060c010b201141016a2210410176220f20094b0d0720072802a802210220074188066a41286a2211410036020020074188066a2002200f6a200220096a10f58d808000200741c8046a41286a2011280200360200200741c8046a41206a20074188066a41206a290200370300200741c8046a41186a20074188066a41186a290200370300200741c8046a41106a20074188066a41106a290200370300200741c8046a41086a20074188066a41086a29020037030020072007290288063703c804200741ac046a200a41086a290000370000200741b4046a200a41106a290000370000200741bc046a200a41186a290000370000200741c4046a200a41206a2800003600002007200a2900003700a4042010410171210a02400240200141286a2802002202450d0020012002417f6a360228200141246a22022002280200220241016a220941002001411c6a280200220f2009200f491b6b360200200141206a28020020024102746a280200220f200141186a28020022024f0d0a200141146a280200200f4107746a220241046a200220022d00004108461b10e385808000200241063a0004200241083a00002002200a36022c200220072900a1043700052002410d6a200741a1046a41086a290000370000200241156a200741a1046a41106a2900003700002002411d6a200741a1046a41186a290000370000200241246a200741c0046a290000370000200220072903c804370230200241386a200741c8046a41086a290300370200200241c0006a200741c8046a41106a290300370200200241c8006a200741c8046a41186a290300370200200241d0006a200741e8046a290300370200200241d8006a200741f0046a2802003602000c010b0240200141186a28020022022001280210470d00200141106a2002109e86808000200128021821020b200141146a28020020024107746a220241063a0004200241083a0000200220072900a1043700052002200a36022c200220072903c8043702302002410d6a200741a1046a41086a290000370000200241156a200741a1046a41106a2900003700002002411d6a200741a1046a41186a290000370000200241246a200741c0046a290000370000200241386a200741c8046a41086a290300370200200241c0006a200741c8046a41106a290300370200200241c8006a200741c8046a41186a290300370200200241d0006a200741e8046a290300370200200241d8006a200741f0046a28020036020020012001280218220f41016a3602180b410021100b2008200c410f71200c410476200b4101711b41246c6a220220103a0000200220072f01c8043b00012002200f3602042002200729038806370208200241036a200741c8046a41026a2d00003a0000200241106a20074188066a41086a290300370200200241186a20074188066a41106a290300370200200241206a20074188066a41186a280200360200200741033a00cc04200720083602f804200741073a00c80420074188066a2001200741c8046a20032004200520061081868080002007280288064102460d1c200741c8006a41086a200741a4066a290200370300200741c8006a41106a20074188066a41246a280200360200200741c0006a200741e8066a280200360200200741186a41206a200741e0066a290200370300200741186a41186a200741d8066a290200370300200741186a41106a200741d0066a290200370300200741186a41086a200741c8066a2902003703002007200729029c06370348200720072902c006370318200728028c06220341087621022007280298062105200728029406210420072f019206210c20072d009106210920072d009006210620072802b006210120072802b406210f20072802b806210820072802bc06210a0c1e0b200b20086a2209410176220b200c4b0d0720072802a802210220074188066a41286a220f410036020020074188066a2002200b6a2002200c6a10f58d808000200741fc056a200f280200360200200741f4056a20074188066a41206a290200370200200741ec056a20074188066a41186a290200370200200741e4056a20074188066a41106a290200370200200741dc056a20074188066a41086a2202290200370200200741d4056a200729028806370200200741b4056a200a41086a290200370200200741bc056a200a41106a290200370200200741c4056a200a41186a290200370200200741cc056a200a41206a2802003602002003200328020820086a360208200720094101713602d0052007200a2902003702ac05200741063a00a80520074188066a2001200741a8056a20032004200520061081868080002007280288064102460d1b200728028c062103200741c8046a200241dc0010848e8080001a20074188066a200741a8026a200810da8d80800002400240200141286a2802002202450d0020012002417f6a360228200141246a22022002280200220241016a220441002001411c6a280200220520042005491b6b360200200141206a28020020024102746a2802002204200141186a28020022024f0d0a200141146a28020020044107746a220241046a200220022d00004108461b10e38580800020022003360204200241083a0000200241086a200741c8046a41dc0010848e8080001a0c010b0240200141186a28020022022001280210470d00200141106a2002109e86808000200128021821020b200141146a28020020024107746a22022003360204200241083a0000200241086a200741c8046a41dc0010848e8080001a20012001280218220441016a3602180b200741206a2007419c066a290200370300200741286a200741a4066a290200370300200741306a200741ac066a290200370300200741386a200741b4066a2802003602002007200729029406370318200728028806210f200728028c062108200728029006210a41002106410621030c1d0b2002280230210a20074198046a2002412c6a28020036020020074190046a200241246a29020037030020074188046a2002411c6a29020037030020074180046a200241146a290200370300200741f0036a41086a2002410c6a290200370300200720022902043703f003200728020c220b41017420072802102208460d1920084101762202200b4f0d09200728020820026a2d000021022003200328020841016a360208200a2002410f71200241047620084101711b41246c6a22022d00002108200241023a000002400240024020084102460d00200741e0006a41096a200241096a290000370000200741e0006a41116a200241116a290000370000200741e0006a41196a200241196a290000370000200741e0006a41206a200241206a280000360000200720083a00602007200229000137006120074188066a2001200741e0006a2003200420052006108286808000200728028806210320072d008c0622044102460d0220022003360204200241003a000020040d0120074193066a200741f0036a41086a2903003700002007419b066a200741f0036a41106a290300370000200741a3066a200741f0036a41186a290300370000200741ab066a200741f0036a41206a290300370000200741b3066a20074198046a280200360000200720072903f00337008b0620002007290088063700052000410d6a20074188066a41086a290000370000200041156a20074188066a41106a2900003700002000411d6a20074188066a41186a290000370000200041256a20074188066a41206a2900003700002000412c6a200741af066a2900003700002000200a360234200041073a0004200041013602000c320b2003280208220641017622082003280204220b4b0d0c2003280200210320074188066a41286a220c410036020020074188066a200320086a2003200b6a10f58d808000200741c8046a41286a200c280200360200200741c8046a41206a20074188066a41206a290200370300200741c8046a41186a20074188066a41186a290200370300200741c8046a41106a20074188066a41106a290200370300200741c8046a41086a20074188066a41086a29020037030020072007290288063703c8042006410171210b02400240200141286a2802002203450d0020012003417f6a360228200141246a22032003280200220341016a220841002001411c6a280200220620082006491b6b360200200141206a28020020034102746a2802002208200141186a28020022034f0d0f200141146a28020020084107746a220341046a200320032d00004108461b10e3858080002003200b360234200320053600302003200436002c200320053600102003200436000c200341003b0008200341053a0004200341083a0000200320072903c804370238200341c0006a200741c8046a41086a290300370200200341c8006a200741d8046a290300370200200341d0006a200741e0046a290300370200200341d8006a200741e8046a290300370200200341e0006a200741f0046a2802003602000c010b0240200141186a28020022032001280210470d00200141106a2003109e86808000200128021821030b200141146a28020020034107746a2203200b360234200320053600302003200436002c200320053600102003200436000c200341003b0008200341053a0004200341083a0000200320072903c804370238200341c0006a200741c8046a41086a290300370200200341c8006a200741d8046a290300370200200341d0006a200741e0046a290300370200200341d8006a200741e8046a290300370200200341e0006a200741f0046a28020036020020012001280218220841016a3602180b20022008360204200241003a00000b200741d0006a20074184046a290200370300200741d8006a2007418c046a280200360200200720072902fc033703484100210b2007280298042108200728029404210f200728029004210120072802f803210520072802f403210420072f01f203210c20072d00f103210920072d00f0032106410721030c2f0b2000410236020020002003360204024020072d00f00322004103460d0002400240024020000e020103000b200728029404220020002802002200417f6a36020020004101470d0220074194046a21000c010b20072802f403220020002802002200417f6a36020020004101470d01200741f0036a41047221000b200010e28a8080000b200a41002802c0a3c68000118080808000000c2f0b200741c8046a41286a200241d8006a290200370300200741c8046a41206a200241d0006a290200370300200741c8046a41186a200241c8006a290200370300200741c8046a41106a200241c0006a290200370300200741c8046a41086a220a200241386a290200370300200720022902303703c804200228022c2108200741a8026a41286a200241286a280200360200200741a8026a41206a200241206a290200370300200741a8026a41186a200241186a290200370300200741a8026a41106a200241106a290200370300200741a8026a41086a200241086a290200370300200720022902003703a802200720072802c80436028c012007200a280200200741f4046a2802002202200241284b22021b36028801200720072802cc04200741c8046a41047220021b360284010240024002400240024002400240024002400240200741086a20074184016a10db8d808000220a2007280288012202410174200728028c01220f6b220b470d00200a200728020c41017420072802106b460d010b200a200b490d012007280210200a6a220b4101762202200728020c220c4f0d02200728020820026a2d000021022003200a20032802086a41016a36020820082002410f712002410476200b4101711b41246c6a22022d0000210a200241023a0000200a4102460d0320074184026a41096a200241096a29000037000020074184026a41116a200241116a29000037000020074184026a41196a200241196a29000037000020074184026a41206a200241206a2800003600002007200a3a008402200720022900013700850220074188066a200120074184026a2003200420052006108286808000200728028806210320072d008c0622044102460d0520022003360204200241003a000020040d04200041346a20074184016a10d98d80800020002008360230200041013602002000412c6a200741d0026a280200360200200041246a200741c8026a2903003702002000411c6a200741c0026a290300370200200041146a200741b8026a2903003702002000410c6a200741b0026a290300370200200020072903a8023702040c060b20072005360298042007200436029404200720053602f803200720043602f4034100210b200741003b01f003024020072d00a8024103460d00200741a8026a200741f0036a10e785808000210b0b200f410176220920024b0d13200728028401210a20074188066a41286a220c410036020020074188066a200a20096a200a20026a10f58d808000200741186a41286a200c280200360200200741186a41206a20074188066a41206a2202290200370300200741186a41186a20074188066a41186a2210290200370300200741186a41106a20074188066a41106a2211290200370300200741186a41086a20074188066a41086a221229020037030020072007290288063703182003280204210a2003280200210920032802082103200728028c012113200728028801211420072f00f103211520072d00f3032116200c200741a8026a41286a2802003602002002200741a8026a41206a2903003703002010200741a8026a41186a2903003703002011200741a8026a41106a2903003703002012200741a8026a41086a290300370300200720072903a802370388062003201441017420136b6a220341017621020240024020034101710d002002200a4b0d16200741003a00dc02200720023602d802200720093602d4020c010b2002200a4b0d162002200a4f0d17200720093602d402200720023602d802200741dd026a200920026a2d000041f001713a0000200741013a00dc020b2004411076210c20044108762109200f410171210a201520164110747221022001200620074188066a200741d4026a1083868080004100210320042106200521112005210f0c070b200f200a41016a22096a220c410176220b20024b0d16200728028401210320074188066a41286a2206410036020020074188066a2003200b6a200320026a10f58d80800020074190016a41286a200628020036020020074190016a41206a20074188066a41206a29020037030020074190016a41186a20074188066a41186a29020037030020074190016a41106a20074188066a41106a29020037030020074190016a41086a20074188066a41086a290200370300200741c0016a41086a200741a8026a41086a290300370300200741c0016a41106a200741a8026a41106a290300370300200741c0016a41186a200741a8026a41186a290300370300200741c0016a41206a200741a8026a41206a290300370300200741c0016a41286a200741a8026a41286a280200360200200720072902880637039001200720072903a8023703c001200728028c01200a6a220641017622022007280288012203490d0520022003418cd5c2800010f980808000000b2002200c418cd5c2800010f980808000000b20032802082206410176220a2003280204220b4b0d152003280200210320074188066a41286a220c410036020020074188066a2003200a6a2003200b6a10f58d808000200741f0036a41286a200c280200360200200741f0036a41206a20074188066a41206a290200370300200741f0036a41186a20074188066a41186a290200370300200741f0036a41106a20074188066a41106a290200370300200741f0036a41086a20074188066a41086a29020037030020072007290288063703f0032006410171210b02400240200141286a2802002203450d0020012003417f6a360228200141246a22032003280200220341016a220a41002001411c6a2802002206200a2006491b6b360200200141206a28020020034102746a280200220a200141186a28020022034f0d18200141146a280200200a4107746a220341046a200320032d00004108461b10e3858080002003200b360234200320053600302003200436002c200320053600102003200436000c200341003b0008200341053a0004200341083a0000200320072903f003370238200341c0006a200741f0036a41086a290300370200200341c8006a20074180046a290300370200200341d0006a20074188046a290300370200200341d8006a20074190046a290300370200200341e0006a20074198046a2802003602000c010b0240200141186a28020022032001280210470d00200141106a2003109e86808000200128021821030b200141146a28020020034107746a2203200b360234200320053600302003200436002c200320053600102003200436000c200341003b0008200341053a0004200341083a0000200320072903f003370238200341c0006a200741f0036a41086a290300370200200341c8006a20074180046a290300370200200341d0006a20074188046a290300370200200341d8006a20074190046a290300370200200341e0006a20074198046a28020036020020012001280218220a41016a3602180b2002200a360204200241003a00000b200728028c012201410176220320072802880122044b0d1620072802840121024100210b20074188066a41286a2205410036020020074188066a200220036a200220046a10f58d808000200741186a41286a2005280200360200200741186a41206a20074188066a41206a290200370300200741186a41186a20074188066a41186a290200370300200741186a41106a20074188066a41106a290200370300200741186a41086a20074188066a41086a290200370300200720072902880637031820072d00a802210320072d00ac02210620072d00ad02210920072f01ae02210c20072802b002211120072802b402210520072f00a902210220072d00ab022104200741c8006a41106a200741a8026a41206a280200360200200741c8006a41086a200741a8026a41186a290300370300200720072903b8023703482001410171210a2002200441107472210220072802d002210f20072802cc0221040c030b2000410236020020002003360204200741a8026a10e585808000200841002802c0a3c68000118080808000000b20072802f4044129490d3020072802cc0441002802c0a3c68000118080808000000c300b20072802840120026a2d0000210341002d00fca3c680001a41c00441002802c8a3c68000118180808000002210450d14200c410171210c201041023a009c04201041023a00f803201041023a00d403201041023a00b003201041023a008c03201041023a00e802201041023a00c402201041023a00a002201041023a00fc01201041023a00d801201041023a00b401201041023a009001201041023a006c201041023a0048201041023a0024201041023a0000200741b3066a200741c0016a41286a280200360000200741ab066a200741c0016a41206a290300370000200741a3066a200741c0016a41186a2903003700002007419b066a200741c0016a41106a29030037000020074193066a200741c8016a290300370000200720072903c00137008b06200141106a210f02400240200141286a2802002202450d0020012002417f6a360228200141246a22022002280200220241016a220b4100200128021c2211200b2011491b6b360200200141206a28020020024102746a280200220b200141186a221128020022024f0d17200141146a280200200b4107746a220241046a200220022d00004108461b10e385808000200241083a00002002200c360234200220083602302002200729008806370001200241096a20074188066a41086a290000370000200241116a20074188066a41106a290000370000200241196a20074188066a41186a290000370000200241216a20074188066a41206a290000370000200241286a200741af066a2900003700002002200729039001370238200241c0006a20074190016a41086a290300370200200241c8006a20074190016a41106a290300370200200241d0006a20074190016a41186a290300370200200241d8006a20074190016a41206a290300370200200241e0006a20074190016a41286a2802003602000c010b0240200141186a221128020022022001280210470d00200f2002109e86808000201128020021020b200141146a28020020024107746a220241083a000020022007290088063700012002200c360234200220083602302002200729039001370238200241096a20074188066a41086a290000370000200241116a20074188066a41106a290000370000200241196a20074188066a41186a290000370000200241216a20074188066a41206a290000370000200241286a200741af066a290000370000200241c0006a20074190016a41086a290300370200200241c8006a20074190016a41106a290300370200200241d0006a20074190016a41186a290300370200200241d8006a20074190016a41206a290300370200200241e0006a20074190016a41286a28020036020020012001280218220b41016a3602180b20102003410f71200341047620064101711b41246c6a2203200b36020441002102200341003a00000240200728020c22034101742007280210220b200a6a2208470d0020074188066a20074184016a200a10da8d808000200741c8006a41106a200741f0016a41106a280200360200200741c8006a41086a200741f0016a41086a290200370300200741186a41086a20074194066a290200370300200741186a41106a2007419c066a290200370300200741306a200741a4066a290200370300200741386a20074188066a41246a290200370300200741c0006a200741b4066a280200360200200720072902f0013703482007200729028c063703182004411076210c20044108762109200728028806210a20042106200521112005210f20102108410021034100210b0c010b2008410176220620034f0d18200b20096a2209410176220c20034b0d162007280208220220066a2d0000210b20074188066a41286a2206410036020020074188066a2002200c6a200220036a10f58d808000200741f0036a41286a2006280200360200200741f0036a41206a20074188066a41206a290200370300200741f0036a41186a20074188066a41186a290200370300200741f0036a41106a20074188066a41106a290200370300200741f0036a41086a20074188066a41086a29020037030020072007290288063703f003200941017121060240024020012802282202450d0020012002417f6a360228200141246a22022002280200220241016a22034100200128021c220c2003200c491b6b360200200141206a28020020024102746a2802002203200128021822024f0d19200141146a28020020034107746a220241046a200220022d00004108461b10e3858080002002410b6a41003a0000200241003b0009200220053602102002200436020c200241003a0008200241053a0004200241083a000020022006360234200220053602302002200436022c200220072902f001370218200241206a200741f0016a41086a290200370200200241286a200741f0016a41106a280200360200200220072903f003370238200241c0006a200741f0036a41086a290300370200200241c8006a200741f0036a41106a290300370200200241d0006a20074188046a290300370200200241d8006a200741f0036a41206a290300370200200241e0006a200741f0036a41286a2802003602000c010b024020112802002202200f280200470d00200f2002109e86808000201128020021020b200141146a28020020024107746a220241003b0009200220053602102002200436020c200241003a0008200241053a0004200241083a0000200220072902f00137021820022006360234200220053602302002200436022c200220072903f0033702382002410b6a41003a0000200241206a200741f0016a41086a290200370200200241286a200741f0016a41106a280200360200200241c0006a200741f0036a41086a290300370200200241c8006a200741f0036a41106a290300370200200241d0006a20074188046a290300370200200241d8006a200741f0036a41206a290300370200200241e0006a200741f0036a41286a28020036020020012001280218220341016a3602180b2010200b410f71200b41047620084101711b41246c6a220220033602044100210b200241003a000020074188066a20074184016a200a10da8d808000200741206a20074194066a290200370300200741286a2007419c066a290200370300200741306a200741a4066a290200370300200741386a20074188066a41246a290200370300200741c0006a200741b4066a2802003602002007200729028c06370318200728028806210a41032103201021080b024020072802f4044129490d0020072802cc0441002802c0a3c68000118080808000000b20042101201121040c2d0b2003200141e491c68000109481808000000b410441c00410b280808000000b2008200c418cd5c2800010f980808000000b200f200941e491c68000109481808000000b200f200241d4d7c2800010f980808000000b200b200c41e491c68000109481808000000b2004200241d4d7c2800010f980808000000b2001200541e491c68000109481808000000b2002200b418cd5c2800010f980808000000b2008200b41e491c68000109481808000000b2008200341d4d7c2800010f980808000000b2009200241e491c68000109481808000000b2002200a41f492c68000109581808000000b2002200a418493c68000109581808000000b2002200a419493c6800010f980808000000b200b200241e491c68000109481808000000b200a200b41e491c68000109481808000000b200a200341d4d7c2800010f980808000000b2003200441e491c68000109481808000000b410441c00410b280808000000b200b200241d4d7c2800010f980808000000b200c200341e491c68000109481808000000b2003200241d4d7c2800010f980808000000b20062003418cd5c2800010f980808000000b200720053602b006200720043602ac0620072005360290062007200436028c064100210b200741003b018806024020072d00f0034103460d00200741f0036a20074188066a10e785808000210b0b2003280208220c410176210802400240024002400240200c4101710d0020082003280204220c4b0d02200741003a00d004200720083602cc04200720032802003602c8040c010b20082003280204220c4b0d022008200c4f0d032007200328020022033602c804200720083602cc04200741d1046a200320086a2d000041f001713a0000200741013a00d0040b20012006200241046a200741c8046a1083868080004100210941072103410021062004210f200521080c170b2008200c41f492c68000109581808000000b2008200c418493c68000109581808000000b2008200c419493c6800010f980808000000b200728028c0621040b2000410236020020002004360204200728029c044129490d1320072802f40341002802c0a3c68000118080808000000c130b4100210b0b200728029c044129490d1020072802f40341002802c0a3c68000118080808000000c100b419cd5c28000412a41c8d5c2800010f880808000000b200a200728020c41017420072802106b470d00200741d0046a2005360200200720043602cc04200741003a00c804200741a8026a200741c8046a10e785808000210c2003280208200a6a220b41017621022003280204210a2003280200210302400240200b4101710d002002200a4b0d05200741003a0090062007200236028c0620072003360288060c010b2002200a4b0d052002200a4f0d0620072003360288062007200236028c0620074191066a200320026a2d000041f001713a0000200741013a0090060b20012006200820074188066a108386808000200741d4046a2102200c0d0120072802f003210a20072802f403210120072802f8032108200728029c0421034100210b20074188066a41286a2206410036020020074188066a20012009200341284b220c1b2201200120082003200c1b6a10f686808000200741186a41286a2006280200360200200741186a41206a20074188066a41206a290200370300200741186a41186a20074188066a41186a290200370300200741186a41106a20074188066a41106a290200370300200741186a41086a20074188066a41086a29020037030020072007290288063703180c020b200a2002490d07200c410176220a200b4b0d0520072802d402210220074188066a41286a2209410036020020074188066a2002200a6a2002200b6a10f58d808000200741c8046a41286a220a2009280200360200200741c8046a41206a220b20074188066a41206a290200370300200741c8046a41186a220920074188066a41186a290200370300200741c8046a41106a220f20074188066a41106a290200370300200741c8046a41086a221020074188066a41086a29020037030020072007290288063703c80441002d00fca3c680001a41c00441002802c8a3c68000118180808000002202450d06200241023a009c04200241023a00f803200241023a00d403200241023a00b003200241023a008c03200241023a00e802200241023a00c402200241023a00a002200241023a00fc01200241023a00d801200241023a00b401200241023a009001200241023a006c200241023a0048200241023a0024200241023a000020074190036a41086a200841086a29020037030020074190036a41106a200841106a29020037030020074190036a41186a200841186a29020037030020074190036a41206a200841206a29020037030020074190036a41286a200841286a2802003602002007200829020037039003200741c4036a20072903c804370200200741cc036a2010290300370200200741d4036a200f290300370200200741dc036a2009290300370200200741e4036a200b290300370200200741ec036a200a2802003602002007200c4101713602c003200720023602bc0320074188066a200120074190036a20032004200520061081868080002007280288064102460d08200741c8006a41086a200741a4066a290200370300200741c8006a41106a200741ac066a280200360200200741c0006a200741e8066a280200360200200741386a200741e0066a290200370300200741306a200741d8066a290200370300200741186a41106a200741d0066a290200370300200741186a41086a200741c8066a2902003703002007200729029c06370348200720072902c006370318200728028c06220341087621022007280298062105200728029406210420072f019206210c20072d009106210920072d009006210620072802b006210120072802b406210f20072802b806210820072802bc06210a4100210b0c0c0b20072802f003210a20072802f403210120072802f8032108200728029c04210320074188066a41286a220b410036020020074188066a20012009200341284b22061b220120012008200320061b6a10f686808000200741186a41286a200b280200360200200741186a41206a20074188066a41206a290200370300200741186a41186a20074188066a41186a290200370300200741186a41106a20074188066a41106a290200370300200741186a41086a20074188066a41086a29020037030020072007290288063703184101210b0b200741c8006a41106a200241106a280200360200200741c8006a41086a200241086a2902003703002007200229020037034841052103410021060c0a0b2002200a41f492c68000109581808000000b2002200a418493c68000109581808000000b2002200a419493c6800010f980808000000b200a200b41e491c68000109481808000000b410441c00410b280808000000b41002d00fca3c680001a41c00441002802c8a3c68000118180808000002202450d01200241023a0000200241023a009c04200241023a00f803200241023a00d403200241023a00b003200241023a008c03200241023a00e802200241023a00c402200241023a00a002200241023a00fc01200241023a00d801200241023a00b401200241023a009001200241023a006c200241023a0048200241023a0024024020072802dc02200a6a220c410176220f20072802d802220b490d00200f200b418cd5c2800010f980808000000b200c41016a22114101762210200b4b0d0220072802d4022209200f6a2d0000210f20074188066a41286a2212410036020020074188066a200920106a2009200b6a10f58d808000200741c8046a41286a2012280200360200200741c8046a41206a20074188066a41206a290200370300200741c8046a41186a20074188066a41186a290200370300200741c8046a41106a20074188066a41106a290200370300200741c8046a41086a20074188066a41086a29020037030020072007290288063703c804200741ec026a200841086a290000370000200741f4026a200841106a290000370000200741fc026a200841186a29000037000020074184036a200841206a2900003700002007418c036a200841286a280000360000200720082900003700e4022011410171210902400240200141286a2802002208450d0020012008417f6a360228200141246a22082008280200220841016a220b41002001411c6a2802002210200b2010491b6b360200200141206a28020020084102746a280200220b200141186a28020022084f0d05200141146a280200200b4107746a220841046a200820082d00004108461b10e385808000200841053a0004200841083a000020082009360234200820072900e1023700052008410d6a200741e1026a41086a290000370000200841156a200741e1026a41106a2900003700002008411d6a200741e1026a41186a290000370000200841256a200741e1026a41206a2900003700002008412c6a20074188036a290000370000200820072903c804370238200841c0006a200741c8046a41086a290300370200200841c8006a200741c8046a41106a290300370200200841d0006a200741c8046a41186a290300370200200841d8006a200741c8046a41206a290300370200200841e0006a200741f0046a2802003602000c010b0240200141186a28020022082001280210470d00200141106a2008109e86808000200128021821080b200141146a28020020084107746a220841053a0004200841083a0000200820072900e10237000520082009360234200820072903c8043702382008410d6a200741e1026a41086a290000370000200841156a200741e1026a41106a2900003700002008411d6a200741e1026a41186a290000370000200841256a200741e1026a41206a2900003700002008412c6a20074188036a290000370000200841c0006a200741c8046a41086a290300370200200841c8006a200741c8046a41106a290300370200200841d0006a200741c8046a41186a290300370200200841d8006a200741c8046a41206a290300370200200841e0006a200741f0046a28020036020020012001280218220b41016a3602180b2002200f410f71200f410476200c4101711b41246c6a2208200b3602044100210b200841003a0000200741f8046a200741086a200a10da8d808000200720023602f404200741033a00c80420074188066a2001200741c8046a20032004200520061081868080002007280288064102460d00200741c8006a41086a200741a4066a290200370300200741c8006a41106a20074188066a41246a280200360200200741c0006a200741e8066a280200360200200741386a200741e0066a290200370300200741306a200741d8066a290200370300200741186a41106a200741d0066a290200370300200741186a41086a200741c8066a2902003703002007200729029c06370348200720072902c006370318200728028c06220341087621022007280298062105200728029406210420072f019206210c20072d009106210920072d009006210620072802b006210120072802b406210f20072802b806210820072802bc06210a0c040b200728028c0621022000410236020020002002360204200728029c044129490d0520072802f40341002802c0a3c68000118080808000000c050b410441c00410b280808000000b2010200b41e491c68000109481808000000b200b200841d4d7c2800010f980808000000b200728029c044129490d0020072802f40341002802c0a3c68000118080808000000b200020023b0005200020053602102000200436020c2000200c3b010a200020093a0009200020063a0008200020033a00042000200b360200200020072903483702142000200a360234200020083602302000200f36022c20002001360228200041076a20024110763a00002000411c6a200741c8006a41086a290300370200200041246a200741c8006a41106a280200360200200041e0006a200741c0006a280200360200200041d8006a200741386a290300370200200041d0006a200741306a290300370200200041c8006a200741186a41106a290300370200200041c0006a200741186a41086a290300370200200020072903183702380b200741f0066a2480808080000bb11503057f017e157f23808080800041b0036b22072480808080002007200536020420072004360200024002400240024002400240024002400240024002400240024020022d00000d00200228020421020c010b200741206a200241196a290000370300200741186a200241116a290000370300200741106a200241096a290000370300200720022900013703082003280208220541017621020240024020054101710d002002200328020422054b0d05200741003a00ec01200720023602e801200720032802003602e4010c010b2002200328020422054b0d05200220054f0d062007200328020022053602e401200720023602e801200741ed016a200520026a2d000041f001713a0000200741013a00ec010b200741cc026a2001200741086a200741e4016a10878680800020072802d002210220072802cc020d010b0240200141286a28020022052001411c6a22082802002204470d00200810a889808000200128021c2104200128022821050b200141206a280200200141246a28020020056a22054100200420052004491b6b4102746a20023602002001200128022841016a360228200141186a280200220420024d0d05200141146a28020020024107746a22022d00002104200741296a200241016a41df0010848e8080001a200241043a0004200241083a0000200228026021092007280200210520072802042108024020044108470d00200741e4016a2007412c6a41dc0010848e8080001a200720033602c802200720093602c002200720013602c402200741cc026a2001200741e4016a200320052008200610818680800020072802cc0222034102460d022003410147210a20072802ac03210b20072902a403210c20072802a0032104200728029c03210520072802980321062007280294032108200728029003210d200728028c03210e200728028803210f2007280284032110200728028003211120072802fc02211220072802f802211320072802f402211420072802f002211520072802ec02211620072802e802211720072802e402211820072802e002211920072802dc02211a20072802d802211b20072802d402211c20072802d0022102410821034100211d0c090b2003280208211d2003280204210b2003280200210a200741e4016a410172200741296a41df0010848e8080001a200741e0016a200241fc006a280000360200200741d8016a200241f4006a290000370300200741c8016a41086a200241ec006a290000370300200720013602c402200720033602c802200720043a00e401200720022900643703c801200741cc026a2001200741e4016a200320052008200610818680800020072802cc0222034102460d0120072902a803210c20072802a403211e20072802a0032104200728029c03210520072802980321062007280294032108200728029003210d200728028c03210e200728028803210f2007280284032110200728028003211120072802fc02211220072802f802211320072802f402211420072802f002211520072802ec02211620072802e802211720072802e402211820072802e002211a20072802dc02211b20072802d802211c20072802d402211f20072802d0022102024020034101460d00201d410176210302400240201d4101710d0002402003200b4b0d00410021190c020b2003200b41f492c68000109581808000000b2003200b4b0d082003200b4f0d09200a20036a2d00004170712120410121190b200c422088a7210b4100211d200741cc026a41286a22214100360200200741cc026a200a200a20036a10f58d80800020074184026a220a41286a2021280200360200200a41206a200741cc026a41206a290200370200200a41186a200741cc026a41186a290200370200200a41106a200741cc026a41106a29020037020041082103200a41086a200741cc026a41086a290200370200200a20072902cc02370200200741b1026a20203a0000200741b0026a20193a0000200741f0016a200741c8016a41086a290300370200200741f8016a200741c8016a41106a29030037020020074180026a200741c8016a41186a280200360200200720093602e401200720072903c8013702e801200141dc006a200741e4016a10958d8080001a200c422086201ead84210c4101210a201a2119201b211a201c211b201f211c0c090b200741a8016a41186a200741c8016a41186a280200360200200741a8016a41106a200741c8016a41106a290300370300200741a8016a41086a200741c8016a41086a290300370300200720072903c8013703a801200241ff01712103200241807e71211d4100210a201821192017211820162117201521162014211520132114201221132011211220102111200f2110200e210f200d210e2008210d200621082005210620042105201e21042009210b201f21020c080b200041023a000420002002360200200420042802002201417f6a36020020014101470d08200710e28a8080000c080b20072802d0022101200041023a0004200020013602000c070b2002200541f492c68000109581808000000b20022005418493c68000109581808000000b20022005419493c6800010f980808000000b2002200441e4d7c2800010f980808000000b2003200b418493c68000109581808000000b2003200b419493c6800010f980808000000b20074188016a41186a200741a8016a41186a28020036020020074188016a41106a200741a8016a41106a29030037030020074188016a41086a200741a8016a41086a290300370300200720072903a801370388012003201d7221090240024020012802282203450d0020012003417f6a36022820012001280224220341016a221d4100200128021c221e201d201e491b6b360224200128022020034102746a28020022032001280218221d4f0d03200128021420034107746a220141046a200120012d00004108461b10e3858080002001200b3602602001200c37025820012004360254200120053602502001200636024c200120083602482001200d3602442001200e3602402001200f36023c2001201036023820012011360234200120123602302001201336022c2001201436022820012015360224200120163602202001201736021c20012018360218200120193602142001201a3602102001201b36020c2001201c3602082001200236020420012009360200200141fc006a200741a0016a280200360200200141f4006a20074198016a290300370200200141ec006a20074188016a41086a29030037020020012007290388013702640c010b0240200128021822032001280210470d00200141106a2003109e86808000200128021821030b200128021420034107746a2203200b3602602003200c37025820032004360254200320053602502003200636024c200320083602482003200d3602442003200e3602402003200f36023c2003201036023820032011360234200320123602302003201336022c2003201436022820032015360224200320163602202003201736021c20032018360218200320193602142003201a3602102003201b36020c2003201c3602082003200236020420032009360200200341fc006a200741a0016a280200360200200341f4006a20074198016a290300370200200341ec006a20074190016a290300370200200320072903880137026420012001280218220341016a3602180b2000200a3a0004200020033602000b200741b0036a2480808080000f0b2003201d41d4d7c2800010f980808000000b840502047f047e23808080800041b0016b2204248080808000024020022d000022054103460d0002400240024002402005417f6a0e020100040b20022d00010d010c030b200241016a21050c010b200241026a21050b200441086a41186a200541186a290000370300200441086a41106a200541106a290000370300200441086a41086a200541086a29000037030020042005290000370308200328020421062003280200210520044184016a41286a2207410036020020044184016a2005200520066a10f686808000200441d8006a41286a20072802002205360200200441d8006a41206a20044184016a41206a2902002208370300200441d8006a41186a20044184016a41186a2902002209370300200441d8006a41106a20044184016a41106a290200220a370300200441d8006a41086a20044184016a41086a290200220b370300200441086a41286a200b370300200441386a200a370300200441c0006a2009370300200441c8006a2008370300200441d0006a20053602002004200429028401220837035820042008370328200441d4006a20032f01083b0100200041dc006a200441086a10958d8080001a0b024020012d000022034103460d0002400240024020030e020103000b2001280224220320032802002203417f6a36020020034101470d02200141246a21030c010b2001280204220320032802002203417f6a36020020034101470d01200141046a21030b200310e28a8080000b20012002290200370200200141286a200241286a280200360200200141206a200241206a290200370200200141186a200241186a290200370200200141106a200241106a290200370200200141086a200241086a290200370200200441b0016a2480808080000bab7803097f027e067f23808080800041f0066b2207248080808000200741086a41086a200341086a28020036020020072003290200370308024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024020022d0000417c6a41ff01712208410420084104491b0e050001020304000b200728021022084101762203200728020c22014b0d04200728020821024100210920074188066a41286a220a410036020020074188066a200220036a200220016a10f58d808000200741186a41286a200a280200360200200741186a41206a20074188066a41206a290200370300200741186a41186a20074188066a41186a290200370300200741186a41106a20074188066a41106a290200370300200741186a41086a20074188066a41086a29020037030020072007290288063703182008410171210a200541204b410174210b4105210c2004210d200521084100210e0c310b200741f0036a41286a200241d8006a290200370300200741f0036a41206a200241d0006a290200370300200741f0036a41186a200241c8006a290200370300200741f0036a41106a200241c0006a290200370300200741f0036a41086a2208200241386a290200370300200720022902303703f003200741a8026a41286a2002412c6a280200360200200741a8026a41206a200241246a290200370300200741a8026a41186a2002411c6a290200370300200741a8026a41106a200241146a290200370300200741a8026a41086a2002410c6a290200370300200720022902043703a802200720072802f0033602dc0220072008280200200741f0036a412c6a2802002208200841284b22081b3602d802200720072802f403200741f0036a410472220f20081b3602d402200241046a2108200741086a200741d4026a10db8d808000220a20072802d802220e41017420072802dc02220c6b2202460d210c220b200741f0036a41286a200241d0006a29020037030020074190046a200241c8006a29020037030020074188046a200241c0006a29020037030020074180046a200241386a290200370300200741f0036a41086a200241306a29020022103703002007200229022822113703f003200241086a280200210d20022d0004210f200720113e02b00220072010a72007419c046a2802002208200841284b22081b3602ac02200720072802f403200741f0036a41047220081b3602a802200241046a210a200741086a200741a8026a10db8d808000210820072802ac02220c410174210b20072802b002210e024002402008450d002008200b200e6b470d012003200328020820086a36020820074188066a2001200a2003200420052006108586808000200728028806210420072d008c0622024102460d1e20072802b0022208410176220520072802ac0222014b0d0b20072802a80221034100210b200741b0066a220a410036020020074188066a200320056a200320016a10f58d808000200741206a20074188066a41106a290200370300200741186a41106a20074188066a41186a290200370300200741186a41186a20074188066a41206a290200370300200741186a41206a200a28020036020020072007290290063703182008410171210d200728028c06210a2007280288062108200245210e4106210c0c200b200b200e460d20200e4101762208200c4f0d0520072802a80220086a2d0000210c41002d00fca3c680001a41c00441002802c8a3c68000118180808000002208450d04200841023a0000200841023a009c04200841023a00f803200841023a00d403200841023a00b003200841023a008c03200841023a00e802200841023a00c402200841023a00a002200841023a00fc01200841023a00d801200841023a00b401200841023a009001200841023a006c200841023a0048200841023a00240240024020072802ac02220b41017420072802b00222096b4101470d00200741c8046a41026a200241056a220a41026a2d00003a000020074188066a41086a2002410c6a220241086a29020037030020074188066a41106a200241106a29020037030020074188066a41186a200241186a2802003602002007200a2f00003b01c80420072002290200370388060c010b200941016a220f410176220d200b4b0d0720072802a802210220074188066a41286a2209410036020020074188066a2002200d6a2002200b6a10f58d808000200741c8046a41286a2009280200360200200741c8046a41206a20074188066a41206a290200370300200741c8046a41186a20074188066a41186a290200370300200741c8046a41106a20074188066a41106a290200370300200741c8046a41086a20074188066a41086a29020037030020072007290288063703c804200741ac046a200a41086a290000370000200741b4046a200a41106a290000370000200741bc046a200a41186a290000370000200741c4046a200a41206a2800003600002007200a2900003700a404200f410171210a02400240200141286a2802002202450d0020012002417f6a360228200141246a22022002280200220241016a220b41002001411c6a280200220d200b200d491b6b360200200141206a28020020024102746a280200220d200141186a28020022024f0d0a200141146a280200200d4107746a220241046a200220022d00004108461b10e385808000200241063a0004200241083a00002002200a36022c200220072900a1043700052002410d6a200741a1046a41086a290000370000200241156a200741a1046a41106a2900003700002002411d6a200741a1046a41186a290000370000200241246a200741c0046a290000370000200220072903c804370230200241386a200741c8046a41086a290300370200200241c0006a200741c8046a41106a290300370200200241c8006a200741c8046a41186a290300370200200241d0006a200741e8046a290300370200200241d8006a200741f0046a2802003602000c010b0240200141186a28020022022001280210470d00200141106a2002109e86808000200128021821020b200141146a28020020024107746a220241063a0004200241083a0000200220072900a1043700052002200a36022c200220072903c8043702302002410d6a200741a1046a41086a290000370000200241156a200741a1046a41106a2900003700002002411d6a200741a1046a41186a290000370000200241246a200741c0046a290000370000200241386a200741c8046a41086a290300370200200241c0006a200741c8046a41106a290300370200200241c8006a200741c8046a41186a290300370200200241d0006a200741e8046a290300370200200241d8006a200741f0046a28020036020020012001280218220d41016a3602180b4100210f0b2008200c410f71200c410476200e4101711b41246c6a2202200f3a0000200220072f01c8043b00012002200d3602042002200729038806370208200241036a200741c8046a41026a2d00003a0000200241106a20074188066a41086a290300370200200241186a20074188066a41106a290300370200200241206a20074188066a41186a280200360200200741033a00cc04200720083602f804200741073a00c80420074188066a2001200741c8046a20032004200520061084868080002007280288064102460d1c200741c8006a41086a200741a4066a290200370300200741c8006a41106a20074188066a41246a280200360200200741c0006a200741e8066a280200360200200741186a41206a200741e0066a290200370300200741186a41186a200741d8066a290200370300200741186a41106a200741d0066a290200370300200741186a41086a200741c8066a2902003703002007200729029c06370348200720072902c006370318200728028c06220c41087621022007280298062105200728029406210420072f019206210f20072d009106210920072d009006210b20072802b006210320072802b406210d20072802b806210820072802bc06210a0c1e0b200e20086a220b410176220e200c4b0d0720072802a802210220074188066a41286a220d410036020020074188066a2002200e6a2002200c6a10f58d808000200741fc056a200d280200360200200741f4056a20074188066a41206a290200370200200741ec056a20074188066a41186a290200370200200741e4056a20074188066a41106a290200370200200741dc056a20074188066a41086a2202290200370200200741d4056a200729028806370200200741b4056a200a41086a290200370200200741bc056a200a41106a290200370200200741c4056a200a41186a290200370200200741cc056a200a41206a2802003602002003200328020820086a3602082007200b4101713602d0052007200a2902003702ac05200741063a00a80520074188066a2001200741a8056a20032004200520061084868080002007280288064102460d1b200728028c062103200741c8046a200241dc0010848e8080001a20074188066a200741a8026a200810da8d80800002400240200141286a2802002202450d0020012002417f6a360228200141246a22022002280200220241016a220441002001411c6a280200220520042005491b6b360200200141206a28020020024102746a2802002204200141186a28020022024f0d0a200141146a28020020044107746a220241046a200220022d00004108461b10e38580800020022003360204200241083a0000200241086a200741c8046a41dc0010848e8080001a0c010b0240200141186a28020022022001280210470d00200141106a2002109e86808000200128021821020b200141146a28020020024107746a22022003360204200241083a0000200241086a200741c8046a41dc0010848e8080001a20012001280218220441016a3602180b200741206a2007419c066a290200370300200741286a200741a4066a290200370300200741306a200741ac066a290200370300200741386a200741b4066a2802003602002007200729029406370318200728028806210d200728028c062108200728029006210a4100210b4106210c0c1d0b2002280230210a20074198046a2002412c6a280200360200200741f0036a41206a200241246a29020037030020074188046a2002411c6a29020037030020074180046a200241146a290200370300200741f0036a41086a2002410c6a290200370300200720022902043703f003200728020c220e41017420072802102208460d1920084101762202200e4f0d09200728020820026a2d000021022003200328020841016a360208200a2002410f71200241047620084101711b41246c6a22022d00002108200241023a000002400240024020084102460d00200741e0006a41096a200241096a290000370000200741e0006a41116a200241116a290000370000200741e0006a41196a200241196a290000370000200741e0006a41206a200241206a280000360000200720083a00602007200229000137006120074188066a2001200741e0006a2003200420052006108586808000200728028806210320072d008c0622044102460d0220022003360204200241003a000020040d0120074193066a200741f0036a41086a2903003700002007419b066a200741f0036a41106a290300370000200741a3066a200741f0036a41186a290300370000200741ab066a200741f0036a41206a290300370000200741b3066a20074198046a280200360000200720072903f00337008b0620002007290088063700052000410d6a20074188066a41086a290000370000200041156a20074188066a41106a2900003700002000411d6a20074188066a41186a290000370000200041256a20074188066a41206a2900003700002000412c6a200741af066a2900003700002000200a360234200041073a0004200041013602000c320b2003280208220641017622082003280204220e4b0d0c200541204b410174210b2003280200210320074188066a41286a220c410036020020074188066a200320086a2003200e6a10f58d808000200741c8046a41286a200c280200360200200741c8046a41206a20074188066a41206a290200370300200741c8046a41186a20074188066a41186a290200370300200741c8046a41106a20074188066a41106a290200370300200741c8046a41086a20074188066a41086a29020037030020072007290288063703c8042006410171210e02400240200141286a2802002203450d0020012003417f6a360228200141246a22032003280200220341016a220841002001411c6a280200220620082006491b6b360200200141206a28020020034102746a2802002208200141186a28020022034f0d0f200141146a28020020084107746a220341046a200320032d00004108461b10e3858080002003200e360234200320053600302003200436002c200320053600102003200436000c200341003a00092003200b3a0008200341053a0004200341083a0000200320072903c804370238200341c0006a200741c8046a41086a290300370200200341c8006a200741d8046a290300370200200341d0006a200741e0046a290300370200200341d8006a200741c8046a41206a290300370200200341e0006a200741f0046a2802003602000c010b0240200141186a28020022032001280210470d00200141106a2003109e86808000200128021821030b200141146a28020020034107746a2203200e360234200320053600302003200436002c200320053600102003200436000c200341003a00092003200b3a0008200341053a0004200341083a0000200320072903c804370238200341c0006a200741c8046a41086a290300370200200341c8006a200741d8046a290300370200200341d0006a200741e0046a290300370200200341d8006a200741e8046a290300370200200341e0006a200741f0046a28020036020020012001280218220841016a3602180b20022008360204200241003a00000b200741d0006a20074184046a290200370300200741d8006a2007418c046a280200360200200720072902fc033703484100210e2007280298042108200728029404210d200728029004210320072802f803210520072802f403210420072f01f203210f20072d00f103210920072d00f003210b4107210c0c2f0b2000410236020020002003360204024020072d00f00322004103460d0002400240024020000e020103000b200728029404220020002802002200417f6a36020020004101470d0220074194046a21000c010b20072802f403220020002802002200417f6a36020020004101470d01200741f0036a41047221000b200010e28a8080000b200a41002802c0a3c68000118080808000000c2f0b200741c8046a41286a200241d8006a290200370300200741c8046a41206a200241d0006a290200370300200741c8046a41186a200241c8006a290200370300200741c8046a41106a200241c0006a290200370300200741c8046a41086a220a200241386a290200370300200720022902303703c804200228022c2108200741a8026a41286a200241286a280200360200200741a8026a41206a200241206a290200370300200741a8026a41186a200241186a290200370300200741a8026a41106a200241106a290200370300200741a8026a41086a200241086a290200370300200720022902003703a802200720072802c80436028c012007200a280200200741f4046a2802002202200241284b22021b36028801200720072802cc04200741c8046a41047220021b360284010240024002400240024002400240024002400240200741086a20074184016a10db8d808000220a2007280288012202410174200728028c01220b6b220e470d00200a200728020c41017420072802106b460d010b200a200e490d012007280210200a6a220e4101762202200728020c220c4f0d02200728020820026a2d000021022003200a20032802086a41016a36020820082002410f712002410476200e4101711b41246c6a22022d0000210a200241023a0000200a4102460d0320074184026a41096a200241096a29000037000020074184026a41116a200241116a29000037000020074184026a41196a200241196a29000037000020074184026a41206a200241206a2800003600002007200a3a008402200720022900013700850220074188066a200120074184026a2003200420052006108586808000200728028806210320072d008c0622044102460d0520022003360204200241003a000020040d04200041346a20074184016a10d98d80800020002008360230200041013602002000412c6a200741d0026a280200360200200041246a200741c8026a2903003702002000411c6a200741c0026a290300370200200041146a200741b8026a2903003702002000410c6a200741b0026a290300370200200020072903a8023702040c060b20072005360298042007200436029404200720053602f803200720043602f4034100210e200741003a00f1032007200541204b410174220c3a00f003024020072d00a8024103460d00200741a8026a200741f0036a10e785808000210e0b200b410176220f20024b0d13200728028401210a20074188066a41286a220d410036020020074188066a200a200f6a200a20026a10f58d808000200741186a41286a200d280200360200200741186a41206a20074188066a41206a2202290200370300200741186a41186a20074188066a41186a2209290200370300200741186a41106a20074188066a41106a2212290200370300200741186a41086a20074188066a41086a221329020037030020072007290288063703182003280204210a2003280200210f20032802082103200728028c012114200728028801211520072f00f103211620072d00f3032117200d200741a8026a41286a2802003602002002200741a8026a41206a2903003703002009200741a8026a41186a2903003703002012200741a8026a41106a2903003703002013200741a8026a41086a290300370300200720072903a802370388062003201541017420146b6a220341017621020240024020034101710d002002200a4b0d16200741003a00dc02200720023602d8022007200f3602d4020c010b2002200a4b0d162002200a4f0d172007200f3602d402200720023602d802200741dd026a200f20026a2d000041f001713a0000200741013a00dc020b2004411076210f20044108762109200b410171210a201620174110747221022001200620074188066a200741d4026a1083868080002004210b200521012005210d0c070b200b200a41016a220d6a220c410176220e20024b0d16200728028401210320074188066a41286a2206410036020020074188066a2003200e6a200320026a10f58d80800020074190016a41286a200628020036020020074190016a41206a20074188066a41206a29020037030020074190016a41186a20074188066a41186a29020037030020074190016a41106a20074188066a41106a29020037030020074190016a41086a20074188066a41086a290200370300200741c0016a41086a200741a8026a41086a290300370300200741c0016a41106a200741a8026a41106a290300370300200741c0016a41186a200741a8026a41186a290300370300200741c0016a41206a200741a8026a41206a290300370300200741c0016a41286a200741a8026a41286a280200360200200720072902880637039001200720072903a8023703c001200728028c01200a6a220b41017622022007280288012203490d0520022003418cd5c2800010f980808000000b2002200c418cd5c2800010f980808000000b20032802082206410176220a2003280204220e4b0d15200541204b410174210b2003280200210320074188066a41286a220c410036020020074188066a2003200a6a2003200e6a10f58d808000200741f0036a41286a200c280200360200200741f0036a41206a20074188066a41206a290200370300200741f0036a41186a20074188066a41186a290200370300200741f0036a41106a20074188066a41106a290200370300200741f0036a41086a20074188066a41086a29020037030020072007290288063703f0032006410171210e02400240200141286a2802002203450d0020012003417f6a360228200141246a22032003280200220341016a220a41002001411c6a2802002206200a2006491b6b360200200141206a28020020034102746a280200220a200141186a28020022034f0d18200141146a280200200a4107746a220341046a200320032d00004108461b10e3858080002003200e360234200320053600302003200436002c200320053600102003200436000c200341003a00092003200b3a0008200341053a0004200341083a0000200320072903f003370238200341c0006a200741f0036a41086a290300370200200341c8006a20074180046a290300370200200341d0006a20074188046a290300370200200341d8006a200741f0036a41206a290300370200200341e0006a20074198046a2802003602000c010b0240200141186a28020022032001280210470d00200141106a2003109e86808000200128021821030b200141146a28020020034107746a2203200e360234200320053600302003200436002c200320053600102003200436000c200341003a00092003200b3a0008200341053a0004200341083a0000200320072903f003370238200341c0006a200741f0036a41086a290300370200200341c8006a20074180046a290300370200200341d0006a20074188046a290300370200200341d8006a20074190046a290300370200200341e0006a20074198046a28020036020020012001280218220a41016a3602180b2002200a360204200241003a00000b200728028c01220a410176220320072802880122044b0d1620072802840121024100210e20074188066a41286a2205410036020020074188066a200220036a200220046a10f58d808000200741186a41286a2005280200360200200741186a41206a20074188066a41206a290200370300200741186a41186a20074188066a41186a290200370300200741186a41106a20074188066a41106a290200370300200741186a41086a20074188066a41086a290200370300200720072902880637031820072d00a802210c20072d00ac02210b20072d00ad02210920072f01ae02210f20072802b002210120072802b402210520072f00a902210220072d00ab022103200741c8006a41106a200741a8026a41206a280200360200200741c8006a41086a200741a8026a41186a290300370300200720072903b802370348200a410171210a2002200341107472210220072802d002210d20072802cc0221040c030b2000410236020020002003360204200741a8026a10e585808000200841002802c0a3c68000118080808000000b20072802f4044129490d3020072802cc0441002802c0a3c68000118080808000000c300b20072802840120026a2d0000210e41002d00fca3c680001a41c00441002802c8a3c68000118180808000002203450d14200c410171210c200341023a009c04200341023a00f803200341023a00d403200341023a00b003200341023a008c03200341023a00e802200341023a00c402200341023a00a002200341023a00fc01200341023a00d801200341023a00b401200341023a009001200341023a006c200341023a0048200341023a0024200341023a0000200741b3066a200741c0016a41286a280200360000200741ab066a200741c0016a41206a290300370000200741a3066a200741c0016a41186a2903003700002007419b066a200741c0016a41106a29030037000020074193066a200741c8016a290300370000200720072903c00137008b06200141106a210f02400240200141286a2802002202450d0020012002417f6a360228200141246a22022002280200220241016a22064100200128021c220920062009491b6b360200200141206a28020020024102746a2802002206200141186a220928020022024f0d17200141146a28020020064107746a220241046a200220022d00004108461b10e385808000200241083a00002002200c360234200220083602302002200729008806370001200241096a20074188066a41086a290000370000200241116a20074188066a41106a290000370000200241196a20074188066a41186a290000370000200241216a20074188066a41206a290000370000200241286a200741af066a2900003700002002200729039001370238200241c0006a20074190016a41086a290300370200200241c8006a20074190016a41106a290300370200200241d0006a20074190016a41186a290300370200200241d8006a20074190016a41206a290300370200200241e0006a20074190016a41286a2802003602000c010b0240200141186a220928020022022001280210470d00200f2002109e86808000200928020021020b200141146a28020020024107746a220241083a000020022007290088063700012002200c360234200220083602302002200729039001370238200241096a20074188066a41086a290000370000200241116a20074188066a41106a290000370000200241196a20074188066a41186a290000370000200241216a20074188066a41206a290000370000200241286a200741af066a290000370000200241c0006a20074190016a41086a290300370200200241c8006a20074190016a41106a290300370200200241d0006a20074190016a41186a290300370200200241d8006a20074190016a41206a290300370200200241e0006a20074190016a41286a28020036020020012001280218220641016a3602180b2003200e410f71200e410476200b4101711b41246c6a2208200636020441002102200841003a0000200541204b410174210c0240200728020c220841017420072802102206200a6a220e470d0020074188066a20074184016a200a10da8d808000200741c8006a41106a200741f0016a41106a280200360200200741c8006a41086a200741f0016a41086a290200370300200741186a41086a20074194066a290200370300200741186a41106a2007419c066a290200370300200741306a200741a4066a290200370300200741186a41206a20074188066a41246a290200370300200741c0006a200741b4066a280200360200200720072902f0013703482007200729028c063703182004411076210f20044108762109200728028806210a2004210b200521012005210d200321084100210e0c010b200e410176220b20084f0d182006200d6a2212410176220d20084b0d1620072802082202200b6a2d0000210620074188066a41286a220b410036020020074188066a2002200d6a200220086a10f58d808000200741f0036a41286a200b280200360200200741f0036a41206a20074188066a41206a290200370300200741f0036a41186a20074188066a41186a290200370300200741f0036a41106a20074188066a41106a290200370300200741f0036a41086a20074188066a41086a29020037030020072007290288063703f0032012410171210b0240024020012802282202450d0020012002417f6a360228200141246a22022002280200220241016a22084100200128021c220d2008200d491b6b360200200141206a28020020024102746a2802002208200128021822024f0d19200141146a28020020084107746a220241046a200220022d00004108461b10e3858080002002410b6a41003a0000200241003b0009200220053602102002200436020c2002200c3a0008200241053a0004200241083a00002002200b360234200220053602302002200436022c200220072902f001370218200241206a200741f0016a41086a290200370200200241286a200741f0016a41106a280200360200200220072903f003370238200241c0006a200741f0036a41086a290300370200200241c8006a200741f0036a41106a290300370200200241d0006a20074188046a290300370200200241d8006a200741f0036a41206a290300370200200241e0006a200741f0036a41286a2802003602000c010b024020092802002202200f280200470d00200f2002109e86808000200928020021020b200141146a28020020024107746a220241003b0009200220053602102002200436020c2002200c3a0008200241053a0004200241083a0000200220072902f0013702182002200b360234200220053602302002200436022c200220072903f0033702382002410b6a41003a0000200241206a200741f0016a41086a290200370200200241286a200741f0016a41106a280200360200200241c0006a200741f0036a41086a290300370200200241c8006a200741f0036a41106a290300370200200241d0006a20074188046a290300370200200241d8006a200741f0036a41206a290300370200200241e0006a200741f0036a41286a28020036020020012001280218220841016a3602180b20032006410f712006410476200e4101711b41246c6a220220083602044100210e200241003a000020074188066a20074184016a200a10da8d808000200741206a20074194066a290200370300200741286a2007419c066a290200370300200741306a200741a4066a290200370300200741386a20074188066a41246a290200370300200741c0006a200741b4066a2802003602002007200729028c06370318200728028806210a4103210c200321080b024020072802f4044129490d0020072802cc0441002802c0a3c68000118080808000000b20042103200121040c2d0b2003200141e491c68000109481808000000b410441c00410b280808000000b2008200c418cd5c2800010f980808000000b200d200b41e491c68000109481808000000b200d200241d4d7c2800010f980808000000b200e200c41e491c68000109481808000000b2004200241d4d7c2800010f980808000000b2005200141e491c68000109481808000000b2002200e418cd5c2800010f980808000000b2008200e41e491c68000109481808000000b2008200341d4d7c2800010f980808000000b200f200241e491c68000109481808000000b2002200a41f492c68000109581808000000b2002200a418493c68000109581808000000b2002200a419493c6800010f980808000000b200e200241e491c68000109481808000000b200a200e41e491c68000109481808000000b200a200341d4d7c2800010f980808000000b2003200441e491c68000109481808000000b410441c00410b280808000000b2006200241d4d7c2800010f980808000000b200d200841e491c68000109481808000000b2008200241d4d7c2800010f980808000000b200b2008418cd5c2800010f980808000000b200720053602b006200720043602ac0620072005360290062007200436028c064100210e200741003a0089062007200541204b410174220b3a008806024020072d00f0034103460d00200741f0036a20074188066a10e785808000210e0b2003280208220c410176210802400240024002400240200c4101710d0020082003280204220c4b0d02200741003a00d004200720083602cc04200720032802003602c8040c010b20082003280204220c4b0d022008200c4f0d032007200328020022033602c804200720083602cc04200741d1046a200320086a2d000041f001713a0000200741013a00d0040b20012006200241046a200741c8046a108386808000410021094107210c2004210d200521080c170b2008200c41f492c68000109581808000000b2008200c418493c68000109581808000000b2008200c419493c6800010f980808000000b200728028c0621040b2000410236020020002004360204200728029c044129490d1320072802f40341002802c0a3c68000118080808000000c130b4100210e0b200728029c044129490d1020072802f40341002802c0a3c68000118080808000000c100b419cd5c28000412a41c8d5c2800010f880808000000b200a200728020c41017420072802106b470d0002400240200541204b0d00200720043602cc04200741d0046a21024100210b0c010b200720043602ec04200741003a00c904200741f0046a21024102210b2004210d0b200220053602002007200b3a00c804200741a8026a200741c8046a10e785808000210e2003280208200a6a220a4101762102200328020421052003280200210302400240200a4101710d00200220054b0d05200741003a0090062007200236028c0620072003360288060c010b200220054b0d05200220054f0d0620072003360288062007200236028c0620074191066a200320026a2d000041f001713a0000200741013a0090060b20012006200820074188066a108386808000200741d4046a2102200e0d0120072802f003210a20072802f403210520072802f8032101200728029c0421034100210e20074188066a41286a2208410036020020074188066a2005200f200341284b22061b220520052001200320061b6a10f686808000200741186a41286a2008280200360200200741186a41206a20074188066a41206a290200370300200741186a41186a20074188066a41186a290200370300200741186a41106a20074188066a41106a290200370300200741186a41086a20074188066a41086a29020037030020072007290288063703180c020b200a2002490d07200c410176220a200e4b0d0520072802d402210220074188066a41286a220b410036020020074188066a2002200a6a2002200e6a10f58d808000200741c8046a41286a220a200b280200360200200741c8046a41206a220e20074188066a41206a290200370300200741c8046a41186a220b20074188066a41186a290200370300200741c8046a41106a220d20074188066a41106a290200370300200741c8046a41086a220f20074188066a41086a29020037030020072007290288063703c80441002d00fca3c680001a41c00441002802c8a3c68000118180808000002202450d06200241023a009c04200241023a00f803200241023a00d403200241023a00b003200241023a008c03200241023a00e802200241023a00c402200241023a00a002200241023a00fc01200241023a00d801200241023a00b401200241023a009001200241023a006c200241023a0048200241023a0024200241023a000020074190036a41086a200841086a29020037030020074190036a41106a200841106a29020037030020074190036a41186a200841186a29020037030020074190036a41206a200841206a29020037030020074190036a41286a200841286a2802003602002007200829020037039003200741c4036a20072903c804370200200741cc036a200f290300370200200741d4036a200d290300370200200741dc036a200b290300370200200741e4036a200e290300370200200741ec036a200a2802003602002007200c4101713602c003200720023602bc0320074188066a200120074190036a20032004200520061084868080002007280288064102460d08200741c8006a41086a200741a4066a290200370300200741c8006a41106a200741ac066a280200360200200741c0006a200741e8066a280200360200200741386a200741e0066a290200370300200741306a200741d8066a290200370300200741186a41106a200741d0066a290200370300200741186a41086a200741c8066a2902003703002007200729029c06370348200720072902c006370318200728028c06220c41087621022007280298062105200728029406210420072f019206210f20072d009106210920072d009006210b20072802b006210320072802b406210d20072802b806210820072802bc06210a4100210e0c0c0b20072802f003210a20072802f403210520072802f8032101200728029c04210320074188066a41286a2208410036020020074188066a2005200f200341284b220e1b2205200520012003200e1b6a10f686808000200741186a41286a2008280200360200200741186a41206a20074188066a41206a290200370300200741186a41186a20074188066a41186a290200370300200741186a41106a20074188066a41106a290200370300200741186a41086a20074188066a41086a29020037030020072007290288063703184101210e0b200741c8006a41086a200241086a290200370300200741c8006a41106a200241106a2802003602002007200229020037034820072802d004210520072802f00421084105210c410021090c0a0b2002200541f492c68000109581808000000b20022005418493c68000109581808000000b20022005419493c6800010f980808000000b200a200e41e491c68000109481808000000b410441c00410b280808000000b41002d00fca3c680001a41c00441002802c8a3c68000118180808000002202450d01200241023a0000200241023a009c04200241023a00f803200241023a00d403200241023a00b003200241023a008c03200241023a00e802200241023a00c402200241023a00a002200241023a00fc01200241023a00d801200241023a00b401200241023a009001200241023a006c200241023a0048200241023a0024024020072802dc02200a6a220c410176220d20072802d802220e490d00200d200e418cd5c2800010f980808000000b200c41016a2209410176220f200e4b0d0220072802d402220b200d6a2d0000210d20074188066a41286a2212410036020020074188066a200b200f6a200b200e6a10f58d808000200741c8046a41286a2012280200360200200741c8046a41206a20074188066a41206a290200370300200741c8046a41186a20074188066a41186a290200370300200741c8046a41106a20074188066a41106a290200370300200741c8046a41086a20074188066a41086a29020037030020072007290288063703c804200741ec026a200841086a290000370000200741f4026a200841106a290000370000200741fc026a200841186a29000037000020074184036a200841206a2900003700002007418c036a200841286a280000360000200720082900003700e4022009410171210b02400240200141286a2802002208450d0020012008417f6a360228200141246a22082008280200220841016a220e41002001411c6a280200220f200e200f491b6b360200200141206a28020020084102746a280200220e200141186a28020022084f0d05200141146a280200200e4107746a220841046a200820082d00004108461b10e385808000200841053a0004200841083a00002008200b360234200820072900e1023700052008410d6a200741e1026a41086a290000370000200841156a200741e1026a41106a2900003700002008411d6a200741e1026a41186a290000370000200841256a200741e1026a41206a2900003700002008412c6a20074188036a290000370000200820072903c804370238200841c0006a200741c8046a41086a290300370200200841c8006a200741c8046a41106a290300370200200841d0006a200741c8046a41186a290300370200200841d8006a200741c8046a41206a290300370200200841e0006a200741f0046a2802003602000c010b0240200141186a28020022082001280210470d00200141106a2008109e86808000200128021821080b200141146a28020020084107746a220841053a0004200841083a0000200820072900e1023700052008200b360234200820072903c8043702382008410d6a200741e1026a41086a290000370000200841156a200741e1026a41106a2900003700002008411d6a200741e1026a41186a290000370000200841256a200741e1026a41206a2900003700002008412c6a20074188036a290000370000200841c0006a200741c8046a41086a290300370200200841c8006a200741c8046a41106a290300370200200841d0006a200741c8046a41186a290300370200200841d8006a200741c8046a41206a290300370200200841e0006a200741f0046a28020036020020012001280218220e41016a3602180b2002200d410f71200d410476200c4101711b41246c6a2208200e3602044100210e200841003a0000200741f8046a200741086a200a10da8d808000200720023602f404200741033a00c80420074188066a2001200741c8046a20032004200520061084868080002007280288064102460d00200741c8006a41086a200741a4066a290200370300200741c8006a41106a20074188066a41246a280200360200200741c0006a200741e8066a280200360200200741386a200741e0066a290200370300200741306a200741d8066a290200370300200741186a41106a200741d0066a290200370300200741186a41086a200741c8066a2902003703002007200729029c06370348200720072902c006370318200728028c06220c41087621022007280298062105200728029406210420072f019206210f20072d009106210920072d009006210b20072802b006210320072802b406210d20072802b806210820072802bc06210a0c040b200728028c0621022000410236020020002002360204200728029c044129490d0520072802f40341002802c0a3c68000118080808000000c050b410441c00410b280808000000b200f200e41e491c68000109481808000000b200e200841d4d7c2800010f980808000000b200728029c044129490d0020072802f40341002802c0a3c68000118080808000000b200020023b0005200020053602102000200436020c2000200f3b010a200020093a00092000200b3a00082000200c3a00042000200e360200200020072903483702142000200a360234200020083602302000200d36022c20002003360228200041076a20024110763a00002000411c6a200741c8006a41086a290300370200200041246a200741c8006a41106a280200360200200041e0006a200741c0006a280200360200200041d8006a200741386a290300370200200041d0006a200741306a290300370200200041c8006a200741186a41106a290300370200200041c0006a200741186a41086a290300370200200020072903183702380b200741f0066a2480808080000bb11503057f017e157f23808080800041b0036b22072480808080002007200536020420072004360200024002400240024002400240024002400240024002400240024020022d00000d00200228020421020c010b200741206a200241196a290000370300200741186a200241116a290000370300200741106a200241096a290000370300200720022900013703082003280208220541017621020240024020054101710d002002200328020422054b0d05200741003a00ec01200720023602e801200720032802003602e4010c010b2002200328020422054b0d05200220054f0d062007200328020022053602e401200720023602e801200741ed016a200520026a2d000041f001713a0000200741013a00ec010b200741cc026a2001200741086a200741e4016a10868680800020072802d002210220072802cc020d010b0240200141286a28020022052001411c6a22082802002204470d00200810a889808000200128021c2104200128022821050b200141206a280200200141246a28020020056a22054100200420052004491b6b4102746a20023602002001200128022841016a360228200141186a280200220420024d0d05200141146a28020020024107746a22022d00002104200741296a200241016a41df0010848e8080001a200241043a0004200241083a0000200228026021092007280200210520072802042108024020044108470d00200741e4016a2007412c6a41dc0010848e8080001a200720033602c802200720093602c002200720013602c402200741cc026a2001200741e4016a200320052008200610848680800020072802cc0222034102460d022003410147210a20072802ac03210b20072902a403210c20072802a0032104200728029c03210520072802980321062007280294032108200728029003210d200728028c03210e200728028803210f2007280284032110200728028003211120072802fc02211220072802f802211320072802f402211420072802f002211520072802ec02211620072802e802211720072802e402211820072802e002211920072802dc02211a20072802d802211b20072802d402211c20072802d0022102410821034100211d0c090b2003280208211d2003280204210b2003280200210a200741e4016a410172200741296a41df0010848e8080001a200741e0016a200241fc006a280000360200200741d8016a200241f4006a290000370300200741c8016a41086a200241ec006a290000370300200720013602c402200720033602c802200720043a00e401200720022900643703c801200741cc026a2001200741e4016a200320052008200610848680800020072802cc0222034102460d0120072902a803210c20072802a403211e20072802a0032104200728029c03210520072802980321062007280294032108200728029003210d200728028c03210e200728028803210f2007280284032110200728028003211120072802fc02211220072802f802211320072802f402211420072802f002211520072802ec02211620072802e802211720072802e402211820072802e002211a20072802dc02211b20072802d802211c20072802d402211f20072802d0022102024020034101460d00201d410176210302400240201d4101710d0002402003200b4b0d00410021190c020b2003200b41f492c68000109581808000000b2003200b4b0d082003200b4f0d09200a20036a2d00004170712120410121190b200c422088a7210b4100211d200741cc026a41286a22214100360200200741cc026a200a200a20036a10f58d80800020074184026a220a41286a2021280200360200200a41206a200741cc026a41206a290200370200200a41186a200741cc026a41186a290200370200200a41106a200741cc026a41106a29020037020041082103200a41086a200741cc026a41086a290200370200200a20072902cc02370200200741b1026a20203a0000200741b0026a20193a0000200741f0016a200741c8016a41086a290300370200200741f8016a200741c8016a41106a29030037020020074180026a200741c8016a41186a280200360200200720093602e401200720072903c8013702e801200141dc006a200741e4016a10958d8080001a200c422086201ead84210c4101210a201a2119201b211a201c211b201f211c0c090b200741a8016a41186a200741c8016a41186a280200360200200741a8016a41106a200741c8016a41106a290300370300200741a8016a41086a200741c8016a41086a290300370300200720072903c8013703a801200241ff01712103200241807e71211d4100210a201821192017211820162117201521162014211520132114201221132011211220102111200f2110200e210f200d210e2008210d200621082005210620042105201e21042009210b201f21020c080b200041023a000420002002360200200420042802002201417f6a36020020014101470d08200710e28a8080000c080b20072802d0022101200041023a0004200020013602000c070b2002200541f492c68000109581808000000b20022005418493c68000109581808000000b20022005419493c6800010f980808000000b2002200441e4d7c2800010f980808000000b2003200b418493c68000109581808000000b2003200b419493c6800010f980808000000b20074188016a41186a200741a8016a41186a28020036020020074188016a41106a200741a8016a41106a29030037030020074188016a41086a200741a8016a41086a290300370300200720072903a801370388012003201d7221090240024020012802282203450d0020012003417f6a36022820012001280224220341016a221d4100200128021c221e201d201e491b6b360224200128022020034102746a28020022032001280218221d4f0d03200128021420034107746a220141046a200120012d00004108461b10e3858080002001200b3602602001200c37025820012004360254200120053602502001200636024c200120083602482001200d3602442001200e3602402001200f36023c2001201036023820012011360234200120123602302001201336022c2001201436022820012015360224200120163602202001201736021c20012018360218200120193602142001201a3602102001201b36020c2001201c3602082001200236020420012009360200200141fc006a200741a0016a280200360200200141f4006a20074198016a290300370200200141ec006a20074188016a41086a29030037020020012007290388013702640c010b0240200128021822032001280210470d00200141106a2003109e86808000200128021821030b200128021420034107746a2203200b3602602003200c37025820032004360254200320053602502003200636024c200320083602482003200d3602442003200e3602402003200f36023c2003201036023820032011360234200320123602302003201336022c2003201436022820032015360224200320163602202003201736021c20032018360218200320193602142003201a3602102003201b36020c2003201c3602082003200236020420032009360200200341fc006a200741a0016a280200360200200341f4006a20074198016a290300370200200341ec006a20074190016a290300370200200320072903880137026420012001280218220341016a3602180b2000200a3a0004200020033602000b200741b0036a2480808080000f0b2003201d41d4d7c2800010f980808000000bc90a01057f2380808080004180026b2204248080808000024002400240024002400240024002400240024020012802682205450d0020052002200141ec006a2802002802181183808080000022050d010b200441e0006a200128025020022003200141d4006a280200280214118680808000000240024020042802602203418080808078470d0020044180016a41186a2205200241186a29000037030020044180016a41106a2203200241106a29000037030020044180016a41086a2206200241086a290000370300200420022900003703800141002d00fca3c680001a413041002802c8a3c680001181808080000022010d014104413010b280808000000b20042802682106200428026421052001280200450d032001280204450d0241d8d5c28000108781808000000b200141858080807836020020012004290380013702042001410c6a2006290300370200200141146a20032903003702002001411c6a200529030037020020004101360200200020013602040c070b02402001280200450d0002402001280204450d0041e8d5c28000108781808000000b2001417f360204200141086a28020021032001410c6a2802002106200441a0016a200241186a29000037020020044180016a41186a200241106a29000037020020044180016a41106a200241086a29000037020020042005360284012004418180808078360280012004200229000037028801200320044180016a200628020c11848080800000200141003602040b20042005200141106a10f2858080000c020b2001417f360204200141086a28020021072001410c6a280200210820044180016a410c6a200636020020044180016a41086a2005360200200441a8016a200241186a290000370200200441a0016a200241106a29000037020020044180016a41186a200241086a2900003702002004428280808088808080807f370280012004200229000037029001200720044180016a200828020c11848080800000200141003602040b200441e0006a41186a200241186a290000370300200441e0006a41106a200241106a290000370300200441e0006a41086a200241086a2900003703002004200229000037036020044180016a200441e0006a20052006200141106a10e88580800020042d00800122064108460d01200420042d0083013a0003200420042f0081013b00012004280284012107200441086a20044180016a41086a41d80010848e8080001a20042007360204200420063a00002003450d00200541002802c0a3c68000118080808000000b20044180016a200441e00010848e8080001a200441f8016a200241186a290000370200200441f0016a200241106a290000370200200441e8016a200241086a290000370200200420022900003702e001200141286a2802002202450d0120012002417f6a360228200141246a22022002280200220241016a220541002001411c6a280200220320052003491b6b360200200141206a28020020024102746a2802002202200141186a28020022054f0d04200141146a28020020024107746a220141046a200120012d00004108461b10e385808000200120044180016a41800110848e8080001a0c020b200428028401210120004101360200200020013602042003450d02200541002802c0a3c68000118080808000000c020b0240200141186a28020022022001280210470d00200141106a2002109e86808000200128021821020b200141146a28020020024107746a20044180016a41800110848e8080001a20012001280218220241016a3602180b20004100360200200020023602040b20044180026a2480808080000f0b2002200541d4d7c2800010f980808000000bc90a01057f2380808080004180026b2204248080808000024002400240024002400240024002400240024020012802682205450d0020052002200141ec006a2802002802181183808080000022050d010b200441e0006a200128025020022003200141d4006a280200280214118680808000000240024020042802602203418080808078470d0020044180016a41186a2205200241186a29000037030020044180016a41106a2203200241106a29000037030020044180016a41086a2206200241086a290000370300200420022900003703800141002d00fca3c680001a413041002802c8a3c680001181808080000022010d014104413010b280808000000b20042802682106200428026421052001280200450d032001280204450d0241d8d5c28000108781808000000b200141858080807836020020012004290380013702042001410c6a2006290300370200200141146a20032903003702002001411c6a200529030037020020004101360200200020013602040c070b02402001280200450d0002402001280204450d0041e8d5c28000108781808000000b2001417f360204200141086a28020021032001410c6a2802002106200441a0016a200241186a29000037020020044180016a41186a200241106a29000037020020044180016a41106a200241086a29000037020020042005360284012004418180808078360280012004200229000037028801200320044180016a200628020c11848080800000200141003602040b20042005200141106a10f0858080000c020b2001417f360204200141086a28020021072001410c6a280200210820044180016a410c6a200636020020044180016a41086a2005360200200441a8016a200241186a290000370200200441a0016a200241106a29000037020020044180016a41186a200241086a2900003702002004428280808088808080807f370280012004200229000037029001200720044180016a200828020c11848080800000200141003602040b200441e0006a41186a200241186a290000370300200441e0006a41106a200241106a290000370300200441e0006a41086a200241086a2900003703002004200229000037036020044180016a200441e0006a20052006200141106a10ec8580800020042d00800122064108460d01200420042d0083013a0003200420042f0081013b00012004280284012107200441086a20044180016a41086a41d80010848e8080001a20042007360204200420063a00002003450d00200541002802c0a3c68000118080808000000b20044180016a200441e00010848e8080001a200441f8016a200241186a290000370200200441f0016a200241106a290000370200200441e8016a200241086a290000370200200420022900003702e001200141286a2802002202450d0120012002417f6a360228200141246a22022002280200220241016a220541002001411c6a280200220320052003491b6b360200200141206a28020020024102746a2802002202200141186a28020022054f0d04200141146a28020020024107746a220141046a200120012d00004108461b10e385808000200120044180016a41800110848e8080001a0c020b200428028401210120004101360200200020013602042003450d02200541002802c0a3c68000118080808000000c020b0240200141186a28020022022001280210470d00200141106a2002109e86808000200128021821020b200141146a28020020024107746a20044180016a41800110848e8080001a20012001280218220241016a3602180b20004100360200200020023602040b20044180026a2480808080000f0b2002200541d4d7c2800010f980808000000b99950105107f017e017f047e397f23808080800041c0086b2205248080808000024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024020022d00002206417c6a41ff01712207410420074104491b417e6a0e03020108000b2000200241e00010848e8080001a0c200b200241056a210820022d000421092002280230220a2d00004102460d0141002106410121070c020b200541c8036a41286a200241d0006a290200370300200541e8036a200241c8006a290200370300200541c8036a41186a200241c0006a290200370300200541c8036a41106a200241386a290200370300200541c8036a41086a2207200241306a290200370300200520022902283703c803200541fe036a200241076a2d00003a000020054198016a41086a200241146a29020037030020054198016a41106a2002411c6a29020037030020054198016a41186a200241246a2802003602002005200241056a2f00003b01fc0320052002410c6a29020037039801200241086a280200210b20022d000421062003280200210a2003280204210820032802082109024002400240024002400240024002400240024020040d002007280200200541f4036a2802002204200441284b1b2207417f6a210c2007450d11200741017420096a20052802c803417f736a2209410176210720052802cc03200541c8036a410472200441284b1b200c6a2d0000410f71210c024020094101710d00200720084d0d022007200841f492c68000109581808000000b200720084b0d1220072008490d0220072008419493c6800010f980808000000b2009410176210702400240200941017122040d00200720084d0d012007200841f492c68000109581808000000b200720084b0d13200720084f0d14200a20076a2d000041707121090b2005420037028002200541003602d804200520073602d4042005200a3602d004200541d8016a200541d0046a4100200510e38d8080001a2004450d06200941f00171210820052d0084024101710d04200541d8016a41046a2104200541d8016a41044128200528028002220d41284b22091b6a280200220c200d412820091b460d022004200541d8016a41286a20091b210420052802d801200541d8016a20091b21090c030b410021042005410036028004200c410474210e20054180046a41046a210f410121080c070b200a20076a22042d0000210941002108200541d8016a41286a220d4100360200200541d8016a200a200410f686808000200541d0046a41286a2210200d280200220d360200200541d0046a41206a2211200541d8016a41206a290200370300200541d0046a41186a2212200541d8016a41186a290200370300200541d0046a41106a2213200541d8016a41106a290200370300200541d0046a41086a2214200541d8016a41086a290200370300200520052902d80122153703d004200541d0046a41047221042009417071200c72211602400240200541d0046a41044128200d41284b22091b6a280200220c200d412820091b460d002004201020091b21042015a7200541d0046a20091b21090c010b200541d0046a10f88680800020052802d404210c20052802d00421090b2009200c6a20163a00002004200428020041016a360200200541b0066a41086a20142903002215370300200541b0066a41106a20132903002217370300200541b0066a41186a20122903002218370300200541b0066a41206a20112903002219370300200541b0066a41286a20102802002204360200200520052903d004221a3703b0062005418c046a201537020020054194046a20173702002005419c046a2018370200200541a4046a2019370200200541ac046a200436020020054101360280042005201a3702840420054180046a41046a210f0c050b200541d8016a10f78d80800020052802dc01210c20052802d80121090b2009200c6a20083a00002004200428020041016a3602000c010b20052802dc012005280280022204200441284b22041b2209450d0f200920052802d801200541d8016a20041b6a417f6a220420042d00002008410476723a00000b200520052802840241016a360284020b200520052802c8033602d8042005200541d0036a280200200541f4036a2802002204200441284b22041b3602d404200520052802cc03200541c8036a41047220041b3602d004200541d8016a200541d0046a4100200510e38d8080001a200528028402220841017621040240024020084101710d0020052802dc012005280280022209200941284b22091b220c2004490d1020052802d801200541d8016a20091b21090c010b20052802dc012005280280022209200941284b1b220c2004490d10200c20044d0d1120052802d801200541d8016a200941284b1b220920046a2d0000417071210e0b200541d0046a41286a220c4100360200200541d0046a2009200920046a10f686808000200541b0066a41286a2204200c280200360200200541b0066a41206a2209200541d0046a41206a290200370300200541b0066a41186a220c200541d0046a41186a290200370300200541b0066a41106a220d200541d0046a41106a290200370300200541b0066a41086a2210200541d0046a41086a290200370300200520052902d0043703b00602402005280280024129490d0020052802d80141002802c0a3c68000118080808000000b2005418c046a201029030037020020054194046a200d2903003702002005419c046a200c290300370200200541a4046a2009290300370200200541ac046a20042802003602002005410136028004200520052903b0063702840420054184046a210f0b20052802880420052802ac042204200441284b22041b2109200528028404200f20041b21040b2009200720041b211b2008410171211c2004200a20041b211d024020064101710d000240200141286a28020022042001411c6a220a2802002207470d00200a10a889808000200128021c2107200128022821040b200141206a280200200141246a28020020046a22044100200720042007491b6b4102746a200b3602002001200128022841016a360228200141186a2802002207200b4b0d1c200b200741e4d7c2800010f980808000000b200541b0046a41026a200541fc036a41026a2d00003a0000200541bf046a20054198016a41086a290300370000200541c7046a200541a8016a290300370000200541cf046a200541b0016a2d00003a0000200520052f01fc033b01b0042005200b3600b30420052005290398013700b7042005200e3a00e1012005201c3a00e0012005201b3602dc012005201d3602d801200541d0046a2001200541b0046a200541d8016a10878680800020052802d404210b20052802d004450d1a200041083a00002000200b3602040240200528028004450d00200f2802284129490d0020052802840441002802c0a3c68000118080808000000b200541f4036a2802004129490d0620052802cc0341002802c0a3c68000118080808000000c060b4101210641022107200a2d00244102470d004102210641032107200a2d00484102470d000240200a2d006c4102460d0041032106410421070c010b0240200a2d0090014102460d0041042106410521070c010b0240200a2d00b4014102460d0041052106410621070c010b0240200a2d00d8014102460d0041062106410721070c010b0240200a2d00fc014102460d0041072106410821070c010b0240200a2d00a0024102460d0041082106410921070c010b0240200a2d00c4024102460d0041092106410a21070c010b0240200a2d00e8024102460d00410a2106410b21070c010b0240200a2d008c034102460d00410b2106410c21070c010b0240200a2d00b0034102460d00410c2106410d21070c010b0240200a2d00d4034102460d00410d2106410e21070c010b0240200a2d00f8034102460d00410e2106410f21070c010b200a2d009c044102460d01410f2106411021070b200741246c21070340200741246a220441e404460d02200a20076a210b20042107200b2d00004102460d000c180b0b200941ff01714103460d15200020082900003700052000412c6a200841276a280000360000200041256a200841206a2900003700002000411d6a200841186a290000370000200041156a200841106a2900003700002000410d6a200841086a290000370000200542003702dc01200541a0d1c280003602d801200041306a200541d8016a10d98d808000200020093a0004200041053a00000c010b200941ff01714103470d15200520063a00d004200541d8016a41286a22074100360200200541d8016a200541d0046a200541d0046a41016a10f58d808000200541086a41286a220b2007280200360200200541086a41206a2208200541d8016a41206a290200370300200541086a41186a2209200541d8016a41186a290200370300200541086a41106a220c200541d8016a41106a290200370300200541086a41086a220d200541d8016a41086a290200370300200520052902d801370308200a200641246c6a22072d00002104200741023a000020044102460d0b2005413d6a2007290001370000200541e4006a2005290308370200200541386a41246a200741206a280000360000200541d5006a200741196a290000370000200541cd006a200741116a290000370000200541c5006a200741096a290000370000200541ec006a200d290300370200200541f4006a200c290300370200200541fc006a200929030037020020054184016a20082903003702002005418c016a200b28020036020020054101360260200541063a0038200520043a003c20002001200541386a200341001088868080000b200a41002802c0a3c68000118080808000000c170b200541a0016a200241c4006a290200370300200541a8016a200241cc006a290200370300200541b0016a200241d4006a29020037030020052002413c6a2902003703980141012107200241016a2108200241306a2109200241dc006a2802002110200241386a2802002111200241346a2802002113200228023021124100210c024002400240200228022c220a2d0000220d4102470d004101210c41022107200a2d0024220d4102470d004102210c41032107200a2d0048220d4102470d000240200a2d006c220d4102460d004103210c410421070c010b0240200a2d009001220d4102460d004104210c410521070c010b0240200a2d00b401220d4102460d004105210c410621070c010b0240200a2d00d801220d4102460d004106210c410721070c010b0240200a2d00fc01220d4102460d004107210c410821070c010b0240200a2d00a002220d4102460d004108210c410921070c010b0240200a2d00c402220d4102460d004109210c410a21070c010b0240200a2d00e802220d4102460d00410a210c410b21070c010b0240200a2d008c03220d4102460d00410b210c410c21070c010b0240200a2d00b003220d4102460d00410c210c410d21070c010b0240200a2d00d403220d4102460d00410d210c410e21070c010b0240200a2d00f803220d4102460d00410e210c410f21070c010b200a2d009c04220d4102460d01410f210c411021070b200741246c21070340200741246a220441e404460d02200a20076a210b20042107200b2d00004102460d000c140b0b200641ff01714103460d1120002008290000370005200020092902003702302000412c6a200841276a280000360000200041256a200841206a2900003700002000411d6a200841186a290000370000200041156a200841106a2900003700002000410d6a200841086a290000370000200041386a200941086a290200370200200041c0006a200941106a290200370200200041c8006a200941186a290200370200200041d0006a200941206a290200370200200041d8006a200941286a290200370200200020063a0004200041053a00000c100b200641ff01714103470d11200a200c41246c6a220741023a0000200d4102460d0a2007280204210b200541bc086a41026a200741036a2d00003a0000200541c0016a200741106a290200370300200541b8016a41106a200741186a290200370300200541b8016a41186a200741206a280200360200200520072f00013b01bc08200520072902083703b801200328020820112010201041284b1b41017420126b6a220841017621072003280204210420032802002103024002400240024020084101710d00200720044d0d012007200441f492c68000109581808000000b200720044b0d0e20072004490d0120072004419493c6800010f980808000000b410021042005410036028004200c4104742109410121060c010b200320076a22042d0000210841002106200541d8016a41286a22094100360200200541d8016a2003200410f686808000200541d0046a41286a221420092802002209360200200541d0046a41206a221e200541d8016a41206a290200370300200541d0046a41186a221f200541d8016a41186a290200370300200541d0046a41106a2220200541d8016a41106a290200370300200541d0046a41086a2221200541d8016a41086a290200370300200520052902d80122153703d004200541d0046a41047221042008417071200c72212202400240200541d0046a41044128200941284b22081b6a28020022162009412820081b460d002004201420081b21042015a7200541d0046a20081b21080c010b200541d0046a10f88680800020052802d404211620052802d00421080b200820166a20223a00002004200428020041016a360200200541ac046a20142802002208360200200541a4046a201e2903003702002005419c046a201f29030037020020054194046a20202903003702002005418c046a2021290300370200200520052903d00422153702840420054101360280042015a720054180046a41046a200841284b22091b2104200528028804200820091b21080b2008200720041b21082004200320041b21070240200d4101710d000240200141286a28020022032001411c6a220d2802002204470d00200d10a889808000200128021c2104200128022821030b200141206a280200200141246a28020020036a22034100200420032004491b6b4102746a200b3602002001200128022841016a360228200141186a2802002204200b4d0d0d200541d8016a200141146a280200200b4107746a220441800110848e8080001a200441043a0004200441083a00000c0f0b200541d8026a41026a200541bc086a41026a2d00003a0000200541e7026a200541b8016a41086a290300370000200541ef026a200541b8016a41106a290300370000200541f7026a200541d0016a2d00003a0000200520052f01bc083b01d8022005200b3600db02200520052903b8013700df02200520093a00d904200520063a00d804200520083602d404200520073602d004200541b0066a2001200541d8026a200541d0046a10878680800020052802b406210420052802b006450d0d200041083a0000200020043602040240200528028004450d0020052802ac044129490d0020052802840441002802c0a3c68000118080808000000b200a41002802c0a3c680001180808080000020104129490d00201341002802c0a3c68000118080808000000b20022d0000417c6a41ff01712207410420074104491b417e6a4103490d170c160b200c200741f8d5c2800010f980808000000b20072008418493c68000109581808000000b20072008418493c68000109581808000000b20072008419493c6800010f980808000000b419c94c68000413a41d894c6800010a181808000000b2004200c41ac95c68000109581808000000b2004200c41bc95c68000109581808000000b2004200c41cc95c6800010f980808000000b41c8d6c28000412441ecd6c2800010a181808000000b41c8d6c280004124418cd7c2800010a181808000000b20072004418493c68000109581808000000b200b200441e4d7c2800010f980808000000b200541d8016a200141106a20041089868080000b0240024020052d00d80122044108470d00200541f8026a41086a200541e5016a290000370300200541f8026a41106a200541ed016a290000370300200541f8026a41186a200541f5016a290000370300200541f8026a41206a200541fd016a2900003703002005419f036a20054184026a280000360000200541b8056a41086a20054194026a290200370300200541b8056a41106a2005419c026a290200370300200541b8056a41186a200541a4026a290200370300200541b8056a41206a200541ac026a290200370300200541e0056a200541b4026a2902003703002005200541dd016a2900003703f80220052005418c026a2902003703b80520054188026a280200210b20052d00dc0121040c010b200541b0076a41086a220d200541e1016a290000370300200541b0076a41106a2214200541e9016a290000370300200541b0076a41186a2216200541f1016a290000370300200541b0076a41206a221e200541f9016a290000370300200541b0076a41276a221f200541d8016a41286a280000360000200541b0066a41086a2220200541d8016a41386a290200370300200541b0066a41106a2221200541d8016a41c0006a290200370300200541b0066a41186a2222200541d8016a41c8006a290200370300200541b0066a41206a2223200541a8026a290200370300200541b0066a41286a2224200541b0026a290200370300200520052900d9013703b00720052005290288023703b006200528028402210b200541a8036a41186a2225200541d0026a290200370300200541a8036a41106a2226200541c8026a290200370300200541a8036a41086a2227200541c0026a290200370300200520052902b8023703a803200541b8056a41286a22034100360200200541b8056a2007200720086a10f686808000200541c8036a41286a22072003280200360200200541c8036a41206a2208200541b8056a41206a220f290200370300200541c8036a41186a2228200541b8056a41186a2229290200370300200541c8036a41106a222a200541b8056a41106a222b290200370300200541c8036a41086a222c200541b8056a41086a222d290200370300200541d0046a41086a2027290300370300200541d0046a41106a2026290300370300200541d0046a41186a2025290300370300200520052902b8053703c803200520052903a8033703d004200541d0046a41c8006a2007280200360200200541d0046a41c0006a2008290300370300200541d0046a41386a202829030037030020054180056a202a290300370300200541d0046a41286a202c290300370300200520052903c8033703f0042005419d056a20093a00002005419c056a20063a0000200141dc006a200541d0046a10958d8080001a200541f8026a41086a200d290300370300200541f8026a41106a2014290300370300200541f8026a41186a2016290300370300200541f8026a41206a201e290300370300200541f8026a41276a201f280000360000202d2020290300370300202b202129030037030020292022290300370300200f202329030037030020032024290300370300200520052903b0073703f802200520052903b0063703b8050b02400240024002402004417c6a41ff01712207410420074104491b417f6a0e0401000002000b419cd7c28000412841c4d7c2800010f880808000000b200541b0066a41286a200541b8056a41286a290300370300200541b0066a41206a200541b8056a41206a290300370300200541b0066a41186a200541b8056a41186a290300370300200541b0066a41106a200541b8056a41106a290300370300200541b0066a41086a2204200541b8056a41086a290300370300200520052903b8053703b006200541d0046a41086a22012011360200200541e4046a20054198016a41086a290300370200200541ec046a20054198016a41106a290300370200200541f4046a20054198016a41186a290300370200200520133602d404200520123602d00420052005290398013702dc04200520103602fc04200541c8036a41086a22074101360200200541013602c8032005200c3a00b0072005200541b0076a3602cc03200541d0046a200541c8036a10e78d80800020072004280200200541dc066a22042802002203200341284b22031b360200200520052802b406200541b0066a41047220031b3602cc03200520052802b0063602c803200541d0046a200541c8036a10e78d808000200041d8006a200541d0046a41286a290200370200200041d0006a200541d0046a41206a290200370200200041c8006a200541d0046a41186a290200370200200041c0006a200541d0046a41106a290200370200200041386a2001290200370200200020052902d004370230200541c8036a410b6a200541f8026a410b6a290000370000200541c8036a41136a200541f8026a41136a290000370000200541c8036a411b6a200541f8026a411b6a290000370000200541c8036a41236a200541f8026a41236a290000370000200520052900fb023700cb03200041053a00002000200b36022c200020052900c803370001200041096a2007290000370000200041116a200541c8036a41106a290000370000200041196a200541c8036a41186a290000370000200041216a200541c8036a41206a290000370000200041286a200541ef036a28000036000020042802004129490d0120052802b40641002802c0a3c68000118080808000000c010b200020043a0000200020052903f802370001200541b0066a41286a200541b8056a41286a290300370300200541b0066a41206a200541b8056a41206a290300370300200541b0066a41186a200541b8056a41186a290300370300200541b0066a41106a200541b8056a41106a290300370300200541b0066a41086a2207200541b8056a41086a290300370300200041096a200541f8026a41086a290300370000200041116a200541f8026a41106a290300370000200041196a200541f8026a41186a290300370000200041216a200541f8026a41206a290300370000200041286a2005419f036a280000360000200520052903b8053703b006200541d0046a41086a22042011360200200541e4046a20054198016a41086a290300370200200541ec046a20054198016a41106a290300370200200541f4046a20054198016a41186a290300370200200520133602d404200520123602d00420052005290398013702dc04200520103602fc04200541c8036a41086a22014101360200200541013602c8032005200c3a00b0072005200541b0076a3602cc03200541d0046a200541c8036a10e78d80800020012007280200200541dc066a22072802002203200341284b22031b360200200520052802b406200541b0066a41047220031b3602cc03200520052802b0063602c803200541d0046a200541c8036a10e78d808000200041d8006a200541d0046a41286a290200370200200041d0006a200541d0046a41206a290200370200200041c8006a200541d0046a41186a290200370200200041c0006a200541d0046a41106a290200370200200041386a2004290200370200200020052902d0043702302000200b36022c20072802004129490d0020052802b40641002802c0a3c68000118080808000000b200528028004450d0020052802ac044129490d0020052802840441002802c0a3c68000118080808000000b200a41002802c0a3c68000118080808000000c060b4188d6c28000412f41fcd6c2800010f880808000000b2000200929020037023020002008290000370001200041d8006a200941286a290200370200200041d0006a200941206a290200370200200041c8006a200941186a290200370200200041c0006a200941106a290200370200200041386a200941086a290200370200200041096a200841086a290000370000200041116a200841106a290000370000200041196a200841186a290000370000200041216a200841206a290000370000200041286a200841276a2800003600002000200a36022c200020063a00000c040b4188d6c28000412f41b8d6c2800010f880808000000b2000200a360230200020093a0004200041073a0000200020082900003700052000412c6a200841276a280000360000200041256a200841206a2900003700002000411d6a200841186a290000370000200041156a200841106a2900003700002000410d6a200841086a2900003700000c020b0240200141286a28020022042001411c6a220a2802002207470d00200a10a889808000200128021c2107200128022821040b200141206a280200200141246a28020020046a22044100200720042007491b6b4102746a200b3602002001200128022841016a360228200141186a2802002207200b4b0d00200b200741e4d7c2800010f980808000000b200141146a280200200b4107746a22072d0060212e20072d005c210b20072d0058210a20072d0054210920072d0050210620072d004c210c20072d0048211020072d0044211120072d0040211220072d003c211420072d0038211620072d0034211e20072d0030212020072d002c212120072d0028212220072d0024212420072d0020212520072d001c212620072d0018212f20072d0014212320072d0010211f20072d000c211320072d0008210d20072d0004210820072d00002104200741043a0004200741083a0000200741e1006a2130200741dd006a2131200741d9006a2132200741d5006a2133200741d1006a2134200741cd006a2135200741c9006a2136200741c5006a2137200741c1006a21382007413d6a2139200741396a213a200741356a213b200741316a213c2007412d6a213d200741296a213e200741256a213f200741216a21402007411d6a212d200741196a212c200741156a212b200741116a212a2007410d6a2129200741096a2128200741056a21270240024020044108460d00200541b8056a41026a200741036a2d00003a0000200541b0076a41026a202741026a2d00003a0000200541b8016a41026a202841026a2d00003a0000200541bc086a41026a202941026a2d00003a0000200541b8086a41026a202a41026a2d00003a0000200520072f00013b01b805200520272f00003b01b007200520282f00003b01b801200520292f00003b01bc082005202a2f00003b01b808200541b4086a41026a202b41026a2d00003a0000200541b0086a41026a202c41026a2d00003a0000200541ac086a41026a202d41026a2d00003a0000200541a8086a41026a204041026a2d00003a0000200541a4086a41026a203f41026a2d00003a00002005202b2f00003b01b4082005202c2f00003b01b0082005202d2f00003b01ac08200520402f00003b01a8082005203f2f00003b01a408200541a0086a41026a203e41026a2d00003a00002005203e2f00003b01a0082005419c086a41026a203d41026a2d00003a00002005203d2f00003b019c0820054198086a41026a203c41026a2d00003a00002005203c2f00003b01980820054194086a41026a203b41026a2d00003a00002005203b2f00003b01940820054190086a41026a203a41026a2d00003a00002005203a2f00003b0190082005418c086a41026a203941026a2d00003a0000200520392f00003b018c0820054188086a41026a203841026a2d00003a0000200520382f00003b01880820054184086a41026a203741026a2d00003a0000200520372f00003b01840820054180086a41026a203641026a2d00003a0000200520362f00003b018008200541fc076a41026a203541026a2d00003a0000200520352f00003b01fc07200541f8076a41026a203441026a2d00003a0000200520342f00003b01f807200541f4076a41026a203341026a2d00003a0000200520332f00003b01f407200541f0076a41026a203241026a2d00003a0000200520322f00003b01f007200541ec076a41026a203141026a2d00003a0000200520312f00003b01ec07200541b0066a41026a203041026a2d00003a0000200520302f00003b01b006200541f0016a200741fc006a280000360200200541e8016a200741f4006a290000370300200541d8016a41086a200741ec006a290000370300200520072900643703d801200a2128200421272009210a20062109200c21062010210c20112110201221112014211220162114201e21162020211e2021212020222121202421222025212420262125202f21260c010b200541b8056a41026a202741026a2d00003a0000200541b0076a41026a202841026a2d00003a0000200541b8016a41026a202941026a2d00003a0000200541bc086a41026a202a41026a2d00003a0000200541b8086a41026a202b41026a2d00003a0000200520272f00003b01b805200520282f00003b01b007200520292f00003b01b8012005202a2f00003b01bc082005202b2f00003b01b808200541b4086a41026a202c41026a2d00003a0000200541b0086a41026a202d41026a2d00003a0000200541ac086a41026a204041026a2d00003a0000200541a8086a41026a203f41026a2d00003a0000200541a4086a41026a203e41026a2d00003a00002005202c2f00003b01b4082005202d2f00003b01b008200520402f00003b01ac082005203f2f00003b01a8082005203e2f00003b01a408200541a0086a41026a203d41026a2d00003a00002005203d2f00003b01a0082005419c086a41026a203c41026a2d00003a00002005203c2f00003b019c0820054198086a41026a203b41026a2d00003a00002005203b2f00003b01980820054194086a41026a203a41026a2d00003a00002005203a2f00003b01940820054190086a41026a203941026a2d00003a0000200520392f00003b0190082005418c086a41026a203841026a2d00003a0000200520382f00003b018c0820054188086a41026a203741026a2d00003a0000200520372f00003b01880820054184086a41026a203641026a2d00003a0000200520362f00003b01840820054180086a41026a203541026a2d00003a0000200520352f00003b018008200541fc076a41026a203441026a2d00003a0000200520342f00003b01fc07200541f8076a41026a203341026a2d00003a0000200520332f00003b01f807200541f4076a41026a203241026a2d00003a0000200520322f00003b01f407200541f0076a41026a203141026a2d00003a0000200520312f00003b01f007200541ec076a41026a203041026a2d00003a0000200520302f00003b01ec07200b2128202e210b20082127200d21082013210d201f21132023211f202f21230b200541d7046a200541b0076a41026a2d00003a0000200541db046a200541b8016a41026a2d00003a0000200541df046a200541bc086a41026a2d00003a0000200520052f01b8053b00d104200520083a00d404200520052f01b0073b00d5042005200d3a00d804200520052f01b8013b00d904200520133a00dc04200520052f01bc083b00dd042005200541b8056a41026a2d00003a00d304200520273a00d004200541e3046a200541b8086a41026a2d00003a0000200541e7046a200541b4086a41026a2d00003a0000200541eb046a200541b0086a41026a2d00003a0000200541ef046a200541ac086a41026a2d00003a00002005201f3a00e004200520233a00e404200520263a00e804200520253a00ec04200520052f01b8083b00e104200520052f01b4083b00e504200520052f01b0083b00e904200520052f01ac083b00ed04200541f3046a200541a8086a41026a2d00003a0000200541f7046a200541a4086a41026a2d00003a0000200541fb046a200541a0086a41026a2d00003a0000200541ff046a2005419c086a41026a2d00003a0000200520243a00f004200520223a00f404200520213a00f804200520203a00fc04200520052f01a8083b00f104200520052f01a4083b00f504200520052f01a0083b00f904200520052f019c083b00fd042005201e3a00800520054183056a20054198086a41026a2d00003a0000200520052f0198083b008105200520163a00840520054187056a20054194086a41026a2d00003a0000200520052f0194083b008505200520143a0088052005418b056a20054190086a41026a2d00003a0000200520052f0190083b008905200520123a008c052005418f056a2005418c086a41026a2d00003a0000200520052f018c083b008d05200520113a00900520054193056a20054188086a41026a2d00003a0000200520052f0188083b009105200520103a00940520054197056a20054184086a41026a2d00003a0000200520052f0184083b0095052005200c3a0098052005419b056a20054180086a41026a2d00003a0000200520052f0180083b009905200520063a009c052005419f056a200541fc076a41026a2d00003a0000200520052f01fc073b009d05200520093a00a005200541a3056a200541f8076a41026a2d00003a0000200520052f01f8073b00a1052005200a3a00a405200541a7056a200541f4076a41026a2d00003a0000200520052f01f4073b00a505200520283a00a805200541ab056a200541f0076a41026a2d00003a0000200520052f01f0073b00a9052005200b3a00ac05200541af056a200541ec076a41026a2d00003a0000200520052f01ec073b00ad05200541b4056a41026a200541b0066a41026a2d00003a0000200520052f01b0063b01b4054108212a200541f8026a41086a200541d8016a41086a290300370300200541f8026a41106a200541d8016a41106a290300370300200541f8026a41186a200541d8016a41186a280200360200200520052903d8013703f802200541d0046a41046a210702400240024002400240024002402027417c6a41ff01712229410420294104491b417f6a0e020102000b200541ad056a2107200541a9056a2103200541a5056a2129200541a1056a212b2005419d056a212c20054199056a212d20054195056a214020054191056a213f2005418d056a213e20054189056a213d20054185056a213c20054181056a213b200541fd046a213a200541f9046a2139200541f5046a2138200541f1046a2137200541ed046a2136200541e9046a2135200541e5046a2134200541e1046a2133200541dd046a2132200541d9046a2131200541d5046a2130200541d0046a410172211d20044108470d02200541e2076a2104200541e6076a210e200541e9076a211b200541ec076a211c200541f0076a212f200541f4076a2141200541f8076a2142200541fc076a214320054180086a214420054184086a214520054188086a21462005418c086a214720054190086a214820054194086a214920054198086a214a2005419c086a214b200541a0086a214c200541a4086a214d200541a8086a214e200541ac086a214f200541b0086a2150200541b4086a2151200541b8086a2152200541bc086a21530c030b200541b0066a41286a20054180056a220b41286a290200370300200541b0066a41206a200b41206a290200370300200541b0066a41186a200b41186a290200370300200541b0066a41106a200b41106a290200370300200541b0066a41086a200b41086a290200370300200541e0066a41086a200741086a290200370300200541e0066a41106a200741106a290200370300200541e0066a41186a200741186a290200370300200541e0066a41206a220a200741206a290200370300200541e0066a41286a200741286a2802003602002005200b2902003703b006200520072902003703e006024020044108460d00200541ac076a41026a2207200541b4056a41026a2d00003a000020054190076a41086a2204200541f8026a41086a29030037030020054190076a41106a220b200541f8026a41106a29030037030020054190076a41186a2203200541f8026a41186a280200360200200520052f01b4053b01ac07200520052903f80237039007200541b8056a41286a22084100360200200541b8056a201d201d201b6a10f686808000200541b0076a41286a22092008280200360200200541b0076a41206a2208200541b8056a41206a290200370300200541b0076a41186a2206200541b8056a41186a290200370300200541b0076a41106a220c200541b8056a41106a290200370300200541b0076a41086a220d200541b8056a41086a290200370300200520052902b8053703b0072005202e3a00d801200520052f01ac073b00d901200520072d00003a00db01200541f4016a2003280200360200200541ec016a200b290300370200200541e4016a200429030037020020052005290390073702dc01200541a0026a200928020036020020054198026a200829030037020020054190026a200629030037020020054188026a200c290300370200200541d8016a41286a200d290300370200200520052903b0073702f801200541a5026a200e3a0000200541a4026a201c3a0000200141dc006a200541d8016a10958d8080001a0b200541d8016a41286a2207200541c8036a41286a290300370300200541d8016a41206a2204200541c8036a41206a290300370300200541d8016a41186a220b200541c8036a41186a290300370300200541d8016a41106a2201200541c8036a41106a290300370300200541d8016a41086a2203200541c8036a41086a290300370300200520052903c8033703d801200541b8056a41086a2208200541b0066a41086a280200200541dc066a22092802002206200641284b22061b360200200520052802b406200541b0066a41047220061b3602bc05200520052802b0063602b805200541d8016a200541b8056a10e78d808000200041d8006a2007290300370200200041d0006a2004290300370200200041c8006a200b290300370200200041c0006a2001290300370200200041386a2003290300370200200020052903d801370230200541c3056a200541e0066a41086a290300370000200541cb056a200541e0066a41106a290300370000200541d3056a200541e0066a41186a290300370000200541db056a200a290300370000200541e3056a200541e0066a41286a280200360000200520052903e0063700bb05200041053a0000200020052900b805370001200041096a2008290000370000200041116a200541b8056a41106a290000370000200041196a200541b8056a41186a290000370000200041216a200541b8056a41206a290000370000200041286a200541df056a29000037000020092802004129490d0320052802b40641002802c0a3c68000118080808000000c030b200541b8056a41286a200541d0046a41286a220b41286a290200370300200541b8056a41206a200b41206a290200370300200541b8056a41186a200b41186a290200370300200541b8056a41106a200b41106a290200370300200541b8056a41086a200b41086a290200370300200541e8056a41086a200741086a290200370300200541e8056a41106a200741106a290200370300200541e8056a41186a200741186a290200370300200541e8056a41206a220a200741206a2802003602002005200b2902003703b805200520072902003703e805024020044108460d00200541ac066a41026a2207200541b4056a41026a2d00003a000020054190066a41086a2204200541f8026a41086a29030037030020054190066a41106a220b200541f8026a41106a29030037030020054190066a41186a2208200541f8026a41186a280200360200200520052f01b4053b01ac06200520052903f80237039006200541b0066a41286a22094100360200200541b0066a201d201d201b6a10f686808000200541b0076a41286a22062009280200360200200541b0076a41206a2209200541b0066a41206a290200370300200541b0076a41186a220c200541b0066a41186a290200370300200541b0076a41106a220d200541b0066a41106a290200370300200541b0076a41086a2210200541b0066a41086a290200370300200520052902b0063703b0072005202e3a00d801200520052f01ac063b00d901200520072d00003a00db01200541f4016a2008280200360200200541ec016a200b290300370200200541e4016a200429030037020020052005290390063702dc01200541a0026a200628020036020020054198026a200929030037020020054190026a200c29030037020020054188026a200d290300370200200541d8016a41286a2010290300370200200520052903b0073702f801200541a5026a200e3a0000200541a4026a201c3a0000200141dc006a200541d8016a10958d8080001a0b200541b0066a41286a2207200541c8036a41286a290300370300200541b0066a41206a2204200541c8036a41206a290300370300200541b0066a41186a220b200541c8036a41186a290300370300200541b0066a41106a2208200541c8036a41106a290300370300200541b0066a41086a2209200541c8036a41086a290300370300200520052903c8033703b006200541d8016a41086a200541b8056a41086a280200200541e4056a2206280200220c200c41284b220c1b360200200520052802bc05200541b8056a410472200c1b3602dc01200520052802b8053602d801200541b0066a200541d8016a10e78d808000200541a8026a2007290300370200200541a0026a200429030037020020054198026a200b29030037020020054190026a200829030037020020054188026a2009290300370200200541e4016a200541e8056a41086a290300370200200541ec016a200541e8056a41106a290300370200200541f4016a200541e8056a41186a290300370200200541fc016a200a280200360200200520052903b00637028002200520052903e8053702dc01200541063a00d80120002001200541d8016a2003410110888680800020062802004129490d0220052802bc0541002802c0a3c68000118080808000000c020b200541e2076a41026a200541b4056a41026a2d00003a0000200541b8056a41086a200541f8026a41086a290300370300200541b8056a41106a200541f8026a41106a290300370300200541b8056a41186a200541f8026a41186a280200360200200520052f01b4053b01e207200520052903f8023703b805200541e6076a2104200541e9076a210e200541ec076a211b200541f0076a211c200541f4076a212f200541f8076a2141200541fc076a214220054180086a214320054184086a214420054188086a21452005418c086a214620054190086a214720054194086a214820054198086a21492005419c086a214a200541a0086a214b200541a4086a214c200541a8086a214d200541ac086a214e200541b0086a214f200541b4086a2150200541b8086a2151200541bc086a2152200541b8016a21532027212a20082127200d21082013210d201f21132023211f202621232025212620242125202221242021212220202121201e21202016211e20142116201221142011211220102111200c21102006210c20092106200a21092028210a200b2128202e210b0b2053201d2f00003b0000205220302f00003b0000205120312f00003b0000205020322f00003b0000204f20332f00003b0000205341026a201d41026a2d00003a0000205241026a203041026a2d00003a0000205141026a203141026a2d00003a0000205041026a203241026a2d00003a0000204f41026a203341026a2d00003a0000204e41026a203441026a2d00003a0000204e20342f00003b0000204d20352f00003b0000204d41026a203541026a2d00003a0000204c20362f00003b0000204c41026a203641026a2d00003a0000204b20372f00003b0000204b41026a203741026a2d00003a0000204a41026a203841026a2d00003a0000204a20382f00003b0000204941026a203941026a2d00003a0000204920392f00003b0000204841026a203a41026a2d00003a00002048203a2f00003b0000204741026a203b41026a2d00003a00002047203b2f00003b0000204641026a203c41026a2d00003a00002046203c2f00003b0000204541026a203d41026a2d00003a00002045203d2f00003b0000204441026a203e41026a2d00003a00002044203e2f00003b0000204341026a203f41026a2d00003a00002043203f2f00003b0000204241026a204041026a2d00003a0000204220402f00003b0000204141026a202d41026a2d00003a00002041202d2f00003b0000202f41026a202c41026a2d00003a0000202f202c2f00003b0000201c41026a202b41026a2d00003a0000201c202b2f00003b0000201b41026a202941026a2d00003a0000201b20292f00003b0000200e41026a200341026a2d00003a0000200e20032f00003b0000200441026a200741026a2d00003a0000200420072f00003b0000200541d8016a41286a200541c8036a41286a290300370300200541d8016a41206a200541c8036a41206a290300370300200541d8016a41186a200541c8036a41186a290300370300200541d8016a41106a200541c8036a41106a290300370300200541d8016a41086a200541c8036a41086a290300370300200520052903c8033703d801024002400240200141286a2802002207450d0020012007417f6a360228200141246a22072007280200220741016a220441002001411c6a280200220320042003491b6b360200200141206a28020020074102746a2802002204200141186a28020022074f0d02200128021420044107746a220741046a200720072d00004108461b10e3858080002007202a3a0000200720273a0004200720083a00082007200d3a000c200720052f00b8013b0001200741036a200541b8016a41026a2d00003a0000200720052f00bc083b0005200741076a200541bc086a41026a2d00003a0000200720052f00b8083b00092007410b6a200541b8086a41026a2d00003a0000200720052f00b4083b000d2007410f6a200541b4086a41026a2d00003a0000200720133a00102007201f3a0014200720233a0018200720263a001c200720052f00b0083b0011200741136a200541b0086a41026a2d00003a0000200720052f00ac083b0015200741176a200541ac086a41026a2d00003a0000200720052f00a8083b00192007411b6a200541a8086a41026a2d00003a0000200720052f00a4083b001d2007411f6a200541a4086a41026a2d00003a0000200720253a0020200720243a0024200720223a0028200720213a002c200720052f00a0083b0021200741236a200541a0086a41026a2d00003a0000200720052f009c083b0025200741276a2005419c086a41026a2d00003a0000200720052f0098083b00292007412b6a20054198086a41026a2d00003a0000200720052f0094083b002d2007412f6a20054194086a41026a2d00003a0000200720203a0030200741336a20054190086a41026a2d00003a0000200720052f0090083b00312007201e3a0034200741376a2005418c086a41026a2d00003a0000200720052f008c083b0035200720163a00382007413b6a20054188086a41026a2d00003a0000200720052f0088083b0039200720143a003c2007413f6a20054184086a41026a2d00003a0000200720052f0084083b003d200720123a0040200741c3006a20054180086a41026a2d00003a0000200720052f0080083b0041200720113a0044200741c7006a200541fc076a41026a2d00003a0000200720052f00fc073b0045200720103a0048200741cb006a200541f8076a41026a2d00003a0000200720052f00f8073b00492007200c3a004c200741cf006a200541f4076a41026a2d00003a0000200720052f00f4073b004d200720063a0050200741d3006a200541f0076a41026a2d00003a0000200720052f00f0073b0051200720093a0054200741d7006a200541ec076a41026a2d00003a0000200720052f00ec073b00552007200a3a0058200741db006a200541e9076a41026a2d00003a0000200720052f00e9073b0059200720283a005c200741df006a200541e6076a41026a2d00003a0000200720052f00e6073b005d2007200b3a0060200741e3006a200541e2076a41026a2d00003a0000200720052f01e2073b0061200741fc006a200541d0056a280200360000200741f4006a200541c8056a290300370000200741ec006a200541b8056a41086a290300370000200720052903b8053700640c010b0240200141186a28020022072001280210470d00200141106a2007109e86808000200128021821070b200128021420074107746a2207202a3a0000200720052f00b8013b0001200720273a0004200720052f00bc083b0005200720083a0008200720052f00b8083b00092007200d3a000c200720052f00b4083b000d200741036a200541b8016a41026a2d00003a0000200741076a200541bc086a41026a2d00003a00002007410b6a200541b8086a41026a2d00003a00002007410f6a200541b4086a41026a2d00003a0000200720133a00102007201f3a0014200720233a0018200720263a001c200720052f00b0083b0011200741136a200541b0086a41026a2d00003a0000200720052f00ac083b0015200741176a200541ac086a41026a2d00003a0000200720052f00a8083b00192007411b6a200541a8086a41026a2d00003a0000200720052f00a4083b001d2007411f6a200541a4086a41026a2d00003a0000200720253a0020200741236a200541a0086a41026a2d00003a0000200720052f00a0083b0021200720243a0024200741276a2005419c086a41026a2d00003a0000200720052f009c083b0025200720223a00282007412b6a20054198086a41026a2d00003a0000200720052f0098083b0029200720213a002c2007412f6a20054194086a41026a2d00003a0000200720052f0094083b002d200720203a0030200741336a20054190086a41026a2d00003a0000200720052f0090083b00312007201e3a0034200741376a2005418c086a41026a2d00003a0000200720052f008c083b0035200720163a00382007413b6a20054188086a41026a2d00003a0000200720052f0088083b0039200720143a003c2007413f6a20054184086a41026a2d00003a0000200720052f0084083b003d200720123a0040200741c3006a20054180086a41026a2d00003a0000200720052f0080083b0041200720113a0044200741c7006a200541fc076a41026a2d00003a0000200720052f00fc073b0045200720103a0048200741cb006a200541f8076a41026a2d00003a0000200720052f00f8073b00492007200c3a004c200741cf006a200541f4076a41026a2d00003a0000200720052f00f4073b004d200720063a0050200741d3006a200541f0076a41026a2d00003a0000200720052f00f0073b0051200720093a0054200741d7006a200541ec076a41026a2d00003a0000200720052f00ec073b00552007200a3a0058200741db006a200541e9076a41026a2d00003a0000200720052f00e9073b0059200720283a005c200741df006a200541e6076a41026a2d00003a0000200720052f00e6073b005d2007200b3a0060200741e3006a200541e2076a41026a2d00003a0000200720052f01e2073b0061200741fc006a200541d0056a280200360000200741f4006a200541c8056a290300370000200741ec006a200541c0056a290300370000200720052903b80537006420012001280218220441016a3602180b200020052903d801370228200020052f00b0073b0005200020052902b00637020c200041d0006a20054180026a290300370200200041c8006a200541f8016a290300370200200041c0006a200541d8016a41186a290300370200200041386a200541d8016a41106a290300370200200041306a200541d8016a41086a290300370200200041076a200541b0076a41026a2d00003a0000200041146a200541b0066a41086a2902003702002000411c6a200541b0066a41106a290200370200200041246a200541b0066a41186a280200360200200041063a0000200020052f00d8013b0001200041036a200541d8016a41026a2d00003a000020002004360208200041003a00040c020b2004200741d4d7c2800010f980808000000b20052d00d004417c6a41ff01712207410420074104491b417f6a4102490d00200541d0046a10e3858080000b200528028004450d00200f2802284129490d0020052802840441002802c0a3c68000118080808000000b20022d0000417c6a41ff01712207410420074104491b417e6a4103490d010b200210e3858080000b200541c0086a2480808080000bb20101027f0240200141186a2802002203200128020c2204470d002001410c6a10a889808000200128020c2104200128021821030b200141106a280200200141146a28020020036a22034100200420032004491b6b4102746a20023602002001200128021841016a36021802402001280208220420024d0d002000200128020420024107746a220141800110848e8080001a200141043a0004200141083a00000f0b2002200441e4d7c2800010f980808000000b99950105107f017e017f047e397f23808080800041c0086b2205248080808000024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024020022d00002206417c6a41ff01712207410420074104491b417e6a0e03020108000b2000200241e00010848e8080001a0c200b200241056a210820022d000421092002280230220a2d00004102460d0141002106410121070c020b200541c8036a41286a200241d0006a290200370300200541e8036a200241c8006a290200370300200541c8036a41186a200241c0006a290200370300200541c8036a41106a200241386a290200370300200541c8036a41086a2207200241306a290200370300200520022902283703c803200541fe036a200241076a2d00003a000020054198016a41086a200241146a29020037030020054198016a41106a2002411c6a29020037030020054198016a41186a200241246a2802003602002005200241056a2f00003b01fc0320052002410c6a29020037039801200241086a280200210b20022d000421062003280200210a2003280204210820032802082109024002400240024002400240024002400240024020040d002007280200200541f4036a2802002204200441284b1b2207417f6a210c2007450d11200741017420096a20052802c803417f736a2209410176210720052802cc03200541c8036a410472200441284b1b200c6a2d0000410f71210c024020094101710d00200720084d0d022007200841f492c68000109581808000000b200720084b0d1220072008490d0220072008419493c6800010f980808000000b2009410176210702400240200941017122040d00200720084d0d012007200841f492c68000109581808000000b200720084b0d13200720084f0d14200a20076a2d000041707121090b2005420037028002200541003602d804200520073602d4042005200a3602d004200541d8016a200541d0046a4100200510e38d8080001a2004450d06200941f00171210820052d0084024101710d04200541d8016a41046a2104200541d8016a41044128200528028002220d41284b22091b6a280200220c200d412820091b460d022004200541d8016a41286a20091b210420052802d801200541d8016a20091b21090c030b410021042005410036028004200c410474210e20054180046a41046a210f410121080c070b200a20076a22042d0000210941002108200541d8016a41286a220d4100360200200541d8016a200a200410f686808000200541d0046a41286a2210200d280200220d360200200541d0046a41206a2211200541d8016a41206a290200370300200541d0046a41186a2212200541d8016a41186a290200370300200541d0046a41106a2213200541d8016a41106a290200370300200541d0046a41086a2214200541d8016a41086a290200370300200520052902d80122153703d004200541d0046a41047221042009417071200c72211602400240200541d0046a41044128200d41284b22091b6a280200220c200d412820091b460d002004201020091b21042015a7200541d0046a20091b21090c010b200541d0046a10f88680800020052802d404210c20052802d00421090b2009200c6a20163a00002004200428020041016a360200200541b0066a41086a20142903002215370300200541b0066a41106a20132903002217370300200541b0066a41186a20122903002218370300200541b0066a41206a20112903002219370300200541b0066a41286a20102802002204360200200520052903d004221a3703b0062005418c046a201537020020054194046a20173702002005419c046a2018370200200541a4046a2019370200200541ac046a200436020020054101360280042005201a3702840420054180046a41046a210f0c050b200541d8016a10f78d80800020052802dc01210c20052802d80121090b2009200c6a20083a00002004200428020041016a3602000c010b20052802dc012005280280022204200441284b22041b2209450d0f200920052802d801200541d8016a20041b6a417f6a220420042d00002008410476723a00000b200520052802840241016a360284020b200520052802c8033602d8042005200541d0036a280200200541f4036a2802002204200441284b22041b3602d404200520052802cc03200541c8036a41047220041b3602d004200541d8016a200541d0046a4100200510e38d8080001a200528028402220841017621040240024020084101710d0020052802dc012005280280022209200941284b22091b220c2004490d1020052802d801200541d8016a20091b21090c010b20052802dc012005280280022209200941284b1b220c2004490d10200c20044d0d1120052802d801200541d8016a200941284b1b220920046a2d0000417071210e0b200541d0046a41286a220c4100360200200541d0046a2009200920046a10f686808000200541b0066a41286a2204200c280200360200200541b0066a41206a2209200541d0046a41206a290200370300200541b0066a41186a220c200541d0046a41186a290200370300200541b0066a41106a220d200541d0046a41106a290200370300200541b0066a41086a2210200541d0046a41086a290200370300200520052902d0043703b00602402005280280024129490d0020052802d80141002802c0a3c68000118080808000000b2005418c046a201029030037020020054194046a200d2903003702002005419c046a200c290300370200200541a4046a2009290300370200200541ac046a20042802003602002005410136028004200520052903b0063702840420054184046a210f0b20052802880420052802ac042204200441284b22041b2109200528028404200f20041b21040b2009200720041b211b2008410171211c2004200a20041b211d024020064101710d000240200141286a28020022042001411c6a220a2802002207470d00200a10a889808000200128021c2107200128022821040b200141206a280200200141246a28020020046a22044100200720042007491b6b4102746a200b3602002001200128022841016a360228200141186a2802002207200b4b0d1c200b200741e4d7c2800010f980808000000b200541b0046a41026a200541fc036a41026a2d00003a0000200541bf046a20054198016a41086a290300370000200541c7046a200541a8016a290300370000200541cf046a200541b0016a2d00003a0000200520052f01fc033b01b0042005200b3600b30420052005290398013700b7042005200e3a00e1012005201c3a00e0012005201b3602dc012005201d3602d801200541d0046a2001200541b0046a200541d8016a10868680800020052802d404210b20052802d004450d1a200041083a00002000200b3602040240200528028004450d00200f2802284129490d0020052802840441002802c0a3c68000118080808000000b200541f4036a2802004129490d0620052802cc0341002802c0a3c68000118080808000000c060b4101210641022107200a2d00244102470d004102210641032107200a2d00484102470d000240200a2d006c4102460d0041032106410421070c010b0240200a2d0090014102460d0041042106410521070c010b0240200a2d00b4014102460d0041052106410621070c010b0240200a2d00d8014102460d0041062106410721070c010b0240200a2d00fc014102460d0041072106410821070c010b0240200a2d00a0024102460d0041082106410921070c010b0240200a2d00c4024102460d0041092106410a21070c010b0240200a2d00e8024102460d00410a2106410b21070c010b0240200a2d008c034102460d00410b2106410c21070c010b0240200a2d00b0034102460d00410c2106410d21070c010b0240200a2d00d4034102460d00410d2106410e21070c010b0240200a2d00f8034102460d00410e2106410f21070c010b200a2d009c044102460d01410f2106411021070b200741246c21070340200741246a220441e404460d02200a20076a210b20042107200b2d00004102460d000c180b0b200941ff01714103460d15200020082900003700052000412c6a200841276a280000360000200041256a200841206a2900003700002000411d6a200841186a290000370000200041156a200841106a2900003700002000410d6a200841086a290000370000200542003702dc01200541a0d1c280003602d801200041306a200541d8016a10d98d808000200020093a0004200041053a00000c010b200941ff01714103470d15200520063a00d004200541d8016a41286a22074100360200200541d8016a200541d0046a200541d0046a41016a10f58d808000200541086a41286a220b2007280200360200200541086a41206a2208200541d8016a41206a290200370300200541086a41186a2209200541d8016a41186a290200370300200541086a41106a220c200541d8016a41106a290200370300200541086a41086a220d200541d8016a41086a290200370300200520052902d801370308200a200641246c6a22072d00002104200741023a000020044102460d0b2005413d6a2007290001370000200541e4006a2005290308370200200541386a41246a200741206a280000360000200541d5006a200741196a290000370000200541cd006a200741116a290000370000200541c5006a200741096a290000370000200541ec006a200d290300370200200541f4006a200c290300370200200541fc006a200929030037020020054184016a20082903003702002005418c016a200b28020036020020054101360260200541063a0038200520043a003c20002001200541386a20034100108a868080000b200a41002802c0a3c68000118080808000000c170b200541a0016a200241c4006a290200370300200541a8016a200241cc006a290200370300200541b0016a200241d4006a29020037030020052002413c6a2902003703980141012107200241016a2108200241306a2109200241dc006a2802002110200241386a2802002111200241346a2802002113200228023021124100210c024002400240200228022c220a2d0000220d4102470d004101210c41022107200a2d0024220d4102470d004102210c41032107200a2d0048220d4102470d000240200a2d006c220d4102460d004103210c410421070c010b0240200a2d009001220d4102460d004104210c410521070c010b0240200a2d00b401220d4102460d004105210c410621070c010b0240200a2d00d801220d4102460d004106210c410721070c010b0240200a2d00fc01220d4102460d004107210c410821070c010b0240200a2d00a002220d4102460d004108210c410921070c010b0240200a2d00c402220d4102460d004109210c410a21070c010b0240200a2d00e802220d4102460d00410a210c410b21070c010b0240200a2d008c03220d4102460d00410b210c410c21070c010b0240200a2d00b003220d4102460d00410c210c410d21070c010b0240200a2d00d403220d4102460d00410d210c410e21070c010b0240200a2d00f803220d4102460d00410e210c410f21070c010b200a2d009c04220d4102460d01410f210c411021070b200741246c21070340200741246a220441e404460d02200a20076a210b20042107200b2d00004102460d000c140b0b200641ff01714103460d1120002008290000370005200020092902003702302000412c6a200841276a280000360000200041256a200841206a2900003700002000411d6a200841186a290000370000200041156a200841106a2900003700002000410d6a200841086a290000370000200041386a200941086a290200370200200041c0006a200941106a290200370200200041c8006a200941186a290200370200200041d0006a200941206a290200370200200041d8006a200941286a290200370200200020063a0004200041053a00000c100b200641ff01714103470d11200a200c41246c6a220741023a0000200d4102460d0a2007280204210b200541bc086a41026a200741036a2d00003a0000200541c0016a200741106a290200370300200541b8016a41106a200741186a290200370300200541b8016a41186a200741206a280200360200200520072f00013b01bc08200520072902083703b801200328020820112010201041284b1b41017420126b6a220841017621072003280204210420032802002103024002400240024020084101710d00200720044d0d012007200441f492c68000109581808000000b200720044b0d0e20072004490d0120072004419493c6800010f980808000000b410021042005410036028004200c4104742109410121060c010b200320076a22042d0000210841002106200541d8016a41286a22094100360200200541d8016a2003200410f686808000200541d0046a41286a221420092802002209360200200541d0046a41206a221e200541d8016a41206a290200370300200541d0046a41186a221f200541d8016a41186a290200370300200541d0046a41106a2220200541d8016a41106a290200370300200541d0046a41086a2221200541d8016a41086a290200370300200520052902d80122153703d004200541d0046a41047221042008417071200c72212202400240200541d0046a41044128200941284b22081b6a28020022162009412820081b460d002004201420081b21042015a7200541d0046a20081b21080c010b200541d0046a10f88680800020052802d404211620052802d00421080b200820166a20223a00002004200428020041016a360200200541ac046a20142802002208360200200541a4046a201e2903003702002005419c046a201f29030037020020054194046a20202903003702002005418c046a2021290300370200200520052903d00422153702840420054101360280042015a720054180046a41046a200841284b22091b2104200528028804200820091b21080b2008200720041b21082004200320041b21070240200d4101710d000240200141286a28020022032001411c6a220d2802002204470d00200d10a889808000200128021c2104200128022821030b200141206a280200200141246a28020020036a22034100200420032004491b6b4102746a200b3602002001200128022841016a360228200141186a2802002204200b4d0d0d200541d8016a200141146a280200200b4107746a220441800110848e8080001a200441043a0004200441083a00000c0f0b200541d8026a41026a200541bc086a41026a2d00003a0000200541e7026a200541b8016a41086a290300370000200541ef026a200541b8016a41106a290300370000200541f7026a200541d0016a2d00003a0000200520052f01bc083b01d8022005200b3600db02200520052903b8013700df02200520093a00d904200520063a00d804200520083602d404200520073602d004200541b0066a2001200541d8026a200541d0046a10868680800020052802b406210420052802b006450d0d200041083a0000200020043602040240200528028004450d0020052802ac044129490d0020052802840441002802c0a3c68000118080808000000b200a41002802c0a3c680001180808080000020104129490d00201341002802c0a3c68000118080808000000b20022d0000417c6a41ff01712207410420074104491b417e6a4103490d170c160b200c200741f8d5c2800010f980808000000b20072008418493c68000109581808000000b20072008418493c68000109581808000000b20072008419493c6800010f980808000000b419c94c68000413a41d894c6800010a181808000000b2004200c41ac95c68000109581808000000b2004200c41bc95c68000109581808000000b2004200c41cc95c6800010f980808000000b41c8d6c28000412441ecd6c2800010a181808000000b41c8d6c280004124418cd7c2800010a181808000000b20072004418493c68000109581808000000b200b200441e4d7c2800010f980808000000b200541d8016a200141106a20041089868080000b0240024020052d00d80122044108470d00200541f8026a41086a200541e5016a290000370300200541f8026a41106a200541ed016a290000370300200541f8026a41186a200541f5016a290000370300200541f8026a41206a200541fd016a2900003703002005419f036a20054184026a280000360000200541b8056a41086a20054194026a290200370300200541b8056a41106a2005419c026a290200370300200541b8056a41186a200541a4026a290200370300200541b8056a41206a200541ac026a290200370300200541e0056a200541b4026a2902003703002005200541dd016a2900003703f80220052005418c026a2902003703b80520054188026a280200210b20052d00dc0121040c010b200541b0076a41086a220d200541e1016a290000370300200541b0076a41106a2214200541e9016a290000370300200541b0076a41186a2216200541f1016a290000370300200541b0076a41206a221e200541f9016a290000370300200541b0076a41276a221f200541d8016a41286a280000360000200541b0066a41086a2220200541d8016a41386a290200370300200541b0066a41106a2221200541d8016a41c0006a290200370300200541b0066a41186a2222200541d8016a41c8006a290200370300200541b0066a41206a2223200541a8026a290200370300200541b0066a41286a2224200541b0026a290200370300200520052900d9013703b00720052005290288023703b006200528028402210b200541a8036a41186a2225200541d0026a290200370300200541a8036a41106a2226200541c8026a290200370300200541a8036a41086a2227200541c0026a290200370300200520052902b8023703a803200541b8056a41286a22034100360200200541b8056a2007200720086a10f686808000200541c8036a41286a22072003280200360200200541c8036a41206a2208200541b8056a41206a220f290200370300200541c8036a41186a2228200541b8056a41186a2229290200370300200541c8036a41106a222a200541b8056a41106a222b290200370300200541c8036a41086a222c200541b8056a41086a222d290200370300200541d0046a41086a2027290300370300200541d0046a41106a2026290300370300200541d0046a41186a2025290300370300200520052902b8053703c803200520052903a8033703d004200541d0046a41c8006a2007280200360200200541d0046a41c0006a2008290300370300200541d0046a41386a202829030037030020054180056a202a290300370300200541d0046a41286a202c290300370300200520052903c8033703f0042005419d056a20093a00002005419c056a20063a0000200141dc006a200541d0046a10958d8080001a200541f8026a41086a200d290300370300200541f8026a41106a2014290300370300200541f8026a41186a2016290300370300200541f8026a41206a201e290300370300200541f8026a41276a201f280000360000202d2020290300370300202b202129030037030020292022290300370300200f202329030037030020032024290300370300200520052903b0073703f802200520052903b0063703b8050b02400240024002402004417c6a41ff01712207410420074104491b417f6a0e0401000002000b419cd7c28000412841c4d7c2800010f880808000000b200541b0066a41286a200541b8056a41286a290300370300200541b0066a41206a200541b8056a41206a290300370300200541b0066a41186a200541b8056a41186a290300370300200541b0066a41106a200541b8056a41106a290300370300200541b0066a41086a2204200541b8056a41086a290300370300200520052903b8053703b006200541d0046a41086a22012011360200200541e4046a20054198016a41086a290300370200200541ec046a20054198016a41106a290300370200200541f4046a20054198016a41186a290300370200200520133602d404200520123602d00420052005290398013702dc04200520103602fc04200541c8036a41086a22074101360200200541013602c8032005200c3a00b0072005200541b0076a3602cc03200541d0046a200541c8036a10e78d80800020072004280200200541dc066a22042802002203200341284b22031b360200200520052802b406200541b0066a41047220031b3602cc03200520052802b0063602c803200541d0046a200541c8036a10e78d808000200041d8006a200541d0046a41286a290200370200200041d0006a200541d0046a41206a290200370200200041c8006a200541d0046a41186a290200370200200041c0006a200541d0046a41106a290200370200200041386a2001290200370200200020052902d004370230200541c8036a410b6a200541f8026a410b6a290000370000200541c8036a41136a200541f8026a41136a290000370000200541c8036a411b6a200541f8026a411b6a290000370000200541c8036a41236a200541f8026a41236a290000370000200520052900fb023700cb03200041053a00002000200b36022c200020052900c803370001200041096a2007290000370000200041116a200541c8036a41106a290000370000200041196a200541c8036a41186a290000370000200041216a200541c8036a41206a290000370000200041286a200541ef036a28000036000020042802004129490d0120052802b40641002802c0a3c68000118080808000000c010b200020043a0000200020052903f802370001200541b0066a41286a200541b8056a41286a290300370300200541b0066a41206a200541b8056a41206a290300370300200541b0066a41186a200541b8056a41186a290300370300200541b0066a41106a200541b8056a41106a290300370300200541b0066a41086a2207200541b8056a41086a290300370300200041096a200541f8026a41086a290300370000200041116a200541f8026a41106a290300370000200041196a200541f8026a41186a290300370000200041216a200541f8026a41206a290300370000200041286a2005419f036a280000360000200520052903b8053703b006200541d0046a41086a22042011360200200541e4046a20054198016a41086a290300370200200541ec046a20054198016a41106a290300370200200541f4046a20054198016a41186a290300370200200520133602d404200520123602d00420052005290398013702dc04200520103602fc04200541c8036a41086a22014101360200200541013602c8032005200c3a00b0072005200541b0076a3602cc03200541d0046a200541c8036a10e78d80800020012007280200200541dc066a22072802002203200341284b22031b360200200520052802b406200541b0066a41047220031b3602cc03200520052802b0063602c803200541d0046a200541c8036a10e78d808000200041d8006a200541d0046a41286a290200370200200041d0006a200541d0046a41206a290200370200200041c8006a200541d0046a41186a290200370200200041c0006a200541d0046a41106a290200370200200041386a2004290200370200200020052902d0043702302000200b36022c20072802004129490d0020052802b40641002802c0a3c68000118080808000000b200528028004450d0020052802ac044129490d0020052802840441002802c0a3c68000118080808000000b200a41002802c0a3c68000118080808000000c060b4188d6c28000412f41fcd6c2800010f880808000000b2000200929020037023020002008290000370001200041d8006a200941286a290200370200200041d0006a200941206a290200370200200041c8006a200941186a290200370200200041c0006a200941106a290200370200200041386a200941086a290200370200200041096a200841086a290000370000200041116a200841106a290000370000200041196a200841186a290000370000200041216a200841206a290000370000200041286a200841276a2800003600002000200a36022c200020063a00000c040b4188d6c28000412f41b8d6c2800010f880808000000b2000200a360230200020093a0004200041073a0000200020082900003700052000412c6a200841276a280000360000200041256a200841206a2900003700002000411d6a200841186a290000370000200041156a200841106a2900003700002000410d6a200841086a2900003700000c020b0240200141286a28020022042001411c6a220a2802002207470d00200a10a889808000200128021c2107200128022821040b200141206a280200200141246a28020020046a22044100200720042007491b6b4102746a200b3602002001200128022841016a360228200141186a2802002207200b4b0d00200b200741e4d7c2800010f980808000000b200141146a280200200b4107746a22072d0060212e20072d005c210b20072d0058210a20072d0054210920072d0050210620072d004c210c20072d0048211020072d0044211120072d0040211220072d003c211420072d0038211620072d0034211e20072d0030212020072d002c212120072d0028212220072d0024212420072d0020212520072d001c212620072d0018212f20072d0014212320072d0010211f20072d000c211320072d0008210d20072d0004210820072d00002104200741043a0004200741083a0000200741e1006a2130200741dd006a2131200741d9006a2132200741d5006a2133200741d1006a2134200741cd006a2135200741c9006a2136200741c5006a2137200741c1006a21382007413d6a2139200741396a213a200741356a213b200741316a213c2007412d6a213d200741296a213e200741256a213f200741216a21402007411d6a212d200741196a212c200741156a212b200741116a212a2007410d6a2129200741096a2128200741056a21270240024020044108460d00200541b8056a41026a200741036a2d00003a0000200541b0076a41026a202741026a2d00003a0000200541b8016a41026a202841026a2d00003a0000200541bc086a41026a202941026a2d00003a0000200541b8086a41026a202a41026a2d00003a0000200520072f00013b01b805200520272f00003b01b007200520282f00003b01b801200520292f00003b01bc082005202a2f00003b01b808200541b4086a41026a202b41026a2d00003a0000200541b0086a41026a202c41026a2d00003a0000200541ac086a41026a202d41026a2d00003a0000200541a8086a41026a204041026a2d00003a0000200541a4086a41026a203f41026a2d00003a00002005202b2f00003b01b4082005202c2f00003b01b0082005202d2f00003b01ac08200520402f00003b01a8082005203f2f00003b01a408200541a0086a41026a203e41026a2d00003a00002005203e2f00003b01a0082005419c086a41026a203d41026a2d00003a00002005203d2f00003b019c0820054198086a41026a203c41026a2d00003a00002005203c2f00003b01980820054194086a41026a203b41026a2d00003a00002005203b2f00003b01940820054190086a41026a203a41026a2d00003a00002005203a2f00003b0190082005418c086a41026a203941026a2d00003a0000200520392f00003b018c0820054188086a41026a203841026a2d00003a0000200520382f00003b01880820054184086a41026a203741026a2d00003a0000200520372f00003b01840820054180086a41026a203641026a2d00003a0000200520362f00003b018008200541fc076a41026a203541026a2d00003a0000200520352f00003b01fc07200541f8076a41026a203441026a2d00003a0000200520342f00003b01f807200541f4076a41026a203341026a2d00003a0000200520332f00003b01f407200541f0076a41026a203241026a2d00003a0000200520322f00003b01f007200541ec076a41026a203141026a2d00003a0000200520312f00003b01ec07200541b0066a41026a203041026a2d00003a0000200520302f00003b01b006200541f0016a200741fc006a280000360200200541e8016a200741f4006a290000370300200541d8016a41086a200741ec006a290000370300200520072900643703d801200a2128200421272009210a20062109200c21062010210c20112110201221112014211220162114201e21162020211e2021212020222121202421222025212420262125202f21260c010b200541b8056a41026a202741026a2d00003a0000200541b0076a41026a202841026a2d00003a0000200541b8016a41026a202941026a2d00003a0000200541bc086a41026a202a41026a2d00003a0000200541b8086a41026a202b41026a2d00003a0000200520272f00003b01b805200520282f00003b01b007200520292f00003b01b8012005202a2f00003b01bc082005202b2f00003b01b808200541b4086a41026a202c41026a2d00003a0000200541b0086a41026a202d41026a2d00003a0000200541ac086a41026a204041026a2d00003a0000200541a8086a41026a203f41026a2d00003a0000200541a4086a41026a203e41026a2d00003a00002005202c2f00003b01b4082005202d2f00003b01b008200520402f00003b01ac082005203f2f00003b01a8082005203e2f00003b01a408200541a0086a41026a203d41026a2d00003a00002005203d2f00003b01a0082005419c086a41026a203c41026a2d00003a00002005203c2f00003b019c0820054198086a41026a203b41026a2d00003a00002005203b2f00003b01980820054194086a41026a203a41026a2d00003a00002005203a2f00003b01940820054190086a41026a203941026a2d00003a0000200520392f00003b0190082005418c086a41026a203841026a2d00003a0000200520382f00003b018c0820054188086a41026a203741026a2d00003a0000200520372f00003b01880820054184086a41026a203641026a2d00003a0000200520362f00003b01840820054180086a41026a203541026a2d00003a0000200520352f00003b018008200541fc076a41026a203441026a2d00003a0000200520342f00003b01fc07200541f8076a41026a203341026a2d00003a0000200520332f00003b01f807200541f4076a41026a203241026a2d00003a0000200520322f00003b01f407200541f0076a41026a203141026a2d00003a0000200520312f00003b01f007200541ec076a41026a203041026a2d00003a0000200520302f00003b01ec07200b2128202e210b20082127200d21082013210d201f21132023211f202f21230b200541d7046a200541b0076a41026a2d00003a0000200541db046a200541b8016a41026a2d00003a0000200541df046a200541bc086a41026a2d00003a0000200520052f01b8053b00d104200520083a00d404200520052f01b0073b00d5042005200d3a00d804200520052f01b8013b00d904200520133a00dc04200520052f01bc083b00dd042005200541b8056a41026a2d00003a00d304200520273a00d004200541e3046a200541b8086a41026a2d00003a0000200541e7046a200541b4086a41026a2d00003a0000200541eb046a200541b0086a41026a2d00003a0000200541ef046a200541ac086a41026a2d00003a00002005201f3a00e004200520233a00e404200520263a00e804200520253a00ec04200520052f01b8083b00e104200520052f01b4083b00e504200520052f01b0083b00e904200520052f01ac083b00ed04200541f3046a200541a8086a41026a2d00003a0000200541f7046a200541a4086a41026a2d00003a0000200541fb046a200541a0086a41026a2d00003a0000200541ff046a2005419c086a41026a2d00003a0000200520243a00f004200520223a00f404200520213a00f804200520203a00fc04200520052f01a8083b00f104200520052f01a4083b00f504200520052f01a0083b00f904200520052f019c083b00fd042005201e3a00800520054183056a20054198086a41026a2d00003a0000200520052f0198083b008105200520163a00840520054187056a20054194086a41026a2d00003a0000200520052f0194083b008505200520143a0088052005418b056a20054190086a41026a2d00003a0000200520052f0190083b008905200520123a008c052005418f056a2005418c086a41026a2d00003a0000200520052f018c083b008d05200520113a00900520054193056a20054188086a41026a2d00003a0000200520052f0188083b009105200520103a00940520054197056a20054184086a41026a2d00003a0000200520052f0184083b0095052005200c3a0098052005419b056a20054180086a41026a2d00003a0000200520052f0180083b009905200520063a009c052005419f056a200541fc076a41026a2d00003a0000200520052f01fc073b009d05200520093a00a005200541a3056a200541f8076a41026a2d00003a0000200520052f01f8073b00a1052005200a3a00a405200541a7056a200541f4076a41026a2d00003a0000200520052f01f4073b00a505200520283a00a805200541ab056a200541f0076a41026a2d00003a0000200520052f01f0073b00a9052005200b3a00ac05200541af056a200541ec076a41026a2d00003a0000200520052f01ec073b00ad05200541b4056a41026a200541b0066a41026a2d00003a0000200520052f01b0063b01b4054108212a200541f8026a41086a200541d8016a41086a290300370300200541f8026a41106a200541d8016a41106a290300370300200541f8026a41186a200541d8016a41186a280200360200200520052903d8013703f802200541d0046a41046a210702400240024002400240024002402027417c6a41ff01712229410420294104491b417f6a0e020102000b200541ad056a2107200541a9056a2103200541a5056a2129200541a1056a212b2005419d056a212c20054199056a212d20054195056a214020054191056a213f2005418d056a213e20054189056a213d20054185056a213c20054181056a213b200541fd046a213a200541f9046a2139200541f5046a2138200541f1046a2137200541ed046a2136200541e9046a2135200541e5046a2134200541e1046a2133200541dd046a2132200541d9046a2131200541d5046a2130200541d0046a410172211d20044108470d02200541e2076a2104200541e6076a210e200541e9076a211b200541ec076a211c200541f0076a212f200541f4076a2141200541f8076a2142200541fc076a214320054180086a214420054184086a214520054188086a21462005418c086a214720054190086a214820054194086a214920054198086a214a2005419c086a214b200541a0086a214c200541a4086a214d200541a8086a214e200541ac086a214f200541b0086a2150200541b4086a2151200541b8086a2152200541bc086a21530c030b200541b0066a41286a20054180056a220b41286a290200370300200541b0066a41206a200b41206a290200370300200541b0066a41186a200b41186a290200370300200541b0066a41106a200b41106a290200370300200541b0066a41086a200b41086a290200370300200541e0066a41086a200741086a290200370300200541e0066a41106a200741106a290200370300200541e0066a41186a200741186a290200370300200541e0066a41206a220a200741206a290200370300200541e0066a41286a200741286a2802003602002005200b2902003703b006200520072902003703e006024020044108460d00200541ac076a41026a2207200541b4056a41026a2d00003a000020054190076a41086a2204200541f8026a41086a29030037030020054190076a41106a220b200541f8026a41106a29030037030020054190076a41186a2203200541f8026a41186a280200360200200520052f01b4053b01ac07200520052903f80237039007200541b8056a41286a22084100360200200541b8056a201d201d201b6a10f686808000200541b0076a41286a22092008280200360200200541b0076a41206a2208200541b8056a41206a290200370300200541b0076a41186a2206200541b8056a41186a290200370300200541b0076a41106a220c200541b8056a41106a290200370300200541b0076a41086a220d200541b8056a41086a290200370300200520052902b8053703b0072005202e3a00d801200520052f01ac073b00d901200520072d00003a00db01200541f4016a2003280200360200200541ec016a200b290300370200200541e4016a200429030037020020052005290390073702dc01200541a0026a200928020036020020054198026a200829030037020020054190026a200629030037020020054188026a200c290300370200200541d8016a41286a200d290300370200200520052903b0073702f801200541a5026a200e3a0000200541a4026a201c3a0000200141dc006a200541d8016a10958d8080001a0b200541d8016a41286a2207200541c8036a41286a290300370300200541d8016a41206a2204200541c8036a41206a290300370300200541d8016a41186a220b200541c8036a41186a290300370300200541d8016a41106a2201200541c8036a41106a290300370300200541d8016a41086a2203200541c8036a41086a290300370300200520052903c8033703d801200541b8056a41086a2208200541b0066a41086a280200200541dc066a22092802002206200641284b22061b360200200520052802b406200541b0066a41047220061b3602bc05200520052802b0063602b805200541d8016a200541b8056a10e78d808000200041d8006a2007290300370200200041d0006a2004290300370200200041c8006a200b290300370200200041c0006a2001290300370200200041386a2003290300370200200020052903d801370230200541c3056a200541e0066a41086a290300370000200541cb056a200541e0066a41106a290300370000200541d3056a200541e0066a41186a290300370000200541db056a200a290300370000200541e3056a200541e0066a41286a280200360000200520052903e0063700bb05200041053a0000200020052900b805370001200041096a2008290000370000200041116a200541b8056a41106a290000370000200041196a200541b8056a41186a290000370000200041216a200541b8056a41206a290000370000200041286a200541df056a29000037000020092802004129490d0320052802b40641002802c0a3c68000118080808000000c030b200541b8056a41286a200541d0046a41286a220b41286a290200370300200541b8056a41206a200b41206a290200370300200541b8056a41186a200b41186a290200370300200541b8056a41106a200b41106a290200370300200541b8056a41086a200b41086a290200370300200541e8056a41086a200741086a290200370300200541e8056a41106a200741106a290200370300200541e8056a41186a200741186a290200370300200541e8056a41206a220a200741206a2802003602002005200b2902003703b805200520072902003703e805024020044108460d00200541ac066a41026a2207200541b4056a41026a2d00003a000020054190066a41086a2204200541f8026a41086a29030037030020054190066a41106a220b200541f8026a41106a29030037030020054190066a41186a2208200541f8026a41186a280200360200200520052f01b4053b01ac06200520052903f80237039006200541b0066a41286a22094100360200200541b0066a201d201d201b6a10f686808000200541b0076a41286a22062009280200360200200541b0076a41206a2209200541b0066a41206a290200370300200541b0076a41186a220c200541b0066a41186a290200370300200541b0076a41106a220d200541b0066a41106a290200370300200541b0076a41086a2210200541b0066a41086a290200370300200520052902b0063703b0072005202e3a00d801200520052f01ac063b00d901200520072d00003a00db01200541f4016a2008280200360200200541ec016a200b290300370200200541e4016a200429030037020020052005290390063702dc01200541a0026a200628020036020020054198026a200929030037020020054190026a200c29030037020020054188026a200d290300370200200541d8016a41286a2010290300370200200520052903b0073702f801200541a5026a200e3a0000200541a4026a201c3a0000200141dc006a200541d8016a10958d8080001a0b200541b0066a41286a2207200541c8036a41286a290300370300200541b0066a41206a2204200541c8036a41206a290300370300200541b0066a41186a220b200541c8036a41186a290300370300200541b0066a41106a2208200541c8036a41106a290300370300200541b0066a41086a2209200541c8036a41086a290300370300200520052903c8033703b006200541d8016a41086a200541b8056a41086a280200200541e4056a2206280200220c200c41284b220c1b360200200520052802bc05200541b8056a410472200c1b3602dc01200520052802b8053602d801200541b0066a200541d8016a10e78d808000200541a8026a2007290300370200200541a0026a200429030037020020054198026a200b29030037020020054190026a200829030037020020054188026a2009290300370200200541e4016a200541e8056a41086a290300370200200541ec016a200541e8056a41106a290300370200200541f4016a200541e8056a41186a290300370200200541fc016a200a280200360200200520052903b00637028002200520052903e8053702dc01200541063a00d80120002001200541d8016a20034101108a8680800020062802004129490d0220052802bc0541002802c0a3c68000118080808000000c020b200541e2076a41026a200541b4056a41026a2d00003a0000200541b8056a41086a200541f8026a41086a290300370300200541b8056a41106a200541f8026a41106a290300370300200541b8056a41186a200541f8026a41186a280200360200200520052f01b4053b01e207200520052903f8023703b805200541e6076a2104200541e9076a210e200541ec076a211b200541f0076a211c200541f4076a212f200541f8076a2141200541fc076a214220054180086a214320054184086a214420054188086a21452005418c086a214620054190086a214720054194086a214820054198086a21492005419c086a214a200541a0086a214b200541a4086a214c200541a8086a214d200541ac086a214e200541b0086a214f200541b4086a2150200541b8086a2151200541bc086a2152200541b8016a21532027212a20082127200d21082013210d201f21132023211f202621232025212620242125202221242021212220202121201e21202016211e20142116201221142011211220102111200c21102006210c20092106200a21092028210a200b2128202e210b0b2053201d2f00003b0000205220302f00003b0000205120312f00003b0000205020322f00003b0000204f20332f00003b0000205341026a201d41026a2d00003a0000205241026a203041026a2d00003a0000205141026a203141026a2d00003a0000205041026a203241026a2d00003a0000204f41026a203341026a2d00003a0000204e41026a203441026a2d00003a0000204e20342f00003b0000204d20352f00003b0000204d41026a203541026a2d00003a0000204c20362f00003b0000204c41026a203641026a2d00003a0000204b20372f00003b0000204b41026a203741026a2d00003a0000204a41026a203841026a2d00003a0000204a20382f00003b0000204941026a203941026a2d00003a0000204920392f00003b0000204841026a203a41026a2d00003a00002048203a2f00003b0000204741026a203b41026a2d00003a00002047203b2f00003b0000204641026a203c41026a2d00003a00002046203c2f00003b0000204541026a203d41026a2d00003a00002045203d2f00003b0000204441026a203e41026a2d00003a00002044203e2f00003b0000204341026a203f41026a2d00003a00002043203f2f00003b0000204241026a204041026a2d00003a0000204220402f00003b0000204141026a202d41026a2d00003a00002041202d2f00003b0000202f41026a202c41026a2d00003a0000202f202c2f00003b0000201c41026a202b41026a2d00003a0000201c202b2f00003b0000201b41026a202941026a2d00003a0000201b20292f00003b0000200e41026a200341026a2d00003a0000200e20032f00003b0000200441026a200741026a2d00003a0000200420072f00003b0000200541d8016a41286a200541c8036a41286a290300370300200541d8016a41206a200541c8036a41206a290300370300200541d8016a41186a200541c8036a41186a290300370300200541d8016a41106a200541c8036a41106a290300370300200541d8016a41086a200541c8036a41086a290300370300200520052903c8033703d801024002400240200141286a2802002207450d0020012007417f6a360228200141246a22072007280200220741016a220441002001411c6a280200220320042003491b6b360200200141206a28020020074102746a2802002204200141186a28020022074f0d02200128021420044107746a220741046a200720072d00004108461b10e3858080002007202a3a0000200720273a0004200720083a00082007200d3a000c200720052f00b8013b0001200741036a200541b8016a41026a2d00003a0000200720052f00bc083b0005200741076a200541bc086a41026a2d00003a0000200720052f00b8083b00092007410b6a200541b8086a41026a2d00003a0000200720052f00b4083b000d2007410f6a200541b4086a41026a2d00003a0000200720133a00102007201f3a0014200720233a0018200720263a001c200720052f00b0083b0011200741136a200541b0086a41026a2d00003a0000200720052f00ac083b0015200741176a200541ac086a41026a2d00003a0000200720052f00a8083b00192007411b6a200541a8086a41026a2d00003a0000200720052f00a4083b001d2007411f6a200541a4086a41026a2d00003a0000200720253a0020200720243a0024200720223a0028200720213a002c200720052f00a0083b0021200741236a200541a0086a41026a2d00003a0000200720052f009c083b0025200741276a2005419c086a41026a2d00003a0000200720052f0098083b00292007412b6a20054198086a41026a2d00003a0000200720052f0094083b002d2007412f6a20054194086a41026a2d00003a0000200720203a0030200741336a20054190086a41026a2d00003a0000200720052f0090083b00312007201e3a0034200741376a2005418c086a41026a2d00003a0000200720052f008c083b0035200720163a00382007413b6a20054188086a41026a2d00003a0000200720052f0088083b0039200720143a003c2007413f6a20054184086a41026a2d00003a0000200720052f0084083b003d200720123a0040200741c3006a20054180086a41026a2d00003a0000200720052f0080083b0041200720113a0044200741c7006a200541fc076a41026a2d00003a0000200720052f00fc073b0045200720103a0048200741cb006a200541f8076a41026a2d00003a0000200720052f00f8073b00492007200c3a004c200741cf006a200541f4076a41026a2d00003a0000200720052f00f4073b004d200720063a0050200741d3006a200541f0076a41026a2d00003a0000200720052f00f0073b0051200720093a0054200741d7006a200541ec076a41026a2d00003a0000200720052f00ec073b00552007200a3a0058200741db006a200541e9076a41026a2d00003a0000200720052f00e9073b0059200720283a005c200741df006a200541e6076a41026a2d00003a0000200720052f00e6073b005d2007200b3a0060200741e3006a200541e2076a41026a2d00003a0000200720052f01e2073b0061200741fc006a200541d0056a280200360000200741f4006a200541c8056a290300370000200741ec006a200541b8056a41086a290300370000200720052903b8053700640c010b0240200141186a28020022072001280210470d00200141106a2007109e86808000200128021821070b200128021420074107746a2207202a3a0000200720052f00b8013b0001200720273a0004200720052f00bc083b0005200720083a0008200720052f00b8083b00092007200d3a000c200720052f00b4083b000d200741036a200541b8016a41026a2d00003a0000200741076a200541bc086a41026a2d00003a00002007410b6a200541b8086a41026a2d00003a00002007410f6a200541b4086a41026a2d00003a0000200720133a00102007201f3a0014200720233a0018200720263a001c200720052f00b0083b0011200741136a200541b0086a41026a2d00003a0000200720052f00ac083b0015200741176a200541ac086a41026a2d00003a0000200720052f00a8083b00192007411b6a200541a8086a41026a2d00003a0000200720052f00a4083b001d2007411f6a200541a4086a41026a2d00003a0000200720253a0020200741236a200541a0086a41026a2d00003a0000200720052f00a0083b0021200720243a0024200741276a2005419c086a41026a2d00003a0000200720052f009c083b0025200720223a00282007412b6a20054198086a41026a2d00003a0000200720052f0098083b0029200720213a002c2007412f6a20054194086a41026a2d00003a0000200720052f0094083b002d200720203a0030200741336a20054190086a41026a2d00003a0000200720052f0090083b00312007201e3a0034200741376a2005418c086a41026a2d00003a0000200720052f008c083b0035200720163a00382007413b6a20054188086a41026a2d00003a0000200720052f0088083b0039200720143a003c2007413f6a20054184086a41026a2d00003a0000200720052f0084083b003d200720123a0040200741c3006a20054180086a41026a2d00003a0000200720052f0080083b0041200720113a0044200741c7006a200541fc076a41026a2d00003a0000200720052f00fc073b0045200720103a0048200741cb006a200541f8076a41026a2d00003a0000200720052f00f8073b00492007200c3a004c200741cf006a200541f4076a41026a2d00003a0000200720052f00f4073b004d200720063a0050200741d3006a200541f0076a41026a2d00003a0000200720052f00f0073b0051200720093a0054200741d7006a200541ec076a41026a2d00003a0000200720052f00ec073b00552007200a3a0058200741db006a200541e9076a41026a2d00003a0000200720052f00e9073b0059200720283a005c200741df006a200541e6076a41026a2d00003a0000200720052f00e6073b005d2007200b3a0060200741e3006a200541e2076a41026a2d00003a0000200720052f01e2073b0061200741fc006a200541d0056a280200360000200741f4006a200541c8056a290300370000200741ec006a200541c0056a290300370000200720052903b80537006420012001280218220441016a3602180b200020052903d801370228200020052f00b0073b0005200020052902b00637020c200041d0006a20054180026a290300370200200041c8006a200541f8016a290300370200200041c0006a200541d8016a41186a290300370200200041386a200541d8016a41106a290300370200200041306a200541d8016a41086a290300370200200041076a200541b0076a41026a2d00003a0000200041146a200541b0066a41086a2902003702002000411c6a200541b0066a41106a290200370200200041246a200541b0066a41186a280200360200200041063a0000200020052f00d8013b0001200041036a200541d8016a41026a2d00003a000020002004360208200041003a00040c020b2004200741d4d7c2800010f980808000000b20052d00d004417c6a41ff01712207410420074104491b417f6a4102490d00200541d0046a10e3858080000b200528028004450d00200f2802284129490d0020052802840441002802c0a3c68000118080808000000b20022d0000417c6a41ff01712207410420074104491b417e6a4103490d010b200210e3858080000b200541c0086a2480808080000bab1903057f017e177f2380808080004190046b2205248080808000024002400240024002400240024002400240024002400240024020022d00000d00200228020421020240200141286a28020022062001411c6a22072802002208470d00200710a889808000200128021c2108200128022821060b200141206a280200200141246a28020020066a22064100200820062008491b6b4102746a20023602002001200128022841016a360228200141186a280200220820024b0d012002200841e4d7c2800010f980808000000b200541a0016a200241196a29000037030020054198016a200241116a29000037030020054190016a200241096a29000037030020052002290001370388012003280208220841017621020240024020084101710d002002200328020422084b0d04200541003a00b003200520023602ac03200520032802003602a8030c010b2002200328020422084b0d04200220084f0d052005200328020022083602a803200520023602ac03200541b1036a200820026a2d000041f001713a0000200541013a00b0030b200541c4026a200120054188016a200541a8036a10868680800020052802c802210220052802c4020d010240200141286a28020022062001411c6a22072802002208470d00200710a889808000200128021c2108200128022821060b200141206a280200200141246a28020020066a22064100200820062008491b6b4102746a20023602002001200128022841016a360228200141186a280200220820024d0d090b200141146a28020020024107746a22022d00002108200541296a200241016a41df0010848e8080001a200241043a0004200241083a0000200541086a41086a200241ec006a290200370300200541186a200241f4006a290200370300200541206a200241fc006a2802003602002005200229026437030820022802602102200541a9016a200541296a41df0010848e8080001a02400240024002400240024020084108470d00200541a8036a200541a9016a41036a41dc0010848e8080001a20052001360288042005200336028c042005200236028404200541c4026a20042001200541a8036a2003108c8680800020052802c40222034103460d0520052802a4032109200529029c03210a20052802980321062005280294032107200528029003210b200528028c03210c200528028803210d200528028403210e200528028003210f20052802fc02211020052802f802211120052802f402211220052802f002211320052802ec02211420052802e802211520052802e402211620052802e002211720052802dc02211820052802d802211920052802d402211a20052802d002211b20052802cc02211c20052802c8022108410121044108211d20030e03030201030b20032802082109200328020421192003280200211d200541a8036a410172200541296a41df0010848e8080001a20052001360288042005200336028c04200520083a00a803200541c4026a20042001200541a8036a2003108c8680800020052802c40222034103460d0420052902a003210a200528029c03211e20052802980321062005280294032107200528029003210b200528028c03210c200528028803210d200528028403210e200528028003210f20052802fc02211020052802f802211120052802f402211220052802f002211320052802ec02211420052802e802211520052802e402211620052802e002211720052802dc02211820052802d802211a20052802d402211b20052802d002211c20052802cc02211f20052802c802210802400240024020030e03000102000b200941017621040240024020094101710d000240200420194b0d00410021190c020b2004201941f492c68000109581808000000b200420194b0d0c200420194f0d0d201d20046a2d00004170712120410121190b200a422088a7210941002103200541c4026a41286a22214100360200200541c4026a201d201d20046a10f58d808000200541c8036a220441286a2021280200360200200441206a200541c4026a41206a290200370200200441186a200541c4026a41186a290200370200200441106a200541c4026a41106a2902003702004108211d200441086a200541c4026a41086a290200370200200420052902c402370200200541f5036a20203a0000200541f4036a20193a0000200541b4036a200541086a41086a290300370200200541bc036a200541086a41106a290300370200200541c4036a200541086a41186a280200360200200520023602a803200520052903083702ac03200141dc006a200541a8036a10958d8080001a200a422086201ead84210a41012104201a2119201b211a201c211b201f211c0c040b200541a8026a41186a200541086a41186a280200360200200541a8026a41106a200541086a41106a290300370300200541a8026a41086a200541086a41086a290300370300200520052903083703a802200841ff0171211d200841807e71210341002104201821192017211820162117201521162014211520132114201221132011211220102111200f2110200e210f200d210e200c210d200b210c2007210b20062107201e210620022109201f21080c030b200941017621030240024020094101710d000240200320194b0d00410021040c020b2003201941f492c68000109581808000000b200320194b0d0c200320194f0d0d201d20036a2d00004170712108410121040b200541c4026a41286a22064100360200200541c4026a201d201d20036a10f58d808000200541c8036a220341286a2006280200360200200341206a200541c4026a41206a290200370200200341186a200541c4026a41186a290200370200200341106a200541c4026a41106a290200370200200341086a200541c4026a41086a290200370200200320052902c402370200200541f5036a20083a0000200541f4036a20043a0000200541b4036a200541086a41086a290300370200200541bc036a200541086a41106a290300370200200541c4036a200541086a41186a280200360200200520023602a803200520052903083702ac03200141dc006a200541a8036a10958d8080001a0b410221040c020b41002103410021040b20054188026a41186a200541a8026a41186a28020036020020054188026a41106a200541a8026a41106a29030037030020054188026a41086a200541a8026a41086a290300370300200520052903a80237038802201d20037221030240200141286a2802002202450d0020012002417f6a360228200141246a22022002280200220241016a221d41002001411c6a280200221e201d201e491b6b360200200141206a28020020024102746a2802002202200141186a280200221d4f0d0c200128021420024107746a220141046a200120012d00004108461b10e385808000200120093602602001200a37025820012006360254200120073602502001200b36024c2001200c3602482001200d3602442001200e3602402001200f36023c2001201036023820012011360234200120123602302001201336022c2001201436022820012015360224200120163602202001201736021c20012018360218200120193602142001201a3602102001201b36020c2001201c3602082001200836020420012003360200200141fc006a200541a0026a280200360200200141f4006a20054198026a290300370200200141ec006a20054188026a41086a29030037020020012005290388023702640c010b0240200141186a28020022022001280210470d00200141106a2002109e86808000200128021821020b200128021420024107746a220220093602602002200a37025820022006360254200220073602502002200b36024c2002200c3602482002200d3602442002200e3602402002200f36023c2002201036023820022011360234200220123602302002201336022c2002201436022820022015360224200220163602202002201736021c20022018360218200220193602142002201a3602102002201b36020c2002201c3602082002200836020420022003360200200241fc006a200541a0026a280200360200200241f4006a20054198026a290300370200200241ec006a20054190026a290300370200200220052903880237026420012001280218220241016a3602180b200020043a0004200020023602000c0b0b20052802c80221020b200041033a0004200020023602000c090b2002200841f492c68000109581808000000b20022008418493c68000109581808000000b20022008419493c6800010f980808000000b20042019418493c68000109581808000000b20042019419493c6800010f980808000000b20032019418493c68000109581808000000b20032019419493c6800010f980808000000b2002200841e4d7c2800010f980808000000b2002201d41d4d7c2800010f980808000000b20054190046a2480808080000be54502117f017e23808080800041a0046b2205248080808000200541086a200441086a28020022063602002005200429020037030020052802042107200541e4006a200341e00010848e8080001a41012108200520062007410174220946220a3a00c4014101210b4102210c024002400240024002400240024002400240024002400240024002400240024002400240024002400240024020052d0064220d417c6a41ff0171220e4104200e4104491b0e051400010203140b200541c0036a41286a200541bc016a290200370300200541c0036a41206a200541b4016a290200370300200541c0036a41186a200541ac016a290200370300200541c0036a41106a200541a4016a290200370300200541c0036a41086a220f2005419c016a290200370300200541b0026a41086a200541f8006a290200370300200541b0026a41106a220b20054180016a290200370300200541b0026a41186a220e20054188016a28020036020020052005290294013703c0032005200541f0006a2902003703b002200541e4006a41086a2802002110200541e9006a2f00002103200541eb006a2d0000210720052d00682111200541e4006a412c6a2802002112200541e4006a41286a2802002113200520052802c003221436028002200520052802c403200541c0036a4104722215200541c0036a412c6a280200220c41284b22081b3602f8012005200f280200200c20081b22083602fc014101210c2003200741107472210f02400240200841017420146b2203200920066b470d00200541f8016a200510db8d8080002003460d0120052802c00321140b200541106a41286a201541286a280200360200200541106a41206a201541206a290200370300200541106a41186a201541186a290200370300200541106a41106a201541106a290200370300200541106a41086a201541086a290200370300200541c0006a41086a200541b0026a41086a290300370300200541c0006a41106a200b290300370300200541c0006a41186a200e280200360200200520052903b00237034020052015290200370310410521150c130b200428020421142004280200210c20042802082104200528028002211520052802fc012103200541e0026a41106a200541b8026a290300370200200541e0026a41186a200541b0026a41106a29030037020020054180036a200541b0026a41186a280200360200200520103602e402200520113a00e002200520052903b0023702e802200520123602880320052013360284032005200f3b00e1022005200f4110763a00e3022004200341017420156b6a221541017621040240024020154101710d00200420144b0d06200541003a00d001200520043602cc012005200c3602c8010c010b200420144b0d06200420144f0d072005200c3602c801200520043602cc01200541d1016a200c20046a2d000041f001713a0000200541013a00d0010b20022001200541e0026a200541c8016a1083868080004102210c024020052802ec0341294f0d00410521150c130b20052802c40341002802c0a3c6800011808080800000410521150c120b200541b0026a41286a200541b4016a290200370300200541b0026a41206a200541ac016a290200370300200541b0026a41186a200541a4016a290200370300200541b0026a41106a2005419c016a290200370300200541b0026a41086a220f20054194016a290200370300200541c8016a41086a200541f0006a290200370300200541c8016a41106a2214200541e4006a41146a290200370300200541c8016a41186a2211200541e4006a411c6a290200370300200541c8016a41206a2210200541e4006a41246a2802003602002005200529028c013703b002200520052902683703c801200520052802b0023602c8032005200f280200200541dc026a2212280200220f200f41284b220f1b3602c403200520052802b402200541b0026a410472200f1b3602c0034101210c0240200541c0036a200510db8d808000220f20052802c40341017420052802c8036b460d00200541106a41086a200541b0026a41146a290200370300200541106a41106a200541b0026a411c6a290200370300200541106a41186a200541b0026a41246a290200370300200541106a41206a2012280200360200200541c0006a41086a2014290300370300200541c0006a41106a2011290300370300200541c0006a41186a2010280200360200200520052902bc02370310200520052903d00137034020052f00c90120052d00cb0141107472210f4106211520052802cc01211020052d00c801211120052802b802211420052802b402211220052802b00221130c120b200541f8016a41086a200441086a2214280200221136020020142011200f6a360200200520042902003703f801200541c0036a2002200541c8016a20042001108b8680800020052802c00321100240024002400240024020052d00c4030e0400010204010b200541106a41086a200541c4026a290200370300200541206a200541cc026a290200370300200541286a200541d4026a290200370300200541306a200541dc026a280200360200200520052902bc02370310410021114106211520052802b802211420052802b402211220052802b00221130c150b200541c0036a41086a201036020020054190046a200541b0026a41286a29030037020020054188046a200541b0026a41206a29030037020020054180046a200541b0026a41186a290300370200200541f8036a200541b0026a41106a290300370200200541f0036a200541b0026a41086a290300370200200520052903b0023702e8034100210c200541003a00c403200541063a00c003200541e0026a2002200541c0036a200541f8016a4100108a8680800020052d00e00222154108460d01200541e2006a20052d00e3023a0000200541c0006a41086a200541f4026a290200370300200541c0006a41106a200541fc026a290200370300200541c0006a41186a20054184036a280200360200200520052f00e1023b0160200520052902ec0237034020052802e80221102005280288032113200528028c032112200528029003211420052802e4022111200541106a41286a200541bc036a280200360200200541106a41206a200541b4036a290200370300200541106a41186a200541ac036a290200370300200541106a41106a200541a4036a290200370300200541106a41086a2005419c036a29020037030020052005290294033703102011410876210f0c140b4102210c024020052802dc0241294f0d000c140b20052802b40241002802c0a3c68000118080808000000c130b20052802e402210420004103360200200020043602040c140b200041033602002000201036020420052802dc024129490d1320052802b40241002802c0a3c68000118080808000000c130b024002400240024002400240200a0d002005280294012114200541d8026a20054190016a280200360200200541d0026a200541e4006a41246a290200370300200541c8026a20054180016a290200370300200541c0026a200541f8006a290200370300200541b8026a200541f0006a290200370300200520052902683703b0022006410176220f20074f0d0120142005280200200f6a2d0000220f410f71200f41047620064101711b41246c6a220f2d00002111200f41023a000020114102460d02200541c8016a41096a200f41096a290000370000200541c8016a41116a200f41116a290000370000200541c8016a41196a200f41196a290000370000200541c8016a41206a200f41206a280000360000200541f8016a41086a200441086a2210280200220c360200200f29000121162010200c41016a360200200520113a00c801200520163700c901200520042902003703f801200541c0036a2002200541c8016a20042001108b8680800020052802c003210420052d00c4032211417e6a0e020407030b41032111200528029401211420052d00684103470d0441012108410721154101210b4101210c0c160b200f2007418cd5c2800010f980808000000b200541c8006a200541b0026a41106a290300370300200541c0006a41106a200541b0026a41186a290300370300200541c0006a41186a200541d0026a280200360200200520052903b80237034020052f00b10220052d00b30241107472210f4101210c0c110b200f2004360204200f41003a0000200541c0006a41086a200541b0026a41106a290300370300200541c0006a41106a200541b0026a41186a290300370300200541c0006a41186a200541b0026a41206a280200360200200520052903b80237034020052f00b10220052d00b30241107472210f201145210c0c100b200541ec036a200541b0026a41286a280200360200200541c0036a41246a200541b0026a41206a290300370200200541c0036a411c6a200541b0026a41186a290300370200200541c0036a41146a200541b0026a41106a290300370200200541cc036a200541b0026a41086a290300370200200520052903b0023702c403200520143602f003200541073a00c0034100210c200541e0026a2002200541c0036a200541f8016a4100108a86808000024020052d00e00222154108460d00200541e2006a20052d00e3023a0000200541c0006a41086a200541e0026a41146a290200370300200541c0006a41106a200541e0026a411c6a290200370300200541c0006a41186a200541e0026a41246a280200360200200520052f00e1023b0160200520052902ec0237034020052802e80221102005280288032113200528028c032112200528029003211420052802e4022111200541106a41286a200541bc036a280200360200200541306a200541b4036a290200370300200541106a41186a200541ac036a290200370300200541106a41106a200541a4036a290200370300200541106a41086a2005419c036a29020037030020052005290294033703102011410876210f0c110b20052802e402210420004103360200200020043602040c130b200541b0026a41286a200541e8006a220f41286a280200360200200541b0026a41206a200f41206a290200370300200541b0026a41186a200f41186a290200370300200541b0026a41106a200f41106a290200370300200541b0026a41086a200f41086a2902003703002005200f2902003703b00220042802082211410176210f0240024020114101710d00200f200428020422114b0d07200541003a00c8032005200f3602c403200520042802003602c0030c010b200f200428020422114b0d07200f20114f0d082005200428020022113602c0032005200f3602c403200541c9036a2011200f6a2d000041f001713a0000200541013a00c8030b20022001200541b0026a200541c0036a108386808000200541033a00c403200520143602f003200541073a00c003200541c8016a41086a200441086a280200360200200520042902003703c8014100210b200541e0026a2002200541c0036a200541c8016a4100108a86808000024020052d00e00222154108460d00200541e2006a20052d00e3023a0000200541c0006a41086a200541f4026a290200370300200541c0006a41106a200541fc026a290200370300200541c0006a41186a20054184036a280200360200200520052f00e1023b0160200520052902ec0237034020052802e80221102005280288032113200528028c032112200528029003211420052802e4022111200541386a200541bc036a280200360200200541306a200541b4036a290200370300200541106a41186a200541ac036a290200370300200541106a41106a200541a4036a290200370300200541106a41086a2005419c036a29020037030020052005290294033703102011410876210f410121084100210c0c120b20052802e402210420004103360200200020043602040c120b20054194016a210f0240200a0d00200541b0026a41286a200f41286a290200370300200541b0026a41206a200f41206a290200370300200541b0026a41186a200f41186a290200370300200541b0026a41106a200f41106a290200370300200541b0026a41086a2214200f41086a2902003703002005200f2902003703b0022005280290012112200541c8016a41286a200341286a280200360200200541c8016a41206a200341206a290200370300200541c8016a41186a200341186a290200370300200541c8016a41106a200341106a290200370300200541c8016a41086a200341086a290200370300200520032902003703c801200520052802b0023602c80320052014280200200541dc026a280200220f200f41284b22141b3602c403200520052802b402200541b0026a410472220f20141b3602c003200541c0036a200510db8d808000221420052802c40341017420052802c8036b2211470d0c2014200528020441017420052802086b470d0c41032115024020052d00c8014103470d00200541106a41086a200f41086a290200370300200541106a41106a200f41106a290200370300200541106a41186a200f41186a290200370300200541106a41206a200f41206a290200370300200541106a41286a200f41286a2802003602002005200f2902003703104101210c20052802b00221140c0e0b200541c0036a41286a200541c8016a41286a280200360200200541c0036a41206a200541c8016a41206a290300370300200541c0036a41186a200541c8016a41186a290300370300200541c0036a41106a200541c8016a41106a290300370300200541c0036a41086a200541c8016a41086a290300370300200520052903c8013703c003200541a0026a41086a200441086a220f28020020146a360200200520042902003703a002200541e0026a200541a0026a10dd8d80800020022001200541c0036a200541e0026a10838680800020054198046a200541b0026a41286a29030037020020054190046a200541b0026a41206a29030037020020054188046a200541b0026a41186a29030037020020054180046a200541b0026a41106a290300370200200541f8036a200541b0026a41086a290300370200200520052903b0023702f003200520123602ec03200541033a00c003200541f8016a41086a200f280200360200200520042902003703f8014100210c200541e0026a2002200541c0036a200541f8016a4100108a86808000024020052d00e00222154108460d00200541e2006a20052d00e3023a0000200541c0006a41086a200541f4026a290200370300200541c0006a41106a200541fc026a290200370300200541c0006a41186a20054184036a280200360200200520052f00e1023b0160200520052902ec0237034020052802e80221102005280288032113200528028c032112200528029003211420052802e4022111200541106a41286a200541bc036a280200360200200541106a41206a200541b4036a290200370300200541106a41186a200541ac036a290200370300200541106a41106a200541a4036a290200370300200541106a41086a2005419c036a29020037030020052005290294033703102011410876210f0c0e0b200020052802e402360204200041033602000c120b41032115200d41ff01714103460d0a20054198046a200f41286a29020037020020054190046a200f41206a29020037020020054188046a200f41186a29020037020020054180046a200f41106a290200370200200541f8036a200f41086a2902003702002005200f2902003702f003200528029001211120042802082214410176210f0240024020144101710d00200f200428020422144b0d09200541003a00e8022005200f3602e402200520042802003602e0020c010b200f200428020422144b0d09200f20144f0d0a2005200428020022143602e0022005200f3602e402200541e9026a2014200f6a2d000041f001713a0000200541013a00e8020b200220012003200541e0026a108386808000200520113602ec03200541033a00c003200541b0026a41086a200441086a280200360200200520042902003703b00241002108200541e0026a2002200541c0036a200541b0026a4100108a86808000024020052d00e00222154108460d00200541e2006a20052d00e3023a0000200541c0006a41086a200541f4026a290200370300200541c0006a41106a200541fc026a290200370300200541c0006a41186a20054184036a280200360200200520052f00e1023b0160200520052902ec0237034020052802e80221102005280288032113200528028c032112200528029003211420052802e4022111200541386a200541bc036a280200360200200541306a200541b4036a290200370300200541106a41186a200541ac036a290200370300200541106a41106a200541a4036a290200370300200541106a41086a2005419c036a29020037030020052005290294033703102011410876210f4101210b4100210c0c110b20052802e402210420004103360200200020043602040c110b2000410336020020002004360204200541b0026a10e585808000201441002802c0a3c68000118080808000000c100b2004201441f492c68000109581808000000b20042014418493c68000109581808000000b20042014419493c6800010f980808000000b200f201141f492c68000109581808000000b200f2011418493c68000109581808000000b200f2011419493c6800010f980808000000b200f201441f492c68000109581808000000b200f2014418493c68000109581808000000b200f2014419493c6800010f980808000000b200541186a200541a0016a290200370300200541206a200541a8016a290200370300200541286a200541b0016a290200370300200541306a200541b8016a290200370300200541386a200541c0016a280200360200200520054198016a29020037031041012108200528029001211220052802940121144101210b4101210c0c050b0240024002400240024002400240024020142011490d00200528020820146a221041017622112005280204220c4f0d012012200528020020116a2d00002211410f71201141047620104101711b41246c6a22112d00002110201141023a000020104102460d02200541f8016a41096a201141096a290000370000200541f8016a41116a201141116a290000370000200541f8016a41196a201141196a290000370000200541f8016a41206a201141206a280000360000200541a0026a41086a200441086a220c280200221536020020112900012116200c201420156a41016a360200200520103a00f801200520163700f901200520042902003703a002200541c0036a2002200541f8016a20042001108b8680800020052802c003210420052d00c403220c417e6a0e020304060b200541e2006a20052d00cb013a0000200541106a41086a200f41086a290200370300200541106a41106a200f41106a290200370300200541106a41186a200f41186a290200370300200541106a41206a200f41206a290200370300200541106a41286a200f41286a280200360200200520052f00c9013b01602005200f29020037031020052802b002211420052d00c8012115200541c0006a41086a200541dc016a290200370300200541c0006a41106a200541e4016a290200370300200541c0006a41186a200541ec016a280200360200200520052902d40137034020052f00cd0120052d00cf0141107472210f0c060b2011200c418cd5c2800010f980808000000b200541e0006a41026a20052d00cb013a0000200541106a41086a200f41086a290200370300200541106a41106a200f41106a290200370300200541106a41186a200f41186a290200370300200541106a41206a200f41206a290200370300200541106a41286a200f41286a280200360200200520052f00c9013b01602005200f29020037031020052802b002211420052d00c8012115200541c0006a41086a200541dc016a290200370300200541c0006a41106a200541e4016a290200370300200541c0006a41186a200541c8016a41246a280200360200200520052902d40137034020052f00cd0120052d00cf0141107472210f0c040b20054198046a200541b0026a41286a29030037030020054190046a200541b0026a41206a29030037030020054188046a200541b0026a41186a29030037030020054180046a200541b0026a41106a290300370300200541f8036a200541b0026a41086a290300370300200541c0036a41086a200541c8016a41086a290300370300200541c0036a41106a200541c8016a41106a290300370300200541c0036a41186a200541c8016a41186a290300370300200541c0036a41206a200541c8016a41206a290300370300200541c0036a41286a200541c8016a41286a280200360200200520052903b0023703f003200520052903c8013703c003200520123602ec034100210c200541e0026a2002200541c0036a200541a0026a4100108a8680800020052d00e00222154108460d01200541e2006a20052d00e3023a0000200541c0006a41086a200541f4026a290200370300200541c0006a41106a200541fc026a290200370300200541c0006a41186a20054184036a280200360200200520052f00e1023b0160200520052902ec0237034020052802e80221102005280288032113200528028c032112200528029003211420052802e4022111200541106a41286a200541bc036a280200360200200541306a200541b4036a290200370300200541106a41186a200541ac036a290200370300200541106a41106a200541a4036a290200370300200541106a41086a2005419c036a29020037030020052005290294033703102011410876210f0c040b2000410336020020002004360204024020052d00c8014103460d00200541c8016a10e4858080000b201241002802c0a3c680001180808080000020052802dc024129490d0820052802b40241002802c0a3c68000118080808000000c080b20052802e402210420004103360200200020043602040c070b20112004360204201141003a0000200541e2006a20052d00cb013a0000200541c0006a41086a200541dc016a290200370300200541c0006a41106a200541e4016a290200370300200541c0006a41186a200541ec016a280200360200200520052f00c9013b0160200520052902d40137034020052802f001211320052802d001211020052d00cc01211120052d00c801211520052802b002211420052f00cd01210420052d00cf012103200541106a41286a200f41286a280200360200200541106a41206a200f41206a290200370300200541106a41186a200f41186a290200370300200541106a41106a200f41106a290200370300200541106a41086a200f41086a2902003703002005200f2902003703102004200341107472210f200c45210c0c010b4101210c20052802f001211320052802d001211020052d00cc0121110b4101210b410021080c030b4107211520052802d802211220052802d402211320052802b402211020052d00b00221110b4100210b410121080c010b410121084101210b0b2000200f3b0009200020153a00042000200c360200200020052f01603b00052000201036020c200020113a0008200020052903403702102000201336022c20002012360230200020143602342000410b6a200f4110763a0000200041076a200541e2006a2d00003a0000200041186a200541c0006a41086a290300370200200041206a200541c0006a41106a290300370200200041286a200541c0006a41186a280200360200200041e0006a200541106a41286a280200360200200041d8006a200541106a41206a290300370200200041d0006a200541106a41186a290300370200200041c8006a200541106a41106a290300370200200041c0006a200541106a41086a29030037020020002005290310370238024002400240024020052d00642204417c6a41ff01712200410420004104491b417d6a0e020001040b200b450d0320052d006822004103460d03024020000e020004020b200541ec006a2200280200220420042802002204417f6a36020020044101460d020c030b2008450d0220044103460d020240024020040e020104000b200528028801220020002802002200417f6a36020020004101470d0320054188016a10e28a8080000c030b2005280268220020002802002200417f6a36020020004101470d02200541e8006a10e28a8080000c020b2005418c016a2200280200220420042802002204417f6a36020020044101470d010b200010e28a8080000b200541a0046a2480808080000bab1903057f017e177f2380808080004190046b2205248080808000024002400240024002400240024002400240024002400240024020022d00000d00200228020421020240200141286a28020022062001411c6a22072802002208470d00200710a889808000200128021c2108200128022821060b200141206a280200200141246a28020020066a22064100200820062008491b6b4102746a20023602002001200128022841016a360228200141186a280200220820024b0d012002200841e4d7c2800010f980808000000b200541a0016a200241196a29000037030020054198016a200241116a29000037030020054190016a200241096a29000037030020052002290001370388012003280208220841017621020240024020084101710d002002200328020422084b0d04200541003a00b003200520023602ac03200520032802003602a8030c010b2002200328020422084b0d04200220084f0d052005200328020022083602a803200520023602ac03200541b1036a200820026a2d000041f001713a0000200541013a00b0030b200541c4026a200120054188016a200541a8036a10878680800020052802c802210220052802c4020d010240200141286a28020022062001411c6a22072802002208470d00200710a889808000200128021c2108200128022821060b200141206a280200200141246a28020020066a22064100200820062008491b6b4102746a20023602002001200128022841016a360228200141186a280200220820024d0d090b200141146a28020020024107746a22022d00002108200541296a200241016a41df0010848e8080001a200241043a0004200241083a0000200541086a41086a200241ec006a290200370300200541186a200241f4006a290200370300200541206a200241fc006a2802003602002005200229026437030820022802602102200541a9016a200541296a41df0010848e8080001a02400240024002400240024020084108470d00200541a8036a200541a9016a41036a41dc0010848e8080001a20052001360288042005200336028c042005200236028404200541c4026a20042001200541a8036a2003108e8680800020052802c40222034103460d0520052802a4032109200529029c03210a20052802980321062005280294032107200528029003210b200528028c03210c200528028803210d200528028403210e200528028003210f20052802fc02211020052802f802211120052802f402211220052802f002211320052802ec02211420052802e802211520052802e402211620052802e002211720052802dc02211820052802d802211920052802d402211a20052802d002211b20052802cc02211c20052802c8022108410121044108211d20030e03030201030b20032802082109200328020421192003280200211d200541a8036a410172200541296a41df0010848e8080001a20052001360288042005200336028c04200520083a00a803200541c4026a20042001200541a8036a2003108e8680800020052802c40222034103460d0420052902a003210a200528029c03211e20052802980321062005280294032107200528029003210b200528028c03210c200528028803210d200528028403210e200528028003210f20052802fc02211020052802f802211120052802f402211220052802f002211320052802ec02211420052802e802211520052802e402211620052802e002211720052802dc02211820052802d802211a20052802d402211b20052802d002211c20052802cc02211f20052802c802210802400240024020030e03000102000b200941017621040240024020094101710d000240200420194b0d00410021190c020b2004201941f492c68000109581808000000b200420194b0d0c200420194f0d0d201d20046a2d00004170712120410121190b200a422088a7210941002103200541c4026a41286a22214100360200200541c4026a201d201d20046a10f58d808000200541c8036a220441286a2021280200360200200441206a200541c4026a41206a290200370200200441186a200541c4026a41186a290200370200200441106a200541c4026a41106a2902003702004108211d200441086a200541c4026a41086a290200370200200420052902c402370200200541f5036a20203a0000200541f4036a20193a0000200541b4036a200541086a41086a290300370200200541bc036a200541086a41106a290300370200200541c4036a200541086a41186a280200360200200520023602a803200520052903083702ac03200141dc006a200541a8036a10958d8080001a200a422086201ead84210a41012104201a2119201b211a201c211b201f211c0c040b200541a8026a41186a200541086a41186a280200360200200541a8026a41106a200541086a41106a290300370300200541a8026a41086a200541086a41086a290300370300200520052903083703a802200841ff0171211d200841807e71210341002104201821192017211820162117201521162014211520132114201221132011211220102111200f2110200e210f200d210e200c210d200b210c2007210b20062107201e210620022109201f21080c030b200941017621030240024020094101710d000240200320194b0d00410021040c020b2003201941f492c68000109581808000000b200320194b0d0c200320194f0d0d201d20036a2d00004170712108410121040b200541c4026a41286a22064100360200200541c4026a201d201d20036a10f58d808000200541c8036a220341286a2006280200360200200341206a200541c4026a41206a290200370200200341186a200541c4026a41186a290200370200200341106a200541c4026a41106a290200370200200341086a200541c4026a41086a290200370200200320052902c402370200200541f5036a20083a0000200541f4036a20043a0000200541b4036a200541086a41086a290300370200200541bc036a200541086a41106a290300370200200541c4036a200541086a41186a280200360200200520023602a803200520052903083702ac03200141dc006a200541a8036a10958d8080001a0b410221040c020b41002103410021040b20054188026a41186a200541a8026a41186a28020036020020054188026a41106a200541a8026a41106a29030037030020054188026a41086a200541a8026a41086a290300370300200520052903a80237038802201d20037221030240200141286a2802002202450d0020012002417f6a360228200141246a22022002280200220241016a221d41002001411c6a280200221e201d201e491b6b360200200141206a28020020024102746a2802002202200141186a280200221d4f0d0c200128021420024107746a220141046a200120012d00004108461b10e385808000200120093602602001200a37025820012006360254200120073602502001200b36024c2001200c3602482001200d3602442001200e3602402001200f36023c2001201036023820012011360234200120123602302001201336022c2001201436022820012015360224200120163602202001201736021c20012018360218200120193602142001201a3602102001201b36020c2001201c3602082001200836020420012003360200200141fc006a200541a0026a280200360200200141f4006a20054198026a290300370200200141ec006a20054188026a41086a29030037020020012005290388023702640c010b0240200141186a28020022022001280210470d00200141106a2002109e86808000200128021821020b200128021420024107746a220220093602602002200a37025820022006360254200220073602502002200b36024c2002200c3602482002200d3602442002200e3602402002200f36023c2002201036023820022011360234200220123602302002201336022c2002201436022820022015360224200220163602202002201736021c20022018360218200220193602142002201a3602102002201b36020c2002201c3602082002200836020420022003360200200241fc006a200541a0026a280200360200200241f4006a20054198026a290300370200200241ec006a20054190026a290300370200200220052903880237026420012001280218220241016a3602180b200020043a0004200020023602000c0b0b20052802c80221020b200041033a0004200020023602000c090b2002200841f492c68000109581808000000b20022008418493c68000109581808000000b20022008419493c6800010f980808000000b20042019418493c68000109581808000000b20042019419493c6800010f980808000000b20032019418493c68000109581808000000b20032019419493c6800010f980808000000b2002200841e4d7c2800010f980808000000b2002201d41d4d7c2800010f980808000000b20054190046a2480808080000be54502117f017e23808080800041a0046b2205248080808000200541086a200441086a28020022063602002005200429020037030020052802042107200541e4006a200341e00010848e8080001a41012108200520062007410174220946220a3a00c4014101210b4102210c024002400240024002400240024002400240024002400240024002400240024002400240024002400240024020052d0064220d417c6a41ff0171220e4104200e4104491b0e051400010203140b200541c0036a41286a200541bc016a290200370300200541c0036a41206a200541b4016a290200370300200541c0036a41186a200541ac016a290200370300200541c0036a41106a200541a4016a290200370300200541c0036a41086a220f2005419c016a290200370300200541b0026a41086a200541f8006a290200370300200541b0026a41106a220b20054180016a290200370300200541b0026a41186a220e20054188016a28020036020020052005290294013703c0032005200541f0006a2902003703b002200541e4006a41086a2802002110200541e9006a2f00002103200541eb006a2d0000210720052d00682111200541e4006a412c6a2802002112200541e4006a41286a2802002113200520052802c003221436028002200520052802c403200541c0036a4104722215200541c0036a412c6a280200220c41284b22081b3602f8012005200f280200200c20081b22083602fc014101210c2003200741107472210f02400240200841017420146b2203200920066b470d00200541f8016a200510db8d8080002003460d0120052802c00321140b200541106a41286a201541286a280200360200200541106a41206a201541206a290200370300200541106a41186a201541186a290200370300200541106a41106a201541106a290200370300200541106a41086a201541086a290200370300200541c0006a41086a200541b0026a41086a290300370300200541c0006a41106a200b290300370300200541c0006a41186a200e280200360200200520052903b00237034020052015290200370310410521150c130b200428020421142004280200210c20042802082104200528028002211520052802fc012103200541e0026a41106a200541b8026a290300370200200541e0026a41186a200541b0026a41106a29030037020020054180036a200541b0026a41186a280200360200200520103602e402200520113a00e002200520052903b0023702e802200520123602880320052013360284032005200f3b00e1022005200f4110763a00e3022004200341017420156b6a221541017621040240024020154101710d00200420144b0d06200541003a00d001200520043602cc012005200c3602c8010c010b200420144b0d06200420144f0d072005200c3602c801200520043602cc01200541d1016a200c20046a2d000041f001713a0000200541013a00d0010b20022001200541e0026a200541c8016a1083868080004102210c024020052802ec0341294f0d00410521150c130b20052802c40341002802c0a3c6800011808080800000410521150c120b200541b0026a41286a200541b4016a290200370300200541b0026a41206a200541ac016a290200370300200541b0026a41186a200541a4016a290200370300200541b0026a41106a2005419c016a290200370300200541b0026a41086a220f20054194016a290200370300200541c8016a41086a200541f0006a290200370300200541c8016a41106a2214200541e4006a41146a290200370300200541c8016a41186a2211200541e4006a411c6a290200370300200541c8016a41206a2210200541e4006a41246a2802003602002005200529028c013703b002200520052902683703c801200520052802b0023602c8032005200f280200200541dc026a2212280200220f200f41284b220f1b3602c403200520052802b402200541b0026a410472200f1b3602c0034101210c0240200541c0036a200510db8d808000220f20052802c40341017420052802c8036b460d00200541106a41086a200541b0026a41146a290200370300200541106a41106a200541b0026a411c6a290200370300200541106a41186a200541b0026a41246a290200370300200541106a41206a2012280200360200200541c0006a41086a2014290300370300200541c0006a41106a2011290300370300200541c0006a41186a2010280200360200200520052902bc02370310200520052903d00137034020052f00c90120052d00cb0141107472210f4106211520052802cc01211020052d00c801211120052802b802211420052802b402211220052802b00221130c120b200541f8016a41086a200441086a2214280200221136020020142011200f6a360200200520042902003703f801200541c0036a2002200541c8016a20042001108d8680800020052802c00321100240024002400240024020052d00c4030e0400010204010b200541106a41086a200541c4026a290200370300200541206a200541cc026a290200370300200541286a200541d4026a290200370300200541306a200541dc026a280200360200200520052902bc02370310410021114106211520052802b802211420052802b402211220052802b00221130c150b200541c0036a41086a201036020020054190046a200541b0026a41286a29030037020020054188046a200541b0026a41206a29030037020020054180046a200541b0026a41186a290300370200200541f8036a200541b0026a41106a290300370200200541f0036a200541b0026a41086a290300370200200520052903b0023702e8034100210c200541003a00c403200541063a00c003200541e0026a2002200541c0036a200541f8016a410010888680800020052d00e00222154108460d01200541e2006a20052d00e3023a0000200541c0006a41086a200541f4026a290200370300200541c0006a41106a200541fc026a290200370300200541c0006a41186a20054184036a280200360200200520052f00e1023b0160200520052902ec0237034020052802e80221102005280288032113200528028c032112200528029003211420052802e4022111200541106a41286a200541bc036a280200360200200541106a41206a200541b4036a290200370300200541106a41186a200541ac036a290200370300200541106a41106a200541a4036a290200370300200541106a41086a2005419c036a29020037030020052005290294033703102011410876210f0c140b4102210c024020052802dc0241294f0d000c140b20052802b40241002802c0a3c68000118080808000000c130b20052802e402210420004103360200200020043602040c140b200041033602002000201036020420052802dc024129490d1320052802b40241002802c0a3c68000118080808000000c130b024002400240024002400240200a0d002005280294012114200541d8026a20054190016a280200360200200541d0026a200541e4006a41246a290200370300200541c8026a20054180016a290200370300200541c0026a200541f8006a290200370300200541b8026a200541f0006a290200370300200520052902683703b0022006410176220f20074f0d0120142005280200200f6a2d0000220f410f71200f41047620064101711b41246c6a220f2d00002111200f41023a000020114102460d02200541c8016a41096a200f41096a290000370000200541c8016a41116a200f41116a290000370000200541c8016a41196a200f41196a290000370000200541c8016a41206a200f41206a280000360000200541f8016a41086a200441086a2210280200220c360200200f29000121162010200c41016a360200200520113a00c801200520163700c901200520042902003703f801200541c0036a2002200541c8016a20042001108d8680800020052802c003210420052d00c4032211417e6a0e020407030b41032111200528029401211420052d00684103470d0441012108410721154101210b4101210c0c160b200f2007418cd5c2800010f980808000000b200541c8006a200541b0026a41106a290300370300200541c0006a41106a200541b0026a41186a290300370300200541c0006a41186a200541d0026a280200360200200520052903b80237034020052f00b10220052d00b30241107472210f4101210c0c110b200f2004360204200f41003a0000200541c0006a41086a200541b0026a41106a290300370300200541c0006a41106a200541b0026a41186a290300370300200541c0006a41186a200541b0026a41206a280200360200200520052903b80237034020052f00b10220052d00b30241107472210f201145210c0c100b200541ec036a200541b0026a41286a280200360200200541c0036a41246a200541b0026a41206a290300370200200541c0036a411c6a200541b0026a41186a290300370200200541c0036a41146a200541b0026a41106a290300370200200541cc036a200541b0026a41086a290300370200200520052903b0023702c403200520143602f003200541073a00c0034100210c200541e0026a2002200541c0036a200541f8016a4100108886808000024020052d00e00222154108460d00200541e2006a20052d00e3023a0000200541c0006a41086a200541e0026a41146a290200370300200541c0006a41106a200541e0026a411c6a290200370300200541c0006a41186a200541e0026a41246a280200360200200520052f00e1023b0160200520052902ec0237034020052802e80221102005280288032113200528028c032112200528029003211420052802e4022111200541106a41286a200541bc036a280200360200200541306a200541b4036a290200370300200541106a41186a200541ac036a290200370300200541106a41106a200541a4036a290200370300200541106a41086a2005419c036a29020037030020052005290294033703102011410876210f0c110b20052802e402210420004103360200200020043602040c130b200541b0026a41286a200541e8006a220f41286a280200360200200541b0026a41206a200f41206a290200370300200541b0026a41186a200f41186a290200370300200541b0026a41106a200f41106a290200370300200541b0026a41086a200f41086a2902003703002005200f2902003703b00220042802082211410176210f0240024020114101710d00200f200428020422114b0d07200541003a00c8032005200f3602c403200520042802003602c0030c010b200f200428020422114b0d07200f20114f0d082005200428020022113602c0032005200f3602c403200541c9036a2011200f6a2d000041f001713a0000200541013a00c8030b20022001200541b0026a200541c0036a108386808000200541033a00c403200520143602f003200541073a00c003200541c8016a41086a200441086a280200360200200520042902003703c8014100210b200541e0026a2002200541c0036a200541c8016a4100108886808000024020052d00e00222154108460d00200541e2006a20052d00e3023a0000200541c0006a41086a200541f4026a290200370300200541c0006a41106a200541fc026a290200370300200541c0006a41186a20054184036a280200360200200520052f00e1023b0160200520052902ec0237034020052802e80221102005280288032113200528028c032112200528029003211420052802e4022111200541386a200541bc036a280200360200200541306a200541b4036a290200370300200541106a41186a200541ac036a290200370300200541106a41106a200541a4036a290200370300200541106a41086a2005419c036a29020037030020052005290294033703102011410876210f410121084100210c0c120b20052802e402210420004103360200200020043602040c120b20054194016a210f0240200a0d00200541b0026a41286a200f41286a290200370300200541b0026a41206a200f41206a290200370300200541b0026a41186a200f41186a290200370300200541b0026a41106a200f41106a290200370300200541b0026a41086a2214200f41086a2902003703002005200f2902003703b0022005280290012112200541c8016a41286a200341286a280200360200200541c8016a41206a200341206a290200370300200541c8016a41186a200341186a290200370300200541c8016a41106a200341106a290200370300200541c8016a41086a200341086a290200370300200520032902003703c801200520052802b0023602c80320052014280200200541dc026a280200220f200f41284b22141b3602c403200520052802b402200541b0026a410472220f20141b3602c003200541c0036a200510db8d808000221420052802c40341017420052802c8036b2211470d0c2014200528020441017420052802086b470d0c41032115024020052d00c8014103470d00200541106a41086a200f41086a290200370300200541106a41106a200f41106a290200370300200541106a41186a200f41186a290200370300200541106a41206a200f41206a290200370300200541106a41286a200f41286a2802003602002005200f2902003703104101210c20052802b00221140c0e0b200541c0036a41286a200541c8016a41286a280200360200200541c0036a41206a200541c8016a41206a290300370300200541c0036a41186a200541c8016a41186a290300370300200541c0036a41106a200541c8016a41106a290300370300200541c0036a41086a200541c8016a41086a290300370300200520052903c8013703c003200541a0026a41086a200441086a220f28020020146a360200200520042902003703a002200541e0026a200541a0026a10dd8d80800020022001200541c0036a200541e0026a10838680800020054198046a200541b0026a41286a29030037020020054190046a200541b0026a41206a29030037020020054188046a200541b0026a41186a29030037020020054180046a200541b0026a41106a290300370200200541f8036a200541b0026a41086a290300370200200520052903b0023702f003200520123602ec03200541033a00c003200541f8016a41086a200f280200360200200520042902003703f8014100210c200541e0026a2002200541c0036a200541f8016a4100108886808000024020052d00e00222154108460d00200541e2006a20052d00e3023a0000200541c0006a41086a200541f4026a290200370300200541c0006a41106a200541fc026a290200370300200541c0006a41186a20054184036a280200360200200520052f00e1023b0160200520052902ec0237034020052802e80221102005280288032113200528028c032112200528029003211420052802e4022111200541106a41286a200541bc036a280200360200200541106a41206a200541b4036a290200370300200541106a41186a200541ac036a290200370300200541106a41106a200541a4036a290200370300200541106a41086a2005419c036a29020037030020052005290294033703102011410876210f0c0e0b200020052802e402360204200041033602000c120b41032115200d41ff01714103460d0a20054198046a200f41286a29020037020020054190046a200f41206a29020037020020054188046a200f41186a29020037020020054180046a200f41106a290200370200200541f8036a200f41086a2902003702002005200f2902003702f003200528029001211120042802082214410176210f0240024020144101710d00200f200428020422144b0d09200541003a00e8022005200f3602e402200520042802003602e0020c010b200f200428020422144b0d09200f20144f0d0a2005200428020022143602e0022005200f3602e402200541e9026a2014200f6a2d000041f001713a0000200541013a00e8020b200220012003200541e0026a108386808000200520113602ec03200541033a00c003200541b0026a41086a200441086a280200360200200520042902003703b00241002108200541e0026a2002200541c0036a200541b0026a4100108886808000024020052d00e00222154108460d00200541e2006a20052d00e3023a0000200541c0006a41086a200541f4026a290200370300200541c0006a41106a200541fc026a290200370300200541c0006a41186a20054184036a280200360200200520052f00e1023b0160200520052902ec0237034020052802e80221102005280288032113200528028c032112200528029003211420052802e4022111200541386a200541bc036a280200360200200541306a200541b4036a290200370300200541106a41186a200541ac036a290200370300200541106a41106a200541a4036a290200370300200541106a41086a2005419c036a29020037030020052005290294033703102011410876210f4101210b4100210c0c110b20052802e402210420004103360200200020043602040c110b2000410336020020002004360204200541b0026a10e585808000201441002802c0a3c68000118080808000000c100b2004201441f492c68000109581808000000b20042014418493c68000109581808000000b20042014419493c6800010f980808000000b200f201141f492c68000109581808000000b200f2011418493c68000109581808000000b200f2011419493c6800010f980808000000b200f201441f492c68000109581808000000b200f2014418493c68000109581808000000b200f2014419493c6800010f980808000000b200541186a200541a0016a290200370300200541206a200541a8016a290200370300200541286a200541b0016a290200370300200541306a200541b8016a290200370300200541386a200541c0016a280200360200200520054198016a29020037031041012108200528029001211220052802940121144101210b4101210c0c050b0240024002400240024002400240024020142011490d00200528020820146a221041017622112005280204220c4f0d012012200528020020116a2d00002211410f71201141047620104101711b41246c6a22112d00002110201141023a000020104102460d02200541f8016a41096a201141096a290000370000200541f8016a41116a201141116a290000370000200541f8016a41196a201141196a290000370000200541f8016a41206a201141206a280000360000200541a0026a41086a200441086a220c280200221536020020112900012116200c201420156a41016a360200200520103a00f801200520163700f901200520042902003703a002200541c0036a2002200541f8016a20042001108d8680800020052802c003210420052d00c403220c417e6a0e020304060b200541e2006a20052d00cb013a0000200541106a41086a200f41086a290200370300200541106a41106a200f41106a290200370300200541106a41186a200f41186a290200370300200541106a41206a200f41206a290200370300200541106a41286a200f41286a280200360200200520052f00c9013b01602005200f29020037031020052802b002211420052d00c8012115200541c0006a41086a200541dc016a290200370300200541c0006a41106a200541e4016a290200370300200541c0006a41186a200541ec016a280200360200200520052902d40137034020052f00cd0120052d00cf0141107472210f0c060b2011200c418cd5c2800010f980808000000b200541e0006a41026a20052d00cb013a0000200541106a41086a200f41086a290200370300200541106a41106a200f41106a290200370300200541106a41186a200f41186a290200370300200541106a41206a200f41206a290200370300200541106a41286a200f41286a280200360200200520052f00c9013b01602005200f29020037031020052802b002211420052d00c8012115200541c0006a41086a200541dc016a290200370300200541c0006a41106a200541e4016a290200370300200541c0006a41186a200541c8016a41246a280200360200200520052902d40137034020052f00cd0120052d00cf0141107472210f0c040b20054198046a200541b0026a41286a29030037030020054190046a200541b0026a41206a29030037030020054188046a200541b0026a41186a29030037030020054180046a200541b0026a41106a290300370300200541f8036a200541b0026a41086a290300370300200541c0036a41086a200541c8016a41086a290300370300200541c0036a41106a200541c8016a41106a290300370300200541c0036a41186a200541c8016a41186a290300370300200541c0036a41206a200541c8016a41206a290300370300200541c0036a41286a200541c8016a41286a280200360200200520052903b0023703f003200520052903c8013703c003200520123602ec034100210c200541e0026a2002200541c0036a200541a0026a410010888680800020052d00e00222154108460d01200541e2006a20052d00e3023a0000200541c0006a41086a200541f4026a290200370300200541c0006a41106a200541fc026a290200370300200541c0006a41186a20054184036a280200360200200520052f00e1023b0160200520052902ec0237034020052802e80221102005280288032113200528028c032112200528029003211420052802e4022111200541106a41286a200541bc036a280200360200200541306a200541b4036a290200370300200541106a41186a200541ac036a290200370300200541106a41106a200541a4036a290200370300200541106a41086a2005419c036a29020037030020052005290294033703102011410876210f0c040b2000410336020020002004360204024020052d00c8014103460d00200541c8016a10e4858080000b201241002802c0a3c680001180808080000020052802dc024129490d0820052802b40241002802c0a3c68000118080808000000c080b20052802e402210420004103360200200020043602040c070b20112004360204201141003a0000200541e2006a20052d00cb013a0000200541c0006a41086a200541dc016a290200370300200541c0006a41106a200541e4016a290200370300200541c0006a41186a200541ec016a280200360200200520052f00c9013b0160200520052902d40137034020052802f001211320052802d001211020052d00cc01211120052d00c801211520052802b002211420052f00cd01210420052d00cf012103200541106a41286a200f41286a280200360200200541106a41206a200f41206a290200370300200541106a41186a200f41186a290200370300200541106a41106a200f41106a290200370300200541106a41086a200f41086a2902003703002005200f2902003703102004200341107472210f200c45210c0c010b4101210c20052802f001211320052802d001211020052d00cc0121110b4101210b410021080c030b4107211520052802d802211220052802d402211320052802b402211020052d00b00221110b4100210b410121080c010b410121084101210b0b2000200f3b0009200020153a00042000200c360200200020052f01603b00052000201036020c200020113a0008200020052903403702102000201336022c20002012360230200020143602342000410b6a200f4110763a0000200041076a200541e2006a2d00003a0000200041186a200541c0006a41086a290300370200200041206a200541c0006a41106a290300370200200041286a200541c0006a41186a280200360200200041e0006a200541106a41286a280200360200200041d8006a200541106a41206a290300370200200041d0006a200541106a41186a290300370200200041c8006a200541106a41106a290300370200200041c0006a200541106a41086a29030037020020002005290310370238024002400240024020052d00642204417c6a41ff01712200410420004104491b417d6a0e020001040b200b450d0320052d006822004103460d03024020000e020004020b200541ec006a2200280200220420042802002204417f6a36020020044101460d020c030b2008450d0220044103460d020240024020040e020104000b200528028801220020002802002200417f6a36020020004101470d0320054188016a10e28a8080000c030b2005280268220020002802002200417f6a36020020004101470d02200541e8006a10e28a8080000c020b2005418c016a2200280200220420042802002204417f6a36020020044101470d010b200010e28a8080000b200541a0046a2480808080000bdb2802087f037e2380808080004180056b2201248080808000200028025c21022000410036025c200041e4006a22032802002104200341003602002001200036020c200041e0006a280200210320012004410020021b3602d003200120033602cc03200120023602c803200141003602c4032001200241004722043602c003200120033602bc03200120023602b803200141003602b403200120043602b003200141106a200141b0036a10e28c808000024020012d005c22034102460d002001419d026a210520014190016a41206a2102200141dd006a2106034020014190016a200141106a41cc0010848e8080001a200541026a200641026a2d00003a0000200520062f00003b0000200141106a41186a20014190016a41186a290200370300200141106a41106a20014190016a41106a290200370300200141106a41086a20014190016a41086a290200370300200141f0016a41086a200241086a290200370300200141f0016a41106a200241106a290200370300200141f0016a41186a200241186a290200370300200141f0016a41206a200241206a290200370300200141f0016a41286a2204200241286a2802003602002001200129029001370310200120022902003703f001200120033a009c022000280250210720002802542108200120012d009d023a00ad02200120033a00ac02200120012802f40120042802002203200341284b22031b3602a802200120012802f001200141f0016a20031b3602a4022007200141106a200141a4026a200828022411858080800000024020042802004129490d0020012802f00141002802c0a3c68000118080808000000b200141106a200141b0036a10e28c80800020012d005c22034102470d000b0b200141b0036a10a48d8080000240024020002d002c0d00200041306a28020021020240200041286a28020022042000411c6a22052802002203470d00200510a889808000200028021c2103200028022821040b200041206a280200200041246a28020020046a22044100200320042003491b6b4102746a20023602002000200028022841016a3602280240024002400240024002400240024002400240200041186a280200220320024d0d00200041146a28020020024107746a22022d00002103200141106a200241016a41ff0010848e8080001a200241043a0004200241083a000002400240024002400240024020034108470d0020014190016a200141136a41e00010848e8080001a4100210220012d0090012104200128020c280268450d052004417c6a41ff01712203410420034104491b0e050501020503050b200128020c22022802582204200129006f370000200441186a20014187016a290000370000200441106a200141ff006a290000370000200441086a200141f7006a290000370000200141ef006a210402400240200241286a2802002200450d0020022000417f6a360228200241246a22002000280200220041016a220541002002411c6a280200220620052006491b6b360200200241206a28020020004102746a2802002200200241186a28020022054f0d08200241146a28020020004107746a220241046a200220022d00004108461b10e385808000200220033a0000200241016a200141106a41df0010848e8080001a200241f8006a200441186a290000370000200241f0006a200441106a290000370000200241e8006a200441086a290000370000200220042900003700600c010b0240200241186a28020022002002280210470d00200241106a2000109e86808000200228021821000b200241146a28020020004107746a220020033a0000200041016a200141106a41df0010848e8080001a200041f8006a200441186a290000370000200041f0006a200441106a290000370000200041e8006a200441086a2900003700002000200429000037006020022002280218220041016a3602180b200128020c220241003a002c200241306a20003602000c0f0b200141c0016a21020c020b200141b8016a21020c010b200141c0016a21020b200120022802003602b8032001200241086a2802002002412c6a2802002203200341284b22031b3602b40320012002280204200241046a20031b3602b003200141f0016a41046a200141b0036a10e48d8080004101210220012d00900121040b200120023602f001200142003702cc02200141e0026a411f6a20014190016a41206a290000370000200141e0026a41186a20014190016a41196a290000370300200141e0026a41106a20014190016a41116a290000370300200141e0026a41086a20014190016a41096a29000037030020012001290091013703e00220012802b801210620012802bc01210020012802c001210320012001410c6a3602ac032001200141a4026a3602a803200141c4016a2102024002400240024002400240024002402004417c6a41ff01712205410420054104491b0e050001020304000b41002d00fca3c680001a410141002802c8a3c68000118180808000002202450d09200241003a0000200141013602dc02200120023602d802200141013602d4020c100b200141bc036a200241086a290200370200200141c4036a200241106a290200370200200141cc036a200241186a290200370200200141b0036a41246a200241206a290200370200200141dc036a2204200241286a280200360200200120033602b003200120022902003702b403200141e0036a41206a20014183036a280000360200200141e0036a41186a200141fb026a290000370300200141e0036a41106a200141f3026a290000370300200141e0036a41086a200141eb026a290000370300200120012900e3023703e00320012000360288042001200636028404200120033602a404200120012802b80320042802002202200241284b22041b22023602a004200120012802b403200141b0036a41046a20041b220536029c04024002400240024020012d00e0030e03010200020b200141003a00f804200120003602d8042001200641086a3602d404200141023a00d00420012001419c046a3602f404200141a8046a200141a4026a2001410c6a200141d0046a2001419c046a4100200110db8580800020012d00a8040d0c200141e8046a2202200141c1046a290000370300200141e0046a2203200141b9046a290000370300200141d0046a41086a200141b1046a290000370300200120012900a9043703d004024020012d00e1030d00200141fa036a2002290300370100200141f2036a2003290300370100200141ea036a200141d8046a290300370100200120012903d0043701e203200141013a00e1030b200141e0036a4102722104412021004101210620012802a0042102200128029c04210520012802a40421030c020b20012802e40341086a2104200141e0036a41086a2802002100410021060c010b41012106200141e0036a4101722104412021000b200120003602b004200120043602ac04200120063602a804200341017621040240024020034101710d00410021060240200420024b0d0020042100410021040c020b2004200241d492c68000109481808000000b200420024f0d0b41012106200441016a2100200520046a2d0000410f7121040b200120043a00dd04200120063a00dc04200141003602d8042001200220006b3602d4042001200520006a3602d004200141d4026a200141d0046a200241017420036b200141a8046a10d08c808000024002400240024020012d00e0030e020103000b200128028404220220022802002202417f6a36020020024101470d02200141e0036a41246a21020c010b20012802e403220220022802002202417f6a36020020024101470d01200141e0036a41047221020b200210e28a8080000b20012802dc034129490d0f20012802b40341002802c0a3c68000118080808000000c0f0b200141c4036a200241086a290200370200200141cc036a200241106a290200370200200141d4036a200241186a290200370200200141dc036a200241206a280200360200200120033602b803200120003602b403200120063602b003200120022902003702bc03200141d0046a41206a20014183036a280000360200200141d0046a41186a200141fb026a290000370300200141d0046a41106a200141f3026a290000370300200141d0046a41086a200141eb026a290000370300200120012900e3023703d0042001200141b0036a41046a41c0d1c28000109687808000200120063602a4042001200129030037029c04200141a8046a2001419c046a10dc8d808000200141003a00f80420012001419c046a3602f404200141e0036a200141a4026a2001410c6a200141d0046a2001419c046a4100200110db85808000200141d4026a200141a8046a20012802a00441017420012802a4046b200141e0036a10cb8c808000000b200141b0036a41206a20014183036a280000360200200141b0036a41186a200141fb026a290000370300200141b0036a41106a200141f3026a290000370300200141b0036a41086a200141eb026a290000370300200120012900e30222093703b003200120003602d803200120063602d4032009a741ff01714103470d01410221020c020b200141bc036a200241086a290200370200200141c4036a200241106a290200370200200141cc036a200241186a290200370200200141d4036a200241206a290200370200200141dc036a2205200241286a280200360200200120033602b003200120022902003702b403200141e0036a41096a20014190016a410172220241086a290000370000200141e0036a41116a200241106a290000370000200141e0036a41196a200241186a290000370000200141e0036a41206a2002411f6a2900003700002001200636028804200120022900003700e103200120043a00e0032001200336029804200120012802b80320052802002202200241284b22051b220236029404200120012802b403200141b0036a41046a20051b220736029004200441ff01714103470d02410221080c0b0b200141d0046a200141b0036a4100200141a4026a2001410c6a10f58580800020012902d404210920012802d00421020b200120093702e403200120023602e003200141d4026a200141d0046a200141e0036a10ca8c808000000b200141e0036a4101722105428080808080042109410121080240200441ff01710e03070800080b200141003a00f804200120063602d804200120012802840441086a3602d404200141023a00d004200120014190046a3602f404200141a8046a200141a4026a2001410c6a200141d0046a20014190046a4100200110db8580800020012d00a8040d05200141e8046a2202200141c1046a290000370300200141e0046a2203200141b9046a290000370300200141d0046a41086a200141b1046a290000370300200120012900a9043703d004024020012d00e1030d00200141fa036a2002290300370100200141f2036a2003290300370100200141ea036a200141d8046a290300370100200120012903d0043701e203200141013a00e1030b200141e0036a41027221052001280294042102200128029004210720012802980421030c070b2002200341e4d7c2800010f980808000000b2000200541d4d7c2800010f980808000000b4101410110b280808000000b200141dc046a4201370200200141013602d404200141fcd1c280003602d004200141e0818080003602940420014180d3c2800036029004200120014190046a3602d804200141d0046a4188d3c2800010f680808000000b2004200241e492c6800010f980808000000b200141dc046a4201370200200141013602d404200141fcd1c280003602d004200141e0818080003602a00420014180d3c2800036029c0420012001419c046a3602d804200141d0046a4188d3c2800010f680808000000b20012802e40341086a2105200141e0036a41086a3502004220862109410021080b20092005ad8421090b200341017621040240024020034101710d00410021060240200420024b0d0020042105410021040c020b2004200241d492c68000109481808000000b200420024f0d0341012106200441016a2105200720046a2d0000410f7121040b200120043a00b504200120063a00b404200141003602b0042001200220056b3602ac042001200720056a3602a804200141e0046a200141a8036a360200200141003602d804200120003602d0042001200041c0046a3602d4042001200141b0036a3602dc04200120093702a0042001200836029c04200141d4026a200141a8046a200241017420036b200141d0046a2001419c046a10cd8c808000024020012d00e00322024103460d0002400240024020020e020103000b200128028404220220022802002202417f6a36020020024101470d0220014184046a21020c010b20012802e403220220022802002202417f6a36020020024101470d01200141e4036a21020b200210e28a8080000b200041002802c0a3c680001180808080000020012802dc034129490d0020012802b40341002802c0a3c68000118080808000000b200128020c220241d4006a280200210320022802502102200141d0046a41086a41002802e0efc08000360200200141002902d8efc080003703d004200141b0036a2002200141d0046a20012802d802220420012802dc022200200328021c11878080800000200128020c2203280258220220012900b003370000200241086a200141b0036a41086a2205290000370000200241106a200141b0036a41106a2206290000370000200241186a200141b0036a41186a220729000037000020052003280258220241086a2900003703002006200241106a2900003703002007200241186a290000370300200120022900003703b0032003200141b0036a20042000200141f0016a10f885808000200128020c2202280258220341186a2900002109200341106a290000210a200341086a290000210b2002412d6a2003290000370000200241013a002c200241356a200b3700002002413d6a200a370000200241c5006a2009370000024020012802d402450d00200441002802c0a3c68000118080808000000b20012802cc024129490d0020012802a40241002802c0a3c68000118080808000000b20014180056a2480808080000f0b2004200241e492c6800010f980808000000bdb2802087f037e2380808080004180056b2201248080808000200028025c21022000410036025c200041e4006a22032802002104200341003602002001200036020c200041e0006a280200210320012004410020021b3602d003200120033602cc03200120023602c803200141003602c4032001200241004722043602c003200120033602bc03200120023602b803200141003602b403200120043602b003200141106a200141b0036a10e28c808000024020012d005c22034102460d002001419d026a210520014190016a41206a2102200141dd006a2106034020014190016a200141106a41cc0010848e8080001a200541026a200641026a2d00003a0000200520062f00003b0000200141106a41186a20014190016a41186a290200370300200141106a41106a20014190016a41106a290200370300200141106a41086a20014190016a41086a290200370300200141f0016a41086a200241086a290200370300200141f0016a41106a200241106a290200370300200141f0016a41186a200241186a290200370300200141f0016a41206a200241206a290200370300200141f0016a41286a2204200241286a2802003602002001200129029001370310200120022902003703f001200120033a009c022000280250210720002802542108200120012d009d023a00ad02200120033a00ac02200120012802f40120042802002203200341284b22031b3602a802200120012802f001200141f0016a20031b3602a4022007200141106a200141a4026a200828022411858080800000024020042802004129490d0020012802f00141002802c0a3c68000118080808000000b200141106a200141b0036a10e28c80800020012d005c22034102470d000b0b200141b0036a10a48d8080000240024020002d002c0d00200041306a28020021020240200041286a28020022042000411c6a22052802002203470d00200510a889808000200028021c2103200028022821040b200041206a280200200041246a28020020046a22044100200320042003491b6b4102746a20023602002000200028022841016a3602280240024002400240024002400240024002400240200041186a280200220320024d0d00200041146a28020020024107746a22022d00002103200141106a200241016a41ff0010848e8080001a200241043a0004200241083a000002400240024002400240024020034108470d0020014190016a200141136a41e00010848e8080001a4100210220012d0090012104200128020c280268450d052004417c6a41ff01712203410420034104491b0e050501020503050b200128020c22022802582204200129006f370000200441186a20014187016a290000370000200441106a200141ff006a290000370000200441086a200141f7006a290000370000200141ef006a210402400240200241286a2802002200450d0020022000417f6a360228200241246a22002000280200220041016a220541002002411c6a280200220620052006491b6b360200200241206a28020020004102746a2802002200200241186a28020022054f0d08200241146a28020020004107746a220241046a200220022d00004108461b10e385808000200220033a0000200241016a200141106a41df0010848e8080001a200241f8006a200441186a290000370000200241f0006a200441106a290000370000200241e8006a200441086a290000370000200220042900003700600c010b0240200241186a28020022002002280210470d00200241106a2000109e86808000200228021821000b200241146a28020020004107746a220020033a0000200041016a200141106a41df0010848e8080001a200041f8006a200441186a290000370000200041f0006a200441106a290000370000200041e8006a200441086a2900003700002000200429000037006020022002280218220041016a3602180b200128020c220241003a002c200241306a20003602000c0f0b200141c0016a21020c020b200141b8016a21020c010b200141c0016a21020b200120022802003602b8032001200241086a2802002002412c6a2802002203200341284b22031b3602b40320012002280204200241046a20031b3602b003200141f0016a41046a200141b0036a10e48d8080004101210220012d00900121040b200120023602f001200142003702cc02200141e0026a411f6a20014190016a41206a290000370000200141e0026a41186a20014190016a41196a290000370300200141e0026a41106a20014190016a41116a290000370300200141e0026a41086a20014190016a41096a29000037030020012001290091013703e00220012802b801210620012802bc01210020012802c001210320012001410c6a3602ac032001200141a4026a3602a803200141c4016a2102024002400240024002400240024002402004417c6a41ff01712205410420054104491b0e050001020304000b41002d00fca3c680001a410141002802c8a3c68000118180808000002202450d09200241003a0000200141013602dc02200120023602d802200141013602d4020c100b200141bc036a200241086a290200370200200141c4036a200241106a290200370200200141cc036a200241186a290200370200200141b0036a41246a200241206a290200370200200141dc036a2204200241286a280200360200200120033602b003200120022902003702b403200141e0036a41206a20014183036a280000360200200141e0036a41186a200141fb026a290000370300200141e0036a41106a200141f3026a290000370300200141e0036a41086a200141eb026a290000370300200120012900e3023703e00320012000360288042001200636028404200120033602a404200120012802b80320042802002202200241284b22041b22023602a004200120012802b403200141b0036a41046a20041b220536029c04024002400240024020012d00e0030e03010200020b200141003a00f804200120003602d8042001200641086a3602d404200141023a00d00420012001419c046a3602f404200141a8046a200141a4026a2001410c6a200141d0046a2001419c046a4100200110d98580800020012d00a8040d0c200141e8046a2202200141c1046a290000370300200141e0046a2203200141b9046a290000370300200141d0046a41086a200141b1046a290000370300200120012900a9043703d004024020012d00e1030d00200141fa036a2002290300370100200141f2036a2003290300370100200141ea036a200141d8046a290300370100200120012903d0043701e203200141013a00e1030b200141e0036a4102722104412021004101210620012802a0042102200128029c04210520012802a40421030c020b20012802e40341086a2104200141e0036a41086a2802002100410021060c010b41012106200141e0036a4101722104412021000b200120003602b004200120043602ac04200120063602a804200341017621040240024020034101710d00410021060240200420024b0d0020042100410021040c020b2004200241d492c68000109481808000000b200420024f0d0b41012106200441016a2100200520046a2d0000410f7121040b200120043a00dd04200120063a00dc04200141003602d8042001200220006b3602d4042001200520006a3602d004200141d4026a200141d0046a200241017420036b200141a8046a10d08c808000024002400240024020012d00e0030e020103000b200128028404220220022802002202417f6a36020020024101470d02200141e0036a41246a21020c010b20012802e403220220022802002202417f6a36020020024101470d01200141e0036a41047221020b200210e28a8080000b20012802dc034129490d0f20012802b40341002802c0a3c68000118080808000000c0f0b200141c4036a200241086a290200370200200141cc036a200241106a290200370200200141d4036a200241186a290200370200200141dc036a200241206a280200360200200120033602b803200120003602b403200120063602b003200120022902003702bc03200141d0046a41206a20014183036a280000360200200141d0046a41186a200141fb026a290000370300200141d0046a41106a200141f3026a290000370300200141d0046a41086a200141eb026a290000370300200120012900e3023703d0042001200141b0036a41046a41c0d1c28000109687808000200120063602a4042001200129030037029c04200141a8046a2001419c046a10dc8d808000200141003a00f80420012001419c046a3602f404200141e0036a200141a4026a2001410c6a200141d0046a2001419c046a4100200110d985808000200141d4026a200141a8046a20012802a00441017420012802a4046b200141e0036a10cb8c808000000b200141b0036a41206a20014183036a280000360200200141b0036a41186a200141fb026a290000370300200141b0036a41106a200141f3026a290000370300200141b0036a41086a200141eb026a290000370300200120012900e30222093703b003200120003602d803200120063602d4032009a741ff01714103470d01410221020c020b200141bc036a200241086a290200370200200141c4036a200241106a290200370200200141cc036a200241186a290200370200200141d4036a200241206a290200370200200141dc036a2205200241286a280200360200200120033602b003200120022902003702b403200141e0036a41096a20014190016a410172220241086a290000370000200141e0036a41116a200241106a290000370000200141e0036a41196a200241186a290000370000200141e0036a41206a2002411f6a2900003700002001200636028804200120022900003700e103200120043a00e0032001200336029804200120012802b80320052802002202200241284b22051b220236029404200120012802b403200141b0036a41046a20051b220736029004200441ff01714103470d02410221080c0b0b200141d0046a200141b0036a4100200141a4026a2001410c6a10f68580800020012902d404210920012802d00421020b200120093702e403200120023602e003200141d4026a200141d0046a200141e0036a10ca8c808000000b200141e0036a4101722105428080808080042109410121080240200441ff01710e03070800080b200141003a00f804200120063602d804200120012802840441086a3602d404200141023a00d004200120014190046a3602f404200141a8046a200141a4026a2001410c6a200141d0046a20014190046a4100200110d98580800020012d00a8040d05200141e8046a2202200141c1046a290000370300200141e0046a2203200141b9046a290000370300200141d0046a41086a200141b1046a290000370300200120012900a9043703d004024020012d00e1030d00200141fa036a2002290300370100200141f2036a2003290300370100200141ea036a200141d8046a290300370100200120012903d0043701e203200141013a00e1030b200141e0036a41027221052001280294042102200128029004210720012802980421030c070b2002200341e4d7c2800010f980808000000b2000200541d4d7c2800010f980808000000b4101410110b280808000000b200141dc046a4201370200200141013602d404200141fcd1c280003602d004200141e0818080003602940420014180d3c2800036029004200120014190046a3602d804200141d0046a4188d3c2800010f680808000000b2004200241e492c6800010f980808000000b200141dc046a4201370200200141013602d404200141fcd1c280003602d004200141e0818080003602a00420014180d3c2800036029c0420012001419c046a3602d804200141d0046a4188d3c2800010f680808000000b20012802e40341086a2105200141e0036a41086a3502004220862109410021080b20092005ad8421090b200341017621040240024020034101710d00410021060240200420024b0d0020042105410021040c020b2004200241d492c68000109481808000000b200420024f0d0341012106200441016a2105200720046a2d0000410f7121040b200120043a00b504200120063a00b404200141003602b0042001200220056b3602ac042001200720056a3602a804200141e0046a200141a8036a360200200141003602d804200120003602d0042001200041c0046a3602d4042001200141b0036a3602dc04200120093702a0042001200836029c04200141d4026a200141a8046a200241017420036b200141d0046a2001419c046a10cc8c808000024020012d00e00322024103460d0002400240024020020e020103000b200128028404220220022802002202417f6a36020020024101470d0220014184046a21020c010b20012802e403220220022802002202417f6a36020020024101470d01200141e4036a21020b200210e28a8080000b200041002802c0a3c680001180808080000020012802dc034129490d0020012802b40341002802c0a3c68000118080808000000b200128020c220241d4006a280200210320022802502102200141d0046a41086a41002802e0efc08000360200200141002902d8efc080003703d004200141b0036a2002200141d0046a20012802d802220420012802dc022200200328021c11878080800000200128020c2203280258220220012900b003370000200241086a200141b0036a41086a2205290000370000200241106a200141b0036a41106a2206290000370000200241186a200141b0036a41186a220729000037000020052003280258220241086a2900003703002006200241106a2900003703002007200241186a290000370300200120022900003703b0032003200141b0036a20042000200141f0016a10f985808000200128020c2202280258220341186a2900002109200341106a290000210a200341086a290000210b2002412d6a2003290000370000200241013a002c200241356a200b3700002002413d6a200a370000200241c5006a2009370000024020012802d402450d00200441002802c0a3c68000118080808000000b20012802cc024129490d0020012802a40241002802c0a3c68000118080808000000b20014180056a2480808080000f0b2004200241e492c6800010f980808000000ba90601037f2380808080004190016b2206248080808000200641033a000c02400240024020054100480d00200541f5ffffff074f0d0102402005410b6a417c7122070d00410421080c030b41002d00fca3c680001a200741002802c8a3c680001181808080000022080d024104200710b280808000000b41fc9bc68000412b200641e0006a41a89cc6800041b89cc68000108981808000000b41e484c08000412b200641e0006a419085c08000419086c08000108981808000000b2008428180808010370200200841086a2004200510848e8080001a0240024020012d002c0d00200141306a2802002104410021070c010b200641d6006a2001412f6a2d00003a0000200641386a41086a2001413c6a290200370300200641c8006a200141c4006a290200370300200641d0006a200141cc006a2d00003a000020062001412d6a2f00003b01542006200141346a290200370338200141306a2802002104410121070b200641e0006a41106a200641386a41086a290300370200200641e0006a41186a200641386a41106a290300370200200641e0006a41206a200641386a41186a280200360200200620073a0060200620062f01543b006120062004360264200620062903383702682006200641d4006a41026a2d00003a00632006410036028c0120062003360288012006200236028401200641d8006a2001200641e0006a20064184016a200820052006410c6a108286808000200628025821050240024020062d005c4102460d00200141003a002c2000200629020c370200200141306a2005360200200041086a2006410c6a41086a290200370200200041106a2006410c6a41106a290200370200200041186a2006410c6a41186a290200370200200041206a2006410c6a41206a290200370200200041286a2006410c6a41286a2802003602000c010b200041043a00002000200536020420062d000c22014103460d000240024020010e020102000b2006280230220120012802002201417f6a36020020014101470d01200641306a10e28a8080000c010b2006280210220120012802002201417f6a36020020014101470d00200641106a10e28a8080000b20064190016a2480808080000ba90601037f2380808080004190016b2206248080808000200641033a000c02400240024020054100480d00200541f5ffffff074f0d0102402005410b6a417c7122070d00410421080c030b41002d00fca3c680001a200741002802c8a3c680001181808080000022080d024104200710b280808000000b41fc9bc68000412b200641e0006a41a89cc6800041b89cc68000108981808000000b41e484c08000412b200641e0006a419085c08000419086c08000108981808000000b2008428180808010370200200841086a2004200510848e8080001a0240024020012d002c0d00200141306a2802002104410021070c010b200641d6006a2001412f6a2d00003a0000200641386a41086a2001413c6a290200370300200641c8006a200141c4006a290200370300200641d0006a200141cc006a2d00003a000020062001412d6a2f00003b01542006200141346a290200370338200141306a2802002104410121070b200641e0006a41106a200641386a41086a290300370200200641e0006a41186a200641386a41106a290300370200200641e0006a41206a200641386a41186a280200360200200620073a0060200620062f01543b006120062004360264200620062903383702682006200641d4006a41026a2d00003a00632006410036028c0120062003360288012006200236028401200641d8006a2001200641e0006a20064184016a200820052006410c6a108586808000200628025821050240024020062d005c4102460d00200141003a002c2000200629020c370200200141306a2005360200200041086a2006410c6a41086a290200370200200041106a2006410c6a41106a290200370200200041186a2006410c6a41186a290200370200200041206a2006410c6a41206a290200370200200041286a2006410c6a41286a2802003602000c010b200041043a00002000200536020420062d000c22014103460d000240024020010e020102000b2006280230220120012802002201417f6a36020020014101470d01200641306a10e28a8080000c010b2006280210220120012802002201417f6a36020020014101470d00200641106a10e28a8080000b20064190016a2480808080000ba60601037f2380808080004190016b22042480808080000240024020012d002c0d00200141306a2802002105410021060c010b2004412a6a2001412f6a2d00003a0000200441106a2001413c6a290200370300200441186a200141c4006a290200370300200441206a200141cc006a2d00003a000020042001412d6a2f00003b01282004200141346a290200370308200141306a2802002105410121060b20044100360234200420033602302004200236022c200441033a0038200441ec006a41106a2203200441086a41086a290300370200200441ec006a41186a2202200441086a41106a2903003702002004418c016a200441086a41186a280200360200200420063a006c200420042f01283b006d20042004412a6a2d00003a006f2004200536027020042004290308370274200441e4006a2001200441ec006a2004412c6a200441386a108d86808000200428026421050240024002400240024020042d0068417e6a0e020100020b200041043a00002000200536020420042d003822014103460d030240024020010e020105000b200428025c220120012802002201417f6a36020020014101470d04200441dc006a10e28a8080000c040b200428023c220120012802002201417f6a36020020014101470d032004413c6a10e28a8080000c030b200441ec006a41badbc5800041014100280298a3c6800011858080800000200141013a002c2001412d6a200429006c370000200141356a200441ec006a41086a22052900003700002001413d6a2003290000370000200141c5006a2002290000370000200441ec006a41badbc5800041014100280298a3c680001185808080000020012802582201200429006c370000200141186a2002290000370000200141106a2003290000370000200141086a20052900003700000c010b200141003a002c200141306a20053602000b20002004290238370200200041286a200441386a41286a280200360200200041206a200441386a41206a290200370200200041186a200441386a41186a290200370200200041106a200441386a41106a290200370200200041086a200441386a41086a2902003702000b20044190016a2480808080000ba60601037f2380808080004190016b22042480808080000240024020012d002c0d00200141306a2802002105410021060c010b2004412a6a2001412f6a2d00003a0000200441106a2001413c6a290200370300200441186a200141c4006a290200370300200441206a200141cc006a2d00003a000020042001412d6a2f00003b01282004200141346a290200370308200141306a2802002105410121060b20044100360234200420033602302004200236022c200441033a0038200441ec006a41106a2203200441086a41086a290300370200200441ec006a41186a2202200441086a41106a2903003702002004418c016a200441086a41186a280200360200200420063a006c200420042f01283b006d20042004412a6a2d00003a006f2004200536027020042004290308370274200441e4006a2001200441ec006a2004412c6a200441386a108b86808000200428026421050240024002400240024020042d0068417e6a0e020100020b200041043a00002000200536020420042d003822014103460d030240024020010e020105000b200428025c220120012802002201417f6a36020020014101470d04200441dc006a10e28a8080000c040b200428023c220120012802002201417f6a36020020014101470d032004413c6a10e28a8080000c030b200441ec006a41badbc5800041014100280298a3c6800011858080800000200141013a002c2001412d6a200429006c370000200141356a200441ec006a41086a22052900003700002001413d6a2003290000370000200141c5006a2002290000370000200441ec006a41badbc5800041014100280298a3c680001185808080000020012802582201200429006c370000200141186a2002290000370000200141106a2003290000370000200141086a20052900003700000c010b200141003a002c200141306a20053602000b20002004290238370200200041286a200441386a41286a280200360200200041206a200441386a41206a290200370200200041186a200441386a41186a290200370200200041106a200441386a41106a290200370200200041086a200441386a41086a2902003702000b20044190016a2480808080000ba50201027f0240024002402001450d002002417f4c0d0102400240024002402003280204450d000240200341086a28020022040d00024020020d00200121030c030b41002d00fca3c680001a200241002802c8a3c680001181808080000021030c020b20032802002105200241002802c8a3c68000118180808000002203450d0320032005200410848e8080001a200541002802c0a3c68000118080808000000c020b024020020d00200121030c010b41002d00fca3c680001a200241002802c8a3c680001181808080000021030b2003450d010b20002003360204200041086a2002360200200041003602000f0b20002001360204200041086a20023602000c020b20004100360204200041086a20023602000c010b200041003602040b200041013602000b8f0100024002400240024020010d00410121020c010b2001417f4c0d0102402002450d00200141002802c8a3c68000118180808000002202450d03200241002001108a8e8080001a0c010b41002d00fca3c680001a200141002802c8a3c68000118180808000002202450d020b20002002360204200020013602000f0b10ae80808000000b4101200110b280808000000bef0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141e8016c2104200141ccfbb4044941037421050240024020030d00200241003602180c010b200241083602182002200341e8016c36021c200220002802043602140b200241086a20052004200241146a109586808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141306c2104200141abd5aa154941027421050240024020030d00200241003602180c010b200241043602182002200341306c36021c200220002802043602140b200241086a20052004200241146a109586808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bee0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b2201410c6c2104200141abd5aad5004941027421050240024020030d00200241003602180c010b2002410436021820022003410c6c36021c200220002802043602140b200241086a20052004200241146a109586808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141286c2104200141b4e6cc194941037421050240024020030d00200241003602180c010b200241083602182002200341286c36021c200220002802043602140b200241086a20052004200241146a109586808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bee0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b2201410274210420014180808080024941027421050240024020030d00200241003602180c010b200241043602182002200341027436021c200220002802043602140b200241086a20052004200241146a109586808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000be00101037f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014108200141084b1b2201417f73411f7621040240024020030d00200241003602180c010b2002200336021c20024101360218200220002802043602140b200241086a20042001200241146a109586808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bef0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141e8006c2104200141b2a7ec094941037421050240024020030d00200241003602180c010b200241083602182002200341e8006c36021c200220002802043602140b200241086a20052004200241146a109586808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b22014107742104200141808080084941027421050240024020030d00200241003602180c010b200241043602182002200341077436021c200220002802043602140b200241086a20052004200241146a109586808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941027421050240024020030d00200241003602180c010b200241043602182002200341386c36021c200220002802043602140b200241086a20052004200241146a109586808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141246c2104200141e4f1b81c4941027421050240024020030d00200241003602180c010b200241043602182002200341246c36021c200220002802043602140b200241086a20052004200241146a109586808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bea0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b22014180808020492104200141057421050240024020030d00200241003602180c010b200241013602182002200341057436021c200220002802043602140b200241086a20042005200241146a109586808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bee0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b22014104742104200141808080c0004941037421050240024020030d00200241003602180c010b200241083602182002200341047436021c200220002802043602140b200241086a20052004200241146a109586808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bee0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b22014104742104200141808080c0004941027421050240024020030d00200241003602180c010b20022000280204360214200241043602182002200341047436021c0b200241086a20052004200241146a109586808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141386c210420014193c9a4124941037421050240024020030d00200241003602180c010b200241083602182002200341386c36021c200220002802043602140b200241086a20052004200241146a109586808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141146c2104200141e7cc99334941027421050240024020030d00200241003602180c010b200241043602182002200341146c36021c200220002802043602140b200241086a20052004200241146a109586808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bed0101047f23808080800041206b220224808080800002400240200141016a2201450d002000280200220341017422042001200420014b1b22014104200141044b1b220141186c2104200141d6aad52a4941027421050240024020030d00200241003602180c010b200241043602182002200341186c36021c200220002802043602140b200241086a20052004200241146a109586808000200228020c2103024020022802080d0020002001360200200020033602040c020b2003418180808078460d012003450d002003200241106a28020010b280808000000b10ae80808000000b200241206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241386c210420024193c9a4124941037421050240024020010d00200341003602180c010b200341083602182003200141386c36021c200320002802043602140b200341086a20052004200341146a109586808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241146c2104200241e7cc99334941027421050240024020010d00200341003602180c010b200341043602182003200141146c36021c200320002802043602140b200341086a20052004200341146a109586808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b220241246c2104200241e4f1b81c4941027421050240024020010d00200341003602180c010b200341043602182003200141246c36021c200320002802043602140b200341086a20052004200341146a109586808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bf00101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b22024104742104200241808080c0004941027421050240024020010d00200341003602180c010b20032000280204360214200341043602182003200141047436021c0b200341086a20052004200341146a109586808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000be20101027f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024108200241084b1b2202417f73411f7621040240024020010d00200341003602180c010b2003200136021c20034101360218200320002802043602140b200341086a20042002200341146a109586808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bef0101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b22024105742104200241808080204941037421050240024020010d00200341003602180c010b200341083602182003200141057436021c200320002802043602140b200341086a20052004200341146a109586808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000bf00101037f23808080800041206b220324808080800002400240200120026a22022001490d002000280200220141017422042002200420024b1b22024104200241044b1b2202410c6c2104200241abd5aad5004941027421050240024020010d00200341003602180c010b2003410436021820032001410c6c36021c200320002802043602140b200341086a20052004200341146a109586808000200328020c2101024020032802080d0020002002360200200020013602040c020b2001418180808078460d012001450d002001200341106a28020010b280808000000b10ae80808000000b200341206a2480808080000b8d07010b7f02402000280200220428020020042802082200470d0020042000410110ab86808000200428020821000b200428020420006a41223a00002004200041016a22053602082002417f6a21062003417f732107200220036a2108410021092002210a02400340410021000240024002400240024002400240024002400240024003400240200a20006a220b2008470d00024020032009460d0002402009450d00200320094d0d04200220096a2c000041bf7f4c0d04200320096b21030b200220096a21000240200428020020056b20034f0d0020042005200310ab86808000200428020821050b200428020420056a2000200310848e8080001a2004200520036a22053602080b024020042802002005470d0020042005410110ab86808000200428020821050b200428020420056a41223a00002004200541016a36020841000f0b200041016a2100200b2d0000220c41a881c280006a2d0000220b450d000b02402009200920006a220d417f6a220e4f0d0002402009450d000240200320094b0d0020032009460d010c0f0b200220096a2c00004140480d0e0b02400240200e2003490d00200d20076a0d0f0c010b200620096a20006a2c000041bf7f4c0d0e0b0240200428020020056b2000417f6a220e4f0d0020042005200e10ab86808000200428020821050b200428020420056a200220096a200e10848e8080001a2004200520006a417f6a22053602080b200a20006a210a41b3d9c280002100200b41a47f6a0e1a080a0a0a0a0a010a0a0a060a0a0a0a0a0a0a050a0a0a040a0302070b2002200320092003418cd9c28000109781808000000b41b5d9c2800021000c060b200c410f71419881c280006a2d0000210b200c410476419881c280006a2d0000210c0240200428020020056b41054b0d0020042005410610ab86808000200428020821050b200428020420056a2200200b3a00052000200c3a0004200041dceac18103360000200541066a21050c060b41bdd9c2800021000c040b41bbd9c2800021000c030b41b9d9c2800021000c020b41b7d9c2800021000c010b200b4122470d0241b1d9c2800021000b0240200428020020056b41014b0d0020042005410210ab86808000200428020821050b200428020420056a20002f00003b0000200541026a21050b20042005360208200d21090c010b0b41f4d7c28000412841fcd8c2800010f880808000000b200220032009200920006a417f6a419cd9c28000109781808000000b830b010a7f0240024002400240024002400240024020002d00000e06000102050304000b024020012802002202280200200228020822006b41034b0d0020022000410410ab86808000200228020821000b200228020420006a41eeeab1e3063600002002200041046a3602080c050b20012802002102024020002d00010d0002402002280200200228020822006b41044b0d0020022000410510ab86808000200228020821000b2002200041056a360208200228020420006a220241002800acd9c28000360000200241046a41002d00b0d9c280003a000041000f0b02402002280200200228020822006b41034b0d0020022000410410ab86808000200228020821000b200228020420006a41f4e4d5ab063600002002200041046a3602080c040b200041086a2802002103024020012802002202280200200228020822046b2000410c6a28020022004f0d0020022004200010ab86808000200228020821040b200228020420046a2003200010848e8080001a2002200420006a3602080c030b2001200041046a10b0868080000f0b2000410c6a280200210402402001280200220528020020052802082202470d0020052002410110ab86808000200528020821020b200528020420026a41fb003a00002005200241016a22023602084180022106024020040d00024020052802002002470d0020052002410110ab86808000200528020821020b200528020420026a41fd003a00002005200241016a360208410021060b4100210220044100200028020422031b210720034100472108200041086a2802002104034002400240024002402007450d000240024020020d0020080d010b20080d0441f887c6800010a081808000000b41012108200321022004450d0220042100024020044107712203450d0003402000417f6a210020022802bc0221022003417f6a22030d000b0b200441084f0d010c020b0240200641ff01710d004100210020064180fe0371450d070240200528020020052802082202470d0020052002410110ab86808000200528020821020b200528020420026a41fd003a00002005200241016a36020841000f0b41f4d7c280004128419cdcc2800010f880808000000b034020022802bc022802bc022802bc022802bc022802bc022802bc022802bc022802bc022102200041786a22000d000b0b41002104410021030b024002400240200420022f01ba02490d00034020022802b0012200450d02200341016a210320022f01b802210420002102200420002f01ba024f0d000b0b200441016a2109024020030d00200221000c020b200220094102746a41bc026a2802002100410021092003417f6a220a450d012003417e6a210b0240200a4107712203450d000340200a417f6a210a20002802bc0221002003417f6a22030d000b0b200b4107490d01034020002802bc022802bc022802bc022802bc022802bc022802bc022802bc022802bc022100200a41786a220a0d000c020b0b41e887c6800010a081808000000b0240200641ff01710d002004410474210320022004410c6c6a220441bc016a280200210a200441b8016a2802002104024020064180fe0371418002460d00024020052802002005280208220b470d002005200b410110ab868080002005280208210b0b2005280204200b6a412c3a00002005200b41016a3602080b200220036a210b200120022004200a10ae868080001a0240200528020020052802082202470d0020052002410110ab86808000200528020821020b2007417f6a2107200641ff817c71418004722106200528020420026a413a3a00002005200241016a360208410021032009210420002102200b200110af868080002200450d010c040b0b41f4d7c280004128418cdcc2800010f880808000000b20012002200041086a2802002000410c6a28020010ae868080001a0b410021000b20000bfd0201047f200128020821022001280204210102402000280200220328020020032802082204470d0020032004410110ab86808000200328020821040b200328020420046a41db003a00002003200441016a22043602080240024002400240024002402002450d0020024104742105418002210203402002220441ff01710d02024020044180fe0371418002460d000240200328020020032802082202470d0020032002410110ab86808000200328020821020b200328020420026a412c3a00002003200241016a3602080b2001200010af8680800022020d06200141106a2101200441ff817c71418004722102200541706a22050d000b200441ff01710d02200328020020032802082204470d040c030b20032802002004460d020c030b41f4d7c28000412841acdcc2800010f880808000000b41f4d7c28000412841bcdcc2800010f880808000000b20032004410110ab86808000200328020821040b200328020420046a41dd003a00002003200441016a360208410021020b20020bc70602077f037e2380808080004180016b2202248080808000024002400240024002400240024010fa81808000220341fe014b0d002002200341016a3602284100210441b1e3c080004113200241286a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128020022032003280200417f6a22053602002001280228210620012d00042107024020050d00200341086a28020022082003410c6a28020022052802001180808080000002402005280204450d00200841002802c0a3c68000118080808000000b200341046a22052005280200417f6a220536020020050d00200341002802c0a3c68000118080808000000b0240200741ff0171450d00410221010c050b200241186a2001412c6a220141186a290000370300200241106a200141106a290000370300200241086a200141086a2900003703002002200129000037030020024200200629030022092009428080e983b1de16541b370320200241286a2002200241206a10a38c808000200241286a41106a290300210a200241286a41086a2903002109024020022802280d002002290320220b2009560d02200b20095a0d0320022009200b7d370328200241286a108d8a8080000c030b200228022c22014108762104200141ff0171410e470d040c030b200041093b0100200128020022012001280200417f6a220036020020000d05200141086a28020022032001410c6a28020022002802001180808080000002402000280204450d00200341002802c0a3c68000118080808000000b200141046a22002000280200417f6a220036020020000d05200141002802c0a3c68000118080808000000c050b2002200b20097d370328200241286a108f8a8080000b200241d0006a200241186a290300370300200241c8006a200241106a290300370300200241286a41186a200241086a2903003703002002200229030037033820022002290320370330200241033a0028200241286a108e8a808000410021040b2004410874410e72210141b0a1c6800021030c010b2004410874200141ff017172210141d0a1c6800021030b2003280200118880808000002000200a37020c2000200937020420002001360200200241ff006a10fb818080000b20024180016a2480808080000bdd0502067f027e23808080800041e0006b2202248080808000024002400240024010fa81808000220341fe014b0d002002200341016a36022041b1e3c080004113200241206a410441002802e0a1c680001186808080000041002802e8a1c68000118880808000002001280228210420012d00042105200128020022032003280200417f6a2206360200024020060d00200341086a28020022072003410c6a28020022062802001180808080000002402006280204450d00200741002802c0a3c68000118080808000000b200341046a22062006280200417f6a220636020020060d00200341002802c0a3c68000118080808000000b0240200541ff01714101460d0041002101410221030c020b200241186a200141056a220341186a290000370300200241106a200341106a290000370300200241086a200341086a29000037030020022003290000370300200241206a41186a2001412c6a220141186a290000370300200241206a41106a200141106a290000370300200241206a41086a200141086a2900003703002002200129000037032041002101200241c0006a2002200241206a20042903004100109d8c808000200241c0006a41106a2903002108200241c0006a41086a290300210902402002280240450d00200228024422034108762101200341ff0171410e470d020b2001410874410e72210141b0a1c6800021030c020b200041093b0100200128020022012001280200417f6a220036020020000d02200141086a28020022032001410c6a28020022002802001180808080000002402000280204450d00200341002802c0a3c68000118080808000000b200141046a22002000280200417f6a220036020020000d02200141002802c0a3c68000118080808000000c020b2001410874200341ff017172210141d0a1c6800021030b2003280200118880808000002000200837020c2000200937020420002001360200200241df006a10fb818080000b200241e0006a2480808080000be70501057f23808080800041106b2202248080808000024002400240024010fa81808000220341fe014b0d002002200341016a36020841b1e3c080004113200241086a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128020022032003280200417f6a22043602002001280228210520012d00042101024020040d00200341086a28020022062003410c6a28020022042802001180808080000002402004280204450d00200641002802c0a3c68000118080808000000b200341046a22042004280200417f6a220436020020040d00200341002802c0a3c68000118080808000000b200141ff01710d010240200541086a2802002203450d002003417f6a41ffffffff01712106200541046a2802002204210102402003410171450d0020042802042004280208200441106a280200200441146a28020041002802e0a1c6800011868080800000200441186a21010b2006450d002004200341186c6a210303402001280204200141086a280200200141106a280200200141146a28020041002802e0a1c68000118680808000002001411c6a280200200141206a280200200141286a2802002001412c6a28020041002802e0a1c6800011868080800000200141306a22012003470d000b0b4100210141002802b0a1c6800011888080800000200041003a001820004200370308200042023703000c020b200041003a001820004200370300200041093b0120200128020022012001280200417f6a220036020020000d02200141086a28020022032001410c6a28020022002802001180808080000002402000280204450d00200341002802c0a3c68000118080808000000b200141046a22002000280200417f6a220036020020000d02200141002802c0a3c68000118080808000000c020b41002802d0a1c680001188808080000020004200370300200041003a0018200041086a4200370300410221010b200020013a00202002410f6a10fb818080000b200241106a2480808080000bb30301047f23808080800041106b22022480808080000240024010fa81808000220341fe014b0d002002200341016a36020841b1e3c080004113200241086a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128020022032003280200417f6a220436020020012d00042101024020040d00200341086a28020022052003410c6a28020022042802001180808080000002402004280204450d00200541002802c0a3c68000118080808000000b200341046a22042004280200417f6a220436020020040d00200341002802c0a3c68000118080808000000b41b0a1c6800041d0a1c68000200141ff017141014622011b280200118880808000002000410e410220011b3a00002002410f6a10fb818080000c010b200041093b0100200128020022012001280200417f6a220036020020000d00200141086a28020022032001410c6a28020022002802001180808080000002402000280204450d00200341002802c0a3c68000118080808000000b200141046a22002000280200417f6a220036020020000d00200141002802c0a3c68000118080808000000b200241106a2480808080000bd00602077f027e23808080800041d0026b22022480808080000240024010fa81808000220341fe014b0d002002200341016a36022041b1e3c080004113200241206a410441002802e0a1c680001186808080000041002802e8a1c680001188808080000020012d00102104200128020821052001280204210320012802002106200128020c22012001280200417f6a2207360200024020070d00200141086a28020022082001410c6a28020022072802001180808080000002402007280204450d00200841002802c0a3c68000118080808000000b200141046a22072007280200417f6a220736020020070d00200141002802c0a3c68000118080808000000b4102210102400240200441ff01710d00200241206a2003200510978a808000024020022d00202201410e470d004100210141e7adc4800041052003200541002802e0a1c6800011868080800000200241043a0020200241206a10f588808000200241163a0020200241023a0028200241206a108e8a80800002402006450d00200341002802c0a3c68000118080808000000b200241206a10e189808000200241c0026a290300210920022903b802210a41002802b0a1c68000118880808000002000420137030820004202370300411021030c020b200241176a200241306a280000360000200241106a200241296a290000370300200220022900213703080b02402006450d00200341002802c0a3c68000118080808000000b20002002290308370021200041306a200241176a28000036000041082103200041296a200241086a41086a29030037000041002802d0a1c680001188808080000042002109200042003703004201210a0b200020036a200a370300200020013a002020002009370318200241cf026a10fb818080000c010b200041003a001820004200370300200041093b0120200128020c22002000280200417f6a2203360200024020030d00200041086a28020022042000410c6a28020022032802001180808080000002402003280204450d00200441002802c0a3c68000118080808000000b200041046a22032003280200417f6a220336020020030d00200041002802c0a3c68000118080808000000b2001280200450d00200128020441002802c0a3c68000118080808000000b200241d0026a2480808080000bf10603037f027e027f23808080800041d0006b22022480808080000240024002400240024002400240024010fa81808000220341fe014b0d002002200341016a36022041b1e3c080004113200241206a410441002802e0a1c680001186808080000041002802e8a1c680001188808080000020012d00382104200129033021052001290328210620012d00042103200128020022012001280200417f6a2207360200024020070d00200141086a28020022082001410c6a28020022072802001180808080000002402007280204450d00200841002802c0a3c68000118080808000000b200141046a22072007280200417f6a220736020020070d00200141002802c0a3c68000118080808000000b200341ff01710d0102400240200650450d002005500d05200441ff01710d010c050b2005500d040b200242919fd78da1a1ad84ff003703282002429ceccef7a6c0dedd2037032020024290f1f7a1ea9083df363703382002428b87a199bee8b0f0ac7f37033041002d00fca3c680001a411241002802c8a3c68000118180808000002201450d0220012006370001200141013a000020012005370009200120043a0011200241206a41202001411241002802e0a1c6800011868080800000200141002802c0a3c68000118080808000002002410e3a000c0c040b200041093b0100200128020022012001280200417f6a220036020020000d06200141086a28020022032001410c6a28020022002802001180808080000002402000280204450d00200341002802c0a3c68000118080808000000b200141046a22002000280200417f6a220036020020000d06200141002802c0a3c68000118080808000000c060b200241023a000c0c030b4101411210b280808000000b2002410c6a410410d28880800020022d000c410e470d010b200241206a41106a2002410c6a41106a280200360200200241206a41086a2002410c6a41086a2902003703002002200229020c37032041b0a1c6800021010c010b200241206a41106a2002410c6a41106a280200360200200241206a41086a2002410c6a41086a2902003703002002200229020c37032041d0a1c6800021010b20002002290320370200200041106a200241206a41106a280200360200200041086a200241206a41086a290300370200200128020011888080800000200241cf006a10fb818080000b200241d0006a2480808080000bc20503057f047e017f23808080800041a0016b2202248080808000024002400240024010fa81808000220341fe014b0d002002200341016a36022841b1e3c080004113200241286a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128020022032003280200417f6a22043602002001280228210520012d00042101024020040d00200341086a28020022062003410c6a28020022042802001180808080000002402004280204450d00200641002802c0a3c68000118080808000000b200341046a22042004280200417f6a220436020020040d00200341002802c0a3c68000118080808000000b200141ff0171450d014102210141d0a1c6800021030c020b200041093b0100200128020022012001280200417f6a220036020020000d02200141086a28020022032001410c6a28020022002802001180808080000002402000280204450d00200341002802c0a3c68000118080808000000b200141046a22002000280200417f6a220036020020000d02200141002802c0a3c68000118080808000000c020b200241086a41186a2201200541186a2900002207370300200241086a41106a2203200541106a2900002208370300200241086a41086a2204200541086a290000220937030020022005290000220a370308200241c1006a22052007370000200241396a22062008370000200241316a220b20093700002002200a370029200241003a0028200241ff006a10ac8a808000200241286a200241ff006a4120109388808000200241c9006a20012903003700002005200329030037000020062004290300370000200b2002290308370000200241d1006a41003a0000200241063a0030200241163a0028200241286a108e8a808000410e210141b0a1c6800021030b200328020011888080800000200020013a00002002419f016a10fb818080000b200241a0016a2480808080000bae0401057f23808080800041306b22022480808080000240024002400240024010fa81808000220341fe014b0d002002200341016a36020c41b1e3c0800041132002410c6a410441002802e0a1c680001186808080000041002802e8a1c68000118880808000002001280228210420012d00042103200128020022012001280200417f6a2205360200024020050d00200141086a28020022062001410c6a28020022052802001180808080000002402005280204450d00200641002802c0a3c68000118080808000000b200141046a22052005280200417f6a220536020020050d00200141002802c0a3c68000118080808000000b200341ff01714101470d022002410c6a2004280204200428020822014100280298a3c680001185808080000020024184dec28000411010d08880800020022802000d014188dfc2800010a081808000000b200041093b0100200128020022012001280200417f6a220036020020000d03200141086a28020022032001410c6a28020022002802001180808080000002402000280204450d00200341002802c0a3c68000118080808000000b200141046a22002000280200417f6a220036020020000d03200141002802c0a3c68000118080808000000c030b200228020420012002410c6a41002802d0a3c6800011858080800000410e210141b0a1c6800021030c010b4102210141d0a1c6800021030b200328020011888080800000200020013a00002002412f6a10fb818080000b200241306a2480808080000b8e0601057f23808080800041106b2202248080808000024002400240024010fa81808000220341fe014b0d002002200341016a36020841b1e3c080004113200241086a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128020022032003280200417f6a22043602002001280228210520012d00042101024020040d00200341086a28020022062003410c6a28020022042802001180808080000002402004280204450d00200641002802c0a3c68000118080808000000b200341046a22042004280200417f6a220436020020040d00200341002802c0a3c68000118080808000000b200141ff01710d010240200541086a2802002204450d002004417f6a41ffffffff03712106200541046a28020022052101024020044103712203450d002005210103402001280204200141086a28020041002802a0a1c68000118480808000002001410c6a21012003417f6a22030d000b0b20064103490d0020052004410c6c6a210303402001280204200141086a28020041002802a0a1c6800011848080800000200141106a280200200141146a28020041002802a0a1c68000118480808000002001411c6a280200200141206a28020041002802a0a1c6800011848080800000200141286a2802002001412c6a28020041002802a0a1c6800011848080800000200141306a22012003470d000b0b4100210141002802b0a1c6800011888080800000200041003a001820004200370308200042023703000c020b200041003a001820004200370300200041093b0120200128020022012001280200417f6a220336020020030d02200141086a28020022002001410c6a28020022032802001180808080000002402003280204450d00200041002802c0a3c68000118080808000000b200141046a22032003280200417f6a220336020020030d02200141002802c0a3c68000118080808000000c020b41002802d0a1c680001188808080000020004200370300200041003a0018200041086a4200370300410221010b200020013a00202002410f6a10fb818080000b200241106a2480808080000b9c0502057f017e23808080800041106b2202248080808000024002400240024010fa81808000220341fe014b0d002002200341016a36020841b1e3c080004113200241086a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128020022032003280200417f6a22043602002001280228210520012d00042101024020040d00200341086a28020022062003410c6a28020022042802001180808080000002402004280204450d00200641002802c0a3c68000118080808000000b200341046a22042004280200417f6a220436020020040d00200341002802c0a3c68000118080808000000b200141ff01714101470d01410021010240200541086a2802002206450d0020064105742104200541046a2802002103410021010340200141016a2205417f20051b2001200310a08c8080001b2101200341206a2103200441606a22040d000b02400240200120064b0d002001ad428094ebdc037e2006ad8022074280808080105a0d002007a721010c010b418094ebdc0321010b200141ffd193ad034b21010b41002802b0a1c6800011888080800000200041003a001820004200370308200042023703000c020b200041003a001820004200370300200041093b0120200128020022012001280200417f6a220336020020030d02200141086a28020022042001410c6a28020022032802001180808080000002402003280204450d00200441002802c0a3c68000118080808000000b200141046a22032003280200417f6a220336020020030d02200141002802c0a3c68000118080808000000c020b41002802d0a1c680001188808080000020004200370300200041003a0018200041086a4200370300410221010b200020013a00202002410f6a10fb818080000b200241106a2480808080000bf30402057f017e23808080800041206b22022480808080000240024002400240024010fa81808000220341fe014b0d002002200341016a36020841b1e3c080004113200241086a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128020022032003280200417f6a22043602002001280228210520012d00042101024020040d00200341086a28020022062003410c6a28020022042802001180808080000002402004280204450d00200641002802c0a3c68000118080808000000b200341046a22042004280200417f6a220436020020040d00200341002802c0a3c68000118080808000000b200141ff0171450d0141002802d0a1c680001188808080000041022101420021070c020b200041003a001820004200370300200041093b0120200128020022002000280200417f6a220136020020010d02200041086a28020022032000410c6a28020022012802001180808080000002402001280204450d00200341002802c0a3c68000118080808000000b200041046a22012001280200417f6a220136020020010d02200041002802c0a3c68000118080808000000c020b4100210141002d00fca3c680001a410841002802c8a3c68000118180808000002203450d022003200529030037000041bfd9c28000410a2003410841002802e0a1c6800011868080800000200341002802c0a3c6800011808080800000200241043a0008200241086a10f58880800041002802b0a1c6800011888080800000420221070b200020013a0020200041003a001820004200370308200020073703002002411f6a10fb818080000b200241206a2480808080000f0b4101410810b280808000000bfc0304027f027e017f017e23808080800041c0006b2202248080808000024002400240024010fa81808000220341fe014b0d002002200341016a36021041b1e3c080004113200241106a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200241306a41086a200141086a28020036020020022001290200370330200241106a200241306a109c8a80800042022104200229031022054202510d01200220022800213602082002200241246a28000036000b20022d0020210120022802282103200228022c2106200229031821072000411c6a200228000b3600002000200228020836001941002802b0a1c6800011888080800000200020073703100c020b200041003a001820004200370300200041093b01202001280200450d02200128020441002802c0a3c68000118080808000000c020b2002200241216a2800003602082002200241246a28000036000b200241206a2d00002101200241286a2802002103200229031821042000412c6a200228000b3600002000200228020836002941002802d0a1c680001188808080000020002003360230200020013a00282004422088a721062004a7210342002104410021010b2000200636022420002003360220200020013a001820002005370308200020043703002002413f6a10fb818080000b200241c0006a2480808080000be10502067f027e23808080800041e0006b2202248080808000024002400240024010fa81808000220341fe014b0d002002200341016a36022041b1e3c080004113200241206a410441002802e0a1c680001186808080000041002802e8a1c68000118880808000002001280228210420012d00042105200128020022032003280200417f6a2206360200024020060d00200341086a28020022072003410c6a28020022062802001180808080000002402006280204450d00200741002802c0a3c68000118080808000000b200341046a22062006280200417f6a220636020020060d00200341002802c0a3c68000118080808000000b0240200541ff01714101460d0041002101410221030c020b200241186a200141056a220341186a290000370300200241106a200341106a290000370300200241086a200341086a29000037030020022003290000370300200241206a41186a2001412c6a220141186a290000370300200241206a41106a200141106a290000370300200241206a41086a200141086a29000037030020022001290000370320200241c0006a2002200241206a20042903004102109d8c808000200241c0006a41106a2903002108200241c0006a41086a29030021090240024020022802400d00410021010c010b200228024422034108762101200341ff0171410e470d020b2001410874410e72210141b0a1c6800021030c020b200041093b0100200128020022012001280200417f6a220036020020000d02200141086a28020022032001410c6a28020022002802001180808080000002402000280204450d00200341002802c0a3c68000118080808000000b200141046a22002000280200417f6a220036020020000d02200141002802c0a3c68000118080808000000c020b2001410874200341ff017172210141d0a1c6800021030b2003280200118880808000002000200837020c2000200937020420002001360200200241df006a10fb818080000b200241e0006a2480808080000b880502067f017e23808080800041a0016b2202248080808000024002400240024010fa81808000220341fe014b0d002002200341016a36024841b1e3c080004113200241c8006a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128020022032003280200417f6a22043602002001280228210520012d00042106024020040d00200341086a28020022072003410c6a28020022042802001180808080000002402004280204450d00200741002802c0a3c68000118080808000000b200341046a22042004280200417f6a220436020020040d00200341002802c0a3c68000118080808000000b200641ff01714101460d014102210141d0a1c680002103420021080c020b200041003a001820004200370300200041093b0120200128020022002000280200417f6a220136020020010d02200041086a28020022032000410c6a28020022012802001180808080000002402001280204450d00200341002802c0a3c68000118080808000000b200041046a22012001280200417f6a220136020020010d02200041002802c0a3c68000118080808000000c020b200241196a200141056a220141186a290000370000200241116a200141106a290000370000200241096a200141086a2900003700002002200129000037000141002101200241216a200528020420052802084100280298a3c6800011858080800000200241053a0000200241c8006a41086a200241c80010848e8080001a200241163a0048200241c8006a108e8a8080004202210841b0a1c6800021030b200328020011888080800000200020013a0020200041003a001820004200370308200020083703002002419f016a10fb818080000b200241a0016a2480808080000bf50301067f23808080800041106b2202248080808000024002400240024010fa81808000220341fe014b0d002002200341016a36020841b1e3c080004113200241086a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128020022032003280200417f6a2204360200200128022c21052001280228210620012d00042101024020040d00200341086a28020022072003410c6a28020022042802001180808080000002402004280204450d00200741002802c0a3c68000118080808000000b200341046a22042004280200417f6a220436020020040d00200341002802c0a3c68000118080808000000b200141ff01714101460d014102210141d0a1c6800021030c020b200041093b0100200128020022012001280200417f6a220036020020000d02200141086a28020022032001410c6a28020022002802001180808080000002402000280204450d00200341002802c0a3c68000118080808000000b200141046a22002000280200417f6a220036020020000d02200141002802c0a3c68000118080808000000c020b200628020420062802082005280204200528020841002802b8a3c6800011868080800000410e210141b0a1c6800021030b200328020011888080800000200020013a00002002410f6a10fb818080000b200241106a2480808080000be40301057f23808080800041106b2202248080808000024002400240024010fa81808000220341fe014b0d002002200341016a36020841b1e3c080004113200241086a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128020022032003280200417f6a22043602002001280228210520012d00042101024020040d00200341086a28020022062003410c6a28020022042802001180808080000002402004280204450d00200641002802c0a3c68000118080808000000b200341046a22042004280200417f6a220436020020040d00200341002802c0a3c68000118080808000000b200141ff01714101460d014102210141d0a1c6800021030c020b200041093b0100200128020022012001280200417f6a220036020020000d02200141086a28020022032001410c6a28020022002802001180808080000002402000280204450d00200341002802c0a3c68000118080808000000b200141046a22002000280200417f6a220036020020000d02200141002802c0a3c68000118080808000000c020b2005280204200528020841002802b0a3c6800011848080800000410e210141b0a1c6800021030b200328020011888080800000200020013a00002002410f6a10fb818080000b200241106a2480808080000b980502077f027e23808080800041c0006b22022480808080000240024002400240024010fa81808000220341fe014b0d002002200341016a36020041b1e3c0800041132002410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128022c21042001280228210520012d00042106200128020022032003280200417f6a2207360200024020070d00200341086a28020022082003410c6a28020022072802001180808080000002402007280204450d00200841002802c0a3c68000118080808000000b200341046a22072007280200417f6a220736020020070d00200341002802c0a3c68000118080808000000b200641ff01714101470d01200241186a200141056a220141186a290000370300200241106a200141106a290000370300200241086a200141086a2900003703002002200129000037030041002101200241206a2002200429030020052d000041017441004100109e8c808000200241206a41106a2903002109200241206a41086a290300210a02402002280220450d00200228022422034108762101200341ff0171410e470d030b2001410874410e72210141b0a1c6800021030c030b200041093b0100200128020022012001280200417f6a220036020020000d03200141086a28020022032001410c6a28020022002802001180808080000002402000280204450d00200341002802c0a3c68000118080808000000b200141046a22002000280200417f6a220036020020000d03200141002802c0a3c68000118080808000000c030b41002101410221030b2001410874200341ff017172210141d0a1c6800021030b2003280200118880808000002000200937020c2000200a370204200020013602002002413f6a10fb818080000b200241c0006a2480808080000ba40401067f23808080800041306b2202248080808000024002400240024010fa81808000220341fe014b0d002002200341016a36020841b1e3c080004113200241086a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128020022032003280200417f6a22043602002001280228210520012d00042106024020040d00200341086a28020022072003410c6a28020022042802001180808080000002402004280204450d00200741002802c0a3c68000118080808000000b200341046a22042004280200417f6a220436020020040d00200341002802c0a3c68000118080808000000b200641ff01710d01200241086a41186a2001412c6a220141186a290000370300200241086a41106a200141106a290000370300200241086a41086a200141086a29000037030020022001290000370308200241086a2005290300109f8c8080001a41002802b0a1c6800011888080800000410e21010c020b200041093b0100200128020022012001280200417f6a220036020020000d02200141086a28020022032001410c6a28020022002802001180808080000002402000280204450d00200341002802c0a3c68000118080808000000b200141046a22002000280200417f6a220036020020000d02200141002802c0a3c68000118080808000000c020b41002802d0a1c6800011888080800000410221010b200020013a00002002412f6a10fb818080000b200241306a2480808080000ba10501067f23808080800041106b220224808080800002400240024002400240024010fa81808000220341fe014b0d002002200341016a36020841b1e3c080004113200241086a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128020022032003280200417f6a2204360200200128022c21052001280228210620012d00042101024020040d00200341086a28020022072003410c6a28020022042802001180808080000002402004280204450d00200741002802c0a3c68000118080808000000b200341046a22042004280200417f6a220436020020040d00200341002802c0a3c68000118080808000000b200141ff01710d0141002101200220062802042204200628020822034101200528020041002802a8a1c680001187808080000002402002280200450d002003450d002003417f4c0d0541002d00fca3c680001a200341002802c8a3c68000118180808000002206450d0620062004200310848e80800041002802c0a3c68000118080808000000b41002802b0a1c6800011888080800000200041003a001820004200370308200042023703000c020b200041003a001820004200370300200041093b0120200128020022002000280200417f6a220136020020010d02200041086a28020022032000410c6a28020022012802001180808080000002402001280204450d00200341002802c0a3c68000118080808000000b200041046a22012001280200417f6a220136020020010d02200041002802c0a3c68000118080808000000c020b41002802d0a1c680001188808080000020004200370300200041003a0018200041086a4200370300410221010b200020013a00202002410f6a10fb818080000b200241106a2480808080000f0b10ae80808000000b4101200310b280808000000bcf0502077f027e23808080800041b0026b22022480808080000240024010fa81808000220341fe014b0d002002200341016a36020041b1e3c0800041132002410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128020c22032003280200417f6a220436020020012d00102105200128020821062001280204210720012802002108024020040d00200341086a28020022042003410c6a28020022012802001180808080000002402001280204450d00200441002802c0a3c68000118080808000000b200341046a22012001280200417f6a220136020020010d00200341002802c0a3c68000118080808000000b02400240200541ff01710d004100210141e7adc4800041052007200641002802e0a1c6800011868080800000200241043a0000200210f588808000200241163a0000200241023a00082002108e8a80800002402008450d00200741002802c0a3c68000118080808000000b200210e189808000200241a0026a2903002109200229039802210a41002802b0a1c68000118880808000002000420137030820004202370300411021030c010b02402008450d00200741002802c0a3c68000118080808000000b41002802d0a1c68000118880808000004200210920004200370300410221014201210a410821030b200020036a200a370300200020013a002020002009370318200241af026a10fb818080000c010b200041003a001820004200370300200041093b0120200128020c22002000280200417f6a2203360200024020030d00200041086a28020022042000410c6a28020022032802001180808080000002402003280204450d00200441002802c0a3c68000118080808000000b200041046a22032003280200417f6a220336020020030d00200041002802c0a3c68000118080808000000b2001280200450d00200128020441002802c0a3c68000118080808000000b200241b0026a2480808080000bc20503057f047e017f23808080800041a0016b2202248080808000024002400240024010fa81808000220341fe014b0d002002200341016a36022841b1e3c080004113200241286a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128020022032003280200417f6a22043602002001280228210520012d00042101024020040d00200341086a28020022062003410c6a28020022042802001180808080000002402004280204450d00200641002802c0a3c68000118080808000000b200341046a22042004280200417f6a220436020020040d00200341002802c0a3c68000118080808000000b200141ff0171450d014102210141d0a1c6800021030c020b200041093b0100200128020022012001280200417f6a220036020020000d02200141086a28020022032001410c6a28020022002802001180808080000002402000280204450d00200341002802c0a3c68000118080808000000b200141046a22002000280200417f6a220036020020000d02200141002802c0a3c68000118080808000000c020b200241086a41186a2201200541186a2900002207370300200241086a41106a2203200541106a2900002208370300200241086a41086a2204200541086a290000220937030020022005290000220a370308200241c1006a22052007370000200241396a22062008370000200241316a220b20093700002002200a370029200241013a0028200241ff006a10ac8a808000200241286a200241ff006a4120109388808000200241c9006a20012903003700002005200329030037000020062004290300370000200b2002290308370000200241d1006a41013a0000200241063a0030200241163a0028200241286a108e8a808000410e210141b0a1c6800021030b200328020011888080800000200020013a00002002419f016a10fb818080000b200241a0016a2480808080000bfb0602067f027e2380808080004180016b220224808080800002400240024002400240024010fa81808000220341fe014b0d002002200341016a36023841b1e3c080004113200241386a410441002802e0a1c680001186808080000041002802e8a1c68000118880808000002001280228210420012d00042105200128020022032003280200417f6a2206360200024020060d00200341086a28020022072003410c6a28020022062802001180808080000002402006280204450d00200741002802c0a3c68000118080808000000b200341046a22062006280200417f6a220636020020060d00200341002802c0a3c68000118080808000000b0240200541ff01714101460d0041002101410221030c040b2001412c6a2103200241186a200141056a220141186a290000370300200241106a200141106a290000370300200241086a200141086a2900003703002002200129000037030020042d00002101200241386a200210fa878080004200200241e0006a2903002208200241d8006a2903007d220920092008561b210820014101742105200241386a41186a290300210920010d012009500d02200241386a200210fa878080002002280268450d02200228026c4102490d010c020b200041093b0100200128020022012001280200417f6a220036020020000d04200141086a28020022032001410c6a28020022002802001180808080000002402000280204450d00200341002802c0a3c68000118080808000000b200141046a22002000280200417f6a220036020020000d04200141002802c0a3c68000118080808000000c040b2008428080e983b1de162008428080e983b1de16561b21080b200241386a41186a200341186a290000370300200241386a41106a200341106a290000370300200241386a41086a200341086a29000037030020022003290000370338200241206a2002200241386a4200200920087d220820082009561b2005109d8c808000200241206a41106a2903002109200241206a41086a29030021080240024020022802200d00410021010c010b200228022422034108762101200341ff0171410e470d010b2001410874410e72210141b0a1c6800021030c010b2001410874200341ff017172210141d0a1c6800021030b2003280200118880808000002000200937020c2000200837020420002001360200200241ff006a10fb818080000b20024180016a2480808080000bb20802067f027e23808080800041a0016b2202248080808000024002400240024002400240024010fa81808000220341fe014b0d002002200341016a36024041b1e3c080004113200241c0006a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128022c21042001280228210520012d00042103200128020022012001280200417f6a2206360200024020060d00200141086a28020022072001410c6a28020022062802001180808080000002402006280204450d00200741002802c0a3c68000118080808000000b200141046a22062006280200417f6a220636020020060d00200141002802c0a3c68000118080808000000b200341ff01710d010240200529030050450d00410c21010c030b20024298d5afd2c6aeacae2f370348200242c2cdc8b0c7b9e78f857f370340200242e4c5bdb4b2e9a5a6807f370358200242d790d7a3fef9fda0c800370350200241186a200241c0006a10e6888080002002290320420020022802181b2108200529030021090240024020042d00000d00427f200820097c220920092008541b21090c010b4200200820097d220920092008561b21090b20024298d5afd2c6aeacae2f370348200242c2cdc8b0c7b9e78f857f370340200242a2bba4efe3ffa586553703582002429c9a9bbf88a5a0fc937f370350200241086a200241c0006a10e68880800002402002280208450d00410b210120022903102009560d030b20024298d5afd2c6aeacae2f370348200242c2cdc8b0c7b9e78f857f370340200242e4c5bdb4b2e9a5a6807f370358200242d790d7a3fef9fda0c8003703502002200937039001200241c0006a412020024190016a410841002802e0a1c68000118680808000002002200937035020022008370348200241153a0040200241c0006a108e8a8080002002410e3a002c0c030b200041093b0100200128020022012001280200417f6a220036020020000d05200141086a28020022032001410c6a28020022002802001180808080000002402000280204450d00200341002802c0a3c68000118080808000000b200141046a22002000280200417f6a220036020020000d05200141002802c0a3c68000118080808000000c050b200241023a002c0c020b2002412c6a200110f88880800020022d002c410e470d010b200241c0006a41106a2002412c6a41106a280200360200200241c0006a41086a2002412c6a41086a2902003703002002200229022c37034041b0a1c6800021010c010b200241c0006a41106a2002412c6a41106a280200360200200241c0006a41086a2002412c6a41086a2902003703002002200229022c37034041d0a1c6800021010b20002002290340370200200041106a200241c0006a41106a280200360200200041086a200241c0006a41086a2903003702002001280200118880808000002002419f016a10fb818080000b200241a0016a2480808080000bdc0502077f027e23808080800041e0006b2202248080808000024002400240024010fa81808000220341fe014b0d002002200341016a3602204100210441b1e3c080004113200241206a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128020022032003280200417f6a22053602002001280228210620012d00042107024020050d00200341086a28020022082003410c6a28020022052802001180808080000002402005280204450d00200841002802c0a3c68000118080808000000b200341046a22052005280200417f6a220536020020050d00200341002802c0a3c68000118080808000000b0240200741ff0171450d00410221010c020b200241186a2001412c6a220341186a290000370300200241106a200341106a290000370300200241086a200341086a29000037030020022003290000370300200241206a41186a200141cc006a220141186a290000370300200241206a41106a200141106a290000370300200241206a41086a200141086a2900003703002002200129000037032041002104200241c0006a2002200241206a20062903004100109d8c808000200241c0006a41106a2903002109200241c0006a41086a290300210a02402002280240450d00200228024422014108762104200141ff0171410e470d020b2004410874410e72210141b0a1c6800021030c020b200041093b0100200128020022012001280200417f6a220036020020000d02200141086a28020022032001410c6a28020022002802001180808080000002402000280204450d00200341002802c0a3c68000118080808000000b200141046a22002000280200417f6a220036020020000d02200141002802c0a3c68000118080808000000c020b2004410874200141ff017172210141d0a1c6800021030b2003280200118880808000002000200937020c2000200a37020420002001360200200241df006a10fb818080000b200241e0006a2480808080000bc30202047f017e23808080800041106b22022480808080000240024010fa81808000220341fe014b0d002002200341016a36020841b1e3c080004113200241086a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200128020c21030240024020012802002204418080808078470d002003280204200328020841002802a0a1c68000118480808000000c010b200128020421052003280204200328020820012902042206a72006422088a741002802e0a1c68000118680808000002004450d00200541002802c0a3c68000118080808000000b41002802b0a1c68000118880808000002000410e3a00002002410f6a10fb818080000c010b200041093b010020012802002200418080808078460d002000450d00200128020441002802c0a3c68000118080808000000b200241106a2480808080000bac0100024002400240024002400240200241746a0e12010404040404040404040404040404020400040b200141d5d9c28000411d10888e8080000d03200041013a00010c020b200141c9d9c28000410c10888e808000450d030c020b200141f2d9c28000411b10888e8080000d01200041023a00010b200041003a00000f0b2000200120024190dac28000410310e88a808000360204200041013a00000f0b200041003a0001200041003a00000bc80501087f23808080800041106b220224808080800002400240024002400240024002400240200141146a2802002203200141106a28020022044f0d00200128020c21050340200520036a2d0000220641776a220741194b0d06024041012007744193808004710d0020074119470d07200241046a200110ee8a80800020022d00040d0320022d00050e03040506040b2001200341016a220336021420042003470d000b0b2002410536020420002001200241046a10c78a8080003602040c050b200020022802083602040c040b41002105200041003a00010c040b200041013a0001410021050c030b200041023a0001410021050c020b0240200641fb00460d002002410a36020420002001200241046a10c78a8080003602040c010b200120012d0018417f6a22073a00180240200741ff0171450d002001200341016a360214200241046a200110ee8a80800002400240024020022d00040d0020022d00052107200110d78a80800022030d01024002400240024020070e03000102000b200110f08a80800022030d04410021080c020b200110f08a80800022030d03410121080c010b200110f08a80800022030d02410221080b41012105200120012d001841016a3a0018024020012802142203200128021022094f0d00200128020c21060340200620036a2d0000220441776a220741174b0d044101200774419380800471450d042001200341016a220336021420092003470d000b0b2002410336020420002001200241046a10d88a8080003602040c050b200228020821030b2000200336020441012105200120012d001841016a3a00180c030b0240200441fd00470d00200020083a00012001200341016a360214410021050c030b2002410a36020420002001200241046a10d88a8080003602040c010b2002411836020420002001200241046a10c78a8080003602040b410121050b200020053a0000200241106a2480808080000bb70301047f23808080800041106b220224808080800041052103024002400240024002400240024002400240200128020022040e03000201000b417f2001410c6a28020041286c220341246a220520052003410472491b41016a2203450d022003417f4a0d0110ae80808000000b411321030b41002d00fca3c680001a200341002802c8a3c68000118180808000002205450d052002200536020820022003360204024020040e03000304000b410021030c010b2002410036020c2002428080808010370204200241046a4100410110b18280800020022802082105200228020c21030b200520036a41013a00002002200341016a36020c200141046a200241046a10af8c8080000c020b200541023a0000410121032002410136020c2001280204210102402002280204417f6a41034b0d00200241046a4101410410b182808000200228020c21030b200228020820036a20013600002002200341046a36020c0c010b200541033a00002002410136020c200141086a200241046a10b08c8080000b20002002290204370200200041086a200241046a41086a280200360200200241106a2480808080000f0b4101200310b280808000000bb90703047f017e037f23808080800041206b220224808080800002400240417f200141d8006a280200220341286c220441cd006a220520052004410472491b2204417f4c0d0041002d00fca3c680001a200441002802c8a3c68000118180808000002205450d01200220053602102002200436020c200520012903183700002002410836021420012903202106410821040240200228020c4178714108470d002002410c6a4108410810b182808000200228021421040b200228021020046a20063700002002200441086a2204360214200129032821060240200228020c20046b41074b0d002002410c6a2004410810b182808000200228021421040b200228021020046a20063700002002200441086a360214200141d4006a2802002104200220033602182002200241186a36021c2002411c6a2002410c6a10c08a8080002002280214210502402003450d002004200341286c6a210703400240200228020c220820056b411f4b0d002002410c6a2005412010b182808000200228020c2108200228021421050b2002280210220920056a22032004290000370000200341186a200441186a290000370000200341106a200441106a290000370000200341086a200441086a2900003700002002200541206a2205360214200441206a29030021060240200820056b41074b0d002002410c6a2005410810b18280800020022802102109200228021421050b200920056a20063700002002200541086a2205360214200441286a22042007470d000b0b200141306a21040240200228020c220820056b411f4b0d002002410c6a2005412010b182808000200228020c2108200228021421050b2002280210220920056a22032004290000370000200341086a200441086a290000370000200341106a200441106a290000370000200341186a200441186a2900003700002002200541206a2204360214200129030021060240200820046b41074b0d002002410c6a2004410810b18280800020022802102109200228020c2108200228021421040b200920046a20063700002002200441086a2204360214200129030821060240200820046b41074b0d002002410c6a2004410810b18280800020022802102109200228021421040b200920046a20063700002002200441086a220436021420012d001021050240200228020c2004470d002002410c6a2004410110b182808000200228021421040b200228021020046a20053a0000200041086a200441016a3602002000200229020c370200200241206a2480808080000f0b10ae80808000000b4101200410b280808000000bf40603047f017e037f23808080800041206b220224808080800002400240417f200141c8006a280200220341286c220441c5006a220520052004410472491b2204417f4c0d0041002d00fca3c680001a200441002802c8a3c68000118180808000002205450d01200220053602102002200436020c200520012903003700002002410836021420012903082106410821040240200228020c4178714108470d002002410c6a4108410810b182808000200228021421040b200228021020046a20063700002002200441086a2204360214200129031021060240200228020c220520046b41074b0d002002410c6a2004410810b182808000200228020c2105200228021421040b2002280210220720046a20063700002002200441086a2204360214200141186a29030021060240200520046b41074b0d002002410c6a2004410810b18280800020022802102107200228021421040b200720046a20063700002002200441086a360214200141c4006a2802002104200220033602182002200241186a36021c2002411c6a2002410c6a10c08a8080002002280214210502402003450d002004200341286c6a210803400240200228020c220720056b411f4b0d002002410c6a2005412010b182808000200228020c2107200228021421050b2002280210220920056a22032004290000370000200341186a200441186a290000370000200341106a200441106a290000370000200341086a200441086a2900003700002002200541206a2205360214200441206a29030021060240200720056b41074b0d002002410c6a2005410810b18280800020022802102109200228021421050b200920056a20063700002002200541086a2205360214200441286a22042008470d000b0b200141206a21040240200228020c20056b411f4b0d002002410c6a2005412010b182808000200228021421050b2002280210220720056a22032004290000370000200341086a200441086a290000370000200341106a200441106a290000370000200341186a200441186a2900003700002002200541206a220436021420012d004c21050240200228020c2004470d002002410c6a2004410110b18280800020022802102107200228021421040b200720046a20053a0000200041086a200441016a3602002000200229020c370200200241206a2480808080000f0b10ae80808000000b4101200410b280808000000b2100200128021441e691c28000411d200141186a28020028020c118280808000000b2100200128021441be91c280004112200141186a28020028020c118280808000000b02000be80802077f027e23808080800041d0006b220224808080800002400240024002400240200128020822030d002002420437020c200220033602080c010b2001280204210441002d00fca3c680001a2003410474220541002802c8a3c68000118180808000002201450d032004200341286c6a2106200241003602102002200136020c20022003360208034041002d00fca3c680001a024002400240412041002802c8a3c68000118180808000002203450d002002200336021c2002410236021820024100360220200241286a200441002f0194a1c6800010c887808000200228022c210502400240200228023022010d00410121070c010b2001417f4c0d0641002d00fca3c680001a200141002802c8a3c68000118180808000002207450d020b20072005200110848e808000210702402002280228450d00200541002802c0a3c68000118080808000000b200320022f00283b0001200341033a00002003200736020820032001360204200341036a200241286a41026a22082d00003a00002003200136020c20024101360220411421030240200429032022094290ce005a0d002009210a0c030b411421030340200241286a20036a2201417c6a200920094290ce0080220a4290ce007e7da7220541ffff037141e4006e220741017441b6dac280006a2f00003b00002001417e6a2005200741e4006c6b41ffff037141017441b6dac280006a2f00003b00002003417c6a2103200942ffc1d72f562101200a210920010d000c030b0b4104412010b280808000000b4101200110b280808000000b0240200aa7220541e3004d0d00200241286a2003417e6a22036a200aa72201200141ffff037141e4006e220541e4006c6b41ffff037141017441b6dac280006a2f00003b00000b024002402005410a490d00200241286a2003417e6a22016a200541017441b6dac280006a2f00003b00000c010b200241286a2003417f6a22016a200541306a3a00000b411420016b210341012105024020014114460d002003417f4c0d0341002d00fca3c680001a200341002802c8a3c68000118180808000002205450d040b2005200241286a20016a200310848e80800021050240200228022022012002280218470d00200241186a200110a386808000200228022021010b200228021c20014104746a2201200336020c2001200536020820012003360204200141023a00002002200228022041016a36022020022802182101200229021c21090240200228021022032002280208470d00200241086a200310a386808000200228021021030b200228020c20034104746a220320022f00283b0001200341043a00002003200937020820032001360204200341036a20082d00003a00002002200228021041016a360210200441286a22042006470d000b0b200241336a200241086a41086a280200360000200041043a00002002200229030837002b20002002290028370001200041086a2002412f6a290000370000200241d0006a2480808080000f0b10ae80808000000b4101200310b280808000000b4104200510b280808000000ba50702047f027e23808080800041106b22042480808080000240024020002d00000d0020002802042105024020002d00014101460d0002402005280200220628020020062802082207470d0020062007410110ab86808000200628020821070b200628020420076a412c3a00002006200741016a3602080b200041023a0001200520052001200210ae868080001a02402005280200220028020020002802082206470d0020002006410110ab86808000200028020821060b200028020420066a413a3a00002000200641016a36020802402005280200220028020020002802082206470d0020002006410110ab86808000200028020821060b200028020420066a41fb003a00002000200641016a36020820032903082108200329030021092005200541a8dac28000410110ae868080001a02402005280200220028020020002802082206470d0020002006410110ab86808000200028020821060b200028020420066a413a3a00002000200641016a36020802402005280200220028020020002802082206470d0020002006410110ab86808000200028020821060b200028020420066a41db003a00002000200641016a3602082004200536020c2004418002360208200441086a200910d486808000200441086a200810d4868080002004280208220041ff01710d01024020004180fe0371450d000240200428020c280200220028020020002802082206470d0020002006410110ab86808000200028020821060b200028020420066a41dd003a00002000200641016a3602080b20032d0010210602402005280200220028020020002802082203470d0020002003410110ab86808000200028020821030b200028020420036a412c3a00002000200341016a3602082005200541a9dac28000410d10ae868080001a02402005280200220028020020002802082203470d0020002003410110ab86808000200028020821030b200028020420036a413a3a00002000200341016a360208024002400240024020060e03000102000b2005200541c9d9c28000410c10ae868080001a0c020b2005200541d5d9c28000411d10ae868080001a0c010b2005200541f2d9c28000411b10ae868080001a0b02402005280200220528020020052802082200470d0020052000410110ab86808000200528020821000b200528020420006a41fd003a00002005200041016a360208200441106a24808080800041000f0b41f4d7c280004128418cdcc2800010f880808000000b41f4d7c28000412841bcdcc2800010f880808000000b980402057f017e23808080800041306b22022480808080000240024020002d00000d0020002802042103024020002d00014101460d0002402003280200220428020020042802082205470d0020042005410110ab86808000200428020821050b200428020420056a412c3a00002004200541016a3602080b200041023a00012003280200210641142100024020014290ce005a0d00200121070c020b411421000340200241086a20006a2203417c6a200120014290ce008022074290ce007e7da7220441ffff037141e4006e220541017441b6dac280006a2f00003b00002003417e6a2004200541e4006c6b41ffff037141017441b6dac280006a2f00003b00002000417c6a2100200142ffc1d72f5621032007210120030d000c020b0b41f4d7c28000412841acdcc2800010f880808000000b02402007a7220341e3004d0d00200241086a2000417e6a22006a2007a72203200341ffff037141e4006e220341e4006c6b41ffff037141017441b6dac280006a2f00003b00000b024002402003410a490d00200241086a2000417e6a22046a200341017441b6dac280006a2f00003b00000c010b200241086a2000417f6a22046a200341306a3a00000b02402006280200200628020822006b411420046b22034f0d0020062000200310ab86808000200628020821000b200628020420006a200241086a20046a200310848e8080001a2006200020036a360208200241306a2480808080000bda0101037f024020002d00000d0020002802042104024020002d00014101460d0002402004280200220528020020052802082206470d0020052006410110ab86808000200528020821060b200528020420066a412c3a00002005200641016a3602080b200041023a0001200420002001200210ae868080001a02402004280200220028020020002802082205470d0020002005410110ab86808000200028020821050b200028020420056a413a3a00002000200541016a3602082003200410a48c8080000f0b41f4d7c280004128418cdcc2800010f880808000000be50701047f23808080800041106b22042480808080000240024020002d00000d0020002802042105024020002d00014101460d0002402005280200220628020020062802082207470d0020062007410110ab86808000200628020821070b200628020420076a412c3a00002006200741016a3602080b200041023a0001200520052001200210ae868080001a200328020821062003280204210202402005280200220028020020002802082203470d0020002003410110ab86808000200028020821030b200028020420036a413a3a00002000200341016a36020802402005280200220028020020002802082203470d0020002003410110ab86808000200028020821030b200028020420036a41db003a00002000200341016a220336020802400240024020060d00024020002802002003470d0020002003410110ab86808000200028020821030b200028020420036a41dd003a00002000200341016a3602080c010b024020002802002003470d0020002003410110ab86808000200028020821030b200028020420036a41db003a00002000200341016a3602082004200536020c20044180043602082002200510ae8980800022000d01200441086a200229032010d4868080002004280208220041ff01710d03024020004180fe0371450d000240200428020c280200220028020020002802082203470d0020002003410110ab86808000200028020821030b200028020420036a41dd003a00002000200341016a3602080b024020064101460d002002200641286c6a2101200241286a2103034002402005280200220028020020002802082206470d0020002006410110ab86808000200028020821060b200028020420066a412c3a00002000200641016a36020802402005280200220028020020002802082206470d0020002006410110ab86808000200028020821060b200028020420066a41db003a00002000200641016a3602082004200536020c20044180043602082003200510ae8980800022000d03200441086a200341206a29030010d4868080002004280208220041ff01710d05024020004180fe0371450d000240200428020c280200220028020020002802082206470d0020002006410110ab86808000200028020821060b200028020420066a41dd003a00002000200641016a3602080b200341286a22032001470d000b0b02402005280200220528020020052802082200470d0020052000410110ab86808000200528020821000b200528020420006a41dd003a00002005200041016a3602080b410021000b200441106a24808080800020000f0b41f4d7c280004128418cdcc2800010f880808000000b41f4d7c28000412841bcdcc2800010f880808000000be80101047f024020002d00000d00200128020821032001280204210420002802042101024020002d00014101460d0002402001280200220528020020052802082206470d0020052006410110ab86808000200528020821060b200528020420066a412c3a00002005200641016a3602080b200041023a0001200120002004200310ae868080001a02402001280200220028020020002802082205470d0020002005410110ab86808000200028020821050b200028020420056a413a3a00002000200541016a3602082002200110af868080000f0b41f4d7c280004128418cdcc2800010f880808000000bca0501047f23808080800041106b2204248080808000024020002d00000d0020002802042105024020002d00014101460d0002402005280200220628020020062802082207470d0020062007410110ab86808000200628020821070b200628020420076a412c3a00002006200741016a3602080b200041023a0001200520042001200210ae868080001a200328020821022003280204210602402005280200220028020020002802082203470d0020002003410110ab86808000200028020821030b200028020420036a413a3a00002000200341016a36020802402005280200220028020020002802082203470d0020002003410110ab86808000200028020821030b200028020420036a41db003a00002000200341016a22033602080240024020020d00200041086a2106200041046a210520002802002003470d0120002003410110ab86808000200028020821030c010b200441046a200641002f0194a1c6800010c8878080002005200420042802082200200428020c10ae868080001a02402004280204450d00200041002802c0a3c68000118080808000000b024020024101460d00200641206a2106200241057441606a2102034002402005280200220028020020002802082203470d0020002003410110ab86808000200028020821030b200028020420036a412c3a00002000200341016a360208200441046a200641002f0194a1c6800010c8878080002005200420042802082200200428020c10ae868080001a02402004280204450d00200041002802c0a3c68000118080808000000b200641206a2106200241606a22020d000b0b02402005280200220028020020002802082203470d0020002003410110ab86808000200028020821030b200041086a2106200041046a21050b200528020020036a41dd003a00002006200341016a360200200441106a24808080800041000f0b41f4d7c280004128418cdcc2800010f880808000000bd70801047f23808080800041206b22042480808080000240024020002d00000d0020002802042105024020002d00014101460d0002402005280200220628020020062802082207470d0020062007410110ab86808000200628020821070b200628020420076a412c3a00002006200741016a3602080b200041023a0001200520042001200210ae868080001a200328020821012003280204210702402005280200220028020020002802082203470d0020002003410110ab86808000200028020821030b200028020420036a413a3a00002000200341016a36020802402005280200220028020020002802082203470d0020002003410110ab86808000200028020821030b200041086a2106200028020420036a41db003a00002000200341016a2203360208200041046a21020240024020010d0020002802002003470d0120002003410110ab86808000200028020821030c010b024020002802002003470d0020002003410110ab86808000200028020821030b200228020020036a41db003a00002006200341016a3602002004200536020c2004418004360208200441146a200741002f0194a1c6800010c8878080002005200420042802182200200428021c10ae868080001a02402004280214450d00200041002802c0a3c68000118080808000000b200441086a200729032010d4868080002004280208220041ff01710d02024020004180fe0371450d000240200428020c280200220028020020002802082203470d0020002003410110ab86808000200028020821030b200028020420036a41dd003a00002000200341016a3602080b024020014101460d002007200141286c6a2102200741286a2103034002402005280200220028020020002802082206470d0020002006410110ab86808000200028020821060b200028020420066a412c3a00002000200641016a36020802402005280200220028020020002802082206470d0020002006410110ab86808000200028020821060b200028020420066a41db003a00002000200641016a3602082004200536020c2004418004360208200441146a200341002f0194a1c6800010c8878080002005200420042802182200200428021c10ae868080001a02402004280214450d00200041002802c0a3c68000118080808000000b200441086a200341206a29030010d4868080002004280208220041ff01710d04024020004180fe0371450d000240200428020c280200220028020020002802082206470d0020002006410110ab86808000200028020821060b200028020420066a41dd003a00002000200641016a3602080b200341286a22032002470d000b0b02402005280200220528020020052802082203470d0020052003410110ab86808000200528020821030b200541086a2106200541046a21020b200228020020036a41dd003a00002006200341016a360200200441206a24808080800041000f0b41f4d7c280004128418cdcc2800010f880808000000b41f4d7c28000412841bcdcc2800010f880808000000bce0201037f024020002d00000d0020002802042104024020002d00014101460d0002402004280200220528020020052802082206470d0020052006410110ab86808000200528020821060b200528020420066a412c3a00002005200641016a3602080b200041023a0001200420002001200210ae868080001a02402004280200220028020020002802082205470d0020002005410110ab86808000200028020821050b200028020420056a413a3a00002000200541016a36020802402004280200220028020020002802082204470d0020002004410110ab86808000200028020821040b200028020420046a41fb003a00002000200441016a2204360208024020002802002004470d0020002004410110ab86808000200028020821040b200028020420046a41fd003a00002000200441016a36020841000f0b41f4d7c280004128418cdcc2800010f880808000000bda0101037f024020002d00000d0020002802042104024020002d00014101460d0002402004280200220528020020052802082206470d0020052006410110ab86808000200528020821060b200528020420066a412c3a00002005200641016a3602080b200041023a0001200420002001200210ae868080001a02402004280200220028020020002802082205470d0020002005410110ab86808000200028020821050b200028020420056a413a3a00002000200541016a3602082003200410b9878080000f0b41f4d7c280004128418cdcc2800010f880808000000bda0101037f024020002d00000d0020002802042104024020002d00014101460d0002402004280200220528020020052802082206470d0020052006410110ab86808000200528020821060b200528020420066a412c3a00002005200641016a3602080b200041023a0001200420002001200210ae868080001a02402004280200220028020020002802082205470d0020002005410110ab86808000200028020821050b200028020420056a413a3a00002000200541016a3602082003200410be888080000f0b41f4d7c280004128418cdcc2800010f880808000000b2100200128021441fedbc28000410c200141186a28020028020c118280808000000ba40a01097f200028020c2201200028020422026b41e8016e2103024020012002460d004100210403400240024002400240024002402002200441e8016c6a2201280200220541736a2206410220064104491b0e03010203000b20012d00084106470d042001410c6a280200450d04200141106a28020021070c030b0240024002400240024002400240024020012d0008417f6a0e0a010b0203040506070b0b000b2001410c6a280200450d0a200141106a28020021070c090b2001410c6a280200450d09200141106a28020021070c080b2001410c6a280200450d08200141106a28020021070c070b2001410c6a280200450d07200141106a28020021070c060b0240200141146a2802002205450d00200141106a2802002106034002402006280200450d00200641046a28020041002802c0a3c68000118080808000000b02402006410c6a280200450d00200641106a28020041002802c0a3c68000118080808000000b200641186a21062005417f6a22050d000b0b200128020c450d06200128021021070c050b0240200141146a2802002206450d00200141106a28020021072006410171210841002105024020064101460d002006417e7121094100210520072106034002402006280200450d00200641046a28020041002802c0a3c68000118080808000000b02402006410c6a280200450d00200641106a28020041002802c0a3c68000118080808000000b200641186a21062009200541026a2205470d000b0b2008450d0020072005410c6c6a2206280200450d00200628020441002802c0a3c68000118080808000000b200128020c450d05200128021021070c040b200141106a280200450d04200141146a28020021070c030b2001410c6a280200450d03200141106a28020021070c020b200141186a2d0000417d6a41ff017141014b0d020240200128020822074198016a2802002209450d004100210620074194016a2802002208210103402008200641146c6a21050240024002400240024020012d00000e0400010102040b200141086a21050c020b200541086a21050c010b200541046a21050b2005280200450d00200528020441002802c0a3c68000118080808000000b200641016a2106200141146a21012009417f6a22090d000b0b0240200728029001450d0020072802940141002802c0a3c68000118080808000000b024020074190026a2802002209450d00410021062007418c026a2802002208210103402008200641146c6a21050240024002400240024020012d00000e0400010102040b200141086a21050c020b200541086a21050c010b200541046a21050b2005280200450d00200528020441002802c0a3c68000118080808000000b200641016a2106200141146a21012009417f6a22090d000b0b200728028802450d01200728028c0241002802c0a3c68000118080808000000c010b0240024002400240024002402005417e6a0e06000102000003070b200141046a21010c030b02402001280210450d00200141146a28020041002802c0a3c68000118080808000000b20012802042206418080808078460d05200141046a21010c030b02402001280204450d00200141086a28020041002802c0a3c68000118080808000000b200141106a21010c010b0240024020012d00040e0400000001050b2001410c6a21010c010b200141086a21010b200128020021060b2006450d01200128020421070b200741002802c0a3c68000118080808000000b200441016a22042003470d000b0b02402000280208450d00200028020041002802c0a3c68000118080808000000b0bab0202017f017e23808080800041106b22042480808080002004200128020020022003109188808000024002400240024002402004280200450d00200441086a28020021010240200428020422030d002001ad422086210541808080807821010c020b0240024020010d00410121020c010b2001417f4c0d0341002d00fca3c680001a200141002802c8a3c68000118180808000002202450d040b2001ad42208620022003200110848e808000ad8421050c010b200420012802042002200310ec8780800020042802002201418180808078460d03200429020421050b2000200537020420002001360200200441106a2480808080000f0b10ae80808000000b4101200110b280808000000b41dcdcc2800041302004410f6a41ccdcc2800041f4ddc28000108981808000000b6f00200042a5e9e3ab9e929adc2c37030820004280808080c00037033820004280808080c000370350200041ef80808000360218200041023a0000200041106a4293888c8f89fdc6ec9e7f370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000be10101017f41002d00fca3c680001a0240413041002802c8a3c680001181808080000022010d004108413010b280808000000b200142e7b0a091f3ed9c85c500370318200142b891b68c98adebcf61370308200142e7b0a091f3ed9c85c500370300200141ea8180800036021020004280808080c00037033820004280808080c0003703502000410236020c2000200136020820004102360204200041043a0000200141206a42b891b68c98adebcf61370300200141286a41ea81808000360200200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6f00200042dbd791d5c2919eaecd0037030820004280808080c00037033820004280808080c000370350200041c880808000360218200041023a0000200041106a42e6ed8d82cc91adcb05370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000be10101017f41002d00fca3c680001a0240413041002802c8a3c680001181808080000022010d004108413010b280808000000b200142cbc4c4bdb7cedec904370318200142b891b68c98adebcf61370308200142e7b0a091f3ed9c85c500370300200141ea8180800036021020004280808080c00037033820004280808080c0003703502000410236020c2000200136020820004102360204200041043a0000200141206a42f3d1f7c4e49ca3ecb07f370300200141286a41eb81808000360200200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b7000200042b7a18ef9ac95b6cbeb0037030820004280808080c00037033820004280808080c000370350200041ec81808000360218200041023a0000200041106a42dda1fc828d83b3faab7f370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b70002000429ca0fdd992b0ed90df0037030820004280808080c00037033820004280808080c000370350200041ed81808000360218200041023a0000200041106a42bbf8f5b8b9e2c39ac400370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6e00200042d7c9cb8fc1cf97db3e37030820004280808080c00037033820004280808080c000370350200041b580808000360218200041023a0000200041106a42e88488d0c0e3aebc13370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6f002000428291fa9e999aa9d24637030820004280808080c00037033820004280808080c000370350200041ee81808000360218200041023a0000200041106a42bc90c1fdf28f8bb0ff00370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6e0020004280c6969198fdafd00537030820004280808080c00037033820004280808080c000370350200041ef81808000360218200041023a0000200041106a42eaacebad91f388e55c370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6f00200042c4ccab9c81ebb4b8db0037030820004280808080c00037033820004280808080c000370350200041f081808000360218200041023a0000200041106a42a4d2928ecac0faa432370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6e0020004285cbd9e891b2d0c16137030820004280808080c00037033820004280808080c000370350200041f181808000360218200041023a0000200041106a4282d2add5d4f1deea6a370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6f00200042d7c9c3bdedbdd399957f37030820004280808080c00037033820004280808080c000370350200041f281808000360218200041023a0000200041106a42a6f8d4ee83cba9e440370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6f00200042ee93e0d6a2949dcf3137030820004280808080c00037033820004280808080c000370350200041f381808000360218200041023a0000200041106a42c49beac9c5ace288fd00370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6e00200042e1e0d7feab89e9c84837030820004280808080c00037033820004280808080c000370350200041f481808000360218200041023a0000200041106a42d391b3b4c8e6f89148370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6f00200042c4ccab9c81ebb4b8db0037030820004280808080c00037033820004280808080c000370350200041f081808000360218200041023a0000200041106a42a4d2928ecac0faa432370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6f00200042c194a6a793ccc3a85737030820004280808080c00037033820004280808080c000370350200041f581808000360218200041023a0000200041106a42ab8bffbed784ffa5937f370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6f00200042ef8683cfe1dddaca6337030820004280808080c00037033820004280808080c000370350200041f681808000360218200041023a0000200041106a4284b2a2d692ae8580b57f370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b7000200042c9fea184df91c8afd40037030820004280808080c00037033820004280808080c000370350200041f781808000360218200041023a0000200041106a42e2a4dca09a8089e2927f370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6e00200042e5f0b3f4e8a9b1b12a37030820004280808080c00037033820004280808080c000370350200041f881808000360218200041023a0000200041106a4281ebc5ecd497b09a0a370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6e00200042a9c4d590f68dedef5837030820004280808080c00037033820004280808080c000370350200041f981808000360218200041023a0000200041106a42dbbc81a3c9e8f4e22a370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b7000200042acf6debeefe0d9c8d30037030820004280808080c00037033820004280808080c000370350200041bd80808000360218200041023a0000200041106a42efc9c9edb5e7b3a6c700370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000b6f00200042e7b0a091f3ed9c85c50037030820004280808080c00037033820004280808080c000370350200041ea81808000360218200041023a0000200041106a42b891b68c98adebcf61370300200041c8006a4208370300200041c0006a4200370300200041d8006a41003602000be10301077f23808080800041106b2203248080808000200041286a2104024002400240200028022822054128200541284b22061b22072000280204200520061b22066b200220016b22084f0d00200620086a22052006490d014100417f2005417f6a677620054102491b41016a2205450d01200341086a2000200510f786808000024020032802082205418180808078460d002005450d022005200328020c10b280808000000b200428020022054128200541284b1b21070b200041046a22092004200541284b22061b21080240024020004104412820061b6a280200220520074f0d002000280200200020061b2106034020012002460d02200620056a20012d00003a0000200141016a21012007200541016a2205470d000b200721050b2008200536020020012002460d02034020012d00002108024002402000410441282000280228220741284b22051b6a28020022062007412820051b460d002009200420051b21072000280200200020051b21050c010b200010f8868080002000280204210620002802002105200921070b200520066a20083a00002007200728020041016a360200200141016a22012002470d000c030b0b200820053602000c010b4198dfc28000411141d8e2c2800010f880808000000b200341106a2480808080000bc70301067f23808080800041106b220324808080800002400240024002400240200128020420012802282204200441284b22051b220620024b0d002004412820051b21072001280200200120051b2108024020024129490d00418180808078210520072002460d040240200241004e0d00410021050c060b02400240024020044129490d002007417f4a0d0120072102410021050c080b41002d00fca3c680001a200241002802c8a3c680001181808080000022040d01410121050c070b0240200241002802c8a3c680001181808080000022040d00410121050c070b200420082007200220072002491b10848e8080001a200841002802c0a3c68000118080808000000c040b20042008200610848e8080001a0c030b418180808078210520044129490d0320012008200610848e80800020063602282007417f4c0d01200841002802c0a3c68000118080808000000c030b41f8e2c2800041204198e3c2800010f880808000000b2003200736020c20034100360208418ce2c28000412b200341086a41b8e2c2800041c8e2c28000108981808000000b2001200236022820012006360204200120043602000b0b2000200236020420002005360200200341106a2480808080000bb90101027f23808080800041106b220124808080800002400240200028020420002802282202200241284b1b41016a2202450d004100417f2002417f6a677620024102491b41016a2202450d00200141086a2000200210f786808000024020012802082200418180808078460d002000450d022000200128020c10b280808000000b200141106a2480808080000f0b4198dfc28000411141e8e2c2800010a181808000000b4198dfc28000411141d8e2c2800010f880808000000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b220641386e2207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710a78680800020022802082109200228020c21080b2009200841386c6a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b41386e2107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b22064105762207200128020822014101764f0d01410021082002410036020c20024280808080800137020441082109024020052004460d00200241046a4100200710ac8680800020022802082109200228020c21080b200920084105746a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b4105762107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bab0201087f23808080800041106b220224808080800002400240024002402001280200220320012802042204460d00200128020c220520046b220641246e2207200128020822014101764f0d01410021082002410036020c20024280808080c00037020441042109024020052004460d00200241046a4100200710a98680800020022802082109200228020c21080b2009200841246c6a2004200610848e8080001a2002200820076a36020c02402001450d00200341002802c0a3c68000118080808000000b20002002290204370200200041086a200241046a41086a2802003602000c030b200128020c20036b41246e2107200128020821010c010b20032004200610fe8d8080001a0b2000200736020820002003360204200020013602000b200241106a2480808080000bdc03010d7f23808080800041106b2202248080808000024002400240024002402001280200220328020422044108490d002003280200220541076a2d00002106200541066a2d00002107200541056a2d00002108200541036a2d0000210920052d0004210a20052d0002210b20052d0001210c20052d0000210d2003200541086a3602002003200441786a360204200241086a200110bc8a808000024020022802080d002001280200220141046a2802002203200228020c2205490d000240024020050d00410121040c010b2005417f4c0d05200541002802c8a3c68000118180808000002204450d06200441002005108a8e8080001a200141046a28020021030b200320054f0d02200441002802c0a3c68000118080808000000b20004180808080783602080c020b20004180808080783602080c010b20042001280200220e200510848e80800021042000200b3a00022000200c3a00012000200d3a0000200141046a200320056b3602002001200e20056a36020020002005360208200041036a20093a000020002005ad4220862004ad8437020c2000200a3a0004200041056a20083a0000200041066a20073a0000200041076a20063a00000b200241106a2480808080000f0b10ae80808000000b4101200510b280808000000b980401077f23808080800041106b2202248080808000200241086a200110bc8a808000024002400240024002400240024020022802080d002001280200220341046a2802002204200228020c2205490d000240024020050d00410121060c010b2005417f4c0d03200541002802c8a3c68000118180808000002206450d04200641002005108a8e8080001a200341046a28020021040b200420054f0d01200641002802c0a3c68000118080808000000b20004180808080783602000c050b200620032802002207200510848e8080002106200341046a200420056b3602002003200720056a3602002002200110bc8a808000024020022802000d002001280200220341046a280200220420022802042201490d000240024020010d00410121070c010b2001417f4c0d02200141002802c8a3c68000118180808000002207450d04200741002001108a8e8080001a200341046a28020021040b200420014f0d04200741002802c0a3c68000118080808000000b20004180808080783602002005450d04200641002802c0a3c68000118080808000000c040b10ae80808000000b4101200510b280808000000b4101200110b280808000000b200720032802002208200110848e8080002107200341046a200420016b3602002003200820016a3602002000200136020c20002005360208200020063602042000200536020020002001ad4220862007ad843702100b200241106a2480808080000b840201047f23808080800041106b2202248080808000200028020421032002200028020822043602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822056b20044f0d0020012005200410b182808000200128020821050b200128020420056a2003200410848e8080001a2001200520046a360208200041106a28020021052002200041146a28020022003602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822046b20004f0d0020012004200010b182808000200128020821040b200128020420046a2005200010848e8080001a2001200420006a360208200241106a2480808080000b02000bb70202067f017e23808080800041106b2202248080808000200028020421032002200028020822003602082002200241086a36020c2002410c6a200110c08a80800002402000450d002003200041286c6a210420012802082100034002402001280200220520006b411f4b0d0020012000412010b18280800020012802002105200128020821000b2001280204220620006a22072003290000370000200741186a200341186a290000370000200741106a200341106a290000370000200741086a200341086a2900003700002001200041206a2207360208200341206a29030021080240200520076b41074b0d0020012007410810b18280800020012802042106200128020821070b2001200741086a2200360208200620076a2008370000200341286a22032004470d000b0b200241106a2480808080000b850202067f017e23808080800041106b2202248080808000200028020421032002200028020822003602082002200241086a36020c2002410c6a200110c08a80800002402000450d00200320004104746a210420012802082100034002402001280200220520006b41074b0d0020012000410810b18280800020012802002105200128020821000b2001200041086a22063602082001280204220720006a2003290300370000200341086a29030021080240200520066b41074b0d0020012006410810b18280800020012802042107200128020821060b2001200641086a2200360208200720066a2008370000200341106a22032004470d000b0b200241106a2480808080000b880203037f017e047f23808080800041106b2202248080808000200028020421032002200028020822003602082002200241086a36020c2002410c6a200110c08a80800002402000450d00200320004104746a21042001280208210003402003290300210502402001280200220620006b41074b0d0020012000410810b18280800020012802002106200128020821000b2001200041086a22073602082001280204220820006a2005370000200328020821090240200620076b41034b0d0020012007410410b18280800020012802042108200128020821070b2001200741046a2200360208200820076a2009360000200341106a22032004470d000b0b200241106a2480808080000b4901017f02402000280200200028020822036b20024f0d0020002003200210ab86808000200028020821030b200028020420036a2001200210848e8080001a2000200320026a3602080bc90103057f027e017f20012802082102200128020022032104024020012802042205200128020c2206460d0020032104034002402005290300220742efb7e9de94adbae42685200541086a290300220842c785eb8b8b8eddc0618584500d0020052802102109200420083703082004200737030020042009360210200441186a21040b200541186a22052006470d000b0b200142888080808001370200200142808080808001370208200020033602042000200420036b41186e3602082000200241186c41186e3602000bc70201067f23808080800041106b220524808080800020032802042106200328020021070240200128020022012802082203200320026a22084f0d00200321080240200128020020036b20024f0d0020012003200210ab86808000200128020821080b2001280204220920086a210a024020024102490d00200a41002002417f6a2202108a8e8080001a2009200820026a22086a210a0b200a41003a0000200841016a21080b20012008360208024020082003490d00200541086a20072006200128020420036a200820036b200410bc80808000200528020c21080240024020052802082202418380c400470d0002402001280208200820036a2203490d00200120033602080b2000418380c400360200200020083602040c010b20002008360204200020023602000b200541106a2480808080000f0b200320084190e1c28000109481808000000bdb0101047f23808080800041106b22022480808080002000280208210320002802042100410121042001280214418fa5c080004101200141186a28020028020c118280808000002105200241003a0009200220053a00082002200136020402402003450d0003402002200036020c200241046a2002410c6a41d0cfc48000108e818080001a200041016a21002003417f6a22030d000b20022d000821050b0240200541ff01710d00200228020422002802144190a5c080004101200041186a28020028020c1182808080000021040b200241106a24808080800020040ba90201027f23808080800041106b22022480808080000240024020002802000d00200128021441a9dfc280004110200141186a28020028020c1182808080000021010c010b20022000360204200128021441b9dfc280004108200141186a28020028020c118280808000002100200241003a000d200220003a000c20022001360208200241086a41c1dfc280004106200241046a41c8dfc28000108c81808000210320022d000c2100024020022d000d0d00200041ff017141004721010c010b41012101200041ff01710d000240200328020022012d001c4104710d0020012802144187a5c080004102200128021828020c1182808080000021010c010b20012802144186a5c080004101200128021828020c1182808080000021010b200241106a24808080800020010bcc0501087f23808080800041206b22032480808080002003200136021c2003410036021820032002360214200341086a200341146a10bc8a808000024002400240024002400240024020032802080d002003280214220441046a2802002205200328020c2201490d000240024020010d00410121060c010b2001417f4c0d03200141002802c8a3c68000118180808000002206450d04200641002001108a8e8080001a200441046a28020021050b024020052001490d00200620042802002207200110848e8080002108200441046a200520016b3602002004200720016a3602002003200341146a10bc8a8080000240024020032802000d002003280214220541046a280200220720032802042204490d000240024020040d00410121090c010b2004417f4c0d06200441002802c8a3c68000118180808000002209450d08200941002004108a8e8080001a200541046a28020021070b200720044f0d01200941002802c0a3c68000118080808000000b20010d010c020b20092005280200220a200410848e8080002109200541046a200720046b3602002005200a20046a3602000240200328021422052802042207450d0020052007417f6a36020420052005280200220741016a3602004101410220072d000022054101461b410020051b22054102470d030b02402004450d00200941002802c0a3c68000118080808000000b2001450d010b200641002802c0a3c68000118080808000000b20004180808080783602000c050b2002280204450d03200041808080807836020002402001450d00200841002802c0a3c68000118080808000000b2004450d04200941002802c0a3c68000118080808000000c040b10ae80808000000b4101200110b280808000000b4101200410b280808000000b200020053a001820002004360214200020093602102000200436020c2000200136020820002008360204200020013602000b200341206a2480808080000bb20501087f23808080800041206b22032480808080002003200136021c2003410036021820032002360214200341086a200341146a10bc8a8080000240024020032802080d002003280214220441046a2802002205200328020c2201490d00024002400240024002400240024020010d00410121060c010b2001417f4c0d01200141002802c8a3c68000118180808000002206450d02200641002001108a8e8080001a200441046a28020021050b20052001490d04200620042802002207200110848e8080002108200441046a200520016b3602002004200720016a3602002003200341146a10bc8a8080000240024020032802000d002003280214220541046a280200220720032802042204490d000240024020040d00410121090c010b2004417f4c0d03200441002802c8a3c68000118180808000002209450d05200941002004108a8e8080001a200541046a28020021070b200720044f0d01200941002802c0a3c68000118080808000000b20010d050c060b20092005280200220a200410848e8080002109200541046a200720046b3602002005200a20046a3602002003280214220528020422074104490d0320052007417c6a36020420052005280200220641046a360200024020022802040d002000200628000036021820002004360214200020093602102000200436020c2000200136020820002008360204200020013602000c070b200041808080807836020002402001450d00200841002802c0a3c68000118080808000000b2004450d06200941002802c0a3c68000118080808000000c060b10ae80808000000b4101200110b280808000000b4101200410b280808000000b02402004450d00200941002802c0a3c68000118080808000000b2001450d010b200641002802c0a3c68000118080808000000b20004180808080783602000b200341206a2480808080000baf0201087f02400240024002400240200128020822020d00410421030c010b200241aad5aad5004b0d022002410c6c2204417f4c0d022001280204210541002d00fca3c680001a200441002802c8a3c68000118180808000002203450d014100210620022107034020042006460d012005280204210802400240200528020822010d00410121090c010b2001417f4c0d0441002d00fca3c680001a200141002802c8a3c68000118180808000002209450d050b2005410c6a210520092008200110848e8080002109200320066a22082001360200200841086a2001360200200841046a20093602002006410c6a21062007417f6a22070d000b0b2000200236020820002003360204200020023602000f0b4104200410b280808000000b10ae80808000000b4101200110b280808000000b840101027f200128020421020240024002400240200128020822010d00410121030c010b2001417f4c0d0141002d00fca3c680001a200141002802c8a3c68000118180808000002203450d020b20032002200110848e80800021022000200136020820002002360204200020013602000f0b10ae80808000000b4101200110b280808000000baa03010a7f024002400240024002400240200128020822020d00410421030c010b200241d5aad52a4b0d03200241186c2204417f4c0d032001280204210141002d00fca3c680001a200441002802c8a3c68000118180808000002203450d014100210520022106034020042005460d0120012802042107410121084101210902402001280208220a450d00200a417f4c0d0541002d00fca3c680001a200a41002802c8a3c68000118180808000002209450d040b20092007200a10848e808000210b200141106a28020021090240200141146a2802002207450d002007417f4c0d0541002d00fca3c680001a200741002802c8a3c68000118180808000002208450d060b200141186a210120082009200710848e8080002108200320056a2209200a360200200941146a2007360200200941106a20083602002009410c6a2007360200200941086a200a360200200941046a200b360200200541186a21052006417f6a22060d000b0b2000200236020820002003360204200020023602000f0b4104200410b280808000000b4101200a10b280808000000b10ae80808000000b4101200710b280808000000bfe06010f7f02400240024002400240024002400240200128020822020d00410421030c010b200241e6cc99334b0d05200241146c2204417f4c0d052001280204210541002d00fca3c680001a200441002802c8a3c68000118180808000002203450d014100210620022107034020042006460d014104210802400240024002400240200520066a22012d00000e050001020304000b200141046a2d00002109200141036a2d0000210a200141026a2d0000210b4101210c200141016a2d0000210d2001410c6a28020021080240200141106a280200220e450d00200e417f4c0d0b41002d00fca3c680001a200e41002802c8a3c6800011818080800000220c450d080b200c2008200e10848e8080001a41002108200e210f0c030b200141046a2d00002109200141036a2d0000210a200141026a2d0000210b41012108200141016a2d0000210d2001410c6a280200210f4101210c0240200141106a280200220e450d00200e417f4c0d0a41002d00fca3c680001a200e41002802c8a3c6800011818080800000220c450d080b200c200f200e10848e8080001a200e210f0c020b200141046a2d00002109200141036a2d0000210a200141026a2d0000210b4101210c200141016a2d0000210d2001410c6a28020021080240200141106a280200220e450d00200e417f4c0d0941002d00fca3c680001a200e41002802c8a3c6800011818080800000220c450d080b200c2008200e10848e8080001a41022108200e210f0c010b2001410f6a2d000021082001410d6a2f000021102001410c6a220e2d00002109200141086a280200210102400240200e280200220c0d004101210e0c010b200c417f4c0d0841002d00fca3c680001a200c41002802c8a3c6800011818080800000220e450d090b20102008411074722110200e2001200c10848e8080001a410321080b200320066a220120083a0000200141076a20104110763a0000200141056a20103b0000200141036a200a3a0000200141026a200b3a0000200141016a200d3a0000200141106a200f3602002001410c6a200c360200200141086a200e360200200141046a20093a0000200641146a21062007417f6a22070d000b0b2000200236020820002003360204200020023602000f0b4104200410b280808000000b4101200e10b280808000000b4101200e10b280808000000b4101200e10b280808000000b10ae80808000000b4101200c10b280808000000bee0601047f024002402000280200220141054b0d002001450d0102400240024002402000410c6a280200417e6a2202410220024102491b0e020103000b200041146a21020c010b200041106a21020b2002280200450d00200228020441002802c0a3c68000118080808000000b0240200041246a280200450d00200041286a28020041002802c0a3c68000118080808000000b20014101460d010240024002400240200041386a280200417e6a2202410220024102491b0e020003010b2000413c6a21020c010b200041c0006a21020b2002280200450d00200228020441002802c0a3c68000118080808000000b0240200041d0006a280200450d00200041d4006a28020041002802c0a3c68000118080808000000b20014102460d010240024002400240200041e4006a280200417e6a2202410220024102491b0e020003010b200041e8006a21020c010b200041ec006a21020b2002280200450d00200228020441002802c0a3c68000118080808000000b0240200041fc006a280200450d0020004180016a28020041002802c0a3c68000118080808000000b20014103460d01024002400240024020004190016a280200417e6a2202410220024102491b0e020003010b20004194016a21020c010b20004198016a21020b2002280200450d00200228020441002802c0a3c68000118080808000000b0240200041a8016a280200450d00200041ac016a28020041002802c0a3c68000118080808000000b20014104460d010240024002400240200041bc016a280200417e6a2201410220014102491b0e020003010b200041c0016a21010c010b200041c4016a21010b2001280200450d00200128020441002802c0a3c68000118080808000000b200041d4016a280200450d01200041d8016a28020041002802c0a3c68000118080808000000f0b200028020421030240200041086a2802002202450d00200341246a21004100210103400240024002400240200041646a280200417e6a2204410220044102491b0e020103000b20032001412c6c6a41106a21040c010b200041686a21040b2004280200450d00200428020441002802c0a3c68000118080808000000b02402000417c6a280200450d00200028020041002802c0a3c68000118080808000000b200141016a21012000412c6a21002002417f6a22020d000b0b200341002802c0a3c68000118080808000000b0be70901087f024020002802082201450d00200028020421024100210303400240024002400240024002402002200341e8016c6a2200280200220441736a2205410220054104491b0e03010203000b20002d00084106470d042000410c6a280200450d04200041106a28020021060c030b0240024002400240024002400240024020002d0008417f6a0e0a010b0203040506070b0b000b2000410c6a280200450d0a200041106a28020021060c090b2000410c6a280200450d09200041106a28020021060c080b2000410c6a280200450d08200041106a28020021060c070b2000410c6a280200450d07200041106a28020021060c060b0240200041146a2802002204450d00200041106a2802002105034002402005280200450d00200541046a28020041002802c0a3c68000118080808000000b02402005410c6a280200450d00200541106a28020041002802c0a3c68000118080808000000b200541186a21052004417f6a22040d000b0b200028020c450d06200028021021060c050b0240200041146a2802002205450d00200041106a28020021062005410171210741002104024020054101460d002005417e7121084100210420062105034002402005280200450d00200541046a28020041002802c0a3c68000118080808000000b02402005410c6a280200450d00200541106a28020041002802c0a3c68000118080808000000b200541186a21052008200441026a2204470d000b0b2007450d0020062004410c6c6a2205280200450d00200528020441002802c0a3c68000118080808000000b200028020c450d05200028021021060c040b200041106a280200450d04200041146a28020021060c030b2000410c6a280200450d03200041106a28020021060c020b200041186a2d0000417d6a41ff017141014b0d020240200028020822064198016a2802002208450d004100210520064194016a2802002207210003402007200541146c6a2104024002400240024020002d00000e0400000001030b200441086a21040c010b200041046a21040b2004280200450d00200428020441002802c0a3c68000118080808000000b200541016a2105200041146a21002008417f6a22080d000b0b024020064190016a280200450d0020062802940141002802c0a3c68000118080808000000b024020064190026a2802002208450d00410021052006418c026a2802002207210003402007200541146c6a2104024002400240024020002d00000e0400000001030b200441086a21040c010b200041046a21040b2004280200450d00200428020441002802c0a3c68000118080808000000b200541016a2105200041146a21002008417f6a22080d000b0b20064188026a280200450d01200628028c0241002802c0a3c68000118080808000000c010b0240024002400240024002402004417e6a0e06000102000003070b200041046a21000c030b02402000280210450d00200041146a28020041002802c0a3c68000118080808000000b20002802042205418080808078460d05200041046a21000c030b02402000280204450d00200041086a28020041002802c0a3c68000118080808000000b200041106a21000c010b0240024020002d00040e0400000001050b2000410c6a21000c010b200041086a21000b200028020021050b2005450d01200028020421060b200641002802c0a3c68000118080808000000b200341016a22032001470d000b0b0bb70401047f024020002802082201450d0020002802042100034002400240024002400240200041046a200020002d00004108461b22022d00002203417c6a41ff01712204410420044104491b0e0404010203000b0240200241dc006a2802004129490d00200241346a28020041002802c0a3c68000118080808000000b200228022c41002802c0a3c680001180808080000020034103460d030240024020030e020105000b2002280224220420042802002204417f6a36020020044101470d04200241246a10e28a8080000c040b2002280204220420042802002204417f6a36020020044101470d03200241046a10e28a8080000c030b0240200241dc006a2802004129490d00200241346a28020041002802c0a3c68000118080808000000b02400240024020022d00040e020105000b200241286a2202280200220420042802002204417f6a36020020044101460d010c040b200241086a2202280200220420042802002204417f6a36020020044101470d030b200210e28a8080000c020b200241d4006a2802004129490d012002412c6a28020041002802c0a3c68000118080808000000c010b200228023041002802c0a3c680001180808080000020022d000422044103460d0002400240024020040e020103000b200241286a2202280200220420042802002204417f6a36020020044101460d010c020b200241086a2202280200220420042802002204417f6a36020020044101470d010b200210e28a8080000b20004180016a21002001417f6a22010d000b0b0bd80201047f23808080800041306b2201248080808000024020002802082202450d002000280204210003400240024002400240024020002d00000e050404010203000b02400240200041046a28020022030d0041002103410021040c010b200120033602242001410036022020012003360214200141003602102001200041086a2802002203360228200120033602182000410c6a2802002104410121030b2001200436022c2001200336021c2001200336020c2001410c6a10aa8d8080000c030b200041046a280200450d02200041086a28020041002802c0a3c68000118080808000000c020b200041046a280200450d01200041086a28020041002802c0a3c68000118080808000000c010b200041046a22031091878080002003280200450d00200041086a28020041002802c0a3c68000118080808000000b200041106a21002002417f6a22020d000b0b200141306a2480808080000b980201057f23808080800041106b2205248080808000024020012802082206200620026a22074f0d00200621070240200128020020066b20024f0d0020012006200210ab86808000200128020821070b2001280204220820076a2109024020024102490d00200941002002417f6a2202108a8e8080001a2008200720026a22076a21090b200941003a0000200741016a21070b20012007360208024020072006490d00200541086a20032802002003280204200128020420066a200720066b200410a888808000200528020c21020240200528020822030d002007200220066a2206490d00200120063602080b2000200336020020002002360204200541106a2480808080000f0b2006200741fce1c28000109481808000000b5601017f23808080800041106b2202248080808000200241086a200110bd8a8080000240024020022802080d0020002001200228020c10ce858080000c010b20004180808080783602000b200241106a2480808080000b5601017f23808080800041106b2202248080808000200241086a200110bc8a8080000240024020022802080d0020002001200228020c10c7858080000c010b20004180808080783602000b200241106a2480808080000bbe0101077f41002103024002402001410f712204410f470d00410121032002280208220541016a200228020422064b0d0020022802002107410f2108200521010340200120064f0d022002200141016a3602080240200720016a2d0000220941ff01470d00200841ff016a210841012103200141026a2109200141016a2101200920064d0d010c020b0b200920086a2104410021030b20002004360204200020033602000f0b20052006200520064b1b200641ecc1c2800010f980808000000b2c01017f2000200128020420012802282203200341284b22031b36020420002001280200200120031b3602000b810701097f23808080800041106b22022480808080000240024002400240024002402001280208220341016a2204200128020422054b22060d000240200320054f0d0020012004360208024002400240024002400240024002400240024002402001280200220720036a2d00002208450d0020084106764102730e0403020a01030b200041003a00000c100b2008413f712208413f470d030240200341026a20054b0d00413f2108200421030340200320054f0d0d2001200341016a2209360208200720036a2d0000220a41ff01470d04200841ff016a2108200341026a210a20092103200a20054d0d000b0b200041053a00000c0f0b2008413f712208413f470d040240200341026a20054b0d00413f2108200421030340200320054f0d0d2001200341016a2209360208200720036a2d0000220a41ff01470d05200841ff016a2108200341026a210a20092103200a20054d0d000b0b200041053a00000c0e0b2008413f712208413f470d050240200341026a20054b0d00413f2108200421030340200320054f0d0d2001200341016a2209360208200720036a2d0000220a41ff01470d06200841ff016a2108200341026a210a20092103200a20054d0d000b0b200041053a00000c0d0b200a20086a21080b20002008360204200041023a00000c0b0b200a20086a21080b2000200836020420004181023b01000c090b200a20086a21080b20002008360204200041013b01000c070b0240024002400240200841e001714120460d00200841f001714110460d01200041053a00000c0a0b2008411f712208411f470d020240200341026a20054b0d00411f2108200421030340200320054f0d0a2001200341016a2209360208200720036a2d0000220a41ff01470d03200841ff016a2108200341026a210a20092103200a20054d0d000b0b200041053a00000c090b200241086a20082001109587808000024020022802080d00200228020c2101200041033a0000200020013602040c090b200041053a00000c080b200a20086a21080b20002008360204200041043a00000c060b2003200541ecc1c2800010f980808000000b200041053a00000c040b2004200520061b200541ecc1c2800010f980808000000b2004200520061b200541ecc1c2800010f980808000000b2004200520061b200541ecc1c2800010f980808000000b2004200520061b200541ecc1c2800010f980808000000b200241106a2480808080000bf70701047f02400240024002400240024020002d00000e050004010203000b0240200128020020012802082202470d0020012002410110b182808000200128020821020b2001200241016a360208200128020420026a41003a00000c040b41ff0020002802042202413e2002413e491b220041c000722002413e4b1b2103200220006b2102410121040340200441ff0171210041002104200321050240024020000e03000100010b2002450d05024002402002418002490d00200241817e6a210241ff0121050c010b2002417f6a2105410021020b410221040b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a20053a00000c000b0b411f20002802042202410e2002410e491b22004110722002410e4b1b2103200220006b2102410121040340200441ff0171210041002104200321050240024020000e03000100010b2002450d04024002402002418002490d00200241817e6a210241ff0121050c010b2002417f6a2105410021020b410221040b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a20053a00000c000b0b413f20002802042202411e2002411e491b22004120722002411e4b1b2103200220006b2102410121040340200441ff0171210041002104200321050240024020000e03000100010b2002450d03024002402002418002490d00200241817e6a210241ff0121050c010b2002417f6a2105410021020b410221040b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a20053a00000c000b0b200028020422042004413e2004413e491b22056b2102024020002d0001450d00417f20054140722004413e4b1b2103410121040340200441ff0171210041002104200321050240024020000e03000100010b2002450d03024002402002418002490d00200241817e6a210241ff0121050c010b2002417f6a2105410021020b410221040b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a20053a00000c000b0b41bf7f200541807f722004413e4b1b2103410121040340200441ff0171210041002104200321050240024020000e03000100010b2002450d02024002402002418002490d00200241817e6a210241ff0121050c010b2002417f6a2105410021020b410221040b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a20053a00000c000b0b0ba707010d7f200141186a28020021022001410d6a2d00002103200141096a2d00002104200141146a28020021052001280210210620012d000c21072001280204210820012d000821092001280200210a0340024002400240024002400240024002400240024002400240024002400240024002400240200a417e6a0e020102000b0240200941ff0171220b4102460d002001200b454101743a000841002109200b450d002004210c0c030b0240200a450d002008450d0002402008418002490d002001200841817e6a220836020441ff01210c410221090c040b200141003602042008417f6a210c41022109410021080c030b41022109200141023602000b200741ff0171210a410221070240200a4102460d0041002107200141003a000c200a0d030b200141033602000b2006450d072005450d07200120052005200220052002491b220a6b220536021420012006200a6a220b360210024002400240200a0e020001020b4100410041f4d3c4800010f980808000000b410141014184d4c4800010f980808000000b20062d000041047420062d000172210c4103210a200b21060b2000280208220b2000280200470d0d200a4103470d024100210d4103210a2006450d0c2005450d0c2002450d01200520026e220d2005200d20026c6b4100476a210d0c0c0b410021072000280208220b2000280200460d024102210a2003210c0c0c0b41b0e4c280004119419ce4c2800010f880808000000b2006450d06200a4102470d024102210a200741ff01714102470d014100210d410221070c050b2003210c2006450d060b200741ff0171410047210d4102210a0c030b200741ff0171220d4102460d012009410171200d4100476a210d0c020b0f0b410221074100210d0240200941ff0171220e4102470d00410221090c010b200e410047210d0b4100210e024002402005450d002002450d01200520026e220e2005200e20026c6b4100476a210e0b417f200d200e6a220e200e200d491b210d0c040b41b0e4c280004119419ce4c2800010f880808000000b200a4102470d014102210a410021064100210d200741ff01714102460d020b41002106200741ff0171410047210d4102210a0c010b0240200741ff0171220d4102460d00410021062009410171200d4100476a210d0c010b410021060240200941ff0171220d4102470d00410221094100210d0c010b41002106200d410047210d0b2000200b200d41016a220d417f200d1b10ab868080000b2000200b41016a3602082000280204200b6a200c3a00000c000b0bff0101077f20012802002102200128020421032001280208210402400240024020012d000c450d002001410d6a2d00002105200141003a000c2005410f7121060c010b200420034f0d012001200441016a2205360208200220046a2d00002106200521040b0240200028020822072000280200470d0020002007410110ab868080000b2000200741016a22053602082000280204220820076a20063a0000200420034f0d0003402001200441016a2207360208200220046a2d00002104024020052000280200470d0020002005410110ab86808000200028020421080b200820056a20043a00002000200541016a22053602082007210420032007470d000b0b0bca04020c7f027e23808080800041206b2202248080808000200241146a200110818a808000024002400240024002402002280214418080808078470d002000410036020820004280808080c0003702000c010b41002d00fca3c680001a413041002802c8a3c68000118180808000002203450d0120032002290214370200200341086a200241146a41086a280200360200200241013602102002200336020c20024104360208024020012802082204200128020422054d0d00200128020c210620012802002107200420056b2108411021054100210903402002200710bc8a8080000240024020022802000d002007280200220a41046a220b280200220420022802042201490d000240024020010d004101210c0c010b2001417f4c0d07200141002802c8a3c6800011818080800000220c450d08200c41002001108a8e8080001a200b28020021040b200420014f0d01200c41002802c0a3c68000118080808000000b200641013a00000c020b200c200a280200220d200110848e808000210c200b200420016b360200200a200d20016a3602002001ad210e200cad210f0240200941016a22042002280208470d00200241086a2004410110ad86808000200228020c21030b200320056a220a200e422086200f84370200200a417c6a20013602002002200941026a3602102005410c6a21052004210920082004470d000b0b20002002290208370200200041086a200241086a41086a2802003602000b200241206a2480808080000f0b4104413010b280808000000b10ae80808000000b4101200110b280808000000bae09010c7f2380808080004190016b2202248080808000200241186a200110e18c8080000240024002400240024020022802182203418080808078460d002002280228210420022802242105200228021c210602402001412c6a2802002207200141286a28020022084d0d0002402003450d00200641002802c0a3c68000118080808000000b2005450d01200441002802c0a3c68000118080808000000c010b200228022c21092002280220210a20012008417f6a36022820012001280224220841046a3602242002200936022c20022004360228200220053602242002200a3602202002200636021c200220073602342002200836023020022003360218200241f0006a2001413c6a200241186a10de8a8080002002280278418080808078470d010b2000410036020820004280808080c000370200200110ab8d8080000c010b20012802202203410020012802282205200128022c22046b41016a20052004491b220520032005491b41016a2203417f20031b22034104200341044b1b220341e6cc99334b0d01200341146c2205417f4c0d0141002d00fca3c680001a200541002802c8a3c6800011818080800000220b450d02200b2002290270370200200b41106a200241f0006a41106a280200360200200b41086a200241f0006a41086a290200370200200241013602142002200b3602102002200336020c200241186a41386a200141386a290200370300200241186a41306a200141306a290200370300200241186a41286a200141286a290200370300200241186a41206a200141206a290200370300200241186a41186a200141186a290200370300200241186a41106a200141106a290200370300200241186a41086a200141086a29020037030020022001290200370318200241f0006a200241186a10e18c808000024020022802702205418080808078460d00200241d4006a210c411421034101210103402002280280012106200228027c210420022802742107024020022802442208200228024022094d0d0002402005450d00200741002802c0a3c68000118080808000000b2004450d02200641002802c0a3c68000118080808000000c020b200228028401210a2002280278210d20022009417f6a3602402002200228023c220941046a36023c2002200a3602840120022006360280012002200436027c2002200d360278200220073602742002200836028c01200220093602880120022005360270200241dc006a200c200241f0006a10de8a8080002002280264418080808078460d0102402001200228020c470d002002410c6a200120022802382205410020022802402204200228024422066b41016a20042006491b220420052004491b41016a2205417f20051b10a8868080002002280210210b0b200b20036a2205200229025c370200200541106a200241dc006a41106a280200360200200541086a200241dc006a41086a2902003702002002200141016a2201360214200341146a2103200241f0006a200241186a10e18c80800020022802702205418080808078470d000b0b200241186a10ab8d808000200041086a2002410c6a41086a2802003602002000200229020c3702000b20024190016a2480808080000f0b10ae80808000000b4104200510b280808000000b8d0e010f7f23808080800041206b2202248080808000024002400240024002400240024020012802004102460d00200241086a200110e08c808000024020022802082203450d00200228020c220441086a28020020042802002205200541054b22051b2206450d03200328020821072003280204210802402006412c6c2004280204200441046a20051b6a41546a2204280208220341014b0d0020042802042106200428021c2105024020042802002209450d0020052006460d010b2002200441106a36021c2002411c6a200920062005108e858080002004200536020420044101360200200428020821030b410021050240024002402003417e6a2203410220034102491b0e03000501000b2004410c6a21040c010b200441106a21040b20042802082106200428020421050c020b200141023602000b20012802242204450d022004200141286a280200460d022001200441186a3602244100200441106a280200200428020c418080808078461b2105200441146a280200210620042802082107200428020421080b20012802242103024020012802004102470d0020030d03410021040c040b200128022021042003450d03417f2004200141286a28020020036b41186e6a220320032004491b21040c030b41fcb8c2800041fc0041f8bac2800010a181808000000b2000410036020820004280808080c0003702000c020b200141286a28020020036b41186e21040b0240200441016a2204417f20041b22044104200441044b1b220341ffffff3f4b0d0020034104742209417f4c0d0041002d00fca3c680001a0240200941002802c8a3c68000118180808000002204450d002004200636020c2004200536020820042007360204200420083602002002410136021820022004360214200220033602102001280228210a200128022421092001280220210b200128020c2103200128020821052001280204210420012802002107024002400340024002400240024002400240024002400240024020074102460d00200b0d014100210b0b2009450d012009200a460d014100200941106a280200200928020c418080808078461b2108200941146a280200210c2009280208210d2009280204210e41022107200941186a22012109200228021822062002280210460d050c080b024002402007450d002004450d010b20070d0441d4fec5800010a081808000000b41012107200521042003450d0220032101024020034107712205450d0003402001417f6a210120042802ac1421042005417f6a22050d000b0b200341084f0d010c020b20002002290210370200200041086a200241106a41086a2802003602000c0c0b034020042802ac142802ac142802ac142802ac142802ac142802ac142802ac142802ac142104200141786a22010d000b0b41002103410021050b0240200320042f01aa14490d00034020042802a0132201450d07200541016a210520042f01a814210320012104200320012f01aa144f0d000b0b200341016a210f0240024020050d00200421010c010b2004200f4102746a41ac146a28020021014100210f2005417f6a2206450d002005417e6a2108024020064107712205450d0003402006417f6a210620012802ac1421012005417f6a22050d000b0b20084107490d00034020012802ac142802ac142802ac142802ac142802ac142802ac142802ac142802ac142101200641786a22060d000b0b2004200341e0016c6a220541086a28020020052802002206200641054b22061b2208450d0620042003410c6c6a220441ac136a280200210d200441a8136a280200210e02402008412c6c2005280204200541046a20061b6a41546a2204280208220341014b0d0020042802042106200428021c2105024020042802002208450d0020052006460d010b2002200441106a36021c2002411c6a200820062005108e858080002004200536020420044101360200200428020821030b410021054100210802400240024002402003417e6a2203410220034102491b0e03000301000b2004410c6a21040c010b200441106a21040b2004280208210c200428020421080b200b417f6a210b0240200228021822062002280210460d0020012104200f21030c040b20074102470d01410221074100210520012104200f210320092101410021102009450d020b200a20016b41186e211041022107200121090c010b41002105024020090d0041002109200f210320012104200b21100c010b417f200b200a20096b41186e6a22042004200b491b2110200f2103200121040b200241106a2006201041016a2201417f20011b10aa868080000b200228021420064104746a2201200c36020c200120083602082001200d3602042001200e3602002002200641016a3602180c000b0b419cd0c2800010a081808000000b41fcb8c2800041fc0041f8bac2800010a181808000000b4104200910b280808000000b10ae80808000000b200241206a2480808080000bb00703077f027e057f23808080800041306b2202248080808000200141086a280200220320012802042204200320044b1b210520012802002106200128020c2107024002400240024003400240024020042005460d002001200441016a2204360204200241186a200610fc8680800020022802202208418080808078470d01200741013a00000b2000410036020820004280808080c0003702000c020b2008418180808078460d000b200229031821092002290224210a41002d00fca3c680001a41d00041002802c8a3c6800011818080800000220b450d01200b200a37020c200b2008360208200b2009370200200241013602142002200b3602102002410436020c0240200420034f0d00200420036b210c41202103410121080340024002402006280200220428020422014108490d002004280200220529000021092004200141786a3602042004200541086a360200200628020022042802042205450d0020042005417f6a220d36020420042004280200220141016a3602000240024002400240024020012d0000220e4103710e0400030102000b200e41027621040c030b20054104490d0320042005417c6a3602042004200141046a360200200141036a2d000041187420012f000141087472200e72220441808004490d03200441027621040c020b200e41044f0d0220054105490d0220042005417b6a3602042004200141056a360200200128000122044180808080044f0d010c020b200d450d0120042005417e6a3602042004200141026a36020020012d0001410874200e7241ffff03712204418002490d01200441027621040b2006280200220541046a220e28020022012004490d000240024020040d004101210d0c010b2004417f4c0d07200441002802c8a3c6800011818080800000220d450d08200d41002004108a8e8080001a200e28020021010b200120044f0d01200d41002802c0a3c68000118080808000000b200741013a00000c020b200d2005280200220f200410848e808000210d200e200120046b3602002005200f20046a3602002004ad422086200dad84210a02402008200228020c470d002002410c6a2008410110a8868080002002280210210b0b200b20036a2201200a3702002001417c6a2004360200200141746a20093702002002200841016a2208360214200341146a2103200c20086a4101470d000b0b2000200229020c370200200041086a2002410c6a41086a2802003602000b200241306a2480808080000f0b410441d00010b280808000000b10ae80808000000b4101200410b280808000000bc60a010b7f23808080800041206b2202248080808000200241086a200110e08c80800002400240024020022802082203450d00200228020c220441086a28020020042802002205200541054b22051b2206450d01200328020821072003280204210802402006412c6c2004280204200441046a20051b6a41546a2204280208220341014b0d0020042802042106200428021c2105024020042802002209450d0020052006460d010b2002200441106a36021c2002411c6a200920062005108e858080002004200536020420044101360200200428020821030b4100210502400240024002402003417e6a2203410220034102491b0e03000301000b2004410c6a21040c010b200441106a21040b20042802082106200428020421050b0240200128022041016a2204417f20041b22044104200441044b1b220341ffffff3f4b0d0020034104742209417f4c0d0041002d00fca3c680001a0240200941002802c8a3c68000118180808000002204450d002004200636020c20042005360208200420073602042004200836020020024101360218200220043602142002200336021002400240024020012802202207450d00200128020021082001280204210420012802082105200128020c210303400240024002402008450d002004450d010b20080d0141d4fec5800010a081808000000b410121082005210402402003450d0020032101024020034107712205450d0003402001417f6a210120042802ac1421042005417f6a22050d000b0b20034108490d00034020042802ac142802ac142802ac142802ac142802ac142802ac142802ac142802ac142104200141786a22010d000b0b41002103410021050b0240200320042f01aa14490d00034020042802a0132201450d04200541016a210520042f01a814210320012104200320012f01aa144f0d000b0b200341016a21090240024020050d00200421010c010b200420094102746a41ac146a2802002101410021092005417f6a2206450d002005417e6a210a024020064107712205450d0003402006417f6a210620012802ac1421012005417f6a22050d000b0b200a4107490d00034020012802ac142802ac142802ac142802ac142802ac142802ac142802ac142802ac142101200641786a22060d000b0b2004200341e0016c6a220541086a28020020052802002206200641054b22061b220a450d0320042003410c6c6a220441ac136a280200210b200441a8136a280200210c0240200a412c6c2005280204200541046a20061b6a41546a2204280208220341014b0d0020042802042106200428021c210502402004280200220a450d0020052006460d010b2002200441106a36021c2002411c6a200a20062005108e858080002004200536020420044101360200200428020821030b4100210502400240024002402003417e6a2203410220034102491b0e03000301000b2004410c6a21040c010b200441106a21040b20042802082106200428020421050b2007417f6a21070240200228021822032002280210470d00200241106a2003200741016a2204417f20041b10aa868080000b200228021420034104746a2204200636020c200420053602082004200b3602042004200c3602002002200341016a36021841002105200921032001210420070d000b0b20002002290210370200200041086a200241106a41086a2802003602000c060b419cd0c2800010a081808000000b41fcb8c2800041fc0041f8bac2800010a181808000000b4104200910b280808000000b10ae80808000000b2000410036020820004280808080c0003702000c010b41fcb8c2800041fc0041f8bac2800010a181808000000b200241206a2480808080000bd20201047f23808080800041c0006b2202248080808000200241206a41086a200141086a29020037030020022001290200370320200241086a200241206a109b87808000024002400240200228021022030d0020004100360208200041003602002002280208450d01200228020c41002802c0a3c68000118080808000000c010b200228020c220120032002413f6a10d88b8080002002280208210441002d00fca3c680001a418c0141002802c8a3c68000118180808000002205450d0120054100360200200541003b018a0120024100360218200220053602142002410036021c200220012003410c6c6a36023820022004360234200220013602302002200136022c2002418180808078360220200241146a200241206a2002411c6a10c3858080002000200228021c360208200020022902143702000b200241c0006a2480808080000f0b4104418c0110b280808000000b7c01017f23808080800041106b2201248080808000200142888080808001370200200142808080808001370208200041c4006a200110c182808000200041c0006a410036020020004280808080c000370338200041d8006a410036020020004280808080c000370350200041850c3b0100200141106a2480808080000bed0201027f23808080800041206b220124808080800041002d00fca3c680001a0240413041002802c8a3c680001181808080000022020d004108413010b280808000000b200242bea4ae99af91eca2827f370318200242dac1a9c58db4fcb8cb00370308200242f1acbf83d7fffdaeb77f370300200241fd81808000360210200241206a42a1efe9e2fe938eee74370300200241286a41fe818080003602002001200241306a36021c200141023602182001200236021420012002360210200141046a200141106a108487808000200142888080808001370210200142808080808001370218200041c4006a200141106a10fa868080002001411b6a200141046a41086a280200360000200041c0006a410036020020004280808080c000370338200041043a0000200041d8006a410036020020004280808080c0003703502001200129020437001320002001290010370001200041086a200141176a290000370000200141206a2480808080000beb0201027f23808080800041206b220124808080800041002d00fca3c680001a0240413041002802c8a3c680001181808080000022020d004108413010b280808000000b200242d7c9cb8fc1cf97db3e37031820024293888c8f89fdc6ec9e7f370308200242a5e9e3ab9e929adc2c370300200241ef80808000360210200241206a42e88488d0c0e3aebc13370300200241286a41b5808080003602002001200241306a36021c200141023602182001200236021420012002360210200141046a200141106a108487808000200142888080808001370210200142808080808001370218200041c4006a200141106a10fa868080002001411b6a200141046a41086a280200360000200041c0006a410036020020004280808080c000370338200041043a0000200041d8006a410036020020004280808080c0003703502001200129020437001320002001290010370001200041086a200141176a290000370000200141206a2480808080000bec0201027f23808080800041206b220124808080800041002d00fca3c680001a0240413041002802c8a3c680001181808080000022020d004108413010b280808000000b200242a5e9e3ab9e929adc2c370318200242949384fce98ae9ac977f370308200242c4a8f7cba2aea4ad35370300200241ff81808000360210200241206a4293888c8f89fdc6ec9e7f370300200241286a41ef808080003602002001200241306a36021c200141023602182001200236021420012002360210200141046a200141106a108487808000200142888080808001370210200142808080808001370218200041c4006a200141106a10fa868080002001411b6a200141046a41086a280200360000200041c0006a410036020020004280808080c000370338200041043a0000200041d8006a410036020020004280808080c0003703502001200129020437001320002001290010370001200041086a200141176a290000370000200141206a2480808080000beb0201027f23808080800041206b220124808080800041002d00fca3c680001a0240413041002802c8a3c680001181808080000022020d004108413010b280808000000b200242b7a0aca7a89ca5e845370318200242d4a5e8fca9e787a92e370308200242cce9ab8ffbfcead3703703002002418082808000360210200241206a42f58896d99bcda087da00370300200241286a4181828080003602002001200241306a36021c200141023602182001200236021420012002360210200141046a200141106a108487808000200142888080808001370210200142808080808001370218200041c4006a200141106a10fa868080002001411b6a200141046a41086a280200360000200041c0006a410036020020004280808080c000370338200041043a0000200041d8006a410036020020004280808080c0003703502001200129020437001320002001290010370001200041086a200141176a290000370000200141206a2480808080000bec0201027f23808080800041206b220124808080800041002d00fca3c680001a0240413041002802c8a3c680001181808080000022020d004108413010b280808000000b200242a5e9e3ab9e929adc2c37031820024293888c8f89fdc6ec9e7f370308200242a5e9e3ab9e929adc2c370300200241ef80808000360210200241206a4293888c8f89fdc6ec9e7f370300200241286a41ef808080003602002001200241306a36021c200141023602182001200236021420012002360210200141046a200141106a108487808000200142888080808001370210200142808080808001370218200041c4006a200141106a10fa868080002001411b6a200141046a41086a280200360000200041c0006a410036020020004280808080c000370338200041043a0000200041d8006a410036020020004280808080c0003703502001200129020437001320002001290010370001200041086a200141176a290000370000200141206a2480808080000baf0101017f23808080800041106b2201248080808000200142888080808001370200200142808080808001370208200041c4006a200110fa86808000200041106a42e6ed8d82cc91adcb05370300200042dbd791d5c2919eaecd00370308200041c0006a410036020020004280808080c000370338200041d8006a410036020020004280808080c00037035020004120360220200041c880808000360218200041033a0000200141106a2480808080000baf0101017f23808080800041106b2201248080808000200142888080808001370200200142808080808001370208200041c4006a200110fa86808000200041106a42e6ed8d82cc91adcb05370300200042dbd791d5c2919eaecd00370308200041c0006a410036020020004280808080c000370338200041d8006a410036020020004280808080c00037035020004108360220200041c880808000360218200041033a0000200141106a2480808080000b8f0503017f017e057f23808080800041106b22022480808080002000290300210302402001280200200128020822046b41074b0d0020012004410810b182808000200128020821040b2001200441086a360208200128020420046a2003370000200041146a28020021052002200041186a28020022043602082002200241086a36020c2002410c6a200110c08a80800002402004450d002004410c6c2106200541086a210503402005417c6a28020021072002200528020022043602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822086b20044f0d0020012008200410b182808000200128020821080b200128020420086a2007200410848e8080001a2001200820046a3602082005410c6a2105200641746a22060d000b0b200041206a28020021052002200041246a28020022043602082002200241086a36020c2002410c6a200110c08a808000024002402004450d002004410c6c2106200541086a210503402005417c6a28020021072002200528020022043602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822086b20044f0d0020012008200410b182808000200128020821080b200128020420086a2007200410848e8080001a2001200820046a22043602082005410c6a2105200641746a22060d000c020b0b200128020821040b200029030821030240200128020020046b41074b0d0020012004410810b182808000200128020821040b200128020420046a20033700002001200441086a220436020820002d00282105024020012802002004470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a20053a0000200241106a2480808080000bd70801047f0240024020002d00000d000240200128020020012802082202470d0020012002410110b182808000200128020821020b2001200241016a22033602082001280204220420026a41003a00000240024002400240024002400240024002400240024020002d00010e0b000102030405060708090a000b024020012802002003470d0020012003410110b18280800020012802042104200128020821030b200420036a41003a00000c0b0b024020012802002003470d0020012003410110b18280800020012802042104200128020821030b200420036a41013a00000c0a0b024020012802002003470d0020012003410110b18280800020012802042104200128020821030b200420036a41023a00000c090b024020012802002003470d0020012003410110b18280800020012802042104200128020821030b200420036a41033a00000c080b024020012802002003470d0020012003410110b18280800020012802042104200128020821030b200420036a41043a00000c070b024020012802002003470d0020012003410110b18280800020012802042104200128020821030b200420036a41053a00000c060b024020012802002003470d0020012003410110b18280800020012802042104200128020821030b200420036a41063a00000c050b200041026a2d0000210502400240200128020022022003460d00200321000c010b20012003410110b1828080002001280200210220012802042104200128020821000b2001200041016a2203360208200420006a41073a0000024020022003470d0020012002410110b18280800020012802042104200128020821030b200420036a20053a00000c040b024020012802002003470d0020012003410110b18280800020012802042104200128020821030b200420036a41083a00000c030b024020012802002003470d0020012003410110b18280800020012802042104200128020821030b200420036a41093a00000c020b024020012802002003470d0020012003410110b18280800020012802042104200128020821030b200420036a410a3a00000c010b0240200128020020012802082202470d0020012002410110b182808000200128020821020b2001200241016a22033602082001280204220420026a41013a000002400240024020002d00010e03000102000b024020012802002003470d0020012003410110b18280800020012802042104200128020821030b200420036a41003a00000c020b024020012802002003470d0020012003410110b18280800020012802042104200128020821030b200420036a41013a00000c010b200041026a2d0000210502400240200128020022022003460d00200321000c010b20012003410110b1828080002001280200210220012802042104200128020821000b2001200041016a2203360208200420006a41023a0000024020022003470d0020012002410110b18280800020012802042104200128020821030b200420036a20053a00000b2001200341016a3602080bb92a03047f017e017f23808080800041f0016b220224808080800002400240024002400240024002400240024002400240024020012d0028417f6a0e0b000102030405060708090a000b200141306a28020021032001412c6a2802002104200128020021010240024010fa81808000220541fe014d0d0042002106410921050c010b2002200541016a3602484100210541b1e3c080004113200241c8006a410441002802e0a1c680001186808080000041002802e8a1c680001188808080000041002802b0a1c6800011888080800000200241c8006a10fb81808000420221060b02402004450d00200341002802c0a3c68000118080808000000b20012001280200417f6a2204360200024020040d00200141086a28020022032001410c6a28020022042802001180808080000002402004280204450d00200341002802c0a3c68000118080808000000b200141046a22042004280200417f6a220436020020040d00200141002802c0a3c68000118080808000000b2000420037030820002006370300200041216a41003a0000200041206a20053a0000200041186a41003a00000c0a0b200141306a2903002106200241b8016a41206a200141206a290300370300200241b8016a41186a2204200141186a290300370300200241b8016a41106a2205200141106a290300370300200241b8016a41086a2203200141086a290300370300200220012903003703b8012002200637038001200220024180016a3602e001200241c8006a200241b8016a10bb8680800002400240200229034822064202520d002004200241c8006a41206a2903003703002005200241c8006a41186a2903003703002003200241c8006a41106a290300370300200220022903503703b8010c010b200020022903703703282004200241c8006a41206a2903003703002005200241c8006a41186a2903003703002003200241c8006a41106a290300370300200041306a200241c8006a41306a290300370300200220022903503703b8010b200041086a220120022903b80137030020002006370300200141186a200241b8016a41186a290300370300200141106a200241b8016a41106a290300370300200141086a200241b8016a41086a2903003703000c090b20024180016a41086a2204200141346a28020036020020022001412c6a29020022063703800120024180016a412c6a200141206a290200370200200241a4016a200141186a2902003702002002419c016a200141106a29020037020020024194016a200141086a2902003702002002200129020037028c01200241b8016a41306a20024180016a41306a280200360200200241b8016a41286a20024180016a41286a290300370300200241b8016a41206a20024180016a41206a290300370300200241b8016a41186a220120024180016a41186a290300370300200241b8016a41106a220520024180016a41106a290300370300200241b8016a41086a22032004290300370300200220063703b801200241c8006a200241b8016a10b58680800002400240200229034822064202520d002001200241c8006a41206a2903003703002005200241c8006a41186a2903003703002003200241c8006a41106a290300370300200220022903503703b8010c010b200020022903703703282001200241c8006a41206a2903003703002005200241c8006a41186a2903003703002003200241c8006a41106a290300370300200041306a200241c8006a41306a290300370300200220022903503703b8010b200041086a220120022903b80137030020002006370300200141186a200241b8016a41186a290300370300200141106a200241b8016a41106a290300370300200141086a200241b8016a41086a2903003703000c080b20024180016a41086a2204200141346a28020036020020022001412c6a29020022063703800120024180016a412c6a200141206a290200370200200241a4016a200141186a2902003702002002419c016a200141106a29020037020020024194016a200141086a2902003702002002200129020037028c01200241b8016a41306a20024180016a41306a280200360200200241b8016a41286a20024180016a41286a290300370300200241b8016a41206a20024180016a41206a290300370300200241b8016a41186a220120024180016a41186a290300370300200241b8016a41106a220520024180016a41106a290300370300200241b8016a41086a22032004290300370300200220063703b801200241c8006a200241b8016a10c48680800002400240200229034822064202520d002001200241c8006a41206a2903003703002005200241c8006a41186a2903003703002003200241c8006a41106a290300370300200220022903503703b8010c010b200020022903703703282001200241c8006a41206a2903003703002005200241c8006a41186a2903003703002003200241c8006a41106a290300370300200041306a200241c8006a41306a290300370300200220022903503703b8010b200041086a220120022903b80137030020002006370300200141186a200241b8016a41186a290300370300200141106a200241b8016a41106a290300370300200141086a200241b8016a41086a2903003703000c070b200241086a41086a2204200141346a28020036020020022001412c6a290200370308200241b8016a41206a200141206a290300370300200241b8016a41186a200141186a290300370300200241b8016a41106a200141106a290300370300200241b8016a41086a200141086a290300370300200220012903003703b8012002200241086a3602e001200241c8006a200241b8016a10b386808000024020042802002204450d00200228020c2101034002402001280200450d00200141046a28020041002802c0a3c68000118080808000000b02402001410c6a280200450d00200141106a28020041002802c0a3c68000118080808000000b200141186a21012004417f6a22040d000b0b02402002280208450d00200228020c41002802c0a3c68000118080808000000b02400240200229034822064202520d00200241b8016a41186a200241e8006a290300370300200241b8016a41106a200241c8006a41186a290300370300200241b8016a41086a200241c8006a41106a290300370300200220022903503703b801200041086a21010c010b20002002290370370328200241b8016a41186a200241e8006a290300370300200241b8016a41106a200241c8006a41186a290300370300200241b8016a41086a200241c8006a41106a290300370300200041306a200241c8006a41306a290300370300200220022903503703b801200041086a21010b200120022903b80137030020002006370300200141186a200241b8016a41186a290300370300200141106a200241b8016a41106a290300370300200141086a200241b8016a41086a2903003703000c060b200241186a41086a2204200141346a28020036020020022001412c6a290200370318200241b8016a41206a200141206a290300370300200241b8016a41186a200141186a290300370300200241b8016a41106a200141106a290300370300200241b8016a41086a200141086a290300370300200220012903003703b8012002200241186a3602e001200241c8006a200241b8016a10b986808000024020042802002201450d00200228021c21072001410171210341002104024020014101460d002001417e7121052007210141002104034002402001280200450d00200141046a28020041002802c0a3c68000118080808000000b02402001410c6a280200450d00200141106a28020041002802c0a3c68000118080808000000b200141186a21012005200441026a2204470d000b0b2003450d0020072004410c6c6a2201280200450d00200128020441002802c0a3c68000118080808000000b02402002280218450d00200228021c41002802c0a3c68000118080808000000b02400240200229034822064202520d00200241b8016a41186a200241e8006a290300370300200241b8016a41106a200241c8006a41186a290300370300200241b8016a41086a200241c8006a41106a290300370300200220022903503703b801200041086a21010c010b20002002290370370328200241b8016a41186a200241e8006a290300370300200241b8016a41106a200241c8006a41186a290300370300200241b8016a41086a200241c8006a41106a290300370300200041306a200241c8006a41306a290300370300200220022903503703b801200041086a21010b200120022903b80137030020002006370300200141186a200241b8016a41186a290300370300200141106a200241b8016a41106a290300370300200141086a200241b8016a41086a2903003703000c050b200241286a41086a200141386a2802003602002002200141306a29020037032820022001412c6a28020036028001200241b8016a41206a200141206a290300370300200241b8016a41186a200141186a290300370300200241b8016a41106a200141106a290300370300200241b8016a41086a200141086a290300370300200220012903003703b801200220024180016a3602e4012002200241286a3602e001200241c8006a200241b8016a10c38680800002402002280228450d00200228022c41002802c0a3c68000118080808000000b02400240200229034822064202520d00200241b8016a41186a200241e8006a290300370300200241b8016a41106a200241c8006a41186a290300370300200241b8016a41086a200241c8006a41106a290300370300200220022903503703b801200041086a21010c010b20002002290370370328200241b8016a41186a200241e8006a290300370300200241b8016a41106a200241c8006a41186a290300370300200241b8016a41086a200241c8006a41106a290300370300200041306a200241c8006a41306a290300370300200220022903503703b801200041086a21010b200120022903b80137030020002006370300200141186a200241b8016a41186a290300370300200141106a200241b8016a41106a290300370300200141086a200241b8016a41086a2903003703000c040b200241386a41086a200141346a28020036020020022001412c6a290200370338200241b8016a41206a200141206a290300370300200241b8016a41186a200141186a290300370300200241b8016a41106a200141106a290300370300200241b8016a41086a200141086a290300370300200220012903003703b8012002200241386a3602e001200241c8006a200241b8016a10be8680800002402002280238450d00200228023c41002802c0a3c68000118080808000000b02400240200229034822064202520d00200241b8016a41186a200241e8006a290300370300200241b8016a41106a200241c8006a41186a290300370300200241b8016a41086a200241c8006a41106a290300370300200220022903503703b801200041086a21010c010b20002002290370370328200241b8016a41186a200241e8006a290300370300200241b8016a41106a200241c8006a41186a290300370300200241b8016a41086a200241c8006a41106a290300370300200041306a200241c8006a41306a290300370300200220022903503703b801200041086a21010b200120022903b80137030020002006370300200141186a200241b8016a41186a290300370300200141106a200241b8016a41106a290300370300200141086a200241b8016a41086a2903003703000c030b200241b8016a41186a200141c1006a290000370300200241b8016a41106a200141396a290000370300200241b8016a41086a200141316a2900003703002002200141296a2900003703b801200241c8006a41206a200141206a290300370300200241c8006a41186a200141186a290300370300200241c8006a41106a200141106a290300370300200241c8006a41086a200141086a290300370300200220012903003703482002200241b8016a36027020024180016a200241c8006a10c5868080000240024020022d008001410e470d0020004200370308200041206a2101420221060c010b2000200229028001370220200041286a20024180016a41086a290200370200200241ca006a20024193016a2d000022013a0000200220022f00910122043b014820022d0090012105200041336a20013a0000200041316a20043b0000200041306a20053a0000200041186a2101420021060b20002006370300200141003a00000c020b200241b8016a41186a200141c1006a290000370300200241b8016a41106a200141396a290000370300200241b8016a41086a200141316a2900003703002002200141296a2900003703b801200241c8006a41206a200141206a290300370300200241c8006a41186a200141186a290300370300200241c8006a41106a200141106a290300370300200241c8006a41086a200141086a290300370300200220012903003703482002200241b8016a36027020024180016a200241c8006a10b7868080000240024020022d008001410e470d0020004200370308200041206a2101420221060c010b2000200229028001370220200041286a20024180016a41086a290200370200200241ca006a20024193016a2d000022013a0000200220022f00910122043b014820022d0090012105200041336a20013a0000200041316a20043b0000200041306a20053a0000200041186a2101420021060b20002006370300200141003a00000c010b20012802002104200241b8016a41086a200141346a28020036020020022001412c6a2902003703b801200241c8006a200241b8016a10bc8680800020042004280200417f6a2201360200024020010d00200441086a28020022052004410c6a28020022012802001180808080000002402001280204450d00200541002802c0a3c68000118080808000000b200441046a22012001280200417f6a220136020020010d00200441002802c0a3c68000118080808000000b02400240200229034822064202520d00200241b8016a41186a200241e8006a290300370300200241b8016a41106a200241c8006a41186a290300370300200241b8016a41086a200241c8006a41106a290300370300200220022903503703b801200041086a21010c010b20002002290370370328200241b8016a41186a200241e8006a290300370300200241b8016a41106a200241c8006a41186a290300370300200241b8016a41086a200241c8006a41106a290300370300200041306a200241c8006a41306a290300370300200220022903503703b801200041086a21010b200120022903b80137030020002006370300200141186a200241b8016a41186a290300370300200141106a200241b8016a41106a290300370300200141086a200241b8016a41086a2903003703000b200241f0016a2480808080000b850702087f017e23808080800041306b2201248080808000200142919fd78da1a1ad84ff003703182001429ceccef7a6c0dedd20370310200142958ed1e593cab9fca47f370328200142e6d0c3af83b9abdfff0037032020014100360204200141106a4120200141046a410441002802e0a1c6800011868080800000024002400240024002400240200041206a2802002202450d002000411c6a2802002103200142919fd78da1a1ad84ff003703182001429ceccef7a6c0dedd2037031020014282fceae49dd9e2861d370328200142de8c84a1ecd0a6d30c370320200141046a200141106a10e288808000024020012802042204418080808078460d0020012802082105200128020c0d022004450d00200541002802c0a3c68000118080808000000b200241b3e6cc194b0d03200241286c2206417f4c0d034100210541002d00fca3c680001a200641002802c8a3c68000118180808000002207450d04200221080240034020062005460d0120032903202109200720056a22042003290300370300200441186a200341186a290300370300200441106a200341106a290300370300200441086a200341086a290300370300200441206a2009370300200541286a2105200341286a21032008417f6a22080d000b0b2002410a4b0d022001200236021020012002ad4220862007ad84370214200141106a10f188808000200141106a10f0888080002001280210450d00200128021441002802c0a3c68000118080808000000b200142919fd78da1a1ad84ff003703182001429ceccef7a6c0dedd20370310200142c2a5b2f6d3b4fb986f370328200142dcd7ddd8f18e8ca1e30037032041002d00fca3c680001a411141002802c8a3c68000118180808000002203450d042003200029030037000020032000290308370008200320002d00103a0010200141106a41202003411141002802e0a1c6800011868080800000200341002802c0a3c6800011808080800000200141306a2480808080000f0b02402004450d00200541002802c0a3c68000118080808000000b2001411c6a420037020020014101360214200141e4ecc28000360210200141e4e7c28000360218200141106a41ececc2800010f680808000000b200741002802c0a3c680001180808080000041dcebc2800041c400200141106a41a0ecc2800041b0ecc28000108981808000000b10ae80808000000b4108200610b280808000000b4101411110b280808000000bf10501047f2380808080004190016b2201248080808000200142919fd78da1a1ad84ff003703082001429ceccef7a6c0dedd20370300200141cc006a2001411041002802c0a1c680001185808080000002400240200128024c2202418080808078460d0020012802502103024020012802544110490d0020012003411010888e808000210402402002450d00200341002802c0a3c68000118080808000000b20040d010c020b2002450d00200341002802c0a3c68000118080808000000b41002102200141003b011e02400240417f4100280284a4c680002203410347200341034b1b2203450d00200341ff017141ff01470d010b200141286a410c6a418282808000360200200141838280800036022c20014104360224200141f5a6c4800036022020012001411e6a3602302001200141206a3602284100280290a1c680002102410028028ca1c6800021034100280280a4c68000210420014184016a4202370200200141fc006a4103360200200141f4006a4116360200200141f0006a41c1e5c28000360200200141e4006a41e8e4c28000ad4280808080900b84370200200141cc006a410c6a41d7e5c28000ad4280808080b0028437020020014180016a200141286a360200200141f0e6c280003602782001410336026c200141003602602001410036025420014281808080800e37024c200341ecf2c08000200441024622041b200141cc006a200241d4f2c0800020041b2802101184808080000020012f011e21020b200141cc006a41f5a6c48000410441002802a0a3c6800011858080800000200141cc006a41106a220341bccdc48000411541002802a0a3c6800011858080800000200141286a41186a200141cc006a41186a290000370300200141286a41106a2003290000370300200141286a41086a200141cc006a41086a2900003703002001200129004c370328200120023b014c200141286a4120200141cc006a410241002802e0a1c68000118680808000000b200042003703082000420037030020014190016a2480808080000b930201027f23808080800041106b2202248080808000200220003602002002200128021441fc94c38000410e200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a2002418c95c38000108d81808000210120022d000c210002400240200128020022030d00200041ff017141004721010c010b41012101200041ff01710d0020022802082100024020034101470d0020022d000d41ff0171450d0020002d001c4104710d00410121012000280214418ca5c080004101200041186a28020028020c118280808000000d010b2000280214418da5c080004101200041186a28020028020c1182808080000021010b200241106a24808080800020010bf51a03017f017e027f23808080800041c0016b2202248080808000024002400240024002400240024002400240024020012d0028417f6a0e09000102030405060708000b200241e4006a200141d0006a290200370200200241dc006a200141c8006a290200370200200241d4006a200141c0006a2902003702002002200141386a29020037024c200141306a2903002103200241206a41086a200141086a290300370300200241206a41106a200141106a290300370300200241206a41186a200141186a290300370300200241206a41206a200141206a29030037030020022001290300370320200220033703102002200241106a36024820024190016a200241206a10b2868080000240024020022d009001410e470d0020004200370308200041206a2101420221030c010b2000200229029001370220200041286a20024190016a41086a290200370200200241226a200241a3016a2d000022013a0000200220022f00a10122043b012020022d00a0012105200041336a20013a0000200041316a20043b0000200041306a20053a0000200041186a2101420021030b20002003370300200141003a00000c080b200241e4006a200141d0006a290200370200200241dc006a200141c8006a290200370200200241d4006a200141c0006a290200370200200241f4006a200141e0006a290200370200200241fc006a200141e8006a29020037020020024184016a200141f0006a2902003702002002200141386a29020037024c2002200141d8006a29020037026c200141306a2903002103200241206a41086a200141086a290300370300200241206a41106a200141106a290300370300200241206a41186a200141186a290300370300200241206a41206a200141206a29030037030020022001290300370320200220033703102002200241106a36024820024190016a200241206a10c8868080000240024020022d009001410e470d0020004200370308200041206a2101420221030c010b2000200229029001370220200041286a20024190016a41086a290200370200200241226a200241a3016a2d000022013a0000200220022f00a10122043b012020022d00a0012105200041336a20013a0000200041316a20043b0000200041306a20053a0000200041186a2101420021030b20002003370300200141003a00000c070b200241e4006a200141d0006a290200370200200241dc006a200141c8006a290200370200200241d4006a200141c0006a2902003702002002200141386a29020037024c200141306a2903002103200241206a41086a200141086a290300370300200241206a41106a200141106a290300370300200241206a41186a200141186a290300370300200241206a41206a200141206a29030037030020022001290300370320200220033703102002200241106a36024820024190016a200241206a10bd868080000240024020022d009001410e470d0020004200370308200041206a2101420221030c010b2000200229029001370220200041286a20024190016a41086a290200370200200241226a200241a3016a2d000022013a0000200220022f00a10122043b012020022d00a0012105200041336a20013a0000200041316a20043b0000200041306a20053a0000200041186a2101420021030b20002003370300200141003a00000c060b200241e4006a200141c1006a290000370200200241dc006a200141396a290000370200200241d4006a200141316a2900003702002002200141296a29000037024c200141c9006a2d00002104200241206a41086a200141086a290300370300200241206a41106a200141106a290300370300200241206a41186a200141186a290300370300200241206a41206a200141206a29030037030020022001290300370320200220043a00102002200241106a36024820024190016a200241206a10c6868080000240024020022d009001410e470d0020004200370308200041206a2101420221030c010b2000200229029001370220200041286a20024190016a41086a290200370200200241226a200241a3016a2d000022013a0000200220022f00a10122043b012020022d00a0012105200041336a20013a0000200041316a20043b0000200041306a20053a0000200041186a2101420021030b20002003370300200141003a00000c050b200241e4006a200141d0006a290200370200200241dc006a200141c8006a290200370200200241d4006a200141c0006a2902003702002002200141386a29020037024c200141306a2903002103200241206a41086a200141086a290300370300200241206a41106a200141106a290300370300200241206a41186a200141186a290300370300200241206a41206a200141206a29030037030020022001290300370320200220033703102002200241106a36024820024190016a200241206a10c2868080000240024020022d009001410e470d0020004200370308200041206a2101420221030c010b2000200229029001370220200041286a20024190016a41086a290200370200200241226a200241a3016a2d000022013a0000200220022f00a10122043b012020022d00a0012105200041336a20013a0000200041316a20043b0000200041306a20053a0000200041186a2101420021030b20002003370300200141003a00000c040b200241086a200141346a28020036020020022001412c6a29020037030020024190016a41206a200141206a29030037030020024190016a41186a200141186a29030037030020024190016a41106a200141106a29030037030020024190016a41086a200141086a2903003703002002200129030037039001200220023602b801200241206a20024190016a10ba8680800002402002280200450d00200228020441002802c0a3c68000118080808000000b02400240200229032022034202520d0020024190016a41186a200241c0006a29030037030020024190016a41106a200241206a41186a29030037030020024190016a41086a200241206a41106a2903003703002002200229032837039001200041086a21010c010b2000200229034837032820024190016a41186a200241c0006a29030037030020024190016a41106a200241206a41186a29030037030020024190016a41086a200241206a41106a290300370300200041306a200241206a41306a2903003703002002200229032837039001200041086a21010b200120022903900137030020002003370300200141186a20024190016a41186a290300370300200141106a20024190016a41106a290300370300200141086a20024190016a41086a2903003703000c030b200241e4006a200141d0006a290200370200200241dc006a200141c8006a290200370200200241d4006a200141c0006a2902003702002002200141386a29020037024c200141306a2903002103200241206a41086a200141086a290300370300200241206a41106a200141106a290300370300200241206a41186a200141186a290300370300200241206a41206a200141206a29030037030020022001290300370320200220033703102002200241106a36024820024190016a200241206a10b1868080000240024020022d009001410e470d0020004200370308200041206a2101420221030c010b2000200229029001370220200041286a20024190016a41086a290200370200200241226a200241a3016a2d000022013a0000200220022f00a10122043b012020022d00a0012105200041336a20013a0000200041316a20043b0000200041306a20053a0000200041186a2101420021030b20002003370300200141003a00000c020b200141306a2903002103200141296a2d00002104200241206a41206a200141206a290300370300200241206a41186a200141186a290300370300200241206a41106a200141106a290300370300200241206a41086a200141086a29030037030020022001290300370320200220043a001f2002200337031020022002411f6a36024c2002200241106a36024820024190016a200241206a10c7868080000240024020022d009001410e470d0020004200370308200041206a2101420221030c010b2000200229029001370220200041286a20024190016a41086a290200370200200241226a200241a3016a2d000022013a0000200220022f00a10122043b012020022d00a0012105200041336a20013a0000200041316a20043b0000200041306a20053a0000200041186a2101420021030b20002003370300200141003a00000c010b200141296a2d00002104200141306a2903002103200241206a41206a200141206a290300370300200241206a41186a200141186a290300370300200241206a41106a200141106a290300370300200241206a41086a200141086a2903003703002002200129030037032020022003370310200220043a001f2002200241106a36024c20022002411f6a36024820024190016a200241206a10c1868080000240024020022d009001410e470d0020004200370308200041206a2101420221030c010b2000200229029001370220200041286a20024190016a41086a290200370200200241226a200241a3016a2d000022013a0000200220022f00a10122043b012020022d00a0012105200041336a20013a0000200041316a20043b0000200041306a20053a0000200041186a2101420021030b20002003370300200141003a00000b200241c0016a2480808080000b910602027f027e23808080800041e0036b220124808080800041002d00fca3c680001a024041980241002802c8a3c680001181808080000022020d00410841980210b280808000000b20002903202103200141f0016a41186a200041c0006a290000370300200141f0016a41106a200041386a290000370300200141f0016a41086a200041306a290000370300200141f0016a41306a200041d8006a290000370300200141f0016a41386a200041e0006a290000370300200141f0016a41c0006a200041e8006a290000370300200141f0016a41d0006a200041f8006a290000370300200141f0016a41d8006a20004180016a290000370300200141f0016a41e0006a20004188016a290000370300200120002900283703f0012001200041d0006a290000370398022001200041f0006a2900003703b802200041c8006a2903002104200141f0016a41e8006a20004190016a108d878080002001200437039002200141e8026a41186a200041b8016a290000370300200141e8026a41106a200041b0016a290000370300200141e8026a41086a200041a8016a290000370300200141e8026a41306a200041d0016a290000370300200141e8026a41386a200041d8016a290000370300200141e8026a41c0006a200041e0016a290000370300200141e8026a41d0006a200041f0016a290000370300200141e8026a41d8006a200041f8016a290000370300200141e8026a41e0006a20004180026a290000370300200120002900a0013703e8022001200041c8016a290000370390032001200041e8016a2900003703b003200041c0016a2903002104200141e8026a41e8006a20004188026a108d878080002001200437038803200141f8006a200141f0016a41f80010848e8080001a2001200141e8026a41f80010848e8080002101200241186a200041186a290300370300200241106a200041106a290300370300200241086a200041086a2903003703002002200029030037030020022003370320200241286a200141f8006a41f80010848e8080001a200241a0016a200141f80010848e8080001a200141e0036a2480808080000b840903017f037e047f23808080800041b0016b2201248080808000200142919fd78da1a1ad84ff003703682001429ceccef7a6c0dedd20370360200142aac0b0e5d1eaef8d63370378200142b8e2b0fbfb91a8ed827f370370200141206a200141e0006a10e688808000024002400240024002402001290328420020012802201b42017c22024200510d00200142919fd78da1a1ad84ff003703682001429ceccef7a6c0dedd20370360200142dca0bb8e8acbf9b3da00370378200142e78ec688edebaee7ba7f3703702001200242004206420010878e808000200141106a200141e0006a10e78880800020012903084200520d01200129030022032001290318420020012802101b7c22042003540d01200142919fd78da1a1ad84ff003703682001429ceccef7a6c0dedd2037036020014292d189e4a1e58a9fcc00370378200142aa9f83c8cbf687edfa0037037020014190016a200141e0006a10e288808000200128029001210520012802940121062001280298012107200142919fd78da1a1ad84ff00370398012001429ceccef7a6c0dedd2037039001200142c2a6e5f5c0d0a4be463703a801200142fccce3cbd7d3cfff023703a001200141e0006a20014190016a10e5888080000240024020012d00600d00200141a8016a4200370300200141a0016a420037030020014198016a420037030020014200370390010c010b200141a8016a200141f9006a290000370300200141a0016a200141f1006a29000037030020014198016a200141e9006a29000037030020012001290061370390010b200142919fd78da1a1ad84ff003703682001429ceccef7a6c0dedd20370360200142addc97bf9599fde7e3003703782001429ad7aad8b5ececacd100370370200141306a200141e0006a10e88880800020012d00404103470d02200142919fd78da1a1ad84ff003703682001429ceccef7a6c0dedd20370360200142c2a5b2f6d3b4fb986f370378200142dcd7ddd8f18e8ca1e300370370200141c8006a200141e0006a10e88880800020012d00584103460d03200141e0006a41106a200141c8006a41106a290300370300200141e0006a41086a200141c8006a41086a290300370300200120012903483703600c040b41ece8c2800041ef0041dce9c2800010a181808000000b41c18fc2800041fa0041bc90c2800010a181808000000b200141e0006a41106a200141306a41106a290300370300200141e0006a41086a200141306a41086a290300370300200120012903303703600c010b41ece9c2800041c80041b4eac2800010a181808000000b200042063703282000200437032020002002370318200020012903900137003020002001290360370300200041d8006a4100200720054180808080784622081b360200200041d4006a4108200620081b36020020004100200520081b360250200041386a20014190016a41086a290300370000200041c0006a20014190016a41106a290300370000200041c8006a200141a8016a290300370000200041086a200141e0006a41086a290300370300200041106a200141e0006a41106a290300370300200141b0016a2480808080000bfb0705017f037e017f017e037f23808080800041b0016b2201248080808000200142919fd78da1a1ad84ff003703682001429ceccef7a6c0dedd20370360200142aac0b0e5d1eaef8d63370378200142b8e2b0fbfb91a8ed827f370370200141386a200141e0006a10e6888080002001290340210220012903382103200142919fd78da1a1ad84ff003703682001429ceccef7a6c0dedd20370360200142aac0b0e5d1eaef8d63370378200142b8e2b0fbfb91a8ed827f370370200141286a200141e0006a10e6888080002001290330210420012802282105200142919fd78da1a1ad84ff003703682001429ceccef7a6c0dedd20370360200142dca0bb8e8acbf9b3da00370378200142e78ec688edebaee7ba7f370370200141086a2004420020051b42004206420010878e808000200141186a200141e0006a10e7888080000240024020012903104200520d00200129030822042001290320420020012802181b7c22062004540d00200142919fd78da1a1ad84ff003703682001429ceccef7a6c0dedd2037036020014282fceae49dd9e2861d370378200142de8c84a1ecd0a6d30c37037020014190016a200141e0006a10e288808000200128029001210520012802940121072001280298012108200142919fd78da1a1ad84ff00370398012001429ceccef7a6c0dedd2037039001200142c6e4a9b1aaa1afebf2003703a801200142fa82b1828b81b8f31e3703a001200141e0006a20014190016a10e5888080000240024020012d00600d00200141a8016a4200370300200141a0016a420037030020014198016a420037030020014200370390010c010b200141a8016a200141f9006a290000370300200141a0016a200141f1006a29000037030020014198016a200141e9006a29000037030020012001290061370390010b200142919fd78da1a1ad84ff003703682001429ceccef7a6c0dedd20370360200142c2a5b2f6d3b4fb986f370378200142dcd7ddd8f18e8ca1e300370370200141c8006a200141e0006a10e88880800020012d00584103460d0120002001290348370300200041106a200141c8006a41106a290300370300200041086a200141c8006a41086a290300370300200041d8006a4100200820054180808080784622091b360200200041d4006a4108200720091b36020020004100200520091b36025020004206370328200020063703202000200242002003a71b3703182000200129039001370030200041386a20014190016a41086a290300370000200041c0006a20014190016a41106a290300370000200041c8006a200141a8016a290300370000200141b0016a2480808080000f0b41c18fc2800041fa0041bc90c2800010a181808000000b41ece9c2800041c80041c4eac2800010a181808000000bf61704017f017e017f017e2380808080004190026b22042480808080000240024002400240024002402000280208450d00200442919fd78da1a1ad84ff003703a8012004429ceccef7a6c0dedd203703a001200442e9c5fea9cdfbc4d26d3703b80120044286aaece2939beae4653703b001200441286a200441a0016a10e7888080002004290330210520042802282106200442919fd78da1a1ad84ff003703a8012004429ceccef7a6c0dedd203703a001200442dca0bb8e8acbf9b3da003703b801200442e78ec688edebaee7ba7f3703b001200441186a200441a0016a10e788808000200442002005420020061b22052004290320420020042802181b7d220720072005561b4206802205370338200442919fd78da1a1ad84ff003703a8012004429ceccef7a6c0dedd203703a001200442aac0b0e5d1eaef8d633703b801200442b8e2b0fbfb91a8ed827f3703b001200441086a200441a0016a10e68880800020024101470d04427f200429031042017c22072007501b420120042802081b2005520d010c040b417f4100280284a4c680002202410247200241024b1b2202450d01200241ff017141ff01460d010c020b200420033602a001200441386a200441a0016a10f788808000200429033821050c020b4100280290a1c680002102410028028ca1c6800021034100280280a4c680002106200441d8016a4200370200200441d4016a41e4e7c28000360200200441d0016a4101360200200441c8016a410d360200200441c4016a41c6e8c28000360200200441b8016a41e8e4c28000ad4280808080900b84370200200441ac016a41f8eac28000ad4280808080b00184370200200441f0eac280003602cc01200441003602b401200441003602a80120044281808080b0ce003702a001200441023602c001200341ecf2c08000200641024622061b200441a0016a200241d4f2c0800020061b280210118480808000000b02402001280200450d00200128020441002802c0a3c68000118080808000000b2000280200450d01200028020441002802c0a3c68000118080808000000c010b200442919fd78da1a1ad84ff003703a8012004429ceccef7a6c0dedd203703a001200442aac0b0e5d1eaef8d633703b801200442b8e2b0fbfb91a8ed827f3703b001200420053703f001200441a0016a4120200441f0016a410841002802e0a1c6800011868080800000200441a0016a41086a200041086a280200360200200420002902003703a001200441a0016a10f3888080000240200429033842017c220550450d004183ebc2800041c90041ccebc2800010a181808000000b200442919fd78da1a1ad84ff003703f8012004429ceccef7a6c0dedd203703f001200442c2a6e5f5c0d0a4be4637038802200442fccce3cbd7d3cfff0237038002200441a0016a200441f0016a10e5888080000240024020042d00a0010d00200441d8006a4200370300200441d0006a4200370300200441c8006a4200370300200442003703400c010b200441d8006a200441b9016a290000370300200441d0006a200441b1016a290000370300200441c8006a200441a9016a290000370300200420042900a1013703400b200442919fd78da1a1ad84ff003703a8012004429ceccef7a6c0dedd203703a001200442958ed1e593cab9fca47f3703b801200442e6d0c3af83b9abdfff003703b0012004200441a0016a412010d0888080002004280204210020042802002102200442919fd78da1a1ad84ff003703a8012004429ceccef7a6c0dedd203703a001200442958ed1e593cab9fca47f3703b801200442e6d0c3af83b9abdfff003703b001200441003602f001200441a0016a4120200441f0016a410441002802e0a1c6800011868080800000200441003602bc01200441003602ac01200442013702a00120042000410020021b22003602a801200441f0016a200441c0006a2005200441a0016a4101200041016a2200417f20001b41087410908a80800041002d00fca3c680001a200442919fd78da1a1ad84ff003703a8012004429ceccef7a6c0dedd203703a001200442c2a6e5f5c0d0a4be463703b801200442fccce3cbd7d3cfff023703b0010240024002400240412041002802c8a3c68000118180808000002200450d00200020042900f001370000200041186a200441f0016a41186a290000370000200041106a200441f0016a41106a290000370000200041086a200441f0016a41086a290000370000200441a0016a41202000412041002802e0a1c6800011868080800000200041002802c0a3c680001180808080000041002d00fca3c680001a200442919fd78da1a1ad84ff003703a8012004429ceccef7a6c0dedd203703a001200442c6e4a9b1aaa1afebf2003703b801200442fa82b1828b81b8f31e3703b001412041002802c8a3c68000118180808000002200450d0120002004290340370000200041186a200441c0006a41186a290300370000200041106a200441c0006a41106a290300370000200041086a200441c0006a41086a290300370000200441a0016a41202000412041002802e0a1c6800011868080800000200041002802c0a3c6800011808080800000200110f08880800010f688808000200442919fd78da1a1ad84ff003703f8012004429ceccef7a6c0dedd203703f001200442c2a6e5f5c0d0a4be4637038802200442fccce3cbd7d3cfff0237038002200441a0016a200441f0016a10e5888080000240024020042d00a0010d0020044184016a4200370200200441fc006a4200370200200441f4006a42003702002004420037026c0c010b20044184016a200441b9016a290000370200200441fc006a200441b1016a290000370200200441f4006a200441a9016a290000370200200420042900a10137026c0b200441e0006a41086a2200200141086a280200360200200420012902002205370360200441cc016a20044188016a280200360200200441c4016a20044180016a290300370200200441bc016a200441f8006a290300370200200441b4016a200441f0006a290300370200200441ac016a2000290300370200200420053702a401200441003602a001200441f0016a41086a200441a0016a10cc86808000200441f4016a41c5003a000020044181848592043602f001200441f0016a10f588808000024020042802a0010d0020042802a401450d00200441a8016a28020041002802c0a3c68000118080808000000b200442919fd78da1a1ad84ff003703a8012004429ceccef7a6c0dedd203703a001200442addc97bf9599fde7e3003703b8012004429ad7aad8b5ececacd1003703b001200441f0016a200441a0016a10e888808000024020042d00800222014103460d0020042903f801210520042903f0012107200442919fd78da1a1ad84ff003703a8012004429ceccef7a6c0dedd203703a001200442c2a5b2f6d3b4fb986f3703b801200442dcd7ddd8f18e8ca1e3003703b00141002d00fca3c680001a411141002802c8a3c68000118180808000002200450d032000200737000020002005370008200020013a0010200441a0016a41202000411141002802e0a1c6800011868080800000200041002802c0a3c68000118080808000000b200441f0016a10f48880800020042d00800222014103460d0420042903f801210520042903f0012107200442919fd78da1a1ad84ff003703a8012004429ceccef7a6c0dedd203703a001200442addc97bf9599fde7e3003703b8012004429ad7aad8b5ececacd1003703b00141002d00fca3c680001a411141002802c8a3c68000118180808000002200450d032000200737000020002005370008200020013a0010200441a0016a41202000411141002802e0a1c6800011868080800000200041002802c0a3c6800011808080800000200441b8016a20013a0000200441b0016a2005370300200441b9016a200428008102360000200441bc016a20044184026a280000360000200420073703a801200441023602a00120044194016a200441a0016a10cc8680800020044190016a41c5003a0000200441818485920436028c012004418c016a10f58880800020042802a0010d0420042802a401450d0420042802a80141002802c0a3c68000118080808000000c040b4101412010b280808000000b4101412010b280808000000b4101411110b280808000000b4101411110b280808000000b20044190026a2480808080000bae0302027f037e23808080800041e0006b2201248080808000410021020240024020004201510d00200142919fd78da1a1ad84ff003703482001429ceccef7a6c0dedd20370340200142e9c5fea9cdfbc4d26d37035820014286aaece2939beae465370350200141306a200141c0006a10e7888080002001290338210320012903302104200142919fd78da1a1ad84ff003703482001429ceccef7a6c0dedd20370340200142aac0b0e5d1eaef8d63370358200142b8e2b0fbfb91a8ed827f370350200141206a200141c0006a10e6888080002001290328210020012802202102200142919fd78da1a1ad84ff003703482001429ceccef7a6c0dedd20370340200142dca0bb8e8acbf9b3da00370358200142e78ec688edebaee7ba7f37035020012000420020021b42004206420010878e808000200141106a200141c0006a10e78880800020012903084200520d01200129030022002001290318420020012802101b7c22052000540d014200200342002004a71b220020057d220320032000561b42055621020b200141e0006a24808080800020020f0b41c18fc2800041fa0041bc90c2800010a181808000000ba42901047f23808080800041206b22012480808080002001410036021020014280808080800137020841002d00fca3c680001a024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240410841002802c8a3c68000118180808000002202450d00200241fcecc280003602002002411536020441002d00fca3c680001a410841002802c8a3c68000118180808000002203450d01200342003700000240200128021022042001280208470d00200141086a2004109d86808000200128021021040b200128020c200441e8006c6a220441013a00602004410136025c2004200236025820044288808080103703502004200336024c2004428a8080808001370244200441a8c0c38000360240200441ef8080800036021820044100360200200442a5e9e3ab9e929adc2c370308200441106a4293888c8f89fdc6ec9e7f3703002001200128021041016a36021041002d00fca3c680001a410841002802c8a3c68000118180808000002204450d022004411b36020420044191edc280003602002001410136021c2001200436021820014101360214200141146a200141086a10fc8880800041002d00fca3c680001a411041002802c8a3c68000118180808000002202450d03200241acedc28000360200200241eaedc280003602082002413e3602042002410c6a412436020041002d00fca3c680001a410841002802c8a3c68000118180808000002203450d04200342003700000240200128021022042001280208470d00200141086a2004109d86808000200128021021040b200128020c200441e8006c6a220441013a00602004410236025c2004200236025820044288808080203703502004200336024c2004428b8080808001370244200441fabdc38000360240200441f580808000360218200442e3a4fae3cee3d18d7237030820044100360200200441106a42b8b6d386cdcbfab1a07f37030041002d00fca3c680001a2001200128021041016a360210410841002802c8a3c68000118180808000002202450d052002418eeec280003602002002411536020441002d00fca3c680001a410841002802c8a3c68000118180808000002203450d06200342003700000240200128021022042001280208470d00200141086a2004109d86808000200128021021040b200128020c200441e8006c6a220441013a00602004410136025c2004200236025820044288808080103703502004200336024c2004428b8080808001370244200441b4bec38000360240200441f580808000360218200442e3a4fae3cee3d18d7237030820044100360200200441106a42b8b6d386cdcbfab1a07f37030041002d00fca3c680001a2001200128021041016a36021041d00041002802c8a3c68000118180808000002204450d07200441a6f1c28000360248200441e5f0c28000360240200441a0f0c28000360238200441dbefc280003602302004419defc28000360228200441dceec28000360220200441e4e7c28000360218200441d1eec28000360210200441e4e7c280003602082004412e360204200441a3eec28000360200200441cc006a41c300360200200441c4006a41c1003602002004413c6a41c500360200200441346a41c5003602002004412c6a413e360200200441246a41c1003602002004411c6a4100360200200441146a410b3602002004410c6a410036020041002d00fca3c680001a412041002802c8a3c68000118180808000002203450d0820034200370000200341186a4200370000200341106a4200370000200341086a42003700000240200128021022022001280208470d00200141086a2002109d86808000200128021021020b200128020c200241e8006c6a220241013a00602002410a36025c20022004360258200242a0808080a0013703502002200336024c2002428a808080800437024420024197c1c38000360240200241f081808000360218200242c4ccab9c81ebb4b8db0037030820024100360200200241106a42a4d2928ecac0faa43237030041002d00fca3c680001a2001200128021041016a360210410841002802c8a3c68000118180808000002202450d09200241e9f1c28000360200200241d80036020441002d00fca3c680001a410141002802c8a3c68000118180808000002203450d0a200341003a00000240200128021022042001280208470d00200141086a2004109d86808000200128021021040b200128020c200441e8006c6a220441003a00602004410136025c2004200236025820044281808080103703502004200336024c200442988080801037024420044181c0c380003602402004418482808000360218200442c7facddcf584b3fea87f37030820044100360200200441106a42a8dcd6c1f2afcba52b37030041002d00fca3c680001a2001200128021041016a360210410841002802c8a3c68000118180808000002203450d0b200341c1f2c280003602002003411736020441002d00fca3c680001a412041002802c8a3c68000118180808000002202450d0c20024200370000200241186a4200370000200241106a4200370000200241086a42003700000240200128021022042001280208470d00200141086a2004109d86808000200128021021040b200128020c200441e8006c6a220441013a00602004410136025c20042003360258200442a0808080103703502004200236024c2004428e808080800437024420044190bfc38000360240200441f08180800036021820044100360200200442c4ccab9c81ebb4b8db00370308200441106a42a4d2928ecac0faa4323703002001200128021041016a36021041002d00fca3c680001a410841002802c8a3c68000118180808000002204450d0d20044118360204200441d8f2c280003602002001410136021c2001200436021820014101360214200141146a200141086a10818980800041002d00fca3c680001a41c80041002802c8a3c68000118180808000002204450d0e200441f0f2c28000360200200441aff5c28000360240200441edf4c28000360238200441b2f4c28000360230200441e4e7c280003602282004418df4c28000360220200441cdf3c280003602182004418ff3c28000360210200441e4e7c280003602082004411f360204200441c4006a41073602002004413c6a41c200360200200441346a413b3602002004412c6a4100360200200441246a41253602002004411c6a41c000360200200441146a413e3602002004410c6a410036020041002d00fca3c680001a410441002802c8a3c68000118180808000002203450d0f200341003600000240200128021022022001280208470d00200141086a2002109d86808000200128021021020b200128020c200241e8006c6a220241013a00602002410936025c200220043602582002428480808090013703502002200336024c2002428c808080c000370244200241e1bfc38000360240200241b58080800036021820024100360200200242d7c9cb8fc1cf97db3e370308200241106a42e88488d0c0e3aebc133703002001200128021041016a36021041002d00fca3c680001a410841002802c8a3c68000118180808000002204450d10200441c500360204200441b6f5c280003602002001410136021c2001200436021820014101360214200141146a200141086a109e8880800041002d00fca3c680001a411041002802c8a3c68000118180808000002202450d11200241fbf5c28000360200200241bbf6c28000360208200241c0003602042002410c6a41c70036020041002d00fca3c680001a410141002802c8a3c68000118180808000002203450d12200341003a00000240200128021022042001280208470d00200141086a2004109d86808000200128021021040b200128020c200441e8006c6a220441003a00602004410236025c2004200236025820044281808080203703502004200336024c2004428b80808010370244200441a9bec380003602402004418582808000360218200442c3d5bbd5b981e38c0337030820044100360200200441106a42c98d9ad7c1aad5e25937030041002d00fca3c680001a2001200128021041016a360210412041002802c8a3c68000118180808000002202450d1320024182f7c280003602002002418df8c28000360218200241e4e7c28000360210200241c7f7c28000360208200241c5003602042002411c6a41d200360200200241146a41003602002002410c6a41c60036020041002d00fca3c680001a410141002802c8a3c68000118180808000002203450d14200341003a00000240200128021022042001280208470d00200141086a2004109d86808000200128021021040b200128020c200441e8006c6a220441013a00602004410436025c2004200236025820044281808080c0003703502004200336024c2004429380808010370244200441ecc0c3800036024020044186828080003602182004428cc882a2e2dcdde2f00037030820044100360200200441106a42aa8be08180efaa931e37030041002d00fca3c680001a2001200128021041016a360210412841002802c8a3c68000118180808000002204450d15200441dff8c28000360200200441e3fac280003602202004418dfac28000360218200441bbf9c28000360210200441b6f9c28000360208200441d700360204200441246a41d6003602002004411c6a41d600360200200441146a41d2003602002004410c6a410536020041002d00fca3c680001a411041002802c8a3c68000118180808000002203450d1620034200370000200341086a42003700000240200128021022022001280208470d00200141086a2002109d86808000200128021021020b200128020c200241e8006c6a220241013a00602002410536025c2002200436025820024290808080d0003703502002200336024c2002428a8080808002370244200241bdc0c380003602402002418782808000360218200242bcf58bd0d59aa7c9db0037030820024100360200200241106a42e0df9add94b69bd2ff0037030041002d00fca3c680001a2001200128021041016a360210412841002802c8a3c68000118180808000002204450d17200441b9fbc28000360200200441f8fcc28000360220200441b4fcc28000360218200441effbc28000360210200441e4e7c2800036020820044136360204200441246a412c3602002004411c6a41c400360200200441146a41c5003602002004410c6a410036020041002d00fca3c680001a410841002802c8a3c68000118180808000002203450d18200342003700000240200128021022022001280208470d00200141086a2002109d86808000200128021021020b200128020c200241e8006c6a220241013a00602002410536025c2002200436025820024288808080d0003703502002200336024c200242888080808001370244200241c7c0c38000360240200241ef80808000360218200242a5e9e3ab9e929adc2c37030820024100360200200241106a4293888c8f89fdc6ec9e7f37030041002d00fca3c680001a2001200128021041016a360210411041002802c8a3c68000118180808000002202450d19200241a4fdc28000360200200241fcfdc28000360208200241d8003602042002410c6a410936020041002d00fca3c680001a410141002802c8a3c68000118180808000002203450d1a200341003a00000240200128021022042001280208470d00200141086a2004109d86808000200128021021040b200128020c200441e8006c6a220441003a00602004410236025c2004200236025820044281808080203703502004200336024c2004428b80808010370244200441b2c0c38000360240200441f680808000360218200442b7e2f1ac9bf5d4dc827f37030820044100360200200441106a42f2b3a2a5fbe78f9ac60037030041002d00fca3c680001a2001200128021041016a360210411041002802c8a3c68000118180808000002202450d1b20024185fec28000360200200241d0fec28000360208200241cb003602042002410c6a413a36020041002d00fca3c680001a410141002802c8a3c68000118180808000002203450d1c200341003a00000240200128021022042001280208470d00200141086a2004109d86808000200128021021040b200128020c200441e8006c6a220441003a00602004410236025c2004200236025820044281808080203703502004200336024c2004428f808080103702442004419abec38000360240200441f68080800036021820044100360200200442b7e2f1ac9bf5d4dc827f370308200441106a42f2b3a2a5fbe78f9ac6003703002001200128021041016a36021041002d00fca3c680001a41c00041002802c8a3c68000118180808000002204450d1d200441a182c38000360238200441d781c380003602302004418981c38000360228200441bc80c38000360220200441f0ffc28000360218200441e4e7c28000360210200441d4ffc28000360208200441ca003602042004418affc280003602002004413c6a412c360200200441346a41ca003602002004412c6a41ce00360200200441246a41cd003602002004411c6a41cc00360200200441146a41003602002004410c6a411c3602002001410836021c2001200436021820014108360214200141146a200141086a108289808000200041086a200141086a41086a28020036020020002001290208370200200041106a4104360200200041f5a6c4800036020c200141206a2480808080000f0b4104410810b280808000000b4101410810b280808000000b4104410810b280808000000b4104411010b280808000000b4101410810b280808000000b4104410810b280808000000b4101410810b280808000000b410441d00010b280808000000b4101412010b280808000000b4104410810b280808000000b4101410110b280808000000b4104410810b280808000000b4101412010b280808000000b4104410810b280808000000b410441c80010b280808000000b4101410410b280808000000b4104410810b280808000000b4104411010b280808000000b4101410110b280808000000b4104412010b280808000000b4101410110b280808000000b4104412810b280808000000b4101411010b280808000000b4104412810b280808000000b4101410810b280808000000b4104411010b280808000000b4101410110b280808000000b4104411010b280808000000b4101410110b280808000000b410441c00010b280808000000ba80901097f41002d00fca3c680001a02400240024002400240024002400240024041e00141002802c8a3c68000118180808000002201450d0041002d00fca3c680001a410841002802c8a3c68000118180808000002202450d012002420637000041002d00fca3c680001a411841002802c8a3c68000118180808000002203450d02200341cd82c38000360200200341ce83c380003602102003418883c380003602082003413b360204200341146a41c8003602002003410c6a41c60036020041002d00fca3c680001a410841002802c8a3c68000118180808000002204450d0320044290ce0037000041002d00fca3c680001a412841002802c8a3c68000118180808000002205450d04200541a384c38000360200200541ab86c38000360220200541e985c38000360218200541a885c38000360210200541e484c38000360208200541c100360204200541246a41283602002005411c6a41c200360200200541146a41c1003602002005410c6a41c40036020041002d00fca3c680001a410441002802c8a3c68000118180808000002206450d052006410a36000041002d00fca3c680001a410841002802c8a3c68000118180808000002207450d06200741e486c380003602002007412236020441002d00fca3c680001a410441002802c8a3c68000118180808000002208450d07200841e40036000041002d00fca3c680001a410841002802c8a3c68000118180808000002209450d08200941353602042009419487c38000360200200141b8016a42e88488d0c0e3aebc13370300200141b0016a42d7c9cb8fc1cf97db3e37030020014180016a42e88488d0c0e3aebc13370300200141f8006a42d7c9cb8fc1cf97db3e370300200141c8006a4293888c8f89fdc6ec9e7f370300200141c0006a42a5e9e3ab9e929adc2c370300200141106a4293888c8f89fdc6ec9e7f370300200142a5e9e3ab9e929adc2c370308200141dc016a4101360200200141d8016a2009360200200141d0016a428480808010370200200141cc016a2008360200200141c8016a4104360200200141c0016a41b580808000360200200141ac016a410d360200200141c987c380003602a801200141a4016a4101360200200141a0016a200736020020014198016a42848080801037020020014194016a200636020020014190016a410436020020014188016a41b580808000360200200141f4006a410e3602002001418687c38000360270200141ec006a4105360200200141e8006a2005360200200141e0006a4288808080d000370200200141dc006a2004360200200141d8006a4108360200200141d0006a41ef808080003602002001413c6a4111360200200141d386c38000360238200141033602342001200336023020014288808080303702282001200236022420014108360220200141ef808080003602182001410d3602042001419684c380003602002000410436020820002001360204200041043602000f0b410841e00110b280808000000b4101410810b280808000000b4104411810b280808000000b4101410810b280808000000b4104412810b280808000000b4101410410b280808000000b4104410810b280808000000b4101410410b280808000000b4104410810b280808000000b860503057f017e027f23808080800041c0006b2201248080808000200141186a41d687c38000410441d7e5c28000411341e4e7c28000410010d882808000200141106a42043702002001420037020820014280808080800137020041002d00fca3c680001a024002400240412041002802c8a3c68000118180808000002202450d002002410036021820024101360204200241da87c3800036020020014101360238200120023602302001200241206a36023c200120023602342001200141306a10fa8680800041002d00fca3c680001a20012802002103200128020421022001280208210420012802182105200129021c2106410841002802c8a3c68000118180808000002207450d01200741002903a088c380003702002001410036020820014280808080c000370200200141306a200141a888c38000411310be8b8080002001200141306a41bb88c38000411c10c18b808000200141246a200141d788c38000411210c48b8080002001200128022436020820012001280228220836020020012008200128022c41246c6a36020c20012008360204200141306a200110fb868080002005418080808078460d022001200336020820012002360200200120023602042001200220044105746a36020c200041c4006a200110fa868080002001410b6a200141306a41086a2802003600002000413c6a200637020020002005360238200041013a0000200041d8006a4101360200200041d4006a2007360200200041013602502001200129023037000320002001290000370001200041086a200141076a290000370000200141c0006a2480808080000f0b4108412010b280808000000b4104410810b280808000000b41a8d8c480004111419cd9c4800010a181808000000be20a03067f017e037f23808080800041c0006b2201248080808000200141206a41e988c38000410541d7e5c28000411341e4e7c28000410010d882808000200141186a42043702002001420037021020014280808080800137020841002d00fca3c680001a0240024002400240024002400240412041002802c8a3c68000118180808000002202450d002002410036021820024101360204200241da87c3800036020020014101360238200120023602302001200241206a36023c20012002360234200141086a200141306a10fa8680800041002d00fca3c680001a20012802082103200128020c2104200128021021052001280220210620012902242107410841002802c8a3c68000118180808000002208450d012008410029039089c3800037020041002d00fca3c680001a41002802c8a3c6800021022001410036021020014280808080c00037020841082002118180808000002209450d02200941002903c8e2c48000370200200141086a410010a086808000200128020c200141086a41086a220a28020041246c6a220241003a00202002411836021c2002419889c3800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141306a41086a200a28020041016a3602002001200129020837033041002d00fca3c680001a410841002802c8a3c68000118180808000002209450d03200941002903f0e3c480003702000240200128023822022001280230470d00200141306a200210a086808000200128023821020b2001280234200241246c6a220241013a00202002411836021c200241b089c3800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141086a41086a200141306a41086a28020041016a3602002001200129033037030841002d00fca3c680001a410841002802c8a3c68000118180808000002209450d0420094100290398e3c480003702000240200128021022022001280208470d00200141086a200210a086808000200128021021020b200128020c200241246c6a220241023a00202002411636021c200241c889c3800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141306a41086a200141086a41086a28020041016a3602002001200129030837033041002d00fca3c680001a410841002802c8a3c68000118180808000002209450d05200941002903f0e1c480003702000240200128023822022001280230470d00200141306a200210a086808000200128023821020b2001280234200241246c6a220241033a00202002411436021c200241de89c38000360218200241013602142002200936021020024280808080103702082002428080808080013702002001280238210920012802342102200120012802303602102001200236020820012002200941246c6a41246a3602142001200236020c200141306a200141086a10fb868080002006418080808078460d0620012003360210200120043602082001200436020c2001200420054105746a360214200041c4006a200141086a10fa86808000200141136a200141306a41086a2802003600002000413c6a200737020020002006360238200041013a0000200041d8006a4101360200200041d4006a2008360200200041013602502001200129023037000b20002001290008370001200041086a2001410f6a290000370000200141c0006a2480808080000f0b4108412010b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b41a8d8c480004111419cd9c4800010a181808000000bd90201037f23808080800041106b220224808080800002402001280200220328020020032802082204470d0020032004410110ab86808000200328020821040b200328020420046a41fb003a00002003200441016a3602082002200136020c20024180023b010802400240200241086a41f289c38000410b200041186a10d68680800022030d00024020022d0008450d0041002d00fca3c680001a411441002802c8a3c68000118180808000002203450d022003420037020c2003410d3602000c010b200241086a41fd89c38000410b200010d38680800022030d0002402002280208220341ff01710d0020034180fe0371450d000240200228020c280200220328020020032802082204470d0020032004410110ab86808000200328020821040b200328020420046a41fd003a00002003200441016a3602080b410021030b200241106a24808080800020030f0b4104411410b280808000000b8a0c02077f027e23808080800041a0026b220224808080800002400240024002400240024002400240200128020022032802042204450d0020032004417f6a36020420032003280200220441016a36020020042d00000e03010504020b200041053a00100c050b2001200128020441016a22033602040240200320012802084b0d0041002d00fca3c680001a41980241002802c8a3c68000118180808000002205450d02200241086a2001108f8d80800002402002280290022203418080808078460d002005200241086a41880210848e80800022064194026a200241086a4194026a280200360200200620022902940237028c02200620033602880220012001280204417f6a3602040240200128020022012802042203450d0020012003417f6a3602042001200128020041016a3602000b024020064198016a2802002207450d0020064194016a280200220821014100210303402008200341146c6a21040240024002400240024020012d00000e0400010102040b200141086a21040c020b200441086a21040c010b200441046a21040b2004280200450d00200428020441002802c0a3c68000118080808000000b200341016a2103200141146a21012007417f6a22070d000b0b0240200628029001450d0020062802940141002802c0a3c68000118080808000000b024020064190026a2802002207450d00200628028c02220821014100210303402008200341146c6a21040240024002400240024020012d00000e0400010102040b200141086a21040c020b200441086a21040c010b200441046a21040b2004280200450d00200428020441002802c0a3c68000118080808000000b200341016a2103200141146a21012007417f6a22070d000b0b200628028802450d002006418c026a28020041002802c0a3c68000118080808000000b200541002802c0a3c68000118080808000000b200041053a00100c040b200041053a00100c030b410841980210b280808000000b0240200128020022012802042203450d0020012003417f6a36020420012001280200220441016a36020020042d00004101470d0020034109490d002001200341776a22073602042001200441096a36020020074108490d002004290001210920012003416f6a22073602042001200441116a3602002007450d002004290009210a20012003416e6a3602042001200441126a36020020042d0011220141034f0d00200020013a00102000200a370308200020093703000c020b200041053a00100c010b2001200128020441016a22033602040240200320012802084b0d0041002d00fca3c680001a41980241002802c8a3c68000118180808000002205450d02200241086a2001108f8d80800002402002280290022203418080808078460d002005200241086a41880210848e80800022064194026a200241086a4194026a280200360200200620022902940237028c02200620033602880220012001280204417f6a3602040240200128020022012802042203450d0020012003417f6a3602042001200128020041016a3602000b024020064198016a2802002207450d0020064194016a280200220821014100210303402008200341146c6a21040240024002400240024020012d00000e0400010102040b200141086a21040c020b200441086a21040c010b200441046a21040b2004280200450d00200428020441002802c0a3c68000118080808000000b200341016a2103200141146a21012007417f6a22070d000b0b0240200628029001450d0020062802940141002802c0a3c68000118080808000000b024020064190026a2802002207450d00200628028c02220821014100210303402008200341146c6a21040240024002400240024020012d00000e0400010102040b200141086a21040c020b200441086a21040c010b200441046a21040b2004280200450d00200428020441002802c0a3c68000118080808000000b200341016a2103200141146a21012007417f6a22070d000b0b200628028802450d002006418c026a28020041002802c0a3c68000118080808000000b200541002802c0a3c68000118080808000000b200041053a00100b200241a0026a2480808080000f0b410841980210b280808000000bb60702047f017e23808080800041106b2202248080808000024002400240024020002d0010417d6a41ff0171220341016a410320034102491b417f6a0e03000102030b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41003a00002001200341016a22033602082000280200210002402001280200220420036b411f4b0d0020012003412010b18280800020012802002104200128020821030b200128020420036a22052000290000370000200541186a200041186a290000370000200541106a200041106a290000370000200541086a200041086a2900003700002001200341206a2203360208200029032021060240200420036b41074b0d0020012003410810b182808000200128020821030b2001200341086a360208200128020420036a2006370000200041286a200110dd8c808000200041a0016a200110dd8c808000200241046a1095888080002002280208210502402001280200200128020822006b200228020c22034f0d0020012000200310b182808000200128020821000b200128020420006a2005200310848e8080001a2001200020036a3602082002280204450d02200541002802c0a3c68000118080808000000c020b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41013a00002001200341016a22033602082000280200210002402001280200220420036b411f4b0d0020012003412010b18280800020012802002104200128020821030b200128020420036a22052000290000370000200541186a200041186a290000370000200541106a200041106a290000370000200541086a200041086a2900003700002001200341206a2203360208200029032021060240200420036b41074b0d0020012003410810b182808000200128020821030b2001200341086a360208200128020420036a2006370000200041286a200110dd8c808000200041a0016a200110dd8c808000200241046a1095888080002002280208210502402001280200200128020822006b200228020c22034f0d0020012000200310b182808000200128020821000b200128020420006a2005200310848e8080001a2001200020036a3602082002280204450d01200541002802c0a3c68000118080808000000c010b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41023a00002000200110b08c8080000b200241106a2480808080000b830402037f017e4101210141002102024002400240024020002d0010417d6a41ff0171220341016a410320034102491b417f6a0e03000102030b410121034101210102402000280200220241c8006a290300220442c000540d0041022101200442808001540d00410421012004428080808004540d004109200479a74103766b21010b20024198016a28020021000240200241c0016a290300220442c000540d0041022103200442808001540d00410421032004428080808004540d004109200479a74103766b21030b417f417f417f200041146c20016a41046a220020002001491b220041286a220120012000491b2200417f20024190026a28020041146c20036a41046a220220022003491b6a220220022000491b21020c020b02402000280200220241c8006a290300220442c000540d0041022101200442808001540d00410421012004428080808004540d004109200479a74103766b21010b20024198016a2802002103410121000240200241c0016a290300220442c000540d0041022100200442808001540d00410421002004428080808004540d004109200479a74103766b21000b417f417f417f200341146c20016a41046a220320032001491b220341286a220120012003491b2203417f20024190026a28020041146c20006a41046a220220022000491b6a220220022003491b21020c010b411221020b200241016a0b9c0601077f23808080800041d0006b2203248080808000200220016b220441286e210502400240024020022001460d0002400240200441d8ffffff794b0d0020054105742202417f4c0d004100210641002d00fca3c680001a200241002802c8a3c68000118180808000002207450d012005410371210802402005417f6a4103490d00200541fcffff3f712109410021062007210220012104034020022004290000370000200241186a200441186a290000370000200241106a200441106a290000370000200241086a200441086a290000370000200241386a200441c0006a290000370000200241306a200441386a290000370000200241286a200441306a290000370000200241206a200441286a290000370000200241d8006a200441e8006a290000370000200241d0006a200441e0006a290000370000200241c8006a200441d8006a290000370000200241c0006a200441d0006a290000370000200241e0006a200441f8006a290000370000200241e8006a20044180016a290000370000200241f0006a20044188016a290000370000200241f8006a20044190016a29000037000020024180016a2102200441a0016a21042009200641046a2206470d000b0b02402008450d002001200641286c6a2102200720064105746a2104034020042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a290000370000200241286a2102200441206a21042008417f6a22080d000b0b20072005200341cf006a10dc8b80800041002d00fca3c680001a41e80241002802c8a3c68000118180808000002202450d04200241003b01e602200241003602e002200341003602102003200236020c20034100360214200341023a00282003200736021c20032007360218200320053602202003200720054105746a3602242003410c6a200341186a200341146a10c285808000200020032802143602082000200329020c3702000c030b10ae80808000000b4101200210b280808000000b20004100360208200041003602000b200341d0006a2480808080000f0b410441e80210b280808000000bbe0905017f017e017f017e017f23808080800041c0026b2204248080808000024002400240024020014180016a2d00004102460d00200441c8006a200141f0006a290000370300200441c0006a200141e8006a290000370300200441386a200141e0006a29000037030020042001290058370330200141f8006a290300210520044180026a200441306a10fa8780800041012106024002400240024020042802b40220042802b80272450d0020042903800222072005510d014103410220072005561b21060b410021080c010b2004200542017c22053703800220044188016a41386a20044180026a41386a29030037030020044188016a41306a20044180026a41306a29030037030020044188016a41286a20044180026a41286a29030037030020044188016a41206a20044180026a41206a29030037030020044188016a41186a20044180026a41186a29030037030020044188016a41106a20044180026a41106a29030037030020044188016a41086a20044180026a41086a2903003703002004200537038801200441306a20044188016a10fe8780800002402002200310d388808000220841ff01714102460d0020084180feff077141087621060c010b200441306a20012002200310dd89808000220841ff01714102460d0120084180feff077141087621060b200020083a000820004203370300200041096a20063b00000c030b200441e0016a41186a200441306a41186a290300370300200441e0016a41106a200441306a41106a290300370300200441e0016a41086a200441306a41086a290300370300200420042903303703e001410121060c010b02402002200310d388808000220341ff01714102460d00200020033a000820004203370300200041096a20034108763b00000c020b4100210602400240024002400240200128020041736a2203410220034104491b0e03000102050b200141086a108a8a80800022034180feff077141087621080c020b4100210341002108200141186a2d00004104470d02200128020810b087808000000b200110ea8780800022034180feff077141087621080b200341ff01714102460d010b200020033a000820004203370300200041096a20083b00000c010b200441186a200441e0016a41086a290300370000200441206a200441e0016a41106a290300370000200441286a200441e0016a41186a290300370000200420063a000f200420042903e00137001020044188016a200141d80010848e8080001a20044180026a2004410f6a10f289808000200441d0006a20044188016a20044180026a10f38980800020044188016a41086a200441d0006a41086a2203200441d0006a20042903504202511b220141086a29030037030020044188016a41106a200141106a29030037030020044188016a41186a200141186a2903003703002004200129030037038801200220044188016a10cd88808000200041306a200441d0006a41306a290300370300200041286a200441d0006a41286a290300370300200041206a200441d0006a41206a290300370300200041186a200441d0006a41186a290300370300200041106a200441d0006a41106a290300370300200041086a2003290300370300200020042903503703000c010b200110bf878080000b200441c0026a2480808080000bef0901057f0240024002400240024002402000280200220141736a2202410220024104491b0e03010203000b20002d00084106470d042000410c6a280200450d04200041106a28020021030c030b0240024002400240024002400240024020002d0008417f6a0e0a010b0203040506070b0b000b2000410c6a280200450d0a200041106a28020021030c090b2000410c6a280200450d09200041106a28020021030c080b2000410c6a280200450d08200041106a28020021030c070b2000410c6a280200450d07200041106a28020021030c060b200041106a28020021030240200041146a2802002201450d0020032102034002402002280200450d00200241046a28020041002802c0a3c68000118080808000000b02402002410c6a280200450d00200241106a28020041002802c0a3c68000118080808000000b200241186a21022001417f6a22010d000b0b200028020c450d060c050b200041106a28020021030240200041146a2802002202450d002002410171210441002101024020024101460d002002417e7121052003210241002101034002402002280200450d00200241046a28020041002802c0a3c68000118080808000000b02402002410c6a280200450d00200241106a28020041002802c0a3c68000118080808000000b200241186a21022005200141026a2201470d000b0b2004450d0020032001410c6c6a2202280200450d00200228020441002802c0a3c68000118080808000000b200028020c0d040c050b200041106a280200450d04200041146a28020021030c030b2000410c6a280200450d03200041106a28020021030c020b200041186a2d0000417d6a41ff017141014b0d020240200028020822034198016a2802002205450d0020034194016a280200220421004100210203402004200241146c6a21010240024002400240024020002d00000e0400010102040b200041086a21010c020b200141086a21010c010b200141046a21010b2001280200450d00200128020441002802c0a3c68000118080808000000b200241016a2102200041146a21002005417f6a22050d000b0b0240200328029001450d0020032802940141002802c0a3c68000118080808000000b024020034190026a2802002205450d002003418c026a280200220421004100210203402004200241146c6a21010240024002400240024020002d00000e0400010102040b200041086a21010c020b200141086a21010c010b200141046a21010b2001280200450d00200128020441002802c0a3c68000118080808000000b200241016a2102200041146a21002005417f6a22050d000b0b200328028802450d01200328028c0241002802c0a3c68000118080808000000c010b024002400240024002400240024002402001417e6a0e06000102030405090b200041046a21000c050b02402000280210450d00200041146a28020041002802c0a3c68000118080808000000b20002802042202418080808078460d07200041046a21000c050b02402000280204450d00200041086a28020041002802c0a3c68000118080808000000b200041106a21000c030b200041046a21000c020b200041046a21000c010b024002400240024020002d00040e0400010203070b2000410c6a21000c030b2000410c6a21000c020b2000410c6a21000c010b200041086a21000b200028020021020b2002450d01200028020421030b200341002802c0a3c68000118080808000000f0b0bca0704067f017e017f017e2380808080004180016b22052480808080000240024020014180016a2d00004102460d002000200141f8006a200141d8006a20012003200410da888080000c010b200541d0006a20012003200410d688808000024020052802602203418080808078460d00200541086a2206200541d0006a41086a22072903003703002005200529035037030020052802642108200528026c21092005280270210a2005290378210b200528026821042005280274210c200541d0006a2002200110f989808000024020052802602201418080808078460d00200541106a41086a2007290300220d370300200541206a411c6a200541d0006a411c6a290200370200200541206a41246a200541d0006a41246a290200370200200541206a412c6a200541d0006a412c6a280200360200200541206a41086a200d370300200520052902643702342005200529035037032020052001360230200720062903003703002005200b3703782005200c3602742005200a3602702005200936026c200520043602682005200836026420052003360260200520052903003703502000200541d0006a200541206a10fd848080000c020b200520052d005222013a0012200520052f015022023b0110200041026a20013a0000200020023b0100200041808080807836021002402004450d002004410171210241002100024020044101460d002004417e7121042008210141002100034002402001280200450d00200141046a28020041002802c0a3c68000118080808000000b02402001410c6a280200450d00200141106a28020041002802c0a3c68000118080808000000b200141186a21012004200041026a2200470d000b0b2002450d0020082000410c6c6a2201280200450d00200128020441002802c0a3c68000118080808000000b02402003450d00200841002802c0a3c68000118080808000000b0240200c450d00200c4101712103410021000240200c4101460d00200c417e712104200a210141002100034002402001280200450d00200141046a28020041002802c0a3c68000118080808000000b02402001410c6a280200450d00200141106a28020041002802c0a3c68000118080808000000b200141186a21012004200041026a2200470d000b0b2003450d00200a2000410c6c6a2201280200450d00200128020441002802c0a3c68000118080808000000b2009450d01200a41002802c0a3c68000118080808000000c010b200520052d005222013a0012200520052f015022043b0110200041026a20013a0000200020043b010020004180808080783602100b20054180016a2480808080000bcb0601057f23808080800041106b22032480808080000240024002402001280204220441feffffff074b0d00200141086a2802000d02200320012802001180808080000002402001280204450d0041908cc38000108781808000000b2001417f36020420012802080d01200141013602082001410c6a2003290200370200200141146a200341086a290200370200410021040c020b41a08cc38000108881808000000b0240200141186a2802002205450d00200141146a28020021040340200428020022062006280200417f6a2207360200024020070d00200641046a22072007280200417f6a220736020020070d00200641002802c0a3c68000118080808000000b200441046a21042005417f6a22050d000b0b02402001280210450d00200128021441002802c0a3c68000118080808000000b200141013602082001410c6a2003290200370200200141146a200341086a2902003702002001200128020441016a2204360204200441ffffffff07490d0041808cc38000108881808000000b2001200441016a36020402400240024002402001410c6a280200220441feffffff074b0d0002400240200141186a280200450d002000200210ab878080000c010b20040d042001417f36020c2002280250280200210641002d00fca3c680001a411041002802c8a3c68000118180808000002204450d022004200636020c2004410036020820044281808080103702000240200128021822062001280210470d00200141106a2006109b86808000200128021821060b200141146a28020020064102746a20043602002001200128021841016a3602182001200128020c41016a36020c2000200210ab87808000200128020c0d032001417f36020c02400240200128021822040d00410021040c010b20012004417f6a2204360218200128021420044102746a28020022042004280200417f6a2206360200024020060d00200441046a22062006280200417f6a220636020020060d00200441002802c0a3c68000118080808000000b200128020c41016a21040b2001200436020c0b20012001280204417f6a360204200341106a2480808080000f0b41888bc38000108881808000000b4104411010b280808000000b41ec8fc38000108781808000000b41f88ac38000108781808000000bb60e02087f017e2380808080004190016b22032480808080000240024002402001280204220441feffffff074b0d00200141086a2802000d02200341d0006a20012802001180808080000002402001280204450d0041908cc38000108781808000000b2001417f36020420012802080d01200141013602082001410c6a2003290250370200200141146a200341d8006a290200370200410021040c020b41a08cc38000108881808000000b0240200141186a2802002205450d00200141146a28020021040340200428020022062006280200417f6a2207360200024020070d00200641046a22072007280200417f6a220736020020070d00200641002802c0a3c68000118080808000000b200441046a21042005417f6a22050d000b0b02402001280210450d00200128021441002802c0a3c68000118080808000000b200141013602082001410c6a2003290250370200200141146a200341d8006a2902003702002001200128020441016a2204360204200441ffffffff07490d0041808cc38000108881808000000b2001200441016a36020402400240024002402001410c6a280200220441feffffff074b0d000240024002400240200141186a280200450d00024020022d00382206417d6a41ff0171220441016a410320044102491b22044103460d002004417f6a0e020202020b200341186a41286a22042002290328370300200341186a41306a2205200241306a290300370300200341d0006a413c6a2002413c6a2800003600002003200228003936008901200341186a41086a2207200241086a290300370300200341186a41106a2208200241106a290300370300200341186a41186a2209200241186a290300370300200341186a41206a220a200241206a29030037030020032002290300370318200341d0006a41086a2007290300370300200341d0006a41106a2008290300370300200341d0006a41186a2009290300370300200341d0006a41206a200a290300370300200341d0006a41286a2004290300370300200341d0006a41306a200529030037030020032003290318370350200320063a008801200341046a200341d0006a10b6868080000240024020032d0004410e470d0020004200370308200041206a21044202210b0c010b20002003290204370220200041286a200341046a41086a290200370200200341d2006a200341176a2d000022043a0000200320032f001522063b015020032d00142102200041336a20043a0000200041316a20063b0000200041306a20023a0000200041186a21044200210b0b2000200b370300200441003a00000c030b20040d062001417f36020c2002280240280200210641002d00fca3c680001a411041002802c8a3c68000118180808000002204450d042004200636020c2004410036020820044281808080103702000240200128021822062001280210470d00200141106a2006109b86808000200128021821060b200141146a28020020064102746a20043602002001200128021841016a3602182001200128020c41016a36020c20022d00382206417d6a41ff0171220441016a410320044102491b22044103460d012004417f6a0e020000000b00000b200341186a41286a22042002290328370300200341186a41306a2205200241306a290300370300200341d0006a413c6a2002413c6a2800003600002003200228003936008901200341186a41086a2207200241086a290300370300200341186a41106a2208200241106a290300370300200341186a41186a2209200241186a290300370300200341186a41206a220a200241206a29030037030020032002290300370318200341d0006a41086a2007290300370300200341d0006a41106a2008290300370300200341d0006a41186a2009290300370300200341d0006a41206a200a290300370300200341d0006a41286a2004290300370300200341d0006a41306a200529030037030020032003290318370350200320063a008801200341046a200341d0006a10b6868080000240024020032d0004410e470d0020004200370308200041206a21064202210b0c010b20002003290204370220200041286a200341046a41086a290200370200200341d2006a200341176a2d000022043a0000200320032f001522063b015020032d00142102200041336a20043a0000200041316a20063b0000200041306a20023a0000200041186a21064200210b0b2000200b37030041002104200641003a0000200128020c0d032001417f36020c024020012802182206450d0020012006417f6a2204360218200128021420044102746a28020022042004280200417f6a2206360200024020060d00200441046a22062006280200417f6a220636020020060d00200441002802c0a3c68000118080808000000b200128020c41016a21040b2001200436020c0b20012001280204417f6a36020420034190016a2480808080000f0b41888bc38000108881808000000b4104411010b280808000000b41ec8fc38000108781808000000b41f88ac38000108781808000000bcc0601057f23808080800041106b22032480808080000240024002402001280204220441feffffff074b0d00200141086a2802000d02200320012802001180808080000002402001280204450d0041908cc38000108781808000000b2001417f36020420012802080d01200141013602082001410c6a2003290200370200200141146a200341086a290200370200410021040c020b41a08cc38000108881808000000b0240200141186a2802002205450d00200141146a28020021040340200428020022062006280200417f6a2207360200024020070d00200641046a22072007280200417f6a220736020020070d00200641002802c0a3c68000118080808000000b200441046a21042005417f6a22050d000b0b02402001280210450d00200128021441002802c0a3c68000118080808000000b200141013602082001410c6a2003290200370200200141146a200341086a2902003702002001200128020441016a2204360204200441ffffffff07490d0041808cc38000108881808000000b2001200441016a36020402400240024002402001410c6a280200220441feffffff074b0d0002400240200141186a280200450d002000200210c4878080000c010b20040d042001417f36020c200228028001280200210641002d00fca3c680001a411041002802c8a3c68000118180808000002204450d022004200636020c2004410036020820044281808080103702000240200128021822062001280210470d00200141106a2006109b86808000200128021821060b200141146a28020020064102746a20043602002001200128021841016a3602182001200128020c41016a36020c2000200210c487808000200128020c0d032001417f36020c02400240200128021822040d00410021040c010b20012004417f6a2204360218200128021420044102746a28020022042004280200417f6a2206360200024020060d00200441046a22062006280200417f6a220636020020060d00200441002802c0a3c68000118080808000000b200128020c41016a21040b2001200436020c0b20012001280204417f6a360204200341106a2480808080000f0b41888bc38000108881808000000b4104411010b280808000000b41ec8fc38000108781808000000b41f88ac38000108781808000000bf21e02057f017e23808080800041a0016b220224808080800002400240024002400240024002400240024002400240024002402001280200417f6a0e0c000102030405060708090a0b000b2001280258210141092103024010fa81808000220441fe014b0d002002200441016a36027041b1e3c080004113200241f0006a410441002802e0a1c680001186808080000041002802e8a1c680001188808080000041002802b0a1c6800011888080800000200241d8006a10fb81808000410e21030b20012001280200417f6a2205360200024020050d00200141086a28020022062001410c6a28020022052802001180808080000002402005280204450d00200641002802c0a3c68000118080808000000b200141046a22052005280200417f6a220536020020050d00200141002802c0a3c68000118080808000000b02400240200441fe014b0d0020004200370308200041206a2101420221070c010b200020033a0020200041216a41003a0000200041186a2101420021070b20002007370300200141003a00000c0b0b200141086a28020021032001280204210420024190016a200141f8006a29030037030020024188016a200141f0006a29030037030020024180016a200141e8006a290300370300200241f0006a41086a200141e0006a29030037030020022001290358370370200241d8006a200241f0006a10b48680800002402004450d00200341002802c0a3c68000118080808000000b0240024020022d0058410e470d0020004200370308200041206a2101420221070c010b20002002290258370220200041286a200241e0006a290200370200200241f2006a200241eb006a2d000022013a0000200220022f006922043b017020022d00682103200041336a20013a0000200041316a20043b0000200041306a20033a0000200041186a2101420021070b20002007370300200141003a00000c0a0b200241086a41086a200141186a2802003602002002200129021037030820012802582104200241d8006a41086a2001410c6a280200360200200220012902043703582002200241086a360264200241f0006a200241d8006a10c98680800002402002280208450d00200228020c41002802c0a3c68000118080808000000b20042004280200417f6a2201360200024020010d00200441086a28020022032004410c6a28020022012802001180808080000002402001280204450d00200341002802c0a3c68000118080808000000b200441046a22012001280200417f6a220136020020010d00200441002802c0a3c68000118080808000000b0240024020022d0070410e470d0020004200370308200041206a2101420221070c010b20002002290270370220200041286a200241f8006a290200370200200241da006a20024183016a2d000022013a0000200220022f00810122043b015820022d0080012103200041336a20013a0000200041316a20043b0000200041306a20033a0000200041186a2101420021070b20002007370300200141003a00000c090b200241186a41086a2001410c6a280200360200200241286a41086a200141186a280200360200200220012902043703182002200129021037032820024190016a200141f8006a290300370300200241f0006a41186a200141f0006a29030037030020024180016a200141e8006a290300370300200241f0006a41086a200141e0006a290300370300200220012903583703702002200241286a36029c012002200241186a36029801200241d8006a200241f0006a10bf8680800002402002280228450d00200228022c41002802c0a3c68000118080808000000b02402002280218450d00200228021c41002802c0a3c68000118080808000000b0240024020022d0058410e470d0020004200370308200041206a2101420221070c010b20002002290258370220200041286a200241e0006a290200370200200241f2006a200241eb006a2d000022013a0000200220022f006922043b017020022d00682103200041336a20013a0000200041316a20043b0000200041306a20033a0000200041186a2101420021070b20002007370300200141003a00000c080b200241386a41086a2001410c6a2802003602002002200129020437033820024190016a200141f8006a29030037030020024188016a200141f0006a29030037030020024180016a200141e8006a290300370300200241f0006a41086a200141e0006a290300370300200220012903583703702002200241386a36029801200241d8006a200241f0006a10c08680800002402002280238450d00200228023c41002802c0a3c68000118080808000000b0240024020022d0058410e470d0020004200370308200041206a2101420221070c010b20002002290258370220200041286a200241e0006a290200370200200241f2006a200241eb006a2d000022013a0000200220022f006922043b017020022d00682103200041336a20013a0000200041316a20043b0000200041306a20033a0000200041186a2101420021070b20002007370300200141003a00000c070b200241c8006a41086a2001410c6a2802003602002002200129020437034820024190016a200141f8006a29030037030020024188016a200141f0006a29030037030020024180016a200141e8006a290300370300200241f0006a41086a200141e0006a290300370300200220012903583703702002200241c8006a36029801200241d8006a200241f0006a10b88680800002402002280248450d00200228024c41002802c0a3c68000118080808000000b0240024020022d0058410e470d0020004200370308200041206a2101420221070c010b20002002290258370220200041286a200241e0006a290200370200200241f2006a200241eb006a2d000022013a0000200220022f006922043b017020022d00682103200041336a20013a0000200041316a20043b0000200041306a20033a0000200041186a2101420021070b20002007370300200141003a00000c060b200241f0006a41206a200141f8006a290200370300200241f0006a41186a200141f0006a29020037030020024180016a200141e8006a290200370300200241f0006a41086a200141e0006a29020037030020022001290258370370200241d8006a200241f0006a200141046a10b6888080000240024020022d0058410e470d0020004200370308200041206a2101420221070c010b20002002290258370220200041286a200241d8006a41086a290200370200200241f2006a200241eb006a2d000022013a0000200220022f006922043b017020022d00682103200041336a20013a0000200041316a20043b0000200041306a20033a0000200041186a2101420021070b20002007370300200141003a00000c050b2001280258210141092103024010fa81808000220441fe014b0d002002200441016a36027041b1e3c080004113200241f0006a410441002802e0a1c680001186808080000041002802e8a1c680001188808080000041002802b0a1c6800011888080800000200241d8006a10fb81808000410e21030b20012001280200417f6a2205360200024020050d00200141086a28020022062001410c6a28020022052802001180808080000002402005280204450d00200641002802c0a3c68000118080808000000b200141046a22052005280200417f6a220536020020050d00200141002802c0a3c68000118080808000000b02400240200441fe014b0d0020004200370308200041206a2101420221070c010b200020033a0020200041216a41003a0000200041186a2101420021070b20002007370300200141003a00000c040b2001280258210141092103024010fa81808000220441fe014b0d002002200441016a36027041b1e3c080004113200241f0006a410441002802e0a1c680001186808080000041002802e8a1c680001188808080000041002802b0a1c6800011888080800000200241d8006a10fb81808000410e21030b20012001280200417f6a2205360200024020050d00200141086a28020022062001410c6a28020022052802001180808080000002402005280204450d00200641002802c0a3c68000118080808000000b200141046a22052005280200417f6a220536020020050d00200141002802c0a3c68000118080808000000b02400240200441fe014b0d0020004200370308200041206a2101420221070c010b200020033a0020200041216a41003a0000200041186a2101420021070b20002007370300200141003a00000c030b200241f0006a41206a200141f8006a290300370300200241f0006a41186a200141f0006a29030037030020024180016a200141e8006a290300370300200241f0006a41086a200141e0006a29030037030020022001290358370370200241d8006a200241f0006a10b4868080000240024020022d0058410e470d0020004200370308200041206a2101420221070c010b20002002290258370220200041286a200241d8006a41086a290200370200200241f2006a200241eb006a2d000022013a0000200220022f006922043b017020022d00682103200041336a20013a0000200041316a20043b0000200041306a20033a0000200041186a2101420021070b20002007370300200141003a00000c020b20012802042104200241f0006a41206a200141f8006a290200370300200241f0006a41186a200141f0006a29020037030020024180016a200141e8006a290200370300200241f0006a41086a200141e0006a29020037030020022001290258370370200241d8006a200241f0006a200410b7888080000240024020022d0058410e470d0020004200370308200041206a2101420221070c010b20002002290258370220200041286a200241d8006a41086a290200370200200241f2006a200241eb006a2d000022013a0000200220022f006922043b017020022d00682103200041336a20013a0000200041316a20043b0000200041306a20033a0000200041186a2101420021070b20002007370300200141003a00000c010b20012802042104200241f0006a41206a200141f8006a290200370300200241f0006a41186a200141f0006a29020037030020024180016a200141e8006a290200370300200241f0006a41086a200141e0006a29020037030020022001290258370370200241d8006a200241f0006a200410b9888080000240024020022d0058410e470d0020004200370308200041206a2101420221070c010b20002002290258370220200041286a200241d8006a41086a290200370200200241f2006a200241eb006a2d000022013a0000200220022f006922043b017020022d00682103200041336a20013a0000200041316a20043b0000200041306a20033a0000200041186a2101420021070b20002007370300200141003a00000b200241a0016a2480808080000bcb0601057f23808080800041106b22032480808080000240024002402001280204220441feffffff074b0d00200141086a2802000d02200320012802001180808080000002402001280204450d0041908cc38000108781808000000b2001417f36020420012802080d01200141013602082001410c6a2003290200370200200141146a200341086a290200370200410021040c020b41a08cc38000108881808000000b0240200141186a2802002205450d00200141146a28020021040340200428020022062006280200417f6a2207360200024020070d00200641046a22072007280200417f6a220736020020070d00200641002802c0a3c68000118080808000000b200441046a21042005417f6a22050d000b0b02402001280210450d00200128021441002802c0a3c68000118080808000000b200141013602082001410c6a2003290200370200200141146a200341086a2902003702002001200128020441016a2204360204200441ffffffff07490d0041808cc38000108881808000000b2001200441016a36020402400240024002402001410c6a280200220441feffffff074b0d0002400240200141186a280200450d002000200210af878080000c010b20040d042001417f36020c2002280278280200210641002d00fca3c680001a411041002802c8a3c68000118180808000002204450d022004200636020c2004410036020820044281808080103702000240200128021822062001280210470d00200141106a2006109b86808000200128021821060b200141146a28020020064102746a20043602002001200128021841016a3602182001200128020c41016a36020c2000200210af87808000200128020c0d032001417f36020c02400240200128021822040d00410021040c010b20012004417f6a2204360218200128021420044102746a28020022042004280200417f6a2206360200024020060d00200441046a22062006280200417f6a220636020020060d00200441002802c0a3c68000118080808000000b200128020c41016a21040b2001200436020c0b20012001280204417f6a360204200341106a2480808080000f0b41888bc38000108881808000000b4104411010b280808000000b41ec8fc38000108781808000000b41f88ac38000108781808000000bd11d03057f047e067f2380808080004180026b220224808080800020012802002103200241386a4208370300200241306a4200370300200242808080801037032820024100360220200242808080808001370318200241023a0010200242043703082002420137030041002d00fca3c680001a02400240024002400240024041800141002802c8a3c68000118180808000002204450d00200241003602f801200220043602f40120024180013602f0012002200241f0016a3602d00102402002200241d0016a10fb898080002205450d0020022802f001450d0620022802f40141002802c0a3c68000118080808000000c060b20022802f401210520022802f0012206418080808078460d0502402003418180808078470d00200020022802f80136020820002005360204200020063602000c050b024002402003418080808078470d00200241d4006a41086a200141086a2802002203360200200241003602542002200128020422013602580c010b200241d4006a2001280204200128020810fc8080800020022802540d03200241dc006a2802002103200228025821010b0240024002400240024002400240024002400240024002400240024002402003417a6a0e020100110b200141b395c38000410710888e8080000d1041002d00fca3c680001a41c00041002802c8a3c68000118180808000002201450d0d200141386a4100290087a3c28000370000200141306a41002900ffa2c28000370000200141286a41002900f7a2c28000370000200141002900efa2c28000370020200141002900cfa2c28000370000200141086a41002900d7a2c28000370000200141106a41002900dfa2c28000370000200141186a41002900e7a2c28000370000200241003602742002410036026c41002d00fca3c680001a410841002802c8a3c68000118180808000002203450d0c200342e2c2b18be6edd8b2f300370000200241083602a801200220033602a401200241083602a001200241003602b401200241003602ac0141002d00fca3c680001a410841002802c8a3c68000118180808000002203450d0b200342e2c2b18be6edd8b2f300370000200241083602cc01200220033602c801200241083602c40141002d00fca3c680001a024041d00041002802c8a3c68000118180808000002203450d00200141086a2900002107200141106a2900002108200141186a29000021092001290000210a20034280809aa6eaafe301370320200341186a2009370000200341106a2008370000200341086a20073700002003200a370000200141206a220441086a2900002107200441106a2900002108200441186a290000210920032004290000370028200341c8006a4280809aa6eaafe301370300200341c0006a2009370000200341386a2008370000200341306a2007370000200141002802c0a3c6800011808080800000200241023602ec01200220033602e801200241023602e401200241f0016a200241e4016a10d28680800020022d00f0014106460d10200241d0016a41086a200241f0016a41086a290200370300200220022902f0013703d001200241f0016a200241ac016a200241c4016a200241d0016a10968d808000024020022d00f0014106460d00200241f0016a10c7878080000b024020022802e401450d0020022802e80141002802c0a3c68000118080808000000b200241dc016a200241b4016a280200360200200220022902ac013702d401200241053a00d001200241f0016a200241ec006a200241a0016a200241d0016a10968d808000024020022d00f0014106460d00200241f0016a10c7878080000b41002d00fca3c680001a410d41002802c8a3c68000118180808000002201450d08200141056a41002900d796c38000370000200141002900d296c380003700002002410d36028c0120022001360288012002410d36028401200241003602a801200241003602a00141002d00fca3c680001a410b41002802c8a3c68000118180808000002201450d07200141076a41002800f989c38000360000200141002900f289c380003700002002410b3602b401200220013602b0012002410b3602ac0141002d00fca3c680001a412041002802c8a3c68000118180808000002201450d06200241c4016a41afa2c2800041002f0194a1c6800010c8878080004101210b20022802c801210c41012104024020022802cc012203450d002003417f4c0d0641002d00fca3c680001a200341002802c8a3c68000118180808000002204450d050b2004200c200310848e808000210d200241e4016a418fa3c2800041002f0194a1c6800010c88780800020022802e801210e024020022802ec012204450d002004417f4c0d0641002d00fca3c680001a200441002802c8a3c6800011818080800000220b450d040b200b200e200410848e808000210b2001411c6a2004360200200141186a200b360200200141146a2004360200200141033a00102001200336020c2001200d36020820012003360204200141033a0000200241fc016a4102360200200241f8016a2001360200200241023602f401200241043a00f001200241d0016a200241a0016a200241ac016a200241f0016a10968d808000024020022d00d0014106460d00200241d0016a10c7878080000b024020022802e401450d00200e41002802c0a3c68000118080808000000b024020022802c401450d00200c41002802c0a3c68000118080808000000b200241f0016a410c6a200241a0016a41086a280200360200200220022902a0013702f401200241053a00f001200241d0016a200241ec006a20024184016a200241f0016a10968d808000024020022d00d0014106460d00200241d0016a10c7878080000b200241c4006a410c6a200241ec006a41086a2802003602002002200229026c370248200241053a00440c020b410841d00010b280808000000b200141ba95c38000410610888e8080000d0f200241003602cc01200241003602c40141002d00fca3c680001a410341002802c8a3c68000118180808000002201450d09200141026a41002d00c295c380003a0000200141002f00c095c380003b0000200241033602ec01200220013602e801200241033602e40141002d00fca3c680001a410341002802c8a3c68000118180808000002201450d08200141026a41002d00c595c380003a0000200141002f00c395c380003b0000200241033602fc01200220013602f801200241033602f401200241033a00f001200241d0016a200241c4016a200241e4016a200241f0016a10968d808000024020022d00d0014106460d00200241d0016a10c7878080000b200241d0006a200241cc016a280200360200200220022902c401370248200241053a00440b41002d00fca3c680001a41800141002802c8a3c68000118180808000002201450d06200220013602f40120024180013602f0012002200241f0016a3602e401200141fb003a0000200241013602f80141800221010240200241c4006a410c6a28020022030d0041012101024020022802f0014101470d00200241f0016a4101410110ab8680800020022802f80121010b20022802f40120016a41fd003a00002002200141016a3602f801410021010b200220013602d00141002101200341002002280248220c1b210e200c410047210f2002200241e4016a3602d401200241c4006a41086a2802002104024003400240024002400240200e450d0002400240200f450d002001450d010b200f0d0441f887c6800010a081808000000b4101210f200c21012004450d022004210302402004410771220c450d0003402003417f6a210320012802bc022101200c417f6a220c0d000b0b200441084f0d010c020b024020022802d001220141ff01710d00024020014180fe0371450d00024020022802d401280200220128020020012802082203470d0020012003410110ab86808000200128020821030b200128020420036a41fd003a00002001200341016a3602080b20022802f401210320022802f0012201418080808078460d05200020022802f8013602082000200336020420002001360200200241c4006a10c7878080000c150b41e493c38000412841ec94c3800010f880808000000b034020012802bc022802bc022802bc022802bc022802bc022802bc022802bc022802bc022101200341786a22030d000b0b410021044100210c0b024002400240200420012f01ba02490d00034020012802b0012203450d02200c41016a210c20012f01b802210420032101200420032f01ba024f0d000b0b2001210b2004220d41016a21040240200c0d00200b21010c020b200b20044102746a41bc026a280200210141002104200c417f6a2203450d01200c417e6a211002402003410771220c450d0003402003417f6a210320012802bc022101200c417f6a220c0d000b0b20104107490d01034020012802bc022802bc022802bc022802bc022802bc022802bc022802bc022802bc022101200341786a22030d000c020b0b41e887c6800010a081808000000b200e417f6a210e4100210c200241d0016a200b200d410c6c6a41b4016a200b200d4104746a10d7868080002203450d000b20022802f001450d0020022802f40141002802c0a3c68000118080808000000b200220033602f00141b08cc38000412f200241f0016a41e08cc3800041e096c38000108981808000000b4101200410b280808000000b4101200310b280808000000b10ae80808000000b4104412010b280808000000b4101410b10b280808000000b4101410d10b280808000000b410141800110b280808000000b4101410310b280808000000b4101410310b280808000000b4101410810b280808000000b4101410810b280808000000b410141c00010b280808000000b410141800110b280808000000b200220022802f4013602d00141c695c38000412b200241d0016a41e08cc3800041f096c38000108981808000000b20004180808080783602000b2006450d00200541002802c0a3c68000118080808000000b02402002280218450d00200228021c41002802c0a3c68000118080808000000b02402002280228450d00200228022c41002802c0a3c68000118080808000000b02402002280234450d00200228023841002802c0a3c68000118080808000000b20024180026a2480808080000f0b200220053602f00141b08cc38000412f200241f0016a41e08cc3800041e08dc38000108981808000000ba60201027f23808080800041306b22012480808080000240024002400240024020002d00000e050404010203000b02400240200028020422020d0041002100410021020c010b200120023602242001410036022020012002360214200141003602102001200041086a2802002202360228200120023602182000410c6a2802002102410121000b2001200236022c2001200036021c2001200036020c2001410c6a10aa8d8080000c030b2000280204450d02200041086a28020041002802c0a3c68000118080808000000c020b2000280204450d01200041086a28020041002802c0a3c68000118080808000000c010b200041046a1091878080002000280204450d00200041086a28020041002802c0a3c68000118080808000000b200141306a2480808080000b840501047f23808080800041c0006b220324808080800041002d00fca3c680001a41002802c8a3c680002104024002400240024002400240200241ffff00712205413f4b0d004101210641012004118180808000002204450d02200420023a00000c010b4102210641022004118180808000002204450d02200420024106742005410876723a00012004200241fc017141027641c000723a00000b200320063602182003200436021420032006360210200341106a2006412010ab8680800020032802142206200328021822046a22022001290000370000200241086a200141086a290000370000200241106a200141106a290000370000200241186a200141186a2900003700002003200441206a22013602182003411c6a2006200110e8838080002003280224220241014d0d022003280220210202402003280210220420016b41014b0d00200341106a2001410210ab868080002003280214210620032802102104200328021821010b200620016a20022f00003b00002003410036023020034280808080103702282003200341286a360234200320063602382003200141026a220536023c200341086a200341346a200141036a41017620056a200341386a41f091c3800010a9898080002003280208210102402004450d00200641002802c0a3c68000118080808000000b20010d0320002003290228370200200041086a200341286a41086a2802003602000240200328021c450d00200241002802c0a3c68000118080808000000b200341c0006a2480808080000f0b4101410110b280808000000b4101410210b280808000000b4102200241e091c38000109581808000000b41b4a1c38000412b200341386a41e0a1c3800041f0a1c38000108981808000000bc10501067f23808080800041b0016b22022480808080002001280204210320012802082104200241003602980120022004360294012002200336029001200241c0006a20024190016a10c58a808000024002400240024020022802742204418080808078470d0020022002280240360280012002419c016a4201370200410121052002410136029401200241848ec380003602900120024188828080003602ac012002200241a8016a36029801200220024180016a3602a80120024184016a20024190016a10b88080800020022802880121060240200228028c012204450d002004417f4c0d0341002d00fca3c680001a200441002802c8a3c68000118180808000002205450d040b20052006200410848e80800021070240200228028401450d00200641002802c0a3c68000118080808000000b024020022802800122052802000d00200541086a280200450d00200528020441002802c0a3c68000118080808000000b200541002802c0a3c68000118080808000002000200436020820002007360204200020043602000c010b200241146a200241c0006a41146a2902003702002002411c6a2205200241c0006a411c6a290200370200200241246a200241c0006a41246a2902003702002002412c6a200241c0006a412c6a2902003702002002200229024c37020c20022002290378370338200220043602342002200228024836020820022002290340370300200210f889808000200041818080807836020002402002280218450d00200528020041002802c0a3c68000118080808000000b02402002280228450d002002412c6a28020041002802c0a3c68000118080808000000b2002280234450d00200228023841002802c0a3c68000118080808000000b02402001280200450d00200341002802c0a3c68000118080808000000b200241b0016a2480808080000f0b10ae80808000000b4101200410b280808000000b4100200042919fd78da1a1ad84ff003700082000429ceccef7a6c0dedd20370000200042dca0bb8e8acbf9b3da00370018200042e78ec688edebaee7ba7f3700100b4000200042919fd78da1a1ad84ff003700082000429ceccef7a6c0dedd20370000200042aac0b0e5d1eaef8d63370018200042b8e2b0fbfb91a8ed827f3700100bae0301047f23808080800041106b2202248080808000200041c0006a210302402001280200200128020822046b411f4b0d0020012004412010b182808000200128020821040b200128020420046a22052003290000370000200541186a200341186a290000370000200541106a200341106a290000370000200541086a200341086a2900003700002001200441206a22033602080240200128020020036b413f4b0d002001200341c00010b182808000200128020821030b2001200341c0006a360208200128020420036a22032000290000370000200341086a200041086a290000370000200341106a200041106a290000370000200341186a200041186a290000370000200341206a200041206a290000370000200341286a200041286a290000370000200341306a200041306a290000370000200341386a200041386a2900003700002002200041e0006a36020c2002410c6a200110c18a808000200041e8006a2d000021030240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a20033a0000200241106a2480808080000b890301047f23808080800041206b2204248080808000024002402001417f4c0d0041002d00fca3c680001a2001410574410472220541002802c8a3c68000118180808000002206450d0120044100360214200420063602102004200536020c200420013602182004200441186a36021c2004411c6a2004410c6a10c08a8080000240024020010d0020042802142101200428021021070c010b200141057421062004280214210103400240200428020c20016b411f4b0d002004410c6a2001412010b182808000200428021421010b2004280210220720016a22052000290000370000200541086a200041086a290000370000200541106a200041106a290000370000200541186a200041186a2900003700002004200141206a2201360214200041206a2100200641606a22060d000b0b200428020c2100200220032007200141002802e0a1c680001186808080000002402000450d00200741002802c0a3c68000118080808000000b200441206a2480808080000f0b10ae80808000000b4101200510b280808000000baf0302057f017e23808080800041206b2203248080808000024002402002417f4c0d0041002d00fca3c680001a200241286c410472220441002802c8a3c68000118180808000002205450d0120034100360214200320053602102003200436020c200320023602182003200341186a36021c2003411c6a2003410c6a10c08a80800002402002450d002001200241286c6a21062003280214210203400240200328020c220520026b411f4b0d002003410c6a2002412010b182808000200328020c2105200328021421020b2003280210220720026a22042001290000370000200441186a200141186a290000370000200441106a200141106a290000370000200441086a200141086a2900003700002003200241206a2202360214200141206a29030021080240200520026b41074b0d002003410c6a2002410810b18280800020032802102107200328021421020b200720026a20083700002003200241086a2202360214200141286a22012006470d000b0b2000200329020c370200200041086a2003410c6a41086a280200360200200341206a2480808080000f0b10ae80808000000b4101200410b280808000000bc01404017f017e057f037e23808080800041900d6b2201248080808000200142919fd78da1a1ad84ff003703b80b2001429ceccef7a6c0dedd203703b00b200142d8d2dfcce2f8e593413703c80b200142faa5fa8ea9819ff1bd7f3703c00b200141a0086a200141b00b6a10e08880800002400240024020012903a00822024204510d00200141b00b6a412041002802a0a1c6800011848080800000200141a0056a200141a0086a41086a41f00010848e8080001a200141286a200141a0056a41f00010848e8080001a20024203510d00200120023703980120014198016a41086a200141286a41f00010848e8080001a20024201510d0020014188026a2802002103200142919fd78da1a1ad84ff003703a8082001429ceccef7a6c0dedd203703a00820014282fceae49dd9e2861d3703b808200142de8c84a1ecd0a6d30c3703b008200141a0056a200141a0086a10e2888080000240024020012802a0052204418080808078470d00410121050c010b20012802a4052106410021070240200320012802a8054f0d00200142919fd78da1a1ad84ff003703a8052001429ceccef7a6c0dedd203703a005200142c6e4a9b1aaa1afebf2003703b805200142fa82b1828b81b8f31e3703b0052006200341286c6a2103200141a0086a200141a0056a10e5888080000240024020012d00a0080d00200141b8056a4200370300200141b0056a4200370300200141a8056a4200370300200142003703a0050c010b200141b8056a200141b9086a290000370300200141b0056a200141b1086a290000370300200141a8056a200141a9086a290000370300200120012900a1083703a0050b200142919fd78da1a1ad84ff003703a8082001429ceccef7a6c0dedd203703a008200142e9c5fea9cdfbc4d26d3703b80820014286aaece2939beae4653703b008200141186a200141a0086a10e7888080002001290320210220012802182105200142919fd78da1a1ad84ff003703a8082001429ceccef7a6c0dedd203703a008200142aac0b0e5d1eaef8d633703b808200142b8e2b0fbfb91a8ed827f3703b008200141086a200141a0086a10e688808000200129031021082001280208210720012002420020051b3703e00320012008420020071b3703b00b200141a0086a41ad8ec28000410410a682808000200141a0086a41b18ec28000410b200141e0036a410810a782808000200141a0086a41bc8ec28000410d200141b00b6a410810a782808000200141a0086a41c98ec280004110200141a0056a412010a78280800020014190026a200141a0086a41d00110848e8080001a200141b00b6a41186a200341186a2900002202370300200141b00b6a41106a200341106a2900002208370300200141b00b6a41086a200341086a290000220937030020012003290000220a3703b00b200141a0056a41186a22032002370300200141a0056a41106a2008370300200141a0056a41086a20093703002001200a3703a005200141a0086a200141a0056a10b18180800002400240024020012802a008450d0020014189046a200141a0086a410d6a29000037000020014190046a200141b4086a290000370000200120012900a5083700810420012d00a408210520014198046a200141bc086a41880110848e8080001a200141e0036a41096a200141a0056a41096a290000370000200141e0036a41116a200141a0056a41116a290000370000200141e0036a41186a2003290000370000200120012900a1053700e103200120053a008004200120012d00a0053a00e00320012f01d803210320012d00da032105200141b00b6a20014190026a41c80110848e8080001a200120053a00fa0c200120033b01f80c200141a0086a200141e0016a200141e0036a200141b00b6a10c48a80800020012802a008450d010b410021070c010b200141a0056a200141a4086a41800310848e8080001a41012107200141a0086a41016a200141a0056a41a08ec38000411310c38a808000200141b80b6a200141a0086a41096a290000370300200141c00b6a200141a0086a41116a290000370300200141c80b6a200141b9086a290000370300200120012900a1083703b00b0b41002105034020014190026a20056a220341003a0000200341016a41003a0000200341026a41003a0000200341036a41003a0000200341046a41003a0000200541056a220541c801470d000b0b02402004450d00200641002802c0a3c68000118080808000000b20074521052007450d002001290398014200520d00200142919fd78da1a1ad84ff003703a8082001429ceccef7a6c0dedd203703a008200142958ed1e593cab9fca47f3703b808200142e6d0c3af83b9abdfff003703b0082001200141a0086a412010d08880800020012001280204410020012802001b3602880d20014190026a200141880d6a10f98780800002400240200128029802220341ff014b0d0002402003200128029002470d0020014190026a200310a18680800020012802980221030b20012802940220034105746a220520012903b00b370000200541086a200141b00b6a41086a290300370000200541106a200141b00b6a41106a290300370000200541186a200141b00b6a41186a2903003700002001200341016a36029802200141880d6a20014190026a1080888080000c010b200120012802880d41016a36028c0d41002d00fca3c680001a412041002802c8a3c68000118180808000002203450d03200320012903b00b370000200341186a200141b00b6a41186a290300370000200341106a200141b00b6a41106a290300370000200341086a200141b00b6a41086a290300370000200141013602a805200120033602a405200141013602a0052001418c0d6a200141a0056a108188808000200142919fd78da1a1ad84ff003703a8082001429ceccef7a6c0dedd203703a008200142958ed1e593cab9fca47f3703b808200142e6d0c3af83b9abdfff003703b0082001200128028c0d3602e003200141a0086a4120200141e0036a410441002802e0a1c68000118680808000000b0240200128029002450d0020012802940241002802c0a3c68000118080808000000b410021050b200142919fd78da1a1ad84ff003703a8082001429ceccef7a6c0dedd203703a0082001428fa194fdfdd0d0e2c5003703b808200142d0effddeadf1b688773703b008410121074100210441002d00fca3c680001a4101412120051b220641002802c8a3c68000118180808000002203450d02024020050d00200320012903b00b370001200341196a200141c80b6a290300370000200341116a200141c00b6a290300370000200341096a200141b80b6a29030037000041212107410121040b200320043a0000200141a0086a41202003200741002802e0a1c6800011868080800000200341002802c0a3c68000118080808000000b200142919fd78da1a1ad84ff003703a8082001429ceccef7a6c0dedd203703a008200142bdff9adbcf84adb29e7f3703b80820014283c69cb2f58af8c40f3703b008200141a0086a412041002802a0a1c6800011848080800000200141900d6a2480808080000f0b4101412010b280808000000b4101200610b280808000000bbb0d04037f017e037f057e23808080800041b0026b2202248080808000200242919fd78da1a1ad84ff003703282002429ceccef7a6c0dedd20370320200242d8d2dfcce2f8e59341370338200242faa5fa8ea9819ff1bd7f37033020024190016a200241206a10e08880800002402002290390014204520d0020024190026a10ee8880800020022802940221030240024020022802980222040d00420321050c010b200441146c210620032107024002400340024020072d00000d00200741016a280000210820022007410c6a29020037027c200841c28289aa04470d0020024190016a200241fc006a10ae8c80800020022903900122054203520d020b200741146a21072006416c6a22060d000b420321050c010b2002290398012109200241206a200241a0016a41d80010848e8080001a200229038002210a20022903f801210b0b200321074100210603402003200641146c6a21080240024002400240024020072d00000e0400010102040b200741086a21080c020b200841086a21080c010b200841046a21080b2008280200450d00200828020441002802c0a3c68000118080808000000b200641016a2106200741146a21072004417f6a22040d000b0b0240200228029002450d00200341002802c0a3c68000118080808000000b024020054203510d00200242919fd78da1a1ad84ff00370398012002429ceccef7a6c0dedd2037039001200242dca0bb8e8acbf9b3da003703a801200242e78ec688edebaee7ba7f3703a0012009200b20054201511b210c200241106a20024190016a10e788808000024002402002280210450d00200229031850450d010b200242919fd78da1a1ad84ff00370398012002429ceccef7a6c0dedd2037039001200242dca0bb8e8acbf9b3da003703a801200242e78ec688edebaee7ba7f3703a0012002200c3703900220024190016a412020024190026a410841002802e0a1c6800011868080800000200242919fd78da1a1ad84ff00370398012002429ceccef7a6c0dedd203703900120024282fceae49dd9e2861d3703a801200242de8c84a1ecd0a6d30c3703a00120024190026a20024190016a10e288808000200228029402210820022802980221042002280290022107200242919fd78da1a1ad84ff00370398022002429ceccef7a6c0dedd2037039002200242c6e4a9b1aaa1afebf2003703a802200242fa82b1828b81b8f31e3703a0024100200420074180808080784622061b21044108200820061b21084100200720061b210720024190016a20024190026a10e5888080000240024020022d0090010d00200241a8026a4200370300200241a0026a420037030020024190026a41086a420037030020024200370390020c010b200241a8026a200241a9016a290000370300200241a0026a200241a1016a29000037030020024190026a41086a20024199016a2900003703002002200229009101370390020b2002419c016a200436020020024190016a41086a200836020020024190016a41106a20022903900237030020024190016a41186a20024190026a41086a290300370300200241b0016a20024190026a41106a290300370300200241b8016a20024190026a41186a29030037030020022007360294012002410036029001200241fc006a41086a20024190016a10cc8680800020024180016a41c5003a0000200241818485920436027c200241fc006a10f5888080002002280290010d00200228029401450d0020022802980141002802c0a3c68000118080808000000b200242919fd78da1a1ad84ff00370398012002429ceccef7a6c0dedd2037039001200242e9c5fea9cdfbc4d26d3703a80120024286aaece2939beae4653703a001200220024190016a10e788808000200228020021072002290308210d200242919fd78da1a1ad84ff00370398012002429ceccef7a6c0dedd2037039001200242bdff9adbcf84adb29e7f3703a80120024283c69cb2f58af8c40f3703a00120024200200c200d42017c420120071b7d220d200d200c561b42ffffffff0f833703900220024190016a412020024190026a410841002802e0a1c6800011868080800000200242919fd78da1a1ad84ff00370398012002429ceccef7a6c0dedd2037039001200242e9c5fea9cdfbc4d26d3703a80120024286aaece2939beae4653703a0012002200c3703900220024190016a412020024190026a410841002802e0a1c68000118680808000000b20022009370398012002200537039001200241a0016a200241206a41d80010848e8080001a2002200a370380022002200b3703f80120024190016a10f288808000200110b88a8080000b2000420037030820004200370300200241b0026a2480808080000b21002001280214418c8ec380004114200141186a28020028020c118280808000000b040041000bb80201027f23808080800041206b2202248080808000200028020021002002200128021441e4e7c280004100200141186a28020028020c118280808000003a00142002200136021041012101200241013a00152002410036020c200220003602182002200041046a36021c2002410c6a200241186a41b88ec38000108d818080002002411c6a41c88ec38000108d81808000210020022d0014210302400240200028020022000d00200341ff017141004721010c010b200341ff01710d0020022802102103024020004101470d0020022d001541ff0171450d0020032d001c4104710d00410121012003280214418ca5c080004101200341186a28020028020c118280808000000d010b2003280214418da5c080004101200341186a28020028020c1182808080000021010b200241206a24808080800020010b2100200128021441e6acc28000410f200141186a28020028020c118280808000000bff0101027f23808080800041106b2202248080808000200220002802003602042001280214419c95c380004111200141186a28020028020c118280808000002100200241003a000d200220003a000c20022001360208200241086a41ad95c380004106200241046a418c95c38000108c81808000210320022d000c21000240024020022d000d0d00200041ff017141004721010c010b41012101200041ff01710d000240200328020022012d001c4104710d0020012802144187a5c080004102200128021828020c1182808080000021010c010b20012802144186a5c080004101200128021828020c1182808080000021010b200241106a24808080800020010b040041000b1200200141b38ec38000410210dd808080000b02000b02000b43000240200028020022002802000d00200041086a280200450d00200028020441002802c0a3c68000118080808000000b200041002802c0a3c68000118080808000000bdd0401027f23808080800041106b22022480808080000240024002400240024002400240024002400240024020002f01000e09000102030405060708000b200128021441d88ec380004109200141186a28020028020c1182808080000021010c090b200128021441e18ec380004109200141186a28020028020c1182808080000021010c080b2002200041026a3602002002200128021441ea8ec380004118200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a200241848fc38000108d81808000210120022d000c21000240200128020022030d00200041ff017141004721010c080b41012101200041ff01710d072002280208210020034101470d0620022d000d41ff0171450d0620002d001c4104710d06410121012000280214418ca5c080004101200041186a28020028020c11828080800000450d060c070b200128021441948fc38000410f200141186a28020028020c1182808080000021010c060b200128021441a38fc38000410d200141186a28020028020c1182808080000021010c050b200128021441b08fc38000410d200141186a28020028020c1182808080000021010c040b200128021441bd8fc38000410b200141186a28020028020c1182808080000021010c030b200128021441c88fc380004110200141186a28020028020c1182808080000021010c020b200128021441d88fc380004112200141186a28020028020c1182808080000021010c010b2000280214418da5c080004101200041186a28020028020c1182808080000021010b200241106a24808080800020010b960301047f23808080800041d0076b22032480808080002003200136028004200341003602fc03200320023602f8030240024020022802042201450d0020022001417f6a36020420022002280200220141016a36020020012d0000220141024b0d00200341e8056a200341f8036a10f58c80800020032802e805450d0020032802e805210420034184046a200341e8056a41047241e40110848e8080001a024020032802f8032205280204220641204f0d00200341e8056a10bf878080000c010b2005200641606a36020420052005280200220641206a36020020034194026a20034184046a41e40110848e8080001a2004450d00200341047220034194026a41e40110848e8080001a20034181026a200641186a290000370000200341f9016a200641106a290000370000200341f1016a200641086a290000370000200320062900003700e901200320013a00e80120032004360200024020022802040d002000200341900210848e8080001a0c020b20004100360200200310bf878080000c010b200041003602000b200341d0076a2480808080000baa03010a7f23808080800041d0006b22042480808080004100210541002106024002402001280200450d0020012802040d012001417f360204200141086a21060b02400240024002402001280210450d0020012802140d012001417f360214200141186a21050b200141246a21074100210820060d01410021090c020b41dc90c38000108781808000000b2006280204210a200628020021090b2007280200210b200128022821072001280220210c0240024020050d000c010b2005280204210d200528020021080b200441c0006a200d360200200441386a200a3602002004410c6a41106a200741086a2900003702002004410c6a41186a200741106a2900003702002004412c6a200741186a2900003702002004200b3602102004200c36020c2004200836023c20042009360234200420072900003702142004410036024c200420033602482004200236024420002004410c6a20022003200441c4006a10c68c80800002402005450d002001200128021441016a3602140b02402006450d002001200128020441016a3602040b200441d0006a2480808080000f0b41ec90c38000108781808000000b840f020a7f027e23808080800041e0006b2202248080808000200241286a200110f48a808000200228022c21030240024002400240024002400240024020022802282204418080808078460d0020022802302101200241f091c380003602502002200136024c20022003360248410021052002410036023020024280808080103702282002200241286a36025c200241d4006a200241dc006a2001200241c8006a41f091c3800010858780800002402002280254418380c400460d0002402002280228450d00200228022c41002802c0a3c68000118080808000000b410021050c050b20022802282206418080808078460d04200228022c210741012105200228023022084102490d0320072d0000220141c000490d0102402001411874411875417f4a0d00410421050c040b4102210920072d0001220a413f714108742001410274200a4106767241ff01717221010c020b200041013a0000200020033602040c060b410121090b2008200941226a470d000240200141feff0071412e470d00410721050c010b200241c8006a20072009412072220510e883808000024002400240024002402002280250220a41014d0d00200228024c210a0240024002400240200820056b4102470d00200720056a2f0000200a2f0000470d00200720096a22092f0003210520092f0001210820092d0000210b2002413f6a2009411c6a280000360000200241386a200941156a290000370300200241306a2009410d6a2900003703002002200929000537032802402002280248450d00200a41002802c0a3c68000118080808000000b02402006450d00200741002802c0a3c68000118080808000000b2001413a490d08200141c00f4a0d02200141b7034a0d01200141416a0ece010808080808080807080808070707080807070807070707070707070707070707070707070707070707070807070707080707080707070807070707070707070807070707080707070707070707070707070707070707070707070707070707070707070707070707070707070708070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070807070807070707070707070707070708030b02402002280248450d00200a41002802c0a3c68000118080808000000b410321050c080b0240200141d5084a0d00024020014194064a0d00200141b803460d082001419a05470d070c080b2001419506460d07200141e307470d060c070b0240200141d0756a0e0a07060606060606060607000b200141d608460d06200141ec0b470d050c060b0240200141de364a0d00024020014196114a0d000240200141bf706a0e30080707070707070707070707070707070707070707070807080707070707070707070707080707070707070707070708000b200141d46f6a220741144b0d064101200774418180c10071450d060c070b0240200141a51f4a0d002001419711460d07200141ce11460d07200141851a470d060c070b0240200141f1284a0d00200141a61f460d07200141e222470d060c070b200141f228460d06200141ce2f470d050c060b0240200141cecd004a0d000240200141a1c5004a0d000240200141a1496a0e0708070707070708000b2001418a39460d07200141df39470d060c070b200141deba7f6a220741144d0d030c040b0240200141fade004a0d000240200141ddd9004a0d00200141cfcd00460d07200141b9ce00470d060c070b200141ded900460d06200141acdc00470d050c060b0240200141bbe6004a0d00200141fbde00460d062001419fdf00470d050c060b200141bce600460d05200141e9f200460d050c040b200141a403470d030c040b4102200a41ac93c38000109581808000000b4101200774418180c800710d020b200141f0c600460d01200141cfcc00460d010b02400240200141c4094a0d00200141a87f6a0e320202020102020101010102020202010101010101010101010101010101010101010101010101010102020101010101010202010b200141bb766a4102490d01200141fc756a4102490d01200141e26e6a4102490d010b200141bca77f6a417d4b0d00200141002f0194a1c68000460d00410221050c030b200241086a41176a2201200241286a41176a280000360000200241086a41106a2207200241286a41106a290300370300200241086a41086a200241286a41086a290300220c37030020022002290328220d370308200041046a20053b0100200041026a20083b01002000200b3a0001200041066a200d3701002000410e6a200c370100200041166a20072903003701002000411d6a2001280000360000410021010c030b2006450d00200741002802c0a3c68000118080808000000b0b200220013b015e200220053b015c200241346a4201370200410121012002410136022c200241bc93c3800036022820024189828080003602582002200241d4006a3602302002200241dc006a360254200241c8006a200241286a10b8808080002000200241c8006a10ea8a8080003602040b200020013a00002004450d00200341002802c0a3c68000118080808000000b200241e0006a2480808080000bc708020c7f027e2380808080004190026b220224808080800002400240200128020422034120490d002001200341606a220436020420012001280200220541206a2206360200200241c8006a41086a200541086a290000370300200241c8006a41106a200541106a290000370300200241c8006a41186a200541186a29000037030020022005290000370348200441c000490d002001200341a07f6a3602042001200541e0006a360200200241e8006a41086a200641086a2201290000370300200241e8006a41106a200641106a2205290000370300200241e8006a41186a200641186a2203290000370300200241e8006a41206a2204200641206a290000370300200241e8006a41286a200641286a290000370300200241e8006a41306a200641306a290000370300200241e8006a41386a200641386a29000037030020022006290000370368200241a8016a41186a2003290000370300200241a8016a41106a2005290000370300200241a8016a41086a2001290000370300200220062900003703a801200241ef016a200241a8016a10ea8280800020022d00ef01450d00200241ee016a2201200241ef016a41036a22052d00003a0000200241d0016a41086a2206200241ef016a410d6a2203290000370300200241d0016a410f6a2207200241ef016a41146a2208290000370000200241c8016a41046a2209200241ef016a41206a2d00003a0000200220022f00f0013b01ec012002200241ef016a41056a220a2900003703d0012002200241ef016a411c6a220b2800003602c801200241ef016a41046a220c2d0000210d200241ef016a200410ea8280800020022d00ef01450d00200241086a220420032900003703002002410f6a22032008290000370000200220022f00f0013b0118200220052d00003a001a2002200a290000370300200220022f01ec013b0140200220012d00003a0042200b350000210e200241ef016a41206a310000210f200c2d00002101200241286a410f6a22052007290000370000200241286a41086a22072006290300370300200220022903d001370328200241206a41046a220620092d00003a0000200220022802c801360220200041d9006a200241e0006a290300370000200041d1006a200241d8006a290300370000200041c9006a200241c8006a41086a290300370000200041c1006a2002290348370000200041046a200d3a0000200020022f01403b0001200041036a20022d00423a0000200041056a20022903283700002000410d6a2007290300370000200041146a2005290000370000200041206a20062d00003a00002000411c6a2002280220360000200041236a20022d001a3a0000200041216a20022f01183b0000200041246a20013a0000200041346a20032900003700002000412d6a2004290300370000200041256a2002290300370000200041c0006a200f3c00002000413c6a200e3e0000200041003a00000c010b200041013a00000b20024190026a2480808080000bf80501087f2380808080004180016b220224808080800041002d00fca3c680001a02400240412041002802c8a3c68000118180808000002203450d0020032000290040370000200341186a2204200041d8006a290000370000200341106a2205200041d0006a290000370000200341086a2206200041c8006a29000037000002402001280200200128020822076b411f4b0d0020012007412010b182808000200128020821070b2001280204220820076a22092003290000370000200941086a2006290000370000200941106a2005290000370000200941186a20042900003700002001200741206a2209360208200341002802c0a3c6800011808080800000200241086a200041086a290000370300200241106a200041106a290000370300200241186a200041186a290000370300200241206a22032000290020370300200241286a2207200041286a290000370300200241306a2204200041306a290000370300200241386a2205200041386a2900003703002002200029000037030041002d00fca3c680001a41c00041002802c8a3c68000118180808000002200450d0120002002290300370000200041386a2005290300370000200041306a2004290300370000200041286a2007290300370000200041206a2003290300370000200041186a2207200241186a290300370000200041106a2204200241106a290300370000200041086a2205200241086a2903003700000240200128020020096b413f4b0d002001200941c00010b18280800020012802042108200128020821090b200820096a22032000290000370000200341386a200041386a290000370000200341306a200041306a290000370000200341286a200041286a290000370000200341206a200041206a290000370000200341186a2007290000370000200341106a2004290000370000200341086a20052900003700002001200941c0006a360208200041002802c0a3c680001180808080000020024180016a2480808080000f0b4101412010b280808000000b410141c00010b280808000000ba60401067f23808080800041d0006b2204248080808000200141246a28020028020c210520012802202106200441186a41086a2207200341086a280200360200200420032902003703182004410c6a20062002200441186a20051186808080000002400240024002400240200428020c2205418080808078470d00200441186a41186a2203200241186a290000370300200441186a41106a2205200241106a2900003703002007200241086a2900003703002004200229000037031841002d00fca3c680001a413041002802c8a3c680001181808080000022010d014104413010b280808000000b20042802142106200428021021072001280210450d022001280214450d0141c493c38000108781808000000b2001418580808078360200200120042903183702042001410c6a200441206a290300370200200141146a20052903003702002001411c6a20032903003702002000418080808078360200200020013602040c020b2001417f360214200141186a28020021082001411c6a2802002109200441c4006a200241186a2900003702002004413c6a200241106a290000370200200441186a411c6a200241086a290000370200200420063602202004200736021c20044180808080783602182004200229000037022c200420032902003702242008200441186a200928020c118480808000002001200128021441016a3602140b2000200636020820002007360204200020053602000b200441d0006a2480808080000b9c0e05047f027e017f017e027f23808080800041d0056b2206248080808000200341086a280200210720032802042108024002400240024002400240024002400240024002400240024002400240200328020022090d0020070d01410121030c020b0240024020070d00410121030c010b2007417f4c0d0741002d00fca3c680001a200741002802c8a3c68000118180808000002203450d080b20032008200710848e8080001a41002104200721080c030b20074120460d012007417f4c0d0541002d00fca3c680001a200741002802c8a3c68000118180808000002203450d070b20032008200710848e808000210841002d00fca3c680001a413041002802c8a3c680001181808080000022030d024104413010b280808000000b200841026a2d000021072008410f6a290000210a200841176a290000210b20082f000021032008280003210c2008290007210d20064188026a411f6a2008411f6a2d00003a000020064188026a41176a200b37000020064188026a410f6a200a37000020064188026a41026a20073a00002006200d37008f022006200c36008b02200620033b018802200141246a280200210720012802202103200641306a41086a200441086a280200360200200620042902003703302006410c6a200320064188026a200641306a200728020c11868080800000200628020c2207418080808078460d08200641f0036a41086a20064188026a41086a290100370300200641f0036a41106a20064188026a41106a290100370300200641f0036a41186a20064188026a41186a29010037030020062006290188023703f0032006280214210820062802102103410121040b200641156a200641f0036a41086a220e2903003700002006411d6a20064180046a220c290300370000200641256a20064188046a290300370000200620043a000c200620062903f00337000d200641f0036a2003200810c28c8080002006410d6a210420062802f003220f4105470d0120064188026a41086a200c2802003602002006200e2902003703880220062802f403210802402007450d00200341002802c0a3c68000118080808000000b200641f0036a41186a22032002200420091b220741186a290000370300200641f0036a41106a2202200741106a290000370300200641f0036a41086a2201200741086a290000370300200620072900003703f00341002d00fca3c680001a413041002802c8a3c680001181808080000022070d054104413010b280808000000b2003200736020c2003200836020820032007360204200341888080807836020020032002290000370010200341186a200241086a290000370000200341206a200241106a290000370000200341286a200241186a2900003700000c070b200641e0036a41086a2202200c280200360200200620062902f8033703e00320062802f403210c20064194026a20064184046a41c80110848e8080001a20064188026a41086a2002280200360200200620062903e00337038802200641306a20064188026a41d40110848e8080001a20090d042005450d042001280210450d04024020012802140d002001417f360214200141186a28020021022001411c6a2802002109200641fc036a2008360200200641f0036a41086a200336020020064198046a200441186a29000037020020064190046a200441106a290000370200200641f0036a41186a200441086a2900003702002006428280808088808080807f3702f00320062004290000370280042002200641f0036a200928020c118480808000002001200128021441016a3602140c050b41d493c38000108781808000000b10ae80808000000b4101200710b280808000000b4101200710b280808000000b200720083602002007200629038802370204200720062903f0033702102007410c6a20064188026a41086a280200360200200741186a2001290300370200200741206a2002290300370200200741286a200329030037020020004105360200200020073602040c030b200041086a200641306a41d40110848e8080001a200020073602dc01200020083602e401200020033602e0012000200c3602042000200f3602002000200629000c3700e801200041f0016a2006410c6a41086a290000370000200041f8016a2006411c6a29000037000020004180026a200641246a29000037000020004188026a2006412c6a2d00003a00000c020b20042d000821082004280204210741002d00fca3c680001a413041002802c8a3c6800011818080800000210302400240024020070d00200841ff0171450d010b418580808078210720030d014104413010b280808000000b418480808078210720030d004104413010b280808000000b2003200736020020032006290188023700042003410c6a20064190026a290100370000200341146a20064198026a2901003700002003411c6a200641a0026a2901003700000b20004105360200200020033602040b200641d0056a2480808080000bb00101017f23808080800041106b2201248080808000200142888080808001370200200142808080808001370208200041c4006a200110fa86808000200041106a42e6ed8d82cc91adcb05370300200042dbd791d5c2919eaecd00370308200041c0006a410036020020004280808080c000370338200041d8006a410036020020004280808080c000370350200041c000360220200041c880808000360218200041033a0000200141106a2480808080000b2a00200041003b01102000420037030820004280c2d72f42e097e1c29b0120012d00104103491b3703000bec0c01027f23808080800041106b22022480808080000240024002400240024002400240024002400240024002400240024002400240024002400240024020002d00000e0e000102030405060708090a0b0c0d000b2002200041046a36020020022001280214418ea2c380004105200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a20024194a2c38000108d81808000210120022d000c21000240200128020022010d00200041ff017141004721030c130b41012103200041ff01710d122002280208210020014101470d1120022d000d41ff0171450d1120002d001c4104710d11410121032000280214418ca5c080004101200041186a28020028020c11828080800000450d110c120b200128021441a4a2c38000410c200141186a28020028020c1182808080000021030c110b200128021441b0a2c380004109200141186a28020028020c1182808080000021030c100b2002200041046a3602002002200128021441b9a2c380004106200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a200241c0a2c38000108d81808000210120022d000c21000240200128020022010d00200041ff017141004721030c100b41012103200041ff01710d0f2002280208210020014101470d0d20022d000d41ff0171450d0d20002d001c4104710d0d410121032000280214418ca5c080004101200041186a28020028020c11828080800000450d0d0c0f0b200128021441d0a2c380004111200141186a28020028020c1182808080000021030c0e0b200128021441e1a2c38000410b200141186a28020028020c1182808080000021030c0d0b200128021441eca2c380004110200141186a28020028020c1182808080000021030c0c0b410121032002200041016a3602002002200128021441fca2c380004105200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a20024184a3c38000108d81808000210120022d000c21000240200128020022010d00200041ff017141004721030c0c0b200041ff01710d0b2002280208210020014101470d0820022d000d41ff0171450d0820002d001c4104710d08410121032000280214418ca5c080004101200041186a28020028020c11828080800000450d080c0b0b410121032002200041016a360200200220012802144194a3c38000410a200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a200241a0a3c38000108d81808000210120022d000c21000240200128020022010d00200041ff017141004721030c0b0b200041ff01710d0a2002280208210020014101470d0620022d000d41ff0171450d0620002d001c4104710d06410121032000280214418ca5c080004101200041186a28020028020c11828080800000450d060c0a0b410121032002200041016a3602002002200128021441b0a3c38000410d200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a200241c0a3c38000108d81808000210120022d000c21000240200128020022010d00200041ff017141004721030c0a0b200041ff01710d092002280208210020014101470d0420022d000d41ff0171450d0420002d001c4104710d04410121032000280214418ca5c080004101200041186a28020028020c11828080800000450d040c090b200128021441d0a3c380004109200141186a28020028020c1182808080000021030c080b200128021441d9a3c38000410a200141186a28020028020c1182808080000021030c070b200128021441e3a3c38000410b200141186a28020028020c1182808080000021030c060b200128021441eea3c38000410e200141186a28020028020c1182808080000021030c050b2000280214418da5c080004101200041186a28020028020c1182808080000021030c040b2000280214418da5c080004101200041186a28020028020c1182808080000021030c030b2000280214418da5c080004101200041186a28020028020c1182808080000021030c020b2000280214418da5c080004101200041186a28020028020c1182808080000021030c010b2000280214418da5c080004101200041186a28020028020c1182808080000021030b200241106a24808080800020030b840601067f23808080800041306b220124808080800020014106360204200141a599c3800036020041002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241a599c38000360200200241046a410636020041a599c38000410610d782808000450d0141002d00fca3c680001a41c00041002802c8a3c68000118180808000002203450d02200342d1c5efcdcd82cde1ff00370308200341ac99c38000360220200341938280800036021820034101360204200341ab99c38000360200200341306a42c0969aec91e181fcf200370300200341286a42d7a7fbff94a5afcaf800370300200341106a429ccab49c93e2e495cf00370300200341386a419482808000360200200341246a4101360200200141023602142001200336020c2001200341c0006a36021820012003360210200141246a2001410c6a10fa86808000200128022c210420012802282103200128022421052001410036021420014280808080c00037020c200141246a2001410c6a41ad99c38000410210a08b8080002001200141246a41af99c38000410310ce8b8080002001200128020036021420012001280204220636020c20012006200128020841246c6a36021820012006360210200141246a2001410c6a10fb86808000200120053602142001200336020c2001200320044105746a36021820012003360210200041c4006a2001410c6a10fa86808000200141176a200141246a41086a2802003600002000413c6a2002ad4280808080108437020020004101360238200041013a0000200041d8006a410036020020004280808080c0003703502001200129022437000f2000200129000c370001200041086a200141136a290000370000200141306a2480808080000f0b4104410810b280808000000b200241002802c0a3c6800011808080800000200141186a42013702002001410236021020014180ddc1800036020c200141b4808080003602282001200141246a360214200120013602242001410c6a4190ddc1800010f680808000000b410841c00010b280808000000b800601067f23808080800041306b220124808080800020014106360204200141a599c3800036020041002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241a599c38000360200200241046a410636020041a599c38000410610d782808000450d0141002d00fca3c680001a41c00041002802c8a3c68000118180808000002203450d022003429198b9e48fc6c3ad1d370308200341ac99c38000360220200341958280800036021820034101360204200341ab99c38000360200200341306a42ab8ad7e1bb97ae9f51370300200341286a42f2f9a5a49996c5e032370300200341106a42f69a89928aa399ea00370300200341386a419682808000360200200341246a4101360200200141023602142001200336020c2001200341c0006a36021820012003360210200141246a2001410c6a10fa86808000200128022c210420012802282103200128022421052001410036021420014280808080c00037020c200141246a2001410c6a41ad99c380004102109a8b8080002001200141246a41af99c38000410310fc8a8080002001200128020036021420012001280204220636020c20012006200128020841246c6a36021820012006360210200141246a2001410c6a10fb86808000200120053602142001200336020c2001200320044105746a36021820012003360210200041c4006a2001410c6a10fa86808000200141176a200141246a41086a2802003600002000413c6a2002ad4280808080108437020020004101360238200041013a0000200041d8006a410036020020004280808080c0003703502001200129022437000f2000200129000c370001200041086a200141136a290000370000200141306a2480808080000f0b4104410810b280808000000b200241002802c0a3c6800011808080800000200141186a42013702002001410236021020014180ddc1800036020c200141b4808080003602282001200141246a360214200120013602242001410c6a4190ddc1800010f680808000000b410841c00010b280808000000b840601067f23808080800041306b220124808080800020014106360204200141a599c3800036020041002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241a599c38000360200200241046a410636020041a599c38000410610d782808000450d0141002d00fca3c680001a41c00041002802c8a3c68000118180808000002203450d02200342d1c5efcdcd82cde1ff00370308200341ac99c38000360220200341938280800036021820034101360204200341ab99c38000360200200341306a42efc9c9edb5e7b3a6c700370300200341286a42acf6debeefe0d9c8d300370300200341106a429ccab49c93e2e495cf00370300200341386a41bd80808000360200200341246a4101360200200141023602142001200336020c2001200341c0006a36021820012003360210200141246a2001410c6a10fa86808000200128022c210420012802282103200128022421052001410036021420014280808080c00037020c200141246a2001410c6a41ad99c38000410210a08b8080002001200141246a41af99c38000410310898b8080002001200128020036021420012001280204220636020c20012006200128020841246c6a36021820012006360210200141246a2001410c6a10fb86808000200120053602142001200336020c2001200320044105746a36021820012003360210200041c4006a2001410c6a10fa86808000200141176a200141246a41086a2802003600002000413c6a2002ad4280808080108437020020004101360238200041013a0000200041d8006a410036020020004280808080c0003703502001200129022437000f2000200129000c370001200041086a200141136a290000370000200141306a2480808080000f0b4104410810b280808000000b200241002802c0a3c6800011808080800000200141186a42013702002001410236021020014180ddc1800036020c200141b4808080003602282001200141246a360214200120013602242001410c6a4190ddc1800010f680808000000b410841c00010b280808000000b800601067f23808080800041306b220124808080800020014106360204200141a599c3800036020041002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241a599c38000360200200241046a410636020041a599c38000410610d782808000450d0141002d00fca3c680001a41c00041002802c8a3c68000118180808000002203450d02200342d9adfaebb1c192ed50370308200341ac99c38000360220200341978280800036021820034101360204200341ab99c38000360200200341306a42ab8ad7e1bb97ae9f51370300200341286a42f2f9a5a49996c5e032370300200341106a42fb8387e2d6839cd949370300200341386a419682808000360200200341246a4101360200200141023602142001200336020c2001200341c0006a36021820012003360210200141246a2001410c6a10fa86808000200128022c210420012802282103200128022421052001410036021420014280808080c00037020c200141246a2001410c6a41ad99c380004102108a8b8080002001200141246a41af99c38000410310fc8a8080002001200128020036021420012001280204220636020c20012006200128020841246c6a36021820012006360210200141246a2001410c6a10fb86808000200120053602142001200336020c2001200320044105746a36021820012003360210200041c4006a2001410c6a10fa86808000200141176a200141246a41086a2802003600002000413c6a2002ad4280808080108437020020004101360238200041013a0000200041d8006a410036020020004280808080c0003703502001200129022437000f2000200129000c370001200041086a200141136a290000370000200141306a2480808080000f0b4104410810b280808000000b200241002802c0a3c6800011808080800000200141186a42013702002001410236021020014180ddc1800036020c200141b4808080003602282001200141246a360214200120013602242001410c6a4190ddc1800010f680808000000b410841c00010b280808000000bac0401097f23808080800041306b220124808080800020012000200010eb8780800002400240024020012802102202418080808078460d002001280224210320012802202104200128021c210520012802142106024020012802182200450d002000410171210741002108024020004101460d002000417e7121092006210041002108034002402000280200450d00200041046a28020041002802c0a3c68000118080808000000b02402000410c6a280200450d00200041106a28020041002802c0a3c68000118080808000000b200041186a21002009200841026a2208470d000b0b2007450d0020062008410c6c6a2200280200450d00200028020441002802c0a3c68000118080808000000b02402002450d00200641002802c0a3c68000118080808000000b02402003450d002003410171210241002108024020034101460d002003417e7121092004210041002108034002402000280200450d00200041046a28020041002802c0a3c68000118080808000000b02402000410c6a280200450d00200041106a28020041002802c0a3c68000118080808000000b200041186a21002009200841026a2208470d000b0b2002450d0020042008410c6c6a2200280200450d00200028020441002802c0a3c68000118080808000000b2005450d01200441002802c0a3c68000118080808000000c010b20012d000041ff017122004102460d0020012f000141087421080c010b41002108410221000b200141306a24808080800020082000720bfb0801087f23808080800041e0006b22032480808080002003200236020c02404100280284a4c680004105470d00200341988280800036025820032003410c6a3602544100280290a1c680002102410028028ca1c6800021044100280280a4c680002105200341c8006a4201370200200341c0006a4101360200200341386a4115360200200341346a41a4a9c38000360200200341286a41c0a7c38000ad4280808080b00e843702002003411c6a41b9a9c38000ad4280808080d00684370200200341c4006a200341d4006a3602002003419ca9c3800036023c20034105360230200341003602242003410036021820034281808080901c370210200441ecf2c08000200541024622051b200341106a200241d4f2c0800020051b28021011848080800000200328020c21020b02400240024002400240024020022802002202410c4b0d004101200274418831710d010b2000418080808078360210200041003b01000c010b41002d00fca3c680001a410c41002802c8a3c68000118180808000002204450d014101210202400240024002400240024002400240024002400240200328020c2205280200417f6a0e0c080600010203090409050505080b417f200541186a28020041046a220241012005410c6a28020041056a2005280204418080808078461b6a220620062002491b21020c060b417f200541186a2802002005410c6a28020041046a22026a41046a220620062002491b21020c050b2005410c6a28020041046a21020c040b2005410c6a28020041046a21020c030b410921020c040b410521020c030b2005410c6a28020041046a21020b4101210641002107200241016a2202450d022002417f4a0d0110ae80808000000b41d10021020b41002d00fca3c680001a200241002802c8a3c68000118180808000002206450d03200221070b2003200636021420032007360210200341003602182005200341106a10ba8880800020032802102102200341106a2003280214220520032802184100280298a3c680001185808080000002402002450d00200541002802c0a3c68000118080808000000b2003410036025c200342808080801037025441002d00fca3c680001a412041002802c8a3c68000118180808000002202450d0320022003290010370000200241186a2207200341106a41186a290000370000200241106a2208200341106a41106a290000370000200241086a2209200341106a41086a290000370000200341d4006a4100412010b1828080002003280258200341d4006a41086a2206280200220a6a22052002290000370000200541086a2009290000370000200541106a2008290000370000200541186a20072900003700002006200a41206a360200200241002802c0a3c6800011808080800000200441086a20062802003602002004200329025437020020004280808080c000370310200041013a0028200041013602242000200436022020004280808080103703182000427f370308200042003703000b200341e0006a2480808080000f0b4104410c10b280808000000b4101200210b280808000000b4101412010b280808000000bfd0201017f23808080800041c0006b22042480808080000240024020012d00780d0020012d00790d01200441386a41a08ac480003602002004412c6a4100360200200442003702242004410036021c20044200370214200420013602342004200141386a36023c200441086a200441146a2002200310dd87808000024002402004280208418180808078460d0020002004290208370200200041086a200441086a41086a2802003602000c010b0240024002400240200428020c220128020041fcffffff076a2203410320034105491b0e0403030102000b2001280204450d02200141086a28020041002802c0a3c68000118080808000000c020b2001280204450d01200141086a28020041002802c0a3c68000118080808000000c010b200110ed878080000b200141002802c0a3c680001180808080000020004181808080783602000b200441c0006a2480808080000f0b41b299c38000410f41b89dc3800010f880808000000b41b299c38000410f41b49ac3800010f880808000000bc50101027f024002400240024020002802002201418080808078732202410220024104491b0e03030301000b0240024002402000280204220028020041fcffffff076a2202410320024105491b0e0404040102000b2000280204450d03200041086a28020041002802c0a3c68000118080808000000c030b2000280204450d02200041086a28020041002802c0a3c68000118080808000000c020b200010ed878080000c010b2001450d01200028020421000b200041002802c0a3c68000118080808000000b0b170041b299c38000410f41b49ac3800010f880808000000b970601087f23808080800041106b2202248080808000024002400240024020002802000e03000102000b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41003a00002001200341016a2204360208200028020421050240200128020020046b41034b0d0020012004410410b182808000200128020821040b2001200441046a2203360208200128020420046a20053600000c020b0240200128020020012802082204470d0020012004410110b182808000200128020821040b2001200441016a2203360208200128020420046a41013a00000c010b0240200128020020012802082204470d0020012004410110b182808000200128020821040b2001200441016a2203360208200128020420046a41023a00000b0240024020002d00084116470d00024020012802002003470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41003a0000200041106a200110a08a8080000c010b200041086a2104024020012802002003470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41033a00002004200110a88c8080000b200041dc006a28020021032002200041e0006a28020022003602082002200241086a36020c2002410c6a200110c08a808000024002402000450d0020004105742106034041002d00fca3c680001a412041002802c8a3c68000118180808000002200450d0220002003290000370000200041186a2207200341186a290000370000200041106a2208200341106a290000370000200041086a2209200341086a29000037000002402001280200200128020822056b411f4b0d0020012005412010b182808000200128020821050b200341206a2103200128020420056a22042000290000370000200441086a2009290000370000200441106a2008290000370000200441186a20072900003700002001200541206a360208200041002802c0a3c6800011808080800000200641606a22060d000b0b200241106a2480808080000f0b4101412010b280808000000bad0b04067f017e027f017e23808080800041d0006b2201248080808000200141286a419f9bc38000410b41aa9bc38000410c418097c38000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d00200242a5e9e3ab9e929adc2c370308200241bb9bc38000360220200241ef8080800036021820024105360204200241b69bc38000360200200241306a42e1be86f0e3e6afc202370300200241286a42c4daf0e7d4cf86cca87f370300200241106a4293888c8f89fdc6ec9e7f370300200241386a419982808000360200200241246a410b36020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a200141106a410c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c2002420537022420024185e4c480003602202002410536021c20024180e4c48000360218200241ef8080800036021020024293888c8f89fdc6ec9e7f370308200242a5e9e3ab9e929adc2c370300200141c0006a41086a2209200828020041016a220236020020012001290210220a37034002402002200aa7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c2002420837022420024193e4c480003602202002410936021c2002419be4c48000360218200241b580808000360210200242e88488d0c0e3aebc13370308200242d7c9cb8fc1cf97db3e3703002008200928020041016a220236020020012001290340220a37031002402002200aa7470d00200141106a200210a486808000200128021821020b2001280214200241386c6a2202420437022c2002420837022420024193e4c480003602202002410936021c2002418ae4c48000360218200241b580808000360210200242e88488d0c0e3aebc13370308200242d7c9cb8fc1cf97db3e370300200141c0006a41086a2208200141106a41086a220928020041016a220236020020012001290310220a37034002402002200aa7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c2002420837022420024193e4c480003602202002410b36021c200241b3e4c48000360218200241b580808000360210200242e88488d0c0e3aebc13370308200242d7c9cb8fc1cf97db3e3703002009200828020041016a220236020020012001290340220a37031002402002200aa7470d00200141106a200210a486808000200128021821020b2001280214200241386c6a2202420437022c2002420b370224200241a8e4c480003602202002410436021c200241a4e4c480003602182002419982808000360210200242e1be86f0e3e6afc202370308200242c4daf0e7d4cf86cca87f370300200128021821082001280214210220012001280210360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000beb0804067f017e027f017e23808080800041d0006b2201248080808000200141286a41c69bc38000410b41aa9bc38000410c418097c38000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d002002428efca59582e3caf618370308200241ab99c380003602202002419a8280800036021820024101360204200241ac99c38000360200200241306a42ab8bffbed784ffa5937f370300200241286a42c194a6a793ccc3a857370300200241106a42988ac9c0f3f1b2b0b57f370300200241386a41f581808000360200200241246a410136020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a200141106a410c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c20024205370224200241d5e4c480003602202002410536021c200241d0e4c480003602182002419b82808000360210200242dd9fc4e2a4bae882d400370308200242fb9bf4d1a5ccd7cbed00370300200141c0006a41086a2209200828020041016a220236020020012001290210220a37034002402002200aa7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c20024201370224200241c3e4c480003602202002410536021c200241bee4c480003602182002419a82808000360210200242988ac9c0f3f1b2b0b57f3703082002428efca59582e3caf6183703002008200928020041016a220236020020012001290340220a37031002402002200aa7470d00200141106a200210a486808000200128021821020b2001280214200241386c6a2202420437022c20024206370224200241cae4c480003602202002410636021c200241c4e4c480003602182002419c82808000360210200242b6e5c682fa82cdd4dc00370308200242b49ee2c5eaeedcf10c370300200128021821082001280214210220012001280210360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000bdd0604067f017e017f017e23808080800041d0006b2201248080808000200141106a41186a41d19bc38000411841aa9bc38000410c418097c38000410010d882808000200141206a42043702002001420037021820014280808080800137021041002d00fca3c680001a02400240412041002802c8a3c68000118180808000002202450d002002410036021820024101360204200241ab99c3800036020020014101360248200120023602402001200241206a36024c20012002360244200141106a200141c0006a10fa86808000200141086a200141106a410c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c20024207370224200241e3e4c480003602202002410936021c200241dae4c48000360218200241f581808000360210200242ab8bffbed784ffa5937f370308200242c194a6a793ccc3a857370300200141c0006a41086a200828020041016a2202360200200120012902102209370340024020022009a7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c20024204370224200241f7e4c480003602202002410d36021c200241eae4c48000360218200241888180800036021020024298848fa1dab08ba174370308200242febac4ad81b6fafcb37f370300200128024821082001280244210220012001280240360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b4108412010b280808000000b41a8d8c480004111419cd9c4800010a181808000000be80501047f23808080800041e0006b2202248080808000200241346a41f9a6c48000410641002802a0a3c6800011858080800000200241346a41106a220341899fc38000410d41002802a0a3c6800011858080800000200241086a41186a200241346a41186a290000370300200241086a41106a2003290000370300200241086a41086a200241346a41086a2900003703002002200229003437030820022001360254200241d8006a200241d4006a410441002802a8a3c68000118580808000002002200241d4006a41046a3602402002200241d8006a41086a3602382002200241d4006a36023c2002200241d8006a360234200241286a200241346a108182808000024002400240024002402002280230220341206a22040d002002410036023c20024280808080103702340c010b2004417f4c0d024100210541002d00fca3c680001a200441002802c8a3c68000118180808000002201450d032002410036023c200220013602382002200436023420034160490d010b200241346a4100412010ab8680800020022802382101200228023c21050b200120056a22042002290308370000200441186a200241086a41186a290300370000200441106a200241086a41106a290300370000200441086a200241086a41086a2903003700002002200541206a220436023c200228022c21050240200228023420046b20034f0d00200241346a2004200310ab8680800020022802382101200228023c21040b200120046a2005200310848e8080001a200420036a21032002280234210402402002280228450d00200541002802c0a3c68000118080808000000b200241346a2001200310e988808000024002402002280234418080808078470d002000410036020820004280808080103702000c010b20002002290234370200200041086a200241346a41086a2802003602000b02402004450d00200141002802c0a3c68000118080808000000b200241e0006a2480808080000f0b10ae80808000000b4101200410b280808000000bb108050b7f017e037f027e017f2380808080004190016b2202248080808000200241086a41f9a6c48000410641002802a0a3c6800011858080800000200241086a41106a220341ea9ec38000410741002802a0a3c6800011858080800000200241d0006a41186a200241086a41186a290000370300200241d0006a41106a2003290000370300200241d0006a41086a200241086a41086a2900003703002002200229000837035041002d00fca3c680001a024002400240412041002802c8a3c68000118180808000002204450d0020042001290000370000200441186a200141186a290000370000200441106a200141106a290000370000200441086a200141086a290000370000200241086a200441204100280290a3c68000118580808000002002200441206a36028c01200220043602880120022003360284012002200241086a36028001200241f4006a20024180016a108182808000200441002802c0a3c6800011808080800000024002400240200228027c220141206a22030d002002410036021020024280808080103702080c010b2003417f4c0d034100210541002d00fca3c680001a200341002802c8a3c68000118180808000002204450d04200241003602102002200436020c2002200336020820014160490d010b200241086a4100412010ab86808000200228020c2104200228021021050b200420056a22032002290350370000200341186a200241d0006a41186a290300370000200341106a200241d0006a41106a290300370000200341086a200241d0006a41086a2903003700002002200541206a2203360210200228027821050240200228020820036b20014f0d00200241086a2003200110ab86808000200228020c2104200228021021030b200420036a2005200110848e8080001a200320016a21012002280208210302402002280274450d00200541002802c0a3c68000118080808000000b200241086a2004200110e4888080000240024020022903084200520d00410521010c010b200241d0006a41086a2205200241306a2206290300370300200241d0006a41106a2207200241386a22082903003703002002200241286a22092903003703500240200241c4006a220a280200220b0d00410521010c010b200241086a41106a220c290300210d200241c0006a220e280200210f200241c8006a2210290300211120022903102112200241206a22132013290300370300200c200d3703002009200229035037030020102011370300200a200b3602002006200529030037030020082007290300370300200e200f41016a2205417f20051b3602002002201237031020024201370308200241086a41086a2004200110f587808000410e21010b200020013a000002402003450d00200441002802c0a3c68000118080808000000b20024190016a2480808080000f0b4101412010b280808000000b10ae80808000000b4101200310b280808000000b980301047f23808080800041106b220324808080800041002d00fca3c680001a0240413c41002802c8a3c68000118180808000002204450d00200320043602082003413c360204200420002903003700002003410836020c200028023021054108210402402003280204417c714108470d00200341046a4108410410b182808000200328020c21040b200328020820046a20053600002003200441046a220436020c200028023421050240200328020420046b41034b0d00200341046a2004410410b182808000200328020c21040b200041086a2106200328020820046a20053600002003200441046a220436020c200028023821000240200328020420046b41034b0d00200341046a2004410410b182808000200328020c21040b200328020820046a20003600002003200441046a36020c2006200341046a10848d808000200328020421002001200220032802082204200328020c41002802e0a1c680001186808080000002402000450d00200441002802c0a3c68000118080808000000b200341106a2480808080000f0b4101413c10b280808000000bc20802067f037e2380808080004190016b2202248080808000200241086a41f9a6c48000410641002802a0a3c6800011858080800000200241086a41106a220341ea9ec38000410741002802a0a3c6800011858080800000200241d0006a41186a200241086a41186a290000370300200241d0006a41106a2003290000370300200241d0006a41086a200241086a41086a2900003703002002200229000837035041002d00fca3c680001a024002400240412041002802c8a3c68000118180808000002204450d0020042001290000370000200441186a200141186a290000370000200441106a200141106a290000370000200441086a200141086a290000370000200241086a200441204100280290a3c68000118580808000002002200441206a36028c01200220043602880120022003360284012002200241086a36028001200241f4006a20024180016a108182808000200441002802c0a3c6800011808080800000024002400240200228027c220141206a22030d002002410036021020024280808080103702080c010b2003417f4c0d034100210541002d00fca3c680001a200341002802c8a3c68000118180808000002204450d04200241003602102002200436020c2002200336020820014160490d010b200241086a4100412010ab86808000200228020c2104200228021021050b200420056a22032002290350370000200341186a200241d0006a41186a290300370000200341106a200241d0006a41106a290300370000200341086a200241d0006a41086a2903003700002002200541206a2203360210200228027821050240200228020820036b20014f0d00200241086a2003200110ab86808000200228020c2104200228021021030b200420036a2005200110848e8080001a200320016a21012002280208210602402002280274450d00200541002802c0a3c68000118080808000000b200241086a2004200110e4888080000240024020022903084200520d00410521030c010b200241d8006a200241306a290300370300200241d0006a41106a200241386a2903003703002002200241286a2903003703500240200241c4006a28020022050d00410521030c010b41062103200241c0006a2802002207410f4b0d00200241086a41106a2903002108200241c8006a29030021092002290310210a200241206a200241206a290300370300200241086a41106a2008370300200241286a2002290350370300200241c8006a2009370300200241c4006a2005360200200241c0006a200741016a360200200241306a200241d0006a41086a290300370300200241386a200241d0006a41106a2903003703002002200a37031020024201370308200241086a41086a2004200110f587808000410e21030b200020033a000002402006450d00200441002802c0a3c68000118080808000000b20024190016a2480808080000f0b4101412010b280808000000b10ae80808000000b4101200310b280808000000be60e01127f23808080800041e0016b220324808080800020034190016a41f9a6c48000410641002802a0a3c680001185808080000020034190016a41106a220441ea9ec38000410741002802a0a3c6800011858080800000200341086a41186a20034190016a41186a290000370300200341086a41106a2004290000370300200341086a41086a20034190016a41086a290000370300200320032900900137030841002d00fca3c680001a024002400240412041002802c8a3c68000118180808000002205450d0020052001290000370000200541186a200141186a290000370000200541106a200141106a290000370000200541086a200141086a29000037000020034190016a200541204100280290a3c68000118580808000002003200541206a36026c2003200536026820032004360264200320034190016a360260200341d4006a200341e0006a108182808000200541002802c0a3c6800011808080800000024002400240200328025c220141206a22040d0020034100360298012003428080808010370290010c010b2004417f4c0d034100210641002d00fca3c680001a200441002802c8a3c68000118180808000002205450d0420034100360298012003200536029401200320043602900120014160490d010b20034190016a4100412010ab86808000200328029401210520032802980121060b200520066a22042003290308370000200441186a200341086a41186a290300370000200441106a200341086a41106a290300370000200441086a200341086a41086a2903003700002003200641206a22043602980120032802582106024020032802900120046b20014f0d0020034190016a2004200110ab86808000200328029401210520032802980121040b200520046a2006200110848e8080001a200420016a2104200328029001210702402003280254450d00200641002802c0a3c68000118080808000000b200341086a2005200410e488808000200328020821012003420037030802400240024002400240024020014101470d00200328024c210820032802482109200328024421062003280240210a200341e0006a41286a220b200341086a41086a220141286a220c290300370300200341e0006a41206a220d200141206a220e290300370300200341e0006a41186a220f200141186a2210290300370300200341e0006a41106a2211200141106a2212290300370300200341e0006a41086a200141086a2903003703002003200129030037036002400240024020060e020001020b0240417f4100280284a4c680002206410147200641014b1b2206417f460d00200641ff01710d010b4100280290a1c680002106410028028ca1c6800021134100280280a4c680002114200341c8016a4200370200200341c4016a418097c38000360200200341c0016a4101360200200341b8016a410f360200200341b4016a41a89cc38000360200200341a8016a41c49ac38000ad4280808080b00b843702002003419c016a41aa9bc38000ad4280808080c00184370200200341f09cc380003602bc01200341013602b001200341003602a40120034100360298012003428180808090bd0137029001201341ecf2c08000201441024622141b20034190016a200641d4f2c0800020141b280210118480808000000b200a200972450d03200a0d02410121060b20012003290360370300200c200b290300370300200e200d2903003703002010200f29030037030020122011290300370300200141086a200341e0006a41086a2903003703002003200836024c200320093602482003200a3602402003420137030820032006417f6a36024420012005200410f587808000410121010c040b0240417f4100280284a4c680002201410147200141014b1b2201417f460d00200141ff01710d030b4100280290a1c680002101410028028ca1c6800021064100280280a4c680002102200341c8016a4200370200200341c4016a418097c38000360200200341c0016a4101360200200341b8016a410f360200200341b4016a41a89cc38000360200200341a8016a41c49ac38000ad4280808080b00b843702002003419c016a41aa9bc38000ad4280808080c00184370200200341b09dc380003602bc01200341013602b001200341003602a401200341003602980120034281808080b0c00137029001200641ecf2c08000200241024622021b20034190016a200141d4f2c0800020021b280210118480808000000c020b410421040c030b20034199016a2002290000370000200341b1016a200241186a290000370000200341a9016a200241106a290000370000200341a1016a200241086a290000370000200341043a009801200341163a00900120034190016a108e8a8080000b410021012005200441002802a0a1c68000118480808000000b410e21040b200020013a0001200020043a000020002003290190013701022000410a6a20034198016a290100370100200041126a200341a0016a2f01003b010002402007450d00200541002802c0a3c68000118080808000000b200341e0016a2480808080000f0b4101412010b280808000000b10ae80808000000b4101200410b280808000000b800801047f2380808080004190016b2202248080808000200241086a41f9a6c48000410641002802a0a3c6800011858080800000200241086a41106a220341ea9ec38000410741002802a0a3c6800011858080800000200241d0006a41186a200241086a41186a290000370300200241d0006a41106a2003290000370300200241d0006a41086a200241086a41086a2900003703002002200229000837035041002d00fca3c680001a024002400240412041002802c8a3c68000118180808000002204450d0020042001290000370000200441186a200141186a290000370000200441106a200141106a290000370000200441086a200141086a290000370000200241086a200441204100280290a3c68000118580808000002002200441206a36028c01200220043602880120022003360284012002200241086a36028001200241f4006a20024180016a108182808000200441002802c0a3c6800011808080800000024002400240200228027c220141206a22030d002002410036021020024280808080103702080c010b2003417f4c0d034100210541002d00fca3c680001a200341002802c8a3c68000118180808000002204450d04200241003602102002200436020c2002200336020820014160490d010b200241086a4100412010ab86808000200228020c2104200228021021050b200420056a22032002290350370000200341186a200241d0006a41186a290300370000200341106a200241d0006a41106a290300370000200341086a200241d0006a41086a2903003700002002200541206a2203360210200228027821050240200228020820036b20014f0d00200241086a2003200110ab86808000200228020c2104200228021021030b200420036a2005200110848e8080001a200320016a21012002280208210302402002280274450d00200541002802c0a3c68000118080808000000b200241086a2004200110e4888080000240024020022903084200520d002000420037030820004200370300200041106a428080808080808080807f370300200041186a4200370300200041206a4200370300200041286a4200370300200041306a4200370300200041386a41003602000c010b20002002290310370300200041386a200241c8006a290300370300200041306a200241086a41386a290300370300200041286a200241086a41306a290300370300200041206a200241086a41286a290300370300200041186a200241086a41206a290300370300200041106a200241086a41186a290300370300200041086a200241086a41106a2903003703000b02402003450d00200441002802c0a3c68000118080808000000b20024190016a2480808080000f0b4101412010b280808000000b10ae80808000000b4101200310b280808000000beb0501047f23808080800041e0006b2202248080808000200241346a41f5a6c48000410441002802a0a3c6800011858080800000200241346a41106a220341f89ec38000411141002802a0a3c6800011858080800000200241086a41186a200241346a41186a290000370300200241086a41106a2003290000370300200241086a41086a200241346a41086a2900003703002002200229003437030820022001280200360254200241d8006a200241d4006a410441002802a8a3c68000118580808000002002200241d4006a41046a3602402002200241d8006a41086a3602382002200241d4006a36023c2002200241d8006a360234200241286a200241346a108182808000024002400240024002402002280230220341206a22040d002002410036023c20024280808080103702340c010b2004417f4c0d024100210541002d00fca3c680001a200441002802c8a3c68000118180808000002201450d032002410036023c200220013602382002200436023420034160490d010b200241346a4100412010ab8680800020022802382101200228023c21050b200120056a22042002290308370000200441186a200241086a41186a290300370000200441106a200241086a41106a290300370000200441086a200241086a41086a2903003700002002200541206a220436023c200228022c21050240200228023420046b20034f0d00200241346a2004200310ab8680800020022802382101200228023c21040b200120046a2005200310848e8080001a200420036a21032002280234210402402002280228450d00200541002802c0a3c68000118080808000000b200241346a2001200310e188808000024002402002280234418080808078470d002000410036020820004280808080103702000c010b20002002290234370200200041086a200241346a41086a2802003602000b02402004450d00200141002802c0a3c68000118080808000000b200241e0006a2480808080000f0b10ae80808000000b4101200410b280808000000b800801047f2380808080004190016b2202248080808000200241086a41f9a6c48000410641002802a0a3c6800011858080800000200241086a41106a220341ea9ec38000410741002802a0a3c6800011858080800000200241d0006a41186a200241086a41186a290000370300200241d0006a41106a2003290000370300200241d0006a41086a200241086a41086a2900003703002002200229000837035041002d00fca3c680001a024002400240412041002802c8a3c68000118180808000002204450d0020042001290000370000200441186a200141186a290000370000200441106a200141106a290000370000200441086a200141086a290000370000200241086a200441204100280290a3c68000118580808000002002200441206a36028c01200220043602880120022003360284012002200241086a36028001200241f4006a20024180016a108182808000200441002802c0a3c6800011808080800000024002400240200228027c220141206a22030d002002410036021020024280808080103702080c010b2003417f4c0d034100210541002d00fca3c680001a200341002802c8a3c68000118180808000002204450d04200241003602102002200436020c2002200336020820014160490d010b200241086a4100412010ab86808000200228020c2104200228021021050b200420056a22032002290350370000200341186a200241d0006a41186a290300370000200341106a200241d0006a41106a290300370000200341086a200241d0006a41086a2903003700002002200541206a2203360210200228027821050240200228020820036b20014f0d00200241086a2003200110ab86808000200228020c2104200228021021030b200420036a2005200110848e8080001a200320016a21012002280208210302402002280274450d00200541002802c0a3c68000118080808000000b200241086a2004200110e4888080000240024020022903084200520d002000420037030820004200370300200041106a428080808080808080807f370300200041186a4200370300200041206a4200370300200041286a4200370300200041306a4200370300200041386a41003602000c010b20002002290310370300200041386a200241c8006a290300370300200041306a200241086a41386a290300370300200041286a200241086a41306a290300370300200041206a200241086a41286a290300370300200041186a200241086a41206a290300370300200041106a200241086a41186a290300370300200041086a200241086a41106a2903003703000b02402003450d00200441002802c0a3c68000118080808000000b20024190016a2480808080000f0b4101412010b280808000000b10ae80808000000b4101200310b280808000000b920601057f23808080800041f0006b22022480808080002002410c6a41f9a6c48000410641002802a0a3c68000118580808000002002410c6a41106a220341d49ec38000410941002802a0a3c6800011858080800000200241306a41186a2002410c6a41186a290000370300200241306a41106a2003290000370300200241306a41086a2002410c6a41086a2900003703002002200229000c37033020022001370360200241e8006a200241e0006a410841002802a8a3c68000118580808000002002200241e0006a41086a3602182002200241e8006a41086a3602102002200241e0006a3602142002200241e8006a36020c200241d4006a2002410c6a10818280800002400240024002400240200228025c220441206a22050d0020024100360214200242808080801037020c0c010b2005417f4c0d024100210641002d00fca3c680001a200541002802c8a3c68000118180808000002203450d0320024100360214200220033602102002200536020c20044160490d010b2002410c6a4100412010ab8680800020022802102103200228021421060b200320066a22052002290330370000200541186a200241306a41186a290300370000200541106a200241306a41106a290300370000200541086a200241306a41086a2903003700002002200641206a2205360214200228025821060240200228020c20056b20044f0d002002410c6a2005200410ab8680800020022802102103200228021421050b200320056a2006200410848e8080001a200520046a2104200228020c210502402002280254450d00200641002802c0a3c68000118080808000000b2002410c6a2003200410dd888080000240024020022d000c0d0020004200370000200041186a4200370000200041106a4200370000200041086a42003700000c010b2000200229000d370000200041186a200241256a290000370000200041106a2002411d6a290000370000200041086a200241156a2900003700000b02402005450d00200341002802c0a3c68000118080808000000b200241f0006a2480808080000f0b10ae80808000000b4101200510b280808000000bee0501057f23808080800041e0006b2202248080808000200241346a41f5a6c48000410441002802a0a3c6800011858080800000200241346a41106a220341f89ec38000411141002802a0a3c6800011858080800000200241086a41186a200241346a41186a290000370300200241086a41106a2003290000370300200241086a41086a200241346a41086a2900003703002002200229003437030820022001280200360254200241d8006a200241d4006a410441002802a8a3c68000118580808000002002200241d4006a41046a3602402002200241d8006a41086a3602382002200241d4006a36023c2002200241d8006a360234200241286a200241346a108182808000024002400240024002402002280230220341206a22040d002002410036023c20024280808080103702340c010b2004417f4c0d024100210541002d00fca3c680001a200441002802c8a3c68000118180808000002201450d032002410036023c200220013602382002200436023420034160490d010b200241346a4100412010ab8680800020022802382101200228023c21050b200120056a22042002290308370000200441186a200241086a41186a290300370000200441106a200241086a41106a290300370000200441086a200241086a41086a2903003700002002200541206a220436023c200228022c21050240200228023420046b20034f0d00200241346a2004200310ab8680800020022802382101200228023c21040b200120046a2005200310848e8080001a200420036a21032002280234210602402002280228450d00200541002802c0a3c68000118080808000000b200241346a2001200310e1888080000240024020022802342204418080808078470d0020004201370204410021040c010b2001200341002802a0a1c6800011848080800000200020022902383702040b2000200436020002402006450d00200141002802c0a3c68000118080808000000b200241e0006a2480808080000f0b10ae80808000000b4101200410b280808000000b910601067f23808080800041e0006b2202248080808000200241306a41f9a6c48000410641002802a0a3c6800011858080800000200241306a41106a220341d49ec38000410941002802a0a3c6800011858080800000200241186a200241306a41186a290000370300200241106a2003290000370300200241086a200241306a41086a2900003703002002200229003037030020022000370350200241d8006a200241d0006a410841002802a8a3c68000118580808000002002200241d0006a41086a36023c2002200241d8006a41086a3602342002200241d0006a3602382002200241d8006a360230200241246a200241306a108182808000024002400240024002400240200228022c220441206a22030d002002410036023820024280808080103702300c010b2003417f4c0d024100210541002d00fca3c680001a200341002802c8a3c68000118180808000002206450d0320024100360238200220063602342002200336023020044160490d010b200241306a4100412010ab8680800020022802342106200228023821050b200620056a22032002290300370000200341186a200241186a290300370000200341106a200241106a290300370000200341086a200241086a2903003700002002200541206a2205360238200228022821030240200228023020056b20044f0d00200241306a2005200410ab8680800020022802342106200228023821050b200620056a2003200410848e8080001a2002280230210702402002280224450d00200341002802c0a3c68000118080808000000b41002d00fca3c680001a412041002802c8a3c68000118180808000002203450d0220032001290000370000200341186a200141186a290000370000200341106a200141106a290000370000200341086a200141086a2900003700002006200520046a2003412041002802e0a1c6800011868080800000200341002802c0a3c680001180808080000002402007450d00200641002802c0a3c68000118080808000000b200241e0006a2480808080000f0b10ae80808000000b4101200310b280808000000b4101412010b280808000000bd90801077f23808080800041e0006b2202248080808000200241306a41f9a6c48000410641002802a0a3c6800011858080800000200241306a41106a220341ea9ec38000410741002802a0a3c6800011858080800000200241186a200241306a41186a290000370300200241106a2003290000370300200241086a200241306a41086a2900003703002002200229003037030041002d00fca3c680001a0240024002400240412041002802c8a3c68000118180808000002204450d0020042000290000370000200441186a200041186a290000370000200441106a200041106a290000370000200441086a200041086a290000370000200241306a200441204100280290a3c68000118580808000002002200441206a36025c20022004360258200220033602542002200241306a360250200241246a200241d0006a108182808000200441002802c0a3c6800011808080800000024002400240200228022c220041206a22030d002002410036023820024280808080103702300c010b2003417f4c0d034100210541002d00fca3c680001a200341002802c8a3c68000118180808000002204450d0420024100360238200220043602342002200336023020004160490d010b200241306a4100412010ab8680800020022802342104200228023821050b200420056a22032002290300370000200341186a200241186a290300370000200341106a200241106a290300370000200341086a200241086a2903003700002002200541206a2203360238200228022821050240200228023020036b20004f0d00200241306a2003200010ab8680800020022802342104200228023821030b200420036a2005200010848e8080001a2002280230210602402002280224450d00200541002802c0a3c68000118080808000000b41002d00fca3c680001a413c41002802c8a3c68000118180808000002205450d03200220053602342002413c3602302005200129030037000020024108360238200128023021074108210502402002280230417c714108470d00200241306a4108410410b182808000200228023821050b200228023420056a20073600002002200541046a2205360238200128023421070240200228023020056b41034b0d00200241306a2005410410b182808000200228023821050b200320006a2103200141086a2108200228023420056a20073600002002200541046a2200360238200128023821010240200228023020006b41034b0d00200241306a2000410410b182808000200228023821000b200228023420006a20013600002002200041046a3602382008200241306a10848d808000200228023021012004200320022802342200200228023841002802e0a1c680001186808080000002402001450d00200041002802c0a3c68000118080808000000b02402006450d00200441002802c0a3c68000118080808000000b200241e0006a2480808080000f0b4101412010b280808000000b10ae80808000000b4101200310b280808000000b4101413c10b280808000000bc70501047f23808080800041e0006b2202248080808000200241346a41f9a6c48000410641002802a0a3c6800011858080800000200241346a41106a220341899fc38000410d41002802a0a3c6800011858080800000200241086a41186a200241346a41186a290000370300200241086a41106a2003290000370300200241086a41086a200241346a41086a2900003703002002200229003437030820022000360254200241d8006a200241d4006a410441002802a8a3c68000118580808000002002200241d4006a41046a3602402002200241d8006a41086a3602382002200241d4006a36023c2002200241d8006a360234200241286a200241346a108182808000024002400240024002402002280230220341206a22040d002002410036023c20024280808080103702340c010b2004417f4c0d024100210541002d00fca3c680001a200441002802c8a3c68000118180808000002200450d032002410036023c200220003602382002200436023420034160490d010b200241346a4100412010ab8680800020022802382100200228023c21050b200020056a22042002290308370000200441186a200241086a41186a290300370000200441106a200241086a41106a290300370000200441086a200241086a41086a2903003700002002200541206a220436023c200228022c21050240200228023420046b20034f0d00200241346a2004200310ab8680800020022802382100200228023c21040b200020046a2005200310848e8080001a200420036a21032002280234210402402002280228450d00200541002802c0a3c68000118080808000000b2001280204220520012802082000200310d58580800002402004450d00200041002802c0a3c68000118080808000000b02402001280200450d00200541002802c0a3c68000118080808000000b200241e0006a2480808080000f0b10ae80808000000b4101200410b280808000000b810801087f23808080800041e0006b2202248080808000200241346a41f5a6c48000410441002802a0a3c6800011858080800000200241346a41106a220341f89ec38000411141002802a0a3c6800011858080800000200241086a41186a200241346a41186a290000370300200241086a41106a2003290000370300200241086a41086a200241346a41086a2900003703002002200229003437030820022000280200360254200241d8006a200241d4006a410441002802a8a3c68000118580808000002002200241d4006a41046a3602402002200241d8006a41086a3602382002200241d4006a36023c2002200241d8006a360234200241286a200241346a1081828080000240024002400240024002402002280230220041206a22030d002002410036023c20024280808080103702340c010b2003417f4c0d024100210441002d00fca3c680001a200341002802c8a3c68000118180808000002205450d032002410036023c200220053602382002200336023420004160490d010b200241346a4100412010ab8680800020022802382105200228023c21040b200520046a22032002290308370000200341186a200241086a41186a290300370000200341106a200241086a41106a290300370000200341086a200241086a41086a2903003700002002200441206a220436023c200228022c21030240200228023420046b20004f0d00200241346a2004200010ab8680800020022802382105200228023c21040b200520046a2003200010848e8080001a2002280234210602402002280228450d00200341002802c0a3c68000118080808000000b20012802082203417f4c0d0041002d00fca3c680001a2003410574410472220741002802c8a3c68000118180808000002208450d02200420006a21092002410036023c200220083602382002200736023420012802042100200220033602282002200241286a360208200241086a200241346a10c08a8080000240024020030d00200228023c2103200228023821070c010b20034105742101200228023c210303400240200228023420036b411f4b0d00200241346a2003412010b182808000200228023c21030b2002280238220720036a22042000290000370000200441086a200041086a290000370000200441106a200041106a290000370000200441186a200041186a2900003700002002200341206a220336023c200041206a2100200141606a22010d000b0b20022802342100200520092007200341002802e0a1c680001186808080000002402000450d00200741002802c0a3c68000118080808000000b02402006450d00200541002802c0a3c68000118080808000000b200241e0006a2480808080000f0b10ae80808000000b4101200310b280808000000b4101200710b280808000000ba20801097f23808080800041e0006b2202248080808000200241346a41f5a6c48000410441002802a0a3c6800011858080800000200241346a41106a220341f89ec38000411141002802a0a3c6800011858080800000200241086a41186a200241346a41186a290000370300200241086a41106a2003290000370300200241086a41086a200241346a41086a2900003703002002200229003437030820022000280200360254200241d8006a200241d4006a410441002802a8a3c68000118580808000002002200241d4006a41046a3602402002200241d8006a41086a3602382002200241d4006a36023c2002200241d8006a360234200241286a200241346a1081828080000240024002400240024002402002280230220041206a22030d002002410036023c20024280808080103702340c010b2003417f4c0d024100210441002d00fca3c680001a200341002802c8a3c68000118180808000002205450d032002410036023c200220053602382002200336023420004160490d010b200241346a4100412010ab8680800020022802382105200228023c21040b200520046a22032002290308370000200341186a200241086a41186a290300370000200341106a200241086a41106a290300370000200341086a200241086a41086a2903003700002002200441206a220436023c200228022c21030240200228023420046b20004f0d00200241346a2004200010ab8680800020022802382105200228023c21040b200520046a2003200010848e8080001a2002280234210602402002280228450d00200341002802c0a3c68000118080808000000b20012802082203417f4c0d0041002d00fca3c680001a2003410574410472220741002802c8a3c68000118180808000002208450d02200420006a21092002410036023c20022008360238200220073602342001280204210a200220033602282002200241286a360208200241086a200241346a10c08a8080000240024020030d00200228023c2103200228023821080c010b20034105742107200228023c2103200a210003400240200228023420036b411f4b0d00200241346a2003412010b182808000200228023c21030b2002280238220820036a22042000290000370000200441086a200041086a290000370000200441106a200041106a290000370000200441186a200041186a2900003700002002200341206a220336023c200041206a2100200741606a22070d000b0b20022802342100200520092008200341002802e0a1c680001186808080000002402000450d00200841002802c0a3c68000118080808000000b02402006450d00200541002802c0a3c68000118080808000000b02402001280200450d00200a41002802c0a3c68000118080808000000b200241e0006a2480808080000f0b10ae80808000000b4101200310b280808000000b4101200710b280808000000bc60906017f037e037f017e017f027e2380808080004190016b2202248080808000200141106a29030021032001290308210420013502002105200241286a41f9a6c48000410641002802a0a3c6800011858080800000200241286a41106a220641ea9ec38000410741002802a0a3c6800011858080800000200241186a200241286a41186a290000370300200241106a2006290000370300200241086a200241286a41086a2900003703002002200229002837030041002d00fca3c680001a024002400240412041002802c8a3c68000118180808000002207450d0020072000290000370000200741186a200041186a290000370000200741106a200041106a290000370000200741086a200041086a290000370000200241286a200741204100280290a3c68000118580808000002002200741206a36028c01200220073602880120022006360284012002200241286a36028001200241f4006a20024180016a108182808000200741002802c0a3c6800011808080800000024002400240200228027c220041206a22060d002002410036023020024280808080103702280c010b2006417f4c0d034100210841002d00fca3c680001a200641002802c8a3c68000118180808000002207450d04200241003602302002200736022c2002200636022820004160490d010b200241286a4100412010ab86808000200228022c2107200228023021080b200720086a22062002290300370000200641186a200241186a290300370000200641106a200241106a290300370000200641086a200241086a2903003700002002200841206a2206360230200228027821080240200228022820066b20004f0d00200241286a2006200010ab86808000200228022c2107200228023021060b200720066a2008200010848e8080001a200620006a21002002280228210602402002280274450d00200841002802c0a3c68000118080808000000b200241286a2007200010e488808000420021090240024020022903284200520d00200241206a4100360200200242003703180c010b200241086a200241d0006a290300370300200241106a200241d8006a290300370300200241186a200241e0006a290300370300200241206a200241e8006a2802003602002002200241286a41206a290300370300200241ec006a280200210a200229033021090b4200210b0240024020054200520d00200241386a4200370300200241306a420037030020024200370328428080808080808080807f21030c010b200241386a200141286a290300370300200241306a200141206a290300370300200220012903183703282004210b0b200241106a200241286a41106a22012903002205370300200241086a200241286a41086a2208290300220437030020022002290328220c370300200241286a41186a20033703002001200b370300200241286a41206a200c370300200241d0006a2004370300200241d8006a2005370300200241e0006a200241186a290300370300200241e8006a200241206a280200360200200241ec006a200a360200200220093703302002420137032820082007200010f58780800002402006450d00200741002802c0a3c68000118080808000000b20024190016a2480808080000f0b4101412010b280808000000b10ae80808000000b4101200610b280808000000bf70a03057f047e037f2380808080004190016b2201248080808000200141086a41f9a6c48000410641002802a0a3c6800011858080800000200141086a41106a220241ea9ec38000410741002802a0a3c6800011858080800000200141d0006a41186a200141086a41186a290000370300200141d0006a41106a2002290000370300200141d0006a41086a200141086a41086a2900003703002001200129000837035041002d00fca3c680001a02400240024002400240412041002802c8a3c68000118180808000002203450d0020032000290000370000200341186a200041186a290000370000200341106a200041106a290000370000200341086a200041086a290000370000200141086a200341204100280290a3c68000118580808000002001200341206a36028c01200120033602880120012002360284012001200141086a36028001200141f4006a20014180016a108182808000200341002802c0a3c6800011808080800000024002400240200128027c220041206a22020d002001410036021020014280808080103702080c010b2002417f4c0d034100210441002d00fca3c680001a200241002802c8a3c68000118180808000002203450d04200141003602102001200336020c2001200236020820004160490d010b200141086a4100412010ab86808000200128020c2103200128021021040b200320046a22022001290350370000200241186a200141d0006a41186a290300370000200241106a200141d0006a41106a290300370000200241086a200141d0006a41086a2903003700002001200441206a2202360210200128027821040240200128020820026b20004f0d00200141086a2002200010ab86808000200128020c2103200128021021020b200320026a2004200010848e8080001a200220006a21002001280208210502402001280274450d00200441002802c0a3c68000118080808000000b200141086a2003200010e48880800042002106024020012903084200520d00200141e0006a4200370300200141d8006a420037030020014200370350428080808080808080807f210742002108420021090c040b200141d8006a200141306a290300370300200141d0006a41106a200141386a2903003703002001200141286a290300370350200141206a2903002107200141086a41106a2903002108200141c4006a2902002106200141cc006a280200210420012903102109200141c0006a2802002202450d032002417f6a21020c040b4101412010b280808000000b10ae80808000000b4101200210b280808000000b410021020240417f4100280284a4c68000220a410147200a41014b1b220a417f460d00200a41ff01710d010b410021024100280290a1c68000210a410028028ca1c68000210b4100280280a4c68000210c200141c0006a42003702002001413c6a418097c38000360200200141386a4101360200200141306a410f3602002001412c6a41a89cc38000360200200141206a41c49ac38000ad4280808080b00b84370200200141146a41aa9bc38000ad4280808080c00184370200200141a09cc38000360234200141013602282001410036021c2001410036021020014281808080d0ce01370208200b41ecf2c08000200c410246220c1b200141086a200a41d4f2c08000200c1b280210118480808000000b200141206a2007370300200141086a41106a2008370300200141286a2001290350370300200141cc006a2004360200200141c4006a2006370200200141c0006a2002360200200141306a200141d0006a41086a290300370300200141386a200141d0006a41106a2903003703002001200937031020014201370308200141086a41086a2003200010f58780800002402005450d00200341002802c0a3c68000118080808000000b20014190016a2480808080000bfc0903057f037e027f2380808080004190016b2202248080808000200241c0006a41f9a6c48000410641002802a0a3c6800011858080800000200241c0006a41106a220341ea9ec38000410741002802a0a3c6800011858080800000200241186a200241c0006a41186a290000370300200241106a2003290000370300200241086a200241c0006a41086a2900003703002002200229004037030041002d00fca3c680001a024002400240412041002802c8a3c68000118180808000002204450d0020042000290000370000200441186a200041186a290000370000200441106a200041106a290000370000200441086a200041086a290000370000200241c0006a200441204100280290a3c68000118580808000002002200441206a36023c20022004360238200220033602342002200241c0006a360230200241246a200241306a108182808000200441002802c0a3c6800011808080800000024002400240200228022c220041206a22030d002002410036024820024280808080103702400c010b2003417f4c0d034100210541002d00fca3c680001a200341002802c8a3c68000118180808000002204450d0420024100360248200220043602442002200336024020004160490d010b200241c0006a4100412010ab8680800020022802442104200228024821050b200420056a22032002290300370000200341186a200241186a290300370000200341106a200241106a290300370000200341086a200241086a2903003700002002200541206a2203360248200228022821050240200228024020036b20004f0d00200241c0006a2003200010ab8680800020022802442104200228024821030b200420036a2005200010848e8080001a200320006a21002002280240210602402002280224450d00200541002802c0a3c68000118080808000000b200241c0006a2004200010e488808000420021070240024020022903404200520d0041002103200241186a4100360200200241106a4200370300200241086a420037030020024200370300428080808080808080807f210842002109410021050c010b200241086a200241e8006a290300370300200241106a200241f0006a290300370300200241186a200241f8006a2802003602002002200241e0006a290300370300200241c0006a41186a2903002108200241c0006a41106a290300210720024184016a280200210a20024180016a2802002103200241fc006a2802002105200229034821090b024002402005200372220b0d00200241c9006a2001290000370000200241e1006a200141186a290000370000200241d9006a200141106a290000370000200241d1006a200141086a290000370000200241033a0048200241163a0040200241c0006a108e8a808000410121010c010b200541016a2205417f20051b21010b200b4100472105200241c0006a41186a2008370300200241c0006a41106a2007370300200241e0006a200229030037030020024184016a200a36020020024180016a2003360200200241fc006a2001360200200241e8006a200241086a290300370300200241f0006a200241106a290300370300200241f8006a200241186a2802003602002002200937034820024201370340200241c0006a41086a2004200010f58780800002402006450d00200441002802c0a3c68000118080808000000b20024190016a24808080800020050f0b4101412010b280808000000b10ae80808000000b4101200310b280808000000b960501057f23808080800041e0006b2201248080808000200141306a41f9a6c48000410641002802a0a3c6800011858080800000200141306a41106a220241d49ec38000410941002802a0a3c6800011858080800000200141186a200141306a41186a290000370300200141106a2002290000370300200141086a200141306a41086a2900003703002001200129003037030020012000370350200141d8006a200141d0006a410841002802a8a3c68000118580808000002001200141d0006a41086a36023c2001200141d8006a41086a3602342001200141d0006a3602382001200141d8006a360230200141246a200141306a10818280800002400240024002400240200128022c220341206a22040d002001410036023820014280808080103702300c010b2004417f4c0d024100210541002d00fca3c680001a200441002802c8a3c68000118180808000002202450d0320014100360238200120023602342001200436023020034160490d010b200141306a4100412010ab8680800020012802342102200128023821050b200220056a22042001290300370000200441186a200141186a290300370000200441106a200141106a290300370000200441086a200141086a2903003700002001200541206a2204360238200128022821050240200128023020046b20034f0d00200141306a2004200310ab8680800020012802342102200128023821040b200220046a2005200310848e8080001a200420036a21032001280230210402402001280224450d00200541002802c0a3c68000118080808000000b2002200341002802a0a1c680001184808080000002402004450d00200241002802c0a3c68000118080808000000b200141e0006a2480808080000f0b10ae80808000000b4101200410b280808000000bf60501047f23808080800041e0006b2201248080808000200141306a41f9a6c48000410641002802a0a3c6800011858080800000200141306a41106a220241ea9ec38000410741002802a0a3c6800011858080800000200141186a200141306a41186a290000370300200141106a2002290000370300200141086a200141306a41086a2900003703002001200129003037030041002d00fca3c680001a024002400240412041002802c8a3c68000118180808000002203450d0020032000290000370000200341186a200041186a290000370000200341106a200041106a290000370000200341086a200041086a290000370000200141306a200341204100280290a3c68000118580808000002001200341206a36025c20012003360258200120023602542001200141306a360250200141246a200141d0006a108182808000200341002802c0a3c6800011808080800000024002400240200128022c220041206a22020d002001410036023820014280808080103702300c010b2002417f4c0d034100210441002d00fca3c680001a200241002802c8a3c68000118180808000002203450d0420014100360238200120033602342001200236023020004160490d010b200141306a4100412010ab8680800020012802342103200128023821040b200320046a22022001290300370000200241186a200141186a290300370000200241106a200141106a290300370000200241086a200141086a2903003700002001200441206a2202360238200128022821040240200128023020026b20004f0d00200141306a2002200010ab8680800020012802342103200128023821020b200320026a2004200010848e8080001a200220006a21002001280230210202402001280224450d00200441002802c0a3c68000118080808000000b2003200041002802a0a1c680001184808080000002402002450d00200341002802c0a3c68000118080808000000b200141e0006a2480808080000f0b4101412010b280808000000b10ae80808000000b4101200210b280808000000b170041b299c38000410f41b89dc3800010f880808000000b170041b299c38000410f41c89dc3800010f880808000000b170041b299c38000410f41d89dc3800010f880808000000b170041b299c38000410f41e89dc3800010f880808000000b170041b299c38000410f41f89dc3800010f880808000000b170041b299c38000410f41889ec3800010f880808000000b170041b299c38000410f41989ec3800010f880808000000b170041b299c38000410f41a89ec3800010f880808000000bae08020a7f017e23808080800041306b2203248080808000024020002802402204418080808078460d0002400240200041cc006a28020022050d0041002105410021060c010b200320053602242003410036022020032005360214200341003602102003200041d0006a280200220536022820032005360218200041d4006a2802002106410121050b2003200636022c2003200536021c2003200536020c2003410c6a10928d8080002004450d00200041c4006a28020041002802c0a3c68000118080808000000b2000418080808078360240024002400240024002400240024020002802200d00200228020021052002280208210620004100360220200041286a2204200429030042017c37030020002802300d014100210420004100360230200041386a22072007290300410020062005418080808078461bad7c3703000240024020002d00d8020d000c010b024020002802a4012208450d00200041a8016a28020021090340200841a07e6a210a200841a4136a210420082f01aa14220b410c6c2105417f2107024002400340024020050d00200b21070c020b200441086a2106200441046a210c200541746a2105200741016a2107200a41e0016a210a2004410c6a2104417f41b99ec38000200c28020020062802002206411020064110491b10888e808000220c411020066b200c1b220641004720064100481b22064101460d000b200641ff0171450d010b2009450d022009417f6a2109200820074102746a41ac146a28020021080c010b0b200a41086a280200200a2802002204200441054b22041b2205450d0402402005412c6c200a41046a2205280200200520041b6a41546a2204280208220541014b0d0020042802042107200428021c210602402004280200220a450d0020062007460d010b2003200441106a36020c2003410c6a200a20072006108e858080002004200636020420044101360200200428020821050b02400240024002402005417e6a2205410220054102491b0e03020100020b200441106a21060c020b20002802000d07200041003602002000200029030842017c37030820002802100d08200041003602100c020b2004410c6a21060b20002802000d07200641086a350200210d41002105200041003602002000200029030842017c37030820002802100d0820004100360210200041186a22042004290300200d7c37030041012104410121070240200641086a2802004104490d00200641046a2802002800002105410021070b2007450d010b41012104417f21050b200041a4016a2001200220042005109085808000200341306a2480808080000f0b41ecb8c28000108781808000000b41dcb8c28000108781808000000b41fcb8c2800041fc0041f8bac2800010a181808000000b41ccb8c28000108781808000000b41bcb8c28000108781808000000b41ccb8c28000108781808000000b41bcb8c28000108781808000000bd51501137f23808080800041f0036b2204248080808000024002400240024002402001280240418080808078460d00200041186a20014190016a290000370000200041106a20014188016a290000370000200041086a20014180016a2900003700002000200141f8006a290000370000410121050c010b41002d00fca3c680001a20012802a4012106200141a0016a28020021072001419c016a2802002108200141ac016a2802002109200141a8016a280200210a2001280298012105410141002802c8a3c6800011818080800000220b450d01200b41003a000020044180016a41186a220c419380c6800041014100280298a3c680001185808080000020044194016a410036020020044201370288012004200b360284012004410136028001200441003602c00120044280808080c0003702b8014104210b0240024020050d004100210d0c010b4100210d2007450d00200441ac026a210e200441c4026a210f200441a0026a41017221104100210d4100210b034002400240200b450d002008211120052112200b21050c010b4100211102402008450d002008210b024020084107712212450d000340200b417f6a210b20052802ac0921052012417f6a22120d000b0b20084108490d00034020052802ac092802ac092802ac092802ac092802ac092802ac092802ac092802ac092105200b41786a220b0d000b0b410021120b0240201120052f01aa09490d00034020052802a008220b450d06201241016a211220052f01a8092111200b21052011200b2f01aa094f0d000b0b201141016a210802400240024020120d002005210b0c010b200520084102746a41ac096a280200210b410021082012417f6a2213450d002012417e6a2114024020134107712212450d0003402013417f6a2113200b2802ac09210b2012417f6a22120d000b0b024020144107490d000340200b2802ac092802ac092802ac092802ac092802ac092802ac092802ac092802ac09210b201341786a22130d000b0b2005450d010b2005201141e0006c6a22112802082112201128020021052004201128020422133602e001200420053602dc01200441003602d801200420133602d001200420053602cc01200441003602c80120042012410020051b3602e4012004200541004722053602d401200420053602c401200441a0026a2002201141d4006a2205200441c4016a200310888d808000200441286a41186a2212201041186a290000370300200441286a41106a2213201041106a290000370300200441286a41086a220d201041086a290000370300200441e8016a41086a200f41086a290200370300200441e8016a41106a200f41106a290200370300200441e8016a41186a200f41186a290200370300200441e8016a41206a200f41206a290200370300200441e8016a41286a200f41286a290200370300200441e8016a41306a200f41306a290200370300200420102900003703282004200f2902003703e80120042d00a0022111200441fc026a200510a08580800020044180016a200441e8016a10ac8d8080000240024020110d00200441a0026a41086a2214200441fc026a41086a280200360200200420042902fc023703a00241002d00fca3c680001a2004410036029003200442808080801037028803412041002802c8a3c68000118180808000002205450d0820052004290328370000200541186a22152012290300370000200541106a22162013290300370000200541086a2213200d29030037000020044188036a4100412010b182808000200428028c0320044188036a41086a2212280200220d6a22112005290000370000201141086a2013290000370000201141106a2016290000370000201141186a20152900003700002012200d41206a360200200541002802c0a3c6800011808080800000200e41086a2012280200360200200e200429028803370200024020042802c001220520042802b801470d00200441b8016a200510a68680800020042802c00121050b20042802bc01200541186c6a221120042903a002370200201141086a2014290300370200201141106a200441a0026a41106a290300370200200541016a210d0c010b024020042802c001220520042802b801470d00200441b8016a200510a68680800020042802c00121050b20042802bc01200541186c6a220520042902fc02370200200541808080807836020c200541086a200441fc026a41086a28020036020020042802c00141016a210d0b2004200d3602c001410021052007417f6a22070d010b0b20042802bc01210b0b2004200b3602e403200441c0036a41286a200b200d41186c6a36020020042009410020061b3602e0032004200a3602dc03200420063602d803200441003602d4032004200641004722053602d0032004200a3602cc03200420063602c803200441003602c403200420053602c003200441a0026a2002200441c0036a200310868d808000200441286a41086a200441a0026a41086a290200370300200441286a41106a200441a0026a41106a290200370300200441286a41186a200441a0026a41186a29020037030020044188036a41086a200441a0026a41286a29020037030020044188036a41106a200441a0026a41306a29020037030020044188036a41186a200441a0026a41386a29020037030020044188036a41206a200441a0026a41c0006a29020037030020044188036a41286a200441a0026a41c8006a29020037030020044188036a41306a200441a0026a41d0006a290200370300200420042902a002370328200420042902c0023703880320044180016a20044188036a10ac8d808000200441286a41d0006a20044180016a41306a290200370300200441286a41c8006a20044180016a41286a290200370300200441286a41c0006a20044180016a41206a290200370300200441286a41386a200c290200370300200441286a41306a20044180016a41106a290200370300200441286a41286a20044180016a41086a2902003703002004200429028001370348024020042802c001220b450d0020042802bc012105034002402005280200450d00200541046a28020041002802c0a3c68000118080808000000b02402005410c6a2802002211418080808078460d002011450d00200541106a28020041002802c0a3c68000118080808000000b200541186a2105200b417f6a220b0d000b0b200141c0006a2113200441286a41206a2105024020042802b801450d0020042802bc0141002802c0a3c68000118080808000000b200441086a41086a220b200441286a41086a290300370300200441086a41106a2211200441286a41106a290300370300200441086a41186a2212200441286a41186a29030037030020042004290328370308200441a0026a41306a200541306a290200370300200441a0026a41286a200541286a290200370300200441a0026a41206a200541206a290200370300200441a0026a41186a200541186a290200370300200441a0026a41106a200541106a290200370300200441a0026a41086a200541086a290200370300200420052902003703a002200441e0026a200b290300370300200441e8026a2011290300370300200441f0026a2012290300370300200420042903083703d8022013200441a0026a41d80010848e8080001a200041186a2012290300370000200041106a2011290300370000200041086a200b29030037000020002004290308370000410021050b200020053a0020200441f0036a2480808080000f0b4101410110b280808000000b419cd0c2800010a081808000000b4101412010b280808000000bd905020a7f017e23808080800041106b2204248080808000024002400240024002400240024020012802a40122050d00410021060c010b200141a8016a28020021070340200541a07e6a2108200541a4136a210620052f01aa142209410c6c210a417f210b0240024003400240200a0d002009210b0c020b200641086a210c200641046a210d200a41746a210a200b41016a210b200841e0016a21082006410c6a2106417f2002200d2802002003200c280200220c2003200c491b10888e808000220d2003200c6b200d1b220c410047200c4100481b220c4101460d000b200c41ff0171450d010b024020070d00410021060c030b2007417f6a21072005200b4102746a41ac146a28020021050c010b0b200841086a28020020082802002206200641054b22061b220a450d010240200a412c6c200841046a220a280200200a20061b6a41546a2206280208220a41014b0d002006280204210c200628021c210302402006280200220b450d002003200c460d010b2004200641106a36020c2004410c6a200b200c2003108e8580800020062003360204200641013602002006280208210a0b02400240024002400240200a417e6a220a4102200a4102491b0e03000301000b2006410c6a21060c010b200641106a21060b20012802000d04200641086a350200210e200141003602002001200129030842017c37030820012802100d0520014100360210200141186a220a200a290300200e7c370300200641086a280200210a200641046a28020021060c010b20012802000d0541002106200141003602002001200129030842017c37030820012802100d06200141003602100b20002006360204200041086a200a360200410121060b20002006360200200441106a2480808080000f0b41fcb8c2800041fc0041f8bac2800010a181808000000b41ccb8c28000108781808000000b41bcb8c28000108781808000000b41ccb8c28000108781808000000b41bcb8c28000108781808000000bab0301037f23808080800041206b2203248080808000410121040240200028020c220541c000490d0041022104200541808001490d00410441052005418080808004491b21040b41002d00fca3c680001a0240200441002802c8a3c68000118180808000002205450d00200341003602102003200536020c2003200436020820032000410c6a360214200341146a200341086a10c08a808000024002402000280200418080808078470d00200341146a2000280204200041086a28020010b4828080000c010b200341146a2000280204200028020810e6848080000b2003280218210502402003280208200328021022006b200328021c22044f0d00200341086a2000200410b182808000200328021021000b200328020c20006a2005200410848e8080001a2003200020046a220036021002402003280214450d00200541002802c0a3c6800011808080800000200328021021000b2003280208210420012002200328020c2205200041002802e0a1c680001186808080000002402004450d00200541002802c0a3c68000118080808000000b200341206a2480808080000f0b4101200410b280808000000bba0301087f23808080800041106b220324808080800041002d00fca3c680001a02400240410141002802c8a3c68000118180808000002204450d002003410036020c200320043602082003410136020441002d00fca3c680001a412041002802c8a3c68000118180808000002204450d0120042000290001370000200441186a2205200041196a290000370000200441106a2206200041116a290000370000200441086a2207200041096a290000370000200341046a4100412010b18280800020032802082208200328020c22096a220a2004290000370000200a41086a2007290000370000200a41106a2006290000370000200a41186a20052900003700002003200941206a220a36020c200441002802c0a3c680001180808080000020002d00002100024020032802042204200a470d00200341046a200a410110b1828080002003280208210820032802042104200328020c210a0b2008200a6a20003a0000200120022008200a41016a41002802e0a1c680001186808080000002402004450d00200841002802c0a3c68000118080808000000b200341106a2480808080000f0b4101410110b280808000000b4101412010b280808000000bc70201057f23808080800041106b2203248080808000410521044100210502400240200028020022060e03010000010b41012105410121040b41002d00fca3c680001a0240200441002802c8a3c68000118180808000002207450d002003200736020820032004360204410121040240024002400240024020060e03000201000b200741003a0000410121042003410136020c200028020421064100210002402005450d00200341046a4101410410b182808000200328020445210020032802082107200328020c21040b200720046a2006360000200120022007200441046a41002802e0a1c68000118680808000002000450d020c030b410221040b200720043a0000200120022007410141002802e0a1c68000118680808000000b200741002802c0a3c68000118080808000000b200341106a2480808080000f0b4101200410b280808000000bca0101057f23808080800041206b220124808080800041002102200141003602102001428080808010370208200141146a10958880800041012103200128021821040240200128021c2205450d00200141086a4100200510b182808000200128020c2103200128021021020b200320026a2004200510848e8080001a2001200220056a36021002402001280214450d00200441002802c0a3c68000118080808000000b20002001290208370200200041086a200141086a41086a280200360200200141206a2480808080000b900201047f23808080800041106b2202248080808000200128020041027441f8adc380006a28020021030240024020012d00084116470d00200141106a10a18a80800021040c010b200141086a10a98c80800021040b02400240417f417f200420036a41016a220420042003491b2203200141e0006a2802004105746a41046a220420042003491b2203417f4c0d0041002d00fca3c680001a200341002802c8a3c68000118180808000002204450d01200241046a41086a2205410036020020022004360208200220033602042001200241046a10ef87808000200041086a200528020036020020002002290204370200200241106a2480808080000f0b10ae80808000000b4101200310b280808000000bcb0401097f23808080800041206b220224808080800041002d00fca3c680001a02400240410141002802c8a3c68000118180808000002203450d00200341053a000041002d00fca3c680001a2002410036021c2002428080808010370214412041002802c8a3c68000118180808000002204450d0120044200370000200441186a22054200370000200441106a22064200370000200441086a22074200370000200241146a4100412010b1828080002002280218200241146a41086a220828020022096a220a2004290000370000200a41086a2007290000370000200a41106a2006290000370000200a41186a20052900003700002008200941206a360200200441002802c0a3c6800011808080800000200241086a41086a220a2008280200360200200220022902143703080240200128020822042001280200470d0020012004109d86808000200128020821040b2001280204200441e8006c6a220442c194a6a793ccc3a857370328200442a5e9e3ab9e929adc2c37031020044109360244200441d49ec38000360240200441f581808000360238200441ef808080003602202004410136020c2004200336020820044281808080103703002004200229030837034820042000290200370254200441306a42ab8bffbed784ffa5937f370300200441186a4293888c8f89fdc6ec9e7f370300200441d0006a200a280200360200200441dc006a200041086a280200360200200441013a00602001200128020841016a360208200241206a2480808080000f0b4101410110b280808000000b4101412010b280808000000bdd0301057f23808080800041306b220224808080800041002d00fca3c680001a02400240410141002802c8a3c68000118180808000002203450d00200341023a00002002410036022020024280808080800137021841002d00fca3c680001a410441002802c8a3c68000118180808000002204450d01200241246a41086a220541003602002002200436022820024104360224200241186a200241246a108187808000200241086a41086a22062005280200360200200220022902243703080240200128020822042001280200470d0020012004109d86808000200128020821040b2001280204200441e8006c6a220442a09acee28b90aca44d370328200442c4ccab9c81ebb4b8db0037031020044108360244200441dd9ec380003602402004419d82808000360238200441f0818080003602202004410136020c2004200336020820044281808080103703002004200229030837034820042000290200370254200441306a42f68ebee6f7f7fefdac7f370300200441186a42a4d2928ecac0faa432370300200441d0006a2006280200360200200441dc006a200041086a280200360200200441013a00602001200128020841016a360208200241306a2480808080000f0b4101410110b280808000000b4101410410b280808000000be90503067f017e027f23808080800041d0006b2201248080808000200141286a419b9fc38000410a41a59fc380004120418097c38000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d0020024285cbd9e891b2d0c161370308200241c59fc38000360220200241f18180800036021820024101360204200241ab99c38000360200200241106a4282d2add5d4f1deea6a370300200241386a4100360200200241246a410136020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a22032001411c6a220241086a28020036020020012002290200370300200128021021042001280214210520012802182106200129022c21072001280228210820014100360218200142808080808001370210200141106a410010a4868080002001280214200128021841386c6a2202420437022c20024206370224200241cae4c48000360220200241003602182002419e82808000360210200242cdec93ccd5b69cacdf00370308200242be8af3e3f0cdac8456370300200128021821092001280214210220012001280210360248200120023602402001200236024420012002200941386c6a41386a36024c200141346a200141c0006a10f9868080002008418080808078460d012001200436021820012005360210200120053602142001200520064105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002008360238200041003a000020002001290300370350200041d8006a20032802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000bd70301057f23808080800041206b220224808080800041002d00fca3c680001a02400240410141002802c8a3c68000118180808000002203450d00200341023a000041002d00fca3c680001a410441002802c8a3c68000118180808000002204450d012002410c6a41086a22054100360200200220043602102002410436020c200241003602182002200241186a36021c2002411c6a2002410c6a10c08a808000200241086a220620052802003602002002200229020c3703000240200128020822042001280200470d0020012004109d86808000200128020821040b2001280204200441e8006c6a220442fb92fda592a6fcd31c370328200442c4ccab9c81ebb4b8db0037031020044105360244200441e59ec380003602402004419f82808000360238200441f0818080003602202004410136020c2004200336020820044281808080103703002004200229030037034820042000290200370254200441306a42b584d6d28c82acc02f370300200441186a42a4d2928ecac0faa432370300200441d0006a2006280200360200200441dc006a200041086a280200360200200441013a00602001200128020841016a360208200241206a2480808080000f0b4101410110b280808000000b4101410410b280808000000b850601057f23808080800041e0006b220224808080800041002d00fca3c680001a02400240410141002802c8a3c68000118180808000002203450d00200341023a0000200241206a428080808080808080807f370300200241286a4200370300200241306a4200370300200241386a4200370300200241c0006a4200370300200241c8006a4100360200200242003703182002420037031041002d00fca3c680001a413c41002802c8a3c68000118180808000002204450d01200220043602582002413c360254200442003700002002410836025c200228024021054108210402402002280254417c714108470d00200241d4006a4108410410b182808000200228025c21040b200228025820046a20053600002002200441046a220436025c200228024421050240200228025420046b41034b0d00200241d4006a2004410410b182808000200228025c21040b200241106a41086a2106200228025820046a20053600002002200441046a220436025c200228024821050240200228025420046b41034b0d00200241d4006a2004410410b182808000200228025c21040b200228025820046a2005360000200241d4006a41086a2205200441046a3602002006200241d4006a10848d808000200241086a22062005280200360200200220022902543703000240200128020822042001280200470d0020012004109d86808000200128020821040b2001280204200441e8006c6a220442f8b3f08d85bca5b6e300370328200442c4ccab9c81ebb4b8db0037031020044107360244200441ea9ec38000360240200441a082808000360238200441f0818080003602202004410136020c2004200336020820044281808080103703002004200229030037034820042000290200370254200441306a4291ba8cfeb2d6b48568370300200441186a42a4d2928ecac0faa432370300200441d0006a2006280200360200200441dc006a200041086a280200360200200441013a00602001200128020841016a360208200241e0006a2480808080000f0b4101410110b280808000000b4101413c10b280808000000bd90301057f23808080800041206b220224808080800041002d00fca3c680001a02400240410141002802c8a3c68000118180808000002203450d00200341023a000041002d00fca3c680001a410441002802c8a3c68000118180808000002204450d012002410c6a41086a22054100360200200220043602102002410436020c200241003602182002200241186a36021c2002411c6a2002410c6a10c08a808000200241086a220620052802003602002002200229020c3703000240200128020822042001280200470d0020012004109d86808000200128020821040b2001280204200441e8006c6a220442fa89d3f6b7fdfb8f9a7f370328200442c4ccab9c81ebb4b8db0037031020044107360244200441f19ec38000360240200441a182808000360238200441f0818080003602202004410136020c2004200336020820044281808080103703002004200229030037034820042000290200370254200441306a42fd8281afbeafaef5807f370300200441186a42a4d2928ecac0faa432370300200441d0006a2006280200360200200441dc006a200041086a280200360200200441013a00602001200128020841016a360208200241206a2480808080000f0b4101410110b280808000000b4101410410b280808000000be90503067f017e027f23808080800041d0006b2201248080808000200141286a419b9fc38000410a41a59fc380004120418097c38000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d00200242ef8683cfe1dddaca63370308200241c59fc38000360220200241f68180800036021820024101360204200241ab99c38000360200200241106a4284b2a2d692ae8580b57f370300200241386a4100360200200241246a410136020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a22032001411c6a220241086a28020036020020012002290200370300200128021021042001280214210520012802182106200129022c21072001280228210820014100360218200142808080808001370210200141106a410010a4868080002001280214200128021841386c6a2202420437022c20024206370224200241cae4c4800036022020024100360218200241a282808000360210200242afc2c78afea4cdde6c370308200242c9aaa6b382c5ade30a370300200128021821092001280214210220012001280210360248200120023602402001200236024420012002200941386c6a41386a36024c200141346a200141c0006a10f9868080002008418080808078460d012001200436021820012005360210200120053602142001200520064105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002008360238200041003a000020002001290300370350200041d8006a20032802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000bd80301057f23808080800041206b220224808080800041002d00fca3c680001a02400240410141002802c8a3c68000118180808000002203450d00200341053a000041002d00fca3c680001a410441002802c8a3c68000118180808000002204450d012002410c6a41086a22054100360200200220043602102002410436020c200241003602182002200241186a36021c2002411c6a2002410c6a10c08a808000200241086a220620052802003602002002200229020c3703000240200128020822042001280200470d0020012004109d86808000200128020821040b2001280204200441e8006c6a220442d6e1dd9492e0e8e8c900370328200442d7c9cb8fc1cf97db3e37031020044111360244200441f89ec38000360240200441a382808000360238200441b5808080003602202004410136020c2004200336020820044281808080103703002004200229030037034820042000290200370254200441306a42efbbaa8aaae9fdc9ab7f370300200441186a42e88488d0c0e3aebc13370300200441d0006a2006280200360200200441dc006a200041086a280200360200200441013a00602001200128020841016a360208200241206a2480808080000f0b4101410110b280808000000b4101410410b280808000000beb0503067f017e027f23808080800041d0006b2201248080808000200141286a419b9fc38000410a41a59fc380004120418097c38000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d00200242c4ccab9c81ebb4b8db00370308200241c59fc38000360220200241f08180800036021820024101360204200241ab99c38000360200200241106a42a4d2928ecac0faa432370300200241386a4100360200200241246a410136020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a22032001411c6a220241086a28020036020020012002290200370300200128021021042001280214210520012802182106200129022c21072001280228210820014100360218200142808080808001370210200141106a410010a4868080002001280214200128021841386c6a2202420437022c20024206370224200241cae4c4800036022020024100360218200241a4828080003602102002429bc594f9e4cbe78bb87f37030820024299f3bffc9a86c097ea00370300200128021821092001280214210220012001280210360248200120023602402001200236024420012002200941386c6a41386a36024c200141346a200141c0006a10f9868080002008418080808078460d012001200436021820012005360210200120053602142001200520064105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002008360238200041003a000020002001290300370350200041d8006a20032802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000b950301037f41002d00fca3c680001a02400240410141002802c8a3c68000118180808000002202450d00200241023a000041002d00fca3c680001a412841002802c8a3c68000118180808000002203450d012003420037001820034200370000200341206a428080808080808080807f370000200341106a4200370000200341086a42003700000240200128020822042001280200470d0020012004109d86808000200128020821040b2001280204200441e8006c6a220442c4daf0e7d4cf86cca87f370328200442c4ccab9c81ebb4b8db00370310200441283602502004200336024c200442878080808005370244200441ea9ec380003602402004419982808000360238200441f0818080003602202004410136020c20042002360208200442818080801037030020042000290200370254200441013a0060200441306a42e1be86f0e3e6afc202370300200441186a42a4d2928ecac0faa432370300200441dc006a200041086a2802003602002001200128020841016a3602080f0b4101410110b280808000000b4101412810b280808000000be70301057f23808080800041206b220224808080800041002d00fca3c680001a02400240410141002802c8a3c68000118180808000002203450d00200341023a000041002d00fca3c680001a410441002802c8a3c68000118180808000002204450d012002410c6a41086a22054100360200200220043602102002410436020c200241003602182002200241186a36021c2002411c6a2002410c6a10c08a808000410841002002410c6a10d385808000200241086a220620052802003602002002200229020c3703000240200128020822042001280200470d0020012004109d86808000200128020821040b2001280204200441e8006c6a2204428f9acc9be4ab9dde977f370328200442c4ccab9c81ebb4b8db0037031020044105360244200441969fc38000360240200441a582808000360238200441f0818080003602202004410136020c2004200336020820044281808080103703002004200229030037034820042000290200370254200441306a42ba98e4d187d5e0d005370300200441186a42a4d2928ecac0faa432370300200441d0006a2006280200360200200441dc006a200041086a280200360200200441013a00602001200128020841016a360208200241206a2480808080000f0b4101410110b280808000000b4101410410b280808000000beb0503067f017e027f23808080800041d0006b2201248080808000200141286a419b9fc38000410a41a59fc380004120418097c38000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d002002428291fa9e999aa9d246370308200241c59fc38000360220200241ee8180800036021820024101360204200241ab99c38000360200200241106a42bc90c1fdf28f8bb0ff00370300200241386a4100360200200241246a410136020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a22032001411c6a220241086a28020036020020012002290200370300200128021021042001280214210520012802182106200129022c21072001280228210820014100360218200142808080808001370210200141106a410010a4868080002001280214200128021841386c6a2202420437022c20024206370224200241cae4c4800036022020024100360218200241a6828080003602102002429bc4fd96a6cbbfd7ed003703082002429db1fdedf2d2e1d1f500370300200128021821092001280214210220012001280210360248200120023602402001200236024420012002200941386c6a41386a36024c200141346a200141c0006a10f9868080002008418080808078460d012001200436021820012005360210200120053602142001200520064105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002008360238200041003a000020002001290300370350200041d8006a20032802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000bea0503067f017e027f23808080800041d0006b2201248080808000200141286a419b9fc38000410a41a59fc380004120418097c38000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d002002429ca0fdd992b0ed90df00370308200241c59fc38000360220200241ed8180800036021820024101360204200241ab99c38000360200200241106a42bbf8f5b8b9e2c39ac400370300200241386a4100360200200241246a410136020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a22032001411c6a220241086a28020036020020012002290200370300200128021021042001280214210520012802182106200129022c21072001280228210820014100360218200142808080808001370210200141106a410010a4868080002001280214200128021841386c6a2202420437022c20024206370224200241cae4c4800036022020024100360218200241a782808000360210200242eab19fb291b0f9fb0c370308200242d39ec9badc8ccad845370300200128021821092001280214210220012001280210360248200120023602402001200236024420012002200941386c6a41386a36024c200141346a200141c0006a10f9868080002008418080808078460d012001200436021820012005360210200120053602142001200520064105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002008360238200041003a000020002001290300370350200041d8006a20032802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000bca0704067f017e017f017e23808080800041d0006b2201248080808000200141286a41c69fc38000411141d79fc380004114418097c38000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d00200242c194a6a793ccc3a857370308200241ec9fc38000360220200241f58180800036021820024101360204200241eb9fc38000360200200241306a4293888c8f89fdc6ec9e7f370300200241286a42a5e9e3ab9e929adc2c370300200241106a42ab8bffbed784ffa5937f370300200241386a41ef80808000360200200241246a410136020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a2001411c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c20024205370224200241f6a4c580003602202002410636021c200241f0a4c58000360218200241ef8080800036021020024293888c8f89fdc6ec9e7f370308200242a5e9e3ab9e929adc2c370300200141c0006a41086a200828020041016a2202360200200120012902102209370340024020022009a7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c2002421237022420024187a5c580003602202002410c36021c200241fba4c58000360218200241a882808000360210200242ef88c9d3f691f5f30b370308200242e0c4f8e5b8f790b077370300200128024821082001280244210220012001280240360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000bde0503057f017e027f23808080800041d0006b2201248080808000200141286a41ed9fc38000410c41d79fc380004114418097c38000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d00200242c194a6a793ccc3a857370308200241ec9fc38000360220200241f58180800036021820024101360204200241eb9fc38000360200200241306a4293888c8f89fdc6ec9e7f370300200241286a42a5e9e3ab9e929adc2c370300200241106a42ab8bffbed784ffa5937f370300200241386a41ef80808000360200200241246a410136020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a2203200141106a410c6a220241086a28020036020020012002290200370300200128021021042001280214210220012802182105200129022c2106200128022821072001410036021820014280808080c000370210200141c0006a200141106a41f99fc38000410710b88b808000200141346a200141c0006a4180a0c38000410910bf8b8080002001200128023436021820012001280238220836021020012008200128023c41246c6a36021c20012008360214200141c0006a200141106a10fb868080002007418080808078460d012001200436021820012002360210200120023602142001200220054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141c0006a41086a2802003600002000413c6a200637020020002007360238200041013a000020002001290300370350200041d8006a20032802003602002001200129024037001320002001290010370001200041086a200141106a41076a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000b2100200128021441d4abc380004114200141186a28020028020c118280808000000b2100200128021441e8abc38000410f200141186a28020028020c118280808000000b920601087f024002402002450d00200120026a2106200341016a210741002108200121090240034002400240200820044b0d0020092d0000210a02402008450d000240024020084101710d002003210b0c010b200320032d0000410874200a72220a200a413a6e220a413a6c6b3a00002007210b0b20084101460d00200320086a210c0340200b200b2d0000410874200a6a220a200a413a6e220a413a6c6b3a0000200b41016a220d200d2d0000410874200a6a220a200a413a6e220a413a6c6b3a0000200b41026a220b200c470d000b0b200a450d0120082004200820044b1b210c034020042008460d040240200c2008460d00200320086a200a200a413a6e220b413a6c6b3a0000200841016a2108200a413a49210d200b210a200d0d030c010b0b200c200441a4a1c3800010f980808000000b200820044194a1c38000109581808000000b200941016a22092006470d000b20082004200820044b1b210a0240034020012d00000d0120042008460d020240200a2008460d00200141016a2101200320086a41003a0000200841016a21082002417f6a22020d010c020b0b200a20044184a1c3800010f980808000000b0240200820044b0d002008450d02200320086a21044100210a024003402003200a6a220d2d0000220b413a4f0d01200d2005200b6a4180016a2d00003a00002008200a41016a220a470d000b4100210a20084102490d042004200841017622016b21024100210a4100210b024020014101460d002008417f6a210d200141feffffff077121064100210b03402003200d6a220c2d00002109200c2003200b6a22042d00003a0000200420093a000020022001200b417e736a6a220c2d00002109200c200441016a22042d00003a0000200420093a0000200d417e6a210d2006200b41026a220b470d000b0b2008410271450d042003200b6a220d2d00002104200d20022001200b417f736a6a220b2d00003a0000200b20043a00000c040b200b413a41f4a0c3800010f980808000000b2008200441e4a0c38000109581808000000b4101210a0c010b410021084100210a0b200020083602042000200a3602000b02000b02000b8d0201037f23808080800041106b2202248080808000410121032001280214418fa5c080004101200141186a28020028020c118280808000002104200241003a0009200220043a0008200220013602042002200036020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041016a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041026a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041036a36020c200241046a2002410c6a41d0cfc48000108e818080001a024020022d00080d00200228020422002802144190a5c080004101200041186a28020028020c1182808080000021030b200241106a24808080800020030ba90901037f23808080800041106b2202248080808000410121032001280214418fa5c080004101200141186a28020028020c118280808000002104200241003a0009200220043a0008200220013602042002200036020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041016a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041026a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041036a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041046a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041056a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041066a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041076a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041086a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041096a36020c200241046a2002410c6a41d0cfc48000108e818080001a20022000410a6a36020c200241046a2002410c6a41d0cfc48000108e818080001a20022000410b6a36020c200241046a2002410c6a41d0cfc48000108e818080001a20022000410c6a36020c200241046a2002410c6a41d0cfc48000108e818080001a20022000410d6a36020c200241046a2002410c6a41d0cfc48000108e818080001a20022000410e6a36020c200241046a2002410c6a41d0cfc48000108e818080001a20022000410f6a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041106a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041116a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041126a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041136a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041146a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041156a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041166a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041176a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041186a36020c200241046a2002410c6a41d0cfc48000108e818080001a2002200041196a36020c200241046a2002410c6a41d0cfc48000108e818080001a20022000411a6a36020c200241046a2002410c6a41d0cfc48000108e818080001a20022000411b6a36020c200241046a2002410c6a41d0cfc48000108e818080001a20022000411c6a36020c200241046a2002410c6a41d0cfc48000108e818080001a20022000411d6a36020c200241046a2002410c6a41d0cfc48000108e818080001a20022000411e6a36020c200241046a2002410c6a41d0cfc48000108e818080001a20022000411f6a36020c200241046a2002410c6a41d0cfc48000108e818080001a024020022d00080d00200228020422002802144190a5c080004101200041186a28020028020c1182808080000021030b200241106a24808080800020030b210020012802144180a2c38000410e200141186a28020028020c118280808000000bf20302037f037e2380808080004180086b22032480808080002003200136028c022003410036028802200320023602840202400240200228020422014108490d002002200141786a220436020420022002280200220541086a3602002004450d00200529000021062002200141776a3602042002200541096a36020002400240024020052d00080e020001030b20034180066a20034184026a10f988808000420021072003290380064200520d0220034188046a20034188066a41f80110848e8080001a0c010b20034180066a20034184026a10f9888080002003290380064200520d0120034188046a20034188066a41f80110848e8080001a420121070b20034190026a20034188046a41f80110848e8080001a200320034184026a10bc8a80800020032802000d0020034180066a20034184026a200328020410d0858080002003280280062201418080808078460d0020033502880621082003280284062105200341086a20034190026a41f80110848e8080001a024020022802040d0020002007370300200041086a200341086a41f80110848e8080001a20002008370390022000200536028c02200020013602880220002006370380020c020b200042023703002001450d01200541002802c0a3c68000118080808000000c010b200042023703000b20034180086a2480808080000b8d0101027f23808080800041306b2202248080808000200241146a42013702002002410236020c2002418ca4c38000360208200241e0818080003602242002411f36022c2002419ca4c38000360228200141186a28020021032002200241206a3602102002200241286a36022020012802142003200241086a10d9808080002101200241306a24808080800020010b8f0302047f017e23808080800041106b22012480808080004102210202400240024020002d0000220341726a0e020201000b20034102744184aec380006a28020021020c010b41014102200041026a2d000022024102491b4102410120024107461b20002d00011b41026a21020b41002d00fca3c680001a0240200241002802c8a3c68000118180808000002204450d002001200436020820012002360204024002402003410f460d00200441003a0000410121022001410136020c02402003410e470d0041012102024020012802044101470d00200141046a4101410110b182808000200128020c21020b200128020820026a41003a00002001200241016a36020c0c020b024020012802044101470d00200141046a4101410110b182808000200128020c21020b200128020820026a41013a00002001200241016a36020c2000200141046a10cc888080000c010b200441013a00002001410136020c200041016a200141046a10aa878080000b20012902082105200141106a24808080800020050f0b4101200210b280808000000bce0203037f017e017f23808080800041206b220124808080800041002d00fca3c680001a0240410141002802c8a3c68000118180808000002202450d002001200236020c200141013602080240024020002802002203418180808078470d00200241003a000042808080801021040c010b200241013a000020014101360210024002402003418080808078470d00200141146a2000280204200041086a28020010b4828080000c010b200141146a2000280204200028020810e6848080000b41012100200128021821050240200128021c2203450d00200141086a4101200310b182808000200128020c2102200128021021000b200220006a2005200310848e8080001a200020036a210002402001280214450d00200541002802c0a3c68000118080808000000b2000ad42208621040b200141206a24808080800020042002ad840f0b4101410110b280808000000b8a0302057f017e23808080800041106b2201248080808000024002400240024002400240024020002802102202418080808078470d004101410220002d000122034102491b4102410120034107461b20002d00001b41026a21030c010b417f417f200041246a280200410c6c417f200041186a280200410c6c2203410c6a22042004200341046a491b22036a41046a220420042003491b220341096a220420042003491b41016a2203450d012003417f4c0d040b4100210541002d00fca3c680001a200341002802c8a3c68000118180808000002204450d0420012004360208200120033602042002418080808078470d01200441013a00002001410136020c2000200141046a10aa878080000c020b2001410036020c2001428080808010370204200141046a4100410110b18280800020012802082104200128020c21050b200420056a41003a00002001200541016a36020c2000200141046a10a9878080000b20012902082106200141106a24808080800020060f0b10ae80808000000b4101200310b280808000000b970501047f23808080800041d0066b220224808080800041002d00fca3c680001a0240024041800341002802c8a3c68000118180808000002203450d0020024198046a41086a2001280228220441086a29000037030020024198046a41106a200441106a29000037030020024198046a41186a200441186a2900003703002002200429000037039804200241bc046a41086a4120360200200220043602c004200241003602bc042002418c026a200120024198046a200241bc046a41d8efc08000410110e28780800002400240200228028c0222014105460d002002280290022105200241086a2002418c026a41086a41810210848e8080001a200241c8046a200241086a41e00110848e8080001a200241a8066a41206a20024188026a2d00003a0000200241a8066a41186a20024180026a290200370300200241a8066a41106a200241086a41f0016a290200370300200241a8066a41086a200241f0016a290200370300200220022902e8013703a80641002d00fca3c680001a41f00141002802c8a3c68000118180808000002204450d032004200536020c200420013602082004428180808010370200200441106a200241c8046a41e00110848e8080001a200320043602082003410036020020004200370234200041013602082000200336020420004108360200200320022903a80637000c200341146a200241a8066a41086a2903003700002003411c6a200241a8066a41106a290300370000200341246a200241c0066a2903003700002003412c6a200241c8066a2d00003a00000c010b2002280290022104200041808080807836020020002004360204200341002802c0a3c68000118080808000000b200241d0066a2480808080000f0b410441800310b280808000000b410441f00110b280808000000bfc3303127f017e017f23808080800041e0026b22032480808080000240024020012802082204450d00200141106a2105200141346a21062001410c6a2107200341086a41e8016a2108200341086a41086a2109200341086a41046a210a03402001280204220b2004417f6a220c41306c6a210403402004280208220d41ec016a280200210e200d41e8016a280200210f024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024020042802000e050300010210030b200d280208417e6a0e03040506030b2004280204210c200d280208220b417d6a22100e020908020b200d2802080e050c0c090a0b0c0b200441014103200b200c41306c6a220d280208280208417e6a4103491b360200200128020c210c2001280210210e20012802342104200341086a41286a220f4100360200200341086a200c2007200441284b220b1b220c200c200e2004200b1b6a10f686808000200341a0026a41286a200f280200360200200341a0026a41206a200341086a41206a290200370300200341a0026a41186a200341086a41186a290200370300200341a0026a41106a200341086a41106a290200370300200341a0026a41086a2009290200370300200320032902083703a002200320012802383602cc02200341086a200d280208220441086a200441e8016a280200200441ec016a28020010f48d8080002003280208417f6a0e040d100f0e100b418ca6c3800041e60041f4a6c3800010f880808000000b200d41106a280200220c200d410c6a280200220b490d11200c200e4b0d12200f200b6a2111200c200b6b210b200d41146a2802002210410176210c0240024020104101710d00410021120240200c200b4b0d00200c21104100210c0c020b200c200b41d492c68000109481808000000b200c200b4f0d1441012112200c41016a21102011200c6a2d0000410f71210c0b2003200c3a0009200320123a00082003200b20106b3602102003201120106a36020c2007200341086a10e28d80800020042d000c4101710d02200341a0026a41186a4200370300200341a0026a41106a4200370300200341a0026a41086a4200370300200342003703a0020c030b2001280234210d024002402001280238220c4101710d000240024020052006200d41284b220e1b220c280200220f200d4128200e1b460d0020072802002007200e1b210d0c010b200710f78d8080002005280200210f2007280200210d2005210c0b200d200f6a41003a0000200c200c28020041016a3602002001280238210c0c010b2005280200200d200d41284b1b450d140b2001200c41016a3602384103210c2004280208210e02400240024002400240024020042802000e0400010203490b41014103200e280208417e6a4103491b210c0c480b41024103200e280208417d6a4102491b210c4100210d0c470b2004280204210f200e280208417d6a0e020102460b4104210c0c450b200f410e4b0d440c430b200f410f490d420c430b200d41106a280200220c200d410c6a280200220b490d13200c200e4b0d14200f200b6a210f200c200b6b210c200d41146a280200220e410176210d02400240200e4101710d004100210b0240200d200c4b0d00200d210e4100210d0c020b200d200c41d492c68000109481808000000b200d200c4f0d164101210b200d41016a210e200f200d6a2d0000410f71210d0b2003200d3a00092003200b3a00082003200c200e6b3602102003200f200e6a36020c2007200341086a10e28d8080002001280234210d024002402001280238220c4101710d000240024020052006200d41284b220e1b220c280200220f200d4128200e1b460d0020072802002007200e1b210d0c010b200710f78d8080002005280200210f2007280200210d2005210c0b200d200f6a41003a0000200c200c28020041016a3602002001280238210c0c010b2005280200200d200d41284b1b450d170b2001200c41016a3602384103210c2004280208210e02400240024002400240024020042802000e0400010203460b41014103200e280208417e6a4103491b210c0c450b41024103200e280208417d6a4102491b210c4100210d0c440b2004280204210f200e280208417d6a0e020102430b4104210c0c420b200f410e4b0d410c400b200f410f490d3f0c400b200341a0026a41186a200441256a290000370300200341a0026a41106a2004411d6a290000370300200341a0026a41086a200441156a29000037030020032004410d6a2900003703a0020b200d41206a280200210c200d411c6a280200210b02400240200d41186a2802000d00200c200b490d174100210d200c200e4d0d01200c200e41e49dc68000109581808000000b200c200b490d174101210d200c200e4b0d180b2003200d360294022003200c200b6b36029c022003200f200b6a360298022001280238220c410176210d02400240200c4101710d0020052802002006280200220c200c41284b220c1b220e200d490d1a20072802002007200c1b210c4100210e0c010b20052802002006280200220c200c41284b220e1b220c200d490d1a200c200d4d0d1b200320072802002007200e1b220c200d6a2d000041f001713a00dd024101210e0b2003200e3a00dc022003200d3602d8022003200c3602d402200341086a2002200341a0026a20034194026a200341d4026a410110e287808000024020032802084105460d0041002d00fca3c680001a41f00141002802c8a3c6800011818080800000220d450d1c200d428180808010370200200d41086a200341086a41e80110848e8080001a0240200128020822042001280200470d0020012004109886808000200128020821040b2001280204200441306c6a2204200d360208200441003602002004200829020037020c200441146a200841086a2902003702002004411c6a200841106a290200370200200441246a200841186a2902003702002004412c6a200841206a2d00003a0000200128020841016a210d0c3d0b4103210a2004280208210d02400240024002400240024020042802000e0400010203400b41014103200d280208417e6a4103491b210a0c3f0b41024103200d280208417d6a4102491b210a410021010c3e0b2004280204210c200d280208417d6a0e0201023d0b4104210a0c3c0b200c410e4b0d3b0c3a0b200c410f490d390c3a0b4106210b0b200c410f4b0d1a024002400240200d200b4102746a200c410c6c6a410c6a220d2802004102460d0002400240024020012802382210450d0020052006200628020041284b220b1b2802002211450d21200141104134200b1b6a2011417f6a2211360200200128020c22122007200b1b20116a2d0000211120012010417f6a220b3602380240200b410171450d00201141f00171211302400240200520062006280200221441284b22101b220b28020022112014412820101b460d002012200720101b21100c010b200710f78d80800020052802002111200728020021102005210b0b201020116a20133a0000200b200b28020041016a3602002001280238210b0b200b4101710d010b200c410474211102400240200520062006280200221241284b220b1b220c280200221020124128200b1b460d0020072802002007200b1b210b0c010b200710f78d808000200528020021102007280200210b2005210c0b200b20106a20113a0000200c200c28020041016a3602000c010b20052802002006280200220b200b41284b220b1b2210450d20201020072802002007200b1b6a417f6a220b200b2d0000200c723a00000b2001200128023841016a221036023820042d000c4101710d01200341a0026a41186a4200370300200341a0026a41106a4200370300200341a0026a41086a4200370300200342003703a0020c020b4103210e0240024020100e0200013a0b200c410e4b0d390c380b200c410f490d370c380b200341a0026a41186a200441256a290000370300200341a0026a41106a2004411d6a290000370300200341a0026a41086a200441156a29000037030020032004410d6a2900003703a0020b200d41086a280200210c200d280204210b02400240200d2802000d00200c200b490d1f4100210d200c200e4d0d01200c200e41e49dc68000109581808000000b200c200b490d1f4101210d200c200e4b0d200b2003200d360294022003200c200b6b36029c022003200f200b6a360298022010410176210d0240024020104101710d0020052802002006280200220c200c41284b220c1b220e200d490d2220072802002007200c1b210c4100210e0c010b20052802002006280200220c200c41284b220e1b220c200d490d22200c200d4d0d23200320072802002007200e1b220c200d6a2d000041f001713a00dd024101210e0b2003200e3a00dc022003200d3602d8022003200c3602d402200341086a2002200341a0026a20034194026a200341d4026a410110e287808000024020032802084105460d0041002d00fca3c680001a41f00141002802c8a3c6800011818080800000220d450d24200d428180808010370200200d41086a200341086a41e80110848e8080001a0240200128020822042001280200470d0020012004109886808000200128020821040b2001280204200441306c6a2204200d360208200441003602002004200829020037020c200441146a200841086a2902003702002004411c6a200841106a290200370200200441246a200841186a2902003702002004412c6a200841206a2d00003a0000200128020841016a210d0c3b0b4103210a2004280208210d02400240024002400240024020042802000e04000102033a0b41014103200d280208417e6a4103491b210a0c390b41024103200d280208417d6a4102491b210a410021010c380b2004280204210c200d280208417d6a0e020102370b4104210a0c360b200c410e4b0d350c340b200c410f490d330c340b2007200d41106a280200200d410c6a2802006b410174200d41146a2802006b10e08d8080000c020b2001280238220d450d0120052006200628020041284b22041b280200220c450d2220014110413420041b6a200c417f6a220c360200200128020c220e200720041b200c6a2d0000210c2001200d417f6a22043602382004410171450d01200c41f00171210f02400240200520062006280200220b41284b220d1b2204280200220c200b4128200d1b460d00200e2007200d1b210d0c010b200710f78d8080002005280200210c2007280200210d200521040b200d200c6a200f3a00002004200428020041016a3602000c010b2007200d41106a280200200d410c6a2802006b410174200d41146a2802006b41016a10e08d8080000b20012802082204450d3e4103210c20012802042004417f6a41306c6a2204280208210e02400240024002400240024020042802000e0400010203340b41014103200e280208417e6a4103491b210c0c330b41024103200e280208417d6a4102491b210c4100210d0c320b2004280204210f200e280208417d6a0e020102310b4104210c0c300b200f410e4b0d2f0c2e0b200f410f490d2d0c2e0b2001200c360208200341a0026a41086a220d200b200c41306c6a2204410c6a290200370300200341a0026a41106a220c200441146a290200370300200341a0026a41186a220e2004411c6a290200370300200341a0026a41206a220f200441246a290200370300200341a0026a41286a220b2004412c6a280200360200200320042902043703a002200428020022044105460d2b200a20032903a002370200200a41286a200b280200360200200a41206a200f290300370200200a41186a200e290300370200200a41106a200c290300370200200a41086a200d290300370200200320043602082003280210220420042802002204417f6a360200024020044101470d00200910e18a8080000b20012802082204450d3d4103210c20012802042004417f6a41306c6a2204280208210e02400240024002400240024020042802000e0400010203300b41014103200e280208417e6a4103491b210c0c2f0b41024103200e280208417d6a4102491b210c4100210d0c2e0b2004280204210f200e280208417d6a0e0201022d0b4104210c0c2c0b200f410e4b0d2b0c2a0b200f410f490d290c2a0b2003280214220e410176210d200329021c2115200328021821042003280210210c200328020c210f02400240200e4101710d004100210b0240200d200c4b0d00200d210e4100210d0c020b200d200c41d492c68000109481808000000b200d200c4f0d204101210b200d41016a210e200f200d6a2d0000410f71210d0b2003200d3a00d5022003200b3a00d4022003200c200e6b3602dc022003200f200e6a3602d802200341a0026a200341d4026a10e28d8080000c040b2003280214220e410176210d20032902dc01211520032802d80121042003280210210c200328020c210f02400240200e4101710d004100210b0240200d200c4b0d00200d210e4100210d0c020b200d200c41d492c68000109481808000000b200d200c4f0d204101210b200d41016a210e200f200d6a2d0000410f71210d0b2003200d3a00d5022003200b3a00d4022003200c200e6b3602dc022003200f200e6a3602d802200341a0026a200341d4026a10e28d80800020044102460d010c030b20032802cc0122044102470d010b20032802c8024129490d2420032802a00241002802c0a3c68000118080808000000c240b20032902d00121150b20032802cc02220c410176210d02400240200c410171220b0d0020032802a40220032802c802220e200e41284b220e1b220f200d490d1e20032802a002200341a0026a200e1b210f0c010b20032802a40220032802c802220e200e41284b220f1b220e200d490d1e200e200d4d0d1f20032802a002200341a0026a200f1b220f200d6a2d000041707121160b4101210e0240200c4102490d0041002d00fca3c680001a200d41002802c8a3c6800011818080800000220e450d200b200e200f200d10848e808000210e024020032802c8024129490d0020032802a00241002802c0a3c68000118080808000000b20044103460d2220044102460d2e0240200b450d0041002d00fca3c680001a413041002802c8a3c68000118180808000002201450d21200120163a00102001200d36020c2001200e3602082001200d36020420014186808080783602002000200136020420004180808080783602000c390b2015422088a7210a2015a7210102400240024020040d002003200a4100109686808000200328020021042003280204220c2001200a10848e8080001a0c010b200341003a00a8022003200d3602a4022003200e3602a002200a4120470d23200341086a41186a200141186a290000370300200341086a41106a200141106a290000370300200341086a41086a200141086a29000037030020032001290000370308200341d4026a2002200341086a200341a0026a10e18780800020032802d4022204418080808078460d0120032802dc02210a20032802d802210c0b2000200a3602142000200c3602102000200436020c2000200d3602082000200e3602042000200d3602000c390b200020032802d8023602042000418080808078360200200c4102490d38200e41002802c0a3c68000118080808000000c380b200b200c41849ec68000109681808000000b200c200e41849ec68000109581808000000b200c200b41e492c6800010f980808000000b419c94c68000413a41d894c6800010a181808000000b200b200c41849ec68000109681808000000b200c200e41849ec68000109581808000000b200d200c41e492c6800010f980808000000b419c94c68000413a41d894c6800010a181808000000b200b200c41e49dc68000109681808000000b200b200c41f49dc68000109681808000000b200c200e41f49dc68000109581808000000b200d200e41ac95c68000109581808000000b200d200c41bc95c68000109581808000000b200d200c41cc95c6800010f980808000000b410441f00110b280808000000b200c411041fca5c3800010f980808000000b41e894c680004122418c95c6800010a181808000000b419c94c68000413a41d894c6800010a181808000000b200b200c41e49dc68000109681808000000b200b200c41f49dc68000109681808000000b200c200e41f49dc68000109581808000000b200d200e41ac95c68000109581808000000b200d200c41bc95c68000109581808000000b200d200c41cc95c6800010f980808000000b410441f00110b280808000000b41e894c680004122418c95c6800010a181808000000b200d200c41e492c6800010f980808000000b200d200c41e492c6800010f980808000000b200d200f41ac95c68000109581808000000b200d200e41bc95c68000109581808000000b200d200e41cc95c6800010f980808000000b4101200d10b280808000000b4104413010b280808000000b4120200a419ca5c3800010a281808000000b200128020822040d130c140b200f41016a210d4102210c0b2004200d3602042004200c3602002001280208210d0c0f0b41aca5c3800041cd004184a7c3800010a181808000000b200f41016a210d4102210c0b2004200d3602042004200c3602002001280208210d0c0c0b200c41016a21014102210a0b200328020c210d200420013602042004200a3602000c040b200c41016a210d4102210e0b2004200d3602042004200e3602002001280208210d0c080b200c41016a21014102210a0b200328020c210d200420013602042004200a3602000b2000200d36020420004180808080783602000c090b2001200d3602080c040b200f41016a210d4102210c0b2004200d3602042004200c3602002001280208210d0c020b200f41016a210d4102210c0b2004200d3602042004200c3602002001280208210d0b2001280204220b200d417f6a220c41306c6a2104200d0d000b0b0b20004181808080783602000b200341e0026a2480808080000b930201027f23808080800041106b220224808080800020022000360200200220012802144194a7c38000410e200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a200241a4a7c38000108d81808000210120022d000c210002400240200128020022030d00200041ff017141004721010c010b41012101200041ff01710d0020022802082100024020034101470d0020022d000d41ff0171450d0020002d001c4104710d00410121012000280214418ca5c080004101200041186a28020028020c118280808000000d010b2000280214418da5c080004101200041186a28020028020c1182808080000021010b200241106a24808080800020010baf0301027f23808080800041206b220324808080800002400240024010fa81808000220441ff01490d00200041093b0100024002400240024020022d00000e0400010203060b200241086a21020c040b200241086a21020c030b200241086a21020c020b200241046a21020c010b2003200441016a36020841b1e3c080004113200341086a410441002802e0a1c680001186808080000041002802e8a1c6800011888080800000200341086a41106a200241106a280200360200200341086a41086a200241086a29020037030020032002290200370308200341086a10f58880800041002802b0a1c68000118880808000002000410e3a0000200341086a10fb818080000c010b2002280200450d00200228020441002802c0a3c68000118080808000000b200128020022022002280200417f6a2201360200024020010d00200241086a28020022002002410c6a28020022012802001180808080000002402001280204450d00200041002802c0a3c68000118080808000000b200241046a22012001280200417f6a220136020020010d00200241002802c0a3c68000118080808000000b200341206a2480808080000bdf0201027f23808080800041206b22032480808080000240024010fa81808000220441fe014b0d002003200441016a36020c41b1e3c0800041132003410c6a410441002802e0a1c680001186808080000041002802e8a1c68000118880808000002003410c6a2002410010b8888080002000200329020c370200200041086a2003410c6a41086a290200370200200041106a2003410c6a41106a28020036020041b0a1c6800041d0a1c6800020032d000c410e461b280200118880808000002003410c6a10fb818080000c010b200041093b01000b200128020022002000280200417f6a2201360200024020010d00200041086a28020022022000410c6a28020022012802001180808080000002402001280204450d00200241002802c0a3c68000118080808000000b200041046a22012001280200417f6a220136020020010d00200041002802c0a3c68000118080808000000b200341206a2480808080000bc60301087f23808080800041306b220324808080800020032001360200024002400240024002402001450d00200341246a21044101210541002106410021070340200341046a2005200741002802c0a1c680001185808080000020032802042208418080808078460d02200341186a20032802082209200328020c220741002802b8a1c680001185808080000002402003280218220a450d002004200328021c2003280220200a28020c118580808000000b02402006450d00200541002802c0a3c68000118080808000000b20092105200821062001417f6a22010d000b20020d022000410e3a00002008450d04200941002802c0a3c68000118080808000000c040b20020d012000410e3a00000c030b2002450d012000410e3a00002006450d02200541002802c0a3c68000118080808000000c020b200341246a42003702002003410136021c200341b8a7c380003602182003418097c38000360220200341186a41b4a8c3800010f680808000000b200341246a42013702002003410236021c200341e8a8c3800036021820034185808080003602142003200341106a36022020032003360210200341186a41f8a8c3800010f680808000000b200341306a2480808080000bdf0201027f23808080800041206b22032480808080000240024010fa81808000220441fe014b0d002003200441016a36020c41b1e3c0800041132003410c6a410441002802e0a1c680001186808080000041002802e8a1c68000118880808000002003410c6a2002410110b8888080002000200329020c370200200041086a2003410c6a41086a290200370200200041106a2003410c6a41106a28020036020041b0a1c6800041d0a1c6800020032d000c410e461b280200118880808000002003410c6a10fb818080000c010b200041093b01000b200128020022002000280200417f6a2201360200024020010d00200041086a28020022022000410c6a28020022012802001180808080000002402001280204450d00200241002802c0a3c68000118080808000000b200041046a22012001280200417f6a220136020020010d00200041002802c0a3c68000118080808000000b200341206a2480808080000bb70e02047f017e23808080800041106b220224808080800002400240024002400240024002400240024002400240024002402000280200417f6a0e0c000102030405060708090a0b000b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41003a0000200041086a200110df898080000c0b0b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41013a00002001200341016a360208200041086a280200210420022000410c6a28020022003602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822036b20004f0d0020012003200010b182808000200128020821030b200128020420036a2004200010848e8080001a2001200320006a3602080c0a0b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41023a00002001200341016a360208200041146a28020021052002200041186a28020022033602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822046b20034f0d0020012004200310b182808000200128020821040b200128020420046a2005200310848e8080001a2001200420036a360208200041046a200110c98c8080000c090b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41033a00002001200341016a360208200041086a280200210520022000410c6a28020022033602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822046b20034f0d0020012004200310b182808000200128020821040b200128020420046a2005200310848e8080001a2001200420036a360208200041146a28020021042002200041186a28020022003602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822036b20004f0d0020012003200010b182808000200128020821030b200128020420036a2004200010848e8080001a2001200320006a3602080c080b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41043a00002001200341016a360208200041086a280200210420022000410c6a28020022003602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822036b20004f0d0020012003200010b182808000200128020821030b200128020420036a2004200010848e8080001a2001200320006a3602080c070b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41053a00002001200341016a360208200041086a280200210420022000410c6a28020022003602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822036b20004f0d0020012003200010b182808000200128020821030b200128020420036a2004200010848e8080001a2001200320006a3602080c060b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41063a0000200041046a200110898d8080000c050b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41073a00002001200341016a2203360208200029030821060240200128020020036b41074b0d0020012003410810b182808000200128020821030b2001200341086a360208200128020420036a20063700000c040b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a41083a00000c030b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41093a00002001200341016a2203360208200028020421000240200128020020036b41034b0d0020012003410410b182808000200128020821030b2001200341046a360208200128020420036a20003600000c020b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a410a3a00002001200341016a2203360208200028020421000240200128020020036b41034b0d0020012003410410b182808000200128020821030b2001200341046a360208200128020420036a20003600000c010b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a410b3a00002001200341016a2203360208200028020421000240200128020020036b41034b0d0020012003410410b182808000200128020821030b2001200341046a360208200128020420036a20003600000b200241106a2480808080000bf50102017f057e23808080800041b0026b2202248080808000024002402001280200410a460d004200210342e40021040c010b20013502042104200241086a10e1898080002004200241a8026a29030022032003428094ebdc03802203428094ebdc037e7d7e2205428094ebdc03802206200320047e7c20052006428094ebdc037e7d4280cab5ee0156ad7c2103200420022903a00222052005428094ebdc03802205428094ebdc037e7d7e2206428094ebdc03802207200520047e7c20062007428094ebdc037e7d4280cab5ee0156ad7c21040b200041003b01102000200337030820002004370300200241b0026a2480808080000b8e0301047f23808080800041306b22012480808080002001410036020c20014280808080800137020441002d00fca3c680001a0240410441002802c8a3c680001181808080000022020d004101410410b280808000000b2001411c6a41086a22034100360200200120023602202001410436021c200141003602282001200141286a36022c2001412c6a2001411c6a10c08a808000200141106a41086a220420032802003602002001200129021c370310200141046a4100109d868080002001280208200141046a41086a220328020041e8006c6a2202410b36024420024185bec38000360240200241a9828080003602182002410036020020022001290310370348200241013a00602002410036025c20024280808080c000370254200241d0006a200428020036020020024287bba5b8adf5dafa5b370308200241106a429595f4d085b899a1603703002003200328020041016a2202360200200041086a200236020020002001290204370200200041106a410d360200200041e8a6c4800036020c200141306a2480808080000bf20501047f2380808080004190016b2201248080808000200142f0fe93e98686d7ab8c7f37030820014280eee1b0e3b7afe9987f370300200141cc006a2001411041002802c0a1c680001185808080000002400240200128024c2202418080808078460d0020012802502103024020012802544110490d0020012003411010888e808000210402402002450d00200341002802c0a3c68000118080808000000b20040d010c020b2002450d00200341002802c0a3c68000118080808000000b41002102200141003b011e02400240417f4100280284a4c680002203410347200341034b1b2203450d00200341ff017141ff01470d010b200141286a410c6a41aa82808000360200200141838280800036022c2001410d360224200141e8a6c4800036022020012001411e6a3602302001200141206a3602284100280290a1c680002102410028028ca1c6800021034100280280a4c68000210420014184016a4202370200200141fc006a4103360200200141f4006a4116360200200141f0006a418cabc38000360200200141e4006a41c0a7c38000ad4280808080b00e84370200200141cc006a410c6a41b9a9c38000ad4280808080d0068437020020014180016a200141286a360200200141f4aac380003602782001410336026c200141003602602001410036025420014281808080e00437024c200341ecf2c08000200441024622041b200141cc006a200241d4f2c0800020041b2802101184808080000020012f011e21020b200141cc006a41e8a6c48000410d41002802a0a3c6800011858080800000200141cc006a41106a220341bccdc48000411541002802a0a3c6800011858080800000200141286a41186a200141cc006a41186a290000370300200141286a41106a2003290000370300200141286a41086a200141cc006a41086a2900003703002001200129004c370328200120023b014c200141286a4120200141cc006a410241002802e0a1c68000118680808000000b200042003703082000420037030020014190016a2480808080000beb0101037f23808080800041106b220224808080800002402001280200220328020020032802082204470d0020032004410110ab86808000200328020821040b200328020420046a41fb003a00002003200441016a3602082002200136020c20024180023b01080240200241086a41c0abc38000410b200010d88680800022030d002002280208220441ff01710d0020044180fe0371450d000240200228020c280200220428020020042802082201470d0020042001410110ab86808000200428020821010b200428020420016a41fd003a00002004200141016a3602080b200241106a24808080800020030b840d03017f0a7e047f23808080800041c0006b22022480808080000240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002402001280200417f6a0e0c000102030405060708090a0b000b200241186a200141206a2903002203370300200241106a200141186a2903002204370300200241086a200141106a2903002205370300200241206a200141286a2903002206370300200241286a200141306a2903002207370300200241306a200141386a2903002208370300200241386a200141c0006a290300220937030020022001290308220a370300200141c8006a290300210b200141d0006a290300210c200041c0006a2009370300200041386a2008370300200041306a2007370300200041286a2006370300200041206a2003370300200041186a2004370300200041106a20053703002000200a370308200041d0006a200c370300200041c8006a200b370300200041013602000c160b200141086a280200210d024002402001410c6a28020022010d004101210e0c010b2001417f4c0d0e41002d00fca3c680001a200141002802c8a3c6800011818080800000220e450d0f0b200e200d200110848e808000210d2000410c6a2001360200200041086a200d36020020002001360204200041023602000c150b200141146a280200210e02400240200141186a280200220d0d004101210f0c010b200d417f4c0d0d41002d00fca3c680001a200d41002802c8a3c6800011818080800000220f450d0f0b200f200e200d10848e808000210f418080808078210e2001280204418080808078460d0b200141086a28020021102001410c6a280200220e0d09410121010c0a0b200141086a28020021104101210f4101210e02402001410c6a280200220d450d00200d417f4c0d0c41002d00fca3c680001a200d41002802c8a3c6800011818080800000220e450d0f0b200e2010200d10848e808000210e2000410c6a200d360200200041086a200e3602002000200d360204200141146a280200210d0240200141186a2802002201450d002001417f4c0d0c41002d00fca3c680001a200141002802c8a3c6800011818080800000220f450d100b200f200d200110848e808000210d200041186a2001360200200041146a200d36020020002001360210200041043602000c130b200141086a280200210d024002402001410c6a28020022010d004101210e0c010b2001417f4c0d0b41002d00fca3c680001a200141002802c8a3c6800011818080800000220e450d100b200e200d200110848e808000210d2000410c6a2001360200200041086a200d36020020002001360204200041053602000c120b200141086a280200210d024002402001410c6a28020022010d004101210e0c010b2001417f4c0d0a41002d00fca3c680001a200141002802c8a3c6800011818080800000220e450d100b200e200d200110848e808000210d2000410c6a2001360200200041086a200d36020020002001360204200041063602000c110b02400240024002400240024020012d00040e050001020304000b200141056a280000210d200241086a2001410c6a108b87808000200241003a00002002200d3600010c040b200141056a280000210d200241086a2001410c6a108b87808000200241013a00002002200d3600010c030b200141056a280000210d200241086a2001410c6a108b87808000200241023a00002002200d3600010c020b200241046a200141086a108b87808000200241033a00000c010b200241043a00000b2000200229020037020420004107360200200041146a200241106a2802003602002000410c6a200241086a2902003702000c100b20004108360200200020012903083703080c0f0b200041093602000c0e0b2000410a360200200020012802043602040c0d0b2000410b360200200020012802043602040c0c0b2000410c360200200020012802043602040c0b0b200e417f4c0d0241002d00fca3c680001a200e41002802c8a3c68000118180808000002201450d090b200ead42208620012010200e10848e808000ad8421030b2000200d3602102000200e36020420004103360200200041186a200d360200200041146a200f360200200041086a20033703000c080b10ae80808000000b4101200110b280808000000b4101200d10b280808000000b4101200d10b280808000000b4101200110b280808000000b4101200110b280808000000b4101200110b280808000000b4101200e10b280808000000b200241c0006a2480808080000b8b1004057f017e037f087e23808080800041c0026b220224808080800002400240024002400240024002400240024002400240024002400240024002400240200128020022032802042204450d0020032004417f6a36020420032003280200220441016a36020020042d00000e0c0102030405060708090a0b0d0c0b200041003602000c0f0b024002402001280200220328020422044120490d002003200441606a220536020420032003280200220141206a220636020020024180026a41086a200141086a29000037030020024180026a41106a200141106a29000037030020024180026a41186a200141186a290000370300200220012900003703800220054120490d002003200441406a22053602042003200141c0006a360200200241a0026a41086a200641086a290000370300200241a0026a41106a200641106a290000370300200241a0026a41186a200641186a290000370300200220062900003703a00220054108490d002003200441b87f6a22063602042003200141c8006a36020020064108490d00200129004021072003200441b07f6a3602042003200141d0006a360200200241c0016a41206a220320022903a002370300200241c0016a41086a220420024180026a41086a290300370300200241c0016a41106a220620024180026a41106a290300370300200241c0016a41186a220520024180026a41186a290300370300200241c0016a41286a2208200241a0026a41086a290300370300200241c0016a41306a2209200241a0026a41106a290300370300200241c0016a41386a220a200241a0026a41186a29030037030020022002290380023703c0012001290048210b20024180016a41386a2201200a29030037030020024180016a41306a2009290300220c37030020024180016a41286a2008290300220d37030020024180016a41206a2003290300220e37030020024180016a41186a2005290300220f37030020024180016a41106a2006290300221037030020024180016a41086a20042903002211370300200220022903c001221237038001200241f0006a200c370200200241e8006a200d370200200241e0006a200e370200200241d8006a200f370200200241d0006a2010370200200241c8006a2011370200200241f8006a200129030037020020022012370240200041046a2002413c6a41c40010848e8080001a2000200b37035020002007370348410121030c010b410021030b200020033602000c0e0b200241086a200110bc8a808000024020022802080d002002413c6a2001200228020c10d085808000200228023c418080808078460d002000200229023c3702042000410c6a200241c4006a280200360200200041023602000c0e0b200041003602000c0d0b200241106a200110bc8a80800020022802100d0a2002413c6a2001200228021410d085808000200228023c2203418080808078460d0a20022802442106200228024021042002413c6a200110c78c8080000240200228023c418180808078460d002000200229023c3702042000410c6a200241c4006a280200360200200020063602182000200436021420002003360210200041033602000c0d0b200041003602002003450d0c200441002802c0a3c68000118080808000000c0c0b200241206a200110bc8a80800020022802200d0a2002413c6a2001200228022410d085808000200228023c2203418080808078460d0a2002280244210620022802402104200241186a200110bc8a808000024020022802180d002002413c6a2001200228021c10d085808000200228023c418080808078460d002000200229023c370210200041186a200241c4006a2802003602002000200636020c2000200436020820002003360204200041043602000c0c0b200041003602002003450d0b200441002802c0a3c68000118080808000000c0b0b200241286a200110bc8a808000024020022802280d002002413c6a2001200228022c10d085808000200228023c418080808078460d002000200229023c3702042000410c6a200241c4006a280200360200200041053602000c0b0b200041003602000c0a0b200241306a200110bc8a808000024020022802300d002002413c6a2001200228023410d085808000200228023c418080808078460d002000200229023c3702042000410c6a200241c4006a280200360200200041063602000c0a0b200041003602000c090b2002413c6a2001109f8d808000024020022d003c4105460d002000200229023c370204200041146a200241cc006a2802003602002000410c6a200241c4006a290200370200200041073602000c090b200041003602000c080b02402001280200220328020422014108490d00200041083602002003200141786a36020420032003280200220141086a360200200020012900003703080c080b200041003602000c070b200041093602000c060b02402001280200220328020422014104490d0020032001417c6a36020420032003280200220141046a36020020012800002203418094ebdc034b0d00200020033602042000410a3602000c060b200041003602000c050b02402001280200220328020422014104490d002000410b36020020032001417c6a36020420032003280200220141046a360200200020012800003602040c050b200041003602000c040b200041003602000c030b02402001280200220328020422014104490d002000410c36020020032001417c6a36020420032003280200220141046a360200200020012800003602040c030b200041003602000c020b200041003602000c010b200041003602000b200241c0026a2480808080000bc60603057f017e027f23808080800041c0006b2201248080808000200141186a41f7abc38000410441b9a9c380004135418097c38000410010d882808000200141106a42043702002001420037020820014280808080800137020041002d00fca3c680001a024002400240412041002802c8a3c68000118180808000002202450d002002410036021820024101360204200241ab99c3800036020020014101360238200120023602302001200241206a36023c200120023602342001200141306a10fa8680800041002d00fca3c680001a20012802002103200128020421022001280208210420012802182105200129021c2106410841002802c8a3c68000118180808000002207450d01200741002903c0acc380003702002001410036020820014280808080c000370200200141306a200141c8acc38000410a10b28b8080002001200141306a41d2acc38000410c10fe8a808000200141306a200141deacc38000410e10938b8080002001200141306a41ecacc38000411210b38b808000200141306a200141feacc38000411410a28b8080002001200141306a4192adc38000410c10a68b808000200141306a2001419eadc38000411710ac8b8080002001200141306a41b5adc38000411210a98b808000200141306a200141c7adc38000411510a18b8080002001200141306a41dcadc38000410a109d8b808000200141306a200141e6adc38000410410828b808000200141246a200141306a41eaadc38000410e10908b8080002001200128022436020820012001280228220836020020012008200128022c41246c6a36020c20012008360204200141306a200110fb868080002005418080808078460d022001200336020820012002360200200120023602042001200220044105746a36020c200041c4006a200110fa868080002001410b6a200141306a41086a2802003600002000413c6a200637020020002005360238200041013a0000200041d8006a4101360200200041d4006a2007360200200041013602502001200129023037000320002001290000370001200041086a200141076a290000370000200141c0006a2480808080000f0b4108412010b280808000000b4104410810b280808000000b41a8d8c480004111419cd9c4800010a181808000000beb0201027f23808080800041206b220124808080800041002d00fca3c680001a0240413041002802c8a3c680001181808080000022020d004108413010b280808000000b200242d0ffe6c89b859fc430370318200242ecb2f697e9f8a9f860370308200242ef899cf7a6a3ca9dd100370300200241b382808000360210200241206a42919fe7f19ec9d1a177370300200241286a41b4828080003602002001200241306a36021c200141023602182001200236021420012002360210200141046a200141106a108487808000200142888080808001370210200142808080808001370218200041c4006a200141106a10fa868080002001411b6a200141046a41086a280200360000200041c0006a410036020020004280808080c000370338200041043a0000200041d8006a410036020020004280808080c0003703502001200129020437001320002001290010370001200041086a200141176a290000370000200141206a2480808080000bcb0704067f017e017f017e23808080800041d0006b2201248080808000200141286a41e1bdc38000410941cfbdc38000411041a0b5c38000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d00200242c194a6a793ccc3a857370308200241e0bdc38000360220200241f58180800036021820024101360204200241dfbdc38000360200200241306a4293888c8f89fdc6ec9e7f370300200241286a42a5e9e3ab9e929adc2c370300200241106a42ab8bffbed784ffa5937f370300200241386a41ef80808000360200200241246a410136020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a2001411c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c20024201370224200241a1a4c580003602202002410b36021c20024196a4c58000360218200241f581808000360210200242ab8bffbed784ffa5937f370308200242c194a6a793ccc3a857370300200141c0006a41086a200828020041016a2202360200200120012902102209370340024020022009a7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c2002420137022420024195a4c580003602202002410d36021c20024188a4c58000360218200241ef8080800036021020024293888c8f89fdc6ec9e7f370308200242a5e9e3ab9e929adc2c370300200128024821082001280244210220012001280240360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000bec0201027f23808080800041206b220124808080800041002d00fca3c680001a0240413041002802c8a3c680001181808080000022020d004108413010b280808000000b200242f099b79de1dbade83c370318200242a6829fd5dedbeecd9a7f370308200242f993ea84fdedefb2d000370300200241b582808000360210200241206a4297b1e6929387a1b844370300200241286a41b6828080003602002001200241306a36021c200141023602182001200236021420012002360210200141046a200141106a108487808000200142888080808001370210200142808080808001370218200041c4006a200141106a10fa868080002001411b6a200141046a41086a280200360000200041c0006a410036020020004280808080c000370338200041043a0000200041d8006a410036020020004280808080c0003703502001200129020437001320002001290010370001200041086a200141176a290000370000200141206a2480808080000bec0201027f23808080800041206b220124808080800041002d00fca3c680001a0240413041002802c8a3c680001181808080000022020d004108413010b280808000000b200242a5e9e3ab9e929adc2c370318200242e9b494c69bdbc4d608370308200242ead283debcdebd93d800370300200241f780808000360210200241206a4293888c8f89fdc6ec9e7f370300200241286a41ef808080003602002001200241306a36021c200141023602182001200236021420012002360210200141046a200141106a108487808000200142888080808001370210200142808080808001370218200041c4006a200141106a10fa868080002001411b6a200141046a41086a280200360000200041c0006a410036020020004280808080c000370338200041043a0000200041d8006a410036020020004280808080c0003703502001200129020437001320002001290010370001200041086a200141176a290000370000200141206a2480808080000bec0201027f23808080800041206b220124808080800041002d00fca3c680001a0240413041002802c8a3c680001181808080000022020d004108413010b280808000000b200242d0ffe6c89b859fc430370318200242c6bb82f1efc886d58a7f370308200242c6bdb6a4b5ec93c2d300370300200241b782808000360210200241206a42919fe7f19ec9d1a177370300200241286a41b4828080003602002001200241306a36021c200141023602182001200236021420012002360210200141046a200141106a108487808000200142888080808001370210200142808080808001370218200041c4006a200141106a10fa868080002001411b6a200141046a41086a280200360000200041c0006a410036020020004280808080c000370338200041043a0000200041d8006a410036020020004280808080c0003703502001200129020437001320002001290010370001200041086a200141176a290000370000200141206a2480808080000bcb0704067f017e017f017e23808080800041d0006b2201248080808000200141286a41c8bdc38000410741cfbdc38000411041a0b5c38000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d00200242c194a6a793ccc3a857370308200241e0bdc38000360220200241f58180800036021820024101360204200241dfbdc38000360200200241306a4293888c8f89fdc6ec9e7f370300200241286a42a5e9e3ab9e929adc2c370300200241106a42ab8bffbed784ffa5937f370300200241386a41ef80808000360200200241246a410136020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a2001411c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c20024201370224200241a1a4c580003602202002410b36021c20024196a4c58000360218200241f581808000360210200242ab8bffbed784ffa5937f370308200242c194a6a793ccc3a857370300200141c0006a41086a200828020041016a2202360200200120012902102209370340024020022009a7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c2002420137022420024195a4c580003602202002410d36021c20024188a4c58000360218200241ef8080800036021020024293888c8f89fdc6ec9e7f370308200242a5e9e3ab9e929adc2c370300200128024821082001280244210220012001280240360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000bd20301027f23808080800041206b220124808080800041002d00fca3c680001a024041e00041002802c8a3c680001181808080000022020d00410841e00010b280808000000b200242ccf3faf4aeedbdfafb00370348200242fc86a1bbccd1ddf472370330200242e1a1e0ae85b5ea80fd00370318200242dca9a2e881dac9f914370308200242b4fd99f0e7c3fbd60e370300200241b882808000360210200241d0006a42cda992f8a2a6d9f2897f370300200241386a428bebb7a9dff4a9fe6a370300200241206a42b285eeed9386eed0d700370300200241d8006a41b982808000360200200241c0006a41ba82808000360200200241286a41bb828080003602002001200241e0006a36021c200141043602182001200236021420012002360210200141046a200141106a108487808000200142888080808001370210200142808080808001370218200041c4006a200141106a10fa868080002001411b6a200141046a41086a280200360000200041c0006a410036020020004280808080c000370338200041043a0000200041d8006a410036020020004280808080c0003703502001200129020437001320002001290010370001200041086a200141176a290000370000200141206a2480808080000b9f0503067f017e027f23808080800041d0006b2201248080808000200141286a41d8aec38000410a41a0b5c38000412541a0b5c38000410010d882808000200141206a42043702002001420037021820014280808080800137021041002d00fca3c680001a02400240412041002802c8a3c68000118180808000002202450d002002410036021820024101360204200241c5b5c3800036020020014101360248200120023602402001200241206a36024c20012002360244200141106a200141c0006a10fa86808000200141086a22032001411c6a220241086a28020036020020012002290200370300200128021021042001280214210520012802182106200129022c21072001280228210820014100360218200142808080808001370210200141106a410010a4868080002001280214200128021841386c6a2202420437022c20024208370224200241f8e3c4800036022020024100360218200241bc82808000360210200242899ac8f29d8ce69ac300370308200242e78dcee4d0becc9757370300200128021821092001280214210220012001280210360248200120023602402001200236024420012002200941386c6a41386a36024c200141346a200141c0006a10f9868080002008418080808078460d012001200436021820012005360210200120053602142001200520064105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002008360238200041003a000020002001290300370350200041d8006a20032802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b4108412010b280808000000b41a8d8c480004111419cd9c4800010a181808000000bab0503067f017e027f23808080800041d0006b2201248080808000200141286a41bcaec3800041114198b0c38000411d41a0b5c38000410010d882808000200141206a42043702002001420037021820014280808080800137021041002d00fca3c680001a02400240412041002802c8a3c68000118180808000002202450d002002410036021820024101360204200241c5b5c3800036020020014101360248200120023602402001200241206a36024c20012002360244200141106a200141c0006a10fa86808000200141086a22032001411c6a220241086a28020036020020012002290200370300200128021021042001280214210520012802182106200129022c21072001280228210820014100360218200142808080808001370210200141106a410010a4868080002001280214200128021841386c6a2202420437022c20024204370224200241e3a6c580003602202002410436021c200241dfa6c58000360218200241bd82808000360210200242cf94e6f2afe8d5d9aa7f370308200242c1b18585f195bbb0d600370300200128021821092001280214210220012001280210360248200120023602402001200236024420012002200941386c6a41386a36024c200141346a200141c0006a10f9868080002008418080808078460d012001200436021820012005360210200120053602142001200520064105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002008360238200041003a000020002001290300370350200041d8006a20032802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b4108412010b280808000000b41a8d8c480004111419cd9c4800010a181808000000b890403057f017e017f23808080800041d0006b2201248080808000200141286a41cdaec38000410b41ceb1c38000412641a0b5c38000410010d882808000200141206a42043702002001420037021820014280808080800137021041002d00fca3c680001a02400240412041002802c8a3c68000118180808000002202450d002002410036021820024101360204200241c5b5c3800036020020014101360248200120023602402001200241206a36024c20012002360244200141106a200141c0006a10fa86808000200141086a22032001411c6a220241086a28020036020020012002290200370300200128021021042001280214210220012802182105200129022c210620012802282107200142808080808001370248200142888080808001370240200141346a200141c0006a10f9868080002007418080808078460d012001200436021820012002360210200120023602142001200220054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200637020020002007360238200041003a000020002001290300370350200041d8006a20032802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b4108412010b280808000000b41a8d8c480004111419cd9c4800010a181808000000bdb0901027f0240024002400240024002400240024002400240024002400240024020002d00000e0e000102030405060708090a0b0c0d000b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a41003a00000f0b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a41013a00000f0b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a41023a00000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41033a00002001200241016a2202360208200041106a2d00002103024020012802002002470d0020012002410110b182808000200128020821020b200128020420026a20033a00002001200241016a22023602080240200128020020026b41034b0d0020012002410410b182808000200128020821020b200128020420026a200028020c3600002001200241046a3602080f0b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a41043a00000f0b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a41053a00000f0b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a41063a00000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41073a00002001200241016a220236020820002d00012100024020012802002002470d0020012002410110b182808000200128020821020b2001200241016a360208200128020420026a20003a00000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41083a00002001200241016a220236020820002d00012100024020012802002002470d0020012002410110b182808000200128020821020b2001200241016a360208200128020420026a20003a00000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41093a00002001200241016a220236020820002d00012100024020012802002002470d0020012002410110b182808000200128020821020b2001200241016a360208200128020420026a20003a00000f0b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a410a3a00000f0b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a410b3a00000f0b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a410c3a00000f0b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a410d3a00000bde0803017f047e017f23808080800041b0016b220224808080800020002903082203210420002903002205210602402001290300500d00200141106a2903002204200320042003541b210420012903082206200520062005541b21060b0240024020032004520d0020052006510d010b200320047d2103200520067d210520002d00102101200241086a10ce88808000200241086a210002400240024020010e03020001020b200241186a21000c010b200241286a21000b200042002000290300220420057d220520052004561b370300200042002000290308220520037d220320032005561b37030820024190016a200241086a41286a290300370300200241e0006a41286a200241086a41206a290300370300200241e0006a41206a200241086a41186a290300370300200241e0006a41186a200241086a41106a290300370300200241e0006a41106a200241086a41086a29030037030020022002290308370368200242013703602002200241e0006a41086a36023c200242fc90b9e5d09296e777370348200242a6d4e6f1a4dd959860370340200242f89aef8eef91e1ce967f370358200242b4d6d6dfccc6b592c3003703502002413c6a200241c0006a412010f98c8080000b02404100280284a4c680004105470d00200241086a10ce88808000200241be828080003602442002200241086a3602404100280290a1c680002100410028028ca1c6800021014100280280a4c68000210720024198016a420137020020024190016a410136020020024188016a410f36020020024184016a41bfb1c38000360200200241f8006a41d0b0c38000ad4280808080f00d84370200200241ec006a41ceb1c38000ad4280808080e0048437020020024194016a200241c0006a360200200241c8b0c3800036028c012002410536028001200241003602742002410036026820024281808080c021370260200141ecf2c08000200741024622071b200241e0006a200041d4f2c0800020071b280210118480808000000b02404100280284a4c680004105470d00200242fc90b9e5d09296e777370368200242a6d4e6f1a4dd95986037036020024295f38cfcb699a3c4ef00370378200242a8db95cdaa86daa7193703702002200241e0006a412010d088808000200241bf8280800036020c20022002280204410020022802001b3602402002200241c0006a3602084100280290a1c680002100410028028ca1c6800021014100280280a4c68000210720024198016a420137020020024190016a410136020020024188016a410f36020020024184016a41bfb1c38000360200200241f8006a41d0b0c38000ad4280808080f00d84370200200241ec006a41ceb1c38000ad4280808080e0048437020020024194016a200241086a36020020024188b2c3800036028c012002410536028001200241003602742002410036026820024281808080a022370260200141ecf2c08000200741024622071b200241e0006a200041d4f2c0800020071b280210118480808000000b200241b0016a2480808080000bb50603017f017e057f23808080800041b0016b2201248080808000200142fc90b9e5d09296e777370308200142a6d4e6f1a4dd959860370300200142f89aef8eef91e1ce967f370318200142b4d6d6dfccc6b592c300370310200141206a2001412041002802b8a1c68000118580808000000240024002402001280220450d00200141306a41086a200141206a41086a29020022023703002001200129022037033020012002a72203360248200120012802342204360244200141e8006a200141c4006a10f18c8080002001290368500d0102400240417f4100280284a4c680002205410147200541014b1b2205417f460d00200541ff01710d010b200141dc006a4194afc3800041022001412010dc8a808000200141cc006a410c6a41c082808000360200200141c1828080003602502001200141af016a3602542001200141dc006a36024c4100280290a1c680002105410028028ca1c6800021064100280280a4c680002107200141a0016a420237020020014198016a410236020020014190016a41103602002001418c016a4184bac3800036020020014180016a4194bac38000ad4280808080900d84370200200141e8006a410c6a41fdbac38000ad42808080808004843702002001419c016a200141cc006a360200200141f4b9c380003602940120014101360288012001410036027c2001410036027020014281808080c003370268200641ecf2c08000200741024622071b200141e8006a200541d4f2c0800020071b28021011848080800000200128025c450d00200128026041002802c0a3c68000118080808000000b2001413c6a20042003200128023028020c118580808000000b20004200370300200041286a4200370300200041206a4200370300200041186a4200370300200041106a4200370300200041086a42003703000c010b20002001290370370300200041286a20014198016a290300370300200041206a200141e8006a41286a290300370300200041186a200141e8006a41206a290300370300200041106a200141e8006a41186a290300370300200041086a200141e8006a41106a2903003703002001413c6a20042003200128023028020c118580808000000b200141b0016a2480808080000b7401017f23808080800041106b22022480808080002002200041206a36020c200141d3cdc38000411041e3cdc380004106200041eccdc3800041fccdc38000410b200041106a41eccdc380004187cec3800041092002410c6a4190cec3800010e0808080002100200241106a24808080800020000bbf0403027f017e027f2380808080004190016b220324808080800041002104200341086a2001200241002802b8a1c68000118580808000000240024020032802080d000c010b200341186a41086a200341086a41086a290200220537030020032003290208370318200328021c2106024002402005a722074104490d0020062800002102410121040c010b02400240417f4100280284a4c680002204410147200441014b1b2204417f460d00200441ff01710d010b2003413c6a4194afc3800041022001200210dc8a8080002003412c6a410c6a41c082808000360200200341c18280800036023020032003418f016a36023420032003413c6a36022c4100280290a1c680002104410028028ca1c6800021024100280280a4c68000210120034180016a4202370200200341f8006a4102360200200341f0006a4110360200200341ec006a4184bac38000360200200341e0006a4194bac38000ad4280808080900d84370200200341c8006a410c6a41fdbac38000ad4280808080800484370200200341fc006a2003412c6a360200200341f4b9c38000360274200341013602682003410036025c2003410036025020034281808080c003370248200241ecf2c08000200141024622011b200341c8006a200441d4f2c0800020011b28021011848080800000200328023c450d00200328024041002802c0a3c68000118080808000000b41002102410021040b200341246a20062007200328021828020c118580808000000b200020023602042000200436020020034190016a2480808080000bed0201037f2380808080004180016b220224808080800002400240024002400240200128021c22034110710d0020034120710d0120003502004101200110fe8080800021000c020b20002802002100410021030340200220036a41ff006a413041d7002000410f712204410a491b20046a3a00002003417f6a210320004110492104200041047621002004450d000b20034180016a22004180014b0d022001410141c48dc080004102200220036a4180016a410020036b10db8080800021000c010b20002802002100410021030340200220036a41ff006a413041372000410f712204410a491b20046a3a00002003417f6a210320004110492104200041047621002004450d000b20034180016a22004180014b0d022001410141c48dc080004102200220036a4180016a410020036b10db8080800021000b20024180016a24808080800020000f0b200041800141c88dc08000109481808000000b200041800141c88dc08000109481808000000bc00301077f23808080800041206b220224808080800041002d00fca3c680001a0240410141002802c8a3c68000118180808000002203450d0020032001417f6a3a0000200241013602102002200336020c20024101360208200241086a4101410310ab86808000200228020c2203200228021022046a220541003b0000200541026a41003a000020022802082105024002400240200441036a22044104470d00200341036a2d0000210420032d0002210620032d0001210720032d000021082005450d01200341002802c0a3c68000118080808000000c010b2005418080808078470d01200341187621042003411076210620034180fe03714108762107200321080b200041033a00002000410d6a20073a00002000410c6a20083a0000200041106a41013a00002000410e6a20063a00002000410f6a20043a0000200041086a200141187441808080786a411875410274220341b0cec380006a2802003602002000200341a0cec380006a280200360204200241206a2480808080000f0b2002200436021c200220033602182002200536021441e0b3c3800041cb00200241146a4188b5c3800041d0b3c38000108981808000000b4101410110b280808000000bfc0e04037f037e017f017e23808080800041f0036b2202248080808000200241306a20002d00102203200110d7888080000240024020022d0030450d0020022f0132210120022d003121000c010b20022802342104200210ce88808000200241306a10e1898080002000290308210520002903002106024002400240024020030e03000102000b427f427f200520024180016a2903007c220720072005541b22052001ad7c220720072005541b2107427f200620022903787c220520052006541b2105200241306a21080c020b427f427f2005200241d8016a2903007c220720072005541b22052001ad7c220720072005541b2107427f2006200241d0016a2903007c220520052006541b210520024188016a21080c010b427f427f2005200241b0026a2903007c220720072005541b22052001ad7c220720072005541b2107427f2006200241a8026a2903007c220520052006541b2105200241e0016a21080b0240024002400240200829031822094200520d002008290330500d010b2002210102400240024020030e03020001020b200241106a21010c010b200241206a21010b02402001290300220620057c22052006540d002001290308220620077c22072006540d0020012007370308200120053703000c020b410021000240417f4100280284a4c680002201410447200141044b1b2201417f460d00200141ff01710d030b410021004100280290a1c680002101410028028ca1c6800021034100280280a4c680002108200241d8036a4200370200200241d4036a41a0b5c38000360200200241d0036a4101360200200241c8036a410f360200200241c4036a41bfb1c38000360200200241b8036a41d0b0c38000ad4280808080f00d84370200200241ac036a41ceb1c38000ad4280808080e00484370200200241bcb7c380003602cc03200241043602c003200241003602b403200241003602a80320024281808080c0133702a003200341ecf2c08000200841024622081b200241a0036a200141d4f2c0800020081b280210118480808000000c020b2002210102400240024020030e03020001020b200241106a21010c010b200241206a21010b2001427f2001290308220620077c220720072006541b3703082001427f2001290300220620057c220520052006541b3703000b2002210102400240024020030e03020001020b200241106a21010c010b200241206a21010b20012903082106200129030021050240024002402009500d002005200841206a290300560d012006200841286a290300560d010b0240427f427f2002290300220720022903107c220920092007541b220720022903207c220920092007541b20022903c802560d00427f427f20022903082207200241186a2903007c220920092007541b2207200241286a2903007c220920092007541b200241d0026a290300580d020b2008290330500d0102402005200841386a290300560d002006200841c0006a290300580d020b410021000240417f4100280284a4c680002201410447200141044b1b2201417f460d00200141ff01710d030b200241a0036a410c6a4200370200200241013602a40320024184b7c380003602a003200241a0b5c380003602a8032002418c036a410c6a41263602002002418cb7c3800036029c03200241ceb1c38000360294032002410f36029003200241bfb1c3800036028c0341002100200241a0036a41042002418c036a4100200210a182808000410621010c030b410021000240417f4100280284a4c680002201410447200141044b1b2201417f460d00200141ff01710d020b410021004100280290a1c680002101410028028ca1c6800021034100280280a4c680002108200241d8036a4200370200200241d4036a41a0b5c38000360200200241d0036a4101360200200241c8036a410f360200200241c4036a41bfb1c38000360200200241b8036a41d0b0c38000ad4280808080f00d84370200200241ac036a41ceb1c38000ad4280808080e00484370200200241dcb6c380003602cc03200241043602c003200241003602b403200241003602a80320024281808080a0153702a003200341ecf2c08000200841024622081b200241a0036a200141d4f2c0800020081b28021011848080800000410621010c020b200241d8026a41286a200241286a290300370300200241d8026a41206a200241206a290300370300200241d8026a41186a200241186a290300370300200241d8026a41106a200241106a290300370300200241d8026a41086a200241086a290300370300200220022903003703d802200010d888808000220041ff01714102470d00200242fc90b9e5d09296e7773703a803200242a6d4e6f1a4dd9598603703a00320024295f38cfcb699a3c4ef003703b803200242a8db95cdaa86daa7193703b0032002200436028c03200241a0036a41202002418c036a410441002802e0a1c6800011868080800000200242fc90b9e5d09296e7773703a803200242a6d4e6f1a4dd9598603703a003200242f89aef8eef91e1ce967f3703b803200242b4d6d6dfccc6b592c3003703b003200241d8026a200241a0036a4120108b8d808000410221000c010b410621010b200241f0036a2480808080002001410874200041ff0171720bc40702067f027e23808080800041c0016b2202248080808000024002400240024020012d000822030d00410021010c010b200241fc006a200141096a10e881808000024020022d007c0d0041002104410121030c020b200241c7006a41196a200241fc006a41196a290000370000200241c7006a41116a200241fc006a41116a290000370000200241c7006a41096a200241fc006a41096a2900003700002002200229007d370048410121010b200220013a004702400240417f4100280284a4c680002204410447200441044b1b2204450d00200441ff017141ff01460d00200241306a200241d3006a290000370300200241386a200241db006a2900003703002002413d6a200241e0006a2900003700002002200229004b37032820022d004a210520022d0049210420022d004821030c010b0240024020030d0020024180808080783602700c010b200241f0006a4194afc380004102200241c8006a10db8a8080000b200241c28280800036026c2002200241f0006a3602684100280290a1c680002101410028028ca1c6800021044100280280a4c680002103200241b4016a4201370200200241ac016a4101360200200241a4016a4116360200200241a0016a4196afc3800036020020024194016a41acafc38000ad4280808080c00d84370200200241fc006a410c6a4198b0c38000ad4280808080d00384370200200241b0016a200241e8006a3602002002418cafc380003602a8012002410436029c012002410036029001200241003602840120024281808080e01237027c200441ecf2c08000200341024622031b200241fc006a200141d4f2c0800020031b28021011848080800000024020022802702201418080808078460d002001450d00200228027441002802c0a3c68000118080808000000b200241306a200241c7006a410c6a290000370300200241386a200241db006a2900003703002002413d6a200241e0006a2900003700002002200229004b37032820022d0048210320022d0049210420022d004a210520022d004722014102460d010b200241086a41156a2206200241286a41156a290000370000200241086a41106a2207200241286a41106a290300370300200241086a41086a200241286a41086a2903002208370300200220022903282209370308200020053a0003200020043a0002200020033a0001200020013a0000200020093700042000410c6a2008370000200041146a2007290300370000200041196a20062900003700000c010b200020033a0001200041023a0000200041036a20053a0000200041026a20043a00000b200241c0016a2480808080000bc70201027f23808080800041106b2202248080808000024002402000280200418080808078470d00200128021441c6c8c380004104200141186a28020028020c1182808080000021010c010b200220003602002002200128021441cac8c380004104200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a200241d0c8c38000108d81808000210120022d000c21000240200128020022030d00200041ff017141004721010c010b41012101200041ff01710d0020022802082100024020034101470d0020022d000d41ff0171450d0020002d001c4104710d00410121012000280214418ca5c080004101200041186a28020028020c118280808000000d010b2000280214418da5c080004101200041186a28020028020c1182808080000021010b200241106a24808080800020010bb00701037f23808080800041a0026b2204248080808000200441013a00880120044204370380012004420037037820044280808080c0003703702004427f37036820044200370360200441013a0028200442043703202004420037031820044280808080c0003703102004427f37030820044200370300200441c0016a200441e0006a200410fd84808000200441e0006a20022d0010200310d78880800002400240024020042d00600d0041062103200210d888808000220241ff01714102470d01200441013a0058200442043703502004420037034820044280808080c0003703402004427f37033820044200370330200441e0006a200441c0016a200441306a10fd84808000200441013a00b801200442043703b001200442003703a80120044280808080c0003703a0012004427f370398012004420037039001200441c0016a200441e0006a20044190016a10fd84808000200441013a0098022004420437039002200442003703880220044280808080c000370380022004427f3703f801200442003703f0012000200441c0016a200441f0016a10fd848080000c020b20042f0162210320042d006121020b2000418080808078360210200041026a20034108763a000020002003410874200241ff0171723b0100200441d4016a28020021050240200441d8016a2802002200450d002000410171210641002103024020004101460d002000417e7121022005210041002103034002402000280200450d00200041046a28020041002802c0a3c68000118080808000000b02402000410c6a280200450d00200041106a28020041002802c0a3c68000118080808000000b200041186a21002002200341026a2203470d000b0b2006450d0020052003410c6c6a2200280200450d00200028020441002802c0a3c68000118080808000000b024020042802d001450d00200541002802c0a3c68000118080808000000b200441e0016a28020021050240200441e4016a2802002200450d002000410171210641002103024020004101460d002000417e7121022005210041002103034002402000280200450d00200041046a28020041002802c0a3c68000118080808000000b02402000410c6a280200450d00200041106a28020041002802c0a3c68000118080808000000b200041186a21002002200341026a2203470d000b0b2006450d0020052003410c6c6a2200280200450d00200028020441002802c0a3c68000118080808000000b20042802dc01450d00200541002802c0a3c68000118080808000000b200441a0026a2480808080000bfe0401047f2380808080004180016b22032480808080002003418080c0023602142003428080f0818080802837020c200342fc90b9e5d09296e777370338200342a6d4e6f1a4dd95986037033020034295f38cfcb699a3c4ef00370348200342a8db95cdaa86daa7193703402003200341306a412010d0888080002003417f2003280204410020032802001b220420026a220220022004491b2204360218200341146a2105200341106a21062003410c6a2102024002400240200141ff01710e03020001020b200621020c010b200521020b02400240200420022802004b0d0020002004360204410021020c010b02400240417f4100280284a4c680002202410447200241044b1b2202450d00200241ff017141ff01470d010b2003410c6a2102024002400240200141ff01710e03020001020b200621020c010b200521020b2003411c6a410c6a41c38280800036020020034185808080003602202003200236022c20032003412c6a3602242003200341186a36021c4100280290a1c680002102410028028ca1c6800021044100280280a4c680002101200341e8006a4202370200200341e0006a4102360200200341d8006a410f360200200341d4006a41bfb1c38000360200200341c8006a41d0b0c38000ad4280808080f00d84370200200341306a410c6a41ceb1c38000ad4280808080e00484370200200341e4006a2003411c6a360200200341e8b5c3800036025c20034104360250200341003602442003410036023820034281808080f009370230200441ecf2c08000200141024622011b200341306a200241d4f2c0800020011b280210118480808000000b200041800c3b0001410121020b200020023a000020034180016a2480808080000bec0302037f027e23808080800041d0026b2201248080808000200141086a10e189808000200141086a210202400240024020002d00100e03020001020b200141e0006a21020c010b200141b8016a21020b41022103024020022802004101470d00200229031021040240200029030020022903082205560d0020002903082004580d010b200120043703b802200120053703b00202400240417f4100280284a4c680002202410447200241044b1b2202450d00200241ff017141ff01470d010b200141c0026a410c6a41c482808000360200200141c4828080003602c402200120003602c0022001200141b0026a3602c8024100280290a1c680002102410028028ca1c6800021004100280280a4c680002103200141c0006a4202370200200141386a4102360200200141306a410f3602002001412c6a41bfb1c38000360200200141206a41d0b0c38000ad4280808080f00d84370200200141086a410c6a41ceb1c38000ad4280808080e004843702002001413c6a200141c0026a360200200141a8b6c38000360234200141043602282001410036021c2001410036021020014281808080e006370208200041ecf2c08000200341024622031b200141086a200241d4f2c0800020031b280210118480808000000b410021030b200141d0026a248080808000200341800c720b9d0b010b7f23808080800041106b22012480808080002001410036020c20014280808080800137020441002d00fca3c680001a0240024002400240413841002802c8a3c68000118180808000002202450d002002410a360204200241d8aec38000360200200241286a2203429ccab49c93e2e495cf00370300200241206a220442d1c5efcdcd82cde1ff00370300200241106a220542dca9a2e881dac9f914370300200241086a220642b4fd99f0e7c3fbd60e370300200241306a2207419382808000360200200241186a220841b882808000360200200141046a4100410110a78680800020012802082209200128020c220a41386c6a220b2002290300370300200b41086a2006290300370300200b41106a2005290300370300200b41186a2008290300370300200b41206a2004290300370300200b41286a2003290300370300200b41306a20072903003703002001200a41016a220336020c200241002802c0a3c680001180808080000041002d00fca3c680001a413841002802c8a3c68000118180808000002202450d01200242d1c5efcdcd82cde1ff00370320200242e1a1e0ae85b5ea80fd003703082002419382808000360230200241bb828080003602182002410b360204200241cdaec38000360200200241286a429ccab49c93e2e495cf00370300200241106a42b285eeed9386eed0d700370300024020012802042003470d00200141046a2003410110a78680800020012802082109200128020c21030b2009200341386c6a220b2002290300370300200b41306a200241306a290300370300200b41286a200241286a290300370300200b41206a200241206a290300370300200b41186a200241186a290300370300200b41106a200241106a290300370300200b41086a200241086a2903003703002001200341016a220336020c200241002802c0a3c680001180808080000041002d00fca3c680001a413841002802c8a3c68000118180808000002202450d02200242d1c5efcdcd82cde1ff00370320200242fc86a1bbccd1ddf4723703082002419382808000360230200241ba8280800036021820024112360204200241b9d0c38000360200200241286a429ccab49c93e2e495cf00370300200241106a428bebb7a9dff4a9fe6a370300024020012802042003470d00200141046a2003410110a78680800020012802082109200128020c21030b2009200341386c6a220b2002290300370300200b41306a200241306a290300370300200b41286a200241286a290300370300200b41206a200241206a290300370300200b41186a200241186a290300370300200b41106a200241106a290300370300200b41086a200241086a2903003703002001200341016a220336020c200241002802c0a3c680001180808080000041002d00fca3c680001a413841002802c8a3c68000118180808000002202450d032002428cc882a2e2dcdde2f000370320200242ccf3faf4aeedbdfafb003703082002418682808000360230200241b98280800036021820024111360204200241bcaec38000360200200241286a42aa8be08180efaa931e370300200241106a42cda992f8a2a6d9f2897f370300024020012802042003470d00200141046a2003410110a78680800020012802082109200128020c21030b2009200341386c6a220b2002290300370300200b41306a200241306a290300370300200b41286a200241286a290300370300200b41206a200241206a290300370300200b41186a200241186a290300370300200b41106a200241106a290300370300200b41086a200241086a290300370300200141046a41086a220b200341016a360200200241002802c0a3c6800011808080800000200041086a200b28020036020020002001290204370200200141106a2480808080000f0b4108413810b280808000000b4108413810b280808000000b4108413810b280808000000b4108413810b280808000000b961303027f027e017f2380808080004180036b220624808080800041012107200641013a00d001200642043703c801200642003703c00120064280808080c0003703b8012006427f3703b001200642003703a80120012903002108200641b8026a200210fa87808000024002400240024020062802ec0220062802f00272450d004103210720062903b80222092008560d0041002d00fca3c680001a0240024002400240410c41002802c8a3c68000118180808000002201450d0041002d00fca3c680001a412841002802c8a3c68000118180808000002207450d0120072002290000370000200741186a200241186a290000370000200741106a200241106a290000370000200741086a200241086a290000370000200720083700202001412836020820012007360204200141283602000240024020092008540d0041042107410021020c010b41002d00fca3c680001a410c41002802c8a3c68000118180808000002207450d0341002d00fca3c680001a412841002802c8a3c6800011818080800000220a450d04200a2002290000370000200a41186a200241186a290000370000200a41106a200241106a290000370000200a41086a200241086a290000370000200a2008427f7c370020200741283602082007200a36020420074128360200410121020b200641013a00302006410136022c2006200136022820064101360224200620023602202006200736021c200620023602182006427f37031020064200370308200641b8026a200641a8016a200641086a10fd84808000200641a8016a20042d0010200510d7888080000240024020062d00a8010d0041062102200410d888808000220741ff01714102470d01200641013a0060200642043703582006420037035020064280808080c0003703482006427f37034020064200370338200641e8006a200641b8026a200641386a10fd848080004100280284a4c680004105460d070c080b20062f01aa01210220062d00a90121070b2000418080808078360210200041026a20024108763a000020002002410874200741ff0171723b0100200641cc026a28020021010240200641d0026a2802002200450d002000410171210441002102024020004101460d002000417e7121072001210041002102034002402000280200450d00200041046a28020041002802c0a3c68000118080808000000b02402000410c6a280200450d00200041106a28020041002802c0a3c68000118080808000000b200041186a21002007200241026a2202470d000b0b2004450d0020012002410c6c6a2200280200450d00200028020441002802c0a3c68000118080808000000b024020062802c802450d00200141002802c0a3c68000118080808000000b200641d8026a28020021010240200641dc026a2802002200450d002000410171210441002102024020004101460d002000417e7121072001210041002102034002402000280200450d00200041046a28020041002802c0a3c68000118080808000000b02402000410c6a280200450d00200041106a28020041002802c0a3c68000118080808000000b200041186a21002007200241026a2202470d000b0b2004450d0020012002410c6c6a2200280200450d00200028020441002802c0a3c68000118080808000000b20062802d402450d07200141002802c0a3c68000118080808000000c070b4104410c10b280808000000b4101412810b280808000000b4104410c10b280808000000b4101412810b280808000000b2000418080808078360210200041026a41003a0000200020074108743b01000c020b4100280290a1c680002102410028028ca1c6800021074100280280a4c680002101200641f0026a4200370200200641ec026a41e8d1c38000360200200641e8026a4101360200200641e0026a4116360200200641dc026a41e88bc48000360200200641d0026a41f48ac48000ad4280808080900c84370200200641c4026a41fe8bc48000ad4280808080e00284370200200641e08bc480003602e402200641053602d802200641003602cc02200641003602c0022006428180808090243702b802200741ecf2c08000200141024622011b200641b8026a200241d4f2c0800020011b280210118480808000000b024002400240200328020041736a220241034b0d0020024102460d00200641013a00d00141002102200641003602cc0120064280808080c0003702c401200642043702bc012006427f3703b001200642003703a8010c010b200641a8016a200310d18c80800020062802b8012202418080808078460d010b20064198016a41086a200641a8016a41086a2903002208370300200641d8016a411c6a200641a8016a411c6a290200370200200641d8016a41246a200641a8016a41246a290200370200200641d8016a412c6a200641a8016a412c6a280200360200200641d8016a41086a2008370300200620062903a801220837039801200620062902bc013702ec01200620083703d801200620023602e801200641b8026a200641e8006a200641d8016a10fd84808000200641013a00b002200642043703a802200642003703a00220064280808080c000370398022006427f3703900220064200370388022000200641b8026a20064188026a10fd848080000c010b200620062d00aa0122023a009a01200620062f01a80122073b019801200041026a20023a0000200020073b01002000418080808078360210200641fc006a2802002101024020064180016a2802002200450d002000410171210441002102024020004101460d002000417e7121072001210041002102034002402000280200450d00200041046a28020041002802c0a3c68000118080808000000b02402000410c6a280200450d00200041106a28020041002802c0a3c68000118080808000000b200041186a21002007200241026a2202470d000b0b2004450d0020012002410c6c6a2200280200450d00200028020441002802c0a3c68000118080808000000b02402006280278450d00200141002802c0a3c68000118080808000000b20064188016a280200210102402006418c016a2802002200450d002000410171210441002102024020004101460d002000417e7121072001210041002102034002402000280200450d00200041046a28020041002802c0a3c68000118080808000000b02402000410c6a280200450d00200041106a28020041002802c0a3c68000118080808000000b200041186a21002007200241026a2202470d000b0b2004450d0020012002410c6c6a2200280200450d00200028020441002802c0a3c68000118080808000000b200628028401450d00200141002802c0a3c68000118080808000000b20064180036a2480808080000b140020002802003502004101200110fe808080000bc00301077f23808080800041206b220224808080800041002d00fca3c680001a0240410141002802c8a3c68000118180808000002203450d0020032001417f6a3a0000200241013602102002200336020c20024101360208200241086a4101410310ab86808000200228020c2203200228021022046a220541003b0000200541026a41003a000020022802082105024002400240200441036a22044104470d00200341036a2d0000210420032d0002210620032d0001210720032d000021082005450d01200341002802c0a3c68000118080808000000c010b2005418080808078470d01200341187621042003411076210620034180fe03714108762107200321080b200041033a00002000410d6a20073a00002000410c6a20083a0000200041106a41003a00002000410e6a20063a00002000410f6a20043a0000200041086a200141187441808080786a411875410274220341c0cec380006a2802003602002000200341e4cec380006a280200360204200241206a2480808080000f0b2002200436021c200220033602182002200536021441e0b3c3800041cb00200241146a4188b5c3800041a0b8c38000108981808000000b4101410110b280808000000be90403017f017e037f2380808080004190016b2203248080808000200341086a2001200241002802b8a1c68000118580808000000240024020032802080d00200041003a00000c010b200341186a41086a200341086a41086a290200220437030020032003290208370318200328021c2105024002402004a722064120490d00200041013a000020002005290000370001200041196a200541186a290000370000200041116a200541106a290000370000200041096a200541086a2900003700000c010b02400240417f4100280284a4c680002207410147200741014b1b2207417f460d00200741ff01710d010b2003413c6a4194afc3800041022001200210dc8a8080002003412c6a410c6a41c082808000360200200341c18280800036023020032003418f016a36023420032003413c6a36022c4100280290a1c680002102410028028ca1c6800021014100280280a4c68000210720034180016a4202370200200341f8006a4102360200200341f0006a4110360200200341ec006a4184bac38000360200200341e0006a4194bac38000ad4280808080900d84370200200341c8006a410c6a41fdbac38000ad4280808080800484370200200341fc006a2003412c6a360200200341f4b9c38000360274200341013602682003410036025c2003410036025020034281808080c003370248200141ecf2c08000200741024622071b200341c8006a200241d4f2c0800020071b28021011848080800000200328023c450d00200328024041002802c0a3c68000118080808000000b200041003a00000b200341246a20052006200328021828020c118580808000000b20034190016a2480808080000b2100200128021441c4cac380004105200141186a28020028020c118280808000000b140020012000280204200028020810dd808080000bb60503017f017e047f23808080800041b0026b2202248080808000200241086a2001412041002802b8a1c68000118580808000000240024020022802080d00200042043703000c010b200241186a41086a200241086a41086a29020022033703002002200229020837031820022003a722043602a0012002200228021c220536029c01024002402004450d0020022004417f6a3602a0012002200541016a36029c01420321030240024020052d00000e020100020b200241b0016a2002419c016a10ae8c80800020022903b00122034203510d01200241286a200241b8016a41f00010848e8080001a0b200041086a200241286a41f00010848e8080001a200020033703000c010b02400240417f4100280284a4c680002206410147200641014b1b2206417f460d00200641ff01710d010b200241a4016a4194afc3800041022001412010dc8a808000200241286a410c6a41c082808000360200200241c18280800036022c2002200241af026a3602302002200241a4016a3602284100280290a1c680002101410028028ca1c6800021064100280280a4c680002107200241e8016a4202370200200241e0016a4102360200200241d8016a4110360200200241d4016a4184bac38000360200200241c8016a4194bac38000ad4280808080900d84370200200241b0016a410c6a41fdbac38000ad4280808080800484370200200241e4016a200241286a360200200241f4b9c380003602dc01200241013602d001200241003602c401200241003602b80120024281808080c0033702b001200641ecf2c08000200741024622071b200241b0016a200141d4f2c0800020071b2802101184808080000020022802a401450d0020022802a80141002802c0a3c68000118080808000000b200042043703000b200241246a20052004200228021828020c118580808000000b200241b0026a2480808080000be60603017f017e047f2380808080004190016b220324808080800020032001200241002802b8a1c68000118580808000000240024020032802000d0020004180808080783602000c010b200341106a41086a200341086a29020022043703002003200329020037031020032004a72205360228200320032802142206360224024002402005450d0020032005417f6a22073602282003200641016a36022402400240024002400240024020062d000022084103710e0400030102000b200841027621080c040b20054104490d0420032005417c6a3602282003200641046a360224200641036a2d000041187420062f000141087472200872220741027621082007418080044921070c020b200841044f0d0320054105490d0320032005417b6a3602282003200641056a360224200628000122084180808080044921070c010b2007450d0220032005417e6a3602282003200641026a36022420062d000141087420087241ffff03712207410276210820074180024921070b20070d0120084180024b0d010b200341c8006a200341246a200810c68580800020032802482208418080808078460d002000200329024c370204200020083602000c010b02400240417f4100280284a4c680002208410147200841014b1b2208417f460d00200841ff01710d010b2003413c6a4194afc3800041022001200210dc8a8080002003412c6a410c6a41c082808000360200200341c18280800036023020032003418f016a36023420032003413c6a36022c4100280290a1c680002102410028028ca1c6800021014100280280a4c68000210820034180016a4202370200200341f8006a4102360200200341f0006a4110360200200341ec006a4184bac38000360200200341e0006a4194bac38000ad4280808080900d84370200200341c8006a410c6a41fdbac38000ad4280808080800484370200200341fc006a2003412c6a360200200341f4b9c38000360274200341013602682003410036025c2003410036025020034281808080c003370248200141ecf2c08000200841024622081b200341c8006a200241d4f2c0800020081b28021011848080800000200328023c450d00200328024041002802c0a3c68000118080808000000b20004180808080783602000b2003411c6a20062005200328021028020c118580808000000b20034190016a2480808080000b9c0903017f017e057f2380808080004190016b220224808080800020022001412041002802b8a1c68000118580808000000240024020022802000d0020004180808080783602000c010b200241106a41086a200241086a29020022033703002002200229020037031020022003a72204360228200220022802142205360224024002402004450d0020022004417f6a22063602282002200541016a3602240240024002400240024020052d000022074103710e0403020001030b20044104490d0420022004417c6a3602282002200541046a360224200541036a2d000041187420052f000141087472200772220741808004490d04200741027621070c030b200741044f0d0320044105490d0320022004417b6a3602282002200541056a360224200528000122074180808080044f0d020c030b2006450d0220022004417e6a3602282002200541026a36022420052d000141087420077241ffff0371220741ff014d0d02200741027621070c010b200741027621070b200241c8006a200241246a200710c58580800020022802482207418080808078460d000240200229024c2203422088a7410b490d000240417f4100280284a4c680002201410247200141024b1b2201417f460d00200141ff01710d010b200241e0818080003602402002410636023020024198b5c3800036022c20022002412c6a36023c4100280290a1c680002101410028028ca1c6800021064100280280a4c68000210820024180016a4201370200200241f8006a4102360200200241f0006a4107360200200241ec006a419cc2c38000360200200241e0006a41a3c2c38000ad4280808080b00e84370200200241d4006a41afc1c38000ad4280808080d00484370200200241fc006a2002413c6a3602002002418cc2c38000360274200241023602682002410036025c20024100360250200242818080809016370248200641ecf2c08000200841024622081b200241c8006a200141d4f2c0800020081b280210118480808000000b20002003370204200020073602000c010b02400240417f4100280284a4c680002207410147200741014b1b2207417f460d00200741ff01710d010b2002413c6a4194afc3800041022001412010dc8a8080002002412c6a410c6a41c082808000360200200241c18280800036023020022002418f016a36023420022002413c6a36022c4100280290a1c680002101410028028ca1c6800021074100280280a4c68000210620024180016a4202370200200241f8006a4102360200200241f0006a4110360200200241ec006a4184bac38000360200200241e0006a4194bac38000ad4280808080900d84370200200241c8006a410c6a41fdbac38000ad4280808080800484370200200241fc006a2002412c6a360200200241f4b9c38000360274200241013602682002410036025c2002410036025020024281808080c003370248200741ecf2c08000200641024622061b200241c8006a200141d4f2c0800020061b28021011848080800000200228023c450d00200228024041002802c0a3c68000118080808000000b20004180808080783602000b2002411c6a20052004200228021028020c118580808000000b20024190016a2480808080000b140020012000280200200028020410dd808080000bbb0503017f017e037f2380808080004190016b2203248080808000200341086a2001200241002802b8a1c68000118580808000000240024020032802080d00200042003703000c010b200341186a41086a200341086a41086a290200220437030020032003290208370318200328021c2105024002402004a722064108490d002006417c7122074108460d002007410c460d0020074110460d002006416c6a4128490d0020052900002104200528000821022000200528000c36023c2000200236023820002004370308200042013703002000200529002c37031020002005280010360240200020052900243703302000200529001c37032820002005290014370320200041186a200541346a2900003703000c010b02400240417f4100280284a4c680002207410147200741014b1b2207417f460d00200741ff01710d010b2003413c6a4194afc3800041022001200210dc8a8080002003412c6a410c6a41c082808000360200200341c18280800036023020032003418f016a36023420032003413c6a36022c4100280290a1c680002102410028028ca1c6800021014100280280a4c68000210720034180016a4202370200200341f8006a4102360200200341f0006a4110360200200341ec006a4184bac38000360200200341e0006a4194bac38000ad4280808080900d84370200200341c8006a410c6a41fdbac38000ad4280808080800484370200200341fc006a2003412c6a360200200341f4b9c38000360274200341013602682003410036025c2003410036025020034281808080c003370248200141ecf2c08000200741024622071b200341c8006a200241d4f2c0800020071b28021011848080800000200328023c450d00200328024041002802c0a3c68000118080808000000b200042003703000b200341246a20052006200328021828020c118580808000000b20034190016a2480808080000be90403017f017e047f2380808080004190016b2202248080808000200241086a2001412041002802b8a1c68000118580808000000240024020022802080d00200041003a00000c010b200241186a41086a200241086a41086a290200220337030020022002290208370318200228021c2104024002402003a722054120490d00200041013a000020002004290000370001200041196a200441186a290000370000200041116a200441106a290000370000200041096a200441086a2900003700000c010b02400240417f4100280284a4c680002206410147200641014b1b2206417f460d00200641ff01710d010b2002413c6a4194afc3800041022001412010dc8a8080002002412c6a410c6a41c082808000360200200241c18280800036023020022002418f016a36023420022002413c6a36022c4100280290a1c680002101410028028ca1c6800021064100280280a4c68000210720024180016a4202370200200241f8006a4102360200200241f0006a4110360200200241ec006a4184bac38000360200200241e0006a4194bac38000ad4280808080900d84370200200241c8006a410c6a41fdbac38000ad4280808080800484370200200241fc006a2002412c6a360200200241f4b9c38000360274200241013602682002410036025c2002410036025020024281808080c003370248200641ecf2c08000200741024622071b200241c8006a200141d4f2c0800020071b28021011848080800000200228023c450d00200228024041002802c0a3c68000118080808000000b200041003a00000b200241246a20042005200228021828020c118580808000000b20024190016a2480808080000bc30405017f017e027f017e027f2380808080004190016b2202248080808000200241086a2001412041002802b8a1c68000118580808000000240024020022802080d00420021030c010b200241186a41086a200241086a41086a290200220337030020022002290208370318200228021c2104024002402003a722054108490d0020042900002106420121030c010b02400240417f4100280284a4c680002207410147200741014b1b2207417f460d00200741ff01710d010b2002413c6a4194afc3800041022001412010dc8a8080002002412c6a410c6a41c082808000360200200241c18280800036023020022002418f016a36023420022002413c6a36022c4100280290a1c680002101410028028ca1c6800021074100280280a4c68000210820024180016a4202370200200241f8006a4102360200200241f0006a4110360200200241ec006a4184bac38000360200200241e0006a4194bac38000ad4280808080900d84370200200241c8006a410c6a41fdbac38000ad4280808080800484370200200241fc006a2002412c6a360200200241f4b9c38000360274200241013602682002410036025c2002410036025020024281808080c003370248200741ecf2c08000200841024622081b200241c8006a200141d4f2c0800020081b28021011848080800000200228023c450d00200228024041002802c0a3c68000118080808000000b42002106420021030b200241246a20042005200228021828020c118580808000000b200020063703082000200337030020024190016a2480808080000bbf0405017f017e027f017e027f2380808080004190016b2202248080808000200241086a2001412041002802b8a1c68000118580808000000240024020022802080d00420021030c010b200241186a41086a200241086a41086a290200220337030020022002290208370318200228021c2104024002402003a722054108490d0020042900002106420121030c010b02400240417f4100280284a4c680002207410147200741014b1b2207417f460d00200741ff01710d010b2002413c6a4194afc3800041022001412010dc8a8080002002412c6a410c6a41c082808000360200200241c18280800036023020022002418f016a36023420022002413c6a36022c4100280290a1c680002101410028028ca1c6800021074100280280a4c68000210820024180016a4202370200200241f8006a4102360200200241f0006a4110360200200241ec006a4184bac38000360200200241e0006a4194bac38000ad4280808080900d84370200200241c8006a410c6a41fdbac38000ad4280808080800484370200200241fc006a2002412c6a360200200241f4b9c38000360274200241013602682002410036025c2002410036025020024281808080c003370248200741ecf2c08000200841024622081b200241c8006a200141d4f2c0800020081b28021011848080800000200228023c450d00200228024041002802c0a3c68000118080808000000b420021030b200241246a20042005200228021828020c118580808000000b200020063703082000200337030020024190016a2480808080000bec0405017f017e037f017e017f2380808080004190016b2202248080808000200241086a2001412041002802b8a1c68000118580808000000240024020022802080d00200041033a00100c010b200241186a41086a200241086a41086a290200220337030020022002290208370318200228021c2104024002402003a722054108490d0020054178714108460d0020054110460d0020042d0010220641024b0d002004290000210320042900082107200020063a001020002007370308200020033703000c010b02400240417f4100280284a4c680002206410147200641014b1b2206417f460d00200641ff01710d010b2002413c6a4194afc3800041022001412010dc8a8080002002412c6a410c6a41c082808000360200200241c18280800036023020022002418f016a36023420022002413c6a36022c4100280290a1c680002101410028028ca1c6800021064100280280a4c68000210820024180016a4202370200200241f8006a4102360200200241f0006a4110360200200241ec006a4184bac38000360200200241e0006a4194bac38000ad4280808080900d84370200200241c8006a410c6a41fdbac38000ad4280808080800484370200200241fc006a2002412c6a360200200241f4b9c38000360274200241013602682002410036025c2002410036025020024281808080c003370248200641ecf2c08000200841024622081b200241c8006a200141d4f2c0800020081b28021011848080800000200228023c450d00200228024041002802c0a3c68000118080808000000b200041033a00100b200241246a20042005200228021828020c118580808000000b20024190016a2480808080000baa0703027f017e047f2380808080004190016b2203248080808000200341086a2001200241002802b8a1c68000118580808000000240024020032802080d0041808080807821040c010b200341186a41086a200341086a41086a290200220537030020032003290208370318200328021c2106024002402005a72207450d002007417f6a21080240024002400240024020062d000022044103710e0400030201000b200641016a2109200441027621040c030b20074105490d03200441034b0d0320062800012204418080808004490d03200641056a21092007417b6a21080c020b20074104490d02200641036a2d000041187420062f000141087472200472220441808004490d0220044102762104200641046a21092007417c6a21080c010b2008450d0120062d000141087420047241ffff03712204418002490d01200641026a21092007417e6a2108200441027621040b20082004490d0002400240024020040d00410121080c010b2004417f4c0d03200441002802c8a3c68000118180808000002208450d01200841002004108a8e8080001a0b20082009200410848e8080002108200341246a20062007200328021828020c118580808000002001200241002802a0a1c68000118480808000002004ad4220862008ad8421050c030b4101200410b280808000000b02400240417f4100280284a4c680002204410147200441014b1b2204417f460d00200441ff01710d010b2003413c6a4194afc3800041022001200210dc8a8080002003412c6a410c6a41c082808000360200200341c18280800036023020032003418f016a36023420032003413c6a36022c4100280290a1c680002104410028028ca1c6800021024100280280a4c68000210120034180016a4202370200200341f8006a4102360200200341f0006a4110360200200341ec006a4184bac38000360200200341e0006a4194bac38000ad4280808080900d84370200200341c8006a410c6a41fdbac38000ad4280808080800484370200200341fc006a2003412c6a360200200341f4b9c38000360274200341013602682003410036025c2003410036025020034281808080c003370248200241ecf2c08000200141024622011b200341c8006a200441d4f2c0800020011b28021011848080800000200328023c450d00200328024041002802c0a3c68000118080808000000b200341246a20062007200328021828020c1185808080000041808080807821040c010b10ae80808000000b200020053702042000200436020020034190016a2480808080000ba90903027f017e067f23808080800041b0016b2201248080808000200142fc90b9e5d09296e777370308200142a6d4e6f1a4dd959860370300200142a0b9ab8f9ae5999778370318200142f999a7c78cd1d1cdb17f370310200141286a2001412041002802b8a1c6800011858080800000024002400240024020012802280d0041818080807821020c010b200141386a41086a200141286a41086a290200220337030020012001290228370338200128023c21040240024002402003a722050d000c010b2005417f6a210602400240024002400240024020042d000022024103710e0400010402000b200441016a2107200241027621080c040b20060d010c040b20054105490d03200241034b0d0320042800012208418080808004490d03200441056a21072005417b6a21060c020b20042d000141087420027241ffff03712202418002490d02200441026a21072005417e6a2106200241027621080c010b20054104490d01200441036a2d000041187420042f000141087472200272220241808004490d0120024102762108200441046a21072005417c6a21060b2006450d002006417f6a21090240024002400240024020072d000022024103710e0400010203000b200741016a2107200241027621020c030b2009450d0320072d000141087420027241ffff03712202418002490d03200741026a21072006417e6a2109200241027621020c020b20064104490d02200741036a2d000041187420072f000141087472200272220241808004490d0220024102762102200741046a21072006417c6a21090c010b20064105490d01200241034b0d0120072800012202418080808004490d01200741056a21072006417b6a21090b20092002490d000240024020020d00410121060c010b2002417f4c0d04200241002802c8a3c68000118180808000002206450d05200641002002108a8e8080001a0b2002ad42208620062007200210848e808000ad8421030c010b02400240417f4100280284a4c680002202410147200241014b1b2202417f460d00200241ff01710d010b200141dc006a4194afc3800041022001412010dc8a808000200141cc006a410c6a41c082808000360200200141c1828080003602502001200141af016a3602542001200141dc006a36024c4100280290a1c680002102410028028ca1c6800021064100280280a4c680002107200141a0016a420237020020014198016a410236020020014190016a41103602002001418c016a4184bac3800036020020014180016a4194bac38000ad4280808080900d84370200200141e8006a410c6a41fdbac38000ad42808080808004843702002001419c016a200141cc006a360200200141f4b9c380003602940120014101360288012001410036027c2001410036027020014281808080c003370268200641ecf2c08000200741024622071b200141e8006a200241d4f2c0800020071b28021011848080800000200128025c450d00200128026041002802c0a3c68000118080808000000b41818080807821020b200141c4006a20042005200128023828020c118580808000000b2000200836020c2000200337020420002002360200200141b0016a2480808080000f0b10ae80808000000b4101200210b280808000000bb90703017f017e057f23808080800041b0016b2201248080808000200142f0fe93e98686d7ab8c7f37030820014280eee1b0e3b7afe9987f37030020014282fceae49dd9e2861d370318200142de8c84a1ecd0a6d30c370310200141206a2001412041002802b8a1c68000118580808000000240024002402001280220450d00200141306a41086a200141206a41086a29020022023703002001200129022037033020012002a72203360248200120012802342204360244024002402003450d0020012003417f6a22053602482001200441016a3602440240024002400240024020042d000022064103710e0400030102000b200641027621060c030b20034104490d0320012003417c6a3602482001200441046a360244200441036a2d000041187420042f000141087472200672220641808004490d03200641027621060c020b200641044f0d0220034105490d0220012003417b6a3602482001200441056a360244200428000122064180808080044f0d010c020b2005450d0120012003417e6a3602482001200441026a36024420042d000141087420067241ffff03712206418002490d01200641027621060b200141e8006a200141c4006a200610ca8580800020012802682206418080808078460d00200129026c21020c010b02400240417f4100280284a4c680002206410147200641014b1b2206417f460d00200641ff01710d010b200141dc006a4194afc3800041022001412010dc8a808000200141cc006a410c6a41c082808000360200200141c1828080003602502001200141af016a3602542001200141dc006a36024c4100280290a1c680002106410028028ca1c6800021054100280280a4c680002107200141a0016a420237020020014198016a410236020020014190016a41103602002001418c016a4184bac3800036020020014180016a4194bac38000ad4280808080900d84370200200141e8006a410c6a41fdbac38000ad42808080808004843702002001419c016a200141cc006a360200200141f4b9c380003602940120014101360288012001410036027c2001410036027020014281808080c003370268200541ecf2c08000200741024622071b200141e8006a200641d4f2c0800020071b28021011848080800000200128025c450d00200128026041002802c0a3c68000118080808000000b41808080807821060b2001413c6a20042003200128023028020c118580808000002006418080808078470d010b2000410036020820004280808080103702000c010b20002002370204200020063602000b200141b0016a2480808080000ba20503027f017e047f23808080800041b0016b2201248080808000200142fc90b9e5d09296e777370308200142a6d4e6f1a4dd95986037030020014293bb8a9cbb9ab6b31a370318200142ffabedd185d3d8d216370310200141286a2001412041002802b8a1c68000118580808000000240024020012802280d00410321020c010b200141386a41086a200141286a41086a290200220337030020012001290228370338200128023c21040240024002402003a72205450d000240024020042d000022020e03000401020b2005417b6a417b4b0d0120042800012106410021020c030b410221020c010b02400240417f4100280284a4c680002206410147200641014b1b2206417f460d00200641ff01710d010b200141dc006a4194afc3800041022001412010dc8a808000200141cc006a410c6a41c082808000360200200141c1828080003602502001200141af016a3602542001200141dc006a36024c4100280290a1c680002106410028028ca1c6800021024100280280a4c680002107200141a0016a420237020020014198016a410236020020014190016a41103602002001418c016a4184bac3800036020020014180016a4194bac38000ad4280808080900d84370200200141e8006a410c6a41fdbac38000ad42808080808004843702002001419c016a200141cc006a360200200141f4b9c380003602940120014101360288012001410036027c2001410036027020014281808080c003370268200241ecf2c08000200741024622071b200141e8006a200641d4f2c0800020071b28021011848080800000200128025c450d00200128026041002802c0a3c68000118080808000000b410321020b0b200141c4006a20042005200128023828020c118580808000000b2000200636020420002002360200200141b0016a2480808080000b910503027f017e057f23808080800041b0016b2200248080808000200042fc90b9e5d09296e777370308200042a6d4e6f1a4dd959860370300200042beee8ae9ce8fa2b1977f37031820004295d4f9afa2868fb86437031041002101200041286a2000412041002802b8a1c680001185808080000002402000280228450d00200041386a41086a200041286a41086a290200220237030020002000290228370338200028023c2103024002402002a72201450d0020032d000041ff01712204450d004101410220044101461b21040c010b410021040b02402004410220011b22044102470d000240417f4100280284a4c680002205410147200541014b1b2205417f460d00200541ff01710d010b200041dc006a4194afc3800041022000412010dc8a808000200041cc006a410c6a41c082808000360200200041c1828080003602502000200041af016a3602542000200041dc006a36024c4100280290a1c680002105410028028ca1c6800021064100280280a4c680002107200041a0016a420237020020004198016a410236020020004190016a41103602002000418c016a4184bac3800036020020004180016a4194bac38000ad4280808080900d84370200200041e8006a410c6a41fdbac38000ad42808080808004843702002000419c016a200041cc006a360200200041f4b9c380003602940120004101360288012000410036027c2000410036027020004281808080c003370268200641ecf2c08000200741024622071b200041e8006a200541d4f2c0800020071b28021011848080800000200028025c450d00200028026041002802c0a3c68000118080808000000b200041c4006a20032001200028023828020c11858080800000200441017121010b200041b0016a24808080800020010bb80703017f017e057f23808080800041b0016b2201248080808000200142fc90b9e5d09296e777370308200142a6d4e6f1a4dd959860370300200142f4fa97f89782c7a62d37031820014299cfe7ffe3b8eac708370310200141206a2001412041002802b8a1c68000118580808000000240024002402001280220450d00200141306a41086a200141206a41086a29020022023703002001200129022037033020012002a72203360248200120012802342204360244024002402003450d0020012003417f6a22053602482001200441016a3602440240024002400240024020042d000022064103710e0400030102000b200641027621060c030b20034104490d0320012003417c6a3602482001200441046a360244200441036a2d000041187420042f000141087472200672220641808004490d03200641027621060c020b200641044f0d0220034105490d0220012003417b6a3602482001200441056a360244200428000122064180808080044f0d010c020b2005450d0120012003417e6a3602482001200441026a36024420042d000141087420067241ffff03712206418002490d01200641027621060b200141e8006a200141c4006a200610c98580800020012802682206418080808078460d00200129026c21020c010b02400240417f4100280284a4c680002206410147200641014b1b2206417f460d00200641ff01710d010b200141dc006a4194afc3800041022001412010dc8a808000200141cc006a410c6a41c082808000360200200141c1828080003602502001200141af016a3602542001200141dc006a36024c4100280290a1c680002106410028028ca1c6800021054100280280a4c680002107200141a0016a420237020020014198016a410236020020014190016a41103602002001418c016a4184bac3800036020020014180016a4194bac38000ad4280808080900d84370200200141e8006a410c6a41fdbac38000ad42808080808004843702002001419c016a200141cc006a360200200141f4b9c380003602940120014101360288012001410036027c2001410036027020014281808080c003370268200541ecf2c08000200741024622071b200141e8006a200641d4f2c0800020071b28021011848080800000200128025c450d00200128026041002802c0a3c68000118080808000000b41808080807821060b2001413c6a20042003200128023028020c118580808000002006418080808078470d010b2000410036020820004280808080c0003702000c010b20002002370204200020063602000b200141b0016a2480808080000ba80603027f017e047f23808080800041d0016b2201248080808000200142fc90b9e5d09296e777370328200142a6d4e6f1a4dd959860370320200142f7b6fccfd083b2cf4d370338200142afd2c6ffdbc495bc08370330200141c8006a200141206a412041002802b8a1c68000118580808000000240024020012802480d00410221020c010b200141d8006a41086a200141c8006a41086a290200220337030020012001290248370358200128025c2104024002402003a722054120490d000240024020054120460d0020042d002041ff01712202450d004101410220024101461b21020c010b410021020b20054120460d0020024102460d00200141186a200441186a290000370300200141106a200441106a290000370300200141086a200441086a290000370300200120042900003703000c010b02400240417f4100280284a4c680002202410147200241014b1b2202417f460d00200241ff01710d010b200141fc006a4194afc380004102200141206a412010dc8a808000200141ec006a410c6a41c082808000360200200141c1828080003602702001200141cf016a3602742001200141fc006a36026c4100280290a1c680002102410028028ca1c6800021064100280280a4c680002107200141c0016a4202370200200141b8016a4102360200200141b0016a4110360200200141ac016a4184bac38000360200200141a0016a4194bac38000ad4280808080900d8437020020014188016a410c6a41fdbac38000ad4280808080800484370200200141bc016a200141ec006a360200200141f4b9c380003602b401200141013602a8012001410036029c01200141003602900120014281808080c00337028801200641ecf2c08000200741024622071b20014188016a200241d4f2c0800020071b28021011848080800000200128027c450d0020012802800141002802c0a3c68000118080808000000b410221020b200141e4006a20042005200128025828020c118580808000000b200020023a000020002001290300370001200041096a200141086a290300370000200041116a200141106a290300370000200041196a200141186a290300370000200141d0016a2480808080000b900201037f23808080800041306b2201248080808000200142919fd78da1a1ad84ff003703082001429ceccef7a6c0dedd2037030020014292d189e4a1e58a9fcc00370318200142aa9f83c8cbf687edfa003703100240024020002802082202417f4c0d0041002d00fca3c680001a200241286c410472220241002802c8a3c68000118180808000002203450d0120012003360228200120023602242001410036022c2000200141246a108087808000200128022421002001412020012802282202200128022c41002802e0a1c680001186808080000002402000450d00200241002802c0a3c68000118080808000000b200141306a2480808080000f0b10ae80808000000b4101200210b280808000000b8e0201037f23808080800041306b2201248080808000200142919fd78da1a1ad84ff003703082001429ceccef7a6c0dedd2037030020014282fceae49dd9e2861d370318200142de8c84a1ecd0a6d30c3703100240024020002802082202417f4c0d0041002d00fca3c680001a200241286c410472220241002802c8a3c68000118180808000002203450d0120012003360228200120023602242001410036022c2000200141246a108087808000200128022421002001412020012802282202200128022c41002802e0a1c680001186808080000002402000450d00200241002802c0a3c68000118080808000000b200141306a2480808080000f0b10ae80808000000b4101200210b280808000000b860203017f017e027f23808080800041306b2201248080808000200142919fd78da1a1ad84ff003703082001429ceccef7a6c0dedd20370300200142d8d2dfcce2f8e59341370318200142faa5fa8ea9819ff1bd7f3703102000290300210241002d00fca3c680001a02404101410e20024203511b220341002802c8a3c68000118180808000002204450d0020012004360228200120033602242001410036022c2000200141246a10b28c808000200128022421002001412020012802282203200128022c41002802e0a1c680001186808080000002402000450d00200341002802c0a3c68000118080808000000b200141306a2480808080000f0b4101200310b280808000000bae0201037f23808080800041306b2201248080808000200142919fd78da1a1ad84ff003703082001429ceccef7a6c0dedd2037030020014282fceae49dd9e2861d370318200142de8c84a1ecd0a6d30c3703100240024020002802082202417f4c0d0041002d00fca3c680001a200241286c410472220241002802c8a3c68000118180808000002203450d0120012003360228200120023602242001410036022c2000200141246a108087808000200128022421022001412020012802282203200128022c41002802e0a1c680001186808080000002402002450d00200341002802c0a3c68000118080808000000b02402000280200450d00200028020441002802c0a3c68000118080808000000b200141306a2480808080000f0b10ae80808000000b4101200210b280808000000b840604027f017e047f017e23808080800041b0016b2201248080808000200142919fd78da1a1ad84ff003703082001429ceccef7a6c0dedd2037030020014290f1f7a1ea9083df363703182001428b87a199bee8b0f0ac7f370310200141286a2001412041002802b8a1c68000118580808000000240024020012802280d00410321020c010b200141386a41086a200141286a41086a290200220337030020012001290228370338200128023c2104024002402003a72205450d0020054109490d0020042d000041ff01714101470d00200541776a4108490d0020054111460d0020042d001122024103490d010b02400240417f4100280284a4c680002202410147200241014b1b2202417f460d00200241ff01710d010b200141dc006a4194afc3800041022001412010dc8a808000200141cc006a410c6a41c082808000360200200141c1828080003602502001200141af016a3602542001200141dc006a36024c4100280290a1c680002102410028028ca1c6800021064100280280a4c680002107200141a0016a420237020020014198016a410236020020014190016a41103602002001418c016a4184bac3800036020020014180016a4194bac38000ad4280808080900d84370200200141e8006a410c6a41fdbac38000ad42808080808004843702002001419c016a200141cc006a360200200141f4b9c380003602940120014101360288012001410036027c2001410036027020014281808080c003370268200641ecf2c08000200741024622071b200141e8006a200241d4f2c0800020071b28021011848080800000200128025c450d00200128026041002802c0a3c68000118080808000000b200141c4006a20042005200128023828020c11858080800000410321020c010b2004290001210820042900092103200141c4006a20042005200128023828020c118580808000002001412041002802a0a1c68000118480808000000b200020033703082000200837030020002001280068360011200020023a0010200041146a200141eb006a280000360000200141b0016a2480808080000be10201027f23808080800041c0006b2201248080808000200142fc90b9e5d09296e777370308200142a6d4e6f1a4dd959860370300200142f4fa97f89782c7a62d37031820014299cfe7ffe3b8eac7083703100240024002400240024020002d000022020e050001020304000b2001200041016a36023420012000410c6a2902003702380c030b2001200041016a36023420012000410c6a2902003702380c020b2001200041016a36023420012000410c6a2902003702380c010b2001200041086a2902003702340b20012002360230200141246a200141306a10ed8480800020014120200141246a4100280298a1c680001185808080000002400240024002400240024020002d00000e0400010203050b200041086a21000c030b200041086a21000c020b200041086a21000c010b200041046a21000b2000280200450d00200028020441002802c0a3c68000118080808000000b200141c0006a2480808080000b9c0706017f027e027f017e037f017e23808080800041c0016b2200248080808000200042919fd78da1a1ad84ff003703182000429ceccef7a6c0dedd20370310200042a4feeaa7f9c4ff8f0e370328200042e99dbcf9dba59e96b37f370320200041306a200041106a412041002802b8a1c68000118580808000000240024020002802300d00420021010c010b200041c0006a41086a200041306a41086a29020022023703002000200029023037034020002802442103024002402002a722044108490d0020044178714108460d0020032900082105420121010c010b02400240417f4100280284a4c680002206410147200641014b1b2206417f460d00200641ff01710d010b200041e4006a4194afc380004102200041106a412010dc8a808000200041d4006a410c6a41c082808000360200200041c1828080003602582000200041bf016a36025c2000200041e4006a3602544100280290a1c680002106410028028ca1c6800021074100280280a4c680002108200041a8016a4202370200200041a0016a410236020020004198016a411036020020004194016a4184bac3800036020020004188016a4194bac38000ad4280808080900d84370200200041f0006a410c6a41fdbac38000ad4280808080800484370200200041a4016a200041d4006a360200200041f4b9c3800036029c01200041013602900120004100360284012000410036027820004281808080c003370270200741ecf2c08000200841024622081b200041f0006a200641d4f2c0800020081b280210118480808000002000280264450d00200028026841002802c0a3c68000118080808000000b420021010b200041cc006a20032004200028024028020c118580808000000b200042fc90b9e5d09296e777370378200042a6d4e6f1a4dd959860370370200042d3d8c5d2a9b9d2c1ac7f3703880120004282ca868eabf3add0cf00370380012000200041f0006a10e6888080002000290308210220002903002109200042919fd78da1a1ad84ff003703782000429ceccef7a6c0dedd20370370200042a4feeaa7f9c4ff8f0e37038801200042e99dbcf9dba59e96b37f3703800141002d00fca3c680001a0240411041002802c8a3c680001181808080000022040d004101411010b280808000000b2004420020052001501b3700002004200242002009a71b370008200041f0006a41202004411041002802e0a1c6800011868080800000200441002802c0a3c6800011808080800000200041c0016a2480808080000bee0e05017f017e047f017e017f23808080800041c0016b2202248080808000200242919fd78da1a1ad84ff003703082002429ceccef7a6c0dedd2037030020024282ea96a7a8d58af34c370318200242b7f092fcc2eaf1f65f370310200241286a2002412041002802b8a1c68000118580808000000240024002400240024002402002280228450d00200241386a41086a200241286a41086a29020022033703002002200229022837033820022003a7220436024c2002200228023c2205360248024002402004450d0020022004417f6a220636024c2002200541016a36024802400240024002400240024020052d000022074103710e0400030102000b200741027621070c040b20044104490d0420022004417c6a36024c2002200541046a360248200541036a2d000041187420052f000141087472200772220641027621072006418080044921060c020b200741044f0d0320044105490d0320022004417b6a36024c2002200541056a360248200528000122074180808080044921060c010b2006450d0220022004417e6a36024c2002200541026a36024820052d000141087420077241ffff03712206410276210720064180024921060b20060d01200741e4004b0d010b200241f0006a200241c8006a200710cb8580800020022802702207418080808078460d00200229027421080c010b02400240417f4100280284a4c680002207410147200741014b1b2207417f460d00200741ff01710d010b200241e4006a4194afc3800041022002412010dc8a808000200241d0006a410c6a41c082808000360200200241c1828080003602542002200241bf016a3602582002200241e4006a3602504100280290a1c680002107410028028ca1c6800021064100280280a4c680002109200241a8016a4202370200200241a0016a410236020020024198016a411036020020024194016a4184bac3800036020020024188016a4194bac38000ad4280808080900d84370200200241f0006a410c6a41fdbac38000ad4280808080800484370200200241a4016a200241d0006a360200200241f4b9c3800036029c01200241013602900120024100360284012002410036027820024281808080c003370270200641ecf2c08000200941024622091b200241f0006a200741d4f2c0800020091b280210118480808000002002280264450d00200228026841002802c0a3c68000118080808000000b41808080807821070b200241c4006a20052004200228023828020c118580808000002007418080808078460d002002200736023820002903002103200128020021042002200837023c20032004ad5a0d010c040b41002107200241003602402002428080808080013703382000290300220320012802002204ad540d030c010b0240024002402008422088a7220541e3004b0d00200541e300470d020c010b2008a72207200741106a200541047441706a10fe8d8080001a200228023821070b41e3002105200241e3003602400b20052007470d010b200241386a200710a286808000200228024021050b200228023c20054104746a22072004360208200720033703002002200228024041016a3602400c010b0240417f4100280284a4c680002207410247200741024b1b2207417f460d00200741ff01710d010b2002410c6a41858080800036020020022001360208200241cf80808000360204200220003602004100280290a1c680002107410028028ca1c6800021044100280280a4c680002105200241a8016a4202370200200241a0016a410336020020024198016a410d36020020024194016a41b5b3c3800036020020024188016a41dcb2c38000ad4280808080900b84370200200241f0006a410c6a41c2b3c38000ad4280808080b00184370200200241a4016a2002360200200241c4b2c3800036029c0120024100360284012002410036027820024281808080c0d1003702702002410236029001200441ecf2c08000200541024622051b200241f0006a200741d4f2c0800020051b280210118480808000000b200241d0006a41086a200241386a41086a28020036020020022002290338220337035002400240024002402003a72207418080808078470d00200242919fd78da1a1ad84ff003703782002429ceccef7a6c0dedd2037037020024282ea96a7a8d58af34c37038801200242b7f092fcc2eaf1f65f37038001200241f0006a412041002802a0a1c68000118480808000000c010b200242919fd78da1a1ad84ff003703782002429ceccef7a6c0dedd2037037020024282ea96a7a8d58af34c37038801200242b7f092fcc2eaf1f65f3703800120022802582204417f4c0d0141002d00fca3c680001a2004410474410472220441002802c8a3c68000118180808000002205450d02200220053602042002200436020020024100360208200241d0006a200210828780800020022802002104200241f0006a412020022802042205200228020841002802e0a1c680001186808080000002402004450d00200541002802c0a3c68000118080808000000b2007450d00200228025441002802c0a3c68000118080808000000b200241c0016a2480808080000f0b10ae80808000000b4101200410b280808000000bc00301077f23808080800041206b220224808080800041002d00fca3c680001a0240410141002802c8a3c68000118180808000002203450d0020032001417f6a3a0000200241013602102002200336020c20024101360208200241086a4101410310ab86808000200228020c2203200228021022046a220541003b0000200541026a41003a000020022802082105024002400240200441036a22044104470d00200341036a2d0000210420032d0002210620032d0001210720032d000021082005450d01200341002802c0a3c68000118080808000000c010b2005418080808078470d01200341187621042003411076210620034180fe03714108762107200321080b200041033a00002000410d6a20073a00002000410c6a20083a0000200041106a41033a00002000410e6a20063a00002000410f6a20043a0000200041086a200141187441808080786a41187541027422034188cfc380006a2802003602002000200341b8cfc380006a280200360204200241206a2480808080000f0b2002200436021c200220033602182002200536021441e0b3c3800041cb00200241146a4188b5c3800041fcbbc38000108981808000000b4101410110b280808000000bc30b06057f017e037f017e017f017e23808080800041c0016b22022480808080000240024002402001280200220128020422034108490d002001200341786a220436020420012001280200220541086a220636020020044120490d01200529000021072001200341586a22083602042001200541286a2204360200200241086a200641086a290000370300200241106a200641106a290000370300200241186a200641186a29000037030020022006290000370300024020084120490d002001200341b87f6a22063602042001200541c8006a360200200241a0016a41086a200441086a290000370300200241a0016a41106a200441106a290000370300200241a0016a41186a200441186a290000370300200220042900003703a00120064108490d002001200341b07f6a22043602042001200541d0006a220636020020024180016a41086a2208200241a0016a41086a29030037030020024180016a41106a2209200241a0016a41106a29030037030020024180016a41186a220a200241a0016a41186a290300370300200220022903a00137038001200441c000490d002005290048210b2001200341f07e6a220c360204200120054190016a2204360200200241206a41086a200641086a290000370300200241206a41106a200641106a290000370300200241206a41186a200641186a290000370300200241206a41206a200641206a290000370300200241206a41286a200641286a290000370300200241206a41306a200641306a290000370300200241206a41386a200641386a29000037030020022006290000370320200241e0006a41186a200a290300370300200241e0006a41106a2009290300370300200241e0006a41086a20082903003703002002200229038001370360200c4120490d002001200341d07e6a22063602042001200541b0016a360200200241a0016a41086a2208200441086a290000370300200241a0016a41106a2209200441106a290000370300200241a0016a41186a220a200441186a290000370300200220042900003703a00120064108490d002001200341c87e6a22043602042001200541b8016a220636020020024180016a41086a200829030037030020024180016a41106a200929030037030020024180016a41186a200a290300370300200220022903a00137038001200441c000490d0020052900b001210d2001200341887e6a3602042001200541f8016a360200200041c0016a2006290000370000200041c8016a200641086a290000370000200041d0016a200641106a290000370000200041d8016a200641186a290000370000200041e0016a200641206a290000370000200041e8016a200641286a290000370000200041f0016a200641306a290000370000200041f8016a200641386a290000370000200041b0016a20024180016a41186a290300370300200041a8016a20024180016a41106a290300370300200041a0016a20024180016a41086a29030037030020004198016a200229038001370300200041206a200241186a290300370000200041186a200241106a290300370000200041106a200241086a29030037000020002002290300370008200041306a2002290360370300200041386a200241e0006a41086a290300370300200041c0006a200241e0006a41106a290300370300200041c8006a200241e0006a41186a290300370300200041d8006a2002290320370300200041e0006a200241206a41086a290300370300200041e8006a200241206a41106a290300370300200041f0006a200241206a41186a290300370300200041f8006a200241206a41206a29030037030020004180016a200241206a41286a29030037030020004188016a200241206a41306a29030037030020004190016a200241206a41386a290300370300200041b8016a200d370300200041d0006a200b370300200041286a2007370300200042003703000c030b200042013703000c020b200042013703000c010b200042013703000b200241c0016a2480808080000bdd0a04067f017e027f017e23808080800041d0006b2201248080808000200141286a41eabdc38000410c41cfbdc38000411041a0b5c38000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041e00041002802c8a3c68000118180808000002202450d00200242c4a8f7cba2aea4ad35370308200241f9bdc38000360240200241f8bdc38000360220200241ff8180800036021820024102360204200241f6bdc38000360200200241d0006a42919fe7f19ec9d1a177370300200241c8006a42d0ffe6c89b859fc430370300200241306a42ecb2f697e9f8a9f860370300200241286a42ef899cf7a6a3ca9dd100370300200241106a42949384fce98ae9ac977f370300200241d8006a41b482808000360200200241c4006a4101360200200241386a41b382808000360200200241246a410136020020014103360248200120023602402001200241e0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a200141106a410c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c20024203370224200241bcf2c480003602202002410c36021c200241bba4c58000360218200241ef8080800036021020024293888c8f89fdc6ec9e7f370308200242a5e9e3ab9e929adc2c370300200141c0006a41086a2209200828020041016a220236020020012001290210220a37034002402002200aa7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c20024202370224200241d0fbc480003602202002410836021c200241b3a4c58000360218200241ff81808000360210200242949384fce98ae9ac977f370308200242c4a8f7cba2aea4ad353703002008200928020041016a220236020020012001290340220a37031002402002200aa7470d00200141106a200210a486808000200128021821020b2001280214200241386c6a2202420437022c20024206370224200241a7a4c580003602202002410536021c200241a2a4c58000360218200241c58280800036021020024285f5e9f2d6c0a4cd48370308200242ece297b9e9e4c9bdda00370300200141c0006a41086a200141106a41086a28020041016a220236020020012001290310220a37034002402002200aa7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c20024206370224200241a7a4c580003602202002410636021c200241ada4c58000360218200241c58280800036021020024285f5e9f2d6c0a4cd48370308200242ece297b9e9e4c9bdda00370300200128024821082001280244210220012001280240360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841e00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000bde0a04067f017e027f017e23808080800041d0006b2201248080808000200141286a41eabdc38000410c41cfbdc38000411041a0b5c38000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041e00041002802c8a3c68000118180808000002202450d00200242c4a8f7cba2aea4ad35370308200241f9bdc38000360240200241f8bdc38000360220200241ff8180800036021820024102360204200241f6bdc38000360200200241d0006a42919fe7f19ec9d1a177370300200241c8006a42d0ffe6c89b859fc430370300200241306a42c6bb82f1efc886d58a7f370300200241286a42c6bdb6a4b5ec93c2d300370300200241106a42949384fce98ae9ac977f370300200241d8006a41b482808000360200200241c4006a4101360200200241386a41b782808000360200200241246a410136020020014103360248200120023602402001200241e0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a200141106a410c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c20024203370224200241bcf2c480003602202002410c36021c200241bba4c58000360218200241ef8080800036021020024293888c8f89fdc6ec9e7f370308200242a5e9e3ab9e929adc2c370300200141c0006a41086a2209200828020041016a220236020020012001290210220a37034002402002200aa7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c20024202370224200241d0fbc480003602202002410836021c200241b3a4c58000360218200241ff81808000360210200242949384fce98ae9ac977f370308200242c4a8f7cba2aea4ad353703002008200928020041016a220236020020012001290340220a37031002402002200aa7470d00200141106a200210a486808000200128021821020b2001280214200241386c6a2202420437022c20024206370224200241a7a4c580003602202002410536021c200241a2a4c58000360218200241c682808000360210200242c5bcc8a2879389a83a3703082002428f8de586a6e995cafa00370300200141c0006a41086a200141106a41086a28020041016a220236020020012001290310220a37034002402002200aa7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c20024206370224200241a7a4c580003602202002410636021c200241ada4c58000360218200241c682808000360210200242c5bcc8a2879389a83a3703082002428f8de586a6e995cafa00370300200128024821082001280244210220012001280240360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841e00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000be30201047f23808080800041206b220224808080800041002d00fca3c680001a0240410441002802c8a3c68000118180808000002203450d002002410c6a41086a22044100360200200220033602102002410436020c200241003602182002200241186a36021c2002411c6a2002410c6a10c08a808000200241086a220520042802003602002002200229020c3703000240200128020822032001280200470d0020012003109d86808000200128020821030b2001280204200341e8006c6a220342a19eece481d0928e393703082003410b36024420034185bec38000360240200341c782808000360218200341003602002003200229030037034820032000290200370254200341013a0060200341106a42cdba88b6a38cd989b57f370300200341d0006a2005280200360200200341dc006a200041086a2802003602002001200128020841016a360208200241206a2480808080000f0b4101410410b280808000000be90503067f017e027f23808080800041d0006b2201248080808000200141286a41a1c1c38000410e41afc1c38000412541a0b5c38000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d00200242e5f0b3f4e8a9b1b12a370308200241f9bdc38000360220200241f88180800036021820024101360204200241c5b5c38000360200200241106a4281ebc5ecd497b09a0a370300200241386a4100360200200241246a410136020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a22032001411c6a220241086a28020036020020012002290200370300200128021021042001280214210520012802182106200129022c21072001280228210820014100360218200142808080808001370210200141106a410010a4868080002001280214200128021841386c6a2202420437022c20024206370224200241cae4c4800036022020024100360218200241c882808000360210200242c6be9491c9dc9cca59370308200242c7e49fb5d2d4f6ebb47f370300200128021821092001280214210220012001280210360248200120023602402001200236024420012002200941386c6a41386a36024c200141346a200141c0006a10f9868080002008418080808078460d012001200436021820012005360210200120053602142001200520064105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002008360238200041003a000020002001290300370350200041d8006a20032802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000bd60301087f23808080800041206b22022480808080002002410036021c200242808080801037021441002d00fca3c680001a0240412041002802c8a3c68000118180808000002203450d0020034200370000200341186a22044200370000200341106a22054200370000200341086a22064200370000200241146a4100412010b1828080002002280218200241146a41086a220728020022086a22092003290000370000200941086a2006290000370000200941106a2005290000370000200941186a20042900003700002007200841206a360200200341002802c0a3c6800011808080800000200241086a41086a22092007280200360200200220022902143703080240200128020822032001280200470d0020012003109d86808000200128020821030b2001280204200341e8006c6a220342c194a6a793ccc3a8573703082003410a36024420034186bfc38000360240200341f581808000360218200341003602002003200229030837034820032000290200370254200341013a0060200341106a42ab8bffbed784ffa5937f370300200341d0006a2009280200360200200341dc006a200041086a2802003602002001200128020841016a360208200241206a2480808080000f0b4101412010b280808000000be30201047f23808080800041206b220224808080800041002d00fca3c680001a0240410441002802c8a3c68000118180808000002203450d002002410c6a41086a22044100360200200220033602102002410436020c200241003602182002200241186a36021c2002411c6a2002410c6a10c08a808000200241086a220520042802003602002002200229020c3703000240200128020822032001280200470d0020012003109d86808000200128020821030b2001280204200341e8006c6a2203428aaac8d3f1d38bddba7f37030820034106360244200341bbbfc38000360240200341c982808000360218200341003602002003200229030037034820032000290200370254200341013a0060200341106a42c6a4f3f9b3bbafd83e370300200341d0006a2005280200360200200341dc006a200041086a2802003602002001200128020841016a360208200241206a2480808080000f0b4101410410b280808000000be40201047f23808080800041206b220224808080800041002d00fca3c680001a0240410441002802c8a3c68000118180808000002203450d002002410c6a41086a22044100360200200220033602102002410436020c200241003602182002200241186a36021c2002411c6a2002410c6a10fb84808000200241086a220520042802003602002002200229020c3703000240200128020822032001280200470d0020012003109d86808000200128020821030b2001280204200341e8006c6a220342fddcc0b9c5fc86d6a17f37030820034106360244200341edbfc38000360240200341ca82808000360218200341003602002003200229030037034820032000290200370254200341013a0060200341106a4296b787c192cbb889d000370300200341d0006a2005280200360200200341dc006a200041086a2802003602002001200128020841016a360208200241206a2480808080000f0b4101410410b280808000000be30201047f23808080800041206b220224808080800041002d00fca3c680001a0240410441002802c8a3c68000118180808000002203450d002002410c6a41086a22044100360200200220033602102002410436020c200241003602182002200241186a36021c2002411c6a2002410c6a10c08a808000200241086a220520042802003602002002200229020c3703000240200128020822032001280200470d0020012003109d86808000200128020821030b2001280204200341e8006c6a220342a19eece481d0928e393703082003410f36024420034199c0c38000360240200341c782808000360218200341003602002003200229030037034820032000290200370254200341013a0060200341106a42cdba88b6a38cd989b57f370300200341d0006a2005280200360200200341dc006a200041086a2802003602002001200128020841016a360208200241206a2480808080000f0b4101410410b280808000000be80201047f23808080800041306b22022480808080002002410036022020024280808080800137021841002d00fca3c680001a0240410441002802c8a3c68000118180808000002203450d00200241246a41086a220441003602002002200336022820024104360224200241186a200241246a108287808000200241086a41086a22052004280200360200200220022902243703080240200128020822032001280200470d0020012003109d86808000200128020821030b2001280204200341e8006c6a22034293bfa0a5e3cecac4ec003703082003410d360244200341dfc0c38000360240200341cb82808000360218200341003602002003200229030837034820032000290200370254200341013a0060200341106a429698dbdf9eeb9da04b370300200341d0006a2005280200360200200341dc006a200041086a2802003602002001200128020841016a360208200241306a2480808080000f0b4101410410b280808000000ba50301077f23808080800041106b220224808080800041002d00fca3c680001a02400240411841002802c8a3c68000118180808000002203450d00200320012802002204290000370000200341106a200441106a290000370000200341086a200441086a29000037000020022003360208200241183602042002411836020c2001280204210141002d00fca3c680001a412041002802c8a3c68000118180808000002203450d0120032001290000370000200341186a2205200141186a290000370000200341106a2206200141106a290000370000200341086a2207200141086a290000370000200241046a4118412010b18280800020022802082204200228020c22086a22012003290000370000200141086a2007290000370000200141106a2006290000370000200141186a200529000037000020022802042101200341002802c0a3c680001180808080000020002004200841206a4100280298a3c680001185808080000002402001450d00200441002802c0a3c68000118080808000000b200241106a2480808080000f0b4101411810b280808000000b4101412010b280808000000bd90201037f23808080800041206b2203248080808000024002402002417f4c0d0041002d00fca3c680001a2002410574410472220441002802c8a3c68000118180808000002205450d0120034100360214200320053602102003200436020c200320023602182003200341186a36021c2003411c6a2003410c6a10c08a80800002402002450d00200241057421052003280214210203400240200328020c20026b411f4b0d002003410c6a2002412010b182808000200328021421020b200328021020026a22042001290000370000200441086a200141086a290000370000200441106a200141106a290000370000200441186a200141186a2900003700002003200241206a2202360214200141206a2101200541606a22050d000b0b2000200329020c370200200041086a2003410c6a41086a280200360200200341206a2480808080000f0b10ae80808000000b4101200410b280808000000be90503067f017e027f23808080800041d0006b2201248080808000200141286a41a1c1c38000410e41afc1c38000412541a0b5c38000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d00200242d7c9c3bdedbdd399957f370308200241f9bdc38000360220200241f28180800036021820024101360204200241c5b5c38000360200200241106a42a6f8d4ee83cba9e440370300200241386a4100360200200241246a410136020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a22032001411c6a220241086a28020036020020012002290200370300200128021021042001280214210520012802182106200129022c21072001280228210820014100360218200142808080808001370210200141106a410010a4868080002001280214200128021841386c6a2202420437022c20024206370224200241cae4c4800036022020024100360218200241cc82808000360210200242dfbcf99189a39d8f56370308200242b2f3abcf8fb9a3b15e370300200128021821092001280214210220012001280210360248200120023602402001200236024420012002200941386c6a41386a36024c200141346a200141c0006a10f9868080002008418080808078460d012001200436021820012005360210200120053602142001200520064105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002008360238200041003a000020002001290300370350200041d8006a20032802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000b040041000bed0201027f23808080800041206b2202248080808000200028020028020021002002200128021441a0b5c380004100200141186a28020028020c118280808000003a000c2002200136020841012103200241013a000d20024100360204200220003602102002200041086a2201360214200220013602182002200136021c200241046a200241106a41b4c4c38000108d81808000200241146a41b4c4c38000108d81808000200241186a41c4c4c38000108d818080002002411c6a41d4c4c38000108d81808000210120022d000c210002400240200128020022010d00200041ff017141004721030c010b200041ff01710d0020022802082100024020014101470d0020022d000d41ff0171450d0020002d001c4104710d00410121032000280214418ca5c080004101200041186a28020028020c118280808000000d010b2000280214418da5c080004101200041186a28020028020c1182808080000021030b200241206a24808080800020030be30101047f23808080800041106b22022480808080002000280200220041046a280200210320002802002100410121042001280214418fa5c080004101200141186a28020028020c118280808000002105200241003a0009200220053a00082002200136020402402003450d0003402002200036020c200241046a2002410c6a41d0cfc48000108e818080001a200041016a21002003417f6a22030d000b20022d000821050b0240200541ff01710d00200228020422002802144190a5c080004101200041186a28020028020c1182808080000021040b200241106a24808080800020040bf60201037f2380808080004180016b22022480808080002000280200210002400240024002400240200128021c22034110710d0020034120710d0120003100004101200110fe8080800021000c020b20002d00002103410021000340200220006a41ff006a413041d7002003410f712204410a491b20046a3a00002000417f6a2100200341ff017122044104762103200441104f0d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000c010b20002d00002103410021000340200220006a41ff006a413041372003410f712204410a491b20046a3a00002000417f6a2100200341ff017122044104762103200441104f0d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000b20024180016a24808080800020000f0b200341800141c88dc08000109481808000000b200341800141c88dc08000109481808000000b12002000280200280200200110ab888080000bf40201037f2380808080004180016b22022480808080002000280200210002400240024002400240200128021c22034110710d0020034120710d0120003502004101200110fe8080800021000c020b20002802002100410021030340200220036a41ff006a413041d7002000410f712204410a491b20046a3a00002003417f6a210320004110492104200041047621002004450d000b20034180016a22004180014b0d022001410141c48dc080004102200220036a4180016a410020036b10db8080800021000c010b20002802002100410021030340200220036a41ff006a413041372000410f712204410a491b20046a3a00002003417f6a210320004110492104200041047621002004450d000b20034180016a22004180014b0d022001410141c48dc080004102200220036a4180016a410020036b10db8080800021000b20024180016a24808080800020000f0b200041800141c88dc08000109481808000000b200041800141c88dc08000109481808000000b9a0201027f23808080800041106b2202248080808000200220002802002200360204200128021441acc6c380004106200141186a28020028020c118280808000002103200241003a000d200220033a000c20022001360208200241086a41b2c6c380004104200041046a41b8c6c38000108c8180800041c8c6c380004105200241046a41d0c6c38000108c81808000210320022d000c21000240024020022d000d0d00200041ff017141004721010c010b41012101200041ff01710d000240200328020022012d001c4104710d0020012802144187a5c080004102200128021828020c1182808080000021010c010b20012802144186a5c080004101200128021828020c1182808080000021010b200241106a24808080800020010b140020002802002000280204200110e2808080000b3c00200128021420002802002d000041027422004190d0c380006a280200200041e8cfc380006a280200200141186a28020028020c118280808000000b9a0201027f23808080800041106b220224808080800020022000280200220041046a360204200128021441e0c6c380004109200141186a28020028020c118280808000002103200241003a000d200220033a000c20022001360208200241086a41e9c6c38000410b200041b8c6c38000108c8180800041f4c6c380004109200241046a4180c7c38000108c81808000210320022d000c21000240024020022d000d0d00200041ff017141004721010c010b41012101200041ff01710d000240200328020022012d001c4104710d0020012802144187a5c080004102200128021828020c1182808080000021010c010b20012802144186a5c080004101200128021828020c1182808080000021010b200241106a24808080800020010b1900200028020022002802002000280204200110e2808080000b0f002000280200200110ac888080000bf80202027f017e2380808080004180016b22022480808080002000280200210002400240024002400240200128021c22034110710d0020034120710d0120002903004101200110fe8080800021000c020b20002903002104410021000340200220006a41ff006a413041d7002004a7410f712203410a491b20036a3a00002000417f6a210020044210542103200442048821042003450d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000c010b20002903002104410021000340200220006a41ff006a413041372004a7410f712203410a491b20036a3a00002000417f6a210020044210542103200442048821042003450d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000b20024180016a24808080800020000f0b200341800141c88dc08000109481808000000b200341800141c88dc08000109481808000000b2100200128021441c4cdc38000410f200141186a28020028020c118280808000000b1f002000280200220041046a280200200041086a280200200110e2808080000b9a0201027f23808080800041106b220224808080800020022000280200220041086a360204200128021441e0c8c380004106200141186a28020028020c118280808000002103200241003a000d200220033a000c20022001360208200241086a41e6c8c380004108200041f0c8c38000108c818080004180c9c38000410a200241046a41e4c3c38000108c81808000210320022d000c21000240024020022d000d0d00200041ff017141004721010c010b41012101200041ff01710d000240200328020022012d001c4104710d0020012802144187a5c080004102200128021828020c1182808080000021010c010b20012802144186a5c080004101200128021828020c1182808080000021010b200241106a24808080800020010bab0201027f23808080800041106b220224808080800020002802002100200128021441bcaec380004111200141186a28020028020c118280808000002103200241003a000d200220033a000c2002200136020841012101200241086a4196c3c380004108200041226a41a0c3c38000108c8180800041b0c3c380004104200041b4c3c38000108c8180800041c4c3c38000410d200041016a41d4c3c38000108c81808000210320022d000c21000240024020022d000d0d00200041ff017141004721010c010b200041ff01710d000240200328020022012d001c4104710d0020012802144187a5c080004102200128021828020c1182808080000021010c010b20012802144186a5c080004101200128021828020c1182808080000021010b200241106a24808080800020010bf80201037f2380808080004180016b22022480808080002000280200210002400240024002400240200128021c22034110710d0020034120710d0120003301004101200110fe8080800021000c020b20002f01002103410021000340200220006a41ff006a413041d7002003410f712204410a491b20046a3a00002000417f6a2100200341ffff037122044104762103200441104f0d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000c010b20002f01002103410021000340200220006a41ff006a413041372003410f712204410a491b20046a3a00002000417f6a2100200341ffff037122044104762103200441104f0d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000b20024180016a24808080800020000f0b200341800141c88dc08000109481808000000b200341800141c88dc08000109481808000000b370020012802144196c9c38000418ac9c3800020002802002d000022001b4107410c20001b200141186a28020028020c118280808000000b7901017f23808080800041106b220224808080800020022000280200220036020c200141d9c5c38000410b41e4c5c3800041052000410c6a41ecc5c3800041fcc5c380004105200041086a4184c6c380004194c6c3800041072002410c6a419cc6c3800010e0808080002100200241106a24808080800020000b9d0101017f23808080800041306b220224808080800020002802002100200241086a410c6a4202370200200241206a410c6a41cf808080003602002002410336020c20024190c8c280003602082002200041086a360228200241cf8080800036022420022000360220200141186a28020021002002200241206a36021020012802142000200241086a10d9808080002101200241306a24808080800020010bef0201037f2380808080004180016b220224808080800002400240024002400240200128021c22034110710d0020034120710d0120003100004101200110fe8080800021000c020b20002d00002103410021000340200220006a41ff006a413041d7002003410f712204410a491b20046a3a00002000417f6a2100200341ff017122044104762103200441104f0d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000c010b20002d00002103410021000340200220006a41ff006a413041372003410f712204410a491b20046a3a00002000417f6a2100200341ff017122044104762103200441104f0d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000b20024180016a24808080800020000f0b200341800141c88dc08000109481808000000b200341800141c88dc08000109481808000000bf10202027f017e2380808080004180016b220224808080800002400240024002400240200128021c22034110710d0020034120710d0120002903004101200110fe8080800021000c020b20002903002104410021000340200220006a41ff006a413041d7002004a7410f712203410a491b20036a3a00002000417f6a210020044210542103200442048821042003450d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000c010b20002903002104410021000340200220006a41ff006a413041372004a7410f712203410a491b20036a3a00002000417f6a210020044210542103200442048821042003450d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000b20024180016a24808080800020000f0b200341800141c88dc08000109481808000000b200341800141c88dc08000109481808000000b02000b02000b02000b220002402000280200450d00200028020441002802c0a3c68000118080808000000b0b220002402000280200450d00200028020441002802c0a3c68000118080808000000b0b02000b4601017f23808080800041106b22052480808080002005200236020c200520013602082000200541086a41e4c3c380002005410c6a41e4c3c380002003200410fb80808000000b4601017f23808080800041106b22052480808080002005200236020c200520013602082000200541086a41f4c3c380002005410c6a41f4c3c380002003200410fb80808000000b4601017f23808080800041106b22052480808080002005200236020c200520013602082000200541086a4184c4c380002005410c6a4184c4c380002003200410fb80808000000b4601017f23808080800041106b22052480808080002005200236020c200520013602082000200541086a4194c4c380002005410c6a4194c4c380002003200410fb80808000000b4601017f23808080800041106b22052480808080002005200236020c200520013602082000200541086a41a4c4c380002005410c6a41a4c4c380002003200410fb80808000000b9b0101057f200020002802002201109b868080000240200028020822022001200028020c22036b4d0d002000280200210402400240200120026b2205200320056b22034d0d00200420016b20034f0d010b20002802042201200420056b22034102746a200120024102746a200541027410fe8d8080001a200020033602080f0b2000280204220020014102746a2000200341027410848e8080001a0b0b800302037f027e23808080800041306b2205248080808000200541106a41086a2001280200220141086a220628020036020020064100360200200520012902003703102001428080808010370200200541086a200541106a200220032004109287808000200528020c21040240024002400240200528020822030d00200528021021022005411c6a200528021422072005280218220610fc808080000240200528021c0d002006ad2108200721060c030b200529022021082002418080808078470d01200721020c020b2005280210450d02200528021441002802c0a3c68000118080808000000c020b200520083702282005200236021c20052006ad4220862007ad84370220419dc9c38000412b2005411c6a41c8c9c3800041b4cac38000108981808000000b2006ad4220862002ad8421092008a7210202402001280200450d00200128020441002802c0a3c68000118080808000000b20012002360208200120093702000b2000200336020020002004360204200541306a2480808080000b950201027f23808080800041106b220224808080800020022000410c6a36020420012802144190c7c38000410d200141186a28020028020c118280808000002103200241003a000d200220033a000c20022001360208200241086a419dc7c38000410520004188b5c38000108c8180800041fcc5c380004105200241046a41a4c7c38000108c81808000210320022d000c21000240024020022d000d0d00200041ff017141004721010c010b41012101200041ff01710d000240200328020022012d001c4104710d0020012802144187a5c080004102200128021828020c1182808080000021010c010b20012802144186a5c080004101200128021828020c1182808080000021010b200241106a24808080800020010bdd0401027f23808080800041106b22022480808080000240024002400240024002400240024002400240024020002f01000e09000102030405060708000b200128021441b4c7c380004109200141186a28020028020c1182808080000021010c090b200128021441bdc7c380004109200141186a28020028020c1182808080000021010c080b2002200041026a3602002002200128021441c6c7c380004118200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a200241e0c7c38000108d81808000210120022d000c21000240200128020022030d00200041ff017141004721010c080b41012101200041ff01710d072002280208210020034101470d0620022d000d41ff0171450d0620002d001c4104710d06410121012000280214418ca5c080004101200041186a28020028020c11828080800000450d060c070b200128021441f0c7c38000410f200141186a28020028020c1182808080000021010c060b200128021441ffc7c38000410d200141186a28020028020c1182808080000021010c050b2001280214418cc8c38000410d200141186a28020028020c1182808080000021010c040b20012802144199c8c38000410b200141186a28020028020c1182808080000021010c030b200128021441a4c8c380004110200141186a28020028020c1182808080000021010c020b200128021441b4c8c380004112200141186a28020028020c1182808080000021010c010b2000280214418da5c080004101200041186a28020028020c1182808080000021010b200241106a24808080800020010b950201027f23808080800041106b22022480808080002002200041086a360204200128021441e0c8c380004106200141186a28020028020c118280808000002103200241003a000d200220033a000c20022001360208200241086a41e6c8c380004108200041f0c8c38000108c818080004180c9c38000410a200241046a41e4c3c38000108c81808000210320022d000c21000240024020022d000d0d00200041ff017141004721010c010b41012101200041ff01710d000240200328020022012d001c4104710d0020012802144187a5c080004102200128021828020c1182808080000021010c010b20012802144186a5c080004101200128021828020c1182808080000021010b200241106a24808080800020010b3400200128021441d1cac3800041c9cac3800020002d000022001b4107410820001b200141186a28020028020c118280808000000bc40501057f23808080800041c0006b220224808080800041002d00fca3c680001a41002802c8a3c68000210302400240024002400240024041002f0194a1c68000220441ffff00712205413f4b0d004101210641012003118180808000002203450d02200320043a00000c010b4102210641022003118180808000002203450d02200320044106742005410876723a00012003200441fc017141027641c000723a00000b200220063602182002200336021420022006360210200241106a2006412010ab8680800020022802142206200228021822046a22032000290000370000200341086a200041086a290000370000200341106a200041106a290000370000200341186a200041186a2900003700002002200441206a22003602182002411c6a2006200010e8838080002002280224220341014d0d022002280220210302402002280210220420006b41014b0d00200241106a2000410210ab868080002002280214210620022802102104200228021821000b200620006a20032f00003b00002002410036023020024280808080103702282002200241286a360234200220063602382002200041026a220536023c200241086a200241346a200041036a41017620056a200241386a41d4cbc3800010a9898080002002280208210002402004450d00200641002802c0a3c68000118080808000000b20000d0320022802302104200228022c2100200228022821050240200228021c450d00200341002802c0a3c68000118080808000000b4100210602402001200141046a2000200410ae86808000450d0010fd8280800021060b02402005450d00200041002802c0a3c68000118080808000000b200241c0006a24808080800020060f0b4101410110b280808000000b4101410210b280808000000b4102200341c4cbc38000109581808000000b41b4a1c38000412b200241386a41e0a1c3800041f0a1c38000108981808000000b9a0f020a7f027e23808080800041e0006b2202248080808000200241286a200110f48a808000200228022c2103024002400240024002400240024020022802282204418080808078460d0020022802302101200241d4cbc38000360254200220013602502002200336024c410021052002410036023020024280808080103702282002200241286a360248200241d8006a200241c8006a2001200241cc006a41d4cbc38000108587808000024020022802582201418380c400460d002001411076210102402002280228450d00200228022c41002802c0a3c68000118080808000000b410021050c050b200228022c2106024020022802282207418080808078470d00200641107621010c050b41012105200228023022084102490d0320062d0000220141c000490d0102402001411874411875417f4a0d00410421050c040b4102210920062d0001220a413f714108742001410274200a4106767241ff01717221010c020b200041013a0000200020033602040c050b410121090b2008200941226a470d000240200141feff0071412e470d00410721050c010b200241cc006a20062009412072220510e883808000024002400240024002402002280254220a41014d0d002002280250210a0240024002400240200820056b4102470d00200620056a2f0000200a2f0000470d00200620096a22092f0003210520092f0001210820092d0000210b2002413f6a2009411c6a280000360000200241386a200941156a290000370300200241306a2009410d6a290000370300200220092900053703280240200228024c450d00200a41002802c0a3c68000118080808000000b02402007450d00200641002802c0a3c68000118080808000000b2001413a490d08200141c00f4a0d02200141b7034a0d01200141416a0ece010808080808080807080808070707080807070807070707070707070707070707070707070707070707070807070707080707080707070807070707070707070807070707080707070707070707070707070707070707070707070707070707070707070707070707070707070708070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070807070807070707070707070707070708030b0240200228024c450d00200a41002802c0a3c68000118080808000000b410321050c080b0240200141d5084a0d00024020014194064a0d00200141b803460d082001419a05470d070c080b2001419506460d07200141e307470d060c070b0240200141d0756a0e0a07060606060606060607000b200141d608460d06200141ec0b470d050c060b0240200141de364a0d00024020014196114a0d000240200141bf706a0e30080707070707070707070707070707070707070707070807080707070707070707070707080707070707070707070708000b200141d46f6a220641144b0d064101200674418180c10071450d060c070b0240200141a51f4a0d002001419711460d07200141ce11460d07200141851a470d060c070b0240200141f1284a0d00200141a61f460d07200141e222470d060c070b200141f228460d06200141ce2f470d050c060b0240200141cecd004a0d000240200141a1c5004a0d000240200141a1496a0e0708070707070708000b2001418a39460d07200141df39470d060c070b200141deba7f6a220641144d0d030c040b0240200141fade004a0d000240200141ddd9004a0d00200141cfcd00460d07200141b9ce00470d060c070b200141ded900460d06200141acdc00470d050c060b0240200141bbe6004a0d00200141fbde00460d062001419fdf00470d050c060b200141bce600460d05200141e9f200460d050c040b200141a403470d030c040b4102200a4190cdc38000109581808000000b4101200674418180c800710d020b200141f0c600460d01200141cfcc00460d010b02400240200141c4094a0d00200141a87f6a0e320202020102020101010102020202010101010101010101010101010101010101010101010101010102020101010101010202010b200141bb766a4102490d01200141fc756a4102490d01200141e26e6a4102490d010b200141bca77f6a417d4b0d00200141002f0194a1c68000460d00410221050c020b200241086a41176a2201200241286a41176a280000360000200241086a41106a2206200241286a41106a290300370300200241086a41086a200241286a41086a290300220c37030020022002290328220d370308200041046a20053b0100200041026a20083b01002000200b3a0001200041066a200d3701002000410e6a200c370100200041166a20062903003701002000411d6a2001280000360000410021010c020b024020070d000c010b200641002802c0a3c68000118080808000000b200220013b014a200220053b0148200241346a4201370200410121012002410136022c200241d8cac38000360228200241cd8280800036025c2002200241d8006a3602302002200241c8006a360258200241cc006a200241286a10b8808080002000200241cc006a10ea8a8080003602040b200020013a00002004450d00200341002802c0a3c68000118080808000000b200241e0006a2480808080000bc30201027f23808080800041106b22022480808080000240024020002d00000d00200128021441a0cdc38000410c200141186a28020028020c1182808080000021030c010b410121032002200041016a3602002002200128021441accdc380004106200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a200241b4cdc38000108d81808000210120022d000c21000240200128020022010d00200041ff017141004721030c010b200041ff01710d0020022802082100024020014101470d0020022d000d41ff0171450d0020002d001c4104710d00410121032000280214418ca5c080004101200041186a28020028020c118280808000000d010b2000280214418da5c080004101200041186a28020028020c1182808080000021030b200241106a24808080800020030bba0203027f017e017f23808080800041d0006b2201248080808000200141286a41b9d0c38000411241fe8bc48000411641e8d1c38000410010d882808000200141086a2202410036020020014280808080c000370300200129022c210320012802282104200142808080808001370218200142888080808001370210200141346a200141106a10f98680800002402004418080808078470d0041a8d8c480004111419cd9c4800010a181808000000b200042808080808001370244200041cc006a4100360200200141cc006a200141346a41086a2802003600002000413c6a200337020020002004360238200041003a000020002001290300370350200041d8006a20022802003602002001200129023437004420002001290041370001200041086a200141c8006a290000370000200141d0006a2480808080000b2000200042bae6c989f69dacb01a3703082000428e8397fa82e98fd06e3703000bca0204067f017e027f017e200141186a2802002102200141106a2802002103200128020821042001280204210520012802002106024003402001418180808078360200024002400240024020064180808080786a0e020200010b20032002460d0120012003410c6a220736021020032802002206418080808078460d0120032902042208422088a721042008a72105200721030b2005210720062109024020032002460d0020012003410c6a220a36021020032802002206418080808078470d020b2001200b37020420014180808080783602000c030b20004180808080783602000f0b20012003290204220b370204200120063602002004200b422088a7470d012007200ba72205200410888e8080000d01200a21032009450d00200741002802c0a3c6800011808080800000200a21030c000b0b2000200436020820002007360204200020093602000b1400200041ecd4c38000360204200020013602000b1400200041ecd4c38000360204200020013602000bef0201027f23808080800041c0006b2203248080808000200320023a000c200320013602082003410036021820034280808080103702102003411c6a200341086a10ec8a8080000240024020032d001c0d002003411c6a41027221020240034020032d001d410171450d010240200328021822042003280210470d00200341106a200410a186808000200328021821040b200328021420044105746a22012002290000370000200141086a200241086a290000370000200141106a200241106a290000370000200141186a200241186a2900003700002003200441016a3602182003411c6a200341086a10ec8a80800020032d001c450d000b2000200328022036020420004180808080783602002003280210450d02200328021441002802c0a3c68000118080808000000c020b20002003290210370200200041086a200341106a41086a2802003602000c010b2000200328022036020420004180808080783602000b200341c0006a2480808080000bdd0702047f017e23808080800041306b2203248080808000024002400240024002402000280200220441736a2205410220054104491b0e0400010203000b41002105024002400240024002400240024002400240024020002d0008417f6a0e0b0001020304050607090908090b200041146a28020041046a21050c080b410821050c070b200041146a28020041046a21050c060b200041146a28020041046a21050c050b200041146a28020041186c41047221050c040b200041146a280200410c6c41046a21050c030b417f200041186a280200220541086a22042004200541046a491b21050c020b200041146a28020041046a21050c010b200041146a28020041046a21050b200541016a21050c030b200041086a10bc8780800021050c020b410021050240024002400240024002400240024002402004417f6a0e0c000102030405080608070707000b41d00021050c070b2000410c6a28020041046a21050c060b417f200041186a28020041046a220541012000410c6a28020041056a2000280204418080808078461b6a220420042005491b21050c050b417f200041186a2802002000410c6a28020041046a22056a41046a220420042005491b21050c040b2000410c6a28020041046a21050c030b2000410c6a28020041046a21050c020b410821050c010b410421050b200541016a21050c010b200041086a10a78c80800021050b41012104200541016a21064102210502402000290358220742c000540d0041032105200742808001540d00410521052007428080808004540d00410a200779a74103766b21050b024002400240200620056a4121410120002d0088011b6a2205450d002005417f4c0d0141002d00fca3c680001a200541002802c8a3c68000118180808000002204450d020b20004188016a210620034100360214200320043602102003200536020c20002003410c6a10b8898080002003200041d8006a36022c2003412c6a2003410c6a10c18a808000200041e0006a2d000021050240200328020c20032802142200470d002003410c6a2000410110b182808000200328021421000b200328021020006a20053a00002003200041016a36021420062003410c6a10b18c80800020032802102100200328020c210502400240200328021422044180024b0d00200120002004200241002802c8a2c680001189808080000021040c010b2003410c6a200020044100280298a3c680001185808080000020012003410c6a4120200241002802c8a2c680001189808080000021040b02402005450d00200041002802c0a3c68000118080808000000b200341306a24808080800020040f0b10ae80808000000b4101200510b280808000000bcd0201017f0240024002400240200028020041736a2202410220024104491b0e0400010203000b0240200128020020012802082202470d0020012002410110b182808000200128020821020b2001200241016a360208200128020420026a41003a0000200041086a200110a78a8080000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b2001200241016a360208200128020420026a41013a0000200041086a200110bb878080000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b2001200241016a360208200128020420026a41023a00002000200110ba888080000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b2001200241016a360208200128020420026a41033a0000200041086a200110a68c8080000bca0101057f23808080800041206b220124808080800041002102200141003602102001428080808010370208200141146a10b98980800041012103200128021821040240200128021c2205450d00200141086a4100200510b182808000200128020c2103200128021021020b200320026a2004200510848e8080001a2001200220056a36021002402001280214450d00200441002802c0a3c68000118080808000000b20002001290208370200200041086a200141086a41086a280200360200200141206a2480808080000b840201037f23808080800041106b22022480808080000240024020012802082203410c6c20034103746a41066a2203417f4c0d0041002d00fca3c680001a200341002802c8a3c68000118180808000002204450d012002200436020820022003360204200420012d000c3a00002002410136020c20012d000d210441012103024020022802044101470d00200241046a4101410110b182808000200228020c21030b200228020820036a20043a0000200241046a41086a2204200341016a3602002001200241046a10d48c808000200041086a200428020036020020002002290204370200200241106a2480808080000f0b10ae80808000000b4101200310b280808000000b2100200128021441f4dec180004108200141186a28020028020c118280808000000b210020012802144194d5c380004111200141186a28020028020c118280808000000b2100200128021441a5d5c38000410a200141186a28020028020c118280808000000b2100200128021441b2e2c180004103200141186a28020028020c118280808000000b2100200128021441d0a7c48000411b200141186a28020028020c118280808000000b2100200128021441f0dec180004104200141186a28020028020c118280808000000b2100200128021441d895c48000410f200141186a28020028020c118280808000000b12002001418a84c48000410210dd808080000b040041010b040041010b02000bb90101037f23808080800041306b220124808080800002400240200028020c22020d0041002102410021030c010b200120023602242001410036022020012002360214200141003602102001200041106a280200220236022820012002360218200041146a2802002103410121020b2001200336022c2001200236021c2001200236020c2001410c6a10928d80800002402000280200450d00200028020441002802c0a3c68000118080808000000b200141306a2480808080000b02000b2d002000410c6a10a58d80800002402000280200450d00200028020441002802c0a3c68000118080808000000b0bef0901057f0240024002400240024002402000280200220141736a2202410220024104491b0e03010203000b20002d00084106470d042000410c6a280200450d04200041106a28020021030c030b0240024002400240024002400240024020002d0008417f6a0e0a010b0203040506070b0b000b2000410c6a280200450d0a200041106a28020021030c090b2000410c6a280200450d09200041106a28020021030c080b2000410c6a280200450d08200041106a28020021030c070b2000410c6a280200450d07200041106a28020021030c060b200041106a28020021030240200041146a2802002201450d0020032102034002402002280200450d00200241046a28020041002802c0a3c68000118080808000000b02402002410c6a280200450d00200241106a28020041002802c0a3c68000118080808000000b200241186a21022001417f6a22010d000b0b200028020c450d060c050b200041106a28020021030240200041146a2802002202450d002002410171210441002101024020024101460d002002417e7121052003210241002101034002402002280200450d00200241046a28020041002802c0a3c68000118080808000000b02402002410c6a280200450d00200241106a28020041002802c0a3c68000118080808000000b200241186a21022005200141026a2201470d000b0b2004450d0020032001410c6c6a2202280200450d00200228020441002802c0a3c68000118080808000000b200028020c0d040c050b200041106a280200450d04200041146a28020021030c030b2000410c6a280200450d03200041106a28020021030c020b200041186a2d0000417d6a41ff017141014b0d020240200028020822034198016a2802002205450d0020034194016a280200220421004100210203402004200241146c6a21010240024002400240024020002d00000e0400010102040b200041086a21010c020b200141086a21010c010b200141046a21010b2001280200450d00200128020441002802c0a3c68000118080808000000b200241016a2102200041146a21002005417f6a22050d000b0b0240200328029001450d0020032802940141002802c0a3c68000118080808000000b024020034190026a2802002205450d002003418c026a280200220421004100210203402004200241146c6a21010240024002400240024020002d00000e0400010102040b200041086a21010c020b200141086a21010c010b200141046a21010b2001280200450d00200128020441002802c0a3c68000118080808000000b200241016a2102200041146a21002005417f6a22050d000b0b200328028802450d01200328028c0241002802c0a3c68000118080808000000c010b024002400240024002400240024002402001417e6a0e06000102030405090b200041046a21000c050b02402000280210450d00200041146a28020041002802c0a3c68000118080808000000b20002802042202418080808078460d07200041046a21000c050b02402000280204450d00200041086a28020041002802c0a3c68000118080808000000b200041106a21000c030b200041046a21000c020b200041046a21000c010b024002400240024020002d00040e0400010203070b2000410c6a21000c030b2000410c6a21000c020b2000410c6a21000c010b200041086a21000b200028020021020b2002450d01200028020421030b200341002802c0a3c68000118080808000000f0b0bc50101027f024002400240024020002802002201418080808078732202410220024104491b0e03030301000b0240024002402000280204220028020041fcffffff076a2202410320024105491b0e0404040102000b2000280204450d03200041086a28020041002802c0a3c68000118080808000000c030b2000280204450d02200041086a28020041002802c0a3c68000118080808000000c020b200010ca898080000c010b2001450d01200028020421000b200041002802c0a3c68000118080808000000b0b29000240200041808080807872418080808078460d00200141002802c0a3c68000118080808000000b0b910401027f23808080800041106b22022480808080000240024002402000280210418080808078460d00200220003602002002200128021441d089c480004102200128021828020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a200241d489c48000108d81808000210120022d000c21000240200128020022030d00200041ff017141004721010c030b41012101200041ff01710d022002280208210020034101470d0120022d000d41ff0171450d0120002d001c4104710d01410121012000280214418ca5c080004101200041186a28020028020c11828080800000450d010c020b200220003602002002200128021441e489c480004103200128021828020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a200241e889c48000108d81808000210120022d000c21000240200128020022030d00200041ff017141004721010c020b41012101200041ff01710d0120022802082100024020034101470d0020022d000d41ff0171450d0020002d001c4104710d00410121012000280214418ca5c080004101200041186a28020028020c118280808000000d020b2000280214418da5c080004101200041186a28020028020c1182808080000021010c010b2000280214418da5c080004101200041186a28020028020c1182808080000021010b200241106a24808080800020010b1900200120002802002200280200200028020410dd808080000bf60b01097f23808080800041e0016b2208248080808000200841356a2002290000370000200841dc006a41ecd4c38000360200200841086a41286a4100360200200841086a41206a4204370200200841086a41186a4200370200200841f4006a2007360200200841ec006a4100360200200841cd006a200241186a290000370000200841c5006a200241106a2900003700002008413d6a200241086a290000370000200820023602602008200136025820084280808080c0003702182008200636027020084100360264200841013a0034200820053602142008410036020c2008200436021020082004410047360208200841b0016a41286a200341286a280200360200200841b0016a41206a200341206a290200370300200841b0016a41186a200341186a290200370300200841b0016a41106a200341106a290200370300200841b0016a41086a2204200341086a290200370300200820032902003703b001200841f8006a200841b0016a109d87808000200828027c210920082802800121032008200841df016a3602b00120092003200841b0016a10d68b808000200841086a41106a210a2008280278210b024002402003450d00200920034104746a210c200841a8016a210d200841b0016a410172210620084184016a41086a210720084184016a41046a210e20084184016a410172210520092103034020032802002201450d012003280204210f02400240024020032802082210450d00200841b0016a200841086a2001200f2010200328020c10918680800020082d00b00122014104460d02200520062f00003b000020072004290200370200200541026a200641026a2d00003a0000200741086a200441086a290200370200200741106a200441106a290200370200200741186a200441186a290200370200200741206a200441206a2802003602000c010b200841b0016a200841086a2001200f10938680800020082d00b00122014104460d01200520062f00003b000020072004290200370200200541026a200641026a2d00003a0000200741086a200441086a290200370200200741106a200441106a290200370200200741186a200441186a290200370200200741206a200441206a2802003602000b200820082802b401220f36028801200820013a008401024020014103460d0002400240024020010e020103000b20082802a80122012001280200220f417f6a360200200d2101200f4101460d010c020b200f200f2802002210417f6a360200200e210120104101470d010b200110e28a8080000b200341106a2203200c470d010c020b0b20082802b4012103200041013a0000200020033602040240200b450d00200941002802c0a3c68000118080808000000b200841086a109086808000200a10908780800002402008280218450d00200828021c41002802c0a3c68000118080808000000b02402008280224450d00200828022841002802c0a3c68000118080808000000b02400240200828026422030d0041002103410021040c010b200820033602c801200841003602c401200820033602b801200841003602b4012008200841e8006a28020022033602cc01200820033602bc0141012103200828026c21040b200820043602d001200820033602c001200820033602b001200841b0016a10a48d8080000c010b0240200b450d00200941002802c0a3c68000118080808000000b200841086a109086808000200a10908780800002402008280218450d00200828021c41002802c0a3c68000118080808000000b02402008280224450d00200828022841002802c0a3c68000118080808000000b4100210341002104024020082802642207450d00200820073602c801200841003602c401200820073602b801200841003602b4012008200841e8006a28020022033602cc01200820033602bc0141012103200828026c21040b200820043602d001200820033602c001200820033602b001200841b0016a10a48d808000200041003a0000200041196a200241186a290000370000200041116a200241106a290000370000200041096a200241086a290000370000200020022900003700010b200841e0016a2480808080000bf60b01097f23808080800041e0016b2208248080808000200841356a2002290000370000200841dc006a41ecd4c38000360200200841086a41286a4100360200200841086a41206a4204370200200841086a41186a4200370200200841f4006a2007360200200841ec006a4100360200200841cd006a200241186a290000370000200841c5006a200241106a2900003700002008413d6a200241086a290000370000200820023602602008200136025820084280808080c0003702182008200636027020084100360264200841013a0034200820053602142008410036020c2008200436021020082004410047360208200841b0016a41286a200341286a280200360200200841b0016a41206a200341206a290200370300200841b0016a41186a200341186a290200370300200841b0016a41106a200341106a290200370300200841b0016a41086a2204200341086a290200370300200820032902003703b001200841f8006a200841b0016a109d87808000200828027c210920082802800121032008200841df016a3602b00120092003200841b0016a10d68b808000200841086a41106a210a2008280278210b024002402003450d00200920034104746a210c200841a8016a210d200841b0016a410172210620084184016a41086a210720084184016a41046a210e20084184016a410172210520092103034020032802002201450d012003280204210f02400240024020032802082210450d00200841b0016a200841086a2001200f2010200328020c10928680800020082d00b00122014104460d02200520062f00003b000020072004290200370200200541026a200641026a2d00003a0000200741086a200441086a290200370200200741106a200441106a290200370200200741186a200441186a290200370200200741206a200441206a2802003602000c010b200841b0016a200841086a2001200f10948680800020082d00b00122014104460d01200520062f00003b000020072004290200370200200541026a200641026a2d00003a0000200741086a200441086a290200370200200741106a200441106a290200370200200741186a200441186a290200370200200741206a200441206a2802003602000b200820082802b401220f36028801200820013a008401024020014103460d0002400240024020010e020103000b20082802a80122012001280200220f417f6a360200200d2101200f4101460d010c020b200f200f2802002210417f6a360200200e210120104101470d010b200110e28a8080000b200341106a2203200c470d010c020b0b20082802b4012103200041013a0000200020033602040240200b450d00200941002802c0a3c68000118080808000000b200841086a108f86808000200a10908780800002402008280218450d00200828021c41002802c0a3c68000118080808000000b02402008280224450d00200828022841002802c0a3c68000118080808000000b02400240200828026422030d0041002103410021040c010b200820033602c801200841003602c401200820033602b801200841003602b4012008200841e8006a28020022033602cc01200820033602bc0141012103200828026c21040b200820043602d001200820033602c001200820033602b001200841b0016a10a48d8080000c010b0240200b450d00200941002802c0a3c68000118080808000000b200841086a108f86808000200a10908780800002402008280218450d00200828021c41002802c0a3c68000118080808000000b02402008280224450d00200828022841002802c0a3c68000118080808000000b4100210341002104024020082802642207450d00200820073602c801200841003602c401200820073602b801200841003602b4012008200841e8006a28020022033602cc01200820033602bc0141012103200828026c21040b200820043602d001200820033602c001200820033602b001200841b0016a10a48d808000200041003a0000200041196a200241186a290000370000200041116a200241106a290000370000200041096a200241086a290000370000200020022900003700010b200841e0016a2480808080000bcc0c01067f2380808080004190026b220a248080808000200a41186a41186a200441186a220b290000370300200a41186a41106a200441106a220c290000370300200a41186a41086a200441086a220d290000370300200a2004290000370318200a410c6a41086a2002360200200a2001360210200a200336020c200a418c016a41f889c48000360200200a41e0006a4100360200200a41386a41206a4204370200200a41386a41186a4200370200200a41a4016a2009360200200a419c016a4100360200200a41fd006a200b290000370000200a41f5006a200c290000370000200a41ed006a200d290000370000200a41e5006a2004290000370000200a4280808080c000370248200a20083602a001200a410036029401200a41013a0064200a200a41186a36029001200a200a410c6a36028801200a2007360244200a410036023c200a2006360240200a2006410047360238200a41e0016a41206a200541206a280200360200200a41e0016a41186a200541186a290200370300200a41e0016a41106a200541106a290200370300200a41e0016a41086a2206200541086a290200370300200a20052902003703e001200a41a8016a200a41e0016a109f87808000200a2802ac01210b200a2802b0012104200a200a418f026a3602e001200b2004200a41e0016a10d68b808000200a41386a41106a210c200a2802a801210d024002402004450d00200b20044104746a2107200a41d8016a210e200a41e0016a4101722101200a41b4016a41086a2105200a41b4016a41046a210f200a41b4016a4101722103200b2104034020042802002202450d0120042802042109024002400240200428020822080d00200a41e0016a200a41386a20022009109486808000200a2d00e00122024104460d01200320012f00003b000020052006290200370200200341026a200141026a2d00003a0000200541086a200641086a290200370200200541106a200641106a290200370200200541186a200641186a290200370200200541206a200641206a2802003602000c020b200a41e0016a200a41386a200220092008200428020c109286808000200a2d00e00122024104460d00200320012f00003b000020052006290200370200200341026a200141026a2d00003a0000200541086a200641086a290200370200200541106a200641106a290200370200200541186a200641186a290200370200200541206a200641206a2802003602000c010b200a2802e4012104200041013a0000200020043602040240200d450d00200b41002802c0a3c68000118080808000000b200a41386a108f86808000200c1090878080000240200a280248450d00200a28024c41002802c0a3c68000118080808000000b0240200a280254450d00200a28025841002802c0a3c68000118080808000000b02400240200a2802940122040d0041002104410021060c010b200a20043602f801200a41003602f401200a20043602e801200a41003602e401200a200a4198016a28020022043602fc01200a20043602ec0141012104200a28029c0121060b200a200636028002200a20043602f001200a20043602e001200a41e0016a10a48d8080000c030b200a200a2802e40122093602b801200a20023a00b401024020024103460d0002400240024020020e020103000b200a2802d801220220022802002209417f6a360200200e210220094101460d010c020b200920092802002208417f6a360200200f210220084101470d010b200210e28a8080000b200441106a22042007470d000b0b0240200d450d00200b41002802c0a3c68000118080808000000b200a41386a108f86808000200c1090878080000240200a280248450d00200a28024c41002802c0a3c68000118080808000000b0240200a280254450d00200a28025841002802c0a3c68000118080808000000b41002104410021060240200a280294012205450d00200a20053602f801200a41003602f401200a20053602e801200a41003602e401200a200a4198016a28020022043602fc01200a20043602ec0141012104200a28029c0121060b200a200636028002200a20043602f001200a20043602e001200a41e0016a10a48d808000200041196a200a41306a290300370000200041116a200a41286a290300370000200041096a200a41206a2903003700002000200a290318370001200041003a00000b200a4190026a2480808080000bcc0c01067f2380808080004190026b220a248080808000200a41186a41186a200441186a220b290000370300200a41186a41106a200441106a220c290000370300200a41186a41086a200441086a220d290000370300200a2004290000370318200a410c6a41086a2002360200200a2001360210200a200336020c200a418c016a41f889c48000360200200a41e0006a4100360200200a41386a41206a4204370200200a41386a41186a4200370200200a41a4016a2009360200200a419c016a4100360200200a41fd006a200b290000370000200a41f5006a200c290000370000200a41ed006a200d290000370000200a41e5006a2004290000370000200a4280808080c000370248200a20083602a001200a410036029401200a41013a0064200a200a41186a36029001200a200a410c6a36028801200a2007360244200a410036023c200a2006360240200a2006410047360238200a41e0016a41206a200541206a280200360200200a41e0016a41186a200541186a290200370300200a41e0016a41106a200541106a290200370300200a41e0016a41086a2206200541086a290200370300200a20052902003703e001200a41a8016a200a41e0016a109f87808000200a2802ac01210b200a2802b0012104200a200a418f026a3602e001200b2004200a41e0016a10d68b808000200a41386a41106a210c200a2802a801210d024002402004450d00200b20044104746a2107200a41d8016a210e200a41e0016a4101722101200a41b4016a41086a2105200a41b4016a41046a210f200a41b4016a4101722103200b2104034020042802002202450d0120042802042109024002400240200428020822080d00200a41e0016a200a41386a20022009109386808000200a2d00e00122024104460d01200320012f00003b000020052006290200370200200341026a200141026a2d00003a0000200541086a200641086a290200370200200541106a200641106a290200370200200541186a200641186a290200370200200541206a200641206a2802003602000c020b200a41e0016a200a41386a200220092008200428020c109186808000200a2d00e00122024104460d00200320012f00003b000020052006290200370200200341026a200141026a2d00003a0000200541086a200641086a290200370200200541106a200641106a290200370200200541186a200641186a290200370200200541206a200641206a2802003602000c010b200a2802e4012104200041013a0000200020043602040240200d450d00200b41002802c0a3c68000118080808000000b200a41386a109086808000200c1090878080000240200a280248450d00200a28024c41002802c0a3c68000118080808000000b0240200a280254450d00200a28025841002802c0a3c68000118080808000000b02400240200a2802940122040d0041002104410021060c010b200a20043602f801200a41003602f401200a20043602e801200a41003602e401200a200a4198016a28020022043602fc01200a20043602ec0141012104200a28029c0121060b200a200636028002200a20043602f001200a20043602e001200a41e0016a10a48d8080000c030b200a200a2802e40122093602b801200a20023a00b401024020024103460d0002400240024020020e020103000b200a2802d801220220022802002209417f6a360200200e210220094101460d010c020b200920092802002208417f6a360200200f210220084101470d010b200210e28a8080000b200441106a22042007470d000b0b0240200d450d00200b41002802c0a3c68000118080808000000b200a41386a109086808000200c1090878080000240200a280248450d00200a28024c41002802c0a3c68000118080808000000b0240200a280254450d00200a28025841002802c0a3c68000118080808000000b41002104410021060240200a280294012205450d00200a20053602f801200a41003602f401200a20053602e801200a41003602e401200a200a4198016a28020022043602fc01200a20043602ec0141012104200a28029c0121060b200a200636028002200a20043602f001200a20043602e001200a41e0016a10a48d808000200041196a200a41306a290300370000200041116a200a41286a290300370000200041096a200a41206a2903003700002000200a290318370001200041003a00000b200a4190026a2480808080000b9b0301077f23808080800041306b220424808080800020032802002105200128020421060240024002400240024020032802042207200141086a28020022086a22090d004101210a0c010b2009417f4c0d01200941002802c8a3c6800011818080800000220a450d02200a41002009108a8e8080001a0b20092008490d02200a2006200810848e808000220a20086a2005200710848e8080001a20012802002101200420032f01083b0114200420093602102004200a36020c200441186a200128020420022004410c6a10e48c8080000240024002402004280218418080808078470d00200441246a2001280200200210e58c8080002004280224418180808078460d0120002004290224370200200041086a200441246a41086a2802003602000c020b20002004290218370200200041086a200441186a41086a2802003602000c010b20004180808080783602000b02402009450d00200a41002802c0a3c68000118080808000000b200441306a2480808080000f0b10ae80808000000b4101200910b280808000000b2008200941c8c5c28000109581808000000b9a0201077f23808080800041106b220524808080800020022802002106200128020421070240024002400240024020022802042208200141086a28020022096a220a0d004101210b0c010b200a417f4c0d01200a41002802c8a3c6800011818080800000220b450d02200b4100200a108a8e8080001a0b200a2009490d02200b2007200910848e808000220b20096a2006200810848e8080001a20012802002101200520022f01083b010c2005200a3602082005200b36020420002001280204200541046a2003200410e78c8080000240200a450d00200b41002802c0a3c68000118080808000000b200541106a2480808080000f0b10ae80808000000b4101200a10b280808000000b2009200a41c8c5c28000109581808000000b960201077f23808080800041106b220324808080800020022802002104200028020421050240024002400240024020022802042206200041086a28020022076a22080d00410121090c010b2008417f4c0d01200841002802c8a3c68000118180808000002209450d02200941002008108a8e8080001a0b20082007490d0220092005200710848e808000220920076a2004200610848e8080001a20002802002100200320022f01083b010c200320083602082003200936020420002802042001200341046a10e98c80800002402008450d00200941002802c0a3c68000118080808000000b200341106a2480808080000f0b10ae80808000000b4101200810b280808000000b2007200841c8c5c28000109581808000000b980201077f23808080800041106b220424808080800020022802002105200028020421060240024002400240024020022802042207200041086a28020022086a22090d004101210a0c010b2009417f4c0d01200941002802c8a3c6800011818080800000220a450d02200a41002009108a8e8080001a0b20092008490d02200a2006200810848e808000220a20086a2005200710848e8080001a20002802002100200420022f01083b010c200420093602082004200a36020420002802042001200441046a200310eb8c80800002402009450d00200a41002802c0a3c68000118080808000000b200441106a2480808080000f0b10ae80808000000b4101200910b280808000000b2008200941c8c5c28000109581808000000bb20301077f23808080800041306b220324808080800020022802002104200028020421050240024002400240024020022802042206200041086a28020022076a22080d00410121090c010b2008417f4c0d01200841002802c8a3c68000118180808000002209450d02200941002008108a8e8080001a0b20082007490d0220092005200710848e808000220920076a2004200610848e8080001a20002802002107200320022f01083b0114200320083602102003200936020c200341186a200728020420012003410c6a10e48c808000024002400240024020032802182200418080808078470d00200341246a2007280200200110e58c80800020032802242200418180808078470d0141808080807821020c030b200328021c21020c010b41808080807821022000418080808078460d01200328022821020b024020000d00410021020c010b200241002802c0a3c6800011808080800000200021020b02402008450d00200941002802c0a3c68000118080808000000b200341306a2480808080002002418080808078470f0b10ae80808000000b4101200810b280808000000b2007200841c8c5c28000109581808000000b1400200041b48ac48000360204200020013602000b1400200041b48ac48000360204200020013602000b1400200041f889c48000360204200020013602000b1400200041f889c48000360204200020013602000b1a0020022001200041dc8ac480002003280228118680808000000b070020002802000bb106010a7f23808080800041d0006b220424808080800002404100280284a4c680004105470d004100280290a1c680002105410028028ca1c6800021064100280280a4c680002107200441c0006a42003702002004413c6a41e8d1c38000360200200441386a4101360200200441306a41163602002004412c6a41e88bc48000360200200441206a41f48ac48000ad4280808080900c84370200200441146a41fe8bc48000ad4280808080e00284370200200441e08bc48000360234200441053602282004410036021c20044100360210200442818080809024370208200641ecf2c08000200741024622071b200441086a200541d4f2c0800020071b280210118480808000000b4102210702400240200128020041736a220541034b0d0020054102460d000c010b200441086a200110d18c808000024020042802182208418080808078460d00200428022c21092004280228210a2004280224210b200428021c210c024020042802202201450d002001410171210d41002105024020014101460d002001417e712106200c210141002105034002402001280200450d00200141046a28020041002802c0a3c68000118080808000000b02402001410c6a280200450d00200141106a28020041002802c0a3c68000118080808000000b200141186a21012006200541026a2205470d000b0b200d450d00200c2005410c6c6a2201280200450d00200128020441002802c0a3c68000118080808000000b02402008450d00200c41002802c0a3c68000118080808000000b02402009450d002009410171210841002105024020094101460d002009417e712106200a210141002105034002402001280200450d00200141046a28020041002802c0a3c68000118080808000000b02402001410c6a280200450d00200141106a28020041002802c0a3c68000118080808000000b200141186a21012006200541026a2205470d000b0b2008450d00200a2005410c6c6a2201280200450d00200128020441002802c0a3c68000118080808000000b0240200b0d000c020b200a41002802c0a3c68000118080808000000c010b20042f0009210120042d000821070b200441d0006a2480808080002001410874200741ff0171720b0700200042017c0b8c0302057f017e024020012802002202200128020822036b411f4b0d0020012003412010b18280800020012802002102200128020821030b2001200341206a22043602082001280204220520036a22032000290000370000200341086a200041086a290000370000200341106a200041106a290000370000200341186a200041186a290000370000200041206a21030240200220046b411f4b0d0020012004412010b1828080002001280200210220012802042105200128020821040b200520046a220620032900003700002001200441206a2204360208200641186a200341186a290000370000200641106a200341106a290000370000200641086a200341086a290000370000200029034021070240200220046b41074b0d0020012004410810b18280800020012802042105200128020821040b200520046a20073700002001200441086a2203360208200029034821070240200128020020036b41074b0d0020012003410810b182808000200128020821030b2001200341086a360208200128020420036a20073700000bcc0704027f017e037f017e23808080800041d0006b2201248080808000200141286a41e795c48000410c41fe8bc48000411641e8d1c38000410010d882808000200141086a410036020020014280808080c00037030020012802282102200129022c2103200141106a41086a22044100360200200142808080808001370210200141106a410010a4868080002001280214200428020041386c6a2205420437022c20054209370224200541edfbc480003602202005410436021c200541daa3c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200141c0006a41086a2206200428020041016a2205360200200120012902102207370340024020052007a7470d00200141c0006a200510a486808000200128024821050b2001280244200541386c6a2205420437022c20054209370224200541edfbc480003602202005410236021c200541d8a3c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db003703002004200628020041016a2205360200200120012903402207370310024020052007a7470d00200141106a200510a486808000200128021821050b2001280214200541386c6a2205420437022c20054207370224200541c7fbc480003602202005410636021c200541c1fbc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c370300200141c0006a41086a200141106a41086a28020041016a2205360200200120012903102207370340024020052007a7470d00200141c0006a200510a486808000200128024821050b2001280244200541386c6a2205420437022c2005420537022420054185e4c480003602202005410536021c20054180e4c48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c370300200128024821042001280244210520012001280240360218200120053602102001200536021420012005200441386c6a41386a36021c200141346a200141106a10f98680800002402002418080808078470d0041a8d8c480004111419cd9c4800010a181808000000b200042808080808001370244200041cc006a4100360200200141cb006a200141346a41086a2802003600002000413c6a200337020020002002360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437004320002001290040370001200041086a200141c7006a290000370000200141d0006a2480808080000ba60501057f23808080800041e0056b220124808080800020014180036a41086a22024200370300200141086a41106a20014190036a41086a290300370300200141e0026a41086a22034200370300200141d0026a41086a22044200370300200141b8026a41086a22054200370300200141b8026a41106a41003602002001420037030820014200370380032001200129039003370310200142003703e002200142003703d002200142003703b80220014201370338200141c8006a200229030037030020012001290380033703402001420037036020014200370358200142d0a38733370350200141f0006a200141b8036a41086a290300370300200120012903b803370368200142003703b801200142003703b001200142d0a387333703a801200141c8016a200141f0026a41086a290300370300200120012903f0023703c001200142003703d001200141e0016a2003290300370300200120012903e0023703d801200142003703e801200141f8016a2004290300370300200120012903d0023703f0012001420037039802200142d8a698d801370390022001420037038802200142d0a3873337038002200141a8026a2005290300370300200120012903b8023703a0022001428180808080a0f8fa053703b0022001428080808080808080c0003703a0012001428090cad2c60e3703980120014201370390012001427f3703880120014280c0a8ca9a3a3703800120014201370378200142ffffffffffffffffbf7f37033020014280b0def7d32b37032820014201370320200141b8036a200141086a108682808000024020012903b8034202520d00200120012d00c0033a00900341aeeac0800041e00020014190036a41c0e9c080004190ebc08000108981808000000b2000200141b8036a41a80210848e8080001a200141e0056a2480808080000bf40701087f23808080800041b0016b2202248080808000024002400240024002400240024020012802002203418080808078460d00200128020421044101210541012106024020012802082207450d002007417f4c0d0441002d00fca3c680001a200741002802c8a3c68000118180808000002206450d050b20062004200710848e8080002106200220073602082002200636020420022007360200200241e4006a41e5c8c9a90320024100280290a2c68000118580808000002007450d0141002d00fca3c680001a200741002802c8a3c680001181808080000022050d014101200710b280808000000b2002418080808078360200200241e4006a41e5c8c9a90320024100280290a2c6800011858080800000200241808080807836020020024184016a41f3e4c9a903200241002802b0a2c680001185808080000020024180808080783602a4010c010b20052004200710848e808000210620022007360208200220063602042002200736020020024184016a41f3e4c9a903200241002802b0a2c68000118580808000000240024020070d00410121060c010b41002d00fca3c680001a200741002802c8a3c68000118180808000002206450d040b20062004200710848e8080002104200220073602ac01200220043602a801200220073602a4010b200241c0006a220441e5c6919b07200241a4016a41002802f0a1c6800011858080800000200241086a2205200241e4006a41086a290000370300200241106a2208200241e4006a41106a290000370300200241186a2209200241e4006a41186a290000370300200241286a20024184016a41086a290000370300200241306a20024184016a41106a290000370300200241386a20024184016a41186a29000037030020022002290064370300200220022900840137032041002d00fca3c680001a41e10041002802c8a3c68000118180808000002207450d03200720022903003700002007200241206a220629000037002020072004290000370040200741186a2009290300370000200741106a2008290300370000200741086a2005290300370000200741286a200641086a290000370000200741306a200641106a290000370000200741386a200641186a290000370000200741c8006a200441086a290000370000200741d0006a200441106a290000370000200741d8006a200441186a290000370000200741e0006a200441206a2d00003a0000200041e10036020820002007360204200041e10036020002402003418080808078460d002003450d00200128020441002802c0a3c68000118080808000000b200241b0016a2480808080000f0b10ae80808000000b4101200710b280808000000b4101200710b280808000000b410141e10010b280808000000b920501037f23808080800041106b22022480808080002002410036020c20024280808080c00037020441002d00fca3c680001a024002400240412041002802c8a3c68000118180808000002203450d0020032001290000370000200341186a200141186a290000370000200341106a200141106a290000370000200341086a200141086a290000370000200241046a410010a3868080002002280208200228020c4104746a220442a0808080d08c999935370208200420033602042004412036020041002d00fca3c680001a2002200228020c41016a36020c412041002802c8a3c68000118180808000002203450d0120032001290020370000200341186a200141386a290000370000200341106a200141306a290000370000200341086a200141286a2900003700000240200228020c22042002280204470d00200241046a200410a386808000200228020c21040b200228020820044104746a220442a0808080b0ce9c993537020820042003360204200441203602002002200228020c41016a36020c41002d00fca3c680001a412141002802c8a3c68000118180808000002203450d0220032001290040370000200341206a200141e0006a2d00003a0000200341186a200141d8006a290000370000200341106a200141d0006a290000370000200341086a200141c8006a2900003700000240200228020c22012002280204470d00200241046a200110a386808000200228020c21010b200228020820014104746a220142a1808080d0ec98b2f300370208200120033602042001412136020020002002290204370200200041086a200241046a41086a28020041016a360200200241106a2480808080000f0b4101412010b280808000000b4101412010b280808000000b4101412110b280808000000bbe9701012b7f2380808080004190016b220124808080800041002d00fca3c680001a0240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024041800441002802c8a3c68000118180808000002202450d00200141106a109e8a8080002001109f8a808000200141386a10b587808000200141286a10b687808000200141d0006a10bc88808000200141f8006a10a18c808000200141e8006a10a28c808000200241386a4289a2e6a5b198dead63370300200242d5d1b5deb9b78d97cf00370330200241206a42e89d8d84e9a7e0ebbf7f370300200242ffa1f591d896faeca07f370318200242829cf890def98fed23370308200242daf597b8b3adb2fc44370300200241b8016a42bba2f9a9eaf2f897ff00370300200241b0016a4288bfcff6aea5a9cbf90037030020024188016a42c2a7b4b19ace9c92d9003703002002429691e8a3b686eae562370380012002410636024c200241f9a6c48000360248200241ec82808000360240200241ed82808000360228200241ee8280800036021020024280808080c000370370200241cc016a4104360200200241c8016a41f5a6c48000360200200241c0016a41ef82808000360200200241a8016a410036020020024190016a41f082808000360200200241003a007c2002410036027820022001290310370350200241d8006a200141106a41086a290300370300200241e0006a200141106a41106a28020036020020022001290300370264200241ec006a200141086a280200360200200241f8016a410036020020024190026a41f182808000360200200241a8026a4100360200200241c0026a4100360200200241c8026a41e8a6c48000360200200241cc026a410d360200200241f0016a4280808080c000370300200242f681cbfbfc8791b01c3703800220024188026a42f8978ab68af4bdd3937f370300200241fc016a41013a0000200241e0016a200141386a41106a280200360200200241d8016a200141386a41086a290300370300200241d0016a2001290338370300200241e4016a2001290328370200200241ec016a200141286a41086a280200360200200241b8036a42f2a19699e4e1bdb1ed00370300200241b0036a4288d7a582bd91bff5867f370300200241a0036a42e5c18a8c83d9a2ecc20037030020024198036a42b4ff9ef1bdc3bdda2837030020024188036a42c9e8d484edccb5e26c370300200242a79fcbb9e09aeaff7c37038003200241fc026a41023a0000200241f4026a4204370200200241ec026a4200370200200241e4026a42808080808001370200200241cc036a4108360200200241c8036a41e0a6c48000360200200241c0036a41f282808000360200200241a8036a41f38280800036020020024190036a41f482808000360200200241e0026a200141d0006a41106a280200360200200241d8026a200141d0006a41086a290300370300200241d0026a2001290350370300200241e0036a200141f8006a41106a280200360200200241d8036a200141f8006a41086a290300370300200241d0036a2001290378370300200241ec036a200141e8006a41086a280200360200200241e4036a2001290368370200200241fc036a41033a0000200241f8036a4100360200200241f0036a4280808080c000370300200141f8006a10d988808000200128027c2203210402402001280280012205450d002003200541386c22056a21062003210403402004280200450d01200441386a2104200541486a22050d000b200621040b2001280278210741002d00fca3c680001a41800341002802c8a3c68000118180808000002206450d0141002d00fca3c680001a41a80141002802c8a3c68000118180808000002208450d0241002d00fca3c680001a410841002802c8a3c68000118180808000002209450d032009419c84c480003602002009412436020441002d00fca3c680001a412041002802c8a3c68000118180808000002205450d04200541d5d7c3800036020020054292a2a3bece87f785bf7f370308200541f58280800036021820054105360204200541106a42fbf4c799cbf4c181c70037030041002d00fca3c680001a410841002802c8a3c6800011818080800000220a450d05200a41c784c48000360200200a411936020441002d00fca3c680001a412041002802c8a3c6800011818080800000220b450d06200b41f9d3c38000360200200b42f68d80b7cfa6d3bb4e370308200b41f682808000360218200b4106360204200b41106a42dc8ec6fd9fd6fcdeb77f37030041002d00fca3c680001a410841002802c8a3c6800011818080800000220c450d07200c41d000360204200c41ed84c4800036020020084180016a42d39e8a9dc98694cb37370300200841f8006a42c4b088d2f7cca0855f370300200841c8006a429ccab49c93e2e495cf00370300200841c0006a42d1c5efcdcd82cde1ff00370300200841106a42fbe0bedd81cff9c317370300200842cec9d2b3fca8a6f5bc7f370308200841a4016a4101360200200841a0016a200c36020020084198016a42818080801037030020084194016a200b36020020084190016a410136020020084188016a41f782808000360200200841f4006a4110360200200841bd85c48000360270200841ec006a4101360200200841e8006a200a360200200841e0006a428180808010370300200841dc006a2005360200200841d8006a4101360200200841d0006a4193828080003602002008413c6a410d360200200841e084c4800036023820084101360234200820093602302008428080808010370328200842808080808001370320200841f88280800036021820084107360204200841c084c4800036020041002d00fca3c680001a410841002802c8a3c6800011818080800000220d450d08200d41cd85c48000360200200d41c80036020441002d00fca3c680001a41a80141002802c8a3c6800011818080800000220e450d0941002d00fca3c680001a410841002802c8a3c6800011818080800000220a450d0a200a419986c48000360200200a412336020441002d00fca3c680001a412041002802c8a3c68000118180808000002209450d0b200941c084c48000360200200942d7c9cb8fc1cf97db3e370308200941b58080800036021820094107360204200941106a42e88488d0c0e3aebc1337030041002d00fca3c680001a412041002802c8a3c68000118180808000002205450d0c200541c486c48000360200200541ae87c48000360218200541ed86c48000360210200541e8d1c38000360208200541293602042005411c6a41dd00360200200541146a41c1003602002005410c6a410036020041002d00fca3c680001a411841002802c8a3c6800011818080800000220b450d0d200b41c788c48000360210200b41e8d1c38000360208200b4129360204200b419e88c48000360200200b41146a4130360200200b410c6a4100360200200e4180016a42ded2f2adffb8f5dd2b370300200e41f8006a42a3ac8ca0a39ceca97a370300200e41c8006a42c89a9d928994f7ea8d7f370300200e41c0006a42fb82e7fdcaf6be9b18370300200e41106a42a8c786dcd8d2a98d17370300200e42cecfe0e5d1c6dbf757370308200e41a4016a4103360200200e41a0016a200b360200200e4198016a428080808030370300200e4190016a42808080808001370300200e4188016a41f982808000360200200e41f4006a4111360200200e41f788c48000360270200e41ec006a4104360200200e41e8006a2005360200200e41e0006a4281808080c000370300200e41dc006a2009360200200e41d8006a4101360200200e41d0006a41fa82808000360200200e413c6a4113360200200e418b88c48000360238200e4101360234200e200a360230200e428080808010370328200e42808080808001370320200e41fb82808000360218200e4108360204200e41bc86c4800036020041002d00fca3c680001a410841002802c8a3c6800011818080800000220f450d0e200f418889c48000360200200f41c00036020441002d00fca3c680001a413841002802c8a3c68000118180808000002210450d0f41002d00fca3c680001a41e00041002802c8a3c6800011818080800000220b450d10200b41fbf4c38000360200200b42b09d9bccd2bafbc85c370308200b4183f5c38000360240200b4181f5c38000360220200b41fc82808000360218200b4106360204200b41d0006a42ab8bffbed784ffa5937f370300200b41c8006a42c194a6a793ccc3a857370300200b41306a42dda1fc828d83b3faab7f370300200b41286a42b7a18ef9ac95b6cbeb00370300200b41106a42909fd09cacfeedb5967f370300200b41d8006a41f581808000360200200b41c4006a410a360200200b41386a41ec81808000360200200b41246a410236020041002d00fca3c680001a41c80041002802c8a3c68000118180808000002205450d1120054194f8c38000360240200541c3f7c38000360238200541e8d1c38000360230200541a4f7c38000360228200541d1f6c3800036022020054180f6c38000360218200541a7f5c38000360210200541e8d1c380003602082005411a3602042005418df5c38000360200200541c4006a41293602002005413c6a41d100360200200541346a41003602002005412c6a411f360200200541246a41d3003602002005411c6a41d100360200200541146a41d9003602002005410c6a4100360200201041bdf8c38000360200201041106a42fbbf92b8c0c3b2e0f200370300201042e7b7af9aebaca1f35b37030820104109360234201020053602302010428380808090013703282010200b36022420104103360220201041fd828080003602182010411436020441002d00fca3c680001a410841002802c8a3c68000118180808000002211450d12201141d1f8c38000360200201141d30036020441002d00fca3c680001a41e00141002802c8a3c68000118180808000002209450d1341002d00fca3c680001a412041002802c8a3c6800011818080800000220a450d14200a41afd5c38000360200200a42b7a18ef9ac95b6cbeb00370308200a41ec81808000360218200a4109360204200a41106a42dda1fc828d83b3faab7f37030041002d00fca3c680001a412041002802c8a3c6800011818080800000220b450d15200b41b8d5c38000360200200b41a1d6c38000360218200b41d3d5c38000360210200b41e8d1c38000360208200b411b360204200b411c6a4113360200200b41146a41ce00360200200b410c6a410036020041002d00fca3c680001a410841002802c8a3c68000118180808000002212450d16201241c3d6c380003602002012411a36020441002d00fca3c680001a412041002802c8a3c6800011818080800000220c450d17200c41ebd6c38000360200200c429cc4c6fe96f1ecf5e000370308200c418981808000360218200c4108360204200c41106a42aeb899d38addfa912137030041002d00fca3c680001a410841002802c8a3c68000118180808000002213450d18201341f3d6c38000360200201341cf0036020441002d00fca3c680001a41c00041002802c8a3c68000118180808000002205450d19200541d5d7c3800036020020054292a2a3bece87f785bf7f370308200541dad7c38000360220200541f58280800036021820054105360204200541306a42aeb899d38addfa9121370300200541286a429cc4c6fe96f1ecf5e000370300200541106a42fbf4c799cbf4c181c700370300200541386a418981808000360200200541246a410436020041002d00fca3c680001a410841002802c8a3c68000118180808000002214450d1a201441d500360204201441ded7c38000360200200941b8016a42a1f9febbd8b1ab95b07f370300200941b0016a42d8bcf6a5ac90bd85c70037030020094180016a429acfecb0d2a8f28be700370300200941f8006a428bdb9ef4d7aab8cbec00370300200941c8006a42dc8ec6fd9fd6fcdeb77f370300200941c0006a42f68d80b7cfa6d3bb4e370300200941106a42d881e89685dfc6955a370300200942f68096ca91b986f9c000370308200941dc016a4101360200200941d8016a2014360200200941d0016a428280808010370300200941cc016a2005360200200941c8016a4102360200200941c0016a41fe82808000360200200941ac016a410f360200200941b3d8c380003602a801200941a4016a4101360200200941a0016a201336020020094198016a42818080801037030020094194016a200c36020020094190016a410136020020094188016a41ff82808000360200200941f4006a4113360200200941c2d7c38000360270200941ec006a4101360200200941e8006a2012360200200941e0006a428080808010370300200941d8006a42808080808001370300200941d0006a4180838080003602002009413c6a410e360200200941ddd6c38000360238200941043602342009200b36023020094281808080c0003703282009200a3602242009410136022020094181838080003602182009410f360204200941b4d6c3800036020041002d00fca3c680001a410841002802c8a3c68000118180808000002215450d1b201541c2d8c38000360200201541dc0036020441002d00fca3c680001a413841002802c8a3c68000118180808000002216450d1c41002d00fca3c680001a412041002802c8a3c68000118180808000002205450d1d2005419783c48000360200200542c4ccab9c81ebb4b8db00370308200541f08180800036021820054107360204200541106a42a4d2928ecac0faa43237030041002d00fca3c680001a410841002802c8a3c6800011818080800000220b450d1e200b4130360204200b419e83c48000360200201641ce83c48000360200201641106a4293888c8f89fdc6ec9e7f370300201642a5e9e3ab9e929adc2c370308201641013602342016200b36023020164281808080103703282016200536022420164101360220201641ef808080003602182016410d36020441002d00fca3c680001a410841002802c8a3c68000118180808000002217450d1f201741db83c480003602002017412036020441002d00fca3c680001a41f00741002802c8a3c68000118180808000002205450d2041002d00fca3c680001a412041002802c8a3c68000118180808000002218450d21201841feebc38000360200201842c4ccab9c81ebb4b8db00370308201841f08180800036021820184102360204201841106a42a4d2928ecac0faa43237030041002d00fca3c680001a410841002802c8a3c68000118180808000002219450d22201941f395c480003602002019412c36020441002d00fca3c680001a412041002802c8a3c6800011818080800000221a450d23201a41a996c48000360200201a42a5e9e3ab9e929adc2c370308201a418283808000360218201a4103360204201a41106a4293888c8f89fdc6ec9e7f37030041002d00fca3c680001a410841002802c8a3c6800011818080800000221b450d24201b41ac96c48000360200201b41ce0036020441002d00fca3c680001a412041002802c8a3c6800011818080800000221c450d25201c418b97c48000360200201c42e8e094efaab6e5ba5b370308201c418383808000360218201c4103360204201c41106a4299b9c2f4a1b0a7a4f10037030041002d00fca3c680001a411041002802c8a3c6800011818080800000221d450d26201d418e97c48000360200201d41e397c48000360208201d41d500360204201d410c6a410836020041002d00fca3c680001a410841002802c8a3c6800011818080800000221e450d27201e418398c48000360200201e411336020441002d00fca3c680001a410841002802c8a3c6800011818080800000221f450d28201f41b098c48000360200201f411436020441002d00fca3c680001a410841002802c8a3c68000118180808000002220450d29202041cc98c48000360200202041c00036020441002d00fca3c680001a410841002802c8a3c68000118180808000002221450d2a202141a399c480003602002021411b36020441002d00fca3c680001a412041002802c8a3c68000118180808000002222450d2b202241d399c48000360200202242d7c9cb8fc1cf97db3e370308202241b58080800036021820224104360204202241106a42e88488d0c0e3aebc1337030041002d00fca3c680001a410841002802c8a3c68000118180808000002223450d2c202341d799c480003602002023412636020441002d00fca3c680001a410841002802c8a3c68000118180808000002224450d2d2024418e9ac480003602002024412636020441002d00fca3c680001a411841002802c8a3c68000118180808000002213450d2e201341c49ac48000360200201341f59ac48000360210201341e8d1c3800036020820134131360204201341146a41ce003602002013410c6a410036020041002d00fca3c680001a411841002802c8a3c68000118180808000002214450d2f201441d69bc48000360200201441879cc48000360210201441e8d1c3800036020820144131360204201441146a413b3602002014410c6a410036020041002d00fca3c680001a411841002802c8a3c68000118180808000002225450d30202541d59cc48000360200202541849dc48000360210202541e8d1c380003602082025412f360204202541146a41393602002025410c6a410036020041002d00fca3c680001a410841002802c8a3c68000118180808000002226450d31202641ce9dc480003602002026412336020441002d00fca3c680001a41c00041002802c8a3c68000118180808000002212450d32201241fd9dc48000360200201242a4d981999395fed957370308201241829ec48000360220201241848380800036021820124105360204201241306a42ab8bffbed784ffa5937f370300201241286a42c194a6a793ccc3a857370300201241106a4290bdcef9d19f84fee800370300201241386a41f581808000360200201241246a410436020041002d00fca3c680001a410841002802c8a3c68000118180808000002227450d33202741869ec480003602002027411136020441002d00fca3c680001a41e00041002802c8a3c6800011818080800000220b450d34200b41dad7c38000360200200b42e7b0a091f3ed9c85c500370308200b41a89ec48000360240200b41a39ec48000360220200b41ea81808000360218200b4104360204200b41d0006a42e88488d0c0e3aebc13370300200b41c8006a42d7c9cb8fc1cf97db3e370300200b41306a42b891b68c98adebcf61370300200b41286a42e7b0a091f3ed9c85c500370300200b41106a42b891b68c98adebcf61370300200b41d8006a41b580808000360200200b41c4006a4103360200200b41386a41ea81808000360200200b41246a410536020041002d00fca3c680001a411041002802c8a3c68000118180808000002228450d35202841ab9ec48000360200202841ed9ec48000360208202841c2003602042028410c6a410b36020041002d00fca3c680001a410841002802c8a3c68000118180808000002229450d362029418f9fc480003602002029411e36020441002d00fca3c680001a41e00041002802c8a3c6800011818080800000220a450d37200a41b99fc48000360200200a42e3beec81d3e996af917f370308200a41c29fc48000360240200a41bc9fc48000360220200a418583808000360218200a4103360204200a41d0006a42b891b68c98adebcf61370300200a41c8006a42e7b0a091f3ed9c85c500370300200a41306a42a4d2928ecac0faa432370300200a41286a42c4ccab9c81ebb4b8db00370300200a41106a42f0a3fcacaeba9c956f370300200a41d8006a41ea81808000360200200a41c4006a4107360200200a41386a41f081808000360200200a41246a410636020041002d00fca3c680001a410841002802c8a3c6800011818080800000222a450d38202a41c99fc48000360200202a413536020441002d00fca3c680001a41e00041002802c8a3c6800011818080800000220c450d39200c418ca0c48000360200200c42e7b0a091f3ed9c85c500370308200c4194a0c48000360240200c418fa0c48000360220200c41ea81808000360218200c4103360204200c41d0006a4298848fa1dab08ba174370300200c41c8006a42febac4ad81b6fafcb37f370300200c41306a42b891b68c98adebcf61370300200c41286a42e7b0a091f3ed9c85c500370300200c41106a42b891b68c98adebcf61370300200c41d8006a418881808000360200200c41c4006a4105360200200c41386a41ea81808000360200200c41246a410536020041002d00fca3c680001a410841002802c8a3c6800011818080800000222b450d3a202b41d800360204202b4199a0c48000360200200541c8076a429ccab49c93e2e495cf00370300200541c0076a42d1c5efcdcd82cde1ff0037030020054190076a4298848fa1dab08ba17437030020054188076a42febac4ad81b6fafcb37f370300200541d8066a429ccab49c93e2e495cf00370300200541d0066a42d1c5efcdcd82cde1ff00370300200541a0066a429ccab49c93e2e495cf0037030020054198066a42d1c5efcdcd82cde1ff00370300200541e8056a429ccab49c93e2e495cf00370300200541e0056a42d1c5efcdcd82cde1ff00370300200541b0056a429ccab49c93e2e495cf00370300200541a8056a42d1c5efcdcd82cde1ff00370300200541f8046a42dfc4809d8ac5e4f1de00370300200541f0046a42e5e882a9be85fc80fa00370300200541c0046a4294ed85e8ed84e1d30b370300200541b8046a42d8e4bea39ae2d6da0637030020054188046a4295d2cbc8a2888c9a7d37030020054180046a42f69cccefd7e0e1c38b7f370300200541d0036a4293888c8f89fdc6ec9e7f370300200541c8036a42a5e9e3ab9e929adc2c37030020054198036a42b891b68c98adebcf6137030020054190036a42e7b0a091f3ed9c85c500370300200541e0026a4293888c8f89fdc6ec9e7f370300200541d8026a42a5e9e3ab9e929adc2c370300200541a8026a4293888c8f89fdc6ec9e7f370300200541a0026a42a5e9e3ab9e929adc2c370300200541f0016a4293888c8f89fdc6ec9e7f370300200541e8016a42a5e9e3ab9e929adc2c370300200541b8016a4293888c8f89fdc6ec9e7f370300200541b0016a42a5e9e3ab9e929adc2c37030020054180016a42fd87d58eecf6d4b521370300200541f8006a42c5ccf6ddf0d2bef89a7f370300200541c8006a4293888c8f89fdc6ec9e7f370300200541c0006a42a5e9e3ab9e929adc2c370300200541106a4293888c8f89fdc6ec9e7f370300200542a5e9e3ab9e929adc2c370308200541ec076a4101360200200541e8076a202b360200200541e0076a428380808010370300200541dc076a200c360200200541d8076a4103360200200541d0076a419382808000360200200541bc076a410f360200200541f1a0c480003602b807200541b4076a4101360200200541b0076a202a360200200541a8076a428380808010370300200541a4076a200a360200200541a0076a410336020020054198076a41888180800036020020054184076a410e360200200541fe9fc4800036028007200541fc066a4101360200200541f8066a2029360200200541f0066a428080808010370300200541e8066a42808080808001370300200541e0066a419382808000360200200541cc066a410c360200200541ad9fc480003602c806200541c4066a4102360200200541c0066a2028360200200541b8066a428380808020370300200541b4066a200b360200200541b0066a4103360200200541a8066a41938280800036020020054194066a4117360200200541f89ec48000360290062005418c066a410136020020054188066a202736020020054180066a428280808010370300200541fc056a2012360200200541f8056a4102360200200541f0056a419382808000360200200541dc056a410c360200200541979ec480003602d805200541d4056a4101360200200541d0056a2026360200200541c8056a428080808010370300200541c0056a42808080808001370300200541b8056a419382808000360200200541a4056a410c360200200541f19dc480003602a0052005419c056a410336020020054198056a202536020020054190056a42808080803037030020054188056a4280808080800137030020054180056a418683808000360200200541ec046a4111360200200541bd9dc480003602e804200541e4046a4103360200200541e0046a2014360200200541d8046a428080808030370300200541d0046a42808080808001370300200541c8046a418783808000360200200541b4046a4113360200200541c29cc480003602b004200541ac046a4103360200200541a8046a2013360200200541a0046a42808080803037030020054198046a4280808080800137030020054190046a418883808000360200200541fc036a4113360200200541c39bc480003602f803200541f4036a4101360200200541f0036a2024360200200541e8036a428080808010370300200541e0036a42808080808001370300200541d8036a41ef80808000360200200541c4036a4110360200200541b49ac480003602c003200541bc036a4101360200200541b8036a2023360200200541b0036a428180808010370300200541ac036a2022360200200541a8036a4101360200200541a0036a41ea818080003602002005418c036a4111360200200541fd99c480003602880320054184036a410136020020054180036a2021360200200541f8026a428080808010370300200541f0026a42808080808001370300200541e8026a41ef80808000360200200541d4026a4115360200200541be99c480003602d002200541cc026a4101360200200541c8026a2020360200200541c0026a428080808010370300200541b8026a42808080808001370300200541b0026a41ef808080003602002005419c026a41173602002005418c99c480003602980220054194026a410136020020054190026a201f36020020054188026a42808080801037030020054180026a42808080808001370300200541f8016a41ef80808000360200200541e4016a4108360200200541c498c480003602e001200541dc016a4101360200200541d8016a201e360200200541d0016a428080808010370300200541c8016a42808080808001370300200541c0016a41ef80808000360200200541ac016a411a3602002005419698c480003602a801200541a4016a4102360200200541a0016a201d36020020054198016a42818080802037030020054194016a201c36020020054190016a410136020020054188016a418383808000360200200541f4006a4118360200200541eb97c48000360270200541ec006a4101360200200541e8006a201b360200200541e0006a428180808010370300200541dc006a201a360200200541d8006a4101360200200541d0006a41ef808080003602002005413c6a4111360200200541fa96c48000360238200541013602342005201936023020054281808080103703282005201836022420054101360220200541ef808080003602182005410a3602042005419f96c4800036020041002d00fca3c680001a41f00041002802c8a3c68000118180808000002214450d3b41002d00fca3c680001a411841002802c8a3c6800011818080800000220b450d3c200b41aad9c38000360200200b41ced9c38000360210200b41e8d1c38000360208200b4124360204200b41146a41c900360200200b410c6a410036020041002d00fca3c680001a410841002802c8a3c6800011818080800000220a450d3d200a4127360204200a41a4dac3800036020020144197dac38000360200201441c8006a42c4e2cedafbc28595bc7f370300201441c0006a42d8b6abf18ab7a1e965370300201441106a42b683bfa183f992fe847f370300201442de88dfa5f1c5f7b6a27f370308201441ec006a4101360200201441e8006a200a360200201441e0006a428080808010370300201441d8006a42808080808001370300201441d0006a4189838080003602002014413c6a410b360200201441cbdac38000360238201441033602342014200b36023020144280808080303703282014428080808080013703202014418a838080003602182014410d36020441002d00fca3c680001a410841002802c8a3c68000118180808000002228450d3e202841d6dac380003602002028412e36020441002d00fca3c680001a41d00241002802c8a3c6800011818080800000220b450d3f41002d00fca3c680001a410841002802c8a3c68000118180808000002218450d402018418bdbc380003602002018412336020441002d00fca3c680001a410841002802c8a3c6800011818080800000221a450d41201a41bbdbc38000360200201a413136020441002d00fca3c680001a410841002802c8a3c6800011818080800000221c450d42201c41ffdbc38000360200201c413136020441002d00fca3c680001a411041002802c8a3c68000118180808000002225450d43202541bddcc38000360200202541fddcc38000360208202541c0003602042025410c6a411736020041002d00fca3c680001a41c00041002802c8a3c68000118180808000002212450d442012419eddc38000360200201242e3a4fae3cee3d18d72370308201241a2ddc38000360220201241f58080800036021820124104360204201241306a42e9b494c69bdbc4d608370300201241286a42ead283debcdebd93d800370300201241106a42b8b6d386cdcbfab1a07f370300201241386a41f780808000360200201241246a410c36020041002d00fca3c680001a41d80041002802c8a3c6800011818080800000220a450d45200a41c0e2c38000360250200a41fee1c38000360248200a41c2e1c38000360240200a41ffe0c38000360238200a41bde0c38000360230200a41fbdfc38000360228200a41b7dfc38000360220200a41f4dec38000360218200a41b3dec38000360210200a41f0ddc38000360208200a41c200360204200a41aeddc38000360200200a41d4006a4134360200200a41cc006a41c200360200200a41c4006a413c360200200a413c6a41c300360200200a41346a41c200360200200a412c6a41c200360200200a41246a41c400360200200a411c6a41c300360200200a41146a41c100360200200a410c6a41c30036020041002d00fca3c680001a41c00041002802c8a3c68000118180808000002213450d4620134190e3c380003602002013428ae790e9b7c48cad987f370308201341a2e3c380003602202013418b8380800036021820134112360204201341306a42b889cc8cd692ff80fa00370300201341286a4288f7e1d594faa5a120370300201341106a42ade681e7ba94a3bd8d7f370300201341386a418c83808000360200201341246a410f36020041002d00fca3c680001a41c00041002802c8a3c6800011818080800000220c450d47200c41fbe6c38000360238200c41bae6c38000360230200c41f8e5c38000360228200c41b3e5c38000360220200c41f2e4c38000360218200c41b3e4c38000360210200c41f5e3c38000360208200c41c400360204200c41b1e3c38000360200200c413c6a41c100360200200c41346a41c100360200200c412c6a41c200360200200c41246a41c500360200200c411c6a41c100360200200c41146a413f360200200c410c6a413e360200200b41a8026a42a0a58eade3c7d8c3f600370300200b41a0026a42bbbde2dfeee883e643370300200b41f0016a42bcded3878bc0cf8f7c370300200b41e8016a42b7bec490f9e7d7cda97f370300200b41b8016a4285b396c8e397b6a6f000370300200b41b0016a42aca08db5a793aef98d7f370300200b4180016a4285b396c8e397b6a6f000370300200b41f8006a42aca08db5a793aef98d7f370300200b41c8006a42b8b6d386cdcbfab1a07f370300200b41c0006a42e3a4fae3cee3d18d72370300200b41106a42e1ca8ae9eabca78cc200370300200b4290c489c6869cfac00d370308200b41cc026a4108360200200b41c8026a200c360200200b41c0026a42828080808001370300200b41bc026a2013360200200b41b8026a4102360200200b41b0026a418d83808000360200200b419c026a412d360200200b41bce7c3800036029802200b4194026a410b360200200b4190026a200a360200200b4188026a4282808080b001370300200b4184026a2012360200200b4180026a4102360200200b41f8016a418e83808000360200200b41e4016a411c360200200b41f4e2c380003602e001200b41dc016a4102360200200b41d8016a2025360200200b41d0016a428080808020370300200b41c8016a42808080808001370300200b41c0016a418f83808000360200200b41ac016a410a360200200b4194ddc380003602a801200b41a4016a4101360200200b41a0016a201c360200200b4198016a428080808010370300200b4190016a42808080808001370300200b4188016a418f83808000360200200b41f4006a410d360200200b41b0dcc38000360270200b41ec006a4101360200200b41e8006a201a360200200b41e0006a428080808010370300200b41d8006a42808080808001370300200b41d0006a41f580808000360200200b413c6a4113360200200b41ecdbc38000360238200b4101360234200b2018360230200b428080808010370328200b42808080808001370320200b419083808000360218200b410d360204200b41aedbc3800036020041002d00fca3c680001a410841002802c8a3c68000118180808000002219450d48201941e9e7c380003602002019412e36020441002d00fca3c680001a413841002802c8a3c6800011818080800000221a450d4941002d00fca3c680001a412041002802c8a3c6800011818080800000220a450d4a200a41f9d3c38000360200200a42f68d80b7cfa6d3bb4e370308200a41f682808000360218200a4106360204200a41106a42dc8ec6fd9fd6fcdeb77f37030041002d00fca3c680001a410841002802c8a3c6800011818080800000220c450d4b200c4132360204200c41ffd3c38000360200201a41b1d4c38000360200201a41106a429ccab49c93e2e495cf00370300201a42d1c5efcdcd82cde1ff00370308201a4101360234201a200c360230201a428180808010370328201a200a360224201a4101360220201a419382808000360218201a410f36020441002d00fca3c680001a410841002802c8a3c6800011818080800000221b450d4c201b41c0d4c38000360200201b411936020441002d00fca3c680001a41f00041002802c8a3c68000118180808000002213450d4d41002d00fca3c680001a412041002802c8a3c68000118180808000002212450d4e201241cbd0c38000360200201242f99f94a5fdd3dbbaf900370308201241918380800036021820124104360204201241106a42f6d183c8fca4ffd45a37030041002d00fca3c680001a413841002802c8a3c6800011818080800000220a450d4f200a41cfd0c38000360200200a4194d2c38000360230200a41e8d1c38000360228200a41e8d1c38000360220200a41e8d1c38000360218200a41d6d1c38000360210200a4194d1c38000360208200a41c500360204200a41346a4134360200200a412c6a4100360200200a41246a412c360200200a411c6a4100360200200a41146a410f360200200a410c6a41c20036020041002d00fca3c680001a412041002802c8a3c68000118180808000002225450d50202541ddd2c38000360200202542e7b0a091f3ed9c85c500370308202541ea8180800036021820254107360204202541106a42b891b68c98adebcf6137030041002d00fca3c680001a411841002802c8a3c6800011818080800000220c450d51200c418ad3c38000360210200c41e8d1c38000360208200c4126360204200c41e4d2c38000360200200c41146a4137360200200c410c6a4100360200201341c8006a42bfd1fbe392d3878718370300201341c0006a42c6b49cae9188d5c4a37f370300201341106a42b891b68c98adebcf61370300201342e7b0a091f3ed9c85c500370308201341ec006a4103360200201341e8006a200c360200201341e0006a428180808030370300201341dc006a2025360200201341d8006a4101360200201341d0006a4192838080003602002013413c6a4113360200201341c1d3c38000360238201341073602342013200a36023020134281808080f0003703282013201236022420134101360220201341ea8180800036021820134115360204201341c8d2c3800036020041002d00fca3c680001a410841002802c8a3c6800011818080800000221e450d52201e41d4d3c38000360200201e411a36020441002d00fca3c680001a41e00141002802c8a3c6800011818080800000220a450d5341002d00fca3c680001a413041002802c8a3c68000118180808000002225450d54202541baf9c38000360200202541ecfbc380003602282025419dfbc38000360220202541d0fac38000360218202541e8d1c3800036021020254189fac38000360208202541cf003602042025412c6a4130360200202541246a41cf003602002025411c6a41cd00360200202541146a41003602002025410c6a41c70036020041002d00fca3c680001a41c00041002802c8a3c68000118180808000002218450d5520184190e3c38000360200201842c1d4a9c8c2d78cfcc000370308201841a2e3c38000360220201841938380800036021820184112360204201841306a4299f0ab899787d3ad3a370300201841286a42d4b0f086cab3d2eb14370300201841106a42e3f39ebbdcd7db8c79370300201841386a419483808000360200201841246a410f36020041002d00fca3c680001a41c00041002802c8a3c68000118180808000002212450d56201241b1e3c38000360200201241fbe6c38000360238201241bae6c38000360230201241f8e5c38000360228201241b3e5c38000360220201241f2e4c38000360218201241b3e4c38000360210201241f5e3c38000360208201241c4003602042012413c6a41c100360200201241346a41c1003602002012412c6a41c200360200201241246a41c5003602002012411c6a41c100360200201241146a413f3602002012410c6a413e36020041002d00fca3c680001a41c00041002802c8a3c6800011818080800000221c450d57201c41affcc38000360200201c42a5e9e3ab9e929adc2c370308201c41a2ddc38000360220201c41ef80808000360218201c4106360204201c41306a42949384fce98ae9ac977f370300201c41286a42c4a8f7cba2aea4ad35370300201c41106a4293888c8f89fdc6ec9e7f370300201c41386a41ff81808000360200201c41246a410c36020041002d00fca3c680001a41d80041002802c8a3c6800011818080800000220c450d58200c41fffec38000360250200c41bcfec38000360248200c41f7fdc38000360240200c41ffe0c38000360238200c41b8fdc38000360230200c41f4fcc38000360228200c41b7dfc38000360220200c41f4dec38000360218200c41b3dec38000360210200c41b5fcc38000360208200c41c200360204200c41aeddc38000360200200c41d4006a411e360200200c41cc006a41c300360200200c41c4006a41c500360200200c413c6a41c300360200200c41346a413f360200200c412c6a41c400360200200c41246a41c400360200200c411c6a41c300360200200c41146a41c100360200200c410c6a413f36020041002d00fca3c680001a410841002802c8a3c68000118180808000002222450d59202241263602042022419dffc38000360200200a41b8016a4293888c8f89fdc6ec9e7f370300200a41b0016a42a5e9e3ab9e929adc2c370300200a4180016a42d38accc2f68caeecba7f370300200a41f8006a42bfcdc4dc94d7acde3b370300200a41c8006a42a0a58eade3c7d8c3f600370300200a41c0006a42bbbde2dfeee883e643370300200a41106a42d592f683e7e68799fc00370300200a4287bf8ac8928ddfce54370308200a41dc016a4101360200200a41d8016a2022360200200a41d0016a428080808010370300200a41c8016a42808080808001370300200a41c0016a41ef80808000360200200a41ac016a410e360200200a41c3ffc380003602a801200a41a4016a410b360200200a41a0016a200c360200200a4198016a4282808080b001370300200a4194016a201c360200200a4190016a4102360200200a4188016a419583808000360200200a41f4006a411c360200200a41f4e2c38000360270200a41ec006a4108360200200a41e8006a2012360200200a41e0006a42828080808001370300200a41dc006a2018360200200a41d8006a4102360200200a41d0006a418d83808000360200200a413c6a412d360200200a41bce7c38000360238200a4106360234200a2025360230200a4280808080e000370328200a42808080808001370320200a419683808000360218200a4113360204200a419cfcc3800036020041002d00fca3c680001a41c80041002802c8a3c68000118180808000002225450d5a202541d1ffc38000360200202541ce82c48000360240202541e8d1c380003602382025419582c48000360230202541c981c480003602282025418681c48000360220202541c180c48000360218202541e8d1c380003602102025419180c48000360208202541c000360204202541c4006a413f3602002025413c6a4100360200202541346a41393602002025412c6a41cc00360200202541246a41c3003602002025411c6a41c500360200202541146a41003602002025410c6a413036020041002d00fca3c680001a41a80141002802c8a3c6800011818080800000220c450d5b41002d00fca3c680001a412041002802c8a3c68000118180808000002222450d5c2022419ee8c38000360200202242e7b0a091f3ed9c85c500370308202241ea8180800036021820224104360204202241106a42b891b68c98adebcf6137030041002d00fca3c680001a41c80041002802c8a3c68000118180808000002218450d5d201841a2e8c38000360200201841dcebc3800036024020184185ebc38000360238201841e8d1c38000360230201841d8eac3800036022820184182eac3800036022020184184e9c38000360218201841e8d1c38000360210201841fbe8c38000360208201841d900360204201841c4006a41173602002018413c6a41d700360200201841346a41003602002018412c6a412d360200201841246a41d6003602002018411c6a41fe00360200201841146a41003602002018410c6a410936020041002d00fca3c680001a412041002802c8a3c6800011818080800000221d450d5e201d41feebc38000360200201d42b9bfe7eaeaf488d21c370308201d419783808000360218201d4102360204201d41106a42a9a98a9dddd4dcd0dc0037030041002d00fca3c680001a41f00041002802c8a3c68000118180808000002212450d5f20124190f2c38000360268201241b8f1c38000360260201241dff0c380003602582012418cf0c38000360250201241b3efc38000360248201241dfeec3800036024020124197eec38000360238201241e8d1c38000360230201241ffedc38000360228201241adedc38000360220201241deecc38000360218201241e8d1c38000360210201241d8ecc38000360208201241d80036020420124180ecc38000360200201241ec006a4121360200201241e4006a41d800360200201241dc006a41d900360200201241d4006a41d300360200201241cc006a41d900360200201241c4006a41d4003602002012413c6a41c800360200201241346a41003602002012412c6a4118360200201241246a41d2003602002012411c6a41cf00360200201241146a41003602002012410c6a410636020041002d00fca3c680001a412041002802c8a3c6800011818080800000221c450d60201c41e7f3c38000360218201c418ff3c38000360210201c41e8d1c38000360208201c41d400360204201c41bbf2c38000360200201c411c6a41c000360200201c41146a41d800360200201c410c6a4100360200200c4180016a42cdbdd1fc838da48215370300200c41f8006a4288bed2e1b0b8c0e336370300200c41c8006a42f6d183c8fca4ffd45a370300200c41c0006a42f99f94a5fdd3dbbaf900370300200c41106a42cfe5aeebd69bcbd576370300200c42eccdbcbecbac99cabc7f370308200c41a4016a4104360200200c41a0016a201c360200200c4198016a4280808080c000370300200c4190016a42808080808001370300200c4188016a419883808000360200200c41f4006a410c360200200c41a7f4c38000360270200c41ec006a410e360200200c41e8006a2012360200200c41e0006a4281808080e001370300200c41dc006a201d360200200c41d8006a4101360200200c41d0006a419183808000360200200c413c6a410a360200200c41b1f2c38000360238200c4109360234200c2018360230200c42818080809001370328200c2022360224200c4101360220200c419983808000360218200c410b360204200c41f3ebc3800036020041002d00fca3c680001a410841002802c8a3c68000118180808000002212450d612012413a360204201241b3f4c38000360200200641fc026a410e360200200641f8026a41edf4c38000360200200641f4026a4101360200200641f0026a2012360200200641e8026a428380808010370200200641e4026a200c360200200641dc026a428a80808030370200200641d8026a418d83c48000360200200641d4026a4109360200200641d0026a2025360200200641c8026a42848080809001370200200641c4026a200a360200200641bc026a428b808080c000370200200641b8026a41eed3c38000360200200641b4026a4101360200200641b0026a201e360200200641a8026a428280808010370200200641a4026a20133602002006419c026a42918080802037020020064198026a41d9d4c3800036020020064194026a410136020020064190026a201b36020020064188026a42818080801037020020064184026a201a360200200641fc016a428780808010370200200641f8016a4197e8c38000360200200641f4016a4101360200200641f0016a2019360200200641e8016a428680808010370200200641e4016a200b360200200641dc016a4287808080e000370200200641d8016a4184dbc38000360200200641d4016a4101360200200641d0016a2028360200200641c8016a428280808010370200200641c4016a2014360200200641bc016a428780808020370200200641b8016a4180a1c48000360200200641b0016a4204370200200641a8016a4212370200200641a4016a20053602002006419c016a428f808080a00237020020064198016a41fb83c4800036020020064194016a410136020020064190016a201736020020064188016a42818080801037020020064184016a2016360200200641fc006a428c80808010370200200641f8006a419ed9c38000360200200641f4006a4101360200200641f0006a2015360200200641e8006a428480808010370200200641e4006a2009360200200641dc006a4296808080c000370200200641d8006a41a4f9c38000360200200641d4006a4101360200200641d0006a2011360200200641c8006a428180808010370200200641c4006a20103602002006413c6a428880808010370200200641386a41c889c48000360200200641346a4101360200200641306a200f360200200641286a428380808010370200200641246a200e360200200642848080803037021c2006419586c48000360218200641013602142006200d36021020064283808080103702082006200836020420064103360200200041f0016a4104360200200041ec016a2002360200200041043602e801200041f0006a41ec8180800036020020004188016a41f081808000360200200041a0016a419a83808000360200200041b8016a418583808000360200200041d0016a419b83808000360200200041d8016a200741386c41386e360200200041dc016a2003360200200041e0016a200420036b41386e360200200041e4016a41043a0000200041c0016a42dba4b9c0a2bed19501370300200041c8016a4283af8bf0ede78391a67f370300200041a8016a42e3beec81d3e996af917f370300200041b0016a42f0a3fcacaeba9c956f37030020004190016a42dbe2e9e8becbf3fb2f37030020004198016a42fec1aad493d7e4ae3e370300200041f8006a42c4ccab9c81ebb4b8db0037030020004180016a42a4d2928ecac0faa432370300200042b7a18ef9ac95b6cbeb00370360200041e8006a42dda1fc828d83b3faab7f3703002000419c838080003602102000410c3602f401200041f8016a2006360200200041fc016a410c360200200042f9ffdfe3cfb190868f7f37030820004287f1a7a98f94a1fd58370300200041286a419a83808000360200200041c0006a419a82808000360200200041d8006a419d83808000360200200041c8006a42d8a6d184eacbe6c8937f370300200041d0006a428fa8ddeba8afb0e82c370300200041306a428efca59582e3caf618370300200041386a42988ac9c0f3f1b2b0b57f370300200042dbe2e9e8becbf3fb2f370318200041206a42fec1aad493d7e4ae3e370300200041e7016a200141fa006a2d00003a0000200041e5016a20012f00783b000020014190016a2480808080000f0b410841800410b280808000000b410441800310b280808000000b410841a80110b280808000000b4104410810b280808000000b4108412010b280808000000b4104410810b280808000000b4108412010b280808000000b4104410810b280808000000b4104410810b280808000000b410841a80110b280808000000b4104410810b280808000000b4108412010b280808000000b4104412010b280808000000b4104411810b280808000000b4104410810b280808000000b4108413810b280808000000b410841e00010b280808000000b410441c80010b280808000000b4104410810b280808000000b410841e00110b280808000000b4108412010b280808000000b4104412010b280808000000b4104410810b280808000000b4108412010b280808000000b4104410810b280808000000b410841c00010b280808000000b4104410810b280808000000b4104410810b280808000000b4108413810b280808000000b4108412010b280808000000b4104410810b280808000000b4104410810b280808000000b410841f00710b280808000000b4108412010b280808000000b4104410810b280808000000b4108412010b280808000000b4104410810b280808000000b4108412010b280808000000b4104411010b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4108412010b280808000000b4104410810b280808000000b4104410810b280808000000b4104411810b280808000000b4104411810b280808000000b4104411810b280808000000b4104410810b280808000000b410841c00010b280808000000b4104410810b280808000000b410841e00010b280808000000b4104411010b280808000000b4104410810b280808000000b410841e00010b280808000000b4104410810b280808000000b410841e00010b280808000000b4104410810b280808000000b410841f00010b280808000000b4104411810b280808000000b4104410810b280808000000b4104410810b280808000000b410841d00210b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104411010b280808000000b410841c00010b280808000000b410441d80010b280808000000b410841c00010b280808000000b410441c00010b280808000000b4104410810b280808000000b4108413810b280808000000b4108412010b280808000000b4104410810b280808000000b4104410810b280808000000b410841f00010b280808000000b4108412010b280808000000b4104413810b280808000000b4108412010b280808000000b4104411810b280808000000b4104410810b280808000000b410841e00110b280808000000b4104413010b280808000000b410841c00010b280808000000b410441c00010b280808000000b410841c00010b280808000000b410441d80010b280808000000b4104410810b280808000000b410441c80010b280808000000b410841a80110b280808000000b4108412010b280808000000b410441c80010b280808000000b4108412010b280808000000b410441f00010b280808000000b4104412010b280808000000b4104410810b280808000000be00903027f017e027f23808080800041d0036b22042480808080000240200241c0016a2d000022054102460d00200441e0006a200241b0016a290300370300200441d8006a200241a8016a290300370300200441d0006a200241a0016a290300370300200441086a41086a200241e0006a290300370300200441086a41106a200241e8006a290300370300200441086a41186a200241f0006a290300370300200441086a41206a200241f8006a290300370300200441306a20024180016a290300370300200441386a20024188016a290300370300200441c0006a20024190016a29030037030020042002290358370308200420024198016a290300370348200441f0016a41206a200241e1016a2d00003a0000200441f0016a41186a200241d9016a290000370300200441f0016a41106a200241d1016a290000370300200441f0016a41086a200241c9016a2900003703002004200241c1016a2900003703f001200241b8016a290300210620044198026a200441086a41e00010848e8080001a0b02400240024002400240200228020041736a2207410220074104491b0e0400010203000b200441f8026a41086a200241086a10a28a8080002004410d3602f8020c030b0240200241186a2d00002207417d6a41ff0171220841016a410320084102491b22084103460d00024002402008417f6a0e020001000b200228020810b087808000000b200228020810b087808000000b200441f8026a41186a20073a0000200441f8026a41106a200241106a2903003703002004410e3602f80220042002290308370380030c020b200441f8026a200210bf888080000c010b200441f8026a41086a200241086a10aa8c808000200441103602f8020b200441086a41d8006a20044198026a41e00010848e8080001a200441c8016a20053a0000200441c0016a2006370300200441c9016a20042903f001370000200441d1016a200441f0016a41086a290300370000200441d9016a200441f0016a41106a290300370000200441e1016a200441f0016a41186a290300370000200441e8016a2004418f026a290000370000200441086a200441f8026a41d80010848e8080001a20044198026a2001200441086a200310818d80800002404100280284a4c680004105470d00200441f8026a410c6a419e838080003602002004419f838080003602fc02200420023602f802200420044198026a360280034100280290a1c680002103410028028ca1c6800021014100280280a4c680002105200441c0006a4202370200200441386a4102360200200441306a41163602002004412c6a41e88bc48000360200200441086a41186a41f48ac48000ad4280808080900c84370200200441086a410c6a41fe8bc48000ad4280808080e002843702002004413c6a200441f8026a360200200441f4a1c48000360234200441053602282004410036021c2004410036021020044281808080e03e370208200141ecf2c08000200541024622051b200441086a200341d4f2c0800020051b280210118480808000000b2000200429039802370300200041286a20044198026a41286a290300370300200041206a20044198026a41206a290300370300200041186a20044198026a41186a290300370300200041106a20044198026a41106a290300370300200041086a20044198026a41086a290300370300200210c989808000200441d0036a2480808080000be41d03087f017e027f23808080800041b0026b220024808080800041002d00fca3c680001a024002400240024002400240024002400240024002400240024002400240024002400240411641002802c8a3c68000118180808000002201450d002001410e6a41002900a28cc48000370000200141086a410029009c8cc48000370000200141002900948cc4800037000041002d00fca3c680001a410a41002802c8a3c68000118180808000002202450d01200241086a41002f00b28cc480003b0000200241002900aa8cc4800037000041002d00fca3c680001a411641002802c8a3c68000118180808000002203450d022003410e6a41002900c28cc48000370000200341086a41002900bc8cc48000370000200341002900b48cc4800037000041002d00fca3c680001a410a41002802c8a3c68000118180808000002204450d03200441086a41002f00d28cc480003b0000200441002900ca8cc4800037000041002d00fca3c680001a413041002802c8a3c68000118180808000002205450d0441002d00fca3c680001a411641002802c8a3c68000118180808000002206450d05200620012900003700002006410e6a2001410e6a290000370000200641086a200141086a29000037000041002d00fca3c680001a410a41002802c8a3c68000118180808000002207450d0620072002290000370000200741086a200241086a2f00003b0000200541163602002005410a3602142005200736021020054296808080a0013702082005200636020441002d00fca3c680001a411641002802c8a3c68000118180808000002206450d07200620032900003700002006410e6a2003410e6a290000370000200641086a200341086a29000037000041002d00fca3c680001a410a41002802c8a3c68000118180808000002207450d0820072004290000370000200741086a200441086a2f00003b00002005412c6a410a360200200541286a2007360200200541206a4296808080a0013702002005411c6a200636020020054116360218200141002802c0a3c6800011808080800000200241002802c0a3c6800011808080800000200341002802c0a3c6800011808080800000200441002802c0a3c680001180808080000041002d00fca3c680001a410141002802c8a3c68000118180808000002201450d09200141003a0000200041186a419380c6800041014100280298a3c6800011858080800000200041146a4100360200200042013702082000200136020420004101360200200041386a41186a22014200370300200041386a41106a22034200370300200041c0006a2202420037030020004200370338200041386a41d48cc4800041014100280298a3c6800011858080800000200041b0016a41b48ac48000360200200041dc006a41286a4100360200200041dc006a41206a4204370200200041dc006a41186a4200370200200041c0016a4200370200200041a1016a200129030037000020004199016a200329030037000020004191016a200229030037000020004189016a200029033837000020004280808080c00037026c200041003602b801200041013a0088012000200041386a3602b401200020003602ac01200041003602642000420037025c200041f8016a200041dc006a2005280204200528020820052802102005280214109286808000200041dc006a41106a2103024020002d00f80122014104460d002000419c026a2104200041f8016a41046a2102024020014103460d0002400240024020010e020103000b200028029c02220120012802002206417f6a3602002004210120064101460d010c020b20002802fc01220120012802002206417f6a3602002002210120064101470d010b200110e28a8080000b200041f8016a200041dc006a200528021c20052802202005280228200528022c10928680800020002d00f80122014104460d00024020014103460d0002400240024020010e020003010b20002802fc01220120012802002201417f6a36020020014101460d010c020b200028029c02220120012802002201417f6a3602002004210220014101470d010b200210e28a8080000b200041dc006a108f8680800020031090878080000240200028026c450d00200028027041002802c0a3c68000118080808000000b02402000280278450d00200028027c41002802c0a3c68000118080808000000b4100210141002103024020002802b8012202450d0020002002360290022000410036028c022000200236028002200041003602fc012000200041bc016a28020022013602940220002001360284024101210120002802c00121030b20002003360298022000200136028802200020013602f801200041f8016a10a48d808000200041f0016a41d88cc48000360200200041cc016a41186a4100360200200042003702dc01200041003602d401200042003702cc012000200041386a3602f401200020003602ec01200041dc006a200041cc016a10b388808000200028025c2201418080808078470d0e42e60021082000280260220328020041fcffffff076a2201410320014105491b0e040f0f0c0b0d0b024002400240024020002802fc01220128020041fcffffff076a2202410320024105491b0e0403030102000b2001280204450d02200141086a28020041002802c0a3c68000118080808000000c020b2001280204450d01200141086a28020041002802c0a3c68000118080808000000c010b200110ca898080000b200141002802c0a3c6800011808080800000200041dc006a108f8680800020031090878080000240200028026c450d00200028027041002802c0a3c68000118080808000000b02402000280278450d00200028027c41002802c0a3c68000118080808000000b410021014100210341002102024020002802b8012204450d0020002004360290022000410036028c022000200436028002200041003602fc012000200041bc016a28020022033602940220002003360284024101210320002802c00121020b20002002360298022000200336028802200020033602f801200041f8016a10a48d808000410021030240200028020c2202450d00200020023602742000410036027020002002360264200041003602602000200041106a28020022013602782000200136026841012101200028021421030b2000200336027c2000200136026c2000200136025c200041dc006a10928d80800002402000280200450d00200028020441002802c0a3c68000118080808000000b02402005280200450d00200528020441002802c0a3c68000118080808000000b0240200528020c450d00200528021041002802c0a3c68000118080808000000b02402005280218450d00200528021c41002802c0a3c68000118080808000000b42e50021082005280224450d100c0f0b4101411610b280808000000b4101410a10b280808000000b4101411610b280808000000b4101410a10b280808000000b4104413010b280808000000b4101411610b280808000000b4101410a10b280808000000b4101411610b280808000000b4101410a10b280808000000b4101410110b280808000000b200310ca898080000c030b2003280204450d02200341086a28020041002802c0a3c68000118080808000000c020b2003280204450d01200341086a28020041002802c0a3c68000118080808000000c010b20004180026a2202200041dc006a41106a290200370300200041f8016a41106a2204200041dc006a41186a290200370300200041f8016a41186a200041dc006a41206a290200370300200041f8016a41206a2206200041dc006a41286a290200370300200041f8016a41286a2207200041dc006a41306a290200370300200041f8016a41306a2209200041dc006a41386a280200360200200020002902643703f8012000280260210a41002d00fca3c680001a41c00041002802c8a3c68000118180808000002203450d032003200a36020420032001360200200320002903f801370208200341106a2002290300370200200341186a2004290300370200200341206a200041f8016a41186a290300370200200341286a2006290300370200200341306a2007290300370200200341386a20092802003602002003200041cc016a36023c200041dc006a2003200041cc016a10b488808000420021080240200028025c2202418180808078460d00410021040340200028026021010240024002402002418080808078470d0041002102024002400240200128020041fcffffff076a2206410320064105491b0e0404040102000b2001280204450d0341002102200141086a28020041002802c0a3c68000118080808000000c030b2001280204450d0241002102200141086a28020041002802c0a3c68000118080808000000c020b200110ca898080000c010b200028026c21062000280268210702402002450d00200141002802c0a3c68000118080808000000b41012102200621012007450d010b200141002802c0a3c68000118080808000000b200041dc006a2003200328023c10b488808000200220046a2104200028025c2202418180808078470d000b2004ad21080b024020032802082202450d00200328020441086a210103402001280200220420042802002204417f6a360200024020044101470d00200110e18a8080000b200141306a21012002417f6a22020d000b0b02402003280200450d00200328020441002802c0a3c68000118080808000000b200341346a2802004129490d00200328020c41002802c0a3c68000118080808000000b41002101200341002802c0a3c6800011808080800000410021030240200028020c2202450d00200020023602742000410036027020002002360264200041003602602000200041106a28020022013602782000200136026841012101200028021421030b2000200336027c2000200136026c2000200136025c200041dc006a10928d80800002402000280200450d00200028020441002802c0a3c68000118080808000000b02402005280200450d00200528020441002802c0a3c68000118080808000000b0240200528020c450d00200528021041002802c0a3c68000118080808000000b02402005280218450d00200528021c41002802c0a3c68000118080808000000b2005280224450d010b200528022841002802c0a3c68000118080808000000b200541002802c0a3c6800011808080800000200041b0026a24808080800020080f0b410441c00010b280808000000be207010a7f2380808080004180026b2201248080808000200141808080807836022c200141b8016a41086a22022001412c6a41086a22032802003602002001200129022c3703b801410021042001410c6a41e5c8c9a903200141b8016a4100280290a2c6800011858080800000200220032802003602002001200129022c3703b801200141386a41e5c8c9a903200141b8016a4100280290a2c6800011858080800000200141d8006a41e5c8c9a9032001412c6a4100280290a2c6800011858080800000200141b8016a41e5c8c9a9034100280298a2c680001184808080000020012802c0012205410574210620012802b80141ffffff3f71210720012802bc01210202400240024002400240034020062004460d01200220046a2103200441206a210420032001410c6a412010888e8080000d000b2005410574210641002104034020062004460d02200220046a2103200441206a21042003200141386a412010888e8080000d000b2005410574210641002104034020062004460d03200220046a2103200441206a21042003200141d8006a412010888e8080000d000b200141b8016a41e5c8c9a9032001410c6a41948ec48000410741002802a0a2c680001187808080000020012d00b801450d03200141f8006a41386a2204200141f1016a290000370300200141f8006a41306a2203200141e9016a290000370300200141f8006a41286a2206200141e1016a290000370300200141f8006a41206a2205200141d9016a290000370300200141f8006a41186a2208200141d1016a290000370300200141f8006a41106a2209200141c9016a290000370300200141f8006a41086a220a200141c1016a290000370300200120012900b901370378200141f8006a41948ec4800041072001410c6a41002802a8a2c6800011898080800000450d04200020012903783700002000200129000c370040200041386a2004290300370000200041306a2003290300370000200041286a2006290300370000200041206a2005290300370000200041186a2008290300370000200041106a2009290300370000200041086a200a290300370000200041c8006a2001410c6a41086a290000370000200041d0006a2001410c6a41106a290000370000200041d8006a2001410c6a41186a29000037000002402007450d00200241002802c0a3c68000118080808000000b20014180026a2480808080000f0b41ec8cc48000412841948dc4800010f880808000000b41a48dc48000412841cc8dc4800010f880808000000b41dc8dc48000412841848ec4800010f880808000000b419b8ec48000412641c48ec4800010a181808000000b41d48ec480004138418c8fc4800010f880808000000be207010a7f2380808080004180026b2201248080808000200141808080807836022c200141b8016a41086a22022001412c6a41086a22032802003602002001200129022c3703b801410021042001410c6a41f3e4c9a903200141b8016a41002802b0a2c6800011858080800000200220032802003602002001200129022c3703b801200141386a41f3e4c9a903200141b8016a41002802b0a2c6800011858080800000200141d8006a41f3e4c9a9032001412c6a41002802b0a2c6800011858080800000200141b8016a41f3e4c9a90341002802b8a2c680001184808080000020012802c0012205410574210620012802b80141ffffff3f71210720012802bc01210202400240024002400240034020062004460d01200220046a2103200441206a210420032001410c6a412010888e8080000d000b2005410574210641002104034020062004460d02200220046a2103200441206a21042003200141386a412010888e8080000d000b2005410574210641002104034020062004460d03200220046a2103200441206a21042003200141d8006a412010888e8080000d000b200141b8016a41f3e4c9a9032001410c6a41cc8fc48000410741002802c0a2c680001187808080000020012d00b801450d03200141f8006a41386a2204200141f1016a290000370300200141f8006a41306a2203200141e9016a290000370300200141f8006a41286a2206200141e1016a290000370300200141f8006a41206a2205200141d9016a290000370300200141f8006a41186a2208200141d1016a290000370300200141f8006a41106a2209200141c9016a290000370300200141f8006a41086a220a200141c1016a290000370300200120012900b901370378200141f8006a41cc8fc4800041072001410c6a41002802c8a2c6800011898080800000450d04200020012903783700002000200129000c370040200041386a2004290300370000200041306a2003290300370000200041286a2006290300370000200041206a2005290300370000200041186a2008290300370000200041106a2009290300370000200041086a200a290300370000200041c8006a2001410c6a41086a290000370000200041d0006a2001410c6a41106a290000370000200041d8006a2001410c6a41186a29000037000002402007450d00200241002802c0a3c68000118080808000000b20014180026a2480808080000f0b41ec8cc480004128419c8fc4800010f880808000000b41a48dc48000412841ac8fc4800010f880808000000b41dc8dc48000412841bc8fc4800010f880808000000b41d38fc48000412641fc8fc4800010a181808000000b418c90c48000413841c490c4800010f880808000000bff0501077f2380808080004180026b22012480808080002001418080808078360228200141b8016a41086a2202200141286a41086a2203280200360200200120012902283703b80141002104200141076a41e5c6919b07200141b8016a41002802f0a1c680001185808080000020022003280200360200200120012902283703b801200141356a41e5c6919b07200141b8016a41002802f0a1c6800011858080800000200141d6006a41e5c6919b07200141286a41002802f0a1c6800011858080800000200141b8016a41e5c6919b0741002802f8a1c680001184808080000020012802c001220541216c210620012802b80141216c210720012802bc01210202400240024002400240034020062004460d01200220046a2103200441216a21042003200141076a412110888e8080000d000b200541216c210641002104034020062004460d02200220046a2103200441216a21042003200141356a412110888e8080000d000b200541216c210641002104034020062004460d03200220046a2103200441216a21042003200141d6006a412110888e8080000d000b200141b8016a41e5c6919b07200141076a418491c4800041054100280280a2c680001187808080000020012d00b801450d03200141f7006a200141b9016a41c10010848e8080001a200141f7006a418491c480004105200141076a4100280288a2c6800011898080800000450d042000200141f7006a41c10010848e808000220441e1006a200141276a2d00003a0000200441d9006a2001411f6a290000370000200441d1006a200141176a290000370000200441c9006a2001410f6a29000037000020042001290007370041024020074121490d00200241002802c0a3c68000118080808000000b20014180026a2480808080000f0b41ec8cc48000412841d490c4800010f880808000000b41a48dc48000412841e490c4800010f880808000000b41dc8dc48000412841f490c4800010f880808000000b418991c48000412441b091c4800010a181808000000b41c091c48000413641f891c4800010f880808000000be50601037f23808080800041e0006b2200248080808000418c92c48000410d418892c48000410441002802e0a1c68000118680808000002000410036022c200041206a418c92c48000410d2000412c6a4104410041002802c8a1c68000118a8080800000200020002802242201360234200020002802202202360230024002400240024002400240024002402002450d0020014104470d00200028022c210220002000412c6a36023c200241f4cacda307470d0120004100360238200041186a418c92c48000410d200041386a4104410441002802c8a1c68000118a80808000002000200028021c220136024020002000280218220236023c2002450d0220010d022000200041386a36024420002802380d0341f092c48000410b41fb92c480004113418892c48000410441002802e8a2c68000118a80808000002000410036022c200041106a41f092c48000410b41fb92c4800041132000412c6a4104410041002802e0a2c68000118b80808000002000200028021422013602342000200028021022023602302002450d0420014104470d04200028022c210220002000412c6a36023c200241f4cacda307470d0520004100360238200041086a41f092c48000410b41fb92c480004113200041386a4104410841002802e0a2c68000118b80808000002000200028020c220136024020002000280208220236023c2002450d0620010d062000200041386a36024420002802380d07200041e0006a2480808080000f0b200041003602484100200041306a419c92c48000200041c8006a41a492c4800010a489808000000b2000410036024841002000413c6a41b492c48000200041c8006a41b892c4800010a589808000000b2000410036024841002000413c6a41ec8ac48000200041c8006a41c892c4800010a489808000000b200041003602484100200041c4006a41dc92c48000200041c8006a41e092c4800010a589808000000b200041003602484100200041306a419c92c48000200041c8006a419093c4800010a489808000000b2000410036024841002000413c6a41b492c48000200041c8006a41a093c4800010a589808000000b2000410036024841002000413c6a41ec8ac48000200041c8006a41b093c4800010a489808000000b200041003602484100200041c4006a41dc92c48000200041c8006a41c093c4800010a589808000000b991701077f23808080800041f0056b2202248080808000200241e0026a41086a2203200041086a280200360200200220002902003703e00220024184026a200241e0026a109a8d808000200241e0026a10a68d808000200241e0026a41306a20024184026a41306a290200370300200241e0026a41286a20024184026a41286a290200370300200241e0026a41206a20024184026a41206a290200370300200241e0026a41186a20024184026a41186a290200370300200241e0026a41106a20024184026a41106a290200370300200320024184026a41086a290200370300200241a0036a200141086a290000370300200241a8036a200141106a290000370300200241b0036a200141186a29000037030020022002290284023703e0022002200129000037039803200241b8036a41b89ec3800041014100280298a3c68000118580808000002002200241e0026a41f80010848e8080002202428080808080808080807f37027c200241003b0178200241d4046a41003a0000200241d0046a41003602002002418c046a420037020020024180046a4200370300200241b4056a4100360200200241a8056a41003a0000200241a4056a4100360200200241e0046a4200370300200241003a00b80520024280808080c0003702ac05200241003602d804200241003602f8032002420037039803200241003602900320024200370388032002410036028003200242003703f802200241003602f002200242003703e802200241003602e00220024180808080783602a003200241003b01cc05200220023602c8052002200241e0026a3602c40520024184026a200241c4056a41d093c48000410610df8680800002400240024002402002280284022200418080808078460d0002402000450d0020022802880241002802c0a3c68000118080808000000b20024184026a200241e0026a20024101109088808000200241d0056a41186a220320024184026a41186a290000370300200241d0056a41106a220420024184026a41106a290000370300200241d0056a41086a220520024184026a41086a29000037030020022002290084023703d00541002d00fca3c680001a2002410036028c022002428080808010370284020240412041002802c8a3c68000118180808000002200450d00200020022903d005370000200041186a22062003290300370000200041106a22072004290300370000200041086a2208200529030037000020024184026a4100412010b1828080002002280288022204200228028c0222056a22032000290000370000200341086a2008290000370000200341106a2007290000370000200341186a2006290000370000200041002802c0a3c680001180808080000020022802840221000240024020050d0020042001412010888e808000450d010b02402000450d00200441002802c0a3c68000118080808000000b41d693c4800041ce0041a494c4800010f880808000000b02402000450d00200441002802c0a3c68000118080808000000b41002d00fca3c680001a410141002802c8a3c68000118180808000002200450d02200041003a0000200241013602d805200220003602d405200241013602d00541002d00fca3c680001a410141002802c8a3c68000118180808000002200450d03200041013a00002002410136028c0220022000360288022002410136028402200241e0026a200241d0056a20024184026a108f8880800020024184026a200241e0026a20024101109088808000200241d0056a41186a220320024184026a41186a290000370300200241d0056a41106a220420024184026a41106a290000370300200241d0056a41086a220520024184026a41086a29000037030020022002290084023703d00541002d00fca3c680001a2002410036028c02200242808080801037028402412041002802c8a3c68000118180808000002200450d04200020022903d005370000200041186a22062003290300370000200041106a22072004290300370000200041086a2204200529030037000020024184026a4100412010b1828080002002280288022205200228028c0222086a22032000290000370000200341086a2004290000370000200341106a2007290000370000200341186a2006290000370000200041002802c0a3c68000118080808000002002280284022100024020080d0020052001412010888e8080000d0002402000450d00200541002802c0a3c68000118080808000000b41b494c4800041ce00418495c4800010f880808000000b20024184046a210102402000450d00200541002802c0a3c68000118080808000000b200241d8046a2103200241f8036a2104200110a38d80800002400240200228029004220141054b0d002001450d01200241e0026a41b4016a21000340200010a68d8080002000410c6a21002001417f6a22010d000c020b0b200241e0026a41b4016a2802002105024020024198046a2802002201450d00200521000340200010a68d8080002000410c6a21002001417f6a22010d000b0b200541002802c0a3c68000118080808000000b200410a28d808000200310a18d8080000240024020022802e404220141054b0d002001450d01200241e0026a4188026a21000340200010a78d8080002000410c6a21002001417f6a22010d000c020b0b200241e0026a4188026a28020021030240200241ec046a2802002201450d00200321000340200010a78d8080002000410c6a21002001417f6a22010d000b0b200341002802c0a3c68000118080808000000b024020022802b4052203450d0020022802b00521052003410171210641002101024020034101460d00200541146a21002003417e7121044100210103400240200041706a2000416c6a22032003280200418080808078461b2203280200450d00200328020441002802c0a3c68000118080808000000b0240200041046a20002000280200418080808078461b2203280200450d00200328020441002802c0a3c68000118080808000000b200041286a21002004200141026a2201470d000b0b2006450d002005200141146c6a220020002802004180808080784622014102746a280200450d00200041046a200020011b28020441002802c0a3c68000118080808000000b024020022802ac05450d0020022802b00541002802c0a3c68000118080808000000b024020022802a003418080808078460d0002400240200241ac036a28020022000d0041002100410021010c010b2002200036029c0220024100360298022002200036028c0220024100360288022002200241b0036a28020022003602a0022002200036029002200241b4036a2802002101410121000b200220013602a4022002200036029402200220003602840220024184026a10928d80800020022802a003450d00200241a4036a28020041002802c0a3c68000118080808000000b2002410c6a10a58d80800002402002280200450d00200228020441002802c0a3c68000118080808000000b02402002280280012200418080808078460d0002402000450d0020024184016a28020041002802c0a3c68000118080808000000b0240200241c8016a2802002200418080808078460d002000450d00200241cc016a28020041002802c0a3c68000118080808000000b0240200241d4016a2802002200418080808078460d002000450d00200241d8016a28020041002802c0a3c68000118080808000000b024020024194016a2802002201450d0020024190016a28020041086a210003402000280200220320032802002203417f6a360200024020034101470d00200010e18a8080000b200041306a21002001417f6a22010d000b0b0240200228028c01450d0020022802900141002802c0a3c68000118080808000000b200241c0016a2802004129490d0020024198016a28020041002802c0a3c68000118080808000000b200241f0056a2480808080000f0b4101412010b280808000000b41808080807820022802880210cb89808000419495c48000413241c895c4800010f880808000000b4101410110b280808000000b4101410110b280808000000b4101412010b280808000000b850201057f23808080800041306b220324808080800020032000280208220436020c2003200028020422053602082003200128020822063602142003200128020422073602100240024020042006470d0020052007200410888e8080000d00200320043602082003200236021020042002470d0102402001280200450d00200741002802c0a3c68000118080808000000b02402000280200450d00200541002802c0a3c68000118080808000000b200341306a2480808080000f0b200341003602184100200341086a200341106a200341186a419ca2c4800010a689808000000b200341003602184100200341086a200341106a200341186a41aca2c4800010a789808000000bce0e020c7f037e23808080800041e0016b220024808080800002404100280284a4c680004105470d004100280290a1c680002101410028028ca1c6800021024100280280a4c680002103200041c0016a4200370200200041bc016a41e8d1c38000360200200041b8016a4101360200200041b0016a4104360200200041ac016a418892c48000360200200041a0016a41f48ac48000ad4280808080900c8437020020004194016a41fe8bc48000ad4280808080e00284370200200041cca2c480003602b401200041053602a8012000410036029c01200041003602900120004281808080e0ca0037028801200241ecf2c08000200341024622031b20004188016a200141d4f2c0800020031b280210118480808000000b2000410f360210200041d4a2c4800036020c02400240024002400240024041002802f8a3c680000d0002400240024041002d00e8a3c680000e03030102000b41e0a3c6800010c58d80800041ff01710e03020001000b41002802e0a3c680002103024002404100280294a4c680004102460d0041988dc68000210241e48dc6800021010c010b41002802f4a3c68000210241002802f0a3c68000210141002802eca3c68000450d002002280208417f6a41787120016a41086a21010b20012003200228021411838080800000450d010b41002802e0a3c68000220141206a28020022020d0141e3a2c4800041224188a3c4800010a181808000000b41002d0090a4c680000d044100280284a4c680004105470d0441002802e0a3c6800021012000410536021420002001290214370218410028028ca1c6800041ecf2c080004100280280a4c6800041024622021b2204200041146a4100280290a1c6800041d4f2c0800020021b220528020c11838080800000450d0441002802e0a3c68000220241206a2802002203450d01200028021c21062000280218210720002802142108200241286a2802002109200241246a280200210a200228021c210b20004100360248200020093602442000200a3602402000200b3602382000200336023c200041cc006a410c6a4200370200200041a8a3c4800036024c200041e8d1c380003602542000410136025020034101460d02200041346a41c0a3c4800036020020004101360274200020093602702000200a36026c200020033602682000200b360264200041b0a3c4800036022820002002411c6a36028401200041306a200041f8006a3602002000200041e4006a36022c2000200041cc006a3602242000200041386a3602202000200041206a36027c20002000410c6a36027820004102360280012001290200210c20004188016a41386a420137020020004188016a41306a4101360200200041b0016a2006360200200041ac016a2007360200200041a0016a200141386a3502004220862001350234220d8437020020004188016a410c6a200141306a350200422086200135022c220e84370200200041bc016a200041d0016a3602002000418c89c680003602b401200020083602a801200041a0838080003602d401200041013a00dc01200041024101200d501b36029c01200041024101200e501b360290012000200041d8016a3602d0012000200041fc006a3602d8012000200c37028801200420004188016a2005280210118480808000000c040b200141286a2802002103200141246a2802002104200128021c2105200041003602482000200336024420002004360240200020053602382000200236023c200041d8006a4200370200200041a8a3c4800036024c200041e8d1c380003602542000410136025020024101460d02200041346a41c0a3c4800036020020004101360274200020033602702000200436026c2000200236026820002005360264200041b0a3c4800036022820002001411c6a36021c200041306a200041d8016a3602002000200041e4006a36022c2000200041cc006a3602242000200041386a3602202000200041206a36021420002000410c6a3602d801200041023602182000200136029c0120004201370388014100280294a4c6800021012000200041146a36029801024020014102470d0041002802f4a3c68000210241002802f0a3c680002101024041002802eca3c68000450d002002280208417f6a41787120016a41086a21010b200120004188016a200228022811838080800000450d00200120004188016a200228022c118480808000000b41002d0090a4c680000d034100280284a4c680004105470d0341002802e0a3c6800021012000410536027c2000200129021437028001410028028ca1c6800041ecf2c080004100280280a4c6800041024622021b2203200041fc006a4100280290a1c6800041d4f2c0800020021b220228020c11838080800000450d0320004188016a41086a200041fc006a41086a2802003602002000200029027c3703880120012003200220004188016a200041146a10c08d8080000c030b41e3a2c4800041224188a3c4800010a181808000000b41e3a2c4800041224188a3c4800010a181808000000b41e3a2c4800041224188a3c4800010a181808000000b200041e0016a2480808080000ba80605027f027e087f017e017f23808080800041d0006b2201248080808000200142919fd78da1a1ad84ff003703082001429ceccef7a6c0dedd20370300200142c2a5b2f6d3b4fb986f370318200142dcd7ddd8f18e8ca1e300370310200141306a200110e88880800020012d004021022001290330210320012903382104200142919fd78da1a1ad84ff003703082001429ceccef7a6c0dedd2037030020014282fceae49dd9e2861d370318200142de8c84a1ecd0a6d30c370310200141306a200110e288808000024002400240024020012802302205418080808078470d00410021064108210741082108410021050c010b200128023421080240200128023822060d0041002106410821070c010b200641b3e6cc194b0d01200641286c2209417f4c0d014100210a41002d00fca3c680001a200941002802c8a3c68000118180808000002207450d022006210b2008210c03402009200a460d01200c290320210d2007200a6a220e200c290300370300200e41186a200c41186a290300370300200e41106a200c41106a290300370300200e41086a200c41086a290300370300200e41206a200d370300200a41286a210a200c41286a210c200b417f6a220b0d000b0b200142919fd78da1a1ad84ff003703382001429ceccef7a6c0dedd20370330200142c6e4a9b1aaa1afebf200370348200142fa82b1828b81b8f31e37034042032003200241ff0171410346220c1b210d420a2004200c1b210341012002200c1b210e200041206a210c2001200141306a10e5888080000240024020012d00000d00200c4200370300200c41186a4200370300200c41106a4200370300200c41086a42003703000c010b200c2001290001370000200c41186a200141196a290000370000200c41106a200141116a290000370000200c41086a200141096a2900003700000b2000200e3a004c200020063602402000200d37031020004206370308200042e807370300200041c8006a2006360200200041c4006a2007360200200041186a200337030002402005450d00200841002802c0a3c68000118080808000000b200141d0006a2480808080000f0b10ae80808000000b4108200910b280808000000b940301047f02402001280200450d00200128020441002802c0a3c68000118080808000000b20004194016a2802002102024020004198016a2802002203450d00200221014100210403402002200441146c6a21050240024002400240024020012d00000e0400010102040b200141086a21050c020b200541086a21050c010b200541046a21050b2005280200450d00200528020441002802c0a3c68000118080808000000b200441016a2104200141146a21012003417f6a22030d000b0b0240200028029001450d00200241002802c0a3c68000118080808000000b2000418c026a2802002102024020004190026a2802002203450d00200221014100210403402002200441146c6a21050240024002400240024020012d00000e0400010102040b200141086a21050c020b200541086a21050c010b200541046a21050b2005280200450d00200528020441002802c0a3c68000118080808000000b200441016a2104200141146a21012003417f6a22030d000b0b0240200028028802450d00200241002802c0a3c68000118080808000000b41000bad0201037f2380808080004180026b220124808080800041002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200242f3deb5abf6ebdab2f90037000041002d00fca3c680001a410841002802c8a3c68000118180808000002203450d0120032000290320370000200141c8016a41023a00002001410836021420012003360210200141083602182001200236021c20014108360220200142838080808001370308200141f0016a200141086a10f78c808000200141f0016a41002802d8a3c6800011818080800000450d024184a4c48000412b200141ff016a41b0a4c4800041c0a4c48000108981808000000b4101410810b280808000000b4101410810b280808000000b200141086a10c98980800020014180026a2480808080000b040041010bc80301057f23808080800041206b22022480808080000240024020012d00000d00410221030c010b200241186a200141196a290000370300200241106a200141116a290000370300200241086a200141096a29000037030020022001290001370300410121030b41002d00fca3c680001a02400240411041002802c8a3c68000118180808000002201450d0020014281808080103702002001410c6a41c8a6c480003602002001410136020841002d00fca3c680001a411041002802c8a3c68000118180808000002204450d01200441b0a6c4800036020c20044101360208200442818080801037020020012001280200417f6a2205360200024020050d00200128020822062001410c6a28020022052802001180808080000002402005280204450d00200641002802c0a3c68000118080808000000b20012001280204417f6a220536020420050d00200141002802c0a3c68000118080808000000b200020033a000420002004360200200020022903003700052000410d6a200241086a290300370000200041156a200241106a2903003700002000411d6a200241186a290300370000200241206a2480808080000f0b4104411010b280808000000b4104411010b280808000000ba90a01077f23808080800041f0026b22032480808080000240024020022d0004450d002002280200220441086a28020020012004410c6a280200280214118380808000000d00200041206a410610dc88808000200041003a00182000420037030020042004280200417f6a2202360200024020020d00200428020822002004410c6a28020022022802001180808080000002402002280204450d00200041002802c0a3c68000118080808000000b200441046a22022002280200417f6a220236020020020d00200441002802c0a3c68000118080808000000b200110c9898080000c010b200128020021042003410c6a200141046a220541d40010848e8080001a0240024002400240200441736a2201410220014104491b0e0400010203000b200341a8016a200341306a290200370300200341a0016a200341286a29020037030020034198016a200341206a29020037030020034190016a200341186a290200370300200341e0006a41086a200241086a290200370300200341e0006a41106a200241106a290200370300200341e0006a41186a200241186a290200370300200341e0006a41206a200241206a290200370300200320032902103703880120032002290200370360200341003602e802200341003602e0022003200341e0026a3602ec02200341d8016a200341e0006a41d00010848e8080001a2003200341ec026a3602a802200041f0a0c68000200341d8016a10c187808000200341e0026a10a98d8080000c030b200341e0006a41286a22012003290210370300200341e0006a41386a2204200341206a290200370300200341e0006a41306a2205200341186a290200370300200341e0006a41086a2206200241086a290200370300200341e0006a41106a2207200241106a290200370300200341e0006a41186a2208200241186a290200370300200341e0006a41206a2209200241206a29020037030020032002290200370360200341003602e802200341003602e0022003200341e0026a3602ec02200341d8016a41386a2004290300370300200341d8016a41306a2005290300370300200341d8016a41286a2001290300370300200341d8016a41206a2009290300370300200341d8016a41186a2008290300370300200341d8016a41106a2007290300370300200341d8016a41086a2006290300370300200320032903603703d8012003200341ec026a36029802200041f0a0c68000200341d8016a10c287808000200341e0026a10a98d8080000c020b200341d8016a410472200541d40010848e8080001a200341d0026a200241206a290200370300200341c8026a200241186a290200370300200341c0026a200241106a290200370300200341b8026a200241086a290200370300200320022902003703b0022003410036026820034100360260200320043602d8012003200341e0026a3602d8022003200341e0006a3602e002200041f0a0c68000200341d8016a10c387808000200341e0006a10a98d8080000c010b20034188016a2003410c6a41046a41d00010848e8080001a200341e0006a41206a200241206a290200370300200341e0006a41186a200241186a290200370300200341e0006a41106a200241106a290200370300200341e0006a41086a200241086a29020037030020032002290200370360200341003602e802200341003602e0022003200341e0026a3602ec02200341d8016a200341e0006a41f80010848e8080001a2003200341ec026a3602d002200041f0a0c68000200341d8016a10c587808000200341e0026a10a98d8080000b200341f0026a2480808080000bcc0303027f017e027f23808080800041d0006b22012480808080002001412c6a41eba7c48000410b41fe8bc48000411641e8d1c38000410010d882808000200141086a41086a2202410036020020014280808080c00037030820012902302103200128022c21042001410036021c20014280808080c000370214200141c4006a200141146a41f9a6c48000410610c98b808000200141146a200141c4006a41f5a6c48000410410948b808000200141c4006a200141146a41e8a6c48000410d10c88b808000200141386a200141c4006a41e0a6c48000410810818b8080002001200128023836021c2001200128023c220536021420012005200128024041246c6a36022020012005360218200141c4006a200141146a10fb8680800002402004418080808078470d0041a8d8c480004111419cd9c4800010a181808000000b200042808080808001370244200041cc006a4100360200200141146a410b6a200141c4006a41086a2802003600002000413c6a200337020020002004360238200041013a000020002001290308370350200041d8006a20022802003602002001200129024437001720002001290014370001200041086a2001411b6a290000370000200141d0006a2480808080000bbd0203027f017e017f23808080800041d0006b2201248080808000200141286a418ea8c48000410741fe8bc48000411641e8d1c38000410010d882808000200141086a2202410036020020014280808080c000370300200129022c210320012802282104200142808080808001370218200142888080808001370210200141346a200141106a10f98680800002402004418080808078470d0041a8d8c480004111419cd9c4800010a181808000000b200042808080808001370244200041cc006a4100360200200141cc006a200141346a41086a2802003600002000413c6a200337020020002004360238200041003a000020002001290300370350200041d8006a20022802003602002001200129023437004420002001290041370001200041086a200141c1006a41076a290000370000200141d0006a2480808080000b970303027f017e027f23808080800041d0006b22012480808080002001412c6a4182a8c48000410c41fe8bc48000411641e8d1c38000410010d882808000200141086a41086a2202410036020020014280808080c00037030820012902302103200128022c21042001410036021c20014280808080c000370214200141c4006a200141146a41f9a6c480004106109c8b808000200141386a200141c4006a41e0a6c48000410810a58b8080002001200128023836021c2001200128023c220536021420012005200128024041246c6a36022020012005360218200141c4006a200141146a10fb8680800002402004418080808078470d0041a8d8c480004111419cd9c4800010a181808000000b200042808080808001370244200041cc006a41003602002001411f6a200141c4006a41086a2802003600002000413c6a200337020020002004360238200041013a000020002001290308370350200041d8006a20022802003602002001200129024437001720002001290014370001200041086a2001411b6a290000370000200141d0006a2480808080000baf0303027f017e027f23808080800041d0006b22012480808080002001412c6a41f6a7c48000410c41fe8bc48000411641e8d1c38000410010d882808000200141086a41086a2202410036020020014280808080c00037030820012902302103200128022c21042001410036021c20014280808080c000370214200141c4006a200141146a41f9a6c48000410610c38b808000200141146a200141c4006a41f5a6c48000410410d28b808000200141386a200141146a41e0a6c48000410810b68b8080002001200128023836021c2001200128023c220536021420012005200128024041246c6a36022020012005360218200141c4006a200141146a10fb8680800002402004418080808078470d0041a8d8c480004111419cd9c4800010a181808000000b200042808080808001370244200041cc006a41003602002001411f6a200141c4006a41086a2802003600002000413c6a200337020020002004360238200041013a000020002001290308370350200041d8006a20022802003602002001200129024437001720002001290014370001200041086a2001411b6a290000370000200141d0006a2480808080000baf0201057f23808080800041206b2201248080808000200041c0006a108c8a808000200010ac878080002000412c6a28020021020240024002400240200041306a28020022030d0041012104410021050c010b200341ffffff1f4b0d0120034105742205417f4c0d0141002d00fca3c680001a200541002802c8a3c68000118180808000002204450d020b20042002200510848e8080002105200142f0fe93e98686d7ab8c7f37030820014280eee1b0e3b7afe9987f37030020014282fceae49dd9e2861d370318200142de8c84a1ecd0a6d30c370310200520032001412010cd8780800002402003450d00200541002802c0a3c68000118080808000000b200041346a10988c80800010928a808000200141206a2480808080000f0b10ae80808000000b4101200510b280808000000bc30301027f23808080800041d0006b2203248080808000024002400240024002400240200228020041736a2204410220044104491b0e03010203000b200041808080807836021020004181023b01000c030b20002001200241086a10b98a8080000c020b0240200241186a2d00004104470d00200141ff017141014d0d030240417f4100280284a4c680002202410247200241024b1b2202417f460d00200241ff01710d010b4100280290a1c680002102410028028ca1c6800021044100280280a4c680002101200341c4006a4200370200200341c0006a41e4e7c280003602002003413c6a4101360200200341346a410d360200200341306a41c6e8c28000360200200341246a41e4e7c28000ad4280808080a00c84370200200341186a41d3e8c28000ad4280808080900384370200200341dce7c28000360238200341003602202003410036021420034281808080b01a37020c2003410236022c200441ecf2c08000200141024622011b2003410c6a200241d4f2c0800020011b280210118480808000000b2000418080808078360210200041003b01000c010b20002001200210eb878080000b200341d0006a2480808080000f0b200228020810b087808000000bb80203027f017e017f23808080800041c0006b2201248080808000200141286a41ffa6c48000411141fe8bc48000411641e8d1c38000410010d882808000200141086a2202410036020020014280808080c000370300200129022c21032001280228210420014284808080c00037021020014280808080c000370218200141346a200141106a10fb8680800002402004418080808078470d0041a8d8c480004111419cd9c4800010a181808000000b200042808080808001370244200041cc006a41003602002001411b6a200141346a41086a2802003600002000413c6a200337020020002004360238200041013a000020002001290300370350200041d8006a20022802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141c0006a2480808080000bc20301037f23808080800041106b220224808080800002402001280200220328020020032802082204470d0020032004410110ab86808000200328020821040b200328020420046a41fb003a00002003200441016a3602082002200136020c20024180023b0108024002400240200241086a4190a7c480004106200041c0006a10da8680800022030d00024020022d0008450d0041002d00fca3c680001a411441002802c8a3c68000118180808000002203450d022003420037020c2003410d3602000c010b200241086a4196a7c480004104200010db8680800022030d00024020022d0008450d0041002d00fca3c680001a411441002802c8a3c68000118180808000002203450d032003420037020c2003410d3602000c010b200241086a419aa7c48000410d200041286a10dc8680800022030d00024020022d0008450d0010868380800021030c010b200241086a41a7a7c480004108200041346a10d58680800022030d0002402002280208220341ff01710d0020034180fe0371450d00200228020c28020041b8d0c3800041011083878080000b410021030b200241106a24808080800020030f0b4104411410b280808000000b4104411410b280808000000bbf010002400240024002400240024002402002417c6a0e0a00050205030505050501050b200128000041e2c289ab06470d04200041013a00010c030b2001419aa7c48000410d10888e8080000d03200041023a00010c020b20014190a7c48000410610888e808000450d030c020b200129000042e2c2b18be6edd8b2f300520d01200041033a00010b200041003a00000f0b20002001200241b0a7c48000410410e58a808000360204200041013a00000f0b200041003a0001200041003a00000bb20602037f047e2380808080004180026b22022480808080000240024002400240024002400240200128020022032802042204450d0020032004417f6a36020420032003280200220441016a36020020042d00000e0401020305040b200041003602000c050b200241e0006a200110a68a8080000240024020022d0060450d00200241b0016a41206a200241e0006a41206a2903002205370300200241b0016a41186a200241e0006a41186a2903002206370300200241b0016a41106a200241e0006a41106a2903002207370300200241b0016a41086a200241e0006a41086a2903002208370300200241086a410c6a2008370200200241086a41146a2007370200200241086a411c6a2006370200200241086a41246a20053702002002200229036022053703b0012002200537020c200020022902083702042000410c6a200241086a41086a290200370200200041146a200241086a41106a2902003702002000411c6a200241086a41186a290200370200200041246a200241086a41206a2902003702002000412c6a200241306a280200360200410d21030c010b410021030b200020033602000c040b200241e0006a200110ba87808000024020022d00704105460d00200241b0016a41106a200241e0006a41106a2903002205370300200241b0016a41086a200241e0006a41086a2903002206370300200241086a410c6a2006370200200241086a41146a20053702002002200229036022053703b0012002200537020c200020022902083702042000410c6a200241086a41086a290200370200200041146a200241086a41106a2902003702002000411c6a200241206a2802003602002000410e3602000c040b200041003602000c030b200241086a200110c08880800002402002280208450d002000200241086a41d80010848e8080001a0c030b200041003602000c020b200041003602000c010b200241e0006a200110a58c808000024020022d0060450d00200241b0016a200241e0006a41d00010848e8080001a200241086a41046a200241b0016a41d00010848e8080001a200041046a200241086a41d40010848e8080001a200041103602000c010b200041003602000b20024180026a2480808080000b2100200128021441d895c48000410f200141186a28020028020c118280808000000be702020c7f017e23808080800041106b220224808080800020002802082203415c6a21042000280204210520002d0001210620002d0000210703400240024002400240024020070e03010200010b20054110200541104b1b21082003200541246c22096a2100200420096a2109200541087441807e6a210a034020082005460d03200941246a2109200a4180026a210a200541016a210520002d0000210b200041246a2100200b4102460d000b200a4180fe0371410172210c2009210d0c030b200c41807e71410272210c0c020b200c41807e71210c0240200641ff01710d00410121062005210d0c020b200c410272210c0c010b200c41807e71410272210c200821050b0240200c41ff01714102460d00200d2d0000450d01200d280204210020022001360204200cad220e42ff01834202510d0120022000ad422086200e42ffff038384370308200241046a200241086a10bd8c8080000c010b0b200241106a2480808080000be702020c7f017e23808080800041106b220224808080800020002802082203415c6a21042000280204210520002d0001210620002d0000210703400240024002400240024020070e03010200010b20054110200541104b1b21082003200541246c22096a2100200420096a2109200541087441807e6a210a034020082005460d03200941246a2109200a4180026a210a200541016a210520002d0000210b200041246a2100200b4102460d000b200a4180fe0371410172210c2009210d0c030b200c41807e71410272210c0c020b200c41807e71210c0240200641ff01710d00410121062005210d0c020b200c410272210c0c010b200c41807e71410272210c200821050b0240200c41ff01714102460d00200d2d0000450d01200d280204210020022001360204200cad220e42ff01834202510d0120022000ad422086200e42ffff038384370308200241046a200241086a10bc8c8080000c010b0b200241106a2480808080000bcc0101067f23808080800041206b2202248080808000200141086a280200220320012802042204200320044b1b210520012802002103200128020c2106024002400240034020052004460d022001200441016a2204360204200241086a200310bc8a80800020022802080d01200241146a2003200228020c10d08580800020022802142207418080808078460d012007418180808078460d000b20002002290218370204200020073602000c020b200641013a00000b20004180808080783602000b200241206a2480808080000bef0601107f2380808080004190016b220124808080800002402000280200220220002802042203460d0020002802102104200028020822054110200541104b1b2106200028020c200541246c6a2107200141ec006a41086a2108200141ec006a41017221090240034020002002410c6a220a3602004102210b024020022802004102460d00200141ec006a200210838a8080004102210b200141c4006a41026a200941026a2d00003a0000200141286a41086a2202200841086a290200370300200141286a41106a220c200841106a290200370300200141286a41186a220d200841186a280200360200200120092f00003b0144200120082902003703282001280270210e024020012d006c220f417e6a0e020301000b200141246a41026a200141c4006a41026a2d00003a0000200141086a41086a2002290300370300200141086a41106a200c290300370300200141086a41186a200d280200360200200120012f01443b012420012001290328370308200f210b0b200141e8006a41026a2202200141246a41026a2d00003a0000200141c8006a41086a220c200141086a41086a290300370300200141c8006a41106a220d200141086a41106a290300370300200141c8006a41186a220f200141086a41186a280200360200200120012f01243b01682001200129030837034802400240024020062005460d0020072d00000e03020102010b2006411041fcc8c4800010f980808000000b200741046a280200221010848a808000201041002802c0a3c68000118080808000000b2007200b3a0000200741016a20012f01683b0000200741046a200e360200200741086a2001290348370200200741036a20022d00003a0000200741106a200c290300370200200741186a200d290300370200200741206a200f2802003602002000200541016a2205360208200741246a2107200a2102200a2003470d000c020b0b024020042802002207450d000240024002400240200728020041fcffffff076a2202410320024105491b0e0403030102000b2007280204450d02200741086a28020041002802c0a3c68000118080808000000c020b2007280204450d01200741086a28020041002802c0a3c68000118080808000000c010b200710858a8080000b200741002802c0a3c68000118080808000000b2004200e3602002000200541016a3602080b20014190016a2480808080000baa0801057f23808080800041800e6b22022480808080000240024002400240024002400240024002400240024002400240024020012802000d0020012802042103200128020822040d01410121010c020b200241e0016a200128020422042001280208220110c28c80800020022802e00122034105470d05200241046a410c6a200241e0016a410c6a290200370200200220022902e4013702080c060b20044120460d012004417f4c0d0741002d00fca3c680001a200441002802c8a3c68000118180808000002201450d080b20012003200410848e808000210341002d00fca3c680001a413041002802c8a3c680001181808080000022010d014104413010b280808000000b200020032f00003b00012000200328000336020420002003290007370008200041036a200341026a2d00003a0000200041106a2003410f6a290000370000200041186a200341176a290000370000200041206a2003411f6a2d00003a0000410021010c010b200142003702102001200436020c2001200336020820012004360204200141888080807836020020002001360204200141186a4200370200200141206a4200370200200141286a4200370200410221010b200020013a00000c070b200241f00d6a41086a2205200241e0016a410c6a290200370300200220022902e4013703f00d200241940c6a41146a200241e0016a41146a41c80110848e8080001a200241940c6a410c6a2005290300370200200220033602940c200220022903f00d3702980c200241046a200241940c6a2004200110f48d80800020022802044105470d010b200241e0016a41186a22044200370300200241e0016a41206a22034200370300200241e0016a41286a22054200370300200241e0016a41086a2206200241106a290200370300200220022902083703e001200242003703f00141002d00fca3c680001a413041002802c8a3c68000118180808000002201450d03200120022903e001370200200141286a2005290300370200200141206a2003290300370200200141186a2004290300370200200141106a200241e0016a41106a290300370200200141086a2006290300370200200041023a0000200020013602040c050b200241940c6a200241046a41dc0110848e8080001a200241e0016a200241940c6a10b68a808000024020022802e00122044108460d0020022802e401210320024180076a200241e0016a41086a41940510848e8080001a41002d00fca3c680001a419c0541002802c8a3c68000118180808000002201450d042001200336020420012004360200200141086a20024180076a41940510848e8080001a20002001360204200041013a00000c050b20022802e4012101200041023a0000200020013602040c040b10ae80808000000b4101200410b280808000000b4104413010b280808000000b4104419c0510b280808000000b200241800e6a2480808080000be11101027f0240024002400240024002402000280200417e6a2201410420014106491b0e050501020304000b2000280204220120012802002201417f6a36020020014101470d04200041046a10e28a8080000c040b02402000412c6a2802004129490d00200028020441002802c0a3c68000118080808000000b20002802342201450d03200120012802002202417f6a36020020024101470d03200041346a10e28a8080000f0b0240200041d0006a2802004129490d00200028022841002802c0a3c68000118080808000000b20002d0004450d02200041086a280200220010848a808000200041002802c0a3c68000118080808000000f0b024020002d003022014102460d002001450d00200041346a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041d4006a2d000022014102460d002001450d00200041d8006a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041f8006a2d000022014102460d002001450d00200041fc006a280200220110848a808000200141002802c0a3c68000118080808000000b02402000419c016a2d000022014102460d002001450d00200041a0016a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041c0016a2d000022014102460d002001450d00200041c4016a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041e4016a2d000022014102460d002001450d00200041e8016a280200220110848a808000200141002802c0a3c68000118080808000000b024020004188026a2d000022014102460d002001450d002000418c026a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041ac026a2d000022014102460d002001450d00200041b0026a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041d0026a2d000022014102460d002001450d00200041d4026a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041f4026a2d000022014102460d002001450d00200041f8026a280200220110848a808000200141002802c0a3c68000118080808000000b024020004198036a2d000022014102460d002001450d002000419c036a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041bc036a2d000022014102460d002001450d00200041c0036a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041e0036a2d000022014102460d002001450d00200041e4036a280200220110848a808000200141002802c0a3c68000118080808000000b024020004184046a2d000022014102460d002001450d0020004188046a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041a8046a2d000022014102460d002001450d00200041ac046a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041cc046a2d000022014102460d002001450d00200041d0046a280200220110848a808000200141002802c0a3c68000118080808000000b2000280204450d01200041086a22012802002200450d01200020002802002202417f6a36020020024101470d01200110e28a8080000f0b024020004194056a2802004129490d0020002802ec0441002802c0a3c68000118080808000000b024020002d002c22014102460d002001450d00200041306a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041d0006a2d000022014102460d002001450d00200041d4006a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041f4006a2d000022014102460d002001450d00200041f8006a280200220110848a808000200141002802c0a3c68000118080808000000b024020004198016a2d000022014102460d002001450d002000419c016a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041bc016a2d000022014102460d002001450d00200041c0016a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041e0016a2d000022014102460d002001450d00200041e4016a280200220110848a808000200141002802c0a3c68000118080808000000b024020004184026a2d000022014102460d002001450d0020004188026a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041a8026a2d000022014102460d002001450d00200041ac026a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041cc026a2d000022014102460d002001450d00200041d0026a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041f0026a2d000022014102460d002001450d00200041f4026a280200220110848a808000200141002802c0a3c68000118080808000000b024020004194036a2d000022014102460d002001450d0020004198036a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041b8036a2d000022014102460d002001450d00200041bc036a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041dc036a2d000022014102460d002001450d00200041e0036a280200220110848a808000200141002802c0a3c68000118080808000000b024020004180046a2d000022014102460d002001450d0020004184046a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041a4046a2d000022014102460d002001450d00200041a8046a280200220110848a808000200141002802c0a3c68000118080808000000b0240200041c8046a2d000022014102460d002001450d00200041cc046a280200220110848a808000200141002802c0a3c68000118080808000000b2000280200450d0020002802042201450d00200120012802002202417f6a36020020024101470d00200041046a10e28a8080000f0b0bc50101027f024002400240024020002802002201418080808078732202410220024104491b0e03030301000b0240024002402000280204220028020041fcffffff076a2202410320024105491b0e0404040102000b2000280204450d03200041086a28020041002802c0a3c68000118080808000000c030b2000280204450d02200041086a28020041002802c0a3c68000118080808000000c020b200010858a8080000c010b2001450d01200028020421000b200041002802c0a3c68000118080808000000b0bef0601107f2380808080004190016b220124808080800002402000280200220220002802042203460d0020002802102104200028020822054110200541104b1b2106200028020c200541246c6a2107200141ec006a41086a2108200141ec006a41017221090240034020002002410c6a220a3602004102210b024020022802004102460d00200141ec006a200210878a8080004102210b200141c4006a41026a200941026a2d00003a0000200141286a41086a2202200841086a290200370300200141286a41106a220c200841106a290200370300200141286a41186a220d200841186a280200360200200120092f00003b0144200120082902003703282001280270210e024020012d006c220f417e6a0e020301000b200141246a41026a200141c4006a41026a2d00003a0000200141086a41086a2002290300370300200141086a41106a200c290300370300200141086a41186a200d280200360200200120012f01443b012420012001290328370308200f210b0b200141e8006a41026a2202200141246a41026a2d00003a0000200141c8006a41086a220c200141086a41086a290300370300200141c8006a41106a220d200141086a41106a290300370300200141c8006a41186a220f200141086a41186a280200360200200120012f01243b01682001200129030837034802400240024020062005460d0020072d00000e03020102010b2006411041ecc8c4800010f980808000000b200741046a280200221010848a808000201041002802c0a3c68000118080808000000b2007200b3a0000200741016a20012f01683b0000200741046a200e360200200741086a2001290348370200200741036a20022d00003a0000200741106a200c290300370200200741186a200d290300370200200741206a200f2802003602002000200541016a2205360208200741246a2107200a2102200a2003470d000c020b0b024020042802002207450d000240024002400240200728020041fcffffff076a2202410320024105491b0e0403030102000b2007280204450d02200741086a28020041002802c0a3c68000118080808000000c020b2007280204450d01200741086a28020041002802c0a3c68000118080808000000c010b200710858a8080000b200741002802c0a3c68000118080808000000b2004200e3602002000200541016a3602080b20014190016a2480808080000baa0801057f23808080800041800e6b22022480808080000240024002400240024002400240024002400240024002400240024020012802000d0020012802042103200128020822040d01410121010c020b200241e0016a200128020422042001280208220110c28c80800020022802e00122034105470d05200241046a410c6a200241e0016a410c6a290200370200200220022902e4013702080c060b20044120460d012004417f4c0d0741002d00fca3c680001a200441002802c8a3c68000118180808000002201450d080b20012003200410848e808000210341002d00fca3c680001a413041002802c8a3c680001181808080000022010d014104413010b280808000000b200020032f00003b00012000200328000336020420002003290007370008200041036a200341026a2d00003a0000200041106a2003410f6a290000370000200041186a200341176a290000370000200041206a2003411f6a2d00003a0000410021010c010b200142003702102001200436020c2001200336020820012004360204200141888080807836020020002001360204200141186a4200370200200141206a4200370200200141286a4200370200410221010b200020013a00000c070b200241f00d6a41086a2205200241e0016a410c6a290200370300200220022902e4013703f00d200241940c6a41146a200241e0016a41146a41c80110848e8080001a200241940c6a410c6a2005290300370200200220033602940c200220022903f00d3702980c200241046a200241940c6a2004200110f48d80800020022802044105470d010b200241e0016a41186a22044200370300200241e0016a41206a22034200370300200241e0016a41286a22054200370300200241e0016a41086a2206200241106a290200370300200220022902083703e001200242003703f00141002d00fca3c680001a413041002802c8a3c68000118180808000002201450d03200120022903e001370200200141286a2005290300370200200141206a2003290300370200200141186a2004290300370200200141106a200241e0016a41106a290300370200200141086a2006290300370200200041023a0000200020013602040c050b200241940c6a200241046a41dc0110848e8080001a200241e0016a200241940c6a10b78a808000024020022802e00122044108460d0020022802e401210320024180076a200241e0016a41086a41940510848e8080001a41002d00fca3c680001a419c0541002802c8a3c68000118180808000002201450d042001200336020420012004360200200141086a20024180076a41940510848e8080001a20002001360204200041013a00000c050b20022802e4012101200041023a0000200020013602040c040b10ae80808000000b4101200410b280808000000b4104413010b280808000000b4104419c0510b280808000000b200241800e6a2480808080000bef0601107f2380808080004190016b220124808080800002402000280200220220002802042203460d0020002802102104200028020822054110200541104b1b2106200028020c200541246c6a2107200141ec006a41086a2108200141ec006a41017221090240034020002002410c6a220a3602004102210b024020022802004102460d00200141ec006a200210878a8080004102210b200141c4006a41026a200941026a2d00003a0000200141286a41086a2202200841086a290200370300200141286a41106a220c200841106a290200370300200141286a41186a220d200841186a280200360200200120092f00003b0144200120082902003703282001280270210e024020012d006c220f417e6a0e020301000b200141246a41026a200141c4006a41026a2d00003a0000200141086a41086a2002290300370300200141086a41106a200c290300370300200141086a41186a200d280200360200200120012f01443b012420012001290328370308200f210b0b200141e8006a41026a2202200141246a41026a2d00003a0000200141c8006a41086a220c200141086a41086a290300370300200141c8006a41106a220d200141086a41106a290300370300200141c8006a41186a220f200141086a41186a280200360200200120012f01243b01682001200129030837034802400240024020062005460d0020072d00000e03020102010b2006411041fcc8c4800010f980808000000b200741046a280200221010848a808000201041002802c0a3c68000118080808000000b2007200b3a0000200741016a20012f01683b0000200741046a200e360200200741086a2001290348370200200741036a20022d00003a0000200741106a200c290300370200200741186a200d290300370200200741206a200f2802003602002000200541016a2205360208200741246a2107200a2102200a2003470d000c020b0b024020042802002207450d000240024002400240200728020041fcffffff076a2202410320024105491b0e0403030102000b2007280204450d02200741086a28020041002802c0a3c68000118080808000000c020b2007280204450d01200741086a28020041002802c0a3c68000118080808000000c010b200710858a8080000b200741002802c0a3c68000118080808000000b2004200e3602002000200541016a3602080b20014190016a2480808080000bef0601107f2380808080004190016b220124808080800002402000280200220220002802042203460d0020002802102104200028020822054110200541104b1b2106200028020c200541246c6a2107200141ec006a41086a2108200141ec006a41017221090240034020002002410c6a220a3602004102210b024020022802004102460d00200141ec006a200210838a8080004102210b200141c4006a41026a200941026a2d00003a0000200141286a41086a2202200841086a290200370300200141286a41106a220c200841106a290200370300200141286a41186a220d200841186a280200360200200120092f00003b0144200120082902003703282001280270210e024020012d006c220f417e6a0e020301000b200141246a41026a200141c4006a41026a2d00003a0000200141086a41086a2002290300370300200141086a41106a200c290300370300200141086a41186a200d280200360200200120012f01443b012420012001290328370308200f210b0b200141e8006a41026a2202200141246a41026a2d00003a0000200141c8006a41086a220c200141086a41086a290300370300200141c8006a41106a220d200141086a41106a290300370300200141c8006a41186a220f200141086a41186a280200360200200120012f01243b01682001200129030837034802400240024020062005460d0020072d00000e03020102010b2006411041ecc8c4800010f980808000000b200741046a280200221010848a808000201041002802c0a3c68000118080808000000b2007200b3a0000200741016a20012f01683b0000200741046a200e360200200741086a2001290348370200200741036a20022d00003a0000200741106a200c290300370200200741186a200d290300370200200741206a200f2802003602002000200541016a2205360208200741246a2107200a2102200a2003470d000c020b0b024020042802002207450d000240024002400240200728020041fcffffff076a2202410320024105491b0e0403030102000b2007280204450d02200741086a28020041002802c0a3c68000118080808000000c020b2007280204450d01200741086a28020041002802c0a3c68000118080808000000c010b200710858a8080000b200741002802c0a3c68000118080808000000b2004200e3602002000200541016a3602080b20014190016a2480808080000bab0201027f23808080800041306b22012480808080004100210202400240024020002d0000410b470d002001410c6a200041086a2802002000410c6a280200108b8a80800020012d000c0d0041002d00fca3c680001a410c41002802c8a3c68000118180808000002202450d0241002d00fca3c680001a412041002802c8a3c68000118180808000002200450d012000200129000d370000200041186a200141256a290000370000200041106a2001411d6a290000370000200041086a200141156a290000370000200241203602002002412036020820022000360204200041002802c0a3c6800011808080800000200241002802c0a3c6800011808080800000410221020b200141306a24808080800020020f0b4101412010b280808000000b4104410c10b280808000000b950301027f23808080800041e0006b22032480808080002003412b6a10ef88808000024002400240024020032d002b22044102460d002003410a6a410a6a2003412b6a410a6a2900003700002003410a6a41126a2003412b6a41126a2900003700002003410a6a41196a2003412b6a41196a2900003700002003200329002d37000c200320032d002c3a000b2003412b6a200120024100280298a3c680001185808080000002402003412b6a2003410b6a412010888e808000450d00200041046a410910dc888080000c030b2004450d01200341cc006a2001200210978a80800020032d004c2202410e460d01200041056a200329004d370000200041146a200341dc006a2800003600002000410d6a200341d5006a290000370000200020023a00040c020b200041046a410810dc88808000200041013a00000c020b2000200329002b370001200041003a0000200041196a200341c3006a290000370000200041116a2003413b6a290000370000200041096a200341336a2900003700000c010b200041013a00000b200341e0006a2480808080000b980601027f23808080800041306b2201248080808000200141106a41186a42c58a95aad4a8d1a2c500370300200141106a41106a42c58a95aad4a8d1a2c500370300200141106a41086a42c58a95aad4a8d1a2c500370300200142c58a95aad4a8d1a2c5003703104200200141106a10fd87808000200142fc90b9e5d09296e777370318200142a6d4e6f1a4dd959860370310200142bb88f596f8cbf6cf4c3703282001428a85cd9fb3e4b2ae6d37032041002d00fca3c680001a02400240412041002802c8a3c68000118180808000002202450d00200242c58a95aad4a8d1a2c500370000200241186a42c58a95aad4a8d1a2c500370000200241106a42c58a95aad4a8d1a2c500370000200241086a42c58a95aad4a8d1a2c500370000200141106a41202002412041002802e0a1c6800011868080800000200241002802c0a3c6800011808080800000200142003702082001418097c380003602042001418080808078360200200142fc90b9e5d09296e777370318200142a6d4e6f1a4dd959860370310200142a0b9ab8f9ae5999778370328200142f999a7c78cd1d1cdb17f3703202001200141106a4120109288808000024020012802002202418080808078460d002002450d00200128020441002802c0a3c68000118080808000000b200142fc90b9e5d09296e777370318200142a6d4e6f1a4dd959860370310200142a2f5bea594dedcdb10370328200142d6888295b2b493ecbf7f370320200141013a0000200141106a41202001410141002802e0a1c6800011868080800000200142fc90b9e5d09296e777370318200142a6d4e6f1a4dd959860370310200142d2daa4a6928283fa39370328200142a7fbb3c3b2f09acd28370320200141013a0000200141106a41202001410141002802e0a1c680001186808080000041002d00fca3c680001a410441002802c8a3c68000118180808000002202450d012002410036000041e6aac4800041102002410441002802e0a1c6800011868080800000200241002802c0a3c6800011808080800000200141306a2480808080000f0b4101412010b280808000000b4101410410b280808000000b9d0202017f037e23808080800041f0006b2201248080808000024020002903002202500d0020014298d5afd2c6aeacae2f370318200142c2cdc8b0c7b9e78f857f370310200142e4c5bdb4b2e9a5a6807f370328200142d790d7a3fef9fda0c8003703202001200141106a10e688808000200129030821032001280200210020014298d5afd2c6aeacae2f370318200142c2cdc8b0c7b9e78f857f370310200142e4c5bdb4b2e9a5a6807f370328200142d790d7a3fef9fda0c800370320200142002003420020001b220320027d220420042003561b370368200141106a4120200141e8006a410841002802e0a1c6800011868080800000200141103a001020012002370318200141106a108e8a8080000b200141f0006a2480808080000baa0401047f23808080800041c0016b2201248080808000200142fc90b9e5d09296e777370328200142a6d4e6f1a4dd959860370320200142d3d8c5d2a9b9d2c1ac7f37033820014282ca868eabf3add0cf00370330200141106a200141206a10e68880800002402001280210450d002001290318500d00200141086a10ec88808000200128020c210220012802082103200142fc90b9e5d09296e777370328200142a6d4e6f1a4dd959860370320200142b7aeb183f6f8ab9cd0003703382001428ab0f6f7cbd3f9e2d8003703302001200141206a412010d0888080002001280204410020012802001b41016a2204450d00200142fc90b9e5d09296e777370328200142a6d4e6f1a4dd959860370320200142b7aeb183f6f8ab9cd0003703382001428ab0f6f7cbd3f9e2d8003703302001200436029001200141206a412020014190016a410441002802e0a1c68000118680808000002001200236022420014102200320034103461b360220200141286a200041d00010848e8080001a20014100360280012001428080808010370378200142fc90b9e5d09296e77737039801200142a6d4e6f1a4dd95986037039001200142bc8986ab88c2dce4573703a80120014280a9fbf0e5a2c1b3e5003703a001200141b4016a200141206a10968880800020014190016a4120200141b4016a4100280298a1c68000118580808000002001280278450d00200128027c41002802c0a3c68000118080808000000b200141c0016a2480808080000b9d0202017f037e23808080800041f0006b2201248080808000024020002903002202500d0020014298d5afd2c6aeacae2f370318200142c2cdc8b0c7b9e78f857f370310200142e4c5bdb4b2e9a5a6807f370328200142d790d7a3fef9fda0c8003703202001200141106a10e688808000200129030821032001280200210020014298d5afd2c6aeacae2f370318200142c2cdc8b0c7b9e78f857f370310200142e4c5bdb4b2e9a5a6807f370328200142d790d7a3fef9fda0c8003703202001427f2003420020001b220320027c220420042003541b370368200141106a4120200141e8006a410841002802e0a1c68000118680808000002001410f3a001020012002370318200141106a108e8a8080000b200141f0006a2480808080000bda0e01097f23808080800041c0006b220624808080800002400240024002400240200541057441286a412820041b2204417f4c0d004100210741002d00fca3c680001a200441002802c8a3c68000118180808000002205450d012006410036020c200620053602082006200436020402402004411f4b0d00200641046a4100412010ab868080002006280204210420062802082105200628020c21070b200520076a22082001290000370000200841186a200141186a290000370000200841106a200141106a290000370000200841086a200141086a2900003700002006200741206a220136020c0240200420016b41074b0d00200641046a2001410810ab8680800020062802082105200628020c21010b200520016a20023700002006200141086a220936020c2003280228210a2003280224210b2003280220210c2003280218210520032802102104200328020c2101200328021c210d2003280214210802402003280200450d002003280208210e2003280204210303402003200e2003200e4b1b210702400240034002402001450d00024020042005460d00200641106a41186a200441186a290000370300200641106a41106a200441106a290000370300200641106a41086a200441086a29000037030020062004290000370310200441206a21040c040b2008450d00200141002802c0a3c68000118080808000000b024020072003470d00200721030c020b2006200336023c200641306a2006413c6a10fc87808000024020062802302201418080808078460d00200341016a21032006280234220420062802384105746a210520012108200421010c010b0b200341016a21030b200d450d07200c200a460d06200641106a41186a200c41186a290000370300200641106a41106a200c41106a290000370300200641106a41086a200c41086a2900003703002006200c290000370310200c41206a210c410021010b0240200628020420096b411f4b0d00200641046a2009412010ab86808000200628020c21090b200628020820096a22072006290310370000200741086a200641106a41086a290300370000200741106a200641106a41106a290300370000200741186a200641106a41186a2903003700002006200941206a220936020c0c000b0b0240200d0d002001450d05024020042005460d000340200641106a41186a2207200441186a290000370300200641106a41106a220c200441106a290000370300200641106a41086a220e200441086a290000370300200620042900003703100240200628020420096b41204f0d00200641046a2009412010ab86808000200628020c21090b200628020820096a22032006290310370000200341086a200e290300370000200341106a200c290300370000200341186a20072903003700002006200941206a220936020c200441206a22042005470d000b0b2008450d05200141002802c0a3c68000118080808000000c050b2008450d0203400240024002402001450d0020042005470d01200141002802c0a3c68000118080808000000b200c200a460d06200641106a41186a200c41186a290000370300200641106a41106a200c41106a290000370300200641106a41086a200c41086a2900003703002006200c290000370310200c41206a210c410021010c010b200641106a41186a200441186a290000370300200641106a41106a200441106a290000370300200641106a41086a200441086a29000037030020062004290000370310200441206a21040b0240200628020420096b411f4b0d00200641046a2009412010ab86808000200628020c21090b200628020820096a22032006290310370000200341086a200641106a41086a290300370000200341106a200641106a41106a290300370000200341186a200641106a41186a2903003700002006200941206a220936020c0c000b0b10ae80808000000b4101200410b280808000000b0240024020010d00410121030c010b410021030b03400240024002400240024020030e020001010b20042005470d010c030b200c200a460d04200641106a41186a200c41186a290000370300200641106a41106a200c41106a290000370300200641106a41086a200c41086a2900003703002006200c290000370310200c41206a210c410021010c010b200641106a41186a200441186a290000370300200641106a41106a200441106a290000370300200641106a41086a200441086a29000037030020062004290000370310200441206a21040b0240200628020420096b411f4b0d00200641046a2009412010ab86808000200628020c21090b200628020820096a22032006290310370000200341086a200641106a41086a290300370000200341106a200641106a41106a290300370000200341186a200641106a41186a2903003700002006200941206a220936020c2001450d00410021030c010b410121030c000b0b200b450d00200d41002802c0a3c68000118080808000000b20002006280208220320094100280298a3c680001185808080000002402006280204450d00200341002802c0a3c68000118080808000000b200641c0006a2480808080000b840a01047f23808080800041e0006b220124808080800002400240417f4100280284a4c680002202410447200241044b1b2202417f460d00200241ff01710d010b200141e08180800036021020014106360218200141f9a6c480003602142001200141146a36020c4100280290a1c680002102410028028ca1c6800021034100280280a4c680002104200141d4006a4201370200200141cc006a4101360200200141c4006a4116360200200141c0006a41bcaac48000360200200141346a4195a8c48000ad4280808080b00b84370200200141286a41d2aac48000ad4280808080c00284370200200141d0006a2001410c6a360200200141b4aac480003602482001410436023c200141003602302001410036022420014281808080c02037021c200341ecf2c08000200441024622041b2001411c6a200241d4f2c0800020041b280210118480808000000b02400240417f4100280284a4c680002202410447200241044b1b2202417f460d00200241ff01710d010b200141e08180800036021020014104360218200141f5a6c480003602142001200141146a36020c4100280290a1c680002102410028028ca1c6800021034100280280a4c680002104200141d4006a4201370200200141cc006a4101360200200141c4006a4116360200200141c0006a41c1e5c28000360200200141346a41e8e4c28000ad4280808080900b84370200200141286a41d7e5c28000ad4280808080b00284370200200141d0006a2001410c6a360200200141e0e4c280003602482001410436023c200141003602302001410036022420014281808080800e37021c200341ecf2c08000200441024622041b2001411c6a200241d4f2c0800020041b280210118480808000000b02400240417f4100280284a4c680002202410447200241044b1b2202417f460d00200241ff01710d010b200141e0818080003602102001410d360218200141e8a6c480003602142001200141146a36020c4100280290a1c680002102410028028ca1c6800021034100280280a4c680002104200141d4006a4201370200200141cc006a4101360200200141c4006a4116360200200141c0006a418cabc38000360200200141346a41c0a7c38000ad4280808080b00e84370200200141286a41b9a9c38000ad4280808080d00684370200200141d0006a2001410c6a360200200141b8abc380003602482001410436023c200141003602302001410036022420014281808080e00437021c200341ecf2c08000200441024622041b2001411c6a200241d4f2c0800020041b280210118480808000000b02400240417f4100280284a4c680002202410447200241044b1b2202417f460d00200241ff01710d010b200141e08180800036021020014108360218200141e0a6c480003602142001200141146a36020c4100280290a1c680002102410028028ca1c6800021034100280280a4c680002104200141d4006a4201370200200141cc006a4101360200200141c4006a4116360200200141c0006a41d4d8c58000360200200141346a41b8d6c58000ad4280808080d00b84370200200141286a41ead8c58000ad4280808080f00284370200200141d0006a2001410c6a360200200141ccd8c580003602482001410436023c200141003602302001410036022420014281808080a01837021c200341ecf2c08000200441024622041b2001411c6a200241d4f2c0800020041b280210118480808000000b2000420037030820004200370300200141e0006a2480808080000bcd0401077f23808080800041c0006b2200248080808000200041206a41f9a6c48000410641002802a0a3c6800011858080800000200041206a41106a220141bccdc48000411541002802a0a3c6800011858080800000200041186a2202200041206a41186a2203290000370300200041106a22042001290000370300200041086a2205200041206a41086a220629000037030020002000290020370300200041003b012020004120200041206a410241002802e0a1c6800011868080800000200041206a41f5a6c48000410441002802a0a3c6800011858080800000200141bccdc48000411541002802a0a3c680001185808080000020022003290000370300200420012900003703002005200629000037030020002000290020370300200041003b012020004120200041206a410241002802e0a1c6800011868080800000200041206a41e8a6c48000410d41002802a0a3c6800011858080800000200141bccdc48000411541002802a0a3c680001185808080000020022003290000370300200420012900003703002005200629000037030020002000290020370300200041003b012020004120200041206a410241002802e0a1c6800011868080800000200041206a41e0a6c48000410841002802a0a3c6800011858080800000200141bccdc48000411541002802a0a3c680001185808080000020022003290000370300200420012900003703002005200629000037030020002000290020370300200041013b012020004120200041206a410241002802e0a1c6800011868080800000200041c0006a2480808080000b8e1008017f087e017f037e017f027e037f027e23808080800041e0016b2204248080808000200441086a200110fa8780800042002105200441186a290300210620032903002107200441306a2903002108200441286a29030021092004290310210a02400240200441206a290300220b4200520d0020094200520d0020084200520d00200a2006428080808080808080807f858450450d00428080808080808080807f210c4100210d4201210e4200210f42002110410021110c010b200b42ffffe883b1de1656210d4200210e410121110240024020094200510d00200a21052006210c2009210f0c010b4200210e2008420052211142002109200a21052006210c4200210f0b200821100b0240200b200f2007200f2007541b22127c2213200b5a0d0002400240417f4100280284a4c680002203410147200341014b1b2203450d00200341ff017141ff01470d010b200441e081808000360274200441b4f9c580003602704100280290a1c680002103410028028ca1c6800021144100280280a4c680002115200441d0016a4201370200200441c8016a4101360200200441c0016a4112360200200441bc016a41bcf9c58000360200200441b0016a41cef9c58000ad4280808080c00c84370200200441a4016a41b2fac58000ad4280808080b00384370200200441cc016a200441f0006a360200200441b4f8c580003602c401200441013602b801200441003602ac01200441003602a00120044281808080b01b37029801201441ecf2c08000201541024622151b20044198016a200341d4f2c0800020151b280210118480808000000b427f200b20127c22132013200b541b21130b200f2007582010507121030240200d417f73201342ffffe883b1de165671450d00200220021084888080001a0b02400240024002400240024002400240201141004722142003710d0002402014200372450d0020030d040c020b20044198016a200210f68780800020042d0098012214410e460d01200435019a012004419e016a33010042208684210b0c020b20021083888080000c020b20044198016a200210fa8780800020042802c8010d0102400240417f4100280284a4c680002214410147201441014b1b2214450d00201441ff017141ff01470d010b4100280290a1c680002114410028028ca1c6800021154100280280a4c680002116200441d0016a4200370200200441cc016a419cadc48000360200200441c8016a4101360200200441c0016a4111360200200441bc016a41c1c7c48000360200200441b0016a41e4c6c48000ad4280808080d00b84370200200441a4016a41d2c7c48000ad4280808080f00284370200200441dcc6c480003602c401200441013602b801200441003602ac01200441003602a00120044281808080d0800137029801201541ecf2c08000201641024622161b20044198016a201441d4f2c0800020161b280210118480808000000b20044198016a200210f68780800020042d0098012214410e460d01200435019a012004419e016a33010042208684210b0b20042802a801210220042902a001211320042d00990121010c010b200d2013428080e983b1de165471450d01200441c8006a2002200210f78780800020042d00482214410e460d01024002402011450d002003450d01200441dc006a200210f68780800020042d005c410e460d01200441f0006a41106a200441dc006a41106a280200360200200441f0006a41086a200441dc006a41086a2902003703002004200429025c3703700240417f4100280284a4c680002202410147200241014b1b2202450d00200241ff017141ff01470d020b20044188016a410c6a41ca83808000360200200441e08180800036028c012004418c98c38000360288012004200441f0006a360290014100280290a1c680002102410028028ca1c6800021014100280280a4c680002103200441d0016a4202370200200441c8016a4102360200200441c0016a4112360200200441bc016a419498c38000360200200441b0016a41a698c38000ad4280808080c00c8437020020044198016a410c6a418a99c38000ad4280808080b00384370200200441cc016a20044188016a3602002004418497c380003602c401200441013602b801200441003602ac01200441003602a00120044281808080e02437029801200141ecf2c08000200341024622031b20044198016a200241d4f2c0800020031b280210118480808000000c010b20030d0020021083888080000b200435014a200441ce006a33010042208684210b200428025821022004290250211320042d004921010b20004202370300200041186a2002360200200041106a20133703002000200b4210862001ad42ff0183420886842014ad843703080c010b0240024002400240201342ffffe883b1de16560d00200f2007580d010b200f20127d21094200211742012118201342808097fccea1697c42818097fccea1695a0d012013210b0c020b420021182013420052ad211720082110200a21052006210c0c010b2013210b200f2007580d020b20044198016a200110fa878080002013422088a721022013a721030240024020042802cc010d0020044198016a200110fa8780800020042802d0010d0020011086888080000c010b200441a8016a200c370300200420053703a001200420103703c001200420093703b8012004200b3703b0012004201837039801200120044198016a1082888080000b200020123703202000200236021c2000200336021820002017370310200020133703082000200e3703000b200441e0016a2480808080000f0b41e9c7c4800041dd0041c8c8c4800010f880808000000baa0f08017f077e017f037e017f027e057f017e2380808080004190026b2203248080808000200341086a200110fa8780800042002104200341186a2903002105200341306a2903002106200341286a29030021072003290310210802400240200341206a29030022094200520d0020074200520d0020064200520d0020082005428080808080808080807f858450450d00428080808080808080807f210a4100210b4201210c4200210d4200210e4100210f0c010b200942ffffe883b1de1656210b4200210c4101210f0240024020074200510d00200821042005210a2007210d0c010b4200210c2006420052210f42002107200821042005210a4200210d0b2006210e0b0240024042002009200228020429030022107d221120112009561b200241086a280200290300580d00200341c8016a410310f888808000200921100c010b2003410e3a00c8010b200341e0006a41106a200341c8016a41106a280200360200200341e0006a41086a200341c8016a41086a290200370300200320032902c801370360200d200e845021120240200b417f73201042ffffe883b1de165671450d002002280200221320131084888080001a0b02400240024002400240024002400240200f41004722132012710d0002402013201272450d0020120d04200228020021140c020b200341c8016a2002280200221410f68780800020032d00c8012213410e460d0120033501ca01200341ce016a3301004220868421090c020b20022802001083888080000c020b200341c8016a201410fa8780800020032802f8010d0102400240417f4100280284a4c680002213410147201341014b1b2213450d00201341ff017141ff01470d010b4100280290a1c680002113410028028ca1c6800021154100280280a4c68000211620034180026a4200370200200341fc016a419cadc48000360200200341f8016a4101360200200341f0016a4111360200200341ec016a41c1c7c48000360200200341e0016a41e4c6c48000ad4280808080d00b84370200200341d4016a41d2c7c48000ad4280808080f00284370200200341dcc6c480003602f401200341013602e801200341003602dc01200341003602d00120034281808080d080013702c801201541ecf2c08000201641024622161b200341c8016a201341d4f2c0800020161b280210118480808000000b200341c8016a201410f68780800020032d00c8012213410e460d0120033501ca01200341ce016a3301004220868421090b20032802d801210220032902d001211020032d00c90121010c010b200b2010428080e983b1de165471450d01200341f8006a20022802002202200210f78780800020032d00782213410e460d0102400240200f450d002012450d012003418c016a200210f68780800020032d008c01410e460d01200341a0016a41106a2003418c016a41106a280200360200200341a0016a41086a2003418c016a41086a2902003703002003200329028c013703a0010240417f4100280284a4c680002202410147200241014b1b2202450d00200241ff017141ff01470d020b200341b8016a410c6a41ca83808000360200200341e0818080003602bc012003418c98c380003602b8012003200341a0016a3602c0014100280290a1c680002102410028028ca1c6800021014100280280a4c68000211220034180026a4202370200200341f8016a4102360200200341f0016a4112360200200341ec016a419498c38000360200200341e0016a41a698c38000ad4280808080c00c84370200200341c8016a410c6a418a99c38000ad4280808080b00384370200200341fc016a200341b8016a3602002003418497c380003602f401200341013602e801200341003602dc01200341003602d00120034281808080e0243702c801200141ecf2c08000201241024622121b200341c8016a200241d4f2c0800020121b280210118480808000000c010b20120d0020021083888080000b200335017a200341fe006a3301004220868421092003280288012102200329028001211020032d007921010b20004202370300200041186a2002360200200041106a2010370300200020094210862001ad42ff0183420886842013ad843703080c010b0240024002400240201042ffffe883b1de16560d0042002111200d4200510d010b4200211742012111201042808097fccea1697c42818097fccea1695a0d01201021090c020b2010420052ad21172006210e2007210d200821042005210a0c010b20102109200d4200510d020b200341c8006a41106a200341e0006a41106a280200360200200341c8006a41086a200341e0006a41086a29030037030020032003290360370348200341c8016a200110fa878080002010422088a721022010a721120240024020032802fc010d00200341c8016a200110fa878080002003280280020d0020011086888080000c010b200341c8016a41106a200a370300200320043703d0012003200e3703f0012003200d3703e801200320093703e001200320113703c8012001200341c8016a1082888080000b2000200236021c2000201236021820002017370310200020103703082000200c37030020002003290348370320200041286a200341d0006a290300370300200041306a200341d8006a2802003602000b20034190026a2480808080000f0b41e9c7c4800041dd0041c8c8c4800010f880808000000b9e0d06017f087e017f027e057f037e23808080800041e0016b2204248080808000200441086a200110fa8780800042002105200441186a290300210620032903002107200441306a2903002108200441286a29030021092004290310210a024002400240200441206a290300220b4200520d0020094200520d0020084200520d00200a2006428080808080808080807f858450450d00428080808080808080807f210c4100210d4200210e4200210f410021030c010b200b42ffffe883b1de1656210d024020094200510d004101211042012105410121032008210f200a210e2006210c410121110c020b2008420052210342012105200a210e2006210c2008210f0b42002109200f4200522111410021100b02402007428080e983b1de16540d00200d0d00200220021084888080001a0b024002400240024002400240024002402003450d002011450d010b0240024020030d0020110d010b2011450d040c020b20044198016a200210f68780800020042d0098012212410e460d01200435019a012004419e016a3301004220868421070c020b20021083888080000c020b20044198016a200210fa8780800020042802c8010d0102400240417f4100280284a4c680002212410147201241014b1b2212450d00201241ff017141ff01470d010b4100280290a1c680002112410028028ca1c6800021134100280280a4c680002114200441d0016a4200370200200441cc016a419cadc48000360200200441c8016a4101360200200441c0016a4111360200200441bc016a41c1c7c48000360200200441b0016a41e4c6c48000ad4280808080d00b84370200200441a4016a41d2c7c48000ad4280808080f00284370200200441dcc6c480003602c401200441013602b801200441003602ac01200441003602a00120044281808080d0800137029801201341ecf2c08000201441024622141b20044198016a201241d4f2c0800020141b280210118480808000000b20044198016a200210f68780800020042d0098012212410e460d01200435019a012004419e016a3301004220868421070b20042802a801210220042902a001210920042d00990121010c010b2007428080e983b1de1654200d71450d01200441c8006a2002200210f78780800020042d00482212410e460d01024002402003450d0020110d01200441dc006a200210f68780800020042d005c410e460d01200441f0006a41106a200441dc006a41106a280200360200200441f0006a41086a200441dc006a41086a2902003703002004200429025c3703700240417f4100280284a4c680002202410147200241014b1b2202450d00200241ff017141ff01470d020b20044188016a410c6a41ca83808000360200200441e08180800036028c012004418c98c38000360288012004200441f0006a360290014100280290a1c680002102410028028ca1c6800021014100280280a4c680002103200441d0016a4202370200200441c8016a4102360200200441c0016a4112360200200441bc016a419498c38000360200200441b0016a41a698c38000ad4280808080c00c8437020020044198016a410c6a418a99c38000ad4280808080b00384370200200441cc016a20044188016a3602002004418497c380003602c401200441013602b801200441003602ac01200441003602a00120044281808080e02437029801200141ecf2c08000200341024622031b20044198016a200241d4f2c0800020031b280210118480808000000c010b2011450d0020021083888080000b200435014a200441ce006a330100422086842107200428025821022004290250210920042d004921010b20004202370300200041186a2002360200200041106a2009370300200020074210862001ad42ff0183420886842012ad843703080c010b02400240200742ffffe883b1de1656201072450d00420021152007211642012117200742808097fccea1697c42818097fccea169542010720d0141e9c7c4800041dd0041c8c8c4800010f880808000000b420021172007420052ad21152008210f200b2116200a210e2006210c0b2005420185210820044198016a200110fa878080002007422088a721022007a721030240024020042802cc010d0020044198016a200110fa8780800020042802d0010d0020011086888080000c010b200441a8016a200c3703002004200e3703a0012004200f3703c001200420093703b801200420163703b0012004201737039801200120044198016a1082888080000b2000200b3703202000200236021c200020033602182000201537031020002007370308200020083703000b200441e0016a2480808080000ba20602017f017e23808080800041d0006b220324808080800020034102360208200342fc90b9e5d09296e777370338200342a6d4e6f1a4dd95986037033020034293bb8a9cbb9ab6b31a370348200342ffabedd185d3d8d216370340200341086a200341306a41201094888080002003410036023041e6aac480004110200341306a410441002802e0a1c680001186808080000020032001360234200341f0acc48000360230200341086a200341306a1083898080004188adc480004113200341086a412041002802e0a1c6800011868080800000200342fc90b9e5d09296e777370338200342a6d4e6f1a4dd959860370330200342d3d8c5d2a9b9d2c1ac7f37034820034282ca868eabf3add0cf00370340200320002903002204370328200341306a4120200341286a410841002802e0a1c6800011868080800000200342fc90b9e5d09296e777370338200342a6d4e6f1a4dd959860370330200342f4fa97f89782c7a62d37034820034299cfe7ffe3b8eac70837034020022802042002280208200341306a4120108a8d80800041002d00fca3c680001a200342fc90b9e5d09296e777370338200342a6d4e6f1a4dd959860370330200342bb88f596f8cbf6cf4c3703482003428a85cd9fb3e4b2ae6d3703400240412041002802c8a3c680001181808080000022020d004101412010b280808000000b20022001290000370000200241186a200141186a290000370000200241106a200141106a290000370000200241086a200141086a290000370000200341306a41202002412041002802e0a1c6800011868080800000200241002802c0a3c68000118080808000002004427f7c200110fd87808000200342fc90b9e5d09296e777370338200342a6d4e6f1a4dd959860370330200342beee8ae9ce8fa2b1977f37034820034295d4f9afa2868fb864370340200341306a412041002802a0a1c6800011848080800000200342fc90b9e5d09296e777370338200342a6d4e6f1a4dd959860370330200342f89aef8eef91e1ce967f370348200342b4d6d6dfccc6b592c300370340200341306a412041002802a0a1c6800011848080800000200341d0006a2480808080000baa04010a7f23808080800041d0006b220324808080800041002104200341046a200120024100280288a3c68000118580808000000240024020032802042202418080808078460d00200328020821012003200328020c36024c20032001360248200341106a200341c8006a4100200310df8c8080000240024020032802102205418180808078470d00410021060c010b200341176a2d000041187420032f001541087472210420032d0014210620032802382107200328022c2108200328022821092003280220210a200328021c210b2003280218210c0b02402002450d00200141002802c0a3c68000118080808000000b2005418180808078460d00200620047221014101210202400240200c0d0020070d01410221020b2000200210dc888080000240200541808080807872418080808078460d00200141002802c0a3c68000118080808000000b0240200b41808080807872418080808078460d00200a41002802c0a3c68000118080808000000b200941808080807872418080808078460d02200841002802c0a3c68000118080808000000c020b2000410e3a00000240200541808080807872418080808078460d00200141002802c0a3c68000118080808000000b0240200b41808080807872418080808078460d00200a41002802c0a3c68000118080808000000b200941808080807872418080808078460d01200841002802c0a3c68000118080808000000c010b2000410310dc888080000b200341d0006a2480808080000b800401027f23808080800041d0006b2200248080808000200042fc90b9e5d09296e777370338200042a6d4e6f1a4dd959860370330200042bc8986ab88c2dce45737034820004280a9fbf0e5a2c1b3e500370340200041306a412041002802a0a1c6800011848080800000200042fc90b9e5d09296e777370338200042a6d4e6f1a4dd959860370330200042b7aeb183f6f8ab9cd0003703482000428ab0f6f7cbd3f9e2d800370340200041306a412041002802a0a1c6800011848080800000200041306a41f9a6c48000410641002802a0a3c6800011858080800000200041306a41106a220141c99ec38000410b41002802a0a3c6800011858080800000200041106a41186a200041306a41186a290000370300200041106a41106a2001290000370300200041106a41086a200041306a41086a29000037030020002000290030370310200041086a200041106a41204101417f41002802a8a1c6800011878080800000024002402000280208450d0041002d00fca3c680001a412041002802c8a3c68000118180808000002201450d0120012000290310370000200141186a200041106a41186a290300370000200141106a200041106a41106a290300370000200141086a200041106a41086a290300370000200141002802c0a3c68000118080808000000b200041d0006a2480808080000f0b4101412010b280808000000bc10904017f017e027f037e23808080800041a0036b2202248080808000200041086a20002000290300220342025122041b210502400240200020044103746a290300500d00200541106a29030022062001290308220720062007541b2106200529030822072001290300220820072008541b21070c010b20012903082106200129030021070b200241206a10e189808000200241206a210402400240024020012d00100e03020001020b200241f8006a21040c010b200241d0016a21040b2001427f200720042903487c220820082007541b3703002001427f2006200441d0006a2903007c220720072006541b37030841012104024020012d00110d0020052d001841004721040b200120043a00110240024020034202520d0020024183036a200141106a290000370000200241fb026a200141086a290000370000200220012900003700f302410021010c010b2002200036029c0302404100280284a4c680004105470d00200242fc90b9e5d09296e777370328200242a6d4e6f1a4dd959860370320200242d3d8c5d2a9b9d2c1ac7f37033820024282ca868eabf3add0cf00370330200241106a200241206a10e688808000200241ec026a410c6a41cb83808000360200200241cc838080003602f00220022002290318420020022802101b3703d00220022002419c036a3602f4022002200241d0026a3602ec024100280290a1c680002100410028028ca1c6800021044100280280a4c680002105200241d8006a4202370200200241d0006a4102360200200241c8006a410f360200200241c4006a41ccadc48000360200200241386a4195a8c48000ad4280808080b00b84370200200241206a410c6a41dbadc48000ad4280808080c00184370200200241d4006a200241ec026a360200200241bcadc4800036024c20024105360240200241003602342002410036022820024281808080e0fd01370220200441ecf2c08000200541024622051b200241206a200041d4f2c0800020051b28021011848080800000200228029c0321000b200241ff026a200041306a280000360000200241f7026a200041286a2900003700002002418b036a200141086a29000037000020024193036a200141106a290000370000200220002900203700ef022002200129000037008303410121010b200241296a20022900ec02370000200241d8006a20022903d002370300200241316a200241ec026a41086a290000370000200241396a200241ec026a41106a290000370000200241c1006a20024184036a290000370000200241c9006a200241ec026a41206a290000370000200241d0006a20024193036a290000370000200241e0006a200241d0026a41086a290300370300200241e8006a200241d0026a41106a290300370300200220013a0028200241163a0020200241206a108e8a808000200241086a41e6aac48000411010d0888080002002200228020c41016a410120022802081b220136022041e6aac480004110200241206a410441002802e0a1c6800011868080800000200220013602f002200241003602ec02200242fc90b9e5d09296e777370328200242a6d4e6f1a4dd95986037032020024293bb8a9cbb9ab6b31a370338200242ffabedd185d3d8d216370330200241ec026a200241206a4120109488808000200241a0036a2480808080000bf10202027f017e2380808080004180016b220224808080800002400240024002400240200128021c22034110710d0020034120710d0120002903004101200110fe8080800021000c020b20002903002104410021000340200220006a41ff006a413041d7002004a7410f712203410a491b20036a3a00002000417f6a210020044210542103200442048821042003450d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000c010b20002903002104410021000340200220006a41ff006a413041372004a7410f712203410a491b20036a3a00002000417f6a210020044210542103200442048821042003450d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000b20024180016a24808080800020000f0b200341800141c88dc08000109481808000000b200341800141c88dc08000109481808000000b9a0201037f23808080800041306b2200248080808000200041e6aac48000411010d088808000200028020421010240200028020022024101470d0041e6aac48000411041002802a0a1c68000118480808000000b200042fc90b9e5d09296e777370318200042a6d4e6f1a4dd959860370310200042bad08eede089ffa812370328200042bd81f785e387e6aa817f37032020002001410020021b360208200041106a4120200041086a410441002802e0a1c680001186808080000020004101360208200042fc90b9e5d09296e777370318200042a6d4e6f1a4dd95986037031020004293bb8a9cbb9ab6b31a370328200042ffabedd185d3d8d216370320200041086a200041106a4120109488808000200041306a2480808080000bad0302037f027e23808080800041d0026b2202248080808000200241206a2001280204220320012802082204108b8a8080000240024020022d00200d002001280200210141e7adc4800041052003200441002802e0a1c6800011868080800000200241043a0020200241206a10f588808000200241163a0020200241023a0028200241206a108e8a80800002402001450d00200341002802c0a3c68000118080808000000b200242fc90b9e5d09296e777370328200242a6d4e6f1a4dd959860370320200242f7b6fccfd083b2cf4d370338200242afd2c6ffdbc495bc08370330200241206a412041002802a0a1c6800011848080800000200241206a10e189808000200241c0026a290300210520022903b8022106200041013a00182000200537031020002006370308200042013703000c010b200241136a200241346a28020022043600002002410b6a2002412c6a2902002205370000200220022902242206370003200041186a2004360000200041106a200537000020002006370008200042023703002001280200450d00200341002802c0a3c68000118080808000000b200241d0026a2480808080000b931a03047f027e027f23808080800041a00d6b220124808080800002400240417f4100280284a4c680002202410447200241044b1b2202450d00200241ff017141ff01470d010b200142fc90b9e5d09296e7773703d809200142a6d4e6f1a4dd9598603703d009200142d3d8c5d2a9b9d2c1ac7f3703e80920014282ca868eabf3add0cf003703e009200141a8016a200141d0096a10e688808000200120012903b001420020012802a8011b37039802200142fc90b9e5d09296e7773703d809200142a6d4e6f1a4dd9598603703d009200142bad08eede089ffa8123703e809200142bd81f785e387e6aa817f3703e009200141a0016a200141d0096a412010d088808000200120012802a401410020012802a0011b3602a002200142fc90b9e5d09296e7773703d809200142a6d4e6f1a4dd9598603703d00920014295f38cfcb699a3c4ef003703e809200142a8db95cdaa86daa7193703e00920014198016a200141d0096a412010d0888080002001200128029c0141002001280298011b3602a402200142fc90b9e5d09296e7773703d809200142a6d4e6f1a4dd9598603703d00920014295f38cfcb699a3c4ef003703e809200142a8db95cdaa86daa7193703e00920014190016a200141d0096a412010d08880800041e400210241e4002103024020012802940141002001280290011b2204418080f0014b0d00200441e4006c418080f0016e21030b200120033a00a902200142fc90b9e5d09296e7773703d809200142a6d4e6f1a4dd9598603703d00920014295f38cfcb699a3c4ef003703e809200142a8db95cdaa86daa7193703e00920014188016a200141d0096a412010d0888080000240200128028c0141002001280288011b2203418080c0024b0d00200341e4006c418080c0026e21020b200120023a00aa02200142fc90b9e5d09296e7773703d809200142a6d4e6f1a4dd9598603703d00920014295f38cfcb699a3c4ef003703e809200142a8db95cdaa86daa7193703e00920014180016a200141d0096a412010d08880800041e4002102024020012802840141002001280280011b2203418080c0024b0d00200341e4006c418080c0026e21020b200120023a00ab02200141b0026a10ce888080002001200141b0026a3602ac02200141e8026a10ce8880800020012903e802210520014198036a10e1898080000240024020014198036a41206a290300427f20012802b0031b2206500d0020052006560d00200141f0006a2005420042e400420010878e808000200141e0006a2001290370200141f0006a41086a2903002006420010898e80800020012903602205428002544100200141e0006a41086a290300501b450d002005a721020c010b41e40021020b200120023a00e702200141c8056a10ce888080002001200141d8056a3602c40520014180066a10ce888080002001290390062105200141b0066a10e18980800002400240200141a8076a290300427f200141a0076a2802001b2206500d0020052006560d00200141d0006a2005420042e400420010878e808000200141c0006a2001290350200141d0006a41086a2903002006420010898e80800020012903402205428002544100200141c0006a41086a290300501b450d002005a721020c010b41e40021020b200120023a00ff05200141e8086a10ce88808000200120014188096a3602e408200141a0096a10ce8880800020012903c0092105200141d0096a10e18980800002400240200141a00b6a290300427f200141980b6a2802001b2206500d0020052006560d00200141306a2005420042e400420010878e808000200141206a2001290330200141306a41086a2903002006420010898e80800020012903202205428002544100200141206a41086a290300501b450d002005a721020c010b41e40021020b20014194026a41cd838080003602002001418c026a41ce8380800036020020014184026a41cd83808000360200200141fc016a41ce83808000360200200141f4016a41cd83808000360200200141b8016a41346a41ce83808000360200200141e4016a41cd83808000360200200141b8016a41246a41cd83808000360200200141d4016a41cd83808000360200200141cc016a418580808000360200200141b8016a410c6a418580808000360200200141cc838080003602bc01200120023a009f0920012001419f096a360290022001200141e4086a360288022001200141ff056a360280022001200141c4056a3602f8012001200141e7026a3602f0012001200141ac026a3602e8012001200141ab026a3602e0012001200141aa026a3602d8012001200141a9026a3602d0012001200141a4026a3602c8012001200141a0026a3602c001200120014198026a3602b8014100280290a1c680002102410028028ca1c6800021034100280280a4c680002104200141900d6a420c370200200141880d6a410d360200200141800d6a410f360200200141d80c6a41246a41ccadc48000360200200141f00c6a4195a8c48000ad4280808080b00b84370200200141d80c6a410c6a41dbadc48000ad4280808080c00184370200200141d80c6a41346a200141b8016a360200200141dcaec480003602840d200141043602f80c200141003602ec0c200141003602e00c2001428180808090e0013702d80c200341ecf2c08000200441024622041b200141d80c6a200241d4f2c0800020041b280210118480808000000b200142fc90b9e5d09296e7773703d809200142a6d4e6f1a4dd9598603703d00920014293bb8a9cbb9ab6b31a3703e809200142ffabedd185d3d8d2163703e009200141d0096a412041002802a0a1c6800011848080800000200142fc90b9e5d09296e7773703d809200142a6d4e6f1a4dd9598603703d00920014295f38cfcb699a3c4ef003703e809200142a8db95cdaa86daa7193703e009200141d0096a412041002802a0a1c68000118480808000004188adc48000411341002802a0a1c6800011848080800000200142fc90b9e5d09296e7773703d809200142a6d4e6f1a4dd9598603703d009200142beee8ae9ce8fa2b1977f3703e80920014295d4f9afa2868fb8643703e009200141d0096a412041002802a0a1c6800011848080800000200142fc90b9e5d09296e7773703d809200142a6d4e6f1a4dd9598603703d009200142d3d8c5d2a9b9d2c1ac7f3703e80920014282ca868eabf3add0cf003703e009200141106a200141d0096a10e6888080002001290318210520012802102107200142fc90b9e5d09296e7773703b806200142a6d4e6f1a4dd9598603703b006200142bb88f596f8cbf6cf4c3703c8062001428a85cd9fb3e4b2ae6d3703c006200141d0096a200141b0066a412010dd888080000240024020012d00d0090d00200141980c6a4200370300200141900c6a4200370300200141880c6a4200370300200142003703800c0c010b200141980c6a200141e9096a290000370300200141900c6a200141e1096a290000370300200141880c6a200141d9096a290000370300200120012900d1093703800c0b200141a00c6a10ee88808000200142fc90b9e5d09296e7773703d809200142a6d4e6f1a4dd9598603703d009200142bad08eede089ffa8123703e809200142bd81f785e387e6aa817f3703e009200141086a200141d0096a412010d088808000200128020c2104024002400240024002400240200128020822024101470d00200141d0096a412041002802a0a1c68000118480808000000c010b2002450d010b2004450d000240200441aad5aad5004b0d002004410c6c2202417f4a0d020b10ae80808000000b41042108410021040c010b4100210341002d00fca3c680001a200241002802c8a3c68000118180808000002208450d01200821020340200141d0096a200310f387808000200241086a200141d0096a41086a280200360200200220012903d0093702002002410c6a21022004200341016a2203470d000b0b200120043602b40c200120083602b00c200120043602ac0c200141b80c6a200141ac0c6a410041002802f0a2c680001185808080000002402005420020071b2205420c540d00200542757c1085888080000b200141d0096a410041002802d8a1c6800011848080800000024020012802d8094120490d00200020012802d4092202290000370028200041c0006a200241186a290000370000200041386a200241106a290000370000200041306a200241086a290000370000024020012802d009450d00200241002802c0a3c68000118080808000000b200020012903800c37000020002005370320200020012900b80c370048200041186a200141800c6a41186a290300370000200041106a200141800c6a41106a290300370000200041086a200141800c6a41086a290300370000200041d0006a200141b80c6a41086a290000370000200041d8006a200141b80c6a41106a290000370000200041e0006a200141b80c6a41186a290000370000200020012902a00c370268200041f0006a200141a00c6a41086a280200360200200141a00d6a2480808080000f0b41c4afc48000412c2001419f0d6a41f0afc480004180b0c48000108981808000000b4104200210b280808000000b862901067f23808080800041e0006b22012480808080002001410036021420014280808080800137020c41002d00fca3c680001a0240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240410841002802c8a3c68000118180808000002202450d002002413a36020420024190b0c48000360200200141013602202001200236021c20014101360218200141186a2001410c6a109b8880800041002d00fca3c680001a410841002802c8a3c68000118180808000002203450d01200341cab0c480003602002003412e36020441002d00fca3c680001a410141002802c8a3c68000118180808000002204450d02200441003a0000024020012802142202200128020c470d002001410c6a2002109d86808000200128021421020b2001280210200241e8006c6a220241003a00602002410136025c2002200336025820024281808080103703502002200436024c2002428e80808010370244200241f8bec38000360240200241b580808000360218200242d7c9cb8fc1cf97db3e37030820024100360200200241106a42e88488d0c0e3aebc1337030041002d00fca3c680001a2001200128021441016a360214410841002802c8a3c68000118180808000002203450d03200341f8b0c480003602002003412936020441002d00fca3c680001a410141002802c8a3c68000118180808000002204450d04200441003a0000024020012802142202200128020c470d002001410c6a2002109d86808000200128021421020b2001280210200241e8006c6a220241013a00602002410136025c2002200336025820024281808080103703502002200436024c2002429080808010370244200241cfc0c380003602402002418881808000360218200242febac4ad81b6fafcb37f37030820024100360200200241106a4298848fa1dab08ba17437030041002d00fca3c680001a2001200128021441016a360214410841002802c8a3c68000118180808000002203450d0520034122360204200341a1b1c48000360200200141c0006a4200370300200141386a4200370300200141306a4200370300200141186a41106a4200370300200141186a41086a420037030020014200370318200141c8006a200141186a108d8d808000024020012802142202200128020c470d002001410c6a2002109d86808000200128021421020b2001280210200241e8006c6a2202410b360244200241d6bfc38000360240200241cf838080003602182002410036020020022001290348370348200241013a00602002410136025c2002200336025820024101360254200241d0006a200141c8006a41086a280200360200200242e6bc8a93f7f6b7ce33370308200241106a42c4f9f3c5a8b6cbf1663703002001200128021441016a36021441002d00fca3c680001a410841002802c8a3c68000118180808000002203450d06200341c3b1c48000360200200341d00036020441002d00fca3c680001a410141002802c8a3c68000118180808000002204450d07200441003a0000024020012802142202200128020c470d002001410c6a2002109d86808000200128021421020b2001280210200241e8006c6a220241003a00602002410136025c2002200336025820024281808080103703502002200436024c20024290808080103702442002419ebfc38000360240200241b58080800036021820024100360200200242d7c9cb8fc1cf97db3e370308200241106a42e88488d0c0e3aebc133703002001200128021441016a36021441002d00fca3c680001a410841002802c8a3c68000118180808000002202450d082002412636020420024193b2c48000360200200141013602202001200236021c20014101360218200141186a2001410c6a10978880800041002d00fca3c680001a410841002802c8a3c68000118180808000002203450d09200341cf00360204200341b9b2c4800036020041002d00fca3c680001a410141002802c8a3c68000118180808000002204450d0a200441053a000041002d00fca3c680001a410441002802c8a3c68000118180808000002202450d0b200141186a41086a220541003602002001200236021c20014104360218200141003602582001200141d8006a36025c200141dc006a200141186a10c08a808000200141c8006a41086a2206200528020036020020012001290218370348024020012802142202200128020c470d002001410c6a2002109d86808000200128021421020b2001280210200241e8006c6a2202410d360244200241899fc38000360240200241ea81808000360238200241b5808080003602202002410136020c20022004360208200242818080801037030020022001290348370348200241013a00602002410136025c2002200336025820024101360254200241d0006a2006280200360200200242e7b0a091f3ed9c85c500370328200242d7c9cb8fc1cf97db3e370310200241306a42b891b68c98adebcf61370300200241186a42e88488d0c0e3aebc133703002001200128021441016a36021441002d00fca3c680001a410841002802c8a3c68000118180808000002203450d0c20034188b3c48000360200200341c20036020441002d00fca3c680001a410841002802c8a3c68000118180808000002204450d0d20044200370000024020012802142202200128020c470d002001410c6a2002109d86808000200128021421020b2001280210200241e8006c6a220241013a00602002410136025c2002200336025820024288808080103703502002200436024c200242868080808001370244200241bfbec38000360240200241ef8080800036021820024100360200200242a5e9e3ab9e929adc2c370308200241106a4293888c8f89fdc6ec9e7f3703002001200128021441016a36021441002d00fca3c680001a410841002802c8a3c68000118180808000002202450d0e2002411c360204200241cab3c48000360200200141013602202001200236021c20014101360218200141186a2001410c6a10fe8880800041002d00fca3c680001a410841002802c8a3c68000118180808000002202450d0f2002413c360204200241e6b3c48000360200200141013602202001200236021c20014101360218200141186a2001410c6a10808980800041002d00fca3c680001a413841002802c8a3c68000118180808000002202450d1020024190b6c48000360230200241c5b5c480003602282002419cadc4800036022020024191b5c48000360218200241cab4c480003602102002419cadc4800036020820024128360204200241a2b4c48000360200200241346a413f3602002002412c6a41cb00360200200241246a41003602002002411c6a4134360200200241146a41c7003602002002410c6a4100360200200141073602202001200236021c20014107360218200141186a2001410c6a10ff8880800041002d00fca3c680001a410841002802c8a3c68000118180808000002203450d11200341cfb6c480003602002003412e36020441002d00fca3c680001a410441002802c8a3c68000118180808000002204450d1220044100360000024020012802142202200128020c470d002001410c6a2002109d86808000200128021421020b2001280210200241e8006c6a220241013a00602002410136025c2002200336025820024284808080103703502002200436024c2002428a808080c00037024420024190bec38000360240200241b580808000360218200242d7c9cb8fc1cf97db3e37030820024100360200200241106a42e88488d0c0e3aebc1337030041002d00fca3c680001a2001200128021441016a36021441d00041002802c8a3c68000118180808000002202450d13200241f2bac480003602482002419fbac48000360240200241c9b9c480003602382002419cadc4800036023020024190b9c48000360228200241bfb8c48000360220200241ebb7c480003602182002419cadc48000360210200241c6b7c48000360208200241c900360204200241fdb6c48000360200200241cc006a41c000360200200241c4006a41d3003602002002413c6a41d600360200200241346a41003602002002412c6a4139360200200241246a41d1003602002002411c6a41d400360200200241146a41003602002002410c6a412536020041002d00fca3c680001a410141002802c8a3c68000118180808000002204450d14200441023a000041002d00fca3c680001a410441002802c8a3c68000118180808000002203450d15200141186a41086a220541003602002001200336021c20014104360218200141003602582001200141d8006a36025c200141dc006a200141186a10c08a808000200141c8006a41086a2206200528020036020020012001290218370348024020012802142203200128020c470d002001410c6a2003109d86808000200128021421030b2001280210200341e8006c6a2203410b360244200341c99ec38000360240200341a782808000360238200341f5818080003602202003410136020c20032004360208200342818080801037030020032001290348370348200341013a00602003410a36025c200320023602582003410a360254200341d0006a2006280200360200200342d39ec9badc8ccad845370328200342c194a6a793ccc3a857370310200341306a42eab19fb291b0f9fb0c370300200341186a42ab8bffbed784ffa5937f3703002001200128021441016a36021441002d00fca3c680001a410841002802c8a3c68000118180808000002203450d16200341b2bbc48000360200200341d50036020441002d00fca3c680001a410141002802c8a3c68000118180808000002204450d17200441003a0000024020012802142202200128020c470d002001410c6a2002109d86808000200128021421020b2001280210200241e8006c6a220241003a00602002410136025c2002200336025820024281808080103703502002200436024c2002429280808010370244200241c5bec38000360240200241d083808000360218200242dfcb98f48cd0bd951f37030820024100360200200241106a42acf0e291d3fdc7d4d20037030041002d00fca3c680001a2001200128021441016a360214410841002802c8a3c68000118180808000002203450d1820034187bcc48000360200200341d30036020441002d00fca3c680001a410141002802c8a3c68000118180808000002204450d19200441003a0000024020012802142202200128020c470d002001410c6a2002109d86808000200128021421020b2001280210200241e8006c6a220241013a00602002410136025c2002200336025820024281808080103703502002200436024c2002429580808010370244200241c1bfc380003602402002418881808000360218200242febac4ad81b6fafcb37f37030820024100360200200241106a4298848fa1dab08ba17437030041002d00fca3c680001a2001200128021441016a360214411041002802c8a3c68000118180808000002203450d1a200341dabcc48000360200200341b1bdc48000360208200341d7003602042003410c6a411236020041002d00fca3c680001a410141002802c8a3c68000118180808000002204450d1b200441003a0000024020012802142202200128020c470d002001410c6a2002109d86808000200128021421020b2001280210200241e8006c6a220241013a00602002410236025c2002200336025820024281808080203703502002200436024c2002429880808010370244200241ffc0c380003602402002418881808000360218200242febac4ad81b6fafcb37f37030820024100360200200241106a4298848fa1dab08ba17437030041002d00fca3c680001a2001200128021441016a360214410841002802c8a3c68000118180808000002203450d1c200341c3bdc480003602002003412236020441002d00fca3c680001a410141002802c8a3c68000118180808000002204450d1d200441003a0000024020012802142202200128020c470d002001410c6a2002109d86808000200128021421020b2001280210200241e8006c6a220241003a00602002410136025c2002200336025820024281808080103703502002200436024c2002428e80808010370244200241f3bfc380003602402002419b82808000360218200242fb9bf4d1a5ccd7cbed0037030820024100360200200241106a42dd9fc4e2a4bae882d40037030041002d00fca3c680001a2001200128021441016a360214410841002802c8a3c68000118180808000002203450d1e2003412e360204200341e5bdc48000360200200141023a0018200141c8006a200141186a10b38c808000024020012802142202200128020c470d002001410c6a2002109d86808000200128021421020b2001280210200241e8006c6a22024111360244200241d7bec38000360240200241d1838080003602182002410036020020022001290348370348200241003a00602002410136025c2002200336025820024101360254200241d0006a200141c8006a41086a280200360200200242fc969fcca28189f9e200370308200241106a42bc9ecabcb9f0b1b0363703002001410c6a41086a2202200228020041016a2202360200200041086a20023602002000200129020c370200200041106a4106360200200041f9a6c4800036020c200141e0006a2480808080000f0b4104410810b280808000000b4104410810b280808000000b4101410110b280808000000b4104410810b280808000000b4101410110b280808000000b4104410810b280808000000b4104410810b280808000000b4101410110b280808000000b4104410810b280808000000b4104410810b280808000000b4101410110b280808000000b4101410410b280808000000b4104410810b280808000000b4101410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104413810b280808000000b4104410810b280808000000b4101410410b280808000000b410441d00010b280808000000b4101410110b280808000000b4101410410b280808000000b4104410810b280808000000b4101410110b280808000000b4104410810b280808000000b4101410110b280808000000b4104411010b280808000000b4101410110b280808000000b4104410810b280808000000b4101410110b280808000000b4104410810b280808000000bb60f010c7f23808080800041d0026b220124808080800041002d00fca3c680001a0240024002400240024002400240024002400240024041d00241002802c8a3c68000118180808000002202450d00200141186a10e189808000200141c4026a200141186a10d48b80800041002d00fca3c680001a410841002802c8a3c68000118180808000002203450d012003413436020420034193bec48000360200200141086a410c6a200141c4026a41086a280200360200200120012902c40237020c41002d00fca3c680001a410c41002802c8a3c68000118180808000002204450d022004428080f081808080283700002004418080c00236000841002d00fca3c680001a410841002802c8a3c68000118180808000002205450d03200541d3bec480003602002005412a36020441002d00fca3c680001a410841002802c8a3c68000118180808000002206450d042006420a37000041002d00fca3c680001a410841002802c8a3c68000118180808000002207450d0520074188bfc48000360200200741d50036020441002d00fca3c680001a411041002802c8a3c68000118180808000002208450d0620084200370000200841086a420037000041002d00fca3c680001a410841002802c8a3c68000118180808000002209450d07200941c200360204200941ebbfc480003602002001412c6a4100360200200141286a418097c38000360200200141386a4200370200200142003702402001428080808080808080807f3702202001418097c3800036021c200141003a004c2001410036024820014280808080c0003702302001418080808078360218200141c4026a200141186a108e8d80800002402001280218220a418080808078460d00200a450d00200128021c41002802c0a3c68000118080808000000b02402001280224220a418080808078460d00200a450d00200128022841002802c0a3c68000118080808000000b02402001280230220a418080808078460d00200a450d00200128023441002802c0a3c68000118080808000000b41002d00fca3c680001a410841002802c8a3c6800011818080800000220b450d08200b4121360204200b41b5c0c48000360200200141246a200141c4026a41086a280200360200200120012902c40237021c41002d00fca3c680001a410241002802c8a3c6800011818080800000220c450d09200c41003b000041002d00fca3c680001a412841002802c8a3c6800011818080800000220a450d0a200a41a1c2c48000360220200a41d5c1c48000360218200a4187c1c48000360210200a419cadc48000360208200a412a360204200a41ddc0c48000360200200a41246a411c360200200a411c6a41cc00360200200a41146a41ce00360200200a410c6a4100360200200241106a428f96cddec79feed66b370300200242c4c5d9da83adf3df4a370308200241f0016a42fbe0bedd81cff9c317370300200241e8016a42cec9d2b3fca8a6f5bc7f370300200241b8016a42e6d58f99bfb09cd660370300200241b0016a428d9fb78edfb6baf67d37030020024180016a4293888c8f89fdc6ec9e7f370300200241f8006a42a5e9e3ab9e929adc2c370300200241c8006a42f6ef8697d7dfa3a3b07f370300200241c0006a42c0f88c9ce3f4f7fcb27f370300200241d2838080003602182002410c360204200241c7bec480003602002002200129020837021c200241246a200141086a41086a290200370200200241f8016a41f882808000360200200241e4016a4107360200200241d6c0c480003602e001200241dc016a4101360200200241d8016a2009360200200241d0016a429080808010370200200241cc016a2008360200200241c8016a4110360200200241c0016a41d383808000360200200241ac016a4108360200200241adc0c480003602a801200241a4016a4101360200200241a0016a200736020020024198016a42888080801037020020024194016a200636020020024190016a410836020020024188016a41ef80808000360200200241f4006a410e360200200241ddbfc48000360270200241ec006a4101360200200241e8006a2005360200200241e0006a428c80808010370200200241dc006a2004360200200241d8006a410c360200200241d0006a41d4838080003602002002413c6a410b360200200241fdbec4800036023820024101360234200220033602302002410136022c2002418c026a410136020020024190026a200b36020020024194026a4101360200200241bdc2c48000360298022002419c026a410a360200200241b0026a41d583808000360200200241b8026a4102360200200241bc026a200c360200200241c8026a200a360200200241cc026a4105360200200241c0026a4282808080d000370200200241a0026a42d7b9acfdf187c880f100370300200241a8026a42a695d4f0d8d192864537030020024184026a200141186a41086a290200370200200241fc016a2001290218370200200041063602082000200236020420004106360200200141d0026a2480808080000f0b410841d00210b280808000000b4104410810b280808000000b4101410c10b280808000000b4104410810b280808000000b4101410810b280808000000b4104410810b280808000000b4101411010b280808000000b4104410810b280808000000b4104410810b280808000000b4101410210b280808000000b4104412810b280808000000bfa0a01067f024002400240024002400240024002400240024020002d00000e0700010203040506070b0240200128020020012802082202470d0020012002410110b182808000200128020821020b2001200241016a360208200128020420026a41003a0000200041086a200110ef8c8080000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b2001200241016a360208200128020420026a41013a0000200041046a200110cc88808000200041186a200110ef8c8080000f0b0240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a41023a00000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41033a00002001200241016a2202360208200041016a21000240200128020020026b411f4b0d0020012002412010b182808000200128020821020b2001200241206a360208200128020420026a22012000290000370000200141086a200041086a290000370000200141106a200041106a290000370000200141186a200041186a2900003700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41043a00002001200241016a2202360208200041016a21000240200128020020026b411f4b0d0020012002412010b182808000200128020821020b2001200241206a360208200128020420026a22012000290000370000200141086a200041086a290000370000200141106a200041106a290000370000200141186a200041186a2900003700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41053a00002001200241016a2203360208200041016a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441086a200241086a290000370000200441106a200241106a290000370000200441186a200241186a2900003700002001200341206a220336020841002d00fca3c680001a412041002802c8a3c68000118180808000002202450d0220022000290021370000200241186a200041396a290000370000200241106a200041316a290000370000200241086a200041296a2900003700000240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22002002290000370000200041086a200241086a290000370000200041106a200241106a290000370000200041186a200241186a2900003700002001200341206a360208200241002802c0a3c68000118080808000000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41063a00002001200241016a220336020841002d00fca3c680001a412041002802c8a3c68000118180808000002202450d0220022000290001370000200241186a2205200041196a290000370000200241106a2206200041116a290000370000200241086a2207200041096a2900003700000240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441086a2007290000370000200441106a2006290000370000200441186a20052900003700002001200341206a2203360208200241002802c0a3c680001180808080000020002d00212100024020012802002003470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a20003a00000b0f0b4101412010b280808000000b4101412010b280808000000bdc0203017f017e027f410021010240024002400240024020002d00000e0700010402020203040b4103210102402000290308220242c000540d0041042101200242808001540d00410621012002428080808004540d00410b200279a74103766b21010b410121030240200041106a290300220242c000540d0041022103200242808001540d00410421032002428080808004540d004109200279a74103766b21030b200320016a21010c030b20002d000441027441e4c9c480006a21034103210102402000290318220242c000540d0041042101200242808001540d00410621012002428080808004540d00410b200279a74103766b21010b20032802002104410121030240200041206a290300220242c000540d0041022103200242808001540d00410421032002428080808004540d004109200279a74103766b21030b200120046a20036a21010c020b412021010c010b410121010b200141016a0bc70801037f02400240024002400240024002400240024002400240024002400240024002400240024020012d0000417f6a0e0b000102030405060708090a000b200141086a28020021024101210302402001410c6a2802002201450d002001417f4c0d0b41002d00fca3c680001a200141002802c8a3c68000118180808000002203450d0c0b20032002200110848e80800021032000410c6a2001360200200041086a200336020020002001360204200041013a00000f0b20002001290308370308200041023a00000f0b200141086a2802002103024002402001410c6a28020022010d00410121020c010b2001417f4c0d0941002d00fca3c680001a200141002802c8a3c68000118180808000002202450d0b0b20022003200110848e80800021032000410c6a2001360200200041086a200336020020002001360204200041033a00000f0b200141086a2802002103024002402001410c6a28020022010d00410121020c010b2001417f4c0d0841002d00fca3c680001a200141002802c8a3c68000118180808000002202450d0b0b20022003200110848e80800021032000410c6a2001360200200041086a200336020020002001360204200041043a00000f0b200041046a200141046a108c87808000200041053a00000f0b200041046a200141046a108a87808000200041063a00000f0b2001410c6a280200210202400240200141106a28020022030d00410121040c010b2003417f4c0d0541002d00fca3c680001a200341002802c8a3c68000118180808000002204450d090b20042002200310848e8080002102200041106a20033602002000410c6a20023602002000200336020820002001280204360204200041073a00000f0b200141086a2802002103024002402001410c6a28020022010d00410121020c010b2001417f4c0d0441002d00fca3c680001a200141002802c8a3c68000118180808000002202450d090b20022003200110848e80800021032000410c6a2001360200200041086a200336020020002001360204200041083a00000f0b20002001290001370001200041196a200141196a290000370000200041116a200141116a290000370000200041096a200141096a290000370000200041093a00000f0b20002001290001370001200041196a200141196a290000370000200041116a200141116a290000370000200041096a200141096a2900003700002000410a3a00000f0b200141086a2802002103024002402001410c6a28020022010d00410121020c010b2001417f4c0d0141002d00fca3c680001a200141002802c8a3c68000118180808000002202450d070b20022003200110848e80800021032000410c6a2001360200200041086a2003360200200020013602042000410b3a00000f0b10ae80808000000b4101200110b280808000000b4101200110b280808000000b4101200110b280808000000b4101200310b280808000000b4101200110b280808000000b4101200110b280808000000bae0603057f017e027f23808080800041c0006b2201248080808000200141186a41c7c2c48000410441d2aac480004114419cadc48000410010d882808000200141106a42043702002001420037020820014280808080800137020041002d00fca3c680001a024002400240412041002802c8a3c68000118180808000002202450d002002410036021820024101360204200241cbc2c4800036020020014101360238200120023602302001200241206a36023c200120023602342001200141306a10fa8680800041002d00fca3c680001a20012802002103200128020421022001280208210420012802182105200129021c2106410841002802c8a3c68000118180808000002207450d0120074100290390c3c480003702002001410036020820014280808080c000370200200141306a200141f0a8c48000410610b18b8080002001200141306a41f6a8c48000410e10988b808000200141306a20014184a9c48000410810868b8080002001200141306a418ca9c48000411710918b808000200141306a200141a3a9c48000410b10838b8080002001200141306a41aea9c48000410c109f8b808000200141306a200141baa9c48000410b10af8b8080002001200141306a41c5a9c48000411110c68b808000200141306a200141d6a9c48000411110d38b8080002001200141306a41e7a9c48000412010a48b808000200141246a20014187aac480004118108d8b8080002001200128022436020820012001280228220836020020012008200128022c41246c6a36020c20012008360204200141306a200110fb868080002005418080808078460d022001200336020820012002360200200120023602042001200220044105746a36020c200041c4006a200110fa868080002001410b6a200141306a41086a2802003600002000413c6a200637020020002005360238200041013a0000200041d8006a4101360200200041d4006a2007360200200041013602502001200129023037000320002001290000370001200041086a200141076a290000370000200141c0006a2480808080000f0b4108412010b280808000000b4104410810b280808000000b41a8d8c480004111419cd9c4800010a181808000000bb11303067f017e037f23808080800041c0006b2201248080808000200141206a4198c3c48000410541d2aac480004114419cadc48000410010d882808000200141186a42043702002001420037021020014280808080800137020841002d00fca3c680001a024002400240024002400240024002400240024002400240412041002802c8a3c68000118180808000002202450d002002410036021820024101360204200241cbc2c4800036020020014101360238200120023602302001200241206a36023c20012002360234200141086a200141306a10fa8680800041002d00fca3c680001a20012802082103200128020c2104200128021021052001280220210620012902242107410841002802c8a3c68000118180808000002208450d01200841002903b8c3c4800037020041002d00fca3c680001a41002802c8a3c6800021022001410036021020014280808080c00037020841102002118180808000002209450d02200941086a41002902d8f3c48000370200200941002902d0f3c48000370200200141086a410010a086808000200128020c200141086a41086a220a28020041246c6a220241003a00202002410f36021c200241c0c3c4800036021820024102360214200220093602102002428080808020370208200242808080808001370200200141306a41086a200a28020041016a3602002001200129020837033041002d00fca3c680001a411041002802c8a3c68000118180808000002209450d03200941086a41002902b8f4c48000370200200941002902b0f4c480003702000240200128023822022001280230470d00200141306a200210a086808000200128023821020b2001280234200241246c6a220241013a00202002411a36021c200241cfc3c4800036021820024102360214200220093602102002428080808020370208200242808080808001370200200141086a41086a200141306a41086a28020041016a3602002001200129033037030841002d00fca3c680001a411841002802c8a3c68000118180808000002209450d04200941106a41002902b8f6c48000370200200941086a41002902b0f6c48000370200200941002902a8f6c480003702000240200128021022022001280208470d00200141086a200210a086808000200128021021020b200128020c200241246c6a220241023a00202002411d36021c200241e9c3c4800036021820024103360214200220093602102002428080808030370208200242808080808001370200200141306a41086a200141086a41086a28020041016a3602002001200129030837033041002d00fca3c680001a410841002802c8a3c68000118180808000002209450d0520094100290380f5c480003702000240200128023822022001280230470d00200141306a200210a086808000200128023821020b2001280234200241246c6a220241033a00202002411336021c20024186c4c4800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141086a41086a200141306a41086a28020041016a3602002001200129033037030841002d00fca3c680001a410841002802c8a3c68000118180808000002209450d06200941002903b0f8c480003702000240200128021022022001280208470d00200141086a200210a086808000200128021021020b200128020c200241246c6a220241043a00202002410f36021c20024199c4c4800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141306a41086a200141086a41086a28020041016a3602002001200129030837033041002d00fca3c680001a410841002802c8a3c68000118180808000002209450d07200941002903f8f6c480003702000240200128023822022001280230470d00200141306a200210a086808000200128023821020b2001280234200241246c6a220241053a00202002410c36021c200241a8c4c4800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141086a41086a200141306a41086a28020041016a3602002001200129033037030841002d00fca3c680001a410841002802c8a3c68000118180808000002209450d08200941002903d8f7c480003702000240200128021022022001280208470d00200141086a200210a086808000200128021021020b200128020c200241246c6a220241063a00202002411b36021c200241b4c4c4800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141306a41086a200141086a41086a28020041016a3602002001200129030837033041002d00fca3c680001a410841002802c8a3c68000118180808000002209450d09200941002903a0f5c480003702000240200128023822022001280230470d00200141306a200210a086808000200128023821020b2001280234200241246c6a220241073a00202002411136021c200241cfc4c4800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141086a41086a200141306a41086a28020041016a3602002001200129033037030841002d00fca3c680001a410841002802c8a3c68000118180808000002209450d0a200941002903e0f8c480003702000240200128021022022001280208470d00200141086a200210a086808000200128021021020b200128020c200241246c6a220241083a00202002410c36021c200241e0c4c480003602182002410136021420022009360210200242808080801037020820024280808080800137020020012802102109200128020c2102200120012802083602102001200236020820012002200941246c6a41246a3602142001200236020c200141306a200141086a10fb868080002006418080808078460d0b20012003360210200120043602082001200436020c2001200420054105746a360214200041c4006a200141086a10fa86808000200141136a200141306a41086a2802003600002000413c6a200737020020002006360238200041013a0000200041d8006a4101360200200041d4006a2008360200200041013602502001200129023037000b20002001290008370001200041086a2001410f6a290000370000200141c0006a2480808080000f0b4108412010b280808000000b4104410810b280808000000b4104411010b280808000000b4104411010b280808000000b4104411810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b41a8d8c480004111419cd9c4800010a181808000000b8a0703067f017e027f23808080800041c0006b2201248080808000200141186a41ecc4c48000410541d2aac480004114419cadc48000410010d882808000200141106a42043702002001420037020820014280808080800137020041002d00fca3c680001a0240024002400240412041002802c8a3c68000118180808000002202450d002002410036021820024101360204200241cbc2c4800036020020014101360238200120023602302001200241206a36023c200120023602342001200141306a10fa8680800041002d00fca3c680001a20012802002103200128020421042001280208210520012802182106200129021c2107410841002802c8a3c68000118180808000002208450d0120084100290390c5c480003702002001410036020820014280808080c000370200200141306a20014198c5c48000411010b48b8080002001200141306a41a8c5c48000410f10fd8a80800041002d00fca3c680001a410841002802c8a3c68000118180808000002209450d02200941002903a0f9c480003702000240200128020822022001280200470d002001200210a086808000200128020821020b2001280204200241246c6a220241023a00202002410b36021c200241b7c5c4800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141306a41086a2209200141086a28020041016a360200200120012902003703302001200141306a41c2c5c48000410a10a88b808000200141306a200141ccc5c48000410d10cb8b8080002001200141306a41d9c5c48000410810878b808000200141246a200141e1c5c48000411110ff8a8080002001200128022436020820012001280228220236020020012002200128022c41246c6a36020c20012002360204200141306a200110fb868080002006418080808078460d032001200336020820012004360200200120043602042001200420054105746a36020c200041c4006a200110fa868080002001410b6a20092802003600002000413c6a200737020020002006360238200041013a0000200041d8006a4101360200200041d4006a2008360200200041013602502001200129023037000320002001290000370001200041086a200141076a290000370000200141c0006a2480808080000f0b4108412010b280808000000b4104410810b280808000000b4104410810b280808000000b41a8d8c480004111419cd9c4800010a181808000000be60d03037f017e017f23808080800041f0006b2202248080808000024002400240024002400240024002400240024002400240024002400240200128020022032802042204450d0020032004417f6a36020420032003280200220441016a36020020042d00000e0c01020304050607080d090a0b0d0b200041003a00000c0d0b2002200110bc8a8080000240024020022802000d00200241d4006a2001200228020410d0858080002002280254418080808078460d00200241e0006a41086a200241d4006a41086a2802002203360200200241cf006a20033600002002200229025422053703602002200537004720002002290044370001200041086a200241cb006a290000370000410121030c010b410021030b200020033a00000c0c0b02402001280200220328020422014108490d00200041023a00002003200141786a36020420032003280200220141086a360200200020012900003703080c0c0b200041003a00000c0b0b200241086a200110bc8a808000024020022802080d00200241d4006a2001200228020c10d0858080002002280254418080808078460d00200241e0006a41086a200241d4006a41086a2802002203360200200241cf006a20033600002002200229025422053703602002200537004720002002290044370001200041086a200241cb006a290000370000200041033a00000c0b0b200041003a00000c0a0b200241106a200110bc8a808000024020022802100d00200241d4006a2001200228021410d0858080002002280254418080808078460d00200241e0006a41086a200241d4006a41086a2802002203360200200241cf006a20033600002002200229025422053703602002200537004720002002290044370001200041086a200241cb006a290000370000200041043a00000c0a0b200041003a00000c090b200241186a200110bc8a808000024020022802180d00200241d4006a2001200228021c10cf858080002002280254418080808078460d00200241e0006a41086a200241d4006a41086a2802002203360200200241cf006a20033600002002200229025422053703602002200537004720002002290044370001200041086a200241cb006a290000370000200041053a00000c090b200041003a00000c080b200241206a200110bc8a808000024020022802200d00200241d4006a2001200228022410cd858080002002280254418080808078460d00200241e0006a41086a200241d4006a41086a2802002203360200200241cf006a20033600002002200229025422053703602002200537004720002002290044370001200041086a200241cb006a290000370000200041063a00000c080b200041003a00000c070b200241286a200110bc8a80800020022802280d04200241c4006a2001200228022c10d08580800020022802442203418080808078460d042002280248210402402001280200220128020422064104490d002000200228024c3602102000200436020c20002003360208200041073a000020012006417c6a36020420012001280200220341046a360200200020032800003602040c070b200041003a00002003450d06200441002802c0a3c68000118080808000000c060b200241306a200110bc8a808000024020022802300d00200241d4006a2001200228023410d0858080002002280254418080808078460d00200241e0006a41086a200241d4006a41086a2802002203360200200241cf006a20033600002002200229025422053703602002200537004720002002290044370001200041086a200241cb006a290000370000200041083a00000c060b200041003a00000c050b4100210302402001280200220128020422044120490d002001200441606a36020420012001280200220441206a3602002000200429000037000141092103200041096a200441086a290000370000200041116a200441106a290000370000200041196a200441186a2900003700000b200020033a00000c040b4100210302402001280200220128020422044120490d002001200441606a36020420012001280200220341206a36020020002003290000370001200041096a200341086a290000370000200041116a200341106a290000370000200041196a200341186a290000370000410a21030b200020033a00000c030b200241386a200110bc8a808000024020022802380d00200241d4006a2001200228023c10d0858080002002280254418080808078460d00200241e0006a41086a200241d4006a41086a2802002203360200200241c4006a410b6a20033600002002200229025422053703602002200537004720002002290044370001200041086a200241cb006a2900003700002000410b3a00000c030b200041003a00000c020b200041003a00000c010b200041003a00000b200241f0006a2480808080000bbe1203037f017e037f23808080800041106b22022480808080000240024002400240024002400240024002400240024002400240024020002d0000417f6a0e0b000102030405060708090a0b0b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41003a0000200041086a280200210420022000410c6a28020022033602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822006b20034f0d0020012000200310b182808000200128020821000b200128020420006a2004200310848e8080001a2001200020036a3602080c0a0b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41013a00002001200341016a2203360208200029030821050240200128020020036b41074b0d0020012003410810b182808000200128020821030b2001200341086a360208200128020420036a20053700000c090b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41023a0000200041086a280200210420022000410c6a28020022033602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822006b20034f0d0020012000200310b182808000200128020821000b200128020420006a2004200310848e8080001a2001200020036a3602080c080b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41033a0000200041086a280200210420022000410c6a28020022033602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822006b20034f0d0020012000200310b182808000200128020821000b200128020420006a2004200310848e8080001a2001200020036a3602080c070b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41043a0000200041086a280200210320022000410c6a28020022003602082002200241086a36020c2002410c6a200110c08a8080002000450d06200041186c210003402003200110fe86808000200341186a2103200041686a22000d000c070b0b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41053a0000200041086a280200210420022000410c6a28020022033602082002200241086a36020c2002410c6a200110c08a8080002003450d052003410c6c2106200441086a210003402000417c6a28020021072002200028020022033602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822046b20034f0d0020012004200310b182808000200128020821040b200128020420046a2007200310848e8080001a2001200420036a3602082000410c6a2100200641746a22060d000c060b0b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41063a00002000410c6a28020021062002200041106a28020022033602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822046b20034f0d0020012004200310b182808000200128020821040b200128020420046a2006200310848e8080001a2001200420036a2203360208200028020421000240200128020020036b41034b0d0020012003410410b182808000200128020821030b2001200341046a360208200128020420036a20003600000c040b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41073a0000200041086a280200210420022000410c6a28020022033602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822006b20034f0d0020012000200310b182808000200128020821000b200128020420006a2004200310848e8080001a2001200020036a3602080c030b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41093a00002001200341016a220436020841002d00fca3c680001a412041002802c8a3c68000118180808000002203450d0320032000290001370000200341186a2206200041196a290000370000200341106a2207200041116a290000370000200341086a2208200041096a2900003700000240200128020020046b411f4b0d0020012004412010b182808000200128020821040b200128020420046a22002003290000370000200041086a2008290000370000200041106a2007290000370000200041186a20062900003700002001200441206a360208200341002802c0a3c68000118080808000000c020b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a410a3a00002001200341016a220436020841002d00fca3c680001a412041002802c8a3c68000118180808000002203450d0320032000290001370000200341186a2206200041196a290000370000200341106a2207200041116a290000370000200341086a2208200041096a2900003700000240200128020020046b411f4b0d0020012004412010b182808000200128020821040b200128020420046a22002003290000370000200041086a2008290000370000200041106a2007290000370000200041186a20062900003700002001200441206a360208200341002802c0a3c68000118080808000000c010b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a410b3a0000200041086a280200210420022000410c6a28020022033602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822006b20034f0d0020012000200310b182808000200128020821000b200128020420006a2004200310848e8080001a2001200020036a3602080b200241106a2480808080000f0b4101412010b280808000000b4101412010b280808000000bd30202017f027e23808080800041d0006b2201248080808000024020002903002202500d0020014298d5afd2c6aeacae2f370328200142c2cdc8b0c7b9e78f857f370320200142e4c5bdb4b2e9a5a6807f370338200142d790d7a3fef9fda0c800370330200141106a200141206a10e688808000200129031821032001280210210020014298d5afd2c6aeacae2f370328200142c2cdc8b0c7b9e78f857f370320200142e4c5bdb4b2e9a5a6807f370338200142d790d7a3fef9fda0c8003703302001200141206a10e68880800020014298d5afd2c6aeacae2f370328200142c2cdc8b0c7b9e78f857f370320200142e4c5bdb4b2e9a5a6807f370338200142d790d7a3fef9fda0c800370330200142002003420020001b220320027d220220022003561b370348200141206a4120200141c8006a410841002802e0a1c68000118680808000000b200141d0006a2480808080000bcd0704067f017e017f017e23808080800041d0006b2201248080808000200141286a41f2c5c48000410841fac5c480004123419cadc48000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d00200242d1c5efcdcd82cde1ff003703082002419fc6c480003602202002419382808000360218200241023602042002419dc6c48000360200200241306a4293888c8f89fdc6ec9e7f370300200241286a42a5e9e3ab9e929adc2c370300200241106a429ccab49c93e2e495cf00370300200241386a41ef80808000360200200241246a410736020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a2001411c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c20024202370224200241d0fbc480003602202002410236021c200241cefbc4800036021820024193828080003602102002429ccab49c93e2e495cf00370308200242d1c5efcdcd82cde1ff00370300200141c0006a41086a200828020041016a2202360200200120012902102209370340024020022009a7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c20024207370224200241c7fbc480003602202002410636021c200241c1fbc48000360218200241ef8080800036021020024293888c8f89fdc6ec9e7f370308200242a5e9e3ab9e929adc2c370300200128024821082001280244210220012001280240360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000bcb0704067f017e017f017e23808080800041d0006b2201248080808000200141286a41f2c5c48000410841fac5c480004123419cadc48000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d002002429fb2eafebdc29397f7003703082002419fc6c48000360220200241d683808000360218200241023602042002419dc6c48000360200200241306a4293888c8f89fdc6ec9e7f370300200241286a42a5e9e3ab9e929adc2c370300200241106a42e0f4d5c484838cd222370300200241386a41ef80808000360200200241246a410736020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a2001411c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c20024202370224200241d0fbc480003602202002410236021c200241cefbc48000360218200241d683808000360210200242e0f4d5c484838cd2223703082002429fb2eafebdc29397f700370300200141c0006a41086a200828020041016a2202360200200120012902102209370340024020022009a7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c20024207370224200241c7fbc480003602202002410636021c200241c1fbc48000360218200241ef8080800036021020024293888c8f89fdc6ec9e7f370308200242a5e9e3ab9e929adc2c370300200128024821082001280244210220012001280240360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000b4000200042fc90b9e5d09296e777370008200042a6d4e6f1a4dd959860370000200042d3d8c5d2a9b9d2c1ac7f37001820004282ca868eabf3add0cf003700100b3e00200042fc90b9e5d09296e777370008200042a6d4e6f1a4dd959860370000200042f7b6fccfd083b2cf4d370018200042afd2c6ffdbc495bc083700100b820702047f057e23808080800041c0016b2201248080808000200142fc90b9e5d09296e777370338200142a6d4e6f1a4dd959860370330200141fc006a200141306a411041002802c0a1c680001185808080000002400240200128027c2202418080808078460d00200128028001210302402001280284014110490d00200141306a2003411010888e808000210402402002450d00200341002802c0a3c68000118080808000000b20040d010c020b2002450d00200341002802c0a3c68000118080808000000b41002102200141003b014e02400240417f4100280284a4c680002203410347200341034b1b2203450d00200341ff017141ff01470d010b200141d8006a410c6a41d783808000360200200141838280800036025c20014106360254200141f9a6c480003602502001200141ce006a3602602001200141d0006a3602584100280290a1c680002102410028028ca1c6800021034100280280a4c680002104200141b4016a4202370200200141ac016a4103360200200141a4016a4116360200200141a0016a41bcaac4800036020020014194016a4195a8c48000ad4280808080b00b84370200200141fc006a410c6a41d2aac48000ad4280808080c00284370200200141b0016a200141d8006a360200200141fcabc480003602a8012001410336029c012001410036029001200141003602840120014281808080c02037027c200341ecf2c08000200441024622041b200141fc006a200241d4f2c0800020041b2802101184808080000020012f014e21020b200141fc006a41f9a6c48000410641002802a0a3c6800011858080800000200141fc006a41106a220341bccdc48000411541002802a0a3c6800011858080800000200141d8006a41186a200141fc006a41186a290000370300200141d8006a41106a2003290000370300200141d8006a41086a200141fc006a41086a2900003703002001200129007c370358200120023b017c200141d8006a4120200141fc006a410241002802e0a1c68000118680808000000b200141206a10ad878080002001290320210520012903282106200141106a10bd888080002001290318210720012903102108200110998c808000200129030021092000427f427f200620077c220720072006541b220620012903087c220720072006541b3703082000427f2009427f200520087c220620062005541b22057c220620062005541b370300200141c0016a2480808080000b930201027f23808080800041106b22022480808080002002200036020020022001280214419cc9c48000410e200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a200241acc9c48000108d81808000210120022d000c210002400240200128020022030d00200041ff017141004721010c010b41012101200041ff01710d0020022802082100024020034101470d0020022d000d41ff0171450d0020002d001c4104710d00410121012000280214418ca5c080004101200041186a28020028020c118280808000000d010b2000280214418da5c080004101200041186a28020028020c1182808080000021010b200241106a24808080800020010b100020004200370308200042003703000b2100200128021441d8c8c480004114200141186a28020028020c118280808000000bcb0601017f0240024020002d00000e03010001000b2000280204220110848a808000200141002802c0a3c68000118080808000000b0240024020002d00240e03010001000b200041286a280200220110848a808000200141002802c0a3c68000118080808000000b0240024020002d00480e03010001000b200041cc006a280200220110848a808000200141002802c0a3c68000118080808000000b0240024020002d006c0e03010001000b200041f0006a280200220110848a808000200141002802c0a3c68000118080808000000b0240024020002d0090010e03010001000b20004194016a280200220110848a808000200141002802c0a3c68000118080808000000b0240024020002d00b4010e03010001000b200041b8016a280200220110848a808000200141002802c0a3c68000118080808000000b0240024020002d00d8010e03010001000b200041dc016a280200220110848a808000200141002802c0a3c68000118080808000000b0240024020002d00fc010e03010001000b20004180026a280200220110848a808000200141002802c0a3c68000118080808000000b0240024020002d00a0020e03010001000b200041a4026a280200220110848a808000200141002802c0a3c68000118080808000000b0240024020002d00c4020e03010001000b200041c8026a280200220110848a808000200141002802c0a3c68000118080808000000b0240024020002d00e8020e03010001000b200041ec026a280200220110848a808000200141002802c0a3c68000118080808000000b0240024020002d008c030e03010001000b20004190036a280200220110848a808000200141002802c0a3c68000118080808000000b0240024020002d00b0030e03010001000b200041b4036a280200220110848a808000200141002802c0a3c68000118080808000000b0240024020002d00d4030e03010001000b200041d8036a280200220110848a808000200141002802c0a3c68000118080808000000b0240024020002d00f8030e03010001000b200041fc036a280200220110848a808000200141002802c0a3c68000118080808000000b0240024020002d009c040e03010001000b200041a0046a280200220010848a808000200041002802c0a3c68000118080808000000b0b02000b02000b890501037f23808080800041a0016b22032480808080002003200136023c20034100360238200320023602342003200341346a10bc8a80800002400240024020032802000d00200328020421042003200328023841016a22013602382001200328023c4b0d00200341003a007f200320043602980120034100360294012003200341346a360290012003200341ff006a36029c0120034180016a20034190016a10a087808000024020032d007f450d0020034180016a10a68d8080000c010b200341f0006a41086a220520034180016a41086a280200360200200320032903800137037020032003280238417f6a36023802402003280234220128020422044120490d002001200441606a36020420012001280200220441206a360200200341c0006a41086a22012005280200360200200341c0006a41146a200441086a290000370200200341c0006a411c6a200441106a290000370200200341c0006a41246a200441186a2900003702002003200429000037024c200341086a41086a2001290300370300200341086a41106a2201200341c0006a41106a290300370300200341086a41186a2204200341c0006a41186a290300370300200341086a41206a200341c0006a41206a290300370300200341086a41286a2205200341c0006a41286a2802003602002003200329037037030820022802040d02200020032903083702042000412c6a2005280200360200200041246a200341286a2903003702002000411c6a2004290300370200200041146a20012903003702002000410c6a200341106a290300370200200041003602000c030b200341f0006a10a68d8080000b200041013602000c010b200341086a10a68d808000200041013602000b200341a0016a2480808080000b210020012802144198c3c480004105200141186a28020028020c118280808000000b8f1401047f23808080800041900a6b220224808080800002400240024002400240024002400240024002400240024020012802000e050100020304010b20024180056a200141046a10e48d808000024020012802100d000240200141186a28020022034100480d00200341f5ffffff074f0d06200141146a280200210402402003410b6a417c7122050d00410421010c0b0b41002d00fca3c680001a200541002802c8a3c680001181808080000022010d0a4104200510b280808000000b41fc9bc68000412b200241106a41a89cc6800041b89cc68000108981808000000b200141186a28020022034120470d052002411c6a200141146a280200220141086a290000370200200241106a41146a200141106a2900003702002002412c6a200141186a29000037020020022001290000370214410021010c090b200041023602000c090b20024180056a200141046a10e48d808000200241106a200141106a10838a808000024020022d001022014102460d00200020022f00113b00052000200229021837020c200041076a20022d00133a0000200041146a200241106a41106a2902003702002000411c6a200241106a41186a290200370200200041246a200241106a41206a28020036020020022802142103200041d0006a200241a8056a290200370200200041c8006a20024180056a41206a290200370200200041c0006a20024180056a41186a290200370200200041386a20024180056a41106a290200370200200041306a20024188056a290200370200200020022902800537022820002003360208200020013a0004200041043602000c090b20022802142101200041083602002000200136020420022802a8054129490d0820022802800541002802c0a3c68000118080808000000c080b200241023a00ac04200241023a008804200241023a00e403200241023a00c003200241023a009c03200241023a00f802200241023a00d402200241023a00b002200241023a008c02200241023a00e801200241023a00c401200241023a00a001200241023a007c200241023a0058200241023a0034200241023a001041002104200241003602d00420024100360288052002200141c4016a2205360284052002200141046a360280052002200241d0046a360290052002200241106a36028c0520024180056a10828a808000024020022802d0042203450d002000410836020020002003360204200241106a10b18a8080000c080b20024180056a200241106a41c00410848e8080001a0240200528020022034102460d000240024020030d002002200141c8016a2802002203200141cc016a280200220410e98d8080002002280200210120022802042105200241d8046a200320044100280298a3c6800011858080800000200220053602d4040c010b200141cc016a28020022034120470d05200241dc046a200141c8016a280200220141086a290000370200200241e4046a200141106a290000370200200241ec046a200141186a290000370200200220012900003702d404410021010b200241e8096a41086a200241d0046a41086a290200370300200241e8096a41106a200241d0046a41106a290200370300200241e8096a41186a200241d0046a41186a290200370300200241e8096a41206a200241d0046a41206a290200370300200220013602d004200220022902d0043703e809410121040b200041306a20024180056a41c00410848e8080001a200041286a200241e8096a41206a290300370200200041206a200241e8096a41186a290300370200200041186a200241e8096a41106a290300370200200041106a200241f0096a290300370200200020022903e80937020820002004360204200041053602000c070b200241023a00ac04200241023a008804200241023a00e403200241023a00c003200241023a009c03200241023a00f802200241023a00d402200241023a00b002200241023a008c02200241023a00e801200241023a00c401200241023a00a001200241023a007c200241023a0058200241023a0034200241023a001041002104200241003602d00420024100360288052002200141d0016a360284052002200141106a360280052002200241d0046a360290052002200241106a36028c0520024180056a10898a808000024020022802d0042203450d002000410836020020002003360204200241106a10b18a8080000c070b200241d0046a200141046a10e48d80800020024180056a200241106a41c00410848e8080001a024020012802d00122034102460d000240024020030d00200241086a200141d4016a2802002203200141d8016a280200220410e98d80800020022802082101200228020c2105200241f0096a200320044100280298a3c6800011858080800000200220053602ec090c010b200141d8016a28020022034120470d05200241f4096a200141d4016a280200220141086a290000370200200241fc096a200141106a290000370200200241840a6a200141186a290000370200200220012900003702ec09410021010b200241c0096a41086a200241e8096a41086a290200370300200241c0096a41106a200241e8096a41106a290200370300200241c0096a41186a200241e8096a41186a290200370300200241c0096a41206a200241e8096a41206a290200370300200220013602e809200220022902e8093703c009410121040b200020022902d0043702ec0420004194056a200241f8046a2902003702002000418c056a200241d0046a41206a29020037020020004184056a200241d0046a41186a290200370200200041fc046a200241d0046a41106a290200370200200041f4046a200241d0046a41086a2902003702002000412c6a20024180056a41c00410848e8080001a200041246a200241c0096a41206a2903003702002000411c6a200241c0096a41186a290300370200200041146a200241c0096a41106a2903003702002000410c6a200241c0096a41086a290300370200200020022903c009370204200020043602000c060b41e484c08000412b200241106a419085c08000419086c08000108981808000000b41202003418cc9c4800010a281808000000b41202003418cc9c4800010a281808000000b41202003418cc9c4800010a281808000000b2001428180808010370200200141086a2004200310848e8080001a200241106a41086a200420034100280298a3c6800011858080800000200220033602140b20002002290280053702042000410c6a20024180056a41086a290200370200200041146a20024180056a41106a2902003702002000411c6a20024180056a41186a290200370200200041246a20024180056a41206a2902003702002000412c6a200241a8056a29020037020020022001360210200020022902103702342000413c6a200241106a41086a290200370200200041c4006a200241106a41106a290200370200200041cc006a200241106a41186a290200370200200041d4006a200241106a41206a290200370200200041033602000b200241900a6a2480808080000b8f1401047f23808080800041900a6b220224808080800002400240024002400240024002400240024002400240024020012802000e050100020304010b20024180056a200141046a10e48d808000024020012802100d000240200141186a28020022034100480d00200341f5ffffff074f0d06200141146a280200210402402003410b6a417c7122050d00410421010c0b0b41002d00fca3c680001a200541002802c8a3c680001181808080000022010d0a4104200510b280808000000b41fc9bc68000412b200241106a41a89cc6800041b89cc68000108981808000000b200141186a28020022034120470d052002411c6a200141146a280200220141086a290000370200200241106a41146a200141106a2900003702002002412c6a200141186a29000037020020022001290000370214410021010c090b200041023602000c090b20024180056a200141046a10e48d808000200241106a200141106a10878a808000024020022d001022014102460d00200020022f00113b00052000200229021837020c200041076a20022d00133a0000200041146a200241106a41106a2902003702002000411c6a200241106a41186a290200370200200041246a200241106a41206a28020036020020022802142103200041d0006a200241a8056a290200370200200041c8006a20024180056a41206a290200370200200041c0006a20024180056a41186a290200370200200041386a20024180056a41106a290200370200200041306a20024188056a290200370200200020022902800537022820002003360208200020013a0004200041043602000c090b20022802142101200041083602002000200136020420022802a8054129490d0820022802800541002802c0a3c68000118080808000000c080b200241023a00ac04200241023a008804200241023a00e403200241023a00c003200241023a009c03200241023a00f802200241023a00d402200241023a00b002200241023a008c02200241023a00e801200241023a00c401200241023a00a001200241023a007c200241023a0058200241023a0034200241023a001041002104200241003602d00420024100360288052002200141c4016a2205360284052002200141046a360280052002200241d0046a360290052002200241106a36028c0520024180056a10888a808000024020022802d0042203450d002000410836020020002003360204200241106a10b18a8080000c080b20024180056a200241106a41c00410848e8080001a0240200528020022034102460d000240024020030d002002200141c8016a2802002203200141cc016a280200220410e98d8080002002280200210120022802042105200241d8046a200320044100280298a3c6800011858080800000200220053602d4040c010b200141cc016a28020022034120470d05200241dc046a200141c8016a280200220141086a290000370200200241e4046a200141106a290000370200200241ec046a200141186a290000370200200220012900003702d404410021010b200241e8096a41086a200241d0046a41086a290200370300200241e8096a41106a200241d0046a41106a290200370300200241e8096a41186a200241d0046a41186a290200370300200241e8096a41206a200241d0046a41206a290200370300200220013602d004200220022902d0043703e809410121040b200041306a20024180056a41c00410848e8080001a200041286a200241e8096a41206a290300370200200041206a200241e8096a41186a290300370200200041186a200241e8096a41106a290300370200200041106a200241f0096a290300370200200020022903e80937020820002004360204200041053602000c070b200241023a00ac04200241023a008804200241023a00e403200241023a00c003200241023a009c03200241023a00f802200241023a00d402200241023a00b002200241023a008c02200241023a00e801200241023a00c401200241023a00a001200241023a007c200241023a0058200241023a0034200241023a001041002104200241003602d00420024100360288052002200141d0016a360284052002200141106a360280052002200241d0046a360290052002200241106a36028c0520024180056a10868a808000024020022802d0042203450d002000410836020020002003360204200241106a10b18a8080000c070b200241d0046a200141046a10e48d80800020024180056a200241106a41c00410848e8080001a024020012802d00122034102460d000240024020030d00200241086a200141d4016a2802002203200141d8016a280200220410e98d80800020022802082101200228020c2105200241f0096a200320044100280298a3c6800011858080800000200220053602ec090c010b200141d8016a28020022034120470d05200241f4096a200141d4016a280200220141086a290000370200200241fc096a200141106a290000370200200241840a6a200141186a290000370200200220012900003702ec09410021010b200241c0096a41086a200241e8096a41086a290200370300200241c0096a41106a200241e8096a41106a290200370300200241c0096a41186a200241e8096a41186a290200370300200241c0096a41206a200241e8096a41206a290200370300200220013602e809200220022902e8093703c009410121040b200020022902d0043702ec0420004194056a200241f8046a2902003702002000418c056a200241d0046a41206a29020037020020004184056a200241d0046a41186a290200370200200041fc046a200241d0046a41106a290200370200200041f4046a200241d0046a41086a2902003702002000412c6a20024180056a41c00410848e8080001a200041246a200241c0096a41206a2903003702002000411c6a200241c0096a41186a290300370200200041146a200241c0096a41106a2903003702002000410c6a200241c0096a41086a290300370200200020022903c009370204200020043602000c060b41e484c08000412b200241106a419085c08000419086c08000108981808000000b41202003418cc9c4800010a281808000000b41202003418cc9c4800010a281808000000b41202003418cc9c4800010a281808000000b2001428180808010370200200141086a2004200310848e8080001a200241106a41086a200420034100280298a3c6800011858080800000200220033602140b20002002290280053702042000410c6a20024180056a41086a290200370200200041146a20024180056a41106a2902003702002000411c6a20024180056a41186a290200370200200041246a20024180056a41206a2902003702002000412c6a200241a8056a29020037020020022001360210200020022902103702342000413c6a200241106a41086a290200370200200041c4006a200241106a41106a290200370200200041cc006a200241106a41186a290200370200200041d4006a200241106a41206a290200370200200041033602000b200241900a6a2480808080000bcd03010a7f23808080800041c0006b2201248080808000024002400240200010b487808000450d00200142919fd78da1a1ad84ff003703282001429ceccef7a6c0dedd2037032020014282fceae49dd9e2861d370338200142de8c84a1ecd0a6d30c370330200141146a200141206a10e2888080000240024020012802142202418080808078470d00410821034100210241002104410821050c010b200128021821030240200128021c22040d0041082105410021040c010b200441b3e6cc194b0d02200441286c2206417f4c0d024100210741002d00fca3c680001a200641002802c8a3c68000118180808000002205450d032004210820032109034020062007460d0120092903202100200520076a220a2009290300370300200a41186a200941186a290300370300200a41106a200941106a290300370300200a41086a200941086a290300370300200a41206a2000370300200741286a2107200941286a21092008417f6a22080d000b0b200120043602102001200536020c20012004360208200120043602282001200336022420012002360220200141206a200141086a4100200910b3878080000b200141c0006a2480808080000f0b10ae80808000000b4108200610b280808000000bcf0201027f23808080800041306b2203248080808000024002400240024020022d0000410b470d002003410c6a200241086a2802002002410c6a280200108b8a80800020032d000c0d0041002d00fca3c680001a410c41002802c8a3c68000118180808000002204450d0241002d00fca3c680001a412041002802c8a3c68000118180808000002202450d032002200329000d370000200241186a200341256a290000370000200241106a2003411d6a290000370000200241086a200341156a290000370000200041013a00282000410136022420002004360220200042808080801037031820004280808080c0003703102000427f370308200042e4003703002004412036020820042002360204200441203602000c010b2000418080808078360210200041003b01000b200341306a2480808080000f0b4104410c10b280808000000b4101412010b280808000000bb40202017f027e4101210242cd0b21034288da9fed002104024002400240024002400240024002400240024020012d0000417f6a0e0b0009010102030405060607000b2001410c6a3502004283037e42d0b8c5007c21040c070b42db8b04210342f88ef9fab10221040c070b2001410c6a35020042d2eb84307e4280e585017c2104420021030c060b2001410c6a35020042d0a2fa2f7e4298e383017c2104420021030c050b41012102200128020441016a2201417f20011bad220442c6007e4286017c2103200442d1a6983c7e42e0d1fe017c21040c040b2001410c6a35020042ef0a7e42efb29c017c21040c020b4200210342f889a43421040c020b42db8b04210342f8b999efc70221040c010b41002102420021030b200041003a0011200020023a001020002003370308200020043703000b840301077f4101210202400240024002402001280208220341016a2204200128020422054d0d000c010b200320054f0d012001200436020802400240024002400240024002402001280200220620036a2d000022074103710e0400010203000b20074102762108410021020c060b200341026a220320054b0d05200120033602082004417f470d02417f200341dcc1c28000109681808000000b200341046a220320054b0d04200120033602082004417d490d022004200341dcc1c28000109681808000000b20074104490d020c030b200620046a2d000041087420077241ffff03712201410276210820014180024921020c020b200620046a220141026a2d000041187420012f000041087472200772220141027621082001418080044921020c010b200341056a220320054b0d00200120033602082004417c4f0d02200620046a28000022084180808080044921020b20002008360204200020023602000f0b2003200541ecc1c2800010f980808000000b2004200341dcc1c28000109681808000000bae0201057f024002402001280200220128020422020d00410121030c010b20012002417f6a22043602044101210320012001280200220541016a36020002400240024002400240024020052d000022064103710e0400010402000b20064102762104410021030c050b20040d010c040b20064104490d020c030b20012002417e6a3602042001200541026a36020020052d000141087420067241ffff03712201410276210420014180024921030c020b20024104490d0120012002417c6a3602042001200541046a360200200541036a2d000041187420052f000141087472200672220141027621042001418080044921030c010b20024105490d0020012002417b6a3602042001200541056a360200200528000122044180808080044921030b20002004360204200020033602000ba90201057f02400240200128020422020d00410121030c010b20012002417f6a22043602044101210320012001280200220541016a36020002400240024002400240024020052d000022064103710e0400010402000b20064102762104410021030c050b20040d010c040b20064104490d020c030b20012002417e6a3602042001200541026a36020020052d000141087420067241ffff03712201410276210420014180024921030c020b20024104490d0120012002417c6a3602042001200541046a360200200541036a2d000041187420052f000141087472200672220141027621042001418080044921030c010b20024105490d0020012002417b6a3602042001200541056a360200200528000122044180808080044921030b20002004360204200020033602000bf40302067f017e024002400240200128020022012802042202450d0020012002417f6a22033602044101210420012001280200220541016a2206360200024002400240024020052d000022074103710e0400010203000b2007410276ad2108410021040c050b2003450d0320012002417e6a3602042001200541026a36020020052d000141087420077241ffff037122014180024921042001410276ad21080c040b20024104490d0320012002417c6a3602042001200541046a360200200541036a2d000041187420052f0001410874722007722201418080044921042001410276ad21080c030b024002400240200741027622030e050102020200020b20024109490d022001200241776a3602042001200541096a36020020052900012208428080808080808080015421040c040b20024105490d0120012002417b6a3602042001200541056a360200200535000122084280808080045421040c030b200741134b0d00200341046a21072002417e6a2104420021084100210203402004417f460d01200120043602042001200641016a22053602002004417f6a210420063100002002410374413871ad86200884210820052106200241016a220241ff01712007490d000b410021042008427f412820034103746b413871ad88560d020b410121040b0b2000200837030820002004ad3703000bef0302067f017e02400240024020012802042202450d0020012002417f6a22033602044101210420012001280200220541016a2206360200024002400240024020052d000022074103710e0400010203000b2007410276ad2108410021040c050b2003450d0320012002417e6a3602042001200541026a36020020052d000141087420077241ffff037122014180024921042001410276ad21080c040b20024104490d0320012002417c6a3602042001200541046a360200200541036a2d000041187420052f0001410874722007722201418080044921042001410276ad21080c030b024002400240200741027622030e050102020200020b20024109490d022001200241776a3602042001200541096a36020020052900012208428080808080808080015421040c040b20024105490d0120012002417b6a3602042001200541056a360200200535000122084280808080045421040c030b200741134b0d00200341046a21072002417e6a2104420021084100210203402004417f460d01200120043602042001200641016a22053602002004417f6a210420063100002002410374413871ad86200884210820052106200241016a220241ff01712007490d000b410021042008427f412820034103746b413871ad88560d020b410121040b0b2000200837030820002004ad3703000b950301037f0240200028020022022802002200413f4b0d00200041027421020240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a20023a00000f0b0240200041ffff004b0d002000410274410172210202402001280200200128020822006b41014b0d0020012000410210b182808000200128020821000b2001200041026a360208200128020420006a20023b00000f0b0240200041ffffffff034b0d002000410274410272210202402001280200200128020822006b41034b0d0020012000410410b182808000200128020821000b2001200041046a360208200128020420006a20023600000f0b02402001280200220320012802082200470d0020012000410110b18280800020012802002103200128020821000b2001280204220420006a41033a00002001200041016a2200360208200228020021020240200320006b41034b0d0020012000410410b18280800020012802042104200128020821000b2001200041046a360208200420006a20023600000bb80404017f017e047f017e23808080800041206b22022480808080000240024020002802002903002203423f560d002003a741027421040240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a20043a00000c010b0240200342ffff00560d002003a7410274410172210402402001280200200128020822006b41014b0d0020012000410210b182808000200128020821000b2001200041026a360208200128020420006a20043b00000c010b0240200342ffffffff03560d002003a7410274410272210402402001280200200128020822006b41034b0d0020012000410410b182808000200128020821000b2001200041046a360208200128020420006a20043600000c010b4113200379a741037622054102746b21060240200128020020012802082204470d0020012004410110b182808000200128020821040b2001200441016a22003602082001280204220720046a20063a0000200541786a21050340200321080240024020012802002000460d00200021040c010b20012000410110b18280800020012802042107200128020821040b2001200441016a2200360208200720046a2008a73a000020084208882103200541016a22050d000b200220033703002008428002540d0020024200370214200241b0cbc480003602102002410136020c200241f0cbc480003602084100200241f8cbc48000200241086a4180ccc4800010a389808000000b200241206a2480808080000ba80101017f23808080800041106b2201248080808000200142888080808001370200200142808080808001370208200041c4006a200110fa86808000200041106a4293888c8f89fdc6ec9e7f370300200042a5e9e3ab9e929adc2c370308200041c0006a410036020020004280808080c000370338200041d8006a410036020020004280808080c000370350200041ef80808000360218200041063a0000200141106a2480808080000bbc0201017f23808080800041f0016b220424808080800020044197ccc48000410910a682808000200441b0cbc4800041002002200310a782808000200441a0ccc4800041062001412010a782808000200441a6ccc480004107200141c0016a412010a782808000200441d0016a41186a22014200370300200441d0016a41106a22034200370300200441d0016a41086a22024200370300200442003703d001200441b0cbc480004100200441d0016a412010a882808000200041186a2001290300370000200041106a2003290300370000200041086a2002290300370000200020042903d001370000410021010340200420016a220041003a0000200041016a41003a0000200041026a41003a0000200041036a41003a0000200041046a41003a0000200141056a220141c801470d000b200441f0016a2480808080000ba40802017f047e23808080800041c0076b2204248080808000200341e0cfc4800041092002412010a78280800020044180036a200341d00110848e8080001a200441d8066a4200370300200441d0066a4200370300200441c8066a4200370300200441a0066a41206a4200370300200441b8066a4200370300200441b0066a4200370300200441a8066a4200370300200442003703a00620044180036a4190ccc480004107200441a0066a41c00010a882808000200441c0016a200441a0066a10b4818080002004200441c0016a10b281808000200441206a200441c0016a41a00110848e8080001a41002102034020044180036a20026a220341003a0000200341016a41003a0000200341026a41003a0000200341036a41003a0000200341046a41003a0000200241056a220241c801470d000b20044180066a41186a2203200141186a29000037030020044180066a41106a200141106a29000037030020044180066a41086a200141086a290000370300200420012900003703800620044180036a20044180066a10b18180800002400240200428028003450d00200441e9016a2004418d036a290000370000200441c0016a41306a20044194036a29000037000020042004290085033700e10120042d0084032102200441a0066a2004419c036a41880110848e8080001a200441c0016a41096a20044180066a41096a290000370000200441c0016a41116a20044180066a41116a290000370000200441c0016a41186a200329000037000020042004290081063700c101200420023a00e001200420042d0080063a00c001200441c0016a41386a200441a0066a41880110848e8080001a20044180036a41206a420037030020044180036a41186a420037030020044180036a41106a420037030020044180036a41086a420037030020044180036a41306a41002902c4c9c48000220537030020044180036a41386a41002902ccc9c480002206370300200441c0036a41002902d4c9c480002207370300200441c8036a41002902dcc9c480002208370300200441d8036a2005370300200441e0036a2006370300200441e8036a2007370300200441f0036a20083703002004420037038003200441002902bcc9c4800022053703a803200420053703d00320044198046a420037030020044190046a420037030020044180036a4188016a420037030020044180046a4200370300200442003703f803200441a0066a20044180036a41a00110848e8080001a0240200441c0016a41206a200441a0066a10b58180800041ff01710d0020044180036a41c0016a200441c0016a41c00110848e8080001a20044180036a200441c00110848e8080001a200041046a20044180036a41800310848e8080001a200041003602000c020b20004101360200200041013a00040c010b20004101360200200041013a00040b200441c0076a2480808080000be80503067f017e077f2380808080004180016b2202248080808000200241206a2203200141086a28020036020020024180013a002420024100360214200242808080801037020c20022001290200370218200241c0006a2002410c6a200220022002200210c68a808000024002400240024020022802742204418080808078460d00200241286a41106a2205200241c0006a41106a290300370300200241286a41086a200241c0006a41086a2903003703002002200229034037032820022802582106200228025c21072002290360210820022802682109200228026c210a2002280270210b2002280278210c200228027c210d02400240200328020022012002410c6a41106a280200220e4f0d002002280218210f0340200f20016a2d000041776a220341174b0d024101200374419380800471450d02200e200141016a2201470d000b2002200e3602200b200020022903283703002000200d36023c2000200c360238200020043602342000200b3602302000200a36022c20002009360228200020083703202000200736021c20002006360218200041106a2005290300370300200041086a200241286a41086a290300370300200228020c450d03200228021041002802c0a3c68000118080808000000c030b200220013602202002200f200e200e200141016a2201200e2001491b10878380800041002d00fca3c680001a200228020421032002280200210e411441002802c8a3c68000118180808000002201450d032001200e36020c200141163602002000418080808078360234200020013602002001200336021002402006450d00200741002802c0a3c68000118080808000000b02402009450d00200a41002802c0a3c68000118080808000000b2004450d01200c41002802c0a3c68000118080808000000c010b2000418080808078360234200020022802403602000b200228020c450d00200228021041002802c0a3c68000118080808000000b20024180016a2480808080000f0b4104411410b280808000000b9d3004077f017e0b7f017e23808080800041c0026b2206248080808000200128020c21070240024002400240024002400240024002400240024002400240024002400240024002400240200141146a2802002208200141106a28020022094f0d002001410c6a210a0340200720086a2d0000220b41776a220c41174b0d024101200c74419380800471450d022001200841016a220836021420092008470d000b200921080b200641a0016a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a20062802a401210c20062802a0012101411441002802c8a3c68000118180808000002208450d012008200136020c200841053602002000418080808078360234200020083602002008200c3602100c110b024002400240200b41db00460d00200b41fb00460d012001200641bf026a4184d5c4800010d58a80800021080c100b200120012d0018417f6a220c3a0018200841016a2108200c41ff0171450d10200120083602140240200820094f0d000340200720086a2d0000220b41776a220c41174b0d0d4101200c74419380800471450d0d2001200841016a220836021420092008470d000b200921080b200641e8006a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a200628026c210c20062802682109411441002802c8a3c68000118180808000002208450d012008200936020c200841023602002008200c3602104200210d418080808078210e0c0c0b200120012d0018417f6a220c3a0018200841016a2108200c41ff0171450d0920012008360214024020082009490d00418080808078210f4180808080782110418080808078210e0c040b20064188026a4104722111418080808078210f4180808080782110418080808078210e410021124100210b0340200a28020021070240024002400240024002400240024002400240024002400240024002400240024002400240024003400240200720086a2d0000220c41776a0e24000003030003030303030303030303030303030303030300030303030303030303030304020b2001200841016a220836021420092008470d000b200921080c180b200c41fd00460d050b200b4101710d012008210b0c0a0b0240200b4101710d002008210b0c0b0b2001200841016a220b360214200b20094f0d0103402007200b6a2d0000220c41776a220841174b0d0a4101200874419380800471450d0a2001200b41016a220b3602142009200b470d000b200921080c020b41012112200641f8006a200720092009200841016a220820092008491b1087838080004100210c41002d00fca3c680001a200628027c210920062802782107411441002802c8a3c68000118180808000002208450d032008200736020c20084108360200200820093602100c150b200841016a21080b4101211220064198016a200720092009200841016a220820092008491b1087838080004100210c41002d00fca3c680001a200628029c0121092006280298012107411441002802c8a3c68000118180808000002208450d022008200736020c20084105360200200820093602100c130b024020124101710d004100210c41f8d7c48000410610e38a8080002108410121120c130b200f418080808078460d0402402010418080808078470d004182d8c48000410d10e38a80800021080c040b200e418080808078460d02200641d8016a41106a200641f0016a41106a280200360200200641d8016a41086a200641f0016a41086a290200370300200620062902f0013703d801201321080c160b4104411410b280808000000b4104411410b280808000000b419ccfc48000410810e38a80800021082010450d00201441002802c0a3c68000118080808000000b20104180808080784621124101210c0240200f0d004100210f0c0f0b201541002802c0a3c68000118080808000000c0e0b4100210c41fed7c48000410410e38a8080002108418080808078210f410121120c0d0b200c4122460d01200c41fd00470d004101211220064190016a200720092009200b41016a220820092008491b1087838080004100210c41002d00fca3c680001a20062802940121092006280290012107411441002802c8a3c68000118180808000002208450d032008200736020c20084115360200200820093602100c0c0b4101211220064180016a200720092009200b41016a220820092008491b1087838080004100210c41002d00fca3c680001a20062802840121092006280280012107411441002802c8a3c68000118180808000002208450d012008200736020c20084111360200200820093602100c0b0b200141003602082001200b41016a36021420064188026a200a2001108a83808000200628028c022108024002402006280288024102460d00200641b4026a200820062802900210fc8980800020062d00b402450d0120062802b80221080b4100210c410121120c0b0b02400240024002400240024020062d00b5020e0400010203000b20124101710d034100210c0240200110d78a8080002208450d00410121120c100b410121122001200820082008200810f68a80800022080d0f0c090b0240200f418080808078460d004100210c4101211241fed7c48000410410e78a80800021080c0f0b0240200110d78a80800022080d0020064188026a2001200820082008200810f78a808000200628028802211320062802a002220f418080808078470d07201321080b4100210c41012112418080808078210f0c0e0b02402010418080808078460d004100210c410121124182d8c48000410d10e78a80800021080c0e0b200110d78a80800022080d0620064188026a2001200820082008200810f58a808000200628028c0221082006280288022210418080808078460d062006280290022116200821140c070b0240200e418080808078460d00410121124100210c419ccfc48000410810e78a80800021080c0f0b0240200110d78a8080002208450d00410121124100210c0c100b20064188026a2001200820082008200810f88a808000200628028c022117200628028802220e418080808078460d0120062802900221180c060b4100210c4101211241f8d7c48000410610e78a80800021080c0b0b410121124100210c201721080c0d0b4104411410b280808000000b4104411410b280808000000b200641f0016a41106a201141106a280200360200200641f0016a41086a201141086a290200370300200620112902003703f00120062802a402211520062903a80221190c010b4100210c4101211241808080807821100c060b4101210b20012802142208200128021022094f0d030c000b0b4104411410b280808000000b4104411410b280808000000b200a28020021070b4101211220064188016a200720092009200841016a220820092008491b1087838080004100210c41002d00fca3c680001a200628028c0121092006280288012107411441002802c8a3c68000118180808000002208450d012008200736020c20084103360200200820093602100b200e418080808078470d010c020b4104411410b280808000000b200e450d00201741002802c0a3c68000118080808000000b02402012450d00201041ffffffff0771450d00201441002802c0a3c68000118080808000000b0240200c0d00200f41808080807872418080808078460d00201541002802c0a3c68000118080808000000b418080808078210e0b200120012d001841016a3a0018200110d98a808000210c02400240200e418080808078460d00200c0d01200641a8016a41106a200641d8016a41106a280200360200200641a8016a41086a200641d8016a41086a290300370300200620062903d8013703a8012018ad422086210d0c060b200c450d060240200c2802000d00200c41086a280200450d00200c28020441002802c0a3c68000118080808000000b200c41002802c0a3c68000118080808000000c060b0240200f450d00201541002802c0a3c68000118080808000000b02402010450d00201441002802c0a3c68000118080808000000b0240200e450d00201741002802c0a3c68000118080808000000b200c21080c050b200641f0006a200720092009200820092008491b10878380800041002d00fca3c680001a2006280274210c20062802702101411441002802c8a3c680001181808080000022080d064104411410b280808000000b0240200b41dd00460d004200210d418080808078210e2001200820082008200810f68a80800022080d01200128020c2109024002400240024002400240024002400240200128021422082001280210220c4f0d0003400240200920086a2d0000220741776a0e24000005050005050505050505050505050505050505050500050505050505050505050503040b2001200841016a2208360214200c2008470d000b200c21080b200641106a2009200c200c200841016a2208200c2008491b10878380800041002d00fca3c680001a2006280214210c20062802102109411441002802c8a3c68000118180808000002208450d032008200936020c200841023602002008200c3602100c090b2001200841016a220836021402402008200c4f0d000340200920086a2d0000220b41776a220741174b0d084101200774419380800471450d082001200841016a2208360214200c2008470d000b200c21080b200641d8006a2009200c200c200841016a2208200c2008491b10878380800041002d00fca3c680001a200628025c210c20062802582109411441002802c8a3c68000118180808000002208450d042008200936020c200841053602002008200c3602100c080b200741dd00460d040b200641086a2009200c200c200841016a2208200c2008491b10878380800041002d00fca3c680001a200628020c210c20062802082109411441002802c8a3c68000118180808000002208450d012008200936020c200841073602002008200c3602100c060b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b410141f0d7c4800041c8cec4800010e68a80800021080c020b024002400240024002400240024002400240200b41dd00470d00200641e0006a2009200c200c200841016a2208200c2008491b10878380800041002d00fca3c680001a2006280264210c20062802602109411441002802c8a3c68000118180808000002208450d012008200936020c200841153602002008200c3602100c0a0b20064188026a2001200820082008200810f78a808000200628028802210820062802a002220f418080808078460d0920064180026a20064188026a41146a280200360200200641f8016a20064194026a2902003703002006200629028c023703f00120062802a4022115200128020c210702400240024002402001280214220c200128021022094f0d0020062903a8022119034002402007200c6a2d0000220b41776a0e24000005050005050505050505050505050505050505050500050505050505050505050503040b2001200c41016a220c3602142009200c470d000b2009210c0b200641206a200720092009200c41016a220820092008491b10878380800041002d00fca3c680001a2006280224210c20062802202109411441002802c8a3c68000118180808000002208450d042008200936020c200841023602002008200c3602100c090b2001200c41016a220c3602140240200c20094f0d0003402007200c6a2d0000220a41776a220b41174b0d094101200b74419380800471450d092001200c41016a220c3602142009200c470d000b2009210c0b200641c8006a200720092009200c41016a220820092008491b10878380800041002d00fca3c680001a200628024c210c20062802482109411441002802c8a3c68000118180808000002208450d052008200936020c200841053602002008200c3602100c080b200b41dd00460d050b200641186a200720092009200c41016a220820092008491b10878380800041002d00fca3c680001a200628021c210c20062802182109411441002802c8a3c68000118180808000002208450d022008200936020c200841073602002008200c3602100c060b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b410241f0d7c4800041c8cec4800010e68a80800021080c010b0240024002400240024002400240200a41dd00470d00200641d0006a200720092009200c41016a220820092008491b10878380800041002d00fca3c680001a2006280254210c20062802502109411441002802c8a3c68000118180808000002208450d012008200936020c200841153602002008200c3602100c070b20064188026a2001200820082008200810f58a80800002402006280288022210418080808078470d00200628028c0221080c070b200628028c022114200128020c2107024002400240024002402001280214220c200128021022094f0d002006280290022116034002402007200c6a2d0000220b41776a0e24000005050005050505050505050505050505050505050500050505050505050505050503040b2001200c41016a220c3602142009200c470d000b2009210c0b200641306a200720092009200c41016a220820092008491b10878380800041002d00fca3c680001a2006280234210c20062802302109411441002802c8a3c68000118180808000002208450d052008200936020c200841023602002008200c3602100c090b2001200c41016a220c3602140240200c20094f0d0003402007200c6a2d0000220a41776a220b41174b0d094101200b74419380800471450d092001200c41016a220c3602142009200c470d000b2009210c0b200641386a200720092009200c41016a220820092008491b10878380800041002d00fca3c680001a200628023c210c20062802382109411441002802c8a3c68000118180808000002208450d062008200936020c200841053602002008200c3602100c080b200b41dd00460d010b200641286a200720092009200c41016a220820092008491b10878380800041002d00fca3c680001a200628022c210c20062802282109411441002802c8a3c68000118180808000002208450d032008200936020c200841073602002008200c3602100c060b410341f0d7c4800041c8cec4800010e68a80800021080c050b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b0240200a41dd00470d00200641c0006a200720092009200c41016a220820092008491b10878380800041002d00fca3c680001a2006280244210c20062802402109411441002802c8a3c68000118180808000002208450d032008200936020c200841153602002008200c3602100c010b20064188026a2001200820082008200810f88a8080000240200628028802220c418080808078470d00200628028c0221080c010b200641c0016a41086a200641f0016a41086a290300370300200641c0016a41106a200641f0016a41106a280200360200200620062903f0013703c001200635029002422086210d200628028c022117200c210e0c050b2010450d00201441002802c0a3c68000118080808000000b200f450d02201541002802c0a3c68000118080808000000c020b4104411410b280808000000b4200210d418080808078210e410041f0d7c4800041c8cec4800010e68a80800021080b0b200120012d001841016a3a0018200110da8a808000210c02400240200e418080808078460d00200c0d01200641a8016a41106a200641c0016a41106a280200360200200641a8016a41086a200641c0016a41086a290300370300200620062903c0013703a8010c020b200c450d020240200c2802000d00200c41086a280200450d00200c28020441002802c0a3c68000118080808000000b200c41002802c0a3c68000118080808000000c020b0240200f450d00201541002802c0a3c68000118080808000000b02402010450d00201441002802c0a3c68000118080808000000b0240200e450d00201741002802c0a3c68000118080808000000b200c21080c010b200020062903a801370204200020163602302000201436022c20002010360228200020193703202000201536021c2000200f3602182000200e36023420002008360200200041146a200641b8016a2802003602002000410c6a200641b0016a2903003702002000200d2017ad843703380c030b2008200110d68a80800021082000418080808078360234200020083602000c020b2006200720092009200820092008491b10878380800041002d00fca3c680001a2006280204210c20062802002101411441002802c8a3c680001181808080000022080d004104411410b280808000000b2008200136020c200841183602002000418080808078360234200020083602002008200c3602100b200641c0026a2480808080000bb50101037f23808080800041106b2202248080808000200241086a200028020c200041106a28020022032003200041146a28020041016a220020032000491b10878380800041002d00fca3c680001a200228020c2103200228020821040240411441002802c8a3c680001181808080000022000d004104411410b280808000000b2000200436020c2000200129020037020020002003360210200041086a200141086a280200360200200241106a24808080800020000b950501077f23808080800041106b220224808080800041002103024002400240200041146a2802002204200041106a28020022054f0d000240200028020c220620046a2d0000220741e500460d00200741c500460d002007412e470d012000200441016a22083602140240200128020822072001280200470d0020012007109c86808000200128020821070b200128020420076a412e3a00002001200128020841016a2207360208024002400240200820054f0d00200441026a2104200620086a2d0000220841506a41ff017141094b0d0120002004360214024020072001280200470d0020012007109c86808000200128020821070b200128020420076a20083a00002001200128020841016a2207360208200420054f0d040340200620046a2d0000220841506a41ff017141094b0d032000200441016a2204360214024020072001280200470d0020012007109c86808000200128020821070b200128020420076a20083a00002001200128020841016a220736020820052004470d000c050b0b200241086a200620052005200441026a220020052000491b10878380800041002d00fca3c680001a200228020c210020022802082101411441002802c8a3c68000118180808000002203450d042003200136020c20034105360200200320003602100c030b2002200620052005200420052004491b10878380800041002d00fca3c680001a2002280204210020022802002101411441002802c8a3c68000118180808000002203450d042003200136020c2003410d360200200320003602100c020b200841207241e500470d0120002008200110c98a80800021030c010b20002007200110c98a80800021030b200241106a24808080800020030f0b4104411410b280808000000b4104411410b280808000000b970501067f23808080800041106b2203248080808000200041146a22042004280200220541016a2204360200024002402001418001490d002001413f7141807f7221062001410676414072210702402002280200200228020822086b41014b0d0020022008410210ab86808000200228020821080b2002200841026a2201360208200228020420086a220820063a0001200820073a00000c010b0240200228020822082002280200470d0020022008109c86808000200228020821080b200228020420086a20013a00002002200228020841016a22013602080b02402004200041106a2802004f0d000240024002400240200028020c20046a2d0000220441556a0e03000401040b2000200541026a36021420012002280200470d020c010b2000200541026a36021420012002280200470d010b20022001109c86808000200228020821010b200228020420016a20043a00002002200228020841016a3602080b200341086a2000200210ca8a808000024002400240024020032d00080d0020032d000941506a41ff0171410a4f0d014100210520002802142201200028021022064f0d02200028020c21070340200720016a2d0000220841506a41ff017141094b0d032000200141016a22013602140240200228020822042002280200470d0020022004109c86808000200228020821040b200228020420046a20083a00002002200228020841016a36020820062001470d000c030b0b200328020c21050c010b2003200028020c2000280210200028021410878380800041002d00fca3c680001a2003280204210220032802002100411441002802c8a3c68000118180808000002205450d012005200036020c2005410d360200200520023602100b200341106a24808080800020050f0b4104411410b280808000000b850301047f23808080800041106b2203248080808000024002400240200141146a2802002204200141106a2802002205490d00200341086a200128020c2005200410878380800041002d00fca3c680001a200328020c210220032802082104411441002802c8a3c68000118180808000002201450d022001200436020c200141053602002000200136020420012002360210410121010c010b2001200441016a36021402400240200128020c20046a2c00002201417f4a0d002001413f7141807f722105200141c00171410676414072210602402002280200200228020822046b41014b0d0020022004410210ab86808000200228020821040b2002200441026a360208200228020420046a220220053a0001200220063a00000c010b0240200228020822042002280200470d0020022004109c86808000200228020821040b200228020420046a20013a00002002200228020841016a3602080b200020013a0001410021010b200020013a0000200341106a2480808080000f0b4104411410b280808000000bd204010a7f23808080800041106b2205248080808000200141146a22062006280200220741016a220836020002400240024002402008200141106a28020022094f0d00200128020c20086a210a200720096b41016a210b41002106024003400240200a20066a2d0000220c41506a220d41ff0171220e410a490d00024020060d00200720066a41016a21080c040b200420066b21060240200c41207241e500460d002000200120022003200610cc8a8080000c050b2000200120022003200610cd8a8080000c040b024020034298b3e6cc99b3e6cc19580d0020034299b3e6cc99b3e6cc19520d02200e41054b0d020b2001200720066a41026a3602142003420a7e200dad42ff01837c2103200b200641016a22066a0d000b2000200120022003200820046a20096b10cc8a8080000c020b2000200120022003200420066b10ce8a8080000c010b2009200841016a220620092006491b2106024020082009490d00200541086a200128020c2009200610878380800041002d00fca3c680001a200528020c210d20052802082101411441002802c8a3c68000118180808000002206450d022006200136020c2006410536020020002006360204200041013602002006200d3602100c010b2005200128020c2009200610878380800041002d00fca3c680001a2005280204210d20052802002101411441002802c8a3c68000118180808000002206450d022006200136020c2006410d36020020002006360204200041013602002006200d3602100b200541106a2480808080000f0b4104411410b280808000000b4104411410b280808000000bd70304017f017c017f017c23808080800041106b22052480808080002003ba21060240024002400240024002400240024020042004411f7522077320076b220741b502490d0003402006440000000000000000610d072004417f4a0d02200644a0c8eb85f3cce17fa32106200441b4026a22042004411f7522077320076b220741b4024b0d000b0b200741037441c0e2c180006a2b030021082004417f4a0d0120062008a321060c050b2005200128020c200141106a280200200141146a28020010878380800041002d00fca3c680001a2005280204210720052802002101411441002802c8a3c68000118180808000002204450d022004200136020c2004410e36020020002004360204200420073602100c010b20062008a222069944000000000000f07f620d03200541086a200128020c200141106a280200200141146a28020010878380800041002d00fca3c680001a200528020c210720052802082101411441002802c8a3c68000118180808000002204450d022004200136020c2004410e36020020002004360204200420073602100b410121040c030b4104411410b280808000000b4104411410b280808000000b2000200620069a20021b390308410021040b20002004360200200541106a2480808080000bed0401077f23808080800041106b220524808080800041012106200141146a22072007280200220741016a220836020002402008200141106a28020022094f0d004101210602400240200128020c20086a2d000041556a0e03010200020b410021060b2001200741026a22083602140b02400240024002400240024002400240200820094f0d002001200841016a2207360214200128020c220a20086a2d000041506a41ff01712208410a4f0d010240200720094f0d000340200a20076a2d000041506a41ff0171220b410a4f0d012001200741016a22073602140240200841cb99b3e6004c0d00200841cc99b3e600470d07200b41074b0d070b2008410a6c200b6a210820092007470d000b0b20060d02200420086b2207411f75418080808078732007200841004a2007200448731b21070c030b200541086a200128020c2009200810878380800041002d00fca3c680001a200528020c210820052802082101411441002802c8a3c68000118180808000002207450d042007200136020c200741053602002000200736020420004101360200200720083602100c060b2005200a2009200710878380800041002d00fca3c680001a2005280204210820052802002101411441002802c8a3c68000118180808000002207450d042007200136020c2007410d3602002000200736020420004101360200200720083602100c050b200420086a2207411f7541808080807873200720084100482007200448731b21070b2000200120022003200710cc8a8080000c030b200020012002200350200610d38a8080000c020b4104411410b280808000000b4104411410b280808000000b200541106a2480808080000b850101047f02400240200141146a2802002205200141106a28020022064f0d00200128020c210702400340200720056a2d0000220841506a41ff017141094b0d012001200541016a220536021420062005470d000c020b0b200841207241e500460d010b2000200120022003200410cc8a8080000f0b2000200120022003200410cd8a8080000bd40804067f017e017f017e23808080800041306b220324808080800002400240024002400240024002400240024002400240024002400240200141146a2802002204200141106a28020022054f0d002001200441016a2206360214200128020c220720046a2d000022084130470d040240200620054f0d00200720066a2d0000220641506a41ff0171410a490d042006412e460d02200641c500460d03200641e500460d030b4200428080808080808080807f20021b21090c0c0b200341186a200128020c2005200410878380800041002d00fca3c680001a200328021c210620032802182105411441002802c8a3c68000118180808000002201450d042001200536020c200141053602002000200136020420004104360200200120063602100c0c0b200341206a200120024200410010cb8a8080002003280220450d0920002003280224360204200041043602000c0b0b200341206a200120024200410010cd8a8080002003280220450d0820002003280224360204200041043602000c0a0b200341086a200720052005200441026a220120052001491b10878380800041002d00fca3c680001a200328020c210620032802082105411441002802c8a3c68000118180808000002201450d022001200536020c2001410d3602002000200136020420004104360200200120063602100c090b02402008414f6a41ff01714109490d00200341106a20072005200610878380800041002d00fca3c680001a2003280214210620032802102105411441002802c8a3c68000118180808000002201450d032001200536020c2001410d3602002000200136020420004104360200200120063602100c090b200841506aad42ff01832109200620054f0d0403400240200720066a2d0000220a41506a220441ff01712208410a490d0002400240200a412e460d00200a41c500460d01200a41e500460d010c080b41002106200341206a200120022009410010cb8a8080002003280220450d0620002003280224360204200041043602000c0b0b41002106200341206a200120022009410010cd8a8080002003280220450d0520002003280224360204200041043602000c0a0b0240024020094299b3e6cc99b3e6cc19540d0020094299b3e6cc99b3e6cc19520d01200841054b0d010b2001200641016a22063602142009420a7e2004ad42ff01837c210920052006470d010c060b0b200341206a20012002200910d08a808000024020032802200d00200020032b0328390308200041003602000c090b20002003280224360204200041043602000c080b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b2003290328210b0c010b4101210602402002450d002009210b0c010b0240420020097d220b4200590d00410221060c010b2009babd428080808080808080807f85210b410021060b2000200b370308200020063602000c020b20032903282109410021020b20002009370308200020023602000b200341306a2480808080000bc30101057f4100210402400240200141106a2802002205200141146a28020022064d0d00200641016a2107200520066b2108200128020c20066a21054100210403400240200520046a2d0000220641506a41ff0171410a490d002006412e460d030240200641c500460d00200641e500470d030b2000200120022003200410cd8a8080000f0b2001200720046a3602142008200441016a2204470d000b200821040b2000200120022003200410cc8a8080000f0b2000200120022003200410cb8a8080000bd30501077f23808080800041206b2201248080808000200028020c21020240024002400240024002400240200041146a2802002203200041106a28020022044f0d002000200341016a22053602140240200220036a2d000022064130470d00200520044f0d05200220056a2d000041506a41ff0171410a490d030c050b2006414f6a41ff017141084d0d01200521030b200141186a20022004200310878380800041002d00fca3c680001a200128021c210020012802182105411441002802c8a3c68000118180808000002203450d022003200536020c2003410d360200200320003602100c040b200520044f0d020340200220056a2d000041506a41ff017141094b0d032000200541016a220536021420042005470d000b410021030c030b200141086a200220042004200341026a220020042000491b10878380800041002d00fca3c680001a200128020c2100200128020821050240411441002802c8a3c68000118180808000002203450d002003200536020c2003410d360200200320003602100c030b4104411410b280808000000b4104411410b280808000000b41002103200520044f0d00024002400240200220056a2d0000220641e500460d00200641c500460d002006412e470d032000200541016a2206360214200620044f0d01200220066a2d000041506a41ff017141094b0d01200541026a2105034020042005460d03200220056a2106200541016a2207210520062d0000220641506a41ff0171410a490d000b20002007417f6a360214200641207241e500470d030b200010d28a80800021030c020b200141106a200220042004200541026a220020042000491b10878380800041002d00fca3c680001a2001280214210020012802102105411441002802c8a3c68000118180808000002203450d022003200536020c2003410d360200200320003602100c010b200020043602140b200141206a24808080800020030f0b4104411410b280808000000bc00201057f23808080800041106b2201248080808000200041146a22022002280200220241016a2203360200200028020c210402402003200041106a28020022054f0d000240200420036a2d000041556a0e03000100010b2000200241026a22033602140b0240024002400240200320054f0d002000200341016a2202360214200420036a2d000041506a41ff017141094d0d01200221030b200141086a20042005200310878380800041002d00fca3c680001a200128020c210220012802082100411441002802c8a3c68000118180808000002203450d022003200036020c2003410d360200200320023602100c010b41002103200220054f0d000340200420026a2d000041506a41ff017141094b0d012000200241016a220236021420052002470d000b0b200141106a24808080800020030f0b4104411410b280808000000ba40201027f23808080800041106b220524808080800002400240024002402004450d002003450d010b200141146a2802002204200141106a28020022034f0d01200128020c21060340200620046a2d000041506a41ff0171410a4f0d022001200441016a220436021420032004470d000c020b0b200541086a200128020c200141106a280200200141146a28020010878380800041002d00fca3c680001a200528020c2101200528020821030240411441002802c8a3c68000118180808000002204450d002004200336020c2004410e3602002000200436020420042001360210410121040c020b4104411410b280808000000b200044000000000000000044000000000000008020021b390308410021040b20002004360200200541106a2480808080000b910802067f027e23808080800041c0006b220324808080800041002d00fca3c680001a02400240024002400240411041002802c8a3c68000118180808000002204450d002003410036022c2003200436022820034110360224024020020d002004412d3a00002003200328022c41016a36022c0b200341306a2001200341246a10ca8a80800020032d00300d0102400240024020032d003122044130470d000240200141146a2802002204200141106a28020022054f0d00200128020c220620046a2d000041506a41ff0171410a490d020b2001200341246a10c88a80800021010c070b2004414f6a41ff017141084b0d010240200141146a2802002204200141106a28020022074f0d00200128020c21080340200820046a2d0000220641506a41ff017141094b0d012001200441016a22043602140240200328022c22052003280224470d00200341246a2005109c86808000200328022c21050b200328022820056a20063a00002003200328022c41016a36022c20072004470d000b0b2001200341246a10c88a80800021010c060b200341106a200620052005200441016a220120052001491b10878380800041002d00fca3c680001a2003280214210420032802102105411441002802c8a3c68000118180808000002201450d032001200536020c2001410d360200200120043602100c050b200341186a200128020c200141106a280200200141146a28020010878380800041002d00fca3c680001a200328021c210420032802182105411441002802c8a3c68000118180808000002201450d032001200536020c2001410d360200200120043602100c040b4101411010b280808000000b200328023421010c020b4104411410b280808000000b4104411410b280808000000b02400240024002400240024020010d00024020020d00200341306a2003280228200328022c410a10d28080800020032d00300d0520002003290338370308200041023602000c040b200328022c2204450d040240200328022822012d000041556a0e03000302030b2004417f6a2204450d04200141016a21010c020b20004104360200200020013602040c020b20044101460d020b0240024020044111490d004200210903402004450d02200320094200420a420010878e80800020012d000041506a220241094b0d0420032903084200520d04200141016a21012004417f6a21042003290300220a2002ad7c2209200a5a0d000c040b0b42002109034020012d000041506a220241094b0d03200141016a21012009420a7e2002ad7c21092004417f6a22040d000b0b20002009370308200041013602000b2003280224450d01200328022841002802c0a3c68000118080808000000c010b20004103360200200020032902243702042000410c6a2003412c6a2802003602000b200341c0006a2480808080000bc30e02087f017e2380808080004180016b2203248080808000200028020c2104024002400240024002400240024002400240024002400240200041146a2802002205200041106a28020022064f0d00024002400240024002400240024002400240024002400240200420056a2d0000220741a57f6a0e21040b0b0b0b0b0b0b0b0b0b030b0b0b0b0b0b0b010b0b0b0b0b020b0b0b0b0b0b05000b2007415e6a0e0c090a0a0a0a0a0a0a0a0a0a080a0b2000200541016a2207360214200720064f0d132000200541026a22083602140240200420076a2d000041f500470d00200820062007200620074b1b2207460d142000200541036a22093602140240200420086a2d000041ec00460d00200921080c010b20092007460d142000200541046a2208360214200420096a2d000041ec00460d050b200341186a20042006200810878380800041002d00fca3c680001a200328021c210520032802182104411441002802c8a3c68000118180808000002200450d0d2000200436020c20004109360200200020053602100c140b2000200541016a2207360214200720064f0d112000200541026a22083602140240200420076a2d000041f200470d00200820062007200620074b1b2207460d122000200541036a22093602140240200420086a2d000041f500460d00200921080c010b20092007460d122000200541046a2208360214200420096a2d000041e500460d050b200341286a20042006200810878380800041002d00fca3c680001a200328022c210520032802282104411441002802c8a3c68000118180808000002200450d0d2000200436020c20004109360200200020053602100c130b2000200541016a2207360214200720064f0d0f2000200541026a22083602140240200420076a2d000041e100470d00200820062007200620074b1b2207460d102000200541036a22093602140240200420086a2d000041ec00460d00200921080c010b20092007460d102000200541046a220a3602140240200420096a2d000041f300460d00200a21080c010b200a2007460d102000200541056a22083602142004200a6a2d000041e500460d050b200341386a20042006200810878380800041002d00fca3c680001a200328023c210520032802382104411441002802c8a3c68000118180808000002200450d0d2000200436020c20004109360200200020053602100c120b2003410a3a0070200341f0006a20012002108383808000200010d68a80800021000c110b2003410b3a0070200341f0006a20012002108383808000200010d68a80800021000c100b200341073a0070200341f0006a20012002108383808000200010d68a80800021000c0f0b20034180023b0170200341f0006a20012002108383808000200010d68a80800021000c0e0b200341003b0170200341f0006a20012002108383808000200010d68a80800021000c0d0b2000200541016a360214200341f0006a2000410010d48a80800020032802704104460d04200341c0006a41086a200341f0006a41086a29030037030020032003290370370340200341c0006a2001200210f682808000200010d68a80800021000c0c0b200041003602082000200541016a360214200341e4006a2000410c6a2000108a83808000024020032802644102460d002003290268210b200341053a00702003200b370274200341f0006a20012002108383808000200010d68a80800021000c0c0b200328026821000c0b0b200741506a41ff0171410a490d010b200341086a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a200328020c210420032802082106411441002802c8a3c68000118180808000002205450d052005200636020c2005410a360200200520043602102005200010d68a80800021000c090b200341f0006a2000410110d48a80800020032802704104460d00200341d0006a41086a200341f0006a41086a29030037030020032003290370370350200341d0006a2001200210f682808000200010d68a80800021000c080b200328027421000c070b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b200341306a20042006200710878380800041002d00fca3c680001a20032802342105200328023021040240411441002802c8a3c68000118180808000002200450d002000200436020c20004105360200200020053602100c030b4104411410b280808000000b200341206a20042006200710878380800041002d00fca3c680001a20032802242105200328022021040240411441002802c8a3c68000118180808000002200450d002000200436020c20004105360200200020053602100c020b4104411410b280808000000b200341106a20042006200710878380800041002d00fca3c680001a2003280214210520032802102104411441002802c8a3c68000118180808000002200450d012000200436020c20004105360200200020053602100b20034180016a24808080800020000f0b4104411410b280808000000be80101047f23808080800041206b220224808080800002400240200028020c450d00200021010c010b200241106a41086a2203200041086a28020036020020022000290200370310200241086a200128020c200141106a280200200141146a28020010878380800041002d00fca3c680001a200228020c2104200228020821050240411441002802c8a3c680001181808080000022010d004104411410b280808000000b200120022903103702002001200536020c20012004360210200141086a2003280200360200200041002802c0a3c68000118080808000000b200241206a24808080800020010b990301047f23808080800041106b2201248080808000200028020c2102024002400240024002400240200041146a2802002203200041106a28020022044f0d0003400240200220036a2d000041776a0e320000040400040404040404040404040404040404040404000404040404040404040404040404040404040404040404040403040b2000200341016a220336021420042003470d000b200421030b200141086a200220042004200341016a220320042003491b10878380800041002d00fca3c680001a200128020c210020012802082104411441002802c8a3c68000118180808000002203450d032003200436020c20034103360200200320003602100c020b2000200341016a360214410021030c010b2001200220042004200341016a220320042003491b10878380800041002d00fca3c680001a2001280204210020012802002104411441002802c8a3c68000118180808000002203450d022003200436020c20034106360200200320003602100b200141106a24808080800020030f0b4104411410b280808000000b4104411410b280808000000ba60101037f23808080800041106b2202248080808000200241086a200028020c200041106a280200200041146a28020010878380800041002d00fca3c680001a200228020c2103200228020821040240411441002802c8a3c680001181808080000022000d004104411410b280808000000b2000200436020c2000200129020037020020002003360210200041086a200141086a280200360200200241106a24808080800020000b900401057f23808080800041206b2201248080808000200028020c2102024002400240024002400240024002400240200041146a2802002203200041106a28020022044f0d0003400240200220036a2d0000220541776a0e24000004040004040404040404040404040404040404040400040404040404040404040406030b2000200341016a220336021420042003470d000b200421030b200141106a200220042004200341016a220320042003491b10878380800041002d00fca3c680001a2001280214210420012802102100411441002802c8a3c68000118180808000002203450d052003200036020c20034103360200200320043602100c040b200541fd00460d010b200141086a200220042004200341016a220320042003491b10878380800041002d00fca3c680001a200128020c210420012802082100411441002802c8a3c68000118180808000002203450d042003200036020c20034116360200200320043602100c020b2000200341016a360214410021030c010b200141186a200220042004200341016a220320042003491b10878380800041002d00fca3c680001a200128021c210420012802182100411441002802c8a3c68000118180808000002203450d032003200036020c20034115360200200320043602100b200141206a24808080800020030f0b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000bde0501067f23808080800041206b2201248080808000200028020c2102024002400240024002400240024002400240024002400240200041146a2802002203200041106a28020022044f0d0003400240200220036a2d0000220541776a0e24000004040004040404040404040404040404040404040400040404040404040404040406030b2000200341016a220336021420042003470d000b200421030b200141086a200220042004200341016a220320042003491b10878380800041002d00fca3c680001a200128020c210420012802082100411441002802c8a3c68000118180808000002203450d042003200036020c20034102360200200320043602100c080b200541dd00460d010b2001200220042004200341016a220320042003491b10878380800041002d00fca3c680001a2001280204210420012802002100411441002802c8a3c68000118180808000002203450d032003200036020c20034116360200200320043602100c060b2000200341016a360214410021030c050b2000200341016a2203360214200320044f0d030340200220036a2d0000220641776a220541174b0d034101200574419380800471450d032000200341016a220336021420042003470d000b200421030c030b4104411410b280808000000b4104411410b280808000000b200641dd00470d00200141186a200220042004200341016a220320042003491b10878380800041002d00fca3c680001a200128021c210420012802182100411441002802c8a3c68000118180808000002203450d032003200036020c20034115360200200320043602100c010b200141106a200220042004200341016a220320042003491b10878380800041002d00fca3c680001a2001280214210420012802102100411441002802c8a3c68000118180808000002203450d012003200036020c20034116360200200320043602100b200141206a24808080800020030f0b4104411410b280808000000b4104411410b280808000000bf20601057f23808080800041106b220424808080800002400240024002400240200241c0006a22050d0020044201370204200420053602000c010b2005417f4c0d034100210641002d00fca3c680001a200541002802c8a3c68000118180808000002207450d022004410036020820042007360204200420053602002002450d010b200120026a210541002106034002400240024020012c00002202417f4c0d00200141016a2101200241ff017121020c010b20012d0001413f7121072002411f712108024002402002415f4b0d0020084106742007722102200141026a21010c010b200741067420012d0002413f717221070240200241704f0d0020072008410c74722102200141036a21010c010b200741067420012d0003413f71722008411274418080f00071722202418080c400460d04200141046a21010b2002418001490d002004410036020c024002402002418010490d0002402002418080044f0d0020042002413f71418001723a000e20042002410c7641e001723a000c20042002410676413f71418001723a000d410321020c020b20042002413f71418001723a000f2004200241127641f001723a000c20042002410676413f71418001723a000e20042002410c76413f71418001723a000d410421020c010b20042002413f71418001723a000d2004200241067641c001723a000c410221020b0240200428020020066b20024f0d0020042006200210ab86808000200428020821060b200428020420066a2004410c6a200210848e8080001a200620026a21060c010b024020062004280200470d0020042006109c86808000200428020821060b200428020420066a20023a0000200428020841016a21060b2004200636020820012005470d000b0b410021010340413041d700200320016a2d0000220241a001491b20024104766a2105024020062004280200470d0020042006109c86808000200428020821060b200428020420066a20053a00002004200428020841016a2206360208024020062004280200470d0020042006109c86808000200428020821060b200428020420066a413041d7002002410f712206410a491b20066a3a00002004200428020841016a2206360208200141016a22014120470d000b20002004290200370200200041086a200441086a280200360200200441106a2480808080000f0b4101200510b280808000000b10ae80808000000beb0601057f23808080800041106b220524808080800041012106024002400240200441017420026a2207450d002007417f4c0d0241002d00fca3c680001a200741002802c8a3c68000118180808000002206450d010b4100210820054100360208200520063602042005200736020002402002450d00200120026a210741002108034002400240024020012c00002202417f4c0d00200141016a2101200241ff017121020c010b20012d0001413f7121062002411f712109024002402002415f4b0d0020094106742006722102200141026a21010c010b200641067420012d0002413f717221060240200241704f0d0020062009410c74722102200141036a21010c010b200641067420012d0003413f71722009411274418080f00071722202418080c400460d04200141046a21010b2002418001490d002005410036020c024002402002418010490d0002402002418080044f0d0020052002413f71418001723a000e20052002410c7641e001723a000c20052002410676413f71418001723a000d410321020c020b20052002413f71418001723a000f2005200241127641f001723a000c20052002410676413f71418001723a000e20052002410c76413f71418001723a000d410421020c010b20052002413f71418001723a000d2005200241067641c001723a000c410221020b0240200528020020086b20024f0d0020052008200210ab86808000200528020821080b200528020420086a2005410c6a200210848e8080001a200820026a21080c010b024020082005280200470d0020052008109c86808000200528020821080b200528020420086a20023a0000200528020841016a21080b2005200836020820012007470d000b0b02402004450d000340413041d70020032d0000220141a001491b20014104766a2102024020082005280200470d0020052008109c86808000200528020821080b200528020420086a20023a00002005200528020841016a2208360208024020082005280200470d0020052008109c86808000200528020821080b200341016a2103200528020420086a413041d7002001410f712208410a491b20086a3a00002005200528020841016a22083602082004417f6a22040d000b0b20002005290200370200200041086a200541086a280200360200200541106a2480808080000f0b4101200710b280808000000b10ae80808000000b3c00200128021420002802002d00004102742200419cd8c480006a28020020004190d8c480006a280200200141186a28020028020c118280808000000be10101037f024002400240024002402002411c6a2802000e020002010b410041004194d7c4800010f980808000000b20022802182203280204220420032802002203490d0220042001280200220128020822054d0d012004200541b4d7c48000109581808000000b4101410141a4d7c4800010f980808000000b200128020421012000200420036b3602042000200120036a3602002000200229020c370208200041106a200241146a28020036020002402002280200450d00200228020441002802c0a3c68000118080808000000b0f0b2003200441b4d7c48000109681808000000b02000b02000b6301017f02402000280200220041e4016a280200450d00200041e8016a28020041002802c0a3c68000118080808000000b02402000417f460d00200020002802042201417f6a36020420014101470d00200041002802c0a3c68000118080808000000b0b4c01027f024020002802002201417f460d0020002802042102200120012802042200417f6a36020420004101470d002002410b6a4104490d00200141002802c0a3c68000118080808000000b0b7801017f23808080800041306b22022480808080002002200136020c200220003602082002411c6a420137020020024102360214200241fccfc48000360210200241e08180800036022c2002200241286a3602182002200241086a360228200241106a10e48a8080002101200241306a24808080800020010b850201037f23808080800041106b22012480808080002000410c6a28020021020240024002400240024002400240024020002802040e020001020b20020d01410121034100210041b0cbc4800021020c030b2002450d010b200141046a200010b8808080000c020b2000280200220028020021020240200028020422000d0041012103410021000c010b2000417f4c0d0241002d00fca3c680001a200041002802c8a3c68000118180808000002203450d030b20032002200010848e80800021022001200036020c20012002360208200120003602040b200141046a1082838080002100200141106a24808080800020000f0b10ae80808000000b4101200010b280808000000bfa0101017f23808080800041c0006b22042480808080002004200136020c200420003602080240024020030d002004411c6a420137020020044102360214200441b4d0c48000360210200441e08180800036022c2004200441286a3602182004200441086a360228200441106a10e48a80800021030c010b200441286a410c6a41dc83808000360200200441106a410c6a420237020020044102360214200441d0d0c48000360210200441e08180800036022c2004200336023c200420023602382004200441286a3602182004200441386a3602302004200441086a360228200441106a10e48a80800021030b200441c0006a24808080800020030b9f0101017f23808080800041c0006b220324808080800020032002360214200320013602102003200036020c200341186a410c6a4202370200200341306a410c6a41dd838080003602002003410236021c200341fcd0c4800036021820034185808080003602342003200341306a3602202003200341106a36023820032003410c6a360230200341186a10e48a8080002102200341c0006a24808080800020020b7801017f23808080800041306b22022480808080002002200136020c200220003602082002411c6a420137020020024102360214200241a0d1c48000360210200241e08180800036022c2002200241286a3602182002200241086a360228200241106a10e48a8080002101200241306a24808080800020010bfa0101017f23808080800041c0006b22042480808080002004200136020c200420003602080240024020030d002004411c6a420137020020044102360214200441dcd1c48000360210200441e08180800036022c2004200441286a3602182004200441086a360228200441106a10e48a80800021030c010b200441286a410c6a41dc83808000360200200441106a410c6a420237020020044102360214200441ecd1c48000360210200441e08180800036022c2004200336023c200420023602382004200441286a3602182004200441386a3602302004200441086a360228200441106a10e48a80800021030b200441c0006a24808080800020030bfb0c02067f017e23808080800041d0006b22022480808080002001280200220328020c2104024002400240024002400240024002400240024002400240200341146a2802002205200341106a28020022064f0d0003400240200420056a2d0000220741776a0e24000004040004040404040404040404040404040404040400040404040404040404040406030b2003200541016a220536021420062005470d000b200621050b200241186a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a200228021c210620022802182103411441002802c8a3c68000118180808000002205450d052005200336020c200541023602002000200536020820004202370300200520063602100c0a0b200741dd00460d010b20012d0004450d020c060b200042003703000c070b20012d00040d042003200541016a22053602140240200520064f0d000340200420056a2d0000220741776a220141174b0d074101200174419380800471450d072003200541016a220536021420062005470d000b200621050b200241206a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a2002280224210620022802202103411441002802c8a3c68000118180808000002205450d032005200336020c200541053602002000200536020820004202370300200520063602100c060b2002200420062006200541016a220520062005491b10878380800041002d00fca3c680001a2002280204210620022802002103411441002802c8a3c68000118180808000002205450d012005200336020c200541073602002000200536020820004202370300200520063602100c050b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b200141003a00040b0240024002400240200741dd00470d00200241086a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a200228020c210620022802082103411441002802c8a3c68000118180808000002205450d012005200336020c200541153602002000200536020820004202370300200520063602100c040b024002400240024002400240200520064f0d0003400240200420056a2d0000220741776a0e2500000404000404040404040404040404040404040404040004040404040404040404040403040b2003200541016a220536021420062005470d000b200621050b200241106a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a2002280214210620022802102103411441002802c8a3c68000118180808000002205450d062005200336020c20054105360200200520063602100c040b2003200541016a36021441002105200241286a2003410010cf8a808000200228022822064104460d02200229033021080240024002400240024020060e0400040102000b200241033a003820022008370340200241386a200241cf006a418c84c4800010838380800021040c020b02402008427f570d00410021050c030b200241023a003820022008370340200241386a200241cf006a41accdc4800010858380800021040c010b20022802302106200228022c21052002410b3a0038200241386a200241cf006a418c84c480001083838080002104200541808080807872418080808078460d00200641002802c0a3c68000118080808000000b410121050b20050d010c060b0240200741506a41ff0171410a490d002003200241cf006a41accdc4800010d58a808000200310d68a80800021050c030b200241286a2003410110cf8a808000200228022822054104460d0141002106200229033021080240024002400240024020050e0400040102000b200241033a003820022008370340200241386a200241cf006a418c84c4800010838380800021040c020b02402008427f570d00410021060c030b200241023a003820022008370340200241386a200241cf006a41accdc4800010858380800021040c010b20022802302106200228022c21052002410b3a0038200241386a200241cf006a418c84c480001083838080002104200541808080807872418080808078460d00200641002802c0a3c68000118080808000000b410121060b2006450d050b2004200310d68a80800021050c010b200228022c21050b20004202370300200020053602080c030b4104411410b280808000000b4104411410b280808000000b20002008370308200042013703000b200241d0006a2480808080000bcc0101047f23808080800041106b2201248080808000200028020421020240024002400240200028020822030d00410121040c010b2003417f4c0d0141002d00fca3c680001a200341002802c8a3c68000118180808000002204450d020b20042002200310848e80800021042001200336020c2001200436020820012003360204200141046a108283808000210302402000280200450d00200241002802c0a3c68000118080808000000b200141106a24808080800020030f0b10ae80808000000b4101200310b280808000000bfc1003067f017e037f23808080800041f0016b22022480808080002001280200220328020c2104024002400240024002400240024002400240024002400240200341146a2802002205200341106a28020022064f0d0003400240200420056a2d0000220741776a0e24000004040004040404040404040404040404040404040400040404040404040404040406030b2003200541016a220536021420062005470d000b200621050b200241306a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a2002280234210620022802302103411441002802c8a3c68000118180808000002205450d052005200336020c200541023602002000200536020820004202370300200520063602100c0a0b200741dd00460d010b20012d0004450d020c060b200042003703000c070b20012d00040d042003200541016a22053602140240200520064f0d000340200420056a2d0000220741776a220141174b0d074101200174419380800471450d072003200541016a220536021420062005470d000b200621050b200241386a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a200228023c210620022802382103411441002802c8a3c68000118180808000002205450d032005200336020c200541053602002000200536020820004202370300200520063602100c060b200241086a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a200228020c210620022802082103411441002802c8a3c68000118180808000002205450d012005200336020c200541073602002000200536020820004202370300200520063602100c050b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b200141003a00040b0240024002400240024002400240024002400240200741dd00470d00200241106a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a2002280214210620022802102103411441002802c8a3c68000118180808000002205450d012005200336020c200541153602002000200536020820004202370300200520063602100c0a0b02400240200520064f0d000340200420056a2d0000220141776a220741174b0d024101200774419380800471450d022003200541016a220536021420062005470d000b200621050b200241286a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a200228022c210620022802282103411441002802c8a3c68000118180808000002205450d022005200336020c20054105360200200520063602100c090b0240200141db00460d002003200241ef016a41d4cdc4800010d58a80800021050c080b200320032d0018417f6a22073a0018200541016a2105200741ff0171450d062003200536021420022003360280010240200520064f0d000340200420056a2d0000220141776a220741174b0d054101200774419380800471450d052003200541016a220536021420062005470d000b200621050b200241206a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a2002280224210620022802202104411441002802c8a3c68000118180808000002205450d022005200436020c20054102360200200520063602100c040b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b0240200141dd00470d004100200241ef016a41d4cdc4800010e68a80800021050c010b200241003a008401200241c8016a200310af89808000024020022d00c801450d0020022802cc0121050c010b200241a8016a41106a200241df016a290000220837030020024188016a41086a2205200241d7016a29000037030020024188016a41106a2206200837030020024188016a41186a2204200241e7016a2f00003b01002002200241cf016a2900003703880120022800cb01210920022f00c901210a200241c8016a20024180016a10e98a8080000240024020022903c80122084202560d0002402008a70e03000102000b4101200241ef016a41d4cdc4800010e68a80800021050c020b200241e0006a41086a2005290300370300200241e0006a41106a2006290300370300200241e0006a41186a20042f01003b0100200220022903880137036020022903d00121084101210b410021010c020b20022802d00121050b4100210b410121010b41012104200320032d001841016a3a001841002107200310da8a8080002106024020010d00024020060d00200241c0006a41186a200241e0006a41186a2f01003b0100200241c0006a41106a200241e0006a41106a290300370300200241c0006a41086a200241e0006a41086a290300370300200220022903603703400b20064521040240200b0d00024020052802000d00200541086a280200450d00200528020441002802c0a3c68000118080808000000b200541002802c0a3c68000118080808000000b20042107200621050b02402006450d002004450d00024020062802000d00200641086a280200450d00200628020441002802c0a3c68000118080808000000b200641002802c0a3c68000118080808000000b2007450d012000200229034037010e200041266a200241d8006a2f01003b01002000411e6a200241d0006a290300370100200041166a200241c8006a290300370100200020083703282000200936010a2000200a3b0108200042013703000c030b200241186a200420062006200520062005491b10878380800041002d00fca3c680001a200228021c2106200228021821030240411441002802c8a3c68000118180808000002205450d002005200336020c20054118360200200520063602100c020b4104411410b280808000000b2005200310d68a80800021050b20004202370300200020053602080b200241f0016a2480808080000ba80701067f23808080800041d0006b22022480808080002001280200220328020c21040240024002400240024002400240024002400240024002400240200341146a2802002205200341106a28020022064f0d0003400240200420056a2d0000220741776a0e24000004040004040404040404040404040404040404040400040404040404040404040406030b2003200541016a220536021420062005470d000b200621050b200241186a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a200228021c210620022802182103411441002802c8a3c68000118180808000002205450d052005200336020c2005410236020020002005360204200041013a0000200520063602100c0a0b200741dd00460d010b20012d0004450d020c060b200041003b01000c070b20012d00040d042003200541016a22053602140240200520064f0d000340200420056a2d0000220741776a220141174b0d074101200174419380800471450d072003200541016a220536021420062005470d000b200621050b200241206a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a2002280224210620022802202103411441002802c8a3c68000118180808000002205450d032005200336020c2005410536020020002005360204200041013a0000200520063602100c060b200241086a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a200228020c210620022802082103411441002802c8a3c68000118180808000002205450d012005200336020c2005410736020020002005360204200041013a0000200520063602100c050b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b200141003a00040b0240200741dd00470d00200241106a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a2002280214210620022802102103411441002802c8a3c68000118180808000002205450d022005200336020c2005411536020020002005360204200041013a0000200520063602100c010b2002412c6a200310de87808000024020022d002c0d0020004180023b0100200041026a200229002d3700002000411a6a200241c5006a290000370000200041126a2002413d6a2900003700002000410a6a200241356a2900003700000c010b20002002280230360204200041013a00000b200241d0006a2480808080000f0b4104411410b280808000000b8f0f03067f017e017f23808080800041c0016b22022480808080002001280200220328020c2104024002400240024002400240024002400240024002400240200341146a2802002205200341106a28020022064f0d0003400240200420056a2d0000220741776a0e24000004040004040404040404040404040404040404040400040404040404040404040406030b2003200541016a220536021420062005470d000b200621050b200241206a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a2002280224210620022802202103411441002802c8a3c68000118180808000002205450d052005200336020c200541023602002000200536020820004202370300200520063602100c0a0b200741dd00460d010b20012d0004450d020c060b200042003703000c070b20012d00040d042003200541016a22053602140240200520064f0d000340200420056a2d0000220741776a220141174b0d074101200174419380800471450d072003200541016a220536021420062005470d000b200621050b200241286a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a200228022c210620022802282103411441002802c8a3c68000118180808000002205450d032005200336020c200541053602002000200536020820004202370300200520063602100c060b2002200420062006200541016a220520062005491b10878380800041002d00fca3c680001a2002280204210620022802002103411441002802c8a3c68000118180808000002205450d012005200336020c200541073602002000200536020820004202370300200520063602100c050b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b200141003a00040b024002400240024002400240024002400240200741dd00470d00200241086a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a200228020c210620022802082103411441002802c8a3c68000118180808000002205450d012005200336020c200541153602002000200536020820004202370300200520063602100c090b02400240200520064f0d000340200420056a2d0000220141776a220741174b0d024101200774419380800471450d022003200541016a220536021420062005470d000b200621050b200241186a200420062006200541016a220520062005491b10878380800041002d00fca3c680001a200228021c210620022802182103411441002802c8a3c68000118180808000002205450d022005200336020c20054105360200200520063602100c080b0240200141db00460d002003200241bf016a41e4cdc4800010d58a80800021070c070b200320032d0018417f6a22073a0018200541016a2105200741ff0171450d0520032005360214200241013a00742002200336027020024198016a200241f0006a10ec8a80800020022d0098010d02024020022d0099014101710d004100200241bf016a41e4cdc4800010e68a80800021070c040b200241f8006a41186a2205200241b2016a290100370300200241f8006a41106a2206200241aa016a290100370300200241f8006a41086a2204200241a2016a2901003703002002200229019a0137037820024198016a200241f0006a10e98a8080000240024020022903980122084202560d0002402008a70e03000102000b4101200241bf016a41e4cdc4800010e68a80800021070c050b200241d0006a41086a2004290300370300200241d0006a41106a2006290300370300200241d0006a41186a20052903003703002002200229037837035020022903a001210841012109410021010c050b20022802a00121070c030b4104411410b280808000000b4104411410b280808000000b200228029c0121070b41002109410121010b41012106200320032d001841016a3a001841002104200310da8a8080002105024020010d00024020050d00200241306a41186a200241d0006a41186a290300370300200241306a41106a200241d0006a41106a290300370300200241306a41086a200241d0006a41086a290300370300200220022903503703300b2005452106024020090d00024020072802000d00200741086a280200450d00200728020441002802c0a3c68000118080808000000b200741002802c0a3c68000118080808000000b20062104200521070b02402005450d002006450d00024020052802000d00200541086a280200450d00200528020441002802c0a3c68000118080808000000b200541002802c0a3c68000118080808000000b2004450d0120002002290330370308200041206a200241306a41186a290300370300200041186a200241306a41106a290300370300200041106a200241386a29030037030020002008370328200042013703000c030b200241106a200420062006200520062005491b10878380800041002d00fca3c680001a20022802142106200228021021030240411441002802c8a3c68000118180808000002205450d002005200336020c20054118360200200520063602100c020b4104411410b280808000000b2007200310d68a80800021050b20004202370300200020053602080b200241c0016a2480808080000bc10301077f23808080800041206b2202248080808000200128020c2103024002400240024002400240200141146a2802002204200141106a28020022054f0d002001410c6a21060340200320046a2d0000220741776a220841174b0d024101200874419380800471450d022001200441016a220436021420052004470d000b200521040b2002200320052005200441016a220420052004491b10878380800041002d00fca3c680001a2002280204210820022802002101411441002802c8a3c68000118180808000002204450d012004200136020c2004410536020020002004360204200041013a0000200420083602100c040b024020074122460d002001200241146a41d4d4c4800010d58a80800021040c020b200141003602082001200441016a360214200241146a20062001108a838080000240024020022802144102460d002002410c6a2002280218200228021c10ca8680800020022d000c450d01200228021021040c030b20002002280218360204200041013a00000c040b200020022d000d3a0001410021040c020b4104411410b280808000000b20002004200110d68a808000360204410121040b200020043a00000b200241206a2480808080000b860703067f027e027f23808080800041306b2202248080808000200128020c2103024002400240200141146a2802002204200141106a28020022054f0d000340200320046a2d0000220641776a220741174b0d024101200774419380800471450d022001200441016a220436021420052004470d000b200521040b410121072002200320052005200441016a220420052004491b10878380800041002d00fca3c680001a20022802042101200228020021050240411441002802c8a3c68000118180808000002204450d002004200536020c2004410536020020002004360204200420013602100c020b4104411410b280808000000b024002400240024002400240200641db00470d00200120012d0018417f6a22073a0018200441016a21040240200741ff01710d00200241086a200320052005200420052004491b10878380800041002d00fca3c680001a200228020c210720022802082101411441002802c8a3c68000118180808000002204450d022004200136020c2004411836020020002004360204200420073602100c060b20012004360214200241013a001420022001360210200241186a200241106a10e98a808000024002400240200229031822084202510d000240200850450d0041002002412f6a41f4cdc4800010e68a80800021050c020b20022903202108200241186a200241106a10e98a8080000240200229031822094202560d0002402009a70e03000102000b41012002412f6a41f4cdc4800010e68a80800021050c020b2002290320210941012106410021070c020b200228022021050b41002106410121070b41012103200120012d001841016a3a0018200110da8a80800021042005210a20070d022004210a20040d024101210b0c030b20012002412f6a41f4cdc4800010d58a808000210a0c030b4104411410b280808000000b410021032007210b0b024020072006720d00024020052802000d00200541086a280200450d00200528020441002802c0a3c68000118080808000000b200541002802c0a3c68000118080808000000b02402004410047200b71450d00024020042802000d00200441086a280200450d00200428020441002802c0a3c68000118080808000000b200441002802c0a3c68000118080808000000b2003450d0020002008370308200041106a2009370300410021070c020b2000200a200110d68a8080003602040b410121070b20002007360200200241306a2480808080000b900501087f23808080800041206b2201248080808000200028020c21020240024002400240200041146a2802002203200041106a28020022044f0d00410020046b2105200341046a21030340200220036a2206417c6a2d0000220741776a220841174b0d024101200874419380800471450d0220002003417d6a3602142005200341016a22036a4104470d000b200421030b2001200220042004200341016a220320042003491b10878380800041002d00fca3c680001a20012802042103200128020021000240411441002802c8a3c68000118180808000002208450d002008200036020c20084105360200200820033602100c020b4104411410b280808000000b024002400240200741ee00470d0020002003417d6a2208360214200820044f0d0220002003417e6a2205360214024002402006417d6a2d000041f500460d00200521030c010b200520042008200420084b1b2208460d0320002003417f6a220536021402402006417e6a2d000041ec00460d00200521030c010b20052008460d0320002003360214410021082006417f6a2d000041ec00460d040b200141106a20022004200310878380800041002d00fca3c680001a2001280214210320012802102100411441002802c8a3c68000118180808000002208450d012008200036020c20084109360200200820033602100c030b20002001411f6a41e4d4c4800010d58a808000200010d68a80800021080c020b4104411410b280808000000b200141086a20022004200810878380800041002d00fca3c680001a200128020c210320012802082100411441002802c8a3c68000118180808000002208450d012008200036020c20084105360200200820033602100b200141206a24808080800020080f0b4104411410b280808000000bc60702067f017e23808080800041e0006b2202248080808000200128020c21030240024002400240200141146a2802002204200141106a28020022054f0d000340200320046a2d0000220641776a220741174b0d024101200774419380800471450d022001200441016a220436021420052004470d000b200521040b2002200320052005200441016a220420052004491b10878380800041002d00fca3c680001a20022802042107200228020021010240411441002802c8a3c68000118180808000002204450d002004200136020c20044105360200200020043602042000418080808078360200200420073602100c020b4104411410b280808000000b0240024002400240200641db00470d00200120012d0018417f6a22073a0018200441016a2104200741ff0171450d0320012004360214200241013a0018200220013602142002410036022420024280808080800137021c200241286a41086a2104024002400340200241286a200241146a10ed8a8080000240200229032822084201510d0020084202520d02200228023021070240200228021c450d00200228022041002802c0a3c68000118080808000000b41808080807821040c030b024020022802242205200228021c470d002002411c6a2005109a86808000200228022421050b2002280220200541286c6a22072004290300370300200741086a200441086a290300370300200741106a200441106a290300370300200741186a200441186a290300370300200741206a200441206a2903003703002002200541016a3602240c000b0b200228021c210420022802202107200228022421030b200120012d001841016a3a0018200110da8a8080002105024002402004418080808078460d002005450d0120040d03200521070c040b2005450d03024020052802000d00200541086a280200450d00200528020441002802c0a3c68000118080808000000b200541002802c0a3c68000118080808000000c030b2000200336020820002007360204200020043602000c040b2001200241df006a4194d4c4800010d58a80800021070c010b200741002802c0a3c6800011808080800000200521070b2007200110d68a80800021042000418080808078360200200020043602040c010b200241086a200320052005200420052004491b10878380800041002d00fca3c680001a200228020c210720022802082101411441002802c8a3c68000118180808000002204450d012004200136020c20044118360200200020043602042000418080808078360200200420073602100b200241e0006a2480808080000f0b4104411410b280808000000bc60702067f017e23808080800041e0006b2202248080808000200128020c21030240024002400240200141146a2802002204200141106a28020022054f0d000340200320046a2d0000220641776a220741174b0d024101200774419380800471450d022001200441016a220436021420052004470d000b200521040b2002200320052005200441016a220420052004491b10878380800041002d00fca3c680001a20022802042107200228020021010240411441002802c8a3c68000118180808000002204450d002004200136020c20044105360200200020043602042000418080808078360200200420073602100c020b4104411410b280808000000b0240024002400240200641db00470d00200120012d0018417f6a22073a0018200441016a2104200741ff0171450d0320012004360214200241013a0018200220013602142002410036022420024280808080800137021c200241286a41086a2104024002400340200241286a200241146a10eb8a8080000240200229032822084201510d0020084202520d02200228023021070240200228021c450d00200228022041002802c0a3c68000118080808000000b41808080807821040c030b024020022802242205200228021c470d002002411c6a2005109a86808000200228022421050b2002280220200541286c6a22072004290300370300200741086a200441086a290300370300200741106a200441106a290300370300200741186a200441186a290300370300200741206a200441206a2903003703002002200541016a3602240c000b0b200228021c210420022802202107200228022421030b200120012d001841016a3a0018200110da8a8080002105024002402004418080808078460d002005450d0120040d03200521070c040b2005450d03024020052802000d00200541086a280200450d00200528020441002802c0a3c68000118080808000000b200541002802c0a3c68000118080808000000c030b2000200336020820002007360204200020043602000c040b2001200241df006a41a4d4c4800010d58a80800021070c010b200741002802c0a3c6800011808080800000200521070b2007200110d68a80800021042000418080808078360200200020043602040c010b200241086a200320052005200420052004491b10878380800041002d00fca3c680001a200228020c210720022802082101411441002802c8a3c68000118180808000002204450d012004200136020c20044118360200200020043602042000418080808078360200200420073602100b200241e0006a2480808080000f0b4104411410b280808000000bb10501067f23808080800041306b2202248080808000200128020c21030240024002400240200141146a2802002204200141106a28020022054f0d000340200320046a2d0000220641776a220741174b0d024101200774419380800471450d022001200441016a220436021420052004470d000b200521040b200241086a200320052005200441016a220420052004491b10878380800041002d00fca3c680001a200228020c2107200228020821010240411441002802c8a3c68000118180808000002204450d002004200136020c20044105360200200020043602042000418080808078360200200420073602100c020b4104411410b280808000000b02400240200641db00470d00200120012d0018417f6a22073a0018200441016a21040240200741ff01710d00200241106a200320052005200420052004491b10878380800041002d00fca3c680001a2002280214210720022802102101411441002802c8a3c68000118180808000002204450d042004200136020c20044118360200200020043602042000418080808078360200200420073602100c030b200120043602142002411c6a2001410110b689808000200120012d001841016a3a0018200110da8a808000210402400240200228021c2207418080808078460d002004450d012007450d03200228022041002802c0a3c68000118080808000000c030b2002280220210702402004450d00024020042802000d00200441086a280200450d00200428020441002802c0a3c68000118080808000000b200441002802c0a3c68000118080808000000b200721040c020b20002002290220370204200020073602000c020b20012002412f6a41b4d4c4800010d58a80800021040b2004200110d68a80800021042000418080808078360200200020043602040b200241306a2480808080000f0b4104411410b280808000000b850401067f23808080800041206b2202248080808000200128020c210302400240024002400240200141146a2802002204200141106a28020022054f0d002001410c6a2106034002400240200320046a2d000041776a220741194b0d0041012007744193808004710d0120074119460d040b2001200241146a41c4d4c4800010d58a808000200110d68a80800021042000418080808078360200200020043602040c040b2001200441016a220436021420052004470d000b200521040b200241086a200320052005200441016a220420052004491b10878380800041002d00fca3c680001a200228020c210720022802082101411441002802c8a3c68000118180808000002204450d022004200136020c20044105360200200020043602042000418080808078360200200420073602100c010b20014100360208410121072001200441016a360214200241146a20062001108a838080000240024020022802144102460d0020022802182101200228021c2204450d012004417f4c0d0441002d00fca3c680001a200441002802c8a3c680001181808080000022070d014101200410b280808000000b2000200228021836020420004180808080783602000c010b20072001200410848e80800021072000200436020820002007360204200020043602000b200241206a2480808080000f0b4104411410b280808000000b10ae80808000000bbe13010a7f23808080800041e0006b2206248080808000200128020c2107024002400240200141146a2802002208200141106a28020022094f0d002001410c6a210a0340200720086a2d0000220b41776a220c41174b0d024101200c74419380800471450d022001200841016a220836021420092008470d000b200921080b200641086a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a200628020c210c200628020821010240411441002802c8a3c68000118180808000002208450d002008200136020c200841053602002000200836020420004180808080783602002008200c3602100c020b4104411410b280808000000b02400240024002400240024002400240024002400240200b41db00460d00200b41fb00460d012001200641df006a41f4d4c4800010d58a80800021080c070b200120012d0018417f6a220c3a0018200841016a2108200c41ff0171450d08200120083602140240200820094f0d000340200720086a2d0000220b41776a220c41174b0d064101200c74419380800471450d062001200841016a220836021420092008470d000b200921080b200641186a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a200628021c210c20062802182109411441002802c8a3c68000118180808000002208450d012008200936020c200841023602002008200c360210418080808078210c0c050b200120012d0018417f6a220c3a0018200841016a2108200c41ff0171450d02200120083602140240024002400240024020082009490d00418080808078210d0c010b418080808078210d4100210e0340200a280200210702400240024002400240024002400240024002400240024002400240024003400240200720086a2d0000220c41776a0e24000003030003030303030303030303030303030303030300030303030303030303030304020b2001200841016a220836021420092008470d000b200921080c100b200c41fd00460d050b200e4101710d012008210b0c070b0240200e4101710d002008210b0c080b2001200841016a220b360214200b20094f0d0103402007200b6a2d0000220c41776a220841174b0d074101200874419380800471450d072001200b41016a220b3602142009200b470d000b200921080c020b200641286a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a200628022c210c20062802282109411441002802c8a3c68000118180808000002208450d032008200936020c200841083602002008200c3602100c0d0b200841016a21080b200641c8006a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a200628024c210c20062802482109411441002802c8a3c68000118180808000002208450d022008200936020c200841053602002008200c3602100c0b0b200d210c200f2108200d418080808078470d0e41d8cec48000410b10e38a8080002108418080808078210c0c0e0b4104411410b280808000000b4104411410b280808000000b200c4122460d01200c41fd00470d00200641c0006a200720092009200b41016a220820092008491b10878380800041002d00fca3c680001a2006280244210c20062802402109411441002802c8a3c68000118180808000002208450d032008200936020c200841153602002008200c3602100c070b200641306a200720092009200b41016a220820092008491b10878380800041002d00fca3c680001a2006280234210c20062802302109411441002802c8a3c68000118180808000002208450d012008200936020c200841113602002008200c3602100c060b200141003602082001200b41016a360214200641d0006a200a2001108a838080002006280254210820062802504102460d05024002402006280258220c410b470d00200841c0abc38000410b10888e808000450d010b2008200c41ccabc38000410110e58a80800021080c060b0240200d418080808078460d0041d8cec48000410b10e78a80800021080c070b0240200110d78a8080002208450d00418080808078210c0c0a0b200641d0006a200110f38a808000418080808078210c2006280254210f2006280250220d418080808078470d02200f21080c090b4104411410b280808000000b4104411410b280808000000b2006280258210b4101210e2001280214220820012802102209490d000b200a28020021070b200641386a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a200628023c210c20062802382109411441002802c8a3c68000118180808000002208450d022008200936020c200841033602002008200c3602100b418080808078210c200d418080808078460d030b418080808078210c0240200d450d00200f41002802c0a3c68000118080808000000b0c020b4104411410b280808000000b4104411410b280808000000b200120012d001841016a3a0018200110d98a808000210902400240200c418080808078460d002009450d06200c0d01200921080c050b2009450d04024020092802000d00200941086a280200450d00200928020441002802c0a3c68000118080808000000b200941002802c0a3c68000118080808000000c040b200841002802c0a3c6800011808080800000200921080c030b200641206a200720092009200820092008491b10878380800041002d00fca3c680001a2006280224210c20062802202101411441002802c8a3c680001181808080000022080d054104411410b280808000000b0240200b41dd00470d00418080808078210c410041c8cfc4800041c8cec4800010e68a80800021080c010b200641d0006a200110f38a808000418080808078210c024020062802502209418080808078470d00200628025421080c010b2006280258210b200628025421082009210c0b200120012d001841016a3a0018200110da8a80800021090240200c418080808078460d002009450d020240200c450d00200841002802c0a3c68000118080808000000b200921080c010b2009450d00024020092802000d00200941086a280200450d00200928020441002802c0a3c68000118080808000000b200941002802c0a3c68000118080808000000b2008200110d68a80800021082000418080808078360200200020083602040c030b2000200b360208200020083602042000200c3602000c020b200641106a200720092009200820092008491b10878380800041002d00fca3c680001a2006280214210c20062802102101411441002802c8a3c680001181808080000022080d004104411410b280808000000b2008200136020c200841183602002000200836020420004180808080783602002008200c3602100b200641e0006a2480808080000b8b0901077f23808080800041c0006b2205248080808000200028020c21060240024002400240200041146a2802002207200041106a28020022084f0d002000410c6a21090340200620076a2d0000220a41776a220b41174b0d024101200b74419380800471450d022000200741016a220736021420082007470d000b200821070b200541086a200620082008200741016a220720082007491b10878380800041002d00fca3c680001a200528020c210b200528020821000240411441002802c8a3c68000118180808000002207450d002007200036020c200741053602002007200b3602100c020b4104411410b280808000000b024002400240024002400240200a41db00460d00200a41fb00460d0120002005413f6a41b4d5c4800010d58a808000210b0c020b200020002d0018220a417f6a220b3a0018200741016a2107200b41ff0171450d042000200a3a00182000200736021441002107200010da8a808000220b0d010c050b200020002d0018417f6a220b3a0018200741016a2107200b41ff0171450d022000200736021402400240024002400240200720084f0d000340200620076a2d0000220a41776a220b41194b0d0402404101200b744193808004710d00200b4119470d05200041003602082000200741016a360214200541306a20092000108a8380800020052802304102460d0320052802342005280238419cadc48000410010e58a808000210b0c060b2000200741016a220736021420082007470d000b200821070b200541286a200620082008200741016a220720082007491b10878380800041002d00fca3c680001a200528022c210720052802282108411441002802c8a3c6800011818080800000220b450d01200b200836020c200b4103360200200b20073602100c030b2005280234210b0c020b4104411410b280808000000b0240200a41fd00470d004100210b0c010b200541206a200620082008200741016a220720082007491b10878380800041002d00fca3c680001a2005280224210720052802202108411441002802c8a3c6800011818080800000220b450d02200b200836020c200b4111360200200b20073602100b200020002d001841016a3a0018200010d98a80800021080240200b0d00410021072008210b20080d010c050b2008450d00024020082802000d00200841086a280200450d00200828020441002802c0a3c68000118080808000000b200841002802c0a3c68000118080808000000b200b200010d68a80800021070c030b4104411410b280808000000b200541186a200620082008200720082007491b10878380800041002d00fca3c680001a200528021c210b200528021821000240411441002802c8a3c68000118180808000002207450d002007200036020c200741183602002007200b3602100c020b4104411410b280808000000b200541106a200620082008200720082007491b10878380800041002d00fca3c680001a2005280214210b20052802102100411441002802c8a3c68000118180808000002207450d012007200036020c200741183602002007200b3602100b200541c0006a24808080800020070f0b4104411410b280808000000bd91e030d7f017e017f23808080800041f0016b2206248080808000200128020c2107024002400240024002400240024002400240024002400240024002400240024002400240200141146a2802002208200141106a28020022094f0d002001410c6a210a0340200720086a2d0000220b41776a220c41174b0d024101200c74419380800471450d022001200841016a220836021420092008470d000b200921080b200641e8006a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a200628026c210c20062802682101411441002802c8a3c68000118180808000002208450d012008200136020c200841053602002000418080808078360218200020083602002008200c3602100c100b024002400240200b41db00460d00200b41fb00460d012001200641ef016a41c4d5c4800010d58a80800021080c110b200120012d0018417f6a220c3a0018200841016a2108200c41ff0171450d0e200120083602140240200820094f0d000340200720086a2d0000220b41776a220c41174b0d0b4101200c74419380800471450d0b2001200841016a220836021420092008470d000b200921080b200641306a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a2006280234210c20062802302109411441002802c8a3c68000118180808000002208450d012008200936020c200841023602002008200c360210418080808078210b0c0a0b200120012d0018417f6a220c3a0018200841016a2108200c41ff0171450d0720012008360214024020082009490d00418080808078210d0c040b200641e1016a210e200641d0016a410472210f418080808078210d410321104100210b0340200a28020021070240024002400240024002400240024002400240024002400240024002400240024003400240200720086a2d0000220c41776a0e24000003030003030303030303030303030303030303030300030303030303030303030304020b2001200841016a220836021420092008470d000b200921080c150b200c41fd00460d050b200b4101710d012008210b0c070b0240200b4101710d002008210b0c080b2001200841016a220b360214200b20094f0d0103402007200b6a2d0000220c41776a220841174b0d074101200874419380800471450d072001200b41016a220b3602142009200b470d000b200921080c020b200641c0006a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a2006280244210c20062802402109411441002802c8a3c68000118180808000002208450d032008200936020c200841083602002008200c3602100c120b200841016a21080b200641e0006a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a2006280264210c20062802602109411441002802c8a3c68000118180808000002208450d022008200936020c200841053602002008200c3602100c100b0240200d418080808078460d000240201041ff01714103470d0041e3cec48000410b10e38a8080002108200d450d07201141002802c0a3c68000118080808000000c070b200641a8016a41086a200641c0016a41086a280200360200200620062902c0013703a801200620062800b8013602a0012006200641b8016a41036a2800003600a3012012ad4220862011ad842113201421080c110b41d8cec48000410b10e38a80800021080c050b4104411410b280808000000b4104411410b280808000000b200c4122460d01200c41fd00470d00200641d8006a200720092009200b41016a220820092008491b10878380800041002d00fca3c680001a200628025c210c20062802582109411441002802c8a3c68000118180808000002208450d042008200936020c200841153602002008200c3602100c0c0b200641c8006a200720092009200b41016a220820092008491b10878380800041002d00fca3c680001a200628024c210c20062802482109411441002802c8a3c68000118180808000002208450d022008200936020c200841113602002008200c3602100c0b0b200141003602082001200b41016a360214200641d0016a200a2001108a8380800020062802d401210820062802d0014102460d0a02400240024020062802d801220c410b470d00200841f289c38000410b10888e808000450d01200841fd89c38000410b10888e808000450d020b2008200c41888ac38000410210e58a80800021080c0c0b0240200d418080808078460d0041d8cec48000410b10e78a80800021080c0c0b200110d78a80800022080d01200641d0016a200110f28a80800020062802d4012111024020062802d001220d418080808078470d00201121080c020b20062802d80121120c050b0240201041ff01714103460d0041e3cec48000410b10e78a80800021080c0b0b200110d78a80800022080d0a200641d0016a2001200820082008200810f98a80800020062802d001211420062d00e00122104103470d03201421080c0a0b418080808078210d0c0a0b4104411410b280808000000b4104411410b280808000000b200641c0016a41086a200f41086a2802003602002006200f2902003703c0012006200e2800003602b8012006200e41036a2800003600bb010b4101210b20012802142208200128021022094f0d030c000b0b4104411410b280808000000b4104411410b280808000000b200a28020021070b200641d0006a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a2006280254210c20062802502109411441002802c8a3c68000118180808000002208450d022008200936020c200841033602002008200c3602100b0240200d41808080807872418080808078460d00201141002802c0a3c68000118080808000000b418080808078210d0b200120012d001841016a3a0018418080808078210c2013a72109200110d98a808000210702400240200d418080808078460d0020070d01200641f8006a41086a200641a8016a41086a280200360200200620062903a801370378200620062802a001360270200620062800a301360073200d210c0c070b2007450d06024020072802000d00200741086a280200450d00200728020441002802c0a3c68000118080808000000b200741002802c0a3c68000118080808000000c060b418080808078210c0240200d0d00200721080c060b200941002802c0a3c6800011808080800000200721080c050b4104411410b280808000000b200641386a200720092009200820092008491b10878380800041002d00fca3c680001a200628023c210c20062802382101411441002802c8a3c680001181808080000022080d064104411410b280808000000b0240200b41dd00470d00418080808078210b41004194cfc4800041c8cec4800010e68a80800021080c010b200641d0016a200110f28a808000418080808078210b024020062802d001220a418080808078470d0020062802d40121080c010b20062802d401210d200128020c21090240024002400240024002400240024002400240200128021422082001280210220c4f0d0020063502d801211303400240200920086a2d0000220741776a0e24000005050005050505050505050505050505050505050500050505050505050505050503040b2001200841016a2208360214200c2008470d000b200c21080b200641186a2009200c200c200841016a2208200c2008491b10878380800041002d00fca3c680001a200628021c210c20062802182109411441002802c8a3c68000118180808000002208450d042008200936020c200841023602002008200c3602100c080b2001200841016a220836021402402008200c4f0d000340200920086a2d0000220b41776a220741174b0d084101200774419380800471450d082001200841016a2208360214200c2008470d000b200c21080b200641206a2009200c200c200841016a2208200c2008491b10878380800041002d00fca3c680001a2006280224210c20062802202109411441002802c8a3c68000118180808000002208450d052008200936020c200841053602002008200c3602100c070b200741dd00460d010b200641106a2009200c200c200841016a2208200c2008491b10878380800041002d00fca3c680001a2006280214210c20062802102109411441002802c8a3c68000118180808000002208450d022008200936020c200841073602002008200c3602100c050b41014194cfc4800041c8cec4800010e68a80800021080c040b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b0240200b41dd00470d00200641286a2009200c200c200841016a2208200c2008491b10878380800041002d00fca3c680001a200628022c210c20062802282109411441002802c8a3c68000118180808000002208450d052008200936020c200841153602002008200c3602100c010b200641d0016a2001200820082008200810f98a80800020062802d001210820062d00e00122104103460d0020064198016a200641dc016a280200360200200620062902d40137039001200620062800e101360288012006200641e4016a28000036008b012013422086200dad842113200a210b0c020b418080808078210b200a450d00200d41002802c0a3c68000118080808000000b0b200120012d001841016a3a0018418080808078210c2013a72109200110da8a808000210702400240200b418080808078460d0020070d01200641f8006a41086a20064190016a41086a280200360200200620062903900137037820062006280288013602702006200628008b01360073200b210c0c020b2007450d01024020072802000d00200741086a280200450d00200728020441002802c0a3c68000118080808000000b200741002802c0a3c68000118080808000000c010b418080808078210c0240200b450d00200941002802c0a3c68000118080808000000b200721080b200c418080808078460d0320002006290378370204200020103a0010200020062802703600112000200936021c2000200c36021820002008360200200041146a2006280073360000200020134220883e02202000410c6a20064180016a2802003602000c040b4104411410b280808000000b200641086a200720092009200820092008491b10878380800041002d00fca3c680001a200628020c210c20062802082101411441002802c8a3c680001181808080000022080d004104411410b280808000000b2008200136020c200841183602002000418080808078360218200020083602002008200c3602100c010b2008200110d68a80800021082000418080808078360218200020083602000b200641f0016a2480808080000bbc13010a7f23808080800041e0006b2206248080808000200128020c2107024002400240200141146a2802002208200141106a28020022094f0d002001410c6a210a0340200720086a2d0000220b41776a220c41174b0d024101200c74419380800471450d022001200841016a220836021420092008470d000b200921080b200641086a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a200628020c210c200628020821010240411441002802c8a3c68000118180808000002208450d002008200136020c200841053602002000200836020420004180808080783602002008200c3602100c020b4104411410b280808000000b02400240024002400240024002400240024002400240200b41db00460d00200b41fb00460d012001200641df006a41a4d5c4800010d58a80800021080c070b200120012d0018417f6a220c3a0018200841016a2108200c41ff0171450d08200120083602140240200820094f0d000340200720086a2d0000220b41776a220c41174b0d064101200c74419380800471450d062001200841016a220836021420092008470d000b200921080b200641186a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a200628021c210c20062802182109411441002802c8a3c68000118180808000002208450d012008200936020c200841023602002008200c360210418080808078210c0c050b200120012d0018417f6a220c3a0018200841016a2108200c41ff0171450d02200120083602140240024002400240024020082009490d00418080808078210d0c010b418080808078210d4100210e0340200a280200210702400240024002400240024002400240024002400240024002400240024003400240200720086a2d0000220c41776a0e24000003030003030303030303030303030303030303030300030303030303030303030304020b2001200841016a220836021420092008470d000b200921080c100b200c41fd00460d050b200e4101710d012008210b0c070b0240200e4101710d002008210b0c080b2001200841016a220b360214200b20094f0d0103402007200b6a2d0000220c41776a220841174b0d074101200874419380800471450d072001200b41016a220b3602142009200b470d000b200921080c020b200641286a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a200628022c210c20062802282109411441002802c8a3c68000118180808000002208450d032008200936020c200841083602002008200c3602100c0d0b200841016a21080b200641c8006a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a200628024c210c20062802482109411441002802c8a3c68000118180808000002208450d022008200936020c200841053602002008200c3602100c0b0b200d210c200f2108200d418080808078470d0e419ccfc48000410810e38a8080002108418080808078210c0c0e0b4104411410b280808000000b4104411410b280808000000b200c4122460d01200c41fd00470d00200641c0006a200720092009200b41016a220820092008491b10878380800041002d00fca3c680001a2006280244210c20062802402109411441002802c8a3c68000118180808000002208450d032008200936020c200841153602002008200c3602100c070b200641306a200720092009200b41016a220820092008491b10878380800041002d00fca3c680001a2006280234210c20062802302109411441002802c8a3c68000118180808000002208450d012008200936020c200841113602002008200c3602100c060b200141003602082001200b41016a360214200641d0006a200a2001108a838080002006280254210820062802504102460d05024002402006280258220c4108470d00200829000041002900f3ebc58000510d010b2008200c41fcebc58000410110e58a80800021080c060b0240200d418080808078460d00419ccfc48000410810e78a80800021080c070b0240200110d78a8080002208450d00418080808078210c0c0a0b200641d0006a200110f18a808000418080808078210c2006280254210f2006280250220d418080808078470d02200f21080c090b4104411410b280808000000b4104411410b280808000000b2006280258210b4101210e2001280214220820012802102209490d000b200a28020021070b200641386a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a200628023c210c20062802382109411441002802c8a3c68000118180808000002208450d022008200936020c200841033602002008200c3602100b418080808078210c200d418080808078460d030b418080808078210c0240200d450d00200f41002802c0a3c68000118080808000000b0c020b4104411410b280808000000b4104411410b280808000000b200120012d001841016a3a0018200110d98a808000210902400240200c418080808078460d002009450d06200c0d01200921080c050b2009450d04024020092802000d00200941086a280200450d00200928020441002802c0a3c68000118080808000000b200941002802c0a3c68000118080808000000c040b200841002802c0a3c6800011808080800000200921080c030b200641206a200720092009200820092008491b10878380800041002d00fca3c680001a2006280224210c20062802202101411441002802c8a3c680001181808080000022080d054104411410b280808000000b0240200b41dd00470d00418080808078210c410041c8cfc4800041c8cec4800010e68a80800021080c010b200641d0006a200110f18a808000418080808078210c024020062802502209418080808078470d00200628025421080c010b2006280258210b200628025421082009210c0b200120012d001841016a3a0018200110da8a80800021090240200c418080808078460d002009450d020240200c450d00200841002802c0a3c68000118080808000000b200921080c010b2009450d00024020092802000d00200941086a280200450d00200928020441002802c0a3c68000118080808000000b200941002802c0a3c68000118080808000000b2008200110d68a80800021082000418080808078360200200020083602040c030b2000200b360208200020083602042000200c3602000c020b200641106a200720092009200820092008491b10878380800041002d00fca3c680001a2006280214210c20062802102101411441002802c8a3c680001181808080000022080d004104411410b280808000000b2008200136020c200841183602002000200836020420004180808080783602002008200c3602100b200641e0006a2480808080000be13205077f017e017f027e057f2380808080004180026b2206248080808000200128020c210702400240024002400240024002400240024002400240024002400240024002400240024002400240200141146a2802002208200141106a28020022094f0d002001410c6a210a0340200720086a2d0000220b41776a220c41174b0d024101200c74419380800471450d022001200841016a220836021420092008470d000b200921080b200641d0016a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a20062802d401210c20062802d0012101411441002802c8a3c68000118180808000002208450d012008200136020c20084105360200200041033a0010200020083602002008200c3602100c120b024002400240200b41db00460d00200b41fb00460d012001200641ff016a4194d5c4800010d58a80800021080c110b200120012d0018417f6a220c3a0018200841016a2108200c41ff0171450d11200120083602140240200820094f0d000340200720086a2d0000220b41776a220c41174b0d0f4101200c74419380800471450d0f2001200841016a220836021420092008470d000b200921080b200641286a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a200628022c210c20062802282109411441002802c8a3c68000118180808000002208450d012008200936020c200841023602002008200c3602104103210c0c0e0b200120012d0018417f6a220c3a0018200841016a2108200c41ff0171450d0b20012008360214200820094f0d084200210d4103210e4100210b0340200a280200210702400240024002400240024002400240024002400240024002400240024002400240024003400240200720086a2d0000220c41776a0e24000003030003030303030303030303030303030303030300030303030303030303030304020b2001200841016a220836021420092008470d000b200921080c1b0b200c41fd00460d050b200b4101710d012008210b0c080b0240200b4101710d002008210b0c090b2001200841016a220b360214200b20094f0d0103402007200b6a2d0000220c41776a220841174b0d084101200874419380800471450d082001200b41016a220b3602142009200b470d000b200921080c020b200641386a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a200628023c210c20062802382109411441002802c8a3c68000118180808000002208450d032008200936020c200841083602002008200c3602104103210c0c180b200841016a21080b200641c8016a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a20062802cc01210c20062802c8012109411441002802c8a3c68000118180808000002208450d022008200936020c200841053602002008200c3602104103210c0c160b200da7450d024103210c0240200e41ff01714103470d004185cec48000410d10e38a80800021080c160b200f422088a72109200fa72108200e210c0c150b4104411410b280808000000b4104411410b280808000000b4103210c4184cec48000410110e38a80800021080c120b200c4122460d01200c41fd00470d00200641c0016a200720092009200b41016a220820092008491b10878380800041002d00fca3c680001a20062802c401210c20062802c0012109411441002802c8a3c68000118180808000002208450d032008200936020c200841153602002008200c3602104103210c0c110b200641c0006a200720092009200b41016a220820092008491b10878380800041002d00fca3c680001a2006280244210c20062802402109411441002802c8a3c68000118180808000002208450d012008200936020c200841113602002008200c3602104103210c0c100b200141003602082001200b41016a360214200641e0016a200a2001108a8380800020062802e4012108024020062802e0014102460d000240024002400240024020062802e801417f6a0e0d00090909090909090909090901090b20082d000041e300460d010c080b200841a9dac28000410d10888e8080000d07200e41ff01714103460d014103210c4185cec48000410d10e78a80800021080c130b200da74101460d05200110d78a80800022080d02200641e0016a200110ef8a80800020062802e0010d0120062903f001211020062903e801210f4201210d0c070b200110d78a80800022080d01200641e0016a200110cb8680800020062d00e0010d0020062d00e101210e0c060b20062802e40121080b4103210c0c0f0b4104411410b280808000000b4104411410b280808000000b4103210c4184cec48000410110e78a80800021080c0c0b4103210c200110d78a80800022080d0820014100360208200128020c210920012802142208200128021022074f0d07410021110340410020076b2112200841026a2108024002400240024002400240024002400240024002400240024002400240024002400240024002400240034002400240200920086a2213417e6a2d0000220b41776a0e2501010808010808080808080808080808080808080808080108060808080808080808080809000b200b41a57f6a0e21060707070707070707070704070707070707070207070707070307070707070706070b20012008417f6a3602142012200841016a22086a4102470d000b200721080c1d0b20012008417f6a220b3602140240200b2007490d002008417f6a210b0c1c0b2001200836021402402013417f6a2d000041f500470d0020082007200b2007200b4b1b220b460d1c2001200841016a2212360214024020132d000041ec00460d00200841016a21080c010b2012200b460d1c2001200841026a360214201341016a2d000041ec00460d08200841026a21080b20064198016a20092007200810878380800041002d00fca3c680001a200628029c0121092006280298012107411441002802c8a3c68000118180808000002208450d0c2008200736020c20084109360200200820093602100c200b20012008417f6a220b3602140240200b2007490d002008417f6a210b0c1a0b2001200836021402402013417f6a2d000041f200470d0020082007200b2007200b4b1b220b460d1a2001200841016a2212360214024020132d000041f500460d00200841016a21080c010b2012200b460d1a2001200841026a360214201341016a2d000041e500460d07200841026a21080b200641a8016a20092007200810878380800041002d00fca3c680001a20062802ac01210920062802a8012107411441002802c8a3c68000118180808000002208450d0a2008200736020c20084109360200200820093602100c1f0b20012008417f6a220b3602140240200b2007490d002008417f6a210b0c180b2001200836021402402013417f6a2d000041e100470d0020082007200b2007200b4b1b220b460d182001200841016a2212360214024020132d000041ec00460d00200841016a21080c010b2012200b460d182001200841026a22123602140240201341016a2d000041f300460d00200841026a21080c010b2012200b460d182001200841036a360214201341026a2d000041e500460d06200841036a21080b200641b8016a20092007200810878380800041002d00fca3c680001a20062802bc01210920062802b8012107411441002802c8a3c68000118180808000002208450d082008200736020c20084109360200200820093602100c1e0b20012008417f6a360214200a108e8380800022080d1a0c040b02402001280200200128020822086b201141017122094f0d0020012008200910ab86808000200128020821080b02402009450d00200128020420086a20143a0000200841016a21080b200120083602082001200128021441016a360214410021150c040b200b41506a41ff0171410a490d0120064188016a2009200720072008417f6a220820072008491b10878380800041002d00fca3c680001a200628028c0121092006280288012107411441002802c8a3c68000118180808000002208450d042008200736020c2008410a360200200820093602100c1b0b20012008417f6a3602140b200110d18a80800022080d160b4101211502402011410171450d002014210b0c010b20012802082208450d0d20012008417f6a2208360208200128020420086a2d0000210b0b02402001280214221320012802102207490d00200b21140c060b20012802042111200128020c210920012802082112200b21140340201321080240024002400240034002400240200920086a2d0000220b41776a0e2401010e0e010e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e010e0e0e0e0e0e0e0e0e0e0e03000b200b41dd00460d03200b41fd00460d040c0d0b2001200841016a220836021420072008470d000b200721130c0a0b2015410171450d0b2001200841016a22083602140c0b0b201441ff017141db00470d090c010b201441ff017141fb00470d080b2001200841016a22133602142012450d0d20012012417f6a2212360208201120126a2d0000211441012115201320074f0d050c000b0b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b200841016a21130b41022109024002400240201441ff0171220841db00460d00200841fb00470d01410321090b200641f8006a200a28020020072007201341016a220820072008491b10878380800041002d00fca3c680001a200628027c21072006280278210b411441002802c8a3c68000118180808000002208450d012008200b36020c20082009360200200820073602100c130b419ccac480004128418ccdc4800010f880808000000b4104411410b280808000000b2015410171450d004107210b201441ff0171220a41db00460d02200a41fb00460d01419ccac480004128419ccdc4800010f880808000000b201441ff017141fb00470d020240200820074f0d00034002400240200920086a2d000041776a220b41194b0d004101200b744193808004710d01200b4119470d002001200841016a360214200a108e8380800022080d10200128020c21090240024002400240024020012802142208200128021022074f0d0003400240200920086a2d000041776a0e320000030300030303030303030303030303030303030303000303030303030303030303030303030303030303030303030304030b2001200841016a220836021420072008470d000b200721080b200641e8006a200920072007200841016a220820072008491b10878380800041002d00fca3c680001a200628026c21092006280268210c411441002802c8a3c68000118180808000002208450d022008200c36020c4103210c20084103360200200820093602100c170b200641e0006a200920072007200841016a220820072008491b10878380800041002d00fca3c680001a2006280264210920062802602107411441002802c8a3c68000118180808000002208450d022008200736020c20084106360200200820093602100c160b2001200841016a22083602140c080b4104411410b280808000000b4104411410b280808000000b200641d0006a200920072007200841016a220820072008491b10878380800041002d00fca3c680001a20062802542109200628025021070240411441002802c8a3c68000118180808000002208450d002008200736020c20084111360200200820093602100c130b4104411410b280808000000b2001200841016a220836021420072008470d000b200721080b200641d8006a200920072007200841016a220820072008491b10878380800041002d00fca3c680001a200628025c21092006280258210c0240411441002802c8a3c68000118180808000002208450d002008200c36020c4103210c20084103360200200820093602100c100b4104411410b280808000000b4108210b0b200641c8006a200920072007200841016a220820072008491b10878380800041002d00fca3c680001a200628024c2109200628024821070240411441002802c8a3c68000118180808000002208450d002008200736020c2008200b360200200820093602100c0e0b4104411410b280808000000b4101211120082007490d000c080b0b4101210b20012802142208200128021022094f0d080c000b0b4104411410b280808000000b4104411410b280808000000b200641b0016a20092007200b10878380800041002d00fca3c680001a20062802b401210920062802b00121070240411441002802c8a3c68000118180808000002208450d002008200736020c20084105360200200820093602100c070b4104411410b280808000000b200641a0016a20092007200b10878380800041002d00fca3c680001a20062802a401210920062802a00121070240411441002802c8a3c68000118180808000002208450d002008200736020c20084105360200200820093602100c060b4104411410b280808000000b20064190016a20092007200b10878380800041002d00fca3c680001a200628029401210920062802900121070240411441002802c8a3c68000118180808000002208450d002008200736020c20084105360200200820093602100c050b4104411410b280808000000b200641f0006a200920072007200841016a220820072008491b10878380800041002d00fca3c680001a20062802742109200628027021070240411441002802c8a3c68000118180808000002208450d002008200736020c20084105360200200820093602100c040b4104411410b280808000000b0c020b200a28020021070b20064180016a200720092009200841016a220820092008491b10878380800041002d00fca3c680001a2006280284012109200628028001210c411441002802c8a3c68000118180808000002208450d012008200c36020c4103210c20084103360200200820093602100b200120012d001841016a3a0018200110d98a808000210b0240200c41ff01714103460d0020082107200b2108200b0d060c050b200b450d050240200b2802000d00200b41086a280200450d00200b28020441002802c0a3c68000118080808000000b200b41002802c0a3c68000118080808000000c050b4104411410b280808000000b200641306a200720092009200820092008491b10878380800041002d00fca3c680001a2006280234210c20062802302101411441002802c8a3c680001181808080000022080d054104411410b280808000000b0240200b41dd00470d004103210c410041c0cec4800041c8cec4800010e68a80800021080c010b200641e0016a200110ef8a808000024020062802e0010d00200128020c210b0240024002400240024002400240024002402001280214220c200128021022074f0d00200641f0016a290300211020062802ec01210920062802e801210803400240200b200c6a2d0000220a41776a0e24000005050005050505050505050505050505050505050500050505050505050505050503040b2001200c41016a220c3602142007200c470d000b2007210c0b200641106a200b20072007200c41016a220820072008491b10878380800041002d00fca3c680001a2006280214210c20062802102109411441002802c8a3c68000118180808000002208450d032008200936020c200841023602002008200c3602104103210c0c090b2001200c41016a220c3602140240200c20074f0d000340200b200c6a2d0000221341776a220a41174b0d084101200a74419380800471450d082001200c41016a220c3602142007200c470d000b2007210c0b200641186a200b20072007200c41016a220820072008491b10878380800041002d00fca3c680001a200628021c210c20062802182109411441002802c8a3c68000118180808000002208450d042008200936020c200841053602002008200c3602104103210c0c080b200a41dd00460d040b200641086a200b20072007200c41016a220820072008491b10878380800041002d00fca3c680001a200628020c210c20062802082109411441002802c8a3c68000118180808000002208450d012008200936020c200841073602002008200c3602104103210c0c060b4104411410b280808000000b4104411410b280808000000b4104411410b280808000000b4103210c410141c0cec4800041c8cec4800010e68a80800021080c020b02400240201341dd00470d00200641206a200b20072007200c41016a220820072008491b10878380800041002d00fca3c680001a2006280224210c20062802202109411441002802c8a3c68000118180808000002208450d012008200936020c200841153602002008200c3602104103210c0c030b200641e0016a200110cb8680800020062d00e0010d0120062d00e101210c0c020b4104411410b280808000000b20062802e40121084103210c0b200120012d001841016a3a0018200110da8a808000210b0240200c41ff01714103460d0020082107200b2108200b450d010c020b200b450d010240200b2802000d00200b41086a280200450d00200b28020441002802c0a3c68000118080808000000b200b41002802c0a3c68000118080808000000c010b200020062800d9013600112000200c3a0010200020103703082000200936020420002007360200200041146a200641dc016a2800003600000c030b2008200110d68a8080002108200041033a0010200020083602000c020b2006200720092009200820092008491b10878380800041002d00fca3c680001a2006280204210c20062802002101411441002802c8a3c680001181808080000022080d004104411410b280808000000b2008200136020c20084118360200200041033a0010200020083602002008200c3602100b20064180026a2480808080000be01b010d7f2380808080004190016b2207248080808000024002400240024002400240024002400240024002400240024002400240024020010e020102000b200041146a2108200141146c2209416c6a41146e210a200041106a280200210b2000410c6a280200210c2000280200210d4100210e200041046a280200220f21100340410021110240200f2008200e41146c6a221241046a2802002213200f2013491b2213450d0020122802002112410021110340200d20116a2d0000201220116a2d0000470d012013201141016a2211470d000b201321110b2011201020112010491b2110200e41016a220e200a470d000b20040d03201020024b0d024100210d0c040b0240200328020822112003280200470d002003201110a885808000200328020821110b200328020420116a41003a00002003200328020841016a3602080c0d0b200041106a28020021112000410c6a280200211320054101470d030240201120064f0d002007410c6a20113602002007201336020820074180808080783602040c0c0b200741106a201320114100280298a3c680001185808080000041002d00fca3c680001a412041002802c8a3c68000118180808000002211450d0420112007290010370000201141186a200741106a41186a290000370000201141106a200741106a41106a290000370000201141086a200741106a41086a2900003700002007412036020c20072011360208200741203602040c0b0b2010200f4b0d04200741003602702007428080808010370268200020012010200741e8006a41002005200610fa8a808000200728026c21132007280268210202400240200728027022114120490d00200741106a201320114100280298a3c6800011858080800000200741203602502007200741d0006a36025c200741dc006a200310c08a80800002402003280200200328020822116b411f4b0d0020032011412010b182808000200328020821110b2003201141206a360208200328020420116a22112007290010370000201141086a200741106a41086a290000370000201141106a200741106a41106a290000370000201141186a200741106a41186a2900003700000c010b2007201136025c2007200741dc006a360210200741106a200310c08a80800002402003280200200328020822126b20114f0d0020032012201110b182808000200328020821120b200328020420126a2013201110848e8080001a2003201220116a3602080b2002450d0b201341002802c0a3c68000118080808000000c0b0b4100210a201020024d0d002010200f4b0d04200d20026a210d201020026b210a201021020b200741c8006a4200370300200741c0006a4200370300200741386a4200370300200741306a4200370300200741286a4200370300200741106a41106a4200370300200741186a4200370300200742003703102002200f46210841002111034020112110200820014b0d0741002113024020082001460d002009200841146c22116b2112200020116a211141002113034020022011280204220e4f0d08201128020020026a2d0000201041ff0171470d01201141146a2111201341016a21132012416c6a22120d000b0b200741106a20104102746a2013360200201320086a2108201041016a22114110470d000b02402002200f470d000240024020054101470d00200b20064f0d010b2007200b3602582007200c360254418080808078211120074180808080783602500c090b200741e8006a200c200b4100280298a3c680001185808080000041002d00fca3c680001a412041002802c8a3c68000118180808000002211450d0520112007290068370000201141186a200741e8006a41186a290000370000201141106a200741e8006a41106a290000370000201141086a200741e8006a41086a290000370000200741203602582007201136025420074120360250200741e8006a200741d0006a108b878080002007280270210b200728026c210c200728026821110c080b418180808078211120074181808080783602500c070b2007410c6a20113602002007201336020820074180808080783602040c070b4101412010b280808000000b2010200f41c4d6c48000109581808000000b2010200f41d4d6c48000109581808000000b4101412010b280808000000b2002200e4184d7c4800010f980808000000b2008200141f4d6c48000109481808000000b02400240024002400240200d450d00024002400240024002404101410241042011418080808078461b2011418180808078461b417f6a0e0400010203000b413e210e200a413e200a413e491b211241bf012108418001210f0c030b413e210e200a413e200a413e491b211241ff01210841c001210f0c020b411e210e200a411e200a411e491b2112413f21084120210f0c010b410e210e200a410e200a410e491b2112411f21084110210f0b0240200a4101712213450d00200d2d000021100b20074180016a4102360200200741fc006a200a417e71360200200720103a0075200741013a0070200741013602682007200a20126b36026c200720133a00742007200d20136a36027820072008200f201272200e200a491b3a00712003200741e8006a1099878080002007280228410047410674200728022c4100474107747220072802204100474104742007280224410047410574722007280214410047410174200728021041004772200728021841004741027472200728021c41004741037472727221122007280234410047410174200728023041004772200728023841004741027472200728023c41004741037472200728024041004741047472200728024441004741057472200728024841004741067472200728024c41004741077472210d02402003280200200328020822136b41014b0d0020032013410210ab86808000200328020821130b200328020420136a200d410874201241ff0171723b00002003201341026a221336020802402011418180808078460d0002402011418080808078470d002007200b36028c0120072007418c016a36025c200741dc006a200310c08a80800002402003280200200328020822116b200b4f0d0020032011200b10ab86808000200328020821110b200328020420116a200c200b10848e8080001a20032011200b6a3602080c010b0240200328020020136b200b4f0d0020032013200b10ab86808000200328020821130b200328020420136a200c200b10848e8080001a20032013200b6a3602082011450d00200c41002802c0a3c68000118080808000000b200241016a2110200728025022084181808080784721112005450d014100211303400240200741106a20136a2802002212450d00201220116a22022012490d05200220014b0d0620074100360264200742808080801037025c2000201141146c6a20122010200741dc006a20044101200610fa8a80800020072802602112200728025c210d02400240200728026422114120490d00200741e8006a201220114100280298a3c68000118580808000002007412036028801200720074188016a36028c012007418c016a200310c08a80800002402003280200200328020822116b411f4b0d0020032011412010b182808000200328020821110b2003201141206a360208200328020420116a22112007290068370000201141086a200741e8006a41086a290000370000201141106a200741e8006a41106a290000370000201141186a200741e8006a41186a2900003700000c010b2007201136028c0120072007418c016a360268200741e8006a200310c08a808000024020032802002003280208220e6b20114f0d002003200e201110b1828080002003280208210e0b2003280204200e6a2012201110848e8080001a2003200e20116a3602080b0240200d450d00201241002802c0a3c68000118080808000000b200221110b201341046a221341c000470d000c030b0b200741f4006a42003702002007410136026c200741f4d2c480003602682007200741dc006a360270200741e8006a41e4d3c4800010f680808000000b4100211303400240200741106a20136a2802002212450d00201220116a22022012490d03200220014b0d0420074100360264200742808080801037025c2000201141146c6a20122010200741dc006a20044100200610fa8a80800020072802602112200728025c210d02400240200728026422114120490d00200741e8006a201220114100280298a3c68000118580808000002007412036028801200720074188016a36028c012007418c016a200310c08a80800002402003280200200328020822116b411f4b0d0020032011412010b182808000200328020821110b2003201141206a360208200328020420116a22112007290068370000201141086a200741e8006a41086a290000370000201141106a200741e8006a41106a290000370000201141186a200741e8006a41186a2900003700000c010b2007201136028c0120072007418c016a360268200741e8006a200310c08a808000024020032802002003280208220e6b20114f0d002003200e201110b1828080002003280208210e0b2003280204200e6a2012201110848e8080001a2003200e20116a3602080b0240200d450d00201241002802c0a3c68000118080808000000b200221110b201341046a221341c000470d000b0b2008418280808078480d032008450d03200728025441002802c0a3c68000118080808000000c030b2011200241e4d6c48000109681808000000b2002200141e4d6c48000109581808000000b0240200041046a28020022112002490d002003200028020020026a201120026b200741046a10af858080000c010b2002201141b4d6c48000109481808000000b20074190016a2480808080000bc50401057f23808080800041f0006b220424808080800020044100360214200442808080801037020c41002d00fca3c680001a0240410441002802c8a3c68000118180808000002205450d00200541003602002004410036025c20044200370254200442818080802037024c20042005360248200441003602442004410036023c200442003702342004410036022c2004420037022420042004410c6a360260200441186a200441246a109c878080002004410036026c2004428080808010370264200428021c2206200428022022074100200441e4006a20012002200310fa8a80800020042802642101200020042802682208200428026c4100280298a3c680001185808080000002402007450d002007410171210041002102024020074101460d00200641206a21032007417e7121074100210203400240200341686a280200450d002003416c6a28020041002802c0a3c68000118080808000000b02402003417c6a280200450d00200328020041002802c0a3c68000118080808000000b200341286a21032007200241026a2202470d000b0b2000450d002006200241146c6a2203280208450d00200341086a28020441002802c0a3c68000118080808000000b02402004280218450d00200641002802c0a3c68000118080808000000b200541002802c0a3c68000118080808000000240200428020c450d00200428021041002802c0a3c68000118080808000000b02402001450d00200841002802c0a3c68000118080808000000b200441f0006a2480808080000f0b4104410410b280808000000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206410036023020064280808080c00037032820064100360220200641003602182006419682808000360210200642ab8ad7e1bb97ae9f51370308200642f2f9a5a49996c5e03237030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bb30403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420d370224200541b4fbc480003602202005410e36021c200541a6fbc480003602182005419482808000360210200542c0969aec91e181fcf200370308200542d7a7fbff94a5afcaf800370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420c37022420054194fbc480003602202005410d36021c20054187fbc48000360218200541ed83808000360210200542e7dca5b4fdd9bda7a17f370308200542accee9bcd783d4ea3937030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a4100290380f9c480003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541013a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c20064207370224200641b0f2c480003602202006410436021c200641a4e4c48000360218200641ea81808000360210200642b891b68c98adebcf61370308200642e7b0a091f3ed9c85c50037030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d00200941002903e0a7c580003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c20054207370224200541e3e4c480003602202005410936021c200541dae4c48000360218200541f581808000360210200542ab8bffbed784ffa5937f370308200542c194a6a793ccc3a857370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c20054204370224200541f7e4c480003602202005410d36021c200541eae4c48000360218200541888180800036021020054298848fa1dab08ba174370308200542febac4ad81b6fafcb37f37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a41002903f0f9c480003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541063a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000bf50403047f017e027f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c200542133702242005418993c580003602202005410936021c2005418093c58000360218200541ee83808000360210200542e5fbf9f4fcf79cdd3a3703082005429bb0d3a2d7cce1b225370300200441086a41086a200641016a2207360200200420042902142208370308024020072008a7470d00200441086a200710a486808000200428021021070b200428020c200741386c6a2205420437022c2005420a370224200541d192c580003602202005410536021c200541b393c58000360218200541bc82808000360210200542899ac8f29d8ce69ac300370308200542e78dcee4d0becc975737030020042802082109200428020c210a41002d00fca3c680001a0240412841002802c8a3c68000118180808000002206450d00200641206a41002902908bc58000370200200641186a41002902888bc58000370200200641106a41002902808bc58000370200200641086a41002902f88ac58000370200200641002902f08ac580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541093a00202005200336021c2005200236021820054105360214200520063602102005410536020c2005200741016a3602082005200a3602042005200936020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104412810b280808000000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c200642ed00370224200641b2b1c5800036022020064100360218200641f482808000360210200642c9e8d484edccb5e26c370308200642a79fcbb9e09aeaff7c37030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641033a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000baa0301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c20064203370224200641e1f2c480003602202006410536021c200641f0adc58000360218200641b580808000360210200642e88488d0c0e3aebc13370308200642d7c9cb8fc1cf97db3e37030041002d00fca3c680001a20042802042107200428020821080240411841002802c8a3c68000118180808000002209450d00200941106a41002902e8adc58000370200200941086a41002902e0adc58000370200200941002902d8adc580003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a2206410a3a00202006200336021c2006200236021820064103360214200620093602102006410336020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104411810b280808000000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c2006420d370224200641cdf2c480003602202006410536021c200641c8f2c48000360218200641ef83808000360210200642ce9de6a5b39faacabf7f3703082006428f8dbaa0f1e8bfc67537030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d0020094100290380ebc480003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641043a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410336021c200541f392c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410636021c200541c1fbc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a41002903d0a3c580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a2205410b3a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000bd40403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c20054214370224200541df92c580003602202005410336021c200541f392c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410836021c200541b893c58000360218200541bc82808000360210200542899ac8f29d8ce69ac300370308200542e78dcee4d0becc975737030020042802082108200428020c210941002d00fca3c680001a0240411841002802c8a3c6800011818080800000220a450d00200a41106a41002902d48fc58000370200200a41086a41002902cc8fc58000370200200a41002902c48fc580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541083a00202005200336021c20052002360218200541033602142005200a3602102005410336020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104411810b280808000000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c20064207370224200641b0f2c480003602202006410436021c200641acf2c48000360218200641ea81808000360210200642b891b68c98adebcf61370308200642e7b0a091f3ed9c85c50037030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d00200941002903b8e7c480003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641023a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410636021c200541a0fbc48000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c20054207370224200541e3e4c480003602202005410436021c20054183fbc48000360218200541f581808000360210200542ab8bffbed784ffa5937f370308200542c194a6a793ccc3a85737030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a41002903e8fac480003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541053a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410336021c200541f392c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410636021c200541c1fbc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a41002903d0a2c580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541053a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000bc00201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206410036023020064280808080c0003703282006410036022020064100360218200641bd80808000360210200642efc9c9edb5e7b3a6c700370308200642acf6debeefe0d9c8d30037030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206410036023020064280808080c00037032820064100360220200641003602182006419782808000360210200642fb8387e2d6839cd949370308200642d9adfaebb1c192ed5037030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641003a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410336021c200541f392c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410636021c200541c1fbc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a41002903f09ac580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541073a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410336021c200541f392c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410636021c200541c1fbc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a41002903909ac580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541143a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000b900301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c20064207370224200641b0f2c480003602202006410436021c200641acf2c48000360218200641ea81808000360210200642b891b68c98adebcf61370308200642e7b0a091f3ed9c85c50037030041002d00fca3c680001a2004280204210720042802082108024041c80041002802c8a3c68000118180808000002206450d00200641e4f1c4800041c80010848e80800021090240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a2206410b3a00202006200336021c2006200236021820064109360214200620093602102006410936020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b410441c80010b280808000000bc30403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410736021c200541f0fac48000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410636021c200541c1fbc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240411041002802c8a3c6800011818080800000220a450d00200a41086a4100290298a1c58000370200200a4100290290a1c580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541013a00202005200336021c20052002360218200541023602142005200a3602102005410236020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104411010b280808000000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c2006420c370224200641f7fac480003602202006410336021c200641f392c58000360218200641f081808000360210200642a4d2928ecac0faa432370308200642c4ccab9c81ebb4b8db0037030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d00200941002903b09ac580003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a2206410e3a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000baa0301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c20064203370224200641e1f2c480003602202006410536021c200641f0adc58000360218200641b580808000360210200642e88488d0c0e3aebc13370308200642d7c9cb8fc1cf97db3e37030041002d00fca3c680001a20042802042107200428020821080240411841002802c8a3c68000118180808000002209450d00200941106a41002902e4aec58000370200200941086a41002902dcaec58000370200200941002902d4aec580003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a2206410b3a00202006200336021c2006200236021820064103360214200620093602102006410336020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104411810b280808000000bbc0301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c20064207370224200641b0f2c480003602202006410436021c200641acf2c48000360218200641ea81808000360210200642b891b68c98adebcf61370308200642e7b0a091f3ed9c85c50037030041002d00fca3c680001a20042802042107200428020821080240412041002802c8a3c68000118180808000002209450d00200941186a41002902dce6c48000370200200941106a41002902d4e6c48000370200200941086a41002902cce6c48000370200200941002902c4e6c480003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641033a00202006200336021c2006200236021820064104360214200620093602102006410436020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104412010b280808000000bbf0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206410036023020064280808080c0003703282006410036022020064100360218200641ea81808000360210200642b891b68c98adebcf61370308200642e7b0a091f3ed9c85c50037030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c20054207370224200541b0f2c480003602202005410336021c200541c0a8c58000360218200541ea81808000360210200542b891b68c98adebcf61370308200542e7b0a091f3ed9c85c500370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420f370224200541c3a8c580003602202005410536021c200541cc92c580003602182005419183808000360210200542f6d183c8fca4ffd45a370308200542f99f94a5fdd3dbbaf90037030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a41002903b8a8c580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541023a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000bbf0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c200642e900370224200641d7afc5800036022020064100360218200641f082808000360210200642c2a7b4b19ace9c92d9003703082006429691e8a3b686eae56237030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bb90403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c20054214370224200541df92c580003602202005410436021c200541db92c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c20054204370224200541f7e4c480003602202005410a36021c200541f692c58000360218200541888180800036021020054298848fa1dab08ba174370308200542febac4ad81b6fafcb37f37030020042802082108200428020c210941002d00fca3c680001a024041f80041002802c8a3c68000118180808000002205450d00200541cc83c5800041f80010848e808000210a0240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541043a00202005200336021c200520023602182005410f3602142005200a3602102005410f36020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b410441f80010b280808000000b980503047f017e027f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c20054214370224200541df92c580003602202005410436021c200541db92c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2207360200200420042902142208370308024020072008a7470d00200441086a200710a486808000200428021021070b200428020c200741386c6a2205420437022c2005420a370224200541d192c580003602202005410536021c200541cc92c58000360218200541bc82808000360210200542899ac8f29d8ce69ac300370308200542e78dcee4d0becc975737030020042802082109200428020c210a41002d00fca3c680001a0240413841002802c8a3c68000118180808000002205450d00200541306a41002902c492c58000370200200541286a41002902bc92c58000370200200541206a41002902b492c58000370200200541186a41002902ac92c58000370200200541106a41002902a492c58000370200200541086a410029029c92c580003702002005410029029492c580003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641003a00202006200336021c2006200236021820064107360214200620053602102006410736020c2006200741016a3602082006200a3602042006200936020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104413810b280808000000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c2006420a370224200641d192c580003602202006410636021c200641c1fbc48000360218200641ef8080800036021020064293888c8f89fdc6ec9e7f370308200642a5e9e3ab9e929adc2c37030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d00200941002903989fc580003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a2206410f3a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c20064203370224200641bcf2c480003602202006410536021c200641b7f2c48000360218200641ef8080800036021020064293888c8f89fdc6ec9e7f370308200642a5e9e3ab9e929adc2c37030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d0020094100290380e8c480003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410336021c200541f392c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410636021c200541c1fbc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a41002903c89dc580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541043a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206410036023020064280808080c00037032820064100360220200641003602182006419582808000360210200642f69a89928aa399ea003703082006429198b9e48fc6c3ad1d37030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641003a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bd40503047f017e027f23808080800041206b2204248080808000200441106a41086a22054100360200200442808080808001370210200441106a410010a48680800020042802142005280200220641386c6a2207420437022c20074214370224200741df92c580003602202007410636021c200741ad93c58000360218200741f081808000360210200742a4d2928ecac0faa432370308200742c4ccab9c81ebb4b8db00370300200441086a200641016a2206360200200420042902102208370300024020062008a7470d002004200610a486808000200428020821060b2004280204200641386c6a2207420437022c20074214370224200741df92c580003602202007410436021c200741db92c58000360218200741f081808000360210200742a4d2928ecac0faa432370308200742c4ccab9c81ebb4b8db003703002005200641016a2206360200200420042903002208370310024020062008a7470d00200441106a200610a486808000200428021821060b2004280214200641386c6a2207420437022c2007420a370224200741d192c580003602202007410536021c200741cc92c58000360218200741bc82808000360210200742899ac8f29d8ce69ac300370308200742e78dcee4d0becc9757370300200428021021092004280214210a41002d00fca3c680001a0240411041002802c8a3c68000118180808000002205450d00200541086a41002902e48ec58000370200200541002902dc8ec580003702000240200128020822072001280200470d002001200710a086808000200128020821070b2001280204200741246c6a220741023a00202007200336021c2007200236021820074102360214200720053602102007410236020c2007200641016a3602082007200a3602042007200936020020002001290200370200200141086a2207200728020041016a2207360200200041086a2007360200200441206a2480808080000f0b4104411010b280808000000bbf0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c2006421c370224200641f5b2c5800036022020064100360218200641ed82808000360210200642e89d8d84e9a7e0ebbf7f370308200642ffa1f591d896faeca07f37030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641003a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c2006420737022420064185adc580003602202006410536021c20064180adc58000360218200641f083808000360210200642fef18cfcdbfae085d000370308200642c1e482bcf69e92d65b37030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d00200941002903f8acc580003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641093a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206410036023020064280808080c0003703282006410036022020064100360218200641fb82808000360210200642a8c786dcd8d2a98d17370308200642cecfe0e5d1c6dbf75737030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c20064208370224200641eef2c480003602202006410436021c200641eaf2c48000360218200641f183808000360210200642aac2d79da4bfd9a04e370308200642e6afce95f6a1ffa6c30037030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d0020094100290398e5c480003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641053a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000bc00201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206410036023020064280808080c000370328200641003602202006410036021820064193828080003602102006429ccab49c93e2e495cf00370308200642d1c5efcdcd82cde1ff0037030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641003a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bd20101027f41002d00fca3c680001a0240410841002802c8a3c68000118180808000002204450d00200441002903c0acc580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541083a00202005200336021c200520023602182005410136021420052004360210200542808080801037020820054280808080800137020020002001290200370200200141086a2201200128020041016a2201360200200041086a20013602000f0b4104410810b280808000000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c20064207370224200641b0f2c480003602202006410336021c200641c0a8c58000360218200641ea81808000360210200642b891b68c98adebcf61370308200642e7b0a091f3ed9c85c50037030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d00200941002903d0a9c580003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641043a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410336021c200541f392c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410636021c200541c1fbc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a4100290398a0c580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541123a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000b820401067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c20064207370224200641e3e4c480003602202006410936021c200641dae4c48000360218200641f581808000360210200642ab8bffbed784ffa5937f370308200642c194a6a793ccc3a85737030041002d00fca3c680001a2004280204210720042802082108024041c00041002802c8a3c68000118180808000002206450d00200641386a41002902a8eec48000370200200641306a41002902a0eec48000370200200641286a4100290298eec48000370200200641206a4100290290eec48000370200200641186a4100290288eec48000370200200641106a4100290280eec48000370200200641086a41002902f8edc48000370200200641002902f0edc480003702000240200128020822092001280200470d002001200910a086808000200128020821090b2001280204200941246c6a2209410a3a00202009200336021c2009200236021820094108360214200920063602102009410836020c2009200541016a360208200920083602042009200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b410441c00010b280808000000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c2006421f37022420064191b3c5800036022020064100360218200641f382808000360210200642e5c18a8c83d9a2ecc200370308200642b4ff9ef1bdc3bdda2837030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641033a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c20064207370224200641b0f2c480003602202006410436021c200641a4e4c48000360218200641ea81808000360210200642b891b68c98adebcf61370308200642e7b0a091f3ed9c85c50037030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d00200941002903f8a9c580003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641053a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000b980503047f017e027f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420a370224200541d192c580003602202005410536021c200541cc92c58000360218200541bc82808000360210200542899ac8f29d8ce69ac300370308200542e78dcee4d0becc9757370300200441086a41086a200641016a2207360200200420042902142208370308024020072008a7470d00200441086a200710a486808000200428021021070b200428020c200741386c6a2205420437022c20054204370224200541f7e4c480003602202005410a36021c200541f692c58000360218200541888180800036021020054298848fa1dab08ba174370308200542febac4ad81b6fafcb37f37030020042802082109200428020c210a41002d00fca3c680001a0240413841002802c8a3c68000118180808000002205450d00200541306a41002902dc87c58000370200200541286a41002902d487c58000370200200541206a41002902cc87c58000370200200541186a41002902c487c58000370200200541106a41002902bc87c58000370200200541086a41002902b487c58000370200200541002902ac87c580003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a2206410a3a00202006200336021c2006200236021820064107360214200620053602102006410736020c2006200741016a3602082006200a3602042006200936020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104413810b280808000000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c2006420c370224200641f7fac480003602202006410736021c200641f0fac48000360218200641f081808000360210200642a4d2928ecac0faa432370308200642c4ccab9c81ebb4b8db0037030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d00200941002903c8f9c480003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641033a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c20064213370224200641f0abc580003602202006410836021c200641e8abc58000360218200641ef8080800036021020064293888c8f89fdc6ec9e7f370308200642a5e9e3ab9e929adc2c37030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d00200941002903e0abc580003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641073a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000bbf0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206410036023020064280808080c0003703282006410036022020064100360218200641f283808000360210200642f9beb892cc84b284c800370308200642cc9a879ae783ad825437030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bc30503047f017e027f23808080800041206b2204248080808000200441106a41086a22054100360200200442808080808001370210200441106a410010a48680800020042802142005280200220641386c6a2207420437022c2007420c370224200741f7fac480003602202007410436021c200741daa3c58000360218200741f081808000360210200742a4d2928ecac0faa432370308200742c4ccab9c81ebb4b8db00370300200441086a200641016a2206360200200420042902102208370300024020062008a7470d002004200610a486808000200428020821060b2004280204200641386c6a2207420437022c2007420c370224200741f7fac480003602202007410236021c200741d8a3c58000360218200741f081808000360210200742a4d2928ecac0faa432370308200742c4ccab9c81ebb4b8db003703002005200641016a2206360200200420042903002208370310024020062008a7470d00200441106a200610a486808000200428021821060b2004280214200641386c6a2207420437022c2007420a370224200741d192c580003602202007410636021c200741c1fbc48000360218200741ef8080800036021020074293888c8f89fdc6ec9e7f370308200742a5e9e3ab9e929adc2c370300200428021021092004280214210a41002d00fca3c680001a0240410841002802c8a3c68000118180808000002205450d00200541002903f09fc580003702000240200128020822072001280200470d002001200710a086808000200128020821070b2001280204200741246c6a220741023a00202007200336021c2007200236021820074101360214200720053602102007410136020c2007200641016a3602082007200a3602042007200936020020002001290200370200200141086a2207200728020041016a2207360200200041086a2007360200200441206a2480808080000f0b4104410810b280808000000b9b0301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c2006421f370224200641ffaac580003602202006410336021c200641fcaac58000360218200641c481808000360210200642e0f4e1e1b7dafaaf8d7f3703082006428ca3c7fa85a49cf2a17f37030041002d00fca3c680001a20042802042107200428020821080240411041002802c8a3c68000118180808000002209450d00200941086a41002902f4aac58000370200200941002902ecaac580003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641063a00202006200336021c2006200236021820064102360214200620093602102006410236020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104411010b280808000000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420a370224200541d192c580003602202005410336021c20054185a4c58000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410336021c20054182a4c58000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a41002903d09fc580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541153a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410336021c200541f392c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410636021c200541c1fbc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a41002903c09ec580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a2205410a3a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000be40403047f017e027f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c20054203370224200541c5f2c480003602202005410636021c200541bff2c48000360218200541ea81808000360210200542b891b68c98adebcf61370308200542e7b0a091f3ed9c85c500370300200441086a41086a200641016a2207360200200420042902142208370308024020072008a7470d00200441086a200710a486808000200428021021070b200428020c200741386c6a2205420437022c20054203370224200541e1f2c480003602202005410736021c200541daf2c48000360218200541b580808000360210200542e88488d0c0e3aebc13370308200542d7c9cb8fc1cf97db3e37030020042802082109200428020c210a41002d00fca3c680001a0240412041002802c8a3c68000118180808000002206450d00200641186a4100290284eac48000370200200641106a41002902fce9c48000370200200641086a41002902f4e9c48000370200200641002902ece9c480003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541063a00202005200336021c2005200236021820054104360214200520063602102005410436020c2005200741016a3602082005200a3602042005200936020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104412010b280808000000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410736021c200541f0fac48000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410c36021c200541dea3c58000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a41002903f099c580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541003a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000bab0301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c20064207370224200641b0f2c480003602202006410636021c200641e4f2c48000360218200641ea81808000360210200642b891b68c98adebcf61370308200642e7b0a091f3ed9c85c50037030041002d00fca3c680001a20042802042107200428020821080240411841002802c8a3c68000118180808000002209450d00200941106a41002902d8eac48000370200200941086a41002902d0eac48000370200200941002902c8eac480003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641003a00202006200336021c2006200236021820064103360214200620093602102006410336020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104411810b280808000000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c2006420c370224200641a8a7c580003602202006410836021c200641a0a7c58000360218200641f383808000360210200642a5c7d7fac9c29fd91637030820064289c487f88aeba5a3be7f37030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d0020094100290398a7c580003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641003a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c20054207370224200541b0f2c480003602202005410336021c200541c0a8c58000360218200541ea81808000360210200542b891b68c98adebcf61370308200542e7b0a091f3ed9c85c500370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c20054207370224200541b0f2c480003602202005410536021c200541cc92c58000360218200541ea81808000360210200542b891b68c98adebcf61370308200542e7b0a091f3ed9c85c50037030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a4100290388a9c580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541033a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c2006420c37022420064194fbc480003602202006410d36021c20064187fbc48000360218200641ed83808000360210200642e7dca5b4fdd9bda7a17f370308200642accee9bcd783d4ea3937030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d00200941002903c0fac480003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641003a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410336021c200541f392c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410636021c200541c1fbc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a41002903e8a1c580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541083a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000bbf0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c2006421f370224200641d6b2c5800036022020064100360218200641f282808000360210200642f2a19699e4e1bdb1ed0037030820064288d7a582bd91bff5867f37030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641033a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410336021c200541f392c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410436021c20054193fcc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a410029038099c580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541033a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000bbf0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c200642e200370224200641fda5c5800036022020064100360218200641f4838080003602102006429dfad7afa1e4f28416370308200642ffc593f1fa9c9cbbed0037030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641003a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410336021c200541f392c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410636021c200541c1fbc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a41002903b899c580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a2205410d3a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c2006420a370224200641d192c580003602202006410636021c200641c1fbc48000360218200641ef8080800036021020064293888c8f89fdc6ec9e7f370308200642a5e9e3ab9e929adc2c37030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d00200941002903c89bc580003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641103a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410336021c200541f392c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410636021c200541c1fbc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a41002903a0a3c580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a2205410c3a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000bc00201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206410036023020064280808080c000370328200641003602202006410036021820064193828080003602102006429ccab49c93e2e495cf00370308200642d1c5efcdcd82cde1ff0037030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bbf0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206410036023020064280808080c00037032820064100360220200641003602182006418c83808000360210200642b889cc8cd692ff80fa0037030820064288f7e1d594faa5a12037030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000be70403047f017e027f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005422437022420054189e1c480003602202005411236021c200541f7e0c48000360218200541f583808000360210200542ade681e7ba94a3bd8d7f3703082005428ae790e9b7c48cad987f370300200441086a41086a200641016a2207360200200420042902142208370308024020072008a7470d00200441086a200710a486808000200428021021070b200428020c200741386c6a2205420437022c20054210370224200541e7e0c480003602202005410f36021c200541d8e0c48000360218200541f683808000360210200542d5a5dfc0a4ecbbc59f7f37030820054299adc2c4dc87d5fcfe0037030020042802082109200428020c210a41002d00fca3c680001a0240412041002802c8a3c68000118180808000002206450d00200641186a4100290298dec48000370200200641106a4100290290dec48000370200200641086a4100290288dec4800037020020064100290280dec480003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541003a00202005200336021c2005200236021820054104360214200520063602102005410436020c2005200741016a3602082005200a3602042005200936020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104412010b280808000000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c200642e40037022420064199a5c5800036022020064100360218200641f783808000360210200642dfac899ed482c1d91e370308200642c0df82eab881b3cb5e37030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206410036023020064280808080c0003703282006410036022020064100360218200641948380800036021020064299f0ab899787d3ad3a370308200642d4b0f086cab3d2eb1437030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bad0503047f017e027f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005422437022420054189e1c480003602202005411236021c200541f7e0c48000360218200541f583808000360210200542ade681e7ba94a3bd8d7f3703082005428ae790e9b7c48cad987f370300200441086a41086a200641016a2207360200200420042902142208370308024020072008a7470d00200441086a200710a486808000200428021021070b200428020c200741386c6a2205420437022c20054210370224200541e7e0c480003602202005410f36021c200541d8e0c48000360218200541f683808000360210200542d5a5dfc0a4ecbbc59f7f37030820054299adc2c4dc87d5fcfe0037030020042802082109200428020c210a41002d00fca3c680001a024041c00041002802c8a3c68000118180808000002205450d00200541386a41002902f8ddc48000370200200541306a41002902f0ddc48000370200200541286a41002902e8ddc48000370200200541206a41002902e0ddc48000370200200541186a41002902d8ddc48000370200200541106a41002902d0ddc48000370200200541086a41002902c8ddc48000370200200541002902c0ddc480003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c2006200236021820064108360214200620053602102006410836020c2006200741016a3602082006200a3602042006200936020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b410441c00010b280808000000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410336021c200541f392c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410636021c200541c1fbc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a41002903889dc580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541113a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c2006421c3702242006419fb2c5800036022020064100360218200641ec8280800036021020064289a2e6a5b198dead63370308200642d5d1b5deb9b78d97cf0037030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641003a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bbc0301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c20064214370224200641b3e1c480003602202006410636021c200641ade1c480003602182006418482808000360210200642a8dcd6c1f2afcba52b370308200642c7facddcf584b3fea87f37030041002d00fca3c680001a20042802042107200428020821080240412041002802c8a3c68000118180808000002209450d00200941186a41002902d0e0c48000370200200941106a41002902c8e0c48000370200200941086a41002902c0e0c48000370200200941002902b8e0c480003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641023a00202006200336021c2006200236021820064104360214200620093602102006410436020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104412010b280808000000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410336021c200541f392c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410636021c200541c1fbc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a41002903909ec580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541093a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c20064207370224200641b0f2c480003602202006410636021c200641e4f2c48000360218200641ea81808000360210200642b891b68c98adebcf61370308200642e7b0a091f3ed9c85c50037030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d0020094100290390e7c480003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641073a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000bb20403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c2005420c370224200541f7fac480003602202005410336021c200541f392c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410636021c200541c1fbc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240410841002802c8a3c6800011818080800000220a450d00200a4100290388a2c580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541133a00202005200336021c20052002360218200541013602142005200a3602102005410136020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104410810b280808000000bbf0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c200642f200370224200641c0b0c5800036022020064100360218200641f182808000360210200642f8978ab68af4bdd3937f370308200642f681cbfbfc8791b01c37030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641023a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c200642eb00370224200641ecaec5800036022020064100360218200641ee82808000360210200642829cf890def98fed23370308200642daf597b8b3adb2fc4437030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641003a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000b870503047f017e027f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c20054214370224200541df92c580003602202005410436021c200541db92c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2207360200200420042902142208370308024020072008a7470d00200441086a200710a486808000200428021021070b200428020c200741386c6a2205420437022c2005420a370224200541d192c580003602202005410536021c200541cc92c58000360218200541bc82808000360210200542899ac8f29d8ce69ac300370308200542e78dcee4d0becc975737030020042802082109200428020c210a41002d00fca3c680001a0240413041002802c8a3c68000118180808000002206450d00200641286a41002902f089c58000370200200641206a41002902e889c58000370200200641186a41002902e089c58000370200200641106a41002902d889c58000370200200641086a41002902d089c58000370200200641002902c889c580003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541033a00202005200336021c2005200236021820054106360214200520063602102005410636020c2005200741016a3602082005200a3602042005200936020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104413010b280808000000b890301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c2006420c370224200641f7fac480003602202006410736021c200641f0fac48000360218200641f081808000360210200642a4d2928ecac0faa432370308200642c4ccab9c81ebb4b8db0037030041002d00fca3c680001a20042802042107200428020821080240410841002802c8a3c68000118180808000002209450d0020094100290390fac480003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641043a00202006200336021c2006200236021820064101360214200620093602102006410136020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104410810b280808000000bbf0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206410036023020064280808080c0003703282006410036022020064100360218200641f081808000360210200642a4d2928ecac0faa432370308200642c4ccab9c81ebb4b8db0037030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000b810401067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c200642113702242006419c93c580003602202006410336021c200641f392c58000360218200641a9828080003602102006429595f4d085b899a16037030820064287bba5b8adf5dafa5b37030041002d00fca3c680001a2004280204210720042802082108024041c00041002802c8a3c68000118180808000002206450d00200641386a41002902e88dc58000370200200641306a41002902e08dc58000370200200641286a41002902d88dc58000370200200641206a41002902d08dc58000370200200641186a41002902c88dc58000370200200641106a41002902c08dc58000370200200641086a41002902b88dc58000370200200641002902b08dc580003702000240200128020822092001280200470d002001200910a086808000200128020821090b2001280204200941246c6a220941063a00202009200336021c2009200236021820094108360214200920063602102009410836020c2009200541016a360208200920083602042009200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b410441c00010b280808000000bc00201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206410036023020064280808080c00037032820064100360220200641003602182006419482808000360210200642c0969aec91e181fcf200370308200642d7a7fbff94a5afcaf80037030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bd40403037f017e037f23808080800041206b2204248080808000200441146a41086a22054100360200200442808080808001370214200441146a410010a48680800020042802182005280200220641386c6a2205420437022c20054214370224200541df92c580003602202005410336021c200541f392c58000360218200541f081808000360210200542a4d2928ecac0faa432370308200542c4ccab9c81ebb4b8db00370300200441086a41086a200641016a2206360200200420042902142207370308024020062007a7470d00200441086a200610a486808000200428021021060b200428020c200641386c6a2205420437022c2005420a370224200541d192c580003602202005410636021c200541c1fbc48000360218200541ef8080800036021020054293888c8f89fdc6ec9e7f370308200542a5e9e3ab9e929adc2c37030020042802082108200428020c210941002d00fca3c680001a0240411841002802c8a3c6800011818080800000220a450d00200a41106a410029029cfdc48000370200200a41086a4100290294fdc48000370200200a410029028cfdc480003702000240200128020822052001280200470d002001200510a086808000200128020821050b2001280204200541246c6a220541053a00202005200336021c20052002360218200541033602142005200a3602102005410336020c2005200641016a360208200520093602042005200836020020002001290200370200200141086a2205200528020041016a2205360200200041086a2005360200200441206a2480808080000f0b4104411810b280808000000bbe0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206410036023020064280808080c0003703282006410036022020064100360218200641f883808000360210200642dfda92949ba784d566370308200642dc85e1fdf99cd0816b37030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000beb0603047f017e027f23808080800041206b2204248080808000200441106a41086a22054100360200200442808080808001370210200441106a410010a48680800020042802142005280200220641386c6a2207420437022c2007420c370224200741f7fac480003602202007410436021c200741daa3c58000360218200741f081808000360210200742a4d2928ecac0faa432370308200742c4ccab9c81ebb4b8db00370300200441086a200641016a2206360200200420042902102208370300024020062008a7470d002004200610a486808000200428020821060b2004280204200641386c6a2207420437022c2007420c370224200741f7fac480003602202007410236021c200741d8a3c58000360218200741f081808000360210200742a4d2928ecac0faa432370308200742c4ccab9c81ebb4b8db003703002005200641016a2206360200200420042903002208370310024020062008a7470d00200441106a200610a486808000200428021821060b2004280214200641386c6a2207420437022c2007420a370224200741d192c580003602202007410636021c200741c1fbc48000360218200741ef8080800036021020074293888c8f89fdc6ec9e7f370308200742a5e9e3ab9e929adc2c370300200441086a200641016a2206360200200420042903102208370300024020062008a7470d002004200610a486808000200428020821060b2004280204200641386c6a2207420437022c20074206370224200741fca3c580003602202007411236021c200741eaa3c58000360218200741f983808000360210200742dcb08885f1e3a29706370308200742db8cf7c6bbd3898c8c7f370300200428020021092004280204210a41002d00fca3c680001a0240411041002802c8a3c68000118180808000002205450d00200541086a41002902e49cc58000370200200541002902dc9cc580003702000240200128020822072001280200470d002001200710a086808000200128020821070b2001280204200741246c6a220741063a00202007200336021c2007200236021820074102360214200720053602102007410236020c2007200641016a3602082007200a3602042007200936020020002001290200370200200141086a2207200728020041016a2207360200200041086a2007360200200441206a2480808080000f0b4104411010b280808000000bbf0201057f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c2006421b370224200641bbb2c5800036022020064100360218200641ef82808000360210200642bba2f9a9eaf2f897ff0037030820064288bfcff6aea5a9cbf90037030020042802042107200428020821080240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641013a00202006200336021c200620023602182006410036021420064280808080c00037020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000bbc0301067f23808080800041106b22042480808080002004410036020c200442808080808001370204200441046a410010a4868080002004280208200428020c220541386c6a2206420437022c20064207370224200641e3e4c480003602202006410936021c200641dae4c48000360218200641f581808000360210200642ab8bffbed784ffa5937f370308200642c194a6a793ccc3a85737030041002d00fca3c680001a20042802042107200428020821080240412041002802c8a3c68000118180808000002209450d00200941186a41002902c8eec48000370200200941106a41002902c0eec48000370200200941086a41002902b8eec48000370200200941002902b0eec480003702000240200128020822062001280200470d002001200610a086808000200128020821060b2001280204200641246c6a220641093a00202006200336021c2006200236021820064104360214200620093602102006410436020c2006200541016a360208200620083602042006200736020020002001290200370200200141086a2206200628020041016a2206360200200041086a2006360200200441106a2480808080000f0b4104412010b280808000000b940201047f23808080800041106b220224808080800002400240024002402001108b8280800022030d00410121040c010b2003417f4c0d0141002d00fca3c680001a200341002802c8a3c68000118180808000002204450d020b200241086a220541003602002002200436020420022003360200200220014188026a36020c2002410c6a200210c18a808000200220014190026a36020c2002410c6a200210c18a808000200220014198026a36020c2002410c6a200210c18a8080002002200141a0026a36020c2002410c6a200210c18a8080002001200210f28c808000200041086a200528020036020020002002290200370200200241106a2480808080000f0b10ae80808000000b4101200310b280808000000bae0101017f23808080800041306b2202248080808000200028020021002002410c6a4202370200200241186a410c6a41d08180800036020020022000280200220036022820024103360204200241f0bfc08000360200200241d18180800036021c200220006836022c200141186a28020021002002200241186a36020820022002412c6a3602202002200241286a36021820012802142000200210d9808080002101200241306a24808080800020010bb30f03117f017e067f23808080800041206b2203248080808000024002400240024020014115490d0041002d00fca3c680001a0240200141037441f0ffffff077141002802c8a3c68000118180808000002204450d0041002d00fca3c680001a41800141002802c8a3c68000118180808000002205450d04200041706a2106200041246a210741002108410021094110210a0240034020002008220b410474220c6a210d0240024002402001200b6b220e4102490d000240200d41106a280200220f200d280200200d41146a2802002210200d41046a280200221120102011491b10888e8080002212201020116b20121b4100480d0041022112200e4102460d022007200c6a21114102211203402011417c6a2802002213200f2011280200220c2010200c2010491b10888e808000220f200c20106b200f1b4100480d03201141106a2111200c21102013210f200e201241016a2212460d020c000b0b410221120240200e4102460d002007200c6a21114102211203402011417c6a2802002213200f2011280200220c2010200c2010491b10888e808000220f200c20106b200f1b417f4a0d01201141106a2111200c21102013210f200e201241016a2212470d000b200e21120b024002402012200b6a22082012490d00200820014b0d0120124102490d042012410176210c200620084104746a2110200d21110340201129020021142011201029020037020020102014370200201141086a220f2902002114200f201041086a220e290200370200200e2014370200201041706a2110201141106a2111200c417f6a220c0d000c050b0b200b200841c0b4c58000109681808000000b2008200141c0b4c58000109581808000000b200e21120b2012200b6a21080b024002402008200b490d00200820014d0d010b41b0b5c58000412c41dcb5c5800010f880808000000b024002400240200820014f0d002012410a490d010b2008200b6b21100c010b200b410a6a2210200120102001491b2208200b490d02200d2008200b6b221020124101201241014b1b10d78b8080000b024002402009200a470d0041002d00fca3c680001a200941047441002802c8a3c68000118180808000002211450d012009410174210a20112005200941037410848e8080002111200541002802c0a3c6800011808080800000201121050b200520094103746a2211200b36020420112010360200200941016a22152109024020154102490d0003400240024002400240200520152213417f6a22154103746a2209280200221020092802046a2001460d00201341037420056a220c41706a280200221220104d0d0041022109201341024d0d0520052013417d6a22164103746a2802002211201220106a4d0d0141032109201341034d0d05200c41606a280200201120126a4d0d01201321090c050b20134103490d0120052013417d6a22164103746a28020021110b20112010490d010b2013417e6a21160b024002400240024002400240201320164d0d002013201641016a22104d0d01200520104103746a2217280204201728020022186a2211200520164103746a2219280204221a490d02201120014b0d032000201a4104746a22092019280200220d41047422126a21102011410474210c02402011201a6b220f200d6b2211200d4f0d00200420102011410474221210848e808000220e20126a2112200d4101480d0520114101480d052006200c6a2111034020112012201241706a220c280200201041706a220f280200200c41046a280200220c200f41046a280200220f200c200f491b10888e808000220b200c200f6b200b1b220c411f75220f417f734104746a22122010200f4104746a2210200c417f4a1b220c290200370200201141086a200c41086a290200370200201020094d0d06201141706a21112012200e4d0d060c000b0b20042009201210848e808000221120126a21120240200d41014e0d00201121110c060b0240200f200d4a0d00201121110c060b2000200c6a210e20112111034020092011201020102802002011280200201041046a280200220c201141046a280200220f200c200f491b10888e808000220b200c200f6b200b1b220b417f4a220c1b220f290200370200200941086a200f41086a290200370200200941106a21092011200c4104746a221120124f0d062010200b411b764110716a2210200e490d000c060b0b200341146a42003702002003410136020c200341c4b3c58000360208200341ccb3c58000360210200341086a41d0b4c5800010f680808000000b200341146a42003702002003410136020c200341c4b3c58000360208200341ccb3c58000360210200341086a41e0b4c5800010f680808000000b201a201141f0b4c58000109681808000000b2011200141f0b4c58000109581808000000b20102109200e21110b20092011201220116b10848e8080001a2017201a36020420172018200d6a3602002019201941086a20132016417f736a41037410fe8d8080001a41012109201541014b0d000b0b200820014f0d050c010b0b41a0b5c5800010a081808000000b200b200841ecb5c58000109681808000000b4180b5c5800010a081808000000b200141014d0d0120002001410110d78b8080000c010b200541002802c0a3c6800011808080800000200441002802c0a3c68000118080808000000b200341206a2480808080000f0b4190b5c5800010a081808000000bd60203077f017e017f02402002417f6a20014f0d000240200220014f0d00200241047420006a41606a210303400240200020024104746a22042802002205200441706a2206280200200441046a2802002207200641046a280200220820072008491b10888e8080002209200720086b20091b417f4a0d0020042006290200370200200441086a2204290200210a2004200641086a290200370200024020024101460d004101210b200321040340200441106a2106200520042802002007200441046a280200220820072008491b10888e8080002209200720086b20091b417f4a0d0120062004290200370200200641086a200441086a290200370200200441706a21042002200b41016a220b470d000b200021060b2006200a37020820062007360204200620053602000b200341106a2103200241016a22022001470d000b0b0f0b41fcb5c58000412e41acb6c5800010f880808000000bbe0f03117f017e067f23808080800041206b2203248080808000024002400240024020014115490d0041002d00fca3c680001a02402001410176410c6c41002802c8a3c68000118180808000002204450d0041002d00fca3c680001a41800141002802c8a3c68000118180808000002205450d04200041746a2106200041206a210741002108410021094110210a0240034020002008220b410c6c220c6a210d0240024002402001200b6b220e4102490d000240200d41106a280200220f200d41046a280200200d41146a2802002210200d41086a280200221120102011491b10888e8080002212201020116b20121b4100480d0041022112200e4102460d022007200c6a21114102211203402011417c6a2802002213200f2011280200220c2010200c2010491b10888e808000220f200c20106b200f1b4100480d032011410c6a2111200c21102013210f200e201241016a2212460d020c000b0b410221120240200e4102460d002007200c6a21114102211203402011417c6a2802002213200f2011280200220c2010200c2010491b10888e808000220f200c20106b200f1b417f4a0d012011410c6a2111200c21102013210f200e201241016a2212470d000b200e21120b024002402012200b6a22082012490d00200820014b0d0120124102490d042012410176210c20062008410c6c6a2110200d211103402011280200210f201120102802003602002010200f360200201141046a220f2902002114200f201041046a220e290200370200200e2014370200201041746a21102011410c6a2111200c417f6a220c0d000c050b0b200b200841c0b4c58000109681808000000b2008200141c0b4c58000109581808000000b200e21120b2012200b6a21080b024002402008200b490d00200820014d0d010b41b0b5c58000412c41dcb5c5800010f880808000000b024002400240200820014f0d002012410a490d010b2008200b6b21100c010b200b410a6a2210200120102001491b2208200b490d02200d2008200b6b221020124101201241014b1b10d98b8080000b024002402009200a470d0041002d00fca3c680001a200941047441002802c8a3c68000118180808000002211450d012009410174210a20112005200941037410848e8080002111200541002802c0a3c6800011808080800000201121050b200520094103746a2211200b36020420112010360200200941016a22152109024020154102490d0003400240024002400240200520152213417f6a22154103746a2209280200221020092802046a2001460d00201341037420056a220c41706a280200221220104d0d0041022109201341024d0d0520052013417d6a22164103746a2802002211201220106a4d0d0141032109201341034d0d05200c41606a280200201120126a4d0d01201321090c050b20134103490d0120052013417d6a22164103746a28020021110b20112010490d010b2013417e6a21160b024002400240024002400240201320164d0d002013201641016a22104d0d01200520104103746a2217280204201728020022186a2211200520164103746a2219280204221a490d02201120014b0d032000201a410c6c6a22092019280200220d410c6c22126a21102011410c6c210c02402011201a6b220f200d6b2211200d4f0d00200420102011410c6c221210848e808000220e20126a2112200d4101480d0520114101480d052006200c6a2111034020112012201241746a220c41046a280200201041746a220f41046a280200200c41086a280200220c200f41086a280200220f200c200f491b10888e808000220b200c200f6b200b1b220c411f75220f417f73410c6c6a22122010200f410c6c6a2210200c417f4a1b220c290200370200201141086a200c41086a280200360200201020094d0d06201141746a21112012200e4d0d060c000b0b20042009201210848e808000221120126a21120240200d41014e0d00201121110c060b0240200f200d4a0d00201121110c060b2000200c6a210e201121110340200920112010201041046a280200201141046a280200201041086a280200220c201141086a280200220f200c200f491b10888e808000220b200c200f6b200b1b220b417f4a220c1b220f290200370200200941086a200f41086a2802003602002009410c6a21092011200c410c6c6a221120124f0d062010200b411f76410c6c6a2210200e490d000c060b0b200341146a42003702002003410136020c200341c4b3c58000360208200341ccb3c58000360210200341086a41d0b4c5800010f680808000000b200341146a42003702002003410136020c200341c4b3c58000360208200341ccb3c58000360210200341086a41e0b4c5800010f680808000000b201a201141f0b4c58000109681808000000b2011200141f0b4c58000109581808000000b20102109200e21110b20092011201220116b10848e8080001a2017201a36020420172018200d6a3602002019201941086a20132016417f736a41037410fe8d8080001a41012109201541014b0d000b0b200820014f0d050c010b0b41a0b5c5800010a081808000000b200b200841ecb5c58000109681808000000b4180b5c5800010a081808000000b200141014d0d0120002001410110d98b8080000c010b200541002802c0a3c6800011808080800000200441002802c0a3c68000118080808000000b200341206a2480808080000f0b4190b5c5800010a081808000000bd602010a7f02402002417f6a20014f0d000240200220014f0d002002410c6c20006a41686a21030340024020002002410c6c6a220441046a2802002205200441746a220641046a280200200441086a22072802002208200641086a2209280200220a2008200a491b10888e808000220b2008200a6b200b1b417f4a0d002004280200210c2004200629020037020020072009280200360200024020024101460d00410121072003210403402004410c6a21062005200441046a2802002008200441086a2209280200220a2008200a491b10888e808000220b2008200a6b200b1b417f4a0d0120062004290200370200200641086a2009280200360200200441746a21042002200741016a2207470d000b200021060b20062008360208200620053602042006200c3602000b2003410c6a2103200241016a22022001470d000b0b0f0b41fcb5c58000412e41acb6c5800010f880808000000bb70e03137f017e047f23808080800041206b2203248080808000024002400240024020014115490d0041002d00fca3c680001a0240200141017641146c41002802c8a3c68000118180808000002204450d0041002d00fca3c680001a41800141002802c8a3c68000118180808000002205450d042000416c6a2106200041146a210741002108410021094110210a0240034020002008220b41146c220c6a210d0240024002402001200b6b220e4102490d000240200d41146a200d410810888e8080004100480d004102210f200e4102460d022007200c6a21104102210f0340201041146a22112010410810888e8080004100480d0320112110200e200f41016a220f460d020c000b0b4102210f0240200e4102460d002007200c6a21104102210f0340201041146a22112010410810888e808000417f4a0d0120112110200e200f41016a220f470d000b200e210f0b02400240200f200b6a2208200f490d00200820014b0d01200f4102490d04200f4101762112200c200f41146c6a2113200621112000211403402014200c6a221041086a221529020021162015201120136a220e41086a221729020037020020172016370200201029020021162010200e290200370200200e2016370200200e41106a220e2802002115200e201041106a2210280200360200201020153602002011416c6a2111201441146a21142012417f6a22120d000c050b0b200b200841c0b4c58000109681808000000b2008200141c0b4c58000109581808000000b200e210f0b200f200b6a21080b024002402008200b490d00200820014d0d010b41b0b5c58000412c41dcb5c5800010f880808000000b024002400240200820014f0d00200f410a490d010b2008200b6b21100c010b200b410a6a2210200120102001491b2208200b490d02200d2008200b6b2210200f4101200f41014b1b10db8b8080000b024002402009200a470d0041002d00fca3c680001a200941047441002802c8a3c6800011818080800000220e450d012009410174210a200e2005200941037410848e808000210e200541002802c0a3c6800011808080800000200e21050b200520094103746a220e200b360204200e2010360200200941016a22182109024020184102490d0003400240024002400240200520182217417f6a22184103746a2209280200221020092802046a2001460d00201741037420056a221141706a280200220f20104d0d0041022109201741024d0d0520052017417d6a220c4103746a280200220e200f20106a4d0d0141032109201741034d0d05201141606a280200200e200f6a4d0d01201721090c050b20174103490d0120052017417d6a220c4103746a280200210e0b200e2010490d010b2017417e6a210c0b0240024002400240024002402017200c4d0d002017200c41016a22104d0d01200520104103746a2213280204201328020022196a220e2005200c4103746a221a280204220d490d02200e20014b0d032000200d41146c6a2209201a280200220b41146c220f6a2110200e41146c21140240200e200d6b2212200b6b2211200b4f0d0020042010201141146c220e10848e8080002212200e6a210e200b4101480d0520114101480d05200620146a210f0340200f200e200e416c6a2010416c6a410810888e8080002211411f752214417f7341146c6a220e2010201441146c6a22102011417f4a1b2211290200370200200f41106a201141106a280200360200200f41086a201141086a290200370200201020094d0d06200f416c6a210f200e20124d0d060c000b0b20042009200f10848e8080002211200f6a210e0240200b41014e0d002011210f0c060b02402012200b4a0d002011210f0c060b200020146a21152011210f03402009200f20102010200f410810888e8080002212417f4a22141b2211290200370200200941106a201141106a280200360200200941086a201141086a290200370200200941146a2109200f201441146c6a220f200e4f0d0620102012411f7641146c6a22102015490d000c060b0b200341146a42003702002003410136020c200341c4b3c58000360208200341ccb3c58000360210200341086a41d0b4c5800010f680808000000b200341146a42003702002003410136020c200341c4b3c58000360208200341ccb3c58000360210200341086a41e0b4c5800010f680808000000b200d200e41f0b4c58000109681808000000b200e200141f0b4c58000109581808000000b201021092012210f0b2009200f200e200f6b10848e8080001a2013200d36020420132019200b6a360200201a201a41086a2017200c417f736a41037410fe8d8080001a41012109201841014b0d000b0b200820014f0d050c010b0b41a0b5c5800010a081808000000b200b200841ecb5c58000109681808000000b4180b5c5800010a081808000000b200141014d0d0120002001410110db8b8080000c010b200541002802c0a3c6800011808080800000200441002802c0a3c68000118080808000000b200341206a2480808080000f0b4190b5c5800010a081808000000b8a0303047f017e037f23808080800041206b220324808080800002402002417f6a20014f0d000240200220014f0d00200241146c20006a41586a2104034002402000200241146c6a22052005416c6a2206410810888e808000417f4a0d002005290200210720052006290200370200200341086a41106a2208200541106a2209280200360200200341086a41086a220a200541086a22052902003703002005200641086a2902003702002009200641106a28020036020020032007370308024020024101460d0041012109200421050340200541146a2106200341086a2005410810888e808000417f4a0d0120062005290200370200200641106a200541106a280200360200200641086a200541086a2902003702002005416c6a21052002200941016a2209470d000b200021060b20062003290308370200200641106a2008280200360200200641086a200a2903003702000b200441146a2104200241016a22022001470d000b0b200341206a2480808080000f0b41fcb5c58000412e41acb6c5800010f880808000000baf1501197f23808080800041206b2203248080808000024002400240024020014115490d0041002d00fca3c680001a0240200141047441e0ffffff077141002802c8a3c68000118180808000002204450d0041002d00fca3c680001a41800141002802c8a3c68000118180808000002205450d04200041606a2106200041206a210741002108410021094110210a0240034020002008220b410574220c6a210d0240024002402001200b6b220e4102490d000240200d41206a200d412010888e8080004100480d004102210f200e4102460d022007200c6a21104102210f0340201041206a22112010412010888e8080004100480d0320112110200e200f41016a220f460d020c000b0b4102210f0240200e4102460d002007200c6a21104102210f0340201041206a22112010412010888e808000417f4a0d0120112110200e200f41016a220f470d000b200e210f0b02400240200f200b6a2208200f490d00200820014b0d01200f4102490d04200f4101762112200c200f4105746a2113200621112000211403402014200c6a221028000021152010201120136a220e280000360000200e2015360000200e41056a2d00002115200e41046a22162d000021172016201041046a22182f00003b0000201041076a2d00002116201041066a22192d0000211a2019200e41066a221b2f00003b0000201820173a0000201041056a20153a0000201b201a3a0000200e41076a20163a0000201041086a22152d000021162015200e41086a22172d00003a0000201720163a0000201041096a22152d000021162015200e41096a22172d00003a0000201720163a00002010410a6a22152d000021162015200e410a6a22172d00003a0000201720163a00002010410b6a22152d000021162015200e410b6a22172d00003a0000201720163a00002010410c6a22152d000021162015200e410c6a22172d00003a0000201720163a00002010410d6a22152d000021162015200e410d6a22172d00003a0000201720163a00002010410e6a22152d000021162015200e410e6a22172d00003a0000201720163a00002010410f6a22152d000021162015200e410f6a22172d00003a0000201720163a0000201041106a22152d000021162015200e41106a22172d00003a0000201720163a0000201041116a22152d000021162015200e41116a22172d00003a0000201720163a0000201041126a22152d000021162015200e41126a22172d00003a0000201720163a0000201041136a22152d000021162015200e41136a22172d00003a0000201720163a0000201041146a22152d000021162015200e41146a22172d00003a0000201720163a0000201041156a22152d000021162015200e41156a22172d00003a0000201720163a0000201041166a22152d000021162015200e41166a22172d00003a0000201720163a0000201041176a22152d000021162015200e41176a22172d00003a0000201720163a0000201041186a22152d000021162015200e41186a22172d00003a0000201720163a0000201041196a22152d000021162015200e41196a22172d00003a0000201720163a00002010411a6a22152d000021162015200e411a6a22172d00003a0000201720163a00002010411b6a22152d000021162015200e411b6a22172d00003a0000201720163a00002010411c6a22152d000021162015200e411c6a22172d00003a0000201720163a00002010411d6a22152d000021162015200e411d6a22172d00003a0000201720163a00002010411e6a22152d000021162015200e411e6a22172d00003a0000201720163a00002010411f6a22102d000021152010200e411f6a220e2d00003a0000200e20153a0000201141606a2111201441206a21142012417f6a22120d000c050b0b200b200841c0b4c58000109681808000000b2008200141c0b4c58000109581808000000b200e210f0b200f200b6a21080b024002402008200b490d00200820014d0d010b41b0b5c58000412c41dcb5c5800010f880808000000b024002400240200820014f0d00200f410a490d010b2008200b6b21100c010b200b410a6a2210200120102001491b2208200b490d02200d2008200b6b2210200f4101200f41014b1b10dd8b8080000b024002402009200a470d0041002d00fca3c680001a200941047441002802c8a3c6800011818080800000220e450d012009410174210a200e2005200941037410848e808000210e200541002802c0a3c6800011808080800000200e21050b200520094103746a220e200b360204200e2010360200200941016a220b21090240200b4102490d00034002400240024002402005200b2216417f6a220b4103746a220e2802002210200e2802046a2001460d00201641037420056a221141706a280200220f20104d0d0041022109201641024d0d0520052016417d6a22184103746a280200220e200f20106a4d0d0141032109201641034d0d05201141606a280200200e200f6a4d0d01201621090c050b20164103490d0120052016417d6a22184103746a280200210e0b200e2010490d010b2016417e6a21180b024002400240024002400240201620184d0d002016201841016a22104d0d01200520104103746a22192802042019280200220c6a2209200520184103746a221b280204221a490d02200920014b0d032000201a4105746a220e201b2802002217410574220f6a21102009410574211102402009201a6b221420176b220920174f0d00200420102009410574220f10848e8080002212200f6a210f20174101480d0520094101480d05200620116a210903402009200f200f41606a201041606a412010888e8080002211411f752214417f734105746a220f201020144105746a22102011417f4a1b2211290000370000200941186a201141186a290000370000200941106a201141106a290000370000200941086a201141086a2900003700002010200e4d0d06200941606a2109200f20124d0d060c000b0b2004200e200f10848e8080002209200f6a210f0240201741014e0d00200921090c060b0240201420174a0d00200921090c060b200020116a2115200921090340200e2009201020102009412010888e8080002212417f4a22141b2211290000370000200e41186a201141186a290000370000200e41106a201141106a290000370000200e41086a201141086a290000370000200e41206a210e200920144105746a2209200f4f0d0620102012411a764120716a22102015490d000c060b0b200341146a42003702002003410136020c200341c4b3c58000360208200341ccb3c58000360210200341086a41d0b4c5800010f680808000000b200341146a42003702002003410136020c200341c4b3c58000360208200341ccb3c58000360210200341086a41e0b4c5800010f680808000000b201a200941f0b4c58000109681808000000b2009200141f0b4c58000109581808000000b2010210e201221090b200e2009200f20096b10848e8080001a2019201a3602042019200c20176a360200201b201b41086a20162018417f736a41037410fe8d8080001a41012109200b41014b0d000b0b200820014f0d050c010b0b41a0b5c5800010a081808000000b200b200841ecb5c58000109681808000000b4180b5c5800010a081808000000b200141014d0d0120002001410110dd8b8080000c010b200541002802c0a3c6800011808080800000200441002802c0a3c68000118080808000000b200341206a2480808080000f0b4190b5c5800010a081808000000bbf0303047f017e057f23808080800041206b220324808080800002402002417f6a20014f0d000240200220014f0d00200241057420006a41406a210403400240200020024105746a2205200541606a2206412010888e808000417f4a0d002005290000210720052006290000370000200341186a2208200541186a2209290000370300200341106a220a200541106a220b290000370300200341086a220c200541086a22052900003703002005200641086a290000370000200b200641106a2900003700002009200641186a29000037000020032007370300024020024101460d0041012109200421050340200541206a210620032005412010888e808000417f4a0d0120062005290000370000200641186a200541186a290000370000200641106a200541106a290000370000200641086a200541086a290000370000200541606a21052002200941016a2209470d000b200021060b20062003290300370000200641186a2008290300370000200641106a200a290300370000200641086a200c2903003700000b200441206a2104200241016a22022001470d000b0b200341206a2480808080000f0b41fcb5c58000412e41acb6c5800010f880808000000b920402027f017e23808080800041d0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c68000024020010d00200241386a410029028cb8c58000370300200241306a4100290284b8c58000370300200241286a41002902fcb7c58000370300200241206a41002902f4b7c58000370300200241186a41002902ecb7c58000370300200241106a41002902e4b7c58000370300200241002902dcb7c58000370308200241c0006a200241086a108e8d8080002002350248210420022802442103024020022802082201418080808078460d002001450d00200228020c41002802c0a3c68000118080808000000b024020022802142201418080808078460d002001450d00200241186a28020041002802c0a3c68000118080808000000b024020022802202201418080808078460d002001450d00200241246a28020041002802c0a3c68000118080808000000b200241d0006a24808080800020044220862003ad840f0b200241146a42003702002002410136020c200241f0b8c580003602082002200241cc006a360210200241086a41dcb9c5800010f680808000000bd50401027f23808080800041d0026b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2100024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680002002200136020420022000360200200241c8016a418002200210988d808000024020022802c002418080808078460d00200241086a200241c8016a41880110848e8080001a02404100280284a4c680004105470d00200241a8016a410136020020024194016a410c6a4101360200200241fa838080003602b001200241013602980120024198a1c48000360294012002200241086a3602ac01200241033a00e401200241043602e001200242203702d801200241023602d001200241023602c8012002200241c8016a3602a4012002200241ac016a36029c01200241b4016a410c6a4116360200200241a0a1c480003602c401200241fe8bc480003602bc01200241163602b801200241e88bc480003602b40120024194016a4105200241b4016a4100200210a1828080000b200241c8016a200241086a41880110848e8080001a200241c8016a10fa8c808000200241d0026a24808080800042010f0b200241146a42013702002002410136020c20024198bac58000360208200241fb8380800036029801200220024194016a3602102002200241b4016a36029401200241086a41dcb9c5800010f680808000000bd50801057f23808080800041a0026b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2100024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c6800020022001360204200220003602002002428080808080203702f001200220023602ec01200241f0006a200241ec016a10dc8c8080000240024020022802d8012200418080808078460d00200241086a200241f0006a41e80010848e8080001a20022802e001210320022802dc0121012002280204450d0102402003450d00200121044100210503402001200541146c6a21060240024002400240024020042d00000e0400010102040b200441086a21060c020b200641086a21060c010b200641046a21060b2006280200450d00200628020441002802c0a3c68000118080808000000b200541016a2105200441146a21042003417f6a22030d000b0b2000450d00200141002802c0a3c68000118080808000000b200241fc006a420137020020024101360274200241d0bac58000360270200241fb8380800036020c2002200241086a3602782002200241ec016a360208200241f0006a41dcb9c5800010f680808000000b20022802e4012104200241f0006a200241086a41e80010848e8080001a200220043602e401200220033602e001200220013602dc01200220003602d8014100280284a4c6800021012002200241f0006a3602e801200241f0006a2103024020014105470d0020024180026a4101360200200241ec016a410c6a4101360200200241fc8380800036028802200241013602f001200241c4a1c480003602ec012002200241e8016a36028402200241033a0024200241043602202002422037021820024102360210200241023602082002200241086a3602fc01200220024184026a3602f4012002418c026a410c6a4116360200200241cca1c4800036029c02200241fe8bc48000360294022002411636029002200241e88bc4800036028c02200241ec016a41052002418c026a4100200310a18280800020022802e80121030b200310fb8c8080002106024020022802e0012204450d0020022802dc01220521034100210103402005200141146c6a21000240024002400240024020032d00000e0400010102040b200341086a21000c020b200041086a21000c010b200041046a21000b2000280200450d00200028020441002802c0a3c68000118080808000000b200141016a2101200341146a21032004417f6a22040d000b0b024020022802d801450d0020022802dc0141002802c0a3c68000118080808000000b41002d00fca3c680001a0240410141002802c8a3c68000118180808000002203450d00200320063a0000200241a0026a2480808080002003ad428080808010840f0b4101410110b280808000000bf60202027f017e23808080800041f0026b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c68000024020010d00200241f0006a10e489808000200241106a200241f0006a10cf84808000200241868080807836020c200241edcad18b0636026c20022002410c6a10c181808000200241f0006a20022802042201200228020810f183808000200235027821042002280274210302402002280200450d00200141002802c0a3c68000118080808000000b200241f0026a24808080800020044220862003ad840f0b200241fc006a420037020020024101360274200241b4bbc5800036027020022002410c6a360278200241f0006a41dcb9c5800010f680808000000bcb0302027f017e23808080800041f0026b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c68000024020014104490d0020014104470d00200041ccb3c5800020011b2800002101200241f0006a10e4898080002002410c6a200241f0006a200110ce8480800002400240200228020c418780808078470d0020024180808080783602000c010b200241f0006a2002410c6a41e40010848e8080001a2002200241f0006a10c1818080000b200241f0006a200210b48c8080002002350278210420022802742101024020022802002203418080808078460d002003450d00200228020441002802c0a3c68000118080808000000b200241f0026a24808080800020044220862001ad840f0b200241fc006a420137020020024101360274200241ecbbc58000360270200241fb8380800036021020022002410c6a3602782002200236020c200241f0006a41dcb9c5800010f680808000000bcb0202027f017e23808080800041206b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680000240024020010d0041002d00fca3c680001a410841002802c8a3c68000118180808000002203450d012003428e808080f001370200200241046a2003410210d88580800020022902082104200341002802c0a3c6800011808080800000200241206a24808080800020040f0b200241106a420037020020024101360208200241d8bcc5800036020420022002411c6a36020c200241046a41dcb9c5800010f680808000000b4104410810b280808000000ba90703027f017e027f23808080800041b0046b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2100024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680002002200136020c20022000360208200241106a418002200241086a10dc8780800002402002280210450d0020022d00f8012103200241a0026a200241106a41e80110848e8080001a20024188046a41186a20024191026a29000037030020024198046a20024189026a29000037030020024190046a20024181026a290000370300200220022900f90137038804200241106a2003200241a0026a20024188046a10e589808000200241106a10b2888080002104024020022802202203418080808078460d000240200241106a41186a2802002200450d00200228022421052000410171210641002101024020004101460d002000417e7121002005210341002101034002402003280200450d00200341046a28020041002802c0a3c68000118080808000000b02402003410c6a280200450d00200341106a28020041002802c0a3c68000118080808000000b200341186a21032000200141026a2201470d000b0b02402006450d0020052001410c6c6a2203280200450d00200328020441002802c0a3c68000118080808000000b200228022021030b02402003450d00200228022441002802c0a3c68000118080808000000b0240200241346a2802002203450d00200241306a28020021052003410171210641002101024020034101460d002003417e7121002005210341002101034002402003280200450d00200341046a28020041002802c0a3c68000118080808000000b02402003410c6a280200450d00200341106a28020041002802c0a3c68000118080808000000b200341186a21032000200141026a2201470d000b0b2006450d0020052001410c6c6a2203280200450d00200328020441002802c0a3c68000118080808000000b200228022c450d00200228023041002802c0a3c68000118080808000000b200241b0046a24808080800020040f0b200241ac026a4201370200200241013602a40220024194bdc580003602a002200241fb8380800036028c04200220024188046a3602a8022002200241af046a36028804200241a0026a41dcb9c5800010f680808000000bbf0302027f017e23808080800041f0036b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2100024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680002002200136020c200220003602082002428080808080203702e8032002200241086a3602e403200241f8016a200241e4036a10f58c8080000240024020022802f8012203450d00200241106a410472200241f8016a410472220141e40110848e808000210020022003360210200228020c450d01200241106a10f68c8080000b20024184026a4201370200200241013602fc01200241c8bdc580003602f801200241fb838080003602142002200241106a360280022002200241e4036a360210200241f8016a41dcb9c5800010f680808000000b2001200041e40110848e8080001a200220033602f801200241106a200241f8016a10fc8c808000200241106a10b0888080002104200241f0036a24808080800020040bfe0503027f017e047f2380808080004190016b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c68000024020010d0002404100280284a4c680004105470d002002410c6a42003702002002410136020420024184a2c48000360200200241e8d1c38000360208200241fc006a410c6a41163602002002418ca2c4800036028c01200241fe8bc48000360284012002411636028001200241e88bc4800036027c20024105200241fc006a4100200310a1828080000b200210ff8c8080004101210302402002290320220442c000540d0041022103200442808001540d00410421032004428080808004540d004109200479a74103766b21030b0240417f200241f0006a28020041146c20036a41046a220120012003491b2203417f4c0d0041002d00fca3c680001a0240200341002802c8a3c68000118180808000002201450d00200241003602840120022001360280012002200336027c2002200241fc006a10dd8c80800020023502840121042002280280012105024020022802702206450d00200241ec006a280200220721034100210103402007200141146c6a21080240024002400240024020032d00000e0400010102040b200341086a21080c020b200841086a21080c010b200841046a21080b2008280200450d00200828020441002802c0a3c68000118080808000000b200141016a2101200341146a21032006417f6a22060d000b0b02402002280268450d00200228026c41002802c0a3c68000118080808000000b20024190016a24808080800020044220862005ad840f0b4101200310b280808000000b10ae80808000000b2002410c6a420037020020024101360204200241b4bec580003602002002200241fc006a360208200241dcb9c5800010f680808000000bfb0402027f017e23808080800041e0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2100024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c6800020022001360214200220003602102002428080808080203702442002200241106a360240200241086a200241c0006a10bc8a8080000240024020022802080d00200228020c21012002200228024441016a2203360244200320022802484b0d00200241003a004f200220013602202002410036021c2002200241c0006a3602182002200241cf006a360224200241d0006a200241186a10d28c808000024020022d004f450d00200241d0006a10d38c8080000c010b200241306a41086a2203200241d0006a41086a280200360200200220022903503703302002280214450d01200241306a10d38c8080000b200241246a42013702002002410136021c200241ecbec58000360218200241fb838080003602542002200241d0006a3602202002200241c0006a360250200241186a41dcb9c5800010f680808000000b200241186a41086a20032802003602002002200229033037031820024100360258200242808080808001370250200241186a10d38c808000200241186a41084100108c8d80800020023502202104200228021c2103200241d0006a108f8780800002402002280250450d00200228025441002802c0a3c68000118080808000000b200241e0006a24808080800020044220862003ad840b9f0502047f017e23808080800041d0026b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2100024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680002002200136021c20022000360218200241206a418002200241186a10998d8080000240200228029801418080808078460d00200241b8016a200241206a41880110848e8080001a200241c0026a41086a200241b0016a280200360200200220022903a8013703c002200241013b01142002410036021020024100360208200241c0026a10d38c808000200241a4026a28020021040240200241a8026a2802002205450d00200421034100210103402004200141146c6a21000240024002400240024020032d00000e0400010102040b200341086a21000c020b200041086a21000c010b200041046a21000b2000280200450d00200028020441002802c0a3c68000118080808000000b200141016a2101200341146a21032005417f6a22050d000b0b024020022802a002450d00200441002802c0a3c68000118080808000000b200241b0026a108f87808000024020022802b002450d00200241b4026a28020041002802c0a3c68000118080808000000b200241206a200241086a10ba8980800020022902242106200241086a10d38c808000200241d0026a24808080800020060f0b200241c4016a4201370200200241013602bc01200241a0bfc580003602b801200241fb8380800036020c2002200241086a3602c0012002200241c0026a360208200241b8016a41dcb9c5800010f680808000000bae0302027f017e23808080800041f0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680000240024020014120490d0020014120470d00200241086a41186a200041ccb3c5800020011b220141186a290000370300200241086a41106a200141106a290000370300200241086a41086a200141086a29000037030020022001290000370308200241286a200241086a10f8878080002002290328210441002d00fca3c680001a410841002802c8a3c68000118180808000002201450d0120012004370000200241f0006a2480808080002001ad42808080808001840f0b200241346a42013702002002410136022c200241d4bfc58000360228200241fb8380800036020c2002200241086a3602302002200241ef006a360208200241286a41dcb9c5800010f680808000000b4101410810b280808000000bb40302027f017e23808080800041f0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680000240024020014120490d0020014120470d00200241086a41186a200041ccb3c5800020011b220141186a290000370300200241086a41106a200141106a290000370300200241086a41086a200141086a29000037030020022001290000370308200241286a200241086a10fa87808000200241286a41186a290300210441002d00fca3c680001a410841002802c8a3c68000118180808000002201450d0120012004370000200241f0006a2480808080002001ad42808080808001840f0b200241346a42013702002002410136022c20024184c0c58000360228200241fb8380800036020c2002200241086a3602302002200241ef006a360208200241286a41dcb9c5800010f680808000000b4101410810b280808000000bd90202027f017e23808080800041306b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680000240024020014108490d0020014108470d00200041ccb3c5800020011b290000210441002d00fca3c680001a410841002802c8a3c68000118180808000002201450d012001200442017c370000200241306a2480808080002001ad42808080808001840f0b200241186a420137020020024101360210200241bcc0c5800036020c200241fb838080003602282002200241246a36021420022002412f6a3602242002410c6a41dcb9c5800010f680808000000b4101410810b280808000000bcd0602077f017e23808080800041c0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2100024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c68000200220013602142002200036021020024280808080802037021c2002200241106a360218200241086a200241186a10bc8a8080000240024020022802080d00200241306a200241186a200228020c10d18580800020022802302204418080808078460d00200228023421052002280214450d012004450d00200541002802c0a3c68000118080808000000b200241246a42013702002002410136021c200241fcc0c58000360218200241fb838080003602342002200241306a36022020022002413f6a360230200241186a41dcb9c5800010f680808000000b0240024002400240200228023822060d00410821070c010b200641ffffffff004b0d0120064103742201417f4c0d0141002d00fca3c680001a200141002802c8a3c68000118180808000002207450d0220072005200110848e808000220021030240200141786a220841037641016a4107712201450d00200141037421012000210303402003200329030042017c370300200341086a2103200141786a22010d000b0b20084138490d00200020064103746a210003402003200329030042017c370300200341086a2201200129030042017c370300200341106a2201200129030042017c370300200341186a2201200129030042017c370300200341206a2201200129030042017c370300200341286a2201200129030042017c370300200341306a2201200129030042017c370300200341386a2201200129030042017c370300200341c0006a22032000470d000b0b02402004450d00200541002802c0a3c68000118080808000000b200241186a2007200610d78580800020023502202109200228021c210302402006450d00200741002802c0a3c68000118080808000000b200241c0006a24808080800020094220862003ad840f0b10ae80808000000b4108200110b280808000000ba50201027f23808080800041206b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680000240024020010d0041002d00fca3c680001a410841002802c8a3c68000118180808000002203450d0120034201370000200241206a2480808080002003ad42808080808001840f0b200241106a420037020020024101360208200241f4c1c5800036020420022002411c6a36020c200241046a41dcb9c5800010f680808000000b4101410810b280808000000baf0202027f017e23808080800041206b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680000240024020010d0010e689808000210441002d00fca3c680001a410841002802c8a3c68000118180808000002203450d0120032004370000200241206a2480808080002003ad42808080808001840f0b200241106a420037020020024101360208200241d8c2c5800036020420022002411c6a36020c200241046a41dcb9c5800010f680808000000b4101410810b280808000000bcc0202027f027e23808080800041206b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680000240024020010d0042002104420021050340200510de8980800020047c2104200542017c220542e807520d000b41002d00fca3c680001a410841002802c8a3c68000118180808000002203450d0120032004370000200241206a2480808080002003ad42808080808001840f0b200241106a420037020020024101360208200241ccc3c5800036020420022002411c6a36020c200241046a41dcb9c5800010f680808000000b4101410810b280808000000bcc0202027f027e23808080800041206b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680000240024020010d0042002104420021050340200510de8980800020047c2104200542017c220542e807520d000b41002d00fca3c680001a410841002802c8a3c68000118180808000002203450d0120032004370000200241206a2480808080002003ad42808080808001840f0b200241106a420037020020024101360208200241bcc4c5800036020420022002411c6a36020c200241046a41dcb9c5800010f680808000000b4101410810b280808000000bb30301027f23808080800041306b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c6800002400240024020014104490d0020014104470d0002400240200041ccb3c5800020011b28000022010d002002410c6a4101410010d68580800020022802142101200228021021030c010b2001417f4c0d0241002d00fca3c680001a200141002802c8a3c68000118180808000002200450d032002410c6a2000410010d6858080002002280214210120022802102103200041002802c0a3c68000118080808000000b200241306a2480808080002001ad4220862003ad840f0b200241186a420137020020024101360210200241f4c4c5800036020c200241fb838080003602282002200241246a36021420022002412f6a3602242002410c6a41dcb9c5800010f680808000000b10ae80808000000b4101200110b280808000000bd80202027f027e23808080800041c0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680000240024020010d002002411c6a10ab8a808000200241086a2002411c6a10e688808000200229031021042002290308210541002d00fca3c680001a410841002802c8a3c68000118180808000002203450d012003200442002005a71b370000200241c0006a2480808080002003ad42808080808001840f0b200241286a420037020020024101360220200241e0c5c5800036021c20022002413c6a3602242002411c6a41dcb9c5800010f680808000000b4101410810b280808000000b860401027f23808080800041f0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680000240024020010d002002410c6a10e78980800041002d00fca3c680001a41e00041002802c8a3c68000118180808000002203450d012003200229000c3700002003200229004c370040200341386a2002410c6a41386a290000370000200341306a2002410c6a41306a290000370000200341286a2002410c6a41286a290000370000200341206a2002410c6a41206a290000370000200341186a2002410c6a41186a290000370000200341106a2002410c6a41106a290000370000200341086a2002410c6a41086a290000370000200341c8006a2002410c6a41c8006a290000370000200341d0006a2002410c6a41d0006a290000370000200341d8006a2002410c6a41d8006a290000370000200241f0006a2480808080002003ad4280808080800c840f0b200241186a420037020020024101360210200241d0c6c5800036020c2002200241ec006a3602142002410c6a41dcb9c5800010f680808000000b410141e00010b280808000000b860401027f23808080800041f0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680000240024020010d002002410c6a10e88980800041002d00fca3c680001a41e00041002802c8a3c68000118180808000002203450d012003200229000c3700002003200229004c370040200341386a2002410c6a41386a290000370000200341306a2002410c6a41306a290000370000200341286a2002410c6a41286a290000370000200341206a2002410c6a41206a290000370000200341186a2002410c6a41186a290000370000200341106a2002410c6a41106a290000370000200341086a2002410c6a41086a290000370000200341c8006a2002410c6a41c8006a290000370000200341d0006a2002410c6a41d0006a290000370000200341d8006a2002410c6a41d8006a290000370000200241f0006a2480808080002003ad4280808080800c840f0b200241186a420037020020024101360210200241c0c7c5800036020c2002200241ec006a3602142002410c6a41dcb9c5800010f680808000000b410141e00010b280808000000b9c0301027f23808080800041f0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680000240024020010d00200241086a10e98980800041002d00fca3c680001a41e20041002802c8a3c68000118180808000002203450d012003200241086a41c10010848e808000220341e1006a200241086a41e1006a2d00003a0000200341d9006a200241086a41d9006a290000370000200341d1006a200241086a41d1006a290000370000200341c9006a200241086a41c9006a29000037000020032002290049370041200241f0006a2480808080002003ad4280808080a00c840f0b200241146a42003702002002410136020c200241acc8c580003602082002200241ec006a360210200241086a41dcb9c5800010f680808000000b410141e20010b280808000000bec0101027f23808080800041206b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c68000024020010d0010ea89808000200241206a24808080800042010f0b200241106a42003702002002410136020820024194c9c5800036020420022002411c6a36020c200241046a41dcb9c5800010f680808000000bab0301027f23808080800041f0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2100024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680002002200136020420022000360200200241086a418002200210b48a808000024020022802080d00200241386a41086a200241146a280200360200200241c8006a41086a200241086a41186a290200370300200241c8006a41106a200241286a290200370300200241c8006a41186a200241306a2902003703002002200229020c3703382002200241086a41106a290200370348200241386a200241c8006a10eb89808000200241f0006a24808080800042010f0b200241d4006a42013702002002410136024c200241c8c9c58000360248200241fb8380800036023c2002200241386a3602502002200241ef006a360238200241c8006a41dcb9c5800010f680808000000b8b0301027f23808080800041d0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2100024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c6800020022001360208200220003602042002410c6a418002200241046a1089878080000240200228020c418080808078460d00200241c0006a41086a2002410c6a41086a280200360200200241286a41086a200241206a2802003602002002200229020c37034020022002290218370328200241c0006a200241286a200228022410ec89808000200241d0006a24808080800042010f0b200241346a42013702002002410136022c20024184cac58000360228200241fb838080003602442002200241c0006a3602302002200241cf006a360240200241286a41dcb9c5800010f680808000000bec0101027f23808080800041206b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c68000024020010d0010ed89808000200241206a24808080800042010f0b200241106a420037020020024101360208200241eccac5800036020420022002411c6a36020c200241046a41dcb9c5800010f680808000000b820a01167f23808080800041d0026b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2100024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680002002200036020c2002200136021020024280808080802037027820022002410c6a36027402400240200141c000490d00200241e0016a41086a200041086a290000370300200241e0016a41106a200041106a290000370300200241e0016a41186a200041186a290000370300200241e0016a41206a200041206a290000370300200241e0016a41286a200041286a290000370300200241e0016a41306a200041306a290000370300200241e0016a41386a200041386a2900003703002002200041c0006a220336020c200220002900003703e0012002200141406a220436021020044120490d00200241a0026a41086a200341086a290000370300200241a0026a41106a200341106a290000370300200241a0026a41186a200341186a2900003703002002200141a07f6a3602102002200041e0006a36020c200220032900003703a0022002200241f4006a10bc8a80800020022802000d00200241c4026a200241f4006a200228020410d08580800020022802c4022201418080808078460d0020024180016a41086a2203200241e0016a41086a220429030037030020024180016a41106a2205200241e0016a41106a220629030037030020024180016a41186a2207200241e0016a41186a220829030037030020024180016a41206a2209200241e0016a41206a220a29030037030020024180016a41286a220b200241e0016a41286a220c29030037030020024180016a41306a220d200241e0016a41306a220e29030037030020024180016a41386a220f200241e0016a41386a2210290300370300200220022903e0013703800120022802cc02211120022802c8022100200241d8016a2212200241a0026a41186a2213290300370300200241d0016a2214200241a0026a41106a2215290300370300200241c8016a2216200241a0026a41086a2217290300370300200220022903a0023703c001200241146a20024180016a41e00010848e8080001a2002280210450d012001450d00200041002802c0a3c68000118080808000000b2002418c016a42013702002002410136028401200241a0cbc5800036028001200241fb838080003602182002200241146a360288012002200241e0016a36021420024180016a41dcb9c5800010f680808000000b20024180016a200241146a41e00010848e8080001a200420032902003703002006200529020037030020082007290200370300200a2009290200370300200c200b290200370300200e200d2902003703002010200f29020037030020172016290200370300201520142902003703002013201229020037030020022002290280013703e001200220022902c0013703a002200241e0016a20002011200241a0026a41002802a8a2c6800011898080800000210302402001450d00200041002802c0a3c68000118080808000000b41002d00fca3c680001a0240410141002802c8a3c68000118180808000002200450d00200020033a0000200241d0026a2480808080002000ad428080808010840f0b4101410110b280808000000be90301047f23808080800041d0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2100024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680002002200136020c20022000360208200241106a418002200241086a1088878080000240024020022802102203418080808078460d0020022d00282101200228021c2100200228021422042002280218200241206a2802002205200241246a28020041002802e0a1c680001186808080000020010d0102402000450d00200541002802c0a3c68000118080808000000b02402003450d00200441002802c0a3c68000118080808000000b200241d0006a24808080800042010f0b200241386a420137020020024101360230200241d4cbc5800036022c200241fb838080003602482002200241c4006a3602342002200241cf006a3602442002412c6a41dcb9c5800010f680808000000b2002411c6a420037020020024101360214200241eca3c48000360210200241e8d1c38000360218200241106a41f4a3c4800010f680808000000ba60201027f23808080800041206b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680000240024020010d0041002d00fca3c680001a410841002802c8a3c68000118180808000002203450d01200342e807370000200241206a2480808080002003ad42808080808001840f0b200241106a420037020020024101360208200241bcccc5800036020420022002411c6a36020c200241046a41dcb9c5800010f680808000000b4101410810b280808000000bc60202037f017e23808080800041206b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c68000024020010d00200241046a10eb8880800020022802042103200241046a20022802082204200228020c108489808000200235020c2105200228020821010240200341ffffff3f71450d00200441002802c0a3c68000118080808000000b200241206a24808080800020054220862001ad840f0b200241106a420037020020024101360208200241a4cdc5800036020420022002411c6a36020c200241046a41dcb9c5800010f680808000000bb70202027f017e23808080800041e0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c68000024020010d00200210ee89808000200241d4006a200210ce86808000200235025c21042002280258210302402002280240450d00200241c4006a28020041002802c0a3c68000118080808000000b200241e0006a24808080800020044220862003ad840f0b2002410c6a4200370200200241013602042002418ccec580003602002002200241d4006a360208200241dcb9c5800010f680808000000bbe0302027f027e23808080800041e0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c6800002400240024020010d002002413c6a10cb87808000200241286a2002413c6a10e688808000200241086a2002290330420020022802281b42004206420010878e8080002002413c6a10ca87808000200241186a2002413c6a10e78880800020022903104200520d01200229030822042002290320420020022802181b7c22052004540d0141002d00fca3c680001a410841002802c8a3c68000118180808000002203450d0220032005370000200241e0006a2480808080002003ad42808080808001840f0b200241c8006a420037020020024101360240200241fccec5800036023c2002200241dc006a3602442002413c6a41dcb9c5800010f680808000000b41c18fc2800041fa0041bc90c2800010a181808000000b4101410810b280808000000bb70202027f017e23808080800041f0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c68000024020010d00200210b287808000200241e4006a200210cd86808000200235026c21042002280268210302402002280250450d00200241d4006a28020041002802c0a3c68000118080808000000b200241f0006a24808080800020044220862003ad840f0b2002410c6a420037020020024101360204200241e4cfc580003602002002200241e4006a360208200241dcb9c5800010f680808000000bb70202027f017e23808080800041f0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c68000024020010d00200210b187808000200241e4006a200210cd86808000200235026c21042002280268210302402002280250450d00200241d4006a28020041002802c0a3c68000118080808000000b200241f0006a24808080800020044220862003ad840f0b2002410c6a420037020020024101360204200241ccd0c580003602002002200241e4006a360208200241dcb9c5800010f680808000000bc40301027f23808080800041e0046b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2100024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680002002200136020c20022000360208200241106a418002200241086a10978d80800002400240200228029802418080808078460d00200241b8026a200241106a41980210848e8080001a200241d8046a200241b0026a280200360200200220022903a8023703d004200241b8026a200241d0046a10ef89808000210141002d00fca3c680001a410141002802c8a3c68000118180808000002203450d01200320013a0000200241e0046a2480808080002003ad428080808010840f0b200241c4026a4201370200200241013602bc02200241a0d1c580003602b802200241fb838080003602d4042002200241d0046a3602c0022002200241df046a3602d004200241b8026a41dcb9c5800010f680808000000b4101410110b280808000000be40202027f017e23808080800041306b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c68000024020014108490d0020014128470d002002418080808078360208200241146a200241086a10b48c808000200235021c210420022802182103024020022802082201418080808078460d002001450d00200228020c41002802c0a3c68000118080808000000b200241306a24808080800020044220862003ad840f0b200241206a420137020020024101360218200241e4d1c58000360214200241fb8380800036020c2002200241086a36021c20022002412f6a360208200241146a41dcb9c5800010f680808000000b9a0601057f2380808080004180026b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2100024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680002002200136020c200220003602082002428080808080203702f8012002200241086a3602f401200241f8006a200241f4016a10dc8c8080000240024020022802e0012200418080808078460d00200241106a200241f8006a41e80010848e8080001a20022802e801210320022802e4012101200228020c450d0102402003450d00200121044100210503402001200541146c6a21060240024002400240024020042d00000e0400010102040b200441086a21060c020b200641086a21060c010b200641046a21060b2006280200450d00200628020441002802c0a3c68000118080808000000b200541016a2105200441146a21042003417f6a22030d000b0b2000450d00200141002802c0a3c68000118080808000000b20024184016a42013702002002410136027c20024198d2c58000360278200241fb838080003602142002200241106a360280012002200241f4016a360210200241f8006a41dcb9c5800010f680808000000b20022802ec012104200241f8006a200241106a41e80010848e8080001a200220043602ec01200220033602e801200220013602e401200220003602e001200241f8006a10f089808000024020022802e8012204450d0020022802e401220521034100210103402005200141146c6a21000240024002400240024020032d00000e0400010102040b200341086a21000c020b200041086a21000c010b200041046a21000b2000280200450d00200028020441002802c0a3c68000118080808000000b200141016a2101200341146a21032004417f6a22040d000b0b024020022802e001450d0020022802e40141002802c0a3c68000118080808000000b20024180026a24808080800042010be80402037f017e23808080800041c0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2104024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c6800020022001360214200220043602102002428080808080203702342002200241106a36023002402001450d0020022001417f6a22013602142002200041016a360210024002400240024020002d00000e020100040b200241086a200241306a10bc8a80800020022802080d03200241186a200241306a200228020c10d08580800020022802182201418080808078460d03200228021c21002002280214450d012001450d03200041002802c0a3c68000118080808000000c030b20010d022002418080808078360218200241306a200241186a10e2898080000c010b2001418180808078460d012002418080808078360218200241306a200241186a10e2898080002001450d00200041002802c0a3c68000118080808000000b200241186a20022802342200200228023810d68580800020023502202105200228021c210102402002280230450d00200041002802c0a3c68000118080808000000b200241c0006a24808080800020054220862001ad840f0b200241246a42013702002002410136021c200241d4d2c58000360218200241fb838080003602342002200241306a36022020022002413f6a360230200241186a41dcb9c5800010f680808000000ba10803027f017e037f23808080800041f0016b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2100024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c68000200220013602202002200036021c20024280808080802037028c0120022002411c6a36028801200241086a20024188016a10bc8a8080000240024020022802080d00200241246a20024188016a200228020c10d08580800020022802242200418080808078460d00200228022821032002280220450d012000450d00200341002802c0a3c68000118080808000000b20024194016a42013702002002410136028c012002418cd3c5800036028801200241fb838080003602282002200241246a360290012002200241106a36022420024188016a41dcb9c5800010f680808000000b024002400240200228022c220141406a4121490d0020014120490d0020014160714120470d010b20024180808080783602100c010b20024188016a41e0006a200341e0006a2d00003a000020024188016a41d8006a200341d8006a29000037030020024188016a41d0006a200341d0006a29000037030020024188016a41c8006a200341c8006a29000037030020024188016a41086a200341086a29000037030020024188016a41106a200341106a29000037030020024188016a41186a200341186a290000370300200220032900403703c801200220032900003703880120024188016a41386a200341386a29000037030020024188016a41306a200341306a29000037030020024188016a41286a200341286a290000370300200220032900203703a801200241246a20024188016a41e10010848e8080001a200241106a200241246a10e3898080000b02402000450d00200341002802c0a3c68000118080808000000b20024188016a200241106a10b58c8080002002350290012104200228028c012105024020022802102203418080808078460d00024020022802182200450d00200228021421062000410171210741002101024020004101460d002000417e7121002006210341002101034002402003280200450d00200341046a28020041002802c0a3c68000118080808000000b0240200341106a280200450d00200341146a28020041002802c0a3c68000118080808000000b200341206a21032000200141026a2201470d000b0b02402007450d00200620014104746a2203280200450d00200328020441002802c0a3c68000118080808000000b200228021021030b2003450d00200228021441002802c0a3c68000118080808000000b200241f0016a24808080800020044220862005ad840bfe0102027f017e23808080800041206b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c6800002402001450d00200241106a420037020020024101360208200241fcd3c5800036020420022002411c6a36020c200241046a41dcb9c5800010f680808000000b200241046a4108410010ce8780800020022902082104200241206a24808080800020040ba50201027f23808080800041206b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680000240024020010d0041002d00fca3c680001a410841002802c8a3c68000118180808000002203450d0120034200370000200241206a2480808080002003ad42808080808001840f0b200241106a420037020020024101360208200241e8d4c5800036020420022002411c6a36020c200241046a41dcb9c5800010f680808000000b4101410810b280808000000b9d0301027f23808080800041d0026b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2100024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c680002002200136020c20022000360208200241106a418002200241086a10ae888080000240024020022903104202510d000240200228029802450d002002419c026a28020041002802c0a3c68000118080808000000b41002d00fca3c680001a410141002802c8a3c68000118180808000002203450d01200341003a0000200241d0026a2480808080002003ad428080808010840f0b200241b8026a4201370200200241013602b002200241a0d1c580003602ac02200241fb838080003602c8022002200241c4026a3602b4022002200241cf026a3602c402200241ac026a41dcb9c5800010f680808000000b4101410110b280808000000be40202027f017e23808080800041306b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c68000024020014108490d0020014128470d002002418080808078360208200241146a200241086a10b48c808000200235021c210420022802182103024020022802082201418080808078460d002001450d00200228020c41002802c0a3c68000118080808000000b200241306a24808080800020044220862003ad840f0b200241206a420137020020024101360218200241e4d1c58000360214200241fb8380800036020c2002200241086a36021c20022002412f6a360208200241146a41dcb9c5800010f680808000000bf20302027f017e23808080800041c0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2100024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c68000200220013602142002200036021020024280808080802037021c2002200241106a360218200241086a200241186a10bc8a8080000240024020022802080d00200241306a200241186a200228020c10d08580800020022802302203418080808078460d00200228023421012002280214450d012003450d00200141002802c0a3c68000118080808000000b200241246a42013702002002410136021c20024198d5c58000360218200241fb838080003602342002200241306a36022020022002413f6a360230200241186a41dcb9c5800010f680808000000b200220022802383602202002200136021c20022003360218200241306a200241186a10c987808000200241306a10b1888080002104024020022802302203418280808078480d002003450d00200228023441002802c0a3c68000118080808000000b200241c0006a24808080800020040b900502037f017e23808080800041c0006b220224808080800041004100280280a4c680002203410120031b360280a4c68000200041ccb3c5800020011b2104024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c6800020022001360214200220043602102002428080808080203702342002200241106a36023002402001450d0020022001417f6a22013602142002200041016a360210024002400240024020002d00000e020100040b200241086a200241306a10bc8a80800020022802080d03200241186a200241306a200228020c10d08580800020022802182201418080808078460d032001418180808078460d03200228021c21002002280214450d012001450d03200041002802c0a3c68000118080808000000c030b20010d0241818080807821010c010b2001418280808078460d01200228022021030b200220033602202002200036021c20022001360218200241306a200241186a10c687808000024020022802182201418280808078480d002001450d00200228021c41002802c0a3c68000118080808000000b200241186a200241306a10b68c80800020023502202105200228021c2101024020022802302200418080808078460d002000450d00200228023441002802c0a3c68000118080808000000b200241c0006a24808080800020054220862001ad840f0b200241246a42013702002002410136021c200241c8d5c58000360218200241fb838080003602342002200241306a36022020022002413f6a360230200241186a41dcb9c5800010f680808000000bc60403027f017e017f23808080800041206b220224808080800041004100280280a4c680002203410120031b360280a4c68000024002402003450d0020034101470d0103404100280280a4c680004101460d000c020b0b410041d8a7c28000360290a1c68000410041f0a7c2800036028ca1c6800041004102360280a4c680000b410041002802d8a2c68000118c808080000041ff0171360284a4c6800002400240024020010d0041002d00fca3c680001a411841002802c8a3c68000118180808000002203450d012003428680808080808080807f370208200341d0a4c480003602042003418080808078360200200341146a4107360200200341106a41d6a4c4800036020041002d00fca3c680001a411c41002802c8a3c68000118180808000002201450d0220024100360208200220013602042002411c360200200241023602182002200241186a36021c2002411c6a200210c08a80800020034102200210d2858080002002350208210420022802042101024020032802002205418080808078460d002005450d00200328020441002802c0a3c68000118080808000000b0240200328020c2205418080808078460d002005450d00200341106a28020041002802c0a3c68000118080808000000b200341002802c0a3c6800011808080800000200241206a24808080800020044220862001ad840f0b2002410c6a420037020020024101360204200241b0d6c5800036020020022002411c6a360208200241dcb9c5800010f680808000000b4104411810b280808000000b4101411c10b280808000000bcf0101027e42891c210242f8eb8717210302400240024002400240024002400240024020012d0000417f6a0e09080001020304050607080b42b430210242c0abe81721030c070b429885991221030c060b42a893cc1621030c050b42e8e89b0821030c040b2001410c6a350200220342ab147e42de077c21022003429bf1d4067e42e0b9f4077c21030c030b42a8afc00921030c020b4200210242d0bb920321030c010b42c091e20e42d88cf00920012d00011b2103420021020b200041003b011020002002370308200020033703000b970601087f23808080800041c0006b220124808080800020014106360210200141a5d8c5800036020c41002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241a5d8c58000360200200241046a410636020041a5d8c58000410610d782808000450d0141002d00fca3c680001a412041002802c8a3c68000118180808000002203450d02200342acf6debeefe0d9c8d300370308200341bd8080800036021820034101360204200341abd8c58000360200200341106a42efc9c9edb5e7b3a6c70037030020014101360220200120033602182001200341206a3602242001200336021c200141306a200141186a10fa86808000200128023821042001280234210520012802302106200141186a41086a2207410036020020014280808080c000370218200141186a410010a086808000200128021c200728020041246c6a220341003a00202003410436021c200341acd8c580003602182003420437021020034200370208200342808080808001370200200141306a41086a2208200728020041016a360200200120012902183703302001410c6a200141306a41b0d8c58000410410898b8080002001200128020c36022020012001280210220336021820012003200128021441246c6a3602242001200336021c200141306a200141186a10fb8680800020012006360220200120053602182001200520044105746a3602242001200536021c200041c4006a200141186a10fa86808000200141236a20082802003600002000413c6a2002ad4280808080108437020020004101360238200041013a0000200041d8006a410036020020004280808080c0003703502001200129023037001b20002001290018370001200041086a2001411f6a290000370000200141c0006a2480808080000f0b4104410810b280808000000b200241002802c0a3c6800011808080800000200141246a42013702002001410236021c20014180ddc18000360218200141b4808080003602342001200141306a36022020012001410c6a360230200141186a4190ddc1800010f680808000000b4108412010b280808000000b950601087f23808080800041c0006b220124808080800020014106360210200141a5d8c5800036020c41002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241a5d8c58000360200200241046a410636020041a5d8c58000410610d782808000450d0141002d00fca3c680001a412041002802c8a3c68000118180808000002203450d02200342dc85e1fdf99cd0816b370308200341f88380800036021820034101360204200341abd8c58000360200200341106a42dfda92949ba784d56637030020014101360220200120033602182001200341206a3602242001200336021c200141306a200141186a10fa86808000200128023821042001280234210520012802302106200141186a41086a2207410036020020014280808080c000370218200141186a410010a086808000200128021c200728020041246c6a220341003a00202003410436021c200341acd8c580003602182003420437021020034200370208200342808080808001370200200141306a41086a2208200728020041016a360200200120012902183703302001410c6a200141306a41b0d8c58000410410d08b8080002001200128020c36022020012001280210220336021820012003200128021441246c6a3602242001200336021c200141306a200141186a10fb8680800020012006360220200120053602182001200520044105746a3602242001200536021c200041c4006a200141186a10fa86808000200141236a20082802003600002000413c6a2002ad4280808080108437020020004101360238200041013a0000200041d8006a410036020020004280808080c0003703502001200129023037001b20002001290018370001200041086a2001411f6a290000370000200141c0006a2480808080000f0b4104410810b280808000000b200241002802c0a3c6800011808080800000200141246a42013702002001410236021c20014180ddc18000360218200141b4808080003602342001200141306a36022020012001410c6a360230200141186a4190ddc1800010f680808000000b4108412010b280808000000b950601087f23808080800041c0006b220124808080800020014106360210200141a5d8c5800036020c41002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241a5d8c58000360200200241046a410636020041a5d8c58000410610d782808000450d0141002d00fca3c680001a412041002802c8a3c68000118180808000002203450d02200342d4b0f086cab3d2eb14370308200341948380800036021820034101360204200341abd8c58000360200200341106a4299f0ab899787d3ad3a37030020014101360220200120033602182001200341206a3602242001200336021c200141306a200141186a10fa86808000200128023821042001280234210520012802302106200141186a41086a2207410036020020014280808080c000370218200141186a410010a086808000200128021c200728020041246c6a220341003a00202003410436021c200341acd8c580003602182003420437021020034200370208200342808080808001370200200141306a41086a2208200728020041016a360200200120012902183703302001410c6a200141306a41b0d8c58000410410c08b8080002001200128020c36022020012001280210220336021820012003200128021441246c6a3602242001200336021c200141306a200141186a10fb8680800020012006360220200120053602182001200520044105746a3602242001200536021c200041c4006a200141186a10fa86808000200141236a20082802003600002000413c6a2002ad4280808080108437020020004101360238200041013a0000200041d8006a410036020020004280808080c0003703502001200129023037001b20002001290018370001200041086a2001411f6a290000370000200141c0006a2480808080000f0b4104410810b280808000000b200241002802c0a3c6800011808080800000200141246a42013702002001410236021c20014180ddc18000360218200141b4808080003602342001200141306a36022020012001410c6a360230200141186a4190ddc1800010f680808000000b4108412010b280808000000b960601087f23808080800041c0006b220124808080800020014106360210200141a5d8c5800036020c41002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241a5d8c58000360200200241046a410636020041a5d8c58000410610d782808000450d0141002d00fca3c680001a412041002802c8a3c68000118180808000002203450d02200342e7b0a091f3ed9c85c500370308200341ea8180800036021820034101360204200341abd8c58000360200200341106a42b891b68c98adebcf6137030020014101360220200120033602182001200341206a3602242001200336021c200141306a200141186a10fa86808000200128023821042001280234210520012802302106200141186a41086a2207410036020020014280808080c000370218200141186a410010a086808000200128021c200728020041246c6a220341003a00202003410436021c200341acd8c580003602182003420437021020034200370208200342808080808001370200200141306a41086a2208200728020041016a360200200120012902183703302001410c6a200141306a41b0d8c58000410410928b8080002001200128020c36022020012001280210220336021820012003200128021441246c6a3602242001200336021c200141306a200141186a10fb8680800020012006360220200120053602182001200520044105746a3602242001200536021c200041c4006a200141186a10fa86808000200141236a20082802003600002000413c6a2002ad4280808080108437020020004101360238200041013a0000200041d8006a410036020020004280808080c0003703502001200129023037001b20002001290018370001200041086a2001411f6a290000370000200141c0006a2480808080000f0b4104410810b280808000000b200241002802c0a3c6800011808080800000200141246a42013702002001410236021c20014180ddc18000360218200141b4808080003602342001200141306a36022020012001410c6a360230200141186a4190ddc1800010f680808000000b4108412010b280808000000b950601087f23808080800041c0006b220124808080800020014106360210200141a5d8c5800036020c41002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241a5d8c58000360200200241046a410636020041a5d8c58000410610d782808000450d0141002d00fca3c680001a412041002802c8a3c68000118180808000002203450d02200342cecfe0e5d1c6dbf757370308200341fb8280800036021820034101360204200341abd8c58000360200200341106a42a8c786dcd8d2a98d1737030020014101360220200120033602182001200341206a3602242001200336021c200141306a200141186a10fa86808000200128023821042001280234210520012802302106200141186a41086a2207410036020020014280808080c000370218200141186a410010a086808000200128021c200728020041246c6a220341003a00202003410436021c200341acd8c580003602182003420437021020034200370208200342808080808001370200200141306a41086a2208200728020041016a360200200120012902183703302001410c6a200141306a41b0d8c580004104109e8b8080002001200128020c36022020012001280210220336021820012003200128021441246c6a3602242001200336021c200141306a200141186a10fb8680800020012006360220200120053602182001200520044105746a3602242001200536021c200041c4006a200141186a10fa86808000200141236a20082802003600002000413c6a2002ad4280808080108437020020004101360238200041013a0000200041d8006a410036020020004280808080c0003703502001200129023037001b20002001290018370001200041086a2001411f6a290000370000200141c0006a2480808080000f0b4104410810b280808000000b200241002802c0a3c6800011808080800000200141246a42013702002001410236021c20014180ddc18000360218200141b4808080003602342001200141306a36022020012001410c6a360230200141186a4190ddc1800010f680808000000b4108412010b280808000000b960601087f23808080800041c0006b220124808080800020014106360210200141a5d8c5800036020c41002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241a5d8c58000360200200241046a410636020041a5d8c58000410610d782808000450d0141002d00fca3c680001a412041002802c8a3c68000118180808000002203450d02200342c4ccab9c81ebb4b8db00370308200341f08180800036021820034101360204200341abd8c58000360200200341106a42a4d2928ecac0faa43237030020014101360220200120033602182001200341206a3602242001200336021c200141306a200141186a10fa86808000200128023821042001280234210520012802302106200141186a41086a2207410036020020014280808080c000370218200141186a410010a086808000200128021c200728020041246c6a220341003a00202003410436021c200341acd8c580003602182003420437021020034200370208200342808080808001370200200141306a41086a2208200728020041016a360200200120012902183703302001410c6a200141306a41b0d8c58000410410cc8b8080002001200128020c36022020012001280210220336021820012003200128021441246c6a3602242001200336021c200141306a200141186a10fb8680800020012006360220200120053602182001200520044105746a3602242001200536021c200041c4006a200141186a10fa86808000200141236a20082802003600002000413c6a2002ad4280808080108437020020004101360238200041013a0000200041d8006a410036020020004280808080c0003703502001200129023037001b20002001290018370001200041086a2001411f6a290000370000200141c0006a2480808080000f0b4104410810b280808000000b200241002802c0a3c6800011808080800000200141246a42013702002001410236021c20014180ddc18000360218200141b4808080003602342001200141306a36022020012001410c6a360230200141186a4190ddc1800010f680808000000b4108412010b280808000000b960601087f23808080800041c0006b220124808080800020014106360210200141a5d8c5800036020c41002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241a5d8c58000360200200241046a410636020041a5d8c58000410610d782808000450d0141002d00fca3c680001a412041002802c8a3c68000118180808000002203450d02200342cc9a879ae783ad8254370308200341f28380800036021820034101360204200341abd8c58000360200200341106a42f9beb892cc84b284c80037030020014101360220200120033602182001200341206a3602242001200336021c200141306a200141186a10fa86808000200128023821042001280234210520012802302106200141186a41086a2207410036020020014280808080c000370218200141186a410010a086808000200128021c200728020041246c6a220341003a00202003410436021c200341acd8c580003602182003420437021020034200370208200342808080808001370200200141306a41086a2208200728020041016a360200200120012902183703302001410c6a200141306a41b0d8c58000410410aa8b8080002001200128020c36022020012001280210220336021820012003200128021441246c6a3602242001200336021c200141306a200141186a10fb8680800020012006360220200120053602182001200520044105746a3602242001200536021c200041c4006a200141186a10fa86808000200141236a20082802003600002000413c6a2002ad4280808080108437020020004101360238200041013a0000200041d8006a410036020020004280808080c0003703502001200129023037001b20002001290018370001200041086a2001411f6a290000370000200141c0006a2480808080000f0b4104410810b280808000000b200241002802c0a3c6800011808080800000200141246a42013702002001410236021c20014180ddc18000360218200141b4808080003602342001200141306a36022020012001410c6a360230200141186a4190ddc1800010f680808000000b4108412010b280808000000b960601087f23808080800041c0006b220124808080800020014106360210200141a5d8c5800036020c41002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241a5d8c58000360200200241046a410636020041a5d8c58000410610d782808000450d0141002d00fca3c680001a412041002802c8a3c68000118180808000002203450d0220034288f7e1d594faa5a1203703082003418c8380800036021820034101360204200341abd8c58000360200200341106a42b889cc8cd692ff80fa0037030020014101360220200120033602182001200341206a3602242001200336021c200141306a200141186a10fa86808000200128023821042001280234210520012802302106200141186a41086a2207410036020020014280808080c000370218200141186a410010a086808000200128021c200728020041246c6a220341003a00202003410436021c200341acd8c580003602182003420437021020034200370208200342808080808001370200200141306a41086a2208200728020041016a360200200120012902183703302001410c6a200141306a41b0d8c58000410410bd8b8080002001200128020c36022020012001280210220336021820012003200128021441246c6a3602242001200336021c200141306a200141186a10fb8680800020012006360220200120053602182001200520044105746a3602242001200536021c200041c4006a200141186a10fa86808000200141236a20082802003600002000413c6a2002ad4280808080108437020020004101360238200041013a0000200041d8006a410036020020004280808080c0003703502001200129023037001b20002001290018370001200041086a2001411f6a290000370000200141c0006a2480808080000f0b4104410810b280808000000b200241002802c0a3c6800011808080800000200141246a42013702002001410236021c20014180ddc18000360218200141b4808080003602342001200141306a36022020012001410c6a360230200141186a4190ddc1800010f680808000000b4108412010b280808000000b970601087f23808080800041c0006b220124808080800020014106360210200141a5d8c5800036020c41002d00fca3c680001a024002400240410841002802c8a3c68000118180808000002202450d00200241a5d8c58000360200200241046a410636020041a5d8c58000410610d782808000450d0141002d00fca3c680001a412041002802c8a3c68000118180808000002203450d02200342d1c5efcdcd82cde1ff00370308200341938280800036021820034101360204200341abd8c58000360200200341106a429ccab49c93e2e495cf0037030020014101360220200120033602182001200341206a3602242001200336021c200141306a200141186a10fa86808000200128023821042001280234210520012802302106200141186a41086a2207410036020020014280808080c000370218200141186a410010a086808000200128021c200728020041246c6a220341003a00202003410436021c200341acd8c580003602182003420437021020034200370208200342808080808001370200200141306a41086a2208200728020041016a360200200120012902183703302001410c6a200141306a41b0d8c58000410410bc8b8080002001200128020c36022020012001280210220336021820012003200128021441246c6a3602242001200336021c200141306a200141186a10fb8680800020012006360220200120053602182001200520044105746a3602242001200536021c200041c4006a200141186a10fa86808000200141236a20082802003600002000413c6a2002ad4280808080108437020020004101360238200041013a0000200041d8006a410036020020004280808080c0003703502001200129023037001b20002001290018370001200041086a2001411f6a290000370000200141c0006a2480808080000f0b4104410810b280808000000b200241002802c0a3c6800011808080800000200141246a42013702002001410236021c20014180ddc18000360218200141b4808080003602342001200141306a36022020012001410c6a360230200141186a4190ddc1800010f680808000000b4108412010b280808000000bfe0503037f017e047f2380808080004190016b22012480808080002000280208220241286c2103200028020421000240024020020d00420021040c010b2002410371210502400240200241044f0d0041002106420021040c010b20004198016a21072002417c712108410021064200210403402007290300200741586a290300200741b07f6a290300200741887f6a29030020047c7c7c7c2104200741a0016a21072008200641046a2206470d000b0b2005450d00200641286c20006a41206a21070340200729030020047c2104200741286a21072005417f6a22050d000b0b200020036a210520014298d5afd2c6aeacae2f370358200142c2cdc8b0c7b9e78f857f370350200142e4c5bdb4b2e9a5a6807f370368200142d790d7a3fef9fda0c80037036020012004370310200141d0006a4120200141106a410841002802e0a1c68000118680808000002000210702400340024020030d00200141046a2000200510bd87808000200128020c2002470d0202402002450d00200241286c2103200141e0006a2107200141d0006a41206a220541086a21060340200041206a2903002104200020001084888080001a200141106a200010fa87808000200141d0006a200010fa87808000024002402001280284010d00200141d0006a200010fa878080002001280288010d0020001086888080000c010b200542003703002007428080808080808080807f370300200642003703002001420037035820012004370368200142013703502000200141d0006a1082888080000b200041286a2100200341586a22030d000b0b200141046a10a88d80800020014190016a2480808080000f0b200341586a210320072903202104200741286a21072004428080e983b1de165a0d000b200141dc006a42003702002001410136025420014188dac58000360250200141a8d9c58000360258200141d0006a4190dac5800010f680808000000b200141dc006a420037020020014101360254200141a0d9c58000360250200141a8d9c58000360258200141d0006a41a8d9c5800010f680808000000bd80501047f2380808080004180016b220124808080800020014298d5afd2c6aeacae2f370308200142c2cdc8b0c7b9e78f857f3703002001413c6a2001411041002802c0a1c680001185808080000002400240200128023c2202418080808078460d0020012802402103024020012802444110490d0020012003411010888e808000210402402002450d00200341002802c0a3c68000118080808000000b20040d010c020b2002450d00200341002802c0a3c68000118080808000000b02400240417f4100280284a4c680002202410347200241034b1b2202450d00200241ff017141ff01470d010b200141186a410c6a41fd83808000360200200141b8dbc58000360220200141838280800036021c20014108360214200141e0a6c480003602102001200141106a3602184100280290a1c680002102410028028ca1c6800021034100280280a4c680002104200141f4006a4202370200200141ec006a4102360200200141e4006a4116360200200141e0006a41d4d8c58000360200200141d4006a41b8d6c58000ad4280808080d00b843702002001413c6a410c6a41ead8c58000ad4280808080f00284370200200141f0006a200141186a360200200141a8dbc580003602682001410336025c200141003602502001410036024420014281808080a01837023c200341ecf2c08000200441024622041b2001413c6a200241d4f2c0800020041b280210118480808000000b2001413c6a41e0a6c48000410841002802a0a3c68000118580808000002001413c6a41106a220241bccdc48000411541002802a0a3c6800011858080800000200141186a41186a2001413c6a41186a290000370300200141186a41106a2002290000370300200141186a41086a2001413c6a41086a2900003703002001200129003c370318200141013b013c200141186a41202001413c6a410241002802e0a1c68000118680808000000b200042003703082000420037030020014180016a2480808080000b930201027f23808080800041106b2202248080808000200220003602002002200128021441dcf6c58000410e200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a200241ecf6c58000108d81808000210120022d000c210002400240200128020022030d00200041ff017141004721010c010b41012101200041ff01710d0020022802082100024020034101470d0020022d000d41ff0171450d0020002d001c4104710d00410121012000280214418ca5c080004101200041186a28020028020c118280808000000d010b2000280214418da5c080004101200041186a28020028020c1182808080000021010b200241106a24808080800020010ba70302027f037e23808080800041c0006b22052480808080002005200110fa87808000200541186a220629030021072005200110fa8780800042004200200541286a2903002208200541206a2903007d220920092008561b20041b2109200629030021080240024002400240200341ff01710e03010002030b2008500d022005200110fa8780800020052802344101470d020c010b2008500d012005200110fa878080002005280230450d01200528023441024f0d010b2009428080e983b1de162009428080e983b1de16561b21090b024002400240200720024200200820097d220920092008561b220820022008541b2208540d0020052001200720087d2208109c8c80800020052802000d0102402005290308500d00200541106a2903002209500d0020052009370300200510a88a8080000b20004200200720087d220820082007561b370308410021010c020b200041073b0104410121010c010b200541086a290300210820052802042101200041106a200541106a290300370300200041086a200837030020002001360204410121010b20002001360200200541c0006a2480808080000bd80703017f027e027f23808080800041e0016b22032480808080002003200237030020034190016a200110fa87808000420021040240200341a8016a2903002202500d0020034190016a200110fa878080004200428080e983b1de1620032802c40141014b1b420020032802c0011b21040b20034200200220047d220420042002561b370308200110a08c8080001a20034190016a41086a200341086a36020020032001360290012003200336029401200341d8006a200120034190016a10948a8080000240024002400240200329035822024202510d00200329037021052003290368210402402002500d0020032903602102200341b8016a200141186a290000370300200341b0016a200141106a29000037030020034190016a41186a200141086a2900003703002003200237039801200341003a009001200320012900003703a00120034190016a108e8a8080000b0240024020044200520d00200341286a410f6a220120034188016a280000360000200341286a41086a220620034181016a290000370300200341106a41086a2006290300370300200341106a410f6a20012800003600002003200329007937031020032d007821010c010b200341b8016a200141186a290000370300200341b0016a200141106a29000037030020034190016a41186a200141086a2900003703002003200537039801200341013a009001200320012900003703a00120034190016a108e8a808000200341286a41086a220620034181016a290000370300200341286a410f6a220720034188016a2800003600002003200329007937032820032d0078210120044202510d02200341106a410f6a2007280000360000200341106a41086a2006290300370300200320032903283703100b200341c0006a410f6a2206200341106a410f6a280000360000200341c0006a41086a2207200341106a41086a29030037030020032003290310370340200141ff0171410e470d022000200437030820004100360200200041106a20053703000c030b200341286a41086a200341e9006a290000370300200341376a200341f0006a2800003600002003200341e1006a29000037032820032d006021010b200341106a410f6a2206200341286a410f6a280000360000200341106a41086a2207200341286a41086a290300370300200320032903282202370310200041146a20062800003600002000410d6a2007290300370000200041056a2002370000200020013a0004200041013602000c010b200020013a000420004101360200200041056a2003290340370000200041146a20062800003600002000410d6a20072903003700000b200341e0016a2480808080000bf60704037f027e017f037e23808080800041e0006b220524808080800002400240024002402003500d0020054298d5afd2c6aeacae2f370318200542c2cdc8b0c7b9e78f857f370310200542e4c5bdb4b2e9a5a6807f370328200542d790d7a3fef9fda0c8003703202005200541106a10e688808000410821062005290308420020052802001b2003540d01200541106a200110fa8780800041072106200541286a220729030022082003540d01200541386a22062903002109200541306a220a290300210b200541106a200110fa8780800042002006290300220c200a2903007d220d200d200c561b210c02402007290300220d500d00200541106a200110fa87808000200c200c428080e983b1de16200c428080e983b1de16561b200528024441014b1b200c20052802401b210c0b41870a21064200200d200c7d220c200c200d561b2003540d010240200820037d220c42ffffe883b1de16560d00200541106a200110fa878080002005280240450d00418702210620052802444102490d020b41870a2106427f200c200b7c220d200d200c541b2009540d010240200441ff0171450d004187102106200c428080e983b1de16540d020b200541106a200210fa87808000024002400240200541286a290300220c20037c220d200c540d00200d428080e983b1de16540d01200541306a290300220c200d7c200c5a0d030b41012101410821020c010b41022101410721020b200041066a2005290110370100200041166a200541206a2f01003b01002000410e6a200541186a290100370100200041056a20013a0000200020023a00040c020b0240024020012002412010888e808000450d00200541106a2001200320044100109b8c80800020052802100d01200541106a200210fa878080000240427f200541106a41186a2206290300220c20037c220d200d200c541b220d200c510d00200d428080e983b1de16540d00200541106a2002200d109c8c80800020052802100d002005290318500d00200541206a290300220c500d002005200c370310200541106a10a88a8080000b200541386a200141186a290000370300200541306a200141106a2900003703002006200141086a290000370300200541c8006a200241086a290000370300200541d0006a200241106a290000370300200541d8006a200241186a29000037030020052003370318200541023a00102005200129000037032020052002290000370340200541106a108e8a8080000b20002003370308410021010c030b200541106a41106a2903002103200541106a41086a290300210c20002005280214360204200041106a2003370300200041086a200c3703000c010b200020063602040b410121010b20002001360200200541e0006a2480808080000be20602017f027e2380808080004190016b2206248080808000200641306a200110fa8780800042004200200641d8006a2903002207200641d0006a2903007d220820082007561b20051b2108200641c8006a29030021070240024002400240200341ff01710e03010002030b2007500d02200641306a200110fa8780800020062802644101470d020c010b2007500d01200641306a200110fa878080002006280260450d01200628026441024f0d010b2008428080e983b1de162008428080e983b1de16561b21080b0240024002404200200720087d220820082007561b220720025a0d0020040d00200041073b01040c010b20064298d5afd2c6aeacae2f370338200642c2cdc8b0c7b9e78f857f370330200642e4c5bdb4b2e9a5a6807f370348200642d790d7a3fef9fda0c800370340200641206a200641306a10e68880800002402006290328420020062802201b2007200220072002541b2207540d00200641306a2001200720032005109b8c808000200641306a41086a2903002107024020062802300d0020064298d5afd2c6aeacae2f370338200642c2cdc8b0c7b9e78f857f370330200642e4c5bdb4b2e9a5a6807f370348200642d790d7a3fef9fda0c800370340200641106a200641306a10e688808000200629031821082006280210210520064298d5afd2c6aeacae2f370338200642c2cdc8b0c7b9e78f857f370330200642e4c5bdb4b2e9a5a6807f370348200642d790d7a3fef9fda0c8003703402006200641306a10e68880800020064298d5afd2c6aeacae2f370338200642c2cdc8b0c7b9e78f857f370330200642e4c5bdb4b2e9a5a6807f370348200642d790d7a3fef9fda0c800370340200642002008420020051b220820077d220220022008561b3703880141002105200641306a412020064188016a410841002802e0a1c6800011868080800000200641d8006a200141186a290000370300200641306a41206a200141106a290000370300200641306a41186a200141086a290000370300200620073703382006410b3a003020062001290000370340200641306a108e8a808000200020073703080c030b200641306a41106a290300210820002006280234360204200041106a2008370300200041086a20073703000c010b20004188023b01040b410121050b2000200536020020064190016a2480808080000b890402017f037e2380808080004180016b22022480808080002002200137030002400240200150450d00420021010c010b200241306a200010fa87808000200241d0006a290300200241c8006a29030084500d00200010a08c8080001a200241086a20002000200210938a8080000240200229030822014202510d0020022903282103200229032021042002290318210502402001500d0020022903102101200241d8006a200041186a290000370300200241d0006a200041106a290000370300200241306a41186a200041086a29000037030020022001370338200241003a003020022000290000370340200241306a108e8a8080000b024020054200510d00200241d8006a200041186a290000370300200241d0006a200041106a290000370300200241306a41186a200041086a2900003703002002200437033820022000290000370340200241013a0030200241306a108e8a8080002005a74101470d00200442ffffe883b1de16200442ffffe883b1de16541b2201500d0020022001370330200241306a10a88a8080000b200241d8006a200041186a290000370300200241d0006a200041106a290000370300200241306a41186a200041086a290000370300200241053a00302002200029000037034020022003370338200241306a108e8a808000200229030020037d21010c010b200229030021010b20024180016a24808080800020010bf10803017f057e027f23808080800041c0016b220124808080800020012000360208200141206a200010fa878080000240200141306a29030022024200530d00200141c8006a2903002103200141386a2903002104200129032821050240200141c0006a2903002206500d0020034200520d00200141206a200010fa87808000024020012802540d0002400240417f4100280284a4c680002207410247200741024b1b2207450d00200741ff017141ff01470d010b200141fe838080003602742001200141086a3602704100280290a1c680002100410028028ca1c6800021074100280280a4c680002108200141d8006a4201370200200141d0006a4102360200200141c8006a4111360200200141c4006a4198dcc58000360200200141386a41b8d6c58000ad4280808080d00b843702002001412c6a41ead8c58000ad4280808080f00284370200200141d4006a200141f0006a36020020014188dcc5800036024c20014102360240200141003602342001410036022820014281808080c0eb00370220200741ecf2c08000200841024622081b200141206a200041d4f2c0800020081b28021011848080800000200128020821000b200020001084888080001a2004428080e983b1de162004428080e983b1de16561b2104200128020821000b2001410c6a200010f487808000024020012d000c410e460d00200141f0006a41106a2001410c6a41106a280200360200200141f0006a41086a2001410c6a41086a2902003703002001200129020c3703700240417f4100280284a4c680002200410147200041014b1b2200450d00200041ff017141ff01470d010b200141b0016a410c6a41ca83808000360200200141e0818080003602b4012001418c98c380003602b0012001200141f0006a3602b8014100280290a1c680002100410028028ca1c6800021074100280280a4c680002108200141d8006a4202370200200141d0006a4102360200200141c8006a4112360200200141c4006a419498c38000360200200141386a41a698c38000ad4280808080c00c84370200200141206a410c6a418a99c38000ad4280808080b00384370200200141d4006a200141b0016a3602002001418497c3800036024c20014101360240200141003602342001410036022820014281808080e024370220200741ecf2c08000200841024622081b200141206a200041d4f2c0800020081b280210118480808000000b200128020821000b200141f0006a200010fa87808000200141206a200010fa878080000240024020012802540d00200141206a200010fa8780800020012802580d0020001086888080000c010b200141306a2002428080808080808080807f8437030020012005370328200120033703482001200637034020012004370338200142013703202000200141206a1082888080000b200141396a2001280208220041186a290000370000200141316a200041106a290000370000200141296a200041086a2900003700002001410e3a002020012000290000370021200141206a108e8a8080000b200141c0016a2480808080002002427f550ba50e01047f23808080800041206b22012480808080002001410036021020014280808080800137020841002d00fca3c680001a024002400240024002400240024002400240410841002802c8a3c68000118180808000002202450d00200241a9dcc580003602002002412636020441002d00fca3c680001a410841002802c8a3c68000118180808000002203450d01200342003700000240200128021022042001280208470d00200141086a2004109d86808000200128021021040b200128020c200441e8006c6a220441013a00602004410136025c2004200236025820044288808080103703502004200336024c2004428d8080808001370244200441aebfc38000360240200441ef80808000360218200442a5e9e3ab9e929adc2c37030820044100360200200441106a4293888c8f89fdc6ec9e7f37030041002d00fca3c680001a2001200128021041016a360210410841002802c8a3c68000118180808000002202450d02200241cfdcc58000360200200241c20036020441002d00fca3c680001a410841002802c8a3c68000118180808000002203450d03200342003700000240200128021022042001280208470d00200141086a2004109d86808000200128021021040b200128020c200441e8006c6a220441013a00602004410136025c2004200236025820044288808080103703502004200336024c200442908080808001370244200441e8bec38000360240200441ef8080800036021820044100360200200442a5e9e3ab9e929adc2c370308200441106a4293888c8f89fdc6ec9e7f3703002001200128021041016a36021041002d00fca3c680001a41c00141002802c8a3c68000118180808000002204450d04200441fee1c580003602b801200441b4e1c580003602b001200441d9e0c580003602a80120044185e0c580003602a001200441a8d9c58000360298012004419fdfc58000360290012004419cdfc5800036028801200441e8dfc5800036028001200441eaddc58000360278200441ddddc58000360270200441a8d9c58000360268200441d3ddc58000360260200441a8d9c58000360258200441a3dfc58000360250200441a8d9c580003602482004419fdfc580003602402004419cdfc5800036023820044196dec58000360230200441eaddc58000360228200441ddddc58000360220200441a8d9c58000360218200441d3ddc58000360210200441a8d9c58000360208200441c20036020420044191ddc58000360200200441bc016a41d000360200200441b4016a41ca00360200200441ac016a41db00360200200441a4016a41d4003602002004419c016a410036020020044194016a41043602002004418c016a410336020020044184016a411d360200200441fc006a412c360200200441f4006a410d360200200441ec006a4100360200200441e4006a410a360200200441dc006a4100360200200441d4006a41c500360200200441cc006a4100360200200441c4006a41043602002004413c6a4103360200200441346a4186013602002004412c6a412c360200200441246a410d3602002004411c6a4100360200200441146a410a3602002004410c6a41003602002001411836021c2001200436021820014118360214200141146a200141086a10a08880800041002d00fca3c680001a412041002802c8a3c68000118180808000002204450d05200441c5e3c58000360218200441a8d9c58000360210200441fce2c580003602082004412e360204200441cee2c580003602002004411c6a41eb00360200200441146a41003602002004410c6a41c9003602002001410436021c2001200436021820014104360214200141146a200141086a109a8880800041002d00fca3c680001a411841002802c8a3c68000118180808000002204450d06200441d9e4c58000360210200441a8d9c5800036020820044129360204200441b0e4c58000360200200441146a41ec003602002004410c6a41003602002001410336021c2001200436021820014103360214200141146a200141086a10988880800041002d00fca3c680001a410841002802c8a3c68000118180808000002204450d072004411b360204200441c5e5c580003602002001410136021c2001200436021820014101360214200141146a200141086a10a18880800041002d00fca3c680001a410841002802c8a3c68000118180808000002204450d0820044122360204200441e0e5c580003602002001410136021c2001200436021820014101360214200141146a200141086a109c88808000200041086a200141086a41086a28020036020020002001290208370200200041106a4108360200200041e0a6c4800036020c200141206a2480808080000f0b4104410810b280808000000b4101410810b280808000000b4104410810b280808000000b4101410810b280808000000b410441c00110b280808000000b4104412010b280808000000b4104411810b280808000000b4104410810b280808000000b4104410810b280808000000bad0a01097f41002d00fca3c680001a02400240024002400240024002400240024041e00141002802c8a3c68000118180808000002201450d0041002d00fca3c680001a410841002802c8a3c68000118180808000002202450d012002428080e983b1de1637000041002d00fca3c680001a41c00041002802c8a3c68000118180808000002203450d0220034182e6c58000360200200341f9e8c58000360238200341a8d9c58000360230200341d6e8c5800036022820034180e8c58000360220200341a8e7c58000360218200341d2e6c58000360210200341a8d9c58000360208200341d0003602042003413c6a413c360200200341346a41003602002003412c6a4123360200200341246a41d6003602002003411c6a41d800360200200341146a41d6003602002003410c6a410036020041002d00fca3c680001a410441002802c8a3c68000118180808000002204450d032004413236000041002d00fca3c680001a412041002802c8a3c68000118180808000002205450d04200541c7e9c58000360200200541c5e3c58000360218200541a8d9c5800036021020054184eac580003602082005413d3602042005411c6a41eb00360200200541146a41003602002005410c6a413736020041002d00fca3c680001a410441002802c8a3c68000118180808000002206450d052006413236000041002d00fca3c680001a411841002802c8a3c68000118180808000002207450d06200741c3eac58000360200200741d9e4c58000360210200741a8d9c58000360208200741c300360204200741146a41ec003602002007410c6a410036020041002d00fca3c680001a410441002802c8a3c68000118180808000002208450d072008410036000041002d00fca3c680001a410841002802c8a3c68000118180808000002209450d08200941d80036020420094191ebc58000360200200141b8016a42e88488d0c0e3aebc13370300200141b0016a42d7c9cb8fc1cf97db3e37030020014180016a42e88488d0c0e3aebc13370300200141f8006a42d7c9cb8fc1cf97db3e370300200141c8006a42e88488d0c0e3aebc13370300200141c0006a42d7c9cb8fc1cf97db3e370300200141106a4293888c8f89fdc6ec9e7f370300200142a5e9e3ab9e929adc2c370308200141dc016a4101360200200141d8016a2009360200200141d0016a428480808010370200200141cc016a2008360200200141c8016a4104360200200141c0016a41b580808000360200200141ac016a410a360200200141e9ebc580003602a801200141a4016a4103360200200141a0016a200736020020014198016a42848080803037020020014194016a200636020020014190016a410436020020014188016a41b580808000360200200141f4006a410b36020020014186ebc58000360270200141ec006a4104360200200141e8006a2005360200200141e0006a4284808080c000370200200141dc006a2004360200200141d8006a4104360200200141d0006a41b5808080003602002001413c6a4108360200200141bbeac5800036023820014108360234200120033602302001428880808080013702282001200236022420014108360220200141ef8080800036021820014112360204200141b5e9c580003602002000410436020820002001360204200041043602000f0b410841e00110b280808000000b4101410810b280808000000b410441c00010b280808000000b4101410410b280808000000b4104412010b280808000000b4101410410b280808000000b4104411810b280808000000b4101410410b280808000000b4104410810b280808000000bad0302017f047e2380808080004180016b2203248080808000200110a08c8080001a200341086a20012001200210958a80800002400240200329030822044202510d0020032903282105200329032021062003290318210702402004500d0020032903102104200341d8006a200141186a290000370300200341d0006a200141106a290000370300200341306a41186a200141086a29000037030020032004370338200341003a003020032001290000370340200341306a108e8a8080000b024020074200510d00200341d8006a200141186a290000370300200341d0006a200141106a290000370300200341306a41186a200141086a2900003703002003200637033820032001290000370340200341013a0030200341306a108e8a8080002007a74101470d00200642ffffe883b1de16200642ffffe883b1de16541b2204500d0020032004370330200341306a10a88a8080000b20002005370308410021010c010b200341146a290200210420032802102101200041106a2003411c6a290200370300200041086a200437030020002001360204410121010b2000200136020020034180016a2480808080000beb0101037f23808080800041106b220224808080800002402001280200220328020020032802082204470d0020032004410110ab86808000200328020821040b200328020420046a41fb003a00002003200441016a3602082002200136020c20024180023b01080240200241086a41f3ebc580004108200010d98680800022030d002002280208220441ff01710d0020044180fe0371450d000240200228020c280200220428020020042802082201470d0020042001410110ab86808000200428020821010b200428020420016a41fd003a00002004200141016a3602080b200241106a24808080800020030b951103057f017e017f23808080800041c0016b22022480808080000240024002400240024002400240024002400240024002400240200128020022032802042204450d0020032004417f6a36020420032003280200220441016a36020020042d00000e0b010a02030405060a0708090a0b200041003a00000c0b0b02402001280200220328020422044120490d002003200441606a36020420032003280200220441206a360200200241a0016a41086a2203200441086a290000370300200241a0016a41106a2205200441106a290000370300200241a0016a41186a2206200441186a290000370300200220042900003703a001200241086a200110be8a8080002002290308a70d0020022903102107200020022903a001370010200041286a2006290300370000200041206a2005290300370000200041186a200329030037000020002007370308200041013a00000c0b0b200041003a00000c0a0b2001280200220328020422044120490d082003200441606a36020420032003280200220441206a36020020024180016a41086a200441086a29000037030020024180016a41106a200441106a29000037030020024180016a41186a200441186a290000370300200220042900003703800102402001280200220328020422044120490d002003200441606a36020420032003280200220441206a360200200241a0016a41086a200441086a290000370300200241a0016a41106a200441106a290000370300200241a0016a41186a200441186a290000370300200220042900003703a001200241186a200110be8a8080002002290318a70d00200229032021072000200229038001370010200020022903a001370030200041286a20024180016a41186a290300370000200041206a20024180016a41106a290300370000200041186a20024180016a41086a290300370000200041386a200241a0016a41086a290300370000200041c0006a200241a0016a41106a290300370000200041c8006a200241a0016a41186a29030037000020002007370308200041023a00000c0a0b200041003a00000c090b02402001280200220328020422044120490d002003200441606a36020420032003280200220441206a360200200241a0016a41086a2203200441086a290000370300200241a0016a41106a2205200441106a290000370300200241a0016a41186a2206200441186a290000370300200220042900003703a001200241286a200110be8a8080002002290328a70d0020022903302107200020022903a001370010200041286a2006290300370000200041206a2005290300370000200041186a200329030037000020002007370308200041033a00000c090b200041003a00000c080b02402001280200220328020422044120490d002003200441606a36020420032003280200220441206a360200200241a0016a41086a200441086a290000370300200241a0016a41106a200441106a290000370300200241a0016a41186a200441186a290000370300200220042900003703a001200128020022032802042201450d0020032001417f6a36020420032003280200220141016a3602004101410220012d000022034101461b410020031b22034102460d00200020022903a001370001200041196a200241b8016a290300370000200041116a200241b0016a290300370000200041096a200241a8016a290300370000200020033a0021200041043a00000c080b200041003a00000c070b02402001280200220328020422044120490d002003200441606a36020420032003280200220441206a360200200241a0016a41086a2205200441086a290000370300200241a0016a41106a2206200441106a290000370300200241a0016a41186a2208200441186a290000370300200220042900003703a0012001280200220328020422014108490d00200020022903a0013700102003200141786a36020420032003280200220141086a360200200041186a2005290300370000200041206a2006290300370000200041286a200829030037000020002001290000370308200041053a00000c070b200041003a00000c060b200241386a200110bc8a808000024020022802380d00200241f4006a2001200228023c10c8858080002002280274418080808078460d0020024180016a41086a200241f4006a41086a2802002203360200200241ab016a200336000020022002290274220737038001200220073700a301200020022900a001370001200041086a200241a7016a290000370000200041063a00000c060b200041003a00000c050b02402001280200220328020422044120490d002003200441606a36020420032003280200220441206a360200200241a0016a41086a2203200441086a290000370300200241a0016a41106a2205200441106a290000370300200241a0016a41186a2206200441186a290000370300200220042900003703a001200241c0006a200110be8a8080002002290340a70d0020022903482107200020022903a001370010200041286a2006290300370000200041206a2005290300370000200041186a200329030037000020002007370308200041073a00000c050b200041003a00000c040b0240200128020022032802042204450d0020032004417f6a36020420032003280200220441016a3602004101410220042d000022034101461b410020031b22034102460d00200241d0006a200110be8a8080002002290350a70d0020022903582107200020033a0001200041083a0000200020073703080c040b200041003a00000c030b200241e0006a200110be8a80800002402002290360a70d00200128020022032802042201450d002002290368210720032001417f6a36020420032003280200220141016a3602004101410220012d000022034101461b410020031b22034102460d00200020033a0001200041093a0000200020073703080c030b200041003a00000c020b200041003a00000c010b200041003a00000b200241c0016a2480808080000ba81002047f017e23808080800041106b2202248080808000024002400240024002400240024002400240024020002d0000417f6a0e09000102030405060708090b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41003a00002001200341016a2204360208200041106a21030240200128020020046b411f4b0d0020012004412010b182808000200128020821040b2001200441206a360208200128020420046a22042003290000370000200441086a200341086a290000370000200441106a200341106a290000370000200441186a200341186a2900003700002002200041086a36020c2002410c6a200110c18a8080000c080b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41023a00002001200341016a2204360208200041106a21030240200128020020046b411f4b0d0020012004412010b182808000200128020821040b200128020420046a22052003290000370000200541186a200341186a290000370000200541106a200341106a290000370000200541086a200341086a2900003700002001200441206a2204360208200041306a21030240200128020020046b411f4b0d0020012004412010b182808000200128020821040b2001200441206a360208200128020420046a22042003290000370000200441086a200341086a290000370000200441106a200341106a290000370000200441186a200341186a2900003700002002200041086a36020c2002410c6a200110c18a8080000c070b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41033a00002001200341016a2204360208200041106a21030240200128020020046b411f4b0d0020012004412010b182808000200128020821040b2001200441206a360208200128020420046a22042003290000370000200441086a200341086a290000370000200441106a200341106a290000370000200441186a200341186a2900003700002002200041086a36020c2002410c6a200110c18a8080000c060b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41043a00002001200341016a2204360208200041016a21030240200128020020046b411f4b0d0020012004412010b182808000200128020821040b200128020420046a22052003290000370000200541186a200341186a290000370000200541106a200341106a290000370000200541086a200341086a2900003700002001200441206a220336020820002d00212100024020012802002003470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a20003a00000c050b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41053a00002001200341016a2204360208200041106a21030240200128020020046b411f4b0d0020012004412010b182808000200128020821040b200128020420046a22052003290000370000200541186a200341186a290000370000200541106a200341106a290000370000200541086a200341086a2900003700002001200441206a2203360208200029030821060240200128020020036b41074b0d0020012003410810b182808000200128020821030b2001200341086a360208200128020420036a20063700000c040b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41063a00002001200341016a360208200041086a280200210320022000410c6a28020022003602082002200241086a36020c2002410c6a200110c08a8080002000450d032000410574210520012802082104034002400240200128020020046b411f4d0d00200421000c010b20012004412010b182808000200128020821000b2001200041206a2204360208200128020420006a22002003290000370000200041086a200341086a290000370000200041106a200341106a290000370000200041186a200341186a290000370000200341206a2103200541606a22050d000c040b0b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41083a00002001200341016a2204360208200041106a21030240200128020020046b411f4b0d0020012004412010b182808000200128020821040b2001200441206a360208200128020420046a22042003290000370000200441086a200341086a290000370000200441106a200341106a290000370000200441186a200341186a2900003700002002200041086a36020c2002410c6a200110c18a8080000c020b0240200128020020012802082203470d0020012003410110b182808000200128020821030b200128020420036a41093a00002001200341016a220336020820002d00012104024020012802002003470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a20043a00002002200041086a36020c2002410c6a200110c18a8080000c010b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a410a3a00002002200041086a36020c2002410c6a200110c18a80800020002d000121000240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a20003a00000b200241106a2480808080000bc30302017f017e41002101024002400240024002400240024002400240024020002d0000417f6a0e09000102030405060807090b412121012000290308220242c000540d0841222101200242808001540d08412421012002428080808004540d084129200279a74103766b21010c080b41c10021012000290308220242c000540d0741c2002101200242808001540d0741c40021012002428080808004540d0741c900200279a74103766b21010c070b412121012000290308220242c000540d0641222101200242808001540d06412421012002428080808004540d064129200279a74103766b21010c060b412121010c050b412821010c040b2000410c6a28020041057441047221010c030b412121012000290308220242c000540d0241222101200242808001540d02412421012002428080808004540d024129200279a74103766b21010c020b410221012000290308220242c000540d0141032101200242808001540d01410521012002428080808004540d01410a200279a74103766b21010c010b410221012000290308220242c000540d0041032101200242808001540d00410521012002428080808004540d00410a200279a74103766b21010b200141016a0b802b02037f017e0240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024020002d00000e16000102030405060708090a0b0c0d0e0f101112131415160b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41003a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41013a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41023a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2203360208200041306a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41033a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41043a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41053a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41063a00002001200241016a2203360208200041026a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2203360208200041226a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029034821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b200128020420026a20053700002001200241086a220236020820002d00012100024020012802002002470d0020012002410110b182808000200128020821020b2001200241016a360208200128020420026a20003a00000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41073a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41083a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41093a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a410a3a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a410b3a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a410c3a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a410d3a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a410e3a00002001200241016a2202360208200041016a21000240200128020020026b411f4b0d0020012002412010b182808000200128020821020b2001200241206a360208200128020420026a22012000290000370000200141086a200041086a290000370000200141106a200041106a290000370000200141186a200041186a2900003700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a410f3a00002001200241016a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41103a00002001200241016a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41113a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41123a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41133a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41143a00002001200241016a2203360208200041106a21020240200128020020036b411f4b0d0020012003412010b182808000200128020821030b200128020420036a22042002290000370000200441186a200241186a290000370000200441106a200241106a290000370000200441086a200241086a2900003700002001200341206a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000f0b0240200128020020012802082202470d0020012002410110b182808000200128020821020b200128020420026a41153a00002001200241016a2202360208200029030821050240200128020020026b41074b0d0020012002410810b182808000200128020821020b200128020420026a20053700002001200241086a2202360208200029031021050240200128020020026b41074b0d0020012002410810b182808000200128020821020b2001200241086a360208200128020420026a20053700000b0b140020002d000041027441a4fcc580006a2802000b970601037f0240024002400240024002400240024002400240024020012d0000417f6a0e09000102030405060708000b2000200129031037031020002001290308370308200041286a200141286a290300370300200041206a200141206a290300370300200041186a200141186a290300370300200041013a00000f0b2000200129031037031020002001290330370330200041286a200141286a290300370300200041206a200141206a290300370300200041186a200141186a290300370300200041386a200141386a290300370300200041c0006a200141c0006a290300370300200041c8006a200141c8006a29030037030020002001290308370308200041023a00000f0b2000200129031037031020002001290308370308200041286a200141286a290300370300200041206a200141206a290300370300200041186a200141186a290300370300200041033a00000f0b20002001290001370001200020012d00213a0021200041196a200141196a290000370000200041116a200141116a290000370000200041096a200141096a290000370000200041043a00000f0b2000200129031037031020002001290308370308200041286a200141286a290300370300200041206a200141206a290300370300200041186a200141186a290300370300200041053a00000f0b200141086a2802002102024002402001410c6a28020022010d0041012103410021040c010b200141ffffff1f4b0d0420014105742204417f4c0d0441002d00fca3c680001a200441002802c8a3c68000118180808000002203450d050b20032002200410848e80800021042000410c6a2001360200200041086a200436020020002001360204200041063a00000f0b2000200129031037031020002001290308370308200041286a200141286a290300370300200041206a200141206a290300370300200041186a200141186a290300370300200041073a00000f0b20002001290308370308200020012d00013a0001200041083a00000f0b20002001290308370308200020012d00013a0001200041093a00000f0b10ae80808000000b4101200410b280808000000ba60603057f017e027f23808080800041c0006b2201248080808000200141186a4184ecc58000410441ead8c58000411741a8d9c58000410010d882808000200141106a42043702002001420037020820014280808080800137020041002d00fca3c680001a02400240024041c00041002802c8a3c68000118180808000002202450d0020024188ecc580003602202002410036021820024101360204200241abd8c58000360200200241386a4100360200200241246a410136020020014102360238200120023602302001200241c0006a36023c200120023602342001200141306a10fa8680800041002d00fca3c680001a20012802002103200128020421022001280208210420012802182105200129021c2106410841002802c8a3c68000118180808000002207450d01200741002903d0ecc580003702002001410036020820014280808080c000370200200141306a20014195d7c58000411410968b8080002001200141306a41a9d7c58000410e109b8b808000200141306a200141b7d7c58000411310ca8b8080002001200141306a41cad7c58000410c10958b808000200141306a200141d6d7c58000410f10cf8b8080002001200141306a41e5d7c58000411010cd8b808000200141306a200141f5d7c58000411110858b8080002001200141306a4186d8c58000411b10808b808000200141246a200141a1d8c58000410410a78b8080002001200128022436020820012001280228220836020020012008200128022c41246c6a36020c20012008360204200141306a200110fb868080002005418080808078460d022001200336020820012002360200200120023602042001200220044105746a36020c200041c4006a200110fa868080002001410b6a200141306a41086a2802003600002000413c6a200637020020002005360238200041013a0000200041d8006a4101360200200041d4006a2007360200200041013602502001200129023037000320002001290000370001200041086a200141076a290000370000200141c0006a2480808080000f0b410841c00010b280808000000b4104410810b280808000000b41a8d8c480004111419cd9c4800010a181808000000bff1703067f017e037f23808080800041c0006b2201248080808000200141206a41d8ecc58000410541ead8c58000411741a8d9c58000410010d882808000200141186a42043702002001420037021020014280808080800137020841002d00fca3c680001a02400240024002400240024002400240024002400240024002400240024041c00041002802c8a3c68000118180808000002202450d0020024188ecc580003602202002410036021820024101360204200241abd8c58000360200200241386a4100360200200241246a410136020020014102360238200120023602302001200241c0006a36023c20012002360234200141086a200141306a10fa8680800041002d00fca3c680001a20012802082103200128020c2104200128021021052001280220210620012902242107410841002802c8a3c68000118180808000002208450d0120084100290380edc5800037020041002d00fca3c680001a41002802c8a3c6800021022001410036021020014280808080c00037020841082002118180808000002209450d02200941002903e893c58000370200200141086a410010a086808000200128020c200141086a41086a220a28020041246c6a220241003a00202002410e36021c20024188edc5800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141306a41086a200a28020041016a3602002001200129020837033041002d00fca3c680001a410841002802c8a3c68000118180808000002209450d03200941002903d097c580003702000240200128023822022001280230470d00200141306a200210a086808000200128023821020b2001280234200241246c6a220241013a00202002411536021c20024196edc5800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141086a41086a200141306a41086a28020041016a3602002001200129033037030841002d00fca3c680001a410841002802c8a3c68000118180808000002209450d04200941002903e896c580003702000240200128021022022001280208470d00200141086a200210a086808000200128021021020b200128020c200241246c6a220241023a00202002411336021c200241abedc5800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141306a41086a200141086a41086a28020041016a3602002001200129030837033041002d00fca3c680001a410841002802c8a3c68000118180808000002209450d052009410029038896c580003702000240200128023822022001280230470d00200141306a200210a086808000200128023821020b2001280234200241246c6a220241033a00202002411236021c200241b5e9c5800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141086a41086a200141306a41086a28020041016a3602002001200129033037030841002d00fca3c680001a410841002802c8a3c68000118180808000002209450d06200941002903c894c580003702000240200128021022022001280208470d00200141086a200210a086808000200128021021020b200128020c200241246c6a220241043a00202002410d36021c200241beedc5800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141306a41086a200141086a41086a28020041016a3602002001200129030837033041002d00fca3c680001a410841002802c8a3c68000118180808000002209450d072009410029039098c580003702000240200128023822022001280230470d00200141306a200210a086808000200128023821020b2001280234200241246c6a220241053a00202002411736021c200241cbedc5800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141086a41086a200141306a41086a28020041016a3602002001200129033037030841002d00fca3c680001a410841002802c8a3c68000118180808000002209450d08200941002903f894c580003702000240200128021022022001280208470d00200141086a200210a086808000200128021021020b200128020c200241246c6a220241063a00202002410b36021c200241e2edc5800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141306a41086a200141086a41086a28020041016a3602002001200129030837033041002d00fca3c680001a410841002802c8a3c68000118180808000002209450d09200941002903c096c580003702000240200128023822022001280230470d00200141306a200210a086808000200128023821020b2001280234200241246c6a220241073a00202002410f36021c200241ededc5800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141086a41086a200141306a41086a28020041016a3602002001200129033037030841002d00fca3c680001a410841002802c8a3c68000118180808000002209450d0a200941002903c095c580003702000240200128021022022001280208470d00200141086a200210a086808000200128021021020b200128020c200241246c6a220241083a00202002410c36021c200241fcedc5800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141306a41086a200141086a41086a28020041016a3602002001200129030837033041002d00fca3c680001a410841002802c8a3c68000118180808000002209450d0b2009410029039894c580003702000240200128023822022001280230470d00200141306a200210a086808000200128023821020b2001280234200241246c6a220241093a00202002410e36021c20024188eec5800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141086a41086a200141306a41086a28020041016a3602002001200129033037030841002d00fca3c680001a410841002802c8a3c68000118180808000002209450d0c200941002903d898c580003702000240200128021022022001280208470d00200141086a200210a086808000200128021021020b200128020c200241246c6a2202410a3a00202002411336021c20024196eec5800036021820024101360214200220093602102002428080808010370208200242808080808001370200200141306a41086a200141086a41086a28020041016a3602002001200129030837033041002d00fca3c680001a410841002802c8a3c68000118180808000002209450d0d2009410029039097c580003702000240200128023822022001280230470d00200141306a200210a086808000200128023821020b2001280234200241246c6a2202410b3a00202002410936021c200241a9eec58000360218200241013602142002200936021020024280808080103702082002428080808080013702002001280238210920012802342102200120012802303602102001200236020820012002200941246c6a41246a3602142001200236020c200141306a200141086a10fb868080002006418080808078460d0e20012003360210200120043602082001200436020c2001200420054105746a360214200041c4006a200141086a10fa86808000200141086a410b6a200141306a41086a2802003600002000413c6a200737020020002006360238200041013a0000200041d8006a4101360200200041d4006a2008360200200041013602502001200129023037000b20002001290008370001200041086a2001410f6a290000370000200141c0006a2480808080000f0b410841c00010b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b4104410810b280808000000b41a8d8c480004111419cd9c4800010a181808000000bba0803057f017e027f23808080800041c0006b2201248080808000200141186a41b2eec58000410541ead8c58000411741a8d9c58000410010d882808000200141106a42043702002001420037020820014280808080800137020041002d00fca3c680001a02400240024041c00041002802c8a3c68000118180808000002202450d0020024188ecc580003602202002410036021820024101360204200241abd8c58000360200200241386a4100360200200241246a410136020020014102360238200120023602302001200241c0006a36023c200120023602342001200141306a10fa8680800041002d00fca3c680001a20012802002103200128020421022001280208210420012802182105200129021c2106410841002802c8a3c68000118180808000002207450d01200741002903d8eec580003702002001410036020820014280808080c000370200200141306a200141e0eec58000410710b08b8080002001200141306a41e7eec580004108108e8b808000200141306a200141efeec58000410810ab8b8080002001200141306a41f7eec58000410a10b78b808000200141306a20014181efc58000410810998b8080002001200141306a4189efc58000410a10888b808000200141306a20014193efc58000411210d18b8080002001200141306a41a5efc580004107108b8b808000200141306a200141acefc58000410810b58b8080002001200141306a41b4efc58000410710c58b808000200141306a200141bbefc58000410610ae8b8080002001200141306a41c1efc58000410610848b808000200141306a200141c7efc58000410910bb8b8080002001200141306a41d0efc58000410810b98b808000200141306a200141d8efc580004108108f8b8080002001200141306a41e0efc58000410610978b808000200141306a200141e6efc58000410910ba8b8080002001200141306a41efefc58000410610c28b808000200141306a200141f5efc58000410810a38b8080002001200141306a41fdefc58000410610c78b808000200141306a20014183f0c580004106108c8b808000200141246a200141306a4189f0c58000411310ad8b8080002001200128022436020820012001280228220836020020012008200128022c41246c6a36020c20012008360204200141306a200110fb868080002005418080808078460d022001200336020820012002360200200120023602042001200220044105746a36020c200041c4006a200110fa868080002001410b6a200141306a41086a2802003600002000413c6a200637020020002005360238200041013a0000200041d8006a4101360200200041d4006a2007360200200041013602502001200129023037000320002001290000370001200041086a200141076a290000370000200141c0006a2480808080000f0b410841c00010b280808000000b4104410810b280808000000b41a8d8c480004111419cd9c4800010a181808000000bfa0302047f017e23808080800041f0006b220224808080800002400240024002400240024020012802042203450d0020012003417f6a36020420012001280200220441016a36020020042d0000417f6a0e03010203040b200042033703000c040b024020034105490d0020012003417b6a22053602042001200441056a36020020054108490d00200428000121052001200341736a36020420012004410d6a360200200429000521062002410f6a200110df8780800020022d000f0d00200041086a200241106a41e00010848e8080001a2000200536027020002006370368200042003703000c040b200042033703000c030b024020034105490d0020012003417b6a22053602042001200441056a36020020054108490d0020002004280001360210200042013703002001200341736a36020420012004410d6a360200200020042900053703080c030b200042033703000c020b024020034105490d0020012003417b6a22053602042001200441056a36020020054108490d00200428000121052001200341736a36020420012004410d6a360200200429000521062002410f6a200110df8780800020022d000f0d00200041086a200241106a41e00010848e8080001a2000200536027020002006370368200042023703000c020b200042033703000c010b200042033703000b200241f0006a2480808080000bb10302077f017e23808080800041106b2202248080808000200028020421032002200028020822043602082002200241086a36020c2002410c6a200110c08a8080000240024020040d00200128020821040c010b2003200441286c6a210520012802082104034002402001280200220620046b411f4b0d0020012004412010b18280800020012802002106200128020821040b2001280204220720046a22082003290000370000200841186a200341186a290000370000200841106a200341106a290000370000200841086a200341086a2900003700002001200441206a2208360208200341206a29030021090240200620086b41074b0d0020012008410810b18280800020012802042107200128020821080b2001200841086a2204360208200720086a2009370000200341286a22032005470d000b0b0240200128020020046b411f4b0d0020012004412010b182808000200128020821040b2001200441206a360208200128020420046a2203200029000c370000200341086a200041146a290000370000200341106a2000411c6a290000370000200341186a200041246a290000370000200241106a2480808080000b9c0202047f017e02402001280200220220012802082203470d0020012003410110b18280800020012802002102200128020821030b2001200341016a22043602082001280204220520036a41013a0000200029030021060240200220046b41074b0d0020012004410810b1828080002001280204210520012802002102200128020821040b2001200441086a2203360208200520046a2006370000200029030821060240200220036b41074b0d0020012003410810b18280800020012802042105200128020821030b200520036a20063700002001200341086a220436020820002d00102103024020012802002004470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a20033a00000b8b0201047f024020002d00000d000240200128020020012802082200470d0020012000410110b182808000200128020821000b200128020420006a41003a00002001200041016a3602080f0b02402001280200220220012802082203470d0020012003410110b18280800020012802002102200128020821030b2001200341016a22043602082001280204220520036a41013a0000200041016a21000240200220046b411f4b0d0020012004412010b18280800020012802042105200128020821040b200520046a22032000290000370000200341186a200041186a290000370000200341106a200041106a290000370000200341086a200041086a2900003700002001200441206a3602080b900602017e037f0240200029030022024203520d000240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a41003a00000f0b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a2204360208200128020420036a41013a00000240024002402002a70e03000102000b024020012802002004470d0020012004410110b182808000200128020821040b200128020420046a41013a00002001200441016a2204360208200041f0006a28020021030240200128020020046b41034b0d0020012004410410b182808000200128020821040b200041086a2105200128020420046a20033600002001200441046a2204360208200041e8006a29030021020240200128020020046b41074b0d0020012004410810b182808000200128020821040b2001200441086a360208200128020420046a20023700002005200110e0878080000f0b024020012802002004470d0020012004410110b182808000200128020821040b200128020420046a41023a00002001200441016a2204360208200041106a2802002103200029030821020240200128020020046b41034b0d0020012004410410b182808000200128020821040b200128020420046a20033600002001200441046a22003602080240200128020020006b41074b0d0020012000410810b182808000200128020821000b2001200041086a360208200128020420006a20023700000f0b024020012802002004470d0020012004410110b182808000200128020821040b200128020420046a41033a00002001200441016a2204360208200041f0006a28020021030240200128020020046b41034b0d0020012004410410b182808000200128020821040b200041086a2105200128020420046a20033600002001200441046a2204360208200041e8006a29030021020240200128020020046b41074b0d0020012004410810b182808000200128020821040b2001200441086a360208200128020420046a20023700002005200110e0878080000bfb0301087f23808080800041106b220224808080800020012d0000210341002d00fca3c680001a41002802c8a3c6800021040240024002400240024020034102460d0041022004118180808000002204450d02200441013a000020022004360208200241023602042002410136020c41002d00fca3c680001a412041002802c8a3c68000118180808000002204450d0320042001290001370000200441186a2205200141196a290000370000200441106a2206200141116a290000370000200441086a2207200141096a290000370000200241046a4101412010b18280800020022802082208200228020c22096a22012004290000370000200141086a2007290000370000200141106a2006290000370000200141186a20052900003700002002200941206a220136020c20022802042105200441002802c0a3c6800011808080800000024020052001470d00200241046a2005410110b18280800020022802082108200228020c21010b200820016a20033a00002002200141016a36020c0c010b41012004118180808000002201450d03200141003a000020022001360208200241013602042002410136020c0b20002002290204370200200041086a200241046a41086a280200360200200241106a2480808080000f0b4101410210b280808000000b4101412010b280808000000b4101410110b280808000000bbb0301057f23808080800041206b2202248080808000024002400240024002402001280200418080808078460d00024002402001280208220341056a2204450d002004417f4c0d044100210541002d00fca3c680001a200441002802c8a3c68000118180808000002206450d05200220063602102002200436020c0c010b20024100360214200242808080801037020c2002410c6a4100410110b18280800020022802102106200228021421050b200620056a41013a00002002200541016a36021420012802042104200220033602182002200241186a36021c2002411c6a2002410c6a10c08a8080000240200228020c200228021422016b20034f0d002002410c6a2001200310b182808000200228021421010b200228021020016a2004200310848e8080001a2002200120036a3602140c010b41002d00fca3c680001a410141002802c8a3c68000118180808000002201450d03200220013602102002410136020c200141003a0000200241013602140b2000200229020c370200200041086a2002410c6a41086a280200360200200241206a2480808080000f0b10ae80808000000b4101200410b280808000000b4101410110b280808000000bbf0201047f23808080800041206b2202248080808000024002400240024002402001280200418080808078460d0020012802082203417f4c0d0241002d00fca3c680001a2003410474410572220441002802c8a3c68000118180808000002205450d03200220053602102002200436020c200541013a00002002410136021420012802042101200220033602182002200241186a36021c2002411c6a2002410c6a10c08a808000200120032002410c6a10d4858080000c010b41002d00fca3c680001a410141002802c8a3c68000118180808000002201450d03200220013602102002410136020c200141003a0000200241013602140b2000200229020c370200200041086a2002410c6a41086a280200360200200241206a2480808080000f0b10ae80808000000b4101200410b280808000000b4101410110b280808000000bbb0301057f23808080800041206b2202248080808000024002400240024002402001280200418080808078460d00024002402001280208220341056a2204450d002004417f4c0d044100210541002d00fca3c680001a200441002802c8a3c68000118180808000002206450d05200220063602102002200436020c0c010b20024100360214200242808080801037020c2002410c6a4100410110b18280800020022802102106200228021421050b200620056a41013a00002002200541016a36021420012802042104200220033602182002200241186a36021c2002411c6a2002410c6a10c08a8080000240200228020c200228021422016b20034f0d002002410c6a2001200310b182808000200228021421010b200228021020016a2004200310848e8080001a2002200120036a3602140c010b41002d00fca3c680001a410141002802c8a3c68000118180808000002201450d03200220013602102002410136020c200141003a0000200241013602140b2000200229020c370200200041086a2002410c6a41086a280200360200200241206a2480808080000f0b10ae80808000000b4101200410b280808000000b4101410110b280808000000b21002001280214419cf0c580004114200141186a28020028020c118280808000000bc80201027f23808080800041106b220224808080800002400240200028020022002802000d00200128021441acd8c580004104200141186a28020028020c1182808080000021010c010b2002200041046a3602002002200128021441b0d8c580004104200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a200241d0f0c58000108d81808000210120022d000c21000240200128020022030d00200041ff017141004721010c010b41012101200041ff01710d0020022802082100024020034101470d0020022d000d41ff0171450d0020002d001c4104710d00410121012000280214418ca5c080004101200041186a28020028020c118280808000000d010b2000280214418da5c080004101200041186a28020028020c1182808080000021010b200241106a24808080800020010bc80201027f23808080800041106b220224808080800002400240200028020022032d00000d00200128021441acd8c580004104200141186a28020028020c1182808080000021000c010b410121002002200341016a3602002002200128021441b0d8c580004104200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a200241b0f0c58000108d81808000210120022d000c21030240200128020022010d00200341ff017141004721000c010b200341ff01710d0020022802082103024020014101470d0020022d000d41ff0171450d0020032d001c4104710d00410121002003280214418ca5c080004101200341186a28020028020c118280808000000d010b2003280214418da5c080004101200341186a28020028020c1182808080000021000b200241106a24808080800020000bc50201027f23808080800041106b220224808080800002400240200028020022002802000d00200128021441acd8c580004104200141186a28020028020c1182808080000021010c010b200220003602002002200128021441b0d8c580004104200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a200241c0f0c58000108d81808000210120022d000c21000240200128020022030d00200041ff017141004721010c010b41012101200041ff01710d0020022802082100024020034101470d0020022d000d41ff0171450d0020002d001c4104710d00410121012000280214418ca5c080004101200041186a28020028020c118280808000000d010b2000280214418da5c080004101200041186a28020028020c1182808080000021010b200241106a24808080800020010bd90703027f017e017f23808080800041a0016b22082480808080002008412c6a2006360200200841246a2004360200200841086a41086a200141086a28020022093602002008411c6a200241086a28020036020020082001290200220a37030820082007360230200820053602282008200336022020082002290200370214200828020c2101024002400240024002400240024002400240024002400240200aa70d00024020072802002202450d0020072802042107200841f0006a20043602002008200336026c20084184808080783602682002200841e8006a200728020c118480808000000b20090d01410121070c020b20094120470d07200841386a41186a2209200141186a290000370300200841386a41106a2202200141106a290000370300200841386a41086a220b200141086a29000037030020082001290000370338200841dc006a2005200841386a200841146a200628020c11868080800000200828025c2206418080808078460d022008280264210120082802602109024020072802002202450d002007280204210720084194016a200841d0006a2903003702002008418c016a200841386a41106a29030037020020084184016a200841c0006a290300370200200841e8006a41106a20043602002008200829033837027c20082003360274200820013602702008200936026c20084180808080783602682002200841e8006a200728020c118480808000000b20010d03410121070c040b2009417f4c0d0741002d00fca3c680001a200941002802c8a3c68000118180808000002207450d080b20072001200910848e80800021012000200936020820002001360204200020093602000c040b200841e8006a41186a2009290300370300200841e8006a41106a2002290300370300200841e8006a41086a200b2903003703002008200829033837036841002d00fca3c680001a413041002802c8a3c680001181808080000022010d024104413010b280808000000b2001417f4c0d0441002d00fca3c680001a200141002802c8a3c68000118180808000002207450d060b20072009200110848e80800021072000200136020820002007360204200020013602002006450d01200941002802c0a3c68000118080808000000c010b2001418580808078360200200120082903683702042001410c6a200841f0006a290300370200200141146a200841f8006a2903003702002001411c6a20084180016a2903003702002000418080808078360200200020013602040b200841a0016a2480808080000f0b4120200941a4f2c5800010a281808000000b10ae80808000000b4101200910b280808000000b4101200110b280808000000b8b0d03017f017e077f23808080800041a0016b22022480808080002001290200210320002802002204280200220128020021052001280204210620012802282100200241d8006a41286a22074100360200200241d8006a20052001200041284b22081b220520052006200020081b6a10f686808000200241086a41286a2007280200360200200241086a41206a200241d8006a41206a290200370300200241086a41186a200241d8006a41186a290200370300200241086a41106a200241d8006a41106a290200370300200241086a41086a200241d8006a41086a290200370300200220022902583703082002200128022c22013602340240200342ff0183500d002003420888a7210002400240024020014101710d00200241086a41047221012000410474210602400240200241086a410441282002280230220741284b22001b6a28020022052007412820001b460d002001200241086a41286a20001b21012002280208200241086a20001b21000c010b200241086a10f78d808000200228020c2105200228020821000b200020056a20063a00002001200128020041016a3602000c010b200228020c20022802302201200141284b22011b2205450d0120052002280208200241086a20011b6a417f6a220120012d00002000723a00000b2002200228023441016a3602340c010b419c94c68000413a41d894c6800010a181808000000b024002400240024002402003422088a722072802002200417e6a2201410420014106491b0e06040200040104040b200741286a21010c020b200741ec046a21010c010b200741046a21010b200241086a200110e18d80800020072802002200417e6a21010b02400240024002400240024002400240024002402001410420014106491b0e06050105030200050b2007410c6a2101200741046a21000c030b2007280234450d03200741346a21002007413c6a21010c020b2000450d022007280204450d02200741046a21002007410c6a21010c010b2007280204450d01200741086a2200280200450d01200741106a21010b200241386a41186a200141186a290200370300200241386a41106a200141106a290200370300200241386a41086a200141086a290200370300200220012902003703382002280208210620042802042802002108410121010240200228020c20022802302205200541284b22051b2209450d002009417f4c0d0241002d00fca3c680001a200941002802c8a3c68000118180808000002201450d030b20012006200241086a20051b200910848e808000210a200041046a2802002106200028020022052005280200220141016a3602002001417f4c0d032002200636025c2002200536025803402005280204210103402001417f460d012001417f4c0d062005200141016a2005280204220020002001461b360204200020014721062000210120060d000b0b200228025c21002002280258220120012802002201417f6a360200024020014101470d00200241d8006a10f18d8080000b0240200828020822012008280200470d0020082001109f86808000200828020821010b2008280204200141386c6a220141023a000c200120093602082001200a360204200120093602002001200229033837000d200120022f00583b002d2001200036023420012005360230200141156a200241c0006a2903003700002001411d6a200241c8006a290300370000200141256a200241d0006a2903003700002001412f6a200241d8006a41026a2d00003a00002008200828020841016a3602080b20042802042802002101200241d8006a41286a200241086a41286a290300370300200241d8006a41206a200241086a41206a290300370300200241d8006a41186a200241086a41186a290300370300200241d8006a41106a200241086a41106a290300370300200241d8006a41086a200241086a41086a29030037030020022002290308370358200220013602880141002105024002400240024002402007280200417e6a2206410420064106491b0e06040400010204040b200741046a2100410121050c030b200741306a21010c010b2007412c6a21010b41022105410021000b200220024188016a360290012002200241d8006a36028c012002200136029c012002200036029801200241003a009501200220053a00940120024194016a2002418c016a10808a80800002402002280280014129490d00200228025841002802c0a3c68000118080808000000b200241a0016a2480808080000f0b10ae80808000000b4101200910b280808000000b00000b10f08d808000000b8b0d03017f017e077f23808080800041a0016b22022480808080002001290200210320002802002204280200220128020021052001280204210620012802282100200241d8006a41286a22074100360200200241d8006a20052001200041284b22081b220520052006200020081b6a10f686808000200241086a41286a2007280200360200200241086a41206a200241d8006a41206a290200370300200241086a41186a200241d8006a41186a290200370300200241086a41106a200241d8006a41106a290200370300200241086a41086a200241d8006a41086a290200370300200220022902583703082002200128022c22013602340240200342ff0183500d002003420888a7210002400240024020014101710d00200241086a41047221012000410474210602400240200241086a410441282002280230220741284b22001b6a28020022052007412820001b460d002001200241086a41286a20001b21012002280208200241086a20001b21000c010b200241086a10f78d808000200228020c2105200228020821000b200020056a20063a00002001200128020041016a3602000c010b200228020c20022802302201200141284b22011b2205450d0120052002280208200241086a20011b6a417f6a220120012d00002000723a00000b2002200228023441016a3602340c010b419c94c68000413a41d894c6800010a181808000000b024002400240024002402003422088a722072802002200417e6a2201410420014106491b0e06040200040104040b200741286a21010c020b200741ec046a21010c010b200741046a21010b200241086a200110e18d80800020072802002200417e6a21010b02400240024002400240024002400240024002402001410420014106491b0e06050105030200050b2007410c6a2101200741046a21000c030b2007280234450d03200741346a21002007413c6a21010c020b2000450d022007280204450d02200741046a21002007410c6a21010c010b2007280204450d01200741086a2200280200450d01200741106a21010b200241386a41186a200141186a290200370300200241386a41106a200141106a290200370300200241386a41086a200141086a290200370300200220012902003703382002280208210620042802042802002108410121010240200228020c20022802302205200541284b22051b2209450d002009417f4c0d0241002d00fca3c680001a200941002802c8a3c68000118180808000002201450d030b20012006200241086a20051b200910848e808000210a200041046a2802002106200028020022052005280200220141016a3602002001417f4c0d032002200636025c2002200536025803402005280204210103402001417f460d012001417f4c0d062005200141016a2005280204220020002001461b360204200020014721062000210120060d000b0b200228025c21002002280258220120012802002201417f6a360200024020014101470d00200241d8006a10f18d8080000b0240200828020822012008280200470d0020082001109f86808000200828020821010b2008280204200141386c6a220141023a000c200120093602082001200a360204200120093602002001200229033837000d200120022f00583b002d2001200036023420012005360230200141156a200241c0006a2903003700002001411d6a200241c8006a290300370000200141256a200241d0006a2903003700002001412f6a200241d8006a41026a2d00003a00002008200828020841016a3602080b20042802042802002101200241d8006a41286a200241086a41286a290300370300200241d8006a41206a200241086a41206a290300370300200241d8006a41186a200241086a41186a290300370300200241d8006a41106a200241086a41106a290300370300200241d8006a41086a200241086a41086a29030037030020022002290308370358200220013602880141002105024002400240024002402007280200417e6a2206410420064106491b0e06040400010204040b200741046a2100410121050c030b200741306a21010c010b2007412c6a21010b41022105410021000b200220024188016a360290012002200241d8006a36028c012002200136029c012002200036029801200241003a009501200220053a00940120024194016a2002418c016a10ff8980800002402002280280014129490d00200228025841002802c0a3c68000118080808000000b200241a0016a2480808080000f0b10ae80808000000b4101200910b280808000000b00000b10f08d808000000b0c002000200110bf8c8080000bc90801087f2380808080004190076b2202248080808000200128020421032001280200280200220428020421052004280200210620012802082207280208200128020c2802006a2208410176210420072802042109200728020021070240024002400240024002400240024020084101710d00200420094b0d02200241003a00b805200220043602b405200220073602b0050c010b200420094b0d02200420094f0d03200220073602b005200220043602b405200241b9056a200720046a2d000041f001713a0000200241013a00b8050b200241c4036a20062003200241b0056a200528020c11868080800000024002400240024020022802c4032204418080808078470d00200241b0056a41086a2204200341086a290000370300200241b0056a41106a2207200341106a290000370300200241b0056a41186a2209200341186a290000370300200220032900003703b0052001280210280200210341002d00fca3c680001a413041002802c8a3c680001181808080000022010d014104413010b280808000000b200241b0056a20022802c803220720022802cc03220110c28c80800020022802b00522094105470d01200241e8016a410c6a200241b0056a410c6a290200370200200220022902b4053702ec010c020b200120022903b005370204200141858080807841848080807820031b3602002001410c6a2004290300370200200141146a20072903003702002001411c6a200929030037020020004108360200200020013602040c070b200241a8056a2208200241b0056a410c6a290200370300200220022902b4053703a005200241c4036a41146a200241b0056a41146a41c80110848e8080001a200241c4036a410c6a2008290300370200200220093602c403200220022903a0053702c803200241e8016a200241c4036a2007200110f48d80800020022802e8014105470d050b200241b0056a41086a2209200241f4016a290200370300200241b0056a41186a2208200341086a290000370300200241b0056a41206a2205200341106a290000370300200241b0056a41286a2206200341186a290000370300200220022902ec013703b005200220032900003703c00541002d00fca3c680001a413041002802c8a3c68000118180808000002201450d03200120022903b005370200200141286a2006290300370200200141206a2005290300370200200141186a2008290300370200200141106a200241b0056a41106a290300370200200141086a200929030037020020004108360200200020013602042004450d05200741002802c0a3c68000118080808000000c050b2004200941f492c68000109581808000000b20042009418493c68000109581808000000b20042009419493c6800010f980808000000b4104413010b280808000000b2002410c6a200241e8016a41dc0110848e8080001a20002002410c6a10b68a8080002004450d00200741002802c0a3c68000118080808000000b20024190076a2480808080000bff0401067f23808080800041306b22022480808080002001280204210320012802002204280200210520042802042104200241106a41086a22062001280208220141086a28020036020020022001290200370310200241046a20052003200241106a200428020c1186808080000002400240024002400240024020022802042205418080808078470d00200241106a41186a2201200341186a290000370300200241106a41106a2204200341106a2900003703002006200341086a2900003703002002200329000037031041002d00fca3c680001a413041002802c8a3c680001181808080000022030d014104413010b280808000000b200228020c2201417f4c0d02200141f5ffffff074f0d0320022802082106024002402001410b6a417c7122070d00410421040c010b41002d00fca3c680001a200741002802c8a3c68000118180808000002204450d050b2004428180808010370200200441086a2006200110848e8080001a02402005450d00200641002802c0a3c68000118080808000000b2000200136020820002004360204200041073602002000200329000037000c200041246a200341186a2900003700002000411c6a200341106a290000370000200041146a200341086a2900003700000c010b2003418580808078360200200320022903103702042003410c6a200241106a41086a290300370200200341146a20042903003702002003411c6a200129030037020020004108360200200020033602040b200241306a2480808080000f0b41bc96c68000412b200241106a41e896c6800041e897c68000108981808000000b41e484c08000412b200241106a419085c08000419086c08000108981808000000b4104200710b280808000000b02000be309020b7f017e23808080800041f0016b22032480808080002003410036022c2003200236022820032001360224200341306a200341246a10978780800002400240024002400240024002400240024020032d003022044105460d00200328023421052004417d6a210620040e050102030203010b2000428580808090808080807f3702000c070b200041003602000c060b20032d00312107200328022c210820054101712209450d04200820024f0d01200120086a2d00004110490d042000428580808080808080807f3702000c050b200328022c210420054101712208450d02200420024f0d01200120046a2d00004110490d022000428580808080808080807f3702000c040b2008200241d4f7c5800010f980808000000b2004200241f4f7c5800010f980808000000b024002400240024002402004200541016a4101766a2202200328022822014b0d002003200236022c20064102490d02200341186a200341246a10bb8a80800020032802180d0141002105200328022c2206200328021c6a220a20032802284d0d032000428580808090808080807f3702000c060b2000428580808090808080807f3702000c050b2000428580808090808080807f3702000c040b4101210520022106200241206a220a20014b0d010b2000200a36021820002006360214200020053602102000200836020c2000200236020820002004360204200041013602000c020b2000428580808090808080807f3702000c010b024002402008200541016a4101766a22052003280228220a4b0d00200541026a220b200a4d0d012000428580808090808080807f3702000c020b2000428580808090808080807f3702000c010b2003200b36022c0240024002402005417d4b0d00200b20024b0d01200120056a2f0000220c450d024102210d02400240024002402004410147200772410171450d000240024020064102490d00200341106a200341246a10bb8a80800020032802100d034100210d200328022c220b20032802146a220220032802284d0d012000428580808090808080807f3702000c090b4101210d200541226a2202200a4b0d030b2003200236022c2002ad422086200bad84210e0b200341023602e401200341023602d801200341023602cc01200341023602c001200341023602b401200341023602a8012003410236029c0120034102360290012003410236028401200341023602782003410236026c2003410236026020034102360254200341023602482003410236023c2003410236023041002101200341306a210203400240200c200141ffff037176410171450d00200341086a200341246a10bb8a80800020032802080d04200328022c2206200328020c220a6a220420032802284b0d04200241086a2004360200200241046a20063602002002200a4120473602002003200436022c0b2002410c6a2102200141016a22014110470d000b2000411c6a200341306a41c00110848e8080001a2000200e3702142000200d3602102000200936020c2000200536020820002008360204200041043602000c060b2000428580808090808080807f3702000c050b2000428580808090808080807f3702000c040b2000428580808090808080807f3702000c030b2005200b41e4f7c58000109681808000000b200b200241e4f7c58000109581808000000b2000428580808090808080807f3702000b200341f0016a2480808080000ba00701027f2380808080004180016b220b248080808000200b2008360214200b200736021002400240024020012802002208450d00200b2008360218200b2001280204220736021c02402009450d00200b41c8006a41086a2004360200200b200336024c200b4184808080783602482009200b41c8006a200a28020c118480808000000b20082008280200220941016a3602002009417f4c0d0120002007360204200020083602002000200141086a2201290200370208200041206a200141186a290200370200200041186a200141106a290200370200200041106a200141086a290200370200200b280218220020002802002200417f6a36020020004101470d02200b41186a10e28a8080000c020b200b41186a41186a2001411c6a2208290000370300200b41186a41106a200141146a2207290000370300200b41186a41086a2001410c6a220c290000370300200b2001290004370318200b41c8006a41186a2008290000370300200b41c8006a41106a2007290000370300200b41c8006a41086a200c290000370300200b2001290004370348200b2002360244200b200b41186a360240200b200b41106a36023c200b41086a2005200b41c8006a200b413c6a41b4f2c58000200628021411878080800000200b28020c21010240200b280208450d0020004100360200200020013602040c020b024002400240024002400240024020012802002207417e6a2208410420084106491b0e06050005020103050b20012802342208450d04200141346a21070c050b2007450d0320012802042208450d03200141046a21070c040b20012802040d010c020b200141046a2107200128020421080c020b200141086a220728020022080d010b41c8f2c5800041ec0041b4f3c5800010a181808000000b200741046a280200210120082008280200220741016a3602002007417f4c0d0002402009450d00200b41c8006a41106a2004360200200b41f4006a200b41306a290300370200200b41ec006a200b41186a41106a290300370200200b41e4006a200b41186a41086a290300370200200b200b29031837025c200b2003360254200b2001360250200b418080808078360248200b200841086a36024c2009200b41c8006a200a28020c118480808000000b2000200b2903183700082000200136020420002008360200200041206a200b41186a41186a290300370000200041186a200b41186a41106a290300370000200041106a200b41206a2903003700000c010b00000b200b4180016a2480808080000bff0401067f23808080800041306b22022480808080002001280204210320012802002204280200210520042802042104200241106a41086a22062001280208220141086a28020036020020022001290200370310200241046a20052003200241106a200428020c1186808080000002400240024002400240024020022802042205418080808078470d00200241106a41186a2201200341186a290000370300200241106a41106a2204200341106a2900003703002006200341086a2900003703002002200329000037031041002d00fca3c680001a413041002802c8a3c680001181808080000022030d014104413010b280808000000b200228020c2201417f4c0d02200141f5ffffff074f0d0320022802082106024002402001410b6a417c7122070d00410421040c010b41002d00fca3c680001a200741002802c8a3c68000118180808000002204450d050b2004428180808010370200200441086a2006200110848e8080001a02402005450d00200641002802c0a3c68000118080808000000b2000200136020820002004360204200041073602002000200329000037000c200041246a200341186a2900003700002000411c6a200341106a290000370000200041146a200341086a2900003700000c010b2003418580808078360200200320022903103702042003410c6a200241106a41086a290300370200200341146a20042903003702002003411c6a200129030037020020004108360200200020033602040b200241306a2480808080000f0b41bc96c68000412b200241106a41e896c6800041e897c68000108981808000000b41e484c08000412b200241106a419085c08000419086c08000108981808000000b4104200710b280808000000bc91e04047f017e0a7f027e23808080800041b0026b2206248080808000200641d0006a41086a200141086a22072802003602002006200129020037035020022802002108200228020421092006200336025c200641e0006a41086a2007280200220236020020062001290200220a370360200641f0006a41186a220b200341206a290000370300200641f0006a41106a220c200341186a290000370300200641f0006a41086a220d200341106a290000370300200620032900083703704100210e20064100360290012006280264220f4101742107200641e0016a41086a2110200aa72111200528021421120240024003402006200e36029401200641e0016a41186a200b290300370300200641e0016a41106a200c2903003703002010200d290300370300200620062903703703e001200620064194016a3602c801200620064190016a3602c4012006200641d0006a3602c0012006200641f0006a3602bc012006200641dc006a3602b801200641086a2004200641e0016a200641b8016a41b8f4c58000201211878080800000200628020c210320062802080d01200e41016a210e0240200628025c22012802302213450d00200141346a2802002101201041186a200b290300370000201041106a200c290300370000201041086a200d29030037000020102006290370370000200620033602e40120064181808080783602e0012013200641e0016a200128020c118480808000000b03400240024002400240024002400240024002400240024002400240024002402003280200417e6a2201410420014106491b0e06000203040501000b200628025c22032802302202450d07200341346a2802002103200641e8016a2009360200200620083602e40120064186808080783602e0012002200641e0016a200328020c118480808000000c070b200641ec016a4201370200200641013602e401200641f8f4c580003602e001200641e0818080003602bc01200641c4f6c580003602b8012006200641b8016a3602e801200641e0016a41ccf6c5800010f680808000000b0240200641e0006a200341046a10df8d8080000d00200628025c22032802302202450d06200341346a2802002103200641e8016a2009360200200620083602e40120064186808080783602e0012002200641e0016a200328020c118480808000000c060b0240024020032802342202450d002003280238210f20022002280200220141016a3602002001417f4c0d0b200641b0016a200341d4006a290000370300200641a8016a200341cc006a290000370300200641a0016a200341c4006a29000037030020062003413c6a290000370398010c010b200641a0016a200341c4006a290000370300200641a8016a200341cc006a290000370300200641b0016a200341d4006a28000036020020062003413c6a290000370398012003280038210f0b200628025c22032802002101200328020421072006290350210a20064188026a200f3602002006418c026a200629039801370200200641e0016a41346a20064198016a41086a2903003702002006419c026a20064198016a41106a290300370200200641a4026a20064198016a41186a290300370200200641e0016a41206a2007360200200641e0016a41186a2005360200200641e0016a41106a200936020020062002360284022006200341306a3602ac02200620013602fc01200620043602f401200620083602ec0141002102200641003a00e8012006200a3702e001200641b8016a20064184026a200641e0016a2008200920042005200120072003280230200341346a28020010c38c80800020062802b8012203450d03200641306a41086a200641b8016a41106a290200370300200641306a41106a200641b8016a41186a290200370300200641306a41186a200641b8016a41206a290200370300200620062902c00137033020062802bc01210f0c070b0240200641e0006a200341286a10de8d8080000d00200628025c22032802302202450d05200341346a2802002103200641e8016a2009360200200620083602e40120064186808080783602e0012002200641e0016a200328020c118480808000000c050b20062002200341d4006a28020022016a2202360268200620012006280290016a36029001200341046a21030c0a0b02400240024020072002460d0020024101762201200f4f0d012003201120016a2d00002201410f71200141047620024101711b41246c6a41306a22032d00004102470d02200628025c22032802302202450d06200341346a2802002103200641e8016a2009360200200620083602e40120064186808080783602e0012002200641e0016a200328020c118480808000000c060b02402003280204450d0002400240200341086a2802002202450d00200328020c210120022002280200220741016a3602002007417f4c0d0c200641b8016a41186a200341286a290000370300200641b8016a41106a200341206a290000370300200641c0016a200341186a2900003703002006200341106a2900003703b8010c010b200328000c2101200641b8016a41186a200341286a280000360200200641b8016a41106a200341206a290000370300200641b8016a41086a200341186a2900003703002006200341106a2900003703b8010b2006418c026a20062903b801370200200641a4026a200641b8016a41186a220f2903003702002006419c026a200641b8016a41106a2211290300370200200641e0016a41346a200641b8016a41086a29030037020020064188026a2001360200200641e0016a41186a2005360200200641e0016a41106a2009360200200641e0016a41206a200628025c220328020422013602002006200236028402200620043602f401200620083602ec0141002102200641003a00e801200620062903503702e0012006200341306a3602ac022006200328020022073602fc01200641b8016a20064184026a200641e0016a2008200920042005200720012003280230200341346a28020010c38c80800020062802b8012203450d04200641306a41086a2011290200370300200641306a41106a200f290200370300200641306a41186a200641b8016a41206a290200370300200620062902c00137033020062802bc01210f0c080b200628025c22032802302202450d06200341346a2802002103200641e8016a2009360200200620083602e40120064186808080783602e0012002200641e0016a200328020c118480808000000c060b2001200f41a8f4c5800010f980808000000b2006200241016a2202360268200620062802900141016a360290010c090b0240200641e0006a200341ec046a10de8d8080000d00200628025c22032802302202450d03200341346a2802002103200641e8016a2009360200200620083602e40120064186808080783602e0012002200641e0016a200328020c118480808000000c030b0240200720026b20034198056a2802002201460d00200120026a22144101762213200f4f0d0202402003201120136a2d00002213410f71201341047620144101711b41246c6a412c6a22032d00004102470d00200628025c22032802302202450d04200341346a2802002103200641e8016a2009360200200620083602e40120064186808080783602e0012002200641e0016a200328020c118480808000000c040b2006200141016a220120026a2202360268200620062802900120016a360290010c090b02402003280200450d000240024020032802042202450d002003280208210120022002280200220741016a3602002007417f4c0d09200641d0016a200341246a290000370300200641c8016a2003411c6a290000370300200641c0016a200341146a29000037030020062003410c6a2900003703b8010c010b20032800082101200641d0016a200341246a280000360200200641c8016a2003411c6a290000370300200641c0016a200341146a29000037030020062003410c6a2900003703b8010b2006418c026a20062903b801370200200641a4026a200641b8016a41186a220f2903003702002006419c026a200641b8016a41106a2211290300370200200641e0016a41346a200641b8016a41086a29030037020020064188026a2001360200200641e0016a41186a2005360200200641e0016a41106a2009360200200641e0016a41206a200628025c220328020422013602002006200236028402200620043602f401200620083602ec0141002102200641003a00e801200620062903503702e0012006200341306a3602ac022006200328020022073602fc01200641b8016a20064184026a200641e0016a2008200920042005200720012003280230200341346a28020010c38c80800020062802b8012203450d01200641306a41086a2011290200370300200641306a41106a200f290200370300200641306a41186a200641b8016a41206a290200370300200620062902c00137033020062802bc01210f0c050b200628025c22032802302202450d03200341346a2802002103200641e8016a2009360200200620083602e40120064186808080783602e0012002200641e0016a200328020c118480808000000c030b4101210220062802bc0121030c030b2013200f41a8f4c5800010f980808000000b41002102410021030c020b41002103410021020b20020d06200641106a41186a200641306a41186a290300370300200641106a41106a200641306a41106a290300370300200641106a41086a200641306a41086a29030037030020062006290330370310024020030d0041002102410021030c010b20032003280200220241016a3602002002417f4c0d01200641f9016a200641286a290300370000200641f1016a200641206a290300370000200641e9016a200641186a290300370000200620062903103700e1012006200f3602bc01200620033602b80103402003280204210203402002417f460d012002417f4c0d042003200241016a2003280204220120012002461b360204200120024721072001210220070d000b0b20062802bc01210220062802b801220120012802002201417f6a360200024020014101470d00200641b8016a10f18d8080000b20064188026a20023602002006200336028402410221020b200620023a00e001200420082009200641e0016a200528021011868080800000200041086a200f36020020002003360204200041003602000c060b00000b10f08d808000000b024020032d00000d00200341096a290000210a200341116a290000211520032900012116200b200341196a290000370300200c2015370300200d200a370300200620163703700c020b200328020421030c000b0b0b20004101360200200020033602040b200641b0026a2480808080000b832a05237f017e017f017e017f23808080800041d0076b2205248080808000200128022821062001410036022802400240024002400240024020060d00200541086a41306a2207200141306a290200370300200541086a41286a200141286a290200370300200541086a41206a2208200141206a290200370300200541086a41186a2206200141186a290200370300200541086a41106a2209200141106a290200370300200541086a41086a220a200141086a29020037030020052001290200370308200541c0006a41086a200441086a2201280200220b36020020052004290200370340200541d0006a41186a220c2008290300370300200541d0006a41106a220d2006290300370300200541d0006a41086a220e20092903003703002005200a2903003703502001280200210f200541d0006a4107722110200541f0056a41186a2111200541f0056a410c6a211220054190016a2113200541f4006a41046a210620054184046a41046a210920054184046a41146a2114200541f0056a41146a2115200541f0056a41046a2116200541f0056a41106a211720042802042118200428020021192005413c6a280200211a2007280200211b200528020c211c2005280208211d4100211e4100210a02400240024002400340200a200f6a220841017621010240024020084101710d00200120184b0d06200541003a00f805200520013602f405200520193602f0050c010b200120184b0d04200120184f0d03200541013a00f805200520193602f005200520013602f4052005201920016a2d000041f001713a00f9050b20054184046a201d200541d0006a200541f0056a201c28020c118680808000000240200528028404221f418080808078470d00200541f0056a41186a2208200541d0006a41186a290300370300200541f0056a41106a220b200541d0006a41106a290300370300200541f0056a41086a2204200541d0006a41086a290300370300200520052903503703f00541002d00fca3c680001a413041002802c8a3c680001181808080000022010d0a4104413010b280808000000b200528028c04210120052802880421200240201b450d0020172005290350370000201741186a200c290300370000201741106a200d290300370000201741086a200e290300370000200520013602fc05200520203602f8052005428280808088808080807f3702f005201b200541f0056a201a28020c118480808000000b200541f0056a2020200110c28c808000200541e0056a41086a2221201641086a2222290200370300200520162902003703e005024020052802f00522044105460d00201e41016a211e200528024422234101742124200528024021252020210803402014201541c80110848e8080001a200941086a2021290300370200200920052903e0053702002005200436028404200541f4006a20054184046a2008200110f48d808000200528027422264105460d08200541f8036a41086a2227200641086a280200360200200520062902003703f80320052802840121042005280288012108200528028c012101200541d0026a201341a80110848e8080001a20052902bc02212820052802b802212902400240024002400240024020260e050001020304000b0240201b450d00200528023c2101200541f8056a2003360200200520023602f40520054186808080783602f005201b200541f0056a200128020c118480808000000b20004180808080783602000c0e0b0240024020052802fc034101742005280280046b2024200b6b220b470d00200541f8036a200541c0006a10db8d808000200b460d010b0240201b450d00200528023c2101200541f8056a2003360200200520023602f40520054186808080783602f005201b200541f0056a200128020c118480808000000b20004180808080783602000c0e0b20054194066a201c3602002005418c066a200336020020054184066a41003a000020054180066a201836020020052007360298062005201d360290062005200236028806200520193602fc05200520013602f805200520083602f405200520043602f00520054184046a200541f0056a200541fc056a20022003201d201c200710bb8c8080000240200528028404418080808078460d002000200529028404370200200041086a20054184046a41086a2802003602000c0e0b200020052802880436020420004181808080783602000c0d0b20052802fc03212920052802800421270240200541c0006a200541f8036a10db8d8080002226202941017420276b460d000240201b450d00200528023c2101200541f8056a2003360200200520023602f40520054186808080783602f005201b200541f0056a200128020c118480808000000b20004180808080783602000c0d0b2005200b20266a220b3602480c020b200541f0056a41086a2027280200360200200520052903f8033703f00520052001360284062005200836028006200520043602fc052011200541d0026a41a80110848e8080001a0240024002402024200b460d00200b410176220120234f0d01200541f0056a202520016a2d00002201410f712001410476200b4101711b410c6c6a220828020022044102470d020240201b450d00200528023c21012005418c046a20033602002005200236028804200541868080807836028404201b20054184046a200128020c118480808000000b20004180808080783602000c0e0b024020294102460d00200541a8046a201c360200200541a0046a200336020020054198046a41003a000020054194046a2018360200200520073602ac042005201d3602a4042005200236029c04200520193602900420052028370288042005202936028404200541f4006a20054184046a20054190046a20022003201d201c200710bb8c80800002402005280274418080808078460d0020002005290274370200200041086a200541f4006a41086a2802003602000c0f0b2000200528027836020420004181808080783602000c0e0b0240201b450d00200528023c21012005418c046a20033602002005200236028804200541868080807836028404201b20054184046a200128020c118480808000000b20004180808080783602000c0d0b2001202341a8f4c5800010f980808000000b410121262005200b41016a220b36024820082802082101200828020421080c010b20052902c802212a20052802c402212b200528028004212620052802fc032127200520013602f805200520083602f405200520043602f0052012200541d0026a41a80110848e8080001a200520283702a807200520293602a4070240200541c0006a200541f8036a10db8d8080002201202741017420266b460d000240201b450d00200528023c21012005418c046a20033602002005200236028804200541868080807836028404201b20054184046a200128020c118480808000000b20004180808080783602000c0b0b024002402024200b6b2001460d00200b20016a2204410176220820234f0d010240200541f0056a202520086a2d00002208410f71200841047620044101711b410c6c6a220828020022044102470d000240201b450d00200528023c21012005418c046a20033602002005200236028804200541868080807836028404201b20054184046a200128020c118480808000000b20004180808080783602000c0d0b2005200b200141016a22266a220b36024820082802082101200828020421080c020b0240202b4102460d00200541a8046a201c360200200541a0046a200336020020054198046a41003a000020054194046a2018360200200520073602ac042005201d3602a4042005200236029c0420052019360290042005202a370288042005202b36028404200541f4006a20054184046a20054190046a20022003201d201c200710bb8c80800002402005280274418080808078460d0020002005290274370200200041086a200541f4006a41086a2802003602000c0d0b2000200528027836020420004181808080783602000c0c0b0240201b450d00200528023c21012005418c046a20033602002005200236028804200541868080807836028404201b20054184046a200128020c118480808000000b20004180808080783602000c0b0b2008202341a8f4c5800010f980808000000b2026200a6a210a024020040d0020014120470d04200841026a2d000021012008410f6a2900002128200841176a290000212a2008411f6a2d0000210420082f000021262008280003212120102008290007370000201041186a20043a0000201041106a202a370000201041086a202837000020052021360053200520013a0052200520263b0150201f450d03202041002802c0a3c68000118080808000000c030b200541f0056a2008200110c28c80800020212022290200370300200520162902003703e00520052802f00522044105470d000b0b0b200620052903e005370200200641086a200541e0056a41086a2903003702000c050b2005200141001096868080002005280200210b20052802042008200110848e808000210441002d00fca3c680001a0240413041002802c8a3c68000118180808000002208450d002008200b36020420084188808080783602002008200136020c2008200436020820002008360204200041818080807836020020082005290350370010200841186a200541d8006a290300370000200841206a200541e0006a290300370000200841286a200541d0006a41186a2903003700000c060b4104413010b280808000000b20012018419493c6800010f980808000000b20012018418493c68000109581808000000b2001201841f492c68000109581808000000b2001412c6a280200210a200541f0056a41306a200141306a2902002228370300200541f0056a41286a200141286a290200370300200541f0056a41206a200141206a290200370300200541f0056a41186a200141186a290200370300200541f0056a41106a200141106a290200370300200541f0056a41086a200141086a290200370300200520012902003703f0052005200336025420052002360250410121160240024002400240024002400240024002400240024002400240024002400240024002402028a72221450d000240202120022003200541a4066a2802002802101182808080000041ff01710e03010002010b410021160b200620022003200a28020c1182808080000022260d010b20054184046a2004200541d0006a200541f0056a2006200a10c58c80800020052802880421082005280284040d012005418c046a28020021090c090b418080808078210920262d00000e030c02010c0b20004181808080783602000c030b20262802242208417f460d01202641016a21292008280200210103402001450d022001417f4c0d042008200141016a2008280200220b200b20014622091b360200200b21012009450d000b201620262802282209412149720d0702402021450d00200541a4066a280200210120054184046a41106a2003360200200541b0046a202941186a290000370200200541a8046a202941106a290000370200200541a0046a202941086a29000037020020052002360290042005200936028c042005200841086a360288042005418080808078360284042005202929000037029804202120054184046a200128020c118480808000000b20052009360288042005200836028404200841086a21010c080b200541a0046a202641196a29000037020020054198046a202641116a29000037020020054190046a202641096a29000037020020054100360284042005202629000137028804200541003a001020052004290200370208200541f4006a20054184046a200541086a200220032006200a20052802f00520052802f4052021200541a4066a28020010c38c808000024020052802742208450d00200541d8026a220b200541f4006a41106a290200370300200541d0026a41106a2204200541f4006a41186a290200370300200541d0026a41186a222620054194016a2902003703002005200529027c3703d0022005280278210920082008280200220141016a3602002001417f4c0d042005419d046a202629030037000020054195046a20042903003700002005418d046a200b290300370000200520052903d00237008504200520093602782005200836027403402008280204210103402001417f460d012001417f4c0d072008200141016a2008280204220b200b2001461b360204200b2001472104200b210120040d000b0b200528027821012005280274220b200b280200220b417f6a3602000240200b4101470d00200541f4006a10f18d8080000b200541ac046a2001360200200520083602a804200541023a00840420062002200320054184046a200a280210118680808000000c070b200528027821012000418180808078360200200020013602040c100b20054184046a2004200541d0006a200541f0056a2006200a10c58c808000200528028804210802402005280284040d002005418c046a28020021090c050b20004181808080783602000b200020083602040c0e0b10f28d808000000b00000b10f08d808000000b20080d0041808080807821090c030b20052009360288042005200836028404200841086a210120090d004101210b410021090c010b2009417f4c0d0241002d00fca3c680001a200941002802c8a3c6800011818080800000220b450d030b200b2001200910848e808000210120082008280200220b417f6a3602000240200b4101470d0020054184046a10e28a8080000b2009ad4220862001ad8421280b20002028370204200020093602000c060b10ae80808000000b4101200910b280808000000b200541f0056a41086a2208200641086a290200370300200541f0056a41186a220b200541d0006a41086a290300370300200541f0056a41206a2204200541d0006a41106a290300370300200541f0056a41286a2209200541d0006a41186a2903003703002005200529035037038006200520062902003703f00541002d00fca3c680001a413041002802c8a3c68000118180808000002201450d01200120052903f005370200200141286a2009290300370200200141206a2004290300370200200141186a200b290300370200200141106a200541f0056a41106a290300370200200141086a20082903003702002000418180808078360200200020013602040b201f450d02202041002802c0a3c68000118080808000000c020b4104413010b280808000000b200120052903f0053702042001418580808078418480808078201e1b3602002001410c6a2004290300370200200141146a200b2903003702002001411c6a20082903003702002000418180808078360200200020013602040b200541d0076a2480808080000bcf0101037f23808080800041206b220224808080800002400240200128020022032802042204450d0020032004417f6a36020420032003280200220441016a36020002400240024020042d00000e020102000b20004181808080783602000c030b20004180808080783602000c020b200241086a200110bc8a80800020022802080d00200241146a2001200228020c10d08580800020022802142203418080808078460d0020002002290218370204200020033602000c010b20004181808080783602000b200241206a2480808080000bd80101027f23808080800041106b22022480808080000240024020002903004200520d000240200128020020012802082200470d0020012000410110b182808000200128020821000b2001200041016a360208200128020420006a41003a00000c010b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41013a00002002200041086a360208200241086a200110c18a8080002002200041106a36020c2002410c6a200110c18a8080000b200241106a2480808080000b9e0201037f23808080800041106b2202248080808000024002402000280200418080808078470d000240200128020020012802082200470d0020012000410110b182808000200128020821000b200128020420006a41003a0000200041016a21000c010b0240200128020020012802082203470d0020012003410110b182808000200128020821030b2001200341016a360208200128020420036a41013a0000200028020421042002200028020822003602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822036b20004f0d0020012003200010b182808000200128020821030b200128020420036a2004200010848e8080001a200320006a21000b20012000360208200241106a2480808080000b4d01017f23808080800041206b2203248080808000200341106a420037020020034101360208200341bcf7c5800036020420032003411c6a36020c200341046a41c4f7c5800010f680808000000b4d01017f23808080800041206b2204248080808000200441106a420037020020044101360208200441bcf7c5800036020420042004411c6a36020c200441046a4184f8c5800010f680808000000bcd0e01117f23808080800041f0006b22052480808080000240024002400240024002400240024020042802002206417f6a0e020200010b200241017641046a2207417f4c0d0241002d00fca3c680001a200741002802c8a3c68000118180808000002208450d03200541c0006a41086a22094100360200200520083602442005200736024020052002360210200541013b010c2005410c6a200541c0006a109887808000200541c0006a2001109a87808000200541086a2009280200360200200520052902403703000c060b200241017641046a2207417f4c0d0141002d00fca3c680001a200741002802c8a3c68000118180808000002208450d03200541c0006a41086a2209410036020020052008360244200520073602402005200236021020054181023b010c2005410c6a200541c0006a109887808000200541c0006a2001109a87808000200541086a2009280200360200200520052902403703000c050b200241017641046a2207417f4c0d0041002d00fca3c680001a200741002802c8a3c68000118180808000002208450d03200541c0006a41086a220941003602002005200836024420052007360240200541033a000c200520023602102005410c6a200541c0006a109887808000200541c0006a2001109a87808000200541086a2009280200360200200520052902403703000c040b10ae80808000000b4101200710b280808000000b4101200710b280808000000b4101200710b280808000000b2005280208220a21020240200a2005280200470d002005200a109c86808000200528020821020b200528020420026a41003a00002005200528020841016a2202360208024020022005280200470d0020052002109c86808000200528020821020b200528020420026a41003a00002005200528020841016a2202360208024002400240024020060e03000103000b200428020421012005200441086a280200220236020c20052005410c6a360240200541c0006a200510c08a80800002402005280200200528020822046b20024f0d0020052004200210ab86808000200528020821040b200528020420046a2001200210848e8080001a200420026a21020c010b200428020421010240200528020020026b200441086a28020022044f0d0020052002200410ab86808000200528020821020b200528020420026a2001200410848e8080001a200220046a21020b200520023602080b02400240024002400240200328020022022003280204220b470d0041002106410021030c010b2003280210210c20032802082104200328020c220841046a210d2005410c6a41086a21092005410c6a410172210e41002106200541c0006a410172220f411f6a2110200f41186a2111200f41106a21124101210102400240034020022d00002103200241023a000020034103460d0241002107024020034102460d00200f20022900013700002010200241206a2800003600002011200241196a2900003700002012200241116a290000370000200f41086a200241096a29000037000020082802042113200828020821142005200828020036023c20052014200828022c2215201541284b22151b36023820052013200d20151b360234200520043a0069200541013a0068200520033a0040200c41046a28020021032005200541346a3602642005410c6a200c2802002003200541c0006a200541346a4101200410d98580800020052d000c22034103460d022005280210211302400240024020030e03000103010b200541203602342005200541346a360240200541c0006a200510c08a80800002402005280200200528020822076b411f4b0d0020052007412010b182808000200528020821070b200528020420076a220320092900003700072003200e2f00003b0000200341026a200e41026a2d00003a0000200320133600032003410f6a200941086a290000370000200341176a200941106a2900003700002003411f6a200941186a2d00003a00002005200741206a3602080c010b200541c0006a41186a200941186a290000370300200541c0006a41106a200941106a290000370300200541c0006a41086a200941086a29000037030020052009290000370340201341214f0d062005201336026c2005200541ec006a360234200541346a200510c08a80800002402005280200200528020822036b20134f0d0020052003201310b182808000200528020821030b200528020420036a200541c0006a201310848e8080001a2005200320136a3602080b200121070b200441016a21042001410174210120072006722106200241246a2202200b470d000b20064180fe037141087621030c020b20064180fe037141087621030c010b20064180fe037141087621030b200a41026a2102200a417d4b0d012002200528020822044b0d022005280204200a6a220220033a0001200220063a000020002005290300370200200041086a200541086a280200360200200541f0006a2480808080000f0b2013412041a4f8c58000109581808000000b200a20024194f8c58000109681808000000b200220044194f8c58000109581808000000bcd0e01117f23808080800041f0006b22052480808080000240024002400240024002400240024020042802002206417f6a0e020200010b200241017641046a2207417f4c0d0241002d00fca3c680001a200741002802c8a3c68000118180808000002208450d03200541c0006a41086a22094100360200200520083602442005200736024020052002360210200541013b010c2005410c6a200541c0006a109887808000200541c0006a2001109a87808000200541086a2009280200360200200520052902403703000c060b200241017641046a2207417f4c0d0141002d00fca3c680001a200741002802c8a3c68000118180808000002208450d03200541c0006a41086a2209410036020020052008360244200520073602402005200236021020054181023b010c2005410c6a200541c0006a109887808000200541c0006a2001109a87808000200541086a2009280200360200200520052902403703000c050b200241017641046a2207417f4c0d0041002d00fca3c680001a200741002802c8a3c68000118180808000002208450d03200541c0006a41086a220941003602002005200836024420052007360240200541033a000c200520023602102005410c6a200541c0006a109887808000200541c0006a2001109a87808000200541086a2009280200360200200520052902403703000c040b10ae80808000000b4101200710b280808000000b4101200710b280808000000b4101200710b280808000000b2005280208220a21020240200a2005280200470d002005200a109c86808000200528020821020b200528020420026a41003a00002005200528020841016a2202360208024020022005280200470d0020052002109c86808000200528020821020b200528020420026a41003a00002005200528020841016a2202360208024002400240024020060e03000103000b200428020421012005200441086a280200220236020c20052005410c6a360240200541c0006a200510c08a80800002402005280200200528020822046b20024f0d0020052004200210ab86808000200528020821040b200528020420046a2001200210848e8080001a200420026a21020c010b200428020421010240200528020020026b200441086a28020022044f0d0020052002200410ab86808000200528020821020b200528020420026a2001200410848e8080001a200220046a21020b200520023602080b02400240024002400240200328020022022003280204220b470d0041002106410021030c010b2003280210210c20032802082104200328020c220841046a210d2005410c6a41086a21092005410c6a410172210e41002106200541c0006a410172220f411f6a2110200f41186a2111200f41106a21124101210102400240034020022d00002103200241023a000020034103460d0241002107024020034102460d00200f20022900013700002010200241206a2800003600002011200241196a2900003700002012200241116a290000370000200f41086a200241096a29000037000020082802042113200828020821142005200828020036023c20052014200828022c2215201541284b22151b36023820052013200d20151b360234200520043a0069200541013a0068200520033a0040200c41046a28020021032005200541346a3602642005410c6a200c2802002003200541c0006a200541346a4101200410db8580800020052d000c22034103460d022005280210211302400240024020030e03000103010b200541203602342005200541346a360240200541c0006a200510c08a80800002402005280200200528020822076b411f4b0d0020052007412010b182808000200528020821070b200528020420076a220320092900003700072003200e2f00003b0000200341026a200e41026a2d00003a0000200320133600032003410f6a200941086a290000370000200341176a200941106a2900003700002003411f6a200941186a2d00003a00002005200741206a3602080c010b200541c0006a41186a200941186a290000370300200541c0006a41106a200941106a290000370300200541c0006a41086a200941086a29000037030020052009290000370340201341214f0d062005201336026c2005200541ec006a360234200541346a200510c08a80800002402005280200200528020822036b20134f0d0020052003201310b182808000200528020821030b200528020420036a200541c0006a201310848e8080001a2005200320136a3602080b200121070b200441016a21042001410174210120072006722106200241246a2202200b470d000b20064180fe037141087621030c020b20064180fe037141087621030c010b20064180fe037141087621030b200a41026a2102200a417d4b0d012002200528020822044b0d022005280204200a6a220220033a0001200220063a000020002005290300370200200041086a200541086a280200360200200541f0006a2480808080000f0b2013412041a4f8c58000109581808000000b200a20024194f8c58000109681808000000b200220044194f8c58000109581808000000bbe0e01117f23808080800041f0006b22052480808080000240024002400240024002400240024020042802002206417f6a0e020200010b200241017641046a2207417f4c0d0241002d00fca3c680001a200741002802c8a3c68000118180808000002208450d03200541c0006a41086a22094100360200200520083602442005200736024020052002360210200541013b010c2005410c6a200541c0006a109887808000200541c0006a2001109a87808000200541086a2009280200360200200520052902403703000c060b200241017641046a2207417f4c0d0141002d00fca3c680001a200741002802c8a3c68000118180808000002208450d03200541c0006a41086a2209410036020020052008360244200520073602402005200236021020054181023b010c2005410c6a200541c0006a109887808000200541c0006a2001109a87808000200541086a2009280200360200200520052902403703000c050b200241017641046a2207417f4c0d0041002d00fca3c680001a200741002802c8a3c68000118180808000002208450d03200541c0006a41086a220941003602002005200836024420052007360240200541033a000c200520023602102005410c6a200541c0006a109887808000200541c0006a2001109a87808000200541086a2009280200360200200520052902403703000c040b10ae80808000000b4101200710b280808000000b4101200710b280808000000b4101200710b280808000000b2005280208220a21020240200a2005280200470d002005200a109c86808000200528020821020b200528020420026a41003a00002005200528020841016a2202360208024020022005280200470d0020052002109c86808000200528020821020b200528020420026a41003a00002005200528020841016a2202360208024002400240024020060e03000103000b200428020421012005200441086a280200220236020c20052005410c6a360240200541c0006a200510c08a80800002402005280200200528020822046b20024f0d0020052004200210ab86808000200528020821040b200528020420046a2001200210848e8080001a200420026a21020c010b200428020421010240200528020020026b200441086a28020022044f0d0020052002200410ab86808000200528020821020b200528020420026a2001200410848e8080001a200220046a21020b200520023602080b02400240024002400240200328020022022003280204220b470d0041002106410021030c010b2003280210210c20032802082104200328020c220841046a210d2005410c6a41086a21092005410c6a410172210e41002106200541c0006a410172220f411f6a2110200f41186a2111200f41106a21124101210102400240034020022d00002103200241023a000020034103460d0241002107024020034102460d00200f20022900013700002010200241206a2800003600002011200241196a2900003700002012200241116a290000370000200f41086a200241096a29000037000020082802042113200828020821142005200828020036023c20052014200828022c2215201541284b22151b36023820052013200d20151b360234200520043a0069200541013a0068200520033a00402005200541346a3602642005410c6a200c200541c0006a200541346a4101200410dc8580800020052d000c22034103460d022005280210211302400240024020030e03000103010b200541203602342005200541346a360240200541c0006a200510c08a80800002402005280200200528020822076b411f4b0d0020052007412010b182808000200528020821070b200528020420076a220320092900003700072003200e2f00003b0000200341026a200e41026a2d00003a0000200320133600032003410f6a200941086a290000370000200341176a200941106a2900003700002003411f6a200941186a2d00003a00002005200741206a3602080c010b200541c0006a41186a200941186a290000370300200541c0006a41106a200941106a290000370300200541c0006a41086a200941086a29000037030020052009290000370340201341214f0d062005201336026c2005200541ec006a360234200541346a200510c08a80800002402005280200200528020822036b20134f0d0020052003201310b182808000200528020821030b200528020420036a200541c0006a201310848e8080001a2005200320136a3602080b200121070b200441016a21042001410174210120072006722106200241246a2202200b470d000b20064180fe037141087621030c020b20064180fe037141087621030c010b20064180fe037141087621030b200a41026a2102200a417d4b0d012002200528020822044b0d022005280204200a6a220220033a0001200220063a000020002005290300370200200041086a200541086a280200360200200541f0006a2480808080000f0b2013412041a4f8c58000109581808000000b200a20024194f8c58000109681808000000b200220044194f8c58000109581808000000bbe0e01117f23808080800041f0006b22052480808080000240024002400240024002400240024020042802002206417f6a0e020200010b200241017641046a2207417f4c0d0241002d00fca3c680001a200741002802c8a3c68000118180808000002208450d03200541c0006a41086a22094100360200200520083602442005200736024020052002360210200541013b010c2005410c6a200541c0006a109887808000200541c0006a2001109a87808000200541086a2009280200360200200520052902403703000c060b200241017641046a2207417f4c0d0141002d00fca3c680001a200741002802c8a3c68000118180808000002208450d03200541c0006a41086a2209410036020020052008360244200520073602402005200236021020054181023b010c2005410c6a200541c0006a109887808000200541c0006a2001109a87808000200541086a2009280200360200200520052902403703000c050b200241017641046a2207417f4c0d0041002d00fca3c680001a200741002802c8a3c68000118180808000002208450d03200541c0006a41086a220941003602002005200836024420052007360240200541033a000c200520023602102005410c6a200541c0006a109887808000200541c0006a2001109a87808000200541086a2009280200360200200520052902403703000c040b10ae80808000000b4101200710b280808000000b4101200710b280808000000b4101200710b280808000000b2005280208220a21020240200a2005280200470d002005200a109c86808000200528020821020b200528020420026a41003a00002005200528020841016a2202360208024020022005280200470d0020052002109c86808000200528020821020b200528020420026a41003a00002005200528020841016a2202360208024002400240024020060e03000103000b200428020421012005200441086a280200220236020c20052005410c6a360240200541c0006a200510c08a80800002402005280200200528020822046b20024f0d0020052004200210ab86808000200528020821040b200528020420046a2001200210848e8080001a200420026a21020c010b200428020421010240200528020020026b200441086a28020022044f0d0020052002200410ab86808000200528020821020b200528020420026a2001200410848e8080001a200220046a21020b200520023602080b02400240024002400240200328020022022003280204220b470d0041002106410021030c010b2003280210210c20032802082104200328020c220841046a210d2005410c6a41086a21092005410c6a410172210e41002106200541c0006a410172220f411f6a2110200f41186a2111200f41106a21124101210102400240034020022d00002103200241023a000020034103460d0241002107024020034102460d00200f20022900013700002010200241206a2800003600002011200241196a2900003700002012200241116a290000370000200f41086a200241096a29000037000020082802042113200828020821142005200828020036023c20052014200828022c2215201541284b22151b36023820052013200d20151b360234200520043a0069200541013a0068200520033a00402005200541346a3602642005410c6a200c200541c0006a200541346a4101200410da8580800020052d000c22034103460d022005280210211302400240024020030e03000103010b200541203602342005200541346a360240200541c0006a200510c08a80800002402005280200200528020822076b411f4b0d0020052007412010b182808000200528020821070b200528020420076a220320092900003700072003200e2f00003b0000200341026a200e41026a2d00003a0000200320133600032003410f6a200941086a290000370000200341176a200941106a2900003700002003411f6a200941186a2d00003a00002005200741206a3602080c010b200541c0006a41186a200941186a290000370300200541c0006a41106a200941106a290000370300200541c0006a41086a200941086a29000037030020052009290000370340201341214f0d062005201336026c2005200541ec006a360234200541346a200510c08a80800002402005280200200528020822036b20134f0d0020052003201310b182808000200528020821030b200528020420036a200541c0006a201310848e8080001a2005200320136a3602080b200121070b200441016a21042001410174210120072006722106200241246a2202200b470d000b20064180fe037141087621030c020b20064180fe037141087621030c010b20064180fe037141087621030b200a41026a2102200a417d4b0d012002200528020822044b0d022005280204200a6a220220033a0001200220063a000020002005290300370200200041086a200541086a280200360200200541f0006a2480808080000f0b2013412041a4f8c58000109581808000000b200a20024194f8c58000109681808000000b200220044194f8c58000109581808000000bc30402047f017e23808080800041206b2204248080808000200241017641046a21050240024002400240024020032802000d002005417f4c0d0241002d00fca3c680001a200541002802c8a3c68000118180808000002206450d03200441146a41086a220741003602002004200636021820042005360214200441023a0000200420023602042004200441146a109887808000200441146a2001109a87808000200441086a2202200728020036020020042004290214370300200328020421012004200328020822053602102004200441106a360214200441146a200410c08a80800002402004280200200228020022036b20054f0d0020042003200510ab86808000200428020821030b200428020420036a2001200510848e8080001a2004200320056a3602080c010b2005417f4c0d0141002d00fca3c680001a200541002802c8a3c68000118180808000002206450d03200441146a41086a220741003602002004200636021820042005360214200441043a0000200420023602042004200441146a109887808000200441146a2001109a87808000200441086a200728020022053602002004200429021422083703002003280204210202402008a720056b200328020822034f0d0020042005200310ab86808000200428020821050b200428020420056a2002200310848e8080001a2004200520036a3602080b20002004290300370200200041086a200441086a280200360200200441206a2480808080000f0b10ae80808000000b4101200510b280808000000b4101200510b280808000000b930302037f017e23808080800041d0006b22022480808080002002200136020002404100280284a4c680004105470d002002419882808000360208200220023602044100280290a1c680002101410028028ca1c6800021034100280280a4c680002104200241c4006a42013702002002413c6a4101360200200241346a4115360200200241306a41dffbc58000360200200241246a41ecfac58000ad4280808080b00e84370200200241186a41f4fbc58000ad4280808080d00584370200200241c0006a200241046a360200200241e4fac580003602382002410536022c200241003602202002410036021420024281808080c01e37020c200341ecf2c08000200441024622041b2002410c6a200141d4f2c0800020041b28021011848080800000200228020021010b4101210342002105024002400240200128020041786a0e020001020b200129030821050c010b41002103420021050b200020033a0028200042043703202000420037031820004280808080c0003703102000427f37030820002005370300200241d0006a2480808080000be70201047f23808080800041c0006b2202248080808000200241186a41086a200141086a290200370300200220012902003703182002200241186a109e8780800020022802042101024002400240200228020822030d0002402002280200450d00200141002802c0a3c68000118080808000000b41002101410021040c010b20022002413f6a36021820012003200241186a10da8b8080002002280200210541002d00fca3c680001a41e40141002802c8a3c68000118180808000002204450d01200441003b01e20120044100360258200241003602102002200436020c2002410036021420022001200341146c6a36023820022005360234200220013602302002200136022c20024181808080783602202002410c6a200241186a200241146a10c18580800020002002280210360204200228020c2101200228021421040b2000200436020820002001360200200241c0006a2480808080000f0b410441e40110b280808000000ba90501077f024020002802002201450d00200028020421020240024020002802082203450d00410021000340024002402000450d002001210420022105200021010c010b4100210402402002450d0020022100024020024107712205450d0003402000417f6a210020012802e40121012005417f6a22050d000b0b20024108490d00034020012802e4012802e4012802e4012802e4012802e4012802e4012802e4012802e4012101200041786a22000d000b0b410021050b024002400240200520012f01e201490d00034020012802582200450d0220012f01e0012105200141002802c0a3c6800011808080800000200441016a210420002101200520002f01e2014f0d000b200021010b200541016a2102024020040d00200121000c020b200120024102746a41e4016a2802002100410021022004417f6a2206450d012004417e6a2107024020064107712204450d0003402006417f6a210620002802e40121002004417f6a22040d000b0b20074107490d01034020002802e4012802e4012802e4012802e4012802e4012802e4012802e4012802e4012100200641786a22060d000c020b0b200141002802c0a3c6800011808080800000418cd0c2800010a081808000000b024020012005410c6c6a41dc006a2201280200450d00200128020441002802c0a3c68000118080808000000b410021012003417f6a22030d000c020b0b024020020d00200121000c010b02400240200241077122050d0020012100200221010c010b200121002002210103402001417f6a210120002802e40121002005417f6a22050d000b0b20024108490d00034020002802e4012802e4012802e4012802e4012802e4012802e4012802e4012802e4012100200141786a22010d000b0b034020002802582101200041002802c0a3c68000118080808000002001210020010d000b0b0bcd0501097f23808080800041106b22022480808080002002200028020822033602082002200241086a36020c2002410c6a200110c08a808000024002402003450d0020002802002204450d0041002105200441004721062000280204210703400240024002402006450d002005450d010b20060d0141f887c6800010a081808000000b410121062004210502402007450d0020072100024020074107712204450d0003402000417f6a210020052802e40121052004417f6a22040d000b0b20074108490d00034020052802e4012802e4012802e4012802e4012802e4012802e4012802e4012802e4012105200041786a22000d000b0b41002104410021070b0240200720052f01e201490d00034020052802582200450d04200441016a210420052f01e001210720002105200720002f01e2014f0d000b0b200741016a21080240024020040d00200521000c010b200520084102746a41e4016a2802002100410021082004417f6a2209450d002004417e6a210a024020094107712204450d0003402009417f6a210920002802e40121002004417f6a22040d000b0b200a4107490d00034020002802e4012802e4012802e4012802e4012802e4012802e4012802e4012802e4012100200941786a22090d000b0b200520074103746a210420052007410c6c6a41dc006a210702402001280200200128020822056b41074b0d0020012005410810b182808000200128020821050b200128020420056a20042900003700002001200541086a360208200728020421042002200728020822053602082002200241086a36020c2002410c6a200110c08a80800002402001280200200128020822076b20054f0d0020012007200510b182808000200128020821070b200128020420076a2004200510848e8080001a2001200720056a3602084100210420082107200021052003417f6a22030d000b0b200241106a2480808080000f0b41e887c6800010a081808000000b0a00200010d68c8080000b850b04067f017e027f017e23808080800041d0006b2201248080808000200141286a419bfdc58000410641aafdc58000411b41fcfcc58000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d00200242a5e9e3ab9e929adc2c370308200241cbfdc58000360220200241ef8080800036021820024106360204200241c5fdc58000360200200241106a4293888c8f89fdc6ec9e7f370300200241386a4100360200200241246a410436020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a2001411c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c2002420c370224200241dad9c480003602202002410b36021c200241e6d9c48000360218200241f581808000360210200242ab8bffbed784ffa5937f370308200242c194a6a793ccc3a857370300200141c0006a41086a2209200828020041016a220236020020012001290210220a37034002402002200aa7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c2002420637022420024186dac480003602202002410636021c20024180dac48000360218200241bc82808000360210200242899ac8f29d8ce69ac300370308200242e78dcee4d0becc97573703002008200928020041016a220236020020012001290340220a37031002402002200aa7470d00200141106a200210a486808000200128021821020b2001280214200241386c6a2202420437022c2002420c370224200241dad9c480003602202002410a36021c200241d0d9c48000360218200241f581808000360210200242ab8bffbed784ffa5937f370308200242c194a6a793ccc3a857370300200141c0006a41086a2208200141106a41086a220928020041016a220236020020012001290310220a37034002402002200aa7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c2002420c370224200241dad9c480003602202002410f36021c200241f1d9c48000360218200241f581808000360210200242ab8bffbed784ffa5937f370308200242c194a6a793ccc3a8573703002009200828020041016a220236020020012001290340220a37031002402002200aa7470d00200141106a200210a486808000200128021821020b2001280214200241386c6a2202420437022c2002420637022420024192dac480003602202002410636021c2002418cdac48000360218200241ca8280800036021020024296b787c192cbb889d000370308200242fddcc0b9c5fc86d6a17f370300200128021821082001280214210220012001280210360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000b0a00200010f1878080000b0a00200010d98c8080000b8f0a04067f017e027f017e23808080800041d0006b2201248080808000200141286a41b087c68000411141c187c68000411241fcfcc58000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d00200242f68d80b7cfa6d3bb4e370308200241d387c680003602202002418083808000360218200241063602042002419bfdc58000360200200241306a42e9b494c69bdbc4d608370300200241286a42ead283debcdebd93d800370300200241106a42dc8ec6fd9fd6fcdeb77f370300200241386a41f780808000360200200241246a410236020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a2001411c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c20024202370224200241d0fbc480003602202002410836021c200241c7a4c58000360218200241f780808000360210200242e9b494c69bdbc4d608370308200242ead283debcdebd93d800370300200141c0006a41086a2209200828020041016a220236020020012001290210220a37034002402002200aa7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c20024204370224200241dfa4c580003602202002410436021c200241dba4c58000360218200241f580808000360210200242b8b6d386cdcbfab1a07f370308200242e3a4fae3cee3d18d723703002008200928020041016a220236020020012001290340220a37031002402002200aa7470d00200141106a200210a486808000200128021821020b2001280214200241386c6a2202420437022c20024206370224200241b2d9c480003602202002410c36021c200241cfa4c580003602182002418083808000360210200242dc8ec6fd9fd6fcdeb77f370308200242f68d80b7cfa6d3bb4e370300200141c0006a41086a200141106a41086a28020041016a220236020020012001290310220a37034002402002200aa7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c20024206370224200241b2d9c480003602202002410d36021c200241e3a4c580003602182002418083808000360210200242dc8ec6fd9fd6fcdeb77f370308200242f68d80b7cfa6d3bb4e370300200128024821082001280244210220012001280240360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000bcd0704067f017e017f017e23808080800041d0006b2201248080808000200141286a41fcfcc5800041054181fdc58000411a41fcfcc58000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d00200242f68d80b7cfa6d3bb4e370308200241a1fdc580003602202002418083808000360218200241063602042002419bfdc58000360200200241306a42dda1fc828d83b3faab7f370300200241286a42b7a18ef9ac95b6cbeb00370300200241106a42dc8ec6fd9fd6fcdeb77f370300200241386a41ec81808000360200200241246a410936020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a2001411c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c20024206370224200241b2d9c480003602202002410636021c200241acd9c480003602182002418083808000360210200242dc8ec6fd9fd6fcdeb77f370308200242f68d80b7cfa6d3bb4e370300200141c0006a41086a200828020041016a2202360200200120012902102209370340024020022009a7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c2002420e370224200241c2d9c480003602202002410a36021c200241b8d9c48000360218200241ff828080003602102002429acfecb0d2a8f28be7003703082002428bdb9ef4d7aab8cbec00370300200128024821082001280244210220012001280240360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000ba60703067f017e027f23808080800041d0006b2201248080808000200141286a41c0ffc58000411241d2ffc58000412810d682808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041800141002802c8a3c68000118180808000002202450d00200242c4ccab9c81ebb4b8db003703082002418e80c680003602602002418580c680003602402002418180c68000360220200241f08180800036021820024107360204200241faffc58000360200200241f0006a4283af8bf0ede78391a67f370300200241e8006a42dba4b9c0a2bed19501370300200241d0006a42f0a3fcacaeba9c956f370300200241c8006a42e3beec81d3e996af917f370300200241306a42fec1aad493d7e4ae3e370300200241286a42dbe2e9e8becbf3fb2f370300200241106a42a4d2928ecac0faa432370300200241f8006a419b83808000360200200241e4006a4105360200200241d8006a418583808000360200200241c4006a4109360200200241386a419a83808000360200200241246a41043602002001410436024820012002360240200120024180016a36024c20012002360244200141106a200141c0006a10fa86808000200141086a22032001411c6a220241086a28020036020020012002290200370300200128021021042001280214210520012802182106200129022c21072001280228210820014100360218200142808080808001370210200141106a410010a4868080002001280214200128021841386c6a2202410036023020024280808080c0003703282002410036022020024100360218200241ea81808000360210200242b891b68c98adebcf61370308200242e7b0a091f3ed9c85c500370300200128021821092001280214210220012001280210360248200120023602402001200236024420012002200941386c6a41386a36024c200141346a200141c0006a10f9868080002008418080808078460d012001200436021820012005360210200120053602142001200520064105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002008360238200041003a000020002001290300370350200041d8006a20032802003602002001200129023437001320002001290010370001200041086a200141106a41076a290000370000200141d0006a2480808080000f0b410841800110b280808000000b41a8d8c480004111419cd9c4800010a181808000000be40502037f027e2380808080004180016b2202248080808000024002400240024002402001280200220328020422044120490d002003200441606a36020420032003280200220441206a360200200241106a41086a200441086a290000370300200241106a41106a200441106a290000370300200241106a41186a200441186a290000370300200220042900003703102002200110be8a8080002002290300a70d012001280200220328020422044120490d02200229030821052003200441606a36020420032003280200220441206a360200200241306a41086a200441086a290000370300200241306a41106a200441106a290000370300200241306a41186a200441186a290000370300200220042900003703302001280200220328020422044120490d032003200441606a36020420032003280200220441206a360200200241d0006a41086a200441086a290000370300200241d0006a41106a200441106a290000370300200241d0006a41186a200441186a29000037030020022004290000370350200241f4006a2001109487808000024020022802742201418080808078460d00200229027821062000200229031037000020002002290330370028200041186a200241106a41186a290300370000200041106a200241106a41106a290300370000200041086a200241106a41086a290300370000200041306a200241306a41086a290300370000200041386a200241306a41106a290300370000200041c0006a200241306a41186a29030037000020002002290350370048200041d0006a200241d0006a41086a290300370000200041d8006a200241d0006a41106a290300370000200041e0006a200241d0006a41186a2903003700002000200637026c20002001360268200020053703200c050b20004180808080783602680c040b20004180808080783602680c030b20004180808080783602680c020b20004180808080783602680c010b20004180808080783602680b20024180016a2480808080000bde0601077f23808080800041106b220224808080800041002d00fca3c680001a024002400240412041002802c8a3c68000118180808000002203450d0020032000290000370000200341186a2204200041186a290000370000200341106a2205200041106a290000370000200341086a2206200041086a29000037000002402001280200200128020822076b411f4b0d0020012007412010b182808000200128020821070b200128020420076a22082003290000370000200841086a2006290000370000200841106a2005290000370000200841186a20042900003700002001200741206a360208200341002802c0a3c68000118080808000002002200041206a360204200241046a200110c18a80800041002d00fca3c680001a412041002802c8a3c68000118180808000002203450d0120032000290028370000200341186a2204200041c0006a290000370000200341106a2205200041386a290000370000200341086a2206200041306a29000037000002402001280200200128020822076b411f4b0d0020012007412010b182808000200128020821070b200128020420076a22082003290000370000200841086a2006290000370000200841106a2005290000370000200841186a20042900003700002001200741206a360208200341002802c0a3c680001180808080000041002d00fca3c680001a412041002802c8a3c68000118180808000002203450d0220032000290048370000200341186a2204200041e0006a290000370000200341106a2205200041d8006a290000370000200341086a2206200041d0006a29000037000002402001280200200128020822076b411f4b0d0020012007412010b182808000200128020821070b200128020420076a22082003290000370000200841086a2006290000370000200841106a2005290000370000200841186a20042900003700002001200741206a360208200341002802c0a3c6800011808080800000200041ec006a28020021032002200041f0006a28020022003602082002200241086a36020c2002410c6a200110c08a80800002402000450d00200041146c210003402003200110de8c808000200341146a21032000416c6a22000d000b0b200241106a2480808080000f0b4101412010b280808000000b4101412010b280808000000b4101412010b280808000000b9b0201037f23808080800041206b22022480808080000240024002400240024020002d000022030e050001020304000b2002200041016a36021420022000410c6a2902003702180c030b2002200041016a36021420022000410c6a2902003702180c020b2002200041016a36021420022000410c6a2902003702180c010b2002200041086a2902003702140b20022003360210200241046a200241106a10ed848080002002280208210402402001280200200128020822006b200228020c22034f0d0020012000200310b182808000200128020821000b200128020420006a2004200310848e8080001a2001200020036a36020802402002280204450d00200441002802c0a3c68000118080808000000b200241206a2480808080000be707010e7f23808080800041206b2204248080808000200441086a200110bd8a80800002400240024020042802080d0020012802042205200428020c2206490d000240024002400240024002400240024002400240024020060d00410121070c010b2006417f4c0d01200641002802c8a3c68000118180808000002207450d02200741002006108a8e8080001a0b200720012802002208200610848e80800021072001200520066b3602042001200820066a3602002004200110bd8a80800020042802000d072001280204220820042802042205490d070240024020050d00410121090c010b2005417f4c0d01200541002802c8a3c68000118180808000002209450d0c200941002005108a8e8080001a0b20092001280200220a200510848e808000210b2001200820056b22083602042001200a20056a220936020020084104490d0520012008417c6a220a3602042001200941046a360200200a4104490d042009280000210c2001200841786a220a3602042001200941086a360200200a4104490d032009280004210d2001200841746a36020420012009410c6a3602002009280008210e200441146a20011093878080002004280214220a418080808078460d02200428021c210f200428021821104101211102400240024020024101460d00200f410c6c21032010417c6a21020340024020030d0041002102410121110c030b200341746a2103200241046a21082002410c6a22092102200829020042dfd5adc696f381b09b7f520d000b200928020021030b024020034103490d00200128020422024104490d0220012002417c6a36020420012001280200220241046a360200200228000021110b4100210220034104490d0020012802042203450d0120012003417f6a36020420012001280200220341016a36020020032d000021020b200020023a0034200020113602302000200e36022c2000200d3602282000200c3602242000200f3602202000201036021c2000200a360218200020053602142000200b3602102000200536020c2000200636020820002007360204200020063602000c0b0b2000418180808078360200200a41808080807872418080808078460d06201041002802c0a3c68000118080808000000c060b10ae80808000000b4101200610b280808000000b20004181808080783602000c030b20004181808080783602000c020b20004181808080783602000c010b20004181808080783602000b2005450d01200b41002802c0a3c68000118080808000000c010b20004181808080783602000b2006450d01200741002802c0a3c68000118080808000000c010b20004181808080783602000b200441206a2480808080000f0b4101200510b280808000000bbb0401077f0240024002400240200128022022020d00410021020c010b20012002417f6a3602202001280204210202400240024020012802002203450d002002450d010b2003450d032001410c6a2802002104200141086a28020021050c010b200141086a280200210202402001410c6a2802002205450d0002400240200541077122040d00200521030c010b2005210303402003417f6a210320022802ac1421022004417f6a22040d000b0b20054108490d00034020022802ac142802ac142802ac142802ac142802ac142802ac142802ac142802ac142102200341786a22030d000b0b20014200370208200120023602042001410136020041002104410021050b02400240200420022f01aa144f0d00200221030c010b034020022802a0132203450d04200541016a210520022f01a814210420032102200420032f01aa144f0d000b0b200441016a21060240024020050d00200321020c010b200320064102746a41ac146a2802002102410021062005417f6a2207450d002005417e6a2108024020074107712205450d0003402007417f6a210720022802ac1421022005417f6a22050d000b0b20084107490d00034020022802ac142802ac142802ac142802ac142802ac142802ac142802ac142802ac142102200741786a22070d000b0b2001200636020c20014100360208200120023602042003200441e0016c6a210520032004410c6c6a41a4136a21020b20002005360204200020023602000f0b41d4fec5800010a081808000000b419cd0c2800010a081808000000bc10601077f0240200128022022020d00200128020021022001410036020002402002450d000240200128020422020d0020012802082102200128020c2203450d0002400240200341077122040d00200321050c010b2003210503402005417f6a210520022802900221022004417f6a22040d000b0b20034108490d000340200228029002280290022802900228029002280290022802900228029002280290022102200541786a22050d000b0b034020022802002105200241002802c0a3c68000118080808000002005210220050d000b0b20004180808080783602000f0b20012002417f6a360220200128020421020240024002400240024020012802002205450d002002450d010b2005450d022001410c6a2802002104200141086a28020021030c010b200141086a280200210202402001410c6a2802002203450d0002400240200341077122040d00200321050c010b2003210503402005417f6a210520022802900221022004417f6a22040d000b0b20034108490d000340200228029002280290022802900228029002280290022802900228029002280290022102200541786a22050d000b0b20014200370208200120023602042001410136020041002104410021030b0240200420022f018e024f0d00200221050c020b0240034020022802002205450d0120022f018c022104200241002802c0a3c6800011808080800000200341016a210320052102200420052f018e02490d030c000b0b200241002802c0a3c6800011808080800000418cd0c2800010a081808000000b41d887c6800010a081808000000b200441016a21060240024020030d00200521020c010b200520064102746a4190026a2802002102410021062003417f6a2207450d002003417e6a2108024020074107712203450d0003402007417f6a210720022802900221022003417f6a22030d000b0b20084107490d000340200228029002280290022802900228029002280290022802900228029002280290022102200741786a22070d000b0b2001200636020c2001410036020820012002360204200020052004410c6c6a220241046a290200370200200041086a2002410c6a280200360200200020024188016a29020037020c200041146a20024190016a2802003602000b900601077f0240200128022022020d00200128020021022001410036020002402002450d000240200128020422020d0020012802082102200128020c2203450d0002400240200341077122040d00200321050c010b2003210503402005417f6a210520022802f80621022004417f6a22040d000b0b20034108490d00034020022802f8062802f8062802f8062802f8062802f8062802f8062802f8062802f8062102200541786a22050d000b0b034020022802f0062105200241002802c0a3c68000118080808000002005210220050d000b0b200041023a004c0f0b20012002417f6a360220200128020421020240024002400240024020012802002205450d002002450d010b2005450d022001410c6a2802002104200141086a28020021030c010b200141086a280200210202402001410c6a2802002203450d0002400240200341077122040d00200321050c010b2003210503402005417f6a210520022802f80621022004417f6a22040d000b0b20034108490d00034020022802f8062802f8062802f8062802f8062802f8062802f8062802f8062802f8062102200541786a22050d000b0b20014200370208200120023602042001410136020041002104410021030b0240200420022f01f6064f0d00200221050c020b0240034020022802f0062205450d0120022f01f4062104200241002802c0a3c6800011808080800000200341016a210320052102200420052f01f606490d030c000b0b200241002802c0a3c6800011808080800000418cd0c2800010a081808000000b41d887c6800010a081808000000b200441016a21060240024020030d00200521020c010b200520064102746a41f8066a2802002102410021062003417f6a2207450d002003417e6a2108024020074107712203450d0003402007417f6a210720022802f80621022003417f6a22030d000b0b20084107490d00034020022802f8062802f8062802f8062802f8062802f8062802f8062802f8062802f8062102200741786a22070d000b0b2001200636020c200141003602082001200236020420002005200441d0006c6a41d00010848e8080001a0bb40101017f23808080800041206b2204248080808000200441086a20012802042002200310e48c8080000240024002402004280208418080808078470d00200441146a2001280200200210e58c8080002004280214418180808078460d0120002004290214370200200041086a200441146a41086a2802003602000c020b20002004290208370200200041086a200441086a41086a2802003602000c010b20004180808080783602000b200441206a2480808080000bd807010a7f23808080800041106b220424808080800002400240024002400240024002400240024002402002200141186a412010888e808000450d00200328020021050240024002402003280204220641216a22070d0020044201370208200420073602040c010b2007417f4c0d084100210841002d00fca3c680001a200741002802c8a3c68000118180808000002209450d092004410036020c20042009360208200420073602042006415f490d010b200441046a4100200610ab868080002004280204210720042802082109200428020c21080b200920086a2005200610848e8080001a2004200820066a220636020c024020032d0008450d00200341096a2d00002103024020062007470d00200441046a2007109c8680800020042802082109200428020c21060b200920066a20033a00002004200428020c41016a220636020c200428020421070b0240200720066b411f4b0d00200441046a2006412010ab8680800020042802042107200428020c21060b2004280208220a20066a22032002290000370000200341086a200241086a290000370000200341106a200241106a290000370000200341186a200241186a290000370000200128020c220b450d02200641206a2106200141106a280200210c0340200b41746a2108200b41b4016a2102200b2f01ba02220d410c6c2101417f210902400340024020010d00200d21090c020b200241086a2103200241046a2105200141746a2101200841106a2108200941016a21092002410c6a2102417f200a200528020020062003280200220320062003491b10888e8080002205200620036b20051b220341004720034100481b22034101460d000b200341ff0171450d030b200c450d03200c417f6a210c200b20094102746a41bc026a280200210b0c000b0b2001280204210602400240200128020822020d00410121010c010b2002417f4c0d0641002d00fca3c680001a200241002802c8a3c68000118180808000002201450d080b20012006200210848e80800021012000200236020820002001360204200020023602000c040b200841086a28020041004a0d010b20004180808080783602000c010b2008280200210102400240200841046a28020022020d00410121060c010b2002417f4c0d0341002d00fca3c680001a200241002802c8a3c68000118180808000002206450d060b20062001200210848e80800021012000200236020820002001360204200020023602000b2007450d00200a41002802c0a3c68000118080808000000b200441106a2480808080000f0b10ae80808000000b4101200710b280808000000b4101200210b280808000000b4101200210b280808000000bd70301087f0240024002400240024002402002200141186a412010888e808000450d0041808080807821030240200128020c22040d000c030b200141106a28020021050340200441dc026a210620042f01960422074105742101417f21082004210902400340024020010d00200721080c020b20022009412010888e808000210a200141606a2101200641106a2106200841016a2108200941206a2109417f200a410047200a4100481b220a4101460d000b200a41ff0171450d030b024020050d000c040b2005417f6a2105200420084102746a4198046a28020021040c000b0b200128020421090240200128020822030d004101210141012009200310848e8080001a0c020b2003417f4c0d0241002d00fca3c680001a200341002802c8a3c68000118180808000002201450d0320012009200310848e8080001a0c010b410121010240200628020041014e0d000c010b200641786a280200210902402006417c6a2802002203450d002003417f4c0d0241002d00fca3c680001a200341002802c8a3c68000118180808000002201450d040b20012009200310848e8080001a0b2000200336020820002001360204200020033602000f0b10ae80808000000b4101200310b280808000000b4101200310b280808000000b15002000200128020420022003200410e78c8080000bec0301037f23808080800041306b220524808080800002400240024002400240024002402004450d002004417f4c0d0241002d00fca3c680001a200441002802c8a3c68000118180808000002206450d0320062003200410848e80800021060240024020012802082004460d00200641002802c0a3c68000118080808000000c010b20062001280204200410888e8080002107200641002802c0a3c68000118080808000002007450d020b200541046a200320044100280298a3c680001185808080000041002d00fca3c680001a200441002802c8a3c680001181808080000022060d054101200410b280808000000b20012802080d030b20002001290018370000200041186a200141306a290000370000200041106a200141286a290000370000200041086a200141206a2900003700000c040b10ae80808000000b4101200410b280808000000b200541046a200341004100280298a3c6800011858080800000410121060b20062003200410848e80800021032005200436022c20052003360228200520043602242001200541046a2002200541246a10eb8c808000200041186a200541046a41186a290000370000200041106a200541046a41106a290000370000200041086a200541046a41086a290000370000200020052900043700000b200541306a2480808080000b110020002802042001200210e98c8080000b890904087f017e027f017e23808080800041e0006b220324808080800002402001200041186a412010888e808000450d002002280200210402400240024002400240024002402002280204220541216a22060d002003420137022c200320063602280c010b2006417f4c0d024100210741002d00fca3c680001a200641002802c8a3c68000118180808000002208450d03200341003602302003200836022c200320063602282005415f490d010b200341286a4100200510ab8680800020032802282106200328022c2108200328023021070b200820076a2004200510848e8080001a2003200720056a2205360230024020022d0008450d00200241096a2d00002102024020052006470d00200341286a2006109c86808000200328022c2108200328023021050b200820056a20023a00002003200328023041016a2205360230200328022821060b0240200620056b411f4b0d00200341286a2005412010ab8680800020032802282106200328023021050b200328022c220920056a22022001290000370000200241086a200141086a290000370000200241106a200141106a290000370000200241186a200141186a290000370000200541206a210202400240200028020c220a0d002002ad4220862009ad84210b4100210a0c010b200041106a280200210c02400340200a41b4016a2101200a2f01ba02220d410c6c2105417f2107024002400340024020050d00200d21070c020b200141086a2108200141046a2104200541746a2105200741016a21072001410c6a2101417f2009200428020020022008280200220820022008491b10888e8080002204200220086b20041b220841004720084100481b22084101460d000b200841ff0171450d010b200c450d02200c417f6a210c200a20074102746a41bc026a280200210a0c010b0b2003200c36022c2003200a3602282003290328210b2006450d05200941002802c0a3c68000118080808000000c050b200320073602302003410036022c2002ad4220862009ad84210b200329022c210e0b2000410c6a21072006418080808078460d032003200e3702202003200a36021c200320073602182003200b3702102003200636020c200342808080807037023020034280808080103702280240200a0d0041002d00fca3c680001a41bc0241002802c8a3c68000118180808000002201450d03200141013b01ba02200141003602b0012001200329020c3702b4012003410c6a41086a280200210520012003290228370200200141086a200341286a41086a290200370200200141bc016a2005360200200041106a4280808080103702002000200136020c0c050b200341386a41086a2003411c6a220141086a28020036020020032001290200370338200341d0006a41086a2003410c6a41086a2802003602002003200329020c370350200341c4006a200341386a200341d0006a200341286a200341186a10c08580800020032802182201200128020841016a3602080c040b10ae80808000000b4101200610b280808000000b410441bc0210b280808000000b200ba720074104746a2201200128020c417f6a36020c0b200341e0006a2480808080000b1300200028020420012002200310eb8c8080000bc20a04097f017e027f017e23808080800041e0006b22042480808080002003280204210502400240024002400240024002400240200328020822062000280208470d0020052000280204200610888e808000450d010b200228020021070240024002402002280204220641216a22080d002004420137022c200420083602280c010b2008417f4c0d044100210941002d00fca3c680001a200841002802c8a3c6800011818080800000220a450d05200441003602302004200a36022c200420083602282006415f490d010b200441286a4100200610ab8680800020042802282108200428022c210a200428023021090b200a20096a2007200610848e8080001a2004200920066a2206360230024020022d0008450d00200241096a2d00002102024020062008470d00200441286a2008109c86808000200428022c210a200428023021060b200a20066a20023a00002004200428023041016a2206360230200428022821080b0240200820066b411f4b0d00200441286a2006412010ab8680800020042802282108200428023021060b200428022c220b20066a22022001290000370000200241086a200141086a290000370000200241106a200141106a290000370000200241186a200141186a290000370000200641206a210202400240200028020c220c0d002002ad422086200bad84210d4100210c0c010b200041106a280200210e02400340200c41b4016a2106200c2f01ba02220f410c6c2101417f2109024002400340024020010d00200f21090c020b200641086a210a200641046a2107200141746a2101200941016a21092006410c6a2106417f200b20072802002002200a280200220a2002200a491b10888e80800022072002200a6b20071b220a410047200a4100481b220a4101460d000b200a41ff0171450d010b200e450d02200e417f6a210e200c20094102746a41bc026a280200210c0c010b0b2004200e36022c2004200c3602282004290328210d2008450d07200b41002802c0a3c68000118080808000000c070b200420093602302004410036022c2002ad422086200bad84210d200429022c21100b2000410c6a21092008418080808078460d05200420103702202004200c36021c200420093602182004200d3702102004200836020c200441286a41086a200341086a2802003602002004410136023420042003290200370328200c0d0141002d00fca3c680001a41bc0241002802c8a3c68000118180808000002206450d04200641013b01ba02200641003602b0012006200429020c3702b4012004410c6a41086a280200210120062004290328370200200641086a200441286a41086a290300370200200641bc016a2001360200200041106a4280808080103702002000200636020c0c060b2003280200450d05200541002802c0a3c68000118080808000000c050b200441386a41086a2004411c6a220641086a28020036020020042006290200370338200441d0006a41086a2004410c6a41086a2802003602002004200429020c370350200441c4006a200441386a200441d0006a200441286a200441186a10c08580800020042802182206200628020841016a3602080c040b10ae80808000000b4101200810b280808000000b410441bc0210b280808000000b0240200da720094104746a220628020c220141004a0d0002402006280200450d00200628020441002802c0a3c6800011808080800000200628020c21010b200620032902003702002006200141016a36020c200641086a200341086a2802003602000c010b2006200141016a36020c2003280200450d00200541002802c0a3c68000118080808000000b200441e0006a2480808080000bcb0101017f23808080800041206b2203248080808000200341086a20002802042001200210e48c808000024002400240024020032802082202418080808078470d00200341146a2000280200200110e58c80800020032802142202418180808078470d0141808080807821000c030b200328020c21000c010b41808080807821002002418080808078460d01200328021821000b024020020d00410021000c010b200041002802c0a3c6800011808080800000200221000b200341206a2480808080002000418080808078470bbf0101027f23808080800041306b2202248080808000410021030240200041c0016a2d00004102460d002002200041b8016a36022c20004198016a21030b200241186a410c6a4184848080003602002002410c6a420237020020022000360220200241858480800036021c2002200336022820024103360204200241fcfec58000360200200141186a28020021002002200241286a3602182002200241186a36020820012802142000200210d9808080002100200241306a24808080800020000bc00201027f23808080800041106b22022480808080000240024020002802000d00200128021441bcffc580004104200141186a28020028020c1182808080000021010c010b2002200036020020022001280214418888c680004104200141186a28020028020c118280808000003a000c20022001360208200241003a000d20024100360204200241046a2002418c88c68000108d81808000210120022d000c21000240200128020022030d00200041ff017141004721010c010b41012101200041ff01710d0020022802082100024020034101470d0020022d000d41ff0171450d0020002d001c4104710d00410121012000280214418ca5c080004101200041186a28020028020c118280808000000d010b2000280214418da5c080004101200041186a28020028020c1182808080000021010b200241106a24808080800020010bce0101037f23808080800041106b220224808080800020022000360208200241086a200110c18a8080002002200041086a36020c2002410c6a200110c18a80800020002d001021030240200128020020012802082204470d0020012004410110b182808000200128020821040b200128020420046a20033a00002001200441016a220436020820002d00112100024020012802002004470d0020012004410110b182808000200128020821040b2001200441016a360208200128020420046a20003a0000200241106a2480808080000b9c0804067f017e027f017e23808080800041d0006b2201248080808000200141286a4194ffc58000411041a4ffc58000411741fcfcc58000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a02400240412041002802c8a3c68000118180808000002202450d002002429b99b4f08dc8ccdea77f370308200241ae8080800036021820024101360204200241bbffc58000360200200241106a428a96cd9bb2e69e8d7237030020014101360248200120023602402001200241206a36024c20012002360244200141106a200141c0006a10fa86808000200141086a2001411c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c20024201370224200241dbfbc480003602202002410636021c200241e7fbc48000360218200241ae808080003602102002428a96cd9bb2e69e8d723703082002429b99b4f08dc8ccdea77f370300200141c0006a41086a2209200828020041016a220236020020012001290210220a37034002402002200aa7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c20024201370224200241dbfbc480003602202002410b36021c200241dcfbc48000360218200241ae808080003602102002428a96cd9bb2e69e8d723703082002429b99b4f08dc8ccdea77f3703002008200928020041016a220236020020012001290340220a37031002402002200aa7470d00200141106a200210a486808000200128021821020b2001280214200241386c6a2202420437022c20024201370224200241dbfbc480003602202002410936021c200241d2fbc48000360218200241ae808080003602102002428a96cd9bb2e69e8d723703082002429b99b4f08dc8ccdea77f370300200128021821082001280214210220012001280210360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b4108412010b280808000000b41a8d8c480004111419cd9c4800010a181808000000b930202017f067e23808080800041e0006b2202248080808000200241d0006a200110bf8a80800042012103024020022802500d0020022903582104200241c0006a200110bf8a8080002002290340a70d0020022903482105200241306a200110bf8a80800020022802300d0020022903382106200241206a200110bf8a8080002002290320a70d0020022903282107200241106a200110bf8a80800020022802100d00200229031821082002200110bf8a8080002002290300a70d002002290308210320002004370308200041306a2003370300200041286a2008370300200041206a2007370300200041186a2006370300200041106a2005370300420021030b20002003370300200241e0006a2480808080000ba80201017f23808080800041106b22022480808080002002200041c8006a36020c2002410c6a200110c18a8080002002200041d0006a36020c2002410c6a200110c18a8080002000200110c88c808000200041186a200110c88c808000200041306a200110c88c8080002002200041a0016a36020c2002410c6a200110c18a8080002002200041a8016a36020c2002410c6a200110c18a808000200041d8006a200110c88c808000200041f0006a200110c88c80800020004188016a200110c88c8080002002200041f8016a36020c2002410c6a200110c18a808000200220004180026a36020c2002410c6a200110c18a808000200041b0016a200110c88c808000200041c8016a200110c88c808000200041e0016a200110c88c808000200241106a2480808080000bd10101017f23808080800041106b220424808080800002400240024002402002200141d8006a412010888e808000450d00200441046a2001200210e58c8080002004280204418180808078460d0120002004290204370200200041086a200441046a41086a2802003602000c020b41002d00fca3c680001a410141002802c8a3c68000118180808000002201450d02200041013602082000200136020420004101360200200141003a00000c010b20004180808080783602000b200441106a2480808080000f0b4101410110b280808000000bda0101027f23808080800041106b2203248080808000024002400240024002402001200041d8006a412010888e808000450d00200341046a2000200110e58c80800041808080807821000240200328020422044180808080786a0e020304000b2004450d0220032802082101200421000c010b41002d00fca3c680001a41012100410141002802c8a3c68000118180808000002201450d03200141003a00000b200141002802c0a3c68000118080808000000c010b200421000b200341106a2480808080002000418080808078470f0b4101410110b280808000000bcf0802087f017e23808080800041f0036b2202248080808000200241106a200110bc8a808000024002400240024020022802100d002001280200220341046a22042802002205450d012002280214210620042005417f6a36020020032003280200220441016a36020002400240024020042d0000220341ff00714104470d002003411874411875417f4c0d01410221040c020b200041003602000c050b024002402001280200220328020422074120490d002003200741606a220836020420032003280200220441206a220936020020024190036a41086a200441086a29000037030020024190036a41106a200441106a29000037030020024190036a41186a200441186a2900003703002002200429000037039003200841c000490d002003200741a07f6a3602042003200441e0006a360200200241b0036a41086a200941086a290000370300200241b0036a41106a200941106a290000370300200241b0036a41186a200941186a290000370300200241b0036a41206a200941206a290000370300200241b0036a41286a200941286a290000370300200241b0036a41306a200941306a290000370300200241b0036a41386a200941386a290000370300200220092900003703b0032002200110be8a80800020022802000d00200128020022032802042204450d002002290308210a20032004417f6a36020420032003280200220441016a3602004101410220042d000022034101461b410020031b22044102470d010b200041003602000c050b200241b0026a20024190036a41186a290300370300200241a8026a20024190036a41106a290300370300200241a0026a20024190036a41086a290300370300200241d8016a41086a200241b0036a41086a290300370300200241d8016a41106a200241b0036a41106a290300370300200241d8016a41186a200241b0036a41186a290300370300200241d8016a41206a200241b0036a41206a290300370300200241d8016a41286a200241b0036a41286a290300370300200241d8016a41306a200241b0036a41306a290300370300200241d8016a41386a200241b0036a41386a290300370300200220022903900337039802200220022903b0033703d801200241f8006a200241d8016a41e00010848e8080001a0b200241186a200241f8006a41e00010848e8080001a200241d8016a200110fd8980800020022802d8012203450d02200241f8006a410472200241d8016a41047241d40010848e8080001a20022003360278024041002005200128020041046a2802006b2201200120054b1b2006470d00200241d8016a41d8006a200241186a41e00010848e8080001a200041003a00c101200241d8016a200241f8006a41d80010848e8080001a2000200241d8016a41b80110848e808000220120043a00c0012001200a3703b8010c040b20004100360200200241f8006a10f68c8080000c030b200041003602000c020b200041003602000c010b200041003602000b200241f0036a2480808080000bef0901057f0240024002400240024002402000280200220141736a2202410220024104491b0e03010203000b20002d00084106470d042000410c6a280200450d04200041106a28020021030c030b0240024002400240024002400240024020002d0008417f6a0e0a010b0203040506070b0b000b2000410c6a280200450d0a200041106a28020021030c090b2000410c6a280200450d09200041106a28020021030c080b2000410c6a280200450d08200041106a28020021030c070b2000410c6a280200450d07200041106a28020021030c060b200041106a28020021030240200041146a2802002201450d0020032102034002402002280200450d00200241046a28020041002802c0a3c68000118080808000000b02402002410c6a280200450d00200241106a28020041002802c0a3c68000118080808000000b200241186a21022001417f6a22010d000b0b200028020c450d060c050b200041106a28020021030240200041146a2802002202450d002002410171210441002101024020024101460d002002417e7121052003210241002101034002402002280200450d00200241046a28020041002802c0a3c68000118080808000000b02402002410c6a280200450d00200241106a28020041002802c0a3c68000118080808000000b200241186a21022005200141026a2201470d000b0b2004450d0020032001410c6c6a2202280200450d00200228020441002802c0a3c68000118080808000000b200028020c0d040c050b200041106a280200450d04200041146a28020021030c030b2000410c6a280200450d03200041106a28020021030c020b200041186a2d0000417d6a41ff017141014b0d020240200028020822034198016a2802002205450d0020034194016a280200220421004100210203402004200241146c6a21010240024002400240024020002d00000e0400010102040b200041086a21010c020b200141086a21010c010b200141046a21010b2001280200450d00200128020441002802c0a3c68000118080808000000b200241016a2102200041146a21002005417f6a22050d000b0b0240200328029001450d0020032802940141002802c0a3c68000118080808000000b024020034190026a2802002205450d002003418c026a280200220421004100210203402004200241146c6a21010240024002400240024020002d00000e0400010102040b200041086a21010c020b200141086a21010c010b200141046a21010b2001280200450d00200128020441002802c0a3c68000118080808000000b200241016a2102200041146a21002005417f6a22050d000b0b200328028802450d01200328028c0241002802c0a3c68000118080808000000c010b024002400240024002400240024002402001417e6a0e06000102030405090b200041046a21000c050b02402000280210450d00200041146a28020041002802c0a3c68000118080808000000b20002802042202418080808078460d07200041046a21000c050b02402000280204450d00200041086a28020041002802c0a3c68000118080808000000b200041106a21000c030b200041046a21000c020b200041046a21000c010b024002400240024020002d00040e0400010203070b2000410c6a21000c030b2000410c6a21000c020b2000410c6a21000c010b200041086a21000b200028020021020b2002450d01200028020421030b200341002802c0a3c68000118080808000000f0b0b890401047f23808080800041206b220224808080800041002d00fca3c680001a02400240024041e80141002802c8a3c68000118180808000002203450d002002410036020820022003360204200241e80136020002400240200141c0016a2d00004102470d00200341043a00002002200228020841016a3602080c010b20034184013a00002002200228020841016a360208200141d8006a200210cc878080000b2001200210b88980800020022002280208220136020c41012104410121030240200141c000490d0041022103200141808001490d00410441052001418080808004491b21030b0240200320016a2201450d002001417f4c0d0241002d00fca3c680001a200141002802c8a3c68000118180808000002204450d030b20024100360218200220043602142002200136021020022002410c6a36021c2002411c6a200241106a10c08a808000200228020421042002280200210502402002280210200228021822016b200228020822034f0d00200241106a2001200310ab86808000200228021821010b200228021420016a2004200310848e8080001a2002200120036a36021802402005450d00200441002802c0a3c68000118080808000000b20002002290210370200200041086a200241106a41086a280200360200200241206a2480808080000f0b410141e80110b280808000000b10ae80808000000b4101200110b280808000000b880703017f057e017f23808080800041f0036b2201248080808000200141c0006a10ce88808000200141f0006a10e18980800002402001290388032202427f427f2001290340220320012903507c220420042003541b220320012903607c220420042003541b2204580d0020014190036a2903002203427f427f20012903482205200141d8006a2903007c220620062005541b2205200141e8006a2903007c220620062005541b2205580d00200141306a20004200200220047d220420042002561b22024200200320057d220420042003561b220341a8c6c480002000a741037141027441a8c6c480006a2207200741b8c6c48000461b2207280200118d8080800000200141206a200042002002200129033022047d220520052002561b42002003200129033822057d220620062003561b41a8c6c48000200741046a2207200741b8c6c48000461b2207280200118d8080800000200141106a200042002002427f200420012903207c220620062004541b22047d220620062002561b42002003427f200520012903287c220620062005541b22057d220620062003561b41a8c6c48000200741046a2207200741b8c6c48000461b2207280200118d80808000002001200042002002427f200420012903107c220620062004541b22047d220620062002561b42002003427f200520012903187c220220022005541b22027d220520052003561b41a8c6c48000200741046a2207200741b8c6c48000461b280200118d8080800000200129030821002001290300210320014198036a10ce8880800020014198036a41286a2207427f20072903002205427f200220007c220020002002541b7c220020002005541b220037030020014198036a41206a2207427f20072903002202427f200420037c220320032004541b7c220320032002541b2202370300200141a0016a2000370300200141f0006a41286a2002370300200141f0006a41206a20014198036a41186a290300370300200141f0006a41186a20014198036a41106a290300370300200141f0006a41106a20014198036a41086a2903003703002001200129039803370378200142013703702001200141f0006a41086a3602cc03200142fc90b9e5d09296e7773703d803200142a6d4e6f1a4dd9598603703d003200142f89aef8eef91e1ce967f3703e803200142b4d6d6dfccc6b592c3003703e003200141cc036a200141d0036a412010f98c8080000b200141f0036a2480808080000bb30503037f017e067f23808080800041106b22032480808080004101210441012105024020002802002200290300220642c000540d0041022105200642808001540d00410421052006428080808004540d004109200679a74103766b21050b02402000290308220642c000540d0041022104200642808001540d00410421042006428080808004540d004109200679a74103766b21040b410121074101210802402000290310220642c000540d0041022108200642808001540d00410421082006428080808004540d004109200679a74103766b21080b0240200041186a2209290300220642c000540d0041022107200642808001540d00410421072006428080808004540d004109200679a74103766b21070b4101210a4101210b02402000290320220642c000540d004102210b200642808001540d004104210b2006428080808004540d004109200679a74103766b210b0b0240200041286a220c290300220642c000540d004102210a200642808001540d004104210a2006428080808004540d004109200679a74103766b210a0b41002d00fca3c680001a0240200420056a20086a20076a200b6a200a6a220441002802c8a3c68000118180808000002207450d002003200736020420032004360200200341003602082003200036020c2003410c6a200310c18a8080002003200041086a36020c2003410c6a200310c18a8080002003200041106a36020c2003410c6a200310c18a8080002003200936020c2003410c6a200310c18a8080002003200041206a36020c2003410c6a200310c18a8080002003200c36020c2003410c6a200310c18a808000200328020021002001200220032802042204200328020841002802e0a1c680001186808080000002402000450d00200441002802c0a3c68000118080808000000b200341106a2480808080000f0b4101200410b280808000000bf70f03017f017e067f23808080800041a0036b2201248080808000200010fb8c8080001a024002400240024020002903202202500d0020014198016a2002427f7c10fb8780800020014198016a2000412010888e8080000d00200141106a200041f80010848e8080001a20002802800121032001200029037822023e02900120012002422088a72200360288012001200036028c0120012000200341e8016c6a22043602940102402003450d0020014198016a410472210502400240034020002802002206450d012005200041046a41e40110848e8080001a200120063602980120014188036a20014198016a10fc8c80800020012d008803410f460d05200041e8016a22002004470d000c020b0b200041e8016a21040b2001200436028c010b20014188016a10de8680800010ed888080000d032003450d020240417f4100280284a4c680002200410147200041014b1b2200417f460d00200041ff01710d030b20014188036a410c6a418382808000360200200141d484c6800036029003200141e08180800036028c03200141f883c68000360288034100280290a1c680002100410028028ca1c6800021064100280280a4c680002104200141d0016a4202370200200141c8016a4102360200200141c0016a4112360200200141bc016a418084c68000360200200141b0016a41d080c68000ad4280808080e00b8437020020014198016a410c6a419284c68000ad4280808080f00184370200200141cc016a20014188036a360200200141a484c680003602c401200141013602b801200141003602ac01200141003602a00120014281808080a0d20037029801200641ecf2c08000200441024622041b20014198016a200041d4f2c0800020041b280210118480808000000c020b200141a4016a42003702002001410136029c01200141f884c6800036029801200141fcfcc580003602a00120014198016a418085c6800010f680808000000b200141086a20012f00890320012d008b034110747210fc84808000200120012903083702800320014180036a10fd8c808000000b10fe8c8080000b109b8a8080002001290330220210f88c808000200210cf8780800020014198016a109d8a8080002001200141106a41f0006a280200220336028003200120014198016a41f0006a28020022003602880102400240024020032000470d002003450d0220014198016a41ec006a2802002107200141106a41ec006a2802002108410021000340200820006a2206200720006a220410ef8480800020062d0000220520042d0000470d020240024002400240024020050e0400010203040b20050d03200641016a280000200441016a280000470d06200641106a2802002205200441106a280200470d062006410c6a2802002004410c6a280200200510888e8080000d060c030b20054101470d02200641016a280000200441016a280000470d05200641106a2802002205200441106a280200470d052006410c6a2802002004410c6a280200200510888e8080000d050c020b20054102470d01200641016a280000200441016a280000470d04200641106a2802002205200441106a280200470d042006410c6a2802002004410c6a280200200510888e808000450d010c040b20054103470d002006410c6a28020022052004410c6a280200470d03200641086a280200200441086a280200200510888e8080000d030b200041146a21002003417f6a22030d000c030b0b2001420037029403200141fcfcc58000360290032001410136028c03200141c880c6800036028803410020014180036a20014188016a20014188036a41b081c6800010a789808000000b20014194036a42003702002001410136028c03200141e482c6800036028803200141fcfcc580003602900320014188036a41ec82c6800010f680808000000b0240200141106a41286a220020014198016a41286a2206412010888e808000450d0041f8a7c28000410e4100280280a3c68000118480808000002000412041002802f8a2c68000118480808000002006412041002802f8a2c68000118480808000000b024020002006412010888e808000450d0020014194036a42003702002001410136028c03200141e881c6800036028803200141fcfcc580003602900320014188036a41f081c6800010f680808000000b0240200141106a41c8006a20014198016a41c8006a412010888e8080000d0002402001280288022205450d00200128028402220321004100210603402003200641146c6a21040240024002400240024020002d00000e0400010102040b200041086a21040c020b200441086a21040c010b200441046a21040b2004280200450d00200428020441002802c0a3c68000118080808000000b200641016a2106200041146a21002005417f6a22050d000b0b0240200128028002450d0020012802840241002802c0a3c68000118080808000000b02402001280280012205450d00200128027c220321004100210603402003200641146c6a21040240024002400240024020002d00000e0400010102040b200041086a21040c020b200441086a21040c010b200441046a21040b2004280200450d00200428020441002802c0a3c68000118080808000000b200641016a2106200041146a21002005417f6a22050d000b0b02402001280278450d00200128027c41002802c0a3c68000118080808000000b200141a0036a2480808080000f0b20014194036a42003702002001410136028c03200141a482c6800036028803200141fcfcc580003602900320014188036a41ac82c6800010f680808000000bc30b020a7f077e23808080800041c0036b220124808080800020002802702102200028026c21032001410036024820014280808080c0003702400240024002402002450d00200241146c21040340024020032d00000d00200341046a2d00002105200341036a2d00002106200341026a2d0000210741012102200341016a2d000021082003410c6a28020021090240200341106a280200220a450d00200a417f4c0d0541002d00fca3c680001a200a41002802c8a3c68000118180808000002202450d040b20022009200a10848e80800021090240200128024822022001280240470d00200141c0006a200210df84808000200128024821020b2001280244200241146c6a220220083a00012002200a3602102002200936020c2002200a360208200241003a0000200241046a20053a0000200241036a20063a0000200241026a20073a00002001200128024841016a3602480b200341146a21032004416c6a22040d000b0b200141306a41086a200141c0006a41086a2802003602002001200129024037033010988a808000200141c0006a10ea888080000240024020012802402203418180808078460d00200128024821020240200341808080807872418080808078460d00200128024441002802c0a3c68000118080808000000b20020d004200210b4200210c0c010b200141206a10ad8a8080002001290320210b2001290328210c200141106a10918a8080002001290310210d2001290318210e200142003702f0022001418097c380003602ec0220014180808080783602e802200142fc90b9e5d09296e777370348200142a6d4e6f1a4dd959860370340200142a0b9ab8f9ae5999778370358200142f999a7c78cd1d1cdb17f370350200141e8026a200141c0006a4120109288808000200c200e7c220e200c542103200b200d7c220c200b542102024020012802e8022204418080808078460d002004450d0020012802ec0241002802c0a3c68000118080808000000b427f200e20031b210b427f200c20021b210c0b200041206a2000200141306a10968a8080002001200029032010d0878080002001290308210d2001290300210e200141c0006a10e189808000200141d0026a290300210f20012903c8022110200141e8026a10ce88808000200141e8026a41286a2203427f20032903002211427f200f427f200b200d7c220d200d200b541b220b7c220d200d200b541b7c220b200b2011541b220b370300200141e8026a41206a2203427f2003290300220d427f2010427f200c200e7c220e200e200c541b220c7c220e200e200c541b7c220c200c200d541b220c370300200141f0006a200b370300200141c0006a41286a200c370300200141c0006a41206a200141e8026a41186a290300370300200141c0006a41186a200141e8026a41106a290300370300200141c0006a41106a200141e8026a41086a290300370300200120012903e802370348200142013703402001200141c0006a41086a36029c03200142fc90b9e5d09296e7773703a803200142a6d4e6f1a4dd9598603703a003200142f89aef8eef91e1ce967f3703b803200142b4d6d6dfccc6b592c3003703b0032001419c036a200141a0036a412010f98c808000200142003702e802200142fc90b9e5d09296e777370348200142a6d4e6f1a4dd95986037034020014293bb8a9cbb9ab6b31a370358200142ffabedd185d3d8d216370350200141e8026a200141c0006a412010948880800002402001280238220a450d002001280234220521034100210203402005200241146c6a21040240024002400240024020032d00000e0400010102040b200341086a21040c020b200441086a21040c010b200441046a21040b2004280200450d00200428020441002802c0a3c68000118080808000000b200241016a2102200341146a2103200a417f6a220a0d000b0b02402001280230450d00200128023441002802c0a3c68000118080808000000b200141c0036a24808080800041000f0b4101200a10b280808000000b10ae80808000000b970802027f017e23808080800041b0056b2202248080808000200241146a200110f78c808000200228021c2103200241f0026a200141e80110848e8080001a200241c8016a200241f0026a200210808d808000024002400240024020022802c8012201450d00200220022d00ce013a00f204200220022f01cc013b01f004200241206a410772200241c8016a41077241a10110848e8080001a200220022f01f0043b0124200220022d00f2043a00262002200136022002400240024002400240200141736a2201410220014104491b0e0400010203000b200241d8046a200241286a10ba8a8080000c030b200241d8046a200241286a10e4878080000c020b200241d8046a200241206a10bb888080000c010b200241d8046a200241286a108e8c8080000b10ed88808000450d010c020b200220022d00ce0122013a00f204200220022f01cc0122033b01f004200041036a20013a0000200020033b00012000410f3a00002002280214450d02200228021841002802c0a3c68000118080808000000c020b10fe8c8080000b200241f0026a41086a200241146a41086a280200360200200220022902143703f002200241086a41e6aac48000411010d088808000200228020c410020022802081b200241f0026a10ff87808000200241f0026a200241206a41a80110848e8080001a200241c8016a200241f0026a200241d8046a200310be878080000240024020022903c80122044203510d00200241f0046a41136a200241c8016a41136a290000370000200241f0046a411b6a200241c8016a411b6a290000370000200241f0046a41236a200241c8016a41236a290000370000200241f0046a412b6a200241c8016a412b6a290000370000200241f0046a41306a200241c8016a41306a290000370000200241f0046a410a6a200241c8016a410a6a2d000022013a0000200220022f01d00122033b01a805200220022900d3013700fb04200220013a00aa05200220033b01f804200220043703f004024020044202510d0020022d00e80441ff01714102460d020b200241f0026a41106a200241d8046a41106a290300370300200241f0026a41086a2201200241d8046a41086a290300370300200220022903d8043703f002200241f0046a200241f0026a10998a808000410e2103024020022903f0044202510d00200120024199056a290000370300200241ff026a200241a0056a28000036000020022002290091053703f00220022d00900521030b200020033a0000200020022903f002370001200041096a2001290300370000200041106a200241ff026a2800003600000c020b200220022f01d00122013b01a8052002200241d2016a2d000022033a00aa05200041036a20033a0000200020013b00012000410f3a00000c010b2000410f3b0100200041026a41083a00000b200241b0056a2480808080000b5c01017f23808080800041206b22012480808080002001410c6a420137020020014101360204200141a085c68000360200200141e08180800036021c200120003602182001200141186a3602082001419085c6800010f680808000000bcf0303017f027e017f2380808080004190036b2200248080808000200042fc90b9e5d09296e777370348200042a6d4e6f1a4dd959860370340200042beee8ae9ce8fa2b1977f37035820004295d4f9afa2868fb864370350200041013a0010200041c0006a4120200041106a410141002802e0a1c6800011868080800000200042fc90b9e5d09296e777370348200042a6d4e6f1a4dd959860370340200042d3d8c5d2a9b9d2c1ac7f37035820004282ca868eabf3add0cf003703502000200041c0006a10e688808000200041106a10ce88808000200041c0006a10e189808000024020002903d802427f427f2000290310220120002903207c220220022001541b220120002903307c220220022001541b580d00200041e0026a290300427f427f20002903182201200041286a2903007c220220022001541b2201200041386a2903007c220220022001541b580d00200041c8006a220310ce8880800020004201370340200020033602ec02200042fc90b9e5d09296e7773703f802200042a6d4e6f1a4dd9598603703f002200042f89aef8eef91e1ce967f37038803200042b4d6d6dfccc6b592c30037038003200041ec026a200041f0026a412010f98c8080000b20004190036a2480808080000ba90102017f017e23808080800041306b2201248080808000024010ed888080000d0010fe8c8080000b109b8a808000200142fc90b9e5d09296e777370318200142a6d4e6f1a4dd959860370310200142d3d8c5d2a9b9d2c1ac7f37032820014282ca868eabf3add0cf003703202001200141106a10e6888080002001290308420020012802001b220210f88c808000200210cf878080002000109d8a808000200141306a2480808080000bed0a020a7f017e23808080800041c0066b220324808080800041022104024002400240200141c0016a22052d00004102470d00200341306a200141d80010848e8080001a0c010b200341e8016a41186a200141b0016a290000370300200341e8016a41106a200141a8016a290000370300200341e8016a41086a200141a0016a290000370300200320014198016a2900003703e801200341e0016a20014190016a290000370300200341d8016a20014188016a290000370300200341a8016a41286a20014180016a290000370300200341a8016a41206a200141f8006a290000370300200341a8016a41186a200141f0006a290000370300200341a8016a41106a200141e8006a290000370300200341a8016a41086a200141e0006a290000370300200320012900583703a801200341e8046a41286a2204200141e0016a290300370300200341e8046a41206a2206200141d8016a290300370300200341e8046a41186a2207200141d0016a290300370300200341e8046a41106a2208200141c8016a290300370300200341e8046a41086a220920052903003703002003200141b8016a2903003703e8042003419e056a200341e8046a10d4888080000240024020032d009e0522054102470d00200320032f009f053b01e4042003200341a1056a2d00003a00e604200110f68c8080000c010b200341c0036a41086a200341aa056a290000370300200341c0036a41106a200341b2056a290000370300200341d5036a220a200341b7056a290000370000200341e2036a220b200341a1056a2d00003a0000200320032900a2053703c003200320032f009f053b01e0032003200141066a2d00003a00e604200320012f01043b01e4042001280200210c200341bf056a200141076a41d10010848e8080001a200341b8066a2004290300370000200341b0066a2006290300370000200341a8066a2007290300370000200341a0066a200829030037000020034198066a2009290300370000200320032903e80437009006200341e3036a200341bf056a41810110848e8080001a200c450d00200320032d00e6043a00ba03200320032f01e4043b01b80320034188026a410772200341e3036a41810110848e8080001a20034193036a200b2d00003a00002003419c036a200341c0036a41086a290300370200200341a4036a200341c0036a41106a290300370200200341a9036a200a290000370000200320032f01e0033b009103200320032903c00337029403200320032f01b8033b018c02200320032d00ba033a008e02200320053a0090032003200c36028802024020034188026a200341a8016a200341e8016a10b7898080000d002000410036020020004180083b010420034188026a10f68c8080000c030b200341306a20034188026a41d80010848e8080001a200341086a41086a200341f1026a290000370300200341086a41106a200341f9026a290000370300200341086a41186a20034181036a290000370300200341276a20034188036a29000037000020034190016a200341e8016a41086a29030037030020034198016a200341e8016a41106a290300370300200341a0016a200341e8016a41186a290300370300200320032900e902370308200320032903e8013703880120032d00e802210420032903e002210d0c010b200320032d00e60422013a00ba03200320032f01e40422043b01b803200041066a20013a0000200020043b0104200041003602000c010b2000200341306a41f80010848e808000220120043a0080012001200d370378200120032903083700810120014189016a200341106a29030037000020014191016a200341186a29030037000020014199016a200341206a290300370000200141a0016a200341276a2900003700000b200341c0066a2480808080000b9f0601057f23808080800041e0046b2204248080808000200442fc90b9e5d09296e7773703f802200442a6d4e6f1a4dd9598603703f002200442d3d8c5d2a9b9d2c1ac7f3703880320044282ca868eabf3add0cf0037038003200441086a200441f0026a10e6888080002004200429031042017c420120042802081b3703c801200441003602f80220044280808080c0003702f002200441c8016a2003200441f0026a10968a808000024020042802f8022205450d0020042802f402220621034100210703402006200741146c6a21080240024002400240024020032d00000e0400010102040b200341086a21080c020b200841086a21080c010b200841046a21080b2008280200450d00200828020441002802c0a3c68000118080808000000b200741016a2107200341146a21032005417f6a22050d000b0b024020042802f002450d0020042802f40241002802c0a3c68000118080808000000b200441f0026a200210f78c80800020042802f8022107024020042802f002450d0020042802f40241002802c0a3c68000118080808000000b200441f0026a200241e80110848e8080001a200441c8016a200441f0026a200310808d8080000240024020042802c8012203450d00200420042d00ce013a00c201200420042f01cc013b01c001200441186a410772200441c8016a41077241a10110848e8080001a200420042f01c0013b011c200420042d00c2013a001e2004200336021802400240024002400240200341736a2203410220034104491b0e0400010203000b200441f0026a200441206a10ba8a8080000c030b200441f0026a200441206a10e4878080000c020b200441f0026a200441186a10bb888080000c010b200441f0026a200441206a108e8c8080000b024020042d0080034102470d00200041808080807836021020004180123b0100200441186a10f68c8080000c020b2000200441186a2001200441f0026a200710c087808000200441186a10f68c8080000c010b200420042d00ce0122033a00c201200420042f01cc0122073b01c001200041026a20033a0000200020073b010020004180808080783602100b200441e0046a2480808080000bc40904067f017e027f017e23808080800041d0006b2201248080808000200141286a41a885c68000410b41b385c68000411641fcfcc58000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a02400240412041002802c8a3c68000118180808000002202450d00200242a5e9e3ab9e929adc2c370308200241ef8080800036021820024107360204200241c985c68000360200200241106a4293888c8f89fdc6ec9e7f370300200120023602402001200241206a36024c2001200236024420014101360248200141106a200141c0006a10fa86808000200141086a2001411c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c20024207370224200241c7fbc480003602202002410436021c20024193fcc48000360218200241ef8080800036021020024293888c8f89fdc6ec9e7f370308200242a5e9e3ab9e929adc2c370300200141c0006a41086a2209200828020041016a220236020020012001290210220a37034002402002200aa7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c20024207370224200241c7fbc480003602202002410836021c2002418bfcc48000360218200241ef8080800036021020024293888c8f89fdc6ec9e7f370308200242a5e9e3ab9e929adc2c3703002008200928020041016a220236020020012001290340220a37031002402002200aa7470d00200141106a200210a486808000200128021821020b2001280214200241386c6a2202420437022c20024207370224200241c7fbc480003602202002410636021c20024185fcc48000360218200241ef8080800036021020024293888c8f89fdc6ec9e7f370308200242a5e9e3ab9e929adc2c370300200141c0006a41086a200141106a41086a28020041016a220236020020012001290310220a37034002402002200aa7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c2002420a370224200241fbfbc480003602202002410536021c200241f6fbc480003602182002418684808000360210200242dcf29fddf2bfe9f9f300370308200242a7a6d9c6f090c49632370300200128024821082001280244210220012001280240360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b4108412010b280808000000b41a8d8c480004111419cd9c4800010a181808000000ba00804067f017e027f017e23808080800041d0006b2201248080808000200141286a41d085c68000410b41b385c68000411641fcfcc58000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a02400240412041002802c8a3c68000118180808000002202450d00200242a5e9e3ab9e929adc2c370308200241ef8080800036021820024107360204200241c985c68000360200200241106a4293888c8f89fdc6ec9e7f370300200120023602402001200241206a36024c2001200236024420014101360248200141106a200141c0006a10fa86808000200141086a2001411c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c2002420e37022420024197fcc480003602202002410236021c200241cefbc480003602182002418784808000360210200242a8c7daf8defb9a8fc400370308200242a7b98ffce8cbcbd38b7f370300200141c0006a41086a2209200828020041016a220236020020012001290210220a37034002402002200aa7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c20024207370224200241c7fbc480003602202002410636021c200241c1fbc48000360218200241ef8080800036021020024293888c8f89fdc6ec9e7f370308200242a5e9e3ab9e929adc2c3703002008200928020041016a220236020020012001290340220a37031002402002200aa7470d00200141106a200210a486808000200128021821020b2001280214200241386c6a2202420437022c20024207370224200241acfcc480003602202002410736021c200241a5fcc48000360218200241888480800036021020024282b0a8d2a7f3c7e316370308200242e59ffc9df1de8b92867f370300200128021821082001280214210220012001280210360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141106a41076a290000370000200141d0006a2480808080000f0b4108412010b280808000000b41a8d8c480004111419cd9c4800010a181808000000bbe0203017e047f017e20002903102102024020012802002203200128020822046b41074b0d0020012004410810b18280800020012802002103200128020821040b2001200441086a22053602082001280204220620046a2002370000200029031821020240200320056b41074b0d0020012005410810b1828080002001280200210320012802042106200128020821050b2001200541086a2204360208200620056a2002370000200029032021020240200320046b41074b0d0020012004410810b18280800020012802042106200128020821040b200620046a20023700002001200441086a2205360208200041086a2903002102200029030021070240200128020020056b410f4b0d0020012005411010b182808000200128020821050b2001200541106a360208200128020420056a22012002370008200120073700000bcd0704067f017e017f017e23808080800041d0006b2201248080808000200141286a41db85c68000410b41b385c68000411641fcfcc58000410010d882808000200141106a41106a42043702002001420037021820014280808080800137021041002d00fca3c680001a0240024041c00041002802c8a3c68000118180808000002202450d00200242a7b98ffce8cbcbd38b7f370308200241c985c68000360220200241878480800036021820024111360204200241e685c68000360200200241306a4293888c8f89fdc6ec9e7f370300200241286a42a5e9e3ab9e929adc2c370300200241106a42a8c7daf8defb9a8fc400370300200241386a41ef80808000360200200241246a410736020020014102360248200120023602402001200241c0006a36024c20012002360244200141106a200141c0006a10fa86808000200141086a2001411c6a220241086a2802003602002001200229020037030020012802102103200128021421042001280218210520012802282106200129022c2107200141106a41086a22084100360200200142808080808001370210200141106a410010a4868080002001280214200828020041386c6a2202420437022c20024211370224200241b3fcc480003602202002410236021c200241cefbc480003602182002418784808000360210200242a8c7daf8defb9a8fc400370308200242a7b98ffce8cbcbd38b7f370300200141c0006a41086a200828020041016a2202360200200120012902102209370340024020022009a7470d00200141c0006a200210a486808000200128024821020b2001280244200241386c6a2202420437022c20024207370224200241c7fbc480003602202002410636021c200241c1fbc48000360218200241ef8080800036021020024293888c8f89fdc6ec9e7f370308200242a5e9e3ab9e929adc2c370300200128024821082001280244210220012001280240360248200120023602402001200236024420012002200841386c6a41386a36024c200141346a200141c0006a10f9868080002006418080808078460d012001200336021820012004360210200120043602142001200420054105746a36021c200041c4006a200141106a10fa868080002001411b6a200141346a41086a2802003600002000413c6a200737020020002006360238200041003a000020002001290300370350200041d8006a200141086a2802003602002001200129023437001320002001290010370001200041086a200141176a290000370000200141d0006a2480808080000f0b410841c00010b280808000000b41a8d8c480004111419cd9c4800010a181808000000b950901027f23808080800041c0016b220424808080800041002d00fca3c680001a024002400240410141002802c8a3c68000118180808000002205450d00200541003a0000200441246a419380c6800041014100280298a3c6800011858080800000200441206a410036020020044201370214200420053602102004410136020c200141386a210520012d00790d0120012d00780d022004200136024420042004410c6a3602480240024020030d00200441f0006a41186a200541186a290000370300200441f0006a41106a200541106a290000370300200441f0006a41086a200541086a2900003703002004200529000037037020044190016a41286a200241286a28020036020020044190016a41206a200241206a29020037030020044190016a41186a200241186a29020037030020044190016a41106a200241106a29020037030020044190016a41086a200241086a2902003703002004200229020037039001200441cc006a200441c4006a200441f0006a20044190016a4100419c87c680004100418087c6800010ce898080000c010b200441f0006a41186a200541186a290000370300200441f0006a41106a200541106a290000370300200441f0006a41086a200541086a2900003703002004200529000037037020044190016a41286a200241286a28020036020020044190016a41206a200241206a29020037030020044190016a41186a200241186a29020037030020044190016a41106a200241106a29020037030020044190016a41086a200241086a2902003703002004200229020037039001200441cc006a200441c4006a200441f0006a20044190016a4100419c87c680004100418087c6800010cf898080000b0240024020042d004c0d00200441a8016a200441e5006a290000370300200441a0016a200441dd006a29000037030020044198016a200441d5006a2900003703002004200429004d370390010c010b20044190016a41086a200541086a29000037030020044190016a41106a200541106a29000037030020044190016a41186a200541186a290000370300200420052900003703900102400240024002402004280250220528020041fcffffff076a2202410320024105491b0e0403030102000b2005280204450d02200541086a28020041002802c0a3c68000118080808000000c020b2005280204450d01200541086a28020041002802c0a3c68000118080808000000c010b200510878d8080000b200541002802c0a3c68000118080808000000b2000200429039001370000200041186a20044190016a41186a290300370000200041106a20044190016a41106a290300370000200041086a20044190016a41086a290300370000200041d0006a2004410c6a41306a290200370200200041c8006a2004410c6a41286a290200370200200041c0006a2004412c6a290200370200200041386a2004410c6a41186a290200370200200041306a2004410c6a41106a290200370200200041286a2004410c6a41086a2902003702002000200429020c370220200441c0016a2480808080000f0b4101410110b280808000000b200141fa006a200510ee87808000000b200141f9006a108888808000000bc50101027f024002400240024020002802002201418080808078732202410220024104491b0e03030301000b0240024002402000280204220028020041fcffffff076a2202410320024105491b0e0404040102000b2000280204450d03200041086a28020041002802c0a3c68000118080808000000c030b2000280204450d02200041086a28020041002802c0a3c68000118080808000000c020b200010878d8080000c010b2001450d01200028020421000b200041002802c0a3c68000118080808000000b0bde0f03047f017e017f2380808080004180026b2205248080808000200541086a41014101412110fb8a80800041002d00fca3c680001a024002400240410141002802c8a3c68000118180808000002206450d00200641003a0000200541c0006a419380c6800041014100280298a3c68000118580808000002005413c6a4100360200200542013702302005200636022c20054101360228200541b0016a200210a085808000200141f9006a2106200141386a210720012d00780d0120062d00000d0220052802b801210820052802b4012106200541f4016a41a08ac48000360200200541e8016a4100360200200520073602f801200520013602f001200542003702e001200541003602d801200542003702d0012005418c016a200541d0016a2006200810dd878080000240024002400240200528028c012207418180808078460d002007418080808078460d012005290290012209422088a722084120460d024120200841f086c6800010a281808000000b0240024002400240200528029001220728020041fcffffff076a2208410320084105491b0e0403030102000b2007280204450d02200741086a28020041002802c0a3c68000118080808000000c020b2007280204450d01200741086a28020041002802c0a3c68000118080808000000c010b200710878d8080000b200741002802c0a3c6800011808080800000024020052802b001450d00200641002802c0a3c68000118080808000000b200541e0006a41086a200541086a41086a290000370300200541e0006a41106a200541086a41106a290000370300200541e0006a41186a200541086a41186a290000370300200520052900083703600c020b024020052802b001450d00200641002802c0a3c68000118080808000000b200541e0006a41086a200541086a41086a290000370300200541e0006a41106a200541086a41106a290000370300200541e0006a41186a200541086a41186a290000370300200520052900083703600c010b200528029001210a200541e0006a41186a2009a7220841186a290000370300200541e0006a41106a200841106a290000370300200541e0006a41086a200841086a2900003703002005200829000037036002402007450d00200a41002802c0a3c68000118080808000000b20052802b001450d00200641002802c0a3c68000118080808000000b20052001360284012005200541286a3602880120022802082101200228020421020240024020040d00200541b0016a41186a200541e0006a41186a290300370300200541b0016a41106a200541e0006a41106a290300370300200541b0016a41086a200541e0006a41086a290300370300200520052903603703b001200541d0016a41206a200341206a280200360200200541d0016a41186a200341186a290200370300200541d0016a41106a200341106a290200370300200541d0016a41086a200341086a290200370300200520032902003703d0012005418c016a2002200120054184016a200541b0016a200541d0016a4100419c87c680004100418087c6800010d1898080000c010b200541b0016a41186a200541e0006a41186a290300370300200541b0016a41106a200541e0006a41106a290300370300200541b0016a41086a200541e0006a41086a290300370300200520052903603703b001200541d0016a41206a200341206a280200360200200541d0016a41186a200341186a290200370300200541d0016a41106a200341106a290200370300200541d0016a41086a200341086a290200370300200520032902003703d0012005418c016a2002200120054184016a200541b0016a200541d0016a4100419c87c680004100418087c6800010d0898080000b0240024020052d008c010d00200541e8016a200541a5016a290000370300200541e0016a2005419d016a290000370300200541d8016a20054195016a2900003703002005200529008d013703d0010c010b200541d0016a41086a200541e0006a41086a290300370300200541d0016a41106a200541e0006a41106a290300370300200541d0016a41186a200541e0006a41186a290300370300200520052903603703d0010240024002400240200528029001220128020041fcffffff076a2203410320034105491b0e0403030102000b2001280204450d02200141086a28020041002802c0a3c68000118080808000000c020b2001280204450d01200141086a28020041002802c0a3c68000118080808000000c010b200110878d8080000b200141002802c0a3c68000118080808000000b200541b0016a41086a2201200541d0016a41086a290300370300200541b0016a41106a2203200541d0016a41106a290300370300200541b0016a41186a2202200541d0016a41186a290300370300200520052903d0013703b001200541b0016a200541086a412010888e8080002106200041196a2002290300370000200041116a2003290300370000200041096a2001290300370000200020052903b001370001200020052902283702242000412c6a200541286a41086a290200370200200041346a200541286a41106a2902003702002000413c6a200541286a41186a290200370200200041c4006a200541286a41206a290200370200200041cc006a200541d0006a290200370200200041d4006a200541d8006a29020037020020002006453a000020054180026a2480808080000f0b4101410110b280808000000b20062007108788808000000b200141fa006a200710ee87808000000b9b0201037f23808080800041206b22022480808080000240024002400240024020002d000022030e050001020304000b2002200041016a36021420022000410c6a2902003702180c030b2002200041016a36021420022000410c6a2902003702180c020b2002200041016a36021420022000410c6a2902003702180c010b2002200041086a2902003702140b20022003360210200241046a200241106a10ed848080002002280208210402402001280200200128020822006b200228020c22034f0d0020012000200310b182808000200128020821000b200128020420006a2004200310848e8080001a2001200020036a36020802402002280204450d00200441002802c0a3c68000118080808000000b200241206a2480808080000b9f0201037f23808080800041206b22042480808080000240024002400240200141146c41046a22050d00410121060c010b2005417f4c0d0141002d00fca3c680001a200541002802c8a3c68000118180808000002206450d020b20044100360214200420063602102004200536020c200420013602182004200441186a36021c2004411c6a2004410c6a10c08a80800002402001450d00200141146c2101034020002004410c6a10de8c808000200041146a21002001416c6a22010d000b0b200428020c21002002200320042802102201200428021441002802e0a1c680001186808080000002402000450d00200141002802c0a3c68000118080808000000b200441206a2480808080000f0b10ae80808000000b4101200510b280808000000bae0503037f017e067f23808080800041106b2203248080808000410121044101210502402000290300220642c000540d0041022105200642808001540d00410421052006428080808004540d004109200679a74103766b21050b02402000290308220642c000540d0041022104200642808001540d00410421042006428080808004540d004109200679a74103766b21040b410121074101210802402000290310220642c000540d0041022108200642808001540d00410421082006428080808004540d004109200679a74103766b21080b0240200041186a2209290300220642c000540d0041022107200642808001540d00410421072006428080808004540d004109200679a74103766b21070b4101210a4101210b02402000290320220642c000540d004102210b200642808001540d004104210b2006428080808004540d004109200679a74103766b210b0b0240200041286a220c290300220642c000540d004102210a200642808001540d004104210a2006428080808004540d004109200679a74103766b210a0b41002d00fca3c680001a0240200420056a20086a20076a200b6a200a6a220441002802c8a3c68000118180808000002207450d002003200736020420032004360200200341003602082003200036020c2003410c6a200310c18a8080002003200041086a36020c2003410c6a200310c18a8080002003200041106a36020c2003410c6a200310c18a8080002003200936020c2003410c6a200310c18a8080002003200041206a36020c2003410c6a200310c18a8080002003200c36020c2003410c6a200310c18a808000200328020021002001200220032802042204200328020841002802e0a1c680001186808080000002402000450d00200441002802c0a3c68000118080808000000b200341106a2480808080000f0b4101200410b280808000000be40201047f23808080800041206b2203248080808000024002402002417f4c0d0041002d00fca3c680001a200241e8016c410472220441002802c8a3c68000118180808000002205450d012003410036020c2003200536020820032004360204200320023602102003200341106a360214200341146a200341046a10c08a80800002402002450d00200241e8016c2106200328020c21020340200341146a200110f78c808000200328021821050240200328020420026b200328021c22044f0d00200341046a2002200410b182808000200328020c21020b200328020820026a2005200410848e8080001a2003200220046a220236020c02402003280214450d00200541002802c0a3c68000118080808000000b200141e8016a2101200641987e6a22060d000b0b20002003290204370200200041086a200341046a41086a280200360200200341206a2480808080000f0b10ae80808000000b4101200410b280808000000b870503037f017e067f23808080800041106b2202248080808000410121034101210402402001290300220542c000540d0041022104200542808001540d00410421042005428080808004540d004109200579a74103766b21040b02402001290308220542c000540d0041022103200542808001540d00410421032005428080808004540d004109200579a74103766b21030b410121064101210702402001290310220542c000540d0041022107200542808001540d00410421072005428080808004540d004109200579a74103766b21070b0240200141186a2208290300220542c000540d0041022106200542808001540d00410421062005428080808004540d004109200579a74103766b21060b410121094101210a02402001290320220542c000540d004102210a200542808001540d004104210a2005428080808004540d004109200579a74103766b210a0b0240200141286a220b290300220542c000540d0041022109200542808001540d00410421092005428080808004540d004109200579a74103766b21090b41002d00fca3c680001a0240200320046a20076a20066a200a6a20096a220341002802c8a3c680001181808080000022060d004101200310b280808000000b200241086a2207410036020020022006360204200220033602002002200136020c2002410c6a200210c18a8080002002200141086a36020c2002410c6a200210c18a8080002002200141106a36020c2002410c6a200210c18a8080002002200836020c2002410c6a200210c18a8080002002200141206a36020c2002410c6a200210c18a8080002002200b36020c2002410c6a200210c18a808000200041086a200728020036020020002002290200370200200241106a2480808080000bd40801077f23808080800041206b220224808080800002400240417f200141206a2802002203410c6c220441156a22052005200441046a491b2205417f4c0d0041002d00fca3c680001a200541002802c8a3c68000118180808000002204450d012002410036020c2002200436020820022005360204024002402001280200418080808078470d00200241106a2001280204200141086a28020010b4828080000c010b200241106a2001280204200128020810e6848080000b410021062002280214210702402005200228021822084f0d00200241046a4100200810b18280800020022802082104200228020c21060b200420066a2007200810848e8080001a2002200620086a220536020c02402002280210450d00200741002802c0a3c68000118080808000000b200141146a2802002106200141106a280200210802400240200128020c418080808078470d00200241106a2008200610b4828080000c010b200241106a2008200610e6848080000b200228021421080240200228020420056b200228021822064f0d00200241046a2005200610b18280800020022802082104200228020c21050b200420056a2008200610848e8080001a2002200520066a220536020c02402002280210450d00200841002802c0a3c68000118080808000000b200128022421060240200228020420056b41034b0d00200241046a2005410410b18280800020022802082104200228020c21050b200420056a20063600002002200541046a220436020c200128022821050240200228020420046b41034b0d00200241046a2004410410b182808000200228020c21040b200228020820046a20053600002002200441046a220436020c200128022c21050240200228020420046b41034b0d00200241046a2004410410b182808000200228020c21040b200228020820046a20053600002002200441046a36020c2001411c6a28020021052002200336021c20022002411c6a360210200241106a200241046a10c08a808000200228020c210402402003450d0020052003410c6c6a2107034002402002280204220320046b41074b0d00200241046a2004410810b18280800020022802042103200228020c21040b2002280208220620046a20052902003700002002200441086a220436020c200541086a28020021080240200320046b41034b0d00200241046a2004410410b18280800020022802082106200228020c21040b200620046a20083600002002200441046a220436020c2005410c6a22052007470d000b0b200128023021050240200228020420046b41034b0d00200241046a2004410410b182808000200228020c21040b200228020820046a20053600002002200441046a220436020c20012d00342105024020022802042004470d00200241046a2004410110b182808000200228020c21040b200228020820046a20053a0000200041086a200441016a36020020002002290204370200200241206a2480808080000f0b10ae80808000000b4101200510b280808000000b811004047f027e057f027e2380808080004190026b2202248080808000024002402001280200220328020422044120490d002003200441606a36020420032003280200220441206a360200200241206a41086a200441086a290000370300200241206a41106a200441106a290000370300200241206a41186a200441186a29000037030020022004290000370320024002402001280200220328020422044108490d002003200441786a36020420032003280200220441086a3602002001280200220328020422054120490d01200429000021062003200541606a36020420032003280200220441206a360200200241a0016a41086a200441086a290000370300200241a0016a41106a200441106a290000370300200241a0016a41186a200441186a290000370300200220042900003703a001200241106a200110be8a8080002002290310a70d012001280200220328020422044120490d01200229031821072003200441606a36020420032003280200220441206a360200200241c0016a41086a200441086a290000370300200241c0016a41106a200441106a290000370300200241c0016a41186a200441186a290000370300200220042900003703c0012001280200220328020422044120490d012003200441606a36020420032003280200220441206a360200200241e0016a41086a200441086a290000370300200241e0016a41106a200441106a290000370300200241e0016a41186a200441186a290000370300200220042900003703e00120024184026a20011094878080002002280284022208418080808078460d0120024180016a41086a200241a0016a41086a220929030037030020024180016a41106a200241a0016a41106a220a29030037030020024180016a41186a200241a0016a41186a220b290300370300200241e0006a41086a200241c0016a41086a290300370300200241e0006a41106a200241c0016a41106a290300370300200241e0006a41186a200241c0016a41186a290300370300200220022903a00137038001200220022903c001370360200228028c022105200228028802210c200241c0006a41186a200241e0016a41186a290300370300200241c0006a41106a200241e0016a41106a290300370300200241c0006a41086a200241e0016a41086a290300370300200220022903e00137034002402001280200220328020422044120490d002003200441606a36020420032003280200220441206a3602002009200441086a290000370300200a200441106a290000370300200b200441186a290000370300200220042900003703a0012002200110be8a8080002002290300a70d002001280200220328020422044120490d002002290308210d2003200441606a36020420032003280200220441206a360200200241c0016a41086a200441086a290000370300200241c0016a41106a200441106a290000370300200241c0016a41186a200441186a290000370300200220042900003703c0012001280200220328020422044120490d002003200441606a36020420032003280200220441206a360200200241e0016a41086a200441086a290000370300200241e0016a41106a200441106a290000370300200241e0016a41186a200441186a290000370300200220042900003703e00120024184026a20011094878080002002280284022201418080808078460d00200229028802210e200020022903a0013700a001200020022903c0013700c801200041b8016a200241a0016a41186a290300370000200041b0016a200241a0016a41106a290300370000200041a8016a200241a0016a41086a290300370000200041d0016a200241c0016a41086a290300370000200041d8016a200241c0016a41106a290300370000200041e0016a200241c0016a41186a290300370000200020022903e0013700e801200041f0016a200241e0016a41086a290300370000200041f8016a200241e0016a41106a29030037000020004180026a200241e0016a41186a2903003700002000200e37028c0220002001360288022000200d3703c00120002002290320370000200041086a200241206a41086a290300370000200041106a200241206a41106a290300370000200041186a200241206a41186a2903003700002000200229038001370328200041306a20024180016a41086a290300370300200041386a20024180016a41106a290300370300200041c0006a20024180016a41186a29030037030020002007370348200041e8006a200241e0006a41186a290300370300200041e0006a200241e0006a41106a290300370300200041d8006a200241e0006a41086a2903003703002000200229036037035020004188016a200241c0006a41186a29030037030020004180016a200241c0006a41106a290300370300200041f8006a200241c0006a41086a2903003703002000200229034037037020002005360298012000200c360294012000200836029001200020063703200c040b20004180808080783602880202402005450d00200c2101410021030340200c200341146c6a21040240024002400240024020012d00000e0400010102040b200141086a21040c020b200441086a21040c010b200441046a21040b2004280200450d00200428020441002802c0a3c68000118080808000000b200341016a2103200141146a21012005417f6a22050d000b0b2008450d03200c41002802c0a3c68000118080808000000c030b2000418080808078360288020c020b2000418080808078360288020c010b2000418080808078360288020b20024190026a2480808080000b21002001280214419c88c68000410f200141186a28020028020c118280808000000b180020002802002001200028020428020c118380808000000bc80601097f2000280200210102400240024020002802202202450d0020002802042103034020002002417f6a220236022002400240024002400240024002402001450d0020030d0020002802082104200028020c2205450d03200541077122060d01200521030c020b2001450d04200028020c210620002802082105200321040c030b2005210303402003417f6a210320042802bc0221042006417f6a22060d000b0b20054108490d00034020042802bc022802bc022802bc022802bc022802bc022802bc022802bc022802bc022104200341786a22030d000b0b2000420037020820002004360204410121012000410136020041002106410021050b02400240200620042f01ba02490d00034020042802b0012203450d0220042f01b8022106200441002802c0a3c6800011808080800000200541016a210520032104200620032f01ba024f0d000b200321040b200641016a2107024020050d00200421030c030b200420074102746a41bc026a2802002103410021072005417f6a2208450d022005417e6a2109024020084107712205450d0003402008417f6a210820032802bc0221032005417f6a22050d000b0b20094107490d02034020032802bc022802bc022802bc022802bc022802bc022802bc022802bc022802bc022103200841786a22080d000c030b0b200441002802c0a3c6800011808080800000418cd0c2800010a081808000000b41d887c6800010a081808000000b2000200736020c2000410036020820002003360204024020042006410c6c6a41b4016a2205280200450d00200528020441002802c0a3c68000118080808000000b0240200420064104746a2204280200450d00200428020441002802c0a3c68000118080808000000b20020d000b200041003602000c010b200041003602002001450d01200028020422030d0020002802082103200028020c2205450d0002400240200541077122060d00200521040c010b2005210403402004417f6a210420032802bc0221032006417f6a22060d000b0b20054108490d00034020032802bc022802bc022802bc022802bc022802bc022802bc022802bc022802bc022103200441786a22040d000b0b034020032802b0012104200341002802c0a3c68000118080808000002004210320040d000b0b0b02000b02000bba0402037f017e23808080800041a0026b22022480808080000240024002400240024002400240200028020022030d002002410c6a200141cc0010848e8080001a2002410a6a200141cf006a2d00003a0000200220012f004d3b010820012d004c2101410021040c010b200241d0016a20032000280204200110c48580800020022802d001450d01200241d8016a290200210520022802d40121042002410c6a200141cc0010848e8080001a2002410a6a200141cf006a2d00003a0000200220012f004d3b010820012d004c21010b41012103200141ff01714102460d03200220003602642002200537025c20022004360258200241e8006a2002410c6a41cc0010848e8080002103200241b7016a200241086a41026a2d00003a0000200220013a00b401200220022f01083b00b50120040d0141002d00fca3c680001a41f80641002802c8a3c68000118180808000002201450d04200141013b01f606200141003602f0062001200341d00010fe8d80800021012000428080808010370204200020013602000c020b41012103200141c8006a2802004129490d02200128022041002802c0a3c68000118080808000000c020b200241b8016a41086a200241d8006a41086a280200360200200220022902583703b801200241d0016a200341d00010848e8080001a200241c4016a200241b8016a200241d0016a200241e4006a10be8580800020022802642201200128020841016a3602080b410021030b200241a0026a24808080800020030f0b410441f80610b280808000000bfe0504027f017e097f017e23808080800041d0006b22042480808080000240024002400240200128020022050d002002290204210620022802002107410021050c010b20022802082108200228020421092001280204210a02400340200541b4016a210720052f01ba02220b410c6c210c417f210d0240024003400240200c0d00200b210d0c020b200741086a210e200741046a210f200c41746a210c200d41016a210d2007410c6a2107417f2009200f2802002008200e280200220e2008200e491b10888e808000220f2008200e6b200f1b220e410047200e4100481b220e4101460d000b200e41ff0171450d010b200a450d02200a417f6a210a2005200d4102746a41bc026a28020021050c010b0b2004200a36024420042005360240200429034021062002280200450d02200941002802c0a3c68000118080808000000c020b2004200d360248200441003602442002280200210720022902042106200429024421100b02402007418080808078470d002001210d0c010b200420103702202004200536021c20042001360218200420063702102004200736020c02400240024020050d0041002d00fca3c680001a41bc0241002802c8a3c68000118180808000002207450d02200741013b01ba02200741003602b0012007200429020c3702b4012004410c6a41086a280200210c20072003290200370200200741086a200341086a290200370200200741bc016a200c3602002001428080808010370204200120073602000c010b200441286a41086a2004411c6a220741086a28020036020020042007290200370328200441c0006a41086a2004410c6a41086a2802003602002004200429020c370340200441346a200441286a200441c0006a2003200441186a10bf8580800020042802182207200728020841016a3602080b200041063a00000c020b410441bc0210b280808000000b20002006a7200d4104746a220729020037020020072003290200370200200041086a200741086a22072902003702002007200341086a2902003702000b200441d0006a2480808080000bc20803097f017e017f23808080800041c0046b2203248080808000200320013602940220034100360290022003200236028c0220034198026a2003418c026a108f8d80800002400240024020032802a0042204418080808078460d0020032802ac04210120032802a804210520032802a404210620032802b003210720032802ac03210820032802a803210920032003418c026a10bc8a808000024020032802000d00200341b4046a2003418c026a200328020410d08580800020032802b404220a418080808078470d020b02402007450d00200821024100210103402008200141146c6a210b0240024002400240024020022d00000e0400010102040b200241086a210b0c020b200b41086a210b0c010b200b41046a210b0b200b280200450d00200b28020441002802c0a3c68000118080808000000b200141016a2101200241146a21022007417f6a22070d000b0b02402009450d00200841002802c0a3c68000118080808000000b02402005450d00200621024100210103402006200141146c6a21070240024002400240024020022d00000e0400010102040b200241086a21070c020b200741086a21070c010b200741046a21070b2007280200450d00200728020441002802c0a3c68000118080808000000b200141016a2101200241146a21022005417f6a22050d000b0b2004450d00200641002802c0a3c68000118080808000000b2000418080808078360288020c010b20033502bc04210c20032802b804210d200341f8006a20034198026a41900110848e8080001a2003410c6a200341b4036a41ec0010848e8080001a02402002280204450d0020004180808080783602880202402007450d00200821024100210103402008200141146c6a210b0240024002400240024020022d00000e0400010102040b200241086a210b0c020b200b41086a210b0c010b200b41046a210b0b200b280200450d00200b28020441002802c0a3c68000118080808000000b200141016a2101200241146a21022007417f6a22070d000b0b02402009450d00200841002802c0a3c68000118080808000000b02402005450d00200621024100210103402006200141146c6a21070240024002400240024020022d00000e0400010102040b200241086a21070c020b200741086a21070c010b200741046a21070b2007280200450d00200728020441002802c0a3c68000118080808000000b200141016a2101200241146a21022005417f6a22050d000b0b02402004450d00200641002802c0a3c68000118080808000000b200a450d01200d41002802c0a3c68000118080808000000c010b2000200341f8006a41900110848e8080002202200736029801200220083602940120022009360290012002419c016a2003410c6a41ec0010848e8080001a2002200c3703a0022002200d36029c022002200a36029802200220013602940220022005360290022002200636028c0220022004360288020b200341c0046a2480808080000b810c03027f017e037f2380808080004190036b2203248080808000200320013602bc01200341003602b801200320023602b401024002400240200228020422014120490d002002200141606a36020420022002280200220141206a360200200341a0026a41086a200141086a290000370300200341a0026a41106a200141106a290000370300200341a0026a41186a200141186a290000370300200320012900003703a002200341186a200341b4016a10be8a8080002003290318a70d0020032802b401220128020422044120490d00200329032021052001200441606a36020420012001280200220441206a360200200341c0026a41086a200441086a290000370300200341c0026a41106a200441106a290000370300200341c0026a41186a200441186a290000370300200320042900003703c00220032802b401220128020422044120490d002001200441606a36020420012001280200220441206a360200200341e0026a41086a200441086a290000370300200341e0026a41106a200441106a290000370300200341e0026a41186a200441186a290000370300200320042900003703e002200341106a200341b4016a10bc8a80800020032802100d0020034184036a200341b4016a200328021410c7858080002003280284032206418080808078460d0020034180026a41086a200341a0026a41086a29030037030020034180026a41106a200341a0026a41106a29030037030020034180026a41186a200341a0026a41186a290300370300200341e0016a41086a200341c0026a41086a290300370300200341e0016a41106a200341c0026a41106a290300370300200341e0016a41186a200341c0026a41186a290300370300200320032903a00237038002200320032903c0023703e001200328028c0321042003280288032107200341c0016a41186a200341e0026a41186a290300370300200341c0016a41106a200341e0026a41106a290300370300200341c0016a41086a200341e0026a41086a290300370300200320032903e0023703c001200341086a200341b4016a10bc8a808000024020032802080d00200341e0026a200341b4016a200328020c10cc8580800020032802e0022201418080808078470d020b02402004450d00200721024100210103402007200141146c6a21080240024002400240024020022d00000e0400010102040b200241086a21080c020b200841086a21080c010b200841046a21080b2008280200450d00200828020441002802c0a3c68000118080808000000b200141016a2101200241146a21022004417f6a22040d000b0b2006450d00200741002802c0a3c68000118080808000000b20004180808080783602780c010b200341286a41086a20034180026a41086a290300370300200341286a41106a20034180026a41106a290300370300200341286a41186a20034180026a41186a290300370300200341d8006a200341e0016a41086a290300370300200341e0006a200341e0016a41106a290300370300200341e8006a200341e0016a41186a290300370300200320032903800237032820032005370348200320032903e00137035020032902e402210520034188016a200341c0016a41186a29030037030020034180016a200341c0016a41106a290300370300200341f8006a200341c0016a41086a290300370300200320063602900120032007360294012003200436029801200320032903c001370370200320013602a001200320053702a40102402002280204450d00200041808080807836027802402004450d00200721024100210103402007200141146c6a21000240024002400240024020022d00000e0400010102040b200241086a21000c020b200041086a21000c010b200041046a21000b2000280200450d00200028020441002802c0a3c68000118080808000000b200141016a2101200241146a21022004417f6a22040d000b0b200341a0016a210202402006450d00200741002802c0a3c68000118080808000000b2002108f8780800020032802a001450d0120032802a40141002802c0a3c68000118080808000000c010b2000200341286a41880110848e8080001a0b20034190036a2480808080000be61003027f017e047f23808080800041d0046b2203248080808000200320013602d401200341003602d001200320023602cc0102400240200228020422014120490d002002200141606a36020420022002280200220141206a360200200341e0036a41086a200141086a290000370300200341e0036a41106a200141106a290000370300200341e0036a41186a200141186a290000370300200320012900003703e003200341206a200341cc016a10be8a8080002003290320a70d0020032802cc01220128020422044120490d00200329032821052001200441606a36020420012001280200220441206a36020020034180046a41086a200441086a29000037030020034180046a41106a200441106a29000037030020034180046a41186a200441186a290000370300200320042900003703800420032802cc01220128020422044120490d002001200441606a36020420012001280200220441206a360200200341a0046a41086a200441086a290000370300200341a0046a41106a200441106a290000370300200341a0046a41186a200441186a290000370300200320042900003703a004200341186a200341cc016a10bc8a80800020032802180d00200341c4046a200341cc016a200328021c10c78580800020032802c4042206418080808078460d00200341c0036a41086a200341e0036a41086a290300370300200341c0036a41106a200341e0036a41106a290300370300200341c0036a41186a200341e0036a41186a290300370300200341a0036a41086a20034180046a41086a290300370300200341a0036a41106a20034180046a41106a290300370300200341a0036a41186a20034180046a41186a290300370300200320032903e0033703c00320032003290380043703a00320032802cc04210420032802c804210720034180036a41186a200341a0046a41186a29030037030020034180036a41106a200341a0046a41106a29030037030020034180036a41086a200341a0046a41086a290300370300200320032903a00437038003200341106a200341cc016a10bc8a8080000240024020032802100d00200341a0046a200341cc016a200328021410cc8580800020032802a0042201418080808078470d010b02402004450d00200721024100210103402007200141146c6a21080240024002400240024020022d00000e0400010102040b200241086a21080c020b200841086a21080c010b200841046a21080b2008280200450d00200828020441002802c0a3c68000118080808000000b200141016a2101200241146a21022004417f6a22040d000b0b2006450d01200741002802c0a3c68000118080808000000c010b200341f8016a41086a200341c0036a41086a290300370300200341f8016a41106a200341c0036a41106a290300370300200341f8016a41186a200341c0036a41186a290300370300200341a8026a200341a0036a41086a290300370300200341b0026a200341a0036a41106a290300370300200341b8026a200341a0036a41186a290300370300200320032903c0033703f8012003200537039802200320032903a0033703a00220032902a4042105200341d8026a20034180036a41186a290300370300200341d0026a20034180036a41106a290300370300200341c8026a20034180036a41086a290300370300200320063602e002200320073602e402200320043602e80220032003290380033703c002200320013602f002200320053702f402200341086a200341cc016a10bc8a8080000240024020032802080d00200328020c2109200320032802d00141016a22083602d001200820032802d4014b0d00200341003a00c003200320093602a804200341003602a4042003200341cc016a3602a0042003200341c0036a3602ac0420034180046a200341a0046a10d28c80800020032d00c003450d0120034180046a10d38c808000200320032802d001417f6a3602d0010b02402004450d00200721024100210103402007200141146c6a21080240024002400240024020022d00000e0400010102040b200241086a21080c020b200841086a21080c010b200841046a21080b2008280200450d00200828020441002802c0a3c68000118080808000000b200141016a2101200241146a21022004417f6a22040d000b0b200341f0026a210202402006450d00200741002802c0a3c68000118080808000000b2002108f8780800020032802f002450d0120032802f40241002802c0a3c68000118080808000000c010b200341e0036a41086a20034180046a41086a2802002204360200200341ec016a2004360200200320032902800422053703e003200320053702e401200341306a200341f8016a41f80010848e8080001a200341d8016a41086a2204200341f4026a220841086a280200360200200341bc016a200341e8016a290300370200200341c4016a200341f0016a280200360200200341b4016a20042903003702002003200829020022053703d801200320053702ac01200320013602a80102402002280204450d0020004180808080783602782003419c016a28020021080240200341a0016a2802002204450d00200821024100210103402008200141146c6a21000240024002400240024020022d00000e0400010102040b200241086a21000c020b200041086a21000c010b200041046a21000b2000280200450d00200028020441002802c0a3c68000118080808000000b200141016a2101200241146a21022004417f6a22040d000b0b200341306a41f8006a21020240200328029801450d00200841002802c0a3c68000118080808000000b2002108f87808000024020032802a801450d0020032802ac0141002802c0a3c68000118080808000000b200341b8016a10d38c8080000c020b2000200341306a41980110848e8080001a0c010b20004180808080783602780b200341d0046a2480808080000bd60f02137f037e2380808080004180026b220224808080800041002d00fca3c680001a024002400240024002400240410141002802c8a3c68000118180808000002203450d00200341003a0000200241186a2204419380c6800041014100280298a3c6800011858080800000200241146a4100360200200242013702082002200336020420024101360200024020012802002203450d0020012802082205450d00200241ac016a210620024188016a41146a21072002418c016a2108200241d8006a41106a21092002410c6a210a2001280204210b410021010340024002402001450d00200b210c2003210d200121030c010b4100210c0240200b450d00200b21010240200b410771220d450d0003402001417f6a2101200328028c012103200d417f6a220d0d000b0b200b4108490d000340200328028c0128028c0128028c0128028c0128028c0128028c0128028c0128028c012103200141786a22010d000b0b4100210d0b0240200c20032f018a01490d00034020032802002201450d05200d41016a210d20032f018801210c20012103200c20012f018a014f0d000b0b200c41016a210b02400240200d0d00200321010c010b2003200b4102746a418c016a28020021014100210b200d417f6a220e450d00200d417e6a210f0240200e410771220d450d000340200e417f6a210e200128028c012101200d417f6a220d0d000b0b200f4107490d000340200128028c0128028c0128028c0128028c0128028c0128028c0128028c0128028c012101200e41786a220e0d000b0b2003200c410c6c6a220c41086a2802002103024002400240200c410c6a2802002210450d002010417f4c0d0741002d00fca3c680001a201041002802c8a3c6800011818080800000220c450d0a200c2003201010848e808000210c2002280204210d0240024020102002280208220e460d00200c41002802c0a3c68000118080808000000c010b200c200d201010888e808000210f200c41002802c0a3c6800011808080800000200f450d030b200241386a200320104100280298a3c680001185808080000041002d00fca3c680001a201041002802c8a3c68000118180808000002211450d0820112003201010848e80800021032010200e470d012003200d201010888e8080000d01200341002802c0a3c68000118080808000000c020b2002280208450d01200241386a200341004100280298a3c6800011858080800000410121110b200241d8006a41186a200241386a41186a290200370300200241d8006a41106a200241386a41106a290200370300200241d8006a41086a200241386a41086a29020037030020022002290238370358024002400240200228020c220f0d004100210f200241386a21030c010b200228021021120340200f41dc026a210c200f2f01960422134105742114417f210d4100210302400340024020142003470d002013210d0c020b200f20036a210e200341206a2103200d41016a210d200c41106a210c417f200241d8006a200e412010888e808000220e410047200e4100481b220e4101460d000b200e41ff0171450d030b02402012450d002012417f6a2112200f200d4102746a4198046a280200210f0c010b0b200dad4220862115200241d8006a21030b200720092902002216370200200241f8006a41086a200941086a2902002217370300200741086a201737020020022002280264360298012002200a36028801200220153702b0012002200f3602ac012002201637037820022003280208360294012002200329020037028c01200241013602c401200220103602c001200220113602bc01200220103602b8010240200f0d0041002d00fca3c680001a41980441002802c8a3c68000118180808000002203450d09200841086a2900002115200841106a2900002116200841186a290000211720032008290000370000200341186a2017370000200341106a2016370000200341086a2015370000200341013b0196042003410036029004200320022902b8013702e002200341e8026a200241b8016a41086a29020037020020024280808080103702102002200336020c0c020b200241c8016a41086a200641086a280200360200200220062902003703c801200241e0016a41186a200841186a290000370300200241e0016a41106a200841106a290000370300200241e0016a41086a200841086a290000370300200220082900003703e001200241d4016a200241c8016a200241e0016a200241b8016a20024188016a10bd858080002002280288012203200328020841016a3602080c010b0240200c280200220341004a0d000240200c41746a220d280200450d00200c41786a28020041002802c0a3c6800011808080800000200c28020021030b200d2010360200200c200341016a360200200c417c6a2010360200200c41786a20113602000c010b200c200341016a3602002010450d00201141002802c0a3c68000118080808000000b410021032005417f6a22050d000b0b20002002290200370200200041306a200241306a290200370200200041286a200241286a290200370200200041206a200241206a290200370200200041186a2004290200370200200041106a200241106a290200370200200041086a200241086a29020037020020024180026a2480808080000f0b4101410110b280808000000b41e887c6800010a081808000000b10ae80808000000b4101201010b280808000000b410441980410b280808000000b4101201010b280808000000bea05010a7f23808080800041106b220324808080800002400240024002402001200041186a412010888e8080000d00410121010c010b200228020021040240024002402002280204220541216a22060d0020034201370208200320063602040c010b2006417f4c0d034100210741002d00fca3c680001a200641002802c8a3c68000118180808000002208450d042003410036020c20032008360208200320063602042005415f490d010b200341046a4100200510ab868080002003280204210620032802082108200328020c21070b200820076a2004200510848e8080001a2003200720056a220536020c024020022d0008450d00200241096a2d00002102024020052006470d00200341046a2006109c8680800020032802082108200328020c21050b200820056a20023a00002003200328020c41016a220536020c200328020421060b0240200620056b411f4b0d00200341046a2005412010ab8680800020032802042106200328020c21050b2003280208220920056a22022001290000370000200241086a200141086a290000370000200241106a200141106a290000370000200241186a200141186a29000037000002400240200028020c220a0d00410021010c010b200541206a2102200041106a280200210b0340200a417c6a2107200a41b4016a2101200a2f01ba02220c410c6c2105417f2108024002400340024020050d00200c21080c020b200141086a2100200141046a2104200541746a2105200841016a2108200741106a21072001410c6a2101417f2009200428020020022000280200220020022000491b10888e8080002204200220006b20041b220041004720004100481b22004101460d000b200041ff0171450d010b0240200b0d00410021010c030b200b417f6a210b200a20084102746a41bc026a280200210a0c010b0b200728020041004a21010b2006450d00200941002802c0a3c68000118080808000000b200341106a24808080800020010f0b10ae80808000000b4101200610b280808000000b1000200020012002200310e48c8080000b0e00200020012002109b8d8080000bac0901097f23808080800041206b220224808080800002400240024002400240024002400240024002400240024020012802042203450d0020012003417f6a36020420012001280200220441016a36020020042d00000e09010000000203040005000b200041053a00000c090b2002200110bd8a80800020022802000d042001280204220420022802042203490d0402400240024020030d00410121050c010b2003417f4c0d0b200341002802c8a3c68000118180808000002205450d01200541002003108a8e8080001a0b200520012802002206200310848e80800021052001200420036b3602042001200620036a36020020002003360204200041033a000020002003ad4220862005ad843702080c090b4101200310b280808000000b20034105490d0420012003417b6a3602042001200441056a360200200441046a2d00002105200441036a2d00002106200441026a2d0000210720042d00012104200241086a200110bd8a80800020022802080d0420012802042208200228020c2203490d0441012109024002402003450d002003417f4c0d0a200341002802c8a3c68000118180808000002209450d01200941002003108a8e8080001a0b20092001280200220a200310848e8080002109200041036a20063a0000200041026a20073a0000200020043a00012001200820036b3602042001200a20036a36020020002003360208200041013a0000200041046a20053a000020002003ad4220862009ad8437020c0c080b4101200310b280808000000b20034105490d0420012003417b6a3602042001200441056a360200200441046a2d00002105200441036a2d00002106200441026a2d0000210720042d00012104200241106a200110bd8a80800020022802100d042001280204220920022802142203490d0402400240024020030d00410121080c010b2003417f4c0d09200341002802c8a3c68000118180808000002208450d01200841002003108a8e8080001a0b20082001280200220a200310848e8080002108200041036a20063a0000200041026a20073a0000200020043a00012001200920036b3602042001200a20036a36020020002003360208200041023a0000200041046a20053a000020002003ad4220862008ad8437020c0c070b4101200310b280808000000b20034105490d0420012003417b6a3602042001200441056a360200200441046a2d00002105200441036a2d00002106200441026a2d0000210720042d00012104200241186a200110bd8a80800020022802180d0420012802042209200228021c2203490d0402400240024020030d00410121080c010b2003417f4c0d08200341002802c8a3c68000118180808000002208450d01200841002003108a8e8080001a0b20082001280200220a200310848e8080002108200041036a20063a0000200041026a20073a0000200020043a00012001200920036b3602042001200a20036a36020020002003360208200041003a0000200041046a20053a000020002003ad4220862008ad8437020c0c060b4101200310b280808000000b200041043a00000c040b200041053a00000c030b200041053a00000c020b200041053a00000c010b200041053a00000b200241206a2480808080000f0b10ae80808000000bc30602087f017e23808080800041306b220224808080800002400240024002400240024002400240024002400240200128020022032802042204450d0020032004417f6a36020420032003280200220441016a36020020042d00000e09010000000203040005000b200041053a00000c090b2002200110bc8a80800020022802000d04200241246a2001200228020410d08580800020022802242203418080808078460d042000200229022837020820002003360204200041033a00000c080b2001280200220328020422054104490d042003280200220441036a2d0000210620042d0002210720042d0001210820042d000021092003200441046a36020020032005417c6a360204200241086a200110bc8a80800020022802080d04200241246a2001200228020c10d08580800020022802242203418080808078460d042002290228210a200041026a20083a0000200020093a00012000200a37020c20002003360208200041013a0000200041036a20073a0000200041046a20063a00000c070b2001280200220328020422054104490d042003280200220441036a2d0000210620042d0002210720042d0001210820042d000021092003200441046a36020020032005417c6a360204200241106a200110bc8a80800020022802100d04200241246a2001200228021410d08580800020022802242203418080808078460d042002290228210a200041026a20083a0000200020093a00012000200a37020c20002003360208200041023a0000200041036a20073a0000200041046a20063a00000c060b2001280200220328020422054104490d042003280200220441036a2d0000210620042d0002210720042d0001210820042d000021092003200441046a36020020032005417c6a360204200241186a200110bc8a80800020022802180d04200241246a2001200228021c10d08580800020022802242203418080808078460d042002290228210a200041026a20083a0000200020093a00012000200a37020c20002003360208200041003a0000200041036a20073a0000200041046a20063a00000c050b200041043a00000c040b200041053a00000c030b200041053a00000c020b200041053a00000c010b200041053a00000b200241306a2480808080000b21002001280214419c88c68000410f200141186a28020028020c118280808000000bd10a01077f024020002802002201450d0020002802042102024002400240200028020822030d00200121000c010b410021000340024002402000450d002001210420022105200021010c010b4100210402402002450d0020022100024020024107712205450d0003402000417f6a210020012802e40c21012005417f6a22050d000b0b20024108490d00034020012802e40c2802e40c2802e40c2802e40c2802e40c2802e40c2802e40c2802e40c2101200041786a22000d000b0b410021050b024002400240200520012f01e20c490d0003402001280288022200450d0220012f01e00c2105200141002802c0a3c6800011808080800000200441016a210420002101200520002f01e20c4f0d000b200021010b200541016a2102024020040d00200121000c020b200120024102746a41e40c6a2802002100410021022004417f6a2206450d012004417e6a2107024020064107712204450d0003402006417f6a210620002802e40c21002004417f6a22040d000b0b20074107490d01034020002802e40c2802e40c2802e40c2802e40c2802e40c2802e40c2802e40c2802e40c2100200641786a22060d000c020b0b200141002802c0a3c6800011808080800000418cd0c2800010a081808000000b02402001200541186c6a2204280200450d00200428020441002802c0a3c68000118080808000000b0240200428020c450d00200441106a28020041002802c0a3c68000118080808000000b2001200541fc006c6a2205418c026a21010240024020054184036a280200220541054b0d002005450d010240200128020c2204418080808078460d002004450d00200141106a28020041002802c0a3c68000118080808000000b02402001280200450d00200128020441002802c0a3c68000118080808000000b20054101460d010240200141246a2802002204418080808078460d002004450d00200141286a28020041002802c0a3c68000118080808000000b02402001280218450d002001411c6a28020041002802c0a3c68000118080808000000b20054102460d0102402001413c6a2802002204418080808078460d002004450d00200141c0006a28020041002802c0a3c68000118080808000000b02402001280230450d00200141346a28020041002802c0a3c68000118080808000000b20054103460d010240200141d4006a2802002204418080808078460d002004450d00200141d8006a28020041002802c0a3c68000118080808000000b02402001280248450d00200141cc006a28020041002802c0a3c68000118080808000000b20054104460d010240200141ec006a2802002205418080808078460d002005450d00200141f0006a28020041002802c0a3c68000118080808000000b2001280260450d01200141e4006a28020041002802c0a3c68000118080808000000c010b20012802002106024020012802042205450d0020062101034002402001410c6a2802002204418080808078460d002004450d00200141106a28020041002802c0a3c68000118080808000000b02402001280200450d00200141046a28020041002802c0a3c68000118080808000000b200141186a21012005417f6a22050d000b0b200641002802c0a3c68000118080808000000b410021012003417f6a22030d000b20000d01410021000b2002450d0002400240200241077122050d00200221010c010b2002210103402001417f6a210120002802e40c21002005417f6a22050d000b0b20024108490d00034020002802e40c2802e40c2802e40c2802e40c2802e40c2802e40c2802e40c2802e40c2100200141786a22010d000b0b03402000280288022101200041002802c0a3c68000118080808000002001210020010d000b0b0ba511010e7f024020002802002201450d0020002802042102024002400240200028020822030d00200121040c010b410021040340024002402004450d002001210520022106200421010c010b4100210502402002450d0020022100024020024107712206450d0003402000417f6a210020012802ac0921012006417f6a22060d000b0b20024108490d00034020012802ac092802ac092802ac092802ac092802ac092802ac092802ac092802ac092101200041786a22000d000b0b410021060b024002400240200620012f01aa09490d00034020012802a0082200450d0220012f01a8092106200141002802c0a3c6800011808080800000200541016a210520002101200620002f01aa094f0d000b200021010b200641016a2102024020050d00200121040c020b200120024102746a41ac096a2802002104410021022005417f6a2200450d012005417e6a2107024020004107712205450d0003402000417f6a210020042802ac0921042005417f6a22050d000b0b20074107490d01034020042802ac092802ac092802ac092802ac092802ac092802ac092802ac092802ac092104200041786a22000d000c020b0b200141002802c0a3c6800011808080800000418cd0c2800010a081808000000b024020012006410c6c6a41a4086a2200280200450d00200028020441002802c0a3c68000118080808000000b2001200641e0006c6a220810a38d80800002400240200828020c220941054b0d002009450d01200841106a210a4100210b03400240200a200b410c6c6a22002802002201450d002000280204210c024002402000280208220d450d00410021000340024002402000450d0020012105200c2106200021010c010b410021050240200c450d00200c21000240200c4107712206450d0003402000417f6a2100200128028c0121012006417f6a22060d000b0b200c4108490d000340200128028c0128028c0128028c0128028c0128028c0128028c0128028c0128028c012101200041786a22000d000b0b410021060b024002400240200620012f018a01490d00034020012802002200450d0220012f0188012106200141002802c0a3c6800011808080800000200541016a210520002101200620002f018a014f0d000b200021010b200641016a210c024020050d00200121000c020b2001200c4102746a418c016a28020021004100210c2005417f6a2207450d012005417e6a210e024020074107712205450d0003402007417f6a2107200028028c0121002005417f6a22050d000b0b200e4107490d010340200028028c0128028c0128028c0128028c0128028c0128028c0128028c0128028c012100200741786a22070d000c020b0b200141002802c0a3c6800011808080800000418cd0c2800010a081808000000b024020012006410c6c6a41046a2201280200450d00200128020441002802c0a3c68000118080808000000b41002101200d417f6a220d0d000c020b0b0240200c0d00200121000c010b02400240200c41077122060d0020012100200c21010c010b20012100200c210103402001417f6a2101200028028c0121002006417f6a22060d000b0b200c4108490d000340200028028c0128028c0128028c0128028c0128028c0128028c0128028c0128028c012100200141786a22010d000b0b034020002802002101200041002802c0a3c68000118080808000002001210020010d000b0b200b41016a220b2009470d000c020b0b200828021021090240200841146a280200220a450d004100210b034002402009200b410c6c6a22002802002201450d002000280204210c024002402000280208220d450d00410021000340024002402000450d0020012105200c2106200021010c010b410021050240200c450d00200c21000240200c4107712206450d0003402000417f6a2100200128028c0121012006417f6a22060d000b0b200c4108490d000340200128028c0128028c0128028c0128028c0128028c0128028c0128028c0128028c012101200041786a22000d000b0b410021060b024002400240200620012f018a01490d00034020012802002200450d0220012f0188012106200141002802c0a3c6800011808080800000200541016a210520002101200620002f018a014f0d000b200021010b200641016a210c024020050d00200121000c020b2001200c4102746a418c016a28020021004100210c2005417f6a2207450d012005417e6a210e024020074107712205450d0003402007417f6a2107200028028c0121002005417f6a22050d000b0b200e4107490d010340200028028c0128028c0128028c0128028c0128028c0128028c0128028c0128028c012100200741786a22070d000c020b0b200141002802c0a3c6800011808080800000418cd0c2800010a081808000000b024020012006410c6c6a41046a2201280200450d00200128020441002802c0a3c68000118080808000000b41002101200d417f6a220d0d000c020b0b0240200c0d00200121000c010b02400240200c41077122060d0020012100200c21010c010b20012100200c210103402001417f6a2101200028028c0121002006417f6a22060d000b0b200c4108490d000340200028028c0128028c0128028c0128028c0128028c0128028c0128028c0128028c012100200141786a22010d000b0b034020002802002101200041002802c0a3c68000118080808000002001210020010d000b0b200b41016a220b200a470d000b0b200941002802c0a3c68000118080808000000b02402008280254450d00200841d8006a28020041002802c0a3c68000118080808000000b410021012003417f6a22030d000b20040d01410021040b2002450d0002400240200241077122000d00200221010c010b2002210103402001417f6a210120042802ac0921042000417f6a22000d000b0b20024108490d00034020042802ac092802ac092802ac092802ac092802ac092802ac092802ac092802ac092104200141786a22010d000b0b034020042802a0082101200441002802c0a3c68000118080808000002001210420010d000b0b0bba0501077f024020002802002201450d00200028020421020240024020002802082203450d00410021000340024002402000450d002001210420022105200021010c010b4100210402402002450d0020022100024020024107712205450d0003402000417f6a210020012802ac1421012005417f6a22050d000b0b20024108490d00034020012802ac142802ac142802ac142802ac142802ac142802ac142802ac142802ac142101200041786a22000d000b0b410021050b024002400240200520012f01aa14490d00034020012802a0132200450d0220012f01a8142105200141002802c0a3c6800011808080800000200441016a210420002101200520002f01aa144f0d000b200021010b200541016a2102024020040d00200121000c020b200120024102746a41ac146a2802002100410021022004417f6a2206450d012004417e6a2107024020064107712204450d0003402006417f6a210620002802ac1421002004417f6a22040d000b0b20074107490d01034020002802ac142802ac142802ac142802ac142802ac142802ac142802ac142802ac142100200641786a22060d000c020b0b200141002802c0a3c6800011808080800000418cd0c2800010a081808000000b024020012005410c6c6a41a4136a2204280200450d00200428020441002802c0a3c68000118080808000000b2001200541e0016c6a108e87808000410021012003417f6a22030d000c020b0b024020020d00200121000c010b02400240200241077122050d0020012100200221010c010b200121002002210103402001417f6a210120002802ac1421002005417f6a22050d000b0b20024108490d00034020002802ac142802ac142802ac142802ac142802ac142802ac142802ac142802ac142100200141786a22010d000b0b034020002802a0132101200041002802c0a3c68000118080808000002001210020010d000b0b0ba60601097f2000280200210102400240024020002802202202450d0020002802042103034020002002417f6a220236022002400240024002400240024002402001450d0020030d0020002802082104200028020c2205450d03200541077122060d01200521030c020b2001450d04200028020c210620002802082105200321040c030b2005210303402003417f6a210320042802f80621042006417f6a22060d000b0b20054108490d00034020042802f8062802f8062802f8062802f8062802f8062802f8062802f8062802f8062104200341786a22030d000b0b2000420037020820002004360204410121012000410136020041002106410021050b02400240200620042f01f606490d00034020042802f0062203450d0220042f01f4062106200441002802c0a3c6800011808080800000200541016a210520032104200620032f01f6064f0d000b200321040b200641016a2107024020050d00200421030c030b200420074102746a41f8066a2802002103410021072005417f6a2208450d022005417e6a2109024020084107712205450d0003402008417f6a210820032802f80621032005417f6a22050d000b0b20094107490d02034020032802f8062802f8062802f8062802f8062802f8062802f8062802f8062802f8062103200841786a22080d000c030b0b200441002802c0a3c6800011808080800000418cd0c2800010a081808000000b41d887c6800010a081808000000b2000200736020c200041003602082000200336020402402004200641d0006c6a220441c8006a2802004129490d00200441206a28020041002802c0a3c68000118080808000000b20020d000b200041003602000c010b200041003602002001450d01200028020422030d0020002802082103200028020c2205450d0002400240200541077122060d00200521040c010b2005210403402004417f6a210420032802f80621032006417f6a22060d000b0b20054108490d00034020032802f8062802f8062802f8062802f8062802f8062802f8062802f8062802f8062103200441786a22040d000b0b034020032802f0062104200341002802c0a3c68000118080808000002004210320040d000b0b0bab0501077f024020002802002201450d00200028020421020240024020002802082203450d00410021000340024002402000450d002001210420022105200021010c010b4100210402402002450d0020022100024020024107712205450d0003402000417f6a210020012802980421012005417f6a22050d000b0b20024108490d000340200128029804280298042802980428029804280298042802980428029804280298042101200041786a22000d000b0b410021050b024002400240200520012f019604490d0003402001280290042200450d0220012f0194042105200141002802c0a3c6800011808080800000200441016a210420002101200520002f0196044f0d000b200021010b200541016a2102024020040d00200121000c020b200120024102746a4198046a2802002100410021022004417f6a2206450d012004417e6a2107024020064107712204450d0003402006417f6a210620002802980421002004417f6a22040d000b0b20074107490d010340200028029804280298042802980428029804280298042802980428029804280298042100200641786a22060d000c020b0b200141002802c0a3c6800011808080800000418cd0c2800010a081808000000b0240200120054104746a41e0026a2201280200450d00200128020441002802c0a3c68000118080808000000b410021012003417f6a22030d000c020b0b024020020d00200121000c010b02400240200241077122050d0020012100200221010c010b200121002002210103402001417f6a210120002802980421002005417f6a22050d000b0b20024108490d000340200028029804280298042802980428029804280298042802980428029804280298042100200141786a22010d000b0b03402000280290042101200041002802c0a3c68000118080808000002001210020010d000b0b0ba80501077f024020002802002201450d00200028020421020240024020002802082203450d00410021000340024002402000450d002001210420022105200021010c010b4100210402402002450d0020022100024020024107712205450d0003402000417f6a2100200128028c0121012005417f6a22050d000b0b20024108490d000340200128028c0128028c0128028c0128028c0128028c0128028c0128028c0128028c012101200041786a22000d000b0b410021050b024002400240200520012f018a01490d00034020012802002200450d0220012f0188012105200141002802c0a3c6800011808080800000200441016a210420002101200520002f018a014f0d000b200021010b200541016a2102024020040d00200121000c020b200120024102746a418c016a2802002100410021022004417f6a2206450d012004417e6a2107024020064107712204450d0003402006417f6a2106200028028c0121002004417f6a22040d000b0b20074107490d010340200028028c0128028c0128028c0128028c0128028c0128028c0128028c0128028c012100200641786a22060d000c020b0b200141002802c0a3c6800011808080800000418cd0c2800010a081808000000b024020012005410c6c6a41046a2201280200450d00200128020441002802c0a3c68000118080808000000b410021012003417f6a22030d000c020b0b024020020d00200121000c010b02400240200241077122050d0020012100200221010c010b200121002002210103402001417f6a2101200028028c0121002005417f6a22050d000b0b20024108490d000340200028028c0128028c0128028c0128028c0128028c0128028c0128028c0128028c012100200141786a22010d000b0b034020002802002101200041002802c0a3c68000118080808000002001210020010d000b0b0bca0501077f024020002802002201450d00200028020421020240024020002802082203450d00410021000340024002402000450d002001210420022105200021010c010b4100210402402002450d0020022100024020024107712205450d0003402000417f6a210020012802900221012005417f6a22050d000b0b20024108490d000340200128029002280290022802900228029002280290022802900228029002280290022101200041786a22000d000b0b410021050b024002400240200520012f018e02490d0003402001280288022200450d0220012f018c022105200141002802c0a3c6800011808080800000200441016a210420002101200520002f018e024f0d000b200021010b200541016a2102024020040d00200121000c020b200120024102746a4190026a2802002100410021022004417f6a2206450d012004417e6a2107024020064107712204450d0003402006417f6a210620002802900221002004417f6a22040d000b0b20074107490d010340200028029002280290022802900228029002280290022802900228029002280290022100200641786a22060d000c020b0b200141002802c0a3c6800011808080800000418cd0c2800010a081808000000b02402001200541186c6a2201280200450d00200128020441002802c0a3c68000118080808000000b0240200128020c450d00200141106a28020041002802c0a3c68000118080808000000b410021012003417f6a22030d000c020b0b024020020d00200121000c010b02400240200241077122050d0020012100200221010c010b200121002002210103402001417f6a210120002802900221002005417f6a22050d000b0b20024108490d000340200028029002280290022802900228029002280290022802900228029002280290022100200141786a22010d000b0b03402000280288022101200041002802c0a3c68000118080808000002001210020010d000b0b0bfb0401057f024020002802002201450d00200028020421020240024020002802082203450d00410021000340024002402000450d0020012104200021010c010b4100210402402002450d0020022100024020024107712205450d0003402000417f6a210020012802e80221012005417f6a22050d000b0b20024108490d00034020012802e8022802e8022802e8022802e8022802e8022802e8022802e8022802e8022101200041786a22000d000b0b410021020b024002400240200220012f01e602490d00034020012802e0022200450d0220012f01e4022102200141002802c0a3c6800011808080800000200441016a210420002101200220002f01e6024f0d000b200021010b200241016a2102024020040d00200121000c020b200120024102746a41e8026a2802002100410021022004417f6a2201450d012004417e6a2105024020014107712204450d0003402001417f6a210120002802e80221002004417f6a22040d000b0b20054107490d01034020002802e8022802e8022802e8022802e8022802e8022802e8022802e8022802e8022100200141786a22010d000c020b0b200141002802c0a3c6800011808080800000418cd0c2800010a081808000000b410021012003417f6a22030d000c020b0b024020020d00200121000c010b02400240200241077122040d0020012100200221010c010b200121002002210103402001417f6a210120002802e80221002004417f6a22040d000b0b20024108490d00034020002802e8022802e8022802e8022802e8022802e8022802e8022802e8022802e8022100200141786a22010d000b0b034020002802e0022101200041002802c0a3c68000118080808000002001210020010d000b0b0bc40501077f024020002802002201450d00200028020421020240024020002802082203450d00410021000340024002402000450d002001210420022105200021010c010b4100210402402002450d0020022100024020024107712205450d0003402000417f6a210020012802900221012005417f6a22050d000b0b20024108490d000340200128029002280290022802900228029002280290022802900228029002280290022101200041786a22000d000b0b410021050b024002400240200520012f018e02490d0003402001280288022200450d0220012f018c022105200141002802c0a3c6800011808080800000200441016a210420002101200520002f018e024f0d000b200021010b200541016a2102024020040d00200121000c020b200120024102746a4190026a2802002100410021022004417f6a2206450d012004417e6a2107024020064107712204450d0003402006417f6a210620002802900221002004417f6a22040d000b0b20074107490d010340200028029002280290022802900228029002280290022802900228029002280290022100200641786a22060d000c020b0b200141002802c0a3c6800011808080800000418cd0c2800010a081808000000b200120054103746a220141b0016a2802002205200141b4016a28020022012802001180808080000002402001280204450d00200541002802c0a3c68000118080808000000b410021012003417f6a22030d000c020b0b024020020d00200121000c010b02400240200241077122050d0020012100200221010c010b200121002002210103402001417f6a210120002802900221002005417f6a22050d000b0b20024108490d000340200028029002280290022802900228029002280290022802900228029002280290022100200141786a22010d000b0b03402000280288022101200041002802c0a3c68000118080808000002001210020010d000b0b0bca08010a7f23808080800041306b22012480808080002000280200210202400240024020002802202203450d0020002802042104034020002003417f6a220336022002400240024002400240024002402002450d0020040d0020002802082105200028020c2206450d03200641077122070d01200621040c020b2002450d04200028020c210720002802082106200421050c030b2006210403402004417f6a210420052802bc0221052007417f6a22070d000b0b20064108490d00034020052802bc022802bc022802bc022802bc022802bc022802bc022802bc022802bc022105200441786a22040d000b0b2000420037020820002005360204410121022000410136020041002107410021060b02400240200720052f01ba02490d00034020052802b0012204450d0220052f01b8022107200541002802c0a3c6800011808080800000200641016a210620042105200720042f01ba024f0d000b200421050b200741016a2108024020060d00200521040c030b200520084102746a41bc026a2802002104410021082006417f6a2209450d022006417e6a210a024020094107712206450d0003402009417f6a210920042802bc0221042006417f6a22060d000b0b200a4107490d02034020042802bc022802bc022802bc022802bc022802bc022802bc022802bc022802bc022104200941786a22090d000c030b0b200541002802c0a3c6800011808080800000418cd0c2800010a081808000000b41d887c6800010a081808000000b2000200836020c2000410036020820002004360204024020052007410c6c6a41b4016a2206280200450d00200628020441002802c0a3c68000118080808000000b02400240024002400240200520074104746a22052d00000e050404010203000b02400240200528020422070d0041002105410021060c010b2005410c6a28020021062001200541086a28020022053602282001200736022420014100360220200120053602182001200736021420014100360210410121050b2001200636022c2001200536021c2001200536020c2001410c6a10aa8d8080000c030b2005280204450d02200541086a28020041002802c0a3c68000118080808000000c020b2005280204450d01200541086a28020041002802c0a3c68000118080808000000c010b200541046a1091878080002005280204450d00200541086a28020041002802c0a3c68000118080808000000b20030d000b200041003602000c010b200041003602002002450d01200028020422040d0020002802082104200028020c2206450d0002400240200641077122070d00200621050c010b2006210503402005417f6a210520042802bc0221042007417f6a22070d000b0b20064108490d00034020042802bc022802bc022802bc022802bc022802bc022802bc022802bc022802bc022104200541786a22050d000b0b034020042802b0012105200441002802c0a3c68000118080808000002005210420050d000b0b200141306a2480808080000bc50601097f2000280200210102400240024020002802202202450d0020002802042103034020002002417f6a220236022002400240024002400240024002402001450d0020030d0020002802082104200028020c2205450d03200541077122060d01200521030c020b2001450d04200028020c210620002802082105200321040c030b2005210303402003417f6a210320042802900221042006417f6a22060d000b0b20054108490d000340200428029002280290022802900228029002280290022802900228029002280290022104200341786a22030d000b0b2000420037020820002004360204410121012000410136020041002106410021050b02400240200620042f018e02490d00034020042802002203450d0220042f018c022106200441002802c0a3c6800011808080800000200541016a210520032104200620032f018e024f0d000b200321040b200641016a2107024020050d00200421030c030b200420074102746a4190026a2802002103410021072005417f6a2208450d022005417e6a2109024020084107712205450d0003402008417f6a210820032802900221032005417f6a22050d000b0b20094107490d020340200328029002280290022802900228029002280290022802900228029002280290022103200841786a22080d000c030b0b200441002802c0a3c6800011808080800000418cd0c2800010a081808000000b41d887c6800010a081808000000b2000200736020c2000410036020820002003360204024020042006410c6c6a220441046a2206280200450d00200628020441002802c0a3c68000118080808000000b024020044188016a2204280200450d00200428020441002802c0a3c68000118080808000000b20020d000b200041003602000c010b200041003602002001450d01200028020422030d0020002802082103200028020c2205450d0002400240200541077122060d00200521040c010b2005210403402004417f6a210420032802900221032006417f6a22060d000b0b20054108490d000340200328029002280290022802900228029002280290022802900228029002280290022103200441786a22040d000b0b034020032802002104200341002802c0a3c68000118080808000002004210320040d000b0b0be10e04147f017e047f017e2380808080004180016b2202248080808000200128020c21032001410036020c200141146a2204280200210520044100360200200141106a280200210620022005410020031b2204360254200220063602502002200336024c20024100360248200220034100472207360244200220063602402002200336023c200241003602382002200736023402400240024002402004450d002000410c6a2108200241046a41106a2109200241046a410c6a210a41002104034020022005417f6a22053602540240024002402007450d002004450d010b20070d0141d887c6800010a081808000000b0240024020060d00200321040c010b024002402006410771220b0d00200321042006210c0c010b200321042006210c0340200c417f6a210c20042802bc022104200b417f6a220b0d000b0b20064108490d00034020042802bc022802bc022802bc022802bc022802bc022802bc022802bc022802bc022104200c41786a220c0d000b0b2002420037023c20022004360238410121072002410136023441002106410021030b024002400240200620042f01ba02490d00034020042802b001220c450d0220042f01b8022106200441002802c0a3c6800011808080800000200341016a2103200c21042006200c2f01ba024f0d000b200c21040b2004210b2006220d41016a2106024020030d00200b21040c020b200b20064102746a41bc026a2802002104410021062003417f6a220c450d012003417e6a210e0240200c4107712203450d000340200c417f6a210c20042802bc0221042003417f6a22030d000b0b200e4107490d01034020042802bc022802bc022802bc022802bc022802bc022802bc022802bc022802bc022104200c41786a220c0d000c020b0b200441002802c0a3c6800011808080800000418cd0c2800010a081808000000b200220063602402002410036023c20022004360238200b200d410c6c6a220341b4016a280200220f418080808078460d04200b200d4104746a220c28020c2110200c2802082111200c2802042112200c2802002113200341bc016a280200210b200341b8016a280200211402400240024002400240200828020022150d00200bad4220862014ad842116410021150c010b2000280210211702400340201541b4016a210c20152f01ba022218410c6c2103417f210e41002119024002400340024020030d002018210e0c020b200c41086a210d200c41046a211a200341746a2103201941106a2119200e41016a210e200c410c6a210c417f2014201a280200200b200d280200220d200b200d491b10888e808000221a200b200d6b201a1b220d410047200d4100481b220d4101460d000b200d41ff0171450d010b2017450d022017417f6a21172015200e4102746a41bc026a28020021150c010b0b2002201736022420022015360220200229032021160240200f450d00201441002802c0a3c68000118080808000000b2016a720196a220c417c6a2203280200220b417f4a0d020240200c41706a220d280200450d00200c41746a28020041002802c0a3c68000118080808000002003280200210b0b200d2013360200200c41786a2011360200200c41746a20123602002003200b20106a3602000c040b2002200e36022820024100360224200bad4220862014ad8421162002290224211b0b2002201b3702182002201536021420022008360210200220163702082002200f3602042002201036022c200220113602282002201236022420022013360220024020150d0041002d00fca3c680001a41bc0241002802c8a3c6800011818080800000220c450d02200c41013b01ba02200c41003602b001200c20022902043702b401200241046a41086a2802002103200c2002290220370200200c41086a200241206a41086a290200370200200c41bc016a200336020020004280808080103702102000200c36020c0c030b200241d8006a41086a200941086a28020036020020022009290200370358200241f0006a41086a200241046a41086a28020036020020022002290204370370200241e4006a200241d8006a200241f0006a200241206a200a10c0858080002002280210220c200c28020841016a3602080c020b2003200b20106a3602002013450d01201241002802c0a3c68000118080808000000c010b410441bc0210b280808000000b4100210320050d000b41002103200241003602342007450d032004450d010c020b200241003602342003450d020b024020060d00200321040c010b024002402006410771220b0d00200321042006210c0c010b200321042006210c0340200c417f6a210c20042802bc022104200b417f6a220b0d000b0b20064108490d00034020042802bc022802bc022802bc022802bc022802bc022802bc022802bc022802bc022104200c41786a220c0d000b0b034020042802b001210c200441002802c0a3c6800011808080800000200c2104200c0d000b0b200241346a10928d808000200241003602542002410036024420024100360234200241346a10928d80800002402001280200450d00200128020441002802c0a3c68000118080808000000b20024180016a2480808080000b1901017f23808080800041106b220120003a000f20012d000f0b3c01017f23808080800041106b22032480808080002003200239030820002001200341086a41ac88c6800010af8d808000200341106a2480808080000bd90402017f017e23808080800041c0006b22042480808080002004200336020c2004200236020802400240024002400240024020002d0004450d00200041003a000420012802102203200128020422024f0d04200128020020034103746a220128020021030240200128020422024107470d00200341d489c68000410710888e808000450d020b20002802002101200441106a410c6a4202370200200441286a410c6a41928480800036020020044102360214200441dc89c68000360210200441938480800036022c2004200236023c20042003360238200141186a28020021032004200441286a3602182004200441086a3602302004200441386a36022820012802142003200441106a10d9808080000d020c030b20012802102202200128020422034f0d0420002802002103200441106a410c6a4202370200200128020020024103746a2902002105200441286a410c6a41928480800036020020044102360214200441c489c6800036021020042005370238200441938480800036022c200341186a28020021012004200441286a3602182004200441086a3602302004200441386a36022820032802142001200441106a10d9808080000d010c020b200028020021012004411c6a4201370200200441013602142004418c89c68000360210200441928480800036022c200141186a28020021032004200441286a3602182004200441086a36022820012802142003200441106a10d980808000450d010b200041013a00050b200441c0006a2480808080000f0b2003200241e88ec6800010f980808000000b2002200341e88ec6800010f980808000000b3c01017f23808080800041106b22032480808080002003200237030820002001200341086a41bc88c6800010af8d808000200341106a2480808080000b3c01017f23808080800041106b22032480808080002003200237030820002001200341086a41cc88c6800010af8d808000200341106a2480808080000b3c01017f23808080800041106b2203248080808000200320023a000f200020012003410f6a41dc88c6800010af8d808000200341106a2480808080000b4001017f23808080800041106b2204248080808000200420033703082004200237030020002001200441ec88c6800010af8d808000200441106a2480808080000b4001017f23808080800041106b2204248080808000200420033703082004200237030020002001200441fc88c6800010af8d808000200441106a2480808080000b2d00024020002d00000d00200141a891c08000410510dd808080000f0b200141ad91c08000410410dd808080000b140020002802002000280204200110e2808080000b180020002802002001200028020428020c118380808000000b140020012000280200200028020410dd808080000b810302027f027e2380808080004180016b220224808080800002400240024002400240200128021c22034110710d0020034120710d01200029030022042004423f8722058520057d2004427f55200110fe8080800021000c020b20002903002104410021000340200220006a41ff006a413041d7002004a7410f712203410a491b20036a3a00002000417f6a210020044210542103200442048821042003450d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000c010b20002903002104410021000340200220006a41ff006a413041372004a7410f712203410a491b20036a3a00002000417f6a210020044210542103200442048821042003450d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000b20024180016a24808080800020000f0b200341800141c88dc08000109481808000000b200341800141c88dc08000109481808000000bf10202027f017e2380808080004180016b220224808080800002400240024002400240200128021c22034110710d0020034120710d0120002903004101200110fe8080800021000c020b20002903002104410021000340200220006a41ff006a413041d7002004a7410f712203410a491b20036a3a00002000417f6a210020044210542103200442048821042003450d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000c010b20002903002104410021000340200220006a41ff006a413041372004a7410f712203410a491b20036a3a00002000417f6a210020044210542103200442048821042003450d000b20004180016a22034180014b0d022001410141c48dc080004102200220006a4180016a410020006b10db8080800021000b20024180016a24808080800020000f0b200341800141c88dc08000109481808000000b200341800141c88dc08000109481808000000bfb0404027f027e037f017e2380808080004180016b22022480808080000240024002400240024002400240200128021c22034110710d0020034120710d014200200029030022047d2004200041086a290300220542005322001b420020052004420052ad7c7d200520001b2005427f55200110d48080800021000c050b200041086a290300210420002903002105418001210020024180016a21034100210603402000450d042003417f6a413041d7002005a72207410f712208410a491b20086a3a00002005421054410020045022081b0d022003417e6a2203413041d700200741ff0171220741a001491b20074104766a3a00002004423886210920054280025421072000417e6a210020044208882104200920054208888421052007410020081b0d030c000b0b200041086a290300210420002903002105418001210020024180016a21034100210602400240024003402000450d022003417f6a413041372005a72207410f712208410a491b20086a3a000002402005421054410020045022081b0d002003417e6a220341304137200741ff0171220741a001491b20074104766a3a00002004423886210920054280025421072000417e6a210020044208882104200920054208888421052007410020081b0d020c010b0b2000417f6a21000b20004180014b0d01200021060b2001410141c48dc080004102200220066a41800120066b10db8080800021000c040b200041800141c88dc08000109481808000000b2000417f6a21000b20004180014b0d02200021060b2001410141c48dc080004102200220066a41800120066b10db8080800021000b20024180016a24808080800020000f0b200041800141c88dc08000109481808000000bd80404027f027e037f017e2380808080004180016b22022480808080000240024002400240024002400240200128021c22034110710d0020034120710d012000290300200041086a2903004101200110d48080800021000c050b200041086a290300210420002903002105418001210020024180016a21034100210603402000450d042003417f6a413041d7002005a72207410f712208410a491b20086a3a00002005421054410020045022081b0d022003417e6a2203413041d700200741ff0171220741a001491b20074104766a3a00002004423886210920054280025421072000417e6a210020044208882104200920054208888421052007410020081b0d030c000b0b200041086a290300210420002903002105418001210020024180016a21034100210602400240024003402000450d022003417f6a413041372005a72207410f712208410a491b20086a3a000002402005421054410020045022081b0d002003417e6a220341304137200741ff0171220741a001491b20074104766a3a00002004423886210920054280025421072000417e6a210020044208882104200920054208888421052007410020081b0d020c010b0b2000417f6a21000b20004180014b0d01200021060b2001410141c48dc080004102200220066a41800120066b10db8080800021000c040b200041800141c88dc08000109481808000000b2000417f6a21000b20004180014b0d02200021060b2001410141c48dc080004102200220066a41800120066b10db8080800021000b20024180016a24808080800020000f0b200041800141c88dc08000109481808000000b02000b02000b02000b900202017f057e23808080800041e0006b22052480808080002005410c6a41386a42013702002005410c6a41306a4101360200200541346a20032802083602002005418c89c68000360238200541c0006a200541d0006a3602002005200329020037022c200041306a3502002106200041386a350200210720002902002108200035022c21092000350234210a200541a083808000360254200541013a005c200520043602582005200541d8006a360250200541246a200a200742208684370200200541186a2009200642208684370200200541024101200a501b3602202005410241012009501b3602142005200837020c20012005410c6a200228021011848080800000200541e0006a2480808080000bc50101047f23808080800041106b220224808080800041002103200241003a000d200220002d00043a000c200220013602080240200028020022012802042204450d0020012802002100200128020828020821052004410c6c210303400240200028020022012802082005470d00200041046a2802002204450d0020042001200241086a419489c68000200041086a28020028020c118680808000000b2000410c6a2100200341746a22030d000b20022d000d41004721030b200241106a24808080800020030bef0101017f23808080800041306b22042480808080002004200336020c20042002360208024020012802102203200128020422024f0d00024002400240200128020020034103746a22032802044107470d00200328020041d489c68000410710888e808000450d010b20002001200441086a41ec89c6800010af8d8080000c010b2004411c6a4201370200200441013602142004418c89c68000360210200441938480800036022c2004200441286a3602182004200441086a36022820002001200441106a41fc89c6800010af8d8080000b200441306a2480808080000f0b2003200241e88ec6800010f980808000000b8b0201067f2380808080004180016b2202248080808000200128020421032001280200210420002802002802002100200128021c2205210602402005410471450d002005410872210620040d0020014281808080a0013702000b2001200641047236021c410021060340200220066a41ff006a413041d7002000410f712207410a491b20076a3a00002006417f6a210620004110492107200041047621002007450d000b024020064180016a22004180014d0d00200041800141c88dc08000109481808000000b2001410141c48dc080004102200220066a4180016a410020066b10db8080800021002001200536021c200120033602042001200436020020024180016a24808080800020000b02000bfc0201047f23808080800041206b220124808080800041012102200020002d00092203410120031b3a0009024002400240024020030d004100210302404100280294a4c680004102470d002000280200210441002802f4a3c68000210241002802f0a3c680002103024041002802eca3c68000450d002002280208417f6a41787120036a41086a21030b41022003200420022802101183808080000041ff0171220341004720034102461b21030b200020033a00084100280288a4c680002103034020002003360204200120033602002001200036020420032000460d04410020004100280288a4c680002202200220034622041b360288a4c68000200221032004450d000b200041023a00090c010b20034102470d010b4102410120002d000822034102461b410020031b21020b200141206a24808080800020020f0b200142003702142001418c8ac680003602102001410136020c200141888cc680003602084101200141046a2001200141086a41f88cc6800010d88d808000000b1c00200041024101200141ff017122014102461b410020011b3a00080b1a0020022001200041888dc680002003280228118680808000000b040041000b2a0020002001360204200020024293c3cef2b89ddac72285200342ebaebfe0a6e7f0cf028584503602000b060042adbd030b02000b02000b040041060b040041010b070020012903000b02000b0900200041023602000b040041000b02000b02000b040041000b02000b02000b4601017f23808080800041106b22052480808080002005200236020c200520013602082000200541086a41e48dc680002005410c6a41f48dc680002003200410fb80808000000bd40101057f23808080800041306b22022480808080000240200128020822034101762204200128020422054b0d00200128020021012002412c6a22064100360200200241046a200120046a200120056a10f58d8080002000412c6a2006280200360200200041246a200241246a2902003702002000411c6a2002411c6a290200370200200041146a200241146a2902003702002000410c6a2002410c6a2902003702002000200229020437020420002003410171360200200241306a2480808080000f0b2004200541e491c68000109481808000000bb20601067f23808080800041306b220324808080800020012802002104024002400240024002400240024002400240024002400240024020012802042205410174200128020822016b20024d0d0020014101762106200120026a2207410176210820074101710d0320082006490d06200820054b0d07200420066a2105200820066b22024129490d0141002d00fca3c680001a200241002802c8a3c68000118180808000002204450d0820042005200210848e8080002105200041086a2002360200200020053602040c020b2001410176220220054b0d04200341286a220641003602002003200420026a200420056a10f58d8080002000412c6a2006280200360200200041246a200341206a2902003702002000411c6a200341186a290200370200200041146a200341106a2902003702002000410c6a200341086a29020037020020002003290200370204200020014101713602000c030b200041046a2005200210848e8080001a0b200020014101713602002000412c6a20023602000c010b200841016a22072006490d05200820054f0d06200420066a210402400240200720066b22054129490d002005417f4c0d0941002d00fca3c680001a200541002802c8a3c68000118180808000002206450d0a20062004200510848e8080002104200341086a2005360200200320043602040c010b200341046a2004200510848e8080001a0b2003412c6a22042005360200200320014101713602002003200241017110e88d8080001a024020034108412c200428020041284b1b6a22012802002202450d0020012002417f6a3602000b20002003290200370200200041286a200341286a290200370200200041206a200341206a290200370200200041186a200341186a290200370200200041106a200341106a290200370200200041086a200341086a2902003702000b200341306a2480808080000f0b2002200541e491c68000109481808000000b2006200841f491c68000109681808000000b2008200541f491c68000109581808000000b4101200210b280808000000b20062007418492c68000109681808000000b20072005418492c68000109581808000000b10ae80808000000b4101200510b280808000000bc204010a7f0240024002400240024002400240024020002802082202410171220320012802082204410171460d0002402000280204220541017420026b22032001280204220641017420046b220720032007491b22080d0041000f0b200128020021092000280200210a410021030340200220036a2200410176220120054f0d03200420036a220b410176220720064f0d04200a20016a2d00002201410f71200141047620004101711b200920076a2d00002201410f712001410476200b4101711b470d022008200341016a2203470d000b20080f0b200441017621042002410176210b200028020421024100210502402003450d00200b20024f0d042004200128020422034f0d0541002103200128020020046a2d00002000280200200b6a2d000073410f710d0141012105200441016a2104200b41016a210b0b2002200b490d05200128020422032004490d06200128020020046a21012000280200200b6a2100410221072002200b6b220b200320046b2203200b2003491b220421030340024020030d00200441017420056a0f0b2003417f6a21032007417e6a210720012d0000210b20002d00002102200041016a2100200141016a21012002200b460d000b200b20027341104920076b20056a21030b20030f0b2001200541e890c6800010f980808000000b2007200641e890c6800010f980808000000b200b2002419492c6800010f980808000000b2004200341a492c6800010f980808000000b200b200241c492c68000109481808000000b2004200341b492c68000109481808000000bb10101047f200128020822024101762103200128020421042001280200210502400240024020024101710d00410021020240200320044b0d0020032101410021030c020b2003200441d492c68000109481808000000b200320044f0d0141012102200341016a2101200520036a2d0000410f7121030b200020033a000d200020023a000c200041003602082000200420016b3602042000200520016a3602000f0b2003200441e492c6800010f980808000000bbf0101027f20012802082202410176210302400240024020024101710d002003200128020422024b0d01200041003a000820002003360204200020012802003602000f0b2003200128020422024b0d010240200320024f0d0020012802002101200041013a00082000200336020420002001360200200041096a200120036a2d000041f001713a00000f0b20032002419493c6800010f980808000000b2003200241f492c68000109581808000000b20032002418493c68000109581808000000bda02010b7f23808080800041106b2202248080808000410021030240024020002802042204410174200028020822056b200128022c2206490d002001280228210702402006410171450d002000280200210820012802002001200741284b22001b21092001280204200720001b210a410021010340200620014622030d020240200520016a2207410176220020044f0d00200a2001410176220b4d0d042001410171210c200141016a2101200820006a2d00002200410f71200041047620074101711b2009200b6a2d00002200410f712000410476200c1b460d010c030b0b2000200441e890c6800010f980808000000b2001280204210b2001280200210c2002410036020c2002200c2001200741284b22031b3602042002200b200720031b22013602082000200241046a10db8d80800020014101744621030b200241106a24808080800020030f0b200b200a418c94c6800010f980808000000be902010c7f23808080800041106b22022480808080004100210302400240024020002802042204410174200028020822056b2206200128022c470d002001280204210702402006410171450d00200128020020012001280228220841284b22091b210a2007200820091b210b2000280200210c410021010340200141016a220020064b22030d02200520016a2208410176220720044f0d03200b200141017622094d0d042001410171210d20002101200a20096a2d00002200410f712000410476200d1b200c20076a2d00002200410f71200041047620084101711b460d000c020b0b2001280200210920012802282108410021032002410036020c200220092001200841284b220d1b360204200220072008200d1b220136020820062001410174470d002000200241046a10db8d80800020064621030b200241106a24808080800020030f0b2007200441e890c6800010f980808000000b2009200b418c94c6800010f980808000000b8d0301087f024002402001450d000240200028022c220220014d0d00200041046a2103200041286a21040240200220016b22054101762202200541017122066a2207200028020420002802282201200141284b1b22014f0d0020012002417f736a210802402001200220056a6b410171450d00200741016a210720032004200428020041284b1b22022802002209450d0020022009417f6a3602000b20082006460d00200120076b21010340024020032004200428020041284b1b22022802002207450d0020022007417f6a3602000b024020032004200428020041284b1b22022802002207450d0020022007417f6a3602000b2001417e6a22010d000b0b2000200536022c2006450d01200328020020042802002204200441284b22011b2204417f6a210320040d0220034100419c95c6800010f980808000000b0240200041044128200028022841284b22041b6a280200450d00200041046a200041286a20041b41003602000b2000410036022c0b0f0b2000280200200020011b20036a220420042d000041f001713a00000be805010c7f0240200128022c2202450d0002400240200028022c22034101710d00200128020420012802282203200341284b22031b2204450d012001280200200120031b2103200041046a2105200041286a2106034020032d00002107024002402000410441282000280228220841284b22011b6a28020022092008412820011b460d002005200620011b21082000280200200020011b21010c010b200010f78d8080002000280204210920002802002101200521080b200120096a20073a00002008200828020041016a360200200341016a21032004417f6a22040d000c020b0b024002400240200028020420002802282208200841284b1b2209200341017622044d0d00200128020420012802282209200941284b1b2206450d01200041046a2105200041286a210a200320026a410171210b20002802002000200841284b1b20046a220320012802002001200941284b1b220c2d000041047620032d000041f00171723a000002402006417f6a220d450d0041002101034020062001460d04200c20016a22032d00002104200341016a2d00002107024002402000410441282000280228220841284b22031b6a28020022092008412820031b460d002005200a20031b21082000280200200020031b21030c010b200010f78d8080002000280204210920002802002103200521080b200320096a20044104742007410476410f71723a00002008200828020041016a360200200d200141016a2201470d000b0b200b450d03200c200d6a2d00004104742108024002402000410441282000280228220941284b22011b6a28020022032009412820011b460d002005200a20011b21052000280200200020011b21010c010b200010f78d80800020002802042103200028020021010b200120036a20083a00002005200528020041016a3602000c030b2004200941dc95c6800010f980808000000b4100410041ec95c6800010f980808000000b2006200641fc95c6800010f980808000000b2000200028022c20026a36022c0b0be808010b7f23808080800041106b2202248080808000200141086a2802002103200128020421040240024002400240024002400240024020012d00004101460d00200028022c21050c010b20012d0001410f7121060240024020002d002c4101710d00200041046a210120064104742107024002402000410441282000280228220841284b22061b6a28020022052008412820061b460d002001200041286a20061b21012000280200200020061b21060c010b200010f78d80800020002802042105200028020021060b200620056a20073a00002001200128020041016a3602000c010b200028020420002802282201200141284b22011b2205450d0220052000280200200020011b6a417f6a220120012d00002006723a00000b2000200028022c41016a220536022c0b200041046a2109200041286a210a024002402000280204220720002802282206200641284b22081b22014101742005470d0002402006412820081b20016b20034f0d00200120036a22062001490d044100417f2006417f6a677620064102491b41016a2206450d04200241086a2000200610f68d808000024020022802082206418180808078460d002006450d052006200228020c10b280808000000b20092802002107200a28020021060b20072006200641284b22051b22062001490d042000280200200020051b20016a220520036a2005200620016b10fe8d8080001a20052004200310848e8080001a200041044128200028022841284b1b6a200620036a3602000c010b2003450d002001417f6a21052001450d0420002802002000200641284b1b20056a220120012d000041f001713a0000200028020420002802282201200141284b22011b220620054d0d052000280200200020011b20056a220120012d000020042d000041f00171410476723a000002402003417f6a220b450d0041002101034020032001460d08200420016a22062d00002108200641016a2d0000210c024002402000410441282000280228220541284b22061b6a28020022072005412820061b460d002009200a20061b21052000280200200020061b21060c010b200010f78d8080002000280204210720002802002106200921050b200620076a2008410474200c410476410f71723a00002005200528020041016a360200200b200141016a2201470d000b0b2004200b6a2d00004104742105024002402000410441282000280228220741284b22011b6a28020022062007412820011b460d002009200a20011b21092000280200200020011b21010c010b200010f78d80800020002802042106200028020021010b200120066a20053a00002009200928020041016a3602000b2000200028022c20034101746a36022c200241106a2480808080000f0b419c94c68000413a41d894c6800010a181808000000b418ca0c68000411141a0a0c6800010f880808000000b41f88ec68000411e41f48fc6800010f880808000000b20054100418c96c6800010f980808000000b20052006419c96c6800010f980808000000b2003200341ac96c6800010f980808000000bec0301067f23808080800041106b2204248080808000024002400240024020010d00410021010c010b20012802082205410176210620012802042107200128020021080240024020054101710d00410021090240200620074b0d0020062101410021060c020b2006200741d492c68000109481808000000b200620074f0d0241012109200641016a2101200820066a2d0000410f7121060b2004410c6a200720016b360200200420063a0005200420093a00042004200820016a3602082000200441046a10e28d808000200741017420056b21010b02402002450d000240024020002d002c4101710d00200041046a210720034104742105024002402000410441282000280228220841284b22061b6a28020022022008412820061b460d002007200041286a20061b21072000280200200020061b21060c010b200010f78d80800020002802042102200028020021060b200620026a20053a00002007200728020041016a3602000c010b200028020420002802282207200741284b22071b2206450d0320062000280200200020071b6a417f6a220720072d00002003723a00000b2000200028022c41016a36022c200141016a21010b200441106a24808080800020010f0b2006200741e492c6800010f980808000000b419c94c68000413a41d894c6800010a181808000000bf303010a7f23808080800041306b220224808080800020024200370228200241286a210302400240024020012802042204410174220520012802082206460d00200520066b22054101200541014b1b2105200241046a2107200128020021084100210103402006410176220920044f0d02200820096a2d00002209410f71200941047620064101711b21090240024020014101710d002009410474210a024002402002410441282002280228220941284b22011b6a280200220b2009412820011b460d002007200320011b21092002280200200220011b21010c010b200210f78d8080002002280204210b20022802002101200721090b2001200b6a200a3a00002009200928020041016a3602000c010b200228020420022802282201200141284b22011b220b450d04200b2002280200200220011b6a417f6a220120012d00002009723a00000b2002200228022c41016a220136022c200641016a21062005417f6a22050d000b0b20002002290200370200200041286a2003290200370200200041206a200241206a290200370200200041186a200241186a290200370200200041106a200241106a290200370200200041086a200241086a290200370200200241306a2480808080000f0b2009200441e890c6800010f980808000000b419c94c68000413a41d894c6800010a181808000000b02000b2100200128021441f897c68000410b200141186a28020028020c118280808000000be70201087f20002001280200220220002802006a41017110e88d8080001a200141086a28020021030240024020020d00410021020c010b024002402003450d00200041086a2802002000412c6a2802002202200241284b22041b2202417f6a21052002450d012000280204200041046a20041b20056a220220022d000020012802042d0000410f71723a0000410121020c020b4100410041e498c6800010f980808000000b2005200241f498c6800010f980808000000b0240200320024d0d00200320026b2105200041086a21062000412c6a2104200041046a2107200128020420026a2101034020012d0000210802400240200620042004280200220941284b22031b220028020022022009412820031b460d002007280200200720031b21030c010b200710f78d8080002006280200210220072802002103200621000b200320026a20083a00002000200028020041016a360200200141016a21012005417f6a22050d000b0b0bc00501057f200028020021022000200136020002400240024002400240024002400240024002400240200220014b0d0041002103200220014f0d04200041086a2104200041046a210220004108412c2000412c6a2205280200220341284b22011b6a28020022062003412820011b460d012004200520011b21032000280204200220011b21010c020b200041046a21020240200041086a28020022032000412c6a2802002201200141284b1b2204417f6a2205450d004101210103402000280208200028022c2203200341284b22061b22032001417f6a4d0d06200320014d0d072002280200200220061b20016a2203417f6a220620062d000041047420032d0000410476410f71723a00002004200141016a2201470d000b20002802082103200028022c21010b20032001200141284b1b220020054d0d0620022802002002200141284b1b20056a220120012d00004104743a00000c020b200210f78d8080002004280200210620022802002101200421030b200120066a41003a00002003200328020041016a36020002402004280200220020052802002201200141284b1b22034102490d002003417f6a21010340200428020020052802002200200041284b22031b22002001417f6a22064d0d07200020014d0d082002280200200220031b20016a22002000417f6a2d000041047420002d0000410476410f71723a0000200141014b21002006210120000d000b20042802002100200528020021010b20002001200141284b22031b450d072002280200200220031b220120012d00004104763a00000b410121030b20030f0b2001417f6a200341889ac6800010f980808000000b2001200341989ac6800010f980808000000b2005200041f899c6800010f980808000000b2001417f6a200041a89ac6800010f980808000000b2001200041b89ac6800010f980808000000b4100410041e899c6800010f980808000000be40101037f23808080800041106b220324808080800002400240024020024100480d00200241f5ffffff074f0d0102402002410b6a417c7122040d00410421050c030b41002d00fca3c680001a200441002802c8a3c680001181808080000022050d024104200410b280808000000b41fc9bc68000412b2003410f6a41a89cc6800041b89cc68000108981808000000b41e484c08000412b2003410f6a419085c08000419086c08000108981808000000b2005428180808010370200200541086a2001200210848e8080001a2000200236020420002005360200200341106a2480808080000bae0101017f23808080800041306b2202248080808000200028020021002002410c6a4202370200200241186a410c6a41d08180800036020020022000280200220036022820024103360204200241f0bfc08000360200200241d18180800036021c200220006836022c200141186a28020021002002200241186a36020820022002412c6a3602202002200241286a36021820012802142000200210d9808080002101200241306a24808080800020010b9a0201027f23808080800041106b2202248080808000200220002802002200360204200128021441c89cc680004106200141186a28020028020c118280808000002103200241003a000d200220033a000c20022001360208200241086a41ce9cc680004104200041046a41d49cc68000108c8180800041e49cc680004105200241046a41ec9cc68000108c81808000210320022d000c21000240024020022d000d0d00200041ff017141004721010c010b41012101200041ff01710d000240200328020022012d001c4104710d0020012802144187a5c080004102200128021828020c1182808080000021010c010b20012802144186a5c080004101200128021828020c1182808080000021010b200241106a24808080800020010b140020012000280200200028020410dd808080000bed0201037f2380808080004180016b220224808080800002400240024002400240200128021c22034110710d0020034120710d0120003502004101200110fe8080800021000c020b20002802002100410021030340200220036a41ff006a413041d7002000410f712204410a491b20046a3a00002003417f6a210320004110492104200041047621002004450d000b20034180016a22004180014b0d022001410141c48dc080004102200220036a4180016a410020036b10db8080800021000c010b20002802002100410021030340200220036a41ff006a413041372000410f712204410a491b20046a3a00002003417f6a210320004110492104200041047621002004450d000b20034180016a22004180014b0d022001410141c48dc080004102200220036a4180016a410020036b10db8080800021000b20024180016a24808080800020000f0b200041800141c88dc08000109481808000000b200041800141c88dc08000109481808000000b02000b02000b6001017f23808080800041206b22002480808080002000410c6a420137020020004101360204200041e49bc68000360200200041bb8480800036021c200041dc9ac680003602182000200041186a360208200041d49bc6800010f680808000000b4c01027f024020002802002201417f460d0020002802042102200120012802042200417f6a36020420004101470d002002410b6a4104490d00200141002802c0a3c68000118080808000000b0b6001017f23808080800041206b22002480808080002000410c6a420137020020004101360204200041e49bc68000360200200041bb8480800036021c200041dc9ac680003602182000200041186a360208200041ec9bc6800010f680808000000b2100200128021441fc9cc68000410b200141186a28020028020c118280808000000b920f03067f017e017f23808080800041c0016b220424808080800002400240024002400240024002400240024002400240024002400240024002400240024002400240024002400240024020012802000e050200010304020b200141086a280200220520012802042206490d04200520034b0d05200141186a2802002107200141146a28020021082001410c6a28020021090240024020012802100d0020072008490d0841002101200720034d0d012007200341949ec68000109581808000000b20072008490d0841012101200720034b0d090b200020013602102000200220066a36020420004101360200200041186a200720086b360200200041146a200220086a3602002000410c6a2009360200200041086a200520066b3602000c160b200141086a280200220520012802042206490d08200520034b0d09200141186a2802002107200141146a28020021082001410c6a28020021090240024020012802100d0020072008490d0c41002101200720034d0d012007200341e49dc68000109581808000000b20072008490d0c41012101200720034b0d0d0b200020013602102000200220066a36020420004102360200200041186a200720086b360200200041146a200220086a3602002000410c6a2009360200200041086a200520066b3602000c150b200041003602000c140b200441023602b401200441023602a8012004410236029c0120044102360290012004410236028401200441023602782004410236026c2004410236026020044102360254200441023602482004410236023c2004410236023020044102360224200441023602182004410236020c20044102360200410021070340410221080240200120076a220541106a28020022094102460d00200541186a2802002106200541146a28020021050240024020090d0020062005490d0f41002108200620034d0d012006200341e49dc68000109581808000000b20062005490d0f41012108200620034b0d100b200620056bad422086200220056aad84210a0b200420076a22052008360200200541046a200a3702002007410c6a220741c001460d130c000b0b200441023602b401200441023602a8012004410236029c0120044102360290012004410236028401200441023602782004410236026c2004410236026020044102360254200441023602482004410236023c2004410236023020044102360224200441023602182004410236020c20044102360200410021070340410221080240200120076a2205411c6a28020022094102460d00200541246a2802002106200541206a28020021050240024020090d0020062005490d1141002108200620034d0d012006200341e49dc68000109581808000000b20062005490d1141012108200620034b0d120b200620056bad422086200220056aad84210a0b200420076a22052008360200200541046a200a3702002007410c6a220741c001460d110c000b0b2006200541849ec68000109681808000000b2005200341849ec68000109581808000000b2008200741949ec68000109681808000000b2008200741a49ec68000109681808000000b2007200341a49ec68000109581808000000b2006200541849ec68000109681808000000b2005200341849ec68000109581808000000b2008200741e49dc68000109681808000000b2008200741f49dc68000109681808000000b2007200341f49dc68000109581808000000b2005200641e49dc68000109681808000000b2005200641f49dc68000109681808000000b2006200341f49dc68000109581808000000b2005200641e49dc68000109681808000000b2005200641f49dc68000109681808000000b2006200341f49dc68000109581808000000b02400240024002400240200141086a280200220720012802042208490d00200720034b0d01410221052001410c6a280200210902402001280210220b4102460d00200141186a2802002106200141146a280200210102400240200b0d0020062001490d0541002105200620034d0d012006200341949ec68000109581808000000b20062001490d0541012105200620034b0d060b200620016bad422086200220016aad84210a0b2000200220086a3602042000410c6a2009360200200041086a200720086b360200200041106a200441c00110848e8080001a200041d4016a200a370200200020053602d001200041043602000c060b2008200741849ec68000109681808000000b2007200341849ec68000109581808000000b2001200641949ec68000109681808000000b2001200641a49ec68000109681808000000b2006200341a49ec68000109581808000000b410221070240024002400240200128020422054102460d002001410c6a2802002108200141086a28020021010240024020050d0020082001490d0341002107200820034d0d012008200341949ec68000109581808000000b20082001490d0341012107200820034b0d040b200820016bad422086200220016aad84210a0b200041046a200441c00110848e8080001a200041c8016a200a370200200020073602c401200041033602000c030b2001200841949ec68000109681808000000b2001200841a49ec68000109681808000000b2008200341a49ec68000109581808000000b200441c0016a2480808080000be10301077f23808080800041106b2203248080808000200041286a2104024002400240200028022822054128200541284b22061b22072000280204200520061b22066b200220016b22084f0d00200620086a22052006490d014100417f2005417f6a677620054102491b41016a2205450d01200341086a2000200510f68d808000024020032802082205418180808078460d002005450d022005200328020c10b280808000000b200428020022054128200541284b1b21070b200041046a22092004200541284b22061b21080240024020004104412820061b6a280200220520074f0d002000280200200020061b2106034020012002460d02200620056a20012d00003a0000200141016a21012007200541016a2205470d000b200721050b2008200536020020012002460d02034020012d00002108024002402000410441282000280228220741284b22051b6a28020022062007412820051b460d002009200420051b21072000280200200020051b21050c010b200010f78d8080002000280204210620002802002105200921070b200520066a20083a00002007200728020041016a360200200141016a22012002470d000c030b0b200820053602000c010b418ca0c68000411141a0a0c6800010f880808000000b200341106a2480808080000bc70301067f23808080800041106b220324808080800002400240024002400240200128020420012802282204200441284b22051b220620024b0d002004412820051b21072001280200200120051b2108024020024129490d00418180808078210520072002460d040240200241004e0d00410021050c060b02400240024020044129490d002007417f4a0d0120072102410021050c080b41002d00fca3c680001a200241002802c8a3c680001181808080000022040d01410121050c070b0240200241002802c8a3c680001181808080000022040d00410121050c070b200420082007200220072002491b10848e8080001a200841002802c0a3c68000118080808000000c040b20042008200610848e8080001a0c030b418180808078210520044129490d0320012008200610848e80800020063602282007417f4c0d01200841002802c0a3c68000118080808000000c030b41c0a0c68000412041e0a0c6800010f880808000000b2003200736020c2003410036020841e49ec68000412b200341086a41909fc6800041fc9fc68000108981808000000b2001200236022820012006360204200120043602000b0b2000200236020420002005360200200341106a2480808080000bb90101027f23808080800041106b220124808080800002400240200028020420002802282202200241284b1b41016a2202450d004100417f2002417f6a677620024102491b41016a2202450d00200141086a2000200210f68d808000024020012802082200418180808078460d002000450d022000200128020c10b280808000000b200141106a2480808080000f0b418ca0c68000411141b0a0c6800010a181808000000b418ca0c68000411141a0a0c6800010f880808000000b02000ba90201027f23808080800041106b22022480808080000240024020002802000d00200128021441b49ec680004110200141186a28020028020c1182808080000021010c010b20022000360204200128021441c49ec680004108200141186a28020028020c118280808000002100200241003a000d200220003a000c20022001360208200241086a41cc9ec680004106200241046a41d49ec68000108c81808000210320022d000c2100024020022d000d0d00200041ff017141004721010c010b41012101200041ff01710d000240200328020022012d001c4104710d0020012802144187a5c080004102200128021828020c1182808080000021010c010b20012802144186a5c080004101200128021828020c1182808080000021010b200241106a24808080800020010b9d0501087f23808080800041306b22022480808080000240024020012d002c4101710d0020012802002103200128020421042001280228210541002106200241046a41286a22074100360200200241046a20032001200541284b22081b220120012004200520081b6a10f58d8080002000412c6a2007280200360200200041246a200241246a2902003702002000411c6a2002411c6a290200370200200041146a200241146a2902003702002000410c6a2002410c6a290200370200200020022902043702040c010b02400240200128020420012802282206200641284b22061b2207450d00200241046a41046a2108200241046a41286a210920022001280200200120061b22032d00004104763a00042007417f6a2105200241046a410172210441002101034020052001460d01200420016a200320016a22062d0000410474200641016a2d0000410476410f71723a0000200141016a22014127470d000b2002412836022c20074129490d01200741586a2104200341286a210103402001417f6a2d000041047420012d0000410476410f7172210702400240200241046a41044128200228022c220541284b22061b6a28020022032005412820061b460d002008200920061b21052002280204200241046a20061b21060c010b200241046a10f78d8080002002280208210320022802042106200821050b200620036a20073a00002005200528020041016a360200200141016a21012004417f6a22040d000c020b0b2002200736022c0b200020022902043702042000412c6a2002412c6a280200360200200041246a200241246a2902003702002000411c6a2002411c6a290200370200200041146a200241146a2902003702002000410c6a2002410c6a290200370200410121060b20002006360200200241306a2480808080000b980605017f017e017f057e017f23808080800041206b2205248080808000024002400240024002402003500d002004500d010b420021062001200354200220045420022004511b0d012002500d01200541106a20032004200479a7200279a76b220741ff007110858e80800042012007413f71ad862108200541186a29030021092005290310210a4200210603400240200220097d2001200a54ad7d220b4200530d00200820068421062001200a7d2201200354200b200454200b2004511b0d04200b21020b200a4201882009423f8684210a20084201882108200942018821090c000b0b0240024002402002500d00024020022003540d0020022003510d022002200382210b2002200380210c024020034280808080105a0d00200b4220862001422088842209200380220a4220862009200382422086200142ffffffff0f83842201200380842106200a422088200c84210c200120038221014200210b0c070b2001200354200b200454200b2004511b0d032004423f8620034201888421092003423f86210a428080808080808080807f210242002104024003400240200b20097d2001200a54ad7d22084200530d002001200a7d2101200220048421042008500d022008210b0b200a4201882009423f8684210a20024201882102200942018821090c000b0b200120038020048421064200210b200120038221010c060b200520032004413f200379a72207200279a7220d6b41c0006a2007200d461b220710858e80800042012007413f71ad86210b200541086a29030021092005290300210a42002104024003400240200220097d2001200a54ad7d22084200530d002001200a7d2101200b20048421042008500d02200821020b200a4201882009423f8684210a200b420188210b200942018821090c000b0b200120038020048421064200210b200120038221010c040b200120038021064200210b200120038221010c030b200120028021064200210b4201210c200120028221010c030b420021060c020b2002210b0b4200210c0b2000200137031020002006370300200041186a200b3703002000200c370308200541206a2480808080000b4901017f23808080800041106b2204248080808000200420012002200310868e808000200429030021022000200441086a29030037030820002002370300200441106a2480808080000b6e01067e2000200342ffffffff0f832205200142ffffffff0f8322067e22072003422088220820067e22062005200142208822097e7c22054220867c220a3703002000200820097e2005200654ad4220862005422088847c200a200754ad7c200420017e200320027e7c7c3703080b0e0020002001200210818e8080000b4b01017f23808080800041206b22052480808080002005200120022003200410fb8d808000200529030021042000200541086a29030037030820002004370300200541206a2480808080000bc10201087f02400240200241104f0d00200021030c010b2000410020006b41037122046a210502402004450d0020002103200121060340200320062d00003a0000200641016a2106200341016a22032005490d000b0b2005200220046b2207417c7122086a210302400240200120046a2209410371450d0020084101480d012009410374220641187121022009417c71220a41046a2101410020066b4118712104200a28020021060340200520062002762001280200220620047472360200200141046a2101200541046a22052003490d000c020b0b20084101480d0020092101034020052001280200360200200141046a2101200541046a22052003490d000b0b20074103712102200920086a21010b02402002450d00200320026a21050340200320012d00003a0000200141016a2101200341016a22032005490d000b0b20000bac0501087f0240024002400240200020016b20024f0d00200120026a2103200020026a21040240200241104f0d00200021050c030b2004417c7121054100200441037122066b210702402006450d00200120026a417f6a210803402004417f6a220420082d00003a00002008417f6a210820052004490d000b0b2005200220066b2209417c7122066b21040240200320076a2207410371450d0020064101480d022007410374220841187121022007417c71220a417c6a2101410020086b4118712103200a280200210803402005417c6a2205200820037420012802002208200276723602002001417c6a210120042005490d000c030b0b20064101480d01200920016a417c6a210103402005417c6a220520012802003602002001417c6a210120042005490d000c020b0b02400240200241104f0d00200021040c010b2000410020006b41037122036a210502402003450d0020002104200121080340200420082d00003a0000200841016a2108200441016a22042005490d000b0b2005200220036b2209417c7122066a210402400240200120036a2207410371450d0020064101480d012007410374220841187121022007417c71220a41046a2101410020086b4118712103200a28020021080340200520082002762001280200220820037472360200200141046a2101200541046a22052004490d000c020b0b20064101480d0020072101034020052001280200360200200141046a2101200541046a22052004490d000b0b20094103712102200720066a21010b2002450d02200420026a21050340200420012d00003a0000200141016a2101200441016a22042005490d000c030b0b20094103712201450d012007410020066b6a2103200420016b21050b2003417f6a210103402004417f6a220420012d00003a00002001417f6a210120052004490d000b0b20000bb50101037f02400240200241104f0d00200021030c010b2000410020006b41037122046a210502402004450d00200021030340200320013a0000200341016a22032005490d000b0b2005200220046b2204417c7122026a2103024020024101480d00200141ff017141818284086c2102034020052002360200200541046a22052003490d000b0b200441037121020b02402002450d00200320026a21050340200320013a0000200341016a22032005490d000b0b20000b4a01037f4100210302402002450d000240034020002d0000220420012d00002205470d01200041016a2100200141016a21012002417f6a2202450d020c000b0b200420056b21030b20030b0e0020002001200210808e8080000b5701017e02400240200341c000710d002003450d0120022003413f71ad2204862001410020036b413f71ad88842102200120048621010c010b20012003413f71ad862102420021010b20002001370300200020023703080b5701017e02400240200341c000710d002003450d012002410020036b413f71ad8620012003413f71ad220488842101200220048821020c010b20022003413f71ad882101420021020b20002001370300200020023703080b4b01017f23808080800041106b22052480808080002005200120022003200410fd8d808000200529030021042000200541086a29030037030820002004370300200541106a2480808080000b0e0020002001200210838e8080000b4b01017f23808080800041106b22052480808080002005200120022003200410ff8d808000200529030021042000200541086a29030037030820002004370300200541106a2480808080000b0e0020002001200210828e8080000b0bb4a4060300418080c0000bf0a006010000000c000000040000000200000003000000040000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f7261775f7665632e72736361706163697479206f766572666c6f770000890010001100000018001000710000003a020000050000004572726f724c61796f75744572726f726d656d6f727920616c6c6f636174696f6e206f6620206279746573206661696c65640000c400100015000000d90010000d0000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f616c6c6f632e727300f80010006f000000a20100000d000000f80010006f000000a00100000d000000060000000c000000040000000700000008000000040000006120666f726d617474696e6720747261697420696d706c656d656e746174696f6e2072657475726e656420616e206572726f72000900000000000000010000000a0000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f666d742e7273000000e40110006d000000640200002000000063616c6c65642060526573756c743a3a756e77726170282960206f6e20616e2060457272602076616c7565000900000000000000010000000b0000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f73796e632e72730000a00210006e00000075010000320000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f61727261792d62797465732d362e322e322f7372632f6c69622e72730000200310005e0000000804000022000000200310005e000000080400004f0000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f627335382d302e352e312f7372632f6465636f64652e72730000a00310005a000000c00100000b000000a00310005a000000ac010000200000004c61796f75744572726f722f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f62797465732d312e362e302f7372632f62797465732e72730000000c0000000d0000000e0000000f000000100000001100000012000000130000001400000015000000120000001600000063616c6c65642060526573756c743a3a756e77726170282960206f6e20616e2060457272602076616c75650017000000000000000100000018000000270410005a0000002b04000032000000270410005a0000003904000049000000190000001a0000001b0000001c00000061626f72742f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f62797465732d312e362e302f7372632f6c69622e727300000025051000580000006e0000000900000066726f6d5f7374725f72616469785f696e743a206d757374206c696520696e207468652072616e676520605b322c2033365d60202d20666f756e6420900510003c0000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f6e756d2f6d6f642e7273d405100070000000a9050000050000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f666d742f6e756d2e7273307800005406100070000000690000001700000030623030303130323033303430353036303730383039313031313132313331343135313631373138313932303231323232333234323532363237323832393330333133323333333433353336333733383339343034313432343334343435343634373438343935303531353235333534353535363537353835393630363136323633363436353636363736383639373037313732373337343735373637373738373938303831383238333834383538363837383838393930393139323933393439353936393739383939617373657274696f6e206661696c65643a202a63757272203e20313900005406100070000000ef010000050000001e0000000c000000040000001f00000020000000210000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f666d742f6d6f642e727330303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030e807100070000000f20500001f00000066616c736574727565000000e807100070000000350900001a000000e8071000700000002e0900002200000070616e69636b6564206174203a0a2f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f6e756d2f6269676e756d2e7273000000e208100073000000ac01000001000000617373657274696f6e206661696c65643a206e6f626f72726f77617373657274696f6e206661696c65643a20646967697473203c203430617373657274696f6e206661696c65643a206f74686572203e20302f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f6e756d2f666c74326465632f6d6f642e7273617373657274696f6e206661696c65643a20216275662e69735f656d707479282900ba09100078000000bc00000005000000617373657274696f6e206661696c65643a206275665b305d203e206227302700ba09100078000000bd00000005000000617373657274696f6e206661696c65643a2070617274732e6c656e2829203e3d20340000ba09100078000000be000000050000002e302e00ba091000780000000b01000005000000ba091000780000000c0100000500000065652d2d2b4e614e696e6630306530617373657274696f6e206661696c65643a206275662e6c656e2829203e3d206d61786c656eba091000780000007f0200000d0000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f7374722f7061747465726e2e7273300b1000740000004605000012000000300b1000740000004605000028000000300b1000740000003906000015000000300b1000740000006706000015000000300b10007400000068060000150000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f756e69636f64652f756e69636f64655f646174612e7273000000f40b10007d0000005000000028000000f40b10007d0000005c000000160000000003000083042000910560005d13a0001217201f0c20601fef2ca02b2a30202c6fa6e02c02a8602d1efb602e00fe20369eff6036fd01e136010a2137240de137ab0e61392f18a139301c6148f31ea14c40346150f06aa1514f6f21529dbca15200cf615365d1a15300da215400e0e155aee26157ece42159d0e8a1592000ee59f0017f5a00700007002d0101010201020101480b30151001650702060202010423011e1b5b0b3a09090118040109010301052b033c082a180120370101010408040103070a021d013a0101010204080109010a021a010202390104020402020303011e0203010b0239010405010204011402160601013a0101020104080107030a021e013b0101010c01090128010301370101030503010407020b021d013a01020102010301050207020b021c02390201010204080109010a021d0148010401020301010801510102070c08620102090b0749021b0101010101370e01050102050b0124090166040106010202021902040310040d01020206010f01000300031d021e021e02400201070801020b09012d030101750222017603040209010603db0202013a010107010101010208060a0201301f310430070101050128090c0220040202010338010102030101033a0802029803010d0107040106010302c6400001c32100038d016020000669020004010a200250020001030104011902050197021a120d012608190b2e0330010204020227014306020202020c0108012f01330101030202050201012a020801ee010201040100010010101000020001e201950500030102050428030401a502000400025003460b31047b01360f290102020a033104020207013d03240501083e010c0234090a0402015f0302010102060102019d010308150239020101010116010e070305c308020301011701510102060101020101020102eb010204060201021b025508020101026a0101010206010165030204010500090102f5010a0201010401900402020401200a280602040801090602032e0d010200070106010152160207010201027a06030101020107010148020301010100020b023405050101010001060f00053b0700013f0451010002002e0217000101030405080802071e0494030037043208010e011605010f000701110207010201056401a00700013d04000400076d07006080f000003a000000f00f100000000000f00f100001000000f00f10000100000025000000000000000100000026000000696e646578206f7574206f6620626f756e64733a20746865206c656e20697320206275742074686520696e6465782069732000001c101000200000003c10100012000000270000000400000004000000280000003d3d213d6d617463686573617373657274696f6e20606c6566742020726967687460206661696c65640a20206c6566743a200a2072696768743a20007b101000100000008b10100017000000a21010000900000020726967687460206661696c65643a200a20206c6566743a200000007b10100010000000c410100010000000d410100009000000a210100009000000010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002020202020202020202020202020202020202020202020202020202020203030303030303030303030303030303040404040400000000000000000000002e2e00000012100002000000426f72726f774572726f72426f72726f774d75744572726f72616c726561647920626f72726f7765643a20002512100012000000616c7265616479206d757461626c7920626f72726f7765643a200000401210001a0000003a2000000012100000000000641210000200000020202020207b202c20207b0a2c0a7d207d28280a2c290a5b5d0000002b0000000c000000040000001f0000002000000021000000010000000a00000064000000e803000010270000a086010040420f008096980000e1f50500ca9a3b0200000014000000c8000000d0070000204e0000400d030080841e00002d310100c2eb0b009435770000c16ff28623000000000081efac855b416d2dee0400000000000000000000011f6abf64ed386eed97a7daf4f93fe9034f180000000000000000000000000000000000013e952e0999df03fd38150f2fe47423ecf5cfd308dc04c4dab0cdbc197f33a603261fe94e0200000000000000000000000000000000000000000000000000000000000000000000017c2e985b87d3be729fd9d8872f1512c650de6b706e4acf0fd895d56e71b226b066c6ad2436151d5ad3423c0e54ff63c07355cc17eff965f228bc55f7c7dc80dced6ef4ceefdc5ff75305002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f6e756d2f666c74326465632f73747261746567792f647261676f6e2e7273617373657274696f6e206661696c65643a20642e6d616e74203e2030d4131000840000007500000005000000617373657274696f6e206661696c65643a20642e6d696e7573203e2030000000d4131000840000007600000005000000617373657274696f6e206661696c65643a20642e706c7573203e2030d4131000840000007700000005000000617373657274696f6e206661696c65643a206275662e6c656e2829203e3d204d41585f5349475f444947495453000000d4131000840000007a00000005000000d413100084000000c100000009000000d413100084000000fa0000000d000000d4131000840000000101000036000000617373657274696f6e206661696c65643a20642e6d616e742e636865636b65645f73756228642e6d696e7573292e69735f736f6d65282900d4131000840000007900000005000000617373657274696f6e206661696c65643a20642e6d616e742e636865636b65645f61646428642e706c7573292e69735f736f6d6528290000d4131000840000007800000005000000d4131000840000000a01000005000000d4131000840000000b01000005000000d4131000840000000c01000005000000d4131000840000007101000024000000d4131000840000007601000057000000d4131000840000008301000036000000d413100084000000650100000d000000d4131000840000004b01000022000000d4131000840000000e01000005000000d4131000840000000d0100000500000072616e676520737461727420696e64657820206f7574206f662072616e676520666f7220736c696365206f66206c656e677468208016100012000000921610002200000072616e676520656e6420696e64657820c4161000100000009216100022000000736c69636520696e64657820737461727473206174202062757420656e64732061742000e416100016000000fa1610000d0000005b2e2e2e5d626567696e203c3d20656e642028203c3d2029207768656e20736c6963696e672060601d1710000e0000002b171000040000002f171000100000003f171000010000006279746520696e64657820206973206e6f742061206368617220626f756e646172793b20697420697320696e7369646520202862797465732029206f66206000601710000b0000006b17100026000000911710000800000099171000060000003f17100001000000206973206f7574206f6620626f756e6473206f6620600000601710000b000000c8171000160000003f171000010000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f7374722f6d6f642e7273f8171000700000000c0100002c0000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f756e69636f64652f7072696e7461626c652e72730000781810007a0000001a00000036000000781810007a0000000a0000002b000000000601010301040205070702080809020a050b020e041001110212051311140115021702190d1c051d081f0124016a046b02af03b102bc02cf02d102d40cd509d602d702da01e005e102e704e802ee20f004f802fa03fb010c273b3e4e4f8f9e9e9f7b8b9396a2b2ba86b1060709363d3e56f3d0d1041418363756577faaaeafbd35e01287898e9e040d0e11122931343a4546494a4e4f64655cb6b71b1c07080a0b141736393aa8a9d8d909379091a8070a3b3e66698f92116f5fbfeeef5a62f4fcff53549a9b2e2f2728559da0a1a3a4a7a8adbabcc4060b0c151d3a3f4551a6a7cccda007191a22253e3fe7ecefffc5c604202325262833383a484a4c50535556585a5c5e606365666b73787d7f8aa4aaafb0c0d0aeaf6e6fbe935e227b0503042d036603012f2e80821d03310f1c0424091e052b0544040e2a80aa06240424042808340b4e43813709160a08183b45390363080930160521031b05014038044b052f040a070907402027040c0936033a051a07040c07504937330d33072e080a8126524b2b082a161a261c1417094e042409440d19070a0648082709750b423e2a063b050a0651060105100305808b621e48080a80a65e22450b0a060d133a060a362c041780b93c64530c48090a46451b4808530d49070a80f6460a1d03474937030e080a0639070a813619073b031c56010f320d839b66750b80c48a4c630d843010168faa8247a1b98239072a045c06260a460a28051382b05b654b0439071140050b020e97f80884d62a09a2e781330f011d060e0408818c89046b050d0309071092604709743c80f60a73087015467a140c140c570919808781470385420f1584501f060680d52b053e2101702d031a040281401f113a050181d02a82e680f7294c040a04028311444c3d80c23c06010455051b3402810e2c04640c560a80ae381d0d2c040907020e06809a83d80411030d0377045f060c04010f0c0438080a062808224e81540c1d03090736080e040907090780cb250a840600010305050606020706080709110a1c0b190c1a0d100e0c0f0410031212130916011704180119031a071b011c021f1620032b032d0b2e01300331023201a702a902aa04ab08fa02fb05fd02fe03ff09ad78798b8da23057588b8c901cdd0e0f4b4cfbfc2e2f3f5c5d5fe2848d8e9192a9b1babbc5c6c9cadee4e5ff00041112293134373a3b3d494a5d848e92a9b1b4babbc6cacecfe4e500040d0e11122931343a3b4546494a5e646584919b9dc9cecf0d11293a3b4549575b5c5e5f64658d91a9b4babbc5c9dfe4e5f00d11454964658084b2bcbebfd5d7f0f183858ba4a6bebfc5c7cfdadb4898bdcdc6cecf494e4f57595e5f898e8fb1b6b7bfc1c6c7d71116175b5cf6f7feff806d71dedf0e1f6e6f1c1d5f7d7eaeaf7fbbbc16171e1f46474e4f585a5c5e7e7fb5c5d4d5dcf0f1f572738f747596262e2fa7afb7bfc7cfd7df9a409798308f1fd2d4ceff4e4f5a5b07080f10272feeef6e6f373d3f42459091536775c8c9d0d1d8d9e7feff00205f2282df048244081b04061181ac0e80ab051f09811b03190801042f043404070301070607110a500f1207550703041c0a090308030703020303030c0405030b06010e15054e071b0757070206170c500443032d03010411060f0c3a041d255f206d046a2580c80582b0031a0682fd03590716091809140c140c6a060a061a0659072b05460a2c040c040103310b2c041a060b0380ac060a062f314d0380a4083c030f033c0738082b0582ff1118082f112d03210f210f808c048297190b158894052f053b07020e180980be22740c80d61a0c0580ff0580df0cf29d033709815c1480b80880cb050a183b030a06380846080c06740b1e035a0459098083181c0a16094c04808a06aba40c170431a10481da26070c050580a61081f50701202a064c04808d0480be031b030f0d303132333435363738396162636465662f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f6573636170652e72735c757b0000a01e10006f000000380000000b000000a01e10006f0000006600000023000000617373657274696f6e206661696c65643a206564656c7461203e3d20302f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f6e756d2f6469795f666c6f61742e727300511f1000760000004c00000009000000511f1000760000004e00000009000000202831203c3c2029e81f100000000000e81f100007000000ef1f100001000000df451a3d03cf1ae6c1fbccfe00000000cac69ac717fe70abdcfbd4fe000000004fdcbcbefcb177fff6fbdcfe000000000cd66b41ef9156be11fce4fe000000003cfc7f90ad1fd08d2cfcecfe00000000839a5531285c51d346fcf4fe00000000b5c9a6ad8fac719d61fcfcfe00000000cb8bee2377229cea7bfc04ff000000006d5378409149ccae96fc0cff0000000057ceb65d79123c82b1fc14ff000000003756fb4d369410c2cbfc1cff000000004f9848386fea9690e6fc24ff00000000c73a8225cb8574d700fd2cff00000000f497bf97cdcf86a01bfd34ff00000000e5ac2a17980a34ef35fd3cff000000008eb2352afb6738b250fd44ff000000003b3fc6d2dfd4c8846bfd4cff00000000bacdd31a2744ddc585fd54ff0000000096c925bbce9f6b93a0fd5cff0000000084a5627d246cacdbbafd64ff00000000f6da5f0d5866aba3d5fd6cff0000000026f1c3de93f8e2f3effd74ff00000000b880ffaaa8adb5b50afe7cff000000008b4a7c6c055f628725fe84ff000000005330c13460ffbcc93ffe8cff000000005526ba918c854e965afe94ff00000000bd7e29702477f9df74fe9cff000000008fb8e5b89fbddfa68ffea4ff00000000947d7488cf5fa9f8a9feacff00000000cf9ba88f937044b9c4feb4ff000000006b150fbff8f0088adffebcff00000000b63131655525b0cdf9fec4ff00000000ac7f7bd0c6e23f9914ffccff00000000063b2b2ac4105ce42effd4ff00000000d3927369992424aa49ffdcff000000000eca0083f2b587fd63ffe4ff00000000eb1a11926408e5bc7effecff00000000cc88506f09ccbc8c99fff4ff000000002c6519e25817b7d1b3fffcff00000000000000000000409cceff0400000000000000000010a5d4e8e8ff0c0000000000000062acc5eb78ad0300140000000000840994f878393f811e001c0000000000b31507c97bce97c03800240000000000705cea7bce327e8f53002c00000000006880e9aba438d2d56d0034000000000045229a1726274f9f88003c000000000027fbc4d431a263eda200440000000000a8adc88c3865deb0bd004c0000000000db65ab1a8e08c783d8005400000000009a1d7142f91d5dc4f2005c000000000058e71ba62c694d920d01640000000000ea8d701a64ee01da27016c00000000004a77ef9a99a36da24201740000000000856b7db47b7809f25c017c00000000007718dd79a1e454b47701840000000000c2c59b5b92865b8692018c00000000003d5d96c8c55335c8ac01940000000000b3a097fa5cb42a95c7019c0000000000e35fa099bd9f46dee101a40000000000258c39db34c29ba5fc01ac00000000005c9f98a3729ac6f61602b40000000000cebee95453bfdcb73102bc0000000000e24122f217f3fc884c02c40000000000a5785cd39bce20cc6602cc0000000000df53217bf35a16988102d400000000003a301f97dcb5a0e29b02dc000000000096b3e35c53d1d9a8b602e400000000003c44a7a4d97c9bfbd002ec00000000001044a4a74c4c76bbeb02f400000000001a9c40b6ef8eab8b0603fc00000000002c8457a610ef1fd02003040100000000293191e9e5a4109b3b030c01000000009d0c9ca1fb9b10e7550314010000000029f43b62d92028ac70031c010000000085cfa77a5e4b44808b032401000000002dddac0340e421bfa5032c01000000008fff445e2f9c678ec00334010000000041b88c9c9d1733d4da033c0100000000a91be3b492db199ef503440100000000d977dfba6ebf96eb0f044c01000000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f6e756d2f666c74326465632f73747261746567792f67726973752e72730018251000830000007d00000015000000617373657274696f6e206661696c65643a20642e6d616e74203e20301825100083000000a900000005000000617373657274696f6e206661696c65643a20642e6d696e7573203e20300000001825100083000000aa00000005000000617373657274696f6e206661696c65643a20642e706c7573203e20301825100083000000ab00000005000000617373657274696f6e206661696c65643a206275662e6c656e2829203e3d204d41585f5349475f4449474954530000001825100083000000ae00000005000000617373657274696f6e206661696c65643a20642e6d616e74202b20642e706c7573203c202831203c3c203631290000001825100083000000af0000000500000018251000830000000a01000011000000000000000000000000000000617474656d707420746f20646976696465206279207a65726f00000018251000830000000d0100000900000018251000830000004001000009000000617373657274696f6e206661696c65643a20642e6d616e742e636865636b65645f73756228642e6d696e7573292e69735f736f6d652829001825100083000000ad00000005000000617373657274696f6e206661696c65643a20642e6d616e742e636865636b65645f61646428642e706c7573292e69735f736f6d65282900001825100083000000ac00000005000000617373657274696f6e206661696c65643a20216275662e69735f656d70747928290000001825100083000000dc01000005000000617373657274696f6e206661696c65643a20642e6d616e74203c202831203c3c203631291825100083000000dd010000050000001825100083000000de01000005000000010000000a00000064000000e803000010270000a086010040420f008096980000e1f50500ca9a3b182510008300000033020000110000001825100083000000360200000900000018251000830000006c020000090000001825100083000000e30200004e0000001825100083000000ef0200004a0000001825100083000000cc0200004a00000063616c6c656420604f7074696f6e3a3a756e77726170282960206f6e206120604e6f6e65602076616c7565000820100000000000736f7572636520736c696365206c656e67746820282920646f6573206e6f74206d617463682064657374696e6174696f6e20736c696365206c656e677468202829000000d028100015000000e52810002b000000102910000100000001000000000000000000000000000000000000000000000000000000000000000000000000000000b0a00e02d2c986019d188f007f693500600cbd00a7d7fb019e4c80026965e1011dfc0400920cae00edd3f51cd21893009635e71d45bdf31d4d0100000000000000000000000000000000100059f1b20209e5a6017add2a021d14d4005280030030d1f3007779400331e39c01ff6dc501671b900001000000000000000000000000000000000000000000000000000000000000000000000000000000ecffff03ffffff01ffffff03ffffff01ffffff03ffffff01ffffff03ffffff01ffffff03ffffff01b0a00e02d2c986019d188f007f693500600cbd00a7d7fb019e4c80026965e1011dfc0400920cae00ea405d00a06a3f0039d357020bd2ba0058bc740240d80100ffc83d01d8429401fffa5c0024b2e10176c15f00657002014ffca102f16ac6018406b200e4df7000dfee550232f31a003e2b8b02ca410a00a37859038472d300bd6e15030e0a6a0029c0010098e87901bb3ca0039871ce01ffb6e202b30d4801204ded0091aa560135263303f080650128794a03eb4e9b00a99769029b294800c266af03cda265011b2e7b0112a8fd01d2af9702c2db60003876be02fdd1f50198647e02e781150134b8f203c6a4dd0001000000000000000000000000000000000000000000000000000000000000000000000000000000a37859038472d300bd6e15030e0a6a0029c0010098e87901bb3ca0039871ce01ffb6e202b30d4801617373657274696f6e206661696c65643a20696478203c2043415041434954592f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f636f6c6c656374696f6e732f62747265652f6e6f64652e7273782b100080000000b302000009000000617373657274696f6e206661696c65643a207372632e6c656e2829203d3d206473742e6c656e2829782b1000800000002f07000005000000617373657274696f6e206661696c65643a206f6c645f6c6566745f6c656e203e3d20636f756e7400782b100080000000dd0500000d000000617373657274696f6e206661696c65643a206c656e203e2030000000782b10008000000065010000090000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f636f6c6c656374696f6e732f62747265652f6e617669676174652e7273a42c1000840000005902000030000000496e646578206f7574206f6620626f756e647300382d1000130000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f736c6963652f736f72742e727300542d1000730000003b0400000e000000542d100073000000480400001c000000542d100073000000490400001d000000542d1000730000004a04000025000000542d1000730000008e04000040000000542d100073000000b40400004e000000542d100073000000c204000056000000617373657274696f6e206661696c65643a20656e64203e3d20737461727420262620656e64203c3d206c656e542d1000730000002d05000005000000542d1000730000003e05000029000000617373657274696f6e206661696c65643a206f666673657420213d2030202626206f6666736574203c3d206c656e0000542d1000730000009b000000050000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f636f6c6c656374696f6e732f62747265652f6e617669676174652e7273c42e100084000000c700000027000000c42e100084000000170200002f000000c42e100084000000a20000002400000050617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e7273000000892f100060000000b20000001e0000003078323465386438623732633063336135663432646465323966346535303235326664663132656465353832643632663564386631633234616237633062373066364d6f64656672616d655f6d657461646174615f686173685f657874656e73696f6e44697361626c6564456e61626c656450617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e7273007f30100060000000b20000001e000000776569676874576569676874636c6173734469737061746368436c617373706179735f666565506179735965734e6f00506179736672616d655f737570706f72743a3a64697370617463684469737061746368436c6173734e6f726d616c4f7065726174696f6e616c4d616e6461746f72794469737061746368496e666f000042616c616e63655374617475736672616d655f737570706f72743a3a7472616974733a3a746f6b656e733a3a6d6973634672656552657365727665644572726f723a7472616e73616374696f6e5f6c6576656c3a57652061726520756e646572666c6f77696e6720776974682063616c63756c6174696e67207472616e73616374696f6e616c206c6576656c732e204e6f742067726561742c20627574206c65742773206e6f742070616e69632e2e2ec43110005c0000006672616d655f737570706f72743a3a73746f726167653a3a7472616e73616374696f6e616c2f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f737570706f72742f7372632f73746f726167652f7472616e73616374696f6e616c2e7273436f727275707465642073746174652061742060603a200000bb32100014000000cf32100003000000307872756e74696d653a3a73746f726167652f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f737570706f72742f7372632f73746f726167652f756e6861736865642e72736672616d655f737570706f72743a3a73746f726167653a3a756e6861736865646361706163697479206f766572666c6f777f331000110000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f7665632f737065635f66726f6d5f697465725f6e65737465642e72730098331000830000003b000000120000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f7665632f6d6f642e72730000002c34100071000000a00b00000d0000004f7074696f6e544e6f6e65536f6d65003900000001000000010000003a0000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f73797374656d2f7372632f6c696d6974732e72734275696c6465722066696e6973686564207769746820606275696c645f6f725f70616e6963603b205468652070616e69632069732065787065637465642069662072756e74696d65207765696768747320617265206e6f7420636f72726563740000d03410005e000000b4010000160000003c7761736d3a73747269707065643e426c6f636b4c656e6774686672616d655f73797374656d3a3a6c696d69747357656967687473506572436c617373426c6f636b576569676874730000005065724469737061746368436c6173736672616d655f737570706f72743a3a64697370617463685450686173656672616d655f73797374656d4170706c7945787472696e73696346696e616c697a6174696f6e496e697469616c697a6174696f6e4c61737452756e74696d6555706772616465496e666f50617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e72737436100060000000b20000001e0000006d616e6461746f7279546e6f726d616c6f7065726174696f6e616c6d61785065724469737061746368436c6173733c7533323e626173655f65787472696e7369635765696768746d61785f65787472696e7369634f7074696f6e3c5765696768743e6d61785f746f74616c7265736572766564626173655f626c6f636b6d61785f626c6f636b7065725f636c6173735065724469737061746368436c6173733c57656967687473506572436c6173733e753332737065635f76657273696f6e636f6465633a3a436f6d706163743c7533323e737065635f6e616d6573705f72756e74696d653a3a52756e74696d65537472696e67d8371000000000000000000000000000010000000000000082800000000000008a8000000000008000800080000000808b800000000000000100008000000000818000800000008009800000000000808a00000000000000880000000000000009800080000000000a000080000000008b800080000000008b0000000000008089800000000000800380000000000080028000000000008080000000000000800a800000000000000a0000800000008081800080000000808080000000000080010000800000000008800080000000802f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f6b656363616b2d302e312e342f7372632f6c69622e72734120726f756e645f636f756e742067726561746572207468616e204b454343414b5f465f524f554e445f434f554e54206973206e6f7420737570706f72746564210000a838100059000000eb000000090000003e00000000000000010000003f00000040000000410000006b65792d76616c756520737570706f7274206973206578706572696d656e74616c20616e64206d75737420626520656e61626c6564207573696e672074686520606b766020666561747572656c3910004c0000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f6c6f672d302e342e32322f7372632f5f5f707269766174655f6170692e7273000000c0391000610000002d00000009000000430000000400000004000000440000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f6d65726c696e2d332e302e302f7372632f7374726f62652e727301a8010001605354524f424576312e302e320000443a10005c0000005e00000009000000443a10005c0000005f00000009000000443a10005c000000680000000d000000443a10005c0000007c00000015000000596f75207573656420746865205420666c61672c207768696368207468697320696d706c656d656e746174696f6e20646f65736e277420737570706f72740000f43a10003e00000000000000443a10005c0000009100000009000000596f7520747269656420746f20636f6e74696e7565206f702020627574206368616e67656420666c61677320746f2000503b100019000000693b100016000000443a10005c000000880000000d000000646f6d2d73657000526561736f6e7370616c6c65745f62616c616e6365733a3a74797065734665654d697363416c6c4578747261466c61677341646a7573746d656e74446972656374696f6e496e637265617365446563726561736550617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e72730000000d3c100060000000b20000001e0000007531323863616e6e6f7420616476616e63652070617374206072656d61696e696e67603a20203c3d20000000843c100021000000a53c1000040000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f62797465732d312e362e302f7372632f62797465732e72730000bc3c10005a0000003b020000090000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7061726974792d7363616c652d636f6465632d332e362e31322f7372632f636f6465632e7273283d100068000000ad01000029000000436f646563206572726f72007072696d69746976655f74797065734832353650617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e7273d03d100060000000b20000001e0000005b75383b2033325d010000000000000000000000000000209a99999999999999999999999999991915ae47e17a14ae47e17a14ae47e17a14de24068195438b6ce7fba9f1d24d621096d40968226c787aa52c431cebe2361aab436e861bf0f96184f068e388b5f8142236583849f3c7b4368dedb5a0f7c6106a238dc00e52a6875748afbc9af2d71a884fd766a541b89fdf398c30e28e791507a6121f51012de6b294d626e80b2e11a40951cb8168aed6b7babdd7d9df7c1bea3aa7a234edf1de5f956479e17ffd15bbc885e8f6f0277f1911ea2d81999711f80dd640beb40c65c281764968c2251c9371de33989070ea019b2ba1869b841643c17e29e0a6f3219b1556e79eaf03123735310fcdd785692bbc89d897b2d21cf9905a3fd7df37218996d44646f50e17fa7348cc45e65fe7a0ab43d2d15d72125d860d7a3c3d66a534acd2b64fc9831db19ed79463971e515d2342920ca19c17c14b79dd82df7eda7d4f9b0e0ab4e31268ac5b62d198642a96e55e171020391e53f0e281a7e0b6ee4451b21240b32d18a9264fce524d92586aa78ea899c2571341a47eb0b77b5027aad87ddaf5d0f21e345065c05fc9a652bb13cbaec440c21890a6ea994cd4eb0ec90f3cf2369ace13800a11c3ad5379b141196050bef6b01f670874028bdc2dc16747b3a6fe5e5a1952a029356fb02434869fc2ebfe4b4814db19ee90f2591d909e7f688965d639105f29b0b41dc3fb4c9732a7a8d523f619b2ba595db135963dac5b1fba77e9c4142862e17d275eab9756494cfb92879d100d9d68c9d8c9abf2f00e7af8b7a5951a3e17ba3a7aa1bc5b5a722e2d93844415cb45fb2ec81acaafae8e8b8a429d0311450992b1a6f7dcb24ae478aa9dfb381b04a141c1eb927df56e832d55b12fc71503b46767897564c4589c577727266c11d2eca5d8db886d6df4c625f20b3de01bdb23eb461607be8ac3381e28a3fd4c1649b655d2116cfe6e9c604b534f31d7110e8aefb64f1397b16067458518828b1ca5a1bff8720fac271ab96a37ad01d6161e4e9960c27256b9e160552c24ce44129516c2cd031e57f535cebb136de33a1dabab010b0318ac2a2bd82f768a4f62175689346f02e0bcbb5513f3c46e0cb51289a8edb1d0ccc792ef1eb8d44a7aee1d07ba578e400ad3dbf24b93106ffbf11706c8df7100d5a87cf56f0fda58fc2713d60c66e933bba7fabb4cb2298e60a61e11d7848729fc5295c9a38e540b1a85180eacd0d2bac9a8aa0783d8766fae9d13e3ac1a1e5edcdadda5d1c057b2b0621f4f8a484b4bb0487e51419aac8ec01b19d9a1d3d5d5596dcbdacde156a53316147b81dc77117b573ce2d7e7abeac211102acf6059825ef2c63626a6acaa04b619bba580476818f56bc551eb56559d911496840006ed792a23d1a722dfdd7d7410560734a3e18fddd1810cd13196fc531a456cf6e81a73e4a7343da7f444fd0f159e56f853e2281d535d97525d6a97d91062578db903db61eb2ef2509510bff51ae845a4c7cf484ebc585bdadda6659115206b836cd9d37163ade2e1171f1e4111cd119fad28861c9f480403f364639b1b0bdb18be536bb0e5069d358f1de91516a21547cb0f89f3ea6b4a9172e420ab1137bc71784cdbb84446aa1b846d01451c5f63c1c6d615c70305554903be9a9d1619e9cd6b45de383637770769feae1712c1411646a263c1565858720e97b1f21cce67abd1811c01df7913f571128e2817a5ec5541ce16347f61dc90c10ed886126e4756357d24206502c7e768e48ca41d253978f7301d80ea016cb9201dd7b61784fa2cf9f3b099bb3423614d17acf81239f74728534e5c5f54386815f2ac5a1e2e2cd3b9750b7d7f436053445b8a48185823dcc7f7d53099cf19a9367c3b6d1326d2f9728c89b48eb28f0ef1f92b151fb8412e8fa3072a7228a60bf4c7bcdd18fa9abea54f39bbc1861ed65c0697e413f6f7300919c25e9cd730f0fad624d41ff85f5a071468e549798d262fdf83761960e6e1051020516ec70a52bfe5cf5e141a8581d10c80daf1056f0e9984d94b10f5d468821400c44fd6e4e3f4a0f5121a2b77ed01aa9969d911b71cf7b3f7db14bcc58a018814eead7492b0c55cf9af102c09de68a6ed7c4954ea806f9428b31a24d4e453b857ca3a10559abf76205c1583761d4360793b6273aaaeff5e8016119ebdc8d166f52b9db810b132cb33571b7f646d4152c4bc7d600df48ea25cdf15ccb68a67db69fdcae63dc3d84e7d7f11df8a7772c50f2fabd72f058ee42eff1b80d5925b0473f288ac8c6a3e1dbf651666444249d028f5d3563d55984affea11a3a003424d4188b95795bbf31032ab1ce9e60268d7cd39617977fcc2405bef1654520220797161e72df9c968cd15591286509d998eb568a57c5b767415565b1dd2a64ae13e912051fd15c5f6dd447c170e1fa21aff404da7ca443792b1d0c9124acb69f764ceae0b116e58504fb40f1e3b3ceec550d88b3ca7f179733f900c18c9c9f137da7909ca85f4c7c232403d13db42e9bff6c2a8a96fba0c9eb766c81ee39bbacc2bcf53212695707e2c52a018824995708972a91ab8dd2665f074b3139d75881a0f8475f78c2f3e08e787851f175ea07b7236915f0a269806ec9f3719dfe419965bf84019d5844605f07f2c144cea47abafc600e1103705d18c99231047dd3f454ca467cee724d5b4478fd21906b1cc9dd6e952d81fb7ddc39f72a81438270a4b45eedb79192c7e6919c2861059d8a911a2e35f298f46300f8f36711a7a13bba7811cb3baa56bf3d8d85e27152fa995ec9ae3286251898fade04bec101775efe0f7380e9de80e4caf9aac131b792a591a932dd8b05372d625e256a9152e5547480fbe798ddcc1deb7814554117cbb0bda7e968f15949c978ccf08ba1b972fd614ff11a67776b0dfd6726d2e16798cde43ffa751f991f3b278f5bdbe118eadfdd2fe3f1cc21cecb75a2263641cd88a64423233b00117f05f15b5b5b61646a2839b8ec25901ac59e6dd90c42b12a303395f1704f6ceacc2a3fc1ad4121d839c2d4cac695e72bd9b1cca484342179ce38ad6895418f5fde2160807699b12c605abbd0f548dee2f6bf10cd874c51d056b22fe7276d7be8c22c170462ad11704bc4ecb28c512ffd64e678d6bbb0d13a0f97d78743b51cb247ed87b125f7c1e4d61fef929c90d09b731adfc417f63180a81cb9421d4d7a0c52724ca34cc821377ce7854cfb9bf676f0c6d4321ad371ff9712ddda594cc1f59708acf4d57f918c7f4bd7d51ddd67f7af3a13f3eacfa130bee2fc9e82ebeffc3b89c32fd79f71fd624f3a020bf316636fa16c2fdc79219781d5c1a1acc27b85efbab01cb6c751460e47c7bae09539318c9bc67a2f05d1099a094c5b042eb1ef474943f6ae72f1ae1e67604270289e55c2add32881ff314e7eb2b9d85cea0b7b0eeb028a07fc210d8dfdf616f4a0159b44a4e7433ccd01aad4ce6e725d5cde029a23e908fd67315f1d651865177714deeb4cbd972782911e857e9d6e8bee87bb054ac8f848d751b201321df5332bafc59dd890c6aa4f7158042e7184328c863ae4a6e70eee99211666ad827380d0d0617114a1a17431e1ceb21adec2ca43d6b12746e7b129c7e16564e57bdf01cfe88db5c58fc41e3fe11234a2562b49496415f618d603605cb1ce9d41de829aaab677fe73d4df8d0081787dd1720bb2156b932b964d7f9736d12a5958c662b6923c2eac13af2c2ec7b1d1dded61e89ba82cebb34625b025796171818df4b076235a5fcf6b4e201acde1259f36479d89c883b94f187373613311ee1f583c7464a6dfcdc5a06c6914227181a2b03069f6e573017af9ed1a79b521390ded13ccb7d251a2518311ca692ea1e40e5a7303cfe1d48b7795ae384a8bb18005186c0c9314bd3c5c7ae829d53c913cdb4a3cd42e9115209a617d1c885a81fa4901c3e0221db7407b8df403a9e5319500d4acb01b415f705601967fbe44214a70a08099b29def837b37a52fc833510d7dd0ca89142308e59b82ab79339ef19134b0a200e028d3ee1f9eef84261bf140f3c08803e9b3d65e7c758fa9b1a9910e42c0d0064f8c86ea50c8e90f9908e1aea23a499e9f9d38bb7a3714061da3e15bb1c50e1ba94a93cf982f4991a15ff102b61b39bc4ba75c78ed120c35dbb311b891a29166a95c4d20b0ee768b162c115a17bba118877d0db6f3e1f87278267119b925d1c40bf802ce663983e3fd0d81b4975e44933cc33bd51b64665ff0c4716d45d506e8fd68fcaa75e0551cc70d21153c9b3e34b571944d9fd6e4eade7831ca93af68209794703e19725a58aeccf16bafbc468d4606ccf807984ea6ef03f122af9070e87347ae59af5d3104b1a331d2294390b6c902e51e22a43da08155c17b5a9c7d5bca68bda8155cfe1d310b012870fd9222e71df909c55e5025381e61d6c0c144f8b5a4cda16de1dcfa89aeb178aa3a9a5a27ba3ae787eb1a520e22213a905a9a26a5fd27d2797b5a29a369e1e54d12082887fdb971facf74e15927e1877a780ce06667c794c23c6d8dd749813f10b01e40a702d8fad6ba32796545a1f5ad60050a259240cbeefb51f7810151915459ad981141d70fef2f7b2f9d91014776a7b149b4317c0fe5bc6282e7b0d10f24392edc405f2ccca2c0a0e7d2baf19c29c0ebed0375b0a6fbda171ca228c14cee33ecb73f948088c97b427d51b7010b09f6478ec5b0edaac25540c55f94c1ac07f5060f0af3e7bbdb7a9d610610a1533664080f3bfcb95972ceede731ad5105270cd665266acef5847b064b990ee1adb59a4b80e852326476cf3b6faa68b1549aeb693d8d0821e6c23295f95853c1175b08a1ff41a9efdac38a8feee08941bf759d5b229afb197bd938698250710162c7b77f5ba258eac97dc9e131e6ca61113c558222b097d7abf2dfeb8c9793d1c766aad4eefa0fd61cc57cb60a1949716c5eebd0b591afee7091309e74ddd12123ab1fc455b5d63a6dc840ed8affbea1cc88d306baf4a1c85b0d03e13f3622217d4d726bcf26ee3d026dacb75c2e88112868ca4c6ea179fb4d72946899da79c1d6b705005efdf182a46ee04a11786b01789f3d99d25b3e0546b8b9d4d799ef3127452f6626febcd8778452f7c2897521e5da85e82bf220bd3c66abfc986124218e4b94b68cc1b3c0f9f88ff3ad20e68136d2979407a2c601898da989183e40c1f24219433c856b34613e2130e361dd718b64d4329a0788f38dcb4dca4914adf138aaf6ba866277f5a602161a182aacb1fa2bfefb9eb8532154db44db49bbb6f194e998c6189d18eaa3d90a4f6e26259140ce1d61aa1a7d8eecad9b62b4f824710459b245e9b72277e11f68adfb1030c1a04491d1849f585fe0df83b195b69d614d0a04a13d45d9ecba4f92f147c87ab104d01115253c963df3a5ce6b9f90bac1a7167da740fa11c192fb01efbfa6f5615c152482ad980b0ad25c04b2f2ff3111134510daa8e34e71509cd12b27eeb4f1bc40d71ee3e5d1fab6d0a0f283289d9159da48d8b651719bc57080c2028d47a11943a7c123cf2f42c590de0ccd9b9f71b439596dbfcf4c3f0e03db370e1c75f1603111216975d365a1acbf5268139e61104e81cf024fc569090de220b358fa31cd0ece38c1d30dfd9a64b82a25d3fe916da23833db1597fe1eba2ce4eb13254125c39382fb5c2cb6879d17de44e84531de32d60bf5d35d65394a76450720376171c8be665b12a78a976ecb6a68ecfc412fa44d76fb5aa260ff1138bd77db2071e626adfbf2a22523f27436fac642806184e887f99884edb651f9cf289502038134a0dcc28744ac56f6593ea0fb433c01e3ba40987f6a16a59840f2273f6c2991896b6076cf8e7eead36d9b4f59135ae1356570ce0f33f7e4924f5ba2283227d1f45acd64cf6ff64d4e99095e868e83019d189783df8ff8343ee7344ed5320271474a19397c6cc9ccff18f03f10f4d1f105202b925a447617f1cb305e87faecb190f35c7b7e9d24dcc165cd1ecfff1a214d990d25f210f0b3d12b0da23335b8210c1e75099684bab6150b32a06852b6a1a67b94014baa2224e405c556b6abc2115539400dd94e84e0bcd4944bceec9e71051ed00c887da171248a9d3c64a760c1bdabd00a06c4846db6c87dc6bd591a315af64cd4cbd0605498a9fe3efdda74f11b13ae27ac80a08a843ff38e62fa6b21bf42ee8fb39a2395369ff931ef38428165df2ec2ffbb4c77587ff0fb2f503ba112eea47e69121d9223fff7fb622d35c1cf254068541817ab565ffff91e8a8b016f5433837010162c4b73233db86ed2612ee9ff3f10168363a5984eb91a4150b1d8b19f6279bb95efbe069bc7450113c17d67a5e86e2fa7e2fe787635d407496125691fdd6d0f797e571d93862cd86bd1dabdaca780d937984c17a2de83dd2ca1756156f2d714261d09ac88a8631a80813222218af4e6a684d91daaa3d4f40741ee8b479f23e8853a4daae88643f005d18875d6128ff6cdce9ae586d50cc997d13a495680d65ae60a9e48d481a7a5c2f1f8344ed3db7beb3ba8371a0ae61b0f218369d8a312c32f62e36c1e6bee759f513f0617782131dbde4899bd7973ff6ee1f5a4e2c35a97dca83a1afdfdf32f88b1915a556f720fea19ce7f2b24cc2f96f14aa1d12f9b3311b4ab9288f709b945910dd95b6c1ecb55e43f50de580c5ed281a4ade5e01575ee535c4a41d67048bed14d5b11801ac7eb7c4691d7e52d008be1022b65a9b799725a10f2f30b7b3a7c91a815e154961acb74dd958f3f8c21f6e159b4b44078123c6d7ade0f59335e624112bacd33e9b053d5949345686223d6e1bbc89dccb159efde06dc3110582caf11563a1e36f1118feb3246941379b3b8e11d19bd27fb559638607753525c5c5161c0ee30e339114e9d1d290f750379e78160b1c3f8fda76ba74750dc6402c18fa1178c631e59024f7edbb48a367e059c31c2d055bb7401d2c8bc9d3b51f4dae021724047c5fcd7d566fd40f2be6708b6812066dc69848c9f07eedb2113d4e12741d9fbd9ee006a1c09857c2a7fda40e9017e6ca4b4dd2800047799becca50a5d912a24479481dce00d88ec5ad448108291e82d02d6d17d833133fd1579d9ad32018cea624247946f6a865a7ac4a15764d137da43aa08e3dbd746fa57a778856e21e645095e63e31645d8cb7fbc50612b518b7a6aaebcb8db64a702c96d16b0ec41357a4aa12131624111a47f0e81217a01fdfe9ee0edc4483da146cf35342df4c198021bfd87c9d02e243232943687f3d143381327afd7d684e361c54cfb9323110b8ce509095c9404abdc6b94b2951e819c60ba7a677d4330831d2c76f87dab9146b09ec1ec67629a08d0ed3bfd2ae9410dfdbac64a35742004917b8ff1d7e871a19e323eab5df01cda0126099b1313915aeb51c88914cce704d75e6ad278efa10e25594a6b5ade31aafbb70490c7d2a1be8774385c457e97bf2628d073d97bb1587f935046a7987c98eb50a0664df621171c2bc06108fa575e48877d66c65d11b2735ca6ba6a5b7f7e9d392abf01d41161fc4a1bc1e1ec65fee0f0f568db1cd1165d302616463a3ff16b3b189484f7c1c51dc9b4d501ce932df288ed406d9c9160e7d497173e3208fb220d87605143b127c2e0f8285059b7eeacd59f13b532b1dcabea5019e37afcbeed747f42fdc5517a19884344bf95809bfac6cc38c16ab120000000000000000000000000000001000000000000000000000000000000014000000000000000000000000000000190000000000000000000000000000401f0000000000000000000000000000881300000000000000000000000000006a180000000000000000000000000080841e00000000000000000000000000d012130000000000000000000000000084d7170000000000000000000000000065cd1d000000000000000000000000205fa012000000000000000000000000e8764817000000000000000000000000a2941a1d000000000000000000000040e59c30120000000000000000000000901ec4bc1600000000000000000000003426f56b1c0000000000000000000080e03779c31100000000000000000000a0d88557341600000000000000000000c84e676dc11b000000000000000000003d9160e45811000000000000000000408cb5781daf1500000000000000000050efe2d6e41a1b00000000000000000092d54d06cff010000000000000000080f64ae1c7022d15000000000000000020b49dd97943781a0000000000000000949002282c2a8b100000000000000000b9340332b7f4ad140000000000000040e70184fee471d91900000000000000883081121f2fe7271000000000000000aa7c21d7e6fae0311400000000000080d4dbe98ca039593e19000000000000a0c95224b00888ef8d1f00000000000004beb3166e05b5b5b81300000000000085ad609cc94622e3a618000000000040e6d878037cd8ea9bd01e0000000000e88f872b824dc7726142130000000000e27369b6e22079cff912180000000080dad003641b695743b8171e00000000908862821eb1a1162ad3ce1200000000b42afb22661d4a9cf48782170000000061f5b9abbfa45cc3f129631d000000a05c3954cbf7e6191a37fa5d12000000c8b34729beb560a0e0c478f516000000baa099b32de378c818f6d6b21c00004074044090fc8d4b7dcf59c6ef11000050910550b47b719e5c43f0b76b160000a4f50664a1da0dc63354eca5061c0080865984dea4a8c85ba0b4b32784110020e86f2516ced2ba72c8a1a031e5150028e2cbae9b8187698f3aca087e5e1b00596d3f4d01b1f4a199647ec50e1b1140af488fa041dd710ac0fddd76d2611510db1ab30892540e0d307d951447ba1aeac8f06f45dbf428083e6edd6c6cb41024fbeccb161232338acdc9148887e114ed39e87e9c96febfec40fc196ae9191a342451cf211efff793a83d50e2315010416d2543aae5fef5b8124de45a3e641492c8eed3149f7e336757609df14d7d19b67aea08da465e00416db8046ea1dc1fb28c924548ec3aa04844f3c2e4e4e913de2ff7565aa749c85a15b0f31d5ee418d6fbb4ec30115c7ab11a9c70a5751d1f651df193be8a79ecae90616687697213bf64ed386eed97a7daf4f93fe9034f18efbd28c7c9e87d511172f88fe3c4621eb576791c7eb1eed24a47fb390ebbfd1262d497a3dd5daa871d197ac8d129bd177bc97d0c55f594e9649f983a4674ac1ded9dce275519fd119f639fe4abc88b126845c271aa5f7cd6863cc7ddd6ba2e17c2d6320e95771b8ca80b39958c69fa1c39c6df28bd2a915749a743ddf7811c12c8b717736c7575ad1b9194d475a2a316baa5dd8fc7d2d29862b5b949138b4c1c9487eab9bcc3839f5d11140eecd6af11792965e8abb46407b5159911a7cc1b16d7737ee2d6e13d49225bffd5d0bfa21b66088f4d26adc66df598bf85e2b7451180caf2e06f5838c9327f2f27db259715207d2fd98b6e867bff5efbf051effc1a34aebd67170534ad5f1b9d369315de10c119ad415d06819837624404f89a151532601892f447a17ec57a5505b6015b1a1f3c4fdbf8cc246fbb6c55c311e17810270b23123700ee4aeac72a3456199714f0cdabd64480a9dde47935c1abdfbc19b6602b062bf0890a2f6cc158cb0b1610e438b6c7356c2ccd3ac7f12ebe8e1b141dc7a339438777800939aeba6d722219e4b80c08146995e04bc75929090f6b1f8ef30785ac615d6c8f1cd8b965e9a21372f049a617ba7447b3234e28bfa38b188f6cdc8f9de85119a0ac61f2ae8cae1ed9c3e9796231d30fe40b7d57ed172d13cf346418bbfdc713dd4e5cade85df81703427dde29fdb9589462b3d86275f61d42490e2b3a3e74b79c1d70c75d09ba1292dbd1b5c84d51e503254c39b58b6817775246e33aa1a5de442e9f87a2ae421d8af30bcec484270beb7cc39425ad49126df08e01f665f1cd255cf4f96e18dc1688acf28173bf6d412f7371b88a1e931cd5ab3731a897e488fde746b316f3db11ca96853d92bd1debfca11860dcef52167dfce6ccf62ce5257cca1e78d3abe71bce5d10401a3caf978d3e132b64cb7011427514d0200b9bfd300ed8353dfecc1592921904e9cd013dbd114e83cc3d401b9bfb8fa2b120214616cb10d29f26081182fa330bde68a9d7dbfd94c647304a1523f9008e15c393cd523d3ab859bc9c1ab69bc078ed597cc053662413b8f5a110a3c2f0d668709bb0e87fed172673ca144cf3ac0c834cc2dce2dfe89def0ffd190f18ece7d16ff9c9ed8bb1c2f5293e10131ee761c6cb773ce9ee5d3373b44d1498e560fab7be958ba36a350090216119fe1ef9f8652e7b6e4cc54200f469b91f5fb39bbbfffc0cc54fbb298038e2d31337a082aa3f3c50b6232a34a0c6dac818444823954f4be4a3ac3441487811fb1e2b0d36bd11af6ee6ebc0282debea5c137590832cd65a0ae026f172f8a52534189374a4b78bf10c9870ad8f760f2f411edcc8c652f716085f66cc19aa69bde812137b7827b51ccaf67f3fa014c4eca217d7995671e2a37cf45f4fc819f5a78b1d2620d6866de6cdf89b311d30f948771230a88be8086001f7027e247c371b15173c92ae220bb8c1b4839d2d5b0562da1c651badf50613f9507282fc58437d08123f6218b3c85737e50ea33b2f949c8a16cf7adedfba2d859ed28b0a3bb9432d1cc10cebcb943c13a36397e6c4534a9c11f1cfe5feb90bd88b3c3d20b6e85c0316ee439f7ea80eceae8b4ca8e32234841b758a234f29c9404dd72f49ce95a03211126deca273fb9020cd7bdb41bb487f155688a78b503ab568c05a5212ea1adf1a36b5485772447141b878734bd270cb1083e21aed8e95cd51e65650de064dfe14249b61a8f2fa40e69f6ce49548e03d1af7003da9d79ce8efe3c3ae5d2dac661034418c930dc4e2ebdc741ab53857801481516ff81075db26141261e2066da019f192459b2a2949984cab7c4d24440410adf7164275735bbe1fd6db602d55051498b59c925250f2ada7cb12b978aa0619ffe2433767e46e99917e57e71655481fdf6d8a82c04ee5ff1aaf96502e358d1357092da370a2debfe15abce479827018ad4bf8cb0c4bd62f9a71eb5d18a38c1e4c2f7bffe7eee55d0027b33aefe517131ffb59ffa16a5f75c0f05f096bdfdd17e779307f4a45b792f0ecb7cb4557d51d304c7e8f4e8bb25b16f4529f8b56a5123cdf5d33222e9ff21bb127872eac4e170b5735c0aaf946ef629df1283a57221d675621b80a5c8cd55d0297598476351201ac29660d73ef4af5c2fc6f25d4c2160117b4bfd04fab9db2f3fbcb2e89731c608ed077e2118ba24f787d3fbd35c811f9b1c4155bd62d8b63d65c8f2c433a1677de35dbf14bf96dfc0b34b3f7d3c81b0aab012977cfbbc47d8700d07a845d11cd1542f354c3ea355da9008499e5b415409b12302a746583b4d300e5ff1e221b08a10b5e9a681fd2508420ef5f53f5104a898ef5c042a70665a5e8ea37a832159d2bf23271135148becea2e545527f1a425bd7bf26ac32ed36c185af6b938f101232cd6f30577fa88431679b4678b314977ec08bfc2c9fd2e5fd40425856e0191e4f58d71d7ca3a3af9e6829f7352c10e6622e4d255b8c8c5bc6c2f3744337149ffb79a0ee71af6ff277b33052144519877a98486a4e9b0bef55e0bc6659961f944c5f6d02114167b5350c36e0f7bd13ba1fb708435511c122438f43d875ad18a8e7e4ca93aa5571eb1373544ed3d81ec910cf5e9c8ad52673ecc7f410844713fbd4827643ed8af08fe7f931156519183a8a235494a8adec7361787e5abe1f1e643696b45c89ec73e83c0b8ff8d6d312fdc3bbe1b3abe790220cceb2b6cc8817fdb42adaa09621352b8f815fe4ff6a1d1eb15a8824fe34017bf9b0bbeedf6212655d71aaad3d82c1d9379d6aea97fb16bfb40d1519cde231d0854405e57dba1cf79028ad2fc02d1fa2d34a23af8ef41135b572983b30f9a68a881dec5ab2711682628f7e4a7cb750adea24a7f11e0e1c919d198faead7252ac12770857d38811f604e0321a590f6757d794ca2c08eb15330698bf602fd3402d0d3afd37ca651be003bf779cfd83483c4844fe629e1f11d8c4ae9503fda45a4b5ad5bdfb8567150e761a7b443c4e31deb04aad7a67c11ac989f0ccaae5d0de8aae4eacace0b8103bac2c80151f85962d5a62d7d718e7144ad737e0da6626fcb8f03acd0ddf201a8ee622cc4800989d73d644a0688b541032a02bff5a00fe84100c56c842ae69143e88f6be71803da6148f6b7ad31984194e2ab42e8ee0cccfd97206594820e51f709a30dd580ce021c807a4372d34ef130dc17c146f0f582aba098d853801eb1850f19bd94a13eeb4284cf0a686c1251fd27601c80ecc1471992f5628f498771386d4017a12ff59cd7fbb6b32317f5518a8498218d77eb0c05faa067ffdde6a1e096e516f464f6ed87b2a646f5ecb02138bc9250b18e389ce1a353d0b367ec317ee3bef0dde5b2c8261820c8ec35db41d7585b5c86ab95bf17cd1c7389aba9012d2e6e27ac5a7b22ddcc5f9c640e9341786a09bd9b6511f395337b8f89023021d544401481293b3039422739b3a562112699501dad677a00439eb4f42c9aba916c3fa8190cc95c84507e6e392bb16541cba3c51da9f5d9d8bc46fce3b358eb411e88be5d007b584aeb50bc28ac2b12116e3ee1ec549e2251aa38e722d331eaa1b4d55331b6ead57f0259967fcdf524a11a12a00a2c9986d6c6f7f81fb97e79c154935800afcfe88474bdf61fa7d21041b4e2190865d9fb50c8f2b7dbcee94e210a12934e83407e3cf72769c6b2a3a1b150a34412202c9db830f948306b508621a86c06855a15d69b2893c122471457d10a7f0c2aa09b5031faccb166dcd969c14d1ac73154ca2c426977e5cc880bcc319034c688d6fe53a781ecf397dd0551a10035fc270cb9e4916e642889c44eb2014c4f6f24c7e06dc9b9f53aac31526291976b42fe01d08d38287e894349b6f731fc9d01dac12e5c3b15411dd00c125a813fc44255757de34dea9551441312f92183b96ee2ced15c255146b5991fdbab61ee51d153cb44d99b5ece2d77ade3432135e651a4b21a1ffe2a7db8d1916c2fe17b6fee09d6989bfdb9152f19f9b72fe1d319fac02e2b557299bd3f643a107bf12fec657835aa3adf38188f49489c96e17bdb82d24310c9970a2aa31faeb7b4a1d76939cb69ea75f86a50a5f7c738d4e1254b843648691f7e74ecd765bd030e21669a654fde775f5a1a280547204bd9a1c01e854feb06939a565d074c722b6e0110222ea3d1dc4870e7f045279abe3581682aa648d24b529d29e85a657961cef1b91ea5ed836115a438313c8f6dd71751136a5768e8495301464187a7455ced215834e14b2e5ba3c197d9e98d1ea81471b12b14c8fcff4c52f0e63ffc232b10c1156dd1f730372b7bbd13bbf737fdd4f15acd4e74f844ea52ac60aaf50dfd4a31aebe4f0b11251a7dabb666d920b65a610261e6d5e572551d16ac008774efecf14b0650836ad6ea58585f0ca14e2fd031a8e3fc5412c65877353d6fe4cad7e4210718f3652773e6950e88b3ea0581e53144e33c426158e8364e22e4ec8eee56719224075709a71a4fd9aba617a6adfc11f1548498600c786dea0147d8ca22bd9131a9adba7c0782816c9599c2f8b76cf18a180d2d1f096b25b3b7083fb2d54031f64902383569e4f19252632bd9c1462137e74ec23ec85a35faeaf7eecc3993a189d91e72c67678cf7995b9ee73440491e02bb107ca0c0b73a40f9c21021c8ed12c3e9149bc8b0654990b7f354293aa9173324dac1fa1cbf5b74a530aab388931da05628b91c7257b968675e4a70357c12486c72e7a34eade74201f65ccc421b175a074fe14ca298a1938133747f13e21c9864d10c7065ff44fc30a0a82f4c0d12bebd0510cc3e3f563b3dc8923b9f90162e2d07147f0ecf2b8a4c7a770ac7341c3d7c846c0f69615bd66fac8a66fca0114c9ba54753c339f2cb8b572d803b09161f028f192834c8eebe6ead38608a8b1b5361f90f99203d5537656c237c363711a8b9f753bf688c2a857e472c1b04851512a8f528ef822f75265e59f72145e61a0b899979d5b13d09d8da973a35ebcf104eebffd74a1e8d0b8ed13d8902e6031522e6ff8ddd65708ef1458d2b83df441ad5efbf78aa3f06f9b64b38fbb10b6b10caebef1695cf47b7a45e067a9ece8514bde6ab5c7ac319e54df687184642a7193670eb792c1a30aff0f954cf6b890810434c6698b720fcda6c382ac3c6ab0a1454df7f7ee528bb1188c6f473b8560d192ad71fde1ef329162af8f19066ac501f7ae6d34af337da4d1a3b971ac06b921319e0881df0c550e1e0093d21b00677181f18eb246cf7a419594c8c295cc8941e13ef1297a31a07b0b7aff79939fd1c13d8aad77c4ce1089ca59b7500883ce4178e950d9c9f190b038f029300aa4bdd1d797d88c103f0e66199e15b404a4faa12d79ceab104ac60baffd972d01ce354170d4465de05d7f8a87f908f04e41b2a1d884affaa63869bc94fbad9826e513a122a1dbf95fc6702bce3289023cae5c81674e42ebbfb0103ab1c3374ac3c1f7b1cc94efd543de1e1eaf19fc8eb85f3cc117ba23caa8c599a65eec7ba66673040161acbcbd4efef00ffe9796940813cd01bf05effe4f595603f32ec41c8d0256211ac363f5e73bb38cf3e6752fa44afba155704cf3550ea06830e01e738165b291bb662a1217252e411a96090e3edd8f91064bb09aa0e675d56d378745c294f38153d2a8c54d2c0f42b089791b3f362861a669ad77483f8781b65fe3a50d8fd931000810d52a4365762febd49644efdb81440e190664d04edfa7d2d5cfda13ce719c88c1a60b022d4bc6e9c593ee5853010fa2f21785c2b096c8a03f08d5ea73c14f87b299633760b076d046c3136d14b19f6dab37bc053ce488805c7bd83c59e1fda68504d58f4802d75639c56723bc3131083a4606e31e178527c43ec4e0ab4183030303130323033303430353036303730383039313031313132313331343135313631373138313932303231323232333234323532363237323832393330333133323333333433353336333733383339343034313432343334343435343634373438343935303531353235333534353535363537353835393630363136323633363436353636363736383639373037313732373337343735373637373738373938303831383238333834383538363837383838393930393139323933393439353936393739383939302e302f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f636f6c6c656374696f6e732f62747265652f6d61702f656e7472792e7273d3681000850000007001000036000000617373657274696f6e206661696c65643a20696478203c2043415041434954592f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f636f6c6c656374696f6e732f62747265652f6e6f64652e7273617373657274696f6e206661696c65643a20656467652e686569676874203d3d2073656c662e686569676874202d20318869100080000000af020000090000008869100080000000b302000009000000617373657274696f6e206661696c65643a207372632e6c656e2829203d3d206473742e6c656e282988691000800000002f070000050000008869100080000000af040000230000008869100080000000ef04000024000000617373657274696f6e206661696c65643a20656467652e686569676874203d3d2073656c662e6e6f64652e686569676874202d20310000008869100080000000f0030000090000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f636f6c6c656374696f6e732f62747265652f6e617669676174652e7273f86a10008400000059020000300000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f7374722f7061747465726e2e72738c6b100074000000b7050000140000008c6b100074000000b7050000210000008c6b100074000000ab050000210000008c6b1000740000003b040000240000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f7374722f7061747465726e2e7273406c100074000000b705000014000000406c100074000000b705000021000000406c100074000000ab050000210000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f636f6c6c656374696f6e732f62747265652f6e617669676174652e7273e46c100084000000170200002f000000e46c100084000000a200000024000000406c1000740000003b040000240000003a3a416c6c2070617468207365676d656e74732073686f756c642062652076616c69642052757374206964656e746966696572734900000008000000040000004a0000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f74792f706174682e72730000dc6d1000620000005a0000000e000000dc6d100062000000720000000a000000206973206e6f7420612076616c69642052757374206964656e74696669657200986d100000000000606e10001f000000dc6d100062000000900000002100000072234d697373696e675365676d656e7473496e76616c69644964656e7469666965727365676d656e740000004900000004000000040000004b0000004c00000004000000040000004d000000012f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f737562746c652d322e352e302f7372632f6c69622e72730000ed6e100059000000bf0200000900000057000000080000000400000058000000590000005a000000756e69746120737472696e6762797465206172726179626f6f6c65616e206060866f1000090000008f6f100001000000696e74656765722060000000a06f1000090000008f6f100001000000666c6f6174696e6720706f696e742060bc6f1000100000008f6f100001000000636861726163746572206000dc6f10000b0000008f6f100001000000737472696e672000f86f100007000000756e69742076616c75654f7074696f6e2076616c75656e6577747970652073747275637473657175656e63656d6170656e756d756e69742076617269616e746e6577747970652076617269616e747475706c652076617269616e747374727563742076617269616e746578706c696369742070616e69632f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f73657264652d312e302e3230362f7372632f64652f6d6f642e72737f7010005d000000ec080000120000008f6f1000010000008f6f10000100000060206f72206000008f6f100001000000fc701000060000008f6f1000010000006f6e65206f66202c20000000586f1000000000002e307536346e756d6265720000000000000000000000f03f000000000000244000000000000059400000000000408f40000000000088c34000000000006af8400000000080842e4100000000d01263410000000084d797410000000065cdcd41000000205fa00242000000e876483742000000a2941a6d42000040e59c30a2420000901ec4bcd64200003426f56b0c430080e03779c3414300a0d8855734764300c84e676dc1ab43003d9160e458e143408cb5781daf154450efe2d6e41a4b4492d54d06cff08044f64ae1c7022db544b49dd9794378ea449102282c2a8b2045350332b7f4ad54450284fee471d9894581121f2fe727c04521d7e6fae031f445ea8ca039593e294624b00888ef8d5f46176e05b5b5b893469cc94622e3a6c846037cd8ea9bd0fe46824dc77261423347e32079cff91268471b695743b8179e47b1a1162ad3ced2471d4a9cf487820748a55cc3f129633d48e7191a37fa5d724861a0e0c478f5a64879c818f6d6b2dc484c7dcf59c6ef11499e5c43f0b76b4649c63354eca5067c495ca0b4b32784b14973c8a1a031e5e5498f3aca087e5e1b4a9a647ec50e1b514ac0fddd76d261854a307d951447baba4a3e6edd6c6cb4f04acec9148887e1244b41fc196ae9195a4ba93d50e23150904b134de45a3e64c44b57609df14d7df94b6db8046ea1dc2f4c44f3c2e4e4e9634c15b0f31d5ee4984c1b9c70a5751dcf4c916166876972034df5f93fe9034f384d72f88fe3c4626e4d47fb390ebbfda24d197ac8d129bdd74d9f983a4674ac0d4e649fe4abc88b424e3dc7ddd6ba2e774e0c39958c69faac4ea743ddf7811ce24e9194d475a2a3164fb5b949138b4c4c4f11140eecd6af814f169911a7cc1bb64f5bffd5d0bfa2eb4f99bf85e2b74521507f2f27db259755505ffbf051effc8a501b9d369315dec050624404f89a15f5507b5505b6015b2a516d55c311e1786051c82a3456199794517a35c1abdfbcc9516cc158cb0b160052c7f12ebe8e1b345239aeba6d72226952c75929090f6b9f521dd8b965e9a2d352244e28bfa38b0853ad61f2ae8cae3e530c7d57ed172d73534f5cade85df8a75363b3d86275f6dd531e70c75d09ba1254254c39b58b6847542e9f87a2ae427d547dc39425ad49b2545cf4f96e18dce6547371b88a1e931c55e846b316f3db5155a21860dcef528655ca1e78d3abe7bb553f132b64cb70f1550ed8353dfecc2556124e83cc3d405b56cb10d29f26089156fe94c647304ac5563d3ab859bc9cfa56662413b8f5a1305780ed172673ca6457e0e89def0ffd99578cb1c2f5293ed057ef5d3373b44d04586b35009021613958c54200f469b96f58bb298038e2d3a3582a34a0c6dac8d8583541487811fb0e59c1282debea5c4359f172f8a525347859ad8f760f2f41ae59cc19aa69bde8e2593fa014c4eca2175a4fc819f5a78b4d5a321d30f94877825a7e247c371b15b75a9e2d5b0562daec5a82fc58437d08225ba33b2f949c8a565b8c0a3bb9432d8c5b97e6c4534a9cc15b3d20b6e85c03f65b4da8e32234842b5c3049ce95a032615c7cdb41bb487f955c5b5212ea1adfca5c79734bd270cb005d5750de064dfe345d6de49548e03d6a5dc4ae5d2dac66a05d751ab5385780d45d1261e2066da0095eab7c4d244404405ed6db602d5505745ecc12b978aa06a95e7f57e7165548df5eaf96502e358d135f5bbce4798270485f72eb5d18a38c7e5f27b33aefe517b35ff15f096bdfdde75fedb7cb4557d51d60f4529f8b56a55260b127872eac4e87609df1283a5722bd60029759847635f260c3fc6f25d4c22661f4fbcb2e89735c61787d3fbd35c89161d65c8f2c433ac6610c34b3f7d3c8fb618700d07a845d3162a9008499e5b46562d400e5ff1e229b628420ef5f53f5d062a5e8ea37a8320563cfa2e545527f3a63c185af6b938f706332679b4678b3a463fe40425856e0d9639f6829f7352c1064c6c2f3744337446478b330521445796456e0bc665996af64360c36e0f7bde364438f43d875ad18651473544ed3d84e65ecc7f41084478365e8f931156519b86561787e5abe1fee653d0b8ff8d6d322660cceb2b6cc8857668f815fe4ff6a8d66f9b0bbeedf62c266389d6aea97fbf666864405e57dba2c67d44a23af8ef46167891dec5ab2719667eb24a7f11e0ecc6713770857d3880168d794ca2c08eb35680d3afd37ca656b684844fe629e1fa1685ad5bdfb8567d568b14aad7a67c10a69af4eacace0b840695a62d7d718e77469f13acd0ddf20aa69d644a0688b54e0690c56c842ae69146a8f6b7ad31984496a7306594820e57f6a08a4372d34efb36a0a8d853801ebe86a4cf0a686c1251f6b305628f49877536bbb6b32317f55886baa067ffdde6abe6b2a646f5ecb02f36b353d0b367ec3276c820c8ec35db45d6cd1c7389aba90926cc6f9c640e934c76c37b8f8902302fd6c23739b3a5621326deb4f42c9aba9666de6e392bb16549c6d70ce3b358eb4d16d0cc28ac2b121066e8f722d331eaa3b6e9967fcdf524a716e7f81fb97e79ca56edf61fa7d2104db6e2c7dbcee94e2106f769c6b2a3a1b456f948306b508627a6f3d122471457db06fcc166dcd969ce46f7f5cc880bcc31970cf397dd0551a507043889c44eb20847054aac3152629b970e994349b6f73ef7011dd00c125a82371561441312f9258716b5991fdbab68e71e3d77ade3432c371dc8d1916c2fef77153f19f9b72fe2d72d4f643a107bf627289f49489c96e9772ab31faeb7b4acd720b5f7c738d4e0273cd765bd030e2367381547204bd9a6c73d074c722b6e0a173045279abe358d67386a657961cef0b7414c8f6dd71754174187a7455ced275749e98d1ea8147ab7463ffc232b10ce1743cbf737fdd4f15750baf50dfd4a34a75676d920b65a68075c008774efecfb475f1ca14e2fd03ea75d6fe4cad7e4220768c3ea0581e5354762f4ec8eee5678976bb617a6adfc1bf76157d8ca22bd9f3765a9c2f8b76cf28777083fb2d54035f772632bd9c14629377b07eecc3993ac8775c9ee7344049fe77f9c21021c8ed3278b8f354293aa96778a530aab388939d78675e4a70357cd27801f65ccc421b07798233747f13e23c7931a0a82f4c0d72793dc8923b9f90a6794d7a770ac734dc7970ac8a66fca0117a8c572d803b09467a6fad38608a8b7b7a656c237c3637b17a7f472c1b0485e57a5e59f72145e61a7bdb973a35ebcf507bd23d8902e603857b468d2b83df44ba7b4c38fbb10b6bf07b5f067a9ece85247cf687184642a7597cfa54cf6b8908907c382ac3c6ab0ac47cc7f473b8560df97cf8f19066ac502f7d3b971ac06b92637d0a3d21b00677987d4c8c295cc894ce7db0f79939fd1c037e9c7500883ce4377e039300aa4bdd6d7ee25b404a4faaa27eda72d01ce354d77e908f04e41b2a0d7fbad9826e513a427f299023cae5c8767f3374ac3c1f7bac7fa0c8eb85f3cce17f600000000c000000040000006100000062000000630000006120446973706c617920696d706c656d656e746174696f6e2072657475726e656420616e206572726f7220756e65787065637465646c7900640000000000000001000000650000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f737472696e672e7273487b100070000000330a00000e0000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f7374722f7061747465726e2e7273c87b1000740000000e06000014000000c87b1000740000000e06000021000000c87b1000740000000206000014000000c87b10007400000002060000210000004572726f72617373657274696f6e206661696c65643a2073656c662e69735f636861725f626f756e64617279286e65775f6c656e29000000487b100070000000740500000d000000c87b1000740000008f04000024000000454f46207768696c652070617273696e672061206c697374454f46207768696c652070617273696e6720616e206f626a656374454f46207768696c652070617273696e67206120737472696e67454f46207768696c652070617273696e6720612076616c7565657870656374656420603a60657870656374656420602c60206f7220605d60657870656374656420602c60206f7220607d606578706563746564206964656e7465787065637465642076616c7565657870656374656420602260696e76616c696420657363617065696e76616c6964206e756d6265726e756d626572206f7574206f662072616e6765696e76616c696420756e69636f646520636f646520706f696e74636f6e74726f6c2063686172616374657220285c75303030302d5c75303031462920666f756e64207768696c652070617273696e67206120737472696e676b6579206d757374206265206120737472696e67696e76616c69642076616c75653a206578706563746564206b657920746f2062652061206e756d62657220696e2071756f746573666c6f6174206b6579206d7573742062652066696e6974652028676f74204e614e206f72202b2f2d696e66296c6f6e65206c656164696e6720737572726f6761746520696e2068657820657363617065747261696c696e6720636f6d6d61747261696c696e672063686172616374657273756e657870656374656420656e64206f662068657820657363617065726563757273696f6e206c696d6974206578636565646564206174206c696e652020636f6c756d6e20000000c87b100000000000087f100009000000117f1000080000004572726f72282c206c696e653a202c20636f6c756d6e3a2029000000347f1000060000003a7f100008000000427f10000a0000004c7f100001000000696e76616c696420747970653a202c20657870656374656420000000707f10000e0000007e7f10000b000000696e76616c69642076616c75653a20009c7f10000f0000007e7f10000b000000666c6f6174696e6720706f696e74206060000000bc7f100010000000cc7f1000010000006e756c6c2f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f73657264655f6a736f6e2d312e302e3132342f7372632f6572726f722e7273000000e47f100061000000f701000021000000e47f100061000000fb0100000c000000e47f1000610000000202000021000000e47f1000610000000b0200002a000000e47f1000610000000f0200002c00000030313233343536373839616263646566757575757575757562746e7566727575757575757575757575757575757575750000220000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f73657264655f6a736f6e2d312e302e3132342f7372632f726561642e7273a881100060000000a301000045000000a881100060000000a80100003d000000a881100060000000b00100001a000000a881100060000000f801000013000000a881100060000000fd01000033000000a881100060000000010200003e000000a881100060000000070200003a000000a8811000600000005402000013000000a8811000600000006c02000025000000a881100060000000bc0300002f000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00010203040506070809ffffffffffffff0a0b0c0d0e0fffffffffffffffffffffffffffffffffffffffffffffffffffff0a0b0c0d0e0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff696e662d696e664e614e0000660000000c00000004000000670000006800000063000000696e7465726e616c206572726f723a20656e746572656420756e726561636861626c6520636f64652f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f73657264655f6a736f6e2d312e302e3132342f7372632f696f2f636f72652e727300f48310006300000012000000090000005075626c696373705f6170706c69636174696f6e5f63727970746f3a3a737232353531393a3a6170705369676e617475726550617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e727300ab84100060000000b20000001e0000005075626c696373705f6170706c69636174696f6e5f63727970746f3a3a65636473613a3a61707073757065723a3a5075626c69635369676e617475726573757065723a3a5369676e617475726573705f6170706c69636174696f6e5f63727970746f3a3a656432353531393a3a61707041726974686d657469634572726f7273705f61726974686d65746963556e646572666c6f774f766572666c6f774469766973696f6e42795a65726f0073705f61726974686d657469633a3a7065725f7468696e677350657262696c6c50617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e7273000000f985100060000000b20000001e00000075333250617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e72738086100060000000b20000001e000000737232353531393a3a5075626c6963005075626c696373705f636f6e73656e7375735f617572613a3a737232353531393a3a6170705f7372323535313942414245736c6f74206e756d62657263757272656e742065706f6368636861696e2072616e646f6d6e6573732f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f7072696d6974697665732f636f6e73656e7375732f626162652f7372632f6c69622e7273736c6f74206e756d626572206973207536343b2069742073686f756c642072656c61746520696e20736f6d652077617920746f2077616c6c20636c6f636b2074696d653b20696620753634206973206e6f7420656e6f7567682077652073686f756c6420637261736820666f72207361666574793b207165642e0059871000680000007e0100000a00000042616265436f6e66696775726174696f6e73705f636f6e73656e7375735f62616265416c6c6f776564536c6f74735072696d617279536c6f74735072696d617279416e645365636f6e64617279506c61696e536c6f74735072696d617279416e645365636f6e64617279565246536c6f747376617269616e74206964656e7469666965724261626545706f6368436f6e66696775726174696f6e737472756374204261626545706f6368436f6e66696775726174696f6e4f70617175654b65794f776e65727368697050726f6f6645706f6368005072696d61727950726544696765737473705f636f6e73656e7375735f626162653a3a646967657374735365636f6e64617279506c61696e5072654469676573745365636f6e646172795652465072654469676573745072654469676573745072696d6172795365636f6e64617279506c61696e5365636f6e646172795652464e657874436f6e66696744657363726970746f7256315075626c696373705f636f6e73656e7375735f626162653a3a61707050617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e727300e389100060000000b20000001e000000617574686f726974795f696e64657873757065723a3a417574686f72697479496e646578736c6f74536c6f747672665f7369676e61747572655672665369676e61747572655072696d6172795072654469676573745365636f6e64617279506c61696e5072654469676573745365636f6e6461727956524650726544696765737463287536342c2075363429616c6c6f7765645f736c6f7473416c6c6f776564536c6f7473737232353531393a3a5075626c6963736c6f745f6475726174696f6e75363465706f63685f6c656e677468617574686f7269746965735665633c28417574686f7269747949642c2042616265417574686f72697479576569676874293e72616e646f6d6e65737352616e646f6d6e6573735665633c75383e65706f63685f696e64657873746172745f736c6f746475726174696f6e636f6e6669674261626545706f6368436f6e66696775726174696f6e50617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e727300bb8b100060000000b20000001e0000005075626c696373705f636f6e73656e7375735f6772616e6470613a3a617070656432353531393a3a5075626c69635369676e6174757265656432353531393a3a5369676e617475726550617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e72730000868c100060000000b20000001e000000536c6f7473705f636f6e73656e7375735f736c6f7473753634536c6f744475726174696f6e0000005353353850524573705f636f72653a3a63727970746f4b6579547970654964004f70617175654d6574616461746173705f636f7265566f69640000005672665369676e617475726573705f636f72653a3a737232353531393a3a76726650617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e727300008e8d100060000000b20000001e0000005b75383b20345d5665633c75383e7072655f6f75747075745672665072654f757470757470726f6f6656726650726f6f6650617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e72730000428e100060000000b20000001e00000042547265654d61704b560000496e686572656e744461746173705f696e686572656e74736461746142547265654d61703c496e686572656e744964656e7469666965722c205665633c75383e3e436865636b496e686572656e7473526573756c746f6b6179626f6f6c666174616c5f6572726f726572726f7273486f737420746f207761736d2076616c7565732061726520656e636f64656420636f72726563746c793b207165648a00000000000000010000008b0000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f7072696d6974697665732f72756e74696d652d696e746572666163652f7372632f696d706c732e72730000006c8f10006d000000d10000002a0000004572726f72486f737420746f207761736d2070726f766964657320612076616c696420656e756d206469736372696d696e616e743b207165640000008d00000000000000010000008e0000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f7072696d6974697665732f72756e74696d652d696e746572666163652f7372632f706173735f62792e727300389010006f000000a8010000200000008f0000000d0000009000000091000000486f737420746f207761736d2076616c7565732061726520656e636f64656420636f72726563746c793b2071656400008d000000000000000100000092000000389010006f000000000100002b00000028294572726f7200b89010000000000072756e74696d65d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a4890b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe221cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c50617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e7273c091100060000000b20000001e000000436f6e73656e737573456e67696e6549645665633c75383e6c6f67735665633c4469676573744974656d3e696e64657875386572726f724d6f64756c654572726f72546f6b656e4572726f7241726974686d657469634572726f72496e76616c69645472616e73616374696f6e556e6b6e6f776e5472616e73616374696f6e7072696f726974795472616e73616374696f6e5072696f7269747972657175697265735665633c5472616e73616374696f6e5461673e70726f76696465736c6f6e6765766974795472616e73616374696f6e4c6f6e67657669747970726f706167617465626f6f6c5b75383b204d41585f4d4f44554c455f4552524f525f454e434f4445445f53495a455d5472616e73616374696f6e616c4572726f724469676573744974656d73705f72756e74696d653a3a67656e657269633a3a64696765737450726552756e74696d65436f6e73656e7375735365616c4f7468657252756e74696d65456e7669726f6e6d656e74557064617465644469676573744974656d206e6f7420657175616c446967657374c70000000c00000004000000c8000000c9000000ca000000cb0000000000000001000000cc000000cd000000ce000000f09310000000000048617368206e6f7420657175616c00005472616e73616374696f6e2063616c6c206973206e6f74206578706563746564496e6162696c69747920746f2070617920736f6d6520666565732028652e672e206163636f756e742062616c616e636520746f6f206c6f77295472616e73616374696f6e2077696c6c2062652076616c696420696e20746865206675747572655472616e73616374696f6e206973206f757464617465645472616e73616374696f6e20686173206120626164207369676e61747572655472616e73616374696f6e2068617320616e20616e6369656e7420626972746820626c6f636b5472616e73616374696f6e20776f756c6420657868617573742074686520626c6f636b206c696d697473496e76616c69645472616e73616374696f6e20637573746f6d206572726f72412063616c6c20776173206c6162656c6c6564206173206d616e6461746f72792c2062757420726573756c74656420696e20616e204572726f722e5472616e73616374696f6e206469737061746368206973206d616e6461746f72793b207472616e73616374696f6e73206d757374206e6f742062652076616c6964617465642e496e76616c6964207369676e696e672061646472657373436f756c64206e6f74206c6f6f6b757020696e666f726d6174696f6e20726571756972656420746f2076616c696461746520746865207472616e73616374696f6e436f756c64206e6f742066696e6420616e20756e7369676e65642076616c696461746f7220666f722074686520756e7369676e6564207472616e73616374696f6e556e6b6e6f776e5472616e73616374696f6e20637573746f6d206572726f723c7761736d3a73747269707065643e43616e6e6f744c6f6f6b7570496e76616c69645472616e73616374696f6e73705f72756e74696d653a3a7472616e73616374696f6e5f76616c696469747943616c6c5061796d656e744675747572655374616c6542616450726f6f66416e6369656e744269727468426c6f636b45786861757374735265736f7572636573437573746f6d4261644d616e6461746f72794d616e6461746f727956616c69646174696f6e4261645369676e6572556e6b6e6f776e5472616e73616374696f6e4e6f556e7369676e656456616c696461746f725472616e73616374696f6e56616c69646974794572726f72496e76616c6964556e6b6e6f776e5472616e73616374696f6e536f75726365496e426c6f636b4c6f63616c45787465726e616c56616c69645472616e73616374696f6e000000089410002894100061941000889410009f941000be941000e49410000e9510002d95100068951000ae951000200000003900000027000000170000001f000000260000002a0000001f0000003b0000004600000017000000c5951000069610004796100041000000410000001f00000044697370617463684572726f7273705f72756e74696d654d6f64756c654572726f725472616e73616374696f6e616c4572726f724c696d6974526561636865644e6f4c617965724f7468657243616e6e6f744c6f6f6b75704261644f726967696e4d6f64756c65436f6e73756d657252656d61696e696e674e6f50726f766964657273546f6f4d616e79436f6e73756d657273546f6b656e41726974686d657469635472616e73616374696f6e616c457868617573746564436f7272757074696f6e556e617661696c61626c65526f6f744e6f74416c6c6f776564546f6b656e4572726f7246756e6473556e617661696c61626c654f6e6c7950726f766964657242656c6f774d696e696d756d43616e6e6f74437265617465556e6b6e6f776e417373657446726f7a656e556e737570706f7274656443616e6e6f74437265617465486f6c644e6f74457870656e6461626c65426c6f636b656445787472696e736963496e636c7573696f6e4d6f6465416c6c45787472696e736963734f6e6c79496e686572656e74734f706171756556616c75652f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f636f6c6c656374696f6e732f62747265652f6d61702f656e7472792e72730000a9991000850000007001000036000000617373657274696f6e206661696c65643a20696478203c2043415041434954592f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f636f6c6c656374696f6e732f62747265652f6e6f64652e7273617373657274696f6e206661696c65643a20656467652e686569676874203d3d2073656c662e686569676874202d2031609a100080000000af02000009000000609a100080000000b302000009000000617373657274696f6e206661696c65643a207372632e6c656e2829203d3d206473742e6c656e2829609a1000800000002f07000005000000609a100080000000af04000023000000609a100080000000ef04000024000000617373657274696f6e206661696c65643a20656467652e686569676874203d3d2073656c662e6e6f64652e686569676874202d2031000000609a100080000000f0030000090000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f7072696d6974697665732f73746174652d6d616368696e652f7372632f73746174732e7273000000d09b1000690000007c00000023000000d09b1000690000007b0000001e000000d09b1000690000008100000024000000d09b100069000000800000001e000000416e204f7665726c617956616c756520697320616c7761797320637265617465642077697468206174206c65617374206f6e65207472616e73616374696f6e20616e642064726f7070656420617320736f6f6e0a09617320746865206c617374207472616e73616374696f6e2069732072656d6f7665643b207165642f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f7072696d6974697665732f73746174652d6d616368696e652f7372632f6f7665726c617965645f6368616e6765732f6368616e67657365742e727300f89c10007f0000000a0100002b000000f89c10007f000000140100002b000000607365745f7072657660206973206f6e6c792060536f6d65285f29602c206966207468652076616c75652063616d652066726f6d20706172656e743b20716564f89c10007f0000007d010000160000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f7665632f6d6f642e7273000000e89d10007100000027080000240000004c61796f757473697a650000d20000000400000004000000d3000000616c69676e000000d20000000400000004000000d400000043617061636974794f766572666c6f77416c6c6f634572726c61796f75740000d50000000400000004000000d60000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f736d616c6c7665632d312e31332e322f7372632f6c69622e727363616c6c65642060526573756c743a3a756e77726170282960206f6e20616e2060457272602076616c756500d50000000800000004000000d7000000d09e10005c000000520100002e0000006361706163697479206f766572666c6f77000000d09e10005c0000004101000036000000d09e10005c000000ce0400000e000000617373657274696f6e206661696c65643a206e65775f636170203e3d206c656ed09e10005c000000990400000d0000003a6368696c645f73746f726167653a64656661756c743a50617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e727304a0100060000000b20000001e0000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f7072696d6974697665732f747269652f7372632f6e6f64655f636f6465632e727300000074a01000650000003f0000002800000074a0100065000000480000001400000073705f747269653a3a73746f726167655f70726f6f6653746f7261676550726f6f66747269655f6e6f64657342547265655365743c5665633c75383e3e2f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f736c6963652f697465722e727339a1100073000000d60500001500000000000000617474656d707420746f20646976696465206279207a65726f4254726565536574542f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f7072696d6974697665732f747269652f7372632f747269655f73747265616d2e7273e2a11000660000004700000040000000e2a1100066000000470000004d0000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f7072696d6974697665732f747269652f7372632f6c69622e7273000068a210005e000000020200000b00000050617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e7273000000e9a2100060000000b20000001e000000436f775452756e74696d6556657273696f6e73705f76657273696f6e737065635f6e616d6552756e74696d65537472696e67696d706c5f6e616d65617574686f72696e675f76657273696f6e753332737065635f76657273696f6e696d706c5f76657273696f6e61706973417069735665637472616e73616374696f6e5f76657273696f6e73746174655f76657273696f6e7538576569676874287265665f74696d653a202c2070726f6f665f73697a653a2029f0a310001100000001a410000e0000000fa410000100000057656967687473705f776569676874733a3a7765696768745f763252756e74696d65446257656967687473705f7765696768747350617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e72730000006da4100060000000b20000001e0000007265665f74696d6575363470726f6f665f73697a657265616477726974652f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f636f6c6c656374696f6e732f62747265652f6d61702f656e7472792e727300fea41000850000007001000036000000617373657274696f6e206661696c65643a20696478203c2043415041434954592f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f636f6c6c656374696f6e732f62747265652f6e6f64652e7273617373657274696f6e206661696c65643a20656467652e686569676874203d3d2073656c662e686569676874202d2031b4a5100080000000af02000009000000b4a5100080000000b302000009000000617373657274696f6e206661696c65643a207372632e6c656e2829203d3d206473742e6c656e2829b4a51000800000002f07000005000000b4a5100080000000af04000023000000b4a5100080000000ef04000024000000617373657274696f6e206661696c65643a20656467652e686569676874203d3d2073656c662e6e6f64652e686569676874202d2031000000b4a5100080000000f003000009000000617373657274696f6e206661696c65643a206f6c645f6c6566745f6c656e203e3d20636f756e7400b4a5100080000000dd0500000d000000617373657274696f6e206661696c65643a206c656e203e2030000000b4a510008000000065010000090000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f636f6c6c656374696f6e732f62747265652f6e617669676174652e727388a7100084000000590200003000000088a7100084000000310200002f0000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f747269652d64622d302e32392e302f7372632f7472696564626d75742e72730000002ca81000610000006c0000001c0000002ca810006100000061010000320000002ca810006100000046010000320000002ca8100061000000d60100003c000000696e7465726e616c206572726f723a20656e746572656420756e726561636861626c6520636f64653a200000d0a810002a000000604e6f64654f776e65643a3a56616c7565602063616e206f6e6c792062652072657475726e656420666f72207468652068617368206f6620612076616c75652e04a91000400000002ca8100061000000bf0100001100000056616c7565206e6f64652063616e206e6576657220626520696e6c696e65643b207165645ca91000240000002ca8100061000000a400000015000000e10000000400000004000000e2000000e3000000e10000000400000004000000e4000000e50000004a75737420656e636f64656420746865206e6f64652c20736f2069742073686f756c64206465636f646520776974686f757420616e79206572726f72733b2071656400002ca81000610000005707000016000000e10000000800000004000000e6000000e70000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f747269652d64622d302e32392e302f7372632f6e6962626c652f6d6f642e7273000028aa100062000000490000001c000000617373657274696f6e206661696c65643a20216578697374696e675f6b65792e69735f656d707479282900002ca8100061000000f5040000150000002ca81000610000001f0300001e0000002ca8100061000000130300001e0000002ca8100061000000a8060000290000004272616e63682077697468206e6f2073756276616c7565732e20536f6d657468696e672077656e742077726f6e672e002ca81000610000002906000019000000757365645f696e646578206f6e6c7920736574206966206f636375706965643b207165642ca8100061000000310600001e0000002ca810006100000059060000190000002ca81000610000005f0600001e000000696e7465726e616c206572726f723a20656e746572656420756e726561636861626c6520636f64652ca810006100000092060000220000002ca810006100000066020000170000002ca81000610000007302000025000000696e7465726e616c206572726f723a20656e746572656420756e726561636861626c6520636f64652f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f73657264655f6a736f6e2d312e302e3132342f7372632f7365722e7273001cac10005f0000000b060000120000001cac10005f0000002e080000330000001cac10005f000000210800004000000066616c73655c225c5c5c625c665c6e5c725c743a6865617070616765735072696d617279536c6f74735072696d617279416e645365636f6e64617279506c61696e536c6f74735072696d617279416e645365636f6e64617279565246536c6f7473000000c9ac10000c000000d5ac10001d000000f2ac10001b00000063616c6c6f7765645f736c6f7473303030313032303330343035303630373038303931303131313231333134313531363137313831393230323132323233323432353236323732383239333033313332333333343335333633373338333934303431343234333434343534363437343834393530353135323533353435353536353735383539363036313632363336343635363636373638363937303731373237333734373537363737373837393830383138323833383438353836383738383839393039313932393339343935393639373938393944656661756c744572726f7200001cac10005f0000007b020000280000001cac10005f000000a0020000280000001cac10005f000000f8010000280000001cac10005f0000000602000028000000e80000000000000001000000e900000045787465726e616c6974696573206e6f7420616c6c6f77656420746f206661696c2077697468696e2072756e74696d652f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f7072696d6974697665732f73746174652d6d616368696e652f7372632f6578742e7273008cae100067000000ae0000003a0000003a65787472696e7369635f696e6465782f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f746573742d7574696c732f72756e74696d652f7372632f7375627374726174655f746573745f70616c6c65742e72730014af100073000000850000004a0000006361706163697479206f766572666c6f7743617061636974794f766572666c6f77416c6c6f634572726c61796f757400fa0000000400000004000000fb0000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f736d616c6c7665632d312e31332e322f7372632f6c69622e72732f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f627335382d302e352e312f7372632f6465636f64652e7273000034b010005a0000006b0000001e0000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f627335382d302e352e312f7372632f656e636f64652e72730000a0b010005a000000410000001e00000063616c6c65642060526573756c743a3a756e77726170282960206f6e20616e2060457272602076616c756500fa0000000800000004000000fc000000d8af10005c000000520100002e000000d8af10005c0000004101000036000000d8af10005c000000ce0400000e000000617373657274696f6e206661696c65643a206e65775f636170203e3d206c656ed8af10005c000000990400000d0000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f736c6963652f697465722e727300a8b1100073000000d60500001500000000000000617474656d707420746f20646976696465206279207a65726fe29c85206e6f206d6967726174696f6e20666f7220000049b21000150000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f626162652f7372632f6c69622e727372756e74696d653a3a6672616d652d737570706f727470616c6c65745f626162653a3a70616c6c6574f09f90a5204e65772070616c6c65742020646574656374656420696e207468652072756e74696d652e205468652070616c6c657420686173206e6f20646566696e65642073746f726167652076657273696f6e2c20736f20746865206f6e2d636861696e2076657273696f6e206973206265696e6720696e697469616c697a656420746f202eeab2100010000000fab21000750000006fb310000100000072656a656374696e6720756e7369676e6564207265706f72742065717569766f636174696f6e207472616e73616374696f6e2062656361757365206974206973206e6f74206c6f63616c2f696e2d626c6f636b2e88b31000540000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f626162652f7372632f65717569766f636174696f6e2e727372756e74696d653a3a6261626570616c6c65745f626162653a3a65717569766f636174696f6e65706f636820696e646578206973207536343b20697420697320616c77617973206f6e6c7920696e6372656d656e746564206279206f6e653b20696620753634206973206e6f7420656e6f7567682077652073686f756c6420637261736820666f72207361666574793b207165642e0068b2100059000000eb0200004600000045706f6368436f6e66696720697320696e697469616c697a656420696e2067656e657369733b207765206e65766572206074616b6560206f7220606b696c6c602069743b2071656468b2100059000000fd0200002900000068b2100059000000e40200001200000049676e6f72696e6720656d7074792065706f6368206368616e67652e54b510001c00000070616c6c65745f6261626565706f636820696e64696365732077696c6c206e6576657220726561636820325e3634206265666f726520746865206465617468206f662074686520756e6976657273653b2071656468b2100059000000a80200000e000000496e697469616c206e756d626572206f6620617574686f7269746965732073686f756c64206265206c6f776572207468616e20543a3a4d6178417574686f7269746965730a01000000000000010000000b01000068b21000590000002103000016000000417574686f7269746965732061726520616c726561647920696e697469616c697a65642140b610002400000068b21000590000001e0300000d0000002043757272656e742065706f636820696e6465782e2043757272656e742065706f636820617574686f7269746965732e2054686520736c6f74206174207768696368207468652066697273742065706f63682061637475616c6c7920737461727465642e2054686973206973203020756e74696c2074686520666972737420626c6f636b206f662074686520636861696e2e2043757272656e7420736c6f74206e756d6265722e205468652065706f63682072616e646f6d6e65737320666f7220746865202a63757272656e742a2065706f63682e20232053656375726974792054686973204d555354204e4f54206265207573656420666f722067616d626c696e672c2061732069742063616e20626520696e666c75656e6365642062792061206d616c6963696f75732076616c696461746f7220696e207468652073686f7274207465726d2e204974204d4159206265207573656420696e206d616e792063727970746f677261706869632070726f746f636f6c732c20686f77657665722c20736f206c6f6e67206173206f6e652072656d656d626572732074686174207468697320286c696b652065766572797468696e6720656c7365206f6e2d636861696e29206974206973207075626c69632e20466f72206578616d706c652c2069742063616e20626520757365642077686572652061206e756d626572206973206e656564656420746861742063616e6e6f742068617665206265656e2063686f73656e20627920616e206164766572736172792c20666f7220707572706f7365732073756368206173207075626c69632d636f696e207a65726f2d6b6e6f776c656467652070726f6f66732e2050656e64696e672065706f636820636f6e66696775726174696f6e206368616e676520746861742077696c6c206265206170706c696564207768656e20746865206e6578742065706f636820697320656e61637465642e204e6578742065706f63682072616e646f6d6e6573732e204e6578742065706f636820617574686f7269746965732e2052616e646f6d6e65737320756e64657220636f6e737472756374696f6e2e205765206d616b6520612074726164652d6f6666206265747765656e2073746f7261676520616363657373657320616e64206c697374206c656e6774682e2057652073746f72652074686520756e6465722d636f6e737472756374696f6e2072616e646f6d6e65737320696e207365676d656e7473206f6620757020746f2060554e4445525f434f4e535452554354494f4e5f5345474d454e545f4c454e475448602e204f6e63652061207365676d656e7420726561636865732074686973206c656e6774682c20776520626567696e20746865206e657874206f6e652e20576520726573657420616c6c207365676d656e747320616e642072657475726e20746f206030602061742074686520626567696e6e696e67206f662065766572792065706f63682e2054574f582d4e4f54453a20605365676d656e74496e6465786020697320616e20696e6372656173696e6720696e74656765722c20736f2074686973206973206f6b61792e2054656d706f726172792076616c75652028636c656172656420617420626c6f636b2066696e616c697a6174696f6e292077686963682069732060536f6d6560206966207065722d626c6f636b20696e697469616c697a6174696f6e2068617320616c7265616479206265656e2063616c6c656420666f722063757272656e7420626c6f636b2e2054686973206669656c642073686f756c6420616c7761797320626520706f70756c6174656420647572696e6720626c6f636b2070726f63657373696e6720756e6c657373207365636f6e6461727920706c61696e20736c6f74732061726520656e61626c65642028776869636820646f6e277420636f6e7461696e206120565246206f7574707574292e2049742069732073657420696e20606f6e5f66696e616c697a65602c206265666f72652069742077696c6c20636f6e7461696e207468652076616c75652066726f6d20746865206c61737420626c6f636b2e2054686520626c6f636b206e756d62657273207768656e20746865206c61737420616e642063757272656e742065706f6368206861766520737461727465642c20726573706563746976656c7920604e2d316020616e6420604e602e204e4f54453a20576520747261636b207468697320697320696e206f7264657220746f20616e6e6f746174652074686520626c6f636b206e756d626572207768656e206120676976656e20706f6f6c206f6620656e74726f7079207761732066697865642028692e652e20697420776173206b6e6f776e20746f20636861696e206f6273657276657273292e2053696e63652065706f6368732061726520646566696e656420696e20736c6f74732c207768696368206d617920626520736b69707065642c2074686520626c6f636b206e756d62657273206d6179206e6f74206c696e6520757020776974682074686520736c6f74206e756d626572732e20486f77206c617465207468652063757272656e7420626c6f636b20697320636f6d706172656420746f2069747320706172656e742e205468697320656e74727920697320706f70756c617465642061732070617274206f6620626c6f636b20657865637574696f6e20616e6420697320636c65616e6564207570206f6e20626c6f636b2066696e616c697a6174696f6e2e205175657279696e6720746869732073746f7261676520656e747279206f757473696465206f6620626c6f636b20657865637574696f6e20636f6e746578742073686f756c6420616c77617973207969656c64207a65726f2e2054686520636f6e66696775726174696f6e20666f72207468652063757272656e742065706f63682e2053686f756c64206e6576657220626520604e6f6e656020617320697420697320696e697469616c697a656420696e2067656e657369732e2054686520636f6e66696775726174696f6e20666f7220746865206e6578742065706f63682c20604e6f6e65602069662074686520636f6e6669672077696c6c206e6f74206368616e67652028796f752063616e2066616c6c6261636b20746f206045706f6368436f6e6669676020696e737465616420696e20746861742063617365292e2041206c697374206f6620746865206c6173742031303020736b69707065642065706f63687320616e642074686520636f72726573706f6e64696e672073657373696f6e20696e646578207768656e207468652065706f63682077617320736b69707065642e2054686973206973206f6e6c79207573656420666f722076616c69646174696e672065717569766f636174696f6e2070726f6f66732e20416e2065717569766f636174696f6e2070726f6f66206d75737420636f6e7461696e732061206b65792d6f776e6572736869702070726f6f6620666f72206120676976656e2073657373696f6e2c207468657265666f7265207765206e65656420612077617920746f2074696520746f6765746865722073657373696f6e7320616e642065706f636820696e64696365732c20692e652e207765206e65656420746f2076616c6964617465207468617420612076616c696461746f722077617320746865206f776e6572206f66206120676976656e206b6579206f6e206120676976656e2073657373696f6e2c20616e64207768617420746865206163746976652065706f636820696e6465782077617320647572696e6720746861742073657373696f6e2e2054686520616d6f756e74206f662074696d652c20696e20736c6f74732c207468617420656163682065706f63682073686f756c64206c6173742e204e4f54453a2043757272656e746c79206974206973206e6f7420706f737369626c6520746f206368616e6765207468652065706f6368206475726174696f6e2061667465722074686520636861696e2068617320737461727465642e20417474656d7074696e6720746f20646f20736f2077696c6c20627269636b20626c6f636b2070726f64756374696f6e2e45706f63684475726174696f6e20546865206578706563746564206176657261676520626c6f636b2074696d6520617420776869636820424142452073686f756c64206265206372656174696e6720626c6f636b732e2053696e636520424142452069732070726f626162696c6973746963206974206973206e6f74207472697669616c20746f20666967757265206f7574207768617420746865206578706563746564206176657261676520626c6f636b2074696d652073686f756c64206265206261736564206f6e2074686520736c6f74206475726174696f6e20616e642074686520736563757269747920706172616d657465722060636020287768657265206031202d20636020726570726573656e7473207468652070726f626162696c697479206f66206120736c6f74206265696e6720656d707479292e4578706563746564426c6f636b54696d65204d6178206e756d626572206f6620617574686f72697469657320616c6c6f7765644d6178417574686f72697469657320546865206d6178696d756d206e756d626572206f66206e6f6d696e61746f727320666f7220656163682076616c696461746f722e4d61784e6f6d696e61746f727343616c6c54436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e0000dbc31000430000007265706f72745f65717569766f636174696f6e7265706f72745f65717569766f636174696f6e5f756e7369676e6564706c616e5f636f6e6669675f6368616e67654572726f7254686520604572726f726020656e756d206f6620746869732070616c6c65742e00006ec4100020000000496e76616c696445717569766f636174696f6e50726f6f66496e76616c69644b65794f776e65727368697050726f6f664475706c69636174654f6666656e63655265706f7274496e76616c6964436f6e66696775726174696f6e617574686f72697469657365706f6368436f6e666967f2c410000b000000fdc410000b0000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f656e7669726f6e6d656e74616c2d312e312e342f7372632f6c69622e727318c5100060000000920000000f00000018c51000600000008e0000000e0000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f656e7669726f6e6d656e74616c2d312e312e342f7372632f6c6f63616c5f6b65792e7273000098c5100066000000280000001800000098c5100066000000230000001900000098c5100066000000210000001700000073657269616c697a6174696f6e20746f206a736f6e20697320657870656374656420746f20776f726b2e207165642e000c01000004000000040000000d0100002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f737570706f72742f7372632f67656e657369735f6275696c6465725f68656c7065722e72730070c610006f0000003400000012000000496e76616c6964204a534f4e20626c6f623a2000f0c61000130000007374727563742047656e65736973436f6e66696742616265565246496e4f7574436f6e7465787428290000000e01000004000000040000000f0100000e0100000400000004000000100100004261644261736535384261644c656e677468556e6b6e6f776e5373353841646472657373466f726d617400000e010000040000000400000011010000496e76616c6964436865636b73756d496e76616c6964507265666978496e76616c6964466f726d6174496e76616c696450617468466f726d61744e6f74416c6c6f77656450617373776f72644e6f74416c6c6f776564000018c5100060000000630000001b0000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f747269652d64622d302e32392e302f7372632f7472696564622e72730000fcc710005e000000110100003d000000fcc710005e00000010010000370000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f7072696d6974697665732f636f72652f7372632f63727970746f2e72730000007cc81000610000005c01000014000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000102030405060708ffffffffffffff090a0b0c0d0e0f10ff1112131415ff161718191a1b1c1d1e1f20ffffffffffff2122232425262728292a2bff2c2d2e2f30313233343536373839ffffffffff31323334353637383941424344454647484a4b4c4d4e505152535455565758595a6162636465666768696a6b6d6e6f707172737475767778797a00007cc8100061000000330100001d000000e4b3100000000000fcc710005e000000ea00000016000000fcc710005e000000d20000001a000000696e7465726e616c206572726f723a20656e746572656420756e726561636861626c6520636f64652f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f73657264655f6a736f6e2d312e302e3132342f7372632f7365722e7273000cca10005f000000a00200002800000053746f7261676556657273696f6e00000e0100000400000004000000120100005373353841646472657373466f726d617470726566697873746167696e67666f6f626172666f6f62617263616c6c65642060526573756c743a3a756e77726170282960206f6e20616e2060457272602076616c75652f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f746573742d7574696c732f72756e74696d652f7372632f6c69622e72737375627374726174655465737400f1ca100061000000ef02000016000000f1ca100061000000df020000190000003a20000080cb10000000000080cb1000020000006120646566656e73697665206661696c75726520686173206265656e207472696767657265643b20706c65617365207265706f72742074686520626c6f636b206e756d6265722061742068747470733a2f2f6769746875622e636f6d2f706172697479746563682f7375627374726174652f69737375657394cb10007800000072756e74696d653a3a646566656e736976652f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f737570706f72742f7372632f7472616974732f6d6973632e72736672616d655f737570706f72743a3a7472616974733a3a6d697363526573756c7454454f6b4572726e6f7420696d706c656d656e7465642f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f7072696d6974697665732f73746174652d6d616368696e652f7372632f747269655f6261636b656e642e7273000000c1cc100070000000ba000000090000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f73797374656d2f7372632f6c69622e72734163636f756e74496e666f6672616d655f73797374656d4e6f6e63654163636f756e74446174614576656e745265636f7264436f646555706772616465417574686f72697a6174696f6e4c6f676963206572726f723a20556e657870656374656420756e646572666c6f7720696e207265647563696e6720636f6e73756d657200e9cd10003600000072756e74696d653a3a73797374656d4c6f676963206572726f723a20556e657870656374656420756e646572666c6f7720696e207265647563696e672070726f766964657200000037ce1000360000004c6f676963206572726f723a204163636f756e7420616c72656164792064656164207768656e207265647563696e672070726f766964657278ce100038000000c1cc1000700000009100000009000000c1cc1000700000009500000009000000c1cc1000700000008400000009000000c1cc1000700000007c00000009000000c1cc1000700000007800000009000000c1cc1000700000008800000009000000c1cc100070000000ad00000009000000c1cc100070000000a900000009000000003a65787472696e7369635f696e6465784576656e74546f70696373426c6f636b4861736852657365727665734c6f636b734163636f756e74467265657a6573556e646572436f6e737472756374696f6e45787472696e73696344617461486f6c6473426f756e646564566563626f756e6465645f636f6c6c656374696f6e733a3a626f756e6465645f7665635345717569766f636174696f6e50726f6f6673705f636f6e73656e7375735f6772616e647061484e45717569766f636174696f6e507265766f7465507265636f6d6d69742f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f627335382d302e352e312f7372632f656e636f64652e72730009d010005a000000d00100001b00000009d010005a000000d10100001000000009d010005a000000cc0100000900000009d010005a000000b90100002000000009d010005a000000c20100000d00000063616c6c65642060526573756c743a3a756e77726170282960206f6e20616e2060457272602076616c7565002b01000000000000010000002c01000009d010005a0000003d01000020000000427566666572546f6f536d616c6c4f74686572002d01000004000000040000002e01000043616e6e6f744c6f6f6b75704261644f726967696e4d6f64756c65002d01000004000000040000002f010000436f6e73756d657252656d61696e696e674e6f50726f766964657273546f6f4d616e79436f6e73756d657273546f6b656e0000002d01000004000000040000003001000041726974686d6574696300002d0100000400000004000000310100005472616e73616374696f6e616c0000002d010000040000000400000032010000457868617573746564436f7272757074696f6e556e617661696c61626c65526f6f744e6f74416c6c6f7765645068616e746f6d446174613c3e000000fcd110000c00000008d21000010000007375627374726174655f746573745f72756e74696d653a3a52756e74696d652f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f747269652d64622d302e32392e302f7372632f6974657261746f722e7273003bd210006000000087000000160000007765277665206a757374206665746368656420746865206c61737420656c656d656e74207573696e6720606c6173745f6d75746020736f20746869732063616e6e6f74206661696c3b207165640000003bd2100060000000cc0100002b0000004372756d623a3a7374657020616e64205472696544424e6f64654974657261746f722061726520696d706c656d656e74656420736f2074686174207468652061626f76652061726d732061726520746865206f6e6c7920706f737369626c652073746174657300003bd2100060000000e2010000160000003bd21000600000008f0100002600000053746f7261676556657273696f6e00002d01000004000000040000001201000042594500b4d31000030000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f746573742d7574696c732f72756e74696d652f7372632f7375627374726174655f746573745f70616c6c65742e727300c0d3100073000000d500000011000000436f756c64206e6f742072656164202074696d65732066726f6d2074686520737461746544d410000f00000053d4100015000000c0d3100073000000cf0000001900000076616c69646174655f756e7369676e656420000088d41000120000007375627374726174655f746573745f70616c6c65747375627374726174655f746573745f72756e74696d653a3a7375627374726174655f746573745f70616c6c65743a3a70616c6c6574f09f90a5204e65772070616c6c65742020646574656374656420696e207468652072756e74696d652e205468652070616c6c657420686173206e6f20646566696e65642073746f726167652076657273696f6e2c20736f20746865206f6e2d636861696e2076657273696f6e206973206265696e6720696e697469616c697a656420746f202eeed4100010000000fed410007500000073d510000100000072756e74696d653a3a6672616d652d737570706f7274e29c85206e6f206d6967726174696f6e20666f722000a2d5100015000000617574686f72697469657300c0d510000b0000007374727563742047656e65736973436f6e6669673c7761736d3a73747269707065643e43616c6c436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e0000fbd510004300000062656e63685f63616c6c696e636c7564655f6461746173746f726167655f6368616e67656f6666636861696e5f696e6465785f7365746f6666636861696e5f696e6465785f636c656172696e64657865645f63616c6c6465706f7369745f6c6f675f6469676573745f6974656d63616c6c5f776974685f7072696f7269747963616c6c5f646f5f6e6f745f70726f70616761746566696c6c5f626c6f636b72656164726561645f616e645f70616e69630500000001000000010000000300000003000000030000000800000003000000030000000300000004000000040000000400000003000000030000000300000003000000436865636b4d6574616461746148617368436865636b576569676874436865636b4e6f6e6365436865636b4d65746164617461486173683a3a6164646974696f6e616c5f7369676e6564203d3e20000062d7100028000000307872756e74696d653a3a6d657461646174612d686173682f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f6d657461646174612d686173682d657874656e73696f6e2f7372632f6c69622e72736672616d655f6d657461646174615f686173685f657874656e73696f6e5573656420626c6f636b207765696768743a2035d81000130000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f73797374656d2f7372632f657874656e73696f6e732f636865636b5f7765696768742e727372756e74696d653a3a73797374656d6672616d655f73797374656d3a3a657874656e73696f6e733a3a636865636b5f7765696768745573656420626c6f636b206c656e6774683a2000f4d810001300000043757272656e742065706f636820696e64657820206973206c6f776572207468616e2073657373696f6e20696e646578202e000010d910001400000024d910001d00000041d91000010000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f626162652f7372632f6c69622e727372756e74696d653a3a6261626570616c6c65745f626162650000005cd9100059000000b00000000f000000656e636f646564206572726f7220697320726573697a656420746f20626520657175616c20746f20746865206d6178696d756d20656e636f646564206572726f722073697a653b20716564496e76616c696445717569766f636174696f6e50726f6f66496e76616c69644b65794f776e65727368697050726f6f664475706c69636174654f6666656e63655265706f7274496e76616c6964436f6e66696775726174696f6e0000004e0100000c000000040000004f0100006465636f646500006672616d655f73797374656d3a3a657874656e73696f6e733a3a636865636b5f6e6f6e636554457863656564656420626c6f636b206c656e677468206c696d69743a20203e200000c6da10001d000000e3da10000300000045787472696e736963202069732067726561746572207468616e20746865206d61782065787472696e73696320000000f8da10000a00000002db100023000000457863656564656420746865207065722d636c61737320616c6c6f77616e63652e00000038db100021000000546f74616c20626c6f636b207765696768742069732065786365656465642e0064db10001f00000050d810006f000000bc00000011000000416c6c2077656967687420636865636b656420616464206f766572666c6f772e9cdb1000200000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f73797374656d2f7372632f6c69622e727300c4db10005b000000590300000f000000496e76616c6964537065634e616d655370656356657273696f6e4e65656473546f496e6372656173654661696c6564546f4578747261637452756e74696d6556657273696f6e4e6f6e44656661756c74436f6d706f736974654e6f6e5a65726f526566436f756e7443616c6c46696c74657265644d756c7469426c6f636b4d6967726174696f6e734f6e676f696e674e6f7468696e67417574686f72697a6564556e617574686f72697a6564436f727275707465642073746174652061742060603a2000dcdc100014000000f0dc10000300000072756e74696d653a3a73746f726167652f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f737570706f72742f7372632f73746f726167652f756e6861736865642e72736672616d655f737570706f72743a3a73746f726167653a3a756e6861736865642f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f62616c616e6365732f7372632f6c69622e727300009ddd10005d0000007d0100000f00000056657374696e6742616c616e63654c69717569646974795265737472696374696f6e73496e73756666696369656e7442616c616e63654578697374656e7469616c4465706f736974457870656e646162696c6974794578697374696e6756657374696e675363686564756c65446561644163636f756e74546f6f4d616e795265736572766573546f6f4d616e79486f6c6473546f6f4d616e79467265657a657349737375616e6365446561637469766174656444656c74615a65726f507265766f746566696e616c6974795f6772616e647061484e507265636f6d6d697445717569766f636174696f6e4964565347656e65736973536c6f74417574686f7269746965734576656e74436f756e744e65787445706f6368436f6e666967496e697469616c697a656443757272656e74536c6f744e756d6265724c61737452756e74696d6555706772616465417574686f72697a656455706772616465496e61637469766549737375616e636545787472696e736963436f756e74506172656e74486173684e65787452616e646f6d6e657373416c6c45787472696e736963734c656e546f74616c49737375616e63654576656e74735570677261646564546f553332526566436f756e74426c6f636b5765696768745365676d656e74496e646578446967657374457865637574696f6e506861736550656e64696e6745706f6368436f6e6669674368616e67654e657874417574686f72697469657345706f6368496e64657845706f6368436f6e66696745706f636853746172744c6174656e657373496e686572656e74734170706c696564536b697070656445706f636873417574686f7256726652616e646f6d6e6573735570677261646564546f547269706c65526566436f756e7452616e646f6d6e6573735765616b426f756e646564566563626f756e6465645f636f6c6c656374696f6e733a3a7765616b5f626f756e6465645f7665636c656e677468206f66206120626f756e64656420766563746f7220696e2073636f706520206973206e6f74207265737065637465642e0000d4e0100024000000f8e010001200000072756e74696d652f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f626f756e6465642d636f6c6c656374696f6e732d302e322e302f7372632f7765616b5f626f756e6465645f7665632e72735f7068616e746f6d0000500100000000000001000000510100006d6f6465520100000100000001000000530100006d657461646174615f6861736800000052010000210000000100000054010000550100000400000004000000560100005501000004000000040000005701000055010000040000000400000058010000550100000400000004000000590100005501000004000000040000005a0100005501000004000000040000005b0100005501000004000000040000005c0100005501000004000000040000005d01000046756e6473556e617661696c61626c654f6e6c7950726f766964657242656c6f774d696e696d756d43616e6e6f74437265617465556e6b6e6f776e417373657446726f7a656e556e737570706f7274656443616e6e6f74437265617465486f6c644e6f74457870656e6461626c65426c6f636b65644d6f64756c654572726f72696e6465780000005201000001000000010000005e0100006572726f720000005201000004000000010000005f0100006d65737361676500550100000400000004000000600100004c61796f757473697a6500005501000004000000040000003f010000616c69676e00000055010000040000000400000061010000557466384572726f7276616c69645f75705f746f6572726f725f6c656e0000005501000004000000040000006201000046726f6d557466384572726f7262797465730000550100000400000004000000630100004261644261736535384261644c656e677468556e6b6e6f776e5373353841646472657373466f726d6174000055010000040000000400000011010000496e76616c6964436865636b73756d496e76616c6964507265666978496e76616c6964466f726d6174496e76616c696450617468466f726d61744e6f74416c6c6f77656450617373776f72644e6f74416c6c6f7765644e6f6e65536f6d650000550100000400000004000000640100005765696768747265665f74696d6500006501000008000000080000006601000070726f6f665f73697a654c696d6974526561636865644e6f4c6179657263616c6c65642060526573756c743a3a756e77726170282960206f6e20616e2060457272602076616c7565670100001400000004000000680100002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f627335382d302e352e312f7372632f656e636f64652e72730000d8e410005a000000930000002b0000004572726f7244697361626c6564456e61626c6564a0da1000000000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f7072696d6974697665732f636f72652f7372632f63727970746f2e727300000060e51000610000005c01000014000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000102030405060708ffffffffffffff090a0b0c0d0e0f10ff1112131415ff161718191a1b1c1d1e1f20ffffffffffff2122232425262728292a2bff2c2d2e2f30313233343536373839ffffffffff31323334353637383941424344454647484a4b4c4d4e505152535455565758595a6162636465666768696a6b6d6e6f707172737475767778797a000060e5100061000000330100001d000000466574636846726f6d456e76437573746f6d0000550100000400000004000000690100003c7761736d3a73747269707065643e5065724469737061746368436c6173736e6f726d616c0000006501000010000000080000006a0100006f7065726174696f6e616c6d616e6461746f72795501000004000000040000006b0100002bda100043da10005bda100071da1000180000001800000016000000140000000f0000001a0000001d000000130000000f0000000c0000001b000000110000000c00000030dc10003fdc100059dc100076dc100089dc100098dc1000a4dc1000bfdc1000d0dc10000e0000001500000013000000120000000d000000170000000b0000000f0000000c0000000e00000013000000090000000cde10001ade10002fde100042de100054de100061de100078de100083de100092de10009ede1000acde1000bfde1000100000000c0000000c0000000c0000000c000000060000000b000000100000000d0000000700000064e2100074e2100080e210008ce2100098e21000a4e21000aae21000b5e21000c5e21000d2e210007d436865636b53756273747261746543616c6c736565642047656e6572617465206120736574206f662073657373696f6e206b6579732077697468206f7074696f6e616c6c79207573696e672074686520676976656e20736565642e20546865206b6579732073686f756c642062652073746f7265642077697468696e20746865206b657973746f7265206578706f736564207669612072756e74696d652065787465726e616c69746965732e000000205468652073656564206e6565647320746f20626520612076616c69642060757466386020737472696e672e2052657475726e732074686520636f6e636174656e61746564205343414c4520656e636f646564207075626c6963206b6579732e67656e65726174655f73657373696f6e5f6b657973656e636f646564204465636f64652074686520676976656e207075626c69632073657373696f6e206b6579732e2052657475726e7320746865206c697374206f66207075626c696320726177207075626c6963206b657973202b206b657920747970652e6465636f64655f73657373696f6e5f6b6579732053657373696f6e206b6579732072756e74696d65206170692e53657373696f6e4b6579736865616465722053746172747320746865206f66662d636861696e207461736b20666f7220676976656e20626c6f636b206865616465722e6f6666636861696e5f776f726b657220546865206f6666636861696e20776f726b6572206170692e4f6666636861696e576f726b65724170690000a10100000800000004000000a2010000a3010000a4010000a5010000a6010000a7010000a801000061207475706c65206f662073697a652032612073657175656e636565787472696e736963204170706c792074686520676976656e2065787472696e7369632e2052657475726e7320616e20696e636c7573696f6e206f7574636f6d652077686963682073706563696669657320696620746869732065787472696e73696320697320696e636c7564656420696e207468697320626c6f636b206f72206e6f742e6170706c795f65787472696e7369632046696e697368207468652063757272656e7420626c6f636b2e66696e616c697a655f626c6f636b696e686572656e742047656e657261746520696e686572656e742065787472696e736963732e2054686520696e686572656e7420646174612077696c6c20766172792066726f6d20636861696e20746f20636861696e2e696e686572656e745f65787472696e73696373626c6f636b6461746120436865636b20746861742074686520696e686572656e7473206172652076616c69642e2054686520696e686572656e7420646174612077696c6c20766172792066726f6d20636861696e20746f20636861696e2e636865636b5f696e686572656e7473205468652060426c6f636b4275696c646572602061706920747261697420746861742070726f7669646573207468652072657175697265642066756e6374696f6e616c69747920666f72206275696c64696e67206120626c6f636b2e426c6f636b4275696c6465722052657475726e732074686520736c6f74206475726174696f6e20666f7220417572612e2043757272656e746c792c206f6e6c79207468652076616c75652070726f7669646564206279207468697320747970652061742067656e657369732077696c6c20626520757365642e736c6f745f6475726174696f6e2052657475726e207468652063757272656e7420736574206f6620617574686f7269746965732e617574686f72697469657320415049206e656365737361727920666f7220626c6f636b20617574686f7273686970207769746820617572612e417572614170692052657475726e2074686520636f6e66696775726174696f6e20666f7220424142452e636f6e66696775726174696f6e2052657475726e732074686520736c6f7420746861742073746172746564207468652063757272656e742065706f63682e63757272656e745f65706f63685f73746172742052657475726e7320696e666f726d6174696f6e20726567617264696e67207468652063757272656e742065706f63682e63757272656e745f65706f63682052657475726e7320696e666f726d6174696f6e20726567617264696e6720746865206e6578742065706f6368202877686963682077617320616c72656164792070726576696f75736c7920616e6e6f756e636564292e6e6578745f65706f6368736c6f74617574686f726974795f69642047656e65726174657320612070726f6f66206f66206b6579206f776e65727368697020666f722074686520676976656e20617574686f7269747920696e207468652063757272656e742065706f63682e20416e206578616d706c65207573616765206f662074686973206d6f64756c6520697320636f75706c65642077697468207468652073657373696f6e20686973746f726963616c206d6f64756c6520746f2070726f76652074686174206120676976656e20617574686f72697479206b6579206973207469656420746f206120676976656e207374616b696e67206964656e7469747920647572696e6720612073706563696669632073657373696f6e2e2050726f6f6673206f66206b6579206f776e65727368697020617265206e656365737361727920666f72207375626d697474696e672065717569766f636174696f6e207265706f7274732e204e4f54453a206576656e2074686f75676820746865204150492074616b657320612060736c6f746020617320706172616d65746572207468652063757272656e7420696d706c656d656e746174696f6e732069676e6f726573207468697320706172616d6574657220616e6420696e73746561642072656c696573206f6e2074686973206d6574686f64206265696e672063616c6c65642061742074686520636f727265637420626c6f636b206865696768742c20692e652e20616e7920706f696e74206174207768696368207468652065706f636820666f722074686520676976656e20736c6f74206973206c697665206f6e2d636861696e2e2046757475726520696d706c656d656e746174696f6e732077696c6c20696e73746561642075736520696e64657865642064617461207468726f75676820616e206f6666636861696e20776f726b65722c206e6f7420726571756972696e67206f6c6465722073746174657320746f20626520617661696c61626c652e67656e65726174655f6b65795f6f776e6572736869705f70726f6f6665717569766f636174696f6e5f70726f6f666b65795f6f776e65725f70726f6f66205375626d69747320616e20756e7369676e65642065787472696e73696320746f207265706f727420616e2065717569766f636174696f6e2e205468652063616c6c6572206d7573742070726f76696465207468652065717569766f636174696f6e2070726f6f6620616e642061206b6579206f776e6572736869702070726f6f66202873686f756c64206265206f627461696e6564207573696e67206067656e65726174655f6b65795f6f776e6572736869705f70726f6f6660292e205468652065787472696e7369632077696c6c20626520756e7369676e656420616e642073686f756c64206f6e6c7920626520616363657074656420666f72206c6f63616c20617574686f727368697020286e6f7420746f2062652062726f61646361737420746f20746865206e6574776f726b292e2054686973206d6574686f642072657475726e7320604e6f6e6560207768656e206372656174696f6e206f66207468652065787472696e736963206661696c732c20652e672e2069662065717569766f636174696f6e207265706f7274696e672069732064697361626c656420666f722074686520676976656e2072756e74696d652028692e652e2074686973206d6574686f642069732068617264636f64656420746f2072657475726e20604e6f6e6560292e204f6e6c792075736566756c20696e20616e206f6666636861696e20636f6e746578742e7375626d69745f7265706f72745f65717569766f636174696f6e5f756e7369676e65645f65787472696e73696320415049206e656365737361727920666f7220626c6f636b20617574686f7273686970207769746820424142452e426162654170696a736f6e204275696c64206052756e74696d6547656e65736973436f6e666967602066726f6d2061204a534f4e20626c6f62206e6f74207573696e6720616e792064656661756c747320616e642073746f726520697420696e207468652073746f726167652e20496e207468652063617365206f662061204652414d452d62617365642072756e74696d652c20746869732066756e6374696f6e20646573657269616c697a6573207468652066756c6c206052756e74696d6547656e65736973436f6e666967602066726f6d2074686520676976656e204a534f4e20626c6f6220616e64207075747320697420696e746f207468652073746f726167652e204966207468652070726f7669646564204a534f4e20626c6f6220697320696e636f7272656374206f7220696e636f6d706c657465206f722074686520646573657269616c697a6174696f6e206661696c732c20616e206572726f722069732072657475726e65642e20506c65617365206e6f746520746861742070726f7669646564204a534f4e20626c6f62206d75737420636f6e7461696e20616c6c206052756e74696d6547656e65736973436f6e66696760206669656c64732c206e6f2064656661756c74732077696c6c20626520757365642e6275696c645f737461746569642052657475726e732061204a534f4e20626c6f6220726570726573656e746174696f6e206f6620746865206275696c742d696e206052756e74696d6547656e65736973436f6e66696760206964656e74696669656420627920606964602e204966206069646020697320604e6f6e6560207468652066756e6374696f6e2072657475726e73204a534f4e20626c6f6220726570726573656e746174696f6e206f66207468652064656661756c74206052756e74696d6547656e65736973436f6e6669676020737472756374206f66207468652072756e74696d652e20496d706c656d656e746174696f6e206d7573742070726f766964652064656661756c74206052756e74696d6547656e65736973436f6e666967602e204f74686572776973652066756e6374696f6e2072657475726e732061204a534f4e20726570726573656e746174696f6e206f6620746865206275696c742d696e2c206e616d6564206052756e74696d6547656e65736973436f6e6669676020707265736574206964656e74696669656420627920606964602c206f7220604e6f6e656020696620737563682070726573657420646f6573206e6f74206578697374732e2052657475726e656420605665633c75383e6020636f6e7461696e73206279746573206f66204a534f4e20626c6f62202870617463682920776869636820636f6d7072697365732061206c697374206f662028706f74656e7469616c6c79206e657374656429206b65792d76616c756520706169727320746861742061726520696e74656e64656420666f7220637573746f6d697a696e67207468652064656661756c742072756e74696d652067656e6573697320636f6e6669672e20546865207061746368207368616c6c206265206d657267656420287266633733383629207769746820746865204a534f4e20726570726573656e746174696f6e206f66207468652064656661756c74206052756e74696d6547656e65736973436f6e6669676020746f20637265617465206120636f6d70726568656e736976652067656e6573697320636f6e66696720746861742063616e206265207573656420696e20606275696c645f737461746560206d6574686f642e6765745f7072657365742052657475726e732061206c697374206f66206964656e7469666965727320666f7220617661696c61626c65206275696c74696e206052756e74696d6547656e65736973436f6e6669676020707265736574732e2054686520707265736574732066726f6d20746865206c6973742063616e20626520717565726965642077697468205b6047656e657369734275696c6465723a3a6765745f707265736574605d206d6574686f642e204966206e6f206e616d65642070726573657473206172652070726f7669646564206279207468652072756e74696d6520746865206c69737420697320656d7074792e7072657365745f6e616d65732041504920746f20696e74657261637420776974682052756e74696d6547656e65736973436f6e66696720666f72207468652072756e74696d6547656e657369734275696c646572736f757263657478626c6f636b5f686173682056616c696461746520746865207472616e73616374696f6e2e2054686973206d6574686f6420697320696e766f6b656420627920746865207472616e73616374696f6e20706f6f6c20746f206c6561726e2064657461696c732061626f757420676976656e207472616e73616374696f6e2e2054686520696d706c656d656e746174696f6e2073686f756c64206d616b65207375726520746f207665726966792074686520636f72726563746e657373206f6620746865207472616e73616374696f6e20616761696e73742063757272656e742073746174652e2054686520676976656e2060626c6f636b5f686173686020636f72726573706f6e647320746f207468652068617368206f662074686520626c6f636b207468617420697320757365642061732063757272656e742073746174652e204e6f7465207468617420746869732063616c6c206d617920626520706572666f726d65642062792074686520706f6f6c206d756c7469706c652074696d657320616e64207472616e73616374696f6e73206d6967687420626520766572696669656420696e20616e7920706f737369626c65206f726465722e76616c69646174655f7472616e73616374696f6e2054686520605461676765645472616e73616374696f6e5175657565602061706920747261697420666f7220696e746572666572696e67207769746820746865207472616e73616374696f6e2071756575652e5461676765645472616e73616374696f6e517565756520476574207468652063757272656e74204752414e44504120617574686f72697469657320616e6420776569676874732e20546869732073686f756c64206e6f74206368616e67652065786365707420666f72207768656e206368616e67657320617265207363686564756c656420616e642074686520636f72726573706f6e64696e672064656c617920686173207061737365642e205768656e2063616c6c656420617420626c6f636b20422c2069742077696c6c2072657475726e2074686520736574206f6620617574686f72697469657320746861742073686f756c64206265207573656420746f2066696e616c697a652064657363656e64616e7473206f66207468697320626c6f636b2028422b312c20422b322c202e2e2e292e2054686520626c6f636b204220697473656c662069732066696e616c697a65642062792074686520617574686f7269746965732066726f6d20626c6f636b20422d312e6772616e6470615f617574686f7269746965737365745f696420676976656e207365742e20416e206578616d706c65207573616765206f662074686973206d6f64756c6520697320636f75706c6564207769746820746865204e4f54453a206576656e2074686f75676820746865204150492074616b6573206120607365745f69646020617320706172616d65746572207468652063757272656e7420696d706c656d656e746174696f6e732069676e6f7265207468697320706172616d6574657220616e6420696e73746561642072656c79206f6e20746869732077686963682074686520676976656e20736574206964206973206c697665206f6e2d636861696e2e2046757475726520696d706c656d656e746174696f6e732077696c6c20696e73746561642075736520696e64657865642064617461207468726f75676820616e206f6666636861696e20776f726b65722c206e6f7420726571756972696e67206f6c6465722073746174657320746f20626520617661696c61626c652e204765742063757272656e74204752414e44504120617574686f72697479207365742069642e63757272656e745f7365745f6964204150497320666f7220696e746567726174696e6720746865204752414e4450412066696e616c6974792067616467657420696e746f2072756e74696d65732e20546869732073686f756c6420626520696d706c656d656e746564206f6e207468652072756e74696d6520736964652e2054686973206973207072696d6172696c79207573656420666f72206e65676f74696174696e6720617574686f726974792d736574206368616e67657320666f7220746865206761646765742e204752414e44504120757365732061207369676e616c696e67206d6f64656c206f66206368616e67696e6720617574686f7269747920736574733a206368616e6765732073686f756c64206265207369676e616c6564207769746820612064656c6179206f66204e20626c6f636b732c20616e64207468656e206175746f6d61746963616c6c79206170706c69656420696e207468652072756e74696d652061667465722074686f7365204e20626c6f636b732068617665207061737365642e2054686520636f6e73656e7375732070726f746f636f6c2077696c6c20636f6f7264696e617465207468652068616e646f66662065787465726e616c6c792e4772616e6470614170696163636f756e74204765742063757272656e74206163636f756e74206e6f6e6365206f6620676976656e20604163636f756e744964602e6163636f756e745f6e6f6e6365205468652041504920746f207175657279206163636f756e74206e6f6e63652e4163636f756e744e6f6e63654170692829a90100000000000001000000aa0100002052657475726e73207468652076657273696f6e206f66207468652072756e74696d652e76657273696f6e20457865637574652074686520676976656e20626c6f636b2e657865637574655f626c6f636b20496e697469616c697a65206120626c6f636b20776974682074686520676976656e2068656164657220616e642072657475726e207468652072756e74696d6520657865637574697665206d6f64652e696e697469616c697a655f626c6f636b205468652060436f7265602072756e74696d65206170692074686174206576657279205375627374726174652072756e74696d65206e6565647320746f20696d706c656d656e742e436f72652052657475726e7320746865206d65746164617461206f6620612072756e74696d652e6d657461646174612052657475726e7320746865206d65746164617461206174206120676976656e2076657273696f6e2e2049662074686520676976656e206076657273696f6e602069736e277420737570706f727465642c20746869732077696c6c2072657475726e20604e6f6e65602e20557365205b6053656c663a3a6d657461646174615f76657273696f6e73605d20746f2066696e64206f75742061626f757420737570706f72746564206d657461646174612076657273696f6e206f66207468652072756e74696d652e6d657461646174615f61745f76657273696f6e2052657475726e732074686520737570706f72746564206d657461646174612076657273696f6e732e20546869732063616e206265207573656420746f2063616c6c20606d657461646174615f61745f76657273696f6e602e6d657461646174615f76657273696f6e732054686520604d65746164617461602061706920747261697420746861742072657475726e73206d6574616461746120666f72207468652072756e74696d652e4d657461646174614f6b0000a10100000400000004000000ab01000045727200a10100000400000004000000ab010000a10100000c00000004000000ac010000ad010000ae010000af010000b0010000b1010000b2010000b30100007c00000004000000b4010000b5010000b60100003800000004000000b7010000b8010000b9010000ba010000bb010000bc010000bd010000a10100000400000004000000be01000001000000000000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f746573742d7574696c732f72756e74696d652f7372632f6c69622e727376616c6964617465000000d5051100080000007375627374726174652d746573742d72756e74696d657375627374726174655f746573745f72756e74696d653031303330303030303030303030303030303034363430343030303030303030303130333030303030303030303030303030303436393034303130303030303000000000b60100003800000004000000bf010000c0010000617373657274696f6e206661696c65643a20616c6c2e636f6e7461696e7328267075626c6963302974051100610000000003000005000000617373657274696f6e206661696c65643a20616c6c2e636f6e7461696e7328267075626c6963312974051100610000000103000005000000617373657274696f6e206661696c65643a20616c6c2e636f6e7461696e7328267075626c69633229740511006100000002030000050000006564323535313947656e65726174657320612076616c696420606564323535313960207369676e61747572652e0000007405110061000000040300002e000000617373657274696f6e206661696c65643a207075626c6963302e76657269667928262265643235353139222c20267369676e6174757265297405110061000000050300000500000074051100610000000f0300000500000074051100610000001003000005000000740511006100000011030000050000007372323535313947656e65726174657320612076616c696420607372323535313960207369676e61747572652e0000007405110061000000130300002e000000617373657274696f6e206661696c65643a207075626c6963302e76657269667928262273723235353139222c20267369676e6174757265297405110061000000140300000500000074051100610000001e0300000500000074051100610000001f0300000500000074051100610000002003000005000000656364736147656e65726174657320612076616c69642060656364736160207369676e61747572652e0000007405110061000000220300002c000000617373657274696f6e206661696c65643a207075626c6963302e7665726966792826226563647361222c20267369676e617475726529000074051100610000002403000005000000746573743a726561645f73746f72616765000000010000000400000074051100610000002e030000050000000809110074051100610000002f0300000500000074051100610000003303000005000000000000005809110074051100610000003403000005000000756e697175655f69645f313a726561645f6368696c645f73746f72616765000074051100610000003e0300000500000074051100610000003f03000005000000740511006100000043030000050000007405110061000000440300000500000076616c756533617373657274696f6e206661696c65643a206578742e73746f726167655f726f6f742844656661756c743a3a64656661756c742829292e61735f736c6963652829203d3d2026726f6f745b2e2e5d74051100610000005303000005000000617373657274696f6e206661696c65643a206578742e73746f726167655f726f6f742844656661756c743a3a64656661756c742829292e61735f736c696365282920213d2026726f6f745b2e2e5d000074051100610000005503000005000000617373657274696f6e206661696c65643a206578742e73746f7261676528622276616c75653322292e69735f736f6d6528290000740511006100000052030000050000003c7761736d3a73747269707065643e5472616e73666572446174612052657475726e207468652062616c616e6365206f662074686520676976656e206163636f756e742069642e62616c616e63655f6f6676616c20412062656e63686d61726b2066756e6374696f6e20746861742061646473206f6e6520746f2074686520676976656e2076616c756520616e642072657475726e732074686520726573756c742e62656e63686d61726b5f6164645f6f6e6576656320412062656e63686d61726b2066756e6374696f6e20746861742061646473206f6e6520746f20656163682076616c756520696e2074686520676976656e20766563746f7220616e642072657475726e732074686520726573756c742e62656e63686d61726b5f766563746f725f6164645f6f6e6520546865206e6577207369676e61747572652e66756e6374696f6e5f7369676e61747572655f6368616e6765642074726965206e6f5f7374642074657374696e677573655f747269652043616c6c732066756e6374696f6e20696e20746865206c6f6f70207573696e67206e657665722d696e6c696e65642066756e6374696f6e20706f696e74657262656e63686d61726b5f696e6469726563745f63616c6c2043616c6c732066756e6374696f6e20696e20746865206c6f6f7062656e63686d61726b5f6469726563745f63616c6c73697a6520416c6c6f636174657320766563746f72207769746820676976656e2063617061636974792e7665635f776974685f63617061636974792052657475726e732074686520696e697469616c697a656420626c6f636b206e756d6265722e6765745f626c6f636b5f6e756d62657220546573742074686174206065643235353139602063727970746f20776f726b7320696e207468652072756e74696d652e2052657475726e7320746865207369676e61747572652067656e65726174656420666f7220746865206d6573736167652060656432353531396020616e6420746865207075626c6963206b65792e746573745f656432353531395f63727970746f20546573742074686174206073723235353139602063727970746f20776f726b7320696e207468652072756e74696d652e2052657475726e7320746865207369676e61747572652067656e65726174656420666f7220746865206d657373616765206073723235353139602e746573745f737232353531395f63727970746f2054657374207468617420606563647361602063727970746f20776f726b7320696e207468652072756e74696d652e2052657475726e7320746865207369676e61747572652067656e65726174656420666f7220746865206d65737361676520606563647361602e746573745f65636473615f63727970746f2052756e20766172696f757320746573747320616761696e73742073746f726167652e746573745f73746f7261676570726f6f66726f6f7420436865636b2061207769746e6573732e746573745f7769746e6573736f746865726e756d2054657374207468617420656e737572657320746861742077652063616e2063616c6c20612066756e6374696f6e20746861742074616b6573206d756c7469706c6520617267756d656e74732e746573745f6d756c7469706c655f617267756d656e747320547261636573206c6f6720224865792049276d2072756e74696d652e22646f5f74726163655f6c6f677369677075626c69636d657373616765205665726966792074686520676976656e207369676e61747572652c207075626c69632026206d6573736167652062756e646c652e7665726966795f656432353531396b657976616c756570616e69632057726974652074686520676976656e206076616c75656020756e6465722074686520676976656e20606b65796020696e746f207468652073746f7261676520616e64207468656e206f7074696f6e616c2070616e69632e77726974655f6b65795f76616c756554657374415049657865637574655f626c6f636b3a200000871011000f0000007405110061000000d80100000d000000696e697469616c697a655f626c6f636b3a200000b0101100120000007405110061000000dd0100000d00000076616c69646174655f7472616e73616374696f6e20200000dc10110015000000f1101100010000005deb10000e0000007405110061000000010200000d0000007405110061000000510200000d0000007405110061000000520200000d0000004865792049276d2072756e74696d65003c1111000f000000544849532049532054524143494e474669656c6453657420636f727275707465642028746869732069732061206275672900000074051100610000005a0200000d0000004865792c2049276d2074726163696e679811110010000000a10100001800000004000000c1010000a10100000400000004000000c201000049276d206a75737420666f6c6c6f77696e67206d79206d6173746572d01111001c0000007405110061000000650200001100000063616c6c65642060526573756c743a3a756e77726170282960206f6e20616e2060457272602076616c756500a90100000000000001000000c30100007405110061000000a60200003f000000666f6f62617273746167696e676576656e74202f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f746573742d7574696c732f72756e74696d652f7372632f6c69622e72733a363032c20f110007000000daeb100004000000a10100000c00000004000000c4010000c5010000c6010000010000005a020000000000005d1211006b0000000809110004000000c812110002000000e0911100d8121100fe05110016000000740511006100000001000000a90100000000000001000000c7010000c8010000c9010000a90100000000000001000000c7010000c9010000c901000042616c616e636573537562737472617465546573744261626553797374656d52756e74696d65486f6c64526561736f6e73797374656d626162657375627374726174655465737462616c616e63657300901311000600000096131100040000009a1311000d000000a7131100080000007374727563742052756e74696d6547656e65736973436f6e66696752756e74696d6543616c6c52756e74696d654572726f7252756e74696d654576656e7452756e74696d652f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f73797374656d2f7372632f6c69622e727372656d61726b7365745f686561705f70616765737365745f636f64657365745f636f64655f776974686f75745f636865636b737365745f73746f726167656b696c6c5f73746f726167656b696c6c5f70726566697872656d61726b5f776974685f6576656e74617574686f72697a655f75706772616465617574686f72697a655f757067726164655f776974686f75745f636865636b736170706c795f617574686f72697a65645f75706772616465e29c85206e6f206d6967726174696f6e20666f72201f1511001500000072756e74696d653a3a6672616d652d737570706f72746672616d655f73797374656d3a3a70616c6c65743a65787472696e7369635f696e646578f09f90a5204e65772070616c6c65742020646574656374656420696e207468652072756e74696d652e205468652070616c6c657420686173206e6f20646566696e65642073746f726167652076657273696f6e2c20736f20746865206f6e2d636861696e2076657273696f6e206973206265696e6720696e697469616c697a656420746f202e76151100100000008615110075000000fb151100010000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f747269652d64622d302e32392e302f7372632f6e6f64652e72736672616d655f73797374656d3a3a696e697469616c697a653a696e747261626c6f636b5f656e74726f70790045787472696e736963206661696c656420617420626c6f636b28293a200000009c1611001a000000b61611000300000072756e74696d653a3a73797374656d6672616d655f73797374656d3a636f64655b5d202065787472696e736963732c206c656e6774683a2020286e6f726d616c20252c206f703a20252c206d616e6461746f7279202529202f206e6f726d616c207765696768743a20282529206f7020776569676874202529202f206d616e6461746f72792077656967687420252900ec16110001000000ed16110002000000ef1611001500000004171100090000000d17110007000000141711000d00000021171100130000003417110002000000361711000d00000034171100020000004317110016000000341711000200000059171100020000004e6f646520697320636f6e6669677572656420746f20757365207468652073616d6520686173683b20716564d80100000000000001000000d9010000151411005b000000470700000e000000205468652066756c6c206163636f756e7420696e666f726d6174696f6e20666f72206120706172746963756c6172206163636f756e742049442e20546f74616c2065787472696e7369637320636f756e7420666f72207468652063757272656e7420626c6f636b2e205768657468657220616c6c20696e686572656e74732068617665206265656e206170706c6965642e205468652063757272656e742077656967687420666f722074686520626c6f636b2e20546f74616c206c656e6774682028696e2062797465732920666f7220616c6c2065787472696e736963732070757420746f6765746865722c20666f72207468652063757272656e7420626c6f636b2e204d6170206f6620626c6f636b206e756d6265727320746f20626c6f636b206861736865732e2045787472696e73696373206461746120666f72207468652063757272656e7420626c6f636b20286d61707320616e2065787472696e736963277320696e64657820746f206974732064617461292e205468652063757272656e7420626c6f636b206e756d626572206265696e672070726f6365737365642e205365742062792060657865637574655f626c6f636b602e2048617368206f66207468652070726576696f757320626c6f636b2e20446967657374206f66207468652063757272656e7420626c6f636b2c20616c736f2070617274206f662074686520626c6f636b206865616465722e204576656e7473206465706f736974656420666f72207468652063757272656e7420626c6f636b2e204e4f54453a20546865206974656d20697320756e626f756e6420616e642073686f756c64207468657265666f7265206e657665722062652072656164206f6e20636861696e2e20497420636f756c64206f746865727769736520696e666c6174652074686520506f562073697a65206f66206120626c6f636b2e204576656e747320686176652061206c6172676520696e2d6d656d6f72792073697a652e20426f7820746865206576656e747320746f206e6f7420676f206f75742d6f662d6d656d6f7279206a75737420696e206361736520736f6d656f6e65207374696c6c207265616473207468656d2066726f6d2077697468696e207468652072756e74696d652e20546865206e756d626572206f66206576656e747320696e2074686520604576656e74733c543e60206c6973742e204d617070696e67206265747765656e206120746f7069632028726570726573656e74656420627920543a3a486173682920616e64206120766563746f72206f6620696e6465786573206f66206576656e747320696e2074686520603c4576656e74733c543e3e60206c6973742e20416c6c20746f70696320766563746f727320686176652064657465726d696e69737469632073746f72616765206c6f636174696f6e7320646570656e64696e67206f6e2074686520746f7069632e205468697320616c6c6f7773206c696768742d636c69656e747320746f206c6576657261676520746865206368616e67657320747269652073746f7261676520747261636b696e67206d656368616e69736d20616e6420696e2063617365206f66206368616e67657320666574636820746865206c697374206f66206576656e7473206f6620696e7465726573742e205468652076616c756520686173207468652074797065206028426c6f636b4e756d626572466f723c543e2c204576656e74496e646578296020626563617573652069662077652075736564206f6e6c79206a7573742074686520604576656e74496e64657860207468656e20696e20636173652069662074686520746f70696320686173207468652073616d6520636f6e74656e7473206f6e20746865206e65787420626c6f636b206e6f206e6f74696669636174696f6e2077696c6c20626520747269676765726564207468757320746865206576656e74206d69676874206265206c6f73742e2053746f726573207468652060737065635f76657273696f6e6020616e642060737065635f6e616d6560206f66207768656e20746865206c6173742072756e74696d6520757067726164652068617070656e65642e2054727565206966207765206861766520757067726164656420736f207468617420607479706520526566436f756e74602069732060753332602e2046616c7365202864656661756c7429206966206e6f742e2054727565206966207765206861766520757067726164656420736f2074686174204163636f756e74496e666f20636f6e7461696e73207468726565207479706573206f662060526566436f756e74602e2046616c7365202864656661756c7429206966206e6f742e2054686520657865637574696f6e207068617365206f662074686520626c6f636b2e2060536f6d6560206966206120636f6465207570677261646520686173206265656e20617574686f72697a65642e20426c6f636b20262065787472696e7369637320776569676874733a20626173652076616c75657320616e64206c696d6974732e426c6f636b5765696768747320546865206d6178696d756d206c656e677468206f66206120626c6f636b2028696e206279746573292e426c6f636b4c656e677468204d6178696d756d206e756d626572206f6620626c6f636b206e756d62657220746f20626c6f636b2068617368206d617070696e677320746f206b65657020286f6c64657374207072756e6564206669727374292e426c6f636b48617368436f756e742054686520776569676874206f662072756e74696d65206461746162617365206f7065726174696f6e73207468652072756e74696d652063616e20696e766f6b652e4462576569676874204765742074686520636861696e277320696e2d636f64652076657273696f6e2e56657273696f6e205468652064657369676e61746564205353353820707265666978206f66207468697320636861696e2e2054686973207265706c6163657320746865202273733538466f726d6174222070726f7065727479206465636c6172656420696e2074686520636861696e20737065632e20526561736f6e2069732074686174207468652072756e74696d652073686f756c64206b6e6f772061626f7574207468652070726566697820696e206f7264657220746f206d616b6520757365206f6620697420617320616e206964656e746966696572206f662074686520636861696e2e5353353850726566697843616c6c54436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e004c211100430000004572726f724572726f7220666f72207468652053797374656d2070616c6c65749d2111001b000000496e76616c6964537065634e616d655370656356657273696f6e4e65656473546f496e6372656173654661696c6564546f4578747261637452756e74696d6556657273696f6e4e6f6e44656661756c74436f6d706f736974654e6f6e5a65726f526566436f756e7443616c6c46696c74657265644d756c7469426c6f636b4d6967726174696f6e734f6e676f696e674e6f7468696e67417574686f72697a6564556e617574686f72697a65644576656e744576656e7420666f72207468652053797374656d2070616c6c65742e000000712211001c00000045787472696e7369635375636365737345787472696e7369634661696c6564436f6465557064617465644e65774163636f756e744b696c6c65644163636f756e7452656d61726b656455706772616465417574686f72697a65644964416d6f756e746672616d655f737570706f72743a3a7472616974733a3a746f6b656e733a3a6d697363496442616c616e63650000da010000da010000da010000da010000446566656e736976656c792062756d70696e67206120636f6e73756d6572207265662e0038231100230000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f62616c616e6365732f7372632f6c69622e727372756e74696d653a3a62616c616e63657370616c6c65745f62616c616e6365733a3a70616c6c6574617373657274696f6e206661696c65643a206163636f756e742e667265652e69735f7a65726f2829207c7c206163636f756e742e66726565203e3d206564207c7c20216163636f756e742e72657365727665642e69735f7a65726f28290000642311005d0000002b040000150000007374727563742047656e65736973436f6e666967141611005c000000e800000019000000141611005c000000da00000019000000141611005c000000870000001e00000053746f7261676556657273696f6e0000db010000040000000400000012010000010000000000000000000000000000000000000000000000000000000000000000000000000000000100000001000000010000000600000001000000010000000100000002000000020000000200000001000000010000000100000001000000696e7465726e616c206572726f723a20656e746572656420756e726561636861626c6520636f64652f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7061726974792d7363616c652d636f6465632d332e362e31322f7372632f636f6d706163742e72730000736869667465642073756666696369656e74206269747320726967687420746f206c656164206f6e6c79206c656164696e67207a65726f733b20716564000000b02511003d0000000000000000000000442511006a0000005e0100001100000056524648617368565246526573756c747672662d696e7672662d6f75742f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f73657264655f6a736f6e2d312e302e3132342f7372632f64652e7273002d2611005e0000009a040000220000002d2611005e0000009004000026000000de0100000000000001000000aa0100003a5f5f53544f524147455f56455253494f4e5f5f3a000000de0100000000000001000000df010000de0100000000000001000000df010000de0100000000000001000000df01000063616c6c6f7765645f736c6f7473737472756374204261626545706f6368436f6e66696775726174696f6e2077697468203220656c656d656e747300122711002d000000e00100000800000004000000e1010000617574686f72697469657365706f6368436f6e6669677374727563742047656e65736973436f6e6669672077697468203220656c656d656e747300006e2711002400000062616c616e6365737374727563742047656e65736973436f6e6669672077697468203120656c656d656e7400a427110023000000e00100000400000004000000e20100007672662d6e6d2d706b6d697373696e67206669656c64206060000000e92711000f000000f827110001000000756e6b6e6f776e206669656c642060602c20746865726520617265206e6f206669656c64730000000c2811000f0000001b28110016000000602c206578706563746564200c2811000f000000442811000c000000696e76616c6964206c656e677468202c206578706563746564200000602811000f0000006f2811000b0000006475706c6963617465206669656c6420600000008c28110011000000f827110001000000756e6b6e6f776e2076617269616e742060602c20746865726520617265206e6f2076617269616e7473000000b028110011000000c128110018000000b028110011000000442811000c000000556e646572666c6f774f766572666c6f774469766973696f6e42795a65726f696e7465726e616c206572726f723a20656e746572656420756e726561636861626c6520636f64653a20747269652073747265616d20636f646563206f6e6c7920666f72206e6f20657874656e73696f6e20747269650000001b291100560000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f7072696d6974697665732f747269652f7372632f747269655f73747265616d2e727300007c29110066000000760000000d0000007c2911006600000047000000400000007c29110066000000470000004d000000de0100000000000001000000e3010000de0100000000000001000000e3010000de0100000000000001000000e3010000de0100000000000001000000e4010000de0100000000000001000000e5010000de0100000000000001000000e6010000de0100000000000001000000e7010000de0100000000000001000000e8010000de0100000000000001000000e9010000de0100000000000001000000ea010000de0100000000000001000000eb010000de0100000000000001000000ec0100002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f747269652d726f6f742d302e31382e302f7372632f6c69622e7273000000d42a11005d0000002901000034000000d42a11005d000000400100002d000000d42a11005d0000003b01000034000000d42a11005d000000790100001f000000d42a11005d0000006001000034000000d42a11005d000000620100002e000000d42a11005d000000a100000026000000d42a11005d000000a10000002c000000d42a11005d000000a1000000250000007374727563742052756e74696d6547656e65736973436f6e6669672077697468203420656c656d656e747300c42b11002b00000073797374656d62616265737562737472617465546573740009000000080000000e000000fc281100052911000d29110050617468206e6f742061737369676e65642f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f7363616c652d696e666f2d322e31312e332f7372632f6275696c642e7273000000392c110060000000b20000001e00000068656164657248656164657265787472696e736963735665633c45787472696e7369633e73746174655f726f6f74486173683a3a4f7574707574706172656e745f6861736865787472696e736963735f726f6f746e756d6265724e756d6265726469676573744469676573745265706f727420617574686f726974792065717569766f636174696f6e2f6d69736265686176696f722e2054686973206d6574686f642077696c6c207665726966797468652065717569766f636174696f6e2070726f6f6620616e642076616c69646174652074686520676976656e206b6579206f776e6572736869702070726f6f66616761696e73742074686520657874726163746564206f6666656e6465722e20496620626f7468206172652076616c69642c20746865206f6666656e63652077696c6c6265207265706f727465642e546869732065787472696e736963206d7573742062652063616c6c656420756e7369676e656420616e642069742069732065787065637465642074686174206f6e6c79626c6f636b20617574686f72732077696c6c2063616c6c206974202876616c69646174656420696e206056616c6964617465556e7369676e656460292c206173207375636869662074686520626c6f636b20617574686f7220697320646566696e65642069742077696c6c20626520646566696e6564206173207468652065717569766f636174696f6e7265706f727465722e182d1100420000005a2d1100410000009b2d110043000000de2d11000c000000ea2d1100430000002d2e110045000000722e110045000000b72e110009000000182d1100420000005a2d1100410000009b2d110043000000de2d11000c000000506c616e20616e2065706f636820636f6e666967206368616e67652e205468652065706f636820636f6e666967206368616e6765206973207265636f7264656420616e642077696c6c20626520656e6163746564206f6e746865206e6578742063616c6c20746f2060656e6163745f65706f63685f6368616e6765602e2054686520636f6e6669672077696c6c20626520616374697661746564206f6e652065706f63682061667465722e4d756c7469706c652063616c6c7320746f2074686973206d6574686f642077696c6c207265706c61636520616e79206578697374696e6720706c616e6e656420636f6e666967206368616e67652074686174206861646e6f74206265656e20656e6163746564207965742e0000202f110057000000772f110054000000cb2f11005600000021301100150000006b65795f6f776e65725f70726f6f66543a3a4b65794f776e657250726f6f6665717569766f636174696f6e5f70726f6f66426f783c45717569766f636174696f6e50726f6f663c486561646572466f723c543e3e3e636f6e6669674e657874436f6e66696744657363726970746f725375626d697474656420636f6e66696775726174696f6e20697320696e76616c69642e000000000000c730110023000000416e2065717569766f636174696f6e2070726f6f662070726f76696465642061732070617274206f6620616e2065717569766f636174696f6e207265706f727420697320696e76616c69642e00000000f83011004c0000004120676976656e2065717569766f636174696f6e207265706f72742069732076616c69642062757420616c72656164792070726576696f75736c79207265706f727465642e000000503111004500000041206b6579206f776e6572736869702070726f6f662070726f76696465642061732070617274206f6620616e2065717569766f636174696f6e207265706f727420697320696e76616c69642e00000000a03111004c000000543a3a4e6f6e63656e6f6e63654e6f6e636570726f766964657273526566436f756e74636f6e73756d657273646174614163636f756e744461746173756666696369656e74736576656e7445746f706963735665633c543e70686173655068617365636f64655f68617368543a3a48617368636865636b5f76657273696f6e626f6f6c4b696c6c20736f6d65206974656d732066726f6d2073746f726167652e7b3211001d00000053657420746865206e65772072756e74696d6520636f646520776974686f757420646f696e6720616e7920636865636b73206f662074686520676976656e2060636f6465602e4e6f746520746861742072756e74696d652075706772616465732077696c6c206e6f742072756e20696620746869732069732063616c6c656420776974682061206e6f742d696e6372656173696e67207370656376657273696f6e210000a032110046000000e632110000000000e6321100540000003a331100080000004d616b6520736f6d65206f6e2d636861696e2072656d61726b20616e6420656d6974206576656e742e000000643311002900000053657420746865206e65772072756e74696d6520636f64652e00000000000000983311001900000053657420746865206e756d626572206f6620706167657320696e2074686520576562417373656d626c7920656e7669726f6e6d656e74277320686561702e0000c03311003e0000004b696c6c20616c6c2073746f72616765206974656d7320776974682061206b657920746861742073746172747320776974682074686520676976656e207072656669782e2a2a4e4f54453a2a2a2057652072656c79206f6e2074686520526f6f74206f726967696e20746f2070726f7669646520757320746865206e756d626572206f66207375626b65797320756e64657274686520707265666978207765206172652072656d6f76696e6720746f2061636375726174656c792063616c63756c6174652074686520776569676874206f6620746869732066756e6374696f6e2e0000000834110044000000e6321100000000004c3411004e0000009a3411004f0000004d616b6520736f6d65206f6e2d636861696e2072656d61726b2e43616e20626520657865637574656420627920657665727920606f726967696e602e0c3511001a000000e632110000000000263511002200000053657420736f6d65206974656d73206f662073746f726167652e000000000000603511001a000000417574686f72697a6520616e207570677261646520746f206120676976656e2060636f64655f686173686020666f72207468652072756e74696d652e205468652072756e74696d652063616e20626520737570706c6965646c617465722e5741524e494e473a205468697320617574686f72697a657320616e207570677261646520746861742077696c6c2074616b6520706c61636520776974686f757420616e792073616665747920636865636b732c20666f726578616d706c652074686174207468652073706563206e616d652072656d61696e73207468652073616d6520616e642074686174207468652076657273696f6e206e756d62657220696e637265617365732e204e6f747265636f6d6d656e64656420666f72206e6f726d616c207573652e205573652060617574686f72697a655f757067726164656020696e73746561642e546869732063616c6c20726571756972657320526f6f74206f726967696e2e00008835110058000000e035110006000000e632110000000000e6351100570000003d36110056000000933611003c000000e632110000000000cf3611001f0000008835110058000000e035110006000000e632110000000000cf3611001f00000050726f766964652074686520707265696d616765202872756e74696d652062696e617279292060636f64656020666f7220616e2075706772616465207468617420686173206265656e20617574686f72697a65642e49662074686520617574686f72697a6174696f6e20726571756972656420612076657273696f6e20636865636b2c20746869732063616c6c2077696c6c20656e73757265207468652073706563206e616d6572656d61696e7320756e6368616e67656420616e6420746861742074686520737065632076657273696f6e2068617320696e637265617365642e446570656e64696e67206f6e207468652072756e74696d65277320604f6e536574436f64656020636f6e66696775726174696f6e2c20746869732066756e6374696f6e206d6179206469726563746c79206170706c79746865206e65772060636f64656020696e207468652073616d6520626c6f636b206f7220617474656d707420746f207363686564756c652074686520757067726164652e416c6c206f726967696e732061726520616c6c6f7765642e005037110055000000e632110000000000a537110052000000f73711003a000000e63211000000000031381100560000008738110044000000e632110000000000cb38110018000000636f64655665633c75383e70616765737536347072656669784b65796974656d735665633c4b657956616c75653e7375626b65797375333272656d61726b6b6579735665633c4b65793e546865206e616d65206f662073706563696669636174696f6e20646f6573206e6f74206d61746368206265747765656e207468652063757272656e742072756e74696d65616e6420746865206e65772072756e74696d652e00007639110044000000ba391100140000005468652073706563696669636174696f6e2076657273696f6e206973206e6f7420616c6c6f77656420746f206465637265617365206265747765656e207468652063757272656e742072756e74696d65e039110050000000ba39110014000000537569636964652063616c6c6564207768656e20746865206163636f756e7420686173206e6f6e2d64656661756c7420636f6d706f7369746520646174612e00403a11003f0000004e6f207570677261646520617574686f72697a65642e0000883a1100160000004661696c656420746f2065787472616374207468652072756e74696d652076657273696f6e2066726f6d20746865206e65772072756e74696d652e4569746865722063616c6c696e672060436f72655f76657273696f6e60206f72206465636f64696e67206052756e74696d6556657273696f6e60206661696c65642e000000a83a11003b000000e632110000000000e33a110042000000546865206f726967696e2066696c7465722070726576656e74207468652063616c6c20746f20626520646973706174636865642e00000000403b11003400000041206d756c74692d626c6f636b206d6967726174696f6e206973206f6e676f696e6720616e642070726576656e7473207468652063757272656e7420636f64652066726f6d206265696e67207265706c616365642e000000803b11005500000054686572652069732061206e6f6e2d7a65726f207265666572656e636520636f756e742070726576656e74696e6720746865206163636f756e742066726f6d206265696e67207075726765642e000000e03b11004d000000546865207375626d697474656420636f6465206973206e6f7420617574686f72697a65642e000000383c110025000000416e2065787472696e736963206661696c65642e00000000683c110014000000603a636f6465602077617320757064617465642e00000000883c11001400000041206e6577206163636f756e742077617320637265617465642e000000000000a83c11001a000000416e20757067726164652077617320617574686f72697a65642e000000000000d03c11001a000000416e206163636f756e7420776173207265617065642e0000f83c110016000000416e2065787472696e73696320636f6d706c65746564207375636365737366756c6c792e00000000183d1100240000004f6e206f6e2d636861696e2072656d61726b2068617070656e65642e00000000483d11001c0000006163636f756e74543a3a4163636f756e7449646861736864697370617463685f696e666f4469737061746368496e666f73656e64657264697370617463685f6572726f7244697370617463684572726f72616d6f756e7442616c616e6365696449646d616e6461746f7279546f7065726174696f6e616c6e6f726d616c4163636f756e744964666c6167734578747261466c61677366726f7a656e7265736572766564667265654c6f636b4964656e746966696572726561736f6e73526561736f6e73526573657276654964656e746966696572556e7265736572766520736f6d652062616c616e63652066726f6d2061207573657220627920666f7263652e43616e206f6e6c792062652063616c6c656420627920524f4f542e00443e11002c000000e632110000000000703e11001b0000005472616e736665722074686520656e74697265207472616e7366657261626c652062616c616e63652066726f6d207468652063616c6c6572206163636f756e742e4e4f54453a20546869732066756e6374696f6e206f6e6c7920617474656d70747320746f207472616e73666572205f7472616e7366657261626c655f2062616c616e6365732e2054686973206d65616e732074686174616e79206c6f636b65642c2072657365727665642c206f72206578697374656e7469616c206465706f7369747320287768656e20606b6565705f616c6976656020697320607472756560292c2077696c6c206e6f742062657472616e7366657272656420627920746869732066756e6374696f6e2e20546f20656e73757265207468617420746869732066756e6374696f6e20726573756c747320696e2061206b696c6c6564206163636f756e742c796f75206d69676874206e65656420746f207072657061726520746865206163636f756e742062792072656d6f76696e6720616e79207265666572656e636520636f756e746572732c2073746f726167656465706f736974732c206574632e2e2e546865206469737061746368206f726967696e206f6620746869732063616c6c206d757374206265205369676e65642e2d206064657374603a2054686520726563697069656e74206f6620746865207472616e736665722e2d20606b6565705f616c697665603a204120626f6f6c65616e20746f2064657465726d696e652069662074686520607472616e736665725f616c6c60206f7065726174696f6e2073686f756c642073656e6420616c6c20206f66207468652066756e647320746865206163636f756e74206861732c2063617573696e67207468652073656e646572206163636f756e7420746f206265206b696c6c6564202866616c7365292c206f7220207472616e736665722065766572797468696e6720657863657074206174206c6561737420746865206578697374656e7469616c206465706f7369742c2077686963682077696c6c2067756172616e74656520746f20206b656570207468652073656e646572206163636f756e7420616c697665202874727565292e000000a43e110041000000e632110000000000e53e1100560000003b3f110058000000933f110057000000ea3f1100510000003b40110010000000e6321100000000004b40110030000000e6321100000000007b40110028000000a340110056000000f9401100530000004c41110056000000a2411100270000004275726e2074686520737065636966696564206c697175696420667265652062616c616e63652066726f6d20746865206f726967696e206163636f756e742e496620746865206f726967696e2773206163636f756e7420656e64732075702062656c6f7720746865206578697374656e7469616c206465706f736974206173206120726573756c746f6620746865206275726e20616e6420606b6565705f616c697665602069732066616c73652c20746865206163636f756e742077696c6c206265207265617065642e556e6c696b652073656e64696e672066756e647320746f2061205f6275726e5f20616464726573732c207768696368206d6572656c79206d616b6573207468652066756e647320696e61636365737369626c652c7468697320606275726e60206f7065726174696f6e2077696c6c2072656475636520746f74616c2069737375616e63652062792074686520616d6f756e74205f6275726e65645f2e0000444211003f000000e6321100000000008342110049000000cc42110042000000e6321100000000000e43110054000000624311004800000053616d6520617320746865205b607472616e736665725f616c6c6f775f6465617468605d2063616c6c2c206275742077697468206120636865636b207468617420746865207472616e736665722077696c6c206e6f746b696c6c20746865206f726967696e206163636f756e742e393925206f66207468652074696d6520796f752077616e74205b607472616e736665725f616c6c6f775f6465617468605d20696e73746561642e5b607472616e736665725f616c6c6f775f6465617468605d3a207374727563742e50616c6c65742e68746d6c236d6574686f642e7472616e73666572e4431100560000003a44110018000000e632110000000000524411003a000000e6321100000000008c4411003c00000041646a7573742074686520746f74616c2069737375616e636520696e20612073617475726174696e67207761792e43616e206f6e6c792062652063616c6c656420627920726f6f7420616e6420616c77617973206e65656473206120706f736974697665206064656c7461602e23204578616d706c650000f84411002e000000e632110000000000264511003f000000e632110000000000654511000900000055706772616465206120737065636966696564206163636f756e742e2d20606f726967696e603a204d75737420626520605369676e6564602e2d206077686f603a20546865206163636f756e7420746f2062652075706772616465642e546869732077696c6c20776169766520746865207472616e73616374696f6e20666565206966206174206c6561737420616c6c2062757420313025206f6620746865206163636f756e7473206e656564656420746f62652075706772616465642e20285765206c657420736f6d65206e6f74206861766520746f206265207570677261646564206a75737420696e206f7264657220746f20616c6c6f7720666f7220746865706f73736962696c697479206f6620636875726e292e984511001c000000e632110000000000b44511001d000000d145110024000000e632110000000000f5451100550000004a461100500000009a4611001600000045786163746c7920617320607472616e736665725f616c6c6f775f6465617468602c2065786365707420746865206f726967696e206d75737420626520726f6f7420616e642074686520736f75726365206163636f756e746d6179206265207370656369666965642e000000f04611005800000048471100110000005365742074686520726567756c61722062616c616e6365206f66206120676976656e206163636f756e742e546865206469737061746368206f726967696e20666f7220746869732063616c6c2069732060726f6f74602e006c4711002b000000e632110000000000974711002c0000005472616e7366657220736f6d65206c697175696420667265652062616c616e636520746f20616e6f74686572206163636f756e742e607472616e736665725f616c6c6f775f6465617468602077696c6c207365742074686520604672656542616c616e636560206f66207468652073656e64657220616e642072656365697665722e4966207468652073656e6465722773206163636f756e742069732062656c6f7720746865206578697374656e7469616c206465706f736974206173206120726573756c746f6620746865207472616e736665722c20746865206163636f756e742077696c6c206265207265617065642e546865206469737061746368206f726967696e20666f7220746869732063616c6c206d75737420626520605369676e65646020627920746865207472616e736163746f722e00dc47110035000000e632110000000000114811004d0000005e48110044000000a24811002c000000e632110000000000ce4811004500000076616c7565543a3a42616c616e6365646573744163636f756e7449644c6f6f6b75704f663c543e77686f6b6565705f616c697665646972656374696f6e41646a7573746d656e74446972656374696f6e5665633c543a3a4163636f756e7449643e736f7572636564656c74616e65775f6672656556657374696e672062616c616e636520746f6f206869676820746f2073656e642076616c75652e00c0491100270000004e756d626572206f6620667265657a65732065786365656420604d6178467265657a6573602e0000f0491100260000005472616e736665722f7061796d656e7420776f756c64206b696c6c206163636f756e742e00000000204a11002400000042656e6566696369617279206163636f756e74206d757374207072652d65786973742e0000000000504a1100230000004e756d626572206f6620686f6c647320657863656564206056617269616e74436f756e744f663c543a3a52756e74696d65486f6c64526561736f6e3e602e0000804a11003e00000056616c756520746f6f206c6f7720746f20637265617465206163636f756e742064756520746f206578697374656e7469616c206465706f7369742e0000000000c84a11003b0000004e756d626572206f66206e616d65642072657365727665732065786365656420604d61785265736572766573602e0000104b11002e00000042616c616e636520746f6f206c6f7720746f2073656e642076616c75652e0000484b11001e0000005468652064656c74612063616e6e6f74206265207a65726f2e00000000000000704b1100190000004163636f756e74206c6971756964697479207265737472696374696f6e732070726576656e74207769746864726177616c2e000000000000984b110032000000412076657374696e67207363686564756c6520616c72656164792065786973747320666f722074686973206163636f756e742e0000000000d84b1100330000005468652069737375616e63652063616e6e6f74206265206d6f6469666965642073696e636520697420697320616c72656164792064656163746976617465642e184c110040000000412062616c616e6365207761732073657420627920726f6f742e000000000000604c11001a000000536f6d6520616d6f756e742077617320726573746f72656420696e746f20616e206163636f756e742e00000000000000884c110029000000416e206163636f756e74207761732063726561746564207769746820736f6d6520667265652062616c616e63652e0000c04c11002e000000536f6d652062616c616e636520776173207468617765642ef84c110018000000416e206163636f756e74207761732075706772616465642e184d110018000000536f6d6520616d6f756e7420776173206465706f73697465642028652e672e20666f72207472616e73616374696f6e2066656573292e0000384d110036000000546f74616c2069737375616e636520776173206465637265617365642062792060616d6f756e74602c206372656174696e672061206465627420746f2062652062616c616e6365642e00000000000000784d110049000000536f6d652062616c616e636520776173206d6f7665642066726f6d207468652072657365727665206f6620746865206669727374206163636f756e7420746f20746865207365636f6e64206163636f756e742e46696e616c20617267756d656e7420696e64696361746573207468652064657374696e6174696f6e2062616c616e636520747970652e000000d04d110053000000234e110036000000536f6d652062616c616e636520776173206c6f636b65642e000000006c4e110018000000536f6d652062616c616e63652077617320726573657276656420286d6f7665642066726f6d206672656520746f207265736572766564292e904e110038000000536f6d6520616d6f756e74207761732072656d6f7665642066726f6d20746865206163636f756e742028652e672e20666f72206d69736265686176696f72292ed04e110040000000536f6d6520616d6f756e7420776173206d696e74656420696e746f20616e206163636f756e742e00184f110027000000546f74616c2069737375616e63652077617320696e637265617365642062792060616d6f756e74602c206372656174696e6720612063726564697420746f2062652062616c616e6365642e0000000000484f11004b0000005468652060546f74616c49737375616e6365602077617320666f72636566756c6c79206368616e6765642e0000000000a04f11002b0000005472616e73666572207375636365656465642e0000000000d84f110013000000536f6d652062616c616e63652077617320756e6c6f636b65642e000000000000f84f11001a000000416e206163636f756e74207761732072656d6f7665642077686f73652062616c616e636520776173206e6f6e2d7a65726f206275742062656c6f77204578697374656e7469616c4465706f7369742c726573756c74696e6720696e20616e206f75747269676874206c6f73732e000000205011004f0000006f5011001e000000536f6d6520616d6f756e74207761732077697468647261776e2066726f6d20746865206163636f756e742028652e672e20666f72207472616e73616374696f6e2066656573292e00a050110047000000536f6d652062616c616e6365207761732066726f7a656e2ef050110018000000536f6d652062616c616e63652077617320756e726573657276656420286d6f7665642066726f6d20726573657276656420746f2066726565292e000000000000105111003a000000536f6d6520616d6f756e74207761732073757370656e6465642066726f6d20616e206163636f756e74202869742063616e20626520726573746f726564206c61746572292e0000005851110045000000536f6d6520616d6f756e7420776173206275726e65642066726f6d20616e206163636f756e742e00a851110027000000746f66726f6d667265655f62616c616e636564657374696e6174696f6e5f7374617475735374617475736e65776f6c647461726765745f6e756d6265724e7461726765745f6861736848666972737428562c2053297365636f6e646964656e74697479726f756e645f6e756d6265726f6666656e64657266697273745f686561646572736c6f74536c6f747365636f6e645f6865616465727365745f6964536574496465717569766f636174696f6e45717569766f636174696f6e3c482c204e3e66696e616c6974795f6772616e6470613a3a45717569766f636174696f6e3c417574686f7269747949642c2066696e616c6974795f6772616e6470613a3a507265636f6d6d69740a3c482c204e3e2c20417574686f726974795369676e61747572652c3e66696e616c6974795f6772616e6470613a3a45717569766f636174696f6e3c417574686f7269747949642c2066696e616c6974795f6772616e6470613a3a507265766f74653c0a482c204e3e2c20417574686f726974795369676e61747572652c3e6d6f64654d6f64654c65676163792063616c6c207573656420696e207472616e73616374696f6e20706f6f6c2062656e63686d61726b732e0067531100300000007472616e736665725472616e7366657244617461496d706c696369746c792066696c6c206120626c6f636b20626f6479207769746820736f6d6520646174612eb45311002c0000005075742f64656c65746520736f6d6520646174612066726f6d2073746f726167652e20496e74656e64656420746f2075736520617320616e20756e7369676e65642065787472696e7369632e00000000e85311004c0000006b65794f7074696f6e3c5665633c75383e3e57726974652061206b65792076616c7565207061697220746f20746865206f6666636861696e2064617461626173652e000000000000525411003000000052656d6f76652061206b657920616e6420616e206173736f6369617465642076616c75652066726f6d20746865206f6666636861696e2064617461626173652e905411004000000043726561746520616e20696e64657820666f7220746869732063616c6c2e0000d85411001e0000004465706f73697420676976656e20646967657374206974656d7320696e746f207468652073797374656d2073746f726167652e20546865792077696c6c20626520696e636c7564656420696e206120686561646572647572696e672066696e616c697a6174696f6e2e000000005511005500000055551100140000006c6f6773705f72756e74696d653a3a67656e657269633a3a4469676573744974656d546869732063616c6c2069732076616c696461746564206173206056616c69645472616e73616374696f6e60207769746820676976656e207072696f726974792e009e551100410000007072696f726974795472616e73616374696f6e5072696f72697479546869732063616c6c2069732076616c696461746564206173206e6f6e2d70726f70616761626c65206056616c69645472616e73616374696f6e602e00035611003c00000046696c6c2074686520626c6f636b2077656967687420757020746f2074686520676976656e20726174696f2e00000000485611002c000000726174696f50657262696c6c5265616420582074696d65732066726f6d2074686520737461746520736f6d6520646174612e50616e6963732069662069742063616e206e6f742072656164206058602074696d65732e00008c56110026000000e632110000000000b256110024000000636f756e745265616420582074696d65732066726f6d2074686520737461746520736f6d65206461746120616e64207468656e2070616e69632152657475726e7320604f6b60206966206974206469646e2774207265616420616e797468696e672e0000f556110035000000e6321100000000002a5711002800000073656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c53797374656d2c2052756e74696d653e73656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c426162652c2052756e74696d653e73656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c537562737472617465546573742c2052756e74696d653e73656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c42616c616e6365732c2052756e74696d653e6672616d655f73797374656d3a3a4572726f723c52756e74696d653e70616c6c65745f626162653a3a4572726f723c52756e74696d653e70616c6c65745f62616c616e6365733a3a4572726f723c52756e74696d653e6672616d655f73797374656d3a3a4576656e743c52756e74696d653e70616c6c65745f62616c616e6365733a3a4576656e743c52756e74696d653e496e646578206f7574206f6620626f756e647300b0591100130000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f636f72652f7372632f736c6963652f736f72742e727300cc591100730000003b0400000e000000cc59110073000000480400001c000000cc59110073000000490400001d000000cc591100730000004a04000025000000cc591100730000008e04000040000000cc59110073000000b40400004e000000cc59110073000000c204000056000000617373657274696f6e206661696c65643a20656e64203e3d20737461727420262620656e64203c3d206c656ecc591100730000002d05000005000000cc591100730000003e05000029000000617373657274696f6e206661696c65643a206f666673657420213d2030202626206f6666736574203c3d206c656e0000cc591100730000009b00000005000000746573747061726974792d7465737400df6acb689907609b0500000037e397fc7c91f5e402000000d2bc9897eed08f150300000040fe3ad401f8959a06000000bc9d89904f5b923f01000000c6e9a76309f39b0902000000dd718d5cc53262d401000000cbca25e39f14238702000000f78b278be53f454c02000000ab3c0572291feb8b01000000ed99c5acb25eedf503000000fbc577b9d747efd601000000000000803c5b11000400000000000080405b11000b000000000000804c5b11000c000000010000000200000002000000010000000100000042616420696e70757420646174612070726f766964656420746f2076657273696f6e3a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e0000145c11005a0000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f746573742d7574696c732f72756e74696d652f7372632f6c69622e7273000000785c110061000000d10100000100000042616420696e70757420646174612070726f766964656420746f20657865637574655f626c6f636b3a200000ec5c11002a00000042616420696e70757420646174612070726f766964656420746f20696e697469616c697a655f626c6f636b3a20000000205d11002d00000042616420696e70757420646174612070726f766964656420746f206d657461646174613a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e00585d11005b00000042616420696e70757420646174612070726f766964656420746f206d657461646174615f61745f76657273696f6e3a20bc5d11003000000042616420696e70757420646174612070726f766964656420746f206d657461646174615f76657273696f6e733a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792ef45d11006400000042616420696e70757420646174612070726f766964656420746f2076616c69646174655f7472616e73616374696f6e3a20000000605e11003100000042616420696e70757420646174612070726f766964656420746f206170706c795f65787472696e7369633a209c5e11002c00000042616420696e70757420646174612070726f766964656420746f2066696e616c697a655f626c6f636b3a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e000000d05e11006100000042616420696e70757420646174612070726f766964656420746f20696e686572656e745f65787472696e736963733a203c5f11003000000042616420696e70757420646174612070726f766964656420746f20636865636b5f696e686572656e74733a20745f11002c00000042616420696e70757420646174612070726f766964656420746f206163636f756e745f6e6f6e63653a200000a85f11002a00000042616420696e70757420646174612070726f766964656420746f2062616c616e63655f6f663a2000dc5f11002700000042616420696e70757420646174612070726f766964656420746f2062656e63686d61726b5f6164645f6f6e653a2000000c6011002e00000042616420696e70757420646174612070726f766964656420746f2062656e63686d61726b5f766563746f725f6164645f6f6e653a20000000446011003500000042616420696e70757420646174612070726f766964656420746f2066756e6374696f6e5f7369676e61747572655f6368616e6765643a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e000000846011006d00000042616420696e70757420646174612070726f766964656420746f207573655f747269653a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e00fc6011005b00000042616420696e70757420646174612070726f766964656420746f2062656e63686d61726b5f696e6469726563745f63616c6c3a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e0000606111006a00000042616420696e70757420646174612070726f766964656420746f2062656e63686d61726b5f6469726563745f63616c6c3a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792ed46111006800000042616420696e70757420646174612070726f766964656420746f207665635f776974685f63617061636974793a200000446211002e00000042616420696e70757420646174612070726f766964656420746f206765745f626c6f636b5f6e756d6265723a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e007c6211006300000042616420696e70757420646174612070726f766964656420746f20746573745f656432353531395f63727970746f3a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e0000e86211006600000042616420696e70757420646174612070726f766964656420746f20746573745f737232353531395f63727970746f3a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e0000586311006600000042616420696e70757420646174612070726f766964656420746f20746573745f65636473615f63727970746f3a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792ec86311006400000042616420696e70757420646174612070726f766964656420746f20746573745f73746f726167653a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e00346411005f00000042616420696e70757420646174612070726f766964656420746f20746573745f7769746e6573733a200000009c6411002900000042616420696e70757420646174612070726f766964656420746f20746573745f6d756c7469706c655f617267756d656e74733a20d06411003400000042616420696e70757420646174612070726f766964656420746f20646f5f74726163655f6c6f673a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e000c6511005f00000042616420696e70757420646174612070726f766964656420746f207665726966795f656432353531393a2000746511002b00000042616420696e70757420646174612070726f766964656420746f2077726974655f6b65795f76616c75653a20a86511002c00000042616420696e70757420646174612070726f766964656420746f20736c6f745f6475726174696f6e3a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792edc6511006000000042616420696e70757420646174612070726f766964656420746f20617574686f7269746965733a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e0000446611005e00000042616420696e70757420646174612070726f766964656420746f20636f6e66696775726174696f6e3a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792eac6611006000000042616420696e70757420646174612070726f766964656420746f2063757272656e745f65706f63685f73746172743a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e0000146711006600000042616420696e70757420646174612070726f766964656420746f2063757272656e745f65706f63683a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e846711006000000042616420696e70757420646174612070726f766964656420746f206e6578745f65706f63683a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e000000ec6711005d00000042616420696e70757420646174612070726f766964656420746f207375626d69745f7265706f72745f65717569766f636174696f6e5f756e7369676e65645f65787472696e7369633a200000546811004a00000042616420696e70757420646174612070726f766964656420746f2067656e65726174655f6b65795f6f776e6572736869705f70726f6f663a20000000a86811003900000042616420696e70757420646174612070726f766964656420746f206f6666636861696e5f776f726b65723a20ec6811002c00000042616420696e70757420646174612070726f766964656420746f2067656e65726174655f73657373696f6e5f6b6579733a200000206911003200000042616420696e70757420646174612070726f766964656420746f206465636f64655f73657373696f6e5f6b6579733a205c6911003000000042616420696e70757420646174612070726f766964656420746f206772616e6470615f617574686f7269746965733a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e0000946911006600000042616420696e70757420646174612070726f766964656420746f2063757272656e745f7365745f69643a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e000000046a11006100000042616420696e70757420646174612070726f766964656420746f206275696c645f73746174653a20706a11002800000042616420696e70757420646174612070726f766964656420746f206765745f7072657365743a2000a06a11002700000042616420696e70757420646174612070726f766964656420746f207072657365745f6e616d65733a206578706563746564206e6f20706172616d65746572732c2062757420696e70757420627566666572206973206e6f7420656d7074792e00d06a11005f0000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f62616c616e6365732f7372632f6c69622e72737472616e736665725f616c6c6f775f6465617468666f7263655f7472616e736665727472616e736665725f6b6565705f616c6976657472616e736665725f616c6c666f7263655f756e72657365727665757067726164655f6163636f756e7473666f7263655f7365745f62616c616e6365666f7263655f61646a7573745f746f74616c5f69737375616e63656275726e4f7074696f6e544e6f6e65536f6d65e29c85206e6f206d6967726174696f6e20666f7220000000346c11001500000072756e74696d653a3a6672616d652d737570706f727470616c6c65745f62616c616e6365733a3a70616c6c65746475706c69636174652062616c616e63657320696e2067656e657369732e00816c11001e000000386b11005d000000110200000d0000007468652062616c616e6365206f6620616e79206163636f756e742073686f756c6420616c77617973206265206174206c6561737420746865206578697374656e7469616c206465706f7369742e000000b86c11004d000000386b11005d0000000302000011000000f09f90a5204e65772070616c6c65742020646574656374656420696e207468652072756e74696d652e20496e697469616c697a696e6720746865206f6e2d636861696e2073746f726167652076657273696f6e20746f206d61746368207468652073746f726167652076657273696f6e20646566696e656420696e207468652070616c6c65743a20206d110010000000306d1100780000000100006163636f756e7420776974682061206e6f6e2d7a65726f20726573657276652062616c616e636520686173206e6f2070726f766964657220726566732c206163636f756e745f69643a2027272ebb6d11004b000000066e11000200000072756e74696d653a3a62616c616e6365732054686520746f74616c20756e6974732069737375656420696e207468652073797374656d2e2054686520746f74616c20756e697473206f66206f75747374616e64696e672064656163746976617465642062616c616e636520696e207468652073797374656d2e205468652042616c616e6365732070616c6c6574206578616d706c65206f662073746f72696e67207468652062616c616e6365206f6620616e206163636f756e742e2023204578616d706c65206060606e6f636f6d70696c652020696d706c2070616c6c65745f62616c616e6365733a3a436f6e66696720666f722052756e74696d65207b2020202074797065204163636f756e7453746f7265203d2053746f726167654d61705368696d3c53656c663a3a4163636f756e743c52756e74696d653e2c206672616d655f73797374656d3a3a50726f76696465723c52756e74696d653e2c204163636f756e7449642c2053656c663a3a4163636f756e74446174613c42616c616e63653e3e20207d2060606020596f752063616e20616c736f2073746f7265207468652062616c616e6365206f6620616e206163636f756e7420696e20746865206053797374656d602070616c6c65742e20202074797065204163636f756e7453746f7265203d2053797374656d20427574207468697320636f6d657320776974682074726164656f6666732c2073746f72696e67206163636f756e742062616c616e63657320696e207468652073797374656d2070616c6c65742073746f72657320606672616d655f73797374656d60206461746120616c6f6e677369646520746865206163636f756e74206461746120636f6e747261727920746f2073746f72696e67206163636f756e742062616c616e63657320696e20746865206042616c616e636573602070616c6c65742c20776869636820757365732061206053746f726167654d61706020746f2073746f72652062616c616e6365732064617461206f6e6c792e204e4f54453a2054686973206973206f6e6c79207573656420696e207468652063617365207468617420746869732070616c6c6574206973207573656420746f2073746f72652062616c616e6365732e20416e79206c6971756964697479206c6f636b73206f6e20736f6d65206163636f756e742062616c616e6365732e204e4f54453a2053686f756c64206f6e6c79206265206163636573736564207768656e2073657474696e672c206368616e67696e6720616e642066726565696e672061206c6f636b2e20557365206f66206c6f636b73206973206465707265636174656420696e206661766f7572206f6620667265657a65732e20536565206068747470733a2f2f6769746875622e636f6d2f706172697479746563682f7375627374726174652f70756c6c2f31323935312f60204e616d6564207265736572766573206f6e20736f6d65206163636f756e742062616c616e6365732e20557365206f66207265736572766573206973206465707265636174656420696e206661766f7572206f6620686f6c64732e20536565206068747470733a2f2f6769746875622e636f6d2f706172697479746563682f7375627374726174652f70756c6c2f31323935312f6020486f6c6473206f6e206163636f756e742062616c616e6365732e20467265657a65206c6f636b73206f6e206163636f756e742062616c616e6365732e20546865206d696e696d756d20616d6f756e7420726571756972656420746f206b65657020616e206163636f756e74206f70656e2e204d5553542042452047524541544552205448414e205a45524f2120496620796f75202a7265616c6c792a206e65656420697420746f206265207a65726f2c20796f752063616e20656e61626c652074686520666561747572652060696e7365637572655f7a65726f5f65646020666f7220746869732070616c6c65742e20486f77657665722c20796f7520646f20736f20617420796f7572206f776e207269736b3a20746869732077696c6c206f70656e2075702061206d616a6f7220446f5320766563746f722e20496e206361736520796f752068617665206d756c7469706c6520736f7572636573206f662070726f7669646572207265666572656e6365732c20796f75206d617920616c736f2067657420756e6578706563746564206265686176696f757220696620796f7520736574207468697320746f207a65726f2e20426f74746f6d206c696e653a20446f20796f757273656c662061206661766f757220616e64206d616b65206974206174206c65617374206f6e65214578697374656e7469616c4465706f73697420546865206d6178696d756d206e756d626572206f66206c6f636b7320746861742073686f756c64206578697374206f6e20616e206163636f756e742e204e6f74207374726963746c7920656e666f726365642c20627574207573656420666f722077656967687420657374696d6174696f6e2e4d61784c6f636b7320546865206d6178696d756d206e756d626572206f66206e616d656420726573657276657320746861742063616e206578697374206f6e20616e206163636f756e742e4d6178526573657276657320546865206d6178696d756d206e756d626572206f6620696e646976696475616c20667265657a65206c6f636b7320746861742063616e206578697374206f6e20616e206163636f756e7420617420616e792074696d652e4d6178467265657a657362616c616e63657300f37511000800000043616c6c49436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e0000000009761100430000004572726f7254686520604572726f726020656e756d206f6620746869732070616c6c65742e0000005d7611002000000056657374696e6742616c616e63654c69717569646974795265737472696374696f6e73496e73756666696369656e7442616c616e6365457870656e646162696c6974794578697374696e6756657374696e675363686564756c65446561644163636f756e74546f6f4d616e795265736572766573546f6f4d616e79486f6c6473546f6f4d616e79467265657a657349737375616e6365446561637469766174656444656c74615a65726f4576656e7454686520604576656e746020656e756d206f6620746869732070616c6c65740000377711001f000000456e646f776564447573744c6f73745472616e7366657242616c616e63655365745265736572766564556e72657365727665645265736572766552657061747269617465644465706f7369745769746864726177536c61736865644d696e7465644275726e656453757370656e646564526573746f726564557067726164656449737375656452657363696e6465644c6f636b6564556e6c6f636b656446726f7a656e546861776564546f74616c49737375616e6365466f726365647374727563742047656e65736973436f6e666967ff0100000400000004000000e2010000ff01000004000000040000002e010000ff01000004000000040000005a0100002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f7072696d6974697665732f747269652f7372632f6e6f64655f636f6465632e72732f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f747269652d64622d302e32392e302f7372632f6c6f6f6b75702e727300c57811005e000000460000001e000000ff0100000c0000000400000000020000010200005765206172652063616368696e67206120604e6f64654f776e65643a3a56616c75656020666f7220612076616c7565206e6f6465206861736820616e64207468697320636163686564206e6f64652068617320616c7761797320646174612061747461636865643b20716564c57811005e00000079000000160000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f747269652d64622d302e32392e302f7372632f6e6962626c652f6d6f642e72730000c479110062000000490000001c000000ff01000014000000040000000202000003020000696e7465726e616c206572726f723a20656e746572656420756e726561636861626c6520636f64653a2000004c7a11002a000000604e6f64654f776e65643a3a56616c7565602063616e206e6f742062652072656163686564206279207573696e67207468652068617368206f662061206e6f64652e20604e6f64654f776e65643a3a56616c756560206973206f6e6c7920636f6e7374727563746564207768656e206c6f6164696e6720612076616c756520696e746f206d656d6f72792c207768696368206e6565647320746f2068617665206120646966666572656e742068617368207468616e20616e79206e6f64653b2071656400807a1100c3000000c57811005e000000d00200001900000053746f7261676556657273696f6e0000ff010000040000000400000012010000696e7465726e616c206572726f723a20656e746572656420756e726561636861626c6520636f64653a204e6f20657874656e73696f6e20636f6465632e0000007c7b11003d0000006078110065000000dd000000090000006078110065000000720000003400000060781100650000007b0000003200000060781100650000009e000000340000006078110065000000d60000000900000060781100650000000c0100000f00000060781100650000000501000029000000a86c1100000000006120646566656e73697665206661696c75726520686173206265656e207472696767657265643b20706c65617365207265706f72742074686520626c6f636b206e756d6265722061742068747470733a2f2f6769746875622e636f6d2f706172697479746563682f7375627374726174652f6973737565733c7c11007800000072756e74696d653a3a646566656e736976652f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f737570706f72742f7372632f7472616974732f6d6973632e72736672616d655f737570706f72743a3a7472616974733a3a6d69736376616c69646174655f72756e74696d655f63616c6c20004d7d1100160000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f746573742d7574696c732f72756e74696d652f7372632f7375627374726174655f746573745f70616c6c65742e72737375627374726174655f746573745f70616c6c65747375627374726174655f746573745f72756e74696d653a3a7375627374726174655f746573745f70616c6c65740000002900000029000000490000002900000029000000290000004a000000290000002900000029000000290000002900000029000000290000002100000009000000090000002900000029000000290000002900000011000000426c6f636b73705f72756e74696d653a3a67656e657269633a3a626c6f636b48656164657245787472696e73696373705f72756e74696d653a3a67656e657269633a3a6865616465724e756d626572486173682f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f636f6c6c656374696f6e732f62747265652f6e617669676174652e727300cf7e110084000000ae00000024000000556e636865636b656445787472696e736963282c20290000647f110013000000777f110002000000797f1100010000005065724469737061746368436c6173736672616d655f737570706f72743a3a6469737061746368544e6f6e65556e636865636b656445787472696e73696373705f72756e74696d653a3a67656e657269633a3a756e636865636b65645f65787472696e7369634164647265737343616c6c5369676e61747572654578747261004e756d626572206f6620646967657374206974656d73206d757374206d6174636820746861742063616c63756c617465642e000014801100320000002f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f6672616d652f6578656375746976652f7372632f6c69622e72730000508011005e000000360300000900000053746f7261676520726f6f74206d757374206d6174636820746861742063616c63756c617465642ec080110028000000508011005e00000044030000090000005472616e73616374696f6e207472696520726f6f74206d7573742062652076616c69642e0081110024000000508011005e0000004603000009000000446967657374206974656d206d757374206d6174636820746861742063616c63756c617465642e003c81110027000000508011005e0000003e0300000d0000003a206120646566656e73697665206661696c75726520686173206265656e207472696767657265643b20706c65617365207265706f72742074686520626c6f636b206e756d6265722061742068747470733a2f2f6769746875622e636f6d2f706172697479746563682f7375627374726174652f69737375657300007e8111007800000072756e74696d653a3a646566656e736976656672616d655f6578656375746976650000007c7e1100000000007c811100020000006e756d5f696e686572656e7473203d3d206e756d5f65787472696e7369637300348211001f000000506172656e7420686173682073686f756c642062652076616c69642e5c8211001c000000508011005e0000007102000009000000508011005e000000b7020000110000007c7e1100000000004163636f756e744461746170616c6c65745f62616c616e6365733a3a747970657342616c616e636542616c616e63654c6f636b5265736572766544617461526573657276654964656e7469666965722f686f6d652f6d69737a6b612f7061726974792f31302d67656e657369732d636f6e6669672f706f6c6b61646f742d73646b2d6d61737465722f7375627374726174652f7072696d6974697665732f73746174652d6d616368696e652f7372632f747269655f6261636b656e645f657373656e63652e727300f782110078000000a20100001b0000000902000000000000010000000a0200000b0200000c0200000d0200000902000000000000010000000e0200000f02000045717569766f636174696f6e50726f6f6673705f636f6e73656e7375735f736c6f74734964000000cf7e110084000000c700000027000000cf7e110084000000170200002f000000cf7e110084000000a200000024000000536f6d65100200000400000004000000110200003c7761736d3a73747269707065643e00140200000800000008000000150200001402000008000000080000001602000014020000080000000800000017020000180200000100000001000000190200001402000010000000080000001a0200001402000010000000080000001b0200008c841100000000001c02000008000000040000001d0200001e0200001f0200002002000021020000220200002302000024020000203d0000c084110001000000c1841100010000006d657373616765008c84110000000000c1841100010000001c0200000800000004000000250200001c020000180000000400000026020000417474656d7074656420746f2072656769737465722061206044656661756c7443616c6c7369746560207468617420616c7265616479206578697374732120546869732077696c6c20636175736520616e20696e66696e697465206c6f6f70207768656e20617474656d7074696e6720746f20726561642066726f6d207468652063616c6c736974652063616368652e2054686973206973206c696b656c792061206275672120596f752073686f756c64206f6e6c79206e65656420746f2063616c6c206044656661756c7443616c6c736974653a3a726567697374657260206f6e636520706572206044656661756c7443616c6c73697465602e000c851100fb0000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f74726163696e672d636f72652d302e312e33322f7372632f63616c6c736974652e72730000001086110065000000bd0100000d00000027020000180000000400000026020000280200000000000001000000290200002a0200002b0200002c0200002d0200002e0200002f02000030020000310200003102000031020000320200003302000034020000350200003602000037020000040000000400000038020000370200000400000004000000380200002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f74726163696e672d636f72652d302e312e33322f7372632f6669656c642e7273000004871100620000000403000009000000617373657274696f6e206661696c65643a20696e646578203c3d206c656e2f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f736d616c6c7665632d312e31332e322f7372632f6c69622e72730000968711005c000000ef060000090000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f747269652d64622d302e32392e302f7372632f6e6962626c652f6d6f642e727300000488110062000000490000001c0000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f747269652d64622d302e32392e302f7372632f6e6962626c652f6e6962626c65736c6963652e72730000788811006a0000003f0000001b000000788811006a0000005000000036000000788811006a000000560000003b000000788811006a0000008f0000002a000000788811006a000000900000002b000000788811006a000000990000004b000000788811006a0000009900000031000000788811006a000000af00000020000000788811006a000000ad00000029000000788811006a000000f400000018000000788811006a000000f600000018000000788811006a000000f60000003d0000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f747269652d64622d302e32392e302f7372632f6e6962626c652f6e6962626c657665632e7273a48911006800000035000000320000006c656e20213d20302073696e6365206c656e2025203220213d20303b20696e6e6572206861732061206c61737420656c656d656e743b207165640000a48911006800000042000000120000006c656e20213d20303b20696e6e657220686173206c61737420656c656d3b207165640000a4891100680000004d00000025000000a489110068000000690000003d000000a4891100680000007200000019000000a4891100680000007400000019000000a4891100680000007400000048000000a4891100680000008500000030000000a4891100680000008500000048000000a4891100680000008700000036000000a4891100680000009c00000043000000a4891100680000009e0000001b000000a4891100680000009f0000004000000063616c6c65642060526573756c743a3a756e77726170282960206f6e20616e2060457272602076616c7565003902000000000000010000003a0200002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f73796e632e72730000788b11006e000000b0070000290000004c61796f75744572726f722f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f747269652d64622d302e32392e302f7372632f7472696564626d75742e7273038c1100610000004a08000032000000038c1100610000004a080000100000002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f747269652d64622d302e32392e302f7372632f6e6962626c652f6d6f642e72730000848c110062000000880000001d000000848c1100620000007f00000022000000848c1100620000007e00000036000000848c1100620000007e00000047000000848c110062000000870000002f000000848c110062000000870000004400000041726320636f756e746572206f766572666c6f77488d1100140000002f686f6d652f6d69737a6b612f2e7275737475702f746f6f6c636861696e732f312e37372e302d7838365f36342d756e6b6e6f776e2d6c696e75782d676e752f6c69622f727573746c69622f7372632f727573742f6c6962726172792f616c6c6f632f7372632f73796e632e72730000648d11006e0000002e0600000d000000488d110000000000648d11006e000000dc0a00000d00000063616c6c65642060526573756c743a3a756e77726170282960206f6e20616e2060457272602076616c7565003c02000000000000010000003d020000648d11006e00000062070000290000004c61796f757473697a6500003e02000004000000040000003f020000616c69676e0000003e0200000400000004000000400200004c61796f75744572726f722f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f747269652d64622d302e32392e302f7372632f6e6f64652e727300878e11005c000000bc01000042000000878e11005c000000bd01000046000000878e11005c000000da01000026000000878e11005c000000ed0100003c000000878e11005c000000ee0100003800000043617061636974794f766572666c6f77416c6c6f634572726c61796f757400004102000004000000040000004202000063616c6c65642060526573756c743a3a756e77726170282960206f6e20616e2060457272602076616c756500410200000800000004000000430200002f686f6d652f6d69737a6b612f2e636172676f2f72656769737472792f7372632f696e6465782e6372617465732e696f2d366631376432326262613135303031662f736d616c6c7665632d312e31332e322f7372632f6c69622e7273a08f11005c000000520100002e0000006361706163697479206f766572666c6f77000000a08f11005c0000004101000036000000a08f11005c000000ce0400000e000000617373657274696f6e206661696c65643a206e65775f636170203e3d206c656ea08f11005c000000990400000d0000000041f0a0c6000b8c033300000000000000000000000000000000000000000000000000000054391000543910002a00000093000000000000009400000000000000950000000000000096000000000000009700000000000000980000000000000099000000000000009a000000000000009b000000000000009c000000000000009d000000000000009e000000000000009f00000000000000a000000000000000a100000000000000a200000000000000a300000000000000a400000000000000a500000000000000a600000000000000a700000000000000a800000000000000a900000000000000aa00000000000000ab00000000000000ac00000000000000ad00000000000000ae00000000000000af00000000000000b000000000000000b100000000000000b200000000000000b300000000000000b400000000000000b500000000000000b600000000000000b700000000000000b800000000000000b900000000000000ba00000000000000bb00000000000000f012110000000000ff00000000000000e486110098861100050000000041fca3c6000b1c0000000000000000000000000000000000000000000000000000000000330f72756e74696d655f76657273696f6e10746573742c7061726974792d74657374010000000200000002000000000100000001009d010c72756e74696d655f61706973df6acb689907609b0500000037e397fc7c91f5e402000000d2bc9897eed08f150300000040fe3ad401f8959a06000000bc9d89904f5b923f01000000c6e9a76309f39b0902000000dd718d5cc53262d401000000cbca25e39f14238702000000f78b278be53f454c02000000ab3c0572291feb8b01000000ed99c5acb25eedf503000000fbc577b9d747efd60100000000e2ca0c046e616d6501acca0c8b0e001c6578745f73746f726167655f617070656e645f76657273696f6e5f31011b6578745f73746f726167655f636c6561725f76657273696f6e5f3102226578745f73746f726167655f636c6561725f7072656669785f76657273696f6e5f3203286578745f73746f726167655f636f6d6d69745f7472616e73616374696f6e5f76657273696f6e5f3104196578745f73746f726167655f6765745f76657273696f6e5f31051e6578745f73746f726167655f6e6578745f6b65795f76657273696f6e5f31061a6578745f73746f726167655f726561645f76657273696f6e5f31072a6578745f73746f726167655f726f6c6c6261636b5f7472616e73616374696f6e5f76657273696f6e5f31081a6578745f73746f726167655f726f6f745f76657273696f6e5f3209196578745f73746f726167655f7365745f76657273696f6e5f310a276578745f73746f726167655f73746172745f7472616e73616374696f6e5f76657273696f6e5f310b206578745f68617368696e675f626c616b65325f3132385f76657273696f6e5f310c206578745f68617368696e675f626c616b65325f3235365f76657273696f6e5f310d1e6578745f68617368696e675f74776f785f3132385f76657273696f6e5f310e1d6578745f68617368696e675f74776f785f36345f76657273696f6e5f310f226578745f6f6666636861696e5f696e6465785f636c6561725f76657273696f6e5f3110206578745f6f6666636861696e5f696e6465785f7365745f76657273696f6e5f3111236578745f63727970746f5f65636473615f67656e65726174655f76657273696f6e5f3112266578745f63727970746f5f65636473615f7075626c69635f6b6579735f76657273696f6e5f31131f6578745f63727970746f5f65636473615f7369676e5f76657273696f6e5f3114216578745f63727970746f5f65636473615f7665726966795f76657273696f6e5f3215256578745f63727970746f5f656432353531395f67656e65726174655f76657273696f6e5f3116286578745f63727970746f5f656432353531395f7075626c69635f6b6579735f76657273696f6e5f3117216578745f63727970746f5f656432353531395f7369676e5f76657273696f6e5f3118236578745f63727970746f5f656432353531395f7665726966795f76657273696f6e5f3119256578745f63727970746f5f737232353531395f67656e65726174655f76657273696f6e5f311a286578745f63727970746f5f737232353531395f7075626c69635f6b6579735f76657273696f6e5f311b216578745f63727970746f5f737232353531395f7369676e5f76657273696f6e5f311c236578745f63727970746f5f737232353531395f7665726966795f76657273696f6e5f321d296578745f6f6666636861696e5f7375626d69745f7472616e73616374696f6e5f76657273696f6e5f311e256578745f7472616e73616374696f6e5f696e6465785f696e6465785f76657273696f6e5f311f196578745f6c6f6767696e675f6c6f675f76657273696f6e5f31201f6578745f6c6f6767696e675f6d61785f6c6576656c5f76657273696f6e5f31211c6578745f616c6c6f6361746f725f667265655f76657273696f6e5f31221e6578745f616c6c6f6361746f725f6d616c6c6f635f76657273696f6e5f3123286578745f64656661756c745f6368696c645f73746f726167655f726561645f76657273696f6e5f3124276578745f64656661756c745f6368696c645f73746f726167655f7365745f76657273696f6e5f31252a6578745f747269655f626c616b65325f3235365f6f7264657265645f726f6f745f76657273696f6e5f32261c6578745f6d6973635f7072696e745f6865785f76657273696f6e5f31271d6578745f6d6973635f7072696e745f757466385f76657273696f6e5f3128226578745f6d6973635f72756e74696d655f76657273696f6e5f76657273696f6e5f31291a5f5f727573745f616c6c6f635f6572726f725f68616e646c65722a305f5a4e34636f726533666d743557726974653977726974655f666d7431376861663163633133303635343162653038452b4c5f5a4e34636f726533707472343264726f705f696e5f706c616365244c5424616c6c6f632e2e737472696e672e2e537472696e672447542431376830336135366130626537376637613432452c5d5f5a4e35385f244c5424616c6c6f632e2e737472696e672e2e537472696e67247532302461732475323024636f72652e2e666d742e2e5772697465244754243977726974655f73747231376830306136663730663763613531633235452d5f5f5a4e35385f244c5424616c6c6f632e2e737472696e672e2e537472696e67247532302461732475323024636f72652e2e666d742e2e577269746524475424313077726974655f6368617231376836656230613834393730363936353764452e385f5a4e35616c6c6f63377261775f766563313763617061636974795f6f766572666c6f7731376833643832313234636131666431396363452f595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137686261373865343038643263393666336545304b5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376838393162626637303462336364333539452e6c6c766d2e31393739383737353732323834393835383834314c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f70757368313768626465353131306261356161353636634532375f5a4e35616c6c6f6335616c6c6f63313868616e646c655f616c6c6f635f6572726f72313768633962373561656666333933346231344533615f5a4e34636f726533707472333764726f705f696e5f706c616365244c5424636f72652e2e666d742e2e4572726f722447542431376866616235626136623938346139656434452e6c6c766d2e313039383239393330343532323938373935313534665f5a4e34636f726533707472343264726f705f696e5f706c616365244c5424616c6c6f632e2e737472696e672e2e537472696e672447542431376830336135366130626537376637613432452e6c6c766d2e3130393832393933303435323239383739353135356c5f5a4e35335f244c5424636f72652e2e666d742e2e4572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376863313334333061303064613965336232452e6c6c766d2e3130393832393933303435323239383739353135367c5f5a4e36395f244c5424636f72652e2e616c6c6f632e2e6c61796f75742e2e4c61796f75744572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376832393637613332326332613165663338452e6c6c766d2e313039383239393330343532323938373935313537095f5f72646c5f6f6f6d38365f5a4e35616c6c6f6333666d7436666f726d61743132666f726d61745f696e6e6572313768313738663061633662316238316561614539775f5a4e35385f244c5424616c6c6f632e2e737472696e672e2e537472696e67247532302461732475323024636f72652e2e666d742e2e5772697465244754243977726974655f73747231376830306136663730663763613531633235452e6c6c766d2e31303938323939333034353232393837393531353a795f5a4e35385f244c5424616c6c6f632e2e737472696e672e2e537472696e67247532302461732475323024636f72652e2e666d742e2e577269746524475424313077726974655f6368617231376836656230613834393730363936353764452e6c6c766d2e31303938323939333034353232393837393531353b375f5a4e36626c616b65323134426c616b653262566172436f726538636f6d707265737331376862373461393335333133656536623032453c305f5a4e3462733538366465636f646531316465636f64655f696e746f31376832366665303063303966626636623863453d575f5a4e34636f726533707472353364726f705f696e5f706c616365244c5424636f72652e2e616c6c6f632e2e6c61796f75742e2e4c61796f75744572726f722447542431376832613236653265366332366232333665453e625f5a4e36395f244c5424636f72652e2e616c6c6f632e2e6c61796f75742e2e4c61796f75744572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376832393637613332326332613165663338453f7a5f5a4e39325f244c542462797465732e2e62797465732e2e4279746573247532302461732475323024636f72652e2e636f6e766572742e2e46726f6d244c5424616c6c6f632e2e7665632e2e566563244c542475382447542424475424244754243466726f6d3137683765643461346138383266613761633045404a5f5a4e35627974657335627974657331327374617469635f636c6f6e6531376833323130386566643438323766363666452e6c6c766d2e3334343432333333353235373333383435333141325f5a4e35627974657335627974657331337374617469635f746f5f7665633137683034383536303131313538303561363745424e5f5a4e35627974657335627974657331367374617469635f69735f756e6971756531376836633034666662366437323062666139452e6c6c766d2e3334343432333333353235373333383435333143495f5a4e35627974657335627974657331317374617469635f64726f7031376864643031336537396536363530303731452e6c6c766d2e33343434323333333532353733333834353331443a5f5a4e356279746573356279746573323170726f6d6f7461626c655f6576656e5f636c6f6e65313768343363333339313537326466383137614545365f5a4e35627974657335627974657331377368616c6c6f775f636c6f6e655f7665633137683061656661613439613631353238316145463b5f5a4e356279746573356279746573323270726f6d6f7461626c655f6576656e5f746f5f766563313768376337343431613235356465346530354547375f5a4e35627974657335627974657331387368617265645f746f5f7665635f696d706c313768326566333265646563363862616531384548395f5a4e356279746573356279746573323070726f6d6f7461626c655f6576656e5f64726f70313768396532366232633039653138383164384549395f5a4e356279746573356279746573323070726f6d6f7461626c655f6f64645f636c6f6e6531376861396666386432383230363239316630454a3a5f5a4e356279746573356279746573323170726f6d6f7461626c655f6f64645f746f5f76656331376839616166303361376137383561363530454b385f5a4e356279746573356279746573313970726f6d6f7461626c655f6f64645f64726f7031376837303033346434313735363163356530454c395f5a4e356279746573356279746573323070726f6d6f7461626c655f69735f756e6971756531376831333537643837376665396133633162454d4a5f5a4e35627974657335627974657331327368617265645f636c6f6e6531376862633730623138623230636237393064452e6c6c766d2e333434343233333335323537333338343533314e4b5f5a4e35627974657335627974657331337368617265645f746f5f76656331376839396139623662356236303333326238452e6c6c766d2e333434343233333335323537333338343533314f355f5a4e35627974657335627974657331367368617265645f69735f756e69717565313768383462326136323837323563336466644550495f5a4e35627974657335627974657331317368617265645f64726f7031376863383731383462666264363461336363452e6c6c766d2e3334343432333333353235373333383435333151235f5a4e3562797465733561626f7274313768616536373034383432313466636561384552305f5a4e34636f7265336e756d313466726f6d5f7374725f72616469783137686662343361316134393034616533366445534e5f5a4e34636f726533666d74336e756d313470617273655f7536345f696e746f31376866333161333061663037626334346262452e6c6c766d2e313832363532393630363530313031383537353054475f5a4e34636f726533666d74336e756d38666d745f7531323831376836663831323232666336353939343036452e6c6c766d2e3138323635323936303635303130313835373530555c5f5a4e34636f726533666d74336e756d35305f244c5424696d706c2475323024636f72652e2e666d742e2e42696e6172792475323024666f72247532302475382447542433666d74313768653039663762636130333134363631304556705f5a4e34636f726533707472353264726f705f696e5f706c616365244c5424636f72652e2e666d742e2e6275696c646572732e2e506164416461707465722447542431376833383639396237306233346438623161452e6c6c766d2e313134313435373138353633303837323235383957535f5a4e34636f72653463686172376d6574686f647332325f244c5424696d706c2475323024636861722447542431366573636170655f64656275675f657874313768313166356433366163663835653436654558565f5a4e35375f244c5424636f72652e2e666d742e2e417267756d656e7473247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d74313768636532626463316362313866386538364559265f5a4e34636f726533666d7435777269746531376833316239306639333939316161316166455a585f5a4e35395f244c5424636f72652e2e666d742e2e417267756d656e7473247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d7431376832323263633935653933313931633065455b385f5a4e34636f726533666d7439466f726d617474657231327061645f696e74656772616c31376865363062666135303835353662663637455c465f5a4e34636f726533666d7439466f726d617474657231327061645f696e74656772616c313277726974655f70726566697831376866363037623038356465323433646430455d2e5f5a4e34636f726533666d7439466f726d61747465723370616431376839643066373134613465333036653537455e3f5f5a4e34636f726533666d7439466f726d617474657231397061645f666f726d61747465645f706172747331376866613566626361396534663366643739455f415f5a4e34636f726533666d7439466f726d6174746572323177726974655f666f726d61747465645f7061727473313768633039663339346136333039306233354560465f5a4e34636f726533666d7439466f726d6174746572323664656275675f7374727563745f6669656c64335f66696e697368313768376531303563333761333334303632364561485f5a4e34335f244c5424626f6f6c247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d74313768386633653662656130636263643036664562455f5a4e34305f244c5424737472247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d74313768373436386136383939373866633230654563465f5a4e34315f244c542463686172247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d74313768383531393237346430323466633533654564485f5a4e34335f244c542463686172247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d74313768613762373831393835373164623434344565475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d74313768346364336130343235653439383736384566475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d74313768646238353166633131656430626537364567495f5a4e34345f244c54242452462454247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d74313768346164326330366665643939366634394568455f5a4e33365f244c542454247532302461732475323024636f72652e2e616e792e2e416e792447542437747970655f6964313768376665643762343638326265623161384569665f5a4e37335f244c5424636f72652e2e70616e69632e2e70616e69635f696e666f2e2e50616e6963496e666f247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d7431376837363235313165623539666437646662456a395f5a4e34636f7265336e756d366269676e756d384269673332783430386d756c5f706f773231376832333939653931373730303134613065456b3c5f5a4e34636f7265336e756d366269676e756d38426967333278343031306d756c5f64696769747331376838323466373032346336373365353031456c3b5f5a4e34636f7265336e756d37666c743264656331376469676974735f746f5f6465635f73747231376861643333386363303862663761646464456d455f5a4e34636f726533666d7435666c6f61743239666c6f61745f746f5f646563696d616c5f636f6d6d6f6e5f657861637431376864626231396163396638623139653135456e485f5a4e34636f726533666d7435666c6f61743332666c6f61745f746f5f646563696d616c5f636f6d6d6f6e5f73686f727465737431376832356165303934633231343635623632456f4c5f5a4e34636f726533666d7435666c6f61743336666c6f61745f746f5f6578706f6e656e7469616c5f636f6d6d6f6e5f73686f72746573743137686634343439643662333438626433336545705e5f5a4e34636f726533666d7435666c6f617435305f244c5424696d706c2475323024636f72652e2e666d742e2e44656275672475323024666f7224753230246636342447542433666d74313768313334356638336136653937306362614571605f5a4e34636f726533666d7435666c6f617435325f244c5424696d706c2475323024636f72652e2e666d742e2e446973706c61792475323024666f7224753230246636342447542433666d74313768323431376263646466626264396133304572395f5a4e34636f726533737472377061747465726e31315374725365617263686572336e65773137683635623239366636353436383834633645734a5f5a4e34636f726537756e69636f64653132756e69636f64655f6461746131356772617068656d655f657874656e64366c6f6f6b75703137683361303135363166376438363565343645743e5f5a4e34636f726533707472323864726f705f696e5f706c616365244c542424524624753634244754243137686632383034353838343339356361626245757a5f5a4e34636f726533707472383864726f705f696e5f706c616365244c5424636f72652e2e70616e69632e2e70616e69635f696e666f2e2e50616e6963496e666f2e2e696e7465726e616c5f636f6e7374727563746f722e2e4e6f5061796c6f616424475424313768393561663934356261646666663164354576305f5a4e34636f72653970616e69636b696e673970616e69635f666d743137683137656265323637393339346434363345773a5f5a4e34636f72653970616e69636b696e67313870616e69635f6e6f756e77696e645f666d743137683162623235323536323563626365336345782c5f5a4e34636f72653970616e69636b696e673570616e69633137683031333364623535393431663137393945793a5f5a4e34636f72653970616e69636b696e67313870616e69635f626f756e64735f636865636b31376863346539323535633831643234393765457a355f5a4e34636f72653970616e69636b696e6731336173736572745f6661696c656431376837343364343064623063656262303634457b3b5f5a4e34636f72653970616e69636b696e6731396173736572745f6661696c65645f696e6e657231376835316661346564363635633865663539457c335f5a4e34636f72653373747238636f6e76657274733966726f6d5f7574663831376830623066636266373533313961663332457d365f5a4e34636f72653373747235636f756e743134646f5f636f756e745f636861727331376833663034386438366363316436653337457e4a5f5a4e34636f726533666d74336e756d33696d7037666d745f75363431376837616563323263663137613936363734452e6c6c766d2e31313731353539313634323639313336333630377f615f5a4e34636f726533666d74336e756d33696d7035315f244c5424696d706c2475323024636f72652e2e666d742e2e446973706c61792475323024666f72247532302475382447542433666d7431376839663264653863653532666165626465458001625f5a4e34636f726533666d74336e756d33696d7035325f244c5424696d706c2475323024636f72652e2e666d742e2e446973706c61792475323024666f7224753230247533322447542433666d7431376862396262366262616238333065376239458101625f5a4e34636f726533666d74336e756d33696d7035325f244c5424696d706c2475323024636f72652e2e666d742e2e446973706c61792475323024666f7224753230246936342447542433666d7431376863663861323531343166393861333230458201625f5a4e34636f726533666d74336e756d33696d7035325f244c5424696d706c2475323024636f72652e2e666d742e2e446973706c61792475323024666f7224753230247536342447542433666d7431376833313364363335616231313038356364458301565f5a4e34636f726533707472353264726f705f696e5f706c616365244c5424636f72652e2e666d742e2e6275696c646572732e2e506164416461707465722447542431376833383639396237306233346438623161458401645f5a4e37315f244c5424636f72652e2e6f70732e2e72616e67652e2e52616e6765244c542449647824475424247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376862343735376365333866656262633462458501595f5a4e36305f244c5424636f72652e2e63656c6c2e2e426f72726f774572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d74313768323332616130653564616239313365364586015c5f5a4e36335f244c5424636f72652e2e63656c6c2e2e426f72726f774d75744572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376863353232313733616163616538656532458701395f5a4e34636f72653463656c6c323270616e69635f616c72656164795f626f72726f77656431376837303439376565663966656636633765458801415f5a4e34636f72653463656c6c333070616e69635f616c72656164795f6d757461626c795f626f72726f77656431376836326239396535336130613565643564458901325f5a4e34636f726536726573756c743133756e777261705f6661696c656431376833353261353332373338663466376236458a01675f5a4e36385f244c5424636f72652e2e666d742e2e6275696c646572732e2e50616441646170746572247532302461732475323024636f72652e2e666d742e2e5772697465244754243977726974655f73747231376863356532376635376237333137666235458b01695f5a4e36385f244c5424636f72652e2e666d742e2e6275696c646572732e2e50616441646170746572247532302461732475323024636f72652e2e666d742e2e577269746524475424313077726974655f6368617231376836666333306633356433313038323264458c013c5f5a4e34636f726533666d74386275696c6465727331314465627567537472756374356669656c6431376830363965386366316632636331343732458d013b5f5a4e34636f726533666d74386275696c64657273313044656275675475706c65356669656c6431376833316165313537643739313566313730458e01395f5a4e34636f726533666d74386275696c646572733944656275674c69737435656e74727931376866346338656232383861636362376565458f01305f5a4e34636f726533666d743557726974653977726974655f666d7431376834383634653635626533623334313461459001425f5a4e34636f7265336e756d37666c743264656338737472617465677936647261676f6e396d756c5f706f77313031376838636138613634343339646563343638459101495f5a4e34636f7265336e756d37666c743264656338737472617465677936647261676f6e3135666f726d61745f73686f727465737431376833666666313032623834656633373637459201465f5a4e34636f7265336e756d37666c743264656338737472617465677936647261676f6e3132666f726d61745f657861637431376832663931616639666233353432336333459301535f5a4e34636f7265336f70733866756e6374696f6e36466e4f6e63653963616c6c5f6f6e636531376832363432343863326539396266316539452e6c6c766d2e313134333339373135393234303630383036389401445f5a4e34636f726535736c69636535696e6465783236736c6963655f73746172745f696e6465785f6c656e5f6661696c31376836343839663765366561306337333166459501425f5a4e34636f726535736c69636535696e6465783234736c6963655f656e645f696e6465785f6c656e5f6661696c31376838376562323035343262366635616166459601405f5a4e34636f726535736c69636535696e6465783232736c6963655f696e6465785f6f726465725f6661696c31376835373830636663326565346438646463459701325f5a4e34636f7265337374723136736c6963655f6572726f725f6661696c31376834626132313961633437626533396631459801355f5a4e34636f7265337374723139736c6963655f6572726f725f6661696c5f7274313768613266313161653166643639613535644599014d5f5a4e34636f726537756e69636f6465397072696e7461626c6535636865636b31376837346365663562363466373538633334452e6c6c766d2e313134333339373135393234303630383036389a013c5f5a4e34636f726537756e69636f6465397072696e7461626c65313269735f7072696e7461626c6531376832663963336261373564323438383466459b01755f5a4e34636f726533666d74336e756d35305f244c5424696d706c2475323024636f72652e2e666d742e2e44656275672475323024666f7224753230247533322447542433666d7431376830396334333131313138623438393031452e6c6c766d2e313139343530303336353038343732313030329c0183015f5a4e37375f244c5424636f72652e2e6e756d2e2e6e6f6e7a65726f2e2e4e6f6e5a65726f244c54247573697a6524475424247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376831323163323638626262326164613534452e6c6c766d2e313139343530303336353038343732313030329d014c5f5a4e34636f7265336e756d37666c74326465633873747261746567793567726973753139666f726d61745f73686f72746573745f6f707431376832303531386436386635656662396139459e01495f5a4e34636f7265336e756d37666c74326465633873747261746567793567726973753136666f726d61745f65786163745f6f707431376866396436363961303335663462643461459f01595f5a4e34636f7265336e756d37666c74326465633873747261746567793567726973753136666f726d61745f65786163745f6f70743134706f737369626c795f726f756e643137683932623532386134663563333831326345a001325f5a4e34636f7265366f7074696f6e3133756e777261705f6661696c65643137686434373562333534393730656433303945a101325f5a4e34636f7265366f7074696f6e31336578706563745f6661696c65643137683761373162626562623861336138346145a201655f5a4e34636f726535736c69636532395f244c5424696d706c24753230242475356224542475356424244754243135636f70795f66726f6d5f736c69636531376c656e5f6d69736d617463685f6661696c3137686161396465386631393031353636383445a3015d5f5a4e3136637572766532353531395f64616c656b376261636b656e643673657269616c33753332356669656c6431364669656c64456c656d656e74323632353861735f62797465733137686366646330653336623538323836656145a40190015f5a4e3136637572766532353531395f64616c656b356669656c6438315f244c5424696d706c2475323024637572766532353531395f64616c656b2e2e6261636b656e642e2e73657269616c2e2e7533322e2e6669656c642e2e4669656c64456c656d656e7432363235244754243132737172745f726174696f5f693137683461626638306435383439626462316445a5017b5f5a4e3136637572766532353531395f64616c656b376261636b656e643673657269616c33753332356669656c6431364669656c64456c656d656e743236323531327371756172655f696e6e657231376832393661646431346662313362343863452e6c6c766d2e33313435303338383738363536343237363236a601d6015f5a4e3138345f244c542424524624637572766532353531395f64616c656b2e2e6261636b656e642e2e73657269616c2e2e7533322e2e6669656c642e2e4669656c64456c656d656e7432363235247532302461732475323024636f72652e2e6f70732e2e61726974682e2e4d756c244c542424524624637572766532353531395f64616c656b2e2e6261636b656e642e2e73657269616c2e2e7533322e2e6669656c642e2e4669656c64456c656d656e74323632352447542424475424336d756c3137686531633862376162376238646465333245a7015a5f5a4e3136637572766532353531395f64616c656b376261636b656e643673657269616c33753332356669656c6431364669656c64456c656d656e743236323535706f77326b3137683333323961306331386633323239396145a801d6015f5a4e3138345f244c542424524624637572766532353531395f64616c656b2e2e6261636b656e642e2e73657269616c2e2e7533322e2e6669656c642e2e4669656c64456c656d656e7432363235247532302461732475323024636f72652e2e6f70732e2e61726974682e2e537562244c542424524624637572766532353531395f64616c656b2e2e6261636b656e642e2e73657269616c2e2e7533322e2e6669656c642e2e4669656c64456c656d656e74323632352447542424475424337375623137686366623936343361316632393537623245a9018b015f5a4e3130395f244c542424524624637572766532353531395f64616c656b2e2e6261636b656e642e2e73657269616c2e2e7533322e2e6669656c642e2e4669656c64456c656d656e7432363235247532302461732475323024636f72652e2e6f70732e2e61726974682e2e4e656724475424336e65673137683836363239633063393965666636346245aa01605f5a4e3136637572766532353531395f64616c656b376261636b656e643673657269616c33753332356669656c6431364669656c64456c656d656e7432363235313066726f6d5f62797465733137683637646232333431303863376333343845ab01585f5a4e3136637572766532353531395f64616c656b367363616c6172365363616c61723672656475636531376837616130396331613530383062303237452e6c6c766d2e3137393331343330323733373936393938373634ac0181015f5a4e3136637572766532353531395f64616c656b367363616c617237345f244c5424696d706c2475323024637572766532353531395f64616c656b2e2e6261636b656e642e2e73657269616c2e2e7533322e2e7363616c61722e2e5363616c6172323924475424347061636b3137686133393036643737393836646631636545ad01505f5a4e3136637572766532353531395f64616c656b376261636b656e643673657269616c33753332367363616c6172385363616c61723239337375623137683137393638663832636236353661396645ae01585f5a4e3136637572766532353531395f64616c656b376261636b656e643673657269616c33753332367363616c6172385363616c61723239313066726f6d5f62797465733137686234626232333062633331333032376345af01fd015f5a4e3136637572766532353531395f64616c656b376261636b656e643673657269616c313263757276655f6d6f64656c733137365f244c5424696d706c2475323024636f72652e2e6f70732e2e61726974682e2e416464244c542424524624637572766532353531395f64616c656b2e2e6261636b656e642e2e73657269616c2e2e63757276655f6d6f64656c732e2e50726f6a6563746976654e69656c73506f696e74244754242475323024666f72247532302424524624637572766532353531395f64616c656b2e2e656477617264732e2e45647761726473506f696e7424475424336164643137686666333438303231386330353465363245b0016e5f5a4e38315f244c5424637572766532353531395f64616c656b2e2e656477617264732e2e45647761726473506f696e74247532302461732475323024636f72652e2e6f70732e2e61726974682e2e41646424475424336164643137683339646434643332326436393935383145b101545f5a4e3136637572766532353531395f64616c656b3972697374726574746f3139436f6d7072657373656452697374726574746f31306465636f6d70726573733137683463656465316565663362356534343245b2014c5f5a4e3136637572766532353531395f64616c656b3972697374726574746f313452697374726574746f506f696e7438636f6d70726573733137686465383739343065626437373931633245b301795f5a4e3136637572766532353531395f64616c656b3972697374726574746f313452697374726574746f506f696e743236656c6c696761746f725f72697374726574746f5f666c61766f7231376863383765363761373331636563663363452e6c6c766d2e3138313535353136363236333533343435313238b401575f5a4e3136637572766532353531395f64616c656b3972697374726574746f313452697374726574746f506f696e74313866726f6d5f756e69666f726d5f62797465733137683434383365323265616362353866373845b501755f5a4e38365f244c5424637572766532353531395f64616c656b2e2e72697374726574746f2e2e52697374726574746f506f696e74247532302461732475323024737562746c652e2e436f6e7374616e7454696d654571244754243563745f65713137686337646539363435646138333437393645b601465f5a4e3136637572766532353531395f64616c656b3972697374726574746f31306465636f6d707265737336737465705f313137683164393730623731313563313734366245b701465f5a4e3136637572766532353531395f64616c656b3972697374726574746f31306465636f6d707265737336737465705f323137683065653861323531646239393538346445b801465f5a4e31387061726974795f7363616c655f636f64656335636f6465633139656e636f64655f736c6963655f6e6f5f6c656e3137686231353637383563343936343739393145b9015d5f5a4e34636f726533707472353964726f705f696e5f706c616365244c54247363616c655f696e666f2e2e706f727461626c652e2e506f727461626c655265676973747279244754243137686364623134646231653164613836646645ba01ac015f5a4e35616c6c6f63337665633136696e5f706c6163655f636f6c6c6563743130385f244c5424696d706c2475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c54245424432449244754242475323024666f722475323024616c6c6f632e2e7665632e2e566563244c54245424475424244754243966726f6d5f697465723137683034653833336137323532393764633745bb01ac015f5a4e35616c6c6f63337665633136696e5f706c6163655f636f6c6c6563743130385f244c5424696d706c2475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c54245424432449244754242475323024666f722475323024616c6c6f632e2e7665632e2e566563244c54245424475424244754243966726f6d5f697465723137683462313431326235333839396533383345bc01ac015f5a4e35616c6c6f63337665633136696e5f706c6163655f636f6c6c6563743130385f244c5424696d706c2475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c54245424432449244754242475323024666f722475323024616c6c6f632e2e7665632e2e566563244c54245424475424244754243966726f6d5f697465723137683862643933623863303837633539653145bd01ac015f5a4e35616c6c6f63337665633136696e5f706c6163655f636f6c6c6563743130385f244c5424696d706c2475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c54245424432449244754242475323024666f722475323024616c6c6f632e2e7665632e2e566563244c54245424475424244754243966726f6d5f697465723137683831623061363765306436393534653045be01ac015f5a4e35616c6c6f63337665633136696e5f706c6163655f636f6c6c6563743130385f244c5424696d706c2475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c54245424432449244754242475323024666f722475323024616c6c6f632e2e7665632e2e566563244c54245424475424244754243966726f6d5f697465723137686438396663646636333161393830333945bf01ac015f5a4e35616c6c6f63337665633136696e5f706c6163655f636f6c6c6563743130385f244c5424696d706c2475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c54245424432449244754242475323024666f722475323024616c6c6f632e2e7665632e2e566563244c54245424475424244754243966726f6d5f697465723137686635633833616265306631623262346145c00185015f5a4e39385f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c5424542443244924475424244754243966726f6d5f697465723137683731393039303236643835396438613045c101a9015f5a4e31346672616d655f6d657461646174613132325f244c5424696d706c2475323024636f72652e2e636f6e766572742e2e46726f6d244c54246672616d655f6d657461646174612e2e52756e74696d654d657461646174615072656669786564244754242475323024666f722475323024616c6c6f632e2e7665632e2e566563244c5424753824475424244754243466726f6d3137683737356566343935383737616665343645c2014c5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376838336466636663303462336235303132452e6c6c766d2e3138303139373434373438303230313830383436c301595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137686163313730333462386433613031616345c401f6015f5a4e35616c6c6f633131636f6c6c656374696f6e7335627472656536617070656e643137385f244c5424696d706c2475323024616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e4e6f6465526566244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4f776e65642443244b24432456244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4c6561664f72496e7465726e616c24475424244754243962756c6b5f707573683137683235396539616534343864363030393045c50185015f5a4e3130325f244c5424636f72652e2e697465722e2e61646170746572732e2e6d61702e2e4d6170244c5424492443244624475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f722447542434666f6c643137683362626131623361343562383339326545c60185015f5a4e3130325f244c5424636f72652e2e697465722e2e61646170746572732e2e6d61702e2e4d6170244c5424492443244624475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f722447542434666f6c643137683364646265613164303537306637396545c70185015f5a4e3130325f244c5424636f72652e2e697465722e2e61646170746572732e2e6d61702e2e4d6170244c5424492443244624475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f722447542434666f6c643137683564633737306237306666643266323745c80185015f5a4e3130325f244c5424636f72652e2e697465722e2e61646170746572732e2e6d61702e2e4d6170244c5424492443244624475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f722447542434666f6c643137686562333033396561303761343465663045c901745f5a4e38365f244c5424616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137683232376265663739626436356331303345ca01745f5a4e38365f244c5424616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137686333376462356162363363666138323345cb018c015f5a4e3130345f244c54247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e436f6d70616374526566244c5424753332244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542439656e636f64655f746f3137683436343566616566316331366631623245cc01ae015f5a4e31346672616d655f6d6574616461746133763134315f3131365f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f6d657461646174612e2e7631342e2e50616c6c6574436f6e7374616e744d65746164617461244c542454244754242447542439656e636f64655f746f3137686233316434393131323239653565393145cd01ac015f5a4e31346672616d655f6d6574616461746133763134315f3131345f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f6d657461646174612e2e7631342e2e53746f72616765456e7472794d65746164617461244c542454244754242447542439656e636f64655f746f3137683534613532656633343633393832643045ce01435f5a4e31346672616d655f6d6574616461746133763134313852756e74696d654d65746164617461563134336e65773137686661383637613661666263623734343145cf0182015f5a4e39305f244c54246672616d655f6d657461646174612e2e7631342e2e50616c6c65744d657461646174612475323024617324753230247363616c655f696e666f2e2e72656769737472792e2e496e746f506f727461626c65244754243133696e746f5f706f727461626c653137686537613439306263303136633531636245d001a1015f5a4e31346672616d655f6d6574616461746133763134315f3130335f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f6d657461646174612e2e7631342e2e52756e74696d654d657461646174615631342447542439656e636f64655f746f3137683838653239666465373933633033633045d101a6015f5a4e31346672616d655f6d6574616461746133763134315f3130385f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f6d657461646174612e2e7631342e2e50616c6c65744d65746164617461244c542454244754242447542439656e636f64655f746f3137683631313831316634663466323731346445d201ad015f5a4e31346672616d655f6d6574616461746133763134315f3131355f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f6d657461646174612e2e7631342e2e50616c6c657453746f726167654d65746164617461244c542454244754242447542439656e636f64655f746f3137686464666539313432646331326664356545d3019b015f5a4e31307363616c655f696e666f327479315f3130325f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230247363616c655f696e666f2e2e74792e2e54797065506172616d65746572244c542454244754242447542439656e636f64655f746f3137683365323165393033323465636362303445d401a2015f5a4e31307363616c655f696e666f327479366669656c6473315f3130325f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230247363616c655f696e666f2e2e74792e2e6669656c64732e2e4669656c64244c542454244754242447542439656e636f64655f746f3137686431313236616462353961386535643745d501a6015f5a4e31307363616c655f696e666f3274793776617269616e74315f3130355f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230247363616c655f696e666f2e2e74792e2e76617269616e742e2e56617269616e74244c542454244754242447542439656e636f64655f746f3137683933376166663334623966323566343445d6019c015f5a4e31307363616c655f696e666f38706f727461626c65315f39385f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230247363616c655f696e666f2e2e706f727461626c652e2e506f727461626c65547970652447542439656e636f64655f746f3137683631383662646536323236346137376645d7013f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137683637383730623630323462363833633345d80194015f5a4e31346672616d655f6d65746164617461315f39355f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f6d657461646174612e2e52756e74696d654d65746164617461244754243973697a655f68696e743137683234363333373636353463303162353545d901335f5a4e34636f726535736c69636534736f727431306d657267655f736f72743137683132646262386136303062393266616545da01425f5a4e34636f726535736c69636534736f72743235696e73657274696f6e5f736f72745f73686966745f6c6566743137686165383738336532656534363238643045db0193015f5a4e3131365f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e496e746f49746572244c54244b244324562443244124475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f7224475424346e6578743137686561613831336538323664343561373745dc01645f5a4e36355f244c542424753562245424753564242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542439656e636f64655f746f3137683536343932353237376139353862663945dd01b0015f5a4e31346672616d655f6d6574616461746133763135315f3131385f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f6d657461646174612e2e7631352e2e52756e74696d654170694d6574686f644d65746164617461244c542454244754242447542439656e636f64655f746f3137683531646264353632373935326536616345de0181015f5a4e39395f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e42547265654d6170244c54244b244324562443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137686531636264666231613731366664663645df0181015f5a4e39395f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e496e746f49746572244c54244b244324562443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137686431616434363739303737613065383445e001435f5a4e31346672616d655f6d6574616461746133763135313852756e74696d654d65746164617461563135336e65773137683035613339323630376565626238643645e10182015f5a4e39305f244c54246672616d655f6d657461646174612e2e7631352e2e437573746f6d4d657461646174612475323024617324753230247363616c655f696e666f2e2e72656769737472792e2e496e746f506f727461626c65244754243133696e746f5f706f727461626c653137683438326137373934313736306361386645e2018d015f5a4e3130305f244c54246672616d655f6d657461646174612e2e7631352e2e52756e74696d654170694d6574686f644d657461646174612475323024617324753230247363616c655f696e666f2e2e72656769737472792e2e496e746f506f727461626c65244754243133696e746f5f706f727461626c653137686634326430663564313764613464636345e30182015f5a4e39305f244c54246672616d655f6d657461646174612e2e7631352e2e50616c6c65744d657461646174612475323024617324753230247363616c655f696e666f2e2e72656769737472792e2e496e746f506f727461626c65244754243133696e746f5f706f727461626c653137686632313563383733646564386237303345e401a1015f5a4e31346672616d655f6d6574616461746133763135315f3130335f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f6d657461646174612e2e7631352e2e52756e74696d654d65746164617461563135244754243973697a655f68696e743137683734306265346164636163643564353645e501a1015f5a4e31346672616d655f6d6574616461746133763135315f3130335f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f6d657461646174612e2e7631352e2e52756e74696d654d657461646174615631352447542439656e636f64655f746f3137686439303738643136663363383232396445e601a6015f5a4e31346672616d655f6d6574616461746133763135315f3130385f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f6d657461646174612e2e7631352e2e50616c6c65744d65746164617461244c542454244754242447542439656e636f64655f746f3137686566633362653338633266623432643745e701aa015f5a4e31346672616d655f6d6574616461746133763135315f3131325f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f6d657461646174612e2e7631352e2e52756e74696d654170694d65746164617461244c542454244754242447542439656e636f64655f746f3137683766336333383032613938663239396145e801495f5a4e32396672616d655f6d657461646174615f686173685f657874656e73696f6e31324d657461646174614861736834686173683137683861383934363732376330366538623745e9019a015f5a4e32396672616d655f6d657461646174615f686173685f657874656e73696f6e315f38365f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e4d6f64652447542439747970655f696e666f3137686464643638626163323937346132363345ea014c5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376862386430393662663631323563313463452e6c6c766d2e3132393736303835353438343938383631353737eb014c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683364323163316434353435366436353945ec014b5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376832313461356361366230373631643032452e6c6c766d2e32353638393138333230313631303133383939ed014c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683561333739303230313839313839336145ee014c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683633616434616535326336616366653945ef014c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137686263393435633536383863363761623845f001595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683332663530636432636439373334663045f101595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137686234393034653565663563316664376445f201595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137686431633330636166626165343733383845f301595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137686638663235393032643839633164303545f4018d015f5a4e31336672616d655f737570706f7274386469737061746368315f38305f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f737570706f72742e2e64697370617463682e2e506179732447542439747970655f696e666f3137683933613030653461343866363835336545f50196015f5a4e31336672616d655f737570706f7274386469737061746368315f38395f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f737570706f72742e2e64697370617463682e2e4469737061746368436c6173732447542439747970655f696e666f3137683162346138653234313337666231376345f60195015f5a4e31336672616d655f737570706f7274386469737061746368315f38385f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f737570706f72742e2e64697370617463682e2e4469737061746368496e666f2447542439747970655f696e666f3137683330333965323338353137616531613545f701ad015f5a4e31336672616d655f737570706f72743674726169747336746f6b656e73346d697363315f3130315f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f737570706f72742e2e7472616974732e2e746f6b656e732e2e6d6973632e2e42616c616e63655374617475732447542439747970655f696e666f3137686664616465373732353836626238323645f801595f5a4e36305f244c5424616c6c6f632e2e737472696e672e2e537472696e67247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d743137683864393031613030383666373938373245f901625f5a4e36395f244c54247061726974795f7363616c655f636f6465632e2e6572726f722e2e4572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686363656231383263633834363164346345fa016d5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c32316765745f7472616e73616374696f6e5f6c6576656c31376831653137366465656436393261346163452e6c6c766d2e33353936323533383937393435343238363335fb0180015f5a4e39385f244c54246672616d655f737570706f72742e2e73746f726167652e2e7472616e73616374696f6e616c2e2e53746f726167654c617965724775617264247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137683538626338313333643830376138393345fc012e5f5a4e313161727261795f6279746573396279746573326865783137686631343332353634373665303466376445fd01465f5a4e31336672616d655f737570706f7274313664697370617463685f636f6e7465787436474c4f42414c365f5f696e69743137683464353735316263336661343564363845fe01ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683034656537646332633336366535343345ff01ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f6974657231376837643962306161376138613966306665458002ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137686366663138666363383630646666323345810285015f5a4e39385f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c5424542443244924475424244754243966726f6d5f697465723137683538313538623061376430616337633245820285015f5a4e31307363616c655f696e666f35696d706c7338305f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242447542439747970655f696e666f3137683364373530643366346539363039363045830295015f5a4e31307363616c655f696e666f35696d706c7339365f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e436f6d70616374244c542454244754242447542439747970655f696e666f31376837383362353261343933313939396431458402765f5a4e34636f726533707472353964726f705f696e5f706c616365244c54246672616d655f73797374656d2e2e6c696d6974732e2e56616c69646174696f6e4572726f72732447542431376837303864616334353033396330373037452e6c6c766d2e343136383139373135313634363230353530318502435f5a4e31326672616d655f73797374656d366c696d6974733132426c6f636b576569676874733876616c696461746531376833363762613931363631303861353462458602475f5a4e31326672616d655f73797374656d366c696d6974733139426c6f636b576569676874734275696c646572356275696c64313768626362643930313934363739316266614587028e015f5a4e31326672616d655f73797374656d366c696d697473315f38345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f73797374656d2e2e6c696d6974732e2e426c6f636b4c656e6774682447542439747970655f696e666f31376832383036633662306365346434323264458802685f5a4e37355f244c54246672616d655f73797374656d2e2e6c696d6974732e2e56616c69646174696f6e4572726f7273247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376863373336373966393830313039636266458902a0015f5a4e31326672616d655f73797374656d366c696d697473315f3130315f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f73797374656d2e2e6c696d6974732e2e57656967687473506572436c617373244754243973697a655f68696e7431376866343861306230666134313537633934458a0292015f5a4e31326672616d655f73797374656d366c696d697473315f38385f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f73797374656d2e2e6c696d6974732e2e57656967687473506572436c6173732447542439747970655f696e666f31376834396462643336346138323936343661458b029c015f5a4e31326672616d655f73797374656d366c696d697473315f39385f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f73797374656d2e2e6c696d6974732e2e426c6f636b57656967687473244754243973697a655f68696e7431376834636435633431633038353437306339458c028f015f5a4e31326672616d655f73797374656d366c696d697473315f38355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f73797374656d2e2e6c696d6974732e2e426c6f636b576569676874732447542439747970655f696e666f31376834626532376363613138613635646663458d02ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f6974657231376830383338346234346561366265666531458e02ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f6974657231376866376230336430643539393737306266458f02ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f6974657231376866383135386663353163396364656166459002a3015f5a4e31336672616d655f737570706f7274386469737061746368315f3130315f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f737570706f72742e2e64697370617463682e2e5065724469737061746368436c617373244c542454244754242447542439747970655f696e666f31376830373666343234633562633634336534459102a3015f5a4e31336672616d655f737570706f7274386469737061746368315f3130315f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f737570706f72742e2e64697370617463682e2e5065724469737061746368436c617373244c542454244754242447542439747970655f696e666f31376830633034303963316432393261366334459202795f5a4e31326672616d655f73797374656d315f37305f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f73797374656d2e2e50686173652447542439747970655f696e666f313768323562363666646130663631633337384593028a015f5a4e31326672616d655f73797374656d315f38375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f73797374656d2e2e4c61737452756e74696d6555706772616465496e666f2447542439747970655f696e666f313768316663333239646138343565363039344594024b5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376864393665316435333530336665356563452e6c6c766d2e3233353739323532383235303835303038333495024c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f70757368313768393036613062636632323935613466374596024c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f7075736831376866396663363665373938653436333731459702595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c6531376834353232316161323963383438393835459802595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c6531376861313265373761306232343831373731459902595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c6531376862356230316132656562653565656466459a02445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e7431376838323838373932303862343037393738459b02445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e7431376865656463666165633630306137323236459c023f5f5a4e366b656363616b386b656363616b5f7031376863386161323430633639663264313561452e6c6c766d2e3339323632383034393030343233353234359d025f5f5a4e34636f726533707472333564726f705f696e5f706c616365244c54246c6f672e2e4e6f704c6f676765722447542431376835383965323339643935303930353363452e6c6c766d2e31303836323130343630343638393633323335399e02665f5a4e34335f244c54246c6f672e2e4e6f704c6f676765722475323024617324753230246c6f672e2e4c6f672447542437656e61626c656431376837626131363931643666333962303861452e6c6c766d2e31303836323130343630343638393633323335399f02625f5a4e34335f244c54246c6f672e2e4e6f704c6f676765722475323024617324753230246c6f672e2e4c6f6724475424336c6f6731376831333563643834666261313161316430452e6c6c766d2e3130383632313034363034363839363332333539a002645f5a4e34335f244c54246c6f672e2e4e6f704c6f676765722475323024617324753230246c6f672e2e4c6f672447542435666c75736831376834636339373639396636626565646331452e6c6c766d2e3130383632313034363034363839363332333539a102335f5a4e336c6f6731335f5f707269766174655f617069386c6f675f696d706c3137683233363835643934323362643030626545a202475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686334393539333131666462356162313345a3023d5f5a4e34636f726533707472323764726f705f696e5f706c616365244c5424245246247538244754243137683536353231383262643035383964353845a402355f5a4e34636f72653970616e69636b696e6731336173736572745f6661696c65643137683932633766366635313863616330373945a502385f5a4e366d65726c696e367374726f6265395374726f626531323838626567696e5f6f703137683935613138383161373539376632353245a6023a5f5a4e366d65726c696e31307472616e73637269707431305472616e736372697074336e65773137686630333838613739613261316435366545a702465f5a4e366d65726c696e31307472616e73637269707431305472616e7363726970743134617070656e645f6d6573736167653137686237386432613861626364316564616645a802475f5a4e366d65726c696e31307472616e73637269707431305472616e73637269707431356368616c6c656e67655f62797465733137683139396639343665653136613232653645a9024b5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376833363434303339363835643063323930452e6c6c766d2e33313730333639323635313932303830323139aa024c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683961343862356639626162363031616645ab024c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137686231636231343532373531396537373445ac028e015f5a4e313570616c6c65745f62616c616e636573357479706573315f38325f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302470616c6c65745f62616c616e6365732e2e74797065732e2e526561736f6e732447542439747970655f696e666f3137683934623234613030333762363530363345ad0291015f5a4e313570616c6c65745f62616c616e636573357479706573315f38355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302470616c6c65745f62616c616e6365732e2e74797065732e2e4578747261466c6167732447542439747970655f696e666f3137683738363661323165396163336662353445ae029a015f5a4e313570616c6c65745f62616c616e636573357479706573315f39345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302470616c6c65745f62616c616e6365732e2e74797065732e2e41646a7573746d656e74446972656374696f6e2447542439747970655f696e666f3137683765323238613762666566366335373745af025e5f5a4e34636f726533666d74336e756d35325f244c5424696d706c2475323024636f72652e2e666d742e2e44656275672475323024666f7224753230247573697a652447542433666d743137686464646433363838656435613663326345b0024b5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376830383063646464613363663233643932452e6c6c766d2e37323338353837393031333039353239363535b102725f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c6531376830306464383863383737323566396339452e6c6c766d2e37323338353837393031333039353239363535b20291015f5a4e39315f244c54247061726974795f7363616c655f636f6465632e2e636f6465632e2e4279746573437572736f722475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e496e7075742447542432377363616c655f696e7465726e616c5f6465636f64655f62797465733137686661393333316138376261633937616245b30285015f5a4e3130325f244c54247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e507265666978496e707574244c542454244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e496e7075742447542434726561643137683832316466626433326334336637656345b402595f5a4e35375f244c54247374722475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542436656e636f64653137683230646433646333616136356166366645b502645f5a4e37315f244c54247061726974795f7363616c655f636f6465632e2e6572726f722e2e4572726f72247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d743137683736653231363062643337396131613845b6027e5f5a4e31357072696d69746976655f7479706573315f37325f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230247072696d69746976655f74797065732e2e483235362447542439747970655f696e666f3137683962666535363234313335363563363845b702655f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f7075736831376830313865383033653035643636626566452e6c6c766d2e37313939383439303534333435303437353339b802ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137686565623638366333623730636330373345b902725f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c6531376862386537663838636630313638306130452e6c6c766d2e37313939383439303534333435303437353339ba024b5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376863306262636233663465353964643634452e6c6c766d2e37313939383439303534333435303437353339bb027e5f5a4e31307363616c655f696e666f35696d706c7337335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024247535622454247533622424753230244e24753564242447542439747970655f696e666f3137686239613237383564623830303561373245bc02405f5a4e3372797536707265747479386d616e7469737361313977726974655f6d616e74697373615f6c6f6e673137683663643363623032376435363635636245bd022b5f5a4e337279753670726574747938666f726d617436343137686430373033343031366539363934613945be029c025f5a4e35616c6c6f633131636f6c6c656374696f6e73356274726565346e6f646532313048616e646c65244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e4e6f6465526566244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4d75742443244b24432456244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4c65616624475424244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e45646765244754243136696e736572745f726563757273696e673137683139636335616331336466383464376645bf029c025f5a4e35616c6c6f633131636f6c6c656374696f6e73356274726565346e6f646532313048616e646c65244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e4e6f6465526566244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4d75742443244b24432456244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4c65616624475424244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e45646765244754243136696e736572745f726563757273696e673137683239643237333762323061613830393045c002a2015f5a4e3130365f244c5424636f72652e2e697465722e2e61646170746572732e2e636861696e2e2e436861696e244c5424412443244224475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f7224475424346e65787431376865386438666238623235346561393937452e6c6c766d2e39303538353435373438363939303634333233c102ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683430316334313061616162663461656445c202ac015f5a4e35616c6c6f63337665633136696e5f706c6163655f636f6c6c6563743130385f244c5424696d706c2475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c54245424432449244754242475323024666f722475323024616c6c6f632e2e7665632e2e566563244c54245424475424244754243966726f6d5f697465723137683236643532313365373031363061633245c302ac015f5a4e35616c6c6f63337665633136696e5f706c6163655f636f6c6c6563743130385f244c5424696d706c2475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c54245424432449244754242475323024666f722475323024616c6c6f632e2e7665632e2e566563244c54245424475424244754243966726f6d5f697465723137683736363639363566326363366361303645c402625f5a4e36375f244c5424616c6c6f632e2e7665632e2e566563244c5424542443244124475424247532302461732475323024636f72652e2e636c6f6e652e2e436c6f6e652447542435636c6f6e653137683061306166383237626337636538653245c502625f5a4e36375f244c5424616c6c6f632e2e7665632e2e566563244c5424542443244124475424247532302461732475323024636f72652e2e636c6f6e652e2e436c6f6e652447542435636c6f6e653137686530656466333463356564656531666145c60285015f5a4e39385f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c5424542443244924475424244754243966726f6d5f697465723137683161643437623264356430626261386145c70285015f5a4e39385f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c5424542443244924475424244754243966726f6d5f697465723137683636656237383536306632623164373545c80285015f5a4e39385f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c5424542443244924475424244754243966726f6d5f697465723137683734316665393834623930396465346645c9024c5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376834393063626561366164316231636530452e6c6c766d2e3135393835393236373434363732363638333538ca024c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683766323232343466306633636135393045cb02595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683461363334346336316139623434666145cc02595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683539393961326663626432626365376245cd02595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683734386136303531643166653138646545ce0285015f5a4e3130325f244c5424636f72652e2e697465722e2e61646170746572732e2e6d61702e2e4d6170244c5424492443244624475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f722447542434666f6c643137683565383762343736333961303339336445cf027d5f5a4e36385f244c54247363616c655f696e666f2e2e74792e2e54797065244c54245424475424247532302461732475323024636f72652e2e636c6f6e652e2e436c6f6e652447542435636c6f6e6531376862333962396564323865356239633933452e6c6c766d2e3130323733353337313336363433353738333932d0028b015f5a4e3130385f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e49746572244c54244b2443245624475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f7224475424346e6578743137686438386238396161363135396630393545d1024b5f5a4e34636f72653373747232315f244c5424696d706c24753230247374722447542431387472696d5f73746172745f6d6174636865733137683136643634396330396130393836363245d202565f5a4e35616c6c6f633131636f6c6c656374696f6e73356274726565336d6170323542547265654d6170244c54244b24432456244324412447542436696e736572743137683864656666343634656335343261636445d30281015f5a4e39395f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e42547265654d6170244c54244b244324562443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137683034393439363666393063613865353045d40281015f5a4e39395f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e42547265654d6170244c54244b244324562443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137686231613133366539613136303232303945d5026f5f5a4e34636f726533707472353264726f705f696e5f706c616365244c54247363616c655f696e666f2e2e74792e2e706174682e2e506174684572726f722447542431376837643834633036333032336638636534452e6c6c766d2e35383434353639303536363434363637353039d602345f5a4e31307363616c655f696e666f32747934706174683450617468336e65773137683737646331386466393439363139323545d7023d5f5a4e31307363616c655f696e666f357574696c73313869735f727573745f6964656e7469666965723137683437663366383065643137396663303245d802425f5a4e31307363616c655f696e666f3274793470617468345061746831366e65775f776974685f7265706c6163653137683337613935333266363961386530623045d9027a5f5a4e36385f244c54247363616c655f696e666f2e2e74792e2e706174682e2e506174684572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376863303238383064363233623735356563452e6c6c766d2e35383434353639303536363434363637353039da02475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683333663866303763653639386461396345db02475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683362396138353262306533643137646345dc024e5f5a4e31307363616c655f696e666f38696e7465726e65723137496e7465726e6572244c542454244754243133696e7465726e5f6f725f6765743137683763613431343133383531656436656545dd02445f5a4e31307363616c655f696e666f387265676973747279385265676973747279313372656769737465725f747970653137683866623063666161646332653436396645de02765f5a4e37385f244c54247363616c655f696e666f2e2e74792e2e547970654465662475323024617324753230247363616c655f696e666f2e2e72656769737472792e2e496e746f506f727461626c65244754243133696e746f5f706f727461626c653137683733386235373762396636396530303545df02485f5a4e31307363616c655f696e666f38726567697374727938526567697374727931376d61705f696e746f5f706f727461626c653137683063643334646364333138373138393945e0026c5f5a4e31307363616c655f696e666f35696d706c7335355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024626f6f6c2447542439747970655f696e666f3137683738613766616237623061383964363745e1026a5f5a4e31307363616c655f696e666f35696d706c7335335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302475382447542439747970655f696e666f3137683638323036303733636633633735306145e2026b5f5a4e31307363616c655f696e666f35696d706c7335345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230247531362447542439747970655f696e666f3137683430313662323339626334336366666345e3026b5f5a4e31307363616c655f696e666f35696d706c7335345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230247533322447542439747970655f696e666f3137686432303832383334363734663733626345e4026b5f5a4e31307363616c655f696e666f35696d706c7335345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230247536342447542439747970655f696e666f3137683735363131616139643165313765383245e5026c5f5a4e31307363616c655f696e666f35696d706c7335355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024753132382447542439747970655f696e666f3137683736323365346661396266313736386245e602705f5a4e31307363616c655f696e666f35696d706c7335395f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c5024245250242447542439747970655f696e666f3137683039666261303039623031346435616345e702475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683331653463666338336463386461346345e8023d5f5a4e34636f726533707472323764726f705f696e5f706c616365244c5424245246247538244754243137683763366334653362613536333161666345e902355f5a4e34636f72653970616e69636b696e6731336173736572745f6661696c65643137683738326430306264303434626232383045ea02405f5a4e31307363686e6f72726b656c32377363616c61725f66726f6d5f63616e6f6e6963616c5f62797465733137683231626133323032643233383433323045eb02475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686538643166363137613534356536633645ec02495f5a4e34345f244c54242452462454247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d743137683263303135353537316533393461333845ed02495f5a4e34345f244c54242452462454247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d743137686436643630343036393461653862316345ee02305f5a4e34636f726533666d743557726974653977726974655f666d743137683731353738323431643336303761393145ef0293015f5a4e34636f72653370747231313264726f705f696e5f706c616365244c5424244c542473657264652e2e64652e2e57697468446563696d616c506f696e74247532302461732475323024636f72652e2e666d742e2e446973706c6179244754242e2e666d742e2e4c6f6f6b466f72446563696d616c506f696e74244754243137683735313030393237663835323937643445f002595f5a4e36305f244c542473657264652e2e64652e2e556e6578706563746564247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d743137683535646662386339623734333434306145f1025f5f5a4e36365f244c542473657264652e2e64652e2e57697468446563696d616c506f696e74247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d743137683965656534616530643961346539336445f2024c5f5a4e34375f244c54242452462473747224753230246173247532302473657264652e2e64652e2e45787065637465642447542433666d743137683133643066303835663963663039313145f302545f5a4e35355f244c542473657264652e2e64652e2e4f6e654f66247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d743137683630656365623964613839656431393045f402a4015f5a4e3132385f244c5424244c542473657264652e2e64652e2e57697468446563696d616c506f696e74247532302461732475323024636f72652e2e666d742e2e446973706c6179244754242e2e666d742e2e4c6f6f6b466f72446563696d616c506f696e74247532302461732475323024636f72652e2e666d742e2e5772697465244754243977726974655f7374723137686639653136666134313235353564636345f502a6015f5a4e3132385f244c5424244c542473657264652e2e64652e2e57697468446563696d616c506f696e74247532302461732475323024636f72652e2e666d742e2e446973706c6179244754242e2e666d742e2e4c6f6f6b466f72446563696d616c506f696e74247532302461732475323024636f72652e2e666d742e2e577269746524475424313077726974655f636861723137686463343231626533393535643266383945f602425f5a4e313073657264655f6a736f6e32646531325061727365724e756d6265723132696e76616c69645f747970653137683736623166383430383066383931663245f702475f5a4e34636f726533707472333764726f705f696e5f706c616365244c5424636f72652e2e666d742e2e4572726f72244754243137686339396332366261663035366662366645f8024c5f5a4e34636f726533707472343264726f705f696e5f706c616365244c5424616c6c6f632e2e737472696e672e2e537472696e67244754243137683761313336376238316230303530613145f902525f5a4e35335f244c5424636f72652e2e666d742e2e4572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686331333433306130306461396533623245fa02575f5a4e35385f244c5424616c6c6f632e2e737472696e672e2e537472696e67247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686435626234303238353735623832616645fb025f5f5a4e35385f244c5424616c6c6f632e2e737472696e672e2e537472696e67247532302461732475323024636f72652e2e666d742e2e577269746524475424313077726974655f636861723137683665623061383439373036393635376445fc025d5f5a4e35385f244c5424616c6c6f632e2e737472696e672e2e537472696e67247532302461732475323024636f72652e2e666d742e2e5772697465244754243977726974655f7374723137683030613666373066376361353163323545fd02325f5a4e313073657264655f6a736f6e356572726f72354572726f7232696f3137683739656331363261323531386161363545fe02605f5a4e36375f244c542473657264655f6a736f6e2e2e6572726f722e2e4572726f72436f6465247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d743137683065666234646363613436386530383245ff025c5f5a4e36335f244c542473657264655f6a736f6e2e2e6572726f722e2e4572726f72247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d74313768316663323462383763363932333434364580035a5f5a4e36315f244c542473657264655f6a736f6e2e2e6572726f722e2e4572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376831346131386137346336366438366530458103775f5a4e36315f244c542473657264655f6a736f6e2e2e6572726f722e2e4572726f7224753230246173247532302473657264652e2e64652e2e4572726f722447542436637573746f6d31376863616237346631363832643338353433452e6c6c766d2e31333431383836383837393433313630393432368203355f5a4e313073657264655f6a736f6e356572726f7231306d616b655f6572726f7231376865313361356437393433623563623636458303645f5a4e36315f244c542473657264655f6a736f6e2e2e6572726f722e2e4572726f7224753230246173247532302473657264652e2e64652e2e4572726f72244754243132696e76616c69645f7479706531376830613931643064626332326365343036458403655f5a4e37325f244c542473657264655f6a736f6e2e2e6572726f722e2e4a736f6e556e6578706563746564247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d7431376838663833663031313161623562383138458503655f5a4e36315f244c542473657264655f6a736f6e2e2e6572726f722e2e4572726f7224753230246173247532302473657264652e2e64652e2e4572726f72244754243133696e76616c69645f76616c756531376861383739623538386666663566323633458603375f5a4e313073657264655f6a736f6e337365723134696e76616c69645f6e756d626572313768646266333563643361333637376237624587035f5f5a4e313073657264655f6a736f6e347265616439536c696365526561643137706f736974696f6e5f6f665f696e64657831376833623736313464303635366238326134452e6c6c766d2e313430393032393737363337313432323534303788035c5f5a4e313073657264655f6a736f6e347265616439536c696365526561643134736b69705f746f5f65736361706531376837393532303930303231376665613639452e6c6c766d2e31343039303239373736333731343232353430378903475f5a4e313073657264655f6a736f6e347265616439536c696365526561643139736b69705f746f5f6573636170655f736c6f7731376865333631346237636530326431633435458a03695f5a4e37305f244c542473657264655f6a736f6e2e2e726561642e2e536c6963655265616424753230246173247532302473657264655f6a736f6e2e2e726561642e2e52656164244754243970617273655f73747231376835346165653164306261646265326437458b03725f5a4e37305f244c542473657264655f6a736f6e2e2e726561642e2e536c6963655265616424753230246173247532302473657264655f6a736f6e2e2e726561642e2e526561642447542431376465636f64655f6865785f65736361706531376830373739613934316163623332653234458c032e5f5a4e313073657264655f6a736f6e3472656164356572726f7231376862666162366537306165643663653863458d03355f5a4e313073657264655f6a736f6e347265616431317065656b5f6f725f656f6631376862393637366362366135633636346663458e036b5f5a4e37305f244c542473657264655f6a736f6e2e2e726561642e2e536c6963655265616424753230246173247532302473657264655f6a736f6e2e2e726561642e2e5265616424475424313069676e6f72655f73747231376861336661396633653430356665636335458f034c5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376866626563383933396537346633386362452e6c6c766d2e313030343030323937363239343334383732393390034c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f7075736831376831653066383863363731616361643662459103595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c6531376866376131666235343636396338356430459203305f5a4e34636f726533666d743557726974653977726974655f666d74313768643761313936356633656162333834644593034c5f5a4e34636f726533707472343264726f705f696e5f706c616365244c5424616c6c6f632e2e737472696e672e2e537472696e6724475424313768376131333637623831623030353061314594035f5f5a4e35385f244c5424616c6c6f632e2e737472696e672e2e537472696e67247532302461732475323024636f72652e2e666d742e2e577269746524475424313077726974655f63686172313768366562306138343937303639363537644595035d5f5a4e35385f244c5424616c6c6f632e2e737472696e672e2e537472696e67247532302461732475323024636f72652e2e666d742e2e5772697465244754243977726974655f73747231376830306136663730663763613531633235459603495f5a4e34345f244c54242452462454247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d7431376837663239616336653031666334353965459703495f5a4e34345f244c54242452462454247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d74313768666639623865323262363462373032324598035e5f5a4e36355f244c542473657264655f6a736f6e2e2e696f2e2e696d702e2e4572726f72247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d7431376862363139313461336363323963613730459903ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f6974657231376863373665373631353137653762663265459a03a6015f5a4e323173705f6170706c69636174696f6e5f63727970746f377372323535313933617070315f39345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f6170706c69636174696f6e5f63727970746f2e2e737232353531392e2e6170702e2e5075626c69632447542439747970655f696e666f31376837303664626431386233343538326432459b03a9015f5a4e323173705f6170706c69636174696f6e5f63727970746f377372323535313933617070315f39375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f6170706c69636174696f6e5f63727970746f2e2e737232353531392e2e6170702e2e5369676e61747572652447542439747970655f696e666f31376866326431653963646632386339303566459c034c5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376838303362643764616634353632636234452e6c6c766d2e31373439383834333437363732353639343532399d034c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f7075736831376837363234626230303465353236643366459e03595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c6531376835383539313937343665393331663663459f03a2015f5a4e323173705f6170706c69636174696f6e5f63727970746f35656364736133617070315f39325f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f6170706c69636174696f6e5f63727970746f2e2e65636473612e2e6170702e2e5075626c69632447542439747970655f696e666f3137686433366365323132363032633732396545a003a5015f5a4e323173705f6170706c69636174696f6e5f63727970746f35656364736133617070315f39355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f6170706c69636174696f6e5f63727970746f2e2e65636473612e2e6170702e2e5369676e61747572652447542439747970655f696e666f3137683839623433643931646431366639353545a103a6015f5a4e323173705f6170706c69636174696f6e5f63727970746f376564323535313933617070315f39345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f6170706c69636174696f6e5f63727970746f2e2e656432353531392e2e6170702e2e5075626c69632447542439747970655f696e666f3137683134613730393530386335343432366545a203a9015f5a4e323173705f6170706c69636174696f6e5f63727970746f376564323535313933617070315f39375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f6170706c69636174696f6e5f63727970746f2e2e656432353531392e2e6170702e2e5369676e61747572652447542439747970655f696e666f3137683737393332393236363038623561656345a3037b5f5a4e38385f244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432454244754242475323024617324753230247363616c655f696e666f2e2e54797065496e666f2447542439747970655f696e666f3137683434393932363236393530636130366245a4037b5f5a4e38385f244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432454244754242475323024617324753230247363616c655f696e666f2e2e54797065496e666f2447542439747970655f696e666f3137683438666664323862306638633837656145a5037b5f5a4e38385f244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432454244754242475323024617324753230247363616c655f696e666f2e2e54797065496e666f2447542439747970655f696e666f3137683537643438323638663839646562356545a6037b5f5a4e38385f244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432454244754242475323024617324753230247363616c655f696e666f2e2e54797065496e666f2447542439747970655f696e666f3137683939363631636239303364663663643145a70385015f5a4e313373705f61726974686d65746963315f38315f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f61726974686d657469632e2e41726974686d657469634572726f722447542439747970655f696e666f3137683165633739313933353365383535336445a803ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683031396664613763616466366136326145a903ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683131613736386137373564353335633245aa03ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683834633634656134623662343136633145ab034c5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376833303065316536323263386334333066452e6c6c766d2e3136313430383732343135383835373835373838ac034c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683232613634316461313338316135306345ad034c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683834373531646437643365393239613845ae03595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683036626233323163356536343439356245af03595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137686537373636396631313538333935316645b003595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137686665316264346630623264393134346645b10395015f5a4e313373705f61726974686d6574696331307065725f7468696e6773315f38355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f61726974686d657469632e2e7065725f7468696e67732e2e50657262696c6c2447542439747970655f696e666f3137683564633862343136306363373832636245b203ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683334636131333136653736393463306145b303ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683636613234623730646533353633313845b403af015f5a4e313773705f636f6e73656e7375735f61757261377372323535313931316170705f73723235353139315f39385f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f617572612e2e737232353531392e2e6170705f737232353531392e2e5075626c69632447542439747970655f696e666f3137686132356530323263373163313663613845b5034b5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376834646539666336353665663230333532452e6c6c766d2e35363434383134383035313531343631323136b6034c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683331306364326261306435643731616245b703595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683038656230313236643736313431666445b803595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683839306237383938396432616238353445b9037b5f5a4e38385f244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432454244754242475323024617324753230247363616c655f696e666f2e2e54797065496e666f2447542439747970655f696e666f3137683030303139613435616466333432306545ba034a5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376830353866643961363931663865663530452e6c6c766d2e373639353136303936333334333635353838bb034c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683065633136343536373565636138633945bc034c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137686330343564326339373839343337363245bd03595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683731626461626234363634616161323745be03595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137686534343135646637373265663133656345bf03595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137686539363639643230303534353535383145c00380015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137686239383864326263313435373163346145c10380015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137686534646537373138626665643962343845c203ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683134613131396537356531333937343645c303ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683536333139376135656565636339343645c403ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137686637663366353635303133353031653345c503ac015f5a4e35616c6c6f63337665633136696e5f706c6163655f636f6c6c6563743130385f244c5424696d706c2475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c54245424432449244754242475323024666f722475323024616c6c6f632e2e7665632e2e566563244c54245424475424244754243966726f6d5f697465723137686565653039623565643236323861303045c6038f015f5a4e313773705f636f6e73656e7375735f62616265315f38375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f626162652e2e42616265436f6e66696775726174696f6e2447542439747970655f696e666f3137683133303039336163653864383735316545c7038a015f5a4e313773705f636f6e73656e7375735f62616265315f38325f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f626162652e2e416c6c6f776564536c6f74732447542439747970655f696e666f3137686238653764313765363635353332636645c80394015f5a4e313773705f636f6e73656e7375735f62616265315f39325f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f626162652e2e4261626545706f6368436f6e66696775726174696f6e2447542439747970655f696e666f3137686235313062306430343536653134356645c90395015f5a4e313773705f636f6e73656e7375735f62616265315f39335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f626162652e2e4f70617175654b65794f776e65727368697050726f6f662447542439747970655f696e666f3137683233383430656335653734376364613145ca0383015f5a4e313773705f636f6e73656e7375735f62616265315f37355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f626162652e2e45706f63682447542439747970655f696e666f3137683233656166383833373030393964633445cb03755f5a4e31307363616c655f696e666f35696d706c7336345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c50244124432442245250242447542439747970655f696e666f3137683135303065376633396238373134333145cc038d015f5a4e313773705f636f6e73656e7375735f6261626533617070315f38315f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f626162652e2e6170702e2e5075626c69632447542439747970655f696e666f3137686230613764623135343632616538623645cd03755f5a4e31307363616c655f696e666f35696d706c7336345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c50244124432442245250242447542439747970655f696e666f3137683562323039393237313537313338303845ce037e5f5a4e31307363616c655f696e666f35696d706c7337335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024247535622454247533622424753230244e24753564242447542439747970655f696e666f3137686461326637613037306666366539353845cf039f015f5a4e313773705f636f6e73656e7375735f626162653764696765737473315f39355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f626162652e2e646967657374732e2e5072696d6172795072654469676573742447542439747970655f696e666f3137683864316135343232636665636263393145d003a7015f5a4e313773705f636f6e73656e7375735f626162653764696765737473315f3130325f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f626162652e2e646967657374732e2e5365636f6e64617279506c61696e5072654469676573742447542439747970655f696e666f3137683431366134656236626331396536333245d103a5015f5a4e313773705f636f6e73656e7375735f626162653764696765737473315f3130305f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f626162652e2e646967657374732e2e5365636f6e646172795652465072654469676573742447542439747970655f696e666f3137683538303435656338663162346665356345d20398015f5a4e313773705f636f6e73656e7375735f626162653764696765737473315f38385f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f626162652e2e646967657374732e2e5072654469676573742447542439747970655f696e666f3137683630316564356639306339666165633145d303a3015f5a4e313773705f636f6e73656e7375735f626162653764696765737473315f39395f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f626162652e2e646967657374732e2e4e657874436f6e66696744657363726970746f722447542439747970655f696e666f3137683064643965323734643561383565373345d403445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683162346266356466666437326436316445d503445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683964333231303561393462383030393445d603445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686330616161613965623834656532393345d703445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686462373064346162653466383735326445d8037b5f5a4e38385f244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432454244754242475323024617324753230247363616c655f696e666f2e2e54797065496e666f2447542439747970655f696e666f3137683339663139383531623739626436613545d9037b5f5a4e38385f244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432454244754242475323024617324753230247363616c655f696e666f2e2e54797065496e666f2447542439747970655f696e666f3137683765316430623430613062393563613945da037b5f5a4e38385f244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432454244754242475323024617324753230247363616c655f696e666f2e2e54797065496e666f2447542439747970655f696e666f3137686131373465393562333964376464643745db034b5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376838656538313264663439373636346330452e6c6c766d2e37353435323632393235353937383930363436dc034c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137686131336535626165396639313136636245dd0393015f5a4e323073705f636f6e73656e7375735f6772616e64706133617070315f38345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f6772616e6470612e2e6170702e2e5075626c69632447542439747970655f696e666f3137683834626335663166633962313461333545de0396015f5a4e323073705f636f6e73656e7375735f6772616e64706133617070315f38375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f6772616e6470612e2e6170702e2e5369676e61747572652447542439747970655f696e666f3137686338383834663633386564643536353745df0384015f5a4e313873705f636f6e73656e7375735f736c6f7473315f37355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f736c6f74732e2e536c6f742447542439747970655f696e666f3137686438396633373730363133323364356645e0038c015f5a4e313873705f636f6e73656e7375735f736c6f7473315f38335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f736c6f74732e2e536c6f744475726174696f6e2447542439747970655f696e666f3137683033313030636665356130646130633645e103325f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f773137683464336263376134326237643032353545e2034c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683663623963646462336162366264383345e3034c5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376864396563376434376531623537656634452e6c6c766d2e3138333531353337343636303635373439303230e4034c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683034396439386334363466613161343545e503595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683536306633613466623834303265336445e603595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683631356366383630613461313935616345e703595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137686363323065333331323561633264663645e8032f5f5a4e3773705f636f72653663727970746f3873733538686173683137683238663732393033333736326233623145e90381015f5a4e3773705f636f72653663727970746f315f37375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f72652e2e63727970746f2e2e4b65795479706549642447542439747970655f696e666f3137683238326237386366323163366636626245ea037e5f5a4e31307363616c655f696e666f35696d706c7337335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024247535622454247533622424753230244e24753564242447542439747970655f696e666f3137683539373733366138633763383264383245eb03775f5a4e3773705f636f7265315f37345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f72652e2e4f70617175654d657461646174612447542439747970655f696e666f3137686666386331393336303266393331626545ec036d5f5a4e3773705f636f7265315f36345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f72652e2e566f69642447542439747970655f696e666f3137686562363633306436313935373263623945ed038c015f5a4e3130345f244c54247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e436f6d70616374526566244c5424753332244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542439656e636f64655f746f3137686138636538626637613932616432303745ee036f5f5a4e37365f244c542473705f636f72652e2e737232353531392e2e7672662e2e5672665072654f75747075742475323024617324753230247363616c655f696e666f2e2e54797065496e666f2447542439747970655f696e666f3137683330393231376461393531323365383745ef036b5f5a4e37325f244c542473705f636f72652e2e737232353531392e2e7672662e2e56726650726f6f662475323024617324753230247363616c655f696e666f2e2e54797065496e666f2447542439747970655f696e666f3137686462376161383863653335323836666245f0038f015f5a4e3773705f636f7265377372323535313933767266315f38365f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f72652e2e737232353531392e2e7672662e2e5672665369676e61747572652447542439747970655f696e666f3137683731626661633661363630366337393845f1033f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137686635303235633330613364653762386145f20380015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137683038383839333532653864656537633845f303ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683434333763636231323833393330353645f403ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683631636138316334326465613433393745f503ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137686638626564613134626663393033626145f603735f5a4e31307363616c655f696e666f35696d706c7336325f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302424753562245424753564242447542439747970655f696e666f3137686233633762363061653337613236623445f703755f5a4e31307363616c655f696e666f35696d706c7336345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c50244124432442245250242447542439747970655f696e666f3137683165626536363035343934616533323545f80380015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137683362376336643634313061353731613545f9039e015f5a4e31307363616c655f696e666f35696d706c733130345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e42547265654d6170244c54244b24432456244754242447542439747970655f696e666f3137686466623936623836623632363963393145fa037e5f5a4e31307363616c655f696e666f35696d706c7337335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024247535622454247533622424753230244e24753564242447542439747970655f696e666f3137683330383333656637343465323132376145fb0380015f5a4e313273705f696e686572656e7473315f37375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f696e686572656e74732e2e496e686572656e74446174612447542439747970655f696e666f3137683163616437323962343236333439656445fc0388015f5a4e313273705f696e686572656e7473315f38355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f696e686572656e74732e2e436865636b496e686572656e7473526573756c742447542439747970655f696e666f3137686132313236633930353262626638633745fd034b5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376835393161623434303232623334316461452e6c6c766d2e34383433383033393637313038353834313131fe034c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137686235343063316530346631383764636345ff03465f5a4e31387061726974795f7363616c655f636f64656335636f64656331396465636f64655f7665635f776974685f6c656e31376836306666356365653137313431633963458004465f5a4e31387061726974795f7363616c655f636f64656335636f64656331396465636f64655f7665635f776974685f6c656e31376838663732633037343362303133386130458104465f5a4e31387061726974795f7363616c655f636f64656335636f64656331396465636f64655f7665635f776974685f6c656e31376866393836343039356430613034653063458204325f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f77313768343263353138373437363962376235394583044c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f70757368313768303435613061343463633239383838344584044c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f7075736831376832653166356261623839333864626662458504a4015f5a4e323073705f72756e74696d655f696e7465726661636535696d706c7339355f244c5424696d706c247532302473705f72756e74696d655f696e746572666163652e2e7761736d2e2e46726f6d46464956616c75652475323024666f722475323024616c6c6f632e2e7665632e2e566563244c5424542447542424475424313466726f6d5f6666695f76616c756531376831366666613936363234353661343866458604a4015f5a4e323073705f72756e74696d655f696e7465726661636535696d706c7339355f244c5424696d706c247532302473705f72756e74696d655f696e746572666163652e2e7761736d2e2e46726f6d46464956616c75652475323024666f722475323024616c6c6f632e2e7665632e2e566563244c5424542447542424475424313466726f6d5f6666695f76616c756531376834643265323865373339313062663361458704a4015f5a4e323073705f72756e74696d655f696e7465726661636535696d706c7339355f244c5424696d706c247532302473705f72756e74696d655f696e746572666163652e2e7761736d2e2e46726f6d46464956616c75652475323024666f722475323024616c6c6f632e2e7665632e2e566563244c5424542447542424475424313466726f6d5f6666695f76616c756531376837356436356566663239343833643631458804a4015f5a4e323073705f72756e74696d655f696e7465726661636535696d706c7339355f244c5424696d706c247532302473705f72756e74696d655f696e746572666163652e2e7761736d2e2e496e746f46464956616c75652475323024666f722475323024616c6c6f632e2e7665632e2e566563244c54245424475424244754243134696e746f5f6666695f76616c756531376838343361646339303639343433633366458904715f5a4e34636f726533707472353364726f705f696e5f706c616365244c54247061726974795f7363616c655f636f6465632e2e6572726f722e2e4572726f722447542431376866666232363230313462646130643736452e6c6c766d2e31323633373838373031343536303132353636358a047c5f5a4e36395f244c54247061726974795f7363616c655f636f6465632e2e6572726f722e2e4572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376863636562313832636338343631643463452e6c6c766d2e31323633373838373031343536303132353636358b04625f5a4e34345f244c54242452462454247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d7431376839633739396534343437343337346537452e6c6c766d2e333033343630303939323437323131313739338c04635f5a4e34355f244c5424244c502424525024247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376834303566303661613534323663666363452e6c6c766d2e333033343630303939323437323131313739338d04585f5a4e34636f726533707472323964726f705f696e5f706c616365244c5424244c5024245250242447542431376834396133376432383766346563623464452e6c6c766d2e333033343630303939323437323131313739338e04415f5a4e35616c6c6f6333666d7436666f726d617431376838346335363334623861393861353135452e6c6c766d2e333033343630303939323437323131313739338f04495f5a4e35627974657335627974657331317374617469635f64726f7031376864643031336537396536363530303731452e6c6c766d2e3330333436303039393234373231313137393390044a5f5a4e35627974657335627974657331327374617469635f636c6f6e6531376833323130386566643438323766363666452e6c6c766d2e3330333436303039393234373231313137393391044e5f5a4e35627974657335627974657331367374617469635f69735f756e6971756531376836633034666662366437323062666139452e6c6c766d2e3330333436303039393234373231313137393392047b5f5a4e36395f244c54247061726974795f7363616c655f636f6465632e2e6572726f722e2e4572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376863636562313832636338343631643463452e6c6c766d2e33303334363030393932343732313131373933930411727573745f626567696e5f756e77696e64940480015f5a4e3573705f696f315f38385f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f64652475323024666f72247532302473705f696f2e2e4b696c6c53746f72616765526573756c7424475424366465636f646531376836373232303238373163626666343261459504485f5a4e3573705f696f3773746f72616765323665787465726e5f686f73745f66756e6374696f6e5f696d706c7336617070656e6431376837656638376163633731303638616436459604475f5a4e3573705f696f3773746f72616765323665787465726e5f686f73745f66756e6374696f6e5f696d706c7335636c656172313768666135643861313862363730623336664597044f5f5a4e3573705f696f3773746f72616765323665787465726e5f686f73745f66756e6374696f6e5f696d706c733132636c6561725f70726566697831376837346164333366346133616366336662459804555f5a4e3573705f696f3773746f72616765323665787465726e5f686f73745f66756e6374696f6e5f696d706c733138636f6d6d69745f7472616e73616374696f6e31376832613739373661363561346634326539459904455f5a4e3573705f696f3773746f72616765323665787465726e5f686f73745f66756e6374696f6e5f696d706c733367657431376830643334386165303737646161623266459a044a5f5a4e3573705f696f3773746f72616765323665787465726e5f686f73745f66756e6374696f6e5f696d706c73386e6578745f6b657931376832313061623939663834653434353262459b04465f5a4e3573705f696f3773746f72616765323665787465726e5f686f73745f66756e6374696f6e5f696d706c73347265616431376839373039333561363633396438653766459c04575f5a4e3573705f696f3773746f72616765323665787465726e5f686f73745f66756e6374696f6e5f696d706c733230726f6c6c6261636b5f7472616e73616374696f6e31376862363132613934643264623564396632459d04465f5a4e3573705f696f3773746f72616765323665787465726e5f686f73745f66756e6374696f6e5f696d706c7334726f6f7431376835383939313164383561653631393035459e04455f5a4e3573705f696f3773746f72616765323665787465726e5f686f73745f66756e6374696f6e5f696d706c733373657431376838656233646237653834376262343737459f04545f5a4e3573705f696f3773746f72616765323665787465726e5f686f73745f66756e6374696f6e5f696d706c73313773746172745f7472616e73616374696f6e3137683165303531396566346636376130613845a0044d5f5a4e3573705f696f3768617368696e67323665787465726e5f686f73745f66756e6374696f6e5f696d706c733130626c616b65325f3132383137683361356361313931653563623962626645a1044d5f5a4e3573705f696f3768617368696e67323665787465726e5f686f73745f66756e6374696f6e5f696d706c733130626c616b65325f3235363137683161656336373863373636643165386545a2044a5f5a4e3573705f696f3768617368696e67323665787465726e5f686f73745f66756e6374696f6e5f696d706c733874776f785f3132383137686363383239356438653834663133646345a304495f5a4e3573705f696f3768617368696e67323665787465726e5f686f73745f66756e6374696f6e5f696d706c733774776f785f36343137686333646631316530373466653466366645a4044f5f5a4e3573705f696f31346f6666636861696e5f696e646578323665787465726e5f686f73745f66756e6374696f6e5f696d706c7335636c6561723137683531376237383864333164323830646445a5044d5f5a4e3573705f696f31346f6666636861696e5f696e646578323665787465726e5f686f73745f66756e6374696f6e5f696d706c73337365743137683130326561633064623336616530613445a604505f5a4e3573705f696f3663727970746f323665787465726e5f686f73745f66756e6374696f6e5f696d706c73313465636473615f67656e65726174653137686138323633393737323633616333316645a704535f5a4e3573705f696f3663727970746f323665787465726e5f686f73745f66756e6374696f6e5f696d706c73313765636473615f7075626c69635f6b6579733137683330623335653238356334363834663745a8044c5f5a4e3573705f696f3663727970746f323665787465726e5f686f73745f66756e6374696f6e5f696d706c73313065636473615f7369676e3137683238363963333661363234393537653245a9044e5f5a4e3573705f696f3663727970746f323665787465726e5f686f73745f66756e6374696f6e5f696d706c73313265636473615f7665726966793137683761633166346166333136616537323445aa04525f5a4e3573705f696f3663727970746f323665787465726e5f686f73745f66756e6374696f6e5f696d706c733136656432353531395f67656e65726174653137686561613135616130333439333934643845ab04555f5a4e3573705f696f3663727970746f323665787465726e5f686f73745f66756e6374696f6e5f696d706c733139656432353531395f7075626c69635f6b6579733137683266653032653161656164666332353645ac044e5f5a4e3573705f696f3663727970746f323665787465726e5f686f73745f66756e6374696f6e5f696d706c733132656432353531395f7369676e3137686534306433623164363061646539623445ad04505f5a4e3573705f696f3663727970746f323665787465726e5f686f73745f66756e6374696f6e5f696d706c733134656432353531395f7665726966793137686331303430653166613136353763366445ae04525f5a4e3573705f696f3663727970746f323665787465726e5f686f73745f66756e6374696f6e5f696d706c733136737232353531395f67656e65726174653137686633363835346633363931326139623545af04555f5a4e3573705f696f3663727970746f323665787465726e5f686f73745f66756e6374696f6e5f696d706c733139737232353531395f7075626c69635f6b6579733137683032626534646434303761346632343545b0044e5f5a4e3573705f696f3663727970746f323665787465726e5f686f73745f66756e6374696f6e5f696d706c733132737232353531395f7369676e3137683765333432313835613237363664663445b104505f5a4e3573705f696f3663727970746f323665787465726e5f686f73745f66756e6374696f6e5f696d706c733134737232353531395f7665726966793137686561346235363063623362346136323545b204565f5a4e3573705f696f386f6666636861696e323665787465726e5f686f73745f66756e6374696f6e5f696d706c7331387375626d69745f7472616e73616374696f6e3137686235333139653432336536323431636345b30486015f5a4e3130315f244c54247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e436f6d70616374244c5424753332244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f64653137683330383434316330633564633035646245b40485015f5a4e3130325f244c54247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e507265666978496e707574244c542454244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e496e7075742447542434726561643137686363393731323631663966623861386145b5048c015f5a4e3130345f244c54247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e436f6d70616374526566244c5424753332244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542439656e636f64655f746f3137686461333636383235366232303338373445b604525f5a4e3573705f696f31377472616e73616374696f6e5f696e646578323665787465726e5f686f73745f66756e6374696f6e5f696d706c7335696e6465783137683237356565323738356263626662366145b704455f5a4e3573705f696f376c6f6767696e67323665787465726e5f686f73745f66756e6374696f6e5f696d706c73336c6f673137683530363462323831636535306338626245b8044b5f5a4e3573705f696f376c6f6767696e67323665787465726e5f686f73745f66756e6374696f6e5f696d706c73396d61785f6c6576656c3137683039323133633762323931343533363745b9043f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137683736323532346232643062373662346145ba04735f5a4e38335f244c5424636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f64653137683035326462633961363633636161653145bb04735f5a4e38335f244c5424636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f64653137683434306165353537613438653862383345bc04735f5a4e38335f244c5424636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f64653137683734303966313861626165383464323245bd04735f5a4e38335f244c5424636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f64653137686435653566336132323136383164643345be04485f5a4e3573705f696f39616c6c6f6361746f72323665787465726e5f686f73745f66756e6374696f6e5f696d706c7334667265653137683462313034643330313832316462623545bf044a5f5a4e3573705f696f39616c6c6f6361746f72323665787465726e5f686f73745f66756e6374696f6e5f696d706c73366d616c6c6f633137683666313530336666343237336661656545c004555f5a4e3573705f696f323164656661756c745f6368696c645f73746f72616765323665787465726e5f686f73745f66756e6374696f6e5f696d706c7334726561643137683439393566306462633964306430616445c104545f5a4e3573705f696f323164656661756c745f6368696c645f73746f72616765323665787465726e5f686f73745f66756e6374696f6e5f696d706c73337365743137683362383564346135336134336334303945c204575f5a4e3573705f696f3474726965323665787465726e5f686f73745f66756e6374696f6e5f696d706c733233626c616b65325f3235365f6f7264657265645f726f6f743137686333303664626430363134643032366645c304485f5a4e3573705f696f346d697363323665787465726e5f686f73745f66756e6374696f6e5f696d706c73397072696e745f6865783137683336336330313365373139373831333745c4044a5f5a4e3573705f696f346d697363323665787465726e5f686f73745f66756e6374696f6e5f696d706c7331307072696e745f757466383137683537646631656164326338316661373345c5044f5f5a4e3573705f696f346d697363323665787465726e5f686f73745f66756e6374696f6e5f696d706c73313572756e74696d655f76657273696f6e3137686165636232366331386530636531333945c604ac015f5a4e35616c6c6f63337665633136696e5f706c6163655f636f6c6c6563743130385f244c5424696d706c2475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c54245424432449244754242475323024666f722475323024616c6c6f632e2e7665632e2e566563244c54245424475424244754243966726f6d5f697465723137683833376166333633613931373333313045c704ac015f5a4e35616c6c6f63337665633136696e5f706c6163655f636f6c6c6563743130385f244c5424696d706c2475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c54245424432449244754242475323024666f722475323024616c6c6f632e2e7665632e2e566563244c54245424475424244754243966726f6d5f697465723137686538646661303830643464363865656545c804ac015f5a4e35616c6c6f63337665633136696e5f706c6163655f636f6c6c6563743130385f244c5424696d706c2475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c54245424432449244754242475323024666f722475323024616c6c6f632e2e7665632e2e566563244c54245424475424244754243966726f6d5f697465723137686632643232323238336133313534656145c904ac015f5a4e35616c6c6f63337665633136696e5f706c6163655f636f6c6c6563743130385f244c5424696d706c2475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c54245424432449244754242475323024666f722475323024616c6c6f632e2e7665632e2e566563244c54245424475424244754243966726f6d5f697465723137686633666162393231656261653934346245ca04645f5a4e37305f244c5424616c6c6f632e2e7665632e2e566563244c5424542443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137683535376230353166643836343730386545cb04645f5a4e35616c6c6f633376656339696e746f5f697465723231496e746f49746572244c54245424432441244754243332666f726765745f616c6c6f636174696f6e5f64726f705f72656d61696e696e673137686338383365666364366162383666666645cc04745f5a4e38365f244c5424616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137683265356239323239646535393731616645cd04b7015f5a4e313473705f6d657461646174615f6972337631353133325f244c5424696d706c2475323024636f72652e2e636f6e766572742e2e46726f6d244c542473705f6d657461646174615f69722e2e74797065732e2e50616c6c65744d657461646174614952244754242475323024666f7224753230246672616d655f6d657461646174612e2e7631352e2e50616c6c65744d65746164617461244754243466726f6d3137686435623565326163663831396134656145ce04355f5a4e313473705f6d657461646174615f69723132696e746f5f76657273696f6e3137683530333863323061393035333164303345cf04b5015f5a4e313473705f6d657461646174615f6972337631343133305f244c5424696d706c2475323024636f72652e2e636f6e766572742e2e46726f6d244c542473705f6d657461646174615f69722e2e74797065732e2e4d657461646174614952244754242475323024666f7224753230246672616d655f6d657461646174612e2e7631342e2e52756e74696d654d65746164617461563134244754243466726f6d3137683139633931353936376261396537646445d004b7015f5a4e313473705f6d657461646174615f6972337631343133325f244c5424696d706c2475323024636f72652e2e636f6e766572742e2e46726f6d244c542473705f6d657461646174615f69722e2e74797065732e2e50616c6c65744d657461646174614952244754242475323024666f7224753230246672616d655f6d657461646174612e2e7631342e2e50616c6c65744d65746164617461244754243466726f6d3137683235643161303161373464633364376545d104c3015f5a4e313473705f6d657461646174615f6972337631343134345f244c5424696d706c2475323024636f72652e2e636f6e766572742e2e46726f6d244c542473705f6d657461646174615f69722e2e74797065732e2e53746f72616765456e7472794d657461646174614952244754242475323024666f7224753230246672616d655f6d657461646174612e2e7631342e2e53746f72616765456e7472794d65746164617461244754243466726f6d3137683665393739663030336133623038353245d204445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683035393961616461346535326437663845d304445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683062663263656631383766663830633645d404445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683138353132653333343430333030653045d504445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683333366537346564623830303865303245d604445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683533313530336365626338303836663345d704445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683635313738316139336339336634353945d804445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683765346636353631653535643638366545d904445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683765633739663066323433663237656345da04445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686339306638353136613862333939366545db04445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686438626361363832393365366333633945dc04445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686466653838373065376131303564653445dd04445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686563623833376463663535396361633745de044b5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376861663733353962356437623435323839452e6c6c766d2e34333231353138303532333134313138383432df044c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683162623765623034376138316563643845e0044c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683837623334663636646631336262613945e1044c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137686532323132633735613132313133353545e204595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683130343866396235333133393238653045e304595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683732653938653237396332383863636445e404595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683763613137613266353966333330393145e504595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137686637366361333633666662336364303445e6043f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137686430623530613164316662636439366545e70480015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137683963343130303562646137393163663245e80480015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137686434663738393338366431316435663145e90480015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137686666646231383766386365336539666645ea04ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683762313739636335376661336662646445eb04ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137686132376664646630313937323438346245ec04ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137686135353530333538646261623334343645ed0480015f5a4e39365f244c542473705f72756e74696d652e2e67656e657269632e2e6469676573742e2e4469676573744974656d5265662475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542436656e636f64653137683232366335316666316535343838633045ee04735f5a4e38305f244c542473705f72756e74696d652e2e67656e657269632e2e6469676573742e2e4469676573744974656d2475323024617324753230247363616c655f696e666f2e2e54797065496e666f2447542439747970655f696e666f3137686337653238613663633730346662396145ef0480015f5a4e39305f244c542473705f72756e74696d652e2e67656e657269632e2e6469676573742e2e4469676573744974656d24753230246173247532302473705f72756e74696d652e2e7472616974732e2e436865636b457175616c244754243131636865636b5f657175616c3137686636313865623538613532373037323145f00496015f5a4e313073705f72756e74696d653767656e6572696336646967657374315f38365f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f72756e74696d652e2e67656e657269632e2e6469676573742e2e4469676573742447542439747970655f696e666f3137686664613539666339343334386235633445f1047e5f5a4e31307363616c655f696e666f35696d706c7337335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024247535622454247533622424753230244e24753564242447542439747970655f696e666f3137683465336538303364333632356639363845f204495f5a4e34345f244c54242452462454247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d743137683062316134646338383337653665333645f304325f5a4e34636f726533666d74355772697465313077726974655f636861723137686139333364376565343564306537363145f404305f5a4e34636f726533666d743557726974653977726974655f666d743137686635363931643462346464346563383645f504455f5a4e34636f726533707472333564726f705f696e5f706c616365244c542473705f7374642e2e577269746572244754243137683833376161323330313165393934653945f604795f5a4e34636f726533707472363264726f705f696e5f706c616365244c542473705f72756e74696d652e2e72756e74696d655f6c6f676765722e2e52756e74696d654c6f676765722447542431376865373335343064313163313234303131452e6c6c766d2e35373635393736363831363435383336383537f70480015f5a4e37305f244c542473705f72756e74696d652e2e72756e74696d655f6c6f676765722e2e52756e74696d654c6f676765722475323024617324753230246c6f672e2e4c6f672447542437656e61626c656431376834313132633135656262343364636134452e6c6c766d2e35373635393736363831363435383336383537f804635f5a4e37305f244c542473705f72756e74696d652e2e72756e74696d655f6c6f676765722e2e52756e74696d654c6f676765722475323024617324753230246c6f672e2e4c6f6724475424336c6f673137686631323665323333336165316132396545f9047e5f5a4e37305f244c542473705f72756e74696d652e2e72756e74696d655f6c6f676765722e2e52756e74696d654c6f676765722475323024617324753230246c6f672e2e4c6f672447542435666c75736831376862656333666262366235663265646233452e6c6c766d2e35373635393736363831363435383336383537fa04755f5a4e38325f244c542473705f72756e74696d652e2e72756e74696d655f737472696e672e2e52756e74696d65537472696e672475323024617324753230247363616c655f696e666f2e2e54797065496e666f2447542439747970655f696e666f3137686562633832343165323336326264373145fb048c015f5a4e3130345f244c54247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e436f6d70616374526566244c5424753332244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542439656e636f64655f746f3137683436326130393931646164633165656445fc04bc015f5a4e313073705f72756e74696d6532307472616e73616374696f6e5f76616c69646974793132335f244c5424696d706c2475323024636f72652e2e636f6e766572742e2e46726f6d244c542473705f72756e74696d652e2e7472616e73616374696f6e5f76616c69646974792e2e5472616e73616374696f6e56616c69646974794572726f72244754242475323024666f72247532302424524624737472244754243466726f6d3137683532356261386464636366316662323245fd04595f5a4e313073705f72756e74696d6532307472616e73616374696f6e5f76616c6964697479313656616c69645472616e73616374696f6e3132636f6d62696e655f776974683137686233656130333266636134636436363945fe04af015f5a4e313073705f72756e74696d6532307472616e73616374696f6e5f76616c6964697479315f3130335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f72756e74696d652e2e7472616e73616374696f6e5f76616c69646974792e2e496e76616c69645472616e73616374696f6e2447542439747970655f696e666f3137683461343164633436303933623039343345ff04af015f5a4e313073705f72756e74696d6532307472616e73616374696f6e5f76616c6964697479315f3130335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f72756e74696d652e2e7472616e73616374696f6e5f76616c69646974792e2e556e6b6e6f776e5472616e73616374696f6e2447542439747970655f696e666f31376837316664613035633163376365386433458005b5015f5a4e313073705f72756e74696d6532307472616e73616374696f6e5f76616c6964697479315f3130395f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f72756e74696d652e2e7472616e73616374696f6e5f76616c69646974792e2e5472616e73616374696f6e56616c69646974794572726f722447542439747970655f696e666f31376866313332313565646631353639333066458105ae015f5a4e313073705f72756e74696d6532307472616e73616374696f6e5f76616c6964697479315f3130325f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f72756e74696d652e2e7472616e73616374696f6e5f76616c69646974792e2e5472616e73616374696f6e536f757263652447542439747970655f696e666f31376861323132383937646166376266363464458205ad015f5a4e313073705f72756e74696d6532307472616e73616374696f6e5f76616c6964697479315f3130315f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f72756e74696d652e2e7472616e73616374696f6e5f76616c69646974792e2e56616c69645472616e73616374696f6e2447542439747970655f696e666f313768346135333331333032613731346464374583057b5f5a4e313073705f72756e74696d65315f37345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f72756e74696d652e2e4d6f64756c654572726f722447542439747970655f696e666f3137686633356435363866663538633436626545840582015f5a4e313073705f72756e74696d65315f38315f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f72756e74696d652e2e5472616e73616374696f6e616c4572726f722447542439747970655f696e666f313768626233306636643963613232396436614585057d5f5a4e313073705f72756e74696d65315f37365f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f72756e74696d652e2e44697370617463684572726f722447542439747970655f696e666f313768326539636666636439663338356531394586057a5f5a4e313073705f72756e74696d65315f37335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f72756e74696d652e2e546f6b656e4572726f722447542439747970655f696e666f3137686638633139353666613738383566646445870586015f5a4e313073705f72756e74696d65315f38355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f72756e74696d652e2e45787472696e736963496e636c7573696f6e4d6f64652447542439747970655f696e666f313768613062393265373430613039626461624588057b5f5a4e313073705f72756e74696d65315f37345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f72756e74696d652e2e4f706171756556616c75652447542439747970655f696e666f313768353135613532643361346434623437634589059c025f5a4e35616c6c6f633131636f6c6c656374696f6e73356274726565346e6f646532313048616e646c65244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e4e6f6465526566244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4d75742443244b24432456244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4c65616624475424244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e45646765244754243136696e736572745f726563757273696e6731376862393130613235323366636336323532458a059c025f5a4e35616c6c6f633131636f6c6c656374696f6e73356274726565346e6f646532313048616e646c65244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e4e6f6465526566244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4d75742443244b24432456244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4c65616624475424244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e45646765244754243136696e736572745f726563757273696e6731376865353939333832616639313063653565458b054b5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376865636165663963333333323162356439452e6c6c766d2e353633363233393834323730393533353139358c054c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f7075736831376834633435393662396662353838666662458d05595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c6531376864666430653266323034313436333362458e054c5f5a4e313673705f73746174655f6d616368696e6533657874313353746f72616765417070656e6431347265706c6163655f6c656e67746831376831623663363864323864626462373966458f059e015f5a4e313673705f73746174655f6d616368696e6531376f7665726c617965645f6368616e676573396368616e676573657438324f7665726c61796564456e747279244c542473705f73746174655f6d616368696e652e2e6f7665726c617965645f6368616e6765732e2e6368616e67657365742e2e53746f72616765456e747279244754243373657431376865646538393361333639336165363366459005b9015f5a4e313673705f73746174655f6d616368696e6531376f7665726c617965645f6368616e676573396368616e67657365743130384f7665726c617965644d6170244c5424616c6c6f632e2e7665632e2e566563244c542475382447542424432473705f73746174655f6d616368696e652e2e6f7665726c617965645f6368616e6765732e2e6368616e67657365742e2e53746f72616765456e7472792447542433736574313768303632636362613763373566353230624591056f5f5a4e38315f244c5424616c6c6f632e2e7665632e2e73706c6963652e2e53706c696365244c5424492443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f70313768336531323765303239373764666538344592053f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f646531376831313637396536386239336435326137459305475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376835633038353733623131643266623333459405475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d74313768643734653131653337373461366332334595055e5f5a4e34636f726533666d74336e756d35325f244c5424696d706c2475323024636f72652e2e666d742e2e44656275672475323024666f7224753230247573697a652447542433666d74313768646464643336383865643561366332634596053c5f5a4e34636f726533707472323664726f705f696e5f706c616365244c54247573697a652447542431376865326239626139343666313039393164459705565f5a4e35616c6c6f633131636f6c6c656374696f6e73356274726565336d6170323542547265654d6170244c54244b24432456244324412447542436696e73657274313768396135373066346631303166633138354598056d5f5a4e34636f726533707472343964726f705f696e5f706c616365244c5424736d616c6c7665632e2e436f6c6c656374696f6e416c6c6f634572722447542431376839323766636536623831336632303330452e6c6c766d2e31373930353630373237323133373537303737309905785f5a4e36355f244c5424736d616c6c7665632e2e436f6c6c656374696f6e416c6c6f63457272247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376833333136353566633965353230393634452e6c6c766d2e31373930353630373237323133373537303737309a054a5f5a4e38736d616c6c7665633137536d616c6c566563244c542441244754243231726573657276655f6f6e655f756e636865636b656431376864303333643431383135396466623731459b054b5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376835646634376333623866393962643264452e6c6c766d2e323037323134373132343138373337383334369c05725f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c6531376866376634363230363333383535363865452e6c6c766d2e323037323134373132343138373337383334369d05565f5a4e35315f244c542473705f7374642e2e577269746572247532302461732475323024636f72652e2e666d742e2e5772697465244754243977726974655f73747231376838306237346666643465623161613832459e054b5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376862386530376363663762666537303564452e6c6c766d2e393537383839383230323436313034313534349f05725f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c6531376864333737366138356664323136613963452e6c6c766d2e39353738383938323032343631303431353434a005435f5a4e313073705f73746f72616765394368696c64496e666f323070726566697865645f73746f726167655f6b65793137683461386161343463313630313733316145a10593015f5a4e3773705f74726965313373746f726167655f70726f6f66315f38375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f747269652e2e73746f726167655f70726f6f662e2e53746f7261676550726f6f662447542439747970655f696e666f3137686237663535333934356338383337333845a205735f5a4e31307363616c655f696e666f35696d706c7336325f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302424753562245424753564242447542439747970655f696e666f3137686265373866346237326364333438633045a30580015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137683764623765623536653135653037353145a405ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683566383362333739363661303639643345a505ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683861653132363930336533393531363745a60587015f5a4e39375f244c5424616c6c6f632e2e7665632e2e566563244c5424542443244124475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f657874656e642e2e53706563457874656e64244c5424542443244924475424244754243131737065635f657874656e643137683765623365623531616535386133343445a7054c5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376832363063323631656562393639646465452e6c6c766d2e3130393530333139383533363038363833313939a8054c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683937646534376331643466636466383645a9054c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683966353264663566653732646438323745aa05595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683764303935616637613231663465393345ab05595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683963313562353162666465663336323545ac05595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137686335333864616432616463643132386545ad058c015f5a4e3130345f244c54247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e436f6d70616374526566244c5424753332244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542439656e636f64655f746f3137683632313236633362643133346332343845ae059a015f5a4e31307363616c655f696e666f35696d706c733130305f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e7365742e2e4254726565536574244c542454244754242447542439747970655f696e666f3137686562383831333432316663396532396445af05705f5a4e37345f244c542473705f747269652e2e747269655f73747265616d2e2e5472696553747265616d247532302461732475323024747269655f726f6f742e2e5472696553747265616d244754243131617070656e645f6c6561663137686236666565333662663936383537333545b005735f5a4e31307363616c655f696e666f35696d706c7336325f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302424753562245424753564242447542439747970655f696e666f3137683238323863636265636637383533376145b105755f5a4e31307363616c655f696e666f35696d706c7336345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c50244124432442245250242447542439747970655f696e666f3137683538356366373361353437336132356545b2057e5f5a4e31307363616c655f696e666f35696d706c7337335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024247535622454247533622424753230244e24753564242447542439747970655f696e666f3137686235333833336264636439306539373045b30583015f5a4e31307363616c655f696e666f35696d706c7337385f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e626f72726f772e2e436f77244c542454244754242447542439747970655f696e666f3137686535393730613739333139613339653345b4057e5f5a4e313073705f76657273696f6e315f37375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f76657273696f6e2e2e52756e74696d6556657273696f6e2447542439747970655f696e666f3137683464316230303235646636323439353345b5054b5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376830656330653335393635356435646662452e6c6c766d2e39353132323739313539363038393431383337b6054c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683833303166623233626361323861313545b70595015f5a4e31307363616c655f696e666f35696d706c7339365f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e436f6d70616374244c542454244754242447542439747970655f696e666f3137686135323839303362656338396664626345b805615f5a4e36385f244c542473705f776569676874732e2e7765696768745f76322e2e576569676874247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d743137683337343865366138326132313838616145b9058b015f5a4e313073705f77656967687473397765696768745f7632315f38305f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f776569676874732e2e7765696768745f76322e2e5765696768742447542439747970655f696e666f3137683338626562633562636662643865656445ba057f5f5a4e313073705f77656967687473315f37385f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f776569676874732e2e52756e74696d6544625765696768742447542439747970655f696e666f3137686439326334626438343632343235376545bb05655f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f7075736831376837306638626536663361633732663732452e6c6c766d2e33373830303837383837373935313935353433bc054b5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376839313930633465383236343363353736452e6c6c766d2e33373830303837383837373935313935353433bd059c025f5a4e35616c6c6f633131636f6c6c656374696f6e73356274726565346e6f646532313048616e646c65244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e4e6f6465526566244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4d75742443244b24432456244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4c65616624475424244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e45646765244754243136696e736572745f726563757273696e673137683131346363653465333666643031366245be059c025f5a4e35616c6c6f633131636f6c6c656374696f6e73356274726565346e6f646532313048616e646c65244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e4e6f6465526566244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4d75742443244b24432456244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4c65616624475424244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e45646765244754243136696e736572745f726563757273696e673137683535613734356536616362656331636145bf059c025f5a4e35616c6c6f633131636f6c6c656374696f6e73356274726565346e6f646532313048616e646c65244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e4e6f6465526566244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4d75742443244b24432456244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4c65616624475424244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e45646765244754243136696e736572745f726563757273696e673137683637346639303866313638346139316545c0059c025f5a4e35616c6c6f633131636f6c6c656374696f6e73356274726565346e6f646532313048616e646c65244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e4e6f6465526566244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4d75742443244b24432456244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4c65616624475424244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e45646765244754243136696e736572745f726563757273696e673137686662326364633663356132326131623845c105f6015f5a4e35616c6c6f633131636f6c6c656374696f6e7335627472656536617070656e643137385f244c5424696d706c2475323024616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e4e6f6465526566244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4f776e65642443244b24432456244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4c6561664f72496e7465726e616c24475424244754243962756c6b5f707573683137683338353663666564353630396262666345c205f6015f5a4e35616c6c6f633131636f6c6c656374696f6e7335627472656536617070656e643137385f244c5424696d706c2475323024616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e4e6f6465526566244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4f776e65642443244b24432456244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4c6561664f72496e7465726e616c24475424244754243962756c6b5f707573683137683538356539373534353336373463353845c305f6015f5a4e35616c6c6f633131636f6c6c656374696f6e7335627472656536617070656e643137385f244c5424696d706c2475323024616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e4e6f6465526566244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4f776e65642443244b24432456244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4c6561664f72496e7465726e616c24475424244754243962756c6b5f707573683137686436653862316533346431613962333345c405d5015f5a4e35616c6c6f633131636f6c6c656374696f6e73356274726565367365617263683134325f244c5424696d706c2475323024616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e4e6f6465526566244c5424426f72726f77547970652443244b24432456244324616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6e6f64652e2e6d61726b65722e2e4c6561664f72496e7465726e616c244754242447542431317365617263685f747265653137683531336161373762313636313439306445c505465f5a4e31387061726974795f7363616c655f636f64656335636f64656331396465636f64655f7665635f776974685f6c656e3137683033326465623466626639643034313845c605465f5a4e31387061726974795f7363616c655f636f64656335636f64656331396465636f64655f7665635f776974685f6c656e3137683430623731346230393362343862353145c705465f5a4e31387061726974795f7363616c655f636f64656335636f64656331396465636f64655f7665635f776974685f6c656e3137683433343537373131666630343138353145c805465f5a4e31387061726974795f7363616c655f636f64656335636f64656331396465636f64655f7665635f776974685f6c656e3137683436353837333462633934643931663645c905465f5a4e31387061726974795f7363616c655f636f64656335636f64656331396465636f64655f7665635f776974685f6c656e3137683562396562383632326538613761653045ca05465f5a4e31387061726974795f7363616c655f636f64656335636f64656331396465636f64655f7665635f776974685f6c656e3137683636336165383130313166633865363045cb05465f5a4e31387061726974795f7363616c655f636f64656335636f64656331396465636f64655f7665635f776974685f6c656e3137683733363938323065356235656134626145cc05465f5a4e31387061726974795f7363616c655f636f64656335636f64656331396465636f64655f7665635f776974685f6c656e3137683862306663356664633339613963323745cd05465f5a4e31387061726974795f7363616c655f636f64656335636f64656331396465636f64655f7665635f776974685f6c656e3137683863626533363332336633643262663845ce05465f5a4e31387061726974795f7363616c655f636f64656335636f64656331396465636f64655f7665635f776974685f6c656e3137686133303066643436643138343932393045cf05465f5a4e31387061726974795f7363616c655f636f64656335636f64656331396465636f64655f7665635f776974685f6c656e3137686261616138653232626364623433663345d005465f5a4e31387061726974795f7363616c655f636f64656335636f64656331396465636f64655f7665635f776974685f6c656e3137686364663666666238396338663766356445d105465f5a4e31387061726974795f7363616c655f636f64656335636f64656331396465636f64655f7665635f776974685f6c656e3137686430346565356330363436623266366645d205465f5a4e31387061726974795f7363616c655f636f64656335636f6465633139656e636f64655f736c6963655f6e6f5f6c656e3137683632643066363338383937373264373945d305465f5a4e31387061726974795f7363616c655f636f64656335636f6465633139656e636f64655f736c6963655f6e6f5f6c656e3137683761653038663635653035633738316245d405465f5a4e31387061726974795f7363616c655f636f64656335636f6465633139656e636f64655f736c6963655f6e6f5f6c656e3137686333323532313665323039353066616245d505475f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646531337573696e675f656e636f6465643137686236366637636233353130373531366645d6053f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137683466623661326265326564323037303245d7053f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137683561323865373533363739613734316245d8053f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137686136383437383761393133336666313145d9057c5f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c2447542436636f6d6d697432385f24753762242475376224636c6f737572652475376424247537642431376839323133383435383330616537633739452e6c6c766d2e3132353235333233373335393830383932393830da0583015f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c244754243132636f6d6d69745f6368696c6432385f24753762242475376224636c6f737572652475376424247537642431376839666630616538376464333130656261452e6c6c766d2e3132353235333233373335393830383932393830db057c5f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c2447542436636f6d6d697432385f24753762242475376224636c6f737572652475376424247537642431376837616231363132313865613535356238452e6c6c766d2e3132353235333233373335393830383932393830dc0583015f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c244754243132636f6d6d69745f6368696c6432385f24753762242475376224636c6f737572652475376424247537642431376865303664303766653561666435643961452e6c6c766d2e3132353235333233373335393830383932393830dd055a5f5a4e34636f7265336f70733866756e6374696f6e36466e4f6e6365343063616c6c5f6f6e636524753762242475376224767461626c652e7368696d247537642424753764243137683261333338386662376137623531303045de05695f5a4e34636f726533707472373164726f705f696e5f706c616365244c542473705f747269652e2e6572726f722e2e4572726f72244c54247072696d69746976655f74797065732e2e4832353624475424244754243137683461363365306537646330353230356545df05b5015f5a4e34636f72653370747231343664726f705f696e5f706c616365244c5424616c6c6f632e2e626f7865642e2e426f78244c5424747269655f64622e2e547269654572726f72244c54247072696d69746976655f74797065732e2e4832353624432473705f747269652e2e6572726f722e2e4572726f72244c54247072696d69746976655f74797065732e2e48323536244754242447542424475424244754243137683666333363313066386139323431346645e0055a5f5a4e34636f7265336f70733866756e6374696f6e36466e4f6e6365343063616c6c5f6f6e636524753762242475376224767461626c652e7368696d247537642424753764243137683336633066636239383763373161396445e1055a5f5a4e34636f7265336f70733866756e6374696f6e36466e4f6e6365343063616c6c5f6f6e636524753762242475376224767461626c652e7368696d247537642424753764243137686532323961626532316333623261356445e2053a5f5a4e34636f7265336f70733866756e6374696f6e36466e4f6e63653963616c6c5f6f6e63653137683062363033303564376532626538373645e30590015f5a4e34636f72653370747231303964726f705f696e5f706c616365244c5424747269655f64622e2e7472696564626d75742e2e4e6f6465244c542473705f747269652e2e4c61796f75745630244c542473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362447542424475424244754243137683636656262373231373030326234306345e40591015f5a4e34636f72653370747231313064726f705f696e5f706c616365244c5424747269655f64622e2e7472696564626d75742e2e56616c7565244c542473705f747269652e2e4c61796f75745630244c542473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362447542424475424244754243137683666346661333363616131323934373645e505ad015f5a4e34636f72653370747231333864726f705f696e5f706c616365244c5424636f72652e2e6f7074696f6e2e2e4f7074696f6e244c5424747269655f64622e2e7472696564626d75742e2e56616c7565244c542473705f747269652e2e4c61796f75745630244c542473705f72756e74696d652e2e7472616974732e2e426c616b6554776f323536244754242447542424475424244754243137686464616263386632333534303430633845e605be015f5a4e34636f72653370747231353564726f705f696e5f706c616365244c5424747269655f64622e2e7472696564626d75742e2e5472696544424d7574244c542473705f747269652e2e4c61796f75745630244c542473705f72756e74696d652e2e7472616974732e2e426c616b6554776f32353624475424244754242e2e63616368655f6e6f64652e2e24753762242475376224636c6f7375726524753764242475376424244754243137683539613639613664623061313439336145e705675f5a4e37355f244c5424747269655f64622e2e7472696564626d75742e2e56616c7565244c54244c24475424247532302461732475323024636f72652e2e636d702e2e5061727469616c4571244754243265713137683236343763396361653063666366353045e805465f5a4e37747269655f6462397472696564626d757431334e6f6465244c54244c24475424313266726f6d5f656e636f6465643137683466333032653430336637666465366245e905485f5a4e37747269655f6462397472696564626d757431334e6f6465244c54244c244754243134696e6c696e655f6f725f686173683137683966343064393064353534373032653845ea05645f5a4e37747269655f6462397472696564626d757431334e6f6465244c54244c24475424313266726f6d5f656e636f64656432385f24753762242475376224636c6f73757265247537642424753764243137683463653136343861653864643537643345eb05645f5a4e37747269655f6462397472696564626d757431334e6f6465244c54244c24475424313266726f6d5f656e636f64656432385f24753762242475376224636c6f73757265247537642424753764243137683031353833363735633234623664396245ec05465f5a4e37747269655f6462397472696564626d757431334e6f6465244c54244c24475424313266726f6d5f656e636f6465643137683733363436313237333437646366346645ed05485f5a4e37747269655f6462397472696564626d757431334e6f6465244c54244c244754243134696e6c696e655f6f725f686173683137683236326661346532303234666261343945ee05645f5a4e37747269655f6462397472696564626d757431334e6f6465244c54244c24475424313266726f6d5f656e636f64656432385f24753762242475376224636c6f73757265247537642424753764243137686238646465343838386132323265623145ef05645f5a4e37747269655f6462397472696564626d757431334e6f6465244c54244c24475424313266726f6d5f656e636f64656432385f24753762242475376224636c6f73757265247537642424753764243137686632363430653862336432643938316445f005495f5a4e37747269655f6462397472696564626d757431334e6f6465244c54244c24475424313566726f6d5f6e6f64655f6f776e65643137683061643664363230323366633034636445f1054e5f5a4e37747269655f6462397472696564626d757431334e6f6465244c54244c244754243230696e6c696e655f6f725f686173685f6f776e65643137683837313032653665336539653334323445f205495f5a4e37747269655f6462397472696564626d757431334e6f6465244c54244c24475424313566726f6d5f6e6f64655f6f776e65643137686162323033316433633638656362333445f3054e5f5a4e37747269655f6462397472696564626d757431334e6f6465244c54244c244754243230696e6c696e655f6f725f686173685f6f776e65643137686531636463386364373731386636373845f405475f5a4e37747269655f6462397472696564626d7574313456616c7565244c54244c244754243132696e746f5f656e636f6465643137683336396365383462323631626161626645f505475f5a4e37747269655f6462397472696564626d7574313456616c7565244c54244c244754243132696e746f5f656e636f6465643137683538643432616639363930313863316345f605475f5a4e37747269655f6462397472696564626d7574313456616c7565244c54244c244754243132696e746f5f656e636f6465643137683561613262393238613735333966643445f705475f5a4e37747269655f6462397472696564626d7574313456616c7565244c54244c244754243132696e746f5f656e636f6465643137686538376363653739363963336634313745f805495f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c24475424313063616368655f6e6f64653137686162353930313831623263353964656145f905495f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c24475424313063616368655f6e6f64653137686663386432393566666137333561323145fa05675f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c24475424313063616368655f6e6f646532385f24753762242475376224636c6f73757265247537642424753764243137683265663263623537366430643733386145fb05675f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c24475424313063616368655f6e6f646532385f24753762242475376224636c6f73757265247537642424753764243137683337636262633562383862303162353545fc05645f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c24475424313163616368655f76616c756531376837313233373464653531363639303734452e6c6c766d2e3132353235333233373335393830383932393830fd05645f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c24475424313163616368655f76616c756531376866363836353265646465326565656533452e6c6c766d2e3132353235333233373335393830383932393830fe05685f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c24475424313163616368655f76616c756532385f24753762242475376224636c6f73757265247537642424753764243137683037323834356562616665386165313345ff05655f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c244754243132636f6d6d69745f6368696c6431376830306632306263363037383764613466452e6c6c766d2e31323532353332333733353938303839323938308006655f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c244754243132636f6d6d69745f6368696c6431376838383664646632303934613566336332452e6c6c766d2e313235323533323337333539383038393239383081064f5f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c244754243136696e736572745f696e73706563746f7231376836366239646534333233626166333133458206615f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c2447542439696e736572745f617431376838636334643565343562653932623463452e6c6c766d2e31323532353332333733353938303839323938308306505f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c2447542431377265706c6163655f6f6c645f76616c7565313768333939303033613033626133346136614584064f5f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c244754243136696e736572745f696e73706563746f7231376862633336326561346630373438316335458506615f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c2447542439696e736572745f617431376832653364316163643233663632346265452e6c6c766d2e31323532353332333733353938303839323938308606435f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c2447542435636163686531376833663462363761383336366439353332458706435f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c2447542435636163686531376837626630383935356361643930646233458806475f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c24475424396669785f696e6e657231376831313930393666356261646130343139458906475f5a4e37747269655f6462397472696564626d757432304e6f646553746f72616765244c54244c244754243764657374726f7931376830323961386361326564336430356130458a06475f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c24475424396669785f696e6e657231376832343966613334316432623030613265458b06475f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c244754243972656d6f76655f617431376839656233356565363964636565323431458c06655f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c244754243972656d6f76655f617432385f24753762242475376224636c6f737572652475376424247537642431376839393937613630346436633430326131458d06475f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c244754243972656d6f76655f617431376866316265663065343338656636363937458e06655f5a4e37747269655f6462397472696564626d757431385472696544424d7574244c54244c244754243972656d6f76655f617432385f24753762242475376224636c6f737572652475376424247537642431376833333462383665633563373463656266458f066e5f5a4e38305f244c5424747269655f64622e2e7472696564626d75742e2e5472696544424d7574244c54244c24475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f70313768303964313966653730363430303536614590066e5f5a4e38305f244c5424747269655f64622e2e7472696564626d75742e2e5472696544424d7574244c54244c24475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f7031376832316434383430366465626333383034459106745f5a4e38345f244c5424747269655f64622e2e7472696564626d75742e2e5472696544424d7574244c54244c24475424247532302461732475323024747269655f64622e2e547269654d7574244c54244c244754242447542436696e7365727431376830623661623133663239343937303336459206745f5a4e38345f244c5424747269655f64622e2e7472696564626d75742e2e5472696544424d7574244c54244c24475424247532302461732475323024747269655f64622e2e547269654d7574244c54244c244754242447542436696e7365727431376839373065633661633839393362643037459306745f5a4e38345f244c5424747269655f64622e2e7472696564626d75742e2e5472696544424d7574244c54244c24475424247532302461732475323024747269655f64622e2e547269654d7574244c54244c24475424244754243672656d6f766531376835653539306337323764653036313034459406745f5a4e38345f244c5424747269655f64622e2e7472696564626d75742e2e5472696544424d7574244c54244c24475424247532302461732475323024747269655f64622e2e547269654d7574244c54244c24475424244754243672656d6f7665313768393535313039363030336432376539324595064b5f5a4e35616c6c6f63377261775f766563313166696e6973685f67726f7731376830313337653736373364626637396536452e6c6c766d2e373231323330373831363737383531343238329606475f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243131616c6c6f636174655f696e313768363630626337313239343632363531304597064c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f70757368313768306162373132643039383663356334394598064c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f70757368313768306265396162623836346664663363314599064c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f7075736831376830636235633630343066303966313063459a064c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f7075736831376832653564616235616564623762313331459b064c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f7075736831376833323066303766656662626238323235459c064c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f7075736831376835633436323064323638336438393734459d064c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f7075736831376836323732323030636134303366663030459e064c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f7075736831376837363433633734393236643730623861459f064c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683762616437653238323238633665346345a0064c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683765643733656235313336633831646445a1064c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137683932633064333965313838646431623645a2064c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137686134653466303530626138363463653245a3064c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137686235353838323331373839613733383145a4064c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137686237353639643761333830376666393845a5064c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137686431363935336130666339333661633645a6064c5f5a4e35616c6c6f63377261775f7665633139526177566563244c54245424432441244754243136726573657276655f666f725f707573683137686534653730666433376639373266353945a706595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683038393338346566313364356235326145a806595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683364393366373138626563306465313645a906595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683461336639666434343161646161333145aa06595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683661346466393932333536616435663845ab06595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137683736623762316263306637343466353145ac06595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137686138303937353232316662653730666645ad06595f5a4e35616c6c6f63377261775f7665633139526177566563244c542454244324412447542437726573657276653231646f5f726573657276655f616e645f68616e646c653137686633613163643730303131346665656145ae063b5f5a4e313073657264655f6a736f6e337365723138666f726d61745f657363617065645f7374723137683361373464336331393330633930666245af069e015f5a4e313073657264655f6a736f6e3576616c75653373657237365f244c5424696d706c247532302473657264652e2e7365722e2e53657269616c697a652475323024666f72247532302473657264655f6a736f6e2e2e76616c75652e2e56616c7565244754243973657269616c697a6531376831313664616335383937343963643837452e6c6c766d2e37363631343238303731363137323835383130b0063a5f5a4e35736572646533736572313053657269616c697a65723131636f6c6c6563745f7365713137683133353762633362323835306432663045b1064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137683132666365376231656564653232666145b2064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137683230373162613062656163663535303445b3064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137683536643439346163666638363764346445b4064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137683538343031626231626463653036396145b5064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137683561356331373936313262323031343545b6064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137683562623962343566333832646635393245b7064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137683661616535636132643264633932376345b8064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137683662616232653132303166333237666345b9064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137683665386536626630326630373362666245ba064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137683738346335386331376133353736316545bb064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137683739323661616134386363643036613745bc064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137683830353233656636636437623733653645bd064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137683863323861616534303636643663323445be064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137683930383935616563303935373864663345bf064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137683931613263373161643161643864633645c0064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137686232326631666433353362663132363445c1064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137686333353230636466376261623963336245c2064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137686338306565336333666533366263303145c3064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137686434316631306437613065383037646645c4064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137686439353231353162616536366439616645c5064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137686534303439333636656463623433666145c6064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137686536323061346530353364356363373445c7064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137686635323730656166663837333362313845c8064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137686635393035366339626265653334383045c9064f5f5a4e31336672616d655f737570706f72743773746f7261676531337472616e73616374696f6e616c3136776974685f7472616e73616374696f6e3137686636613136656531613638313537623245ca06d1015f5a4e3137335f244c542473705f636f6e73656e7375735f626162652e2e5f2e2e244c5424696d706c247532302473657264652e2e64652e2e446573657269616c697a652475323024666f72247532302473705f636f6e73656e7375735f626162652e2e416c6c6f776564536c6f7473244754242e2e646573657269616c697a652e2e5f5f4669656c6456697369746f7224753230246173247532302473657264652e2e64652e2e56697369746f72244754243976697369745f7374723137686535653030346637306539383362663445cb068f015f5a4e313773705f636f6e73656e7375735f62616265315f38345f244c5424696d706c247532302473657264652e2e64652e2e446573657269616c697a652475323024666f72247532302473705f636f6e73656e7375735f626162652e2e416c6c6f776564536c6f7473244754243131646573657269616c697a653137683335346631326638353935333339303245cc063f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137683232343336396233623134303639383445cd063f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137683433623233306631373331613862303245ce063f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137683738373230343137336162356233306645cf06465f5a4e34315f244c54245424753230246173247532302473657264652e2e64652e2e45787065637465642447542433666d743137683636393539613139633137323664633045d006465f5a4e34315f244c54245424753230246173247532302473657264652e2e64652e2e45787065637465642447542433666d743137683964356432353266656631633537313545d1066e5f5a4e34636f726533707472353164726f705f696e5f706c616365244c542473705f73746174655f6d616368696e652e2e44656661756c744572726f722447542431376836656330653864613137386438613066452e6c6c766d2e37363631343238303731363137323835383130d2063a5f5a4e35736572646533736572313053657269616c697a65723131636f6c6c6563745f7365713137686437316136363935396137363431373845d306405f5a4e35736572646533736572313253657269616c697a654d6170313573657269616c697a655f656e7472793137683234666165366264343161343930363145d4069a015f5a4e38355f244c542473657264655f6a736f6e2e2e7365722e2e436f6d706f756e64244c542457244324462447542424753230246173247532302473657264652e2e7365722e2e53657269616c697a655475706c6524475424313773657269616c697a655f656c656d656e7431376831336565366338666134346665393839452e6c6c766d2e37363631343238303731363137323835383130d506405f5a4e35736572646533736572313253657269616c697a654d6170313573657269616c697a655f656e7472793137683330623833333533343937663866323545d606405f5a4e35736572646533736572313253657269616c697a654d6170313573657269616c697a655f656e7472793137683463383137313865363032333565396345d706405f5a4e35736572646533736572313253657269616c697a654d6170313573657269616c697a655f656e7472793137683564666263626438663537656634623445d806405f5a4e35736572646533736572313253657269616c697a654d6170313573657269616c697a655f656e7472793137683761306531386263323134653235653445d906405f5a4e35736572646533736572313253657269616c697a654d6170313573657269616c697a655f656e7472793137683839636265363364366363313735353245da06405f5a4e35736572646533736572313253657269616c697a654d6170313573657269616c697a655f656e7472793137683864386537396663373331316338363545db06405f5a4e35736572646533736572313253657269616c697a654d6170313573657269616c697a655f656e7472793137686136333239613263326631396634626445dc06405f5a4e35736572646533736572313253657269616c697a654d6170313573657269616c697a655f656e7472793137686433633461386665383831316137623745dd06795f5a4e36375f244c542473705f73746174655f6d616368696e652e2e44656661756c744572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376837323136666635326339346461646539452e6c6c766d2e37363631343238303731363137323835383130de06745f5a4e38365f244c5424616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137683865393535343531636433333931383345df067c5f5a4e39315f244c542473705f73746174655f6d616368696e652e2e6578742e2e457874244c542448244324422447542424753230246173247532302473705f65787465726e616c69746965732e2e45787465726e616c6974696573244754243773746f726167653137683462623866386263643230653633356345e0066d5f5a4e31307363616c655f696e666f35696d706c7335365f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302424524624542447542439747970655f696e666f3137686163363436313539633264383139663145e106755f5a4e31307363616c655f696e666f35696d706c7336345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c50244124432442245250242447542439747970655f696e666f3137683334383232313130653038396134393145e20680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137683432353362623338616438363837376245e306755f5a4e31307363616c655f696e666f35696d706c7336345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c50244124432442245250242447542439747970655f696e666f3137686561393033386265643136343533343245e40680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137683364383165356632393864623263343945e50680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137683461303834306265616236343763633045e60680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137683537623862643131386632643934613845e70680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137683563316434656139616164356662646345e80680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137683563346236393839616366316131633445e90680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137683665666433666139653236373961316445ea0680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137683730623533323366643962393633633945eb0680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137683730633831323938633663316361643045ec0680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137683839323538666433373663333337373945ed0680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137683839353761353561386337616333343745ee0680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137686162363435393230383137336363616545ef0680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137686332636530326631316437303131663345f00680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137686436623861383765373630623062363545f10680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137686530333934326465386238383766613045f20680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137686535663763393635316265303039373045f30680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137686539343565303536383833643830363645f40680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137686636383039303163383230646436316645f50680015f5a4e31307363616c655f696e666f35696d706c7337355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e7665632e2e566563244c542454244754242447542439747970655f696e666f3137686661306432313763316562383265646645f606a6015f5a4e3133335f244c5424736d616c6c7665632e2e536d616c6c566563244c54244124475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e636f6c6c6563742e2e457874656e64244c5424244c542441247532302461732475323024736d616c6c7665632e2e4172726179244754242e2e4974656d244754242447542436657874656e643137686630633639663932326163376438393945f706565f5a4e38736d616c6c7665633137536d616c6c566563244c54244124475424387472795f67726f7731376835376661373337366334356335393730452e6c6c766d2e3130393532323235313434363530373438323637f8064a5f5a4e38736d616c6c7665633137536d616c6c566563244c542441244754243231726573657276655f6f6e655f756e636865636b65643137683439646637656438363939323432666245f906ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683135323864363439626563323331383545fa06ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683666623739323031363866326231386445fb06ad015f5a4e3133375f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c542454244324616c6c6f632e2e7665632e2e696e746f5f697465722e2e496e746f49746572244c5424542447542424475424244754243966726f6d5f697465723137683734636434646662633365313634343145fc069b015f5a4e31387061726974795f7363616c655f636f64656335636f6465633136696e6e65725f7475706c655f696d706c37395f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f64652475323024666f722475323024244c5024513024432452302452502424475424366465636f64653137683836646437666566363630643965386545fd069b015f5a4e31387061726974795f7363616c655f636f64656335636f6465633136696e6e65725f7475706c655f696d706c37395f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f64652475323024666f722475323024244c5024513024432452302452502424475424366465636f64653137686633616262303864656430333335343145fe069e015f5a4e31387061726974795f7363616c655f636f64656335636f6465633136696e6e65725f7475706c655f696d706c37395f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f722475323024244c502451302443245230245250242447542439656e636f64655f746f3137683563313861623532383737386263373145ff066d5f5a4e34636f726533707472343964726f705f696e5f706c616365244c5424736d616c6c7665632e2e436f6c6c656374696f6e416c6c6f634572722447542431376833666264306261303836613233333534452e6c6c766d2e313039353232323531343436353037343832363780075a5f5a4e35355f244c5424582475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542439656e636f64655f746f313768313837613332646335353536363537644581075a5f5a4e35355f244c5424582475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542439656e636f64655f746f313768363134353735376263393666653137384582075a5f5a4e35355f244c5424582475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542439656e636f64655f746f31376866383036646135353233356165386634458307465f5a4e35616c6c6f63337665633136566563244c54245424432441244754243137657874656e645f66726f6d5f736c69636531376835386436306262626438306365636634458407ac015f5a4e35616c6c6f63337665633136696e5f706c6163655f636f6c6c6563743130385f244c5424696d706c2475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c54245424432449244754242475323024666f722475323024616c6c6f632e2e7665632e2e566563244c54245424475424244754243966726f6d5f6974657231376838366430626338303239393733343035458507625f5a4e36305f244c5424245246246d7574247532302454247532302461732475323024627335382e2e6465636f64652e2e4465636f64655461726765742447542431316465636f64655f77697468313768363938346436393533666632373435624586075e5f5a4e36355f244c5424616c6c6f632e2e7665632e2e566563244c5424542443244124475424247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376838326633383830343432666162373935458707785f5a4e36355f244c5424736d616c6c7665632e2e436f6c6c656374696f6e416c6c6f63457272247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376833333136353566633965353230393634452e6c6c766d2e31303935323232353134343635303734383236378807785f5a4e36365f244c5424542475323024617324753230247061726974795f7363616c655f636f6465632e2e64657074685f6c696d69742e2e4465636f64654c696d69742447542432376465636f64655f616c6c5f776974685f64657074685f6c696d697431376833353766323864373430376338666132458907785f5a4e36365f244c5424542475323024617324753230247061726974795f7363616c655f636f6465632e2e64657074685f6c696d69742e2e4465636f64654c696d69742447542432376465636f64655f616c6c5f776974685f64657074685f6c696d697431376834306564306261623664643838613964458a07625f5a4e36375f244c5424616c6c6f632e2e7665632e2e566563244c5424542443244124475424247532302461732475323024636f72652e2e636c6f6e652e2e436c6f6e652447542435636c6f6e6531376830613133303836323936373939623162458b07625f5a4e36375f244c5424616c6c6f632e2e7665632e2e566563244c5424542443244124475424247532302461732475323024636f72652e2e636c6f6e652e2e436c6f6e652447542435636c6f6e6531376833303032623036633430666461313233458c07625f5a4e36375f244c5424616c6c6f632e2e7665632e2e566563244c5424542443244124475424247532302461732475323024636f72652e2e636c6f6e652e2e436c6f6e652447542435636c6f6e6531376861333334663465336539326366646530458d07625f5a4e36375f244c5424616c6c6f632e2e7665632e2e566563244c5424542443244124475424247532302461732475323024636f72652e2e636c6f6e652e2e436c6f6e652447542435636c6f6e6531376864353761393339363337663532343466458e07635f5a4e36395f244c5424736d616c6c7665632e2e536d616c6c566563244c54244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f7031376865333561626530343936313131376166458f07645f5a4e37305f244c5424616c6c6f632e2e7665632e2e566563244c5424542443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f7031376836353938316562653430626633323837459007645f5a4e37305f244c5424616c6c6f632e2e7665632e2e566563244c5424542443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f7031376838383938623038633564333539643731459107645f5a4e37305f244c5424616c6c6f632e2e7665632e2e566563244c5424542443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f70313768393733393934663265303735616331314592076e5f5a4e37325f244c5424616c6c6f632e2e7665632e2e566563244c5424753824475424247532302461732475323024627335382e2e656e636f64652e2e456e636f6465546172676574244754243131656e636f64655f77697468313768636338313364316636326463303937334593076e5f5a4e37385f244c5424616c6c6f632e2e7665632e2e566563244c542454244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f6465313768376163333139353063613339383532394594076e5f5a4e37385f244c5424616c6c6f632e2e7665632e2e566563244c542454244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f646531376864303036616366333634393034633837459507395f5a4e3773705f7472696531316e6f64655f68656164657231316465636f64655f73697a65313768383538353837336231616233373036334596076f5f5a4e38305f244c5424736d616c6c7665632e2e536d616c6c566563244c54244124475424247532302461732475323024636f72652e2e6f70732e2e696e6465782e2e496e646578244c542449244754242447542435696e64657831376834623132313564373666326538663630459707765f5a4e38365f244c542473705f747269652e2e6e6f64655f6865616465722e2e4e6f64654865616465722475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f646531376866326532623830316361643465373138459807795f5a4e38365f244c542473705f747269652e2e6e6f64655f6865616465722e2e4e6f64654865616465722475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542439656e636f64655f746f3137686362323764343539666137623939373945990787015f5a4e39375f244c5424616c6c6f632e2e7665632e2e566563244c5424542443244124475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f657874656e642e2e53706563457874656e64244c5424542443244924475424244754243131737065635f657874656e6431376832666337393939646230643131366535459a0787015f5a4e39375f244c5424616c6c6f632e2e7665632e2e566563244c5424542443244124475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f657874656e642e2e53706563457874656e64244c5424542443244924475424244754243131737065635f657874656e6431376865353762633734333463373832613732459b0785015f5a4e39385f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c5424542443244924475424244754243966726f6d5f6974657231376832356664626462343531623738633036459c0785015f5a4e39385f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c5424542443244924475424244754243966726f6d5f6974657231376832633736393065646566623836343338459d0785015f5a4e39385f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c5424542443244924475424244754243966726f6d5f6974657231376839353935366534663861303965666432459e0785015f5a4e39385f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c5424542443244924475424244754243966726f6d5f6974657231376863623331393938643433633537623762459f0785015f5a4e39385f244c5424616c6c6f632e2e7665632e2e566563244c54245424475424247532302461732475323024616c6c6f632e2e7665632e2e737065635f66726f6d5f697465722e2e5370656346726f6d49746572244c5424542443244924475424244754243966726f6d5f697465723137686437313433393938616462323538643745a0079c015f5a4e3132305f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e7365742e2e4254726565536574244c54245424475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e636f6c6c6563742e2e46726f6d4974657261746f72244c54245424475424244754243966726f6d5f697465723137683865393137613335386432633634313145a1076d5f5a4e31307363616c655f696e666f35696d706c7335365f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302424524624542447542439747970655f696e666f3137686465303439376265633665313438623445a207755f5a4e31307363616c655f696e666f35696d706c7336345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c50244124432442245250242447542439747970655f696e666f3137683032343032303466343961646362366345a307755f5a4e31307363616c655f696e666f35696d706c7336345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c50244124432442245250242447542439747970655f696e666f3137683338616562623263383631333330393945a407755f5a4e31307363616c655f696e666f35696d706c7336345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c50244124432442245250242447542439747970655f696e666f3137683465383931393834353164373436663145a507755f5a4e31307363616c655f696e666f35696d706c7336345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c50244124432442245250242447542439747970655f696e666f3137686135393864323635336666633139646245a607755f5a4e31307363616c655f696e666f35696d706c7336345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c50244124432442245250242447542439747970655f696e666f3137686265616265663563626436326663386545a7077e5f5a4e31307363616c655f696e666f35696d706c7337335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024247535622454247533622424753230244e24753564242447542439747970655f696e666f3137683837373365663339663464326662633845a8077e5f5a4e31307363616c655f696e666f35696d706c7337335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024247535622454247533622424753230244e24753564242447542439747970655f696e666f3137686266663934386536613364636431636245a907ba015f5a4e313073705f72756e74696d6532307472616e73616374696f6e5f76616c6964697479315f3131345f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f72247532302473705f72756e74696d652e2e7472616e73616374696f6e5f76616c69646974792e2e56616c69645472616e73616374696f6e2447542439656e636f64655f746f3137683932643632336634356433613163656445aa07c2015f5a4e313073705f72756e74696d6532307472616e73616374696f6e5f76616c6964697479315f3132325f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f72247532302473705f72756e74696d652e2e7472616e73616374696f6e5f76616c69646974792e2e5472616e73616374696f6e56616c69646974794572726f722447542439656e636f64655f746f3137683866663031363537666337336132363545ab07bf015f5a4e3131315f244c54246672616d655f73797374656d2e2e70616c6c65742e2e43616c6c244c542454244754242475323024617324753230246672616d655f737570706f72742e2e7472616974732e2e64697370617463682e2e556e66696c7465726564446973706174636861626c6524475424323264697370617463685f6279706173735f66696c74657232385f24753762242475376224636c6f73757265247537642424753764243137686235383865363334323538373038363445ac0790015f5a4e3131325f244c542470616c6c65745f626162652e2e70616c6c65742e2e47656e65736973436f6e666967244c542454244754242475323024617324753230246672616d655f737570706f72742e2e7472616974732e2e686f6f6b732e2e4275696c6447656e65736973436f6e66696724475424356275696c643137683037613262636133366132616266616445ad07aa015f5a4e3131335f244c542470616c6c65745f626162652e2e70616c6c65742e2e50616c6c6574244c542454244754242475323024617324753230246672616d655f737570706f72742e2e7472616974732e2e686f6f6b732e2e4265666f7265416c6c52756e74696d654d6967726174696f6e732447542432396265666f72655f616c6c5f72756e74696d655f6d6967726174696f6e733137683065346136626334393135383062613245ae078a015f5a4e38345f244c54246672616d655f737570706f72742e2e7472616974732e2e6d657461646174612e2e53746f7261676556657273696f6e247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376863323937656563623863373563383766452e6c6c766d2e39343631363039353438343436323532303735af07c6015f5a4e3131385f244c542470616c6c65745f62616c616e6365732e2e70616c6c65742e2e43616c6c244c54245424432449244754242475323024617324753230246672616d655f737570706f72742e2e7472616974732e2e64697370617463682e2e556e66696c7465726564446973706174636861626c6524475424323264697370617463685f6279706173735f66696c74657232385f24753762242475376224636c6f73757265247537642424753764243137686433326539663565346335653235353845b0077d5f5a4e36395f244c5424616c6c6f632e2e626f7865642e2e426f78244c5424542443244124475424247532302461732475323024636f72652e2e636c6f6e652e2e436c6f6e652447542435636c6f6e6531376831376565303832613762326463353736452e6c6c766d2e39343631363039353438343436323532303735b107685f5a4e313170616c6c65745f6261626535345f244c5424696d706c247532302470616c6c65745f626162652e2e70616c6c65742e2e50616c6c6574244c542454244754242447542431306e6578745f65706f63683137683136663632663231306531353732396345b2076b5f5a4e313170616c6c65745f6261626535345f244c5424696d706c247532302470616c6c65745f626162652e2e70616c6c65742e2e50616c6c6574244c5424542447542424475424313363757272656e745f65706f63683137683963363535383962366139353862666545b307705f5a4e313170616c6c65745f6261626535345f244c5424696d706c247532302470616c6c65745f626162652e2e70616c6c65742e2e50616c6c6574244c54245424475424244754243138656e6163745f65706f63685f6368616e67653137683039336432646335303061633739393345b407715f5a4e313170616c6c65745f6261626535345f244c5424696d706c247532302470616c6c65745f626162652e2e70616c6c65742e2e50616c6c6574244c5424542447542424475424313973686f756c645f65706f63685f6368616e67653137686239306363633831366239653838646245b5074e5f5a4e313170616c6c65745f626162653670616c6c6574313550616c6c6574244c54245424475424313673746f726167655f6d657461646174613137683235626233396134643839356236613245b607575f5a4e313170616c6c65745f626162653670616c6c6574313550616c6c6574244c54245424475424323570616c6c65745f636f6e7374616e74735f6d657461646174613137686166333135303736303232313833373145b7078e015f5a4e313170616c6c65745f626162653670616c6c6574315f38355f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302470616c6c65745f626162652e2e70616c6c65742e2e43616c6c244c542454244754242447542439747970655f696e666f3137683339313262653331316166316435386545b8078f015f5a4e313170616c6c65745f626162653670616c6c6574315f38365f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302470616c6c65745f626162652e2e70616c6c65742e2e4572726f72244c542454244754242447542439747970655f696e666f3137683666313137333538656566303830373145b90798015f5a4e313170616c6c65745f626162653670616c6c6574315f39355f244c5424696d706c247532302473657264652e2e7365722e2e53657269616c697a652475323024666f72247532302470616c6c65745f626162652e2e70616c6c65742e2e47656e65736973436f6e666967244c54245424475424244754243973657269616c697a653137686263666137313832383731356563623545ba0798015f5a4e313170616c6c65745f626162653670616c6c6574315f39385f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f64652475323024666f72247532302470616c6c65745f626162652e2e70616c6c65742e2e43616c6c244c5424542447542424475424366465636f64653137683135303438653062653630326135656445bb079b015f5a4e313170616c6c65745f626162653670616c6c6574315f39385f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f72247532302470616c6c65745f626162652e2e70616c6c65742e2e43616c6c244c542454244754242447542439656e636f64655f746f3137683539336663663337626663366532663545bc079b015f5a4e313170616c6c65745f626162653670616c6c6574315f39385f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f72247532302470616c6c65745f626162652e2e70616c6c65742e2e43616c6c244c54245424475424244754243973697a655f68696e743137683964663930363632653766356161363045bd079c015f5a4e3132305f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e7365742e2e4254726565536574244c54245424475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e636f6c6c6563742e2e46726f6d4974657261746f72244c54245424475424244754243966726f6d5f697465723137686236353537653230613766343632663745be07aa015f5a4e3133385f244c542473705f72756e74696d652e2e67656e657269632e2e636865636b65645f65787472696e7369632e2e436865636b656445787472696e736963244c54244163636f756e74496424432443616c6c24432445787472612447542424753230246173247532302473705f72756e74696d652e2e7472616974732e2e4170706c7961626c6524475424356170706c793137686239633935636431396265636339316645bf07735f5a4e34636f726533707472353664726f705f696e5f706c616365244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c2447542431376865643735326330326539353332656332452e6c6c766d2e39343631363039353438343436323532303735c007ad015f5a4e3133385f244c542473705f72756e74696d652e2e67656e657269632e2e636865636b65645f65787472696e7369632e2e436865636b656445787472696e736963244c54244163636f756e74496424432443616c6c24432445787472612447542424753230246173247532302473705f72756e74696d652e2e7472616974732e2e4170706c7961626c65244754243876616c69646174653137686264386666643132653563316166666245c107485f5a4e3133656e7669726f6e6d656e74616c396c6f63616c5f6b657931374c6f63616c4b6579244c5424542447542434776974683137683232663162323039353639636464666445c207485f5a4e3133656e7669726f6e6d656e74616c396c6f63616c5f6b657931374c6f63616c4b6579244c5424542447542434776974683137683532316362643865313434646331653845c307485f5a4e3133656e7669726f6e6d656e74616c396c6f63616c5f6b657931374c6f63616c4b6579244c5424542447542434776974683137686136353839623338326365336532303545c407e0015f5a4e3134345f244c54247375627374726174655f746573745f72756e74696d652e2e7375627374726174655f746573745f70616c6c65742e2e70616c6c65742e2e43616c6c244c542454244754242475323024617324753230246672616d655f737570706f72742e2e7472616974732e2e64697370617463682e2e556e66696c7465726564446973706174636861626c6524475424323264697370617463685f6279706173735f66696c74657232385f24753762242475376224636c6f73757265247537642424753764243137686161626165313833653136633736316645c507485f5a4e3133656e7669726f6e6d656e74616c396c6f63616c5f6b657931374c6f63616c4b6579244c5424542447542434776974683137686530623431396530386539343037623345c6074a5f5a4e31336672616d655f737570706f7274323267656e657369735f6275696c6465725f68656c70657231306765745f7072657365743137686636343962323165643633353662323645c7074f5f5a4e34636f726533707472343564726f705f696e5f706c616365244c542473657264655f6a736f6e2e2e76616c75652e2e56616c7565244754243137683733356637666563393664656465613845c807645f5a4e3773705f636f72653663727970746f3953733538436f6465633235746f5f73733538636865636b5f776974685f76657273696f6e31376865323637376132633136346338303136452e6c6c766d2e39343631363039353438343436323532303735c9074b5f5a4e31336672616d655f737570706f7274323267656e657369735f6275696c6465725f68656c70657231316275696c645f73746174653137686435306265353165646563633930373045ca07535f5a4e31336672616d655f737570706f7274367472616974733773746f72616765313553746f72616765496e7374616e636531317072656669785f686173683137683132653939646132623161373536653145cb07535f5a4e31336672616d655f737570706f7274367472616974733773746f72616765313553746f72616765496e7374616e636531317072656669785f686173683137683937363037303730336132303835656145cc07a3015f5a4e31387061726974795f7363616c655f636f64656335636f6465633136696e6e65725f7475706c655f696d706c38345f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f722475323024244c5024503024432451302443245230245250242447542439656e636f64655f746f3137683631313139623830623437383439313745cd07475f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646531337573696e675f656e636f6465643137683261396461636530663964633761393245ce073f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137683965663864643633313532343734653445cf07bd025f5a4e3237385f244c542470616c6c65745f626162652e2e70616c6c65742e2e50616c6c6574244c542454244754242475323024617324753230246672616d655f737570706f72742e2e7472616974732e2e686f6f6b732e2e4f6e46696e616c697a65244c5424244c5424244c5424244c5424542475323024617324753230246672616d655f73797374656d2e2e70616c6c65742e2e436f6e666967244754242e2e426c6f636b24753230246173247532302473705f72756e74696d652e2e7472616974732e2e48656164657250726f7669646572244754242e2e4865616465725424753230246173247532302473705f72756e74696d652e2e7472616974732e2e486561646572244754242e2e4e756d626572244754242447542431316f6e5f66696e616c697a653137683130663065333535613565633034383145d007c1025f5a4e3238305f244c542470616c6c65745f626162652e2e70616c6c65742e2e50616c6c6574244c542454244754242475323024617324753230246672616d655f737570706f72742e2e7472616974732e2e686f6f6b732e2e4f6e496e697469616c697a65244c5424244c5424244c5424244c5424542475323024617324753230246672616d655f73797374656d2e2e70616c6c65742e2e436f6e666967244754242e2e426c6f636b24753230246173247532302473705f72756e74696d652e2e7472616974732e2e48656164657250726f7669646572244754242e2e4865616465725424753230246173247532302473705f72756e74696d652e2e7472616974732e2e486561646572244754242e2e4e756d626572244754242447542431336f6e5f696e697469616c697a653137683532323039643163636332623664366245d107465f5a4e34315f244c54245424753230246173247532302473657264652e2e64652e2e45787065637465642447542433666d743137686137306565646538373033376166626445d207475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683265336435616636383537353430386545d307475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683434613634303666316265313261353745d407475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683764613033323564633637633134313445d507475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686266616664653532306364383336663945d607475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686466346237373361346665623739633145d707635f5a4e34355f244c5424244c502424525024247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376834303566303661613534323663666363452e6c6c766d2e39343631363039353438343436323532303735d80797015f5a4e34636f72653370747231313664726f705f696e5f706c616365244c5424245246242452462473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c542433325f7573697a6524432473705f636f72652e2e737232353531392e2e537232353531395075626c696354616724475424244754243137686433343161303565316536303537616445d907585f5a4e34636f726533707472323964726f705f696e5f706c616365244c5424244c5024245250242447542431376830653433316634653265363962306630452e6c6c766d2e39343631363039353438343436323532303735da074f5f5a4e34636f726533707472343564726f705f696e5f706c616365244c542473657264655f6a736f6e2e2e6572726f722e2e4572726f72244754243137686534363938663030393533343532613145db075e5f5a4e36355f244c542473705f636f72652e2e63727970746f2e2e5075626c69634572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686237303639316163613335313462346345dc07785f5a4e36365f244c5424542475323024617324753230247061726974795f7363616c655f636f6465632e2e64657074685f6c696d69742e2e4465636f64654c696d69742447542432376465636f64655f616c6c5f776974685f64657074685f6c696d69743137683039616266633131396433323330373245dd076d5f5a4e37355f244c5424747269655f64622e2e7472696564622e2e547269654442244c54244c24475424247532302461732475323024747269655f64622e2e54726965244c54244c2447542424475424386765745f776974683137683766356261393066306137383936633945de07bc015f5a4e3773705f636f726537737232353531393133335f244c5424696d706c247532302473657264652e2e64652e2e446573657269616c697a652475323024666f72247532302473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432473705f636f72652e2e737232353531392e2e537232353531395075626c696354616724475424244754243131646573657269616c697a653137686262303665643533353333663139393445df0799015f5a4e3773705f636f7265377372323535313933767266315f39395f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f64652475323024666f72247532302473705f636f72652e2e737232353531392e2e7672662e2e5672665369676e617475726524475424366465636f64653137686236636666643737393065363830636445e0079c015f5a4e3773705f636f7265377372323535313933767266315f39395f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f72247532302473705f636f72652e2e737232353531392e2e7672662e2e5672665369676e61747572652447542439656e636f64655f746f3137683438356234383663366364306433633545e107445f5a4e37747269655f6462367472696564623135547269654442244c54244c24475424313166657463685f76616c75653137686133303638623636643838303530363445e2074a5f5a4e37747269655f6462367472696564623135547269654442244c54244c2447542431376765745f7261775f6f725f6c6f6f6b75703137683839396361353734613134393163633545e3077b5f5a4e38385f244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432454244754242475323024617324753230247363616c655f696e666f2e2e54797065496e666f2447542439747970655f696e666f3137683538303231336363326563643032306445e4078b015f5a4e39355f244c542470616c6c65745f626162652e2e70616c6c65742e2e43616c6c244c542454244754242475323024617324753230246672616d655f737570706f72742e2e64697370617463682e2e4765744469737061746368496e666f2447542431376765745f64697370617463685f696e666f3137683964383838393334366463666261333945e507745f5a4e36325f244c542473705f72756e74696d652e2e44697370617463684572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376832343537376561373336343862333634452e6c6c766d2e34343635333131303635343831343136313531e60789015f5a4e31307363616c655f696e666f35696d706c7338345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024636f72652e2e726573756c742e2e526573756c74244c54245424432445244754242447542439747970655f696e666f3137683237633234346263613464343563366445e70789015f5a4e31307363616c655f696e666f35696d706c7338345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024636f72652e2e726573756c742e2e526573756c74244c54245424432445244754242447542439747970655f696e666f3137683338633839336262326233353965646145e80789015f5a4e31307363616c655f696e666f35696d706c7338345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024636f72652e2e726573756c742e2e526573756c74244c54245424432445244754242447542439747970655f696e666f3137686464616434303265373165386364363345e90789015f5a4e31307363616c655f696e666f35696d706c7338345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024636f72652e2e726573756c742e2e526573756c74244c54245424432445244754242447542439747970655f696e666f3137686666613866383636633261373263306445ea074a5f5a4e313073705f72756e74696d6536747261697473313656616c6964617465556e7369676e656431327072655f64697370617463683137683261623537386336646337666366393745eb07ac015f5a4e3132375f244c54247375627374726174655f746573745f72756e74696d652e2e7375627374726174655f746573745f70616c6c65742e2e70616c6c65742e2e50616c6c6574244c5424542447542424753230246173247532302473705f72756e74696d652e2e7472616974732e2e56616c6964617465556e7369676e656424475424313776616c69646174655f756e7369676e65643137683365306535333762383466316433333445ec07a2015f5a4e3132385f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e642e2e547269654261636b656e64244c5424532443244824432443244324522447542424753230246173247532302473705f73746174655f6d616368696e652e2e6261636b656e642e2e4261636b656e64244c54244824475424244754243773746f726167653137683134306438336137373938393464326145ed0782015f5a4e34636f726533707472373164726f705f696e5f706c616365244c542473705f747269652e2e6572726f722e2e4572726f72244c54247072696d69746976655f74797065732e2e48323536244754242447542431376834613633653065376463303532303565452e6c6c766d2e34343635333131303635343831343136313531ee07ad015f5a4e3132395f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e642e2e556e696d706c656d656e7465645265636f7264657250726f7669646572244c5424482447542424753230246173247532302473705f747269652e2e547269655265636f7264657250726f7669646572244c5424482447542424475424313661735f747269655f7265636f726465723137683936626163303934663037653363613045ef079a015f5a4e31326672616d655f73797374656d315f3130325f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f73797374656d2e2e4576656e745265636f7264244c54244524432454244754242447542439656e636f64655f746f3137686534346435323937393632363336383645f007b4015f5a4e31326672616d655f73797374656d315f3130335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f73797374656d2e2e4163636f756e74496e666f244c54244e6f6e63652443244163636f756e7444617461244754242447542439747970655f696e666f31376861356339616239386635333233366263452e6c6c766d2e34343635333131303635343831343136313531f1078c015f5a4e31326672616d655f73797374656d315f38395f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f73797374656d2e2e4576656e745265636f7264244c54244524432454244754242447542439747970655f696e666f3137683561633331363564313635393565336645f20795015f5a4e31326672616d655f73797374656d315f39385f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f73797374656d2e2e436f646555706772616465417574686f72697a6174696f6e244c542454244754242447542439747970655f696e666f3137683263626564643639373935336164303745f30790015f5a4e31336672616d655f737570706f72743773746f72616765357479706573336d6170383153746f726167654d6170244c54245072656669782443244861736865722443244b657924432456616c756524432451756572794b696e642443244f6e456d7074792443244d617856616c756573244754243474616b653137683365343230316431333031303335653145f40799015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f722475323024472447542431307472795f6d75746174653137683232613938646165323437376236366145f507605f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646531337573696e675f656e636f64656431376864346531326335346534623064376538452e6c6c766d2e34343635333131303635343831343136313531f60799015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f722475323024472447542431307472795f6d75746174653137686463656538666461326632666138306345f707a0015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f722475323024472447542431377472795f6d75746174655f6578697374733137686531333834333538366532383863653945f80791015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f7224753230244724475424336765743137683030393039326162616232333938336245f90791015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f7224753230244724475424336765743137683132633739353633636264663864376345fa0791015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f7224753230244724475424336765743137683339343634353436316266393466336645fb0791015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f7224753230244724475424336765743137683561343934343838353335343735636445fc0792015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f72247532302447244754243474616b653137683535316530633332616638393233626245fd0794015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f722475323024472447542436696e736572743137683366313030366364656537626234313345fe0794015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f722475323024472447542436696e736572743137683533336434626134386462303233353745ff0794015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f722475323024472447542436696e736572743137686462616363396637376564623432626145800894015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f722475323024472447542436696e736572743137686531336639613466333237663362656145810894015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f722475323024472447542436696e736572743137686562346335363162333939376639313045820894015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f7224753230244724475424366d75746174653137683263323638653437306261303935363645830894015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f7224753230244724475424366d75746174653137683365663539343766363638326535633445840894015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f7224753230244724475424366d75746174653137683435373838366462343039353536336445850894015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f72247532302447244754243672656d6f76653137683935636234333435313032666538393345860894015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f72336d617037395f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f726167654d6170244c54244b24432456244754242475323024666f72247532302447244754243672656d6f766531376839663430653730363033386464666339458708be015f5a4e3134365f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e642e2e556e696d706c656d656e746564436163686550726f7669646572244c5424482447542424753230246173247532302473705f73746174655f6d616368696e652e2e747269655f6261636b656e642e2e54726965436163686550726f7669646572244c5424482447542424475424313661735f747269655f64625f636163686531376838343439306132336634343234613262458808c2015f5a4e3134365f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e642e2e556e696d706c656d656e746564436163686550726f7669646572244c5424482447542424753230246173247532302473705f73746174655f6d616368696e652e2e747269655f6261636b656e642e2e54726965436163686550726f7669646572244c5424482447542424475424323061735f747269655f64625f6d75745f636163686531376861613462396464383762376235653361458908c7015f5a4e3135335f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e642e2e556e696d706c656d656e746564436163686550726f7669646572244c54244824475424247532302461732475323024747269655f64622e2e547269654361636865244c542473705f747269652e2e6e6f64655f636f6465632e2e4e6f6465436f646563244c54244824475424244754242447542431386765745f6f725f696e736572745f6e6f646531376835626133383363343466666534366663458a08c8015f5a4e3135335f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e642e2e556e696d706c656d656e746564436163686550726f7669646572244c54244824475424247532302461732475323024747269655f64622e2e547269654361636865244c542473705f747269652e2e6e6f64655f636f6465632e2e4e6f6465436f646563244c542448244754242447542424475424313963616368655f76616c75655f666f725f6b657931376863656663303636346664386465346366458b08c9015f5a4e3135335f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e642e2e556e696d706c656d656e746564436163686550726f7669646572244c54244824475424247532302461732475323024747269655f64622e2e547269654361636865244c542473705f747269652e2e6e6f64655f636f6465632e2e4e6f6465436f646563244c54244824475424244754242447542432306c6f6f6b75705f76616c75655f666f725f6b657931376838363066376566333836626339386531458c08bc015f5a4e3135335f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e642e2e556e696d706c656d656e746564436163686550726f7669646572244c54244824475424247532302461732475323024747269655f64622e2e547269654361636865244c542473705f747269652e2e6e6f64655f636f6465632e2e4e6f6465436f646563244c542448244754242447542424475424386765745f6e6f646531376866616537653961643963663838623238458d08d8015f5a4e3136315f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e642e2e556e696d706c656d656e7465645265636f7264657250726f7669646572244c54244824475424247532302461732475323024747269655f64622e2e547269655265636f72646572244c5424244c542448247532302461732475323024686173685f64622e2e486173686572244754242e2e4f757424475424244754243237747269655f6e6f6465735f7265636f726465645f666f725f6b657931376865643932643636643639633037653066458e08c2015f5a4e3136315f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e642e2e556e696d706c656d656e7465645265636f7264657250726f7669646572244c54244824475424247532302461732475323024747269655f64622e2e547269655265636f72646572244c5424244c542448247532302461732475323024686173685f64622e2e486173686572244754242e2e4f75742447542424475424367265636f726431376862353435343464666462323435643339458f08645f5a4e313673705f73746174655f6d616368696e6531376f7665726c617965645f6368616e67657332354f7665726c617965644368616e676573244c5424482447542431317365745f73746f7261676531376864333738646666366564376433343736459008655f5a4e313673705f73746174655f6d616368696e6531376f7665726c617965645f6368616e67657332354f7665726c617965644368616e676573244c54244824475424313273746f726167655f726f6f74313768383265626636626635666638623639634591085f5f5a4e313673705f73746174655f6d616368696e6531376f7665726c617965645f6368616e67657332354f7665726c617965644368616e676573244c542448244754243773746f7261676531376861346266363131363634356331623036459208475f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646531337573696e675f656e636f64656431376835653063643533316231623536346432459308475f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646531337573696e675f656e636f64656431376862343135656238373732383666333231459408475f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646531337573696e675f656e636f64656431376863313364623435363063653135323366459508585f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f646531376836386231353732613461613337306561452e6c6c766d2e3434363533313130363534383134313631353196083f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f646531376864353565386236353361656636306465459708ee015f5a4e3139365f244c54246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e6d61702e2e53746f726167654d6170244c54245072656669782443244861736865722443244b657924432456616c756524432451756572794b696e642443244f6e456d7074792443244d617856616c756573244754242475323024617324753230246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e53746f72616765456e7472794d657461646174614275696c6465722447542431346275696c645f6d6574616461746131376830393738326539643064303330383564459808ee015f5a4e3139365f244c54246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e6d61702e2e53746f726167654d6170244c54245072656669782443244861736865722443244b657924432456616c756524432451756572794b696e642443244f6e456d7074792443244d617856616c756573244754242475323024617324753230246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e53746f72616765456e7472794d657461646174614275696c6465722447542431346275696c645f6d6574616461746131376831363363656566303465366439633330459908cd015f5a4e3139626f756e6465645f636f6c6c656374696f6e733131626f756e6465645f766563315f3130385f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024626f756e6465645f636f6c6c656374696f6e732e2e626f756e6465645f7665632e2e426f756e646564566563244c54245424432453244754242447542439747970655f696e666f31376862336661666136623733383239343334452e6c6c766d2e343436353331313036353438313431363135319a08ee015f5a4e3139365f244c54246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e6d61702e2e53746f726167654d6170244c54245072656669782443244861736865722443244b657924432456616c756524432451756572794b696e642443244f6e456d7074792443244d617856616c756573244754242475323024617324753230246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e53746f72616765456e7472794d657461646174614275696c6465722447542431346275696c645f6d6574616461746131376831633132303730623963323465343636459b08ee015f5a4e3139365f244c54246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e6d61702e2e53746f726167654d6170244c54245072656669782443244861736865722443244b657924432456616c756524432451756572794b696e642443244f6e456d7074792443244d617856616c756573244754242475323024617324753230246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e53746f72616765456e7472794d657461646174614275696c6465722447542431346275696c645f6d6574616461746131376835663236666438376163623639386537459c08ee015f5a4e3139365f244c54246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e6d61702e2e53746f726167654d6170244c54245072656669782443244861736865722443244b657924432456616c756524432451756572794b696e642443244f6e456d7074792443244d617856616c756573244754242475323024617324753230246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e53746f72616765456e7472794d657461646174614275696c6465722447542431346275696c645f6d6574616461746131376836613032303264336364396638353538459d08cd015f5a4e3139626f756e6465645f636f6c6c656374696f6e733131626f756e6465645f766563315f3130385f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024626f756e6465645f636f6c6c656374696f6e732e2e626f756e6465645f7665632e2e426f756e646564566563244c54245424432453244754242447542439747970655f696e666f31376831313432386235313165633561633762452e6c6c766d2e343436353331313036353438313431363135319e08ee015f5a4e3139365f244c54246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e6d61702e2e53746f726167654d6170244c54245072656669782443244861736865722443244b657924432456616c756524432451756572794b696e642443244f6e456d7074792443244d617856616c756573244754242475323024617324753230246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e53746f72616765456e7472794d657461646174614275696c6465722447542431346275696c645f6d6574616461746131376837316230363031343636396137323261459f08cd015f5a4e3139626f756e6465645f636f6c6c656374696f6e733131626f756e6465645f766563315f3130385f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024626f756e6465645f636f6c6c656374696f6e732e2e626f756e6465645f7665632e2e426f756e646564566563244c54245424432453244754242447542439747970655f696e666f31376832396533346563346531636436636164452e6c6c766d2e34343635333131303635343831343136313531a008ee015f5a4e3139365f244c54246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e6d61702e2e53746f726167654d6170244c54245072656669782443244861736865722443244b657924432456616c756524432451756572794b696e642443244f6e456d7074792443244d617856616c756573244754242475323024617324753230246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e53746f72616765456e7472794d657461646174614275696c6465722447542431346275696c645f6d657461646174613137686165373162666362343635373764376445a108ee015f5a4e3139365f244c54246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e6d61702e2e53746f726167654d6170244c54245072656669782443244861736865722443244b657924432456616c756524432451756572794b696e642443244f6e456d7074792443244d617856616c756573244754242475323024617324753230246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e53746f72616765456e7472794d657461646174614275696c6465722447542431346275696c645f6d657461646174613137686533653833326139326662363061623445a208cd015f5a4e3139626f756e6465645f636f6c6c656374696f6e733131626f756e6465645f766563315f3130385f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024626f756e6465645f636f6c6c656374696f6e732e2e626f756e6465645f7665632e2e426f756e646564566563244c54245424432453244754242447542439747970655f696e666f31376838313839363565613438303932343866452e6c6c766d2e34343635333131303635343831343136313531a308b4015f5a4e3139626f756e6465645f636f6c6c656374696f6e733131626f756e6465645f766563315f3130385f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024626f756e6465645f636f6c6c656374696f6e732e2e626f756e6465645f7665632e2e426f756e646564566563244c54245424432453244754242447542439747970655f696e666f3137683632326164343332663330346262396345a408a3015f5a4e323073705f636f6e73656e7375735f6772616e647061315f3130335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f6772616e6470612e2e45717569766f636174696f6e50726f6f66244c5424482443244e244754242447542439747970655f696e666f3137683538356338616330396335303332313945a5089d015f5a4e323073705f636f6e73656e7375735f6772616e647061315f39385f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f6772616e6470612e2e45717569766f636174696f6e244c5424482443244e244754242447542439747970655f696e666f3137683363373265306238366130306461393645a608465f5a4e34315f244c54245424753230246173247532302473657264652e2e64652e2e45787065637465642447542433666d743137683032383039393935386265613032383945a708475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686563323865313866636363343834663345a808305f5a4e346273353836656e636f64653131656e636f64655f696e746f3137686130653937303161376663656634306445a908b2015f5a4e34636f72653370747231313864726f705f696e5f706c616365244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e642e2e556e696d706c656d656e746564436163686550726f7669646572244c542473705f72756e74696d652e2e7472616974732e2e426c616b6554776f323536244754242447542431376837343366356666663932653264303539452e6c6c766d2e34343635333131303635343831343136313531aa083e5f5a4e34636f726533707472323864726f705f696e5f706c616365244c542424524624753136244754243137683665616535333638616239373933636345ab086d5f5a4e34636f726535617272617936395f244c5424696d706c2475323024636f72652e2e666d742e2e44656275672475323024666f722475323024247535622454247533622424753230244e24753564242447542433666d743137683664633539656264356333613331306245ac086d5f5a4e34636f726535617272617936395f244c5424696d706c2475323024636f72652e2e666d742e2e44656275672475323024666f722475323024247535622454247533622424753230244e24753564242447542433666d743137686439653333636131656461373437343445ad086e5f5a4e35365f244c5424627335382e2e656e636f64652e2e4572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376862623965643035343061613830346334452e6c6c766d2e34343635333131303635343831343136313531ae08785f5a4e36365f244c5424542475323024617324753230247061726974795f7363616c655f636f6465632e2e64657074685f6c696d69742e2e4465636f64654c696d69742447542432376465636f64655f616c6c5f776974685f64657074685f6c696d69743137683136396539656638656531346431363845af08645f5a4e37315f244c5424636f72652e2e6d61726b65722e2e5068616e746f6d44617461244c54245424475424247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686437323734313730623666356231363245b008425f5a4e3773705f636f72653333746f5f7375627374726174655f7761736d5f666e5f72657475726e5f76616c75653137683464623033656131363732616134663545b108425f5a4e3773705f636f72653333746f5f7375627374726174655f7761736d5f666e5f72657475726e5f76616c75653137686231336232306537636632336662643445b208425f5a4e3773705f636f72653333746f5f7375627374726174655f7761736d5f666e5f72657475726e5f76616c75653137686635343031646464316132656463633445b308485f5a4e37747269655f6462386974657261746f7232365472696544425261774974657261746f72244c54244c24475424336e65773137683334353433623139373032613932333845b4084e5f5a4e37747269655f6462386974657261746f7232365472696544425261774974657261746f72244c54244c24475424396e6578745f6974656d3137686266303663353063386165373336303745b5088a015f5a4e38345f244c54246672616d655f737570706f72742e2e7472616974732e2e6d657461646174612e2e53746f7261676556657273696f6e247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376863323937656563623863373563383766452e6c6c766d2e34343635333131303635343831343136313531b608775f5a4e32327375627374726174655f746573745f72756e74696d6532317375627374726174655f746573745f70616c6c65743670616c6c6574313550616c6c6574244c5424542447542432336465706f7369745f6c6f675f6469676573745f6974656d3137686232386631323733356335653335383645b708635f5a4e32327375627374726174655f746573745f72756e74696d6532317375627374726174655f746573745f70616c6c65743670616c6c6574313550616c6c6574244c5424542447542434726561643137683266363834653430643332626636633945b8086c5f5a4e32327375627374726174655f746573745f72756e74696d6532317375627374726174655f746573745f70616c6c65743670616c6c6574313550616c6c6574244c542454244754243132657865637574655f726561643137686630343132346630393935653161316445b9086e5f5a4e32327375627374726174655f746573745f72756e74696d6532317375627374726174655f746573745f70616c6c65743670616c6c6574313550616c6c6574244c542454244754243134726561645f616e645f70616e69633137683733613232323435393463353765653845ba08e0015f5a4e32327375627374726174655f746573745f72756e74696d6532317375627374726174655f746573745f70616c6c65743670616c6c6574315f3133325f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e7375627374726174655f746573745f70616c6c65742e2e70616c6c65742e2e43616c6c244c542454244754242447542439656e636f64655f746f3137686434366135336534363535303135373245bb08ae015f5a4e3132395f244c54247375627374726174655f746573745f72756e74696d652e2e7375627374726174655f746573745f70616c6c65742e2e70616c6c65742e2e43616c6c244c542454244754242475323024617324753230246672616d655f737570706f72742e2e64697370617463682e2e4765744469737061746368496e666f2447542431376765745f64697370617463685f696e666f3137683862336231613264376162303635323945bc08705f5a4e32327375627374726174655f746573745f72756e74696d6532317375627374726174655f746573745f70616c6c65743670616c6c6574313550616c6c6574244c54245424475424313673746f726167655f6d657461646174613137686337633866323336313830636435666145bd08cc015f5a4e3134375f244c54247375627374726174655f746573745f72756e74696d652e2e7375627374726174655f746573745f70616c6c65742e2e70616c6c65742e2e50616c6c6574244c542454244754242475323024617324753230246672616d655f737570706f72742e2e7472616974732e2e686f6f6b732e2e4265666f7265416c6c52756e74696d654d6967726174696f6e732447542432396265666f72655f616c6c5f72756e74696d655f6d6967726174696f6e733137686430646131386266373663636231393845be08dd015f5a4e32327375627374726174655f746573745f72756e74696d6532317375627374726174655f746573745f70616c6c65743670616c6c6574315f3132395f244c5424696d706c247532302473657264652e2e7365722e2e53657269616c697a652475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e7375627374726174655f746573745f70616c6c65742e2e70616c6c65742e2e47656e65736973436f6e666967244c54245424475424244754243973657269616c697a653137686465623536323735636339306430373645bf08cd015f5a4e32327375627374726174655f746573745f72756e74696d6532317375627374726174655f746573745f70616c6c65743670616c6c6574315f3131375f244c5424696d706c2475323024636f72652e2e636c6f6e652e2e436c6f6e652475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e7375627374726174655f746573745f70616c6c65742e2e70616c6c65742e2e43616c6c244c542454244754242447542435636c6f6e653137686637303937303464613631373164393245c008dd015f5a4e32327375627374726174655f746573745f72756e74696d6532317375627374726174655f746573745f70616c6c65743670616c6c6574315f3133325f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f64652475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e7375627374726174655f746573745f70616c6c65742e2e70616c6c65742e2e43616c6c244c5424542447542424475424366465636f64653137683964643330356239383635653137653945c108d3015f5a4e32327375627374726174655f746573745f72756e74696d6532317375627374726174655f746573745f70616c6c65743670616c6c6574315f3131395f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e7375627374726174655f746573745f70616c6c65742e2e70616c6c65742e2e43616c6c244c542454244754242447542439747970655f696e666f3137683636616437663531306639613865663045c208755f5a4e31307363616c655f696e666f35696d706c7336345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c50244124432442245250242447542439747970655f696e666f3137683439633735333166633935653437343145c30892015f5a4e313666696e616c6974795f6772616e647061315f39315f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302466696e616c6974795f6772616e6470612e2e507265636f6d6d6974244c5424482443244e244754242447542439747970655f696e666f3137683062383331356530383235316161616645c408755f5a4e31307363616c655f696e666f35696d706c7336345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c50244124432442245250242447542439747970655f696e666f3137683638363838646465613030663937626145c508755f5a4e31307363616c655f696e666f35696d706c7336345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c50244124432442245250242447542439747970655f696e666f3137686135643337336631363435356132383645c608755f5a4e31307363616c655f696e666f35696d706c7336345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c50244124432442245250242447542439747970655f696e666f3137686338333732643832633464313261646145c70890015f5a4e313666696e616c6974795f6772616e647061315f38395f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302466696e616c6974795f6772616e6470612e2e507265766f7465244c5424482443244e244754242447542439747970655f696e666f3137683631316165633464653435636165613945c8087d5f5a4e31307363616c655f696e666f35696d706c7337325f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024244c502441244324422443244324432444245250242447542439747970655f696e666f3137683134643531376233663132646366626245c908ba015f5a4e31326672616d655f73797374656d3130657874656e73696f6e733131636865636b5f6e6f6e6365315f3130395f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f6e6f6e63652e2e436865636b4e6f6e6365244c542454244754242447542439747970655f696e666f3137683966313231303161633862623464353545ca08b1015f5a4e32396672616d655f6d657461646174615f686173685f657874656e73696f6e315f3130385f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e436865636b4d6574616461746148617368244c542454244754242447542439747970655f696e666f3137683563643234653436363135663765323145cb08bd015f5a4e31326672616d655f73797374656d3130657874656e73696f6e733132636865636b5f776569676874315f3131315f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f7765696768742e2e436865636b576569676874244c542454244754242447542439747970655f696e666f3137683466633431303934656430363432646445cc088a015f5a4e313073705f72756e74696d65315f38395f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f72247532302473705f72756e74696d652e2e44697370617463684572726f722447542439656e636f64655f746f3137683965396532323862323839366165363845cd08b5015f5a4e3131365f244c54246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f7765696768742e2e436865636b576569676874244c5424542447542424753230246173247532302473705f72756e74696d652e2e7472616974732e2e5369676e6564457874656e73696f6e244754243133706f73745f646973706174636831376862343833663134383166613630616464452e6c6c766d2e363831373539343630333232373130383833ce0891015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f723576616c756537375f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f7261676556616c7565244c542454244754242475323024666f7224753230244724475424336765743137686636383238623939366237653363313445cf08745f5a4e38375f244c54246672616d655f737570706f72742e2e64697370617463682e2e5065724469737061746368436c617373244c54245424475424247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686338356638316434306262626633386345d0083b5f5a4e31336672616d655f737570706f72743773746f7261676538756e686173686564336765743137686633343130326665633334346565636145d1085c5f5a4e34636f726533666d74336e756d35305f244c5424696d706c2475323024636f72652e2e666d742e2e44656275672475323024666f7224753230247533322447542433666d743137683039633433313131313862343839303145d208a9015f5a4e313170616c6c65745f626162653670616c6c65743131385f244c5424696d706c2475323024636f72652e2e636f6e766572742e2e46726f6d244c542470616c6c65745f626162652e2e70616c6c65742e2e4572726f72244c54245424475424244754242475323024666f72247532302473705f72756e74696d652e2e44697370617463684572726f72244754243466726f6d3137686264353735313430616262613433386345d3087e5f5a4e31326672616d655f73797374656d3130657874656e73696f6e733132636865636b5f7765696768743230436865636b576569676874244c542454244754243135646f5f7072655f646973706174636831376837313835366264363832363535663934452e6c6c766d2e363831373539343630333232373130383833d408aa015f5a4e3132355f244c5424244c50245475706c65456c656d656e74302443245475706c65456c656d656e74312443245475706c65456c656d656e74322443245475706c65456c656d656e74332452502424753230246173247532302473705f72756e74696d652e2e7472616974732e2e5369676e6564457874656e73696f6e2447542431376164646974696f6e616c5f7369676e65643137686562643666666362323432623862643745d5085f5f5a4e36365f244c5424636f72652e2e6f7074696f6e2e2e4f7074696f6e244c54245424475424247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686436653932303764623531313734353745d608aa015f5a4e3132355f244c5424244c50245475706c65456c656d656e74302443245475706c65456c656d656e74312443245475706c65456c656d656e74322443245475706c65456c656d656e74332452502424753230246173247532302473705f72756e74696d652e2e7472616974732e2e5369676e6564457874656e73696f6e24475424313776616c69646174655f756e7369676e65643137683463633738646162303064633965346345d708695f5a4e31326672616d655f73797374656d3130657874656e73696f6e733132636865636b5f7765696768743230436865636b576569676874244c542454244754243138636865636b5f626c6f636b5f6c656e6774683137686434373666396639323539346662356345d8086d5f5a4e31326672616d655f73797374656d3130657874656e73696f6e733132636865636b5f7765696768743230436865636b576569676874244c542454244754243232636865636b5f65787472696e7369635f7765696768743137683536643330386534616638383864346445d908a0015f5a4e3132355f244c5424244c50245475706c65456c656d656e74302443245475706c65456c656d656e74312443245475706c65456c656d656e74322443245475706c65456c656d656e74332452502424753230246173247532302473705f72756e74696d652e2e7472616974732e2e5369676e6564457874656e73696f6e24475424386d657461646174613137686261663864356538616533386436393545da08a0015f5a4e3132355f244c5424244c50245475706c65456c656d656e74302443245475706c65456c656d656e74312443245475706c65456c656d656e74322443245475706c65456c656d656e74332452502424753230246173247532302473705f72756e74696d652e2e7472616974732e2e5369676e6564457874656e73696f6e244754243876616c69646174653137683864383663336665373630656435383345db08495f5a4e34345f244c54242452462454247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d743137686663313736633761623337386434396445dc08ab015f5a4e31326672616d655f73797374656d3670616c6c65743131395f244c5424696d706c2475323024636f72652e2e636f6e766572742e2e46726f6d244c54246672616d655f73797374656d2e2e70616c6c65742e2e4572726f72244c54245424475424244754242475323024666f72247532302473705f72756e74696d652e2e44697370617463684572726f72244754243466726f6d3137683439323434396438616365353764653145dd083b5f5a4e31336672616d655f737570706f72743773746f7261676538756e686173686564336765743137683036383136373332313164353262333345de087a5f5a4e36395f244c54247061726974795f7363616c655f636f6465632e2e6572726f722e2e4572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376863636562313832636338343631643463452e6c6c766d2e363831373539343630333232373130383833df08715f5a4e36305f244c5424616c6c6f632e2e737472696e672e2e537472696e67247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d7431376838643930316130303836663739383732452e6c6c766d2e363831373539343630333232373130383833e008535f5a4e31336672616d655f737570706f72743773746f7261676538756e6861736865643367657431376831643061343633386265393631396331452e6c6c766d2e363831373539343630333232373130383833e1083b5f5a4e31336672616d655f737570706f72743773746f7261676538756e686173686564336765743137683233623431666266643334386335363145e208535f5a4e31336672616d655f737570706f72743773746f7261676538756e6861736865643367657431376834313365393366653631653239646364452e6c6c766d2e363831373539343630333232373130383833e308495f5a4e34345f244c54242452462454247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d743137683937303730333131386533396361383445e4083b5f5a4e31336672616d655f737570706f72743773746f7261676538756e686173686564336765743137683436336338663161383834343732396345e508535f5a4e31336672616d655f737570706f72743773746f7261676538756e6861736865643367657431376835636432353630346332363666343430452e6c6c766d2e363831373539343630333232373130383833e608535f5a4e31336672616d655f737570706f72743773746f7261676538756e6861736865643367657431376837623730613933656537623531353063452e6c6c766d2e363831373539343630333232373130383833e708535f5a4e31336672616d655f737570706f72743773746f7261676538756e6861736865643367657431376863383637363566636335633065356464452e6c6c766d2e363831373539343630333232373130383833e808535f5a4e31336672616d655f737570706f72743773746f7261676538756e6861736865643367657431376865333432316233616232383631316363452e6c6c766d2e363831373539343630333232373130383833e9083c5f5a4e31336672616d655f737570706f72743773746f7261676538756e6861736865643474616b653137683437343139656634653239653162313845ea0891015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f723576616c756537375f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f7261676556616c7565244c542454244754242475323024666f7224753230244724475424336765743137683035663333613639353237336438323845eb0891015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f723576616c756537375f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f7261676556616c7565244c542454244754242475323024666f7224753230244724475424336765743137683061643832313565643832663138306245ec0891015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f723576616c756537375f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f7261676556616c7565244c542454244754242475323024666f7224753230244724475424336765743137683635316662383361346237356438663545ed0891015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f723576616c756537375f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f7261676556616c7565244c542454244754242475323024666f7224753230244724475424336765743137683665656138393966373463623063626245ee0891015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f723576616c756537375f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f7261676556616c7565244c542454244754242475323024666f7224753230244724475424336765743137683839376231313230643762336633383045ef0891015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f723576616c756537375f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f7261676556616c7565244c542454244754242475323024666f7224753230244724475424336765743137686136326566613532663665366534663545f00891015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f723576616c756537375f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f7261676556616c7565244c542454244754242475323024666f7224753230244724475424337075743137683464303238656331373830313938316145f10891015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f723576616c756537375f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f7261676556616c7565244c542454244754242475323024666f7224753230244724475424337075743137683630343239373738333034626265323745f20891015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f723576616c756537375f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f7261676556616c7565244c542454244754242475323024666f7224753230244724475424337075743137686232386630373034616139633863323045f30891015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f723576616c756537375f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f7261676556616c7565244c542454244754242475323024666f7224753230244724475424337075743137686464383666303565653339396436343945f40892015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f723576616c756537375f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f7261676556616c7565244c542454244754242475323024666f72247532302447244754243474616b653137686561616132653936363865643839366645f50894015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f723576616c756537375f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f7261676556616c7565244c542454244754242475323024666f722475323024472447542436617070656e643137683762396565663239356265303763376645f60894015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f723576616c756537375f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f7261676556616c7565244c542454244754242475323024666f7224753230244724475424366d75746174653137683765303134326562633665633461373445f70894015f5a4e31336672616d655f737570706f72743773746f726167653967656e657261746f723576616c756537375f244c5424696d706c24753230246672616d655f737570706f72742e2e73746f726167652e2e53746f7261676556616c7565244c542454244754242475323024666f7224753230244724475424366d75746174653137686539666164356566636562633866376545f808b5015f5a4e313570616c6c65745f62616c616e6365733670616c6c65743132365f244c5424696d706c2475323024636f72652e2e636f6e766572742e2e46726f6d244c542470616c6c65745f62616c616e6365732e2e70616c6c65742e2e4572726f72244c5424542443244924475424244754242475323024666f72247532302473705f72756e74696d652e2e44697370617463684572726f72244754243466726f6d3137683565626264376664623235306333356445f908a5015f5a4e313666696e616c6974795f6772616e647061315f3131325f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f64652475323024666f72247532302466696e616c6974795f6772616e6470612e2e45717569766f636174696f6e244c5424496424432456244324532447542424475424366465636f64653137683438386439393231336237346338653345fa089a015f5a4e313666696e616c6974795f6772616e647061315f39395f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302466696e616c6974795f6772616e6470612e2e45717569766f636174696f6e244c542449642443245624432453244754242447542439747970655f696e666f3137683162346235636266336365393564306545fb089a015f5a4e313666696e616c6974795f6772616e647061315f39395f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302466696e616c6974795f6772616e6470612e2e45717569766f636174696f6e244c542449642443245624432453244754242447542439747970655f696e666f3137686434613034666430623365353738356345fc08d7015f5a4e3137335f244c54246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e76616c75652e2e53746f7261676556616c7565244c542450726566697824432456616c756524432451756572794b696e642443244f6e456d707479244754242475323024617324753230246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e53746f72616765456e7472794d657461646174614275696c6465722447542431346275696c645f6d657461646174613137683334636635333639306531323335363445fd08da015f5a4e3139626f756e6465645f636f6c6c656374696f6e7331367765616b5f626f756e6465645f766563315f3131375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024626f756e6465645f636f6c6c656374696f6e732e2e7765616b5f626f756e6465645f7665632e2e5765616b426f756e646564566563244c54245424432453244754242447542439747970655f696e666f31376833636465356335323837316365343634452e6c6c766d2e363831373539343630333232373130383833fe08d7015f5a4e3137335f244c54246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e76616c75652e2e53746f7261676556616c7565244c542450726566697824432456616c756524432451756572794b696e642443244f6e456d707479244754242475323024617324753230246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e53746f72616765456e7472794d657461646174614275696c6465722447542431346275696c645f6d657461646174613137683635653131633235303832396435613945ff08d7015f5a4e3137335f244c54246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e76616c75652e2e53746f7261676556616c7565244c542450726566697824432456616c756524432451756572794b696e642443244f6e456d707479244754242475323024617324753230246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e53746f72616765456e7472794d657461646174614275696c6465722447542431346275696c645f6d6574616461746131376838663536326335373535366132393065458009d7015f5a4e3137335f244c54246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e76616c75652e2e53746f7261676556616c7565244c542450726566697824432456616c756524432451756572794b696e642443244f6e456d707479244754242475323024617324753230246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e53746f72616765456e7472794d657461646174614275696c6465722447542431346275696c645f6d6574616461746131376861303465656238663230616331386466458109d7015f5a4e3137335f244c54246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e76616c75652e2e53746f7261676556616c7565244c542450726566697824432456616c756524432451756572794b696e642443244f6e456d707479244754242475323024617324753230246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e53746f72616765456e7472794d657461646174614275696c6465722447542431346275696c645f6d6574616461746131376861666134363039313236646232313231458209d7015f5a4e3137335f244c54246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e76616c75652e2e53746f7261676556616c7565244c542450726566697824432456616c756524432451756572794b696e642443244f6e456d707479244754242475323024617324753230246672616d655f737570706f72742e2e73746f726167652e2e74797065732e2e53746f72616765456e7472794d657461646174614275696c6465722447542431346275696c645f6d6574616461746131376865623265633538366362313934663265458309475f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646531337573696e675f656e636f646564313768613630613131333835366232303766364584093f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f646531376866373263386264306633376532366462458509c2015f5a4e3139626f756e6465645f636f6c6c656374696f6e7331367765616b5f626f756e6465645f766563315f3131375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024626f756e6465645f636f6c6c656374696f6e732e2e7765616b5f626f756e6465645f7665632e2e5765616b426f756e646564566563244c54245424432453244754242447542439747970655f696e666f31376837303164363330393765353933386132458609475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376831313936306663346334646135633161458709475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376831623562656336363136343663626563458809475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376831643033326265636338323865306333458909475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376832346662326634333933653362343162458a09475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376832366333383762396631376139656363458b09475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376832623735333133343339346638353237458c09475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376834353536346165356364626339663031458d09475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376835616131386563386630363065383432458e09475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376836323262373135613565383538363633458f09475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376837303231316436326463313864646464459009475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376838343635316636316232393039316464459109475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376839333934316436373961663662373832459209475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376839346165383636643238376263393861459309475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376861303161366163343165306366643533459409475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376861353032313234663738316561373966459509475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376861373365306230313439343134336238459609475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376862613137393863316561316362313731459709475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376863396234613936653964613066323064459809475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376864636330323831373936336234646236459909475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376865333865346161343334646262613365459a09495f5a4e34345f244c54242452462454247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d7431376838656231313430646437633330646531459b095b5f5a4e34636f726533666d74336e756d34395f244c5424696d706c2475323024636f72652e2e666d742e2e44656275672475323024666f72247532302475382447542433666d7431376838366432656134356430313966386636459c095c5f5a4e34636f726533666d74336e756d35305f244c5424696d706c2475323024636f72652e2e666d742e2e44656275672475323024666f7224753230247536342447542433666d7431376832643962636565363333316266326131459d0993015f5a4e34636f72653370747231313264726f705f696e5f706c616365244c5424245246246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e436865636b4d6574616461746148617368244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242447542431376837393734336537333833613137326366459e09395f5a4e34636f726533707472323364726f705f696e5f706c616365244c542475382447542431376831363136316234616634343531613863459f093a5f5a4e34636f726533707472323464726f705f696e5f706c616365244c5424753634244754243137683064326333386435323663323762613145a009685f5a4e34636f726533707472343664726f705f696e5f706c616365244c5424616c6c6f632e2e7665632e2e566563244c54247538244754242447542431376838343231626263386233396462633533452e6c6c766d2e363831373539343630333232373130383833a1096b5f5a4e34636f726533707472343964726f705f696e5f706c616365244c5424616c6c6f632e2e737472696e672e2e46726f6d557466384572726f722447542431376836313730643138326564656234646266452e6c6c766d2e363831373539343630333232373130383833a2096f5f5a4e34636f726533707472353364726f705f696e5f706c616365244c54247061726974795f7363616c655f636f6465632e2e6572726f722e2e4572726f722447542431376866386339663061313434386130383237452e6c6c766d2e363831373539343630333232373130383833a309355f5a4e34636f72653970616e69636b696e6731336173736572745f6661696c65643137683265396463363139363632653336383145a409355f5a4e34636f72653970616e69636b696e6731336173736572745f6661696c65643137683265613064613961633530303030666145a509355f5a4e34636f72653970616e69636b696e6731336173736572745f6661696c65643137683432386631303536353936666339323945a609355f5a4e34636f72653970616e69636b696e6731336173736572745f6661696c65643137683630383339323439356531643537633845a709355f5a4e34636f72653970616e69636b696e6731336173736572745f6661696c65643137683736373833396463323062393162303445a809685f5a4e35616c6c6f633131636f6c6c656374696f6e73397665635f646571756532315665634465717565244c54245424432441244754243467726f7731376831343964363038613963613634393962452e6c6c766d2e363831373539343630333232373130383833a909625f5a4e36305f244c5424245246246d7574247532302454247532302461732475323024627335382e2e656e636f64652e2e456e636f6465546172676574244754243131656e636f64655f776974683137683261366439363839646263373632643945aa09765f5a4e36355f244c5424616c6c6f632e2e737472696e672e2e46726f6d557466384572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376835393137346462613138663336353532452e6c6c766d2e363831373539343630333232373130383833ab095e5f5a4e36355f244c542473705f636f72652e2e63727970746f2e2e5075626c69634572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686237303639316163613335313462346345ac095f5f5a4e36365f244c542473705f776569676874732e2e7765696768745f76322e2e576569676874247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683333346365376538623833366664373445ad09655f5a4e37325f244c54246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e4d6f6465247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683135396330356432356335393231666445ae096b5f5a4e37325f244c542473705f636f6e73656e7375735f626162652e2e6170702e2e5075626c696324753230246173247532302473657264652e2e7365722e2e53657269616c697a65244754243973657269616c697a653137686665326364396132633536316361613545af096f5f5a4e37335f244c542473705f636f6e73656e7375735f626162652e2e6170702e2e5075626c696324753230246173247532302473657264652e2e64652e2e446573657269616c697a65244754243131646573657269616c697a653137683939353933646237663863393633396145b0096d5f5a4e38305f244c54246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e4d6574616461746148617368247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686166616464303364353963333234666145b1099a015f5a4e32327375627374726174655f746573745f72756e74696d65315f39335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e436865636b53756273747261746543616c6c2447542439747970655f696e666f3137686539626263343730666536333261313745b209485f5a4e313274726163696e675f636f72653863616c6c736974653843616c6c736974653135707269766174655f747970655f69643137683563306264636438373465386266306445b309a8015f5a4e3133375f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e64656475705f736f727465645f697465722e2e4465647570536f7274656449746572244c54244b244324562443244924475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f7224475424346e6578743137683738623766316330343166653262353845b409af015f5a4e3133375f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e645f657373656e63652e2e457068656d6572616c244c5424532443244824475424247532302461732475323024686173685f64622e2e4173486173684442244c542448244324616c6c6f632e2e7665632e2e566563244c54247538244754242447542424475424313061735f686173685f64623137683733386430313837383861646531326145b509b3015f5a4e3133375f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e645f657373656e63652e2e457068656d6572616c244c5424532443244824475424247532302461732475323024686173685f64622e2e4173486173684442244c542448244324616c6c6f632e2e7665632e2e566563244c54247538244754242447542424475424313461735f686173685f64625f6d75743137686331333132653035396136626533366245b609cb015f5a4e3136375f244c542473657264652e2e64652e2e696d706c732e2e244c5424696d706c247532302473657264652e2e64652e2e446573657269616c697a652475323024666f722475323024616c6c6f632e2e7665632e2e566563244c54245424475424244754242e2e646573657269616c697a652e2e56656356697369746f72244c5424542447542424753230246173247532302473657264652e2e64652e2e56697369746f72244754243976697369745f7365713137683338333865383832656362346165663345b709475f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646531337573696e675f656e636f6465643137686133653531353133396262646461613545b809a0015f5a4e32327375627374726174655f746573745f72756e74696d65315f39395f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c2447542439656e636f64655f746f3137686539363262326538393264613935626545b909585f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f646531376834343338643261363832646166643934452e6c6c766d2e32383034353134343737303732373934353638ba093f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137683830336232383263646337663632666445bb09465f5a4e34315f244c54245424753230246173247532302473657264652e2e64652e2e45787065637465642447542433666d743137683266313238633266393938656161616645bc09465f5a4e34315f244c54245424753230246173247532302473657264652e2e64652e2e45787065637465642447542433666d743137683663393365316631306530376535306445bd09465f5a4e34315f244c54245424753230246173247532302473657264652e2e64652e2e45787065637465642447542433666d743137683830653964343161386331306139636345be09465f5a4e34315f244c54245424753230246173247532302473657264652e2e64652e2e45787065637465642447542433666d743137686164373265356439313463346166366145bf09465f5a4e34315f244c54245424753230246173247532302473657264652e2e64652e2e45787065637465642447542433666d743137686531393635393636343265396564303645c009465f5a4e34315f244c54245424753230246173247532302473657264652e2e64652e2e45787065637465642447542433666d743137686563333138356165653831643066653645c109475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686161373834363830643961643162383545c209635f5a4e34355f244c5424244c502424525024247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376834303566303661613534323663666363452e6c6c766d2e32383034353134343737303732373934353638c309515f5a4e34636f7265336f70733866756e6374696f6e35466e4d75743863616c6c5f6d757431376864643638666565636462316135306133452e6c6c766d2e32383034353134343737303732373934353638c409735f5a4e34636f7265336f70733866756e6374696f6e36466e4f6e6365343063616c6c5f6f6e636524753762242475376224767461626c652e7368696d2475376424247537642431376830623662356162393134373765373065452e6c6c766d2e32383034353134343737303732373934353638c509b9015f5a4e34636f72653370747231323564726f705f696e5f706c616365244c542473657264652e2e64652e2e696d706c732e2e244c5424696d706c247532302473657264652e2e64652e2e446573657269616c697a652475323024666f722475323024753634244754242e2e646573657269616c697a652e2e5072696d697469766556697369746f722447542431376832633438373739393063633837336265452e6c6c766d2e32383034353134343737303732373934353638c609ce015f5a4e34636f72653370747231373164726f705f696e5f706c616365244c54246d656d6f72795f64622e2e4d656d6f72794442244c542473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362443246d656d6f72795f64622e2e50726566697865644b6579244c542473705f72756e74696d652e2e7472616974732e2e426c616b6554776f32353624475424244324616c6c6f632e2e7665632e2e566563244c542475382447542424475424244754243137686461333031613034303666356666306445c709a5025f5a4e34636f72653370747232353864726f705f696e5f706c616365244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e645f657373656e63652e2e457068656d6572616c244c54246d656d6f72795f64622e2e4d656d6f72794442244c542473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362443246d656d6f72795f64622e2e486173684b6579244c542473705f72756e74696d652e2e7472616974732e2e426c616b6554776f32353624475424244324616c6c6f632e2e7665632e2e566563244c54247538244754242447542424432473705f72756e74696d652e2e7472616974732e2e426c616b6554776f32353624475424244754243137683265326562643765346433373065386545c80992045f5a4e34636f72653370747234373064726f705f696e5f706c616365244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e645f657373656e63652e2e547269654261636b656e64457373656e6365244c54246d656d6f72795f64622e2e4d656d6f72794442244c542473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362443246d656d6f72795f64622e2e486173684b6579244c542473705f72756e74696d652e2e7472616974732e2e426c616b6554776f32353624475424244324616c6c6f632e2e7665632e2e566563244c54247538244754242447542424432473705f72756e74696d652e2e7472616974732e2e426c616b6554776f32353624432473705f73746174655f6d616368696e652e2e747269655f6261636b656e642e2e556e696d706c656d656e746564436163686550726f7669646572244c542473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362447542424432473705f73746174655f6d616368696e652e2e747269655f6261636b656e642e2e556e696d706c656d656e7465645265636f7264657250726f7669646572244c542473705f72756e74696d652e2e7472616974732e2e426c616b6554776f32353624475424244754242447542431376864343233326631353931363835616634452e6c6c766d2e32383034353134343737303732373934353638c909735f5a4e34636f726533707472353664726f705f696e5f706c616365244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c2447542431376865643735326330326539353332656332452e6c6c766d2e32383034353134343737303732373934353638ca09695f5a4e34636f726533707472373164726f705f696e5f706c616365244c542473705f747269652e2e6572726f722e2e4572726f72244c54247072696d69746976655f74797065732e2e4832353624475424244754243137683461363365306537646330353230356545cb096c5f5a4e34636f726533707472373464726f705f696e5f706c616365244c5424636f72652e2e6f7074696f6e2e2e4f7074696f6e244c5424616c6c6f632e2e7665632e2e566563244c542475382447542424475424244754243137683465633563653962343563363036306145cc09635f5a4e37305f244c5424636f72652e2e726573756c742e2e526573756c74244c5424542443244524475424247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683433376161653961336434353232303345cd096c5f5a4e37395f244c542474726163696e675f636f72652e2e6669656c642e2e446973706c617956616c7565244c54245424475424247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683731663366343637386639633833356145ce09305f5a4e3773705f74726965313564656c74615f747269655f726f6f743137683132363765636536363430333138393245cf09305f5a4e3773705f74726965313564656c74615f747269655f726f6f743137686234366236343335666436393337363645d009365f5a4e3773705f7472696532316368696c645f64656c74615f747269655f726f6f743137683336613536633230386630316331636345d109365f5a4e3773705f7472696532316368696c645f64656c74615f747269655f726f6f743137686131623063316635363531393665303445d209735f5a4e38365f244c542473705f747269652e2e4b657953706163656444424d7574244c542444422443244824475424247532302461732475323024686173685f64622e2e486173684442244c542448244324542447542424475424336765743137686336356265633939363937353031303845d309765f5a4e38365f244c542473705f747269652e2e4b657953706163656444424d7574244c542444422443244824475424247532302461732475323024686173685f64622e2e486173684442244c54244824432454244754242447542436696e736572743137683331323562383037323935343434366145d409765f5a4e38365f244c542473705f747269652e2e4b657953706163656444424d7574244c542444422443244824475424247532302461732475323024686173685f64622e2e486173684442244c5424482443245424475424244754243672656d6f76653137686662363563663966306264626433363945d509775f5a4e38365f244c542473705f747269652e2e4b657953706163656444424d7574244c542444422443244824475424247532302461732475323024686173685f64622e2e486173684442244c54244824432454244754242447542437656d706c6163653137683161613861333239633037343236366445d609785f5a4e38365f244c542473705f747269652e2e4b657953706163656444424d7574244c542444422443244824475424247532302461732475323024686173685f64622e2e486173684442244c54244824432454244754242447542438636f6e7461696e733137686337663665353265646334323730376645d7097d5f5a4e38385f244c54246d656d6f72795f64622e2e4d656d6f72794442244c5424482443244b462443245424475424247532302461732475323024686173685f64622e2e4173486173684442244c542448244324542447542424475424313061735f686173685f64623137686265373334373938313031643634373545d80981015f5a4e38385f244c54246d656d6f72795f64622e2e4d656d6f72794442244c5424482443244b462443245424475424247532302461732475323024686173685f64622e2e4173486173684442244c542448244324542447542424475424313461735f686173685f64625f6d75743137683234396363393830393034333434663145d9097d5f5a4e38385f244c542473705f747269652e2e4b657953706163656444424d7574244c542444422443244824475424247532302461732475323024686173685f64622e2e4173486173684442244c542448244324542447542424475424313061735f686173685f64623137683932383034353935303636636632373945da0981015f5a4e38385f244c542473705f747269652e2e4b657953706163656444424d7574244c542444422443244824475424247532302461732475323024686173685f64622e2e4173486173684442244c542448244324542447542424475424313461735f686173685f64625f6d75743137683038386363646330333432396164343645db09795f5a4e38395f244c542474726163696e675f636f72652e2e6669656c642e2e446973706c617956616c7565244c5424542447542424753230246173247532302474726163696e675f636f72652e2e6669656c642e2e56616c756524475424367265636f72643137683632313736663931616432666139396645dc097e5f5a4e39325f244c542474726163696e675f636f72652e2e63616c6c736974652e2e44656661756c7443616c6c7369746524753230246173247532302474726163696e675f636f72652e2e63616c6c736974652e2e43616c6c7369746524475424386d657461646174613137683032373862373566376265346534323345dd0989015f5a4e39385f244c54247375627374726174655f746573745f72756e74696d652e2e436865636b53756273747261746543616c6c24753230246173247532302473705f72756e74696d652e2e7472616974732e2e5369676e6564457874656e73696f6e2447542431327072655f64697370617463683137683730356533303231333030653331643745de095b5f5a4e32327375627374726174655f746573745f72756e74696d65313762656e63686d61726b5f6164645f6f6e6531376864333461666361623764633464303137452e6c6c766d2e32383034353134343737303732373934353638df09a2015f5a4e32327375627374726174655f746573745f72756e74696d65315f3130305f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e5472616e73666572446174612447542439656e636f64655f746f3137686431396234356235633363323632386445e00994015f5a4e32327375627374726174655f746573745f72756e74696d65315f38375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e5472616e73666572446174612447542439747970655f696e666f3137686230363038376636353061666534373845e1097f5f5a4e39385f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65426c6f636b57656967687473247532302461732475323024626f756e6465645f636f6c6c656374696f6e732e2e476574244c54245f492447542424475424336765743137683763333838653230303264616462323045e209455f5a4e32327375627374726174655f746573745f72756e74696d65313153657373696f6e4b6579733867656e65726174653137686137353433623064623231663463323145e309525f5a4e32327375627374726174655f746573745f72756e74696d65313153657373696f6e4b6579733230696e746f5f7261775f7075626c69635f6b6579733137686166383865613065633061663336376445e4095d5f5a4e32327375627374726174655f746573745f72756e74696d653752756e74696d6531316d657461646174615f697231376833376263393537633561663737366534452e6c6c766d2e32383034353134343737303732373934353638e509da075f5a4e3933385f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d6524753230246173247532302473705f7472616e73616374696f6e5f706f6f6c2e2e72756e74696d655f6170692e2e72756e74696d655f6465636c5f666f725f7461676765645f7472616e73616374696f6e5f71756575652e2e5461676765645472616e73616374696f6e51756575655633244c542473705f72756e74696d652e2e67656e657269632e2e626c6f636b2e2e426c6f636b244c542473705f72756e74696d652e2e67656e657269632e2e6865616465722e2e486561646572244c542475363424432473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362447542424432473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432473705f636f72652e2e737232353531392e2e537232353531395075626c6963546167244754242443247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c24432473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f244324244c502473705f636f72652e2e63727970746f5f62797465732e2e7369676e61747572655f62797465732e2e5369676e617475726554616724432473705f636f72652e2e737232353531392e2e537232353531395461672452502424475424244324244c50246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f6e6f6e63652e2e436865636b4e6f6e6365244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f7765696768742e2e436865636b576569676874244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443247375627374726174655f746573745f72756e74696d652e2e436865636b53756273747261746543616c6c2443246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e436865636b4d6574616461746148617368244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242452502424475424244754242447542424475424323076616c69646174655f7472616e73616374696f6e3137686363623362633033333064313535353745e609a4075f5a4e3839375f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652475323024617324753230247375627374726174655f746573745f72756e74696d652e2e72756e74696d655f6465636c5f666f725f746573745f6170692e2e546573744150495632244c542473705f72756e74696d652e2e67656e657269632e2e626c6f636b2e2e426c6f636b244c542473705f72756e74696d652e2e67656e657269632e2e6865616465722e2e486561646572244c542475363424432473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362447542424432473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432473705f636f72652e2e737232353531392e2e537232353531395075626c6963546167244754242443247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c24432473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f244324244c502473705f636f72652e2e63727970746f5f62797465732e2e7369676e61747572655f62797465732e2e5369676e617475726554616724432473705f636f72652e2e737232353531392e2e537232353531395461672452502424475424244324244c50246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f6e6f6e63652e2e436865636b4e6f6e6365244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f7765696768742e2e436865636b576569676874244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443247375627374726174655f746573745f72756e74696d652e2e436865636b53756273747261746543616c6c2443246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e436865636b4d6574616461746148617368244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242452502424475424244754242447542424475424387573655f747269653137686363326261656138653839373635656445e709b0075f5a4e3839375f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652475323024617324753230247375627374726174655f746573745f72756e74696d652e2e72756e74696d655f6465636c5f666f725f746573745f6170692e2e546573744150495632244c542473705f72756e74696d652e2e67656e657269632e2e626c6f636b2e2e426c6f636b244c542473705f72756e74696d652e2e67656e657269632e2e6865616465722e2e486561646572244c542475363424432473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362447542424432473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432473705f636f72652e2e737232353531392e2e537232353531395075626c6963546167244754242443247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c24432473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f244324244c502473705f636f72652e2e63727970746f5f62797465732e2e7369676e61747572655f62797465732e2e5369676e617475726554616724432473705f636f72652e2e737232353531392e2e537232353531395461672452502424475424244324244c50246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f6e6f6e63652e2e436865636b4e6f6e6365244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f7765696768742e2e436865636b576569676874244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443247375627374726174655f746573745f72756e74696d652e2e436865636b53756273747261746543616c6c2443246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e436865636b4d6574616461746148617368244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652447542424525024244754242447542424475424244754243139746573745f656432353531395f63727970746f3137683436633632326563353036636666653245e809b0075f5a4e3839375f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652475323024617324753230247375627374726174655f746573745f72756e74696d652e2e72756e74696d655f6465636c5f666f725f746573745f6170692e2e546573744150495632244c542473705f72756e74696d652e2e67656e657269632e2e626c6f636b2e2e426c6f636b244c542473705f72756e74696d652e2e67656e657269632e2e6865616465722e2e486561646572244c542475363424432473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362447542424432473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432473705f636f72652e2e737232353531392e2e537232353531395075626c6963546167244754242443247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c24432473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f244324244c502473705f636f72652e2e63727970746f5f62797465732e2e7369676e61747572655f62797465732e2e5369676e617475726554616724432473705f636f72652e2e737232353531392e2e537232353531395461672452502424475424244324244c50246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f6e6f6e63652e2e436865636b4e6f6e6365244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f7765696768742e2e436865636b576569676874244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443247375627374726174655f746573745f72756e74696d652e2e436865636b53756273747261746543616c6c2443246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e436865636b4d6574616461746148617368244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652447542424525024244754242447542424475424244754243139746573745f737232353531395f63727970746f3137683436333437616266653366313064613545e909ae075f5a4e3839375f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652475323024617324753230247375627374726174655f746573745f72756e74696d652e2e72756e74696d655f6465636c5f666f725f746573745f6170692e2e546573744150495632244c542473705f72756e74696d652e2e67656e657269632e2e626c6f636b2e2e426c6f636b244c542473705f72756e74696d652e2e67656e657269632e2e6865616465722e2e486561646572244c542475363424432473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362447542424432473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432473705f636f72652e2e737232353531392e2e537232353531395075626c6963546167244754242443247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c24432473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f244324244c502473705f636f72652e2e63727970746f5f62797465732e2e7369676e61747572655f62797465732e2e5369676e617475726554616724432473705f636f72652e2e737232353531392e2e537232353531395461672452502424475424244324244c50246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f6e6f6e63652e2e436865636b4e6f6e6365244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f7765696768742e2e436865636b576569676874244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443247375627374726174655f746573745f72756e74696d652e2e436865636b53756273747261746543616c6c2443246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e436865636b4d6574616461746148617368244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652447542424525024244754242447542424475424244754243137746573745f65636473615f63727970746f3137683437646132616337383833366533353745ea09a9075f5a4e3839375f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652475323024617324753230247375627374726174655f746573745f72756e74696d652e2e72756e74696d655f6465636c5f666f725f746573745f6170692e2e546573744150495632244c542473705f72756e74696d652e2e67656e657269632e2e626c6f636b2e2e426c6f636b244c542473705f72756e74696d652e2e67656e657269632e2e6865616465722e2e486561646572244c542475363424432473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362447542424432473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432473705f636f72652e2e737232353531392e2e537232353531395075626c6963546167244754242443247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c24432473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f244324244c502473705f636f72652e2e63727970746f5f62797465732e2e7369676e61747572655f62797465732e2e5369676e617475726554616724432473705f636f72652e2e737232353531392e2e537232353531395461672452502424475424244324244c50246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f6e6f6e63652e2e436865636b4e6f6e6365244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f7765696768742e2e436865636b576569676874244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443247375627374726174655f746573745f72756e74696d652e2e436865636b53756273747261746543616c6c2443246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e436865636b4d6574616461746148617368244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652447542424525024244754242447542424475424244754243132746573745f73746f726167653137686636646136396433303663323262306445eb09a9075f5a4e3839375f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652475323024617324753230247375627374726174655f746573745f72756e74696d652e2e72756e74696d655f6465636c5f666f725f746573745f6170692e2e546573744150495632244c542473705f72756e74696d652e2e67656e657269632e2e626c6f636b2e2e426c6f636b244c542473705f72756e74696d652e2e67656e657269632e2e6865616465722e2e486561646572244c542475363424432473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362447542424432473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432473705f636f72652e2e737232353531392e2e537232353531395075626c6963546167244754242443247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c24432473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f244324244c502473705f636f72652e2e63727970746f5f62797465732e2e7369676e61747572655f62797465732e2e5369676e617475726554616724432473705f636f72652e2e737232353531392e2e537232353531395461672452502424475424244324244c50246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f6e6f6e63652e2e436865636b4e6f6e6365244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f7765696768742e2e436865636b576569676874244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443247375627374726174655f746573745f72756e74696d652e2e436865636b53756273747261746543616c6c2443246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e436865636b4d6574616461746148617368244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652447542424525024244754242447542424475424244754243132746573745f7769746e6573733137683138616333363238346534396462666645ec09b4075f5a4e3839375f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652475323024617324753230247375627374726174655f746573745f72756e74696d652e2e72756e74696d655f6465636c5f666f725f746573745f6170692e2e546573744150495632244c542473705f72756e74696d652e2e67656e657269632e2e626c6f636b2e2e426c6f636b244c542473705f72756e74696d652e2e67656e657269632e2e6865616465722e2e486561646572244c542475363424432473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362447542424432473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432473705f636f72652e2e737232353531392e2e537232353531395075626c6963546167244754242443247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c24432473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f244324244c502473705f636f72652e2e63727970746f5f62797465732e2e7369676e61747572655f62797465732e2e5369676e617475726554616724432473705f636f72652e2e737232353531392e2e537232353531395461672452502424475424244324244c50246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f6e6f6e63652e2e436865636b4e6f6e6365244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f7765696768742e2e436865636b576569676874244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443247375627374726174655f746573745f72756e74696d652e2e436865636b53756273747261746543616c6c2443246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e436865636b4d6574616461746148617368244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652447542424525024244754242447542424475424244754243233746573745f6d756c7469706c655f617267756d656e74733137683333386336323461363132343833363345ed09a9075f5a4e3839375f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652475323024617324753230247375627374726174655f746573745f72756e74696d652e2e72756e74696d655f6465636c5f666f725f746573745f6170692e2e546573744150495632244c542473705f72756e74696d652e2e67656e657269632e2e626c6f636b2e2e426c6f636b244c542473705f72756e74696d652e2e67656e657269632e2e6865616465722e2e486561646572244c542475363424432473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362447542424432473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432473705f636f72652e2e737232353531392e2e537232353531395075626c6963546167244754242443247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c24432473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f244324244c502473705f636f72652e2e63727970746f5f62797465732e2e7369676e61747572655f62797465732e2e5369676e617475726554616724432473705f636f72652e2e737232353531392e2e537232353531395461672452502424475424244324244c50246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f6e6f6e63652e2e436865636b4e6f6e6365244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f7765696768742e2e436865636b576569676874244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443247375627374726174655f746573745f72756e74696d652e2e436865636b53756273747261746543616c6c2443246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e436865636b4d6574616461746148617368244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652447542424525024244754242447542424475424244754243132646f5f74726163655f6c6f673137683938323835313338653265303636613045ee09a5075f5a4e3839325f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d6524753230246173247532302473705f636f6e73656e7375735f626162652e2e72756e74696d655f6465636c5f666f725f626162655f6170692e2e426162654170695632244c542473705f72756e74696d652e2e67656e657269632e2e626c6f636b2e2e426c6f636b244c542473705f72756e74696d652e2e67656e657269632e2e6865616465722e2e486561646572244c542475363424432473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362447542424432473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432473705f636f72652e2e737232353531392e2e537232353531395075626c6963546167244754242443247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c24432473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f244324244c502473705f636f72652e2e63727970746f5f62797465732e2e7369676e61747572655f62797465732e2e5369676e617475726554616724432473705f636f72652e2e737232353531392e2e537232353531395461672452502424475424244324244c50246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f6e6f6e63652e2e436865636b4e6f6e6365244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f7765696768742e2e436865636b576569676874244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443247375627374726174655f746573745f72756e74696d652e2e436865636b53756273747261746543616c6c2443246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e436865636b4d6574616461746148617368244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652447542424525024244754242447542424475424244754243133636f6e66696775726174696f6e3137686239666332353039636638333064643745ef09c5075f5a4e3839325f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d6524753230246173247532302473705f636f6e73656e7375735f626162652e2e72756e74696d655f6465636c5f666f725f626162655f6170692e2e426162654170695632244c542473705f72756e74696d652e2e67656e657269632e2e626c6f636b2e2e426c6f636b244c542473705f72756e74696d652e2e67656e657269632e2e6865616465722e2e486561646572244c542475363424432473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362447542424432473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432473705f636f72652e2e737232353531392e2e537232353531395075626c6963546167244754242443247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c24432473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f244324244c502473705f636f72652e2e63727970746f5f62797465732e2e7369676e61747572655f62797465732e2e5369676e617475726554616724432473705f636f72652e2e737232353531392e2e537232353531395461672452502424475424244324244c50246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f6e6f6e63652e2e436865636b4e6f6e6365244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f7765696768742e2e436865636b576569676874244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443247375627374726174655f746573745f72756e74696d652e2e436865636b53756273747261746543616c6c2443246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e436865636b4d6574616461746148617368244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d6524475424245250242447542424475424244754242447542434357375626d69745f7265706f72745f65717569766f636174696f6e5f756e7369676e65645f65787472696e7369633137686630396464613362373561303561373845f009b6075f5a4e3930375f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d6524753230246173247532302473705f6f6666636861696e2e2e72756e74696d655f6465636c5f666f725f6f6666636861696e5f776f726b65725f6170692e2e4f6666636861696e576f726b65724170695632244c542473705f72756e74696d652e2e67656e657269632e2e626c6f636b2e2e426c6f636b244c542473705f72756e74696d652e2e67656e657269632e2e6865616465722e2e486561646572244c542475363424432473705f72756e74696d652e2e7472616974732e2e426c616b6554776f3235362447542424432473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c542473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f24432473705f636f72652e2e737232353531392e2e537232353531395075626c6963546167244754242443247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c24432473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c54245f244324244c502473705f636f72652e2e63727970746f5f62797465732e2e7369676e61747572655f62797465732e2e5369676e617475726554616724432473705f636f72652e2e737232353531392e2e537232353531395461672452502424475424244324244c50246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f6e6f6e63652e2e436865636b4e6f6e6365244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f7765696768742e2e436865636b576569676874244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443247375627374726174655f746573745f72756e74696d652e2e436865636b53756273747261746543616c6c2443246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e436865636b4d6574616461746148617368244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d6524475424245250242447542424475424244754242447542431356f6666636861696e5f776f726b65723137683366626135393464643266633963333945f109cf015f5a4e3132315f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d654f726967696e247532302461732475323024636f72652e2e636f6e766572742e2e46726f6d244c54247375627374726174655f746573745f72756e74696d652e2e4f726967696e43616c6c657224475424244754243466726f6d32385f24753762242475376224636c6f737572652475376424247537642431376864303636643239376139373430323738452e6c6c766d2e32383034353134343737303732373934353638f209ea015f5a4e3230335f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d654f726967696e247532302461732475323024636f72652e2e636f6e766572742e2e46726f6d244c5424636f72652e2e6f7074696f6e2e2e4f7074696f6e244c5424244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d652475323024617324753230246672616d655f73797374656d2e2e70616c6c65742e2e436f6e666967244754242e2e4163636f756e7449642447542424475424244754243466726f6d3137683364643932613435356237396637313145f3097a5f5a4e38385f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c24753230246173247532302473705f72756e74696d652e2e7472616974732e2e446973706174636861626c65244754243864697370617463683137686632333937666433383236326439356245f40993015f5a4e32327375627374726174655f746573745f72756e74696d65315f38365f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c2447542439747970655f696e666f3137683239623966616665666232666161333945f5098f015f5a4e32327375627374726174655f746573745f72756e74696d65315f38325f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e52756e74696d652447542439747970655f696e666f3137686462346630393030666665363439346445f60994015f5a4e32327375627374726174655f746573745f72756e74696d65315f38375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e52756e74696d654576656e742447542439747970655f696e666f3137683262393366343361653934623132363245f70994015f5a4e32327375627374726174655f746573745f72756e74696d65315f38375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e52756e74696d654572726f722447542439747970655f696e666f3137683930333133386565633964303937346645f80991015f5a4e3131335f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d6547656e65736973436f6e6669672475323024617324753230246672616d655f737570706f72742e2e7472616974732e2e686f6f6b732e2e4275696c6447656e65736973436f6e66696724475424356275696c643137683664396534353630653431326232623045f90984015f5a4e38385f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d6524753230246173247532302473705f72756e74696d652e2e7472616974732e2e56616c6964617465556e7369676e656424475424313776616c69646174655f756e7369676e65643137686664623139316535336263306235616245fa0999015f5a4e32327375627374726174655f746573745f72756e74696d65315f39325f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e52756e74696d65486f6c64526561736f6e2447542439747970655f696e666f3137686566363564613130363437663938646345fb099d015f5a4e32327375627374726174655f746573745f72756e74696d65315f39365f244c5424696d706c247532302473657264652e2e7365722e2e53657269616c697a652475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e52756e74696d6547656e65736973436f6e666967244754243973657269616c697a653137683435363938366661383636353863336245fc09e3015f5a4e3139315f244c54247375627374726174655f746573745f72756e74696d652e2e5f2e2e244c5424696d706c247532302473657264652e2e64652e2e446573657269616c697a652475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e52756e74696d6547656e65736973436f6e666967244754242e2e646573657269616c697a652e2e5f5f4669656c6456697369746f7224753230246173247532302473657264652e2e64652e2e56697369746f72244754243976697369745f7374723137683437396439643336333362656263306445fd099d015f5a4e32327375627374726174655f746573745f72756e74696d65315f39395f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f64652475323024666f7224753230247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c24475424366465636f64653137683461376632346664356331333266393945fe09655f5a4e37325f244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683839326636663537303635343431623145ff0985015f5a4e3130325f244c5424636f72652e2e697465722e2e61646170746572732e2e6d61702e2e4d6170244c5424492443244624475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f722447542434666f6c643137683030396663353565363965313064383945800a85015f5a4e3130325f244c5424636f72652e2e697465722e2e61646170746572732e2e6d61702e2e4d6170244c5424492443244624475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f722447542434666f6c643137683836396235316235383536663236303245810a89015f5a4e3130365f244c5424636f72652e2e697465722e2e61646170746572732e2e47656e657269635368756e74244c5424492443245224475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f7224475424346e6578743137686561386362636263336262306237336145820a8d015f5a4e3130365f244c5424636f72652e2e697465722e2e61646170746572732e2e47656e657269635368756e74244c5424492443245224475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f7224475424387472795f666f6c643137683561663635316663663564303763333645830a415f5a4e37747269655f6462346e6f646531304e6f646548616e646c653135746f5f6f776e65645f68616e646c653137683231663965393066326537633534383145840a6c5f5a4e34636f726533707472373464726f705f696e5f706c616365244c5424747269655f64622e2e6e6f64652e2e4e6f64654f776e6564244c54247072696d69746976655f74797065732e2e4832353624475424244754243137683462386365613062656464353664383245850a695f5a4e34636f726533707472373164726f705f696e5f706c616365244c542473705f747269652e2e6572726f722e2e4572726f72244c54247072696d69746976655f74797065732e2e4832353624475424244754243137683461363365306537646330353230356545860a8d015f5a4e3130365f244c5424636f72652e2e697465722e2e61646170746572732e2e47656e657269635368756e74244c5424492443245224475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f7224475424387472795f666f6c643137686235656135663436353739313338383145870a415f5a4e37747269655f6462346e6f646531304e6f646548616e646c653135746f5f6f776e65645f68616e646c653137686233653961643936303138643965373845880a8d015f5a4e3130365f244c5424636f72652e2e697465722e2e61646170746572732e2e47656e657269635368756e74244c5424492443245224475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f7224475424387472795f666f6c643137686532656131653065636163303235326645890a8d015f5a4e3130365f244c5424636f72652e2e697465722e2e61646170746572732e2e47656e657269635368756e74244c5424492443245224475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f7224475424387472795f666f6c6431376866636633353363663464356433323264458a0a4a5f5a4e313073705f72756e74696d6536747261697473313656616c6964617465556e7369676e656431327072655f646973706174636831376839343631383135343031346431633032458b0a95015f5a4e31326672616d655f73797374656d35355f244c5424696d706c24753230246672616d655f73797374656d2e2e70616c6c65742e2e50616c6c6574244c5424542447542424475424323776616c69646174655f617574686f72697a65645f7570677261646531376861643931366330346635393236633565452e6c6c766d2e31373132343635353732373637363833303235308c0a91015f5a4e3131335f244c54246672616d655f73797374656d2e2e70616c6c65742e2e47656e65736973436f6e666967244c542454244754242475323024617324753230246672616d655f737570706f72742e2e7472616974732e2e686f6f6b732e2e4275696c6447656e65736973436f6e66696724475424356275696c6431376834303339323931613632323230313734458d0a93015f5a4e3131365f244c542470616c6c65745f62616c616e6365732e2e696d706c5f63757272656e63792e2e696d62616c616e6365732e2e4e65676174697665496d62616c616e6365244c5424542443244924475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f7031376838646237336362366362366132636638458e0a8f015f5a4e31326672616d655f73797374656d35355f244c5424696d706c24753230246672616d655f73797374656d2e2e70616c6c65742e2e50616c6c6574244c542454244754242447542432316465706f7369745f6576656e745f696e646578656431376863356236303733346338323866623062452e6c6c766d2e31373132343635353732373637363833303235308f0a93015f5a4e3131365f244c542470616c6c65745f62616c616e6365732e2e696d706c5f63757272656e63792e2e696d62616c616e6365732e2e506f736974697665496d62616c616e6365244c5424542443244924475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137683330626439373131323466383232333045900a385f5a4e313170616c6c65745f626162653138636f6d707574655f72616e646f6d6e6573733137683966376133623661303862303739346345910aa6015f5a4e3132305f244c5424244c50245475706c65456c656d656e74302443245475706c65456c656d656e74312443245475706c65456c656d656e7432245250242475323024617324753230246672616d655f737570706f72742e2e7472616974732e2e686f6f6b732e2e4f6e52756e74696d65557067726164652447542431386f6e5f72756e74696d655f757067726164653137683866656666656336333862636233383545920aa7015f5a4e3132395f244c5424244c50245475706c65456c656d656e74302443245475706c65456c656d656e74312443245475706c65456c656d656e74322443245475706c65456c656d656e7433245250242475323024617324753230246672616d655f737570706f72742e2e7472616974732e2e686f6f6b732e2e4f6e47656e657369732447542431306f6e5f67656e657369733137683838383031663964353934323735643045930ab0025f5a4e31326672616d655f73797374656d3234355f244c5424696d706c24753230246672616d655f737570706f72742e2e7472616974732e2e73746f7265645f6d61702e2e53746f7265644d6170244c5424244c5424542475323024617324753230246672616d655f73797374656d2e2e70616c6c65742e2e436f6e666967244754242e2e4163636f756e744964244324244c5424542475323024617324753230246672616d655f73797374656d2e2e70616c6c65742e2e436f6e666967244754242e2e4163636f756e7444617461244754242475323024666f7224753230246672616d655f73797374656d2e2e70616c6c65742e2e50616c6c6574244c542454244754242447542431377472795f6d75746174655f6578697374733137683533323961643038616539383936306145940ab0025f5a4e31326672616d655f73797374656d3234355f244c5424696d706c24753230246672616d655f737570706f72742e2e7472616974732e2e73746f7265645f6d61702e2e53746f7265644d6170244c5424244c5424542475323024617324753230246672616d655f73797374656d2e2e70616c6c65742e2e436f6e666967244754242e2e4163636f756e744964244324244c5424542475323024617324753230246672616d655f73797374656d2e2e70616c6c65742e2e436f6e666967244754242e2e4163636f756e7444617461244754242475323024666f7224753230246672616d655f73797374656d2e2e70616c6c65742e2e50616c6c6574244c542454244754242447542431377472795f6d75746174655f6578697374733137686231373635333836303366643030316545950ab0025f5a4e31326672616d655f73797374656d3234355f244c5424696d706c24753230246672616d655f737570706f72742e2e7472616974732e2e73746f7265645f6d61702e2e53746f7265644d6170244c5424244c5424542475323024617324753230246672616d655f73797374656d2e2e70616c6c65742e2e436f6e666967244754242e2e4163636f756e744964244324244c5424542475323024617324753230246672616d655f73797374656d2e2e70616c6c65742e2e436f6e666967244754242e2e4163636f756e7444617461244754242475323024666f7224753230246672616d655f73797374656d2e2e70616c6c65742e2e50616c6c6574244c542454244754242447542431377472795f6d75746174655f6578697374733137686462356435363266346438633962313845960a6a5f5a4e31326672616d655f73797374656d35355f244c5424696d706c24753230246672616d655f73797374656d2e2e70616c6c65742e2e50616c6c6574244c54245424475424244754243130696e697469616c697a653137683730346430333563623465363335313545970a6c5f5a4e31326672616d655f73797374656d35355f244c5424696d706c24753230246672616d655f73797374656d2e2e70616c6c65742e2e50616c6c6574244c5424542447542424475424313263616e5f7365745f636f64653137686264396233333131373765383265333045980a6c5f5a4e31326672616d655f73797374656d35355f244c5424696d706c24753230246672616d655f73797374656d2e2e70616c6c65742e2e50616c6c6574244c5424542447542424475424313272657365745f6576656e74733137686365356438343733373938666663616245990a765f5a4e31326672616d655f73797374656d35355f244c5424696d706c24753230246672616d655f73797374656d2e2e70616c6c65742e2e50616c6c6574244c542454244754242447542432326e6f74655f6170706c6965645f65787472696e73696331376831373337383933343937626563383362459a0a5c5f5a4e34636f726533666d74336e756d35305f244c5424696d706c2475323024636f72652e2e666d742e2e44656275672475323024666f7224753230247536342447542433666d7431376832643962636565363333316266326131459b0a785f5a4e31326672616d655f73797374656d35355f244c5424696d706c24753230246672616d655f73797374656d2e2e70616c6c65742e2e50616c6c6574244c542454244754242447542432346e6f74655f66696e69736865645f65787472696e7369637331376832623939323564376638376365393536459c0a7a5f5a4e31326672616d655f73797374656d35355f244c5424696d706c24753230246672616d655f73797374656d2e2e70616c6c65742e2e50616c6c6574244c54245424475424244754243236646f5f6170706c795f617574686f72697a655f7570677261646531376835396266666130383332633039383633459d0a675f5a4e31326672616d655f73797374656d35355f244c5424696d706c24753230246672616d655f73797374656d2e2e70616c6c65742e2e50616c6c6574244c54245424475424244754243866696e616c697a6531376864316236633764323436616530376661459e0a4f5f5a4e31326672616d655f73797374656d3670616c6c6574313550616c6c6574244c54245424475424313673746f726167655f6d6574616461746131376832343534393565633831326334666639459f0a585f5a4e31326672616d655f73797374656d3670616c6c6574313550616c6c6574244c54245424475424323570616c6c65745f636f6e7374616e74735f6d657461646174613137683362646363376130386336383366343845a00a9f015f5a4e31326672616d655f73797374656d3670616c6c6574315f3130305f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f73797374656d2e2e70616c6c65742e2e4576656e74244c542454244754242447542439656e636f64655f746f3137683338663536306335353431353661386445a10a9f015f5a4e31326672616d655f73797374656d3670616c6c6574315f3130305f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f73797374656d2e2e70616c6c65742e2e4576656e74244c54245424475424244754243973697a655f68696e743137683065623633323464383265323664646345a20a8a015f5a4e31326672616d655f73797374656d3670616c6c6574315f38345f244c5424696d706c2475323024636f72652e2e636c6f6e652e2e436c6f6e652475323024666f7224753230246672616d655f73797374656d2e2e70616c6c65742e2e43616c6c244c542454244754242447542435636c6f6e653137683663663436393636323963623833616445a30a90015f5a4e31326672616d655f73797374656d3670616c6c6574315f38365f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f73797374656d2e2e70616c6c65742e2e43616c6c244c542454244754242447542439747970655f696e666f3137683536363733373163666238613534373545a40a91015f5a4e31326672616d655f73797374656d3670616c6c6574315f38375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f73797374656d2e2e70616c6c65742e2e4572726f72244c542454244754242447542439747970655f696e666f3137683361653831346237383064373962623245a50a91015f5a4e31326672616d655f73797374656d3670616c6c6574315f38375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f73797374656d2e2e70616c6c65742e2e4576656e74244c542454244754242447542439747970655f696e666f3137683137343038623832396561356134393845a60a9a015f5a4e31326672616d655f73797374656d3670616c6c6574315f39395f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f64652475323024666f7224753230246672616d655f73797374656d2e2e70616c6c65742e2e43616c6c244c5424542447542424475424366465636f64653137683662653239666339383430316663633945a70a9d015f5a4e31326672616d655f73797374656d3670616c6c6574315f39395f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f73797374656d2e2e70616c6c65742e2e43616c6c244c542454244754242447542439656e636f64655f746f3137686339313031356637323934396463643445a80aa9015f5a4e3133385f244c54246672616d655f737570706f72742e2e7472616974732e2e746f6b656e732e2e66756e6769626c652e2e696d62616c616e63652e2e496d62616c616e6365244c5424422443244f6e44726f702443244f70706f736974654f6e44726f7024475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137683933343634333363353264323535623145a90abc015f5a4e31336672616d655f737570706f72743674726169747336746f6b656e73346d697363315f3131365f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f737570706f72742e2e7472616974732e2e746f6b656e732e2e6d6973632e2e4964416d6f756e74244c5424496424432442616c616e6365244754242447542439747970655f696e666f3137683132303635396163343461666636363345aa0abc015f5a4e31336672616d655f737570706f72743674726169747336746f6b656e73346d697363315f3131365f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f737570706f72742e2e7472616974732e2e746f6b656e732e2e6d6973632e2e4964416d6f756e74244c5424496424432442616c616e6365244754242447542439747970655f696e666f3137683363333862373062316161613033666545ab0a535f5a4e31336672616d655f737570706f7274367472616974733773746f72616765313553746f72616765496e7374616e636531317072656669785f686173683137683837643166303733626537306238333545ac0a535f5a4e31336672616d655f737570706f7274367472616974733773746f72616765313553746f72616765496e7374616e636531317072656669785f686173683137686363393137396133393363343837393245ad0acb015f5a4e3134365f244c5424244c50245475706c65456c656d656e74302443245475706c65456c656d656e74312443245475706c65456c656d656e74322443245475706c65456c656d656e7433245250242475323024617324753230246672616d655f737570706f72742e2e7472616974732e2e686f6f6b732e2e4265666f7265416c6c52756e74696d654d6967726174696f6e732447542432396265666f72655f616c6c5f72756e74696d655f6d6967726174696f6e733137686166326132386336613562383961326345ae0a715f5a4e38345f244c54246672616d655f737570706f72742e2e7472616974732e2e6d657461646174612e2e53746f7261676556657273696f6e247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686332393765656362386337356338376645af0ace025f5a4e3237345f244c542470616c6c65745f626162652e2e70616c6c65742e2e50616c6c6574244c542454244754242475323024617324753230246672616d655f737570706f72742e2e7472616974732e2e686f6f6b732e2e4f6e49646c65244c5424244c5424244c5424244c5424542475323024617324753230246672616d655f73797374656d2e2e70616c6c65742e2e436f6e666967244754242e2e426c6f636b24753230246173247532302473705f72756e74696d652e2e7472616974732e2e48656164657250726f7669646572244754242e2e4865616465725424753230246173247532302473705f72756e74696d652e2e7472616974732e2e486561646572244754242e2e4e756d6265722447542424475424376f6e5f69646c6531376862303164656131613465313733656362452e6c6c766d2e3137313234363535373237363736383330323530b00a465f5a4e34315f244c54245424753230246173247532302473657264652e2e64652e2e45787065637465642447542433666d743137686336316461363234623733656264333045b10aa5015f5a4e34636f72653370747231333064726f705f696e5f706c616365244c54242475356224636f72652e2e6f7074696f6e2e2e4f7074696f6e244c5424747269655f64622e2e6e6f64652e2e4e6f646548616e646c654f776e6564244c54247072696d69746976655f74797065732e2e4832353624475424244754242475336224247532302431362475356424244754243137683464363263343032613462393966356245b20a3e5f5a4e34636f726533707472323864726f705f696e5f706c616365244c542424524624753136244754243137683665616535333638616239373933636345b30a715f5a4e34636f726533707472353364726f705f696e5f706c616365244c54247061726974795f7363616c655f636f6465632e2e6572726f722e2e4572726f722447542431376866386339663061313434386130383237452e6c6c766d2e3137313234363535373237363736383330323530b40a785f5a4e36365f244c5424542475323024617324753230247061726974795f7363616c655f636f6465632e2e64657074685f6c696d69742e2e4465636f64654c696d69742447542432376465636f64655f616c6c5f776974685f64657074685f6c696d69743137683964616534343632633339303363663745b50a7c5f5a4e36395f244c54247061726974795f7363616c655f636f6465632e2e6572726f722e2e4572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376863636562313832636338343631643463452e6c6c766d2e3137313234363535373237363736383330323530b60a385f5a4e37747269655f6462346e6f6465344e6f64653133746f5f6f776e65645f6e6f64653137683939636439343431333765346334396245b70a385f5a4e37747269655f6462346e6f6465344e6f64653133746f5f6f776e65645f6e6f64653137686632346131306365306563333730653445b80a785f5a4e38375f244c542470616c6c65745f626162652e2e53616d65417574686f726974696573466f726576657224753230246173247532302470616c6c65745f626162652e2e45706f63684368616e6765547269676765722447542437747269676765723137683563393766633066646338646137646345b90a8a015f5a4e39345f244c54246672616d655f73797374656d2e2e70616c6c65742e2e50616c6c6574244c5424542447542424753230246173247532302473705f72756e74696d652e2e7472616974732e2e56616c6964617465556e7369676e656424475424313776616c69646174655f756e7369676e65643137683763633366633333663562393939626245ba0a8c015f5a4e39365f244c54246672616d655f73797374656d2e2e70616c6c65742e2e43616c6c244c542454244754242475323024617324753230246672616d655f737570706f72742e2e64697370617463682e2e4765744469737061746368496e666f2447542431376765745f64697370617463685f696e666f3137683737366362346135636539313131633545bb0a86015f5a4e3130315f244c54247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e436f6d70616374244c5424753332244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f64653137683037333433373337663466363565313245bc0a86015f5a4e3130315f244c54247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e436f6d70616374244c5424753332244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f64653137686162386230393332393466663765353545bd0a86015f5a4e3130315f244c54247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e436f6d70616374244c5424753332244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f64653137686339613638313439636431656664636645be0a86015f5a4e3130315f244c54247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e436f6d70616374244c5424753634244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f64653137683065343237336433373932373636383445bf0a86015f5a4e3130315f244c54247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e436f6d70616374244c5424753634244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f64653137683939386433313432343839616330323945c00a8c015f5a4e3130345f244c54247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e436f6d70616374526566244c5424753332244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542439656e636f64655f746f3137686130623235303763636430386336663945c10a8c015f5a4e3130345f244c54247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e436f6d70616374526566244c5424753634244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542439656e636f64655f746f3137686162383430623030653531313334376645c20a95015f5a4e31307363616c655f696e666f35696d706c7339365f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230247061726974795f7363616c655f636f6465632e2e636f6d706163742e2e436f6d70616374244c542454244754242447542439747970655f696e666f3137686436666266333566353631333232643145c30a3c5f5a4e31307363686e6f72726b656c3376726638565246496e4f757431306d616b655f62797465733137683565326431363264636330633964663845c40a445f5a4e31307363686e6f72726b656c33767266395652465072654f757431376174746163685f696e7075745f686173683137686239323333336663616132313737343345c50a325f5a4e313073657264655f6a736f6e326465313066726f6d5f74726169743137686133616163653963653362613764616545c60a8a015f5a4e39335f244c5424245246246d7574247532302473657264655f6a736f6e2e2e64652e2e446573657269616c697a6572244c5424522447542424753230246173247532302473657264652e2e64652e2e446573657269616c697a6572244754243138646573657269616c697a655f7374727563743137683330636661616663613032363930376245c70a495f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c5424522447542431307065656b5f6572726f723137683233643937613864646630313864333545c80a4a5f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c5424522447542431317363616e5f6e756d6265723137683931356135646362653961386134656645c90a4c5f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c5424522447542431337363616e5f6578706f6e656e743137686666663863633264356330373030343545ca0a4a5f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c5424522447542431317363616e5f6f725f656f663137683934313964656662346237313064326145cb0a4c5f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c54245224475424313370617273655f646563696d616c3137683130653563616664666163326334333045cc0a4d5f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c5424522447542431346636345f66726f6d5f70617274733137686332333536346565646433663139363845cd0a4d5f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c54245224475424313470617273655f6578706f6e656e743137686539613036646233653532303532316445ce0a555f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c54245224475424323270617273655f646563696d616c5f6f766572666c6f773137683661346533653430383835346633306345cf0a4c5f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c54245224475424313370617273655f696e74656765723137686163383264616632613834343062326445d00a515f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c54245224475424313870617273655f6c6f6e675f696e74656765723137686436663433633030353966323837663845d10a4d5f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c54245224475424313469676e6f72655f696e74656765723137683132646237623166306636393438666545d20a4e5f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c54245224475424313569676e6f72655f6578706f6e656e743137686561663837613163326236323934346545d30a565f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c54245224475424323370617273655f6578706f6e656e745f6f766572666c6f773137686163623532653432653165323862656245d40a4f5f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c54245224475424313670617273655f616e795f6e756d6265723137686533666539303035323061373439656645d50a695f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c5424522447542431377065656b5f696e76616c69645f7479706531376837626138363266646533336131303362452e6c6c766d2e37373639353536303138373433393139313834d60a565f5a4e313073657264655f6a736f6e356572726f72354572726f7231326669785f706f736974696f6e31376835393362366639643165386538333239452e6c6c766d2e37373639353536303138373433393139313834d70a6a5f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c54245224475424313870617273655f6f626a6563745f636f6c6f6e31376864316632643330613939383561396530452e6c6c766d2e37373639353536303138373433393139313834d80a435f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c54245224475424356572726f723137686431316338336139343666383533376445d90a455f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c5424522447542437656e645f6d61703137683330643738346362333636626139326545da0a455f5a4e313073657264655f6a736f6e3264653231446573657269616c697a6572244c5424522447542437656e645f7365713137683165303965653035643161393536323545db0a2e5f5a4e313161727261795f6279746573396279746573326865783137683063623463653263633034626330383045dc0a2e5f5a4e313161727261795f6279746573396279746573326865783137683232393761396336663734663439643245dd0a475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683737323034346237316564633364663845de0a8b015f5a4e34636f7265336f70733866756e6374696f6e35696d706c7338305f244c5424696d706c2475323024636f72652e2e6f70732e2e66756e6374696f6e2e2e466e4f6e6365244c542441244754242475323024666f722475323024245246246d7574247532302446244754243963616c6c5f6f6e63653137683737313436643233663735313461383545df0a565f5a4e34636f726533707472323764726f705f696e5f706c616365244c54242452462475382447542431376833376634303563343765346531373866452e6c6c766d2e37373639353536303138373433393139313834e00a6d5f5a4e34636f726533707472353064726f705f696e5f706c616365244c542473657264652e2e64652e2e696d706c732e2e556e697456697369746f722447542431376830393231376639376139393130616232452e6c6c766d2e37373639353536303138373433393139313834e10a3e5f5a4e35616c6c6f633473796e633136417263244c54245424432441244754243964726f705f736c6f773137686166393637633761376534636431646145e20a3e5f5a4e35616c6c6f633473796e633136417263244c54245424432441244754243964726f705f736c6f773137686563656130623932323531393531653745e30a355f5a4e357365726465326465354572726f7231336d697373696e675f6669656c643137683033306566393061666338653838636245e40a765f5a4e36315f244c542473657264655f6a736f6e2e2e6572726f722e2e4572726f7224753230246173247532302473657264652e2e64652e2e4572726f722447542436637573746f6d31376832373365313031396430336531316561452e6c6c766d2e37373639353536303138373433393139313834e50a355f5a4e357365726465326465354572726f723133756e6b6e6f776e5f6669656c643137683130373837303036663830633263633345e60a365f5a4e357365726465326465354572726f723134696e76616c69645f6c656e6774683137683664313765333462306438356364303845e70a375f5a4e357365726465326465354572726f7231356475706c69636174655f6669656c643137683163303365373735306264393965613445e80a375f5a4e357365726465326465354572726f723135756e6b6e6f776e5f76617269616e743137686335313534333036373462653431376245e90a385f5a4e3573657264653264653953657141636365737331326e6578745f656c656d656e743137683738656235363266616461613037663345ea0a5d5f5a4e36315f244c542473657264655f6a736f6e2e2e6572726f722e2e4572726f7224753230246173247532302473657264652e2e64652e2e4572726f722447542436637573746f6d3137683862363935326366396561353563656245eb0a775f5a4e37355f244c542473657264655f6a736f6e2e2e64652e2e536571416363657373244c5424522447542424753230246173247532302473657264652e2e64652e2e5365714163636573732447542431376e6578745f656c656d656e745f736565643137683365663434356464643431623033356145ec0a775f5a4e37355f244c542473657264655f6a736f6e2e2e64652e2e536571416363657373244c5424522447542424753230246173247532302473657264652e2e64652e2e5365714163636573732447542431376e6578745f656c656d656e745f736565643137683864313161346136366661643365323145ed0a775f5a4e37355f244c542473657264655f6a736f6e2e2e64652e2e536571416363657373244c5424522447542424753230246173247532302473657264652e2e64652e2e5365714163636573732447542431376e6578745f656c656d656e745f736565643137683962623239626138336432363334333345ee0a90015f5a4e38315f244c5424636f72652e2e6d61726b65722e2e5068616e746f6d44617461244c5424542447542424753230246173247532302473657264652e2e64652e2e446573657269616c697a6553656564244754243131646573657269616c697a6531376836386234313135373032353937303533452e6c6c766d2e37373639353536303138373433393139313834ef0a775f5a4e38315f244c5424636f72652e2e6d61726b65722e2e5068616e746f6d44617461244c5424542447542424753230246173247532302473657264652e2e64652e2e446573657269616c697a6553656564244754243131646573657269616c697a653137686565343631383532376534323532666145f00a7a5f5a4e38335f244c542473657264655f6a736f6e2e2e64652e2e56617269616e74416363657373244c5424522447542424753230246173247532302473657264652e2e64652e2e56617269616e74416363657373244754243132756e69745f76617269616e743137683164323563643663323337313437623645f10a87015f5a4e39335f244c5424245246246d7574247532302473657264655f6a736f6e2e2e64652e2e446573657269616c697a6572244c5424522447542424753230246173247532302473657264652e2e64652e2e446573657269616c697a6572244754243135646573657269616c697a655f7365713137683133316532643334643832313063356645f20a87015f5a4e39335f244c5424245246246d7574247532302473657264655f6a736f6e2e2e64652e2e446573657269616c697a6572244c5424522447542424753230246173247532302473657264652e2e64652e2e446573657269616c697a6572244754243135646573657269616c697a655f7365713137683333616565303336666437323937643745f30a87015f5a4e39335f244c5424245246246d7574247532302473657264655f6a736f6e2e2e64652e2e446573657269616c697a6572244c5424522447542424753230246173247532302473657264652e2e64652e2e446573657269616c697a6572244754243135646573657269616c697a655f7365713137683663396638383330623338303337313545f40a8a015f5a4e39335f244c5424245246246d7574247532302473657264655f6a736f6e2e2e64652e2e446573657269616c697a6572244c5424522447542424753230246173247532302473657264652e2e64652e2e446573657269616c697a6572244754243138646573657269616c697a655f737472696e673137683839653363643432376634353935343245f50a8a015f5a4e39335f244c5424245246246d7574247532302473657264655f6a736f6e2e2e64652e2e446573657269616c697a6572244c5424522447542424753230246173247532302473657264652e2e64652e2e446573657269616c697a6572244754243138646573657269616c697a655f7374727563743137683133666266633963376265386462393445f60a8a015f5a4e39335f244c5424245246246d7574247532302473657264655f6a736f6e2e2e64652e2e446573657269616c697a6572244c5424522447542424753230246173247532302473657264652e2e64652e2e446573657269616c697a6572244754243138646573657269616c697a655f7374727563743137686535623663386532373639373632626345f70a8a015f5a4e39335f244c5424245246246d7574247532302473657264655f6a736f6e2e2e64652e2e446573657269616c697a6572244c5424522447542424753230246173247532302473657264652e2e64652e2e446573657269616c697a6572244754243138646573657269616c697a655f7374727563743137686566636263366436383438333235326245f80a8a015f5a4e39335f244c5424245246246d7574247532302473657264655f6a736f6e2e2e64652e2e446573657269616c697a6572244c5424522447542424753230246173247532302473657264652e2e64652e2e446573657269616c697a6572244754243138646573657269616c697a655f7374727563743137686235353539633235633436323432356545f90a8a015f5a4e39335f244c5424245246246d7574247532302473657264655f6a736f6e2e2e64652e2e446573657269616c697a6572244c5424522447542424753230246173247532302473657264652e2e64652e2e446573657269616c697a6572244754243138646573657269616c697a655f7374727563743137683430393835643635653733353831316645fa0a2d5f5a4e39747269655f726f6f7431306275696c645f747269653137683437353364386565376366326539383645fb0a325f5a4e39747269655f726f6f743135747269655f726f6f745f696e6e65723137686566653532343337373534333465623345fc0a445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683030616563653665323137386330646445fd0a445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683037313835633266366330333932393445fe0a445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683038643064363230373233316536636245ff0a445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683130336561626664653264353661353645800b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683130643134376238373439313331346545810b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683134323333613839656530623038303845820b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683135616335316334656332366666383045830b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683136666362373563613666386236613245840b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683163653735303634376465383064393445850b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683165613930653339666234366139653445860b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683230306234356262343932633065613445870b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683236623332343563653639383333666145880b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683237303838303066656432396633636645890b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e7431376832383039643364363662366530373533458a0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e7431376832393564336134613435376262613036458b0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e7431376832633733386432363637663938303466458c0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e7431376832646331303331353661306266663038458d0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e7431376832646334386361623830663431336138458e0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e7431376832666134383133626565333030306539458f0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683266653933346435336435313863653945900b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683335366562633262643764336166346245910b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683336353466663132396338313138346145920b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683365383139636234383536336538306445930b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683430666239643364333530666439366245940b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683435663761326331623865316562666345950b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683437323533623436643562616661366545960b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683439666436383365636532313233326245970b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683461316465626635623235393464363545980b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683461323566366265353038353861363245990b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e7431376834626332386663333935646230643333459a0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e7431376834633938663539663265613564643666459b0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e7431376835393035333162633465336430663637459c0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e7431376835653938333636326131306237616161459d0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e7431376835663462646433393136653031366563459e0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e7431376836343963393533396238326634666564459f0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683634666132373235396332663732636545a00b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683661376630643532316137353636363745a10b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683730373530393661383738343065646145a20b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683737356231613734343562333237336445a30b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683738643237373036303263356239396645a40b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683830343763633137326231393933666545a50b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683834303138643236386534336537383845a60b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683835666432633338313735313533653645a70b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683838376639343433353132663831316245a80b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683838653963303261666236633261343445a90b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683866373338343432346232313636613045aa0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683936316136653734656365653737356545ab0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683937383936356139383339613435343045ac0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683939303939336665343538346238393145ad0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683939653732666438653237303437396145ae0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683962346138653763356239656334346345af0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683962393538363530663533353839613845b00b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683964613439323066353366376535313645b10b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683966323338343265303431396661393145b20b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683966343335626261636266633736363445b30b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137683966383466363636306634333664643245b40b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686131623965393436663166313138376245b50b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686132623264633339663762376138363745b60b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686137393339643062376337306231616345b70b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686138386235323738323335353038393445b80b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686163343137323631656663366563626345b90b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686230653739613163313362666462643045ba0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686237633161336133386236303064316145bb0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686332333163376537646533326434666345bc0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686332343831663966356432653234333545bd0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686332363437393531383731333639333645be0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686339656164383866613064633239383745bf0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686362633966633235616461666139653645c00b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686364366563623564396630653339663045c10b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686366626139343562626165653039653445c20b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686433353935363861383538666161383945c30b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686435356430343030666339306462343445c40b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686465306265363338386138373939303045c50b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686466316531316231633732306466623745c60b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686531663061343762326532303865316545c70b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686533353733306536616237393035616545c80b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686538333463336163323532633865316645c90b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686562346535653733653861346530343545ca0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686563313262653263396537353961316345cb0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686565373262633365336365616561306545cc0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686565383336636530373061666264666145cd0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686631623637326232383937643763393645ce0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686634393739666330356461613364613245cf0b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686636336137623266373037313832306645d00b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686661356466633461303466636135623245d10b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686661396335666330393538383037303645d20b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686662643432613436323530623239356145d30b445f5a4e31307363616c655f696e666f356275696c64313756617269616e7473244c542446244754243776617269616e743137686663393765626566616262353133626345d40b3f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137683065383237623433386462613730663345d50b475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683661386535316538663131333137366145d60b335f5a4e34636f726535736c69636534736f727431306d657267655f736f72743137683235623134616661336262353865303645d70b425f5a4e34636f726535736c69636534736f72743235696e73657274696f6e5f736f72745f73686966745f6c6566743137683030373765646163333738383764326545d80b335f5a4e34636f726535736c69636534736f727431306d657267655f736f72743137683438386463366339616430316232373145d90b425f5a4e34636f726535736c69636534736f72743235696e73657274696f6e5f736f72745f73686966745f6c6566743137686638376131316465326661346234663545da0b335f5a4e34636f726535736c69636534736f727431306d657267655f736f72743137683465366339386664343132333031646245db0b425f5a4e34636f726535736c69636534736f72743235696e73657274696f6e5f736f72745f73686966745f6c6566743137683965616238643961663662306561386245dc0b335f5a4e34636f726535736c69636534736f727431306d657267655f736f72743137683534323937616362663532376163653645dd0b425f5a4e34636f726535736c69636534736f72743235696e73657274696f6e5f736f72745f73686966745f6c6566743137683430656136363234633962303434343945de0b0c436f72655f76657273696f6edf0b12436f72655f657865637574655f626c6f636be00b15436f72655f696e697469616c697a655f626c6f636be10b114d657461646174615f6d65746164617461e20b1c4d657461646174615f6d657461646174615f61745f76657273696f6ee30b1a4d657461646174615f6d657461646174615f76657273696f6e73e40b2b5461676765645472616e73616374696f6e51756575655f76616c69646174655f7472616e73616374696f6ee50b1c426c6f636b4275696c6465725f6170706c795f65787472696e736963e60b1b426c6f636b4275696c6465725f66696e616c697a655f626c6f636be70b20426c6f636b4275696c6465725f696e686572656e745f65787472696e73696373e80b1c426c6f636b4275696c6465725f636865636b5f696e686572656e7473e90b1d4163636f756e744e6f6e63654170695f6163636f756e745f6e6f6e6365ea0b12546573744150495f62616c616e63655f6f66eb0b19546573744150495f62656e63686d61726b5f6164645f6f6e65ec0b20546573744150495f62656e63686d61726b5f766563746f725f6164645f6f6e65ed0b22546573744150495f66756e6374696f6e5f7369676e61747572655f6368616e676564ee0b10546573744150495f7573655f74726965ef0b1f546573744150495f62656e63686d61726b5f696e6469726563745f63616c6cf00b1d546573744150495f62656e63686d61726b5f6469726563745f63616c6cf10b19546573744150495f7665635f776974685f6361706163697479f20b18546573744150495f6765745f626c6f636b5f6e756d626572f30b1b546573744150495f746573745f656432353531395f63727970746ff40b1b546573744150495f746573745f737232353531395f63727970746ff50b19546573744150495f746573745f65636473615f63727970746ff60b14546573744150495f746573745f73746f72616765f70b14546573744150495f746573745f7769746e657373f80b1f546573744150495f746573745f6d756c7469706c655f617267756d656e7473f90b14546573744150495f646f5f74726163655f6c6f67fa0b16546573744150495f7665726966795f65643235353139fb0b17546573744150495f77726974655f6b65795f76616c7565fc0b15417572614170695f736c6f745f6475726174696f6efd0b13417572614170695f617574686f726974696573fe0b15426162654170695f636f6e66696775726174696f6eff0b1b426162654170695f63757272656e745f65706f63685f7374617274800c15426162654170695f63757272656e745f65706f6368810c12426162654170695f6e6578745f65706f6368820c35426162654170695f7375626d69745f7265706f72745f65717569766f636174696f6e5f756e7369676e65645f65787472696e736963830c24426162654170695f67656e65726174655f6b65795f6f776e6572736869705f70726f6f66840c214f6666636861696e576f726b65724170695f6f6666636861696e5f776f726b6572850c2153657373696f6e4b6579735f67656e65726174655f73657373696f6e5f6b657973860c1f53657373696f6e4b6579735f6465636f64655f73657373696f6e5f6b657973870c1e4772616e6470614170695f6772616e6470615f617574686f726974696573880c194772616e6470614170695f63757272656e745f7365745f6964890c384772616e6470614170695f7375626d69745f7265706f72745f65717569766f636174696f6e5f756e7369676e65645f65787472696e7369638a0c274772616e6470614170695f67656e65726174655f6b65795f6f776e6572736869705f70726f6f668b0c1a47656e657369734275696c6465725f6275696c645f73746174658c0c1947656e657369734275696c6465725f6765745f7072657365748d0c1b47656e657369734275696c6465725f7072657365745f6e616d65738e0c94015f5a4e3130335f244c542470616c6c65745f62616c616e6365732e2e70616c6c65742e2e43616c6c244c54245424432449244754242475323024617324753230246672616d655f737570706f72742e2e64697370617463682e2e4765744469737061746368496e666f2447542431376765745f64697370617463685f696e666f31376838616666613935313965656633323339458f0c6d5f5a4e31307363616c655f696e666f35696d706c7335365f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302424524624542447542439747970655f696e666f3137683636666361346538633762323339616445900c85015f5a4e31307363616c655f696e666f35696d706c7338305f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242447542439747970655f696e666f3137683032393366666334386263613131643645910c85015f5a4e31307363616c655f696e666f35696d706c7338305f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242447542439747970655f696e666f3137683239303431626534353431656533323045920c85015f5a4e31307363616c655f696e666f35696d706c7338305f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242447542439747970655f696e666f3137683666636137386131386235313132313245930c85015f5a4e31307363616c655f696e666f35696d706c7338305f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242447542439747970655f696e666f3137683861356637323636636261316638313545940c85015f5a4e31307363616c655f696e666f35696d706c7338305f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242447542439747970655f696e666f3137686130656233363139643961623935623245950c85015f5a4e31307363616c655f696e666f35696d706c7338305f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242447542439747970655f696e666f3137686166326361666361363561316462356445960c85015f5a4e31307363616c655f696e666f35696d706c7338305f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242447542439747970655f696e666f3137686166616239643865646238326130666545970c85015f5a4e31307363616c655f696e666f35696d706c7338305f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242447542439747970655f696e666f3137686337366332323233316264626633613845980c98015f5a4e3132305f244c542470616c6c65745f62616c616e6365732e2e70616c6c65742e2e47656e65736973436f6e666967244c54245424432449244754242475323024617324753230246672616d655f737570706f72742e2e7472616974732e2e686f6f6b732e2e4275696c6447656e65736973436f6e66696724475424356275696c643137683263343865643639393362323135333545990cb2015f5a4e3132315f244c542470616c6c65745f62616c616e6365732e2e70616c6c65742e2e50616c6c6574244c54245424432449244754242475323024617324753230246672616d655f737570706f72742e2e7472616974732e2e686f6f6b732e2e4265666f7265416c6c52756e74696d654d6967726174696f6e732447542432396265666f72655f616c6c5f72756e74696d655f6d6967726174696f6e7331376861626231343531363065396666333539459a0c8a015f5a4e38345f244c54246672616d655f737570706f72742e2e7472616974732e2e6d657461646174612e2e53746f7261676556657273696f6e247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376863323937656563623863373563383766452e6c6c766d2e383531373635383236373037343038323237359b0c7c5f5a4e31336672616d655f737570706f72743674726169747336746f6b656e733866756e6769626c6537726567756c61723130556e62616c616e636564313664656372656173655f62616c616e636531376836643366616235353565643963623762452e6c6c766d2e383531373635383236373037343038323237359c0cad025f5a4e313570616c6c65745f62616c616e6365733133696d706c5f66756e6769626c653230335f244c5424696d706c24753230246672616d655f737570706f72742e2e7472616974732e2e746f6b656e732e2e66756e6769626c652e2e726567756c61722e2e556e62616c616e636564244c5424244c5424542475323024617324753230246672616d655f73797374656d2e2e70616c6c65742e2e436f6e666967244754242e2e4163636f756e744964244754242475323024666f72247532302470616c6c65745f62616c616e6365732e2e70616c6c65742e2e50616c6c6574244c542454244324492447542424475424313377726974655f62616c616e636531376831353333343339363330323865613831452e6c6c766d2e383531373635383236373037343038323237359d0c555f5a4e31336672616d655f737570706f72743674726169747336746f6b656e733866756e6769626c6537726567756c6172364d7574617465387472616e7366657231376838303261613936376539353332663664459e0c565f5a4e31336672616d655f737570706f72743674726169747336746f6b656e733866756e6769626c6537726567756c6172364d7574617465396275726e5f66726f6d31376865303134306335626565393033393631459f0c9a025f5a4e313570616c6c65745f62616c616e6365733133696d706c5f63757272656e63793231345f244c5424696d706c24753230246672616d655f737570706f72742e2e7472616974732e2e746f6b656e732e2e63757272656e63792e2e72657365727661626c652e2e52657365727661626c6543757272656e6379244c5424244c5424542475323024617324753230246672616d655f73797374656d2e2e70616c6c65742e2e436f6e666967244754242e2e4163636f756e744964244754242475323024666f72247532302470616c6c65745f62616c616e6365732e2e70616c6c65742e2e50616c6c6574244c54245424432449244754242447542439756e726573657276653137686636633263303331396432633664663445a00c555f5a4e313570616c6c65745f62616c616e6365733670616c6c6574313950616c6c6574244c54245424432449244754243135656e737572655f75706772616465643137686161653230653531616331663136666345a10c565f5a4e313570616c6c65745f62616c616e6365733670616c6c6574313950616c6c6574244c5424542443244924475424313673746f726167655f6d657461646174613137683462373366346566306130363765313845a20c5f5f5a4e313570616c6c65745f62616c616e6365733670616c6c6574313950616c6c6574244c5424542443244924475424323570616c6c65745f636f6e7374616e74735f6d657461646174613137683931313030616265386164616432633245a30c625f5a4e313570616c6c65745f62616c616e6365733670616c6c6574313950616c6c6574244c542454244324492447542432386d75746174655f6163636f756e745f68616e646c696e675f647573743137686466353435633939303335356331356445a40ca5015f5a4e313570616c6c65745f62616c616e6365733670616c6c6574315f3130335f244c5424696d706c247532302473657264652e2e7365722e2e53657269616c697a652475323024666f72247532302470616c6c65745f62616c616e6365732e2e70616c6c65742e2e47656e65736973436f6e666967244c5424542443244924475424244754243973657269616c697a653137686134333862383138653961336639373345a50ca5015f5a4e313570616c6c65745f62616c616e6365733670616c6c6574315f3130365f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f64652475323024666f72247532302470616c6c65745f62616c616e6365732e2e70616c6c65742e2e43616c6c244c542454244324492447542424475424366465636f64653137686631613331303832333539323230653145a60ca8015f5a4e313570616c6c65745f62616c616e6365733670616c6c6574315f3130365f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f72247532302470616c6c65745f62616c616e6365732e2e70616c6c65742e2e43616c6c244c54245424432449244754242447542439656e636f64655f746f3137686639383839393837626365626265316345a70ca8015f5a4e313570616c6c65745f62616c616e6365733670616c6c6574315f3130365f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f72247532302470616c6c65745f62616c616e6365732e2e70616c6c65742e2e43616c6c244c5424542443244924475424244754243973697a655f68696e743137686161313338616130666533383935303145a80ca9015f5a4e313570616c6c65745f62616c616e6365733670616c6c6574315f3130375f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f72247532302470616c6c65745f62616c616e6365732e2e70616c6c65742e2e4576656e74244c54245424432449244754242447542439656e636f64655f746f3137683561613033636133646530393239313645a90ca9015f5a4e313570616c6c65745f62616c616e6365733670616c6c6574315f3130375f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f72247532302470616c6c65745f62616c616e6365732e2e70616c6c65742e2e4576656e74244c5424542443244924475424244754243973697a655f68696e743137686135626466653236666337303064376345aa0c94015f5a4e313570616c6c65745f62616c616e6365733670616c6c6574315f39315f244c5424696d706c2475323024636f72652e2e636c6f6e652e2e436c6f6e652475323024666f72247532302470616c6c65745f62616c616e6365732e2e70616c6c65742e2e43616c6c244c54245424432449244754242447542435636c6f6e653137686633613263613039636236316434363045ab0c9a015f5a4e313570616c6c65745f62616c616e6365733670616c6c6574315f39335f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302470616c6c65745f62616c616e6365732e2e70616c6c65742e2e43616c6c244c54245424432449244754242447542439747970655f696e666f3137686464383063303636333030663661306645ac0c9b015f5a4e313570616c6c65745f62616c616e6365733670616c6c6574315f39345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302470616c6c65745f62616c616e6365732e2e70616c6c65742e2e4572726f72244c54245424432449244754242447542439747970655f696e666f3137683931363838333533316635313465373345ad0c9b015f5a4e313570616c6c65745f62616c616e6365733670616c6c6574315f39345f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302470616c6c65745f62616c616e6365732e2e70616c6c65742e2e4576656e74244c54245424432449244754242447542439747970655f696e666f3137686262343536633138323264383963626445ae0ca3015f5a4e313773705f636f6e73656e7375735f626162653764696765737473315f3130315f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f64652475323024666f72247532302473705f636f6e73656e7375735f626162652e2e646967657374732e2e50726544696765737424475424366465636f64653137686165346537376534363539393339343245af0cb0015f5a4e313773705f636f6e73656e7375735f626162653764696765737473315f3131315f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f72247532302473705f636f6e73656e7375735f626162652e2e646967657374732e2e4e65787445706f636844657363726970746f722447542439656e636f64655f746f3137686365396637626163306434386265636145b00cb1015f5a4e313773705f636f6e73656e7375735f626162653764696765737473315f3131325f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f72247532302473705f636f6e73656e7375735f626162652e2e646967657374732e2e4e657874436f6e66696744657363726970746f722447542439656e636f64655f746f3137686364633034616339663262383636633745b10ca8015f5a4e31387061726974795f7363616c655f636f64656335636f6465633136696e6e65725f7475706c655f696d706c38395f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f722475323024244c50244f30244324503024432451302443245230245250242447542439656e636f64655f746f3137683433663439383132626261343263333245b20c8f015f5a4e38335f244c5424636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542439656e636f64655f746f31376832613162373561323035646233633263452e6c6c766d2e38353137363538323637303734303832323735b30c3f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137683032346265353630383738313666663645b40c3f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137683064643463333836363661633961396545b50c3f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137686534616164343835353932656463383945b60c3f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f64653137686634396532633239636638313337323945b70c465f5a4e34315f244c54245424753230246173247532302473657264652e2e64652e2e45787065637465642447542433666d743137686439646431616132666662333065646345b80c475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683238636534333339373333346432316245b90c475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683662623638356631633230373232663945ba0c475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686463313334623230623362326435393245bb0c315f5a4e34636f7265336f70733866756e6374696f6e32466e3463616c6c3137686536313432333437666131616638386445bc0c89015f5a4e34636f7265336f70733866756e6374696f6e35696d706c7337395f244c5424696d706c2475323024636f72652e2e6f70732e2e66756e6374696f6e2e2e466e4d7574244c542441244754242475323024666f722475323024245246246d7574247532302446244754243863616c6c5f6d75743137683461363664643761386165333132383245bd0c89015f5a4e34636f7265336f70733866756e6374696f6e35696d706c7337395f244c5424696d706c2475323024636f72652e2e6f70732e2e66756e6374696f6e2e2e466e4d7574244c542441244754242475323024666f722475323024245246246d7574247532302446244754243863616c6c5f6d75743137686539333236653837353730653031613945be0c5a5f5a4e34636f7265336f70733866756e6374696f6e36466e4f6e6365343063616c6c5f6f6e636524753762242475376224767461626c652e7368696d247537642424753764243137686263343230643933396564353465393145bf0c765f5a4e37747269655f6462366c6f6f6b757031394c6f6f6b7570244c54244c244324512447542432376c6f6f6b5f75705f776974685f63616368655f696e7465726e616c32385f24753762242475376224636c6f73757265247537642424753764243137686339633439643432333965356530343945c00c5a5f5a4e34636f7265336f70733866756e6374696f6e36466e4f6e6365343063616c6c5f6f6e636524753762242475376224767461626c652e7368696d247537642424753764243137686366383239653539326533323031353845c10c3d5f5a4e34636f726533707472323764726f705f696e5f706c616365244c5424245246247538244754243137683337663430356334376534653137386645c20c80015f5a4e39305f244c542473705f747269652e2e6e6f64655f636f6465632e2e4e6f6465436f646563244c54244824475424247532302461732475323024747269655f64622e2e6e6f64655f636f6465632e2e4e6f6465436f6465632447542431316465636f64655f706c616e3137683163626365316538333232386338336545c30c4d5f5a4e37747269655f6462366c6f6f6b757031394c6f6f6b7570244c54244c244324512447542431366c6f61645f6f776e65645f76616c75653137683938303862306133313464646166643645c40c6b5f5a4e37747269655f6462366c6f6f6b757031394c6f6f6b7570244c54244c244324512447542431366c6f61645f6f776e65645f76616c756532385f24753762242475376224636c6f73757265247537642424753764243137683861626363663136623866363531633945c50c6d5f5a4e37747269655f6462366c6f6f6b757031394c6f6f6b7570244c54244c244324512447542431386c6f6f6b5f75705f776974685f636163686532385f24753762242475376224636c6f73757265247537642424753764243137683738326530353363613166643966303445c60c435f5a4e37747269655f6462366c6f6f6b757031394c6f6f6b7570244c54244c2443245124475424376c6f6f6b5f75703137683039633332636135613062313065323645c70c735f5a4e38335f244c5424636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f64653137686430306162643333393938356563313545c80c765f5a4e38335f244c5424636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542439656e636f64655f746f3137686365393539636261366432663034333745c90c765f5a4e38335f244c5424636f72652e2e6f7074696f6e2e2e4f7074696f6e244c542454244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542439656e636f64655f746f3137686566326534323335346535653666373945ca0c80015f5a4e39305f244c542473705f747269652e2e6e6f64655f636f6465632e2e4e6f6465436f646563244c54244824475424247532302461732475323024747269655f64622e2e6e6f64655f636f6465632e2e4e6f6465436f6465632447542431316272616e63685f6e6f64653137683438333833323634383332626431643245cb0c83015f5a4e39305f244c542473705f747269652e2e6e6f64655f636f6465632e2e4e6f6465436f646563244c54244824475424247532302461732475323024747269655f64622e2e6e6f64655f636f6465632e2e4e6f6465436f646563244754243134657874656e73696f6e5f6e6f64653137686562313332643132376363663866356445cc0c88015f5a4e39305f244c542473705f747269652e2e6e6f64655f636f6465632e2e4e6f6465436f646563244c54244824475424247532302461732475323024747269655f64622e2e6e6f64655f636f6465632e2e4e6f6465436f6465632447542431396272616e63685f6e6f64655f6e6962626c65643137683262356631643639323265306662353945cd0c88015f5a4e39305f244c542473705f747269652e2e6e6f64655f636f6465632e2e4e6f6465436f646563244c54244824475424247532302461732475323024747269655f64622e2e6e6f64655f636f6465632e2e4e6f6465436f6465632447542431396272616e63685f6e6f64655f6e6962626c65643137686133663734346261323639376661663045ce0c88015f5a4e39305f244c542473705f747269652e2e6e6f64655f636f6465632e2e4e6f6465436f646563244c54244824475424247532302461732475323024747269655f64622e2e6e6f64655f636f6465632e2e4e6f6465436f6465632447542431396272616e63685f6e6f64655f6e6962626c65643137686236613133316364346335396536356145cf0c88015f5a4e39305f244c542473705f747269652e2e6e6f64655f636f6465632e2e4e6f6465436f646563244c54244824475424247532302461732475323024747269655f64622e2e6e6f64655f636f6465632e2e4e6f6465436f6465632447542431396272616e63685f6e6f64655f6e6962626c65643137686439613634363266306461333065336445d00c7d5f5a4e39305f244c542473705f747269652e2e6e6f64655f636f6465632e2e4e6f6465436f646563244c54244824475424247532302461732475323024747269655f64622e2e6e6f64655f636f6465632e2e4e6f6465436f64656324475424396c6561665f6e6f64653137686261396464373764653533373335366445d10c5d5f5a4e32327375627374726174655f746573745f72756e74696d6532317375627374726174655f746573745f70616c6c6574323176616c69646174655f72756e74696d655f63616c6c3137683836306266353738353930393135613945d20cac015f5a4e3133365f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e42547265654d6170244c54244b2443245624475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e636f6c6c6563742e2e46726f6d4974657261746f72244c5424244c50244b244324562452502424475424244754243966726f6d5f697465723137683536643164323365356138313661613245d30c81015f5a4e39395f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e42547265654d6170244c54244b244324562443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137686135393236383334343631613333386545d40c8f015f5a4e3130375f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e42547265654d6170244c54244b24432456244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542439656e636f64655f746f3137686366393330323439386233373934636345d50c6d5f5a4e31307363616c655f696e666f35696d706c7335365f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302424524624542447542439747970655f696e666f3137683038626339333737656366373463653245d60cac015f5a4e313073705f72756e74696d653767656e6572696336686561646572315f3130375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f72756e74696d652e2e67656e657269632e2e6865616465722e2e486561646572244c54244e756d62657224432448617368244754242447542439747970655f696e666f3137683865663066323732343530363036303145d70c82015f5a4e31307363616c655f696e666f35696d706c7337375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e626f7865642e2e426f78244c542454244754242447542439747970655f696e666f3137683832373066666663316432333263386345d80c82015f5a4e31307363616c655f696e666f35696d706c7337375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f722475323024616c6c6f632e2e626f7865642e2e426f78244c542454244754242447542439747970655f696e666f3137686539376331383464303737613437353445d90ca5015f5a4e313873705f636f6e73656e7375735f736c6f7473315f3130375f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f636f6e73656e7375735f736c6f74732e2e45717569766f636174696f6e50726f6f66244c54244865616465722443244964244754242447542439747970655f696e666f3137686331353661643662343466373838303845da0cae015f5a4e313073705f72756e74696d653767656e6572696335626c6f636b315f3131305f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302473705f72756e74696d652e2e67656e657269632e2e626c6f636b2e2e426c6f636b244c542448656164657224432445787472696e736963244754242447542439747970655f696e666f3137683934303539386666323466623937353845db0cb3015f5a4e3134335f244c542473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c54244164647265737324432443616c6c2443245369676e61747572652443244578747261244754242475323024617324753230247363616c655f696e666f2e2e54797065496e666f2447542439747970655f696e666f3137686662323639313630316561613333323945dc0cd0015f5a4e313073705f72756e74696d653767656e6572696336686561646572315f3132305f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f64652475323024666f72247532302473705f72756e74696d652e2e67656e657269632e2e6865616465722e2e486561646572244c54244e756d626572244324486173682447542424475424366465636f646531376832636162303533353564396634646261452e6c6c766d2e3138313031373033313138373539303137313631dd0cd3015f5a4e313073705f72756e74696d653767656e6572696336686561646572315f3132305f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f72247532302473705f72756e74696d652e2e67656e657269632e2e6865616465722e2e486561646572244c54244e756d62657224432448617368244754242447542439656e636f64655f746f31376832346161363264393065653734316231452e6c6c766d2e3138313031373033313138373539303137313631de0c425f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646539656e636f64655f746f3137683938333034656333636130633633643745df0c4d5f5a4e313073705f76657273696f6e313452756e74696d6556657273696f6e32346465636f64655f776974685f76657273696f6e5f68696e743137683164353430623435326438383665376145e00c8e015f5a4e3131315f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e497465724d7574244c54244b2443245624475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f7224475424346e6578743137683966643839393537643137643833343545e10c93015f5a4e3131365f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e496e746f49746572244c54244b244324562443244124475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f7224475424346e6578743137683666623561626332313434333336393145e20c93015f5a4e3131365f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e496e746f49746572244c54244b244324562443244124475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e6974657261746f722e2e4974657261746f7224475424346e6578743137686363626231376336303463656532333945e30ca5015f5a4e3133355f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e645f657373656e63652e2e457068656d6572616c244c5424532443244824475424247532302461732475323024686173685f64622e2e486173684442244c542448244324616c6c6f632e2e7665632e2e566563244c54247538244754242447542424475424336765743137686137613137616435646636313164636645e40c735f5a4e38365f244c54246d656d6f72795f64622e2e4d656d6f72794442244c5424482443244b462443245424475424247532302461732475323024686173685f64622e2e486173684442244c542448244324542447542424475424336765743137683639643861333435376236646162633445e50ccd015f5a4e3134395f244c54246d656d6f72795f64622e2e4d656d6f72794442244c5424482443244b46244324616c6c6f632e2e7665632e2e566563244c54247538244754242447542424753230246173247532302473705f73746174655f6d616368696e652e2e747269655f6261636b656e645f657373656e63652e2e547269654261636b656e6453746f72616765244c54244824475424244754243367657431376831393138373136346637356462663366452e6c6c766d2e3138313031373033313138373539303137313631e60ca8015f5a4e3133355f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e645f657373656e63652e2e457068656d6572616c244c5424532443244824475424247532302461732475323024686173685f64622e2e486173684442244c542448244324616c6c6f632e2e7665632e2e566563244c5424753824475424244754242447542436696e736572743137683166303861393434346131393735616545e70c765f5a4e38365f244c54246d656d6f72795f64622e2e4d656d6f72794442244c5424482443244b462443245424475424247532302461732475323024686173685f64622e2e486173684442244c54244824432454244754242447542436696e736572743137683433393435626236396464636266613245e80ca8015f5a4e3133355f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e645f657373656e63652e2e457068656d6572616c244c5424532443244824475424247532302461732475323024686173685f64622e2e486173684442244c542448244324616c6c6f632e2e7665632e2e566563244c542475382447542424475424244754243672656d6f76653137683139636137613135653039353764353445e90c765f5a4e38365f244c54246d656d6f72795f64622e2e4d656d6f72794442244c5424482443244b462443245424475424247532302461732475323024686173685f64622e2e486173684442244c5424482443245424475424244754243672656d6f76653137686132396562643361393730306162656545ea0ca9015f5a4e3133355f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e645f657373656e63652e2e457068656d6572616c244c5424532443244824475424247532302461732475323024686173685f64622e2e486173684442244c542448244324616c6c6f632e2e7665632e2e566563244c5424753824475424244754242447542437656d706c6163653137686563616663366130356366386464616245eb0c775f5a4e38365f244c54246d656d6f72795f64622e2e4d656d6f72794442244c5424482443244b462443245424475424247532302461732475323024686173685f64622e2e486173684442244c54244824432454244754242447542437656d706c6163653137686366346133346533343239376530613045ec0caa015f5a4e3133355f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e645f657373656e63652e2e457068656d6572616c244c5424532443244824475424247532302461732475323024686173685f64622e2e486173684442244c542448244324616c6c6f632e2e7665632e2e566563244c5424753824475424244754242447542438636f6e7461696e733137683461396435393332316430376534316645ed0ca9015f5a4e3133395f244c542473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c54244164647265737324432443616c6c2443245369676e6174757265244324457874726124475424247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686634663865333431323537373033336245ee0c5f5f5a4e36365f244c5424636f72652e2e6f7074696f6e2e2e4f7074696f6e244c54245424475424247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683165353837306562393762303264316545ef0ca3015f5a4e31336672616d655f737570706f7274386469737061746368315f3130315f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f737570706f72742e2e64697370617463682e2e4469737061746368496e666f2447542439656e636f64655f746f3137686263363463666435616161623033366645f00ca3015f5a4e31336672616d655f737570706f7274386469737061746368315f3130315f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f7224753230246672616d655f737570706f72742e2e64697370617463682e2e5065724469737061746368436c617373244c542454244754242447542439747970655f696e666f3137686633663866386634386137363064326145f10cad015f5a4e31336672616d655f737570706f7274386469737061746368315f3131345f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f64652475323024666f7224753230246672616d655f737570706f72742e2e64697370617463682e2e5065724469737061746368436c617373244c5424542447542424475424366465636f64653137683261633565376135333733323135316545f20cb0015f5a4e31336672616d655f737570706f7274386469737061746368315f3131345f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f7224753230246672616d655f737570706f72742e2e64697370617463682e2e5065724469737061746368436c617373244c542454244754242447542439656e636f64655f746f3137683333636539393337353030643631343245f30cb9015f5a4e3135355f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e645f657373656e63652e2e547269654261636b656e64457373656e6365244c54245324432448244324432443245224475424247532302461732475323024686173685f64622e2e486173684442526566244c542448244324616c6c6f632e2e7665632e2e566563244c54247538244754242447542424475424336765743137683162653135656637343731616435643945f40cbe015f5a4e3135355f244c542473705f73746174655f6d616368696e652e2e747269655f6261636b656e645f657373656e63652e2e547269654261636b656e64457373656e6365244c54245324432448244324432443245224475424247532302461732475323024686173685f64622e2e486173684442526566244c542448244324616c6c6f632e2e7665632e2e566563244c5424753824475424244754242447542438636f6e7461696e733137683062326533613037353336623930623845f50cbd015f5a4e3135365f244c542473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c54244164647265737324432443616c6c2443245369676e61747572652443244578747261244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f64653137686437376630616436393464663664313445f60c745f5a4e34636f726533707472353664726f705f696e5f706c616365244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d6543616c6c2447542431376865643735326330326539353332656332452e6c6c766d2e3138313031373033313138373539303137313631f70cbd015f5a4e3135365f244c542473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c54244164647265737324432443616c6c2443245369676e61747572652443244578747261244754242475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542436656e636f64653137683730376439356464356233623031353045f80cbb015f5a4e31356672616d655f657865637574697665313034457865637574697665244c542453797374656d244324426c6f636b244324436f6e74657874244324556e7369676e656456616c696461746f72244324416c6c50616c6c6574735769746853797374656d244324434f6e52756e74696d65557067726164652447542431326f6e5f69646c655f686f6f6b31376831383766656437613963623039306539452e6c6c766d2e3138313031373033313138373539303137313631f90c5f5f5a4e35355f244c5424582475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652447542431337573696e675f656e636f6465643137683831373638333938333633333033306245fa0ca2015f5a4e31356672616d655f657865637574697665313034457865637574697665244c542453797374656d244324426c6f636b244324436f6e74657874244324556e7369676e656456616c696461746f72244324416c6c50616c6c6574735769746853797374656d244324434f6e52756e74696d6555706772616465244754243133657865637574655f626c6f636b3137683063343561666665333663653436306145fb0ca5015f5a4e31356672616d655f657865637574697665313034457865637574697665244c542453797374656d244324426c6f636b244324436f6e74657874244324556e7369676e656456616c696461746f72244324416c6c50616c6c6574735769746853797374656d244324434f6e52756e74696d6555706772616465244754243136696e697469616c697a655f626c6f636b3137683931653935623836356530336238303045fc0ca4015f5a4e31356672616d655f657865637574697665313034457865637574697665244c542453797374656d244324426c6f636b244324436f6e74657874244324556e7369676e656456616c696461746f72244324416c6c50616c6c6574735769746853797374656d244324434f6e52756e74696d65557067726164652447542431356170706c795f65787472696e7369633137683439316434313334663736393261393645fd0cd7015f5a4e31356672616d655f657865637574697665313034457865637574697665244c542453797374656d244324426c6f636b244324436f6e74657874244324556e7369676e656456616c696461746f72244324416c6c50616c6c6574735769746853797374656d244324434f6e52756e74696d65557067726164652447542431366170706c795f65787472696e7369637332385f24753762242475376224636c6f7375726524753764242475376424313870616e69635f636f6c645f646973706c61793137686364643631656330303265373534326545fe0cc0015f5a4e31356672616d655f657865637574697665313034457865637574697665244c542453797374656d244324426c6f636b244324436f6e74657874244324556e7369676e656456616c696461746f72244324416c6c50616c6c6574735769746853797374656d244324434f6e52756e74696d6555706772616465244754243137696e686572656e74735f6170706c69656431376861383461383266323165663734373661452e6c6c766d2e3138313031373033313138373539303137313631ff0ca3015f5a4e31356672616d655f657865637574697665313034457865637574697665244c542453797374656d244324426c6f636b244324436f6e74657874244324556e7369676e656456616c696461746f72244324416c6c50616c6c6574735769746853797374656d244324434f6e52756e74696d655570677261646524475424313466696e616c697a655f626c6f636b3137683933343261346466646133393330363045800de5015f5a4e3137315f244c542473705f72756e74696d652e2e67656e657269632e2e756e636865636b65645f65787472696e7369632e2e556e636865636b656445787472696e736963244c54244c6f6f6b7570536f7572636524432443616c6c2443245369676e617475726524432445787472612447542424753230246173247532302473705f72756e74696d652e2e7472616974732e2e436865636b61626c65244c54244c6f6f6b7570244754242447542435636865636b31376864313234663163626239633562363831452e6c6c766d2e3138313031373033313138373539303137313631810da9015f5a4e31356672616d655f657865637574697665313034457865637574697665244c542453797374656d244324426c6f636b244324436f6e74657874244324556e7369676e656456616c696461746f72244324416c6c50616c6c6574735769746853797374656d244324434f6e52756e74696d655570677261646524475424323076616c69646174655f7472616e73616374696f6e3137686533626636663266623762616137653745820da2015f5a4e313570616c6c65745f62616c616e636573357479706573315f3130315f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302470616c6c65745f62616c616e6365732e2e74797065732e2e4163636f756e7444617461244c542442616c616e6365244754242447542439747970655f696e666f3137686134663065333463623066366537386445830da2015f5a4e313570616c6c65745f62616c616e636573357479706573315f3130315f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302470616c6c65745f62616c616e6365732e2e74797065732e2e42616c616e63654c6f636b244c542442616c616e6365244754242447542439747970655f696e666f3137686461366330346230616464663931643545840daf015f5a4e313570616c6c65745f62616c616e636573357479706573315f3131345f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e456e636f64652475323024666f72247532302470616c6c65745f62616c616e6365732e2e74797065732e2e4163636f756e7444617461244c542442616c616e6365244754242447542439656e636f64655f746f3137683035653134323635383662663030363345850db6015f5a4e313570616c6c65745f62616c616e636573357479706573315f3132315f244c5424696d706c24753230247363616c655f696e666f2e2e54797065496e666f2475323024666f72247532302470616c6c65745f62616c616e6365732e2e74797065732e2e5265736572766544617461244c5424526573657276654964656e74696669657224432442616c616e6365244754242447542439747970655f696e666f3137683337306339643564353236313836653145860d765f5a4e313673705f73746174655f6d616368696e653230747269655f6261636b656e645f657373656e63653339547269654261636b656e64457373656e6365244c54245324432448244324432443245224475424313273746f726167655f726f6f743137683865666630326464376262303636333445870d695f5a4e34636f726533707472373164726f705f696e5f706c616365244c542473705f747269652e2e6572726f722e2e4572726f72244c54247072696d69746976655f74797065732e2e4832353624475424244754243137683461363365306537646330353230356545880d7c5f5a4e313673705f73746174655f6d616368696e653230747269655f6261636b656e645f657373656e63653339547269654261636b656e64457373656e6365244c5424532443244824432443244324522447542431386368696c645f73746f726167655f726f6f743137683366373864393233626563646561333245890d475f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646531337573696e675f656e636f64656431376836333366663933643065626462333132458a0d475f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646531337573696e675f656e636f64656431376836656362633037343939303031313061458b0d475f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646531337573696e675f656e636f64656431376865386434663665316538323630646237458c0d3f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f646531376831383263376235323162326232653630458d0d3f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f646531376837666664336633303830343134386662458e0d3f5f5a4e31387061726974795f7363616c655f636f64656335636f64656336456e636f646536656e636f646531376862313363623731303961363536316162458f0dc9015f5a4e313873705f636f6e73656e7375735f736c6f7473315f3132305f244c5424696d706c24753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f64652475323024666f72247532302473705f636f6e73656e7375735f736c6f74732e2e45717569766f636174696f6e50726f6f66244c542448656164657224432449642447542424475424366465636f646531376838666136643630663765366265636434452e6c6c766d2e3138313031373033313138373539303137313631900d475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686361636439366563393062393632343545910d495f5a4e34345f244c54242452462454247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d743137686635656232366434323534626535343245920dc5015f5a4e34636f72653370747231333664726f705f696e5f706c616365244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e496e746f49746572244c5424616c6c6f632e2e7665632e2e566563244c5424753824475424244324244c5024616c6c6f632e2e7665632e2e566563244c542475382447542424432469333224525024244754242447542431376839643730633362626631376334313230452e6c6c766d2e3138313031373033313138373539303137313631930dea035f5a4e34636f72653370747234353564726f705f696e5f706c616365244c542424524624244c50242452462473705f636f72652e2e63727970746f5f62797465732e2e43727970746f4279746573244c542433325f7573697a6524432473705f636f72652e2e737232353531392e2e537232353531395075626c69635461672447542424432424524624244c50246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f6e6f6e63652e2e436865636b4e6f6e6365244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443246672616d655f73797374656d2e2e657874656e73696f6e732e2e636865636b5f7765696768742e2e436865636b576569676874244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242443247375627374726174655f746573745f72756e74696d652e2e436865636b53756273747261746543616c6c2443246672616d655f6d657461646174615f686173685f657874656e73696f6e2e2e436865636b4d6574616461746148617368244c54247375627374726174655f746573745f72756e74696d652e2e52756e74696d65244754242452502424525024244754243137683161306665303638363763346132373745940d715f5a4e34636f726533707472353364726f705f696e5f706c616365244c54247061726974795f7363616c655f636f6465632e2e6572726f722e2e4572726f722447542431376866386339663061313434386130383237452e6c6c766d2e3138313031373033313138373539303137313631950d565f5a4e35616c6c6f633131636f6c6c656374696f6e73356274726565336d6170323542547265654d6170244c54244b24432456244324412447542436696e736572743137683135646363323935343434666563323345960d565f5a4e35616c6c6f633131636f6c6c656374696f6e73356274726565336d6170323542547265654d6170244c54244b24432456244324412447542436696e736572743137683665313937353537323838653633313745970d785f5a4e36365f244c5424542475323024617324753230247061726974795f7363616c655f636f6465632e2e64657074685f6c696d69742e2e4465636f64654c696d69742447542432376465636f64655f616c6c5f776974685f64657074685f6c696d69743137683335326534363933313933653464326645980d785f5a4e36365f244c5424542475323024617324753230247061726974795f7363616c655f636f6465632e2e64657074685f6c696d69742e2e4465636f64654c696d69742447542432376465636f64655f616c6c5f776974685f64657074685f6c696d69743137683566366230393138373838366563393145990d785f5a4e36365f244c5424542475323024617324753230247061726974795f7363616c655f636f6465632e2e64657074685f6c696d69742e2e4465636f64654c696d69742447542432376465636f64655f616c6c5f776974685f64657074685f6c696d697431376863323961306165633566333636633636459a0dee015f5a4e3773705f74726965313373746f726167655f70726f6f663138345f244c5424696d706c2475323024636f72652e2e636f6e766572742e2e46726f6d244c54242452462473705f747269652e2e73746f726167655f70726f6f662e2e53746f7261676550726f6f66244754242475323024666f7224753230246d656d6f72795f64622e2e4d656d6f72794442244c5424482443246d656d6f72795f64622e2e486173684b6579244c54244824475424244324616c6c6f632e2e7665632e2e566563244c542475382447542424475424244754243466726f6d31376861623934623361316664313434343030459b0d785f5a4e38365f244c54246d656d6f72795f64622e2e4d656d6f72794442244c5424482443244b462443245424475424247532302461732475323024686173685f64622e2e486173684442244c54244824432454244754242447542438636f6e7461696e7331376861623135376233326539323331633162459c0d765f5a4e38395f244c54246d656d6f72795f64622e2e4d656d6f72794442244c5424482443244b462443245424475424247532302461732475323024686173685f64622e2e486173684442526566244c5424482443245424475424244754243367657431376830303965353733643864393062663764459d0d7b5f5a4e38395f244c54246d656d6f72795f64622e2e4d656d6f72794442244c5424482443244b462443245424475424247532302461732475323024686173685f64622e2e486173684442526566244c54244824432454244754242447542438636f6e7461696e7331376864333366386466313231343538613835459e0d7d5f5a4e39335f244c542473705f72756e74696d652e2e67656e657269632e2e6469676573742e2e4469676573744974656d2475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f646531376833633830616566343032373932323931459f0d7d5f5a4e39335f244c542473705f72756e74696d652e2e67656e657269632e2e6469676573742e2e4469676573744974656d2475323024617324753230247061726974795f7363616c655f636f6465632e2e636f6465632e2e4465636f646524475424366465636f64653137686633633061353438323530333035643445a00d7d5f5a4e39365f244c542473705f72756e74696d652e2e67656e657269632e2e626c6f636b2e2e426c6f636b244c542448656164657224432445787472696e73696324475424247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683166353036303735386338633231313445a10d81015f5a4e39395f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e42547265654d6170244c54244b244324562443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137683037643637626264633938386438353245a20d81015f5a4e39395f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e42547265654d6170244c54244b244324562443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137683133313338353835373532333264363945a30d81015f5a4e39395f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e42547265654d6170244c54244b244324562443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137686564363336353739313661396638326145a40d81015f5a4e39395f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e496e746f49746572244c54244b244324562443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137683361336466663835326134633765373645a50d81015f5a4e39395f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e42547265654d6170244c54244b244324562443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137683335663838613730393939656235363745a60d81015f5a4e39395f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e42547265654d6170244c54244b244324562443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137683637383233316232383133303563366445a70d81015f5a4e39395f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e42547265654d6170244c54244b244324562443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137683766393065373362353864666664333645a80d81015f5a4e39395f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e42547265654d6170244c54244b244324562443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137686433396639663265653064366333656245a90d81015f5a4e39395f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e42547265654d6170244c54244b244324562443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137686562313166663963323164383266663645aa0d9b015f5a4e39395f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e496e746f49746572244c54244b244324562443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f7031376833336539636534316536303136666136452e6c6c766d2e3138313031373033313138373539303137313631ab0d81015f5a4e39395f244c5424616c6c6f632e2e636f6c6c656374696f6e732e2e62747265652e2e6d61702e2e496e746f49746572244c54244b244324562443244124475424247532302461732475323024636f72652e2e6f70732e2e64726f702e2e44726f70244754243464726f703137686166386362623963653664613164326645ac0d4a5f5a4e396d656d6f72795f646232364d656d6f72794442244c5424482443244b4624432454244754243131636f6e736f6c69646174653137683238313430653033383934626438393345ad0d285f5a4e36737562746c6539626c61636b5f626f783137686536323366376466326130616262376645ae0d3d5f5a4e313274726163696e675f636f7265356669656c6435566973697431307265636f72645f6636343137683032636465383037316532366131373045af0da7015f5a4e3132375f244c5424244c542474726163696e672e2e6c6f672e2e4c6f6756616c7565536574247532302461732475323024636f72652e2e666d742e2e446973706c6179244754242e2e666d742e2e4c6f6756697369746f7224753230246173247532302474726163696e675f636f72652e2e6669656c642e2e56697369742447542431327265636f72645f64656275673137683035623037636631373862623438333145b00d3d5f5a4e313274726163696e675f636f7265356669656c6435566973697431307265636f72645f6936343137683433396262373930623838383436626245b10d3d5f5a4e313274726163696e675f636f7265356669656c6435566973697431307265636f72645f7536343137683639653062656331666361353130633545b20d3e5f5a4e313274726163696e675f636f7265356669656c6435566973697431317265636f72645f626f6f6c3137683762663261356164643464383863343345b30d3e5f5a4e313274726163696e675f636f7265356669656c6435566973697431317265636f72645f693132383137683963623938653735633836613462336245b40d3e5f5a4e313274726163696e675f636f7265356669656c6435566973697431317265636f72645f753132383137686561396665346331326634353739303845b50d465f5a4e34315f244c5424626f6f6c247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683465363937313737333463656137303845b60d475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683232363036316239616165643162393045b70d475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137686330653239363533313033343163393845b80d495f5a4e34345f244c54242452462454247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d743137683730376362363161356265646162303345b90d5c5f5a4e34636f726533666d74336e756d35305f244c5424696d706c2475323024636f72652e2e666d742e2e44656275672475323024666f7224753230246936342447542433666d743137686334636434656639303331326536626445ba0d5c5f5a4e34636f726533666d74336e756d35305f244c5424696d706c2475323024636f72652e2e666d742e2e44656275672475323024666f7224753230247536342447542433666d743137683264396263656536333331626632613145bb0d5d5f5a4e34636f726533666d74336e756d35315f244c5424696d706c2475323024636f72652e2e666d742e2e44656275672475323024666f722475323024693132382447542433666d743137686137323862663432373035353335623445bc0d5d5f5a4e34636f726533666d74336e756d35315f244c5424696d706c2475323024636f72652e2e666d742e2e44656275672475323024666f722475323024753132382447542433666d743137683566373264343963666530306435336645bd0d88015f5a4e34636f72653370747231303164726f705f696e5f706c616365244c5424244c542474726163696e672e2e6c6f672e2e4c6f6756616c7565536574247532302461732475323024636f72652e2e666d742e2e446973706c6179244754242e2e666d742e2e4c6f6756697369746f72244754243137686562653432376662666164633931376245be0d3a5f5a4e34636f726533707472323464726f705f696e5f706c616365244c5424663634244754243137686339666135376563323131306438396245bf0d3b5f5a4e34636f726533707472323564726f705f696e5f706c616365244c5424626f6f6c244754243137686139326339383939636639633037383445c00d3f5f5a4e3774726163696e6731355f5f6d6163726f5f737570706f727431335f5f74726163696e675f6c6f673137683532346131313064333765356338383145c10d775f5a4e36345f244c542474726163696e672e2e6c6f672e2e4c6f6756616c7565536574247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d7431376837366261326436393237626538666137452e6c6c766d2e3131373638333234303836353937383036383133c20da5015f5a4e3132375f244c5424244c542474726163696e672e2e6c6f672e2e4c6f6756616c7565536574247532302461732475323024636f72652e2e666d742e2e446973706c6179244754242e2e666d742e2e4c6f6756697369746f7224753230246173247532302474726163696e675f636f72652e2e6669656c642e2e56697369742447542431307265636f72645f7374723137683339316662663266616136303662393345c30d475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683166323337343539633033646162363345c40d4b5f5a4e34636f726533707472343164726f705f696e5f706c616365244c5424636f72652e2e666d742e2e417267756d656e7473244754243137683764303763313638373039316236653745c50d485f5a4e313274726163696e675f636f72653863616c6c73697465313544656661756c7443616c6c736974653872656769737465723137686630623433383431666539386634623145c60d83015f5a4e39325f244c542474726163696e675f636f72652e2e63616c6c736974652e2e44656661756c7443616c6c7369746524753230246173247532302474726163696e675f636f72652e2e63616c6c736974652e2e43616c6c736974652447542431327365745f696e7465726573743137686361386138323934313130643135363145c70d635f5a4e36375f244c5424636f72652e2e666d742e2e417267756d656e747324753230246173247532302474726163696e675f636f72652e2e6669656c642e2e56616c756524475424367265636f72643137683939643261656636643838393265613545c80d475f5a4e313274726163696e675f636f7265313073756273637269626572313053756273637269626572397472795f636c6f73653137686263386632666133646235333434646145c90d4b5f5a4e313274726163696e675f636f72653130737562736372696265723130537562736372696265723132646f776e636173745f7261773137683037653037303361386630383366383545ca0d81015f5a4e39355f244c542474726163696e675f636f72652e2e737562736372696265722e2e4e6f5375627363726962657224753230246173247532302474726163696e675f636f72652e2e737562736372696265722e2e5375627363726962657224475424386e65775f7370616e3137683562646637646638623162643266616545cb0d775f5a4e34636f726533707472353964726f705f696e5f706c616365244c542474726163696e675f636f72652e2e737562736372696265722e2e4e6f537562736372696265722447542431376833373964396464396234373139633638452e6c6c766d2e3130323639303030353131313334363639333430cc0d6d5f5a4e313274726163696e675f636f726531307375627363726962657231305375627363726962657232306f6e5f72656769737465725f646973706174636831376834643661353662653364386230313431452e6c6c766d2e3130323639303030353131313334363639333430cd0d675f5a4e313274726163696e675f636f726531307375627363726962657231305375627363726962657231346d61785f6c6576656c5f68696e7431376833343066346339643730633865343032452e6c6c766d2e3130323639303030353131313334363639333430ce0d665f5a4e313274726163696e675f636f726531307375627363726962657231305375627363726962657231336576656e745f656e61626c656431376833616263323733313839313039623462452e6c6c766d2e3130323639303030353131313334363639333430cf0d635f5a4e313274726163696e675f636f72653130737562736372696265723130537562736372696265723130636c6f6e655f7370616e31376835383138383935623936343235316638452e6c6c766d2e3130323639303030353131313334363639333430d00d615f5a4e313274726163696e675f636f72653130737562736372696265723130537562736372696265723964726f705f7370616e31376862323464363466303435306434636233452e6c6c766d2e3130323639303030353131313334363639333430d10d655f5a4e313274726163696e675f636f7265313073756273637269626572313053756273637269626572313263757272656e745f7370616e31376835396432613239363934366337646462452e6c6c766d2e3130323639303030353131313334363639333430d20da5015f5a4e39355f244c542474726163696e675f636f72652e2e737562736372696265722e2e4e6f5375627363726962657224753230246173247532302474726163696e675f636f72652e2e737562736372696265722e2e5375627363726962657224475424313772656769737465725f63616c6c7369746531376866323833386662633532643466316435452e6c6c766d2e3130323639303030353131313334363639333430d30d99015f5a4e39355f244c542474726163696e675f636f72652e2e737562736372696265722e2e4e6f5375627363726962657224753230246173247532302474726163696e675f636f72652e2e737562736372696265722e2e5375627363726962657224475424367265636f726431376831306563386338633164663962363030452e6c6c766d2e3130323639303030353131313334363639333430d40da7015f5a4e39355f244c542474726163696e675f636f72652e2e737562736372696265722e2e4e6f5375627363726962657224753230246173247532302474726163696e675f636f72652e2e737562736372696265722e2e537562736372696265722447542431397265636f72645f666f6c6c6f77735f66726f6d31376838653839643339656135643966376464452e6c6c766d2e3130323639303030353131313334363639333430d50d9a015f5a4e39355f244c542474726163696e675f636f72652e2e737562736372696265722e2e4e6f5375627363726962657224753230246173247532302474726163696e675f636f72652e2e737562736372696265722e2e537562736372696265722447542437656e61626c656431376833323734303061373662396461333461452e6c6c766d2e3130323639303030353131313334363639333430d60d97015f5a4e39355f244c542474726163696e675f636f72652e2e737562736372696265722e2e4e6f5375627363726962657224753230246173247532302474726163696e675f636f72652e2e737562736372696265722e2e5375627363726962657224475424346578697431376863623362646338663464303538383463452e6c6c766d2e3130323639303030353131313334363639333430d70d6e5f5a4e34636f726533707472373664726f705f696e5f706c616365244c542424524624244250246d7574247532302474726163696e675f636f72652e2e63616c6c736974652e2e44656661756c7443616c6c73697465244754243137683334633333616638313230383566333045d80d355f5a4e34636f72653970616e69636b696e6731336173736572745f6661696c65643137683366353738623363653165626438353545d90d6d5f5a4e37747269655f6462366e6962626c6531316e6962626c65736c69636534365f244c5424696d706c2475323024747269655f64622e2e6e6962626c652e2e4e6962626c65536c6963652447542439746f5f73746f7265643137683931356331353631343931613734666145da0d745f5a4e37747269655f6462366e6962626c6531316e6962626c65736c69636534365f244c5424696d706c2475323024747269655f64622e2e6e6962626c652e2e4e6962626c65536c696365244754243135746f5f73746f7265645f72616e67653137686433333136363561393364653634343145db0d725f5a4e37747269655f6462366e6962626c6531316e6962626c65736c69636534365f244c5424696d706c2475323024747269655f64622e2e6e6962626c652e2e4e6962626c65536c696365244754243133636f6d6d6f6e5f7072656669783137683235343737646533326132613162353645dc0d6f5f5a4e37747269655f6462366e6962626c6531316e6962626c65736c69636534365f244c5424696d706c2475323024747269655f64622e2e6e6962626c652e2e4e6962626c65536c69636524475424313072696768745f697465723137686235343230316661316537616364363045dd0d685f5a4e37747269655f6462366e6962626c6531316e6962626c65736c69636534365f244c5424696d706c2475323024747269655f64622e2e6e6962626c652e2e4e6962626c65536c69636524475424346c6566743137686635313365356637333463363664643045de0d745f5a4e37747269655f6462366e6962626c6531316e6962626c65736c69636534365f244c5424696d706c2475323024747269655f64622e2e6e6962626c652e2e4e6962626c65536c6963652447542431357374617274735f776974685f7665633137683734663162383465393461636635356145df0daa015f5a4e37747269655f6462366e6962626c6531316e6962626c65736c6963653131335f244c5424696d706c2475323024636f72652e2e636d702e2e5061727469616c4571244c5424747269655f64622e2e6e6962626c652e2e4e6962626c65566563244754242475323024666f722475323024747269655f64622e2e6e6962626c652e2e4e6962626c65536c696365244754243265713137683963306233343864386130653163663045e00d6a5f5a4e37747269655f6462366e6962626c65396e6962626c6576656334345f244c5424696d706c2475323024747269655f64622e2e6e6962626c652e2e4e6962626c6556656324475424313064726f705f6c617374733137683633363961666232393238326361623545e10d655f5a4e37747269655f6462366e6962626c65396e6962626c6576656334345f244c5424696d706c2475323024747269655f64622e2e6e6962626c652e2e4e6962626c655665632447542436617070656e643137683335666230356161626264346235303145e20d6e5f5a4e37747269655f6462366e6962626c65396e6962626c6576656334345f244c5424696d706c2475323024747269655f64622e2e6e6962626c652e2e4e6962626c65566563244754243134617070656e645f7061727469616c3137683461356639346431393430353232636345e30d80015f5a4e37747269655f6462366e6962626c65396e6962626c6576656334345f244c5424696d706c2475323024747269655f64622e2e6e6962626c652e2e4e6962626c65566563244754243332617070656e645f6f7074696f6e616c5f736c6963655f616e645f6e6962626c653137683635363635313439663931623065336445e40da8015f5a4e37747269655f6462366e6962626c65396e6962626c657665633131325f244c5424696d706c2475323024636f72652e2e636f6e766572742e2e46726f6d244c5424747269655f64622e2e6e6962626c652e2e4e6962626c65536c696365244754242475323024666f722475323024747269655f64622e2e6e6962626c652e2e4e6962626c65566563244754243466726f6d3137686534366236343339336536646133666645e50d705f5a4e34636f726533707472353364726f705f696e5f706c616365244c5424636f72652e2e616c6c6f632e2e6c61796f75742e2e4c61796f75744572726f722447542431376861626564323066396436393063386566452e6c6c766d2e31333436363036313031373639303532353734e60d7b5f5a4e36395f244c5424636f72652e2e616c6c6f632e2e6c61796f75742e2e4c61796f75744572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376832393637613332326332613165663338452e6c6c766d2e31333436363036313031373639303532353734e70d365f5a4e37747269655f6462397472696564626d75743131636f6d62696e655f6b65793137683530623530376135646631343734613145e80d3c5f5a4e37747269655f6462366e6962626c6531306e6962626c655f6f70733973686966745f6b65793137683339633735366532633839303766323745e90d6c5f5a4e37385f244c5424747269655f64622e2e4279746573247532302461732475323024636f72652e2e636f6e766572742e2e46726f6d244c54242452462424753562247538247535642424475424244754243466726f6d3137683564653466383332346233346439333145ea0d475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683538363636663532356634663137613045eb0d475f5a4e34325f244c54242452462454247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d743137683666353731353236306639323165623745ec0d495f5a4e34345f244c54242452462454247532302461732475323024636f72652e2e666d742e2e446973706c61792447542433666d743137686265326338666336373362383163383845ed0d5e5f5a4e34636f726533666d74336e756d35325f244c5424696d706c2475323024636f72652e2e666d742e2e44656275672475323024666f7224753230247573697a652447542433666d743137686464646433363838656435613663326345ee0d3c5f5a4e34636f726533707472323664726f705f696e5f706c616365244c54247573697a65244754243137686239336233313532626436383233646645ef0d705f5a4e34636f726533707472353364726f705f696e5f706c616365244c5424636f72652e2e616c6c6f632e2e6c61796f75742e2e4c61796f75744572726f722447542431376861626564323066396436393063386566452e6c6c766d2e36343031373133383638333435343636393034f00d6b5f5a4e35616c6c6f633473796e633136417263244c542454244324412447542439646f776e6772616465313870616e69635f636f6c645f646973706c617931376863653662613065323361326562653438452e6c6c766d2e36343031373133383638333435343636393034f10d3e5f5a4e35616c6c6f633473796e633136417263244c54245424432441244754243964726f705f736c6f773137683564653931393331643466363565636645f20d7d5f5a4e35616c6c6f633473796e6331375765616b244c542454244324412447542437757067726164653137636865636b65645f696e6372656d656e74313870616e69635f636f6c645f646973706c617931376865663764356135623466353639313230452e6c6c766d2e36343031373133383638333435343636393034f30d7b5f5a4e36395f244c5424636f72652e2e616c6c6f632e2e6c61796f75742e2e4c61796f75744572726f72247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376832393637613332326332613165663338452e6c6c766d2e36343031373133383638333435343636393034f40d335f5a4e37747269655f6462346e6f6465384e6f6465506c616e356275696c643137686538616464623066313161666230323545f50da6015f5a4e3133335f244c5424736d616c6c7665632e2e536d616c6c566563244c54244124475424247532302461732475323024636f72652e2e697465722e2e7472616974732e2e636f6c6c6563742e2e457874656e64244c5424244c542441247532302461732475323024736d616c6c7665632e2e4172726179244754242e2e4974656d244754242447542436657874656e643137683630616130333963383834623164346145f60d555f5a4e38736d616c6c7665633137536d616c6c566563244c54244124475424387472795f67726f7731376835386632666135343233623631386433452e6c6c766d2e36393837333237333039333733343939333838f70d4a5f5a4e38736d616c6c7665633137536d616c6c566563244c542441244754243231726573657276655f6f6e655f756e636865636b65643137683764353134363435393339633539333945f80d6c5f5a4e34636f726533707472343964726f705f696e5f706c616365244c5424736d616c6c7665632e2e436f6c6c656374696f6e416c6c6f634572722447542431376862336365363536333030343731626264452e6c6c766d2e36393837333237333039333733343939333838f90d775f5a4e36355f244c5424736d616c6c7665632e2e436f6c6c656374696f6e416c6c6f63457272247532302461732475323024636f72652e2e666d742e2e44656275672447542433666d7431376833333136353566633965353230393634452e6c6c766d2e36393837333237333039333733343939333838fa0dd2015f5a4e37747269655f6462366e6962626c65396e6962626c657665633135345f244c5424696d706c2475323024636f72652e2e636f6e766572742e2e46726f6d244c542424524624747269655f64622e2e6e6962626c652e2e4e6962626c65566563244754242475323024666f722475323024244c50247573697a65244324736d616c6c7665632e2e536d616c6c566563244c54242475356224753824753362242475323024343024753564242447542424525024244754243466726f6d3137683666336266376235316639313062633945fb0d515f5a4e3137636f6d70696c65725f6275696c74696e7333696e7431397370656369616c697a65645f6469765f72656d3132753132385f6469765f72656d3137686430656666646565363632343038643145fc0d095f5f6c736872746933fd0d3b5f5a4e3137636f6d70696c65725f6275696c74696e7333696e74336d756c385f5f6d756c7469333137683733666338306336316261303338656345fe0d076d656d6d6f7665ff0d3d5f5a4e3137636f6d70696c65725f6275696c74696e7333696e743475646976395f5f756469767469333137683033376537333965303466656466353445800e355f5a4e3137636f6d70696c65725f6275696c74696e73336d656d366d656d6370793137683336626139613364623131396632306145810e365f5a4e3137636f6d70696c65725f6275696c74696e73336d656d376d656d6d6f76653137683732666133663134303163653961663645820e355f5a4e3137636f6d70696c65725f6275696c74696e73336d656d366d656d7365743137686131343663346337393933363137383845830e355f5a4e3137636f6d70696c65725f6275696c74696e73336d656d366d656d636d703137683935383264343235323262343232666245840e066d656d637079850e095f5f6173686c746933860e3e5f5a4e3137636f6d70696c65725f6275696c74696e7333696e74357368696674395f5f6c7368727469333137686430316336383630363837336164333045870e085f5f6d756c746933880e066d656d636d70890e095f5f756469767469338a0e066d656d736574071201000f5f5f737461636b5f706f696e74657209170300072e726f6461746101052e6461746102042e627373003d0970726f647563657273010c70726f6365737365642d6279010572757374631d312e37372e30202861656464313733613220323032342d30332d313729", + "config": { + "babe": { + "authorities": [], + "epochConfig": { + "allowed_slots": "PrimaryAndSecondaryVRFSlots", + "c": [ + 1, + 4 + ] + } + }, + "balances": { + "balances": [] + }, + "substrateTest": { + "authorities": [] + }, + "system": {} + } + } + } +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/input/chain_spec_plain.json b/substrate/bin/utils/chain-spec-builder/tests/input/chain_spec_plain.json new file mode 100644 index 0000000000000000000000000000000000000000..fe1fb889f27850841d4dc57887e02d98e480de81 --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/input/chain_spec_plain.json @@ -0,0 +1,40 @@ +{ + "name": "Custom", + "id": "custom", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "relay_chain": "rococo-local", + "para_id": 10101, + "custom_field": "custom_value", + "codeSubstitutes": {}, + "genesis": { + "runtimeGenesis": { + "code": "0x010203", + "config": { + "babe": { + "authorities": [], + "epochConfig": { + "allowed_slots": "PrimaryAndSecondaryVRFSlots", + "c": [ + 1, + 4 + ] + } + }, + "balances": { + "balances": [] + }, + "substrateTest": { + "authorities": [] + }, + "system": {} + } + } + } +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/input/chain_spec_raw.json b/substrate/bin/utils/chain-spec-builder/tests/input/chain_spec_raw.json new file mode 100644 index 0000000000000000000000000000000000000000..0501d6cbe45b645e9b34bec58537063d264b3a67 --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/input/chain_spec_raw.json @@ -0,0 +1,38 @@ +{ + "name": "Custom", + "id": "custom", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "tokenDecimals": 12, + "tokenSymbol": "UNIT" + }, + "codeSubstitutes": {}, + "genesis": { + "raw": { + "top": { + "0x00771836bebdd29870ff246d305c578c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x00771836bebdd29870ff246d305c578c5e0621c4869aa60c02be9adcc98a0d1d": "0x0cd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d1cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c186e1bafbb1430668c95d89b77217a402a74f64c3e103137b69e95e4b6e06b1e", + "0x1cb6f36e027abb2091cfb5110ab5087f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x1cb6f36e027abb2091cfb5110ab5087f66e8f035c8adbe7f1547b43c51e6f8a4": "0x00000000", + "0x1cb6f36e027abb2091cfb5110ab5087fdc6b171b77304263c292cc3ea5ed31ef": "0x0100000000000000040000000000000002", + "0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746bb1bdbcacd6ac9340000000000000000": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da92c2a60ec6dd16cd8ab911865ecf7555b186e1bafbb1430668c95d89b77217a402a74f64c3e103137b69e95e4b6e06b1e": "0x00000000000000000000000001000000000000000080e03779c311000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da94f9aea1afa791265fae359272badc1cf8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48": "0x00000000000000000000000001000000000000000080c6a47e8d03000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9b0edae20838083f2cde1c4080db8cf8090b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22": "0x00000000000000000000000001000000000000000080c6a47e8d03000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x0000", + "0x3a636f6465": "0x010203", + "0x3a65787472696e7369635f696e646578": "0x00000000", + "0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x00806d8176de1800" + }, + "childrenDefault": {} + } + } +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/input/code_040506.blob b/substrate/bin/utils/chain-spec-builder/tests/input/code_040506.blob new file mode 100644 index 0000000000000000000000000000000000000000..2dd38093cfdb39ef65a9b58bd2fbb7aa3b5abf70 --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/input/code_040506.blob @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/substrate/bin/utils/chain-spec-builder/tests/input/full.json b/substrate/bin/utils/chain-spec-builder/tests/input/full.json new file mode 100644 index 0000000000000000000000000000000000000000..f05e3505a2bb662042483c32aa6210fc74ba3124 --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/input/full.json @@ -0,0 +1,40 @@ +{ + "babe": { + "authorities": [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", + "5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b" + ], + "epochConfig": { + "allowed_slots": "PrimaryAndSecondaryVRFSlots", + "c": [ + 2, + 4 + ] + } + }, + "balances": { + "balances": [ + [ + "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + 2000000000000000 + ], + [ + "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", + 2000000000000000 + ], + [ + "5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b", + 5000000000000000 + ] + ] + }, + "substrateTest": { + "authorities": [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", + "5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b" + ] + }, + "system": {} +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/input/patch.json b/substrate/bin/utils/chain-spec-builder/tests/input/patch.json new file mode 100644 index 0000000000000000000000000000000000000000..cd909bbe3c3f904594de53839c6d0c7c748cd595 --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/input/patch.json @@ -0,0 +1,25 @@ +{ + "balances": { + "balances": [ + [ + "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + 1000000000000000 + ], + [ + "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", + 1000000000000000 + ], + [ + "5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b", + 5000000000000000 + ] + ] + }, + "substrateTest": { + "authorities": [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", + "5CcjiSgG2KLuKAsqkE2Nak1S2FbAcMr5SxRASUuwR3zSNV2b" + ] + } +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/test.rs b/substrate/bin/utils/chain-spec-builder/tests/test.rs new file mode 100644 index 0000000000000000000000000000000000000000..f553f05f20a049aecaab7e06d86e01d3685a326e --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/test.rs @@ -0,0 +1,194 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use std::fs::File; + +use clap::Parser; +use sc_chain_spec::update_code_in_json_chain_spec; +use staging_chain_spec_builder::ChainSpecBuilder; + +// note: the runtime path will not be read, runtime code will be set directly, to avoid hassle with +// creating the wasm file or providing a valid existing path during test execution. +const DUMMY_PATH: &str = "fake-runtime-path"; + +const OUTPUT_FILE: &str = "/tmp/chain_spec_builder.test_output_file.json"; + +/// Asserts that the JSON in output file matches the JSON in expected file. +/// +/// This helper function reads the JSON content from the file at `OUTPUT_FILE + suffix` path. If the +/// `overwrite_code` flag is set, it updates the output chain specification with a sample code +/// vector `[1, 2, 3]` (to avoid bulky *expected* files), and then compares it against the JSON +/// content from the given `expected_path`. +fn assert_output_eq_expected(overwrite_code: bool, output_suffix: &str, expected_path: &str) { + let path = OUTPUT_FILE.to_string() + output_suffix; + let mut output: serde_json::Value = + serde_json::from_reader(File::open(path.clone()).unwrap()).unwrap(); + if overwrite_code { + update_code_in_json_chain_spec(&mut output, &vec![1, 2, 3]); + } + let expected: serde_json::Value = + serde_json::from_reader(File::open(expected_path).unwrap()).unwrap(); + + assert_eq!(expected, output); + + std::fs::remove_file(path).expect("Failed to delete file"); +} + +fn get_builder(suffix: &str, command_args: Vec<&str>) -> ChainSpecBuilder { + let path = OUTPUT_FILE.to_string() + suffix; + let mut base_args = vec!["dummy", "-c", path.as_str()]; + base_args.extend(command_args); + ChainSpecBuilder::parse_from(base_args) +} + +#[test] +fn test_create_default() { + const SUFFIX: &str = "00"; + let mut builder = get_builder(SUFFIX, vec!["create", "-r", DUMMY_PATH, "default"]); + builder.set_create_cmd_runtime_code(substrate_test_runtime::WASM_BINARY.unwrap().into()); + builder.run().unwrap(); + assert_output_eq_expected(true, SUFFIX, "tests/expected/create_default.json"); +} + +#[test] +fn test_create_with_named_preset() { + const SUFFIX: &str = "01"; + let mut builder = + get_builder(SUFFIX, vec!["create", "-r", DUMMY_PATH, "named-preset", "staging"]); + builder.set_create_cmd_runtime_code(substrate_test_runtime::WASM_BINARY.unwrap().into()); + builder.run().unwrap(); + assert_output_eq_expected(true, SUFFIX, "tests/expected/create_with_named_preset.json"); +} + +#[test] +fn test_create_with_patch() { + const SUFFIX: &str = "02"; + let mut builder = + get_builder(SUFFIX, vec!["create", "-r", DUMMY_PATH, "patch", "tests/input/patch.json"]); + builder.set_create_cmd_runtime_code(substrate_test_runtime::WASM_BINARY.unwrap().into()); + builder.run().unwrap(); + assert_output_eq_expected(true, SUFFIX, "tests/expected/create_with_patch.json"); +} + +#[test] +fn test_create_with_full() { + const SUFFIX: &str = "03"; + let mut builder = + get_builder(SUFFIX, vec!["create", "-r", DUMMY_PATH, "full", "tests/input/full.json"]); + builder.set_create_cmd_runtime_code(substrate_test_runtime::WASM_BINARY.unwrap().into()); + builder.run().unwrap(); + assert_output_eq_expected(true, SUFFIX, "tests/expected/create_with_full.json"); +} + +#[test] +fn test_create_with_params() { + const SUFFIX: &str = "04"; + let mut builder = get_builder( + SUFFIX, + vec!["create", "-r", DUMMY_PATH, "-n", "test_chain", "-i", "100", "-t", "live", "default"], + ); + builder.set_create_cmd_runtime_code(substrate_test_runtime::WASM_BINARY.unwrap().into()); + builder.run().unwrap(); + assert_output_eq_expected(true, SUFFIX, "tests/expected/create_with_params.json"); +} + +#[test] +fn test_create_parachain() { + const SUFFIX: &str = "05"; + let mut builder = get_builder( + SUFFIX, + vec![ + "create", + "-r", + DUMMY_PATH, + "-n", + "test_chain", + "-i", + "100", + "-t", + "live", + "--para-id", + "10101", + "--relay-chain", + "rococo-local", + "default", + ], + ); + builder.set_create_cmd_runtime_code(substrate_test_runtime::WASM_BINARY.unwrap().into()); + builder.run().unwrap(); + assert_output_eq_expected(true, SUFFIX, "tests/expected/create_parachain.json"); +} + +#[test] +fn test_create_raw_storage() { + const SUFFIX: &str = "06"; + let mut builder = get_builder( + SUFFIX, + vec!["create", "-r", DUMMY_PATH, "-s", "patch", "tests/input/patch.json"], + ); + builder.set_create_cmd_runtime_code(substrate_test_runtime::WASM_BINARY.unwrap().into()); + builder.run().unwrap(); + assert_output_eq_expected(true, SUFFIX, "tests/expected/create_raw_storage.json"); +} + +#[test] +fn test_update_code() { + const SUFFIX: &str = "07"; + let builder = get_builder( + SUFFIX, + vec!["update-code", "tests/input/chain_spec_plain.json", "tests/input/code_040506.blob"], + ); + builder.run().unwrap(); + assert_output_eq_expected(false, SUFFIX, "tests/expected/update_code.json"); +} + +#[test] +fn test_update_code_raw() { + const SUFFIX: &str = "08"; + let builder = get_builder( + SUFFIX, + vec!["update-code", "tests/input/chain_spec_raw.json", "tests/input/code_040506.blob"], + ); + builder.run().unwrap(); + assert_output_eq_expected(false, SUFFIX, "tests/expected/update_code_raw.json"); +} + +#[test] +fn test_convert_to_raw() { + const SUFFIX: &str = "09"; + let builder = + get_builder(SUFFIX, vec!["convert-to-raw", "tests/input/chain_spec_conversion_test.json"]); + builder.run().unwrap(); + assert_output_eq_expected(true, SUFFIX, "tests/expected/convert_to_raw.json"); +} + +#[test] +fn test_add_code_substitute() { + const SUFFIX: &str = "10"; + let builder = get_builder( + SUFFIX, + vec![ + "add-code-substitute", + "tests/input/chain_spec_plain.json", + "tests/input/code_040506.blob", + "100", + ], + ); + builder.run().unwrap(); + assert_output_eq_expected(true, SUFFIX, "tests/expected/add_code_substitute.json"); +} diff --git a/substrate/client/api/src/backend.rs b/substrate/client/api/src/backend.rs index 0b2a349524018d33b8003b6832363286b77d8503..9c9601a912acb22da431fff56735723add366f5e 100644 --- a/substrate/client/api/src/backend.rs +++ b/substrate/client/api/src/backend.rs @@ -232,6 +232,9 @@ pub trait BlockImportOperation { /// Add a transaction index operation. fn update_transaction_index(&mut self, index: Vec) -> sp_blockchain::Result<()>; + + /// Configure whether to create a block gap if newly imported block is missing parent + fn set_create_gap(&mut self, create_gap: bool); } /// Interface for performing operations on the backend. diff --git a/substrate/client/api/src/client.rs b/substrate/client/api/src/client.rs index 45cfafb258463a50c2ce0ce5b843346bd6cb9dcb..764930984ed716f4725bd67c618e0c690cfa59fa 100644 --- a/substrate/client/api/src/client.rs +++ b/substrate/client/api/src/client.rs @@ -65,9 +65,16 @@ pub trait BlockOf { pub trait BlockchainEvents { /// Get block import event stream. /// - /// Not guaranteed to be fired for every imported block, only fired when the node - /// has synced to the tip or there is a re-org. Use `every_import_notification_stream()` - /// if you want a notification of every imported block regardless. + /// Not guaranteed to be fired for every imported block. Use + /// `every_import_notification_stream()` if you want a notification of every imported block + /// regardless. + /// + /// The events for this notification stream are emitted: + /// - During initial sync process: if there is a re-org while importing blocks. See + /// [here](https://github.com/paritytech/substrate/pull/7118#issuecomment-694091901) for the + /// rationale behind this. + /// - After initial sync process: on every imported block, regardless of whether it is + /// the new best block or not, causes a re-org or not. fn import_notification_stream(&self) -> ImportNotifications; /// Get a stream of every imported block. diff --git a/substrate/client/api/src/in_mem.rs b/substrate/client/api/src/in_mem.rs index ba89aede9147ff0ebe16beb6fedf39389ee9c700..c045a393bb216d412db8e654d09454f3e50810bc 100644 --- a/substrate/client/api/src/in_mem.rs +++ b/substrate/client/api/src/in_mem.rs @@ -584,6 +584,8 @@ impl backend::BlockImportOperation for BlockImportOperatio ) -> sp_blockchain::Result<()> { Ok(()) } + + fn set_create_gap(&mut self, _create_gap: bool) {} } /// In-memory backend. Keeps all states and blocks in memory. diff --git a/substrate/client/authority-discovery/src/worker/tests.rs b/substrate/client/authority-discovery/src/worker/tests.rs index b49615382b8a2b2734185da50fd5b9515d42b1d7..d71a85db8b813004ba74773a1d818c8b70dd1cff 100644 --- a/substrate/client/authority-discovery/src/worker/tests.rs +++ b/substrate/client/authority-discovery/src/worker/tests.rs @@ -117,10 +117,10 @@ sp_api::mock_impl_runtime_apis! { #[derive(Debug)] pub enum TestNetworkEvent { - GetCalled(KademliaKey), - PutCalled(KademliaKey, Vec), - PutToCalled(Record, HashSet, bool), - StoreRecordCalled(KademliaKey, Vec, Option, Option), + GetCalled, + PutCalled, + PutToCalled, + StoreRecordCalled, } pub struct TestNetwork { @@ -190,17 +190,11 @@ impl NetworkSigner for TestNetwork { impl NetworkDHTProvider for TestNetwork { fn put_value(&self, key: KademliaKey, value: Vec) { self.put_value_call.lock().unwrap().push((key.clone(), value.clone())); - self.event_sender - .clone() - .unbounded_send(TestNetworkEvent::PutCalled(key, value)) - .unwrap(); + self.event_sender.clone().unbounded_send(TestNetworkEvent::PutCalled).unwrap(); } fn get_value(&self, key: &KademliaKey) { self.get_value_call.lock().unwrap().push(key.clone()); - self.event_sender - .clone() - .unbounded_send(TestNetworkEvent::GetCalled(key.clone())) - .unwrap(); + self.event_sender.clone().unbounded_send(TestNetworkEvent::GetCalled).unwrap(); } fn put_record_to( @@ -214,10 +208,7 @@ impl NetworkDHTProvider for TestNetwork { peers.clone(), update_local_storage, )); - self.event_sender - .clone() - .unbounded_send(TestNetworkEvent::PutToCalled(record, peers, update_local_storage)) - .unwrap(); + self.event_sender.clone().unbounded_send(TestNetworkEvent::PutToCalled).unwrap(); } fn store_record( @@ -235,7 +226,7 @@ impl NetworkDHTProvider for TestNetwork { )); self.event_sender .clone() - .unbounded_send(TestNetworkEvent::StoreRecordCalled(key, value, publisher, expires)) + .unbounded_send(TestNetworkEvent::StoreRecordCalled) .unwrap(); } } @@ -536,7 +527,7 @@ fn dont_stop_polling_dht_event_stream_after_bogus_event() { pool.run_until(async { // Assert worker to trigger a lookup for the one and only authority. - assert!(matches!(network_events.next().await, Some(TestNetworkEvent::GetCalled(_)))); + assert!(matches!(network_events.next().await, Some(TestNetworkEvent::GetCalled))); // Send an event that should generate an error dht_event_tx @@ -1137,7 +1128,7 @@ fn lookup_throttling() { async { // Assert worker to trigger MAX_IN_FLIGHT_LOOKUPS lookups. for _ in 0..MAX_IN_FLIGHT_LOOKUPS { - assert!(matches!(receiver.next().await, Some(TestNetworkEvent::GetCalled(_)))); + assert!(matches!(receiver.next().await, Some(TestNetworkEvent::GetCalled))); } assert_eq!( metrics.requests_pending.get(), @@ -1168,7 +1159,7 @@ fn lookup_throttling() { } // Assert worker to trigger another lookup. - assert!(matches!(receiver.next().await, Some(TestNetworkEvent::GetCalled(_)))); + assert!(matches!(receiver.next().await, Some(TestNetworkEvent::GetCalled))); assert_eq!( metrics.requests_pending.get(), (remote_public_keys.len() - MAX_IN_FLIGHT_LOOKUPS - 1) as u64 @@ -1181,7 +1172,7 @@ fn lookup_throttling() { dht_event_tx.send(dht_event).await.expect("Channel has capacity of 1."); // Assert worker to trigger another lookup. - assert!(matches!(receiver.next().await, Some(TestNetworkEvent::GetCalled(_)))); + assert!(matches!(receiver.next().await, Some(TestNetworkEvent::GetCalled))); assert_eq!( metrics.requests_pending.get(), (remote_public_keys.len() - MAX_IN_FLIGHT_LOOKUPS - 2) as u64 diff --git a/substrate/client/block-builder/src/lib.rs b/substrate/client/block-builder/src/lib.rs index 2f22cd42591fc60bf1665d00a1c5b2b548ad3aea..d02d0e32180519c667b42aa737ca083465be644f 100644 --- a/substrate/client/block-builder/src/lib.rs +++ b/substrate/client/block-builder/src/lib.rs @@ -320,7 +320,7 @@ where header.extrinsics_root().clone(), HashingFor::::ordered_trie_root( self.extrinsics.iter().map(Encode::encode).collect(), - sp_runtime::StateVersion::V0, + self.api.version(self.parent_hash)?.extrinsics_root_state_version(), ), ); diff --git a/substrate/client/chain-spec/src/genesis_config_builder.rs b/substrate/client/chain-spec/src/genesis_config_builder.rs index 13a2f3c072f5ace38cd48eb0f2f4492b60474e5b..66989495d4231bc933efc34bdd2a2d726f9f5df3 100644 --- a/substrate/client/chain-spec/src/genesis_config_builder.rs +++ b/substrate/client/chain-spec/src/genesis_config_builder.rs @@ -27,6 +27,7 @@ use sp_core::{ traits::{CallContext, CodeExecutor, Externalities, FetchRuntimeCode, RuntimeCode}, }; use sp_genesis_builder::{PresetId, Result as BuildResult}; +pub use sp_genesis_builder::{DEV_RUNTIME_PRESET, LOCAL_TESTNET_RUNTIME_PRESET}; use sp_state_machine::BasicExternalities; use std::borrow::Cow; diff --git a/substrate/client/chain-spec/src/lib.rs b/substrate/client/chain-spec/src/lib.rs index 5451428d34811bfa54cb87a767d8434b5f4b7524..43639ffb5aaeffffc5f9fdf78808376222ae7eed 100644 --- a/substrate/client/chain-spec/src/lib.rs +++ b/substrate/client/chain-spec/src/lib.rs @@ -347,7 +347,9 @@ pub use self::{ construct_genesis_block, resolve_state_version_from_wasm, BuildGenesisBlock, GenesisBlockBuilder, }, - genesis_config_builder::GenesisConfigBuilderRuntimeCaller, + genesis_config_builder::{ + GenesisConfigBuilderRuntimeCaller, DEV_RUNTIME_PRESET, LOCAL_TESTNET_RUNTIME_PRESET, + }, json_patch::merge as json_merge, }; pub use sc_chain_spec_derive::{ChainSpecExtension, ChainSpecGroup}; @@ -357,9 +359,9 @@ use sc_telemetry::TelemetryEndpoints; use sp_core::storage::Storage; use sp_runtime::BuildStorage; -/// The type of a chain. +/// The type of chain. /// -/// This can be used by tools to determine the type of a chain for displaying +/// This can be used by tools to determine the type of chain for displaying /// additional information or enabling additional features. #[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)] #[cfg_attr(feature = "clap", derive(clap::ValueEnum))] diff --git a/substrate/client/cli/src/commands/import_blocks_cmd.rs b/substrate/client/cli/src/commands/import_blocks_cmd.rs index 815c6ab18aa6cdef0fd217b06333ec4e059912c4..6bd607901e388b179712058bb173904c10544b7a 100644 --- a/substrate/client/cli/src/commands/import_blocks_cmd.rs +++ b/substrate/client/cli/src/commands/import_blocks_cmd.rs @@ -28,7 +28,7 @@ use sp_runtime::traits::Block as BlockT; use std::{ fmt::Debug, fs, - io::{self, Read, Seek}, + io::{self, Read}, path::PathBuf, sync::Arc, }; @@ -58,11 +58,6 @@ pub struct ImportBlocksCmd { pub import_params: ImportParams, } -/// Internal trait used to cast to a dynamic type that implements Read and Seek. -trait ReadPlusSeek: Read + Seek {} - -impl ReadPlusSeek for T {} - impl ImportBlocksCmd { /// Run the import-blocks command pub async fn run(&self, client: Arc, import_queue: IQ) -> error::Result<()> diff --git a/substrate/client/cli/src/commands/run_cmd.rs b/substrate/client/cli/src/commands/run_cmd.rs index 7245b46e2f7d01990a39d4403b27f7db50b793b6..f91d18aca749730cd110853e2c83ed4cb4a41128 100644 --- a/substrate/client/cli/src/commands/run_cmd.rs +++ b/substrate/client/cli/src/commands/run_cmd.rs @@ -17,15 +17,12 @@ // along with this program. If not, see . use crate::{ - arg_enums::{Cors, RpcMethods}, error::{Error, Result}, params::{ ImportParams, KeystoreParams, NetworkParams, OffchainWorkerParams, RpcEndpoint, SharedParams, TransactionPoolParams, }, - CliConfiguration, PrometheusParams, RuntimeParams, TelemetryParams, - RPC_DEFAULT_MAX_CONNECTIONS, RPC_DEFAULT_MAX_REQUEST_SIZE_MB, RPC_DEFAULT_MAX_RESPONSE_SIZE_MB, - RPC_DEFAULT_MAX_SUBS_PER_CONN, RPC_DEFAULT_MESSAGE_CAPACITY_PER_CONN, + CliConfiguration, PrometheusParams, RpcParams, RuntimeParams, TelemetryParams, }; use clap::Parser; use regex::Regex; @@ -36,10 +33,7 @@ use sc_service::{ ChainSpec, Role, }; use sc_telemetry::TelemetryEndpoints; -use std::{ - net::{Ipv4Addr, Ipv6Addr, SocketAddr}, - num::NonZeroU32, -}; +use std::num::NonZeroU32; /// The `run` command used to run a node. #[derive(Debug, Clone, Parser)] @@ -59,154 +53,16 @@ pub struct RunCmd { #[arg(long)] pub no_grandpa: bool, - /// Listen to all RPC interfaces (default: local). - /// - /// 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)] - pub rpc_external: bool, - - /// Listen to all RPC interfaces. - /// - /// Same as `--rpc-external`. - #[arg(long)] - pub unsafe_rpc_external: bool, - - /// RPC methods to expose. - #[arg( - long, - value_name = "METHOD SET", - value_enum, - ignore_case = true, - default_value_t = RpcMethods::Auto, - verbatim_doc_comment - )] - 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, - - /// Disable RPC rate limiting for certain ip addresses. - /// - /// Each IP address must be in CIDR notation such as `1.2.3.4/24`. - #[arg(long, num_args = 1..)] - pub rpc_rate_limit_whitelisted_ips: Vec, - - /// Trust proxy headers for disable rate limiting. - /// - /// By default the rpc server will not trust headers such `X-Real-IP`, `X-Forwarded-For` and - /// `Forwarded` and this option will make the rpc server to trust these headers. - /// - /// For instance this may be secure if the rpc server is behind a reverse proxy and that the - /// proxy always sets these headers. - #[arg(long)] - pub rpc_rate_limit_trust_proxy_headers: bool, - - /// 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, - - /// Set the maximum RPC response payload size for both HTTP and WS in megabytes. - #[arg(long, default_value_t = RPC_DEFAULT_MAX_RESPONSE_SIZE_MB)] - pub rpc_max_response_size: u32, - - /// Set the maximum concurrent subscriptions per connection. - #[arg(long, default_value_t = RPC_DEFAULT_MAX_SUBS_PER_CONN)] - pub rpc_max_subscriptions_per_connection: u32, - - /// Specify JSON-RPC server TCP port. - #[arg(long, value_name = "PORT")] - pub rpc_port: Option, - - /// EXPERIMENTAL: Specify the JSON-RPC server interface and this option which can be enabled - /// several times if you want expose several RPC interfaces with different configurations. - /// - /// The format for this option is: - /// `--experimental-rpc-endpoint" listen-addr=,,..."` where each option is - /// separated by a comma and `listen-addr` is the only required param. - /// - /// The following options are available: - /// โ€ข listen-addr: The socket address (ip:port) to listen on. Be careful to not expose the - /// server to the public internet unless you know what you're doing. (required) - /// โ€ข disable-batch-requests: Disable batch requests (optional) - /// โ€ข max-connections: The maximum number of concurrent connections that the server will - /// accept (optional) - /// โ€ข max-request-size: The maximum size of a request body in megabytes (optional) - /// โ€ข max-response-size: The maximum size of a response body in megabytes (optional) - /// โ€ข max-subscriptions-per-connection: The maximum number of subscriptions per connection - /// (optional) - /// โ€ข max-buffer-capacity-per-connection: The maximum buffer capacity per connection - /// (optional) - /// โ€ข max-batch-request-len: The maximum number of requests in a batch (optional) - /// โ€ข cors: The CORS allowed origins, this can enabled more than once (optional) - /// โ€ข methods: Which RPC methods to allow, valid values are "safe", "unsafe" and "auto" - /// (optional) - /// โ€ข optional: If the listen address is optional i.e the interface is not required to be - /// available For example this may be useful if some platforms doesn't support ipv6 - /// (optional) - /// โ€ข rate-limit: The rate limit in calls per minute for each connection (optional) - /// โ€ข rate-limit-trust-proxy-headers: Trust proxy headers for disable rate limiting (optional) - /// โ€ข rate-limit-whitelisted-ips: Disable rate limiting for certain ip addresses, this can be - /// enabled more than once (optional) โ€ข retry-random-port: If the port is already in use, - /// retry with a random port (optional) - /// - /// Use with care, this flag is unstable and subject to change. - #[arg( - long, - num_args = 1.., - verbatim_doc_comment, - conflicts_with_all = &["rpc_external", "unsafe_rpc_external", "rpc_port", "rpc_cors", "rpc_rate_limit_trust_proxy_headers", "rpc_rate_limit", "rpc_rate_limit_whitelisted_ips", "rpc_message_buffer_capacity_per_connection", "rpc_disable_batch_requests", "rpc_max_subscriptions_per_connection", "rpc_max_request_size", "rpc_max_response_size"] - )] - pub experimental_rpc_endpoint: Vec, - - /// Maximum number of RPC server connections. - #[arg(long, value_name = "COUNT", default_value_t = RPC_DEFAULT_MAX_CONNECTIONS)] - pub rpc_max_connections: u32, - - /// 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` - /// 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. - #[arg(long, value_name = "ORIGINS")] - pub rpc_cors: Option, - /// The human-readable name for this node. /// /// It's used as network node name. #[arg(long, value_name = "NAME")] pub name: Option, + #[allow(missing_docs)] + #[clap(flatten)] + pub rpc_params: RpcParams, + #[allow(missing_docs)] #[clap(flatten)] pub telemetry_params: TelemetryParams, @@ -345,7 +201,17 @@ impl CliConfiguration for RunCmd { } fn network_params(&self) -> Option<&NetworkParams> { - Some(&self.network_params) + let network_params = &self.network_params; + let is_authority = self.role(self.is_dev().ok()?).ok()?.is_authority(); + if is_authority && network_params.public_addr.is_empty() { + eprintln!( + "WARNING: No public address specified, validator node may not be reachable. + Consider setting `--public-addr` to the public IP address of this node. + This will become a hard requirement in future versions." + ); + } + + Some(network_params) } fn keystore_params(&self) -> Option<&KeystoreParams> { @@ -427,136 +293,51 @@ impl CliConfiguration for RunCmd { } fn rpc_max_connections(&self) -> Result { - Ok(self.rpc_max_connections) + Ok(self.rpc_params.rpc_max_connections) } fn rpc_cors(&self, is_dev: bool) -> Result>> { - Ok(self - .rpc_cors - .clone() - .unwrap_or_else(|| { - if is_dev { - log::warn!("Running in --dev mode, RPC CORS has been disabled."); - Cors::All - } else { - Cors::List(vec![ - "http://localhost:*".into(), - "http://127.0.0.1:*".into(), - "https://localhost:*".into(), - "https://127.0.0.1:*".into(), - "https://polkadot.js.org".into(), - ]) - } - }) - .into()) + self.rpc_params.rpc_cors(is_dev) } fn rpc_addr(&self, default_listen_port: u16) -> Result>> { - if !self.experimental_rpc_endpoint.is_empty() { - for endpoint in &self.experimental_rpc_endpoint { - // Technically, `0.0.0.0` isn't a public IP address, but it's a way to listen on - // all interfaces. Thus, we consider it as a public endpoint and warn about it. - if endpoint.rpc_methods == RpcMethods::Unsafe && endpoint.is_global() || - endpoint.listen_addr.ip().is_unspecified() - { - log::warn!( - "It isn't safe to expose RPC publicly without a proxy server that filters \ - available set of RPC methods." - ); - } - } - - return Ok(Some(self.experimental_rpc_endpoint.clone())); - } - - let (ipv4, ipv6) = rpc_interface( - self.rpc_external, - self.unsafe_rpc_external, - self.rpc_methods, - self.validator, - )?; - - let cors = self.rpc_cors(self.is_dev()?)?; - let port = self.rpc_port.unwrap_or(default_listen_port); - - Ok(Some(vec![ - RpcEndpoint { - batch_config: self.rpc_batch_config()?, - max_connections: self.rpc_max_connections, - listen_addr: SocketAddr::new(std::net::IpAddr::V4(ipv4), port), - rpc_methods: self.rpc_methods, - rate_limit: self.rpc_rate_limit, - rate_limit_trust_proxy_headers: self.rpc_rate_limit_trust_proxy_headers, - rate_limit_whitelisted_ips: self.rpc_rate_limit_whitelisted_ips.clone(), - max_payload_in_mb: self.rpc_max_request_size, - max_payload_out_mb: self.rpc_max_response_size, - max_subscriptions_per_connection: self.rpc_max_subscriptions_per_connection, - max_buffer_capacity_per_connection: self.rpc_message_buffer_capacity_per_connection, - cors: cors.clone(), - retry_random_port: true, - is_optional: false, - }, - RpcEndpoint { - batch_config: self.rpc_batch_config()?, - max_connections: self.rpc_max_connections, - listen_addr: SocketAddr::new(std::net::IpAddr::V6(ipv6), port), - rpc_methods: self.rpc_methods, - rate_limit: self.rpc_rate_limit, - rate_limit_trust_proxy_headers: self.rpc_rate_limit_trust_proxy_headers, - rate_limit_whitelisted_ips: self.rpc_rate_limit_whitelisted_ips.clone(), - max_payload_in_mb: self.rpc_max_request_size, - max_payload_out_mb: self.rpc_max_response_size, - max_subscriptions_per_connection: self.rpc_max_subscriptions_per_connection, - max_buffer_capacity_per_connection: self.rpc_message_buffer_capacity_per_connection, - cors: cors.clone(), - retry_random_port: true, - is_optional: true, - }, - ])) + self.rpc_params.rpc_addr(self.is_dev()?, self.validator, default_listen_port) } fn rpc_methods(&self) -> Result { - Ok(self.rpc_methods.into()) + Ok(self.rpc_params.rpc_methods.into()) } fn rpc_max_request_size(&self) -> Result { - Ok(self.rpc_max_request_size) + Ok(self.rpc_params.rpc_max_request_size) } fn rpc_max_response_size(&self) -> Result { - Ok(self.rpc_max_response_size) + Ok(self.rpc_params.rpc_max_response_size) } fn rpc_max_subscriptions_per_connection(&self) -> Result { - Ok(self.rpc_max_subscriptions_per_connection) + Ok(self.rpc_params.rpc_max_subscriptions_per_connection) } fn rpc_buffer_capacity_per_connection(&self) -> Result { - Ok(self.rpc_message_buffer_capacity_per_connection) + Ok(self.rpc_params.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) + self.rpc_params.rpc_batch_config() } fn rpc_rate_limit(&self) -> Result> { - Ok(self.rpc_rate_limit) + Ok(self.rpc_params.rpc_rate_limit) } fn rpc_rate_limit_whitelisted_ips(&self) -> Result> { - Ok(self.rpc_rate_limit_whitelisted_ips.clone()) + Ok(self.rpc_params.rpc_rate_limit_whitelisted_ips.clone()) } fn rpc_rate_limit_trust_proxy_headers(&self) -> Result { - Ok(self.rpc_rate_limit_trust_proxy_headers) + Ok(self.rpc_params.rpc_rate_limit_trust_proxy_headers) } fn transaction_pool(&self, is_dev: bool) -> Result { @@ -590,57 +371,28 @@ pub fn is_node_name_valid(_name: &str) -> std::result::Result<(), &str> { let name = _name.to_string(); if name.is_empty() { - return Err("Node name cannot be empty") + return Err("Node name cannot be empty"); } if name.chars().count() >= crate::NODE_NAME_MAX_LENGTH { - return Err("Node name too long") + return Err("Node name too long"); } let invalid_chars = r"[\\.@]"; let re = Regex::new(invalid_chars).unwrap(); if re.is_match(&name) { - return Err("Node name should not contain invalid chars such as '.' and '@'") + return Err("Node name should not contain invalid chars such as '.' and '@'"); } let invalid_patterns = r"^https?:"; let re = Regex::new(invalid_patterns).unwrap(); if re.is_match(&name) { - return Err("Node name should not contain urls") + return Err("Node name should not contain urls"); } Ok(()) } -fn rpc_interface( - is_external: bool, - is_unsafe_external: bool, - rpc_methods: RpcMethods, - is_validator: bool, -) -> Result<(Ipv4Addr, Ipv6Addr)> { - if is_external && is_validator && rpc_methods != RpcMethods::Unsafe { - return Err(Error::Input( - "--rpc-external option shouldn't be used if the node is running as \ - a validator. Use `--unsafe-rpc-external` or `--rpc-methods=unsafe` if you understand \ - the risks. See the options description for more information." - .to_owned(), - )) - } - - if is_external || is_unsafe_external { - if rpc_methods == RpcMethods::Unsafe { - log::warn!( - "It isn't safe to expose RPC publicly without a proxy server that filters \ - available set of RPC methods." - ); - } - - Ok((Ipv4Addr::UNSPECIFIED, Ipv6Addr::UNSPECIFIED)) - } else { - Ok((Ipv4Addr::LOCALHOST, Ipv6Addr::LOCALHOST)) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/substrate/client/cli/src/commands/vanity.rs b/substrate/client/cli/src/commands/vanity.rs index 330a59493efc9536cb1d78f2d97b40a11c94d4b0..9acacb4b15b20bc08799b7c16ca72635d3cd300a 100644 --- a/substrate/client/cli/src/commands/vanity.rs +++ b/substrate/client/cli/src/commands/vanity.rs @@ -166,8 +166,6 @@ mod tests { crypto::{default_ss58_version, Ss58AddressFormatRegistry, Ss58Codec}, sr25519, Pair, }; - #[cfg(feature = "bench")] - use test::Bencher; #[test] fn vanity() { @@ -225,16 +223,4 @@ mod tests { 0 ); } - - #[cfg(feature = "bench")] - #[bench] - fn bench_paranoiac(b: &mut Bencher) { - b.iter(|| generate_key("polk")); - } - - #[cfg(feature = "bench")] - #[bench] - fn bench_not_paranoiac(b: &mut Bencher) { - b.iter(|| generate_key("polk")); - } } diff --git a/substrate/client/cli/src/config.rs b/substrate/client/cli/src/config.rs index 7c235847761132822952d9942fb9b382d2a99b83..59238b3307cf25bced2bfa3c5b99391214954bca 100644 --- a/substrate/client/cli/src/config.rs +++ b/substrate/client/cli/src/config.rs @@ -27,10 +27,10 @@ use log::warn; use names::{Generator, Name}; use sc_service::{ config::{ - BasePath, Configuration, DatabaseSource, IpNetwork, KeystoreConfig, NetworkConfiguration, - NodeKeyConfig, OffchainWorkerConfig, PrometheusConfig, PruningMode, Role, - RpcBatchRequestConfig, RpcMethods, TelemetryEndpoints, TransactionPoolOptions, - WasmExecutionMethod, + BasePath, Configuration, DatabaseSource, ExecutorConfiguration, IpNetwork, KeystoreConfig, + NetworkConfiguration, NodeKeyConfig, OffchainWorkerConfig, PrometheusConfig, PruningMode, + Role, RpcBatchRequestConfig, RpcConfiguration, RpcMethods, TelemetryEndpoints, + TransactionPoolOptions, WasmExecutionMethod, }, BlocksPruning, ChainSpec, TracingReceiver, }; @@ -530,26 +530,32 @@ pub trait CliConfiguration: Sized { trie_cache_maximum_size: self.trie_cache_maximum_size()?, state_pruning: self.state_pruning()?, blocks_pruning: self.blocks_pruning()?, - wasm_method: self.wasm_method()?, + executor: ExecutorConfiguration { + wasm_method: self.wasm_method()?, + default_heap_pages: self.default_heap_pages()?, + max_runtime_instances, + runtime_cache_size, + }, wasm_runtime_overrides: self.wasm_runtime_overrides(), - rpc_addr: rpc_addrs, - rpc_methods: self.rpc_methods()?, - rpc_max_connections: self.rpc_max_connections()?, - rpc_cors: self.rpc_cors(is_dev)?, - rpc_max_request_size: self.rpc_max_request_size()?, - rpc_max_response_size: self.rpc_max_response_size()?, - rpc_id_provider: None, - rpc_max_subs_per_conn: self.rpc_max_subscriptions_per_connection()?, - rpc_port: DCV::rpc_listen_port(), - rpc_message_buffer_capacity: self.rpc_buffer_capacity_per_connection()?, - rpc_batch_config: self.rpc_batch_config()?, - rpc_rate_limit: self.rpc_rate_limit()?, - rpc_rate_limit_whitelisted_ips: self.rpc_rate_limit_whitelisted_ips()?, - rpc_rate_limit_trust_proxy_headers: self.rpc_rate_limit_trust_proxy_headers()?, + rpc: RpcConfiguration { + addr: rpc_addrs, + methods: self.rpc_methods()?, + max_connections: self.rpc_max_connections()?, + cors: self.rpc_cors(is_dev)?, + max_request_size: self.rpc_max_request_size()?, + max_response_size: self.rpc_max_response_size()?, + id_provider: None, + max_subs_per_conn: self.rpc_max_subscriptions_per_connection()?, + port: DCV::rpc_listen_port(), + message_buffer_capacity: self.rpc_buffer_capacity_per_connection()?, + batch_config: self.rpc_batch_config()?, + rate_limit: self.rpc_rate_limit()?, + rate_limit_whitelisted_ips: self.rpc_rate_limit_whitelisted_ips()?, + rate_limit_trust_proxy_headers: self.rpc_rate_limit_trust_proxy_headers()?, + }, prometheus_config: self .prometheus_config(DCV::prometheus_listen_port(), &chain_spec)?, telemetry_endpoints, - default_heap_pages: self.default_heap_pages()?, offchain_worker: self.offchain_worker(&role)?, force_authoring: self.force_authoring()?, disable_grandpa: self.disable_grandpa()?, @@ -557,11 +563,9 @@ pub trait CliConfiguration: Sized { tracing_targets: self.tracing_targets()?, tracing_receiver: self.tracing_receiver()?, chain_spec, - max_runtime_instances, announce_block: self.announce_block()?, role, base_path, - runtime_cache_size, }) } @@ -618,15 +622,9 @@ pub trait CliConfiguration: Sized { /// } /// } /// ``` - fn init( - &self, - support_url: &String, - impl_version: &String, - logger_hook: F, - config: &Configuration, - ) -> Result<()> + fn init(&self, support_url: &String, impl_version: &String, logger_hook: F) -> Result<()> where - F: FnOnce(&mut LoggerBuilder, &Configuration), + F: FnOnce(&mut LoggerBuilder), { sp_panic_handler::set(support_url, impl_version); @@ -645,18 +643,10 @@ pub trait CliConfiguration: Sized { } // Call hook for custom profiling setup. - logger_hook(&mut logger, config); + logger_hook(&mut logger); logger.init()?; - if config.role.is_authority() && config.network.public_addresses.is_empty() { - warn!( - "WARNING: No public address specified, validator node may not be reachable. - Consider setting `--public-addr` to the public IP address of this node. - This will become a hard requirement in future versions." - ); - } - match fdlimit::raise_fd_limit() { Ok(fdlimit::Outcome::LimitRaised { to, .. }) => if to < RECOMMENDED_OPEN_FILE_DESCRIPTOR_LIMIT { diff --git a/substrate/client/cli/src/lib.rs b/substrate/client/cli/src/lib.rs index 1bb9fec0e27690f9b7ec21918f3dce329100de3a..4db70f99c803bf2271a3384de64e347f1ce3a2ab 100644 --- a/substrate/client/cli/src/lib.rs +++ b/substrate/client/cli/src/lib.rs @@ -25,6 +25,7 @@ #![warn(unused_imports)] use clap::{CommandFactory, FromArgMatches, Parser}; +use log::warn; use sc_service::Configuration; pub mod arg_enums; @@ -242,7 +243,10 @@ pub trait SubstrateCli: Sized { let config = command.create_configuration(self, tokio_runtime.handle().clone())?; - command.init(&Self::support_url(), &Self::impl_version(), logger_hook, &config)?; + command.init(&Self::support_url(), &Self::impl_version(), |logger_builder| { + logger_hook(logger_builder, &config) + })?; + Runner::new(config, tokio_runtime, signals) } } diff --git a/substrate/client/cli/src/params/node_key_params.rs b/substrate/client/cli/src/params/node_key_params.rs index 0e12c7a2a2d3742ef3d9aa3681f900036e67a467..70671bff8c05cd6325b4dc2e90da19fc93bd6f0e 100644 --- a/substrate/client/cli/src/params/node_key_params.rs +++ b/substrate/client/cli/src/params/node_key_params.rs @@ -116,8 +116,8 @@ impl NodeKeyParams { .clone() .unwrap_or_else(|| net_config_dir.join(NODE_KEY_ED25519_FILE)); if !self.unsafe_force_node_key_generation && - role.is_authority() && !is_dev && - !key_path.exists() + role.is_authority() && + !is_dev && !key_path.exists() { return Err(Error::NetworkKeyNotFound(key_path)) } @@ -237,7 +237,6 @@ mod tests { |params| { let dir = PathBuf::from(net_config_dir.clone()); let typ = params.node_key_type; - let role = role.clone(); params.node_key(net_config_dir, role, is_dev).and_then(move |c| match c { NodeKeyConfig::Ed25519(sc_network::config::Secret::File(ref f)) if typ == NodeKeyType::Ed25519 && diff --git a/substrate/client/cli/src/params/rpc_params.rs b/substrate/client/cli/src/params/rpc_params.rs index d0ec1fd004431cb207c87219039dc6bf934ab168..f9937b59bbaf4fb00ccd5a3e09ad1cbc67801b13 100644 --- a/substrate/client/cli/src/params/rpc_params.rs +++ b/substrate/client/cli/src/params/rpc_params.rs @@ -17,12 +17,16 @@ // along with this program. If not, see . use crate::{ - arg_enums::RpcMethods, + arg_enums::{Cors, RpcMethods}, params::{IpNetwork, RpcBatchRequestConfig}, RPC_DEFAULT_MAX_CONNECTIONS, RPC_DEFAULT_MAX_REQUEST_SIZE_MB, RPC_DEFAULT_MAX_RESPONSE_SIZE_MB, RPC_DEFAULT_MAX_SUBS_PER_CONN, RPC_DEFAULT_MESSAGE_CAPACITY_PER_CONN, }; -use std::{net::SocketAddr, num::NonZeroU32}; +use clap::Args; +use std::{ + net::{Ipv4Addr, Ipv6Addr, SocketAddr}, + num::NonZeroU32, +}; const RPC_LISTEN_ADDR: &str = "listen-addr"; const RPC_CORS: &str = "cors"; @@ -40,6 +44,288 @@ const RPC_OPTIONAL: &str = "optional"; const RPC_DISABLE_BATCH: &str = "disable-batch-requests"; const RPC_BATCH_LIMIT: &str = "max-batch-request-len"; +/// Parameters of RPC. +#[derive(Debug, Clone, Args)] +pub struct RpcParams { + /// Listen to all RPC interfaces (default: local). + /// + /// 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)] + pub rpc_external: bool, + + /// Listen to all RPC interfaces. + /// + /// Same as `--rpc-external`. + #[arg(long)] + pub unsafe_rpc_external: bool, + + /// RPC methods to expose. + #[arg( + long, + value_name = "METHOD SET", + value_enum, + ignore_case = true, + default_value_t = RpcMethods::Auto, + verbatim_doc_comment + )] + 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, + + /// Disable RPC rate limiting for certain ip addresses. + /// + /// Each IP address must be in CIDR notation such as `1.2.3.4/24`. + #[arg(long, num_args = 1..)] + pub rpc_rate_limit_whitelisted_ips: Vec, + + /// Trust proxy headers for disable rate limiting. + /// + /// By default the rpc server will not trust headers such `X-Real-IP`, `X-Forwarded-For` and + /// `Forwarded` and this option will make the rpc server to trust these headers. + /// + /// For instance this may be secure if the rpc server is behind a reverse proxy and that the + /// proxy always sets these headers. + #[arg(long)] + pub rpc_rate_limit_trust_proxy_headers: bool, + + /// 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, + + /// Set the maximum RPC response payload size for both HTTP and WS in megabytes. + #[arg(long, default_value_t = RPC_DEFAULT_MAX_RESPONSE_SIZE_MB)] + pub rpc_max_response_size: u32, + + /// Set the maximum concurrent subscriptions per connection. + #[arg(long, default_value_t = RPC_DEFAULT_MAX_SUBS_PER_CONN)] + pub rpc_max_subscriptions_per_connection: u32, + + /// Specify JSON-RPC server TCP port. + #[arg(long, value_name = "PORT")] + pub rpc_port: Option, + + /// EXPERIMENTAL: Specify the JSON-RPC server interface and this option which can be enabled + /// several times if you want expose several RPC interfaces with different configurations. + /// + /// The format for this option is: + /// `--experimental-rpc-endpoint" listen-addr=,,..."` where each option is + /// separated by a comma and `listen-addr` is the only required param. + /// + /// The following options are available: + /// โ€ข listen-addr: The socket address (ip:port) to listen on. Be careful to not expose the + /// server to the public internet unless you know what you're doing. (required) + /// โ€ข disable-batch-requests: Disable batch requests (optional) + /// โ€ข max-connections: The maximum number of concurrent connections that the server will + /// accept (optional) + /// โ€ข max-request-size: The maximum size of a request body in megabytes (optional) + /// โ€ข max-response-size: The maximum size of a response body in megabytes (optional) + /// โ€ข max-subscriptions-per-connection: The maximum number of subscriptions per connection + /// (optional) + /// โ€ข max-buffer-capacity-per-connection: The maximum buffer capacity per connection + /// (optional) + /// โ€ข max-batch-request-len: The maximum number of requests in a batch (optional) + /// โ€ข cors: The CORS allowed origins, this can enabled more than once (optional) + /// โ€ข methods: Which RPC methods to allow, valid values are "safe", "unsafe" and "auto" + /// (optional) + /// โ€ข optional: If the listen address is optional i.e the interface is not required to be + /// available For example this may be useful if some platforms doesn't support ipv6 + /// (optional) + /// โ€ข rate-limit: The rate limit in calls per minute for each connection (optional) + /// โ€ข rate-limit-trust-proxy-headers: Trust proxy headers for disable rate limiting (optional) + /// โ€ข rate-limit-whitelisted-ips: Disable rate limiting for certain ip addresses, this can be + /// enabled more than once (optional) โ€ข retry-random-port: If the port is already in use, + /// retry with a random port (optional) + /// + /// Use with care, this flag is unstable and subject to change. + #[arg( + long, + num_args = 1.., + verbatim_doc_comment, + conflicts_with_all = &["rpc_external", "unsafe_rpc_external", "rpc_port", "rpc_cors", "rpc_rate_limit_trust_proxy_headers", "rpc_rate_limit", "rpc_rate_limit_whitelisted_ips", "rpc_message_buffer_capacity_per_connection", "rpc_disable_batch_requests", "rpc_max_subscriptions_per_connection", "rpc_max_request_size", "rpc_max_response_size"] + )] + pub experimental_rpc_endpoint: Vec, + + /// Maximum number of RPC server connections. + #[arg(long, value_name = "COUNT", default_value_t = RPC_DEFAULT_MAX_CONNECTIONS)] + pub rpc_max_connections: u32, + + /// 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` + /// 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. + #[arg(long, value_name = "ORIGINS")] + pub rpc_cors: Option, +} + +impl RpcParams { + /// Returns the RPC CORS configuration. + pub fn rpc_cors(&self, is_dev: bool) -> crate::Result>> { + Ok(self + .rpc_cors + .clone() + .unwrap_or_else(|| { + if is_dev { + log::warn!("Running in --dev mode, RPC CORS has been disabled."); + Cors::All + } else { + Cors::List(vec![ + "http://localhost:*".into(), + "http://127.0.0.1:*".into(), + "https://localhost:*".into(), + "https://127.0.0.1:*".into(), + "https://polkadot.js.org".into(), + ]) + } + }) + .into()) + } + + /// Returns the RPC endpoints. + pub fn rpc_addr( + &self, + is_dev: bool, + is_validator: bool, + default_listen_port: u16, + ) -> crate::Result>> { + if !self.experimental_rpc_endpoint.is_empty() { + for endpoint in &self.experimental_rpc_endpoint { + // Technically, `0.0.0.0` isn't a public IP address, but it's a way to listen on + // all interfaces. Thus, we consider it as a public endpoint and warn about it. + if endpoint.rpc_methods == RpcMethods::Unsafe && endpoint.is_global() || + endpoint.listen_addr.ip().is_unspecified() + { + eprintln!( + "It isn't safe to expose RPC publicly without a proxy server that filters \ + available set of RPC methods." + ); + } + } + + return Ok(Some(self.experimental_rpc_endpoint.clone())); + } + + let (ipv4, ipv6) = rpc_interface( + self.rpc_external, + self.unsafe_rpc_external, + self.rpc_methods, + is_validator, + )?; + + let cors = self.rpc_cors(is_dev)?; + let port = self.rpc_port.unwrap_or(default_listen_port); + + Ok(Some(vec![ + RpcEndpoint { + batch_config: self.rpc_batch_config()?, + max_connections: self.rpc_max_connections, + listen_addr: SocketAddr::new(std::net::IpAddr::V4(ipv4), port), + rpc_methods: self.rpc_methods, + rate_limit: self.rpc_rate_limit, + rate_limit_trust_proxy_headers: self.rpc_rate_limit_trust_proxy_headers, + rate_limit_whitelisted_ips: self.rpc_rate_limit_whitelisted_ips.clone(), + max_payload_in_mb: self.rpc_max_request_size, + max_payload_out_mb: self.rpc_max_response_size, + max_subscriptions_per_connection: self.rpc_max_subscriptions_per_connection, + max_buffer_capacity_per_connection: self.rpc_message_buffer_capacity_per_connection, + cors: cors.clone(), + retry_random_port: true, + is_optional: false, + }, + RpcEndpoint { + batch_config: self.rpc_batch_config()?, + max_connections: self.rpc_max_connections, + listen_addr: SocketAddr::new(std::net::IpAddr::V6(ipv6), port), + rpc_methods: self.rpc_methods, + rate_limit: self.rpc_rate_limit, + rate_limit_trust_proxy_headers: self.rpc_rate_limit_trust_proxy_headers, + rate_limit_whitelisted_ips: self.rpc_rate_limit_whitelisted_ips.clone(), + max_payload_in_mb: self.rpc_max_request_size, + max_payload_out_mb: self.rpc_max_response_size, + max_subscriptions_per_connection: self.rpc_max_subscriptions_per_connection, + max_buffer_capacity_per_connection: self.rpc_message_buffer_capacity_per_connection, + cors: cors.clone(), + retry_random_port: true, + is_optional: true, + }, + ])) + } + + /// Returns the configuration for batch RPC requests. + pub fn rpc_batch_config(&self) -> crate::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_interface( + is_external: bool, + is_unsafe_external: bool, + rpc_methods: RpcMethods, + is_validator: bool, +) -> crate::Result<(Ipv4Addr, Ipv6Addr)> { + if is_external && is_validator && rpc_methods != RpcMethods::Unsafe { + return Err(crate::Error::Input( + "--rpc-external option shouldn't be used if the node is running as \ + a validator. Use `--unsafe-rpc-external` or `--rpc-methods=unsafe` if you understand \ + the risks. See the options description for more information." + .to_owned(), + )); + } + + if is_external || is_unsafe_external { + if rpc_methods == RpcMethods::Unsafe { + eprintln!( + "It isn't safe to expose RPC publicly without a proxy server that filters \ + available set of RPC methods." + ); + } + + Ok((Ipv4Addr::UNSPECIFIED, Ipv6Addr::UNSPECIFIED)) + } else { + Ok((Ipv4Addr::LOCALHOST, Ipv6Addr::LOCALHOST)) + } +} + /// Represent a single RPC endpoint with its configuration. #[derive(Debug, Clone)] pub struct RpcEndpoint { diff --git a/substrate/client/cli/src/runner.rs b/substrate/client/cli/src/runner.rs index b0dbccfa634c881afe7b90b5e3a98a2fdbab6904..9c5834d8d80ae5ec2b929691d29c227036642f60 100644 --- a/substrate/client/cli/src/runner.rs +++ b/substrate/client/cli/src/runner.rs @@ -193,7 +193,10 @@ pub fn print_node_infos(config: &Configuration) { mod tests { use super::*; use sc_network::config::NetworkConfiguration; - use sc_service::{Arc, ChainType, GenericChainSpec, NoExtension}; + use sc_service::{ + config::{ExecutorConfiguration, RpcConfiguration}, + Arc, ChainType, GenericChainSpec, NoExtension, + }; use std::{ path::PathBuf, sync::atomic::{AtomicU64, Ordering}, @@ -262,36 +265,35 @@ mod tests { .with_genesis_config_patch(Default::default()) .build(), ), - wasm_method: Default::default(), + executor: ExecutorConfiguration::default(), wasm_runtime_overrides: None, - rpc_addr: None, - rpc_max_connections: Default::default(), - rpc_cors: None, - rpc_methods: Default::default(), - rpc_max_request_size: Default::default(), - rpc_max_response_size: Default::default(), - rpc_id_provider: Default::default(), - rpc_max_subs_per_conn: Default::default(), - rpc_message_buffer_capacity: Default::default(), - rpc_port: 9944, - rpc_batch_config: sc_service::config::RpcBatchRequestConfig::Unlimited, - rpc_rate_limit: None, - rpc_rate_limit_whitelisted_ips: Default::default(), - rpc_rate_limit_trust_proxy_headers: Default::default(), + rpc: RpcConfiguration { + addr: None, + max_connections: Default::default(), + cors: None, + methods: Default::default(), + max_request_size: Default::default(), + max_response_size: Default::default(), + id_provider: Default::default(), + max_subs_per_conn: Default::default(), + message_buffer_capacity: Default::default(), + port: 9944, + batch_config: sc_service::config::RpcBatchRequestConfig::Unlimited, + rate_limit: None, + rate_limit_whitelisted_ips: Default::default(), + rate_limit_trust_proxy_headers: Default::default(), + }, prometheus_config: None, telemetry_endpoints: None, - default_heap_pages: None, offchain_worker: Default::default(), force_authoring: false, disable_grandpa: false, dev_key_seed: None, tracing_targets: None, tracing_receiver: Default::default(), - max_runtime_instances: 8, announce_block: true, base_path: sc_service::BasePath::new(root.clone()), data_path: root, - runtime_cache_size: 2, }, runtime, Signals::dummy(), diff --git a/substrate/client/consensus/babe/src/authorship.rs b/substrate/client/consensus/babe/src/authorship.rs index 57ee706a04f6bfc80ff29b6e0bbf91a4874991b1..aa54da2a4434ea5cd5bd607e0536f2492f7ad10f 100644 --- a/substrate/client/consensus/babe/src/authorship.rs +++ b/substrate/client/consensus/babe/src/authorship.rs @@ -108,7 +108,8 @@ pub(super) fn secondary_slot_author( return None } - let rand = U256::from((randomness, slot).using_encoded(sp_crypto_hashing::blake2_256)); + let rand = + U256::from_big_endian(&(randomness, slot).using_encoded(sp_crypto_hashing::blake2_256)); let authorities_len = U256::from(authorities.len()); let idx = rand % authorities_len; @@ -271,7 +272,9 @@ fn claim_primary_slot( #[cfg(test)] mod tests { use super::*; - use sp_consensus_babe::{AllowedSlots, AuthorityId, BabeEpochConfiguration, Epoch}; + use sp_consensus_babe::{ + AllowedSlots, AuthorityId, BabeEpochConfiguration, Epoch, RANDOMNESS_LENGTH, + }; use sp_core::{crypto::Pair as _, sr25519::Pair}; use sp_keystore::testing::MemoryKeystore; @@ -305,4 +308,18 @@ mod tests { epoch.authorities.push((valid_public_key.into(), 10)); assert_eq!(claim_slot(10.into(), &epoch, &keystore).unwrap().1, valid_public_key.into()); } + + #[test] + fn secondary_slot_author_selection_works() { + let authorities = (0..1000) + .map(|i| (AuthorityId::from(Pair::generate().0.public()), i)) + .collect::>(); + + let randomness = [3; RANDOMNESS_LENGTH]; + + assert_eq!( + *secondary_slot_author(100.into(), &authorities, randomness).unwrap(), + authorities[167].0 + ); + } } diff --git a/substrate/client/consensus/babe/src/lib.rs b/substrate/client/consensus/babe/src/lib.rs index 9770b16871e1193230d057acb571d2c0acca108e..4cf66302ec858618f6860f8fa7b9cf992b902c5f 100644 --- a/substrate/client/consensus/babe/src/lib.rs +++ b/substrate/client/consensus/babe/src/lib.rs @@ -1146,7 +1146,9 @@ where let info = self.client.info(); let number = *block.header.number(); - if info.block_gap.map_or(false, |(s, e)| s <= number && number <= e) || block.with_state() { + if info.block_gap.map_or(false, |gap| gap.start <= number && number <= gap.end) || + block.with_state() + { // Verification for imported blocks is skipped in two cases: // 1. When importing blocks below the last finalized block during network initial // synchronization. @@ -1420,7 +1422,7 @@ where // Skip babe logic if block already in chain or importing blocks during initial sync, // otherwise the check for epoch changes will error because trying to re-import an // epoch change or because of missing epoch data in the tree, respectively. - if info.block_gap.map_or(false, |(s, e)| s <= number && number <= e) || + if info.block_gap.map_or(false, |gap| gap.start <= number && number <= gap.end) || block_status == BlockStatus::InChain { // When re-importing existing block strip away intermediates. diff --git a/substrate/client/consensus/beefy/src/communication/request_response/outgoing_requests_engine.rs b/substrate/client/consensus/beefy/src/communication/request_response/outgoing_requests_engine.rs index 95ecf35557a5189fae4cc117e71126d8c8076536..5408d95acf2d41a4e98d2d4d6f0c422b23b0ae86 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 @@ -38,7 +38,7 @@ use crate::{ request_response::{Error, JustificationRequest, BEEFY_SYNC_LOG_TARGET}, }, justification::{decode_and_verify_finality_proof, BeefyVersionedFinalityProof}, - metric_inc, + metric_inc, metric_set, metrics::{register_metrics, OnDemandOutgoingRequestsMetrics}, KnownPeers, }; @@ -242,6 +242,8 @@ impl OnDemandJustificationsEngine { diff --git a/substrate/client/consensus/beefy/src/fisherman.rs b/substrate/client/consensus/beefy/src/fisherman.rs index faa4d34eff5ac74b5024f720b975ff72bb5e9b11..2b2683b35f0a850abcc93c364fa0338c45cc65d9 100644 --- a/substrate/client/consensus/beefy/src/fisherman.rs +++ b/substrate/client/consensus/beefy/src/fisherman.rs @@ -32,9 +32,8 @@ use sp_runtime::{ }; use std::{marker::PhantomData, sync::Arc}; -/// Helper struct containing the id and the key ownership proof for a validator. -pub struct ProvedValidator<'a, AuthorityId: AuthorityIdBound> { - pub id: &'a AuthorityId, +/// Helper struct containing the key ownership proof for a validator. +pub struct ProvedValidator { pub key_owner_proof: OpaqueKeyOwnershipProof, } @@ -66,7 +65,7 @@ where at: BlockId, offender_ids: impl Iterator, validator_set_id: ValidatorSetId, - ) -> Result>, Error> { + ) -> Result, Error> { let hash = match at { BlockId::Hash(hash) => hash, BlockId::Number(number) => self @@ -91,7 +90,7 @@ where offender_id.clone(), ) { Ok(Some(key_owner_proof)) => { - proved_offenders.push(ProvedValidator { id: offender_id, key_owner_proof }); + proved_offenders.push(ProvedValidator { key_owner_proof }); }, Ok(None) => { debug!( diff --git a/substrate/client/consensus/beefy/src/metrics.rs b/substrate/client/consensus/beefy/src/metrics.rs index 30180fe43ec43b4dcc0ef6fbd4500b4445900c0b..15f2f9f90334accd7e967b02a5711ccd645ebfef 100644 --- a/substrate/client/consensus/beefy/src/metrics.rs +++ b/substrate/client/consensus/beefy/src/metrics.rs @@ -236,6 +236,8 @@ pub struct OnDemandOutgoingRequestsMetrics { pub beefy_on_demand_justification_invalid_proof: Counter, /// Number of on-demand justification good proof pub beefy_on_demand_justification_good_proof: Counter, + /// Number of live beefy peers available for requests. + pub beefy_on_demand_live_peers: Gauge, } impl PrometheusRegister for OnDemandOutgoingRequestsMetrics { @@ -277,6 +279,13 @@ impl PrometheusRegister for OnDemandOutgoingRequestsMetrics { )?, registry, )?, + beefy_on_demand_live_peers: register( + Gauge::new( + "substrate_beefy_on_demand_live_peers", + "Number of live beefy peers available for requests.", + )?, + registry, + )?, }) } } diff --git a/substrate/client/consensus/common/src/block_import.rs b/substrate/client/consensus/common/src/block_import.rs index 4d7b89f37d86c7e178ed3cabb1c6dba997b83103..0fcf96a963682a3677f3c52cde164b840a8b14cc 100644 --- a/substrate/client/consensus/common/src/block_import.rs +++ b/substrate/client/consensus/common/src/block_import.rs @@ -214,6 +214,8 @@ pub struct BlockImportParams { pub fork_choice: Option, /// Re-validate existing block. pub import_existing: bool, + /// Whether to create "block gap" in case this block doesn't have parent. + pub create_gap: bool, /// Cached full header hash (with post-digests applied). pub post_hash: Option, } @@ -234,6 +236,7 @@ impl BlockImportParams { auxiliary: Vec::new(), fork_choice: None, import_existing: false, + create_gap: true, post_hash: None, } } diff --git a/substrate/client/consensus/grandpa/src/aux_schema.rs b/substrate/client/consensus/grandpa/src/aux_schema.rs index 8ec882591be9a682456e55f41fd47becd803a6c4..c42310dcd72cfe60a1f5e1a27709c683cf4839d5 100644 --- a/substrate/client/consensus/grandpa/src/aux_schema.rs +++ b/substrate/client/consensus/grandpa/src/aux_schema.rs @@ -743,7 +743,9 @@ mod test { substrate_test_runtime_client::runtime::Block, _, _, - >(&client, H256::random(), 0, || unreachable!()) + >( + &client, H256::random(), 0, || unreachable!() + ) .unwrap(); assert_eq!( diff --git a/substrate/client/consensus/grandpa/src/voting_rule.rs b/substrate/client/consensus/grandpa/src/voting_rule.rs index c1d3cd2fbd6ab6b5f8d45e555a43aeb289791a80..6072f1895fd0be5a41539228cd6fad88e1c22353 100644 --- a/substrate/client/consensus/grandpa/src/voting_rule.rs +++ b/substrate/client/consensus/grandpa/src/voting_rule.rs @@ -82,7 +82,7 @@ where /// /// In the best case our vote is exactly N blocks /// behind the best block, but if there is a scenario where either -/// >34% of validators run without this rule or the fork-choice rule +/// \>34% of validators run without this rule or the fork-choice rule /// can prioritize shorter chains over longer ones, the vote may be /// closer to the best block than N. #[derive(Clone)] diff --git a/substrate/client/consensus/slots/build.rs b/substrate/client/consensus/slots/build.rs index a68cb706e8fbdc2526aa18730f0e7ddb9719f838..c63f0b8b66744738df18d99a274632cb1ecef980 100644 --- a/substrate/client/consensus/slots/build.rs +++ b/substrate/client/consensus/slots/build.rs @@ -20,6 +20,6 @@ use std::env; fn main() { if let Ok(profile) = env::var("PROFILE") { - println!("cargo:rustc-cfg=build_type=\"{}\"", profile); + println!("cargo:rustc-cfg=build_profile=\"{}\"", profile); } } diff --git a/substrate/client/consensus/slots/src/lib.rs b/substrate/client/consensus/slots/src/lib.rs index 06e0756fc968910cf1333c13303c2fa65479df71..4f7e85541777ac99be4c70107f314079142c2a6c 100644 --- a/substrate/client/consensus/slots/src/lib.rs +++ b/substrate/client/consensus/slots/src/lib.rs @@ -227,7 +227,7 @@ pub trait SimpleSlotWorker { "โŒ›๏ธ Discarding proposal for slot {}; block production took too long", slot, ); // If the node was compiled with debug, tell the user to use release optimizations. - #[cfg(build_type = "debug")] + #[cfg(build_profile = "debug")] info!( target: log_target, "๐Ÿ‘‰ Recompile your node in `--release` mode to mitigate this problem.", diff --git a/substrate/client/db/src/lib.rs b/substrate/client/db/src/lib.rs index ba0cbc09d53dad38939efd4017da27ecd48f213a..72707c306f5823b461822a2de395a806b0870ec5 100644 --- a/substrate/client/db/src/lib.rs +++ b/substrate/client/db/src/lib.rs @@ -61,6 +61,7 @@ use codec::{Decode, Encode}; use hash_db::Prefix; use sc_client_api::{ backend::NewBlockState, + blockchain::{BlockGap, BlockGapType}, leaves::{FinalizationOutcome, LeafSet}, utils::is_descendent_of, IoInfo, MemoryInfo, MemorySize, UsageInfo, @@ -91,6 +92,7 @@ use sp_state_machine::{ StorageValue, UsageInfo as StateUsageInfo, }; use sp_trie::{cache::SharedTrieCache, prefixed_key, MemoryDB, MerkleValue, PrefixedMemoryDB}; +use utils::BLOCK_GAP_CURRENT_VERSION; // Re-export the Database trait so that one can pass an implementation of it. pub use sc_state_db::PruningMode; @@ -522,7 +524,7 @@ impl BlockchainDb { } } - fn update_block_gap(&self, gap: Option<(NumberFor, NumberFor)>) { + fn update_block_gap(&self, gap: Option>>) { let mut meta = self.meta.write(); meta.block_gap = gap; } @@ -538,7 +540,7 @@ impl BlockchainDb { fn insert_justifications_if_pinned(&self, hash: Block::Hash, justification: Justification) { let mut cache = self.pinned_blocks_cache.write(); if !cache.contains(hash) { - return + return; } let justifications = Justifications::from(justification); @@ -551,7 +553,7 @@ impl BlockchainDb { fn insert_persisted_justifications_if_pinned(&self, hash: Block::Hash) -> ClientResult<()> { let mut cache = self.pinned_blocks_cache.write(); if !cache.contains(hash) { - return Ok(()) + return Ok(()); } let justifications = self.justifications_uncached(hash)?; @@ -565,7 +567,7 @@ impl BlockchainDb { fn insert_persisted_body_if_pinned(&self, hash: Block::Hash) -> ClientResult<()> { let mut cache = self.pinned_blocks_cache.write(); if !cache.contains(hash) { - return Ok(()) + return Ok(()); } let body = self.body_uncached(hash)?; @@ -594,8 +596,7 @@ impl BlockchainDb { Ok(justifications) => Ok(Some(justifications)), Err(err) => return Err(sp_blockchain::Error::Backend(format!( - "Error decoding justifications: {}", - err + "Error decoding justifications: {err}" ))), }, None => Ok(None), @@ -610,10 +611,7 @@ impl BlockchainDb { match Decode::decode(&mut &body[..]) { Ok(body) => return Ok(Some(body)), Err(err) => - return Err(sp_blockchain::Error::Backend(format!( - "Error decoding body: {}", - err - ))), + return Err(sp_blockchain::Error::Backend(format!("Error decoding body: {err}"))), } } @@ -636,8 +634,7 @@ impl BlockchainDb { let ex = Block::Extrinsic::decode(&mut input).map_err( |err| { sp_blockchain::Error::Backend(format!( - "Error decoding indexed extrinsic: {}", - err + "Error decoding indexed extrinsic: {err}" )) }, )?; @@ -645,8 +642,7 @@ impl BlockchainDb { }, None => return Err(sp_blockchain::Error::Backend(format!( - "Missing indexed transaction {:?}", - hash + "Missing indexed transaction {hash:?}" ))), }; }, @@ -655,12 +651,11 @@ impl BlockchainDb { }, } } - return Ok(Some(body)) + return Ok(Some(body)); }, Err(err) => return Err(sp_blockchain::Error::Backend(format!( - "Error decoding body list: {}", - err + "Error decoding body list: {err}", ))), } } @@ -672,7 +667,7 @@ impl sc_client_api::blockchain::HeaderBackend for Blockcha fn header(&self, hash: Block::Hash) -> ClientResult> { let mut cache = self.header_cache.lock(); if let Some(result) = cache.get_refresh(&hash) { - return Ok(result.clone()) + return Ok(result.clone()); } let header = utils::read_header( &*self.db, @@ -724,7 +719,7 @@ impl sc_client_api::blockchain::Backend for BlockchainDb ClientResult>> { let cache = self.pinned_blocks_cache.read(); if let Some(result) = cache.body(&hash) { - return Ok(result.clone()) + return Ok(result.clone()); } self.body_uncached(hash) @@ -733,7 +728,7 @@ impl sc_client_api::blockchain::Backend for BlockchainDb ClientResult> { let cache = self.pinned_blocks_cache.read(); if let Some(result) = cache.justifications(&hash) { - return Ok(result.clone()) + return Ok(result.clone()); } self.justifications_uncached(hash) @@ -778,8 +773,7 @@ impl sc_client_api::blockchain::Backend for BlockchainDb transactions.push(t), None => return Err(sp_blockchain::Error::Backend(format!( - "Missing indexed transaction {:?}", - hash + "Missing indexed transaction {hash:?}", ))), } } @@ -787,7 +781,7 @@ impl sc_client_api::blockchain::Backend for BlockchainDb - Err(sp_blockchain::Error::Backend(format!("Error decoding body list: {}", err))), + Err(sp_blockchain::Error::Backend(format!("Error decoding body list: {err}"))), } } } @@ -810,8 +804,7 @@ impl HeaderMetadata for BlockchainDb { }) .ok_or_else(|| { ClientError::UnknownBlock(format!( - "Header was not found in the database: {:?}", - hash + "Header was not found in the database: {hash:?}", )) }) }, @@ -841,6 +834,7 @@ pub struct BlockImportOperation { finalized_blocks: Vec<(Block::Hash, Option)>, set_head: Option, commit_state: bool, + create_gap: bool, index_ops: Vec, } @@ -858,7 +852,7 @@ impl BlockImportOperation { } if count > 0 { - log::debug!(target: "sc_offchain", "Applied {} offchain indexing changes.", count); + log::debug!(target: "sc_offchain", "Applied {count} offchain indexing changes."); } } @@ -877,7 +871,7 @@ impl BlockImportOperation { state_version: StateVersion, ) -> ClientResult { if storage.top.keys().any(|k| well_known_keys::is_child_storage_key(k)) { - return Err(sp_blockchain::Error::InvalidState) + return Err(sp_blockchain::Error::InvalidState); } let child_delta = storage.children_default.values().map(|child_content| { @@ -995,6 +989,10 @@ impl sc_client_api::backend::BlockImportOperation self.index_ops = index_ops; Ok(()) } + + fn set_create_gap(&mut self, create_gap: bool) { + self.create_gap = create_gap; + } } struct StorageDb { @@ -1011,7 +1009,7 @@ impl sp_state_machine::Storage> for StorageDb Backend { if meta.best_number.saturating_sub(best_number).saturated_into::() > self.canonicalization_delay { - return Err(sp_blockchain::Error::SetHeadTooOld) + return Err(sp_blockchain::Error::SetHeadTooOld); } let parent_exists = @@ -1307,7 +1305,7 @@ impl Backend { (&r.number, &r.hash) ); - return Err(sp_blockchain::Error::NotInFinalizedChain) + return Err(sp_blockchain::Error::NotInFinalizedChain); } retracted.push(r.hash); @@ -1349,10 +1347,9 @@ impl Backend { *header.parent_hash() != last_finalized { return Err(sp_blockchain::Error::NonSequentialFinalization(format!( - "Last finalized {:?} not parent of {:?}", - last_finalized, + "Last finalized {last_finalized:?} not parent of {:?}", header.hash() - ))) + ))); } Ok(()) } @@ -1429,10 +1426,10 @@ impl Backend { hash_to_canonicalize, to_canonicalize.saturated_into(), ) { - return Ok(()) + return Ok(()); } - trace!(target: "db", "Canonicalize block #{} ({:?})", to_canonicalize, hash_to_canonicalize); + trace!(target: "db", "Canonicalize block #{to_canonicalize} ({hash_to_canonicalize:?})"); let commit = self.storage.state_db.canonicalize_block(&hash_to_canonicalize).map_err( sp_blockchain::Error::from_state_db::< sc_state_db::Error, @@ -1456,6 +1453,8 @@ impl Backend { (meta.best_number, meta.finalized_hash, meta.finalized_number, meta.block_gap) }; + let mut block_gap_updated = false; + let mut current_transaction_justifications: HashMap = HashMap::new(); let mut finalized_blocks = operation.finalized_blocks.into_iter().peekable(); @@ -1623,13 +1622,8 @@ impl Backend { let is_best = pending_block.leaf_state.is_best(); debug!( target: "db", - "DB Commit {:?} ({}), best={}, state={}, existing={}, finalized={}", - hash, - number, - is_best, + "DB Commit {hash:?} ({number}), best={is_best}, state={}, existing={existing_header}, finalized={finalized}", operation.commit_state, - existing_header, - finalized, ); self.state_usage.merge_sm(operation.old_state.usage_info()); @@ -1684,36 +1678,60 @@ impl Backend { ); } - if let Some((mut start, end)) = block_gap { - if number == start { - start += One::one(); - utils::insert_number_to_key_mapping( - &mut transaction, - columns::KEY_LOOKUP, - number, - hash, - )?; - } - if start > end { - transaction.remove(columns::META, meta_keys::BLOCK_GAP); - block_gap = None; - debug!(target: "db", "Removed block gap."); - } else { - block_gap = Some((start, end)); - debug!(target: "db", "Update block gap. {:?}", block_gap); - transaction.set( - columns::META, - meta_keys::BLOCK_GAP, - &(start, end).encode(), - ); + if let Some(mut gap) = block_gap { + match gap.gap_type { + BlockGapType::MissingHeaderAndBody => + if number == gap.start { + gap.start += One::one(); + utils::insert_number_to_key_mapping( + &mut transaction, + columns::KEY_LOOKUP, + number, + hash, + )?; + if gap.start > gap.end { + transaction.remove(columns::META, meta_keys::BLOCK_GAP); + transaction.remove(columns::META, meta_keys::BLOCK_GAP_VERSION); + block_gap = None; + debug!(target: "db", "Removed block gap."); + } else { + block_gap = Some(gap); + debug!(target: "db", "Update block gap. {block_gap:?}"); + transaction.set( + columns::META, + meta_keys::BLOCK_GAP, + &gap.encode(), + ); + transaction.set( + columns::META, + meta_keys::BLOCK_GAP_VERSION, + &BLOCK_GAP_CURRENT_VERSION.encode(), + ); + } + block_gap_updated = true; + }, + BlockGapType::MissingBody => { + unreachable!("Unsupported block gap. TODO: https://github.com/paritytech/polkadot-sdk/issues/5406") + }, } - } else if number > best_num + One::one() && - number > One::one() && self.blockchain.header(parent_hash)?.is_none() + } else if operation.create_gap && + number > best_num + One::one() && + self.blockchain.header(parent_hash)?.is_none() { - let gap = (best_num + One::one(), number - One::one()); + let gap = BlockGap { + start: best_num + One::one(), + end: number - One::one(), + gap_type: BlockGapType::MissingHeaderAndBody, + }; transaction.set(columns::META, meta_keys::BLOCK_GAP, &gap.encode()); + transaction.set( + columns::META, + meta_keys::BLOCK_GAP_VERSION, + &BLOCK_GAP_CURRENT_VERSION.encode(), + ); block_gap = Some(gap); - debug!(target: "db", "Detected block gap {:?}", block_gap); + block_gap_updated = true; + debug!(target: "db", "Detected block gap {block_gap:?}"); } } @@ -1747,9 +1765,8 @@ impl Backend { }); } else { return Err(sp_blockchain::Error::UnknownBlock(format!( - "Cannot set head {:?}", - set_head - ))) + "Cannot set head {set_head:?}", + ))); } } @@ -1759,7 +1776,7 @@ impl Backend { // Code beyond this point can't fail. if let Some((header, hash)) = imported { - trace!(target: "db", "DB Commit done {:?}", hash); + trace!(target: "db", "DB Commit done {hash:?}"); let header_metadata = CachedHeaderMetadata::from(&header); self.blockchain.insert_header_metadata(header_metadata.hash, header_metadata); cache_header(&mut self.blockchain.header_cache.lock(), hash, Some(header)); @@ -1768,7 +1785,9 @@ impl Backend { for m in meta_updates { self.blockchain.update_meta(m); } - self.blockchain.update_block_gap(block_gap); + if block_gap_updated { + self.blockchain.update_block_gap(block_gap); + } Ok(()) } @@ -1875,7 +1894,7 @@ impl Backend { transaction: &mut Transaction, id: BlockId, ) -> ClientResult<()> { - debug!(target: "db", "Removing block #{}", id); + debug!(target: "db", "Removing block #{id}"); utils::remove_from_db( transaction, &*self.storage.db, @@ -1909,8 +1928,7 @@ impl Backend { }, Err(err) => return Err(sp_blockchain::Error::Backend(format!( - "Error decoding body list: {}", - err + "Error decoding body list: {err}", ))), } } @@ -2060,6 +2078,7 @@ impl sc_client_api::backend::Backend for Backend { finalized_blocks: Vec::new(), set_head: None, commit_state: false, + create_gap: true, index_ops: Default::default(), }) } @@ -2138,14 +2157,14 @@ impl sc_client_api::backend::Backend for Backend { if number > self.blockchain.info().finalized_number || (hash != last_finalized && !is_descendent_of(&hash, &last_finalized)?) { - return Err(ClientError::NotInFinalizedChain) + return Err(ClientError::NotInFinalizedChain); } let justifications = if let Some(mut stored_justifications) = self.blockchain.justifications(hash)? { if !stored_justifications.append(justification) { - return Err(ClientError::BadJustification("Duplicate consensus engine ID".into())) + return Err(ClientError::BadJustification("Duplicate consensus engine ID".into())); } stored_justifications } else { @@ -2230,13 +2249,12 @@ impl sc_client_api::backend::Backend for Backend { let mut revert_blocks = || -> ClientResult> { for c in 0..n.saturated_into::() { if number_to_revert.is_zero() { - return Ok(c.saturated_into::>()) + return Ok(c.saturated_into::>()); } let mut transaction = Transaction::new(); let removed = self.blockchain.header(hash_to_revert)?.ok_or_else(|| { sp_blockchain::Error::UnknownBlock(format!( - "Error reverting to {}. Block header not found.", - hash_to_revert, + "Error reverting to {hash_to_revert}. Block header not found.", )) })?; let removed_hash = removed.hash(); @@ -2246,7 +2264,7 @@ impl sc_client_api::backend::Backend for Backend { if prev_number == best_number { best_hash } else { *removed.parent_hash() }; if !self.have_state_at(prev_hash, prev_number) { - return Ok(c.saturated_into::>()) + return Ok(c.saturated_into::>()); } match self.storage.state_db.revert_one() { @@ -2342,23 +2360,21 @@ impl sc_client_api::backend::Backend for Backend { let best_hash = self.blockchain.info().best_hash; if best_hash == hash { - return Err(sp_blockchain::Error::Backend(format!("Can't remove best block {:?}", hash))) + return Err(sp_blockchain::Error::Backend(format!("Can't remove best block {hash:?}"))); } let hdr = self.blockchain.header_metadata(hash)?; if !self.have_state_at(hash, hdr.number) { return Err(sp_blockchain::Error::UnknownBlock(format!( - "State already discarded for {:?}", - hash - ))) + "State already discarded for {hash:?}", + ))); } let mut leaves = self.blockchain.leaves.write(); if !leaves.contains(hdr.number, hash) { return Err(sp_blockchain::Error::Backend(format!( - "Can't remove non-leaf block {:?}", - hash - ))) + "Can't remove non-leaf block {hash:?}", + ))); } let mut transaction = Transaction::new(); @@ -2398,7 +2414,7 @@ impl sc_client_api::backend::Backend for Backend { if let Some(outcome) = remove_outcome { leaves.undo().undo_remove(outcome); } - return Err(e.into()) + return Err(e.into()); } self.blockchain().remove_header_metadata(hash); Ok(()) @@ -2420,7 +2436,7 @@ impl sc_client_api::backend::Backend for Backend { .build(); let state = RefTrackingState::new(db_state, self.storage.clone(), None); - return Ok(RecordStatsState::new(state, None, self.state_usage.clone())) + return Ok(RecordStatsState::new(state, None, self.state_usage.clone())); } } @@ -2446,8 +2462,7 @@ impl sc_client_api::backend::Backend for Backend { Ok(RecordStatsState::new(state, Some(hash), self.state_usage.clone())) } else { Err(sp_blockchain::Error::UnknownBlock(format!( - "State already discarded for {:?}", - hash + "State already discarded for {hash:?}", ))) } }, @@ -2512,16 +2527,14 @@ impl sc_client_api::backend::Backend for Backend { self.storage.state_db.pin(&hash, number.saturated_into::(), hint).map_err( |_| { sp_blockchain::Error::UnknownBlock(format!( - "Unable to pin: state already discarded for `{:?}`", - hash + "Unable to pin: state already discarded for `{hash:?}`", )) }, )?; } else { return Err(ClientError::UnknownBlock(format!( - "Can not pin block with hash `{:?}`. Block not found.", - hash - ))) + "Can not pin block with hash `{hash:?}`. Block not found.", + ))); } if self.blocks_pruning != BlocksPruning::KeepAll { @@ -4226,8 +4239,9 @@ pub(crate) mod tests { match pruning_mode { // we can only revert to blocks for which we have state, if pruning is enabled // then the last state available will be that of the latest finalized block - BlocksPruning::Some(_) => - assert_eq!(backend.blockchain().info().finalized_number, 8), + BlocksPruning::Some(_) => { + assert_eq!(backend.blockchain().info().finalized_number, 8) + }, // otherwise if we're not doing state pruning we can revert past finalized blocks _ => assert_eq!(backend.blockchain().info().finalized_number, 5), } diff --git a/substrate/client/db/src/utils.rs b/substrate/client/db/src/utils.rs index b532e0d4666273648a7cef073a11ab21eb3a2554..0b591c967e6019339e5970e4f94ae5e89abded82 100644 --- a/substrate/client/db/src/utils.rs +++ b/substrate/client/db/src/utils.rs @@ -25,10 +25,14 @@ use log::{debug, info}; use crate::{Database, DatabaseSource, DbHash}; use codec::Decode; +use sc_client_api::blockchain::{BlockGap, BlockGapType}; use sp_database::Transaction; use sp_runtime::{ generic::BlockId, - traits::{Block as BlockT, Header as HeaderT, UniqueSaturatedFrom, UniqueSaturatedInto, Zero}, + traits::{ + Block as BlockT, Header as HeaderT, NumberFor, UniqueSaturatedFrom, UniqueSaturatedInto, + Zero, + }, }; use sp_trie::DBValue; @@ -38,6 +42,9 @@ pub const NUM_COLUMNS: u32 = 13; /// Meta column. The set of keys in the column is shared by full && light storages. pub const COLUMN_META: u32 = 0; +/// Current block gap version. +pub const BLOCK_GAP_CURRENT_VERSION: u32 = 1; + /// Keys of entries in COLUMN_META. pub mod meta_keys { /// Type of storage (full or light). @@ -50,6 +57,8 @@ pub mod meta_keys { pub const FINALIZED_STATE: &[u8; 6] = b"fstate"; /// Block gap. pub const BLOCK_GAP: &[u8; 3] = b"gap"; + /// Block gap version. + pub const BLOCK_GAP_VERSION: &[u8; 7] = b"gap_ver"; /// Genesis block hash. pub const GENESIS_HASH: &[u8; 3] = b"gen"; /// Leaves prefix list key. @@ -73,8 +82,8 @@ pub struct Meta { pub genesis_hash: H, /// Finalized state, if any pub finalized_state: Option<(H, N)>, - /// Block gap, start and end inclusive, if any. - pub block_gap: Option<(N, N)>, + /// Block gap, if any. + pub block_gap: Option>, } /// A block lookup key: used for canonical lookup from block number to hash @@ -197,7 +206,7 @@ fn open_database_at( open_kvdb_rocksdb::(path, db_type, create, *cache_size)?, DatabaseSource::Custom { db, require_create_flag } => { if *require_create_flag && !create { - return Err(OpenDbError::DoesNotExist) + return Err(OpenDbError::DoesNotExist); } db.clone() }, @@ -364,7 +373,7 @@ pub fn check_database_type( return Err(OpenDbError::UnexpectedDbType { expected: db_type, found: stored_type.to_owned(), - }) + }); }, None => { let mut transaction = Transaction::new(); @@ -515,9 +524,31 @@ where } else { None }; - let block_gap = db - .get(COLUMN_META, meta_keys::BLOCK_GAP) - .and_then(|d| Decode::decode(&mut d.as_slice()).ok()); + let block_gap = match db + .get(COLUMN_META, meta_keys::BLOCK_GAP_VERSION) + .and_then(|d| u32::decode(&mut d.as_slice()).ok()) + { + None => { + let old_block_gap: Option<(NumberFor, NumberFor)> = db + .get(COLUMN_META, meta_keys::BLOCK_GAP) + .and_then(|d| Decode::decode(&mut d.as_slice()).ok()); + + old_block_gap.map(|(start, end)| BlockGap { + start, + end, + gap_type: BlockGapType::MissingHeaderAndBody, + }) + }, + Some(version) => match version { + BLOCK_GAP_CURRENT_VERSION => db + .get(COLUMN_META, meta_keys::BLOCK_GAP) + .and_then(|d| Decode::decode(&mut d.as_slice()).ok()), + v => + return Err(sp_blockchain::Error::Backend(format!( + "Unsupported block gap DB version: {v}" + ))), + }, + }; debug!(target: "db", "block_gap={:?}", block_gap); Ok(Meta { diff --git a/substrate/client/executor/src/wasm_runtime.rs b/substrate/client/executor/src/wasm_runtime.rs index be8344ba79b7d78f8d49880ea73754788f33ff3e..77dfc09c8807e7bb6fa9289bf3a58cc909b9f4f8 100644 --- a/substrate/client/executor/src/wasm_runtime.rs +++ b/substrate/client/executor/src/wasm_runtime.rs @@ -480,7 +480,7 @@ mod tests { let version = decode_version(&old_runtime_version.encode()).unwrap(); assert_eq!(1, version.transaction_version); - assert_eq!(0, version.state_version); + assert_eq!(0, version.system_version); } #[test] @@ -507,12 +507,12 @@ mod tests { impl_version: 1, apis: create_apis_vec!([(>::ID, 3)]), transaction_version: 3, - state_version: 4, + system_version: 4, }; let version = decode_version(&old_runtime_version.encode()).unwrap(); assert_eq!(3, version.transaction_version); - assert_eq!(0, version.state_version); + assert_eq!(0, version.system_version); let old_runtime_version = RuntimeVersion { spec_name: "test".into(), @@ -522,12 +522,12 @@ mod tests { impl_version: 1, apis: create_apis_vec!([(>::ID, 4)]), transaction_version: 3, - state_version: 4, + system_version: 4, }; let version = decode_version(&old_runtime_version.encode()).unwrap(); assert_eq!(3, version.transaction_version); - assert_eq!(4, version.state_version); + assert_eq!(4, version.system_version); } #[test] @@ -545,7 +545,7 @@ mod tests { impl_version: 100, apis: create_apis_vec!([(>::ID, 4)]), transaction_version: 100, - state_version: 1, + system_version: 1, }; let embedded = sp_version::embed::embed_runtime_version(&wasm, runtime_version.clone()) diff --git a/substrate/client/executor/wasmtime/build.rs b/substrate/client/executor/wasmtime/build.rs index a68cb706e8fbdc2526aa18730f0e7ddb9719f838..c63f0b8b66744738df18d99a274632cb1ecef980 100644 --- a/substrate/client/executor/wasmtime/build.rs +++ b/substrate/client/executor/wasmtime/build.rs @@ -20,6 +20,6 @@ use std::env; fn main() { if let Ok(profile) = env::var("PROFILE") { - println!("cargo:rustc-cfg=build_type=\"{}\"", profile); + println!("cargo:rustc-cfg=build_profile=\"{}\"", profile); } } diff --git a/substrate/client/executor/wasmtime/src/tests.rs b/substrate/client/executor/wasmtime/src/tests.rs index f86a4275769401712622112c6a2c587d11e1adc4..abf2b9509c2b26136fdfaa5a24275f2a2c6518b8 100644 --- a/substrate/client/executor/wasmtime/src/tests.rs +++ b/substrate/client/executor/wasmtime/src/tests.rs @@ -455,7 +455,7 @@ fn test_max_memory_pages( // This test takes quite a while to execute in a debug build (over 6 minutes on a TR 3970x) // so it's ignored by default unless it was compiled with `--release`. -#[cfg_attr(build_type = "debug", ignore)] +#[cfg_attr(build_profile = "debug", ignore)] #[test] fn test_instances_without_reuse_are_not_leaked() { let runtime = crate::create_runtime::( diff --git a/substrate/client/network-gossip/src/bridge.rs b/substrate/client/network-gossip/src/bridge.rs index 414da9b2a5890bb61fb77e3d942bb04e3e44add7..a4bd922a76d5e690d2ed2cc0138622ef767a33d0 100644 --- a/substrate/client/network-gossip/src/bridge.rs +++ b/substrate/client/network-gossip/src/bridge.rs @@ -377,9 +377,6 @@ mod tests { #[derive(Clone, Default)] struct TestNetwork {} - #[derive(Clone, Default)] - struct TestNetworkInner {} - #[async_trait::async_trait] impl NetworkPeers for TestNetwork { fn set_authorized_peers(&self, _peers: HashSet) { diff --git a/substrate/client/network/common/src/role.rs b/substrate/client/network/common/src/role.rs index 11b7a7924c46ad90c4662221db7c50866e81e195..e2218b37f0887433d84883447cb1f81bb03de48c 100644 --- a/substrate/client/network/common/src/role.rs +++ b/substrate/client/network/common/src/role.rs @@ -58,7 +58,7 @@ impl From for ObservedRole { } /// Role of the local node. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub enum Role { /// Regular full node. Full, diff --git a/substrate/client/network/light/src/light_client_requests.rs b/substrate/client/network/light/src/light_client_requests.rs index e55ceb62d7cd80b26b8926c627f4407a918fc7aa..a8ce601d6fc2dc0c73ae7e74273532de5f975137 100644 --- a/substrate/client/network/light/src/light_client_requests.rs +++ b/substrate/client/network/light/src/light_client_requests.rs @@ -18,7 +18,9 @@ //! Helpers for outgoing and incoming light client requests. -use sc_network::{config::ProtocolId, request_responses::IncomingRequest, NetworkBackend}; +use sc_network::{ + config::ProtocolId, request_responses::IncomingRequest, NetworkBackend, MAX_RESPONSE_SIZE, +}; use sp_runtime::traits::Block; use std::time::Duration; @@ -57,7 +59,7 @@ pub fn generate_protocol_config< generate_protocol_name(genesis_hash, fork_id).into(), std::iter::once(generate_legacy_protocol_name(protocol_id).into()).collect(), 1 * 1024 * 1024, - 16 * 1024 * 1024, + MAX_RESPONSE_SIZE, Duration::from_secs(15), Some(inbound_queue), ) diff --git a/substrate/client/network/src/behaviour.rs b/substrate/client/network/src/behaviour.rs index 9a6324dafd37da57aacfc8fceb29324fc80d998d..5ecbec52d507eeed4ba7be0609a278dc594d68ea 100644 --- a/substrate/client/network/src/behaviour.rs +++ b/substrate/client/network/src/behaviour.rs @@ -76,8 +76,6 @@ pub enum BehaviourOut { /// /// This event is generated for statistics purposes. InboundRequest { - /// Peer which sent us a request. - peer: PeerId, /// Protocol name of the request. protocol: ProtocolName, /// If `Ok`, contains the time elapsed between when we received the request and when we @@ -89,8 +87,6 @@ pub enum BehaviourOut { /// /// This event is generated for statistics purposes. RequestFinished { - /// Peer that we send a request to. - peer: PeerId, /// Name of the protocol in question. protocol: ProtocolName, /// Duration the request took. @@ -350,10 +346,10 @@ impl From for BehaviourOut { impl From for BehaviourOut { fn from(event: request_responses::Event) -> Self { match event { - request_responses::Event::InboundRequest { peer, protocol, result } => - BehaviourOut::InboundRequest { peer, protocol, result }, - request_responses::Event::RequestFinished { peer, protocol, duration, result } => - BehaviourOut::RequestFinished { peer, protocol, duration, result }, + request_responses::Event::InboundRequest { protocol, result, .. } => + BehaviourOut::InboundRequest { protocol, result }, + request_responses::Event::RequestFinished { protocol, duration, result, .. } => + BehaviourOut::RequestFinished { protocol, duration, result }, request_responses::Event::ReputationChanges { peer, changes } => BehaviourOut::ReputationChanges { peer, changes }, } diff --git a/substrate/client/network/src/bitswap/mod.rs b/substrate/client/network/src/bitswap/mod.rs index 1e20572eeeb11e90f60461ad04c8f24f5f0f7909..e45c95c7d3c84e8950381bacccff8fffb7a4c54f 100644 --- a/substrate/client/network/src/bitswap/mod.rs +++ b/substrate/client/network/src/bitswap/mod.rs @@ -23,6 +23,7 @@ use crate::{ request_responses::{IncomingRequest, OutgoingResponse, ProtocolConfig}, types::ProtocolName, + MAX_RESPONSE_SIZE, }; use cid::{self, Version}; @@ -47,7 +48,7 @@ const LOG_TARGET: &str = "bitswap"; // https://github.com/ipfs/js-ipfs-bitswap/blob/ // d8f80408aadab94c962f6b88f343eb9f39fa0fcc/src/decision-engine/index.js#L16 // We set it to the same value as max substrate protocol message -const MAX_PACKET_SIZE: u64 = 16 * 1024 * 1024; +const MAX_PACKET_SIZE: u64 = MAX_RESPONSE_SIZE; /// Max number of queued responses before denying requests. const MAX_REQUEST_QUEUE: usize = 20; diff --git a/substrate/client/network/src/lib.rs b/substrate/client/network/src/lib.rs index 99a972f914e261e60ef8692fc3fab7a53051d3df..9300cbccc9ad3054d9e8b3cdf0c5fa4801adece6 100644 --- a/substrate/client/network/src/lib.rs +++ b/substrate/client/network/src/lib.rs @@ -302,3 +302,6 @@ const MAX_CONNECTIONS_PER_PEER: usize = 2; /// The maximum number of concurrent established connections that were incoming. const MAX_CONNECTIONS_ESTABLISHED_INCOMING: u32 = 10_000; + +/// Maximum response size limit. +pub const MAX_RESPONSE_SIZE: u64 = 16 * 1024 * 1024; diff --git a/substrate/client/network/src/litep2p/discovery.rs b/substrate/client/network/src/litep2p/discovery.rs index 62d5f0fb6f06a864f2d4c9f134c33ba3686f67fd..5fe944cadc0b29bcb96f53e683edfead9b82bcb9 100644 --- a/substrate/client/network/src/litep2p/discovery.rs +++ b/substrate/client/network/src/litep2p/discovery.rs @@ -95,15 +95,6 @@ pub enum DiscoveryEvent { /// Peer ID. peer: PeerId, - /// Identify protocol version. - protocol_version: Option, - - /// Identify user agent version. - user_agent: Option, - - /// Observed address. - observed_address: Multiaddr, - /// Listen addresses. listen_addresses: Vec, @@ -243,11 +234,9 @@ impl Discovery { ) -> (Self, PingConfig, IdentifyConfig, KademliaConfig, Option) { let (ping_config, ping_event_stream) = PingConfig::default(); let user_agent = format!("{} ({})", config.client_version, config.node_name); - let (identify_config, identify_event_stream) = IdentifyConfig::new( - "/substrate/1.0".to_string(), - Some(user_agent), - config.public_addresses.clone().into_iter().map(Into::into).collect(), - ); + + let (identify_config, identify_event_stream) = + IdentifyConfig::new("/substrate/1.0".to_string(), Some(user_agent)); let (mdns_config, mdns_event_stream) = match config.transport { crate::config::TransportConfig::Normal { enable_mdns, .. } => match enable_mdns { @@ -563,11 +552,10 @@ impl Stream for Discovery { Poll::Ready(None) => return Poll::Ready(None), Poll::Ready(Some(IdentifyEvent::PeerIdentified { peer, - protocol_version, - user_agent, listen_addresses, supported_protocols, observed_address, + .. })) => { if this.is_new_external_address(&observed_address, peer) { this.pending_events.push_back(DiscoveryEvent::ExternalAddressDiscovered { @@ -577,10 +565,7 @@ impl Stream for Discovery { return Poll::Ready(Some(DiscoveryEvent::Identified { peer, - protocol_version, - user_agent, listen_addresses, - observed_address, supported_protocols, })); }, diff --git a/substrate/client/network/src/litep2p/mod.rs b/substrate/client/network/src/litep2p/mod.rs index 521f1a5fe0f79a7be793431d1f86b38ae744fb99..277f0759729c84275abeb48a953aa9daf29d4808 100644 --- a/substrate/client/network/src/litep2p/mod.rs +++ b/substrate/client/network/src/litep2p/mod.rs @@ -54,6 +54,7 @@ use libp2p::kad::{PeerRecord, Record as P2PRecord, RecordKey}; use litep2p::{ config::ConfigBuilder, crypto::ed25519::Keypair, + error::{DialError, NegotiationError}, executor::Executor, protocol::{ libp2p::{ @@ -64,15 +65,14 @@ use litep2p::{ }, transport::{ tcp::config::Config as TcpTransportConfig, - websocket::config::Config as WebSocketTransportConfig, Endpoint, + websocket::config::Config as WebSocketTransportConfig, ConnectionLimitsConfig, Endpoint, }, types::{ multiaddr::{Multiaddr, Protocol}, ConnectionId, }, - Error as Litep2pError, Litep2p, Litep2pEvent, ProtocolName as Litep2pProtocolName, + Litep2p, Litep2pEvent, ProtocolName as Litep2pProtocolName, }; -use parking_lot::RwLock; use prometheus_endpoint::Registry; use sc_client_api::BlockBackend; @@ -183,9 +183,6 @@ pub struct Litep2pNetworkBackend { /// Prometheus metrics. metrics: Option, - - /// External addresses. - external_addresses: Arc>>, } impl Litep2pNetworkBackend { @@ -557,6 +554,9 @@ impl NetworkBackend for Litep2pNetworkBac .with_libp2p_ping(ping_config) .with_libp2p_identify(identify_config) .with_libp2p_kademlia(kademlia_config) + .with_connection_limits(ConnectionLimitsConfig::default().max_incoming_connections( + Some(crate::MAX_CONNECTIONS_ESTABLISHED_INCOMING as usize), + )) .with_executor(executor); if let Some(config) = maybe_mdns_config { @@ -570,15 +570,22 @@ impl NetworkBackend for Litep2pNetworkBac let litep2p = Litep2p::new(config_builder.build()).map_err(|error| Error::Litep2p(error))?; - let external_addresses: Arc>> = Arc::new(RwLock::new( - HashSet::from_iter(network_config.public_addresses.iter().cloned().map(Into::into)), - )); litep2p.listen_addresses().for_each(|address| { log::debug!(target: LOG_TARGET, "listening on: {address}"); listen_addresses.write().insert(address.clone()); }); + let public_addresses = litep2p.public_addresses(); + for address in network_config.public_addresses.iter() { + if let Err(err) = public_addresses.add_address(address.clone().into()) { + log::warn!( + target: LOG_TARGET, + "failed to add public address {address:?}: {err:?}", + ); + } + } + let network_service = Arc::new(Litep2pNetworkService::new( local_peer_id, keypair.clone(), @@ -588,7 +595,7 @@ impl NetworkBackend for Litep2pNetworkBac block_announce_protocol.clone(), request_response_senders, Arc::clone(&listen_addresses), - Arc::clone(&external_addresses), + public_addresses, )); // register rest of the metrics now that `Litep2p` has been created @@ -614,7 +621,6 @@ impl NetworkBackend for Litep2pNetworkBac event_streams: out_events::OutChannels::new(None)?, peers: HashMap::new(), litep2p, - external_addresses, }) } @@ -917,10 +923,16 @@ impl NetworkBackend for Litep2pNetworkBac self.discovery.add_self_reported_address(peer, supported_protocols, listen_addresses).await; } Some(DiscoveryEvent::ExternalAddressDiscovered { address }) => { - let mut addresses = self.external_addresses.write(); - - if addresses.insert(address.clone()) { - log::info!(target: LOG_TARGET, "๐Ÿ” Discovered new external address for our node: {address}"); + match self.litep2p.public_addresses().add_address(address.clone().into()) { + Ok(inserted) => if inserted { + log::info!(target: LOG_TARGET, "๐Ÿ” Discovered new external address for our node: {address}"); + }, + Err(err) => { + log::warn!( + target: LOG_TARGET, + "๐Ÿ” Failed to add discovered external address {address:?}: {err:?}", + ); + }, } } Some(DiscoveryEvent::Ping { peer, rtt }) => { @@ -1006,20 +1018,40 @@ impl NetworkBackend for Litep2pNetworkBac } } Some(Litep2pEvent::DialFailure { address, error }) => { - log::trace!( + log::debug!( target: LOG_TARGET, "failed to dial peer at {address:?}: {error:?}", ); - let reason = match error { - Litep2pError::PeerIdMismatch(_, _) => "invalid-peer-id", - Litep2pError::Timeout | Litep2pError::TransportError(_) | - Litep2pError::IoError(_) | Litep2pError::WebSocket(_) => "transport-error", - _ => "other", - }; + if let Some(metrics) = &self.metrics { + let reason = match error { + DialError::Timeout => "timeout", + DialError::AddressError(_) => "invalid-address", + DialError::DnsError(_) => "cannot-resolve-dns", + DialError::NegotiationError(error) => match error { + NegotiationError::Timeout => "timeout", + NegotiationError::PeerIdMissing => "missing-peer-id", + NegotiationError::StateMismatch => "state-mismatch", + NegotiationError::PeerIdMismatch(_,_) => "peer-id-missmatch", + NegotiationError::MultistreamSelectError(_) => "multistream-select-error", + NegotiationError::SnowError(_) => "noise-error", + NegotiationError::ParseError(_) => "parse-error", + NegotiationError::IoError(_) => "io-error", + NegotiationError::WebSocket(_) => "webscoket-error", + } + }; + + metrics.pending_connections_errors_total.with_label_values(&[&reason]).inc(); + } + } + Some(Litep2pEvent::ListDialFailures { errors }) => { + log::debug!( + target: LOG_TARGET, + "failed to dial peer on multiple addresses {errors:?}", + ); if let Some(metrics) = &self.metrics { - metrics.pending_connections_errors_total.with_label_values(&[reason]).inc(); + metrics.pending_connections_errors_total.with_label_values(&["transport-errors"]).inc(); } } _ => {} diff --git a/substrate/client/network/src/litep2p/service.rs b/substrate/client/network/src/litep2p/service.rs index 67fc44e6bfe0efa1c39bfc5804faac2ba02e563d..693217f5ad94c5e33fc3fc2b0c8871e978b55ad5 100644 --- a/substrate/client/network/src/litep2p/service.rs +++ b/substrate/client/network/src/litep2p/service.rs @@ -36,7 +36,10 @@ use crate::litep2p::Record; use codec::DecodeAll; use futures::{channel::oneshot, stream::BoxStream}; use libp2p::{identity::SigningError, kad::record::Key as KademliaKey}; -use litep2p::{crypto::ed25519::Keypair, types::multiaddr::Multiaddr as LiteP2pMultiaddr}; +use litep2p::{ + addresses::PublicAddresses, crypto::ed25519::Keypair, + types::multiaddr::Multiaddr as LiteP2pMultiaddr, +}; use parking_lot::RwLock; use sc_network_common::{ @@ -196,7 +199,7 @@ pub struct Litep2pNetworkService { listen_addresses: Arc>>, /// External addresses. - external_addresses: Arc>>, + external_addresses: PublicAddresses, } impl Litep2pNetworkService { @@ -210,7 +213,7 @@ impl Litep2pNetworkService { block_announce_protocol: ProtocolName, request_response_protocols: HashMap>, listen_addresses: Arc>>, - external_addresses: Arc>>, + external_addresses: PublicAddresses, ) -> Self { Self { local_peer_id, @@ -323,9 +326,8 @@ impl NetworkStatusProvider for Litep2pNetworkService { .collect(), external_addresses: self .external_addresses - .read() - .iter() - .cloned() + .get_addresses() + .into_iter() .map(|a| Multiaddr::from(a).into()) .collect(), connected_peers: HashMap::new(), @@ -491,7 +493,7 @@ impl NetworkEventStream for Litep2pNetworkService { impl NetworkStateInfo for Litep2pNetworkService { fn external_addresses(&self) -> Vec { - self.external_addresses.read().iter().cloned().map(Into::into).collect() + self.external_addresses.get_addresses().into_iter().map(Into::into).collect() } fn listen_addresses(&self) -> Vec { diff --git a/substrate/client/network/src/litep2p/shim/request_response/mod.rs b/substrate/client/network/src/litep2p/shim/request_response/mod.rs index a77acb464144f6b04ce1c8ad3b1b63ff6e535256..bfd7a60ef9fe60f65c8407eeb9343453b2409306 100644 --- a/substrate/client/network/src/litep2p/shim/request_response/mod.rs +++ b/substrate/client/network/src/litep2p/shim/request_response/mod.rs @@ -29,8 +29,10 @@ use crate::{ use futures::{channel::oneshot, future::BoxFuture, stream::FuturesUnordered, StreamExt}; use litep2p::{ + error::{ImmediateDialError, NegotiationError, SubstreamError}, protocol::request_response::{ - DialOptions, RequestResponseError, RequestResponseEvent, RequestResponseHandle, + DialOptions, RejectReason, RequestResponseError, RequestResponseEvent, + RequestResponseHandle, }, types::RequestId, }; @@ -372,7 +374,32 @@ impl RequestResponseProtocol { let status = match error { RequestResponseError::NotConnected => Some((RequestFailure::NotConnected, "not-connected")), - RequestResponseError::Rejected => Some((RequestFailure::Refused, "rejected")), + RequestResponseError::Rejected(reason) => { + let reason = match reason { + RejectReason::ConnectionClosed => "connection-closed", + RejectReason::SubstreamClosed => "substream-closed", + RejectReason::SubstreamOpenError(substream_error) => match substream_error { + SubstreamError::NegotiationError(NegotiationError::Timeout) => + "substream-timeout", + _ => "substream-open-error", + }, + RejectReason::DialFailed(None) => "dial-failed", + RejectReason::DialFailed(Some(ImmediateDialError::AlreadyConnected)) => + "dial-already-connected", + RejectReason::DialFailed(Some(ImmediateDialError::PeerIdMissing)) => + "dial-peerid-missing", + RejectReason::DialFailed(Some(ImmediateDialError::TriedToDialSelf)) => + "dial-tried-to-dial-self", + RejectReason::DialFailed(Some(ImmediateDialError::NoAddressAvailable)) => + "dial-no-address-available", + RejectReason::DialFailed(Some(ImmediateDialError::TaskClosed)) => + "dial-task-closed", + RejectReason::DialFailed(Some(ImmediateDialError::ChannelClogged)) => + "dial-channel-clogged", + }; + + Some((RequestFailure::Refused, reason)) + }, RequestResponseError::Timeout => Some((RequestFailure::Network(OutboundFailure::Timeout), "timeout")), RequestResponseError::Canceled => { diff --git a/substrate/client/network/src/litep2p/shim/request_response/tests.rs b/substrate/client/network/src/litep2p/shim/request_response/tests.rs index e3e82aa395c58f96d4f8d4844057c39d349541c8..78b6ef0a481c981c550627b1210510dba63a637a 100644 --- a/substrate/client/network/src/litep2p/shim/request_response/tests.rs +++ b/substrate/client/network/src/litep2p/shim/request_response/tests.rs @@ -271,7 +271,12 @@ async fn too_many_inbound_requests() { match handle2.next().await { Some(RequestResponseEvent::RequestFailed { peer, error, .. }) => { assert_eq!(peer, peer1); - assert_eq!(error, RequestResponseError::Rejected); + assert_eq!( + error, + RequestResponseError::Rejected( + litep2p::protocol::request_response::RejectReason::SubstreamClosed + ) + ); }, event => panic!("inavlid event: {event:?}"), } diff --git a/substrate/client/network/src/protocol.rs b/substrate/client/network/src/protocol.rs index 977c4c4de663211a5e1694ada392c3e1164818a3..402baa7bb2a407cfd9d44f9248bbac2ee6cc8cdf 100644 --- a/substrate/client/network/src/protocol.rs +++ b/substrate/client/network/src/protocol.rs @@ -22,6 +22,7 @@ use crate::{ protocol_controller::{self, SetId}, service::{metrics::NotificationMetrics, traits::Direction}, types::ProtocolName, + MAX_RESPONSE_SIZE, }; use codec::Encode; @@ -56,7 +57,7 @@ pub mod message; /// Maximum size used for notifications in the block announce and transaction protocols. // Must be equal to `max(MAX_BLOCK_ANNOUNCE_SIZE, MAX_TRANSACTIONS_SIZE)`. -pub(crate) const BLOCK_ANNOUNCES_TRANSACTIONS_SUBSTREAM_SIZE: u64 = 16 * 1024 * 1024; +pub(crate) const BLOCK_ANNOUNCES_TRANSACTIONS_SUBSTREAM_SIZE: u64 = MAX_RESPONSE_SIZE; /// Identifier of the peerset for the block announces protocol. const HARDCODED_PEERSETS_SYNC: SetId = SetId::from(0); diff --git a/substrate/client/network/src/protocol/notifications/behaviour.rs b/substrate/client/network/src/protocol/notifications/behaviour.rs index cb4f089995e3c6bffce8e6b8460602343d310174..a562546145c8ef635151f3227702ea3104633e6b 100644 --- a/substrate/client/network/src/protocol/notifications/behaviour.rs +++ b/substrate/client/network/src/protocol/notifications/behaviour.rs @@ -33,7 +33,7 @@ use bytes::BytesMut; use fnv::FnvHashMap; use futures::{future::BoxFuture, prelude::*, stream::FuturesUnordered}; use libp2p::{ - core::{ConnectedPoint, Endpoint, Multiaddr}, + core::{Endpoint, Multiaddr}, swarm::{ behaviour::{ConnectionClosed, ConnectionEstablished, DialFailure, FromSwarm}, ConnectionDenied, ConnectionId, DialError, NetworkBehaviour, NotifyHandler, PollParameters, @@ -362,8 +362,6 @@ pub enum NotificationsOut { received_handshake: Vec, /// Object that permits sending notifications to the peer. notifications_sink: NotificationsSink, - /// Is the connection inbound. - inbound: bool, }, /// The [`NotificationsSink`] object used to send notifications with the given peer must be @@ -1223,33 +1221,20 @@ impl NetworkBehaviour for Notifications { &mut self, _connection_id: ConnectionId, peer: PeerId, - local_addr: &Multiaddr, - remote_addr: &Multiaddr, + _local_addr: &Multiaddr, + _remote_addr: &Multiaddr, ) -> Result, ConnectionDenied> { - Ok(NotifsHandler::new( - peer, - ConnectedPoint::Listener { - local_addr: local_addr.clone(), - send_back_addr: remote_addr.clone(), - }, - self.notif_protocols.clone(), - Some(self.metrics.clone()), - )) + Ok(NotifsHandler::new(peer, self.notif_protocols.clone(), Some(self.metrics.clone()))) } fn handle_established_outbound_connection( &mut self, _connection_id: ConnectionId, peer: PeerId, - addr: &Multiaddr, - role_override: Endpoint, + _addr: &Multiaddr, + _role_override: Endpoint, ) -> Result, ConnectionDenied> { - Ok(NotifsHandler::new( - peer, - ConnectedPoint::Dialer { address: addr.clone(), role_override }, - self.notif_protocols.clone(), - Some(self.metrics.clone()), - )) + Ok(NotifsHandler::new(peer, self.notif_protocols.clone(), Some(self.metrics.clone()))) } fn on_swarm_event(&mut self, event: FromSwarm) { @@ -2061,7 +2046,6 @@ impl NetworkBehaviour for Notifications { let event = NotificationsOut::CustomProtocolOpen { peer_id, set_id, - inbound, direction: if inbound { Direction::Inbound } else { @@ -2383,6 +2367,7 @@ mod tests { protocol::notifications::handler::tests::*, protocol_controller::{IncomingIndex, ProtoSetConfig, ProtocolController}, }; + use libp2p::core::ConnectedPoint; use sc_utils::mpsc::tracing_unbounded; use std::{collections::HashSet, iter}; @@ -2413,7 +2398,8 @@ mod tests { } fn development_notifs( - ) -> (Notifications, ProtocolController, Box) { + ) -> (Notifications, ProtocolController, Box) + { let (protocol_handle_pair, notif_service) = crate::protocol::notifications::service::notification_service("/proto/1".into()); let (to_notifications, from_controller) = @@ -2668,7 +2654,7 @@ mod tests { peer_id: peer, connection_id: conn, endpoint: &connected.clone(), - handler: NotifsHandler::new(peer, connected, vec![], None), + handler: NotifsHandler::new(peer, vec![], None), remaining_established: 0usize, }, )); @@ -2868,7 +2854,7 @@ mod tests { peer_id: peer, connection_id: conn, endpoint: &connected.clone(), - handler: NotifsHandler::new(peer, connected, vec![], None), + handler: NotifsHandler::new(peer, vec![], None), remaining_established: 0usize, }, )); @@ -3021,7 +3007,7 @@ mod tests { peer_id: peer, connection_id: conn, endpoint: &connected.clone(), - handler: NotifsHandler::new(peer, connected, vec![], None), + handler: NotifsHandler::new(peer, vec![], None), remaining_established: 0usize, }, )); @@ -3065,7 +3051,7 @@ mod tests { peer_id: peer, connection_id: conn, endpoint: &connected.clone(), - handler: NotifsHandler::new(peer, connected, vec![], None), + handler: NotifsHandler::new(peer, vec![], None), remaining_established: 0usize, }, )); @@ -3135,7 +3121,7 @@ mod tests { peer_id: peer, connection_id: conn, endpoint: &connected.clone(), - handler: NotifsHandler::new(peer, connected, vec![], None), + handler: NotifsHandler::new(peer, vec![], None), remaining_established: 0usize, }, )); @@ -3188,7 +3174,7 @@ mod tests { assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Enabled { .. }))); // open new substream - let event = conn_yielder.open_substream(peer, 0, connected, vec![1, 2, 3, 4]); + let event = conn_yielder.open_substream(peer, 0, vec![1, 2, 3, 4]); notif.on_connection_handler_event(peer, conn, event); assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Enabled { .. }))); @@ -3261,7 +3247,7 @@ mod tests { notif.on_connection_handler_event( peer, *conn, - conn_yielder.open_substream(peer, 0, connected.clone(), vec![1, 2, 3, 4]), + conn_yielder.open_substream(peer, 0, vec![1, 2, 3, 4]), ); } @@ -3283,7 +3269,7 @@ mod tests { peer_id: peer, connection_id: conn1, endpoint: &connected.clone(), - handler: NotifsHandler::new(peer, connected, vec![], None), + handler: NotifsHandler::new(peer, vec![], None), remaining_established: 0usize, }, )); @@ -3354,7 +3340,7 @@ mod tests { notif.on_connection_handler_event( peer, conn, - conn_yielder.open_substream(peer, 0, connected, vec![1, 2, 3, 4]), + conn_yielder.open_substream(peer, 0, vec![1, 2, 3, 4]), ); if let Some(PeerState::Enabled { ref connections, .. }) = notif.peers.get(&(peer, set_id)) { @@ -3409,7 +3395,7 @@ mod tests { peer_id: peer, connection_id: conn, endpoint: &connected.clone(), - handler: NotifsHandler::new(peer, connected, vec![], None), + handler: NotifsHandler::new(peer, vec![], None), remaining_established: 0usize, }, )); @@ -3483,7 +3469,7 @@ mod tests { peer_id: peer, connection_id: conn, endpoint: &connected.clone(), - handler: NotifsHandler::new(peer, connected, vec![], None), + handler: NotifsHandler::new(peer, vec![], None), remaining_established: 0usize, }, )); @@ -3546,7 +3532,7 @@ mod tests { peer_id: peer, connection_id: conn1, endpoint: &connected.clone(), - handler: NotifsHandler::new(peer, connected.clone(), vec![], None), + handler: NotifsHandler::new(peer, vec![], None), remaining_established: 0usize, }, )); @@ -3560,7 +3546,7 @@ mod tests { peer_id: peer, connection_id: conn2, endpoint: &connected.clone(), - handler: NotifsHandler::new(peer, connected, vec![], None), + handler: NotifsHandler::new(peer, vec![], None), remaining_established: 0usize, }, )); @@ -3614,7 +3600,7 @@ mod tests { peer_id: peer, connection_id: conn, endpoint: &connected.clone(), - handler: NotifsHandler::new(peer, connected, vec![], None), + handler: NotifsHandler::new(peer, vec![], None), remaining_established: 0usize, }, )); @@ -3672,7 +3658,7 @@ mod tests { peer_id: peer, connection_id: conn2, endpoint: &connected.clone(), - handler: NotifsHandler::new(peer, connected, vec![], None), + handler: NotifsHandler::new(peer, vec![], None), remaining_established: 0usize, }, )); @@ -3733,7 +3719,7 @@ mod tests { peer_id: peer, connection_id: conn1, endpoint: &connected.clone(), - handler: NotifsHandler::new(peer, connected, vec![], None), + handler: NotifsHandler::new(peer, vec![], None), remaining_established: 0usize, }, )); @@ -3786,7 +3772,7 @@ mod tests { notif.on_connection_handler_event( peer, conn1, - conn_yielder.open_substream(peer, 0, connected.clone(), vec![1, 2, 3, 4]), + conn_yielder.open_substream(peer, 0, vec![1, 2, 3, 4]), ); if let Some(PeerState::Enabled { ref connections, .. }) = notif.peers.get(&(peer, set_id)) { @@ -3802,7 +3788,7 @@ mod tests { peer_id: peer, connection_id: conn1, endpoint: &connected.clone(), - handler: NotifsHandler::new(peer, connected, vec![], None), + handler: NotifsHandler::new(peer, vec![], None), remaining_established: 0usize, }, )); @@ -3843,7 +3829,7 @@ mod tests { peer_id: peer, connection_id: conn, endpoint: &connected.clone(), - handler: NotifsHandler::new(peer, connected, vec![], None), + handler: NotifsHandler::new(peer, vec![], None), remaining_established: 0usize, }, )); @@ -3966,7 +3952,7 @@ mod tests { peer_id: peer, connection_id: conn, endpoint: &connected.clone(), - handler: NotifsHandler::new(peer, connected, vec![], None), + handler: NotifsHandler::new(peer, vec![], None), remaining_established: 0usize, }, )); @@ -4015,10 +4001,6 @@ mod tests { let peer = PeerId::random(); let conn = ConnectionId::new_unchecked(0); let set_id = SetId::from(0); - let connected = ConnectedPoint::Listener { - local_addr: Multiaddr::empty(), - send_back_addr: Multiaddr::empty(), - }; let mut conn_yielder = ConnectionYielder::new(); // move the peer to `Enabled` state @@ -4052,7 +4034,7 @@ mod tests { notif.protocol_report_accept(IncomingIndex(0)); assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Enabled { .. }))); - let event = conn_yielder.open_substream(peer, 0, connected, vec![1, 2, 3, 4]); + let event = conn_yielder.open_substream(peer, 0, vec![1, 2, 3, 4]); notif.on_connection_handler_event(peer, conn, event); assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Enabled { .. }))); @@ -4167,7 +4149,7 @@ mod tests { notif.peerset_report_connect(peer, set_id); assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Enabled { .. }))); - let event = conn_yielder.open_substream(peer, 0, connected, vec![1, 2, 3, 4]); + let event = conn_yielder.open_substream(peer, 0, vec![1, 2, 3, 4]); notif.on_connection_handler_event(peer, conn, event); assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Enabled { .. }))); @@ -4280,7 +4262,7 @@ mod tests { peer_id: peer, connection_id: conn, endpoint: &connected.clone(), - handler: NotifsHandler::new(peer, connected, vec![], None), + handler: NotifsHandler::new(peer, vec![], None), remaining_established: 0usize, }, )); @@ -4521,7 +4503,7 @@ mod tests { peer_id: peer, connection_id: ConnectionId::new_unchecked(0), endpoint: &connected.clone(), - handler: NotifsHandler::new(peer, connected, vec![], None), + handler: NotifsHandler::new(peer, vec![], None), remaining_established: 0usize, }, )); @@ -4623,7 +4605,7 @@ mod tests { peer_id: peer, connection_id: ConnectionId::new_unchecked(0), endpoint: &connected.clone(), - handler: NotifsHandler::new(peer, connected, vec![], None), + handler: NotifsHandler::new(peer, vec![], None), remaining_established: 0usize, }, )); @@ -4681,7 +4663,7 @@ mod tests { notif.peerset_report_connect(peer, set_id); assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Enabled { .. }))); - let event = conn_yielder.open_substream(peer, 0, connected, vec![1, 2, 3, 4]); + let event = conn_yielder.open_substream(peer, 0, vec![1, 2, 3, 4]); notif.on_connection_handler_event(peer, conn, event); assert!(std::matches!(notif.peers.get(&(peer, set_id)), Some(&PeerState::Enabled { .. }))); @@ -4705,7 +4687,7 @@ mod tests { peer_id: peer, connection_id: ConnectionId::new_unchecked(0), endpoint: &endpoint.clone(), - handler: NotifsHandler::new(peer, endpoint, vec![], None), + handler: NotifsHandler::new(peer, vec![], None), remaining_established: 0usize, }, )); @@ -4822,7 +4804,7 @@ mod tests { peer_id: peer, connection_id: ConnectionId::new_unchecked(1337), endpoint: &connected.clone(), - handler: NotifsHandler::new(peer, connected, vec![], None), + handler: NotifsHandler::new(peer, vec![], None), remaining_established: 0usize, }, )); @@ -4857,7 +4839,7 @@ mod tests { peer_id: peer, connection_id: ConnectionId::new_unchecked(1337), endpoint: &connected.clone(), - handler: NotifsHandler::new(peer, connected, vec![], None), + handler: NotifsHandler::new(peer, vec![], None), remaining_established: 0usize, }, )); @@ -4908,7 +4890,7 @@ mod tests { peer_id: peer, connection_id: ConnectionId::new_unchecked(1337), endpoint: &connected.clone(), - handler: NotifsHandler::new(peer, connected, vec![], None), + handler: NotifsHandler::new(peer, vec![], None), remaining_established: 0usize, }, )); @@ -4955,7 +4937,7 @@ mod tests { peer_id: peer, connection_id: conn, endpoint: &connected.clone(), - handler: NotifsHandler::new(peer, connected, vec![], None), + handler: NotifsHandler::new(peer, vec![], None), remaining_established: 0usize, }, )); @@ -5005,7 +4987,7 @@ mod tests { peer_id: peer, connection_id: ConnectionId::new_unchecked(1337), endpoint: &connected.clone(), - handler: NotifsHandler::new(peer, connected, vec![], None), + handler: NotifsHandler::new(peer, vec![], None), remaining_established: 0usize, }, )); @@ -5048,7 +5030,7 @@ mod tests { peer_id: peer, connection_id: conn, endpoint: &connected.clone(), - handler: NotifsHandler::new(peer, connected.clone(), vec![], None), + handler: NotifsHandler::new(peer, vec![], None), remaining_established: 0usize, }, )); @@ -5059,7 +5041,7 @@ mod tests { peer_id: peer, connection_id: conn, endpoint: &connected.clone(), - handler: NotifsHandler::new(peer, connected, vec![], None), + handler: NotifsHandler::new(peer, vec![], None), remaining_established: 0usize, }, )); @@ -5071,16 +5053,12 @@ mod tests { fn open_result_ok_non_existent_peer() { let (mut notif, _controller, _notif_service) = development_notifs(); let conn = ConnectionId::new_unchecked(0); - let connected = ConnectedPoint::Listener { - local_addr: Multiaddr::empty(), - send_back_addr: Multiaddr::empty(), - }; let mut conn_yielder = ConnectionYielder::new(); notif.on_connection_handler_event( PeerId::random(), conn, - conn_yielder.open_substream(PeerId::random(), 0, connected, vec![1, 2, 3, 4]), + conn_yielder.open_substream(PeerId::random(), 0, vec![1, 2, 3, 4]), ); } } diff --git a/substrate/client/network/src/protocol/notifications/handler.rs b/substrate/client/network/src/protocol/notifications/handler.rs index 967ef614c5560ca5815193bbd597b13e35b2076e..bff60ba1125f64e133d9e3b12e3923b9385c600e 100644 --- a/substrate/client/network/src/protocol/notifications/handler.rs +++ b/substrate/client/network/src/protocol/notifications/handler.rs @@ -73,7 +73,6 @@ use futures::{ prelude::*, }; use libp2p::{ - core::ConnectedPoint, swarm::{ handler::ConnectionEvent, ConnectionHandler, ConnectionHandlerEvent, KeepAlive, Stream, SubstreamProtocol, @@ -117,9 +116,6 @@ pub struct NotifsHandler { /// When the connection with the remote has been successfully established. when_connection_open: Instant, - /// Whether we are the connection dialer or listener. - endpoint: ConnectedPoint, - /// Remote we are connected to. peer_id: PeerId, @@ -136,7 +132,6 @@ impl NotifsHandler { /// Creates new [`NotifsHandler`]. pub fn new( peer_id: PeerId, - endpoint: ConnectedPoint, protocols: Vec, metrics: Option, ) -> Self { @@ -154,7 +149,6 @@ impl NotifsHandler { }) .collect(), peer_id, - endpoint, when_connection_open: Instant::now(), events_queue: VecDeque::with_capacity(16), metrics: metrics.map_or(None, |metrics| Some(Arc::new(metrics))), @@ -281,8 +275,6 @@ pub enum NotifsHandlerOut { protocol_index: usize, /// Name of the protocol that was actually negotiated, if the default one wasn't available. negotiated_fallback: Option, - /// The endpoint of the connection that is open for custom protocols. - endpoint: ConnectedPoint, /// Handshake that was sent to us. /// This is normally a "Status" message, but this out of the concern of this code. received_handshake: Vec, @@ -590,7 +582,6 @@ impl ConnectionHandler for NotifsHandler { NotifsHandlerOut::OpenResultOk { protocol_index, negotiated_fallback: new_open.negotiated_fallback, - endpoint: self.endpoint.clone(), received_handshake: new_open.handshake, notifications_sink, inbound, @@ -889,7 +880,6 @@ pub mod tests { use libp2p::{ core::muxing::SubstreamBox, swarm::handler::{self, StreamUpgradeError}, - Multiaddr, Stream, }; use multistream_select::{dialer_select_proto, listener_select_proto, Negotiated, Version}; use std::{ @@ -925,7 +915,6 @@ pub mod tests { &mut self, peer: PeerId, protocol_index: usize, - endpoint: ConnectedPoint, received_handshake: Vec, ) -> NotifsHandlerOut { let (async_tx, async_rx) = @@ -954,7 +943,6 @@ pub mod tests { NotifsHandlerOut::OpenResultOk { protocol_index, negotiated_fallback: None, - endpoint, received_handshake, notifications_sink, inbound: false, @@ -1110,10 +1098,6 @@ pub mod tests { NotifsHandler { protocols: vec![proto], when_connection_open: Instant::now(), - endpoint: ConnectedPoint::Listener { - local_addr: Multiaddr::empty(), - send_back_addr: Multiaddr::empty(), - }, peer_id: PeerId::random(), events_queue: VecDeque::new(), metrics: None, @@ -1131,7 +1115,6 @@ pub mod tests { let notif_in = NotificationsInOpen { handshake: b"hello, world".to_vec(), - negotiated_fallback: None, substream: NotificationsInSubstream::new( Framed::new(io, codec), NotificationsInSubstreamHandshake::NotSent, @@ -1158,7 +1141,6 @@ pub mod tests { let notif_in = NotificationsInOpen { handshake: b"hello, world".to_vec(), - negotiated_fallback: None, substream: NotificationsInSubstream::new( Framed::new(io, codec), NotificationsInSubstreamHandshake::NotSent, @@ -1191,7 +1173,6 @@ pub mod tests { let notif_in = NotificationsInOpen { handshake: b"hello, world".to_vec(), - negotiated_fallback: None, substream: NotificationsInSubstream::new( Framed::new(io, codec), NotificationsInSubstreamHandshake::NotSent, @@ -1225,7 +1206,6 @@ pub mod tests { let notif_in = NotificationsInOpen { handshake: b"hello, world".to_vec(), - negotiated_fallback: None, substream: NotificationsInSubstream::new( Framed::new(io, codec), NotificationsInSubstreamHandshake::NotSent, @@ -1265,7 +1245,6 @@ pub mod tests { let notif_in = NotificationsInOpen { handshake: b"hello, world".to_vec(), - negotiated_fallback: None, substream: NotificationsInSubstream::new( Framed::new(io, codec), NotificationsInSubstreamHandshake::NotSent, @@ -1316,7 +1295,6 @@ pub mod tests { codec.set_max_len(usize::MAX); let notif_in = NotificationsInOpen { handshake: b"hello, world".to_vec(), - negotiated_fallback: None, substream: NotificationsInSubstream::new( Framed::new(io, codec), NotificationsInSubstreamHandshake::NotSent, @@ -1355,7 +1333,6 @@ pub mod tests { let notif_in = NotificationsInOpen { handshake: b"hello, world".to_vec(), - negotiated_fallback: None, substream: NotificationsInSubstream::new( Framed::new(io, codec), NotificationsInSubstreamHandshake::NotSent, @@ -1415,7 +1392,6 @@ pub mod tests { let notif_in = NotificationsInOpen { handshake: b"hello, world".to_vec(), - negotiated_fallback: None, substream: NotificationsInSubstream::new( Framed::new(io, codec), NotificationsInSubstreamHandshake::NotSent, @@ -1452,7 +1428,6 @@ pub mod tests { let notif_in = NotificationsInOpen { handshake: b"hello, world".to_vec(), - negotiated_fallback: None, substream: NotificationsInSubstream::new( Framed::new(io, codec), NotificationsInSubstreamHandshake::NotSent, @@ -1498,7 +1473,6 @@ pub mod tests { let notif_in = NotificationsInOpen { handshake: b"hello, world".to_vec(), - negotiated_fallback: None, substream: NotificationsInSubstream::new( Framed::new(io, codec), NotificationsInSubstreamHandshake::NotSent, @@ -1547,7 +1521,6 @@ pub mod tests { let notif_in = NotificationsInOpen { handshake: b"hello, world".to_vec(), - negotiated_fallback: None, substream: NotificationsInSubstream::new( Framed::new(io, codec), NotificationsInSubstreamHandshake::NotSent, @@ -1583,7 +1556,6 @@ pub mod tests { let notif_in = NotificationsInOpen { handshake: b"hello, world".to_vec(), - negotiated_fallback: None, substream: NotificationsInSubstream::new( Framed::new(io, codec), NotificationsInSubstreamHandshake::NotSent, @@ -1658,7 +1630,6 @@ pub mod tests { let notif_in = NotificationsInOpen { handshake: b"hello, world".to_vec(), - negotiated_fallback: None, substream: NotificationsInSubstream::new( Framed::new(io, codec), NotificationsInSubstreamHandshake::PendingSend(vec![1, 2, 3, 4]), diff --git a/substrate/client/network/src/protocol/notifications/service/mod.rs b/substrate/client/network/src/protocol/notifications/service/mod.rs index 4f6d32ae3b356f8813bb38cab5b80050f600931d..a7eb31fc5795d6ee25d5769292428f40f3237d65 100644 --- a/substrate/client/network/src/protocol/notifications/service/mod.rs +++ b/substrate/client/network/src/protocol/notifications/service/mod.rs @@ -89,9 +89,8 @@ impl MessageSink for NotificationSink { .await .map_err(|_| error::Error::ConnectionClosed)?; - permit.send(notification).map_err(|_| error::Error::ChannelClosed).map(|res| { + permit.send(notification).map_err(|_| error::Error::ChannelClosed).inspect(|_| { metrics::register_notification_sent(sink.0.metrics(), &sink.1, notification_len); - res }) } } @@ -263,13 +262,12 @@ impl NotificationService for NotificationHandle { .map_err(|_| error::Error::ConnectionClosed)? .send(notification) .map_err(|_| error::Error::ChannelClosed) - .map(|res| { + .inspect(|_| { metrics::register_notification_sent( sink.metrics(), &self.protocol, notification_len, ); - res }) } diff --git a/substrate/client/network/src/protocol/notifications/upgrade/notifications.rs b/substrate/client/network/src/protocol/notifications/upgrade/notifications.rs index a8a9e453a7bb0c313e95dc1ffee954fa144f4d11..e01bcbe0bad795dc452b73b802e89f8c2043ca14 100644 --- a/substrate/client/network/src/protocol/notifications/upgrade/notifications.rs +++ b/substrate/client/network/src/protocol/notifications/upgrade/notifications.rs @@ -151,7 +151,7 @@ where type Future = Pin> + Send>>; type Error = NotificationsHandshakeError; - fn upgrade_inbound(self, mut socket: TSubstream, negotiated_name: Self::Info) -> Self::Future { + fn upgrade_inbound(self, mut socket: TSubstream, _negotiated_name: Self::Info) -> Self::Future { Box::pin(async move { let handshake_len = unsigned_varint::aio::read_usize(&mut socket).await?; if handshake_len > MAX_HANDSHAKE_SIZE { @@ -174,15 +174,7 @@ where handshake: NotificationsInSubstreamHandshake::NotSent, }; - Ok(NotificationsInOpen { - handshake, - negotiated_fallback: if negotiated_name == self.protocol_names[0] { - None - } else { - Some(negotiated_name) - }, - substream, - }) + Ok(NotificationsInOpen { handshake, substream }) }) } } @@ -191,9 +183,6 @@ where pub struct NotificationsInOpen { /// Handshake sent by the remote. pub handshake: Vec, - /// If the negotiated name is not the "main" protocol name but a fallback, contains the - /// name of the negotiated fallback. - pub negotiated_fallback: Option, /// Implementation of `Stream` that allows receives messages from the substream. pub substream: NotificationsInSubstream, } diff --git a/substrate/client/network/src/request_responses.rs b/substrate/client/network/src/request_responses.rs index 3671d76ea630b1a83808a2a1ce3b2fa5d3ce4152..6c2631924df4a96f0067fe53e8e1c68520329204 100644 --- a/substrate/client/network/src/request_responses.rs +++ b/substrate/client/network/src/request_responses.rs @@ -64,7 +64,69 @@ use std::{ time::{Duration, Instant}, }; -pub use libp2p::request_response::{Config, InboundFailure, OutboundFailure, RequestId}; +pub use libp2p::request_response::{Config, RequestId}; + +/// Possible failures occurring in the context of sending an outbound request and receiving the +/// response. +#[derive(Debug, thiserror::Error)] +pub enum OutboundFailure { + /// The request could not be sent because a dialing attempt failed. + #[error("Failed to dial the requested peer")] + DialFailure, + /// The request timed out before a response was received. + #[error("Timeout while waiting for a response")] + Timeout, + /// The connection closed before a response was received. + #[error("Connection was closed before a response was received")] + ConnectionClosed, + /// The remote supports none of the requested protocols. + #[error("The remote supports none of the requested protocols")] + UnsupportedProtocols, +} + +impl From for OutboundFailure { + fn from(out: request_response::OutboundFailure) -> Self { + match out { + request_response::OutboundFailure::DialFailure => OutboundFailure::DialFailure, + request_response::OutboundFailure::Timeout => OutboundFailure::Timeout, + request_response::OutboundFailure::ConnectionClosed => + OutboundFailure::ConnectionClosed, + request_response::OutboundFailure::UnsupportedProtocols => + OutboundFailure::UnsupportedProtocols, + } + } +} + +/// Possible failures occurring in the context of receiving an inbound request and sending a +/// response. +#[derive(Debug, thiserror::Error)] +pub enum InboundFailure { + /// The inbound request timed out, either while reading the incoming request or before a + /// response is sent + #[error("Timeout while receiving request or sending response")] + Timeout, + /// The connection closed before a response could be send. + #[error("Connection was closed before a response could be sent")] + ConnectionClosed, + /// The local peer supports none of the protocols requested by the remote. + #[error("The local peer supports none of the protocols requested by the remote")] + UnsupportedProtocols, + /// The local peer failed to respond to an inbound request + #[error("The response channel was dropped without sending a response to the remote")] + ResponseOmission, +} + +impl From for InboundFailure { + fn from(out: request_response::InboundFailure) -> Self { + match out { + request_response::InboundFailure::ResponseOmission => InboundFailure::ResponseOmission, + request_response::InboundFailure::Timeout => InboundFailure::Timeout, + request_response::InboundFailure::ConnectionClosed => InboundFailure::ConnectionClosed, + request_response::InboundFailure::UnsupportedProtocols => + InboundFailure::UnsupportedProtocols, + } + } +} /// Error in a request. #[derive(Debug, thiserror::Error)] @@ -808,7 +870,9 @@ impl NetworkBehaviour for RequestResponsesBehaviour { }) => { // Try using the fallback request if the protocol was not // supported. - if let OutboundFailure::UnsupportedProtocols = error { + if let request_response::OutboundFailure::UnsupportedProtocols = + error + { if let Some((fallback_request, fallback_protocol)) = fallback_request { @@ -829,7 +893,7 @@ impl NetworkBehaviour for RequestResponsesBehaviour { } if response_tx - .send(Err(RequestFailure::Network(error.clone()))) + .send(Err(RequestFailure::Network(error.clone().into()))) .is_err() { log::debug!( @@ -856,7 +920,7 @@ impl NetworkBehaviour for RequestResponsesBehaviour { peer, protocol: protocol.clone(), duration: started.elapsed(), - result: Err(RequestFailure::Network(error)), + result: Err(RequestFailure::Network(error.into())), }; return Poll::Ready(ToSwarm::GenerateEvent(out)) @@ -873,7 +937,7 @@ impl NetworkBehaviour for RequestResponsesBehaviour { let out = Event::InboundRequest { peer, protocol: protocol.clone(), - result: Err(ResponseFailure::Network(error)), + result: Err(ResponseFailure::Network(error.into())), }; return Poll::Ready(ToSwarm::GenerateEvent(out)) }, diff --git a/substrate/client/network/src/service/metrics.rs b/substrate/client/network/src/service/metrics.rs index 202dc7b2ed695551329419d09e5c3ae47964e299..e48c53953ff813eaac02c088557a64185554e551 100644 --- a/substrate/client/network/src/service/metrics.rs +++ b/substrate/client/network/src/service/metrics.rs @@ -72,7 +72,6 @@ pub struct Metrics { pub distinct_peers_connections_opened_total: Counter, pub incoming_connections_errors_total: CounterVec, pub incoming_connections_total: Counter, - pub issued_light_requests: Counter, pub kademlia_query_duration: HistogramVec, pub kademlia_random_queries_total: Counter, pub kademlia_records_count: Gauge, @@ -126,10 +125,6 @@ impl Metrics { "substrate_sub_libp2p_incoming_connections_total", "Total number of incoming connections on the listening sockets" )?, registry)?, - issued_light_requests: prometheus::register(Counter::new( - "substrate_issued_light_requests", - "Number of light client requests that our node has issued.", - )?, registry)?, kademlia_query_duration: prometheus::register(HistogramVec::new( HistogramOpts { common_opts: Opts::new( diff --git a/substrate/client/network/sync/Cargo.toml b/substrate/client/network/sync/Cargo.toml index b29a9ccaaf1a226bdb852d34a7faecbf45ec5de2..378b7c12e9b75f46c798f5f1cc5da1c571db3d0d 100644 --- a/substrate/client/network/sync/Cargo.toml +++ b/substrate/client/network/sync/Cargo.toml @@ -25,7 +25,6 @@ async-trait = { workspace = true } codec = { features = ["derive"], workspace = true, default-features = true } futures = { workspace = true } futures-timer = { workspace = true } -libp2p = { workspace = true } log = { workspace = true, default-features = true } mockall = { workspace = true } prost = { workspace = true } diff --git a/substrate/client/network/sync/src/block_request_handler.rs b/substrate/client/network/sync/src/block_request_handler.rs index 5aa374057a4a231144d39fc2c48818e906b5a245..6e970b3993106958a2b2bafbd28ae0bc11afad2f 100644 --- a/substrate/client/network/sync/src/block_request_handler.rs +++ b/substrate/client/network/sync/src/block_request_handler.rs @@ -39,7 +39,7 @@ use sc_network::{ request_responses::{IfDisconnected, IncomingRequest, OutgoingResponse, RequestFailure}, service::traits::RequestResponseConfig, types::ProtocolName, - NetworkBackend, + NetworkBackend, MAX_RESPONSE_SIZE, }; use sc_network_common::sync::message::{BlockAttributes, BlockData, BlockRequest, FromBlock}; use sc_network_types::PeerId; @@ -89,7 +89,7 @@ pub fn generate_protocol_config< generate_protocol_name(genesis_hash, fork_id).into(), std::iter::once(generate_legacy_protocol_name(protocol_id).into()).collect(), 1024 * 1024, - 16 * 1024 * 1024, + MAX_RESPONSE_SIZE, Duration::from_secs(20), Some(inbound_queue), ) diff --git a/substrate/client/network/sync/src/engine.rs b/substrate/client/network/sync/src/engine.rs index 4b6ccb085834ffa3478efe975a0320487799dc97..dceea9954c6e9ff696d9ee01098713857984ed41 100644 --- a/substrate/client/network/sync/src/engine.rs +++ b/substrate/client/network/sync/src/engine.rs @@ -24,7 +24,6 @@ use crate::{ BlockAnnounceValidationResult, BlockAnnounceValidator as BlockAnnounceValidatorStream, }, block_relay_protocol::{BlockDownloader, BlockResponseError}, - block_request_handler::MAX_BLOCKS_IN_RESPONSE, pending_responses::{PendingResponses, ResponseEvent}, schema::v1::{StateRequest, StateResponse}, service::{ @@ -32,8 +31,8 @@ use crate::{ syncing_service::{SyncingService, ToServiceCommand}, }, strategy::{ - warp::{EncodedProof, WarpProofRequest, WarpSyncConfig}, - StrategyKey, SyncingAction, SyncingConfig, SyncingStrategy, + warp::{EncodedProof, WarpProofRequest}, + StrategyKey, SyncingAction, SyncingStrategy, }, types::{ BadPeer, ExtendedPeerInfo, OpaqueStateRequest, OpaqueStateResponse, PeerRequest, SyncEvent, @@ -43,7 +42,6 @@ use crate::{ use codec::{Decode, DecodeAll, Encode}; use futures::{channel::oneshot, FutureExt, StreamExt}; -use libp2p::request_response::OutboundFailure; use log::{debug, error, trace, warn}; use prometheus_endpoint::{ register, Counter, Gauge, MetricSource, Opts, PrometheusError, Registry, SourcedGauge, U64, @@ -57,7 +55,7 @@ use sc_consensus::{import_queue::ImportQueueService, IncomingBlock}; use sc_network::{ config::{FullNetworkConfiguration, NotificationHandshake, ProtocolId, SetConfig}, peer_store::PeerStoreProvider, - request_responses::{IfDisconnected, RequestFailure}, + request_responses::{IfDisconnected, OutboundFailure, RequestFailure}, service::{ traits::{Direction, NotificationConfig, NotificationEvent, ValidationResult}, NotificationMetrics, @@ -189,7 +187,7 @@ pub struct Peer { pub struct SyncingEngine { /// Syncing strategy. - strategy: SyncingStrategy, + strategy: Box>, /// Blockchain client. client: Arc, @@ -271,12 +269,6 @@ pub struct SyncingEngine { /// Block downloader block_downloader: Arc>, - /// Protocol name used to send out state requests - state_request_protocol_name: ProtocolName, - - /// Protocol name used to send out warp sync requests - warp_sync_protocol_name: Option, - /// Handle to import queue. import_queue: Box>, } @@ -301,35 +293,15 @@ where protocol_id: ProtocolId, fork_id: &Option, block_announce_validator: Box + Send>, - warp_sync_config: Option>, + syncing_strategy: Box>, network_service: service::network::NetworkServiceHandle, import_queue: Box>, block_downloader: Arc>, - state_request_protocol_name: ProtocolName, - warp_sync_protocol_name: Option, peer_store_handle: Arc, ) -> Result<(Self, SyncingService, N::NotificationProtocolConfig), ClientError> where N: NetworkBackend::Hash>, { - let mode = net_config.network_config.sync_mode; - let max_parallel_downloads = net_config.network_config.max_parallel_downloads; - let max_blocks_per_request = - if net_config.network_config.max_blocks_per_request > MAX_BLOCKS_IN_RESPONSE as u32 { - log::info!( - target: LOG_TARGET, - "clamping maximum blocks per request to {MAX_BLOCKS_IN_RESPONSE}", - ); - MAX_BLOCKS_IN_RESPONSE as u32 - } else { - net_config.network_config.max_blocks_per_request - }; - let syncing_config = SyncingConfig { - mode, - max_parallel_downloads, - max_blocks_per_request, - metrics_registry: metrics_registry.cloned(), - }; let cache_capacity = (net_config.network_config.default_peers_set.in_peers + net_config.network_config.default_peers_set.out_peers) .max(1); @@ -388,9 +360,6 @@ where Arc::clone(&peer_store_handle), ); - // Initialize syncing strategy. - 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); let num_connected = Arc::new(AtomicUsize::new(0)); @@ -412,7 +381,7 @@ where Self { roles, client, - strategy, + strategy: syncing_strategy, network_service, peers: HashMap::new(), block_announce_data_cache: LruMap::new(ByLength::new(cache_capacity)), @@ -449,8 +418,6 @@ where }, pending_responses: PendingResponses::new(), block_downloader, - state_request_protocol_name, - warp_sync_protocol_name, import_queue, }, SyncingService::new(tx, num_connected, is_major_syncing), @@ -651,16 +618,16 @@ where "Processed {action:?}, response removed: {removed}.", ); }, - SyncingAction::SendStateRequest { peer_id, key, request } => { - self.send_state_request(peer_id, key, request); + SyncingAction::SendStateRequest { peer_id, key, protocol_name, request } => { + self.send_state_request(peer_id, key, protocol_name, request); trace!( target: LOG_TARGET, "Processed `ChainSyncAction::SendStateRequest` to {peer_id}.", ); }, - SyncingAction::SendWarpProofRequest { peer_id, key, request } => { - self.send_warp_proof_request(peer_id, key, request.clone()); + SyncingAction::SendWarpProofRequest { peer_id, key, protocol_name, request } => { + self.send_warp_proof_request(peer_id, key, protocol_name, request.clone()); trace!( target: LOG_TARGET, @@ -697,7 +664,7 @@ where number, ) }, - // Nothing to do, this is handled internally by `SyncingStrategy`. + // Nothing to do, this is handled internally by `PolkadotSyncingStrategy`. SyncingAction::Finished => {}, } } @@ -845,7 +812,8 @@ where } if !self.default_peers_set_no_slot_connected_peers.remove(&peer_id) && - info.inbound && info.info.roles.is_full() + info.inbound && + info.info.roles.is_full() { match self.num_in_peers.checked_sub(1) { Some(value) => { @@ -1053,6 +1021,7 @@ where &mut self, peer_id: PeerId, key: StrategyKey, + protocol_name: ProtocolName, request: OpaqueStateRequest, ) { if !self.peers.contains_key(&peer_id) { @@ -1069,7 +1038,7 @@ where Ok(data) => { self.network_service.start_request( peer_id, - self.state_request_protocol_name.clone(), + protocol_name, data, tx, IfDisconnected::ImmediateError, @@ -1088,6 +1057,7 @@ where &mut self, peer_id: PeerId, key: StrategyKey, + protocol_name: ProtocolName, request: WarpProofRequest, ) { if !self.peers.contains_key(&peer_id) { @@ -1100,21 +1070,13 @@ where self.pending_responses.insert(peer_id, key, PeerRequest::WarpProof, rx.boxed()); - match &self.warp_sync_protocol_name { - Some(name) => self.network_service.start_request( - peer_id, - name.clone(), - request.encode(), - tx, - IfDisconnected::ImmediateError, - ), - None => { - log::warn!( - target: LOG_TARGET, - "Trying to send warp sync request when no protocol is configured {request:?}", - ); - }, - } + self.network_service.start_request( + peer_id, + protocol_name, + request.encode(), + tx, + IfDisconnected::ImmediateError, + ); } fn encode_state_request(request: &OpaqueStateRequest) -> Result, String> { diff --git a/substrate/client/network/sync/src/lib.rs b/substrate/client/network/sync/src/lib.rs index ca7280edba5f5df5ec9be586f2048a5638604ae4..c458c7a5da498bbec42db83b5df210eda9fe5d70 100644 --- a/substrate/client/network/sync/src/lib.rs +++ b/substrate/client/network/sync/src/lib.rs @@ -26,7 +26,6 @@ mod block_announce_validator; mod futures_stream; mod justification_requests; mod pending_responses; -mod request_metrics; mod schema; pub mod types; diff --git a/substrate/client/network/sync/src/request_metrics.rs b/substrate/client/network/sync/src/request_metrics.rs deleted file mode 100644 index 455f57ec3933992cb2e84ba35ce52b7ea3d350e8..0000000000000000000000000000000000000000 --- a/substrate/client/network/sync/src/request_metrics.rs +++ /dev/null @@ -1,25 +0,0 @@ -// 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 . - -#[derive(Debug)] -pub struct Metrics { - pub pending_requests: u32, - pub active_requests: u32, - pub importing_requests: u32, - pub failed_requests: u32, -} diff --git a/substrate/client/network/sync/src/state_request_handler.rs b/substrate/client/network/sync/src/state_request_handler.rs index 0e713626ecaa3433d371ac762a62def0f0d6af03..36a15f1f42400e5b344e8ae060bd1b10ed5fd090 100644 --- a/substrate/client/network/sync/src/state_request_handler.rs +++ b/substrate/client/network/sync/src/state_request_handler.rs @@ -33,7 +33,7 @@ use sc_client_api::{BlockBackend, ProofProvider}; use sc_network::{ config::ProtocolId, request_responses::{IncomingRequest, OutgoingResponse}, - NetworkBackend, + NetworkBackend, MAX_RESPONSE_SIZE, }; use sp_runtime::traits::Block as BlockT; @@ -69,7 +69,7 @@ pub fn generate_protocol_config< generate_protocol_name(genesis_hash, fork_id).into(), std::iter::once(generate_legacy_protocol_name(protocol_id).into()).collect(), 1024 * 1024, - 16 * 1024 * 1024, + MAX_RESPONSE_SIZE, Duration::from_secs(40), Some(inbound_queue), ) diff --git a/substrate/client/network/sync/src/strategy.rs b/substrate/client/network/sync/src/strategy.rs index ad3a9461c93b8bc518e7d69ecfef45e44fde3f1d..81998b7576bbfb807fbfc8c8e1d79ff087ff8906 100644 --- a/substrate/client/network/sync/src/strategy.rs +++ b/substrate/client/network/sync/src/strategy.rs @@ -16,7 +16,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -//! [`SyncingStrategy`] is a proxy between [`crate::engine::SyncingEngine`] +//! [`PolkadotSyncingStrategy`] is a proxy between [`crate::engine::SyncingEngine`] //! and specific syncing algorithms. pub mod chain_sync; @@ -26,14 +26,16 @@ pub mod state_sync; pub mod warp; use crate::{ + block_request_handler::MAX_BLOCKS_IN_RESPONSE, types::{BadPeer, OpaqueStateRequest, OpaqueStateResponse, SyncStatus}, LOG_TARGET, }; -use chain_sync::{ChainSync, ChainSyncAction, ChainSyncMode}; +use chain_sync::{ChainSync, ChainSyncMode}; use log::{debug, error, info}; use prometheus_endpoint::Registry; use sc_client_api::{BlockBackend, ProofProvider}; use sc_consensus::{BlockImportError, BlockImportStatus, IncomingBlock}; +use sc_network::ProtocolName; use sc_network_common::sync::{ message::{BlockAnnounce, BlockData, BlockRequest}, SyncMode, @@ -59,6 +61,108 @@ fn chain_sync_mode(sync_mode: SyncMode) -> ChainSyncMode { } } +/// Syncing strategy for syncing engine to use +pub trait SyncingStrategy: Send +where + B: BlockT, +{ + /// Notify syncing state machine that a new sync peer has connected. + fn add_peer(&mut self, peer_id: PeerId, best_hash: B::Hash, best_number: NumberFor); + + /// Notify that a sync peer has disconnected. + fn remove_peer(&mut self, peer_id: &PeerId); + + /// Submit a validated block announcement. + /// + /// Returns new best hash & best number of the peer if they are updated. + #[must_use] + fn on_validated_block_announce( + &mut self, + is_best: bool, + peer_id: PeerId, + announce: &BlockAnnounce, + ) -> Option<(B::Hash, NumberFor)>; + + /// Configure an explicit fork sync request in case external code has detected that there is a + /// stale fork missing. + /// + /// Note that this function should not be used for recent blocks. + /// Sync should be able to download all the recent forks normally. + /// + /// Passing empty `peers` set effectively removes the sync request. + fn set_sync_fork_request(&mut self, peers: Vec, hash: &B::Hash, number: NumberFor); + + /// Request extra justification. + fn request_justification(&mut self, hash: &B::Hash, number: NumberFor); + + /// Clear extra justification requests. + fn clear_justification_requests(&mut self); + + /// Report a justification import (successful or not). + fn on_justification_import(&mut self, hash: B::Hash, number: NumberFor, success: bool); + + /// Process block response. + fn on_block_response( + &mut self, + peer_id: PeerId, + key: StrategyKey, + request: BlockRequest, + blocks: Vec>, + ); + + /// Process state response. + fn on_state_response( + &mut self, + peer_id: PeerId, + key: StrategyKey, + response: OpaqueStateResponse, + ); + + /// Process warp proof response. + fn on_warp_proof_response( + &mut self, + peer_id: &PeerId, + key: StrategyKey, + response: EncodedProof, + ); + + /// A batch of blocks that have been processed, with or without errors. + /// + /// Call this when a batch of blocks that have been processed by the import queue, with or + /// without errors. + fn on_blocks_processed( + &mut self, + imported: usize, + count: usize, + results: Vec<(Result>, BlockImportError>, B::Hash)>, + ); + + /// Notify a syncing strategy that a block has been finalized. + fn on_block_finalized(&mut self, hash: &B::Hash, number: NumberFor); + + /// Inform sync about a new best imported block. + fn update_chain_info(&mut self, best_hash: &B::Hash, best_number: NumberFor); + + // Are we in major sync mode? + fn is_major_syncing(&self) -> bool; + + /// Get the number of peers known to the syncing strategy. + fn num_peers(&self) -> usize; + + /// Returns the current sync status. + fn status(&self) -> SyncStatus; + + /// Get the total number of downloaded blocks. + fn num_downloaded_blocks(&self) -> usize; + + /// Get an estimate of the number of parallel sync requests. + fn num_sync_requests(&self) -> usize; + + /// Get actions that should be performed by the owner on the strategy's behalf + #[must_use] + fn actions(&mut self) -> Result>, ClientError>; +} + /// Syncing configuration containing data for all strategies. #[derive(Clone, Debug)] pub struct SyncingConfig { @@ -70,6 +174,8 @@ pub struct SyncingConfig { pub max_blocks_per_request: u32, /// Prometheus metrics registry. pub metrics_registry: Option, + /// Protocol name used to send out state requests + pub state_request_protocol_name: ProtocolName, } /// The key identifying a specific strategy for responses routing. @@ -88,9 +194,19 @@ pub enum SyncingAction { /// Send block request to peer. Always implies dropping a stale block request to the same peer. SendBlockRequest { peer_id: PeerId, key: StrategyKey, request: BlockRequest }, /// Send state request to peer. - SendStateRequest { peer_id: PeerId, key: StrategyKey, request: OpaqueStateRequest }, + SendStateRequest { + peer_id: PeerId, + key: StrategyKey, + protocol_name: ProtocolName, + request: OpaqueStateRequest, + }, /// Send warp proof request to peer. - SendWarpProofRequest { peer_id: PeerId, key: StrategyKey, request: WarpProofRequest }, + SendWarpProofRequest { + peer_id: PeerId, + key: StrategyKey, + protocol_name: ProtocolName, + request: WarpProofRequest, + }, /// Drop stale request. CancelRequest { peer_id: PeerId, key: StrategyKey }, /// Peer misbehaved. Disconnect, report it and cancel any requests to it. @@ -104,7 +220,7 @@ pub enum SyncingAction { number: NumberFor, justifications: Justifications, }, - /// Strategy finished. Nothing to do, this is handled by `SyncingStrategy`. + /// Strategy finished. Nothing to do, this is handled by `PolkadotSyncingStrategy`. Finished, } @@ -117,8 +233,13 @@ impl SyncingAction { 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::SendWarpProofRequest { peer_id, protocol_name, request } => + SyncingAction::SendWarpProofRequest { + peer_id, + key: StrategyKey::Warp, + protocol_name, + request, + }, WarpSyncAction::SendBlockRequest { peer_id, request } => SyncingAction::SendBlockRequest { peer_id, key: StrategyKey::Warp, request }, WarpSyncAction::DropPeer(bad_peer) => SyncingAction::DropPeer(bad_peer), @@ -130,8 +251,13 @@ impl From> for SyncingAction { 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::SendStateRequest { peer_id, protocol_name, request } => + SyncingAction::SendStateRequest { + peer_id, + key: StrategyKey::State, + protocol_name, + request, + }, StateStrategyAction::DropPeer(bad_peer) => SyncingAction::DropPeer(bad_peer), StateStrategyAction::ImportBlocks { origin, blocks } => SyncingAction::ImportBlocks { origin, blocks }, @@ -140,26 +266,8 @@ impl From> for SyncingAction { } } -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 struct SyncingStrategy { +/// Proxy to specific syncing strategies used in Polkadot. +pub struct PolkadotSyncingStrategy { /// Initial syncing configuration. config: SyncingConfig, /// Client used by syncing strategies. @@ -171,11 +279,11 @@ pub struct SyncingStrategy { /// `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`]. + /// `PolkadotSyncingStrategy::proceed_to_next`. peer_best_blocks: HashMap)>, } -impl SyncingStrategy +impl SyncingStrategy for PolkadotSyncingStrategy where B: BlockT, Client: HeaderBackend @@ -186,46 +294,7 @@ where + Sync + 'static, { - /// Initialize a new syncing strategy. - pub fn new( - config: SyncingConfig, - client: Arc, - warp_sync_config: Option>, - ) -> Result { - if let SyncMode::Warp = config.mode { - let warp_sync_config = warp_sync_config - .expect("Warp sync configuration must be supplied in warp sync mode."); - 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 { - let chain_sync = ChainSync::new( - chain_sync_mode(config.mode), - client.clone(), - config.max_parallel_downloads, - config.max_blocks_per_request, - config.metrics_registry.as_ref(), - 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) { + fn add_peer(&mut self, peer_id: PeerId, best_hash: B::Hash, best_number: NumberFor) { 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)); @@ -233,8 +302,7 @@ where 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) { + fn remove_peer(&mut self, peer_id: &PeerId) { 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)); @@ -242,10 +310,7 @@ where self.peer_best_blocks.remove(peer_id); } - /// Submit a validated block announcement. - /// - /// Returns new best hash & best number of the peer if they are updated. - pub fn on_validated_block_announce( + fn on_validated_block_announce( &mut self, is_best: bool, peer_id: PeerId, @@ -278,46 +343,35 @@ where new_best } - /// Configure an explicit fork sync request in case external code has detected that there is a - /// stale fork missing. - pub fn set_sync_fork_request( - &mut self, - peers: Vec, - hash: &B::Hash, - number: NumberFor, - ) { + fn set_sync_fork_request(&mut self, peers: Vec, hash: &B::Hash, number: NumberFor) { // 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) { + fn request_justification(&mut self, hash: &B::Hash, number: NumberFor) { // 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) { + fn clear_justification_requests(&mut self) { // 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) { + fn on_justification_import(&mut self, hash: B::Hash, number: NumberFor, success: bool) { // 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); } } - /// Process block response. - pub fn on_block_response( + fn on_block_response( &mut self, peer_id: PeerId, key: StrategyKey, @@ -329,7 +383,7 @@ where } else if let (StrategyKey::ChainSync, Some(ref mut chain_sync)) = (key, &mut self.chain_sync) { - chain_sync.on_block_response(peer_id, request, blocks); + chain_sync.on_block_response(peer_id, key, request, blocks); } else { error!( target: LOG_TARGET, @@ -340,8 +394,7 @@ where } } - /// Process state response. - pub fn on_state_response( + fn on_state_response( &mut self, peer_id: PeerId, key: StrategyKey, @@ -352,7 +405,7 @@ where } else if let (StrategyKey::ChainSync, Some(ref mut chain_sync)) = (key, &mut self.chain_sync) { - chain_sync.on_state_response(peer_id, response); + chain_sync.on_state_response(peer_id, key, response); } else { error!( target: LOG_TARGET, @@ -363,8 +416,7 @@ where } } - /// Process warp proof response. - pub fn on_warp_proof_response( + fn on_warp_proof_response( &mut self, peer_id: &PeerId, key: StrategyKey, @@ -382,8 +434,7 @@ where } } - /// A batch of blocks have been processed, with or without errors. - pub fn on_blocks_processed( + fn on_blocks_processed( &mut self, imported: usize, count: usize, @@ -397,24 +448,21 @@ where } } - /// Notify a syncing strategy that a block has been finalized. - pub fn on_block_finalized(&mut self, hash: &B::Hash, number: NumberFor) { + fn on_block_finalized(&mut self, hash: &B::Hash, number: NumberFor) { // 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) { + fn update_chain_info(&mut self, best_hash: &B::Hash, best_number: NumberFor) { // 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 { + fn is_major_syncing(&self) -> bool { self.warp.is_some() || self.state.is_some() || match self.chain_sync { @@ -423,13 +471,11 @@ where } } - /// Get the number of peers known to the syncing strategy. - pub fn num_peers(&self) -> usize { + fn num_peers(&self) -> usize { self.peer_best_blocks.len() } - /// Returns the current sync status. - pub fn status(&self) -> SyncStatus { + fn status(&self) -> SyncStatus { // This function presumes that strategies are executed serially and must be refactored // once we have parallel strategies. if let Some(ref warp) = self.warp { @@ -443,21 +489,17 @@ where } } - /// Get the total number of downloaded blocks. - pub fn num_downloaded_blocks(&self) -> usize { + fn num_downloaded_blocks(&self) -> usize { 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 { + fn num_sync_requests(&self) -> usize { self.chain_sync.as_ref().map_or(0, |chain_sync| chain_sync.num_sync_requests()) } - /// Get actions that should be performed by the owner on the strategy's behalf - #[must_use] - pub fn actions(&mut self) -> Result>, ClientError> { + 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 { @@ -465,7 +507,7 @@ where } 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() + chain_sync.actions()? } else { unreachable!("At least one syncing strategy is always active; qed") }; @@ -476,6 +518,67 @@ where Ok(actions) } +} + +impl PolkadotSyncingStrategy +where + B: BlockT, + Client: HeaderBackend + + BlockBackend + + HeaderMetadata + + ProofProvider + + Send + + Sync + + 'static, +{ + /// Initialize a new syncing strategy. + pub fn new( + mut config: SyncingConfig, + client: Arc, + warp_sync_config: Option>, + warp_sync_protocol_name: Option, + ) -> Result { + if config.max_blocks_per_request > MAX_BLOCKS_IN_RESPONSE as u32 { + info!( + target: LOG_TARGET, + "clamping maximum blocks per request to {MAX_BLOCKS_IN_RESPONSE}", + ); + config.max_blocks_per_request = MAX_BLOCKS_IN_RESPONSE as u32; + } + + if let SyncMode::Warp = config.mode { + let warp_sync_config = warp_sync_config + .expect("Warp sync configuration must be supplied in warp sync mode."); + let warp_sync = + WarpSync::new(client.clone(), warp_sync_config, warp_sync_protocol_name); + Ok(Self { + config, + client, + warp: Some(warp_sync), + state: None, + chain_sync: None, + peer_best_blocks: Default::default(), + }) + } else { + let chain_sync = ChainSync::new( + chain_sync_mode(config.mode), + client.clone(), + config.max_parallel_downloads, + config.max_blocks_per_request, + config.state_request_protocol_name.clone(), + config.metrics_registry.as_ref(), + std::iter::empty(), + )?; + Ok(Self { + config, + client, + warp: None, + state: None, + chain_sync: Some(chain_sync), + peer_best_blocks: Default::default(), + }) + } + } /// Proceed with the next strategy if the active one finished. pub fn proceed_to_next(&mut self) -> Result<(), ClientError> { @@ -496,6 +599,7 @@ where self.peer_best_blocks .iter() .map(|(peer_id, (_, best_number))| (*peer_id, *best_number)), + self.config.state_request_protocol_name.clone(), ); self.warp = None; @@ -512,6 +616,7 @@ where self.client.clone(), self.config.max_parallel_downloads, self.config.max_blocks_per_request, + self.config.state_request_protocol_name.clone(), self.config.metrics_registry.as_ref(), self.peer_best_blocks.iter().map(|(peer_id, (best_hash, best_number))| { (*peer_id, *best_hash, *best_number) @@ -540,6 +645,7 @@ where self.client.clone(), self.config.max_parallel_downloads, self.config.max_blocks_per_request, + self.config.state_request_protocol_name.clone(), self.config.metrics_registry.as_ref(), self.peer_best_blocks.iter().map(|(peer_id, (best_hash, best_number))| { (*peer_id, *best_hash, *best_number) diff --git a/substrate/client/network/sync/src/strategy/chain_sync.rs b/substrate/client/network/sync/src/strategy/chain_sync.rs index 21e47404862575acddc4b3204a5c4d38d8da4814..b0e28d00f64ac5608e395939d8e51b72898b1a9c 100644 --- a/substrate/client/network/sync/src/strategy/chain_sync.rs +++ b/substrate/client/network/sync/src/strategy/chain_sync.rs @@ -35,7 +35,8 @@ use crate::{ strategy::{ disconnected_peers::DisconnectedPeers, state_sync::{ImportResult, StateSync, StateSyncProvider}, - warp::{WarpSyncPhase, WarpSyncProgress}, + warp::{EncodedProof, WarpSyncPhase, WarpSyncProgress}, + StrategyKey, SyncingAction, SyncingStrategy, }, types::{BadPeer, OpaqueStateRequest, OpaqueStateResponse, SyncState, SyncStatus}, LOG_TARGET, @@ -44,8 +45,9 @@ use crate::{ use codec::Encode; use log::{debug, error, info, trace, warn}; use prometheus_endpoint::{register, Gauge, PrometheusError, Registry, U64}; -use sc_client_api::{BlockBackend, ProofProvider}; +use sc_client_api::{blockchain::BlockGap, BlockBackend, ProofProvider}; use sc_consensus::{BlockImportError, BlockImportStatus, IncomingBlock}; +use sc_network::ProtocolName; use sc_network_common::sync::message::{ BlockAnnounce, BlockAttributes, BlockData, BlockRequest, BlockResponse, Direction, FromBlock, }; @@ -146,6 +148,7 @@ impl Metrics { } } +#[derive(Debug, Clone)] enum AllowedRequests { Some(HashSet), All, @@ -197,28 +200,6 @@ struct GapSync { target: NumberFor, } -/// Action that the parent of [`ChainSync`] should perform after reporting a network or block event. -#[derive(Debug)] -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 }, - /// 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. - ImportBlocks { origin: BlockOrigin, blocks: Vec> }, - /// Import justifications. - ImportJustifications { - peer_id: PeerId, - hash: B::Hash, - number: NumberFor, - justifications: Justifications, - }, -} - /// Sync operation mode. #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum ChainSyncMode { @@ -233,50 +214,6 @@ pub enum ChainSyncMode { }, } -/// The main data structure which contains all the state for a chains -/// active syncing strategy. -pub struct ChainSync { - /// Chain client. - client: Arc, - /// The active peers that we are using to sync and their PeerSync status - peers: HashMap>, - disconnected_peers: DisconnectedPeers, - /// A `BlockCollection` of blocks that are being downloaded from peers - blocks: BlockCollection, - /// The best block number in our queue of blocks to import - best_queued_number: NumberFor, - /// The best block hash in our queue of blocks to import - best_queued_hash: B::Hash, - /// Current mode (full/light) - mode: ChainSyncMode, - /// Any extra justification requests. - extra_justifications: ExtraRequests, - /// A set of hashes of blocks that are being downloaded or have been - /// downloaded and are queued for import. - queue_blocks: HashSet, - /// Fork sync targets. - fork_targets: HashMap>, - /// A set of peers for which there might be potential block requests - allowed_requests: AllowedRequests, - /// Maximum number of peers to ask the same blocks in parallel. - max_parallel_downloads: u32, - /// Maximum blocks per request. - max_blocks_per_request: u32, - /// Total number of downloaded blocks. - downloaded_blocks: usize, - /// State sync in progress, if any. - state_sync: Option>, - /// Enable importing existing blocks. This is used used after the state download to - /// catch up to the latest state while re-importing blocks. - import_existing: bool, - /// Gap download process. - gap_sync: Option>, - /// Pending actions. - actions: Vec>, - /// Prometheus metrics. - metrics: Option, -} - /// All the data we have about a Peer that we are trying to sync with #[derive(Debug, Clone)] pub(crate) struct PeerSync { @@ -346,7 +283,61 @@ impl PeerSyncState { } } -impl ChainSync +/// The main data structure which contains all the state for a chains +/// active syncing strategy. +pub struct ChainSync { + /// Chain client. + client: Arc, + /// The active peers that we are using to sync and their PeerSync status + peers: HashMap>, + disconnected_peers: DisconnectedPeers, + /// A `BlockCollection` of blocks that are being downloaded from peers + blocks: BlockCollection, + /// The best block number in our queue of blocks to import + best_queued_number: NumberFor, + /// The best block hash in our queue of blocks to import + best_queued_hash: B::Hash, + /// Current mode (full/light) + mode: ChainSyncMode, + /// Any extra justification requests. + extra_justifications: ExtraRequests, + /// A set of hashes of blocks that are being downloaded or have been + /// downloaded and are queued for import. + queue_blocks: HashSet, + /// A pending attempt to start the state sync. + /// + /// The initiation of state sync may be deferred in cases where other conditions + /// are not yet met when the finalized block notification is received, such as + /// when `queue_blocks` is not empty or there are no peers. This field holds the + /// necessary information to attempt the state sync at a later point when + /// conditions are satisfied. + pending_state_sync_attempt: Option<(B::Hash, NumberFor, bool)>, + /// Fork sync targets. + fork_targets: HashMap>, + /// A set of peers for which there might be potential block requests + allowed_requests: AllowedRequests, + /// Maximum number of peers to ask the same blocks in parallel. + max_parallel_downloads: u32, + /// Maximum blocks per request. + max_blocks_per_request: u32, + /// Protocol name used to send out state requests + state_request_protocol_name: ProtocolName, + /// Total number of downloaded blocks. + downloaded_blocks: usize, + /// State sync in progress, if any. + state_sync: Option>, + /// Enable importing existing blocks. This is used used after the state download to + /// catch up to the latest state while re-importing blocks. + import_existing: bool, + /// Gap download process. + gap_sync: Option>, + /// Pending actions. + actions: Vec>, + /// Prometheus metrics. + metrics: Option, +} + +impl SyncingStrategy for ChainSync where B: BlockT, Client: HeaderBackend @@ -357,119 +348,615 @@ where + Sync + 'static, { - /// Create a new instance. - pub fn new( - mode: ChainSyncMode, - client: Arc, - max_parallel_downloads: u32, - max_blocks_per_request: u32, - metrics_registry: Option<&Registry>, - initial_peers: impl Iterator)>, - ) -> Result { - let mut sync = Self { - client, - peers: HashMap::new(), - disconnected_peers: DisconnectedPeers::new(), - blocks: BlockCollection::new(), - best_queued_hash: Default::default(), - best_queued_number: Zero::zero(), - extra_justifications: ExtraRequests::new("justification", metrics_registry), - mode, - queue_blocks: Default::default(), - fork_targets: Default::default(), - allowed_requests: Default::default(), - max_parallel_downloads, - max_blocks_per_request, - downloaded_blocks: 0, - state_sync: None, - import_existing: false, - gap_sync: None, - actions: Vec::new(), - metrics: metrics_registry.and_then(|r| match Metrics::register(r) { - Ok(metrics) => Some(metrics), - Err(err) => { - log::error!( - target: LOG_TARGET, - "Failed to register `ChainSync` metrics {err:?}", - ); - None - }, + fn add_peer(&mut self, peer_id: PeerId, best_hash: B::Hash, best_number: NumberFor) { + match self.add_peer_inner(peer_id, best_hash, best_number) { + Ok(Some(request)) => self.actions.push(SyncingAction::SendBlockRequest { + peer_id, + key: StrategyKey::ChainSync, + request, }), - }; + Ok(None) => {}, + Err(bad_peer) => self.actions.push(SyncingAction::DropPeer(bad_peer)), + } + } - sync.reset_sync_start_point()?; - initial_peers.for_each(|(peer_id, best_hash, best_number)| { - sync.add_peer(peer_id, best_hash, best_number); + fn remove_peer(&mut self, peer_id: &PeerId) { + self.blocks.clear_peer_download(peer_id); + if let Some(gap_sync) = &mut self.gap_sync { + gap_sync.blocks.clear_peer_download(peer_id) + } + + if let Some(state) = self.peers.remove(peer_id) { + if !state.state.is_available() { + if let Some(bad_peer) = + self.disconnected_peers.on_disconnect_during_request(*peer_id) + { + self.actions.push(SyncingAction::DropPeer(bad_peer)); + } + } + } + + self.extra_justifications.peer_disconnected(peer_id); + self.allowed_requests.set_all(); + self.fork_targets.retain(|_, target| { + target.peers.remove(peer_id); + !target.peers.is_empty() }); + if let Some(metrics) = &self.metrics { + metrics.fork_targets.set(self.fork_targets.len().try_into().unwrap_or(u64::MAX)); + } - Ok(sync) + let blocks = self.ready_blocks(); + + if !blocks.is_empty() { + self.validate_and_queue_blocks(blocks, false); + } } - /// Returns the current sync status. - pub fn status(&self) -> SyncStatus { - let median_seen = self.median_seen(); - let best_seen_block = - median_seen.and_then(|median| (median > self.best_queued_number).then_some(median)); - let sync_state = if let Some(target) = median_seen { - // A chain is classified as downloading if the provided best block is - // more than `MAJOR_SYNC_BLOCKS` behind the best block or as importing - // if the same can be said about queued blocks. - let best_block = self.client.info().best_number; - if target > best_block && target - best_block > MAJOR_SYNC_BLOCKS.into() { - // If target is not queued, we're downloading, otherwise importing. - if target > self.best_queued_number { - SyncState::Downloading { target } - } else { - SyncState::Importing { target } - } - } else { - SyncState::Idle - } + fn on_validated_block_announce( + &mut self, + is_best: bool, + peer_id: PeerId, + announce: &BlockAnnounce, + ) -> Option<(B::Hash, NumberFor)> { + let number = *announce.header.number(); + let hash = announce.header.hash(); + let parent_status = + self.block_status(announce.header.parent_hash()).unwrap_or(BlockStatus::Unknown); + let known_parent = parent_status != BlockStatus::Unknown; + let ancient_parent = parent_status == BlockStatus::InChainPruned; + + let known = self.is_known(&hash); + let peer = if let Some(peer) = self.peers.get_mut(&peer_id) { + peer } else { - SyncState::Idle + error!(target: LOG_TARGET, "๐Ÿ’” Called `on_validated_block_announce` with a bad peer ID {peer_id}"); + return Some((hash, number)) }; - let warp_sync_progress = self.gap_sync.as_ref().map(|gap_sync| WarpSyncProgress { - phase: WarpSyncPhase::DownloadingBlocks(gap_sync.best_queued_number), - total_bytes: 0, + if let PeerSyncState::AncestorSearch { .. } = peer.state { + trace!(target: LOG_TARGET, "Peer {} is in the ancestor search state.", peer_id); + return None + } + + let peer_info = is_best.then(|| { + // update their best block + peer.best_number = number; + peer.best_hash = hash; + + (hash, number) }); - SyncStatus { - state: sync_state, - best_seen_block, - num_peers: self.peers.len() as u32, - queued_blocks: self.queue_blocks.len() as u32, - state_sync: self.state_sync.as_ref().map(|s| s.progress()), - warp_sync: warp_sync_progress, + // If the announced block is the best they have and is not ahead of us, our common number + // is either one further ahead or it's the one they just announced, if we know about it. + if is_best { + if known && self.best_queued_number >= number { + self.update_peer_common_number(&peer_id, number); + } else if announce.header.parent_hash() == &self.best_queued_hash || + known_parent && self.best_queued_number >= number + { + self.update_peer_common_number(&peer_id, number.saturating_sub(One::one())); + } } - } + self.allowed_requests.add(&peer_id); - /// Get an estimate of the number of parallel sync requests. - pub fn num_sync_requests(&self) -> usize { - self.fork_targets - .values() - .filter(|f| f.number <= self.best_queued_number) - .count() - } + // known block case + if known || self.is_already_downloading(&hash) { + trace!(target: LOG_TARGET, "Known block announce from {}: {}", peer_id, hash); + if let Some(target) = self.fork_targets.get_mut(&hash) { + target.peers.insert(peer_id); + } + return peer_info + } - /// Get the total number of downloaded blocks. - pub fn num_downloaded_blocks(&self) -> usize { - self.downloaded_blocks - } + if ancient_parent { + trace!( + target: LOG_TARGET, + "Ignored ancient block announced from {}: {} {:?}", + peer_id, + hash, + announce.header, + ); + return peer_info + } + + if self.status().state == SyncState::Idle { + trace!( + target: LOG_TARGET, + "Added sync target for block announced from {}: {} {:?}", + peer_id, + hash, + announce.summary(), + ); + self.fork_targets + .entry(hash) + .or_insert_with(|| { + if let Some(metrics) = &self.metrics { + metrics.fork_targets.inc(); + } + + ForkTarget { + number, + parent_hash: Some(*announce.header.parent_hash()), + peers: Default::default(), + } + }) + .peers + .insert(peer_id); + } + + peer_info + } + + // The implementation is similar to `on_validated_block_announce` with unknown parent hash. + fn set_sync_fork_request( + &mut self, + mut peers: Vec, + hash: &B::Hash, + number: NumberFor, + ) { + if peers.is_empty() { + peers = self + .peers + .iter() + // Only request blocks from peers who are ahead or on a par. + .filter(|(_, peer)| peer.best_number >= number) + .map(|(id, _)| *id) + .collect(); + + debug!( + target: LOG_TARGET, + "Explicit sync request for block {hash:?} with no peers specified. \ + Syncing from these peers {peers:?} instead.", + ); + } else { + debug!( + target: LOG_TARGET, + "Explicit sync request for block {hash:?} with {peers:?}", + ); + } + + if self.is_known(hash) { + debug!(target: LOG_TARGET, "Refusing to sync known hash {hash:?}"); + return + } + + trace!(target: LOG_TARGET, "Downloading requested old fork {hash:?}"); + for peer_id in &peers { + if let Some(peer) = self.peers.get_mut(peer_id) { + if let PeerSyncState::AncestorSearch { .. } = peer.state { + continue + } + + if number > peer.best_number { + peer.best_number = number; + peer.best_hash = *hash; + } + self.allowed_requests.add(peer_id); + } + } + + self.fork_targets + .entry(*hash) + .or_insert_with(|| { + if let Some(metrics) = &self.metrics { + metrics.fork_targets.inc(); + } + + ForkTarget { number, peers: Default::default(), parent_hash: None } + }) + .peers + .extend(peers); + } + + fn request_justification(&mut self, hash: &B::Hash, number: NumberFor) { + let client = &self.client; + self.extra_justifications + .schedule((*hash, number), |base, block| is_descendent_of(&**client, base, block)) + } + + fn clear_justification_requests(&mut self) { + self.extra_justifications.reset(); + } + + fn on_justification_import(&mut self, hash: B::Hash, number: NumberFor, success: bool) { + let finalization_result = if success { Ok((hash, number)) } else { Err(()) }; + self.extra_justifications + .try_finalize_root((hash, number), finalization_result, true); + self.allowed_requests.set_all(); + } + + fn on_block_response( + &mut self, + peer_id: PeerId, + key: StrategyKey, + request: BlockRequest, + blocks: Vec>, + ) { + if key != StrategyKey::ChainSync { + error!( + target: LOG_TARGET, + "`on_block_response()` called with unexpected key {key:?} for chain sync", + ); + debug_assert!(false); + } + let block_response = BlockResponse:: { id: request.id, blocks }; + + let blocks_range = || match ( + block_response + .blocks + .first() + .and_then(|b| b.header.as_ref().map(|h| h.number())), + block_response.blocks.last().and_then(|b| b.header.as_ref().map(|h| h.number())), + ) { + (Some(first), Some(last)) if first != last => format!(" ({}..{})", first, last), + (Some(first), Some(_)) => format!(" ({})", first), + _ => Default::default(), + }; + trace!( + target: LOG_TARGET, + "BlockResponse {} from {} with {} blocks {}", + block_response.id, + peer_id, + block_response.blocks.len(), + blocks_range(), + ); + + let res = if request.fields == BlockAttributes::JUSTIFICATION { + self.on_block_justification(peer_id, block_response) + } else { + self.on_block_data(&peer_id, Some(request), block_response) + }; + + if let Err(bad_peer) = res { + self.actions.push(SyncingAction::DropPeer(bad_peer)); + } + } + + fn on_state_response( + &mut self, + peer_id: PeerId, + key: StrategyKey, + response: OpaqueStateResponse, + ) { + if key != StrategyKey::ChainSync { + error!( + target: LOG_TARGET, + "`on_state_response()` called with unexpected key {key:?} for chain sync", + ); + debug_assert!(false); + } + if let Err(bad_peer) = self.on_state_data(&peer_id, response) { + self.actions.push(SyncingAction::DropPeer(bad_peer)); + } + } + + fn on_warp_proof_response( + &mut self, + _peer_id: &PeerId, + _key: StrategyKey, + _response: EncodedProof, + ) { + error!( + target: LOG_TARGET, + "`on_warp_proof_response()` called for chain sync strategy", + ); + debug_assert!(false); + } + + fn on_blocks_processed( + &mut self, + imported: usize, + count: usize, + results: Vec<(Result>, BlockImportError>, B::Hash)>, + ) { + trace!(target: LOG_TARGET, "Imported {imported} of {count}"); + + let mut has_error = false; + for (_, hash) in &results { + if self.queue_blocks.remove(hash) { + if let Some(metrics) = &self.metrics { + metrics.queued_blocks.dec(); + } + } + self.blocks.clear_queued(hash); + if let Some(gap_sync) = &mut self.gap_sync { + gap_sync.blocks.clear_queued(hash); + } + } + for (result, hash) in results { + if has_error { + break + } + + has_error |= result.is_err(); + + match result { + Ok(BlockImportStatus::ImportedKnown(number, peer_id)) => + if let Some(peer) = peer_id { + self.update_peer_common_number(&peer, number); + }, + Ok(BlockImportStatus::ImportedUnknown(number, aux, peer_id)) => { + if aux.clear_justification_requests { + trace!( + target: LOG_TARGET, + "Block imported clears all pending justification requests {number}: {hash:?}", + ); + self.clear_justification_requests(); + } + + if aux.needs_justification { + trace!( + target: LOG_TARGET, + "Block imported but requires justification {number}: {hash:?}", + ); + self.request_justification(&hash, number); + } + + if aux.bad_justification { + if let Some(ref peer) = peer_id { + warn!("๐Ÿ’” Sent block with bad justification to import"); + self.actions.push(SyncingAction::DropPeer(BadPeer( + *peer, + rep::BAD_JUSTIFICATION, + ))); + } + } + + if let Some(peer) = peer_id { + self.update_peer_common_number(&peer, number); + } + let state_sync_complete = + self.state_sync.as_ref().map_or(false, |s| s.target_hash() == hash); + if state_sync_complete { + info!( + target: LOG_TARGET, + "State sync is complete ({} MiB), restarting block sync.", + self.state_sync.as_ref().map_or(0, |s| s.progress().size / (1024 * 1024)), + ); + self.state_sync = None; + self.mode = ChainSyncMode::Full; + self.restart(); + } + let gap_sync_complete = + self.gap_sync.as_ref().map_or(false, |s| s.target == number); + if gap_sync_complete { + info!( + target: LOG_TARGET, + "Block history download is complete." + ); + self.gap_sync = None; + } + }, + Err(BlockImportError::IncompleteHeader(peer_id)) => + if let Some(peer) = peer_id { + warn!( + target: LOG_TARGET, + "๐Ÿ’” Peer sent block with incomplete header to import", + ); + self.actions + .push(SyncingAction::DropPeer(BadPeer(peer, rep::INCOMPLETE_HEADER))); + self.restart(); + }, + Err(BlockImportError::VerificationFailed(peer_id, e)) => { + let extra_message = peer_id + .map_or_else(|| "".into(), |peer| format!(" received from ({peer})")); + + warn!( + target: LOG_TARGET, + "๐Ÿ’” Verification failed for block {hash:?}{extra_message}: {e:?}", + ); + + if let Some(peer) = peer_id { + self.actions + .push(SyncingAction::DropPeer(BadPeer(peer, rep::VERIFICATION_FAIL))); + } + + self.restart(); + }, + Err(BlockImportError::BadBlock(peer_id)) => + if let Some(peer) = peer_id { + warn!( + target: LOG_TARGET, + "๐Ÿ’” Block {hash:?} received from peer {peer} has been blacklisted", + ); + self.actions.push(SyncingAction::DropPeer(BadPeer(peer, rep::BAD_BLOCK))); + }, + Err(BlockImportError::MissingState) => { + // This may happen if the chain we were requesting upon has been discarded + // in the meantime because other chain has been finalized. + // Don't mark it as bad as it still may be synced if explicitly requested. + trace!(target: LOG_TARGET, "Obsolete block {hash:?}"); + }, + e @ Err(BlockImportError::UnknownParent) | e @ Err(BlockImportError::Other(_)) => { + warn!(target: LOG_TARGET, "๐Ÿ’” Error importing block {hash:?}: {}", e.unwrap_err()); + self.state_sync = None; + self.restart(); + }, + Err(BlockImportError::Cancelled) => {}, + }; + } + + self.allowed_requests.set_all(); + } + + fn on_block_finalized(&mut self, hash: &B::Hash, number: NumberFor) { + let client = &self.client; + let r = self.extra_justifications.on_block_finalized(hash, number, |base, block| { + is_descendent_of(&**client, base, block) + }); + + if let ChainSyncMode::LightState { skip_proofs, .. } = &self.mode { + if self.state_sync.is_none() { + if !self.peers.is_empty() && self.queue_blocks.is_empty() { + self.attempt_state_sync(*hash, number, *skip_proofs); + } else { + self.pending_state_sync_attempt.replace((*hash, number, *skip_proofs)); + } + } + } + + if let Err(err) = r { + warn!( + target: LOG_TARGET, + "๐Ÿ’” Error cleaning up pending extra justification data requests: {err}", + ); + } + } + + fn update_chain_info(&mut self, best_hash: &B::Hash, best_number: NumberFor) { + self.on_block_queued(best_hash, best_number); + } + + fn is_major_syncing(&self) -> bool { + self.status().state.is_major_syncing() + } + + fn num_peers(&self) -> usize { + self.peers.len() + } + + fn status(&self) -> SyncStatus { + let median_seen = self.median_seen(); + let best_seen_block = + median_seen.and_then(|median| (median > self.best_queued_number).then_some(median)); + let sync_state = if let Some(target) = median_seen { + // A chain is classified as downloading if the provided best block is + // more than `MAJOR_SYNC_BLOCKS` behind the best block or as importing + // if the same can be said about queued blocks. + let best_block = self.client.info().best_number; + if target > best_block && target - best_block > MAJOR_SYNC_BLOCKS.into() { + // If target is not queued, we're downloading, otherwise importing. + if target > self.best_queued_number { + SyncState::Downloading { target } + } else { + SyncState::Importing { target } + } + } else { + SyncState::Idle + } + } else { + SyncState::Idle + }; + + let warp_sync_progress = self.gap_sync.as_ref().map(|gap_sync| WarpSyncProgress { + phase: WarpSyncPhase::DownloadingBlocks(gap_sync.best_queued_number), + total_bytes: 0, + }); + + SyncStatus { + state: sync_state, + best_seen_block, + num_peers: self.peers.len() as u32, + queued_blocks: self.queue_blocks.len() as u32, + state_sync: self.state_sync.as_ref().map(|s| s.progress()), + warp_sync: warp_sync_progress, + } + } + + fn num_downloaded_blocks(&self) -> usize { + self.downloaded_blocks + } + + fn num_sync_requests(&self) -> usize { + self.fork_targets + .values() + .filter(|f| f.number <= self.best_queued_number) + .count() + } + + fn actions(&mut self) -> Result>, ClientError> { + if !self.peers.is_empty() && self.queue_blocks.is_empty() { + if let Some((hash, number, skip_proofs)) = self.pending_state_sync_attempt.take() { + self.attempt_state_sync(hash, number, skip_proofs); + } + } + + let block_requests = self.block_requests().into_iter().map(|(peer_id, request)| { + SyncingAction::SendBlockRequest { peer_id, key: StrategyKey::ChainSync, request } + }); + self.actions.extend(block_requests); + + let justification_requests = + self.justification_requests().into_iter().map(|(peer_id, request)| { + SyncingAction::SendBlockRequest { peer_id, key: StrategyKey::ChainSync, request } + }); + self.actions.extend(justification_requests); + + let state_request = self.state_request().into_iter().map(|(peer_id, request)| { + SyncingAction::SendStateRequest { + peer_id, + key: StrategyKey::ChainSync, + protocol_name: self.state_request_protocol_name.clone(), + request, + } + }); + self.actions.extend(state_request); + + Ok(std::mem::take(&mut self.actions)) + } +} + +impl ChainSync +where + B: BlockT, + Client: HeaderBackend + + BlockBackend + + HeaderMetadata + + ProofProvider + + Send + + Sync + + 'static, +{ + /// Create a new instance. + pub fn new( + mode: ChainSyncMode, + client: Arc, + max_parallel_downloads: u32, + max_blocks_per_request: u32, + state_request_protocol_name: ProtocolName, + metrics_registry: Option<&Registry>, + initial_peers: impl Iterator)>, + ) -> Result { + let mut sync = Self { + client, + peers: HashMap::new(), + disconnected_peers: DisconnectedPeers::new(), + blocks: BlockCollection::new(), + best_queued_hash: Default::default(), + best_queued_number: Zero::zero(), + extra_justifications: ExtraRequests::new("justification", metrics_registry), + mode, + queue_blocks: Default::default(), + pending_state_sync_attempt: None, + fork_targets: Default::default(), + allowed_requests: Default::default(), + max_parallel_downloads, + max_blocks_per_request, + state_request_protocol_name, + downloaded_blocks: 0, + state_sync: None, + import_existing: false, + gap_sync: None, + actions: Vec::new(), + metrics: metrics_registry.and_then(|r| match Metrics::register(r) { + Ok(metrics) => Some(metrics), + Err(err) => { + log::error!( + target: LOG_TARGET, + "Failed to register `ChainSync` metrics {err:?}", + ); + None + }, + }), + }; - /// Get the number of peers known to the syncing state machine. - pub fn num_peers(&self) -> usize { - self.peers.len() - } + sync.reset_sync_start_point()?; + initial_peers.for_each(|(peer_id, best_hash, best_number)| { + sync.add_peer(peer_id, best_hash, best_number); + }); - /// Notify syncing state machine that a new sync peer has connected. - pub fn add_peer(&mut self, peer_id: PeerId, best_hash: B::Hash, best_number: NumberFor) { - match self.add_peer_inner(peer_id, best_hash, best_number) { - Ok(Some(request)) => - self.actions.push(ChainSyncAction::SendBlockRequest { peer_id, request }), - Ok(None) => {}, - Err(bad_peer) => self.actions.push(ChainSyncAction::DropPeer(bad_peer)), - } + Ok(sync) } #[must_use] @@ -497,7 +984,7 @@ where "๐Ÿ’” New peer {} with unknown genesis hash {} ({}).", peer_id, best_hash, best_number, ); - return Err(BadPeer(peer_id, rep::GENESIS_MISMATCH)) + return Err(BadPeer(peer_id, rep::GENESIS_MISMATCH)); } // If there are more than `MAJOR_SYNC_BLOCKS` in the import queue then we have @@ -521,7 +1008,7 @@ where state: PeerSyncState::Available, }, ); - return Ok(None) + return Ok(None); } // If we are at genesis, just start downloading. @@ -590,91 +1077,6 @@ where } } - /// Inform sync about a new best imported block. - pub fn update_chain_info(&mut self, best_hash: &B::Hash, best_number: NumberFor) { - self.on_block_queued(best_hash, best_number); - } - - /// Request extra justification. - pub fn request_justification(&mut self, hash: &B::Hash, number: NumberFor) { - let client = &self.client; - self.extra_justifications - .schedule((*hash, number), |base, block| is_descendent_of(&**client, base, block)) - } - - /// Clear extra justification requests. - pub fn clear_justification_requests(&mut self) { - self.extra_justifications.reset(); - } - - /// Configure an explicit fork sync request in case external code has detected that there is a - /// stale fork missing. - /// - /// Note that this function should not be used for recent blocks. - /// Sync should be able to download all the recent forks normally. - /// - /// Passing empty `peers` set effectively removes the sync request. - // The implementation is similar to `on_validated_block_announce` with unknown parent hash. - pub fn set_sync_fork_request( - &mut self, - mut peers: Vec, - hash: &B::Hash, - number: NumberFor, - ) { - if peers.is_empty() { - peers = self - .peers - .iter() - // Only request blocks from peers who are ahead or on a par. - .filter(|(_, peer)| peer.best_number >= number) - .map(|(id, _)| *id) - .collect(); - - debug!( - target: LOG_TARGET, - "Explicit sync request for block {hash:?} with no peers specified. \ - Syncing from these peers {peers:?} instead.", - ); - } else { - debug!( - target: LOG_TARGET, - "Explicit sync request for block {hash:?} with {peers:?}", - ); - } - - if self.is_known(hash) { - debug!(target: LOG_TARGET, "Refusing to sync known hash {hash:?}"); - return - } - - trace!(target: LOG_TARGET, "Downloading requested old fork {hash:?}"); - for peer_id in &peers { - if let Some(peer) = self.peers.get_mut(peer_id) { - if let PeerSyncState::AncestorSearch { .. } = peer.state { - continue - } - - if number > peer.best_number { - peer.best_number = number; - peer.best_hash = *hash; - } - self.allowed_requests.add(peer_id); - } - } - - self.fork_targets - .entry(*hash) - .or_insert_with(|| { - if let Some(metrics) = &self.metrics { - metrics.fork_targets.inc(); - } - - ForkTarget { number, peers: Default::default(), parent_hash: None } - }) - .peers - .extend(peers); - } - /// Submit a block response for processing. #[must_use] fn on_block_data( @@ -748,14 +1150,14 @@ where blocks } else { debug!(target: LOG_TARGET, "Unexpected gap block response from {peer_id}"); - return Err(BadPeer(*peer_id, rep::NO_BLOCK)) + return Err(BadPeer(*peer_id, rep::NO_BLOCK)); } }, PeerSyncState::DownloadingStale(_) => { peer.state = PeerSyncState::Available; if blocks.is_empty() { debug!(target: LOG_TARGET, "Empty block response from {peer_id}"); - return Err(BadPeer(*peer_id, rep::NO_BLOCK)) + return Err(BadPeer(*peer_id, rep::NO_BLOCK)); } validate_blocks::(&blocks, peer_id, Some(request))?; blocks @@ -796,14 +1198,14 @@ where target: LOG_TARGET, "Invalid response when searching for ancestor from {peer_id}", ); - return Err(BadPeer(*peer_id, rep::UNKNOWN_ANCESTOR)) + return Err(BadPeer(*peer_id, rep::UNKNOWN_ANCESTOR)); }, (_, Err(e)) => { info!( target: LOG_TARGET, "โŒ Error answering legitimate blockchain query: {e}", ); - return Err(BadPeer(*peer_id, rep::BLOCKCHAIN_READ_ERROR)) + return Err(BadPeer(*peer_id, rep::BLOCKCHAIN_READ_ERROR)); }, }; if matching_hash.is_some() { @@ -837,7 +1239,7 @@ where target: LOG_TARGET, "Ancestry search: genesis mismatch for peer {peer_id}", ); - return Err(BadPeer(*peer_id, rep::GENESIS_MISMATCH)) + return Err(BadPeer(*peer_id, rep::GENESIS_MISMATCH)); } if let Some((next_state, next_num)) = handle_ancestor_search_state(state, *current, matching_hash.is_some()) @@ -848,11 +1250,12 @@ where state: next_state, }; let request = ancestry_request::(next_num); - self.actions.push(ChainSyncAction::SendBlockRequest { + self.actions.push(SyncingAction::SendBlockRequest { peer_id: *peer_id, + key: StrategyKey::ChainSync, request, }); - return Ok(()) + return Ok(()); } else { // Ancestry search is complete. Check if peer is on a stale fork unknown // to us and add it to sync targets if necessary. @@ -892,7 +1295,7 @@ where .insert(*peer_id); } peer.state = PeerSyncState::Available; - return Ok(()) + return Ok(()); } }, PeerSyncState::Available | @@ -925,7 +1328,7 @@ where } } else { // We don't know of this peer, so we also did not request anything from it. - return Err(BadPeer(*peer_id, rep::NOT_REQUESTED)) + return Err(BadPeer(*peer_id, rep::NOT_REQUESTED)); }; self.validate_and_queue_blocks(new_blocks, gap); @@ -947,235 +1350,54 @@ where target: LOG_TARGET, "๐Ÿ’” Called on_block_justification with a peer ID of an unknown peer", ); - return Ok(()) - }; - - self.allowed_requests.add(&peer_id); - if let PeerSyncState::DownloadingJustification(hash) = peer.state { - peer.state = PeerSyncState::Available; - - // We only request one justification at a time - let justification = if let Some(block) = response.blocks.into_iter().next() { - if hash != block.hash { - warn!( - target: LOG_TARGET, - "๐Ÿ’” Invalid block justification provided by {}: requested: {:?} got: {:?}", - peer_id, - hash, - block.hash, - ); - return Err(BadPeer(peer_id, rep::BAD_JUSTIFICATION)) - } - - block - .justifications - .or_else(|| legacy_justification_mapping(block.justification)) - } else { - // we might have asked the peer for a justification on a block that we assumed it - // had but didn't (regardless of whether it had a justification for it or not). - trace!( - target: LOG_TARGET, - "Peer {peer_id:?} provided empty response for justification request {hash:?}", - ); - - None - }; - - if let Some((peer_id, hash, number, justifications)) = - self.extra_justifications.on_response(peer_id, justification) - { - self.actions.push(ChainSyncAction::ImportJustifications { - peer_id, - hash, - number, - justifications, - }); - return Ok(()) - } - } - - Ok(()) - } - - /// Report a justification import (successful or not). - pub fn on_justification_import(&mut self, hash: B::Hash, number: NumberFor, success: bool) { - let finalization_result = if success { Ok((hash, number)) } else { Err(()) }; - self.extra_justifications - .try_finalize_root((hash, number), finalization_result, true); - self.allowed_requests.set_all(); - } - - /// Notify sync that a block has been finalized. - pub fn on_block_finalized(&mut self, hash: &B::Hash, number: NumberFor) { - let client = &self.client; - let r = self.extra_justifications.on_block_finalized(hash, number, |base, block| { - is_descendent_of(&**client, base, block) - }); - - if let ChainSyncMode::LightState { skip_proofs, .. } = &self.mode { - if self.state_sync.is_none() && !self.peers.is_empty() && self.queue_blocks.is_empty() { - // Finalized a recent block. - let mut heads: Vec<_> = self.peers.values().map(|peer| peer.best_number).collect(); - heads.sort(); - let median = heads[heads.len() / 2]; - if number + STATE_SYNC_FINALITY_THRESHOLD.saturated_into() >= median { - if let Ok(Some(header)) = self.client.header(*hash) { - log::debug!( - target: LOG_TARGET, - "Starting state sync for #{number} ({hash})", - ); - self.state_sync = Some(StateSync::new( - self.client.clone(), - header, - None, - None, - *skip_proofs, - )); - self.allowed_requests.set_all(); - } - } - } - } - - if let Err(err) = r { - warn!( - target: LOG_TARGET, - "๐Ÿ’” Error cleaning up pending extra justification data requests: {err}", - ); - } - } - - /// Submit a validated block announcement. - /// - /// Returns new best hash & best number of the peer if they are updated. - #[must_use] - pub fn on_validated_block_announce( - &mut self, - is_best: bool, - peer_id: PeerId, - announce: &BlockAnnounce, - ) -> Option<(B::Hash, NumberFor)> { - let number = *announce.header.number(); - let hash = announce.header.hash(); - let parent_status = - self.block_status(announce.header.parent_hash()).unwrap_or(BlockStatus::Unknown); - let known_parent = parent_status != BlockStatus::Unknown; - let ancient_parent = parent_status == BlockStatus::InChainPruned; - - let known = self.is_known(&hash); - let peer = if let Some(peer) = self.peers.get_mut(&peer_id) { - peer - } else { - error!(target: LOG_TARGET, "๐Ÿ’” Called `on_validated_block_announce` with a bad peer ID {peer_id}"); - return Some((hash, number)) + return Ok(()); }; - if let PeerSyncState::AncestorSearch { .. } = peer.state { - trace!(target: LOG_TARGET, "Peer {} is in the ancestor search state.", peer_id); - return None - } - - let peer_info = is_best.then(|| { - // update their best block - peer.best_number = number; - peer.best_hash = hash; - - (hash, number) - }); - - // If the announced block is the best they have and is not ahead of us, our common number - // is either one further ahead or it's the one they just announced, if we know about it. - if is_best { - if known && self.best_queued_number >= number { - self.update_peer_common_number(&peer_id, number); - } else if announce.header.parent_hash() == &self.best_queued_hash || - known_parent && self.best_queued_number >= number - { - self.update_peer_common_number(&peer_id, number.saturating_sub(One::one())); - } - } - self.allowed_requests.add(&peer_id); - - // known block case - if known || self.is_already_downloading(&hash) { - trace!(target: LOG_TARGET, "Known block announce from {}: {}", peer_id, hash); - if let Some(target) = self.fork_targets.get_mut(&hash) { - target.peers.insert(peer_id); - } - return peer_info - } - - if ancient_parent { - trace!( - target: LOG_TARGET, - "Ignored ancient block announced from {}: {} {:?}", - peer_id, - hash, - announce.header, - ); - return peer_info - } - - if self.status().state == SyncState::Idle { - trace!( - target: LOG_TARGET, - "Added sync target for block announced from {}: {} {:?}", - peer_id, - hash, - announce.summary(), - ); - self.fork_targets - .entry(hash) - .or_insert_with(|| { - if let Some(metrics) = &self.metrics { - metrics.fork_targets.inc(); - } - - ForkTarget { - number, - parent_hash: Some(*announce.header.parent_hash()), - peers: Default::default(), - } - }) - .peers - .insert(peer_id); - } - - peer_info - } - - /// Notify that a sync peer has disconnected. - pub fn remove_peer(&mut self, peer_id: &PeerId) { - self.blocks.clear_peer_download(peer_id); - if let Some(gap_sync) = &mut self.gap_sync { - gap_sync.blocks.clear_peer_download(peer_id) - } + self.allowed_requests.add(&peer_id); + if let PeerSyncState::DownloadingJustification(hash) = peer.state { + peer.state = PeerSyncState::Available; - if let Some(state) = self.peers.remove(peer_id) { - if !state.state.is_available() { - if let Some(bad_peer) = - self.disconnected_peers.on_disconnect_during_request(*peer_id) - { - self.actions.push(ChainSyncAction::DropPeer(bad_peer)); + // We only request one justification at a time + let justification = if let Some(block) = response.blocks.into_iter().next() { + if hash != block.hash { + warn!( + target: LOG_TARGET, + "๐Ÿ’” Invalid block justification provided by {}: requested: {:?} got: {:?}", + peer_id, + hash, + block.hash, + ); + return Err(BadPeer(peer_id, rep::BAD_JUSTIFICATION)); } - } - } - self.extra_justifications.peer_disconnected(peer_id); - self.allowed_requests.set_all(); - self.fork_targets.retain(|_, target| { - target.peers.remove(peer_id); - !target.peers.is_empty() - }); - if let Some(metrics) = &self.metrics { - metrics.fork_targets.set(self.fork_targets.len().try_into().unwrap_or(u64::MAX)); - } + block + .justifications + .or_else(|| legacy_justification_mapping(block.justification)) + } else { + // we might have asked the peer for a justification on a block that we assumed it + // had but didn't (regardless of whether it had a justification for it or not). + trace!( + target: LOG_TARGET, + "Peer {peer_id:?} provided empty response for justification request {hash:?}", + ); - let blocks = self.ready_blocks(); + None + }; - if !blocks.is_empty() { - self.validate_and_queue_blocks(blocks, false); + if let Some((peer_id, hash, number, justifications)) = + self.extra_justifications.on_response(peer_id, justification) + { + self.actions.push(SyncingAction::ImportJustifications { + peer_id, + hash, + number, + justifications, + }); + return Ok(()); + } } + + Ok(()) } /// Returns the median seen block number. @@ -1249,7 +1471,7 @@ where .set(self.queue_blocks.len().try_into().unwrap_or(u64::MAX)); } - self.actions.push(ChainSyncAction::ImportBlocks { origin, blocks: new_blocks }) + self.actions.push(SyncingAction::ImportBlocks { origin, blocks: new_blocks }) } fn update_peer_common_number(&mut self, peer_id: &PeerId, new_common: NumberFor) { @@ -1281,7 +1503,7 @@ where for (n, peer) in self.peers.iter_mut() { if let PeerSyncState::AncestorSearch { .. } = peer.state { // Wait for ancestry search to complete first. - continue + continue; } let new_common_number = if peer.best_number >= number { number } else { peer.best_number }; @@ -1328,7 +1550,10 @@ where PeerSyncState::DownloadingGap(_) | PeerSyncState::DownloadingState => { // Cancel a request first, as `add_peer` may generate a new request. - self.actions.push(ChainSyncAction::CancelRequest { peer_id }); + self.actions.push(SyncingAction::CancelRequest { + peer_id, + key: StrategyKey::ChainSync, + }); self.add_peer(peer_id, peer_sync.best_hash, peer_sync.best_number); }, PeerSyncState::DownloadingJustification(_) => { @@ -1381,7 +1606,7 @@ where } } - if let Some((start, end)) = info.block_gap { + if let Some(BlockGap { start, end, .. }) = info.block_gap { debug!(target: LOG_TARGET, "Starting gap sync #{start} - #{end}"); self.gap_sync = Some(GapSync { best_queued_number: start - One::one(), @@ -1401,7 +1626,7 @@ where /// What is the status of the block corresponding to the given hash? fn block_status(&self, hash: &B::Hash) -> Result { if self.queue_blocks.contains(hash) { - return Ok(BlockStatus::Queued) + return Ok(BlockStatus::Queued); } self.client.block_status(*hash) } @@ -1444,53 +1669,6 @@ where .collect() } - /// Submit blocks received in a response. - pub fn on_block_response( - &mut self, - peer_id: PeerId, - request: BlockRequest, - blocks: Vec>, - ) { - let block_response = BlockResponse:: { id: request.id, blocks }; - - let blocks_range = || match ( - block_response - .blocks - .first() - .and_then(|b| b.header.as_ref().map(|h| h.number())), - block_response.blocks.last().and_then(|b| b.header.as_ref().map(|h| h.number())), - ) { - (Some(first), Some(last)) if first != last => format!(" ({}..{})", first, last), - (Some(first), Some(_)) => format!(" ({})", first), - _ => Default::default(), - }; - trace!( - target: LOG_TARGET, - "BlockResponse {} from {} with {} blocks {}", - block_response.id, - peer_id, - block_response.blocks.len(), - blocks_range(), - ); - - let res = if request.fields == BlockAttributes::JUSTIFICATION { - self.on_block_justification(peer_id, block_response) - } else { - self.on_block_data(&peer_id, Some(request), block_response) - }; - - if let Err(bad_peer) = res { - self.actions.push(ChainSyncAction::DropPeer(bad_peer)); - } - } - - /// Submit a state received in a response. - pub fn on_state_response(&mut self, peer_id: PeerId, response: OpaqueStateResponse) { - if let Err(bad_peer) = self.on_state_data(&peer_id, response) { - self.actions.push(ChainSyncAction::DropPeer(bad_peer)); - } - } - /// Get justification requests scheduled by sync to be sent out. fn justification_requests(&mut self) -> Vec<(PeerId, BlockRequest)> { let peers = &mut self.peers; @@ -1521,12 +1699,12 @@ where /// Get block requests scheduled by sync to be sent out. fn block_requests(&mut self) -> Vec<(PeerId, BlockRequest)> { if self.allowed_requests.is_empty() || self.state_sync.is_some() { - return Vec::new() + return Vec::new(); } if self.queue_blocks.len() > MAX_IMPORTING_BLOCKS { trace!(target: LOG_TARGET, "Too many blocks in the queue."); - return Vec::new() + return Vec::new(); } let is_major_syncing = self.status().state.is_major_syncing(); let attrs = self.required_block_attributes(); @@ -1537,20 +1715,21 @@ where let best_queued = self.best_queued_number; let client = &self.client; let queue_blocks = &self.queue_blocks; - let allowed_requests = self.allowed_requests.take(); + let allowed_requests = self.allowed_requests.clone(); let max_parallel = if is_major_syncing { 1 } else { self.max_parallel_downloads }; let max_blocks_per_request = self.max_blocks_per_request; let gap_sync = &mut self.gap_sync; let disconnected_peers = &mut self.disconnected_peers; let metrics = self.metrics.as_ref(); - self.peers + let requests = self + .peers .iter_mut() .filter_map(move |(&id, peer)| { if !peer.state.is_available() || !allowed_requests.contains(&id) || !disconnected_peers.is_peer_available(&id) { - return None + return None; } // If our best queued is more than `MAX_BLOCKS_TO_LOOK_BACKWARDS` blocks away from @@ -1642,23 +1821,31 @@ where None } }) - .collect() + .collect::>(); + + // Clear the allowed_requests state when sending new block requests + // to prevent multiple inflight block requests from being issued. + if !requests.is_empty() { + self.allowed_requests.take(); + } + + requests } /// Get a state request scheduled by sync to be sent out (if any). fn state_request(&mut self) -> Option<(PeerId, OpaqueStateRequest)> { if self.allowed_requests.is_empty() { - return None + return None; } if self.state_sync.is_some() && self.peers.iter().any(|(_, peer)| peer.state == PeerSyncState::DownloadingState) { // Only one pending state request is allowed. - return None + return None; } if let Some(sync) = &self.state_sync { if sync.is_complete() { - return None + return None; } for (id, peer) in self.peers.iter_mut() { @@ -1670,7 +1857,7 @@ where let request = sync.next_request(); trace!(target: LOG_TARGET, "New StateRequest for {}: {:?}", id, request); self.allowed_requests.clear(); - return Some((*id, OpaqueStateRequest(Box::new(request)))) + return Some((*id, OpaqueStateRequest(Box::new(request)))); } } } @@ -1709,7 +1896,7 @@ where sync.import(*response) } else { debug!(target: LOG_TARGET, "Ignored obsolete state response from {peer_id}"); - return Err(BadPeer(*peer_id, rep::NOT_REQUESTED)) + return Err(BadPeer(*peer_id, rep::NOT_REQUESTED)); }; match import_result { @@ -1728,7 +1915,7 @@ where state: Some(state), }; debug!(target: LOG_TARGET, "State download is complete. Import is queued"); - self.actions.push(ChainSyncAction::ImportBlocks { origin, blocks: vec![block] }); + self.actions.push(SyncingAction::ImportBlocks { origin, blocks: vec![block] }); Ok(()) }, ImportResult::Continue => Ok(()), @@ -1739,174 +1926,39 @@ where } } - /// A batch of blocks have been processed, with or without errors. - /// - /// Call this when a batch of blocks have been processed by the import - /// queue, with or without errors. - pub fn on_blocks_processed( + fn attempt_state_sync( &mut self, - imported: usize, - count: usize, - results: Vec<(Result>, BlockImportError>, B::Hash)>, + finalized_hash: B::Hash, + finalized_number: NumberFor, + skip_proofs: bool, ) { - trace!(target: LOG_TARGET, "Imported {imported} of {count}"); - - let mut has_error = false; - for (_, hash) in &results { - if self.queue_blocks.remove(hash) { - if let Some(metrics) = &self.metrics { - metrics.queued_blocks.dec(); - } - } - self.blocks.clear_queued(hash); - if let Some(gap_sync) = &mut self.gap_sync { - gap_sync.blocks.clear_queued(hash); - } - } - for (result, hash) in results { - if has_error { - break + let mut heads: Vec<_> = self.peers.values().map(|peer| peer.best_number).collect(); + heads.sort(); + let median = heads[heads.len() / 2]; + if finalized_number + STATE_SYNC_FINALITY_THRESHOLD.saturated_into() >= median { + if let Ok(Some(header)) = self.client.header(finalized_hash) { + log::debug!( + target: LOG_TARGET, + "Starting state sync for #{finalized_number} ({finalized_hash})", + ); + self.state_sync = + Some(StateSync::new(self.client.clone(), header, None, None, skip_proofs)); + self.allowed_requests.set_all(); + } else { + log::error!( + target: LOG_TARGET, + "Failed to start state sync: header for finalized block \ + #{finalized_number} ({finalized_hash}) is not available", + ); + debug_assert!(false); } - - has_error |= result.is_err(); - - match result { - Ok(BlockImportStatus::ImportedKnown(number, peer_id)) => - if let Some(peer) = peer_id { - self.update_peer_common_number(&peer, number); - }, - Ok(BlockImportStatus::ImportedUnknown(number, aux, peer_id)) => { - if aux.clear_justification_requests { - trace!( - target: LOG_TARGET, - "Block imported clears all pending justification requests {number}: {hash:?}", - ); - self.clear_justification_requests(); - } - - if aux.needs_justification { - trace!( - target: LOG_TARGET, - "Block imported but requires justification {number}: {hash:?}", - ); - self.request_justification(&hash, number); - } - - if aux.bad_justification { - if let Some(ref peer) = peer_id { - warn!("๐Ÿ’” Sent block with bad justification to import"); - self.actions.push(ChainSyncAction::DropPeer(BadPeer( - *peer, - rep::BAD_JUSTIFICATION, - ))); - } - } - - if let Some(peer) = peer_id { - self.update_peer_common_number(&peer, number); - } - let state_sync_complete = - self.state_sync.as_ref().map_or(false, |s| s.target_hash() == hash); - if state_sync_complete { - info!( - target: LOG_TARGET, - "State sync is complete ({} MiB), restarting block sync.", - self.state_sync.as_ref().map_or(0, |s| s.progress().size / (1024 * 1024)), - ); - self.state_sync = None; - self.mode = ChainSyncMode::Full; - self.restart(); - } - let gap_sync_complete = - self.gap_sync.as_ref().map_or(false, |s| s.target == number); - if gap_sync_complete { - info!( - target: LOG_TARGET, - "Block history download is complete." - ); - self.gap_sync = None; - } - }, - Err(BlockImportError::IncompleteHeader(peer_id)) => - if let Some(peer) = peer_id { - warn!( - target: LOG_TARGET, - "๐Ÿ’” Peer sent block with incomplete header to import", - ); - self.actions - .push(ChainSyncAction::DropPeer(BadPeer(peer, rep::INCOMPLETE_HEADER))); - self.restart(); - }, - Err(BlockImportError::VerificationFailed(peer_id, e)) => { - let extra_message = peer_id - .map_or_else(|| "".into(), |peer| format!(" received from ({peer})")); - - warn!( - target: LOG_TARGET, - "๐Ÿ’” Verification failed for block {hash:?}{extra_message}: {e:?}", - ); - - if let Some(peer) = peer_id { - self.actions - .push(ChainSyncAction::DropPeer(BadPeer(peer, rep::VERIFICATION_FAIL))); - } - - self.restart(); - }, - Err(BlockImportError::BadBlock(peer_id)) => - if let Some(peer) = peer_id { - warn!( - target: LOG_TARGET, - "๐Ÿ’” Block {hash:?} received from peer {peer} has been blacklisted", - ); - self.actions.push(ChainSyncAction::DropPeer(BadPeer(peer, rep::BAD_BLOCK))); - }, - Err(BlockImportError::MissingState) => { - // This may happen if the chain we were requesting upon has been discarded - // in the meantime because other chain has been finalized. - // Don't mark it as bad as it still may be synced if explicitly requested. - trace!(target: LOG_TARGET, "Obsolete block {hash:?}"); - }, - e @ Err(BlockImportError::UnknownParent) | e @ Err(BlockImportError::Other(_)) => { - warn!(target: LOG_TARGET, "๐Ÿ’” Error importing block {hash:?}: {}", e.unwrap_err()); - self.state_sync = None; - self.restart(); - }, - Err(BlockImportError::Cancelled) => {}, - }; } - - self.allowed_requests.set_all(); - } - - /// Get pending actions to perform. - #[must_use] - pub fn actions(&mut self) -> impl Iterator> { - let block_requests = self - .block_requests() - .into_iter() - .map(|(peer_id, request)| ChainSyncAction::SendBlockRequest { peer_id, request }); - self.actions.extend(block_requests); - - let justification_requests = self - .justification_requests() - .into_iter() - .map(|(peer_id, request)| ChainSyncAction::SendBlockRequest { peer_id, request }); - self.actions.extend(justification_requests); - - let state_request = self - .state_request() - .into_iter() - .map(|(peer_id, request)| ChainSyncAction::SendStateRequest { peer_id, request }); - self.actions.extend(state_request); - - std::mem::take(&mut self.actions).into_iter() } /// A version of `actions()` that doesn't schedule extra requests. For testing only. #[cfg(test)] #[must_use] - fn take_actions(&mut self) -> impl Iterator> { + fn take_actions(&mut self) -> impl Iterator> { std::mem::take(&mut self.actions).into_iter() } } @@ -1964,7 +2016,7 @@ fn handle_ancestor_search_state( if block_hash_match && next_distance_to_tip == One::one() { // We found the ancestor in the first step so there is no need to execute binary // search. - return None + return None; } if block_hash_match { let left = curr_block_num; @@ -1983,7 +2035,7 @@ fn handle_ancestor_search_state( }, AncestorSearchState::BinarySearch(mut left, mut right) => { if left >= curr_block_num { - return None + return None; } if block_hash_match { left = curr_block_num; @@ -2014,7 +2066,7 @@ fn peer_block_request( ) -> Option<(Range>, BlockRequest)> { if best_num >= peer.best_number { // Will be downloaded as alternative fork instead. - return None + return None; } else if peer.common_number < finalized { trace!( target: LOG_TARGET, @@ -2103,7 +2155,7 @@ fn fork_sync_request( hash, r.number, ); - return false + return false; } if check_block(hash) != BlockStatus::Unknown { trace!( @@ -2112,7 +2164,7 @@ fn fork_sync_request( hash, r.number, ); - return false + return false; } true }); @@ -2121,7 +2173,7 @@ fn fork_sync_request( } for (hash, r) in fork_targets { if !r.peers.contains(&id) { - continue + continue; } // Download the fork only if it is behind or not too far ahead our tip of the chain // Otherwise it should be downloaded in full sync mode. @@ -2148,7 +2200,7 @@ fn fork_sync_request( direction: Direction::Descending, max: Some(count), }, - )) + )); } else { trace!(target: LOG_TARGET, "Fork too far in the future: {:?} (#{})", hash, r.number); } @@ -2167,7 +2219,7 @@ where T: HeaderMetadata + ?Sized, { if base == block { - return Ok(false) + return Ok(false); } let ancestor = sp_blockchain::lowest_common_ancestor(client, *block, *base)?; @@ -2194,7 +2246,7 @@ pub fn validate_blocks( blocks.len(), ); - return Err(BadPeer(*peer_id, rep::NOT_REQUESTED)) + return Err(BadPeer(*peer_id, rep::NOT_REQUESTED)); } let block_header = @@ -2214,7 +2266,7 @@ pub fn validate_blocks( block_header, ); - return Err(BadPeer(*peer_id, rep::NOT_REQUESTED)) + return Err(BadPeer(*peer_id, rep::NOT_REQUESTED)); } if request.fields.contains(BlockAttributes::HEADER) && @@ -2225,7 +2277,7 @@ pub fn validate_blocks( "Missing requested header for a block in response from {peer_id}.", ); - return Err(BadPeer(*peer_id, rep::BAD_RESPONSE)) + return Err(BadPeer(*peer_id, rep::BAD_RESPONSE)); } if request.fields.contains(BlockAttributes::BODY) && blocks.iter().any(|b| b.body.is_none()) @@ -2235,7 +2287,7 @@ pub fn validate_blocks( "Missing requested body for a block in response from {peer_id}.", ); - return Err(BadPeer(*peer_id, rep::BAD_RESPONSE)) + return Err(BadPeer(*peer_id, rep::BAD_RESPONSE)); } } @@ -2250,7 +2302,7 @@ pub fn validate_blocks( b.hash, hash, ); - return Err(BadPeer(*peer_id, rep::BAD_BLOCK)) + return Err(BadPeer(*peer_id, rep::BAD_BLOCK)); } } if let (Some(header), Some(body)) = (&b.header, &b.body) { @@ -2268,7 +2320,7 @@ pub fn validate_blocks( expected, got, ); - return Err(BadPeer(*peer_id, rep::BAD_BLOCK)) + return Err(BadPeer(*peer_id, rep::BAD_BLOCK)); } } } 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 39d0c8f8d4d63c2ae959bf1604fe27abdf0d80a2..d13f034e2e8da25df6171c0e21c607d4af722bc2 100644 --- a/substrate/client/network/sync/src/strategy/chain_sync/test.rs +++ b/substrate/client/network/sync/src/strategy/chain_sync/test.rs @@ -38,9 +38,16 @@ 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, std::iter::empty()) - .unwrap(); + let mut sync = ChainSync::new( + ChainSyncMode::Full, + client.clone(), + 1, + 64, + ProtocolName::Static(""), + None, + std::iter::empty(), + ) + .unwrap(); let (a1_hash, a1_number) = { let a1 = BlockBuilderBuilder::new(&*client) @@ -95,9 +102,16 @@ fn restart_doesnt_affect_peers_downloading_finality_data() { // 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 mut sync = ChainSync::new( + ChainSyncMode::Full, + client.clone(), + 1, + 8, + ProtocolName::Static(""), + None, + std::iter::empty(), + ) + .unwrap(); let peer_id1 = PeerId::random(); let peer_id2 = PeerId::random(); @@ -128,10 +142,10 @@ fn restart_doesnt_affect_peers_downloading_finality_data() { // we wil send block requests to these peers // for these blocks we don't know about - let actions = sync.actions().collect::>(); + let actions = sync.actions().unwrap(); assert_eq!(actions.len(), 2); assert!(actions.iter().all(|action| match action { - ChainSyncAction::SendBlockRequest { peer_id, .. } => + SyncingAction::SendBlockRequest { peer_id, .. } => peer_id == &peer_id1 || peer_id == &peer_id2, _ => false, })); @@ -162,15 +176,15 @@ fn restart_doesnt_affect_peers_downloading_finality_data() { sync.restart(); // which should make us cancel and send out again block requests to the first two peers - let actions = sync.actions().collect::>(); + let actions = sync.actions().unwrap(); assert_eq!(actions.len(), 4); let mut cancelled_first = HashSet::new(); assert!(actions.iter().all(|action| match action { - ChainSyncAction::CancelRequest { peer_id, .. } => { + SyncingAction::CancelRequest { peer_id, .. } => { cancelled_first.insert(peer_id); peer_id == &peer_id1 || peer_id == &peer_id2 }, - ChainSyncAction::SendBlockRequest { peer_id, .. } => { + SyncingAction::SendBlockRequest { peer_id, .. } => { assert!(cancelled_first.remove(peer_id)); peer_id == &peer_id1 || peer_id == &peer_id2 }, @@ -291,9 +305,16 @@ fn do_ancestor_search_when_common_block_to_best_queued_gap_is_to_big() { let client = Arc::new(TestClientBuilder::new().build()); let info = client.info(); - let mut sync = - ChainSync::new(ChainSyncMode::Full, client.clone(), 5, 64, None, std::iter::empty()) - .unwrap(); + let mut sync = ChainSync::new( + ChainSyncMode::Full, + client.clone(), + 5, + 64, + ProtocolName::Static(""), + None, + std::iter::empty(), + ) + .unwrap(); let peer_id1 = PeerId::random(); let peer_id2 = PeerId::random(); @@ -329,7 +350,7 @@ fn do_ancestor_search_when_common_block_to_best_queued_gap_is_to_big() { assert_eq!(actions.len(), 1); assert!(matches!( &actions[0], - ChainSyncAction::ImportBlocks{ origin: _, blocks } if blocks.len() == max_blocks_to_request as usize, + SyncingAction::ImportBlocks{ origin: _, blocks } if blocks.len() == max_blocks_to_request as usize, )); best_block_num += max_blocks_to_request as u32; @@ -438,9 +459,16 @@ fn can_sync_huge_fork() { let info = client.info(); - let mut sync = - ChainSync::new(ChainSyncMode::Full, client.clone(), 5, 64, None, std::iter::empty()) - .unwrap(); + let mut sync = ChainSync::new( + ChainSyncMode::Full, + client.clone(), + 5, + 64, + ProtocolName::Static(""), + 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()); @@ -476,7 +504,7 @@ fn can_sync_huge_fork() { } else { assert_eq!(actions.len(), 1); match &actions[0] { - ChainSyncAction::SendBlockRequest { peer_id: _, request } => request.clone(), + SyncingAction::SendBlockRequest { peer_id: _, request, key: _ } => request.clone(), action @ _ => panic!("Unexpected action: {action:?}"), } }; @@ -508,7 +536,7 @@ fn can_sync_huge_fork() { assert_eq!(actions.len(), 1); assert!(matches!( &actions[0], - ChainSyncAction::ImportBlocks{ origin: _, blocks } if blocks.len() == sync.max_blocks_per_request as usize + SyncingAction::ImportBlocks{ origin: _, blocks } if blocks.len() == sync.max_blocks_per_request as usize )); best_block_num += sync.max_blocks_per_request as u32; @@ -572,9 +600,16 @@ fn syncs_fork_without_duplicate_requests() { let info = client.info(); - let mut sync = - ChainSync::new(ChainSyncMode::Full, client.clone(), 5, 64, None, std::iter::empty()) - .unwrap(); + let mut sync = ChainSync::new( + ChainSyncMode::Full, + client.clone(), + 5, + 64, + ProtocolName::Static(""), + 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()); @@ -610,7 +645,7 @@ fn syncs_fork_without_duplicate_requests() { } else { assert_eq!(actions.len(), 1); match &actions[0] { - ChainSyncAction::SendBlockRequest { peer_id: _, request } => request.clone(), + SyncingAction::SendBlockRequest { peer_id: _, request, key: _ } => request.clone(), action @ _ => panic!("Unexpected action: {action:?}"), } }; @@ -646,7 +681,7 @@ fn syncs_fork_without_duplicate_requests() { assert_eq!(actions.len(), 1); assert!(matches!( &actions[0], - ChainSyncAction::ImportBlocks{ origin: _, blocks } if blocks.len() == max_blocks_to_request as usize + SyncingAction::ImportBlocks{ origin: _, blocks } if blocks.len() == max_blocks_to_request as usize )); best_block_num += max_blocks_to_request as u32; @@ -709,9 +744,16 @@ fn removes_target_fork_on_disconnect() { let client = Arc::new(TestClientBuilder::new().build()); let blocks = (0..3).map(|_| build_block(&client, None, false)).collect::>(); - let mut sync = - ChainSync::new(ChainSyncMode::Full, client.clone(), 1, 64, None, std::iter::empty()) - .unwrap(); + let mut sync = ChainSync::new( + ChainSyncMode::Full, + client.clone(), + 1, + 64, + ProtocolName::Static(""), + None, + std::iter::empty(), + ) + .unwrap(); let peer_id1 = PeerId::random(); let common_block = blocks[1].clone(); @@ -736,9 +778,16 @@ 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, std::iter::empty()) - .unwrap(); + let mut sync = ChainSync::new( + ChainSyncMode::Full, + empty_client.clone(), + 1, + 64, + ProtocolName::Static(""), + None, + std::iter::empty(), + ) + .unwrap(); let peer_id1 = PeerId::random(); let best_block = blocks[3].clone(); @@ -769,9 +818,16 @@ fn ancestor_search_repeat() { #[test] fn sync_restart_removes_block_but_not_justification_requests() { let client = Arc::new(TestClientBuilder::new().build()); - let mut sync = - ChainSync::new(ChainSyncMode::Full, client.clone(), 1, 64, None, std::iter::empty()) - .unwrap(); + let mut sync = ChainSync::new( + ChainSyncMode::Full, + client.clone(), + 1, + 64, + ProtocolName::Static(""), + None, + std::iter::empty(), + ) + .unwrap(); let peers = vec![PeerId::random(), PeerId::random()]; @@ -839,10 +895,10 @@ fn sync_restart_removes_block_but_not_justification_requests() { let actions = sync.take_actions().collect::>(); for action in actions.iter() { match action { - ChainSyncAction::CancelRequest { peer_id } => { + SyncingAction::CancelRequest { peer_id, key: _ } => { pending_responses.remove(&peer_id); }, - ChainSyncAction::SendBlockRequest { peer_id, .. } => { + SyncingAction::SendBlockRequest { peer_id, .. } => { // we drop obsolete response, but don't register a new request, it's checked in // the `assert!` below pending_responses.remove(&peer_id); @@ -852,7 +908,7 @@ fn sync_restart_removes_block_but_not_justification_requests() { } assert!(actions.iter().any(|action| { match action { - ChainSyncAction::SendBlockRequest { peer_id, .. } => peer_id == &peers[0], + SyncingAction::SendBlockRequest { peer_id, .. } => peer_id == &peers[0], _ => false, } })); @@ -913,9 +969,16 @@ fn request_across_forks() { fork_blocks }; - let mut sync = - ChainSync::new(ChainSyncMode::Full, client.clone(), 5, 64, None, std::iter::empty()) - .unwrap(); + let mut sync = ChainSync::new( + ChainSyncMode::Full, + client.clone(), + 5, + 64, + ProtocolName::Static(""), + None, + std::iter::empty(), + ) + .unwrap(); // Add the peers, all at the common ancestor 100. let common_block = blocks.last().unwrap(); @@ -943,7 +1006,7 @@ fn request_across_forks() { assert_eq!(actions.len(), 1); assert!(matches!( &actions[0], - ChainSyncAction::ImportBlocks{ origin: _, blocks } if blocks.len() == 7_usize + SyncingAction::ImportBlocks{ origin: _, blocks } if blocks.len() == 7_usize )); assert_eq!(sync.best_queued_number, 107); assert_eq!(sync.best_queued_hash, block.hash()); @@ -988,7 +1051,7 @@ fn request_across_forks() { assert_eq!(actions.len(), 1); assert!(matches!( &actions[0], - ChainSyncAction::ImportBlocks{ origin: _, blocks } if blocks.len() == 1_usize + SyncingAction::ImportBlocks{ origin: _, blocks } if blocks.len() == 1_usize )); assert!(sync.is_known(&block.header.parent_hash())); } diff --git a/substrate/client/network/sync/src/strategy/state.rs b/substrate/client/network/sync/src/strategy/state.rs index 6f06f238fe3a610adedfd992523ef62a2e79d68f..a04ab8be4fea5b182da5fa855b18271833f70b20 100644 --- a/substrate/client/network/sync/src/strategy/state.rs +++ b/substrate/client/network/sync/src/strategy/state.rs @@ -30,6 +30,7 @@ use crate::{ use log::{debug, error, trace}; use sc_client_api::ProofProvider; use sc_consensus::{BlockImportError, BlockImportStatus, IncomingBlock}; +use sc_network::ProtocolName; use sc_network_common::sync::message::BlockAnnounce; use sc_network_types::PeerId; use sp_consensus::BlockOrigin; @@ -52,7 +53,7 @@ mod rep { /// Action that should be performed on [`StateStrategy`]'s behalf. pub enum StateStrategyAction { /// Send state request to peer. - SendStateRequest { peer_id: PeerId, request: OpaqueStateRequest }, + SendStateRequest { peer_id: PeerId, protocol_name: ProtocolName, request: OpaqueStateRequest }, /// Disconnect and report peer. DropPeer(BadPeer), /// Import blocks. @@ -83,6 +84,7 @@ pub struct StateStrategy { peers: HashMap>, disconnected_peers: DisconnectedPeers, actions: Vec>, + protocol_name: ProtocolName, succeeded: bool, } @@ -95,6 +97,7 @@ impl StateStrategy { target_justifications: Option, skip_proof: bool, initial_peers: impl Iterator)>, + protocol_name: ProtocolName, ) -> Self where Client: ProofProvider + Send + Sync + 'static, @@ -115,6 +118,7 @@ impl StateStrategy { peers, disconnected_peers: DisconnectedPeers::new(), actions: Vec::new(), + protocol_name, succeeded: false, } } @@ -125,6 +129,7 @@ impl StateStrategy { fn new_with_provider( state_sync_provider: Box>, initial_peers: impl Iterator)>, + protocol_name: ProtocolName, ) -> Self { Self { state_sync: state_sync_provider, @@ -135,6 +140,7 @@ impl StateStrategy { .collect(), disconnected_peers: DisconnectedPeers::new(), actions: Vec::new(), + protocol_name, succeeded: false, } } @@ -349,10 +355,13 @@ impl StateStrategy { /// Get actions that should be performed by the owner on [`WarpSync`]'s behalf #[must_use] pub fn actions(&mut self) -> impl Iterator> { - let state_request = self - .state_request() - .into_iter() - .map(|(peer_id, request)| StateStrategyAction::SendStateRequest { peer_id, request }); + let state_request = self.state_request().into_iter().map(|(peer_id, request)| { + StateStrategyAction::SendStateRequest { + peer_id, + protocol_name: self.protocol_name.clone(), + request, + } + }); self.actions.extend(state_request); std::mem::take(&mut self.actions).into_iter() @@ -409,8 +418,15 @@ mod test { .block; let target_header = target_block.header().clone(); - let mut state_strategy = - StateStrategy::new(client, target_header, None, None, false, std::iter::empty()); + let mut state_strategy = StateStrategy::new( + client, + target_header, + None, + None, + false, + std::iter::empty(), + ProtocolName::Static(""), + ); assert!(state_strategy .schedule_next_peer(PeerState::DownloadingState, Zero::zero()) @@ -442,6 +458,7 @@ mod test { None, false, initial_peers, + ProtocolName::Static(""), ); let peer_id = @@ -475,6 +492,7 @@ mod test { None, false, initial_peers, + ProtocolName::Static(""), ); let peer_id = state_strategy.schedule_next_peer(PeerState::DownloadingState, 10); @@ -508,6 +526,7 @@ mod test { None, false, initial_peers, + ProtocolName::Static(""), ); // Disconnecting a peer without an inflight request has no effect on persistent states. @@ -557,6 +576,7 @@ mod test { None, false, initial_peers, + ProtocolName::Static(""), ); let (_peer_id, mut opaque_request) = state_strategy.state_request().unwrap(); @@ -587,6 +607,7 @@ mod test { None, false, initial_peers, + ProtocolName::Static(""), ); // First request is sent. @@ -602,8 +623,11 @@ mod test { state_sync_provider.expect_import().return_once(|_| ImportResult::Continue); let peer_id = PeerId::random(); let initial_peers = std::iter::once((peer_id, 10)); - let mut state_strategy = - StateStrategy::new_with_provider(Box::new(state_sync_provider), initial_peers); + let mut state_strategy = StateStrategy::new_with_provider( + Box::new(state_sync_provider), + initial_peers, + ProtocolName::Static(""), + ); // Manually set the peer's state. state_strategy.peers.get_mut(&peer_id).unwrap().state = PeerState::DownloadingState; @@ -620,8 +644,11 @@ mod test { state_sync_provider.expect_import().return_once(|_| ImportResult::BadResponse); let peer_id = PeerId::random(); let initial_peers = std::iter::once((peer_id, 10)); - let mut state_strategy = - StateStrategy::new_with_provider(Box::new(state_sync_provider), initial_peers); + let mut state_strategy = StateStrategy::new_with_provider( + Box::new(state_sync_provider), + initial_peers, + ProtocolName::Static(""), + ); // Manually set the peer's state. state_strategy.peers.get_mut(&peer_id).unwrap().state = PeerState::DownloadingState; let dummy_response = OpaqueStateResponse(Box::new(StateResponse::default())); @@ -639,8 +666,11 @@ mod test { state_sync_provider.expect_import().return_once(|_| ImportResult::Continue); let peer_id = PeerId::random(); let initial_peers = std::iter::once((peer_id, 10)); - let mut state_strategy = - StateStrategy::new_with_provider(Box::new(state_sync_provider), initial_peers); + let mut state_strategy = StateStrategy::new_with_provider( + Box::new(state_sync_provider), + initial_peers, + ProtocolName::Static(""), + ); // Manually set the peer's state . state_strategy.peers.get_mut(&peer_id).unwrap().state = PeerState::DownloadingState; @@ -698,8 +728,11 @@ mod test { // Prepare `StateStrategy`. let peer_id = PeerId::random(); let initial_peers = std::iter::once((peer_id, 10)); - let mut state_strategy = - StateStrategy::new_with_provider(Box::new(state_sync_provider), initial_peers); + let mut state_strategy = StateStrategy::new_with_provider( + Box::new(state_sync_provider), + initial_peers, + ProtocolName::Static(""), + ); // Manually set the peer's state . state_strategy.peers.get_mut(&peer_id).unwrap().state = PeerState::DownloadingState; @@ -722,8 +755,11 @@ mod test { let mut state_sync_provider = MockStateSync::::new(); state_sync_provider.expect_target_hash().return_const(target_hash); - let mut state_strategy = - StateStrategy::new_with_provider(Box::new(state_sync_provider), std::iter::empty()); + let mut state_strategy = StateStrategy::new_with_provider( + Box::new(state_sync_provider), + std::iter::empty(), + ProtocolName::Static(""), + ); // Unknown block imported. state_strategy.on_blocks_processed( @@ -745,8 +781,11 @@ mod test { let mut state_sync_provider = MockStateSync::::new(); state_sync_provider.expect_target_hash().return_const(target_hash); - let mut state_strategy = - StateStrategy::new_with_provider(Box::new(state_sync_provider), std::iter::empty()); + let mut state_strategy = StateStrategy::new_with_provider( + Box::new(state_sync_provider), + std::iter::empty(), + ProtocolName::Static(""), + ); // Target block imported. state_strategy.on_blocks_processed( @@ -769,8 +808,11 @@ mod test { let mut state_sync_provider = MockStateSync::::new(); state_sync_provider.expect_target_hash().return_const(target_hash); - let mut state_strategy = - StateStrategy::new_with_provider(Box::new(state_sync_provider), std::iter::empty()); + let mut state_strategy = StateStrategy::new_with_provider( + Box::new(state_sync_provider), + std::iter::empty(), + ProtocolName::Static(""), + ); // Target block import failed. state_strategy.on_blocks_processed( @@ -797,8 +839,11 @@ mod test { // Get enough peers for possible spurious requests. let initial_peers = (1..=10).map(|best_number| (PeerId::random(), best_number)); - let mut state_strategy = - StateStrategy::new_with_provider(Box::new(state_sync_provider), initial_peers); + let mut state_strategy = StateStrategy::new_with_provider( + Box::new(state_sync_provider), + initial_peers, + ProtocolName::Static(""), + ); state_strategy.on_blocks_processed( 1, diff --git a/substrate/client/network/sync/src/strategy/warp.rs b/substrate/client/network/sync/src/strategy/warp.rs index 99405c2e5f08bd5e7845c97e66db6cfed2c972da..cce6a93caf43dc3e2b31ff33f6b76fe422732d8c 100644 --- a/substrate/client/network/sync/src/strategy/warp.rs +++ b/substrate/client/network/sync/src/strategy/warp.rs @@ -26,7 +26,8 @@ use crate::{ LOG_TARGET, }; use codec::{Decode, Encode}; -use log::{debug, error, trace}; +use log::{debug, error, trace, warn}; +use sc_network::ProtocolName; use sc_network_common::sync::message::{ BlockAnnounce, BlockAttributes, BlockData, BlockRequest, Direction, FromBlock, }; @@ -188,7 +189,11 @@ struct Peer { /// Action that should be performed on [`WarpSync`]'s behalf. pub enum WarpSyncAction { /// Send warp proof request to peer. - SendWarpProofRequest { peer_id: PeerId, request: WarpProofRequest }, + SendWarpProofRequest { + peer_id: PeerId, + protocol_name: ProtocolName, + request: WarpProofRequest, + }, /// Send block request to peer. Always implies dropping a stale block request to the same peer. SendBlockRequest { peer_id: PeerId, request: BlockRequest }, /// Disconnect and report peer. @@ -211,6 +216,7 @@ pub struct WarpSync { total_state_bytes: u64, peers: HashMap>, disconnected_peers: DisconnectedPeers, + protocol_name: Option, actions: Vec>, result: Option>, } @@ -223,7 +229,11 @@ where /// Create a new instance. When passing a warp sync provider we will be checking for proof and /// authorities. Alternatively we can pass a target block when we want to skip downloading /// proofs, in this case we will continue polling until the target block is known. - pub fn new(client: Arc, warp_sync_config: WarpSyncConfig) -> Self { + pub fn new( + client: Arc, + warp_sync_config: WarpSyncConfig, + protocol_name: Option, + ) -> Self { if client.info().finalized_state.is_some() { error!( target: LOG_TARGET, @@ -236,6 +246,7 @@ where total_state_bytes: 0, peers: HashMap::new(), disconnected_peers: DisconnectedPeers::new(), + protocol_name, actions: vec![WarpSyncAction::Finished], result: None, } @@ -254,6 +265,7 @@ where total_state_bytes: 0, peers: HashMap::new(), disconnected_peers: DisconnectedPeers::new(), + protocol_name, actions: Vec::new(), result: None, } @@ -469,7 +481,7 @@ where } /// Produce warp proof request. - fn warp_proof_request(&mut self) -> Option<(PeerId, WarpProofRequest)> { + fn warp_proof_request(&mut self) -> Option<(PeerId, ProtocolName, WarpProofRequest)> { let Phase::WarpProof { last_hash, .. } = &self.phase else { return None }; // Copy `last_hash` early to cut the borrowing tie. @@ -487,7 +499,17 @@ where let peer_id = self.schedule_next_peer(PeerState::DownloadingProofs, None)?; trace!(target: LOG_TARGET, "New WarpProofRequest to {peer_id}, begin hash: {begin}."); - Some((peer_id, WarpProofRequest { begin })) + let request = WarpProofRequest { begin }; + + let Some(protocol_name) = self.protocol_name.clone() else { + warn!( + target: LOG_TARGET, + "Trying to send warp sync request when no protocol is configured {request:?}", + ); + return None; + }; + + Some((peer_id, protocol_name, request)) } /// Produce target block request. @@ -585,10 +607,10 @@ where /// Get actions that should be performed by the owner on [`WarpSync`]'s behalf #[must_use] pub fn actions(&mut self) -> impl Iterator> { - let warp_proof_request = self - .warp_proof_request() - .into_iter() - .map(|(peer_id, request)| WarpSyncAction::SendWarpProofRequest { peer_id, request }); + let warp_proof_request = + self.warp_proof_request().into_iter().map(|(peer_id, protocol_name, request)| { + WarpSyncAction::SendWarpProofRequest { peer_id, protocol_name, request } + }); self.actions.extend(warp_proof_request); let target_block_request = self @@ -694,7 +716,7 @@ mod test { let client = mock_client_with_state(); let provider = MockWarpSyncProvider::::new(); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(Arc::new(client), config); + let mut warp_sync = WarpSync::new(Arc::new(client), config, None); // Warp sync instantly finishes let actions = warp_sync.actions().collect::>(); @@ -715,7 +737,7 @@ mod test { Default::default(), Default::default(), )); - let mut warp_sync = WarpSync::new(Arc::new(client), config); + let mut warp_sync = WarpSync::new(Arc::new(client), config, None); // Warp sync instantly finishes let actions = warp_sync.actions().collect::>(); @@ -731,7 +753,7 @@ mod test { let client = mock_client_without_state(); let provider = MockWarpSyncProvider::::new(); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(Arc::new(client), config); + let mut warp_sync = WarpSync::new(Arc::new(client), config, None); // No actions are emitted. assert_eq!(warp_sync.actions().count(), 0) @@ -747,7 +769,7 @@ mod test { Default::default(), Default::default(), )); - let mut warp_sync = WarpSync::new(Arc::new(client), config); + let mut warp_sync = WarpSync::new(Arc::new(client), config, None); // No actions are emitted. assert_eq!(warp_sync.actions().count(), 0) @@ -762,7 +784,7 @@ mod test { .once() .return_const(AuthorityList::default()); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(Arc::new(client), config); + let mut warp_sync = WarpSync::new(Arc::new(client), config, None); // Warp sync is not started when there is not enough peers. for _ in 0..(MIN_PEERS_TO_START_WARP_SYNC - 1) { @@ -780,7 +802,7 @@ mod test { let client = mock_client_without_state(); let provider = MockWarpSyncProvider::::new(); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(Arc::new(client), config); + let mut warp_sync = WarpSync::new(Arc::new(client), config, None); assert!(warp_sync.schedule_next_peer(PeerState::DownloadingProofs, None).is_none()); } @@ -804,7 +826,7 @@ mod test { .once() .return_const(AuthorityList::default()); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(Arc::new(client), config); + let mut warp_sync = WarpSync::new(Arc::new(client), config, None); for best_number in 1..11 { warp_sync.add_peer(PeerId::random(), Hash::random(), best_number); @@ -825,7 +847,7 @@ mod test { .once() .return_const(AuthorityList::default()); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(Arc::new(client), config); + let mut warp_sync = WarpSync::new(Arc::new(client), config, None); for best_number in 1..11 { warp_sync.add_peer(PeerId::random(), Hash::random(), best_number); @@ -845,7 +867,7 @@ mod test { .once() .return_const(AuthorityList::default()); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(Arc::new(client), config); + let mut warp_sync = WarpSync::new(Arc::new(client), config, None); for best_number in 1..11 { warp_sync.add_peer(PeerId::random(), Hash::random(), best_number); @@ -889,7 +911,7 @@ mod test { .once() .return_const(AuthorityList::default()); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(Arc::new(client), config); + let mut warp_sync = WarpSync::new(Arc::new(client), config, Some(ProtocolName::Static(""))); // Make sure we have enough peers to make a request. for best_number in 1..11 { @@ -918,7 +940,7 @@ mod test { .once() .return_const(AuthorityList::default()); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(Arc::new(client), config); + let mut warp_sync = WarpSync::new(Arc::new(client), config, Some(ProtocolName::Static(""))); // Make sure we have enough peers to make a request. for best_number in 1..11 { @@ -936,7 +958,7 @@ mod test { _ => panic!("Invalid phase."), } - let (_peer_id, request) = warp_sync.warp_proof_request().unwrap(); + let (_peer_id, _protocol_name, request) = warp_sync.warp_proof_request().unwrap(); assert_eq!(request.begin, known_last_hash); } @@ -949,7 +971,7 @@ mod test { .once() .return_const(AuthorityList::default()); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(Arc::new(client), config); + let mut warp_sync = WarpSync::new(Arc::new(client), config, Some(ProtocolName::Static(""))); // Make sure we have enough peers to make requests. for best_number in 1..11 { @@ -976,7 +998,7 @@ mod test { Err(Box::new(std::io::Error::new(ErrorKind::Other, "test-verification-failure"))) }); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(Arc::new(client), config); + let mut warp_sync = WarpSync::new(Arc::new(client), config, Some(ProtocolName::Static(""))); // Make sure we have enough peers to make a request. for best_number in 1..11 { @@ -1017,7 +1039,7 @@ mod test { Ok(VerificationResult::Partial(set_id, authorities, Hash::random())) }); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(Arc::new(client), config); + let mut warp_sync = WarpSync::new(Arc::new(client), config, Some(ProtocolName::Static(""))); // Make sure we have enough peers to make a request. for best_number in 1..11 { @@ -1061,7 +1083,7 @@ mod test { Ok(VerificationResult::Complete(set_id, authorities, target_header)) }); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(client, config); + let mut warp_sync = WarpSync::new(client, config, Some(ProtocolName::Static(""))); // Make sure we have enough peers to make a request. for best_number in 1..11 { @@ -1094,7 +1116,7 @@ mod test { .once() .return_const(AuthorityList::default()); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(Arc::new(client), config); + let mut warp_sync = WarpSync::new(Arc::new(client), config, None); // Make sure we have enough peers to make a request. for best_number in 1..11 { @@ -1129,7 +1151,7 @@ mod test { Ok(VerificationResult::Complete(set_id, authorities, target_header)) }); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(client, config); + let mut warp_sync = WarpSync::new(client, config, None); // Make sure we have enough peers to make a request. for best_number in 1..11 { @@ -1161,7 +1183,7 @@ mod test { .block; let target_header = target_block.header().clone(); let config = WarpSyncConfig::WithTarget(target_header); - let mut warp_sync = WarpSync::new(client, config); + let mut warp_sync = WarpSync::new(client, config, None); // Make sure we have enough peers to make a request. for best_number in 1..11 { @@ -1201,7 +1223,7 @@ mod test { Ok(VerificationResult::Complete(set_id, authorities, target_header)) }); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(client, config); + let mut warp_sync = WarpSync::new(client, config, None); // Make sure we have enough peers to make a request. for best_number in 1..11 { @@ -1239,7 +1261,7 @@ mod test { Ok(VerificationResult::Complete(set_id, authorities, target_header)) }); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(client, config); + let mut warp_sync = WarpSync::new(client, config, None); // Make sure we have enough peers to make a request. for best_number in 1..11 { @@ -1293,7 +1315,7 @@ mod test { Ok(VerificationResult::Complete(set_id, authorities, target_header)) }); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(client, config); + let mut warp_sync = WarpSync::new(client, config, None); // Make sure we have enough peers to make a request. for best_number in 1..11 { @@ -1370,7 +1392,7 @@ mod test { Ok(VerificationResult::Complete(set_id, authorities, target_header)) }); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(client, config); + let mut warp_sync = WarpSync::new(client, config, None); // Make sure we have enough peers to make a request. for best_number in 1..11 { @@ -1423,7 +1445,7 @@ mod test { Ok(VerificationResult::Complete(set_id, authorities, target_header)) }); let config = WarpSyncConfig::WithProvider(Arc::new(provider)); - let mut warp_sync = WarpSync::new(client, config); + let mut warp_sync = WarpSync::new(client, config, None); // Make sure we have enough peers to make a request. for best_number in 1..11 { diff --git a/substrate/client/network/sync/src/warp_request_handler.rs b/substrate/client/network/sync/src/warp_request_handler.rs index 371b04ec9e4d50a009d1a62e372ebf471ed0e243..8d0b757ff821c47814c505d907268f1aa93f0220 100644 --- a/substrate/client/network/sync/src/warp_request_handler.rs +++ b/substrate/client/network/sync/src/warp_request_handler.rs @@ -27,14 +27,12 @@ use crate::{ use sc_network::{ config::ProtocolId, request_responses::{IncomingRequest, OutgoingResponse}, - NetworkBackend, + NetworkBackend, MAX_RESPONSE_SIZE, }; use sp_runtime::traits::Block as BlockT; use std::{sync::Arc, time::Duration}; -const MAX_RESPONSE_SIZE: u64 = 16 * 1024 * 1024; - /// Incoming warp requests bounded queue size. const MAX_WARP_REQUEST_QUEUE: usize = 20; diff --git a/substrate/client/network/test/src/lib.rs b/substrate/client/network/test/src/lib.rs index f84f353fb4a024ac57d080bf72c09ef2c31f15f5..06e243342fb2dd4a9f8fac8680545ef481c66b66 100644 --- a/substrate/client/network/test/src/lib.rs +++ b/substrate/client/network/test/src/lib.rs @@ -66,8 +66,12 @@ use sc_network_sync::{ block_request_handler::BlockRequestHandler, service::{network::NetworkServiceProvider, syncing_service::SyncingService}, state_request_handler::StateRequestHandler, - strategy::warp::{ - AuthorityList, EncodedProof, SetId, VerificationResult, WarpSyncConfig, WarpSyncProvider, + strategy::{ + warp::{ + AuthorityList, EncodedProof, SetId, VerificationResult, WarpSyncConfig, + WarpSyncProvider, + }, + PolkadotSyncingStrategy, SyncingConfig, }, warp_request_handler, }; @@ -624,9 +628,8 @@ struct VerifierAdapter { impl Verifier for VerifierAdapter { async fn verify(&self, block: BlockImportParams) -> Result, String> { let hash = block.header.hash(); - self.verifier.lock().await.verify(block).await.map_err(|e| { + self.verifier.lock().await.verify(block).await.inspect_err(|e| { self.failed_verifications.lock().insert(hash, e.clone()); - e }) } } @@ -905,6 +908,24 @@ pub trait TestNetFactory: Default + Sized + Send { ::Hash, >>::register_notification_metrics(None); + let syncing_config = SyncingConfig { + mode: network_config.sync_mode, + max_parallel_downloads: network_config.max_parallel_downloads, + max_blocks_per_request: network_config.max_blocks_per_request, + metrics_registry: None, + state_request_protocol_name: state_request_protocol_config.name.clone(), + }; + // Initialize syncing strategy. + let syncing_strategy = Box::new( + PolkadotSyncingStrategy::new( + syncing_config, + client.clone(), + Some(warp_sync_config), + Some(warp_protocol_config.name.clone()), + ) + .unwrap(), + ); + let (engine, sync_service, block_announce_config) = sc_network_sync::engine::SyncingEngine::new( Roles::from(if config.is_authority { &Role::Authority } else { &Role::Full }), @@ -915,12 +936,10 @@ pub trait TestNetFactory: Default + Sized + Send { protocol_id.clone(), &fork_id, block_announce_validator, - Some(warp_sync_config), + syncing_strategy, chain_sync_network_handle, import_queue.service(), block_relay_params.downloader, - state_request_protocol_config.name.clone(), - Some(warp_protocol_config.name.clone()), peer_store_handle.clone(), ) .unwrap(); diff --git a/substrate/client/network/test/src/service.rs b/substrate/client/network/test/src/service.rs index a5cee97531ca59ad99b6176a0c99bd7969edb991..ad2d1d9ec24de96b00846127b6bb0e71638429b4 100644 --- a/substrate/client/network/test/src/service.rs +++ b/substrate/client/network/test/src/service.rs @@ -34,6 +34,7 @@ use sc_network_sync::{ engine::SyncingEngine, service::network::{NetworkServiceHandle, NetworkServiceProvider}, state_request_handler::StateRequestHandler, + strategy::{PolkadotSyncingStrategy, SyncingConfig}, }; use sp_blockchain::HeaderBackend; use sp_runtime::traits::{Block as BlockT, Zero}; @@ -202,6 +203,18 @@ impl TestNetworkBuilder { let peer_store_handle: Arc = Arc::new(peer_store.handle()); tokio::spawn(peer_store.run().boxed()); + let syncing_config = SyncingConfig { + mode: network_config.sync_mode, + max_parallel_downloads: network_config.max_parallel_downloads, + max_blocks_per_request: network_config.max_blocks_per_request, + metrics_registry: None, + state_request_protocol_name: state_request_protocol_config.name.clone(), + }; + // Initialize syncing strategy. + let syncing_strategy = Box::new( + PolkadotSyncingStrategy::new(syncing_config, client.clone(), None, None).unwrap(), + ); + let (engine, chain_sync_service, block_announce_config) = SyncingEngine::new( Roles::from(&config::Role::Full), client.clone(), @@ -211,12 +224,10 @@ impl TestNetworkBuilder { protocol_id.clone(), &None, Box::new(sp_consensus::block_validation::DefaultBlockAnnounceValidator), - None, + syncing_strategy, chain_sync_network_handle, import_queue.service(), block_relay_params.downloader, - state_request_protocol_config.name.clone(), - None, Arc::clone(&peer_store_handle), ) .unwrap(); diff --git a/substrate/client/network/test/src/sync.rs b/substrate/client/network/test/src/sync.rs index 4244c49bf7fb38a42339102836bc0c206d4d48de..91307d8692816254167a5e329d069d36255f4619 100644 --- a/substrate/client/network/test/src/sync.rs +++ b/substrate/client/network/test/src/sync.rs @@ -749,24 +749,6 @@ async fn sync_blocks_when_block_announce_validator_says_it_is_new_best() { } } -/// Waits for some time until the validation is successful. -struct DeferredBlockAnnounceValidator; - -impl BlockAnnounceValidator for DeferredBlockAnnounceValidator { - fn validate( - &mut self, - _: &Header, - _: &[u8], - ) -> Pin>> + Send>> - { - async { - futures_timer::Delay::new(std::time::Duration::from_millis(500)).await; - Ok(Validation::Success { is_new_best: false }) - } - .boxed() - } -} - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn wait_until_deferred_block_announce_validation_is_ready() { sp_tracing::try_init_simple(); diff --git a/substrate/client/network/transactions/src/config.rs b/substrate/client/network/transactions/src/config.rs index fdf81fcd9ff47123b2cd07d1307158b755436a69..239b76b51485f128f2a374575c0ffee352929e48 100644 --- a/substrate/client/network/transactions/src/config.rs +++ b/substrate/client/network/transactions/src/config.rs @@ -19,6 +19,7 @@ //! Configuration of the transaction protocol use futures::prelude::*; +use sc_network::MAX_RESPONSE_SIZE; use sc_network_common::ExHashT; use sp_runtime::traits::Block as BlockT; use std::{collections::HashMap, future::Future, pin::Pin, time}; @@ -32,7 +33,7 @@ pub(crate) const PROPAGATE_TIMEOUT: time::Duration = time::Duration::from_millis pub(crate) const MAX_KNOWN_TRANSACTIONS: usize = 10240; // ~300kb per peer + overhead. /// Maximum allowed size for a transactions notification. -pub(crate) const MAX_TRANSACTIONS_SIZE: u64 = 16 * 1024 * 1024; +pub(crate) const MAX_TRANSACTIONS_SIZE: u64 = MAX_RESPONSE_SIZE; /// Maximum number of transaction validation request we keep at any moment. pub(crate) const MAX_PENDING_TRANSACTIONS: usize = 8192; diff --git a/substrate/client/offchain/Cargo.toml b/substrate/client/offchain/Cargo.toml index 4b5b04cca627be105506ff63f7a81df8cbe45f9b..bbbe7018d1060e5e87ed94dca9b1afabab978cc9 100644 --- a/substrate/client/offchain/Cargo.toml +++ b/substrate/client/offchain/Cargo.toml @@ -22,7 +22,10 @@ codec = { features = ["derive"], workspace = true, default-features = true } fnv = { workspace = true } futures = { workspace = true } futures-timer = { workspace = true } -hyperv14 = { features = ["http2", "stream"], workspace = true, default-features = true } +hyperv14 = { features = [ + "http2", + "stream", +], workspace = true, default-features = true } hyper-rustls = { features = ["http2"], workspace = true } num_cpus = { workspace = true } once_cell = { workspace = true } @@ -46,7 +49,6 @@ log = { workspace = true, default-features = true } [dev-dependencies] async-trait = { workspace = true } -lazy_static = { workspace = true } tokio = { workspace = true, default-features = true } sc-block-builder = { workspace = true, default-features = true } sc-client-db = { default-features = true, workspace = true } diff --git a/substrate/client/offchain/src/api/http.rs b/substrate/client/offchain/src/api/http.rs index fda5728b0d03e5dd2445d7f88f85c6987f5bf399..73407b1359d772cb28071fa0ba2f6e35b8b47f00 100644 --- a/substrate/client/offchain/src/api/http.rs +++ b/substrate/client/offchain/src/api/http.rs @@ -763,14 +763,12 @@ mod tests { use crate::api::timestamp; use core::convert::Infallible; use futures::{future, StreamExt}; - use lazy_static::lazy_static; use sp_core::offchain::{Duration, Externalities, HttpError, HttpRequestId, HttpRequestStatus}; + use std::sync::LazyLock; - // Using lazy_static to avoid spawning lots of different SharedClients, + // Using LazyLock to avoid spawning lots of different SharedClients, // as spawning a SharedClient is CPU-intensive and opens lots of fds. - lazy_static! { - static ref SHARED_CLIENT: SharedClient = SharedClient::new(); - } + static SHARED_CLIENT: LazyLock = LazyLock::new(|| SharedClient::new()); // Returns an `HttpApi` whose worker is ran in the background, and a `SocketAddr` to an HTTP // server that runs in the background as well. diff --git a/substrate/client/rpc-servers/src/lib.rs b/substrate/client/rpc-servers/src/lib.rs index ca74c2371c257fbfd54784704452158061afa0ed..31e4042d81f27342a80b48587bb7ad130607edcc 100644 --- a/substrate/client/rpc-servers/src/lib.rs +++ b/substrate/client/rpc-servers/src/lib.rs @@ -23,14 +23,15 @@ pub mod middleware; pub mod utils; -use std::{error::Error as StdError, time::Duration}; +use std::{error::Error as StdError, net::SocketAddr, time::Duration}; use jsonrpsee::{ core::BoxError, - server::{serve_with_graceful_shutdown, stop_channel, ws, PingConfig, StopHandle}, + server::{ + serve_with_graceful_shutdown, stop_channel, ws, PingConfig, ServerHandle, StopHandle, + }, Methods, RpcModule, }; -use middleware::NodeHealthProxyLayer; use tower::Service; use utils::{ build_rpc_api, deny_unsafe, format_listen_addrs, get_proxy_ip, ListenAddrError, RpcSettings, @@ -41,13 +42,43 @@ pub use jsonrpsee::{ core::id_providers::{RandomIntegerIdProvider, RandomStringIdProvider}, server::{middleware::rpc::RpcServiceBuilder, BatchRequestConfig}, }; -pub use middleware::{Metrics, MiddlewareLayer, RpcMetrics}; +pub use middleware::{Metrics, MiddlewareLayer, NodeHealthProxyLayer, RpcMetrics}; pub use utils::{RpcEndpoint, RpcMethods}; const MEGABYTE: u32 = 1024 * 1024; -/// Type alias for the JSON-RPC server. -pub type Server = jsonrpsee::server::ServerHandle; +/// Type to encapsulate the server handle and listening address. +pub struct Server { + /// Handle to the rpc server + handle: ServerHandle, + /// Listening address of the server + listen_addrs: Vec, +} + +impl Server { + /// Creates a new Server. + pub fn new(handle: ServerHandle, listen_addrs: Vec) -> Server { + Server { handle, listen_addrs } + } + + /// Returns the `jsonrpsee::server::ServerHandle` for this Server. Can be used to stop the + /// server. + pub fn handle(&self) -> &ServerHandle { + &self.handle + } + + /// The listen address for the running RPC service. + pub fn listen_addrs(&self) -> &[SocketAddr] { + &self.listen_addrs + } +} + +impl Drop for Server { + fn drop(&mut self) { + // This doesn't not wait for the server to be stopped but fires the signal. + let _ = self.handle.stop(); + } +} /// Trait for providing subscription IDs that can be cloned. pub trait SubscriptionIdProvider: @@ -224,8 +255,9 @@ where ), }; - let rpc_middleware = - RpcServiceBuilder::new().option_layer(middleware_layer.clone()); + let rpc_middleware = RpcServiceBuilder::new() + .rpc_logger(1024) + .option_layer(middleware_layer.clone()); let mut svc = service_builder .set_rpc_middleware(rpc_middleware) .build(methods, stop_handle); @@ -273,5 +305,5 @@ where // This is to make it work with old scripts/utils that parse the logs. log::info!("Running JSON-RPC server: addr={}", format_listen_addrs(&local_addrs)); - Ok(server_handle) + Ok(Server::new(server_handle, local_addrs)) } diff --git a/substrate/client/rpc-servers/src/middleware/node_health.rs b/substrate/client/rpc-servers/src/middleware/node_health.rs index 69c9e0829ac98286e8ac4d5e6ddf55772862cd41..105199d9b4b777da0d6f894bc31d3b9d808d222c 100644 --- a/substrate/client/rpc-servers/src/middleware/node_health.rs +++ b/substrate/client/rpc-servers/src/middleware/node_health.rs @@ -98,17 +98,17 @@ where let fut = self.0.call(req); async move { - let res = fut.await.map_err(|err| err.into())?; - Ok(match maybe_intercept { InterceptRequest::Deny => http_response(StatusCode::METHOD_NOT_ALLOWED, HttpBody::empty()), - InterceptRequest::No => res, + InterceptRequest::No => fut.await.map_err(|err| err.into())?, InterceptRequest::Health => { + let res = fut.await.map_err(|err| err.into())?; let health = parse_rpc_response(res.into_body()).await?; http_ok_response(serde_json::to_string(&health)?) }, InterceptRequest::Readiness => { + let res = fut.await.map_err(|err| err.into())?; let health = parse_rpc_response(res.into_body()).await?; if (!health.is_syncing && health.peers > 0) || !health.should_have_peers { http_ok_response(HttpBody::empty()) diff --git a/substrate/client/rpc-servers/src/utils.rs b/substrate/client/rpc-servers/src/utils.rs index 5b4a4bf22b950a5b5096ca4b0b6adf6fe69f097d..d9b2db7af13351d248d72155cb6eb4fce4ee3866 100644 --- a/substrate/client/rpc-servers/src/utils.rs +++ b/substrate/client/rpc-servers/src/utils.rs @@ -284,7 +284,7 @@ pub(crate) fn get_proxy_ip(req: &http::Request) -> Option { /// Get the `deny_unsafe` setting based on the address and the RPC methods exposed by the interface. pub fn deny_unsafe(addr: &SocketAddr, methods: &RpcMethods) -> DenyUnsafe { match (addr.ip().is_loopback(), methods) { - | (_, RpcMethods::Unsafe) | (false, RpcMethods::Auto) => DenyUnsafe::No, + (_, RpcMethods::Unsafe) | (true, RpcMethods::Auto) => DenyUnsafe::No, _ => DenyUnsafe::Yes, } } diff --git a/substrate/client/rpc-spec-v2/Cargo.toml b/substrate/client/rpc-spec-v2/Cargo.toml index ae21895de38d19c1deb25f0103bf8a899d6fd72a..58dd8b830bebf5b25ceae5c36953b3ad286eb833 100644 --- a/substrate/client/rpc-spec-v2/Cargo.toml +++ b/substrate/client/rpc-spec-v2/Cargo.toml @@ -28,7 +28,6 @@ sp-rpc = { workspace = true, default-features = true } sp-blockchain = { workspace = true, default-features = true } sp-version = { workspace = true, default-features = true } sc-client-api = { workspace = true, default-features = true } -sc-utils = { workspace = true, default-features = true } sc-rpc = { workspace = true, default-features = true } codec = { workspace = true, default-features = true } thiserror = { workspace = true } @@ -56,6 +55,8 @@ sp-externalities = { workspace = true, default-features = true } sp-maybe-compressed-blob = { workspace = true, default-features = true } sc-block-builder = { workspace = true, default-features = true } sc-service = { features = ["test-helpers"], workspace = true, default-features = true } +sc-rpc = { workspace = true, default-features = true, features = ["test-helpers"] } assert_matches = { workspace = true } pretty_assertions = { workspace = true } sc-transaction-pool = { workspace = true, default-features = true } +sc-utils = { workspace = true, default-features = true } diff --git a/substrate/client/rpc-spec-v2/src/archive/archive.rs b/substrate/client/rpc-spec-v2/src/archive/archive.rs index 82c6b2cacc2f4b02991dc89faa8e28a43318b223..dd6c566a76ed475f41acf189dc5e6734e68f58bc 100644 --- a/substrate/client/rpc-spec-v2/src/archive/archive.rs +++ b/substrate/client/rpc-spec-v2/src/archive/archive.rs @@ -275,6 +275,7 @@ where 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/chain_head/chain_head.rs b/substrate/client/rpc-spec-v2/src/chain_head/chain_head.rs index 1bc5cecb205bbd58a1cc6ebb5f0da9c94f6e26b7..a88e7f2a0b3a40269d41459591af9842029d9dd4 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 @@ -27,14 +27,15 @@ use crate::{ api::ChainHeadApiServer, chain_head_follow::ChainHeadFollower, error::Error as ChainHeadRpcError, - event::{FollowEvent, MethodResponse, OperationError}, - subscription::{SubscriptionManagement, SubscriptionManagementError}, + event::{FollowEvent, MethodResponse, OperationError, OperationId, OperationStorageItems}, + subscription::{StopHandle, SubscriptionManagement, SubscriptionManagementError}, + FollowEventSendError, FollowEventSender, }, - common::events::StorageQuery, + common::{events::StorageQuery, storage::QueryResult}, hex_string, SubscriptionTaskExecutor, }; use codec::Encode; -use futures::{channel::oneshot, future::FutureExt}; +use futures::{channel::oneshot, future::FutureExt, SinkExt}; use jsonrpsee::{ core::async_trait, server::ResponsePayload, types::SubscriptionId, ConnectionId, Extensions, MethodResponseFuture, PendingSubscriptionSink, @@ -51,9 +52,16 @@ use sp_core::{traits::CallContext, Bytes}; use sp_rpc::list::ListOrValue; use sp_runtime::traits::Block as BlockT; use std::{marker::PhantomData, sync::Arc, time::Duration}; +use tokio::sync::mpsc; pub(crate) const LOG_TARGET: &str = "rpc-spec-v2"; +/// The buffer capacity for each storage query. +/// +/// This is small because the underlying JSON-RPC server has +/// its down buffer capacity per connection as well. +const STORAGE_QUERY_BUF: usize = 16; + /// The configuration of [`ChainHead`]. pub struct ChainHeadConfig { /// The maximum number of pinned blocks across all subscriptions. @@ -65,9 +73,6 @@ pub struct ChainHeadConfig { /// Stop all subscriptions if the distance between the leaves and the current finalized /// block is larger than this value. pub max_lagging_distance: usize, - /// The maximum number of items reported by the `chainHead_storage` before - /// pagination is required. - pub operation_max_storage_items: usize, /// The maximum number of `chainHead_follow` subscriptions per connection. pub max_follow_subscriptions_per_connection: usize, } @@ -87,10 +92,6 @@ const MAX_PINNED_DURATION: Duration = Duration::from_secs(60); /// Note: The lower limit imposed by the spec is 16. const MAX_ONGOING_OPERATIONS: usize = 16; -/// The maximum number of items the `chainHead_storage` can return -/// before paginations is required. -const MAX_STORAGE_ITER_ITEMS: usize = 5; - /// Stop all subscriptions if the distance between the leaves and the current finalized /// block is larger than this value. const MAX_LAGGING_DISTANCE: usize = 128; @@ -105,7 +106,6 @@ impl Default for ChainHeadConfig { subscription_max_pinned_duration: MAX_PINNED_DURATION, subscription_max_ongoing_operations: MAX_ONGOING_OPERATIONS, max_lagging_distance: MAX_LAGGING_DISTANCE, - operation_max_storage_items: MAX_STORAGE_ITER_ITEMS, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, } } @@ -121,9 +121,6 @@ pub struct ChainHead, Block: BlockT, Client> { executor: SubscriptionTaskExecutor, /// Keep track of the pinned blocks for each subscription. subscriptions: SubscriptionManagement, - /// The maximum number of items reported by the `chainHead_storage` before - /// pagination is required. - operation_max_storage_items: usize, /// Stop all subscriptions if the distance between the leaves and the current finalized /// block is larger than this value. max_lagging_distance: usize, @@ -150,7 +147,6 @@ impl, Block: BlockT, Client> ChainHead { config.max_follow_subscriptions_per_connection, backend, ), - operation_max_storage_items: config.operation_max_storage_items, max_lagging_distance: config.max_lagging_distance, _phantom: PhantomData, } @@ -314,7 +310,7 @@ where }), }; - let (rp, rp_fut) = method_started_response(operation_id, None); + let (rp, rp_fut) = method_started_response(operation_id); let fut = async move { // Wait for the server to send out the response and if it produces an error no event // should be generated. @@ -322,7 +318,7 @@ where return; } - let _ = block_guard.response_sender().unbounded_send(event); + let _ = block_guard.response_sender().send(event).await; }; executor.spawn_blocking("substrate-rpc-subscription", Some("rpc"), fut.boxed()); @@ -426,20 +422,10 @@ where Err(_) => return ResponsePayload::error(ChainHeadRpcError::InvalidBlock), }; - let mut storage_client = ChainHeadStorage::::new( - self.client.clone(), - self.operation_max_storage_items, - ); - let operation = block_guard.operation(); - let operation_id = operation.operation_id(); + let mut storage_client = ChainHeadStorage::::new(self.client.clone()); - // The number of operations we are allowed to execute. - let num_operations = operation.num_reserved(); - let discarded = items.len().saturating_sub(num_operations); - let mut items = items; - items.truncate(num_operations); + let (rp, rp_fut) = method_started_response(block_guard.operation().operation_id()); - let (rp, rp_fut) = method_started_response(operation_id, Some(discarded)); let fut = async move { // Wait for the server to send out the response and if it produces an error no event // should be generated. @@ -447,10 +433,20 @@ where return; } - storage_client.generate_events(block_guard, hash, items, child_trie).await; + let (tx, rx) = tokio::sync::mpsc::channel(STORAGE_QUERY_BUF); + let operation_id = block_guard.operation().operation_id(); + let stop_handle = block_guard.operation().stop_handle().clone(); + let response_sender = block_guard.response_sender(); + + // May fail if the channel is closed or the connection is closed. + // which is okay to ignore. + let _ = futures::future::join( + storage_client.generate_events(hash, items, child_trie, tx), + process_storage_items(rx, response_sender, operation_id, &stop_handle), + ) + .await; }; - self.executor - .spawn_blocking("substrate-rpc-subscription", Some("rpc"), fut.boxed()); + self.executor.spawn("substrate-rpc-subscription", Some("rpc"), fut.boxed()); rp } @@ -503,7 +499,7 @@ where let operation_id = block_guard.operation().operation_id(); let client = self.client.clone(); - let (rp, rp_fut) = method_started_response(operation_id.clone(), None); + let (rp, rp_fut) = method_started_response(operation_id.clone()); let fut = async move { // Wait for the server to send out the response and if it produces an error no event // should be generated. @@ -527,7 +523,7 @@ where }) }); - let _ = block_guard.response_sender().unbounded_send(event); + let _ = block_guard.response_sender().send(event).await; }; self.executor .spawn_blocking("substrate-rpc-subscription", Some("rpc"), fut.boxed()); @@ -588,13 +584,9 @@ where return Ok(()) } - let Some(operation) = self.subscriptions.get_operation(&follow_subscription, &operation_id) - else { - return Ok(()) - }; - - if !operation.submit_continue() { - // Continue called without generating a `WaitingForContinue` event. + // WaitingForContinue event is never emitted, in such cases + // emit an `InvalidContinue error`. + if self.subscriptions.get_operation(&follow_subscription, &operation_id).is_some() { Err(ChainHeadRpcError::InvalidContinue.into()) } else { Ok(()) @@ -616,12 +608,13 @@ where return Ok(()) } - let Some(operation) = self.subscriptions.get_operation(&follow_subscription, &operation_id) + let Some(mut operation) = + self.subscriptions.get_operation(&follow_subscription, &operation_id) else { return Ok(()) }; - operation.stop_operation(); + operation.stop(); Ok(()) } @@ -629,9 +622,8 @@ where fn method_started_response( operation_id: String, - discarded_items: Option, ) -> (ResponsePayload<'static, MethodResponse>, MethodResponseFuture) { - let rp = MethodResponse::Started(MethodResponseStarted { operation_id, discarded_items }); + let rp = MethodResponse::Started(MethodResponseStarted { operation_id, discarded_items: None }); ResponsePayload::success(rp).notify_on_completion() } @@ -657,3 +649,46 @@ where rx } + +async fn process_storage_items( + mut storage_query_stream: mpsc::Receiver, + mut sender: FollowEventSender, + operation_id: String, + stop_handle: &StopHandle, +) -> Result<(), FollowEventSendError> { + loop { + tokio::select! { + _ = stop_handle.stopped() => { + break; + }, + + maybe_storage = storage_query_stream.recv() => { + let Some(storage) = maybe_storage else { + break; + }; + + let item = match storage { + QueryResult::Err(error) => { + return sender + .send(FollowEvent::OperationError(OperationError { operation_id, error })) + .await + } + QueryResult::Ok(Some(v)) => v, + QueryResult::Ok(None) => continue, + }; + + sender + .send(FollowEvent::OperationStorageItems(OperationStorageItems { + operation_id: operation_id.clone(), + items: vec![item], + })).await?; + }, + } + } + + sender + .send(FollowEvent::OperationStorageDone(OperationId { operation_id })) + .await?; + + Ok(()) +} 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 1d28d207124872e0a5c0ff9801775f5c1a5e3181..f2326f0156779c5f8544d37cdbce81d70d4a2105 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::{SubscriptionManagement, SubscriptionManagementError}, + subscription::{InsertedSubscriptionData, SubscriptionManagement, SubscriptionManagementError}, }; use futures::{ channel::oneshot, @@ -53,8 +53,6 @@ use std::{ /// `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> { /// Substrate client. @@ -71,11 +69,76 @@ pub struct ChainHeadFollower, Block: BlockT, Client> { current_best_block: Option, /// LRU cache of pruned blocks. pruned_blocks: LruMap, + /// LRU cache of announced blocks. + announced_blocks: AnnouncedBlocks, /// Stop all subscriptions if the distance between the leaves and the current finalized /// block is larger than this value. max_lagging_distance: usize, } +struct AnnouncedBlocks { + /// Unfinalized blocks. + blocks: LruMap, + /// Finalized blocks. + finalized: MostRecentFinalizedBlocks, +} + +/// Wrapper over LRU to efficiently lookup hashes and remove elements as FIFO queue. +/// +/// For the finalized blocks we use `peek` to avoid moving the block counter to the front. +/// This effectively means that the LRU acts as a FIFO queue. Otherwise, we might +/// end up with scenarios where the "finalized block" in the end of LRU is overwritten which +/// may not necessarily be the oldest finalized block i.e, possible that "get" promotes an +/// older finalized block because it was accessed more recently. +struct MostRecentFinalizedBlocks(LruMap); + +impl MostRecentFinalizedBlocks { + /// Insert the finalized block hash into the LRU cache. + fn insert(&mut self, block: Block::Hash) { + self.0.insert(block, ()); + } + + /// Check if the block is contained in the LRU cache. + fn contains(&mut self, block: &Block::Hash) -> Option<&()> { + self.0.peek(block) + } +} + +impl AnnouncedBlocks { + /// Creates a new `AnnouncedBlocks`. + fn new() -> Self { + Self { + // The total number of pinned blocks is `MAX_PINNED_BLOCKS`, ensure we don't + // exceed the limit. + blocks: LruMap::new(ByLength::new((MAX_PINNED_BLOCKS - MAX_FINALIZED_BLOCKS) as u32)), + // We are keeping a smaller number of announced finalized blocks in memory. + // This is because the `Finalized` event might be triggered before the `NewBlock` event. + finalized: MostRecentFinalizedBlocks(LruMap::new(ByLength::new( + MAX_FINALIZED_BLOCKS as u32, + ))), + } + } + + /// Insert the block into the announced blocks. + fn insert(&mut self, block: Block::Hash, finalized: bool) { + if finalized { + // When a block is declared as finalized, it is removed from the unfinalized blocks. + // + // Given that the finalized blocks are bounded to `MAX_FINALIZED_BLOCKS`, + // this ensures we keep the minimum number of blocks in memory. + self.blocks.remove(&block); + self.finalized.insert(block); + } else { + self.blocks.insert(block, ()); + } + } + + /// Check if the block was previously announced. + fn was_announced(&mut self, block: &Block::Hash) -> bool { + self.blocks.get(block).is_some() || self.finalized.contains(block).is_some() + } +} + impl, Block: BlockT, Client> ChainHeadFollower { /// Create a new [`ChainHeadFollower`]. pub fn new( @@ -96,6 +159,7 @@ impl, Block: BlockT, Client> ChainHeadFollower, startup_point: &StartupPoint, ) -> Result>, SubscriptionManagementError> { - // The block was already pinned by the initial block events or by the finalized event. - if !self.sub_handle.pin_block(&self.sub_id, notification.hash)? { - return Ok(Default::default()) - } + let block_hash = notification.hash; // Ensure we are only reporting blocks after the starting point. if *notification.header.number() < startup_point.finalized_number { return Ok(Default::default()) } - Ok(self.generate_import_events( - notification.hash, - *notification.header.parent_hash(), - notification.is_new_best, - )) + // Ensure the block can be pinned before generating the events. + if !self.sub_handle.pin_block(&self.sub_id, block_hash)? { + // The block is already pinned, this is similar to the check above. + // + // The `SubscriptionManagement` ensures the block is tracked until (short lived): + // - 2 calls to `pin_block` are made (from `Finalized` and `NewBlock` branches). + // - the block is unpinned by the user + // + // This is rather a sanity checks for edge-cases (in theory), where + // [`MAX_FINALIZED_BLOCKS` + 1] finalized events are triggered before the `NewBlock` + // event of the first `Finalized` event. + return Ok(Default::default()) + } + + if self.announced_blocks.was_announced(&block_hash) { + // Block was already reported by the finalized branch. + return Ok(Default::default()) + } + + // Double check the parent hash. If the parent hash is not reported, we have a gap. + let parent_block_hash = *notification.header.parent_hash(); + if !self.announced_blocks.was_announced(&parent_block_hash) { + // The parent block was not reported, we have a gap. + return Err(SubscriptionManagementError::Custom("Parent block was not reported".into())) + } + + self.announced_blocks.insert(block_hash, false); + Ok(self.generate_import_events(block_hash, parent_block_hash, notification.is_new_best)) } /// Generates new block events from the given finalized hashes. /// /// It may be possible that the `Finalized` event fired before the `NewBlock` - /// event. In that case, for each finalized hash that was not reported yet - /// generate the `NewBlock` event. For the final finalized hash we must also - /// generate one `BestBlock` event. + /// event. Only in that case we generate: + /// - `NewBlock` event for all finalized hashes. + /// - `BestBlock` event for the last finalized hash. + /// + /// This function returns an empty list if all finalized hashes were already reported + /// and are pinned. fn generate_finalized_events( &mut self, finalized_block_hashes: &[Block::Hash], @@ -445,43 +548,53 @@ where return Err(SubscriptionManagementError::BlockHeaderAbsent) }; + if !self.announced_blocks.was_announced(first_header.parent_hash()) { + return Err(SubscriptionManagementError::Custom( + "Parent block was not reported for a finalized block".into(), + )); + } + let parents = std::iter::once(first_header.parent_hash()).chain(finalized_block_hashes.iter()); for (i, (hash, parent)) in finalized_block_hashes.iter().zip(parents).enumerate() { - // Check if the block was already reported and thus, is already pinned. - if !self.sub_handle.pin_block(&self.sub_id, *hash)? { - continue + // Ensure the block is pinned before generating the events. + self.sub_handle.pin_block(&self.sub_id, *hash)?; + + // Check if the block was already reported. + if self.announced_blocks.was_announced(hash) { + continue; } // Generate `NewBlock` events for all blocks beside the last block in the list - if i + 1 != finalized_block_hashes.len() { + let is_last = i + 1 == finalized_block_hashes.len(); + if !is_last { // Generate only the `NewBlock` event for this block. events.extend(self.generate_import_events(*hash, *parent, false)); - } else { + self.announced_blocks.insert(*hash, true); + continue; + } + + if let Some(best_block_hash) = self.current_best_block { + let ancestor = + sp_blockchain::lowest_common_ancestor(&*self.client, *hash, best_block_hash)?; + // If we end up here and the `best_block` is a descendent of the finalized block // (last block in the list), it means that there were skipped notifications. - // Otherwise `pin_block` would had returned `true`. + // Otherwise `pin_block` would had returned `false`. // // When the node falls out of sync and then syncs up to the tip of the chain, it can // happen that we skip notifications. Then it is better to terminate the connection // instead of trying to send notifications for all missed blocks. - if let Some(best_block_hash) = self.current_best_block { - let ancestor = sp_blockchain::lowest_common_ancestor( - &*self.client, - *hash, - best_block_hash, - )?; - - if ancestor.hash == *hash { - return Err(SubscriptionManagementError::Custom( - "A descendent of the finalized block was already reported".into(), - )) - } + if ancestor.hash == *hash { + return Err(SubscriptionManagementError::Custom( + "A descendent of the finalized block was already reported".into(), + )) } - - // Let's generate the `NewBlock` and `NewBestBlock` events for the block. - events.extend(self.generate_import_events(*hash, *parent, true)) } + + // Let's generate the `NewBlock` and `NewBestBlock` events for the block. + events.extend(self.generate_import_events(*hash, *parent, true)); + self.announced_blocks.insert(*hash, true); } Ok(events) @@ -543,45 +656,42 @@ where let pruned_block_hashes = self.get_pruned_hashes(¬ification.stale_heads, last_finalized)?; + for finalized in &finalized_block_hashes { + self.announced_blocks.insert(*finalized, true); + } + let finalized_event = FollowEvent::Finalized(Finalized { finalized_block_hashes, pruned_block_hashes: pruned_block_hashes.clone(), }); if let Some(current_best_block) = self.current_best_block { - // The best reported block is in the pruned list. Report a new best block. + // We need to generate a new best block if the best block is in the pruned list. let is_in_pruned_list = pruned_block_hashes.iter().any(|hash| *hash == current_best_block); - // The block is not the last finalized block. - // - // It can be either: - // - a descendant of the last finalized block - // - a block on a fork that will be pruned in the future. - // - // In those cases, we emit a new best block. - let is_not_last_finalized = current_best_block != last_finalized; - - if is_in_pruned_list || is_not_last_finalized { - // We need to generate a best block event. - let best_block_hash = self.client.info().best_hash; - - // Defensive check against state missmatch. - if best_block_hash == current_best_block { - // The client doest not have any new information about the best block. - // The information from `.info()` is updated from the DB as the last - // step of the finalization and it should be up to date. - // If the info is outdated, there is nothing the RPC can do for now. - debug!( - target: LOG_TARGET, - "[follow][id={:?}] Client does not contain different best block", - self.sub_id, - ); - } else { - // The RPC needs to also submit a new best block changed before the - // finalized event. - self.current_best_block = Some(best_block_hash); - events - .push(FollowEvent::BestBlockChanged(BestBlockChanged { best_block_hash })); + if is_in_pruned_list { + self.current_best_block = Some(last_finalized); + events.push(FollowEvent::BestBlockChanged(BestBlockChanged { + best_block_hash: last_finalized, + })); + } else { + // The pruning logic ensures that when the finalized block is announced, + // all blocks on forks that have the common ancestor lower or equal + // to the finalized block are reported. + // + // However, we double check if the best block is a descendant of the last finalized + // block to ensure we don't miss any events. + let ancestor = sp_blockchain::lowest_common_ancestor( + &*self.client, + last_finalized, + current_best_block, + )?; + let is_descendant = ancestor.hash == last_finalized; + if !is_descendant { + self.current_best_block = Some(last_finalized); + events.push(FollowEvent::BestBlockChanged(BestBlockChanged { + best_block_hash: last_finalized, + })); } } } diff --git a/substrate/client/rpc-spec-v2/src/chain_head/chain_head_storage.rs b/substrate/client/rpc-spec-v2/src/chain_head/chain_head_storage.rs index ee39ec253a30cdca26a95ba78174b52b68ee202a..936117e66f98128425b4020e06a4ed7d758cb709 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/chain_head_storage.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/chain_head_storage.rs @@ -18,45 +18,34 @@ //! Implementation of the `chainHead_storage` method. -use std::{collections::VecDeque, marker::PhantomData, sync::Arc}; +use std::{marker::PhantomData, sync::Arc}; use sc_client_api::{Backend, ChildInfo, StorageKey, StorageProvider}; -use sc_utils::mpsc::TracingUnboundedSender; use sp_runtime::traits::Block as BlockT; +use tokio::sync::mpsc; -use crate::{ - chain_head::{ - event::{OperationError, OperationId, OperationStorageItems}, - subscription::BlockGuard, - FollowEvent, - }, - common::{ - events::{StorageQuery, StorageQueryType}, - storage::{IterQueryType, QueryIter, QueryIterResult, Storage}, - }, +use crate::common::{ + events::{StorageQuery, StorageQueryType}, + storage::{IterQueryType, QueryIter, QueryResult, Storage}, }; /// Generates the events of the `chainHead_storage` method. pub struct ChainHeadStorage { /// Storage client. client: Storage, - /// Queue of operations that may require pagination. - iter_operations: VecDeque, - /// The maximum number of items reported by the `chainHead_storage` before - /// pagination is required. - operation_max_storage_items: usize, _phandom: PhantomData<(BE, Block)>, } +impl Clone for ChainHeadStorage { + fn clone(&self) -> Self { + Self { client: self.client.clone(), _phandom: PhantomData } + } +} + impl ChainHeadStorage { /// Constructs a new [`ChainHeadStorage`]. - pub fn new(client: Arc, operation_max_storage_items: usize) -> Self { - Self { - client: Storage::new(client), - iter_operations: VecDeque::new(), - operation_max_storage_items, - _phandom: PhantomData, - } + pub fn new(client: Arc) -> Self { + Self { client: Storage::new(client), _phandom: PhantomData } } } @@ -64,146 +53,71 @@ impl ChainHeadStorage where Block: BlockT + 'static, BE: Backend + 'static, - Client: StorageProvider + 'static, + Client: StorageProvider + Send + Sync + 'static, { - /// Iterate over (key, hash) and (key, value) generating the `WaitingForContinue` event if - /// necessary. - async fn generate_storage_iter_events( - &mut self, - mut block_guard: BlockGuard, - hash: Block::Hash, - child_key: Option, - ) { - let sender = block_guard.response_sender(); - let operation = block_guard.operation(); - - while let Some(query) = self.iter_operations.pop_front() { - if operation.was_stopped() { - return - } - - let result = self.client.query_iter_pagination( - query, - hash, - child_key.as_ref(), - self.operation_max_storage_items, - ); - let (events, maybe_next_query) = match result { - QueryIterResult::Ok(result) => result, - QueryIterResult::Err(error) => { - send_error::(&sender, operation.operation_id(), error.to_string()); - return - }, - }; - - if !events.is_empty() { - // Send back the results of the iteration produced so far. - let _ = sender.unbounded_send(FollowEvent::::OperationStorageItems( - OperationStorageItems { operation_id: operation.operation_id(), items: events }, - )); - } - - if let Some(next_query) = maybe_next_query { - let _ = - sender.unbounded_send(FollowEvent::::OperationWaitingForContinue( - OperationId { operation_id: operation.operation_id() }, - )); - - // The operation might be continued or cancelled only after the - // `OperationWaitingForContinue` is generated above. - operation.wait_for_continue().await; - - // Give a chance for the other items to advance next time. - self.iter_operations.push_back(next_query); - } - } - - if operation.was_stopped() { - return - } - - let _ = - sender.unbounded_send(FollowEvent::::OperationStorageDone(OperationId { - operation_id: operation.operation_id(), - })); - } - /// Generate the block events for the `chainHead_storage` method. pub async fn generate_events( &mut self, - mut block_guard: BlockGuard, hash: Block::Hash, items: Vec>, child_key: Option, - ) { - let sender = block_guard.response_sender(); - let operation = block_guard.operation(); - - let mut storage_results = Vec::with_capacity(items.len()); - for item in items { - match item.query_type { - StorageQueryType::Value => { - match self.client.query_value(hash, &item.key, child_key.as_ref()) { - Ok(Some(value)) => storage_results.push(value), - Ok(None) => continue, - Err(error) => { - send_error::(&sender, operation.operation_id(), error); - return - }, - } - }, - StorageQueryType::Hash => - match self.client.query_hash(hash, &item.key, child_key.as_ref()) { - Ok(Some(value)) => storage_results.push(value), - Ok(None) => continue, - Err(error) => { - send_error::(&sender, operation.operation_id(), error); - return - }, + tx: mpsc::Sender, + ) -> Result<(), tokio::task::JoinError> { + let this = self.clone(); + + tokio::task::spawn_blocking(move || { + for item in items { + match item.query_type { + StorageQueryType::Value => { + let rp = this.client.query_value(hash, &item.key, child_key.as_ref()); + if tx.blocking_send(rp).is_err() { + break; + } }, - StorageQueryType::ClosestDescendantMerkleValue => - match self.client.query_merkle_value(hash, &item.key, child_key.as_ref()) { - Ok(Some(value)) => storage_results.push(value), - Ok(None) => continue, - Err(error) => { - send_error::(&sender, operation.operation_id(), error); - return - }, + StorageQueryType::Hash => { + let rp = this.client.query_hash(hash, &item.key, child_key.as_ref()); + if tx.blocking_send(rp).is_err() { + break; + } }, - StorageQueryType::DescendantsValues => self.iter_operations.push_back(QueryIter { - query_key: item.key, - ty: IterQueryType::Value, - pagination_start_key: None, - }), - StorageQueryType::DescendantsHashes => self.iter_operations.push_back(QueryIter { - query_key: item.key, - ty: IterQueryType::Hash, - pagination_start_key: None, - }), - }; - } - - if !storage_results.is_empty() { - let _ = sender.unbounded_send(FollowEvent::::OperationStorageItems( - OperationStorageItems { - operation_id: operation.operation_id(), - items: storage_results, - }, - )); - } + StorageQueryType::ClosestDescendantMerkleValue => { + let rp = + this.client.query_merkle_value(hash, &item.key, child_key.as_ref()); + if tx.blocking_send(rp).is_err() { + break; + } + }, + StorageQueryType::DescendantsValues => { + let query = QueryIter { + query_key: item.key, + ty: IterQueryType::Value, + pagination_start_key: None, + }; + this.client.query_iter_pagination_with_producer( + query, + hash, + child_key.as_ref(), + &tx, + ) + }, + StorageQueryType::DescendantsHashes => { + let query = QueryIter { + query_key: item.key, + ty: IterQueryType::Hash, + pagination_start_key: None, + }; + this.client.query_iter_pagination_with_producer( + query, + hash, + child_key.as_ref(), + &tx, + ) + }, + } + } + }) + .await?; - self.generate_storage_iter_events(block_guard, hash, child_key).await + Ok(()) } } - -/// Build and send the opaque error back to the `chainHead_follow` method. -fn send_error( - sender: &TracingUnboundedSender>, - operation_id: String, - error: String, -) { - let _ = sender.unbounded_send(FollowEvent::::OperationError(OperationError { - operation_id, - error, - })); -} 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 c9fe19aca2b1898da25e45019e1924256d732d9a..98ddfbbdc63f2dfae7bd90eef5e72b6d0fe21ccc 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/mod.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/mod.rs @@ -42,3 +42,10 @@ pub use event::{ BestBlockChanged, ErrorEvent, Finalized, FollowEvent, Initialized, NewBlock, RuntimeEvent, RuntimeVersionEvent, }; + +/// Follow event sender. +pub(crate) type FollowEventSender = futures::channel::mpsc::Sender>; +/// Follow event receiver. +pub(crate) type FollowEventReceiver = futures::channel::mpsc::Receiver>; +/// Follow event send error. +pub(crate) type FollowEventSendError = futures::channel::mpsc::SendError; 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 14325b4fbb9807d25b7292aa143cbdb2840d0753..95a7c7fe183205d376d9c6d0da797cae1eaa967c 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 @@ -19,18 +19,25 @@ use futures::channel::oneshot; use parking_lot::Mutex; 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, HashSet}, - sync::{atomic::AtomicBool, Arc}, + sync::Arc, time::{Duration, Instant}, }; -use crate::chain_head::{subscription::SubscriptionManagementError, FollowEvent}; +use crate::chain_head::{ + subscription::SubscriptionManagementError, FollowEventReceiver, FollowEventSender, +}; + +type NotifyOnDrop = tokio::sync::mpsc::Receiver<()>; +type SharedOperations = Arc>>; -/// The queue size after which the `sc_utils::mpsc::tracing_unbounded` would produce warnings. -const QUEUE_SIZE_WARNING: usize = 512; +/// The buffer capacity for each subscription +/// +/// Beware of that the JSON-RPC server has a global +/// buffer per connection and this a extra buffer. +const BUF_CAP_PER_SUBSCRIPTION: usize = 16; /// The state machine of a block of a single subscription ID. /// @@ -138,7 +145,7 @@ impl LimitOperations { .try_acquire_many_owned(num_ops.try_into().ok()?) .ok()?; - Some(PermitOperations { num_ops, _permit: permits }) + Some(permits) } } @@ -148,79 +155,36 @@ impl LimitOperations { /// to guarantee the RPC server can execute the number of operations. /// /// The number of reserved items are given back to the [`LimitOperations`] on drop. -struct PermitOperations { - /// The number of operations permitted (reserved). - num_ops: usize, - /// The permit for these operations. - _permit: tokio::sync::OwnedSemaphorePermit, -} +type PermitOperations = tokio::sync::OwnedSemaphorePermit; -/// The state of one operation. -/// -/// This is directly exposed to users via `chain_head_unstable_continue` and -/// `chain_head_unstable_stop_operation`. +/// Stop handle for the operation. #[derive(Clone)] -pub struct OperationState { - /// The shared operation state that holds information about the - /// `waitingForContinue` event and cancellation. - shared_state: Arc, - /// Send notifications when the user calls `chainHead_continue` method. - send_continue: tokio::sync::mpsc::Sender<()>, -} +pub struct StopHandle(tokio::sync::mpsc::Sender<()>); -impl OperationState { - /// Returns true if `chainHead_continue` is called after the - /// `waitingForContinue` event was emitted for the associated - /// operation ID. - pub fn submit_continue(&self) -> bool { - // `waitingForContinue` not generated. - if !self.shared_state.requested_continue.load(std::sync::atomic::Ordering::Acquire) { - return false - } - - // Has enough capacity for 1 message. - // Can fail if the `stop_operation` propagated the stop first. - self.send_continue.try_send(()).is_ok() +impl StopHandle { + pub async fn stopped(&self) { + self.0.closed().await; } - /// Stops the operation if `waitingForContinue` event was emitted for the associated - /// operation ID. - /// - /// Returns nothing in accordance with `chainHead_v1_stopOperation`. - pub fn stop_operation(&self) { - // `waitingForContinue` not generated. - if !self.shared_state.requested_continue.load(std::sync::atomic::Ordering::Acquire) { - return - } - - self.shared_state - .operation_stopped - .store(true, std::sync::atomic::Ordering::Release); - - // Send might not have enough capacity if `submit_continue` was sent first. - // However, the `operation_stopped` boolean was set. - let _ = self.send_continue.try_send(()); + pub fn is_stopped(&self) -> bool { + self.0.is_closed() } } /// The shared operation state between the backend [`RegisteredOperation`] and frontend /// [`RegisteredOperation`]. -struct SharedOperationState { - /// True if the `chainHead` generated `waitingForContinue` event. - requested_continue: AtomicBool, - /// True if the operation was cancelled by the user. - operation_stopped: AtomicBool, +#[derive(Clone)] +pub struct OperationState { + stop: StopHandle, + operations: SharedOperations, + operation_id: String, } -impl SharedOperationState { - /// Constructs a new [`SharedOperationState`]. - /// - /// This is efficiently cloned under a single heap allocation. - fn new() -> Arc { - Arc::new(SharedOperationState { - requested_continue: AtomicBool::new(false), - operation_stopped: AtomicBool::new(false), - }) +impl OperationState { + pub fn stop(&mut self) { + if !self.stop.is_stopped() { + self.operations.lock().remove(&self.operation_id); + } } } @@ -228,59 +192,31 @@ impl SharedOperationState { /// /// This is used internally by the `chainHead` methods. pub struct RegisteredOperation { - /// The shared operation state that holds information about the - /// `waitingForContinue` event and cancellation. - shared_state: Arc, - /// Receive notifications when the user calls `chainHead_continue` method. - recv_continue: tokio::sync::mpsc::Receiver<()>, + /// Stop handle for the operation. + stop_handle: StopHandle, + /// Track the operations ID of this subscription. + operations: SharedOperations, /// The operation ID of the request. operation_id: String, - /// Track the operations ID of this subscription. - operations: Arc>>, /// Permit a number of items to be executed by this operation. - permit: PermitOperations, + _permit: PermitOperations, } impl RegisteredOperation { - /// Wait until the user calls `chainHead_continue` or the operation - /// is cancelled via `chainHead_stopOperation`. - pub async fn wait_for_continue(&mut self) { - self.shared_state - .requested_continue - .store(true, std::sync::atomic::Ordering::Release); - - // The sender part of this channel is around for as long as this object exists, - // because it is stored in the `OperationState` of the `operations` field. - // The sender part is removed from tracking when this object is dropped. - let _ = self.recv_continue.recv().await; - - self.shared_state - .requested_continue - .store(false, std::sync::atomic::Ordering::Release); - } - - /// Returns true if the current operation was stopped. - pub fn was_stopped(&self) -> bool { - self.shared_state.operation_stopped.load(std::sync::atomic::Ordering::Acquire) + /// Stop handle for the operation. + pub fn stop_handle(&self) -> &StopHandle { + &self.stop_handle } /// Get the operation ID. pub fn operation_id(&self) -> String { self.operation_id.clone() } - - /// Returns the number of reserved elements for this permit. - /// - /// This can be smaller than the number of items requested via [`LimitOperations::reserve()`]. - pub fn num_reserved(&self) -> usize { - self.permit.num_ops - } } impl Drop for RegisteredOperation { fn drop(&mut self) { - let mut operations = self.operations.lock(); - operations.remove(&self.operation_id); + self.operations.lock().remove(&self.operation_id); } } @@ -291,7 +227,7 @@ struct Operations { /// Limit the number of ongoing operations. limits: LimitOperations, /// Track the operations ID of this subscription. - operations: Arc>>, + operations: SharedOperations, } impl Operations { @@ -307,25 +243,25 @@ impl Operations { /// Register a new operation. pub fn register_operation(&mut self, to_reserve: usize) -> Option { let permit = self.limits.reserve_at_most(to_reserve)?; - let operation_id = self.next_operation_id(); - // At most one message can be sent. - let (send_continue, recv_continue) = tokio::sync::mpsc::channel(1); - let shared_state = SharedOperationState::new(); - - let state = OperationState { send_continue, shared_state: shared_state.clone() }; - - // Cloned operations for removing the current ID on drop. + let (tx, rx) = tokio::sync::mpsc::channel(1); + let stop_handle = StopHandle(tx); let operations = self.operations.clone(); - operations.lock().insert(operation_id.clone(), state); + operations.lock().insert(operation_id.clone(), (rx, stop_handle.clone())); - Some(RegisteredOperation { shared_state, operation_id, recv_continue, operations, permit }) + Some(RegisteredOperation { stop_handle, operation_id, operations, _permit: permit }) } /// Get the associated operation state with the ID. pub fn get_operation(&self, id: &str) -> Option { - self.operations.lock().get(id).map(|state| state.clone()) + let stop = self.operations.lock().get(id).map(|(_, stop)| stop.clone())?; + + Some(OperationState { + stop, + operations: self.operations.clone(), + operation_id: id.to_string(), + }) } /// Generate the next operation ID for this subscription. @@ -352,7 +288,7 @@ struct SubscriptionState { /// The sender of message responses to the `chainHead_follow` events. /// /// This object is cloned between methods. - response_sender: TracingUnboundedSender>, + response_sender: FollowEventSender, /// The ongoing operations of a subscription. operations: Operations, /// Track the block hashes available for this subscription. @@ -486,7 +422,7 @@ impl SubscriptionState { pub struct BlockGuard> { hash: Block::Hash, with_runtime: bool, - response_sender: TracingUnboundedSender>, + response_sender: FollowEventSender, operation: RegisteredOperation, backend: Arc, } @@ -504,7 +440,7 @@ impl> BlockGuard { fn new( hash: Block::Hash, with_runtime: bool, - response_sender: TracingUnboundedSender>, + response_sender: FollowEventSender, operation: RegisteredOperation, backend: Arc, ) -> Result { @@ -521,7 +457,7 @@ impl> BlockGuard { } /// Send message responses from the `chainHead` methods to `chainHead_follow`. - pub fn response_sender(&self) -> TracingUnboundedSender> { + pub fn response_sender(&self) -> FollowEventSender { self.response_sender.clone() } @@ -543,7 +479,7 @@ pub struct InsertedSubscriptionData { /// Signal that the subscription must stop. pub rx_stop: oneshot::Receiver<()>, /// Receive message responses from the `chainHead` methods. - pub response_receiver: TracingUnboundedReceiver>, + pub response_receiver: FollowEventReceiver, } pub struct SubscriptionsInner> { @@ -594,7 +530,7 @@ impl> SubscriptionsInner { if let Entry::Vacant(entry) = self.subs.entry(sub_id) { let (tx_stop, rx_stop) = oneshot::channel(); let (response_sender, response_receiver) = - tracing_unbounded("chain-head-method-responses", QUEUE_SIZE_WARNING); + futures::channel::mpsc::channel(BUF_CAP_PER_SUBSCRIPTION); let state = SubscriptionState:: { with_runtime, tx_stop: Some(tx_stop), @@ -972,8 +908,7 @@ mod tests { #[test] fn sub_state_register_twice() { - let (response_sender, _response_receiver) = - tracing_unbounded("test-chain-head-method-responses", QUEUE_SIZE_WARNING); + let (response_sender, _response_receiver) = futures::channel::mpsc::channel(1); let mut sub_state = SubscriptionState:: { with_runtime: false, tx_stop: None, @@ -1001,8 +936,7 @@ mod tests { #[test] fn sub_state_register_unregister() { - let (response_sender, _response_receiver) = - tracing_unbounded("test-chain-head-method-responses", QUEUE_SIZE_WARNING); + let (response_sender, _response_receiver) = futures::channel::mpsc::channel(1); let mut sub_state = SubscriptionState:: { with_runtime: false, tx_stop: None, @@ -1349,12 +1283,12 @@ mod tests { // One operation is reserved. let permit_one = ops.reserve_at_most(1).unwrap(); - assert_eq!(permit_one.num_ops, 1); + assert_eq!(permit_one.num_permits(), 1); // Request 2 operations, however there is capacity only for one. let permit_two = ops.reserve_at_most(2).unwrap(); // Number of reserved permits is smaller than provided. - assert_eq!(permit_two.num_ops, 1); + assert_eq!(permit_two.num_permits(), 1); // Try to reserve operations when there's no space. let permit = ops.reserve_at_most(1); @@ -1365,7 +1299,7 @@ mod tests { // Can reserve again let permit_three = ops.reserve_at_most(1).unwrap(); - assert_eq!(permit_three.num_ops, 1); + assert_eq!(permit_three.num_permits(), 1); } #[test] diff --git a/substrate/client/rpc-spec-v2/src/chain_head/subscription/mod.rs b/substrate/client/rpc-spec-v2/src/chain_head/subscription/mod.rs index f266c9d8b34fc774e04a5825740204913851db82..84d1b8f8f9b71d17d7148ef867ce680064dc7067 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/subscription/mod.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/subscription/mod.rs @@ -34,7 +34,7 @@ use self::inner::SubscriptionsInner; pub use self::inner::OperationState; pub use error::SubscriptionManagementError; -pub use inner::{BlockGuard, InsertedSubscriptionData}; +pub use inner::{BlockGuard, InsertedSubscriptionData, StopHandle}; /// Manage block pinning / unpinning for subscription IDs. pub struct SubscriptionManagement> { 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 ab5be1f24e5d85b1be51a0ad1b3d42c3dbcbf730..fa10fde388f97a915881fd3f08010b8c91a012b6 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/test_utils.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/test_utils.rs @@ -69,7 +69,7 @@ impl ChainHeadMockClient { } } - pub async fn trigger_finality_stream(&self, header: Header) { + pub async fn trigger_finality_stream(&self, header: Header, stale_heads: Vec) { // Ensure the client called the `finality_notification_stream`. while self.finality_sinks.lock().is_empty() { tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; @@ -77,11 +77,8 @@ impl ChainHeadMockClient { // Build the notification. let (sink, _stream) = tracing_unbounded("test_sink", 100_000); - let summary = FinalizeSummary { - header: header.clone(), - finalized: vec![header.hash()], - stale_heads: vec![], - }; + let summary = + FinalizeSummary { header: header.clone(), finalized: vec![header.hash()], stale_heads }; let notification = FinalityNotification::from_summary(summary, sink); for sink in self.finality_sinks.lock().iter_mut() { @@ -346,7 +343,8 @@ where fn number( &self, hash: Block::Hash, - ) -> sc_client_api::blockchain::Result::Header as HeaderT>::Number>> { + ) -> sc_client_api::blockchain::Result::Header as HeaderT>::Number>> + { self.client.number(hash) } 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 38f091471f8779f7dbf7deb2838dd84406ae79c3..0c2486157bd033b829079afae86ca262f6742c67 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/tests.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/tests.rs @@ -33,12 +33,12 @@ use jsonrpsee::{ }; use sc_block_builder::BlockBuilderBuilder; use sc_client_api::ChildInfo; +use sc_rpc::testing::TokioTestExecutor; use sc_service::client::new_in_mem; use sp_blockchain::HeaderBackend; use sp_consensus::BlockOrigin; use sp_core::{ storage::well_known_keys::{self, CODE}, - testing::TaskExecutor, Blake2Hasher, Hasher, }; use sp_runtime::traits::Block as BlockT; @@ -60,7 +60,6 @@ type Block = substrate_test_runtime_client::runtime::Block; const MAX_PINNED_BLOCKS: usize = 32; const MAX_PINNED_SECS: u64 = 60; const MAX_OPERATIONS: usize = 16; -const MAX_PAGINATION_LIMIT: usize = 5; const MAX_LAGGING_DISTANCE: usize = 128; const MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION: usize = 4; @@ -80,12 +79,11 @@ pub async fn run_server() -> std::net::SocketAddr { let api = ChainHead::new( client, backend, - Arc::new(TaskExecutor::default()), + Arc::new(TokioTestExecutor::default()), ChainHeadConfig { global_max_pinned_blocks: MAX_PINNED_BLOCKS, subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, - operation_max_storage_items: MAX_PAGINATION_LIMIT, max_follow_subscriptions_per_connection: 1, max_lagging_distance: MAX_LAGGING_DISTANCE, }, @@ -142,12 +140,11 @@ async fn setup_api() -> ( let api = ChainHead::new( client.clone(), backend, - Arc::new(TaskExecutor::default()), + Arc::new(TokioTestExecutor::default()), ChainHeadConfig { global_max_pinned_blocks: MAX_PINNED_BLOCKS, subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, - operation_max_storage_items: MAX_PAGINATION_LIMIT, max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, @@ -250,13 +247,11 @@ async fn follow_subscription_produces_blocks() { let api = ChainHead::new( client.clone(), backend, - Arc::new(TaskExecutor::default()), + Arc::new(TokioTestExecutor::default()), ChainHeadConfig { global_max_pinned_blocks: MAX_PINNED_BLOCKS, subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, - operation_max_storage_items: MAX_PAGINATION_LIMIT, - max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, @@ -321,13 +316,11 @@ async fn follow_with_runtime() { let api = ChainHead::new( client.clone(), backend, - Arc::new(TaskExecutor::default()), + Arc::new(TokioTestExecutor::default()), ChainHeadConfig { global_max_pinned_blocks: MAX_PINNED_BLOCKS, subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, - operation_max_storage_items: MAX_PAGINATION_LIMIT, - max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, @@ -346,7 +339,7 @@ async fn follow_with_runtime() { [\"0x37e397fc7c91f5e4\",2],[\"0xd2bc9897eed08f15\",3],[\"0x40fe3ad401f8959a\",6],\ [\"0xbc9d89904f5b923f\",1],[\"0xc6e9a76309f39b09\",2],[\"0xdd718d5cc53262d4\",1],\ [\"0xcbca25e39f142387\",2],[\"0xf78b278be53f454c\",2],[\"0xab3c0572291feb8b\",1],\ - [\"0xed99c5acb25eedf5\",3],[\"0xfbc577b9d747efd6\",1]],\"transactionVersion\":1,\"stateVersion\":1}"; + [\"0xed99c5acb25eedf5\",3],[\"0xfbc577b9d747efd6\",1]],\"transactionVersion\":1,\"systemVersion\":1}"; let runtime: RuntimeVersion = serde_json::from_str(runtime_str).unwrap(); @@ -631,13 +624,11 @@ async fn call_runtime_without_flag() { let api = ChainHead::new( client.clone(), backend, - Arc::new(TaskExecutor::default()), + Arc::new(TokioTestExecutor::default()), ChainHeadConfig { global_max_pinned_blocks: MAX_PINNED_BLOCKS, subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, - operation_max_storage_items: MAX_PAGINATION_LIMIT, - max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, @@ -1292,13 +1283,11 @@ async fn separate_operation_ids_for_subscriptions() { let api = ChainHead::new( client.clone(), backend, - Arc::new(TaskExecutor::default()), + Arc::new(TokioTestExecutor::default()), ChainHeadConfig { global_max_pinned_blocks: MAX_PINNED_BLOCKS, subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, - operation_max_storage_items: MAX_PAGINATION_LIMIT, - max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, @@ -1380,13 +1369,11 @@ async fn follow_generates_initial_blocks() { let api = ChainHead::new( client.clone(), backend, - Arc::new(TaskExecutor::default()), + Arc::new(TokioTestExecutor::default()), ChainHeadConfig { global_max_pinned_blocks: MAX_PINNED_BLOCKS, subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, - operation_max_storage_items: MAX_PAGINATION_LIMIT, - max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, @@ -1538,13 +1525,11 @@ async fn follow_exceeding_pinned_blocks() { let api = ChainHead::new( client.clone(), backend, - Arc::new(TaskExecutor::default()), + Arc::new(TokioTestExecutor::default()), ChainHeadConfig { global_max_pinned_blocks: 2, subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, - operation_max_storage_items: MAX_PAGINATION_LIMIT, - max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, @@ -1617,13 +1602,11 @@ async fn follow_with_unpin() { let api = ChainHead::new( client.clone(), backend, - Arc::new(TaskExecutor::default()), + Arc::new(TokioTestExecutor::default()), ChainHeadConfig { global_max_pinned_blocks: 2, subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, - operation_max_storage_items: MAX_PAGINATION_LIMIT, - max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, @@ -1725,13 +1708,11 @@ async fn unpin_duplicate_hashes() { let api = ChainHead::new( client.clone(), backend, - Arc::new(TaskExecutor::default()), + Arc::new(TokioTestExecutor::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, - max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, @@ -1830,13 +1811,11 @@ async fn follow_with_multiple_unpin_hashes() { let api = ChainHead::new( client.clone(), backend, - Arc::new(TaskExecutor::default()), + Arc::new(TokioTestExecutor::default()), ChainHeadConfig { global_max_pinned_blocks: MAX_PINNED_BLOCKS, subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, - operation_max_storage_items: MAX_PAGINATION_LIMIT, - max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, @@ -1977,13 +1956,11 @@ async fn follow_prune_best_block() { let api = ChainHead::new( client.clone(), backend, - Arc::new(TaskExecutor::default()), + Arc::new(TokioTestExecutor::default()), ChainHeadConfig { global_max_pinned_blocks: MAX_PINNED_BLOCKS, subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, - operation_max_storage_items: MAX_PAGINATION_LIMIT, - max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, @@ -2165,13 +2142,11 @@ async fn follow_forks_pruned_block() { let api = ChainHead::new( client.clone(), backend, - Arc::new(TaskExecutor::default()), + Arc::new(TokioTestExecutor::default()), ChainHeadConfig { global_max_pinned_blocks: MAX_PINNED_BLOCKS, subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, - operation_max_storage_items: MAX_PAGINATION_LIMIT, - max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, @@ -2327,13 +2302,11 @@ async fn follow_report_multiple_pruned_block() { let api = ChainHead::new( client.clone(), backend, - Arc::new(TaskExecutor::default()), + Arc::new(TokioTestExecutor::default()), ChainHeadConfig { global_max_pinned_blocks: MAX_PINNED_BLOCKS, subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, - operation_max_storage_items: MAX_PAGINATION_LIMIT, - max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, @@ -2566,7 +2539,7 @@ async fn pin_block_references() { genesis_block_builder, None, None, - Box::new(TaskExecutor::new()), + Box::new(TokioTestExecutor::default()), client_config, ) .unwrap(), @@ -2575,13 +2548,11 @@ async fn pin_block_references() { let api = ChainHead::new( client.clone(), backend.clone(), - Arc::new(TaskExecutor::default()), + Arc::new(TokioTestExecutor::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, - max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, @@ -2712,13 +2683,11 @@ async fn follow_finalized_before_new_block() { let api = ChainHead::new( client_mock.clone(), backend, - Arc::new(TaskExecutor::default()), + Arc::new(TokioTestExecutor::default()), ChainHeadConfig { global_max_pinned_blocks: MAX_PINNED_BLOCKS, subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, - operation_max_storage_items: MAX_PAGINATION_LIMIT, - max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, @@ -2743,7 +2712,7 @@ async fn follow_finalized_before_new_block() { // expect for the `chainHead` to generate `NewBlock`, `BestBlock` and `Finalized` events. // Trigger the Finalized notification before the NewBlock one. - run_with_timeout(client_mock.trigger_finality_stream(block_1.header.clone())).await; + run_with_timeout(client_mock.trigger_finality_stream(block_1.header.clone(), vec![])).await; // Initialized must always be reported first. let finalized_hash = client.info().finalized_hash; @@ -2829,13 +2798,11 @@ async fn ensure_operation_limits_works() { let api = ChainHead::new( client.clone(), backend, - Arc::new(TaskExecutor::default()), + Arc::new(TokioTestExecutor::default()), ChainHeadConfig { global_max_pinned_blocks: MAX_PINNED_BLOCKS, subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: 1, - operation_max_storage_items: MAX_PAGINATION_LIMIT, - max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, @@ -2887,7 +2854,7 @@ async fn ensure_operation_limits_works() { let operation_id = match response { MethodResponse::Started(started) => { // Check discarded items. - assert_eq!(started.discarded_items.unwrap(), 3); + assert!(started.discarded_items.is_none()); started.operation_id }, MethodResponse::LimitReached => panic!("Expected started response"), @@ -2922,7 +2889,7 @@ async fn ensure_operation_limits_works() { } #[tokio::test] -async fn check_continue_operation() { +async fn storage_is_backpressured() { let child_info = ChildInfo::new_default(CHILD_STORAGE_KEY); let builder = TestClientBuilder::new().add_extra_child_storage( &child_info, @@ -2936,13 +2903,11 @@ async fn check_continue_operation() { let api = ChainHead::new( client.clone(), backend, - Arc::new(TaskExecutor::default()), + Arc::new(TokioTestExecutor::default()), ChainHeadConfig { global_max_pinned_blocks: MAX_PINNED_BLOCKS, subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, - operation_max_storage_items: 1, - max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, @@ -3021,18 +2986,6 @@ async fn check_continue_operation() { res.items[0].result == StorageResultType::Value(hex_string(b"a")) ); - // Pagination event. - assert_matches!( - get_next_event::>(&mut sub).await, - FollowEvent::OperationWaitingForContinue(res) if res.operation_id == operation_id - ); - - does_not_produce_event::>( - &mut sub, - std::time::Duration::from_secs(DOES_NOT_PRODUCE_EVENTS_SECONDS), - ) - .await; - let _res: () = api.call("chainHead_v1_continue", [&sub_id, &operation_id]).await.unwrap(); assert_matches!( get_next_event::>(&mut sub).await, FollowEvent::OperationStorageItems(res) if res.operation_id == operation_id && @@ -3041,17 +2994,6 @@ async fn check_continue_operation() { res.items[0].result == StorageResultType::Value(hex_string(b"ab")) ); - // Pagination event. - assert_matches!( - get_next_event::>(&mut sub).await, - FollowEvent::OperationWaitingForContinue(res) if res.operation_id == operation_id - ); - does_not_produce_event::>( - &mut sub, - std::time::Duration::from_secs(DOES_NOT_PRODUCE_EVENTS_SECONDS), - ) - .await; - let _res: () = api.call("chainHead_v1_continue", [&sub_id, &operation_id]).await.unwrap(); assert_matches!( get_next_event::>(&mut sub).await, FollowEvent::OperationStorageItems(res) if res.operation_id == operation_id && @@ -3060,18 +3002,6 @@ async fn check_continue_operation() { res.items[0].result == StorageResultType::Value(hex_string(b"abcmoD")) ); - // Pagination event. - assert_matches!( - get_next_event::>(&mut sub).await, - FollowEvent::OperationWaitingForContinue(res) if res.operation_id == operation_id - ); - - does_not_produce_event::>( - &mut sub, - std::time::Duration::from_secs(DOES_NOT_PRODUCE_EVENTS_SECONDS), - ) - .await; - let _res: () = api.call("chainHead_v1_continue", [&sub_id, &operation_id]).await.unwrap(); assert_matches!( get_next_event::>(&mut sub).await, FollowEvent::OperationStorageItems(res) if res.operation_id == operation_id && @@ -3080,17 +3010,6 @@ async fn check_continue_operation() { res.items[0].result == StorageResultType::Value(hex_string(b"abc")) ); - // Pagination event. - assert_matches!( - get_next_event::>(&mut sub).await, - FollowEvent::OperationWaitingForContinue(res) if res.operation_id == operation_id - ); - does_not_produce_event::>( - &mut sub, - std::time::Duration::from_secs(DOES_NOT_PRODUCE_EVENTS_SECONDS), - ) - .await; - let _res: () = api.call("chainHead_v1_continue", [&sub_id, &operation_id]).await.unwrap(); assert_matches!( get_next_event::>(&mut sub).await, FollowEvent::OperationStorageItems(res) if res.operation_id == operation_id && @@ -3121,13 +3040,11 @@ async fn stop_storage_operation() { let api = ChainHead::new( client.clone(), backend, - Arc::new(TaskExecutor::default()), + Arc::new(TokioTestExecutor::default()), ChainHeadConfig { global_max_pinned_blocks: MAX_PINNED_BLOCKS, subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, - operation_max_storage_items: 1, - max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, @@ -3203,15 +3120,22 @@ async fn stop_storage_operation() { res.items[0].result == StorageResultType::Value(hex_string(b"a")) ); - // Pagination event. assert_matches!( get_next_event::>(&mut sub).await, - FollowEvent::OperationWaitingForContinue(res) if res.operation_id == operation_id + FollowEvent::OperationStorageItems(res) if res.operation_id == operation_id && + res.items.len() == 1 && + res.items[0].key == hex_string(b":mo") && + res.items[0].result == StorageResultType::Value(hex_string(b"ab")) ); // Stop the operation. let _res: () = api.call("chainHead_v1_stopOperation", [&sub_id, &operation_id]).await.unwrap(); + assert_matches!( + get_next_event::>(&mut sub).await, + FollowEvent::OperationStorageDone(done) if done.operation_id == operation_id + ); + does_not_produce_event::>( &mut sub, std::time::Duration::from_secs(DOES_NOT_PRODUCE_EVENTS_SECONDS), @@ -3289,30 +3213,23 @@ async fn storage_closest_merkle_value() { MethodResponse::LimitReached => panic!("Expected started response"), }; - let event = get_next_event::>(&mut sub).await; - let merkle_values: HashMap<_, _> = match event { - FollowEvent::OperationStorageItems(res) => { - assert_eq!(res.operation_id, operation_id); + let mut merkle_values = HashMap::new(); - res.items - .into_iter() - .map(|res| { + loop { + match get_next_event::>(&mut sub).await { + FollowEvent::OperationStorageItems(res) if res.operation_id == operation_id => + for res in res.items { let value = match res.result { StorageResultType::ClosestDescendantMerkleValue(value) => value, _ => panic!("Unexpected StorageResultType"), }; - (res.key, value) - }) - .collect() - }, - _ => panic!("Expected OperationStorageItems event"), - }; - - // Finished. - assert_matches!( - get_next_event::>(&mut sub).await, - FollowEvent::OperationStorageDone(done) if done.operation_id == operation_id - ); + merkle_values.insert(res.key, value); + }, + FollowEvent::OperationStorageDone(done) if done.operation_id == operation_id => + break, + _ => panic!("Unexpected event"), + } + } // Response for AAAA, AAAB, A and AA. assert_eq!(merkle_values.len(), 4); @@ -3420,12 +3337,11 @@ async fn chain_head_stop_all_subscriptions() { let api = ChainHead::new( client.clone(), backend, - Arc::new(TaskExecutor::default()), + Arc::new(TokioTestExecutor::default()), ChainHeadConfig { global_max_pinned_blocks: MAX_PINNED_BLOCKS, subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, - operation_max_storage_items: MAX_PAGINATION_LIMIT, max_lagging_distance: 5, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, }, @@ -3634,12 +3550,11 @@ async fn chain_head_limit_reached() { let api = ChainHead::new( client.clone(), backend, - Arc::new(TaskExecutor::default()), + Arc::new(TokioTestExecutor::default()), ChainHeadConfig { global_max_pinned_blocks: MAX_PINNED_BLOCKS, subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, - operation_max_storage_items: MAX_PAGINATION_LIMIT, max_lagging_distance: MAX_LAGGING_DISTANCE, max_follow_subscriptions_per_connection: 1, }, @@ -3675,12 +3590,11 @@ async fn follow_unique_pruned_blocks() { let api = ChainHead::new( client.clone(), backend, - Arc::new(TaskExecutor::default()), + Arc::new(TokioTestExecutor::default()), ChainHeadConfig { global_max_pinned_blocks: MAX_PINNED_BLOCKS, subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), subscription_max_ongoing_operations: MAX_OPERATIONS, - operation_max_storage_items: MAX_PAGINATION_LIMIT, max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, max_lagging_distance: MAX_LAGGING_DISTANCE, }, @@ -3833,3 +3747,289 @@ async fn follow_unique_pruned_blocks() { }); assert_eq!(event, expected); } + +#[tokio::test] +async fn follow_report_best_block_of_a_known_block() { + let builder = TestClientBuilder::new(); + let backend = builder.backend(); + let client = Arc::new(builder.build()); + + let client_mock = Arc::new(ChainHeadMockClient::new(client.clone())); + + let api = ChainHead::new( + client_mock.clone(), + backend, + Arc::new(TokioTestExecutor::default()), + ChainHeadConfig { + global_max_pinned_blocks: MAX_PINNED_BLOCKS, + subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), + subscription_max_ongoing_operations: MAX_OPERATIONS, + max_lagging_distance: MAX_LAGGING_DISTANCE, + max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, + }, + ) + .into_rpc(); + + let finalized_hash = client.info().finalized_hash; + let mut sub = api.subscribe_unbounded("chainHead_v1_follow", [false]).await.unwrap(); + // Initialized must always be reported first. + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::Initialized(Initialized { + finalized_block_hashes: vec![format!("{:?}", finalized_hash)], + finalized_block_runtime: None, + with_runtime: false, + }); + assert_eq!(event, expected); + + // Block tree: + // + // finalized -> block 1 -> block 2 + // ^^^ best block reported + // + // -> block 1 -> block 2_f -> block 3 (best) + // ^^^ finalized + + let block_1 = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; + let block_1_hash = block_1.hash(); + client.import(BlockOrigin::Own, block_1.clone()).await.unwrap(); + let block_2_f = BlockBuilderBuilder::new(&*client) + .on_parent_block(block_1_hash) + .with_parent_block_number(1) + .build() + .unwrap() + .build() + .unwrap() + .block; + let block_2_f_hash = block_2_f.hash(); + client.import(BlockOrigin::Own, block_2_f.clone()).await.unwrap(); + + // Import block 2 as best on the fork. + let mut block_builder = BlockBuilderBuilder::new(&*client) + .on_parent_block(block_1_hash) + .with_parent_block_number(1) + .build() + .unwrap(); + // This push is required as otherwise block 3 has the same hash as block 2 and won't get + // imported + block_builder + .push_transfer(Transfer { + from: AccountKeyring::Alice.into(), + to: AccountKeyring::Ferdie.into(), + amount: 41, + nonce: 0, + }) + .unwrap(); + let block_2 = block_builder.build().unwrap().block; + let block_2_hash = block_2.header.hash(); + client.import_as_best(BlockOrigin::Own, block_2.clone()).await.unwrap(); + + run_with_timeout(client_mock.trigger_import_stream(block_1.header.clone())).await; + run_with_timeout(client_mock.trigger_import_stream(block_2_f.header.clone())).await; + run_with_timeout(client_mock.trigger_import_stream(block_2.header.clone())).await; + + // Check block 1. + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::NewBlock(NewBlock { + block_hash: format!("{:?}", block_1_hash), + parent_block_hash: format!("{:?}", finalized_hash), + new_runtime: None, + with_runtime: false, + }); + assert_eq!(event, expected); + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::BestBlockChanged(BestBlockChanged { + best_block_hash: format!("{:?}", block_1_hash), + }); + assert_eq!(event, expected); + + // Check block 2. + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::NewBlock(NewBlock { + block_hash: format!("{:?}", block_2_f_hash), + parent_block_hash: format!("{:?}", block_1_hash), + new_runtime: None, + with_runtime: false, + }); + assert_eq!(event, expected); + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::BestBlockChanged(BestBlockChanged { + best_block_hash: format!("{:?}", block_2_f_hash), + }); + assert_eq!(event, expected); + + // Check block 2, that we imported as custom best. + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::NewBlock(NewBlock { + block_hash: format!("{:?}", block_2_hash), + parent_block_hash: format!("{:?}", block_1_hash), + new_runtime: None, + with_runtime: false, + }); + assert_eq!(event, expected); + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::BestBlockChanged(BestBlockChanged { + best_block_hash: format!("{:?}", block_2_hash), + }); + assert_eq!(event, expected); + + // Craft block 3 and import it later to simulate a race condition. + let block_3 = BlockBuilderBuilder::new(&*client) + .on_parent_block(block_2_f_hash) + .with_parent_block_number(2) + .build() + .unwrap() + .build() + .unwrap() + .block; + let block_3_hash = block_3.hash(); + + // Set best block info to block 3, that is not announced yet. + // + // This simulates the following edge-case: + // - The client imports a new block as best block. + // - The finality stream is triggered before the block is announced. + // + // This generated in the past a `BestBlock` event for the block that was not announced + // by `NewBlock` events. + // + // This happened because the chainHead was using the `client.info()` without verifying + // if the block was announced or not. This was fixed by using the latest finalized + // block instead as fallback. For more info see: https://github.com/paritytech/polkadot-sdk/issues/5512. + client_mock.set_best_block(block_3_hash, 3); + + // Finalize the block 2 from the fork. + client.finalize_block(block_2_f_hash, None).unwrap(); + run_with_timeout( + client_mock.trigger_finality_stream(block_2_f.header.clone(), vec![block_2_hash]), + ) + .await; + + // Block 2f is now the best block, not the block 3 that is not announced yet. + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::BestBlockChanged(BestBlockChanged { + best_block_hash: format!("{:?}", block_2_f_hash), + }); + assert_eq!(event, expected); + // Block 2 must be reported as pruned, even if it was the previous best. + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::Finalized(Finalized { + finalized_block_hashes: vec![ + // Note: the client mock is only reporting one block at a time. + // format!("{:?}", block_1_hash), + format!("{:?}", block_2_f_hash), + ], + pruned_block_hashes: vec![format!("{:?}", block_2_hash)], + }); + assert_eq!(event, expected); + + // Block 3 is now imported as best. + client.import_as_best(BlockOrigin::Own, block_3.clone()).await.unwrap(); + run_with_timeout(client_mock.trigger_import_stream(block_3.header.clone())).await; + + // Check block 3. + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::NewBlock(NewBlock { + block_hash: format!("{:?}", block_3_hash), + parent_block_hash: format!("{:?}", block_2_f_hash), + new_runtime: None, + with_runtime: false, + }); + assert_eq!(event, expected); + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::BestBlockChanged(BestBlockChanged { + best_block_hash: format!("{:?}", block_3_hash), + }); + assert_eq!(event, expected); + + // Pruned hash can be unpinned. + let sub_id = sub.subscription_id(); + let sub_id = serde_json::to_string(&sub_id).unwrap(); + let hash = format!("{:?}", block_2_hash); + let _res: () = api.call("chainHead_v1_unpin", rpc_params![&sub_id, &hash]).await.unwrap(); + + // Finalize the block 3. + client.finalize_block(block_3_hash, None).unwrap(); + run_with_timeout(client_mock.trigger_finality_stream(block_3.header.clone(), vec![])).await; + + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::Finalized(Finalized { + finalized_block_hashes: vec![format!("{:?}", block_3_hash)], + pruned_block_hashes: vec![], + }); + assert_eq!(event, expected); +} + +#[tokio::test] +async fn follow_event_with_unknown_parent() { + let builder = TestClientBuilder::new(); + let backend = builder.backend(); + let client = Arc::new(builder.build()); + + let client_mock = Arc::new(ChainHeadMockClient::new(client.clone())); + + let api = ChainHead::new( + client_mock.clone(), + backend, + Arc::new(TokioTestExecutor::default()), + ChainHeadConfig { + global_max_pinned_blocks: MAX_PINNED_BLOCKS, + subscription_max_pinned_duration: Duration::from_secs(MAX_PINNED_SECS), + subscription_max_ongoing_operations: MAX_OPERATIONS, + max_follow_subscriptions_per_connection: MAX_FOLLOW_SUBSCRIPTIONS_PER_CONNECTION, + max_lagging_distance: MAX_LAGGING_DISTANCE, + }, + ) + .into_rpc(); + + let finalized_hash = client.info().finalized_hash; + let mut sub = api.subscribe_unbounded("chainHead_v1_follow", [false]).await.unwrap(); + // Initialized must always be reported first. + let event: FollowEvent = get_next_event(&mut sub).await; + let expected = FollowEvent::Initialized(Initialized { + finalized_block_hashes: vec![format!("{:?}", finalized_hash)], + finalized_block_runtime: None, + with_runtime: false, + }); + assert_eq!(event, expected); + + // Block tree: + // + // finalized -> (gap: block 1) -> block 2 + // + // Block 1 is not announced yet. ChainHead should report the stop + // event when encountering an unknown parent of block 2. + + // Note: `client` is used just for constructing the blocks. + // The blocks are imported to chainHead using the `client_mock`. + let block_1 = BlockBuilderBuilder::new(&*client) + .on_parent_block(client.chain_info().genesis_hash) + .with_parent_block_number(0) + .build() + .unwrap() + .build() + .unwrap() + .block; + let block_1_hash = block_1.hash(); + client.import(BlockOrigin::Own, block_1.clone()).await.unwrap(); + + let block_2 = BlockBuilderBuilder::new(&*client) + .on_parent_block(block_1_hash) + .with_parent_block_number(1) + .build() + .unwrap() + .build() + .unwrap() + .block; + client.import(BlockOrigin::Own, block_2.clone()).await.unwrap(); + + run_with_timeout(client_mock.trigger_import_stream(block_2.header)).await; + // When importing the block 2, chainHead detects a gap in our blocks and stops. + assert_matches!(get_next_event::>(&mut sub).await, FollowEvent::Stop); +} diff --git a/substrate/client/rpc-spec-v2/src/common/storage.rs b/substrate/client/rpc-spec-v2/src/common/storage.rs index bd249e033f8f9d27cdcab2e19a41a3323d560b50..2e24a8da8ca8451e93b91d0aa1e220a1829df7e4 100644 --- a/substrate/client/rpc-spec-v2/src/common/storage.rs +++ b/substrate/client/rpc-spec-v2/src/common/storage.rs @@ -22,6 +22,7 @@ use std::{marker::PhantomData, sync::Arc}; use sc_client_api::{Backend, ChildInfo, StorageKey, StorageProvider}; use sp_runtime::traits::Block as BlockT; +use tokio::sync::mpsc; use super::events::{StorageResult, StorageResultType}; use crate::hex_string; @@ -33,6 +34,12 @@ pub struct Storage { _phandom: PhantomData<(BE, Block)>, } +impl Clone for Storage { + fn clone(&self) -> Self { + Self { client: self.client.clone(), _phandom: PhantomData } + } +} + impl Storage { /// Constructs a new [`Storage`]. pub fn new(client: Arc) -> Self { @@ -41,6 +48,7 @@ impl Storage { } /// Query to iterate over storage. +#[derive(Debug)] pub struct QueryIter { /// The key from which the iteration was started. pub query_key: StorageKey, @@ -51,6 +59,7 @@ pub struct QueryIter { } /// The query type of an iteration. +#[derive(Debug)] pub enum IterQueryType { /// Iterating over (key, value) pairs. Value, @@ -123,7 +132,7 @@ where key: &StorageKey, child_key: Option<&ChildInfo>, ) -> QueryResult { - let result = if let Some(child_key) = child_key { + let result = if let Some(ref child_key) = child_key { self.client.child_closest_merkle_value(hash, child_key, key) } else { self.client.closest_merkle_value(hash, key) @@ -146,6 +155,50 @@ where .unwrap_or_else(|error| QueryResult::Err(error.to_string())) } + /// Iterate over the storage keys and send the results to the provided sender. + /// + /// Because this relies on a bounded channel, it will pause the storage iteration + // if the channel is becomes full which in turn provides backpressure. + pub fn query_iter_pagination_with_producer( + &self, + query: QueryIter, + hash: Block::Hash, + child_key: Option<&ChildInfo>, + tx: &mpsc::Sender, + ) { + let QueryIter { ty, query_key, pagination_start_key } = query; + + let maybe_storage = if let Some(child_key) = child_key { + self.client.child_storage_keys( + hash, + child_key.to_owned(), + Some(&query_key), + pagination_start_key.as_ref(), + ) + } else { + self.client.storage_keys(hash, Some(&query_key), pagination_start_key.as_ref()) + }; + + let keys_iter = match maybe_storage { + Ok(keys_iter) => keys_iter, + Err(error) => { + _ = tx.blocking_send(Err(error.to_string())); + return; + }, + }; + + for key in keys_iter { + let result = match ty { + IterQueryType::Value => self.query_value(hash, &key, child_key), + IterQueryType::Hash => self.query_hash(hash, &key, child_key), + }; + + if tx.blocking_send(result).is_err() { + break; + } + } + } + /// Iterate over at most the provided number of keys. /// /// Returns the storage result with a potential next key to resume iteration. diff --git a/substrate/client/rpc/src/state/tests.rs b/substrate/client/rpc/src/state/tests.rs index eef795070343e1501fc93cfbdf5c55e8b96572d8..6b711f2425e95891ad2fd775528e7bafaef451ee 100644 --- a/substrate/client/rpc/src/state/tests.rs +++ b/substrate/client/rpc/src/state/tests.rs @@ -476,7 +476,8 @@ async fn should_return_runtime_version() { [\"0x37e397fc7c91f5e4\",2],[\"0xd2bc9897eed08f15\",3],[\"0x40fe3ad401f8959a\",6],\ [\"0xbc9d89904f5b923f\",1],[\"0xc6e9a76309f39b09\",2],[\"0xdd718d5cc53262d4\",1],\ [\"0xcbca25e39f142387\",2],[\"0xf78b278be53f454c\",2],[\"0xab3c0572291feb8b\",1],\ - [\"0xed99c5acb25eedf5\",3],[\"0xfbc577b9d747efd6\",1]],\"transactionVersion\":1,\"stateVersion\":1}"; + [\"0xed99c5acb25eedf5\",3],[\"0xfbc577b9d747efd6\",1]],\"transactionVersion\":1,\"systemVersion\":1,\ + \"stateVersion\":1}"; let runtime_version = api.runtime_version(None.into()).unwrap(); let serialized = serde_json::to_string(&runtime_version).unwrap(); diff --git a/substrate/client/service/src/builder.rs b/substrate/client/service/src/builder.rs index 9a6c3fde37d93733dd66b015be33e119fed025d5..f27b7ec6fbad87725a560192bd063737ce2157d6 100644 --- a/substrate/client/service/src/builder.rs +++ b/substrate/client/service/src/builder.rs @@ -19,7 +19,7 @@ use crate::{ build_network_future, build_system_rpc_future, client::{Client, ClientConfig}, - config::{Configuration, KeystoreConfig, PrometheusConfig}, + config::{Configuration, ExecutorConfiguration, KeystoreConfig, Multiaddr, PrometheusConfig}, error::Error, metrics::MetricsService, start_rpc_servers, BuildGenesisBlock, GenesisBlockBuilder, RpcHandlers, SpawnTaskHandle, @@ -29,12 +29,12 @@ use futures::{channel::oneshot, future::ready, FutureExt, StreamExt}; use jsonrpsee::RpcModule; use log::info; use prometheus_endpoint::Registry; -use sc_chain_spec::get_extension; +use sc_chain_spec::{get_extension, ChainSpec}; use sc_client_api::{ execution_extensions::ExecutionExtensions, proof_provider::ProofProvider, BadBlocks, BlockBackend, BlockchainEvents, ExecutorProvider, ForkBlocks, StorageProvider, UsageProvider, }; -use sc_client_db::{Backend, DatabaseSettings}; +use sc_client_db::{Backend, BlocksPruning, DatabaseSettings, PruningMode}; use sc_consensus::import_queue::ImportQueue; use sc_executor::{ sp_wasm_interface::HostFunctions, HeapAllocStrategy, NativeExecutionDispatch, RuntimeVersionOf, @@ -42,7 +42,8 @@ use sc_executor::{ }; use sc_keystore::LocalKeystore; use sc_network::{ - config::{FullNetworkConfiguration, SyncMode}, + config::{FullNetworkConfiguration, ProtocolId, SyncMode}, + multiaddr::Protocol, service::{ traits::{PeerStore, RequestResponseConfig}, NotificationMetrics, @@ -52,10 +53,14 @@ use sc_network::{ use sc_network_common::role::Roles; use sc_network_light::light_client_requests::handler::LightClientRequestHandler; use sc_network_sync::{ - block_relay_protocol::BlockRelayParams, block_request_handler::BlockRequestHandler, - engine::SyncingEngine, service::network::NetworkServiceProvider, + block_relay_protocol::BlockRelayParams, + block_request_handler::BlockRequestHandler, + engine::SyncingEngine, + service::network::NetworkServiceProvider, state_request_handler::StateRequestHandler, - warp_request_handler::RequestHandler as WarpSyncRequestHandler, SyncingService, WarpSyncConfig, + strategy::{PolkadotSyncingStrategy, SyncingConfig, SyncingStrategy}, + warp_request_handler::RequestHandler as WarpSyncRequestHandler, + SyncingService, WarpSyncConfig, }; use sc_rpc::{ author::AuthorApiServer, @@ -212,10 +217,7 @@ where .unwrap_or_default(); let client = { - let extensions = sc_client_api::execution_extensions::ExecutionExtensions::new( - None, - Arc::new(executor.clone()), - ); + let extensions = ExecutionExtensions::new(None, Arc::new(executor.clone())); let wasm_runtime_substitutes = config .chain_spec @@ -268,11 +270,11 @@ pub fn new_native_or_wasm_executor( config: &Configuration, ) -> sc_executor::NativeElseWasmExecutor { #[allow(deprecated)] - sc_executor::NativeElseWasmExecutor::new_with_wasm_executor(new_wasm_executor(config)) + sc_executor::NativeElseWasmExecutor::new_with_wasm_executor(new_wasm_executor(&config.executor)) } -/// Creates a [`WasmExecutor`] according to [`Configuration`]. -pub fn new_wasm_executor(config: &Configuration) -> WasmExecutor { +/// Creates a [`WasmExecutor`] according to [`ExecutorConfiguration`]. +pub fn new_wasm_executor(config: &ExecutorConfiguration) -> WasmExecutor { let strategy = config .default_heap_pages .map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |p| HeapAllocStrategy::Static { extra_pages: p as _ }); @@ -434,7 +436,17 @@ where let telemetry = telemetry .map(|telemetry| { - init_telemetry(&mut config, network.clone(), client.clone(), telemetry, Some(sysinfo)) + init_telemetry( + config.network.node_name.clone(), + config.impl_name.clone(), + config.impl_version.clone(), + config.chain_spec.name().to_string(), + config.role.is_authority(), + network.clone(), + client.clone(), + telemetry, + Some(sysinfo), + ) }) .transpose()?; @@ -463,7 +475,13 @@ where let metrics_service = if let Some(PrometheusConfig { port, registry }) = config.prometheus_config.clone() { // Set static metrics. - let metrics = MetricsService::with_prometheus(telemetry, ®istry, &config)?; + let metrics = MetricsService::with_prometheus( + telemetry, + ®istry, + config.role, + &config.network.node_name, + &config.impl_version, + )?; spawn_handle.spawn( "prometheus-endpoint", None, @@ -487,7 +505,7 @@ where ), ); - let rpc_id_provider = config.rpc_id_provider.take(); + let rpc_id_provider = config.rpc.id_provider.take(); // jsonrpsee RPC let gen_rpc_module = || { @@ -497,20 +515,41 @@ where transaction_pool.clone(), keystore.clone(), system_rpc_tx.clone(), - &config, + config.impl_name.clone(), + config.impl_version.clone(), + config.chain_spec.as_ref(), + &config.state_pruning, + config.blocks_pruning, backend.clone(), &*rpc_builder, ) }; - let rpc_server_handle = start_rpc_servers(&config, gen_rpc_module, rpc_id_provider)?; + let rpc_server_handle = start_rpc_servers( + &config.rpc, + config.prometheus_registry(), + &config.tokio_handle, + gen_rpc_module, + rpc_id_provider, + )?; + + let listen_addrs = rpc_server_handle + .listen_addrs() + .into_iter() + .map(|socket_addr| { + let mut multiaddr: Multiaddr = socket_addr.ip().into(); + multiaddr.push(Protocol::Tcp(socket_addr.port())); + multiaddr + }) + .collect(); + let in_memory_rpc = { let mut module = gen_rpc_module()?; module.extensions_mut().insert(DenyUnsafe::No); module }; - let in_memory_rpc_handle = RpcHandlers::new(Arc::new(in_memory_rpc)); + let in_memory_rpc_handle = RpcHandlers::new(Arc::new(in_memory_rpc), listen_addrs); // Spawn informant task spawn_handle.spawn( @@ -555,7 +594,11 @@ pub async fn propagate_transaction_notifications( /// Initialize telemetry with provided configuration and return telemetry handle pub fn init_telemetry( - config: &mut Configuration, + name: String, + implementation: String, + version: String, + chain: String, + authority: bool, network: Network, client: Arc, telemetry: &mut Telemetry, @@ -568,16 +611,16 @@ where { let genesis_hash = client.block_hash(Zero::zero()).ok().flatten().unwrap_or_default(); let connection_message = ConnectionMessage { - name: config.network.node_name.to_owned(), - implementation: config.impl_name.to_owned(), - version: config.impl_version.to_owned(), + name, + implementation, + version, target_os: sc_sysinfo::TARGET_OS.into(), target_arch: sc_sysinfo::TARGET_ARCH.into(), target_env: sc_sysinfo::TARGET_ENV.into(), config: String::new(), - chain: config.chain_spec.name().to_owned(), + chain, genesis_hash: format!("{:?}", genesis_hash), - authority: config.role.is_authority(), + authority, startup_time: SystemTime::UNIX_EPOCH .elapsed() .map(|dur| dur.as_millis()) @@ -599,7 +642,11 @@ pub fn gen_rpc_module( transaction_pool: Arc, keystore: KeystorePtr, system_rpc_tx: TracingUnboundedSender>, - config: &Configuration, + impl_name: String, + impl_version: String, + chain_spec: &dyn ChainSpec, + state_pruning: &Option, + blocks_pruning: BlocksPruning, backend: Arc, rpc_builder: &(dyn Fn(SubscriptionTaskExecutor) -> Result, Error>), ) -> Result, Error> @@ -624,11 +671,11 @@ where TBl::Header: Unpin, { let system_info = sc_rpc::system::SystemInfo { - chain_name: config.chain_spec.name().into(), - impl_name: config.impl_name.clone(), - impl_version: config.impl_version.clone(), - properties: config.chain_spec.properties(), - chain_type: config.chain_spec.chain_type(), + chain_name: chain_spec.name().into(), + impl_name, + impl_version, + properties: chain_spec.properties(), + chain_type: chain_spec.chain_type(), }; let mut rpc_api = RpcModule::new(()); @@ -673,8 +720,8 @@ where // An archive node that can respond to the `archive` RPC-v2 queries is a node with: // - state pruning in archive mode: The storage of blocks is kept around // - block pruning in archive mode: The block's body is kept around - let is_archive_node = config.state_pruning.as_ref().map(|sp| sp.is_archive()).unwrap_or(false) && - config.blocks_pruning.is_archive(); + let is_archive_node = state_pruning.as_ref().map(|sp| sp.is_archive()).unwrap_or(false) && + blocks_pruning.is_archive(); let genesis_hash = client.hash(Zero::zero()).ok().flatten().expect("Genesis block exists; qed"); if is_archive_node { let archive_v2 = sc_rpc_spec_v2::archive::Archive::new( @@ -690,9 +737,9 @@ where // ChainSpec RPC-v2. let chain_spec_v2 = sc_rpc_spec_v2::chain_spec::ChainSpec::new( - config.chain_spec.name().into(), + chain_spec.name().into(), genesis_hash, - config.chain_spec.properties(), + chain_spec.properties(), ) .into_rpc(); @@ -734,65 +781,63 @@ where } /// Parameters to pass into `build_network`. -pub struct BuildNetworkParams< - 'a, - TBl: BlockT, - TNet: NetworkBackend::Hash>, - TExPool, - TImpQu, - TCl, -> { +pub struct BuildNetworkParams<'a, Block, Net, TxPool, IQ, Client> +where + Block: BlockT, + Net: NetworkBackend::Hash>, +{ /// The service configuration. pub config: &'a Configuration, /// Full network configuration. - pub net_config: FullNetworkConfiguration::Hash, TNet>, + pub net_config: FullNetworkConfiguration::Hash, Net>, /// A shared client returned by `new_full_parts`. - pub client: Arc, + pub client: Arc, /// A shared transaction pool. - pub transaction_pool: Arc, + pub transaction_pool: Arc, /// A handle for spawning tasks. pub spawn_handle: SpawnTaskHandle, /// An import queue. - pub import_queue: TImpQu, + pub import_queue: IQ, /// A block announce validator builder. - pub block_announce_validator_builder: - Option) -> Box + Send> + Send>>, - /// Optional warp sync config. - pub warp_sync_config: Option>, + pub block_announce_validator_builder: Option< + Box) -> Box + Send> + Send>, + >, + /// Syncing strategy to use in syncing engine. + pub syncing_strategy: Box>, /// User specified block relay params. If not specified, the default /// block request handler will be used. - pub block_relay: Option>, + pub block_relay: Option>, /// Metrics. pub metrics: NotificationMetrics, } /// Build the network service, the network status sinks and an RPC sender. -pub fn build_network( - params: BuildNetworkParams, +pub fn build_network( + params: BuildNetworkParams, ) -> Result< ( Arc, - TracingUnboundedSender>, - sc_network_transactions::TransactionsHandlerController<::Hash>, + TracingUnboundedSender>, + sc_network_transactions::TransactionsHandlerController<::Hash>, NetworkStarter, - Arc>, + Arc>, ), Error, > where - TBl: BlockT, - TCl: ProvideRuntimeApi - + HeaderMetadata - + Chain - + BlockBackend - + BlockIdTo - + ProofProvider - + HeaderBackend - + BlockchainEvents + Block: BlockT, + Client: ProvideRuntimeApi + + HeaderMetadata + + Chain + + BlockBackend + + BlockIdTo + + ProofProvider + + HeaderBackend + + BlockchainEvents + 'static, - TExPool: TransactionPool::Hash> + 'static, - TImpQu: ImportQueue + 'static, - TNet: NetworkBackend::Hash>, + TxPool: TransactionPool::Hash> + 'static, + IQ: ImportQueue + 'static, + Net: NetworkBackend::Hash>, { let BuildNetworkParams { config, @@ -802,30 +847,13 @@ where spawn_handle, import_queue, block_announce_validator_builder, - warp_sync_config, + syncing_strategy, block_relay, metrics, } = params; - if warp_sync_config.is_none() && config.network.sync_mode.is_warp() { - return Err("Warp sync enabled, but no warp sync provider configured.".into()) - } - - if client.requires_full_sync() { - match config.network.sync_mode { - SyncMode::LightState { .. } => - return Err("Fast sync doesn't work for archive nodes".into()), - SyncMode::Warp => return Err("Warp sync doesn't work for archive nodes".into()), - SyncMode::Full => {}, - } - } - let protocol_id = config.protocol_id(); - let genesis_hash = client - .block_hash(0u32.into()) - .ok() - .flatten() - .expect("Genesis block exists; qed"); + let genesis_hash = client.info().genesis_hash; let block_announce_validator = if let Some(f) = block_announce_validator_builder { f(client.clone()) @@ -839,7 +867,7 @@ where None => { // Custom protocol was not specified, use the default block handler. // Allow both outgoing and incoming requests. - let params = BlockRequestHandler::new::( + let params = BlockRequestHandler::new::( chain_sync_network_handle.clone(), &protocol_id, config.chain_spec.fork_id(), @@ -854,42 +882,9 @@ where block_server.run().await; }); - let (state_request_protocol_config, state_request_protocol_name) = { - let num_peer_hint = net_config.network_config.default_peers_set_num_full as usize + - net_config.network_config.default_peers_set.reserved_nodes.len(); - // Allow both outgoing and incoming requests. - let (handler, protocol_config) = StateRequestHandler::new::( - &protocol_id, - config.chain_spec.fork_id(), - client.clone(), - num_peer_hint, - ); - let config_name = protocol_config.protocol_name().clone(); - - spawn_handle.spawn("state-request-handler", Some("networking"), handler.run()); - (protocol_config, config_name) - }; - - let (warp_sync_protocol_config, warp_request_protocol_name) = match warp_sync_config.as_ref() { - Some(WarpSyncConfig::WithProvider(warp_with_provider)) => { - // Allow both outgoing and incoming requests. - let (handler, protocol_config) = WarpSyncRequestHandler::new::<_, TNet>( - protocol_id.clone(), - genesis_hash, - config.chain_spec.fork_id(), - warp_with_provider.clone(), - ); - let config_name = protocol_config.protocol_name().clone(); - - spawn_handle.spawn("warp-sync-request-handler", Some("networking"), handler.run()); - (Some(protocol_config), Some(config_name)) - }, - _ => (None, None), - }; - let light_client_request_protocol_config = { // Allow both outgoing and incoming requests. - let (handler, protocol_config) = LightClientRequestHandler::new::( + let (handler, protocol_config) = LightClientRequestHandler::new::( &protocol_id, config.chain_spec.fork_id(), client.clone(), @@ -900,15 +895,10 @@ where // install request handlers to `FullNetworkConfiguration` net_config.add_request_response_protocol(block_request_protocol_config); - net_config.add_request_response_protocol(state_request_protocol_config); net_config.add_request_response_protocol(light_client_request_protocol_config); - if let Some(config) = warp_sync_protocol_config { - net_config.add_request_response_protocol(config); - } - let bitswap_config = config.network.ipfs_server.then(|| { - let (handler, config) = TNet::bitswap_server(client.clone()); + let (handler, config) = Net::bitswap_server(client.clone()); spawn_handle.spawn("bitswap-request-handler", Some("networking"), handler); config @@ -917,7 +907,7 @@ where // create transactions protocol and add it to the list of supported protocols of let peer_store_handle = net_config.peer_store_handle(); let (transactions_handler_proto, transactions_config) = - sc_network_transactions::TransactionsHandlerPrototype::new::<_, TBl, TNet>( + sc_network_transactions::TransactionsHandlerPrototype::new::<_, Block, Net>( protocol_id.clone(), genesis_hash, config.chain_spec.fork_id(), @@ -940,20 +930,17 @@ where protocol_id.clone(), &config.chain_spec.fork_id().map(ToOwned::to_owned), block_announce_validator, - warp_sync_config, + syncing_strategy, chain_sync_network_handle, import_queue.service(), block_downloader, - state_request_protocol_name, - warp_request_protocol_name, Arc::clone(&peer_store_handle), )?; let sync_service_import_queue = sync_service.clone(); let sync_service = Arc::new(sync_service); - let genesis_hash = client.hash(Zero::zero()).ok().flatten().expect("Genesis block exists; qed"); - let network_params = sc_network::config::Params::::Hash, TNet> { - role: config.role.clone(), + let network_params = sc_network::config::Params::::Hash, Net> { + role: config.role, executor: { let spawn_handle = Clone::clone(&spawn_handle); Box::new(move |fut| { @@ -962,7 +949,7 @@ where }, network_config: net_config, genesis_hash, - protocol_id: protocol_id.clone(), + protocol_id, fork_id: config.chain_spec.fork_id().map(ToOwned::to_owned), metrics_registry: config.prometheus_config.as_ref().map(|config| config.registry.clone()), block_announce_config, @@ -971,7 +958,7 @@ where }; let has_bootnodes = !network_params.network_config.network_config.boot_nodes.is_empty(); - let network_mut = TNet::new(network_params)?; + let network_mut = Net::new(network_params)?; let network = network_mut.network_service().clone(); let (tx_handler, tx_handler_controller) = transactions_handler_proto.build( @@ -998,8 +985,8 @@ where spawn_handle.spawn( "system-rpc-handler", Some("networking"), - build_system_rpc_future::<_, _, ::Hash>( - config.role.clone(), + build_system_rpc_future::<_, _, ::Hash>( + config.role, network_mut.network_service(), sync_service.clone(), client.clone(), @@ -1008,7 +995,7 @@ where ), ); - let future = build_network_future::<_, _, ::Hash, _>( + let future = build_network_future::<_, _, ::Hash, _>( network_mut, client, sync_service.clone(), @@ -1060,6 +1047,91 @@ where )) } +/// Build standard polkadot syncing strategy +pub fn build_polkadot_syncing_strategy( + protocol_id: ProtocolId, + fork_id: Option<&str>, + net_config: &mut FullNetworkConfiguration::Hash, Net>, + warp_sync_config: Option>, + client: Arc, + spawn_handle: &SpawnTaskHandle, + metrics_registry: Option<&Registry>, +) -> Result>, Error> +where + Block: BlockT, + Client: HeaderBackend + + BlockBackend + + HeaderMetadata + + ProofProvider + + Send + + Sync + + 'static, + + Net: NetworkBackend::Hash>, +{ + if warp_sync_config.is_none() && net_config.network_config.sync_mode.is_warp() { + return Err("Warp sync enabled, but no warp sync provider configured.".into()) + } + + if client.requires_full_sync() { + match net_config.network_config.sync_mode { + SyncMode::LightState { .. } => + return Err("Fast sync doesn't work for archive nodes".into()), + SyncMode::Warp => return Err("Warp sync doesn't work for archive nodes".into()), + SyncMode::Full => {}, + } + } + + let genesis_hash = client.info().genesis_hash; + + let (state_request_protocol_config, state_request_protocol_name) = { + let num_peer_hint = net_config.network_config.default_peers_set_num_full as usize + + net_config.network_config.default_peers_set.reserved_nodes.len(); + // Allow both outgoing and incoming requests. + let (handler, protocol_config) = + StateRequestHandler::new::(&protocol_id, fork_id, client.clone(), num_peer_hint); + let config_name = protocol_config.protocol_name().clone(); + + spawn_handle.spawn("state-request-handler", Some("networking"), handler.run()); + (protocol_config, config_name) + }; + net_config.add_request_response_protocol(state_request_protocol_config); + + let (warp_sync_protocol_config, warp_sync_protocol_name) = match warp_sync_config.as_ref() { + Some(WarpSyncConfig::WithProvider(warp_with_provider)) => { + // Allow both outgoing and incoming requests. + let (handler, protocol_config) = WarpSyncRequestHandler::new::<_, Net>( + protocol_id, + genesis_hash, + fork_id, + warp_with_provider.clone(), + ); + let config_name = protocol_config.protocol_name().clone(); + + spawn_handle.spawn("warp-sync-request-handler", Some("networking"), handler.run()); + (Some(protocol_config), Some(config_name)) + }, + _ => (None, None), + }; + if let Some(config) = warp_sync_protocol_config { + net_config.add_request_response_protocol(config); + } + + let syncing_config = SyncingConfig { + mode: net_config.network_config.sync_mode, + max_parallel_downloads: net_config.network_config.max_parallel_downloads, + max_blocks_per_request: net_config.network_config.max_blocks_per_request, + metrics_registry: metrics_registry.cloned(), + state_request_protocol_name, + }; + Ok(Box::new(PolkadotSyncingStrategy::new( + syncing_config, + client, + warp_sync_config, + warp_sync_protocol_name, + )?)) +} + /// Object used to start the network. #[must_use] pub struct NetworkStarter(oneshot::Sender<()>); diff --git a/substrate/client/service/src/client/client.rs b/substrate/client/service/src/client/client.rs index 22defd7c551437ad2249d0bbac10b35a30c5df5c..ce5b92551bf2ef69931b2c6cf14c5fd2f42866c2 100644 --- a/substrate/client/service/src/client/client.rs +++ b/substrate/client/service/src/client/client.rs @@ -513,6 +513,7 @@ where fork_choice, intermediates, import_existing, + create_gap, .. } = import_block; @@ -537,6 +538,8 @@ where *self.importing_block.write() = Some(hash); + operation.op.set_create_gap(create_gap); + let result = self.execute_and_import_block( operation, origin, @@ -604,9 +607,8 @@ where } let info = self.backend.blockchain().info(); - let gap_block = info - .block_gap - .map_or(false, |(start, _)| *import_headers.post().number() == start); + let gap_block = + info.block_gap.map_or(false, |gap| *import_headers.post().number() == gap.start); // the block is lower than our last finalized block so it must revert // finality, refusing import. diff --git a/substrate/client/service/src/config.rs b/substrate/client/service/src/config.rs index 8387f818e68bc545e4cc355efee08ac7f562ed82..6f65c2e2d81b12172637a240d38f9da4d7f00b48 100644 --- a/substrate/client/service/src/config.rs +++ b/substrate/client/service/src/config.rs @@ -78,48 +78,18 @@ pub struct Configuration { pub blocks_pruning: BlocksPruning, /// Chain configuration. pub chain_spec: Box, - /// Wasm execution method. - pub wasm_method: WasmExecutionMethod, + /// Runtime executor configuration. + pub executor: ExecutorConfiguration, /// Directory where local WASM runtimes live. These runtimes take precedence /// over on-chain runtimes when the spec version matches. Set to `None` to /// disable overrides (default). pub wasm_runtime_overrides: Option, - /// JSON-RPC server endpoints. - pub rpc_addr: Option>, - /// Maximum number of connections for JSON-RPC server. - pub rpc_max_connections: u32, - /// CORS settings for HTTP & WS servers. `None` if all origins are allowed. - pub rpc_cors: Option>, - /// RPC methods to expose (by default only a safe subset or all of them). - pub rpc_methods: RpcMethods, - /// Maximum payload of a rpc request - pub rpc_max_request_size: u32, - /// Maximum payload of a rpc response. - pub rpc_max_response_size: u32, - /// Custom JSON-RPC subscription ID provider. - /// - /// Default: [`crate::RandomStringSubscriptionId`]. - pub rpc_id_provider: Option>, - /// Maximum allowed subscriptions per rpc connection - pub rpc_max_subs_per_conn: u32, - /// JSON-RPC server default port. - pub rpc_port: u16, - /// The number of messages the JSON-RPC server is allowed to keep in memory. - pub rpc_message_buffer_capacity: u32, - /// JSON-RPC server batch config. - pub rpc_batch_config: RpcBatchRequestConfig, - /// RPC rate limit per minute. - pub rpc_rate_limit: Option, - /// RPC rate limit whitelisted ip addresses. - pub rpc_rate_limit_whitelisted_ips: Vec, - /// RPC rate limit trust proxy headers. - pub rpc_rate_limit_trust_proxy_headers: bool, + /// RPC configuration. + pub rpc: RpcConfiguration, /// Prometheus endpoint configuration. `None` if disabled. pub prometheus_config: Option, /// Telemetry service URL. `None` if disabled. pub telemetry_endpoints: Option, - /// The default number of 64KB pages to allocate for Wasm execution - pub default_heap_pages: Option, /// Should offchain workers be executed. pub offchain_worker: OffchainWorkerConfig, /// Enable authoring even when offline. @@ -137,18 +107,12 @@ pub struct Configuration { pub tracing_targets: Option, /// Tracing receiver pub tracing_receiver: sc_tracing::TracingReceiver, - /// The size of the instances cache. - /// - /// The default value is 8. - pub max_runtime_instances: usize, /// Announce block automatically after they have been imported pub announce_block: bool, /// Data path root for the configured chain. pub data_path: PathBuf, /// Base path of the configuration. This is shared between chains. pub base_path: BasePath, - /// Maximum number of different runtime versions that can be cached. - pub runtime_cache_size: u8, } /// Type for tasks spawned by the executor. @@ -323,3 +287,64 @@ impl From for BasePath { BasePath::new(path) } } + +/// RPC configuration. +#[derive(Debug)] +pub struct RpcConfiguration { + /// JSON-RPC server endpoints. + pub addr: Option>, + /// Maximum number of connections for JSON-RPC server. + pub max_connections: u32, + /// CORS settings for HTTP & WS servers. `None` if all origins are allowed. + pub cors: Option>, + /// RPC methods to expose (by default only a safe subset or all of them). + pub methods: RpcMethods, + /// Maximum payload of a rpc request + pub max_request_size: u32, + /// Maximum payload of a rpc response. + pub max_response_size: u32, + /// Custom JSON-RPC subscription ID provider. + /// + /// Default: [`crate::RandomStringSubscriptionId`]. + pub id_provider: Option>, + /// Maximum allowed subscriptions per rpc connection + pub max_subs_per_conn: u32, + /// JSON-RPC server default port. + pub port: u16, + /// The number of messages the JSON-RPC server is allowed to keep in memory. + pub message_buffer_capacity: u32, + /// JSON-RPC server batch config. + pub batch_config: RpcBatchRequestConfig, + /// RPC rate limit per minute. + pub rate_limit: Option, + /// RPC rate limit whitelisted ip addresses. + pub rate_limit_whitelisted_ips: Vec, + /// RPC rate limit trust proxy headers. + pub rate_limit_trust_proxy_headers: bool, +} + +/// Runtime executor configuration. +#[derive(Debug, Clone)] +pub struct ExecutorConfiguration { + /// Wasm execution method. + pub wasm_method: WasmExecutionMethod, + /// The size of the instances cache. + /// + /// The default value is 8. + pub max_runtime_instances: usize, + /// The default number of 64KB pages to allocate for Wasm execution + pub default_heap_pages: Option, + /// Maximum number of different runtime versions that can be cached. + pub runtime_cache_size: u8, +} + +impl Default for ExecutorConfiguration { + fn default() -> Self { + Self { + wasm_method: WasmExecutionMethod::default(), + max_runtime_instances: 8, + default_heap_pages: None, + runtime_cache_size: 2, + } + } +} diff --git a/substrate/client/service/src/lib.rs b/substrate/client/service/src/lib.rs index 9e8aed39c897c6e48ec82f1f63f697aa889d4273..b6acdb8ed002a848cb62ec872cae1786c4434ed1 100644 --- a/substrate/client/service/src/lib.rs +++ b/substrate/client/service/src/lib.rs @@ -34,6 +34,7 @@ mod client; mod metrics; mod task_manager; +use crate::config::Multiaddr; use std::{ collections::HashMap, net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}, @@ -50,6 +51,7 @@ use sc_network::{ }; use sc_network_sync::SyncingService; use sc_network_types::PeerId; +use sc_rpc_server::Server; use sc_utils::mpsc::TracingUnboundedReceiver; use sp_blockchain::HeaderMetadata; use sp_consensus::SyncOracle; @@ -57,11 +59,11 @@ use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; pub use self::{ builder::{ - build_network, gen_rpc_module, init_telemetry, new_client, new_db_backend, new_full_client, - new_full_parts, new_full_parts_record_import, new_full_parts_with_genesis_builder, - new_wasm_executor, propagate_transaction_notifications, spawn_tasks, BuildNetworkParams, - KeystoreContainer, NetworkStarter, SpawnTasksParams, TFullBackend, TFullCallExecutor, - TFullClient, + build_network, build_polkadot_syncing_strategy, gen_rpc_module, init_telemetry, new_client, + new_db_backend, new_full_client, new_full_parts, new_full_parts_record_import, + new_full_parts_with_genesis_builder, new_wasm_executor, + propagate_transaction_notifications, spawn_tasks, BuildNetworkParams, KeystoreContainer, + NetworkStarter, SpawnTasksParams, TFullBackend, TFullCallExecutor, TFullClient, }, client::{ClientConfig, LocalCallExecutor}, error::Error, @@ -83,6 +85,8 @@ pub use sc_chain_spec::{ Properties, }; +use crate::config::RpcConfiguration; +use prometheus_endpoint::Registry; pub use sc_consensus::ImportQueue; pub use sc_executor::NativeExecutionDispatch; pub use sc_network_sync::WarpSyncConfig; @@ -95,17 +99,26 @@ pub use sc_transaction_pool_api::{error::IntoPoolError, InPoolTransaction, Trans #[doc(hidden)] pub use std::{ops::Deref, result::Result, sync::Arc}; pub use task_manager::{SpawnTaskHandle, Task, TaskManager, TaskRegistry, DEFAULT_GROUP_NAME}; +use tokio::runtime::Handle; const DEFAULT_PROTOCOL_ID: &str = "sup"; -/// RPC handlers that can perform RPC queries. +/// A running RPC service that can perform in-memory RPC queries. #[derive(Clone)] -pub struct RpcHandlers(Arc>); +pub struct RpcHandlers { + // This is legacy and may be removed at some point, it was for WASM stuff before smoldot was a + // thing. https://github.com/paritytech/polkadot-sdk/pull/5038#discussion_r1694971805 + rpc_module: Arc>, + + // This can be used to introspect the port the RPC server is listening on. SDK consumers are + // depending on this and it should be supported even if in-memory query support is removed. + listen_addresses: Vec, +} impl RpcHandlers { /// Create PRC handlers instance. - pub fn new(inner: Arc>) -> Self { - Self(inner) + pub fn new(rpc_module: Arc>, listen_addresses: Vec) -> Self { + Self { rpc_module, listen_addresses } } /// Starts an RPC query. @@ -127,12 +140,17 @@ impl RpcHandlers { // This limit is used to prevent panics and is large enough. const TOKIO_MPSC_MAX_SIZE: usize = tokio::sync::Semaphore::MAX_PERMITS; - self.0.raw_json_request(json_query, TOKIO_MPSC_MAX_SIZE).await + self.rpc_module.raw_json_request(json_query, TOKIO_MPSC_MAX_SIZE).await } /// Provides access to the underlying `RpcModule` pub fn handle(&self) -> Arc> { - self.0.clone() + self.rpc_module.clone() + } + + /// Provides access to listen addresses + pub fn listen_addresses(&self) -> &[Multiaddr] { + &self.listen_addresses[..] } } @@ -360,74 +378,63 @@ pub async fn build_system_rpc_future< debug!("`NetworkWorker` has terminated, shutting down the system RPC future."); } -// Wrapper for HTTP and WS servers that makes sure they are properly shut down. -mod waiting { - pub struct Server(pub Option); - - impl Drop for Server { - fn drop(&mut self) { - if let Some(server) = self.0.take() { - // This doesn't not wait for the server to be stopped but fires the signal. - let _ = server.stop(); - } - } - } -} - /// Starts RPC servers. pub fn start_rpc_servers( - config: &Configuration, + rpc_configuration: &RpcConfiguration, + registry: Option<&Registry>, + tokio_handle: &Handle, gen_rpc_module: R, rpc_id_provider: Option>, -) -> Result, error::Error> +) -> Result where R: Fn() -> Result, Error>, { let endpoints: Vec = if let Some(endpoints) = - config.rpc_addr.as_ref() + rpc_configuration.addr.as_ref() { endpoints.clone() } else { - let ipv6 = SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::LOCALHOST, config.rpc_port, 0, 0)); - let ipv4 = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, config.rpc_port)); + let ipv6 = + SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::LOCALHOST, rpc_configuration.port, 0, 0)); + let ipv4 = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, rpc_configuration.port)); vec![ sc_rpc_server::RpcEndpoint { - batch_config: config.rpc_batch_config, - cors: config.rpc_cors.clone(), + batch_config: rpc_configuration.batch_config, + cors: rpc_configuration.cors.clone(), listen_addr: ipv4, - max_buffer_capacity_per_connection: config.rpc_message_buffer_capacity, - max_connections: config.rpc_max_connections, - max_payload_in_mb: config.rpc_max_request_size, - max_payload_out_mb: config.rpc_max_response_size, - max_subscriptions_per_connection: config.rpc_max_subs_per_conn, - rpc_methods: config.rpc_methods.into(), - rate_limit: config.rpc_rate_limit, - rate_limit_trust_proxy_headers: config.rpc_rate_limit_trust_proxy_headers, - rate_limit_whitelisted_ips: config.rpc_rate_limit_whitelisted_ips.clone(), + max_buffer_capacity_per_connection: rpc_configuration.message_buffer_capacity, + max_connections: rpc_configuration.max_connections, + max_payload_in_mb: rpc_configuration.max_request_size, + max_payload_out_mb: rpc_configuration.max_response_size, + max_subscriptions_per_connection: rpc_configuration.max_subs_per_conn, + rpc_methods: rpc_configuration.methods.into(), + rate_limit: rpc_configuration.rate_limit, + rate_limit_trust_proxy_headers: rpc_configuration.rate_limit_trust_proxy_headers, + rate_limit_whitelisted_ips: rpc_configuration.rate_limit_whitelisted_ips.clone(), retry_random_port: true, is_optional: false, }, sc_rpc_server::RpcEndpoint { - batch_config: config.rpc_batch_config, - cors: config.rpc_cors.clone(), + batch_config: rpc_configuration.batch_config, + cors: rpc_configuration.cors.clone(), listen_addr: ipv6, - max_buffer_capacity_per_connection: config.rpc_message_buffer_capacity, - max_connections: config.rpc_max_connections, - max_payload_in_mb: config.rpc_max_request_size, - max_payload_out_mb: config.rpc_max_response_size, - max_subscriptions_per_connection: config.rpc_max_subs_per_conn, - rpc_methods: config.rpc_methods.into(), - rate_limit: config.rpc_rate_limit, - rate_limit_trust_proxy_headers: config.rpc_rate_limit_trust_proxy_headers, - rate_limit_whitelisted_ips: config.rpc_rate_limit_whitelisted_ips.clone(), + max_buffer_capacity_per_connection: rpc_configuration.message_buffer_capacity, + max_connections: rpc_configuration.max_connections, + max_payload_in_mb: rpc_configuration.max_request_size, + max_payload_out_mb: rpc_configuration.max_response_size, + max_subscriptions_per_connection: rpc_configuration.max_subs_per_conn, + rpc_methods: rpc_configuration.methods.into(), + rate_limit: rpc_configuration.rate_limit, + rate_limit_trust_proxy_headers: rpc_configuration.rate_limit_trust_proxy_headers, + rate_limit_whitelisted_ips: rpc_configuration.rate_limit_whitelisted_ips.clone(), retry_random_port: true, is_optional: true, }, ] }; - let metrics = sc_rpc_server::RpcMetrics::new(config.prometheus_registry())?; + let metrics = sc_rpc_server::RpcMetrics::new(registry)?; let rpc_api = gen_rpc_module()?; let server_config = sc_rpc_server::Config { @@ -435,7 +442,7 @@ where rpc_api, metrics, id_provider: rpc_id_provider, - tokio_handle: config.tokio_handle.clone(), + tokio_handle: tokio_handle.clone(), }; // TODO: https://github.com/paritytech/substrate/issues/13773 @@ -443,9 +450,9 @@ where // `block_in_place` is a hack to allow callers to call `block_on` prior to // calling `start_rpc_servers`. match tokio::task::block_in_place(|| { - config.tokio_handle.block_on(sc_rpc_server::start_server(server_config)) + tokio_handle.block_on(sc_rpc_server::start_server(server_config)) }) { - Ok(server) => Ok(Box::new(waiting::Server(Some(server)))), + Ok(server) => Ok(server), Err(e) => Err(Error::Application(e)), } } diff --git a/substrate/client/service/src/metrics.rs b/substrate/client/service/src/metrics.rs index a411a83a784e92f5fa424fd7e68e1d410d948ada..2bcb83f4197900659b6f5750f77b9e4cdc177564 100644 --- a/substrate/client/service/src/metrics.rs +++ b/substrate/client/service/src/metrics.rs @@ -16,9 +16,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::time::SystemTime; - -use crate::config::Configuration; use futures_timer::Delay; use prometheus_endpoint::{register, Gauge, GaugeVec, Opts, PrometheusError, Registry, U64}; use sc_client_api::{ClientInfo, UsageProvider}; @@ -31,7 +28,7 @@ use sp_api::ProvideRuntimeApi; use sp_runtime::traits::{Block, NumberFor, SaturatedConversion, UniqueSaturatedInto}; use std::{ sync::Arc, - time::{Duration, Instant}, + time::{Duration, Instant, SystemTime}, }; struct PrometheusMetrics { @@ -149,26 +146,24 @@ impl MetricsService { pub fn with_prometheus( telemetry: Option, registry: &Registry, - config: &Configuration, + role: Role, + node_name: &str, + impl_version: &str, ) -> Result { - let role_bits = match config.role { + let role_bits = match role { Role::Full => 1u64, // 2u64 used to represent light client role Role::Authority { .. } => 4u64, }; - PrometheusMetrics::setup( - registry, - &config.network.node_name, - &config.impl_version, - role_bits, - ) - .map(|p| MetricsService { - metrics: Some(p), - last_total_bytes_inbound: 0, - last_total_bytes_outbound: 0, - last_update: Instant::now(), - telemetry, + PrometheusMetrics::setup(registry, node_name, impl_version, role_bits).map(|p| { + MetricsService { + metrics: Some(p), + last_total_bytes_inbound: 0, + last_total_bytes_outbound: 0, + last_update: Instant::now(), + telemetry, + } }) } diff --git a/substrate/client/service/test/src/client/mod.rs b/substrate/client/service/test/src/client/mod.rs index 13e63962fe8fecba1f571e149f6d52cdf749965b..55bbfcdd8594800d6ab2d0cbdf9e6a3842b155b1 100644 --- a/substrate/client/service/test/src/client/mod.rs +++ b/substrate/client/service/test/src/client/mod.rs @@ -1748,11 +1748,9 @@ fn respects_block_rules() { } #[test] -#[cfg(disable_flaky)] -#[allow(dead_code)] -// FIXME: https://github.com/paritytech/substrate/issues/11321 +// FIXME: https://github.com/paritytech/polkadot-sdk/issues/48 fn returns_status_for_pruned_blocks() { - use sc_consensus::BlockStatus; + use sp_consensus::BlockStatus; sp_tracing::try_init_simple(); let tmp = tempfile::tempdir().unwrap(); diff --git a/substrate/client/service/test/src/lib.rs b/substrate/client/service/test/src/lib.rs index 5b9bb537563d9505295c03c35979185b30d20dcd..d64581480cdb862ba4d89fbcb8aa464bc8a0a53d 100644 --- a/substrate/client/service/test/src/lib.rs +++ b/substrate/client/service/test/src/lib.rs @@ -29,7 +29,10 @@ use sc_network::{ use sc_network_sync::SyncingService; use sc_service::{ client::Client, - config::{BasePath, DatabaseSource, KeystoreConfig, RpcBatchRequestConfig}, + config::{ + BasePath, DatabaseSource, ExecutorConfiguration, KeystoreConfig, RpcBatchRequestConfig, + RpcConfiguration, + }, BlocksPruning, ChainSpecExtension, Configuration, Error, GenericChainSpec, Role, SpawnTaskHandle, TaskManager, }; @@ -235,36 +238,35 @@ fn node_config( state_pruning: Default::default(), blocks_pruning: BlocksPruning::KeepFinalized, chain_spec: Box::new((*spec).clone()), - wasm_method: Default::default(), + executor: ExecutorConfiguration::default(), wasm_runtime_overrides: Default::default(), - rpc_addr: Default::default(), - rpc_max_connections: Default::default(), - rpc_cors: None, - rpc_methods: Default::default(), - rpc_max_request_size: Default::default(), - rpc_max_response_size: Default::default(), - rpc_id_provider: Default::default(), - rpc_max_subs_per_conn: Default::default(), - rpc_port: 9944, - rpc_message_buffer_capacity: Default::default(), - rpc_batch_config: RpcBatchRequestConfig::Unlimited, - rpc_rate_limit: None, - rpc_rate_limit_whitelisted_ips: Default::default(), - rpc_rate_limit_trust_proxy_headers: Default::default(), + rpc: RpcConfiguration { + addr: Default::default(), + max_connections: Default::default(), + cors: None, + methods: Default::default(), + max_request_size: Default::default(), + max_response_size: Default::default(), + id_provider: Default::default(), + max_subs_per_conn: Default::default(), + port: 9944, + message_buffer_capacity: Default::default(), + batch_config: RpcBatchRequestConfig::Unlimited, + rate_limit: None, + rate_limit_whitelisted_ips: Default::default(), + rate_limit_trust_proxy_headers: Default::default(), + }, prometheus_config: None, telemetry_endpoints: None, - default_heap_pages: None, offchain_worker: Default::default(), force_authoring: false, disable_grandpa: false, dev_key_seed: key_seed, tracing_targets: None, tracing_receiver: Default::default(), - max_runtime_instances: 8, announce_block: true, base_path: BasePath::new(root.clone()), data_path: root, - runtime_cache_size: 2, } } diff --git a/substrate/client/sysinfo/src/lib.rs b/substrate/client/sysinfo/src/lib.rs index 7065c9b997e72c0d5d2e65ab53ec640ada6b38b5..7bc30ae0221da3cef8a1f26fcdc5ee65ef1749ac 100644 --- a/substrate/client/sysinfo/src/lib.rs +++ b/substrate/client/sysinfo/src/lib.rs @@ -27,10 +27,10 @@ mod sysinfo; mod sysinfo_linux; pub use sysinfo::{ - benchmark_cpu, benchmark_disk_random_writes, benchmark_disk_sequential_writes, - benchmark_memory, benchmark_sr25519_verify, gather_hwbench, gather_sysinfo, - serialize_throughput, serialize_throughput_option, Metric, Requirement, Requirements, - Throughput, + benchmark_cpu, benchmark_cpu_parallelism, benchmark_disk_random_writes, + benchmark_disk_sequential_writes, benchmark_memory, benchmark_sr25519_verify, gather_hwbench, + gather_sysinfo, serialize_throughput, serialize_throughput_option, Metric, Requirement, + Requirements, Throughput, }; /// The operating system part of the current target triplet. @@ -48,6 +48,12 @@ pub struct HwBench { /// The CPU speed, as measured in how many MB/s it can hash using the BLAKE2b-256 hash. #[serde(serialize_with = "serialize_throughput")] pub cpu_hashrate_score: Throughput, + /// The parallel CPU speed, as measured in how many MB/s it can hash in parallel using the + /// BLAKE2b-256 hash. + #[serde(serialize_with = "serialize_throughput")] + pub parallel_cpu_hashrate_score: Throughput, + /// The number of expected cores used for computing the parallel CPU speed. + pub parallel_cpu_cores: usize, /// Memory bandwidth in MB/s, calculated by measuring the throughput of `memcpy`. #[serde(serialize_with = "serialize_throughput")] pub memory_memcpy_score: Throughput, @@ -65,6 +71,7 @@ pub struct HwBench { pub disk_random_write_score: Option, } +#[derive(Copy, Clone, Debug)] /// Limit the execution time of a benchmark. pub enum ExecutionLimit { /// Limit by the maximal duration. @@ -132,7 +139,12 @@ pub fn print_sysinfo(sysinfo: &sc_telemetry::SysInfo) { /// Prints out the results of the hardware benchmarks in the logs. pub fn print_hwbench(hwbench: &HwBench) { - log::info!("๐Ÿ CPU score: {}", hwbench.cpu_hashrate_score); + log::info!( + "๐Ÿ CPU single core score: {}, parallelism score: {} with expected cores: {}", + hwbench.cpu_hashrate_score, + hwbench.parallel_cpu_hashrate_score, + hwbench.parallel_cpu_cores, + ); log::info!("๐Ÿ Memory score: {}", hwbench.memory_memcpy_score); if let Some(score) = hwbench.disk_sequential_write_score { diff --git a/substrate/client/sysinfo/src/sysinfo.rs b/substrate/client/sysinfo/src/sysinfo.rs index 37b35fcb910327abef20968d4af973ecdd88412f..4bcdd673455c8925352d98d6567b082a7fe7ebe6 100644 --- a/substrate/client/sysinfo/src/sysinfo.rs +++ b/substrate/client/sysinfo/src/sysinfo.rs @@ -22,16 +22,18 @@ use sc_telemetry::SysInfo; use sp_core::{sr25519, Pair}; use sp_io::crypto::sr25519_verify; +use core::f64; use derive_more::From; use rand::{seq::SliceRandom, Rng, RngCore}; use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; use std::{ - fmt, - fmt::{Display, Formatter}, + borrow::Cow, + fmt::{self, Display, Formatter}, fs::File, io::{Seek, SeekFrom, Write}, ops::{Deref, DerefMut}, path::{Path, PathBuf}, + sync::{Arc, Barrier}, time::{Duration, Instant}, }; @@ -42,6 +44,8 @@ pub enum Metric { Sr25519Verify, /// Blake2-256 hashing algorithm. Blake2256, + /// Blake2-256 hashing algorithm executed in parallel + Blake2256Parallel { num_cores: usize }, /// Copying data in RAM. MemCopy, /// Disk sequential write. @@ -85,20 +89,22 @@ impl Metric { /// The category of the metric. pub fn category(&self) -> &'static str { match self { - Self::Sr25519Verify | Self::Blake2256 => "CPU", + Self::Sr25519Verify | Self::Blake2256 | Self::Blake2256Parallel { .. } => "CPU", Self::MemCopy => "Memory", Self::DiskSeqWrite | Self::DiskRndWrite => "Disk", } } /// The name of the metric. It is always prefixed by the [`self.category()`]. - pub fn name(&self) -> &'static str { + pub fn name(&self) -> Cow<'static, str> { match self { - Self::Sr25519Verify => "SR25519-Verify", - Self::Blake2256 => "BLAKE2-256", - Self::MemCopy => "Copy", - Self::DiskSeqWrite => "Seq Write", - Self::DiskRndWrite => "Rnd Write", + Self::Sr25519Verify => Cow::Borrowed("SR25519-Verify"), + Self::Blake2256 => Cow::Borrowed("BLAKE2-256"), + Self::Blake2256Parallel { num_cores } => + Cow::Owned(format!("BLAKE2-256-Parallel-{}", num_cores)), + Self::MemCopy => Cow::Borrowed("Copy"), + Self::DiskSeqWrite => Cow::Borrowed("Seq Write"), + Self::DiskRndWrite => Cow::Borrowed("Rnd Write"), } } } @@ -253,6 +259,10 @@ pub struct Requirement { deserialize_with = "deserialize_throughput" )] pub minimum: Throughput, + /// Check this requirement only for relay chain validator nodes. + #[serde(default)] + #[serde(skip_serializing_if = "core::ops::Not::not")] + pub validator_only: bool, } #[inline(always)] @@ -343,8 +353,18 @@ fn clobber_value(input: &mut T) { pub const DEFAULT_CPU_EXECUTION_LIMIT: ExecutionLimit = ExecutionLimit::Both { max_iterations: 4 * 1024, max_duration: Duration::from_millis(100) }; -// This benchmarks the CPU speed as measured by calculating BLAKE2b-256 hashes, in bytes per second. +// This benchmarks the single core CPU speed as measured by calculating BLAKE2b-256 hashes, in bytes +// per second. pub fn benchmark_cpu(limit: ExecutionLimit) -> Throughput { + benchmark_cpu_parallelism(limit, 1) +} + +// This benchmarks the entire CPU speed as measured by calculating BLAKE2b-256 hashes, in bytes per +// second. It spawns multiple threads to measure the throughput of the entire CPU and averages the +// score obtained by each thread. If we have at least `refhw_num_cores` available then the +// average throughput should be relatively close to the single core performance as measured by +// calling this function with refhw_num_cores equal to 1. +pub fn benchmark_cpu_parallelism(limit: ExecutionLimit, refhw_num_cores: usize) -> Throughput { // In general the results of this benchmark are somewhat sensitive to how much // data we hash at the time. The smaller this is the *less* B/s we can hash, // the bigger this is the *more* B/s we can hash, up until a certain point @@ -359,20 +379,38 @@ pub fn benchmark_cpu(limit: ExecutionLimit) -> Throughput { // but without hitting its theoretical maximum speed. const SIZE: usize = 32 * 1024; - let mut buffer = Vec::new(); - buffer.resize(SIZE, 0x66); - let mut hash = Default::default(); + let ready_to_run_benchmark = Arc::new(Barrier::new(refhw_num_cores)); + let mut benchmark_threads = Vec::new(); - let run = || -> Result<(), ()> { - clobber_slice(&mut buffer); - hash = sp_crypto_hashing::blake2_256(&buffer); - clobber_slice(&mut hash); + // Spawn a thread for each expected core and average the throughput for each of them. + for _ in 0..refhw_num_cores { + let ready_to_run_benchmark = ready_to_run_benchmark.clone(); - Ok(()) - }; + let handle = std::thread::spawn(move || { + let mut buffer = Vec::new(); + buffer.resize(SIZE, 0x66); + let mut hash = Default::default(); - benchmark("CPU score", SIZE, limit.max_iterations(), limit.max_duration(), run) - .expect("benchmark cannot fail; qed") + let run = || -> Result<(), ()> { + clobber_slice(&mut buffer); + hash = sp_crypto_hashing::blake2_256(&buffer); + clobber_slice(&mut hash); + + Ok(()) + }; + ready_to_run_benchmark.wait(); + benchmark("CPU score", SIZE, limit.max_iterations(), limit.max_duration(), run) + .expect("benchmark cannot fail; qed") + }); + benchmark_threads.push(handle); + } + + let average_score = benchmark_threads + .into_iter() + .map(|thread| thread.join().map(|throughput| throughput.as_kibs()).unwrap_or(0.0)) + .sum::() / + refhw_num_cores as f64; + Throughput::from_kibs(average_score) } /// A default [`ExecutionLimit`] that can be used to call [`benchmark_memory`]. @@ -624,10 +662,25 @@ pub fn benchmark_sr25519_verify(limit: ExecutionLimit) -> Throughput { /// Optionally accepts a path to a `scratch_directory` to use to benchmark the /// disk. Also accepts the `requirements` for the hardware benchmark and a /// boolean to specify if the node is an authority. -pub fn gather_hwbench(scratch_directory: Option<&Path>) -> HwBench { +pub fn gather_hwbench(scratch_directory: Option<&Path>, requirements: &Requirements) -> HwBench { + let cpu_hashrate_score = benchmark_cpu(DEFAULT_CPU_EXECUTION_LIMIT); + let (parallel_cpu_hashrate_score, parallel_cpu_cores) = requirements + .0 + .iter() + .filter_map(|req| { + if let Metric::Blake2256Parallel { num_cores } = req.metric { + Some((benchmark_cpu_parallelism(DEFAULT_CPU_EXECUTION_LIMIT, num_cores), num_cores)) + } else { + None + } + }) + .next() + .unwrap_or((cpu_hashrate_score, 1)); #[allow(unused_mut)] let mut hwbench = HwBench { - cpu_hashrate_score: benchmark_cpu(DEFAULT_CPU_EXECUTION_LIMIT), + cpu_hashrate_score, + parallel_cpu_hashrate_score, + parallel_cpu_cores, memory_memcpy_score: benchmark_memory(DEFAULT_MEMORY_EXECUTION_LIMIT), disk_sequential_write_score: None, disk_random_write_score: None, @@ -659,9 +712,17 @@ pub fn gather_hwbench(scratch_directory: Option<&Path>) -> HwBench { impl Requirements { /// Whether the hardware requirements are met by the provided benchmark results. - pub fn check_hardware(&self, hwbench: &HwBench) -> Result<(), CheckFailures> { + pub fn check_hardware( + &self, + hwbench: &HwBench, + is_rc_authority: bool, + ) -> Result<(), CheckFailures> { let mut failures = Vec::new(); for requirement in self.0.iter() { + if requirement.validator_only && !is_rc_authority { + continue + } + match requirement.metric { Metric::Blake2256 => if requirement.minimum > hwbench.cpu_hashrate_score { @@ -671,6 +732,14 @@ impl Requirements { found: hwbench.cpu_hashrate_score, }); }, + Metric::Blake2256Parallel { .. } => + if requirement.minimum > hwbench.parallel_cpu_hashrate_score { + failures.push(CheckFailure { + metric: requirement.metric, + expected: requirement.minimum, + found: hwbench.parallel_cpu_hashrate_score, + }); + }, Metric::MemCopy => if requirement.minimum > hwbench.memory_memcpy_score { failures.push(CheckFailure { @@ -732,6 +801,13 @@ mod tests { assert!(benchmark_cpu(DEFAULT_CPU_EXECUTION_LIMIT) > Throughput::from_mibs(0.0)); } + #[test] + fn test_benchmark_parallel_cpu() { + assert!( + benchmark_cpu_parallelism(DEFAULT_CPU_EXECUTION_LIMIT, 8) > Throughput::from_mibs(0.0) + ); + } + #[test] fn test_benchmark_memory() { assert!(benchmark_memory(DEFAULT_MEMORY_EXECUTION_LIMIT) > Throughput::from_mibs(0.0)); @@ -781,6 +857,8 @@ mod tests { fn hwbench_serialize_works() { let hwbench = HwBench { cpu_hashrate_score: Throughput::from_gibs(1.32), + parallel_cpu_hashrate_score: Throughput::from_gibs(1.32), + parallel_cpu_cores: 4, memory_memcpy_score: Throughput::from_kibs(9342.432), disk_sequential_write_score: Some(Throughput::from_kibs(4332.12)), disk_random_write_score: None, @@ -788,6 +866,6 @@ mod tests { let serialized = serde_json::to_string(&hwbench).unwrap(); // Throughput from all of the benchmarks should be converted to MiBs. - assert_eq!(serialized, "{\"cpu_hashrate_score\":1351,\"memory_memcpy_score\":9,\"disk_sequential_write_score\":4}"); + assert_eq!(serialized, "{\"cpu_hashrate_score\":1351,\"parallel_cpu_hashrate_score\":1351,\"parallel_cpu_cores\":4,\"memory_memcpy_score\":9,\"disk_sequential_write_score\":4}"); } } diff --git a/substrate/client/tracing/Cargo.toml b/substrate/client/tracing/Cargo.toml index 09571610a3a60a8bc0e01bac83b1d9c4ee0e7d03..b8f5e40caf83b5c04d5823bb9ecefa0aa9b10bee 100644 --- a/substrate/client/tracing/Cargo.toml +++ b/substrate/client/tracing/Cargo.toml @@ -20,7 +20,6 @@ console = { workspace = true } is-terminal = { workspace = true } chrono = { workspace = true } codec = { workspace = true, default-features = true } -lazy_static = { workspace = true } libc = { workspace = true } log = { workspace = true, default-features = true } parking_lot = { workspace = true, default-features = true } @@ -29,7 +28,10 @@ serde = { workspace = true, default-features = true } thiserror = { workspace = true } tracing = { workspace = true, default-features = true } tracing-log = { workspace = true } -tracing-subscriber = { workspace = true, features = ["env-filter", "parking_lot"] } +tracing-subscriber = { workspace = true, features = [ + "env-filter", + "parking_lot", +] } sc-client-api = { workspace = true, default-features = true } sc-tracing-proc-macro = { workspace = true, default-features = true } sp-api = { workspace = true, default-features = true } diff --git a/substrate/client/tracing/src/logging/mod.rs b/substrate/client/tracing/src/logging/mod.rs index 74ce5f90ede9e2b5585984ddc8dc47bf0f92ca31..33fec2d418813e24b34d3b7ee0957cb32772f27e 100644 --- a/substrate/client/tracing/src/logging/mod.rs +++ b/substrate/client/tracing/src/logging/mod.rs @@ -138,6 +138,9 @@ where .add_directive( parse_default_directive("trust_dns_proto=off").expect("provided directive is valid"), ) + .add_directive( + parse_default_directive("hickory_proto=off").expect("provided directive is valid"), + ) .add_directive( parse_default_directive("libp2p_mdns::behaviour::iface=off") .expect("provided directive is valid"), diff --git a/substrate/client/transaction-pool/src/graph/tracked_map.rs b/substrate/client/transaction-pool/src/graph/tracked_map.rs index 47ad22603e469c297bd1a5f9f133f7d1997adaee..44c2c738ab1bdbe829e61a81d465ade413702a73 100644 --- a/substrate/client/transaction-pool/src/graph/tracked_map.rs +++ b/substrate/client/transaction-pool/src/graph/tracked_map.rs @@ -119,10 +119,9 @@ where let new_bytes = val.size(); self.bytes.fetch_add(new_bytes as isize, AtomicOrdering::Relaxed); self.length.fetch_add(1, AtomicOrdering::Relaxed); - self.inner_guard.insert(key, val).map(|old_val| { + self.inner_guard.insert(key, val).inspect(|old_val| { self.bytes.fetch_sub(old_val.size() as isize, AtomicOrdering::Relaxed); self.length.fetch_sub(1, AtomicOrdering::Relaxed); - old_val }) } diff --git a/substrate/client/utils/Cargo.toml b/substrate/client/utils/Cargo.toml index 6c3a2228952efa292fa101ceff94b2b17cc6a3fe..485261058d5902f115bef06307de69124259b163 100644 --- a/substrate/client/utils/Cargo.toml +++ b/substrate/client/utils/Cargo.toml @@ -16,7 +16,6 @@ workspace = true async-channel = { workspace = true } futures = { workspace = true } futures-timer = { workspace = true } -lazy_static = { workspace = true } log = { workspace = true, default-features = true } parking_lot = { workspace = true, default-features = true } prometheus = { workspace = true } diff --git a/substrate/client/utils/src/metrics.rs b/substrate/client/utils/src/metrics.rs index 308e90cb25379d62fe02f1878a664ff44377c9df..9b6e1e47039e76c55fa1f7bde80bc64da206c7dc 100644 --- a/substrate/client/utils/src/metrics.rs +++ b/substrate/client/utils/src/metrics.rs @@ -18,42 +18,49 @@ //! Metering primitives and globals -use lazy_static::lazy_static; use prometheus::{ core::{AtomicU64, GenericCounter, GenericGauge}, Error as PrometheusError, Registry, }; +use std::sync::LazyLock; use prometheus::{ core::{GenericCounterVec, GenericGaugeVec}, Opts, }; -lazy_static! { - pub static ref TOKIO_THREADS_TOTAL: GenericCounter = - GenericCounter::new("substrate_tokio_threads_total", "Total number of threads created") - .expect("Creating of statics doesn't fail. qed"); - pub static ref TOKIO_THREADS_ALIVE: GenericGauge = - GenericGauge::new("substrate_tokio_threads_alive", "Number of threads alive right now") - .expect("Creating of statics doesn't fail. qed"); -} +pub static TOKIO_THREADS_TOTAL: LazyLock> = LazyLock::new(|| { + GenericCounter::new("substrate_tokio_threads_total", "Total number of threads created") + .expect("Creating of statics doesn't fail. qed") +}); -lazy_static! { - pub static ref UNBOUNDED_CHANNELS_COUNTER: GenericCounterVec = GenericCounterVec::new( - Opts::new( - "substrate_unbounded_channel_len", - "Items sent/received/dropped on each mpsc::unbounded instance" - ), - &["entity", "action"], // name of channel, send|received|dropped - ).expect("Creating of statics doesn't fail. qed"); - pub static ref UNBOUNDED_CHANNELS_SIZE: GenericGaugeVec = GenericGaugeVec::new( +pub static TOKIO_THREADS_ALIVE: LazyLock> = LazyLock::new(|| { + GenericGauge::new("substrate_tokio_threads_alive", "Number of threads alive right now") + .expect("Creating of statics doesn't fail. qed") +}); + +pub static UNBOUNDED_CHANNELS_COUNTER: LazyLock> = + LazyLock::new(|| { + GenericCounterVec::new( + Opts::new( + "substrate_unbounded_channel_len", + "Items sent/received/dropped on each mpsc::unbounded instance", + ), + &["entity", "action"], // name of channel, send|received|dropped + ) + .expect("Creating of statics doesn't fail. qed") + }); + +pub static UNBOUNDED_CHANNELS_SIZE: LazyLock> = LazyLock::new(|| { + GenericGaugeVec::new( Opts::new( "substrate_unbounded_channel_size", "Size (number of messages to be processed) of each mpsc::unbounded instance", ), &["entity"], // name of channel - ).expect("Creating of statics doesn't fail. qed"); -} + ) + .expect("Creating of statics doesn't fail. qed") +}); pub static SENT_LABEL: &'static str = "send"; pub static RECEIVED_LABEL: &'static str = "received"; diff --git a/substrate/client/utils/src/mpsc.rs b/substrate/client/utils/src/mpsc.rs index 91db7e1e7b011142f5b1089eb74a209a612cf4b9..051cb5b387ca8ac1fd12cffbcec9897d765526d1 100644 --- a/substrate/client/utils/src/mpsc.rs +++ b/substrate/client/utils/src/mpsc.rs @@ -103,7 +103,7 @@ impl TracingUnboundedSender { /// Proxy function to `async_channel::Sender::try_send`. pub fn unbounded_send(&self, msg: T) -> Result<(), TrySendError> { - self.inner.try_send(msg).map(|s| { + self.inner.try_send(msg).inspect(|_| { UNBOUNDED_CHANNELS_COUNTER.with_label_values(&[self.name, SENT_LABEL]).inc(); UNBOUNDED_CHANNELS_SIZE .with_label_values(&[self.name]) @@ -124,8 +124,6 @@ impl TracingUnboundedSender { Backtrace::force_capture(), ); } - - s }) } @@ -144,12 +142,11 @@ impl TracingUnboundedReceiver { /// Proxy function to [`async_channel::Receiver`] /// that discounts the messages taken out. pub fn try_recv(&mut self) -> Result { - self.inner.try_recv().map(|s| { + self.inner.try_recv().inspect(|_| { UNBOUNDED_CHANNELS_COUNTER.with_label_values(&[self.name, RECEIVED_LABEL]).inc(); UNBOUNDED_CHANNELS_SIZE .with_label_values(&[self.name]) .set(self.inner.len().saturated_into()); - s }) } diff --git a/substrate/frame/alliance/src/mock.rs b/substrate/frame/alliance/src/mock.rs index 9cd96019781e7ec1cd3d0b6c1f5873126027e23f..5442e87790205e2a7de1eadac1e0bf8fc5d33709 100644 --- a/substrate/frame/alliance/src/mock.rs +++ b/substrate/frame/alliance/src/mock.rs @@ -77,6 +77,9 @@ impl pallet_collective::Config for Test { type WeightInfo = (); type SetMembersOrigin = EnsureRoot; type MaxProposalWeight = MaxProposalWeight; + type DisapproveOrigin = EnsureRoot; + type KillOrigin = EnsureRoot; + type Consideration = (); } parameter_types! { diff --git a/substrate/frame/assets-freezer/src/mock.rs b/substrate/frame/assets-freezer/src/mock.rs index 5e04dfe8e2b9c4adf3553919dfe14b5fe55870ae..bc903a018f7b8f6962d721f4160ac6cdfa35bbf2 100644 --- a/substrate/frame/assets-freezer/src/mock.rs +++ b/substrate/frame/assets-freezer/src/mock.rs @@ -87,6 +87,7 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); + type DoneSlashHandler = (); } impl pallet_assets::Config for Test { diff --git a/substrate/frame/authorship/src/lib.rs b/substrate/frame/authorship/src/lib.rs index a0cca806e7863cef0259febfb3cba0cdeee4238f..1de2262a2014f3b280a9408f30953358bb0f992e 100644 --- a/substrate/frame/authorship/src/lib.rs +++ b/substrate/frame/authorship/src/lib.rs @@ -84,9 +84,8 @@ impl Pallet { let digest = >::digest(); let pre_runtime_digests = digest.logs.iter().filter_map(|d| d.as_pre_runtime()); - T::FindAuthor::find_author(pre_runtime_digests).map(|a| { + T::FindAuthor::find_author(pre_runtime_digests).inspect(|a| { >::put(&a); - a }) } } diff --git a/substrate/frame/babe/src/mock.rs b/substrate/frame/babe/src/mock.rs index 912cb3e27cd5bdf7e31adf33bf4d5e117d00682d..d416e31b25f71ae759cdfef987100ac994085ef0 100644 --- a/substrate/frame/babe/src/mock.rs +++ b/substrate/frame/babe/src/mock.rs @@ -25,12 +25,12 @@ use frame_election_provider_support::{ }; use frame_support::{ derive_impl, parameter_types, - traits::{ConstU128, ConstU32, ConstU64, KeyOwnerProofSystem, OnInitialize}, + traits::{ConstU128, ConstU32, ConstU64, OnInitialize}, }; use pallet_session::historical as pallet_session_historical; use sp_consensus_babe::{AuthorityId, AuthorityPair, Randomness, Slot, VrfSignature}; use sp_core::{ - crypto::{KeyTypeId, Pair, VrfSecret}, + crypto::{Pair, VrfSecret}, U256, }; use sp_io; @@ -182,7 +182,7 @@ impl Config for Test { type WeightInfo = (); type MaxAuthorities = ConstU32<10>; type MaxNominators = ConstU32<100>; - type KeyOwnerProof = >::Proof; + type KeyOwnerProof = sp_session::MembershipProof; type EquivocationReportSystem = super::EquivocationReportSystem; } @@ -291,7 +291,7 @@ pub fn new_test_ext_with_pairs( authorities_len: usize, ) -> (Vec, sp_io::TestExternalities) { let pairs = (0..authorities_len) - .map(|i| AuthorityPair::from_seed(&U256::from(i).into())) + .map(|i| AuthorityPair::from_seed(&U256::from(i).to_little_endian())) .collect::>(); let public = pairs.iter().map(|p| p.public()).collect(); diff --git a/substrate/frame/bags-list/remote-tests/src/migration.rs b/substrate/frame/bags-list/remote-tests/src/migration.rs index dc133745afe073e33d466503307e86f834cc7382..03475b2358504df8b5de2827496feff6a6758cdc 100644 --- a/substrate/frame/bags-list/remote-tests/src/migration.rs +++ b/substrate/frame/bags-list/remote-tests/src/migration.rs @@ -1,18 +1,18 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Substrate. -// Polkadot is free software: you can redistribute it and/or modify +// Substrate is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Polkadot is distributed in the hope that it will be useful, +// Substrate is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . +// along with Substrate. If not, see . //! Test to check the migration of the voter bag. diff --git a/substrate/frame/bags-list/remote-tests/src/snapshot.rs b/substrate/frame/bags-list/remote-tests/src/snapshot.rs index 81a8905e6b4b2c6e2f89f00c84cbc421bf3ae92b..cb23fc6f5817d2c9029a8d29032a0a507f6026a9 100644 --- a/substrate/frame/bags-list/remote-tests/src/snapshot.rs +++ b/substrate/frame/bags-list/remote-tests/src/snapshot.rs @@ -1,18 +1,18 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Substrate. -// Polkadot is free software: you can redistribute it and/or modify +// Substrate is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Polkadot is distributed in the hope that it will be useful, +// Substrate is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . +// along with Substrate. If not, see . //! Test to execute the snapshot using the voter bag. diff --git a/substrate/frame/bags-list/remote-tests/src/try_state.rs b/substrate/frame/bags-list/remote-tests/src/try_state.rs index 83930024c89a51be8126caea07647784b8c1f66d..d4ce0d4f736ca8804f05fdabe4bd48b914447eed 100644 --- a/substrate/frame/bags-list/remote-tests/src/try_state.rs +++ b/substrate/frame/bags-list/remote-tests/src/try_state.rs @@ -1,18 +1,18 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Substrate. -// Polkadot is free software: you can redistribute it and/or modify +// Substrate is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Polkadot is distributed in the hope that it will be useful, +// Substrate is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . +// along with Substrate. If not, see . //! Test to execute the sanity-check of the voter bag. diff --git a/substrate/frame/bags-list/src/lib.rs b/substrate/frame/bags-list/src/lib.rs index f6af1da5e7b78e9274add30b81e9cf25cf499df5..ee36a3a3ebd82a063d937832f78ad95388116f77 100644 --- a/substrate/frame/bags-list/src/lib.rs +++ b/substrate/frame/bags-list/src/lib.rs @@ -491,7 +491,7 @@ impl, I: 'static> ScoreProvider for Pallet { Node::::get(id).map(|node| node.score()).unwrap_or_default() } - frame_election_provider_support::runtime_benchmarks_fuzz_or_std_enabled! { + frame_election_provider_support::runtime_benchmarks_or_std_enabled! { fn set_score_of(id: &T::AccountId, new_score: T::Score) { ListNodes::::mutate(id, |maybe_node| { if let Some(node) = maybe_node.as_mut() { diff --git a/substrate/frame/bags-list/src/list/tests.rs b/substrate/frame/bags-list/src/list/tests.rs index e5fff76d75c70bf0cec47f1083a34a3ed6f65fd6..fc4c4fbd088b24712036c139a7b8117edcdec19c 100644 --- a/substrate/frame/bags-list/src/list/tests.rs +++ b/substrate/frame/bags-list/src/list/tests.rs @@ -778,7 +778,7 @@ mod bags { assert_eq!(bag_1000.iter().count(), 3); bag_1000.insert_node_unchecked(node(4, None, None, bag_1000.bag_upper)); // panics in debug assert_eq!(bag_1000.iter().count(), 3); // in release we expect it to silently ignore the - // request. + // request. }); } diff --git a/substrate/frame/bags-list/src/mock.rs b/substrate/frame/bags-list/src/mock.rs index ea677cb9e73e37b36cee813b372efe809b47375d..3690a876f62d82ed6358307212eb28ca38c3b8d6 100644 --- a/substrate/frame/bags-list/src/mock.rs +++ b/substrate/frame/bags-list/src/mock.rs @@ -41,7 +41,7 @@ impl frame_election_provider_support::ScoreProvider for StakingMock { *NextVoteWeightMap::get().get(id).unwrap_or(&NextVoteWeight::get()) } - frame_election_provider_support::runtime_benchmarks_fuzz_or_std_enabled! { + frame_election_provider_support::runtime_benchmarks_or_std_enabled! { fn set_score_of(id: &AccountId, weight: Self::Score) { NEXT_VOTE_WEIGHT_MAP.with(|m| m.borrow_mut().insert(*id, weight)); } diff --git a/substrate/frame/balances/src/impl_fungible.rs b/substrate/frame/balances/src/impl_fungible.rs index 0f4e51f35012a23a0d229b4ad6ab3efe2e7e83d7..4470c3cc9eb1ab7092783f344a93cfb1411f3145 100644 --- a/substrate/frame/balances/src/impl_fungible.rs +++ b/substrate/frame/balances/src/impl_fungible.rs @@ -354,7 +354,9 @@ impl, I: 'static> fungible::Balanced for Pallet Self::deposit_event(Event::::Withdraw { who: who.clone(), amount }); } fn done_issue(amount: Self::Balance) { - Self::deposit_event(Event::::Issued { amount }); + if !amount.is_zero() { + Self::deposit_event(Event::::Issued { amount }); + } } fn done_rescind(amount: Self::Balance) { Self::deposit_event(Event::::Rescinded { amount }); @@ -363,6 +365,14 @@ impl, I: 'static> fungible::Balanced for Pallet impl, I: 'static> fungible::BalancedHold for Pallet {} +impl, I: 'static> + fungible::hold::DoneSlash for Pallet +{ + fn done_slash(reason: &T::RuntimeHoldReason, who: &T::AccountId, amount: T::Balance) { + T::DoneSlashHandler::done_slash(reason, who, amount); + } +} + impl, I: 'static> AccountTouch<(), T::AccountId> for Pallet { type Balance = T::Balance; fn deposit_required(_: ()) -> Self::Balance { diff --git a/substrate/frame/balances/src/lib.rs b/substrate/frame/balances/src/lib.rs index 87d2029d488e51dfeb133bf56f207d12de979f1d..65e594a904f91a93591a846e0f490038e915d2f2 100644 --- a/substrate/frame/balances/src/lib.rs +++ b/substrate/frame/balances/src/lib.rs @@ -234,6 +234,7 @@ pub mod pallet { type MaxFreezes = VariantCountOf; type WeightInfo = (); + type DoneSlashHandler = (); } } @@ -312,6 +313,14 @@ pub mod pallet { /// The maximum number of individual freeze locks that can exist on an account at any time. #[pallet::constant] type MaxFreezes: Get; + + /// Allows callbacks to other pallets so they can update their bookkeeping when a slash + /// occurs. + type DoneSlashHandler: fungible::hold::DoneSlash< + Self::RuntimeHoldReason, + Self::AccountId, + Self::Balance, + >; } /// The in-code storage version. @@ -1031,7 +1040,7 @@ pub mod pallet { } if did_provide && !does_provide { // This could reap the account so must go last. - frame_system::Pallet::::dec_providers(who).map_err(|r| { + frame_system::Pallet::::dec_providers(who).inspect_err(|_| { // best-effort revert consumer change. if did_consume && !does_consume { let _ = frame_system::Pallet::::inc_consumers(who).defensive(); @@ -1039,7 +1048,6 @@ pub mod pallet { if !did_consume && does_consume { let _ = frame_system::Pallet::::dec_consumers(who); } - r })?; } diff --git a/substrate/frame/balances/src/migration.rs b/substrate/frame/balances/src/migration.rs index 568c3fbb7cf0926f9f23b2bdd23d840fa85bad5c..920c6eea56241c0a984677530eb292546571603a 100644 --- a/substrate/frame/balances/src/migration.rs +++ b/substrate/frame/balances/src/migration.rs @@ -1,18 +1,18 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Substrate. -// Polkadot is free software: you can redistribute it and/or modify +// Substrate is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Polkadot is distributed in the hope that it will be useful, +// Substrate is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . +// along with Substrate. If not, see . use super::*; use frame_support::{ diff --git a/substrate/frame/balances/src/tests/currency_tests.rs b/substrate/frame/balances/src/tests/currency_tests.rs index 2243859458be25a6428ab76c2e37c916cd123da1..a4984b34f6dbf96671cef77e63c74e2e69caefa2 100644 --- a/substrate/frame/balances/src/tests/currency_tests.rs +++ b/substrate/frame/balances/src/tests/currency_tests.rs @@ -1017,7 +1017,7 @@ fn slash_consumed_slash_full_works() { ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { Balances::make_free_balance_be(&1, 1_000); assert_ok!(System::inc_consumers(&1)); // <-- Reference counter added here is enough for all tests - // Slashed completed in full + // Slashed completed in full assert_eq!(Balances::slash(&1, 900), (NegativeImbalance::new(900), 0)); // Account is still alive assert!(System::account_exists(&1)); @@ -1029,7 +1029,7 @@ fn slash_consumed_slash_over_works() { ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { Balances::make_free_balance_be(&1, 1_000); assert_ok!(System::inc_consumers(&1)); // <-- Reference counter added here is enough for all tests - // Slashed completed in full + // Slashed completed in full assert_eq!(Balances::slash(&1, 1_000), (NegativeImbalance::new(900), 100)); // Account is still alive assert!(System::account_exists(&1)); @@ -1041,7 +1041,7 @@ fn slash_consumed_slash_partial_works() { ExtBuilder::default().existential_deposit(100).build_and_execute_with(|| { Balances::make_free_balance_be(&1, 1_000); assert_ok!(System::inc_consumers(&1)); // <-- Reference counter added here is enough for all tests - // Slashed completed in full + // Slashed completed in full assert_eq!(Balances::slash(&1, 800), (NegativeImbalance::new(800), 0)); // Account is still alive assert!(System::account_exists(&1)); diff --git a/substrate/frame/balances/src/tests/fungible_tests.rs b/substrate/frame/balances/src/tests/fungible_tests.rs index ac373a351d343d299fc2df7e5218e9fd69661170..4a8b213c645e0fc37a80edc911d7eebb68ed9232 100644 --- a/substrate/frame/balances/src/tests/fungible_tests.rs +++ b/substrate/frame/balances/src/tests/fungible_tests.rs @@ -518,8 +518,9 @@ fn freeze_consideration_works() { let who = 4; // freeze amount taken somewhere outside of our (Consideration) scope. let extend_freeze = 15; - let zero_ticket = Consideration::new(&who, Footprint::from_parts(0, 0)).unwrap(); - assert!(zero_ticket.is_none()); + + let ticket = Consideration::new(&who, Footprint::from_parts(0, 0)).unwrap(); + assert!(ticket.is_none()); assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 0); let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); @@ -536,7 +537,6 @@ fn freeze_consideration_works() { let ticket = ticket.update(&who, Footprint::from_parts(0, 0)).unwrap(); assert!(ticket.is_none()); - assert_eq!(ticket, zero_ticket); assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 0 + extend_freeze); let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); @@ -565,11 +565,11 @@ fn hold_consideration_works() { // hold amount taken somewhere outside of our (Consideration) scope. let extend_hold = 15; - let zero_ticket = Consideration::new(&who, Footprint::from_parts(0, 0)).unwrap(); - assert!(zero_ticket.is_none()); + let ticket = Consideration::new(&who, Footprint::from_parts(0, 0)).unwrap(); + assert!(ticket.is_none()); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 0); - let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); + let ticket = ticket.update(&who, Footprint::from_parts(10, 1)).unwrap(); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 10); let ticket = ticket.update(&who, Footprint::from_parts(4, 1)).unwrap(); @@ -583,7 +583,6 @@ fn hold_consideration_works() { let ticket = ticket.update(&who, Footprint::from_parts(0, 0)).unwrap(); assert!(ticket.is_none()); - assert_eq!(ticket, zero_ticket); assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 0 + extend_hold); let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap(); diff --git a/substrate/frame/balances/src/weights.rs b/substrate/frame/balances/src/weights.rs index e82c97160efcfdac6b994345e0baeb787e69bcb9..55decef273f6c919e57d4b2029626fb051838900 100644 --- a/substrate/frame/balances/src/weights.rs +++ b/substrate/frame/balances/src/weights.rs @@ -17,27 +17,27 @@ //! Autogenerated weights for `pallet_balances` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-05-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 42.0.0 +//! DATE: 2024-09-04, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-unxyhko3-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` +//! HOSTNAME: `8f4ffe8f7785`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` // Executed Command: -// target/production/substrate-node +// frame-omni-bencher +// v1 // benchmark // pallet -// --steps=50 -// --repeat=20 // --extrinsic=* +// --runtime=target/release/wbuild/kitchensink-runtime/kitchensink_runtime.wasm +// --pallet=pallet_balances +// --header=/__w/polkadot-sdk/polkadot-sdk/substrate/HEADER-APACHE2 +// --output=/__w/polkadot-sdk/polkadot-sdk/substrate/frame/balances/src/weights.rs // --wasm-execution=compiled +// --steps=50 +// --repeat=20 // --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json -// --pallet=pallet_balances -// --chain=dev -// --header=./substrate/HEADER-APACHE2 -// --output=./substrate/frame/balances/src/weights.rs -// --template=./substrate/.maintain/frame-weight-template.hbs +// --template=substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -71,8 +71,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 47_552_000 picoseconds. - Weight::from_parts(48_363_000, 3593) + // Minimum execution time: 75_624_000 picoseconds. + Weight::from_parts(77_290_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -82,8 +82,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 37_565_000 picoseconds. - Weight::from_parts(38_159_000, 3593) + // Minimum execution time: 60_398_000 picoseconds. + Weight::from_parts(61_290_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -91,10 +91,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn force_set_balance_creating() -> Weight { // Proof Size summary in bytes: - // Measured: `174` + // Measured: `52` // Estimated: `3593` - // Minimum execution time: 14_147_000 picoseconds. - Weight::from_parts(14_687_000, 3593) + // Minimum execution time: 18_963_000 picoseconds. + Weight::from_parts(19_802_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -102,10 +102,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn force_set_balance_killing() -> Weight { // Proof Size summary in bytes: - // Measured: `174` + // Measured: `52` // Estimated: `3593` - // Minimum execution time: 19_188_000 picoseconds. - Weight::from_parts(19_929_000, 3593) + // Minimum execution time: 30_517_000 picoseconds. + Weight::from_parts(31_293_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -113,10 +113,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn force_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `103` + // Measured: `52` // Estimated: `6196` - // Minimum execution time: 48_903_000 picoseconds. - Weight::from_parts(49_944_000, 6196) + // Minimum execution time: 77_017_000 picoseconds. + Weight::from_parts(78_184_000, 6196) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -126,8 +126,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 46_573_000 picoseconds. - Weight::from_parts(47_385_000, 3593) + // Minimum execution time: 75_600_000 picoseconds. + Weight::from_parts(76_817_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -135,10 +135,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn force_unreserve() -> Weight { // Proof Size summary in bytes: - // Measured: `174` + // Measured: `52` // Estimated: `3593` - // Minimum execution time: 16_750_000 picoseconds. - Weight::from_parts(17_233_000, 3593) + // Minimum execution time: 24_503_000 picoseconds. + Weight::from_parts(25_026_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -149,10 +149,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0 + u * (135 ยฑ0)` // Estimated: `990 + u * (2603 ยฑ0)` - // Minimum execution time: 16_333_000 picoseconds. - Weight::from_parts(16_588_000, 990) - // Standard Error: 12_254 - .saturating_add(Weight::from_parts(13_973_659, 0).saturating_mul(u.into())) + // Minimum execution time: 24_077_000 picoseconds. + Weight::from_parts(24_339_000, 990) + // Standard Error: 18_669 + .saturating_add(Weight::from_parts(21_570_294, 0).saturating_mul(u.into())) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(u.into()))) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(u.into()))) .saturating_add(Weight::from_parts(0, 2603).saturating_mul(u.into())) @@ -161,22 +161,22 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_265_000 picoseconds. - Weight::from_parts(6_594_000, 0) + // Minimum execution time: 8_070_000 picoseconds. + Weight::from_parts(8_727_000, 0) } fn burn_allow_death() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 30_151_000 picoseconds. - Weight::from_parts(30_968_000, 0) + // Minimum execution time: 46_978_000 picoseconds. + Weight::from_parts(47_917_000, 0) } fn burn_keep_alive() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 20_055_000 picoseconds. - Weight::from_parts(20_711_000, 0) + // Minimum execution time: 31_141_000 picoseconds. + Weight::from_parts(31_917_000, 0) } } @@ -188,8 +188,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 47_552_000 picoseconds. - Weight::from_parts(48_363_000, 3593) + // Minimum execution time: 75_624_000 picoseconds. + Weight::from_parts(77_290_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -199,8 +199,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 37_565_000 picoseconds. - Weight::from_parts(38_159_000, 3593) + // Minimum execution time: 60_398_000 picoseconds. + Weight::from_parts(61_290_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -208,10 +208,10 @@ impl WeightInfo for () { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn force_set_balance_creating() -> Weight { // Proof Size summary in bytes: - // Measured: `174` + // Measured: `52` // Estimated: `3593` - // Minimum execution time: 14_147_000 picoseconds. - Weight::from_parts(14_687_000, 3593) + // Minimum execution time: 18_963_000 picoseconds. + Weight::from_parts(19_802_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -219,10 +219,10 @@ impl WeightInfo for () { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn force_set_balance_killing() -> Weight { // Proof Size summary in bytes: - // Measured: `174` + // Measured: `52` // Estimated: `3593` - // Minimum execution time: 19_188_000 picoseconds. - Weight::from_parts(19_929_000, 3593) + // Minimum execution time: 30_517_000 picoseconds. + Weight::from_parts(31_293_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -230,10 +230,10 @@ impl WeightInfo for () { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn force_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `103` + // Measured: `52` // Estimated: `6196` - // Minimum execution time: 48_903_000 picoseconds. - Weight::from_parts(49_944_000, 6196) + // Minimum execution time: 77_017_000 picoseconds. + Weight::from_parts(78_184_000, 6196) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -243,8 +243,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `3593` - // Minimum execution time: 46_573_000 picoseconds. - Weight::from_parts(47_385_000, 3593) + // Minimum execution time: 75_600_000 picoseconds. + Weight::from_parts(76_817_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -252,10 +252,10 @@ impl WeightInfo for () { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) fn force_unreserve() -> Weight { // Proof Size summary in bytes: - // Measured: `174` + // Measured: `52` // Estimated: `3593` - // Minimum execution time: 16_750_000 picoseconds. - Weight::from_parts(17_233_000, 3593) + // Minimum execution time: 24_503_000 picoseconds. + Weight::from_parts(25_026_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -266,10 +266,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0 + u * (135 ยฑ0)` // Estimated: `990 + u * (2603 ยฑ0)` - // Minimum execution time: 16_333_000 picoseconds. - Weight::from_parts(16_588_000, 990) - // Standard Error: 12_254 - .saturating_add(Weight::from_parts(13_973_659, 0).saturating_mul(u.into())) + // Minimum execution time: 24_077_000 picoseconds. + Weight::from_parts(24_339_000, 990) + // Standard Error: 18_669 + .saturating_add(Weight::from_parts(21_570_294, 0).saturating_mul(u.into())) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(u.into()))) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(u.into()))) .saturating_add(Weight::from_parts(0, 2603).saturating_mul(u.into())) @@ -278,21 +278,21 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_265_000 picoseconds. - Weight::from_parts(6_594_000, 0) + // Minimum execution time: 8_070_000 picoseconds. + Weight::from_parts(8_727_000, 0) } fn burn_allow_death() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 30_151_000 picoseconds. - Weight::from_parts(30_968_000, 0) + // Minimum execution time: 46_978_000 picoseconds. + Weight::from_parts(47_917_000, 0) } fn burn_keep_alive() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 20_055_000 picoseconds. - Weight::from_parts(20_711_000, 0) + // Minimum execution time: 31_141_000 picoseconds. + Weight::from_parts(31_917_000, 0) } } diff --git a/substrate/frame/beefy-mmr/src/benchmarking.rs b/substrate/frame/beefy-mmr/src/benchmarking.rs index 135f95eabb998ba2709c232940b608966760c6c4..fea6a2078f0f1b1fd50b4e29e9d14abcb2c5b5fa 100644 --- a/substrate/frame/beefy-mmr/src/benchmarking.rs +++ b/substrate/frame/beefy-mmr/src/benchmarking.rs @@ -51,9 +51,7 @@ mod benchmarks { #[benchmark] fn extract_validation_context() { - if !cfg!(feature = "test") { - pallet_mmr::UseLocalStorage::::set(true); - } + pallet_mmr::UseLocalStorage::::set(true); init_block::(1); let header = System::::finalize(); @@ -71,9 +69,7 @@ mod benchmarks { #[benchmark] fn read_peak() { - if !cfg!(feature = "test") { - pallet_mmr::UseLocalStorage::::set(true); - } + pallet_mmr::UseLocalStorage::::set(true); init_block::(1); @@ -91,9 +87,7 @@ mod benchmarks { /// the verification. We need to account for the peaks separately. #[benchmark] fn n_items_proof_is_non_canonical(n: Linear<2, 512>) { - if !cfg!(feature = "test") { - pallet_mmr::UseLocalStorage::::set(true); - } + pallet_mmr::UseLocalStorage::::set(true); for block_num in 1..=n { init_block::(block_num); diff --git a/substrate/frame/beefy-mmr/src/lib.rs b/substrate/frame/beefy-mmr/src/lib.rs index 73119c3faa9b28abd1e1c6018939a9156e9f9276..ef99bc1e9cf11821718085804b50f25b80e5e8f0 100644 --- a/substrate/frame/beefy-mmr/src/lib.rs +++ b/substrate/frame/beefy-mmr/src/lib.rs @@ -258,17 +258,33 @@ where }, }; - let commitment_root = - match commitment.payload.get_decoded::>(&known_payloads::MMR_ROOT_ID) { - Some(commitment_root) => commitment_root, + let mut found_commitment_root = false; + let commitment_roots = commitment + .payload + .get_all_decoded::>(&known_payloads::MMR_ROOT_ID); + for maybe_commitment_root in commitment_roots { + match maybe_commitment_root { + Some(commitment_root) => { + found_commitment_root = true; + if canonical_prev_root != commitment_root { + // If the commitment contains an MMR root, that is not equal to + // `canonical_prev_root`, the commitment is invalid + return true; + } + }, None => { - // If the commitment doesn't contain any MMR root, while the proof is valid, - // the commitment is invalid - return true + // If the commitment contains an MMR root, that can't be decoded, it is invalid. + return true; }, - }; + } + } + if !found_commitment_root { + // If the commitment doesn't contain any MMR root, while the proof is valid, + // the commitment is invalid + return true; + } - canonical_prev_root != commitment_root + false } } diff --git a/substrate/frame/beefy-mmr/src/tests.rs b/substrate/frame/beefy-mmr/src/tests.rs index b126a01012b4e9e1c57dcc223b4981dd5fe6946a..297fb28647ac3221f0ad824eab1d9c289e430f37 100644 --- a/substrate/frame/beefy-mmr/src/tests.rs +++ b/substrate/frame/beefy-mmr/src/tests.rs @@ -278,8 +278,28 @@ fn is_non_canonical_should_work_correctly() { &Commitment { payload: Payload::from_single_entry( known_payloads::MMR_ROOT_ID, - H256::repeat_byte(0).encode(), - ), + prev_roots[250 - 1].encode() + ) + .push_raw(known_payloads::MMR_ROOT_ID, H256::repeat_byte(0).encode(),), + block_number: 250, + validator_set_id: 0, + }, + valid_proof.clone(), + Mmr::mmr_root(), + ), + true + ); + + // If the `commitment.payload` contains an MMR root that can't be decoded, + // it's non-canonical. + assert_eq!( + BeefyMmr::is_non_canonical( + &Commitment { + payload: Payload::from_single_entry( + known_payloads::MMR_ROOT_ID, + prev_roots[250 - 1].encode() + ) + .push_raw(known_payloads::MMR_ROOT_ID, vec![],), block_number: 250, validator_set_id: 0, }, diff --git a/substrate/frame/benchmarking/pov/src/benchmarking.rs b/substrate/frame/benchmarking/pov/src/benchmarking.rs index bf3d406d0b2b04364646ac4051f73d7efcebe502..d52fcc2689c4dea7725d912de47d7ed0f365b29e 100644 --- a/substrate/frame/benchmarking/pov/src/benchmarking.rs +++ b/substrate/frame/benchmarking/pov/src/benchmarking.rs @@ -26,6 +26,11 @@ use frame_support::traits::UnfilteredDispatchable; use frame_system::{Pallet as System, RawOrigin}; use sp_runtime::traits::Hash; +#[cfg(feature = "std")] +frame_support::parameter_types! { + pub static StorageRootHash: Option> = None; +} + #[benchmarks] mod benchmarks { use super::*; @@ -392,6 +397,32 @@ mod benchmarks { } } + #[benchmark] + fn storage_root_is_the_same_every_time(i: Linear<0, 10>) { + #[cfg(feature = "std")] + let root = sp_io::storage::root(sp_runtime::StateVersion::V1); + + #[cfg(feature = "std")] + match (i, StorageRootHash::get()) { + (0, Some(_)) => panic!("StorageRootHash should be None initially"), + (0, None) => StorageRootHash::set(Some(root)), + (_, Some(r)) if r == root => {}, + (_, Some(r)) => + panic!("StorageRootHash should be the same every time: {:?} vs {:?}", r, root), + (_, None) => panic!("StorageRootHash should be Some after the first iteration"), + } + + // Also test that everything is reset correctly: + sp_io::storage::set(b"key1", b"value"); + + #[block] + { + sp_io::storage::set(b"key2", b"value"); + } + + sp_io::storage::set(b"key3", b"value"); + } + impl_benchmark_test_suite!(Pallet, super::mock::new_test_ext(), super::mock::Test,); } diff --git a/substrate/frame/benchmarking/pov/src/weights.rs b/substrate/frame/benchmarking/pov/src/weights.rs index c4fc03d1dd937bba6ea0f83eec89c88704a6f31e..1f20d5f0b515fe403db7607041af80b5a84f9242 100644 --- a/substrate/frame/benchmarking/pov/src/weights.rs +++ b/substrate/frame/benchmarking/pov/src/weights.rs @@ -45,6 +45,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; /// Weight functions needed for `frame_benchmarking_pallet_pov`. +#[allow(dead_code)] pub trait WeightInfo { fn storage_single_value_read() -> Weight; fn storage_single_value_ignored_read() -> Weight; @@ -79,6 +80,7 @@ pub trait WeightInfo { } /// Weights for `frame_benchmarking_pallet_pov` using the Substrate node and recommended hardware. +#[allow(dead_code)] pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { /// Storage: `Pov::Value` (r:1 w:0) diff --git a/substrate/frame/bounties/src/lib.rs b/substrate/frame/bounties/src/lib.rs index 7b89a6e3e76f5ead57e76c17a304080d2dc978d0..e30d6fa2d143da7514facecb364b03d07991c964 100644 --- a/substrate/frame/bounties/src/lib.rs +++ b/substrate/frame/bounties/src/lib.rs @@ -459,12 +459,12 @@ pub mod pallet { Bounties::::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult { let bounty = maybe_bounty.as_mut().ok_or(Error::::InvalidIndex)?; - let slash_curator = |curator: &T::AccountId, - curator_deposit: &mut BalanceOf| { - let imbalance = T::Currency::slash_reserved(curator, *curator_deposit).0; - T::OnSlash::on_unbalanced(imbalance); - *curator_deposit = Zero::zero(); - }; + let slash_curator = + |curator: &T::AccountId, curator_deposit: &mut BalanceOf| { + let imbalance = T::Currency::slash_reserved(curator, *curator_deposit).0; + T::OnSlash::on_unbalanced(imbalance); + *curator_deposit = Zero::zero(); + }; match bounty.status { BountyStatus::Proposed | BountyStatus::Approved | BountyStatus::Funded => { diff --git a/substrate/frame/broker/src/test_fungibles.rs b/substrate/frame/broker/src/test_fungibles.rs index b0a06fc1a326d837e49506726a211ca600230138..1015e1fac252e23d9ccdfc7a667f640adde0f868 100644 --- a/substrate/frame/broker/src/test_fungibles.rs +++ b/substrate/frame/broker/src/test_fungibles.rs @@ -269,6 +269,20 @@ where { } +impl< + Instance: Get, + AccountId: Encode, + AssetId: tokens::AssetId + Copy, + MinimumBalance: TypedGet, + HoldReason: Encode + Decode + TypeInfo + 'static, + Balance: tokens::Balance, + > fungibles::hold::DoneSlash + for TestFungibles +where + MinimumBalance::Type: tokens::Balance, +{ +} + impl< Instance: Get, AccountId: Encode, diff --git a/substrate/frame/child-bounties/src/lib.rs b/substrate/frame/child-bounties/src/lib.rs index 911fd4c4c49f7118557dcc04dbe53600f4cbd1f7..660a30ca5d26424e56597575b286f443bfed658d 100644 --- a/substrate/frame/child-bounties/src/lib.rs +++ b/substrate/frame/child-bounties/src/lib.rs @@ -473,12 +473,13 @@ pub mod pallet { let child_bounty = maybe_child_bounty.as_mut().ok_or(BountiesError::::InvalidIndex)?; - let slash_curator = |curator: &T::AccountId, - curator_deposit: &mut BalanceOf| { - let imbalance = T::Currency::slash_reserved(curator, *curator_deposit).0; - T::OnSlash::on_unbalanced(imbalance); - *curator_deposit = Zero::zero(); - }; + let slash_curator = + |curator: &T::AccountId, curator_deposit: &mut BalanceOf| { + let imbalance = + T::Currency::slash_reserved(curator, *curator_deposit).0; + T::OnSlash::on_unbalanced(imbalance); + *curator_deposit = Zero::zero(); + }; match child_bounty.status { ChildBountyStatus::Added => { diff --git a/substrate/frame/collective/Cargo.toml b/substrate/frame/collective/Cargo.toml index f6810f26f0566bc48f04e7e130db1024d25737b4..59a9d23f7b1958b4137ea6b8412a0d3bfe44e163 100644 --- a/substrate/frame/collective/Cargo.toml +++ b/substrate/frame/collective/Cargo.toml @@ -17,15 +17,19 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { features = ["derive"], workspace = true } +docify = { workspace = true } log = { workspace = true } scale-info = { features = ["derive"], workspace = true } frame-benchmarking = { optional = true, workspace = true } -frame-support = { workspace = true } +frame-support = { features = ["experimental"], workspace = true } frame-system = { workspace = true } sp-core = { workspace = true } sp-io = { workspace = true } sp-runtime = { workspace = true } +[dev-dependencies] +pallet-balances = { workspace = true, default-features = false } + [features] default = ["std"] std = [ @@ -34,6 +38,7 @@ std = [ "frame-support/std", "frame-system/std", "log/std", + "pallet-balances/std", "scale-info/std", "sp-core/std", "sp-io/std", @@ -43,10 +48,12 @@ runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", "sp-runtime/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime", + "pallet-balances/try-runtime", "sp-runtime/try-runtime", ] diff --git a/substrate/frame/collective/src/benchmarking.rs b/substrate/frame/collective/src/benchmarking.rs index 7b5df17b60a696a17b706dd01f05f2a01d3e787e..baf7cce8437f333236a75008fed23e2a2506ba61 100644 --- a/substrate/frame/collective/src/benchmarking.rs +++ b/substrate/frame/collective/src/benchmarking.rs @@ -23,7 +23,10 @@ use crate::Pallet as Collective; use core::mem::size_of; use sp_runtime::traits::Bounded; -use frame_benchmarking::v1::{account, benchmarks_instance_pallet, whitelisted_caller}; +use frame_benchmarking::{ + v1::{account, whitelisted_caller}, + v2::*, +}; use frame_system::{ pallet_prelude::BlockNumberFor, Call as SystemCall, Pallet as System, RawOrigin as SystemOrigin, }; @@ -36,24 +39,31 @@ fn assert_last_event, I: 'static>(generic_event: >:: frame_system::Pallet::::assert_last_event(generic_event.into()); } +fn assert_has_event, I: 'static>(generic_event: >::RuntimeEvent) { + frame_system::Pallet::::assert_has_event(generic_event.into()); +} + fn id_to_remark_data(id: u32, length: usize) -> Vec { id.to_le_bytes().into_iter().cycle().take(length).collect() } -benchmarks_instance_pallet! { - set_members { - let m in 0 .. T::MaxMembers::get(); - let n in 0 .. T::MaxMembers::get(); - let p in 0 .. T::MaxProposals::get(); +#[instance_benchmarks(where T: Config, I: 'static)] +mod benchmarks { + use super::*; + #[benchmark] + fn set_members( + m: Linear<0, { T::MaxMembers::get() }>, + n: Linear<0, { T::MaxMembers::get() }>, + p: Linear<0, { T::MaxProposals::get() }>, + ) -> Result<(), BenchmarkError> { // Set old members. // We compute the difference of old and new members, so it should influence timing. let mut old_members = vec![]; - for i in 0 .. m { + for i in 0..m { let old_member = account::("old member", i, SEED); old_members.push(old_member); } - let old_members_count = old_members.len() as u32; Collective::::set_members( SystemOrigin::Root.into(), @@ -64,24 +74,27 @@ benchmarks_instance_pallet! { // If there were any old members generate a bunch of proposals. if m > 0 { + let caller = old_members.last().unwrap().clone(); // Set a high threshold for proposals passing so that they stay around. let threshold = m.max(2); // Length of the proposals should be irrelevant to `set_members`. let length = 100; - for i in 0 .. p { + for i in 0..p { + T::Consideration::ensure_successful(&caller, i); // Proposals should be different so that different proposal hashes are generated - let proposal: T::Proposal = SystemCall::::remark { remark: id_to_remark_data(i, length) }.into(); + let proposal: T::Proposal = + SystemCall::::remark { remark: id_to_remark_data(i, length) }.into(); Collective::::propose( - SystemOrigin::Signed(old_members.last().unwrap().clone()).into(), + SystemOrigin::Signed(caller.clone()).into(), threshold, Box::new(proposal.clone()), MAX_BYTES, )?; let hash = T::Hashing::hash_of(&proposal); // Vote on the proposal to increase state relevant for `set_members`. - // Not voting for last old member because they proposed and not voting for the first member - // to keep the proposal from passing. - for j in 2 .. m - 1 { + // Not voting for last old member because they proposed and not voting for the first + // member to keep the proposal from passing. + for j in 2..m - 1 { let voter = &old_members[j as usize]; let approve = true; Collective::::vote( @@ -97,26 +110,33 @@ benchmarks_instance_pallet! { // Construct `new_members`. // It should influence timing since it will sort this vector. let mut new_members = vec![]; - for i in 0 .. n { + for i in 0..n { let member = account::("member", i, SEED); new_members.push(member); } + #[extrinsic_call] + _( + SystemOrigin::Root, + new_members.clone(), + new_members.last().cloned(), + T::MaxMembers::get(), + ); - }: _(SystemOrigin::Root, new_members.clone(), new_members.last().cloned(), T::MaxMembers::get()) - verify { new_members.sort(); assert_eq!(Members::::get(), new_members); + Ok(()) } - execute { - let b in 2 .. MAX_BYTES; - let m in 1 .. T::MaxMembers::get(); - + #[benchmark] + fn execute( + b: Linear<2, MAX_BYTES>, + m: Linear<1, { T::MaxMembers::get() }>, + ) -> Result<(), BenchmarkError> { let bytes_in_storage = b + size_of::() as u32; // Construct `members`. let mut members = vec![]; - for i in 0 .. m - 1 { + for i in 0..m - 1 { let member = account::("member", i, SEED); members.push(member); } @@ -124,29 +144,36 @@ benchmarks_instance_pallet! { let caller: T::AccountId = whitelisted_caller(); members.push(caller.clone()); - Collective::::set_members(SystemOrigin::Root.into(), members, None, T::MaxMembers::get())?; + Collective::::set_members( + SystemOrigin::Root.into(), + members, + None, + T::MaxMembers::get(), + )?; + + let proposal: T::Proposal = + SystemCall::::remark { remark: id_to_remark_data(1, b as usize) }.into(); - let proposal: T::Proposal = SystemCall::::remark { remark: id_to_remark_data(1, b as usize) }.into(); + #[extrinsic_call] + _(SystemOrigin::Signed(caller), Box::new(proposal.clone()), bytes_in_storage); - }: _(SystemOrigin::Signed(caller), Box::new(proposal.clone()), bytes_in_storage) - verify { let proposal_hash = T::Hashing::hash_of(&proposal); // Note that execution fails due to mis-matched origin - assert_last_event::( - Event::MemberExecuted { proposal_hash, result: Ok(()) }.into() - ); + assert_last_event::(Event::MemberExecuted { proposal_hash, result: Ok(()) }.into()); + Ok(()) } // This tests when execution would happen immediately after proposal - propose_execute { - let b in 2 .. MAX_BYTES; - let m in 1 .. T::MaxMembers::get(); - + #[benchmark] + fn propose_execute( + b: Linear<2, MAX_BYTES>, + m: Linear<1, { T::MaxMembers::get() }>, + ) -> Result<(), BenchmarkError> { let bytes_in_storage = b + size_of::() as u32; // Construct `members`. let mut members = vec![]; - for i in 0 .. m - 1 { + for i in 0..m - 1 { let member = account::("member", i, SEED); members.push(member); } @@ -154,43 +181,62 @@ benchmarks_instance_pallet! { let caller: T::AccountId = whitelisted_caller(); members.push(caller.clone()); - Collective::::set_members(SystemOrigin::Root.into(), members, None, T::MaxMembers::get())?; + Collective::::set_members( + SystemOrigin::Root.into(), + members, + None, + T::MaxMembers::get(), + )?; - let proposal: T::Proposal = SystemCall::::remark { remark: id_to_remark_data(1, b as usize) }.into(); + let proposal: T::Proposal = + SystemCall::::remark { remark: id_to_remark_data(1, b as usize) }.into(); let threshold = 1; - }: propose(SystemOrigin::Signed(caller), threshold, Box::new(proposal.clone()), bytes_in_storage) - verify { + #[extrinsic_call] + propose( + SystemOrigin::Signed(caller), + threshold, + Box::new(proposal.clone()), + bytes_in_storage, + ); + let proposal_hash = T::Hashing::hash_of(&proposal); // Note that execution fails due to mis-matched origin - assert_last_event::( - Event::Executed { proposal_hash, result: Ok(()) }.into() - ); + assert_last_event::(Event::Executed { proposal_hash, result: Ok(()) }.into()); + Ok(()) } // This tests when proposal is created and queued as "proposed" - propose_proposed { - let b in 2 .. MAX_BYTES; - let m in 2 .. T::MaxMembers::get(); - let p in 1 .. T::MaxProposals::get(); - + #[benchmark] + fn propose_proposed( + b: Linear<2, MAX_BYTES>, + m: Linear<2, { T::MaxMembers::get() }>, + p: Linear<1, { T::MaxProposals::get() }>, + ) -> Result<(), BenchmarkError> { let bytes_in_storage = b + size_of::() as u32; // Construct `members`. let mut members = vec![]; - for i in 0 .. m - 1 { + for i in 0..m - 1 { let member = account::("member", i, SEED); members.push(member); } let caller: T::AccountId = whitelisted_caller(); members.push(caller.clone()); - Collective::::set_members(SystemOrigin::Root.into(), members, None, T::MaxMembers::get())?; + Collective::::set_members( + SystemOrigin::Root.into(), + members, + None, + T::MaxMembers::get(), + )?; let threshold = m; // Add previous proposals. - for i in 0 .. p - 1 { + for i in 0..p - 1 { + T::Consideration::ensure_successful(&caller, i); // Proposals should be different so that different proposal hashes are generated - let proposal: T::Proposal = SystemCall::::remark { remark: id_to_remark_data(i, b as usize) }.into(); + let proposal: T::Proposal = + SystemCall::::remark { remark: id_to_remark_data(i, b as usize) }.into(); Collective::::propose( SystemOrigin::Signed(caller.clone()).into(), threshold, @@ -201,20 +247,31 @@ benchmarks_instance_pallet! { assert_eq!(Proposals::::get().len(), (p - 1) as usize); - let proposal: T::Proposal = SystemCall::::remark { remark: id_to_remark_data(p, b as usize) }.into(); + T::Consideration::ensure_successful(&caller, p); + + let proposal: T::Proposal = + SystemCall::::remark { remark: id_to_remark_data(p, b as usize) }.into(); + #[extrinsic_call] + propose( + SystemOrigin::Signed(caller.clone()), + threshold, + Box::new(proposal.clone()), + bytes_in_storage, + ); - }: propose(SystemOrigin::Signed(caller.clone()), threshold, Box::new(proposal.clone()), bytes_in_storage) - verify { // New proposal is recorded assert_eq!(Proposals::::get().len(), p as usize); let proposal_hash = T::Hashing::hash_of(&proposal); - assert_last_event::(Event::Proposed { account: caller, proposal_index: p - 1, proposal_hash, threshold }.into()); + assert_last_event::( + Event::Proposed { account: caller, proposal_index: p - 1, proposal_hash, threshold } + .into(), + ); + Ok(()) } - vote { - // We choose 5 as a minimum so we always trigger a vote in the voting loop (`for j in ...`) - let m in 5 .. T::MaxMembers::get(); - + #[benchmark] + // We choose 5 as a minimum so we always trigger a vote in the voting loop (`for j in ...`) + fn vote(m: Linear<5, { T::MaxMembers::get() }>) -> Result<(), BenchmarkError> { let p = T::MaxProposals::get(); let b = MAX_BYTES; let bytes_in_storage = b + size_of::() as u32; @@ -223,22 +280,29 @@ benchmarks_instance_pallet! { let mut members = vec![]; let proposer: T::AccountId = account::("proposer", 0, SEED); members.push(proposer.clone()); - for i in 1 .. m - 1 { + for i in 1..m - 1 { let member = account::("member", i, SEED); members.push(member); } let voter: T::AccountId = account::("voter", 0, SEED); members.push(voter.clone()); - Collective::::set_members(SystemOrigin::Root.into(), members.clone(), None, T::MaxMembers::get())?; + Collective::::set_members( + SystemOrigin::Root.into(), + members.clone(), + None, + T::MaxMembers::get(), + )?; // Threshold is 1 less than the number of members so that one person can vote nay let threshold = m - 1; // Add previous proposals let mut last_hash = T::Hash::default(); - for i in 0 .. p { + for i in 0..p { + T::Consideration::ensure_successful(&proposer, i); // Proposals should be different so that different proposal hashes are generated - let proposal: T::Proposal = SystemCall::::remark { remark: id_to_remark_data(i, b as usize) }.into(); + let proposal: T::Proposal = + SystemCall::::remark { remark: id_to_remark_data(i, b as usize) }.into(); Collective::::propose( SystemOrigin::Signed(proposer.clone()).into(), threshold, @@ -250,7 +314,7 @@ benchmarks_instance_pallet! { let index = p - 1; // Have almost everyone vote aye on last proposal, while keeping it from passing. - for j in 0 .. m - 3 { + for j in 0..m - 3 { let voter = &members[j as usize]; let approve = true; Collective::::vote( @@ -277,20 +341,24 @@ benchmarks_instance_pallet! { // Whitelist voter account from further DB operations. let voter_key = frame_system::Account::::hashed_key_for(&voter); frame_benchmarking::benchmarking::add_to_whitelist(voter_key.into()); - }: _(SystemOrigin::Signed(voter), last_hash, index, approve) - verify { + + #[extrinsic_call] + _(SystemOrigin::Signed(voter), last_hash, index, approve); + // All proposals exist and the last proposal has just been updated. assert_eq!(Proposals::::get().len(), p as usize); let voting = Voting::::get(&last_hash).ok_or("Proposal Missing")?; assert_eq!(voting.ayes.len(), (m - 3) as usize); assert_eq!(voting.nays.len(), 1); + Ok(()) } - close_early_disapproved { - // We choose 4 as a minimum so we always trigger a vote in the voting loop (`for j in ...`) - let m in 4 .. T::MaxMembers::get(); - let p in 1 .. T::MaxProposals::get(); - + // We choose 4 as a minimum so we always trigger a vote in the voting loop (`for j in ...`) + #[benchmark] + fn close_early_disapproved( + m: Linear<4, { T::MaxMembers::get() }>, + p: Linear<1, { T::MaxProposals::get() }>, + ) -> Result<(), BenchmarkError> { let bytes = 100; let bytes_in_storage = bytes + size_of::() as u32; @@ -298,22 +366,29 @@ benchmarks_instance_pallet! { let mut members = vec![]; let proposer = account::("proposer", 0, SEED); members.push(proposer.clone()); - for i in 1 .. m - 1 { + for i in 1..m - 1 { let member = account::("member", i, SEED); members.push(member); } let voter = account::("voter", 0, SEED); members.push(voter.clone()); - Collective::::set_members(SystemOrigin::Root.into(), members.clone(), None, T::MaxMembers::get())?; + Collective::::set_members( + SystemOrigin::Root.into(), + members.clone(), + None, + T::MaxMembers::get(), + )?; // Threshold is total members so that one nay will disapprove the vote let threshold = m; // Add previous proposals let mut last_hash = T::Hash::default(); - for i in 0 .. p { + for i in 0..p { + T::Consideration::ensure_successful(&proposer, i); // Proposals should be different so that different proposal hashes are generated - let proposal: T::Proposal = SystemCall::::remark { remark: id_to_remark_data(i, bytes as usize) }.into(); + let proposal: T::Proposal = + SystemCall::::remark { remark: id_to_remark_data(i, bytes as usize) }.into(); Collective::::propose( SystemOrigin::Signed(proposer.clone()).into(), threshold, @@ -325,7 +400,7 @@ benchmarks_instance_pallet! { let index = p - 1; // Have most everyone vote aye on last proposal, while keeping it from passing. - for j in 0 .. m - 2 { + for j in 0..m - 2 { let voter = &members[j as usize]; let approve = true; Collective::::vote( @@ -358,39 +433,50 @@ benchmarks_instance_pallet! { // Whitelist voter account from further DB operations. let voter_key = frame_system::Account::::hashed_key_for(&voter); frame_benchmarking::benchmarking::add_to_whitelist(voter_key.into()); - }: close(SystemOrigin::Signed(voter), last_hash, index, Weight::MAX, bytes_in_storage) - verify { + + #[extrinsic_call] + close(SystemOrigin::Signed(voter), last_hash, index, Weight::MAX, bytes_in_storage); + // The last proposal is removed. assert_eq!(Proposals::::get().len(), (p - 1) as usize); assert_last_event::(Event::Disapproved { proposal_hash: last_hash }.into()); + Ok(()) } - close_early_approved { - let b in 2 .. MAX_BYTES; - // We choose 4 as a minimum so we always trigger a vote in the voting loop (`for j in ...`) - let m in 4 .. T::MaxMembers::get(); - let p in 1 .. T::MaxProposals::get(); - + // m: we choose 4 as a minimum so we always trigger a vote in the voting loop (`for j in ...`) + #[benchmark] + fn close_early_approved( + b: Linear<2, MAX_BYTES>, + m: Linear<4, { T::MaxMembers::get() }>, + p: Linear<1, { T::MaxProposals::get() }>, + ) -> Result<(), BenchmarkError> { let bytes_in_storage = b + size_of::() as u32; // Construct `members`. let mut members = vec![]; - for i in 0 .. m - 1 { + for i in 0..m - 1 { let member = account::("member", i, SEED); members.push(member); } let caller: T::AccountId = whitelisted_caller(); members.push(caller.clone()); - Collective::::set_members(SystemOrigin::Root.into(), members.clone(), None, T::MaxMembers::get())?; + Collective::::set_members( + SystemOrigin::Root.into(), + members.clone(), + None, + T::MaxMembers::get(), + )?; // Threshold is 2 so any two ayes will approve the vote let threshold = 2; // Add previous proposals let mut last_hash = T::Hash::default(); - for i in 0 .. p { + for i in 0..p { + T::Consideration::ensure_successful(&caller, i); // Proposals should be different so that different proposal hashes are generated - let proposal: T::Proposal = SystemCall::::remark { remark: id_to_remark_data(i, b as usize) }.into(); + let proposal: T::Proposal = + SystemCall::::remark { remark: id_to_remark_data(i, b as usize) }.into(); Collective::::propose( SystemOrigin::Signed(caller.clone()).into(), threshold, @@ -400,7 +486,8 @@ benchmarks_instance_pallet! { last_hash = T::Hashing::hash_of(&proposal); } - // Caller switches vote to nay on their own proposal, allowing them to be the deciding approval vote + // Caller switches vote to nay on their own proposal, allowing them to be the deciding + // approval vote Collective::::vote( SystemOrigin::Signed(caller.clone()).into(), last_hash, @@ -409,7 +496,7 @@ benchmarks_instance_pallet! { )?; // Have almost everyone vote nay on last proposal, while keeping it from failing. - for j in 2 .. m - 1 { + for j in 2..m - 1 { let voter = &members[j as usize]; let approve = false; Collective::::vote( @@ -436,27 +523,33 @@ benchmarks_instance_pallet! { Collective::::vote( SystemOrigin::Signed(caller.clone()).into(), last_hash, - index, approve, + index, + approve, )?; - }: close(SystemOrigin::Signed(caller), last_hash, index, Weight::MAX, bytes_in_storage) - verify { + #[extrinsic_call] + close(SystemOrigin::Signed(caller), last_hash, index, Weight::MAX, bytes_in_storage); + // The last proposal is removed. assert_eq!(Proposals::::get().len(), (p - 1) as usize); - assert_last_event::(Event::Executed { proposal_hash: last_hash, result: Ok(()) }.into()); + assert_last_event::( + Event::Executed { proposal_hash: last_hash, result: Ok(()) }.into(), + ); + Ok(()) } - close_disapproved { - // We choose 4 as a minimum so we always trigger a vote in the voting loop (`for j in ...`) - let m in 4 .. T::MaxMembers::get(); - let p in 1 .. T::MaxProposals::get(); - + // m: we choose 4 as a minimum so we always trigger a vote in the voting loop (`for j in ...`) + #[benchmark] + fn close_disapproved( + m: Linear<4, { T::MaxMembers::get() }>, + p: Linear<1, { T::MaxProposals::get() }>, + ) -> Result<(), BenchmarkError> { let bytes = 100; let bytes_in_storage = bytes + size_of::() as u32; // Construct `members`. let mut members = vec![]; - for i in 0 .. m - 1 { + for i in 0..m - 1 { let member = account::("member", i, SEED); members.push(member); } @@ -474,9 +567,11 @@ benchmarks_instance_pallet! { // Add proposals let mut last_hash = T::Hash::default(); - for i in 0 .. p { + for i in 0..p { + T::Consideration::ensure_successful(&caller, i); // Proposals should be different so that different proposal hashes are generated - let proposal: T::Proposal = SystemCall::::remark { remark: id_to_remark_data(i, bytes as usize) }.into(); + let proposal: T::Proposal = + SystemCall::::remark { remark: id_to_remark_data(i, bytes as usize) }.into(); Collective::::propose( SystemOrigin::Signed(caller.clone()).into(), threshold, @@ -490,7 +585,7 @@ benchmarks_instance_pallet! { // Have almost everyone vote aye on last proposal, while keeping it from passing. // A few abstainers will be the nay votes needed to fail the vote. let mut yes_votes: MemberCount = 0; - for j in 2 .. m - 1 { + for j in 2..m - 1 { let voter = &members[j as usize]; let approve = true; yes_votes += 1; @@ -499,7 +594,8 @@ benchmarks_instance_pallet! { Some(false), yes_votes, 0, - m,) { + m, + ) { break; } Collective::::vote( @@ -522,23 +618,26 @@ benchmarks_instance_pallet! { assert_eq!(Proposals::::get().len(), p as usize); // Prime nay will close it as disapproved - }: close(SystemOrigin::Signed(caller), last_hash, index, Weight::MAX, bytes_in_storage) - verify { + #[extrinsic_call] + close(SystemOrigin::Signed(caller), last_hash, index, Weight::MAX, bytes_in_storage); + assert_eq!(Proposals::::get().len(), (p - 1) as usize); assert_last_event::(Event::Disapproved { proposal_hash: last_hash }.into()); + Ok(()) } - close_approved { - let b in 2 .. MAX_BYTES; - // We choose 4 as a minimum so we always trigger a vote in the voting loop (`for j in ...`) - let m in 4 .. T::MaxMembers::get(); - let p in 1 .. T::MaxProposals::get(); - + // m: we choose 4 as a minimum so we always trigger a vote in the voting loop (`for j in ...`) + #[benchmark] + fn close_approved( + b: Linear<2, MAX_BYTES>, + m: Linear<4, { T::MaxMembers::get() }>, + p: Linear<1, { T::MaxProposals::get() }>, + ) -> Result<(), BenchmarkError> { let bytes_in_storage = b + size_of::() as u32; // Construct `members`. let mut members = vec![]; - for i in 0 .. m - 1 { + for i in 0..m - 1 { let member = account::("member", i, SEED); members.push(member); } @@ -556,9 +655,11 @@ benchmarks_instance_pallet! { // Add proposals let mut last_hash = T::Hash::default(); - for i in 0 .. p { + for i in 0..p { + T::Consideration::ensure_successful(&caller, i); // Proposals should be different so that different proposal hashes are generated - let proposal: T::Proposal = SystemCall::::remark { remark: id_to_remark_data(i, b as usize) }.into(); + let proposal: T::Proposal = + SystemCall::::remark { remark: id_to_remark_data(i, b as usize) }.into(); Collective::::propose( SystemOrigin::Signed(caller.clone()).into(), threshold, @@ -573,19 +674,19 @@ benchmarks_instance_pallet! { SystemOrigin::Signed(caller.clone()).into(), last_hash, p - 1, - true // Vote aye. + true, // Vote aye. )?; // Have almost everyone vote nay on last proposal, while keeping it from failing. // A few abstainers will be the aye votes needed to pass the vote. - for j in 2 .. m - 1 { + for j in 2..m - 1 { let voter = &members[j as usize]; let approve = false; Collective::::vote( SystemOrigin::Signed(voter.clone()).into(), last_hash, p - 1, - approve + approve, )?; } @@ -594,22 +695,25 @@ benchmarks_instance_pallet! { assert_eq!(Proposals::::get().len(), p as usize); // Prime aye will close it as approved - }: close(SystemOrigin::Signed(caller), last_hash, p - 1, Weight::MAX, bytes_in_storage) - verify { + #[extrinsic_call] + close(SystemOrigin::Signed(caller), last_hash, p - 1, Weight::MAX, bytes_in_storage); + assert_eq!(Proposals::::get().len(), (p - 1) as usize); - assert_last_event::(Event::Executed { proposal_hash: last_hash, result: Ok(()) }.into()); + assert_last_event::( + Event::Executed { proposal_hash: last_hash, result: Ok(()) }.into(), + ); + Ok(()) } - disapprove_proposal { - let p in 1 .. T::MaxProposals::get(); - + #[benchmark] + fn disapprove_proposal(p: Linear<1, { T::MaxProposals::get() }>) -> Result<(), BenchmarkError> { let m = 3; let b = MAX_BYTES; let bytes_in_storage = b + size_of::() as u32; // Construct `members`. let mut members = vec![]; - for i in 0 .. m - 1 { + for i in 0..m - 1 { let member = account::("member", i, SEED); members.push(member); } @@ -627,9 +731,11 @@ benchmarks_instance_pallet! { // Add proposals let mut last_hash = T::Hash::default(); - for i in 0 .. p { + for i in 0..p { + T::Consideration::ensure_successful(&caller, i); // Proposals should be different so that different proposal hashes are generated - let proposal: T::Proposal = SystemCall::::remark { remark: id_to_remark_data(i, b as usize) }.into(); + let proposal: T::Proposal = + SystemCall::::remark { remark: id_to_remark_data(i, b as usize) }.into(); Collective::::propose( SystemOrigin::Signed(caller.clone()).into(), threshold, @@ -642,11 +748,150 @@ benchmarks_instance_pallet! { System::::set_block_number(BlockNumberFor::::max_value()); assert_eq!(Proposals::::get().len(), p as usize); - }: _(SystemOrigin::Root, last_hash) - verify { + let origin = + T::DisapproveOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + + #[extrinsic_call] + _(origin as ::RuntimeOrigin, last_hash); + assert_eq!(Proposals::::get().len(), (p - 1) as usize); assert_last_event::(Event::Disapproved { proposal_hash: last_hash }.into()); + Ok(()) + } + + // d: `0` - if deposit is not present and `1` otherwise. + #[benchmark] + fn kill( + d: Linear<0, 1>, + p: Linear<1, { T::MaxProposals::get() }>, + ) -> Result<(), BenchmarkError> { + let m = 3; + let b = MAX_BYTES; + let bytes_in_storage = b + size_of::() as u32; + + // Construct `members`. + let mut members = vec![]; + for i in 0..m - 1 { + let member = account::("member", i, SEED); + members.push(member); + } + let caller = account::("caller", 0, SEED); + members.push(caller.clone()); + Collective::::set_members( + SystemOrigin::Root.into(), + members.clone(), + Some(caller.clone()), + T::MaxMembers::get(), + )?; + + // Threshold is one less than total members so that two nays will disapprove the vote + let threshold = m - 1; + + // Add proposals + let mut last_hash = T::Hash::default(); + for i in 0..p { + T::Consideration::ensure_successful(&caller, i); + + // Proposals should be different so that different proposal hashes are generated + let proposal: T::Proposal = + SystemCall::::remark { remark: id_to_remark_data(i, b as usize) }.into(); + Collective::::propose( + SystemOrigin::Signed(caller.clone()).into(), + threshold, + Box::new(proposal.clone()), + bytes_in_storage, + )?; + last_hash = T::Hashing::hash_of(&proposal); + } + + System::::set_block_number(BlockNumberFor::::max_value()); + assert_eq!(Proposals::::get().len(), p as usize); + + if d == 0 { + CostOf::::remove(last_hash); + } + let cost_present = CostOf::::get(last_hash).is_some(); + + let origin = + T::KillOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + + #[extrinsic_call] + _(origin as ::RuntimeOrigin, last_hash); + + assert_eq!(Proposals::::get().len(), (p - 1) as usize); + assert_last_event::(Event::Killed { proposal_hash: last_hash }.into()); + if cost_present { + assert_has_event::( + Event::ProposalCostBurned { proposal_hash: last_hash, who: caller }.into(), + ); + } + Ok(()) + } + + #[benchmark] + fn release_proposal_cost() -> Result<(), BenchmarkError> { + let m = 3; + let p = T::MaxProposals::get(); + let b = MAX_BYTES; + let bytes_in_storage = b + size_of::() as u32; + + // Construct `members`. + let mut members = vec![]; + for i in 0..m - 1 { + let member = account::("member", i, SEED); + members.push(member); + } + let caller = account::("caller", 0, SEED); + members.push(caller.clone()); + Collective::::set_members( + SystemOrigin::Root.into(), + members.clone(), + Some(caller.clone()), + T::MaxMembers::get(), + )?; + + // Add proposals + let threshold = 2; + let mut last_hash = T::Hash::default(); + for i in 0..p { + T::Consideration::ensure_successful(&caller, i); + + // Proposals should be different so that different proposal hashes are generated + let proposal: T::Proposal = + SystemCall::::remark { remark: id_to_remark_data(i, b as usize) }.into(); + Collective::::propose( + SystemOrigin::Signed(caller.clone()).into(), + threshold, + Box::new(proposal.clone()), + bytes_in_storage, + )?; + last_hash = T::Hashing::hash_of(&proposal); + } + + System::::set_block_number(BlockNumberFor::::max_value()); + assert_eq!(Proposals::::get().len(), p as usize); + + assert_eq!(Proposals::::get().len(), p as usize); + let _ = Collective::::remove_proposal(last_hash); + assert_eq!(Proposals::::get().len(), (p - 1) as usize); + + let cost_present = CostOf::::get(last_hash).is_some(); + + #[extrinsic_call] + _(SystemOrigin::Signed(caller.clone()), last_hash); + + assert_eq!(CostOf::::get(last_hash), None); + if cost_present { + assert_last_event::( + Event::ProposalCostReleased { proposal_hash: last_hash, who: caller }.into(), + ); + } + Ok(()) } - impl_benchmark_test_suite!(Collective, crate::tests::ExtBuilder::default().build(), crate::tests::Test); + impl_benchmark_test_suite!( + Collective, + crate::tests::ExtBuilder::default().build(), + crate::tests::Test + ); } diff --git a/substrate/frame/collective/src/lib.rs b/substrate/frame/collective/src/lib.rs index 3544a8cddb45cadf022a2808f3c199651d5e6798..79428689caaf7d2999754fe3786464fb97940fb9 100644 --- a/substrate/frame/collective/src/lib.rs +++ b/substrate/frame/collective/src/lib.rs @@ -59,8 +59,8 @@ use frame_support::{ }, ensure, impl_ensure_origin_with_arg_ignoring_arg, traits::{ - Backing, ChangeMembers, EnsureOrigin, EnsureOriginWithArg, Get, GetBacking, - InitializeMembers, StorageVersion, + Backing, ChangeMembers, Consideration, EnsureOrigin, EnsureOriginWithArg, Get, GetBacking, + InitializeMembers, MaybeConsideration, StorageVersion, }, weights::Weight, }; @@ -173,6 +173,143 @@ pub struct Votes { end: BlockNumber, } +/// Types implementing various cost strategies for a given proposal count. +/// +/// These types implement [Convert](sp_runtime::traits::Convert) trait and can be used with types +/// like [HoldConsideration](`frame_support::traits::fungible::HoldConsideration`) implementing +/// [Consideration](`frame_support::traits::Consideration`) trait. +/// +/// ### Example: +/// +/// 1. Linear increasing with helper types. +#[doc = docify::embed!("src/tests.rs", deposit_types_with_linear_work)] +/// +/// 2. Geometrically increasing with helper types. +#[doc = docify::embed!("src/tests.rs", deposit_types_with_geometric_work)] +/// +/// 3. Geometrically increasing with rounding. +#[doc = docify::embed!("src/tests.rs", deposit_round_with_geometric_work)] +pub mod deposit { + use core::marker::PhantomData; + use sp_core::Get; + use sp_runtime::{traits::Convert, FixedPointNumber, FixedU128, Saturating}; + + /// Constant deposit amount regardless of current proposal count. + /// Returns `None` if configured with zero deposit. + pub struct Constant(PhantomData); + impl Convert for Constant + where + Deposit: Get, + Balance: frame_support::traits::tokens::Balance, + { + fn convert(_: u32) -> Balance { + Deposit::get() + } + } + + /// Linear increasing with some offset. + /// f(x) = ax + b, a = `Slope`, x = `proposal_count`, b = `Offset`. + pub struct Linear(PhantomData<(Slope, Offset)>); + impl Convert for Linear + where + Slope: Get, + Offset: Get, + Balance: frame_support::traits::tokens::Balance, + { + fn convert(proposal_count: u32) -> Balance { + let base: Balance = Slope::get().saturating_mul(proposal_count).into(); + Offset::get().saturating_add(base) + } + } + + /// Geometrically increasing. + /// f(x) = a * r^x, a = `Base`, x = `proposal_count`, r = `Ratio`. + pub struct Geometric(PhantomData<(Ratio, Base)>); + impl Convert for Geometric + where + Ratio: Get, + Base: Get, + Balance: frame_support::traits::tokens::Balance, + { + fn convert(proposal_count: u32) -> Balance { + Ratio::get() + .saturating_pow(proposal_count as usize) + .saturating_mul_int(Base::get()) + } + } + + /// Rounds a `Deposit` result with `Precision`. + /// Particularly useful for types like [`Geometric`] that might produce deposits with high + /// precision. + pub struct Round(PhantomData<(Precision, Deposit)>); + impl Convert for Round + where + Precision: Get, + Deposit: Convert, + Balance: frame_support::traits::tokens::Balance, + { + fn convert(proposal_count: u32) -> Balance { + let deposit = Deposit::convert(proposal_count); + if !deposit.is_zero() { + let factor: Balance = + Balance::from(10u32).saturating_pow(Precision::get() as usize); + if factor > deposit { + deposit + } else { + (deposit / factor) * factor + } + } else { + deposit + } + } + } + + /// Defines `Period` for supplied `Step` implementing [`Convert`] trait. + pub struct Stepped(PhantomData<(Period, Step)>); + impl Convert for Stepped + where + Period: Get, + Step: Convert, + Balance: frame_support::traits::tokens::Balance, + { + fn convert(proposal_count: u32) -> Balance { + let step_num = proposal_count / Period::get(); + Step::convert(step_num) + } + } + + /// Defines `Delay` for supplied `Step` implementing [`Convert`] trait. + pub struct Delayed(PhantomData<(Delay, Deposit)>); + impl Convert for Delayed + where + Delay: Get, + Deposit: Convert, + Balance: frame_support::traits::tokens::Balance, + { + fn convert(proposal_count: u32) -> Balance { + let delay = Delay::get(); + if delay > proposal_count { + return Balance::zero(); + } + let pos = proposal_count.saturating_sub(delay); + Deposit::convert(pos) + } + } + + /// Defines `Ceil` for supplied `Step` implementing [`Convert`] trait. + pub struct WithCeil(PhantomData<(Ceil, Deposit)>); + impl Convert for WithCeil + where + Ceil: Get, + Deposit: Convert, + Balance: frame_support::traits::tokens::Balance, + { + fn convert(proposal_count: u32) -> Balance { + Deposit::convert(proposal_count).min(Ceil::get()) + } + } +} + #[frame_support::pallet] pub mod pallet { use super::*; @@ -229,6 +366,27 @@ pub mod pallet { /// The maximum weight of a dispatch call that can be proposed and executed. #[pallet::constant] type MaxProposalWeight: Get; + + /// Origin from which a proposal in any status may be disapproved without associated cost + /// for a proposer. + type DisapproveOrigin: EnsureOrigin<::RuntimeOrigin>; + + /// Origin from which any malicious proposal may be killed with associated cost for a + /// proposer. + /// + /// The associated cost is set by [`Config::Consideration`] and can be none. + type KillOrigin: EnsureOrigin<::RuntimeOrigin>; + + /// Mechanism to assess the necessity of some cost for publishing and storing a proposal. + /// + /// The footprint is defined as `proposal_count`, which reflects the total number of active + /// proposals in the system, excluding the one currently being proposed. The cost may vary + /// based on this count. + /// + /// Note: If the resulting deposits are excessively high and cause benchmark failures, + /// consider using a constant cost (e.g., [`crate::deposit::Constant`]) equal to the minimum + /// balance under the `runtime-benchmarks` feature. + type Consideration: MaybeConsideration; } #[pallet::genesis_config] @@ -272,6 +430,14 @@ pub mod pallet { pub type ProposalOf, I: 'static = ()> = StorageMap<_, Identity, T::Hash, >::Proposal, OptionQuery>; + /// Consideration cost created for publishing and storing a proposal. + /// + /// Determined by [Config::Consideration] and may be not present for certain proposals (e.g. if + /// the proposal count at the time of creation was below threshold N). + #[pallet::storage] + pub type CostOf, I: 'static = ()> = + StorageMap<_, Identity, T::Hash, (T::AccountId, T::Consideration), OptionQuery>; + /// Votes on a given proposal, if it is ongoing. #[pallet::storage] pub type Voting, I: 'static = ()> = @@ -320,6 +486,12 @@ pub mod pallet { MemberExecuted { proposal_hash: T::Hash, result: DispatchResult }, /// A proposal was closed because its threshold was reached or after its duration was up. Closed { proposal_hash: T::Hash, yes: MemberCount, no: MemberCount }, + /// A proposal was killed. + Killed { proposal_hash: T::Hash }, + /// Some cost for storing a proposal was burned. + ProposalCostBurned { proposal_hash: T::Hash, who: T::AccountId }, + /// Some cost for storing a proposal was released. + ProposalCostReleased { proposal_hash: T::Hash, who: T::AccountId }, } #[pallet::error] @@ -346,6 +518,8 @@ pub mod pallet { WrongProposalLength, /// Prime account is not a member PrimeAccountNotMember, + /// Proposal is still active. + ProposalActive, } #[pallet::hooks] @@ -356,6 +530,14 @@ pub mod pallet { } } + /// A reason for the pallet placing a hold on funds. + #[pallet::composite_enum] + pub enum HoldReason { + /// Funds are held for submitting and storing a proposal. + #[codec(index = 0)] + ProposalSubmission, + } + // Note that councillor operations are assigned to the operational class. #[pallet::call] impl, I: 'static> Pallet { @@ -593,7 +775,7 @@ pub mod pallet { origin: OriginFor, proposal_hash: T::Hash, ) -> DispatchResultWithPostInfo { - ensure_root(origin)?; + T::DisapproveOrigin::ensure_origin(origin)?; let proposal_count = Self::do_disapprove_proposal(proposal_hash); Ok(Some(T::WeightInfo::disapprove_proposal(proposal_count)).into()) } @@ -648,6 +830,63 @@ pub mod pallet { Self::do_close(proposal_hash, index, proposal_weight_bound, length_bound) } + + /// Disapprove the proposal and burn the cost held for storing this proposal. + /// + /// Parameters: + /// - `origin`: must be the `KillOrigin`. + /// - `proposal_hash`: The hash of the proposal that should be killed. + /// + /// Emits `Killed` and `ProposalCostBurned` if any cost was held for a given proposal. + #[pallet::call_index(7)] + #[pallet::weight(T::WeightInfo::kill(1, T::MaxProposals::get()))] + pub fn kill(origin: OriginFor, proposal_hash: T::Hash) -> DispatchResultWithPostInfo { + T::KillOrigin::ensure_origin(origin)?; + ensure!( + ProposalOf::::get(&proposal_hash).is_some(), + Error::::ProposalMissing + ); + let burned = if let Some((who, cost)) = >::take(proposal_hash) { + cost.burn(&who); + Self::deposit_event(Event::ProposalCostBurned { proposal_hash, who }); + true + } else { + false + }; + let proposal_count = Self::remove_proposal(proposal_hash); + + Self::deposit_event(Event::Killed { proposal_hash }); + + Ok(Some(T::WeightInfo::kill(burned as u32, proposal_count)).into()) + } + + /// Release the cost held for storing a proposal once the given proposal is completed. + /// + /// If there is no associated cost for the given proposal, this call will have no effect. + /// + /// Parameters: + /// - `origin`: must be `Signed` or `Root`. + /// - `proposal_hash`: The hash of the proposal. + /// + /// Emits `ProposalCostReleased` if any cost held for a given proposal. + #[pallet::call_index(8)] + #[pallet::weight(T::WeightInfo::release_proposal_cost())] + pub fn release_proposal_cost( + origin: OriginFor, + proposal_hash: T::Hash, + ) -> DispatchResult { + let _ = ensure_signed_or_root(origin)?; + ensure!( + ProposalOf::::get(&proposal_hash).is_none(), + Error::::ProposalActive + ); + if let Some((who, cost)) = >::take(proposal_hash) { + let _ = cost.drop(&who)?; + Self::deposit_event(Event::ProposalCostReleased { proposal_hash, who }); + } + + Ok(()) + } } } @@ -718,7 +957,13 @@ impl, I: 'static> Pallet { Ok(proposals.len()) })?; + let cost = T::Consideration::new(&who, active_proposals as u32 - 1)?; + if !cost.is_none() { + >::insert(proposal_hash, (who.clone(), cost)); + } + let index = ProposalCount::::get(); + >::mutate(|i| *i += 1); >::insert(proposal_hash, proposal); let votes = { diff --git a/substrate/frame/collective/src/tests.rs b/substrate/frame/collective/src/tests.rs index 5240dc215ff7b19b774dc32e2a33092e137d6d28..70ce221f10d0e5ab5bbe63b73835d2542209251a 100644 --- a/substrate/frame/collective/src/tests.rs +++ b/substrate/frame/collective/src/tests.rs @@ -21,12 +21,19 @@ use frame_support::{ assert_noop, assert_ok, derive_impl, dispatch::Pays, parameter_types, - traits::{ConstU32, ConstU64, StorageVersion}, + traits::{ + fungible::{HoldConsideration, Inspect, Mutate}, + ConstU32, ConstU64, StorageVersion, + }, Hashable, }; use frame_system::{EnsureRoot, EventRecord, Phase}; -use sp_core::H256; -use sp_runtime::{testing::Header, traits::BlakeTwo256, BuildStorage}; +use sp_core::{ConstU128, H256}; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, Convert, Zero}, + BuildStorage, FixedU128, +}; pub type Block = sp_runtime::generic::Block; pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic; @@ -35,6 +42,7 @@ frame_support::construct_runtime!( pub enum Test { System: frame_system, + Balances: pallet_balances, Collective: pallet_collective::, CollectiveMajority: pallet_collective::, DefaultCollective: pallet_collective, @@ -90,7 +98,26 @@ parameter_types! { #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; + type AccountData = pallet_balances::AccountData; +} + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)] +impl pallet_balances::Config for Test { + type ReserveIdentifier = [u8; 8]; + type AccountStore = System; + type RuntimeHoldReason = RuntimeHoldReason; } + +parameter_types! { + pub ProposalDepositBase: u64 = Balances::minimum_balance() + Balances::minimum_balance(); + pub const ProposalDepositDelay: u32 = 2; + pub const ProposalHoldReason: RuntimeHoldReason = + RuntimeHoldReason::Collective(pallet_collective::HoldReason::ProposalSubmission); +} + +type CollectiveDeposit = + deposit::Delayed>; + impl Config for Test { type RuntimeOrigin = RuntimeOrigin; type Proposal = RuntimeCall; @@ -102,7 +129,14 @@ impl Config for Test { type WeightInfo = (); type SetMembersOrigin = EnsureRoot; type MaxProposalWeight = MaxProposalWeight; + type DisapproveOrigin = EnsureRoot; + type KillOrigin = EnsureRoot; + type Consideration = + HoldConsideration; } + +type CollectiveMajorityDeposit = deposit::Linear, ProposalDepositBase>; + impl Config for Test { type RuntimeOrigin = RuntimeOrigin; type Proposal = RuntimeCall; @@ -114,11 +148,22 @@ impl Config for Test { type WeightInfo = (); type SetMembersOrigin = EnsureRoot; type MaxProposalWeight = MaxProposalWeight; + // type ProposalDeposit = CollectiveMajorityDeposit; + type DisapproveOrigin = EnsureRoot; + type KillOrigin = EnsureRoot; + type Consideration = + HoldConsideration; } impl mock_democracy::Config for Test { type RuntimeEvent = RuntimeEvent; type ExternalMajorityOrigin = EnsureProportionAtLeast; } +parameter_types! { + pub const Ratio2: FixedU128 = FixedU128::from_u32(2); + pub ProposalDepositCeil: u64 = Balances::minimum_balance() * 100; +} +type DefaultCollectiveDeposit = + deposit::WithCeil>; impl Config for Test { type RuntimeOrigin = RuntimeOrigin; type Proposal = RuntimeCall; @@ -130,6 +175,12 @@ impl Config for Test { type WeightInfo = (); type SetMembersOrigin = EnsureRoot; type MaxProposalWeight = MaxProposalWeight; + // type ProposalDeposit = + // deposit::WithCeil>; + type DisapproveOrigin = EnsureRoot; + type KillOrigin = EnsureRoot; + type Consideration = + HoldConsideration; } pub struct ExtBuilder { @@ -151,6 +202,8 @@ impl ExtBuilder { pub fn build(self) -> sp_io::TestExternalities { let mut ext: sp_io::TestExternalities = RuntimeGenesisConfig { system: frame_system::GenesisConfig::default(), + // balances: pallet_balances::GenesisConfig::default(), + balances: pallet_balances::GenesisConfig { balances: vec![(1, 100), (2, 200)] }, collective: pallet_collective::GenesisConfig { members: self.collective_members, phantom: Default::default(), @@ -550,16 +603,28 @@ fn close_with_no_prime_but_majority_works() { MaxMembers::get() )); + let deposit = >::convert(0); + let ed = Balances::minimum_balance(); + let _ = Balances::mint_into(&5, ed + deposit); + System::reset_events(); + assert_ok!(CollectiveMajority::propose( - RuntimeOrigin::signed(1), + RuntimeOrigin::signed(5), 5, Box::new(proposal.clone()), proposal_len )); + assert_eq!(Balances::balance(&5), ed); + assert_ok!(CollectiveMajority::vote(RuntimeOrigin::signed(1), hash, 0, true)); assert_ok!(CollectiveMajority::vote(RuntimeOrigin::signed(2), hash, 0, true)); assert_ok!(CollectiveMajority::vote(RuntimeOrigin::signed(3), hash, 0, true)); + assert_noop!( + CollectiveMajority::release_proposal_cost(RuntimeOrigin::signed(1), hash), + Error::::ProposalActive + ); + System::set_block_number(4); assert_ok!(CollectiveMajority::close( RuntimeOrigin::signed(4), @@ -569,11 +634,14 @@ fn close_with_no_prime_but_majority_works() { proposal_len )); + assert_ok!(CollectiveMajority::release_proposal_cost(RuntimeOrigin::signed(5), hash)); + assert_eq!(Balances::balance(&5), ed + deposit); + assert_eq!( System::events(), vec![ record(RuntimeEvent::CollectiveMajority(CollectiveEvent::Proposed { - account: 1, + account: 5, proposal_index: 0, proposal_hash: hash, threshold: 5 @@ -610,6 +678,10 @@ fn close_with_no_prime_but_majority_works() { record(RuntimeEvent::CollectiveMajority(CollectiveEvent::Executed { proposal_hash: hash, result: Err(DispatchError::BadOrigin) + })), + record(RuntimeEvent::CollectiveMajority(CollectiveEvent::ProposalCostReleased { + proposal_hash: hash, + who: 5, })) ] ); @@ -757,9 +829,13 @@ fn propose_works() { #[test] fn limit_active_proposals() { ExtBuilder::default().build_and_execute(|| { + let ed = Balances::minimum_balance(); + assert_ok!(Balances::mint_into(&1, ed)); for i in 0..MaxProposals::get() { let proposal = make_proposal(i as u64); let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); + let deposit = >::convert(0); + assert_ok!(Balances::mint_into(&1, deposit)); assert_ok!(Collective::propose( RuntimeOrigin::signed(1), 3, @@ -1499,3 +1575,183 @@ fn migration_v4() { crate::migrations::v4::post_migrate::(old_pallet); }); } + +#[test] +fn kill_proposal_with_deposit() { + ExtBuilder::default().build_and_execute(|| { + let ed = Balances::minimum_balance(); + assert_ok!(Balances::mint_into(&1, ed)); + let mut last_deposit = None; + let mut last_hash = None; + for i in 0..=ProposalDepositDelay::get() { + let proposal = make_proposal(i as u64); + let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); + last_hash = Some(BlakeTwo256::hash_of(&proposal)); + let deposit = >::convert(i); + assert_ok!(Balances::mint_into(&1, deposit)); + last_deposit = Some(deposit); + + assert_ok!(Collective::propose( + RuntimeOrigin::signed(1), + 3, + Box::new(proposal.clone()), + proposal_len + )); + assert_eq!( + CostOf::::get(last_hash.unwrap()).is_none(), + deposit.is_zero() + ); + } + let balance = Balances::total_balance(&1); + System::reset_events(); + + let unpublished = make_proposal((ProposalDepositDelay::get() + 1).into()); + assert_noop!( + Collective::kill(RuntimeOrigin::root(), BlakeTwo256::hash_of(&unpublished)), + Error::::ProposalMissing + ); + + assert_ok!(Collective::kill(RuntimeOrigin::root(), last_hash.unwrap())); + assert_eq!(Balances::total_balance(&1), balance - last_deposit.unwrap()); + + assert_eq!( + System::events(), + vec![ + record(RuntimeEvent::Collective(CollectiveEvent::ProposalCostBurned { + proposal_hash: last_hash.unwrap(), + who: 1, + })), + record(RuntimeEvent::Collective(CollectiveEvent::Killed { + proposal_hash: last_hash.unwrap(), + })), + ] + ); + }) +} + +#[docify::export] +#[test] +fn deposit_types_with_linear_work() { + type LinearWithSlop2 = crate::deposit::Linear, ConstU128<10>>; + assert_eq!(>::convert(0), 10); + assert_eq!(>::convert(1), 12); + assert_eq!(>::convert(2), 14); + assert_eq!(>::convert(3), 16); + assert_eq!(>::convert(4), 18); + + type SteppedWithStep3 = crate::deposit::Stepped, LinearWithSlop2>; + assert_eq!(>::convert(0), 10); + assert_eq!(>::convert(1), 10); + assert_eq!(>::convert(2), 10); + assert_eq!(>::convert(3), 12); + assert_eq!(>::convert(4), 12); + assert_eq!(>::convert(5), 12); + assert_eq!(>::convert(6), 14); + + type DelayedWithDelay4 = crate::deposit::Delayed, SteppedWithStep3>; + assert_eq!(>::convert(0), 0); + assert_eq!(>::convert(3), 0); + assert_eq!(>::convert(4), 10); + assert_eq!(>::convert(5), 10); + assert_eq!(>::convert(6), 10); + assert_eq!(>::convert(7), 12); + assert_eq!(>::convert(9), 12); + assert_eq!(>::convert(10), 14); + assert_eq!(>::convert(13), 16); + + type WithCeil13 = crate::deposit::WithCeil, DelayedWithDelay4>; + assert_eq!(>::convert(0), 0); + assert_eq!(>::convert(4), 10); + assert_eq!(>::convert(9), 12); + assert_eq!(>::convert(10), 13); + assert_eq!(>::convert(11), 13); + assert_eq!(>::convert(13), 13); +} + +#[docify::export] +#[test] +fn deposit_types_with_geometric_work() { + parameter_types! { + pub const Ratio2: FixedU128 = FixedU128::from_u32(2); + } + type WithRatio2Base10 = crate::deposit::Geometric>; + assert_eq!(>::convert(0), 10); + assert_eq!(>::convert(1), 20); + assert_eq!(>::convert(2), 40); + assert_eq!(>::convert(3), 80); + assert_eq!(>::convert(4), 160); + assert_eq!(>::convert(5), 320); + assert_eq!(>::convert(6), 640); + assert_eq!(>::convert(7), 1280); + assert_eq!(>::convert(8), 2560); + assert_eq!(>::convert(9), 5120); + + type SteppedWithStep3 = crate::deposit::Stepped, WithRatio2Base10>; + assert_eq!(>::convert(0), 10); + assert_eq!(>::convert(1), 10); + assert_eq!(>::convert(2), 10); + assert_eq!(>::convert(3), 20); + assert_eq!(>::convert(4), 20); + assert_eq!(>::convert(5), 20); + assert_eq!(>::convert(6), 40); + + type DelayedWithDelay4 = crate::deposit::Delayed, SteppedWithStep3>; + assert_eq!(>::convert(0), 0); + assert_eq!(>::convert(3), 0); + assert_eq!(>::convert(4), 10); + assert_eq!(>::convert(5), 10); + assert_eq!(>::convert(6), 10); + assert_eq!(>::convert(7), 20); + assert_eq!(>::convert(9), 20); + assert_eq!(>::convert(10), 40); + assert_eq!(>::convert(13), 80); + + type WithCeil21 = crate::deposit::WithCeil, DelayedWithDelay4>; + assert_eq!(>::convert(0), 0); + assert_eq!(>::convert(4), 10); + assert_eq!(>::convert(9), 20); + assert_eq!(>::convert(10), 21); + assert_eq!(>::convert(11), 21); + assert_eq!(>::convert(13), 21); +} + +#[docify::export] +#[test] +fn deposit_round_with_geometric_work() { + parameter_types! { + pub const Ratio1_5: FixedU128 = FixedU128::from_rational(3, 2); + } + type WithRatio1_5Base10 = crate::deposit::Geometric>; + assert_eq!(>::convert(0), 10000); + assert_eq!(>::convert(1), 15000); + assert_eq!(>::convert(2), 22500); + assert_eq!(>::convert(3), 33750); + assert_eq!(>::convert(4), 50625); + assert_eq!(>::convert(5), 75937); + + type RoundWithPrecision3 = crate::deposit::Round, WithRatio1_5Base10>; + assert_eq!(>::convert(0), 10000); + assert_eq!(>::convert(1), 15000); + assert_eq!(>::convert(2), 22000); + assert_eq!(>::convert(3), 33000); + assert_eq!(>::convert(4), 50000); + assert_eq!(>::convert(5), 75000); +} + +#[test] +fn constant_deposit_work() { + type Constant0 = crate::deposit::Constant>; + assert_eq!(>::convert(0), 0); + assert_eq!(>::convert(1), 0); + assert_eq!(>::convert(2), 0); + + type Constant1 = crate::deposit::Constant>; + assert_eq!(>::convert(0), 1); + assert_eq!(>::convert(1), 1); + assert_eq!(>::convert(2), 1); + + type Constant12 = crate::deposit::Constant>; + assert_eq!(>::convert(0), 12); + assert_eq!(>::convert(1), 12); + assert_eq!(>::convert(2), 12); +} diff --git a/substrate/frame/collective/src/weights.rs b/substrate/frame/collective/src/weights.rs index cadfbfcbfbfbf0ef9fa3d9922df6a067791b1368..1a7485b4ab7bf4281d88a1a05c43b948949fe09b 100644 --- a/substrate/frame/collective/src/weights.rs +++ b/substrate/frame/collective/src/weights.rs @@ -18,27 +18,25 @@ //! Autogenerated weights for `pallet_collective` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-04-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-09-02, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-anb7yjbi-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-svzsllib-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` // Executed Command: -// ./target/production/substrate-node +// target/production/substrate-node // benchmark // pallet -// --chain=dev // --steps=50 // --repeat=20 -// --pallet=pallet_collective -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --wasm-execution=compiled // --heap-pages=4096 -// --output=./substrate/frame/collective/src/weights.rs +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_collective +// --chain=dev // --header=./substrate/HEADER-APACHE2 +// --output=./substrate/frame/collective/src/weights.rs // --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -61,6 +59,8 @@ pub trait WeightInfo { fn close_disapproved(m: u32, p: u32, ) -> Weight; fn close_approved(b: u32, m: u32, p: u32, ) -> Weight; fn disapprove_proposal(p: u32, ) -> Weight; + fn kill(d: u32, p: u32, ) -> Weight; + fn release_proposal_cost() -> Weight; } /// Weights for `pallet_collective` using the Substrate node and recommended hardware. @@ -80,13 +80,13 @@ impl WeightInfo for SubstrateWeight { fn set_members(m: u32, _n: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + m * (3232 ยฑ0) + p * (3190 ยฑ0)` - // Estimated: `15894 + m * (1967 ยฑ24) + p * (4332 ยฑ24)` - // Minimum execution time: 15_780_000 picoseconds. - Weight::from_parts(16_193_000, 15894) - // Standard Error: 56_777 - .saturating_add(Weight::from_parts(4_269_920, 0).saturating_mul(m.into())) - // Standard Error: 56_777 - .saturating_add(Weight::from_parts(8_046_697, 0).saturating_mul(p.into())) + // Estimated: `15894 + m * (1967 ยฑ23) + p * (4332 ยฑ23)` + // Minimum execution time: 16_699_000 picoseconds. + Weight::from_parts(17_015_000, 15894) + // Standard Error: 63_844 + .saturating_add(Weight::from_parts(4_593_256, 0).saturating_mul(m.into())) + // Standard Error: 63_844 + .saturating_add(Weight::from_parts(8_935_845, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(p.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) @@ -106,12 +106,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `380 + m * (32 ยฑ0)` // Estimated: `3997 + m * (32 ยฑ0)` - // Minimum execution time: 19_760_000 picoseconds. - Weight::from_parts(18_849_113, 3997) - // Standard Error: 48 - .saturating_add(Weight::from_parts(2_076, 0).saturating_mul(b.into())) - // Standard Error: 501 - .saturating_add(Weight::from_parts(17_171, 0).saturating_mul(m.into())) + // Minimum execution time: 22_010_000 picoseconds. + Weight::from_parts(21_392_812, 3997) + // Standard Error: 34 + .saturating_add(Weight::from_parts(1_533, 0).saturating_mul(b.into())) + // Standard Error: 354 + .saturating_add(Weight::from_parts(15_866, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) } @@ -129,12 +129,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `380 + m * (32 ยฑ0)` // Estimated: `3997 + m * (32 ยฑ0)` - // Minimum execution time: 21_535_000 picoseconds. - Weight::from_parts(20_564_012, 3997) - // Standard Error: 66 - .saturating_add(Weight::from_parts(2_252, 0).saturating_mul(b.into())) - // Standard Error: 689 - .saturating_add(Weight::from_parts(33_509, 0).saturating_mul(m.into())) + // Minimum execution time: 24_250_000 picoseconds. + Weight::from_parts(23_545_893, 3997) + // Standard Error: 40 + .saturating_add(Weight::from_parts(1_646, 0).saturating_mul(b.into())) + // Standard Error: 421 + .saturating_add(Weight::from_parts(26_248, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) } @@ -144,27 +144,31 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Council::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Council::Proposals` (r:1 w:1) /// Proof: `Council::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `MaxEncodedLen`) /// Storage: `Council::ProposalCount` (r:1 w:1) /// Proof: `Council::ProposalCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Council::Voting` (r:0 w:1) /// Proof: `Council::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Council::CostOf` (r:0 w:1) + /// Proof: `Council::CostOf` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `b` is `[2, 1024]`. /// The range of component `m` is `[2, 100]`. /// The range of component `p` is `[1, 100]`. fn propose_proposed(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `525 + m * (32 ยฑ0) + p * (36 ยฑ0)` - // Estimated: `3917 + m * (33 ยฑ0) + p * (36 ยฑ0)` - // Minimum execution time: 22_759_000 picoseconds. - Weight::from_parts(21_344_363, 3917) - // Standard Error: 121 - .saturating_add(Weight::from_parts(3_231, 0).saturating_mul(b.into())) - // Standard Error: 1_265 - .saturating_add(Weight::from_parts(31_197, 0).saturating_mul(m.into())) - // Standard Error: 1_249 - .saturating_add(Weight::from_parts(200_231, 0).saturating_mul(p.into())) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(4_u64)) + // Measured: `618 + m * (32 ยฑ0) + p * (36 ยฑ0)` + // Estimated: `3991 + m * (33 ยฑ0) + p * (36 ยฑ0)` + // Minimum execution time: 46_538_000 picoseconds. + Weight::from_parts(63_900_448, 3991) + // Standard Error: 350 + .saturating_add(Weight::from_parts(2_827, 0).saturating_mul(b.into())) + // Standard Error: 3_658 + .saturating_add(Weight::from_parts(53_340, 0).saturating_mul(m.into())) + // Standard Error: 3_611 + .saturating_add(Weight::from_parts(213_719, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) .saturating_add(Weight::from_parts(0, 33).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 36).saturating_mul(p.into())) } @@ -175,12 +179,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `m` is `[5, 100]`. fn vote(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `974 + m * (64 ยฑ0)` - // Estimated: `4438 + m * (64 ยฑ0)` - // Minimum execution time: 22_935_000 picoseconds. - Weight::from_parts(23_694_302, 4438) - // Standard Error: 959 - .saturating_add(Weight::from_parts(57_721, 0).saturating_mul(m.into())) + // Measured: `1011 + m * (64 ยฑ0)` + // Estimated: `4475 + m * (64 ยฑ0)` + // Minimum execution time: 28_413_000 picoseconds. + Weight::from_parts(28_981_832, 4475) + // Standard Error: 665 + .saturating_add(Weight::from_parts(43_005, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) @@ -197,14 +201,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `p` is `[1, 100]`. fn close_early_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `563 + m * (64 ยฑ0) + p * (36 ยฑ0)` - // Estimated: `4008 + m * (65 ยฑ0) + p * (36 ยฑ0)` - // Minimum execution time: 25_507_000 picoseconds. - Weight::from_parts(24_849_399, 4008) - // Standard Error: 1_089 - .saturating_add(Weight::from_parts(40_933, 0).saturating_mul(m.into())) - // Standard Error: 1_062 - .saturating_add(Weight::from_parts(198_325, 0).saturating_mul(p.into())) + // Measured: `600 + m * (64 ยฑ0) + p * (36 ยฑ0)` + // Estimated: `4042 + m * (65 ยฑ0) + p * (36 ยฑ0)` + // Minimum execution time: 27_725_000 picoseconds. + Weight::from_parts(30_174_093, 4042) + // Standard Error: 1_458 + .saturating_add(Weight::from_parts(41_100, 0).saturating_mul(m.into())) + // Standard Error: 1_422 + .saturating_add(Weight::from_parts(177_303, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 65).saturating_mul(m.into())) @@ -227,16 +231,16 @@ impl WeightInfo for SubstrateWeight { /// The range of component `p` is `[1, 100]`. fn close_early_approved(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1010 + b * (1 ยฑ0) + m * (64 ยฑ0) + p * (40 ยฑ0)` - // Estimated: `4327 + b * (1 ยฑ0) + m * (66 ยฑ0) + p * (40 ยฑ0)` - // Minimum execution time: 43_927_000 picoseconds. - Weight::from_parts(43_733_720, 4327) - // Standard Error: 157 - .saturating_add(Weight::from_parts(3_737, 0).saturating_mul(b.into())) - // Standard Error: 1_664 - .saturating_add(Weight::from_parts(31_334, 0).saturating_mul(m.into())) - // Standard Error: 1_622 - .saturating_add(Weight::from_parts(222_964, 0).saturating_mul(p.into())) + // Measured: `1047 + b * (1 ยฑ0) + m * (64 ยฑ0) + p * (40 ยฑ0)` + // Estimated: `4360 + b * (1 ยฑ0) + m * (66 ยฑ0) + p * (40 ยฑ0)` + // Minimum execution time: 48_882_000 picoseconds. + Weight::from_parts(51_938_773, 4360) + // Standard Error: 208 + .saturating_add(Weight::from_parts(3_559, 0).saturating_mul(b.into())) + // Standard Error: 2_201 + .saturating_add(Weight::from_parts(38_678, 0).saturating_mul(m.into())) + // Standard Error: 2_145 + .saturating_add(Weight::from_parts(214_061, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(b.into())) @@ -257,14 +261,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `p` is `[1, 100]`. fn close_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `583 + m * (64 ยฑ0) + p * (36 ยฑ0)` - // Estimated: `4028 + m * (65 ยฑ0) + p * (36 ยฑ0)` - // Minimum execution time: 28_129_000 picoseconds. - Weight::from_parts(26_799_151, 4028) - // Standard Error: 1_325 - .saturating_add(Weight::from_parts(41_545, 0).saturating_mul(m.into())) - // Standard Error: 1_292 - .saturating_add(Weight::from_parts(224_499, 0).saturating_mul(p.into())) + // Measured: `620 + m * (64 ยฑ0) + p * (36 ยฑ0)` + // Estimated: `4062 + m * (65 ยฑ0) + p * (36 ยฑ0)` + // Minimum execution time: 30_613_000 picoseconds. + Weight::from_parts(36_174_190, 4062) + // Standard Error: 1_899 + .saturating_add(Weight::from_parts(46_781, 0).saturating_mul(m.into())) + // Standard Error: 1_851 + .saturating_add(Weight::from_parts(185_875, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 65).saturating_mul(m.into())) @@ -289,16 +293,16 @@ impl WeightInfo for SubstrateWeight { /// The range of component `p` is `[1, 100]`. fn close_approved(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1030 + b * (1 ยฑ0) + m * (64 ยฑ0) + p * (40 ยฑ0)` - // Estimated: `4347 + b * (1 ยฑ0) + m * (66 ยฑ0) + p * (40 ยฑ0)` - // Minimum execution time: 45_700_000 picoseconds. - Weight::from_parts(45_661_144, 4347) - // Standard Error: 149 - .saturating_add(Weight::from_parts(3_068, 0).saturating_mul(b.into())) - // Standard Error: 1_583 - .saturating_add(Weight::from_parts(36_426, 0).saturating_mul(m.into())) - // Standard Error: 1_543 - .saturating_add(Weight::from_parts(229_986, 0).saturating_mul(p.into())) + // Measured: `1067 + b * (1 ยฑ0) + m * (64 ยฑ0) + p * (40 ยฑ0)` + // Estimated: `4380 + b * (1 ยฑ0) + m * (66 ยฑ0) + p * (40 ยฑ0)` + // Minimum execution time: 51_253_000 picoseconds. + Weight::from_parts(56_399_941, 4380) + // Standard Error: 218 + .saturating_add(Weight::from_parts(2_920, 0).saturating_mul(b.into())) + // Standard Error: 2_310 + .saturating_add(Weight::from_parts(30_473, 0).saturating_mul(m.into())) + // Standard Error: 2_252 + .saturating_add(Weight::from_parts(208_468, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(b.into())) @@ -316,14 +320,62 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `392 + p * (32 ยฑ0)` // Estimated: `1877 + p * (32 ยฑ0)` - // Minimum execution time: 13_163_000 picoseconds. - Weight::from_parts(14_467_031, 1877) - // Standard Error: 1_073 - .saturating_add(Weight::from_parts(190_879, 0).saturating_mul(p.into())) + // Minimum execution time: 14_646_000 picoseconds. + Weight::from_parts(17_305_497, 1877) + // Standard Error: 1_331 + .saturating_add(Weight::from_parts(156_038, 0).saturating_mul(p.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(p.into())) } + /// Storage: `Council::ProposalOf` (r:1 w:1) + /// Proof: `Council::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Council::CostOf` (r:1 w:1) + /// Proof: `Council::CostOf` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `MaxEncodedLen`) + /// Storage: `Council::Proposals` (r:1 w:1) + /// Proof: `Council::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::Voting` (r:0 w:1) + /// Proof: `Council::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `d` is `[0, 1]`. + /// The range of component `p` is `[1, 100]`. + fn kill(d: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1863 + d * (212 ยฑ0) + p * (41 ยฑ0)` + // Estimated: `5172 + d * (1901 ยฑ14) + p * (43 ยฑ0)` + // Minimum execution time: 22_164_000 picoseconds. + Weight::from_parts(24_932_256, 5172) + // Standard Error: 404_014 + .saturating_add(Weight::from_parts(33_833_807, 0).saturating_mul(d.into())) + // Standard Error: 6_256 + .saturating_add(Weight::from_parts(281_910, 0).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(d.into()))) + .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(d.into()))) + .saturating_add(Weight::from_parts(0, 1901).saturating_mul(d.into())) + .saturating_add(Weight::from_parts(0, 43).saturating_mul(p.into())) + } + /// Storage: `Council::ProposalOf` (r:1 w:0) + /// Proof: `Council::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Council::CostOf` (r:1 w:1) + /// Proof: `Council::CostOf` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `MaxEncodedLen`) + fn release_proposal_cost() -> Weight { + // Proof Size summary in bytes: + // Measured: `1964` + // Estimated: `5429` + // Minimum execution time: 69_220_000 picoseconds. + Weight::from_parts(70_215_000, 5429) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } } // For backwards compatibility and tests. @@ -342,13 +394,13 @@ impl WeightInfo for () { fn set_members(m: u32, _n: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0 + m * (3232 ยฑ0) + p * (3190 ยฑ0)` - // Estimated: `15894 + m * (1967 ยฑ24) + p * (4332 ยฑ24)` - // Minimum execution time: 15_780_000 picoseconds. - Weight::from_parts(16_193_000, 15894) - // Standard Error: 56_777 - .saturating_add(Weight::from_parts(4_269_920, 0).saturating_mul(m.into())) - // Standard Error: 56_777 - .saturating_add(Weight::from_parts(8_046_697, 0).saturating_mul(p.into())) + // Estimated: `15894 + m * (1967 ยฑ23) + p * (4332 ยฑ23)` + // Minimum execution time: 16_699_000 picoseconds. + Weight::from_parts(17_015_000, 15894) + // Standard Error: 63_844 + .saturating_add(Weight::from_parts(4_593_256, 0).saturating_mul(m.into())) + // Standard Error: 63_844 + .saturating_add(Weight::from_parts(8_935_845, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(p.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) @@ -368,12 +420,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `380 + m * (32 ยฑ0)` // Estimated: `3997 + m * (32 ยฑ0)` - // Minimum execution time: 19_760_000 picoseconds. - Weight::from_parts(18_849_113, 3997) - // Standard Error: 48 - .saturating_add(Weight::from_parts(2_076, 0).saturating_mul(b.into())) - // Standard Error: 501 - .saturating_add(Weight::from_parts(17_171, 0).saturating_mul(m.into())) + // Minimum execution time: 22_010_000 picoseconds. + Weight::from_parts(21_392_812, 3997) + // Standard Error: 34 + .saturating_add(Weight::from_parts(1_533, 0).saturating_mul(b.into())) + // Standard Error: 354 + .saturating_add(Weight::from_parts(15_866, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) } @@ -391,12 +443,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `380 + m * (32 ยฑ0)` // Estimated: `3997 + m * (32 ยฑ0)` - // Minimum execution time: 21_535_000 picoseconds. - Weight::from_parts(20_564_012, 3997) - // Standard Error: 66 - .saturating_add(Weight::from_parts(2_252, 0).saturating_mul(b.into())) - // Standard Error: 689 - .saturating_add(Weight::from_parts(33_509, 0).saturating_mul(m.into())) + // Minimum execution time: 24_250_000 picoseconds. + Weight::from_parts(23_545_893, 3997) + // Standard Error: 40 + .saturating_add(Weight::from_parts(1_646, 0).saturating_mul(b.into())) + // Standard Error: 421 + .saturating_add(Weight::from_parts(26_248, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(m.into())) } @@ -406,27 +458,31 @@ impl WeightInfo for () { /// Proof: `Council::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `Council::Proposals` (r:1 w:1) /// Proof: `Council::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `MaxEncodedLen`) /// Storage: `Council::ProposalCount` (r:1 w:1) /// Proof: `Council::ProposalCount` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Council::Voting` (r:0 w:1) /// Proof: `Council::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Council::CostOf` (r:0 w:1) + /// Proof: `Council::CostOf` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `b` is `[2, 1024]`. /// The range of component `m` is `[2, 100]`. /// The range of component `p` is `[1, 100]`. fn propose_proposed(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `525 + m * (32 ยฑ0) + p * (36 ยฑ0)` - // Estimated: `3917 + m * (33 ยฑ0) + p * (36 ยฑ0)` - // Minimum execution time: 22_759_000 picoseconds. - Weight::from_parts(21_344_363, 3917) - // Standard Error: 121 - .saturating_add(Weight::from_parts(3_231, 0).saturating_mul(b.into())) - // Standard Error: 1_265 - .saturating_add(Weight::from_parts(31_197, 0).saturating_mul(m.into())) - // Standard Error: 1_249 - .saturating_add(Weight::from_parts(200_231, 0).saturating_mul(p.into())) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(4_u64)) + // Measured: `618 + m * (32 ยฑ0) + p * (36 ยฑ0)` + // Estimated: `3991 + m * (33 ยฑ0) + p * (36 ยฑ0)` + // Minimum execution time: 46_538_000 picoseconds. + Weight::from_parts(63_900_448, 3991) + // Standard Error: 350 + .saturating_add(Weight::from_parts(2_827, 0).saturating_mul(b.into())) + // Standard Error: 3_658 + .saturating_add(Weight::from_parts(53_340, 0).saturating_mul(m.into())) + // Standard Error: 3_611 + .saturating_add(Weight::from_parts(213_719, 0).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) .saturating_add(Weight::from_parts(0, 33).saturating_mul(m.into())) .saturating_add(Weight::from_parts(0, 36).saturating_mul(p.into())) } @@ -437,12 +493,12 @@ impl WeightInfo for () { /// The range of component `m` is `[5, 100]`. fn vote(m: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `974 + m * (64 ยฑ0)` - // Estimated: `4438 + m * (64 ยฑ0)` - // Minimum execution time: 22_935_000 picoseconds. - Weight::from_parts(23_694_302, 4438) - // Standard Error: 959 - .saturating_add(Weight::from_parts(57_721, 0).saturating_mul(m.into())) + // Measured: `1011 + m * (64 ยฑ0)` + // Estimated: `4475 + m * (64 ยฑ0)` + // Minimum execution time: 28_413_000 picoseconds. + Weight::from_parts(28_981_832, 4475) + // Standard Error: 665 + .saturating_add(Weight::from_parts(43_005, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 64).saturating_mul(m.into())) @@ -459,14 +515,14 @@ impl WeightInfo for () { /// The range of component `p` is `[1, 100]`. fn close_early_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `563 + m * (64 ยฑ0) + p * (36 ยฑ0)` - // Estimated: `4008 + m * (65 ยฑ0) + p * (36 ยฑ0)` - // Minimum execution time: 25_507_000 picoseconds. - Weight::from_parts(24_849_399, 4008) - // Standard Error: 1_089 - .saturating_add(Weight::from_parts(40_933, 0).saturating_mul(m.into())) - // Standard Error: 1_062 - .saturating_add(Weight::from_parts(198_325, 0).saturating_mul(p.into())) + // Measured: `600 + m * (64 ยฑ0) + p * (36 ยฑ0)` + // Estimated: `4042 + m * (65 ยฑ0) + p * (36 ยฑ0)` + // Minimum execution time: 27_725_000 picoseconds. + Weight::from_parts(30_174_093, 4042) + // Standard Error: 1_458 + .saturating_add(Weight::from_parts(41_100, 0).saturating_mul(m.into())) + // Standard Error: 1_422 + .saturating_add(Weight::from_parts(177_303, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 65).saturating_mul(m.into())) @@ -489,16 +545,16 @@ impl WeightInfo for () { /// The range of component `p` is `[1, 100]`. fn close_early_approved(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1010 + b * (1 ยฑ0) + m * (64 ยฑ0) + p * (40 ยฑ0)` - // Estimated: `4327 + b * (1 ยฑ0) + m * (66 ยฑ0) + p * (40 ยฑ0)` - // Minimum execution time: 43_927_000 picoseconds. - Weight::from_parts(43_733_720, 4327) - // Standard Error: 157 - .saturating_add(Weight::from_parts(3_737, 0).saturating_mul(b.into())) - // Standard Error: 1_664 - .saturating_add(Weight::from_parts(31_334, 0).saturating_mul(m.into())) - // Standard Error: 1_622 - .saturating_add(Weight::from_parts(222_964, 0).saturating_mul(p.into())) + // Measured: `1047 + b * (1 ยฑ0) + m * (64 ยฑ0) + p * (40 ยฑ0)` + // Estimated: `4360 + b * (1 ยฑ0) + m * (66 ยฑ0) + p * (40 ยฑ0)` + // Minimum execution time: 48_882_000 picoseconds. + Weight::from_parts(51_938_773, 4360) + // Standard Error: 208 + .saturating_add(Weight::from_parts(3_559, 0).saturating_mul(b.into())) + // Standard Error: 2_201 + .saturating_add(Weight::from_parts(38_678, 0).saturating_mul(m.into())) + // Standard Error: 2_145 + .saturating_add(Weight::from_parts(214_061, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(b.into())) @@ -519,14 +575,14 @@ impl WeightInfo for () { /// The range of component `p` is `[1, 100]`. fn close_disapproved(m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `583 + m * (64 ยฑ0) + p * (36 ยฑ0)` - // Estimated: `4028 + m * (65 ยฑ0) + p * (36 ยฑ0)` - // Minimum execution time: 28_129_000 picoseconds. - Weight::from_parts(26_799_151, 4028) - // Standard Error: 1_325 - .saturating_add(Weight::from_parts(41_545, 0).saturating_mul(m.into())) - // Standard Error: 1_292 - .saturating_add(Weight::from_parts(224_499, 0).saturating_mul(p.into())) + // Measured: `620 + m * (64 ยฑ0) + p * (36 ยฑ0)` + // Estimated: `4062 + m * (65 ยฑ0) + p * (36 ยฑ0)` + // Minimum execution time: 30_613_000 picoseconds. + Weight::from_parts(36_174_190, 4062) + // Standard Error: 1_899 + .saturating_add(Weight::from_parts(46_781, 0).saturating_mul(m.into())) + // Standard Error: 1_851 + .saturating_add(Weight::from_parts(185_875, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 65).saturating_mul(m.into())) @@ -551,16 +607,16 @@ impl WeightInfo for () { /// The range of component `p` is `[1, 100]`. fn close_approved(b: u32, m: u32, p: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1030 + b * (1 ยฑ0) + m * (64 ยฑ0) + p * (40 ยฑ0)` - // Estimated: `4347 + b * (1 ยฑ0) + m * (66 ยฑ0) + p * (40 ยฑ0)` - // Minimum execution time: 45_700_000 picoseconds. - Weight::from_parts(45_661_144, 4347) - // Standard Error: 149 - .saturating_add(Weight::from_parts(3_068, 0).saturating_mul(b.into())) - // Standard Error: 1_583 - .saturating_add(Weight::from_parts(36_426, 0).saturating_mul(m.into())) - // Standard Error: 1_543 - .saturating_add(Weight::from_parts(229_986, 0).saturating_mul(p.into())) + // Measured: `1067 + b * (1 ยฑ0) + m * (64 ยฑ0) + p * (40 ยฑ0)` + // Estimated: `4380 + b * (1 ยฑ0) + m * (66 ยฑ0) + p * (40 ยฑ0)` + // Minimum execution time: 51_253_000 picoseconds. + Weight::from_parts(56_399_941, 4380) + // Standard Error: 218 + .saturating_add(Weight::from_parts(2_920, 0).saturating_mul(b.into())) + // Standard Error: 2_310 + .saturating_add(Weight::from_parts(30_473, 0).saturating_mul(m.into())) + // Standard Error: 2_252 + .saturating_add(Weight::from_parts(208_468, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(b.into())) @@ -578,12 +634,60 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `392 + p * (32 ยฑ0)` // Estimated: `1877 + p * (32 ยฑ0)` - // Minimum execution time: 13_163_000 picoseconds. - Weight::from_parts(14_467_031, 1877) - // Standard Error: 1_073 - .saturating_add(Weight::from_parts(190_879, 0).saturating_mul(p.into())) + // Minimum execution time: 14_646_000 picoseconds. + Weight::from_parts(17_305_497, 1877) + // Standard Error: 1_331 + .saturating_add(Weight::from_parts(156_038, 0).saturating_mul(p.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) .saturating_add(Weight::from_parts(0, 32).saturating_mul(p.into())) } + /// Storage: `Council::ProposalOf` (r:1 w:1) + /// Proof: `Council::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Council::CostOf` (r:1 w:1) + /// Proof: `Council::CostOf` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `MaxEncodedLen`) + /// Storage: `Council::Proposals` (r:1 w:1) + /// Proof: `Council::Proposals` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Council::Voting` (r:0 w:1) + /// Proof: `Council::Voting` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `d` is `[0, 1]`. + /// The range of component `p` is `[1, 100]`. + fn kill(d: u32, p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1863 + d * (212 ยฑ0) + p * (41 ยฑ0)` + // Estimated: `5172 + d * (1901 ยฑ14) + p * (43 ยฑ0)` + // Minimum execution time: 22_164_000 picoseconds. + Weight::from_parts(24_932_256, 5172) + // Standard Error: 404_014 + .saturating_add(Weight::from_parts(33_833_807, 0).saturating_mul(d.into())) + // Standard Error: 6_256 + .saturating_add(Weight::from_parts(281_910, 0).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(d.into()))) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(d.into()))) + .saturating_add(Weight::from_parts(0, 1901).saturating_mul(d.into())) + .saturating_add(Weight::from_parts(0, 43).saturating_mul(p.into())) + } + /// Storage: `Council::ProposalOf` (r:1 w:0) + /// Proof: `Council::ProposalOf` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Council::CostOf` (r:1 w:1) + /// Proof: `Council::CostOf` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `MaxEncodedLen`) + fn release_proposal_cost() -> Weight { + // Proof Size summary in bytes: + // Measured: `1964` + // Estimated: `5429` + // Minimum execution time: 69_220_000 picoseconds. + Weight::from_parts(70_215_000, 5429) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } } diff --git a/substrate/frame/contracts/fixtures/Cargo.toml b/substrate/frame/contracts/fixtures/Cargo.toml index 6cb6447d8fd7ecbcfd401cba4bf8fcb27c1db549..4c01c1f061b7a8e70251a22e46add05b86971d60 100644 --- a/substrate/frame/contracts/fixtures/Cargo.toml +++ b/substrate/frame/contracts/fixtures/Cargo.toml @@ -20,8 +20,4 @@ parity-wasm = { workspace = true } tempfile = { workspace = true } toml = { workspace = true } twox-hash = { workspace = true, default-features = true } -polkavm-linker = { workspace = true, optional = true } anyhow = { workspace = true, default-features = true } - -[features] -riscv = ["polkavm-linker"] diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index baaeaf0342032f43821f19c5526d3a8850e3a035..7af733c536d08a7bf671a1cc2fe2da13b9f13070 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Compile contracts to wasm and RISC-V binaries. +//! Compile contracts to wasm. use anyhow::{bail, Context, Result}; use parity_wasm::elements::{deserialize_file, serialize_to_file, Internal}; use std::{ @@ -89,12 +89,6 @@ impl Entry { fn out_wasm_filename(&self) -> String { format!("{}.wasm", self.name()) } - - /// Return the name of the RISC-V polkavm file. - #[cfg(feature = "riscv")] - fn out_riscv_filename(&self) -> String { - format!("{}.polkavm", self.name()) - } } /// Collect all contract entries from the given source directory. @@ -236,53 +230,6 @@ fn post_process_wasm(input_path: &Path, output_path: &Path) -> Result<()> { serialize_to_file(output_path, module).map_err(Into::into) } -/// Build contracts for RISC-V. -#[cfg(feature = "riscv")] -fn invoke_riscv_build(current_dir: &Path) -> Result<()> { - let encoded_rustflags = [ - "-Crelocation-model=pie", - "-Clink-arg=--emit-relocs", - "-Clink-arg=--export-dynamic-symbol=__polkavm_symbol_export_hack__*", - ] - .join("\x1f"); - - let build_res = Command::new(env::var("CARGO")?) - .current_dir(current_dir) - .env_clear() - .env("PATH", env::var("PATH").unwrap_or_default()) - .env("CARGO_ENCODED_RUSTFLAGS", encoded_rustflags) - .env("RUSTUP_TOOLCHAIN", "rve-nightly") - .env("RUSTUP_HOME", env::var("RUSTUP_HOME").unwrap_or_default()) - .args(["build", "--release", "--target=riscv32ema-unknown-none-elf"]) - .output() - .expect("failed to execute process"); - - if build_res.status.success() { - return Ok(()) - } - - let stderr = String::from_utf8_lossy(&build_res.stderr); - - if stderr.contains("'rve-nightly' is not installed") { - eprintln!("RISC-V toolchain is not installed.\nDownload and install toolchain from https://github.com/paritytech/rustc-rv32e-toolchain."); - eprintln!("{}", stderr); - } else { - eprintln!("{}", stderr); - } - - bail!("Failed to build contracts"); -} -/// Post-process the compiled wasm contracts. -#[cfg(feature = "riscv")] -fn post_process_riscv(input_path: &Path, output_path: &Path) -> Result<()> { - let mut config = polkavm_linker::Config::default(); - config.set_strip(true); - let orig = fs::read(input_path).with_context(|| format!("Failed to read {:?}", input_path))?; - let linked = polkavm_linker::program_from_elf(config, orig.as_ref()) - .map_err(|err| anyhow::format_err!("Failed to link polkavm program: {}", err))?; - fs::write(output_path, linked.as_bytes()).map_err(Into::into) -} - /// Write the compiled contracts to the given output directory. fn write_output(build_dir: &Path, out_dir: &Path, entries: Vec) -> Result<()> { for entry in entries { @@ -292,12 +239,6 @@ fn write_output(build_dir: &Path, out_dir: &Path, entries: Vec) -> Result &out_dir.join(&wasm_output), )?; - #[cfg(feature = "riscv")] - post_process_riscv( - &build_dir.join("target/riscv32ema-unknown-none-elf/release").join(entry.name()), - &out_dir.join(entry.out_riscv_filename()), - )?; - entry.update_cache(out_dir)?; } @@ -347,9 +288,6 @@ fn main() -> Result<()> { invoke_wasm_build(tmp_dir_path)?; - #[cfg(feature = "riscv")] - invoke_riscv_build(tmp_dir_path)?; - write_output(tmp_dir_path, &out_dir, entries)?; Ok(()) } diff --git a/substrate/frame/contracts/fixtures/contracts/common/src/lib.rs b/substrate/frame/contracts/fixtures/contracts/common/src/lib.rs index 80e1f543a7b53ba6585e0d8a453cacce02fdac3d..504fa7f9e81df51ebda590be8946965c6dd0ec36 100644 --- a/substrate/frame/contracts/fixtures/contracts/common/src/lib.rs +++ b/substrate/frame/contracts/fixtures/contracts/common/src/lib.rs @@ -22,13 +22,6 @@ pub use uapi::{HostFn, HostFnImpl as api}; fn panic(_info: &core::panic::PanicInfo) -> ! { #[cfg(target_arch = "wasm32")] core::arch::wasm32::unreachable(); - - #[cfg(target_arch = "riscv32")] - // Safety: The unimp instruction is guaranteed to trap - unsafe { - core::arch::asm!("unimp"); - core::hint::unreachable_unchecked(); - } } /// Utility macro to read input passed to a contract. diff --git a/substrate/frame/contracts/fixtures/src/lib.rs b/substrate/frame/contracts/fixtures/src/lib.rs index 56a8e2321c5c3cc2181cbfeb676700993e3f0a77..f9272a32ad6136693c06a3a7ee626dd950617e17 100644 --- a/substrate/frame/contracts/fixtures/src/lib.rs +++ b/substrate/frame/contracts/fixtures/src/lib.rs @@ -39,7 +39,5 @@ mod test { fn out_dir_should_have_compiled_mocks() { let out_dir: std::path::PathBuf = env!("OUT_DIR").into(); assert!(out_dir.join("dummy.wasm").exists()); - #[cfg(feature = "riscv")] - assert!(out_dir.join("dummy.polkavm").exists()); } } diff --git a/substrate/frame/contracts/mock-network/src/lib.rs b/substrate/frame/contracts/mock-network/src/lib.rs index 34cc95f2eae0e8138a3bb988e0351f1002a04bee..cb9e22439b76d824d6bed684ae01de37ebc26120 100644 --- a/substrate/frame/contracts/mock-network/src/lib.rs +++ b/substrate/frame/contracts/mock-network/src/lib.rs @@ -1,18 +1,18 @@ // Copyright Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Substrate. -// Polkadot is free software: you can redistribute it and/or modify +// Substrate is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Polkadot is distributed in the hope that it will be useful, +// Substrate is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . +// along with Substrate. If not, see . pub mod mocks; pub mod parachain; diff --git a/substrate/frame/contracts/mock-network/src/mocks.rs b/substrate/frame/contracts/mock-network/src/mocks.rs index bf3baec7a524abc0a9c5eee8e637b4b9a9cf3e04..6903f01e91a2b5543235fdeddb74f46634855c6b 100644 --- a/substrate/frame/contracts/mock-network/src/mocks.rs +++ b/substrate/frame/contracts/mock-network/src/mocks.rs @@ -1,18 +1,18 @@ // Copyright Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Substrate. -// Polkadot is free software: you can redistribute it and/or modify +// Substrate is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Polkadot is distributed in the hope that it will be useful, +// Substrate is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . +// along with Substrate. If not, see . pub mod msg_queue; pub mod relay_message_queue; diff --git a/substrate/frame/contracts/mock-network/src/mocks/msg_queue.rs b/substrate/frame/contracts/mock-network/src/mocks/msg_queue.rs index 6e922c16c2982b8399978511b9dd5558ba2c0ce8..88ab8135ea1297d44b025ddf6d66fafbbe674858 100644 --- a/substrate/frame/contracts/mock-network/src/mocks/msg_queue.rs +++ b/substrate/frame/contracts/mock-network/src/mocks/msg_queue.rs @@ -1,18 +1,18 @@ // Copyright Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Substrate. -// Polkadot is free software: you can redistribute it and/or modify +// Substrate is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Polkadot is distributed in the hope that it will be useful, +// Substrate is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . +// along with Substrate. If not, see . //! Parachain runtime mock. diff --git a/substrate/frame/contracts/mock-network/src/mocks/relay_message_queue.rs b/substrate/frame/contracts/mock-network/src/mocks/relay_message_queue.rs index 14099965e3f173d7f06b86c08b722ceeded26418..50f0febd2c4403b4981aec0a416b5814f9902df7 100644 --- a/substrate/frame/contracts/mock-network/src/mocks/relay_message_queue.rs +++ b/substrate/frame/contracts/mock-network/src/mocks/relay_message_queue.rs @@ -1,18 +1,18 @@ // Copyright Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Substrate. -// Polkadot is free software: you can redistribute it and/or modify +// Substrate is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Polkadot is distributed in the hope that it will be useful, +// Substrate is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . +// along with Substrate. If not, see . use frame_support::{parameter_types, weights::Weight}; use xcm::latest::prelude::*; diff --git a/substrate/frame/contracts/mock-network/src/parachain.rs b/substrate/frame/contracts/mock-network/src/parachain.rs index 3579b46ea6e9c858e73cfaa879104c550042a011..6edbfb0e7e86a540e965dca80ebfe4ad01f35abf 100644 --- a/substrate/frame/contracts/mock-network/src/parachain.rs +++ b/substrate/frame/contracts/mock-network/src/parachain.rs @@ -1,18 +1,18 @@ // Copyright Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Substrate. -// Polkadot is free software: you can redistribute it and/or modify +// Substrate is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Polkadot is distributed in the hope that it will be useful, +// Substrate is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . +// along with Substrate. If not, see . //! Parachain runtime mock. @@ -94,6 +94,7 @@ impl pallet_balances::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type WeightInfo = (); + type DoneSlashHandler = (); } parameter_types! { 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 bf3c00b3ff1f5f3e78c164875d92742ea74a0cf0..9ad978bbc0317da57b5c114e193345d4fbf46652 100644 --- a/substrate/frame/contracts/mock-network/src/parachain/contracts_config.rs +++ b/substrate/frame/contracts/mock-network/src/parachain/contracts_config.rs @@ -1,18 +1,18 @@ // Copyright Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Substrate. -// Polkadot is free software: you can redistribute it and/or modify +// Substrate is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Polkadot is distributed in the hope that it will be useful, +// Substrate is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . +// along with Substrate. If not, see . use super::{Balances, Runtime, RuntimeCall, RuntimeEvent}; use crate::parachain::RuntimeHoldReason; diff --git a/substrate/frame/contracts/mock-network/src/primitives.rs b/substrate/frame/contracts/mock-network/src/primitives.rs index efc42772f88ade5868492964b1b383c26ee689ac..cb9b2f00f9e7374d4db4d7d7a13a4e1983325182 100644 --- a/substrate/frame/contracts/mock-network/src/primitives.rs +++ b/substrate/frame/contracts/mock-network/src/primitives.rs @@ -1,18 +1,18 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Substrate. -// Polkadot is free software: you can redistribute it and/or modify +// Substrate is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Polkadot is distributed in the hope that it will be useful, +// Substrate is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . +// along with Substrate. If not, see . pub type Balance = u128; diff --git a/substrate/frame/contracts/mock-network/src/relay_chain.rs b/substrate/frame/contracts/mock-network/src/relay_chain.rs index 8829fff3d0436d95b3e44d87b2258d13c167efc1..5fed061f80b45aaaad89e3a4ef3b0abef41000d8 100644 --- a/substrate/frame/contracts/mock-network/src/relay_chain.rs +++ b/substrate/frame/contracts/mock-network/src/relay_chain.rs @@ -1,18 +1,18 @@ // Copyright Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Substrate. -// Polkadot is free software: you can redistribute it and/or modify +// Substrate is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Polkadot is distributed in the hope that it will be useful, +// Substrate is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . +// along with Substrate. If not, see . //! Relay chain runtime mock. @@ -89,6 +89,7 @@ impl pallet_balances::Config for Runtime { type MaxFreezes = ConstU32<0>; type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; + type DoneSlashHandler = (); } impl shared::Config for Runtime { diff --git a/substrate/frame/contracts/src/benchmarking/code.rs b/substrate/frame/contracts/src/benchmarking/code.rs index 1473022b5537885ad2a068a6cf97b26e755811ef..b5918a5e182d2d0db168f9abb9a988aed9bdcd66 100644 --- a/substrate/frame/contracts/src/benchmarking/code.rs +++ b/substrate/frame/contracts/src/benchmarking/code.rs @@ -114,7 +114,6 @@ pub struct ImportedFunction { pub struct WasmModule { pub code: Vec, pub hash: ::Output, - pub memory: Option, } impl From for WasmModule { @@ -233,7 +232,7 @@ impl From for WasmModule { let code = contract.build().into_bytes().unwrap(); let hash = T::Hashing::hash(&code); - Self { code: code.into(), hash, memory: def.memory } + Self { code: code.into(), hash } } } diff --git a/substrate/frame/contracts/src/exec.rs b/substrate/frame/contracts/src/exec.rs index 31e0bf50b73e7de2131fac77487de1c421cabb3e..046affe32d966e6695edd0a0580c332a062e6e82 100644 --- a/substrate/frame/contracts/src/exec.rs +++ b/substrate/frame/contracts/src/exec.rs @@ -454,9 +454,6 @@ pub trait Executable: Sized { /// The code hash of the executable. fn code_hash(&self) -> &CodeHash; - /// Size of the contract code in bytes. - fn code_len(&self) -> u32; - /// The code does not contain any instructions which could lead to indeterminism. fn is_deterministic(&self) -> bool; } @@ -1838,10 +1835,6 @@ mod tests { &self.code_info } - fn code_len(&self) -> u32 { - 0 - } - fn is_deterministic(&self) -> bool { true } diff --git a/substrate/frame/contracts/src/wasm/mod.rs b/substrate/frame/contracts/src/wasm/mod.rs index f4ee76459c4eaa7fc117baece31b10333173cf41..c9786fa1516b52e58dfc5aaed7b2f100cead576f 100644 --- a/substrate/frame/contracts/src/wasm/mod.rs +++ b/substrate/frame/contracts/src/wasm/mod.rs @@ -488,10 +488,6 @@ impl Executable for WasmBlob { &self.code_info } - fn code_len(&self) -> u32 { - self.code.len() as u32 - } - fn is_deterministic(&self) -> bool { matches!(self.code_info.determinism, Determinism::Enforced) } diff --git a/substrate/frame/contracts/uapi/Cargo.toml b/substrate/frame/contracts/uapi/Cargo.toml index ccd86ba7890cb9a6a7d371bc0e578d921450b17c..09c70c287899667735be6dfe5fe179425a499df8 100644 --- a/substrate/frame/contracts/uapi/Cargo.toml +++ b/substrate/frame/contracts/uapi/Cargo.toml @@ -20,9 +20,6 @@ codec = { features = [ "max-encoded-len", ], optional = true, workspace = true } -[target.'cfg(target_arch = "riscv32")'.dependencies] -polkavm-derive = { workspace = true } - [package.metadata.docs.rs] default-target = ["wasm32-unknown-unknown"] diff --git a/substrate/frame/contracts/uapi/src/host.rs b/substrate/frame/contracts/uapi/src/host.rs index 51f0cd7eb2dc002ff041f1f27589f243e22cb896..52c9ea387e1c20cb9f57db5aa0a2e70eb46f24e0 100644 --- a/substrate/frame/contracts/uapi/src/host.rs +++ b/substrate/frame/contracts/uapi/src/host.rs @@ -17,9 +17,6 @@ use paste::paste; #[cfg(target_arch = "wasm32")] mod wasm32; -#[cfg(target_arch = "riscv32")] -mod riscv32; - macro_rules! hash_fn { ( $name:ident, $bytes:literal ) => { paste! { @@ -66,7 +63,7 @@ fn ptr_or_sentinel(data: &Option<&[u8]>) -> *const u8 { /// Implements [`HostFn`] for each supported target architecture. pub enum HostFnImpl {} -/// Defines all the host apis implemented by both wasm and RISC-V vms. +/// Defines all the host apis implemented by the wasm vm. pub trait HostFn: private::Sealed { /// Returns the number of times specified contract exists on the call stack. Delegated calls are /// not counted as separate calls. diff --git a/substrate/frame/contracts/uapi/src/host/riscv32.rs b/substrate/frame/contracts/uapi/src/host/riscv32.rs deleted file mode 100644 index 355520233212113df9b864add68d52f4b368ee60..0000000000000000000000000000000000000000 --- a/substrate/frame/contracts/uapi/src/host/riscv32.rs +++ /dev/null @@ -1,327 +0,0 @@ -#![allow(unused_variables, unused_mut)] -// 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. -// TODO: bring up to date with wasm32.rs - -use super::{CallFlags, HostFn, HostFnImpl, Result}; -use crate::ReturnFlags; - -/// A macro to implement all Host functions with a signature of `fn(&mut &mut [u8])`. -/// -/// Example: -/// ```nocompile -// impl_wrapper_for! { -// () => [gas_left], -// (v1) => [gas_left], -// } -// ``` -// -// Expands to: -// ```nocompile -// fn gas_left(output: &mut &mut [u8]) { -// unsafe { sys::gas_left(...); } -// } -// fn gas_left_v1(output: &mut &mut [u8]) { -// unsafe { sys::v1::gas_left(...); } -// } -// ``` -macro_rules! impl_wrapper_for { - (@impl_fn $( $mod:ident )::*, $suffix_sep: literal, $suffix:tt, $name:ident) => { - paste::paste! { - fn [<$name $suffix_sep $suffix>](output: &mut &mut [u8]) { - todo!() - } - } - }; - - () => {}; - - (($mod:ident) => [$( $name:ident),*], $($tail:tt)*) => { - $(impl_wrapper_for!(@impl_fn sys::$mod, "_", $mod, $name);)* - impl_wrapper_for!($($tail)*); - }; - - (() => [$( $name:ident),*], $($tail:tt)*) => { - $(impl_wrapper_for!(@impl_fn sys, "", "", $name);)* - impl_wrapper_for!($($tail)*); - }; -} - -/// A macro to implement all the hash functions Apis. -macro_rules! impl_hash_fn { - ( $name:ident, $bytes_result:literal ) => { - paste::item! { - fn [](input: &[u8], output: &mut [u8; $bytes_result]) { - todo!() - } - } - }; -} - -/// A macro to implement the get_storage functions. -macro_rules! impl_get_storage { - ($fn_name:ident, $sys_get_storage:path) => { - fn $fn_name(key: &[u8], output: &mut &mut [u8]) -> Result { - todo!() - } - }; -} - -impl HostFn for HostFnImpl { - fn instantiate_v1( - code_hash: &[u8], - gas: u64, - value: &[u8], - input: &[u8], - mut address: Option<&mut &mut [u8]>, - mut output: Option<&mut &mut [u8]>, - salt: &[u8], - ) -> Result { - todo!() - } - - fn instantiate_v2( - code_hash: &[u8], - ref_time_limit: u64, - proof_size_limit: u64, - deposit: Option<&[u8]>, - value: &[u8], - input: &[u8], - mut address: Option<&mut &mut [u8]>, - mut output: Option<&mut &mut [u8]>, - salt: &[u8], - ) -> Result { - todo!() - } - - fn call( - callee: &[u8], - gas: u64, - value: &[u8], - input_data: &[u8], - mut output: Option<&mut &mut [u8]>, - ) -> Result { - todo!() - } - - fn call_v1( - flags: CallFlags, - callee: &[u8], - gas: u64, - value: &[u8], - input_data: &[u8], - mut output: Option<&mut &mut [u8]>, - ) -> Result { - todo!() - } - - fn call_v2( - flags: CallFlags, - callee: &[u8], - ref_time_limit: u64, - proof_size_limit: u64, - deposit: Option<&[u8]>, - value: &[u8], - input_data: &[u8], - mut output: Option<&mut &mut [u8]>, - ) -> Result { - todo!() - } - - fn caller_is_root() -> u32 { - todo!() - } - - fn delegate_call( - flags: CallFlags, - code_hash: &[u8], - input: &[u8], - mut output: Option<&mut &mut [u8]>, - ) -> Result { - todo!() - } - - fn transfer(account_id: &[u8], value: &[u8]) -> Result { - todo!() - } - - fn deposit_event(topics: &[u8], data: &[u8]) { - todo!() - } - - fn set_storage(key: &[u8], value: &[u8]) { - todo!() - } - - fn set_storage_v1(key: &[u8], encoded_value: &[u8]) -> Option { - todo!() - } - - fn set_storage_v2(key: &[u8], encoded_value: &[u8]) -> Option { - todo!() - } - - fn set_transient_storage(key: &[u8], encoded_value: &[u8]) -> Option { - todo!() - } - - fn clear_storage(key: &[u8]) { - todo!() - } - - fn clear_storage_v1(key: &[u8]) -> Option { - todo!() - } - - fn clear_transient_storage(key: &[u8]) -> Option { - todo!() - } - - impl_get_storage!(get_storage, sys::get_storage); - impl_get_storage!(get_storage_v1, sys::v1::get_storage); - - fn get_transient_storage(key: &[u8], output: &mut &mut [u8]) -> Result { - todo!() - } - - fn take_storage(key: &[u8], output: &mut &mut [u8]) -> Result { - todo!() - } - - fn take_transient_storage(key: &[u8], output: &mut &mut [u8]) -> Result { - todo!() - } - - fn contains_storage(key: &[u8]) -> Option { - todo!() - } - - fn contains_storage_v1(key: &[u8]) -> Option { - todo!() - } - - fn contains_transient_storage(key: &[u8]) -> Option { - todo!() - } - - fn terminate(beneficiary: &[u8]) -> ! { - todo!() - } - - fn terminate_v1(beneficiary: &[u8]) -> ! { - todo!() - } - - fn call_chain_extension(func_id: u32, input: &[u8], output: Option<&mut &mut [u8]>) -> u32 { - todo!() - } - - fn input(output: &mut &mut [u8]) { - todo!() - } - - fn return_value(flags: ReturnFlags, return_value: &[u8]) -> ! { - todo!() - } - - fn call_runtime(call: &[u8]) -> Result { - todo!() - } - - fn debug_message(str: &[u8]) -> Result { - todo!() - } - - impl_wrapper_for! { - () => [caller, block_number, address, balance, gas_left, value_transferred, now, minimum_balance], - (v1) => [gas_left], - } - - fn weight_to_fee(gas: u64, output: &mut &mut [u8]) { - todo!() - } - - fn weight_to_fee_v1(ref_time_limit: u64, proof_size_limit: u64, output: &mut &mut [u8]) { - todo!() - } - - impl_hash_fn!(sha2_256, 32); - impl_hash_fn!(keccak_256, 32); - impl_hash_fn!(blake2_256, 32); - impl_hash_fn!(blake2_128, 16); - - fn ecdsa_recover( - signature: &[u8; 65], - message_hash: &[u8; 32], - output: &mut [u8; 33], - ) -> Result { - todo!() - } - - fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result { - todo!() - } - - fn sr25519_verify(signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> Result { - todo!() - } - - fn is_contract(account_id: &[u8]) -> bool { - todo!() - } - - fn caller_is_origin() -> bool { - todo!() - } - - fn set_code_hash(code_hash: &[u8]) -> Result { - todo!() - } - - fn code_hash(account_id: &[u8], output: &mut [u8]) -> Result { - todo!() - } - - fn own_code_hash(output: &mut [u8]) { - todo!() - } - - fn account_reentrance_count(account: &[u8]) -> u32 { - todo!() - } - - fn lock_delegate_dependency(code_hash: &[u8]) { - todo!() - } - - fn unlock_delegate_dependency(code_hash: &[u8]) { - todo!() - } - - fn instantiation_nonce() -> u64 { - todo!() - } - - fn reentrance_count() -> u32 { - todo!() - } - - fn xcm_execute(msg: &[u8]) -> Result { - todo!() - } - - fn xcm_send(dest: &[u8], msg: &[u8], output: &mut [u8; 32]) -> Result { - todo!() - } -} diff --git a/substrate/frame/contracts/uapi/src/lib.rs b/substrate/frame/contracts/uapi/src/lib.rs index 83877c6efd408c27a58727c846210f915280d50f..7e30c811ee07c7bede46b57f6b53fd98a8d3504d 100644 --- a/substrate/frame/contracts/uapi/src/lib.rs +++ b/substrate/frame/contracts/uapi/src/lib.rs @@ -21,10 +21,10 @@ mod flags; pub use flags::*; -#[cfg(any(target_arch = "wasm32", target_arch = "riscv32"))] +#[cfg(target_arch = "wasm32")] mod host; -#[cfg(any(target_arch = "wasm32", target_arch = "riscv32"))] +#[cfg(target_arch = "wasm32")] pub use host::*; macro_rules! define_error_codes { diff --git a/substrate/frame/delegated-staking/src/impls.rs b/substrate/frame/delegated-staking/src/impls.rs index 4e6812dee24930ad204784f57125dec07f907427..a443df7b20f568f6be1b9b0b679d55fff8e0f3c9 100644 --- a/substrate/frame/delegated-staking/src/impls.rs +++ b/substrate/frame/delegated-staking/src/impls.rs @@ -124,7 +124,7 @@ impl DelegationMigrator for Pallet { /// Only used for testing. #[cfg(feature = "runtime-benchmarks")] - fn migrate_to_direct_staker(agent: Agent) { + fn force_kill_agent(agent: Agent) { >::remove(agent.clone().get()); >::iter() .filter(|(_, delegation)| delegation.agent == agent.clone().get()) @@ -136,8 +136,6 @@ impl DelegationMigrator for Pallet { ); >::remove(&delegator); }); - - T::CoreStaking::migrate_to_direct_staker(&agent.get()); } } diff --git a/substrate/frame/delegated-staking/src/lib.rs b/substrate/frame/delegated-staking/src/lib.rs index 7b8d14b0a611897339f8c2104cc1ab3187ddd215..1d181eb29cab7321d94b083149a8e58a17ecd924 100644 --- a/substrate/frame/delegated-staking/src/lib.rs +++ b/substrate/frame/delegated-staking/src/lib.rs @@ -71,8 +71,8 @@ //! - Migrate a `Nominator` account to an `agent` account. See [`Pallet::migrate_to_agent`]. //! Explained in more detail in the `Migration` section. //! - Migrate unclaimed delegated funds from `agent` to delegator. When a nominator migrates to an -//! agent, the funds are held in a proxy account. This function allows the delegator to claim their -//! share of the funds from the proxy account. See [`Pallet::migrate_delegation`]. +//! agent, the funds are held in a proxy account. This function allows the delegator to claim +//! their share of the funds from the proxy account. See [`Pallet::migrate_delegation`]. //! //! ## Lazy Slashing //! One of the reasons why direct nominators on staking pallet cannot scale well is because all diff --git a/substrate/frame/delegated-staking/src/tests.rs b/substrate/frame/delegated-staking/src/tests.rs index 2c965e18b1b335d25ec4fbd571c3e032a782dba5..b7b82a43771eb388ee1a5e0e11145e614f56930f 100644 --- a/substrate/frame/delegated-staking/src/tests.rs +++ b/substrate/frame/delegated-staking/src/tests.rs @@ -676,7 +676,7 @@ mod staking_integration { // in equal parts. lets try to migrate this nominator into delegate based stake. // all balance currently is in 200 - assert_eq!(Balances::free_balance(agent), agent_amount); + assert_eq!(pallet_staking::asset::stakeable_balance::(&agent), agent_amount); // to migrate, nominator needs to set an account as a proxy delegator where staked funds // will be moved and delegated back to this old nominator account. This should be funded diff --git a/substrate/frame/election-provider-multi-phase/src/lib.rs b/substrate/frame/election-provider-multi-phase/src/lib.rs index 09248e77848b6f72ff7cb99ae20a7002a07d9288..072cfe176b61991081d64e3f83fc1f353d4ae0c6 100644 --- a/substrate/frame/election-provider-multi-phase/src/lib.rs +++ b/substrate/frame/election-provider-multi-phase/src/lib.rs @@ -1208,9 +1208,8 @@ pub mod pallet { } let _ = Self::unsigned_pre_dispatch_checks(raw_solution) - .map_err(|err| { + .inspect_err(|err| { log!(debug, "unsigned transaction validation failed due to {:?}", err); - err }) .map_err(dispatch_error_to_invalid)?; 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 0dc202ff2115391c462748c2669ea5fbc16e02bb..13514969438760d13d8d34b369787deddeb205f6 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 @@ -322,24 +322,24 @@ fn automatic_unbonding_pools() { 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 init_stakeable_balance_2 = pallet_staking::asset::stakeable_balance::(&2); + let init_stakeable_balance_3 = pallet_staking::asset::stakeable_balance::(&3); let pool_bonded_account = Pools::generate_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); + assert_eq!(staked_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); + assert_eq!(staked_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!(staked_amount_for(pool_bonded_account), 25); assert_eq!(TotalValueLocked::::get(), 25); @@ -350,7 +350,7 @@ fn automatic_unbonding_pools() { 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); + assert_eq!(staked_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); @@ -372,8 +372,8 @@ fn automatic_unbonding_pools() { 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); + let staked_before_withdraw_pool = staked_amount_for(pool_bonded_account); + assert_eq!(pallet_staking::asset::stakeable_balance::(&pool_bonded_account), 26); // now unbonding 3 will work, although the pool's ledger still has the unlocking chunks // filled up. @@ -391,20 +391,21 @@ fn automatic_unbonding_pools() { ); // 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); + assert_eq!(pallet_staking::asset::stakeable_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)); + assert_eq!(staked_before_withdraw_pool - 10, staked_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); + // from the pool's non staked balance. + assert_eq!(pallet_staking::asset::stakeable_balance::(&pool_bonded_account), 26); + assert_eq!(pallet_staking::asset::staked::(&pool_bonded_account), 15); assert_ok!(Pools::withdraw_unbonded(RuntimeOrigin::signed(2), 2, 10)); - assert_eq!(Balances::free_balance(pool_bonded_account), 16); + assert_eq!(pallet_staking::asset::stakeable_balance::(&pool_bonded_account), 16); - assert_eq!(Balances::free_balance(2), 20); + assert_eq!(pallet_staking::asset::stakeable_balance::(&2), 20); assert_eq!(TotalValueLocked::::get(), 15); // 3 cannot withdraw yet. @@ -423,9 +424,15 @@ fn automatic_unbonding_pools() { 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!(pallet_staking::asset::stakeable_balance::(&pool_bonded_account), 6); // 5 init bonded + ED + assert_eq!( + pallet_staking::asset::stakeable_balance::(&2), + init_stakeable_balance_2 + ); + assert_eq!( + pallet_staking::asset::stakeable_balance::(&3), + init_stakeable_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 e45452c1ddf9dcd693a93e1969abec3cd2a37352..f20e3983b09d1b12770de142694ddcfabc07f9c3 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 @@ -915,9 +915,8 @@ 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 staked_amount_for(account_id: AccountId) -> Balance { + pallet_staking::asset::staked::(&account_id) } pub(crate) fn staking_events() -> Vec> { diff --git a/substrate/frame/election-provider-support/src/lib.rs b/substrate/frame/election-provider-support/src/lib.rs index 394f58a38442519dd27286bd74f3459e63b6a857..cb3249e388a318abc065eefacc429b7adc128c9b 100644 --- a/substrate/frame/election-provider-support/src/lib.rs +++ b/substrate/frame/election-provider-support/src/lib.rs @@ -687,7 +687,7 @@ sp_core::generate_feature_enabled_macro!( ); sp_core::generate_feature_enabled_macro!( - runtime_benchmarks_fuzz_or_std_enabled, - any(feature = "runtime-benchmarks", feature = "fuzzing", feature = "std"), + runtime_benchmarks_or_std_enabled, + any(feature = "runtime-benchmarks", feature = "std"), $ ); diff --git a/substrate/frame/elections-phragmen/src/lib.rs b/substrate/frame/elections-phragmen/src/lib.rs index 6d91448fd1859cf0eb1143ca6341e08f0d4353eb..a1c5f69e1b65abcd2b77e78fd5fe73a9231de7c9 100644 --- a/substrate/frame/elections-phragmen/src/lib.rs +++ b/substrate/frame/elections-phragmen/src/lib.rs @@ -829,7 +829,7 @@ impl Pallet { T::Currency::unreserve(who, removed.deposit); } - let maybe_next_best = RunnersUp::::mutate(|r| r.pop()).map(|next_best| { + let maybe_next_best = RunnersUp::::mutate(|r| r.pop()).inspect(|next_best| { // defensive-only: Members and runners-up are disjoint. This will always be err and // give us an index to insert. if let Err(index) = members.binary_search_by(|m| m.who.cmp(&next_best.who)) { @@ -839,7 +839,6 @@ impl Pallet { // is already a member, so not much more to do. log::error!(target: LOG_TARGET, "A member seems to also be a runner-up."); } - next_best }); Ok(maybe_next_best) })?; diff --git a/substrate/frame/examples/multi-block-migrations/src/migrations/v1/mod.rs b/substrate/frame/examples/multi-block-migrations/src/migrations/v1/mod.rs index 2016b03de45e53e94a77e29a87f4cb70f8e17e9b..6243846d86b06f7938541865756d3511bbbb9ae5 100644 --- a/substrate/frame/examples/multi-block-migrations/src/migrations/v1/mod.rs +++ b/substrate/frame/examples/multi-block-migrations/src/migrations/v1/mod.rs @@ -21,6 +21,8 @@ //! [`v0::MyMap`](`crate::migrations::v1::v0::MyMap`) storage map, transforms them, //! and inserts them into the [`MyMap`](`crate::pallet::MyMap`) storage map. +extern crate alloc; + use super::PALLET_MIGRATIONS_ID; use crate::pallet::{Config, MyMap}; use frame_support::{ @@ -29,6 +31,12 @@ use frame_support::{ weights::WeightMeter, }; +#[cfg(feature = "try-runtime")] +use alloc::collections::btree_map::BTreeMap; + +#[cfg(feature = "try-runtime")] +use alloc::vec::Vec; + mod benchmarks; mod tests; pub mod weights; @@ -115,4 +123,39 @@ impl SteppedMigration for LazyMigrationV1 Result, frame_support::sp_runtime::TryRuntimeError> { + use codec::Encode; + + // Return the state of the storage before the migration. + Ok(v0::MyMap::::iter().collect::>().encode()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(prev: Vec) -> Result<(), frame_support::sp_runtime::TryRuntimeError> { + use codec::Decode; + + // Check the state of the storage after the migration. + let prev_map = BTreeMap::::decode(&mut &prev[..]) + .expect("Failed to decode the previous storage state"); + + // Check the len of prev and post are the same. + assert_eq!( + MyMap::::iter().count(), + prev_map.len(), + "Migration failed: the number of items in the storage after the migration is not the same as before" + ); + + for (key, value) in prev_map { + let new_value = + MyMap::::get(key).expect("Failed to get the value after the migration"); + assert_eq!( + value as u64, new_value, + "Migration failed: the value after the migration is not the same as before" + ); + } + + Ok(()) + } } diff --git a/substrate/frame/examples/offchain-worker/src/tests.rs b/substrate/frame/examples/offchain-worker/src/tests.rs index b665cbbb62aed3c38326881904ec685070e4b67f..741adbe6d26ac4b0f8fb0157b4668949c0cca2c9 100644 --- a/substrate/frame/examples/offchain-worker/src/tests.rs +++ b/substrate/frame/examples/offchain-worker/src/tests.rs @@ -266,11 +266,12 @@ fn should_submit_unsigned_transaction_on_chain_for_any_account() { { assert_eq!(body, price_payload); - let signature_valid = - ::Public, - frame_system::pallet_prelude::BlockNumberFor, - > as SignedPayload>::verify::(&price_payload, signature); + let signature_valid = ::Public, + frame_system::pallet_prelude::BlockNumberFor, + > as SignedPayload>::verify::( + &price_payload, signature + ); assert!(signature_valid); } @@ -320,11 +321,12 @@ fn should_submit_unsigned_transaction_on_chain_for_all_accounts() { { assert_eq!(body, price_payload); - let signature_valid = - ::Public, - frame_system::pallet_prelude::BlockNumberFor, - > as SignedPayload>::verify::(&price_payload, signature); + let signature_valid = ::Public, + frame_system::pallet_prelude::BlockNumberFor, + > as SignedPayload>::verify::( + &price_payload, signature + ); assert!(signature_valid); } diff --git a/substrate/frame/examples/single-block-migrations/src/migrations/v1.rs b/substrate/frame/examples/single-block-migrations/src/migrations/v1.rs index 55cf7cef9a7a85a2a2174e5f9b2cb48c38db2549..922c03afdd1e7405b1927903ddf29197e93b7829 100644 --- a/substrate/frame/examples/single-block-migrations/src/migrations/v1.rs +++ b/substrate/frame/examples/single-block-migrations/src/migrations/v1.rs @@ -60,7 +60,7 @@ impl UncheckedOnRuntimeUpgrade for InnerMigrateV0ToV1 { /// /// - 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`]. + /// [`crate::CurrentAndPreviousValue`]. fn on_runtime_upgrade() -> frame_support::weights::Weight { // Read the old value from storage if let Some(old_value) = v0::Value::::take() { diff --git a/substrate/frame/executive/src/lib.rs b/substrate/frame/executive/src/lib.rs index 1e7bac64e18fd68ddec72fe38eff5b67e2ca7c2e..fe702e1fc3951e846a8adffd7615f93e318a9d79 100644 --- a/substrate/frame/executive/src/lib.rs +++ b/substrate/frame/executive/src/lib.rs @@ -382,9 +382,8 @@ where , >>::try_state(*header.number(), select.clone()) - .map_err(|e| { + .inspect_err(|e| { log::error!(target: LOG_TARGET, "failure: {:?}", e); - e })?; if select.any() { let res = AllPalletsWithSystem::try_decode_entire_state(); diff --git a/substrate/frame/fast-unstake/src/tests.rs b/substrate/frame/fast-unstake/src/tests.rs index 77128872f285f379237153290e394f3a461226e8..7c11f381ca102927e10a3b8241d87dfa28a07d5b 100644 --- a/substrate/frame/fast-unstake/src/tests.rs +++ b/substrate/frame/fast-unstake/src/tests.rs @@ -137,15 +137,16 @@ fn deregister_works() { ExtBuilder::default().build_and_execute(|| { ErasToCheckPerBlock::::put(1); - assert_eq!(::Currency::reserved_balance(&1), 0); + // reserved balance prior to registering for fast unstake. + let pre_reserved = ::Currency::reserved_balance(&1); // Controller account registers for fast unstake. assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(1))); - assert_eq!(::Currency::reserved_balance(&1), Deposit::get()); + assert_eq!(::Currency::reserved_balance(&1) - pre_reserved, Deposit::get()); // Controller then changes mind and deregisters. assert_ok!(FastUnstake::deregister(RuntimeOrigin::signed(1))); - assert_eq!(::Currency::reserved_balance(&1), 0); + assert_eq!(::Currency::reserved_balance(&1) - pre_reserved, 0); // Ensure stash no longer exists in the queue. assert_eq!(Queue::::get(1), None); @@ -243,7 +244,8 @@ mod on_idle { CurrentEra::::put(BondingDuration::get()); // given - assert_eq!(::Currency::reserved_balance(&1), 0); + // reserved balance prior to registering for fast unstake. + let pre_reserved = ::Currency::reserved_balance(&1); assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(1))); assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(3))); @@ -251,7 +253,10 @@ mod on_idle { assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(7))); assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(9))); - assert_eq!(::Currency::reserved_balance(&1), Deposit::get()); + assert_eq!( + ::Currency::reserved_balance(&1) - pre_reserved, + Deposit::get() + ); assert_eq!(Queue::::count(), 5); assert_eq!(Head::::get(), None); @@ -279,6 +284,9 @@ mod on_idle { // when next_block(true); + // pre_reserve may change due to unstaked amount. + let pre_reserved = ::Currency::reserved_balance(&1); + // then assert_eq!( Head::::get(), @@ -289,7 +297,7 @@ mod on_idle { ); assert_eq!(Queue::::count(), 3); - assert_eq!(::Currency::reserved_balance(&1), 0); + assert_eq!(::Currency::reserved_balance(&1) - pre_reserved, 0); assert_eq!( fast_unstake_events_since_last_call(), diff --git a/substrate/frame/grandpa/src/mock.rs b/substrate/frame/grandpa/src/mock.rs index ae230a0209a7d1ec5341fd200f766dc64e36da05..caac4107cfb711813cd2b98c85ed5b0dc54c0466 100644 --- a/substrate/frame/grandpa/src/mock.rs +++ b/substrate/frame/grandpa/src/mock.rs @@ -28,11 +28,11 @@ use frame_election_provider_support::{ }; use frame_support::{ derive_impl, parameter_types, - traits::{ConstU128, ConstU32, ConstU64, KeyOwnerProofSystem, OnFinalize, OnInitialize}, + traits::{ConstU128, ConstU32, ConstU64, OnFinalize, OnInitialize}, }; use pallet_session::historical as pallet_session_historical; use sp_consensus_grandpa::{RoundNumber, SetId, GRANDPA_ENGINE_ID}; -use sp_core::{crypto::KeyTypeId, H256}; +use sp_core::H256; use sp_keyring::Ed25519Keyring; use sp_runtime::{ curve::PiecewiseLinear, @@ -186,7 +186,7 @@ impl Config for Test { type MaxAuthorities = ConstU32<100>; type MaxNominators = ConstU32<1000>; type MaxSetIdSessionEntries = MaxSetIdSessionEntries; - type KeyOwnerProof = >::Proof; + type KeyOwnerProof = sp_session::MembershipProof; type EquivocationReportSystem = super::EquivocationReportSystem; } diff --git a/substrate/frame/message-queue/src/integration_test.rs b/substrate/frame/message-queue/src/integration_test.rs index 14b8d2217eb2b3908377014feb90c73588c79eff..e4db87d8be7a32638b34133139712052d79bf135 100644 --- a/substrate/frame/message-queue/src/integration_test.rs +++ b/substrate/frame/message-queue/src/integration_test.rs @@ -151,6 +151,7 @@ fn stress_test_recursive() { TotalEnqueued::set(TotalEnqueued::get() + enqueued); Enqueued::set(Enqueued::get() + enqueued); Called::set(Called::get() + 1); + Ok(()) })); build_and_execute::(|| { diff --git a/substrate/frame/message-queue/src/lib.rs b/substrate/frame/message-queue/src/lib.rs index 2dbffef7e5a242b1636313a76bba9fadcf3f9d28..48002acb14746fc3a52fbbfa8ec9d517974d7d68 100644 --- a/substrate/frame/message-queue/src/lib.rs +++ b/substrate/frame/message-queue/src/lib.rs @@ -225,7 +225,7 @@ use sp_arithmetic::traits::{BaseArithmetic, Unsigned}; use sp_core::{defer, H256}; use sp_runtime::{ traits::{One, Zero}, - SaturatedConversion, Saturating, + SaturatedConversion, Saturating, TransactionOutcome, }; use sp_weights::WeightMeter; pub use weights::WeightInfo; @@ -1435,6 +1435,8 @@ impl Pallet { /// The base weight of this function needs to be accounted for by the caller. `weight` is the /// remaining weight to process the message. `overweight_limit` is the maximum weight that a /// message can ever consume. Messages above this limit are marked as permanently overweight. + /// This process is also transactional, any form of error that occurs in processing a message + /// causes storage changes to be rolled back. fn process_message_payload( origin: MessageOriginOf, page_index: PageIndex, @@ -1447,7 +1449,27 @@ impl Pallet { use ProcessMessageError::*; let prev_consumed = meter.consumed(); - match T::MessageProcessor::process_message(message, origin.clone(), meter, &mut id) { + let transaction = + storage::with_transaction(|| -> TransactionOutcome> { + let res = + T::MessageProcessor::process_message(message, origin.clone(), meter, &mut id); + match &res { + Ok(_) => TransactionOutcome::Commit(Ok(res)), + Err(_) => TransactionOutcome::Rollback(Ok(res)), + } + }); + + let transaction = match transaction { + Ok(result) => result, + _ => { + defensive!( + "Error occurred processing message, storage changes will be rolled back" + ); + return MessageExecutionStatus::Unprocessable { permanent: true } + }, + }; + + match transaction { Err(Overweight(w)) if w.any_gt(overweight_limit) => { // Permanently overweight. Self::deposit_event(Event::::OverweightEnqueued { diff --git a/substrate/frame/message-queue/src/mock.rs b/substrate/frame/message-queue/src/mock.rs index 26533cc7c330c00564d0a14a9898ca4568fffbcf..d3f719c623565d7adbe71e201e1e2032296d8282 100644 --- a/substrate/frame/message-queue/src/mock.rs +++ b/substrate/frame/message-queue/src/mock.rs @@ -184,8 +184,15 @@ impl ProcessMessage for RecordingMessageProcessor { if meter.try_consume(required).is_ok() { if let Some(p) = message.strip_prefix(&b"callback="[..]) { let s = String::from_utf8(p.to_vec()).expect("Need valid UTF8"); - Callback::get()(&origin, s.parse().expect("Expected an u32")); + if let Err(()) = Callback::get()(&origin, s.parse().expect("Expected an u32")) { + return Err(ProcessMessageError::Corrupt) + } + + if s.contains("000") { + return Ok(false) + } } + let mut m = MessagesProcessed::get(); m.push((message.to_vec(), origin)); MessagesProcessed::set(m); @@ -197,7 +204,7 @@ impl ProcessMessage for RecordingMessageProcessor { } parameter_types! { - pub static Callback: Box = Box::new(|_, _| {}); + pub static Callback: Box Result<(), ()>> = Box::new(|_, _| { Ok(()) }); pub static IgnoreStackOvError: bool = false; } @@ -252,7 +259,9 @@ impl ProcessMessage for CountingMessageProcessor { if meter.try_consume(required).is_ok() { if let Some(p) = message.strip_prefix(&b"callback="[..]) { let s = String::from_utf8(p.to_vec()).expect("Need valid UTF8"); - Callback::get()(&origin, s.parse().expect("Expected an u32")); + if let Err(()) = Callback::get()(&origin, s.parse().expect("Expected an u32")) { + return Err(ProcessMessageError::Corrupt) + } } NumMessagesProcessed::set(NumMessagesProcessed::get() + 1); Ok(true) diff --git a/substrate/frame/message-queue/src/tests.rs b/substrate/frame/message-queue/src/tests.rs index e89fdb8b3208e2e218f2f8ba8dd797f3d08e12bb..fac135f135cefd9258ec1dd7c71677b093194fbd 100644 --- a/substrate/frame/message-queue/src/tests.rs +++ b/substrate/frame/message-queue/src/tests.rs @@ -1675,6 +1675,7 @@ fn regression_issue_2319() { build_and_execute::(|| { Callback::set(Box::new(|_, _| { MessageQueue::enqueue_message(mock_helpers::msg("anothermessage"), There); + Ok(()) })); use MessageOrigin::*; @@ -1695,23 +1696,26 @@ fn regression_issue_2319() { #[test] fn recursive_enqueue_works() { build_and_execute::(|| { - Callback::set(Box::new(|o, i| match i { - 0 => { - MessageQueue::enqueue_message(msg(&format!("callback={}", 1)), *o); - }, - 1 => { - for _ in 0..100 { - MessageQueue::enqueue_message(msg(&format!("callback={}", 2)), *o); - } - for i in 0..100 { - MessageQueue::enqueue_message(msg(&format!("callback={}", 3)), i.into()); - } - }, - 2 | 3 => { - MessageQueue::enqueue_message(msg(&format!("callback={}", 4)), *o); - }, - 4 => (), - _ => unreachable!(), + Callback::set(Box::new(|o, i| { + match i { + 0 => { + MessageQueue::enqueue_message(msg(&format!("callback={}", 1)), *o); + }, + 1 => { + for _ in 0..100 { + MessageQueue::enqueue_message(msg(&format!("callback={}", 2)), *o); + } + for i in 0..100 { + MessageQueue::enqueue_message(msg(&format!("callback={}", 3)), i.into()); + } + }, + 2 | 3 => { + MessageQueue::enqueue_message(msg(&format!("callback={}", 4)), *o); + }, + 4 => (), + _ => unreachable!(), + }; + Ok(()) })); MessageQueue::enqueue_message(msg("callback=0"), MessageOrigin::Here); @@ -1735,6 +1739,7 @@ fn recursive_service_is_forbidden() { // This call will fail since it is recursive. But it will not mess up the state. assert_storage_noop!(MessageQueue::service_queues(10.into_weight())); MessageQueue::enqueue_message(msg("m2"), There); + Ok(()) })); for _ in 0..5 { @@ -1778,6 +1783,7 @@ fn recursive_overweight_while_service_is_forbidden() { ), ExecuteOverweightError::RecursiveDisallowed ); + Ok(()) })); MessageQueue::enqueue_message(msg("weight=10"), There); @@ -1800,6 +1806,7 @@ fn recursive_reap_page_is_forbidden() { Callback::set(Box::new(|_, _| { // This call will fail since it is recursive. But it will not mess up the state. assert_noop!(MessageQueue::do_reap_page(&Here, 0), Error::::RecursiveDisallowed); + Ok(()) })); // Create 10 pages more than the stale limit. @@ -1975,3 +1982,55 @@ fn execute_overweight_keeps_stack_ov_message() { System::reset_events(); }); } + +#[test] +fn process_message_error_reverts_storage_changes() { + build_and_execute::(|| { + assert!(!sp_io::storage::exists(b"key"), "Key should not exist"); + + Callback::set(Box::new(|_, _| { + sp_io::storage::set(b"key", b"value"); + Err(()) + })); + + MessageQueue::enqueue_message(msg("callback=0"), MessageOrigin::Here); + MessageQueue::service_queues(10.into_weight()); + + assert!(!sp_io::storage::exists(b"key"), "Key should have been rolled back"); + }); +} + +#[test] +fn process_message_ok_false_keeps_storage_changes() { + build_and_execute::(|| { + assert!(!sp_io::storage::exists(b"key"), "Key should not exist"); + + Callback::set(Box::new(|_, _| { + sp_io::storage::set(b"key", b"value"); + Ok(()) + })); + + // 000 will make it return `Ok(false)` + MessageQueue::enqueue_message(msg("callback=000"), MessageOrigin::Here); + MessageQueue::service_queues(10.into_weight()); + + assert_eq!(sp_io::storage::exists(b"key"), true); + }); +} + +#[test] +fn process_message_ok_true_keeps_storage_changes() { + build_and_execute::(|| { + assert!(!sp_io::storage::exists(b"key"), "Key should not exist"); + + Callback::set(Box::new(|_, _| { + sp_io::storage::set(b"key", b"value"); + Ok(()) + })); + + MessageQueue::enqueue_message(msg("callback=0"), MessageOrigin::Here); + MessageQueue::service_queues(10.into_weight()); + + assert_eq!(sp_io::storage::exists(b"key"), true); + }); +} diff --git a/substrate/frame/migrations/Cargo.toml b/substrate/frame/migrations/Cargo.toml index 5fbed74a4400704db6610ec54e3aba8dd234cdae..a32e48e652805f91fabf340916351f6c27d9b79a 100644 --- a/substrate/frame/migrations/Cargo.toml +++ b/substrate/frame/migrations/Cargo.toml @@ -12,6 +12,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { features = ["derive"], workspace = true } +cfg-if = { workspace = true } docify = { workspace = true } impl-trait-for-tuples = { workspace = true } log = { workspace = true, default-features = true } diff --git a/substrate/frame/migrations/src/lib.rs b/substrate/frame/migrations/src/lib.rs index 68041a57eaa2e8f673f5f9c9fb774027587c25cb..d9490e7dcfe99fed3cc70560f6f25e61f4fc190f 100644 --- a/substrate/frame/migrations/src/lib.rs +++ b/substrate/frame/migrations/src/lib.rs @@ -70,21 +70,26 @@ //! 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 @@ -156,11 +161,15 @@ use core::ops::ControlFlow; use frame_support::{ defensive, defensive_assert, migrations::*, + pallet_prelude::*, traits::Get, weights::{Weight, WeightMeter}, BoundedVec, }; -use frame_system::{pallet_prelude::BlockNumberFor, Pallet as System}; +use frame_system::{ + pallet_prelude::{BlockNumberFor, *}, + Pallet as System, +}; use sp_runtime::Saturating; /// Points to the next migration to execute. @@ -262,6 +271,7 @@ pub type IdentifierOf = BoundedVec::IdentifierMaxLen>; pub type ActiveCursorOf = ActiveCursor, BlockNumberFor>; /// Trait for a tuple of No-OP migrations with one element. +#[impl_trait_for_tuples::impl_for_tuples(30)] pub trait MockedMigrations: SteppedMigrations { /// The migration should fail after `n` steps. fn set_fail_after(n: u32); @@ -269,11 +279,24 @@ pub trait MockedMigrations: SteppedMigrations { fn set_success_after(n: u32); } +#[cfg(feature = "try-runtime")] +/// Wrapper for pre-upgrade bytes, allowing us to impl MEL on it. +/// +/// For `try-runtime` testing only. +#[derive(Debug, Clone, Eq, PartialEq, Encode, Decode, scale_info::TypeInfo, Default)] +struct PreUpgradeBytesWrapper(pub Vec); + +/// Data stored by the pre-upgrade hook of the MBMs. Only used for `try-runtime` testing. +/// +/// Define this outside of the pallet so it is not confused with actual storage. +#[cfg(feature = "try-runtime")] +#[frame_support::storage_alias] +type PreUpgradeBytes = + StorageMap, Twox64Concat, IdentifierOf, PreUpgradeBytesWrapper, ValueQuery>; + #[frame_support::pallet] pub mod pallet { use super::*; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; #[pallet::pallet] pub struct Pallet(_); @@ -678,7 +701,7 @@ impl Pallet { return Some(ControlFlow::Break(cursor)) } - let Some(id) = T::Migrations::nth_id(cursor.index) else { + if cursor.index >= T::Migrations::len() { // No more migrations in the tuple - we are done. defensive_assert!(cursor.index == T::Migrations::len(), "Inconsistent MBMs tuple"); Self::deposit_event(Event::UpgradeCompleted); @@ -687,8 +710,9 @@ impl Pallet { return None; }; - let Ok(bounded_id): Result, _> = id.try_into() else { - defensive!("integrity_test ensures that all identifiers' MEL bounds fit into CursorMaxLen; qed."); + let id = T::Migrations::nth_id(cursor.index).map(TryInto::try_into); + let Some(Ok(bounded_id)): Option, _>> = id else { + defensive!("integrity_test ensures that all identifiers are present and bounde; qed."); Self::upgrade_failed(Some(cursor.index)); return None }; @@ -700,6 +724,16 @@ impl Pallet { } let max_steps = T::Migrations::nth_max_steps(cursor.index); + + // If this is the first time running this migration, exec the pre-upgrade hook. + #[cfg(feature = "try-runtime")] + if !PreUpgradeBytes::::contains_key(&bounded_id) { + let bytes = T::Migrations::nth_pre_upgrade(cursor.index) + .expect("Invalid cursor.index") + .expect("Pre-upgrade failed"); + PreUpgradeBytes::::insert(&bounded_id, PreUpgradeBytesWrapper(bytes)); + } + let next_cursor = T::Migrations::nth_transactional_step( cursor.index, cursor.inner_cursor.clone().map(|c| c.into_inner()), @@ -734,6 +768,16 @@ impl Pallet { }, Ok(None) => { // A migration is done when it returns cursor `None`. + + // Run post-upgrade checks. + #[cfg(feature = "try-runtime")] + T::Migrations::nth_post_upgrade( + cursor.index, + PreUpgradeBytes::::get(&bounded_id).0, + ) + .expect("Invalid cursor.index.") + .expect("Post-upgrade failed."); + Self::deposit_event(Event::MigrationCompleted { index: cursor.index, took }); Historic::::insert(&bounded_id, ()); cursor.goto_next_migration(System::::block_number()); @@ -758,14 +802,21 @@ impl Pallet { } /// Fail the current runtime upgrade, caused by `migration`. + /// + /// When the `try-runtime` feature is enabled, this function will panic. + // Allow unreachable code so it can compile without warnings when `try-runtime` is enabled. 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 => {}, + if cfg!(feature = "try-runtime") { + panic!("Migration with index {:?} failed.", migration); + } else { + match T::FailedMigrationHandler::failed(migration) { + KeepStuck => Cursor::::set(Some(MigrationCursor::Stuck)), + ForceUnstuck => Cursor::::kill(), + Ignore => {}, + } } } diff --git a/substrate/frame/migrations/src/mock_helpers.rs b/substrate/frame/migrations/src/mock_helpers.rs index 9d3b4d1193f21e8a0fd35463d161cc4df978146a..a03c70051d308d7882e8dca68bc27fcd4636f510 100644 --- a/substrate/frame/migrations/src/mock_helpers.rs +++ b/substrate/frame/migrations/src/mock_helpers.rs @@ -43,6 +43,12 @@ pub enum MockedMigrationKind { /// Cause an [`SteppedMigrationError::InsufficientWeight`] error after its number of steps /// elapsed. HighWeightAfter(Weight), + /// PreUpgrade should fail. + #[cfg(feature = "try-runtime")] + PreUpgradeFail, + /// PostUpgrade should fail. + #[cfg(feature = "try-runtime")] + PostUpgradeFail, } use MockedMigrationKind::*; // C style @@ -99,6 +105,8 @@ impl SteppedMigrations for MockedMigrations { Err(SteppedMigrationError::Failed) }, TimeoutAfter => unreachable!(), + #[cfg(feature = "try-runtime")] + PreUpgradeFail | PostUpgradeFail => Ok(None), }) } @@ -115,6 +123,31 @@ impl SteppedMigrations for MockedMigrations { MIGRATIONS::get().get(n as usize).map(|(_, s)| Some(*s)) } + #[cfg(feature = "try-runtime")] + fn nth_pre_upgrade(n: u32) -> Option, sp_runtime::TryRuntimeError>> { + let (kind, _) = MIGRATIONS::get()[n as usize]; + + if let PreUpgradeFail = kind { + return Some(Err("Some pre-upgrade error".into())) + } + + Some(Ok(vec![])) + } + + #[cfg(feature = "try-runtime")] + fn nth_post_upgrade( + n: u32, + _state: Vec, + ) -> Option> { + let (kind, _) = MIGRATIONS::get()[n as usize]; + + if let PostUpgradeFail = kind { + return Some(Err("Some post-upgrade error".into())) + } + + Some(Ok(())) + } + fn cursor_max_encoded_len() -> usize { 65_536 } diff --git a/substrate/frame/migrations/src/tests.rs b/substrate/frame/migrations/src/tests.rs index 9c9043d37a62e5f1df3d12da979b68251621c9d0..55f212bcf373cfb921a96f3b44a95d2f2b7f1611 100644 --- a/substrate/frame/migrations/src/tests.rs +++ b/substrate/frame/migrations/src/tests.rs @@ -17,16 +17,42 @@ #![cfg(test)] +use frame_support::{pallet_prelude::Weight, traits::OnRuntimeUpgrade}; + 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, 2)]); + + System::set_block_number(1); + Migrations::on_runtime_upgrade(); + run_to_block(10); + + // Check that the executed migrations are recorded in `Historical`. + assert_eq!(historic(), vec![mocked_id(SucceedAfter, 2),]); + + // Check that we got all events. + assert_events(vec![ + UpgradeStarted { migrations: 1 }, + MigrationAdvanced { index: 0, took: 1 }, + MigrationAdvanced { index: 0, took: 2 }, + MigrationCompleted { index: 0, took: 3 }, + UpgradeCompleted, + ]); + }); +} + +#[test] +fn simple_multiple_works() { use Event::*; test_closure(|| { // Add three migrations, each taking one block longer than the previous. @@ -61,6 +87,7 @@ fn simple_works() { } #[test] +#[cfg_attr(feature = "try-runtime", should_panic)] fn failing_migration_sets_cursor_to_stuck() { test_closure(|| { FailedUpgradeResponse::set(FailedMigrationHandling::KeepStuck); @@ -91,6 +118,7 @@ fn failing_migration_sets_cursor_to_stuck() { } #[test] +#[cfg_attr(feature = "try-runtime", should_panic)] fn failing_migration_force_unstuck_works() { test_closure(|| { FailedUpgradeResponse::set(FailedMigrationHandling::ForceUnstuck); @@ -123,6 +151,7 @@ fn failing_migration_force_unstuck_works() { /// A migration that reports not getting enough weight errors if it is the first one to run in that /// block. #[test] +#[cfg_attr(feature = "try-runtime", should_panic)] fn high_weight_migration_singular_fails() { test_closure(|| { MockedMigrations::set(vec![(HighWeightAfter(Weight::zero()), 2)]); @@ -151,6 +180,7 @@ fn high_weight_migration_singular_fails() { /// 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] +#[cfg_attr(feature = "try-runtime", should_panic)] fn high_weight_migration_retries_once() { test_closure(|| { MockedMigrations::set(vec![(SucceedAfter, 0), (HighWeightAfter(Weight::zero()), 0)]); @@ -180,6 +210,7 @@ fn high_weight_migration_retries_once() { // Note: Same as `high_weight_migration_retries_once` but with different required weight for the // migration. #[test] +#[cfg_attr(feature = "try-runtime", should_panic)] fn high_weight_migration_permanently_overweight_fails() { test_closure(|| { MockedMigrations::set(vec![(SucceedAfter, 0), (HighWeightAfter(Weight::MAX), 0)]); @@ -275,6 +306,7 @@ fn historic_skipping_works() { /// When another upgrade happens while a migration is still running, it should set the cursor to /// stuck. #[test] +#[cfg_attr(feature = "try-runtime", should_panic)] fn upgrade_fails_when_migration_active() { test_closure(|| { MockedMigrations::set(vec![(SucceedAfter, 10)]); @@ -301,6 +333,7 @@ fn upgrade_fails_when_migration_active() { } #[test] +#[cfg_attr(feature = "try-runtime", should_panic)] fn migration_timeout_errors() { test_closure(|| { MockedMigrations::set(vec![(TimeoutAfter, 3)]); @@ -333,3 +366,91 @@ fn migration_timeout_errors() { assert_eq!(upgrades_started_completed_failed(), (0, 0, 1)); }); } + +#[cfg(feature = "try-runtime")] +#[test] +fn try_runtime_success_case() { + 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 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] +#[cfg(feature = "try-runtime")] +#[should_panic] +fn try_runtime_pre_upgrade_failure() { + test_closure(|| { + // Add three migrations, it should fail after the second one. + MockedMigrations::set(vec![(SucceedAfter, 0), (PreUpgradeFail, 1), (SucceedAfter, 2)]); + + System::set_block_number(1); + Migrations::on_runtime_upgrade(); + + // should panic + run_to_block(10); + }); +} + +#[test] +#[cfg(feature = "try-runtime")] +#[should_panic] +fn try_runtime_post_upgrade_failure() { + test_closure(|| { + // Add three migrations, it should fail after the second one. + MockedMigrations::set(vec![(SucceedAfter, 0), (PostUpgradeFail, 1), (SucceedAfter, 2)]); + + System::set_block_number(1); + Migrations::on_runtime_upgrade(); + + // should panic + run_to_block(10); + }); +} + +#[test] +#[cfg(feature = "try-runtime")] +#[should_panic] +fn try_runtime_migration_failure() { + test_closure(|| { + // Add three migrations, it should fail after the second one. + MockedMigrations::set(vec![(SucceedAfter, 0), (FailAfter, 5), (SucceedAfter, 10)]); + + System::set_block_number(1); + Migrations::on_runtime_upgrade(); + + // should panic + run_to_block(10); + }); +} + +#[test] +fn try_runtime_no_migrations() { + test_closure(|| { + MockedMigrations::set(vec![]); + + System::set_block_number(1); + Migrations::on_runtime_upgrade(); + + run_to_block(10); + + assert_eq!(System::events().len(), 0); + }); +} diff --git a/substrate/frame/nis/src/lib.rs b/substrate/frame/nis/src/lib.rs index 016daa4cb78be7adc714d47132a7d335741a7530..87e2276e768d0fcb5370b189882823d0604898a2 100644 --- a/substrate/frame/nis/src/lib.rs +++ b/substrate/frame/nis/src/lib.rs @@ -756,15 +756,13 @@ pub mod pallet { .map(|_| ()) // We ignore this error as it just means the amount we're trying to deposit is // dust and the beneficiary account doesn't exist. - .or_else( - |e| { - if e == TokenError::CannotCreate.into() { - Ok(()) - } else { - Err(e) - } - }, - )?; + .or_else(|e| { + if e == TokenError::CannotCreate.into() { + Ok(()) + } else { + Err(e) + } + })?; summary.receipts_on_hold.saturating_reduce(on_hold); } T::Currency::release(&HoldReason::NftReceipt.into(), &who, amount, Exact)?; diff --git a/substrate/frame/nis/src/mock.rs b/substrate/frame/nis/src/mock.rs index f3320a306df738e26cedfff8d57affd2076bee4d..2b008f8ec2a41e3580e61d4bd1c28ba044368273 100644 --- a/substrate/frame/nis/src/mock.rs +++ b/substrate/frame/nis/src/mock.rs @@ -64,6 +64,7 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; + type DoneSlashHandler = (); } impl pallet_balances::Config for Test { @@ -84,6 +85,7 @@ impl pallet_balances::Config for Test { type MaxFreezes = (); type RuntimeHoldReason = (); type RuntimeFreezeReason = (); + type DoneSlashHandler = (); } parameter_types! { diff --git a/substrate/frame/nomination-pools/benchmarking/src/inner.rs b/substrate/frame/nomination-pools/benchmarking/src/inner.rs index 2a455942511123327d1c6cb2826f46d4afea77ba..b0c8f3655a50dd5694d1920629bca3ef63ab4802 100644 --- a/substrate/frame/nomination-pools/benchmarking/src/inner.rs +++ b/substrate/frame/nomination-pools/benchmarking/src/inner.rs @@ -41,7 +41,7 @@ use sp_runtime::{ traits::{Bounded, StaticLookup, Zero}, Perbill, }; -use sp_staking::EraIndex; +use sp_staking::{EraIndex, StakingUnchecked}; // `frame_benchmarking::benchmarks!` macro needs this use pallet_nomination_pools::Call; @@ -131,6 +131,8 @@ fn migrate_to_transfer_stake(pool_id: PoolId) { ) .expect("member should have enough balance to transfer"); }); + + pallet_staking::Pallet::::migrate_to_direct_staker(&pool_acc); } fn vote_to_balance( diff --git a/substrate/frame/nomination-pools/src/adapter.rs b/substrate/frame/nomination-pools/src/adapter.rs index 272b3b60612b1324b7ef393c232f0b465d4bada6..f125919dabfa6bce68dea67dd4fd60ed26ec0e3c 100644 --- a/substrate/frame/nomination-pools/src/adapter.rs +++ b/substrate/frame/nomination-pools/src/adapter.rs @@ -460,6 +460,6 @@ impl< #[cfg(feature = "runtime-benchmarks")] fn remove_as_agent(pool: Pool) { - Delegation::migrate_to_direct_staker(pool.into()) + Delegation::force_kill_agent(pool.into()) } } diff --git a/substrate/frame/offences/benchmarking/src/inner.rs b/substrate/frame/offences/benchmarking/src/inner.rs index b16e5be653d1e7354ebe29e62304b1c6d065e80c..573114de074224db6e0e8d562d069852ca066092 100644 --- a/substrate/frame/offences/benchmarking/src/inner.rs +++ b/substrate/frame/offences/benchmarking/src/inner.rs @@ -20,7 +20,7 @@ use alloc::{vec, vec::Vec}; use frame_benchmarking::v1::{account, benchmarks}; -use frame_support::traits::{Currency, Get}; +use frame_support::traits::Get; use frame_system::{Config as SystemConfig, Pallet as System, RawOrigin}; use sp_runtime::{ @@ -77,8 +77,7 @@ where } type LookupSourceOf = <::Lookup as StaticLookup>::Source; -type BalanceOf = - <::Currency as Currency<::AccountId>>::Balance; +type BalanceOf = ::CurrencyBalance; struct Offender { pub controller: T::AccountId, @@ -89,7 +88,7 @@ struct Offender { } fn bond_amount() -> BalanceOf { - T::Currency::minimum_balance().saturating_mul(10_000u32.into()) + pallet_staking::asset::existential_deposit::().saturating_mul(10_000u32.into()) } fn create_offender(n: u32, nominators: u32) -> Result, &'static str> { @@ -99,7 +98,7 @@ fn create_offender(n: u32, nominators: u32) -> Result, &' let amount = bond_amount::(); // add twice as much balance to prevent the account from being killed. let free_amount = amount.saturating_mul(2u32.into()); - T::Currency::make_free_balance_be(&stash, free_amount); + pallet_staking::asset::set_stakeable_balance::(&stash, free_amount); Staking::::bond( RawOrigin::Signed(stash.clone()).into(), amount, @@ -116,7 +115,7 @@ fn create_offender(n: u32, nominators: u32) -> Result, &' for i in 0..nominators { let nominator_stash: T::AccountId = account("nominator stash", n * MAX_NOMINATORS + i, SEED); - T::Currency::make_free_balance_be(&nominator_stash, free_amount); + pallet_staking::asset::set_stakeable_balance::(&nominator_stash, free_amount); Staking::::bond( RawOrigin::Signed(nominator_stash.clone()).into(), @@ -172,6 +171,14 @@ fn make_offenders( } benchmarks! { + where_clause { + where + ::RuntimeEvent: TryInto>, + ::RuntimeEvent: TryInto>, + ::RuntimeEvent: TryInto, + ::RuntimeEvent: TryInto>, + } + report_offence_grandpa { let n in 0 .. MAX_NOMINATORS.min(MaxNominationsOf::::get()); @@ -196,17 +203,19 @@ benchmarks! { let _ = Offences::::report_offence(reporters, offence); } verify { - // make sure that all slashes have been applied #[cfg(test)] - assert_eq!( - System::::event_count(), 0 - + 1 // offence - + 3 // reporter (reward + endowment) - + 1 // offenders reported - + 3 // offenders slashed - + 1 // offenders chilled - + 3 * n // nominators slashed - ); + { + // make sure that all slashes have been applied + // (n nominators + one validator) * (slashed + unlocked) + deposit to reporter + reporter + // account endowed + some funds rescinded from issuance. + assert_eq!(System::::read_events_for_pallet::>().len(), 2 * (n + 1) as usize + 3); + // (n nominators + one validator) * slashed + Slash Reported + assert_eq!(System::::read_events_for_pallet::>().len(), 1 * (n + 1) as usize + 1); + // offence + assert_eq!(System::::read_events_for_pallet::().len(), 1); + // reporter new account + assert_eq!(System::::read_events_for_pallet::>().len(), 1); + } } report_offence_babe { @@ -233,17 +242,19 @@ benchmarks! { let _ = Offences::::report_offence(reporters, offence); } verify { - // make sure that all slashes have been applied #[cfg(test)] - assert_eq!( - System::::event_count(), 0 - + 1 // offence - + 3 // reporter (reward + endowment) - + 1 // offenders reported - + 3 // offenders slashed - + 1 // offenders chilled - + 3 * n // nominators slashed - ); + { + // make sure that all slashes have been applied + // (n nominators + one validator) * (slashed + unlocked) + deposit to reporter + reporter + // account endowed + some funds rescinded from issuance. + assert_eq!(System::::read_events_for_pallet::>().len(), 2 * (n + 1) as usize + 3); + // (n nominators + one validator) * slashed + Slash Reported + assert_eq!(System::::read_events_for_pallet::>().len(), 1 * (n + 1) as usize + 1); + // offence + assert_eq!(System::::read_events_for_pallet::().len(), 1); + // reporter new account + assert_eq!(System::::read_events_for_pallet::>().len(), 1); + } } impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test); diff --git a/substrate/frame/referenda/src/mock.rs b/substrate/frame/referenda/src/mock.rs index bf0fa4e1a12e17a90f2d35fd555d5805765b26be..c96a50af86581978e2e6ffe359a2417e4796665b 100644 --- a/substrate/frame/referenda/src/mock.rs +++ b/substrate/frame/referenda/src/mock.rs @@ -24,7 +24,6 @@ use frame_support::{ assert_ok, derive_impl, ord_parameter_types, parameter_types, traits::{ ConstU32, ConstU64, Contains, EqualPrivilegeOnly, OnInitialize, OriginTrait, Polling, - SortedMembers, }, weights::Weight, }; @@ -98,14 +97,6 @@ ord_parameter_types! { pub const Five: u64 = 5; pub const Six: u64 = 6; } -pub struct OneToFive; -impl SortedMembers for OneToFive { - fn sorted_members() -> Vec { - vec![1, 2, 3, 4, 5] - } - #[cfg(feature = "runtime-benchmarks")] - fn add(_m: &u64) {} -} pub struct TestTracksInfo; impl TracksInfo for TestTracksInfo { diff --git a/substrate/frame/referenda/src/types.rs b/substrate/frame/referenda/src/types.rs index 1039b288b2aecde8db69453f7883f41be349314a..e83f28b472cda7fa3dc6329fd026440ec415874d 100644 --- a/substrate/frame/referenda/src/types.rs +++ b/substrate/frame/referenda/src/types.rs @@ -258,7 +258,8 @@ impl< Tally: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone, AccountId: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone, ScheduleAddress: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone, - > ReferendumInfo + > + ReferendumInfo { /// Take the Decision Deposit from `self`, if there is one. Returns an `Err` if `self` is not /// in a valid state for the Decision Deposit to be refunded. diff --git a/substrate/frame/revive/Cargo.toml b/substrate/frame/revive/Cargo.toml index 747c2283e21747845d3ab363bca09b73cebf6623..e896d9e8fa26b636f7177d08a6c4a083dddcc86d 100644 --- a/substrate/frame/revive/Cargo.toml +++ b/substrate/frame/revive/Cargo.toml @@ -3,7 +3,6 @@ name = "pallet-revive" version = "0.1.0" authors.workspace = true edition.workspace = true -build = "build.rs" license = "Apache-2.0" homepage.workspace = true repository.workspace = true @@ -18,8 +17,10 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] +environmental = { workspace = true } paste = { workspace = true } -polkavm = { version = "0.10.0", default-features = false } +polkavm = { version = "0.12.0", default-features = false } +polkavm-common = { version = "0.12.0", default-features = false } bitflags = { workspace = true } codec = { features = [ "derive", @@ -29,9 +30,9 @@ scale-info = { features = ["derive"], workspace = true } log = { workspace = true } serde = { optional = true, features = ["derive"], workspace = true, default-features = true } impl-trait-for-tuples = { workspace = true } +rlp = { workspace = true } -# Substrate Dependencies -environmental = { workspace = true } +# Polkadot SDK Dependencies frame-benchmarking = { optional = true, workspace = true } frame-support = { workspace = true } frame-system = { workspace = true } @@ -54,10 +55,7 @@ pretty_assertions = { workspace = true } wat = { workspace = true } pallet-revive-fixtures = { workspace = true, default-features = true } -# Polkadot Dependencies -xcm-builder = { workspace = true, default-features = true } - -# Substrate Dependencies +# Polkadot SDK Dependencies pallet-balances = { workspace = true, default-features = true } pallet-timestamp = { workspace = true, default-features = true } pallet-message-queue = { workspace = true, default-features = true } @@ -66,6 +64,7 @@ pallet-assets = { workspace = true, default-features = true } pallet-proxy = { workspace = true, default-features = true } sp-keystore = { workspace = true, default-features = true } sp-tracing = { workspace = true, default-features = true } +xcm-builder = { workspace = true, default-features = true } [features] default = ["std"] @@ -85,7 +84,9 @@ std = [ "pallet-revive-fixtures/std", "pallet-timestamp/std", "pallet-utility/std", + "polkavm-common/std", "polkavm/std", + "rlp/std", "scale-info/std", "serde", "sp-api/std", diff --git a/substrate/frame/revive/build.rs b/substrate/frame/revive/build.rs deleted file mode 100644 index ca8e62df6047ce2038d648527e3782c88d8dae89..0000000000000000000000000000000000000000 --- a/substrate/frame/revive/build.rs +++ /dev/null @@ -1,78 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::io::Write; - -/// We start with version 2 instead of 0 when adding the pallet. -/// -/// Because otherwise we can't test any migrations since they require the storage version -/// to be lower than the pallet version in order to be triggerd. With the pallet version -/// at the minimum (0) this would not work. -const LOWEST_STORAGE_VERSION: u16 = 2; - -/// Get the latest migration version. -/// -/// Find the highest version number from the available migration files. -/// Each migration file should follow the naming convention `vXX.rs`, where `XX` is the version -/// number. -fn get_latest_version() -> u16 { - let Ok(dir) = std::fs::read_dir("src/migration") else { return LOWEST_STORAGE_VERSION }; - dir.filter_map(|entry| { - let file_name = entry.as_ref().ok()?.file_name(); - let file_name = file_name.to_str()?; - if file_name.starts_with('v') && file_name.ends_with(".rs") { - let version = &file_name[1..&file_name.len() - 3]; - let version = version.parse::().ok()?; - - // Ensure that the version matches the one defined in the file. - let path = entry.unwrap().path(); - let file_content = std::fs::read_to_string(&path).ok()?; - assert!( - file_content.contains(&format!("const VERSION: u16 = {}", version)), - "Invalid MigrationStep::VERSION in {:?}", - path - ); - - return Some(version) - } - None - }) - .max() - .unwrap_or(LOWEST_STORAGE_VERSION) -} - -/// Generates a module that exposes the latest migration version, and the benchmark migrations type. -fn main() -> Result<(), Box> { - let out_dir = std::env::var("OUT_DIR")?; - let path = std::path::Path::new(&out_dir).join("migration_codegen.rs"); - let mut f = std::fs::File::create(path)?; - let version = get_latest_version(); - write!( - f, - " - pub mod codegen {{ - use crate::NoopMigration; - /// The latest migration version, pulled from the latest migration file. - pub const LATEST_MIGRATION_VERSION: u16 = {version}; - /// The Migration Steps used for benchmarking the migration framework. - pub type BenchMigrations = (NoopMigration<{}>, NoopMigration<{version}>); - }}", - version - 1, - )?; - - Ok(()) -} diff --git a/substrate/frame/revive/fixtures/Cargo.toml b/substrate/frame/revive/fixtures/Cargo.toml index 9e54acdace709103be12905e39283e5e776e9b29..75b23fdd44d1f65adaaccedb38064378db35f1f4 100644 --- a/substrate/frame/revive/fixtures/Cargo.toml +++ b/substrate/frame/revive/fixtures/Cargo.toml @@ -1,6 +1,5 @@ [package] name = "pallet-revive-fixtures" -publish = true version = "0.1.0" authors.workspace = true edition.workspace = true @@ -12,15 +11,18 @@ workspace = true [dependencies] frame-system = { workspace = true, default-features = true, optional = true } +sp-core = { workspace = true, default-features = true, optional = true } +sp-io = { workspace = true, default-features = true, optional = true } sp-runtime = { workspace = true, default-features = true, optional = true } -anyhow = { workspace = true } +anyhow = { workspace = true, default-features = true, optional = true } +log = { workspace = true } [build-dependencies] parity-wasm = { workspace = true } tempfile = { workspace = true } toml = { workspace = true } -polkavm-linker = { version = "0.10.0" } -anyhow = { workspace = true } +polkavm-linker = { version = "0.12.0" } +anyhow = { workspace = true, default-features = true } [features] default = ["std"] @@ -30,7 +32,10 @@ default = ["std"] riscv = [] # only when std is enabled all fixtures are available std = [ - "anyhow/std", + "anyhow", "frame-system", + "log/std", + "sp-core", + "sp-io", "sp-runtime", ] diff --git a/substrate/frame/revive/fixtures/build.rs b/substrate/frame/revive/fixtures/build.rs index ed981a75467b74e4622f889c2d0d16315c2f3f3c..3178baf6bbe425fac7867354cfb03224d094a5b0 100644 --- a/substrate/frame/revive/fixtures/build.rs +++ b/substrate/frame/revive/fixtures/build.rs @@ -115,6 +115,7 @@ mod build { fn invoke_build(current_dir: &Path) -> Result<()> { let encoded_rustflags = [ + "-Dwarnings", "-Crelocation-model=pie", "-Clink-arg=--emit-relocs", "-Clink-arg=--export-dynamic-symbol=__polkavm_symbol_export_hack__*", @@ -158,7 +159,8 @@ mod build { /// Post-process the compiled code. fn post_process(input_path: &Path, output_path: &Path) -> Result<()> { let mut config = polkavm_linker::Config::default(); - config.set_strip(true); + config.set_strip(false); + config.set_optimize(true); let orig = fs::read(input_path).with_context(|| format!("Failed to read {:?}", input_path))?; let linked = polkavm_linker::program_from_elf(config, orig.as_ref()) diff --git a/substrate/frame/revive/fixtures/build/Cargo.toml b/substrate/frame/revive/fixtures/build/Cargo.toml index 7dead51b23060846a63563ad7161f7bf5b212a12..948d7438cf98ee93c463214e95711be2a6a856c8 100644 --- a/substrate/frame/revive/fixtures/build/Cargo.toml +++ b/substrate/frame/revive/fixtures/build/Cargo.toml @@ -11,7 +11,7 @@ edition = "2021" [dependencies] uapi = { package = 'pallet-revive-uapi', path = "", default-features = false } common = { package = 'pallet-revive-fixtures-common', path = "" } -polkavm-derive = { version = "0.10.0" } +polkavm-derive = { version = "0.12.0" } [profile.release] opt-level = 3 diff --git a/substrate/frame/revive/fixtures/contracts/balance.rs b/substrate/frame/revive/fixtures/contracts/balance.rs index 4011b8379cbfa25b4f12289f9f28b9103af08e8c..4606135d9807acf0e5979ad78c6b0ece7a678077 100644 --- a/substrate/frame/revive/fixtures/contracts/balance.rs +++ b/substrate/frame/revive/fixtures/contracts/balance.rs @@ -18,7 +18,7 @@ #![no_std] #![no_main] -use common::output; +use common::u64_output; use uapi::{HostFn, HostFnImpl as api}; #[no_mangle] @@ -28,9 +28,6 @@ pub extern "C" fn deploy() {} #[no_mangle] #[polkavm_derive::polkavm_export] pub extern "C" fn call() { - // Initialize buffer with 1s so that we can check that it is overwritten. - output!(balance, [1u8; 8], api::balance,); - - // Assert that the balance is 0. - assert_eq!(&[0u8; 8], balance); + let balance = u64_output!(api::balance,); + assert_eq!(balance, 0); } diff --git a/substrate/frame/revive/fixtures/contracts/balance_of.rs b/substrate/frame/revive/fixtures/contracts/balance_of.rs new file mode 100644 index 0000000000000000000000000000000000000000..1f94d3e506c9380b5cc50494f8f976d82398bc55 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/balance_of.rs @@ -0,0 +1,36 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::{input, u64_output}; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(address: &[u8; 20],); + + let reported_free_balance = u64_output!(api::balance_of, address); + + assert_ne!(reported_free_balance, 0); +} diff --git a/substrate/frame/revive/fixtures/contracts/basic_block.rs b/substrate/frame/revive/fixtures/contracts/basic_block.rs new file mode 100644 index 0000000000000000000000000000000000000000..0cde7a264632a47f0a29b7ffd2d8d0ca60b71ecb --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/basic_block.rs @@ -0,0 +1,47 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Create a basic block that is larger than we allow. + +#![no_std] +#![no_main] + +extern crate common; + +use core::arch::asm; + +// Export that is never called. We can put code here that should be in the binary +// but is never supposed to be run. +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call_never() { + // Stores cannot be optimized away because the optimizer cannot + // know whether they have side effects. + let value: u32 = 42; + unsafe { + // Repeat 1001 times to intentionally exceed the allowed basic block limit (1000) + asm!(".rept 1001", "sw {x}, 0(sp)", ".endr", x = in(reg) value); + } +} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() {} diff --git a/substrate/frame/revive/fixtures/contracts/call.rs b/substrate/frame/revive/fixtures/contracts/call.rs index a75aee65c20525f19ed386d67c7dee020de2a66b..ee51548879d9de9479d5d9f1c695f6c6d4bc5889 100644 --- a/substrate/frame/revive/fixtures/contracts/call.rs +++ b/substrate/frame/revive/fixtures/contracts/call.rs @@ -31,17 +31,17 @@ pub extern "C" fn deploy() {} pub extern "C" fn call() { input!( callee_input: [u8; 4], - callee_addr: [u8; 32], + callee_addr: &[u8; 20], ); // Call the callee api::call( uapi::CallFlags::empty(), callee_addr, - 0u64, // How much ref_time to devote for the execution. 0 = all. - 0u64, // How much proof_size to devote for the execution. 0 = all. - None, // No deposit limit. - &0u64.to_le_bytes(), // Value transferred to the contract. + 0u64, // How much ref_time to devote for the execution. 0 = all. + 0u64, // How much proof_size to devote for the execution. 0 = all. + None, // No deposit limit. + &[0u8; 32], // Value transferred to the contract. callee_input, None, ) diff --git a/substrate/frame/revive/fixtures/contracts/call_diverging_out_len.rs b/substrate/frame/revive/fixtures/contracts/call_diverging_out_len.rs new file mode 100644 index 0000000000000000000000000000000000000000..129adde2cec915adbf2f3327ab6b536466b607e9 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/call_diverging_out_len.rs @@ -0,0 +1,111 @@ +// 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 tests that the correct output data is written when the provided +//! output buffer length is smaller than what was actually returned during +//! calls and instantiations. +//! +//! To not need an additional callee fixture, we call ourself recursively +//! and also instantiate our own code hash (constructor and recursive calls +//! always return `BUF_SIZE` bytes of data). + +#![no_std] +#![no_main] + +extern crate common; + +use uapi::{HostFn, HostFnImpl as api}; + +const BUF_SIZE: usize = 8; +static DATA: [u8; BUF_SIZE] = [1, 2, 3, 4, 5, 6, 7, 8]; + +/// Call `callee_address` with an output buf of size `N` +/// and expect the call output to match `expected_output`. +fn assert_call(callee_address: &[u8; 20], expected_output: [u8; BUF_SIZE]) { + let mut output_buf = [0u8; BUF_SIZE]; + let output_buf_capped = &mut &mut output_buf[..N]; + + api::call( + uapi::CallFlags::ALLOW_REENTRY, + callee_address, + 0u64, + 0u64, + None, + &[0u8; 32], + &[], + Some(output_buf_capped), + ) + .unwrap(); + + // The (capped) output buf should get properly resized + assert_eq!(output_buf_capped.len(), N); + assert_eq!(output_buf, expected_output); +} + +/// Instantiate this contract with an output buf of size `N` +/// and expect the instantiate output to match `expected_output`. +fn assert_instantiate(expected_output: [u8; BUF_SIZE]) { + let mut code_hash = [0; 32]; + api::own_code_hash(&mut code_hash); + + let mut output_buf = [0u8; BUF_SIZE]; + let output_buf_capped = &mut &mut output_buf[..N]; + + api::instantiate( + &code_hash, + 0u64, + 0u64, + None, + &[0; 32], + &[0; 32], + None, + Some(output_buf_capped), + None, + ) + .unwrap(); + + // The (capped) output buf should get properly resized + assert_eq!(output_buf_capped.len(), N); + assert_eq!(output_buf, expected_output); +} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() { + api::return_value(uapi::ReturnFlags::empty(), &DATA); +} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + let mut caller_address = [0u8; 20]; + api::caller(&mut caller_address); + + let mut callee_address = [0u8; 20]; + api::address(&mut callee_address); + + // we already recurse; return data + if caller_address == callee_address { + api::return_value(uapi::ReturnFlags::empty(), &DATA); + } + + assert_call::<0>(&callee_address, [0; 8]); + assert_call::<4>(&callee_address, [1, 2, 3, 4, 0, 0, 0, 0]); + + assert_instantiate::<0>([0; 8]); + assert_instantiate::<4>([1, 2, 3, 4, 0, 0, 0, 0]); +} diff --git a/substrate/frame/revive/fixtures/contracts/call_return_code.rs b/substrate/frame/revive/fixtures/contracts/call_return_code.rs index 32654d59ef1f5e6e4aeb5ac7370288d0edff3397..2d13b9f7095638a29f523d15b7d1c94dd2012946 100644 --- a/substrate/frame/revive/fixtures/contracts/call_return_code.rs +++ b/substrate/frame/revive/fixtures/contracts/call_return_code.rs @@ -33,7 +33,8 @@ pub extern "C" fn deploy() {} pub extern "C" fn call() { input!( 100, - callee_addr: [u8; 32], + callee_addr: &[u8; 20], + value: &[u8; 32], input: [u8], ); @@ -41,10 +42,10 @@ pub extern "C" fn call() { let err_code = match api::call( uapi::CallFlags::empty(), callee_addr, - 0u64, // How much ref_time to devote for the execution. 0 = all. - 0u64, // How much proof_size to devote for the execution. 0 = all. - None, // No deposit limit. - &100u64.to_le_bytes(), // Value transferred to the contract. + 0u64, // How much ref_time to devote for the execution. 0 = all. + 0u64, // How much proof_size to devote for the execution. 0 = all. + None, // No deposit limit. + value, // Value transferred to the contract. input, None, ) { diff --git a/substrate/frame/revive/fixtures/contracts/call_runtime_and_call.rs b/substrate/frame/revive/fixtures/contracts/call_runtime_and_call.rs index 1323c8c5d55db4e71978e1bb7e2586f7cb739c96..8c8aee9628498e4cbc67e8e9f304106c717a486f 100644 --- a/substrate/frame/revive/fixtures/contracts/call_runtime_and_call.rs +++ b/substrate/frame/revive/fixtures/contracts/call_runtime_and_call.rs @@ -31,7 +31,7 @@ pub extern "C" fn call() { input!( 512, callee_input: [u8; 4], - callee_addr: [u8; 32], + callee_addr: &[u8; 20], call: [u8], ); @@ -42,10 +42,10 @@ pub extern "C" fn call() { api::call( uapi::CallFlags::empty(), callee_addr, - 0u64, // How much ref_time to devote for the execution. 0 = all. - 0u64, // How much proof_size to devote for the execution. 0 = all. - None, // No deposit limit. - &0u64.to_le_bytes(), // Value transferred to the contract. + 0u64, // How much ref_time to devote for the execution. 0 = all. + 0u64, // How much proof_size to devote for the execution. 0 = all. + None, // No deposit limit. + &[0u8; 32], // Value transferred to the contract. callee_input, None, ) diff --git a/substrate/frame/revive/fixtures/contracts/call_with_flags_and_value.rs b/substrate/frame/revive/fixtures/contracts/call_with_flags_and_value.rs index a078162d7995547e914a2964b827f7eaee574552..330393e706e98fea7f7e6b04816885f37ac21073 100644 --- a/substrate/frame/revive/fixtures/contracts/call_with_flags_and_value.rs +++ b/substrate/frame/revive/fixtures/contracts/call_with_flags_and_value.rs @@ -19,7 +19,7 @@ #![no_std] #![no_main] -use common::input; +use common::{input, u256_bytes}; use uapi::{HostFn, HostFnImpl as api}; #[no_mangle] @@ -31,7 +31,7 @@ pub extern "C" fn deploy() {} pub extern "C" fn call() { input!( 256, - callee_addr: [u8; 32], + callee_addr: &[u8; 20], flags: u32, value: u64, forwarded_input: [u8], @@ -40,10 +40,10 @@ pub extern "C" fn call() { api::call( uapi::CallFlags::from_bits(flags).unwrap(), callee_addr, - 0u64, // How much ref_time to devote for the execution. 0 = all. - 0u64, // How much proof_size to devote for the execution. 0 = all. - None, // No deposit limit. - &value.to_le_bytes(), // Value transferred to the contract. + 0u64, // How much ref_time to devote for the execution. 0 = all. + 0u64, // How much proof_size to devote for the execution. 0 = all. + None, // No deposit limit. + &u256_bytes(value), // Value transferred to the contract. forwarded_input, None, ) diff --git a/substrate/frame/revive/fixtures/contracts/call_with_limit.rs b/substrate/frame/revive/fixtures/contracts/call_with_limit.rs index a5356924f24f14926112de5562b1f3a96c7be638..6ab892a6b7ae8416d923215df0a13b9ee13faf0c 100644 --- a/substrate/frame/revive/fixtures/contracts/call_with_limit.rs +++ b/substrate/frame/revive/fixtures/contracts/call_with_limit.rs @@ -32,7 +32,7 @@ pub extern "C" fn deploy() {} pub extern "C" fn call() { input!( 256, - callee_addr: [u8; 32], + callee_addr: &[u8; 20], ref_time: u64, proof_size: u64, forwarded_input: [u8], @@ -43,8 +43,8 @@ pub extern "C" fn call() { callee_addr, ref_time, proof_size, - None, // No deposit limit. - &0u64.to_le_bytes(), // value transferred to the contract. + None, // No deposit limit. + &[0u8; 32], // value transferred to the contract. forwarded_input, None, ) diff --git a/substrate/frame/revive/fixtures/contracts/caller_contract.rs b/substrate/frame/revive/fixtures/contracts/caller_contract.rs index 2fa11df82d04a605e7020deda31e8ad6ba4045f9..f9a30b87df47e071ae808b4530caed8e4eebea55 100644 --- a/substrate/frame/revive/fixtures/contracts/caller_contract.rs +++ b/substrate/frame/revive/fixtures/contracts/caller_contract.rs @@ -18,7 +18,7 @@ #![no_std] #![no_main] -use common::input; +use common::{input, u256_bytes}; use uapi::{HostFn, HostFnImpl as api, ReturnErrorCode}; #[no_mangle] @@ -28,12 +28,12 @@ pub extern "C" fn deploy() {} #[no_mangle] #[polkavm_derive::polkavm_export] pub extern "C" fn call() { - input!(code_hash: [u8; 32],); + input!(code_hash: &[u8; 32],); // The value to transfer on instantiation and calls. Chosen to be greater than existential // deposit. - let value = 32768u64.to_le_bytes(); - let salt = [0u8; 0]; + let value = u256_bytes(32768u64); + let salt = [0u8; 32]; // Callee will use the first 4 bytes of the input to return an exit status. let input = [0u8, 1, 34, 51, 68, 85, 102, 119]; @@ -49,31 +49,40 @@ pub extern "C" fn call() { &reverted_input, None, None, - &salt, + Some(&salt), ); assert!(matches!(res, Err(ReturnErrorCode::CalleeReverted))); // Fail to deploy the contract due to insufficient ref_time weight. let res = api::instantiate( - code_hash, 1u64, // too little ref_time weight + code_hash, + 1u64, // too little ref_time weight 0u64, // How much proof_size weight to devote for the execution. 0 = all. None, // No deposit limit. - &value, &input, None, None, &salt, + &value, + &input, + None, + None, + Some(&salt), ); assert!(matches!(res, Err(ReturnErrorCode::CalleeTrapped))); // Fail to deploy the contract due to insufficient proof_size weight. let res = api::instantiate( - code_hash, 0u64, // How much ref_time weight to devote for the execution. 0 = all. + code_hash, + 0u64, // How much ref_time weight to devote for the execution. 0 = all. 1u64, // Too little proof_size weight None, // No deposit limit. - &value, &input, None, None, &salt, + &value, + &input, + None, + None, + Some(&salt), ); assert!(matches!(res, Err(ReturnErrorCode::CalleeTrapped))); // Deploy the contract successfully. - let mut callee = [0u8; 32]; - let callee = &mut &mut callee[..]; + let mut callee = [0u8; 20]; api::instantiate( code_hash, @@ -82,17 +91,16 @@ pub extern "C" fn call() { None, // No deposit limit. &value, &input, - Some(callee), + Some(&mut callee), None, - &salt, + Some(&salt), ) .unwrap(); - assert_eq!(callee.len(), 32); // Call the new contract and expect it to return failing exit code. let res = api::call( uapi::CallFlags::empty(), - callee, + &callee, 0u64, // How much ref_time weight to devote for the execution. 0 = all. 0u64, // How much proof_size weight to devote for the execution. 0 = all. None, // No deposit limit. @@ -105,7 +113,7 @@ pub extern "C" fn call() { // Fail to call the contract due to insufficient ref_time weight. let res = api::call( uapi::CallFlags::empty(), - callee, + &callee, 1u64, // Too little ref_time weight. 0u64, // How much proof_size weight to devote for the execution. 0 = all. None, // No deposit limit. @@ -118,7 +126,7 @@ pub extern "C" fn call() { // Fail to call the contract due to insufficient proof_size weight. let res = api::call( uapi::CallFlags::empty(), - callee, + &callee, 0u64, // How much ref_time weight to devote for the execution. 0 = all. 1u64, // too little proof_size weight None, // No deposit limit. @@ -132,7 +140,7 @@ pub extern "C" fn call() { let mut output = [0u8; 4]; api::call( uapi::CallFlags::empty(), - callee, + &callee, 0u64, // How much ref_time weight to devote for the execution. 0 = all. 0u64, // How much proof_size weight to devote for the execution. 0 = all. None, // No deposit limit. diff --git a/substrate/frame/revive/fixtures/contracts/chain_extension_temp_storage.rs b/substrate/frame/revive/fixtures/contracts/chain_extension_temp_storage.rs index c7596e44dab3546bb64ea6a7fe67cfa40b6a85d8..22d6c5b548d87acd2bffc17401f42a682d328dd7 100644 --- a/substrate/frame/revive/fixtures/contracts/chain_extension_temp_storage.rs +++ b/substrate/frame/revive/fixtures/contracts/chain_extension_temp_storage.rs @@ -20,7 +20,7 @@ #![no_std] #![no_main] -use common::{input, output}; +use common::input; use uapi::{HostFn, HostFnImpl as api}; #[no_mangle] @@ -47,16 +47,17 @@ pub extern "C" fn call() { input[8] = 1u8; // Read the contract address. - output!(addr, [0u8; 32], api::address,); + let mut addr = [0u8; 20]; + api::address(&mut addr); // call self api::call( uapi::CallFlags::ALLOW_REENTRY, - addr, - 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. + &addr, + 0u64, // How much ref_time to devote for the execution. 0 = all. + 0u64, // How much proof_size to devote for the execution. 0 = all. + None, // No deposit limit. + &[0u8; 32], // Value transferred to the contract. input, None, ) diff --git a/substrate/frame/revive/fixtures/contracts/chain_id.rs b/substrate/frame/revive/fixtures/contracts/chain_id.rs new file mode 100644 index 0000000000000000000000000000000000000000..ce7a0cc671ce6068df605e1dde4f06bc623e1b62 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/chain_id.rs @@ -0,0 +1,37 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +extern crate common; + +use uapi::{HostFn, HostFnImpl as api, ReturnFlags}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() { + call() +} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + let mut buf = [0; 32]; + api::chain_id(&mut buf); + api::return_value(ReturnFlags::empty(), &buf); +} diff --git a/substrate/frame/revive/fixtures/contracts/common/src/lib.rs b/substrate/frame/revive/fixtures/contracts/common/src/lib.rs index 6631af8292feac3f4659c2fa52783fc2690a1745..abfba282bec14254f76797f776bc83b0a7f5e315 100644 --- a/substrate/frame/revive/fixtures/contracts/common/src/lib.rs +++ b/substrate/frame/revive/fixtures/contracts/common/src/lib.rs @@ -100,11 +100,23 @@ macro_rules! input { input!(@inner $input, $cursor + $n, $($rest)*); }; + // Match an array reference of the given size. + // e.g input!(var1: &[u8; 32], ); + (@inner $input:expr, $cursor:expr, $var:ident: &[u8; $n:expr], $($rest:tt)*) => { + let $var: &[u8; $n] = &$input[$cursor..$cursor+$n].try_into().unwrap(); + input!(@inner $input, $cursor + $n, $($rest)*); + }; + // Size of a u8 slice. (@size $size:expr, $var:ident: [u8; $n:expr], $($rest:tt)*) => { input!(@size $size + $n, $($rest)*) }; + // Size of an array reference. + (@size $size:expr, $var:ident: &[u8; $n:expr], $($rest:tt)*) => { + input!(@size $size + $n, $($rest)*) + }; + // Entry point, with the buffer and it's size specified first. // e.g input!(buffer, 512, var1: u32, var2: [u8], ); ($buffer:ident, $size:expr, $($rest:tt)*) => { @@ -155,3 +167,30 @@ macro_rules! unwrap_output { $host_fn($($arg,)* $output).unwrap(); }; } + +/// Call the host function and convert the [u8; 32] output to u64. +#[macro_export] +macro_rules! u64_output { + ($host_fn:path, $($arg:expr),*) => {{ + let mut buffer = [1u8; 32]; + $host_fn($($arg,)* &mut buffer); + assert!(buffer[8..].iter().all(|&x| x == 0)); + u64::from_le_bytes(buffer[..8].try_into().unwrap()) + }}; +} + +/// Convert a u64 into a [u8; 32]. +pub const fn u256_bytes(value: u64) -> [u8; 32] { + let mut buffer = [0u8; 32]; + let bytes = value.to_le_bytes(); + + buffer[0] = bytes[0]; + buffer[1] = bytes[1]; + buffer[2] = bytes[2]; + buffer[3] = bytes[3]; + buffer[4] = bytes[4]; + buffer[5] = bytes[5]; + buffer[6] = bytes[6]; + buffer[7] = bytes[7]; + buffer +} diff --git a/substrate/frame/revive/fixtures/contracts/create1_with_value.rs b/substrate/frame/revive/fixtures/contracts/create1_with_value.rs new file mode 100644 index 0000000000000000000000000000000000000000..c6adab82886076484b57972c943d676c443d41e9 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/create1_with_value.rs @@ -0,0 +1,39 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(code_hash: &[u8; 32],); + + let mut value = [0; 32]; + api::value_transferred(&mut value); + + // Deploy the contract with no salt (equivalent to create1). + let ret = api::instantiate(code_hash, 0u64, 0u64, None, &value, &[], None, None, None); + assert!(ret.is_ok()); +} diff --git a/substrate/frame/revive/fixtures/contracts/create_storage_and_call.rs b/substrate/frame/revive/fixtures/contracts/create_storage_and_call.rs index d0d3651dfe4d70f899334bf86dc422b55d8e35f1..4fa2db0c8c1cf1a8758426622a5f53c46f4c9fd7 100644 --- a/substrate/frame/revive/fixtures/contracts/create_storage_and_call.rs +++ b/substrate/frame/revive/fixtures/contracts/create_storage_and_call.rs @@ -32,8 +32,8 @@ pub extern "C" fn call() { input!( buffer, input: [u8; 4], - callee: [u8; 32], - deposit_limit: [u8; 8], + callee: &[u8; 20], + deposit_limit: &[u8; 32], ); // create 4 byte of storage before calling @@ -46,7 +46,7 @@ pub extern "C" fn call() { 0u64, // How much ref_time weight to devote for the execution. 0 = all. 0u64, // How much proof_size weight to devote for the execution. 0 = all. Some(deposit_limit), - &0u64.to_le_bytes(), // Value transferred to the contract. + &[0u8; 32], // Value transferred to the contract. input, None, ) diff --git a/substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs b/substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs index 918a4abe6b21c6c243913ca9825b99e9b69765e6..463706457a15c8a558a3294002d0a018998c898b 100644 --- a/substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs +++ b/substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs @@ -19,7 +19,7 @@ #![no_std] #![no_main] -use common::input; +use common::{input, u256_bytes}; use uapi::{HostFn, HostFnImpl as api}; #[no_mangle] @@ -31,14 +31,13 @@ pub extern "C" fn deploy() {} pub extern "C" fn call() { input!( input: [u8; 4], - code_hash: [u8; 32], - deposit_limit: [u8; 8], + code_hash: &[u8; 32], + deposit_limit: &[u8; 32], ); - let value = 10_000u64.to_le_bytes(); - let salt = [0u8; 0]; - let mut address = [0u8; 32]; - let address = &mut &mut address[..]; + let value = u256_bytes(10_000u64); + let salt = [0u8; 32]; + let mut address = [0u8; 20]; api::instantiate( code_hash, @@ -47,12 +46,12 @@ pub extern "C" fn call() { Some(deposit_limit), &value, input, - Some(address), + Some(&mut address), None, - &salt, + Some(&salt), ) .unwrap(); // Return the deployed contract address. - api::return_value(uapi::ReturnFlags::empty(), address); + api::return_value(uapi::ReturnFlags::empty(), &address); } diff --git a/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs b/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs index a2a0e85bcf64f5409b509d32d4813b31659c4a55..d2efb26e5cebcc41febe22822c7a18036ae2b331 100644 --- a/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs +++ b/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs @@ -35,7 +35,7 @@ pub extern "C" fn call() { buffer, len: u32, input: [u8; 4], - callee: [u8; 32], + callee: &[u8; 20], ); let rounds = len as usize / BUFFER.len(); @@ -52,7 +52,7 @@ pub extern "C" fn call() { 0u64, // How much ref_time weight to devote for the execution. 0 = all. 0u64, // How much proof_size weight to devote for the execution. 0 = all. None, - &0u64.to_le_bytes(), // Value transferred to the contract. + &[0u8; 32], // Value transferred to the contract. input, None, ) diff --git a/substrate/frame/revive/fixtures/contracts/delegate_call.rs b/substrate/frame/revive/fixtures/contracts/delegate_call.rs index d03ddab1bc5881748a3ab44b70e2536e6e3c334b..9fd155408af3df0030a0c432df507f506aa9d655 100644 --- a/substrate/frame/revive/fixtures/contracts/delegate_call.rs +++ b/substrate/frame/revive/fixtures/contracts/delegate_call.rs @@ -28,7 +28,7 @@ pub extern "C" fn deploy() {} #[no_mangle] #[polkavm_derive::polkavm_export] pub extern "C" fn call() { - input!(code_hash: [u8; 32],); + input!(code_hash: &[u8; 32],); let mut key = [0u8; 32]; key[0] = 1u8; diff --git a/substrate/frame/revive/fixtures/contracts/delegate_call_lib.rs b/substrate/frame/revive/fixtures/contracts/delegate_call_lib.rs index 055760729bd250982ac682e16c550171c0381c0d..95c1bd2aa6cd666669079076251804c373039486 100644 --- a/substrate/frame/revive/fixtures/contracts/delegate_call_lib.rs +++ b/substrate/frame/revive/fixtures/contracts/delegate_call_lib.rs @@ -18,7 +18,7 @@ #![no_std] #![no_main] -use common::output; +use common::u64_output; use uapi::{HostFn, HostFnImpl as api, StorageFlags}; #[no_mangle] @@ -39,11 +39,11 @@ pub extern "C" fn call() { // Assert that `value_transferred` is equal to the value // passed to the `caller` contract: 1337. - output!(value_transferred, [0u8; 8], api::value_transferred,); - let value_transferred = u64::from_le_bytes(value_transferred[..].try_into().unwrap()); - assert_eq!(value_transferred, 1337); + let value = u64_output!(api::value_transferred,); + assert_eq!(value, 1337); // Assert that ALICE is the caller of the contract. - output!(caller, [0u8; 32], api::caller,); - assert_eq!(&caller[..], &[1u8; 32]); + let mut caller = [0u8; 20]; + api::caller(&mut caller); + assert_eq!(caller, [1u8; 20]); } diff --git a/substrate/frame/revive/fixtures/contracts/delegate_call_simple.rs b/substrate/frame/revive/fixtures/contracts/delegate_call_simple.rs index cf3351c52fdc22e5f530329f511ffc647d9dd7c3..20f8ec3364ee27b12077a60b80c7332f749d27d7 100644 --- a/substrate/frame/revive/fixtures/contracts/delegate_call_simple.rs +++ b/substrate/frame/revive/fixtures/contracts/delegate_call_simple.rs @@ -28,7 +28,7 @@ pub extern "C" fn deploy() {} #[no_mangle] #[polkavm_derive::polkavm_export] pub extern "C" fn call() { - input!(code_hash: [u8; 32],); + input!(code_hash: &[u8; 32],); // Delegate call into passed code hash. let input = [0u8; 0]; diff --git a/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs b/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs index 8b74493a1e38c9c843775c071c573fcf0a961892..8342f4acf95296d621c855408aacc5b8457a4234 100644 --- a/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs +++ b/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs @@ -18,21 +18,20 @@ #![no_std] #![no_main] -use common::input; +use common::{input, u256_bytes}; use uapi::{HostFn, HostFnImpl as api, StorageFlags}; const ADDRESS_KEY: [u8; 32] = [0u8; 32]; -const VALUE: [u8; 8] = [0, 0, 1u8, 0, 0, 0, 0, 0]; +const VALUE: [u8; 32] = u256_bytes(65536); #[no_mangle] #[polkavm_derive::polkavm_export] pub extern "C" fn deploy() { - input!(code_hash: [u8; 32],); + input!(code_hash: &[u8; 32],); let input = [0u8; 0]; - let mut address = [0u8; 32]; - let address = &mut &mut address[..]; - let salt = [71u8, 17u8]; + let mut address = [0u8; 20]; + let salt = [47u8; 32]; api::instantiate( code_hash, @@ -41,27 +40,28 @@ pub extern "C" fn deploy() { None, // No deposit limit. &VALUE, &input, - Some(address), + Some(&mut address), None, - &salt, + Some(&salt), ) .unwrap(); // Return the deployed contract address. - api::set_storage(StorageFlags::empty(), &ADDRESS_KEY, address); + api::set_storage(StorageFlags::empty(), &ADDRESS_KEY, &address); } #[no_mangle] #[polkavm_derive::polkavm_export] pub extern "C" fn call() { - let mut callee_addr = [0u8; 32]; - let callee_addr = &mut &mut callee_addr[..]; - api::get_storage(StorageFlags::empty(), &ADDRESS_KEY, callee_addr).unwrap(); + let mut callee_addr = [0u8; 20]; + let callee = &mut &mut callee_addr[..]; + api::get_storage(StorageFlags::empty(), &ADDRESS_KEY, callee).unwrap(); + assert!(callee.len() == 20); // Calling the destination contract with non-empty input data should fail. let res = api::call( uapi::CallFlags::empty(), - callee_addr, + &callee_addr, 0u64, // How much ref_time weight to devote for the execution. 0 = all. 0u64, // How much proof_size weight to devote for the execution. 0 = all. None, // No deposit limit. @@ -74,7 +74,7 @@ pub extern "C" fn call() { // Call the destination contract regularly, forcing it to self-destruct. api::call( uapi::CallFlags::empty(), - callee_addr, + &callee_addr, 0u64, // How much ref_time weight to devote for the execution. 0 = all. 0u64, // How much proof_size weight to devote for the execution. 0 = all. None, // No deposit limit. diff --git a/substrate/frame/revive/fixtures/contracts/drain.rs b/substrate/frame/revive/fixtures/contracts/drain.rs index f5c8681c9382834a11be3d613f1d7140d33c9337..0d644a4238c4c44d9f70d9379d6249bebfd483f3 100644 --- a/substrate/frame/revive/fixtures/contracts/drain.rs +++ b/substrate/frame/revive/fixtures/contracts/drain.rs @@ -18,7 +18,7 @@ #![no_std] #![no_main] -use common::output; +use common::{u256_bytes, u64_output}; use uapi::{HostFn, HostFnImpl as api}; #[no_mangle] @@ -28,17 +28,14 @@ pub extern "C" fn deploy() {} #[no_mangle] #[polkavm_derive::polkavm_export] pub extern "C" fn call() { - output!(balance, [0u8; 8], api::balance,); - let balance = u64::from_le_bytes(balance[..].try_into().unwrap()); - - output!(minimum_balance, [0u8; 8], api::minimum_balance,); - let minimum_balance = u64::from_le_bytes(minimum_balance[..].try_into().unwrap()); + let balance = u64_output!(api::balance,); + let minimum_balance = u64_output!(api::minimum_balance,); // Make the transferred value exceed the balance by adding the minimum balance. let balance = balance + minimum_balance; // Try to self-destruct by sending more balance to the 0 address. // The call will fail because a contract transfer has a keep alive requirement. - let res = api::transfer(&[0u8; 32], &balance.to_le_bytes()); + let res = api::transfer(&[0u8; 20], &u256_bytes(balance)); assert!(matches!(res, Err(uapi::ReturnErrorCode::TransferFailed))); } diff --git a/substrate/frame/revive/fixtures/contracts/event_and_return_on_deploy.rs b/substrate/frame/revive/fixtures/contracts/event_and_return_on_deploy.rs index 9186835d29118b4b2fb7667430f3cf3fea30adcf..5c438c1a75a13566ba05a48658c4bf56c45d0460 100644 --- a/substrate/frame/revive/fixtures/contracts/event_and_return_on_deploy.rs +++ b/substrate/frame/revive/fixtures/contracts/event_and_return_on_deploy.rs @@ -25,7 +25,8 @@ use uapi::{HostFn, HostFnImpl as api}; #[polkavm_derive::polkavm_export] pub extern "C" fn deploy() { let buffer = [1u8, 2, 3, 4]; - api::deposit_event(&[0u8; 0], &buffer); + let topics = [[42u8; 32]; 1]; + api::deposit_event(&topics, &buffer); api::return_value(uapi::ReturnFlags::empty(), &buffer); } diff --git a/substrate/frame/revive/fixtures/contracts/event_size.rs b/substrate/frame/revive/fixtures/contracts/event_size.rs index 2b56de4bd3fd90622205bade0ad02b004126f0fe..7f04ae42765aa5a81a27a9fa24969ba28d9a8d00 100644 --- a/substrate/frame/revive/fixtures/contracts/event_size.rs +++ b/substrate/frame/revive/fixtures/contracts/event_size.rs @@ -33,6 +33,7 @@ pub extern "C" fn call() { input!(len: u32,); let data = &BUFFER[..len as usize]; + let topics = [[0u8; 32]; 0]; - api::deposit_event(&[0u8; 0], data); + api::deposit_event(&topics, data); } diff --git a/substrate/frame/revive/fixtures/contracts/immutable_data.rs b/substrate/frame/revive/fixtures/contracts/immutable_data.rs new file mode 100644 index 0000000000000000000000000000000000000000..ac50e61a400b98262ceb3dcbadd5349b09fe102b --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/immutable_data.rs @@ -0,0 +1,43 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Tests that the `get_immutable_data` and `set_immutable_data` APIs work. + +#![no_std] +#![no_main] + +use common::input; +use uapi::{HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() { + input!(data: &[u8; 8],); + + api::set_immutable_data(data); +} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(data: &[u8; 8],); + + let mut buf = [0; 8]; + api::get_immutable_data(&mut &mut buf[..]); + + assert_eq!(data, &buf); +} diff --git a/substrate/frame/revive/fixtures/contracts/instantiate_return_code.rs b/substrate/frame/revive/fixtures/contracts/instantiate_return_code.rs index 90884f1a2a6890af2c1ea47d9d87e819c48ae99c..9764859c619b90452eeacbb337a7b94a2cd7d86c 100644 --- a/substrate/frame/revive/fixtures/contracts/instantiate_return_code.rs +++ b/substrate/frame/revive/fixtures/contracts/instantiate_return_code.rs @@ -18,7 +18,7 @@ #![no_std] #![no_main] -use common::input; +use common::{input, u256_bytes}; use uapi::{HostFn, HostFnImpl as api}; #[no_mangle] @@ -28,7 +28,7 @@ pub extern "C" fn deploy() {} #[no_mangle] #[polkavm_derive::polkavm_export] pub extern "C" fn call() { - input!(buffer, 36, code_hash: [u8; 32],); + input!(buffer, 36, code_hash: &[u8; 32],); let input = &buffer[32..]; let err_code = match api::instantiate( @@ -36,12 +36,12 @@ pub extern "C" fn call() { 0u64, // How much ref_time weight to devote for the execution. 0 = all. 0u64, /* How much proof_size weight to devote for the execution. 0 = * all. */ - None, // No deposit limit. - &10_000u64.to_le_bytes(), // Value to transfer. + None, // No deposit limit. + &u256_bytes(10_000u64), // Value to transfer. input, None, None, - &[0u8; 0], // Empty salt. + Some(&[0u8; 32]), // Salt. ) { Ok(_) => 0u32, Err(code) => code as u32, diff --git a/substrate/frame/revive/fixtures/contracts/instr_benchmark.rs b/substrate/frame/revive/fixtures/contracts/instr_benchmark.rs index c5fb382c32767602593ba4998d6a1008f04636c5..0492652b0d036c08881b756226a59b2b4cbf39ca 100644 --- a/substrate/frame/revive/fixtures/contracts/instr_benchmark.rs +++ b/substrate/frame/revive/fixtures/contracts/instr_benchmark.rs @@ -22,6 +22,8 @@ extern crate common; use common::input; use uapi::{HostFn, HostFnImpl as api, ReturnFlags}; +static mut MULT: [u32; 5_000] = [1u32; 5_000]; + #[no_mangle] #[polkavm_derive::polkavm_export] pub extern "C" fn deploy() {} @@ -29,13 +31,13 @@ pub extern "C" fn deploy() {} #[no_mangle] #[polkavm_derive::polkavm_export] pub extern "C" fn call() { - input!(rounds: u32, start: u32, div: u32, mult: u32, add: u32, ); + input!(rounds: u32, ); - let mut acc = start; + let mut acc = 5; - for _ in 0..rounds { - acc = acc / div * mult + add; + for i in 0..rounds { + acc = acc * unsafe { MULT[i as usize] } } - api::return_value(ReturnFlags::empty(), start.to_le_bytes().as_ref()); + api::return_value(ReturnFlags::empty(), acc.to_le_bytes().as_ref()); } diff --git a/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs b/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs index 3ed886b494891f24bc8310cd4f6ecb41439fcff7..2efacb4e683ffedbe1dce29b07130fd2b26fed87 100644 --- a/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs +++ b/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs @@ -23,14 +23,14 @@ use common::input; use uapi::{HostFn, HostFnImpl as api}; -const ALICE: [u8; 32] = [1u8; 32]; +const ETH_ALICE: [u8; 20] = [1u8; 20]; /// Load input data and perform the action specified by the input. /// If `delegate_call` is true, then delegate call into the contract. fn load_input(delegate_call: bool) { input!( action: u32, - code_hash: [u8; 32], + code_hash: &[u8; 32], ); match action { @@ -44,7 +44,7 @@ fn load_input(delegate_call: bool) { }, // 3 = Terminate 3 => { - api::terminate(&ALICE); + api::terminate(Ð_ALICE); }, // Everything else is a noop _ => {}, diff --git a/substrate/frame/revive/fixtures/contracts/oom_ro.rs b/substrate/frame/revive/fixtures/contracts/oom_ro.rs new file mode 100644 index 0000000000000000000000000000000000000000..41c080d5847e8a6ff357528eb4445d01b0ea0351 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/oom_ro.rs @@ -0,0 +1,44 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This creates a large ro section. Even though it is zero +//! initialized we expect them to be included into the blob. +//! This means it will fail at the blob size check. + +#![no_std] +#![no_main] + +extern crate common; + +use uapi::{HostFn, HostFnImpl as api, ReturnFlags}; + +static BUFFER: [u8; 1025 * 1024] = [0; 1025 * 1024]; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call_never() { + // make sure the buffer is not optimized away + api::return_value(ReturnFlags::empty(), &BUFFER); +} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() {} diff --git a/substrate/frame/revive/fixtures/contracts/oom_rw_included.rs b/substrate/frame/revive/fixtures/contracts/oom_rw_included.rs new file mode 100644 index 0000000000000000000000000000000000000000..2cdcf7bafed1e533cf6dbec6028f14cfdfa45e17 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/oom_rw_included.rs @@ -0,0 +1,44 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This creates a large rw section but with its contents +//! included into the blob. It should be rejected for its +//! blob size. + +#![no_std] +#![no_main] + +extern crate common; + +use uapi::{HostFn, HostFnImpl as api, ReturnFlags}; + +static mut BUFFER: [u8; 513 * 1024] = [42; 513 * 1024]; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub unsafe extern "C" fn call_never() { + // make sure the buffer is not optimized away + api::return_value(ReturnFlags::empty(), &BUFFER); +} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() {} diff --git a/substrate/frame/revive/fixtures/contracts/oom_rw_trailing.rs b/substrate/frame/revive/fixtures/contracts/oom_rw_trailing.rs new file mode 100644 index 0000000000000000000000000000000000000000..993be8e9cda41a314c58cc703bc7c1743769ef8a --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/oom_rw_trailing.rs @@ -0,0 +1,44 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This creates a large rw section but the trailing zeroes +//! are removed by the linker. It should be rejected even +//! though the blob is small enough. + +#![no_std] +#![no_main] + +extern crate common; + +use uapi::{HostFn, HostFnImpl as api, ReturnFlags}; + +static mut BUFFER: [u8; 1025 * 1024] = [0; 1025 * 1024]; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub unsafe extern "C" fn call_never() { + // make sure the buffer is not optimized away + api::return_value(ReturnFlags::empty(), &BUFFER); +} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() {} diff --git a/substrate/frame/revive/fixtures/contracts/read_only_call.rs b/substrate/frame/revive/fixtures/contracts/read_only_call.rs index a62bb205039f859db74eeb8fe1d2fcf331ccff33..ea74d56867f5efd1dd2f671a7b015418f7b1d7b7 100644 --- a/substrate/frame/revive/fixtures/contracts/read_only_call.rs +++ b/substrate/frame/revive/fixtures/contracts/read_only_call.rs @@ -31,7 +31,7 @@ pub extern "C" fn deploy() {} pub extern "C" fn call() { input!( 256, - callee_addr: [u8; 32], + callee_addr: &[u8; 20], callee_input: [u8], ); @@ -39,10 +39,10 @@ pub extern "C" fn call() { api::call( uapi::CallFlags::READ_ONLY, callee_addr, - 0u64, // How much ref_time to devote for the execution. 0 = all. - 0u64, // How much proof_size to devote for the execution. 0 = all. - None, // No deposit limit. - &0u64.to_le_bytes(), // Value transferred to the contract. + 0u64, // How much ref_time to devote for the execution. 0 = all. + 0u64, // How much proof_size to devote for the execution. 0 = all. + None, // No deposit limit. + &[0u8; 32], // Value transferred to the contract. callee_input, None, ) diff --git a/substrate/frame/revive/fixtures/contracts/recurse.rs b/substrate/frame/revive/fixtures/contracts/recurse.rs index f4dfd6c965d04d8b10b81dcfbc31c8ef4e0c25da..2e70d67d8c738c700f35d281a51605e7b9c76170 100644 --- a/substrate/frame/revive/fixtures/contracts/recurse.rs +++ b/substrate/frame/revive/fixtures/contracts/recurse.rs @@ -20,7 +20,7 @@ #![no_std] #![no_main] -use common::{input, output}; +use common::input; use uapi::{HostFn, HostFnImpl as api}; #[no_mangle] @@ -33,7 +33,8 @@ pub extern "C" fn call() { input!(calls_left: u32, ); // own address - output!(addr, [0u8; 32], api::address,); + let mut addr = [0u8; 20]; + api::address(&mut addr); if calls_left == 0 { return @@ -41,11 +42,11 @@ pub extern "C" fn call() { api::call( uapi::CallFlags::ALLOW_REENTRY, - addr, - 0u64, // How much ref_time to devote for the execution. 0 = all. - 0u64, // How much deposit_limit to devote for the execution. 0 = all. - None, // No deposit limit. - &0u64.to_le_bytes(), // Value transferred to the contract. + &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. + &[0u8; 32], // Value transferred to the contract. &(calls_left - 1).to_le_bytes(), None, ) diff --git a/substrate/frame/revive/fixtures/contracts/return_data_api.rs b/substrate/frame/revive/fixtures/contracts/return_data_api.rs new file mode 100644 index 0000000000000000000000000000000000000000..846396b0944d303479d98b6ff227c938b086ec62 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/return_data_api.rs @@ -0,0 +1,166 @@ +// 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 tests that the `return_data_size` and `return_data_copy` APIs work. +//! +//! It does so by calling and instantiating the "return_with_data" fixture, +//! which always echoes back the input[4..] regardless of the call outcome. +//! +//! We also check that the saved return data is properly reset after a trap +//! and unaffected by plain transfers. + +#![no_std] +#![no_main] + +use common::{input, u256_bytes}; +use uapi::{HostFn, HostFnImpl as api}; + +const INPUT_BUF_SIZE: usize = 128; +static INPUT_DATA: [u8; INPUT_BUF_SIZE] = [0xFF; INPUT_BUF_SIZE]; +/// The "return_with_data" fixture echoes back 4 bytes less than the input +const OUTPUT_BUF_SIZE: usize = INPUT_BUF_SIZE - 4; +static OUTPUT_DATA: [u8; OUTPUT_BUF_SIZE] = [0xEE; OUTPUT_BUF_SIZE]; + +fn assert_return_data_after_call(input: &[u8]) { + assert_return_data_size_of(OUTPUT_BUF_SIZE as u64); + assert_plain_transfer_does_not_reset(OUTPUT_BUF_SIZE as u64); + assert_return_data_copy(&input[4..]); + reset_return_data(); +} + +/// Assert that what we get from [api::return_data_copy] matches `whole_return_data`, +/// either fully or partially with an offset and limited size. +fn assert_return_data_copy(whole_return_data: &[u8]) { + // The full return data should match + let mut buf = OUTPUT_DATA; + let mut full = &mut buf[..whole_return_data.len()]; + api::return_data_copy(&mut full, 0); + assert_eq!(whole_return_data, full); + + // Partial return data should match + let mut buf = OUTPUT_DATA; + let offset = 5; // we just pick some offset + let size = 32; // we just pick some size + let mut partial = &mut buf[offset..offset + size]; + api::return_data_copy(&mut partial, offset as u32); + assert_eq!(*partial, whole_return_data[offset..offset + size]); +} + +/// This function panics in a recursive contract call context. +fn recursion_guard() -> [u8; 20] { + let mut caller_address = [0u8; 20]; + api::caller(&mut caller_address); + + let mut own_address = [0u8; 20]; + api::address(&mut own_address); + + assert_ne!(caller_address, own_address); + + own_address +} + +/// Call ourselves recursively, which panics the callee and thus resets the return data. +fn reset_return_data() { + api::call( + uapi::CallFlags::ALLOW_REENTRY, + &recursion_guard(), + 0u64, + 0u64, + None, + &[0u8; 32], + &[0u8; 32], + None, + ) + .unwrap_err(); + assert_return_data_size_of(0); +} + +/// Assert [api::return_data_size] to match the `expected` value. +fn assert_return_data_size_of(expected: u64) { + let mut return_data_size = [0xff; 32]; + api::return_data_size(&mut return_data_size); + assert_eq!(return_data_size, u256_bytes(expected)); +} + +/// Assert [api::return_data_size] to match the `expected` value after a plain transfer +/// (plain transfers don't issue a call and so should not reset the return data) +fn assert_plain_transfer_does_not_reset(expected: u64) { + api::transfer(&[0; 20], &u256_bytes(128)).unwrap(); + assert_return_data_size_of(expected); +} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!(code_hash: &[u8; 32],); + + // We didn't do anything yet; return data size should be 0 + assert_return_data_size_of(0); + + recursion_guard(); + + let mut address_buf = [0; 20]; + let construct_input = |exit_flag| { + let mut input = INPUT_DATA; + input[0] = exit_flag; + input[9] = 7; + input[17 / 2] = 127; + input[89 / 2] = 127; + input + }; + let mut instantiate = |exit_flag| { + api::instantiate( + code_hash, + 0u64, + 0u64, + None, + &[0; 32], + &construct_input(exit_flag), + Some(&mut address_buf), + None, + None, + ) + }; + let call = |exit_flag, address_buf| { + api::call( + uapi::CallFlags::empty(), + address_buf, + 0u64, + 0u64, + None, + &[0; 32], + &construct_input(exit_flag), + None, + ) + }; + + instantiate(0).unwrap(); + assert_return_data_after_call(&construct_input(0)[..]); + + instantiate(1).unwrap_err(); + assert_return_data_after_call(&construct_input(1)[..]); + + call(0, &address_buf).unwrap(); + assert_return_data_after_call(&construct_input(0)[..]); + + call(1, &address_buf).unwrap_err(); + assert_return_data_after_call(&construct_input(1)[..]); +} diff --git a/substrate/frame/revive/fixtures/contracts/sbrk.rs b/substrate/frame/revive/fixtures/contracts/sbrk.rs new file mode 100644 index 0000000000000000000000000000000000000000..5b0bba99df81fe3a0e0ca74927ecb516b2e1a5fb --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/sbrk.rs @@ -0,0 +1,39 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Uses the sbrk instruction in order to test that it is rejected. + +#![no_std] +#![no_main] + +extern crate common; + +// Export that is never called. We can put code here that should be in the binary +// but is never supposed to be run. +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call_never() { + polkavm_derive::sbrk(4); +} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() {} diff --git a/substrate/frame/revive/fixtures/contracts/self_destruct.rs b/substrate/frame/revive/fixtures/contracts/self_destruct.rs index 10f33226acf224bacbd718015f8f06f1862d95d9..524979991ec7ab1020073d48022d773ca809c3b8 100644 --- a/substrate/frame/revive/fixtures/contracts/self_destruct.rs +++ b/substrate/frame/revive/fixtures/contracts/self_destruct.rs @@ -18,10 +18,10 @@ #![no_std] #![no_main] -use common::{input, output}; +use common::input; use uapi::{HostFn, HostFnImpl as api}; -const DJANGO: [u8; 32] = [4u8; 32]; +const ETH_DJANGO: [u8; 20] = [4u8; 20]; #[no_mangle] #[polkavm_derive::polkavm_export] @@ -36,20 +36,22 @@ pub extern "C" fn call() { input!(input, 4,); if !input.is_empty() { - output!(addr, [0u8; 32], api::address,); + let mut addr = [0u8; 20]; + api::address(&mut addr); + api::call( uapi::CallFlags::ALLOW_REENTRY, - addr, - 0u64, // How much ref_time to devote for the execution. 0 = all. - 0u64, // How much proof_size to devote for the execution. 0 = all. - None, // No deposit limit. - &0u64.to_le_bytes(), // Value to transfer. + &addr, + 0u64, // How much ref_time to devote for the execution. 0 = all. + 0u64, // How much proof_size to devote for the execution. 0 = all. + None, // No deposit limit. + &[0u8; 32], // Value to transfer. &[0u8; 0], None, ) .unwrap(); } else { // Try to terminate and give balance to django. - api::terminate(&DJANGO); + api::terminate(Ð_DJANGO); } } diff --git a/substrate/frame/revive/fixtures/contracts/self_destructing_constructor.rs b/substrate/frame/revive/fixtures/contracts/self_destructing_constructor.rs index 28bcef978231aa05e3889f5c26a3bab09d73acc3..3285aecbe780971ebb2af792529c62efd275b4b7 100644 --- a/substrate/frame/revive/fixtures/contracts/self_destructing_constructor.rs +++ b/substrate/frame/revive/fixtures/contracts/self_destructing_constructor.rs @@ -24,7 +24,7 @@ use uapi::{HostFn, HostFnImpl as api}; #[no_mangle] #[polkavm_derive::polkavm_export] pub extern "C" fn deploy() { - api::terminate(&[0u8; 32]); + api::terminate(&[0u8; 20]); } #[no_mangle] diff --git a/substrate/frame/revive/fixtures/contracts/set_code_hash.rs b/substrate/frame/revive/fixtures/contracts/set_code_hash.rs index e3cf4becfb97241c20046674e95d9316a61eeb8f..75995d7bb8a2809d02d4796576efc1d0e29a79d0 100644 --- a/substrate/frame/revive/fixtures/contracts/set_code_hash.rs +++ b/substrate/frame/revive/fixtures/contracts/set_code_hash.rs @@ -28,7 +28,7 @@ pub extern "C" fn deploy() {} #[no_mangle] #[polkavm_derive::polkavm_export] pub extern "C" fn call() { - input!(addr: [u8; 32],); + input!(addr: &[u8; 32],); api::set_code_hash(addr).unwrap(); // we return 1 after setting new code_hash diff --git a/substrate/frame/revive/fixtures/contracts/transfer_return_code.rs b/substrate/frame/revive/fixtures/contracts/transfer_return_code.rs index d3f6a1dd3a08170121ad7175eeddf8208c932315..bfeca9b8b4a42d0276285cf7332ad6a9ddb77e09 100644 --- a/substrate/frame/revive/fixtures/contracts/transfer_return_code.rs +++ b/substrate/frame/revive/fixtures/contracts/transfer_return_code.rs @@ -18,7 +18,7 @@ #![no_std] #![no_main] -extern crate common; +use common::u256_bytes; use uapi::{HostFn, HostFnImpl as api}; #[no_mangle] @@ -28,7 +28,7 @@ pub extern "C" fn deploy() {} #[no_mangle] #[polkavm_derive::polkavm_export] pub extern "C" fn call() { - let ret_code = match api::transfer(&[0u8; 32], &100u64.to_le_bytes()) { + let ret_code = match api::transfer(&[0u8; 20], &u256_bytes(100u64)) { Ok(_) => 0u32, Err(code) => code as u32, }; diff --git a/substrate/frame/revive/fixtures/src/lib.rs b/substrate/frame/revive/fixtures/src/lib.rs index 1b6103f57aee2c9469a8687b709f1d87ddadd978..5548dca66d0753fdbee76eaaa92f392a39f8f64a 100644 --- a/substrate/frame/revive/fixtures/src/lib.rs +++ b/substrate/frame/revive/fixtures/src/lib.rs @@ -21,18 +21,13 @@ extern crate alloc; /// Load a given wasm module and returns a wasm binary contents along with it's hash. #[cfg(feature = "std")] -pub fn compile_module( - fixture_name: &str, -) -> anyhow::Result<(Vec, ::Output)> -where - T: frame_system::Config, -{ - use sp_runtime::traits::Hash; +pub fn compile_module(fixture_name: &str) -> anyhow::Result<(Vec, sp_core::H256)> { let out_dir: std::path::PathBuf = env!("OUT_DIR").into(); let fixture_path = out_dir.join(format!("{fixture_name}.polkavm")); + log::debug!("Loading fixture from {fixture_path:?}"); let binary = std::fs::read(fixture_path)?; - let code_hash = T::Hashing::hash(&binary); - Ok((binary, code_hash)) + let code_hash = sp_io::hashing::keccak_256(&binary); + Ok((binary, sp_core::H256(code_hash))) } /// Fixtures used in runtime benchmarks. diff --git a/substrate/frame/revive/mock-network/Cargo.toml b/substrate/frame/revive/mock-network/Cargo.toml index 0d597bbdc2283faaf3bd0dd6d7abaea490943d87..85656a57b49ccf22c2f6bdd0e97f5f4dc557ff54 100644 --- a/substrate/frame/revive/mock-network/Cargo.toml +++ b/substrate/frame/revive/mock-network/Cargo.toml @@ -48,6 +48,7 @@ pallet-revive-fixtures = { workspace = true } [features] default = ["std"] +riscv = ["pallet-revive-fixtures/riscv"] std = [ "codec/std", "frame-support/std", diff --git a/substrate/frame/revive/mock-network/src/lib.rs b/substrate/frame/revive/mock-network/src/lib.rs index 2e4f273a010319eead81b8ba74acce457be99cf9..8489946539727c507b15e874917f4f38cd73bb5c 100644 --- a/substrate/frame/revive/mock-network/src/lib.rs +++ b/substrate/frame/revive/mock-network/src/lib.rs @@ -1,18 +1,18 @@ // Copyright Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Substrate. -// Polkadot is free software: you can redistribute it and/or modify +// Substrate is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Polkadot is distributed in the hope that it will be useful, +// Substrate is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . +// along with Substrate. If not, see . pub mod mocks; pub mod parachain; diff --git a/substrate/frame/revive/mock-network/src/mocks.rs b/substrate/frame/revive/mock-network/src/mocks.rs index bf3baec7a524abc0a9c5eee8e637b4b9a9cf3e04..6903f01e91a2b5543235fdeddb74f46634855c6b 100644 --- a/substrate/frame/revive/mock-network/src/mocks.rs +++ b/substrate/frame/revive/mock-network/src/mocks.rs @@ -1,18 +1,18 @@ // Copyright Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Substrate. -// Polkadot is free software: you can redistribute it and/or modify +// Substrate is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Polkadot is distributed in the hope that it will be useful, +// Substrate is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . +// along with Substrate. If not, see . pub mod msg_queue; pub mod relay_message_queue; diff --git a/substrate/frame/revive/mock-network/src/mocks/msg_queue.rs b/substrate/frame/revive/mock-network/src/mocks/msg_queue.rs index 6e922c16c2982b8399978511b9dd5558ba2c0ce8..88ab8135ea1297d44b025ddf6d66fafbbe674858 100644 --- a/substrate/frame/revive/mock-network/src/mocks/msg_queue.rs +++ b/substrate/frame/revive/mock-network/src/mocks/msg_queue.rs @@ -1,18 +1,18 @@ // Copyright Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Substrate. -// Polkadot is free software: you can redistribute it and/or modify +// Substrate is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Polkadot is distributed in the hope that it will be useful, +// Substrate is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . +// along with Substrate. If not, see . //! Parachain runtime mock. diff --git a/substrate/frame/revive/mock-network/src/mocks/relay_message_queue.rs b/substrate/frame/revive/mock-network/src/mocks/relay_message_queue.rs index 14099965e3f173d7f06b86c08b722ceeded26418..50f0febd2c4403b4981aec0a416b5814f9902df7 100644 --- a/substrate/frame/revive/mock-network/src/mocks/relay_message_queue.rs +++ b/substrate/frame/revive/mock-network/src/mocks/relay_message_queue.rs @@ -1,18 +1,18 @@ // Copyright Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Substrate. -// Polkadot is free software: you can redistribute it and/or modify +// Substrate is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Polkadot is distributed in the hope that it will be useful, +// Substrate is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . +// along with Substrate. If not, see . use frame_support::{parameter_types, weights::Weight}; use xcm::latest::prelude::*; diff --git a/substrate/frame/revive/mock-network/src/parachain.rs b/substrate/frame/revive/mock-network/src/parachain.rs index 3def48cca96b829a91b29379ffb17cea95e12e22..26a8fdcada27e890e2cee926da9bc0448ac3d2bc 100644 --- a/substrate/frame/revive/mock-network/src/parachain.rs +++ b/substrate/frame/revive/mock-network/src/parachain.rs @@ -1,18 +1,18 @@ // Copyright Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Substrate. -// Polkadot is free software: you can redistribute it and/or modify +// Substrate is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Polkadot is distributed in the hope that it will be useful, +// Substrate is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . +// along with Substrate. If not, see . //! Parachain runtime mock. @@ -94,6 +94,7 @@ impl pallet_balances::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; type WeightInfo = (); + type DoneSlashHandler = (); } parameter_types! { diff --git a/substrate/frame/revive/mock-network/src/parachain/contracts_config.rs b/substrate/frame/revive/mock-network/src/parachain/contracts_config.rs index 49f53ae5bc3d5f581b650335a1fdb155cb696b3e..c13c337d1667077301acef8a8e5d8e5537bc60f1 100644 --- a/substrate/frame/revive/mock-network/src/parachain/contracts_config.rs +++ b/substrate/frame/revive/mock-network/src/parachain/contracts_config.rs @@ -1,18 +1,18 @@ // Copyright Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Substrate. -// Polkadot is free software: you can redistribute it and/or modify +// Substrate is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Polkadot is distributed in the hope that it will be useful, +// Substrate is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . +// along with Substrate. If not, see . use super::{Balances, Runtime, RuntimeCall, RuntimeEvent}; use crate::parachain::RuntimeHoldReason; @@ -20,7 +20,7 @@ use frame_support::derive_impl; #[derive_impl(pallet_revive::config_preludes::TestDefaultConfig)] impl pallet_revive::Config for Runtime { - type AddressGenerator = pallet_revive::DefaultAddressGenerator; + type AddressMapper = pallet_revive::DefaultAddressMapper; type Currency = Balances; type Time = super::Timestamp; type Xcm = pallet_xcm::Pallet; diff --git a/substrate/frame/revive/mock-network/src/primitives.rs b/substrate/frame/revive/mock-network/src/primitives.rs index efc42772f88ade5868492964b1b383c26ee689ac..cb9b2f00f9e7374d4db4d7d7a13a4e1983325182 100644 --- a/substrate/frame/revive/mock-network/src/primitives.rs +++ b/substrate/frame/revive/mock-network/src/primitives.rs @@ -1,18 +1,18 @@ // Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Substrate. -// Polkadot is free software: you can redistribute it and/or modify +// Substrate is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Polkadot is distributed in the hope that it will be useful, +// Substrate is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . +// along with Substrate. If not, see . pub type Balance = u128; diff --git a/substrate/frame/revive/mock-network/src/relay_chain.rs b/substrate/frame/revive/mock-network/src/relay_chain.rs index 8829fff3d0436d95b3e44d87b2258d13c167efc1..5fed061f80b45aaaad89e3a4ef3b0abef41000d8 100644 --- a/substrate/frame/revive/mock-network/src/relay_chain.rs +++ b/substrate/frame/revive/mock-network/src/relay_chain.rs @@ -1,18 +1,18 @@ // Copyright Parity Technologies (UK) Ltd. -// This file is part of Polkadot. +// This file is part of Substrate. -// Polkadot is free software: you can redistribute it and/or modify +// Substrate is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Polkadot is distributed in the hope that it will be useful, +// Substrate is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License -// along with Polkadot. If not, see . +// along with Substrate. If not, see . //! Relay chain runtime mock. @@ -89,6 +89,7 @@ impl pallet_balances::Config for Runtime { type MaxFreezes = ConstU32<0>; type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; + type DoneSlashHandler = (); } impl shared::Config for Runtime { diff --git a/substrate/frame/revive/mock-network/src/tests.rs b/substrate/frame/revive/mock-network/src/tests.rs index 9259dd6f169e3153230ed13ebc5e20ce1cd5941d..bd05726a1a45f505e5105c673a7d0944f0cb26cb 100644 --- a/substrate/frame/revive/mock-network/src/tests.rs +++ b/substrate/frame/revive/mock-network/src/tests.rs @@ -16,10 +16,8 @@ // limitations under the License. use crate::{ - parachain::{self, Runtime}, - parachain_account_sovereign_account_id, - primitives::{AccountId, CENTS}, - relay_chain, MockNet, ParaA, ParachainBalances, Relay, ALICE, BOB, INITIAL_BALANCE, + parachain, parachain_account_sovereign_account_id, primitives::CENTS, relay_chain, MockNet, + ParaA, ParachainBalances, Relay, ALICE, BOB, INITIAL_BALANCE, }; use codec::{Decode, Encode}; use frame_support::traits::{fungibles::Mutate, Currency}; @@ -30,6 +28,7 @@ use pallet_revive::{ }; use pallet_revive_fixtures::compile_module; use pallet_revive_uapi::ReturnErrorCode; +use sp_core::H160; use xcm::{v4::prelude::*, VersionedLocation, VersionedXcm}; use xcm_simulator::TestExt; @@ -39,41 +38,43 @@ macro_rules! assert_return_code { }}; } -fn bare_call(dest: sp_runtime::AccountId32) -> BareCallBuilder { +fn bare_call(dest: H160) -> BareCallBuilder { BareCallBuilder::::bare_call(RawOrigin::Signed(ALICE).into(), dest) } /// Instantiate the tests contract, and fund it with some balance and assets. -fn instantiate_test_contract(name: &str) -> AccountId { - let (wasm, _) = compile_module::(name).unwrap(); +fn instantiate_test_contract(name: &str) -> Contract { + let (wasm, _) = compile_module(name).unwrap(); // Instantiate contract. - let contract_addr = ParaA::execute_with(|| { + let contract = ParaA::execute_with(|| { BareInstantiateBuilder::::bare_instantiate( RawOrigin::Signed(ALICE).into(), Code::Upload(wasm), ) - .build_and_unwrap_account_id() + .storage_deposit_limit(1_000_000_000_000) + .build_and_unwrap_contract() }); // Funds contract account with some balance and assets. ParaA::execute_with(|| { - parachain::Balances::make_free_balance_be(&contract_addr, INITIAL_BALANCE); - parachain::Assets::mint_into(0u32.into(), &contract_addr, INITIAL_BALANCE).unwrap(); + parachain::Balances::make_free_balance_be(&contract.account_id, INITIAL_BALANCE); + parachain::Assets::mint_into(0u32.into(), &contract.account_id, INITIAL_BALANCE).unwrap(); }); Relay::execute_with(|| { - let sovereign_account = parachain_account_sovereign_account_id(1u32, contract_addr.clone()); + let sovereign_account = + parachain_account_sovereign_account_id(1u32, contract.account_id.clone()); relay_chain::Balances::make_free_balance_be(&sovereign_account, INITIAL_BALANCE); }); - contract_addr + contract } #[test] fn test_xcm_execute() { MockNet::reset(); - let contract_addr = instantiate_test_contract("xcm_execute"); + let Contract { addr, account_id } = instantiate_test_contract("xcm_execute"); // Execute XCM instructions through the contract. ParaA::execute_with(|| { @@ -87,9 +88,7 @@ fn test_xcm_execute() { .deposit_asset(assets, beneficiary) .build(); - let result = bare_call(contract_addr.clone()) - .data(VersionedXcm::V4(message).encode()) - .build(); + let result = bare_call(addr).data(VersionedXcm::V4(message).encode()).build(); assert_eq!(result.gas_consumed, result.gas_required); assert_return_code!(&result.result.unwrap(), ReturnErrorCode::Success); @@ -98,7 +97,7 @@ fn test_xcm_execute() { // Bob. let initial = INITIAL_BALANCE; assert_eq!(ParachainBalances::free_balance(BOB), initial + amount); - assert_eq!(ParachainBalances::free_balance(&contract_addr), initial - amount); + assert_eq!(ParachainBalances::free_balance(&account_id), initial - amount); }); } @@ -106,7 +105,7 @@ fn test_xcm_execute() { fn test_xcm_execute_incomplete() { MockNet::reset(); - let contract_addr = instantiate_test_contract("xcm_execute"); + let Contract { addr, account_id } = instantiate_test_contract("xcm_execute"); let amount = 10 * CENTS; // Execute XCM instructions through the contract. @@ -124,15 +123,13 @@ fn test_xcm_execute_incomplete() { .deposit_asset(assets, beneficiary) .build(); - let result = bare_call(contract_addr.clone()) - .data(VersionedXcm::V4(message).encode()) - .build(); + let result = bare_call(addr).data(VersionedXcm::V4(message).encode()).build(); assert_eq!(result.gas_consumed, result.gas_required); assert_return_code!(&result.result.unwrap(), ReturnErrorCode::XcmExecutionFailed); assert_eq!(ParachainBalances::free_balance(BOB), INITIAL_BALANCE); - assert_eq!(ParachainBalances::free_balance(&contract_addr), INITIAL_BALANCE - amount); + assert_eq!(ParachainBalances::free_balance(&account_id), INITIAL_BALANCE - amount); }); } @@ -140,11 +137,11 @@ fn test_xcm_execute_incomplete() { fn test_xcm_execute_reentrant_call() { MockNet::reset(); - let contract_addr = instantiate_test_contract("xcm_execute"); + let Contract { addr, .. } = instantiate_test_contract("xcm_execute"); ParaA::execute_with(|| { let transact_call = parachain::RuntimeCall::Contracts(pallet_revive::Call::call { - dest: contract_addr.clone(), + dest: addr, gas_limit: 1_000_000.into(), storage_deposit_limit: test_utils::deposit_limit::(), data: vec![], @@ -157,7 +154,7 @@ fn test_xcm_execute_reentrant_call() { .expect_transact_status(MaybeErrorCode::Success) .build(); - let result = bare_call(contract_addr.clone()) + let result = bare_call(addr) .data(VersionedXcm::V4(message).encode()) .build_and_unwrap_result(); @@ -171,7 +168,7 @@ fn test_xcm_execute_reentrant_call() { #[test] fn test_xcm_send() { MockNet::reset(); - let contract_addr = instantiate_test_contract("xcm_send"); + let Contract { addr, account_id } = instantiate_test_contract("xcm_send"); let amount = 1_000 * CENTS; let fee = parachain::estimate_message_fee(4); // Accounts for the `DescendOrigin` instruction added by `send_xcm` @@ -188,7 +185,7 @@ fn test_xcm_send() { .deposit_asset(assets, beneficiary) .build(); - let result = bare_call(contract_addr.clone()) + let result = bare_call(addr) .data((dest, VersionedXcm::V4(message)).encode()) .build_and_unwrap_result(); @@ -197,7 +194,7 @@ fn test_xcm_send() { }); Relay::execute_with(|| { - let derived_contract_addr = ¶chain_account_sovereign_account_id(1, contract_addr); + let derived_contract_addr = ¶chain_account_sovereign_account_id(1, account_id); assert_eq!( INITIAL_BALANCE - amount, relay_chain::Balances::free_balance(derived_contract_addr) diff --git a/substrate/frame/revive/proc-macro/src/lib.rs b/substrate/frame/revive/proc-macro/src/lib.rs index 95f4110a2d76d903785b261caade68661397e186..012b4bfab9a9b129cfaa590dd91642a00a7533c4 100644 --- a/substrate/frame/revive/proc-macro/src/lib.rs +++ b/substrate/frame/revive/proc-macro/src/lib.rs @@ -349,18 +349,19 @@ where let Some(ident) = path.path.get_ident() else { panic!("Type needs to be ident"); }; - let size = - if ident == "i8" || - ident == "i16" || ident == "i32" || - ident == "u8" || ident == "u16" || - ident == "u32" - { - 1 - } else if ident == "i64" || ident == "u64" { - 2 - } else { - panic!("Pass by value only supports primitives"); - }; + let size = if ident == "i8" || + ident == "i16" || + ident == "i32" || + ident == "u8" || + ident == "u16" || + ident == "u32" + { + 1 + } else if ident == "i64" || ident == "u64" { + 2 + } else { + panic!("Pass by value only supports primitives"); + }; registers_used += size; if registers_used > ALLOWED_REGISTERS { return quote! { diff --git a/substrate/frame/revive/src/address.rs b/substrate/frame/revive/src/address.rs index 5758daf7b1ff8117d1de5e48e8c1e40490ed7a69..c51940ba771e6ba81cb8fadb27a23a4eed655bd7 100644 --- a/substrate/frame/revive/src/address.rs +++ b/substrate/frame/revive/src/address.rs @@ -15,54 +15,114 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Functions that deal with address derivation. +//! Functions that deal contract addresses. -use crate::{CodeHash, Config}; -use codec::{Decode, Encode}; -use sp_runtime::traits::{Hash, TrailingZeroInput}; +use alloc::vec::Vec; +use sp_core::H160; +use sp_io::hashing::keccak_256; +use sp_runtime::AccountId32; -/// Provides the contract address generation method. +/// Map between the native chain account id `T` and an Ethereum [`H160`]. /// -/// See [`DefaultAddressGenerator`] for the default implementation. +/// This trait exists only to emulate specialization for different concrete +/// native account ids. **Not** to make the mapping user configurable. Hence +/// the trait is `Sealed` and only one mandatory implementor [`DefaultAddressMapper`] +/// exists. /// -/// # Note for implementors -/// -/// 1. Make sure that there are no collisions, different inputs never lead to the same output. -/// 2. Make sure that the same inputs lead to the same output. -pub trait AddressGenerator { - /// The address of a contract based on the given instantiate parameters. +/// Please note that we assume that the native account is at least 20 bytes and +/// only implement this type for a `T` where this is the case. Luckily, this is the +/// case for all existing runtimes as of right now. Reasing is that this will allow +/// us to reverse an address -> account_id mapping by just stripping the prefix. +pub trait AddressMapper: private::Sealed { + /// Convert an account id to an ethereum adress. + /// + /// This mapping is **not** required to be reversible. + fn to_address(account_id: &T) -> H160; + + /// Convert an ethereum address to a native account id. /// - /// Changing the formular for an already deployed chain is fine as long as no collisions - /// with the old formular. Changes only affect existing contracts. - fn contract_address( - deploying_address: &T::AccountId, - code_hash: &CodeHash, - input_data: &[u8], - salt: &[u8], - ) -> T::AccountId; + /// This mapping is **required** to be reversible. + fn to_account_id(address: &H160) -> T; + + /// Same as [`Self::to_account_id`] but when we know the address is a contract. + /// + /// This is only the case when we just generated the new address. + fn to_account_id_contract(address: &H160) -> T; } -/// Default address generator. -/// -/// This is the default address generator used by contract instantiation. Its result -/// is only dependent on its inputs. It can therefore be used to reliably predict the -/// address of a contract. This is akin to the formula of eth's CREATE2 opcode. There -/// is no CREATE equivalent because CREATE2 is strictly more powerful. -/// Formula: -/// `hash("contract_addr_v1" ++ deploying_address ++ code_hash ++ input_data ++ salt)` -pub struct DefaultAddressGenerator; - -impl AddressGenerator for DefaultAddressGenerator { - /// Formula: `hash("contract_addr_v1" ++ deploying_address ++ code_hash ++ input_data ++ salt)` - fn contract_address( - deploying_address: &T::AccountId, - code_hash: &CodeHash, - input_data: &[u8], - salt: &[u8], - ) -> T::AccountId { - let entropy = (b"contract_addr_v1", deploying_address, code_hash, input_data, salt) - .using_encoded(T::Hashing::hash); - Decode::decode(&mut TrailingZeroInput::new(entropy.as_ref())) - .expect("infinite length input; no invalid inputs for type; qed") +mod private { + pub trait Sealed {} + impl Sealed for super::DefaultAddressMapper {} +} + +/// The only implementor for `AddressMapper`. +pub enum DefaultAddressMapper {} + +impl AddressMapper for DefaultAddressMapper { + fn to_address(account_id: &AccountId32) -> H160 { + H160::from_slice(&>::as_ref(&account_id)[..20]) + } + + fn to_account_id(address: &H160) -> AccountId32 { + let mut account_id = AccountId32::new([0xEE; 32]); + >::as_mut(&mut account_id)[..20] + .copy_from_slice(address.as_bytes()); + account_id + } + + fn to_account_id_contract(address: &H160) -> AccountId32 { + Self::to_account_id(address) + } +} + +/// Determine the address of a contract using CREATE semantics. +pub fn create1(deployer: &H160, nonce: u64) -> H160 { + let mut list = rlp::RlpStream::new_list(2); + list.append(&deployer.as_bytes()); + list.append(&nonce); + let hash = keccak_256(&list.out()); + H160::from_slice(&hash[12..]) +} + +/// Determine the address of a contract using the CREATE2 semantics. +pub fn create2(deployer: &H160, code: &[u8], input_data: &[u8], salt: &[u8; 32]) -> H160 { + let init_code_hash = { + let init_code: Vec = code.into_iter().chain(input_data).cloned().collect(); + keccak_256(init_code.as_ref()) + }; + let mut bytes = [0; 85]; + bytes[0] = 0xff; + bytes[1..21].copy_from_slice(deployer.as_bytes()); + bytes[21..53].copy_from_slice(salt); + bytes[53..85].copy_from_slice(&init_code_hash); + let hash = keccak_256(&bytes); + H160::from_slice(&hash[12..]) +} + +#[cfg(test)] +mod test { + use super::*; + use crate::test_utils::ALICE_ADDR; + use sp_core::{hex2array, H160}; + + #[test] + fn create1_works() { + assert_eq!( + create1(&ALICE_ADDR, 1u64), + H160(hex2array!("c851da37e4e8d3a20d8d56be2963934b4ad71c3b")), + ) + } + + #[test] + fn create2_works() { + assert_eq!( + create2( + &ALICE_ADDR, + &hex2array!("600060005560016000"), + &hex2array!("55"), + &hex2array!("1234567890123456789012345678901234567890123456789012345678901234") + ), + H160(hex2array!("7f31e795e5836a19a8f919ab5a9de9a197ecd2b6")), + ) } } diff --git a/substrate/frame/revive/src/benchmarking/call_builder.rs b/substrate/frame/revive/src/benchmarking/call_builder.rs index 654ba3de4f729756844415010b3b127732f5b918..8a859a3a5089933c4aa8c7420a54c315f106babc 100644 --- a/substrate/frame/revive/src/benchmarking/call_builder.rs +++ b/substrate/frame/revive/src/benchmarking/call_builder.rs @@ -16,17 +16,17 @@ // limitations under the License. use crate::{ + address::AddressMapper, benchmarking::{default_deposit_limit, Contract, WasmModule}, exec::{ExportedFunction, Ext, Key, Stack}, storage::meter::Meter, transient_storage::MeterEntry, wasm::{ApiVersion, PreparedCall, Runtime}, - BalanceOf, Config, DebugBuffer, Error, GasMeter, Origin, TypeInfo, WasmBlob, Weight, + BalanceOf, Config, DebugBuffer, Error, GasMeter, MomentOf, Origin, WasmBlob, Weight, }; use alloc::{vec, vec::Vec}; -use codec::{Encode, HasCompact}; -use core::fmt::Debug; use frame_benchmarking::benchmarking; +use sp_core::U256; type StackExt<'a, T> = Stack<'a, T, WasmBlob>; @@ -46,7 +46,8 @@ pub struct CallSetup { impl Default for CallSetup where T: Config + pallet_balances::Config, - as HasCompact>::Type: Clone + Eq + PartialEq + Debug + TypeInfo + Encode, + BalanceOf: Into + TryFrom, + MomentOf: Into, { fn default() -> Self { Self::new(WasmModule::dummy()) @@ -56,10 +57,11 @@ where impl CallSetup where T: Config + pallet_balances::Config, - as HasCompact>::Type: Clone + Eq + PartialEq + Debug + TypeInfo + Encode, + BalanceOf: Into + TryFrom, + MomentOf: Into, { /// Setup a new call for the given module. - pub fn new(module: WasmModule) -> Self { + pub fn new(module: WasmModule) -> Self { let contract = Contract::::new(module.clone(), vec![]).unwrap(); let dest = contract.account_id.clone(); let origin = Origin::from_account_id(contract.caller.clone()); @@ -74,7 +76,10 @@ where // Whitelist the contract's contractInfo as it is already accounted for in the call // benchmark benchmarking::add_to_whitelist( - crate::ContractInfoOf::::hashed_key_for(&contract.account_id).into(), + crate::ContractInfoOf::::hashed_key_for(&T::AddressMapper::to_address( + &contract.account_id, + )) + .into(), ); Self { @@ -138,7 +143,7 @@ where /// Build the call stack. pub fn ext(&mut self) -> (StackExt<'_, T>, WasmBlob) { let mut ext = StackExt::bench_new_call( - self.dest.clone(), + T::AddressMapper::to_address(&self.dest), self.origin.clone(), &mut self.gas_meter, &mut self.storage_meter, diff --git a/substrate/frame/revive/src/benchmarking/code.rs b/substrate/frame/revive/src/benchmarking/code.rs index eba4710d8a2c50a8bc6b894c80cd933a556c22c5..ede3bb69b11bbcf1e631384fdaec6e02a3e5561f 100644 --- a/substrate/frame/revive/src/benchmarking/code.rs +++ b/substrate/frame/revive/src/benchmarking/code.rs @@ -24,19 +24,19 @@ //! we define this simple definition of a contract that can be passed to `create_code` that //! compiles it down into a `WasmModule` that can be used as a contract's code. -use crate::Config; use alloc::vec::Vec; use pallet_revive_fixtures::bench as bench_fixtures; -use sp_runtime::traits::Hash; +use sp_core::H256; +use sp_io::hashing::keccak_256; /// A wasm module ready to be put on chain. #[derive(Clone)] -pub struct WasmModule { +pub struct WasmModule { pub code: Vec, - pub hash: ::Output, + pub hash: H256, } -impl WasmModule { +impl WasmModule { /// Return a contract code that does nothing. pub fn dummy() -> Self { Self::new(bench_fixtures::DUMMY.to_vec()) @@ -63,7 +63,7 @@ impl WasmModule { } fn new(code: Vec) -> Self { - let hash = T::Hashing::hash(&code); - Self { code, hash } + let hash = keccak_256(&code); + Self { code, hash: H256(hash) } } } diff --git a/substrate/frame/revive/src/benchmarking/mod.rs b/substrate/frame/revive/src/benchmarking/mod.rs index b4bd028d6f032500fc6a4d33be34b5536595d17f..ebafb6c7054ae7c1208c819f9533eca328d06134 100644 --- a/substrate/frame/revive/src/benchmarking/mod.rs +++ b/substrate/frame/revive/src/benchmarking/mod.rs @@ -25,7 +25,6 @@ use self::{call_builder::CallSetup, code::WasmModule}; use crate::{ exec::{Key, MomentOf}, limits, - migration::codegen::LATEST_MIGRATION_VERSION, storage::WriteOutcome, Pallet as Contracts, *, }; @@ -34,7 +33,6 @@ use codec::{Encode, MaxEncodedLen}; use frame_benchmarking::v2::*; use frame_support::{ self, assert_ok, - pallet_prelude::StorageVersion, storage::child, traits::{fungible::InspectHold, Currency}, weights::{Weight, WeightMeter}, @@ -65,23 +63,28 @@ const UNBALANCED_TRIE_LAYERS: u32 = 20; struct Contract { caller: T::AccountId, account_id: T::AccountId, - addr: AccountIdLookupOf, } impl Contract where T: Config + pallet_balances::Config, - as HasCompact>::Type: Clone + Eq + PartialEq + Debug + TypeInfo + Encode, + BalanceOf: Into + TryFrom, + MomentOf: Into, { + /// Returns the address of the contract. + fn address(&self) -> H160 { + T::AddressMapper::to_address(&self.account_id) + } + /// Create new contract and use a default account id as instantiator. - fn new(module: WasmModule, data: Vec) -> Result, &'static str> { + fn new(module: WasmModule, data: Vec) -> Result, &'static str> { Self::with_index(0, module, data) } /// Create new contract and use an account id derived from the supplied index as instantiator. fn with_index( index: u32, - module: WasmModule, + module: WasmModule, data: Vec, ) -> Result, &'static str> { Self::with_caller(account("instantiator", index, 0), module, data) @@ -90,11 +93,11 @@ where /// Create new contract and use the supplied `caller` as instantiator. fn with_caller( caller: T::AccountId, - module: WasmModule, + module: WasmModule, data: Vec, ) -> Result, &'static str> { T::Currency::set_balance(&caller, caller_funding::()); - let salt = vec![0xff]; + let salt = Some([0xffu8; 32]); let outcome = Contracts::::bare_instantiate( RawOrigin::Signed(caller.clone()).into(), @@ -108,20 +111,17 @@ where CollectEvents::Skip, ); - let addr = outcome.result?.account_id; - let result = Contract { caller, account_id: addr.clone(), addr: T::Lookup::unlookup(addr) }; + let address = outcome.result?.addr; + let account_id = T::AddressMapper::to_account_id_contract(&address); + let result = Contract { caller, account_id: account_id.clone() }; - ContractInfoOf::::insert(&result.account_id, result.info()?); + ContractInfoOf::::insert(&address, result.info()?); Ok(result) } /// Create a new contract with the supplied storage item count and size each. - fn with_storage( - code: WasmModule, - stor_num: u32, - stor_size: u32, - ) -> Result { + fn with_storage(code: WasmModule, stor_num: u32, stor_size: u32) -> Result { let contract = Contract::::new(code, vec![])?; let storage_items = (0..stor_num) .map(|i| { @@ -143,12 +143,12 @@ where info.write(&Key::Fix(item.0), Some(item.1.clone()), None, false) .map_err(|_| "Failed to write storage to restoration dest")?; } - >::insert(&self.account_id, info); + >::insert(T::AddressMapper::to_address(&self.account_id), info); Ok(()) } /// Create a new contract with the specified unbalanced storage trie. - fn with_unbalanced_storage_trie(code: WasmModule, key: &[u8]) -> Result { + fn with_unbalanced_storage_trie(code: WasmModule, key: &[u8]) -> Result { if (key.len() as u32) < (UNBALANCED_TRIE_LAYERS + 1) / 2 { return Err("Key size too small to create the specified trie"); } @@ -179,7 +179,8 @@ where /// Get the `ContractInfo` of the `addr` or an error if it no longer exists. fn address_info(addr: &T::AccountId) -> Result, &'static str> { - ContractInfoOf::::get(addr).ok_or("Expected contract to exist at this point.") + ContractInfoOf::::get(T::AddressMapper::to_address(addr)) + .ok_or("Expected contract to exist at this point.") } /// Get the `ContractInfo` of this contract or an error if it no longer exists. @@ -193,12 +194,12 @@ where } /// Returns `true` iff all storage entries related to code storage exist. - fn code_exists(hash: &CodeHash) -> bool { + fn code_exists(hash: &sp_core::H256) -> bool { >::contains_key(hash) && >::contains_key(&hash) } /// Returns `true` iff no storage entry related to code storage exist. - fn code_removed(hash: &CodeHash) -> bool { + fn code_removed(hash: &sp_core::H256) -> bool { !>::contains_key(hash) && !>::contains_key(&hash) } } @@ -218,9 +219,10 @@ fn default_deposit_limit() -> BalanceOf { #[benchmarks( where - as codec::HasCompact>::Type: Clone + Eq + PartialEq + core::fmt::Debug + scale_info::TypeInfo + codec::Encode, + BalanceOf: Into + TryFrom, T: Config + pallet_balances::Config, - BalanceOf: From< as Currency>::Balance>, + MomentOf: Into, + ::RuntimeEvent: From>, as Currency>::Balance: From>, )] mod benchmarks { @@ -248,73 +250,6 @@ mod benchmarks { Ok(()) } - // This benchmarks the weight of executing Migration::migrate to execute a noop migration. - #[benchmark(pov_mode = Measured)] - fn migration_noop() { - let version = LATEST_MIGRATION_VERSION; - StorageVersion::new(version).put::>(); - #[block] - { - Migration::::migrate(&mut WeightMeter::new()); - } - assert_eq!(StorageVersion::get::>(), version); - } - - // This benchmarks the weight of dispatching migrate to execute 1 `NoopMigration` - #[benchmark(pov_mode = Measured)] - fn migrate() { - let latest_version = LATEST_MIGRATION_VERSION; - StorageVersion::new(latest_version - 2).put::>(); - as frame_support::traits::OnRuntimeUpgrade>::on_runtime_upgrade(); - - #[extrinsic_call] - _(RawOrigin::Signed(whitelisted_caller()), Weight::MAX); - - assert_eq!(StorageVersion::get::>(), latest_version - 1); - } - - // This benchmarks the weight of running on_runtime_upgrade when there are no migration in - // progress. - #[benchmark(pov_mode = Measured)] - fn on_runtime_upgrade_noop() { - let latest_version = LATEST_MIGRATION_VERSION; - StorageVersion::new(latest_version).put::>(); - #[block] - { - as frame_support::traits::OnRuntimeUpgrade>::on_runtime_upgrade(); - } - assert!(MigrationInProgress::::get().is_none()); - } - - // This benchmarks the weight of running on_runtime_upgrade when there is a migration in - // progress. - #[benchmark(pov_mode = Measured)] - fn on_runtime_upgrade_in_progress() { - let latest_version = LATEST_MIGRATION_VERSION; - StorageVersion::new(latest_version - 2).put::>(); - let v = vec![42u8].try_into().ok(); - MigrationInProgress::::set(v.clone()); - #[block] - { - as frame_support::traits::OnRuntimeUpgrade>::on_runtime_upgrade(); - } - assert!(MigrationInProgress::::get().is_some()); - assert_eq!(MigrationInProgress::::get(), v); - } - - // This benchmarks the weight of running on_runtime_upgrade when there is a migration to - // process. - #[benchmark(pov_mode = Measured)] - fn on_runtime_upgrade() { - let latest_version = LATEST_MIGRATION_VERSION; - StorageVersion::new(latest_version - 2).put::>(); - #[block] - { - as frame_support::traits::OnRuntimeUpgrade>::on_runtime_upgrade(); - } - assert!(MigrationInProgress::::get().is_some()); - } - // This benchmarks the overhead of loading a code of size `c` byte from storage and into // the execution engine. This does **not** include the actual execution for which the gas meter // is responsible. This is achieved by generating all code to the `deploy` function @@ -323,12 +258,12 @@ mod benchmarks { // call_with_code_per_byte(0)`. #[benchmark(pov_mode = Measured)] fn call_with_code_per_byte( - c: Linear<0, { T::MaxCodeLen::get() }>, + c: Linear<0, { limits::code::BLOB_BYTES }>, ) -> Result<(), BenchmarkError> { let instance = Contract::::with_caller(whitelisted_caller(), WasmModule::sized(c), vec![])?; let value = Pallet::::min_balance(); - let callee = instance.addr; + let callee = T::AddressMapper::to_address(&instance.account_id); let storage_deposit = default_deposit_limit::(); #[extrinsic_call] @@ -346,27 +281,27 @@ mod benchmarks { // `c`: Size of the code in bytes. // `i`: Size of the input in bytes. - // `s`: Size of the salt in bytes. #[benchmark(pov_mode = Measured)] fn instantiate_with_code( - c: Linear<0, { T::MaxCodeLen::get() }>, - i: Linear<0, { limits::MEMORY_BYTES }>, - s: Linear<0, { limits::MEMORY_BYTES }>, + c: Linear<0, { limits::code::BLOB_BYTES }>, + i: Linear<0, { limits::code::BLOB_BYTES }>, ) { let input = vec![42u8; i as usize]; - let salt = vec![42u8; s as usize]; + let salt = [42u8; 32]; let value = Pallet::::min_balance(); let caller = whitelisted_caller(); T::Currency::set_balance(&caller, caller_funding::()); - let WasmModule { code, hash, .. } = WasmModule::::sized(c); + let WasmModule { code, .. } = WasmModule::sized(c); let origin = RawOrigin::Signed(caller.clone()); - let addr = Contracts::::contract_address(&caller, &hash, &input, &salt); + let deployer = T::AddressMapper::to_address(&caller); + let addr = crate::address::create2(&deployer, &code, &input, &salt); + let account_id = T::AddressMapper::to_account_id_contract(&addr); let storage_deposit = default_deposit_limit::(); #[extrinsic_call] - _(origin, value, Weight::MAX, storage_deposit, code, input, salt); + _(origin, value, Weight::MAX, storage_deposit, code, input, Some(salt)); let deposit = - T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &addr); + T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &account_id); // uploading the code reserves some balance in the callers account let code_deposit = T::Currency::balance_on_hold(&HoldReason::CodeUploadDepositReserve.into(), &caller); @@ -375,27 +310,26 @@ mod benchmarks { caller_funding::() - value - deposit - code_deposit - Pallet::::min_balance(), ); // contract has the full value - assert_eq!(T::Currency::balance(&addr), value + Pallet::::min_balance()); + assert_eq!(T::Currency::balance(&account_id), value + Pallet::::min_balance()); } // `i`: Size of the input in bytes. - // `s`: Size of the salt in bytes. + // `s`: Size of e salt in bytes. #[benchmark(pov_mode = Measured)] - fn instantiate( - i: Linear<0, { limits::MEMORY_BYTES }>, - s: Linear<0, { limits::MEMORY_BYTES }>, - ) -> Result<(), BenchmarkError> { + fn instantiate(i: Linear<0, { limits::code::BLOB_BYTES }>) -> Result<(), BenchmarkError> { let input = vec![42u8; i as usize]; - let salt = vec![42u8; s as usize]; + let salt = [42u8; 32]; let value = Pallet::::min_balance(); let caller = whitelisted_caller(); T::Currency::set_balance(&caller, caller_funding::()); let origin = RawOrigin::Signed(caller.clone()); - let WasmModule { code, .. } = WasmModule::::dummy(); + let WasmModule { code, .. } = WasmModule::dummy(); let storage_deposit = default_deposit_limit::(); + let deployer = T::AddressMapper::to_address(&caller); + let addr = crate::address::create2(&deployer, &code, &input, &salt); let hash = Contracts::::bare_upload_code(origin.into(), code, storage_deposit)?.code_hash; - let addr = Contracts::::contract_address(&caller, &hash, &input, &salt); + let account_id = T::AddressMapper::to_account_id_contract(&addr); #[extrinsic_call] _( @@ -405,20 +339,20 @@ mod benchmarks { storage_deposit, hash, input, - salt, + Some(salt), ); let deposit = - T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &addr); + T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &account_id); let code_deposit = - T::Currency::balance_on_hold(&HoldReason::CodeUploadDepositReserve.into(), &caller); + T::Currency::balance_on_hold(&HoldReason::CodeUploadDepositReserve.into(), &account_id); // value was removed from the caller assert_eq!( - T::Currency::balance(&caller), + T::Currency::total_balance(&caller), caller_funding::() - value - deposit - code_deposit - Pallet::::min_balance(), ); // contract has the full value - assert_eq!(T::Currency::balance(&addr), value + Pallet::::min_balance()); + assert_eq!(T::Currency::balance(&account_id), value + Pallet::::min_balance()); Ok(()) } @@ -437,7 +371,7 @@ mod benchmarks { Contract::::with_caller(whitelisted_caller(), WasmModule::dummy(), vec![])?; let value = Pallet::::min_balance(); let origin = RawOrigin::Signed(instance.caller.clone()); - let callee = instance.addr.clone(); + let callee = T::AddressMapper::to_address(&instance.account_id); let before = T::Currency::balance(&instance.account_id); let storage_deposit = default_deposit_limit::(); #[extrinsic_call] @@ -467,10 +401,10 @@ mod benchmarks { // It creates a maximum number of metering blocks per byte. // `c`: Size of the code in bytes. #[benchmark(pov_mode = Measured)] - fn upload_code(c: Linear<0, { T::MaxCodeLen::get() }>) { + fn upload_code(c: Linear<0, { limits::code::BLOB_BYTES }>) { let caller = whitelisted_caller(); T::Currency::set_balance(&caller, caller_funding::()); - let WasmModule { code, hash, .. } = WasmModule::::sized(c); + let WasmModule { code, hash, .. } = WasmModule::sized(c); let origin = RawOrigin::Signed(caller.clone()); let storage_deposit = default_deposit_limit::(); #[extrinsic_call] @@ -487,7 +421,7 @@ mod benchmarks { fn remove_code() -> Result<(), BenchmarkError> { let caller = whitelisted_caller(); T::Currency::set_balance(&caller, caller_funding::()); - let WasmModule { code, hash, .. } = WasmModule::::dummy(); + let WasmModule { code, hash, .. } = WasmModule::dummy(); let origin = RawOrigin::Signed(caller.clone()); let storage_deposit = default_deposit_limit::(); let uploaded = @@ -508,12 +442,12 @@ mod benchmarks { let instance = >::with_caller(whitelisted_caller(), WasmModule::dummy(), vec![])?; // we just add some bytes so that the code hash is different - let WasmModule { code, .. } = >::dummy_unique(128); + let WasmModule { code, .. } = WasmModule::dummy_unique(128); let origin = RawOrigin::Signed(instance.caller.clone()); let storage_deposit = default_deposit_limit::(); let hash = >::bare_upload_code(origin.into(), code, storage_deposit)?.code_hash; - let callee = instance.addr.clone(); + let callee = T::AddressMapper::to_address(&instance.account_id); assert_ne!(instance.info()?.code_hash, hash); #[extrinsic_call] _(RawOrigin::Root, callee, hash); @@ -534,19 +468,19 @@ mod benchmarks { #[benchmark(pov_mode = Measured)] fn seal_caller() { - let len = ::max_encoded_len() as u32; - build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]); + let len = H160::len_bytes(); + build_runtime!(runtime, memory: [vec![0u8; len as _], ]); let result; #[block] { - result = runtime.bench_caller(memory.as_mut_slice(), 4, 0); + result = runtime.bench_caller(memory.as_mut_slice(), 0); } assert_ok!(result); assert_eq!( - &::decode(&mut &memory[4..]).unwrap(), - runtime.ext().caller().account_id().unwrap() + ::decode(&mut &memory[..]).unwrap(), + T::AddressMapper::to_address(&runtime.ext().caller().account_id().unwrap()) ); } @@ -569,35 +503,35 @@ mod benchmarks { #[benchmark(pov_mode = Measured)] fn seal_code_hash() { let contract = Contract::::with_index(1, WasmModule::dummy(), vec![]).unwrap(); - let len = as MaxEncodedLen>::max_encoded_len() as u32; - build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], contract.account_id.encode(), ]); + let len = ::max_encoded_len() as u32; + build_runtime!(runtime, memory: [vec![0u8; len as _], contract.account_id.encode(), ]); let result; #[block] { - result = runtime.bench_code_hash(memory.as_mut_slice(), 4 + len, 4, 0); + result = runtime.bench_code_hash(memory.as_mut_slice(), len, 0); } assert_ok!(result); assert_eq!( - as Decode>::decode(&mut &memory[4..]).unwrap(), + ::decode(&mut &memory[..]).unwrap(), contract.info().unwrap().code_hash ); } #[benchmark(pov_mode = Measured)] fn seal_own_code_hash() { - let len = as MaxEncodedLen>::max_encoded_len() as u32; - build_runtime!(runtime, contract, memory: [len.to_le_bytes(), vec![0u8; len as _], ]); + let len = ::max_encoded_len() as u32; + build_runtime!(runtime, contract, memory: [vec![0u8; len as _], ]); let result; #[block] { - result = runtime.bench_own_code_hash(memory.as_mut_slice(), 4, 0); + result = runtime.bench_own_code_hash(memory.as_mut_slice(), 0); } assert_ok!(result); assert_eq!( - as Decode>::decode(&mut &memory[4..]).unwrap(), + ::decode(&mut &memory[..]).unwrap(), contract.info().unwrap().code_hash ); } @@ -631,19 +565,16 @@ mod benchmarks { #[benchmark(pov_mode = Measured)] fn seal_address() { - let len = as MaxEncodedLen>::max_encoded_len() as u32; - build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]); + let len = H160::len_bytes(); + build_runtime!(runtime, memory: [vec![0u8; len as _], ]); let result; #[block] { - result = runtime.bench_address(memory.as_mut_slice(), 4, 0); + result = runtime.bench_address(memory.as_mut_slice(), 0); } assert_ok!(result); - assert_eq!( - &::decode(&mut &memory[4..]).unwrap(), - runtime.ext().address() - ); + assert_eq!(::decode(&mut &memory[..]).unwrap(), runtime.ext().address()); } #[benchmark(pov_mode = Measured)] @@ -667,85 +598,134 @@ mod benchmarks { #[benchmark(pov_mode = Measured)] fn seal_balance() { - let len = ::max_encoded_len() as u32; - build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]); + build_runtime!(runtime, memory: [[0u8;32], ]); let result; #[block] { - result = runtime.bench_balance(memory.as_mut_slice(), 4, 0); + result = runtime.bench_balance(memory.as_mut_slice(), 0); } assert_ok!(result); - assert_eq!( - ::decode(&mut &memory[4..]).unwrap(), - runtime.ext().balance().into() + assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().balance()); + } + + #[benchmark(pov_mode = Measured)] + fn seal_balance_of() { + let len = ::max_encoded_len(); + let account = account::("target", 0, 0); + let address = T::AddressMapper::to_address(&account); + let balance = Pallet::::min_balance() * 2u32.into(); + T::Currency::set_balance(&account, balance); + + build_runtime!(runtime, memory: [vec![0u8; len], address.0, ]); + + let result; + #[block] + { + result = runtime.bench_balance_of(memory.as_mut_slice(), len as u32, 0); + } + + assert_ok!(result); + assert_eq!(U256::from_little_endian(&memory[..len]), runtime.ext().balance_of(&address)); + } + + #[benchmark(pov_mode = Measured)] + fn seal_get_immutable_data(n: Linear<1, { limits::IMMUTABLE_BYTES }>) { + let len = n as usize; + let immutable_data = vec![1u8; len]; + + build_runtime!(runtime, contract, memory: [(len as u32).encode(), vec![0u8; len],]); + + >::insert::<_, BoundedVec<_, _>>( + contract.address(), + immutable_data.clone().try_into().unwrap(), ); + + let result; + #[block] + { + result = runtime.bench_get_immutable_data(memory.as_mut_slice(), 4, 0 as u32); + } + + assert_ok!(result); + assert_eq!(&memory[0..4], (len as u32).encode()); + assert_eq!(&memory[4..len + 4], &immutable_data); } #[benchmark(pov_mode = Measured)] - fn seal_value_transferred() { - let len = ::max_encoded_len() as u32; - build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]); + fn seal_set_immutable_data(n: Linear<1, { limits::IMMUTABLE_BYTES }>) { + let len = n as usize; + let mut memory = vec![1u8; len]; + let mut setup = CallSetup::::default(); + let input = setup.data(); + let (mut ext, _) = setup.ext(); + ext.override_export(crate::debug::ExportedFunction::Constructor); + + let mut runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, input); + let result; #[block] { - result = runtime.bench_value_transferred(memory.as_mut_slice(), 4, 0); + result = runtime.bench_set_immutable_data(memory.as_mut_slice(), 0, n); } + assert_ok!(result); assert_eq!( - ::decode(&mut &memory[4..]).unwrap(), - runtime.ext().value_transferred().into() + &memory[..], + &>::get(setup.contract().address()).unwrap()[..] ); } + #[benchmark(pov_mode = Measured)] + fn seal_value_transferred() { + build_runtime!(runtime, memory: [[0u8;32], ]); + let result; + #[block] + { + result = runtime.bench_value_transferred(memory.as_mut_slice(), 0); + } + assert_ok!(result); + assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().value_transferred()); + } + #[benchmark(pov_mode = Measured)] fn seal_minimum_balance() { - let len = ::max_encoded_len() as u32; - build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]); + build_runtime!(runtime, memory: [[0u8;32], ]); let result; #[block] { - result = runtime.bench_minimum_balance(memory.as_mut_slice(), 4, 0); + result = runtime.bench_minimum_balance(memory.as_mut_slice(), 0); } assert_ok!(result); - assert_eq!( - ::decode(&mut &memory[4..]).unwrap(), - runtime.ext().minimum_balance().into() - ); + assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().minimum_balance()); } #[benchmark(pov_mode = Measured)] fn seal_block_number() { - let len = as MaxEncodedLen>::max_encoded_len() as u32; - build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]); + build_runtime!(runtime, memory: [[0u8;32], ]); let result; #[block] { - result = runtime.bench_block_number(memory.as_mut_slice(), 4, 0); + result = runtime.bench_block_number(memory.as_mut_slice(), 0); } assert_ok!(result); - assert_eq!( - >::decode(&mut &memory[4..]).unwrap(), - runtime.ext().block_number() - ); + assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().block_number()); } #[benchmark(pov_mode = Measured)] fn seal_now() { - let len = as MaxEncodedLen>::max_encoded_len() as u32; - build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]); + build_runtime!(runtime, memory: [[0u8;32], ]); let result; #[block] { - result = runtime.bench_now(memory.as_mut_slice(), 4, 0); + result = runtime.bench_now(memory.as_mut_slice(), 0); } assert_ok!(result); - assert_eq!(>::decode(&mut &memory[4..]).unwrap(), *runtime.ext().now()); + assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().now()); } #[benchmark(pov_mode = Measured)] fn seal_weight_to_fee() { - let len = ::max_encoded_len() as u32; - build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]); + build_runtime!(runtime, memory: [[0u8;32], ]); let weight = Weight::from_parts(500_000, 300_000); let result; #[block] @@ -754,19 +734,15 @@ mod benchmarks { memory.as_mut_slice(), weight.ref_time(), weight.proof_size(), - 4, 0, ); } assert_ok!(result); - assert_eq!( - >::decode(&mut &memory[4..]).unwrap(), - runtime.ext().get_weight_price(weight) - ); + assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().get_weight_price(weight)); } #[benchmark(pov_mode = Measured)] - fn seal_input(n: Linear<0, { limits::MEMORY_BYTES - 4 }>) { + fn seal_input(n: Linear<0, { limits::code::BLOB_BYTES - 4 }>) { let mut setup = CallSetup::::default(); let (mut ext, _) = setup.ext(); let mut runtime = crate::wasm::Runtime::new(&mut ext, vec![42u8; n as usize]); @@ -781,7 +757,7 @@ mod benchmarks { } #[benchmark(pov_mode = Measured)] - fn seal_return(n: Linear<0, { limits::MEMORY_BYTES - 4 }>) { + fn seal_return(n: Linear<0, { limits::code::BLOB_BYTES - 4 }>) { build_runtime!(runtime, memory: [n.to_le_bytes(), vec![42u8; n as usize], ]); let result; @@ -809,7 +785,7 @@ mod benchmarks { build_runtime!(runtime, memory: [beneficiary.encode(),]); (0..n).for_each(|i| { - let new_code = WasmModule::::dummy_unique(65 + i); + let new_code = WasmModule::dummy_unique(65 + i); Contracts::::bare_upload_code(origin.clone().into(), new_code.code, storage_deposit) .unwrap(); runtime.ext().lock_delegate_dependency(new_code.hash).unwrap(); @@ -834,28 +810,33 @@ mod benchmarks { t: Linear<0, { limits::NUM_EVENT_TOPICS as u32 }>, n: Linear<0, { limits::PAYLOAD_BYTES }>, ) { - let topics = (0..t).map(|i| T::Hashing::hash_of(&i)).collect::>().encode(); - let topics_len = topics.len() as u32; - - build_runtime!(runtime, memory: [ - n.to_le_bytes(), - topics, - vec![0u8; n as _], - ]); + let num_topic = t as u32; + let topics = (0..t).map(|i| H256::repeat_byte(i as u8)).collect::>(); + let topics_data = + topics.iter().flat_map(|hash| hash.as_bytes().to_vec()).collect::>(); + let data = vec![42u8; n as _]; + build_runtime!(runtime, instance, memory: [ topics_data, data, ]); let result; #[block] { result = runtime.bench_deposit_event( memory.as_mut_slice(), - 4, // topics_ptr - topics_len, // topics_len - 4 + topics_len, // data_ptr - 0, // data_len + 0, // topics_ptr + num_topic, + topics_data.len() as u32, // data_ptr + n, // data_len ); } - assert_ok!(result); + + let events = System::::events(); + let record = &events[events.len() - 1]; + + assert_eq!( + record.event, + crate::Event::ContractEmitted { contract: instance.address(), data, topics }.into(), + ); } // Benchmark debug_message call @@ -866,7 +847,7 @@ mod benchmarks { // buffer size, whichever is less. #[benchmark] fn seal_debug_message( - i: Linear<0, { (limits::MEMORY_BYTES).min(limits::DEBUG_BUFFER_BYTES) }>, + i: Linear<0, { (limits::code::BLOB_BYTES).min(limits::DEBUG_BUFFER_BYTES) }>, ) { let mut setup = CallSetup::::default(); setup.enable_debug_message(); @@ -1441,7 +1422,7 @@ mod benchmarks { let account_bytes = account.encode(); let account_len = account_bytes.len() as u32; - let value_bytes = value.encode(); + let value_bytes = Into::::into(value).encode(); let mut memory = memory!(account_bytes, value_bytes,); let result; @@ -1460,17 +1441,17 @@ mod benchmarks { // t: with or without some value to transfer // i: size of the input data #[benchmark(pov_mode = Measured)] - fn seal_call(t: Linear<0, 1>, i: Linear<0, { limits::MEMORY_BYTES }>) { + fn seal_call(t: Linear<0, 1>, i: Linear<0, { limits::code::BLOB_BYTES }>) { let Contract { account_id: callee, .. } = Contract::::with_index(1, WasmModule::dummy(), vec![]).unwrap(); let callee_bytes = callee.encode(); let callee_len = callee_bytes.len() as u32; let value: BalanceOf = t.into(); - let value_bytes = value.encode(); + let value_bytes = Into::::into(value).encode(); let deposit: BalanceOf = (u32::MAX - 100).into(); - let deposit_bytes = deposit.encode(); + let deposit_bytes = Into::::into(deposit).encode(); let deposit_len = deposit_bytes.len() as u32; let mut setup = CallSetup::::default(); @@ -1534,22 +1515,19 @@ mod benchmarks { // t: value to transfer // i: size of input in bytes - // s: size of salt in bytes #[benchmark(pov_mode = Measured)] - fn seal_instantiate( - i: Linear<0, { limits::MEMORY_BYTES }>, - s: Linear<0, { limits::MEMORY_BYTES }>, - ) -> Result<(), BenchmarkError> { + fn seal_instantiate(i: Linear<0, { limits::code::BLOB_BYTES }>) -> Result<(), BenchmarkError> { + let code = WasmModule::dummy(); let hash = Contract::::with_index(1, WasmModule::dummy(), vec![])?.info()?.code_hash; let hash_bytes = hash.encode(); let hash_len = hash_bytes.len() as u32; let value: BalanceOf = 1u32.into(); - let value_bytes = value.encode(); + let value_bytes = Into::::into(value).encode(); let value_len = value_bytes.len() as u32; let deposit: BalanceOf = 0u32.into(); - let deposit_bytes = deposit.encode(); + let deposit_bytes = Into::::into(deposit).encode(); let deposit_len = deposit_bytes.len() as u32; let mut setup = CallSetup::::default(); @@ -1561,8 +1539,10 @@ mod benchmarks { let mut runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, vec![]); let input = vec![42u8; i as _]; - let salt = vec![42u8; s as _]; - let addr = Contracts::::contract_address(&account_id, &hash, &input, &salt); + let salt = [42u8; 32]; + let deployer = T::AddressMapper::to_address(&account_id); + let addr = crate::address::create2(&deployer, &code.code, &input, &salt); + let account_id = T::AddressMapper::to_account_id_contract(&addr); let mut memory = memory!(hash_bytes, deposit_bytes, value_bytes, input, salt,); let mut offset = { @@ -1588,23 +1568,21 @@ mod benchmarks { offset(value_len), // input_data_ptr i, // input_data_len SENTINEL, // address_ptr - 0, // address_len_ptr SENTINEL, // output_ptr 0, // output_len_ptr offset(i), // salt_ptr - s, // salt_len ); } assert_ok!(result); assert!(ContractInfoOf::::get(&addr).is_some()); - assert_eq!(T::Currency::balance(&addr), Pallet::::min_balance() + value); + assert_eq!(T::Currency::balance(&account_id), Pallet::::min_balance() + value); Ok(()) } // `n`: Input to hash in bytes #[benchmark(pov_mode = Measured)] - fn seal_hash_sha2_256(n: Linear<0, { limits::MEMORY_BYTES }>) { + fn seal_hash_sha2_256(n: Linear<0, { limits::code::BLOB_BYTES }>) { build_runtime!(runtime, memory: [[0u8; 32], vec![0u8; n as usize], ]); let result; @@ -1618,7 +1596,7 @@ mod benchmarks { // `n`: Input to hash in bytes #[benchmark(pov_mode = Measured)] - fn seal_hash_keccak_256(n: Linear<0, { limits::MEMORY_BYTES }>) { + fn seal_hash_keccak_256(n: Linear<0, { limits::code::BLOB_BYTES }>) { build_runtime!(runtime, memory: [[0u8; 32], vec![0u8; n as usize], ]); let result; @@ -1632,7 +1610,7 @@ mod benchmarks { // `n`: Input to hash in bytes #[benchmark(pov_mode = Measured)] - fn seal_hash_blake2_256(n: Linear<0, { limits::MEMORY_BYTES }>) { + fn seal_hash_blake2_256(n: Linear<0, { limits::code::BLOB_BYTES }>) { build_runtime!(runtime, memory: [[0u8; 32], vec![0u8; n as usize], ]); let result; @@ -1646,7 +1624,7 @@ mod benchmarks { // `n`: Input to hash in bytes #[benchmark(pov_mode = Measured)] - fn seal_hash_blake2_128(n: Linear<0, { limits::MEMORY_BYTES }>) { + fn seal_hash_blake2_128(n: Linear<0, { limits::code::BLOB_BYTES }>) { build_runtime!(runtime, memory: [[0u8; 16], vec![0u8; n as usize], ]); let result; @@ -1661,7 +1639,7 @@ mod benchmarks { // `n`: Message input length to verify in bytes. // need some buffer so the code size does not exceed the max code size. #[benchmark(pov_mode = Measured)] - fn seal_sr25519_verify(n: Linear<0, { T::MaxCodeLen::get() - 255 }>) { + fn seal_sr25519_verify(n: Linear<0, { limits::code::BLOB_BYTES - 255 }>) { let message = (0..n).zip((32u8..127u8).cycle()).map(|(_, c)| c).collect::>(); let message_len = message.len() as u32; @@ -1796,11 +1774,9 @@ mod benchmarks { // Benchmark the execution of instructions. #[benchmark(pov_mode = Ignored)] fn instr(r: Linear<0, INSTR_BENCHMARK_RUNS>) { - // (round, start, div, mult, add) - let input = (r, 1_000u32, 2u32, 3u32, 100u32).encode(); let mut setup = CallSetup::::new(WasmModule::instr()); let (mut ext, module) = setup.ext(); - let prepared = CallSetup::::prepare_call(&mut ext, module, input); + let prepared = CallSetup::::prepare_call(&mut ext, module, r.encode()); #[block] { prepared.call().unwrap(); diff --git a/substrate/frame/revive/src/debug.rs b/substrate/frame/revive/src/debug.rs index 467f4e1ad4910127667ba17eaea3be75266bfbc3..d1fc0823e03dff6719018ee60f0c0668aca37d7f 100644 --- a/substrate/frame/revive/src/debug.rs +++ b/substrate/frame/revive/src/debug.rs @@ -20,6 +20,7 @@ pub use crate::{ primitives::ExecReturnValue, }; use crate::{Config, LOG_TARGET}; +use sp_core::H160; /// Umbrella trait for all interfaces that serves for debugging. pub trait Debugger: Tracing + CallInterceptor {} @@ -43,7 +44,7 @@ pub trait Tracing { /// * `entry_point` - Describes whether the call is the constructor or a regular call. /// * `input_data` - The raw input data of the call. fn new_call_span( - contract_address: &T::AccountId, + contract_address: &H160, entry_point: ExportedFunction, input_data: &[u8], ) -> Self::CallSpan; @@ -62,12 +63,8 @@ pub trait CallSpan { impl Tracing for () { type CallSpan = (); - fn new_call_span( - contract_address: &T::AccountId, - entry_point: ExportedFunction, - input_data: &[u8], - ) { - log::trace!(target: LOG_TARGET, "call {entry_point:?} account: {contract_address:?}, input_data: {input_data:?}") + fn new_call_span(contract_address: &H160, entry_point: ExportedFunction, input_data: &[u8]) { + log::trace!(target: LOG_TARGET, "call {entry_point:?} address: {contract_address:?}, input_data: {input_data:?}") } } @@ -95,7 +92,7 @@ pub trait CallInterceptor { /// is returned. /// * `None` - otherwise, i.e. the call should be executed normally. fn intercept_call( - contract_address: &T::AccountId, + contract_address: &H160, entry_point: ExportedFunction, input_data: &[u8], ) -> Option; @@ -103,7 +100,7 @@ pub trait CallInterceptor { impl CallInterceptor for () { fn intercept_call( - _contract_address: &T::AccountId, + _contract_address: &H160, _entry_point: ExportedFunction, _input_data: &[u8], ) -> Option { diff --git a/substrate/frame/revive/src/exec.rs b/substrate/frame/revive/src/exec.rs index 9740707ae706596ddeb96dc034d8862cd275b0a3..c6d2f205ae226fa4cf92570734b3041782cdaed1 100644 --- a/substrate/frame/revive/src/exec.rs +++ b/substrate/frame/revive/src/exec.rs @@ -16,6 +16,7 @@ // limitations under the License. use crate::{ + address::{self, AddressMapper}, debug::{CallInterceptor, CallSpan, Tracing}, gas::GasMeter, limits, @@ -23,8 +24,8 @@ use crate::{ runtime_decl_for_revive_api::{Decode, Encode, RuntimeDebugNoBound, TypeInfo}, storage::{self, meter::Diff, WriteOutcome}, transient_storage::TransientStorage, - BalanceOf, CodeHash, CodeInfo, CodeInfoOf, Config, ContractInfo, ContractInfoOf, DebugBuffer, - Error, Event, Pallet as Contracts, LOG_TARGET, + BalanceOf, CodeInfo, CodeInfoOf, Config, ContractInfo, ContractInfoOf, DebugBuffer, Error, + Event, ImmutableData, ImmutableDataOf, Pallet as Contracts, LOG_TARGET, }; use alloc::vec::Vec; use core::{fmt::Debug, marker::PhantomData, mem}; @@ -48,24 +49,23 @@ use frame_system::{ use sp_core::{ ecdsa::Public as ECDSAPublic, sr25519::{Public as SR25519Public, Signature as SR25519Signature}, - ConstU32, Get, + ConstU32, Get, H160, H256, U256, }; use sp_io::{crypto::secp256k1_ecdsa_recover_compressed, hashing::blake2_256}; use sp_runtime::{ traits::{BadOrigin, Convert, Dispatchable, Zero}, - DispatchError, + DispatchError, SaturatedConversion, }; pub type AccountIdOf = ::AccountId; pub type MomentOf = <::Time as Time>::Moment; pub type ExecResult = Result; -/// A type that represents a topic of an event. At the moment a hash is used. -pub type TopicOf = ::Hash; - /// Type for variable sized storage key. Used for transparent hashing. type VarSizedKey = BoundedVec>; +const FRAME_ALWAYS_EXISTS_ON_INSTANTIATE: &str = "The return value is only `None` if no contract exists at the specified address. This cannot happen on instantiate or delegate; qed"; + /// Combined key type for both fixed and variable sized storage keys. pub enum Key { /// Variant for fixed sized keys. @@ -183,22 +183,18 @@ pub trait Ext: sealing::Sealed { fn call( &mut self, gas_limit: Weight, - deposit_limit: BalanceOf, - to: AccountIdOf, - value: BalanceOf, + deposit_limit: U256, + to: &H160, + value: U256, input_data: Vec, allows_reentry: bool, read_only: bool, - ) -> Result; + ) -> Result<(), ExecError>; /// Execute code in the current frame. /// /// Returns the code size of the called contract. - fn delegate_call( - &mut self, - code: CodeHash, - input_data: Vec, - ) -> Result; + fn delegate_call(&mut self, code: H256, input_data: Vec) -> Result<(), ExecError>; /// Instantiate a contract from the given code. /// @@ -208,12 +204,12 @@ pub trait Ext: sealing::Sealed { fn instantiate( &mut self, gas_limit: Weight, - deposit_limit: BalanceOf, - code: CodeHash, - value: BalanceOf, + deposit_limit: U256, + code: H256, + value: U256, input_data: Vec, - salt: &[u8], - ) -> Result<(AccountIdOf, ExecReturnValue), ExecError>; + salt: Option<&[u8; 32]>, + ) -> Result; /// Transfer all funds to `beneficiary` and delete the contract. /// @@ -222,10 +218,10 @@ pub trait Ext: sealing::Sealed { /// /// This function will fail if the same contract is present on the contract /// call stack. - fn terminate(&mut self, beneficiary: &AccountIdOf) -> DispatchResult; + fn terminate(&mut self, beneficiary: &H160) -> DispatchResult; /// Transfer some amount of funds into the specified account. - fn transfer(&mut self, to: &AccountIdOf, value: BalanceOf) -> DispatchResult; + fn transfer(&mut self, to: &H160, value: U256) -> DispatchResult; /// Returns the storage entry of the executing account by the given `key`. /// @@ -273,15 +269,15 @@ pub trait Ext: sealing::Sealed { fn caller(&self) -> Origin; /// Check if a contract lives at the specified `address`. - fn is_contract(&self, address: &AccountIdOf) -> bool; + fn is_contract(&self, address: &H160) -> bool; /// Returns the code hash of the contract for the given `address`. /// /// Returns `None` if the `address` does not belong to a contract. - fn code_hash(&self, address: &AccountIdOf) -> Option>; + fn code_hash(&self, address: &H160) -> Option; /// Returns the code hash of the contract being executed. - fn own_code_hash(&mut self) -> &CodeHash; + fn own_code_hash(&mut self) -> &H256; /// Check if the caller of the current contract is the origin of the whole call stack. /// @@ -293,35 +289,57 @@ pub trait Ext: sealing::Sealed { fn caller_is_root(&self) -> bool; /// Returns a reference to the account id of the current contract. - fn address(&self) -> &AccountIdOf; + fn account_id(&self) -> &AccountIdOf; + + /// Returns a reference to the [`H160`] address of the current contract. + fn address(&self) -> H160 { + ::AddressMapper::to_address(self.account_id()) + } + + /// Returns the immutable data of the current contract. + /// + /// Returns `Err(InvalidImmutableAccess)` if called from a constructor. + fn get_immutable_data(&mut self) -> Result; + + /// Set the the immutable data of the current contract. + /// + /// Returns `Err(InvalidImmutableAccess)` if not called from a constructor. + /// + /// Note: Requires &mut self to access the contract info. + fn set_immutable_data(&mut self, data: ImmutableData) -> Result<(), DispatchError>; /// Returns the balance of the current contract. /// /// The `value_transferred` is already added. - fn balance(&self) -> BalanceOf; + fn balance(&self) -> U256; + + /// Returns the balance of the supplied account. + /// + /// The `value_transferred` is already added. + fn balance_of(&self, address: &H160) -> U256; /// Returns the value transferred along with this call. - fn value_transferred(&self) -> BalanceOf; + fn value_transferred(&self) -> U256; - /// Returns a reference to the timestamp of the current block - fn now(&self) -> &MomentOf; + /// Returns the timestamp of the current block + fn now(&self) -> U256; /// Returns the minimum balance that is required for creating an account. - fn minimum_balance(&self) -> BalanceOf; + fn minimum_balance(&self) -> U256; /// Deposit an event with the given topics. /// /// There should not be any duplicates in `topics`. - fn deposit_event(&mut self, topics: Vec>, data: Vec); + fn deposit_event(&mut self, topics: Vec, data: Vec); /// Returns the current block number. - fn block_number(&self) -> BlockNumberFor; + fn block_number(&self) -> U256; /// Returns the maximum allowed size of a storage item. fn max_value_size(&self) -> u32; /// Returns the price for the specified amount of weight. - fn get_weight_price(&self, weight: Weight) -> BalanceOf; + fn get_weight_price(&self, weight: Weight) -> U256; /// Get an immutable reference to the nested gas meter. fn gas_meter(&self) -> &GasMeter; @@ -367,8 +385,8 @@ pub trait Ext: sealing::Sealed { #[cfg(feature = "runtime-benchmarks")] fn transient_storage(&mut self) -> &mut TransientStorage; - /// Sets new code hash for existing contract. - fn set_code_hash(&mut self, hash: CodeHash) -> DispatchResult; + /// Sets new code hash and immutable data for an existing contract. + fn set_code_hash(&mut self, hash: H256) -> DispatchResult; /// Returns the number of times the specified contract exists on the call stack. Delegated calls /// Increment the reference count of a of a stored code by one. @@ -377,7 +395,7 @@ pub trait Ext: sealing::Sealed { /// /// [`Error::CodeNotFound`] is returned if no stored code found having the specified /// `code_hash`. - fn increment_refcount(code_hash: CodeHash) -> DispatchResult; + fn increment_refcount(code_hash: H256) -> DispatchResult; /// Decrement the reference count of a stored code by one. /// @@ -385,7 +403,7 @@ pub trait Ext: sealing::Sealed { /// /// A contract whose reference count dropped to zero isn't automatically removed. A /// `remove_code` transaction must be submitted by the original uploader to do so. - fn decrement_refcount(code_hash: CodeHash); + fn decrement_refcount(code_hash: H256); /// Adds a delegate dependency to [`ContractInfo`]'s `delegate_dependencies` field. /// @@ -398,7 +416,7 @@ pub trait Ext: sealing::Sealed { /// - [`Error::MaxDelegateDependenciesReached`] /// - [`Error::CannotAddSelfAsDelegateDependency`] /// - [`Error::DelegateDependencyAlreadyExists`] - fn lock_delegate_dependency(&mut self, code_hash: CodeHash) -> DispatchResult; + fn lock_delegate_dependency(&mut self, code_hash: H256) -> DispatchResult; /// Removes a delegate dependency from [`ContractInfo`]'s `delegate_dependencies` field. /// @@ -408,7 +426,7 @@ pub trait Ext: sealing::Sealed { /// # Errors /// /// - [`Error::DelegateDependencyNotFound`] - fn unlock_delegate_dependency(&mut self, code_hash: &CodeHash) -> DispatchResult; + fn unlock_delegate_dependency(&mut self, code_hash: &H256) -> DispatchResult; /// Returns the number of locked delegate dependencies. /// @@ -417,6 +435,12 @@ pub trait Ext: sealing::Sealed { /// Check if running in read-only context. fn is_read_only(&self) -> bool; + + /// Returns an immutable reference to the output of the last executed call frame. + fn last_frame_output(&self) -> &ExecReturnValue; + + /// Returns a mutable reference to the output of the last executed call frame. + fn last_frame_output_mut(&mut self) -> &mut ExecReturnValue; } /// Describes the different functions that can be exported by an [`Executable`]. @@ -447,10 +471,7 @@ pub trait Executable: Sized { /// /// # Note /// Charges size base load weight from the gas meter. - fn from_storage( - code_hash: CodeHash, - gas_meter: &mut GasMeter, - ) -> Result; + fn from_storage(code_hash: H256, gas_meter: &mut GasMeter) -> Result; /// Execute the specified exported function and return the result. /// @@ -471,8 +492,11 @@ pub trait Executable: Sized { /// The code info of the executable. fn code_info(&self) -> &CodeInfo; + /// The raw code of the executable. + fn code(&self) -> &[u8]; + /// The code hash of the executable. - fn code_hash(&self) -> &CodeHash; + fn code_hash(&self) -> &H256; } /// The complete call stack of a contract execution. @@ -519,7 +543,7 @@ pub struct Stack<'a, T: Config, E> { /// For each nested contract call or instantiate one frame is created. It holds specific /// information for the said call and caches the in-storage `ContractInfo` data structure. struct Frame { - /// The account id of the executing contract. + /// The address of the executing contract. account_id: T::AccountId, /// The cached in-storage data of the contract. contract_info: CachedContract, @@ -537,6 +561,8 @@ struct Frame { read_only: bool, /// The caller of the currently executing frame which was spawned by `delegate_call`. delegate_caller: Option>, + /// The output of the last executed call frame. + last_frame_output: ExecReturnValue, } /// Used in a delegate call frame arguments in order to override the executable and caller. @@ -567,7 +593,7 @@ enum FrameArgs<'a, T: Config, E> { /// The executable whose `deploy` function is run. executable: E, /// A salt used in the contract address derivation of the new contract. - salt: &'a [u8], + salt: Option<&'a [u8; 32]>, /// The input data is used in the contract address derivation of the new contract. input_data: &'a [u8], }, @@ -588,26 +614,6 @@ enum CachedContract { Terminated, } -impl CachedContract { - /// Return `Some(ContractInfo)` if the contract is in cached state. `None` otherwise. - fn into_contract(self) -> Option> { - if let CachedContract::Cached(contract) = self { - Some(contract) - } else { - None - } - } - - /// Return `Some(&mut ContractInfo)` if the contract is in cached state. `None` otherwise. - fn as_contract(&mut self) -> Option<&mut ContractInfo> { - if let CachedContract::Cached(contract) = self { - Some(contract) - } else { - None - } - } -} - impl Frame { /// Return the `contract_info` of the current contract. fn contract_info(&mut self) -> &mut ContractInfo { @@ -665,10 +671,28 @@ macro_rules! top_frame_mut { } impl CachedContract { + /// Return `Some(ContractInfo)` if the contract is in cached state. `None` otherwise. + fn into_contract(self) -> Option> { + if let CachedContract::Cached(contract) = self { + Some(contract) + } else { + None + } + } + + /// Return `Some(&mut ContractInfo)` if the contract is in cached state. `None` otherwise. + fn as_contract(&mut self) -> Option<&mut ContractInfo> { + if let CachedContract::Cached(contract) = self { + Some(contract) + } else { + None + } + } + /// Load the `contract_info` from storage if necessary. fn load(&mut self, account_id: &T::AccountId) { if let CachedContract::Invalidated = self { - let contract = >::get(&account_id); + let contract = >::get(T::AddressMapper::to_address(account_id)); if let Some(contract) = contract { *self = CachedContract::Cached(contract); } @@ -691,6 +715,8 @@ impl CachedContract { impl<'a, T, E> Stack<'a, T, E> where T: Config, + BalanceOf: Into + TryFrom, + MomentOf: Into, E: Executable, { /// Create and run a new call stack by calling into `dest`. @@ -705,22 +731,26 @@ where /// Result<(ExecReturnValue, CodeSize), (ExecError, CodeSize)> pub fn run_call( origin: Origin, - dest: T::AccountId, + dest: H160, gas_meter: &'a mut GasMeter, storage_meter: &'a mut storage::meter::Meter, value: BalanceOf, input_data: Vec, debug_message: Option<&'a mut DebugBuffer>, - ) -> Result { - let (mut stack, executable) = Self::new( - FrameArgs::Call { dest, cached_info: None, delegated_call: None }, - origin, + ) -> ExecResult { + let dest = T::AddressMapper::to_account_id(&dest); + if let Some((mut stack, executable)) = Self::new( + FrameArgs::Call { dest: dest.clone(), cached_info: None, delegated_call: None }, + origin.clone(), gas_meter, storage_meter, value, debug_message, - )?; - stack.run(executable, input_data) + )? { + stack.run(executable, input_data).map(|_| stack.first_frame.last_frame_output) + } else { + Self::transfer_no_contract(&origin, &dest, value) + } } /// Create and run a new call stack by instantiating a new contract. @@ -740,9 +770,9 @@ where storage_meter: &'a mut storage::meter::Meter, value: BalanceOf, input_data: Vec, - salt: &[u8], + salt: Option<&[u8; 32]>, debug_message: Option<&'a mut DebugBuffer>, - ) -> Result<(T::AccountId, ExecReturnValue), ExecError> { + ) -> Result<(H160, ExecReturnValue), ExecError> { let (mut stack, executable) = Self::new( FrameArgs::Instantiate { sender: origin.clone(), @@ -755,14 +785,17 @@ where storage_meter, value, debug_message, - )?; - let account_id = stack.top_frame().account_id.clone(); - stack.run(executable, input_data).map(|ret| (account_id, ret)) + )? + .expect(FRAME_ALWAYS_EXISTS_ON_INSTANTIATE); + let address = T::AddressMapper::to_address(&stack.top_frame().account_id); + stack + .run(executable, input_data) + .map(|_| (address, stack.first_frame.last_frame_output)) } #[cfg(all(feature = "runtime-benchmarks", feature = "riscv"))] pub fn bench_new_call( - dest: T::AccountId, + dest: H160, origin: Origin, gas_meter: &'a mut GasMeter, storage_meter: &'a mut storage::meter::Meter, @@ -770,7 +803,11 @@ where debug_message: Option<&'a mut DebugBuffer>, ) -> (Self, E) { Self::new( - FrameArgs::Call { dest, cached_info: None, delegated_call: None }, + FrameArgs::Call { + dest: T::AddressMapper::to_account_id(&dest), + cached_info: None, + delegated_call: None, + }, origin, gas_meter, storage_meter, @@ -778,9 +815,13 @@ where debug_message, ) .unwrap() + .unwrap() } /// Create a new call stack. + /// + /// Returns `None` when calling a non existant contract. This is not an error case + /// since this will result in a value transfer. fn new( args: FrameArgs, origin: Origin, @@ -788,8 +829,8 @@ where storage_meter: &'a mut storage::meter::Meter, value: BalanceOf, debug_message: Option<&'a mut DebugBuffer>, - ) -> Result<(Self, E), ExecError> { - let (first_frame, executable) = Self::new_frame( + ) -> Result, ExecError> { + let Some((first_frame, executable)) = Self::new_frame( args, value, gas_meter, @@ -797,7 +838,10 @@ where storage_meter, BalanceOf::::zero(), false, - )?; + )? + else { + return Ok(None); + }; let stack = Self { origin, @@ -812,7 +856,7 @@ where _phantom: Default::default(), }; - Ok((stack, executable)) + Ok(Some((stack, executable))) } /// Construct a new frame. @@ -827,14 +871,20 @@ where storage_meter: &mut storage::meter::GenericMeter, deposit_limit: BalanceOf, read_only: bool, - ) -> Result<(Frame, E), ExecError> { + ) -> Result, E)>, ExecError> { let (account_id, contract_info, executable, delegate_caller, entry_point) = match frame_args { FrameArgs::Call { dest, cached_info, delegated_call } => { let contract = if let Some(contract) = cached_info { contract } else { - >::get(&dest).ok_or(>::ContractNotFound)? + if let Some(contract) = + >::get(T::AddressMapper::to_address(&dest)) + { + contract + } else { + return Ok(None); + } }; let (executable, delegate_caller) = @@ -847,18 +897,25 @@ where (dest, contract, executable, delegate_caller, ExportedFunction::Call) }, FrameArgs::Instantiate { sender, executable, salt, input_data } => { - let account_id = Contracts::::contract_address( - &sender, - &executable.code_hash(), - input_data, - salt, - ); + let deployer = T::AddressMapper::to_address(&sender); + let account_nonce = >::account_nonce(&sender); + let address = if let Some(salt) = salt { + address::create2(&deployer, executable.code(), input_data, salt) + } else { + address::create1(&deployer, account_nonce.saturated_into()) + }; let contract = ContractInfo::new( - &account_id, + &address, >::account_nonce(&sender), *executable.code_hash(), )?; - (account_id, contract, executable, None, ExportedFunction::Constructor) + ( + T::AddressMapper::to_account_id_contract(&address), + contract, + executable, + None, + ExportedFunction::Constructor, + ) }, }; @@ -872,9 +929,10 @@ where nested_storage: storage_meter.nested(deposit_limit), allows_reentry: true, read_only, + last_frame_output: Default::default(), }; - Ok((frame, executable)) + Ok(Some((frame, executable))) } /// Create a subsequent nested frame. @@ -885,9 +943,9 @@ where gas_limit: Weight, deposit_limit: BalanceOf, read_only: bool, - ) -> Result { + ) -> Result, ExecError> { if self.frames.len() as u32 == limits::CALL_STACK_DEPTH { - return Err(Error::::MaxCallDepthReached.into()) + return Err(Error::::MaxCallDepthReached.into()); } // We need to make sure that changes made to the contract info are not discarded. @@ -898,13 +956,16 @@ where if let (CachedContract::Cached(contract), ExportedFunction::Call) = (&frame.contract_info, frame.entry_point) { - >::insert(frame.account_id.clone(), contract.clone()); + >::insert( + T::AddressMapper::to_address(&frame.account_id), + contract.clone(), + ); } let frame = top_frame_mut!(self); let nested_gas = &mut frame.nested_gas; let nested_storage = &mut frame.nested_storage; - let (frame, executable) = Self::new_frame( + if let Some((frame, executable)) = Self::new_frame( frame_args, value_transferred, nested_gas, @@ -912,30 +973,47 @@ where nested_storage, deposit_limit, read_only, - )?; - self.frames.try_push(frame).map_err(|_| Error::::MaxCallDepthReached)?; - Ok(executable) + )? { + self.frames.try_push(frame).map_err(|_| Error::::MaxCallDepthReached)?; + Ok(Some(executable)) + } else { + Ok(None) + } } /// Run the current (top) frame. /// /// This can be either a call or an instantiate. - fn run(&mut self, executable: E, input_data: Vec) -> Result { + fn run(&mut self, executable: E, input_data: Vec) -> Result<(), ExecError> { let frame = self.top_frame(); let entry_point = frame.entry_point; let delegated_code_hash = if frame.delegate_caller.is_some() { Some(*executable.code_hash()) } else { None }; + // The output of the caller frame will be replaced by the output of this run. + // It is also not accessible from nested frames. + // Hence we drop it early to save the memory. + let frames_len = self.frames.len(); + if let Some(caller_frame) = match frames_len { + 0 => None, + 1 => Some(&mut self.first_frame.last_frame_output), + _ => self.frames.get_mut(frames_len - 2).map(|frame| &mut frame.last_frame_output), + } { + *caller_frame = Default::default(); + } + self.transient_storage.start_transaction(); let do_transaction = || { + let caller = self.caller(); + let frame = top_frame_mut!(self); + // We need to charge the storage deposit before the initial transfer so that // it can create the account in case the initial transfer is < ed. if entry_point == ExportedFunction::Constructor { // Root origin can't be used to instantiate a contract, so it is safe to assume that // if we reached this point the origin has an associated account. let origin = &self.origin.account_id()?; - let frame = top_frame_mut!(self); frame.nested_storage.charge_instantiate( origin, &frame.account_id, @@ -944,17 +1022,21 @@ where )?; // Needs to be incremented before calling into the code so that it is visible // in case of recursion. - >::inc_account_nonce(self.caller().account_id()?); + >::inc_account_nonce(caller.account_id()?); } // Every non delegate call or instantiate also optionally transfers the balance. - self.initial_transfer()?; + // If it is a delegate call, then we've already transferred tokens in the + // last non-delegate frame. + if delegated_code_hash.is_none() { + Self::transfer_from_origin(&caller, &frame.account_id, frame.value_transferred)?; + } - let contract_address = &top_frame!(self).account_id; + let contract_address = T::AddressMapper::to_address(&top_frame!(self).account_id); - let call_span = T::Debug::new_call_span(contract_address, entry_point, &input_data); + let call_span = T::Debug::new_call_span(&contract_address, entry_point, &input_data); - let output = T::Debug::intercept_call(contract_address, entry_point, &input_data) + let output = T::Debug::intercept_call(&contract_address, entry_point, &input_data) .unwrap_or_else(|| { executable .execute(self, entry_point, input_data) @@ -965,7 +1047,7 @@ where // Avoid useless work that would be reverted anyways. if output.did_revert() { - return Ok(output) + return Ok(output); } // Storage limit is normally enforced as late as possible (when the last frame returns) @@ -980,12 +1062,12 @@ where } let frame = self.top_frame(); - let account_id = &frame.account_id.clone(); + let account_id = T::AddressMapper::to_address(&frame.account_id); match (entry_point, delegated_code_hash) { (ExportedFunction::Constructor, _) => { // It is not allowed to terminate a contract inside its constructor. if matches!(frame.contract_info, CachedContract::Terminated) { - return Err(Error::::TerminatedInConstructor.into()) + return Err(Error::::TerminatedInConstructor.into()); } // If a special limit was set for the sub-call, we enforce it here. @@ -995,17 +1077,17 @@ where let contract = frame.contract_info.as_contract(); frame.nested_storage.enforce_subcall_limit(contract)?; - let caller = self.caller().account_id()?.clone(); + let caller = T::AddressMapper::to_address(self.caller().account_id()?); // Deposit an instantiation event. Contracts::::deposit_event(Event::Instantiated { deployer: caller, - contract: account_id.clone(), + contract: account_id, }); }, (ExportedFunction::Call, Some(code_hash)) => { Contracts::::deposit_event(Event::DelegateCalled { - contract: account_id.clone(), + contract: account_id, code_hash, }); }, @@ -1019,7 +1101,7 @@ where let caller = self.caller(); Contracts::::deposit_event(Event::Called { caller: caller.clone(), - contract: account_id.clone(), + contract: account_id, }); }, } @@ -1058,7 +1140,9 @@ where } self.pop_frame(success); - output + output.map(|output| { + self.top_frame_mut().last_frame_output = output; + }) } /// Remove the current (top) frame from the stack. @@ -1081,7 +1165,7 @@ where // Only gas counter changes are persisted in case of a failure. if !persist { - return + return; } // Record the storage meter changes of the nested call into the parent meter. @@ -1100,7 +1184,7 @@ where // trigger a rollback. if prev.account_id == *account_id { prev.contract_info = CachedContract::Cached(contract); - return + return; } // Predecessor is a different contract: We persist the info and invalidate the first @@ -1108,7 +1192,7 @@ where // because that case is already handled by the optimization above. Only the first // cache needs to be invalidated because that one will invalidate the next cache // when it is popped from the stack. - >::insert(account_id, contract); + >::insert(T::AddressMapper::to_address(account_id), contract); if let Some(c) = self.frames_mut().skip(1).find(|f| f.account_id == *account_id) { c.contract_info = CachedContract::Invalidated; } @@ -1123,7 +1207,7 @@ where } self.gas_meter.absorb_nested(mem::take(&mut self.first_frame.nested_gas)); if !persist { - return + return; } let mut contract = self.first_frame.contract_info.as_contract(); self.storage_meter.absorb( @@ -1132,46 +1216,49 @@ where contract.as_deref_mut(), ); if let Some(contract) = contract { - >::insert(&self.first_frame.account_id, contract); + >::insert( + T::AddressMapper::to_address(&self.first_frame.account_id), + contract, + ); } } } /// Transfer some funds from `from` to `to`. - fn transfer( - preservation: Preservation, - from: &T::AccountId, - to: &T::AccountId, - value: BalanceOf, - ) -> DispatchResult { - if !value.is_zero() && from != to { - T::Currency::transfer(from, to, value, preservation) + fn transfer(from: &T::AccountId, to: &T::AccountId, value: BalanceOf) -> DispatchResult { + // this avoids events to be emitted for zero balance transfers + if !value.is_zero() { + T::Currency::transfer(from, to, value, Preservation::Preserve) .map_err(|_| Error::::TransferFailed)?; } Ok(()) } - // The transfer as performed by a call or instantiate. - fn initial_transfer(&self) -> DispatchResult { - let frame = self.top_frame(); - - // If it is a delegate call, then we've already transferred tokens in the - // last non-delegate frame. - if frame.delegate_caller.is_some() { - return Ok(()) - } - - let value = frame.value_transferred; - - // Get the account id from the caller. - // If the caller is root there is no account to transfer from, and therefore we can't take - // any `value` other than 0. - let caller = match self.caller() { + /// Same as `transfer` but `from` is an `Origin`. + fn transfer_from_origin( + from: &Origin, + to: &T::AccountId, + value: BalanceOf, + ) -> DispatchResult { + // If the from address is root there is no account to transfer from, and therefore we can't + // take any `value` other than 0. + let from = match from { Origin::Signed(caller) => caller, Origin::Root if value.is_zero() => return Ok(()), Origin::Root => return DispatchError::RootNotAllowed.into(), }; - Self::transfer(Preservation::Preserve, &caller, &frame.account_id, value) + Self::transfer(from, to, value) + } + + /// Same as `transfer_from_origin` but creates an `ExecReturnValue` on success. + fn transfer_no_contract( + from: &Origin, + to: &T::AccountId, + value: BalanceOf, + ) -> ExecResult { + Self::transfer_from_origin(from, to, value) + .map(|_| ExecReturnValue::default()) + .map_err(Into::into) } /// Reference to the current (top) frame. @@ -1203,36 +1290,53 @@ where } /// Returns whether the specified contract allows to be reentered right now. - fn allows_reentry(&self, id: &AccountIdOf) -> bool { + fn allows_reentry(&self, id: &T::AccountId) -> bool { !self.frames().any(|f| &f.account_id == id && !f.allows_reentry) } + + /// Returns the *free* balance of the supplied AccountId. + fn account_balance(&self, who: &T::AccountId) -> U256 { + T::Currency::reducible_balance(who, Preservation::Preserve, Fortitude::Polite).into() + } + + /// Certain APIs, e.g. `{set,get}_immutable_data` behave differently depending + /// on the configured entry point. Thus, we allow setting the export manually. + #[cfg(all(feature = "runtime-benchmarks", feature = "riscv"))] + pub(crate) fn override_export(&mut self, export: ExportedFunction) { + self.top_frame_mut().entry_point = export; + } } impl<'a, T, E> Ext for Stack<'a, T, E> where T: Config, E: Executable, + BalanceOf: Into + TryFrom, + MomentOf: Into, { type T = T; fn call( &mut self, gas_limit: Weight, - deposit_limit: BalanceOf, - to: T::AccountId, - value: BalanceOf, + deposit_limit: U256, + dest: &H160, + value: U256, input_data: Vec, allows_reentry: bool, read_only: bool, - ) -> Result { + ) -> Result<(), ExecError> { // Before pushing the new frame: Protect the caller contract against reentrancy attacks. // It is important to do this before calling `allows_reentry` so that a direct recursion // is caught by it. self.top_frame_mut().allows_reentry = allows_reentry; + let dest = T::AddressMapper::to_account_id(dest); + let value = value.try_into().map_err(|_| Error::::BalanceConversionFailed)?; + let try_call = || { - if !self.allows_reentry(&to) { - return Err(>::ReentranceDenied.into()) + if !self.allows_reentry(&dest) { + return Err(>::ReentranceDenied.into()); } // We ignore instantiate frames in our search for a cached contract. @@ -1240,20 +1344,28 @@ where // constructor: We disallow calling not fully constructed contracts. let cached_info = self .frames() - .find(|f| f.entry_point == ExportedFunction::Call && f.account_id == to) + .find(|f| f.entry_point == ExportedFunction::Call && f.account_id == dest) .and_then(|f| match &f.contract_info { CachedContract::Cached(contract) => Some(contract.clone()), _ => None, }); - let executable = self.push_frame( - FrameArgs::Call { dest: to, cached_info, delegated_call: None }, + if let Some(executable) = self.push_frame( + FrameArgs::Call { dest: dest.clone(), cached_info, delegated_call: None }, value, gas_limit, - deposit_limit, + deposit_limit.try_into().map_err(|_| Error::::BalanceConversionFailed)?, // Enable read-only access if requested; cannot disable it if already set. read_only || self.is_read_only(), - )?; - self.run(executable, input_data) + )? { + self.run(executable, input_data) + } else { + Self::transfer_no_contract( + &Origin::from_account_id(self.account_id().clone()), + &dest, + value, + )?; + Ok(()) + } }; // We need to make sure to reset `allows_reentry` even on failure. @@ -1265,11 +1377,7 @@ where result } - fn delegate_call( - &mut self, - code_hash: CodeHash, - input_data: Vec, - ) -> Result { + fn delegate_call(&mut self, code_hash: H256, input_data: Vec) -> Result<(), ExecError> { let executable = E::from_storage(code_hash, self.gas_meter_mut())?; let top_frame = self.top_frame_mut(); let contract_info = top_frame.contract_info().clone(); @@ -1286,18 +1394,18 @@ where BalanceOf::::zero(), self.is_read_only(), )?; - self.run(executable, input_data) + self.run(executable.expect(FRAME_ALWAYS_EXISTS_ON_INSTANTIATE), input_data) } fn instantiate( &mut self, gas_limit: Weight, - deposit_limit: BalanceOf, - code_hash: CodeHash, - value: BalanceOf, + deposit_limit: U256, + code_hash: H256, + value: U256, input_data: Vec, - salt: &[u8], - ) -> Result<(AccountIdOf, ExecReturnValue), ExecError> { + salt: Option<&[u8; 32]>, + ) -> Result { let executable = E::from_storage(code_hash, self.gas_meter_mut())?; let sender = &self.top_frame().account_id; let executable = self.push_frame( @@ -1307,25 +1415,29 @@ where salt, input_data: input_data.as_ref(), }, - value, + value.try_into().map_err(|_| Error::::BalanceConversionFailed)?, gas_limit, - deposit_limit, + deposit_limit.try_into().map_err(|_| Error::::BalanceConversionFailed)?, self.is_read_only(), )?; - let account_id = self.top_frame().account_id.clone(); - self.run(executable, input_data).map(|ret| (account_id, ret)) + let address = T::AddressMapper::to_address(&self.top_frame().account_id); + self.run(executable.expect(FRAME_ALWAYS_EXISTS_ON_INSTANTIATE), input_data) + .map(|_| address) } - fn terminate(&mut self, beneficiary: &AccountIdOf) -> DispatchResult { + fn terminate(&mut self, beneficiary: &H160) -> DispatchResult { if self.is_recursive() { - return Err(Error::::TerminatedWhileReentrant.into()) + return Err(Error::::TerminatedWhileReentrant.into()); } let frame = self.top_frame_mut(); let info = frame.terminate(); - frame.nested_storage.terminate(&info, beneficiary.clone()); + let beneficiary_account = T::AddressMapper::to_account_id(beneficiary); + frame.nested_storage.terminate(&info, beneficiary_account); info.queue_trie_for_deletion(); - ContractInfoOf::::remove(&frame.account_id); + let account_address = T::AddressMapper::to_address(&frame.account_id); + ContractInfoOf::::remove(&account_address); + ImmutableDataOf::::remove(&account_address); Self::decrement_refcount(info.code_hash); for (code_hash, deposit) in info.delegate_dependencies() { @@ -1336,14 +1448,18 @@ where } Contracts::::deposit_event(Event::Terminated { - contract: frame.account_id.clone(), - beneficiary: beneficiary.clone(), + contract: account_address, + beneficiary: *beneficiary, }); Ok(()) } - fn transfer(&mut self, to: &T::AccountId, value: BalanceOf) -> DispatchResult { - Self::transfer(Preservation::Preserve, &self.top_frame().account_id, to, value) + fn transfer(&mut self, to: &H160, value: U256) -> DispatchResult { + Self::transfer( + &self.top_frame().account_id, + &T::AddressMapper::to_account_id(to), + value.try_into().map_err(|_| Error::::BalanceConversionFailed)?, + ) } fn get_storage(&mut self, key: &Key) -> Option> { @@ -1370,11 +1486,13 @@ where } fn get_transient_storage(&self, key: &Key) -> Option> { - self.transient_storage.read(self.address(), key) + self.transient_storage.read(self.account_id(), key) } fn get_transient_storage_size(&self, key: &Key) -> Option { - self.transient_storage.read(self.address(), key).map(|value| value.len() as _) + self.transient_storage + .read(self.account_id(), key) + .map(|value| value.len() as _) } fn set_transient_storage( @@ -1383,11 +1501,11 @@ where value: Option>, take_old: bool, ) -> Result { - let account_id = self.address().clone(); + let account_id = self.account_id().clone(); self.transient_storage.write(&account_id, key, value, take_old) } - fn address(&self) -> &T::AccountId { + fn account_id(&self) -> &T::AccountId { &self.top_frame().account_id } @@ -1402,15 +1520,15 @@ where } } - fn is_contract(&self, address: &T::AccountId) -> bool { + fn is_contract(&self, address: &H160) -> bool { ContractInfoOf::::contains_key(&address) } - fn code_hash(&self, address: &T::AccountId) -> Option> { + fn code_hash(&self, address: &H160) -> Option { >::get(&address).map(|contract| contract.code_hash) } - fn own_code_hash(&mut self) -> &CodeHash { + fn own_code_hash(&mut self) -> &H256 { &self.top_frame_mut().contract_info().code_hash } @@ -1423,43 +1541,68 @@ where self.caller_is_origin() && self.origin == Origin::Root } - fn balance(&self) -> BalanceOf { - T::Currency::reducible_balance( - &self.top_frame().account_id, - Preservation::Preserve, - Fortitude::Polite, - ) + fn get_immutable_data(&mut self) -> Result { + if self.top_frame().entry_point == ExportedFunction::Constructor { + return Err(Error::::InvalidImmutableAccess.into()); + } + + let address = T::AddressMapper::to_address(self.account_id()); + Ok(>::get(address).ok_or_else(|| Error::::InvalidImmutableAccess)?) } - fn value_transferred(&self) -> BalanceOf { - self.top_frame().value_transferred + fn set_immutable_data(&mut self, data: ImmutableData) -> Result<(), DispatchError> { + if self.top_frame().entry_point == ExportedFunction::Call { + return Err(Error::::InvalidImmutableAccess.into()); + } + + let account_id = self.account_id().clone(); + let len = data.len() as u32; + let amount = self.top_frame_mut().contract_info().set_immutable_data_len(len)?; + self.top_frame_mut().nested_storage.charge_deposit(account_id.clone(), amount); + + >::insert(T::AddressMapper::to_address(&account_id), &data); + + Ok(()) + } + + fn balance(&self) -> U256 { + self.account_balance(&self.top_frame().account_id) + } + + fn balance_of(&self, address: &H160) -> U256 { + self.account_balance(&::AddressMapper::to_account_id(address)) + } + + fn value_transferred(&self) -> U256 { + self.top_frame().value_transferred.into() } - fn now(&self) -> &MomentOf { - &self.timestamp + fn now(&self) -> U256 { + self.timestamp.into() } - fn minimum_balance(&self) -> BalanceOf { - T::Currency::minimum_balance() + fn minimum_balance(&self) -> U256 { + T::Currency::minimum_balance().into() } - fn deposit_event(&mut self, topics: Vec, data: Vec) { - Contracts::::deposit_indexed_event( + fn deposit_event(&mut self, topics: Vec, data: Vec) { + Contracts::::deposit_event(Event::ContractEmitted { + contract: T::AddressMapper::to_address(self.account_id()), + data, topics, - Event::ContractEmitted { contract: self.top_frame().account_id.clone(), data }, - ); + }); } - fn block_number(&self) -> BlockNumberFor { - self.block_number + fn block_number(&self) -> U256 { + self.block_number.into() } fn max_value_size(&self) -> u32 { limits::PAYLOAD_BYTES } - fn get_weight_price(&self, weight: Weight) -> BalanceOf { - T::WeightPrice::convert(weight) + fn get_weight_price(&self, weight: Weight) -> U256 { + T::WeightPrice::convert(weight).into() } fn gas_meter(&self) -> &GasMeter { @@ -1497,7 +1640,7 @@ where } fn call_runtime(&self, call: ::RuntimeCall) -> DispatchResultWithPostInfo { - let mut origin: T::RuntimeOrigin = RawOrigin::Signed(self.address().clone()).into(); + let mut origin: T::RuntimeOrigin = RawOrigin::Signed(self.account_id().clone()).into(); origin.add_filter(T::CallFilter::contains); call.dispatch(origin) } @@ -1528,7 +1671,22 @@ where &mut self.transient_storage } - fn set_code_hash(&mut self, hash: CodeHash) -> DispatchResult { + /// TODO: This should be changed to run the constructor of the supplied `hash`. + /// + /// Because the immutable data is attached to a contract and not a code, + /// we need to update the immutable data too. + /// + /// Otherwise we open a massive footgun: + /// If the immutables changed in the new code, the contract will brick. + /// + /// A possible implementation strategy is to add a flag to `FrameArgs::Instantiate`, + /// so that `fn run()` will roll back any changes if this flag is set. + /// + /// After running the constructor, the new immutable data is already stored in + /// `self.immutable_data` at the address of the (reverted) contract instantiation. + /// + /// The `set_code_hash` contract API stays disabled until this change is implemented. + fn set_code_hash(&mut self, hash: H256) -> DispatchResult { let frame = top_frame_mut!(self); let info = frame.contract_info(); @@ -1548,14 +1706,14 @@ where Self::increment_refcount(hash)?; Self::decrement_refcount(prev_hash); Contracts::::deposit_event(Event::ContractCodeUpdated { - contract: frame.account_id.clone(), + contract: T::AddressMapper::to_address(&frame.account_id), new_code_hash: hash, old_code_hash: prev_hash, }); Ok(()) } - fn increment_refcount(code_hash: CodeHash) -> DispatchResult { + fn increment_refcount(code_hash: H256) -> DispatchResult { >::mutate(code_hash, |existing| -> Result<(), DispatchError> { if let Some(info) = existing { *info.refcount_mut() = info.refcount().saturating_add(1); @@ -1566,7 +1724,7 @@ where }) } - fn decrement_refcount(code_hash: CodeHash) { + fn decrement_refcount(code_hash: H256) { >::mutate(code_hash, |existing| { if let Some(info) = existing { *info.refcount_mut() = info.refcount().saturating_sub(1); @@ -1574,7 +1732,7 @@ where }); } - fn lock_delegate_dependency(&mut self, code_hash: CodeHash) -> DispatchResult { + fn lock_delegate_dependency(&mut self, code_hash: H256) -> DispatchResult { let frame = self.top_frame_mut(); let info = frame.contract_info.get(&frame.account_id); ensure!(code_hash != info.code_hash, Error::::CannotAddSelfAsDelegateDependency); @@ -1590,7 +1748,7 @@ where Ok(()) } - fn unlock_delegate_dependency(&mut self, code_hash: &CodeHash) -> DispatchResult { + fn unlock_delegate_dependency(&mut self, code_hash: &H256) -> DispatchResult { let frame = self.top_frame_mut(); let info = frame.contract_info.get(&frame.account_id); @@ -1609,6 +1767,14 @@ where fn is_read_only(&self) -> bool { self.top_frame().read_only } + + fn last_frame_output(&self) -> &ExecReturnValue { + &self.top_frame().last_frame_output + } + + fn last_frame_output_mut(&mut self) -> &mut ExecReturnValue { + &mut self.top_frame_mut().last_frame_output + } } mod sealing { @@ -1635,10 +1801,9 @@ mod tests { test_utils::{get_balance, place_contract, set_balance}, ExtBuilder, RuntimeCall, RuntimeEvent as MetaEvent, Test, TestFilter, }, - Error, + AddressMapper, Error, }; use assert_matches::assert_matches; - use codec::{Decode, Encode}; use frame_support::{assert_err, assert_ok, parameter_types}; use frame_system::{EventRecord, Phase}; use pallet_revive_uapi::ReturnFlags; @@ -1673,25 +1838,25 @@ mod tests { struct MockExecutable { func: Rc Fn(MockCtx<'a>, &Self) -> ExecResult + 'static>, func_type: ExportedFunction, - code_hash: CodeHash, + code_hash: H256, code_info: CodeInfo, } #[derive(Default, Clone)] pub struct MockLoader { - map: HashMap, MockExecutable>, + map: HashMap, counter: u64, } impl MockLoader { - fn code_hashes() -> Vec> { + fn code_hashes() -> Vec { Loader::get().map.keys().copied().collect() } fn insert( func_type: ExportedFunction, f: impl Fn(MockCtx, &MockExecutable) -> ExecResult + 'static, - ) -> CodeHash { + ) -> H256 { Loader::mutate(|loader| { // Generate code hashes as monotonically increasing values. let hash = ::Hash::from_low_u64_be(loader.counter); @@ -1712,7 +1877,7 @@ mod tests { impl Executable for MockExecutable { fn from_storage( - code_hash: CodeHash, + code_hash: H256, _gas_meter: &mut GasMeter, ) -> Result { Loader::mutate(|loader| { @@ -1746,7 +1911,12 @@ mod tests { } } - fn code_hash(&self) -> &CodeHash { + fn code(&self) -> &[u8] { + // The mock executable doesn't have code", so we return the code hash. + self.code_hash.as_ref() + } + + fn code_hash(&self) -> &H256 { &self.code_hash } @@ -1784,7 +1954,7 @@ mod tests { assert_matches!( MockStack::run_call( Origin::from_account_id(ALICE), - BOB, + BOB_ADDR, &mut gas_meter, &mut storage_meter, value, @@ -1802,41 +1972,36 @@ mod tests { fn transfer_works() { // This test verifies that a contract is able to transfer // some funds to another account. - let origin = ALICE; - let dest = BOB; - ExtBuilder::default().build().execute_with(|| { - set_balance(&origin, 100); - set_balance(&dest, 0); + set_balance(&ALICE, 100); + set_balance(&BOB, 0); - MockStack::transfer(Preservation::Preserve, &origin, &dest, 55).unwrap(); + MockStack::transfer(&ALICE, &BOB, 55).unwrap(); - assert_eq!(get_balance(&origin), 45); - assert_eq!(get_balance(&dest), 55); + assert_eq!(get_balance(&ALICE), 45); + assert_eq!(get_balance(&BOB), 55); }); } #[test] fn correct_transfer_on_call() { - let origin = ALICE; - let dest = BOB; let value = 55; let success_ch = MockLoader::insert(Call, move |ctx, _| { - assert_eq!(ctx.ext.value_transferred(), value); + assert_eq!(ctx.ext.value_transferred(), U256::from(value)); Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() }) }); ExtBuilder::default().build().execute_with(|| { - place_contract(&dest, success_ch); - set_balance(&origin, 100); - let balance = get_balance(&dest); - let contract_origin = Origin::from_account_id(origin.clone()); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, value).unwrap(); + place_contract(&BOB, success_ch); + set_balance(&ALICE, 100); + let balance = get_balance(&BOB_CONTRACT_ID); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, value).unwrap(); let _ = MockStack::run_call( - contract_origin.clone(), - dest.clone(), + origin.clone(), + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, value, @@ -1845,38 +2010,36 @@ mod tests { ) .unwrap(); - assert_eq!(get_balance(&origin), 100 - value); - assert_eq!(get_balance(&dest), balance + value); + assert_eq!(get_balance(&ALICE), 100 - value); + assert_eq!(get_balance(&BOB_CONTRACT_ID), balance + value); }); } #[test] fn correct_transfer_on_delegate_call() { - let origin = ALICE; - let dest = BOB; let value = 35; let success_ch = MockLoader::insert(Call, move |ctx, _| { - assert_eq!(ctx.ext.value_transferred(), value); + assert_eq!(ctx.ext.value_transferred(), U256::from(value)); Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() }) }); let delegate_ch = MockLoader::insert(Call, move |ctx, _| { - assert_eq!(ctx.ext.value_transferred(), value); + assert_eq!(ctx.ext.value_transferred(), U256::from(value)); let _ = ctx.ext.delegate_call(success_ch, Vec::new())?; Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() }) }); ExtBuilder::default().build().execute_with(|| { - place_contract(&dest, delegate_ch); - set_balance(&origin, 100); - let balance = get_balance(&dest); - let contract_origin = Origin::from_account_id(origin.clone()); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 55).unwrap(); + place_contract(&BOB, delegate_ch); + set_balance(&ALICE, 100); + let balance = get_balance(&BOB_CONTRACT_ID); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 55).unwrap(); let _ = MockStack::run_call( - contract_origin.clone(), - dest.clone(), + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, value, @@ -1885,8 +2048,8 @@ mod tests { ) .unwrap(); - assert_eq!(get_balance(&origin), 100 - value); - assert_eq!(get_balance(&dest), balance + value); + assert_eq!(get_balance(&ALICE), 100 - value); + assert_eq!(get_balance(&BOB_CONTRACT_ID), balance + value); }); } @@ -1894,23 +2057,21 @@ mod tests { fn changes_are_reverted_on_failing_call() { // This test verifies that changes are reverted on a call which fails (or equally, returns // a non-zero status code). - let origin = ALICE; - let dest = BOB; let return_ch = MockLoader::insert(Call, |_, _| { Ok(ExecReturnValue { flags: ReturnFlags::REVERT, data: Vec::new() }) }); ExtBuilder::default().build().execute_with(|| { - place_contract(&dest, return_ch); - set_balance(&origin, 100); - let balance = get_balance(&dest); - let contract_origin = Origin::from_account_id(origin.clone()); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 55).unwrap(); + place_contract(&BOB, return_ch); + set_balance(&ALICE, 100); + let balance = get_balance(&BOB); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 55).unwrap(); let output = MockStack::run_call( - contract_origin.clone(), - dest.clone(), + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 55, @@ -1920,8 +2081,8 @@ mod tests { .unwrap(); assert!(output.did_revert()); - assert_eq!(get_balance(&origin), 100); - assert_eq!(get_balance(&dest), balance); + assert_eq!(get_balance(&ALICE), 100); + assert_eq!(get_balance(&BOB), balance); }); } @@ -1935,7 +2096,7 @@ mod tests { ExtBuilder::default().build().execute_with(|| { set_balance(&origin, 0); - let result = MockStack::transfer(Preservation::Preserve, &origin, &dest, 100); + let result = MockStack::transfer(&origin, &dest, 100); assert_eq!(result, Err(Error::::TransferFailed.into())); assert_eq!(get_balance(&origin), 0); @@ -1947,20 +2108,18 @@ mod tests { fn output_is_returned_on_success() { // Verifies that if a contract returns data with a successful exit status, this data // is returned from the execution context. - let origin = ALICE; - let dest = BOB; let return_ch = MockLoader::insert(Call, |_, _| { Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: vec![1, 2, 3, 4] }) }); ExtBuilder::default().build().execute_with(|| { - let contract_origin = Origin::from_account_id(origin); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); place_contract(&BOB, return_ch); let result = MockStack::run_call( - contract_origin, - dest, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -1978,20 +2137,18 @@ mod tests { fn output_is_returned_on_failure() { // Verifies that if a contract returns data with a failing exit status, this data // is returned from the execution context. - let origin = ALICE; - let dest = BOB; let return_ch = MockLoader::insert(Call, |_, _| { Ok(ExecReturnValue { flags: ReturnFlags::REVERT, data: vec![1, 2, 3, 4] }) }); ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, return_ch); - let contract_origin = Origin::from_account_id(origin); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); let result = MockStack::run_call( - contract_origin, - dest, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -2015,12 +2172,12 @@ mod tests { // This one tests passing the input data into a contract via call. ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, input_data_ch); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); let result = MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -2048,13 +2205,10 @@ mod tests { let executable = MockExecutable::from_storage(input_data_ch, &mut gas_meter).unwrap(); set_balance(&ALICE, min_balance * 10_000); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new( - &contract_origin, - deposit_limit::(), - min_balance, - ) - .unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&origin, deposit_limit::(), min_balance) + .unwrap(); let result = MockStack::run_instantiate( ALICE, @@ -2063,7 +2217,7 @@ mod tests { &mut storage_meter, min_balance, vec![1, 2, 3, 4], - &[], + Some(&[0; 32]), None, ); assert_matches!(result, Ok(_)); @@ -2082,9 +2236,9 @@ mod tests { // Try to call into yourself. let r = ctx.ext.call( Weight::zero(), - BalanceOf::::zero(), - BOB, - 0, + U256::zero(), + &BOB_ADDR, + U256::zero(), vec![], true, false, @@ -2108,12 +2262,12 @@ mod tests { ExtBuilder::default().build().execute_with(|| { set_balance(&BOB, 1); place_contract(&BOB, recurse_ch); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, value).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, value).unwrap(); let result = MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, value, @@ -2127,27 +2281,27 @@ mod tests { #[test] fn caller_returns_proper_values() { - let origin = ALICE; - let dest = BOB; - parameter_types! { - static WitnessedCallerBob: Option> = None; - static WitnessedCallerCharlie: Option> = None; + static WitnessedCallerBob: Option = None; + static WitnessedCallerCharlie: Option = None; } let bob_ch = MockLoader::insert(Call, |ctx, _| { // Record the caller for bob. WitnessedCallerBob::mutate(|caller| { - *caller = Some(ctx.ext.caller().account_id().unwrap().clone()) + let origin = ctx.ext.caller(); + *caller = Some(::AddressMapper::to_address( + &origin.account_id().unwrap(), + )); }); // Call into CHARLIE contract. assert_matches!( ctx.ext.call( Weight::zero(), - BalanceOf::::zero(), - CHARLIE, - 0, + U256::zero(), + &CHARLIE_ADDR, + U256::zero(), vec![], true, false @@ -2159,20 +2313,23 @@ mod tests { let charlie_ch = MockLoader::insert(Call, |ctx, _| { // Record the caller for charlie. WitnessedCallerCharlie::mutate(|caller| { - *caller = Some(ctx.ext.caller().account_id().unwrap().clone()) + let origin = ctx.ext.caller(); + *caller = Some(::AddressMapper::to_address( + &origin.account_id().unwrap(), + )); }); exec_success() }); ExtBuilder::default().build().execute_with(|| { - place_contract(&dest, bob_ch); + place_contract(&BOB, bob_ch); place_contract(&CHARLIE, charlie_ch); - let contract_origin = Origin::from_account_id(origin.clone()); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); let result = MockStack::run_call( - contract_origin.clone(), - dest.clone(), + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -2183,28 +2340,28 @@ mod tests { assert_matches!(result, Ok(_)); }); - assert_eq!(WitnessedCallerBob::get(), Some(origin)); - assert_eq!(WitnessedCallerCharlie::get(), Some(dest)); + assert_eq!(WitnessedCallerBob::get(), Some(ALICE_ADDR)); + assert_eq!(WitnessedCallerCharlie::get(), Some(BOB_ADDR)); } #[test] fn is_contract_returns_proper_values() { let bob_ch = MockLoader::insert(Call, |ctx, _| { // Verify that BOB is a contract - assert!(ctx.ext.is_contract(&BOB)); + assert!(ctx.ext.is_contract(&BOB_ADDR)); // Verify that ALICE is not a contract - assert!(!ctx.ext.is_contract(&ALICE)); + assert!(!ctx.ext.is_contract(&ALICE_ADDR)); exec_success() }); ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, bob_ch); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); let result = MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -2219,20 +2376,20 @@ mod tests { fn code_hash_returns_proper_values() { let code_bob = MockLoader::insert(Call, |ctx, _| { // ALICE is not a contract and hence they do not have a code_hash - assert!(ctx.ext.code_hash(&ALICE).is_none()); + assert!(ctx.ext.code_hash(&ALICE_ADDR).is_none()); // BOB is a contract and hence it has a code_hash - assert!(ctx.ext.code_hash(&BOB).is_some()); + assert!(ctx.ext.code_hash(&BOB_ADDR).is_some()); exec_success() }); ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, code_bob); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); // ALICE (not contract) -> BOB (contract) let result = MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -2246,19 +2403,19 @@ mod tests { #[test] fn own_code_hash_returns_proper_values() { let bob_ch = MockLoader::insert(Call, |ctx, _| { - let code_hash = ctx.ext.code_hash(&BOB).unwrap(); + let code_hash = ctx.ext.code_hash(&BOB_ADDR).unwrap(); assert_eq!(*ctx.ext.own_code_hash(), code_hash); exec_success() }); ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, bob_ch); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); // ALICE (not contract) -> BOB (contract) let result = MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -2282,18 +2439,27 @@ mod tests { assert!(ctx.ext.caller_is_origin()); // BOB calls CHARLIE ctx.ext - .call(Weight::zero(), BalanceOf::::zero(), CHARLIE, 0, vec![], true, false) + .call( + Weight::zero(), + U256::zero(), + &CHARLIE_ADDR, + U256::zero(), + vec![], + true, + false, + ) + .map(|_| ctx.ext.last_frame_output().clone()) }); ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, code_bob); place_contract(&CHARLIE, code_charlie); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); // ALICE -> BOB (caller is origin) -> CHARLIE (caller is not origin) let result = MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -2314,12 +2480,12 @@ mod tests { ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, code_bob); - let contract_origin = Origin::Root; - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::Root; + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); // root -> BOB (caller is root) let result = MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -2340,12 +2506,12 @@ mod tests { ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, code_bob); - let contract_origin = Origin::Root; - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::Root; + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); // root -> BOB (caller is root) let result = MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 1, @@ -2369,18 +2535,27 @@ mod tests { assert!(ctx.ext.caller_is_root()); // BOB calls CHARLIE. ctx.ext - .call(Weight::zero(), BalanceOf::::zero(), CHARLIE, 0, vec![], true, false) + .call( + Weight::zero(), + U256::zero(), + &CHARLIE_ADDR, + U256::zero(), + vec![], + true, + false, + ) + .map(|_| ctx.ext.last_frame_output().clone()) }); ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, code_bob); place_contract(&CHARLIE, code_charlie); - let contract_origin = Origin::Root; - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::Root; + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); // root -> BOB (caller is root) -> CHARLIE (caller is not root) let result = MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -2395,15 +2570,15 @@ mod tests { fn address_returns_proper_values() { let bob_ch = MockLoader::insert(Call, |ctx, _| { // Verify that address matches BOB. - assert_eq!(*ctx.ext.address(), BOB); + assert_eq!(ctx.ext.address(), BOB_ADDR); // Call into charlie contract. assert_matches!( ctx.ext.call( Weight::zero(), - BalanceOf::::zero(), - CHARLIE, - 0, + U256::zero(), + &CHARLIE_ADDR, + U256::zero(), vec![], true, false @@ -2413,19 +2588,19 @@ mod tests { exec_success() }); let charlie_ch = MockLoader::insert(Call, |ctx, _| { - assert_eq!(*ctx.ext.address(), CHARLIE); + assert_eq!(ctx.ext.address(), CHARLIE_ADDR); exec_success() }); ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, bob_ch); place_contract(&CHARLIE, charlie_ch); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); let result = MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -2444,8 +2619,8 @@ mod tests { ExtBuilder::default().existential_deposit(15).build().execute_with(|| { let mut gas_meter = GasMeter::::new(GAS_LIMIT); let executable = MockExecutable::from_storage(dummy_ch, &mut gas_meter).unwrap(); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); assert_matches!( MockStack::run_instantiate( @@ -2455,7 +2630,7 @@ mod tests { &mut storage_meter, 0, // <- zero value vec![], - &[], + Some(&[0; 32]), None, ), Err(_) @@ -2478,10 +2653,9 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); let executable = MockExecutable::from_storage(dummy_ch, &mut gas_meter).unwrap(); set_balance(&ALICE, min_balance * 1000); - let contract_origin = Origin::from_account_id(ALICE); + let origin = Origin::from_account_id(ALICE); let mut storage_meter = - storage::meter::Meter::new(&contract_origin, min_balance * 100, min_balance) - .unwrap(); + storage::meter::Meter::new(&origin, min_balance * 100, min_balance).unwrap(); let instantiated_contract_address = assert_matches!( MockStack::run_instantiate( @@ -2492,22 +2666,26 @@ mod tests { min_balance, vec![], - &[], + Some(&[0 ;32]), None, ), Ok((address, ref output)) if output.data == vec![80, 65, 83, 83] => address ); + let instantiated_contract_id = + ::AddressMapper::to_account_id_contract( + &instantiated_contract_address, + ); // Check that the newly created account has the expected code hash and // there are instantiation event. assert_eq!( - ContractInfo::::load_code_hash(&instantiated_contract_address).unwrap(), + ContractInfo::::load_code_hash(&instantiated_contract_id).unwrap(), dummy_ch ); assert_eq!( &events(), &[Event::Instantiated { - deployer: ALICE, + deployer: ALICE_ADDR, contract: instantiated_contract_address }] ); @@ -2529,10 +2707,9 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); let executable = MockExecutable::from_storage(dummy_ch, &mut gas_meter).unwrap(); set_balance(&ALICE, min_balance * 1000); - let contract_origin = Origin::from_account_id(ALICE); + let origin = Origin::from_account_id(ALICE); let mut storage_meter = - storage::meter::Meter::new(&contract_origin, min_balance * 100, min_balance) - .unwrap(); + storage::meter::Meter::new(&origin, min_balance * 100, min_balance).unwrap(); let instantiated_contract_address = assert_matches!( MockStack::run_instantiate( @@ -2543,16 +2720,19 @@ mod tests { min_balance, vec![], - &[], + Some(&[0; 32]), None, ), Ok((address, ref output)) if output.data == vec![70, 65, 73, 76] => address ); + let instantiated_contract_id = + ::AddressMapper::to_account_id_contract( + &instantiated_contract_address, + ); + // Check that the account has not been created. - assert!( - ContractInfo::::load_code_hash(&instantiated_contract_address).is_none() - ); + assert!(ContractInfo::::load_code_hash(&instantiated_contract_id).is_none()); assert!(events().is_empty()); }); } @@ -2560,7 +2740,7 @@ mod tests { #[test] fn instantiation_from_contract() { let dummy_ch = MockLoader::insert(Call, |_, _| exec_success()); - let instantiated_contract_address = Rc::new(RefCell::new(None::>)); + let instantiated_contract_address = Rc::new(RefCell::new(None::)); let instantiator_ch = MockLoader::insert(Call, { let instantiated_contract_address = Rc::clone(&instantiated_contract_address); move |ctx, _| { @@ -2569,15 +2749,16 @@ mod tests { .ext .instantiate( Weight::zero(), - BalanceOf::::zero(), + U256::zero(), dummy_ch, - ::Currency::minimum_balance(), + ::Currency::minimum_balance().into(), vec![], - &[48, 49, 50], + Some(&[48; 32]), ) + .map(|address| (address, ctx.ext.last_frame_output().clone())) .unwrap(); - *instantiated_contract_address.borrow_mut() = address.into(); + *instantiated_contract_address.borrow_mut() = Some(address); Ok(output) } }); @@ -2590,18 +2771,15 @@ mod tests { let min_balance = ::Currency::minimum_balance(); set_balance(&ALICE, min_balance * 100); place_contract(&BOB, instantiator_ch); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new( - &contract_origin, - min_balance * 10, - min_balance * 10, - ) - .unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&origin, min_balance * 10, min_balance * 10) + .unwrap(); assert_matches!( MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, min_balance * 10, @@ -2612,22 +2790,30 @@ mod tests { ); let instantiated_contract_address = - instantiated_contract_address.borrow().as_ref().unwrap().clone(); + *instantiated_contract_address.borrow().as_ref().unwrap(); + + let instantiated_contract_id = + ::AddressMapper::to_account_id_contract( + &instantiated_contract_address, + ); // Check that the newly created account has the expected code hash and // there are instantiation event. assert_eq!( - ContractInfo::::load_code_hash(&instantiated_contract_address).unwrap(), + ContractInfo::::load_code_hash(&instantiated_contract_id).unwrap(), dummy_ch ); assert_eq!( &events(), &[ Event::Instantiated { - deployer: BOB, + deployer: BOB_ADDR, contract: instantiated_contract_address }, - Event::Called { caller: Origin::from_account_id(ALICE), contract: BOB }, + Event::Called { + caller: Origin::from_account_id(ALICE), + contract: BOB_ADDR + }, ] ); }); @@ -2642,11 +2828,11 @@ mod tests { assert_matches!( ctx.ext.instantiate( Weight::zero(), - BalanceOf::::zero(), + U256::zero(), dummy_ch, - ::Currency::minimum_balance(), + ::Currency::minimum_balance().into(), vec![], - &[], + Some(&[0; 32]), ), Err(ExecError { error: DispatchError::Other("It's a trap!"), @@ -2664,16 +2850,15 @@ mod tests { .build() .execute_with(|| { set_balance(&ALICE, 1000); - set_balance(&BOB, 100); + set_balance(&BOB_CONTRACT_ID, 100); place_contract(&BOB, instantiator_ch); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = - storage::meter::Meter::new(&contract_origin, 200, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 200, 0).unwrap(); assert_matches!( MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -2687,7 +2872,7 @@ mod tests { // event here. assert_eq!( &events(), - &[Event::Called { caller: Origin::from_account_id(ALICE), contract: BOB },] + &[Event::Called { caller: Origin::from_account_id(ALICE), contract: BOB_ADDR },] ); }); } @@ -2695,7 +2880,7 @@ mod tests { #[test] fn termination_from_instantiate_fails() { let terminate_ch = MockLoader::insert(Constructor, |ctx, _| { - ctx.ext.terminate(&ALICE).unwrap(); + ctx.ext.terminate(&ALICE_ADDR).unwrap(); exec_success() }); @@ -2708,10 +2893,9 @@ mod tests { let executable = MockExecutable::from_storage(terminate_ch, &mut gas_meter).unwrap(); set_balance(&ALICE, 10_000); - let contract_origin = Origin::from_account_id(ALICE); + let origin = Origin::from_account_id(ALICE); let mut storage_meter = - storage::meter::Meter::new(&contract_origin, deposit_limit::(), 100) - .unwrap(); + storage::meter::Meter::new(&origin, deposit_limit::(), 100).unwrap(); assert_eq!( MockStack::run_instantiate( @@ -2721,7 +2905,7 @@ mod tests { &mut storage_meter, 100, vec![], - &[], + Some(&[0; 32]), None, ), Err(Error::::TerminatedInConstructor.into()) @@ -2747,15 +2931,17 @@ mod tests { assert_eq!(info.storage_byte_deposit, 0); info.storage_byte_deposit = 42; assert_eq!( - ctx.ext.call( - Weight::zero(), - BalanceOf::::zero(), - CHARLIE, - 0, - vec![], - true, - false - ), + ctx.ext + .call( + Weight::zero(), + U256::zero(), + &CHARLIE_ADDR, + U256::zero(), + vec![], + true, + false + ) + .map(|_| ctx.ext.last_frame_output().clone()), exec_trapped() ); assert_eq!(ctx.ext.contract_info().storage_byte_deposit, 42); @@ -2765,7 +2951,7 @@ mod tests { let code_charlie = MockLoader::insert(Call, |ctx, _| { assert!(ctx .ext - .call(Weight::zero(), BalanceOf::::zero(), BOB, 0, vec![99], true, false) + .call(Weight::zero(), U256::zero(), &BOB_ADDR, U256::zero(), vec![99], true, false) .is_ok()); exec_trapped() }); @@ -2774,12 +2960,12 @@ mod tests { ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, code_bob); place_contract(&CHARLIE, code_charlie); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); let result = MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -2791,12 +2977,35 @@ mod tests { } #[test] - fn recursive_call_during_constructor_fails() { + fn recursive_call_during_constructor_is_balance_transfer() { let code = MockLoader::insert(Constructor, |ctx, _| { - assert_matches!( - ctx.ext.call(Weight::zero(), BalanceOf::::zero(), ctx.ext.address().clone(), 0, vec![], true, false), - Err(ExecError{error, ..}) if error == >::ContractNotFound.into() - ); + let account_id = ctx.ext.account_id().clone(); + let addr = ::AddressMapper::to_address(&account_id); + let balance = ctx.ext.balance(); + + // Calling ourselves during the constructor will trigger a balance + // transfer since no contract exist yet. + assert_ok!(ctx.ext.call( + Weight::zero(), + U256::zero(), + &addr, + (balance - 1).into(), + vec![], + true, + false + )); + + // Should also work with call data set as it is ignored when no + // contract is deployed. + assert_ok!(ctx.ext.call( + Weight::zero(), + U256::zero(), + &addr, + 1u32.into(), + vec![1, 2, 3, 4], + true, + false + )); exec_success() }); @@ -2809,22 +3018,19 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); let executable = MockExecutable::from_storage(code, &mut gas_meter).unwrap(); set_balance(&ALICE, min_balance * 10_000); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new( - &contract_origin, - deposit_limit::(), - min_balance, - ) - .unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = + storage::meter::Meter::new(&origin, deposit_limit::(), min_balance) + .unwrap(); let result = MockStack::run_instantiate( ALICE, executable, &mut gas_meter, &mut storage_meter, - min_balance, + 10, vec![], - &[], + Some(&[0; 32]), None, ); assert_matches!(result, Ok(_)); @@ -2832,34 +3038,79 @@ mod tests { } #[test] - fn printing_works() { + fn cannot_send_more_balance_than_available_to_self() { let code_hash = MockLoader::insert(Call, |ctx, _| { - ctx.ext.append_debug_buffer("This is a test"); - ctx.ext.append_debug_buffer("More text"); + let account_id = ctx.ext.account_id().clone(); + let addr = ::AddressMapper::to_address(&account_id); + let balance = ctx.ext.balance(); + + assert_err!( + ctx.ext.call( + Weight::zero(), + U256::zero(), + &addr, + (balance + 1).into(), + vec![], + true, + false + ), + >::TransferFailed + ); exec_success() }); - let mut debug_buffer = DebugBuffer::try_from(Vec::new()).unwrap(); - - ExtBuilder::default().build().execute_with(|| { - let min_balance = ::Currency::minimum_balance(); - - let mut gas_meter = GasMeter::::new(GAS_LIMIT); - set_balance(&ALICE, min_balance * 10); - place_contract(&BOB, code_hash); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); - MockStack::run_call( - contract_origin, - BOB, - &mut gas_meter, - &mut storage_meter, - 0, - vec![], - Some(&mut debug_buffer), - ) - .unwrap(); - }); + ExtBuilder::default() + .with_code_hashes(MockLoader::code_hashes()) + .build() + .execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + set_balance(&ALICE, min_balance * 10); + place_contract(&BOB, code_hash); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); + MockStack::run_call( + origin, + BOB_ADDR, + &mut gas_meter, + &mut storage_meter, + 0, + vec![], + None, + ) + .unwrap(); + }); + } + + #[test] + fn printing_works() { + let code_hash = MockLoader::insert(Call, |ctx, _| { + ctx.ext.append_debug_buffer("This is a test"); + ctx.ext.append_debug_buffer("More text"); + exec_success() + }); + + let mut debug_buffer = DebugBuffer::try_from(Vec::new()).unwrap(); + + ExtBuilder::default().build().execute_with(|| { + let min_balance = ::Currency::minimum_balance(); + + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + set_balance(&ALICE, min_balance * 10); + place_contract(&BOB, code_hash); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); + MockStack::run_call( + origin, + BOB_ADDR, + &mut gas_meter, + &mut storage_meter, + 0, + vec![], + Some(&mut debug_buffer), + ) + .unwrap(); + }); assert_eq!(&String::from_utf8(debug_buffer.to_vec()).unwrap(), "This is a testMore text"); } @@ -2880,11 +3131,11 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 10); place_contract(&BOB, code_hash); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); let result = MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut gas_meter, &mut storage_meter, 0, @@ -2913,11 +3164,11 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 10); place_contract(&BOB, code_hash); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut gas_meter, &mut storage_meter, 0, @@ -2933,9 +3184,10 @@ mod tests { fn call_reentry_direct_recursion() { // call the contract passed as input with disabled reentry let code_bob = MockLoader::insert(Call, |ctx, _| { - let dest = Decode::decode(&mut ctx.input_data.as_ref()).unwrap(); + let dest = H160::from_slice(ctx.input_data.as_ref()); ctx.ext - .call(Weight::zero(), BalanceOf::::zero(), dest, 0, vec![], false, false) + .call(Weight::zero(), U256::zero(), &dest, U256::zero(), vec![], false, false) + .map(|_| ctx.ext.last_frame_output().clone()) }); let code_charlie = MockLoader::insert(Call, |_, _| exec_success()); @@ -2943,29 +3195,29 @@ mod tests { ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, code_bob); place_contract(&CHARLIE, code_charlie); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); // Calling another contract should succeed assert_ok!(MockStack::run_call( - contract_origin.clone(), - BOB, + origin.clone(), + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, - CHARLIE.encode(), + CHARLIE_ADDR.as_bytes().to_vec(), None, )); // Calling into oneself fails assert_err!( MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, - BOB.encode(), + BOB_ADDR.as_bytes().to_vec(), None, ) .map_err(|e| e.error), @@ -2978,15 +3230,17 @@ mod tests { fn call_deny_reentry() { let code_bob = MockLoader::insert(Call, |ctx, _| { if ctx.input_data[0] == 0 { - ctx.ext.call( - Weight::zero(), - BalanceOf::::zero(), - CHARLIE, - 0, - vec![], - false, - false, - ) + ctx.ext + .call( + Weight::zero(), + U256::zero(), + &CHARLIE_ADDR, + U256::zero(), + vec![], + false, + false, + ) + .map(|_| ctx.ext.last_frame_output().clone()) } else { exec_success() } @@ -2995,20 +3249,21 @@ mod tests { // call BOB with input set to '1' let code_charlie = MockLoader::insert(Call, |ctx, _| { ctx.ext - .call(Weight::zero(), BalanceOf::::zero(), BOB, 0, vec![1], true, false) + .call(Weight::zero(), U256::zero(), &BOB_ADDR, U256::zero(), vec![1], true, false) + .map(|_| ctx.ext.last_frame_output().clone()) }); ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, code_bob); place_contract(&CHARLIE, code_charlie); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); // BOB -> CHARLIE -> BOB fails as BOB denies reentry. assert_err!( MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -3037,12 +3292,12 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 10); place_contract(&BOB, code_hash); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); System::reset_events(); MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut gas_meter, &mut storage_meter, 0, @@ -3058,7 +3313,7 @@ mod tests { EventRecord { phase: Phase::Initialization, event: MetaEvent::System(frame_system::Event::Remarked { - sender: BOB, + sender: BOB_CONTRACT_ID, hash: remark_hash }), topics: vec![], @@ -3067,7 +3322,7 @@ mod tests { phase: Phase::Initialization, event: MetaEvent::Contracts(crate::Event::Called { caller: Origin::from_account_id(ALICE), - contract: BOB, + contract: BOB_ADDR, }), topics: vec![], }, @@ -3121,12 +3376,12 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 10); place_contract(&BOB, code_hash); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); System::reset_events(); MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut gas_meter, &mut storage_meter, 0, @@ -3142,7 +3397,7 @@ mod tests { EventRecord { phase: Phase::Initialization, event: MetaEvent::System(frame_system::Event::Remarked { - sender: BOB, + sender: BOB_CONTRACT_ID, hash: remark_hash }), topics: vec![], @@ -3164,7 +3419,7 @@ mod tests { phase: Phase::Initialization, event: MetaEvent::Contracts(crate::Event::Called { caller: Origin::from_account_id(ALICE), - contract: BOB, + contract: BOB_ADDR, }), topics: vec![], }, @@ -3181,50 +3436,44 @@ mod tests { ctx.ext .instantiate( Weight::zero(), - BalanceOf::::zero(), + U256::zero(), fail_code, ctx.ext.minimum_balance() * 100, vec![], - &[], + Some(&[0; 32]), ) .ok(); exec_success() }); let succ_succ_code = MockLoader::insert(Constructor, move |ctx, _| { let alice_nonce = System::account_nonce(&ALICE); - assert_eq!(System::account_nonce(ctx.ext.address()), 0); + assert_eq!(System::account_nonce(ctx.ext.account_id()), 0); assert_eq!(ctx.ext.caller().account_id().unwrap(), &ALICE); - let (account_id, _) = ctx + let addr = ctx .ext .instantiate( Weight::zero(), - BalanceOf::::zero(), + U256::zero(), success_code, ctx.ext.minimum_balance() * 100, vec![], - &[], + Some(&[0; 32]), ) .unwrap(); + let account_id = ::AddressMapper::to_account_id_contract(&addr); + assert_eq!(System::account_nonce(&ALICE), alice_nonce); - assert_eq!(System::account_nonce(ctx.ext.address()), 1); + assert_eq!(System::account_nonce(ctx.ext.account_id()), 1); assert_eq!(System::account_nonce(&account_id), 0); // a plain call should not influence the account counter ctx.ext - .call( - Weight::zero(), - BalanceOf::::zero(), - account_id.clone(), - 0, - vec![], - false, - false, - ) + .call(Weight::zero(), U256::zero(), &addr, U256::zero(), vec![], false, false) .unwrap(); assert_eq!(System::account_nonce(ALICE), alice_nonce); - assert_eq!(System::account_nonce(ctx.ext.address()), 1); + assert_eq!(System::account_nonce(ctx.ext.account_id()), 1); assert_eq!(System::account_nonce(&account_id), 0); exec_success() @@ -3246,13 +3495,10 @@ mod tests { MockExecutable::from_storage(succ_succ_code, &mut gas_meter).unwrap(); set_balance(&ALICE, min_balance * 10_000); set_balance(&BOB, min_balance * 10_000); - let contract_origin = Origin::from_account_id(BOB); - let mut storage_meter = storage::meter::Meter::new( - &contract_origin, - deposit_limit::(), - min_balance * 100, - ) - .unwrap(); + let origin = Origin::from_account_id(BOB); + let mut storage_meter = + storage::meter::Meter::new(&origin, deposit_limit::(), min_balance * 100) + .unwrap(); // fail should not increment MockStack::run_instantiate( @@ -3262,7 +3508,7 @@ mod tests { &mut storage_meter, min_balance * 100, vec![], - &[], + Some(&[0; 32]), None, ) .ok(); @@ -3275,7 +3521,7 @@ mod tests { &mut storage_meter, min_balance * 100, vec![], - &[], + Some(&[0; 32]), None, )); assert_eq!(System::account_nonce(&ALICE), 1); @@ -3287,7 +3533,7 @@ mod tests { &mut storage_meter, min_balance * 200, vec![], - &[], + Some(&[0; 32]), None, )); assert_eq!(System::account_nonce(&ALICE), 2); @@ -3299,7 +3545,7 @@ mod tests { &mut storage_meter, min_balance * 200, vec![], - &[], + Some(&[0; 32]), None, )); assert_eq!(System::account_nonce(&ALICE), 3); @@ -3358,12 +3604,12 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 1000); place_contract(&BOB, code_hash); - let contract_origin = Origin::from_account_id(ALICE); + let origin = Origin::from_account_id(ALICE); let mut storage_meter = - storage::meter::Meter::new(&contract_origin, deposit_limit::(), 0).unwrap(); + storage::meter::Meter::new(&origin, deposit_limit::(), 0).unwrap(); assert_ok!(MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut gas_meter, &mut storage_meter, 0, @@ -3469,12 +3715,12 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 1000); place_contract(&BOB, code_hash); - let contract_origin = Origin::from_account_id(ALICE); + let origin = Origin::from_account_id(ALICE); let mut storage_meter = - storage::meter::Meter::new(&contract_origin, deposit_limit::(), 0).unwrap(); + storage::meter::Meter::new(&origin, deposit_limit::(), 0).unwrap(); assert_ok!(MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut gas_meter, &mut storage_meter, 0, @@ -3508,12 +3754,12 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 1000); place_contract(&BOB, code_hash); - let contract_origin = Origin::from_account_id(ALICE); + let origin = Origin::from_account_id(ALICE); let mut storage_meter = - storage::meter::Meter::new(&contract_origin, deposit_limit::(), 0).unwrap(); + storage::meter::Meter::new(&origin, deposit_limit::(), 0).unwrap(); assert_ok!(MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut gas_meter, &mut storage_meter, 0, @@ -3547,12 +3793,12 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 1000); place_contract(&BOB, code_hash); - let contract_origin = Origin::from_account_id(ALICE); + let origin = Origin::from_account_id(ALICE); let mut storage_meter = - storage::meter::Meter::new(&contract_origin, deposit_limit::(), 0).unwrap(); + storage::meter::Meter::new(&origin, deposit_limit::(), 0).unwrap(); assert_ok!(MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut gas_meter, &mut storage_meter, 0, @@ -3600,12 +3846,12 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 1000); place_contract(&BOB, code_hash); - let contract_origin = Origin::from_account_id(ALICE); + let origin = Origin::from_account_id(ALICE); let mut storage_meter = - storage::meter::Meter::new(&contract_origin, deposit_limit::(), 0).unwrap(); + storage::meter::Meter::new(&origin, deposit_limit::(), 0).unwrap(); assert_ok!(MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut gas_meter, &mut storage_meter, 0, @@ -3656,12 +3902,12 @@ mod tests { let mut gas_meter = GasMeter::::new(GAS_LIMIT); set_balance(&ALICE, min_balance * 1000); place_contract(&BOB, code_hash); - let contract_origin = Origin::from_account_id(ALICE); + let origin = Origin::from_account_id(ALICE); let mut storage_meter = - storage::meter::Meter::new(&contract_origin, deposit_limit::(), 0).unwrap(); + storage::meter::Meter::new(&origin, deposit_limit::(), 0).unwrap(); assert_ok!(MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut gas_meter, &mut storage_meter, 0, @@ -3731,12 +3977,12 @@ mod tests { ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, code_hash); - let contract_origin = Origin::from_account_id(ALICE); + let origin = Origin::from_account_id(ALICE); let mut storage_meter = - storage::meter::Meter::new(&contract_origin, deposit_limit::(), 0).unwrap(); + storage::meter::Meter::new(&origin, deposit_limit::(), 0).unwrap(); assert_ok!(MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -3759,15 +4005,17 @@ mod tests { Ok(WriteOutcome::New) ); assert_eq!( - ctx.ext.call( - Weight::zero(), - BalanceOf::::zero(), - CHARLIE, - 0, - vec![], - true, - false, - ), + ctx.ext + .call( + Weight::zero(), + U256::zero(), + &CHARLIE_ADDR, + U256::zero(), + vec![], + true, + false, + ) + .map(|_| ctx.ext.last_frame_output().clone()), exec_success() ); assert_eq!(ctx.ext.get_transient_storage(storage_key_1), Some(vec![3])); @@ -3788,7 +4036,7 @@ mod tests { let code_charlie = MockLoader::insert(Call, |ctx, _| { assert!(ctx .ext - .call(Weight::zero(), BalanceOf::::zero(), BOB, 0, vec![99], true, false) + .call(Weight::zero(), U256::zero(), &BOB_ADDR, U256::zero(), vec![99], true, false) .is_ok()); // CHARLIE can not read BOB`s storage. assert_eq!(ctx.ext.get_transient_storage(storage_key_1), None); @@ -3799,12 +4047,12 @@ mod tests { ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, code_bob); place_contract(&CHARLIE, code_charlie); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); let result = MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -3838,11 +4086,11 @@ mod tests { ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, code_hash); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); assert_ok!(MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -3863,15 +4111,17 @@ mod tests { Ok(WriteOutcome::New) ); assert_eq!( - ctx.ext.call( - Weight::zero(), - BalanceOf::::zero(), - CHARLIE, - 0, - vec![], - true, - false - ), + ctx.ext + .call( + Weight::zero(), + U256::zero(), + &CHARLIE_ADDR, + U256::zero(), + vec![], + true, + false + ) + .map(|_| ctx.ext.last_frame_output().clone()), exec_trapped() ); assert_eq!(ctx.ext.get_transient_storage(storage_key), Some(vec![1, 2])); @@ -3888,7 +4138,7 @@ mod tests { let code_charlie = MockLoader::insert(Call, |ctx, _| { assert!(ctx .ext - .call(Weight::zero(), BalanceOf::::zero(), BOB, 0, vec![99], true, false) + .call(Weight::zero(), U256::zero(), &BOB_ADDR, U256::zero(), vec![99], true, false) .is_ok()); exec_trapped() }); @@ -3897,12 +4147,12 @@ mod tests { ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, code_bob); place_contract(&CHARLIE, code_charlie); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); let result = MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -3931,11 +4181,11 @@ mod tests { ExtBuilder::default().build().execute_with(|| { place_contract(&BOB, bob_ch); - let contract_origin = Origin::from_account_id(ALICE); - let mut storage_meter = storage::meter::Meter::new(&contract_origin, 0, 0).unwrap(); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); let result = MockStack::run_call( - contract_origin, - BOB, + origin, + BOB_ADDR, &mut GasMeter::::new(GAS_LIMIT), &mut storage_meter, 0, @@ -3945,4 +4195,360 @@ mod tests { assert_matches!(result, Ok(_)); }); } + + #[test] + fn last_frame_output_works_on_instantiate() { + let ok_ch = MockLoader::insert(Constructor, move |_, _| { + Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: vec![127] }) + }); + let revert_ch = MockLoader::insert(Constructor, move |_, _| { + Ok(ExecReturnValue { flags: ReturnFlags::REVERT, data: vec![70] }) + }); + let trap_ch = MockLoader::insert(Constructor, |_, _| Err("It's a trap!".into())); + let instantiator_ch = MockLoader::insert(Call, { + move |ctx, _| { + let value = ::Currency::minimum_balance().into(); + + // Successful instantiation should set the output + let address = ctx + .ext + .instantiate(Weight::zero(), U256::zero(), ok_ch, value, vec![], None) + .unwrap(); + assert_eq!( + ctx.ext.last_frame_output(), + &ExecReturnValue { flags: ReturnFlags::empty(), data: vec![127] } + ); + + // Plain transfers should not set the output + ctx.ext.transfer(&address, U256::from(1)).unwrap(); + assert_eq!( + ctx.ext.last_frame_output(), + &ExecReturnValue { flags: ReturnFlags::empty(), data: vec![127] } + ); + + // Reverted instantiation should set the output + ctx.ext + .instantiate(Weight::zero(), U256::zero(), revert_ch, value, vec![], None) + .unwrap(); + assert_eq!( + ctx.ext.last_frame_output(), + &ExecReturnValue { flags: ReturnFlags::REVERT, data: vec![70] } + ); + + // Trapped instantiation should clear the output + ctx.ext + .instantiate(Weight::zero(), U256::zero(), trap_ch, value, vec![], None) + .unwrap_err(); + assert_eq!( + ctx.ext.last_frame_output(), + &ExecReturnValue { flags: ReturnFlags::empty(), data: vec![] } + ); + + exec_success() + } + }); + + ExtBuilder::default() + .with_code_hashes(MockLoader::code_hashes()) + .existential_deposit(15) + .build() + .execute_with(|| { + set_balance(&ALICE, 1000); + set_balance(&BOB_CONTRACT_ID, 100); + place_contract(&BOB, instantiator_ch); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 200, 0).unwrap(); + + MockStack::run_call( + origin, + BOB_ADDR, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![], + None, + ) + .unwrap() + }); + } + + #[test] + fn last_frame_output_works_on_nested_call() { + // Call stack: BOB -> CHARLIE(revert) -> BOB' (success) + let code_bob = MockLoader::insert(Call, |ctx, _| { + if ctx.input_data.is_empty() { + // We didn't do anything yet + assert_eq!( + ctx.ext.last_frame_output(), + &ExecReturnValue { flags: ReturnFlags::empty(), data: vec![] } + ); + + ctx.ext + .call( + Weight::zero(), + U256::zero(), + &CHARLIE_ADDR, + U256::zero(), + vec![], + true, + false, + ) + .unwrap(); + assert_eq!( + ctx.ext.last_frame_output(), + &ExecReturnValue { flags: ReturnFlags::REVERT, data: vec![70] } + ); + } + + Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: vec![127] }) + }); + let code_charlie = MockLoader::insert(Call, |ctx, _| { + // We didn't do anything yet + assert_eq!( + ctx.ext.last_frame_output(), + &ExecReturnValue { flags: ReturnFlags::empty(), data: vec![] } + ); + + assert!(ctx + .ext + .call(Weight::zero(), U256::zero(), &BOB_ADDR, U256::zero(), vec![99], true, false) + .is_ok()); + assert_eq!( + ctx.ext.last_frame_output(), + &ExecReturnValue { flags: ReturnFlags::empty(), data: vec![127] } + ); + + Ok(ExecReturnValue { flags: ReturnFlags::REVERT, data: vec![70] }) + }); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, code_bob); + place_contract(&CHARLIE, code_charlie); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 0, 0).unwrap(); + + let result = MockStack::run_call( + origin, + BOB_ADDR, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![0], + None, + ); + assert_matches!(result, Ok(_)); + }); + } + + #[test] + fn immutable_data_access_checks_work() { + let dummy_ch = MockLoader::insert(Constructor, move |ctx, _| { + // Calls can not store immutable data + assert_eq!( + ctx.ext.get_immutable_data(), + Err(Error::::InvalidImmutableAccess.into()) + ); + exec_success() + }); + let instantiator_ch = MockLoader::insert(Call, { + move |ctx, _| { + let value = ::Currency::minimum_balance().into(); + + assert_eq!( + ctx.ext.set_immutable_data(vec![0, 1, 2, 3].try_into().unwrap()), + Err(Error::::InvalidImmutableAccess.into()) + ); + + // Constructors can not access the immutable data + ctx.ext + .instantiate(Weight::zero(), U256::zero(), dummy_ch, value, vec![], None) + .unwrap(); + + exec_success() + } + }); + ExtBuilder::default() + .with_code_hashes(MockLoader::code_hashes()) + .existential_deposit(15) + .build() + .execute_with(|| { + set_balance(&ALICE, 1000); + set_balance(&BOB_CONTRACT_ID, 100); + place_contract(&BOB, instantiator_ch); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 200, 0).unwrap(); + + MockStack::run_call( + origin, + BOB_ADDR, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![], + None, + ) + .unwrap() + }); + } + + #[test] + fn correct_immutable_data_in_delegate_call() { + let charlie_ch = MockLoader::insert(Call, |ctx, _| { + Ok(ExecReturnValue { + flags: ReturnFlags::empty(), + data: ctx.ext.get_immutable_data()?.to_vec(), + }) + }); + let bob_ch = MockLoader::insert(Call, move |ctx, _| { + // In a regular call, we should witness the callee immutable data + assert_eq!( + ctx.ext + .call( + Weight::zero(), + U256::zero(), + &CHARLIE_ADDR, + U256::zero(), + vec![], + true, + false, + ) + .map(|_| ctx.ext.last_frame_output().data.clone()), + Ok(vec![2]), + ); + + // In a delegate call, we should witness the caller immutable data + assert_eq!( + ctx.ext.delegate_call(charlie_ch, Vec::new()).map(|_| ctx + .ext + .last_frame_output() + .data + .clone()), + Ok(vec![1]) + ); + + exec_success() + }); + ExtBuilder::default() + .with_code_hashes(MockLoader::code_hashes()) + .existential_deposit(15) + .build() + .execute_with(|| { + place_contract(&BOB, bob_ch); + place_contract(&CHARLIE, charlie_ch); + + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 200, 0).unwrap(); + + // Place unique immutable data for each contract + >::insert::<_, ImmutableData>( + BOB_ADDR, + vec![1].try_into().unwrap(), + ); + >::insert::<_, ImmutableData>( + CHARLIE_ADDR, + vec![2].try_into().unwrap(), + ); + + MockStack::run_call( + origin, + BOB_ADDR, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![], + None, + ) + .unwrap() + }); + } + + #[test] + fn immutable_data_set_works_only_once() { + let dummy_ch = MockLoader::insert(Constructor, move |ctx, _| { + // Calling `set_immutable_data` the first time should work + assert_ok!(ctx.ext.set_immutable_data(vec![0, 1, 2, 3].try_into().unwrap())); + // Calling `set_immutable_data` the second time should error out + assert_eq!( + ctx.ext.set_immutable_data(vec![0, 1, 2, 3].try_into().unwrap()), + Err(Error::::InvalidImmutableAccess.into()) + ); + exec_success() + }); + let instantiator_ch = MockLoader::insert(Call, { + move |ctx, _| { + let value = ::Currency::minimum_balance().into(); + ctx.ext + .instantiate(Weight::zero(), U256::zero(), dummy_ch, value, vec![], None) + .unwrap(); + + exec_success() + } + }); + ExtBuilder::default() + .with_code_hashes(MockLoader::code_hashes()) + .existential_deposit(15) + .build() + .execute_with(|| { + set_balance(&ALICE, 1000); + set_balance(&BOB_CONTRACT_ID, 100); + place_contract(&BOB, instantiator_ch); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 200, 0).unwrap(); + + MockStack::run_call( + origin, + BOB_ADDR, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![], + None, + ) + .unwrap() + }); + } + + #[test] + fn immutable_data_set_errors_with_empty_data() { + let dummy_ch = MockLoader::insert(Constructor, move |ctx, _| { + // Calling `set_immutable_data` with empty data should error out + assert_eq!( + ctx.ext.set_immutable_data(Default::default()), + Err(Error::::InvalidImmutableAccess.into()) + ); + exec_success() + }); + let instantiator_ch = MockLoader::insert(Call, { + move |ctx, _| { + let value = ::Currency::minimum_balance().into(); + ctx.ext + .instantiate(Weight::zero(), U256::zero(), dummy_ch, value, vec![], None) + .unwrap(); + + exec_success() + } + }); + ExtBuilder::default() + .with_code_hashes(MockLoader::code_hashes()) + .existential_deposit(15) + .build() + .execute_with(|| { + set_balance(&ALICE, 1000); + set_balance(&BOB_CONTRACT_ID, 100); + place_contract(&BOB, instantiator_ch); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(&origin, 200, 0).unwrap(); + + MockStack::run_call( + origin, + BOB_ADDR, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + 0, + vec![], + None, + ) + .unwrap() + }); + } } diff --git a/substrate/frame/revive/src/gas.rs b/substrate/frame/revive/src/gas.rs index 2034f39e9bc535ae0a61b914cbfd28c4f97c84f3..9aad84e69201e4991679f7b1e57590598760415e 100644 --- a/substrate/frame/revive/src/gas.rs +++ b/substrate/frame/revive/src/gas.rs @@ -76,9 +76,7 @@ impl EngineMeter { // We execute 6 different instructions therefore we have to divide the actual // computed gas costs by 6 to have a rough estimate as to how expensive each // single executed instruction is going to be. - let instr_cost = T::WeightInfo::instr_i64_load_store(1) - .saturating_sub(T::WeightInfo::instr_i64_load_store(0)) - .ref_time(); + let instr_cost = T::WeightInfo::instr(1).saturating_sub(T::WeightInfo::instr(0)).ref_time(); instr_cost / 6 } } diff --git a/substrate/frame/revive/src/lib.rs b/substrate/frame/revive/src/lib.rs index 303c649bc8cf1b0dfbf2eb5247d04655db88eddc..9986da472c9682627e4f112c1f7485620c08081a 100644 --- a/substrate/frame/revive/src/lib.rs +++ b/substrate/frame/revive/src/lib.rs @@ -27,7 +27,10 @@ mod benchmarking_dummy; mod exec; mod gas; mod primitives; +use crate::exec::MomentOf; +use frame_support::traits::IsType; pub use primitives::*; +use sp_core::U256; mod limits; mod storage; @@ -36,7 +39,6 @@ mod wasm; pub mod chain_extension; pub mod debug; -pub mod migration; pub mod test_utils; pub mod weights; @@ -48,18 +50,17 @@ use crate::{ storage::{meter::Meter as StorageMeter, ContractInfo, DeletionQueueManager}, wasm::{CodeInfo, RuntimeCosts, WasmBlob}, }; -use codec::{Codec, Decode, Encode, HasCompact}; -use core::fmt::Debug; +use codec::{Codec, Decode, Encode}; use environmental::*; use frame_support::{ dispatch::{ DispatchErrorWithPostInfo, DispatchResultWithPostInfo, GetDispatchInfo, Pays, - PostDispatchInfo, RawOrigin, WithPostDispatchInfo, + PostDispatchInfo, RawOrigin, }, ensure, traits::{ fungible::{Inspect, Mutate, MutateHold}, - ConstU32, Contains, EnsureOrigin, Get, Time, + ConstU32, ConstU64, Contains, EnsureOrigin, Get, Time, }, weights::{Weight, WeightMeter}, BoundedVec, RuntimeDebugNoBound, @@ -70,15 +71,15 @@ use frame_system::{ EventRecord, Pallet as System, }; use scale_info::TypeInfo; +use sp_core::{H160, H256}; use sp_runtime::{ - traits::{BadOrigin, Convert, Dispatchable, Saturating, StaticLookup}, + traits::{BadOrigin, Convert, Dispatchable, Saturating}, DispatchError, }; pub use crate::{ - address::{AddressGenerator, DefaultAddressGenerator}, + address::{AddressMapper, DefaultAddressMapper}, debug::Tracing, - migration::{MigrateSequence, Migration, NoopMigration}, pallet::*, }; pub use weights::WeightInfo; @@ -86,15 +87,14 @@ pub use weights::WeightInfo; #[cfg(doc)] pub use crate::wasm::SyscallDoc; -type CodeHash = ::Hash; type TrieId = BoundedVec>; type BalanceOf = <::Currency as Inspect<::AccountId>>::Balance; -type CodeVec = BoundedVec::MaxCodeLen>; -type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; +type CodeVec = BoundedVec>; type EventRecordOf = EventRecord<::RuntimeEvent, ::Hash>; type DebugBuffer = BoundedVec>; +type ImmutableData = BoundedVec>; /// Used as a sentinel value when reading and writing contract memory. /// @@ -130,6 +130,7 @@ pub mod pallet { use crate::debug::Debugger; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; + use sp_core::U256; use sp_runtime::Perbill; /// The in-code storage version. @@ -207,7 +208,7 @@ pub mod pallet { /// /// # Note /// - /// It is safe to chage this value on a live chain as all refunds are pro rata. + /// It is safe to change this value on a live chain as all refunds are pro rata. #[pallet::constant] #[pallet::no_default_bounds] type DepositPerByte: Get>; @@ -216,7 +217,7 @@ pub mod pallet { /// /// # Note /// - /// It is safe to chage this value on a live chain as all refunds are pro rata. + /// It is safe to change this value on a live chain as all refunds are pro rata. #[pallet::constant] #[pallet::no_default_bounds] type DepositPerItem: Get>; @@ -228,17 +229,9 @@ pub mod pallet { #[pallet::constant] type CodeHashLockupDepositPercent: Get; - /// The address generator used to generate the addresses of contracts. + /// Only valid type is [`DefaultAddressMapper`]. #[pallet::no_default_bounds] - type AddressGenerator: AddressGenerator; - - /// The maximum length of a contract code in bytes. - /// - /// This value hugely affects the memory requirements of this pallet since all the code of - /// all contracts on the call stack will need to be held in memory. Setting of a correct - /// value will be enforced in [`Pallet::integrity_test`]. - #[pallet::constant] - type MaxCodeLen: Get; + type AddressMapper: AddressMapper>; /// Make contract callable functions marked as `#[unstable]` available. /// @@ -272,25 +265,6 @@ pub mod pallet { #[pallet::no_default_bounds] type InstantiateOrigin: EnsureOrigin; - /// The sequence of migration steps that will be applied during a migration. - /// - /// # Examples - /// ```ignore - /// use pallet_revive::migration::{v10, v11}; - /// # struct Runtime {}; - /// # struct Currency {}; - /// type Migrations = (v10::Migration, v11::Migration); - /// ``` - /// - /// If you have a single migration step, you can use a tuple with a single element: - /// ```ignore - /// use pallet_revive::migration::v10; - /// # struct Runtime {}; - /// # struct Currency {}; - /// type Migrations = (v10::Migration,); - /// ``` - type Migrations: MigrateSequence; - /// For most production chains, it's recommended to use the `()` implementation of this /// trait. This implementation offers additional logging when the log target /// "runtime::revive" is set to trace. @@ -306,13 +280,13 @@ pub mod pallet { BlockNumberFor, >; - /// The amount of memory in bytes that parachain nodes alot to the runtime. + /// The amount of memory in bytes that parachain nodes a lot to the runtime. /// /// This is used in [`Pallet::integrity_test`] to make sure that the runtime has enough /// memory to support this pallet if set to the correct value. type RuntimeMemory: Get; - /// The amount of memory in bytes that relay chain validators alot to the PoV. + /// The amount of memory in bytes that relay chain validators a lot to the PoV. /// /// This is used in [`Pallet::integrity_test`] to make sure that the runtime has enough /// memory to support this pallet if set to the correct value. @@ -320,6 +294,13 @@ pub mod pallet { /// This value is usually higher than [`Self::RuntimeMemory`] to account for the fact /// that validators have to hold all storage items in PvF memory. type PVFMemory: Get; + + /// The [EIP-155](https://eips.ethereum.org/EIPS/eip-155) chain ID. + /// + /// This is a unique identifier assigned to each blockchain network, + /// preventing replay attacks. + #[pallet::constant] + type ChainId: Get; } /// Container for different types that implement [`DefaultConfig`]` of this pallet. @@ -376,15 +357,12 @@ pub mod pallet { #[inject_runtime_type] type RuntimeCall = (); - - type AddressGenerator = DefaultAddressGenerator; + type AddressMapper = DefaultAddressMapper; type CallFilter = (); type ChainExtension = (); type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; type DepositPerByte = DepositPerByte; type DepositPerItem = DepositPerItem; - type MaxCodeLen = ConstU32<{ 123 * 1024 }>; - type Migrations = (); type Time = Self; type UnsafeUnstableInterface = ConstBool; type UploadOrigin = EnsureSigned; @@ -395,13 +373,14 @@ pub mod pallet { type Xcm = (); type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>; type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>; + type ChainId = ConstU64<{ 0 }>; } } #[pallet::event] pub enum Event { /// Contract deployed by address at the specified address. - Instantiated { deployer: T::AccountId, contract: T::AccountId }, + Instantiated { deployer: H160, contract: H160 }, /// Contract has been removed. /// @@ -411,34 +390,37 @@ pub mod pallet { /// `seal_terminate`. Terminated { /// The contract that was terminated. - contract: T::AccountId, + contract: H160, /// The account that received the contracts remaining balance - beneficiary: T::AccountId, + beneficiary: H160, }, /// Code with the specified hash has been stored. - CodeStored { code_hash: T::Hash, deposit_held: BalanceOf, uploader: T::AccountId }, + CodeStored { code_hash: H256, deposit_held: BalanceOf, uploader: H160 }, /// A custom event emitted by the contract. ContractEmitted { /// The contract that emitted the event. - contract: T::AccountId, + contract: H160, /// Data supplied by the contract. Metadata generated during contract compilation /// is needed to decode it. data: Vec, + /// A list of topics used to index the event. + /// Number of topics is capped by [`limits::NUM_EVENT_TOPICS`]. + topics: Vec, }, /// A code with the specified hash was removed. - CodeRemoved { code_hash: T::Hash, deposit_released: BalanceOf, remover: T::AccountId }, + CodeRemoved { code_hash: H256, deposit_released: BalanceOf, remover: H160 }, /// A contract's code was updated. ContractCodeUpdated { /// The contract that has been updated. - contract: T::AccountId, + contract: H160, /// New code hash that was set for the contract. - new_code_hash: T::Hash, + new_code_hash: H256, /// Previous code hash of the contract. - old_code_hash: T::Hash, + old_code_hash: H256, }, /// A contract was called either by a plain account or another contract. @@ -452,7 +434,7 @@ pub mod pallet { /// The caller of the `contract`. caller: Origin, /// The contract that was called. - contract: T::AccountId, + contract: H160, }, /// A contract delegate called a code hash. @@ -465,24 +447,16 @@ pub mod pallet { DelegateCalled { /// The contract that performed the delegate call and hence in whose context /// the `code_hash` is executed. - contract: T::AccountId, + contract: H160, /// The code hash that was delegate called. - code_hash: CodeHash, + code_hash: H256, }, /// Some funds have been transferred and held as storage deposit. - StorageDepositTransferredAndHeld { - from: T::AccountId, - to: T::AccountId, - amount: BalanceOf, - }, + StorageDepositTransferredAndHeld { from: H160, to: H160, amount: BalanceOf }, /// Some storage deposit funds have been transferred and released. - StorageDepositTransferredAndReleased { - from: T::AccountId, - to: T::AccountId, - amount: BalanceOf, - }, + StorageDepositTransferredAndReleased { from: H160, to: H160, amount: BalanceOf }, } #[pallet::error] @@ -493,8 +467,6 @@ pub mod pallet { InvalidCallFlags, /// The executed contract exhausted its gas limit. OutOfGas, - /// The output buffer supplied to a contract API call was too small. - OutputBufferTooSmall, /// Performing the requested transfer failed. Probably because there isn't enough /// free balance in the sender's account. TransferFailed, @@ -503,9 +475,6 @@ pub mod pallet { MaxCallDepthReached, /// No contract was found at the specified address. ContractNotFound, - /// The code supplied to `instantiate_with_code` exceeds the limit specified in the - /// current schedule. - CodeTooLarge, /// No code could be found at the supplied code hash. CodeNotFound, /// No code info could be found at the supplied code hash. @@ -559,10 +528,15 @@ pub mod pallet { /// A more detailed error can be found on the node console if debug messages are enabled /// by supplying `-lruntime::revive=debug`. CodeRejected, - /// A pending migration needs to complete before the extrinsic can be called. - MigrationInProgress, - /// Migrate dispatch call was attempted but no migration was performed. - NoMigrationPerformed, + /// The code blob supplied is larger than [`limits::code::BLOB_BYTES`]. + BlobTooLarge, + /// The static memory consumption of the blob will be larger than + /// [`limits::code::STATIC_MEMORY_BYTES`]. + StaticMemoryTooLarge, + /// The program contains a basic block that is larger than allowed. + BasicBlockTooLarge, + /// The program contains an invalid instruction. + InvalidInstruction, /// The contract has reached its maximum number of delegate dependencies. MaxDelegateDependenciesReached, /// The dependency was not found in the contract's delegate dependencies. @@ -579,6 +553,11 @@ pub mod pallet { InvalidStorageFlags, /// PolkaVM failed during code execution. Probably due to a malformed program. ExecutionFailed, + /// Failed to convert a U256 to a Balance. + BalanceConversionFailed, + /// Immutable data can only be set during deploys and only be read during calls. + /// Additionally, it is only valid to set the data once and it must not be empty. + InvalidImmutableAccess, } /// A reason for the pallet contracts placing a hold on funds. @@ -592,16 +571,19 @@ pub mod pallet { /// A mapping from a contract's code hash to its code. #[pallet::storage] - pub(crate) type PristineCode = StorageMap<_, Identity, CodeHash, CodeVec>; + pub(crate) type PristineCode = StorageMap<_, Identity, H256, CodeVec>; /// A mapping from a contract's code hash to its code info. #[pallet::storage] - pub(crate) type CodeInfoOf = StorageMap<_, Identity, CodeHash, CodeInfo>; + pub(crate) type CodeInfoOf = StorageMap<_, Identity, H256, CodeInfo>; /// The code associated with a given account. #[pallet::storage] - pub(crate) type ContractInfoOf = - StorageMap<_, Identity, T::AccountId, ContractInfo>; + pub(crate) type ContractInfoOf = StorageMap<_, Identity, H160, ContractInfo>; + + /// The immutable data associated with a given account. + #[pallet::storage] + pub(crate) type ImmutableDataOf = StorageMap<_, Identity, H160, ImmutableData>; /// Evicted contracts that await child trie deletion. /// @@ -616,12 +598,6 @@ pub mod pallet { pub(crate) type DeletionQueueCounter = StorageValue<_, DeletionQueueManager, ValueQuery>; - /// A migration can span across multiple blocks. This storage defines a cursor to track the - /// progress of the migration, enabling us to resume from the last completed position. - #[pallet::storage] - pub(crate) type MigrationInProgress = - StorageValue<_, migration::Cursor, OptionQuery>; - #[pallet::extra_constants] impl Pallet { #[pallet::constant_name(ApiVersion)] @@ -633,36 +609,16 @@ pub mod pallet { #[pallet::hooks] impl Hooks> for Pallet { fn on_idle(_block: BlockNumberFor, limit: Weight) -> Weight { - use migration::MigrateResult::*; let mut meter = WeightMeter::with_limit(limit); - - loop { - match Migration::::migrate(&mut meter) { - // There is not enough weight to perform a migration. - // We can't do anything more, so we return the used weight. - NoMigrationPerformed | InProgress { steps_done: 0 } => return meter.consumed(), - // Migration is still in progress, we can start the next step. - InProgress { .. } => continue, - // Either no migration is in progress, or we are done with all migrations, we - // can do some more other work with the remaining weight. - Completed | NoMigrationInProgress => break, - } - } - ContractInfo::::process_deletion_queue_batch(&mut meter); meter.consumed() } fn integrity_test() { - Migration::::integrity_test(); + use limits::code::STATIC_MEMORY_BYTES; - // Total runtime memory limit + // The memory available in the block building runtime let max_runtime_mem: u32 = T::RuntimeMemory::get(); - // Memory limits for a single contract: - // Value stack size: 1Mb per contract, default defined in wasmi - const MAX_STACK_SIZE: u32 = 1024 * 1024; - // Heap limit is normally 16 mempages of 64kb each = 1Mb per contract - let max_heap_size = limits::MEMORY_BYTES; // The root frame is not accounted in CALL_STACK_DEPTH let max_call_depth = limits::CALL_STACK_DEPTH.checked_add(1).expect("CallStack size is too big"); @@ -672,50 +628,36 @@ pub mod pallet { .checked_mul(2) .expect("MaxTransientStorageSize is too large"); - // Check that given configured `MaxCodeLen`, runtime heap memory limit can't be broken. - // - // In worst case, the decoded Wasm contract code would be `x16` times larger than the - // encoded one. This is because even a single-byte wasm instruction has 16-byte size in - // wasmi. This gives us `MaxCodeLen*16` safety margin. - // - // Next, the pallet keeps the Wasm blob for each - // contract, hence we add up `MaxCodeLen` to the safety margin. - // + // We only allow 50% of the runtime memory to be utilized by the contracts call + // stack, keeping the rest for other facilities, such as PoV, etc. + const TOTAL_MEMORY_DEVIDER: u32 = 2; + // The inefficiencies of the freeing-bump allocator // being used in the client for the runtime memory allocations, could lead to possible - // memory allocations for contract code grow up to `x4` times in some extreme cases, - // which gives us total multiplier of `17*4` for `MaxCodeLen`. - // - // That being said, for every contract executed in runtime, at least `MaxCodeLen*17*4` - // memory should be available. Note that maximum allowed heap memory and stack size per - // each contract (stack frame) should also be counted. - // - // The pallet holds transient storage with a size up to `max_transient_storage_size`. - // - // Finally, we allow 50% of the runtime memory to be utilized by the contracts call - // stack, keeping the rest for other facilities, such as PoV, etc. - // - // This gives us the following formula: + // memory allocations grow up to `x4` times in some extreme cases. + const MEMORY_ALLOCATOR_INEFFICENCY_DEVIDER: u32 = 4; + + // Check that the configured `STATIC_MEMORY_BYTES` fits into runtime memory. // - // `(MaxCodeLen * 17 * 4 + MAX_STACK_SIZE + max_heap_size) * max_call_depth + - // max_transient_storage_size < max_runtime_mem/2` + // `STATIC_MEMORY_BYTES` is the amount of memory that a contract can consume + // in memory and is enforced at upload time. // - // Hence the upper limit for the `MaxCodeLen` can be defined as follows: - let code_len_limit = max_runtime_mem - .saturating_div(2) + // Dynamic allocations are not available, yet. Hence are not taken into consideration + // here. + let static_memory_limit = max_runtime_mem + .saturating_div(TOTAL_MEMORY_DEVIDER) .saturating_sub(max_transient_storage_size) .saturating_div(max_call_depth) - .saturating_sub(max_heap_size) - .saturating_sub(MAX_STACK_SIZE) - .saturating_div(17 * 4); + .saturating_sub(STATIC_MEMORY_BYTES) + .saturating_div(MEMORY_ALLOCATOR_INEFFICENCY_DEVIDER); assert!( - T::MaxCodeLen::get() < code_len_limit, - "Given `CallStack` height {:?}, `MaxCodeLen` should be set less than {:?} \ + STATIC_MEMORY_BYTES < static_memory_limit, + "Given `CallStack` height {:?}, `STATIC_MEMORY_LIMIT` should be set less than {:?} \ (current value is {:?}), to avoid possible runtime oom issues.", max_call_depth, - code_len_limit, - T::MaxCodeLen::get(), + static_memory_limit, + STATIC_MEMORY_BYTES, ); // Validators are configured to be able to use more memory than block builders. This is @@ -735,6 +677,16 @@ pub mod pallet { .hash() .len() as u32; + let max_immutable_key_size = T::AccountId::max_encoded_len() as u32; + let max_immutable_size: u32 = ((max_block_ref_time / + (>::weight(&RuntimeCosts::SetImmutableData( + limits::IMMUTABLE_BYTES, + )) + .ref_time())) + .saturating_mul(limits::IMMUTABLE_BYTES.saturating_add(max_immutable_key_size) as u64)) + .try_into() + .expect("Immutable data size too big"); + // We can use storage to store items using the available block ref_time with the // `set_storage` host function. let max_storage_size: u32 = ((max_block_ref_time / @@ -744,6 +696,7 @@ pub mod pallet { }) .ref_time())) .saturating_mul(max_payload_size.saturating_add(max_key_size) as u64)) + .saturating_add(max_immutable_size.into()) .try_into() .expect("Storage size too big"); @@ -782,7 +735,8 @@ pub mod pallet { #[pallet::call] impl Pallet where - as HasCompact>::Type: Clone + Eq + PartialEq + Debug + TypeInfo + Encode, + BalanceOf: Into + TryFrom, + MomentOf: Into, { /// Makes a call to an account, optionally transferring some balance. /// @@ -804,13 +758,12 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::call().saturating_add(*gas_limit))] pub fn call( origin: OriginFor, - dest: AccountIdLookupOf, + dest: H160, #[pallet::compact] value: BalanceOf, gas_limit: Weight, #[pallet::compact] storage_deposit_limit: BalanceOf, data: Vec, ) -> DispatchResultWithPostInfo { - let dest = T::Lookup::lookup(dest)?; let mut output = Self::bare_call( origin, dest, @@ -836,19 +789,18 @@ pub mod pallet { /// must be supplied. #[pallet::call_index(1)] #[pallet::weight( - T::WeightInfo::instantiate(data.len() as u32, salt.len() as u32).saturating_add(*gas_limit) + T::WeightInfo::instantiate(data.len() as u32).saturating_add(*gas_limit) )] pub fn instantiate( origin: OriginFor, #[pallet::compact] value: BalanceOf, gas_limit: Weight, #[pallet::compact] storage_deposit_limit: BalanceOf, - code_hash: CodeHash, + code_hash: sp_core::H256, data: Vec, - salt: Vec, + salt: Option<[u8; 32]>, ) -> DispatchResultWithPostInfo { let data_len = data.len() as u32; - let salt_len = salt.len() as u32; let mut output = Self::bare_instantiate( origin, value, @@ -868,7 +820,7 @@ pub mod pallet { dispatch_result( output.result.map(|result| result.result), output.gas_consumed, - T::WeightInfo::instantiate(data_len, salt_len), + T::WeightInfo::instantiate(data_len), ) } @@ -887,7 +839,9 @@ pub mod pallet { /// from the caller to pay for the storage consumed. /// * `code`: The contract code to deploy in raw bytes. /// * `data`: The input data to pass to the contract constructor. - /// * `salt`: Used for the address derivation. See [`Pallet::contract_address`]. + /// * `salt`: Used for the address derivation. If `Some` is supplied then `CREATE2` + /// semantics are used. If `None` then `CRATE1` is used. + /// /// /// Instantiation is executed as follows: /// @@ -899,7 +853,7 @@ pub mod pallet { /// - The `deploy` function is executed in the context of the newly-created account. #[pallet::call_index(2)] #[pallet::weight( - T::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32, salt.len() as u32) + T::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32) .saturating_add(*gas_limit) )] pub fn instantiate_with_code( @@ -909,11 +863,10 @@ pub mod pallet { #[pallet::compact] storage_deposit_limit: BalanceOf, code: Vec, data: Vec, - salt: Vec, + salt: Option<[u8; 32]>, ) -> DispatchResultWithPostInfo { let code_len = code.len() as u32; let data_len = data.len() as u32; - let salt_len = salt.len() as u32; let mut output = Self::bare_instantiate( origin, value, @@ -933,7 +886,7 @@ pub mod pallet { dispatch_result( output.result.map(|result| result.result), output.gas_consumed, - T::WeightInfo::instantiate_with_code(code_len, data_len, salt_len), + T::WeightInfo::instantiate_with_code(code_len, data_len), ) } @@ -950,7 +903,7 @@ pub mod pallet { /// only be instantiated by permissioned entities. The same is true when uploading /// through [`Self::instantiate_with_code`]. #[pallet::call_index(3)] - #[pallet::weight(T::WeightInfo::upload_code_determinism_enforced(code.len() as u32))] + #[pallet::weight(T::WeightInfo::upload_code(code.len() as u32))] pub fn upload_code( origin: OriginFor, code: Vec, @@ -967,9 +920,8 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::remove_code())] pub fn remove_code( origin: OriginFor, - code_hash: CodeHash, + code_hash: sp_core::H256, ) -> DispatchResultWithPostInfo { - Migration::::ensure_migrated()?; let origin = ensure_signed(origin)?; >::remove(&origin, code_hash)?; // we waive the fee because removing unused code is beneficial @@ -990,22 +942,20 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::set_code())] pub fn set_code( origin: OriginFor, - dest: AccountIdLookupOf, - code_hash: CodeHash, + dest: H160, + code_hash: sp_core::H256, ) -> DispatchResult { - Migration::::ensure_migrated()?; ensure_root(origin)?; - let dest = T::Lookup::lookup(dest)?; >::try_mutate(&dest, |contract| { let contract = if let Some(contract) = contract { contract } else { - return Err(>::ContractNotFound.into()) + return Err(>::ContractNotFound.into()); }; >>::increment_refcount(code_hash)?; >>::decrement_refcount(contract.code_hash); Self::deposit_event(Event::ContractCodeUpdated { - contract: dest.clone(), + contract: dest, new_code_hash: code_hash, old_code_hash: contract.code_hash, }); @@ -1013,40 +963,6 @@ pub mod pallet { Ok(()) }) } - - /// When a migration is in progress, this dispatchable can be used to run migration steps. - /// Calls that contribute to advancing the migration have their fees waived, as it's helpful - /// for the chain. Note that while the migration is in progress, the pallet will also - /// leverage the `on_idle` hooks to run migration steps. - #[pallet::call_index(6)] - #[pallet::weight(T::WeightInfo::migrate().saturating_add(*weight_limit))] - pub fn migrate(origin: OriginFor, weight_limit: Weight) -> DispatchResultWithPostInfo { - use migration::MigrateResult::*; - ensure_signed(origin)?; - - let weight_limit = weight_limit.saturating_add(T::WeightInfo::migrate()); - let mut meter = WeightMeter::with_limit(weight_limit); - let result = Migration::::migrate(&mut meter); - - match result { - Completed => Ok(PostDispatchInfo { - actual_weight: Some(meter.consumed()), - pays_fee: Pays::No, - }), - InProgress { steps_done, .. } if steps_done > 0 => Ok(PostDispatchInfo { - actual_weight: Some(meter.consumed()), - pays_fee: Pays::No, - }), - InProgress { .. } => Ok(PostDispatchInfo { - actual_weight: Some(meter.consumed()), - pays_fee: Pays::Yes, - }), - NoMigrationInProgress | NoMigrationPerformed => { - let err: DispatchError = >::NoMigrationPerformed.into(); - Err(err.with_weight(meter.consumed())) - }, - } - } } } @@ -1066,7 +982,11 @@ fn dispatch_result( .map_err(|e| DispatchErrorWithPostInfo { post_info, error: e }) } -impl Pallet { +impl Pallet +where + BalanceOf: Into + TryFrom, + MomentOf: Into, +{ /// A generalized version of [`Self::call`]. /// /// Identical to [`Self::call`] but tailored towards being called by other code within the @@ -1075,7 +995,7 @@ impl Pallet { /// collection). pub fn bare_call( origin: OriginFor, - dest: T::AccountId, + dest: H160, value: BalanceOf, gas_limit: Weight, storage_deposit_limit: BalanceOf, @@ -1091,7 +1011,6 @@ impl Pallet { None }; let try_call = || { - Migration::::ensure_migrated()?; let origin = Origin::from_runtime_origin(origin)?; let mut storage_meter = StorageMeter::new(&origin, storage_deposit_limit, value)?; let result = ExecStack::>::run_call( @@ -1133,28 +1052,23 @@ impl Pallet { value: BalanceOf, gas_limit: Weight, mut storage_deposit_limit: BalanceOf, - code: Code>, + code: Code, data: Vec, - salt: Vec, + salt: Option<[u8; 32]>, debug: DebugInfo, collect_events: CollectEvents, - ) -> ContractInstantiateResult, EventRecordOf> { + ) -> ContractInstantiateResult, EventRecordOf> { let mut gas_meter = GasMeter::new(gas_limit); let mut storage_deposit = Default::default(); let mut debug_message = if debug == DebugInfo::UnsafeDebug { Some(DebugBuffer::default()) } else { None }; let try_instantiate = || { - Migration::::ensure_migrated()?; let instantiate_account = T::InstantiateOrigin::ensure_origin(origin.clone())?; let (executable, upload_deposit) = match code { Code::Upload(code) => { let upload_account = T::UploadOrigin::ensure_origin(origin)?; - let (executable, upload_deposit) = Self::try_upload_code( - upload_account, - code, - storage_deposit_limit, - debug_message.as_mut(), - )?; + let (executable, upload_deposit) = + Self::try_upload_code(upload_account, code, storage_deposit_limit)?; storage_deposit_limit.saturating_reduce(upload_deposit); (executable, upload_deposit) }, @@ -1171,7 +1085,7 @@ impl Pallet { &mut storage_meter, value, data, - &salt, + salt.as_ref(), debug_message.as_mut(), ); storage_deposit = storage_meter @@ -1187,7 +1101,7 @@ impl Pallet { }; ContractInstantiateResult { result: output - .map(|(account_id, result)| InstantiateReturnValue { result, account_id }) + .map(|(addr, result)| InstantiateReturnValue { result, addr }) .map_err(|e| e.error), gas_consumed: gas_meter.gas_consumed(), gas_required: gas_meter.gas_required(), @@ -1204,76 +1118,33 @@ impl Pallet { origin: OriginFor, code: Vec, storage_deposit_limit: BalanceOf, - ) -> CodeUploadResult, BalanceOf> { - Migration::::ensure_migrated()?; + ) -> CodeUploadResult> { let origin = T::UploadOrigin::ensure_origin(origin)?; - let (module, deposit) = Self::try_upload_code(origin, code, storage_deposit_limit, None)?; + let (module, deposit) = Self::try_upload_code(origin, code, storage_deposit_limit)?; Ok(CodeUploadReturnValue { code_hash: *module.code_hash(), deposit }) } /// Query storage of a specified contract under a specified key. - pub fn get_storage(address: T::AccountId, key: Vec) -> GetStorageResult { - if Migration::::in_progress() { - return Err(ContractAccessError::MigrationInProgress) - } + pub fn get_storage(address: H160, key: [u8; 32]) -> GetStorageResult { let contract_info = ContractInfoOf::::get(&address).ok_or(ContractAccessError::DoesntExist)?; - let maybe_value = contract_info.read( - &Key::try_from_var(key) - .map_err(|_| ContractAccessError::KeyDecodingFailed)? - .into(), - ); + let maybe_value = contract_info.read(&Key::from_fixed(key)); Ok(maybe_value) } - /// Determine the address of a contract. - /// - /// This is the address generation function used by contract instantiation. See - /// [`DefaultAddressGenerator`] for the default implementation. - pub fn contract_address( - deploying_address: &T::AccountId, - code_hash: &CodeHash, - input_data: &[u8], - salt: &[u8], - ) -> T::AccountId { - T::AddressGenerator::contract_address(deploying_address, code_hash, input_data, salt) - } - /// Uploads new code and returns the Wasm blob and deposit amount collected. fn try_upload_code( origin: T::AccountId, code: Vec, storage_deposit_limit: BalanceOf, - mut debug_message: Option<&mut DebugBuffer>, ) -> Result<(WasmBlob, BalanceOf), DispatchError> { - let mut module = WasmBlob::from_code(code, origin).map_err(|(err, msg)| { - debug_message.as_mut().map(|d| d.try_extend(msg.bytes())); - err - })?; + let mut module = WasmBlob::from_code(code, origin)?; let deposit = module.store_code()?; ensure!(storage_deposit_limit >= deposit, >::StorageDepositLimitExhausted); Ok((module, deposit)) } - /// Deposit a pallet contracts event. - fn deposit_event(event: Event) { - >::deposit_event(::RuntimeEvent::from(event)) - } - - /// Deposit a pallet contracts indexed event. - fn deposit_indexed_event(topics: Vec, event: Event) { - >::deposit_event_indexed( - &topics, - ::RuntimeEvent::from(event).into(), - ) - } - - /// Return the existential deposit of [`Config::Currency`]. - fn min_balance() -> BalanceOf { - >>::minimum_balance() - } - /// Run the supplied function `f` if no other instance of this pallet is on the stack. fn run_guarded Result>(f: F) -> Result { executing_contract::using_once(&mut false, || { @@ -1294,17 +1165,28 @@ impl Pallet { } } +impl Pallet { + /// Return the existential deposit of [`Config::Currency`]. + fn min_balance() -> BalanceOf { + >>::minimum_balance() + } + + /// Deposit a pallet contracts event. + fn deposit_event(event: Event) { + >::deposit_event(::RuntimeEvent::from(event)) + } +} + // Set up a global reference to the boolean flag used for the re-entrancy guard. environmental!(executing_contract: bool); sp_api::decl_runtime_apis! { /// The API used to dry-run contract interactions. #[api_version(1)] - pub trait ReviveApi where + pub trait ReviveApi where AccountId: Codec, Balance: Codec, BlockNumber: Codec, - Hash: Codec, EventRecord: Codec, { /// Perform a call from a specified account to a given contract. @@ -1312,7 +1194,7 @@ sp_api::decl_runtime_apis! { /// See [`crate::Pallet::bare_call`]. fn call( origin: AccountId, - dest: AccountId, + dest: H160, value: Balance, gas_limit: Option, storage_deposit_limit: Option, @@ -1327,10 +1209,10 @@ sp_api::decl_runtime_apis! { value: Balance, gas_limit: Option, storage_deposit_limit: Option, - code: Code, + code: Code, data: Vec, - salt: Vec, - ) -> ContractInstantiateResult; + salt: Option<[u8; 32]>, + ) -> ContractInstantiateResult; /// Upload new code without instantiating a contract from it. /// @@ -1339,7 +1221,7 @@ sp_api::decl_runtime_apis! { origin: AccountId, code: Vec, storage_deposit_limit: Option, - ) -> CodeUploadResult; + ) -> CodeUploadResult; /// Query a given storage key in a given contract. /// @@ -1347,8 +1229,8 @@ sp_api::decl_runtime_apis! { /// specified account and `Ok(None)` if it doesn't. If the account specified by the address /// doesn't exist, or doesn't have a contract then `Err` is returned. fn get_storage( - address: AccountId, - key: Vec, + address: H160, + key: [u8; 32], ) -> GetStorageResult; } } diff --git a/substrate/frame/revive/src/limits.rs b/substrate/frame/revive/src/limits.rs index 1a714a89d4869b0712e21f97a5e2cd2502684bb3..c6d5ef8d8b1b2b6e51c7114233c7bf02e8a6c75f 100644 --- a/substrate/frame/revive/src/limits.rs +++ b/substrate/frame/revive/src/limits.rs @@ -22,15 +22,21 @@ //! is meant for. This is true for either increasing or decreasing the limit. //! //! Limits in this file are different from the limits configured on the [`Config`] trait which are -//! generally only affect actions that cannot be performed by a contract: For example, uploading new -//! code only be done via a transaction but not by a contract. Hence the maximum contract size can -//! be raised (but not lowered) by the runtime configuration. +//! generally only affect actions that cannot be performed by a contract: For example things related +//! to deposits and weights are allowed to be changed as they are paid by root callers which +//! are not contracts. +//! +//! Exceptions to this rule apply: Limits in the [`code`] module can be increased +//! without emulating the old values for existing contracts. Reason is that those limits are only +//! applied **once** at code upload time. Since this action cannot be performed by contracts we +//! can change those limits without breaking existing contracts. Please keep in mind that we should +//! only ever **increase** those values but never decrease. /// The maximum depth of the call stack. /// /// A 0 means that no callings of other contracts are possible. In other words only the origin /// called "root contract" is allowed to execute then. -pub const CALL_STACK_DEPTH: u32 = 5; +pub const CALL_STACK_DEPTH: u32 = 10; /// The maximum number of topics a call to [`crate::SyscallDoc::deposit_event`] can emit. /// @@ -40,10 +46,7 @@ pub const NUM_EVENT_TOPICS: u32 = 4; /// The maximum number of code hashes a contract can lock. pub const DELEGATE_DEPENDENCIES: u32 = 32; -/// How much memory do we allow the contract to allocate. -pub const MEMORY_BYTES: u32 = 16 * 64 * 1024; - -/// Maximum size of events (excluding topics) and storage values. +/// Maximum size of events (including topics) and storage values. pub const PAYLOAD_BYTES: u32 = 512; /// The maximum size of the transient storage in bytes. @@ -58,3 +61,124 @@ pub const STORAGE_KEY_BYTES: u32 = 128; /// /// The buffer will always be disabled for on-chain execution. pub const DEBUG_BUFFER_BYTES: u32 = 2 * 1024 * 1024; + +/// The page size in which PolkaVM should allocate memory chunks. +pub const PAGE_SIZE: u32 = 4 * 1024; + +/// The maximum amount of immutable bytes a single contract can store. +/// +/// The current limit of 4kb allows storing up 16 U256 immutable variables. +/// Which should always be enough because Solidity allows for 16 local (stack) variables. +pub const IMMUTABLE_BYTES: u32 = 4 * 1024; + +/// Limits that are only enforced on code upload. +/// +/// # Note +/// +/// This limit can be increased later without breaking existing contracts +/// as it is only enforced at code upload time. Code already uploaded +/// will not be affected by those limits. +pub mod code { + use super::PAGE_SIZE; + use crate::{CodeVec, Config, Error, LOG_TARGET}; + use alloc::vec::Vec; + use sp_runtime::DispatchError; + + /// The maximum length of a code blob in bytes. + /// + /// This mostly exist to prevent parsing too big blobs and to + /// have a maximum encoded length. The actual memory calculation + /// is purely based off [`STATIC_MEMORY_BYTES`]. + pub const BLOB_BYTES: u32 = 256 * 1024; + + /// Maximum size the program is allowed to take in memory. + /// + /// This includes data and code. Increasing this limit will allow + /// for more code or more data. However, since code will decompress + /// into a bigger representation on compilation it will only increase + /// the allowed code size by [`BYTE_PER_INSTRUCTION`]. + pub const STATIC_MEMORY_BYTES: u32 = 1024 * 1024; + + /// How much memory each instruction will take in-memory after compilation. + /// + /// This is `size_of() + 16`. But we don't use `usize` here so it isn't + /// different on the native runtime (used for testing). + const BYTES_PER_INSTRUCTION: u32 = 20; + + /// The code is stored multiple times as part of the compiled program. + const EXTRA_OVERHEAD_PER_CODE_BYTE: u32 = 4; + + /// The maximum size of a basic block in number of instructions. + /// + /// We need to limit the size of basic blocks because the interpreters lazy compilation + /// compiles one basic block at a time. A malicious program could trigger the compilation + /// of the whole program by creating one giant basic block otherwise. + const BASIC_BLOCK_SIZE: u32 = 1000; + + /// Make sure that the various program parts are within the defined limits. + pub fn enforce(blob: Vec) -> Result { + fn round_page(n: u32) -> u64 { + // performing the rounding in u64 in order to prevent overflow + u64::from(n).next_multiple_of(PAGE_SIZE.into()) + } + + let blob: CodeVec = blob.try_into().map_err(|_| >::BlobTooLarge)?; + + let program = polkavm::ProgramBlob::parse(blob.as_slice().into()).map_err(|err| { + log::debug!(target: LOG_TARGET, "failed to parse polkavm blob: {err:?}"); + Error::::CodeRejected + })?; + + // This scans the whole program but we only do it once on code deployment. + // It is safe to do unchecked math in u32 because the size of the program + // was already checked above. + use polkavm_common::program::ISA32_V1_NoSbrk as ISA; + let mut num_instructions: u32 = 0; + let mut max_basic_block_size: u32 = 0; + let mut basic_block_size: u32 = 0; + for inst in program.instructions(ISA) { + num_instructions += 1; + basic_block_size += 1; + if inst.kind.opcode().starts_new_basic_block() { + max_basic_block_size = max_basic_block_size.max(basic_block_size); + basic_block_size = 0; + } + if matches!(inst.kind, polkavm::program::Instruction::invalid) { + log::debug!(target: LOG_TARGET, "invalid instruction at offset {}", inst.offset); + return Err(>::InvalidInstruction.into()) + } + } + + if max_basic_block_size > BASIC_BLOCK_SIZE { + log::debug!(target: LOG_TARGET, "basic block too large: {max_basic_block_size} limit: {BASIC_BLOCK_SIZE}"); + return Err(Error::::BasicBlockTooLarge.into()) + } + + // The memory consumptions is the byte size of the whole blob, + // minus the RO data payload in the blob, + // minus the RW data payload in the blob, + // plus the RO data in memory (which is always equal or bigger than the RO payload), + // plus RW data in memory, plus stack size in memory. + // plus the overhead of instructions in memory which is derived from the code + // size itself and the number of instruction + let memory_size = (blob.len() as u64) + .saturating_add(round_page(program.ro_data_size())) + .saturating_sub(program.ro_data().len() as u64) + .saturating_add(round_page(program.rw_data_size())) + .saturating_sub(program.rw_data().len() as u64) + .saturating_add(round_page(program.stack_size())) + .saturating_add( + u64::from(num_instructions).saturating_mul(BYTES_PER_INSTRUCTION.into()), + ) + .saturating_add( + (program.code().len() as u64).saturating_mul(EXTRA_OVERHEAD_PER_CODE_BYTE.into()), + ); + + if memory_size > STATIC_MEMORY_BYTES.into() { + log::debug!(target: LOG_TARGET, "static memory too large: {memory_size} limit: {STATIC_MEMORY_BYTES}"); + return Err(Error::::StaticMemoryTooLarge.into()) + } + + Ok(blob) + } +} diff --git a/substrate/frame/revive/src/migration.rs b/substrate/frame/revive/src/migration.rs deleted file mode 100644 index b67467b322f5f014255dc54ba7e8fc69b2674b26..0000000000000000000000000000000000000000 --- a/substrate/frame/revive/src/migration.rs +++ /dev/null @@ -1,650 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Multi-block Migration framework for pallet-revive. -//! -//! This module allows us to define a migration as a sequence of [`MigrationStep`]s that can be -//! executed across multiple blocks. -//! -//! # Usage -//! -//! A migration step is defined under `src/migration/vX.rs`, where `X` is the version number. -//! For example, `vX.rs` defines a migration from version `X - 1` to version `X`. -//! -//! ## Example: -//! -//! To configure a migration to `v11` for a runtime using `v10` of pallet-revive on the chain, -//! you would set the `Migrations` type as follows: -//! -//! ```ignore -//! use pallet_revive::migration::{v10, v11}; -//! # pub enum Runtime {}; -//! # struct Currency; -//! type Migrations = (v10::Migration, v11::Migration); -//! ``` -//! -//! ## Notes: -//! -//! - Migrations should always be tested with `try-runtime` before being deployed. -//! - By testing with `try-runtime` against a live network, you ensure that all migration steps work -//! and that you have included the required steps. -//! -//! ## Low Level / Implementation Details -//! -//! When a migration starts and [`OnRuntimeUpgrade::on_runtime_upgrade`] is called, instead of -//! performing the actual migration, we set a custom storage item [`MigrationInProgress`]. -//! This storage item defines a [`Cursor`] for the current migration. -//! -//! If the [`MigrationInProgress`] storage item exists, it means a migration is in progress, and its -//! value holds a cursor for the current migration step. These migration steps are executed during -//! [`Hooks::on_idle`] or when the [`Pallet::migrate`] dispatchable is -//! called. -//! -//! While the migration is in progress, all dispatchables except `migrate`, are blocked, and returns -//! a `MigrationInProgress` error. - -include!(concat!(env!("OUT_DIR"), "/migration_codegen.rs")); - -use crate::{weights::WeightInfo, Config, Error, MigrationInProgress, Pallet, Weight, LOG_TARGET}; -use codec::{Codec, Decode}; -use core::marker::PhantomData; -use frame_support::{ - pallet_prelude::*, - traits::{ConstU32, OnRuntimeUpgrade}, - weights::WeightMeter, -}; -use sp_runtime::Saturating; - -#[cfg(feature = "try-runtime")] -use alloc::vec::Vec; -#[cfg(feature = "try-runtime")] -use sp_runtime::TryRuntimeError; - -const PROOF_ENCODE: &str = "Tuple::max_encoded_len() < Cursor::max_encoded_len()` is verified in `Self::integrity_test()`; qed"; -const PROOF_DECODE: &str = - "We encode to the same type in this trait only. No other code touches this item; qed"; - -fn invalid_version(version: StorageVersion) -> ! { - panic!("Required migration {version:?} not supported by this runtime. This is a bug."); -} - -/// The cursor used to encode the position (usually the last iterated key) of the current migration -/// step. -pub type Cursor = BoundedVec>; - -/// IsFinished describes whether a migration is finished or not. -pub enum IsFinished { - Yes, - No, -} - -/// A trait that allows to migrate storage from one version to another. -/// -/// The migration is done in steps. The migration is finished when -/// `step()` returns `IsFinished::Yes`. -pub trait MigrationStep: Codec + MaxEncodedLen + Default { - /// Returns the version of the migration. - const VERSION: u16; - - /// Returns the maximum weight that can be consumed in a single step. - fn max_step_weight() -> Weight; - - /// Process one step of the migration. - /// - /// Returns whether the migration is finished. - fn step(&mut self, meter: &mut WeightMeter) -> IsFinished; - - /// Verify that the migration step fits into `Cursor`, and that `max_step_weight` is not greater - /// than `max_block_weight`. - fn integrity_test(max_block_weight: Weight) { - if Self::max_step_weight().any_gt(max_block_weight) { - panic!( - "Invalid max_step_weight for Migration {}. Value should be lower than {}", - Self::VERSION, - max_block_weight - ); - } - - let len = ::max_encoded_len(); - let max = Cursor::bound(); - if len > max { - panic!( - "Migration {} has size {} which is bigger than the maximum of {}", - Self::VERSION, - len, - max, - ); - } - } - - /// Execute some pre-checks prior to running the first step of this migration. - #[cfg(feature = "try-runtime")] - fn pre_upgrade_step() -> Result, TryRuntimeError> { - Ok(Vec::new()) - } - - /// Execute some post-checks after running the last step of this migration. - #[cfg(feature = "try-runtime")] - fn post_upgrade_step(_state: Vec) -> Result<(), TryRuntimeError> { - Ok(()) - } -} - -/// A noop migration that can be used when there is no migration to be done for a given version. -#[doc(hidden)] -#[derive(frame_support::DefaultNoBound, Encode, Decode, MaxEncodedLen)] -pub struct NoopMigration; - -impl MigrationStep for NoopMigration { - const VERSION: u16 = N; - fn max_step_weight() -> Weight { - Weight::zero() - } - fn step(&mut self, _meter: &mut WeightMeter) -> IsFinished { - log::debug!(target: LOG_TARGET, "Noop migration for version {}", N); - IsFinished::Yes - } -} - -mod private { - use crate::migration::MigrationStep; - pub trait Sealed {} - #[impl_trait_for_tuples::impl_for_tuples(10)] - #[tuple_types_custom_trait_bound(MigrationStep)] - impl Sealed for Tuple {} -} - -/// Defines a sequence of migrations. -/// -/// The sequence must be defined by a tuple of migrations, each of which must implement the -/// `MigrationStep` trait. Migrations must be ordered by their versions with no gaps. -pub trait MigrateSequence: private::Sealed { - /// Returns the range of versions that this migrations sequence can handle. - /// Migrations must be ordered by their versions with no gaps. - /// - /// The following code will fail to compile: - /// - /// ```compile_fail - /// # use pallet_revive::{NoopMigration, MigrateSequence}; - /// let _ = <(NoopMigration<1>, NoopMigration<3>)>::VERSION_RANGE; - /// ``` - /// The following code will compile: - /// ``` - /// # use pallet_revive::{NoopMigration, MigrateSequence}; - /// let _ = <(NoopMigration<1>, NoopMigration<2>)>::VERSION_RANGE; - /// ``` - const VERSION_RANGE: (u16, u16); - - /// Returns the default cursor for the given version. - fn new(version: StorageVersion) -> Cursor; - - #[cfg(feature = "try-runtime")] - fn pre_upgrade_step(_version: StorageVersion) -> Result, TryRuntimeError> { - Ok(Vec::new()) - } - - #[cfg(feature = "try-runtime")] - fn post_upgrade_step(_version: StorageVersion, _state: Vec) -> Result<(), TryRuntimeError> { - Ok(()) - } - - /// Execute the migration step until the available weight is consumed. - fn steps(version: StorageVersion, cursor: &[u8], meter: &mut WeightMeter) -> StepResult; - - /// Verify that the migration step fits into `Cursor`, and that `max_step_weight` is not greater - /// than `max_block_weight`. - fn integrity_test(max_block_weight: Weight); - - /// Returns whether migrating from `in_storage` to `target` is supported. - /// - /// A migration is supported if `VERSION_RANGE` is (in_storage + 1, target). - fn is_upgrade_supported(in_storage: StorageVersion, target: StorageVersion) -> bool { - let (low, high) = Self::VERSION_RANGE; - target == high && in_storage + 1 == low - } -} - -/// Performs all necessary migrations based on `StorageVersion`. -/// -/// If `TEST_ALL_STEPS == true` and `try-runtime` is enabled, this will run all the migrations -/// inside `on_runtime_upgrade`. This should be set to false in tests that want to ensure the step -/// by step migration works. -pub struct Migration(PhantomData); - -#[cfg(feature = "try-runtime")] -impl Migration { - fn run_all_steps() -> Result<(), TryRuntimeError> { - let mut meter = &mut WeightMeter::new(); - let name = >::name(); - loop { - let in_progress_version = >::on_chain_storage_version() + 1; - let state = T::Migrations::pre_upgrade_step(in_progress_version)?; - let before = meter.consumed(); - let status = Self::migrate(&mut meter); - log::info!( - target: LOG_TARGET, - "{name}: Migration step {:?} weight = {}", - in_progress_version, - meter.consumed() - before - ); - T::Migrations::post_upgrade_step(in_progress_version, state)?; - if matches!(status, MigrateResult::Completed) { - break - } - } - - let name = >::name(); - log::info!(target: LOG_TARGET, "{name}: Migration steps weight = {}", meter.consumed()); - Ok(()) - } -} - -impl OnRuntimeUpgrade for Migration { - fn on_runtime_upgrade() -> Weight { - let name = >::name(); - let in_code_version = >::in_code_storage_version(); - let on_chain_version = >::on_chain_storage_version(); - - if on_chain_version == in_code_version { - log::warn!( - target: LOG_TARGET, - "{name}: No Migration performed storage_version = latest_version = {:?}", - &on_chain_version - ); - return T::WeightInfo::on_runtime_upgrade_noop() - } - - // In case a migration is already in progress we create the next migration - // (if any) right when the current one finishes. - if Self::in_progress() { - log::warn!( - target: LOG_TARGET, - "{name}: Migration already in progress {:?}", - &on_chain_version - ); - - return T::WeightInfo::on_runtime_upgrade_in_progress() - } - - log::info!( - target: LOG_TARGET, - "{name}: Upgrading storage from {on_chain_version:?} to {in_code_version:?}.", - ); - - let cursor = T::Migrations::new(on_chain_version + 1); - MigrationInProgress::::set(Some(cursor)); - - #[cfg(feature = "try-runtime")] - if TEST_ALL_STEPS { - Self::run_all_steps().unwrap(); - } - - T::WeightInfo::on_runtime_upgrade() - } - - #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result, TryRuntimeError> { - // We can't really do much here as our migrations do not happen during the runtime upgrade. - // Instead, we call the migrations `pre_upgrade` and `post_upgrade` hooks when we iterate - // over our migrations. - let on_chain_version = >::on_chain_storage_version(); - let in_code_version = >::in_code_storage_version(); - - if on_chain_version == in_code_version { - return Ok(Default::default()) - } - - log::debug!( - target: LOG_TARGET, - "Requested migration of {} from {:?}(on-chain storage version) to {:?}(in-code storage version)", - >::name(), on_chain_version, in_code_version - ); - - ensure!( - T::Migrations::is_upgrade_supported(on_chain_version, in_code_version), - "Unsupported upgrade: VERSION_RANGE should be (on-chain storage version + 1, in-code storage version)" - ); - - Ok(Default::default()) - } - - #[cfg(feature = "try-runtime")] - fn post_upgrade(_state: Vec) -> Result<(), TryRuntimeError> { - if !TEST_ALL_STEPS { - return Ok(()) - } - - log::info!(target: LOG_TARGET, "=== POST UPGRADE CHECKS ==="); - - // Ensure that the hashing algorithm is correct for each storage map. - if let Some(hash) = crate::CodeInfoOf::::iter_keys().next() { - crate::CodeInfoOf::::get(hash).expect("CodeInfo exists for hash; qed"); - } - if let Some(hash) = crate::PristineCode::::iter_keys().next() { - crate::PristineCode::::get(hash).expect("PristineCode exists for hash; qed"); - } - if let Some(account_id) = crate::ContractInfoOf::::iter_keys().next() { - crate::ContractInfoOf::::get(account_id) - .expect("ContractInfo exists for account_id; qed"); - } - if let Some(nonce) = crate::DeletionQueue::::iter_keys().next() { - crate::DeletionQueue::::get(nonce).expect("DeletionQueue exists for nonce; qed"); - } - - Ok(()) - } -} - -/// The result of running the migration. -#[derive(Debug, PartialEq)] -pub enum MigrateResult { - /// No migration was performed - NoMigrationPerformed, - /// No migration currently in progress - NoMigrationInProgress, - /// A migration is in progress - InProgress { steps_done: u32 }, - /// All migrations are completed - Completed, -} - -/// The result of running a migration step. -#[derive(Debug, PartialEq)] -pub enum StepResult { - InProgress { cursor: Cursor, steps_done: u32 }, - Completed { steps_done: u32 }, -} - -impl Migration { - /// Verify that each migration's step of the [`Config::Migrations`] sequence fits into - /// `Cursor`. - pub(crate) fn integrity_test() { - let max_weight = ::BlockWeights::get().max_block; - T::Migrations::integrity_test(max_weight) - } - - /// Execute the multi-step migration. - /// Returns whether or not a migration is in progress - pub(crate) fn migrate(mut meter: &mut WeightMeter) -> MigrateResult { - let name = >::name(); - - if meter.try_consume(T::WeightInfo::migrate()).is_err() { - return MigrateResult::NoMigrationPerformed - } - - MigrationInProgress::::mutate_exists(|progress| { - let Some(cursor_before) = progress.as_mut() else { - meter.consume(T::WeightInfo::migration_noop()); - return MigrateResult::NoMigrationInProgress - }; - - // if a migration is running it is always upgrading to the next version - let storage_version = >::on_chain_storage_version(); - let in_progress_version = storage_version + 1; - - log::info!( - target: LOG_TARGET, - "{name}: Migrating from {:?} to {:?},", - storage_version, - in_progress_version, - ); - - let result = - match T::Migrations::steps(in_progress_version, cursor_before.as_ref(), &mut meter) - { - StepResult::InProgress { cursor, steps_done } => { - *progress = Some(cursor); - MigrateResult::InProgress { steps_done } - }, - StepResult::Completed { steps_done } => { - in_progress_version.put::>(); - if >::in_code_storage_version() != in_progress_version { - log::info!( - target: LOG_TARGET, - "{name}: Next migration is {:?},", - in_progress_version + 1 - ); - *progress = Some(T::Migrations::new(in_progress_version + 1)); - MigrateResult::InProgress { steps_done } - } else { - log::info!( - target: LOG_TARGET, - "{name}: All migrations done. At version {:?},", - in_progress_version - ); - *progress = None; - MigrateResult::Completed - } - }, - }; - - result - }) - } - - pub(crate) fn ensure_migrated() -> DispatchResult { - if Self::in_progress() { - Err(Error::::MigrationInProgress.into()) - } else { - Ok(()) - } - } - - pub(crate) fn in_progress() -> bool { - MigrationInProgress::::exists() - } -} - -#[impl_trait_for_tuples::impl_for_tuples(10)] -#[tuple_types_custom_trait_bound(MigrationStep)] -impl MigrateSequence for Tuple { - const VERSION_RANGE: (u16, u16) = { - let mut versions: (u16, u16) = (0, 0); - for_tuples!( - #( - match versions { - (0, 0) => { - versions = (Tuple::VERSION, Tuple::VERSION); - }, - (min_version, last_version) if Tuple::VERSION == last_version + 1 => { - versions = (min_version, Tuple::VERSION); - }, - _ => panic!("Migrations must be ordered by their versions with no gaps.") - } - )* - ); - versions - }; - - fn new(version: StorageVersion) -> Cursor { - for_tuples!( - #( - if version == Tuple::VERSION { - return Tuple::default().encode().try_into().expect(PROOF_ENCODE) - } - )* - ); - invalid_version(version) - } - - #[cfg(feature = "try-runtime")] - /// Execute the pre-checks of the step associated with this version. - fn pre_upgrade_step(version: StorageVersion) -> Result, TryRuntimeError> { - for_tuples!( - #( - if version == Tuple::VERSION { - return Tuple::pre_upgrade_step() - } - )* - ); - invalid_version(version) - } - - #[cfg(feature = "try-runtime")] - /// Execute the post-checks of the step associated with this version. - fn post_upgrade_step(version: StorageVersion, state: Vec) -> Result<(), TryRuntimeError> { - for_tuples!( - #( - if version == Tuple::VERSION { - return Tuple::post_upgrade_step(state) - } - )* - ); - invalid_version(version) - } - - fn steps(version: StorageVersion, mut cursor: &[u8], meter: &mut WeightMeter) -> StepResult { - for_tuples!( - #( - if version == Tuple::VERSION { - let mut migration = ::decode(&mut cursor) - .expect(PROOF_DECODE); - let max_weight = Tuple::max_step_weight(); - let mut steps_done = 0; - while meter.can_consume(max_weight) { - steps_done.saturating_accrue(1); - if matches!(migration.step(meter), IsFinished::Yes) { - return StepResult::Completed{ steps_done } - } - } - return StepResult::InProgress{cursor: migration.encode().try_into().expect(PROOF_ENCODE), steps_done } - } - )* - ); - invalid_version(version) - } - - fn integrity_test(max_block_weight: Weight) { - for_tuples!( - #( - Tuple::integrity_test(max_block_weight); - )* - ); - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::{ - migration::codegen::LATEST_MIGRATION_VERSION, - tests::{ExtBuilder, Test}, - }; - - #[derive(Default, Encode, Decode, MaxEncodedLen)] - struct MockMigration { - // MockMigration needs `N` steps to finish - count: u16, - } - - impl MigrationStep for MockMigration { - const VERSION: u16 = N; - fn max_step_weight() -> Weight { - Weight::from_all(1) - } - fn step(&mut self, meter: &mut WeightMeter) -> IsFinished { - assert!(self.count != N); - self.count += 1; - meter.consume(Weight::from_all(1)); - if self.count == N { - IsFinished::Yes - } else { - IsFinished::No - } - } - } - - #[test] - fn test_storage_version_matches_last_migration_file() { - assert_eq!(StorageVersion::new(LATEST_MIGRATION_VERSION), crate::pallet::STORAGE_VERSION); - } - - #[test] - fn version_range_works() { - let range = <(MockMigration<1>, MockMigration<2>)>::VERSION_RANGE; - assert_eq!(range, (1, 2)); - } - - #[test] - fn is_upgrade_supported_works() { - type Migrations = (MockMigration<9>, MockMigration<10>, MockMigration<11>); - assert!(Migrations::is_upgrade_supported(StorageVersion::new(8), StorageVersion::new(11))); - assert!(!Migrations::is_upgrade_supported(StorageVersion::new(9), StorageVersion::new(11))); - assert!(!Migrations::is_upgrade_supported(StorageVersion::new(8), StorageVersion::new(12))); - } - - #[test] - fn steps_works() { - type Migrations = (MockMigration<2>, MockMigration<3>); - let version = StorageVersion::new(2); - let mut cursor = Migrations::new(version); - - let mut meter = WeightMeter::with_limit(Weight::from_all(1)); - let result = Migrations::steps(version, &cursor, &mut meter); - cursor = alloc::vec![1u8, 0].try_into().unwrap(); - assert_eq!(result, StepResult::InProgress { cursor: cursor.clone(), steps_done: 1 }); - assert_eq!(meter.consumed(), Weight::from_all(1)); - - let mut meter = WeightMeter::with_limit(Weight::from_all(1)); - assert_eq!( - Migrations::steps(version, &cursor, &mut meter), - StepResult::Completed { steps_done: 1 } - ); - } - - #[test] - fn no_migration_in_progress_works() { - type TestMigration = Migration; - - ExtBuilder::default().build().execute_with(|| { - assert_eq!(StorageVersion::get::>(), LATEST_MIGRATION_VERSION); - assert_eq!( - TestMigration::migrate(&mut WeightMeter::new()), - MigrateResult::NoMigrationInProgress - ) - }); - } - - #[test] - fn migration_works() { - type TestMigration = Migration; - - ExtBuilder::default() - .set_storage_version(LATEST_MIGRATION_VERSION - 2) - .build() - .execute_with(|| { - assert_eq!(StorageVersion::get::>(), LATEST_MIGRATION_VERSION - 2); - TestMigration::on_runtime_upgrade(); - for (version, status) in [ - (LATEST_MIGRATION_VERSION - 1, MigrateResult::InProgress { steps_done: 1 }), - (LATEST_MIGRATION_VERSION, MigrateResult::Completed), - ] { - assert_eq!(TestMigration::migrate(&mut WeightMeter::new()), status); - assert_eq!( - >::on_chain_storage_version(), - StorageVersion::new(version) - ); - } - - assert_eq!( - TestMigration::migrate(&mut WeightMeter::new()), - MigrateResult::NoMigrationInProgress - ); - assert_eq!(StorageVersion::get::>(), LATEST_MIGRATION_VERSION); - }); - } -} diff --git a/substrate/frame/revive/src/primitives.rs b/substrate/frame/revive/src/primitives.rs index a4a1133b71041969fecafbd2496879c249ba9f5b..67bc144c3dd233924928695afdbe5a5057ce46a9 100644 --- a/substrate/frame/revive/src/primitives.rs +++ b/substrate/frame/revive/src/primitives.rs @@ -17,6 +17,7 @@ //! A crate that hosts a common definitions that are relevant for the pallet-revive. +use crate::H160; use alloc::vec::Vec; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::weights::Weight; @@ -86,12 +87,11 @@ pub type ContractExecResult = ContractResult, Balance, EventRecord>; /// Result type of a `bare_instantiate` call as well as `ContractsApi::instantiate`. -pub type ContractInstantiateResult = - ContractResult, DispatchError>, Balance, EventRecord>; +pub type ContractInstantiateResult = + ContractResult, Balance, EventRecord>; /// Result type of a `bare_code_upload` call. -pub type CodeUploadResult = - Result, DispatchError>; +pub type CodeUploadResult = Result, DispatchError>; /// Result type of a `get_storage` call. pub type GetStorageResult = Result>, ContractAccessError>; @@ -103,12 +103,10 @@ pub enum ContractAccessError { DoesntExist, /// Storage key cannot be decoded from the provided input data. KeyDecodingFailed, - /// Storage is migrating. Try again later. - MigrationInProgress, } /// Output of a contract call or instantiation which ran to completion. -#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] +#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo, Default)] pub struct ExecReturnValue { /// Flags passed along by `seal_return`. Empty when `seal_return` was never called. pub flags: ReturnFlags, @@ -125,29 +123,29 @@ impl ExecReturnValue { /// The result of a successful contract instantiation. #[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] -pub struct InstantiateReturnValue { +pub struct InstantiateReturnValue { /// The output of the called constructor. pub result: ExecReturnValue, - /// The account id of the new contract. - pub account_id: AccountId, + /// The address of the new contract. + pub addr: H160, } /// The result of successfully uploading a contract. #[derive(Clone, PartialEq, Eq, Encode, Decode, MaxEncodedLen, RuntimeDebug, TypeInfo)] -pub struct CodeUploadReturnValue { +pub struct CodeUploadReturnValue { /// The key under which the new code is stored. - pub code_hash: CodeHash, + pub code_hash: sp_core::H256, /// The deposit that was reserved at the caller. Is zero when the code already existed. pub deposit: Balance, } /// Reference to an existing code hash or a new wasm module. #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] -pub enum Code { +pub enum Code { /// A wasm module as raw bytes. Upload(Vec), /// The code hash of an on-chain wasm blob. - Existing(Hash), + Existing(sp_core::H256), } /// The amount of balance that was either charged or refunded in order to pay for storage. diff --git a/substrate/frame/revive/src/storage.rs b/substrate/frame/revive/src/storage.rs index 87274ce407fa4508f0f205463fe60cafc5789750..db4db3e8eac3eda8b9c955d4f99e043aa9c8ff8d 100644 --- a/substrate/frame/revive/src/storage.rs +++ b/substrate/frame/revive/src/storage.rs @@ -20,12 +20,13 @@ pub mod meter; use crate::{ + address::AddressMapper, exec::{AccountIdOf, Key}, limits, storage::meter::Diff, weights::WeightInfo, - BalanceOf, CodeHash, CodeInfo, Config, ContractInfoOf, DeletionQueue, DeletionQueueCounter, - Error, TrieId, SENTINEL, + BalanceOf, CodeInfo, Config, ContractInfoOf, DeletionQueue, DeletionQueueCounter, Error, + StorageDeposit, TrieId, SENTINEL, }; use alloc::vec::Vec; use codec::{Decode, Encode, MaxEncodedLen}; @@ -35,8 +36,9 @@ use frame_support::{ weights::{Weight, WeightMeter}, CloneNoBound, DefaultNoBound, }; +use meter::DepositOf; use scale_info::TypeInfo; -use sp_core::{ConstU32, Get}; +use sp_core::{ConstU32, Get, H160}; use sp_io::KillStorageResult; use sp_runtime::{ traits::{Hash, Saturating, Zero}, @@ -44,7 +46,7 @@ use sp_runtime::{ }; type DelegateDependencyMap = - BoundedBTreeMap, BalanceOf, ConstU32<{ limits::DELEGATE_DEPENDENCIES }>>; + BoundedBTreeMap, ConstU32<{ limits::DELEGATE_DEPENDENCIES }>>; /// Information for managing an account and its sub trie abstraction. /// This is the required info to cache for an account. @@ -54,7 +56,7 @@ pub struct ContractInfo { /// Unique ID for the subtree encoded as a bytes vector. pub trie_id: TrieId, /// The code associated with a given account. - pub code_hash: CodeHash, + pub code_hash: sp_core::H256, /// How many bytes of storage are accumulated in this contract's child trie. storage_bytes: u32, /// How many items of storage are accumulated in this contract's child trie. @@ -74,6 +76,8 @@ pub struct ContractInfo { /// to the map can not be removed from the chain state and can be safely used for delegate /// calls. delegate_dependencies: DelegateDependencyMap, + /// The size of the immutable data of this contract. + immutable_data_len: u32, } impl ContractInfo { @@ -82,16 +86,16 @@ impl ContractInfo { /// This returns an `Err` if an contract with the supplied `account` already exists /// in storage. pub fn new( - account: &AccountIdOf, + address: &H160, nonce: T::Nonce, - code_hash: CodeHash, + code_hash: sp_core::H256, ) -> Result { - if >::contains_key(account) { - return Err(Error::::DuplicateContract.into()) + if >::contains_key(address) { + return Err(Error::::DuplicateContract.into()); } let trie_id = { - let buf = ("bcontract_trie_v1", account, nonce).using_encoded(T::Hashing::hash); + let buf = ("bcontract_trie_v1", address, nonce).using_encoded(T::Hashing::hash); buf.as_ref() .to_vec() .try_into() @@ -107,6 +111,7 @@ impl ContractInfo { storage_item_deposit: Zero::zero(), storage_base_deposit: Zero::zero(), delegate_dependencies: Default::default(), + immutable_data_len: 0, }; Ok(contract) @@ -259,7 +264,7 @@ impl ContractInfo { /// the delegate dependency already exists. pub fn lock_delegate_dependency( &mut self, - code_hash: CodeHash, + code_hash: sp_core::H256, amount: BalanceOf, ) -> DispatchResult { self.delegate_dependencies @@ -275,7 +280,7 @@ impl ContractInfo { /// Returns an error if the entry doesn't exist. pub fn unlock_delegate_dependency( &mut self, - code_hash: &CodeHash, + code_hash: &sp_core::H256, ) -> Result, DispatchError> { self.delegate_dependencies .remove(code_hash) @@ -352,8 +357,37 @@ impl ContractInfo { } /// Returns the code hash of the contract specified by `account` ID. - pub fn load_code_hash(account: &AccountIdOf) -> Option> { - >::get(account).map(|i| i.code_hash) + pub fn load_code_hash(account: &AccountIdOf) -> Option { + >::get(&T::AddressMapper::to_address(account)).map(|i| i.code_hash) + } + + /// Returns the amount of immutable bytes of this contract. + pub fn immutable_data_len(&self) -> u32 { + self.immutable_data_len + } + + /// Set the number of immutable bytes of this contract. + /// + /// On success, returns the storage deposit to be charged. + /// + /// Returns `Err(InvalidImmutableAccess)` if: + /// - The immutable bytes of this contract are not 0. This indicates that the immutable data + /// have already been set; it is only valid to set the immutable data exactly once. + /// - The provided `immutable_data_len` value was 0; it is invalid to set empty immutable data. + pub fn set_immutable_data_len( + &mut self, + immutable_data_len: u32, + ) -> Result, DispatchError> { + if self.immutable_data_len != 0 || immutable_data_len == 0 { + return Err(Error::::InvalidImmutableAccess.into()); + } + + self.immutable_data_len = immutable_data_len; + + let amount = T::DepositPerByte::get() + .saturating_mul(immutable_data_len.into()) + .saturating_add(T::DepositPerItem::get()); + Ok(StorageDeposit::Charge(amount)) } } diff --git a/substrate/frame/revive/src/storage/meter.rs b/substrate/frame/revive/src/storage/meter.rs index 8735aa8234213b49e8b6f93a9996ea06593d9728..712010bc8257454defa0ad81f466fbddc5822a9a 100644 --- a/substrate/frame/revive/src/storage/meter.rs +++ b/substrate/frame/revive/src/storage/meter.rs @@ -18,10 +18,9 @@ //! This module contains functions to meter the storage deposit. use crate::{ - storage::ContractInfo, AccountIdOf, BalanceOf, CodeInfo, Config, Error, Event, HoldReason, - Inspect, Origin, Pallet, StorageDeposit as Deposit, System, LOG_TARGET, + address::AddressMapper, storage::ContractInfo, AccountIdOf, BalanceOf, CodeInfo, Config, Error, + Event, HoldReason, Inspect, Origin, Pallet, StorageDeposit as Deposit, System, LOG_TARGET, }; - use alloc::vec::Vec; use core::{fmt::Debug, marker::PhantomData}; use frame_support::{ @@ -397,11 +396,7 @@ where } /// Functions that only apply to the nested state. -impl RawMeter -where - T: Config, - E: Ext, -{ +impl> RawMeter { /// Charges `diff` from the meter. pub fn charge(&mut self, diff: &Diff) { match &mut self.own_contribution { @@ -537,8 +532,8 @@ impl Ext for ReservingExt { )?; Pallet::::deposit_event(Event::StorageDepositTransferredAndHeld { - from: origin.clone(), - to: contract.clone(), + from: T::AddressMapper::to_address(origin), + to: T::AddressMapper::to_address(contract), amount: *amount, }); }, @@ -554,8 +549,8 @@ impl Ext for ReservingExt { )?; Pallet::::deposit_event(Event::StorageDepositTransferredAndReleased { - from: contract.clone(), - to: origin.clone(), + from: T::AddressMapper::to_address(contract), + to: T::AddressMapper::to_address(origin), amount: transferred, }); @@ -679,6 +674,7 @@ mod tests { items: u32, bytes_deposit: BalanceOf, items_deposit: BalanceOf, + immutable_data_len: u32, } fn new_info(info: StorageInfo) -> ContractInfo { @@ -691,6 +687,7 @@ mod tests { storage_item_deposit: info.items_deposit, storage_base_deposit: Default::default(), delegate_dependencies: Default::default(), + immutable_data_len: info.immutable_data_len, } } @@ -778,6 +775,7 @@ mod tests { items: 5, bytes_deposit: 100, items_deposit: 10, + immutable_data_len: 0, }); let mut nested0 = meter.nested(BalanceOf::::zero()); nested0.charge(&Diff { @@ -793,6 +791,7 @@ mod tests { items: 10, bytes_deposit: 100, items_deposit: 20, + immutable_data_len: 0, }); let mut nested1 = nested0.nested(BalanceOf::::zero()); nested1.charge(&Diff { items_removed: 5, ..Default::default() }); @@ -803,6 +802,7 @@ mod tests { items: 7, bytes_deposit: 100, items_deposit: 20, + immutable_data_len: 0, }); let mut nested2 = nested0.nested(BalanceOf::::zero()); nested2.charge(&Diff { items_removed: 7, ..Default::default() }); @@ -872,6 +872,7 @@ mod tests { items: 10, bytes_deposit: 100, items_deposit: 20, + immutable_data_len: 0, }); let mut nested1 = nested0.nested(BalanceOf::::zero()); nested1.charge(&Diff { items_removed: 5, ..Default::default() }); diff --git a/substrate/frame/revive/src/test_utils.rs b/substrate/frame/revive/src/test_utils.rs index 2bfe754f86cdeab81d30516e9877d567f149deb7..92c21297a3ece617175358ef3f95f6d34ba3bff2 100644 --- a/substrate/frame/revive/src/test_utils.rs +++ b/substrate/frame/revive/src/test_utils.rs @@ -24,12 +24,41 @@ pub mod builder; use crate::{BalanceOf, Config}; use frame_support::weights::Weight; +use sp_core::H160; pub use sp_runtime::AccountId32; +const fn ee_suffix(addr: H160) -> AccountId32 { + let mut id = [0u8; 32]; + let mut i = 0; + while i < 20 { + id[i] = addr.0[i]; + i += 1; + } + + let mut j = 20; + while j < 32 { + id[j] = 0xee; + j += 1; + } + + AccountId32::new(id) +} + pub const ALICE: AccountId32 = AccountId32::new([1u8; 32]); +pub const ALICE_ADDR: H160 = H160([1u8; 20]); +pub const ETH_ALICE: AccountId32 = ee_suffix(ALICE_ADDR); + pub const BOB: AccountId32 = AccountId32::new([2u8; 32]); +pub const BOB_ADDR: H160 = H160([2u8; 20]); +pub const BOB_CONTRACT_ID: AccountId32 = ee_suffix(BOB_ADDR); + pub const CHARLIE: AccountId32 = AccountId32::new([3u8; 32]); +pub const CHARLIE_ADDR: H160 = H160([3u8; 20]); +pub const CHARLIE_CONTRACT_ID: AccountId32 = ee_suffix(CHARLIE_ADDR); + pub const DJANGO: AccountId32 = AccountId32::new([4u8; 32]); +pub const DJANGO_ADDR: H160 = H160([4u8; 20]); +pub const ETH_DJANGO: AccountId32 = ee_suffix(DJANGO_ADDR); pub const GAS_LIMIT: Weight = Weight::from_parts(100_000_000_000, 3 * 1024 * 1024); diff --git a/substrate/frame/revive/src/test_utils/builder.rs b/substrate/frame/revive/src/test_utils/builder.rs index bf8cbcd5a01f3b4206d8a66429ad875ce419b7ac..d361590df95ae8d1b3aedaec391c4b5f772e266e 100644 --- a/substrate/frame/revive/src/test_utils/builder.rs +++ b/substrate/frame/revive/src/test_utils/builder.rs @@ -17,15 +17,13 @@ use super::{deposit_limit, GAS_LIMIT}; use crate::{ - AccountIdLookupOf, AccountIdOf, BalanceOf, Code, CodeHash, CollectEvents, Config, + address::AddressMapper, AccountIdOf, BalanceOf, Code, CollectEvents, Config, ContractExecResult, ContractInstantiateResult, DebugInfo, EventRecordOf, ExecReturnValue, InstantiateReturnValue, OriginFor, Pallet, Weight, }; -use codec::{Encode, HasCompact}; -use core::fmt::Debug; use frame_support::pallet_prelude::DispatchResultWithPostInfo; use paste::paste; -use scale_info::TypeInfo; +use sp_core::H160; /// Helper macro to generate a builder for contract API calls. macro_rules! builder { @@ -52,7 +50,9 @@ macro_rules! builder { #[allow(dead_code)] impl $name where - as HasCompact>::Type: Clone + Eq + PartialEq + Debug + TypeInfo + Encode, + BalanceOf: Into + TryFrom, + crate::MomentOf: Into, + T::Hash: frame_support::traits::IsType, { $( #[doc = concat!("Set the ", stringify!($field))] @@ -74,6 +74,11 @@ macro_rules! builder { } } +pub struct Contract { + pub account_id: AccountIdOf, + pub addr: H160, +} + builder!( instantiate_with_code( origin: OriginFor, @@ -82,7 +87,7 @@ builder!( storage_deposit_limit: BalanceOf, code: Vec, data: Vec, - salt: Vec, + salt: Option<[u8; 32]>, ) -> DispatchResultWithPostInfo; /// Create an [`InstantiateWithCodeBuilder`] with default values. @@ -94,7 +99,7 @@ builder!( storage_deposit_limit: deposit_limit::(), code, data: vec![], - salt: vec![], + salt: Some([0; 32]), } } ); @@ -105,13 +110,13 @@ builder!( value: BalanceOf, gas_limit: Weight, storage_deposit_limit: BalanceOf, - code_hash: CodeHash, + code_hash: sp_core::H256, data: Vec, - salt: Vec, + salt: Option<[u8; 32]>, ) -> DispatchResultWithPostInfo; /// Create an [`InstantiateBuilder`] with default values. - pub fn instantiate(origin: OriginFor, code_hash: CodeHash) -> Self { + pub fn instantiate(origin: OriginFor, code_hash: sp_core::H256) -> Self { Self { origin, value: 0u32.into(), @@ -119,7 +124,7 @@ builder!( storage_deposit_limit: deposit_limit::(), code_hash, data: vec![], - salt: vec![], + salt: Some([0; 32]), } } ); @@ -130,24 +135,27 @@ builder!( value: BalanceOf, gas_limit: Weight, storage_deposit_limit: BalanceOf, - code: Code>, + code: Code, data: Vec, - salt: Vec, + salt: Option<[u8; 32]>, debug: DebugInfo, collect_events: CollectEvents, - ) -> ContractInstantiateResult, BalanceOf, EventRecordOf>; + ) -> ContractInstantiateResult, EventRecordOf>; /// Build the instantiate call and unwrap the result. - pub fn build_and_unwrap_result(self) -> InstantiateReturnValue> { + pub fn build_and_unwrap_result(self) -> InstantiateReturnValue { self.build().result.unwrap() } /// Build the instantiate call and unwrap the account id. - pub fn build_and_unwrap_account_id(self) -> AccountIdOf { - self.build().result.unwrap().account_id + pub fn build_and_unwrap_contract(self) -> Contract { + let addr = self.build().result.unwrap().addr; + let account_id = T::AddressMapper::to_account_id(&addr); + Contract{ account_id, addr } } - pub fn bare_instantiate(origin: OriginFor, code: Code>) -> Self { + /// Create a [`BareInstantiateBuilder`] with default values. + pub fn bare_instantiate(origin: OriginFor, code: Code) -> Self { Self { origin, value: 0u32.into(), @@ -155,7 +163,7 @@ builder!( storage_deposit_limit: deposit_limit::(), code, data: vec![], - salt: vec![], + salt: Some([0; 32]), debug: DebugInfo::UnsafeDebug, collect_events: CollectEvents::Skip, } @@ -165,7 +173,7 @@ builder!( builder!( call( origin: OriginFor, - dest: AccountIdLookupOf, + dest: H160, value: BalanceOf, gas_limit: Weight, storage_deposit_limit: BalanceOf, @@ -173,7 +181,7 @@ builder!( ) -> DispatchResultWithPostInfo; /// Create a [`CallBuilder`] with default values. - pub fn call(origin: OriginFor, dest: AccountIdLookupOf) -> Self { + pub fn call(origin: OriginFor, dest: H160) -> Self { CallBuilder { origin, dest, @@ -188,7 +196,7 @@ builder!( builder!( bare_call( origin: OriginFor, - dest: AccountIdOf, + dest: H160, value: BalanceOf, gas_limit: Weight, storage_deposit_limit: BalanceOf, @@ -203,7 +211,7 @@ builder!( } /// Create a [`BareCallBuilder`] with default values. - pub fn bare_call(origin: OriginFor, dest: AccountIdOf) -> Self { + pub fn bare_call(origin: OriginFor, dest: H160) -> Self { Self { origin, dest, diff --git a/substrate/frame/revive/src/tests.rs b/substrate/frame/revive/src/tests.rs index 52ee7b310542f245aea786b5a5a3314e7aad21ed..4816e65f8f5c3951c91775fbd1726e764ea51ec1 100644 --- a/substrate/frame/revive/src/tests.rs +++ b/substrate/frame/revive/src/tests.rs @@ -26,29 +26,30 @@ use self::{ }; use crate::{ self as pallet_revive, + address::{create1, create2, AddressMapper}, chain_extension::{ ChainExtension, Environment, Ext, RegisteredChainExtension, Result as ExtensionResult, RetVal, ReturnFlags, }, exec::Key, limits, - migration::codegen::LATEST_MIGRATION_VERSION, primitives::CodeUploadReturnValue, storage::DeletionQueueManager, test_utils::*, tests::test_utils::{get_contract, get_contract_checked}, wasm::Memory, weights::WeightInfo, - BalanceOf, Code, CodeHash, CodeInfoOf, CollectEvents, Config, ContractInfo, ContractInfoOf, - DebugInfo, DefaultAddressGenerator, DeletionQueueCounter, Error, HoldReason, - MigrationInProgress, Origin, Pallet, PristineCode, + BalanceOf, Code, CodeInfoOf, CollectEvents, Config, ContractInfo, ContractInfoOf, DebugInfo, + DefaultAddressMapper, DeletionQueueCounter, Error, HoldReason, Origin, Pallet, PristineCode, + H160, }; + +use crate::test_utils::builder::Contract; use assert_matches::assert_matches; use codec::{Decode, Encode}; use frame_support::{ assert_err, assert_err_ignore_postinfo, assert_err_with_weight, assert_noop, assert_ok, derive_impl, - dispatch::{DispatchErrorWithPostInfo, PostDispatchInfo}, pallet_prelude::EnsureOrigin, parameter_types, storage::child, @@ -62,12 +63,11 @@ use frame_support::{ use frame_system::{EventRecord, Phase}; use pallet_revive_fixtures::{bench::dummy_unique, compile_module}; use pallet_revive_uapi::ReturnErrorCode as RuntimeReturnCode; -use sp_core::ByteArray; use sp_io::hashing::blake2_256; use sp_keystore::{testing::MemoryKeystore, KeystoreExt}; use sp_runtime::{ testing::H256, - traits::{BlakeTwo256, Convert, Hash, IdentityLookup}, + traits::{BlakeTwo256, Convert, IdentityLookup}, AccountId32, BuildStorage, DispatchError, Perbill, TokenError, }; @@ -102,15 +102,17 @@ macro_rules! assert_refcount { pub mod test_utils { use super::{Contracts, DepositPerByte, DepositPerItem, Test}; use crate::{ - exec::AccountIdOf, BalanceOf, CodeHash, CodeInfo, CodeInfoOf, Config, ContractInfo, - ContractInfoOf, PristineCode, + address::AddressMapper, exec::AccountIdOf, BalanceOf, CodeInfo, CodeInfoOf, Config, + ContractInfo, ContractInfoOf, PristineCode, }; use codec::{Encode, MaxEncodedLen}; use frame_support::traits::fungible::{InspectHold, Mutate}; + use sp_core::H160; - pub fn place_contract(address: &AccountIdOf, code_hash: CodeHash) { + pub fn place_contract(address: &AccountIdOf, code_hash: sp_core::H256) { set_balance(address, Contracts::min_balance() * 10); >::insert(code_hash, CodeInfo::new(address.clone())); + let address = ::AddressMapper::to_address(&address); let contract = >::new(&address, 0, code_hash).unwrap(); >::insert(address, contract); } @@ -126,23 +128,30 @@ pub mod test_utils { ) -> u64 { ::Currency::balance_on_hold(reason.into(), who) } - pub fn get_contract(addr: &AccountIdOf) -> ContractInfo { + pub fn get_contract(addr: &H160) -> ContractInfo { get_contract_checked(addr).unwrap() } - pub fn get_contract_checked(addr: &AccountIdOf) -> Option> { + pub fn get_contract_checked(addr: &H160) -> Option> { ContractInfoOf::::get(addr) } - pub fn get_code_deposit(code_hash: &CodeHash) -> BalanceOf { + pub fn get_code_deposit(code_hash: &sp_core::H256) -> BalanceOf { crate::CodeInfoOf::::get(code_hash).unwrap().deposit() } - pub fn contract_info_storage_deposit( - addr: &::AccountId, - ) -> BalanceOf { + pub fn contract_info_storage_deposit(addr: &H160) -> BalanceOf { let contract_info = self::get_contract(&addr); let info_size = contract_info.encoded_size() as u64; - DepositPerByte::get() + let info_deposit = DepositPerByte::get() .saturating_mul(info_size) - .saturating_add(DepositPerItem::get()) + .saturating_add(DepositPerItem::get()); + let immutable_size = contract_info.immutable_data_len() as u64; + if immutable_size > 0 { + let immutable_deposit = DepositPerByte::get() + .saturating_mul(immutable_size) + .saturating_add(DepositPerItem::get()); + info_deposit.saturating_add(immutable_deposit) + } else { + info_deposit + } } pub fn expected_deposit(code_len: usize) -> u64 { // For code_info, the deposit for max_encoded_len is taken. @@ -152,27 +161,34 @@ pub mod test_utils { DepositPerByte::get().saturating_mul(code_len as u64 + code_info_len) + DepositPerItem::get().saturating_mul(2) } - pub fn ensure_stored(code_hash: CodeHash) -> usize { + pub fn ensure_stored(code_hash: sp_core::H256) -> usize { // Assert that code_info is stored assert!(CodeInfoOf::::contains_key(&code_hash)); // Assert that contract code is stored, and get its size. PristineCode::::try_get(&code_hash).unwrap().len() } + pub fn u256_bytes(u: u64) -> [u8; 32] { + let mut buffer = [0u8; 32]; + let bytes = u.to_le_bytes(); + buffer[..8].copy_from_slice(&bytes); + buffer + } } mod builder { use super::Test; use crate::{ - test_utils::{builder::*, AccountId32, ALICE}, + test_utils::{builder::*, ALICE}, tests::RuntimeOrigin, - AccountIdLookupOf, Code, CodeHash, + Code, }; + use sp_core::{H160, H256}; - pub fn bare_instantiate(code: Code>) -> BareInstantiateBuilder { + pub fn bare_instantiate(code: Code) -> BareInstantiateBuilder { BareInstantiateBuilder::::bare_instantiate(RuntimeOrigin::signed(ALICE), code) } - pub fn bare_call(dest: AccountId32) -> BareCallBuilder { + pub fn bare_call(dest: H160) -> BareCallBuilder { BareCallBuilder::::bare_call(RuntimeOrigin::signed(ALICE), dest) } @@ -183,11 +199,11 @@ mod builder { ) } - pub fn instantiate(code_hash: CodeHash) -> InstantiateBuilder { + pub fn instantiate(code_hash: H256) -> InstantiateBuilder { InstantiateBuilder::::instantiate(RuntimeOrigin::signed(ALICE), code_hash) } - pub fn call(dest: AccountIdLookupOf) -> CallBuilder { + pub fn call(dest: H160) -> CallBuilder { CallBuilder::::call(RuntimeOrigin::signed(ALICE), dest) } } @@ -405,6 +421,7 @@ parameter_types! { pub static DepositPerByte: BalanceOf = 1; pub const DepositPerItem: BalanceOf = 2; pub static CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(0); + pub static ChainId: u64 = 384; } impl Convert> for Test { @@ -483,19 +500,19 @@ impl Config for Test { (TestExtension, DisabledExtension, RevertingExtension, TempStorageExtension); type DepositPerByte = DepositPerByte; type DepositPerItem = DepositPerItem; - type AddressGenerator = DefaultAddressGenerator; + type AddressMapper = DefaultAddressMapper; type UnsafeUnstableInterface = UnstableInterface; type UploadOrigin = EnsureAccount; type InstantiateOrigin = EnsureAccount; - type Migrations = crate::migration::codegen::BenchMigrations; type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; type Debug = TestDebug; + type ChainId = ChainId; } pub struct ExtBuilder { existential_deposit: u64, storage_version: Option, - code_hashes: Vec>, + code_hashes: Vec, } impl Default for ExtBuilder { @@ -513,17 +530,13 @@ impl ExtBuilder { self.existential_deposit = existential_deposit; self } - pub fn with_code_hashes(mut self, code_hashes: Vec>) -> Self { + pub fn with_code_hashes(mut self, code_hashes: Vec) -> Self { self.code_hashes = code_hashes; self } pub fn set_associated_consts(&self) { EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); } - pub fn set_storage_version(mut self, version: u16) -> Self { - self.storage_version = Some(StorageVersion::new(version)); - self - } pub fn build(self) -> sp_io::TestExternalities { sp_tracing::try_init_simple(); self.set_associated_consts(); @@ -590,92 +603,23 @@ impl Default for Origin { mod run_tests { use super::*; use pretty_assertions::{assert_eq, assert_ne}; + use sp_core::U256; - // Perform a call to a plain account. - // The actual transfer fails because we can only call contracts. - // Then we check that at least the base costs where charged (no runtime gas costs.) #[test] - fn calling_plain_account_fails() { + fn calling_plain_account_is_balance_transfer() { ExtBuilder::default().build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 100_000_000); - let base_cost = <::WeightInfo as WeightInfo>::call(); - - assert_eq!( - builder::call(BOB).build(), - Err(DispatchErrorWithPostInfo { - error: Error::::ContractNotFound.into(), - post_info: PostDispatchInfo { - actual_weight: Some(base_cost), - pays_fee: Default::default(), - }, - }) - ); - }); - } - - #[test] - fn migration_on_idle_hooks_works() { - // Defines expectations of how many migration steps can be done given the weight limit. - let tests = [ - (Weight::zero(), LATEST_MIGRATION_VERSION - 2), - (::WeightInfo::migrate() + 1.into(), LATEST_MIGRATION_VERSION - 1), - (Weight::MAX, LATEST_MIGRATION_VERSION), - ]; - - for (weight, expected_version) in tests { - ExtBuilder::default() - .set_storage_version(LATEST_MIGRATION_VERSION - 2) - .build() - .execute_with(|| { - MigrationInProgress::::set(Some(Default::default())); - Contracts::on_idle(System::block_number(), weight); - assert_eq!(StorageVersion::get::>(), expected_version); - }); - } - } - - #[test] - fn migration_in_progress_works() { - let (wasm, code_hash) = compile_module::("dummy").unwrap(); - - ExtBuilder::default().existential_deposit(1).build().execute_with(|| { - let _ = ::Currency::set_balance(&ALICE, 1_000_000); - MigrationInProgress::::set(Some(Default::default())); - - assert_err!( - Contracts::upload_code( - RuntimeOrigin::signed(ALICE), - vec![], - deposit_limit::(), - ), - Error::::MigrationInProgress, - ); - assert_err!( - Contracts::remove_code(RuntimeOrigin::signed(ALICE), code_hash), - Error::::MigrationInProgress, - ); - assert_err!( - Contracts::set_code(RuntimeOrigin::signed(ALICE), BOB.clone(), code_hash), - Error::::MigrationInProgress, - ); - assert_err_ignore_postinfo!( - builder::call(BOB).build(), - Error::::MigrationInProgress - ); - assert_err_ignore_postinfo!( - builder::instantiate_with_code(wasm).value(100_000).build(), - Error::::MigrationInProgress, - ); - assert_err_ignore_postinfo!( - builder::instantiate(code_hash).value(100_000).build(), - Error::::MigrationInProgress, - ); + assert!(!>::contains_key(BOB_ADDR)); + assert_eq!(test_utils::get_balance(&BOB_CONTRACT_ID), 0); + let result = builder::bare_call(BOB_ADDR).value(42).build_and_unwrap_result(); + assert_eq!(test_utils::get_balance(&BOB_CONTRACT_ID), 42); + assert_eq!(result, Default::default()); }); } #[test] fn instantiate_and_call_and_deposit_event() { - let (wasm, code_hash) = compile_module::("event_and_return_on_deploy").unwrap(); + let (wasm, code_hash) = compile_module("event_and_return_on_deploy").unwrap(); ExtBuilder::default().existential_deposit(1).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); @@ -694,9 +638,10 @@ mod run_tests { initialize_block(2); // Check at the end to get hash on error easily - let addr = builder::bare_instantiate(Code::Existing(code_hash)) - .value(value) - .build_and_unwrap_account_id(); + let Contract { addr, account_id } = + builder::bare_instantiate(Code::Existing(code_hash)) + .value(value) + .build_and_unwrap_contract(); assert!(ContractInfoOf::::contains_key(&addr)); assert_eq!( @@ -705,14 +650,14 @@ mod run_tests { EventRecord { phase: Phase::Initialization, event: RuntimeEvent::System(frame_system::Event::NewAccount { - account: addr.clone() + account: account_id.clone() }), topics: vec![], }, EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Endowed { - account: addr.clone(), + account: account_id.clone(), free_balance: min_balance, }), topics: vec![], @@ -721,7 +666,7 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { from: ALICE, - to: addr.clone(), + to: account_id.clone(), amount: min_balance, }), topics: vec![], @@ -730,7 +675,7 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { from: ALICE, - to: addr.clone(), + to: account_id.clone(), amount: value, }), topics: vec![], @@ -738,16 +683,17 @@ mod run_tests { EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::ContractEmitted { - contract: addr.clone(), - data: vec![1, 2, 3, 4] + contract: addr, + data: vec![1, 2, 3, 4], + topics: vec![H256::repeat_byte(42)], }), topics: vec![], }, EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Instantiated { - deployer: ALICE, - contract: addr.clone() + deployer: ALICE_ADDR, + contract: addr }), topics: vec![], }, @@ -755,8 +701,8 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Contracts( pallet_revive::Event::StorageDepositTransferredAndHeld { - from: ALICE, - to: addr.clone(), + from: ALICE_ADDR, + to: addr, amount: test_utils::contract_info_storage_deposit(&addr), } ), @@ -767,19 +713,60 @@ mod run_tests { }); } + #[test] + fn create1_address_from_extrinsic() { + let (wasm, code_hash) = compile_module("dummy").unwrap(); + + ExtBuilder::default().existential_deposit(1).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + assert_ok!(Contracts::upload_code( + RuntimeOrigin::signed(ALICE), + wasm.clone(), + deposit_limit::(), + )); + + assert_eq!(System::account_nonce(&ALICE), 0); + + for nonce in 0..3 { + let Contract { addr, .. } = builder::bare_instantiate(Code::Existing(code_hash)) + .salt(None) + .build_and_unwrap_contract(); + assert!(ContractInfoOf::::contains_key(&addr)); + assert_eq!( + addr, + create1(&::AddressMapper::to_address(&ALICE), nonce) + ); + } + assert_eq!(System::account_nonce(&ALICE), 3); + + for nonce in 3..6 { + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm.clone())) + .salt(None) + .build_and_unwrap_contract(); + assert!(ContractInfoOf::::contains_key(&addr)); + assert_eq!( + addr, + create1(&::AddressMapper::to_address(&ALICE), nonce) + ); + } + assert_eq!(System::account_nonce(&ALICE), 6); + }); + } + #[test] fn deposit_event_max_value_limit() { - let (wasm, _code_hash) = compile_module::("event_size").unwrap(); + let (wasm, _code_hash) = compile_module("event_size").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create let _ = ::Currency::set_balance(&ALICE, 1_000_000); - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) .value(30_000) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); // Call contract with allowed storage value. - assert_ok!(builder::call(addr.clone()) + assert_ok!(builder::call(addr) .gas_limit(GAS_LIMIT.set_ref_time(GAS_LIMIT.ref_time() * 2)) // we are copying a huge buffer, .data(limits::PAYLOAD_BYTES.encode()) .build()); @@ -795,14 +782,14 @@ mod run_tests { // Fail out of fuel (ref_time weight) in the engine. #[test] fn run_out_of_fuel_engine() { - let (wasm, _code_hash) = compile_module::("run_out_of_gas").unwrap(); + let (wasm, _code_hash) = compile_module("run_out_of_gas").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1_000_000); - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) .value(100 * min_balance) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); // Call the contract with a fixed gas limit. It must run out of gas because it just // loops forever. @@ -818,19 +805,19 @@ mod run_tests { // Fail out of fuel (ref_time weight) in the host. #[test] fn run_out_of_fuel_host() { - let (code, _hash) = compile_module::("chain_extension").unwrap(); + let (code, _hash) = compile_module("chain_extension").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); - let addr = builder::bare_instantiate(Code::Upload(code)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code)) .value(min_balance * 100) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); let gas_limit = Weight::from_parts(u32::MAX as u64, GAS_LIMIT.proof_size()); // Use chain extension to charge more ref_time than it is available. - let result = builder::bare_call(addr.clone()) + let result = builder::bare_call(addr) .gas_limit(gas_limit) .data( ExtensionInput { extension_id: 0, func_id: 2, extra: &u32::MAX.encode() } @@ -844,16 +831,17 @@ mod run_tests { #[test] fn gas_syncs_work() { - let (code, _code_hash) = compile_module::("caller_is_origin_n").unwrap(); + let (code, _code_hash) = compile_module("caller_is_origin_n").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); - let addr = builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_account_id(); + let contract = + builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract(); - let result = builder::bare_call(addr.clone()).data(0u32.encode()).build(); + let result = builder::bare_call(contract.addr).data(0u32.encode()).build(); assert_ok!(result.result); let engine_consumed_noop = result.gas_consumed.ref_time(); - let result = builder::bare_call(addr.clone()).data(1u32.encode()).build(); + let result = builder::bare_call(contract.addr).data(1u32.encode()).build(); assert_ok!(result.result); let gas_consumed_once = result.gas_consumed.ref_time(); let host_consumed_once = @@ -861,7 +849,7 @@ mod run_tests { let engine_consumed_once = gas_consumed_once - host_consumed_once - engine_consumed_noop; - let result = builder::bare_call(addr).data(2u32.encode()).build(); + let result = builder::bare_call(contract.addr).data(2u32.encode()).build(); assert_ok!(result.result); let gas_consumed_twice = result.gas_consumed.ref_time(); let host_consumed_twice = host_consumed_once * 2; @@ -878,7 +866,7 @@ mod run_tests { /// Check the `Nonce` storage item for more information. #[test] fn instantiate_unique_trie_id() { - let (wasm, code_hash) = compile_module::("self_destruct").unwrap(); + let (wasm, code_hash) = compile_module("self_destruct").unwrap(); ExtBuilder::default().existential_deposit(500).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); @@ -886,8 +874,8 @@ mod run_tests { .unwrap(); // Instantiate the contract and store its trie id for later comparison. - let addr = - builder::bare_instantiate(Code::Existing(code_hash)).build_and_unwrap_account_id(); + let Contract { addr, .. } = + builder::bare_instantiate(Code::Existing(code_hash)).build_and_unwrap_contract(); let trie_id = get_contract(&addr).trie_id; // Try to instantiate it again without termination should yield an error. @@ -897,7 +885,7 @@ mod run_tests { ); // Terminate the contract. - assert_ok!(builder::call(addr.clone()).build()); + assert_ok!(builder::call(addr).build()); // Re-Instantiate after termination. assert_ok!(builder::instantiate(code_hash).build()); @@ -909,14 +897,14 @@ mod run_tests { #[test] fn storage_work() { - let (code, _code_hash) = compile_module::("storage").unwrap(); + let (code, _code_hash) = compile_module("storage").unwrap(); ExtBuilder::default().build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); let min_balance = Contracts::min_balance(); - let addr = builder::bare_instantiate(Code::Upload(code)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code)) .value(min_balance * 100) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); builder::bare_call(addr).build_and_unwrap_result(); }); @@ -924,18 +912,18 @@ mod run_tests { #[test] fn storage_max_value_limit() { - let (wasm, _code_hash) = compile_module::("storage_size").unwrap(); + let (wasm, _code_hash) = compile_module("storage_size").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create let _ = ::Currency::set_balance(&ALICE, 1_000_000); - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) .value(30_000) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); get_contract(&addr); // Call contract with allowed storage value. - assert_ok!(builder::call(addr.clone()) + assert_ok!(builder::call(addr) .gas_limit(GAS_LIMIT.set_ref_time(GAS_LIMIT.ref_time() * 2)) // we are copying a huge buffer .data(limits::PAYLOAD_BYTES.encode()) .build()); @@ -950,14 +938,14 @@ mod run_tests { #[test] fn transient_storage_work() { - let (code, _code_hash) = compile_module::("transient_storage").unwrap(); + let (code, _code_hash) = compile_module("transient_storage").unwrap(); ExtBuilder::default().build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); let min_balance = Contracts::min_balance(); - let addr = builder::bare_instantiate(Code::Upload(code)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code)) .value(min_balance * 100) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); builder::bare_call(addr).build_and_unwrap_result(); }); @@ -966,28 +954,27 @@ mod run_tests { #[test] fn transient_storage_limit_in_call() { let (wasm_caller, _code_hash_caller) = - compile_module::("create_transient_storage_and_call").unwrap(); - let (wasm_callee, _code_hash_callee) = - compile_module::("set_transient_storage").unwrap(); + compile_module("create_transient_storage_and_call").unwrap(); + let (wasm_callee, _code_hash_callee) = compile_module("set_transient_storage").unwrap(); ExtBuilder::default().build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Create both contracts: Constructors do nothing. - let addr_caller = - builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_account_id(); - let addr_callee = - builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_account_id(); + let Contract { addr: addr_caller, .. } = + builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_contract(); + let Contract { addr: addr_callee, .. } = + builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_contract(); // Call contracts with storage values within the limit. // Caller and Callee contracts each set a transient storage value of size 100. - assert_ok!(builder::call(addr_caller.clone()) + assert_ok!(builder::call(addr_caller) .data((100u32, 100u32, &addr_callee).encode()) .build(),); // Call a contract with a storage value that is too large. // Limit exceeded in the caller contract. assert_err_ignore_postinfo!( - builder::call(addr_caller.clone()) + builder::call(addr_caller) .data((4u32 * 1024u32, 200u32, &addr_callee).encode()) .build(), >::OutOfTransientStorage, @@ -1006,17 +993,27 @@ mod run_tests { #[test] fn deploy_and_call_other_contract() { - let (caller_wasm, _caller_code_hash) = compile_module::("caller_contract").unwrap(); - let (callee_wasm, callee_code_hash) = compile_module::("return_with_data").unwrap(); + let (caller_wasm, _caller_code_hash) = compile_module("caller_contract").unwrap(); + let (callee_wasm, callee_code_hash) = compile_module("return_with_data").unwrap(); ExtBuilder::default().existential_deposit(1).build().execute_with(|| { let min_balance = Contracts::min_balance(); // Create let _ = ::Currency::set_balance(&ALICE, 1_000_000); - let caller_addr = builder::bare_instantiate(Code::Upload(caller_wasm)) - .value(100_000) - .build_and_unwrap_account_id(); + let Contract { addr: caller_addr, account_id: caller_account } = + builder::bare_instantiate(Code::Upload(caller_wasm)) + .value(100_000) + .build_and_unwrap_contract(); + + let callee_addr = create2( + &caller_addr, + &callee_wasm, + &[0, 1, 34, 51, 68, 85, 102, 119], // hard coded in wasm + &[0u8; 32], + ); + let callee_account = ::AddressMapper::to_account_id(&callee_addr); + Contracts::upload_code( RuntimeOrigin::signed(ALICE), callee_wasm, @@ -1024,21 +1021,12 @@ mod run_tests { ) .unwrap(); - let callee_addr = Contracts::contract_address( - &caller_addr, - &callee_code_hash, - &[0, 1, 34, 51, 68, 85, 102, 119], // hard coded in wasm - &[], - ); - // Drop previous events initialize_block(2); // Call BOB contract, which attempts to instantiate and call the callee contract and // makes various assertions on the results from those calls. - assert_ok!(builder::call(caller_addr.clone()) - .data(callee_code_hash.as_ref().to_vec()) - .build()); + assert_ok!(builder::call(caller_addr).data(callee_code_hash.as_ref().to_vec()).build()); assert_eq!( System::events(), @@ -1046,14 +1034,14 @@ mod run_tests { EventRecord { phase: Phase::Initialization, event: RuntimeEvent::System(frame_system::Event::NewAccount { - account: callee_addr.clone() + account: callee_account.clone() }), topics: vec![], }, EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Endowed { - account: callee_addr.clone(), + account: callee_account.clone(), free_balance: min_balance, }), topics: vec![], @@ -1062,7 +1050,7 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { from: ALICE, - to: callee_addr.clone(), + to: callee_account.clone(), amount: min_balance, }), topics: vec![], @@ -1070,8 +1058,8 @@ mod run_tests { EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { - from: caller_addr.clone(), - to: callee_addr.clone(), + from: caller_account.clone(), + to: callee_account.clone(), amount: 32768 // hardcoded in wasm }), topics: vec![], @@ -1079,16 +1067,16 @@ mod run_tests { EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Instantiated { - deployer: caller_addr.clone(), - contract: callee_addr.clone(), + deployer: caller_addr, + contract: callee_addr, }), topics: vec![], }, EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { - from: caller_addr.clone(), - to: callee_addr.clone(), + from: caller_account.clone(), + to: callee_account.clone(), amount: 32768, }), topics: vec![], @@ -1096,8 +1084,8 @@ mod run_tests { EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Called { - caller: Origin::from_account_id(caller_addr.clone()), - contract: callee_addr.clone(), + caller: Origin::from_account_id(caller_account.clone()), + contract: callee_addr, }), topics: vec![], }, @@ -1105,7 +1093,7 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Called { caller: Origin::from_account_id(ALICE), - contract: caller_addr.clone(), + contract: caller_addr, }), topics: vec![], }, @@ -1113,8 +1101,8 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Contracts( pallet_revive::Event::StorageDepositTransferredAndHeld { - from: ALICE, - to: callee_addr.clone(), + from: ALICE_ADDR, + to: callee_addr, amount: test_utils::contract_info_storage_deposit(&callee_addr), } ), @@ -1127,20 +1115,21 @@ mod run_tests { #[test] fn delegate_call() { - let (caller_wasm, _caller_code_hash) = compile_module::("delegate_call").unwrap(); - let (callee_wasm, callee_code_hash) = compile_module::("delegate_call_lib").unwrap(); + let (caller_wasm, _caller_code_hash) = compile_module("delegate_call").unwrap(); + let (callee_wasm, callee_code_hash) = compile_module("delegate_call_lib").unwrap(); ExtBuilder::default().existential_deposit(500).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Instantiate the 'caller' - let caller_addr = builder::bare_instantiate(Code::Upload(caller_wasm)) - .value(300_000) - .build_and_unwrap_account_id(); + let Contract { addr: caller_addr, .. } = + builder::bare_instantiate(Code::Upload(caller_wasm)) + .value(300_000) + .build_and_unwrap_contract(); // Only upload 'callee' code assert_ok!(Contracts::upload_code(RuntimeOrigin::signed(ALICE), callee_wasm, 100_000,)); - assert_ok!(builder::call(caller_addr.clone()) + assert_ok!(builder::call(caller_addr) .value(1337) .data(callee_code_hash.as_ref().to_vec()) .build()); @@ -1149,29 +1138,33 @@ mod run_tests { #[test] fn transfer_expendable_cannot_kill_account() { - let (wasm, _code_hash) = compile_module::("dummy").unwrap(); + let (wasm, _code_hash) = compile_module("dummy").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Instantiate the BOB contract. - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) .value(1_000) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); // Check that the BOB contract has been instantiated. get_contract(&addr); - let total_balance = ::Currency::total_balance(&addr); + let account = ::AddressMapper::to_account_id(&addr); + let total_balance = ::Currency::total_balance(&account); assert_eq!( - test_utils::get_balance_on_hold(&HoldReason::StorageDepositReserve.into(), &addr), + test_utils::get_balance_on_hold( + &HoldReason::StorageDepositReserve.into(), + &account + ), test_utils::contract_info_storage_deposit(&addr) ); // Some ot the total balance is held, so it can't be transferred. assert_err!( <::Currency as Mutate>::transfer( - &addr, + &account, &ALICE, total_balance, Preservation::Expendable, @@ -1179,33 +1172,34 @@ mod run_tests { TokenError::FundsUnavailable, ); - assert_eq!(::Currency::total_balance(&addr), total_balance); + assert_eq!(::Currency::total_balance(&account), total_balance); }); } #[test] fn cannot_self_destruct_through_draining() { - let (wasm, _code_hash) = compile_module::("drain").unwrap(); + let (wasm, _code_hash) = compile_module("drain").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); let value = 1_000; let min_balance = Contracts::min_balance(); // Instantiate the BOB contract. - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) .value(value) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); + let account = ::AddressMapper::to_account_id(&addr); // Check that the BOB contract has been instantiated. get_contract(&addr); // Call BOB which makes it send all funds to the zero address // The contract code asserts that the transfer fails with the correct error code - assert_ok!(builder::call(addr.clone()).build()); + assert_ok!(builder::call(addr).build()); // Make sure the account wasn't remove by sending all free balance away. assert_eq!( - ::Currency::total_balance(&addr), + ::Currency::total_balance(&account), value + test_utils::contract_info_storage_deposit(&addr) + min_balance, ); }); @@ -1213,54 +1207,54 @@ mod run_tests { #[test] fn cannot_self_destruct_through_storage_refund_after_price_change() { - let (wasm, _code_hash) = compile_module::("store_call").unwrap(); + let (wasm, _code_hash) = compile_module("store_call").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); let min_balance = Contracts::min_balance(); // Instantiate the BOB contract. - let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id(); - - let info_deposit = test_utils::contract_info_storage_deposit(&addr); + let contract = + builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract(); + let info_deposit = test_utils::contract_info_storage_deposit(&contract.addr); // Check that the contract has been instantiated and has the minimum balance - assert_eq!(get_contract(&addr).total_deposit(), info_deposit); - assert_eq!(get_contract(&addr).extra_deposit(), 0); + assert_eq!(get_contract(&contract.addr).total_deposit(), info_deposit); + assert_eq!(get_contract(&contract.addr).extra_deposit(), 0); assert_eq!( - ::Currency::total_balance(&addr), + ::Currency::total_balance(&contract.account_id), info_deposit + min_balance ); // Create 100 bytes of storage with a price of per byte and a single storage item of // price 2 - assert_ok!(builder::call(addr.clone()).data(100u32.to_le_bytes().to_vec()).build()); - assert_eq!(get_contract(&addr).total_deposit(), info_deposit + 102); + assert_ok!(builder::call(contract.addr).data(100u32.to_le_bytes().to_vec()).build()); + assert_eq!(get_contract(&contract.addr).total_deposit(), info_deposit + 102); // Increase the byte price and trigger a refund. This should not have any influence // because the removal is pro rata and exactly those 100 bytes should have been // removed. DEPOSIT_PER_BYTE.with(|c| *c.borrow_mut() = 500); - assert_ok!(builder::call(addr.clone()).data(0u32.to_le_bytes().to_vec()).build()); + assert_ok!(builder::call(contract.addr).data(0u32.to_le_bytes().to_vec()).build()); // Make sure the account wasn't removed by the refund assert_eq!( - ::Currency::total_balance(&addr), - get_contract(&addr).total_deposit() + min_balance, + ::Currency::total_balance(&contract.account_id), + get_contract(&contract.addr).total_deposit() + min_balance, ); - assert_eq!(get_contract(&addr).extra_deposit(), 2); + assert_eq!(get_contract(&contract.addr).extra_deposit(), 2); }); } #[test] fn cannot_self_destruct_while_live() { - let (wasm, _code_hash) = compile_module::("self_destruct").unwrap(); + let (wasm, _code_hash) = compile_module("self_destruct").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Instantiate the BOB contract. - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) .value(100_000) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); // Check that the BOB contract has been instantiated. get_contract(&addr); @@ -1268,7 +1262,7 @@ mod run_tests { // Call BOB with input data, forcing it make a recursive call to itself to // self-destruct, resulting in a trap. assert_err_ignore_postinfo!( - builder::call(addr.clone()).data(vec![0]).build(), + builder::call(addr).data(vec![0]).build(), Error::::ContractTrapped, ); @@ -1279,38 +1273,38 @@ mod run_tests { #[test] fn self_destruct_works() { - let (wasm, code_hash) = compile_module::("self_destruct").unwrap(); + let (wasm, code_hash) = compile_module("self_destruct").unwrap(); ExtBuilder::default().existential_deposit(1_000).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); - let _ = ::Currency::set_balance(&DJANGO, 1_000_000); + let _ = ::Currency::set_balance(Ð_DJANGO, 1_000_000); let min_balance = Contracts::min_balance(); // Instantiate the BOB contract. - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let contract = builder::bare_instantiate(Code::Upload(wasm)) .value(100_000) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); // Check that the BOB contract has been instantiated. - let _ = get_contract(&addr); + let _ = get_contract(&contract.addr); - let info_deposit = test_utils::contract_info_storage_deposit(&addr); + let info_deposit = test_utils::contract_info_storage_deposit(&contract.addr); // Drop all previous events initialize_block(2); // Call BOB without input data which triggers termination. - assert_matches!(builder::call(addr.clone()).build(), Ok(_)); + assert_matches!(builder::call(contract.addr).build(), Ok(_)); // Check that code is still there but refcount dropped to zero. assert_refcount!(&code_hash, 0); // Check that account is gone - assert!(get_contract_checked(&addr).is_none()); - assert_eq!(::Currency::total_balance(&addr), 0); + assert!(get_contract_checked(&contract.addr).is_none()); + assert_eq!(::Currency::total_balance(&contract.account_id), 0); // Check that the beneficiary (django) got remaining balance. assert_eq!( - ::Currency::free_balance(DJANGO), + ::Currency::free_balance(ETH_DJANGO), 1_000_000 + 100_000 + min_balance ); @@ -1327,8 +1321,8 @@ mod run_tests { EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Terminated { - contract: addr.clone(), - beneficiary: DJANGO + contract: contract.addr, + beneficiary: DJANGO_ADDR, }), topics: vec![], }, @@ -1336,7 +1330,7 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Called { caller: Origin::from_account_id(ALICE), - contract: addr.clone(), + contract: contract.addr, }), topics: vec![], }, @@ -1344,8 +1338,8 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Contracts( pallet_revive::Event::StorageDepositTransferredAndReleased { - from: addr.clone(), - to: ALICE, + from: contract.addr, + to: ALICE_ADDR, amount: info_deposit, } ), @@ -1354,15 +1348,15 @@ mod run_tests { EventRecord { phase: Phase::Initialization, event: RuntimeEvent::System(frame_system::Event::KilledAccount { - account: addr.clone() + account: contract.account_id.clone() }), topics: vec![], }, EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { - from: addr.clone(), - to: DJANGO, + from: contract.account_id.clone(), + to: ETH_DJANGO, amount: 100_000 + min_balance, }), topics: vec![], @@ -1376,30 +1370,30 @@ mod run_tests { // additional funds after it has been drained. #[test] fn destroy_contract_and_transfer_funds() { - let (callee_wasm, callee_code_hash) = compile_module::("self_destruct").unwrap(); - let (caller_wasm, _caller_code_hash) = - compile_module::("destroy_and_transfer").unwrap(); + let (callee_wasm, callee_code_hash) = compile_module("self_destruct").unwrap(); + let (caller_wasm, _caller_code_hash) = compile_module("destroy_and_transfer").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create code hash for bob to instantiate let _ = ::Currency::set_balance(&ALICE, 1_000_000); Contracts::upload_code( RuntimeOrigin::signed(ALICE), - callee_wasm, + callee_wasm.clone(), deposit_limit::(), ) .unwrap(); // This deploys the BOB contract, which in turn deploys the CHARLIE contract during // construction. - let addr_bob = builder::bare_instantiate(Code::Upload(caller_wasm)) - .value(200_000) - .data(callee_code_hash.as_ref().to_vec()) - .build_and_unwrap_account_id(); + let Contract { addr: addr_bob, .. } = + builder::bare_instantiate(Code::Upload(caller_wasm)) + .value(200_000) + .data(callee_code_hash.as_ref().to_vec()) + .build_and_unwrap_contract(); // Check that the CHARLIE contract has been instantiated. - let addr_charlie = - Contracts::contract_address(&addr_bob, &callee_code_hash, &[], &[0x47, 0x11]); + let salt = [47; 32]; // hard coded in fixture. + let addr_charlie = create2(&addr_bob, &callee_wasm, &[], &salt); get_contract(&addr_charlie); // Call BOB, which calls CHARLIE, forcing CHARLIE to self-destruct. @@ -1412,7 +1406,7 @@ mod run_tests { #[test] fn cannot_self_destruct_in_constructor() { - let (wasm, _) = compile_module::("self_destructing_constructor").unwrap(); + let (wasm, _) = compile_module("self_destructing_constructor").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); @@ -1426,15 +1420,15 @@ mod run_tests { #[test] fn crypto_hashes() { - let (wasm, _code_hash) = compile_module::("crypto_hashes").unwrap(); + let (wasm, _code_hash) = compile_module("crypto_hashes").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Instantiate the CRYPTO_HASHES contract. - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) .value(100_000) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); // Perform the call. let input = b"_DEAD_BEEF"; use sp_io::hashing::*; @@ -1456,8 +1450,7 @@ mod run_tests { // We offset data in the contract tables by 1. let mut params = vec![(n + 1) as u8]; params.extend_from_slice(input); - let result = - builder::bare_call(addr.clone()).data(params).build_and_unwrap_result(); + let result = builder::bare_call(addr).data(params).build_and_unwrap_result(); assert!(!result.did_revert()); let expected = hash_fn(input.as_ref()); assert_eq!(&result.data[..*expected_size], &*expected); @@ -1467,55 +1460,89 @@ mod run_tests { #[test] fn transfer_return_code() { - let (wasm, _code_hash) = compile_module::("transfer_return_code").unwrap(); + let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let contract = builder::bare_instantiate(Code::Upload(wasm)) .value(min_balance * 100) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); // Contract has only the minimal balance so any transfer will fail. - ::Currency::set_balance(&addr, min_balance); - let result = builder::bare_call(addr.clone()).build_and_unwrap_result(); + ::Currency::set_balance(&contract.account_id, min_balance); + let result = builder::bare_call(contract.addr).build_and_unwrap_result(); assert_return_code!(result, RuntimeReturnCode::TransferFailed); }); } #[test] fn call_return_code() { - let (caller_code, _caller_hash) = compile_module::("call_return_code").unwrap(); - let (callee_code, _callee_hash) = compile_module::("ok_trap_revert").unwrap(); + use test_utils::u256_bytes; + + let (caller_code, _caller_hash) = compile_module("call_return_code").unwrap(); + let (callee_code, _callee_hash) = compile_module("ok_trap_revert").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); let _ = ::Currency::set_balance(&CHARLIE, 1000 * min_balance); - let addr_bob = builder::bare_instantiate(Code::Upload(caller_code)) + let bob = builder::bare_instantiate(Code::Upload(caller_code)) .value(min_balance * 100) - .data(vec![0]) - .build_and_unwrap_account_id(); - ::Currency::set_balance(&addr_bob, min_balance); + .build_and_unwrap_contract(); // Contract calls into Django which is no valid contract - let result = builder::bare_call(addr_bob.clone()) - .data(AsRef::<[u8]>::as_ref(&DJANGO).to_vec()) + // This will be a balance transfer into a new account + // with more than the contract has which will make the transfer fail + let result = builder::bare_call(bob.addr) + .data( + AsRef::<[u8]>::as_ref(&DJANGO_ADDR) + .iter() + .chain(&u256_bytes(min_balance * 200)) + .cloned() + .collect(), + ) + .build_and_unwrap_result(); + assert_return_code!(result, RuntimeReturnCode::TransferFailed); + + // Sending less than the minimum balance will also make the transfer fail + let result = builder::bare_call(bob.addr) + .data( + AsRef::<[u8]>::as_ref(&DJANGO_ADDR) + .iter() + .chain(&u256_bytes(42)) + .cloned() + .collect(), + ) .build_and_unwrap_result(); - assert_return_code!(result, RuntimeReturnCode::NotCallable); + assert_return_code!(result, RuntimeReturnCode::TransferFailed); - let addr_django = builder::bare_instantiate(Code::Upload(callee_code)) + // Sending at least the minimum balance should result in success but + // no code called. + assert_eq!(test_utils::get_balance(Ð_DJANGO), 0); + let result = builder::bare_call(bob.addr) + .data( + AsRef::<[u8]>::as_ref(&DJANGO_ADDR) + .iter() + .chain(&u256_bytes(55)) + .cloned() + .collect(), + ) + .build_and_unwrap_result(); + assert_return_code!(result, RuntimeReturnCode::Success); + assert_eq!(test_utils::get_balance(Ð_DJANGO), 55); + + let django = builder::bare_instantiate(Code::Upload(callee_code)) .origin(RuntimeOrigin::signed(CHARLIE)) .value(min_balance * 100) - .data(vec![0]) - .build_and_unwrap_account_id(); - ::Currency::set_balance(&addr_django, min_balance); + .build_and_unwrap_contract(); - // Contract has only the minimal balance so any transfer will fail. - let result = builder::bare_call(addr_bob.clone()) + // Sending more than the contract has will make the transfer fail. + let result = builder::bare_call(bob.addr) .data( - AsRef::<[u8]>::as_ref(&addr_django) + AsRef::<[u8]>::as_ref(&django.addr) .iter() + .chain(&u256_bytes(min_balance * 300)) .chain(&0u32.to_le_bytes()) .cloned() .collect(), @@ -1524,11 +1551,12 @@ mod run_tests { assert_return_code!(result, RuntimeReturnCode::TransferFailed); // Contract has enough balance but callee reverts because "1" is passed. - ::Currency::set_balance(&addr_bob, min_balance + 1000); - let result = builder::bare_call(addr_bob.clone()) + ::Currency::set_balance(&bob.account_id, min_balance + 1000); + let result = builder::bare_call(bob.addr) .data( - AsRef::<[u8]>::as_ref(&addr_django) + AsRef::<[u8]>::as_ref(&django.addr) .iter() + .chain(&u256_bytes(5)) .chain(&1u32.to_le_bytes()) .cloned() .collect(), @@ -1537,10 +1565,11 @@ mod run_tests { assert_return_code!(result, RuntimeReturnCode::CalleeReverted); // Contract has enough balance but callee traps because "2" is passed. - let result = builder::bare_call(addr_bob) + let result = builder::bare_call(bob.addr) .data( - AsRef::<[u8]>::as_ref(&addr_django) + AsRef::<[u8]>::as_ref(&django.addr) .iter() + .chain(&u256_bytes(5)) .chain(&2u32.to_le_bytes()) .cloned() .collect(), @@ -1552,9 +1581,8 @@ mod run_tests { #[test] fn instantiate_return_code() { - let (caller_code, _caller_hash) = - compile_module::("instantiate_return_code").unwrap(); - let (callee_code, callee_hash) = compile_module::("ok_trap_revert").unwrap(); + let (caller_code, _caller_hash) = compile_module("instantiate_return_code").unwrap(); + let (callee_code, callee_hash) = compile_module("ok_trap_revert").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); @@ -1565,31 +1593,31 @@ mod run_tests { .value(min_balance * 100) .build()); - let addr = builder::bare_instantiate(Code::Upload(caller_code)) + let contract = builder::bare_instantiate(Code::Upload(caller_code)) .value(min_balance * 100) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); // Contract has only the minimal balance so any transfer will fail. - ::Currency::set_balance(&addr, min_balance); - let result = builder::bare_call(addr.clone()) + ::Currency::set_balance(&contract.account_id, min_balance); + let result = builder::bare_call(contract.addr) .data(callee_hash.clone()) .build_and_unwrap_result(); assert_return_code!(result, RuntimeReturnCode::TransferFailed); // Contract has enough balance but the passed code hash is invalid - ::Currency::set_balance(&addr, min_balance + 10_000); + ::Currency::set_balance(&contract.account_id, min_balance + 10_000); let result = - builder::bare_call(addr.clone()).data(vec![0; 33]).build_and_unwrap_result(); + builder::bare_call(contract.addr).data(vec![0; 33]).build_and_unwrap_result(); assert_return_code!(result, RuntimeReturnCode::CodeNotFound); // Contract has enough balance but callee reverts because "1" is passed. - let result = builder::bare_call(addr.clone()) + let result = builder::bare_call(contract.addr) .data(callee_hash.iter().chain(&1u32.to_le_bytes()).cloned().collect()) .build_and_unwrap_result(); assert_return_code!(result, RuntimeReturnCode::CalleeReverted); // Contract has enough balance but callee traps because "2" is passed. - let result = builder::bare_call(addr) + let result = builder::bare_call(contract.addr) .data(callee_hash.iter().chain(&2u32.to_le_bytes()).cloned().collect()) .build_and_unwrap_result(); assert_return_code!(result, RuntimeReturnCode::CalleeTrapped); @@ -1598,16 +1626,16 @@ mod run_tests { #[test] fn disabled_chain_extension_errors_on_call() { - let (code, _hash) = compile_module::("chain_extension").unwrap(); + let (code, _hash) = compile_module("chain_extension").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); - let addr = builder::bare_instantiate(Code::Upload(code)) + let contract = builder::bare_instantiate(Code::Upload(code)) .value(min_balance * 100) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); TestExtension::disable(); assert_err_ignore_postinfo!( - builder::call(addr.clone()).data(vec![7u8; 8]).build(), + builder::call(contract.addr).data(vec![7u8; 8]).build(), Error::::NoChainExtension, ); }); @@ -1615,46 +1643,46 @@ mod run_tests { #[test] fn chain_extension_works() { - let (code, _hash) = compile_module::("chain_extension").unwrap(); + let (code, _hash) = compile_module("chain_extension").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); - let addr = builder::bare_instantiate(Code::Upload(code)) + let contract = builder::bare_instantiate(Code::Upload(code)) .value(min_balance * 100) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); // 0 = read input buffer and pass it through as output let input: Vec = ExtensionInput { extension_id: 0, func_id: 0, extra: &[99] }.into(); - let result = builder::bare_call(addr.clone()).data(input.clone()).build(); + let result = builder::bare_call(contract.addr).data(input.clone()).build(); assert_eq!(TestExtension::last_seen_buffer(), input); assert_eq!(result.result.unwrap().data, input); // 1 = treat inputs as integer primitives and store the supplied integers - builder::bare_call(addr.clone()) + builder::bare_call(contract.addr) .data(ExtensionInput { extension_id: 0, func_id: 1, extra: &[] }.into()) .build_and_unwrap_result(); assert_eq!(TestExtension::last_seen_input_len(), 4); // 2 = charge some extra weight (amount supplied in the fifth byte) - let result = builder::bare_call(addr.clone()) + let result = builder::bare_call(contract.addr) .data(ExtensionInput { extension_id: 0, func_id: 2, extra: &0u32.encode() }.into()) .build(); assert_ok!(result.result); let gas_consumed = result.gas_consumed; - let result = builder::bare_call(addr.clone()) + let result = builder::bare_call(contract.addr) .data(ExtensionInput { extension_id: 0, func_id: 2, extra: &42u32.encode() }.into()) .build(); assert_ok!(result.result); assert_eq!(result.gas_consumed.ref_time(), gas_consumed.ref_time() + 42); - let result = builder::bare_call(addr.clone()) + let result = builder::bare_call(contract.addr) .data(ExtensionInput { extension_id: 0, func_id: 2, extra: &95u32.encode() }.into()) .build(); assert_ok!(result.result); assert_eq!(result.gas_consumed.ref_time(), gas_consumed.ref_time() + 95); // 3 = diverging chain extension call that sets flags to 0x1 and returns a fixed buffer - let result = builder::bare_call(addr.clone()) + let result = builder::bare_call(contract.addr) .data(ExtensionInput { extension_id: 0, func_id: 3, extra: &[] }.into()) .build_and_unwrap_result(); assert_eq!(result.flags, ReturnFlags::REVERT); @@ -1663,7 +1691,7 @@ mod run_tests { // diverging to second chain extension that sets flags to 0x1 and returns a fixed buffer // We set the MSB part to 1 (instead of 0) which routes the request into the second // extension - let result = builder::bare_call(addr.clone()) + let result = builder::bare_call(contract.addr) .data(ExtensionInput { extension_id: 1, func_id: 0, extra: &[] }.into()) .build_and_unwrap_result(); assert_eq!(result.flags, ReturnFlags::REVERT); @@ -1673,7 +1701,7 @@ mod run_tests { // We set the MSB part to 2 (instead of 0) which routes the request into the third // extension assert_err_ignore_postinfo!( - builder::call(addr.clone()) + builder::call(contract.addr) .data(ExtensionInput { extension_id: 2, func_id: 0, extra: &[] }.into()) .build(), Error::::NoChainExtension, @@ -1683,13 +1711,13 @@ mod run_tests { #[test] fn chain_extension_temp_storage_works() { - let (code, _hash) = compile_module::("chain_extension_temp_storage").unwrap(); + let (code, _hash) = compile_module("chain_extension_temp_storage").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); - let addr = builder::bare_instantiate(Code::Upload(code)) + let contract = builder::bare_instantiate(Code::Upload(code)) .value(min_balance * 100) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); // Call func 0 and func 1 back to back. let stop_recursion = 0u8; @@ -1701,32 +1729,32 @@ mod run_tests { .as_ref(), ); - assert_ok!(builder::bare_call(addr.clone()).data(input.clone()).build().result); + assert_ok!(builder::bare_call(contract.addr).data(input.clone()).build().result); }) } #[test] fn lazy_removal_works() { - let (code, _hash) = compile_module::("self_destruct").unwrap(); + let (code, _hash) = compile_module("self_destruct").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); - let addr = builder::bare_instantiate(Code::Upload(code)) + let contract = builder::bare_instantiate(Code::Upload(code)) .value(min_balance * 100) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); - let info = get_contract(&addr); + let info = get_contract(&contract.addr); let trie = &info.child_trie_info(); // Put value into the contracts child trie child::put(trie, &[99], &42); // Terminate the contract - assert_ok!(builder::call(addr.clone()).build()); + assert_ok!(builder::call(contract.addr).build()); // Contract info should be gone - assert!(!>::contains_key(&addr)); + assert!(!>::contains_key(&contract.addr)); // But value should be still there as the lazy removal did not run, yet. assert_matches!(child::get(trie, &[99]), Some(42)); @@ -1741,19 +1769,19 @@ mod run_tests { #[test] fn lazy_batch_removal_works() { - let (code, _hash) = compile_module::("self_destruct").unwrap(); + let (code, _hash) = compile_module("self_destruct").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); let mut tries: Vec = vec![]; for i in 0..3u8 { - let addr = builder::bare_instantiate(Code::Upload(code.clone())) + let contract = builder::bare_instantiate(Code::Upload(code.clone())) .value(min_balance * 100) - .salt(vec![i]) - .build_and_unwrap_account_id(); + .salt(Some([i; 32])) + .build_and_unwrap_contract(); - let info = get_contract(&addr); + let info = get_contract(&contract.addr); let trie = &info.child_trie_info(); // Put value into the contracts child trie @@ -1761,9 +1789,9 @@ mod run_tests { // Terminate the contract. Contract info should be gone, but value should be still // there as the lazy removal did not run, yet. - assert_ok!(builder::call(addr.clone()).build()); + assert_ok!(builder::call(contract.addr).build()); - assert!(!>::contains_key(&addr)); + assert!(!>::contains_key(&contract.addr)); assert_matches!(child::get(trie, &[99]), Some(42)); tries.push(trie.clone()) @@ -1781,7 +1809,7 @@ mod run_tests { #[test] fn lazy_removal_partial_remove_works() { - let (code, _hash) = compile_module::("self_destruct").unwrap(); + let (code, _hash) = compile_module("self_destruct").unwrap(); // We create a contract with some extra keys above the weight limit let extra_keys = 7u32; @@ -1797,9 +1825,9 @@ mod run_tests { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); - let addr = builder::bare_instantiate(Code::Upload(code)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code)) .value(min_balance * 100) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); let info = get_contract(&addr); @@ -1810,7 +1838,7 @@ mod run_tests { >::insert(&addr, info.clone()); // Terminate the contract - assert_ok!(builder::call(addr.clone()).build()); + assert_ok!(builder::call(addr).build()); // Contract info should be gone assert!(!>::contains_key(&addr)); @@ -1856,14 +1884,14 @@ mod run_tests { #[test] fn lazy_removal_does_no_run_on_low_remaining_weight() { - let (code, _hash) = compile_module::("self_destruct").unwrap(); + let (code, _hash) = compile_module("self_destruct").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); - let addr = builder::bare_instantiate(Code::Upload(code)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code)) .value(min_balance * 100) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); let info = get_contract(&addr); let trie = &info.child_trie_info(); @@ -1872,7 +1900,7 @@ mod run_tests { child::put(trie, &[99], &42); // Terminate the contract - assert_ok!(builder::call(addr.clone()).build()); + assert_ok!(builder::call(addr).build()); // Contract info should be gone assert!(!>::contains_key(&addr)); @@ -1906,7 +1934,7 @@ mod run_tests { #[test] fn lazy_removal_does_not_use_all_weight() { - let (code, _hash) = compile_module::("self_destruct").unwrap(); + let (code, _hash) = compile_module("self_destruct").unwrap(); let mut meter = WeightMeter::with_limit(Weight::from_parts(5_000_000_000, 100 * 1024)); let mut ext = ExtBuilder::default().existential_deposit(50).build(); @@ -1915,9 +1943,9 @@ mod run_tests { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); - let addr = builder::bare_instantiate(Code::Upload(code)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code)) .value(min_balance * 100) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); let info = get_contract(&addr); let (weight_per_key, max_keys) = ContractInfo::::deletion_budget(&meter); @@ -1935,7 +1963,7 @@ mod run_tests { >::insert(&addr, info.clone()); // Terminate the contract - assert_ok!(builder::call(addr.clone()).build()); + assert_ok!(builder::call(addr).build()); // Contract info should be gone assert!(!>::contains_key(&addr)); @@ -1970,7 +1998,7 @@ mod run_tests { #[test] fn deletion_queue_ring_buffer_overflow() { - let (code, _hash) = compile_module::("self_destruct").unwrap(); + let (code, _hash) = compile_module("self_destruct").unwrap(); let mut ext = ExtBuilder::default().existential_deposit(50).build(); // setup the deletion queue with custom counters @@ -1989,10 +2017,10 @@ mod run_tests { // add 3 contracts to the deletion queue for i in 0..3u8 { - let addr = builder::bare_instantiate(Code::Upload(code.clone())) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code.clone())) .value(min_balance * 100) - .salt(vec![i]) - .build_and_unwrap_account_id(); + .salt(Some([i; 32])) + .build_and_unwrap_contract(); let info = get_contract(&addr); let trie = &info.child_trie_info(); @@ -2002,7 +2030,7 @@ mod run_tests { // Terminate the contract. Contract info should be gone, but value should be still // there as the lazy removal did not run, yet. - assert_ok!(builder::call(addr.clone()).build()); + assert_ok!(builder::call(addr).build()); assert!(!>::contains_key(&addr)); assert_matches!(child::get(trie, &[99]), Some(42)); @@ -2024,27 +2052,29 @@ mod run_tests { } #[test] fn refcounter() { - let (wasm, code_hash) = compile_module::("self_destruct").unwrap(); + let (wasm, code_hash) = compile_module("self_destruct").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); let min_balance = Contracts::min_balance(); // Create two contracts with the same code and check that they do in fact share it. - let addr0 = builder::bare_instantiate(Code::Upload(wasm.clone())) - .value(min_balance * 100) - .salt(vec![0]) - .build_and_unwrap_account_id(); - let addr1 = builder::bare_instantiate(Code::Upload(wasm.clone())) - .value(min_balance * 100) - .salt(vec![1]) - .build_and_unwrap_account_id(); + let Contract { addr: addr0, .. } = + builder::bare_instantiate(Code::Upload(wasm.clone())) + .value(min_balance * 100) + .salt(Some([0; 32])) + .build_and_unwrap_contract(); + let Contract { addr: addr1, .. } = + builder::bare_instantiate(Code::Upload(wasm.clone())) + .value(min_balance * 100) + .salt(Some([1; 32])) + .build_and_unwrap_contract(); assert_refcount!(code_hash, 2); // Sharing should also work with the usual instantiate call - let addr2 = builder::bare_instantiate(Code::Existing(code_hash)) + let Contract { addr: addr2, .. } = builder::bare_instantiate(Code::Existing(code_hash)) .value(min_balance * 100) - .salt(vec![2]) - .build_and_unwrap_account_id(); + .salt(Some([2; 32])) + .build_and_unwrap_contract(); assert_refcount!(code_hash, 3); // Terminating one contract should decrement the refcount @@ -2069,13 +2099,13 @@ mod run_tests { #[test] fn debug_message_works() { - let (wasm, _code_hash) = compile_module::("debug_message_works").unwrap(); + let (wasm, _code_hash) = compile_module("debug_message_works").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) .value(30_000) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); let result = builder::bare_call(addr).debug(DebugInfo::UnsafeDebug).build(); assert_matches!(result.result, Ok(_)); @@ -2085,13 +2115,13 @@ mod run_tests { #[test] fn debug_message_logging_disabled() { - let (wasm, _code_hash) = compile_module::("debug_message_logging_disabled").unwrap(); + let (wasm, _code_hash) = compile_module("debug_message_logging_disabled").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) .value(30_000) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); // the dispatchables always run without debugging assert_ok!(Contracts::call( RuntimeOrigin::signed(ALICE), @@ -2106,13 +2136,13 @@ mod run_tests { #[test] fn debug_message_invalid_utf8() { - let (wasm, _code_hash) = compile_module::("debug_message_invalid_utf8").unwrap(); + let (wasm, _code_hash) = compile_module("debug_message_invalid_utf8").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) .value(30_000) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); let result = builder::bare_call(addr).debug(DebugInfo::UnsafeDebug).build(); assert_ok!(result.result); assert!(result.debug_message.is_empty()); @@ -2121,24 +2151,27 @@ mod run_tests { #[test] fn gas_estimation_for_subcalls() { - let (caller_code, _caller_hash) = compile_module::("call_with_limit").unwrap(); - let (call_runtime_code, _caller_hash) = compile_module::("call_runtime").unwrap(); - let (dummy_code, _callee_hash) = compile_module::("dummy").unwrap(); + let (caller_code, _caller_hash) = compile_module("call_with_limit").unwrap(); + let (call_runtime_code, _caller_hash) = compile_module("call_runtime").unwrap(); + let (dummy_code, _callee_hash) = compile_module("dummy").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 2_000 * min_balance); - let addr_caller = builder::bare_instantiate(Code::Upload(caller_code)) - .value(min_balance * 100) - .build_and_unwrap_account_id(); + let Contract { addr: addr_caller, .. } = + builder::bare_instantiate(Code::Upload(caller_code)) + .value(min_balance * 100) + .build_and_unwrap_contract(); - let addr_dummy = builder::bare_instantiate(Code::Upload(dummy_code)) - .value(min_balance * 100) - .build_and_unwrap_account_id(); + let Contract { addr: addr_dummy, .. } = + builder::bare_instantiate(Code::Upload(dummy_code)) + .value(min_balance * 100) + .build_and_unwrap_contract(); - let addr_call_runtime = builder::bare_instantiate(Code::Upload(call_runtime_code)) - .value(min_balance * 100) - .build_and_unwrap_account_id(); + let Contract { addr: addr_call_runtime, .. } = + builder::bare_instantiate(Code::Upload(call_runtime_code)) + .value(min_balance * 100) + .build_and_unwrap_contract(); // Run the test for all of those weight limits for the subcall let weights = [ @@ -2174,8 +2207,7 @@ mod run_tests { .collect(); // Call in order to determine the gas that is required for this call - let result_orig = - builder::bare_call(addr_caller.clone()).data(input.clone()).build(); + let result_orig = builder::bare_call(addr_caller).data(input.clone()).build(); assert_ok!(&result_orig.result); // If the out of gas happens in the subcall the caller contract @@ -2190,7 +2222,7 @@ mod run_tests { }; // Make the same call using the estimated gas. Should succeed. - let result = builder::bare_call(addr_caller.clone()) + let result = builder::bare_call(addr_caller) .gas_limit(result_orig.gas_required) .storage_deposit_limit(result_orig.storage_deposit.charge_or_zero()) .data(input.clone()) @@ -2198,7 +2230,7 @@ mod run_tests { assert_ok!(&result.result); // Check that it fails with too little ref_time - let result = builder::bare_call(addr_caller.clone()) + let result = builder::bare_call(addr_caller) .gas_limit(result_orig.gas_required.sub_ref_time(1)) .storage_deposit_limit(result_orig.storage_deposit.charge_or_zero()) .data(input.clone()) @@ -2206,7 +2238,7 @@ mod run_tests { assert_err!(result.result, error); // Check that it fails with too little proof_size - let result = builder::bare_call(addr_caller.clone()) + let result = builder::bare_call(addr_caller) .gas_limit(result_orig.gas_required.sub_proof_size(1)) .storage_deposit_limit(result_orig.storage_deposit.charge_or_zero()) .data(input.clone()) @@ -2219,16 +2251,17 @@ mod run_tests { #[test] fn gas_estimation_call_runtime() { - let (caller_code, _caller_hash) = compile_module::("call_runtime").unwrap(); + let (caller_code, _caller_hash) = compile_module("call_runtime").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); let _ = ::Currency::set_balance(&CHARLIE, 1000 * min_balance); - let addr_caller = builder::bare_instantiate(Code::Upload(caller_code)) - .value(min_balance * 100) - .salt(vec![0]) - .build_and_unwrap_account_id(); + let Contract { addr: addr_caller, .. } = + builder::bare_instantiate(Code::Upload(caller_code)) + .value(min_balance * 100) + .salt(Some([0; 32])) + .build_and_unwrap_contract(); // Call something trivial with a huge gas limit so that we can observe the effects // of pre-charging. This should create a difference between consumed and required. @@ -2236,7 +2269,7 @@ mod run_tests { pre_charge: Weight::from_parts(10_000_000, 1_000), actual_weight: Weight::from_parts(100, 100), }); - let result = builder::bare_call(addr_caller.clone()).data(call.encode()).build(); + let result = builder::bare_call(addr_caller).data(call.encode()).build(); // contract encodes the result of the dispatch runtime let outcome = u32::decode(&mut result.result.unwrap().data.as_ref()).unwrap(); assert_eq!(outcome, 0); @@ -2255,22 +2288,24 @@ mod run_tests { #[test] fn call_runtime_reentrancy_guarded() { - let (caller_code, _caller_hash) = compile_module::("call_runtime").unwrap(); - let (callee_code, _callee_hash) = compile_module::("dummy").unwrap(); + let (caller_code, _caller_hash) = compile_module("call_runtime").unwrap(); + let (callee_code, _callee_hash) = compile_module("dummy").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); let _ = ::Currency::set_balance(&CHARLIE, 1000 * min_balance); - let addr_caller = builder::bare_instantiate(Code::Upload(caller_code)) - .value(min_balance * 100) - .salt(vec![0]) - .build_and_unwrap_account_id(); + let Contract { addr: addr_caller, .. } = + builder::bare_instantiate(Code::Upload(caller_code)) + .value(min_balance * 100) + .salt(Some([0; 32])) + .build_and_unwrap_contract(); - let addr_callee = builder::bare_instantiate(Code::Upload(callee_code)) - .value(min_balance * 100) - .salt(vec![1]) - .build_and_unwrap_account_id(); + let Contract { addr: addr_callee, .. } = + builder::bare_instantiate(Code::Upload(callee_code)) + .value(min_balance * 100) + .salt(Some([1; 32])) + .build_and_unwrap_contract(); // Call pallet_revive call() dispatchable let call = RuntimeCall::Contracts(crate::Call::call { @@ -2283,9 +2318,8 @@ mod run_tests { // Call runtime to re-enter back to contracts engine by // calling dummy contract - let result = builder::bare_call(addr_caller.clone()) - .data(call.encode()) - .build_and_unwrap_result(); + let result = + builder::bare_call(addr_caller).data(call.encode()).build_and_unwrap_result(); // Call to runtime should fail because of the re-entrancy guard assert_return_code!(result, RuntimeReturnCode::CallRuntimeFailed); }); @@ -2293,15 +2327,15 @@ mod run_tests { #[test] fn ecdsa_recover() { - let (wasm, _code_hash) = compile_module::("ecdsa_recover").unwrap(); + let (wasm, _code_hash) = compile_module("ecdsa_recover").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Instantiate the ecdsa_recover contract. - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) .value(100_000) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); #[rustfmt::skip] let signature: [u8; 65] = [ @@ -2326,7 +2360,7 @@ mod run_tests { params.extend_from_slice(&signature); params.extend_from_slice(&message_hash); assert!(params.len() == 65 + 32); - let result = builder::bare_call(addr.clone()).data(params).build_and_unwrap_result(); + let result = builder::bare_call(addr).data(params).build_and_unwrap_result(); assert!(!result.did_revert()); assert_eq!(result.data, EXPECTED_COMPRESSED_PUBLIC_KEY); }) @@ -2334,7 +2368,7 @@ mod run_tests { #[test] fn bare_instantiate_returns_events() { - let (wasm, _code_hash) = compile_module::("transfer_return_code").unwrap(); + let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); @@ -2352,7 +2386,7 @@ mod run_tests { #[test] fn bare_instantiate_does_not_return_events() { - let (wasm, _code_hash) = compile_module::("transfer_return_code").unwrap(); + let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); @@ -2368,18 +2402,17 @@ mod run_tests { #[test] fn bare_call_returns_events() { - let (wasm, _code_hash) = compile_module::("transfer_return_code").unwrap(); + let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) .value(min_balance * 100) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); - let result = builder::bare_call(addr.clone()) - .collect_events(CollectEvents::UnsafeCollect) - .build(); + let result = + builder::bare_call(addr).collect_events(CollectEvents::UnsafeCollect).build(); let events = result.events.unwrap(); assert_return_code!(&result.result.unwrap(), RuntimeReturnCode::Success); @@ -2390,16 +2423,16 @@ mod run_tests { #[test] fn bare_call_does_not_return_events() { - let (wasm, _code_hash) = compile_module::("transfer_return_code").unwrap(); + let (wasm, _code_hash) = compile_module("transfer_return_code").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = Contracts::min_balance(); let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) .value(min_balance * 100) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); - let result = builder::bare_call(addr.clone()).build(); + let result = builder::bare_call(addr).build(); let events = result.events; assert_return_code!(&result.result.unwrap(), RuntimeReturnCode::Success); @@ -2410,15 +2443,15 @@ mod run_tests { #[test] fn sr25519_verify() { - let (wasm, _code_hash) = compile_module::("sr25519_verify").unwrap(); + let (wasm, _code_hash) = compile_module("sr25519_verify").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Instantiate the sr25519_verify contract. - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm)) .value(100_000) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); let call_with = |message: &[u8; 11]| { // Alice's signature for "hello world" @@ -2442,7 +2475,7 @@ mod run_tests { params.extend_from_slice(&public_key); params.extend_from_slice(message); - builder::bare_call(addr.clone()).data(params).build_and_unwrap_result() + builder::bare_call(addr).data(params).build_and_unwrap_result() }; // verification should succeed for "hello world" @@ -2455,8 +2488,8 @@ mod run_tests { #[test] fn failed_deposit_charge_should_roll_back_call() { - let (wasm_caller, _) = compile_module::("call_runtime_and_call").unwrap(); - let (wasm_callee, _) = compile_module::("store_call").unwrap(); + let (wasm_caller, _) = compile_module("call_runtime_and_call").unwrap(); + let (wasm_callee, _) = compile_module("store_call").unwrap(); const ED: u64 = 200; let execute = || { @@ -2464,15 +2497,16 @@ mod run_tests { let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Instantiate both contracts. - let addr_caller = builder::bare_instantiate(Code::Upload(wasm_caller.clone())) - .build_and_unwrap_account_id(); - let addr_callee = builder::bare_instantiate(Code::Upload(wasm_callee.clone())) - .build_and_unwrap_account_id(); + let caller = builder::bare_instantiate(Code::Upload(wasm_caller.clone())) + .build_and_unwrap_contract(); + let Contract { addr: addr_callee, .. } = + builder::bare_instantiate(Code::Upload(wasm_callee.clone())) + .build_and_unwrap_contract(); // Give caller proxy access to Alice. assert_ok!(Proxy::add_proxy( RuntimeOrigin::signed(ALICE), - addr_caller.clone(), + caller.account_id.clone(), (), 0 )); @@ -2497,7 +2531,7 @@ mod run_tests { transfer_proxy_call, ); - builder::call(addr_caller).data(data.encode()).build() + builder::call(caller.addr).data(data.encode()).build() }) }; @@ -2511,7 +2545,7 @@ mod run_tests { #[test] fn upload_code_works() { - let (wasm, code_hash) = compile_module::("dummy").unwrap(); + let (wasm, code_hash) = compile_module("dummy").unwrap(); ExtBuilder::default().existential_deposit(100).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); @@ -2532,7 +2566,7 @@ mod run_tests { event: RuntimeEvent::Contracts(crate::Event::CodeStored { code_hash, deposit_held: deposit_expected, - uploader: ALICE + uploader: ALICE_ADDR }), topics: vec![], },] @@ -2542,7 +2576,7 @@ mod run_tests { #[test] fn upload_code_limit_too_low() { - let (wasm, _code_hash) = compile_module::("dummy").unwrap(); + let (wasm, _code_hash) = compile_module("dummy").unwrap(); let deposit_expected = expected_deposit(wasm.len()); let deposit_insufficient = deposit_expected.saturating_sub(1); @@ -2563,7 +2597,7 @@ mod run_tests { #[test] fn upload_code_not_enough_balance() { - let (wasm, _code_hash) = compile_module::("dummy").unwrap(); + let (wasm, _code_hash) = compile_module("dummy").unwrap(); let deposit_expected = expected_deposit(wasm.len()); let deposit_insufficient = deposit_expected.saturating_sub(1); @@ -2584,7 +2618,7 @@ mod run_tests { #[test] fn remove_code_works() { - let (wasm, code_hash) = compile_module::("dummy").unwrap(); + let (wasm, code_hash) = compile_module("dummy").unwrap(); ExtBuilder::default().existential_deposit(100).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); @@ -2605,7 +2639,7 @@ mod run_tests { event: RuntimeEvent::Contracts(crate::Event::CodeStored { code_hash, deposit_held: deposit_expected, - uploader: ALICE + uploader: ALICE_ADDR }), topics: vec![], }, @@ -2614,7 +2648,7 @@ mod run_tests { event: RuntimeEvent::Contracts(crate::Event::CodeRemoved { code_hash, deposit_released: deposit_expected, - remover: ALICE + remover: ALICE_ADDR }), topics: vec![], }, @@ -2625,7 +2659,7 @@ mod run_tests { #[test] fn remove_code_wrong_origin() { - let (wasm, code_hash) = compile_module::("dummy").unwrap(); + let (wasm, code_hash) = compile_module("dummy").unwrap(); ExtBuilder::default().existential_deposit(100).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); @@ -2649,7 +2683,7 @@ mod run_tests { event: RuntimeEvent::Contracts(crate::Event::CodeStored { code_hash, deposit_held: deposit_expected, - uploader: ALICE + uploader: ALICE_ADDR }), topics: vec![], },] @@ -2659,7 +2693,7 @@ mod run_tests { #[test] fn remove_code_in_use() { - let (wasm, code_hash) = compile_module::("dummy").unwrap(); + let (wasm, code_hash) = compile_module("dummy").unwrap(); ExtBuilder::default().existential_deposit(100).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); @@ -2680,7 +2714,7 @@ mod run_tests { #[test] fn remove_code_not_found() { - let (_wasm, code_hash) = compile_module::("dummy").unwrap(); + let (_wasm, code_hash) = compile_module("dummy").unwrap(); ExtBuilder::default().existential_deposit(100).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); @@ -2699,7 +2733,7 @@ mod run_tests { #[test] fn instantiate_with_zero_balance_works() { - let (wasm, code_hash) = compile_module::("dummy").unwrap(); + let (wasm, code_hash) = compile_module("dummy").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); let min_balance = Contracts::min_balance(); @@ -2708,15 +2742,16 @@ mod run_tests { initialize_block(2); // Instantiate the BOB contract. - let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id(); + let Contract { addr, account_id } = + builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract(); // Ensure the contract was stored and get expected deposit amount to be reserved. let deposit_expected = expected_deposit(ensure_stored(code_hash)); // Make sure the account exists even though no free balance was send - assert_eq!(::Currency::free_balance(&addr), min_balance); + assert_eq!(::Currency::free_balance(&account_id), min_balance); assert_eq!( - ::Currency::total_balance(&addr), + ::Currency::total_balance(&account_id), min_balance + test_utils::contract_info_storage_deposit(&addr) ); @@ -2728,21 +2763,21 @@ mod run_tests { event: RuntimeEvent::Contracts(crate::Event::CodeStored { code_hash, deposit_held: deposit_expected, - uploader: ALICE + uploader: ALICE_ADDR }), topics: vec![], }, EventRecord { phase: Phase::Initialization, event: RuntimeEvent::System(frame_system::Event::NewAccount { - account: addr.clone(), + account: account_id.clone(), }), topics: vec![], }, EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Endowed { - account: addr.clone(), + account: account_id.clone(), free_balance: min_balance, }), topics: vec![], @@ -2751,7 +2786,7 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { from: ALICE, - to: addr.clone(), + to: account_id, amount: min_balance, }), topics: vec![], @@ -2759,8 +2794,8 @@ mod run_tests { EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Instantiated { - deployer: ALICE, - contract: addr.clone(), + deployer: ALICE_ADDR, + contract: addr, }), topics: vec![], }, @@ -2768,8 +2803,8 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Contracts( pallet_revive::Event::StorageDepositTransferredAndHeld { - from: ALICE, - to: addr.clone(), + from: ALICE_ADDR, + to: addr, amount: test_utils::contract_info_storage_deposit(&addr), } ), @@ -2782,7 +2817,7 @@ mod run_tests { #[test] fn instantiate_with_below_existential_deposit_works() { - let (wasm, code_hash) = compile_module::("dummy").unwrap(); + let (wasm, code_hash) = compile_module("dummy").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); let min_balance = Contracts::min_balance(); @@ -2792,16 +2827,16 @@ mod run_tests { initialize_block(2); // Instantiate the BOB contract. - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let Contract { addr, account_id } = builder::bare_instantiate(Code::Upload(wasm)) .value(value) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); // Ensure the contract was stored and get expected deposit amount to be reserved. let deposit_expected = expected_deposit(ensure_stored(code_hash)); // Make sure the account exists even though not enough free balance was send - assert_eq!(::Currency::free_balance(&addr), min_balance + value); + assert_eq!(::Currency::free_balance(&account_id), min_balance + value); assert_eq!( - ::Currency::total_balance(&addr), + ::Currency::total_balance(&account_id), min_balance + value + test_utils::contract_info_storage_deposit(&addr) ); @@ -2813,21 +2848,21 @@ mod run_tests { event: RuntimeEvent::Contracts(crate::Event::CodeStored { code_hash, deposit_held: deposit_expected, - uploader: ALICE + uploader: ALICE_ADDR }), topics: vec![], }, EventRecord { phase: Phase::Initialization, event: RuntimeEvent::System(frame_system::Event::NewAccount { - account: addr.clone() + account: account_id.clone() }), topics: vec![], }, EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Endowed { - account: addr.clone(), + account: account_id.clone(), free_balance: min_balance, }), topics: vec![], @@ -2836,7 +2871,7 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { from: ALICE, - to: addr.clone(), + to: account_id.clone(), amount: min_balance, }), topics: vec![], @@ -2845,7 +2880,7 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { from: ALICE, - to: addr.clone(), + to: account_id.clone(), amount: 50, }), topics: vec![], @@ -2853,8 +2888,8 @@ mod run_tests { EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Instantiated { - deployer: ALICE, - contract: addr.clone(), + deployer: ALICE_ADDR, + contract: addr, }), topics: vec![], }, @@ -2862,8 +2897,8 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Contracts( pallet_revive::Event::StorageDepositTransferredAndHeld { - from: ALICE, - to: addr.clone(), + from: ALICE_ADDR, + to: addr, amount: test_utils::contract_info_storage_deposit(&addr), } ), @@ -2876,11 +2911,12 @@ mod run_tests { #[test] fn storage_deposit_works() { - let (wasm, _code_hash) = compile_module::("multi_store").unwrap(); + let (wasm, _code_hash) = compile_module("multi_store").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); - let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id(); + let Contract { addr, account_id } = + builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract(); let mut deposit = test_utils::contract_info_storage_deposit(&addr); @@ -2888,20 +2924,20 @@ mod run_tests { initialize_block(2); // Create storage - assert_ok!(builder::call(addr.clone()).value(42).data((50u32, 20u32).encode()).build()); + assert_ok!(builder::call(addr).value(42).data((50u32, 20u32).encode()).build()); // 4 is for creating 2 storage items let charged0 = 4 + 50 + 20; deposit += charged0; assert_eq!(get_contract(&addr).total_deposit(), deposit); // Add more storage (but also remove some) - assert_ok!(builder::call(addr.clone()).data((100u32, 10u32).encode()).build()); + assert_ok!(builder::call(addr).data((100u32, 10u32).encode()).build()); let charged1 = 50 - 10; deposit += charged1; assert_eq!(get_contract(&addr).total_deposit(), deposit); // Remove more storage (but also add some) - assert_ok!(builder::call(addr.clone()).data((10u32, 20u32).encode()).build()); + assert_ok!(builder::call(addr).data((10u32, 20u32).encode()).build()); // -1 for numeric instability let refunded0 = 90 - 10 - 1; deposit -= refunded0; @@ -2914,7 +2950,7 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Balances(pallet_balances::Event::Transfer { from: ALICE, - to: addr.clone(), + to: account_id.clone(), amount: 42, }), topics: vec![], @@ -2923,7 +2959,7 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Called { caller: Origin::from_account_id(ALICE), - contract: addr.clone(), + contract: addr, }), topics: vec![], }, @@ -2931,8 +2967,8 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Contracts( pallet_revive::Event::StorageDepositTransferredAndHeld { - from: ALICE, - to: addr.clone(), + from: ALICE_ADDR, + to: addr, amount: charged0, } ), @@ -2942,7 +2978,7 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Called { caller: Origin::from_account_id(ALICE), - contract: addr.clone(), + contract: addr, }), topics: vec![], }, @@ -2950,8 +2986,8 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Contracts( pallet_revive::Event::StorageDepositTransferredAndHeld { - from: ALICE, - to: addr.clone(), + from: ALICE_ADDR, + to: addr, amount: charged1, } ), @@ -2961,7 +2997,7 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Called { caller: Origin::from_account_id(ALICE), - contract: addr.clone(), + contract: addr, }), topics: vec![], }, @@ -2969,8 +3005,8 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Contracts( pallet_revive::Event::StorageDepositTransferredAndReleased { - from: addr.clone(), - to: ALICE, + from: addr, + to: ALICE_ADDR, amount: refunded0, } ), @@ -2983,24 +3019,24 @@ mod run_tests { #[test] fn storage_deposit_callee_works() { - let (wasm_caller, _code_hash_caller) = compile_module::("call").unwrap(); - let (wasm_callee, _code_hash_callee) = compile_module::("store_call").unwrap(); + let (wasm_caller, _code_hash_caller) = compile_module("call").unwrap(); + let (wasm_callee, _code_hash_callee) = compile_module("store_call").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); let min_balance = Contracts::min_balance(); // Create both contracts: Constructors do nothing. - let addr_caller = - builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_account_id(); - let addr_callee = - builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_account_id(); + let Contract { addr: addr_caller, .. } = + builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_contract(); + let Contract { addr: addr_callee, account_id } = + builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_contract(); assert_ok!(builder::call(addr_caller).data((100u32, &addr_callee).encode()).build()); let callee = get_contract(&addr_callee); let deposit = DepositPerByte::get() * 100 + DepositPerItem::get() * 1; - assert_eq!(test_utils::get_balance(&addr_callee), min_balance); + assert_eq!(test_utils::get_balance(&account_id), min_balance); assert_eq!( callee.total_deposit(), deposit + test_utils::contract_info_storage_deposit(&addr_callee) @@ -3010,15 +3046,16 @@ mod run_tests { #[test] fn set_code_extrinsic() { - let (wasm, code_hash) = compile_module::("dummy").unwrap(); - let (new_wasm, new_code_hash) = compile_module::("crypto_hashes").unwrap(); + let (wasm, code_hash) = compile_module("dummy").unwrap(); + let (new_wasm, new_code_hash) = compile_module("crypto_hashes").unwrap(); assert_ne!(code_hash, new_code_hash); ExtBuilder::default().existential_deposit(100).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); - let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id(); + let Contract { addr, .. } = + builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract(); assert_ok!(Contracts::upload_code( RuntimeOrigin::signed(ALICE), @@ -3035,7 +3072,7 @@ mod run_tests { // only root can execute this extrinsic assert_noop!( - Contracts::set_code(RuntimeOrigin::signed(ALICE), addr.clone(), new_code_hash), + Contracts::set_code(RuntimeOrigin::signed(ALICE), addr, new_code_hash), sp_runtime::traits::BadOrigin, ); assert_eq!(get_contract(&addr).code_hash, code_hash); @@ -3045,7 +3082,7 @@ mod run_tests { // contract must exist assert_noop!( - Contracts::set_code(RuntimeOrigin::root(), BOB, new_code_hash), + Contracts::set_code(RuntimeOrigin::root(), BOB_ADDR, new_code_hash), >::ContractNotFound, ); assert_eq!(get_contract(&addr).code_hash, code_hash); @@ -3055,7 +3092,7 @@ mod run_tests { // new code hash must exist assert_noop!( - Contracts::set_code(RuntimeOrigin::root(), addr.clone(), Default::default()), + Contracts::set_code(RuntimeOrigin::root(), addr, Default::default()), >::CodeNotFound, ); assert_eq!(get_contract(&addr).code_hash, code_hash); @@ -3064,7 +3101,7 @@ mod run_tests { assert_eq!(System::events(), vec![]); // successful call - assert_ok!(Contracts::set_code(RuntimeOrigin::root(), addr.clone(), new_code_hash)); + assert_ok!(Contracts::set_code(RuntimeOrigin::root(), addr, new_code_hash)); assert_eq!(get_contract(&addr).code_hash, new_code_hash); assert_refcount!(&code_hash, 0); assert_refcount!(&new_code_hash, 1); @@ -3073,7 +3110,7 @@ mod run_tests { vec![EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Contracts(pallet_revive::Event::ContractCodeUpdated { - contract: addr.clone(), + contract: addr, new_code_hash, old_code_hash: code_hash, }), @@ -3085,15 +3122,15 @@ mod run_tests { #[test] fn slash_cannot_kill_account() { - let (wasm, _code_hash) = compile_module::("dummy").unwrap(); + let (wasm, _code_hash) = compile_module("dummy").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let value = 700; let _ = ::Currency::set_balance(&ALICE, 1_000_000); let min_balance = Contracts::min_balance(); - let addr = builder::bare_instantiate(Code::Upload(wasm)) + let Contract { addr, account_id } = builder::bare_instantiate(Code::Upload(wasm)) .value(value) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); // Drop previous events initialize_block(2); @@ -3101,12 +3138,15 @@ mod run_tests { let info_deposit = test_utils::contract_info_storage_deposit(&addr); assert_eq!( - test_utils::get_balance_on_hold(&HoldReason::StorageDepositReserve.into(), &addr), + test_utils::get_balance_on_hold( + &HoldReason::StorageDepositReserve.into(), + &account_id + ), info_deposit ); assert_eq!( - ::Currency::total_balance(&addr), + ::Currency::total_balance(&account_id), info_deposit + value + min_balance ); @@ -3116,18 +3156,18 @@ mod run_tests { // in staking. let _ = ::Currency::slash( &HoldReason::StorageDepositReserve.into(), - &addr, - ::Currency::total_balance(&addr), + &account_id, + ::Currency::total_balance(&account_id), ); // Slashing only removed the balance held. - assert_eq!(::Currency::total_balance(&addr), value + min_balance); + assert_eq!(::Currency::total_balance(&account_id), value + min_balance); }); } #[test] fn contract_reverted() { - let (wasm, code_hash) = compile_module::("return_with_data").unwrap(); + let (wasm, code_hash) = compile_module("return_with_data").unwrap(); ExtBuilder::default().existential_deposit(100).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); @@ -3163,21 +3203,21 @@ mod run_tests { .build_and_unwrap_result(); assert_eq!(result.result.flags, flags); assert_eq!(result.result.data, buffer); - assert!(!>::contains_key(result.account_id)); + assert!(!>::contains_key(result.addr)); // Pass empty flags and therefore successfully instantiate the contract for later use. - let addr = builder::bare_instantiate(Code::Existing(code_hash)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Existing(code_hash)) .data(ReturnFlags::empty().bits().encode()) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); // Calling extrinsic: revert leads to an error assert_err_ignore_postinfo!( - builder::call(addr.clone()).data(input.clone()).build(), + builder::call(addr).data(input.clone()).build(), >::ContractReverted, ); // Calling directly: revert leads to success but the flags indicate the error - let result = builder::bare_call(addr.clone()).data(input).build_and_unwrap_result(); + let result = builder::bare_call(addr).data(input).build_and_unwrap_result(); assert_eq!(result.flags, flags); assert_eq!(result.data, buffer); }); @@ -3185,17 +3225,17 @@ mod run_tests { #[test] fn set_code_hash() { - let (wasm, code_hash) = compile_module::("set_code_hash").unwrap(); - let (new_wasm, new_code_hash) = - compile_module::("new_set_code_hash_contract").unwrap(); + let (wasm, code_hash) = compile_module("set_code_hash").unwrap(); + let (new_wasm, new_code_hash) = compile_module("new_set_code_hash_contract").unwrap(); ExtBuilder::default().existential_deposit(100).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Instantiate the 'caller' - let contract_addr = builder::bare_instantiate(Code::Upload(wasm)) - .value(300_000) - .build_and_unwrap_account_id(); + let Contract { addr: contract_addr, .. } = + builder::bare_instantiate(Code::Upload(wasm)) + .value(300_000) + .build_and_unwrap_contract(); // upload new code assert_ok!(Contracts::upload_code( RuntimeOrigin::signed(ALICE), @@ -3206,14 +3246,14 @@ mod run_tests { System::reset_events(); // First call sets new code_hash and returns 1 - let result = builder::bare_call(contract_addr.clone()) + let result = builder::bare_call(contract_addr) .data(new_code_hash.as_ref().to_vec()) .debug(DebugInfo::UnsafeDebug) .build_and_unwrap_result(); assert_return_code!(result, 1); // Second calls new contract code that returns 2 - let result = builder::bare_call(contract_addr.clone()) + let result = builder::bare_call(contract_addr) .debug(DebugInfo::UnsafeDebug) .build_and_unwrap_result(); assert_return_code!(result, 2); @@ -3225,7 +3265,7 @@ mod run_tests { EventRecord { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::ContractCodeUpdated { - contract: contract_addr.clone(), + contract: contract_addr, new_code_hash, old_code_hash: code_hash, }), @@ -3235,7 +3275,7 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Called { caller: Origin::from_account_id(ALICE), - contract: contract_addr.clone(), + contract: contract_addr, }), topics: vec![], }, @@ -3243,7 +3283,7 @@ mod run_tests { phase: Phase::Initialization, event: RuntimeEvent::Contracts(crate::Event::Called { caller: Origin::from_account_id(ALICE), - contract: contract_addr.clone(), + contract: contract_addr, }), topics: vec![], }, @@ -3254,7 +3294,7 @@ mod run_tests { #[test] fn storage_deposit_limit_is_enforced() { - let (wasm, _code_hash) = compile_module::("store_call").unwrap(); + let (wasm, _code_hash) = compile_module("store_call").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); let min_balance = Contracts::min_balance(); @@ -3270,13 +3310,14 @@ mod run_tests { ); // Instantiate the BOB contract. - let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id(); + let Contract { addr, account_id } = + builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract(); let info_deposit = test_utils::contract_info_storage_deposit(&addr); // Check that the BOB contract has been instantiated and has the minimum balance assert_eq!(get_contract(&addr).total_deposit(), info_deposit); assert_eq!( - ::Currency::total_balance(&addr), + ::Currency::total_balance(&account_id), info_deposit + min_balance ); @@ -3284,7 +3325,7 @@ mod run_tests { // setting insufficient deposit limit, as it requires 3 Balance: // 2 for the item added + 1 for the new storage item. assert_err_ignore_postinfo!( - builder::call(addr.clone()) + builder::call(addr) .storage_deposit_limit(2) .data(1u32.to_le_bytes().to_vec()) .build(), @@ -3294,7 +3335,7 @@ mod run_tests { // Create 1 byte of storage, should cost 3 Balance: // 2 for the item added + 1 for the new storage item. // Should pass as it fallbacks to DefaultDepositLimit. - assert_ok!(builder::call(addr.clone()) + assert_ok!(builder::call(addr) .storage_deposit_limit(3) .data(1u32.to_le_bytes().to_vec()) .build()); @@ -3302,7 +3343,7 @@ mod run_tests { // Use 4 more bytes of the storage for the same item, which requires 4 Balance. // Should fail as DefaultDepositLimit is 3 and hence isn't enough. assert_err_ignore_postinfo!( - builder::call(addr.clone()) + builder::call(addr) .storage_deposit_limit(3) .data(5u32.to_le_bytes().to_vec()) .build(), @@ -3313,21 +3354,20 @@ mod run_tests { #[test] fn deposit_limit_in_nested_calls() { - let (wasm_caller, _code_hash_caller) = - compile_module::("create_storage_and_call").unwrap(); - let (wasm_callee, _code_hash_callee) = compile_module::("store_call").unwrap(); + let (wasm_caller, _code_hash_caller) = compile_module("create_storage_and_call").unwrap(); + let (wasm_callee, _code_hash_callee) = compile_module("store_call").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Create both contracts: Constructors do nothing. - let addr_caller = - builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_account_id(); - let addr_callee = - builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_account_id(); + let Contract { addr: addr_caller, .. } = + builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_contract(); + let Contract { addr: addr_callee, .. } = + builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_contract(); // Create 100 bytes of storage with a price of per byte // This is 100 Balance + 2 Balance for the item - assert_ok!(builder::call(addr_callee.clone()) + assert_ok!(builder::call(addr_callee) .storage_deposit_limit(102) .data(100u32.to_le_bytes().to_vec()) .build()); @@ -3338,9 +3378,9 @@ mod run_tests { // This should fail as the specified parent's limit is less than the cost: 13 < // 14. assert_err_ignore_postinfo!( - builder::call(addr_caller.clone()) + builder::call(addr_caller) .storage_deposit_limit(13) - .data((100u32, &addr_callee, 0u64).encode()) + .data((100u32, &addr_callee, U256::from(0u64)).encode()) .build(), >::StorageDepositLimitExhausted, ); @@ -3352,9 +3392,9 @@ mod run_tests { // This should fail as the specified parent's limit is less than the cost: 14 // < 15. assert_err_ignore_postinfo!( - builder::call(addr_caller.clone()) + builder::call(addr_caller) .storage_deposit_limit(14) - .data((101u32, &addr_callee, 0u64).encode()) + .data((101u32, &addr_callee, U256::from(0u64)).encode()) .build(), >::StorageDepositLimitExhausted, ); @@ -3365,9 +3405,9 @@ mod run_tests { // that the nested call should have a deposit limit of at least 2 Balance. The // sub-call should be rolled back, which is covered by the next test case. assert_err_ignore_postinfo!( - builder::call(addr_caller.clone()) + builder::call(addr_caller) .storage_deposit_limit(16) - .data((102u32, &addr_callee, 1u64).encode()) + .data((102u32, &addr_callee, U256::from(1u64)).encode()) .build(), >::StorageDepositLimitExhausted, ); @@ -3376,9 +3416,9 @@ mod run_tests { // caller. Note that if previous sub-call wouldn't roll back, this call would pass // making the test case fail. We don't set a special limit for the nested call here. assert_err_ignore_postinfo!( - builder::call(addr_caller.clone()) + builder::call(addr_caller) .storage_deposit_limit(0) - .data((87u32, &addr_callee, 0u64).encode()) + .data((87u32, &addr_callee, U256::from(0u64)).encode()) .build(), >::StorageDepositLimitExhausted, ); @@ -3388,8 +3428,8 @@ mod run_tests { // Require more than the sender's balance. // We don't set a special limit for the nested call. assert_err_ignore_postinfo!( - builder::call(addr_caller.clone()) - .data((512u32, &addr_callee, 1u64).encode()) + builder::call(addr_caller) + .data((512u32, &addr_callee, U256::from(1u64)).encode()) .build(), >::StorageDepositLimitExhausted, ); @@ -3397,9 +3437,9 @@ mod run_tests { // Same as above but allow for the additional deposit of 1 Balance in parent. // We set the special deposit limit of 1 Balance for the nested call, which isn't // enforced as callee frees up storage. This should pass. - assert_ok!(builder::call(addr_caller.clone()) + assert_ok!(builder::call(addr_caller) .storage_deposit_limit(1) - .data((87u32, &addr_callee, 1u64).encode()) + .data((87u32, &addr_callee, U256::from(1u64)).encode()) .build()); }); } @@ -3407,20 +3447,21 @@ mod run_tests { #[test] fn deposit_limit_in_nested_instantiate() { let (wasm_caller, _code_hash_caller) = - compile_module::("create_storage_and_instantiate").unwrap(); - let (wasm_callee, code_hash_callee) = compile_module::("store_deploy").unwrap(); + compile_module("create_storage_and_instantiate").unwrap(); + let (wasm_callee, code_hash_callee) = compile_module("store_deploy").unwrap(); const ED: u64 = 5; ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); let _ = ::Currency::set_balance(&BOB, 1_000_000); // Create caller contract - let addr_caller = builder::bare_instantiate(Code::Upload(wasm_caller)) - .value(10_000u64) // this balance is later passed to the deployed contract - .build_and_unwrap_account_id(); + let Contract { addr: addr_caller, account_id: caller_id } = + builder::bare_instantiate(Code::Upload(wasm_caller)) + .value(10_000u64) // this balance is later passed to the deployed contract + .build_and_unwrap_contract(); // Deploy a contract to get its occupied storage size - let addr = builder::bare_instantiate(Code::Upload(wasm_callee)) + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(wasm_callee)) .data(vec![0, 0, 0, 0]) - .build_and_unwrap_account_id(); + .build_and_unwrap_contract(); let callee_info_len = ContractInfoOf::::get(&addr).unwrap().encoded_size() as u64; @@ -3436,10 +3477,10 @@ mod run_tests { // Provided the limit is set to be 1 Balance less, // this call should fail on the return from the caller contract. assert_err_ignore_postinfo!( - builder::call(addr_caller.clone()) + builder::call(addr_caller) .origin(RuntimeOrigin::signed(BOB)) .storage_deposit_limit(callee_info_len + 2 + ED + 1) - .data((0u32, &code_hash_callee, 0u64).encode()) + .data((0u32, &code_hash_callee, U256::from(0u64)).encode()) .build(), >::StorageDepositLimitExhausted, ); @@ -3450,10 +3491,10 @@ mod run_tests { // byte in the constructor. Hence +1 Balance to the limit is needed. This should fail on // the return from constructor. assert_err_ignore_postinfo!( - builder::call(addr_caller.clone()) + builder::call(addr_caller) .origin(RuntimeOrigin::signed(BOB)) .storage_deposit_limit(callee_info_len + 2 + ED + 2) - .data((1u32, &code_hash_callee, 0u64).encode()) + .data((1u32, &code_hash_callee, U256::from(0u64)).encode()) .build(), >::StorageDepositLimitExhausted, ); @@ -3464,10 +3505,13 @@ mod run_tests { // instantiate. This should fail during the charging for the instantiation in // `RawMeter::charge_instantiate()` assert_err_ignore_postinfo!( - builder::call(addr_caller.clone()) + builder::call(addr_caller) .origin(RuntimeOrigin::signed(BOB)) .storage_deposit_limit(callee_info_len + 2 + ED + 2) - .data((0u32, &code_hash_callee, callee_info_len + 2 + ED + 1).encode()) + .data( + (0u32, &code_hash_callee, U256::from(callee_info_len + 2 + ED + 1)) + .encode() + ) .build(), >::StorageDepositLimitExhausted, ); @@ -3479,10 +3523,13 @@ mod run_tests { // Now we set enough limit for the parent call, but insufficient limit for child // instantiate. This should fail right after the constructor execution. assert_err_ignore_postinfo!( - builder::call(addr_caller.clone()) + builder::call(addr_caller) .origin(RuntimeOrigin::signed(BOB)) .storage_deposit_limit(callee_info_len + 2 + ED + 3) // enough parent limit - .data((1u32, &code_hash_callee, callee_info_len + 2 + ED + 2).encode()) + .data( + (1u32, &code_hash_callee, U256::from(callee_info_len + 2 + ED + 2)) + .encode() + ) .build(), >::StorageDepositLimitExhausted, ); @@ -3490,25 +3537,30 @@ mod run_tests { assert_eq!(::Currency::free_balance(&BOB), 1_000_000); // Set enough deposit limit for the child instantiate. This should succeed. - let result = builder::bare_call(addr_caller.clone()) + let result = builder::bare_call(addr_caller) .origin(RuntimeOrigin::signed(BOB)) - .storage_deposit_limit(callee_info_len + 2 + ED + 4) - .data((1u32, &code_hash_callee, callee_info_len + 2 + ED + 3).encode()) + .storage_deposit_limit(callee_info_len + 2 + ED + 4 + 2) + .data( + (1u32, &code_hash_callee, U256::from(callee_info_len + 2 + ED + 3 + 2)) + .encode(), + ) .build(); let returned = result.result.unwrap(); // All balance of the caller except ED has been transferred to the callee. // No deposit has been taken from it. - assert_eq!(::Currency::free_balance(&addr_caller), ED); + assert_eq!(::Currency::free_balance(&caller_id), ED); // Get address of the deployed contract. - let addr_callee = AccountId32::from_slice(&returned.data[0..32]).unwrap(); + let addr_callee = H160::from_slice(&returned.data[0..20]); + let callee_account_id = ::AddressMapper::to_account_id(&addr_callee); // 10_000 should be sent to callee from the caller contract, plus ED to be sent from the // origin. - assert_eq!(::Currency::free_balance(&addr_callee), 10_000 + ED); + assert_eq!(::Currency::free_balance(&callee_account_id), 10_000 + ED); // The origin should be charged with: // - callee instantiation deposit = (callee_info_len + 2) // - callee account ED // - for writing an item of 1 byte to storage = 3 Balance + // - Immutable data storage item deposit assert_eq!( ::Currency::free_balance(&BOB), 1_000_000 - (callee_info_len + 2 + ED + 3) @@ -3520,7 +3572,7 @@ mod run_tests { #[test] fn deposit_limit_honors_liquidity_restrictions() { - let (wasm, _code_hash) = compile_module::("store_call").unwrap(); + let (wasm, _code_hash) = compile_module("store_call").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let bobs_balance = 1_000; let _ = ::Currency::set_balance(&ALICE, 1_000_000); @@ -3528,13 +3580,14 @@ mod run_tests { let min_balance = Contracts::min_balance(); // Instantiate the BOB contract. - let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id(); + let Contract { addr, account_id } = + builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract(); let info_deposit = test_utils::contract_info_storage_deposit(&addr); // Check that the contract has been instantiated and has the minimum balance assert_eq!(get_contract(&addr).total_deposit(), info_deposit); assert_eq!( - ::Currency::total_balance(&addr), + ::Currency::total_balance(&account_id), info_deposit + min_balance ); @@ -3546,7 +3599,7 @@ mod run_tests { ) .unwrap(); assert_err_ignore_postinfo!( - builder::call(addr.clone()) + builder::call(addr) .origin(RuntimeOrigin::signed(BOB)) .storage_deposit_limit(10_000) .data(100u32.to_le_bytes().to_vec()) @@ -3559,27 +3612,28 @@ mod run_tests { #[test] fn deposit_limit_honors_existential_deposit() { - let (wasm, _code_hash) = compile_module::("store_call").unwrap(); + let (wasm, _code_hash) = compile_module("store_call").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); let _ = ::Currency::set_balance(&BOB, 300); let min_balance = Contracts::min_balance(); // Instantiate the BOB contract. - let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id(); + let Contract { addr, account_id } = + builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract(); let info_deposit = test_utils::contract_info_storage_deposit(&addr); // Check that the contract has been instantiated and has the minimum balance assert_eq!(get_contract(&addr).total_deposit(), info_deposit); assert_eq!( - ::Currency::total_balance(&addr), + ::Currency::total_balance(&account_id), min_balance + info_deposit ); // check that the deposit can't bring the account below the existential deposit assert_err_ignore_postinfo!( - builder::call(addr.clone()) + builder::call(addr) .origin(RuntimeOrigin::signed(BOB)) .storage_deposit_limit(10_000) .data(100u32.to_le_bytes().to_vec()) @@ -3592,14 +3646,15 @@ mod run_tests { #[test] fn deposit_limit_honors_min_leftover() { - let (wasm, _code_hash) = compile_module::("store_call").unwrap(); + let (wasm, _code_hash) = compile_module("store_call").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); let _ = ::Currency::set_balance(&BOB, 1_000); let min_balance = Contracts::min_balance(); // Instantiate the BOB contract. - let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id(); + let Contract { addr, account_id } = + builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract(); let info_deposit = test_utils::contract_info_storage_deposit(&addr); @@ -3607,7 +3662,7 @@ mod run_tests { // storage deposit assert_eq!(get_contract(&addr).total_deposit(), info_deposit); assert_eq!( - ::Currency::total_balance(&addr), + ::Currency::total_balance(&account_id), info_deposit + min_balance ); @@ -3616,7 +3671,7 @@ mod run_tests { // 50 for the storage deposit. Which is not enough to store the 50 bytes // as we also need 2 bytes for the item assert_err_ignore_postinfo!( - builder::call(addr.clone()) + builder::call(addr) .origin(RuntimeOrigin::signed(BOB)) .value(750) .storage_deposit_limit(10_000) @@ -3633,13 +3688,12 @@ mod run_tests { // set hash lock up deposit to 30%, to test deposit calculation. CODE_HASH_LOCKUP_DEPOSIT_PERCENT.with(|c| *c.borrow_mut() = Perbill::from_percent(30)); - let (wasm_caller, self_code_hash) = - compile_module::("locking_delegate_dependency").unwrap(); + let (wasm_caller, self_code_hash) = compile_module("locking_delegate_dependency").unwrap(); let callee_codes: Vec<_> = (0..limits::DELEGATE_DEPENDENCIES + 1).map(|idx| dummy_unique(idx)).collect(); let callee_hashes: Vec<_> = callee_codes .iter() - .map(|c| ::Hashing::hash(c)) + .map(|c| sp_core::H256(sp_io::hashing::keccak_256(c))) .collect(); // Define inputs with various actions to test locking / unlocking delegate_dependencies. @@ -3652,17 +3706,21 @@ mod run_tests { // Instantiate the caller contract with the given input. let instantiate = |input: &(u32, H256)| { builder::bare_instantiate(Code::Upload(wasm_caller.clone())) + .origin(RuntimeOrigin::signed(ETH_ALICE)) .data(input.encode()) .build() }; // Call contract with the given input. - let call = |addr_caller: &AccountId32, input: &(u32, H256)| { - builder::bare_call(addr_caller.clone()).data(input.encode()).build() + let call = |addr_caller: &H160, input: &(u32, H256)| { + builder::bare_call(*addr_caller) + .origin(RuntimeOrigin::signed(ETH_ALICE)) + .data(input.encode()) + .build() }; const ED: u64 = 2000; ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { - let _ = Balances::set_balance(&ALICE, 1_000_000); + let _ = Balances::set_balance(Ð_ALICE, 1_000_000); // Instantiate with lock_delegate_dependency should fail since the code is not yet on // chain. @@ -3676,7 +3734,7 @@ mod run_tests { for code in callee_codes.iter() { let CodeUploadReturnValue { deposit: deposit_per_code, .. } = Contracts::bare_upload_code( - RuntimeOrigin::signed(ALICE), + RuntimeOrigin::signed(ETH_ALICE), code.clone(), deposit_limit::(), ) @@ -3685,8 +3743,8 @@ mod run_tests { } // Instantiate should now work. - let addr_caller = - instantiate(&lock_delegate_dependency_input).result.unwrap().account_id; + let addr_caller = instantiate(&lock_delegate_dependency_input).result.unwrap().addr; + let caller_account_id = ::AddressMapper::to_account_id(&addr_caller); // There should be a dependency and a deposit. let contract = test_utils::get_contract(&addr_caller); @@ -3699,14 +3757,14 @@ mod run_tests { assert_eq!( test_utils::get_balance_on_hold( &HoldReason::StorageDepositReserve.into(), - &addr_caller + &caller_account_id ), dependency_deposit + contract.storage_base_deposit() ); // Removing the code should fail, since we have added a dependency. assert_err!( - Contracts::remove_code(RuntimeOrigin::signed(ALICE), callee_hashes[0]), + Contracts::remove_code(RuntimeOrigin::signed(ETH_ALICE), callee_hashes[0]), >::CodeInUse ); @@ -3742,7 +3800,7 @@ mod run_tests { assert_eq!( test_utils::get_balance_on_hold( &HoldReason::StorageDepositReserve.into(), - &addr_caller + &caller_account_id ), contract.storage_base_deposit() ); @@ -3755,7 +3813,7 @@ mod run_tests { // Locking a dependency with a storage limit too low should fail. assert_err!( - builder::bare_call(addr_caller.clone()) + builder::bare_call(addr_caller) .storage_deposit_limit(dependency_deposit - 1) .data(lock_delegate_dependency_input.encode()) .build() @@ -3764,14 +3822,14 @@ mod run_tests { ); // Since we unlocked the dependency we should now be able to remove the code. - assert_ok!(Contracts::remove_code(RuntimeOrigin::signed(ALICE), callee_hashes[0])); + assert_ok!(Contracts::remove_code(RuntimeOrigin::signed(ETH_ALICE), callee_hashes[0])); // Calling should fail since the delegated contract is not on chain anymore. assert_err!(call(&addr_caller, &noop_input).result, Error::::ContractTrapped); // Add the dependency back. Contracts::upload_code( - RuntimeOrigin::signed(ALICE), + RuntimeOrigin::signed(ETH_ALICE), callee_codes[0].clone(), deposit_limit::(), ) @@ -3779,22 +3837,22 @@ mod run_tests { call(&addr_caller, &lock_delegate_dependency_input).result.unwrap(); // Call terminate should work, and return the deposit. - let balance_before = test_utils::get_balance(&ALICE); + let balance_before = test_utils::get_balance(Ð_ALICE); assert_ok!(call(&addr_caller, &terminate_input).result); assert_eq!( - test_utils::get_balance(&ALICE), + test_utils::get_balance(Ð_ALICE), ED + balance_before + contract.storage_base_deposit() + dependency_deposit ); // Terminate should also remove the dependency, so we can remove the code. - assert_ok!(Contracts::remove_code(RuntimeOrigin::signed(ALICE), callee_hashes[0])); + assert_ok!(Contracts::remove_code(RuntimeOrigin::signed(ETH_ALICE), callee_hashes[0])); }); } #[test] fn native_dependency_deposit_works() { - let (wasm, code_hash) = compile_module::("set_code_hash").unwrap(); - let (dummy_wasm, dummy_code_hash) = compile_module::("dummy").unwrap(); + let (wasm, code_hash) = compile_module("set_code_hash").unwrap(); + let (dummy_wasm, dummy_code_hash) = compile_module("dummy").unwrap(); // Set hash lock up deposit to 30%, to test deposit calculation. CODE_HASH_LOCKUP_DEPOSIT_PERCENT.with(|c| *c.borrow_mut() = Perbill::from_percent(30)); @@ -3830,7 +3888,8 @@ mod run_tests { // Instantiate the set_code_hash contract. let res = builder::bare_instantiate(code).build(); - let addr = res.result.unwrap().account_id; + let addr = res.result.unwrap().addr; + let account_id = ::AddressMapper::to_account_id(&addr); let base_deposit = test_utils::contract_info_storage_deposit(&addr); let upload_deposit = test_utils::get_code_deposit(&code_hash); let extra_deposit = add_upload_deposit.then(|| upload_deposit).unwrap_or_default(); @@ -3846,7 +3905,7 @@ mod run_tests { ); // call set_code_hash - builder::bare_call(addr.clone()) + builder::bare_call(addr) .data(dummy_code_hash.encode()) .build_and_unwrap_result(); @@ -3858,7 +3917,7 @@ mod run_tests { assert_eq!( test_utils::get_balance_on_hold( &HoldReason::StorageDepositReserve.into(), - &addr + &account_id ), deposit ); @@ -3868,7 +3927,7 @@ mod run_tests { #[test] fn root_cannot_upload_code() { - let (wasm, _) = compile_module::("dummy").unwrap(); + let (wasm, _) = compile_module("dummy").unwrap(); ExtBuilder::default().build().execute_with(|| { assert_noop!( @@ -3880,7 +3939,7 @@ mod run_tests { #[test] fn root_cannot_remove_code() { - let (_, code_hash) = compile_module::("dummy").unwrap(); + let (_, code_hash) = compile_module("dummy").unwrap(); ExtBuilder::default().build().execute_with(|| { assert_noop!( @@ -3892,11 +3951,11 @@ mod run_tests { #[test] fn signed_cannot_set_code() { - let (_, code_hash) = compile_module::("dummy").unwrap(); + let (_, code_hash) = compile_module("dummy").unwrap(); ExtBuilder::default().build().execute_with(|| { assert_noop!( - Contracts::set_code(RuntimeOrigin::signed(ALICE), BOB, code_hash), + Contracts::set_code(RuntimeOrigin::signed(ALICE), BOB_ADDR, code_hash), DispatchError::BadOrigin, ); }); @@ -3906,7 +3965,7 @@ mod run_tests { fn none_cannot_call_code() { ExtBuilder::default().build().execute_with(|| { assert_err_ignore_postinfo!( - builder::call(BOB).origin(RuntimeOrigin::none()).build(), + builder::call(BOB_ADDR).origin(RuntimeOrigin::none()).build(), DispatchError::BadOrigin, ); }); @@ -3914,21 +3973,22 @@ mod run_tests { #[test] fn root_can_call() { - let (wasm, _) = compile_module::("dummy").unwrap(); + let (wasm, _) = compile_module("dummy").unwrap(); ExtBuilder::default().existential_deposit(100).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); - let addr = builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_account_id(); + let Contract { addr, .. } = + builder::bare_instantiate(Code::Upload(wasm)).build_and_unwrap_contract(); // Call the contract. - assert_ok!(builder::call(addr.clone()).origin(RuntimeOrigin::root()).build()); + assert_ok!(builder::call(addr).origin(RuntimeOrigin::root()).build()); }); } #[test] fn root_cannot_instantiate_with_code() { - let (wasm, _) = compile_module::("dummy").unwrap(); + let (wasm, _) = compile_module("dummy").unwrap(); ExtBuilder::default().build().execute_with(|| { assert_err_ignore_postinfo!( @@ -3940,7 +4000,7 @@ mod run_tests { #[test] fn root_cannot_instantiate() { - let (_, code_hash) = compile_module::("dummy").unwrap(); + let (_, code_hash) = compile_module("dummy").unwrap(); ExtBuilder::default().build().execute_with(|| { assert_err_ignore_postinfo!( @@ -3952,7 +4012,7 @@ mod run_tests { #[test] fn only_upload_origin_can_upload() { - let (wasm, _) = compile_module::("dummy").unwrap(); + let (wasm, _) = compile_module("dummy").unwrap(); UploadAccount::set(Some(ALICE)); ExtBuilder::default().build().execute_with(|| { let _ = Balances::set_balance(&ALICE, 1_000_000); @@ -3987,7 +4047,7 @@ mod run_tests { #[test] fn only_instantiation_origin_can_instantiate() { - let (code, code_hash) = compile_module::("dummy").unwrap(); + let (code, code_hash) = compile_module("dummy").unwrap(); InstantiateAccount::set(Some(ALICE)); ExtBuilder::default().build().execute_with(|| { let _ = Balances::set_balance(&ALICE, 1_000_000); @@ -4018,25 +4078,50 @@ mod run_tests { }); } + #[test] + fn balance_of_api() { + let (wasm, _code_hash) = compile_module("balance_of").unwrap(); + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = Balances::set_balance(&ALICE, 1_000_000); + let _ = Balances::set_balance(Ð_ALICE, 1_000_000); + + let Contract { addr, .. } = + builder::bare_instantiate(Code::Upload(wasm.to_vec())).build_and_unwrap_contract(); + + // The fixture asserts a non-zero returned free balance of the account; + // The ETH_ALICE account is endowed; + // Hence we should not revert + assert_ok!(builder::call(addr).data(ALICE_ADDR.0.to_vec()).build()); + + // The fixture asserts a non-zero returned free balance of the account; + // The ETH_BOB account is not endowed; + // Hence we should revert + assert_err_ignore_postinfo!( + builder::call(addr).data(BOB_ADDR.0.to_vec()).build(), + >::ContractTrapped + ); + }); + } + #[test] fn balance_api_returns_free_balance() { - let (wasm, _code_hash) = compile_module::("balance").unwrap(); + let (wasm, _code_hash) = compile_module("balance").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Instantiate the BOB contract without any extra balance. - let addr = builder::bare_instantiate(Code::Upload(wasm.to_vec())) - .build_and_unwrap_account_id(); + let Contract { addr, .. } = + builder::bare_instantiate(Code::Upload(wasm.to_vec())).build_and_unwrap_contract(); let value = 0; // Call BOB which makes it call the balance runtime API. // The contract code asserts that the returned balance is 0. - assert_ok!(builder::call(addr.clone()).value(value).build()); + assert_ok!(builder::call(addr).value(value).build()); let value = 1; // Calling with value will trap the contract. assert_err_ignore_postinfo!( - builder::call(addr.clone()).value(value).build(), + builder::call(addr).value(value).build(), >::ContractTrapped ); }); @@ -4044,17 +4129,18 @@ mod run_tests { #[test] fn gas_consumed_is_linear_for_nested_calls() { - let (code, _code_hash) = compile_module::("recurse").unwrap(); + let (code, _code_hash) = compile_module("recurse").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); - let addr = builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_account_id(); + let Contract { addr, .. } = + builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract(); let [gas_0, gas_1, gas_2, gas_max] = { [0u32, 1u32, 2u32, limits::CALL_STACK_DEPTH] .iter() .map(|i| { - let result = builder::bare_call(addr.clone()).data(i.encode()).build(); + let result = builder::bare_call(addr).data(i.encode()).build(); assert_ok!(result.result); result.gas_consumed }) @@ -4070,16 +4156,16 @@ mod run_tests { #[test] fn read_only_call_cannot_store() { - let (wasm_caller, _code_hash_caller) = compile_module::("read_only_call").unwrap(); - let (wasm_callee, _code_hash_callee) = compile_module::("store_call").unwrap(); + let (wasm_caller, _code_hash_caller) = compile_module("read_only_call").unwrap(); + let (wasm_callee, _code_hash_callee) = compile_module("store_call").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Create both contracts: Constructors do nothing. - let addr_caller = - builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_account_id(); - let addr_callee = - builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_account_id(); + let Contract { addr: addr_caller, .. } = + builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_contract(); + let Contract { addr: addr_callee, .. } = + builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_contract(); // Read-only call fails when modifying storage. assert_err_ignore_postinfo!( @@ -4091,17 +4177,16 @@ mod run_tests { #[test] fn read_only_call_cannot_transfer() { - let (wasm_caller, _code_hash_caller) = - compile_module::("call_with_flags_and_value").unwrap(); - let (wasm_callee, _code_hash_callee) = compile_module::("dummy").unwrap(); + let (wasm_caller, _code_hash_caller) = compile_module("call_with_flags_and_value").unwrap(); + let (wasm_callee, _code_hash_callee) = compile_module("dummy").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Create both contracts: Constructors do nothing. - let addr_caller = - builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_account_id(); - let addr_callee = - builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_account_id(); + let Contract { addr: addr_caller, .. } = + builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_contract(); + let Contract { addr: addr_callee, .. } = + builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_contract(); // Read-only call fails when a non-zero value is set. assert_err_ignore_postinfo!( @@ -4118,21 +4203,20 @@ mod run_tests { #[test] fn read_only_subsequent_call_cannot_store() { - let (wasm_read_only_caller, _code_hash_caller) = - compile_module::("read_only_call").unwrap(); - let (wasm_caller, _code_hash_caller) = - compile_module::("call_with_flags_and_value").unwrap(); - let (wasm_callee, _code_hash_callee) = compile_module::("store_call").unwrap(); + let (wasm_read_only_caller, _code_hash_caller) = compile_module("read_only_call").unwrap(); + let (wasm_caller, _code_hash_caller) = compile_module("call_with_flags_and_value").unwrap(); + let (wasm_callee, _code_hash_callee) = compile_module("store_call").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Create contracts: Constructors do nothing. - let addr_caller = builder::bare_instantiate(Code::Upload(wasm_read_only_caller)) - .build_and_unwrap_account_id(); - let addr_subsequent_caller = - builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_account_id(); - let addr_callee = - builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_account_id(); + let Contract { addr: addr_caller, .. } = + builder::bare_instantiate(Code::Upload(wasm_read_only_caller)) + .build_and_unwrap_contract(); + let Contract { addr: addr_subsequent_caller, .. } = + builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_contract(); + let Contract { addr: addr_callee, .. } = + builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_contract(); // Subsequent call input. let input = (&addr_callee, pallet_revive_uapi::CallFlags::empty().bits(), 0u64, 100u32); @@ -4149,18 +4233,213 @@ mod run_tests { #[test] fn read_only_call_works() { - let (wasm_caller, _code_hash_caller) = compile_module::("read_only_call").unwrap(); - let (wasm_callee, _code_hash_callee) = compile_module::("dummy").unwrap(); + let (wasm_caller, _code_hash_caller) = compile_module("read_only_call").unwrap(); + let (wasm_callee, _code_hash_callee) = compile_module("dummy").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Create both contracts: Constructors do nothing. - let addr_caller = - builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_account_id(); - let addr_callee = - builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_account_id(); + let Contract { addr: addr_caller, .. } = + builder::bare_instantiate(Code::Upload(wasm_caller)).build_and_unwrap_contract(); + let Contract { addr: addr_callee, .. } = + builder::bare_instantiate(Code::Upload(wasm_callee)).build_and_unwrap_contract(); + + assert_ok!(builder::call(addr_caller).data(addr_callee.encode()).build()); + }); + } + + #[test] + fn create1_with_value_works() { + let (code, code_hash) = compile_module("create1_with_value").unwrap(); + let value = 42; + ExtBuilder::default().existential_deposit(200).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Create the contract: Constructor does nothing. + let Contract { addr, .. } = + builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract(); + + // Call the contract: Deploys itself using create1 and the expected value + assert_ok!(builder::call(addr).value(value).data(code_hash.encode()).build()); + + // We should see the expected balance at the expected account + let address = crate::address::create1(&addr, 0); + let account_id = ::AddressMapper::to_account_id(&address); + let usable_balance = ::Currency::usable_balance(&account_id); + assert_eq!(usable_balance, value); + }); + } + + #[test] + fn static_data_limit_is_enforced() { + let (oom_rw_trailing, _) = compile_module("oom_rw_trailing").unwrap(); + let (oom_rw_included, _) = compile_module("oom_rw_included").unwrap(); + let (oom_ro, _) = compile_module("oom_ro").unwrap(); + + ExtBuilder::default().build().execute_with(|| { + let _ = Balances::set_balance(&ALICE, 1_000_000); + + assert_err!( + Contracts::upload_code( + RuntimeOrigin::signed(ALICE), + oom_rw_trailing, + deposit_limit::(), + ), + >::StaticMemoryTooLarge + ); + + assert_err!( + Contracts::upload_code( + RuntimeOrigin::signed(ALICE), + oom_rw_included, + deposit_limit::(), + ), + >::BlobTooLarge + ); + + assert_err!( + Contracts::upload_code( + RuntimeOrigin::signed(ALICE), + oom_ro, + deposit_limit::(), + ), + >::BlobTooLarge + ); + }); + } + + #[test] + fn call_diverging_out_len_works() { + let (code, _) = compile_module("call_diverging_out_len").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Create the contract: Constructor does nothing + let Contract { addr, .. } = + builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract(); + + // Call the contract: It will issue calls and deploys, asserting on + // correct output if the supplied output length was smaller than + // than what the callee returned. + assert_ok!(builder::call(addr).build()); + }); + } + + #[test] + fn chain_id_works() { + let (code, _) = compile_module("chain_id").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + let chain_id = U256::from(::ChainId::get()); + let received = builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_result(); + assert_eq!(received.result.data, chain_id.encode()); + }); + } + + #[test] + fn return_data_api_works() { + let (code_return_data_api, _) = compile_module("return_data_api").unwrap(); + let (code_return_with_data, hash_return_with_data) = + compile_module("return_with_data").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Upload the io echoing fixture for later use + assert_ok!(Contracts::upload_code( + RuntimeOrigin::signed(ALICE), + code_return_with_data, + deposit_limit::(), + )); + + // Create fixture: Constructor does nothing + let Contract { addr, .. } = + builder::bare_instantiate(Code::Upload(code_return_data_api)) + .build_and_unwrap_contract(); + + // Call the contract: It will issue calls and deploys, asserting on + assert_ok!(builder::call(addr) + .value(10 * 1024) + .data(hash_return_with_data.encode()) + .build()); + }); + } + + #[test] + fn immutable_data_works() { + let (code, _) = compile_module("immutable_data").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + let data = [0xfe; 8]; + + // Create fixture: Constructor sets the immtuable data + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code)) + .data(data.to_vec()) + .build_and_unwrap_contract(); + + // Storing immmutable data charges storage deposit; verify it explicitly. + assert_eq!( + test_utils::get_balance_on_hold( + &HoldReason::StorageDepositReserve.into(), + &::AddressMapper::to_account_id(&addr) + ), + test_utils::contract_info_storage_deposit(&addr) + ); + assert_eq!(test_utils::get_contract(&addr).immutable_data_len(), data.len() as u32); + + // Call the contract: Asserts the input to equal the immutable data + assert_ok!(builder::call(addr).data(data.to_vec()).build()); + }); + } + + #[test] + fn sbrk_cannot_be_deployed() { + let (code, _) = compile_module("sbrk").unwrap(); + + ExtBuilder::default().build().execute_with(|| { + let _ = Balances::set_balance(&ALICE, 1_000_000); + + assert_err!( + Contracts::upload_code( + RuntimeOrigin::signed(ALICE), + code.clone(), + deposit_limit::(), + ), + >::InvalidInstruction + ); - assert_ok!(builder::call(addr_caller.clone()).data(addr_callee.encode()).build()); + assert_err!( + builder::bare_instantiate(Code::Upload(code)).build().result, + >::InvalidInstruction + ); + }); + } + + #[test] + fn overweight_basic_block_cannot_be_deployed() { + let (code, _) = compile_module("basic_block").unwrap(); + + ExtBuilder::default().build().execute_with(|| { + let _ = Balances::set_balance(&ALICE, 1_000_000); + + assert_err!( + Contracts::upload_code( + RuntimeOrigin::signed(ALICE), + code.clone(), + deposit_limit::(), + ), + >::BasicBlockTooLarge + ); + + assert_err!( + builder::bare_instantiate(Code::Upload(code)).build().result, + >::BasicBlockTooLarge + ); }); } } diff --git a/substrate/frame/revive/src/tests/test_debug.rs b/substrate/frame/revive/src/tests/test_debug.rs index 166a0a8606a69c98fb38ea657d98bbf9e051c217..1e94d5cafb81344793ee19602d0c6c16bb818b05 100644 --- a/substrate/frame/revive/src/tests/test_debug.rs +++ b/substrate/frame/revive/src/tests/test_debug.rs @@ -16,18 +16,19 @@ // limitations under the License. use super::*; + use crate::{ debug::{CallInterceptor, CallSpan, ExecResult, ExportedFunction, Tracing}, primitives::ExecReturnValue, test_utils::*, - AccountIdOf, }; use frame_support::traits::Currency; +use sp_core::H160; use std::cell::RefCell; #[derive(Clone, PartialEq, Eq, Debug)] struct DebugFrame { - contract_account: AccountId32, + contract_address: sp_core::H160, call: ExportedFunction, input: Vec, result: Option>, @@ -35,12 +36,12 @@ struct DebugFrame { thread_local! { static DEBUG_EXECUTION_TRACE: RefCell> = RefCell::new(Vec::new()); - static INTERCEPTED_ADDRESS: RefCell> = RefCell::new(None); + static INTERCEPTED_ADDRESS: RefCell> = RefCell::new(None); } pub struct TestDebug; pub struct TestCallSpan { - contract_account: AccountId32, + contract_address: sp_core::H160, call: ExportedFunction, input: Vec, } @@ -49,20 +50,20 @@ impl Tracing for TestDebug { type CallSpan = TestCallSpan; fn new_call_span( - contract_account: &AccountIdOf, + contract_address: &crate::H160, entry_point: ExportedFunction, input_data: &[u8], ) -> TestCallSpan { DEBUG_EXECUTION_TRACE.with(|d| { d.borrow_mut().push(DebugFrame { - contract_account: contract_account.clone(), + contract_address: *contract_address, call: entry_point, input: input_data.to_vec(), result: None, }) }); TestCallSpan { - contract_account: contract_account.clone(), + contract_address: *contract_address, call: entry_point, input: input_data.to_vec(), } @@ -71,7 +72,7 @@ impl Tracing for TestDebug { impl CallInterceptor for TestDebug { fn intercept_call( - contract_address: &::AccountId, + contract_address: &sp_core::H160, _entry_point: ExportedFunction, _input_data: &[u8], ) -> Option { @@ -89,7 +90,7 @@ impl CallSpan for TestCallSpan { fn after_call(self, output: &ExecReturnValue) { DEBUG_EXECUTION_TRACE.with(|d| { d.borrow_mut().push(DebugFrame { - contract_account: self.contract_account, + contract_address: self.contract_address, call: self.call, input: self.input, result: Some(output.data.clone()), @@ -106,14 +107,14 @@ mod run_tests { #[test] fn debugging_works() { - let (wasm_caller, _) = compile_module::("call").unwrap(); - let (wasm_callee, _) = compile_module::("store_call").unwrap(); + let (wasm_caller, _) = compile_module("call").unwrap(); + let (wasm_callee, _) = compile_module("store_call").unwrap(); fn current_stack() -> Vec { DEBUG_EXECUTION_TRACE.with(|stack| stack.borrow().clone()) } - fn deploy(wasm: Vec) -> AccountId32 { + fn deploy(wasm: Vec) -> H160 { Contracts::bare_instantiate( RuntimeOrigin::signed(ALICE), 0, @@ -121,27 +122,27 @@ mod run_tests { deposit_limit::(), Code::Upload(wasm), vec![], - vec![], + Some([0u8; 32]), DebugInfo::Skip, CollectEvents::Skip, ) .result .unwrap() - .account_id + .addr } - fn constructor_frame(contract_account: &AccountId32, after: bool) -> DebugFrame { + fn constructor_frame(contract_address: &H160, after: bool) -> DebugFrame { DebugFrame { - contract_account: contract_account.clone(), + contract_address: *contract_address, call: ExportedFunction::Constructor, input: vec![], result: if after { Some(vec![]) } else { None }, } } - fn call_frame(contract_account: &AccountId32, args: Vec, after: bool) -> DebugFrame { + fn call_frame(contract_address: &H160, args: Vec, after: bool) -> DebugFrame { DebugFrame { - contract_account: contract_account.clone(), + contract_address: *contract_address, call: ExportedFunction::Call, input: args, result: if after { Some(vec![]) } else { None }, @@ -171,7 +172,7 @@ mod run_tests { assert_ok!(Contracts::call( RuntimeOrigin::signed(ALICE), - addr_caller.clone(), + addr_caller, 0, GAS_LIMIT, deposit_limit::(), @@ -193,7 +194,7 @@ mod run_tests { #[test] fn call_interception_works() { - let (wasm, _) = compile_module::("dummy").unwrap(); + let (wasm, _) = compile_module("dummy").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = Balances::deposit_creating(&ALICE, 1_000_000); @@ -206,18 +207,18 @@ mod run_tests { Code::Upload(wasm), vec![], // some salt to ensure that the address of this contract is unique among all tests - vec![0x41, 0x41, 0x41, 0x41], + Some([0x41; 32]), DebugInfo::Skip, CollectEvents::Skip, ) .result .unwrap() - .account_id; + .addr; // no interception yet assert_ok!(Contracts::call( RuntimeOrigin::signed(ALICE), - account_id.clone(), + account_id, 0, GAS_LIMIT, deposit_limit::(), @@ -225,7 +226,7 @@ mod run_tests { )); // intercept calls to this contract - INTERCEPTED_ADDRESS.with(|i| *i.borrow_mut() = Some(account_id.clone())); + INTERCEPTED_ADDRESS.with(|i| *i.borrow_mut() = Some(account_id)); assert_err_ignore_postinfo!( Contracts::call( diff --git a/substrate/frame/revive/src/wasm/mod.rs b/substrate/frame/revive/src/wasm/mod.rs index 784993ca793d969a18b988dc6ea2a2f73ec1e6d3..e2256d7dcea77a384268c054803ab92f24c41de0 100644 --- a/substrate/frame/revive/src/wasm/mod.rs +++ b/substrate/frame/revive/src/wasm/mod.rs @@ -32,12 +32,14 @@ pub use crate::wasm::runtime::{ReturnData, TrapReason}; pub use crate::wasm::runtime::{ApiVersion, Memory, Runtime, RuntimeCosts}; use crate::{ + address::AddressMapper, exec::{ExecResult, Executable, ExportedFunction, Ext}, gas::{GasMeter, Token}, + limits, storage::meter::Diff, weights::WeightInfo, - AccountIdOf, BadOrigin, BalanceOf, CodeHash, CodeInfoOf, CodeVec, Config, Error, Event, - ExecError, HoldReason, Pallet, PristineCode, Weight, API_VERSION, LOG_TARGET, + AccountIdOf, BadOrigin, BalanceOf, CodeInfoOf, CodeVec, Config, Error, Event, ExecError, + HoldReason, Pallet, PristineCode, Weight, API_VERSION, LOG_TARGET, }; use alloc::vec::Vec; use codec::{Decode, Encode, MaxEncodedLen}; @@ -46,8 +48,8 @@ use frame_support::{ ensure, traits::{fungible::MutateHold, tokens::Precision::BestEffort}, }; -use sp_core::Get; -use sp_runtime::{traits::Hash, DispatchError}; +use sp_core::{Get, U256}; +use sp_runtime::DispatchError; /// Validated Wasm module ready for execution. /// This data structure is immutable once created and stored. @@ -55,13 +57,13 @@ use sp_runtime::{traits::Hash, DispatchError}; #[codec(mel_bound())] #[scale_info(skip_type_params(T))] pub struct WasmBlob { - code: CodeVec, + code: CodeVec, // This isn't needed for contract execution and is not stored alongside it. #[codec(skip)] code_info: CodeInfo, // This is for not calculating the hash every time we need it. #[codec(skip)] - code_hash: CodeHash, + code_hash: sp_core::H256, } /// Contract code related data, such as: @@ -122,14 +124,16 @@ impl Token for CodeLoadToken { } } -impl WasmBlob { +impl WasmBlob +where + BalanceOf: Into + TryFrom, +{ /// We only check for size and nothing else when the code is uploaded. - pub fn from_code( - code: Vec, - owner: AccountIdOf, - ) -> Result { - let code: CodeVec = - code.try_into().map_err(|_| (>::CodeTooLarge.into(), ""))?; + pub fn from_code(code: Vec, owner: AccountIdOf) -> Result { + // We do size checks when new code is deployed. This allows us to increase + // the limits later without affecting already deployed code. + let code = limits::code::enforce::(code)?; + let code_len = code.len() as u32; let bytes_added = code_len.saturating_add(>::max_encoded_len() as u32); let deposit = Diff { bytes_added, items_added: 2, ..Default::default() } @@ -143,14 +147,14 @@ impl WasmBlob { api_version: API_VERSION, behaviour_version: Default::default(), }; - let code_hash = T::Hashing::hash(&code); + let code_hash = sp_core::H256(sp_io::hashing::keccak_256(&code)); Ok(WasmBlob { code, code_info, code_hash }) } /// Remove the code from storage and refund the deposit to its owner. /// /// Applies all necessary checks before removing the code. - pub fn remove(origin: &T::AccountId, code_hash: CodeHash) -> DispatchResult { + pub fn remove(origin: &T::AccountId, code_hash: sp_core::H256) -> DispatchResult { >::try_mutate_exists(&code_hash, |existing| { if let Some(code_info) = existing { ensure!(code_info.refcount == 0, >::CodeInUse); @@ -162,7 +166,7 @@ impl WasmBlob { BestEffort, ); let deposit_released = code_info.deposit; - let remover = code_info.owner.clone(); + let remover = T::AddressMapper::to_address(&code_info.owner); *existing = None; >::remove(&code_hash); @@ -201,10 +205,11 @@ impl WasmBlob { self.code_info.refcount = 0; >::insert(code_hash, &self.code); *stored_code_info = Some(self.code_info.clone()); + let uploader = T::AddressMapper::to_address(&self.code_info.owner); >::deposit_event(Event::CodeStored { code_hash, deposit_held: deposit, - uploader: self.code_info.owner.clone(), + uploader, }); Ok(deposit) }, @@ -249,7 +254,11 @@ pub struct PreparedCall<'a, E: Ext> { api_version: ApiVersion, } -impl<'a, E: Ext> PreparedCall<'a, E> { +impl<'a, E: Ext> PreparedCall<'a, E> +where + BalanceOf: Into, + BalanceOf: TryFrom, +{ pub fn call(mut self) -> ExecResult { let exec_result = loop { let interrupt = self.instance.run(); @@ -274,16 +283,17 @@ impl WasmBlob { entry_point: ExportedFunction, api_version: ApiVersion, ) -> Result, ExecError> { - let code = self.code.as_slice(); - let mut config = polkavm::Config::default(); config.set_backend(Some(polkavm::BackendKind::Interpreter)); let engine = polkavm::Engine::new(&config).expect("interpreter is available on all plattforms; qed"); let mut module_config = polkavm::ModuleConfig::new(); + module_config.set_page_size(limits::PAGE_SIZE); module_config.set_gas_metering(Some(polkavm::GasMeteringKind::Sync)); - let module = polkavm::Module::new(&engine, &module_config, code.into()).map_err(|err| { + module_config.set_allow_sbrk(false); + let module = polkavm::Module::new(&engine, &module_config, self.code.into_inner().into()) + .map_err(|err| { log::debug!(target: LOG_TARGET, "failed to create polkavm module: {err:?}"); Error::::CodeRejected })?; @@ -313,9 +323,12 @@ impl WasmBlob { } } -impl Executable for WasmBlob { +impl Executable for WasmBlob +where + BalanceOf: Into + TryFrom, +{ fn from_storage( - code_hash: CodeHash, + code_hash: sp_core::H256, gas_meter: &mut GasMeter, ) -> Result { let code_info = >::get(code_hash).ok_or(Error::::CodeNotFound)?; @@ -340,11 +353,15 @@ impl Executable for WasmBlob { prepared_call.call() } - fn code_info(&self) -> &CodeInfo { - &self.code_info + fn code(&self) -> &[u8] { + self.code.as_ref() } - fn code_hash(&self) -> &CodeHash { + fn code_hash(&self) -> &sp_core::H256 { &self.code_hash } + + fn code_info(&self) -> &CodeInfo { + &self.code_info + } } diff --git a/substrate/frame/revive/src/wasm/runtime.rs b/substrate/frame/revive/src/wasm/runtime.rs index de910e79e73e94a31a235a5d6a1765d6e2c0c9e9..245c91278a7f80343f592a8f7dec152ae12094db 100644 --- a/substrate/frame/revive/src/wasm/runtime.rs +++ b/substrate/frame/revive/src/wasm/runtime.rs @@ -18,30 +18,37 @@ //! Environment definition of the wasm smart-contract runtime. use crate::{ - exec::{ExecError, ExecResult, Ext, Key, TopicOf}, + address::AddressMapper, + exec::{ExecError, ExecResult, Ext, Key}, gas::{ChargedAmount, Token}, limits, primitives::ExecReturnValue, weights::WeightInfo, - BalanceOf, CodeHash, Config, Error, LOG_TARGET, SENTINEL, + Config, Error, LOG_TARGET, SENTINEL, }; use alloc::{boxed::Box, vec, vec::Vec}; use codec::{Decode, DecodeLimit, Encode, MaxEncodedLen}; -use core::{fmt, marker::PhantomData}; +use core::{fmt, marker::PhantomData, mem}; use frame_support::{ dispatch::DispatchInfo, ensure, pallet_prelude::DispatchResultWithPostInfo, parameter_types, traits::Get, weights::Weight, }; use pallet_revive_proc_macro::define_env; use pallet_revive_uapi::{CallFlags, ReturnErrorCode, ReturnFlags, StorageFlags}; +use sp_core::{H160, H256, U256}; use sp_io::hashing::{blake2_128, blake2_256, keccak_256, sha2_256}; -use sp_runtime::{traits::Zero, DispatchError, RuntimeDebug}; +use sp_runtime::{DispatchError, RuntimeDebug}; type CallOf = ::RuntimeCall; /// The maximum nesting depth a contract can use when encoding types. const MAX_DECODE_NESTING: u32 = 256; +/// Encode a `U256` into a 32 byte buffer. +fn as_bytes(u: U256) -> [u8; 32] { + u.to_little_endian() +} + #[derive(Clone, Copy)] pub enum ApiVersion { /// Expose all APIs even unversioned ones. Only used for testing and benchmarking. @@ -82,6 +89,32 @@ pub trait Memory { Ok(buf) } + /// Same as `read` but reads into a fixed size buffer. + fn read_array(&self, ptr: u32) -> Result<[u8; N], DispatchError> { + let mut buf = [0u8; N]; + self.read_into_buf(ptr, &mut buf)?; + Ok(buf) + } + + /// Read a `u32` from the sandbox memory. + fn read_u32(&self, ptr: u32) -> Result { + let buf: [u8; 4] = self.read_array(ptr)?; + Ok(u32::from_le_bytes(buf)) + } + + /// Read a `U256` from the sandbox memory. + fn read_u256(&self, ptr: u32) -> Result { + let buf: [u8; 32] = self.read_array(ptr)?; + Ok(U256::from_little_endian(&buf)) + } + + /// Read a `H256` from the sandbox memory. + fn read_h256(&self, ptr: u32) -> Result { + let mut code_hash = H256::default(); + self.read_into_buf(ptr, code_hash.as_bytes_mut())?; + Ok(code_hash) + } + /// Read designated chunk from the sandbox memory and attempt to decode into the specified type. /// /// Returns `Err` if one of the following conditions occurs: @@ -202,8 +235,8 @@ parameter_types! { const XcmExecutionFailed: ReturnErrorCode = ReturnErrorCode::XcmExecutionFailed; } -impl From for ReturnErrorCode { - fn from(from: ExecReturnValue) -> Self { +impl From<&ExecReturnValue> for ReturnErrorCode { + fn from(from: &ExecReturnValue) -> Self { if from.flags.contains(ReturnFlags::REVERT) { Self::CalleeReverted } else { @@ -275,10 +308,12 @@ pub enum RuntimeCosts { CallerIsRoot, /// Weight of calling `seal_address`. Address, - /// Weight of calling `seal_gas_left`. - GasLeft, + /// Weight of calling `seal_weight_left`. + WeightLeft, /// Weight of calling `seal_balance`. Balance, + /// Weight of calling `seal_balance_of`. + BalanceOf, /// Weight of calling `seal_value_transferred`. ValueTransferred, /// Weight of calling `seal_minimum_balance`. @@ -325,8 +360,8 @@ pub enum RuntimeCosts { CallTransferSurcharge, /// Weight per byte that is cloned by supplying the `CLONE_INPUT` flag. CallInputCloned(u32), - /// Weight of calling `seal_instantiate` for the given input length and salt. - Instantiate { input_data_len: u32, salt_len: u32 }, + /// Weight of calling `seal_instantiate` for the given input lenth. + Instantiate { input_data_len: u32 }, /// Weight of calling `seal_hash_sha_256` for the given input size. HashSha256(u32), /// Weight of calling `seal_hash_keccak_256` for the given input size. @@ -353,6 +388,10 @@ pub enum RuntimeCosts { LockDelegateDependency, /// Weight of calling `unlock_delegate_dependency` UnlockDelegateDependency, + /// Weight of calling `get_immutable_dependency` + GetImmutableData(u32), + /// Weight of calling `set_immutable_dependency` + SetImmutableData(u32), } /// For functions that modify storage, benchmarks are performed with one item in the @@ -420,8 +459,9 @@ impl Token for RuntimeCosts { CallerIsOrigin => T::WeightInfo::seal_caller_is_origin(), CallerIsRoot => T::WeightInfo::seal_caller_is_root(), Address => T::WeightInfo::seal_address(), - GasLeft => T::WeightInfo::seal_gas_left(), + WeightLeft => T::WeightInfo::seal_weight_left(), Balance => T::WeightInfo::seal_balance(), + BalanceOf => T::WeightInfo::seal_balance_of(), ValueTransferred => T::WeightInfo::seal_value_transferred(), MinimumBalance => T::WeightInfo::seal_minimum_balance(), BlockNumber => T::WeightInfo::seal_block_number(), @@ -430,29 +470,34 @@ impl Token for RuntimeCosts { Terminate(locked_dependencies) => T::WeightInfo::seal_terminate(locked_dependencies), DepositEvent { num_topic, len } => T::WeightInfo::seal_deposit_event(num_topic, len), DebugMessage(len) => T::WeightInfo::seal_debug_message(len), - SetStorage { new_bytes, old_bytes } => - cost_storage!(write, seal_set_storage, new_bytes, old_bytes), + SetStorage { new_bytes, old_bytes } => { + cost_storage!(write, seal_set_storage, new_bytes, old_bytes) + }, ClearStorage(len) => cost_storage!(write, seal_clear_storage, len), ContainsStorage(len) => cost_storage!(read, seal_contains_storage, len), GetStorage(len) => cost_storage!(read, seal_get_storage, len), TakeStorage(len) => cost_storage!(write, seal_take_storage, len), - SetTransientStorage { new_bytes, old_bytes } => - cost_storage!(write_transient, seal_set_transient_storage, new_bytes, old_bytes), - ClearTransientStorage(len) => - cost_storage!(write_transient, seal_clear_transient_storage, len), - ContainsTransientStorage(len) => - cost_storage!(read_transient, seal_contains_transient_storage, len), - GetTransientStorage(len) => - cost_storage!(read_transient, seal_get_transient_storage, len), - TakeTransientStorage(len) => - cost_storage!(write_transient, seal_take_transient_storage, len), + SetTransientStorage { new_bytes, old_bytes } => { + cost_storage!(write_transient, seal_set_transient_storage, new_bytes, old_bytes) + }, + ClearTransientStorage(len) => { + cost_storage!(write_transient, seal_clear_transient_storage, len) + }, + ContainsTransientStorage(len) => { + cost_storage!(read_transient, seal_contains_transient_storage, len) + }, + GetTransientStorage(len) => { + cost_storage!(read_transient, seal_get_transient_storage, len) + }, + TakeTransientStorage(len) => { + cost_storage!(write_transient, seal_take_transient_storage, len) + }, Transfer => T::WeightInfo::seal_transfer(), CallBase => T::WeightInfo::seal_call(0, 0), DelegateCallBase => T::WeightInfo::seal_delegate_call(), CallTransferSurcharge => cost_args!(seal_call, 1, 0), CallInputCloned(len) => cost_args!(seal_call, 0, len), - Instantiate { input_data_len, salt_len } => - T::WeightInfo::seal_instantiate(input_data_len, salt_len), + Instantiate { input_data_len } => T::WeightInfo::seal_instantiate(input_data_len), HashSha256(len) => T::WeightInfo::seal_hash_sha2_256(len), HashKeccak256(len) => T::WeightInfo::seal_hash_keccak_256(len), HashBlake256(len) => T::WeightInfo::seal_hash_blake2_256(len), @@ -464,6 +509,8 @@ impl Token for RuntimeCosts { EcdsaToEthAddress => T::WeightInfo::seal_ecdsa_to_eth_address(), LockDelegateDependency => T::WeightInfo::lock_delegate_dependency(), UnlockDelegateDependency => T::WeightInfo::unlock_delegate_dependency(), + GetImmutableData(len) => T::WeightInfo::seal_get_immutable_data(len), + SetImmutableData(len) => T::WeightInfo::seal_set_immutable_data(len), } } } @@ -534,7 +581,7 @@ impl<'a, E: Ext, M: PolkaVmInstance> Runtime<'a, E, M> { Ok(Step) => None, Ok(Ecalli(idx)) => { let Some(syscall_symbol) = module.imports().get(idx) else { - return Some(Err(>::InvalidSyscall.into())) + return Some(Err(>::InvalidSyscall.into())); }; match self.handle_ecall(instance, syscall_symbol.as_bytes(), api_version) { Ok(None) => None, @@ -547,8 +594,7 @@ impl<'a, E: Ext, M: PolkaVmInstance> Runtime<'a, E, M> { None => Some(Err(Error::::InvalidCallFlags.into())), Some(flags) => Some(Ok(ExecReturnValue { flags, data })), }, - Err(TrapReason::Termination) => - Some(Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() })), + Err(TrapReason::Termination) => Some(Ok(Default::default())), Err(TrapReason::SupervisorError(error)) => Some(Err(error.into())), } }, @@ -615,11 +661,12 @@ impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { /// Write the given buffer and its length to the designated locations in sandbox memory and /// charge gas according to the token returned by `create_token`. - // + /// /// `out_ptr` is the location in sandbox memory where `buf` should be written to. /// `out_len_ptr` is an in-out location in sandbox memory. It is read to determine the - /// length of the buffer located at `out_ptr`. If that buffer is large enough the actual - /// `buf.len()` is written to this location. + /// length of the buffer located at `out_ptr`. If that buffer is smaller than the actual + /// `buf.len()`, only what fits into that buffer is written to `out_ptr`. + /// The actual amount of bytes copied to `out_ptr` is written to `out_len_ptr`. /// /// If `out_ptr` is set to the sentinel value of `SENTINEL` and `allow_skip` is true the /// operation is skipped and `Ok` is returned. This is supposed to help callers to make copying @@ -642,22 +689,39 @@ impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { create_token: impl FnOnce(u32) -> Option, ) -> Result<(), DispatchError> { if allow_skip && out_ptr == SENTINEL { - return Ok(()) + return Ok(()); } - let buf_len = buf.len() as u32; - let len: u32 = memory.read_as(out_len_ptr)?; + let len = memory.read_u32(out_len_ptr)?; + let buf_len = len.min(buf.len() as u32); - if len < buf_len { - return Err(Error::::OutputBufferTooSmall.into()) + if let Some(costs) = create_token(buf_len) { + self.charge_gas(costs)?; } + memory.write(out_ptr, &buf[..buf_len as usize])?; + memory.write(out_len_ptr, &buf_len.encode()) + } + + /// Same as `write_sandbox_output` but for static size output. + pub fn write_fixed_sandbox_output( + &mut self, + memory: &mut M, + out_ptr: u32, + buf: &[u8], + allow_skip: bool, + create_token: impl FnOnce(u32) -> Option, + ) -> Result<(), DispatchError> { + if allow_skip && out_ptr == SENTINEL { + return Ok(()); + } + + let buf_len = buf.len() as u32; if let Some(costs) = create_token(buf_len) { self.charge_gas(costs)?; } - memory.write(out_ptr, buf)?; - memory.write(out_len_ptr, &buf_len.encode()) + memory.write(out_ptr, buf) } /// Computes the given hash function on the supplied input. @@ -709,20 +773,16 @@ impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { } } - /// Fallible conversion of a `ExecResult` to `ReturnErrorCode`. - fn exec_into_return_code(from: ExecResult) -> Result { + /// Fallible conversion of a `ExecError` to `ReturnErrorCode`. + fn exec_error_into_return_code(from: ExecError) -> Result { use crate::exec::ErrorOrigin::Callee; - let ExecError { error, origin } = match from { - Ok(retval) => return Ok(retval.into()), - Err(err) => err, - }; - - match (error, origin) { + match (from.error, from.origin) { (_, Callee) => Ok(ReturnErrorCode::CalleeTrapped), (err, _) => Self::err_into_return_code(err), } } + fn decode_key(&self, memory: &M, key_ptr: u32, key_len: u32) -> Result { let res = match key_len { SENTINEL => { @@ -766,7 +826,7 @@ impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { let max_size = self.ext.max_value_size(); let charged = self.charge_gas(costs(value_len, self.ext.max_value_size()))?; if value_len > max_size { - return Err(Error::::ValueTooLarge.into()) + return Err(Error::::ValueTooLarge.into()); } let key = self.decode_key(memory, key_ptr, key_len)?; let value = Some(memory.read(value_ptr, value_len)?); @@ -939,15 +999,15 @@ impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { let call_outcome = match call_type { CallType::Call { callee_ptr, value_ptr, deposit_ptr, weight } => { - let callee: <::T as frame_system::Config>::AccountId = - memory.read_as(callee_ptr)?; - let deposit_limit: BalanceOf<::T> = if deposit_ptr == SENTINEL { - BalanceOf::<::T>::zero() + let mut callee = H160::zero(); + memory.read_into_buf(callee_ptr, callee.as_bytes_mut())?; + let deposit_limit = if deposit_ptr == SENTINEL { + U256::zero() } else { - memory.read_as(deposit_ptr)? + memory.read_u256(deposit_ptr)? }; let read_only = flags.contains(CallFlags::READ_ONLY); - let value: BalanceOf<::T> = memory.read_as(value_ptr)?; + let value = memory.read_u256(value_ptr)?; if value > 0u32.into() { // If the call value is non-zero and state change is not allowed, issue an // error. @@ -959,7 +1019,7 @@ impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { self.ext.call( weight, deposit_limit, - callee, + &callee, value, input_data, flags.contains(CallFlags::ALLOW_REENTRY), @@ -968,35 +1028,40 @@ impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { }, CallType::DelegateCall { code_hash_ptr } => { if flags.intersects(CallFlags::ALLOW_REENTRY | CallFlags::READ_ONLY) { - return Err(Error::::InvalidCallFlags.into()) + return Err(Error::::InvalidCallFlags.into()); } - let code_hash = memory.read_as(code_hash_ptr)?; + + let code_hash = memory.read_h256(code_hash_ptr)?; self.ext.delegate_call(code_hash, input_data) }, }; - // `TAIL_CALL` only matters on an `OK` result. Otherwise the call stack comes to - // a halt anyways without anymore code being executed. - if flags.contains(CallFlags::TAIL_CALL) { - if let Ok(return_value) = call_outcome { + match call_outcome { + // `TAIL_CALL` only matters on an `OK` result. Otherwise the call stack comes to + // a halt anyways without anymore code being executed. + Ok(_) if flags.contains(CallFlags::TAIL_CALL) => { + let output = mem::take(self.ext.last_frame_output_mut()); return Err(TrapReason::Return(ReturnData { - flags: return_value.flags.bits(), - data: return_value.data, - })) - } - } - - if let Ok(output) = &call_outcome { - self.write_sandbox_output( - memory, - output_ptr, - output_len_ptr, - &output.data, - true, - |len| Some(RuntimeCosts::CopyToContract(len)), - )?; + flags: output.flags.bits(), + data: output.data, + })); + }, + Ok(_) => { + let output = mem::take(self.ext.last_frame_output_mut()); + let write_result = self.write_sandbox_output( + memory, + output_ptr, + output_len_ptr, + &output.data, + true, + |len| Some(RuntimeCosts::CopyToContract(len)), + ); + *self.ext.last_frame_output_mut() = output; + write_result?; + Ok(self.ext.last_frame_output().into()) + }, + Err(err) => Ok(Self::exec_error_into_return_code(err)?), } - Ok(Self::exec_into_return_code(call_outcome)?) } fn instantiate( @@ -1009,53 +1074,64 @@ impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { input_data_ptr: u32, input_data_len: u32, address_ptr: u32, - address_len_ptr: u32, output_ptr: u32, output_len_ptr: u32, salt_ptr: u32, - salt_len: u32, ) -> Result { - self.charge_gas(RuntimeCosts::Instantiate { input_data_len, salt_len })?; - let deposit_limit: BalanceOf<::T> = if deposit_ptr == SENTINEL { - BalanceOf::<::T>::zero() + self.charge_gas(RuntimeCosts::Instantiate { input_data_len })?; + let deposit_limit: U256 = + if deposit_ptr == SENTINEL { U256::zero() } else { memory.read_u256(deposit_ptr)? }; + let value = memory.read_u256(value_ptr)?; + let code_hash = memory.read_h256(code_hash_ptr)?; + let input_data = memory.read(input_data_ptr, input_data_len)?; + let salt = if salt_ptr == SENTINEL { + None } else { - memory.read_as(deposit_ptr)? + let salt: [u8; 32] = memory.read_array(salt_ptr)?; + Some(salt) }; - let value: BalanceOf<::T> = memory.read_as(value_ptr)?; - let code_hash: CodeHash<::T> = memory.read_as(code_hash_ptr)?; - let input_data = memory.read(input_data_ptr, input_data_len)?; - let salt = memory.read(salt_ptr, salt_len)?; - let instantiate_outcome = - self.ext.instantiate(weight, deposit_limit, code_hash, value, input_data, &salt); - if let Ok((address, output)) = &instantiate_outcome { - if !output.flags.contains(ReturnFlags::REVERT) { - self.write_sandbox_output( + + match self.ext.instantiate( + weight, + deposit_limit, + code_hash, + value, + input_data, + salt.as_ref(), + ) { + Ok(address) => { + if !self.ext.last_frame_output().flags.contains(ReturnFlags::REVERT) { + self.write_fixed_sandbox_output( + memory, + address_ptr, + &address.as_bytes(), + true, + already_charged, + )?; + } + let output = mem::take(self.ext.last_frame_output_mut()); + let write_result = self.write_sandbox_output( memory, - address_ptr, - address_len_ptr, - &address.encode(), + output_ptr, + output_len_ptr, + &output.data, true, - already_charged, - )?; - } - self.write_sandbox_output( - memory, - output_ptr, - output_len_ptr, - &output.data, - true, - |len| Some(RuntimeCosts::CopyToContract(len)), - )?; + |len| Some(RuntimeCosts::CopyToContract(len)), + ); + *self.ext.last_frame_output_mut() = output; + write_result?; + Ok(self.ext.last_frame_output().into()) + }, + Err(err) => Ok(Self::exec_error_into_return_code(err)?), } - Ok(Self::exec_into_return_code(instantiate_outcome.map(|(_, retval)| retval))?) } fn terminate(&mut self, memory: &M, beneficiary_ptr: u32) -> Result<(), TrapReason> { let count = self.ext.locked_delegate_dependencies_count() as _; self.charge_gas(RuntimeCosts::Terminate(count))?; - let beneficiary: <::T as frame_system::Config>::AccountId = - memory.read_as(beneficiary_ptr)?; + let mut beneficiary = H160::zero(); + memory.read_into_buf(beneficiary_ptr, beneficiary.as_bytes_mut())?; self.ext.terminate(&beneficiary)?; Err(TrapReason::Termination) } @@ -1157,13 +1233,13 @@ pub mod env { fn transfer( &mut self, memory: &mut M, - account_ptr: u32, + address_ptr: u32, value_ptr: u32, ) -> Result { self.charge_gas(RuntimeCosts::Transfer)?; - let callee: <::T as frame_system::Config>::AccountId = - memory.read_as(account_ptr)?; - let value: BalanceOf<::T> = memory.read_as(value_ptr)?; + let mut callee = H160::zero(); + memory.read_into_buf(address_ptr, callee.as_bytes_mut())?; + let value: U256 = memory.read_u256(value_ptr)?; let result = self.ext.transfer(&callee, value); match result { Ok(()) => Ok(ReturnErrorCode::Success), @@ -1246,11 +1322,9 @@ pub mod env { input_data_ptr: u32, input_data_len: u32, address_ptr: u32, - address_len_ptr: u32, output_ptr: u32, output_len_ptr: u32, salt_ptr: u32, - salt_len: u32, ) -> Result { self.instantiate( memory, @@ -1261,11 +1335,9 @@ pub mod env { input_data_ptr, input_data_len, address_ptr, - address_len_ptr, output_ptr, output_len_ptr, salt_ptr, - salt_len, ) } @@ -1309,14 +1381,13 @@ pub mod env { /// Stores the address of the caller into the supplied buffer. /// See [`pallet_revive_uapi::HostFn::caller`]. #[api_version(0)] - fn caller(&mut self, memory: &mut M, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { + fn caller(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> { self.charge_gas(RuntimeCosts::Caller)?; - let caller = self.ext.caller().account_id()?.clone(); - Ok(self.write_sandbox_output( + let caller = ::AddressMapper::to_address(self.ext.caller().account_id()?); + Ok(self.write_fixed_sandbox_output( memory, out_ptr, - out_len_ptr, - &caller.encode(), + caller.as_bytes(), false, already_charged, )?) @@ -1327,9 +1398,8 @@ pub mod env { #[api_version(0)] fn is_contract(&mut self, memory: &mut M, account_ptr: u32) -> Result { self.charge_gas(RuntimeCosts::IsContract)?; - let address: <::T as frame_system::Config>::AccountId = - memory.read_as(account_ptr)?; - + let mut address = H160::zero(); + memory.read_into_buf(account_ptr, address.as_bytes_mut())?; Ok(self.ext.is_contract(&address) as u32) } @@ -1339,19 +1409,17 @@ pub mod env { fn code_hash( &mut self, memory: &mut M, - account_ptr: u32, + addr_ptr: u32, out_ptr: u32, - out_len_ptr: u32, ) -> Result { self.charge_gas(RuntimeCosts::CodeHash)?; - let address: <::T as frame_system::Config>::AccountId = - memory.read_as(account_ptr)?; + let mut address = H160::zero(); + memory.read_into_buf(addr_ptr, address.as_bytes_mut())?; if let Some(value) = self.ext.code_hash(&address) { - self.write_sandbox_output( + self.write_fixed_sandbox_output( memory, out_ptr, - out_len_ptr, - &value.encode(), + &value.as_bytes(), false, already_charged, )?; @@ -1364,19 +1432,13 @@ pub mod env { /// Retrieve the code hash of the currently executing contract. /// See [`pallet_revive_uapi::HostFn::own_code_hash`]. #[api_version(0)] - fn own_code_hash( - &mut self, - memory: &mut M, - out_ptr: u32, - out_len_ptr: u32, - ) -> Result<(), TrapReason> { + fn own_code_hash(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> { self.charge_gas(RuntimeCosts::OwnCodeHash)?; - let code_hash_encoded = &self.ext.own_code_hash().encode(); - Ok(self.write_sandbox_output( + let code_hash = *self.ext.own_code_hash(); + Ok(self.write_fixed_sandbox_output( memory, out_ptr, - out_len_ptr, - code_hash_encoded, + code_hash.as_bytes(), false, already_charged, )?) @@ -1401,18 +1463,13 @@ pub mod env { /// Stores the address of the current contract into the supplied buffer. /// See [`pallet_revive_uapi::HostFn::address`]. #[api_version(0)] - fn address( - &mut self, - memory: &mut M, - out_ptr: u32, - out_len_ptr: u32, - ) -> Result<(), TrapReason> { + fn address(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> { self.charge_gas(RuntimeCosts::Address)?; - Ok(self.write_sandbox_output( + let address = self.ext.address(); + Ok(self.write_fixed_sandbox_output( memory, out_ptr, - out_len_ptr, - &self.ext.address().encode(), + address.as_bytes(), false, already_charged, )?) @@ -1427,14 +1484,12 @@ pub mod env { ref_time_limit: u64, proof_size_limit: u64, out_ptr: u32, - out_len_ptr: u32, ) -> Result<(), TrapReason> { let weight = Weight::from_parts(ref_time_limit, proof_size_limit); self.charge_gas(RuntimeCosts::WeightToFee)?; - Ok(self.write_sandbox_output( + Ok(self.write_fixed_sandbox_output( memory, out_ptr, - out_len_ptr, &self.ext.get_weight_price(weight).encode(), false, already_charged, @@ -1450,7 +1505,7 @@ pub mod env { out_ptr: u32, out_len_ptr: u32, ) -> Result<(), TrapReason> { - self.charge_gas(RuntimeCosts::GasLeft)?; + self.charge_gas(RuntimeCosts::WeightLeft)?; let gas_left = &self.ext.gas_meter().gas_left().encode(); Ok(self.write_sandbox_output( memory, @@ -1462,41 +1517,93 @@ pub mod env { )?) } - /// Stores the *free* balance of the current account into the supplied buffer. - /// See [`pallet_revive_uapi::HostFn::balance`]. + /// Stores the immutable data into the supplied buffer. + /// See [`pallet_revive_uapi::HostFn::get_immutable_data`]. #[api_version(0)] - fn balance( + fn get_immutable_data( &mut self, memory: &mut M, out_ptr: u32, out_len_ptr: u32, ) -> Result<(), TrapReason> { + let charged = self.charge_gas(RuntimeCosts::GetImmutableData(limits::IMMUTABLE_BYTES))?; + let data = self.ext.get_immutable_data()?; + self.adjust_gas(charged, RuntimeCosts::GetImmutableData(data.len() as u32)); + self.write_sandbox_output(memory, out_ptr, out_len_ptr, &data, false, already_charged)?; + Ok(()) + } + + /// Attaches the supplied immutable data to the currently executing contract. + /// See [`pallet_revive_uapi::HostFn::set_immutable_data`]. + #[api_version(0)] + fn set_immutable_data(&mut self, memory: &mut M, ptr: u32, len: u32) -> Result<(), TrapReason> { + if len > limits::IMMUTABLE_BYTES { + return Err(Error::::OutOfBounds.into()); + } + self.charge_gas(RuntimeCosts::SetImmutableData(len))?; + let buf = memory.read(ptr, len)?; + let data = buf.try_into().expect("bailed out earlier; qed"); + self.ext.set_immutable_data(data)?; + Ok(()) + } + + /// Stores the *free* balance of the current account into the supplied buffer. + /// See [`pallet_revive_uapi::HostFn::balance`]. + #[api_version(0)] + fn balance(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> { self.charge_gas(RuntimeCosts::Balance)?; - Ok(self.write_sandbox_output( + Ok(self.write_fixed_sandbox_output( memory, out_ptr, - out_len_ptr, - &self.ext.balance().encode(), + &as_bytes(self.ext.balance()), false, already_charged, )?) } - /// Stores the value transferred along with this call/instantiate into the supplied buffer. - /// See [`pallet_revive_uapi::HostFn::value_transferred`]. + /// Stores the *free* balance of the supplied address into the supplied buffer. + /// See [`pallet_revive_uapi::HostFn::balance`]. #[api_version(0)] - fn value_transferred( + fn balance_of( &mut self, memory: &mut M, + addr_ptr: u32, out_ptr: u32, - out_len_ptr: u32, ) -> Result<(), TrapReason> { + self.charge_gas(RuntimeCosts::BalanceOf)?; + let mut address = H160::zero(); + memory.read_into_buf(addr_ptr, address.as_bytes_mut())?; + Ok(self.write_fixed_sandbox_output( + memory, + out_ptr, + &as_bytes(self.ext.balance_of(&address)), + false, + already_charged, + )?) + } + + /// Returns the chain ID. + /// See [`pallet_revive_uapi::HostFn::chain_id`]. + #[api_version(0)] + fn chain_id(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> { + Ok(self.write_fixed_sandbox_output( + memory, + out_ptr, + &as_bytes(U256::from(::ChainId::get())), + false, + |_| Some(RuntimeCosts::CopyToContract(32)), + )?) + } + + /// Stores the value transferred along with this call/instantiate into the supplied buffer. + /// See [`pallet_revive_uapi::HostFn::value_transferred`]. + #[api_version(0)] + fn value_transferred(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> { self.charge_gas(RuntimeCosts::ValueTransferred)?; - Ok(self.write_sandbox_output( + Ok(self.write_fixed_sandbox_output( memory, out_ptr, - out_len_ptr, - &self.ext.value_transferred().encode(), + &as_bytes(self.ext.value_transferred()), false, already_charged, )?) @@ -1505,13 +1612,12 @@ pub mod env { /// Load the latest block timestamp into the supplied buffer /// See [`pallet_revive_uapi::HostFn::now`]. #[api_version(0)] - fn now(&mut self, memory: &mut M, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { + fn now(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> { self.charge_gas(RuntimeCosts::Now)?; - Ok(self.write_sandbox_output( + Ok(self.write_fixed_sandbox_output( memory, out_ptr, - out_len_ptr, - &self.ext.now().encode(), + &as_bytes(self.ext.now()), false, already_charged, )?) @@ -1520,18 +1626,12 @@ pub mod env { /// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer. /// See [`pallet_revive_uapi::HostFn::minimum_balance`]. #[api_version(0)] - fn minimum_balance( - &mut self, - memory: &mut M, - out_ptr: u32, - out_len_ptr: u32, - ) -> Result<(), TrapReason> { + fn minimum_balance(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> { self.charge_gas(RuntimeCosts::MinimumBalance)?; - Ok(self.write_sandbox_output( + Ok(self.write_fixed_sandbox_output( memory, out_ptr, - out_len_ptr, - &self.ext.minimum_balance().encode(), + &as_bytes(self.ext.minimum_balance()), false, already_charged, )?) @@ -1545,50 +1645,47 @@ pub mod env { &mut self, memory: &mut M, topics_ptr: u32, - topics_len: u32, + num_topic: u32, data_ptr: u32, data_len: u32, ) -> Result<(), TrapReason> { - let num_topic = topics_len - .checked_div(core::mem::size_of::>() as u32) - .ok_or("Zero sized topics are not allowed")?; self.charge_gas(RuntimeCosts::DepositEvent { num_topic, len: data_len })?; + + if num_topic > limits::NUM_EVENT_TOPICS { + return Err(Error::::TooManyTopics.into()); + } + if data_len > self.ext.max_value_size() { return Err(Error::::ValueTooLarge.into()); } - let topics: Vec::T>> = match topics_len { + let topics: Vec = match num_topic { 0 => Vec::new(), - _ => memory.read_as_unbounded(topics_ptr, topics_len)?, + _ => { + let mut v = Vec::with_capacity(num_topic as usize); + let topics_len = num_topic * H256::len_bytes() as u32; + let buf = memory.read(topics_ptr, topics_len)?; + for chunk in buf.chunks_exact(H256::len_bytes()) { + v.push(H256::from_slice(chunk)); + } + v + }, }; - // If there are more than `event_topics`, then trap. - if topics.len() as u32 > limits::NUM_EVENT_TOPICS { - return Err(Error::::TooManyTopics.into()); - } - let event_data = memory.read(data_ptr, data_len)?; - self.ext.deposit_event(topics, event_data); - Ok(()) } /// Stores the current block number of the current contract into the supplied buffer. /// See [`pallet_revive_uapi::HostFn::block_number`]. #[api_version(0)] - fn block_number( - &mut self, - memory: &mut M, - out_ptr: u32, - out_len_ptr: u32, - ) -> Result<(), TrapReason> { + fn block_number(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> { self.charge_gas(RuntimeCosts::BlockNumber)?; - Ok(self.write_sandbox_output( + Ok(self.write_fixed_sandbox_output( memory, out_ptr, - out_len_ptr, - &self.ext.block_number().encode(), + &as_bytes(self.ext.block_number()), false, already_charged, )?) @@ -1754,7 +1851,7 @@ pub mod env { dispatch_info, RuntimeCosts::CallXcmExecute, |runtime| { - let origin = crate::RawOrigin::Signed(runtime.ext.address().clone()).into(); + let origin = crate::RawOrigin::Signed(runtime.ext.account_id().clone()).into(); let weight_used = <::Xcm>::execute( origin, Box::new(message), @@ -1773,6 +1870,7 @@ pub mod env { &mut self, memory: &mut M, dest_ptr: u32, + dest_len: u32, msg_ptr: u32, msg_len: u32, output_ptr: u32, @@ -1780,13 +1878,15 @@ pub mod env { use xcm::{VersionedLocation, VersionedXcm}; use xcm_builder::{SendController, SendControllerWeightInfo}; - self.charge_gas(RuntimeCosts::CopyFromContract(msg_len))?; - let dest: VersionedLocation = memory.read_as(dest_ptr)?; + self.charge_gas(RuntimeCosts::CopyFromContract(dest_len))?; + let dest: VersionedLocation = memory.read_as_unbounded(dest_ptr, dest_len)?; + self.charge_gas(RuntimeCosts::CopyFromContract(msg_len))?; let message: VersionedXcm<()> = memory.read_as_unbounded(msg_ptr, msg_len)?; + let weight = <::Xcm as SendController<_>>::WeightInfo::send(); self.charge_gas(RuntimeCosts::CallRuntime(weight))?; - let origin = crate::RawOrigin::Signed(self.ext.address().clone()).into(); + let origin = crate::RawOrigin::Signed(self.ext.account_id().clone()).into(); match <::Xcm>::send(origin, dest.into(), message.into()) { Ok(message_id) => { @@ -1864,7 +1964,9 @@ pub mod env { /// Replace the contract code at the specified address with new code. /// See [`pallet_revive_uapi::HostFn::set_code_hash`]. - #[api_version(0)] + /// + /// Disabled until the internal implementation takes care of collecting + /// the immutable data of the new code hash. #[mutating] fn set_code_hash( &mut self, @@ -1872,7 +1974,7 @@ pub mod env { code_hash_ptr: u32, ) -> Result { self.charge_gas(RuntimeCosts::SetCodeHash)?; - let code_hash: CodeHash<::T> = memory.read_as(code_hash_ptr)?; + let code_hash: H256 = memory.read_h256(code_hash_ptr)?; match self.ext.set_code_hash(code_hash) { Err(err) => { let code = Self::err_into_return_code(err)?; @@ -1914,7 +2016,7 @@ pub mod env { code_hash_ptr: u32, ) -> Result<(), TrapReason> { self.charge_gas(RuntimeCosts::LockDelegateDependency)?; - let code_hash = memory.read_as(code_hash_ptr)?; + let code_hash = memory.read_h256(code_hash_ptr)?; self.ext.lock_delegate_dependency(code_hash)?; Ok(()) } @@ -1929,8 +2031,48 @@ pub mod env { code_hash_ptr: u32, ) -> Result<(), TrapReason> { self.charge_gas(RuntimeCosts::UnlockDelegateDependency)?; - let code_hash = memory.read_as(code_hash_ptr)?; + let code_hash = memory.read_h256(code_hash_ptr)?; self.ext.unlock_delegate_dependency(&code_hash)?; Ok(()) } + + /// Stores the length of the data returned by the last call into the supplied buffer. + /// See [`pallet_revive_uapi::HostFn::return_data_size`]. + #[api_version(0)] + fn return_data_size(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> { + Ok(self.write_fixed_sandbox_output( + memory, + out_ptr, + &as_bytes(U256::from(self.ext.last_frame_output().data.len())), + false, + |len| Some(RuntimeCosts::CopyToContract(len)), + )?) + } + + /// Stores data returned by the last call, starting from `offset`, into the supplied buffer. + /// See [`pallet_revive_uapi::HostFn::return_data`]. + #[api_version(0)] + fn return_data_copy( + &mut self, + memory: &mut M, + out_ptr: u32, + out_len_ptr: u32, + offset: u32, + ) -> Result<(), TrapReason> { + let output = mem::take(self.ext.last_frame_output_mut()); + let result = if offset as usize > output.data.len() { + Err(Error::::OutOfBounds.into()) + } else { + self.write_sandbox_output( + memory, + out_ptr, + out_len_ptr, + &output.data[offset as usize..], + false, + |len| Some(RuntimeCosts::CopyToContract(len)), + ) + }; + *self.ext.last_frame_output_mut() = output; + Ok(result?) + } } diff --git a/substrate/frame/revive/src/weights.rs b/substrate/frame/revive/src/weights.rs index 6a0d27529d873cb4214784cd08c992ae3a6f0a78..9a1b2310b4eb98c3b3e9c7206c5e60414b6ea471 100644 --- a/substrate/frame/revive/src/weights.rs +++ b/substrate/frame/revive/src/weights.rs @@ -18,9 +18,9 @@ //! Autogenerated weights for `pallet_revive` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-07-17, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-10-06, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-yaoqqom-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-jniz7bxx-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` // Executed Command: @@ -36,7 +36,7 @@ // --pallet=pallet_revive // --chain=dev // --header=./substrate/HEADER-APACHE2 -// --output=./substrate/frame/contracts/src/weights.rs +// --output=./substrate/frame/revive/src/weights.rs // --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -51,25 +51,11 @@ use core::marker::PhantomData; pub trait WeightInfo { fn on_process_deletion_queue_batch() -> Weight; fn on_initialize_per_trie_key(k: u32, ) -> Weight; - fn v9_migration_step(c: u32, ) -> Weight; - fn v10_migration_step() -> Weight; - fn v11_migration_step(k: u32, ) -> Weight; - fn v12_migration_step(c: u32, ) -> Weight; - fn v13_migration_step() -> Weight; - fn v14_migration_step() -> Weight; - fn v15_migration_step() -> Weight; - fn v16_migration_step() -> Weight; - fn migration_noop() -> Weight; - fn migrate() -> Weight; - fn on_runtime_upgrade_noop() -> Weight; - fn on_runtime_upgrade_in_progress() -> Weight; - fn on_runtime_upgrade() -> Weight; fn call_with_code_per_byte(c: u32, ) -> Weight; - fn instantiate_with_code(c: u32, i: u32, s: u32, ) -> Weight; - fn instantiate(i: u32, s: u32, ) -> Weight; + fn instantiate_with_code(c: u32, i: u32, ) -> Weight; + fn instantiate(i: u32, ) -> Weight; fn call() -> Weight; - fn upload_code_determinism_enforced(c: u32, ) -> Weight; - fn upload_code_determinism_relaxed(c: u32, ) -> Weight; + fn upload_code(c: u32, ) -> Weight; fn remove_code() -> Weight; fn set_code() -> Weight; fn noop_host_fn(r: u32, ) -> Weight; @@ -80,8 +66,11 @@ pub trait WeightInfo { fn seal_caller_is_origin() -> Weight; fn seal_caller_is_root() -> Weight; fn seal_address() -> Weight; - fn seal_gas_left() -> Weight; + fn seal_weight_left() -> Weight; fn seal_balance() -> Weight; + fn seal_balance_of() -> Weight; + fn seal_get_immutable_data(n: u32, ) -> Weight; + fn seal_set_immutable_data(n: u32, ) -> Weight; fn seal_value_transferred() -> Weight; fn seal_minimum_balance() -> Weight; fn seal_block_number() -> Weight; @@ -90,7 +79,6 @@ pub trait WeightInfo { fn seal_input(n: u32, ) -> Weight; fn seal_return(n: u32, ) -> Weight; fn seal_terminate(n: u32, ) -> Weight; - fn seal_random() -> Weight; fn seal_deposit_event(t: u32, n: u32, ) -> Weight; fn seal_debug_message(i: u32, ) -> Weight; fn get_storage_empty() -> Weight; @@ -115,7 +103,7 @@ pub trait WeightInfo { fn seal_transfer() -> Weight; fn seal_call(t: u32, i: u32, ) -> Weight; fn seal_delegate_call() -> Weight; - fn seal_instantiate(i: u32, s: u32, ) -> Weight; + fn seal_instantiate(i: u32, ) -> Weight; fn seal_hash_sha2_256(n: u32, ) -> Weight; fn seal_hash_keccak_256(n: u32, ) -> Weight; fn seal_hash_blake2_256(n: u32, ) -> Weight; @@ -126,23 +114,20 @@ pub trait WeightInfo { fn seal_set_code_hash() -> Weight; fn lock_delegate_dependency() -> Weight; fn unlock_delegate_dependency() -> Weight; - fn seal_reentrance_count() -> Weight; - fn seal_account_reentrance_count() -> Weight; - fn seal_instantiation_nonce() -> Weight; - fn instr_i64_load_store(r: u32, ) -> Weight; + fn instr(r: u32, ) -> Weight; } /// Weights for `pallet_revive` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: `Contracts::DeletionQueueCounter` (r:1 w:0) - /// Proof: `Contracts::DeletionQueueCounter` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `Revive::DeletionQueueCounter` (r:1 w:0) + /// Proof: `Revive::DeletionQueueCounter` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) fn on_process_deletion_queue_batch() -> Weight { // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `1627` - // Minimum execution time: 1_915_000 picoseconds. - Weight::from_parts(1_986_000, 1627) + // Measured: `109` + // Estimated: `1594` + // Minimum execution time: 2_712_000 picoseconds. + Weight::from_parts(2_882_000, 1594) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -150,375 +135,150 @@ impl WeightInfo for SubstrateWeight { /// The range of component `k` is `[0, 1024]`. fn on_initialize_per_trie_key(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `452 + k * (69 ยฑ0)` - // Estimated: `442 + k * (70 ยฑ0)` - // Minimum execution time: 11_103_000 picoseconds. - Weight::from_parts(11_326_000, 442) - // Standard Error: 2_291 - .saturating_add(Weight::from_parts(1_196_329, 0).saturating_mul(k.into())) + // Measured: `392 + k * (69 ยฑ0)` + // Estimated: `382 + k * (70 ยฑ0)` + // Minimum execution time: 13_394_000 picoseconds. + Weight::from_parts(13_668_000, 382) + // Standard Error: 2_208 + .saturating_add(Weight::from_parts(1_340_842, 0).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) .saturating_add(Weight::from_parts(0, 70).saturating_mul(k.into())) } - /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553022fca90611ba8b7942f8bdb3b97f6580` (r:2 w:1) - /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553022fca90611ba8b7942f8bdb3b97f6580` (r:2 w:1) - /// The range of component `c` is `[0, 125952]`. - fn v9_migration_step(c: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `211 + c * (1 ยฑ0)` - // Estimated: `6149 + c * (1 ยฑ0)` - // Minimum execution time: 7_783_000 picoseconds. - Weight::from_parts(4_462_075, 6149) - // Standard Error: 5 - .saturating_add(Weight::from_parts(1_634, 0).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) - } - /// Storage: `Contracts::ContractInfoOf` (r:2 w:1) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) - /// Storage: `System::Account` (r:1 w:0) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) - fn v10_migration_step() -> Weight { - // Proof Size summary in bytes: - // Measured: `510` - // Estimated: `6450` - // Minimum execution time: 15_971_000 picoseconds. - Weight::from_parts(16_730_000, 6450) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `Contracts::DeletionQueue` (r:1 w:1025) - /// Proof: `Contracts::DeletionQueue` (`max_values`: None, `max_size`: Some(142), added: 2617, mode: `Measured`) - /// Storage: `Contracts::DeletionQueueCounter` (r:0 w:1) - /// Proof: `Contracts::DeletionQueueCounter` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) - /// The range of component `k` is `[0, 1024]`. - fn v11_migration_step(k: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `171 + k * (1 ยฑ0)` - // Estimated: `3635 + k * (1 ยฑ0)` - // Minimum execution time: 3_149_000 picoseconds. - Weight::from_parts(3_264_000, 3635) - // Standard Error: 559 - .saturating_add(Weight::from_parts(1_111_209, 0).saturating_mul(k.into())) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(k.into()))) - .saturating_add(Weight::from_parts(0, 1).saturating_mul(k.into())) - } - /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553053f13fd319a03c211337c76e0fe776df` (r:2 w:0) - /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553053f13fd319a03c211337c76e0fe776df` (r:2 w:0) - /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553022fca90611ba8b7942f8bdb3b97f6580` (r:1 w:1) - /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553022fca90611ba8b7942f8bdb3b97f6580` (r:1 w:1) - /// Storage: `System::Account` (r:1 w:0) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) - /// Storage: `Contracts::CodeInfoOf` (r:0 w:1) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) - /// The range of component `c` is `[0, 125952]`. - fn v12_migration_step(c: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `325 + c * (1 ยฑ0)` - // Estimated: `6263 + c * (1 ยฑ0)` - // Minimum execution time: 15_072_000 picoseconds. - Weight::from_parts(15_721_891, 6263) - // Standard Error: 2 - .saturating_add(Weight::from_parts(428, 0).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) - } - /// Storage: `Contracts::ContractInfoOf` (r:2 w:1) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) - fn v13_migration_step() -> Weight { - // Proof Size summary in bytes: - // Measured: `440` - // Estimated: `6380` - // Minimum execution time: 12_047_000 picoseconds. - Weight::from_parts(12_500_000, 6380) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `Contracts::CodeInfoOf` (r:2 w:0) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) - /// Storage: `Balances::Holds` (r:1 w:0) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) - fn v14_migration_step() -> Weight { - // Proof Size summary in bytes: - // Measured: `352` - // Estimated: `6292` - // Minimum execution time: 47_488_000 picoseconds. - Weight::from_parts(48_482_000, 6292) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `Contracts::ContractInfoOf` (r:2 w:1) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) - /// Storage: `System::Account` (r:2 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) - fn v15_migration_step() -> Weight { - // Proof Size summary in bytes: - // Measured: `594` - // Estimated: `6534` - // Minimum execution time: 52_801_000 picoseconds. - Weight::from_parts(54_230_000, 6534) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } - /// Storage: `Contracts::ContractInfoOf` (r:2 w:1) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) - fn v16_migration_step() -> Weight { - // Proof Size summary in bytes: - // Measured: `409` - // Estimated: `6349` - // Minimum execution time: 11_618_000 picoseconds. - Weight::from_parts(12_068_000, 6349) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `Contracts::MigrationInProgress` (r:1 w:1) - /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) - fn migration_noop() -> Weight { - // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `1627` - // Minimum execution time: 2_131_000 picoseconds. - Weight::from_parts(2_255_000, 1627) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `Contracts::MigrationInProgress` (r:1 w:1) - /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) - /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:1) - /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:1) - fn migrate() -> Weight { - // Proof Size summary in bytes: - // Measured: `166` - // Estimated: `3631` - // Minimum execution time: 10_773_000 picoseconds. - Weight::from_parts(11_118_000, 3631) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } - /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) - /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) - fn on_runtime_upgrade_noop() -> Weight { - // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `3607` - // Minimum execution time: 4_371_000 picoseconds. - Weight::from_parts(4_624_000, 3607) - .saturating_add(T::DbWeight::get().reads(1_u64)) - } - /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) - /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) - /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) - /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) - fn on_runtime_upgrade_in_progress() -> Weight { - // Proof Size summary in bytes: - // Measured: `167` - // Estimated: `3632` - // Minimum execution time: 5_612_000 picoseconds. - Weight::from_parts(5_838_000, 3632) - .saturating_add(T::DbWeight::get().reads(2_u64)) - } - /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) - /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) - /// Storage: `Contracts::MigrationInProgress` (r:1 w:1) - /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) - fn on_runtime_upgrade() -> Weight { - // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `3607` - // Minimum execution time: 5_487_000 picoseconds. - Weight::from_parts(5_693_000, 3607) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) - /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) - /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) - /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) - /// Storage: `Contracts::PristineCode` (r:1 w:0) - /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// Storage: `Revive::ContractInfoOf` (r:1 w:1) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) + /// Storage: `Revive::CodeInfoOf` (r:1 w:0) + /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) + /// Storage: `Revive::PristineCode` (r:1 w:0) + /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) /// Storage: `Timestamp::Now` (r:1 w:0) /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) - /// The range of component `c` is `[0, 125952]`. + /// The range of component `c` is `[0, 262144]`. fn call_with_code_per_byte(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `800 + c * (1 ยฑ0)` - // Estimated: `4266 + c * (1 ยฑ0)` - // Minimum execution time: 247_545_000 picoseconds. - Weight::from_parts(268_016_699, 4266) - // Standard Error: 4 - .saturating_add(Weight::from_parts(700, 0).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(6_u64)) + // Measured: `1466` + // Estimated: `4931` + // Minimum execution time: 80_390_000 picoseconds. + Weight::from_parts(83_627_295, 4931) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) - .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) } - /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) - /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) - /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Revive::CodeInfoOf` (r:1 w:1) + /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) /// Storage: `Balances::Holds` (r:2 w:2) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) - /// Storage: `Contracts::Nonce` (r:1 w:1) - /// Proof: `Contracts::Nonce` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) - /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `Measured`) + /// Storage: `Revive::ContractInfoOf` (r:1 w:1) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Timestamp::Now` (r:1 w:0) /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) - /// Storage: `Contracts::PristineCode` (r:0 w:1) - /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) - /// The range of component `c` is `[0, 125952]`. - /// The range of component `i` is `[0, 1048576]`. - /// The range of component `s` is `[0, 1048576]`. - fn instantiate_with_code(c: u32, i: u32, s: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `323` - // Estimated: `6262` - // Minimum execution time: 4_396_772_000 picoseconds. - Weight::from_parts(235_107_907, 6262) - // Standard Error: 185 - .saturating_add(Weight::from_parts(53_843, 0).saturating_mul(c.into())) - // Standard Error: 22 - .saturating_add(Weight::from_parts(2_143, 0).saturating_mul(i.into())) - // Standard Error: 22 - .saturating_add(Weight::from_parts(2_210, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) - .saturating_add(T::DbWeight::get().writes(7_u64)) - } - /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) - /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) - /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) - /// Storage: `Contracts::PristineCode` (r:1 w:0) - /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) - /// Storage: `Contracts::Nonce` (r:1 w:1) - /// Proof: `Contracts::Nonce` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) - /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `Revive::PristineCode` (r:0 w:1) + /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) + /// The range of component `c` is `[0, 262144]`. + /// The range of component `i` is `[0, 262144]`. + fn instantiate_with_code(_c: u32, i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `303` + // Estimated: `6232` + // Minimum execution time: 184_708_000 picoseconds. + Weight::from_parts(177_995_416, 6232) + // Standard Error: 11 + .saturating_add(Weight::from_parts(4_609, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + /// Storage: `Revive::CodeInfoOf` (r:1 w:1) + /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) + /// Storage: `Revive::PristineCode` (r:1 w:0) + /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) + /// Storage: `Revive::ContractInfoOf` (r:1 w:1) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Timestamp::Now` (r:1 w:0) /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) - /// The range of component `i` is `[0, 1048576]`. - /// The range of component `s` is `[0, 1048576]`. - fn instantiate(i: u32, s: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `560` - // Estimated: `4017` - // Minimum execution time: 2_240_868_000 picoseconds. - Weight::from_parts(2_273_668_000, 4017) - // Standard Error: 32 - .saturating_add(Weight::from_parts(934, 0).saturating_mul(i.into())) - // Standard Error: 32 - .saturating_add(Weight::from_parts(920, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(8_u64)) - .saturating_add(T::DbWeight::get().writes(5_u64)) - } - /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) - /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) - /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) - /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) - /// Storage: `Contracts::PristineCode` (r:1 w:0) - /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `Measured`) + /// The range of component `i` is `[0, 262144]`. + fn instantiate(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1261` + // Estimated: `4706` + // Minimum execution time: 150_137_000 picoseconds. + Weight::from_parts(136_548_469, 4706) + // Standard Error: 16 + .saturating_add(Weight::from_parts(4_531, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) + } + /// Storage: `Revive::ContractInfoOf` (r:1 w:1) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) + /// Storage: `Revive::CodeInfoOf` (r:1 w:0) + /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) + /// Storage: `Revive::PristineCode` (r:1 w:0) + /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) /// Storage: `Timestamp::Now` (r:1 w:0) /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) fn call() -> Weight { // Proof Size summary in bytes: - // Measured: `826` - // Estimated: `4291` - // Minimum execution time: 165_067_000 picoseconds. - Weight::from_parts(168_582_000, 4291) - .saturating_add(T::DbWeight::get().reads(6_u64)) + // Measured: `1466` + // Estimated: `4931` + // Minimum execution time: 83_178_000 picoseconds. + Weight::from_parts(84_633_000, 4931) + .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) - /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) - /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) - /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) - /// Storage: `Contracts::PristineCode` (r:0 w:1) - /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) - /// The range of component `c` is `[0, 125952]`. - fn upload_code_determinism_enforced(c: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `3607` - // Minimum execution time: 229_454_000 picoseconds. - Weight::from_parts(251_495_551, 3607) - // Standard Error: 71 - .saturating_add(Weight::from_parts(51_428, 0).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) - } - /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) - /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) - /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Revive::CodeInfoOf` (r:1 w:1) + /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) - /// Storage: `Contracts::PristineCode` (r:0 w:1) - /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) - /// The range of component `c` is `[0, 125952]`. - fn upload_code_determinism_relaxed(c: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `3607` - // Minimum execution time: 240_390_000 picoseconds. - Weight::from_parts(273_854_266, 3607) - // Standard Error: 243 - .saturating_add(Weight::from_parts(51_836, 0).saturating_mul(c.into())) - .saturating_add(T::DbWeight::get().reads(3_u64)) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `Measured`) + /// Storage: `Revive::PristineCode` (r:0 w:1) + /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) + /// The range of component `c` is `[0, 262144]`. + fn upload_code(_c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `3574` + // Minimum execution time: 51_526_000 picoseconds. + Weight::from_parts(54_565_973, 3574) + .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) - /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) - /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Revive::CodeInfoOf` (r:1 w:1) + /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) - /// Storage: `Contracts::PristineCode` (r:0 w:1) - /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `Measured`) + /// Storage: `Revive::PristineCode` (r:0 w:1) + /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) fn remove_code() -> Weight { // Proof Size summary in bytes: - // Measured: `315` - // Estimated: `3780` - // Minimum execution time: 39_374_000 picoseconds. - Weight::from_parts(40_247_000, 3780) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Measured: `285` + // Estimated: `3750` + // Minimum execution time: 41_885_000 picoseconds. + Weight::from_parts(42_467_000, 3750) + .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) - /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) - /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) - /// Storage: `Contracts::CodeInfoOf` (r:2 w:2) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Revive::ContractInfoOf` (r:1 w:1) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) + /// Storage: `Revive::CodeInfoOf` (r:2 w:2) + /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) fn set_code() -> Weight { // Proof Size summary in bytes: - // Measured: `552` - // Estimated: `6492` - // Minimum execution time: 24_473_000 picoseconds. - Weight::from_parts(25_890_000, 6492) - .saturating_add(T::DbWeight::get().reads(4_u64)) + // Measured: `492` + // Estimated: `6432` + // Minimum execution time: 24_905_000 picoseconds. + Weight::from_parts(25_483_000, 6432) + .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } /// The range of component `r` is `[0, 1600]`. @@ -526,107 +286,144 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_528_000 picoseconds. - Weight::from_parts(9_301_010, 0) - // Standard Error: 98 - .saturating_add(Weight::from_parts(53_173, 0).saturating_mul(r.into())) + // Minimum execution time: 6_979_000 picoseconds. + Weight::from_parts(8_272_348, 0) + // Standard Error: 137 + .saturating_add(Weight::from_parts(172_489, 0).saturating_mul(r.into())) } fn seal_caller() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 643_000 picoseconds. - Weight::from_parts(678_000, 0) + // Minimum execution time: 251_000 picoseconds. + Weight::from_parts(318_000, 0) } - /// Storage: `Contracts::ContractInfoOf` (r:1 w:0) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `Revive::ContractInfoOf` (r:1 w:0) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) fn seal_is_contract() -> Weight { // Proof Size summary in bytes: - // Measured: `354` - // Estimated: `3819` - // Minimum execution time: 6_107_000 picoseconds. - Weight::from_parts(6_235_000, 3819) + // Measured: `272` + // Estimated: `3737` + // Minimum execution time: 6_966_000 picoseconds. + Weight::from_parts(7_240_000, 3737) .saturating_add(T::DbWeight::get().reads(1_u64)) } - /// Storage: `Contracts::ContractInfoOf` (r:1 w:0) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `Revive::ContractInfoOf` (r:1 w:0) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) fn seal_code_hash() -> Weight { // Proof Size summary in bytes: - // Measured: `447` - // Estimated: `3912` - // Minimum execution time: 7_316_000 picoseconds. - Weight::from_parts(7_653_000, 3912) + // Measured: `369` + // Estimated: `3834` + // Minimum execution time: 7_589_000 picoseconds. + Weight::from_parts(7_958_000, 3834) .saturating_add(T::DbWeight::get().reads(1_u64)) } fn seal_own_code_hash() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 721_000 picoseconds. - Weight::from_parts(764_000, 0) + // Minimum execution time: 235_000 picoseconds. + Weight::from_parts(285_000, 0) } fn seal_caller_is_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 369_000 picoseconds. - Weight::from_parts(417_000, 0) + // Minimum execution time: 283_000 picoseconds. + Weight::from_parts(326_000, 0) } fn seal_caller_is_root() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 318_000 picoseconds. - Weight::from_parts(349_000, 0) + // Minimum execution time: 266_000 picoseconds. + Weight::from_parts(298_000, 0) } fn seal_address() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 590_000 picoseconds. - Weight::from_parts(628_000, 0) + // Minimum execution time: 240_000 picoseconds. + Weight::from_parts(290_000, 0) } - fn seal_gas_left() -> Weight { + fn seal_weight_left() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 660_000 picoseconds. - Weight::from_parts(730_000, 0) + // Minimum execution time: 651_000 picoseconds. + Weight::from_parts(714_000, 0) } fn seal_balance() -> Weight { // Proof Size summary in bytes: - // Measured: `140` + // Measured: `103` + // Estimated: `0` + // Minimum execution time: 4_476_000 picoseconds. + Weight::from_parts(4_671_000, 0) + } + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + fn seal_balance_of() -> Weight { + // Proof Size summary in bytes: + // Measured: `52` + // Estimated: `3517` + // Minimum execution time: 3_800_000 picoseconds. + Weight::from_parts(3_968_000, 3517) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `Revive::ImmutableDataOf` (r:1 w:0) + /// Proof: `Revive::ImmutableDataOf` (`max_values`: None, `max_size`: Some(4118), added: 6593, mode: `Measured`) + /// The range of component `n` is `[1, 4096]`. + fn seal_get_immutable_data(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `205 + n * (1 ยฑ0)` + // Estimated: `3670 + n * (1 ยฑ0)` + // Minimum execution time: 5_845_000 picoseconds. + Weight::from_parts(6_473_478, 3670) + // Standard Error: 4 + .saturating_add(Weight::from_parts(651, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) + } + /// Storage: `Revive::ImmutableDataOf` (r:0 w:1) + /// Proof: `Revive::ImmutableDataOf` (`max_values`: None, `max_size`: Some(4118), added: 6593, mode: `Measured`) + /// The range of component `n` is `[1, 4096]`. + fn seal_set_immutable_data(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_361_000 picoseconds. - Weight::from_parts(4_577_000, 0) + // Minimum execution time: 1_980_000 picoseconds. + Weight::from_parts(2_324_567, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(512, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().writes(1_u64)) } fn seal_value_transferred() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 560_000 picoseconds. - Weight::from_parts(603_000, 0) + // Minimum execution time: 259_000 picoseconds. + Weight::from_parts(285_000, 0) } fn seal_minimum_balance() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 561_000 picoseconds. - Weight::from_parts(610_000, 0) + // Minimum execution time: 244_000 picoseconds. + Weight::from_parts(291_000, 0) } fn seal_block_number() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 557_000 picoseconds. - Weight::from_parts(583_000, 0) + // Minimum execution time: 252_000 picoseconds. + Weight::from_parts(291_000, 0) } fn seal_now() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 550_000 picoseconds. - Weight::from_parts(602_000, 0) + // Minimum execution time: 245_000 picoseconds. + Weight::from_parts(277_000, 0) } /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `Measured`) @@ -634,117 +431,104 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `67` // Estimated: `1552` - // Minimum execution time: 4_065_000 picoseconds. - Weight::from_parts(4_291_000, 1552) + // Minimum execution time: 5_650_000 picoseconds. + Weight::from_parts(5_783_000, 1552) .saturating_add(T::DbWeight::get().reads(1_u64)) } - /// The range of component `n` is `[0, 1048572]`. + /// The range of component `n` is `[0, 262140]`. fn seal_input(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 487_000 picoseconds. - Weight::from_parts(517_000, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(301, 0).saturating_mul(n.into())) + // Minimum execution time: 427_000 picoseconds. + Weight::from_parts(351_577, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(114, 0).saturating_mul(n.into())) } - /// The range of component `n` is `[0, 1048572]`. + /// The range of component `n` is `[0, 262140]`. fn seal_return(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 318_000 picoseconds. - Weight::from_parts(372_000, 0) - // Standard Error: 10 - .saturating_add(Weight::from_parts(411, 0).saturating_mul(n.into())) - } - /// Storage: `Contracts::DeletionQueueCounter` (r:1 w:1) - /// Proof: `Contracts::DeletionQueueCounter` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) - /// Storage: `Contracts::CodeInfoOf` (r:33 w:33) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) - /// Storage: `Contracts::DeletionQueue` (r:0 w:1) - /// Proof: `Contracts::DeletionQueue` (`max_values`: None, `max_size`: Some(142), added: 2617, mode: `Measured`) + // Minimum execution time: 265_000 picoseconds. + Weight::from_parts(746_316, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(202, 0).saturating_mul(n.into())) + } + /// Storage: `Revive::DeletionQueueCounter` (r:1 w:1) + /// Proof: `Revive::DeletionQueueCounter` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `Revive::CodeInfoOf` (r:33 w:33) + /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) + /// Storage: `Revive::DeletionQueue` (r:0 w:1) + /// Proof: `Revive::DeletionQueue` (`max_values`: None, `max_size`: Some(142), added: 2617, mode: `Measured`) + /// Storage: `Revive::ImmutableDataOf` (r:0 w:1) + /// Proof: `Revive::ImmutableDataOf` (`max_values`: None, `max_size`: Some(4118), added: 6593, mode: `Measured`) /// The range of component `n` is `[0, 32]`. fn seal_terminate(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `319 + n * (78 ยฑ0)` - // Estimated: `3784 + n * (2553 ยฑ0)` - // Minimum execution time: 13_251_000 picoseconds. - Weight::from_parts(15_257_892, 3784) - // Standard Error: 7_089 - .saturating_add(Weight::from_parts(3_443_907, 0).saturating_mul(n.into())) + // Measured: `272 + n * (88 ยฑ0)` + // Estimated: `3737 + n * (2563 ยฑ0)` + // Minimum execution time: 15_988_000 picoseconds. + Weight::from_parts(18_796_705, 3737) + // Standard Error: 10_437 + .saturating_add(Weight::from_parts(4_338_085, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(n.into()))) - .saturating_add(T::DbWeight::get().writes(3_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 2553).saturating_mul(n.into())) - } - /// Storage: `RandomnessCollectiveFlip::RandomMaterial` (r:1 w:0) - /// Proof: `RandomnessCollectiveFlip::RandomMaterial` (`max_values`: Some(1), `max_size`: Some(2594), added: 3089, mode: `Measured`) - fn seal_random() -> Weight { - // Proof Size summary in bytes: - // Measured: `76` - // Estimated: `1561` - // Minimum execution time: 3_434_000 picoseconds. - Weight::from_parts(3_605_000, 1561) - .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(Weight::from_parts(0, 2563).saturating_mul(n.into())) } - /// Storage: `System::EventTopics` (r:4 w:4) - /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `t` is `[0, 4]`. - /// The range of component `n` is `[0, 16384]`. + /// The range of component `n` is `[0, 512]`. fn seal_deposit_event(t: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` - // Estimated: `990 + t * (2475 ยฑ0)` - // Minimum execution time: 3_668_000 picoseconds. - Weight::from_parts(3_999_591, 990) - // Standard Error: 5_767 - .saturating_add(Weight::from_parts(2_011_090, 0).saturating_mul(t.into())) - // Standard Error: 1 - .saturating_add(Weight::from_parts(12, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_parts(0, 2475).saturating_mul(t.into())) + // Estimated: `0` + // Minimum execution time: 4_237_000 picoseconds. + Weight::from_parts(4_128_112, 0) + // Standard Error: 2_947 + .saturating_add(Weight::from_parts(198_825, 0).saturating_mul(t.into())) + // Standard Error: 26 + .saturating_add(Weight::from_parts(1_007, 0).saturating_mul(n.into())) } - /// The range of component `i` is `[0, 1048576]`. + /// The range of component `i` is `[0, 262144]`. fn seal_debug_message(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 443_000 picoseconds. - Weight::from_parts(472_000, 0) - // Standard Error: 10 - .saturating_add(Weight::from_parts(1_207, 0).saturating_mul(i.into())) + // Minimum execution time: 292_000 picoseconds. + Weight::from_parts(1_297_376, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(724, 0).saturating_mul(i.into())) } /// Storage: `Skipped::Metadata` (r:0 w:0) /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) fn get_storage_empty() -> Weight { // Proof Size summary in bytes: - // Measured: `16618` - // Estimated: `16618` - // Minimum execution time: 13_752_000 picoseconds. - Weight::from_parts(14_356_000, 16618) + // Measured: `744` + // Estimated: `744` + // Minimum execution time: 7_812_000 picoseconds. + Weight::from_parts(8_171_000, 744) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) fn get_storage_full() -> Weight { // Proof Size summary in bytes: - // Measured: `26628` - // Estimated: `26628` - // Minimum execution time: 43_444_000 picoseconds. - Weight::from_parts(45_087_000, 26628) + // Measured: `10754` + // Estimated: `10754` + // Minimum execution time: 44_179_000 picoseconds. + Weight::from_parts(45_068_000, 10754) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) fn set_storage_empty() -> Weight { // Proof Size summary in bytes: - // Measured: `16618` - // Estimated: `16618` - // Minimum execution time: 15_616_000 picoseconds. - Weight::from_parts(16_010_000, 16618) + // Measured: `744` + // Estimated: `744` + // Minimum execution time: 8_964_000 picoseconds. + Weight::from_parts(9_336_000, 744) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -752,85 +536,85 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) fn set_storage_full() -> Weight { // Proof Size summary in bytes: - // Measured: `26628` - // Estimated: `26628` - // Minimum execution time: 47_020_000 picoseconds. - Weight::from_parts(50_152_000, 26628) + // Measured: `10754` + // Estimated: `10754` + // Minimum execution time: 45_606_000 picoseconds. + Weight::from_parts(47_190_000, 10754) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// The range of component `n` is `[0, 16384]`. - /// The range of component `o` is `[0, 16384]`. + /// The range of component `n` is `[0, 512]`. + /// The range of component `o` is `[0, 512]`. fn seal_set_storage(n: u32, o: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `250 + o * (1 ยฑ0)` - // Estimated: `249 + o * (1 ยฑ0)` - // Minimum execution time: 8_824_000 picoseconds. - Weight::from_parts(8_915_233, 249) - // Standard Error: 1 - .saturating_add(Weight::from_parts(255, 0).saturating_mul(n.into())) - // Standard Error: 1 - .saturating_add(Weight::from_parts(39, 0).saturating_mul(o.into())) + // Measured: `248 + o * (1 ยฑ0)` + // Estimated: `247 + o * (1 ยฑ0)` + // Minimum execution time: 9_077_000 picoseconds. + Weight::from_parts(9_823_489, 247) + // Standard Error: 54 + .saturating_add(Weight::from_parts(392, 0).saturating_mul(n.into())) + // Standard Error: 54 + .saturating_add(Weight::from_parts(408, 0).saturating_mul(o.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(o.into())) } /// Storage: `Skipped::Metadata` (r:0 w:0) /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// The range of component `n` is `[0, 16384]`. + /// The range of component `n` is `[0, 512]`. fn seal_clear_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `248 + n * (1 ยฑ0)` - // Estimated: `248 + n * (1 ยฑ0)` - // Minimum execution time: 7_133_000 picoseconds. - Weight::from_parts(7_912_778, 248) - // Standard Error: 1 - .saturating_add(Weight::from_parts(88, 0).saturating_mul(n.into())) + // Estimated: `247 + n * (1 ยฑ0)` + // Minimum execution time: 8_812_000 picoseconds. + Weight::from_parts(9_626_925, 247) + // Standard Error: 77 + .saturating_add(Weight::from_parts(269, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } /// Storage: `Skipped::Metadata` (r:0 w:0) /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// The range of component `n` is `[0, 16384]`. + /// The range of component `n` is `[0, 512]`. fn seal_get_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `248 + n * (1 ยฑ0)` - // Estimated: `248 + n * (1 ยฑ0)` - // Minimum execution time: 6_746_000 picoseconds. - Weight::from_parts(7_647_236, 248) - // Standard Error: 2 - .saturating_add(Weight::from_parts(603, 0).saturating_mul(n.into())) + // Estimated: `247 + n * (1 ยฑ0)` + // Minimum execution time: 8_143_000 picoseconds. + Weight::from_parts(9_229_363, 247) + // Standard Error: 77 + .saturating_add(Weight::from_parts(1_198, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } /// Storage: `Skipped::Metadata` (r:0 w:0) /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// The range of component `n` is `[0, 16384]`. + /// The range of component `n` is `[0, 512]`. fn seal_contains_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `248 + n * (1 ยฑ0)` - // Estimated: `248 + n * (1 ยฑ0)` - // Minimum execution time: 6_247_000 picoseconds. - Weight::from_parts(6_952_661, 248) - // Standard Error: 1 - .saturating_add(Weight::from_parts(77, 0).saturating_mul(n.into())) + // Estimated: `247 + n * (1 ยฑ0)` + // Minimum execution time: 7_899_000 picoseconds. + Weight::from_parts(8_591_860, 247) + // Standard Error: 56 + .saturating_add(Weight::from_parts(461, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } /// Storage: `Skipped::Metadata` (r:0 w:0) /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// The range of component `n` is `[0, 16384]`. + /// The range of component `n` is `[0, 512]`. fn seal_take_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `248 + n * (1 ยฑ0)` - // Estimated: `248 + n * (1 ยฑ0)` - // Minimum execution time: 7_428_000 picoseconds. - Weight::from_parts(8_384_015, 248) - // Standard Error: 2 - .saturating_add(Weight::from_parts(625, 0).saturating_mul(n.into())) + // Estimated: `247 + n * (1 ยฑ0)` + // Minimum execution time: 9_215_000 picoseconds. + Weight::from_parts(10_198_528, 247) + // Standard Error: 75 + .saturating_add(Weight::from_parts(1_521, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -839,303 +623,270 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_478_000 picoseconds. - Weight::from_parts(1_533_000, 0) + // Minimum execution time: 1_406_000 picoseconds. + Weight::from_parts(1_515_000, 0) } fn set_transient_storage_full() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_485_000 picoseconds. - Weight::from_parts(2_728_000, 0) + // Minimum execution time: 1_782_000 picoseconds. + Weight::from_parts(1_890_000, 0) } fn get_transient_storage_empty() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_195_000 picoseconds. - Weight::from_parts(3_811_000, 0) + // Minimum execution time: 1_380_000 picoseconds. + Weight::from_parts(1_422_000, 0) } fn get_transient_storage_full() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_902_000 picoseconds. - Weight::from_parts(4_118_000, 0) + // Minimum execution time: 1_504_000 picoseconds. + Weight::from_parts(1_583_000, 0) } fn rollback_transient_storage() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_571_000 picoseconds. - Weight::from_parts(1_662_000, 0) + // Minimum execution time: 1_045_000 picoseconds. + Weight::from_parts(1_138_000, 0) } - /// The range of component `n` is `[0, 16384]`. - /// The range of component `o` is `[0, 16384]`. + /// The range of component `n` is `[0, 512]`. + /// The range of component `o` is `[0, 512]`. fn seal_set_transient_storage(n: u32, o: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_250_000 picoseconds. - Weight::from_parts(2_465_568, 0) - // Standard Error: 0 - .saturating_add(Weight::from_parts(201, 0).saturating_mul(n.into())) - // Standard Error: 0 - .saturating_add(Weight::from_parts(223, 0).saturating_mul(o.into())) + // Minimum execution time: 2_039_000 picoseconds. + Weight::from_parts(2_317_406, 0) + // Standard Error: 13 + .saturating_add(Weight::from_parts(283, 0).saturating_mul(n.into())) + // Standard Error: 13 + .saturating_add(Weight::from_parts(347, 0).saturating_mul(o.into())) } - /// The range of component `n` is `[0, 16384]`. + /// The range of component `n` is `[0, 512]`. fn seal_clear_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_012_000 picoseconds. - Weight::from_parts(2_288_004, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(239, 0).saturating_mul(n.into())) + // Minimum execution time: 1_880_000 picoseconds. + Weight::from_parts(2_251_392, 0) + // Standard Error: 17 + .saturating_add(Weight::from_parts(313, 0).saturating_mul(n.into())) } - /// The range of component `n` is `[0, 16384]`. + /// The range of component `n` is `[0, 512]`. fn seal_get_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_906_000 picoseconds. - Weight::from_parts(2_121_040, 0) - // Standard Error: 0 - .saturating_add(Weight::from_parts(225, 0).saturating_mul(n.into())) + // Minimum execution time: 1_763_000 picoseconds. + Weight::from_parts(1_951_912, 0) + // Standard Error: 13 + .saturating_add(Weight::from_parts(268, 0).saturating_mul(n.into())) } - /// The range of component `n` is `[0, 16384]`. + /// The range of component `n` is `[0, 512]`. fn seal_contains_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_736_000 picoseconds. - Weight::from_parts(1_954_728, 0) - // Standard Error: 0 - .saturating_add(Weight::from_parts(111, 0).saturating_mul(n.into())) + // Minimum execution time: 1_536_000 picoseconds. + Weight::from_parts(1_779_085, 0) + // Standard Error: 14 + .saturating_add(Weight::from_parts(148, 0).saturating_mul(n.into())) } - /// The range of component `n` is `[0, 16384]`. - fn seal_take_transient_storage(_n: u32, ) -> Weight { + /// The range of component `n` is `[0, 512]`. + fn seal_take_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_872_000 picoseconds. - Weight::from_parts(8_125_644, 0) + // Minimum execution time: 2_343_000 picoseconds. + Weight::from_parts(2_587_750, 0) + // Standard Error: 22 + .saturating_add(Weight::from_parts(30, 0).saturating_mul(n.into())) } fn seal_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `140` + // Measured: `103` // Estimated: `0` - // Minimum execution time: 8_489_000 picoseconds. - Weight::from_parts(8_791_000, 0) + // Minimum execution time: 9_250_000 picoseconds. + Weight::from_parts(9_637_000, 0) } - /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) - /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) - /// Storage: `Contracts::PristineCode` (r:1 w:0) - /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Revive::ContractInfoOf` (r:1 w:0) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) + /// Storage: `Revive::CodeInfoOf` (r:1 w:0) + /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) + /// Storage: `Revive::PristineCode` (r:1 w:0) + /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) /// The range of component `t` is `[0, 1]`. - /// The range of component `i` is `[0, 1048576]`. + /// The range of component `i` is `[0, 262144]`. fn seal_call(t: u32, i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `620 + t * (280 ยฑ0)` - // Estimated: `4085 + t * (2182 ยฑ0)` - // Minimum execution time: 122_759_000 picoseconds. - Weight::from_parts(120_016_020, 4085) - // Standard Error: 173_118 - .saturating_add(Weight::from_parts(42_848_338, 0).saturating_mul(t.into())) + // Measured: `1221 + t * (103 ยฑ0)` + // Estimated: `4686 + t * (103 ยฑ0)` + // Minimum execution time: 33_333_000 picoseconds. + Weight::from_parts(34_378_774, 4686) + // Standard Error: 41_131 + .saturating_add(Weight::from_parts(1_756_626, 0).saturating_mul(t.into())) // Standard Error: 0 - .saturating_add(Weight::from_parts(6, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(3, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_parts(0, 2182).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 103).saturating_mul(t.into())) } - /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) - /// Storage: `Contracts::PristineCode` (r:1 w:0) - /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// Storage: `Revive::CodeInfoOf` (r:1 w:0) + /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) + /// Storage: `Revive::PristineCode` (r:1 w:0) + /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) fn seal_delegate_call() -> Weight { // Proof Size summary in bytes: - // Measured: `430` - // Estimated: `3895` - // Minimum execution time: 111_566_000 picoseconds. - Weight::from_parts(115_083_000, 3895) + // Measured: `1048` + // Estimated: `4513` + // Minimum execution time: 27_096_000 picoseconds. + Weight::from_parts(27_934_000, 4513) .saturating_add(T::DbWeight::get().reads(2_u64)) } - /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) - /// Storage: `Contracts::PristineCode` (r:1 w:0) - /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) - /// Storage: `Contracts::Nonce` (r:1 w:0) - /// Proof: `Contracts::Nonce` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) - /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `Revive::CodeInfoOf` (r:1 w:1) + /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) + /// Storage: `Revive::PristineCode` (r:1 w:0) + /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) + /// Storage: `Revive::ContractInfoOf` (r:1 w:1) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) - /// The range of component `i` is `[0, 983040]`. - /// The range of component `s` is `[0, 983040]`. - fn seal_instantiate(i: u32, s: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `676` - // Estimated: `4132` - // Minimum execution time: 1_871_402_000 picoseconds. - Weight::from_parts(1_890_038_000, 4132) - // Standard Error: 24 - .saturating_add(Weight::from_parts(581, 0).saturating_mul(i.into())) - // Standard Error: 24 - .saturating_add(Weight::from_parts(915, 0).saturating_mul(s.into())) - .saturating_add(T::DbWeight::get().reads(5_u64)) + /// The range of component `i` is `[0, 262144]`. + fn seal_instantiate(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1257` + // Estimated: `4715` + // Minimum execution time: 118_412_000 picoseconds. + Weight::from_parts(106_130_041, 4715) + // Standard Error: 12 + .saturating_add(Weight::from_parts(4_235, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// The range of component `n` is `[0, 1048576]`. + /// The range of component `n` is `[0, 262144]`. fn seal_hash_sha2_256(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 966_000 picoseconds. - Weight::from_parts(9_599_151, 0) - // Standard Error: 1 - .saturating_add(Weight::from_parts(1_336, 0).saturating_mul(n.into())) + // Minimum execution time: 614_000 picoseconds. + Weight::from_parts(4_320_897, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(1_371, 0).saturating_mul(n.into())) } - /// The range of component `n` is `[0, 1048576]`. + /// The range of component `n` is `[0, 262144]`. fn seal_hash_keccak_256(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_416_000 picoseconds. - Weight::from_parts(10_964_255, 0) - // Standard Error: 1 - .saturating_add(Weight::from_parts(3_593, 0).saturating_mul(n.into())) + // Minimum execution time: 1_062_000 picoseconds. + Weight::from_parts(4_571_371, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(3_572, 0).saturating_mul(n.into())) } - /// The range of component `n` is `[0, 1048576]`. + /// The range of component `n` is `[0, 262144]`. fn seal_hash_blake2_256(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 821_000 picoseconds. - Weight::from_parts(6_579_283, 0) - // Standard Error: 0 - .saturating_add(Weight::from_parts(1_466, 0).saturating_mul(n.into())) + // Minimum execution time: 609_000 picoseconds. + Weight::from_parts(4_008_056, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(1_497, 0).saturating_mul(n.into())) } - /// The range of component `n` is `[0, 1048576]`. + /// The range of component `n` is `[0, 262144]`. fn seal_hash_blake2_128(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 773_000 picoseconds. - Weight::from_parts(10_990_209, 0) - // Standard Error: 1 - .saturating_add(Weight::from_parts(1_457, 0).saturating_mul(n.into())) + // Minimum execution time: 608_000 picoseconds. + Weight::from_parts(3_839_383, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(1_504, 0).saturating_mul(n.into())) } - /// The range of component `n` is `[0, 125697]`. + /// The range of component `n` is `[0, 261889]`. fn seal_sr25519_verify(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 43_195_000 picoseconds. - Weight::from_parts(41_864_855, 0) - // Standard Error: 9 - .saturating_add(Weight::from_parts(5_154, 0).saturating_mul(n.into())) + // Minimum execution time: 43_110_000 picoseconds. + Weight::from_parts(31_941_593, 0) + // Standard Error: 12 + .saturating_add(Weight::from_parts(5_233, 0).saturating_mul(n.into())) } fn seal_ecdsa_recover() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 47_747_000 picoseconds. - Weight::from_parts(49_219_000, 0) + // Minimum execution time: 47_798_000 picoseconds. + Weight::from_parts(49_225_000, 0) } fn seal_ecdsa_to_eth_address() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 12_854_000 picoseconds. - Weight::from_parts(12_962_000, 0) + // Minimum execution time: 12_576_000 picoseconds. + Weight::from_parts(12_731_000, 0) } - /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) - /// Storage: `Contracts::PristineCode` (r:1 w:0) - /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// Storage: `Revive::CodeInfoOf` (r:1 w:1) + /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) fn seal_set_code_hash() -> Weight { // Proof Size summary in bytes: - // Measured: `430` - // Estimated: `3895` - // Minimum execution time: 17_868_000 picoseconds. - Weight::from_parts(18_486_000, 3895) - .saturating_add(T::DbWeight::get().reads(2_u64)) + // Measured: `266` + // Estimated: `3731` + // Minimum execution time: 14_306_000 picoseconds. + Weight::from_parts(15_011_000, 3731) + .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Revive::CodeInfoOf` (r:1 w:1) + /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) fn lock_delegate_dependency() -> Weight { // Proof Size summary in bytes: - // Measured: `355` - // Estimated: `3820` - // Minimum execution time: 8_393_000 picoseconds. - Weight::from_parts(8_640_000, 3820) + // Measured: `303` + // Estimated: `3768` + // Minimum execution time: 10_208_000 picoseconds. + Weight::from_parts(10_514_000, 3768) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `MaxEncodedLen`) + /// Storage: `Revive::CodeInfoOf` (r:1 w:1) + /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `MaxEncodedLen`) fn unlock_delegate_dependency() -> Weight { // Proof Size summary in bytes: - // Measured: `355` - // Estimated: `3558` - // Minimum execution time: 7_489_000 picoseconds. - Weight::from_parts(7_815_000, 3558) + // Measured: `303` + // Estimated: `3561` + // Minimum execution time: 9_062_000 picoseconds. + Weight::from_parts(9_414_000, 3561) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - fn seal_reentrance_count() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 299_000 picoseconds. - Weight::from_parts(339_000, 0) - } - fn seal_account_reentrance_count() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 324_000 picoseconds. - Weight::from_parts(380_000, 0) - } - /// Storage: `Contracts::Nonce` (r:1 w:0) - /// Proof: `Contracts::Nonce` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) - fn seal_instantiation_nonce() -> Weight { - // Proof Size summary in bytes: - // Measured: `219` - // Estimated: `1704` - // Minimum execution time: 2_768_000 picoseconds. - Weight::from_parts(3_025_000, 1704) - .saturating_add(T::DbWeight::get().reads(1_u64)) - } /// The range of component `r` is `[0, 5000]`. - fn instr_i64_load_store(r: u32, ) -> Weight { + fn instr(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 766_000 picoseconds. - Weight::from_parts(722_169, 0) - // Standard Error: 10 - .saturating_add(Weight::from_parts(7_191, 0).saturating_mul(r.into())) + // Minimum execution time: 8_074_000 picoseconds. + Weight::from_parts(9_646_158, 0) + // Standard Error: 58 + .saturating_add(Weight::from_parts(84_694, 0).saturating_mul(r.into())) } } // For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: `Contracts::DeletionQueueCounter` (r:1 w:0) - /// Proof: `Contracts::DeletionQueueCounter` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `Revive::DeletionQueueCounter` (r:1 w:0) + /// Proof: `Revive::DeletionQueueCounter` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) fn on_process_deletion_queue_batch() -> Weight { // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `1627` - // Minimum execution time: 1_915_000 picoseconds. - Weight::from_parts(1_986_000, 1627) + // Measured: `109` + // Estimated: `1594` + // Minimum execution time: 2_712_000 picoseconds. + Weight::from_parts(2_882_000, 1594) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -1143,375 +894,150 @@ impl WeightInfo for () { /// The range of component `k` is `[0, 1024]`. fn on_initialize_per_trie_key(k: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `452 + k * (69 ยฑ0)` - // Estimated: `442 + k * (70 ยฑ0)` - // Minimum execution time: 11_103_000 picoseconds. - Weight::from_parts(11_326_000, 442) - // Standard Error: 2_291 - .saturating_add(Weight::from_parts(1_196_329, 0).saturating_mul(k.into())) + // Measured: `392 + k * (69 ยฑ0)` + // Estimated: `382 + k * (70 ยฑ0)` + // Minimum execution time: 13_394_000 picoseconds. + Weight::from_parts(13_668_000, 382) + // Standard Error: 2_208 + .saturating_add(Weight::from_parts(1_340_842, 0).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(k.into()))) .saturating_add(Weight::from_parts(0, 70).saturating_mul(k.into())) } - /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553022fca90611ba8b7942f8bdb3b97f6580` (r:2 w:1) - /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553022fca90611ba8b7942f8bdb3b97f6580` (r:2 w:1) - /// The range of component `c` is `[0, 125952]`. - fn v9_migration_step(c: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `211 + c * (1 ยฑ0)` - // Estimated: `6149 + c * (1 ยฑ0)` - // Minimum execution time: 7_783_000 picoseconds. - Weight::from_parts(4_462_075, 6149) - // Standard Error: 5 - .saturating_add(Weight::from_parts(1_634, 0).saturating_mul(c.into())) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) - } - /// Storage: `Contracts::ContractInfoOf` (r:2 w:1) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) - /// Storage: `System::Account` (r:1 w:0) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) - fn v10_migration_step() -> Weight { - // Proof Size summary in bytes: - // Measured: `510` - // Estimated: `6450` - // Minimum execution time: 15_971_000 picoseconds. - Weight::from_parts(16_730_000, 6450) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `Contracts::DeletionQueue` (r:1 w:1025) - /// Proof: `Contracts::DeletionQueue` (`max_values`: None, `max_size`: Some(142), added: 2617, mode: `Measured`) - /// Storage: `Contracts::DeletionQueueCounter` (r:0 w:1) - /// Proof: `Contracts::DeletionQueueCounter` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) - /// The range of component `k` is `[0, 1024]`. - fn v11_migration_step(k: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `171 + k * (1 ยฑ0)` - // Estimated: `3635 + k * (1 ยฑ0)` - // Minimum execution time: 3_149_000 picoseconds. - Weight::from_parts(3_264_000, 3635) - // Standard Error: 559 - .saturating_add(Weight::from_parts(1_111_209, 0).saturating_mul(k.into())) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(k.into()))) - .saturating_add(Weight::from_parts(0, 1).saturating_mul(k.into())) - } - /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553053f13fd319a03c211337c76e0fe776df` (r:2 w:0) - /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553053f13fd319a03c211337c76e0fe776df` (r:2 w:0) - /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553022fca90611ba8b7942f8bdb3b97f6580` (r:1 w:1) - /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc553022fca90611ba8b7942f8bdb3b97f6580` (r:1 w:1) - /// Storage: `System::Account` (r:1 w:0) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) - /// Storage: `Contracts::CodeInfoOf` (r:0 w:1) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) - /// The range of component `c` is `[0, 125952]`. - fn v12_migration_step(c: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `325 + c * (1 ยฑ0)` - // Estimated: `6263 + c * (1 ยฑ0)` - // Minimum execution time: 15_072_000 picoseconds. - Weight::from_parts(15_721_891, 6263) - // Standard Error: 2 - .saturating_add(Weight::from_parts(428, 0).saturating_mul(c.into())) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) - } - /// Storage: `Contracts::ContractInfoOf` (r:2 w:1) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) - fn v13_migration_step() -> Weight { - // Proof Size summary in bytes: - // Measured: `440` - // Estimated: `6380` - // Minimum execution time: 12_047_000 picoseconds. - Weight::from_parts(12_500_000, 6380) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `Contracts::CodeInfoOf` (r:2 w:0) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) - /// Storage: `Balances::Holds` (r:1 w:0) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) - fn v14_migration_step() -> Weight { - // Proof Size summary in bytes: - // Measured: `352` - // Estimated: `6292` - // Minimum execution time: 47_488_000 picoseconds. - Weight::from_parts(48_482_000, 6292) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `Contracts::ContractInfoOf` (r:2 w:1) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) - /// Storage: `System::Account` (r:2 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) - fn v15_migration_step() -> Weight { - // Proof Size summary in bytes: - // Measured: `594` - // Estimated: `6534` - // Minimum execution time: 52_801_000 picoseconds. - Weight::from_parts(54_230_000, 6534) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } - /// Storage: `Contracts::ContractInfoOf` (r:2 w:1) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) - fn v16_migration_step() -> Weight { - // Proof Size summary in bytes: - // Measured: `409` - // Estimated: `6349` - // Minimum execution time: 11_618_000 picoseconds. - Weight::from_parts(12_068_000, 6349) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `Contracts::MigrationInProgress` (r:1 w:1) - /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) - fn migration_noop() -> Weight { - // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `1627` - // Minimum execution time: 2_131_000 picoseconds. - Weight::from_parts(2_255_000, 1627) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `Contracts::MigrationInProgress` (r:1 w:1) - /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) - /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:1) - /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:1) - fn migrate() -> Weight { - // Proof Size summary in bytes: - // Measured: `166` - // Estimated: `3631` - // Minimum execution time: 10_773_000 picoseconds. - Weight::from_parts(11_118_000, 3631) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } - /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) - /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) - fn on_runtime_upgrade_noop() -> Weight { - // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `3607` - // Minimum execution time: 4_371_000 picoseconds. - Weight::from_parts(4_624_000, 3607) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - } - /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) - /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) - /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) - /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) - fn on_runtime_upgrade_in_progress() -> Weight { - // Proof Size summary in bytes: - // Measured: `167` - // Estimated: `3632` - // Minimum execution time: 5_612_000 picoseconds. - Weight::from_parts(5_838_000, 3632) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - } - /// Storage: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) - /// Proof: UNKNOWN KEY `0x4342193e496fab7ec59d615ed0dc55304e7b9012096b41c4eb3aaf947f6ea429` (r:1 w:0) - /// Storage: `Contracts::MigrationInProgress` (r:1 w:1) - /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) - fn on_runtime_upgrade() -> Weight { - // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `3607` - // Minimum execution time: 5_487_000 picoseconds. - Weight::from_parts(5_693_000, 3607) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) - /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) - /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) - /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) - /// Storage: `Contracts::PristineCode` (r:1 w:0) - /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// Storage: `Revive::ContractInfoOf` (r:1 w:1) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) + /// Storage: `Revive::CodeInfoOf` (r:1 w:0) + /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) + /// Storage: `Revive::PristineCode` (r:1 w:0) + /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) /// Storage: `Timestamp::Now` (r:1 w:0) /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) - /// The range of component `c` is `[0, 125952]`. + /// The range of component `c` is `[0, 262144]`. fn call_with_code_per_byte(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `800 + c * (1 ยฑ0)` - // Estimated: `4266 + c * (1 ยฑ0)` - // Minimum execution time: 247_545_000 picoseconds. - Weight::from_parts(268_016_699, 4266) - // Standard Error: 4 - .saturating_add(Weight::from_parts(700, 0).saturating_mul(c.into())) - .saturating_add(RocksDbWeight::get().reads(6_u64)) + // Measured: `1466` + // Estimated: `4931` + // Minimum execution time: 80_390_000 picoseconds. + Weight::from_parts(83_627_295, 4931) + // Standard Error: 0 + .saturating_add(Weight::from_parts(1, 0).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) - .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) } - /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) - /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) - /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Revive::CodeInfoOf` (r:1 w:1) + /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) /// Storage: `Balances::Holds` (r:2 w:2) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) - /// Storage: `Contracts::Nonce` (r:1 w:1) - /// Proof: `Contracts::Nonce` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) - /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `Measured`) + /// Storage: `Revive::ContractInfoOf` (r:1 w:1) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Timestamp::Now` (r:1 w:0) /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) - /// Storage: `Contracts::PristineCode` (r:0 w:1) - /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) - /// The range of component `c` is `[0, 125952]`. - /// The range of component `i` is `[0, 1048576]`. - /// The range of component `s` is `[0, 1048576]`. - fn instantiate_with_code(c: u32, i: u32, s: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `323` - // Estimated: `6262` - // Minimum execution time: 4_396_772_000 picoseconds. - Weight::from_parts(235_107_907, 6262) - // Standard Error: 185 - .saturating_add(Weight::from_parts(53_843, 0).saturating_mul(c.into())) - // Standard Error: 22 - .saturating_add(Weight::from_parts(2_143, 0).saturating_mul(i.into())) - // Standard Error: 22 - .saturating_add(Weight::from_parts(2_210, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) - .saturating_add(RocksDbWeight::get().writes(7_u64)) - } - /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) - /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) - /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) - /// Storage: `Contracts::PristineCode` (r:1 w:0) - /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) - /// Storage: `Contracts::Nonce` (r:1 w:1) - /// Proof: `Contracts::Nonce` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) - /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `Revive::PristineCode` (r:0 w:1) + /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) + /// The range of component `c` is `[0, 262144]`. + /// The range of component `i` is `[0, 262144]`. + fn instantiate_with_code(_c: u32, i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `303` + // Estimated: `6232` + // Minimum execution time: 184_708_000 picoseconds. + Weight::from_parts(177_995_416, 6232) + // Standard Error: 11 + .saturating_add(Weight::from_parts(4_609, 0).saturating_mul(i.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + /// Storage: `Revive::CodeInfoOf` (r:1 w:1) + /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) + /// Storage: `Revive::PristineCode` (r:1 w:0) + /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) + /// Storage: `Revive::ContractInfoOf` (r:1 w:1) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `Timestamp::Now` (r:1 w:0) /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) - /// The range of component `i` is `[0, 1048576]`. - /// The range of component `s` is `[0, 1048576]`. - fn instantiate(i: u32, s: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `560` - // Estimated: `4017` - // Minimum execution time: 2_240_868_000 picoseconds. - Weight::from_parts(2_273_668_000, 4017) - // Standard Error: 32 - .saturating_add(Weight::from_parts(934, 0).saturating_mul(i.into())) - // Standard Error: 32 - .saturating_add(Weight::from_parts(920, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(8_u64)) - .saturating_add(RocksDbWeight::get().writes(5_u64)) - } - /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) - /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) - /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) - /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) - /// Storage: `Contracts::PristineCode` (r:1 w:0) - /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `Measured`) + /// The range of component `i` is `[0, 262144]`. + fn instantiate(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1261` + // Estimated: `4706` + // Minimum execution time: 150_137_000 picoseconds. + Weight::from_parts(136_548_469, 4706) + // Standard Error: 16 + .saturating_add(Weight::from_parts(4_531, 0).saturating_mul(i.into())) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) + } + /// Storage: `Revive::ContractInfoOf` (r:1 w:1) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) + /// Storage: `Revive::CodeInfoOf` (r:1 w:0) + /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) + /// Storage: `Revive::PristineCode` (r:1 w:0) + /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) /// Storage: `Timestamp::Now` (r:1 w:0) /// Proof: `Timestamp::Now` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) fn call() -> Weight { // Proof Size summary in bytes: - // Measured: `826` - // Estimated: `4291` - // Minimum execution time: 165_067_000 picoseconds. - Weight::from_parts(168_582_000, 4291) - .saturating_add(RocksDbWeight::get().reads(6_u64)) + // Measured: `1466` + // Estimated: `4931` + // Minimum execution time: 83_178_000 picoseconds. + Weight::from_parts(84_633_000, 4931) + .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) - /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) - /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) - /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) - /// Storage: `Contracts::PristineCode` (r:0 w:1) - /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) - /// The range of component `c` is `[0, 125952]`. - fn upload_code_determinism_enforced(c: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `3607` - // Minimum execution time: 229_454_000 picoseconds. - Weight::from_parts(251_495_551, 3607) - // Standard Error: 71 - .saturating_add(Weight::from_parts(51_428, 0).saturating_mul(c.into())) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) - } - /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) - /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) - /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Revive::CodeInfoOf` (r:1 w:1) + /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) - /// Storage: `Contracts::PristineCode` (r:0 w:1) - /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) - /// The range of component `c` is `[0, 125952]`. - fn upload_code_determinism_relaxed(c: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `142` - // Estimated: `3607` - // Minimum execution time: 240_390_000 picoseconds. - Weight::from_parts(273_854_266, 3607) - // Standard Error: 243 - .saturating_add(Weight::from_parts(51_836, 0).saturating_mul(c.into())) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `Measured`) + /// Storage: `Revive::PristineCode` (r:0 w:1) + /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) + /// The range of component `c` is `[0, 262144]`. + fn upload_code(_c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `3574` + // Minimum execution time: 51_526_000 picoseconds. + Weight::from_parts(54_565_973, 3574) + .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) - /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) - /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Revive::CodeInfoOf` (r:1 w:1) + /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) /// Storage: `Balances::Holds` (r:1 w:1) - /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(193), added: 2668, mode: `Measured`) - /// Storage: `Contracts::PristineCode` (r:0 w:1) - /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(337), added: 2812, mode: `Measured`) + /// Storage: `Revive::PristineCode` (r:0 w:1) + /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) fn remove_code() -> Weight { // Proof Size summary in bytes: - // Measured: `315` - // Estimated: `3780` - // Minimum execution time: 39_374_000 picoseconds. - Weight::from_parts(40_247_000, 3780) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Measured: `285` + // Estimated: `3750` + // Minimum execution time: 41_885_000 picoseconds. + Weight::from_parts(42_467_000, 3750) + .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: `Contracts::MigrationInProgress` (r:1 w:0) - /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) - /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) - /// Storage: `Contracts::CodeInfoOf` (r:2 w:2) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Revive::ContractInfoOf` (r:1 w:1) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) + /// Storage: `Revive::CodeInfoOf` (r:2 w:2) + /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) fn set_code() -> Weight { // Proof Size summary in bytes: - // Measured: `552` - // Estimated: `6492` - // Minimum execution time: 24_473_000 picoseconds. - Weight::from_parts(25_890_000, 6492) - .saturating_add(RocksDbWeight::get().reads(4_u64)) + // Measured: `492` + // Estimated: `6432` + // Minimum execution time: 24_905_000 picoseconds. + Weight::from_parts(25_483_000, 6432) + .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } /// The range of component `r` is `[0, 1600]`. @@ -1519,107 +1045,144 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 8_528_000 picoseconds. - Weight::from_parts(9_301_010, 0) - // Standard Error: 98 - .saturating_add(Weight::from_parts(53_173, 0).saturating_mul(r.into())) + // Minimum execution time: 6_979_000 picoseconds. + Weight::from_parts(8_272_348, 0) + // Standard Error: 137 + .saturating_add(Weight::from_parts(172_489, 0).saturating_mul(r.into())) } fn seal_caller() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 643_000 picoseconds. - Weight::from_parts(678_000, 0) + // Minimum execution time: 251_000 picoseconds. + Weight::from_parts(318_000, 0) } - /// Storage: `Contracts::ContractInfoOf` (r:1 w:0) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `Revive::ContractInfoOf` (r:1 w:0) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) fn seal_is_contract() -> Weight { // Proof Size summary in bytes: - // Measured: `354` - // Estimated: `3819` - // Minimum execution time: 6_107_000 picoseconds. - Weight::from_parts(6_235_000, 3819) + // Measured: `272` + // Estimated: `3737` + // Minimum execution time: 6_966_000 picoseconds. + Weight::from_parts(7_240_000, 3737) .saturating_add(RocksDbWeight::get().reads(1_u64)) } - /// Storage: `Contracts::ContractInfoOf` (r:1 w:0) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `Revive::ContractInfoOf` (r:1 w:0) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) fn seal_code_hash() -> Weight { // Proof Size summary in bytes: - // Measured: `447` - // Estimated: `3912` - // Minimum execution time: 7_316_000 picoseconds. - Weight::from_parts(7_653_000, 3912) + // Measured: `369` + // Estimated: `3834` + // Minimum execution time: 7_589_000 picoseconds. + Weight::from_parts(7_958_000, 3834) .saturating_add(RocksDbWeight::get().reads(1_u64)) } fn seal_own_code_hash() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 721_000 picoseconds. - Weight::from_parts(764_000, 0) + // Minimum execution time: 235_000 picoseconds. + Weight::from_parts(285_000, 0) } fn seal_caller_is_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 369_000 picoseconds. - Weight::from_parts(417_000, 0) + // Minimum execution time: 283_000 picoseconds. + Weight::from_parts(326_000, 0) } fn seal_caller_is_root() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 318_000 picoseconds. - Weight::from_parts(349_000, 0) + // Minimum execution time: 266_000 picoseconds. + Weight::from_parts(298_000, 0) } fn seal_address() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 590_000 picoseconds. - Weight::from_parts(628_000, 0) + // Minimum execution time: 240_000 picoseconds. + Weight::from_parts(290_000, 0) } - fn seal_gas_left() -> Weight { + fn seal_weight_left() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 660_000 picoseconds. - Weight::from_parts(730_000, 0) + // Minimum execution time: 651_000 picoseconds. + Weight::from_parts(714_000, 0) } fn seal_balance() -> Weight { // Proof Size summary in bytes: - // Measured: `140` + // Measured: `103` + // Estimated: `0` + // Minimum execution time: 4_476_000 picoseconds. + Weight::from_parts(4_671_000, 0) + } + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + fn seal_balance_of() -> Weight { + // Proof Size summary in bytes: + // Measured: `52` + // Estimated: `3517` + // Minimum execution time: 3_800_000 picoseconds. + Weight::from_parts(3_968_000, 3517) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: `Revive::ImmutableDataOf` (r:1 w:0) + /// Proof: `Revive::ImmutableDataOf` (`max_values`: None, `max_size`: Some(4118), added: 6593, mode: `Measured`) + /// The range of component `n` is `[1, 4096]`. + fn seal_get_immutable_data(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `205 + n * (1 ยฑ0)` + // Estimated: `3670 + n * (1 ยฑ0)` + // Minimum execution time: 5_845_000 picoseconds. + Weight::from_parts(6_473_478, 3670) + // Standard Error: 4 + .saturating_add(Weight::from_parts(651, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) + } + /// Storage: `Revive::ImmutableDataOf` (r:0 w:1) + /// Proof: `Revive::ImmutableDataOf` (`max_values`: None, `max_size`: Some(4118), added: 6593, mode: `Measured`) + /// The range of component `n` is `[1, 4096]`. + fn seal_set_immutable_data(n: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_361_000 picoseconds. - Weight::from_parts(4_577_000, 0) + // Minimum execution time: 1_980_000 picoseconds. + Weight::from_parts(2_324_567, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(512, 0).saturating_mul(n.into())) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } fn seal_value_transferred() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 560_000 picoseconds. - Weight::from_parts(603_000, 0) + // Minimum execution time: 259_000 picoseconds. + Weight::from_parts(285_000, 0) } fn seal_minimum_balance() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 561_000 picoseconds. - Weight::from_parts(610_000, 0) + // Minimum execution time: 244_000 picoseconds. + Weight::from_parts(291_000, 0) } fn seal_block_number() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 557_000 picoseconds. - Weight::from_parts(583_000, 0) + // Minimum execution time: 252_000 picoseconds. + Weight::from_parts(291_000, 0) } fn seal_now() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 550_000 picoseconds. - Weight::from_parts(602_000, 0) + // Minimum execution time: 245_000 picoseconds. + Weight::from_parts(277_000, 0) } /// Storage: `TransactionPayment::NextFeeMultiplier` (r:1 w:0) /// Proof: `TransactionPayment::NextFeeMultiplier` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `Measured`) @@ -1627,117 +1190,104 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `67` // Estimated: `1552` - // Minimum execution time: 4_065_000 picoseconds. - Weight::from_parts(4_291_000, 1552) + // Minimum execution time: 5_650_000 picoseconds. + Weight::from_parts(5_783_000, 1552) .saturating_add(RocksDbWeight::get().reads(1_u64)) } - /// The range of component `n` is `[0, 1048572]`. + /// The range of component `n` is `[0, 262140]`. fn seal_input(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 487_000 picoseconds. - Weight::from_parts(517_000, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(301, 0).saturating_mul(n.into())) + // Minimum execution time: 427_000 picoseconds. + Weight::from_parts(351_577, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(114, 0).saturating_mul(n.into())) } - /// The range of component `n` is `[0, 1048572]`. + /// The range of component `n` is `[0, 262140]`. fn seal_return(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 318_000 picoseconds. - Weight::from_parts(372_000, 0) - // Standard Error: 10 - .saturating_add(Weight::from_parts(411, 0).saturating_mul(n.into())) - } - /// Storage: `Contracts::DeletionQueueCounter` (r:1 w:1) - /// Proof: `Contracts::DeletionQueueCounter` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) - /// Storage: `Contracts::CodeInfoOf` (r:33 w:33) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) - /// Storage: `Contracts::DeletionQueue` (r:0 w:1) - /// Proof: `Contracts::DeletionQueue` (`max_values`: None, `max_size`: Some(142), added: 2617, mode: `Measured`) + // Minimum execution time: 265_000 picoseconds. + Weight::from_parts(746_316, 0) + // Standard Error: 0 + .saturating_add(Weight::from_parts(202, 0).saturating_mul(n.into())) + } + /// Storage: `Revive::DeletionQueueCounter` (r:1 w:1) + /// Proof: `Revive::DeletionQueueCounter` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) + /// Storage: `Revive::CodeInfoOf` (r:33 w:33) + /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) + /// Storage: `Revive::DeletionQueue` (r:0 w:1) + /// Proof: `Revive::DeletionQueue` (`max_values`: None, `max_size`: Some(142), added: 2617, mode: `Measured`) + /// Storage: `Revive::ImmutableDataOf` (r:0 w:1) + /// Proof: `Revive::ImmutableDataOf` (`max_values`: None, `max_size`: Some(4118), added: 6593, mode: `Measured`) /// The range of component `n` is `[0, 32]`. fn seal_terminate(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `319 + n * (78 ยฑ0)` - // Estimated: `3784 + n * (2553 ยฑ0)` - // Minimum execution time: 13_251_000 picoseconds. - Weight::from_parts(15_257_892, 3784) - // Standard Error: 7_089 - .saturating_add(Weight::from_parts(3_443_907, 0).saturating_mul(n.into())) + // Measured: `272 + n * (88 ยฑ0)` + // Estimated: `3737 + n * (2563 ยฑ0)` + // Minimum execution time: 15_988_000 picoseconds. + Weight::from_parts(18_796_705, 3737) + // Standard Error: 10_437 + .saturating_add(Weight::from_parts(4_338_085, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(n.into()))) - .saturating_add(RocksDbWeight::get().writes(3_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(n.into()))) - .saturating_add(Weight::from_parts(0, 2553).saturating_mul(n.into())) - } - /// Storage: `RandomnessCollectiveFlip::RandomMaterial` (r:1 w:0) - /// Proof: `RandomnessCollectiveFlip::RandomMaterial` (`max_values`: Some(1), `max_size`: Some(2594), added: 3089, mode: `Measured`) - fn seal_random() -> Weight { - // Proof Size summary in bytes: - // Measured: `76` - // Estimated: `1561` - // Minimum execution time: 3_434_000 picoseconds. - Weight::from_parts(3_605_000, 1561) - .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(Weight::from_parts(0, 2563).saturating_mul(n.into())) } - /// Storage: `System::EventTopics` (r:4 w:4) - /// Proof: `System::EventTopics` (`max_values`: None, `max_size`: None, mode: `Measured`) /// The range of component `t` is `[0, 4]`. - /// The range of component `n` is `[0, 16384]`. + /// The range of component `n` is `[0, 512]`. fn seal_deposit_event(t: u32, n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` - // Estimated: `990 + t * (2475 ยฑ0)` - // Minimum execution time: 3_668_000 picoseconds. - Weight::from_parts(3_999_591, 990) - // Standard Error: 5_767 - .saturating_add(Weight::from_parts(2_011_090, 0).saturating_mul(t.into())) - // Standard Error: 1 - .saturating_add(Weight::from_parts(12, 0).saturating_mul(n.into())) - .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) - .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_parts(0, 2475).saturating_mul(t.into())) + // Estimated: `0` + // Minimum execution time: 4_237_000 picoseconds. + Weight::from_parts(4_128_112, 0) + // Standard Error: 2_947 + .saturating_add(Weight::from_parts(198_825, 0).saturating_mul(t.into())) + // Standard Error: 26 + .saturating_add(Weight::from_parts(1_007, 0).saturating_mul(n.into())) } - /// The range of component `i` is `[0, 1048576]`. + /// The range of component `i` is `[0, 262144]`. fn seal_debug_message(i: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 443_000 picoseconds. - Weight::from_parts(472_000, 0) - // Standard Error: 10 - .saturating_add(Weight::from_parts(1_207, 0).saturating_mul(i.into())) + // Minimum execution time: 292_000 picoseconds. + Weight::from_parts(1_297_376, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(724, 0).saturating_mul(i.into())) } /// Storage: `Skipped::Metadata` (r:0 w:0) /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) fn get_storage_empty() -> Weight { // Proof Size summary in bytes: - // Measured: `16618` - // Estimated: `16618` - // Minimum execution time: 13_752_000 picoseconds. - Weight::from_parts(14_356_000, 16618) + // Measured: `744` + // Estimated: `744` + // Minimum execution time: 7_812_000 picoseconds. + Weight::from_parts(8_171_000, 744) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) fn get_storage_full() -> Weight { // Proof Size summary in bytes: - // Measured: `26628` - // Estimated: `26628` - // Minimum execution time: 43_444_000 picoseconds. - Weight::from_parts(45_087_000, 26628) + // Measured: `10754` + // Estimated: `10754` + // Minimum execution time: 44_179_000 picoseconds. + Weight::from_parts(45_068_000, 10754) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) fn set_storage_empty() -> Weight { // Proof Size summary in bytes: - // Measured: `16618` - // Estimated: `16618` - // Minimum execution time: 15_616_000 picoseconds. - Weight::from_parts(16_010_000, 16618) + // Measured: `744` + // Estimated: `744` + // Minimum execution time: 8_964_000 picoseconds. + Weight::from_parts(9_336_000, 744) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1745,85 +1295,85 @@ impl WeightInfo for () { /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) fn set_storage_full() -> Weight { // Proof Size summary in bytes: - // Measured: `26628` - // Estimated: `26628` - // Minimum execution time: 47_020_000 picoseconds. - Weight::from_parts(50_152_000, 26628) + // Measured: `10754` + // Estimated: `10754` + // Minimum execution time: 45_606_000 picoseconds. + Weight::from_parts(47_190_000, 10754) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// The range of component `n` is `[0, 16384]`. - /// The range of component `o` is `[0, 16384]`. + /// The range of component `n` is `[0, 512]`. + /// The range of component `o` is `[0, 512]`. fn seal_set_storage(n: u32, o: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `250 + o * (1 ยฑ0)` - // Estimated: `249 + o * (1 ยฑ0)` - // Minimum execution time: 8_824_000 picoseconds. - Weight::from_parts(8_915_233, 249) - // Standard Error: 1 - .saturating_add(Weight::from_parts(255, 0).saturating_mul(n.into())) - // Standard Error: 1 - .saturating_add(Weight::from_parts(39, 0).saturating_mul(o.into())) + // Measured: `248 + o * (1 ยฑ0)` + // Estimated: `247 + o * (1 ยฑ0)` + // Minimum execution time: 9_077_000 picoseconds. + Weight::from_parts(9_823_489, 247) + // Standard Error: 54 + .saturating_add(Weight::from_parts(392, 0).saturating_mul(n.into())) + // Standard Error: 54 + .saturating_add(Weight::from_parts(408, 0).saturating_mul(o.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(o.into())) } /// Storage: `Skipped::Metadata` (r:0 w:0) /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// The range of component `n` is `[0, 16384]`. + /// The range of component `n` is `[0, 512]`. fn seal_clear_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `248 + n * (1 ยฑ0)` - // Estimated: `248 + n * (1 ยฑ0)` - // Minimum execution time: 7_133_000 picoseconds. - Weight::from_parts(7_912_778, 248) - // Standard Error: 1 - .saturating_add(Weight::from_parts(88, 0).saturating_mul(n.into())) + // Estimated: `247 + n * (1 ยฑ0)` + // Minimum execution time: 8_812_000 picoseconds. + Weight::from_parts(9_626_925, 247) + // Standard Error: 77 + .saturating_add(Weight::from_parts(269, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } /// Storage: `Skipped::Metadata` (r:0 w:0) /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// The range of component `n` is `[0, 16384]`. + /// The range of component `n` is `[0, 512]`. fn seal_get_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `248 + n * (1 ยฑ0)` - // Estimated: `248 + n * (1 ยฑ0)` - // Minimum execution time: 6_746_000 picoseconds. - Weight::from_parts(7_647_236, 248) - // Standard Error: 2 - .saturating_add(Weight::from_parts(603, 0).saturating_mul(n.into())) + // Estimated: `247 + n * (1 ยฑ0)` + // Minimum execution time: 8_143_000 picoseconds. + Weight::from_parts(9_229_363, 247) + // Standard Error: 77 + .saturating_add(Weight::from_parts(1_198, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } /// Storage: `Skipped::Metadata` (r:0 w:0) /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// The range of component `n` is `[0, 16384]`. + /// The range of component `n` is `[0, 512]`. fn seal_contains_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `248 + n * (1 ยฑ0)` - // Estimated: `248 + n * (1 ยฑ0)` - // Minimum execution time: 6_247_000 picoseconds. - Weight::from_parts(6_952_661, 248) - // Standard Error: 1 - .saturating_add(Weight::from_parts(77, 0).saturating_mul(n.into())) + // Estimated: `247 + n * (1 ยฑ0)` + // Minimum execution time: 7_899_000 picoseconds. + Weight::from_parts(8_591_860, 247) + // Standard Error: 56 + .saturating_add(Weight::from_parts(461, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } /// Storage: `Skipped::Metadata` (r:0 w:0) /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) - /// The range of component `n` is `[0, 16384]`. + /// The range of component `n` is `[0, 512]`. fn seal_take_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `248 + n * (1 ยฑ0)` - // Estimated: `248 + n * (1 ยฑ0)` - // Minimum execution time: 7_428_000 picoseconds. - Weight::from_parts(8_384_015, 248) - // Standard Error: 2 - .saturating_add(Weight::from_parts(625, 0).saturating_mul(n.into())) + // Estimated: `247 + n * (1 ยฑ0)` + // Minimum execution time: 9_215_000 picoseconds. + Weight::from_parts(10_198_528, 247) + // Standard Error: 75 + .saturating_add(Weight::from_parts(1_521, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1832,289 +1382,256 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_478_000 picoseconds. - Weight::from_parts(1_533_000, 0) + // Minimum execution time: 1_406_000 picoseconds. + Weight::from_parts(1_515_000, 0) } fn set_transient_storage_full() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_485_000 picoseconds. - Weight::from_parts(2_728_000, 0) + // Minimum execution time: 1_782_000 picoseconds. + Weight::from_parts(1_890_000, 0) } fn get_transient_storage_empty() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_195_000 picoseconds. - Weight::from_parts(3_811_000, 0) + // Minimum execution time: 1_380_000 picoseconds. + Weight::from_parts(1_422_000, 0) } fn get_transient_storage_full() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 3_902_000 picoseconds. - Weight::from_parts(4_118_000, 0) + // Minimum execution time: 1_504_000 picoseconds. + Weight::from_parts(1_583_000, 0) } fn rollback_transient_storage() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_571_000 picoseconds. - Weight::from_parts(1_662_000, 0) + // Minimum execution time: 1_045_000 picoseconds. + Weight::from_parts(1_138_000, 0) } - /// The range of component `n` is `[0, 16384]`. - /// The range of component `o` is `[0, 16384]`. + /// The range of component `n` is `[0, 512]`. + /// The range of component `o` is `[0, 512]`. fn seal_set_transient_storage(n: u32, o: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 5_250_000 picoseconds. - Weight::from_parts(2_465_568, 0) - // Standard Error: 0 - .saturating_add(Weight::from_parts(201, 0).saturating_mul(n.into())) - // Standard Error: 0 - .saturating_add(Weight::from_parts(223, 0).saturating_mul(o.into())) + // Minimum execution time: 2_039_000 picoseconds. + Weight::from_parts(2_317_406, 0) + // Standard Error: 13 + .saturating_add(Weight::from_parts(283, 0).saturating_mul(n.into())) + // Standard Error: 13 + .saturating_add(Weight::from_parts(347, 0).saturating_mul(o.into())) } - /// The range of component `n` is `[0, 16384]`. + /// The range of component `n` is `[0, 512]`. fn seal_clear_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_012_000 picoseconds. - Weight::from_parts(2_288_004, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(239, 0).saturating_mul(n.into())) + // Minimum execution time: 1_880_000 picoseconds. + Weight::from_parts(2_251_392, 0) + // Standard Error: 17 + .saturating_add(Weight::from_parts(313, 0).saturating_mul(n.into())) } - /// The range of component `n` is `[0, 16384]`. + /// The range of component `n` is `[0, 512]`. fn seal_get_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_906_000 picoseconds. - Weight::from_parts(2_121_040, 0) - // Standard Error: 0 - .saturating_add(Weight::from_parts(225, 0).saturating_mul(n.into())) + // Minimum execution time: 1_763_000 picoseconds. + Weight::from_parts(1_951_912, 0) + // Standard Error: 13 + .saturating_add(Weight::from_parts(268, 0).saturating_mul(n.into())) } - /// The range of component `n` is `[0, 16384]`. + /// The range of component `n` is `[0, 512]`. fn seal_contains_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_736_000 picoseconds. - Weight::from_parts(1_954_728, 0) - // Standard Error: 0 - .saturating_add(Weight::from_parts(111, 0).saturating_mul(n.into())) + // Minimum execution time: 1_536_000 picoseconds. + Weight::from_parts(1_779_085, 0) + // Standard Error: 14 + .saturating_add(Weight::from_parts(148, 0).saturating_mul(n.into())) } - /// The range of component `n` is `[0, 16384]`. - fn seal_take_transient_storage(_n: u32, ) -> Weight { + /// The range of component `n` is `[0, 512]`. + fn seal_take_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_872_000 picoseconds. - Weight::from_parts(8_125_644, 0) + // Minimum execution time: 2_343_000 picoseconds. + Weight::from_parts(2_587_750, 0) + // Standard Error: 22 + .saturating_add(Weight::from_parts(30, 0).saturating_mul(n.into())) } fn seal_transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `140` + // Measured: `103` // Estimated: `0` - // Minimum execution time: 8_489_000 picoseconds. - Weight::from_parts(8_791_000, 0) + // Minimum execution time: 9_250_000 picoseconds. + Weight::from_parts(9_637_000, 0) } - /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) - /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) - /// Storage: `Contracts::PristineCode` (r:1 w:0) - /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) - /// Storage: `System::Account` (r:1 w:1) - /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// Storage: `Revive::ContractInfoOf` (r:1 w:0) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) + /// Storage: `Revive::CodeInfoOf` (r:1 w:0) + /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) + /// Storage: `Revive::PristineCode` (r:1 w:0) + /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) /// The range of component `t` is `[0, 1]`. - /// The range of component `i` is `[0, 1048576]`. + /// The range of component `i` is `[0, 262144]`. fn seal_call(t: u32, i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `620 + t * (280 ยฑ0)` - // Estimated: `4085 + t * (2182 ยฑ0)` - // Minimum execution time: 122_759_000 picoseconds. - Weight::from_parts(120_016_020, 4085) - // Standard Error: 173_118 - .saturating_add(Weight::from_parts(42_848_338, 0).saturating_mul(t.into())) + // Measured: `1221 + t * (103 ยฑ0)` + // Estimated: `4686 + t * (103 ยฑ0)` + // Minimum execution time: 33_333_000 picoseconds. + Weight::from_parts(34_378_774, 4686) + // Standard Error: 41_131 + .saturating_add(Weight::from_parts(1_756_626, 0).saturating_mul(t.into())) // Standard Error: 0 - .saturating_add(Weight::from_parts(6, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(3, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(t.into()))) - .saturating_add(Weight::from_parts(0, 2182).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 103).saturating_mul(t.into())) } - /// Storage: `Contracts::CodeInfoOf` (r:1 w:0) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) - /// Storage: `Contracts::PristineCode` (r:1 w:0) - /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// Storage: `Revive::CodeInfoOf` (r:1 w:0) + /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) + /// Storage: `Revive::PristineCode` (r:1 w:0) + /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) fn seal_delegate_call() -> Weight { // Proof Size summary in bytes: - // Measured: `430` - // Estimated: `3895` - // Minimum execution time: 111_566_000 picoseconds. - Weight::from_parts(115_083_000, 3895) + // Measured: `1048` + // Estimated: `4513` + // Minimum execution time: 27_096_000 picoseconds. + Weight::from_parts(27_934_000, 4513) .saturating_add(RocksDbWeight::get().reads(2_u64)) } - /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) - /// Storage: `Contracts::PristineCode` (r:1 w:0) - /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) - /// Storage: `Contracts::Nonce` (r:1 w:0) - /// Proof: `Contracts::Nonce` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) - /// Storage: `Contracts::ContractInfoOf` (r:1 w:1) - /// Proof: `Contracts::ContractInfoOf` (`max_values`: None, `max_size`: Some(1795), added: 4270, mode: `Measured`) + /// Storage: `Revive::CodeInfoOf` (r:1 w:1) + /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) + /// Storage: `Revive::PristineCode` (r:1 w:0) + /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) + /// Storage: `Revive::ContractInfoOf` (r:1 w:1) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(1779), added: 4254, mode: `Measured`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) - /// The range of component `i` is `[0, 983040]`. - /// The range of component `s` is `[0, 983040]`. - fn seal_instantiate(i: u32, s: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `676` - // Estimated: `4132` - // Minimum execution time: 1_871_402_000 picoseconds. - Weight::from_parts(1_890_038_000, 4132) - // Standard Error: 24 - .saturating_add(Weight::from_parts(581, 0).saturating_mul(i.into())) - // Standard Error: 24 - .saturating_add(Weight::from_parts(915, 0).saturating_mul(s.into())) - .saturating_add(RocksDbWeight::get().reads(5_u64)) + /// The range of component `i` is `[0, 262144]`. + fn seal_instantiate(i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `1257` + // Estimated: `4715` + // Minimum execution time: 118_412_000 picoseconds. + Weight::from_parts(106_130_041, 4715) + // Standard Error: 12 + .saturating_add(Weight::from_parts(4_235, 0).saturating_mul(i.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// The range of component `n` is `[0, 1048576]`. + /// The range of component `n` is `[0, 262144]`. fn seal_hash_sha2_256(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 966_000 picoseconds. - Weight::from_parts(9_599_151, 0) - // Standard Error: 1 - .saturating_add(Weight::from_parts(1_336, 0).saturating_mul(n.into())) + // Minimum execution time: 614_000 picoseconds. + Weight::from_parts(4_320_897, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(1_371, 0).saturating_mul(n.into())) } - /// The range of component `n` is `[0, 1048576]`. + /// The range of component `n` is `[0, 262144]`. fn seal_hash_keccak_256(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_416_000 picoseconds. - Weight::from_parts(10_964_255, 0) - // Standard Error: 1 - .saturating_add(Weight::from_parts(3_593, 0).saturating_mul(n.into())) + // Minimum execution time: 1_062_000 picoseconds. + Weight::from_parts(4_571_371, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(3_572, 0).saturating_mul(n.into())) } - /// The range of component `n` is `[0, 1048576]`. + /// The range of component `n` is `[0, 262144]`. fn seal_hash_blake2_256(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 821_000 picoseconds. - Weight::from_parts(6_579_283, 0) - // Standard Error: 0 - .saturating_add(Weight::from_parts(1_466, 0).saturating_mul(n.into())) + // Minimum execution time: 609_000 picoseconds. + Weight::from_parts(4_008_056, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(1_497, 0).saturating_mul(n.into())) } - /// The range of component `n` is `[0, 1048576]`. + /// The range of component `n` is `[0, 262144]`. fn seal_hash_blake2_128(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 773_000 picoseconds. - Weight::from_parts(10_990_209, 0) - // Standard Error: 1 - .saturating_add(Weight::from_parts(1_457, 0).saturating_mul(n.into())) + // Minimum execution time: 608_000 picoseconds. + Weight::from_parts(3_839_383, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(1_504, 0).saturating_mul(n.into())) } - /// The range of component `n` is `[0, 125697]`. + /// The range of component `n` is `[0, 261889]`. fn seal_sr25519_verify(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 43_195_000 picoseconds. - Weight::from_parts(41_864_855, 0) - // Standard Error: 9 - .saturating_add(Weight::from_parts(5_154, 0).saturating_mul(n.into())) + // Minimum execution time: 43_110_000 picoseconds. + Weight::from_parts(31_941_593, 0) + // Standard Error: 12 + .saturating_add(Weight::from_parts(5_233, 0).saturating_mul(n.into())) } fn seal_ecdsa_recover() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 47_747_000 picoseconds. - Weight::from_parts(49_219_000, 0) + // Minimum execution time: 47_798_000 picoseconds. + Weight::from_parts(49_225_000, 0) } fn seal_ecdsa_to_eth_address() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 12_854_000 picoseconds. - Weight::from_parts(12_962_000, 0) + // Minimum execution time: 12_576_000 picoseconds. + Weight::from_parts(12_731_000, 0) } - /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) - /// Storage: `Contracts::PristineCode` (r:1 w:0) - /// Proof: `Contracts::PristineCode` (`max_values`: None, `max_size`: Some(125988), added: 128463, mode: `Measured`) + /// Storage: `Revive::CodeInfoOf` (r:1 w:1) + /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) fn seal_set_code_hash() -> Weight { // Proof Size summary in bytes: - // Measured: `430` - // Estimated: `3895` - // Minimum execution time: 17_868_000 picoseconds. - Weight::from_parts(18_486_000, 3895) - .saturating_add(RocksDbWeight::get().reads(2_u64)) + // Measured: `266` + // Estimated: `3731` + // Minimum execution time: 14_306_000 picoseconds. + Weight::from_parts(15_011_000, 3731) + .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `Revive::CodeInfoOf` (r:1 w:1) + /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) fn lock_delegate_dependency() -> Weight { // Proof Size summary in bytes: - // Measured: `355` - // Estimated: `3820` - // Minimum execution time: 8_393_000 picoseconds. - Weight::from_parts(8_640_000, 3820) + // Measured: `303` + // Estimated: `3768` + // Minimum execution time: 10_208_000 picoseconds. + Weight::from_parts(10_514_000, 3768) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: `Contracts::CodeInfoOf` (r:1 w:1) - /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `MaxEncodedLen`) + /// Storage: `Revive::CodeInfoOf` (r:1 w:1) + /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `MaxEncodedLen`) fn unlock_delegate_dependency() -> Weight { // Proof Size summary in bytes: - // Measured: `355` - // Estimated: `3558` - // Minimum execution time: 7_489_000 picoseconds. - Weight::from_parts(7_815_000, 3558) + // Measured: `303` + // Estimated: `3561` + // Minimum execution time: 9_062_000 picoseconds. + Weight::from_parts(9_414_000, 3561) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - fn seal_reentrance_count() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 299_000 picoseconds. - Weight::from_parts(339_000, 0) - } - fn seal_account_reentrance_count() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 324_000 picoseconds. - Weight::from_parts(380_000, 0) - } - /// Storage: `Contracts::Nonce` (r:1 w:0) - /// Proof: `Contracts::Nonce` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) - fn seal_instantiation_nonce() -> Weight { - // Proof Size summary in bytes: - // Measured: `219` - // Estimated: `1704` - // Minimum execution time: 2_768_000 picoseconds. - Weight::from_parts(3_025_000, 1704) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - } /// The range of component `r` is `[0, 5000]`. - fn instr_i64_load_store(r: u32, ) -> Weight { + fn instr(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 766_000 picoseconds. - Weight::from_parts(722_169, 0) - // Standard Error: 10 - .saturating_add(Weight::from_parts(7_191, 0).saturating_mul(r.into())) + // Minimum execution time: 8_074_000 picoseconds. + Weight::from_parts(9_646_158, 0) + // Standard Error: 58 + .saturating_add(Weight::from_parts(84_694, 0).saturating_mul(r.into())) } } diff --git a/substrate/frame/revive/uapi/Cargo.toml b/substrate/frame/revive/uapi/Cargo.toml index 862bf36f07cd0c561da56a53931be408a3adf793..8705781db002c2a2da19ecd5124f7d8b3d2b124b 100644 --- a/substrate/frame/revive/uapi/Cargo.toml +++ b/substrate/frame/revive/uapi/Cargo.toml @@ -21,7 +21,7 @@ codec = { features = [ ], optional = true, workspace = true } [target.'cfg(target_arch = "riscv32")'.dependencies] -polkavm-derive = { version = "0.10.0" } +polkavm-derive = { version = "0.12.0" } [package.metadata.docs.rs] default-target = ["wasm32-unknown-unknown"] diff --git a/substrate/frame/revive/uapi/src/flags.rs b/substrate/frame/revive/uapi/src/flags.rs index 17b91ce2b3be8225924dd6ddfe908ad9592a381a..763a89d6c030489b3867417357c8310cbfa686f3 100644 --- a/substrate/frame/revive/uapi/src/flags.rs +++ b/substrate/frame/revive/uapi/src/flags.rs @@ -20,6 +20,7 @@ use bitflags::bitflags; bitflags! { /// Flags used by a contract to customize exit behaviour. #[cfg_attr(feature = "scale", derive(codec::Encode, codec::Decode, scale_info::TypeInfo))] + #[derive(Default)] pub struct ReturnFlags: u32 { /// If this bit is set all changes made by the contract execution are rolled back. const REVERT = 0x0000_0001; diff --git a/substrate/frame/revive/uapi/src/host.rs b/substrate/frame/revive/uapi/src/host.rs index 6eb662363f7c2938cbd3ea47baf3664dac8e9cfd..2106b8fb49b7929975cbebba112038ced6c0bfbe 100644 --- a/substrate/frame/revive/uapi/src/host.rs +++ b/substrate/frame/revive/uapi/src/host.rs @@ -40,12 +40,10 @@ pub enum HostFnImpl {} pub trait HostFn: private::Sealed { /// Stores the address of the current contract into the supplied buffer. /// - /// If the available space in `output` is less than the size of the value a trap is triggered. - /// /// # Parameters /// /// - `output`: A reference to the output data buffer to write the address. - fn address(output: &mut &mut [u8]); + fn address(output: &mut [u8; 20]); /// Lock a new delegate dependency to the contract. /// @@ -56,25 +54,56 @@ pub trait HostFn: private::Sealed { /// /// - `code_hash`: The code hash of the dependency. Should be decodable as an `T::Hash`. Traps /// otherwise. - fn lock_delegate_dependency(code_hash: &[u8]); + fn lock_delegate_dependency(code_hash: &[u8; 32]); - /// Stores the *free* balance of the current account into the supplied buffer. + /// Get the contract immutable data. /// - /// If the available space in `output` is less than the size of the value a trap is triggered. + /// Traps if: + /// - Called from within the deploy export. + /// - Called by contracts that didn't set immutable data by calling `set_immutable_data` during + /// their constructor execution. + /// + /// # Parameters + /// - `output`: A reference to the output buffer to write the immutable bytes. + fn get_immutable_data(output: &mut &mut [u8]); + + /// Set the contract immutable data. + /// + /// It is only valid to set non-empty immutable data in the constructor once. + /// + /// Traps if: + /// - Called from within the call export. + /// - Called more than once. + /// - The provided data was empty. + /// + /// # Parameters + /// - `data`: A reference to the data to be stored as immutable bytes. + fn set_immutable_data(data: &[u8]); + + /// Stores the **reducible** balance of the current account into the supplied buffer. /// /// # Parameters /// /// - `output`: A reference to the output data buffer to write the balance. - fn balance(output: &mut &mut [u8]); + fn balance(output: &mut [u8; 32]); - /// Stores the current block number of the current contract into the supplied buffer. + /// Stores the **reducible** balance of the supplied address into the supplied buffer. /// - /// If the available space in `output` is less than the size of the value a trap is triggered. + /// # Parameters + /// + /// - `addr`: The target address of which to retreive the free balance. + /// - `output`: A reference to the output data buffer to write the balance. + fn balance_of(addr: &[u8; 20], output: &mut [u8; 32]); + + /// Returns the [EIP-155](https://eips.ethereum.org/EIPS/eip-155) chain ID. + fn chain_id(output: &mut [u8; 32]); + + /// Stores the current block number of the current contract into the supplied buffer. /// /// # Parameters /// /// - `output`: A reference to the output data buffer to write the block number. - fn block_number(output: &mut &mut [u8]); + fn block_number(output: &mut [u8; 32]); /// Call (possibly transferring some amount of funds) into the specified account. /// @@ -85,11 +114,10 @@ pub trait HostFn: private::Sealed { /// otherwise. /// - `ref_time_limit`: how much *ref_time* Weight to devote to the execution. /// - `proof_size_limit`: how much *proof_size* Weight to devote to the execution. - /// - `deposit`: The storage deposit limit for instantiation. Should be decodable as a - /// `Option`. Traps otherwise. Passing `None` means setting no specific limit for - /// the call, which implies storage usage up to the limit of the parent call. - /// - `value`: The value to transfer into the contract. Should be decodable as a `T::Balance`. - /// Traps otherwise. + /// - `deposit`: The storage deposit limit for instantiation. Passing `None` means setting no + /// specific limit for the call, which implies storage usage up to the limit of the parent + /// call. + /// - `value`: The value to transfer into the contract. /// - `input`: The input data buffer used to call the contract. /// - `output`: A reference to the output data buffer to write the call output buffer. If `None` /// is provided then the output buffer is not copied. @@ -105,11 +133,11 @@ pub trait HostFn: private::Sealed { /// - [NotCallable][`crate::ReturnErrorCode::NotCallable] fn call( flags: CallFlags, - callee: &[u8], + callee: &[u8; 20], ref_time_limit: u64, proof_size_limit: u64, - deposit: Option<&[u8]>, - value: &[u8], + deposit: Option<&[u8; 32]>, + value: &[u8; 32], input_data: &[u8], output: Option<&mut &mut [u8]>, ) -> Result; @@ -166,8 +194,6 @@ pub trait HostFn: private::Sealed { /// Stores the address of the caller into the supplied buffer. /// - /// If the available space in `output` is less than the size of the value a trap is triggered. - /// /// If this is a top-level call (i.e. initiated by an extrinsic) the origin address of the /// extrinsic will be returned. Otherwise, if this call is initiated by another contract then /// the address of the contract will be returned. @@ -178,7 +204,7 @@ pub trait HostFn: private::Sealed { /// # Parameters /// /// - `output`: A reference to the output data buffer to write the caller address. - fn caller(output: &mut &mut [u8]); + fn caller(output: &mut [u8; 20]); /// Checks whether the caller of the current contract is the origin of the whole call stack. /// @@ -216,15 +242,13 @@ pub trait HostFn: private::Sealed { /// /// # Parameters /// - /// - `account_id`: The address of the contract.Should be decodable as an `T::AccountId`. Traps - /// otherwise. + /// - `addr`: The address of the contract. /// - `output`: A reference to the output data buffer to write the code hash. /// - /// /// # Errors /// /// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound] - fn code_hash(account_id: &[u8], output: &mut [u8]) -> Result; + fn code_hash(addr: &[u8; 20], output: &mut [u8; 32]) -> Result; /// Checks whether there is a value stored under the given key. /// @@ -281,7 +305,7 @@ pub trait HostFn: private::Sealed { /// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound] fn delegate_call( flags: CallFlags, - code_hash: &[u8], + code_hash: &[u8; 32], input_data: &[u8], output: Option<&mut &mut [u8]>, ) -> Result; @@ -293,8 +317,8 @@ pub trait HostFn: private::Sealed { /// /// # Parameters /// - /// - `topics`: The topics list encoded as `Vec`. It can't contain duplicates. - fn deposit_event(topics: &[u8], data: &[u8]); + /// - `topics`: The topics list. It can't contain duplicates. + fn deposit_event(topics: &[[u8; 32]], data: &[u8]); /// Recovers the ECDSA public key from the given message hash and signature. /// @@ -380,11 +404,10 @@ pub trait HostFn: private::Sealed { /// - `code_hash`: The hash of the code to be instantiated. /// - `ref_time_limit`: how much *ref_time* Weight to devote to the execution. /// - `proof_size_limit`: how much *proof_size* Weight to devote to the execution. - /// - `deposit`: The storage deposit limit for instantiation. Should be decodable as a - /// `Option`. Traps otherwise. Passing `None` means setting no specific limit for - /// the call, which implies storage usage up to the limit of the parent call. - /// - `value`: The value to transfer into the contract. Should be decodable as a `T::Balance`. - /// Traps otherwise. + /// - `deposit`: The storage deposit limit for instantiation. Passing `None` means setting no + /// specific limit for the call, which implies storage usage up to the limit of the parent + /// call. + /// - `value`: The value to transfer into the contract. /// - `input`: The input data buffer. /// - `address`: A reference to the address buffer to write the address of the contract. If /// `None` is provided then the output buffer is not copied. @@ -405,54 +428,48 @@ pub trait HostFn: private::Sealed { /// - [TransferFailed][`crate::ReturnErrorCode::TransferFailed] /// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound] fn instantiate( - code_hash: &[u8], + code_hash: &[u8; 32], ref_time_limit: u64, proof_size_limit: u64, - deposit: Option<&[u8]>, - value: &[u8], + deposit: Option<&[u8; 32]>, + value: &[u8; 32], input: &[u8], - address: Option<&mut &mut [u8]>, + address: Option<&mut [u8; 20]>, output: Option<&mut &mut [u8]>, - salt: &[u8], + salt: Option<&[u8; 32]>, ) -> Result; /// Checks whether a specified address belongs to a contract. /// /// # Parameters /// - /// - `account_id`: The address to check. Should be decodable as an `T::AccountId`. Traps - /// otherwise. + /// - `address`: The address to check /// /// # Return /// /// Returns `true` if the address belongs to a contract. - fn is_contract(account_id: &[u8]) -> bool; + fn is_contract(address: &[u8; 20]) -> bool; /// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer. - /// The data is encoded as `T::Balance`. - /// - /// If the available space in `output` is less than the size of the value a trap is triggered. /// /// # Parameters /// /// - `output`: A reference to the output data buffer to write the minimum balance. - fn minimum_balance(output: &mut &mut [u8]); + fn minimum_balance(output: &mut [u8; 32]); /// Retrieve the code hash of the currently executing contract. /// /// # Parameters /// /// - `output`: A reference to the output data buffer to write the code hash. - fn own_code_hash(output: &mut [u8]); + fn own_code_hash(output: &mut [u8; 32]); /// Load the latest block timestamp into the supplied buffer /// - /// If the available space in `output` is less than the size of the value a trap is triggered. - /// /// # Parameters /// /// - `output`: A reference to the output data buffer to write the timestamp. - fn now(output: &mut &mut [u8]); + fn now(output: &mut [u8; 32]); /// Removes the delegate dependency from the contract. /// @@ -462,7 +479,7 @@ pub trait HostFn: private::Sealed { /// /// - `code_hash`: The code hash of the dependency. Should be decodable as an `T::Hash`. Traps /// otherwise. - fn unlock_delegate_dependency(code_hash: &[u8]); + fn unlock_delegate_dependency(code_hash: &[u8; 32]); /// Cease contract execution and save a data buffer as a result of the execution. /// @@ -510,7 +527,7 @@ pub trait HostFn: private::Sealed { /// # Errors /// /// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound] - fn set_code_hash(code_hash: &[u8]) -> Result; + fn set_code_hash(code_hash: &[u8; 32]) -> Result; /// Set the value at the given key in the contract storage. /// @@ -554,14 +571,13 @@ pub trait HostFn: private::Sealed { /// /// # Parameters /// - /// - `account_id`: The address of the account to transfer funds to. Should be decodable as an - /// `T::AccountId`. Traps otherwise. - /// - `value`: The value to transfer. Should be decodable as a `T::Balance`. Traps otherwise. + /// - `address`: The address of the account to transfer funds to. + /// - `value`: The U256 value to transfer. /// /// # Errors /// /// - [TransferFailed][`crate::ReturnErrorCode::TransferFailed] - fn transfer(account_id: &[u8], value: &[u8]) -> Result; + fn transfer(address: &[u8; 20], value: &[u8; 32]) -> Result; /// Remove the calling account and transfer remaining **free** balance. /// @@ -571,37 +587,30 @@ pub trait HostFn: private::Sealed { /// /// # Parameters /// - /// - `beneficiary`: The address of the beneficiary account, Should be decodable as an - /// `T::AccountId`. + /// - `beneficiary`: The address of the beneficiary account /// /// # Traps /// /// - The contract is live i.e is already on the call stack. /// - Failed to send the balance to the beneficiary. /// - The deletion queue is full. - fn terminate(beneficiary: &[u8]) -> !; + fn terminate(beneficiary: &[u8; 20]) -> !; /// Stores the value transferred along with this call/instantiate into the supplied buffer. - /// The data is encoded as `T::Balance`. - /// - /// If the available space in `output` is less than the size of the value a trap is triggered. /// /// # Parameters /// /// - `output`: A reference to the output data buffer to write the transferred value. - fn value_transferred(output: &mut &mut [u8]); + fn value_transferred(output: &mut [u8; 32]); /// Stores the price for the specified amount of gas into the supplied buffer. - /// The data is encoded as `T::Balance`. - /// - /// If the available space in `output` is less than the size of the value a trap is triggered. /// /// # Parameters /// /// - `ref_time_limit`: The *ref_time* Weight limit to query the price for. /// - `proof_size_limit`: The *proof_size* Weight limit to query the price for. /// - `output`: A reference to the output data buffer to write the price. - fn weight_to_fee(ref_time_limit: u64, proof_size_limit: u64, output: &mut &mut [u8]); + fn weight_to_fee(ref_time_limit: u64, proof_size_limit: u64, output: &mut [u8; 32]); /// Execute an XCM program locally, using the contract's address as the origin. /// This is equivalent to dispatching `pallet_xcm::execute` through call_runtime, except that @@ -635,6 +644,20 @@ pub trait HostFn: private::Sealed { /// Returns `ReturnCode::Success` when the message was successfully sent. When the XCM /// execution fails, `ReturnErrorCode::XcmSendFailed` is returned. fn xcm_send(dest: &[u8], msg: &[u8], output: &mut [u8; 32]) -> Result; + + /// Stores the size of the returned data of the last contract call or instantiation. + /// + /// # Parameters + /// + /// - `output`: A reference to the output buffer to write the size. + fn return_data_size(output: &mut [u8; 32]); + + /// Stores the returned data of the last contract call or contract instantiation. + /// + /// # Parameters + /// - `output`: A reference to the output buffer to write the data. + /// - `offset`: Byte offset into the returned data + fn return_data_copy(output: &mut &mut [u8], offset: u32); } mod private { diff --git a/substrate/frame/revive/uapi/src/host/riscv32.rs b/substrate/frame/revive/uapi/src/host/riscv32.rs index 0b7130015f1329609fda648ec2427b301f1023e2..866b0ee8dd1762ad8fafcf820ecce0410a3cb038 100644 --- a/substrate/frame/revive/uapi/src/host/riscv32.rs +++ b/substrate/frame/revive/uapi/src/host/riscv32.rs @@ -58,7 +58,7 @@ mod sys { out_ptr: *mut u8, out_len_ptr: *mut u32, ) -> ReturnCode; - pub fn transfer(account_ptr: *const u8, value_ptr: *const u8) -> ReturnCode; + pub fn transfer(address_ptr: *const u8, value_ptr: *const u8) -> ReturnCode; pub fn call(ptr: *const u8) -> ReturnCode; pub fn delegate_call( flags: u32, @@ -72,35 +72,30 @@ mod sys { pub fn terminate(beneficiary_ptr: *const u8); pub fn input(out_ptr: *mut u8, out_len_ptr: *mut u32); pub fn seal_return(flags: u32, data_ptr: *const u8, data_len: u32); - pub fn caller(out_ptr: *mut u8, out_len_ptr: *mut u32); + pub fn caller(out_ptr: *mut u8); pub fn is_contract(account_ptr: *const u8) -> ReturnCode; - pub fn code_hash( - account_ptr: *const u8, - out_ptr: *mut u8, - out_len_ptr: *mut u32, - ) -> ReturnCode; - pub fn own_code_hash(out_ptr: *mut u8, out_len_ptr: *mut u32); + pub fn code_hash(address_ptr: *const u8, out_ptr: *mut u8) -> ReturnCode; + pub fn own_code_hash(out_ptr: *mut u8); pub fn caller_is_origin() -> ReturnCode; pub fn caller_is_root() -> ReturnCode; - pub fn address(out_ptr: *mut u8, out_len_ptr: *mut u32); - pub fn weight_to_fee( - ref_time: u64, - proof_size: u64, - out_ptr: *mut u8, - out_len_ptr: *mut u32, - ); + pub fn address(out_ptr: *mut u8); + pub fn weight_to_fee(ref_time: u64, proof_size: u64, out_ptr: *mut u8); pub fn weight_left(out_ptr: *mut u8, out_len_ptr: *mut u32); - pub fn balance(out_ptr: *mut u8, out_len_ptr: *mut u32); - pub fn value_transferred(out_ptr: *mut u8, out_len_ptr: *mut u32); - pub fn now(out_ptr: *mut u8, out_len_ptr: *mut u32); - pub fn minimum_balance(out_ptr: *mut u8, out_len_ptr: *mut u32); + pub fn get_immutable_data(out_ptr: *mut u8, out_len_ptr: *mut u32); + pub fn set_immutable_data(ptr: *const u8, len: u32); + pub fn balance(out_ptr: *mut u8); + pub fn balance_of(addr_ptr: *const u8, out_ptr: *mut u8); + pub fn chain_id(out_ptr: *mut u8); + pub fn value_transferred(out_ptr: *mut u8); + pub fn now(out_ptr: *mut u8); + pub fn minimum_balance(out_ptr: *mut u8); pub fn deposit_event( - topics_ptr: *const u8, - topics_len: u32, + topics_ptr: *const [u8; 32], + num_topic: u32, data_ptr: *const u8, data_len: u32, ); - pub fn block_number(out_ptr: *mut u8, out_len_ptr: *mut u32); + pub fn block_number(out_ptr: *mut u8); pub fn hash_sha2_256(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8); pub fn hash_keccak_256(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8); pub fn hash_blake2_256(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8); @@ -133,28 +128,30 @@ mod sys { pub fn xcm_execute(msg_ptr: *const u8, msg_len: u32) -> ReturnCode; pub fn xcm_send( dest_ptr: *const u8, + dest_len: *const u8, msg_ptr: *const u8, msg_len: u32, out_ptr: *mut u8, ) -> ReturnCode; + pub fn return_data_size(out_ptr: *mut u8); + pub fn return_data_copy(out_ptr: *mut u8, out_len_ptr: *mut u32, offset: u32); } } +/// A macro to implement all Host functions with a signature of `fn(&mut [u8; n])`. macro_rules! impl_wrapper_for { - ( $( $name:ident, )* ) => { - $( - fn $name(output: &mut &mut [u8]) { - let mut output_len = output.len() as u32; - unsafe { - sys::$name( - output.as_mut_ptr(), - &mut output_len, - ) - } - extract_from_slice(output, output_len as usize) - } - )* - } + (@impl_fn $name:ident, $n: literal) => { + fn $name(output: &mut [u8; $n]) { + unsafe { sys::$name(output.as_mut_ptr()) } + } + }; + + () => {}; + + ([u8; $n: literal] => $($name:ident),*; $($tail:tt)*) => { + $(impl_wrapper_for!(@impl_fn $name, $n);)* + impl_wrapper_for!($($tail)*); + }; } macro_rules! impl_hash_fn { @@ -189,7 +186,7 @@ fn ptr_len_or_sentinel(data: &mut Option<&mut &mut [u8]>) -> (*mut u8, u32) { } #[inline(always)] -fn ptr_or_sentinel(data: &Option<&[u8]>) -> *const u8 { +fn ptr_or_sentinel(data: &Option<&[u8; 32]>) -> *const u8 { match data { Some(ref data) => data.as_ptr(), None => crate::SENTINEL as _, @@ -198,19 +195,23 @@ fn ptr_or_sentinel(data: &Option<&[u8]>) -> *const u8 { impl HostFn for HostFnImpl { fn instantiate( - code_hash: &[u8], + code_hash: &[u8; 32], ref_time_limit: u64, proof_size_limit: u64, - deposit_limit: Option<&[u8]>, - value: &[u8], + deposit_limit: Option<&[u8; 32]>, + value: &[u8; 32], input: &[u8], - mut address: Option<&mut &mut [u8]>, + mut address: Option<&mut [u8; 20]>, mut output: Option<&mut &mut [u8]>, - salt: &[u8], + salt: Option<&[u8; 32]>, ) -> Result { - let (address_ptr, mut address_len) = ptr_len_or_sentinel(&mut address); + let address = match address { + Some(ref mut data) => data.as_mut_ptr(), + None => crate::SENTINEL as _, + }; let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output); let deposit_limit_ptr = ptr_or_sentinel(&deposit_limit); + let salt_ptr = ptr_or_sentinel(&salt); #[repr(packed)] #[allow(dead_code)] struct Args { @@ -220,13 +221,11 @@ impl HostFn for HostFnImpl { deposit_limit: *const u8, value: *const u8, input: *const u8, - input_len: usize, + input_len: u32, address: *const u8, - address_len: *mut u32, output: *mut u8, output_len: *mut u32, salt: *const u8, - salt_len: usize, } let args = Args { code_hash: code_hash.as_ptr(), @@ -235,21 +234,15 @@ impl HostFn for HostFnImpl { deposit_limit: deposit_limit_ptr, value: value.as_ptr(), input: input.as_ptr(), - input_len: input.len(), - address: address_ptr, - address_len: &mut address_len as *mut _, + input_len: input.len() as _, + address, output: output_ptr, output_len: &mut output_len as *mut _, - salt: salt.as_ptr(), - salt_len: salt.len(), + salt: salt_ptr, }; let ret_code = { unsafe { sys::instantiate(&args as *const Args as *const _) } }; - if let Some(ref mut address) = address { - extract_from_slice(address, address_len as usize); - } - if let Some(ref mut output) = output { extract_from_slice(output, output_len as usize); } @@ -259,11 +252,11 @@ impl HostFn for HostFnImpl { fn call( flags: CallFlags, - callee: &[u8], + callee: &[u8; 20], ref_time_limit: u64, proof_size_limit: u64, - deposit_limit: Option<&[u8]>, - value: &[u8], + deposit_limit: Option<&[u8; 32]>, + value: &[u8; 32], input: &[u8], mut output: Option<&mut &mut [u8]>, ) -> Result { @@ -279,7 +272,7 @@ impl HostFn for HostFnImpl { deposit_limit: *const u8, value: *const u8, input: *const u8, - input_len: usize, + input_len: u32, output: *mut u8, output_len: *mut u32, } @@ -291,7 +284,7 @@ impl HostFn for HostFnImpl { deposit_limit: deposit_limit_ptr, value: value.as_ptr(), input: input.as_ptr(), - input_len: input.len(), + input_len: input.len() as _, output: output_ptr, output_len: &mut output_len as *mut _, }; @@ -311,7 +304,7 @@ impl HostFn for HostFnImpl { fn delegate_call( flags: CallFlags, - code_hash: &[u8], + code_hash: &[u8; 32], input: &[u8], mut output: Option<&mut &mut [u8]>, ) -> Result { @@ -336,12 +329,12 @@ impl HostFn for HostFnImpl { ret_code.into() } - fn transfer(account_id: &[u8], value: &[u8]) -> Result { - let ret_code = unsafe { sys::transfer(account_id.as_ptr(), value.as_ptr()) }; + fn transfer(address: &[u8; 20], value: &[u8; 32]) -> Result { + let ret_code = unsafe { sys::transfer(address.as_ptr(), value.as_ptr()) }; ret_code.into() } - fn deposit_event(topics: &[u8], data: &[u8]) { + fn deposit_event(topics: &[[u8; 32]], data: &[u8]) { unsafe { sys::deposit_event( topics.as_ptr(), @@ -415,7 +408,7 @@ impl HostFn for HostFnImpl { ret_code.into() } - fn terminate(beneficiary: &[u8]) -> ! { + fn terminate(beneficiary: &[u8; 20]) -> ! { unsafe { sys::terminate(beneficiary.as_ptr()) } panic!("terminate does not return"); } @@ -459,24 +452,18 @@ impl HostFn for HostFnImpl { } impl_wrapper_for! { - caller, block_number, address, balance, - value_transferred,now, minimum_balance, - weight_left, + [u8; 32] => block_number, balance, value_transferred, now, minimum_balance, chain_id; + [u8; 20] => address, caller; } - fn weight_to_fee(ref_time_limit: u64, proof_size_limit: u64, output: &mut &mut [u8]) { + fn weight_left(output: &mut &mut [u8]) { let mut output_len = output.len() as u32; - { - unsafe { - sys::weight_to_fee( - ref_time_limit, - proof_size_limit, - output.as_mut_ptr(), - &mut output_len, - ) - }; - } - extract_from_slice(output, output_len as usize); + unsafe { sys::weight_left(output.as_mut_ptr(), &mut output_len) } + extract_from_slice(output, output_len as usize) + } + + fn weight_to_fee(ref_time_limit: u64, proof_size_limit: u64, output: &mut [u8; 32]) { + unsafe { sys::weight_to_fee(ref_time_limit, proof_size_limit, output.as_mut_ptr()) }; } impl_hash_fn!(sha2_256, 32); @@ -512,38 +499,49 @@ impl HostFn for HostFnImpl { ret_code.into() } - fn is_contract(account_id: &[u8]) -> bool { - let ret_val = unsafe { sys::is_contract(account_id.as_ptr()) }; + fn is_contract(address: &[u8; 20]) -> bool { + let ret_val = unsafe { sys::is_contract(address.as_ptr()) }; ret_val.into_bool() } + fn get_immutable_data(output: &mut &mut [u8]) { + let mut output_len = output.len() as u32; + unsafe { sys::get_immutable_data(output.as_mut_ptr(), &mut output_len) }; + extract_from_slice(output, output_len as usize); + } + + fn set_immutable_data(data: &[u8]) { + unsafe { sys::set_immutable_data(data.as_ptr(), data.len() as u32) } + } + + fn balance_of(address: &[u8; 20], output: &mut [u8; 32]) { + unsafe { sys::balance_of(address.as_ptr(), output.as_mut_ptr()) }; + } + fn caller_is_origin() -> bool { let ret_val = unsafe { sys::caller_is_origin() }; ret_val.into_bool() } - fn set_code_hash(code_hash: &[u8]) -> Result { + fn set_code_hash(code_hash: &[u8; 32]) -> Result { let ret_val = unsafe { sys::set_code_hash(code_hash.as_ptr()) }; ret_val.into() } - fn code_hash(account_id: &[u8], output: &mut [u8]) -> Result { - let mut output_len = output.len() as u32; - let ret_val = - unsafe { sys::code_hash(account_id.as_ptr(), output.as_mut_ptr(), &mut output_len) }; + fn code_hash(address: &[u8; 20], output: &mut [u8; 32]) -> Result { + let ret_val = unsafe { sys::code_hash(address.as_ptr(), output.as_mut_ptr()) }; ret_val.into() } - fn own_code_hash(output: &mut [u8]) { - let mut output_len = output.len() as u32; - unsafe { sys::own_code_hash(output.as_mut_ptr(), &mut output_len) } + fn own_code_hash(output: &mut [u8; 32]) { + unsafe { sys::own_code_hash(output.as_mut_ptr()) } } - fn lock_delegate_dependency(code_hash: &[u8]) { + fn lock_delegate_dependency(code_hash: &[u8; 32]) { unsafe { sys::lock_delegate_dependency(code_hash.as_ptr()) } } - fn unlock_delegate_dependency(code_hash: &[u8]) { + fn unlock_delegate_dependency(code_hash: &[u8; 32]) { unsafe { sys::unlock_delegate_dependency(code_hash.as_ptr()) } } @@ -554,8 +552,26 @@ impl HostFn for HostFnImpl { fn xcm_send(dest: &[u8], msg: &[u8], output: &mut [u8; 32]) -> Result { let ret_code = unsafe { - sys::xcm_send(dest.as_ptr(), msg.as_ptr(), msg.len() as _, output.as_mut_ptr()) + sys::xcm_send( + dest.as_ptr(), + dest.len() as _, + msg.as_ptr(), + msg.len() as _, + output.as_mut_ptr(), + ) }; ret_code.into() } + + fn return_data_size(output: &mut [u8; 32]) { + unsafe { sys::return_data_size(output.as_mut_ptr()) }; + } + + fn return_data_copy(output: &mut &mut [u8], offset: u32) { + let mut output_len = output.len() as u32; + { + unsafe { sys::return_data_copy(output.as_mut_ptr(), &mut output_len, offset) }; + } + extract_from_slice(output, output_len as usize); + } } diff --git a/substrate/frame/root-offences/src/tests.rs b/substrate/frame/root-offences/src/tests.rs index f96884d750da8a7d101b650caf4607afbc51bd35..289bb708efbbc0d9496499a2c9e8b25c240715f9 100644 --- a/substrate/frame/root-offences/src/tests.rs +++ b/substrate/frame/root-offences/src/tests.rs @@ -17,7 +17,8 @@ use super::*; use frame_support::{assert_err, assert_ok}; -use mock::{active_era, start_session, Balances, ExtBuilder, RootOffences, RuntimeOrigin, System}; +use mock::{active_era, start_session, ExtBuilder, RootOffences, RuntimeOrigin, System, Test as T}; +use pallet_staking::asset; #[test] fn create_offence_fails_given_signed_origin() { @@ -35,18 +36,18 @@ fn create_offence_works_given_root_origin() { assert_eq!(active_era(), 0); - assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(asset::staked::(&11), 1000); let offenders = [(11, Perbill::from_percent(50))].to_vec(); assert_ok!(RootOffences::create_offence(RuntimeOrigin::root(), offenders.clone())); System::assert_last_event(Event::OffenceCreated { offenders }.into()); // the slash should be applied right away. - assert_eq!(Balances::free_balance(11), 500); + assert_eq!(asset::staked::(&11), 500); // the other validator should keep their balance, because we only created // an offences for the first validator. - assert_eq!(Balances::free_balance(21), 1000); + assert_eq!(asset::staked::(&21), 1000); }) } @@ -58,7 +59,7 @@ fn create_offence_wont_slash_non_active_validators() { assert_eq!(active_era(), 0); // 31 is not an active validator. - assert_eq!(Balances::free_balance(31), 500); + assert_eq!(asset::staked::(&31), 500); let offenders = [(31, Perbill::from_percent(20)), (11, Perbill::from_percent(20))].to_vec(); assert_ok!(RootOffences::create_offence(RuntimeOrigin::root(), offenders.clone())); @@ -66,10 +67,10 @@ fn create_offence_wont_slash_non_active_validators() { System::assert_last_event(Event::OffenceCreated { offenders }.into()); // so 31 didn't get slashed. - assert_eq!(Balances::free_balance(31), 500); + assert_eq!(asset::staked::(&31), 500); // but 11 is an active validator so they got slashed. - assert_eq!(Balances::free_balance(11), 800); + assert_eq!(asset::staked::(&11), 800); }) } @@ -81,7 +82,7 @@ fn create_offence_wont_slash_idle() { assert_eq!(active_era(), 0); // 41 is idle. - assert_eq!(Balances::free_balance(41), 1000); + assert_eq!(asset::staked::(&41), 1000); let offenders = [(41, Perbill::from_percent(50))].to_vec(); assert_ok!(RootOffences::create_offence(RuntimeOrigin::root(), offenders.clone())); @@ -89,6 +90,6 @@ fn create_offence_wont_slash_idle() { System::assert_last_event(Event::OffenceCreated { offenders }.into()); // 41 didn't get slashed. - assert_eq!(Balances::free_balance(41), 1000); + assert_eq!(asset::staked::(&41), 1000); }) } diff --git a/substrate/frame/sassafras/src/mock.rs b/substrate/frame/sassafras/src/mock.rs index f145bffa3a05c1c9b05087490792245e58c2a352..d2b329e8a2ba1314e832495a4f1b40cf22ff101f 100644 --- a/substrate/frame/sassafras/src/mock.rs +++ b/substrate/frame/sassafras/src/mock.rs @@ -89,7 +89,7 @@ pub fn new_test_ext_with_pairs( with_ring_context: bool, ) -> (Vec, sp_io::TestExternalities) { let pairs = (0..authorities_len) - .map(|i| AuthorityPair::from_seed(&U256::from(i).into())) + .map(|i| AuthorityPair::from_seed(&U256::from(i).to_big_endian())) .collect::>(); let authorities: Vec<_> = pairs.iter().map(|p| p.public()).collect(); diff --git a/substrate/frame/society/src/lib.rs b/substrate/frame/society/src/lib.rs index b4c5c88af3d6e9abbd66fe51d1b6438d32c43ca3..04879cd87091eb45bfc7973d76e9a59fed68560e 100644 --- a/substrate/frame/society/src/lib.rs +++ b/substrate/frame/society/src/lib.rs @@ -1387,18 +1387,6 @@ impl_ensure_origin_with_arg_ignoring_arg! { {} } -struct InputFromRng<'a, T>(&'a mut T); -impl<'a, T: RngCore> codec::Input for InputFromRng<'a, T> { - fn remaining_len(&mut self) -> Result, codec::Error> { - return Ok(None) - } - - fn read(&mut self, into: &mut [u8]) -> Result<(), codec::Error> { - self.0.fill_bytes(into); - Ok(()) - } -} - pub enum Period { Voting { elapsed: BlockNumber, more: BlockNumber }, Claim { elapsed: BlockNumber, more: BlockNumber }, diff --git a/substrate/frame/society/src/tests.rs b/substrate/frame/society/src/tests.rs index df8e844cdad9939c9848b106ce4a097195ac2d37..2a13f99855b59f66764ffc2fd93fe3479df47c46 100644 --- a/substrate/frame/society/src/tests.rs +++ b/substrate/frame/society/src/tests.rs @@ -281,7 +281,7 @@ fn bidding_works() { // No more candidates satisfy the requirements assert_eq!(candidacies(), vec![]); assert_ok!(Society::defender_vote(Origin::signed(10), true)); // Keep defender around - // Next period + // Next period run_to_block(16); // Same members assert_eq!(members(), vec![10, 30, 40, 50]); diff --git a/substrate/frame/staking/src/asset.rs b/substrate/frame/staking/src/asset.rs new file mode 100644 index 0000000000000000000000000000000000000000..23368b1f8fca713f78405165c00ec09d54a8d257 --- /dev/null +++ b/substrate/frame/staking/src/asset.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. + +//! Contains all the interactions with [`Config::Currency`] to manipulate the underlying staking +//! asset. + +use frame_support::traits::{Currency, InspectLockableCurrency, LockableCurrency}; + +use crate::{BalanceOf, Config, NegativeImbalanceOf, PositiveImbalanceOf}; + +/// Existential deposit for the chain. +pub fn existential_deposit() -> BalanceOf { + T::Currency::minimum_balance() +} + +/// Total issuance of the chain. +pub fn total_issuance() -> BalanceOf { + T::Currency::total_issuance() +} + +/// Total balance of `who`. Includes both, free and reserved. +pub fn total_balance(who: &T::AccountId) -> BalanceOf { + T::Currency::total_balance(who) +} + +/// Stakeable balance of `who`. +/// +/// This includes balance free to stake along with any balance that is already staked. +pub fn stakeable_balance(who: &T::AccountId) -> BalanceOf { + T::Currency::free_balance(who) +} + +/// Balance of `who` that is currently at stake. +/// +/// The staked amount is locked and cannot be transferred out of `who`s account. +pub fn staked(who: &T::AccountId) -> BalanceOf { + T::Currency::balance_locked(crate::STAKING_ID, who) +} + +/// Set balance that can be staked for `who`. +/// +/// This includes any balance that is already staked. +#[cfg(any(test, feature = "runtime-benchmarks"))] +pub fn set_stakeable_balance(who: &T::AccountId, value: BalanceOf) { + T::Currency::make_free_balance_be(who, value); +} + +/// Update `amount` at stake for `who`. +/// +/// Overwrites the existing stake amount. If passed amount is lower than the existing stake, the +/// difference is unlocked. +pub fn update_stake(who: &T::AccountId, amount: BalanceOf) { + T::Currency::set_lock( + crate::STAKING_ID, + who, + amount, + frame_support::traits::WithdrawReasons::all(), + ); +} + +/// Kill the stake of `who`. +/// +/// All locked amount is unlocked. +pub fn kill_stake(who: &T::AccountId) { + T::Currency::remove_lock(crate::STAKING_ID, who); +} + +/// Slash the value from `who`. +/// +/// A negative imbalance is returned which can be resolved to deposit the slashed value. +pub fn slash( + who: &T::AccountId, + value: BalanceOf, +) -> (NegativeImbalanceOf, BalanceOf) { + T::Currency::slash(who, value) +} + +/// Mint `value` into an existing account `who`. +/// +/// This does not increase the total issuance. +pub fn mint_existing( + who: &T::AccountId, + value: BalanceOf, +) -> Option> { + T::Currency::deposit_into_existing(who, value).ok() +} + +/// Mint reward and create account for `who` if it does not exist. +/// +/// This does not increase the total issuance. +pub fn mint_creating(who: &T::AccountId, value: BalanceOf) -> PositiveImbalanceOf { + T::Currency::deposit_creating(who, value) +} + +/// Deposit newly issued or slashed `value` into `who`. +pub fn deposit_slashed(who: &T::AccountId, value: NegativeImbalanceOf) { + T::Currency::resolve_creating(who, value) +} + +/// Issue `value` increasing total issuance. +/// +/// Creates a negative imbalance. +pub fn issue(value: BalanceOf) -> NegativeImbalanceOf { + T::Currency::issue(value) +} + +/// Burn the amount from the total issuance. +#[cfg(feature = "runtime-benchmarks")] +pub fn burn(amount: BalanceOf) -> PositiveImbalanceOf { + T::Currency::burn(amount) +} diff --git a/substrate/frame/staking/src/benchmarking.rs b/substrate/frame/staking/src/benchmarking.rs index 1f8580d7a3e67336776517809203a28b0973ebda..a25085a1803692f9d93c8d98886b844f8f6565e3 100644 --- a/substrate/frame/staking/src/benchmarking.rs +++ b/substrate/frame/staking/src/benchmarking.rs @@ -18,7 +18,7 @@ //! Staking pallet benchmarking. use super::*; -use crate::{ConfigOp, Pallet as Staking}; +use crate::{asset, ConfigOp, Pallet as Staking}; use testing_utils::*; use codec::Decode; @@ -26,7 +26,7 @@ use frame_election_provider_support::{bounds::DataProviderBounds, SortedListProv use frame_support::{ pallet_prelude::*, storage::bounded_vec::BoundedVec, - traits::{Currency, Get, Imbalance, UnfilteredDispatchable}, + traits::{Get, Imbalance, UnfilteredDispatchable}, }; use sp_runtime::{ traits::{Bounded, One, StaticLookup, TrailingZeroInput, Zero}, @@ -132,7 +132,7 @@ pub fn create_validator_with_nominators( ErasRewardPoints::::insert(current_era, reward); // Create reward pool - let total_payout = T::Currency::minimum_balance() + let total_payout = asset::existential_deposit::() .saturating_mul(upper_bound.into()) .saturating_mul(1000u32.into()); >::insert(current_era, total_payout); @@ -167,7 +167,7 @@ impl ListScenario { ensure!(!origin_weight.is_zero(), "origin weight must be greater than 0"); // burn the entire issuance. - let i = T::Currency::burn(T::Currency::total_issuance()); + let i = asset::burn::(asset::total_issuance::()); core::mem::forget(i); // create accounts with the origin weight @@ -197,7 +197,7 @@ impl ListScenario { let dest_weight_as_vote = T::VoterList::score_update_worst_case(&origin_stash1, is_increase); - let total_issuance = T::Currency::total_issuance(); + let total_issuance = asset::total_issuance::(); let dest_weight = T::CurrencyToVote::to_currency(dest_weight_as_vote as u128, total_issuance); @@ -223,7 +223,7 @@ benchmarks! { bond { let stash = create_funded_user::("stash", USER_SEED, 100); let reward_destination = RewardDestination::Staked; - let amount = T::Currency::minimum_balance() * 10u32.into(); + let amount = asset::existential_deposit::() * 10u32.into(); whitelist_account!(stash); }: _(RawOrigin::Signed(stash.clone()), amount, reward_destination) verify { @@ -235,7 +235,7 @@ benchmarks! { // clean up any existing state. clear_validators_and_nominators::(); - let origin_weight = MinNominatorBond::::get().max(T::Currency::minimum_balance()); + let origin_weight = MinNominatorBond::::get().max(asset::existential_deposit::()); // setup the worst case list scenario. @@ -249,7 +249,7 @@ benchmarks! { let original_bonded: BalanceOf = Ledger::::get(&controller).map(|l| l.active).ok_or("ledger not created after")?; - let _ = T::Currency::deposit_into_existing(&stash, max_additional).unwrap(); + let _ = asset::mint_existing::(&stash, max_additional).unwrap(); whitelist_account!(stash); }: _(RawOrigin::Signed(stash), max_additional) @@ -264,7 +264,7 @@ benchmarks! { clear_validators_and_nominators::(); // setup the worst case list scenario. - let total_issuance = T::Currency::total_issuance(); + let total_issuance = asset::total_issuance::(); // the weight the nominator will start at. The value used here is expected to be // significantly higher than the first position in a list (e.g. the first bag threshold). let origin_weight = BalanceOf::::try_from(952_994_955_240_703u128) @@ -292,7 +292,7 @@ benchmarks! { let s in 0 .. MAX_SPANS; 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 + let amount = asset::existential_deposit::() * 5u32.into(); // Half of total Staking::::unbond(RawOrigin::Signed(controller.clone()).into(), amount)?; CurrentEra::::put(EraIndex::max_value()); let ledger = Ledger::::get(&controller).ok_or("ledger not created before")?; @@ -312,7 +312,7 @@ benchmarks! { // clean up any existing state. clear_validators_and_nominators::(); - let origin_weight = MinNominatorBond::::get().max(T::Currency::minimum_balance()); + let origin_weight = MinNominatorBond::::get().max(asset::existential_deposit::()); // setup a worst case list scenario. Note that we don't care about the setup of the // destination position because we are doing a removal from the list but no insert. @@ -322,7 +322,7 @@ benchmarks! { add_slashing_spans::(&stash, s); assert!(T::VoterList::contains(&stash)); - let ed = T::Currency::minimum_balance(); + let ed = asset::existential_deposit::(); let mut ledger = Ledger::::get(&controller).unwrap(); ledger.active = ed - One::one(); Ledger::::insert(&controller, ledger); @@ -422,7 +422,7 @@ benchmarks! { // clean up any existing state. clear_validators_and_nominators::(); - let origin_weight = MinNominatorBond::::get().max(T::Currency::minimum_balance()); + let origin_weight = MinNominatorBond::::get().max(asset::existential_deposit::()); // setup a worst case list scenario. Note we don't care about the destination position, because // we are just doing an insert into the origin position. @@ -448,7 +448,7 @@ benchmarks! { // clean up any existing state. clear_validators_and_nominators::(); - let origin_weight = MinNominatorBond::::get().max(T::Currency::minimum_balance()); + let origin_weight = MinNominatorBond::::get().max(asset::existential_deposit::()); // setup a worst case list scenario. Note that we don't care about the setup of the // destination position because we are doing a removal from the list but no insert. @@ -564,7 +564,7 @@ benchmarks! { // Clean up any existing state. clear_validators_and_nominators::(); - let origin_weight = MinNominatorBond::::get().max(T::Currency::minimum_balance()); + let origin_weight = MinNominatorBond::::get().max(asset::existential_deposit::()); // setup a worst case list scenario. Note that we don't care about the setup of the // destination position because we are doing a removal from the list but no insert. @@ -611,21 +611,21 @@ benchmarks! { >::insert(current_era, validator.clone(), >::validators(&validator)); let caller = whitelisted_caller(); - let balance_before = T::Currency::free_balance(&validator); + let balance_before = asset::stakeable_balance::(&validator); let mut nominator_balances_before = Vec::new(); for (stash, _) in &nominators { - let balance = T::Currency::free_balance(stash); + let balance = asset::stakeable_balance::(stash); nominator_balances_before.push(balance); } }: payout_stakers(RawOrigin::Signed(caller), validator.clone(), current_era) verify { - let balance_after = T::Currency::free_balance(&validator); + let balance_after = asset::stakeable_balance::(&validator); ensure!( balance_before < balance_after, "Balance of validator stash should have increased after payout.", ); for ((stash, _), balance_before) in nominators.iter().zip(nominator_balances_before.iter()) { - let balance_after = T::Currency::free_balance(stash); + let balance_after = asset::stakeable_balance::(stash); ensure!( balance_before < &balance_after, "Balance of nominator stash should have increased after payout.", @@ -640,7 +640,7 @@ benchmarks! { clear_validators_and_nominators::(); let origin_weight = MinNominatorBond::::get() - .max(T::Currency::minimum_balance()) + .max(asset::existential_deposit::()) // we use 100 to play friendly with the list threshold values in the mock .max(100u32.into()); @@ -686,7 +686,7 @@ benchmarks! { // clean up any existing state. clear_validators_and_nominators::(); - let origin_weight = MinNominatorBond::::get().max(T::Currency::minimum_balance()); + let origin_weight = MinNominatorBond::::get().max(asset::existential_deposit::()); // setup a worst case list scenario. Note that we don't care about the setup of the // destination position because we are doing a removal from the list but no insert. @@ -697,7 +697,7 @@ benchmarks! { add_slashing_spans::(&stash, s); let l = StakingLedger::::new( stash.clone(), - T::Currency::minimum_balance() - One::one(), + asset::existential_deposit::() - One::one(), ); Ledger::::insert(&controller, l); @@ -764,7 +764,7 @@ benchmarks! { ErasRewardPoints::::insert(current_era, reward); // Create reward pool - let total_payout = T::Currency::minimum_balance() * 1000u32.into(); + let total_payout = asset::existential_deposit::() * 1000u32.into(); >::insert(current_era, total_payout); let caller: T::AccountId = whitelisted_caller(); @@ -793,8 +793,8 @@ benchmarks! { staking_ledger.unlocking.try_push(unlock_chunk.clone()).unwrap(); } Ledger::::insert(controller, staking_ledger); - let slash_amount = T::Currency::minimum_balance() * 10u32.into(); - let balance_before = T::Currency::free_balance(&stash); + let slash_amount = asset::existential_deposit::() * 10u32.into(); + let balance_before = asset::stakeable_balance::(&stash); }: { crate::slashing::do_slash::( &stash, @@ -804,7 +804,7 @@ benchmarks! { EraIndex::zero() ); } verify { - let balance_after = T::Currency::free_balance(&stash); + let balance_after = asset::stakeable_balance::(&stash); assert!(balance_before > balance_after); } @@ -890,7 +890,7 @@ benchmarks! { // clean up any existing state. clear_validators_and_nominators::(); - let origin_weight = MinNominatorBond::::get().max(T::Currency::minimum_balance()); + let origin_weight = MinNominatorBond::::get().max(asset::existential_deposit::()); // setup a worst case list scenario. Note that we don't care about the setup of the // destination position because we are doing a removal from the list but no insert. @@ -972,7 +972,7 @@ benchmarks! { #[cfg(test)] mod tests { use super::*; - use crate::mock::{Balances, ExtBuilder, RuntimeOrigin, Staking, Test}; + use crate::mock::{ExtBuilder, RuntimeOrigin, Staking, Test}; use frame_support::assert_ok; #[test] @@ -1019,16 +1019,17 @@ mod tests { let current_era = CurrentEra::::get().unwrap(); - let original_free_balance = Balances::free_balance(&validator_stash); + let original_stakeable_balance = asset::stakeable_balance::(&validator_stash); assert_ok!(Staking::payout_stakers_by_page( RuntimeOrigin::signed(1337), validator_stash, current_era, 0 )); - let new_free_balance = Balances::free_balance(&validator_stash); + let new_stakeable_balance = asset::stakeable_balance::(&validator_stash); - assert!(original_free_balance < new_free_balance); + // reward increases stakeable balance + assert!(original_stakeable_balance < new_stakeable_balance); }); } diff --git a/substrate/frame/staking/src/ledger.rs b/substrate/frame/staking/src/ledger.rs index dc4b4fc326b819304d719aedbfbefcf3963ec0fd..ac3be04cf607176d6aee437a7b49bc3f4afffd55 100644 --- a/substrate/frame/staking/src/ledger.rs +++ b/substrate/frame/staking/src/ledger.rs @@ -31,15 +31,12 @@ //! performed through the methods exposed by the [`StakingLedger`] implementation in order to ensure //! state consistency. -use frame_support::{ - defensive, ensure, - traits::{Defensive, LockableCurrency}, -}; +use frame_support::{defensive, ensure, traits::Defensive}; use sp_staking::{StakingAccount, StakingInterface}; use crate::{ - BalanceOf, Bonded, Config, Error, Ledger, Pallet, Payee, RewardDestination, StakingLedger, - VirtualStakers, STAKING_ID, + asset, BalanceOf, Bonded, Config, Error, Ledger, Pallet, Payee, RewardDestination, + StakingLedger, VirtualStakers, }; #[cfg(any(feature = "runtime-benchmarks", test))] @@ -190,12 +187,7 @@ impl StakingLedger { // We skip locking virtual stakers. if !Pallet::::is_virtual_staker(&self.stash) { // for direct stakers, update lock on stash based on ledger. - T::Currency::set_lock( - STAKING_ID, - &self.stash, - self.total, - frame_support::traits::WithdrawReasons::all(), - ); + asset::update_stake::(&self.stash, self.total); } Ledger::::insert( @@ -269,7 +261,7 @@ impl StakingLedger { // kill virtual staker if it exists. if >::take(&stash).is_none() { // if not virtual staker, clear locks. - T::Currency::remove_lock(STAKING_ID, &ledger.stash); + asset::kill_stake::(&ledger.stash); } Ok(()) diff --git a/substrate/frame/staking/src/lib.rs b/substrate/frame/staking/src/lib.rs index 9e59cbd3d0cb8bc2b2b83c13f07d3951d21c095b..19d999109d8ddf6631ae8b4c5e943a90f3dd5e2a 100644 --- a/substrate/frame/staking/src/lib.rs +++ b/substrate/frame/staking/src/lib.rs @@ -295,6 +295,7 @@ pub(crate) mod mock; #[cfg(test)] mod tests; +pub mod asset; pub mod election_size_tracker; pub mod inflation; pub mod ledger; diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 2df3bc084eb0b4f6aef5cbbe665d3f2b66ec80cb..6c4fe8140e8ef2528ba65d6d279c87a7520cd721 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -27,8 +27,8 @@ use frame_support::{ dispatch::WithPostDispatchInfo, pallet_prelude::*, traits::{ - Currency, Defensive, DefensiveSaturating, EstimateNextNewSession, Get, Imbalance, - InspectLockableCurrency, Len, LockableCurrency, OnUnbalanced, TryCollect, UnixTime, + Defensive, DefensiveSaturating, EstimateNextNewSession, Get, Imbalance, Len, OnUnbalanced, + TryCollect, UnixTime, }, weights::Weight, }; @@ -50,7 +50,7 @@ use sp_staking::{ }; use crate::{ - election_size_tracker::StaticTracker, log, slashing, weights::WeightInfo, ActiveEraInfo, + asset, election_size_tracker::StaticTracker, log, slashing, weights::WeightInfo, ActiveEraInfo, BalanceOf, EraInfo, EraPayout, Exposure, ExposureOf, Forcing, IndividualExposure, LedgerIntegrityState, MaxNominationsOf, MaxWinnersOf, Nominations, NominationsQuota, PositiveImbalanceOf, RewardDestination, SessionInterface, StakingLedger, ValidatorPrefs, @@ -96,7 +96,7 @@ impl Pallet { pub(crate) fn inspect_bond_state( stash: &T::AccountId, ) -> Result> { - let lock = T::Currency::balance_locked(crate::STAKING_ID, &stash); + let lock = asset::staked::(&stash); let controller = >::get(stash).ok_or_else(|| { if lock == Zero::zero() { @@ -142,7 +142,7 @@ impl Pallet { pub fn weight_of_fn() -> Box VoteWeight> { // NOTE: changing this to unboxed `impl Fn(..)` return type and the pallet will still // compile, while some types in mock fail to resolve. - let issuance = T::Currency::total_issuance(); + let issuance = asset::total_issuance::(); Box::new(move |who: &T::AccountId| -> VoteWeight { Self::slashable_balance_of_vote_weight(who, issuance) }) @@ -150,7 +150,7 @@ impl Pallet { /// Same as `weight_of_fn`, but made for one time use. pub fn weight_of(who: &T::AccountId) -> VoteWeight { - let issuance = T::Currency::total_issuance(); + let issuance = asset::total_issuance::(); Self::slashable_balance_of_vote_weight(who, issuance) } @@ -164,7 +164,7 @@ impl Pallet { } else { // additional amount or actual balance of stash whichever is lower. additional.min( - T::Currency::free_balance(stash) + asset::stakeable_balance::(stash) .checked_sub(&ledger.total) .ok_or(ArithmeticError::Overflow)?, ) @@ -173,7 +173,7 @@ impl Pallet { ledger.total = ledger.total.checked_add(&extra).ok_or(ArithmeticError::Overflow)?; ledger.active = ledger.active.checked_add(&extra).ok_or(ArithmeticError::Overflow)?; // last check: the new active amount of ledger must be more than ED. - ensure!(ledger.active >= T::Currency::minimum_balance(), Error::::InsufficientBond); + ensure!(ledger.active >= asset::existential_deposit::(), Error::::InsufficientBond); // NOTE: ledger must be updated prior to calling `Self::weight_of`. ledger.update()?; @@ -198,7 +198,7 @@ impl Pallet { } let new_total = ledger.total; - let ed = T::Currency::minimum_balance(); + let ed = asset::existential_deposit::(); let used_weight = if ledger.unlocking.is_empty() && (ledger.active < ed || ledger.active.is_zero()) { // This account must have called `unbond()` with some value that caused the active @@ -414,12 +414,12 @@ impl Pallet { 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 => asset::mint_existing::(stash, amount), RewardDestination::Staked => Self::ledger(Stash(stash.clone())) .and_then(|mut ledger| { ledger.active += amount; ledger.total += amount; - let r = T::Currency::deposit_into_existing(stash, amount).ok(); + let r = asset::mint_existing::(stash, amount); let _ = ledger .update() @@ -429,7 +429,7 @@ impl Pallet { }) .unwrap_or_default(), RewardDestination::Account(ref dest_account) => - Some(T::Currency::deposit_creating(&dest_account, amount)), + Some(asset::mint_creating::(&dest_account, amount)), RewardDestination::None => None, #[allow(deprecated)] RewardDestination::Controller => Self::bonded(stash) @@ -437,7 +437,7 @@ impl Pallet { defensive!("Paying out controller as reward destination which is deprecated and should be migrated."); // This should never happen once payees with a `Controller` variant have been migrated. // But if it does, just pay the controller account. - T::Currency::deposit_creating(&controller, amount) + asset::mint_creating::(&controller, amount) }), }; maybe_imbalance.map(|imbalance| (imbalance, dest)) @@ -576,7 +576,7 @@ impl Pallet { let era_duration = (now_as_millis_u64.defensive_saturating_sub(active_era_start)) .saturated_into::(); let staked = Self::eras_total_stake(&active_era.index); - let issuance = T::Currency::total_issuance(); + let issuance = asset::total_issuance::(); let (validator_payout, remainder) = T::EraPayout::era_payout(staked, issuance, era_duration); @@ -597,7 +597,7 @@ impl Pallet { // Set ending era reward. >::insert(&active_era.index, validator_payout); - T::RewardRemainder::on_unbalanced(T::Currency::issue(remainder)); + T::RewardRemainder::on_unbalanced(asset::issue::(remainder)); // Clear disabled validators. >::kill(); @@ -748,7 +748,7 @@ impl Pallet { fn collect_exposures( supports: BoundedSupportsOf, ) -> BoundedVec<(T::AccountId, Exposure>), MaxWinnersOf> { - let total_issuance = T::Currency::total_issuance(); + let total_issuance = asset::total_issuance::(); let to_currency = |e: frame_election_provider_support::ExtendedBalance| { T::CurrencyToVote::to_currency(e, total_issuance) }; @@ -1581,7 +1581,7 @@ impl ScoreProvider for Pallet { // also, we play a trick to make sure that a issuance based-`CurrencyToVote` behaves well: // This will make sure that total issuance is zero, thus the currency to vote will be a 1-1 // conversion. - let imbalance = T::Currency::burn(T::Currency::total_issuance()); + let imbalance = asset::burn::(asset::total_issuance::()); // kinda ugly, but gets the job done. The fact that this works here is a HUGE exception. // Don't try this pattern in other places. core::mem::forget(imbalance); @@ -1919,7 +1919,7 @@ impl StakingInterface for Pallet { impl sp_staking::StakingUnchecked for Pallet { fn migrate_to_virtual_staker(who: &Self::AccountId) { - T::Currency::remove_lock(crate::STAKING_ID, who); + asset::kill_stake::(who); VirtualStakers::::insert(who, ()); } @@ -1956,12 +1956,7 @@ impl sp_staking::StakingUnchecked for Pallet { fn migrate_to_direct_staker(who: &Self::AccountId) { assert!(VirtualStakers::::contains_key(who)); let ledger = StakingLedger::::get(Stash(who.clone())).unwrap(); - T::Currency::set_lock( - crate::STAKING_ID, - who, - ledger.total, - frame_support::traits::WithdrawReasons::all(), - ); + asset::update_stake::(who, ledger.total); VirtualStakers::::remove(who); } } @@ -2097,7 +2092,7 @@ impl Pallet { // ensure locks consistency. if VirtualStakers::::contains_key(stash.clone()) { ensure!( - T::Currency::balance_locked(crate::STAKING_ID, &stash) == Zero::zero(), + asset::staked::(&stash) == Zero::zero(), "virtual stakers should not have any locked balance" ); ensure!( diff --git a/substrate/frame/staking/src/pallet/mod.rs b/substrate/frame/staking/src/pallet/mod.rs index 79f9d298ada788ff7ee4278bfb4290eaf44e3c10..28aa4f89b6227e8c58dd832f15a5699838398bdd 100644 --- a/substrate/frame/staking/src/pallet/mod.rs +++ b/substrate/frame/staking/src/pallet/mod.rs @@ -25,8 +25,8 @@ use frame_election_provider_support::{ use frame_support::{ pallet_prelude::*, traits::{ - Currency, Defensive, DefensiveSaturating, EnsureOrigin, EstimateNextNewSession, Get, - InspectLockableCurrency, LockableCurrency, OnUnbalanced, UnixTime, WithdrawReasons, + Defensive, DefensiveSaturating, EnsureOrigin, EstimateNextNewSession, Get, + InspectLockableCurrency, LockableCurrency, OnUnbalanced, UnixTime, }, weights::Weight, BoundedVec, @@ -48,11 +48,11 @@ mod impls; pub use impls::*; use crate::{ - slashing, weights::WeightInfo, AccountIdLookupOf, ActiveEraInfo, BalanceOf, DisablingStrategy, - EraPayout, EraRewardPoints, Exposure, ExposurePage, Forcing, LedgerIntegrityState, - MaxNominationsOf, NegativeImbalanceOf, Nominations, NominationsQuota, PositiveImbalanceOf, - RewardDestination, SessionInterface, StakingLedger, UnappliedSlash, UnlockChunk, - ValidatorPrefs, + asset, slashing, weights::WeightInfo, AccountIdLookupOf, ActiveEraInfo, BalanceOf, + DisablingStrategy, EraPayout, EraRewardPoints, Exposure, ExposurePage, Forcing, + LedgerIntegrityState, MaxNominationsOf, NegativeImbalanceOf, Nominations, NominationsQuota, + PositiveImbalanceOf, RewardDestination, SessionInterface, StakingLedger, UnappliedSlash, + UnlockChunk, ValidatorPrefs, }; // The speculative number of spans are used as an input of the weight annotation of @@ -779,7 +779,7 @@ pub mod pallet { status ); assert!( - T::Currency::free_balance(stash) >= balance, + asset::stakeable_balance::(stash) >= balance, "Stash does not have enough balance to bond." ); frame_support::assert_ok!(>::bond( @@ -1023,13 +1023,14 @@ pub mod pallet { } // Reject a bond which is considered to be _dust_. - if value < T::Currency::minimum_balance() { + if value < asset::existential_deposit::() { return Err(Error::::InsufficientBond.into()) } - frame_system::Pallet::::inc_consumers(&stash).map_err(|_| Error::::BadState)?; + // Would fail if account has no provider. + frame_system::Pallet::::inc_consumers(&stash)?; - let stash_balance = T::Currency::free_balance(&stash); + let stash_balance = asset::stakeable_balance::(&stash); let value = value.min(stash_balance); Self::deposit_event(Event::::Bonded { stash: stash.clone(), amount: value }); let ledger = StakingLedger::::new(stash.clone(), value); @@ -1067,7 +1068,7 @@ pub mod pallet { /// Schedule a portion of the stash to be unlocked ready for transfer out after the bond /// period ends. If this leaves an amount actively bonded less than - /// T::Currency::minimum_balance(), then it is increased to the full amount. + /// [`asset::existential_deposit`], then it is increased to the full amount. /// /// The dispatch origin for this call must be _Signed_ by the controller, not the stash. /// @@ -1123,7 +1124,7 @@ pub mod pallet { ledger.active -= value; // Avoid there being a dust balance left in the staking system. - if ledger.active < T::Currency::minimum_balance() { + if ledger.active < asset::existential_deposit::() { value += ledger.active; ledger.active = Zero::zero(); } @@ -1653,7 +1654,10 @@ pub mod pallet { let initial_unlocking = ledger.unlocking.len() as u32; let (ledger, rebonded_value) = ledger.rebond(value); // Last check: the new active amount of ledger must be more than ED. - ensure!(ledger.active >= T::Currency::minimum_balance(), Error::::InsufficientBond); + ensure!( + ledger.active >= asset::existential_deposit::(), + Error::::InsufficientBond + ); Self::deposit_event(Event::::Bonded { stash: ledger.stash.clone(), @@ -1705,8 +1709,8 @@ pub mod pallet { // virtual stakers should not be allowed to be reaped. ensure!(!Self::is_virtual_staker(&stash), Error::::VirtualStakerNotAllowed); - let ed = T::Currency::minimum_balance(); - let origin_balance = T::Currency::total_balance(&stash); + let ed = asset::existential_deposit::(); + let origin_balance = asset::total_balance::(&stash); let ledger_total = Self::ledger(Stash(stash.clone())).map(|l| l.total).unwrap_or_default(); let reapable = origin_balance < ed || @@ -2073,8 +2077,8 @@ pub mod pallet { // cannot restore ledger for virtual stakers. ensure!(!Self::is_virtual_staker(&stash), Error::::VirtualStakerNotAllowed); - let current_lock = T::Currency::balance_locked(crate::STAKING_ID, &stash); - let stash_balance = T::Currency::free_balance(&stash); + let current_lock = asset::staked::(&stash); + let stash_balance = asset::stakeable_balance::(&stash); let (new_controller, new_total) = match Self::inspect_bond_state(&stash) { Ok(LedgerIntegrityState::Corrupted) => { @@ -2083,12 +2087,7 @@ pub mod pallet { let new_total = if let Some(total) = maybe_total { let new_total = total.min(stash_balance); // enforce lock == ledger.amount. - T::Currency::set_lock( - crate::STAKING_ID, - &stash, - new_total, - WithdrawReasons::all(), - ); + asset::update_stake::(&stash, new_total); new_total } else { current_lock @@ -2115,18 +2114,13 @@ pub mod pallet { // to enforce a new ledger.total and staking lock for this stash. let new_total = maybe_total.ok_or(Error::::CannotRestoreLedger)?.min(stash_balance); - T::Currency::set_lock( - crate::STAKING_ID, - &stash, - new_total, - WithdrawReasons::all(), - ); + asset::update_stake::(&stash, new_total); Ok((stash.clone(), new_total)) }, Err(Error::::BadState) => { // the stash and ledger do not exist but lock is lingering. - T::Currency::remove_lock(crate::STAKING_ID, &stash); + asset::kill_stake::(&stash); ensure!( Self::inspect_bond_state(&stash) == Err(Error::::NotStash), Error::::BadState diff --git a/substrate/frame/staking/src/slashing.rs b/substrate/frame/staking/src/slashing.rs index 9bc8197c50b3ec29f8a308128383ce50dba95a77..9fb782265b8b973cf90feac8fe1d521ff5132380 100644 --- a/substrate/frame/staking/src/slashing.rs +++ b/substrate/frame/staking/src/slashing.rs @@ -50,15 +50,15 @@ //! Based on research at use crate::{ - BalanceOf, Config, DisabledValidators, DisablingStrategy, Error, Exposure, NegativeImbalanceOf, - NominatorSlashInEra, Pallet, Perbill, SessionInterface, SpanSlash, UnappliedSlash, - ValidatorSlashInEra, + asset, BalanceOf, Config, DisabledValidators, DisablingStrategy, Error, Exposure, + NegativeImbalanceOf, NominatorSlashInEra, Pallet, Perbill, SessionInterface, SpanSlash, + UnappliedSlash, ValidatorSlashInEra, }; use alloc::vec::Vec; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{ ensure, - traits::{Currency, Defensive, DefensiveSaturating, Imbalance, OnUnbalanced}, + traits::{Defensive, DefensiveSaturating, Imbalance, OnUnbalanced}, }; use scale_info::TypeInfo; use sp_runtime::{ @@ -578,7 +578,7 @@ pub fn do_slash( Err(_) => return, // nothing to do. }; - let value = ledger.slash(value, T::Currency::minimum_balance(), slash_era); + let value = ledger.slash(value, asset::existential_deposit::(), slash_era); if value.is_zero() { // nothing to do return @@ -586,7 +586,7 @@ pub fn do_slash( // Skip slashing for virtual stakers. The pallets managing them should handle the slashing. if !Pallet::::is_virtual_staker(stash) { - let (imbalance, missing) = T::Currency::slash(stash, value); + let (imbalance, missing) = asset::slash::(stash, value); slashed_imbalance.subsume(imbalance); if !missing.is_zero() { @@ -656,7 +656,7 @@ fn pay_reporters( // this cancels out the reporter reward imbalance internally, leading // to no change in total issuance. - T::Currency::resolve_creating(reporter, reporter_reward); + asset::deposit_slashed::(reporter, reporter_reward); } // the rest goes to the on-slash imbalance handler (e.g. treasury) diff --git a/substrate/frame/staking/src/testing_utils.rs b/substrate/frame/staking/src/testing_utils.rs index 65aaa5f09de4e8476f961574946f9dcb1f4f4d8d..efd4a40f1ab4a6d82fbc83b2b18d4497a154aaaa 100644 --- a/substrate/frame/staking/src/testing_utils.rs +++ b/substrate/frame/staking/src/testing_utils.rs @@ -28,7 +28,7 @@ use rand_chacha::{ use sp_io::hashing::blake2_256; use frame_election_provider_support::SortedListProvider; -use frame_support::{pallet_prelude::*, traits::Currency}; +use frame_support::pallet_prelude::*; use sp_runtime::{traits::StaticLookup, Perbill}; const SEED: u32 = 0; @@ -53,8 +53,8 @@ pub fn create_funded_user( balance_factor: u32, ) -> T::AccountId { let user = account(string, n, SEED); - let balance = T::Currency::minimum_balance() * balance_factor.into(); - let _ = T::Currency::make_free_balance_be(&user, balance); + let balance = asset::existential_deposit::() * balance_factor.into(); + let _ = asset::set_stakeable_balance::(&user, balance); user } @@ -65,7 +65,7 @@ pub fn create_funded_user_with_balance( balance: BalanceOf, ) -> T::AccountId { let user = account(string, n, SEED); - let _ = T::Currency::make_free_balance_be(&user, balance); + let _ = asset::set_stakeable_balance::(&user, balance); user } @@ -77,7 +77,7 @@ pub fn create_stash_controller( ) -> Result<(T::AccountId, T::AccountId), &'static str> { let staker = create_funded_user::("stash", n, balance_factor); let amount = - T::Currency::minimum_balance().max(1u64.into()) * (balance_factor / 10).max(1).into(); + asset::existential_deposit::().max(1u64.into()) * (balance_factor / 10).max(1).into(); Staking::::bond(RawOrigin::Signed(staker.clone()).into(), amount, destination)?; Ok((staker.clone(), staker)) } @@ -96,7 +96,7 @@ pub fn create_unique_stash_controller( } else { create_funded_user::("controller", n, balance_factor) }; - let amount = T::Currency::minimum_balance() * (balance_factor / 10).max(1).into(); + let amount = asset::existential_deposit::() * (balance_factor / 10).max(1).into(); Staking::::bond(RawOrigin::Signed(stash.clone()).into(), amount, destination)?; // update ledger to be a *different* controller to stash @@ -129,7 +129,7 @@ pub fn create_stash_and_dead_payee( let staker = create_funded_user::("stash", n, 0); // payee has no funds let payee = create_funded_user::("payee", n, 0); - let amount = T::Currency::minimum_balance() * (balance_factor / 10).max(1).into(); + let amount = asset::existential_deposit::() * (balance_factor / 10).max(1).into(); Staking::::bond( RawOrigin::Signed(staker.clone()).into(), amount, diff --git a/substrate/frame/staking/src/tests.rs b/substrate/frame/staking/src/tests.rs index ab2c00ca9cccd0b5b933519a2e3d0cec725d821d..639f4096456fb2d59e195aac4377de84a3917d55 100644 --- a/substrate/frame/staking/src/tests.rs +++ b/substrate/frame/staking/src/tests.rs @@ -18,7 +18,7 @@ //! Tests for the module. use super::{ConfigOp, Event, *}; -use crate::ledger::StakingLedgerInspect; +use crate::{asset, ledger::StakingLedgerInspect}; use frame_election_provider_support::{ bounds::{DataProviderBounds, ElectionBoundsBuilder}, ElectionProvider, SortedListProvider, Support, @@ -27,7 +27,7 @@ use frame_support::{ assert_noop, assert_ok, assert_storage_noop, dispatch::{extract_actual_weight, GetDispatchInfo, WithPostDispatchInfo}, pallet_prelude::*, - traits::{Currency, Get, InspectLockableCurrency, ReservableCurrency}, + traits::{Currency, Get, ReservableCurrency}, }; use mock::*; @@ -229,8 +229,8 @@ fn basic_setup_works() { assert_eq!(active_era(), 0); // Account 10 has `balance_factor` free balance - assert_eq!(Balances::free_balance(10), 1); - assert_eq!(Balances::free_balance(10), 1); + assert_eq!(asset::stakeable_balance::(&10), 1); + assert_eq!(asset::stakeable_balance::(&10), 1); // New era is not being forced assert_eq!(Staking::force_era(), Forcing::NotForcing); @@ -311,9 +311,9 @@ fn change_controller_already_paired_once_stash() { #[test] fn rewards_should_work() { ExtBuilder::default().nominate(true).session_per_era(3).build_and_execute(|| { - let init_balance_11 = Balances::total_balance(&11); - let init_balance_21 = Balances::total_balance(&21); - let init_balance_101 = Balances::total_balance(&101); + let init_balance_11 = asset::total_balance::(&11); + let init_balance_21 = asset::total_balance::(&21); + let init_balance_101 = asset::total_balance::(&101); // Set payees Payee::::insert(11, RewardDestination::Account(11)); @@ -332,9 +332,9 @@ fn rewards_should_work() { start_session(1); assert_eq_uvec!(Session::validators(), vec![11, 21]); - assert_eq!(Balances::total_balance(&11), init_balance_11); - assert_eq!(Balances::total_balance(&21), init_balance_21); - assert_eq!(Balances::total_balance(&101), init_balance_101); + assert_eq!(asset::total_balance::(&11), init_balance_11); + assert_eq!(asset::total_balance::(&21), init_balance_21); + assert_eq!(asset::total_balance::(&101), init_balance_101); assert_eq!( Staking::eras_reward_points(active_era()), EraRewardPoints { @@ -363,17 +363,17 @@ fn rewards_should_work() { mock::make_all_reward_payment(0); assert_eq_error_rate!( - Balances::total_balance(&11), + asset::total_balance::(&11), init_balance_11 + part_for_11 * total_payout_0 * 2 / 3, 2, ); assert_eq_error_rate!( - Balances::total_balance(&21), + asset::total_balance::(&21), init_balance_21 + part_for_21 * total_payout_0 * 1 / 3, 2, ); assert_eq_error_rate!( - Balances::total_balance(&101), + asset::total_balance::(&101), init_balance_101 + part_for_101_from_11 * total_payout_0 * 2 / 3 + part_for_101_from_21 * total_payout_0 * 1 / 3, @@ -402,17 +402,17 @@ fn rewards_should_work() { mock::make_all_reward_payment(1); assert_eq_error_rate!( - Balances::total_balance(&11), + asset::total_balance::(&11), init_balance_11 + part_for_11 * (total_payout_0 * 2 / 3 + total_payout_1), 2, ); assert_eq_error_rate!( - Balances::total_balance(&21), + asset::total_balance::(&21), init_balance_21 + part_for_21 * total_payout_0 * 1 / 3, 2, ); assert_eq_error_rate!( - Balances::total_balance(&101), + asset::total_balance::(&101), init_balance_101 + part_for_101_from_11 * (total_payout_0 * 2 / 3 + total_payout_1) + part_for_101_from_21 * total_payout_0 * 1 / 3, @@ -429,7 +429,7 @@ fn staking_should_work() { // put some money in account that we'll use. for i in 1..5 { - let _ = Balances::make_free_balance_be(&i, 2000); + let _ = asset::set_stakeable_balance::(&i, 2000); } // --- Block 2: @@ -611,7 +611,7 @@ fn nominating_and_rewards_should_work() { // give the man some money let initial_balance = 1000; for i in [1, 3, 5, 11, 21].iter() { - let _ = Balances::make_free_balance_be(i, initial_balance); + let _ = asset::set_stakeable_balance::(&i, initial_balance); } // bond two account pairs and state interest in nomination. @@ -636,12 +636,12 @@ fn nominating_and_rewards_should_work() { assert_eq_uvec!(validator_controllers(), vec![21, 11]); // old validators must have already received some rewards. - let initial_balance_41 = Balances::total_balance(&41); - let mut initial_balance_21 = Balances::total_balance(&21); + let initial_balance_41 = asset::total_balance::(&41); + let mut initial_balance_21 = asset::total_balance::(&21); mock::make_all_reward_payment(0); - assert_eq!(Balances::total_balance(&41), initial_balance_41 + total_payout_0 / 2); - assert_eq!(Balances::total_balance(&21), initial_balance_21 + total_payout_0 / 2); - initial_balance_21 = Balances::total_balance(&21); + assert_eq!(asset::total_balance::(&41), initial_balance_41 + total_payout_0 / 2); + assert_eq!(asset::total_balance::(&21), initial_balance_21 + total_payout_0 / 2); + initial_balance_21 = asset::total_balance::(&21); assert_eq!(ErasStakersPaged::::iter_prefix_values((active_era(),)).count(), 2); assert_eq!( @@ -683,30 +683,30 @@ fn nominating_and_rewards_should_work() { // Nominator 2: has [400/1800 ~ 2/9 from 10] + [600/2200 ~ 3/11 from 21]'s reward. ==> // 2/9 + 3/11 assert_eq_error_rate!( - Balances::total_balance(&1), + asset::total_balance::(&1), initial_balance + (2 * payout_for_11 / 9 + 3 * payout_for_21 / 11), 2, ); // Nominator 3: has [400/1800 ~ 2/9 from 10] + [600/2200 ~ 3/11 from 21]'s reward. ==> // 2/9 + 3/11 - assert_eq!(Balances::total_balance(&3), initial_balance); + assert_eq!(asset::total_balance::(&3), initial_balance); // 333 is the reward destination for 3. assert_eq_error_rate!( - Balances::total_balance(&333), + asset::total_balance::(&333), 2 * payout_for_11 / 9 + 3 * payout_for_21 / 11, 2 ); // Validator 11: got 800 / 1800 external stake => 8/18 =? 4/9 => Validator's share = 5/9 assert_eq_error_rate!( - Balances::total_balance(&11), + asset::total_balance::(&11), initial_balance + 5 * payout_for_11 / 9, 2, ); // Validator 21: got 1200 / 2200 external stake => 12/22 =? 6/11 => Validator's share = // 5/11 assert_eq_error_rate!( - Balances::total_balance(&21), + asset::total_balance::(&21), initial_balance_21 + 5 * payout_for_21 / 11, 2, ); @@ -993,7 +993,7 @@ fn cannot_transfer_staked_balance() { // Confirm account 11 is stashed assert_eq!(Staking::bonded(&11), Some(11)); // Confirm account 11 has some free balance - assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(asset::stakeable_balance::(&11), 1000); // Confirm account 11 (via controller) is totally staked assert_eq!(Staking::eras_stakers(active_era(), &11).total, 1000); // Confirm account 11 cannot transfer as a result @@ -1003,7 +1003,7 @@ fn cannot_transfer_staked_balance() { ); // Give account 11 extra free balance - let _ = Balances::make_free_balance_be(&11, 10000); + let _ = asset::set_stakeable_balance::(&11, 10000); // Confirm that account 11 can now transfer some balance assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(11), 21, 1)); }); @@ -1018,7 +1018,7 @@ fn cannot_transfer_staked_balance_2() { // Confirm account 21 is stashed assert_eq!(Staking::bonded(&21), Some(21)); // Confirm account 21 has some free balance - assert_eq!(Balances::free_balance(21), 2000); + assert_eq!(asset::stakeable_balance::(&21), 2000); // Confirm account 21 (via controller) is totally staked assert_eq!(Staking::eras_stakers(active_era(), &21).total, 1000); // Confirm account 21 can transfer at most 1000 @@ -1037,14 +1037,14 @@ fn cannot_reserve_staked_balance() { // Confirm account 11 is stashed assert_eq!(Staking::bonded(&11), Some(11)); // Confirm account 11 has some free balance - assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(asset::stakeable_balance::(&11), 1000); // Confirm account 11 (via controller 10) is totally staked assert_eq!(Staking::eras_stakers(active_era(), &11).own, 1000); // Confirm account 11 cannot reserve as a result assert_noop!(Balances::reserve(&11, 1), BalancesError::::LiquidityRestrictions); // Give account 11 extra free balance - let _ = Balances::make_free_balance_be(&11, 10000); + let _ = asset::set_stakeable_balance::(&11, 10000); // Confirm account 11 can now reserve balance assert_ok!(Balances::reserve(&11, 1)); }); @@ -1057,9 +1057,9 @@ fn reward_destination_works() { // Check that account 11 is a validator assert!(Session::validators().contains(&11)); // Check the balance of the validator account - assert_eq!(Balances::free_balance(10), 1); + assert_eq!(asset::stakeable_balance::(&10), 1); // Check the balance of the stash account - assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(asset::stakeable_balance::(&11), 1000); // Check how much is at stake assert_eq!( Staking::ledger(11.into()).unwrap(), @@ -1082,7 +1082,7 @@ fn reward_destination_works() { // 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); + assert_eq!(asset::stakeable_balance::(&11), 1000 + total_payout_0); // Check that amount at stake increased accordingly assert_eq!( Staking::ledger(11.into()).unwrap(), @@ -1111,7 +1111,7 @@ fn reward_destination_works() { // Check that RewardDestination is 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); + assert_eq!(asset::stakeable_balance::(&11), 1000 + total_payout_0 + total_payout_1); // Record this value let recorded_stash_balance = 1000 + total_payout_0 + total_payout_1; // Check that amount at stake is NOT increased @@ -1133,7 +1133,7 @@ fn reward_destination_works() { >::insert(&11, RewardDestination::Account(11)); // Check controller balance - assert_eq!(Balances::free_balance(11), 23150); + assert_eq!(asset::stakeable_balance::(&11), 23150); // Compute total payout now for whole duration as other parameter won't change let total_payout_2 = current_total_payout_for_duration(reward_time_per_era()); @@ -1145,7 +1145,7 @@ fn reward_destination_works() { // Check that RewardDestination is 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); + assert_eq!(asset::stakeable_balance::(&11), recorded_stash_balance + total_payout_2); // Check that amount at stake is NOT increased assert_eq!( Staking::ledger(11.into()).unwrap(), @@ -1179,8 +1179,8 @@ fn validator_payment_prefs_work() { mock::start_active_era(1); mock::make_all_reward_payment(0); - let balance_era_1_11 = Balances::total_balance(&11); - let balance_era_1_101 = Balances::total_balance(&101); + let balance_era_1_11 = asset::total_balance::(&11); + let balance_era_1_101 = asset::total_balance::(&101); // Compute total payout now for whole duration as other parameter won't change let total_payout_1 = current_total_payout_for_duration(reward_time_per_era()); @@ -1194,8 +1194,16 @@ fn validator_payment_prefs_work() { let shared_cut = total_payout_1 - taken_cut; let reward_of_10 = shared_cut * exposure_1.own / exposure_1.total + taken_cut; let reward_of_100 = shared_cut * exposure_1.others[0].value / exposure_1.total; - assert_eq_error_rate!(Balances::total_balance(&11), balance_era_1_11 + reward_of_10, 2); - assert_eq_error_rate!(Balances::total_balance(&101), balance_era_1_101 + reward_of_100, 2); + assert_eq_error_rate!( + asset::total_balance::(&11), + balance_era_1_11 + reward_of_10, + 2 + ); + assert_eq_error_rate!( + asset::total_balance::(&101), + balance_era_1_101 + reward_of_100, + 2 + ); }); } @@ -1222,7 +1230,7 @@ fn bond_extra_works() { ); // Give account 11 some large free balance greater than total - let _ = Balances::make_free_balance_be(&11, 1000000); + let _ = asset::set_stakeable_balance::(&11, 1000000); // Call the bond_extra function from controller, add only 100 assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(11), 100)); @@ -1284,13 +1292,13 @@ fn bond_extra_and_withdraw_unbonded_works() { assert_ok!(Staking::set_payee(RuntimeOrigin::signed(11), RewardDestination::Stash)); // Give account 11 some large free balance greater than total - let _ = Balances::make_free_balance_be(&11, 1000000); + let _ = asset::set_stakeable_balance::(&11, 1000000); // Initial config should be correct assert_eq!(active_era(), 0); // check the balance of a validator accounts. - assert_eq!(Balances::total_balance(&11), 1000000); + assert_eq!(asset::total_balance::(&11), 1000000); // confirm that 10 is a normal validator and gets paid at the end of the era. mock::start_active_era(1); @@ -1495,7 +1503,7 @@ fn rebond_works() { assert_ok!(Staking::set_payee(RuntimeOrigin::signed(11), RewardDestination::Stash)); // Give account 11 some large free balance greater than total - let _ = Balances::make_free_balance_be(&11, 1000000); + let _ = asset::set_stakeable_balance::(&11, 1000000); // confirm that 10 is a normal validator and gets paid at the end of the era. mock::start_active_era(1); @@ -1621,7 +1629,7 @@ fn rebond_is_fifo() { assert_ok!(Staking::set_payee(RuntimeOrigin::signed(11), RewardDestination::Stash)); // Give account 11 some large free balance greater than total - let _ = Balances::make_free_balance_be(&11, 1000000); + let _ = asset::set_stakeable_balance::(&11, 1000000); // confirm that 10 is a normal validator and gets paid at the end of the era. mock::start_active_era(1); @@ -1717,7 +1725,7 @@ fn rebond_emits_right_value_in_event() { assert_ok!(Staking::set_payee(RuntimeOrigin::signed(11), RewardDestination::Stash)); // Give account 11 some large free balance greater than total - let _ = Balances::make_free_balance_be(&11, 1000000); + let _ = asset::set_stakeable_balance::(&11, 1000000); // confirm that 10 is a normal validator and gets paid at the end of the era. mock::start_active_era(1); @@ -1852,8 +1860,8 @@ fn reward_to_stake_works() { assert_eq!(Staking::eras_stakers(active_era(), &21).total, 2000); // Give the man some money. - let _ = Balances::make_free_balance_be(&10, 1000); - let _ = Balances::make_free_balance_be(&20, 1000); + let _ = asset::set_stakeable_balance::(&10, 1000); + let _ = asset::set_stakeable_balance::(&20, 1000); // Bypass logic and change current exposure EraInfo::::set_exposure(0, &21, Exposure { total: 69, own: 69, others: vec![] }); @@ -1880,7 +1888,7 @@ fn reward_to_stake_works() { assert_eq!(Staking::eras_stakers(active_era(), &11).total, 1000); assert_eq!(Staking::eras_stakers(active_era(), &21).total, 2000); - let _11_balance = Balances::free_balance(&11); + let _11_balance = asset::stakeable_balance::(&11); assert_eq!(_11_balance, 1000 + total_payout_0 / 2); // Trigger another new era as the info are frozen before the era start. @@ -1899,7 +1907,7 @@ fn reap_stash_works() { .balance_factor(10) .build_and_execute(|| { // given - assert_eq!(Balances::balance_locked(STAKING_ID, &11), 10 * 1000); + assert_eq!(asset::staked::(&11), 10 * 1000); assert_eq!(Staking::bonded(&11), Some(11)); assert!(>::contains_key(&11)); @@ -1926,7 +1934,7 @@ fn reap_stash_works() { assert!(!>::contains_key(&11)); assert!(!>::contains_key(&11)); // lock is removed. - assert_eq!(Balances::balance_locked(STAKING_ID, &11), 0); + assert_eq!(asset::staked::(&11), 0); }); } @@ -1937,7 +1945,7 @@ fn reap_stash_works_with_existential_deposit_zero() { .balance_factor(10) .build_and_execute(|| { // given - assert_eq!(Balances::balance_locked(STAKING_ID, &11), 10 * 1000); + assert_eq!(asset::staked::(&11), 10 * 1000); assert_eq!(Staking::bonded(&11), Some(11)); assert!(>::contains_key(&11)); @@ -1964,7 +1972,7 @@ fn reap_stash_works_with_existential_deposit_zero() { assert!(!>::contains_key(&11)); assert!(!>::contains_key(&11)); // lock is removed. - assert_eq!(Balances::balance_locked(STAKING_ID, &11), 0); + assert_eq!(asset::staked::(&11), 0); }); } @@ -2111,8 +2119,8 @@ fn bond_with_little_staked_value_bounded() { // setup assert_ok!(Staking::chill(RuntimeOrigin::signed(31))); assert_ok!(Staking::set_payee(RuntimeOrigin::signed(11), RewardDestination::Stash)); - let init_balance_1 = Balances::free_balance(&1); - let init_balance_11 = Balances::free_balance(&11); + let init_balance_1 = asset::stakeable_balance::(&1); + let init_balance_11 = asset::stakeable_balance::(&11); // Stingy validator. assert_ok!(Staking::bond(RuntimeOrigin::signed(1), 1, RewardDestination::Account(1))); @@ -2137,12 +2145,12 @@ fn bond_with_little_staked_value_bounded() { // Old ones are rewarded. assert_eq_error_rate!( - Balances::free_balance(11), + asset::stakeable_balance::(&11), init_balance_11 + total_payout_0 / 3, 1 ); // no rewards paid to 2. This was initial election. - assert_eq!(Balances::free_balance(1), init_balance_1); + assert_eq!(asset::stakeable_balance::(&1), init_balance_1); // reward era 2 let total_payout_1 = current_total_payout_for_duration(reward_time_per_era()); @@ -2155,12 +2163,12 @@ fn bond_with_little_staked_value_bounded() { // 2 is now rewarded. assert_eq_error_rate!( - Balances::free_balance(1), + asset::stakeable_balance::(&1), init_balance_1 + total_payout_1 / 3, 1 ); assert_eq_error_rate!( - Balances::free_balance(&11), + asset::stakeable_balance::(&11), init_balance_11 + total_payout_0 / 3 + total_payout_1 / 3, 2, ); @@ -2188,7 +2196,7 @@ fn bond_with_duplicate_vote_should_be_ignored_by_election_provider() { // give the man some money. let initial_balance = 1000; for i in [1, 2, 3, 4].iter() { - let _ = Balances::make_free_balance_be(i, initial_balance); + let _ = asset::set_stakeable_balance::(&i, initial_balance); } assert_ok!(Staking::bond( @@ -2241,7 +2249,7 @@ fn bond_with_duplicate_vote_should_be_ignored_by_election_provider_elected() { // give the man some money. let initial_balance = 1000; for i in [1, 2, 3, 4].iter() { - let _ = Balances::make_free_balance_be(i, initial_balance); + let _ = asset::set_stakeable_balance::(&i, initial_balance); } assert_ok!(Staking::bond( @@ -2317,7 +2325,7 @@ fn reward_validator_slashing_validator_does_not_overflow() { assert!(stake.checked_mul(reward_slash).is_none()); // Set staker - let _ = Balances::make_free_balance_be(&11, stake); + let _ = asset::set_stakeable_balance::(&11, stake); let exposure = Exposure:: { total: stake, own: stake, others: vec![] }; let reward = EraRewardPoints:: { @@ -2330,11 +2338,11 @@ fn reward_validator_slashing_validator_does_not_overflow() { EraInfo::::set_exposure(0, &11, exposure); ErasValidatorReward::::insert(0, stake); assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 0, 0)); - assert_eq!(Balances::total_balance(&11), stake * 2); + assert_eq!(asset::total_balance::(&11), stake * 2); // Set staker - let _ = Balances::make_free_balance_be(&11, stake); - let _ = Balances::make_free_balance_be(&2, stake); + let _ = asset::set_stakeable_balance::(&11, stake); + let _ = asset::set_stakeable_balance::(&2, stake); // only slashes out of bonded stake are applied. without this line, it is 0. Staking::bond(RuntimeOrigin::signed(2), stake - 1, RewardDestination::Staked).unwrap(); @@ -2358,8 +2366,8 @@ fn reward_validator_slashing_validator_does_not_overflow() { &[Perbill::from_percent(100)], ); - assert_eq!(Balances::total_balance(&11), stake - 1); - assert_eq!(Balances::total_balance(&2), 1); + assert_eq!(asset::total_balance::(&11), stake - 1); + assert_eq!(asset::total_balance::(&2), 1); }) } @@ -2526,7 +2534,7 @@ fn slashing_performed_according_exposure() { ); // The stash account should be slashed for 250 (50% of 500). - assert_eq!(Balances::free_balance(11), 1000 - 250); + assert_eq!(asset::stakeable_balance::(&11), 1000 - 250); }); } @@ -2619,8 +2627,8 @@ fn reporters_receive_their_slice() { // 50% * (10% * initial_balance / 2) let reward = (initial_balance / 20) / 2; let reward_each = reward / 2; // split into two pieces. - assert_eq!(Balances::free_balance(1), 10 + reward_each); - assert_eq!(Balances::free_balance(2), 20 + reward_each); + assert_eq!(asset::stakeable_balance::(&1), 10 + reward_each); + assert_eq!(asset::stakeable_balance::(&2), 20 + reward_each); }); } @@ -2645,7 +2653,7 @@ fn subsequent_reports_in_same_span_pay_out_less() { // F1 * (reward_proportion * slash - 0) // 50% * (10% * initial_balance * 20%) let reward = (initial_balance / 5) / 20; - assert_eq!(Balances::free_balance(1), 10 + reward); + assert_eq!(asset::stakeable_balance::(&1), 10 + reward); on_offence_now( &[OffenceDetails { @@ -2660,7 +2668,7 @@ fn subsequent_reports_in_same_span_pay_out_less() { // F1 * (reward_proportion * slash - prior_payout) // 50% * (10% * (initial_balance / 2) - prior_payout) let reward = ((initial_balance / 20) - prior_payout) / 2; - assert_eq!(Balances::free_balance(1), 10 + prior_payout + reward); + assert_eq!(asset::stakeable_balance::(&1), 10 + prior_payout + reward); }); } @@ -2668,14 +2676,17 @@ fn subsequent_reports_in_same_span_pay_out_less() { fn invulnerables_are_not_slashed() { // For invulnerable validators no slashing is performed. ExtBuilder::default().invulnerables(vec![11]).build_and_execute(|| { - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(21), 2000); + assert_eq!(asset::stakeable_balance::(&11), 1000); + assert_eq!(asset::stakeable_balance::(&21), 2000); let exposure = Staking::eras_stakers(active_era(), &21); let initial_balance = Staking::slashable_balance_of(&21); - let nominator_balances: Vec<_> = - exposure.others.iter().map(|o| Balances::free_balance(&o.who)).collect(); + let nominator_balances: Vec<_> = exposure + .others + .iter() + .map(|o| asset::stakeable_balance::(&o.who)) + .collect(); on_offence_now( &[ @@ -2692,14 +2703,14 @@ fn invulnerables_are_not_slashed() { ); // The validator 11 hasn't been slashed, but 21 has been. - assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(asset::stakeable_balance::(&11), 1000); // 2000 - (0.2 * initial_balance) - assert_eq!(Balances::free_balance(21), 2000 - (2 * initial_balance / 10)); + assert_eq!(asset::stakeable_balance::(&21), 2000 - (2 * initial_balance / 10)); // ensure that nominators were slashed as well. for (initial_balance, other) in nominator_balances.into_iter().zip(exposure.others) { assert_eq!( - Balances::free_balance(&other.who), + asset::stakeable_balance::(&other.who), initial_balance - (2 * other.value / 10), ); } @@ -2710,7 +2721,7 @@ fn invulnerables_are_not_slashed() { fn dont_slash_if_fraction_is_zero() { // Don't slash if the fraction is zero. ExtBuilder::default().build_and_execute(|| { - assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(asset::stakeable_balance::(&11), 1000); on_offence_now( &[OffenceDetails { @@ -2721,7 +2732,7 @@ fn dont_slash_if_fraction_is_zero() { ); // The validator hasn't been slashed. The new era is not forced. - assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(asset::stakeable_balance::(&11), 1000); assert_eq!(Staking::force_era(), Forcing::NotForcing); }); } @@ -2731,7 +2742,7 @@ fn only_slash_for_max_in_era() { // multiple slashes within one era are only applied if it is more than any previous slash in the // same era. ExtBuilder::default().build_and_execute(|| { - assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(asset::stakeable_balance::(&11), 1000); on_offence_now( &[OffenceDetails { @@ -2742,7 +2753,7 @@ fn only_slash_for_max_in_era() { ); // The validator has been slashed and has been force-chilled. - assert_eq!(Balances::free_balance(11), 500); + assert_eq!(asset::stakeable_balance::(&11), 500); assert_eq!(Staking::force_era(), Forcing::NotForcing); on_offence_now( @@ -2754,7 +2765,7 @@ fn only_slash_for_max_in_era() { ); // The validator has not been slashed additionally. - assert_eq!(Balances::free_balance(11), 500); + assert_eq!(asset::stakeable_balance::(&11), 500); on_offence_now( &[OffenceDetails { @@ -2765,7 +2776,7 @@ fn only_slash_for_max_in_era() { ); // The validator got slashed 10% more. - assert_eq!(Balances::free_balance(11), 400); + assert_eq!(asset::stakeable_balance::(&11), 400); }) } @@ -2776,7 +2787,7 @@ fn garbage_collection_after_slashing() { .existential_deposit(2) .balance_factor(2) .build_and_execute(|| { - assert_eq!(Balances::free_balance(11), 2000); + assert_eq!(asset::stakeable_balance::(&11), 2000); on_offence_now( &[OffenceDetails { @@ -2786,7 +2797,7 @@ fn garbage_collection_after_slashing() { &[Perbill::from_percent(10)], ); - assert_eq!(Balances::free_balance(11), 2000 - 200); + assert_eq!(asset::stakeable_balance::(&11), 2000 - 200); assert!(SlashingSpans::::get(&11).is_some()); assert_eq!(SpanSlash::::get(&(11, 0)).amount(), &200); @@ -2801,8 +2812,8 @@ fn garbage_collection_after_slashing() { // validator and nominator slash in era are garbage-collected by era change, // so we don't test those here. - assert_eq!(Balances::free_balance(11), 2); - assert_eq!(Balances::total_balance(&11), 2); + assert_eq!(asset::stakeable_balance::(&11), 2); + assert_eq!(asset::total_balance::(&11), 2); let slashing_spans = SlashingSpans::::get(&11).unwrap(); assert_eq!(slashing_spans.iter().count(), 2); @@ -2826,11 +2837,11 @@ fn garbage_collection_on_window_pruning() { ExtBuilder::default().build_and_execute(|| { mock::start_active_era(1); - assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(asset::stakeable_balance::(&11), 1000); let now = active_era(); let exposure = Staking::eras_stakers(now, &11); - assert_eq!(Balances::free_balance(101), 2000); + assert_eq!(asset::stakeable_balance::(&101), 2000); let nominated_value = exposure.others.iter().find(|o| o.who == 101).unwrap().value; on_offence_now( @@ -2841,8 +2852,8 @@ fn garbage_collection_on_window_pruning() { &[Perbill::from_percent(10)], ); - assert_eq!(Balances::free_balance(11), 900); - assert_eq!(Balances::free_balance(101), 2000 - (nominated_value / 10)); + assert_eq!(asset::stakeable_balance::(&11), 900); + assert_eq!(asset::stakeable_balance::(&101), 2000 - (nominated_value / 10)); assert!(ValidatorSlashInEra::::get(&now, &11).is_some()); assert!(NominatorSlashInEra::::get(&now, &101).is_some()); @@ -2867,9 +2878,9 @@ fn slashing_nominators_by_span_max() { mock::start_active_era(2); mock::start_active_era(3); - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(21), 2000); - assert_eq!(Balances::free_balance(101), 2000); + assert_eq!(asset::stakeable_balance::(&11), 1000); + assert_eq!(asset::stakeable_balance::(&21), 2000); + assert_eq!(asset::stakeable_balance::(&101), 2000); assert_eq!(Staking::slashable_balance_of(&21), 1000); let exposure_11 = Staking::eras_stakers(active_era(), &11); @@ -2886,10 +2897,10 @@ fn slashing_nominators_by_span_max() { 2, ); - assert_eq!(Balances::free_balance(11), 900); + assert_eq!(asset::stakeable_balance::(&11), 900); let slash_1_amount = Perbill::from_percent(10) * nominated_value_11; - assert_eq!(Balances::free_balance(101), 2000 - slash_1_amount); + assert_eq!(asset::stakeable_balance::(&101), 2000 - slash_1_amount); let expected_spans = vec![ slashing::SlashingSpan { index: 1, start: 4, length: None }, @@ -2913,14 +2924,14 @@ fn slashing_nominators_by_span_max() { ); // 11 was not further slashed, but 21 and 101 were. - assert_eq!(Balances::free_balance(11), 900); - assert_eq!(Balances::free_balance(21), 1700); + assert_eq!(asset::stakeable_balance::(&11), 900); + assert_eq!(asset::stakeable_balance::(&21), 1700); let slash_2_amount = Perbill::from_percent(30) * nominated_value_21; assert!(slash_2_amount > slash_1_amount); // only the maximum slash in a single span is taken. - assert_eq!(Balances::free_balance(101), 2000 - slash_2_amount); + assert_eq!(asset::stakeable_balance::(&101), 2000 - slash_2_amount); // third slash: in same era and on same validator as first, higher // in-era value, but lower slash value than slash 2. @@ -2934,15 +2945,15 @@ fn slashing_nominators_by_span_max() { ); // 11 was further slashed, but 21 and 101 were not. - assert_eq!(Balances::free_balance(11), 800); - assert_eq!(Balances::free_balance(21), 1700); + assert_eq!(asset::stakeable_balance::(&11), 800); + assert_eq!(asset::stakeable_balance::(&21), 1700); let slash_3_amount = Perbill::from_percent(20) * nominated_value_21; assert!(slash_3_amount < slash_2_amount); assert!(slash_3_amount > slash_1_amount); // only the maximum slash in a single span is taken. - assert_eq!(Balances::free_balance(101), 2000 - slash_2_amount); + assert_eq!(asset::stakeable_balance::(&101), 2000 - slash_2_amount); }); } @@ -2953,7 +2964,7 @@ fn slashes_are_summed_across_spans() { mock::start_active_era(2); mock::start_active_era(3); - assert_eq!(Balances::free_balance(21), 2000); + assert_eq!(asset::stakeable_balance::(&21), 2000); assert_eq!(Staking::slashable_balance_of(&21), 1000); let get_span = |account| SlashingSpans::::get(&account).unwrap(); @@ -2972,7 +2983,7 @@ fn slashes_are_summed_across_spans() { ]; assert_eq!(get_span(21).iter().collect::>(), expected_spans); - assert_eq!(Balances::free_balance(21), 1900); + assert_eq!(asset::stakeable_balance::(&21), 1900); // 21 has been force-chilled. re-signal intent to validate. Staking::validate(RuntimeOrigin::signed(21), Default::default()).unwrap(); @@ -2996,7 +3007,7 @@ fn slashes_are_summed_across_spans() { ]; assert_eq!(get_span(21).iter().collect::>(), expected_spans); - assert_eq!(Balances::free_balance(21), 1810); + assert_eq!(asset::stakeable_balance::(&21), 1810); }); } @@ -3005,10 +3016,10 @@ fn deferred_slashes_are_deferred() { ExtBuilder::default().slash_defer_duration(2).build_and_execute(|| { mock::start_active_era(1); - assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(asset::stakeable_balance::(&11), 1000); let exposure = Staking::eras_stakers(active_era(), &11); - assert_eq!(Balances::free_balance(101), 2000); + assert_eq!(asset::stakeable_balance::(&101), 2000); let nominated_value = exposure.others.iter().find(|o| o.who == 101).unwrap().value; System::reset_events(); @@ -3024,25 +3035,25 @@ fn deferred_slashes_are_deferred() { // nominations are not removed regardless of the deferring. assert_eq!(Staking::nominators(101).unwrap().targets, vec![11, 21]); - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(101), 2000); + assert_eq!(asset::stakeable_balance::(&11), 1000); + assert_eq!(asset::stakeable_balance::(&101), 2000); mock::start_active_era(2); - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(101), 2000); + assert_eq!(asset::stakeable_balance::(&11), 1000); + assert_eq!(asset::stakeable_balance::(&101), 2000); mock::start_active_era(3); - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(101), 2000); + assert_eq!(asset::stakeable_balance::(&11), 1000); + assert_eq!(asset::stakeable_balance::(&101), 2000); // at the start of era 4, slashes from era 1 are processed, // after being deferred for at least 2 full eras. mock::start_active_era(4); - assert_eq!(Balances::free_balance(11), 900); - assert_eq!(Balances::free_balance(101), 2000 - (nominated_value / 10)); + assert_eq!(asset::stakeable_balance::(&11), 900); + assert_eq!(asset::stakeable_balance::(&101), 2000 - (nominated_value / 10)); assert!(matches!( staking_events_since_last_call().as_slice(), @@ -3140,8 +3151,8 @@ fn staker_cannot_bail_deferred_slash() { ExtBuilder::default().slash_defer_duration(2).build_and_execute(|| { mock::start_active_era(1); - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(101), 2000); + assert_eq!(asset::stakeable_balance::(&11), 1000); + assert_eq!(asset::stakeable_balance::(&101), 2000); let exposure = Staking::eras_stakers(active_era(), &11); let nominated_value = exposure.others.iter().find(|o| o.who == 101).unwrap().value; @@ -3173,20 +3184,20 @@ fn staker_cannot_bail_deferred_slash() { ); // no slash yet. - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(101), 2000); + assert_eq!(asset::stakeable_balance::(&11), 1000); + assert_eq!(asset::stakeable_balance::(&101), 2000); // no slash yet. mock::start_active_era(2); - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(101), 2000); + assert_eq!(asset::stakeable_balance::(&11), 1000); + assert_eq!(asset::stakeable_balance::(&101), 2000); assert_eq!(Staking::current_era().unwrap(), 2); assert_eq!(active_era(), 2); // no slash yet. mock::start_active_era(3); - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(101), 2000); + assert_eq!(asset::stakeable_balance::(&11), 1000); + assert_eq!(asset::stakeable_balance::(&101), 2000); assert_eq!(Staking::current_era().unwrap(), 3); assert_eq!(active_era(), 3); @@ -3203,8 +3214,8 @@ fn staker_cannot_bail_deferred_slash() { // after being deferred for at least 2 full eras. mock::start_active_era(4); - assert_eq!(Balances::free_balance(11), 900); - assert_eq!(Balances::free_balance(101), 2000 - (nominated_value / 10)); + assert_eq!(asset::stakeable_balance::(&11), 900); + assert_eq!(asset::stakeable_balance::(&101), 2000 - (nominated_value / 10)); // and the leftover of the funds can now be unbonded. }) @@ -3215,10 +3226,10 @@ fn remove_deferred() { ExtBuilder::default().slash_defer_duration(2).build_and_execute(|| { mock::start_active_era(1); - assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(asset::stakeable_balance::(&11), 1000); let exposure = Staking::eras_stakers(active_era(), &11); - assert_eq!(Balances::free_balance(101), 2000); + assert_eq!(asset::stakeable_balance::(&101), 2000); let nominated_value = exposure.others.iter().find(|o| o.who == 101).unwrap().value; // deferred to start of era 4. @@ -3227,8 +3238,8 @@ fn remove_deferred() { &[Perbill::from_percent(10)], ); - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(101), 2000); + assert_eq!(asset::stakeable_balance::(&11), 1000); + assert_eq!(asset::stakeable_balance::(&101), 2000); mock::start_active_era(2); @@ -3249,13 +3260,13 @@ fn remove_deferred() { // cancel one of them. assert_ok!(Staking::cancel_deferred_slash(RuntimeOrigin::root(), 4, vec![0])); - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(101), 2000); + assert_eq!(asset::stakeable_balance::(&11), 1000); + assert_eq!(asset::stakeable_balance::(&101), 2000); mock::start_active_era(3); - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(101), 2000); + assert_eq!(asset::stakeable_balance::(&11), 1000); + assert_eq!(asset::stakeable_balance::(&101), 2000); // at the start of era 4, slashes from era 1 are processed, // after being deferred for at least 2 full eras. @@ -3280,8 +3291,8 @@ fn remove_deferred() { let actual_slash = total_slash - initial_slash; // 5% slash (15 - 10) processed now. - assert_eq!(Balances::free_balance(11), 950); - assert_eq!(Balances::free_balance(101), 2000 - actual_slash); + assert_eq!(asset::stakeable_balance::(&11), 950); + assert_eq!(asset::stakeable_balance::(&101), 2000 - actual_slash); }) } @@ -3290,10 +3301,10 @@ fn remove_multi_deferred() { ExtBuilder::default().slash_defer_duration(2).build_and_execute(|| { mock::start_active_era(1); - assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(asset::stakeable_balance::(&11), 1000); let exposure = Staking::eras_stakers(active_era(), &11); - assert_eq!(Balances::free_balance(101), 2000); + assert_eq!(asset::stakeable_balance::(&101), 2000); on_offence_now( &[OffenceDetails { offender: (11, exposure.clone()), reporters: vec![] }], @@ -3363,8 +3374,8 @@ fn slash_kicks_validators_not_nominators_and_disables_nominator_for_kicked_valid assert_eq_uvec!(Session::validators(), vec![11, 21, 31, 41, 51, 201, 202]); // pre-slash balance - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(101), 2000); + assert_eq!(asset::stakeable_balance::(&11), 1000); + assert_eq!(asset::stakeable_balance::(&101), 2000); // 100 has approval for 11 as of now assert!(Staking::nominators(101).unwrap().targets.contains(&11)); @@ -3398,8 +3409,8 @@ fn slash_kicks_validators_not_nominators_and_disables_nominator_for_kicked_valid // post-slash balance let nominator_slash_amount_11 = 125 / 10; - assert_eq!(Balances::free_balance(11), 900); - assert_eq!(Balances::free_balance(101), 2000 - nominator_slash_amount_11); + assert_eq!(asset::stakeable_balance::(&11), 900); + assert_eq!(asset::stakeable_balance::(&101), 2000 - nominator_slash_amount_11); // check that validator was disabled. assert!(is_disabled(11)); @@ -3663,8 +3674,8 @@ fn claim_reward_at_the_last_era_and_no_double_claim_and_invalid_claim() { // Consumed weight for all payout_stakers dispatches that fail let err_weight = ::WeightInfo::payout_stakers_alive_staked(0); - let init_balance_11 = Balances::total_balance(&11); - let init_balance_101 = Balances::total_balance(&101); + let init_balance_11 = asset::total_balance::(&11); + let init_balance_101 = asset::total_balance::(&101); let part_for_11 = Perbill::from_rational::(1000, 1125); let part_for_101 = Perbill::from_rational::(125, 1125); @@ -3728,11 +3739,11 @@ fn claim_reward_at_the_last_era_and_no_double_claim_and_invalid_claim() { // only era 1 and 2 can be rewarded. assert_eq!( - Balances::total_balance(&11), + asset::total_balance::(&11), init_balance_11 + part_for_11 * (total_payout_1 + total_payout_2), ); assert_eq!( - Balances::total_balance(&101), + asset::total_balance::(&101), init_balance_101 + part_for_101 * (total_payout_1 + total_payout_2), ); }); @@ -3749,18 +3760,18 @@ fn zero_slash_keeps_nominators() { .build_and_execute(|| { mock::start_active_era(1); - assert_eq!(Balances::free_balance(11), 1000); + assert_eq!(asset::stakeable_balance::(&11), 1000); let exposure = Staking::eras_stakers(active_era(), &11); - assert_eq!(Balances::free_balance(101), 2000); + assert_eq!(asset::stakeable_balance::(&101), 2000); on_offence_now( &[OffenceDetails { offender: (11, exposure.clone()), reporters: vec![] }], &[Perbill::from_percent(0)], ); - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(101), 2000); + assert_eq!(asset::stakeable_balance::(&11), 1000); + assert_eq!(asset::stakeable_balance::(&101), 2000); // 11 is not removed but disabled assert!(Validators::::iter().any(|(stash, _)| stash == 11)); @@ -3837,7 +3848,7 @@ fn test_nominators_over_max_exposure_page_size_are_rewarded() { for i in 0..=MaxExposurePageSize::get() { let stash = 10_000 + i as AccountId; let balance = 10_000 + i as Balance; - Balances::make_free_balance_be(&stash, balance); + asset::set_stakeable_balance::(&stash, balance); assert_ok!(Staking::bond( RuntimeOrigin::signed(stash), balance, @@ -3859,13 +3870,13 @@ fn test_nominators_over_max_exposure_page_size_are_rewarded() { while i < MaxExposurePageSize::get() { let stash = 10_000 + i as AccountId; let balance = 10_000 + i as Balance; - assert!(Balances::free_balance(&stash) > balance); + assert!(asset::stakeable_balance::(&stash) > balance); i += 1; } // Assert overflowing nominators from page 1 are also rewarded let stash = 10_000 + i as AccountId; - assert!(Balances::free_balance(&stash) > (10_000 + i) as Balance); + assert!(asset::stakeable_balance::(&stash) > (10_000 + i) as Balance); }); } @@ -3878,7 +3889,7 @@ fn test_nominators_are_rewarded_for_all_exposure_page() { for i in 0..nominator_count { let stash = 10_000 + i as AccountId; let balance = 10_000 + i as Balance; - Balances::make_free_balance_be(&stash, balance); + asset::set_stakeable_balance::(&stash, balance); assert_ok!(Staking::bond( RuntimeOrigin::signed(stash), balance, @@ -3900,9 +3911,10 @@ fn test_nominators_are_rewarded_for_all_exposure_page() { // Assert all nominators are rewarded according to their stake for i in 0..nominator_count { // balance of the nominator after the reward payout. - let current_balance = Balances::free_balance(&((10000 + i) as AccountId)); + let current_balance = asset::stakeable_balance::(&((10000 + i) as AccountId)); // balance of the nominator in the previous iteration. - let previous_balance = Balances::free_balance(&((10000 + i - 1) as AccountId)); + let previous_balance = + asset::stakeable_balance::(&((10000 + i - 1) as AccountId)); // balance before the reward. let original_balance = 10_000 + i as Balance; @@ -3958,7 +3970,7 @@ fn test_multi_page_payout_stakers_by_page() { RewardOnUnbalanceWasCalled::set(false); System::reset_events(); - let controller_balance_before_p0_payout = Balances::free_balance(&11); + let controller_balance_before_p0_payout = asset::stakeable_balance::(&11); // Payout rewards for first exposure page assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 1, 0)); @@ -3972,7 +3984,7 @@ fn test_multi_page_payout_stakers_by_page() { ] )); - let controller_balance_after_p0_payout = Balances::free_balance(&11); + let controller_balance_after_p0_payout = asset::stakeable_balance::(&11); // verify rewards have been paid out but still some left assert!(pallet_balances::TotalIssuance::::get() > pre_payout_total_issuance); @@ -3996,7 +4008,7 @@ fn test_multi_page_payout_stakers_by_page() { ] )); // verify the validator was not rewarded the second time - assert_eq!(Balances::free_balance(&11), controller_balance_after_p0_payout); + assert_eq!(asset::stakeable_balance::(&11), controller_balance_after_p0_payout); // verify all rewards have been paid out assert_eq_error_rate!( @@ -4007,9 +4019,9 @@ fn test_multi_page_payout_stakers_by_page() { assert!(RewardOnUnbalanceWasCalled::get()); // Top 64 nominators of validator 11 automatically paid out, including the validator - assert!(Balances::free_balance(&11) > balance); + assert!(asset::stakeable_balance::(&11) > balance); for i in 0..100 { - assert!(Balances::free_balance(&(1000 + i)) > balance + i as Balance); + assert!(asset::stakeable_balance::(&(1000 + i)) > balance + i as Balance); } // verify we no longer track rewards in `legacy_claimed_rewards` vec @@ -4178,7 +4190,7 @@ fn test_multi_page_payout_stakers_backward_compatible() { let pre_payout_total_issuance = pallet_balances::TotalIssuance::::get(); RewardOnUnbalanceWasCalled::set(false); - let controller_balance_before_p0_payout = Balances::free_balance(&11); + let controller_balance_before_p0_payout = asset::stakeable_balance::(&11); // Payout rewards for first exposure page assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 1)); // page 0 is claimed @@ -4187,7 +4199,7 @@ fn test_multi_page_payout_stakers_backward_compatible() { Error::::AlreadyClaimed.with_weight(err_weight) ); - let controller_balance_after_p0_payout = Balances::free_balance(&11); + let controller_balance_after_p0_payout = asset::stakeable_balance::(&11); // verify rewards have been paid out but still some left assert!(pallet_balances::TotalIssuance::::get() > pre_payout_total_issuance); @@ -4206,7 +4218,7 @@ fn test_multi_page_payout_stakers_backward_compatible() { ); // verify the validator was not rewarded the second time - assert_eq!(Balances::free_balance(&11), controller_balance_after_p0_payout); + assert_eq!(asset::stakeable_balance::(&11), controller_balance_after_p0_payout); // verify all rewards have been paid out assert_eq_error_rate!( @@ -4218,9 +4230,9 @@ fn test_multi_page_payout_stakers_backward_compatible() { // verify all nominators of validator 11 are paid out, including the validator // Validator payout goes to controller. - assert!(Balances::free_balance(&11) > balance); + assert!(asset::stakeable_balance::(&11) > balance); for i in 0..100 { - assert!(Balances::free_balance(&(1000 + i)) > balance + i as Balance); + assert!(asset::stakeable_balance::(&(1000 + i)) > balance + i as Balance); } // verify we no longer track rewards in `legacy_claimed_rewards` vec @@ -4578,25 +4590,29 @@ fn test_commission_paid_across_pages() { let payout = current_total_payout_for_duration(reward_time_per_era()); mock::start_active_era(2); - let initial_balance = Balances::free_balance(&11); + let initial_balance = asset::stakeable_balance::(&11); // Payout rewards for first exposure page assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 1, 0)); - let controller_balance_after_p0_payout = Balances::free_balance(&11); + let controller_balance_after_p0_payout = asset::stakeable_balance::(&11); // some commission is paid assert!(initial_balance < controller_balance_after_p0_payout); // payout all pages for i in 1..4 { - let before_balance = Balances::free_balance(&11); + let before_balance = asset::stakeable_balance::(&11); assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 1, i)); - let after_balance = Balances::free_balance(&11); + let after_balance = asset::stakeable_balance::(&11); // some commission is paid for every page assert!(before_balance < after_balance); } - assert_eq_error_rate!(Balances::free_balance(&11), initial_balance + payout / 2, 1,); + assert_eq_error_rate!( + asset::stakeable_balance::(&11), + initial_balance + payout / 2, + 1, + ); }); } @@ -4852,7 +4868,7 @@ fn payout_to_any_account_works() { assert_ok!(Staking::set_payee(RuntimeOrigin::signed(1234), RewardDestination::Account(42))); // Reward Destination account doesn't exist - assert_eq!(Balances::free_balance(42), 0); + assert_eq!(asset::stakeable_balance::(&42), 0); mock::start_active_era(1); Staking::reward_by_ids(vec![(11, 1)]); @@ -4862,7 +4878,7 @@ fn payout_to_any_account_works() { assert_ok!(Staking::payout_stakers_by_page(RuntimeOrigin::signed(1337), 11, 1, 0)); // Payment is successful - assert!(Balances::free_balance(42) > 0); + assert!(asset::stakeable_balance::(&42) > 0); }) } @@ -5661,9 +5677,9 @@ fn chill_other_works() { let a = 4 * i; let b = 4 * i + 2; let c = 4 * i + 3; - Balances::make_free_balance_be(&a, 100_000); - Balances::make_free_balance_be(&b, 100_000); - Balances::make_free_balance_be(&c, 100_000); + asset::set_stakeable_balance::(&a, 100_000); + asset::set_stakeable_balance::(&b, 100_000); + asset::set_stakeable_balance::(&c, 100_000); // Nominator assert_ok!(Staking::bond(RuntimeOrigin::signed(a), 1000, RewardDestination::Stash)); @@ -6859,7 +6875,7 @@ fn test_runtime_api_pending_rewards() { // Set staker for v in validator_one..=validator_three { - let _ = Balances::make_free_balance_be(&v, stake); + let _ = asset::set_stakeable_balance::(&v, stake); assert_ok!(Staking::bond(RuntimeOrigin::signed(v), stake, RewardDestination::Staked)); } @@ -7049,7 +7065,7 @@ mod staking_interface { assert!(!>::contains_key(&11)); assert!(!>::contains_key(&11)); // lock is removed. - assert_eq!(Balances::balance_locked(STAKING_ID, &11), 0); + assert_eq!(asset::staked::(&11), 0); }); } @@ -7086,12 +7102,12 @@ mod staking_unchecked { fn virtual_bond_does_not_lock() { ExtBuilder::default().build_and_execute(|| { mock::start_active_era(1); - assert_eq!(Balances::free_balance(10), 1); + assert_eq!(asset::stakeable_balance::(&10), 1); // 10 can bond more than its balance amount since we do not require lock for virtual // bonding. assert_ok!(::virtual_bond(&10, 100, &15)); // nothing is locked on 10. - assert_eq!(Balances::balance_locked(STAKING_ID, &10), 0); + assert_eq!(asset::staked::(&10), 0); // adding more balance does not lock anything as well. assert_ok!(::bond_extra(&10, 1000)); // but ledger is updated correctly. @@ -7118,7 +7134,7 @@ mod staking_unchecked { Ok(Stake { total: 1100, active: 900 }) ); // still no locks. - assert_eq!(Balances::balance_locked(STAKING_ID, &10), 0); + assert_eq!(asset::staked::(&10), 0); mock::start_active_era(2); // cannot withdraw without waiting for unbonding period. @@ -7218,11 +7234,11 @@ mod staking_unchecked { fn migrate_virtual_staker() { ExtBuilder::default().build_and_execute(|| { // give some balance to 200 - Balances::make_free_balance_be(&200, 2000); + asset::set_stakeable_balance::(&200, 2000); // stake assert_ok!(Staking::bond(RuntimeOrigin::signed(200), 1000, RewardDestination::Staked)); - assert_eq!(Balances::balance_locked(crate::STAKING_ID, &200), 1000); + assert_eq!(asset::staked::(&200), 1000); // migrate them to virtual staker ::migrate_to_virtual_staker(&200); @@ -7230,7 +7246,7 @@ mod staking_unchecked { assert_ok!(::set_payee(&200, &201)); // ensure the balance is not locked anymore - assert_eq!(Balances::balance_locked(crate::STAKING_ID, &200), 0); + assert_eq!(asset::staked::(&200), 0); // and they are marked as virtual stakers assert_eq!(Pallet::::is_virtual_staker(&200), true); @@ -7300,7 +7316,7 @@ mod staking_unchecked { assert!(is_disabled(11)); // but virtual nominator's balance is not slashed. - assert_eq!(Balances::free_balance(&101), nominator_balance); + assert_eq!(asset::stakeable_balance::(&101), nominator_balance); // but slash is broadcasted to slash observers. assert_eq!(SlashObserver::get().get(&101).unwrap(), &nominator_share); }) @@ -7332,9 +7348,9 @@ mod staking_unchecked { assert_ok!(::set_payee(&101, &102)); // cache values - let validator_balance = Balances::free_balance(&11); + let validator_balance = asset::stakeable_balance::(&11); let validator_stake = Staking::ledger(11.into()).unwrap().total; - let nominator_balance = Balances::free_balance(&101); + let nominator_balance = asset::stakeable_balance::(&101); let nominator_stake = Staking::ledger(101.into()).unwrap().total; // 11 goes offline @@ -7353,14 +7369,14 @@ mod staking_unchecked { // all validator stake is slashed assert_eq_error_rate!( validator_balance - validator_stake, - Balances::free_balance(&11), + asset::stakeable_balance::(&11), 1 ); // Because slashing happened. assert!(is_disabled(11)); // Virtual nominator's balance is not slashed. - assert_eq!(Balances::free_balance(&101), nominator_balance); + assert_eq!(asset::stakeable_balance::(&101), nominator_balance); // Slash is broadcasted to slash observers. assert_eq!(SlashObserver::get().get(&101).unwrap(), &nominator_stake); @@ -7900,7 +7916,7 @@ mod ledger_recovery { ExtBuilder::default().has_stakers(true).try_state(false).build_and_execute(|| { setup_double_bonded_ledgers(); - let lock_333_before = Balances::balance_locked(crate::STAKING_ID, &333); + let lock_333_before = asset::staked::(&333); // get into corrupted and killed ledger state by killing a corrupted ledger: // init state: @@ -7936,14 +7952,14 @@ mod ledger_recovery { // side effects on 333 - ledger, bonded, payee, lock should be completely empty. // however, 333 lock remains. - assert_eq!(Balances::balance_locked(crate::STAKING_ID, &333), lock_333_before); // NOK + assert_eq!(asset::staked::(&333), lock_333_before); // NOK assert!(Bonded::::get(&333).is_none()); // OK assert!(Payee::::get(&333).is_none()); // OK assert!(Ledger::::get(&444).is_none()); // OK // side effects on 444 - ledger, bonded, payee, lock should remain be intact. // however, 444 lock was removed. - assert_eq!(Balances::balance_locked(crate::STAKING_ID, &444), 0); // NOK + assert_eq!(asset::staked::(&444), 0); // NOK assert!(Bonded::::get(&444).is_some()); // OK assert!(Payee::::get(&444).is_some()); // OK assert!(Ledger::::get(&555).is_none()); // NOK @@ -7957,7 +7973,7 @@ mod ledger_recovery { ExtBuilder::default().has_stakers(true).try_state(false).build_and_execute(|| { setup_double_bonded_ledgers(); - let lock_333_before = Balances::balance_locked(crate::STAKING_ID, &333); + let lock_333_before = asset::staked::(&333); // get into corrupted and killed ledger state by killing a corrupted ledger: // init state: @@ -7992,14 +8008,14 @@ mod ledger_recovery { assert_eq!(Staking::inspect_bond_state(&444), Err(Error::::NotStash)); // side effects on 333 - ledger, bonded, payee, lock should be intact. - assert_eq!(Balances::balance_locked(crate::STAKING_ID, &333), lock_333_before); // OK + assert_eq!(asset::staked::(&333), lock_333_before); // OK assert_eq!(Bonded::::get(&333), Some(444)); // OK assert!(Payee::::get(&333).is_some()); // OK - // however, ledger associated with its controller was killed. + // however, ledger associated with its controller was killed. assert!(Ledger::::get(&444).is_none()); // NOK // side effects on 444 - ledger, bonded, payee, lock should be completely removed. - assert_eq!(Balances::balance_locked(crate::STAKING_ID, &444), 0); // OK + assert_eq!(asset::staked::(&444), 0); // OK assert!(Bonded::::get(&444).is_none()); // OK assert!(Payee::::get(&444).is_none()); // OK assert!(Ledger::::get(&555).is_none()); // OK @@ -8080,7 +8096,7 @@ mod ledger_recovery { setup_double_bonded_ledgers(); // ledger.total == lock - let total_444_before_corruption = Balances::balance_locked(crate::STAKING_ID, &444); + let total_444_before_corruption = asset::staked::(&444); // get into corrupted and killed ledger state by killing a corrupted ledger: // init state: @@ -8182,8 +8198,8 @@ mod ledger_recovery { ExtBuilder::default().has_stakers(true).build_and_execute(|| { setup_double_bonded_ledgers(); - let lock_333_before = Balances::balance_locked(crate::STAKING_ID, &333); - let lock_444_before = Balances::balance_locked(crate::STAKING_ID, &444); + let lock_333_before = asset::staked::(&333); + let lock_444_before = asset::staked::(&444); // get into corrupted and killed ledger state by killing a corrupted ledger: // init state: @@ -8203,16 +8219,13 @@ mod ledger_recovery { // if 444 bonds extra, the locks remain in sync. bond_extra_no_checks(&444, 40); - assert_eq!(Balances::balance_locked(crate::STAKING_ID, &333), lock_333_before); - assert_eq!(Balances::balance_locked(crate::STAKING_ID, &444), lock_444_before + 40); + assert_eq!(asset::staked::(&333), lock_333_before); + assert_eq!(asset::staked::(&444), lock_444_before + 40); // however if 333 bonds extra, the wrong lock is updated. bond_extra_no_checks(&333, 30); - assert_eq!( - Balances::balance_locked(crate::STAKING_ID, &333), - lock_444_before + 40 + 30 - ); //not OK - assert_eq!(Balances::balance_locked(crate::STAKING_ID, &444), lock_444_before + 40); // OK + assert_eq!(asset::staked::(&333), lock_444_before + 40 + 30); //not OK + assert_eq!(asset::staked::(&444), lock_444_before + 40); // OK // recover the ledger bonded by 333 stash. Note that the total/lock needs to be // re-written since on-chain data lock has become out of sync. @@ -8247,9 +8260,9 @@ mod ledger_recovery { let ledger_444 = Bonded::::get(&444).and_then(Ledger::::get).unwrap(); assert_eq!(ledger_333.total, lock_333_before + 30); - assert_eq!(Balances::balance_locked(crate::STAKING_ID, &333), ledger_333.total); + assert_eq!(asset::staked::(&333), ledger_333.total); assert_eq!(ledger_444.total, lock_444_before + 40); - assert_eq!(Balances::balance_locked(crate::STAKING_ID, &444), ledger_444.total); + assert_eq!(asset::staked::(&444), ledger_444.total); // try-state checks are ok now. assert_ok!(Staking::do_try_state(System::block_number())); diff --git a/substrate/frame/support/Cargo.toml b/substrate/frame/support/Cargo.toml index 549059e261cc3a4ad2865f63789e69691e908405..9e9741ee1619a4184dc7e21d27594165613b2db2 100644 --- a/substrate/frame/support/Cargo.toml +++ b/substrate/frame/support/Cargo.toml @@ -17,6 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] array-bytes = { workspace = true } +binary-merkle-tree.workspace = true serde = { features = ["alloc", "derive"], workspace = true } codec = { features = [ "derive", @@ -44,6 +45,7 @@ sp-staking = { workspace = true } sp-weights = { workspace = true } sp-debug-derive = { workspace = true } sp-metadata-ir = { workspace = true } +sp-trie = { workspace = true } tt-call = { workspace = true } macro_magic = { workspace = true } frame-support-procedural = { workspace = true } @@ -73,6 +75,7 @@ sp-crypto-hashing = { workspace = true, default-features = true } [features] default = ["std"] std = [ + "binary-merkle-tree/std", "codec/std", "environmental/std", "frame-metadata/std", @@ -97,6 +100,7 @@ std = [ "sp-std/std", "sp-timestamp/std", "sp-tracing/std", + "sp-trie/std", "sp-weights/std", ] runtime-benchmarks = [ diff --git a/substrate/frame/support/procedural/examples/proc_main/inject_runtime_type.rs b/substrate/frame/support/procedural/examples/proc_main/inject_runtime_type.rs index cd5660f2bda767e713702d438085e83d077cd386..d0725ba2049fc35655d3d54b5b40e89624225bcf 100644 --- a/substrate/frame/support/procedural/examples/proc_main/inject_runtime_type.rs +++ b/substrate/frame/support/procedural/examples/proc_main/inject_runtime_type.rs @@ -43,6 +43,7 @@ fn derive_impl_works_with_no_aggregated_types() { type Block = super::Block; type AccountId = super::AccountId; type PalletInfo = super::PalletInfo; + type ExampleConstant = (); } assert_type_eq_all!(::RuntimeOrigin, ()); diff --git a/substrate/frame/support/procedural/examples/proc_main/runtime.rs b/substrate/frame/support/procedural/examples/proc_main/runtime.rs index a9d9281f50da36e988482feac010ab778864d7dc..109ca4f6dc488228cc596c2d3ff2447aa4cacff4 100644 --- a/substrate/frame/support/procedural/examples/proc_main/runtime.rs +++ b/substrate/frame/support/procedural/examples/proc_main/runtime.rs @@ -14,6 +14,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +#![allow(deprecated, clippy::deprecated_semver)] use super::{frame_system, Block}; use crate::derive_impl; @@ -121,6 +122,7 @@ mod runtime { // Ensure that the runtime does not export the calls from the pallet #[runtime::pallet_index(4)] #[runtime::disable_call] + #[deprecated = "example"] pub type PalletWithDisabledCall = pallet_with_disabled_call::Pallet; // Ensure that the runtime does not export the unsigned calls from the pallet 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 daef1b171617fbbd87ea13fc73989bd4b01738a2..f3724f4ccb69748187b1117977c49a030a55da77 100644 --- a/substrate/frame/support/procedural/src/construct_runtime/expand/metadata.rs +++ b/substrate/frame/support/procedural/src/construct_runtime/expand/metadata.rs @@ -46,7 +46,7 @@ pub fn expand_runtime_metadata( let index = &decl.index; let storage = expand_pallet_metadata_storage(&filtered_names, runtime, decl); let calls = expand_pallet_metadata_calls(&filtered_names, runtime, decl); - let event = expand_pallet_metadata_events(&filtered_names, runtime, scrate, decl); + let event = expand_pallet_metadata_events(&filtered_names, runtime, decl); let constants = expand_pallet_metadata_constants(runtime, decl); let errors = expand_pallet_metadata_errors(runtime, decl); let docs = expand_pallet_metadata_docs(runtime, decl); @@ -58,7 +58,7 @@ pub fn expand_runtime_metadata( #attr } }); - + let deprecation_info = expand_pallet_metadata_deprecation(runtime, decl); quote! { #attr #scrate::__private::metadata_ir::PalletMetadataIR { @@ -70,6 +70,7 @@ pub fn expand_runtime_metadata( constants: #constants, error: #errors, docs: #docs, + deprecation_info: #deprecation_info, } } }) @@ -200,7 +201,6 @@ fn expand_pallet_metadata_calls( fn expand_pallet_metadata_events( filtered_names: &[&'static str], runtime: &Ident, - scrate: &TokenStream, decl: &Pallet, ) -> TokenStream { if filtered_names.contains(&"Event") { @@ -220,9 +220,7 @@ fn expand_pallet_metadata_events( quote! { Some( - #scrate::__private::metadata_ir::PalletEventMetadataIR { - ty: #scrate::__private::scale_info::meta_type::<#pallet_event>() - } + #pallet_event::event_metadata::<#pallet_event>() ) } } else { @@ -230,6 +228,13 @@ fn expand_pallet_metadata_events( } } +fn expand_pallet_metadata_deprecation(runtime: &Ident, decl: &Pallet) -> TokenStream { + let path = &decl.path; + let instance = decl.instance.as_ref().into_iter(); + + quote! { #path::Pallet::<#runtime #(, #path::#instance)*>::deprecation_info() } +} + fn expand_pallet_metadata_constants(runtime: &Ident, decl: &Pallet) -> TokenStream { let path = &decl.path; let instance = decl.instance.as_ref().into_iter(); diff --git a/substrate/frame/support/procedural/src/construct_runtime/parse.rs b/substrate/frame/support/procedural/src/construct_runtime/parse.rs index 532e032d0cb78ac536fcebe16267d1d8c6d2949e..729a803a302ed7451f890ab6a81b666379d68d07 100644 --- a/substrate/frame/support/procedural/src/construct_runtime/parse.rs +++ b/substrate/frame/support/procedural/src/construct_runtime/parse.rs @@ -130,9 +130,9 @@ impl Parse for WhereSection { definitions.push(definition); if !input.peek(Token![,]) { if !input.peek(token::Brace) { - return Err(input.error("Expected `,` or `{`")) + return Err(input.error("Expected `,` or `{`")); } - break + break; } input.parse::()?; } @@ -144,7 +144,7 @@ impl Parse for WhereSection { "`{:?}` was declared above. Please use exactly one declaration for `{:?}`.", kind, kind ); - return Err(Error::new(*kind_span, msg)) + return Err(Error::new(*kind_span, msg)); } Ok(Self { span: input.span() }) } @@ -173,7 +173,7 @@ impl Parse for WhereDefinition { } else if lookahead.peek(keyword::UncheckedExtrinsic) { (input.parse::()?.span(), WhereKind::UncheckedExtrinsic) } else { - return Err(lookahead.error()) + return Err(lookahead.error()); }; let _: Token![=] = input.parse()?; @@ -270,7 +270,7 @@ impl Parse for PalletDeclaration { { return Err(input.error( "Unexpected tokens, expected one of `::{`, `exclude_parts`, `use_parts`, `=`, `,`", - )) + )); } else { is_expanded.then_some(extra_parts) }; @@ -283,7 +283,7 @@ impl Parse for PalletDeclaration { let _: keyword::use_parts = input.parse()?; SpecifiedParts::Use(parse_pallet_parts_no_generic(input)?) } else if !input.peek(Token![=]) && !input.peek(Token![,]) && !input.is_empty() { - return Err(input.error("Unexpected tokens, expected one of `exclude_parts`, `=`, `,`")) + return Err(input.error("Unexpected tokens, expected one of `exclude_parts`, `=`, `,`")); } else { SpecifiedParts::All }; @@ -295,7 +295,7 @@ impl Parse for PalletDeclaration { let index = index.base10_parse::()?; Some(index) } else if !input.peek(Token![,]) && !input.is_empty() { - return Err(input.error("Unexpected tokens, expected one of `=`, `,`")) + return Err(input.error("Unexpected tokens, expected one of `=`, `,`")); } else { None }; @@ -339,7 +339,7 @@ impl Parse for PalletPath { let ident = input.call(Ident::parse_any)?; res.inner.segments.push(ident.into()); } else { - return Err(lookahead.error()) + return Err(lookahead.error()); } while input.peek(Token![::]) && input.peek3(Ident) { @@ -370,7 +370,7 @@ fn parse_pallet_parts(input: ParseStream) -> Result> { "`{}` was already declared before. Please remove the duplicate declaration", part.name(), ); - return Err(Error::new(part.keyword.span(), msg)) + return Err(Error::new(part.keyword.span(), msg)); } } @@ -505,7 +505,7 @@ impl Parse for PalletPart { keyword.name(), valid_generics, ); - return Err(syn::Error::new(keyword.span(), msg)) + return Err(syn::Error::new(keyword.span(), msg)); } Ok(Self { keyword, generics }) @@ -566,7 +566,7 @@ fn parse_pallet_parts_no_generic(input: ParseStream) -> Result) -> syn::Result { if pallets.iter().any(|pallet| pallet.pallet_parts.is_none()) { - return Ok(PalletsConversion::Implicit(pallets)) + return Ok(PalletsConversion::Implicit(pallets)); } let mut indices = HashMap::new(); @@ -678,7 +678,7 @@ fn convert_pallets(pallets: Vec) -> syn::Result) -> syn::Result) -> syn::Result (), @@ -738,7 +738,7 @@ fn convert_pallets(pallets: Vec) -> syn::Result String { + format!( + r#"{msg} + help: the following are the possible correct uses +| +| #[deprecated = "reason"] +| +| #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")] +| +| #[deprecated] +|"# + ) +} + +fn parse_deprecated_meta(crate_: &TokenStream, attr: &syn::Attribute) -> Result { + match &attr.meta { + Meta::List(meta_list) => { + let parsed = meta_list + .parse_args_with(Punctuated::::parse_terminated) + .map_err(|e| Error::new(attr.span(), e.to_string()))?; + let (note, since) = parsed.iter().try_fold((None, None), |mut acc, item| { + let value = match &item.value { + Expr::Lit(ExprLit { lit: lit @ Lit::Str(_), .. }) => Ok(lit), + _ => Err(Error::new( + attr.span(), + deprecation_msg_formatter( + "Invalid deprecation attribute: expected string literal", + ), + )), + }?; + if item.path.is_ident("note") { + acc.0.replace(value); + } else if item.path.is_ident("since") { + acc.1.replace(value); + } + Ok::<(Option<&syn::Lit>, Option<&syn::Lit>), Error>(acc) + })?; + note.map_or_else( + || Err(Error::new(attr.span(), deprecation_msg_formatter("Invalid deprecation attribute: missing `note`"))), + |note| { + let since = if let Some(str) = since { + quote! { Some(#str) } + } else { + quote! { None } + }; + let doc = quote! { #crate_::__private::metadata_ir::DeprecationStatusIR::Deprecated { note: #note, since: #since }}; + Ok(doc) + }, + ) + }, + Meta::NameValue(MetaNameValue { + value: Expr::Lit(ExprLit { lit: lit @ Lit::Str(_), .. }), + .. + }) => { + // #[deprecated = "lit"] + let doc = quote! { #crate_::__private::metadata_ir::DeprecationStatusIR::Deprecated { note: #lit, since: None } }; + Ok(doc) + }, + Meta::Path(_) => { + // #[deprecated] + Ok( + quote! { #crate_::__private::metadata_ir::DeprecationStatusIR::DeprecatedWithoutNote }, + ) + }, + _ => Err(Error::new( + attr.span(), + deprecation_msg_formatter("Invalid deprecation attribute: expected string literal"), + )), + } +} + +/// collects deprecation attribute if its present. +pub fn get_deprecation(path: &TokenStream, attrs: &[syn::Attribute]) -> Result { + parse_deprecation(path, attrs).map(|item| { + item.unwrap_or_else(|| { + quote! {#path::__private::metadata_ir::DeprecationStatusIR::NotDeprecated} + }) + }) +} + +fn parse_deprecation(path: &TokenStream, attrs: &[syn::Attribute]) -> Result> { + attrs + .iter() + .find(|a| a.path().is_ident("deprecated")) + .map(|a| parse_deprecated_meta(path, a)) + .transpose() +} + +/// collects deprecation attribute if its present for enum-like types +pub fn get_deprecation_enum<'a>( + path: &TokenStream, + parent_attrs: &[syn::Attribute], + children_attrs: impl Iterator, +) -> Result { + let parent_deprecation = parse_deprecation(path, parent_attrs)?; + + let children = children_attrs + .filter_map(|(key, attributes)| { + let key = quote::quote! { #path::__private::codec::Compact(#key as u8) }; + let deprecation_status = parse_deprecation(path, attributes).transpose(); + deprecation_status.map(|item| item.map(|item| quote::quote! { (#key, #item) })) + }) + .collect::>>()?; + match (parent_deprecation, children.as_slice()) { + (None, []) => + Ok(quote::quote! { #path::__private::metadata_ir::DeprecationInfoIR::NotDeprecated }), + (None, _) => { + let children = quote::quote! { #path::__private::scale_info::prelude::collections::BTreeMap::from([#( #children),*]) }; + Ok( + quote::quote! { #path::__private::metadata_ir::DeprecationInfoIR::VariantsDeprecated(#children) }, + ) + }, + (Some(depr), _) => Ok( + quote::quote! { #path::__private::metadata_ir::DeprecationInfoIR::ItemDeprecated(#depr) }, + ), + } +} + +/// Gets the index for the variant inside `Error` or `Event` declaration. +/// priority is as follows: +/// Manual `#[codec(index = N)]` +/// Explicit discriminant `Variant = N` +/// Variant's definition index +pub fn variant_index_for_deprecation(index: u8, item: &Variant) -> u8 { + let index: u8 = + if let Some((_, Expr::Lit(ExprLit { lit: Lit::Int(num_lit), .. }))) = &item.discriminant { + num_lit.base10_parse::().unwrap_or(index as u8) + } else { + index as u8 + }; + + item.attrs + .iter() + .find(|attr| attr.path().is_ident("codec")) + .and_then(|attr| { + if let Meta::List(meta_list) = &attr.meta { + meta_list + .parse_args_with(Punctuated::::parse_terminated) + .ok() + } else { + None + } + }) + .and_then(|parsed| { + parsed.iter().fold(None, |mut acc, item| { + if let Expr::Lit(ExprLit { lit: Lit::Int(num_lit), .. }) = &item.value { + num_lit.base10_parse::().iter().for_each(|val| { + if item.path.is_ident("index") { + acc.replace(*val); + } + }) + }; + acc + }) + }) + .unwrap_or(index) +} diff --git a/substrate/frame/support/procedural/src/derive_impl.rs b/substrate/frame/support/procedural/src/derive_impl.rs index 54755f1163a1de279cad99b16f9500ccb39956eb..5d39c2707def5f81da729f6c9dbc22b6cea2bef7 100644 --- a/substrate/frame/support/procedural/src/derive_impl.rs +++ b/substrate/frame/support/procedural/src/derive_impl.rs @@ -17,13 +17,13 @@ //! Implementation of the `derive_impl` attribute macro. -use derive_syn_parse::Parse; use macro_magic::mm_core::ForeignPath; use proc_macro2::TokenStream as TokenStream2; use quote::{quote, ToTokens}; use std::collections::HashSet; use syn::{ - parse2, parse_quote, spanned::Spanned, token, Ident, ImplItem, ItemImpl, Path, Result, Token, + parse2, parse_quote, spanned::Spanned, token, AngleBracketedGenericArguments, Ident, ImplItem, + ItemImpl, Path, PathArguments, PathSegment, Result, Token, }; mod keyword { @@ -56,18 +56,60 @@ fn is_runtime_type(item: &syn::ImplItemType) -> bool { false }) } - -#[derive(Parse, Debug)] pub struct DeriveImplAttrArgs { pub default_impl_path: Path, + pub generics: Option, _as: Option, - #[parse_if(_as.is_some())] pub disambiguation_path: Option, _comma: Option, - #[parse_if(_comma.is_some())] pub no_aggregated_types: Option, } +impl syn::parse::Parse for DeriveImplAttrArgs { + fn parse(input: syn::parse::ParseStream) -> Result { + let mut default_impl_path: Path = input.parse()?; + // Extract the generics if any + let (default_impl_path, generics) = match default_impl_path.clone().segments.last() { + Some(PathSegment { ident, arguments: PathArguments::AngleBracketed(args) }) => { + default_impl_path.segments.pop(); + default_impl_path + .segments + .push(PathSegment { ident: ident.clone(), arguments: PathArguments::None }); + (default_impl_path, Some(args.clone())) + }, + Some(PathSegment { arguments: PathArguments::None, .. }) => (default_impl_path, None), + _ => return Err(syn::Error::new(default_impl_path.span(), "Invalid default impl path")), + }; + + let lookahead = input.lookahead1(); + let (_as, disambiguation_path) = if lookahead.peek(Token![as]) { + let _as: Token![as] = input.parse()?; + let disambiguation_path: Path = input.parse()?; + (Some(_as), Some(disambiguation_path)) + } else { + (None, None) + }; + + let lookahead = input.lookahead1(); + let (_comma, no_aggregated_types) = if lookahead.peek(Token![,]) { + let _comma: Token![,] = input.parse()?; + let no_aggregated_types: keyword::no_aggregated_types = input.parse()?; + (Some(_comma), Some(no_aggregated_types)) + } else { + (None, None) + }; + + Ok(DeriveImplAttrArgs { + default_impl_path, + generics, + _as, + disambiguation_path, + _comma, + no_aggregated_types, + }) + } +} + impl ForeignPath for DeriveImplAttrArgs { fn foreign_path(&self) -> &Path { &self.default_impl_path @@ -77,6 +119,7 @@ impl ForeignPath for DeriveImplAttrArgs { impl ToTokens for DeriveImplAttrArgs { fn to_tokens(&self, tokens: &mut TokenStream2) { tokens.extend(self.default_impl_path.to_token_stream()); + tokens.extend(self.generics.to_token_stream()); tokens.extend(self._as.to_token_stream()); tokens.extend(self.disambiguation_path.to_token_stream()); tokens.extend(self._comma.to_token_stream()); @@ -117,6 +160,7 @@ fn combine_impls( default_impl_path: Path, disambiguation_path: Path, inject_runtime_types: bool, + generics: Option, ) -> ItemImpl { let (existing_local_keys, existing_unsupported_items): (HashSet, HashSet) = local_impl @@ -155,7 +199,7 @@ fn combine_impls( // modify and insert uncolliding type items let modified_item: ImplItem = parse_quote! { #( #cfg_attrs )* - type #ident = <#default_impl_path as #disambiguation_path>::#ident; + type #ident = <#default_impl_path #generics as #disambiguation_path>::#ident; }; return Some(modified_item) } @@ -216,6 +260,7 @@ pub fn derive_impl( local_tokens: TokenStream2, disambiguation_path: Option, no_aggregated_types: Option, + generics: Option, ) -> Result { let local_impl = parse2::(local_tokens)?; let foreign_impl = parse2::(foreign_tokens)?; @@ -234,6 +279,7 @@ pub fn derive_impl( default_impl_path, disambiguation_path, no_aggregated_types.is_none(), + generics, ); Ok(quote!(#combined_impl)) @@ -258,6 +304,7 @@ fn test_derive_impl_attr_args_parsing() { #[test] fn test_runtime_type_with_doc() { + #[allow(dead_code)] trait TestTrait { type Test; } @@ -301,3 +348,16 @@ fn test_disambiguation_path() { compute_disambiguation_path(None, foreign_impl.clone(), parse_quote!(SomeType)); assert_eq!(disambiguation_path.unwrap(), parse_quote!(SomeTrait)); } + +#[test] +fn test_derive_impl_attr_args_parsing_with_generic() { + let args = parse2::(quote!( + some::path::TestDefaultConfig as some::path::DefaultConfig + )) + .unwrap(); + assert_eq!(args.default_impl_path, parse_quote!(some::path::TestDefaultConfig)); + assert_eq!(args.generics.unwrap().args[0], parse_quote!(Config)); + let args = parse2::(quote!(TestDefaultConfig)).unwrap(); + assert_eq!(args.default_impl_path, parse_quote!(TestDefaultConfig)); + assert_eq!(args.generics.unwrap().args[0], parse_quote!(Config2)); +} diff --git a/substrate/frame/support/procedural/src/lib.rs b/substrate/frame/support/procedural/src/lib.rs index ef99faee86aedc1168e1870b79932d6140358c13..a2c1e6eec7f78c7074185fe8f1b72f8c70d00b0c 100644 --- a/substrate/frame/support/procedural/src/lib.rs +++ b/substrate/frame/support/procedural/src/lib.rs @@ -23,6 +23,7 @@ mod benchmark; mod construct_runtime; mod crate_version; +mod deprecation; mod derive_impl; mod dummy_part_checker; mod dynamic_params; @@ -320,9 +321,10 @@ pub fn derive_debug_no_bound(input: TokenStream) -> TokenStream { /// This behaviour is useful to prevent bloating the runtime WASM blob from unneeded code. #[proc_macro_derive(RuntimeDebugNoBound)] 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 try_runtime_or_std_impl: proc_macro2::TokenStream = + no_bound::debug::derive_debug_no_bound(input.clone()).into(); + + let stripped_impl = { let input = syn::parse_macro_input!(input as syn::DeriveInput); let name = &input.ident; @@ -337,8 +339,22 @@ pub fn derive_runtime_debug_no_bound(input: TokenStream) -> TokenStream { } }; ) - .into() - } + }; + + let frame_support = match generate_access_from_frame_or_crate("frame-support") { + Ok(frame_support) => frame_support, + Err(e) => return e.to_compile_error().into(), + }; + + quote::quote!( + #frame_support::try_runtime_or_std_enabled! { + #try_runtime_or_std_impl + } + #frame_support::try_runtime_and_std_not_enabled! { + #stripped_impl + } + ) + .into() } /// Derive [`PartialEq`] but do not bound any generic. @@ -682,6 +698,7 @@ pub fn derive_impl(attrs: TokenStream, input: TokenStream) -> TokenStream { input.into(), custom_attrs.disambiguation_path, custom_attrs.no_aggregated_types, + custom_attrs.generics, ) .unwrap_or_else(|r| r.into_compile_error()) .into() @@ -812,7 +829,7 @@ pub fn inject_runtime_type(_: TokenStream, tokens: TokenStream) -> TokenStream { `RuntimeTask`, `RuntimeOrigin`, `RuntimeParameters` or `PalletInfo`", ) .to_compile_error() - .into() + .into(); } tokens } @@ -1221,7 +1238,7 @@ pub fn import_section(attr: TokenStream, tokens: TokenStream) -> TokenStream { "`#[import_section]` can only be applied to a valid pallet module", ) .to_compile_error() - .into() + .into(); } if let Some(ref mut content) = internal_mod.content { diff --git a/substrate/frame/support/procedural/src/pallet/expand/call.rs b/substrate/frame/support/procedural/src/pallet/expand/call.rs index f395872c8a80a6b9a2ddd391ea106284140d53ba..8b333d19087d287d5cf0c3ce307ba9681fd0a32c 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/call.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/call.rs @@ -18,7 +18,7 @@ use crate::{ pallet::{ expand::warnings::{weight_constant_warning, weight_witness_warning}, - parse::call::CallWeightDef, + parse::{call::CallWeightDef, helper::CallReturnType}, Def, }, COUNTER, @@ -197,18 +197,36 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream { let capture_docs = if cfg!(feature = "no-metadata-docs") { "never" } else { "always" }; // Wrap all calls inside of storage layers - if let Some(syn::Item::Impl(item_impl)) = def - .call - .as_ref() - .map(|c| &mut def.item.content.as_mut().expect("Checked by def parser").1[c.index]) - { - item_impl.items.iter_mut().for_each(|i| { - if let syn::ImplItem::Fn(method) = i { + if let Some(call) = def.call.as_ref() { + let item_impl = + &mut def.item.content.as_mut().expect("Checked by def parser").1[call.index]; + let syn::Item::Impl(item_impl) = item_impl else { + unreachable!("Checked by def parser"); + }; + + item_impl.items.iter_mut().enumerate().for_each(|(i, item)| { + if let syn::ImplItem::Fn(method) = item { + let return_type = + &call.methods.get(i).expect("def should be consistent with item").return_type; + + let (ok_type, err_type) = match return_type { + CallReturnType::DispatchResult => ( + quote::quote!(()), + quote::quote!(#frame_support::pallet_prelude::DispatchError), + ), + CallReturnType::DispatchResultWithPostInfo => ( + quote::quote!(#frame_support::dispatch::PostDispatchInfo), + quote::quote!(#frame_support::dispatch::DispatchErrorWithPostInfo), + ), + }; + let block = &method.block; method.block = syn::parse_quote! {{ // We execute all dispatchable in a new storage layer, allowing them // to return an error at any point, and undoing any storage changes. - #frame_support::storage::with_storage_layer(|| #block) + #frame_support::storage::with_storage_layer::<#ok_type, #err_type, _>( + || #block + ) }}; } }); @@ -245,6 +263,15 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream { } }); + let deprecation = match crate::deprecation::get_deprecation_enum( + "e::quote! {#frame_support}, + def.call.as_ref().map(|call| call.attrs.as_ref()).unwrap_or(&[]), + methods.iter().map(|item| (item.call_index as u8, item.attrs.as_ref())), + ) { + Ok(deprecation) => deprecation, + Err(e) => return e.into_compile_error(), + }; + quote::quote_spanned!(span => #[doc(hidden)] mod warnings { @@ -445,7 +472,10 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream { #[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() + #frame_support::__private::metadata_ir::PalletCallMetadataIR { + ty: #frame_support::__private::scale_info::meta_type::<#call_ident<#type_use_gen>>(), + deprecation_info: #deprecation, + } } } ) diff --git a/substrate/frame/support/procedural/src/pallet/expand/constants.rs b/substrate/frame/support/procedural/src/pallet/expand/constants.rs index a36df790bd2987ba422d04fa4d36d3b74dcc5c76..ca34631e7494d645be290e694acf028d86ad91b9 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/constants.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/constants.rs @@ -28,6 +28,8 @@ struct ConstDef { pub default_byte_impl: proc_macro2::TokenStream, /// Constant name for Metadata (optional) pub metadata_name: Option, + /// Deprecation_info: + pub deprecation_info: proc_macro2::TokenStream, } /// Implement the `pallet_constants_metadata` function for the pallet. @@ -42,11 +44,18 @@ pub fn expand_constants(def: &mut Def) -> proc_macro2::TokenStream { where_clauses.extend(def.extra_constants.iter().map(|d| &d.where_clause)); let completed_where_clause = super::merge_where_clauses(&where_clauses); - let config_consts = def.config.consts_metadata.iter().map(|const_| { + let mut config_consts = vec![]; + for const_ in def.config.consts_metadata.iter() { let ident = &const_.ident; let const_type = &const_.type_; - - ConstDef { + let deprecation_info = match crate::deprecation::get_deprecation( + "e::quote! { #frame_support }, + &const_.attrs, + ) { + Ok(deprecation) => deprecation, + Err(e) => return e.into_compile_error(), + }; + config_consts.push(ConstDef { ident: const_.ident.clone(), type_: const_.type_.clone(), doc: const_.doc.clone(), @@ -56,13 +65,22 @@ pub fn expand_constants(def: &mut Def) -> proc_macro2::TokenStream { #frame_support::__private::codec::Encode::encode(&value) ), metadata_name: None, - } - }); + deprecation_info, + }) + } - let extra_consts = def.extra_constants.iter().flat_map(|d| &d.extra_constants).map(|const_| { + let mut extra_consts = vec![]; + for const_ in def.extra_constants.iter().flat_map(|d| &d.extra_constants) { let ident = &const_.ident; - - ConstDef { + let deprecation_info = match crate::deprecation::get_deprecation( + "e::quote! { #frame_support }, + &const_.attrs, + ) { + Ok(deprecation) => deprecation, + Err(e) => return e.into_compile_error(), + }; + + extra_consts.push(ConstDef { ident: const_.ident.clone(), type_: const_.type_.clone(), doc: const_.doc.clone(), @@ -71,10 +89,11 @@ pub fn expand_constants(def: &mut Def) -> proc_macro2::TokenStream { #frame_support::__private::codec::Encode::encode(&value) ), metadata_name: const_.metadata_name.clone(), - } - }); + deprecation_info, + }) + } - let consts = config_consts.chain(extra_consts).map(|const_| { + let consts = config_consts.into_iter().chain(extra_consts.into_iter()).map(|const_| { let const_type = &const_.type_; let ident_str = format!("{}", const_.metadata_name.unwrap_or(const_.ident)); @@ -82,13 +101,14 @@ pub fn expand_constants(def: &mut Def) -> proc_macro2::TokenStream { let doc = if cfg!(feature = "no-metadata-docs") { &no_docs } else { &const_.doc }; let default_byte_impl = &const_.default_byte_impl; - + let deprecation_info = &const_.deprecation_info; quote::quote!({ #frame_support::__private::metadata_ir::PalletConstantMetadataIR { name: #ident_str, ty: #frame_support::__private::scale_info::meta_type::<#const_type>(), value: { #default_byte_impl }, docs: #frame_support::__private::vec![ #( #doc ),* ], + deprecation_info: #deprecation_info } }) }); diff --git a/substrate/frame/support/procedural/src/pallet/expand/error.rs b/substrate/frame/support/procedural/src/pallet/expand/error.rs index 05478ee3908416f4fe67ac142428f7dd1c039bf6..646655cfe14eb20f78b3e5f296c51aa7d1247d6e 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/error.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/error.rs @@ -104,6 +104,19 @@ pub fn expand_error(def: &mut Def) -> proc_macro2::TokenStream { let capture_docs = if cfg!(feature = "no-metadata-docs") { "never" } else { "always" }; + let deprecation = match crate::deprecation::get_deprecation_enum( + "e::quote! {#frame_support}, + &error.attrs, + error_item.variants.iter().enumerate().map(|(index, item)| { + let index = crate::deprecation::variant_index_for_deprecation(index as u8, item); + + (index, item.attrs.as_ref()) + }), + ) { + Ok(deprecation) => deprecation, + Err(e) => return e.into_compile_error(), + }; + // derive TypeInfo for error metadata error_item.attrs.push(syn::parse_quote! { #[derive( @@ -189,5 +202,16 @@ pub fn expand_error(def: &mut Def) -> proc_macro2::TokenStream { } pub use #error_token_unique_id as tt_error_token; + + impl<#type_impl_gen> #error_ident<#type_use_gen> #config_where_clause { + #[allow(dead_code)] + #[doc(hidden)] + pub fn error_metadata() -> #frame_support::__private::metadata_ir::PalletErrorMetadataIR { + #frame_support::__private::metadata_ir::PalletErrorMetadataIR { + ty: #frame_support::__private::scale_info::meta_type::<#error_ident<#type_use_gen>>(), + deprecation_info: #deprecation, + } + } + } ) } diff --git a/substrate/frame/support/procedural/src/pallet/expand/event.rs b/substrate/frame/support/procedural/src/pallet/expand/event.rs index 655fc5507d2654cfed4db4553ce5a997e4221125..8519143179d6549204dce1d9b5f3c161734b2ae5 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/event.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/event.rs @@ -71,7 +71,6 @@ pub fn expand_event(def: &mut Def) -> proc_macro2::TokenStream { let frame_support = &def.frame_support; let event_use_gen = &event.gen_kind.type_use_gen(event.attr_span); let event_impl_gen = &event.gen_kind.type_impl_gen(event.attr_span); - let event_item = { let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[event.index]; if let syn::Item::Enum(item) = item { @@ -96,6 +95,19 @@ pub fn expand_event(def: &mut Def) -> proc_macro2::TokenStream { event_item.variants.push(variant); } + let deprecation = match crate::deprecation::get_deprecation_enum( + "e::quote! {#frame_support}, + &event.attrs, + event_item.variants.iter().enumerate().map(|(index, item)| { + let index = crate::deprecation::variant_index_for_deprecation(index as u8, item); + + (index, item.attrs.as_ref()) + }), + ) { + Ok(deprecation) => deprecation, + Err(e) => return e.into_compile_error(), + }; + if get_doc_literals(&event_item.attrs).is_empty() { event_item .attrs @@ -170,5 +182,16 @@ pub fn expand_event(def: &mut Def) -> proc_macro2::TokenStream { impl<#event_impl_gen> From<#event_ident<#event_use_gen>> for () #event_where_clause { fn from(_: #event_ident<#event_use_gen>) {} } + + impl<#event_impl_gen> #event_ident<#event_use_gen> #event_where_clause { + #[allow(dead_code)] + #[doc(hidden)] + pub fn event_metadata() -> #frame_support::__private::metadata_ir::PalletEventMetadataIR { + #frame_support::__private::metadata_ir::PalletEventMetadataIR { + ty: #frame_support::__private::scale_info::meta_type::(), + deprecation_info: #deprecation, + } + } + } ) } diff --git a/substrate/frame/support/procedural/src/pallet/expand/genesis_build.rs b/substrate/frame/support/procedural/src/pallet/expand/genesis_build.rs index 248e83469435c96960f34ddccd55c71ebf650566..b71aed680dc82453d7fb7e9a9d1d2b8f3013be95 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/genesis_build.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/genesis_build.rs @@ -35,7 +35,7 @@ pub fn expand_genesis_build(def: &mut Def) -> proc_macro2::TokenStream { let where_clause = &genesis_build.where_clause; quote::quote_spanned!(genesis_build.attr_span => - #[cfg(feature = "std")] + #frame_support::std_enabled! { impl<#type_impl_gen> #frame_support::sp_runtime::BuildStorage for #gen_cfg_ident<#gen_cfg_use_gen> #where_clause { fn assimilate_storage(&self, storage: &mut #frame_support::sp_runtime::Storage) -> std::result::Result<(), std::string::String> { @@ -45,5 +45,6 @@ pub fn expand_genesis_build(def: &mut Def) -> proc_macro2::TokenStream { }) } } + } ) } diff --git a/substrate/frame/support/procedural/src/pallet/expand/hooks.rs b/substrate/frame/support/procedural/src/pallet/expand/hooks.rs index 1b0c09c4e36576eb8ba694decf0b7cfb6a99a6f1..c31ddd8a47ba7fb87e16253cb1350bf9f05ce166 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/hooks.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/hooks.rs @@ -324,15 +324,13 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { Self as #frame_support::traits::Hooks< #frame_system::pallet_prelude::BlockNumberFor:: > - >::try_state(n).map_err(|err| { + >::try_state(n).inspect_err(|err| { #frame_support::__private::log::error!( target: #frame_support::LOG_TARGET, "โŒ {:?} try_state checks failed: {:?}", #pallet_name, err ); - - err }) } } 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 7ebc4bb2e9dc28c825a91e99b4feaf3ed6e0bcd8..c6166ff45b1965dcbe297f3853336b5bd85d8ae2 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/pallet_struct.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/pallet_struct.rs @@ -83,9 +83,7 @@ pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream { impl<#type_impl_gen> #pallet_ident<#type_use_gen> #config_where_clause { #[doc(hidden)] pub fn error_metadata() -> Option<#frame_support::__private::metadata_ir::PalletErrorMetadataIR> { - Some(#frame_support::__private::metadata_ir::PalletErrorMetadataIR { - ty: #frame_support::__private::scale_info::meta_type::<#error_ident<#type_use_gen>>() - }) + Some(<#error_ident<#type_use_gen>>::error_metadata()) } } ) @@ -187,7 +185,12 @@ pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream { } } ]; - + let deprecation_status = + match crate::deprecation::get_deprecation("e::quote! {#frame_support}, &def.item.attrs) + { + Ok(deprecation) => deprecation, + Err(e) => return e.into_compile_error(), + }; quote::quote_spanned!(def.pallet_struct.attr_span => #pallet_error_metadata @@ -286,5 +289,13 @@ pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream { #storage_info #whitelisted_storage_keys_impl + + impl<#type_use_gen> #pallet_ident<#type_use_gen> { + #[allow(dead_code)] + #[doc(hidden)] + pub fn deprecation_info() -> #frame_support::__private::metadata_ir::DeprecationStatusIR { + #deprecation_status + } + } ) } diff --git a/substrate/frame/support/procedural/src/pallet/expand/storage.rs b/substrate/frame/support/procedural/src/pallet/expand/storage.rs index 9abb3029bbd94c6df602074e117975ad061727a8..e5bfa2793cbb2ce6a84772d7f8abc8a2d7b6b8ad 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/storage.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/storage.rs @@ -62,7 +62,7 @@ fn check_prefix_duplicates( if let Some(other_dup_err) = used_prefixes.insert(prefix.clone(), dup_err.clone()) { let mut err = dup_err; err.combine(other_dup_err); - return Err(err) + return Err(err); } if let Metadata::CountedMap { .. } = storage_def.metadata { @@ -79,7 +79,7 @@ fn check_prefix_duplicates( if let Some(other_dup_err) = used_prefixes.insert(counter_prefix, counter_dup_err.clone()) { let mut err = counter_dup_err; err.combine(other_dup_err); - return Err(err) + return Err(err); } } @@ -152,7 +152,7 @@ pub fn process_generics(def: &mut Def) -> syn::Result syn::Result proc_macro2::TokenStream { .filter_map(|storage_def| check_prefix_duplicates(storage_def, &mut prefix_set).err()); if let Some(mut final_error) = errors.next() { errors.for_each(|error| final_error.combine(error)); - return final_error.into_compile_error() + return final_error.into_compile_error(); } let frame_support = &def.frame_support; let frame_system = &def.frame_system; let pallet_ident = &def.pallet_struct.pallet; - - let entries_builder = def.storages.iter().map(|storage| { + let mut entries_builder = vec![]; + for storage in def.storages.iter() { let no_docs = vec![]; let docs = if cfg!(feature = "no-metadata-docs") { &no_docs } else { &storage.docs }; @@ -418,19 +418,26 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { let full_ident = quote::quote_spanned!(storage.attr_span => #ident<#gen> ); let cfg_attrs = &storage.cfg_attrs; - - quote::quote_spanned!(storage.attr_span => + let deprecation = match crate::deprecation::get_deprecation( + "e::quote! { #frame_support }, + &storage.attrs, + ) { + Ok(deprecation) => deprecation, + Err(e) => return e.into_compile_error(), + }; + entries_builder.push(quote::quote_spanned!(storage.attr_span => #(#cfg_attrs)* { <#full_ident as #frame_support::storage::StorageEntryMetadataBuilder>::build_metadata( + #deprecation, #frame_support::__private::vec![ #( #docs, )* ], &mut entries, ); } - ) - }); + )) + } let getters = def.storages.iter().map(|storage| { if let Some(getter) = &storage.getter { diff --git a/substrate/frame/support/procedural/src/pallet/parse/call.rs b/substrate/frame/support/procedural/src/pallet/parse/call.rs index 4e09b86fddec171cdbcd9d4a9c79fe9c6d922960..68ced1bc0ed32246837d1ffa0e018d4e16198c48 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/call.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/call.rs @@ -51,6 +51,8 @@ pub struct CallDef { pub docs: Vec, /// The optional `weight` attribute on the `pallet::call`. pub inherited_call_weight: Option, + /// attributes + pub attrs: Vec, } /// The weight of a call. @@ -89,6 +91,8 @@ pub struct CallVariantDef { pub cfg_attrs: Vec, /// The optional `feeless_if` attribute on the `pallet::call`. pub feeless_check: Option, + /// The return type of the call: `DispatchInfo` or `DispatchResultWithPostInfo`. + pub return_type: helper::CallReturnType, } /// Attributes for functions in call impl block. @@ -122,7 +126,7 @@ impl syn::parse::Parse for FunctionAttr { let index = call_index_content.parse::()?; if !index.suffix().is_empty() { let msg = "Number literal must not have a suffix"; - return Err(syn::Error::new(index.span(), msg)) + return Err(syn::Error::new(index.span(), msg)); } Ok(FunctionAttr::CallIndex(index.base10_parse()?)) } else if lookahead.peek(keyword::feeless_if) { @@ -198,9 +202,9 @@ pub fn check_dispatchable_first_arg_type(ty: &syn::Type, is_ref: bool) -> syn::R } else { "Invalid type: expected `OriginFor` or `T::RuntimeOrigin`" }; - return Err(syn::Error::new(ty.span(), msg)) + return Err(syn::Error::new(ty.span(), msg)); }, - } + }; } impl CallDef { @@ -214,9 +218,8 @@ impl CallDef { let item_impl = if let syn::Item::Impl(item) = item { item } else { - return Err(syn::Error::new(item.span(), "Invalid pallet::call, expected item impl")) + return Err(syn::Error::new(item.span(), "Invalid pallet::call, expected item impl")); }; - let instances = vec![ helper::check_impl_gen(&item_impl.generics, item_impl.impl_token.span())?, helper::check_pallet_struct_usage(&item_impl.self_ty)?, @@ -225,7 +228,7 @@ impl CallDef { if let Some((_, _, for_)) = item_impl.trait_ { let msg = "Invalid pallet::call, expected no trait ident as in \ `impl<..> Pallet<..> { .. }`"; - return Err(syn::Error::new(for_.span(), msg)) + return Err(syn::Error::new(for_.span(), msg)); } let mut methods = vec![]; @@ -242,31 +245,25 @@ impl CallDef { _ => method.vis.span(), }; - return Err(syn::Error::new(span, msg)) + return Err(syn::Error::new(span, msg)); } match method.sig.inputs.first() { None => { let msg = "Invalid pallet::call, must have at least origin arg"; - return Err(syn::Error::new(method.sig.span(), msg)) + return Err(syn::Error::new(method.sig.span(), msg)); }, Some(syn::FnArg::Receiver(_)) => { let msg = "Invalid pallet::call, first argument must be a typed argument, \ e.g. `origin: OriginFor`"; - return Err(syn::Error::new(method.sig.span(), msg)) + return Err(syn::Error::new(method.sig.span(), msg)); }, Some(syn::FnArg::Typed(arg)) => { check_dispatchable_first_arg_type(&arg.ty, false)?; }, } - if let syn::ReturnType::Type(_, type_) = &method.sig.output { - helper::check_pallet_call_return_type(type_)?; - } else { - let msg = "Invalid pallet::call, require return type \ - DispatchResultWithPostInfo"; - return Err(syn::Error::new(method.sig.span(), msg)) - } + let return_type = helper::check_pallet_call_return_type(&method.sig)?; let cfg_attrs: Vec = helper::get_item_cfg_attrs(&method.attrs); let mut call_idx_attrs = vec![]; @@ -308,13 +305,13 @@ impl CallDef { }, _ => { let msg = "Invalid pallet::call, too many weight attributes given"; - return Err(syn::Error::new(method.sig.span(), msg)) + return Err(syn::Error::new(method.sig.span(), msg)); }, }; if call_idx_attrs.len() > 1 { let msg = "Invalid pallet::call, too many call_index attributes given"; - return Err(syn::Error::new(method.sig.span(), msg)) + return Err(syn::Error::new(method.sig.span(), msg)); } let call_index = call_idx_attrs.pop().map(|attr| match attr { FunctionAttr::CallIndex(idx) => idx, @@ -339,7 +336,7 @@ impl CallDef { ); let mut err = syn::Error::new(used_fn.span(), &msg); err.combine(syn::Error::new(method.sig.ident.span(), msg)); - return Err(err) + return Err(err); } let mut args = vec![]; @@ -355,14 +352,14 @@ impl CallDef { if arg_attrs.len() > 1 { let msg = "Invalid pallet::call, argument has too many attributes"; - return Err(syn::Error::new(arg.span(), msg)) + return Err(syn::Error::new(arg.span(), msg)); } let arg_ident = if let syn::Pat::Ident(pat) = &*arg.pat { pat.ident.clone() } else { let msg = "Invalid pallet::call, argument must be ident"; - return Err(syn::Error::new(arg.pat.span(), msg)) + return Err(syn::Error::new(arg.pat.span(), msg)); }; args.push((!arg_attrs.is_empty(), arg_ident, arg.ty.clone())); @@ -372,7 +369,7 @@ impl CallDef { if feeless_attrs.len() > 1 { let msg = "Invalid pallet::call, there can only be one feeless_if attribute"; - return Err(syn::Error::new(feeless_attrs[1].0, msg)) + return Err(syn::Error::new(feeless_attrs[1].0, msg)); } let feeless_check: Option = feeless_attrs.pop().map(|(_, attr)| match attr { @@ -384,13 +381,13 @@ impl CallDef { if feeless_check.inputs.len() != args.len() + 1 { let msg = "Invalid pallet::call, feeless_if closure must have same \ number of arguments as the dispatchable function"; - return Err(syn::Error::new(feeless_check.span(), msg)) + return Err(syn::Error::new(feeless_check.span(), msg)); } match feeless_check.inputs.first() { None => { let msg = "Invalid pallet::call, feeless_if closure must have at least origin arg"; - return Err(syn::Error::new(feeless_check.span(), msg)) + return Err(syn::Error::new(feeless_check.span(), msg)); }, Some(syn::Pat::Type(arg)) => { check_dispatchable_first_arg_type(&arg.ty, true)?; @@ -398,29 +395,30 @@ impl CallDef { _ => { let msg = "Invalid pallet::call, feeless_if closure first argument must be a typed argument, \ e.g. `origin: OriginFor`"; - return Err(syn::Error::new(feeless_check.span(), msg)) + return Err(syn::Error::new(feeless_check.span(), msg)); }, } for (feeless_arg, arg) in feeless_check.inputs.iter().skip(1).zip(args.iter()) { - let feeless_arg_type = - if let syn::Pat::Type(syn::PatType { ty, .. }) = feeless_arg.clone() { - if let syn::Type::Reference(pat) = *ty { - pat.elem.clone() - } else { - let msg = "Invalid pallet::call, feeless_if closure argument must be a reference"; - return Err(syn::Error::new(ty.span(), msg)) - } + let feeless_arg_type = if let syn::Pat::Type(syn::PatType { ty, .. }) = + feeless_arg.clone() + { + if let syn::Type::Reference(pat) = *ty { + pat.elem.clone() } else { - let msg = "Invalid pallet::call, feeless_if closure argument must be a type ascription pattern"; - return Err(syn::Error::new(feeless_arg.span(), msg)) - }; + let msg = "Invalid pallet::call, feeless_if closure argument must be a reference"; + return Err(syn::Error::new(ty.span(), msg)); + } + } else { + let msg = "Invalid pallet::call, feeless_if closure argument must be a type ascription pattern"; + return Err(syn::Error::new(feeless_arg.span(), msg)); + }; if feeless_arg_type != arg.2 { let msg = "Invalid pallet::call, feeless_if closure argument must have \ a reference to the same type as the dispatchable function argument"; - return Err(syn::Error::new(feeless_arg.span(), msg)) + return Err(syn::Error::new(feeless_arg.span(), msg)); } } @@ -433,7 +431,7 @@ impl CallDef { }; if !valid_return { let msg = "Invalid pallet::call, feeless_if closure must return `bool`"; - return Err(syn::Error::new(feeless_check.output.span(), msg)) + return Err(syn::Error::new(feeless_check.output.span(), msg)); } } @@ -447,10 +445,11 @@ impl CallDef { attrs: method.attrs.clone(), cfg_attrs, feeless_check, + return_type, }); } else { let msg = "Invalid pallet::call, only method accepted"; - return Err(syn::Error::new(item.span(), msg)) + return Err(syn::Error::new(item.span(), msg)); } } @@ -462,6 +461,7 @@ impl CallDef { where_clause: item_impl.generics.where_clause.clone(), docs: get_doc_literals(&item_impl.attrs), inherited_call_weight, + attrs: item_impl.attrs.clone(), }) } } diff --git a/substrate/frame/support/procedural/src/pallet/parse/config.rs b/substrate/frame/support/procedural/src/pallet/parse/config.rs index 6febaac9ffa3284248f6d22466a1eb350520330d..9a59d7114202ff4d2b95fe99fdce9d7dbf839bef 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/config.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/config.rs @@ -78,6 +78,8 @@ pub struct ConstMetadataDef { pub type_: syn::Type, /// The doc associated pub doc: Vec, + /// attributes + pub attrs: Vec, } impl TryFrom<&syn::TraitItemType> for ConstMetadataDef { @@ -100,22 +102,22 @@ impl TryFrom<&syn::TraitItemType> for ConstMetadataDef { .ok_or_else(|| err(trait_ty.span(), "`Get` trait bound not found"))?; let syn::PathArguments::AngleBracketed(ref ab) = bound.arguments else { - return Err(err(bound.span(), "Expected trait generic args")) + return Err(err(bound.span(), "Expected trait generic args")); }; // Only one type argument is expected. if ab.args.len() != 1 { - return Err(err(bound.span(), "Expected a single type argument")) + return Err(err(bound.span(), "Expected a single type argument")); } let syn::GenericArgument::Type(ref type_arg) = ab.args[0] else { - return Err(err(ab.args[0].span(), "Expected a type argument")) + return Err(err(ab.args[0].span(), "Expected a type argument")); }; let type_ = syn::parse2::(replace_self_by_t(type_arg.to_token_stream())) .expect("Internal error: replacing `Self` by `T` should result in valid type"); - Ok(Self { ident, type_, doc }) + Ok(Self { ident, type_, doc, attrs: trait_ty.attrs.clone() }) } } @@ -220,7 +222,7 @@ fn check_event_type( let syn::TraitItem::Type(type_) = trait_item else { return Ok(false) }; if type_.ident != "RuntimeEvent" { - return Ok(false) + return Ok(false); } // Check event has no generics @@ -228,7 +230,7 @@ fn check_event_type( let msg = "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must have\ no generics nor where_clause"; - return Err(syn::Error::new(trait_item.span(), msg)) + return Err(syn::Error::new(trait_item.span(), msg)); } // Check bound contains IsType and From @@ -242,7 +244,7 @@ fn check_event_type( "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must \ bound: `IsType<::RuntimeEvent>`" .to_string(); - return Err(syn::Error::new(type_.span(), msg)) + return Err(syn::Error::new(type_.span(), msg)); } let from_event_bound = type_ @@ -254,7 +256,7 @@ fn check_event_type( let msg = "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must \ bound: `From` or `From>` or `From>`"; - return Err(syn::Error::new(type_.span(), msg)) + return Err(syn::Error::new(type_.span(), msg)); }; if from_event_bound.is_generic && (from_event_bound.has_instance != trait_has_instance) { @@ -262,7 +264,7 @@ fn check_event_type( "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` bounds inconsistent \ `From`. Config and generic Event must be both with instance or \ without instance"; - return Err(syn::Error::new(type_.span(), msg)) + return Err(syn::Error::new(type_.span(), msg)); } Ok(true) @@ -274,7 +276,7 @@ fn check_event_type( fn has_expected_system_config(path: syn::Path, frame_system: &syn::Path) -> bool { // Check if `frame_system` is actually 'frame_system'. if path.segments.iter().all(|s| s.ident != "frame_system") { - return false + return false; } let mut expected_system_config = @@ -329,12 +331,12 @@ impl ConfigDef { ) -> syn::Result { let syn::Item::Trait(item) = item else { let msg = "Invalid pallet::config, expected trait definition"; - return Err(syn::Error::new(item.span(), msg)) + return Err(syn::Error::new(item.span(), msg)); }; if !matches!(item.vis, syn::Visibility::Public(_)) { let msg = "Invalid pallet::config, trait must be public"; - return Err(syn::Error::new(item.span(), msg)) + return Err(syn::Error::new(item.span(), msg)); } syn::parse2::(item.ident.to_token_stream())?; @@ -349,7 +351,7 @@ impl ConfigDef { if item.generics.params.len() > 1 { let msg = "Invalid pallet::config, expected no more than one generic"; - return Err(syn::Error::new(item.generics.params[2].span(), msg)) + return Err(syn::Error::new(item.generics.params[2].span(), msg)); } let has_instance = if item.generics.params.first().is_some() { @@ -391,7 +393,7 @@ impl ConfigDef { return Err(syn::Error::new( pallet_attr._bracket.span.join(), "Duplicate #[pallet::constant] attribute not allowed.", - )) + )); } already_constant = true; consts_metadata.push(ConstMetadataDef::try_from(typ)?); @@ -407,13 +409,13 @@ impl ConfigDef { pallet_attr._bracket.span.join(), "`#[pallet:no_default]` can only be used if `#[pallet::config(with_default)]` \ has been specified" - )) + )); } if already_no_default { return Err(syn::Error::new( pallet_attr._bracket.span.join(), "Duplicate #[pallet::no_default] attribute not allowed.", - )) + )); } already_no_default = true; @@ -424,13 +426,13 @@ impl ConfigDef { pallet_attr._bracket.span.join(), "`#[pallet:no_default_bounds]` can only be used if `#[pallet::config(with_default)]` \ has been specified" - )) + )); } if already_no_default_bounds { return Err(syn::Error::new( pallet_attr._bracket.span.join(), "Duplicate #[pallet::no_default_bounds] attribute not allowed.", - )) + )); } already_no_default_bounds = true; }, @@ -472,7 +474,7 @@ impl ConfigDef { frame_system.to_token_stream(), found, ); - return Err(syn::Error::new(item.span(), msg)) + return Err(syn::Error::new(item.span(), msg)); } Ok(Self { diff --git a/substrate/frame/support/procedural/src/pallet/parse/error.rs b/substrate/frame/support/procedural/src/pallet/parse/error.rs index bc4087a0ea763d5ea6b80f221ab53a50386a8f02..1a44e79cf0b872df90f83afb77698ebd705ea295 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/error.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/error.rs @@ -53,6 +53,8 @@ pub struct ErrorDef { pub error: keyword::Error, /// The span of the pallet::error attribute. pub attr_span: proc_macro2::Span, + /// Attributes + pub attrs: Vec, } impl ErrorDef { @@ -102,6 +104,6 @@ impl ErrorDef { }) .collect::>()?; - Ok(ErrorDef { attr_span, index, variants, instances, error }) + Ok(ErrorDef { attr_span, index, variants, instances, error, attrs: item.attrs.clone() }) } } diff --git a/substrate/frame/support/procedural/src/pallet/parse/event.rs b/substrate/frame/support/procedural/src/pallet/parse/event.rs index 0fb8ee4f549777fe6c9d25e99e3e082553c70d7c..518a9597e2a0e2a2253c2a6ec60cc6982ac473a1 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/event.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/event.rs @@ -43,6 +43,8 @@ pub struct EventDef { pub where_clause: Option, /// The span of the pallet::event attribute. pub attr_span: proc_macro2::Span, + /// event attributes + pub attrs: Vec, } /// Attribute for a pallet's Event. @@ -106,7 +108,7 @@ impl EventDef { } else { return Err(syn::Error::new(item.span(), "Invalid pallet::event, expected enum item")) }; - + let attrs = item.attrs.clone(); let event_attrs: Vec = helper::take_item_pallet_attrs(&mut item.attrs)?; let attr_info = PalletEventAttrInfo::from_attrs(event_attrs)?; @@ -136,6 +138,15 @@ impl EventDef { let event = syn::parse2::(item.ident.to_token_stream())?; - Ok(EventDef { attr_span, index, instances, deposit_event, event, gen_kind, where_clause }) + Ok(EventDef { + attr_span, + index, + instances, + deposit_event, + event, + gen_kind, + where_clause, + attrs, + }) } } diff --git a/substrate/frame/support/procedural/src/pallet/parse/extra_constants.rs b/substrate/frame/support/procedural/src/pallet/parse/extra_constants.rs index 12a373db180c328b380b42a5fbf1cbec6efe5e81..d1960d8c6f0123304d52a53e8842e371c68685c6 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/extra_constants.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/extra_constants.rs @@ -51,6 +51,8 @@ pub struct ExtraConstantDef { pub doc: Vec, /// Optional MetaData Name pub metadata_name: Option, + /// Attributes + pub attrs: Vec, } /// Attributes for functions in extra_constants impl block. @@ -82,7 +84,7 @@ impl ExtraConstantsDef { return Err(syn::Error::new( item.span(), "Invalid pallet::extra_constants, expected item impl", - )) + )); }; let instances = vec![ @@ -93,7 +95,7 @@ impl ExtraConstantsDef { if let Some((_, _, for_)) = item.trait_ { let msg = "Invalid pallet::call, expected no trait ident as in \ `impl<..> Pallet<..> { .. }`"; - return Err(syn::Error::new(for_.span(), msg)) + return Err(syn::Error::new(for_.span(), msg)); } let mut extra_constants = vec![]; @@ -102,28 +104,28 @@ impl ExtraConstantsDef { method } else { let msg = "Invalid pallet::call, only method accepted"; - return Err(syn::Error::new(impl_item.span(), msg)) + return Err(syn::Error::new(impl_item.span(), msg)); }; if !method.sig.inputs.is_empty() { let msg = "Invalid pallet::extra_constants, method must have 0 args"; - return Err(syn::Error::new(method.sig.span(), msg)) + return Err(syn::Error::new(method.sig.span(), msg)); } if !method.sig.generics.params.is_empty() { let msg = "Invalid pallet::extra_constants, method must have 0 generics"; - return Err(syn::Error::new(method.sig.generics.params[0].span(), msg)) + return Err(syn::Error::new(method.sig.generics.params[0].span(), msg)); } if method.sig.generics.where_clause.is_some() { let msg = "Invalid pallet::extra_constants, method must have no where clause"; - return Err(syn::Error::new(method.sig.generics.where_clause.span(), msg)) + return Err(syn::Error::new(method.sig.generics.where_clause.span(), msg)); } let type_ = match &method.sig.output { syn::ReturnType::Default => { let msg = "Invalid pallet::extra_constants, method must have a return type"; - return Err(syn::Error::new(method.span(), msg)) + return Err(syn::Error::new(method.span(), msg)); }, syn::ReturnType::Type(_, type_) => *type_.clone(), }; @@ -135,7 +137,7 @@ impl ExtraConstantsDef { if extra_constant_attrs.len() > 1 { let msg = "Invalid attribute in pallet::constant_name, only one attribute is expected"; - return Err(syn::Error::new(extra_constant_attrs[1].metadata_name.span(), msg)) + return Err(syn::Error::new(extra_constant_attrs[1].metadata_name.span(), msg)); } let metadata_name = extra_constant_attrs.pop().map(|attr| attr.metadata_name); @@ -145,6 +147,7 @@ impl ExtraConstantsDef { type_, doc: get_doc_literals(&method.attrs), metadata_name, + attrs: method.attrs.clone(), }); } diff --git a/substrate/frame/support/procedural/src/pallet/parse/helper.rs b/substrate/frame/support/procedural/src/pallet/parse/helper.rs index d4f58a4c56df8ba61e26535cc19287f336e78846..d5ae607d90f9cc0e3c722ea607481f1fe3735bd6 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/helper.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/helper.rs @@ -597,25 +597,38 @@ pub fn check_type_value_gen( Ok(i) } +/// The possible return type of a dispatchable. +#[derive(Clone)] +pub enum CallReturnType { + DispatchResult, + DispatchResultWithPostInfo, +} + /// Check the keyword `DispatchResultWithPostInfo` or `DispatchResult`. -pub fn check_pallet_call_return_type(type_: &syn::Type) -> syn::Result<()> { - pub struct Checker; +pub fn check_pallet_call_return_type(sig: &syn::Signature) -> syn::Result { + let syn::ReturnType::Type(_, type_) = &sig.output else { + let msg = "Invalid pallet::call, require return type \ + DispatchResultWithPostInfo"; + return Err(syn::Error::new(sig.span(), msg)) + }; + + pub struct Checker(CallReturnType); impl syn::parse::Parse for Checker { fn parse(input: syn::parse::ParseStream) -> syn::Result { let lookahead = input.lookahead1(); if lookahead.peek(keyword::DispatchResultWithPostInfo) { input.parse::()?; - Ok(Self) + Ok(Self(CallReturnType::DispatchResultWithPostInfo)) } else if lookahead.peek(keyword::DispatchResult) { input.parse::()?; - Ok(Self) + Ok(Self(CallReturnType::DispatchResult)) } else { Err(lookahead.error()) } } } - syn::parse2::(type_.to_token_stream()).map(|_| ()) + syn::parse2::(type_.to_token_stream()).map(|c| c.0) } pub(crate) fn two128_str(s: &str) -> TokenStream { diff --git a/substrate/frame/support/procedural/src/pallet/parse/mod.rs b/substrate/frame/support/procedural/src/pallet/parse/mod.rs index f55b166c7917cb019d82bd243539cea6aa0862b3..b9c7afcab0f917b4242543a2967e3e9a9de999e0 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/mod.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/mod.rs @@ -76,7 +76,6 @@ impl Def { pub fn try_from(mut item: syn::ItemMod, dev_mode: bool) -> syn::Result { let frame_system = generate_access_from_frame_or_crate("frame-system")?; let frame_support = generate_access_from_frame_or_crate("frame-support")?; - let item_span = item.span(); let items = &mut item .content @@ -221,7 +220,7 @@ impl Def { genesis_config.as_ref().map_or("unused", |_| "used"), genesis_build.as_ref().map_or("unused", |_| "used"), ); - return Err(syn::Error::new(item_span, msg)) + return Err(syn::Error::new(item_span, msg)); } Self::resolve_tasks(&item_span, &mut tasks, &mut task_enum, items)?; @@ -284,7 +283,7 @@ impl Def { tasks.item_impl.impl_token.span(), "A `#[pallet::tasks_experimental]` attribute must be attached to your `Task` impl if the \ task enum has been omitted", - )) + )); } else { }, _ => (), @@ -313,7 +312,7 @@ impl Def { // `task_enum`. We use a no-op instead of simply removing it from the vec // so that any indices collected by `Def::try_from` remain accurate *item = syn::Item::Verbatim(quote::quote!()); - break + break; } } *task_enum = result; @@ -336,7 +335,7 @@ impl Def { let syn::Type::Path(target_path) = &*item_impl.self_ty else { continue }; let target_path = target_path.path.segments.iter().collect::>(); let (Some(target_ident), None) = (target_path.get(0), target_path.get(1)) else { - continue + continue; }; let matches_task_enum = match task_enum { Some(task_enum) => task_enum.item_enum.ident == target_ident.ident, @@ -344,7 +343,7 @@ impl Def { }; if trait_last_seg.ident == "Task" && matches_task_enum { result = Some(syn::parse2::(item_impl.to_token_stream())?); - break + break; } } *tasks = result; @@ -407,7 +406,7 @@ impl Def { let mut errors = instances.into_iter().filter_map(|instances| { if instances.has_instance == self.config.has_instance { - return None + return None; } let msg = if self.config.has_instance { "Invalid generic declaration, trait is defined with instance but generic use none" @@ -739,7 +738,7 @@ impl syn::parse::Parse for InheritedCallWeightAttr { content.parse::().expect("peeked"); content } else { - return Err(lookahead.error()) + return Err(lookahead.error()); }; Ok(Self { typename: buffer.parse()? }) diff --git a/substrate/frame/support/procedural/src/pallet/parse/storage.rs b/substrate/frame/support/procedural/src/pallet/parse/storage.rs index 9d96a18b56943db4715fabd30e8bc0f1c0aa7d28..352f691151bef78692e7a19dddce6b1d491a8921 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/storage.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/storage.rs @@ -207,6 +207,8 @@ pub struct StorageDef { pub try_decode: bool, /// Whether or not a default hasher is allowed to replace `_` pub use_default_hasher: bool, + /// Attributes + pub attrs: Vec, } /// The parsed generic from the @@ -373,7 +375,7 @@ fn process_named_generics( let msg = "Invalid pallet::storage, Duplicated named generic"; let mut err = syn::Error::new(arg.ident.span(), msg); err.combine(syn::Error::new(other.ident.span(), msg)); - return Err(err) + return Err(err); } parsed.insert(arg.ident.to_string(), arg.clone()); } @@ -666,7 +668,7 @@ fn process_generics( in order to expand metadata, found `{}`.", found, ); - return Err(syn::Error::new(segment.ident.span(), msg)) + return Err(syn::Error::new(segment.ident.span(), msg)); }, }; @@ -677,7 +679,7 @@ fn process_generics( _ => { let msg = "Invalid pallet::storage, invalid number of generic generic arguments, \ expect more that 0 generic arguments."; - return Err(syn::Error::new(segment.span(), msg)) + return Err(syn::Error::new(segment.span(), msg)); }, }; @@ -724,7 +726,7 @@ fn extract_key(ty: &syn::Type) -> syn::Result { typ } else { let msg = "Invalid pallet::storage, expected type path"; - return Err(syn::Error::new(ty.span(), msg)) + return Err(syn::Error::new(ty.span(), msg)); }; let key_struct = typ.path.segments.last().ok_or_else(|| { @@ -733,14 +735,14 @@ fn extract_key(ty: &syn::Type) -> syn::Result { })?; if key_struct.ident != "Key" && key_struct.ident != "NMapKey" { let msg = "Invalid pallet::storage, expected Key or NMapKey struct"; - return Err(syn::Error::new(key_struct.ident.span(), msg)) + return Err(syn::Error::new(key_struct.ident.span(), msg)); } let ty_params = if let syn::PathArguments::AngleBracketed(args) = &key_struct.arguments { args } else { let msg = "Invalid pallet::storage, expected angle bracketed arguments"; - return Err(syn::Error::new(key_struct.arguments.span(), msg)) + return Err(syn::Error::new(key_struct.arguments.span(), msg)); }; if ty_params.args.len() != 2 { @@ -749,14 +751,14 @@ fn extract_key(ty: &syn::Type) -> syn::Result { for Key struct, expected 2 args, found {}", ty_params.args.len() ); - return Err(syn::Error::new(ty_params.span(), msg)) + return Err(syn::Error::new(ty_params.span(), msg)); } let key = match &ty_params.args[1] { syn::GenericArgument::Type(key_ty) => key_ty.clone(), _ => { let msg = "Invalid pallet::storage, expected type"; - return Err(syn::Error::new(ty_params.args[1].span(), msg)) + return Err(syn::Error::new(ty_params.args[1].span(), msg)); }, }; @@ -790,7 +792,7 @@ impl StorageDef { let item = if let syn::Item::Type(item) = item { item } else { - return Err(syn::Error::new(item.span(), "Invalid pallet::storage, expect item type.")) + return Err(syn::Error::new(item.span(), "Invalid pallet::storage, expect item type.")); }; let attrs: Vec = helper::take_item_pallet_attrs(&mut item.attrs)?; @@ -810,12 +812,12 @@ impl StorageDef { typ } else { let msg = "Invalid pallet::storage, expected type path"; - return Err(syn::Error::new(item.ty.span(), msg)) + return Err(syn::Error::new(item.ty.span(), msg)); }; if typ.path.segments.len() != 1 { let msg = "Invalid pallet::storage, expected type path with one segment"; - return Err(syn::Error::new(item.ty.span(), msg)) + return Err(syn::Error::new(item.ty.span(), msg)); } let (named_generics, metadata, query_kind, use_default_hasher) = @@ -858,7 +860,7 @@ impl StorageDef { for ResultQuery, expected 1 type argument, found {}", args.len(), ); - return Err(syn::Error::new(args.span(), msg)) + return Err(syn::Error::new(args.span(), msg)); } args[0].clone() @@ -869,7 +871,7 @@ impl StorageDef { expected angle-bracketed arguments, found `{}`", args.to_token_stream().to_string() ); - return Err(syn::Error::new(args.span(), msg)) + return Err(syn::Error::new(args.span(), msg)); }, }; @@ -885,7 +887,7 @@ impl StorageDef { segments, found {}", err_variant.len(), ); - return Err(syn::Error::new(err_variant.span(), msg)) + return Err(syn::Error::new(err_variant.span(), msg)); } let mut error = err_variant.clone(); let err_variant = error @@ -921,7 +923,7 @@ impl StorageDef { let msg = "Invalid pallet::storage, cannot generate getter because QueryKind is not \ identifiable. QueryKind must be `OptionQuery`, `ResultQuery`, `ValueQuery`, or default \ one to be identifiable."; - return Err(syn::Error::new(getter.span(), msg)) + return Err(syn::Error::new(getter.span(), msg)); } Ok(StorageDef { @@ -942,6 +944,7 @@ impl StorageDef { whitelisted, try_decode, use_default_hasher, + attrs: item.attrs.clone(), }) } } diff --git a/substrate/frame/support/procedural/src/runtime/expand/mod.rs b/substrate/frame/support/procedural/src/runtime/expand/mod.rs index f34ab1cef5436ffd6fccfff628a94d149a9ed5f5..666bc03aa415df6b495cd479e62cb929f8767cd8 100644 --- a/substrate/frame/support/procedural/src/runtime/expand/mod.rs +++ b/substrate/frame/support/procedural/src/runtime/expand/mod.rs @@ -77,7 +77,7 @@ pub fn expand(def: Def, legacy_ordering: bool) -> TokenStream2 { }; let res = expander::Expander::new("construct_runtime") - .dry(std::env::var("FRAME_EXPAND").is_err()) + .dry(std::env::var("EXPAND_MACROS").is_err()) .verbose(true) .write_to_out_dir(res) .expect("Does not fail because of IO in OUT_DIR; qed"); diff --git a/substrate/frame/support/procedural/src/runtime/parse/mod.rs b/substrate/frame/support/procedural/src/runtime/parse/mod.rs index a3d1c9417df81208a4e7f9e9b0a9e00afe9d1ca4..27a7c35a73004feac70fd3aa029bf16508ad950d 100644 --- a/substrate/frame/support/procedural/src/runtime/parse/mod.rs +++ b/substrate/frame/support/procedural/src/runtime/parse/mod.rs @@ -87,7 +87,7 @@ impl syn::parse::Parse for RuntimeAttr { let pallet_index = pallet_index_content.parse::()?; if !pallet_index.suffix().is_empty() { let msg = "Number literal must not have a suffix"; - return Err(syn::Error::new(pallet_index.span(), msg)) + return Err(syn::Error::new(pallet_index.span(), msg)); } Ok(RuntimeAttr::PalletIndex(pallet_index.span(), pallet_index.base10_parse()?)) } else if lookahead.peek(keyword::disable_call) { @@ -171,14 +171,14 @@ impl Def { Some((index, item.clone())) } else { let msg = "Invalid runtime::pallet_index, expected type definition"; - return Err(syn::Error::new(span, msg)) + return Err(syn::Error::new(span, msg)); }; }, RuntimeAttr::DisableCall(_) => disable_call = true, RuntimeAttr::DisableUnsigned(_) => disable_unsigned = true, attr => { let msg = "Invalid duplicated attribute"; - return Err(syn::Error::new(attr.span(), msg)) + return Err(syn::Error::new(attr.span(), msg)); }, } } @@ -196,7 +196,7 @@ impl Def { let mut err = syn::Error::new(used_pallet, &msg); err.combine(syn::Error::new(pallet_decl.name.span(), &msg)); - return Err(err) + return Err(err); } pallet_decls.push(pallet_decl); @@ -219,7 +219,7 @@ impl Def { ); let mut err = syn::Error::new(used_pallet.span(), &msg); err.combine(syn::Error::new(pallet.name.span(), msg)); - return Err(err) + return Err(err); } pallets.push(pallet); @@ -229,7 +229,7 @@ impl Def { } else { if let syn::Item::Type(item) = item { let msg = "Missing pallet index for pallet declaration. Please add `#[runtime::pallet_index(...)]`"; - return Err(syn::Error::new(item.span(), &msg)) + return Err(syn::Error::new(item.span(), &msg)); } } } diff --git a/substrate/frame/support/procedural/src/runtime/parse/pallet.rs b/substrate/frame/support/procedural/src/runtime/parse/pallet.rs index ebfd0c9ccceed5156d48c8704cae2eb14a3423b5..52f57cd2cd8b66fd574a48663ebc5bd832b642e5 100644 --- a/substrate/frame/support/procedural/src/runtime/parse/pallet.rs +++ b/substrate/frame/support/procedural/src/runtime/parse/pallet.rs @@ -49,7 +49,7 @@ impl Pallet { return Err(Error::new( attr_span, "Invalid pallet declaration, expected a path or a trait object", - )) + )); }; } diff --git a/substrate/frame/support/src/lib.rs b/substrate/frame/support/src/lib.rs index bd571571ee24a872ec87c047abac7431190ab2d7..269867e38c512232ef2c41bc561e294b08f365f6 100644 --- a/substrate/frame/support/src/lib.rs +++ b/substrate/frame/support/src/lib.rs @@ -1051,6 +1051,12 @@ pub mod pallet_prelude { /// [`StorageInfoTrait`](frame_support::traits::StorageInfoTrait) for the pallet using the /// [`PartialStorageInfoTrait`](frame_support::traits::PartialStorageInfoTrait) /// implementation of storages. +/// +/// ## Note on deprecation. +/// +/// - Usage of `deprecated` attribute will propagate deprecation information to the pallet +/// metadata. +/// - For general usage examples of `deprecated` attribute please refer to pub use frame_support_procedural::pallet; /// Contains macro stubs for all of the `pallet::` macros @@ -1892,6 +1898,15 @@ pub mod pallet_macros { /// /// The macro also implements `From>` for `&'static str` and `From>` for /// `DispatchError`. + /// + /// ## Note on deprecation of Errors + /// + /// - Usage of `deprecated` attribute will propagate deprecation information to the pallet + /// metadata where the item was declared. + /// - For general usage examples of `deprecated` attribute please refer to + /// - It's possible to deprecated either certain variants inside the `Error` or the whole + /// `Error` itself. If both the `Error` and its variants are deprecated a compile error + /// will be returned. pub use frame_support_procedural::error; /// Allows defining pallet events. @@ -1933,6 +1948,15 @@ pub mod pallet_macros { /// 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`]. + /// + /// ## Note on deprecation of Events + /// + /// - Usage of `deprecated` attribute will propagate deprecation information to the pallet + /// metadata where the item was declared. + /// - For general usage examples of `deprecated` attribute please refer to + /// - It's possible to deprecated either certain variants inside the `Event` or the whole + /// `Event` itself. If both the `Event` and its variants are deprecated a compile error + /// will be returned. pub use frame_support_procedural::event; /// Allows a pallet to declare a set of functions as a *dispatchable extrinsic*. @@ -2053,6 +2077,12 @@ pub mod pallet_macros { /// pub trait Config: frame_system::Config {} /// } /// ``` + /// + /// ## Note on deprecation of Calls + /// + /// - Usage of `deprecated` attribute will propagate deprecation information to the pallet + /// metadata where the item was declared. + /// - For general usage examples of `deprecated` attribute please refer to pub use frame_support_procedural::call; /// Enforce the index of a variant in the generated `enum Call`. @@ -2185,6 +2215,12 @@ pub mod pallet_macros { /// } /// } /// ``` + /// + /// ## Note on deprecation of constants + /// + /// - Usage of `deprecated` attribute will propagate deprecation information to the pallet + /// metadata where the item was declared. + /// - For general usage examples of `deprecated` attribute please refer to pub use frame_support_procedural::constant; /// Declares a type alias as a storage item. @@ -2403,6 +2439,12 @@ pub mod pallet_macros { /// pub type Foo = StorageValue<_, u32, ValueQuery>; /// } /// ``` + /// + /// ## Note on deprecation of storage items + /// + /// - Usage of `deprecated` attribute will propagate deprecation information to the pallet + /// metadata where the storage item was declared. + /// - For general usage examples of `deprecated` attribute please refer to pub use frame_support_procedural::storage; pub use frame_support_procedural::{ @@ -2481,6 +2523,8 @@ pub use frame_support_procedural::register_default_impl; sp_core::generate_feature_enabled_macro!(std_enabled, feature = "std", $); // Generate a macro that will enable/disable code based on `try-runtime` feature being active. sp_core::generate_feature_enabled_macro!(try_runtime_enabled, feature = "try-runtime", $); +sp_core::generate_feature_enabled_macro!(try_runtime_or_std_enabled, any(feature = "try-runtime", feature = "std"), $); +sp_core::generate_feature_enabled_macro!(try_runtime_and_std_not_enabled, all(not(feature = "try-runtime"), not(feature = "std")), $); // Helper for implementing GenesisBuilder runtime API pub mod genesis_builder_helper; diff --git a/substrate/frame/support/src/migrations.rs b/substrate/frame/support/src/migrations.rs index 7f74614695640dfd07771f689bb93971199baf10..905d6143e4f1c9ba97c73963fd97b165e51adb06 100644 --- a/substrate/frame/support/src/migrations.rs +++ b/substrate/frame/support/src/migrations.rs @@ -529,6 +529,25 @@ pub trait SteppedMigration { }) .map_err(|()| SteppedMigrationError::Failed)? } + + /// Hook for testing that is run before the migration is started. + /// + /// Returns some bytes which are passed into `post_upgrade` after the migration is completed. + /// This is not run for the real migration, so panicking is not an issue here. + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { + Ok(Vec::new()) + } + + /// Hook for testing that is run after the migration is completed. + /// + /// Should be used to verify the state of the chain after the migration. The `state` parameter + /// is the return value from `pre_upgrade`. This is not run for the real migration, so panicking + /// is not an issue here. + #[cfg(feature = "try-runtime")] + fn post_upgrade(_state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + Ok(()) + } } /// Error that can occur during a [`SteppedMigration`]. @@ -673,7 +692,8 @@ pub trait SteppedMigrations { /// The `n`th [`SteppedMigration::id`]. /// - /// Is guaranteed to return `Some` if `n < Self::len()`. + /// Is guaranteed to return `Some` if `n < Self::len()`. Calling this with any index larger or + /// equal to `Self::len()` MUST return `None`. fn nth_id(n: u32) -> Option>; /// The [`SteppedMigration::max_steps`] of the `n`th migration. @@ -699,6 +719,19 @@ pub trait SteppedMigrations { meter: &mut WeightMeter, ) -> Option>, SteppedMigrationError>>; + /// Call the pre-upgrade hooks of the `n`th migration. + /// + /// Returns `None` if the index is out of bounds. + #[cfg(feature = "try-runtime")] + fn nth_pre_upgrade(n: u32) -> Option, sp_runtime::TryRuntimeError>>; + + /// Call the post-upgrade hooks of the `n`th migration. + /// + /// Returns `None` if the index is out of bounds. + #[cfg(feature = "try-runtime")] + fn nth_post_upgrade(n: u32, _state: Vec) + -> Option>; + /// The maximal encoded length across all cursors. fn cursor_max_encoded_len() -> usize; @@ -762,6 +795,19 @@ impl SteppedMigrations for () { None } + #[cfg(feature = "try-runtime")] + fn nth_pre_upgrade(_n: u32) -> Option, sp_runtime::TryRuntimeError>> { + Some(Ok(Vec::new())) + } + + #[cfg(feature = "try-runtime")] + fn nth_post_upgrade( + _n: u32, + _state: Vec, + ) -> Option> { + Some(Ok(())) + } + fn cursor_max_encoded_len() -> usize { 0 } @@ -777,8 +823,10 @@ impl SteppedMigrations for T { 1 } - fn nth_id(_n: u32) -> Option> { - Some(T::id().encode()) + fn nth_id(n: u32) -> Option> { + n.is_zero() + .then_some(T::id().encode()) + .defensive_proof("nth_id should only be called with n==0") } fn nth_max_steps(n: u32) -> Option> { @@ -789,11 +837,11 @@ impl SteppedMigrations for T { } fn nth_step( - _n: u32, + n: u32, cursor: Option>, meter: &mut WeightMeter, ) -> Option>, SteppedMigrationError>> { - if !_n.is_zero() { + if !n.is_zero() { defensive!("nth_step should only be called with n==0"); return None } @@ -832,6 +880,23 @@ impl SteppedMigrations for T { ) } + #[cfg(feature = "try-runtime")] + fn nth_pre_upgrade(n: u32) -> Option, sp_runtime::TryRuntimeError>> { + if n != 0 { + defensive!("nth_pre_upgrade should only be called with n==0"); + } + + Some(T::pre_upgrade()) + } + + #[cfg(feature = "try-runtime")] + fn nth_post_upgrade(n: u32, state: Vec) -> Option> { + if n != 0 { + defensive!("nth_post_upgrade should only be called with n==0"); + } + Some(T::post_upgrade(state)) + } + fn cursor_max_encoded_len() -> usize { T::Cursor::max_encoded_len() } @@ -897,6 +962,36 @@ impl SteppedMigrations for Tuple { None } + #[cfg(feature = "try-runtime")] + fn nth_pre_upgrade(n: u32) -> Option, sp_runtime::TryRuntimeError>> { + let mut i = 0; + + for_tuples! ( #( + if (i + Tuple::len()) > n { + return Tuple::nth_pre_upgrade(n - i) + } + + i += Tuple::len(); + )* ); + + None + } + + #[cfg(feature = "try-runtime")] + fn nth_post_upgrade(n: u32, state: Vec) -> Option> { + let mut i = 0; + + for_tuples! ( #( + if (i + Tuple::len()) > n { + return Tuple::nth_post_upgrade(n - i, state) + } + + i += Tuple::len(); + )* ); + + None + } + fn nth_max_steps(n: u32) -> Option> { let mut i = 0; diff --git a/substrate/frame/support/src/storage/generator/double_map.rs b/substrate/frame/support/src/storage/generator/double_map.rs index b68f3fa495ff9a63cf4e17ec2f9e0e263257e2ed..a9116f1f66bd98ed23e1c4b938155ad9b574f2dd 100644 --- a/substrate/frame/support/src/storage/generator/double_map.rs +++ b/substrate/frame/support/src/storage/generator/double_map.rs @@ -346,9 +346,8 @@ where final_key }; - unhashed::take(old_key.as_ref()).map(|value| { + unhashed::take(old_key.as_ref()).inspect(|value| { unhashed::put(Self::storage_double_map_final_key(key1, key2).as_ref(), &value); - value }) } } diff --git a/substrate/frame/support/src/storage/generator/map.rs b/substrate/frame/support/src/storage/generator/map.rs index 7a74308fdeac16ac208f67b5208dadd7406e29fa..2d1f6c9f73a29636194f55c6f4159e7592150ba3 100644 --- a/substrate/frame/support/src/storage/generator/map.rs +++ b/substrate/frame/support/src/storage/generator/map.rs @@ -136,7 +136,7 @@ where loop { previous_key = Self::translate_next(previous_key, &mut f); if previous_key.is_none() { - break + break; } } } @@ -158,7 +158,7 @@ where "Invalid translation: failed to decode old value for key", array_bytes::bytes2hex("0x", ¤t_key) ); - return Some(current_key) + return Some(current_key); }, }; @@ -170,7 +170,7 @@ where "Invalid translation: failed to decode key", array_bytes::bytes2hex("0x", ¤t_key) ); - return Some(current_key) + return Some(current_key); }, }; @@ -311,9 +311,8 @@ impl> storage::StorageMap final_key }; - unhashed::take(old_key.as_ref()).map(|value| { + unhashed::take(old_key.as_ref()).inspect(|value| { unhashed::put(Self::storage_map_final_key(key).as_ref(), &value); - value }) } } diff --git a/substrate/frame/support/src/storage/generator/nmap.rs b/substrate/frame/support/src/storage/generator/nmap.rs index 0466583a27955665090c33e6527caa9182abf90f..9083aba9d32c0006e2a69a3c348c7824fb5f76a8 100755 --- a/substrate/frame/support/src/storage/generator/nmap.rs +++ b/substrate/frame/support/src/storage/generator/nmap.rs @@ -305,9 +305,8 @@ where final_key }; - unhashed::take(old_key.as_ref()).map(|value| { + unhashed::take(old_key.as_ref()).inspect(|value| { unhashed::put(Self::storage_n_map_final_key::(key).as_ref(), &value); - value }) } } diff --git a/substrate/frame/support/src/storage/mod.rs b/substrate/frame/support/src/storage/mod.rs index 7fb991d377923a936abf808ca07627ca9fbead3f..6193925630359c1a51ac4a9a15f81b8a4a0609f2 100644 --- a/substrate/frame/support/src/storage/mod.rs +++ b/substrate/frame/support/src/storage/mod.rs @@ -1693,6 +1693,46 @@ where } } +/// Storage N map that is capable of [`StorageTryAppend`]. +pub trait TryAppendNMap, I: Encode> { + /// Try and append the `item` into the storage N map at the given `key`. + /// + /// This might fail if bounds are not respected. + fn try_append< + LikeK: EncodeLikeTuple + TupleToEncodedIter + Clone, + LikeI: EncodeLike, + >( + key: LikeK, + item: LikeI, + ) -> Result<(), ()>; +} + +impl TryAppendNMap for StorageNMapT +where + K: KeyGenerator, + T: FullCodec + StorageTryAppend, + I: Encode, + StorageNMapT: generator::StorageNMap, +{ + fn try_append< + LikeK: EncodeLikeTuple + TupleToEncodedIter + Clone, + LikeI: EncodeLike, + >( + key: LikeK, + item: LikeI, + ) -> Result<(), ()> { + let bound = T::bound(); + let current = Self::decode_len(key.clone()).unwrap_or_default(); + if current < bound { + let key = Self::storage_n_map_final_key::(key); + sp_io::storage::append(&key, item.encode()); + Ok(()) + } else { + Err(()) + } + } +} + /// Returns the storage prefix for a specific pallet name and storage name. /// /// The storage prefix is `concat(twox_128(pallet_name), twox_128(storage_name))`. @@ -2019,6 +2059,17 @@ mod test { (NMapKey, NMapKey, NMapKey), u64, >; + #[crate::storage_alias] + type FooQuadMap = StorageNMap< + Prefix, + ( + NMapKey, + NMapKey, + NMapKey, + NMapKey, + ), + BoundedVec>, + >; #[test] fn contains_prefix_works() { @@ -2109,6 +2160,31 @@ mod test { BoundedVec::>::try_from(vec![4, 5]).unwrap(), ); }); + + TestExternalities::default().execute_with(|| { + let bounded: BoundedVec> = vec![1, 2, 3].try_into().unwrap(); + FooQuadMap::insert((1, 1, 1, 1), bounded); + + assert_ok!(FooQuadMap::try_append((1, 1, 1, 1), 4)); + assert_ok!(FooQuadMap::try_append((1, 1, 1, 1), 5)); + assert_ok!(FooQuadMap::try_append((1, 1, 1, 1), 6)); + assert_ok!(FooQuadMap::try_append((1, 1, 1, 1), 7)); + assert_eq!(FooQuadMap::decode_len((1, 1, 1, 1)).unwrap(), 7); + assert!(FooQuadMap::try_append((1, 1, 1, 1), 8).is_err()); + + // append to a non-existing + assert!(FooQuadMap::get((2, 1, 1, 1)).is_none()); + assert_ok!(FooQuadMap::try_append((2, 1, 1, 1), 4)); + assert_eq!( + FooQuadMap::get((2, 1, 1, 1)).unwrap(), + BoundedVec::>::try_from(vec![4]).unwrap(), + ); + assert_ok!(FooQuadMap::try_append((2, 1, 1, 1), 5)); + assert_eq!( + FooQuadMap::get((2, 1, 1, 1)).unwrap(), + BoundedVec::>::try_from(vec![4, 5]).unwrap(), + ); + }); } #[crate::storage_alias] diff --git a/substrate/frame/support/src/storage/types/counted_map.rs b/substrate/frame/support/src/storage/types/counted_map.rs index 9adcb33ae07434010b3a63fa09112ab71d69f17b..ae155eab5dd77423e38ce1a1a8be2c2eb47c9a1a 100644 --- a/substrate/frame/support/src/storage/types/counted_map.rs +++ b/substrate/frame/support/src/storage/types/counted_map.rs @@ -508,9 +508,14 @@ where OnEmpty: Get + 'static, MaxValues: Get>, { - fn build_metadata(docs: Vec<&'static str>, entries: &mut Vec) { - ::Map::build_metadata(docs, entries); + fn build_metadata( + deprecation_status: sp_metadata_ir::DeprecationStatusIR, + docs: Vec<&'static str>, + entries: &mut Vec, + ) { + ::Map::build_metadata(deprecation_status.clone(), docs, entries); CounterFor::::build_metadata( + deprecation_status, if cfg!(feature = "no-metadata-docs") { vec![] } else { @@ -1193,7 +1198,7 @@ mod test { fn test_metadata() { type A = CountedStorageMap; let mut entries = vec![]; - A::build_metadata(vec![], &mut entries); + A::build_metadata(sp_metadata_ir::DeprecationStatusIR::NotDeprecated, vec![], &mut entries); assert_eq!( entries, vec![ @@ -1207,6 +1212,7 @@ mod test { }, default: 97u32.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "counter_for_foo", @@ -1218,6 +1224,7 @@ mod test { } else { vec!["Counter for the related counted storage map"] }, + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, ] ); diff --git a/substrate/frame/support/src/storage/types/counted_nmap.rs b/substrate/frame/support/src/storage/types/counted_nmap.rs index 13c1b10be39c47dfe4792b9f91cf84944f2867ae..45bd82ffe7e5ed9fcf49422a1ef62763ea76d50d 100644 --- a/substrate/frame/support/src/storage/types/counted_nmap.rs +++ b/substrate/frame/support/src/storage/types/counted_nmap.rs @@ -632,9 +632,14 @@ where OnEmpty: Get + 'static, MaxValues: Get>, { - fn build_metadata(docs: Vec<&'static str>, entries: &mut Vec) { - ::Map::build_metadata(docs, entries); + fn build_metadata( + deprecation_status: sp_metadata_ir::DeprecationStatusIR, + docs: Vec<&'static str>, + entries: &mut Vec, + ) { + ::Map::build_metadata(deprecation_status.clone(), docs, entries); CounterFor::::build_metadata( + deprecation_status, vec![&"Counter for the related counted storage map"], entries, ); @@ -859,8 +864,16 @@ mod test { assert_eq!(A::count(), 2); let mut entries = vec![]; - A::build_metadata(vec![], &mut entries); - AValueQueryWithAnOnEmpty::build_metadata(vec![], &mut entries); + A::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); + AValueQueryWithAnOnEmpty::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); assert_eq!( entries, vec![ @@ -874,6 +887,7 @@ mod test { }, default: Option::::None.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Foo", @@ -885,6 +899,7 @@ mod test { } else { vec!["Counter for the related counted storage map"] }, + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Foo", @@ -896,6 +911,7 @@ mod test { }, default: 98u32.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Foo", @@ -907,6 +923,7 @@ mod test { } else { vec!["Counter for the related counted storage map"] }, + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, ] ); @@ -1108,8 +1125,16 @@ mod test { assert_eq!(A::count(), 2); let mut entries = vec![]; - A::build_metadata(vec![], &mut entries); - AValueQueryWithAnOnEmpty::build_metadata(vec![], &mut entries); + A::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); + AValueQueryWithAnOnEmpty::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); assert_eq!( entries, vec![ @@ -1126,6 +1151,7 @@ mod test { }, default: Option::::None.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Foo", @@ -1137,6 +1163,7 @@ mod test { } else { vec!["Counter for the related counted storage map"] }, + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Foo", @@ -1151,6 +1178,7 @@ mod test { }, default: 98u32.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Foo", @@ -1162,6 +1190,7 @@ mod test { } else { vec!["Counter for the related counted storage map"] }, + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, ] ); @@ -1394,8 +1423,16 @@ mod test { assert_eq!(A::count(), 2); let mut entries = vec![]; - A::build_metadata(vec![], &mut entries); - AValueQueryWithAnOnEmpty::build_metadata(vec![], &mut entries); + A::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); + AValueQueryWithAnOnEmpty::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); assert_eq!( entries, vec![ @@ -1413,6 +1450,7 @@ mod test { }, default: Option::::None.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Foo", @@ -1424,6 +1462,7 @@ mod test { } else { vec!["Counter for the related counted storage map"] }, + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Foo", @@ -1439,6 +1478,7 @@ mod test { }, default: 98u32.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Foo", @@ -1450,6 +1490,7 @@ mod test { } else { vec!["Counter for the related counted storage map"] }, + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, ] ); diff --git a/substrate/frame/support/src/storage/types/double_map.rs b/substrate/frame/support/src/storage/types/double_map.rs index 3d227feb902f304f8596f20bc288487e272316cc..24aad3de0b3316bbf85ffc617e8dc8d16091539c 100644 --- a/substrate/frame/support/src/storage/types/double_map.rs +++ b/substrate/frame/support/src/storage/types/double_map.rs @@ -129,7 +129,8 @@ impl OnEmpty, MaxValues, >, - > where + > +where Prefix: StorageInstance, Hasher1: crate::hash::StorageHasher, Hasher2: crate::hash::StorageHasher, @@ -732,7 +733,11 @@ where OnEmpty: Get + 'static, MaxValues: Get>, { - fn build_metadata(docs: Vec<&'static str>, entries: &mut Vec) { + fn build_metadata( + deprecation_status: sp_metadata_ir::DeprecationStatusIR, + docs: Vec<&'static str>, + entries: &mut Vec, + ) { let docs = if cfg!(feature = "no-metadata-docs") { vec![] } else { docs }; let entry = StorageEntryMetadataIR { @@ -745,6 +750,7 @@ where }, default: OnEmpty::get().encode(), docs, + deprecation_info: deprecation_status, }; entries.push(entry); @@ -985,8 +991,16 @@ mod test { assert_eq!(A::iter().collect::>(), vec![(4, 40, 1600), (3, 30, 900)]); let mut entries = vec![]; - A::build_metadata(vec![], &mut entries); - AValueQueryWithAnOnEmpty::build_metadata(vec![], &mut entries); + A::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); + AValueQueryWithAnOnEmpty::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); assert_eq!( entries, vec![ @@ -1003,6 +1017,7 @@ mod test { }, default: Option::::None.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated }, StorageEntryMetadataIR { name: "foo", @@ -1017,6 +1032,7 @@ mod test { }, default: 97u32.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated } ] ); diff --git a/substrate/frame/support/src/storage/types/map.rs b/substrate/frame/support/src/storage/types/map.rs index b70026eea50e130cb2fd7c7dc743aec744b8e5d8..3582448c7dd530f9fcdecff106155d594d79aa3a 100644 --- a/substrate/frame/support/src/storage/types/map.rs +++ b/substrate/frame/support/src/storage/types/map.rs @@ -490,7 +490,11 @@ where OnEmpty: Get + 'static, MaxValues: Get>, { - fn build_metadata(docs: Vec<&'static str>, entries: &mut Vec) { + fn build_metadata( + deprecation_status: sp_metadata_ir::DeprecationStatusIR, + docs: Vec<&'static str>, + entries: &mut Vec, + ) { let docs = if cfg!(feature = "no-metadata-docs") { vec![] } else { docs }; let entry = StorageEntryMetadataIR { @@ -503,6 +507,7 @@ where }, default: OnEmpty::get().encode(), docs, + deprecation_info: deprecation_status, }; entries.push(entry); @@ -791,8 +796,16 @@ mod test { assert_eq!(A::iter().collect::>(), vec![(3, 10)]); let mut entries = vec![]; - A::build_metadata(vec![], &mut entries); - AValueQueryWithAnOnEmpty::build_metadata(vec![], &mut entries); + A::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); + AValueQueryWithAnOnEmpty::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); assert_eq!( entries, vec![ @@ -806,6 +819,7 @@ mod test { }, default: Option::::None.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated }, StorageEntryMetadataIR { name: "foo", @@ -817,6 +831,7 @@ mod test { }, default: 97u32.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated } ] ); diff --git a/substrate/frame/support/src/storage/types/mod.rs b/substrate/frame/support/src/storage/types/mod.rs index b063e11621d61a7481c5a3dd409321bd76e33597..b1c8a58fc8fbf29e5f560e983a175c87a4fb1221 100644 --- a/substrate/frame/support/src/storage/types/mod.rs +++ b/substrate/frame/support/src/storage/types/mod.rs @@ -141,7 +141,11 @@ where /// Implemented by each of the storage types: value, map, countedmap, doublemap and nmap. pub trait StorageEntryMetadataBuilder { /// Build into `entries` the storage metadata entries of a storage given some `docs`. - fn build_metadata(doc: Vec<&'static str>, entries: &mut Vec); + fn build_metadata( + deprecation_status: sp_metadata_ir::DeprecationStatusIR, + doc: Vec<&'static str>, + entries: &mut Vec, + ); } #[cfg(test)] diff --git a/substrate/frame/support/src/storage/types/nmap.rs b/substrate/frame/support/src/storage/types/nmap.rs index c3dfd5b3e48c9dc742499e5b9c7de032d7066d72..0fc22b35352d163f51e6bd2c2b25a52db4bf7add 100755 --- a/substrate/frame/support/src/storage/types/nmap.rs +++ b/substrate/frame/support/src/storage/types/nmap.rs @@ -25,6 +25,7 @@ use crate::{ StorageEntryMetadataBuilder, TupleToEncodedIter, }, KeyGenerator, PrefixIterator, StorageAppend, StorageDecodeLength, StoragePrefixedMap, + StorageTryAppend, }, traits::{Get, GetDefault, StorageInfo, StorageInstance}, }; @@ -338,6 +339,19 @@ where >::append(key, item) } + /// Try and append the given item to the value in the storage. + /// + /// Is only available if `Value` of the storage implements [`StorageTryAppend`]. + pub fn try_append(key: KArg, item: EncodeLikeItem) -> Result<(), ()> + where + KArg: EncodeLikeTuple + TupleToEncodedIter + Clone, + Item: Encode, + EncodeLikeItem: EncodeLike, + Value: StorageTryAppend, + { + >::try_append(key, item) + } + /// Read the length of the storage value without decoding the entire value under the /// given `key1` and `key2`. /// @@ -583,7 +597,11 @@ where OnEmpty: Get + 'static, MaxValues: Get>, { - fn build_metadata(docs: Vec<&'static str>, entries: &mut Vec) { + fn build_metadata( + deprecation_status: sp_metadata_ir::DeprecationStatusIR, + docs: Vec<&'static str>, + entries: &mut Vec, + ) { let docs = if cfg!(feature = "no-metadata-docs") { vec![] } else { docs }; let entry = StorageEntryMetadataIR { @@ -596,6 +614,7 @@ where }, default: OnEmpty::get().encode(), docs, + deprecation_info: deprecation_status, }; entries.push(entry); @@ -820,8 +839,16 @@ mod test { assert_eq!(A::iter().collect::>(), vec![(4, 40), (3, 30)]); let mut entries = vec![]; - A::build_metadata(vec![], &mut entries); - AValueQueryWithAnOnEmpty::build_metadata(vec![], &mut entries); + A::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); + AValueQueryWithAnOnEmpty::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); assert_eq!( entries, vec![ @@ -835,6 +862,7 @@ mod test { }, default: Option::::None.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Foo", @@ -846,6 +874,7 @@ mod test { }, default: 98u32.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, } ] ); @@ -1020,8 +1049,16 @@ mod test { assert_eq!(A::iter().collect::>(), vec![((4, 40), 1600), ((3, 30), 900)]); let mut entries = vec![]; - A::build_metadata(vec![], &mut entries); - AValueQueryWithAnOnEmpty::build_metadata(vec![], &mut entries); + A::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); + AValueQueryWithAnOnEmpty::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); assert_eq!( entries, vec![ @@ -1038,6 +1075,7 @@ mod test { }, default: Option::::None.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Foo", @@ -1052,6 +1090,7 @@ mod test { }, default: 98u32.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, } ] ); @@ -1261,8 +1300,16 @@ mod test { assert_eq!(A::iter().collect::>(), vec![((4, 40, 400), 4), ((3, 30, 300), 3)]); let mut entries = vec![]; - A::build_metadata(vec![], &mut entries); - AValueQueryWithAnOnEmpty::build_metadata(vec![], &mut entries); + A::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); + AValueQueryWithAnOnEmpty::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); assert_eq!( entries, vec![ @@ -1280,6 +1327,7 @@ mod test { }, default: Option::::None.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Foo", @@ -1295,6 +1343,7 @@ mod test { }, default: 98u32.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, } ] ); diff --git a/substrate/frame/support/src/storage/types/value.rs b/substrate/frame/support/src/storage/types/value.rs index 9cc985b36d8c60aa1969286912ca46c6ae446827..69a8f3bb4b8c65679a8d02c1e1630561353856c0 100644 --- a/substrate/frame/support/src/storage/types/value.rs +++ b/substrate/frame/support/src/storage/types/value.rs @@ -277,7 +277,11 @@ where QueryKind: QueryKindTrait, OnEmpty: crate::traits::Get + 'static, { - fn build_metadata(docs: Vec<&'static str>, entries: &mut Vec) { + fn build_metadata( + deprecation_status: sp_metadata_ir::DeprecationStatusIR, + docs: Vec<&'static str>, + entries: &mut Vec, + ) { let docs = if cfg!(feature = "no-metadata-docs") { vec![] } else { docs }; let entry = StorageEntryMetadataIR { @@ -286,6 +290,7 @@ where ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), default: OnEmpty::get().encode(), docs, + deprecation_info: deprecation_status, }; entries.push(entry); @@ -415,8 +420,16 @@ mod test { assert_eq!(A::try_get(), Err(())); let mut entries = vec![]; - A::build_metadata(vec![], &mut entries); - AValueQueryWithAnOnEmpty::build_metadata(vec![], &mut entries); + A::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); + AValueQueryWithAnOnEmpty::build_metadata( + sp_metadata_ir::DeprecationStatusIR::NotDeprecated, + vec![], + &mut entries, + ); assert_eq!( entries, vec![ @@ -426,6 +439,7 @@ mod test { ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), default: Option::::None.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated }, StorageEntryMetadataIR { name: "foo", @@ -433,6 +447,7 @@ mod test { ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), default: 97u32.encode(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated } ] ); diff --git a/substrate/frame/support/src/tests/mod.rs b/substrate/frame/support/src/tests/mod.rs index a9f8ebdd63f21e869abb7a25702f8842787eac74..7c90a12d4167e376841cb0c5a683d56d36559c5c 100644 --- a/substrate/frame/support/src/tests/mod.rs +++ b/substrate/frame/support/src/tests/mod.rs @@ -14,6 +14,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +#![allow(deprecated, clippy::deprecated_semver)] use super::*; use sp_io::{MultiRemovalResults, TestExternalities}; @@ -74,6 +75,10 @@ pub mod frame_system { #[pallet::no_default_bounds] type PalletInfo: crate::traits::PalletInfo; type DbWeight: Get; + #[pallet::constant] + #[pallet::no_default] + #[deprecated = "this constant is deprecated"] + type ExampleConstant: Get<()>; } #[pallet::error] @@ -97,11 +102,11 @@ pub mod frame_system { #[pallet::weight(task.weight())] pub fn do_task(_origin: OriginFor, task: T::RuntimeTask) -> DispatchResultWithPostInfo { if !task.is_valid() { - return Err(Error::::InvalidTask.into()) + return Err(Error::::InvalidTask.into()); } if let Err(_err) = task.run() { - return Err(Error::::FailedTask.into()) + return Err(Error::::FailedTask.into()); } Ok(().into()) @@ -109,18 +114,22 @@ pub mod frame_system { } #[pallet::storage] + #[deprecated] pub type Data = StorageMap<_, Twox64Concat, u32, u64, ValueQuery>; #[pallet::storage] + #[deprecated(note = "test")] pub type OptionLinkedMap = StorageMap<_, Blake2_128Concat, u32, u32, OptionQuery>; #[pallet::storage] #[pallet::getter(fn generic_data)] + #[deprecated(note = "test", since = "test")] pub type GenericData = StorageMap<_, Identity, BlockNumberFor, BlockNumberFor, ValueQuery>; #[pallet::storage] #[pallet::getter(fn generic_data2)] + #[deprecated = "test"] pub type GenericData2 = StorageMap<_, Blake2_128Concat, BlockNumberFor, BlockNumberFor, OptionQuery>; @@ -240,6 +249,7 @@ mod runtime { impl Config for Runtime { type Block = Block; type AccountId = AccountId; + type ExampleConstant = (); } fn new_test_ext() -> TestExternalities { @@ -585,6 +595,7 @@ fn expected_metadata() -> PalletStorageMetadataIR { }, default: vec![0, 0, 0, 0, 0, 0, 0, 0], docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::DeprecatedWithoutNote, }, StorageEntryMetadataIR { name: "OptionLinkedMap", @@ -596,6 +607,10 @@ fn expected_metadata() -> PalletStorageMetadataIR { }, default: vec![0], docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::Deprecated { + note: "test", + since: None, + }, }, StorageEntryMetadataIR { name: "GenericData", @@ -607,6 +622,10 @@ fn expected_metadata() -> PalletStorageMetadataIR { }, default: vec![0, 0, 0, 0], docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::Deprecated { + note: "test", + since: Some("test"), + }, }, StorageEntryMetadataIR { name: "GenericData2", @@ -618,6 +637,10 @@ fn expected_metadata() -> PalletStorageMetadataIR { }, default: vec![0], docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::Deprecated { + note: "test", + since: None, + }, }, StorageEntryMetadataIR { name: "DataDM", @@ -629,6 +652,7 @@ fn expected_metadata() -> PalletStorageMetadataIR { }, default: vec![0, 0, 0, 0, 0, 0, 0, 0], docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "GenericDataDM", @@ -640,6 +664,7 @@ fn expected_metadata() -> PalletStorageMetadataIR { }, default: vec![0, 0, 0, 0], docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "GenericData2DM", @@ -651,6 +676,7 @@ fn expected_metadata() -> PalletStorageMetadataIR { }, default: vec![0], docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "AppendableDM", @@ -665,6 +691,7 @@ fn expected_metadata() -> PalletStorageMetadataIR { }, default: vec![0], docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Total", @@ -672,6 +699,7 @@ fn expected_metadata() -> PalletStorageMetadataIR { ty: StorageEntryTypeIR::Plain(scale_info::meta_type::<(u32, u32)>()), default: vec![0, 0, 0, 0, 0, 0, 0, 0], docs: vec![" Some running total."], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Numbers", @@ -683,6 +711,7 @@ fn expected_metadata() -> PalletStorageMetadataIR { }, default: vec![0], docs: vec![" Numbers to be added into the total."], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, ], } @@ -694,6 +723,25 @@ fn store_metadata() { pretty_assertions::assert_eq!(expected_metadata(), metadata); } +#[test] +fn constant_metadata() { + let metadata: Vec = + Pallet::::pallet_constants_metadata(); + pretty_assertions::assert_eq!( + metadata, + vec![sp_metadata_ir::PalletConstantMetadataIR { + name: "ExampleConstant", + ty: scale_info::meta_type::<()>(), + value: vec![], + docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::Deprecated { + note: "this constant is deprecated", + since: None + } + },] + ); +} + parameter_types! { storage StorageParameter: u64 = 10; } @@ -721,5 +769,6 @@ fn derive_partial_eq_no_bound_core_mod() { crate::DefaultNoBound, crate::EqNoBound, )] + #[allow(dead_code)] struct Test; } diff --git a/substrate/frame/support/src/traits.rs b/substrate/frame/support/src/traits.rs index f635ed32a12483ce88080f5a2b68df22c7a0fda3..631225b9a327c6ff467db9815b9539215ad39ca7 100644 --- a/substrate/frame/support/src/traits.rs +++ b/substrate/frame/support/src/traits.rs @@ -23,7 +23,8 @@ pub mod tokens; pub use tokens::{ currency::{ ActiveIssuanceOf, Currency, InspectLockableCurrency, LockIdentifier, LockableCurrency, - NamedReservableCurrency, ReservableCurrency, TotalIssuanceOf, VestingSchedule, + NamedReservableCurrency, ReservableCurrency, TotalIssuanceOf, VestedTransfer, + VestingSchedule, }, fungible, fungibles, imbalance::{Imbalance, OnUnbalanced, SignedImbalance}, @@ -132,6 +133,9 @@ pub mod dynamic_params; pub mod tasks; pub use tasks::Task; +mod proving; +pub use proving::*; + #[cfg(feature = "try-runtime")] mod try_runtime; #[cfg(feature = "try-runtime")] diff --git a/substrate/frame/support/src/traits/misc.rs b/substrate/frame/support/src/traits/misc.rs index 492475d6f63c66fc4ac55eb9a67e8ec534db3df5..4d3b122daf67bf78e775c6ab54e6b6d91be5a3ed 100644 --- a/substrate/frame/support/src/traits/misc.rs +++ b/substrate/frame/support/src/traits/misc.rs @@ -488,7 +488,7 @@ pub trait DefensiveMin { /// assert_eq!(4, 4_u32.defensive_min(4_u32)); /// ``` /// - /// ```#[cfg_attr(debug_assertions, should_panic)] + /// ```should_panic /// use frame_support::traits::DefensiveMin; /// // min(4, 3) panics. /// 4_u32.defensive_min(3_u32); @@ -505,7 +505,7 @@ pub trait DefensiveMin { /// assert_eq!(3, 3_u32.defensive_strict_min(4_u32)); /// ``` /// - /// ```#[cfg_attr(debug_assertions, should_panic)] + /// ```should_panic /// use frame_support::traits::DefensiveMin; /// // min(4, 4) panics. /// 4_u32.defensive_strict_min(4_u32); @@ -552,7 +552,7 @@ pub trait DefensiveMax { /// assert_eq!(4, 4_u32.defensive_max(4_u32)); /// ``` /// - /// ```#[cfg_attr(debug_assertions, should_panic)] + /// ```should_panic /// use frame_support::traits::DefensiveMax; /// // max(4, 5) panics. /// 4_u32.defensive_max(5_u32); @@ -569,7 +569,7 @@ pub trait DefensiveMax { /// assert_eq!(4, 4_u32.defensive_strict_max(3_u32)); /// ``` /// - /// ```#[cfg_attr(debug_assertions, should_panic)] + /// ```should_panic /// use frame_support::traits::DefensiveMax; /// // max(4, 4) panics. /// 4_u32.defensive_strict_max(4_u32); diff --git a/substrate/frame/support/src/traits/proving.rs b/substrate/frame/support/src/traits/proving.rs new file mode 100644 index 0000000000000000000000000000000000000000..84e37bde38dbcac2fe6b052cb6b6254b2b299c1e --- /dev/null +++ b/substrate/frame/support/src/traits/proving.rs @@ -0,0 +1,229 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Provides functionality for verifying proofs. + +use alloc::vec::Vec; +use codec::{Decode, Encode}; +use sp_core::Hasher; +use sp_runtime::DispatchError; + +// Re-export the `proving_trie` types and traits. +pub use sp_runtime::proving_trie::*; + +/// Something that can verify the existence of some data in a given proof. +pub trait VerifyExistenceProof { + /// The proof type. + type Proof; + /// The hash type. + type Hash; + + /// Verify the given `proof`. + /// + /// Ensures that the `proof` was build for `root` and returns the proved data. + fn verify_proof(proof: Self::Proof, root: &Self::Hash) -> Result, DispatchError>; +} + +/// Implements [`VerifyExistenceProof`] using a binary merkle tree. +pub struct BinaryMerkleTreeProver(core::marker::PhantomData); + +impl VerifyExistenceProof for BinaryMerkleTreeProver +where + H::Out: Decode + Encode, +{ + type Proof = binary_merkle_tree::MerkleProof>; + type Hash = H::Out; + + fn verify_proof(proof: Self::Proof, root: &Self::Hash) -> Result, DispatchError> { + if proof.root != *root { + return Err(TrieError::RootMismatch.into()); + } + + if binary_merkle_tree::verify_proof::( + &proof.root, + proof.proof, + proof.number_of_leaves, + proof.leaf_index, + &proof.leaf, + ) { + Ok(proof.leaf) + } else { + Err(TrieError::IncompleteProof.into()) + } + } +} + +impl ProofToHashes for BinaryMerkleTreeProver { + type Proof = binary_merkle_tree::MerkleProof>; + + // This base 2 merkle trie includes a `proof` field which is a `Vec`. + // The length of this vector tells us the depth of the proof, and how many + // hashes we need to calculate. + fn proof_to_hashes(proof: &Self::Proof) -> Result { + let depth = proof.proof.len(); + Ok(depth as u32) + } +} + +/// Proof used by [`SixteenPatriciaMerkleTreeProver`] for [`VerifyExistenceProof`]. +#[derive(Encode, Decode, Clone)] +pub struct SixteenPatriciaMerkleTreeExistenceProof { + /// The key of the value to prove. + pub key: Vec, + /// The value for that the existence is proved. + pub value: Vec, + /// The encoded nodes to prove the existence of the data under `key`. + pub proof: Vec>, +} + +/// Implements [`VerifyExistenceProof`] using a 16-patricia merkle tree. +pub struct SixteenPatriciaMerkleTreeProver(core::marker::PhantomData); + +impl VerifyExistenceProof for SixteenPatriciaMerkleTreeProver { + type Proof = SixteenPatriciaMerkleTreeExistenceProof; + type Hash = H::Out; + + fn verify_proof(proof: Self::Proof, root: &Self::Hash) -> Result, DispatchError> { + sp_trie::verify_trie_proof::, _, _, _>( + &root, + &proof.proof, + [&(&proof.key, Some(&proof.value))], + ) + .map_err(|err| TrieError::from(err).into()) + .map(|_| proof.value) + } +} + +impl ProofToHashes for SixteenPatriciaMerkleTreeProver { + type Proof = SixteenPatriciaMerkleTreeExistenceProof; + + // This base 16 trie uses a raw proof of `Vec`, where the length of the first `Vec` + // is the depth of the trie. We can use this to predict the number of hashes. + fn proof_to_hashes(proof: &Self::Proof) -> Result { + let depth = proof.proof.len(); + Ok(depth as u32) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use sp_runtime::{ + proving_trie::{base16::BasicProvingTrie, ProvingTrie}, + traits::BlakeTwo256, + }; + + #[test] + fn verify_binary_merkle_tree_prover_works() { + let proof = binary_merkle_tree::merkle_proof::( + vec![b"hey".encode(), b"yes".encode()], + 1, + ); + let root = proof.root; + + assert_eq!( + BinaryMerkleTreeProver::::verify_proof(proof, &root).unwrap(), + b"yes".encode() + ); + } + + #[test] + fn verify_sixteen_patricia_merkle_tree_prover_works() { + let trie = BasicProvingTrie::::generate_for(vec![ + (0u32, String::from("hey")), + (1u32, String::from("yes")), + ]) + .unwrap(); + let proof = trie.create_proof(&1u32).unwrap(); + let structured_proof: Vec> = Decode::decode(&mut &proof[..]).unwrap(); + let root = *trie.root(); + + let proof = SixteenPatriciaMerkleTreeExistenceProof { + key: 1u32.encode(), + value: String::from("yes").encode(), + proof: structured_proof, + }; + + assert_eq!( + SixteenPatriciaMerkleTreeProver::::verify_proof(proof, &root).unwrap(), + String::from("yes").encode() + ); + } + + #[test] + fn proof_to_hashes_sixteen() { + let mut i: u32 = 1; + + // Compute log base 16 and round up + let log16 = |x: u32| -> u32 { + let x_f64 = x as f64; + let log16_x = (x_f64.ln() / 16_f64.ln()).ceil(); + log16_x as u32 + }; + + while i < 10_000_000 { + let trie = BasicProvingTrie::::generate_for( + (0..i).map(|i| (i, u128::from(i))), + ) + .unwrap(); + let proof = trie.create_proof(&0).unwrap(); + let structured_proof: Vec> = Decode::decode(&mut &proof[..]).unwrap(); + let root = *trie.root(); + + let proof = SixteenPatriciaMerkleTreeExistenceProof { + key: 0u32.encode(), + value: 0u128.encode(), + proof: structured_proof, + }; + let hashes = + SixteenPatriciaMerkleTreeProver::::proof_to_hashes(&proof).unwrap(); + let log16 = log16(i).max(1); + assert_eq!(hashes, log16); + + assert_eq!( + SixteenPatriciaMerkleTreeProver::::verify_proof(proof.clone(), &root) + .unwrap(), + proof.value + ); + + i = i * 10; + } + } + + #[test] + fn proof_to_hashes_binary() { + let mut i: u32 = 1; + while i < 10_000_000 { + let proof = binary_merkle_tree::merkle_proof::( + (0..i).map(|i| u128::from(i).encode()), + 0, + ); + let root = proof.root; + + let hashes = BinaryMerkleTreeProver::::proof_to_hashes(&proof).unwrap(); + let log2 = (i as f64).log2().ceil() as u32; + assert_eq!(hashes, log2); + + assert_eq!( + BinaryMerkleTreeProver::::verify_proof(proof, &root).unwrap(), + 0u128.encode() + ); + + i = i * 10; + } + } +} diff --git a/substrate/frame/support/src/traits/tokens/currency.rs b/substrate/frame/support/src/traits/tokens/currency.rs index b3db4c98001d900522b33c6a25e3e9b9119c3ea5..ea2c66a32cb04e0022d59163f5b4be6eef0faf57 100644 --- a/substrate/frame/support/src/traits/tokens/currency.rs +++ b/substrate/frame/support/src/traits/tokens/currency.rs @@ -30,7 +30,9 @@ use sp_runtime::{traits::MaybeSerializeDeserialize, DispatchError}; mod reservable; pub use reservable::{NamedReservableCurrency, ReservableCurrency}; mod lockable; -pub use lockable::{InspectLockableCurrency, LockIdentifier, LockableCurrency, VestingSchedule}; +pub use lockable::{ + InspectLockableCurrency, LockIdentifier, LockableCurrency, VestedTransfer, VestingSchedule, +}; /// Abstraction over a fungible assets system. pub trait Currency { diff --git a/substrate/frame/support/src/traits/tokens/currency/lockable.rs b/substrate/frame/support/src/traits/tokens/currency/lockable.rs index 51a48dd15ce85c8f41393164f42ca347d0114d1d..4ec45c908e688c132794f62173f6d93a1ab115c0 100644 --- a/substrate/frame/support/src/traits/tokens/currency/lockable.rs +++ b/substrate/frame/support/src/traits/tokens/currency/lockable.rs @@ -112,3 +112,56 @@ pub trait VestingSchedule { /// NOTE: This doesn't alter the free balance of the account. fn remove_vesting_schedule(who: &AccountId, schedule_index: u32) -> DispatchResult; } + +/// A vested transfer over a currency. This allows a transferred amount to vest over time. +pub trait VestedTransfer { + /// The quantity used to denote time; usually just a `BlockNumber`. + type Moment; + + /// The currency that this schedule applies to. + type Currency: Currency; + + /// Execute a vested transfer from `source` to `target` with the given schedule: + /// - `locked`: The amount to be transferred and for the vesting schedule to apply to. + /// - `per_block`: The amount to be unlocked each block. (linear vesting) + /// - `starting_block`: The block where the vesting should start. This block can be in the past + /// or future, and should adjust when the tokens become available to the user. + /// + /// Example: Assume we are on block 100. If `locked` amount is 100, and `per_block` is 1: + /// - If `starting_block` is 0, then the whole 100 tokens will be available right away as the + /// vesting schedule started in the past and has fully completed. + /// - If `starting_block` is 50, then 50 tokens are made available right away, and 50 more + /// tokens will unlock one token at a time until block 150. + /// - If `starting_block` is 100, then each block, 1 token will be unlocked until the whole + /// balance is unlocked at block 200. + /// - If `starting_block` is 200, then the 100 token balance will be completely locked until + /// block 200, and then start to unlock one token at a time until block 300. + fn vested_transfer( + source: &AccountId, + target: &AccountId, + locked: >::Balance, + per_block: >::Balance, + starting_block: Self::Moment, + ) -> DispatchResult; +} + +// An no-op implementation of `VestedTransfer` for pallets that require this trait, but users may +// not want to implement this functionality +pub struct NoVestedTransfers { + phantom: core::marker::PhantomData, +} + +impl> VestedTransfer for NoVestedTransfers { + type Moment = (); + type Currency = C; + + fn vested_transfer( + _source: &AccountId, + _target: &AccountId, + _locked: >::Balance, + _per_block: >::Balance, + _starting_block: Self::Moment, + ) -> DispatchResult { + Err(sp_runtime::DispatchError::Unavailable.into()) + } +} diff --git a/substrate/frame/support/src/traits/tokens/fungible/hold.rs b/substrate/frame/support/src/traits/tokens/fungible/hold.rs index 28ece25c91d427146354e3ad91257e01cd9c4cb5..6737cfe707ac33bc907a9eaeea2104f75fd6e56a 100644 --- a/substrate/frame/support/src/traits/tokens/fungible/hold.rs +++ b/substrate/frame/support/src/traits/tokens/fungible/hold.rs @@ -430,7 +430,11 @@ pub trait Mutate: } /// Trait for slashing a fungible asset which can be place on hold. -pub trait Balanced: super::Balanced + Unbalanced { +pub trait Balanced: + super::Balanced + + Unbalanced + + DoneSlash +{ /// Reduce the balance of some funds on hold in an account. /// /// The resulting imbalance is the first item of the tuple returned. @@ -449,6 +453,16 @@ pub trait Balanced: super::Balanced + Unbalanced { + fn done_slash(_reason: &Reason, _who: &AccountId, _amount: Balance) {} +} + +#[impl_trait_for_tuples::impl_for_tuples(30)] +impl DoneSlash for Tuple { + fn done_slash(reason: &Reason, who: &AccountId, amount: Balance) { + for_tuples!( #( Tuple::done_slash(reason, who, amount); )* ); + } } diff --git a/substrate/frame/support/src/traits/tokens/fungible/item_of.rs b/substrate/frame/support/src/traits/tokens/fungible/item_of.rs index c9f366911a8b62932ffbbb757d13e72a31b3c718..309288d8278fd0bfe121aab10c8d958b4229a60c 100644 --- a/substrate/frame/support/src/traits/tokens/fungible/item_of.rs +++ b/substrate/frame/support/src/traits/tokens/fungible/item_of.rs @@ -30,6 +30,7 @@ use crate::traits::{ WithdrawConsequence, }, }; +use frame_support::traits::fungible::hold::DoneSlash; use sp_core::Get; use sp_runtime::{DispatchError, DispatchResult}; @@ -467,5 +468,21 @@ impl< } } +impl< + F: fungibles::BalancedHold, + A: Get<>::AssetId>, + AccountId, + > DoneSlash for ItemOf +{ + fn done_slash(reason: &F::Reason, who: &AccountId, amount: F::Balance) { + >::done_slash( + A::get(), + reason, + who, + amount, + ) + } +} + #[test] fn test() {} diff --git a/substrate/frame/support/src/traits/tokens/fungible/union_of.rs b/substrate/frame/support/src/traits/tokens/fungible/union_of.rs index 3adbbdda31431ee1f7b8e7646825bee1964131f7..5cb1d0a9e7b0758504ad92a0a6698ad1f652e078 100644 --- a/substrate/frame/support/src/traits/tokens/fungible/union_of.rs +++ b/substrate/frame/support/src/traits/tokens/fungible/union_of.rs @@ -844,8 +844,10 @@ impl< } impl< - Left: fungible::BalancedHold, - Right: fungibles::BalancedHold, + Left: fungible::BalancedHold + + fungible::hold::DoneSlash, + Right: fungibles::BalancedHold + + fungibles::hold::DoneSlash, Criterion: Convert>, AssetKind: AssetId, AccountId, @@ -871,6 +873,29 @@ impl< } } } +impl< + Reason, + Balance, + Left: fungible::hold::DoneSlash, + Right: fungibles::hold::DoneSlash + + fungibles::Inspect, + Criterion: Convert>, + AssetKind: AssetId, + AccountId, + > fungibles::hold::DoneSlash + for UnionOf +{ + fn done_slash(asset: AssetKind, reason: &Reason, who: &AccountId, amount: Balance) { + match Criterion::convert(asset.clone()) { + Left(()) => { + Left::done_slash(reason, who, amount); + }, + Right(a) => { + Right::done_slash(a, reason, who, amount); + }, + } + } +} impl< Left: fungible::Inspect, diff --git a/substrate/frame/support/src/traits/tokens/fungibles/hold.rs b/substrate/frame/support/src/traits/tokens/fungibles/hold.rs index ef3fef7a300d96b5c741983fd0b19297b44f541c..026bfc872e0c4fc0ed399bf13afe7269817f0e2b 100644 --- a/substrate/frame/support/src/traits/tokens/fungibles/hold.rs +++ b/substrate/frame/support/src/traits/tokens/fungibles/hold.rs @@ -214,7 +214,11 @@ pub trait Unbalanced: Inspect { } /// Trait for slashing a fungible asset which can be place on hold. -pub trait Balanced: super::Balanced + Unbalanced { +pub trait Balanced: + super::Balanced + + Unbalanced + + DoneSlash +{ /// Reduce the balance of some funds on hold in an account. /// /// The resulting imbalance is the first item of the tuple returned. @@ -238,13 +242,19 @@ pub trait Balanced: super::Balanced + Unbalanced { + fn done_slash(_asset: AssetId, _reason: &Reason, _who: &AccountId, _amount: Balance) {} +} + +#[impl_trait_for_tuples::impl_for_tuples(30)] +impl DoneSlash + for Tuple +{ + fn done_slash(asset_id: AssetId, reason: &Reason, who: &AccountId, amount: Balance) { + for_tuples!( #( Tuple::done_slash(asset_id, reason, who, amount); )* ); } } diff --git a/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs b/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs index 77047150e00cee0481eaf555e001bfe1a73be431..ec066dddcfac801ba2dd0757c94783e3c6e8b2c3 100644 --- a/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs +++ b/substrate/frame/support/src/traits/tokens/fungibles/union_of.rs @@ -825,8 +825,10 @@ impl< } impl< - Left: fungibles::BalancedHold, - Right: fungibles::BalancedHold, + Left: fungibles::BalancedHold + + fungibles::hold::DoneSlash, + Right: fungibles::BalancedHold + + fungibles::hold::DoneSlash, Criterion: Convert>, AssetKind: AssetId, AccountId, @@ -853,6 +855,31 @@ impl< } } +impl< + Reason, + Balance, + Left: fungibles::Inspect + + fungibles::hold::DoneSlash, + Right: fungibles::Inspect + + fungibles::hold::DoneSlash, + Criterion: Convert>, + AssetKind: AssetId, + AccountId, + > fungibles::hold::DoneSlash + for UnionOf +{ + fn done_slash(asset: AssetKind, reason: &Reason, who: &AccountId, amount: Balance) { + match Criterion::convert(asset.clone()) { + Left(a) => { + Left::done_slash(a, reason, who, amount); + }, + Right(a) => { + Right::done_slash(a, reason, who, amount); + }, + } + } +} + impl< Left: fungibles::Inspect + fungibles::Create, Right: fungibles::Inspect + fungibles::Create, diff --git a/substrate/frame/support/src/traits/try_runtime/decode_entire_state.rs b/substrate/frame/support/src/traits/try_runtime/decode_entire_state.rs index 8dbeecd8e860eeaaacbaf4edb5a6f098e9d19d39..a7465c87fb27fdc8be5681780f469a30abc97346 100644 --- a/substrate/frame/support/src/traits/try_runtime/decode_entire_state.rs +++ b/substrate/frame/support/src/traits/try_runtime/decode_entire_state.rs @@ -197,7 +197,8 @@ impl TryDecodeEntireS QueryKind, OnEmpty, MaxValues, - > where + > +where Prefix: CountedStorageMapInstance, Hasher: StorageHasher, Key: FullCodec, @@ -229,7 +230,8 @@ impl QueryKind, OnEmpty, MaxValues, - > where + > +where Prefix: StorageInstance, Hasher1: StorageHasher, Key1: FullCodec, diff --git a/substrate/frame/support/src/traits/try_runtime/mod.rs b/substrate/frame/support/src/traits/try_runtime/mod.rs index 09c33c01440677e14eacbd92063f7ffafb267c71..284ba3d7422da26122da07a135f20abee18b8382 100644 --- a/substrate/frame/support/src/traits/try_runtime/mod.rs +++ b/substrate/frame/support/src/traits/try_runtime/mod.rs @@ -28,7 +28,7 @@ use sp_arithmetic::traits::AtLeast32BitUnsigned; use sp_runtime::TryRuntimeError; /// Which state tests to execute. -#[derive(codec::Encode, codec::Decode, Clone, scale_info::TypeInfo)] +#[derive(codec::Encode, codec::Decode, Clone, scale_info::TypeInfo, PartialEq)] pub enum Select { /// None of them. None, @@ -95,7 +95,7 @@ impl std::str::FromStr for Select { } /// Select which checks should be run when trying a runtime upgrade upgrade. -#[derive(codec::Encode, codec::Decode, Clone, Debug, Copy, scale_info::TypeInfo)] +#[derive(codec::Encode, codec::Decode, Clone, Debug, Copy, scale_info::TypeInfo, PartialEq)] pub enum UpgradeCheckSelect { /// Run no checks. None, diff --git a/substrate/frame/support/test/compile_pass/src/lib.rs b/substrate/frame/support/test/compile_pass/src/lib.rs index 37af683fbc7f239c3481ee5e9b368902d363aff6..677ef4e94c89665bf639a3897cd4617f9ed18e60 100644 --- a/substrate/frame/support/test/compile_pass/src/lib.rs +++ b/substrate/frame/support/test/compile_pass/src/lib.rs @@ -40,7 +40,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: sp_version::create_apis_vec!([]), transaction_version: 0, - state_version: 0, + system_version: 0, }; pub type Signature = sr25519::Signature; diff --git a/substrate/frame/support/test/tests/benchmark_ui/bad_return_type_blank_with_question.stderr b/substrate/frame/support/test/tests/benchmark_ui/bad_return_type_blank_with_question.stderr index 7e0a02be649b4755edac171619b4808b739844a4..04203e4b684b259c75fe3a61165447efc88d5af3 100644 --- a/substrate/frame/support/test/tests/benchmark_ui/bad_return_type_blank_with_question.stderr +++ b/substrate/frame/support/test/tests/benchmark_ui/bad_return_type_blank_with_question.stderr @@ -8,3 +8,7 @@ error[E0277]: the `?` operator can only be used in a function that returns `Resu | ^ cannot use the `?` operator in a function that returns `()` | = help: the trait `FromResidual>` is not implemented for `()` +help: consider adding return type + | +31 | fn bench() -> Result<(), Box> { + | +++++++++++++++++++++++++++++++++++++++++ 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 b28cae2ddefab062666fc6ba1334deb2b02ce9a8..59e36775d464315b0786a1faa654be7d21419179 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 @@ -53,7 +53,15 @@ note: required by a bound in `frame_system::Event` | ^^^^^^ required by this bound in `Event` = note: this error originates in the macro `frame_support::construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: the trait bound `Runtime: Config` is not satisfied in `RuntimeEvent` +error[E0277]: the trait bound `Runtime: Config` is not satisfied + --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 + | +20 | construct_runtime! { + | ^ the trait `Config` is not implemented for `Runtime` + | + = 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 `RawOrigin<_>: TryFrom` is not satisfied --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 | 20 | / construct_runtime! { @@ -63,9 +71,12 @@ error[E0277]: the trait bound `Runtime: Config` is not satisfied in `RuntimeEven ... | 27 | | } 28 | | } - | |_^ within `RuntimeEvent`, the trait `Config` is not implemented for `Runtime`, which is required by `RuntimeEvent: Sized` + | |_^ the trait `TryFrom` is not implemented for `RawOrigin<_>` | -note: required because it appears within the type `RuntimeEvent` + = help: the trait `TryFrom` is implemented for `RawOrigin<::AccountId>` + = note: this error originates in the macro `frame_support::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! { @@ -75,15 +86,25 @@ note: required because it appears within the type `RuntimeEvent` ... | 27 | | } 28 | | } - | |_^ -note: required by a bound in `Clone` - --> $RUST/core/src/clone.rs + | |_^ the trait `Config` is not implemented for `Runtime`, which is required by `Pallet: Callable` | - | pub trait Clone: Sized { - | ^^^^^ required by this bound in `Clone` - = note: this error originates in the derive macro `Clone` which comes from the expansion of the macro `frame_support::construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) + = help: the trait `Callable` is implemented for `Pallet` + = note: required for `Pallet` to implement `Callable` + = note: this error originates in the macro `frame_support::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:26:3 + | +26 | System: frame_system::{Pallet, Call, Storage, Config, Event}, + | ^^^^^^ the trait `Config` is not implemented for `Runtime` + | +note: required by a bound in `GenesisConfig` + --> $WORKSPACE/substrate/frame/system/src/lib.rs + | + | pub struct GenesisConfig { + | ^^^^^^ required by this bound in `GenesisConfig` -error[E0277]: the trait bound `Runtime: Config` is not satisfied in `RuntimeEvent` +error[E0277]: the trait bound `Runtime: Config` is not satisfied --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 | 20 | / construct_runtime! { @@ -93,9 +114,16 @@ error[E0277]: the trait bound `Runtime: Config` is not satisfied in `RuntimeEven ... | 27 | | } 28 | | } - | |_^ within `RuntimeEvent`, the trait `Config` is not implemented for `Runtime`, which is required by `RuntimeEvent: Sized` + | |_^ the trait `Config` is not implemented for `Runtime` | -note: required because it appears within the type `RuntimeEvent` +note: required by a bound in `frame_system::Event` + --> $WORKSPACE/substrate/frame/system/src/lib.rs + | + | pub enum Event { + | ^^^^^^ required by this bound in `Event` + = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0369]: binary operation `==` cannot be applied to type `&frame_system::Event` --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 | 20 | / construct_runtime! { @@ -106,14 +134,21 @@ note: required because it appears within the type `RuntimeEvent` 27 | | } 28 | | } | |_^ -note: required by a bound in `EncodeLike` - --> $CARGO/parity-scale-codec-3.6.12/src/encode_like.rs | - | pub trait EncodeLike: Sized + Encode {} - | ^^^^^ required by this bound in `EncodeLike` - = note: this error originates in the macro `frame_support::construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) +note: an implementation of `Config` might be missing for `Runtime` + --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 + | +20 | / construct_runtime! { +21 | | pub struct Runtime where + | |______________________^ must implement `Config` +note: the trait `Config` must be implemented + --> $WORKSPACE/substrate/frame/system/src/lib.rs + | + | pub trait Config: 'static + Eq + Clone { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the derive macro `PartialEq` 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 in `RuntimeEvent` +error[E0277]: the trait bound `Runtime: Config` is not satisfied --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 | 20 | / construct_runtime! { @@ -123,9 +158,16 @@ error[E0277]: the trait bound `Runtime: Config` is not satisfied in `RuntimeEven ... | 27 | | } 28 | | } - | |_^ within `RuntimeEvent`, the trait `Config` is not implemented for `Runtime`, which is required by `RuntimeEvent: Sized` + | |_^ the trait `Config` is not implemented for `Runtime` | -note: required because it appears within the type `RuntimeEvent` +note: required by a bound in `frame_system::Event` + --> $WORKSPACE/substrate/frame/system/src/lib.rs + | + | pub enum Event { + | ^^^^^^ required by this bound in `Event` + = note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `frame_system::Event: Encode` is not satisfied --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 | 20 | / construct_runtime! { @@ -135,15 +177,12 @@ note: required because it appears within the type `RuntimeEvent` ... | 27 | | } 28 | | } - | |_^ -note: required by a bound in `Decode` - --> $CARGO/parity-scale-codec-3.6.12/src/codec.rs + | |_^ the trait `Encode` is not implemented for `frame_system::Event` | - | pub trait Decode: Sized { - | ^^^^^ required by this bound in `Decode` - = note: this error originates in the macro `frame_support::construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) + = help: the trait `Encode` is implemented for `frame_system::Event` + = note: this error originates in the derive macro `self::sp_api_hidden_includes_construct_runtime::hidden_include::__private::codec::Encode` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: the trait bound `Runtime: Config` is not satisfied in `frame_system::Event` +error[E0277]: the trait bound `Runtime: Config` is not satisfied --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 | 20 | / construct_runtime! { @@ -153,21 +192,16 @@ error[E0277]: the trait bound `Runtime: Config` is not satisfied in `frame_syste ... | 27 | | } 28 | | } - | |_^ within `frame_system::Event`, the trait `Config` is not implemented for `Runtime`, which is required by `frame_system::Event: Sized` + | |_^ the trait `Config` is not implemented for `Runtime` | -note: required because it appears within the type `frame_system::Event` +note: required by a bound in `frame_system::Event` --> $WORKSPACE/substrate/frame/system/src/lib.rs | | pub enum Event { - | ^^^^^ -note: required by a bound in `From` - --> $RUST/core/src/convert/mod.rs - | - | pub trait From: Sized { - | ^ required by this bound in `From` - = note: this error originates in the macro `frame_support::construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) + | ^^^^^^ required by this bound in `Event` + = note: this error originates in the derive macro `self::sp_api_hidden_includes_construct_runtime::hidden_include::__private::codec::Encode` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: the trait bound `Runtime: Config` is not satisfied in `frame_system::Event` +error[E0277]: the trait bound `frame_system::Event: Decode` is not satisfied --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 | 20 | / construct_runtime! { @@ -177,29 +211,40 @@ error[E0277]: the trait bound `Runtime: Config` is not satisfied in `frame_syste ... | 27 | | } 28 | | } - | |_^ within `frame_system::Event`, the trait `Config` is not implemented for `Runtime`, which is required by `frame_system::Event: Sized` + | |_^ the trait `Decode` is not implemented for `frame_system::Event` | -note: required because it appears within the type `frame_system::Event` + = help: the trait `Decode` is implemented for `frame_system::Event` + +error[E0277]: the trait bound `Runtime: Config` is not satisfied + --> tests/construct_runtime_ui/deprecated_where_block.rs:26:11 + | +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::Event` --> $WORKSPACE/substrate/frame/system/src/lib.rs | | pub enum Event { - | ^^^^^ -note: required by a bound in `TryInto` - --> $RUST/core/src/convert/mod.rs - | - | pub trait TryInto: Sized { - | ^ required by this bound in `TryInto` - = note: this error originates in the macro `frame_support::construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) + | ^^^^^^ required by this bound in `Event` error[E0277]: the trait bound `Runtime: Config` is not satisfied --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 | -20 | construct_runtime! { - | ^ the trait `Config` is not implemented for `Runtime` +20 | / construct_runtime! { +21 | | pub struct Runtime where +22 | | Block = Block, +23 | | NodeBlock = Block, +... | +27 | | } +28 | | } + | |_^ the trait `Config` is not implemented for `Runtime`, which is required by `frame_system::Event: std::fmt::Debug` | - = 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) + = help: the trait `std::fmt::Debug` is implemented for `frame_system::Event` + = note: required for `frame_system::Event` to implement `std::fmt::Debug` + = note: required for the cast from `&frame_system::Event` to `&dyn std::fmt::Debug` + = note: this error originates in the derive macro `self::sp_api_hidden_includes_construct_runtime::hidden_include::__private::RuntimeDebug` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: the trait bound `RawOrigin<_>: TryFrom` is not satisfied +error[E0277]: the trait bound `Runtime: Config` is not satisfied --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 | 20 | / construct_runtime! { @@ -209,10 +254,12 @@ error[E0277]: the trait bound `RawOrigin<_>: TryFrom` is not satis ... | 27 | | } 28 | | } - | |_^ the trait `TryFrom` is not implemented for `RawOrigin<_>` + | |_^ the trait `Config` is not implemented for `Runtime`, which is required by `frame_system::Error: std::fmt::Debug` | - = help: the trait `TryFrom` is implemented for `RawOrigin<::AccountId>` - = note: this error originates in the macro `frame_support::construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) + = help: the trait `std::fmt::Debug` is implemented for `frame_system::Error` + = note: required for `frame_system::Error` to implement `std::fmt::Debug` + = note: required for the cast from `&frame_system::Error` to `&dyn std::fmt::Debug` + = note: this error originates in the derive macro `self::sp_api_hidden_includes_construct_runtime::hidden_include::__private::RuntimeDebug` (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 @@ -224,11 +271,15 @@ error[E0277]: the trait bound `Runtime: Config` is not satisfied ... | 27 | | } 28 | | } - | |_^ the trait `Config` is not implemented for `Runtime`, which is required by `Pallet: Callable` + | |_^ the trait `Config` is not implemented for `Runtime` | - = help: the trait `Callable` is implemented for `Pallet` - = note: required for `Pallet` to implement `Callable` - = note: this error originates in the macro `frame_support::construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the derive macro `Clone` (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:21:13 + | +21 | pub struct Runtime where + | ^^^^^^^ the trait `Config` is not implemented for `Runtime` error[E0277]: the trait bound `Runtime: Config` is not satisfied --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 @@ -240,10 +291,12 @@ error[E0277]: the trait bound `Runtime: Config` is not satisfied ... | 27 | | } 28 | | } - | |_^ the trait `Config` is not implemented for `Runtime`, which is required by `RuntimeCall: Sized` + | |_^ the trait `Config` is not implemented for `Runtime`, which is required by `RawOrigin<_>: Into<_>` | - = note: required for `Pallet` to implement `Callable` -note: required because it appears within the type `RuntimeCall` + = note: required for `RawOrigin<_>` to implement `Into` + = note: this error originates in the macro `frame_support::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! { @@ -253,13 +306,9 @@ note: required because it appears within the type `RuntimeCall` ... | 27 | | } 28 | | } - | |_^ -note: required by a bound in `Clone` - --> $RUST/core/src/clone.rs + | |_^ the trait `Config` is not implemented for `Runtime` | - | pub trait Clone: Sized { - | ^^^^^ required by this bound in `Clone` - = note: this error originates in the derive macro `Clone` which comes from the expansion of the macro `frame_support::construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the derive macro `PartialEq` (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 @@ -271,10 +320,17 @@ error[E0277]: the trait bound `Runtime: Config` is not satisfied ... | 27 | | } 28 | | } - | |_^ the trait `Config` is not implemented for `Runtime`, which is required by `RuntimeCall: Sized` + | |_^ the trait `Config` is not implemented for `Runtime` | - = note: required for `Pallet` to implement `Callable` -note: required because it appears within the type `RuntimeCall` + = note: this error originates in the derive macro `self::sp_api_hidden_includes_construct_runtime::hidden_include::__private::RuntimeDebug` (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:26:11 + | +26 | System: frame_system::{Pallet, Call, Storage, Config, Event}, + | ^^^^^^^^^^^^ the trait `Config` is not implemented for `Runtime` + +error[E0277]: the trait bound `Runtime: Config` is not satisfied --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 | 20 | / construct_runtime! { @@ -284,12 +340,10 @@ note: required because it appears within the type `RuntimeCall` ... | 27 | | } 28 | | } - | |_^ -note: required by a bound in `EncodeLike` - --> $CARGO/parity-scale-codec-3.6.12/src/encode_like.rs + | |_^ the trait `Config` is not implemented for `Runtime`, which is required by `Pallet: PalletInfoAccess` | - | pub trait EncodeLike: Sized + Encode {} - | ^^^^^ required by this bound in `EncodeLike` + = help: the trait `PalletInfoAccess` is implemented for `Pallet` + = note: required for `Pallet` to implement `PalletInfoAccess` = note: this error originates in the macro `frame_support::construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `Runtime: Config` is not satisfied @@ -302,10 +356,13 @@ error[E0277]: the trait bound `Runtime: Config` is not satisfied ... | 27 | | } 28 | | } - | |_^ the trait `Config` is not implemented for `Runtime`, which is required by `RuntimeCall: Sized` + | |_^ the trait `Config` is not implemented for `Runtime`, which is required by `Pallet: Callable` | + = help: the trait `Callable` is implemented for `Pallet` = note: required for `Pallet` to implement `Callable` -note: required because it appears within the type `RuntimeCall` + = note: this error originates in the derive macro `Clone` (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! { @@ -315,15 +372,13 @@ note: required because it appears within the type `RuntimeCall` ... | 27 | | } 28 | | } - | |_^ -note: required by a bound in `Decode` - --> $CARGO/parity-scale-codec-3.6.12/src/codec.rs + | |_^ the trait `Config` is not implemented for `Runtime`, which is required by `Pallet: Callable` | - | pub trait Decode: Sized { - | ^^^^^ required by this bound in `Decode` - = note: this error originates in the macro `frame_support::construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) + = help: the trait `Callable` is implemented for `Pallet` + = note: required for `Pallet` to implement `Callable` + = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: the trait bound `Runtime: Config` is not satisfied +error[E0369]: binary operation `==` cannot be applied to type `&frame_system::Call` --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 | 20 | / construct_runtime! { @@ -333,10 +388,22 @@ error[E0277]: the trait bound `Runtime: Config` is not satisfied ... | 27 | | } 28 | | } - | |_^ the trait `Config` is not implemented for `Runtime`, which is required by `RuntimeCall: Sized` + | |_^ | - = note: required for `Pallet` to implement `Callable` -note: required because it appears within the type `RuntimeCall` +note: an implementation of `Config` might be missing for `Runtime` + --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 + | +20 | / construct_runtime! { +21 | | pub struct Runtime where + | |______________________^ must implement `Config` +note: the trait `Config` must be implemented + --> $WORKSPACE/substrate/frame/system/src/lib.rs + | + | pub trait Config: 'static + Eq + Clone { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the derive macro `PartialEq` 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 `frame_system::Call: Encode` is not satisfied --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 | 20 | / construct_runtime! { @@ -346,27 +413,31 @@ note: required because it appears within the type `RuntimeCall` ... | 27 | | } 28 | | } - | |_^ -note: required by a bound in `frame_support::sp_runtime::traits::Dispatchable::Config` - --> $WORKSPACE/substrate/primitives/runtime/src/traits.rs + | |_^ the trait `Encode` is not implemented for `frame_system::Call` | - | type Config; - | ^^^^^^^^^^^^ required by this bound in `Dispatchable::Config` - = note: this error originates in the macro `frame_support::construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) + = help: the trait `Encode` is implemented for `frame_system::Call` + = note: this error originates in the derive macro `self::sp_api_hidden_includes_construct_runtime::hidden_include::__private::codec::Encode` (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:26:3 + --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 | -26 | System: frame_system::{Pallet, Call, Storage, Config, Event}, - | ^^^^^^ the trait `Config` is not implemented for `Runtime` +20 | / construct_runtime! { +21 | | pub struct Runtime where +22 | | Block = Block, +23 | | NodeBlock = Block, +... | +27 | | } +28 | | } + | |_^ the trait `Config` is not implemented for `Runtime` | -note: required by a bound in `GenesisConfig` +note: required by a bound in `frame_system::Call` --> $WORKSPACE/substrate/frame/system/src/lib.rs | - | pub struct GenesisConfig { - | ^^^^^^ required by this bound in `GenesisConfig` + | #[pallet::call] + | ^^^^ required by this bound in `Call` + = note: this error originates in the derive macro `self::sp_api_hidden_includes_construct_runtime::hidden_include::__private::codec::Encode` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: the trait bound `Runtime: Config` is not satisfied +error[E0277]: the trait bound `frame_system::Call: Decode` is not satisfied --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 | 20 | / construct_runtime! { @@ -376,10 +447,12 @@ error[E0277]: the trait bound `Runtime: Config` is not satisfied ... | 27 | | } 28 | | } - | |_^ the trait `Config` is not implemented for `Runtime`, which is required by `RuntimeCall: Sized` + | |_^ the trait `Decode` is not implemented for `frame_system::Call` | - = note: required for `Pallet` to implement `Callable` -note: required because it appears within the type `RuntimeCall` + = help: the trait `Decode` is implemented for `frame_system::Call` + = note: this error originates in the macro `frame_support::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! { @@ -389,15 +462,256 @@ note: required because it appears within the type `RuntimeCall` ... | 27 | | } 28 | | } - | |_^ -note: required by a bound in `frame_support::pallet_prelude::ValidateUnsigned::Call` - --> $WORKSPACE/substrate/primitives/runtime/src/traits.rs + | |_^ the trait `Config` is not implemented for `Runtime`, which is required by `Pallet: Callable` + | + = help: the trait `Callable` is implemented for `Pallet` + = note: required for `Pallet` to implement `Callable` + = note: this error originates in the derive macro `self::sp_api_hidden_includes_construct_runtime::hidden_include::__private::RuntimeDebug` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0599]: the method `get_dispatch_info` exists for reference `&Call`, but its trait bounds were not satisfied + --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 + | +20 | construct_runtime! { + | __^ + | | _| + | || +21 | || pub struct Runtime where + | ||______________________- doesn't satisfy `Runtime: Config` +22 | | Block = Block, +23 | | NodeBlock = Block, +... | +27 | | } +28 | | } + | |__^ method cannot be called on `&Call` due to unsatisfied trait bounds + | + ::: $WORKSPACE/substrate/frame/system/src/lib.rs + | + | #[pallet::call] + | ---- doesn't satisfy `frame_system::Call: GetDispatchInfo` + | + = note: the following trait bounds were not satisfied: + `Runtime: Config` + which is required by `frame_system::Call: GetDispatchInfo` +note: the trait `Config` must be implemented + --> $WORKSPACE/substrate/frame/system/src/lib.rs + | + | pub trait Config: 'static + Eq + Clone { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the macro `frame_support::construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0599]: the method `is_feeless` exists for reference `&Call`, but its trait bounds were not satisfied + --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 + | +20 | construct_runtime! { + | __^ + | | _| + | || +21 | || pub struct Runtime where + | ||______________________- doesn't satisfy `Runtime: Config` +22 | | Block = Block, +23 | | NodeBlock = Block, +... | +27 | | } +28 | | } + | |__^ method cannot be called on `&Call` due to unsatisfied trait bounds + | + ::: $WORKSPACE/substrate/frame/system/src/lib.rs + | + | #[pallet::call] + | ---- doesn't satisfy `frame_system::Call: CheckIfFeeless` + | + = note: the following trait bounds were not satisfied: + `Runtime: Config` + which is required by `frame_system::Call: CheckIfFeeless` +note: the trait `Config` must be implemented + --> $WORKSPACE/substrate/frame/system/src/lib.rs | - | type Call; - | ^^^^^^^^^^ required by this bound in `ValidateUnsigned::Call` + | pub trait Config: 'static + Eq + Clone { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `frame_support::construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: the trait bound `Runtime: Config` is not satisfied in `RuntimeEvent` +error[E0599]: the method `get_call_name` exists for reference `&Call`, but its trait bounds were not satisfied + --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 + | +20 | construct_runtime! { + | __^ + | | _| + | || +21 | || pub struct Runtime where + | ||______________________- doesn't satisfy `Runtime: Config` +22 | | Block = Block, +23 | | NodeBlock = Block, +... | +27 | | } +28 | | } + | |__^ method cannot be called on `&Call` due to unsatisfied trait bounds + | + ::: $WORKSPACE/substrate/frame/system/src/lib.rs + | + | #[pallet::call] + | ---- doesn't satisfy `frame_system::Call: GetCallName` + | + = note: the following trait bounds were not satisfied: + `Runtime: Config` + which is required by `frame_system::Call: GetCallName` +note: the trait `Config` must be implemented + --> $WORKSPACE/substrate/frame/system/src/lib.rs + | + | pub trait Config: 'static + Eq + Clone { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the macro `frame_support::construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0599]: the function or associated item `storage_metadata` exists for struct `Pallet`, but its trait bounds were not satisfied + --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 + | +20 | construct_runtime! { + | __^ + | | _| + | || +21 | || pub struct Runtime where + | ||______________________- doesn't satisfy `Runtime: Config` +22 | | Block = Block, +23 | | NodeBlock = Block, +... | +27 | | } +28 | | } + | |__^ function or associated item cannot be called on `Pallet` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `Runtime: Config` +note: the trait `Config` must be implemented + --> $WORKSPACE/substrate/frame/system/src/lib.rs + | + | pub trait Config: 'static + Eq + Clone { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the macro `frame_support::construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0599]: the function or associated item `call_functions` exists for struct `Pallet`, but its trait bounds were not satisfied + --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 + | +20 | construct_runtime! { + | __^ + | | _| + | || +21 | || pub struct Runtime where + | ||______________________- doesn't satisfy `Runtime: Config` +22 | | Block = Block, +23 | | NodeBlock = Block, +... | +27 | | } +28 | | } + | |__^ function or associated item cannot be called on `Pallet` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `Runtime: Config` +note: the trait `Config` must be implemented + --> $WORKSPACE/substrate/frame/system/src/lib.rs + | + | pub trait Config: 'static + Eq + Clone { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the macro `frame_support::construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0599]: the variant or associated item `event_metadata` exists for enum `Event`, but its trait bounds were not satisfied + --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 + | +20 | construct_runtime! { + | __^ + | | _| + | || +21 | || pub struct Runtime where + | ||______________________- doesn't satisfy `Runtime: Config` +22 | | Block = Block, +23 | | NodeBlock = Block, +... | +27 | | } +28 | | } + | |__^ variant or associated item cannot be called on `Event` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `Runtime: Config` +note: the trait `Config` must be implemented + --> $WORKSPACE/substrate/frame/system/src/lib.rs + | + | pub trait Config: 'static + Eq + Clone { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the macro `frame_support::construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0599]: the function or associated item `pallet_constants_metadata` exists for struct `Pallet`, but its trait bounds were not satisfied + --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 + | +20 | construct_runtime! { + | __^ + | | _| + | || +21 | || pub struct Runtime where + | ||______________________- doesn't satisfy `Runtime: Config` +22 | | Block = Block, +23 | | NodeBlock = Block, +... | +27 | | } +28 | | } + | |__^ function or associated item cannot be called on `Pallet` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `Runtime: Config` +note: the trait `Config` must be implemented + --> $WORKSPACE/substrate/frame/system/src/lib.rs + | + | pub trait Config: 'static + Eq + Clone { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the macro `frame_support::construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0599]: the function or associated item `error_metadata` exists for struct `Pallet`, but its trait bounds were not satisfied + --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 + | +20 | construct_runtime! { + | __^ + | | _| + | || +21 | || pub struct Runtime where + | ||______________________- doesn't satisfy `Runtime: Config` +22 | | Block = Block, +23 | | NodeBlock = Block, +... | +27 | | } +28 | | } + | |__^ function or associated item cannot be called on `Pallet` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `Runtime: Config` +note: the trait `Config` must be implemented + --> $WORKSPACE/substrate/frame/system/src/lib.rs + | + | pub trait Config: 'static + Eq + Clone { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the macro `frame_support::construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0599]: the function or associated item `pallet_documentation_metadata` exists for struct `Pallet`, but its trait bounds were not satisfied + --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 + | +20 | construct_runtime! { + | __^ + | | _| + | || +21 | || pub struct Runtime where + | ||______________________- doesn't satisfy `Runtime: Config` +22 | | Block = Block, +23 | | NodeBlock = Block, +... | +27 | | } +28 | | } + | |__^ function or associated item cannot be called on `Pallet` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `Runtime: Config` +note: the trait `Config` must be implemented + --> $WORKSPACE/substrate/frame/system/src/lib.rs + | + | pub trait Config: 'static + Eq + Clone { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the macro `frame_support::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! { @@ -407,9 +721,21 @@ error[E0277]: the trait bound `Runtime: Config` is not satisfied in `RuntimeEven ... | 27 | | } 28 | | } - | |_^ within `RuntimeEvent`, the trait `Config` is not implemented for `Runtime`, which is required by `RuntimeEvent: Sized` - | -note: required because it appears within the type `RuntimeEvent` + | |_^ the trait `Config` is not implemented for `Runtime`, which is required by `GenesisConfig: Serialize` + | + = help: the trait `Serialize` is implemented for `GenesisConfig` + = note: required for `GenesisConfig` to implement `Serialize` +note: required by a bound in `frame_support::sp_runtime::serde::ser::SerializeStruct::serialize_field` + --> $CARGO/serde-1.0.210/src/ser/mod.rs + | + | fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> + | --------------- required by a bound in this associated function + | where + | T: ?Sized + Serialize; + | ^^^^^^^^^ required by this bound in `SerializeStruct::serialize_field` + = note: this error originates in the macro `frame_support::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! { @@ -419,15 +745,16 @@ note: required because it appears within the type `RuntimeEvent` ... | 27 | | } 28 | | } - | |_^ -note: required by a bound in `Result` - --> $RUST/core/src/result.rs + | |_^ the trait `Config` is not implemented for `Runtime` | - | 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 `frame_support::construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) +note: required by a bound in `GenesisConfig` + --> $WORKSPACE/substrate/frame/system/src/lib.rs + | + | pub struct GenesisConfig { + | ^^^^^^ required by this bound in `GenesisConfig` + = note: this error originates in the macro `frame_support::construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0277]: the trait bound `Runtime: Config` is not satisfied in `RuntimeEvent` +error[E0277]: the trait bound `Runtime: Config` is not satisfied --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 | 20 | / construct_runtime! { @@ -437,9 +764,16 @@ error[E0277]: the trait bound `Runtime: Config` is not satisfied in `RuntimeEven ... | 27 | | } 28 | | } - | |_^ within `RuntimeEvent`, the trait `Config` is not implemented for `Runtime`, which is required by `RuntimeEvent: Sized` + | |_^ the trait `Config` is not implemented for `Runtime` | -note: required because it appears within the type `RuntimeEvent` +note: required by a bound in `GenesisConfig` + --> $WORKSPACE/substrate/frame/system/src/lib.rs + | + | pub struct GenesisConfig { + | ^^^^^^ required by this bound in `GenesisConfig` + = note: this error originates in the derive macro `self::sp_api_hidden_includes_construct_runtime::hidden_include::__private::serde::Deserialize` (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! { @@ -449,13 +783,11 @@ note: required because it appears within the type `RuntimeEvent` ... | 27 | | } 28 | | } - | |_^ -note: required by a bound in `TryInto` - --> $RUST/core/src/convert/mod.rs + | |_^ the trait `Config` is not implemented for `Runtime`, which is required by `GenesisConfig: std::default::Default` | - | pub trait TryInto: Sized { - | ^^^^^ required by this bound in `TryInto` - = note: this error originates in the macro `frame_support::construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) + = help: the trait `std::default::Default` is implemented for `GenesisConfig` + = note: required for `GenesisConfig` to implement `std::default::Default` + = note: this error originates in the derive macro `Default` (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 @@ -467,10 +799,24 @@ error[E0277]: the trait bound `Runtime: Config` is not satisfied ... | 27 | | } 28 | | } - | |_^ the trait `Config` is not implemented for `Runtime`, which is required by `RuntimeCall: Sized` - | - = note: required for `Pallet` to implement `Callable` -note: required because it appears within the type `RuntimeCall` + | |_^ the trait `Config` is not implemented for `Runtime`, which is required by `(Pallet,): OnGenesis` + | + = help: the following other types implement trait `OnGenesis`: + () + (TupleElement0, TupleElement1) + (TupleElement0, TupleElement1, TupleElement2) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6, TupleElement7) + and $N others + = note: required for `Pallet` to implement `OnGenesis` + = note: 1 redundant requirement hidden + = note: required for `(Pallet,)` to implement `OnGenesis` + = note: this error originates in the macro `frame_support::construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0282]: type annotations needed --> tests/construct_runtime_ui/deprecated_where_block.rs:20:1 | 20 | / construct_runtime! { @@ -480,10 +826,8 @@ note: required because it appears within the type `RuntimeCall` ... | 27 | | } 28 | | } - | |_^ -note: required by a bound in `Result` - --> $RUST/core/src/result.rs + | |_^ cannot infer type | - | 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 `frame_support::construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `frame_support::construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: internal compiler error: compiler/rustc_middle/src/ty/normalize_erasing_regions.rs:168:90: Failed to normalize std::rc::Rc::RuntimeCall,)>), bound_vars: [Region(BrAnon)] }, Binder { value: Projection(Output = bool), bound_vars: [Region(BrAnon)] }] + '{erased}, std::alloc::Global>, std::alloc::Global>, maybe try to call `try_normalize_erasing_regions` instead diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/undefined_event_part.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/undefined_event_part.stderr index 6f412fe89eab7eec7cf045a786cb178d80fb6591..c50cba71d4e7dbf6a458ddc5a88bdc784a0acd7f 100644 --- a/substrate/frame/support/test/tests/construct_runtime_ui/undefined_event_part.stderr +++ b/substrate/frame/support/test/tests/construct_runtime_ui/undefined_event_part.stderr @@ -28,7 +28,27 @@ error[E0412]: cannot find type `Event` in module `pallet` | |_^ not found in `pallet` | = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) -help: consider importing one of these items +help: consider importing one of these enums + | +18 + use frame_support_test::Event; + | +18 + use frame_system::Event; + | + +error[E0433]: failed to resolve: could not find `Event` in `pallet` + --> tests/construct_runtime_ui/undefined_event_part.rs:66:1 + | +66 | / construct_runtime! { +67 | | pub struct Runtime +68 | | { +69 | | System: frame_system expanded::{}::{Pallet, Call, Storage, Config, Event}, +70 | | Pallet: pallet expanded::{}::{Pallet, Event}, +71 | | } +72 | | } + | |_^ could not find `Event` in `pallet` + | + = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing one of these enums | 18 + use frame_support_test::Event; | diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/undefined_genesis_config_part.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/undefined_genesis_config_part.stderr index 10093b26f5a8d2908f74f9a5f5b5c9cf3e71efa9..2aa794edc3c937452e5a12dc6695ab7748a884d3 100644 --- a/substrate/frame/support/test/tests/construct_runtime_ui/undefined_genesis_config_part.stderr +++ b/substrate/frame/support/test/tests/construct_runtime_ui/undefined_genesis_config_part.stderr @@ -28,7 +28,7 @@ error[E0412]: cannot find type `GenesisConfig` in module `pallet` | |_^ not found in `pallet` | = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) -help: consider importing one of these items +help: consider importing one of these structs | 18 + use frame_system::GenesisConfig; | 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 30005c07cb631dd48425117507006faa870866cd..d8dc7bd45bc66bb097373a441a5d1f0a4f9da1d9 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 @@ -54,8 +54,8 @@ error[E0599]: no function or associated item named `is_inherent` found for struc | = help: items from traits can only be used if the trait is implemented and in scope = note: the following traits define an item `is_inherent`, perhaps you need to implement one of them: - candidate #1: `ProvideInherent` - candidate #2: `IsInherent` + candidate #1: `IsInherent` + candidate #2: `ProvideInherent` = 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 diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/undefined_origin_part.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/undefined_origin_part.stderr index d0f4b44ab0d57eb4dc8cee10816c7158c0232bca..58c42311b87b5cbd7a33995c2f76c39d5b51833a 100644 --- a/substrate/frame/support/test/tests/construct_runtime_ui/undefined_origin_part.stderr +++ b/substrate/frame/support/test/tests/construct_runtime_ui/undefined_origin_part.stderr @@ -28,7 +28,7 @@ error[E0412]: cannot find type `Origin` in module `pallet` | |_^ not found in `pallet` | = note: this error originates in the macro `construct_runtime` (in Nightly builds, run with -Z macro-backtrace for more info) -help: consider importing one of these items +help: consider importing one of these type aliases | 18 + use frame_support_test::Origin; | diff --git a/substrate/frame/support/test/tests/derive_impl.rs b/substrate/frame/support/test/tests/derive_impl.rs index 675e85f4bfce5388ae0bf790a2355bff242cd477..3514593c8568d38f84d6cecc9f8f7b81574d4841 100644 --- a/substrate/frame/support/test/tests/derive_impl.rs +++ b/substrate/frame/support/test/tests/derive_impl.rs @@ -25,15 +25,9 @@ struct SomeRectangle {} #[frame_support::register_default_impl(SomeRectangle)] impl Shape for SomeRectangle { - #[cfg(not(feature = "feature-frame-testing"))] fn area(&self) -> u32 { 10 } - - #[cfg(feature = "feature-frame-testing")] - fn area(&self) -> u32 { - 0 - } } struct SomeSquare {} @@ -44,9 +38,5 @@ impl Shape for SomeSquare {} #[test] fn test_feature_parsing() { let square = SomeSquare {}; - #[cfg(not(feature = "feature-frame-testing"))] assert_eq!(square.area(), 10); - - #[cfg(feature = "feature-frame-testing")] - assert_eq!(square.area(), 0); } diff --git a/substrate/frame/support/test/tests/derive_no_bound.rs b/substrate/frame/support/test/tests/derive_no_bound.rs index b191470780514617f23231c6a310ab570948a846..6fc4ea12c5131904ab03e9127f0eec93ba595b9b 100644 --- a/substrate/frame/support/test/tests/derive_no_bound.rs +++ b/substrate/frame/support/test/tests/derive_no_bound.rs @@ -159,6 +159,7 @@ fn test_struct_unnamed() { PartialOrdNoBound, OrdNoBound, )] +#[allow(dead_code)] struct StructNoGenerics { field1: u32, field2: u64, 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 index db8a507960770dcfeb8d5768ebf5b9ed6cc3da12..8bf82bff78093fac049b31ff9b2c126e8a3f7806 100644 --- a/substrate/frame/support/test/tests/derive_no_bound_ui/ord.stderr +++ b/substrate/frame/support/test/tests/derive_no_bound_ui/ord.stderr @@ -23,3 +23,13 @@ note: required by a bound in `std::cmp::Eq` | | pub trait Eq: PartialEq { | ^^^^^^^^^^^^^^^ required by this bound in `Eq` + +error[E0599]: `::C` is not an iterator + --> tests/derive_no_bound_ui/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` diff --git a/substrate/frame/support/test/tests/enum_deprecation.rs b/substrate/frame/support/test/tests/enum_deprecation.rs new file mode 100644 index 0000000000000000000000000000000000000000..ed9b2b5a735d177d35c88c01d652021e3dcda698 --- /dev/null +++ b/substrate/frame/support/test/tests/enum_deprecation.rs @@ -0,0 +1,173 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#![allow(useless_deprecated, deprecated, clippy::deprecated_semver)] + +use std::collections::BTreeMap; + +use frame_support::{ + derive_impl, + dispatch::Parameter, + parameter_types, + traits::{ConstU32, StorageVersion}, + OrdNoBound, PartialOrdNoBound, +}; +use scale_info::TypeInfo; + +parameter_types! { + /// Used to control if the storage version should be updated. + storage UpdateStorageVersion: bool = false; +} + +pub struct SomeType1; +impl From for u64 { + fn from(_t: SomeType1) -> Self { + 0u64 + } +} + +pub trait SomeAssociation1 { + type _1: Parameter + codec::MaxEncodedLen + TypeInfo; +} +impl SomeAssociation1 for u64 { + type _1 = u64; +} + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::pallet_prelude::*; + + pub(crate) const STORAGE_VERSION: StorageVersion = StorageVersion::new(10); + + #[pallet::config] + pub trait Config: frame_system::Config + where + ::AccountId: From + SomeAssociation1, + { + type Balance: Parameter + Default + TypeInfo; + + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + } + + #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] + pub struct Pallet(_); + + #[pallet::error] + #[derive(PartialEq, Eq)] + pub enum Error { + /// error doc comment put in metadata + InsufficientProposersBalance, + NonExistentStorageValue, + Code(u8), + #[codec(skip)] + Skipped(u128), + CompactU8(#[codec(compact)] u8), + } + + #[pallet::event] + pub enum Event + where + T::AccountId: SomeAssociation1 + From, + { + #[deprecated = "second"] + A, + #[deprecated = "first"] + #[codec(index = 0)] + B, + } + + #[pallet::origin] + #[derive( + EqNoBound, + RuntimeDebugNoBound, + CloneNoBound, + PartialEqNoBound, + PartialOrdNoBound, + OrdNoBound, + Encode, + Decode, + TypeInfo, + MaxEncodedLen, + )] + pub struct Origin(PhantomData); +} + +frame_support::parameter_types!( + pub const MyGetParam3: u32 = 12; +); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Runtime { + type BaseCallFilter = frame_support::traits::Everything; + type RuntimeOrigin = RuntimeOrigin; + type Nonce = u64; + type RuntimeCall = RuntimeCall; + type Hash = sp_runtime::testing::H256; + type Hashing = sp_runtime::traits::BlakeTwo256; + type AccountId = u64; + type Lookup = sp_runtime::traits::IdentityLookup; + type Block = Block; + type RuntimeEvent = RuntimeEvent; + type MaxConsumers = ConstU32<16>; +} +impl pallet::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = u64; +} + +pub type Header = sp_runtime::generic::Header; +pub type Block = sp_runtime::generic::Block; +pub type UncheckedExtrinsic = + sp_runtime::testing::TestXt>; + +frame_support::construct_runtime!( + pub struct Runtime { + // Exclude part `Storage` in order not to check its metadata in tests. + System: frame_system exclude_parts { Pallet, Storage }, + Example: pallet, + + } +); + +#[test] +fn pallet_metadata() { + use sp_metadata_ir::{DeprecationInfoIR, DeprecationStatusIR}; + let pallets = Runtime::metadata_ir().pallets; + let example = pallets[0].clone(); + { + // Example pallet events are partially and fully deprecated + let meta = example.event.unwrap(); + assert_eq!( + // Result should be this, but instead we get the result below + // see: https://github.com/paritytech/parity-scale-codec/issues/507 + // + // DeprecationInfoIR::VariantsDeprecated(BTreeMap::from([ + // (codec::Compact(0), DeprecationStatusIR::Deprecated { note: "first", since: None + // }), ( + // codec::Compact(1), + // DeprecationStatusIR::Deprecated { note: "second", since: None } + // ) + // ])), + DeprecationInfoIR::VariantsDeprecated(BTreeMap::from([( + codec::Compact(0), + DeprecationStatusIR::Deprecated { note: "first", since: None } + ),])), + meta.deprecation_info + ); + } +} diff --git a/substrate/frame/support/test/tests/instance.rs b/substrate/frame/support/test/tests/instance.rs index 30b8338bc5c7052a6a50474cd44e46a7202ed77e..7f8423a0127e40b2b22b3eafeb4565cd8bd05c79 100644 --- a/substrate/frame/support/test/tests/instance.rs +++ b/substrate/frame/support/test/tests/instance.rs @@ -441,6 +441,7 @@ fn expected_metadata() -> PalletStorageMetadataIR { ty: StorageEntryTypeIR::Plain(scale_info::meta_type::()), default: vec![0, 0, 0, 0], docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "Map", @@ -452,6 +453,7 @@ fn expected_metadata() -> PalletStorageMetadataIR { }, default: [0u8; 8].to_vec(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, StorageEntryMetadataIR { name: "DoubleMap", @@ -463,6 +465,7 @@ fn expected_metadata() -> PalletStorageMetadataIR { }, default: [0u8; 8].to_vec(), docs: vec![], + deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated, }, ], } diff --git a/substrate/frame/support/test/tests/pallet.rs b/substrate/frame/support/test/tests/pallet.rs index eed8a22e8e79884da30002692b44adccea955ba9..3d6aa1d83749e447269fd97e54a6215ca9409b95 100644 --- a/substrate/frame/support/test/tests/pallet.rs +++ b/substrate/frame/support/test/tests/pallet.rs @@ -14,6 +14,9 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +#![allow(useless_deprecated, deprecated, clippy::deprecated_semver)] + +use std::collections::BTreeMap; use frame_support::{ assert_ok, derive_impl, @@ -212,6 +215,7 @@ pub mod pallet { /// call foo doc comment put in metadata #[pallet::call_index(0)] #[pallet::weight(Weight::from_parts(*foo as u64, 0))] + #[deprecated = "test"] pub fn foo( origin: OriginFor, #[pallet::compact] foo: u32, @@ -272,6 +276,7 @@ pub mod pallet { pub enum Error { /// error doc comment put in metadata InsufficientProposersBalance, + #[deprecated = "test"] NonExistentStorageValue, Code(u8), #[codec(skip)] @@ -283,6 +288,7 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(fn deposit_event)] + #[deprecated = "test"] pub enum Event where T::AccountId: SomeAssociation1 + From, @@ -486,7 +492,7 @@ pub mod pallet { let _ = T::AccountId::from(SomeType1); // Test for where clause let _ = T::AccountId::from(SomeType5); // Test for where clause if matches!(call, Call::foo_storage_layer { .. }) { - return Ok(ValidTransaction::default()) + return Ok(ValidTransaction::default()); } Err(TransactionValidityError::Invalid(InvalidTransaction::Call)) } @@ -553,6 +559,7 @@ pub mod pallet { // Test that a pallet with non generic event and generic genesis_config is correctly handled // and that a pallet with the attribute without_storage_info is correctly handled. #[frame_support::pallet] +#[deprecated = "test"] pub mod pallet2 { use super::{SomeAssociation1, SomeType1, UpdateStorageVersion}; use frame_support::pallet_prelude::*; @@ -2424,9 +2431,10 @@ fn post_runtime_upgrade_detects_storage_version_issues() { // any storage version "enabled". assert!( ExecutiveWithUpgradePallet4::try_runtime_upgrade(UpgradeCheckSelect::PreAndPost) - .unwrap_err() == "On chain storage version set, while the pallet \ + .unwrap_err() == + "On chain storage version set, while the pallet \ doesn't have the `#[pallet::storage_version(VERSION)]` attribute." - .into() + .into() ); }); } @@ -2478,3 +2486,56 @@ fn test_error_feature_parsing() { pallet::Error::__Ignore(_, _) => (), } } + +#[test] +fn pallet_metadata() { + use sp_metadata_ir::{DeprecationInfoIR, DeprecationStatusIR}; + let pallets = Runtime::metadata_ir().pallets; + let example = pallets[0].clone(); + let example2 = pallets[1].clone(); + { + // Example2 pallet is deprecated + assert_eq!( + &DeprecationStatusIR::Deprecated { note: "test", since: None }, + &example2.deprecation_info + ) + } + { + // Example pallet calls is fully and partially deprecated + let meta = &example.calls.unwrap(); + assert_eq!( + DeprecationInfoIR::VariantsDeprecated(BTreeMap::from([( + codec::Compact(0), + DeprecationStatusIR::Deprecated { note: "test", since: None } + )])), + meta.deprecation_info + ) + } + { + // Example pallet errors are partially and fully deprecated + let meta = &example.error.unwrap(); + assert_eq!( + DeprecationInfoIR::VariantsDeprecated(BTreeMap::from([( + codec::Compact(2), + DeprecationStatusIR::Deprecated { note: "test", since: None } + )])), + meta.deprecation_info + ) + } + { + // Example pallet events are partially and fully deprecated + let meta = example.event.unwrap(); + assert_eq!( + DeprecationInfoIR::ItemDeprecated(DeprecationStatusIR::Deprecated { + note: "test", + since: None + }), + meta.deprecation_info + ); + } + { + // Example2 pallet events are not deprecated + let meta = example2.event.unwrap(); + assert_eq!(DeprecationInfoIR::NotDeprecated, meta.deprecation_info); + } +} diff --git a/substrate/frame/support/test/tests/pallet_ui/call_argument_invalid_bound.stderr b/substrate/frame/support/test/tests/pallet_ui/call_argument_invalid_bound.stderr index 2a4ceecd8fa4b361241378e2fa6b71fa5b2ffda1..1f91f7740238622275f09f803ccea748d260b966 100644 --- a/substrate/frame/support/test/tests/pallet_ui/call_argument_invalid_bound.stderr +++ b/substrate/frame/support/test/tests/pallet_ui/call_argument_invalid_bound.stderr @@ -33,3 +33,12 @@ error[E0369]: binary operation `==` cannot be applied to type `&, _bar: T::Bar) -> DispatchResultWithPostInfo { | ^^^^ + +error: unused variable: `origin` + --> tests/pallet_ui/call_argument_invalid_bound.rs:38:14 + | +38 | pub fn foo(origin: OriginFor, _bar: T::Bar) -> DispatchResultWithPostInfo { + | ^^^^^^ help: if this is intentional, prefix it with an underscore: `_origin` + | + = note: `-D unused-variables` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(unused_variables)]` diff --git a/substrate/frame/support/test/tests/pallet_ui/call_argument_invalid_bound_2.stderr b/substrate/frame/support/test/tests/pallet_ui/call_argument_invalid_bound_2.stderr index fc993e9ff68f522d5bd29609ffadb22e06b4f20b..4657c0a0c601f96709cee2ace0c45c7cdf436bef 100644 --- a/substrate/frame/support/test/tests/pallet_ui/call_argument_invalid_bound_2.stderr +++ b/substrate/frame/support/test/tests/pallet_ui/call_argument_invalid_bound_2.stderr @@ -34,7 +34,7 @@ error[E0369]: binary operation `==` cannot be applied to type `&, _bar: T::Bar) -> DispatchResultWithPostInfo { | ^^^^ -error[E0277]: the trait bound `::Bar: WrapperTypeEncode` is not satisfied +error[E0277]: the trait bound `::Bar: Encode` is not satisfied --> tests/pallet_ui/call_argument_invalid_bound_2.rs:38:36 | 18 | #[frame_support::pallet] @@ -45,10 +45,19 @@ error[E0277]: the trait bound `::Bar: WrapperTypeEncode` is | = note: required for `::Bar` to implement `Encode` -error[E0277]: the trait bound `::Bar: WrapperTypeDecode` is not satisfied +error[E0277]: the trait bound `::Bar: Decode` is not satisfied --> tests/pallet_ui/call_argument_invalid_bound_2.rs:38:42 | 38 | pub fn foo(origin: OriginFor, _bar: T::Bar) -> DispatchResultWithPostInfo { | ^^^^^^ the trait `WrapperTypeDecode` is not implemented for `::Bar`, which is required by `::Bar: Decode` | = note: required for `::Bar` to implement `Decode` + +error: unused variable: `origin` + --> tests/pallet_ui/call_argument_invalid_bound_2.rs:38:14 + | +38 | pub fn foo(origin: OriginFor, _bar: T::Bar) -> DispatchResultWithPostInfo { + | ^^^^^^ help: if this is intentional, prefix it with an underscore: `_origin` + | + = note: `-D unused-variables` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(unused_variables)]` diff --git a/substrate/frame/support/test/tests/pallet_ui/call_argument_invalid_bound_3.stderr b/substrate/frame/support/test/tests/pallet_ui/call_argument_invalid_bound_3.stderr index d6486a490794d51caf6fa38b9e90d596447a5dfb..f829baeb4c112727b6eccc845494831833983b92 100644 --- a/substrate/frame/support/test/tests/pallet_ui/call_argument_invalid_bound_3.stderr +++ b/substrate/frame/support/test/tests/pallet_ui/call_argument_invalid_bound_3.stderr @@ -27,3 +27,12 @@ help: consider annotating `Bar` with `#[derive(Debug)]` 34 + #[derive(Debug)] 35 | struct Bar; | + +error: unused variable: `origin` + --> tests/pallet_ui/call_argument_invalid_bound_3.rs:40:14 + | +40 | pub fn foo(origin: OriginFor, _bar: Bar) -> DispatchResultWithPostInfo { + | ^^^^^^ help: if this is intentional, prefix it with an underscore: `_origin` + | + = note: `-D unused-variables` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(unused_variables)]` diff --git a/substrate/frame/support/test/tests/pallet_ui/call_span_for_error.rs b/substrate/frame/support/test/tests/pallet_ui/call_span_for_error.rs new file mode 100644 index 0000000000000000000000000000000000000000..08b42c29a68b60faf4283bd23f5858294ba847b6 --- /dev/null +++ b/substrate/frame/support/test/tests/pallet_ui/call_span_for_error.rs @@ -0,0 +1,37 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#[frame_support::pallet(dev_mode)] +mod pallet { + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::call] + impl Pallet { + pub fn foo(origin: OriginFor) -> DispatchResultWithPostInfo { + return Err(DispatchError::BadOrigin); + } + } +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/pallet_ui/call_span_for_error.stderr b/substrate/frame/support/test/tests/pallet_ui/call_span_for_error.stderr new file mode 100644 index 0000000000000000000000000000000000000000..8f3003c02227d00144a34014e9e13a2c5b4d0032 --- /dev/null +++ b/substrate/frame/support/test/tests/pallet_ui/call_span_for_error.stderr @@ -0,0 +1,26 @@ +error[E0308]: mismatched types + --> tests/pallet_ui/call_span_for_error.rs:32:15 + | +32 | return Err(DispatchError::BadOrigin); + | --- ^^^^^^^^^^^^^^^^^^^^^^^^ expected `DispatchErrorWithPostInfo`, found `DispatchError` + | | + | arguments to this enum variant are incorrect + | + = note: expected struct `DispatchErrorWithPostInfo` + found enum `frame_support::pallet_prelude::DispatchError` +help: the type constructed contains `frame_support::pallet_prelude::DispatchError` due to the type of the argument passed + --> tests/pallet_ui/call_span_for_error.rs:32:11 + | +32 | return Err(DispatchError::BadOrigin); + | ^^^^------------------------^ + | | + | this argument influences the type of `Err` +note: tuple variant defined here + --> $RUST/core/src/result.rs + | + | Err(#[stable(feature = "rust1", since = "1.0.0")] E), + | ^^^ +help: call `Into::into` on this expression to convert `frame_support::pallet_prelude::DispatchError` into `DispatchErrorWithPostInfo` + | +32 | return Err(DispatchError::BadOrigin.into()); + | +++++++ diff --git a/substrate/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid5.stderr b/substrate/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid5.stderr index e12fbfcf4b48ce82fcc0863878158f707a22d130..477dc05d2e7318f826940e8817d49c4d56a21c4b 100644 --- a/substrate/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid5.stderr +++ b/substrate/frame/support/test/tests/pallet_ui/call_weight_inherited_invalid5.stderr @@ -1,10 +1,10 @@ -error: unexpected token +error: unexpected token, expected `)` --> tests/pallet_ui/call_weight_inherited_invalid5.rs:31:50 | 31 | #[pallet::call(weight(::WeightInfo straycat))] | ^^^^^^^^ -error: unexpected token +error: unexpected token, expected `)` --> tests/pallet_ui/call_weight_inherited_invalid5.rs:51:52 | 51 | #[pallet::call(weight = ::WeightInfo straycat)] 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 3256e69528a2d28527ccfe1fa4d77dadfc77baa1..8049c07648cadd8e86c5249f315deca243390bf3 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 @@ -5,3 +5,9 @@ error[E0369]: binary operation `!=` cannot be applied to type `NoStorageVersionS | ------------------------------- ^^ -------------------------------- StorageVersion | | | NoStorageVersionSet + | +note: the foreign item type `NoStorageVersionSet` doesn't implement `PartialEq` + --> $WORKSPACE/substrate/frame/support/src/traits/metadata.rs + | + | pub struct NoStorageVersionSet; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not implement `PartialEq` diff --git a/substrate/frame/support/test/tests/pallet_ui/dev_mode_without_arg_max_encoded_len.stderr b/substrate/frame/support/test/tests/pallet_ui/dev_mode_without_arg_max_encoded_len.stderr index 629fefebbe2c709147c29ad307ca58bea79b2440..2fcc3328214082b97bfdda3e3acabf73ddd9b9be 100644 --- a/substrate/frame/support/test/tests/pallet_ui/dev_mode_without_arg_max_encoded_len.stderr +++ b/substrate/frame/support/test/tests/pallet_ui/dev_mode_without_arg_max_encoded_len.stderr @@ -38,13 +38,13 @@ error[E0277]: the trait bound `Vec: MaxEncodedLen` is not satisfied | |__________________^ the trait `MaxEncodedLen` is not implemented for `Vec`, which is required by `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageMyStorage, Vec>: StorageInfoTrait` | = help: the following other types implement trait `MaxEncodedLen`: - bool - i8 - i16 - i32 - i64 - i128 - u8 - u16 + () + (TupleElement0, TupleElement1) + (TupleElement0, TupleElement1, TupleElement2) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6, TupleElement7) and $N others = note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageMyStorage, Vec>` to implement `StorageInfoTrait` 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 44d8d3fcadbfdfe0b5e66d4a71796e8a7b40f7f8..92fb5b9cb38ddfdfe381d592a3f3ed0f2387a5b3 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 @@ -5,12 +5,12 @@ error[E0277]: the trait bound `MyError: PalletError` is not satisfied | ^^^^^^^^^^^^^^ the trait `PalletError` is not implemented for `MyError` | = help: the following other types implement trait `PalletError`: - bool - i8 - i16 - i32 - i64 - i128 - u8 - u16 + () + (TupleElement0, TupleElement1) + (TupleElement0, TupleElement1, TupleElement2) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6, TupleElement7) and $N others diff --git a/substrate/frame/support/test/tests/pallet_ui/hooks_invalid_item.stderr b/substrate/frame/support/test/tests/pallet_ui/hooks_invalid_item.stderr index b7327943ee20e66433326e7922a50919c530ef7c..c04499dbbd144d2c85a84c908544041dd8603387 100644 --- a/substrate/frame/support/test/tests/pallet_ui/hooks_invalid_item.stderr +++ b/substrate/frame/support/test/tests/pallet_ui/hooks_invalid_item.stderr @@ -13,3 +13,9 @@ help: add missing generic argument | 29 | impl Hooks for Pallet {} | +++++++++++++ + +error[E0277]: the trait bound `pallet::Pallet: Hooks<<<::Block as frame_support::sp_runtime::traits::Block>::Header as frame_support::sp_runtime::traits::Header>::Number>` is not satisfied + --> tests/pallet_ui/hooks_invalid_item.rs:28:12 + | +28 | #[pallet::hooks] + | ^^^^^ the trait `Hooks<<<::Block as frame_support::sp_runtime::traits::Block>::Header as frame_support::sp_runtime::traits::Header>::Number>` is not implemented for `pallet::Pallet` diff --git a/substrate/frame/support/test/tests/pallet_ui/inherent_check_inner_span.stderr b/substrate/frame/support/test/tests/pallet_ui/inherent_check_inner_span.stderr index 5ea3be470a068b1468beb754b6b4caeca25d93e4..516bddd2c61b9ac1b3de9ff357ed330a85e31467 100644 --- a/substrate/frame/support/test/tests/pallet_ui/inherent_check_inner_span.stderr +++ b/substrate/frame/support/test/tests/pallet_ui/inherent_check_inner_span.stderr @@ -6,6 +6,6 @@ error[E0046]: not all trait items implemented, missing: `Call`, `Error`, `INHERE | = help: implement the missing item: `type Call = /* Type */;` = help: implement the missing item: `type Error = /* Type */;` - = help: implement the missing item: `const INHERENT_IDENTIFIER: [u8; 8] = value;` + = help: implement the missing item: `const INHERENT_IDENTIFIER: [u8; 8] = [42; 8];` = help: implement the missing item: `fn create_inherent(_: &InherentData) -> std::option::Option<::Call> { todo!() }` = help: implement the missing item: `fn is_inherent(_: &::Call) -> bool { todo!() }` diff --git a/substrate/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen.stderr b/substrate/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen.stderr index c8c41e8050145be714e53394e95d5fdda0b7dcbd..fa6b7284d889f548561cae7160b7f2da75d0ff36 100644 --- a/substrate/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen.stderr +++ b/substrate/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen.stderr @@ -12,10 +12,10 @@ error[E0277]: the trait bound `Bar: WrapperTypeDecode` is not satisfied | |____________^ the trait `WrapperTypeDecode` is not implemented for `Bar`, which is required by `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>: PartialStorageInfoTrait` | = help: the following other types implement trait `WrapperTypeDecode`: + Arc Box - frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes Rc - Arc + frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes = note: required for `Bar` to implement `Decode` = note: required for `Bar` to implement `FullCodec` = note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>` to implement `PartialStorageInfoTrait` @@ -34,14 +34,14 @@ error[E0277]: the trait bound `Bar: EncodeLike` is not satisfied | |____________^ the trait `EncodeLike` is not implemented for `Bar`, which is required by `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>: PartialStorageInfoTrait` | = help: the following other types implement trait `EncodeLike`: - - - - - - - - + `&&T` implements `EncodeLike` + `&T` implements `EncodeLike` + `&T` implements `EncodeLike` + `&[(K, V)]` implements `EncodeLike>` + `&[(T,)]` implements `EncodeLike>` + `&[(T,)]` implements `EncodeLike>` + `&[(T,)]` implements `EncodeLike>` + `&[T]` implements `EncodeLike>` and $N others = note: required for `Bar` to implement `FullEncode` = note: required for `Bar` to implement `FullCodec` @@ -61,14 +61,14 @@ error[E0277]: the trait bound `Bar: WrapperTypeEncode` is not satisfied | |____________^ the trait `WrapperTypeEncode` is not implemented for `Bar`, which is required by `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>: PartialStorageInfoTrait` | = help: the following other types implement trait `WrapperTypeEncode`: + &T + &mut T + Arc Box - bytes::bytes::Bytes Cow<'a, T> - parity_scale_codec::Ref<'a, T, U> - frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes Rc - Arc Vec + bytes::bytes::Bytes and $N others = note: required for `Bar` to implement `Encode` = note: required for `Bar` to implement `FullEncode` @@ -84,14 +84,14 @@ error[E0277]: the trait bound `Bar: TypeInfo` is not satisfied | |____________^ the trait `TypeInfo` is not implemented for `Bar`, which is required by `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>: StorageEntryMetadataBuilder` | = help: the following other types implement trait `TypeInfo`: - bool - char - i8 - i16 - i32 - i64 - i128 - u8 + &T + &mut T + () + (A, B) + (A, B, C) + (A, B, C, D) + (A, B, C, D, E) + (A, B, C, D, E, F) and $N others = note: required for `Bar` to implement `StaticTypeInfo` = note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>` to implement `StorageEntryMetadataBuilder` @@ -105,10 +105,10 @@ error[E0277]: the trait bound `Bar: WrapperTypeDecode` is not satisfied | |____________^ the trait `WrapperTypeDecode` is not implemented for `Bar`, which is required by `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>: StorageEntryMetadataBuilder` | = help: the following other types implement trait `WrapperTypeDecode`: + Arc Box - frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes Rc - Arc + frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes = note: required for `Bar` to implement `Decode` = note: required for `Bar` to implement `FullCodec` = note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>` to implement `StorageEntryMetadataBuilder` @@ -122,14 +122,14 @@ error[E0277]: the trait bound `Bar: EncodeLike` is not satisfied | |____________^ the trait `EncodeLike` is not implemented for `Bar`, which is required by `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>: StorageEntryMetadataBuilder` | = help: the following other types implement trait `EncodeLike`: - - - - - - - - + `&&T` implements `EncodeLike` + `&T` implements `EncodeLike` + `&T` implements `EncodeLike` + `&[(K, V)]` implements `EncodeLike>` + `&[(T,)]` implements `EncodeLike>` + `&[(T,)]` implements `EncodeLike>` + `&[(T,)]` implements `EncodeLike>` + `&[T]` implements `EncodeLike>` and $N others = note: required for `Bar` to implement `FullEncode` = note: required for `Bar` to implement `FullCodec` @@ -144,14 +144,14 @@ error[E0277]: the trait bound `Bar: WrapperTypeEncode` is not satisfied | |____________^ the trait `WrapperTypeEncode` is not implemented for `Bar`, which is required by `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>: StorageEntryMetadataBuilder` | = help: the following other types implement trait `WrapperTypeEncode`: + &T + &mut T + Arc Box - bytes::bytes::Bytes Cow<'a, T> - parity_scale_codec::Ref<'a, T, U> - frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes Rc - Arc Vec + bytes::bytes::Bytes and $N others = note: required for `Bar` to implement `Encode` = note: required for `Bar` to implement `FullEncode` @@ -167,10 +167,10 @@ error[E0277]: the trait bound `Bar: WrapperTypeDecode` is not satisfied | |____________^ the trait `WrapperTypeDecode` is not implemented for `Bar`, which is required by `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>: TryDecodeEntireStorage` | = help: the following other types implement trait `WrapperTypeDecode`: + Arc Box - frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes Rc - Arc + frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes = note: required for `Bar` to implement `Decode` = note: required for `Bar` to implement `FullCodec` = note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>` to implement `TryDecodeEntireStorage` @@ -184,14 +184,14 @@ error[E0277]: the trait bound `Bar: EncodeLike` is not satisfied | |____________^ the trait `EncodeLike` is not implemented for `Bar`, which is required by `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>: TryDecodeEntireStorage` | = help: the following other types implement trait `EncodeLike`: - - - - - - - - + `&&T` implements `EncodeLike` + `&T` implements `EncodeLike` + `&T` implements `EncodeLike` + `&[(K, V)]` implements `EncodeLike>` + `&[(T,)]` implements `EncodeLike>` + `&[(T,)]` implements `EncodeLike>` + `&[(T,)]` implements `EncodeLike>` + `&[T]` implements `EncodeLike>` and $N others = note: required for `Bar` to implement `FullEncode` = note: required for `Bar` to implement `FullCodec` @@ -206,14 +206,14 @@ error[E0277]: the trait bound `Bar: WrapperTypeEncode` is not satisfied | |____________^ the trait `WrapperTypeEncode` is not implemented for `Bar`, which is required by `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>: TryDecodeEntireStorage` | = help: the following other types implement trait `WrapperTypeEncode`: + &T + &mut T + Arc Box - bytes::bytes::Bytes Cow<'a, T> - parity_scale_codec::Ref<'a, T, U> - frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes Rc - Arc Vec + bytes::bytes::Bytes and $N others = note: required for `Bar` to implement `Encode` = note: required for `Bar` to implement `FullEncode` diff --git a/substrate/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen_unnamed.stderr b/substrate/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen_unnamed.stderr index 08b35eb8ed1536d83678e5b438687cdef51cf91f..944b194b7bcf132971ae5c4dd081e075a4380625 100644 --- a/substrate/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen_unnamed.stderr +++ b/substrate/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen_unnamed.stderr @@ -12,10 +12,10 @@ error[E0277]: the trait bound `Bar: WrapperTypeDecode` is not satisfied | |____________^ the trait `WrapperTypeDecode` is not implemented for `Bar`, which is required by `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>: PartialStorageInfoTrait` | = help: the following other types implement trait `WrapperTypeDecode`: + Arc Box - frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes Rc - Arc + frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes = note: required for `Bar` to implement `Decode` = note: required for `Bar` to implement `FullCodec` = note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>` to implement `PartialStorageInfoTrait` @@ -34,14 +34,14 @@ error[E0277]: the trait bound `Bar: EncodeLike` is not satisfied | |____________^ the trait `EncodeLike` is not implemented for `Bar`, which is required by `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>: PartialStorageInfoTrait` | = help: the following other types implement trait `EncodeLike`: - - - - - - - - + `&&T` implements `EncodeLike` + `&T` implements `EncodeLike` + `&T` implements `EncodeLike` + `&[(K, V)]` implements `EncodeLike>` + `&[(T,)]` implements `EncodeLike>` + `&[(T,)]` implements `EncodeLike>` + `&[(T,)]` implements `EncodeLike>` + `&[T]` implements `EncodeLike>` and $N others = note: required for `Bar` to implement `FullEncode` = note: required for `Bar` to implement `FullCodec` @@ -61,14 +61,14 @@ error[E0277]: the trait bound `Bar: WrapperTypeEncode` is not satisfied | |____________^ the trait `WrapperTypeEncode` is not implemented for `Bar`, which is required by `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>: PartialStorageInfoTrait` | = help: the following other types implement trait `WrapperTypeEncode`: + &T + &mut T + Arc Box - bytes::bytes::Bytes Cow<'a, T> - parity_scale_codec::Ref<'a, T, U> - frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes Rc - Arc Vec + bytes::bytes::Bytes and $N others = note: required for `Bar` to implement `Encode` = note: required for `Bar` to implement `FullEncode` @@ -84,14 +84,14 @@ error[E0277]: the trait bound `Bar: TypeInfo` is not satisfied | |____________^ the trait `TypeInfo` is not implemented for `Bar`, which is required by `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>: StorageEntryMetadataBuilder` | = help: the following other types implement trait `TypeInfo`: - bool - char - i8 - i16 - i32 - i64 - i128 - u8 + &T + &mut T + () + (A, B) + (A, B, C) + (A, B, C, D) + (A, B, C, D, E) + (A, B, C, D, E, F) and $N others = note: required for `Bar` to implement `StaticTypeInfo` = note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>` to implement `StorageEntryMetadataBuilder` @@ -105,10 +105,10 @@ error[E0277]: the trait bound `Bar: WrapperTypeDecode` is not satisfied | |____________^ the trait `WrapperTypeDecode` is not implemented for `Bar`, which is required by `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>: StorageEntryMetadataBuilder` | = help: the following other types implement trait `WrapperTypeDecode`: + Arc Box - frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes Rc - Arc + frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes = note: required for `Bar` to implement `Decode` = note: required for `Bar` to implement `FullCodec` = note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>` to implement `StorageEntryMetadataBuilder` @@ -122,14 +122,14 @@ error[E0277]: the trait bound `Bar: EncodeLike` is not satisfied | |____________^ the trait `EncodeLike` is not implemented for `Bar`, which is required by `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>: StorageEntryMetadataBuilder` | = help: the following other types implement trait `EncodeLike`: - - - - - - - - + `&&T` implements `EncodeLike` + `&T` implements `EncodeLike` + `&T` implements `EncodeLike` + `&[(K, V)]` implements `EncodeLike>` + `&[(T,)]` implements `EncodeLike>` + `&[(T,)]` implements `EncodeLike>` + `&[(T,)]` implements `EncodeLike>` + `&[T]` implements `EncodeLike>` and $N others = note: required for `Bar` to implement `FullEncode` = note: required for `Bar` to implement `FullCodec` @@ -144,14 +144,14 @@ error[E0277]: the trait bound `Bar: WrapperTypeEncode` is not satisfied | |____________^ the trait `WrapperTypeEncode` is not implemented for `Bar`, which is required by `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>: StorageEntryMetadataBuilder` | = help: the following other types implement trait `WrapperTypeEncode`: + &T + &mut T + Arc Box - bytes::bytes::Bytes Cow<'a, T> - parity_scale_codec::Ref<'a, T, U> - frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes Rc - Arc Vec + bytes::bytes::Bytes and $N others = note: required for `Bar` to implement `Encode` = note: required for `Bar` to implement `FullEncode` @@ -167,10 +167,10 @@ error[E0277]: the trait bound `Bar: WrapperTypeDecode` is not satisfied | |____________^ the trait `WrapperTypeDecode` is not implemented for `Bar`, which is required by `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>: TryDecodeEntireStorage` | = help: the following other types implement trait `WrapperTypeDecode`: + Arc Box - frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes Rc - Arc + frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes = note: required for `Bar` to implement `Decode` = note: required for `Bar` to implement `FullCodec` = note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>` to implement `TryDecodeEntireStorage` @@ -184,14 +184,14 @@ error[E0277]: the trait bound `Bar: EncodeLike` is not satisfied | |____________^ the trait `EncodeLike` is not implemented for `Bar`, which is required by `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>: TryDecodeEntireStorage` | = help: the following other types implement trait `EncodeLike`: - - - - - - - - + `&&T` implements `EncodeLike` + `&T` implements `EncodeLike` + `&T` implements `EncodeLike` + `&[(K, V)]` implements `EncodeLike>` + `&[(T,)]` implements `EncodeLike>` + `&[(T,)]` implements `EncodeLike>` + `&[(T,)]` implements `EncodeLike>` + `&[T]` implements `EncodeLike>` and $N others = note: required for `Bar` to implement `FullEncode` = note: required for `Bar` to implement `FullCodec` @@ -206,14 +206,14 @@ error[E0277]: the trait bound `Bar: WrapperTypeEncode` is not satisfied | |____________^ the trait `WrapperTypeEncode` is not implemented for `Bar`, which is required by `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>: TryDecodeEntireStorage` | = help: the following other types implement trait `WrapperTypeEncode`: + &T + &mut T + Arc Box - bytes::bytes::Bytes Cow<'a, T> - parity_scale_codec::Ref<'a, T, U> - frame_support::sp_runtime::sp_application_crypto::sp_core::Bytes Rc - Arc Vec + bytes::bytes::Bytes and $N others = note: required for `Bar` to implement `Encode` = note: required for `Bar` to implement `FullEncode` diff --git a/substrate/frame/support/test/tests/pallet_ui/storage_info_unsatisfied.stderr b/substrate/frame/support/test/tests/pallet_ui/storage_info_unsatisfied.stderr index 042a6f67fd316c27a802c14f2497d86f6c9bd5bc..95ec76e29c0be79585d5902b87694804be88c148 100644 --- a/substrate/frame/support/test/tests/pallet_ui/storage_info_unsatisfied.stderr +++ b/substrate/frame/support/test/tests/pallet_ui/storage_info_unsatisfied.stderr @@ -12,13 +12,13 @@ error[E0277]: the trait bound `Bar: MaxEncodedLen` is not satisfied | |____________^ the trait `MaxEncodedLen` is not implemented for `Bar`, which is required by `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>: StorageInfoTrait` | = help: the following other types implement trait `MaxEncodedLen`: - bool - i8 - i16 - i32 - i64 - i128 - u8 - u16 + () + (TupleElement0, TupleElement1) + (TupleElement0, TupleElement1, TupleElement2) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6, TupleElement7) and $N others = note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>` to implement `StorageInfoTrait` diff --git a/substrate/frame/support/test/tests/pallet_ui/storage_info_unsatisfied_nmap.stderr b/substrate/frame/support/test/tests/pallet_ui/storage_info_unsatisfied_nmap.stderr index 9f57b85f3a8a3ae0797efb618c1f8c1123da070d..8351dd92d5944c14ebd1ccd1b6d17c3eaf017f4d 100644 --- a/substrate/frame/support/test/tests/pallet_ui/storage_info_unsatisfied_nmap.stderr +++ b/substrate/frame/support/test/tests/pallet_ui/storage_info_unsatisfied_nmap.stderr @@ -12,14 +12,14 @@ error[E0277]: the trait bound `Bar: MaxEncodedLen` is not satisfied | |____________^ the trait `MaxEncodedLen` is not implemented for `Bar`, which is required by `frame_support::pallet_prelude::StorageNMap<_GeneratedPrefixForStorageFoo, NMapKey, u32>: StorageInfoTrait` | = help: the following other types implement trait `MaxEncodedLen`: - bool - i8 - i16 - i32 - i64 - i128 - u8 - u16 + () + (TupleElement0, TupleElement1) + (TupleElement0, TupleElement1, TupleElement2) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6) + (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6, TupleElement7) and $N others = note: required for `NMapKey` to implement `KeyGeneratorMaxEncodedLen` = note: required for `frame_support::pallet_prelude::StorageNMap<_GeneratedPrefixForStorageFoo, NMapKey, u32>` to implement `StorageInfoTrait` diff --git a/substrate/frame/support/test/tests/pallet_ui/type_value_error_in_block.stderr b/substrate/frame/support/test/tests/pallet_ui/type_value_error_in_block.stderr index 41dcd273d9620d4151bdd6e7ba65dd9c24174322..0b13dcff90c68ceede6b4bbd119b217548aa486f 100644 --- a/substrate/frame/support/test/tests/pallet_ui/type_value_error_in_block.stderr +++ b/substrate/frame/support/test/tests/pallet_ui/type_value_error_in_block.stderr @@ -3,3 +3,9 @@ error[E0599]: no function or associated item named `new` found for type `u32` in | 37 | u32::new() | ^^^ function or associated item not found in `u32` + | +help: there is a method `ne` with a similar name, but with different arguments + --> $RUST/core/src/cmp.rs + | + | fn ne(&self, other: &Rhs) -> bool { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/substrate/frame/support/test/tests/runtime_metadata.rs b/substrate/frame/support/test/tests/runtime_metadata.rs index 48e4d975eb083ec38baf6521cac1891b1e5e3d82..81377210eb43f8aa90f9e4ba795e816793002d25 100644 --- a/substrate/frame/support/test/tests/runtime_metadata.rs +++ b/substrate/frame/support/test/tests/runtime_metadata.rs @@ -14,11 +14,13 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +#![allow(useless_deprecated, deprecated, clippy::deprecated_semver)] use frame_support::{derive_impl, traits::ConstU32}; use scale_info::{form::MetaForm, meta_type}; use sp_metadata_ir::{ - RuntimeApiMetadataIR, RuntimeApiMethodMetadataIR, RuntimeApiMethodParamMetadataIR, + DeprecationStatusIR, RuntimeApiMetadataIR, RuntimeApiMethodMetadataIR, + RuntimeApiMethodParamMetadataIR, }; use sp_runtime::traits::Block as BlockT; @@ -64,12 +66,16 @@ sp_api::decl_runtime_apis! { /// ApiWithCustomVersion trait documentation /// /// Documentation on multiline. + #[deprecated] pub trait Api { fn test(data: u64); /// something_with_block. fn something_with_block(block: Block) -> Block; + #[deprecated = "example"] fn function_with_two_args(data: u64, block: Block); + #[deprecated(note = "example", since = "example")] fn same_name(); + #[deprecated(note = "example")] fn wild_card(_: u32); } } @@ -128,6 +134,7 @@ fn runtime_metadata() { }], output: meta_type::<()>(), docs: vec![], + deprecation_info: DeprecationStatusIR::NotDeprecated, }, RuntimeApiMethodMetadataIR { name: "something_with_block", @@ -137,6 +144,7 @@ fn runtime_metadata() { }], output: meta_type::(), docs: maybe_docs(vec![" something_with_block."]), + deprecation_info: DeprecationStatusIR::NotDeprecated, }, RuntimeApiMethodMetadataIR { name: "function_with_two_args", @@ -152,13 +160,21 @@ fn runtime_metadata() { ], output: meta_type::<()>(), docs: vec![], + deprecation_info: DeprecationStatusIR::Deprecated { + note: "example", + since: None, + } }, RuntimeApiMethodMetadataIR { name: "same_name", inputs: vec![], output: meta_type::<()>(), docs: vec![], - }, + deprecation_info: DeprecationStatusIR::Deprecated { + note: "example", + since: Some("example"), + } + }, RuntimeApiMethodMetadataIR { name: "wild_card", inputs: vec![RuntimeApiMethodParamMetadataIR:: { @@ -167,6 +183,10 @@ fn runtime_metadata() { }], output: meta_type::<()>(), docs: vec![], + deprecation_info: DeprecationStatusIR::Deprecated { + note: "example", + since: None, + } }, ], docs: maybe_docs(vec![ @@ -174,6 +194,8 @@ fn runtime_metadata() { "", " Documentation on multiline.", ]), + deprecation_info: DeprecationStatusIR::DeprecatedWithoutNote, + }, RuntimeApiMetadataIR { name: "Core", @@ -183,6 +205,7 @@ fn runtime_metadata() { inputs: vec![], output: meta_type::(), docs: maybe_docs(vec![" Returns the version of the runtime."]), + deprecation_info: DeprecationStatusIR::NotDeprecated, }, RuntimeApiMethodMetadataIR { name: "execute_block", @@ -192,6 +215,8 @@ fn runtime_metadata() { }], output: meta_type::<()>(), docs: maybe_docs(vec![" Execute the given block."]), + deprecation_info: DeprecationStatusIR::NotDeprecated, + }, RuntimeApiMethodMetadataIR { name: "initialize_block", @@ -201,11 +226,13 @@ fn runtime_metadata() { }], output: meta_type::(), docs: maybe_docs(vec![" Initialize a block with the given header and return the runtime executive mode."]), + deprecation_info: DeprecationStatusIR::NotDeprecated, }, ], docs: maybe_docs(vec![ " The `Core` runtime api that every Substrate runtime needs to implement.", ]), + deprecation_info: DeprecationStatusIR::NotDeprecated, }, ]; diff --git a/substrate/frame/system/src/lib.rs b/substrate/frame/system/src/lib.rs index abacfa7b62ccc0bb7af388f7a0b16511ce87989d..a5c5f1ed2e47032338ff5424ea483d70d68e64df 100644 --- a/substrate/frame/system/src/lib.rs +++ b/substrate/frame/system/src/lib.rs @@ -175,6 +175,7 @@ pub use extensions::{ pub use extensions::check_mortality::CheckMortality as CheckEra; pub use frame_support::dispatch::RawOrigin; use frame_support::traits::{PostInherents, PostTransactions, PreInherents}; +use sp_core::storage::StateVersion; pub use weights::WeightInfo; const LOG_TARGET: &str = "runtime::system"; @@ -182,17 +183,20 @@ const LOG_TARGET: &str = "runtime::system"; /// Compute the trie root of a list of extrinsics. /// /// The merkle proof is using the same trie as runtime state with -/// `state_version` 0. -pub fn extrinsics_root(extrinsics: &[E]) -> H::Output { - extrinsics_data_root::(extrinsics.iter().map(codec::Encode::encode).collect()) +/// `state_version` 0 or 1. +pub fn extrinsics_root( + extrinsics: &[E], + state_version: StateVersion, +) -> H::Output { + extrinsics_data_root::(extrinsics.iter().map(codec::Encode::encode).collect(), state_version) } /// Compute the trie root of a list of extrinsics. /// /// The merkle proof is using the same trie as runtime state with -/// `state_version` 0. -pub fn extrinsics_data_root(xts: Vec>) -> H::Output { - H::ordered_trie_root(xts, sp_core::storage::StateVersion::V0) +/// `state_version` 0 or 1. +pub fn extrinsics_data_root(xts: Vec>, state_version: StateVersion) -> H::Output { + H::ordered_trie_root(xts, state_version) } /// An object to track the currently used extrinsic weight in a block. @@ -360,7 +364,7 @@ pub mod pallet { type MaxConsumers = frame_support::traits::ConstU32<128>; /// The default data to be stored in an account. - type AccountData = crate::AccountInfo; + type AccountData = (); /// What to do if a new account is created. type OnNewAccount = (); @@ -1847,7 +1851,9 @@ impl Pallet { let extrinsics = (0..ExtrinsicCount::::take().unwrap_or_default()) .map(ExtrinsicData::::take) .collect(); - let extrinsics_root = extrinsics_data_root::(extrinsics); + let extrinsics_root_state_version = T::Version::get().extrinsics_root_state_version(); + let extrinsics_root = + extrinsics_data_root::(extrinsics, extrinsics_root_state_version); // move block hash pruning window by one block let block_hash_count = T::BlockHashCount::get(); diff --git a/substrate/frame/system/src/mock.rs b/substrate/frame/system/src/mock.rs index fff848b3b0e50bde09527856f76c6ae3c84f6ea7..f43ffe3c87eed971f1debd0fbd575ec6f3af4658 100644 --- a/substrate/frame/system/src/mock.rs +++ b/substrate/frame/system/src/mock.rs @@ -40,7 +40,7 @@ parameter_types! { impl_version: 1, apis: sp_version::create_apis_vec!([]), transaction_version: 1, - state_version: 1, + system_version: 1, }; pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { read: 10, diff --git a/substrate/frame/system/src/tests.rs b/substrate/frame/system/src/tests.rs index b2cd017e1e206d9bf0f486bc964a4514789dcadc..aa1094e3fe4800cc3268d14611fdb927d4247967 100644 --- a/substrate/frame/system/src/tests.rs +++ b/substrate/frame/system/src/tests.rs @@ -789,7 +789,10 @@ fn extrinsics_root_is_calculated_correctly() { System::note_finished_extrinsics(); let header = System::finalize(); - let ext_root = extrinsics_data_root::(vec![vec![1], vec![2]]); + let ext_root = extrinsics_data_root::( + vec![vec![1], vec![2]], + sp_core::storage::StateVersion::V0, + ); assert_eq!(ext_root, *header.extrinsics_root()); }); } @@ -845,6 +848,7 @@ pub fn from_post_weight_info(ref_time: Option, pays_fee: Pays) -> PostDispa #[docify::export] #[test] fn last_runtime_upgrade_spec_version_usage() { + #[allow(dead_code)] struct Migration; impl OnRuntimeUpgrade for Migration { diff --git a/substrate/frame/transaction-payment/src/tests.rs b/substrate/frame/transaction-payment/src/tests.rs index 35d5322a6f33f40a97a00805dffb9f2a76b2a7cf..bac89967d6af89040d6ddf763825fa525009276c 100644 --- a/substrate/frame/transaction-payment/src/tests.rs +++ b/substrate/frame/transaction-payment/src/tests.rs @@ -273,8 +273,10 @@ fn signed_ext_length_fee_is_also_updated_per_congestion() { NextFeeMultiplier::::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)); + assert_ok!( + ChargeTransactionPayment::::from(10) // tipped + .pre_dispatch(&1, CALL, &info_from_weight(Weight::from_parts(3, 0)), len) + ); assert_eq!( Balances::free_balance(1), 100 // original diff --git a/substrate/frame/treasury/Cargo.toml b/substrate/frame/treasury/Cargo.toml index 55bdd4f7a4986b093b7d22c26751e9d6a98f5b66..93a3d9bea93d1b78f01e19206a4b1b9d31e31ba4 100644 --- a/substrate/frame/treasury/Cargo.toml +++ b/substrate/frame/treasury/Cargo.toml @@ -30,6 +30,7 @@ frame-system = { workspace = true } pallet-balances = { workspace = true } sp-runtime = { workspace = true } sp-core = { optional = true, workspace = true } +log = { workspace = true } [dev-dependencies] sp-io = { workspace = true, default-features = true } @@ -43,6 +44,7 @@ std = [ "frame-benchmarking?/std", "frame-support/std", "frame-system/std", + "log/std", "pallet-balances/std", "pallet-utility/std", "scale-info/std", diff --git a/substrate/frame/treasury/src/benchmarking.rs b/substrate/frame/treasury/src/benchmarking.rs index e63febb5798b4f598cc37f4d200c3cc8ee7ab44b..650e5376fa4b285f3a289271b922a9e3b47a4819 100644 --- a/substrate/frame/treasury/src/benchmarking.rs +++ b/substrate/frame/treasury/src/benchmarking.rs @@ -26,7 +26,7 @@ use frame_benchmarking::{ v2::*, }; use frame_support::{ - ensure, + assert_err, assert_ok, ensure, traits::{ tokens::{ConversionFromAssetBalance, PaymentStatus}, EnsureOrigin, OnInitialize, @@ -73,12 +73,19 @@ fn setup_proposal, I: 'static>( // Create proposals that are approved for use in `on_initialize`. fn create_approved_proposals, I: 'static>(n: u32) -> Result<(), &'static str> { - let origin = T::SpendOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + let spender = T::SpendOrigin::try_successful_origin(); + for i in 0..n { let (_, value, lookup) = setup_proposal::(i); - Treasury::::spend_local(origin.clone(), value, lookup)?; + + if let Ok(origin) = &spender { + Treasury::::spend_local(origin.clone(), value, lookup)?; + } + } + + if spender.is_ok() { + ensure!(Approvals::::get().len() == n as usize, "Not all approved"); } - ensure!(Approvals::::get().len() == n as usize, "Not all approved"); Ok(()) } @@ -106,8 +113,8 @@ fn create_spend_arguments, I: 'static>( mod benchmarks { use super::*; - // This benchmark is short-circuited if `SpendOrigin` cannot provide - // a successful origin, in which case `spend` is un-callable and can use weight=0. + /// This benchmark is short-circuited if `SpendOrigin` cannot provide + /// a successful origin, in which case `spend` is un-callable and can use weight=0. #[benchmark] fn spend_local() -> Result<(), BenchmarkError> { let (_, value, beneficiary_lookup) = setup_proposal::(SEED); @@ -126,16 +133,31 @@ mod benchmarks { #[benchmark] fn remove_approval() -> Result<(), BenchmarkError> { - let origin = - T::SpendOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - let (_, value, beneficiary_lookup) = setup_proposal::(SEED); - Treasury::::spend_local(origin, value, beneficiary_lookup)?; - let proposal_id = ProposalCount::::get() - 1; + let (spend_exists, proposal_id) = + if let Ok(origin) = T::SpendOrigin::try_successful_origin() { + let (_, value, beneficiary_lookup) = setup_proposal::(SEED); + Treasury::::spend_local(origin, value, beneficiary_lookup)?; + let proposal_id = ProposalCount::::get() - 1; + + (true, proposal_id) + } else { + (false, 0) + }; + let reject_origin = T::RejectOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - #[extrinsic_call] - _(reject_origin as T::RuntimeOrigin, proposal_id); + #[block] + { + let res = + Treasury::::remove_approval(reject_origin as T::RuntimeOrigin, proposal_id); + + if spend_exists { + assert_ok!(res); + } else { + assert_err!(res, Error::::ProposalNotApproved); + } + } Ok(()) } @@ -155,6 +177,8 @@ mod benchmarks { Ok(()) } + /// This benchmark is short-circuited if `SpendOrigin` cannot provide + /// a successful origin, in which case `spend` is un-callable and can use weight=0. #[benchmark] fn spend() -> Result<(), BenchmarkError> { let origin = @@ -190,85 +214,135 @@ mod benchmarks { #[benchmark] fn payout() -> Result<(), BenchmarkError> { - let origin = T::SpendOrigin::try_successful_origin().map_err(|_| "No origin")?; let (asset_kind, amount, beneficiary, beneficiary_lookup) = create_spend_arguments::(SEED); T::BalanceConverter::ensure_successful(asset_kind.clone()); - Treasury::::spend( - origin, - Box::new(asset_kind.clone()), - amount, - Box::new(beneficiary_lookup), - None, - )?; + + let spend_exists = if let Ok(origin) = T::SpendOrigin::try_successful_origin() { + Treasury::::spend( + origin, + Box::new(asset_kind.clone()), + amount, + Box::new(beneficiary_lookup), + None, + )?; + + true + } else { + false + }; + T::Paymaster::ensure_successful(&beneficiary, asset_kind, amount); let caller: T::AccountId = account("caller", 0, SEED); - #[extrinsic_call] - _(RawOrigin::Signed(caller.clone()), 0u32); - - let id = match Spends::::get(0).unwrap().status { - PaymentState::Attempted { id, .. } => { - assert_ne!(T::Paymaster::check_payment(id), PaymentStatus::Failure); - id - }, - _ => panic!("No payout attempt made"), - }; - assert_last_event::(Event::Paid { index: 0, payment_id: id }.into()); - assert!(Treasury::::payout(RawOrigin::Signed(caller).into(), 0u32).is_err()); + #[block] + { + let res = Treasury::::payout(RawOrigin::Signed(caller.clone()).into(), 0u32); + + if spend_exists { + assert_ok!(res); + } else { + assert_err!(res, crate::Error::::InvalidIndex); + } + } + + if spend_exists { + let id = match Spends::::get(0).unwrap().status { + PaymentState::Attempted { id, .. } => { + assert_ne!(T::Paymaster::check_payment(id), PaymentStatus::Failure); + id + }, + _ => panic!("No payout attempt made"), + }; + assert_last_event::(Event::Paid { index: 0, payment_id: id }.into()); + assert!(Treasury::::payout(RawOrigin::Signed(caller).into(), 0u32).is_err()); + } + Ok(()) } #[benchmark] fn check_status() -> Result<(), BenchmarkError> { - let origin = T::SpendOrigin::try_successful_origin().map_err(|_| "No origin")?; let (asset_kind, amount, beneficiary, beneficiary_lookup) = create_spend_arguments::(SEED); + T::BalanceConverter::ensure_successful(asset_kind.clone()); - Treasury::::spend( - origin, - Box::new(asset_kind.clone()), - amount, - Box::new(beneficiary_lookup), - None, - )?; - T::Paymaster::ensure_successful(&beneficiary, asset_kind, amount); + T::Paymaster::ensure_successful(&beneficiary, asset_kind.clone(), amount); let caller: T::AccountId = account("caller", 0, SEED); - Treasury::::payout(RawOrigin::Signed(caller.clone()).into(), 0u32)?; - match Spends::::get(0).unwrap().status { - PaymentState::Attempted { id, .. } => { - T::Paymaster::ensure_concluded(id); - }, - _ => panic!("No payout attempt made"), + + let spend_exists = if let Ok(origin) = T::SpendOrigin::try_successful_origin() { + Treasury::::spend( + origin, + Box::new(asset_kind), + amount, + Box::new(beneficiary_lookup), + None, + )?; + + Treasury::::payout(RawOrigin::Signed(caller.clone()).into(), 0u32)?; + match Spends::::get(0).unwrap().status { + PaymentState::Attempted { id, .. } => { + T::Paymaster::ensure_concluded(id); + }, + _ => panic!("No payout attempt made"), + }; + + true + } else { + false }; - #[extrinsic_call] - _(RawOrigin::Signed(caller.clone()), 0u32); + #[block] + { + let res = + Treasury::::check_status(RawOrigin::Signed(caller.clone()).into(), 0u32); + + if spend_exists { + assert_ok!(res); + } else { + assert_err!(res, crate::Error::::InvalidIndex); + } + } if let Some(s) = Spends::::get(0) { assert!(!matches!(s.status, PaymentState::Attempted { .. })); } + Ok(()) } #[benchmark] fn void_spend() -> Result<(), BenchmarkError> { - let origin = T::SpendOrigin::try_successful_origin().map_err(|_| "No origin")?; let (asset_kind, amount, _, beneficiary_lookup) = create_spend_arguments::(SEED); T::BalanceConverter::ensure_successful(asset_kind.clone()); - Treasury::::spend( - origin, - Box::new(asset_kind.clone()), - amount, - Box::new(beneficiary_lookup), - None, - )?; - assert!(Spends::::get(0).is_some()); + let spend_exists = if let Ok(origin) = T::SpendOrigin::try_successful_origin() { + Treasury::::spend( + origin, + Box::new(asset_kind.clone()), + amount, + Box::new(beneficiary_lookup), + None, + )?; + assert!(Spends::::get(0).is_some()); + + true + } else { + false + }; + let origin = T::RejectOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - #[extrinsic_call] - _(origin as T::RuntimeOrigin, 0u32); + #[block] + { + let res = Treasury::::void_spend(origin as T::RuntimeOrigin, 0u32); + + if spend_exists { + assert_ok!(res); + } else { + assert_err!(res, crate::Error::::InvalidIndex); + } + } assert!(Spends::::get(0).is_none()); Ok(()) @@ -279,4 +353,15 @@ mod benchmarks { crate::tests::ExtBuilder::default().build(), crate::tests::Test ); + + mod no_spend_origin_tests { + use super::*; + + impl_benchmark_test_suite!( + Treasury, + crate::tests::ExtBuilder::default().spend_origin_succesful_origin_err().build(), + crate::tests::Test, + benchmarks_path = benchmarking + ); + } } diff --git a/substrate/frame/treasury/src/lib.rs b/substrate/frame/treasury/src/lib.rs index edb39f2306421a75f09690be3b64feb4c187c998..ad74495ce090b216b0ccf2a19ee4b0843865071b 100644 --- a/substrate/frame/treasury/src/lib.rs +++ b/substrate/frame/treasury/src/lib.rs @@ -73,6 +73,7 @@ #![cfg_attr(not(feature = "std"), no_std)] mod benchmarking; +pub mod migration; #[cfg(test)] mod tests; pub mod weights; diff --git a/substrate/frame/treasury/src/migration.rs b/substrate/frame/treasury/src/migration.rs new file mode 100644 index 0000000000000000000000000000000000000000..c0de4ce43108840b22096bb09c3338bdf1d9afd3 --- /dev/null +++ b/substrate/frame/treasury/src/migration.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. + +//! Treasury pallet migrations. + +use super::*; +use alloc::collections::BTreeSet; +#[cfg(feature = "try-runtime")] +use alloc::vec::Vec; +use core::marker::PhantomData; +use frame_support::{defensive, traits::OnRuntimeUpgrade}; + +/// The log target for this pallet. +const LOG_TARGET: &str = "runtime::treasury"; + +pub mod cleanup_proposals { + use super::*; + + /// Migration to cleanup unapproved proposals to return the bonds back to the proposers. + /// Proposals can no longer be created and the `Proposal` storage item will be removed in the + /// future. + /// + /// `UnreserveWeight` returns `Weight` of `unreserve_balance` operation which is perfomed during + /// this migration. + pub struct Migration(PhantomData<(T, I, UnreserveWeight)>); + + impl, I: 'static, UnreserveWeight: Get> OnRuntimeUpgrade + for Migration + { + fn on_runtime_upgrade() -> frame_support::weights::Weight { + let mut approval_index = BTreeSet::new(); + for approval in Approvals::::get().iter() { + approval_index.insert(*approval); + } + + let mut proposals_processed = 0; + for (proposal_index, p) in Proposals::::iter() { + if !approval_index.contains(&proposal_index) { + let err_amount = T::Currency::unreserve(&p.proposer, p.bond); + if err_amount.is_zero() { + Proposals::::remove(proposal_index); + log::info!( + target: LOG_TARGET, + "Released bond amount of {:?} to proposer {:?}", + p.bond, + p.proposer, + ); + } else { + defensive!( + "err_amount is non zero for proposal {:?}", + (proposal_index, err_amount) + ); + Proposals::::mutate_extant(proposal_index, |proposal| { + proposal.value = err_amount; + }); + log::info!( + target: LOG_TARGET, + "Released partial bond amount of {:?} to proposer {:?}", + p.bond - err_amount, + p.proposer, + ); + } + proposals_processed += 1; + } + } + + log::info!( + target: LOG_TARGET, + "Migration for pallet-treasury finished, released {} proposal bonds.", + proposals_processed, + ); + + // calculate and return migration weights + let approvals_read = 1; + T::DbWeight::get().reads_writes( + proposals_processed as u64 + approvals_read, + proposals_processed as u64, + ) + UnreserveWeight::get() * proposals_processed + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { + let value = ( + Proposals::::iter_values().count() as u32, + Approvals::::get().len() as u32, + ); + log::info!( + target: LOG_TARGET, + "Proposals and Approvals count {:?}", + value, + ); + Ok(value.encode()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + let (old_proposals_count, old_approvals_count) = + <(u32, u32)>::decode(&mut &state[..]).expect("Known good"); + let new_proposals_count = Proposals::::iter_values().count() as u32; + let new_approvals_count = Approvals::::get().len() as u32; + + log::info!( + target: LOG_TARGET, + "Proposals and Approvals count {:?}", + (new_proposals_count, new_approvals_count), + ); + + ensure!( + new_proposals_count <= old_proposals_count, + "Proposals after migration should be less or equal to old proposals" + ); + ensure!( + new_approvals_count == old_approvals_count, + "Approvals after migration should remain the same" + ); + Ok(()) + } + } +} diff --git a/substrate/frame/treasury/src/tests.rs b/substrate/frame/treasury/src/tests.rs index a895ea8151afc65b3744aa555172119049774df6..106bfb530a88d964afdd036ded6e88d8845b5479 100644 --- a/substrate/frame/treasury/src/tests.rs +++ b/substrate/frame/treasury/src/tests.rs @@ -77,6 +77,9 @@ thread_local! { pub static PAID: RefCell> = RefCell::new(BTreeMap::new()); pub static STATUS: RefCell> = RefCell::new(BTreeMap::new()); pub static LAST_ID: RefCell = RefCell::new(0u64); + + #[cfg(feature = "runtime-benchmarks")] + pub static TEST_SPEND_ORIGIN_TRY_SUCCESFUL_ORIGIN_ERR: RefCell = RefCell::new(false); } /// paid balance for a given account and asset ids @@ -131,6 +134,7 @@ parameter_types! { pub TreasuryAccount: u128 = Treasury::account_id(); pub const SpendPayoutPeriod: u64 = 5; } + pub struct TestSpendOrigin; impl frame_support::traits::EnsureOrigin for TestSpendOrigin { type Success = u64; @@ -147,7 +151,11 @@ impl frame_support::traits::EnsureOrigin for TestSpendOrigin { } #[cfg(feature = "runtime-benchmarks")] fn try_successful_origin() -> Result { - Ok(RuntimeOrigin::root()) + if TEST_SPEND_ORIGIN_TRY_SUCCESFUL_ORIGIN_ERR.with(|i| *i.borrow()) { + Err(()) + } else { + Ok(frame_system::RawOrigin::Root.into()) + } } } @@ -187,11 +195,20 @@ pub struct ExtBuilder {} impl Default for ExtBuilder { fn default() -> Self { + #[cfg(feature = "runtime-benchmarks")] + TEST_SPEND_ORIGIN_TRY_SUCCESFUL_ORIGIN_ERR.with(|i| *i.borrow_mut() = false); + Self {} } } impl ExtBuilder { + #[cfg(feature = "runtime-benchmarks")] + pub fn spend_origin_succesful_origin_err(self) -> Self { + TEST_SPEND_ORIGIN_TRY_SUCCESFUL_ORIGIN_ERR.with(|i| *i.borrow_mut() = true); + self + } + pub fn build(self) -> sp_io::TestExternalities { let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); pallet_balances::GenesisConfig:: { diff --git a/substrate/frame/utility/src/lib.rs b/substrate/frame/utility/src/lib.rs index 3ce5b4ff86496d48ffa60d953b86d56880ec65c5..a4f66298f3fa24bba5a33d6d48cff74a7e2fb20a 100644 --- a/substrate/frame/utility/src/lib.rs +++ b/substrate/frame/utility/src/lib.rs @@ -74,7 +74,7 @@ pub use pallet::*; #[frame_support::pallet] pub mod pallet { use super::*; - use frame_support::pallet_prelude::*; + use frame_support::{dispatch::DispatchClass, pallet_prelude::*}; use frame_system::pallet_prelude::*; #[pallet::pallet] @@ -134,8 +134,8 @@ pub mod pallet { fn batched_calls_limit() -> u32 { let allocator_limit = sp_core::MAX_POSSIBLE_ALLOCATION; let call_size = ((core::mem::size_of::<::RuntimeCall>() as u32 + - CALL_ALIGN - 1) / CALL_ALIGN) * - CALL_ALIGN; + CALL_ALIGN - 1) / + CALL_ALIGN) * CALL_ALIGN; // The margin to take into account vec doubling capacity. let margin_factor = 3; @@ -183,21 +183,8 @@ pub mod pallet { /// event is deposited. #[pallet::call_index(0)] #[pallet::weight({ - let dispatch_infos = calls.iter().map(|call| call.get_dispatch_info()).collect::>(); - let dispatch_weight = dispatch_infos.iter() - .map(|di| di.weight) - .fold(Weight::zero(), |total: Weight, weight: Weight| total.saturating_add(weight)) - .saturating_add(T::WeightInfo::batch(calls.len() as u32)); - let dispatch_class = { - let all_operational = dispatch_infos.iter() - .map(|di| di.class) - .all(|class| class == DispatchClass::Operational); - if all_operational { - DispatchClass::Operational - } else { - DispatchClass::Normal - } - }; + let (dispatch_weight, dispatch_class) = Pallet::::weight_and_dispatch_class(&calls); + let dispatch_weight = dispatch_weight.saturating_add(T::WeightInfo::batch(calls.len() as u32)); (dispatch_weight, dispatch_class) })] pub fn batch( @@ -233,13 +220,13 @@ pub mod pallet { // Take the weight of this function itself into account. let base_weight = T::WeightInfo::batch(index.saturating_add(1) as u32); // Return the actual used weight + base_weight of this call. - return Ok(Some(base_weight + weight).into()) + return Ok(Some(base_weight.saturating_add(weight)).into()) } Self::deposit_event(Event::ItemCompleted); } Self::deposit_event(Event::BatchCompleted); let base_weight = T::WeightInfo::batch(calls_len as u32); - Ok(Some(base_weight + weight).into()) + Ok(Some(base_weight.saturating_add(weight)).into()) } /// Send a call through an indexed pseudonym of the sender. @@ -305,21 +292,8 @@ pub mod pallet { /// - O(C) where C is the number of calls to be batched. #[pallet::call_index(2)] #[pallet::weight({ - let dispatch_infos = calls.iter().map(|call| call.get_dispatch_info()).collect::>(); - let dispatch_weight = dispatch_infos.iter() - .map(|di| di.weight) - .fold(Weight::zero(), |total: Weight, weight: Weight| total.saturating_add(weight)) - .saturating_add(T::WeightInfo::batch_all(calls.len() as u32)); - let dispatch_class = { - let all_operational = dispatch_infos.iter() - .map(|di| di.class) - .all(|class| class == DispatchClass::Operational); - if all_operational { - DispatchClass::Operational - } else { - DispatchClass::Normal - } - }; + let (dispatch_weight, dispatch_class) = Pallet::::weight_and_dispatch_class(&calls); + let dispatch_weight = dispatch_weight.saturating_add(T::WeightInfo::batch_all(calls.len() as u32)); (dispatch_weight, dispatch_class) })] pub fn batch_all( @@ -359,7 +333,7 @@ pub mod pallet { // Take the weight of this function itself into account. let base_weight = T::WeightInfo::batch_all(index.saturating_add(1) as u32); // Return the actual used weight + base_weight of this call. - err.post_info = Some(base_weight + weight).into(); + err.post_info = Some(base_weight.saturating_add(weight)).into(); err })?; Self::deposit_event(Event::ItemCompleted); @@ -414,21 +388,8 @@ pub mod pallet { /// - O(C) where C is the number of calls to be batched. #[pallet::call_index(4)] #[pallet::weight({ - let dispatch_infos = calls.iter().map(|call| call.get_dispatch_info()).collect::>(); - let dispatch_weight = dispatch_infos.iter() - .map(|di| di.weight) - .fold(Weight::zero(), |total: Weight, weight: Weight| total.saturating_add(weight)) - .saturating_add(T::WeightInfo::force_batch(calls.len() as u32)); - let dispatch_class = { - let all_operational = dispatch_infos.iter() - .map(|di| di.class) - .all(|class| class == DispatchClass::Operational); - if all_operational { - DispatchClass::Operational - } else { - DispatchClass::Normal - } - }; + let (dispatch_weight, dispatch_class) = Pallet::::weight_and_dispatch_class(&calls); + let dispatch_weight = dispatch_weight.saturating_add(T::WeightInfo::force_batch(calls.len() as u32)); (dispatch_weight, dispatch_class) })] pub fn force_batch( @@ -494,6 +455,27 @@ pub mod pallet { res.map(|_| ()).map_err(|e| e.error) } } + + impl Pallet { + /// Get the accumulated `weight` and the dispatch class for the given `calls`. + fn weight_and_dispatch_class( + calls: &[::RuntimeCall], + ) -> (Weight, DispatchClass) { + let dispatch_infos = calls.iter().map(|call| call.get_dispatch_info()); + let (dispatch_weight, dispatch_class) = dispatch_infos.fold( + (Weight::zero(), DispatchClass::Operational), + |(total_weight, dispatch_class): (Weight, DispatchClass), di| { + ( + total_weight.saturating_add(di.weight), + // If not all are `Operational`, we want to use `DispatchClass::Normal`. + if di.class == DispatchClass::Normal { di.class } else { dispatch_class }, + ) + }, + ); + + (dispatch_weight, dispatch_class) + } + } } /// A pallet identifier. These are per pallet and should be stored in a registry somewhere. diff --git a/substrate/frame/utility/src/tests.rs b/substrate/frame/utility/src/tests.rs index 0a58a92b4c9ea7564a221c7b9cfb02b3f83b1d90..9755efaea41a2f7eee8ee230729e5c2d9d1534fa 100644 --- a/substrate/frame/utility/src/tests.rs +++ b/substrate/frame/utility/src/tests.rs @@ -29,6 +29,7 @@ use frame_support::{ traits::{ConstU64, Contains}, weights::Weight, }; +use frame_system::EnsureRoot; use pallet_collective::{EnsureProportionAtLeast, Instance1}; use sp_runtime::{ traits::{BadOrigin, BlakeTwo256, Dispatchable, Hash}, @@ -189,6 +190,9 @@ impl pallet_collective::Config for Test { type WeightInfo = (); type SetMembersOrigin = frame_system::EnsureRoot; type MaxProposalWeight = MaxProposalWeight; + type DisapproveOrigin = EnsureRoot; + type KillOrigin = EnsureRoot; + type Consideration = (); } impl example::Config for Test {} diff --git a/substrate/frame/vesting/src/benchmarking.rs b/substrate/frame/vesting/src/benchmarking.rs index 68214c4f47ccca2749074bc6e75cc8d218b2e3f7..736dd6eac1a8c52899a9cb74e53e8805ecf1c00f 100644 --- a/substrate/frame/vesting/src/benchmarking.rs +++ b/substrate/frame/vesting/src/benchmarking.rs @@ -42,7 +42,7 @@ fn add_locks(who: &T::AccountId, n: u8) { } fn add_vesting_schedules( - target: AccountIdLookupOf, + target: &T::AccountId, n: u32, ) -> Result, &'static str> { let min_transfer = T::MinVestedTransfer::get(); @@ -52,7 +52,6 @@ fn add_vesting_schedules( let starting_block = 1u32; let source: T::AccountId = account("source", 0, SEED); - let source_lookup = T::Lookup::unlookup(source.clone()); T::Currency::make_free_balance_be(&source, BalanceOf::::max_value()); T::BlockNumberProvider::set_block_number(BlockNumberFor::::zero()); @@ -62,11 +61,7 @@ fn add_vesting_schedules( total_locked += locked; let schedule = VestingInfo::new(locked, per_block, starting_block.into()); - assert_ok!(Vesting::::do_vested_transfer( - source_lookup.clone(), - target.clone(), - schedule - )); + assert_ok!(Vesting::::do_vested_transfer(&source, target, schedule)); // Top up to guarantee we can always transfer another schedule. T::Currency::make_free_balance_be(&source, BalanceOf::::max_value()); @@ -81,11 +76,10 @@ benchmarks! { let s in 1 .. T::MAX_VESTING_SCHEDULES; let caller: T::AccountId = whitelisted_caller(); - let caller_lookup = T::Lookup::unlookup(caller.clone()); T::Currency::make_free_balance_be(&caller, T::Currency::minimum_balance()); add_locks::(&caller, l as u8); - let expected_balance = add_vesting_schedules::(caller_lookup, s)?; + let expected_balance = add_vesting_schedules::(&caller, s)?; // At block zero, everything is vested. assert_eq!(System::::block_number(), BlockNumberFor::::zero()); @@ -109,11 +103,10 @@ benchmarks! { let s in 1 .. T::MAX_VESTING_SCHEDULES; let caller: T::AccountId = whitelisted_caller(); - let caller_lookup = T::Lookup::unlookup(caller.clone()); T::Currency::make_free_balance_be(&caller, T::Currency::minimum_balance()); add_locks::(&caller, l as u8); - add_vesting_schedules::(caller_lookup, s)?; + add_vesting_schedules::(&caller, s)?; // At block 21, everything is unlocked. T::BlockNumberProvider::set_block_number(21u32.into()); @@ -141,7 +134,7 @@ benchmarks! { T::Currency::make_free_balance_be(&other, T::Currency::minimum_balance()); add_locks::(&other, l as u8); - let expected_balance = add_vesting_schedules::(other_lookup.clone(), s)?; + let expected_balance = add_vesting_schedules::(&other, s)?; // At block zero, everything is vested. assert_eq!(System::::block_number(), BlockNumberFor::::zero()); @@ -171,7 +164,7 @@ benchmarks! { T::Currency::make_free_balance_be(&other, T::Currency::minimum_balance()); add_locks::(&other, l as u8); - add_vesting_schedules::(other_lookup.clone(), s)?; + add_vesting_schedules::(&other, s)?; // At block 21 everything is unlocked. T::BlockNumberProvider::set_block_number(21u32.into()); @@ -206,7 +199,7 @@ benchmarks! { add_locks::(&target, l as u8); // Add one vesting schedules. let orig_balance = T::Currency::free_balance(&target); - let mut expected_balance = add_vesting_schedules::(target_lookup.clone(), s)?; + let mut expected_balance = add_vesting_schedules::(&target, s)?; let transfer_amount = T::MinVestedTransfer::get(); let per_block = transfer_amount.checked_div(&20u32.into()).unwrap(); @@ -246,7 +239,7 @@ benchmarks! { add_locks::(&target, l as u8); // Add one less than max vesting schedules let orig_balance = T::Currency::free_balance(&target); - let mut expected_balance = add_vesting_schedules::(target_lookup.clone(), s)?; + let mut expected_balance = add_vesting_schedules::(&target, s)?; let transfer_amount = T::MinVestedTransfer::get(); let per_block = transfer_amount.checked_div(&20u32.into()).unwrap(); @@ -281,7 +274,7 @@ benchmarks! { T::Currency::make_free_balance_be(&caller, T::Currency::minimum_balance()); add_locks::(&caller, l as u8); // Add max vesting schedules. - let expected_balance = add_vesting_schedules::(caller_lookup, s)?; + let expected_balance = add_vesting_schedules::(&caller, s)?; // Schedules are not vesting at block 0. assert_eq!(System::::block_number(), BlockNumberFor::::zero()); @@ -332,7 +325,7 @@ benchmarks! { T::Currency::make_free_balance_be(&caller, T::Currency::minimum_balance()); add_locks::(&caller, l as u8); // Add max vesting schedules. - let total_transferred = add_vesting_schedules::(caller_lookup, s)?; + let total_transferred = add_vesting_schedules::(&caller, s)?; // Go to about half way through all the schedules duration. (They all start at 1, and have a duration of 20 or 21). T::BlockNumberProvider::set_block_number(11u32.into()); @@ -397,7 +390,7 @@ force_remove_vesting_schedule { // Give target existing locks. add_locks::(&target, l as u8); - let _ = add_vesting_schedules::(target_lookup.clone(), s)?; + add_vesting_schedules::(&target, s)?; // The last vesting schedule. let schedule_index = s - 1; diff --git a/substrate/frame/vesting/src/lib.rs b/substrate/frame/vesting/src/lib.rs index bfc10efeed796bdf85d42c48bcb3c41ffa8ce8e0..15f8d397f81c9c345348a16bca290d6ab86837cc 100644 --- a/substrate/frame/vesting/src/lib.rs +++ b/substrate/frame/vesting/src/lib.rs @@ -66,8 +66,8 @@ use frame_support::{ ensure, storage::bounded_vec::BoundedVec, traits::{ - Currency, ExistenceRequirement, Get, LockIdentifier, LockableCurrency, VestingSchedule, - WithdrawReasons, + Currency, ExistenceRequirement, Get, LockIdentifier, LockableCurrency, VestedTransfer, + VestingSchedule, WithdrawReasons, }, weights::Weight, }; @@ -351,8 +351,8 @@ pub mod pallet { schedule: VestingInfo, BlockNumberFor>, ) -> DispatchResult { let transactor = ensure_signed(origin)?; - let transactor = ::unlookup(transactor); - Self::do_vested_transfer(transactor, target, schedule) + let target = T::Lookup::lookup(target)?; + Self::do_vested_transfer(&transactor, &target, schedule) } /// Force a vested transfer. @@ -380,7 +380,9 @@ pub mod pallet { schedule: VestingInfo, BlockNumberFor>, ) -> DispatchResult { ensure_root(origin)?; - Self::do_vested_transfer(source, target, schedule) + let target = T::Lookup::lookup(target)?; + let source = T::Lookup::lookup(source)?; + Self::do_vested_transfer(&source, &target, schedule) } /// Merge two vesting schedules together, creating a new vesting schedule that unlocks over @@ -525,8 +527,8 @@ impl Pallet { // Execute a vested transfer from `source` to `target` with the given `schedule`. fn do_vested_transfer( - source: AccountIdLookupOf, - target: AccountIdLookupOf, + source: &T::AccountId, + target: &T::AccountId, schedule: VestingInfo, BlockNumberFor>, ) -> DispatchResult { // Validate user inputs. @@ -534,27 +536,22 @@ impl Pallet { if !schedule.is_valid() { return Err(Error::::InvalidScheduleParams.into()) }; - let target = T::Lookup::lookup(target)?; - let source = T::Lookup::lookup(source)?; // Check we can add to this account prior to any storage writes. Self::can_add_vesting_schedule( - &target, + target, schedule.locked(), schedule.per_block(), schedule.starting_block(), )?; - T::Currency::transfer( - &source, - &target, - schedule.locked(), - ExistenceRequirement::AllowDeath, - )?; + T::Currency::transfer(source, target, schedule.locked(), ExistenceRequirement::AllowDeath)?; // We can't let this fail because the currency transfer has already happened. + // Must be successful as it has been checked before. + // Better to return error on failure anyway. let res = Self::add_vesting_schedule( - &target, + target, schedule.locked(), schedule.per_block(), schedule.starting_block(), @@ -751,8 +748,8 @@ where Ok(()) } - // Ensure we can call `add_vesting_schedule` without error. This should always - // be called prior to `add_vesting_schedule`. + /// Ensure we can call `add_vesting_schedule` without error. This should always + /// be called prior to `add_vesting_schedule`. fn can_add_vesting_schedule( who: &T::AccountId, locked: BalanceOf, @@ -784,3 +781,32 @@ where Ok(()) } } + +/// An implementation that allows the Vesting Pallet to handle a vested transfer +/// on behalf of another Pallet. +impl VestedTransfer for Pallet +where + BalanceOf: MaybeSerializeDeserialize + Debug, +{ + type Currency = T::Currency; + type Moment = BlockNumberFor; + + fn vested_transfer( + source: &T::AccountId, + target: &T::AccountId, + locked: BalanceOf, + per_block: BalanceOf, + starting_block: BlockNumberFor, + ) -> DispatchResult { + use frame_support::storage::{with_transaction, TransactionOutcome}; + let schedule = VestingInfo::new(locked, per_block, starting_block); + with_transaction(|| -> TransactionOutcome { + let result = Self::do_vested_transfer(source, target, schedule); + + match &result { + Ok(()) => TransactionOutcome::Commit(result), + _ => TransactionOutcome::Rollback(result), + } + }) + } +} diff --git a/substrate/frame/vesting/src/tests.rs b/substrate/frame/vesting/src/tests.rs index 004da0dfbfa137be6b3f3e5c5894ebdb5a36e992..0dd7133d930ad192c0fb1ea2beef915d1b8c3346 100644 --- a/substrate/frame/vesting/src/tests.rs +++ b/substrate/frame/vesting/src/tests.rs @@ -182,7 +182,7 @@ fn unvested_balance_should_not_transfer() { ExtBuilder::default().existential_deposit(10).build().execute_with(|| { let user1_free_balance = Balances::free_balance(&1); assert_eq!(user1_free_balance, 100); // Account 1 has free balance - // Account 1 has only 5 units vested at block 1 (plus 50 unvested) + // Account 1 has only 5 units vested at block 1 (plus 50 unvested) assert_eq!(Vesting::vesting_balance(&1), Some(45)); // Account 1 cannot send more than vested amount... assert_noop!(Balances::transfer_allow_death(Some(1).into(), 2, 56), TokenError::Frozen); @@ -194,7 +194,7 @@ fn vested_balance_should_transfer() { ExtBuilder::default().existential_deposit(10).build().execute_with(|| { let user1_free_balance = Balances::free_balance(&1); assert_eq!(user1_free_balance, 100); // Account 1 has free balance - // Account 1 has only 5 units vested at block 1 (plus 50 unvested) + // Account 1 has only 5 units vested at block 1 (plus 50 unvested) assert_eq!(Vesting::vesting_balance(&1), Some(45)); assert_ok!(Vesting::vest(Some(1).into())); assert_ok!(Balances::transfer_allow_death(Some(1).into(), 2, 55)); @@ -232,7 +232,7 @@ fn vested_balance_should_transfer_using_vest_other() { ExtBuilder::default().existential_deposit(10).build().execute_with(|| { let user1_free_balance = Balances::free_balance(&1); assert_eq!(user1_free_balance, 100); // Account 1 has free balance - // Account 1 has only 5 units vested at block 1 (plus 50 unvested) + // Account 1 has only 5 units vested at block 1 (plus 50 unvested) assert_eq!(Vesting::vesting_balance(&1), Some(45)); assert_ok!(Vesting::vest_other(Some(2).into(), 1)); assert_ok!(Balances::transfer_allow_death(Some(1).into(), 2, 55)); @@ -280,13 +280,14 @@ fn extra_balance_should_transfer() { // Account 1 has only 5 units vested at block 1 (plus 150 unvested) assert_eq!(Vesting::vesting_balance(&1), Some(45)); assert_ok!(Vesting::vest(Some(1).into())); - assert_ok!(Balances::transfer_allow_death(Some(1).into(), 3, 155)); // Account 1 can send extra units gained + // Account 1 can send extra units gained + assert_ok!(Balances::transfer_allow_death(Some(1).into(), 3, 155)); // Account 2 has no units vested at block 1, but gained 100 assert_eq!(Vesting::vesting_balance(&2), Some(200)); assert_ok!(Vesting::vest(Some(2).into())); - assert_ok!(Balances::transfer_allow_death(Some(2).into(), 3, 100)); // Account 2 can send extra - // units gained + // Account 2 can send extra units gained + assert_ok!(Balances::transfer_allow_death(Some(2).into(), 3, 100)); }); } @@ -295,14 +296,16 @@ fn liquid_funds_should_transfer_with_delayed_vesting() { ExtBuilder::default().existential_deposit(256).build().execute_with(|| { let user12_free_balance = Balances::free_balance(&12); - assert_eq!(user12_free_balance, 2560); // Account 12 has free balance - // Account 12 has liquid funds + // Account 12 has free balance + assert_eq!(user12_free_balance, 2560); + // Account 12 has liquid funds assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5)); // Account 12 has delayed vesting let user12_vesting_schedule = VestingInfo::new( 256 * 5, - 64, // Vesting over 20 blocks + // Vesting over 20 blocks + 64, 10, ); assert_eq!(VestingStorage::::get(&12).unwrap(), vec![user12_vesting_schedule]); @@ -630,8 +633,10 @@ fn merge_ongoing_schedules() { let sched1 = VestingInfo::new( ED * 10, - ED, // Vest over 10 blocks. - sched0.starting_block() + 5, // Start at block 15. + // Vest over 10 blocks. + ED, + // Start at block 15. + sched0.starting_block() + 5, ); assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched1)); assert_eq!(VestingStorage::::get(&2).unwrap(), vec![sched0, sched1]); @@ -1191,3 +1196,43 @@ fn remove_vesting_schedule() { ); }); } + +#[test] +fn vested_transfer_impl_works() { + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + assert_eq!(Balances::free_balance(&3), 256 * 30); + assert_eq!(Balances::free_balance(&4), 256 * 40); + // Account 4 should not have any vesting yet. + assert_eq!(VestingStorage::::get(&4), None); + + // Basic working scenario + assert_ok!(>::vested_transfer( + &3, + &4, + ED * 5, + ED * 5 / 20, + 10 + )); + // Now account 4 should have vesting. + let new_vesting_schedule = VestingInfo::new( + ED * 5, + (ED * 5) / 20, // Vesting over 20 blocks + 10, + ); + assert_eq!(VestingStorage::::get(&4).unwrap(), vec![new_vesting_schedule]); + // Account 4 has 5 * 256 locked. + assert_eq!(Vesting::vesting_balance(&4), Some(256 * 5)); + + // If the transfer fails (because they don't have enough balance), no storage is changed. + assert_noop!( + >::vested_transfer(&3, &4, ED * 9999, ED * 5 / 20, 10), + TokenError::FundsUnavailable + ); + + // If applying the vesting schedule fails (per block is 0), no storage is changed. + assert_noop!( + >::vested_transfer(&3, &4, ED * 5, 0, 10), + Error::::InvalidScheduleParams + ); + }); +} 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 2c423f8c28dd435599e1cc5b2d5a49807fd73abb..de922e3253e47c929ea348f187f3440a0ed1f33a 100644 --- a/substrate/primitives/api/proc-macro/src/impl_runtime_apis.rs +++ b/substrate/primitives/api/proc-macro/src/impl_runtime_apis.rs @@ -18,7 +18,7 @@ use crate::{ common::API_VERSION_ATTRIBUTE, utils::{ - extract_all_signature_types, extract_block_type_from_trait_path, extract_impl_trait, + extract_block_type_from_trait_path, extract_impl_trait, extract_parameter_names_types_and_borrows, generate_crate_access, generate_runtime_mod_name_for_trait, parse_runtime_api_version, prefix_function_with_trait, versioned_trait_name, AllowSelfRefInParameters, RequireQualifiedTraitPath, @@ -471,7 +471,7 @@ fn extend_with_api_version(mut trait_: Path, version: Option) -> Path { v } else { // nothing to do - return trait_ + return trait_; }; let trait_name = &mut trait_ @@ -632,11 +632,6 @@ impl<'a> Fold for ApiRuntimeImplToApiRuntimeApiImpl<'a> { } fn fold_item_impl(&mut self, mut input: ItemImpl) -> ItemImpl { - // All this `UnwindSafe` magic below here is required for this rust bug: - // https://github.com/rust-lang/rust/issues/24159 - // Before we directly had the final block type and rust could determine that it is unwind - // safe, but now we just have a generic parameter `Block`. - let crate_ = generate_crate_access(); // Implement the trait for the `RuntimeApiImpl` @@ -644,9 +639,9 @@ impl<'a> Fold for ApiRuntimeImplToApiRuntimeApiImpl<'a> { Box::new(parse_quote!( RuntimeApiImpl<__SrApiBlock__, RuntimeApiImplCall> )); input.generics.params.push(parse_quote!( - __SrApiBlock__: #crate_::BlockT + std::panic::UnwindSafe + - std::panic::RefUnwindSafe + __SrApiBlock__: #crate_::BlockT )); + input .generics .params @@ -661,17 +656,6 @@ impl<'a> Fold for ApiRuntimeImplToApiRuntimeApiImpl<'a> { where_clause.predicates.push(parse_quote! { &'static RuntimeApiImplCall: Send }); - // Require that all types used in the function signatures are unwind safe. - extract_all_signature_types(&input.items).iter().for_each(|i| { - where_clause.predicates.push(parse_quote! { - #i: std::panic::UnwindSafe + std::panic::RefUnwindSafe - }); - }); - - where_clause.predicates.push(parse_quote! { - __SrApiBlock__::Header: std::panic::UnwindSafe + std::panic::RefUnwindSafe - }); - input.attrs = filter_cfg_attrs(&input.attrs); fold::fold_item_impl(self, input) @@ -762,7 +746,7 @@ fn generate_runtime_api_versions(impls: &[ItemImpl]) -> Result { error.combine(Error::new(other_span, "First trait implementation.")); - return Err(error) + return Err(error); } let id: Path = parse_quote!( #path ID ); @@ -892,7 +876,7 @@ fn extract_cfg_api_version(attrs: &Vec, span: Span) -> Result, span: Span) -> Result Each runtime API can have only one version.", API_VERSION_ATTRIBUTE ), - )) + )); } // Parse the runtime version if there exists one. diff --git a/substrate/primitives/api/proc-macro/src/runtime_metadata.rs b/substrate/primitives/api/proc-macro/src/runtime_metadata.rs index 9944927d557302db454e9589be681ffb26c7558e..4cba524dbe253562f7641365f31636cc144aeea6 100644 --- a/substrate/primitives/api/proc-macro/src/runtime_metadata.rs +++ b/substrate/primitives/api/proc-macro/src/runtime_metadata.rs @@ -94,7 +94,7 @@ pub fn generate_decl_runtime_metadata(decl: &ItemTrait) -> TokenStream2 { let is_changed_in = method.attrs.iter().any(|attr| attr.path().is_ident(CHANGED_IN_ATTRIBUTE)); if is_changed_in { - continue + continue; } let mut inputs = Vec::new(); @@ -131,6 +131,10 @@ pub fn generate_decl_runtime_metadata(decl: &ItemTrait) -> TokenStream2 { // Include the method metadata only if its `cfg` features are enabled. let attrs = filter_cfg_attributes(&method.attrs); + let deprecation = match crate::utils::get_deprecation(&crate_, &method.attrs) { + Ok(deprecation) => deprecation, + Err(e) => return e.into_compile_error(), + }; methods.push(quote!( #( #attrs )* #crate_::metadata_ir::RuntimeApiMethodMetadataIR { @@ -138,6 +142,7 @@ pub fn generate_decl_runtime_metadata(decl: &ItemTrait) -> TokenStream2 { inputs: #crate_::vec![ #( #inputs, )* ], output: #output, docs: #docs, + deprecation_info: #deprecation, } )); } @@ -145,6 +150,10 @@ pub fn generate_decl_runtime_metadata(decl: &ItemTrait) -> TokenStream2 { let trait_name_ident = &decl.ident; let trait_name = trait_name_ident.to_string(); let docs = collect_docs(&decl.attrs, &crate_); + let deprecation = match crate::utils::get_deprecation(&crate_, &decl.attrs) { + Ok(deprecation) => deprecation, + Err(e) => return e.into_compile_error(), + }; let attrs = filter_cfg_attributes(&decl.attrs); // The trait generics where already extended with `Block: BlockT`. let mut generics = decl.generics.clone(); @@ -174,6 +183,7 @@ pub fn generate_decl_runtime_metadata(decl: &ItemTrait) -> TokenStream2 { name: #trait_name, methods: #crate_::vec![ #( #methods, )* ], docs: #docs, + deprecation_info: #deprecation, } } } @@ -187,7 +197,7 @@ pub fn generate_decl_runtime_metadata(decl: &ItemTrait) -> TokenStream2 { /// exposed by `generate_decl_runtime_metadata`. pub fn generate_impl_runtime_metadata(impls: &[ItemImpl]) -> Result { if impls.is_empty() { - return Ok(quote!()) + return Ok(quote!()); } let crate_ = generate_crate_access(); diff --git a/substrate/primitives/api/proc-macro/src/utils.rs b/substrate/primitives/api/proc-macro/src/utils.rs index 36577670a40ce48df9ea805adb2fde5f02399599..dc993c2ac420ef431a4222afa24e2c0e47f5465d 100644 --- a/substrate/primitives/api/proc-macro/src/utils.rs +++ b/substrate/primitives/api/proc-macro/src/utils.rs @@ -21,8 +21,9 @@ use proc_macro2::{Span, TokenStream}; use proc_macro_crate::{crate_name, FoundCrate}; use quote::{format_ident, quote}; use syn::{ - parse_quote, spanned::Spanned, token::And, Attribute, Error, FnArg, GenericArgument, Ident, - ImplItem, ItemImpl, Pat, Path, PathArguments, Result, ReturnType, Signature, Type, TypePath, + parse_quote, punctuated::Punctuated, spanned::Spanned, token::And, Attribute, Error, Expr, + ExprLit, FnArg, GenericArgument, Ident, ItemImpl, Lit, Meta, MetaNameValue, Pat, Path, + PathArguments, Result, ReturnType, Signature, Token, Type, TypePath, }; /// Generates the access to the `sc_client` crate. @@ -33,7 +34,7 @@ pub fn generate_crate_access() -> TokenStream { let renamed_name = Ident::new(&renamed_name, Span::call_site()); quote!(#renamed_name::__private) }, - Err(e) => + Err(e) => { if let Ok(FoundCrate::Name(name)) = crate_name(&"polkadot-sdk-frame").or_else(|_| crate_name(&"frame")) { @@ -47,7 +48,8 @@ pub fn generate_crate_access() -> TokenStream { } else { let err = Error::new(Span::call_site(), e).to_compile_error(); quote!( #err ) - }, + } + }, } } @@ -144,7 +146,7 @@ pub fn extract_parameter_names_types_and_borrows( return Err(Error::new(input.span(), "`self` parameter not supported!")), FnArg::Receiver(recv) => if recv.mutability.is_some() || recv.reference.is_none() { - return Err(Error::new(recv.span(), "Only `&self` is supported!")) + return Err(Error::new(recv.span(), "Only `&self` is supported!")); }, } } @@ -157,37 +159,6 @@ pub fn prefix_function_with_trait(trait_: &Ident, function: &F) -> format!("{}_{}", trait_, function.to_string()) } -/// Extract all types that appear in signatures in the given `ImplItem`'s. -/// -/// If a type is a reference, the inner type is extracted (without the reference). -pub fn extract_all_signature_types(items: &[ImplItem]) -> Vec { - items - .iter() - .filter_map(|i| match i { - ImplItem::Fn(method) => Some(&method.sig), - _ => None, - }) - .flat_map(|sig| { - let ret_ty = match &sig.output { - ReturnType::Default => None, - ReturnType::Type(_, ty) => Some((**ty).clone()), - }; - - sig.inputs - .iter() - .filter_map(|i| match i { - FnArg::Typed(arg) => Some(&arg.ty), - _ => None, - }) - .map(|ty| match &**ty { - Type::Reference(t) => (*t.elem).clone(), - _ => (**ty).clone(), - }) - .chain(ret_ty) - }) - .collect() -} - /// Extracts the block type from a trait path. /// /// It is expected that the block type is the first type in the generic arguments. @@ -284,6 +255,85 @@ pub fn filter_cfg_attributes(attrs: &[syn::Attribute]) -> Vec { attrs.iter().filter(|a| a.path().is_ident("cfg")).cloned().collect() } +fn deprecation_msg_formatter(msg: &str) -> String { + format!( + r#"{msg} + help: the following are the possible correct uses +| +| #[deprecated = "reason"] +| +| #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")] +| +| #[deprecated] +|"# + ) +} + +fn parse_deprecated_meta(crate_: &TokenStream, attr: &syn::Attribute) -> Result { + match &attr.meta { + Meta::List(meta_list) => { + let parsed = meta_list + .parse_args_with(Punctuated::::parse_terminated) + .map_err(|e| Error::new(attr.span(), e.to_string()))?; + let (note, since) = parsed.iter().try_fold((None, None), |mut acc, item| { + let value = match &item.value { + Expr::Lit(ExprLit { lit: lit @ Lit::Str(_), .. }) => Ok(lit), + _ => Err(Error::new( + attr.span(), + deprecation_msg_formatter( + "Invalid deprecation attribute: expected string literal", + ), + )), + }?; + if item.path.is_ident("note") { + acc.0.replace(value); + } else if item.path.is_ident("since") { + acc.1.replace(value); + } + Ok::<(Option<&syn::Lit>, Option<&syn::Lit>), Error>(acc) + })?; + note.map_or_else( + || Err(Error::new(attr.span(), deprecation_msg_formatter( + "Invalid deprecation attribute: missing `note`"))), + |note| { + let since = if let Some(str) = since { + quote! { Some(#str) } + } else { + quote! { None } + }; + let doc = quote! { #crate_::metadata_ir::DeprecationStatusIR::Deprecated { note: #note, since: #since }}; + Ok(doc) + }, + ) + }, + Meta::NameValue(MetaNameValue { + value: Expr::Lit(ExprLit { lit: lit @ Lit::Str(_), .. }), + .. + }) => { + // #[deprecated = "lit"] + let doc = quote! { #crate_::metadata_ir::DeprecationStatusIR::Deprecated { note: #lit, since: None } }; + Ok(doc) + }, + Meta::Path(_) => { + // #[deprecated] + Ok(quote! { #crate_::metadata_ir::DeprecationStatusIR::DeprecatedWithoutNote }) + }, + _ => Err(Error::new( + attr.span(), + deprecation_msg_formatter("Invalid deprecation attribute: expected string literal"), + )), + } +} + +/// collects deprecation attribute if its present. +pub fn get_deprecation(crate_: &TokenStream, attrs: &[syn::Attribute]) -> Result { + attrs + .iter() + .find(|a| a.path().is_ident("deprecated")) + .map(|a| parse_deprecated_meta(&crate_, a)) + .unwrap_or_else(|| Ok(quote! {#crate_::metadata_ir::DeprecationStatusIR::NotDeprecated})) +} + #[cfg(test)] mod tests { use assert_matches::assert_matches; @@ -330,4 +380,38 @@ mod tests { assert_eq!(cfg_std, filtered[0]); assert_eq!(cfg_benchmarks, filtered[1]); } + + #[test] + fn check_deprecated_attr() { + const FIRST: &'static str = "hello"; + const SECOND: &'static str = "WORLD"; + + let simple: Attribute = parse_quote!(#[deprecated]); + let simple_path: Attribute = parse_quote!(#[deprecated = #FIRST]); + let meta_list: Attribute = parse_quote!(#[deprecated(note = #FIRST)]); + let meta_list_with_since: Attribute = + parse_quote!(#[deprecated(note = #FIRST, since = #SECOND)]); + let extra_fields: Attribute = + parse_quote!(#[deprecated(note = #FIRST, since = #SECOND, extra = "Test")]); + assert_eq!( + get_deprecation("e! { crate }, &[simple]).unwrap().to_string(), + quote! { crate::metadata_ir::DeprecationStatusIR::DeprecatedWithoutNote }.to_string() + ); + assert_eq!( + get_deprecation("e! { crate }, &[simple_path]).unwrap().to_string(), + quote! { crate::metadata_ir::DeprecationStatusIR::Deprecated { note: #FIRST, since: None } }.to_string() + ); + assert_eq!( + get_deprecation("e! { crate }, &[meta_list]).unwrap().to_string(), + quote! { crate::metadata_ir::DeprecationStatusIR::Deprecated { note: #FIRST, since: None } }.to_string() + ); + assert_eq!( + get_deprecation("e! { crate }, &[meta_list_with_since]).unwrap().to_string(), + quote! { crate::metadata_ir::DeprecationStatusIR::Deprecated { note: #FIRST, since: Some(#SECOND) }}.to_string() + ); + assert_eq!( + get_deprecation("e! { crate }, &[extra_fields]).unwrap().to_string(), + quote! { crate::metadata_ir::DeprecationStatusIR::Deprecated { note: #FIRST, since: Some(#SECOND) }}.to_string() + ); + } } diff --git a/substrate/primitives/api/src/lib.rs b/substrate/primitives/api/src/lib.rs index d254bf20601fe12a7c960ae2381c5d7a75c70d67..700e212688c8d7bf2666e37a123d4ea5413a1808 100644 --- a/substrate/primitives/api/src/lib.rs +++ b/substrate/primitives/api/src/lib.rs @@ -258,6 +258,11 @@ pub const MAX_EXTRINSIC_DEPTH: u32 = 256; /// ``` /// Note that the latest version (4 in our example above) always contains all methods from all /// the versions before. +/// +/// ## Note on deprecation. +/// +/// - Usage of `deprecated` attribute will propagate deprecation information to the metadata. +/// - For general usage examples of `deprecated` attribute please refer to pub use sp_api_proc_macro::decl_runtime_apis; /// Tags given trait implementations as runtime apis. @@ -341,7 +346,7 @@ pub use sp_api_proc_macro::decl_runtime_apis; /// // Here we are exposing the runtime api versions. /// apis: RUNTIME_API_VERSIONS, /// transaction_version: 1, -/// state_version: 1, +/// system_version: 1, /// }; /// /// # fn main() {} diff --git a/substrate/primitives/api/test/Cargo.toml b/substrate/primitives/api/test/Cargo.toml index 121ce6b999388635d04c46bda001b6f15d25ed98..1d21f23eb8042200030c64295665d1b32c5730f0 100644 --- a/substrate/primitives/api/test/Cargo.toml +++ b/substrate/primitives/api/test/Cargo.toml @@ -41,3 +41,4 @@ harness = false [features] "enable-staging-api" = [] +disable-ui-tests = [] diff --git a/substrate/primitives/api/test/tests/trybuild.rs b/substrate/primitives/api/test/tests/trybuild.rs index b0a334eb7a224174dd56d2b70fdd86a5493c2d36..b13e5df9d6f858db1d92b360efb27fbd6db8e56f 100644 --- a/substrate/primitives/api/test/tests/trybuild.rs +++ b/substrate/primitives/api/test/tests/trybuild.rs @@ -15,18 +15,20 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::env; - #[rustversion::attr(not(stable), ignore)] +#[cfg(not(feature = "disable-ui-tests"))] #[test] fn ui() { // Only run the ui tests when `RUN_UI_TESTS` is set. - if env::var("RUN_UI_TESTS").is_err() { + if std::env::var("RUN_UI_TESTS").is_err() { return } // As trybuild is using `cargo check`, we don't need the real WASM binaries. - env::set_var("SKIP_WASM_BUILD", "1"); + std::env::set_var("SKIP_WASM_BUILD", "1"); + + // Warnings are part of our UI. + std::env::set_var("RUSTFLAGS", "--deny warnings"); let t = trybuild::TestCases::new(); t.compile_fail("tests/ui/*.rs"); diff --git a/cumulus/parachains/runtimes/starters/seedling/build.rs b/substrate/primitives/api/test/tests/ui/deprecation_info.rs similarity index 75% rename from cumulus/parachains/runtimes/starters/seedling/build.rs rename to substrate/primitives/api/test/tests/ui/deprecation_info.rs index 60f8a125129ff1344a1799246e931acdb1d139d5..61f93fcd921dbe6ba988e8a65659393c1f4e9d22 100644 --- a/cumulus/parachains/runtimes/starters/seedling/build.rs +++ b/substrate/primitives/api/test/tests/ui/deprecation_info.rs @@ -1,3 +1,5 @@ +// This file is part of Substrate. + // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 @@ -13,14 +15,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -#[cfg(feature = "std")] -fn main() { - substrate_wasm_builder::WasmBuilder::new() - .with_current_project() - .export_heap_base() - .import_memory() - .build() +sp_api::decl_runtime_apis! { + pub trait Api { + #[deprecated(unknown_kw = "test")] + fn test(); + #[deprecated(since = 5)] + fn test2(); + #[deprecated = 5] + fn test3(); + } } -#[cfg(not(feature = "std"))] fn main() {} diff --git a/substrate/primitives/api/test/tests/ui/deprecation_info.stderr b/substrate/primitives/api/test/tests/ui/deprecation_info.stderr new file mode 100644 index 0000000000000000000000000000000000000000..78c687e876de9bc3902bf1edc9032100023739fd --- /dev/null +++ b/substrate/primitives/api/test/tests/ui/deprecation_info.stderr @@ -0,0 +1,40 @@ +error: Invalid deprecation attribute: missing `note` + help: the following are the possible correct uses + | + | #[deprecated = "reason"] + | + | #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")] + | + | #[deprecated] + | + --> tests/ui/deprecation_info.rs:20:3 + | +20 | #[deprecated(unknown_kw = "test")] + | ^ + +error: malformed `deprecated` attribute input + --> tests/ui/deprecation_info.rs:24:3 + | +24 | #[deprecated = 5] + | ^^^^^^^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +24 | #[deprecated = "reason"] + | +24 | #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")] + | +24 | #[deprecated] + | + +error[E0541]: unknown meta item 'unknown_kw' + --> tests/ui/deprecation_info.rs:20:16 + | +20 | #[deprecated(unknown_kw = "test")] + | ^^^^^^^^^^^^^^^^^^^ expected one of `since`, `note` + +error[E0565]: literal in `deprecated` value must be a string + --> tests/ui/deprecation_info.rs:22:24 + | +22 | #[deprecated(since = 5)] + | ^ diff --git a/substrate/primitives/api/test/tests/ui/impl_incorrect_method_signature.stderr b/substrate/primitives/api/test/tests/ui/impl_incorrect_method_signature.stderr index 535bbb178d5f961a629fe3a0e1951c49060f83a4..d625020fe4d3adf90581fc4f654e59920a427f31 100644 --- a/substrate/primitives/api/test/tests/ui/impl_incorrect_method_signature.stderr +++ b/substrate/primitives/api/test/tests/ui/impl_incorrect_method_signature.stderr @@ -22,10 +22,7 @@ error[E0053]: method `test` has an incompatible type for trait --> tests/ui/impl_incorrect_method_signature.rs:33:17 | 33 | fn test(data: String) {} - | ^^^^^^ - | | - | expected `u64`, found `std::string::String` - | help: change the parameter type to match the trait: `u64` + | ^^^^^^ expected `u64`, found `std::string::String` | note: type in trait --> tests/ui/impl_incorrect_method_signature.rs:27:17 @@ -34,6 +31,10 @@ note: type in trait | ^^^ = note: expected signature `fn(u64)` found signature `fn(std::string::String)` +help: change the parameter type to match the trait + | +33 | fn test(data: u64) {} + | ~~~ error[E0308]: mismatched types --> tests/ui/impl_incorrect_method_signature.rs:33:11 @@ -53,3 +54,12 @@ note: associated function defined here | 27 | fn test(data: u64); | ^^^^ + +error: unused variable: `data` + --> tests/ui/impl_incorrect_method_signature.rs:33:11 + | +33 | fn test(data: String) {} + | ^^^^ help: if this is intentional, prefix it with an underscore: `_data` + | + = note: `-D unused-variables` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(unused_variables)]` diff --git a/substrate/primitives/api/test/tests/ui/mock_only_self_reference.stderr b/substrate/primitives/api/test/tests/ui/mock_only_self_reference.stderr index 8457557718771db27ae6c0b9ae7cf59f9e4a8e78..764a0bafaa4fb0e3334b9b344edee6a9cd011c2b 100644 --- a/substrate/primitives/api/test/tests/ui/mock_only_self_reference.stderr +++ b/substrate/primitives/api/test/tests/ui/mock_only_self_reference.stderr @@ -21,8 +21,7 @@ error[E0050]: method `test` has 2 parameters but the declaration in trait `Api:: 29 | / sp_api::mock_impl_runtime_apis! { 30 | | impl Api for MockApi { 31 | | fn test(self, data: u64) {} -32 | | -33 | | fn test2(&mut self, data: u64) {} +... | 34 | | } 35 | | } | |_^ expected 3 parameters, found 2 @@ -41,8 +40,7 @@ error[E0050]: method `test2` has 2 parameters but the declaration in trait `Api: 29 | / sp_api::mock_impl_runtime_apis! { 30 | | impl Api for MockApi { 31 | | fn test(self, data: u64) {} -32 | | -33 | | fn test2(&mut self, data: u64) {} +... | 34 | | } 35 | | } | |_^ expected 3 parameters, found 2 diff --git a/substrate/primitives/api/test/tests/ui/type_reference_in_impl_runtime_apis_call.stderr b/substrate/primitives/api/test/tests/ui/type_reference_in_impl_runtime_apis_call.stderr index f4e0f3b0afb047c4c2c377c16daa38ec1238a67e..26be311c02fa4297c153cded586a3180fa23a589 100644 --- a/substrate/primitives/api/test/tests/ui/type_reference_in_impl_runtime_apis_call.stderr +++ b/substrate/primitives/api/test/tests/ui/type_reference_in_impl_runtime_apis_call.stderr @@ -22,10 +22,7 @@ error[E0053]: method `test` has an incompatible type for trait --> tests/ui/type_reference_in_impl_runtime_apis_call.rs:33:17 | 33 | fn test(data: &u64) { - | ^^^^ - | | - | expected `u64`, found `&u64` - | help: change the parameter type to match the trait: `u64` + | ^^^^ expected `u64`, found `&u64` | note: type in trait --> tests/ui/type_reference_in_impl_runtime_apis_call.rs:27:17 @@ -34,6 +31,10 @@ note: type in trait | ^^^ = note: expected signature `fn(_)` found signature `fn(&_)` +help: change the parameter type to match the trait + | +33 | fn test(data: u64) { + | ~~~ error[E0308]: mismatched types --> tests/ui/type_reference_in_impl_runtime_apis_call.rs:33:11 @@ -57,3 +58,12 @@ help: consider removing the borrow | 33 | fn test(data: &u64) { | + +error: unused variable: `data` + --> tests/ui/type_reference_in_impl_runtime_apis_call.rs:33:11 + | +33 | fn test(data: &u64) { + | ^^^^ help: if this is intentional, prefix it with an underscore: `_data` + | + = note: `-D unused-variables` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(unused_variables)]` diff --git a/substrate/primitives/blockchain/src/backend.rs b/substrate/primitives/blockchain/src/backend.rs index fd0c5795cbfd23b2e90c86ea0f2572f4a7babeea..d7386a71a0d1eec86d0eb85bf2b59d5d5b6cfd4c 100644 --- a/substrate/primitives/blockchain/src/backend.rs +++ b/substrate/primitives/blockchain/src/backend.rs @@ -17,6 +17,7 @@ //! Substrate blockchain trait +use codec::{Decode, Encode}; use parking_lot::RwLock; use sp_runtime::{ generic::BlockId, @@ -109,7 +110,7 @@ pub trait ForkBackend: for block in tree_route.retracted() { expanded_forks.insert(block.hash); } - continue + continue; }, Err(_) => { // There are cases when blocks are missing (e.g. warp-sync). @@ -196,7 +197,7 @@ pub trait Backend: let info = self.info(); if info.finalized_number > *base_header.number() { // `base_header` is on a dead fork. - return Ok(None) + return Ok(None); } self.leaves()? }; @@ -207,7 +208,7 @@ pub trait Backend: // go backwards through the chain (via parent links) loop { if current_hash == base_hash { - return Ok(Some(leaf_hash)) + return Ok(Some(leaf_hash)); } let current_header = self @@ -216,7 +217,7 @@ pub trait Backend: // stop search in this chain once we go below the target's block number if current_header.number() < base_header.number() { - break + break; } current_hash = *current_header.parent_hash(); @@ -266,7 +267,7 @@ pub trait Backend: // If we have only one leaf there are no forks, and we can return early. if finalized_block_number == Zero::zero() || leaves.len() == 1 { - return Ok(DisplacedLeavesAfterFinalization::default()) + return Ok(DisplacedLeavesAfterFinalization::default()); } // Store hashes of finalized blocks for quick checking later, the last block is the @@ -332,7 +333,7 @@ pub trait Backend: elapsed = ?now.elapsed(), "Added genesis leaf to displaced leaves." ); - continue + continue; } debug!( @@ -539,6 +540,29 @@ impl DisplacedLeavesAfterFinalization { } } +/// Represents the type of block gaps that may result from either warp sync or fast sync. +#[derive(Debug, Clone, Copy, Eq, PartialEq, Encode, Decode)] +pub enum BlockGapType { + /// Both the header and body are missing, as a result of warp sync. + MissingHeaderAndBody, + /// The block body is missing, as a result of fast sync. + MissingBody, +} + +/// Represents the block gap resulted by warp sync or fast sync. +/// +/// A block gap is a range of blocks where either the bodies, or both headers and bodies are +/// missing. +#[derive(Debug, Clone, Copy, Eq, PartialEq, Encode, Decode)] +pub struct BlockGap { + /// The starting block number of the gap (inclusive). + pub start: N, + /// The ending block number of the gap (inclusive). + pub end: N, + /// The type of gap. + pub gap_type: BlockGapType, +} + /// Blockchain info #[derive(Debug, Eq, PartialEq, Clone)] pub struct Info { @@ -556,8 +580,8 @@ pub struct Info { pub finalized_state: Option<(Block::Hash, <::Header as HeaderT>::Number)>, /// Number of concurrent leave forks. pub number_leaves: usize, - /// Missing blocks after warp sync. (start, end). - pub block_gap: Option<(NumberFor, NumberFor)>, + /// Missing blocks after warp sync or fast sync. + pub block_gap: Option>>, } /// Block status. diff --git a/substrate/primitives/consensus/beefy/Cargo.toml b/substrate/primitives/consensus/beefy/Cargo.toml index 57ddab9a70ceacb430cdcf65fd2af1cfbc05ec77..13d80683c853da5db00efe896161158a2a06041a 100644 --- a/substrate/primitives/consensus/beefy/Cargo.toml +++ b/substrate/primitives/consensus/beefy/Cargo.toml @@ -28,7 +28,6 @@ sp-runtime = { workspace = true } sp-keystore = { workspace = true } sp-weights = { workspace = true } strum = { features = ["derive"], workspace = true } -lazy_static = { optional = true, workspace = true } [dev-dependencies] array-bytes = { workspace = true, default-features = true } @@ -38,7 +37,6 @@ w3f-bls = { features = ["std"], workspace = true, default-features = true } default = ["std"] std = [ "codec/std", - "dep:lazy_static", "scale-info/std", "serde/std", "sp-api/std", diff --git a/substrate/primitives/consensus/beefy/src/payload.rs b/substrate/primitives/consensus/beefy/src/payload.rs index d22255c384bc2f2a87c607e208ef10157ac1b405..9e792670fef5a70b58fa0e5a8a64dd066e5ed86f 100644 --- a/substrate/primitives/consensus/beefy/src/payload.rs +++ b/substrate/primitives/consensus/beefy/src/payload.rs @@ -56,6 +56,16 @@ impl Payload { Some(&self.0[index].1) } + /// Returns all the raw payloads under given `id`. + pub fn get_all_raw<'a>( + &'a self, + id: &'a BeefyPayloadId, + ) -> impl Iterator> + 'a { + self.0 + .iter() + .filter_map(move |probe| if &probe.0 != id { return None } else { Some(&probe.1) }) + } + /// Returns a decoded payload value under given `id`. /// /// In case the value is not there, or it cannot be decoded `None` is returned. @@ -63,6 +73,14 @@ impl Payload { self.get_raw(id).and_then(|raw| T::decode(&mut &raw[..]).ok()) } + /// Returns all decoded payload values under given `id`. + pub fn get_all_decoded<'a, T: Decode>( + &'a self, + id: &'a BeefyPayloadId, + ) -> impl Iterator> + 'a { + self.get_all_raw(id).map(|raw| T::decode(&mut &raw[..]).ok()) + } + /// Push a `Vec` with a given id into the payload vec. /// This method will internally sort the payload vec after every push. /// diff --git a/substrate/primitives/consensus/beefy/src/test_utils.rs b/substrate/primitives/consensus/beefy/src/test_utils.rs index bd335ede489380541fe1b2c166d58f59e2a88614..4460bcefd45f92ad263e10128091a721e44080c1 100644 --- a/substrate/primitives/consensus/beefy/src/test_utils.rs +++ b/substrate/primitives/consensus/beefy/src/test_utils.rs @@ -26,7 +26,7 @@ use sp_core::{ecdsa, Pair}; use sp_runtime::traits::{BlockNumber, Hash, Header as HeaderT}; use codec::Encode; -use std::{collections::HashMap, marker::PhantomData}; +use std::{collections::HashMap, marker::PhantomData, sync::LazyLock}; use strum::IntoEnumIterator; /// Set of test accounts using [`crate::ecdsa_crypto`] types. @@ -111,12 +111,15 @@ where } } -lazy_static::lazy_static! { - 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(); -} +static PRIVATE_KEYS: LazyLock, ecdsa_crypto::Pair>> = + LazyLock::new(|| Keyring::iter().map(|i| (i.clone(), i.pair())).collect()); +static PUBLIC_KEYS: LazyLock, ecdsa_crypto::Public>> = + LazyLock::new(|| { + 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 { diff --git a/substrate/primitives/core/Cargo.toml b/substrate/primitives/core/Cargo.toml index 51cbfa3bdfbe24210ee7d2805f52f928e73df54a..f6bc17bccacaf48354ad43dcd327c2577a35ad66 100644 --- a/substrate/primitives/core/Cargo.toml +++ b/substrate/primitives/core/Cargo.toml @@ -26,10 +26,14 @@ impl-serde = { optional = true, workspace = true } hash-db = { workspace = true } hash256-std-hasher = { workspace = true } bs58 = { optional = true, workspace = true } -rand = { features = ["small_rng"], optional = true, workspace = true, default-features = true } +rand = { features = [ + "small_rng", +], optional = true, workspace = true, default-features = true } substrate-bip39 = { workspace = true } # personal fork here as workaround for: https://github.com/rust-bitcoin/rust-bip39/pull/64 -bip39 = { package = "parity-bip39", version = "2.0.1", default-features = false, features = ["alloc"] } +bip39 = { package = "parity-bip39", version = "2.0.1", default-features = false, features = [ + "alloc", +] } zeroize = { workspace = true } secrecy = { features = ["alloc"], workspace = true } parking_lot = { optional = true, workspace = true, default-features = true } @@ -58,17 +62,21 @@ sp-runtime-interface = { workspace = true } # k256 crate, better portability, intended to be used in substrate-runtimes (no-std) k256 = { features = ["alloc", "ecdsa"], workspace = true } # secp256k1 crate, better performance, intended to be used on host side (std) -secp256k1 = { features = ["alloc", "recovery"], optional = true, workspace = true } +secp256k1 = { features = [ + "alloc", + "recovery", +], optional = true, workspace = true } # bls crypto w3f-bls = { optional = true, workspace = true } # bandersnatch crypto -bandersnatch_vrfs = { git = "https://github.com/w3f/ring-vrf", rev = "0fef826", default-features = false, features = ["substrate-curves"], optional = true } +bandersnatch_vrfs = { git = "https://github.com/w3f/ring-vrf", rev = "0fef826", default-features = false, features = [ + "substrate-curves", +], optional = true } [dev-dependencies] criterion = { workspace = true, default-features = true } serde_json = { workspace = true, default-features = true } -lazy_static = { workspace = true } regex = { workspace = true } [[bench]] diff --git a/substrate/primitives/core/fuzz/Cargo.toml b/substrate/primitives/core/fuzz/Cargo.toml index 46dfe8d483b7468b06d3dccb5494e6c6ec8cefe0..b6ef395adf9ac9a9a2a44cb1aa208eb736049fe8 100644 --- a/substrate/primitives/core/fuzz/Cargo.toml +++ b/substrate/primitives/core/fuzz/Cargo.toml @@ -11,7 +11,6 @@ workspace = true cargo-fuzz = true [dependencies] -lazy_static = { workspace = true } libfuzzer-sys = { workspace = true } regex = { workspace = true } diff --git a/substrate/primitives/core/fuzz/fuzz_targets/fuzz_address_uri.rs b/substrate/primitives/core/fuzz/fuzz_targets/fuzz_address_uri.rs index e2d9e2fc8b0822ae9d984683cdaaf71a97bd7c2e..ac84faf2d8986e47c713cf445ed11cf6b93480ea 100644 --- a/substrate/primitives/core/fuzz/fuzz_targets/fuzz_address_uri.rs +++ b/substrate/primitives/core/fuzz/fuzz_targets/fuzz_address_uri.rs @@ -24,11 +24,12 @@ extern crate sp_core; use libfuzzer_sys::fuzz_target; use regex::Regex; use sp_core::crypto::AddressUri; +use std::sync::LazyLock; -lazy_static::lazy_static! { - static ref SECRET_PHRASE_REGEX: Regex = Regex::new(r"^(?P[a-zA-Z0-9 ]+)?(?P(//?[^/]+)*)(///(?P.*))?$") - .expect("constructed from known-good static value; qed"); -} +static SECRET_PHRASE_REGEX: LazyLock = LazyLock::new(|| { + Regex::new(r"^(?P[a-zA-Z0-9 ]+)?(?P(//?[^/]+)*)(///(?P.*))?$") + .expect("constructed from known-good static value; qed") +}); fuzz_target!(|input: &str| { let regex_result = SECRET_PHRASE_REGEX.captures(input); diff --git a/substrate/primitives/core/src/address_uri.rs b/substrate/primitives/core/src/address_uri.rs index bbe31b7553bd2b2304384934bdf9ee1928d63aa1..4877250cf3acd619b5ddee15768bdff83501cf56 100644 --- a/substrate/primitives/core/src/address_uri.rs +++ b/substrate/primitives/core/src/address_uri.rs @@ -196,11 +196,12 @@ impl<'a> AddressUri<'a> { mod tests { use super::*; use regex::Regex; + use std::sync::LazyLock; - lazy_static::lazy_static! { - static ref SECRET_PHRASE_REGEX: Regex = Regex::new(r"^(?P[a-zA-Z0-9 ]+)?(?P(//?[^/]+)*)(///(?P.*))?$") - .expect("constructed from known-good static value; qed"); - } + static SECRET_PHRASE_REGEX: LazyLock = LazyLock::new(|| { + Regex::new(r"^(?P[a-zA-Z0-9 ]+)?(?P(//?[^/]+)*)(///(?P.*))?$") + .expect("constructed from known-good static value; qed") + }); fn check_with_regex(input: &str) { let regex_result = SECRET_PHRASE_REGEX.captures(input); diff --git a/substrate/primitives/genesis-builder/src/lib.rs b/substrate/primitives/genesis-builder/src/lib.rs index 2cbac305b4d97438389b288c804152462769501e..07317bc4cb5253b23e069ae9b99fe5f4ab5dff74 100644 --- a/substrate/primitives/genesis-builder/src/lib.rs +++ b/substrate/primitives/genesis-builder/src/lib.rs @@ -62,6 +62,18 @@ pub type Result = core::result::Result<(), sp_runtime::RuntimeString>; /// The type representing preset ID. pub type PresetId = sp_runtime::RuntimeString; +/// The default `development` preset used to communicate with the runtime via +/// [`GenesisBuilder`] interface. +/// +/// (Recommended for testing with a single node, e.g., for benchmarking) +pub const DEV_RUNTIME_PRESET: &'static str = "development"; + +/// The default `local_testnet` preset used to communicate with the runtime via +/// [`GenesisBuilder`] interface. +/// +/// (Recommended for local testing with multiple nodes) +pub const LOCAL_TESTNET_RUNTIME_PRESET: &'static str = "local_testnet"; + sp_api::decl_runtime_apis! { /// API to interact with RuntimeGenesisConfig for the runtime pub trait GenesisBuilder { @@ -85,7 +97,7 @@ sp_api::decl_runtime_apis! { /// /// Otherwise function returns a JSON representation of the built-in, named /// `RuntimeGenesisConfig` preset identified by `id`, or `None` if such preset does not - /// exists. Returned `Vec` contains bytes of JSON blob (patch) which comprises a list of + /// exist. Returned `Vec` contains bytes of JSON blob (patch) which comprises a list of /// (potentially nested) key-value pairs that are intended for customizing the default /// runtime genesis config. The patch shall be merged (rfc7386) with the JSON representation /// of the default `RuntimeGenesisConfig` to create a comprehensive genesis config that can diff --git a/substrate/primitives/metadata-ir/src/types.rs b/substrate/primitives/metadata-ir/src/types.rs index b05f26ff55d4e408e1d8459ba5539f44423b2c18..4ebe8c25a6757e8877e5a01ae21b789265c6cf37 100644 --- a/substrate/primitives/metadata-ir/src/types.rs +++ b/substrate/primitives/metadata-ir/src/types.rs @@ -15,11 +15,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -use codec::Encode; +use codec::{Compact, Encode}; use scale_info::{ form::{Form, MetaForm, PortableForm}, - prelude::vec::Vec, - IntoPortable, MetaType, Registry, + prelude::{collections::BTreeMap, vec::Vec}, + IntoPortable, Registry, }; /// The intermediate representation for the runtime metadata. @@ -52,6 +52,8 @@ pub struct RuntimeApiMetadataIR { pub methods: Vec>, /// Trait documentation. pub docs: Vec, + /// Deprecation info + pub deprecation_info: DeprecationStatusIR, } impl IntoPortable for RuntimeApiMetadataIR { @@ -62,6 +64,7 @@ impl IntoPortable for RuntimeApiMetadataIR { name: self.name.into_portable(registry), methods: registry.map_into_portable(self.methods), docs: registry.map_into_portable(self.docs), + deprecation_info: self.deprecation_info.into_portable(registry), } } } @@ -77,6 +80,8 @@ pub struct RuntimeApiMethodMetadataIR { pub output: T::Type, /// Method documentation. pub docs: Vec, + /// Deprecation info + pub deprecation_info: DeprecationStatusIR, } impl IntoPortable for RuntimeApiMethodMetadataIR { @@ -88,6 +93,7 @@ impl IntoPortable for RuntimeApiMethodMetadataIR { inputs: registry.map_into_portable(self.inputs), output: registry.register_type(&self.output), docs: registry.map_into_portable(self.docs), + deprecation_info: self.deprecation_info.into_portable(registry), } } } @@ -132,6 +138,8 @@ pub struct PalletMetadataIR { pub index: u8, /// Pallet documentation. pub docs: Vec, + /// Deprecation info + pub deprecation_info: DeprecationStatusIR, } impl IntoPortable for PalletMetadataIR { @@ -147,6 +155,7 @@ impl IntoPortable for PalletMetadataIR { error: self.error.map(|error| error.into_portable(registry)), index: self.index, docs: registry.map_into_portable(self.docs), + deprecation_info: self.deprecation_info.into_portable(registry), } } } @@ -245,6 +254,8 @@ pub struct StorageEntryMetadataIR { pub default: Vec, /// Storage entry documentation. pub docs: Vec, + /// Deprecation info + pub deprecation_info: DeprecationStatusIR, } impl IntoPortable for StorageEntryMetadataIR { @@ -257,6 +268,7 @@ impl IntoPortable for StorageEntryMetadataIR { ty: self.ty.into_portable(registry), default: self.default, docs: registry.map_into_portable(self.docs), + deprecation_info: self.deprecation_info.into_portable(registry), } } } @@ -331,19 +343,18 @@ impl IntoPortable for StorageEntryTypeIR { pub struct PalletCallMetadataIR { /// The corresponding enum type for the pallet call. pub ty: T::Type, + /// Deprecation status of the pallet call + pub deprecation_info: DeprecationInfoIR, } impl IntoPortable for PalletCallMetadataIR { type Output = PalletCallMetadataIR; fn into_portable(self, registry: &mut Registry) -> Self::Output { - PalletCallMetadataIR { ty: registry.register_type(&self.ty) } - } -} - -impl From for PalletCallMetadataIR { - fn from(ty: MetaType) -> Self { - Self { ty } + PalletCallMetadataIR { + ty: registry.register_type(&self.ty), + deprecation_info: self.deprecation_info.into_portable(registry), + } } } @@ -352,19 +363,18 @@ impl From for PalletCallMetadataIR { pub struct PalletEventMetadataIR { /// The Event type. pub ty: T::Type, + /// Deprecation info of the event + pub deprecation_info: DeprecationInfoIR, } impl IntoPortable for PalletEventMetadataIR { type Output = PalletEventMetadataIR; fn into_portable(self, registry: &mut Registry) -> Self::Output { - PalletEventMetadataIR { ty: registry.register_type(&self.ty) } - } -} - -impl From for PalletEventMetadataIR { - fn from(ty: MetaType) -> Self { - Self { ty } + PalletEventMetadataIR { + ty: registry.register_type(&self.ty), + deprecation_info: self.deprecation_info.into_portable(registry), + } } } @@ -379,6 +389,8 @@ pub struct PalletConstantMetadataIR { pub value: Vec, /// Documentation of the constant. pub docs: Vec, + /// Deprecation info + pub deprecation_info: DeprecationStatusIR, } impl IntoPortable for PalletConstantMetadataIR { @@ -390,6 +402,7 @@ impl IntoPortable for PalletConstantMetadataIR { ty: registry.register_type(&self.ty), value: self.value, docs: registry.map_into_portable(self.docs), + deprecation_info: self.deprecation_info.into_portable(registry), } } } @@ -399,19 +412,18 @@ impl IntoPortable for PalletConstantMetadataIR { pub struct PalletErrorMetadataIR { /// The error type information. pub ty: T::Type, + /// Deprecation info + pub deprecation_info: DeprecationInfoIR, } impl IntoPortable for PalletErrorMetadataIR { type Output = PalletErrorMetadataIR; fn into_portable(self, registry: &mut Registry) -> Self::Output { - PalletErrorMetadataIR { ty: registry.register_type(&self.ty) } - } -} - -impl From for PalletErrorMetadataIR { - fn from(ty: MetaType) -> Self { - Self { ty } + PalletErrorMetadataIR { + ty: registry.register_type(&self.ty), + deprecation_info: self.deprecation_info.into_portable(registry), + } } } @@ -451,3 +463,61 @@ impl IntoPortable for OuterEnumsIR { } } } + +/// Deprecation status for an entry inside MetadataIR +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +pub enum DeprecationStatusIR { + /// Entry is not deprecated + NotDeprecated, + /// Deprecated without a note. + DeprecatedWithoutNote, + /// Entry is deprecated with an note and an optional `since` field. + Deprecated { + /// Note explaining the deprecation + note: T::String, + /// Optional value for denoting version when the deprecation occured + since: Option, + }, +} +impl IntoPortable for DeprecationStatusIR { + type Output = DeprecationStatusIR; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + match self { + Self::Deprecated { note, since } => { + let note = note.into_portable(registry); + let since = since.map(|x| x.into_portable(registry)); + DeprecationStatusIR::Deprecated { note, since } + }, + Self::DeprecatedWithoutNote => DeprecationStatusIR::DeprecatedWithoutNote, + Self::NotDeprecated => DeprecationStatusIR::NotDeprecated, + } + } +} +/// Deprecation info for an enums/errors/calls. +/// Denotes full/partial deprecation of the type +#[derive(Clone, PartialEq, Eq, Encode, Debug)] +pub enum DeprecationInfoIR { + /// Type is not deprecated + NotDeprecated, + /// Entry is fully deprecated. + ItemDeprecated(DeprecationStatusIR), + /// Entry is partially deprecated. + VariantsDeprecated(BTreeMap, DeprecationStatusIR>), +} +impl IntoPortable for DeprecationInfoIR { + type Output = DeprecationInfoIR; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + match self { + Self::VariantsDeprecated(entries) => { + let entries = + entries.into_iter().map(|(k, entry)| (k, entry.into_portable(registry))); + DeprecationInfoIR::VariantsDeprecated(entries.collect()) + }, + Self::ItemDeprecated(deprecation) => + DeprecationInfoIR::ItemDeprecated(deprecation.into_portable(registry)), + Self::NotDeprecated => DeprecationInfoIR::NotDeprecated, + } + } +} diff --git a/substrate/primitives/panic-handler/Cargo.toml b/substrate/primitives/panic-handler/Cargo.toml index 395e788eb244e9a9ae3114bc04d35f184c2f7f3a..012fe08f7cd533416bd46f31bb06c45122b51ba6 100644 --- a/substrate/primitives/panic-handler/Cargo.toml +++ b/substrate/primitives/panic-handler/Cargo.toml @@ -18,5 +18,4 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] backtrace = { workspace = true } -lazy_static = { workspace = true } regex = { workspace = true } diff --git a/substrate/primitives/panic-handler/src/lib.rs b/substrate/primitives/panic-handler/src/lib.rs index e2a9bfa195a6672d13c472b115b3ca4ed4348325..c4a7eb8dc67c7cf8e4defcb5f60746a16d04cc40 100644 --- a/substrate/primitives/panic-handler/src/lib.rs +++ b/substrate/primitives/panic-handler/src/lib.rs @@ -31,6 +31,7 @@ use std::{ io::{self, Write}, marker::PhantomData, panic::{self, PanicInfo}, + sync::LazyLock, thread, }; @@ -128,8 +129,9 @@ impl Drop for AbortGuard { // NOTE: When making any changes here make sure to also change this function in `sc-tracing`. fn strip_control_codes(input: &str) -> std::borrow::Cow { - lazy_static::lazy_static! { - static ref RE: Regex = Regex::new(r#"(?x) + static RE: LazyLock = LazyLock::new(|| { + Regex::new( + r#"(?x) \x1b\[[^m]+m| # VT100 escape codes [ \x00-\x09\x0B-\x1F # ASCII control codes / Unicode C0 control codes, except \n @@ -138,8 +140,10 @@ fn strip_control_codes(input: &str) -> std::borrow::Cow { \u{202A}-\u{202E} # Unicode left-to-right / right-to-left control characters \u{2066}-\u{2069} # Same as above ] - "#).expect("regex parsing doesn't fail; qed"); - } + "#, + ) + .expect("regex parsing doesn't fail; qed") + }); RE.replace_all(input, "") } diff --git a/substrate/primitives/runtime-interface/tests/ui.rs b/substrate/primitives/runtime-interface/tests/ui.rs index 821d0b73f268b02ea801247c1ce27520d40150d8..408ddbc981ee95541b72a784ed4f4c5a5d8831f8 100644 --- a/substrate/primitives/runtime-interface/tests/ui.rs +++ b/substrate/primitives/runtime-interface/tests/ui.rs @@ -15,18 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::env; - #[rustversion::attr(not(stable), ignore)] #[test] fn ui() { // Only run the ui tests when `RUN_UI_TESTS` is set. - if env::var("RUN_UI_TESTS").is_err() { + if std::env::var("RUN_UI_TESTS").is_err() { return } // As trybuild is using `cargo check`, we don't need the real WASM binaries. - env::set_var("SKIP_WASM_BUILD", "1"); + std::env::set_var("SKIP_WASM_BUILD", "1"); let t = trybuild::TestCases::new(); t.compile_fail("tests/ui/*.rs"); diff --git a/substrate/primitives/runtime-interface/tests/ui/no_feature_gated_method.stderr b/substrate/primitives/runtime-interface/tests/ui/no_feature_gated_method.stderr index 10012ede793dee015a10279257a9d640096de0c8..1c1649d011e686d9d30166cf0461add1bbb94006 100644 --- a/substrate/primitives/runtime-interface/tests/ui/no_feature_gated_method.stderr +++ b/substrate/primitives/runtime-interface/tests/ui/no_feature_gated_method.stderr @@ -9,9 +9,41 @@ note: found an item that was configured out | 25 | fn bar() {} | ^^^ - = note: the item is gated behind the `bar-feature` feature +note: the item is gated behind the `bar-feature` feature + --> tests/ui/no_feature_gated_method.rs:24:8 + | +24 | #[cfg(feature = "bar-feature")] + | ^^^^^^^^^^^^^^^^^^^^^^^ note: found an item that was configured out --> tests/ui/no_feature_gated_method.rs:25:5 | 25 | fn bar() {} | ^^^ +note: the item is gated here + --> tests/ui/no_feature_gated_method.rs:20:1 + | +20 | #[runtime_interface] + | ^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the attribute macro `runtime_interface` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unexpected `cfg` condition value: `bar-feature` + --> tests/ui/no_feature_gated_method.rs:24:8 + | +24 | #[cfg(feature = "bar-feature")] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected values for `feature` are: `default`, `disable_target_static_assertions`, and `std` + = help: consider adding `bar-feature` as a feature in `Cargo.toml` + = note: see for more information about checking conditional configuration + = note: `-D unexpected-cfgs` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(unexpected_cfgs)]` + +error: unexpected `cfg` condition value: `bar-feature` + --> tests/ui/no_feature_gated_method.rs:27:12 + | +27 | #[cfg(not(feature = "bar-feature"))] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected values for `feature` are: `default`, `disable_target_static_assertions`, and `std` + = help: consider adding `bar-feature` as a feature in `Cargo.toml` + = note: see for more information about checking conditional configuration diff --git a/substrate/primitives/runtime/Cargo.toml b/substrate/primitives/runtime/Cargo.toml index 55e9f4b319954f7d0585d66d00b72a340f292d9f..c3413775b1cc25b5c0f6bcf4a57cb32b234b415c 100644 --- a/substrate/primitives/runtime/Cargo.toml +++ b/substrate/primitives/runtime/Cargo.toml @@ -32,9 +32,11 @@ sp-arithmetic = { workspace = true } sp-core = { workspace = true } sp-io = { workspace = true } sp-std = { workspace = true } +sp-trie = { workspace = true } sp-weights = { workspace = true } docify = { workspace = true } tracing = { workspace = true, features = ["log"], default-features = false } +binary-merkle-tree = { workspace = true } simple-mermaid = { version = "0.1.1", optional = true } @@ -52,6 +54,7 @@ runtime-benchmarks = [] try-runtime = [] default = ["std"] std = [ + "binary-merkle-tree/std", "codec/std", "either/use_std", "hash256-std-hasher/std", @@ -69,6 +72,7 @@ std = [ "sp-state-machine/std", "sp-std/std", "sp-tracing/std", + "sp-trie/std", "sp-weights/std", "tracing/std", ] diff --git a/substrate/primitives/runtime/src/lib.rs b/substrate/primitives/runtime/src/lib.rs index fd10dee2a7c5bc5bb21d97f75d79a50d3b0bff10..260c9a91855a4ccc95557842ef837ee7365d536a 100644 --- a/substrate/primitives/runtime/src/lib.rs +++ b/substrate/primitives/runtime/src/lib.rs @@ -49,7 +49,7 @@ extern crate alloc; #[doc(hidden)] -pub use alloc::vec::Vec; +pub use alloc::{format, vec::Vec}; #[doc(hidden)] pub use codec; #[doc(hidden)] @@ -79,8 +79,6 @@ use sp_core::{ sr25519, }; -#[cfg(all(not(feature = "std"), feature = "serde"))] -use alloc::format; use alloc::vec; use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; @@ -90,6 +88,7 @@ pub mod generic; pub mod legacy; mod multiaddress; pub mod offchain; +pub mod proving_trie; pub mod runtime_logger; mod runtime_string; #[cfg(feature = "std")] @@ -103,6 +102,8 @@ pub use crate::runtime_string::*; // Re-export Multiaddress pub use multiaddress::MultiAddress; +use proving_trie::TrieError; + /// Re-export these since they're only "kind of" generic. pub use generic::{Digest, DigestItem}; @@ -592,6 +593,8 @@ pub enum DispatchError { Unavailable, /// Root origin is not allowed. RootNotAllowed, + /// An error with tries. + Trie(TrieError), } /// Result of a `Dispatchable` which contains the `DispatchResult` and additional information about @@ -697,6 +700,12 @@ impl From for DispatchError { } } +impl From for DispatchError { + fn from(e: TrieError) -> DispatchError { + Self::Trie(e) + } +} + impl From<&'static str> for DispatchError { fn from(err: &'static str) -> DispatchError { Self::Other(err) @@ -721,6 +730,7 @@ impl From for &'static str { Corruption => "State corrupt", Unavailable => "Resource unavailable", RootNotAllowed => "Root not allowed", + Trie(e) => e.into(), } } } @@ -768,6 +778,10 @@ impl traits::Printable for DispatchError { Corruption => "State corrupt".print(), Unavailable => "Resource unavailable".print(), RootNotAllowed => "Root not allowed".print(), + Trie(e) => { + "Trie error: ".print(); + <&'static str>::from(*e).print(); + }, } } } diff --git a/substrate/primitives/runtime/src/proving_trie/base16.rs b/substrate/primitives/runtime/src/proving_trie/base16.rs new file mode 100644 index 0000000000000000000000000000000000000000..da05c551c6d9478fd393a9daefdc2398c662cbae --- /dev/null +++ b/substrate/primitives/runtime/src/proving_trie/base16.rs @@ -0,0 +1,327 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Types for a compact base-16 merkle trie used for checking and generating proofs within the +//! runtime. The `sp-trie` crate exposes all of these same functionality (and more), but this +//! library is designed to work more easily with runtime native types, which simply need to +//! implement `Encode`/`Decode`. It also exposes a runtime friendly `TrieError` type which can be +//! use inside of a FRAME Pallet. +//! +//! Proofs are created with latest substrate trie format (`LayoutV1`), and are not compatible with +//! proofs using `LayoutV0`. + +use super::{ProofToHashes, ProvingTrie, TrieError}; +use crate::{Decode, DispatchError, Encode}; +use codec::MaxEncodedLen; +use sp_std::vec::Vec; +use sp_trie::{ + trie_types::{TrieDBBuilder, TrieDBMutBuilderV1}, + LayoutV1, MemoryDB, Trie, TrieMut, +}; + +/// A helper structure for building a basic base-16 merkle trie and creating compact proofs for that +/// trie. Proofs are created with latest substrate trie format (`LayoutV1`), and are not compatible +/// with proofs using `LayoutV0`. +pub struct BasicProvingTrie +where + Hashing: sp_core::Hasher, +{ + db: MemoryDB, + root: Hashing::Out, + _phantom: core::marker::PhantomData<(Key, Value)>, +} + +impl BasicProvingTrie +where + Hashing: sp_core::Hasher, + Key: Encode, +{ + /// Create a compact merkle proof needed to prove all `keys` and their values are in the trie. + /// + /// When verifying the proof created by this function, you must include all of the keys and + /// values of the proof, else the verifier will complain that extra nodes are provided in the + /// proof that are not needed. + pub fn create_multi_proof(&self, keys: &[Key]) -> Result, DispatchError> { + sp_trie::generate_trie_proof::, _, _, _>( + &self.db, + self.root, + &keys.into_iter().map(|k| k.encode()).collect::>>(), + ) + .map_err(|err| TrieError::from(*err).into()) + .map(|structured_proof| structured_proof.encode()) + } +} + +impl ProvingTrie for BasicProvingTrie +where + Hashing: sp_core::Hasher, + Key: Encode, + Value: Encode + Decode, +{ + /// Create a new instance of a `ProvingTrie` using an iterator of key/value pairs. + fn generate_for(items: I) -> Result + where + I: IntoIterator, + { + let mut db = MemoryDB::default(); + let mut root = Default::default(); + + { + let mut trie = TrieDBMutBuilderV1::new(&mut db, &mut root).build(); + for (key, value) in items.into_iter() { + key.using_encoded(|k| value.using_encoded(|v| trie.insert(k, v))) + .map_err(|_| "failed to insert into trie")?; + } + } + + Ok(Self { db, root, _phantom: Default::default() }) + } + + /// Access the underlying trie root. + fn root(&self) -> &Hashing::Out { + &self.root + } + + /// Query a value contained within the current trie. Returns `None` if the + /// nodes within the current `MemoryDB` are insufficient to query the item. + fn query(&self, key: &Key) -> Option { + let trie = TrieDBBuilder::new(&self.db, &self.root).build(); + key.using_encoded(|s| trie.get(s)) + .ok()? + .and_then(|raw| Value::decode(&mut &*raw).ok()) + } + + /// Create a compact merkle proof needed to prove a single key and its value are in the trie. + fn create_proof(&self, key: &Key) -> Result, DispatchError> { + sp_trie::generate_trie_proof::, _, _, _>( + &self.db, + self.root, + &[key.encode()], + ) + .map_err(|err| TrieError::from(*err).into()) + .map(|structured_proof| structured_proof.encode()) + } + + /// Verify the existence of `key` and `value` in a given trie root and proof. + fn verify_proof( + root: &Hashing::Out, + proof: &[u8], + key: &Key, + value: &Value, + ) -> Result<(), DispatchError> { + verify_proof::(root, proof, key, value) + } +} + +impl ProofToHashes for BasicProvingTrie +where + Hashing: sp_core::Hasher, + Hashing::Out: MaxEncodedLen, +{ + // Our proof is just raw bytes. + type Proof = [u8]; + // This base 16 trie uses a raw proof of `Vec`, where the length of the first `Vec` + // is the depth of the trie. We can use this to predict the number of hashes. + fn proof_to_hashes(proof: &[u8]) -> Result { + use codec::DecodeLength; + let depth = + > as DecodeLength>::len(proof).map_err(|_| TrieError::DecodeError)?; + Ok(depth as u32) + } +} + +/// Verify the existence of `key` and `value` in a given trie root and proof. +pub fn verify_proof( + root: &Hashing::Out, + proof: &[u8], + key: &Key, + value: &Value, +) -> Result<(), DispatchError> +where + Hashing: sp_core::Hasher, + Key: Encode, + Value: Encode, +{ + let structured_proof: Vec> = + Decode::decode(&mut &proof[..]).map_err(|_| TrieError::DecodeError)?; + sp_trie::verify_trie_proof::, _, _, _>( + &root, + &structured_proof, + &[(key.encode(), Some(value.encode()))], + ) + .map_err(|err| TrieError::from(err).into()) +} + +/// Verify the existence of multiple `items` in a given trie root and proof. +pub fn verify_multi_proof( + root: &Hashing::Out, + proof: &[u8], + items: &[(Key, Value)], +) -> Result<(), DispatchError> +where + Hashing: sp_core::Hasher, + Key: Encode, + Value: Encode, +{ + let structured_proof: Vec> = + Decode::decode(&mut &proof[..]).map_err(|_| TrieError::DecodeError)?; + let items_encoded = items + .into_iter() + .map(|(key, value)| (key.encode(), Some(value.encode()))) + .collect::, Option>)>>(); + + sp_trie::verify_trie_proof::, _, _, _>( + &root, + &structured_proof, + &items_encoded, + ) + .map_err(|err| TrieError::from(err).into()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::traits::BlakeTwo256; + use sp_core::H256; + use sp_std::collections::btree_map::BTreeMap; + + // A trie which simulates a trie of accounts (u32) and balances (u128). + type BalanceTrie = BasicProvingTrie; + + // The expected root hash for an empty trie. + fn empty_root() -> H256 { + sp_trie::empty_trie_root::>() + } + + fn create_balance_trie() -> BalanceTrie { + // Create a map of users and their balances. + let mut map = BTreeMap::::new(); + for i in 0..100u32 { + map.insert(i, i.into()); + } + + // Put items into the trie. + let balance_trie = BalanceTrie::generate_for(map).unwrap(); + + // Root is changed. + let root = *balance_trie.root(); + assert!(root != empty_root()); + + // Assert valid keys are queryable. + assert_eq!(balance_trie.query(&6u32), Some(6u128)); + assert_eq!(balance_trie.query(&9u32), Some(9u128)); + assert_eq!(balance_trie.query(&69u32), Some(69u128)); + // Invalid key returns none. + assert_eq!(balance_trie.query(&6969u32), None); + + balance_trie + } + + #[test] + fn empty_trie_works() { + let empty_trie = BalanceTrie::generate_for(Vec::new()).unwrap(); + assert_eq!(*empty_trie.root(), empty_root()); + } + + #[test] + fn basic_end_to_end_single_value() { + let balance_trie = create_balance_trie(); + let root = *balance_trie.root(); + + // Create a proof for a valid key. + let proof = balance_trie.create_proof(&6u32).unwrap(); + + // Assert key is provable, all other keys are invalid. + for i in 0..200u32 { + if i == 6 { + assert_eq!( + verify_proof::(&root, &proof, &i, &u128::from(i)), + Ok(()) + ); + // Wrong value is invalid. + assert_eq!( + verify_proof::(&root, &proof, &i, &u128::from(i + 1)), + Err(TrieError::RootMismatch.into()) + ); + } else { + assert!( + verify_proof::(&root, &proof, &i, &u128::from(i)).is_err() + ); + } + } + } + + #[test] + fn basic_end_to_end_multi() { + let balance_trie = create_balance_trie(); + let root = *balance_trie.root(); + + // Create a proof for a valid and invalid key. + let proof = balance_trie.create_multi_proof(&[6u32, 9u32, 69u32]).unwrap(); + let items = [(6u32, 6u128), (9u32, 9u128), (69u32, 69u128)]; + + assert_eq!(verify_multi_proof::(&root, &proof, &items), Ok(())); + } + + #[test] + fn proof_fails_with_bad_data() { + let balance_trie = create_balance_trie(); + let root = *balance_trie.root(); + + // Create a proof for a valid key. + let proof = balance_trie.create_proof(&6u32).unwrap(); + + // Correct data verifies successfully + assert_eq!(verify_proof::(&root, &proof, &6u32, &6u128), Ok(())); + + // Fail to verify proof with wrong root + assert_eq!( + verify_proof::(&Default::default(), &proof, &6u32, &6u128), + Err(TrieError::RootMismatch.into()) + ); + + // Crete a bad proof. + let bad_proof = balance_trie.create_proof(&99u32).unwrap(); + + // Fail to verify data with the wrong proof + assert_eq!( + verify_proof::(&root, &bad_proof, &6u32, &6u128), + Err(TrieError::ExtraneousHashReference.into()) + ); + } + + #[test] + fn proof_to_hashes() { + let mut i: u32 = 1; + // Compute log base 16 and round up + let log16 = |x: u32| -> u32 { + let x_f64 = x as f64; + let log16_x = (x_f64.ln() / 16_f64.ln()).ceil(); + log16_x as u32 + }; + + while i < 10_000_000 { + let trie = BalanceTrie::generate_for((0..i).map(|i| (i, u128::from(i)))).unwrap(); + let proof = trie.create_proof(&0).unwrap(); + let hashes = BalanceTrie::proof_to_hashes(&proof).unwrap(); + let log16 = log16(i).max(1); + + assert_eq!(hashes, log16); + i = i * 10; + } + } +} diff --git a/substrate/primitives/runtime/src/proving_trie/base2.rs b/substrate/primitives/runtime/src/proving_trie/base2.rs new file mode 100644 index 0000000000000000000000000000000000000000..2b14a59ab056c87dca2352a7824b9f740945e897 --- /dev/null +++ b/substrate/primitives/runtime/src/proving_trie/base2.rs @@ -0,0 +1,288 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Types for a base-2 merkle tree used for checking and generating proofs within the +//! runtime. The `binary-merkle-tree` crate exposes all of these same functionality (and more), but +//! this library is designed to work more easily with runtime native types, which simply need to +//! implement `Encode`/`Decode`. + +use super::{ProofToHashes, ProvingTrie, TrieError}; +use crate::{Decode, DispatchError, Encode}; +use binary_merkle_tree::{merkle_proof, merkle_root, MerkleProof}; +use codec::MaxEncodedLen; +use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; + +/// A helper structure for building a basic base-2 merkle trie and creating compact proofs for that +/// trie. +pub struct BasicProvingTrie +where + Hashing: sp_core::Hasher, +{ + db: BTreeMap, + root: Hashing::Out, + _phantom: core::marker::PhantomData<(Key, Value)>, +} + +impl ProvingTrie for BasicProvingTrie +where + Hashing: sp_core::Hasher, + Hashing::Out: Encode + Decode, + Key: Encode + Decode + Ord, + Value: Encode + Decode + Clone, +{ + /// Create a new instance of a `ProvingTrie` using an iterator of key/value pairs. + fn generate_for(items: I) -> Result + where + I: IntoIterator, + { + let mut db = BTreeMap::default(); + for (key, value) in items.into_iter() { + db.insert(key, value); + } + let root = merkle_root::(db.iter().map(|item| item.encode())); + Ok(Self { db, root, _phantom: Default::default() }) + } + + /// Access the underlying trie root. + fn root(&self) -> &Hashing::Out { + &self.root + } + + /// Query a value contained within the current trie. Returns `None` if the + /// nodes within the current `db` are insufficient to query the item. + fn query(&self, key: &Key) -> Option { + self.db.get(&key).cloned() + } + + /// Create a compact merkle proof needed to prove a single key and its value are in the trie. + /// Returns an error if the nodes within the current `db` are insufficient to create a proof. + fn create_proof(&self, key: &Key) -> Result, DispatchError> { + let mut encoded = Vec::with_capacity(self.db.len()); + let mut found_index = None; + + // Find the index of our key, and encode the (key, value) pair. + for (i, (k, v)) in self.db.iter().enumerate() { + // If we found the key we are looking for, save it. + if k == key { + found_index = Some(i); + } + + encoded.push((k, v).encode()); + } + + let index = found_index.ok_or(TrieError::IncompleteDatabase)?; + let proof = merkle_proof::>, Vec>(encoded, index as u32); + Ok(proof.encode()) + } + + /// Verify the existence of `key` and `value` in a given trie root and proof. + fn verify_proof( + root: &Hashing::Out, + proof: &[u8], + key: &Key, + value: &Value, + ) -> Result<(), DispatchError> { + verify_proof::(root, proof, key, value) + } +} + +impl ProofToHashes for BasicProvingTrie +where + Hashing: sp_core::Hasher, + Hashing::Out: MaxEncodedLen + Decode, + Key: Decode, + Value: Decode, +{ + // Our proof is just raw bytes. + type Proof = [u8]; + // This base 2 merkle trie includes a `proof` field which is a `Vec`. + // The length of this vector tells us the depth of the proof, and how many + // hashes we need to calculate. + fn proof_to_hashes(proof: &[u8]) -> Result { + let decoded_proof: MerkleProof> = + Decode::decode(&mut &proof[..]).map_err(|_| TrieError::IncompleteProof)?; + let depth = decoded_proof.proof.len(); + Ok(depth as u32) + } +} + +/// Verify the existence of `key` and `value` in a given trie root and proof. +pub fn verify_proof( + root: &Hashing::Out, + proof: &[u8], + key: &Key, + value: &Value, +) -> Result<(), DispatchError> +where + Hashing: sp_core::Hasher, + Hashing::Out: Decode, + Key: Encode + Decode, + Value: Encode + Decode, +{ + let decoded_proof: MerkleProof> = + Decode::decode(&mut &proof[..]).map_err(|_| TrieError::IncompleteProof)?; + if *root != decoded_proof.root { + return Err(TrieError::RootMismatch.into()); + } + + if (key, value).encode() != decoded_proof.leaf { + return Err(TrieError::ValueMismatch.into()); + } + + if binary_merkle_tree::verify_proof::( + &decoded_proof.root, + decoded_proof.proof, + decoded_proof.number_of_leaves, + decoded_proof.leaf_index, + &decoded_proof.leaf, + ) { + Ok(()) + } else { + Err(TrieError::IncompleteProof.into()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::traits::BlakeTwo256; + use sp_core::H256; + use sp_std::collections::btree_map::BTreeMap; + + // A trie which simulates a trie of accounts (u32) and balances (u128). + type BalanceTrie = BasicProvingTrie; + + // The expected root hash for an empty trie. + fn empty_root() -> H256 { + let tree = BalanceTrie::generate_for(Vec::new()).unwrap(); + *tree.root() + } + + fn create_balance_trie() -> BalanceTrie { + // Create a map of users and their balances. + let mut map = BTreeMap::::new(); + for i in 0..100u32 { + map.insert(i, i.into()); + } + + // Put items into the trie. + let balance_trie = BalanceTrie::generate_for(map).unwrap(); + + // Root is changed. + let root = *balance_trie.root(); + assert!(root != empty_root()); + + // Assert valid keys are queryable. + assert_eq!(balance_trie.query(&6u32), Some(6u128)); + assert_eq!(balance_trie.query(&9u32), Some(9u128)); + assert_eq!(balance_trie.query(&69u32), Some(69u128)); + + balance_trie + } + + #[test] + fn empty_trie_works() { + let empty_trie = BalanceTrie::generate_for(Vec::new()).unwrap(); + assert_eq!(*empty_trie.root(), empty_root()); + } + + #[test] + fn basic_end_to_end_single_value() { + let balance_trie = create_balance_trie(); + let root = *balance_trie.root(); + + // Create a proof for a valid key. + let proof = balance_trie.create_proof(&6u32).unwrap(); + + // Assert key is provable, all other keys are invalid. + for i in 0..200u32 { + if i == 6 { + assert_eq!( + verify_proof::(&root, &proof, &i, &u128::from(i)), + Ok(()) + ); + // Wrong value is invalid. + assert_eq!( + verify_proof::(&root, &proof, &i, &u128::from(i + 1)), + Err(TrieError::ValueMismatch.into()) + ); + } else { + assert!( + verify_proof::(&root, &proof, &i, &u128::from(i)).is_err() + ); + } + } + } + + #[test] + fn proof_fails_with_bad_data() { + let balance_trie = create_balance_trie(); + let root = *balance_trie.root(); + + // Create a proof for a valid key. + let proof = balance_trie.create_proof(&6u32).unwrap(); + + // Correct data verifies successfully + assert_eq!(verify_proof::(&root, &proof, &6u32, &6u128), Ok(())); + + // Fail to verify proof with wrong root + assert_eq!( + verify_proof::(&Default::default(), &proof, &6u32, &6u128), + Err(TrieError::RootMismatch.into()) + ); + + // Fail to verify proof with wrong data + assert_eq!( + verify_proof::(&root, &[], &6u32, &6u128), + Err(TrieError::IncompleteProof.into()) + ); + } + + // We make assumptions about the structure of the merkle proof in order to provide the + // `proof_to_hashes` function. This test keeps those assumptions checked. + #[test] + fn assert_structure_of_merkle_proof() { + let balance_trie = create_balance_trie(); + let root = *balance_trie.root(); + // Create a proof for a valid key. + let proof = balance_trie.create_proof(&6u32).unwrap(); + let decoded_proof: MerkleProof> = Decode::decode(&mut &proof[..]).unwrap(); + + let constructed_proof = MerkleProof::> { + root, + proof: decoded_proof.proof.clone(), + number_of_leaves: 100, + leaf_index: 6, + leaf: (6u32, 6u128).encode(), + }; + assert_eq!(constructed_proof, decoded_proof); + } + + #[test] + fn proof_to_hashes() { + let mut i: u32 = 1; + while i < 10_000_000 { + let trie = BalanceTrie::generate_for((0..i).map(|i| (i, u128::from(i)))).unwrap(); + let proof = trie.create_proof(&0).unwrap(); + let hashes = BalanceTrie::proof_to_hashes(&proof).unwrap(); + let log2 = (i as f64).log2().ceil() as u32; + + assert_eq!(hashes, log2); + i = i * 10; + } + } +} diff --git a/substrate/primitives/runtime/src/proving_trie/mod.rs b/substrate/primitives/runtime/src/proving_trie/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..009aa6d4935fd9231c57a5425365bebdc72411c1 --- /dev/null +++ b/substrate/primitives/runtime/src/proving_trie/mod.rs @@ -0,0 +1,187 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Types for merkle tries compatible with the runtime. + +pub mod base16; +pub mod base2; + +use crate::{Decode, DispatchError, Encode, MaxEncodedLen, TypeInfo}; +#[cfg(feature = "serde")] +use crate::{Deserialize, Serialize}; +use sp_std::vec::Vec; +use sp_trie::{trie_types::TrieError as SpTrieError, VerifyError}; + +/// A runtime friendly error type for tries. +#[derive(Eq, PartialEq, Clone, Copy, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum TrieError { + /* From TrieError */ + /// Attempted to create a trie with a state root not in the DB. + InvalidStateRoot, + /// Trie item not found in the database, + IncompleteDatabase, + /// A value was found in the trie with a nibble key that was not byte-aligned. + ValueAtIncompleteKey, + /// Corrupt Trie item. + DecoderError, + /// Hash is not value. + InvalidHash, + /* From VerifyError */ + /// The statement being verified contains multiple key-value pairs with the same key. + DuplicateKey, + /// The proof contains at least one extraneous node. + ExtraneousNode, + /// The proof contains at least one extraneous value which should have been omitted from the + /// proof. + ExtraneousValue, + /// The proof contains at least one extraneous hash reference the should have been omitted. + ExtraneousHashReference, + /// The proof contains an invalid child reference that exceeds the hash length. + InvalidChildReference, + /// The proof indicates that an expected value was not found in the trie. + ValueMismatch, + /// The proof is missing trie nodes required to verify. + IncompleteProof, + /// The root hash computed from the proof is incorrect. + RootMismatch, + /// One of the proof nodes could not be decoded. + DecodeError, +} + +impl From> for TrieError { + fn from(error: SpTrieError) -> Self { + match error { + SpTrieError::InvalidStateRoot(..) => Self::InvalidStateRoot, + SpTrieError::IncompleteDatabase(..) => Self::IncompleteDatabase, + SpTrieError::ValueAtIncompleteKey(..) => Self::ValueAtIncompleteKey, + SpTrieError::DecoderError(..) => Self::DecoderError, + SpTrieError::InvalidHash(..) => Self::InvalidHash, + } + } +} + +impl From> for TrieError { + fn from(error: VerifyError) -> Self { + match error { + VerifyError::DuplicateKey(..) => Self::DuplicateKey, + VerifyError::ExtraneousNode => Self::ExtraneousNode, + VerifyError::ExtraneousValue(..) => Self::ExtraneousValue, + VerifyError::ExtraneousHashReference(..) => Self::ExtraneousHashReference, + VerifyError::InvalidChildReference(..) => Self::InvalidChildReference, + VerifyError::ValueMismatch(..) => Self::ValueMismatch, + VerifyError::IncompleteProof => Self::IncompleteProof, + VerifyError::RootMismatch(..) => Self::RootMismatch, + VerifyError::DecodeError(..) => Self::DecodeError, + } + } +} + +impl From for &'static str { + fn from(e: TrieError) -> &'static str { + match e { + TrieError::InvalidStateRoot => "The state root is not in the database.", + TrieError::IncompleteDatabase => "A trie item was not found in the database.", + TrieError::ValueAtIncompleteKey => + "A value was found with a key that is not byte-aligned.", + TrieError::DecoderError => "A corrupt trie item was encountered.", + TrieError::InvalidHash => "The hash does not match the expected value.", + TrieError::DuplicateKey => "The proof contains duplicate keys.", + TrieError::ExtraneousNode => "The proof contains extraneous nodes.", + TrieError::ExtraneousValue => "The proof contains extraneous values.", + TrieError::ExtraneousHashReference => "The proof contains extraneous hash references.", + TrieError::InvalidChildReference => "The proof contains an invalid child reference.", + TrieError::ValueMismatch => "The proof indicates a value mismatch.", + TrieError::IncompleteProof => "The proof is incomplete.", + TrieError::RootMismatch => "The root hash computed from the proof is incorrect.", + TrieError::DecodeError => "One of the proof nodes could not be decoded.", + } + } +} + +/// An interface for creating, interacting with, and creating proofs in a merkle trie. +pub trait ProvingTrie +where + Self: Sized, + Hashing: sp_core::Hasher, +{ + /// Create a new instance of a `ProvingTrie` using an iterator of key/value pairs. + fn generate_for(items: I) -> Result + where + I: IntoIterator; + /// Access the underlying trie root. + fn root(&self) -> &Hashing::Out; + /// Query a value contained within the current trie. Returns `None` if the + /// the value does not exist in the trie. + fn query(&self, key: &Key) -> Option; + /// Create a proof that can be used to verify a key and its value are in the trie. + fn create_proof(&self, key: &Key) -> Result, DispatchError>; + /// Verify the existence of `key` and `value` in a given trie root and proof. + fn verify_proof( + root: &Hashing::Out, + proof: &[u8], + key: &Key, + value: &Value, + ) -> Result<(), DispatchError>; +} + +/// This trait is one strategy that can be used to benchmark a trie proof verification for the +/// runtime. This strategy assumes that the majority complexity of verifying a merkle proof comes +/// from computing hashes to recreate the merkle root. This trait converts the the proof, some +/// bytes, to the number of hashes we expect to execute to verify that proof. +pub trait ProofToHashes { + /// The Proof type we will use to determine the number of hashes. + type Proof: ?Sized; + /// This function returns the number of hashes we expect to calculate based on the + /// size of the proof. This is used for benchmarking, so for worst case scenario, we should + /// round up. + /// + /// The major complexity of doing a `verify_proof` is computing the hashes needed + /// to calculate the merkle root. For tries, it should be easy to predict the depth + /// of the trie (which is equivalent to the hashes), by looking at the length of the proof. + fn proof_to_hashes(proof: &Self::Proof) -> Result; +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::traits::BlakeTwo256; + + // A trie which simulates a trie of accounts (u32) and balances (u128). + type BalanceTrie2 = base2::BasicProvingTrie; + type BalanceTrie16 = base16::BasicProvingTrie; + + #[test] + fn basic_api_usage_base_2() { + let balance_trie = BalanceTrie2::generate_for((0..100u32).map(|i| (i, i.into()))).unwrap(); + let root = *balance_trie.root(); + assert_eq!(balance_trie.query(&69), Some(69)); + assert_eq!(balance_trie.query(&6969), None); + let proof = balance_trie.create_proof(&69u32).unwrap(); + assert_eq!(BalanceTrie2::verify_proof(&root, &proof, &69u32, &69u128), Ok(())); + } + + #[test] + fn basic_api_usage_base_16() { + let balance_trie = BalanceTrie16::generate_for((0..100u32).map(|i| (i, i.into()))).unwrap(); + let root = *balance_trie.root(); + assert_eq!(balance_trie.query(&69), Some(69)); + assert_eq!(balance_trie.query(&6969), None); + let proof = balance_trie.create_proof(&69u32).unwrap(); + assert_eq!(BalanceTrie16::verify_proof(&root, &proof, &69u32, &69u128), Ok(())); + } +} diff --git a/substrate/primitives/runtime/src/runtime_string.rs b/substrate/primitives/runtime/src/runtime_string.rs index 71aacf07a762e2fe0886bedae28b408079781ed4..bb0347badcbbe510d3f3ff35942a373ac1dc19cb 100644 --- a/substrate/primitives/runtime/src/runtime_string.rs +++ b/substrate/primitives/runtime/src/runtime_string.rs @@ -50,7 +50,7 @@ macro_rules! format_runtime_string { } #[cfg(not(feature = "std"))] { - sp_runtime::RuntimeString::Owned(alloc::format!($($args)*).as_bytes().to_vec()) + sp_runtime::RuntimeString::Owned($crate::format!($($args)*).as_bytes().to_vec()) } }}; } diff --git a/substrate/primitives/runtime/src/testing.rs b/substrate/primitives/runtime/src/testing.rs index b4aeda5a0e7a3e75a44c9d50b4ed22788d1f96e8..a4ce4b5fc1a05b740d641a2d220ed78c60a07ee3 100644 --- a/substrate/primitives/runtime/src/testing.rs +++ b/substrate/primitives/runtime/src/testing.rs @@ -29,10 +29,7 @@ use crate::{ ApplyExtrinsicResultWithInfo, KeyTypeId, }; use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize, Serializer}; -use sp_core::{ - crypto::{key_types, ByteArray, CryptoType, Dummy}, - U256, -}; +use sp_core::crypto::{key_types, ByteArray, CryptoType, Dummy}; pub use sp_core::{sr25519, H256}; use std::{ cell::RefCell, @@ -79,7 +76,8 @@ impl From for u64 { impl UintAuthorityId { /// Convert this authority ID into a public key. pub fn to_public_key(&self) -> T { - let bytes: [u8; 32] = U256::from(self.0).into(); + let mut bytes = [0u8; 32]; + bytes[0..8].copy_from_slice(&self.0.to_le_bytes()); T::from_slice(&bytes).unwrap() } } diff --git a/substrate/primitives/runtime/src/traits.rs b/substrate/primitives/runtime/src/traits.rs index 25ef15eaf56effe93f56e51b093626ce68679918..fc63bc76decb78a53b9878edb8d18d55ddcd6f79 100644 --- a/substrate/primitives/runtime/src/traits.rs +++ b/substrate/primitives/runtime/src/traits.rs @@ -2342,8 +2342,6 @@ impl BlockNumberProvider for () { mod tests { use super::*; use crate::codec::{Decode, Encode, Input}; - #[cfg(feature = "bls-experimental")] - use sp_core::{bls377, bls381}; use sp_core::{ crypto::{Pair, UncheckedFrom}, ecdsa, ed25519, sr25519, @@ -2486,14 +2484,4 @@ mod tests { fn ecdsa_verify_works() { signature_verify_test!(ecdsa); } - - #[cfg(feature = "bls-experimental")] - fn bls377_verify_works() { - signature_verify_test!(bls377) - } - - #[cfg(feature = "bls-experimental")] - fn bls381_verify_works() { - signature_verify_test!(bls381) - } } diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index 5e94524816a051ac7e1996d05f0875dea5b0df9b..17010a8907fc23325d2726e59285313f5fea53a6 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -619,7 +619,7 @@ pub trait DelegationMigrator { /// /// Also removed from [`StakingUnchecked`] as a Virtual Staker. Useful for testing. #[cfg(feature = "runtime-benchmarks")] - fn migrate_to_direct_staker(agent: Agent); + fn force_kill_agent(agent: Agent); } sp_core::generate_feature_enabled_macro!(runtime_benchmarks_enabled, feature = "runtime-benchmarks", $); diff --git a/substrate/primitives/state-machine/src/ext.rs b/substrate/primitives/state-machine/src/ext.rs index 7a79c4e8a1f1bbd51fbdb34f5b91d2427bdd2995..baad7e621bedcc28923bb668b1f49357afa2291e 100644 --- a/substrate/primitives/state-machine/src/ext.rs +++ b/substrate/primitives/state-machine/src/ext.rs @@ -713,6 +713,7 @@ where } /// Implement `Encode` by forwarding the stored raw vec. +#[allow(dead_code)] struct EncodeOpaqueValue(Vec); impl Encode for EncodeOpaqueValue { diff --git a/substrate/primitives/storage/src/lib.rs b/substrate/primitives/storage/src/lib.rs index 3b9afae4ca078d1e69f5e33e9b3cfeee76b4a328..4b25f85fba68bd80a3c1040b45b213cfdad72563 100644 --- a/substrate/primitives/storage/src/lib.rs +++ b/substrate/primitives/storage/src/lib.rs @@ -444,6 +444,7 @@ impl TryFrom for StateVersion { match val { 0 => Ok(StateVersion::V0), 1 => Ok(StateVersion::V1), + 2 => Ok(StateVersion::V1), _ => Err(()), } } diff --git a/substrate/primitives/trie/Cargo.toml b/substrate/primitives/trie/Cargo.toml index a28f29b01581380bd9435ec075db9e0970ce043e..7f27bb097290e06cee472a3ddfbfff5eb0783c09 100644 --- a/substrate/primitives/trie/Cargo.toml +++ b/substrate/primitives/trie/Cargo.toml @@ -24,7 +24,6 @@ harness = false ahash = { optional = true, workspace = true } codec = { workspace = true } hash-db = { workspace = true } -lazy_static = { optional = true, workspace = true } memory-db = { workspace = true } nohash-hasher = { optional = true, workspace = true } parking_lot = { optional = true, workspace = true, default-features = true } @@ -51,7 +50,6 @@ std = [ "ahash", "codec/std", "hash-db/std", - "lazy_static", "memory-db/std", "nohash-hasher", "parking_lot", diff --git a/substrate/primitives/trie/src/cache/shared_cache.rs b/substrate/primitives/trie/src/cache/shared_cache.rs index e3ba94a2af7c1c9a4565169c85d6bb6053107a11..7f6da80fe95f13a2a828ba994d1bca1ffe445675 100644 --- a/substrate/primitives/trie/src/cache/shared_cache.rs +++ b/substrate/primitives/trie/src/cache/shared_cache.rs @@ -25,17 +25,15 @@ use schnellru::LruMap; use std::{ collections::{hash_map::Entry as SetEntry, HashMap}, hash::{BuildHasher, Hasher as _}, - sync::Arc, + sync::{Arc, LazyLock}, }; use trie_db::{node::NodeOwned, CachedValue}; -lazy_static::lazy_static! { - static ref RANDOM_STATE: ahash::RandomState = { - use rand::Rng; - let mut rng = rand::thread_rng(); - ahash::RandomState::generate_with(rng.gen(), rng.gen(), rng.gen(), rng.gen()) - }; -} +static RANDOM_STATE: LazyLock = LazyLock::new(|| { + use rand::Rng; + let mut rng = rand::thread_rng(); + ahash::RandomState::generate_with(rng.gen(), rng.gen(), rng.gen(), rng.gen()) +}); pub struct SharedNodeCacheLimiter { /// The maximum size (in bytes) the cache can hold inline. diff --git a/substrate/primitives/version/proc-macro/Cargo.toml b/substrate/primitives/version/proc-macro/Cargo.toml index 35c49360b7f89de21fe8fdbac825858c154e6942..a3be654547d901b26ec286943fa39017efdae6e5 100644 --- a/substrate/primitives/version/proc-macro/Cargo.toml +++ b/substrate/primitives/version/proc-macro/Cargo.toml @@ -20,6 +20,7 @@ proc-macro = true [dependencies] codec = { features = ["derive"], workspace = true, default-features = true } +proc-macro-warning = { workspace = true } proc-macro2 = { workspace = true } quote = { workspace = true } syn = { features = ["extra-traits", "fold", "full", "visit"], workspace = true } diff --git a/substrate/primitives/version/proc-macro/src/decl_runtime_version.rs b/substrate/primitives/version/proc-macro/src/decl_runtime_version.rs index 3671d4aff6bb0d14dd100427340f366b92b5b3a1..b4f749c90f59204029242b6275c3ff9ba140bc2c 100644 --- a/substrate/primitives/version/proc-macro/src/decl_runtime_version.rs +++ b/substrate/primitives/version/proc-macro/src/decl_runtime_version.rs @@ -17,6 +17,7 @@ use codec::Encode; use proc_macro2::{Span, TokenStream}; +use proc_macro_warning::Warning; use quote::quote; use syn::{ parse::{Error, Result}, @@ -37,13 +38,19 @@ pub fn decl_runtime_version_impl(input: proc_macro::TokenStream) -> proc_macro:: } fn decl_runtime_version_impl_inner(item: ItemConst) -> Result { - let runtime_version = ParseRuntimeVersion::parse_expr(&item.expr)?.build(item.expr.span())?; + let (parsed_runtime_version, warnings) = ParseRuntimeVersion::parse_expr(&item.expr)?; + let runtime_version = parsed_runtime_version.build(item.expr.span())?; let link_section = generate_emit_link_section_decl(&runtime_version.encode(), "runtime_version"); Ok(quote! { #item #link_section + const _:() = { + #( + #warnings + )* + }; }) } @@ -63,7 +70,7 @@ struct RuntimeVersion { impl_version: u32, apis: u8, transaction_version: u32, - state_version: u8, + system_version: u8, } #[derive(Default, Debug)] @@ -74,11 +81,11 @@ struct ParseRuntimeVersion { spec_version: Option, impl_version: Option, transaction_version: Option, - state_version: Option, + system_version: Option, } impl ParseRuntimeVersion { - fn parse_expr(init_expr: &Expr) -> Result { + fn parse_expr(init_expr: &Expr) -> Result<(ParseRuntimeVersion, Vec)> { let init_expr = match init_expr { Expr::Struct(ref e) => e, _ => @@ -86,13 +93,14 @@ impl ParseRuntimeVersion { }; let mut parsed = ParseRuntimeVersion::default(); + let mut warnings = vec![]; for field_value in init_expr.fields.iter() { - parsed.parse_field_value(field_value)?; + warnings.append(&mut parsed.parse_field_value(field_value)?) } - Ok(parsed) + Ok((parsed, warnings)) } - fn parse_field_value(&mut self, field_value: &FieldValue) -> Result<()> { + fn parse_field_value(&mut self, field_value: &FieldValue) -> Result> { let field_name = match field_value.member { syn::Member::Named(ref ident) => ident, syn::Member::Unnamed(_) => @@ -112,6 +120,7 @@ impl ParseRuntimeVersion { } } + let mut warnings = vec![]; if field_name == "spec_name" { parse_once(&mut self.spec_name, field_value, Self::parse_str_literal)?; } else if field_name == "impl_name" { @@ -125,7 +134,16 @@ impl ParseRuntimeVersion { } else if field_name == "transaction_version" { parse_once(&mut self.transaction_version, field_value, Self::parse_num_literal)?; } else if field_name == "state_version" { - parse_once(&mut self.state_version, field_value, Self::parse_num_literal_u8)?; + let warning = Warning::new_deprecated("RuntimeVersion") + .old("state_version") + .new("system_version)") + .help_link("https://github.com/paritytech/polkadot-sdk/pull/4257") + .span(field_name.span()) + .build_or_panic(); + warnings.push(warning); + parse_once(&mut self.system_version, field_value, Self::parse_num_literal_u8)?; + } else if field_name == "system_version" { + parse_once(&mut self.system_version, field_value, Self::parse_num_literal_u8)?; } else if field_name == "apis" { // Intentionally ignored // @@ -136,7 +154,7 @@ impl ParseRuntimeVersion { return Err(Error::new(field_name.span(), "unknown field")) } - Ok(()) + Ok(warnings) } fn parse_num_literal(expr: &Expr) -> Result { @@ -198,7 +216,7 @@ impl ParseRuntimeVersion { spec_version, impl_version, transaction_version, - state_version, + system_version, } = self; Ok(RuntimeVersion { @@ -208,7 +226,7 @@ impl ParseRuntimeVersion { spec_version: required!(spec_version), impl_version: required!(impl_version), transaction_version: required!(transaction_version), - state_version: required!(state_version), + system_version: required!(system_version), apis: 0, }) } @@ -240,7 +258,7 @@ mod tests { impl_version: 1, apis: 0, transaction_version: 2, - state_version: 1, + system_version: 1, } .encode(); @@ -255,7 +273,7 @@ mod tests { impl_version: 1, apis: Cow::Owned(vec![]), transaction_version: 2, - state_version: 1, + system_version: 1, }, ); } diff --git a/substrate/primitives/version/src/lib.rs b/substrate/primitives/version/src/lib.rs index 55dea364eef4d7089859583005ca3ed7192b2c41..a9f1c2373069523cc503071626907626a88de745 100644 --- a/substrate/primitives/version/src/lib.rs +++ b/substrate/primitives/version/src/lib.rs @@ -35,12 +35,12 @@ extern crate alloc; +#[cfg(any(feature = "std", feature = "serde"))] +use alloc::fmt; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; #[cfg(feature = "std")] use std::collections::HashSet; -#[cfg(feature = "std")] -use std::fmt; #[doc(hidden)] pub use alloc::borrow::Cow; @@ -83,7 +83,7 @@ pub mod embed; /// impl_version: 1, /// apis: RUNTIME_API_VERSIONS, /// transaction_version: 2, -/// state_version: 1, +/// system_version: 1, /// }; /// /// # const RUNTIME_API_VERSIONS: sp_version::ApisVec = sp_version::create_apis_vec!([]); @@ -160,8 +160,6 @@ macro_rules! create_apis_vec { /// `authoring_version`, absolutely not `impl_version` since they change the semantics of the /// runtime. #[derive(Clone, PartialEq, Eq, Encode, Default, sp_runtime::RuntimeDebug, TypeInfo)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] pub struct RuntimeVersion { /// Identifies the different Substrate runtimes. There'll be at least polkadot and node. /// A different on-chain spec_name to that of the native runtime would normally result @@ -200,13 +198,6 @@ pub struct RuntimeVersion { pub impl_version: u32, /// List of supported API "features" along with their versions. - #[cfg_attr( - feature = "serde", - serde( - serialize_with = "apis_serialize::serialize", - deserialize_with = "apis_serialize::deserialize", - ) - )] pub apis: ApisVec, /// All existing calls (dispatchables) are fully compatible when this number doesn't change. If @@ -230,9 +221,406 @@ pub struct RuntimeVersion { /// This number should never decrease. pub transaction_version: u32, - /// Version of the state implementation used by this runtime. + /// Version of the system implementation used by this runtime. /// Use of an incorrect version is consensus breaking. - pub state_version: u8, + pub system_version: u8, +} + +// Manual implementation in order to sprinkle `stateVersion` at the end for migration purposes +// after the field was renamed from `state_version` to `system_version` +#[cfg(feature = "serde")] +impl serde::Serialize for RuntimeVersion { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + + let mut state = serializer.serialize_struct("RuntimeVersion", 9)?; + state.serialize_field("specName", &self.spec_name)?; + state.serialize_field("implName", &self.impl_name)?; + state.serialize_field("authoringVersion", &self.authoring_version)?; + state.serialize_field("specVersion", &self.spec_version)?; + state.serialize_field("implVersion", &self.impl_version)?; + state.serialize_field("apis", { + struct SerializeWith<'a>(&'a ApisVec); + + impl<'a> serde::Serialize for SerializeWith<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + apis_serialize::serialize(self.0, serializer) + } + } + + &SerializeWith(&self.apis) + })?; + state.serialize_field("transactionVersion", &self.transaction_version)?; + state.serialize_field("systemVersion", &self.system_version)?; + state.serialize_field("stateVersion", &self.system_version)?; + state.end() + } +} + +// Manual implementation in order to allow both old `stateVersion` and new `systemVersion` to be +// present at the same time +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for RuntimeVersion { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + use core::marker::PhantomData; + + enum Field { + SpecName, + ImplName, + AuthoringVersion, + SpecVersion, + ImplVersion, + Apis, + TransactionVersion, + SystemVersion, + Ignore, + } + + struct FieldVisitor; + + impl<'de> serde::de::Visitor<'de> for FieldVisitor { + type Value = Field; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("field identifier") + } + + fn visit_u64(self, value: u64) -> Result + where + E: serde::de::Error, + { + match value { + 0 => Ok(Field::SpecName), + 1 => Ok(Field::ImplName), + 2 => Ok(Field::AuthoringVersion), + 3 => Ok(Field::SpecVersion), + 4 => Ok(Field::ImplVersion), + 5 => Ok(Field::Apis), + 6 => Ok(Field::TransactionVersion), + 7 => Ok(Field::SystemVersion), + _ => Ok(Field::Ignore), + } + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + match value { + "specName" => Ok(Field::SpecName), + "implName" => Ok(Field::ImplName), + "authoringVersion" => Ok(Field::AuthoringVersion), + "specVersion" => Ok(Field::SpecVersion), + "implVersion" => Ok(Field::ImplVersion), + "apis" => Ok(Field::Apis), + "transactionVersion" => Ok(Field::TransactionVersion), + "systemVersion" | "stateVersion" => Ok(Field::SystemVersion), + _ => Ok(Field::Ignore), + } + } + + fn visit_bytes(self, value: &[u8]) -> Result + where + E: serde::de::Error, + { + match value { + b"specName" => Ok(Field::SpecName), + b"implName" => Ok(Field::ImplName), + b"authoringVersion" => Ok(Field::AuthoringVersion), + b"specVersion" => Ok(Field::SpecVersion), + b"implVersion" => Ok(Field::ImplVersion), + b"apis" => Ok(Field::Apis), + b"transactionVersion" => Ok(Field::TransactionVersion), + b"systemVersion" | b"stateVersion" => Ok(Field::SystemVersion), + _ => Ok(Field::Ignore), + } + } + } + + impl<'de> serde::Deserialize<'de> for Field { + #[inline] + fn deserialize(deserializer: E) -> Result + where + E: serde::Deserializer<'de>, + { + deserializer.deserialize_identifier(FieldVisitor) + } + } + + struct Visitor<'de> { + lifetime: PhantomData<&'de ()>, + } + impl<'de> serde::de::Visitor<'de> for Visitor<'de> { + type Value = RuntimeVersion; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("struct RuntimeVersion") + } + + #[inline] + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + let spec_name = match seq.next_element()? { + Some(spec_name) => spec_name, + None => + return Err(serde::de::Error::invalid_length( + 0usize, + &"struct RuntimeVersion with 8 elements", + )), + }; + let impl_name = match seq.next_element()? { + Some(impl_name) => impl_name, + None => + return Err(serde::de::Error::invalid_length( + 1usize, + &"struct RuntimeVersion with 8 elements", + )), + }; + let authoring_version = match seq.next_element()? { + Some(authoring_version) => authoring_version, + None => + return Err(serde::de::Error::invalid_length( + 2usize, + &"struct RuntimeVersion with 8 elements", + )), + }; + let spec_version = match seq.next_element()? { + Some(spec_version) => spec_version, + None => + return Err(serde::de::Error::invalid_length( + 3usize, + &"struct RuntimeVersion with 8 elements", + )), + }; + let impl_version = match seq.next_element()? { + Some(impl_version) => impl_version, + None => + return Err(serde::de::Error::invalid_length( + 4usize, + &"struct RuntimeVersion with 8 elements", + )), + }; + let apis = match { + struct DeserializeWith<'de> { + value: ApisVec, + + phantom: PhantomData, + lifetime: PhantomData<&'de ()>, + } + impl<'de> serde::Deserialize<'de> for DeserializeWith<'de> { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + Ok(DeserializeWith { + value: apis_serialize::deserialize(deserializer)?, + phantom: PhantomData, + lifetime: PhantomData, + }) + } + } + seq.next_element::>()?.map(|wrap| wrap.value) + } { + Some(apis) => apis, + None => + return Err(serde::de::Error::invalid_length( + 5usize, + &"struct RuntimeVersion with 8 elements", + )), + }; + let transaction_version = match seq.next_element()? { + Some(transaction_version) => transaction_version, + None => + return Err(serde::de::Error::invalid_length( + 6usize, + &"struct RuntimeVersion with 8 elements", + )), + }; + let system_version = match seq.next_element()? { + Some(system_version) => system_version, + None => + return Err(serde::de::Error::invalid_length( + 7usize, + &"struct RuntimeVersion with 8 elements", + )), + }; + Ok(RuntimeVersion { + spec_name, + impl_name, + authoring_version, + spec_version, + impl_version, + apis, + transaction_version, + system_version, + }) + } + + #[inline] + fn visit_map(self, mut map: A) -> Result + where + A: serde::de::MapAccess<'de>, + { + let mut spec_name: Option = None; + let mut impl_name: Option = None; + let mut authoring_version: Option = None; + let mut spec_version: Option = None; + let mut impl_version: Option = None; + let mut apis: Option = None; + let mut transaction_version: Option = None; + let mut system_version: Option = None; + + while let Some(key) = map.next_key()? { + match key { + Field::SpecName => { + if spec_name.is_some() { + return Err(::duplicate_field( + "specName", + )); + } + spec_name = Some(map.next_value()?); + }, + Field::ImplName => { + if impl_name.is_some() { + return Err(::duplicate_field( + "implName", + )); + } + impl_name = Some(map.next_value()?); + }, + Field::AuthoringVersion => { + if authoring_version.is_some() { + return Err(::duplicate_field( + "authoringVersion", + )); + } + authoring_version = Some(map.next_value()?); + }, + Field::SpecVersion => { + if spec_version.is_some() { + return Err(::duplicate_field( + "specVersion", + )); + } + spec_version = Some(map.next_value()?); + }, + Field::ImplVersion => { + if impl_version.is_some() { + return Err(::duplicate_field( + "implVersion", + )); + } + impl_version = Some(map.next_value()?); + }, + Field::Apis => { + if apis.is_some() { + return Err(::duplicate_field("apis")); + } + apis = Some({ + struct DeserializeWith<'de> { + value: ApisVec, + lifetime: PhantomData<&'de ()>, + } + impl<'de> serde::Deserialize<'de> for DeserializeWith<'de> { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + Ok(DeserializeWith { + value: apis_serialize::deserialize(deserializer)?, + lifetime: PhantomData, + }) + } + } + + map.next_value::>()?.value + }); + }, + Field::TransactionVersion => { + if transaction_version.is_some() { + return Err(::duplicate_field( + "transactionVersion", + )); + } + transaction_version = Some(map.next_value()?); + }, + Field::SystemVersion => + if let Some(system_version) = system_version { + let new_value = map.next_value::()?; + if system_version != new_value { + return Err(::custom( + alloc::format!( + r#"Duplicated "stateVersion" and "systemVersion" \ + fields must have the same value, but different values \ + were provided: {system_version} vs {new_value}"# + ), + )); + } + } else { + system_version = Some(map.next_value()?); + }, + _ => { + let _ = map.next_value::()?; + }, + } + } + let spec_name = spec_name + .ok_or_else(|| ::missing_field("specName"))?; + let impl_name = impl_name + .ok_or_else(|| ::missing_field("implName"))?; + let authoring_version = authoring_version.ok_or_else(|| { + ::missing_field("authoringVersion") + })?; + let spec_version = spec_version + .ok_or_else(|| ::missing_field("specVersion"))?; + let impl_version = impl_version + .ok_or_else(|| ::missing_field("implVersion"))?; + let apis = + apis.ok_or_else(|| ::missing_field("apis"))?; + let transaction_version = transaction_version.ok_or_else(|| { + ::missing_field("transactionVersion") + })?; + let system_version = system_version.ok_or_else(|| { + ::missing_field("systemVersion") + })?; + Ok(RuntimeVersion { + spec_name, + impl_name, + authoring_version, + spec_version, + impl_version, + apis, + transaction_version, + system_version, + }) + } + } + + const FIELDS: &[&str] = &[ + "specName", + "implName", + "authoringVersion", + "specVersion", + "implVersion", + "apis", + "transactionVersion", + "stateVersion", + "systemVersion", + ]; + + deserializer.deserialize_struct("RuntimeVersion", FIELDS, Visitor { lifetime: PhantomData }) + } } impl RuntimeVersion { @@ -257,7 +645,7 @@ impl RuntimeVersion { if core_version.is_some() { core_version } else { core_version_from_apis(&apis) }; let transaction_version = if core_version.map(|v| v >= 3).unwrap_or(false) { Decode::decode(input)? } else { 1 }; - let state_version = + let system_version = if core_version.map(|v| v >= 4).unwrap_or(false) { Decode::decode(input)? } else { 0 }; Ok(RuntimeVersion { spec_name, @@ -267,7 +655,7 @@ impl RuntimeVersion { impl_version, apis, transaction_version, - state_version, + system_version, }) } } @@ -334,7 +722,17 @@ impl RuntimeVersion { /// Otherwise, V1 trie version will be use. pub fn state_version(&self) -> StateVersion { // If version > than 1, keep using latest version. - self.state_version.try_into().unwrap_or(StateVersion::V1) + self.system_version.try_into().unwrap_or(StateVersion::V1) + } + + /// Returns the state version to use for Extrinsics root. + pub fn extrinsics_root_state_version(&self) -> StateVersion { + match self.system_version { + // for system version 0 and 1, return V0 + 0 | 1 => StateVersion::V0, + // anything above 1, return V1 + _ => StateVersion::V1, + } } } diff --git a/substrate/test-utils/cli/build.rs b/substrate/test-utils/cli/build.rs index a68cb706e8fbdc2526aa18730f0e7ddb9719f838..c63f0b8b66744738df18d99a274632cb1ecef980 100644 --- a/substrate/test-utils/cli/build.rs +++ b/substrate/test-utils/cli/build.rs @@ -20,6 +20,6 @@ use std::env; fn main() { if let Ok(profile) = env::var("PROFILE") { - println!("cargo:rustc-cfg=build_type=\"{}\"", profile); + println!("cargo:rustc-cfg=build_profile=\"{}\"", profile); } } diff --git a/substrate/test-utils/cli/src/lib.rs b/substrate/test-utils/cli/src/lib.rs index d77a89b4dbf4c9048d3ba3546cde0d32d645dc0a..70d68f6f18359460724baad23b259160cf106db5 100644 --- a/substrate/test-utils/cli/src/lib.rs +++ b/substrate/test-utils/cli/src/lib.rs @@ -130,7 +130,7 @@ pub fn start_node() -> Child { /// build_substrate(&["--features=try-runtime"]); /// ``` pub fn build_substrate(args: &[&str]) { - let is_release_build = !cfg!(build_type = "debug"); + let is_release_build = !cfg!(build_profile = "debug"); // Get the root workspace directory from the CARGO_MANIFEST_DIR environment variable let mut cmd = Command::new("cargo"); diff --git a/substrate/test-utils/runtime/src/lib.rs b/substrate/test-utils/runtime/src/lib.rs index 514f3bcba20441c44195fb9c0272f8fabf9ba072..74965264c0343dabf0e535ba855c3fce23452dda 100644 --- a/substrate/test-utils/runtime/src/lib.rs +++ b/substrate/test-utils/runtime/src/lib.rs @@ -119,7 +119,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 2, apis: RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 1, + system_version: 1, }; fn version() -> RuntimeVersion { @@ -391,6 +391,7 @@ impl pallet_balances::Config for Runtime { type MaxFreezes = (); type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; + type DoneSlashHandler = (); } impl substrate_test_pallet::Config for Runtime {} diff --git a/substrate/utils/binary-merkle-tree/Cargo.toml b/substrate/utils/binary-merkle-tree/Cargo.toml index 087ec5fd6c6d620a4f18cdf886a3e504c630e79a..9577d94ef0bfa1e63262ffd267930cffa0ada9e9 100644 --- a/substrate/utils/binary-merkle-tree/Cargo.toml +++ b/substrate/utils/binary-merkle-tree/Cargo.toml @@ -12,6 +12,7 @@ homepage.workspace = true workspace = true [dependencies] +codec = { workspace = true, features = ["derive"] } array-bytes = { optional = true, workspace = true, default-features = true } log = { optional = true, workspace = true } hash-db = { workspace = true } @@ -25,4 +26,10 @@ sp-runtime = { workspace = true, default-features = true } [features] debug = ["array-bytes", "log"] default = ["debug", "std"] -std = ["hash-db/std", "log/std", "sp-core/std", "sp-runtime/std"] +std = [ + "codec/std", + "hash-db/std", + "log/std", + "sp-core/std", + "sp-runtime/std", +] diff --git a/substrate/utils/binary-merkle-tree/src/lib.rs b/substrate/utils/binary-merkle-tree/src/lib.rs index f2d338cf028e40cf277096614cc8f21095482417..f98ee0609014fca2c04821401992470bd1171d02 100644 --- a/substrate/utils/binary-merkle-tree/src/lib.rs +++ b/substrate/utils/binary-merkle-tree/src/lib.rs @@ -37,6 +37,7 @@ use alloc::vec; #[cfg(not(feature = "std"))] use alloc::vec::Vec; +use codec::{Decode, Encode}; use hash_db::Hasher; /// Construct a root hash of a Binary Merkle Tree created from given leaves. @@ -87,7 +88,7 @@ where /// A generated merkle proof. /// /// The structure contains all necessary data to later on verify the proof and the leaf itself. -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Encode, Decode)] pub struct MerkleProof { /// Root hash of generated merkle tree. pub root: H, @@ -100,9 +101,9 @@ pub struct MerkleProof { /// /// This is needed to detect a case where we have an odd number of leaves that "get promoted" /// to upper layers. - pub number_of_leaves: usize, + pub number_of_leaves: u32, /// Index of the leaf the proof is for (0-based). - pub leaf_index: usize, + pub leaf_index: u32, /// Leaf content. pub leaf: L, } @@ -121,13 +122,13 @@ trait Visitor { /// The method will also visit the `root` hash (level 0). /// /// The `index` is an index of `left` item. - fn visit(&mut self, index: usize, left: &Option, right: &Option); + fn visit(&mut self, index: u32, left: &Option, right: &Option); } /// No-op implementation of the visitor. impl Visitor for () { fn move_up(&mut self) {} - fn visit(&mut self, _index: usize, _left: &Option, _right: &Option) {} + fn visit(&mut self, _index: u32, _left: &Option, _right: &Option) {} } /// Construct a Merkle Proof for leaves given by indices. @@ -140,7 +141,7 @@ impl Visitor for () { /// # Panic /// /// The function will panic if given `leaf_index` is greater than the number of leaves. -pub fn merkle_proof(leaves: I, leaf_index: usize) -> MerkleProof +pub fn merkle_proof(leaves: I, leaf_index: u32) -> MerkleProof where H: Hasher, H::Out: Default + Copy + AsRef<[u8]>, @@ -151,7 +152,7 @@ where let mut leaf = None; let iter = leaves.into_iter().enumerate().map(|(idx, l)| { let hash = ::hash(l.as_ref()); - if idx == leaf_index { + if idx as u32 == leaf_index { leaf = Some(l); } hash @@ -160,11 +161,11 @@ where /// The struct collects a proof for single leaf. struct ProofCollection { proof: Vec, - position: usize, + position: u32, } impl ProofCollection { - fn new(position: usize) -> Self { + fn new(position: u32) -> Self { ProofCollection { proof: Default::default(), position } } } @@ -174,7 +175,7 @@ where self.position /= 2; } - fn visit(&mut self, index: usize, left: &Option, right: &Option) { + fn visit(&mut self, index: u32, left: &Option, right: &Option) { // we are at left branch - right goes to the proof. if self.position == index { if let Some(right) = right { @@ -190,7 +191,7 @@ where } } - let number_of_leaves = iter.len(); + let number_of_leaves = iter.len() as u32; let mut collect_proof = ProofCollection::new(leaf_index); let root = merkelize::(iter, &mut collect_proof); @@ -237,8 +238,8 @@ impl<'a, H, T: AsRef<[u8]>> From<&'a T> for Leaf<'a, H> { pub fn verify_proof<'a, H, P, L>( root: &'a H::Out, proof: P, - number_of_leaves: usize, - leaf_index: usize, + number_of_leaves: u32, + leaf_index: u32, leaf: L, ) -> bool where @@ -440,7 +441,7 @@ mod tests { assert!(verify_proof::( &proof0.root, proof0.proof.clone(), - data.len(), + data.len() as _, proof0.leaf_index, &proof0.leaf, )); @@ -449,7 +450,7 @@ mod tests { assert!(verify_proof::( &proof1.root, proof1.proof, - data.len(), + data.len() as _, proof1.leaf_index, &proof1.leaf, )); @@ -458,7 +459,7 @@ mod tests { assert!(verify_proof::( &proof2.root, proof2.proof, - data.len(), + data.len() as _, proof2.leaf_index, &proof2.leaf )); @@ -479,7 +480,7 @@ mod tests { ) .into(), proof0.proof, - data.len(), + data.len() as _, proof0.leaf_index, &proof0.leaf )); @@ -487,7 +488,7 @@ mod tests { assert!(!verify_proof::( &proof0.root.into(), vec![], - data.len(), + data.len() as _, proof0.leaf_index, &proof0.leaf )); @@ -498,14 +499,14 @@ mod tests { // given let data = vec!["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]; - for l in 0..data.len() { + for l in 0..data.len() as u32 { // when let proof = merkle_proof::(data.clone(), l); // then assert!(verify_proof::( &proof.root, proof.proof, - data.len(), + data.len() as _, proof.leaf_index, &proof.leaf )); @@ -523,14 +524,14 @@ mod tests { } } - for l in 0..data.len() { + for l in 0..data.len() as u32 { // when let proof = merkle_proof::(data.clone(), l); // then assert!(verify_proof::( &proof.root, proof.proof, - data.len(), + data.len() as _, proof.leaf_index, &proof.leaf )); @@ -546,14 +547,14 @@ mod tests { data.push(format!("{}", i)); } - for l in (0..data.len()).step_by(13) { + for l in (0..data.len() as u32).step_by(13) { // when let proof = merkle_proof::(data.clone(), l); // then assert!(verify_proof::( &proof.root, proof.proof, - data.len(), + data.len() as _, proof.leaf_index, &proof.leaf )); @@ -747,24 +748,24 @@ mod tests { .map(|address| array_bytes::hex2bytes_unchecked(&address)) .collect::>(); - for l in 0..data.len() { + for l in 0..data.len() as u32 { // when let proof = merkle_proof::(data.clone(), l); assert_eq!(array_bytes::bytes2hex("", &proof.root), array_bytes::bytes2hex("", &root)); assert_eq!(proof.leaf_index, l); - assert_eq!(&proof.leaf, &data[l]); + assert_eq!(&proof.leaf, &data[l as usize]); // then assert!(verify_proof::( &proof.root, proof.proof, - data.len(), + data.len() as _, proof.leaf_index, &proof.leaf )); } - let proof = merkle_proof::(data.clone(), data.len() - 1); + let proof = merkle_proof::(data.clone(), data.len() as u32 - 1); assert_eq!( proof, @@ -788,8 +789,8 @@ mod tests { ) .into(), ], - number_of_leaves: data.len(), - leaf_index: data.len() - 1, + number_of_leaves: data.len() as _, + leaf_index: data.len() as u32 - 1, leaf: array_bytes::hex2array_unchecked::<_, 20>( "c26B34D375533fFc4c5276282Fa5D660F3d8cbcB" ) diff --git a/substrate/utils/fork-tree/src/lib.rs b/substrate/utils/fork-tree/src/lib.rs index ff86467c85d5445a66b3d86cc04f83fcf5a110c7..fe349b6c29afbd8b404b0e6f598c9c8a4c4fdfaa 100644 --- a/substrate/utils/fork-tree/src/lib.rs +++ b/substrate/utils/fork-tree/src/lib.rs @@ -810,12 +810,11 @@ impl<'a, H, N, V> Iterator for ForkTreeIterator<'a, H, N, V> { type Item = &'a Node; fn next(&mut self) -> Option { - self.stack.pop().map(|node| { + self.stack.pop().inspect(|node| { // child nodes are stored ordered by max branch height (decreasing), // we want to keep this ordering while iterating but since we're // using a stack for iterator state we need to reverse it. self.stack.extend(node.children.iter().rev()); - node }) } } diff --git a/substrate/utils/frame/benchmarking-cli/Cargo.toml b/substrate/utils/frame/benchmarking-cli/Cargo.toml index 4e88e3360e3914a78d0bd8b71b8b55ab8bd22f8f..ee5522f5bc0466a46b7c17a150c9ceece3b46c91 100644 --- a/substrate/utils/frame/benchmarking-cli/Cargo.toml +++ b/substrate/utils/frame/benchmarking-cli/Cargo.toml @@ -24,7 +24,6 @@ comfy-table = { workspace = true } handlebars = { workspace = true } Inflector = { workspace = true } itertools = { workspace = true } -lazy_static = { workspace = true } linked-hash-map = { workspace = true } log = { workspace = true, default-features = true } rand = { features = ["small_rng"], workspace = true, default-features = true } diff --git a/substrate/utils/frame/benchmarking-cli/build.rs b/substrate/utils/frame/benchmarking-cli/build.rs index 1545d1e0c21e9416b8df6f591c1a1f7307b05996..06cdb7973abd37b237ce161c457c6f2948960eee 100644 --- a/substrate/utils/frame/benchmarking-cli/build.rs +++ b/substrate/utils/frame/benchmarking-cli/build.rs @@ -24,8 +24,12 @@ use std::env; pub fn main() { if let Ok(opt_level) = env::var("OPT_LEVEL") { println!("cargo:rustc-cfg=build_opt_level={:?}", opt_level); + } else { + println!("cargo:rustc-cfg=build_opt_level={:?}", "unknown"); } if let Ok(profile) = env::var("PROFILE") { println!("cargo:rustc-cfg=build_profile={:?}", profile); + } else { + println!("cargo:rustc-cfg=build_profile={:?}", "unknown"); } } diff --git a/substrate/utils/frame/benchmarking-cli/src/machine/hardware.rs b/substrate/utils/frame/benchmarking-cli/src/machine/hardware.rs index 555e848f8ccc648cdb972213ab266c3cedb7a234..f542eb60520ed063e51f7226ab367f61cba44668 100644 --- a/substrate/utils/frame/benchmarking-cli/src/machine/hardware.rs +++ b/substrate/utils/frame/benchmarking-cli/src/machine/hardware.rs @@ -17,19 +17,17 @@ //! Contains types to define hardware requirements. -use lazy_static::lazy_static; use sc_sysinfo::Requirements; +use std::sync::LazyLock; -lazy_static! { - /// The hardware requirements as measured on reference hardware. - /// - /// These values are provided by Parity, however it is possible - /// to use your own requirements if you are running a custom chain. - pub static ref SUBSTRATE_REFERENCE_HARDWARE: Requirements = { - let raw = include_bytes!("reference_hardware.json").as_slice(); - serde_json::from_slice(raw).expect("Hardcoded data is known good; qed") - }; -} +/// The hardware requirements as measured on reference hardware. +/// +/// These values are provided by Parity, however it is possible +/// to use your own requirements if you are running a custom chain. +pub static SUBSTRATE_REFERENCE_HARDWARE: LazyLock = LazyLock::new(|| { + let raw = include_bytes!("reference_hardware.json").as_slice(); + serde_json::from_slice(raw).expect("Hardcoded data is known good; qed") +}); #[cfg(test)] mod tests { @@ -51,17 +49,36 @@ mod tests { assert_eq!( *SUBSTRATE_REFERENCE_HARDWARE, Requirements(vec![ - Requirement { metric: Metric::Blake2256, minimum: Throughput::from_mibs(1000.00) }, + Requirement { + metric: Metric::Blake2256, + minimum: Throughput::from_mibs(1000.00), + validator_only: false + }, + Requirement { + metric: Metric::Blake2256Parallel { num_cores: 8 }, + minimum: Throughput::from_mibs(1000.00), + validator_only: true, + }, Requirement { metric: Metric::Sr25519Verify, minimum: Throughput::from_kibs(637.619999744), + validator_only: false }, Requirement { metric: Metric::MemCopy, minimum: Throughput::from_gibs(11.4925205078125003), + validator_only: false, + }, + Requirement { + metric: Metric::DiskSeqWrite, + minimum: Throughput::from_mibs(950.0), + validator_only: false, + }, + Requirement { + metric: Metric::DiskRndWrite, + minimum: Throughput::from_mibs(420.0), + validator_only: false }, - Requirement { metric: Metric::DiskSeqWrite, minimum: Throughput::from_mibs(950.0) }, - Requirement { metric: Metric::DiskRndWrite, minimum: Throughput::from_mibs(420.0) }, ]) ); } diff --git a/substrate/utils/frame/benchmarking-cli/src/machine/mod.rs b/substrate/utils/frame/benchmarking-cli/src/machine/mod.rs index fb9f14c9a4af18ed540c74849934b6e124f4671e..0186ca58762d561a24747d8cb95d7ff8158cfcb2 100644 --- a/substrate/utils/frame/benchmarking-cli/src/machine/mod.rs +++ b/substrate/utils/frame/benchmarking-cli/src/machine/mod.rs @@ -29,9 +29,9 @@ use log::{error, info, warn}; use sc_cli::{CliConfiguration, Result, SharedParams}; use sc_service::Configuration; use sc_sysinfo::{ - benchmark_cpu, benchmark_disk_random_writes, benchmark_disk_sequential_writes, - benchmark_memory, benchmark_sr25519_verify, ExecutionLimit, Metric, Requirement, Requirements, - Throughput, + benchmark_cpu, benchmark_cpu_parallelism, benchmark_disk_random_writes, + benchmark_disk_sequential_writes, benchmark_memory, benchmark_sr25519_verify, ExecutionLimit, + Metric, Requirement, Requirements, Throughput, }; use crate::shared::check_build_profile; @@ -150,6 +150,8 @@ impl MachineCmd { let score = match metric { Metric::Blake2256 => benchmark_cpu(hash_limit), + Metric::Blake2256Parallel { num_cores } => + benchmark_cpu_parallelism(hash_limit, *num_cores), Metric::Sr25519Verify => benchmark_sr25519_verify(verify_limit), Metric::MemCopy => benchmark_memory(memory_limit), Metric::DiskSeqWrite => benchmark_disk_sequential_writes(disk_limit, dir)?, diff --git a/substrate/utils/frame/benchmarking-cli/src/machine/reference_hardware.json b/substrate/utils/frame/benchmarking-cli/src/machine/reference_hardware.json index cec42b8f245cd7c1063394c828cada2c1a79a86f..654eaa6ff138a0ed845c1075d795b56b28f11757 100644 --- a/substrate/utils/frame/benchmarking-cli/src/machine/reference_hardware.json +++ b/substrate/utils/frame/benchmarking-cli/src/machine/reference_hardware.json @@ -3,6 +3,11 @@ "metric": "Blake2256", "minimum": 1000.00 }, + { + "metric": {"Blake2256Parallel":{"num_cores":8}}, + "minimum": 1000.00, + "validator_only": true + }, { "metric": "Sr25519Verify", "minimum": 0.622675781 diff --git a/substrate/utils/frame/benchmarking-cli/src/pallet/command.rs b/substrate/utils/frame/benchmarking-cli/src/pallet/command.rs index 47191981520630a8be7e7a5329b1da8bb8444ce6..f3334819057715bffc6945838e4510ba639d6b88 100644 --- a/substrate/utils/frame/benchmarking-cli/src/pallet/command.rs +++ b/substrate/utils/frame/benchmarking-cli/src/pallet/command.rs @@ -19,7 +19,7 @@ use super::{ types::{ComponentRange, ComponentRangeMap}, writer, ListOutput, PalletCmd, }; -use crate::pallet::{types::FetchedCode, GenesisBuilder}; +use crate::pallet::{types::FetchedCode, GenesisBuilderPolicy}; use codec::{Decode, Encode}; use frame_benchmarking::{ Analysis, BenchmarkBatch, BenchmarkBatchSplitResults, BenchmarkList, BenchmarkParameter, @@ -27,7 +27,7 @@ use frame_benchmarking::{ }; use frame_support::traits::StorageInfo; use linked_hash_map::LinkedHashMap; -use sc_chain_spec::json_patch::merge as json_merge; +use sc_chain_spec::GenesisConfigBuilderRuntimeCaller; use sc_cli::{execution_method_from_cli, ChainSpec, CliConfiguration, Result, SharedParams}; use sc_client_db::BenchmarkingState; use sc_executor::{HeapAllocStrategy, WasmExecutor, DEFAULT_HEAP_ALLOC_STRATEGY}; @@ -40,10 +40,10 @@ use sp_core::{ Hasher, }; use sp_externalities::Extensions; -use sp_genesis_builder::{PresetId, Result as GenesisBuildResult}; use sp_keystore::{testing::MemoryKeystore, KeystoreExt}; use sp_runtime::traits::Hash; -use sp_state_machine::{OverlayedChanges, StateMachine}; +use sp_state_machine::StateMachine; +use sp_storage::{well_known_keys::CODE, Storage}; use sp_trie::{proof_size_extension::ProofSizeExt, recorder::Recorder}; use sp_wasm_interface::HostFunctions; use std::{ @@ -162,9 +162,6 @@ generate the genesis state is deprecated. Please remove the `--chain`/`--dev`/`- point `--runtime` to your runtime blob and set `--genesis-builder=runtime`. This warning may \ become a hard error any time after December 2024."; -/// The preset that we expect to find in the GenesisBuilder runtime API. -const GENESIS_PRESET: &str = "development"; - impl PalletCmd { /// Runs the command and benchmarks a pallet. #[deprecated( @@ -214,9 +211,7 @@ impl PalletCmd { return self.output_from_results(&batches) } - let (genesis_storage, genesis_changes) = - self.genesis_storage::(&chain_spec)?; - let mut changes = genesis_changes.clone(); + let genesis_storage = self.genesis_storage::(&chain_spec)?; let cache_size = Some(self.database_cache_size as usize); let state_with_tracking = BenchmarkingState::::new( @@ -262,7 +257,7 @@ impl PalletCmd { Self::exec_state_machine( StateMachine::new( state, - &mut changes, + &mut Default::default(), &executor, "Benchmark_benchmark_metadata", &(self.extra).encode(), @@ -347,7 +342,6 @@ impl PalletCmd { for (s, selected_components) in all_components.iter().enumerate() { // First we run a verification if !self.no_verify { - let mut changes = genesis_changes.clone(); let state = &state_without_tracking; // Don't use these results since verification code will add overhead. let _batch: Vec = match Self::exec_state_machine::< @@ -357,7 +351,7 @@ impl PalletCmd { >( StateMachine::new( state, - &mut changes, + &mut Default::default(), &executor, "Benchmark_dispatch_benchmark", &( @@ -375,12 +369,12 @@ impl PalletCmd { "dispatch a benchmark", ) { Err(e) => { - log::error!("Error executing and verifying runtime benchmark: {}", e); + log::error!(target: LOG_TARGET, "Error executing and verifying runtime benchmark: {}", e); failed.push((pallet.clone(), extrinsic.clone())); continue 'outer }, Ok(Err(e)) => { - log::error!("Error executing and verifying runtime benchmark: {}", e); + log::error!(target: LOG_TARGET, "Error executing and verifying runtime benchmark: {}", e); failed.push((pallet.clone(), extrinsic.clone())); continue 'outer }, @@ -389,7 +383,6 @@ impl PalletCmd { } // Do one loop of DB tracking. { - let mut changes = genesis_changes.clone(); let state = &state_with_tracking; let batch: Vec = match Self::exec_state_machine::< std::result::Result, String>, @@ -398,7 +391,7 @@ impl PalletCmd { >( StateMachine::new( state, // todo remove tracking - &mut changes, + &mut Default::default(), &executor, "Benchmark_dispatch_benchmark", &( @@ -416,12 +409,12 @@ impl PalletCmd { "dispatch a benchmark", ) { Err(e) => { - log::error!("Error executing runtime benchmark: {}", e); + log::error!(target: LOG_TARGET, "Error executing runtime benchmark: {}", e); failed.push((pallet.clone(), extrinsic.clone())); continue 'outer }, Ok(Err(e)) => { - log::error!("Benchmark {pallet}::{extrinsic} failed: {e}",); + log::error!(target: LOG_TARGET, "Benchmark {pallet}::{extrinsic} failed: {e}",); failed.push((pallet.clone(), extrinsic.clone())); continue 'outer }, @@ -432,7 +425,6 @@ impl PalletCmd { } // Finally run a bunch of loops to get extrinsic timing information. for r in 0..self.external_repeat { - let mut changes = genesis_changes.clone(); let state = &state_without_tracking; let batch = match Self::exec_state_machine::< std::result::Result, String>, @@ -441,7 +433,7 @@ impl PalletCmd { >( StateMachine::new( state, // todo remove tracking - &mut changes, + &mut Default::default(), &executor, "Benchmark_dispatch_benchmark", &( @@ -511,33 +503,28 @@ impl PalletCmd { } fn select_benchmarks_to_run(&self, list: Vec) -> Result> { - 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(); // Use the benchmark list and the user input to determine the set of benchmarks to run. let mut benchmarks_to_run = Vec::new(); - list.iter() - .filter(|item| pallet.is_empty() || pallet == &b"*"[..] || pallet == &item.pallet[..]) - .for_each(|item| { - for benchmark in &item.benchmarks { - let benchmark_name = &benchmark.name; - if extrinsic.is_empty() || - extrinsic.as_bytes() == &b"*"[..] || - extrinsics.contains(&&benchmark_name[..]) - { - benchmarks_to_run.push(( - item.pallet.clone(), - benchmark.name.clone(), - benchmark.components.clone(), - benchmark.pov_modes.clone(), - )) - } + list.iter().filter(|item| self.pallet_selected(&item.pallet)).for_each(|item| { + for benchmark in &item.benchmarks { + let benchmark_name = &benchmark.name; + if extrinsic.is_empty() || + extrinsic.as_bytes() == &b"*"[..] || + extrinsics.contains(&&benchmark_name[..]) + { + benchmarks_to_run.push(( + item.pallet.clone(), + benchmark.name.clone(), + benchmark.components.clone(), + benchmark.pov_modes.clone(), + )) } - }); + } + }); // Convert `Vec` to `String` for better readability. let benchmarks_to_run: Vec<_> = benchmarks_to_run .into_iter() @@ -567,20 +554,29 @@ impl PalletCmd { Ok(benchmarks_to_run) } - /// Produce a genesis storage and genesis changes. + /// Whether this pallet should be run. + fn pallet_selected(&self, pallet: &Vec) -> bool { + let include = self.pallet.clone().unwrap_or_default(); + + let included = include.is_empty() || include == "*" || include.as_bytes() == pallet; + let excluded = self.exclude_pallets.iter().any(|p| p.as_bytes() == pallet); + + included && !excluded + } + + /// Build the genesis storage by either the Genesis Builder API, chain spec or nothing. /// - /// It would be easier to only return one type, but there is no easy way to convert them. - // TODO: Re-write `BenchmarkingState` to not be such a clusterfuck and only accept - // `OverlayedChanges` instead of a mix between `OverlayedChanges` and `State`. But this can only - // be done once we deprecated and removed the legacy interface :( - fn genesis_storage( + /// Behaviour can be controlled by the `--genesis-builder` flag. + fn genesis_storage( &self, chain_spec: &Option>, - ) -> Result<(sp_storage::Storage, OverlayedChanges)> { - Ok(match (self.genesis_builder, self.runtime.is_some()) { - (Some(GenesisBuilder::None), _) => Default::default(), - (Some(GenesisBuilder::Spec), _) | (None, false) => { - log::warn!("{WARN_SPEC_GENESIS_CTOR}"); + ) -> Result { + Ok(match (self.genesis_builder, self.runtime.as_ref()) { + (Some(GenesisBuilderPolicy::None), _) => Storage::default(), + (Some(GenesisBuilderPolicy::SpecGenesis | GenesisBuilderPolicy::Spec), Some(_)) => + return Err("Cannot use `--genesis-builder=spec-genesis` with `--runtime` since the runtime would be ignored.".into()), + (Some(GenesisBuilderPolicy::SpecGenesis | GenesisBuilderPolicy::Spec), None) | (None, None) => { + log::warn!(target: LOG_TARGET, "{WARN_SPEC_GENESIS_CTOR}"); let Some(chain_spec) = chain_spec else { return Err("No chain spec specified to generate the genesis state".into()); }; @@ -589,111 +585,74 @@ impl PalletCmd { .build_storage() .map_err(|e| format!("{ERROR_CANNOT_BUILD_GENESIS}\nError: {e}"))?; - (storage, Default::default()) + storage }, - (Some(GenesisBuilder::Runtime), _) | (None, true) => - (Default::default(), self.genesis_from_runtime::()?), - }) - } + (Some(GenesisBuilderPolicy::SpecRuntime), Some(_)) => + return Err("Cannot use `--genesis-builder=spec` with `--runtime` since the runtime would be ignored.".into()), + (Some(GenesisBuilderPolicy::SpecRuntime), None) => { + let Some(chain_spec) = chain_spec else { + return Err("No chain spec specified to generate the genesis state".into()); + }; - /// Generate the genesis changeset by the runtime API. - fn genesis_from_runtime(&self) -> Result> { - let state = BenchmarkingState::::new( - Default::default(), - Some(self.database_cache_size as usize), - false, - false, - )?; + self.genesis_from_spec_runtime::(chain_spec.as_ref())? + }, + (Some(GenesisBuilderPolicy::Runtime), None) => return Err("Cannot use `--genesis-builder=runtime` without `--runtime`".into()), + (Some(GenesisBuilderPolicy::Runtime), Some(runtime)) | (None, Some(runtime)) => { + log::info!(target: LOG_TARGET, "Loading WASM from {}", runtime.display()); + + let code = fs::read(&runtime).map_err(|e| { + format!( + "Could not load runtime file from path: {}, error: {}", + runtime.display(), + e + ) + })?; - // Create a dummy WasmExecutor just to build the genesis storage. - let method = - execution_method_from_cli(self.wasm_method, self.wasmtime_instantiation_strategy); - let executor = WasmExecutor::<( - sp_io::SubstrateHostFunctions, - frame_benchmarking::benchmarking::HostFunctions, - F, - )>::builder() - .with_execution_method(method) - .with_allow_missing_host_functions(self.allow_missing_host_functions) - .build(); + self.genesis_from_code::(&code)? + } + }) + } - let runtime = self.runtime_blob(&state)?; - let runtime_code = runtime.code()?; + /// Setup the genesis state by calling the runtime APIs of the chain-specs genesis runtime. + fn genesis_from_spec_runtime( + &self, + chain_spec: &dyn ChainSpec, + ) -> Result { + log::info!(target: LOG_TARGET, "Building genesis state from chain spec runtime"); + let storage = chain_spec + .build_storage() + .map_err(|e| format!("{ERROR_CANNOT_BUILD_GENESIS}\nError: {e}"))?; - // We cannot use the `GenesisConfigBuilderRuntimeCaller` here since it returns the changes - // as `Storage` item, but we need it as `OverlayedChanges`. - let genesis_json: Option> = Self::exec_state_machine( - StateMachine::new( - &state, - &mut Default::default(), - &executor, - "GenesisBuilder_get_preset", - &None::.encode(), // Use the default preset - &mut Self::build_extensions(executor.clone(), state.recorder()), - &runtime_code, - CallContext::Offchain, - ), - "build the genesis spec", - )?; + let code: &Vec = + storage.top.get(CODE).ok_or("No runtime code in the genesis storage")?; - let Some(base_genesis_json) = genesis_json else { - return Err("GenesisBuilder::get_preset returned no data".into()) - }; - - let base_genesis_json = serde_json::from_slice::(&base_genesis_json) - .map_err(|e| format!("GenesisBuilder::get_preset returned invalid JSON: {:?}", e))?; - - let dev_genesis_json: Option> = Self::exec_state_machine( - StateMachine::new( - &state, - &mut Default::default(), - &executor, - "GenesisBuilder_get_preset", - &Some::(GENESIS_PRESET.into()).encode(), // Use the default preset - &mut Self::build_extensions(executor.clone(), state.recorder()), - &runtime_code, - CallContext::Offchain, - ), - "build the genesis spec", - )?; + self.genesis_from_code::(code) + } - let mut genesis_json = serde_json::Value::default(); - json_merge(&mut genesis_json, base_genesis_json); + fn genesis_from_code(&self, code: &[u8]) -> Result { + let genesis_config_caller = GenesisConfigBuilderRuntimeCaller::<( + sp_io::SubstrateHostFunctions, + frame_benchmarking::benchmarking::HostFunctions, + EHF, + )>::new(code); + let preset = Some(&self.genesis_builder_preset); - if let Some(dev) = dev_genesis_json { - let dev: serde_json::Value = serde_json::from_slice(&dev).map_err(|e| { - format!("GenesisBuilder::get_preset returned invalid JSON: {:?}", e) + let mut storage = + genesis_config_caller.get_storage_for_named_preset(preset).inspect_err(|e| { + let presets = genesis_config_caller.preset_names().unwrap_or_default(); + log::error!( + target: LOG_TARGET, + "Please pick one of the available presets with \ + `--genesis-builder-preset=` or use a different `--genesis-builder-policy`. Available presets ({}): {:?}. Error: {:?}", + presets.len(), + presets, + e + ); })?; - json_merge(&mut genesis_json, dev); - } else { - log::warn!( - "Could not find genesis preset '{GENESIS_PRESET}'. Falling back to default." - ); - } - let json_pretty_str = serde_json::to_string_pretty(&genesis_json) - .map_err(|e| format!("json to string failed: {e}"))?; - - let mut changes = Default::default(); - let build_res: GenesisBuildResult = Self::exec_state_machine( - StateMachine::new( - &state, - &mut changes, - &executor, - "GenesisBuilder_build_state", - &json_pretty_str.encode(), - &mut Extensions::default(), - &runtime_code, - CallContext::Offchain, - ), - "populate the genesis state", - )?; + storage.top.insert(CODE.into(), code.into()); - if let Err(e) = build_res { - return Err(format!("GenesisBuilder::build_state failed: {}", e).into()) - } - - Ok(changes) + Ok(storage) } /// Execute a state machine and decode its return value as `R`. @@ -737,15 +696,21 @@ impl PalletCmd { &self, state: &'a BenchmarkingState, ) -> Result, H>> { - if let Some(runtime) = &self.runtime { - log::info!("Loading WASM from {}", runtime.display()); - let code = fs::read(runtime)?; + if let Some(runtime) = self.runtime.as_ref() { + log::info!(target: LOG_TARGET, "Loading WASM from file"); + let code = fs::read(runtime).map_err(|e| { + format!( + "Could not load runtime file from path: {}, error: {}", + runtime.display(), + e + ) + })?; let hash = sp_core::blake2_256(&code).to_vec(); let wrapped_code = WrappedRuntimeCode(Cow::Owned(code)); Ok(FetchedCode::FromFile { wrapped_code, heap_pages: self.heap_pages, hash }) } else { - log::info!("Loading WASM from genesis state"); + log::info!(target: LOG_TARGET, "Loading WASM from state"); let state = sp_state_machine::backend::BackendRuntimeCode::new(state); Ok(FetchedCode::FromGenesis { state }) @@ -990,19 +955,25 @@ impl PalletCmd { if let Some(output_path) = &self.output { if !output_path.is_dir() && output_path.file_name().is_none() { - return Err("Output file or path is invalid!".into()) + return Err(format!( + "Output path is neither a directory nor a file: {output_path:?}" + ) + .into()) } } if let Some(header_file) = &self.header { if !header_file.is_file() { - return Err("Header file is invalid!".into()) + return Err(format!("Header file could not be found: {header_file:?}").into()) }; } if let Some(handlebars_template_file) = &self.template { if !handlebars_template_file.is_file() { - return Err("Handlebars template file is invalid!".into()) + return Err(format!( + "Handlebars template file could not be found: {handlebars_template_file:?}" + ) + .into()) }; } diff --git a/substrate/utils/frame/benchmarking-cli/src/pallet/mod.rs b/substrate/utils/frame/benchmarking-cli/src/pallet/mod.rs index ebf737be1dbf645cd60443f562b406fd061e70d3..412a1a86cb8e7741a15de1e43bb47169c4f13de3 100644 --- a/substrate/utils/frame/benchmarking-cli/src/pallet/mod.rs +++ b/substrate/utils/frame/benchmarking-cli/src/pallet/mod.rs @@ -19,7 +19,7 @@ mod command; mod types; mod writer; -use crate::{pallet::types::GenesisBuilder, shared::HostInfoParams}; +use crate::{pallet::types::GenesisBuilderPolicy, shared::HostInfoParams}; use clap::ValueEnum; use sc_cli::{ WasmExecutionMethod, WasmtimeInstantiationStrategy, DEFAULT_WASMTIME_INSTANTIATION_STRATEGY, @@ -53,6 +53,10 @@ pub struct PalletCmd { #[arg(short, long, required_unless_present_any = ["list", "json_input", "all"], default_value_if("all", "true", Some("*".into())))] pub extrinsic: Option, + /// Comma separated list of pallets that should be excluded from the benchmark. + #[arg(long, value_parser, num_args = 1.., value_delimiter = ',')] + pub exclude_pallets: Vec, + /// Run benchmarks for all pallets and extrinsics. /// /// This is equivalent to running `--pallet * --extrinsic *`. @@ -177,9 +181,17 @@ pub struct PalletCmd { /// How to construct the genesis state. /// - /// Uses `GenesisBuilder::Spec` by default and `GenesisBuilder::Runtime` if `runtime` is set. - #[arg(long, value_enum)] - pub genesis_builder: Option, + /// Uses `GenesisBuilderPolicy::Spec` by default and `GenesisBuilderPolicy::Runtime` if + /// `runtime` is set. + #[arg(long, value_enum, alias = "genesis-builder-policy")] + pub genesis_builder: Option, + + /// The preset that we expect to find in the GenesisBuilder runtime API. + /// + /// This can be useful when a runtime has a dedicated benchmarking preset instead of using the + /// default one. + #[arg(long, default_value = sp_genesis_builder::DEV_RUNTIME_PRESET)] + pub genesis_builder_preset: String, /// DEPRECATED: This argument has no effect. #[arg(long = "execution")] diff --git a/substrate/utils/frame/benchmarking-cli/src/pallet/types.rs b/substrate/utils/frame/benchmarking-cli/src/pallet/types.rs index 2bb00d66560f3ed09aea5398837e90687eb28630..a4799dc92369850a5f32505996bfbc407ef98d45 100644 --- a/substrate/utils/frame/benchmarking-cli/src/pallet/types.rs +++ b/substrate/utils/frame/benchmarking-cli/src/pallet/types.rs @@ -24,7 +24,7 @@ use sp_runtime::traits::Hash; /// How the genesis state for benchmarking should be build. #[derive(clap::ValueEnum, Debug, Eq, PartialEq, Clone, Copy)] #[clap(rename_all = "kebab-case")] -pub enum GenesisBuilder { +pub enum GenesisBuilderPolicy { /// Do not provide any genesis state. /// /// Benchmarks are advised to function with this, since they should setup their own required @@ -32,7 +32,11 @@ pub enum GenesisBuilder { None, /// Let the runtime build the genesis state through its `BuildGenesisConfig` runtime API. Runtime, + // Use the runtime from the Spec file to build the genesis state. + SpecRuntime, /// Use the spec file to build the genesis state. This fails when there is no spec. + SpecGenesis, + /// Same as `SpecGenesis` - only here for backwards compatibility. Spec, } diff --git a/substrate/utils/frame/benchmarking-cli/src/pallet/writer.rs b/substrate/utils/frame/benchmarking-cli/src/pallet/writer.rs index df7d81b2822e30926704980fb21e0a8d2cdbd66c..28918dd4e6a3975ead5aee9fe051958dc39e7b31 100644 --- a/substrate/utils/frame/benchmarking-cli/src/pallet/writer.rs +++ b/substrate/utils/frame/benchmarking-cli/src/pallet/writer.rs @@ -484,7 +484,9 @@ pub(crate) fn write_results( benchmarks: results.clone(), }; - let mut output_file = fs::File::create(&file_path)?; + let mut output_file = fs::File::create(&file_path).map_err(|e| { + format!("Could not write weight file to: {:?}. Error: {:?}", &file_path, e) + })?; handlebars .render_template_to_write(&template, &hbs_data, &mut output_file) .map_err(|e| io_error(&e.to_string()))?; diff --git a/substrate/utils/prometheus/src/lib.rs b/substrate/utils/prometheus/src/lib.rs index 7a8c65590605265aebc91f02b9f17880d3ab926f..460640bcd8e410452106de2655cf1a4f357689dc 100644 --- a/substrate/utils/prometheus/src/lib.rs +++ b/substrate/utils/prometheus/src/lib.rs @@ -86,9 +86,10 @@ async fn request_metrics( /// Initializes the metrics context, and starts an HTTP server /// to serve metrics. pub async fn init_prometheus(prometheus_addr: SocketAddr, registry: Registry) -> Result<(), Error> { - let listener = tokio::net::TcpListener::bind(&prometheus_addr) - .await - .map_err(|_| Error::PortInUse(prometheus_addr))?; + let listener = tokio::net::TcpListener::bind(&prometheus_addr).await.map_err(|e| { + log::error!(target: "prometheus", "Error binding to '{:#?}': {:#?}", prometheus_addr, e); + Error::PortInUse(prometheus_addr) + })?; init_prometheus_with_listener(listener, registry).await } diff --git a/substrate/utils/wasm-builder/Cargo.toml b/substrate/utils/wasm-builder/Cargo.toml index 15a1fd007ca25f4f93b1857ec70220b71b8c3d16..8f0e8a23e54af70f3281740ef1f1a0db5b4e643c 100644 --- a/substrate/utils/wasm-builder/Cargo.toml +++ b/substrate/utils/wasm-builder/Cargo.toml @@ -39,6 +39,7 @@ frame-metadata = { features = ["current"], optional = true, workspace = true, de codec = { optional = true, workspace = true, default-features = true } array-bytes = { optional = true, workspace = true, default-features = true } sp-tracing = { optional = true, workspace = true, default-features = true } +shlex = { workspace = true } [features] # Enable support for generating the metadata hash. diff --git a/substrate/utils/wasm-builder/src/lib.rs b/substrate/utils/wasm-builder/src/lib.rs index 07de4c15831b842e6ac559ebf445901744d7d98f..e3f2ff5cd733498a01e3d92b9f731d7247c9761f 100644 --- a/substrate/utils/wasm-builder/src/lib.rs +++ b/substrate/utils/wasm-builder/src/lib.rs @@ -84,6 +84,9 @@ //! - `WASM_BUILD_STD` - Sets whether the Rust's standard library crates will also be built. This is //! necessary to make sure the standard library crates only use the exact WASM feature set that //! our executor supports. Enabled by default. +//! - `WASM_BUILD_CARGO_ARGS` - This can take a string as space separated list of `cargo` arguments. +//! It was added specifically for the use case of enabling JSON diagnostic messages during the +//! build phase, to be used by IDEs that parse them, but it might be useful for other cases too. //! - `CARGO_NET_OFFLINE` - If `true`, `--offline` will be passed to all processes launched to //! prevent network access. Useful in offline environments. //! @@ -161,6 +164,10 @@ 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"; +/// Environment variable to set additional cargo arguments that might be useful +/// during the build phase. +const WASM_BUILD_CARGO_ARGS: &str = "WASM_BUILD_CARGO_ARGS"; + /// The target to use for the runtime. Valid values are `wasm` (default) or `riscv`. const RUNTIME_TARGET: &str = "SUBSTRATE_RUNTIME_TARGET"; diff --git a/substrate/utils/wasm-builder/src/wasm_project.rs b/substrate/utils/wasm-builder/src/wasm_project.rs index a6eda078fde0fa6192e56f605c4af058208976e4..26edd2ea1f2242525ed61216a309a1d9be367881 100644 --- a/substrate/utils/wasm-builder/src/wasm_project.rs +++ b/substrate/utils/wasm-builder/src/wasm_project.rs @@ -601,9 +601,10 @@ fn project_enabled_features( // We don't want to enable the `std`/`default` feature for the wasm build and // we need to check if the feature is enabled by checking the env variable. *f != "std" && - *f != "default" && env::var(format!("CARGO_FEATURE_{}", feature_env)) - .map(|v| v == "1") - .unwrap_or_default() + *f != "default" && + env::var(format!("CARGO_FEATURE_{feature_env}")) + .map(|v| v == "1") + .unwrap_or_default() }) .map(|d| d.0.clone()) .collect::>(); @@ -868,6 +869,18 @@ fn build_bloaty_blob( // We don't want to call ourselves recursively .env(crate::SKIP_BUILD_ENV, ""); + let cargo_args = env::var(crate::WASM_BUILD_CARGO_ARGS).unwrap_or_default(); + if !cargo_args.is_empty() { + let Some(args) = shlex::split(&cargo_args) else { + build_helper::warning(format!( + "the {} environment variable is not a valid shell string", + crate::WASM_BUILD_CARGO_ARGS + )); + std::process::exit(1); + }; + build_cmd.args(args); + } + #[cfg(feature = "metadata-hash")] if let Some(hash) = metadata_hash { build_cmd.env("RUNTIME_METADATA_HASH", array_bytes::bytes2hex("0x", &hash)); @@ -1139,6 +1152,7 @@ fn generate_rerun_if_changed_instructions( 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); + println!("cargo:rerun-if-env-changed={}", crate::WASM_BUILD_CARGO_ARGS); } /// Track files and paths related to the given package to rerun `build.rs` on any relevant change. diff --git a/templates/minimal/README.md b/templates/minimal/README.md index 180c229e744e92eac0533157eb22cf3968c78d44..fe1317a033c7dab4c50aa552ee2a8e14e49d882a 100644 --- a/templates/minimal/README.md +++ b/templates/minimal/README.md @@ -37,6 +37,14 @@ A Polkadot SDK based project such as this one consists of: * ๐Ÿ› ๏ธ Depending on your operating system and Rust version, there might be additional packages required to compile this template - please take note of the Rust compiler output. +Fetch minimal template code: + +```sh +git clone https://github.com/paritytech/polkadot-sdk-minimal-template.git minimal-template + +cd minimal-template +``` + ### Build ๐Ÿ”จ Use the following command to build the node without launching it: diff --git a/templates/minimal/node/src/chain_spec.rs b/templates/minimal/node/src/chain_spec.rs index 0646460acef641ffd7155a35ae6fb0dba8742ef4..17b98137b416a989ae2668ad5468e0d9bc62de84 100644 --- a/templates/minimal/node/src/chain_spec.rs +++ b/templates/minimal/node/src/chain_spec.rs @@ -15,13 +15,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -use minimal_template_runtime::{BalancesConfig, SudoConfig, WASM_BINARY}; +use minimal_template_runtime::WASM_BINARY; use polkadot_sdk::{ sc_service::{ChainType, Properties}, - sp_keyring::AccountKeyring, *, }; -use serde_json::{json, Value}; /// This is a specialization of the general Substrate ChainSpec type. pub type ChainSpec = sc_service::GenericChainSpec; @@ -33,26 +31,12 @@ fn props() -> Properties { properties } -pub fn development_config() -> Result { +pub fn development_chain_spec() -> Result { Ok(ChainSpec::builder(WASM_BINARY.expect("Development wasm not available"), Default::default()) .with_name("Development") .with_id("dev") .with_chain_type(ChainType::Development) - .with_genesis_config_patch(testnet_genesis()) + .with_genesis_config_preset_name(sp_genesis_builder::DEV_RUNTIME_PRESET) .with_properties(props()) .build()) } - -/// Configure initial storage state for FRAME pallets. -fn testnet_genesis() -> Value { - use minimal_template_runtime::interface::{Balance, MinimumBalance}; - use polkadot_sdk::polkadot_sdk_frame::traits::Get; - let endowment = >::get().max(1) * 1000; - let balances = AccountKeyring::iter() - .map(|a| (a.to_account_id(), endowment)) - .collect::>(); - json!({ - "balances": BalancesConfig { balances }, - "sudo": SudoConfig { key: Some(AccountKeyring::Alice.to_account_id()) }, - }) -} diff --git a/templates/minimal/node/src/command.rs b/templates/minimal/node/src/command.rs index b09ea1fab2379d1e675b91393204002dfd971ebf..5cb0694d982820519a2a78e72f7f4e081b5771c0 100644 --- a/templates/minimal/node/src/command.rs +++ b/templates/minimal/node/src/command.rs @@ -49,7 +49,7 @@ impl SubstrateCli for Cli { fn load_spec(&self, id: &str) -> Result, String> { Ok(match id { - "dev" => Box::new(chain_spec::development_config()?), + "dev" => Box::new(chain_spec::development_chain_spec()?), path => Box::new(chain_spec::ChainSpec::from_json_file(std::path::PathBuf::from(path))?), }) diff --git a/templates/minimal/node/src/service.rs b/templates/minimal/node/src/service.rs index ce3afe1e6b761e8afe3fdf92c164052a683d82e4..08cd345f1e3edf1f524afc42c4ac2c749fe1996e 100644 --- a/templates/minimal/node/src/service.rs +++ b/templates/minimal/node/src/service.rs @@ -15,12 +15,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +use crate::cli::Consensus; use futures::FutureExt; use minimal_template_runtime::{interface::OpaqueBlock as Block, RuntimeApi}; use polkadot_sdk::{ sc_client_api::backend::Backend, sc_executor::WasmExecutor, - sc_service::{error::Error as ServiceError, Configuration, TaskManager}, + sc_service::{ + build_polkadot_syncing_strategy, error::Error as ServiceError, Configuration, TaskManager, + }, sc_telemetry::{Telemetry, TelemetryWorker}, sc_transaction_pool_api::OffchainTransactionPoolFactory, sp_runtime::traits::Block as BlockT, @@ -28,8 +31,6 @@ use polkadot_sdk::{ }; use std::sync::Arc; -use crate::cli::Consensus; - type HostFunctions = sp_io::SubstrateHostFunctions; #[docify::export] @@ -61,7 +62,7 @@ pub fn new_partial(config: &Configuration) -> Result { }) .transpose()?; - let executor = sc_service::new_wasm_executor(config); + let executor = sc_service::new_wasm_executor(&config.executor); let (client, backend, keystore_container, task_manager) = sc_service::new_full_parts::( @@ -120,7 +121,7 @@ pub fn new_full::Ha other: mut telemetry, } = new_partial(&config)?; - let net_config = sc_network::config::FullNetworkConfiguration::< + let mut net_config = sc_network::config::FullNetworkConfiguration::< Block, ::Hash, Network, @@ -132,6 +133,16 @@ pub fn new_full::Ha config.prometheus_config.as_ref().map(|cfg| &cfg.registry), ); + let syncing_strategy = build_polkadot_syncing_strategy( + config.protocol_id(), + config.chain_spec.fork_id(), + &mut net_config, + None, + client.clone(), + &task_manager.spawn_handle(), + config.prometheus_config.as_ref().map(|config| &config.registry), + )?; + let (network, system_rpc_tx, tx_handler_controller, network_starter, sync_service) = sc_service::build_network(sc_service::BuildNetworkParams { config: &config, @@ -141,7 +152,7 @@ pub fn new_full::Ha import_queue, net_config, block_announce_validator_builder: None, - warp_sync_config: None, + syncing_strategy, block_relay: None, metrics, })?; diff --git a/templates/minimal/runtime/Cargo.toml b/templates/minimal/runtime/Cargo.toml index 49ddf3987e96f09171a1a6e8cc457fd292dd77ce..74a09b9396e50b8323b441ab46f169d61a4f758a 100644 --- a/templates/minimal/runtime/Cargo.toml +++ b/templates/minimal/runtime/Cargo.toml @@ -21,6 +21,7 @@ polkadot-sdk = { workspace = true, features = [ "pallet-transaction-payment-rpc-runtime-api", "runtime", ] } +serde_json = { workspace = true, default-features = false, features = ["alloc"] } # local pallet templates pallet-minimal-template = { workspace = true } @@ -37,4 +38,5 @@ std = [ "pallet-minimal-template/std", "polkadot-sdk/std", "scale-info/std", + "serde_json/std", ] diff --git a/templates/minimal/runtime/src/lib.rs b/templates/minimal/runtime/src/lib.rs index 474d9ddfb9e87e881c363a0a238032d2e19af61b..7379e33b6b3e8236c7fae32d2af2a82242442523 100644 --- a/templates/minimal/runtime/src/lib.rs +++ b/templates/minimal/runtime/src/lib.rs @@ -25,7 +25,7 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); extern crate alloc; -use alloc::{vec, vec::Vec}; +use alloc::vec::Vec; use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo}; use polkadot_sdk::{ polkadot_sdk_frame::{ @@ -36,6 +36,54 @@ use polkadot_sdk::{ *, }; +/// Provides getters for genesis configuration presets. +pub mod genesis_config_presets { + use crate::{ + interface::{Balance, MinimumBalance}, + sp_genesis_builder::PresetId, + sp_keyring::AccountKeyring, + BalancesConfig, RuntimeGenesisConfig, SudoConfig, + }; + + use alloc::{vec, vec::Vec}; + use polkadot_sdk::{sp_core::Get, sp_genesis_builder}; + use serde_json::Value; + + /// Returns a development genesis config preset. + pub fn development_config_genesis() -> Value { + let endowment = >::get().max(1) * 1000; + let config = RuntimeGenesisConfig { + balances: BalancesConfig { + balances: AccountKeyring::iter() + .map(|a| (a.to_account_id(), endowment)) + .collect::>(), + }, + sudo: SudoConfig { key: Some(AccountKeyring::Alice.to_account_id()) }, + ..Default::default() + }; + + serde_json::to_value(config).expect("Could not build genesis config.") + } + + /// Get the set of the available genesis config presets. + pub fn get_preset(id: &PresetId) -> Option> { + let patch = match id.try_into() { + Ok(sp_genesis_builder::DEV_RUNTIME_PRESET) => development_config_genesis(), + _ => return None, + }; + Some( + serde_json::to_string(&patch) + .expect("serialization to json is expected to work. qed.") + .into_bytes(), + ) + } + + /// List of supported presets. + pub fn preset_names() -> Vec { + vec![PresetId::from(sp_genesis_builder::DEV_RUNTIME_PRESET)] + } +} + /// The runtime version. #[runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { @@ -46,7 +94,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 1, + system_version: 1, }; /// The version information used to identify this runtime when compiled natively. @@ -272,11 +320,11 @@ impl_runtime_apis! { } fn get_preset(id: &Option) -> Option> { - get_preset::(id, |_| None) + get_preset::(id, self::genesis_config_presets::get_preset) } fn preset_names() -> Vec { - vec![] + self::genesis_config_presets::preset_names() } } } diff --git a/templates/parachain/README.md b/templates/parachain/README.md index b912d8e005c7f9b155990633408df3a01f290d66..3de85cbeb4dc77a386fc702f645c0bb4e7ffb96d 100644 --- a/templates/parachain/README.md +++ b/templates/parachain/README.md @@ -39,6 +39,14 @@ A Polkadot SDK based project such as this one consists of: * ๐Ÿ› ๏ธ Depending on your operating system and Rust version, there might be additional packages required to compile this template - please take note of the Rust compiler output. +Fetch parachain template code: + +```sh +git clone https://github.com/paritytech/polkadot-sdk-parachain-template.git parachain-template + +cd parachain-template +``` + ### Build ๐Ÿ”จ Use the following command to build the node without launching it: diff --git a/templates/parachain/node/Cargo.toml b/templates/parachain/node/Cargo.toml index c782888a3e8913e7ec5bbbe831d7ac32093315e3..c0c81d8222a444503b30323eff0f44086bf87788 100644 --- a/templates/parachain/node/Cargo.toml +++ b/templates/parachain/node/Cargo.toml @@ -51,6 +51,7 @@ sp-block-builder = { workspace = true, default-features = true } sp-blockchain = { workspace = true, default-features = true } sp-consensus-aura = { workspace = true, default-features = true } sp-core = { workspace = true, default-features = true } +sp-genesis-builder = { workspace = true, default-features = true } sp-keystore = { workspace = true, default-features = true } sp-io = { workspace = true, default-features = true } sp-runtime = { workspace = true, default-features = true } diff --git a/templates/parachain/node/src/chain_spec.rs b/templates/parachain/node/src/chain_spec.rs index 3fa91c0261622cd71dbf2b473eb70748f3a29b96..af82607bc8e41947cb41220263cfbdfde3a45e81 100644 --- a/templates/parachain/node/src/chain_spec.rs +++ b/templates/parachain/node/src/chain_spec.rs @@ -1,25 +1,11 @@ -use cumulus_primitives_core::ParaId; use parachain_template_runtime as runtime; -use runtime::{AccountId, AuraId, Signature, EXISTENTIAL_DEPOSIT}; use sc_chain_spec::{ChainSpecExtension, ChainSpecGroup}; use sc_service::ChainType; use serde::{Deserialize, Serialize}; -use sp_core::{sr25519, Pair, Public}; -use sp_runtime::traits::{IdentifyAccount, Verify}; /// Specialized `ChainSpec` for the normal parachain runtime. pub type ChainSpec = sc_service::GenericChainSpec; -/// The default XCM version to set in genesis config. -const SAFE_XCM_VERSION: u32 = xcm::prelude::XCM_VERSION; - -/// Helper function to generate a crypto pair from seed -pub fn get_from_seed(seed: &str) -> ::Public { - TPublic::Pair::from_string(&format!("//{}", seed), None) - .expect("static values are valid; qed") - .public() -} - /// The extensions for the [`ChainSpec`]. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension)] pub struct Extensions { @@ -38,31 +24,7 @@ impl Extensions { } } -type AccountPublic = ::Signer; - -/// Generate collator keys from seed. -/// -/// This function's return type must always match the session keys of the chain in tuple format. -pub fn get_collator_keys_from_seed(seed: &str) -> AuraId { - get_from_seed::(seed) -} - -/// Helper function to generate an account ID from seed -pub fn get_account_id_from_seed(seed: &str) -> AccountId -where - AccountPublic: From<::Public>, -{ - AccountPublic::from(get_from_seed::(seed)).into_account() -} - -/// Generate the session keys from individual elements. -/// -/// The input must be a tuple of individual keys (a single arg for now since we have just one key). -pub fn template_session_keys(keys: AuraId) -> runtime::SessionKeys { - runtime::SessionKeys { aura: keys } -} - -pub fn development_config() -> ChainSpec { +pub fn development_chain_spec() -> ChainSpec { // Give your base currency a unit name and decimal places let mut properties = sc_chain_spec::Properties::new(); properties.insert("tokenSymbol".into(), "UNIT".into()); @@ -80,39 +42,11 @@ pub fn development_config() -> ChainSpec { .with_name("Development") .with_id("dev") .with_chain_type(ChainType::Development) - .with_genesis_config_patch(testnet_genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - get_account_id_from_seed::("Alice"), - 1000.into(), - )) + .with_genesis_config_preset_name(sp_genesis_builder::DEV_RUNTIME_PRESET) .build() } -pub fn local_testnet_config() -> ChainSpec { +pub fn local_chain_spec() -> ChainSpec { // Give your base currency a unit name and decimal places let mut properties = sc_chain_spec::Properties::new(); properties.insert("tokenSymbol".into(), "UNIT".into()); @@ -131,72 +65,8 @@ pub fn local_testnet_config() -> ChainSpec { .with_name("Local Testnet") .with_id("local_testnet") .with_chain_type(ChainType::Local) - .with_genesis_config_patch(testnet_genesis( - // initial collators. - vec![ - ( - get_account_id_from_seed::("Alice"), - get_collator_keys_from_seed("Alice"), - ), - ( - get_account_id_from_seed::("Bob"), - get_collator_keys_from_seed("Bob"), - ), - ], - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - get_account_id_from_seed::("Alice"), - 1000.into(), - )) + .with_genesis_config_preset_name(sc_chain_spec::LOCAL_TESTNET_RUNTIME_PRESET) .with_protocol_id("template-local") .with_properties(properties) .build() } - -fn testnet_genesis( - invulnerables: Vec<(AccountId, AuraId)>, - endowed_accounts: Vec, - root: AccountId, - id: ParaId, -) -> serde_json::Value { - serde_json::json!({ - "balances": { - "balances": endowed_accounts.iter().cloned().map(|k| (k, 1u64 << 60)).collect::>(), - }, - "parachainInfo": { - "parachainId": id, - }, - "collatorSelection": { - "invulnerables": invulnerables.iter().cloned().map(|(acc, _)| acc).collect::>(), - "candidacyBond": EXISTENTIAL_DEPOSIT * 16, - }, - "session": { - "keys": invulnerables - .into_iter() - .map(|(acc, aura)| { - ( - acc.clone(), // account id - acc, // validator id - template_session_keys(aura), // session keys - ) - }) - .collect::>(), - }, - "polkadotXcm": { - "safeXcmVersion": Some(SAFE_XCM_VERSION), - }, - "sudo": { "key": Some(root) } - }) -} diff --git a/templates/parachain/node/src/command.rs b/templates/parachain/node/src/command.rs index f346ae4386a68334fa9d4395ce6b73f85638f33c..6b9deade1271b183dd4e3de24075a0a804e29d3a 100644 --- a/templates/parachain/node/src/command.rs +++ b/templates/parachain/node/src/command.rs @@ -17,9 +17,9 @@ use crate::{ fn load_spec(id: &str) -> std::result::Result, String> { Ok(match id { - "dev" => Box::new(chain_spec::development_config()), - "template-rococo" => Box::new(chain_spec::local_testnet_config()), - "" | "local" => Box::new(chain_spec::local_testnet_config()), + "dev" => Box::new(chain_spec::development_chain_spec()), + "template-rococo" => Box::new(chain_spec::local_chain_spec()), + "" | "local" => Box::new(chain_spec::local_chain_spec()), path => Box::new(chain_spec::ChainSpec::from_json_file(std::path::PathBuf::from(path))?), }) } @@ -220,7 +220,10 @@ pub fn run() -> Result<()> { let hwbench = (!cli.no_hardware_benchmarks) .then_some(config.database.path().map(|database_path| { let _ = std::fs::create_dir_all(database_path); - sc_sysinfo::gather_hwbench(Some(database_path)) + sc_sysinfo::gather_hwbench( + Some(database_path), + &SUBSTRATE_REFERENCE_HARDWARE, + ) })) .flatten(); @@ -307,15 +310,9 @@ impl CliConfiguration for RelayChainCli { self.base.base.prometheus_config(default_listen_port, chain_spec) } - fn init( - &self, - _support_url: &String, - _impl_version: &String, - _logger_hook: F, - _config: &sc_service::Configuration, - ) -> Result<()> + fn init(&self, _support_url: &String, _impl_version: &String, _logger_hook: F) -> Result<()> where - F: FnOnce(&mut sc_cli::LoggerBuilder, &sc_service::Configuration), + F: FnOnce(&mut sc_cli::LoggerBuilder), { unreachable!("PolkadotCli is never initialized; qed"); } diff --git a/templates/parachain/node/src/service.rs b/templates/parachain/node/src/service.rs index b46b6ecfde18384836953797cc58f915ca254753..04e20be2bd40d0a80f4494926b44ee6d3b24e563 100644 --- a/templates/parachain/node/src/service.rs +++ b/templates/parachain/node/src/service.rs @@ -77,15 +77,16 @@ pub fn new_partial(config: &Configuration) -> Result .transpose()?; let heap_pages = config + .executor .default_heap_pages .map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |h| HeapAllocStrategy::Static { extra_pages: h as _ }); let executor = ParachainExecutor::builder() - .with_execution_method(config.wasm_method) + .with_execution_method(config.executor.wasm_method) .with_onchain_heap_alloc_strategy(heap_pages) .with_offchain_heap_alloc_strategy(heap_pages) - .with_max_runtime_instances(config.max_runtime_instances) - .with_runtime_cache_size(config.runtime_cache_size) + .with_max_runtime_instances(config.executor.max_runtime_instances) + .with_runtime_cache_size(config.executor.runtime_cache_size) .build(); let (client, backend, keystore_container, task_manager) = @@ -333,7 +334,7 @@ pub async fn start_parachain_node( // Here you can check whether the hardware meets your chains' requirements. Putting a link // in there and swapping out the requirements for your own are probably a good idea. The // requirements for a para-chain are dictated by its relay-chain. - match SUBSTRATE_REFERENCE_HARDWARE.check_hardware(&hwbench) { + match SUBSTRATE_REFERENCE_HARDWARE.check_hardware(&hwbench, false) { Err(err) if validator => { log::warn!( "โš ๏ธ The hardware does not meet the minimal requirements {} for role 'Authority'.", diff --git a/templates/parachain/pallets/template/src/benchmarking.rs b/templates/parachain/pallets/template/src/benchmarking.rs index 5acad6e60decc5eb3e80833712fadfc0d42c1857..29572c3ff6096154dca22097f58df3e5af7e8c25 100644 --- a/templates/parachain/pallets/template/src/benchmarking.rs +++ b/templates/parachain/pallets/template/src/benchmarking.rs @@ -1,5 +1,4 @@ //! Benchmarking setup for pallet-template -#![cfg(feature = "runtime-benchmarks")] use super::*; use frame_benchmarking::v2::*; diff --git a/templates/parachain/pallets/template/src/mock.rs b/templates/parachain/pallets/template/src/mock.rs index 46e3117596f599ca923425dd72be7c01421b4ffa..b8c730b6eb5d68634b43838a360a67e1fab298e2 100644 --- a/templates/parachain/pallets/template/src/mock.rs +++ b/templates/parachain/pallets/template/src/mock.rs @@ -22,7 +22,7 @@ mod test_runtime { #[runtime::pallet_index(0)] pub type System = frame_system; #[runtime::pallet_index(1)] - pub type TemplateModule = crate; + pub type Template = crate; } #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] diff --git a/templates/parachain/pallets/template/src/tests.rs b/templates/parachain/pallets/template/src/tests.rs index a4a41af63c2e9df1d438e22f13da3ea01fcf7a7b..2f641ce08fbb032aa1722a29712c1c862eaa0858 100644 --- a/templates/parachain/pallets/template/src/tests.rs +++ b/templates/parachain/pallets/template/src/tests.rs @@ -5,7 +5,7 @@ use frame_support::{assert_noop, assert_ok}; fn it_works_for_default_value() { new_test_ext().execute_with(|| { // Dispatch a signed extrinsic. - assert_ok!(TemplateModule::do_something(RuntimeOrigin::signed(1), 42)); + assert_ok!(Template::do_something(RuntimeOrigin::signed(1), 42)); // Read pallet storage and assert an expected result. assert_eq!(Something::::get().map(|v| v.block_number), Some(42)); }); @@ -15,9 +15,6 @@ fn it_works_for_default_value() { fn correct_error_for_none_value() { new_test_ext().execute_with(|| { // Ensure the expected error is thrown when no value is present. - assert_noop!( - TemplateModule::cause_error(RuntimeOrigin::signed(1)), - Error::::NoneValue - ); + assert_noop!(Template::cause_error(RuntimeOrigin::signed(1)), Error::::NoneValue); }); } diff --git a/templates/parachain/pallets/template/src/weights.rs b/templates/parachain/pallets/template/src/weights.rs index 5bfe28e8b71e36d7ee06127b3c1f769617e96494..4b4522429c6ceab1d027194e3f85e070ead595c6 100644 --- a/templates/parachain/pallets/template/src/weights.rs +++ b/templates/parachain/pallets/template/src/weights.rs @@ -41,8 +41,8 @@ pub trait WeightInfo { /// 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) + /// Storage: Template Something (r:0 w:1) + /// Proof: Template Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn do_something() -> Weight { // Proof Size summary in bytes: // Measured: `0` @@ -51,8 +51,8 @@ impl WeightInfo for SubstrateWeight { 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) + /// Storage: Template Something (r:1 w:1) + /// Proof: Template Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn cause_error() -> Weight { // Proof Size summary in bytes: // Measured: `32` @@ -66,8 +66,8 @@ impl WeightInfo for SubstrateWeight { // 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) + /// Storage: Template Something (r:0 w:1) + /// Proof: Template Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn do_something() -> Weight { // Proof Size summary in bytes: // Measured: `0` @@ -76,8 +76,8 @@ impl WeightInfo for () { 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) + /// Storage: Template Something (r:1 w:1) + /// Proof: Template Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn cause_error() -> Weight { // Proof Size summary in bytes: // Measured: `32` diff --git a/templates/parachain/runtime/Cargo.toml b/templates/parachain/runtime/Cargo.toml index 939fa245d2a0c1b6e03ecd0541a2b9aa63af75bd..236b7c048632e7d196982c38ac6e0db311f9290f 100644 --- a/templates/parachain/runtime/Cargo.toml +++ b/templates/parachain/runtime/Cargo.toml @@ -27,6 +27,7 @@ scale-info = { features = [ ], workspace = true } smallvec = { workspace = true, default-features = true } docify = { workspace = true } +serde_json = { workspace = true, default-features = false, features = ["alloc"] } # Local pallet-parachain-template = { workspace = true } @@ -126,6 +127,7 @@ std = [ "polkadot-parachain-primitives/std", "polkadot-runtime-common/std", "scale-info/std", + "serde_json/std", "sp-api/std", "sp-block-builder/std", "sp-consensus-aura/std", diff --git a/templates/parachain/runtime/src/apis.rs b/templates/parachain/runtime/src/apis.rs index f5d5d3e63027be2d7cdb20fb24bb4c6dcf3c25c9..243db1b6dde0c27e87b9c4c50dddc5aa2c4c2ef8 100644 --- a/templates/parachain/runtime/src/apis.rs +++ b/templates/parachain/runtime/src/apis.rs @@ -295,11 +295,11 @@ impl_runtime_apis! { } fn get_preset(id: &Option) -> Option> { - get_preset::(id, |_| None) + get_preset::(id, crate::genesis_config_presets::get_preset) } fn preset_names() -> Vec { - Default::default() + crate::genesis_config_presets::preset_names() } } } diff --git a/templates/parachain/runtime/src/configs/mod.rs b/templates/parachain/runtime/src/configs/mod.rs index 607797e690ba14cade930f7c8ad1454e182cd136..43ab63bd0b67834a6acdd56786d2dc49bdb378c4 100644 --- a/templates/parachain/runtime/src/configs/mod.rs +++ b/templates/parachain/runtime/src/configs/mod.rs @@ -158,6 +158,7 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type FreezeIdentifier = RuntimeFreezeReason; type MaxFreezes = VariantCountOf; + type DoneSlashHandler = (); } parameter_types! { @@ -198,6 +199,7 @@ impl cumulus_pallet_parachain_system::Config for Runtime { type ReservedXcmpWeight = ReservedXcmpWeight; type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases; type ConsensusHook = ConsensusHook; + type SelectCore = cumulus_pallet_parachain_system::DefaultCoreSelector; } impl parachain_info::Config for Runtime {} diff --git a/templates/parachain/runtime/src/genesis_config_presets.rs b/templates/parachain/runtime/src/genesis_config_presets.rs new file mode 100644 index 0000000000000000000000000000000000000000..fec53d17394ca2363094026526cbc59bd25385e5 --- /dev/null +++ b/templates/parachain/runtime/src/genesis_config_presets.rs @@ -0,0 +1,151 @@ +use cumulus_primitives_core::ParaId; + +use crate::{ + AccountId, BalancesConfig, CollatorSelectionConfig, ParachainInfoConfig, PolkadotXcmConfig, + RuntimeGenesisConfig, SessionConfig, SessionKeys, SudoConfig, EXISTENTIAL_DEPOSIT, +}; +use alloc::{vec, vec::Vec}; +use parachains_common::{genesis_config_helpers::*, AuraId}; +use serde_json::Value; +use sp_core::sr25519; +use sp_genesis_builder::PresetId; + +/// The default XCM version to set in genesis config. +const SAFE_XCM_VERSION: u32 = xcm::prelude::XCM_VERSION; + +/// Generate the session keys from individual elements. +/// +/// The input must be a tuple of individual keys (a single arg for now since we have just one key). +pub fn template_session_keys(keys: AuraId) -> SessionKeys { + SessionKeys { aura: keys } +} + +fn testnet_genesis( + invulnerables: Vec<(AccountId, AuraId)>, + endowed_accounts: Vec, + root: AccountId, + id: ParaId, +) -> Value { + let config = RuntimeGenesisConfig { + balances: BalancesConfig { + balances: endowed_accounts + .iter() + .cloned() + .map(|k| (k, 1u128 << 60)) + .collect::>(), + }, + parachain_info: ParachainInfoConfig { parachain_id: id, ..Default::default() }, + collator_selection: CollatorSelectionConfig { + invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect::>(), + candidacy_bond: EXISTENTIAL_DEPOSIT * 16, + ..Default::default() + }, + session: SessionConfig { + keys: invulnerables + .into_iter() + .map(|(acc, aura)| { + ( + acc.clone(), // account id + acc, // validator id + template_session_keys(aura), // session keys + ) + }) + .collect::>(), + ..Default::default() + }, + polkadot_xcm: PolkadotXcmConfig { + safe_xcm_version: Some(SAFE_XCM_VERSION), + ..Default::default() + }, + sudo: SudoConfig { key: Some(root) }, + ..Default::default() + }; + + serde_json::to_value(config).expect("Could not build genesis config.") +} + +fn local_testnet_genesis() -> Value { + testnet_genesis( + // initial collators. + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed::("Bob"), + ), + ], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + get_account_id_from_seed::("Alice"), + 1000.into(), + ) +} + +fn development_config_genesis() -> Value { + testnet_genesis( + // initial collators. + vec![ + ( + get_account_id_from_seed::("Alice"), + get_collator_keys_from_seed::("Alice"), + ), + ( + get_account_id_from_seed::("Bob"), + get_collator_keys_from_seed::("Bob"), + ), + ], + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + get_account_id_from_seed::("Alice"), + 1000.into(), + ) +} + +/// Provides the JSON representation of predefined genesis config for given `id`. +pub fn get_preset(id: &PresetId) -> Option> { + let patch = match id.try_into() { + Ok(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET) => local_testnet_genesis(), + Ok(sp_genesis_builder::DEV_RUNTIME_PRESET) => development_config_genesis(), + _ => return None, + }; + Some( + serde_json::to_string(&patch) + .expect("serialization to json is expected to work. qed.") + .into_bytes(), + ) +} + +/// List of supported presets. +pub fn preset_names() -> Vec { + vec![ + PresetId::from(sp_genesis_builder::DEV_RUNTIME_PRESET), + PresetId::from(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET), + ] +} diff --git a/templates/parachain/runtime/src/lib.rs b/templates/parachain/runtime/src/lib.rs index 55714a1248138f64f741caa3c7f4f93d91de7e12..ccec648ce4c19e52a73659fd8dc4a9ab9582414e 100644 --- a/templates/parachain/runtime/src/lib.rs +++ b/templates/parachain/runtime/src/lib.rs @@ -10,6 +10,7 @@ pub mod apis; #[cfg(feature = "runtime-benchmarks")] mod benchmarks; pub mod configs; +mod genesis_config_presets; mod weights; extern crate alloc; @@ -171,7 +172,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: apis::RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 1, + system_version: 1, }; #[docify::export] diff --git a/templates/solochain/README.md b/templates/solochain/README.md index 6a5a7853f9c0530761dac02f27e476d82d7a241f..7f36a997985d7a527435a79e0b20a46326fb0dc8 100644 --- a/templates/solochain/README.md +++ b/templates/solochain/README.md @@ -23,9 +23,17 @@ packages required to compile this template. Check the the most common dependencies. Alternatively, you can use one of the [alternative installation](#alternatives-installations) options. +Fetch solochain template code: + +```sh +git clone https://github.com/paritytech/polkadot-sdk-solochain-template.git solochain-template + +cd solochain-template +``` + ### Build -Use the following command to build the node without launching it: +๐Ÿ”จ Use the following command to build the node without launching it: ```sh cargo build --release @@ -177,7 +185,7 @@ template and note the following: configuration is defined by a code block that begins with `impl $PALLET_NAME::Config for Runtime`. - The pallets are composed into a single runtime by way of the - [`construct_runtime!`](https://paritytech.github.io/substrate/master/frame_support/macro.construct_runtime.html) + [#[runtime]](https://paritytech.github.io/polkadot-sdk/master/frame_support/attr.runtime.html) macro, which is part of the [core FRAME pallet library](https://docs.substrate.io/reference/frame-pallets/#system-pallets). diff --git a/templates/solochain/node/Cargo.toml b/templates/solochain/node/Cargo.toml index 8f74c6b3cb55bc190616292458e847002faf440b..a0285e048d1b41647b34a6447204327f9c1b32ae 100644 --- a/templates/solochain/node/Cargo.toml +++ b/templates/solochain/node/Cargo.toml @@ -35,6 +35,7 @@ sp-consensus-aura = { workspace = true, default-features = true } sc-consensus = { workspace = true, default-features = true } sc-consensus-grandpa = { workspace = true, default-features = true } sp-consensus-grandpa = { workspace = true, default-features = true } +sp-genesis-builder = { workspace = true, default-features = true } sc-client-api = { workspace = true, default-features = true } sc-basic-authorship = { workspace = true, default-features = true } @@ -66,9 +67,7 @@ substrate-build-script-utils = { workspace = true, default-features = true } [features] default = ["std"] -std = [ - "solochain-template-runtime/std", -] +std = ["solochain-template-runtime/std"] # Dependencies that are only required if runtime benchmarking should be build. runtime-benchmarks = [ "frame-benchmarking-cli/runtime-benchmarks", diff --git a/templates/solochain/node/src/chain_spec.rs b/templates/solochain/node/src/chain_spec.rs index 651025e68ded916485f03b377bb44482d89ac5ba..086bf7accf3a9f9cccaa2eff2ec6b0d080a3809e 100644 --- a/templates/solochain/node/src/chain_spec.rs +++ b/templates/solochain/node/src/chain_spec.rs @@ -1,39 +1,10 @@ use sc_service::ChainType; -use solochain_template_runtime::{AccountId, Signature, WASM_BINARY}; -use sp_consensus_aura::sr25519::AuthorityId as AuraId; -use sp_consensus_grandpa::AuthorityId as GrandpaId; -use sp_core::{sr25519, Pair, Public}; -use sp_runtime::traits::{IdentifyAccount, Verify}; - -// The URL for the telemetry server. -// const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/"; +use solochain_template_runtime::WASM_BINARY; /// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type. pub type ChainSpec = sc_service::GenericChainSpec; -/// Generate a crypto pair from seed. -pub fn get_from_seed(seed: &str) -> ::Public { - TPublic::Pair::from_string(&format!("//{}", seed), None) - .expect("static values are valid; qed") - .public() -} - -type AccountPublic = ::Signer; - -/// Generate an account ID from seed. -pub fn get_account_id_from_seed(seed: &str) -> AccountId -where - AccountPublic: From<::Public>, -{ - AccountPublic::from(get_from_seed::(seed)).into_account() -} - -/// Generate an Aura authority key. -pub fn authority_keys_from_seed(s: &str) -> (AuraId, GrandpaId) { - (get_from_seed::(s), get_from_seed::(s)) -} - -pub fn development_config() -> Result { +pub fn development_chain_spec() -> Result { Ok(ChainSpec::builder( WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?, None, @@ -41,24 +12,11 @@ pub fn development_config() -> Result { .with_name("Development") .with_id("dev") .with_chain_type(ChainType::Development) - .with_genesis_config_patch(testnet_genesis( - // Initial PoA authorities - vec![authority_keys_from_seed("Alice")], - // Sudo account - get_account_id_from_seed::("Alice"), - // Pre-funded accounts - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - ], - true, - )) + .with_genesis_config_preset_name(sp_genesis_builder::DEV_RUNTIME_PRESET) .build()) } -pub fn local_testnet_config() -> Result { +pub fn local_chain_spec() -> Result { Ok(ChainSpec::builder( WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?, None, @@ -66,52 +24,6 @@ pub fn local_testnet_config() -> Result { .with_name("Local Testnet") .with_id("local_testnet") .with_chain_type(ChainType::Local) - .with_genesis_config_patch(testnet_genesis( - // Initial PoA authorities - vec![authority_keys_from_seed("Alice"), authority_keys_from_seed("Bob")], - // Sudo account - get_account_id_from_seed::("Alice"), - // Pre-funded accounts - vec![ - get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Charlie"), - get_account_id_from_seed::("Dave"), - get_account_id_from_seed::("Eve"), - get_account_id_from_seed::("Ferdie"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ], - true, - )) + .with_genesis_config_preset_name(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET) .build()) } - -/// Configure initial storage state for FRAME modules. -fn testnet_genesis( - initial_authorities: Vec<(AuraId, GrandpaId)>, - root_key: AccountId, - endowed_accounts: Vec, - _enable_println: bool, -) -> serde_json::Value { - serde_json::json!({ - "balances": { - // Configure endowed accounts with initial balance of 1 << 60. - "balances": endowed_accounts.iter().cloned().map(|k| (k, 1u64 << 60)).collect::>(), - }, - "aura": { - "authorities": initial_authorities.iter().map(|x| (x.0.clone())).collect::>(), - }, - "grandpa": { - "authorities": initial_authorities.iter().map(|x| (x.1.clone(), 1)).collect::>(), - }, - "sudo": { - // Assign network admin rights. - "key": Some(root_key), - }, - }) -} diff --git a/templates/solochain/node/src/command.rs b/templates/solochain/node/src/command.rs index 624ace1bf350a1d5b36d0f409d0ca3868fd127fc..e2c7657c95cce937395dedc624b2cff51e937297 100644 --- a/templates/solochain/node/src/command.rs +++ b/templates/solochain/node/src/command.rs @@ -37,8 +37,8 @@ impl SubstrateCli for Cli { fn load_spec(&self, id: &str) -> Result, String> { Ok(match id { - "dev" => Box::new(chain_spec::development_config()?), - "" | "local" => Box::new(chain_spec::local_testnet_config()?), + "dev" => Box::new(chain_spec::development_chain_spec()?), + "" | "local" => Box::new(chain_spec::local_chain_spec()?), path => Box::new(chain_spec::ChainSpec::from_json_file(std::path::PathBuf::from(path))?), }) diff --git a/templates/solochain/node/src/service.rs b/templates/solochain/node/src/service.rs index 2b43ecfa1ce2a708de83585baf0461e08f76f784..2de543235ec8cc1b6cf011e699cd5f21293b64a8 100644 --- a/templates/solochain/node/src/service.rs +++ b/templates/solochain/node/src/service.rs @@ -4,7 +4,10 @@ use futures::FutureExt; use sc_client_api::{Backend, BlockBackend}; use sc_consensus_aura::{ImportQueueParams, SlotProportion, StartAuraParams}; use sc_consensus_grandpa::SharedVoterState; -use sc_service::{error::Error as ServiceError, Configuration, TaskManager, WarpSyncConfig}; +use sc_service::{ + build_polkadot_syncing_strategy, error::Error as ServiceError, Configuration, TaskManager, + WarpSyncConfig, +}; use sc_telemetry::{Telemetry, TelemetryWorker}; use sc_transaction_pool_api::OffchainTransactionPoolFactory; use solochain_template_runtime::{self, apis::RuntimeApi, opaque::Block}; @@ -48,7 +51,7 @@ pub fn new_partial(config: &Configuration) -> Result { }) .transpose()?; - let executor = sc_service::new_wasm_executor::(config); + let executor = sc_service::new_wasm_executor::(&config.executor); let (client, backend, keystore_container, task_manager) = sc_service::new_full_parts::( config, @@ -166,6 +169,16 @@ pub fn new_full< Vec::default(), )); + let syncing_strategy = build_polkadot_syncing_strategy( + config.protocol_id(), + config.chain_spec.fork_id(), + &mut net_config, + Some(WarpSyncConfig::WithProvider(warp_sync)), + client.clone(), + &task_manager.spawn_handle(), + config.prometheus_config.as_ref().map(|config| &config.registry), + )?; + let (network, system_rpc_tx, tx_handler_controller, network_starter, sync_service) = sc_service::build_network(sc_service::BuildNetworkParams { config: &config, @@ -175,7 +188,7 @@ pub fn new_full< spawn_handle: task_manager.spawn_handle(), import_queue, block_announce_validator_builder: None, - warp_sync_config: Some(WarpSyncConfig::WithProvider(warp_sync)), + syncing_strategy, block_relay: None, metrics, })?; @@ -201,7 +214,7 @@ pub fn new_full< ); } - let role = config.role.clone(); + let role = config.role; let force_authoring = config.force_authoring; let backoff_authoring_blocks: Option<()> = None; let name = config.network.node_name.clone(); diff --git a/templates/solochain/pallets/template/src/benchmarking.rs b/templates/solochain/pallets/template/src/benchmarking.rs index d1a9554aed6dc0533f914e18661942ed56bcd514..8af5d246f76145bac77312c5a57fd648741e87e0 100644 --- a/templates/solochain/pallets/template/src/benchmarking.rs +++ b/templates/solochain/pallets/template/src/benchmarking.rs @@ -1,5 +1,5 @@ //! Benchmarking setup for pallet-template -#![cfg(feature = "runtime-benchmarks")] + use super::*; #[allow(unused)] diff --git a/templates/solochain/pallets/template/src/mock.rs b/templates/solochain/pallets/template/src/mock.rs index 0c2a247e802b35fcccd770bec2e5fa723bf49891..1b86cd9b7709a43bf7abe45893569b53a614f0e3 100644 --- a/templates/solochain/pallets/template/src/mock.rs +++ b/templates/solochain/pallets/template/src/mock.rs @@ -4,14 +4,30 @@ use sp_runtime::BuildStorage; type Block = frame_system::mocking::MockBlock; -// Configure a mock runtime to test the pallet. -frame_support::construct_runtime!( - pub enum Test - { - System: frame_system, - TemplateModule: pallet_template, - } -); +#[frame_support::runtime] +mod runtime { + // The main runtime + #[runtime::runtime] + // Runtime Types to be generated + #[runtime::derive( + RuntimeCall, + RuntimeEvent, + RuntimeError, + RuntimeOrigin, + RuntimeFreezeReason, + RuntimeHoldReason, + RuntimeSlashReason, + RuntimeLockId, + RuntimeTask + )] + pub struct Test; + + #[runtime::pallet_index(0)] + pub type System = frame_system::Pallet; + + #[runtime::pallet_index(1)] + pub type Template = pallet_template::Pallet; +} #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { diff --git a/templates/solochain/pallets/template/src/tests.rs b/templates/solochain/pallets/template/src/tests.rs index 83e4bea7377b348d8ac1d389813a788f79b81970..d05433c3add6ba1ec6ed194722ffe52b5a8bf19e 100644 --- a/templates/solochain/pallets/template/src/tests.rs +++ b/templates/solochain/pallets/template/src/tests.rs @@ -7,7 +7,7 @@ fn it_works_for_default_value() { // Go past genesis block so events get deposited System::set_block_number(1); // Dispatch a signed extrinsic. - assert_ok!(TemplateModule::do_something(RuntimeOrigin::signed(1), 42)); + assert_ok!(Template::do_something(RuntimeOrigin::signed(1), 42)); // Read pallet storage and assert an expected result. assert_eq!(Something::::get(), Some(42)); // Assert that the correct event was deposited @@ -19,9 +19,6 @@ fn it_works_for_default_value() { fn correct_error_for_none_value() { new_test_ext().execute_with(|| { // Ensure the expected error is thrown when no value is present. - assert_noop!( - TemplateModule::cause_error(RuntimeOrigin::signed(1)), - Error::::NoneValue - ); + assert_noop!(Template::cause_error(RuntimeOrigin::signed(1)), Error::::NoneValue); }); } diff --git a/templates/solochain/pallets/template/src/weights.rs b/templates/solochain/pallets/template/src/weights.rs index 7c42936e09f292de831d28460a3bc39436c3323f..c2879fa503c63580ccbd91c77ffedaa85d9fbf44 100644 --- a/templates/solochain/pallets/template/src/weights.rs +++ b/templates/solochain/pallets/template/src/weights.rs @@ -41,8 +41,8 @@ pub trait WeightInfo { /// 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) + /// Storage: Template Something (r:0 w:1) + /// Proof: Template Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn do_something() -> Weight { // Proof Size summary in bytes: // Measured: `0` @@ -51,8 +51,8 @@ impl WeightInfo for SubstrateWeight { 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) + /// Storage: Template Something (r:1 w:1) + /// Proof: Template Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn cause_error() -> Weight { // Proof Size summary in bytes: // Measured: `32` @@ -66,8 +66,8 @@ impl WeightInfo for SubstrateWeight { // 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) + /// Storage: Template Something (r:0 w:1) + /// Proof: Template Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn do_something() -> Weight { // Proof Size summary in bytes: // Measured: `0` @@ -76,8 +76,8 @@ impl WeightInfo for () { 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) + /// Storage: Template Something (r:1 w:1) + /// Proof: Template Something (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn cause_error() -> Weight { // Proof Size summary in bytes: // Measured: `32` diff --git a/templates/solochain/runtime/Cargo.toml b/templates/solochain/runtime/Cargo.toml index 9a1f7145c2ca3bd49ee489bbeeda72efe739bc5e..8a7fa74a59779b87ceeb5309128fab27b678d452 100644 --- a/templates/solochain/runtime/Cargo.toml +++ b/templates/solochain/runtime/Cargo.toml @@ -20,6 +20,7 @@ scale-info = { features = [ "derive", "serde", ], workspace = true } +serde_json = { workspace = true, default-features = false, features = ["alloc"] } # frame frame-support = { features = ["experimental"], workspace = true } @@ -45,6 +46,7 @@ sp-consensus-aura = { features = [ sp-consensus-grandpa = { features = [ "serde", ], workspace = true } +sp-keyring = { workspace = true } sp-core = { features = [ "serde", ], workspace = true } @@ -114,6 +116,8 @@ std = [ "sp-transaction-pool/std", "sp-version/std", + "serde_json/std", + "sp-keyring/std", "substrate-wasm-builder", ] diff --git a/templates/solochain/runtime/src/apis.rs b/templates/solochain/runtime/src/apis.rs index 1e3dc452857c993711698c29eb995afe5d955a91..87a09a5f7feefabcb3b5608fe52fc3edb1ac0fde 100644 --- a/templates/solochain/runtime/src/apis.rs +++ b/templates/solochain/runtime/src/apis.rs @@ -24,7 +24,7 @@ // For more information, please refer to // External crates imports -use alloc::{vec, vec::Vec}; +use alloc::vec::Vec; use frame_support::{ genesis_builder_helper::{build_state, get_preset}, weights::Weight, @@ -285,11 +285,11 @@ impl_runtime_apis! { } fn get_preset(id: &Option) -> Option> { - get_preset::(id, |_| None) + get_preset::(id, crate::genesis_config_presets::get_preset) } fn preset_names() -> Vec { - vec![] + crate::genesis_config_presets::preset_names() } } } diff --git a/templates/solochain/runtime/src/benchmarks.rs b/templates/solochain/runtime/src/benchmarks.rs index a42daf56b58eadfa0c6f5a10de574fc7af60ad1f..f39c2bd295935645703a3e56723b0c4da5695ece 100644 --- a/templates/solochain/runtime/src/benchmarks.rs +++ b/templates/solochain/runtime/src/benchmarks.rs @@ -29,5 +29,5 @@ frame_benchmarking::define_benchmarks!( [pallet_balances, Balances] [pallet_timestamp, Timestamp] [pallet_sudo, Sudo] - [pallet_template, TemplateModule] + [pallet_template, Template] ); diff --git a/templates/solochain/runtime/src/configs/mod.rs b/templates/solochain/runtime/src/configs/mod.rs index a5c12fbd79ab00f4d161c49692499c77d491f7f0..4f77ad37fe6402e8ace41434e9bccfff8f3b7f72 100644 --- a/templates/solochain/runtime/src/configs/mod.rs +++ b/templates/solochain/runtime/src/configs/mod.rs @@ -134,6 +134,7 @@ impl pallet_balances::Config for Runtime { type MaxFreezes = VariantCountOf; type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeHoldReason; + type DoneSlashHandler = (); } parameter_types! { diff --git a/templates/solochain/runtime/src/genesis_config_presets.rs b/templates/solochain/runtime/src/genesis_config_presets.rs new file mode 100644 index 0000000000000000000000000000000000000000..693ae5c2221f268ce9d04703ae9e6e140d7c4c50 --- /dev/null +++ b/templates/solochain/runtime/src/genesis_config_presets.rs @@ -0,0 +1,112 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{AccountId, BalancesConfig, RuntimeGenesisConfig, SudoConfig}; +use alloc::{vec, vec::Vec}; +use serde_json::Value; +use sp_consensus_aura::sr25519::AuthorityId as AuraId; +use sp_consensus_grandpa::AuthorityId as GrandpaId; +use sp_genesis_builder::{self, PresetId}; +use sp_keyring::AccountKeyring; + +// Returns the genesis config presets populated with given parameters. +fn testnet_genesis( + initial_authorities: Vec<(AuraId, GrandpaId)>, + endowed_accounts: Vec, + root: AccountId, +) -> Value { + let config = RuntimeGenesisConfig { + balances: BalancesConfig { + balances: endowed_accounts + .iter() + .cloned() + .map(|k| (k, 1u128 << 60)) + .collect::>(), + }, + aura: pallet_aura::GenesisConfig { + authorities: initial_authorities.iter().map(|x| (x.0.clone())).collect::>(), + }, + grandpa: pallet_grandpa::GenesisConfig { + authorities: initial_authorities.iter().map(|x| (x.1.clone(), 1)).collect::>(), + ..Default::default() + }, + sudo: SudoConfig { key: Some(root) }, + ..Default::default() + }; + + serde_json::to_value(config).expect("Could not build genesis config.") +} + +/// Return the development genesis config. +pub fn development_config_genesis() -> Value { + testnet_genesis( + vec![( + sp_keyring::Sr25519Keyring::Alice.public().into(), + sp_keyring::Ed25519Keyring::Alice.public().into(), + )], + vec![ + AccountKeyring::Alice.to_account_id(), + AccountKeyring::Bob.to_account_id(), + AccountKeyring::AliceStash.to_account_id(), + AccountKeyring::BobStash.to_account_id(), + ], + sp_keyring::AccountKeyring::Alice.to_account_id(), + ) +} + +/// Return the local genesis config preset. +pub fn local_config_genesis() -> Value { + testnet_genesis( + vec![ + ( + sp_keyring::Sr25519Keyring::Alice.public().into(), + sp_keyring::Ed25519Keyring::Alice.public().into(), + ), + ( + sp_keyring::Sr25519Keyring::Bob.public().into(), + sp_keyring::Ed25519Keyring::Bob.public().into(), + ), + ], + AccountKeyring::iter() + .filter(|v| v != &AccountKeyring::One && v != &AccountKeyring::Two) + .map(|v| v.to_account_id()) + .collect::>(), + AccountKeyring::Alice.to_account_id(), + ) +} + +/// Provides the JSON representation of predefined genesis config for given `id`. +pub fn get_preset(id: &PresetId) -> Option> { + let patch = match id.try_into() { + Ok(sp_genesis_builder::DEV_RUNTIME_PRESET) => development_config_genesis(), + Ok(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET) => local_config_genesis(), + _ => return None, + }; + Some( + serde_json::to_string(&patch) + .expect("serialization to json is expected to work. qed.") + .into_bytes(), + ) +} + +/// List of supported presets. +pub fn preset_names() -> Vec { + vec![ + PresetId::from(sp_genesis_builder::DEV_RUNTIME_PRESET), + PresetId::from(sp_genesis_builder::LOCAL_TESTNET_RUNTIME_PRESET), + ] +} diff --git a/templates/solochain/runtime/src/lib.rs b/templates/solochain/runtime/src/lib.rs index 6cbfbb879602e8cef718481ed199919eb49270e6..bc47f3aeac85d5f4c5e8b551ca345950bcb6491e 100644 --- a/templates/solochain/runtime/src/lib.rs +++ b/templates/solochain/runtime/src/lib.rs @@ -25,6 +25,8 @@ pub use pallet_timestamp::Call as TimestampCall; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; +pub mod genesis_config_presets; + /// Opaque types. These are used by the CLI to instantiate machinery that don't need to know /// the specifics of the runtime. They can then be made to be agnostic over specific formats /// of data like extrinsics, allowing for them to continue syncing the network through upgrades @@ -71,7 +73,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 1, apis: apis::RUNTIME_API_VERSIONS, transaction_version: 1, - state_version: 1, + system_version: 1, }; mod block_times { @@ -220,5 +222,5 @@ mod runtime { // Include the custom logic from the pallet-template in the runtime. #[runtime::pallet_index(7)] - pub type TemplateModule = pallet_template; + pub type Template = pallet_template; } diff --git a/templates/zombienet/Cargo.toml b/templates/zombienet/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..f29325dbe6a90ae45fbd39cec85a804ac753ee8b --- /dev/null +++ b/templates/zombienet/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "template-zombienet-tests" +description = "Zombienet test for templates." +version = "0.0.0" +license = "Unlicense" +authors.workspace = true +homepage.workspace = true +repository.workspace = true +edition.workspace = true +publish = false + +[dependencies] +env_logger = { workspace = true } +log = { workspace = true } +tokio = { workspace = true, features = ["rt-multi-thread"] } +anyhow = { workspace = true } +zombienet-sdk = { workspace = true } + +[features] +zombienet = [] diff --git a/templates/zombienet/tests/smoke.rs b/templates/zombienet/tests/smoke.rs new file mode 100644 index 0000000000000000000000000000000000000000..ba5f42142f31e2fa60fc4baa2b85f7737e60752e --- /dev/null +++ b/templates/zombienet/tests/smoke.rs @@ -0,0 +1,96 @@ +//! This test is setup to run with the `native` provider and needs these binaries in your PATH +//! `polkadot`, `polkadot-prepare-worker`, `polkadot-execute-worker`, `parachain-template-node`. +//! You can follow these steps to compile and export the binaries: +//! `cargo build --release -features fast-runtime --bin polkadot --bin polkadot-execute-worker --bin +//! polkadot-prepare-worker` +//! `cargo build --package parachain-template-node --release` +//! `cargo build --package minimal-template-node --release` +//! `export PATH=/target/release:$PATH +//! +//! The you can run the test with +//! `cargo test -p template-zombienet-tests` + +#[cfg(feature = "zombienet")] +mod smoke { + use anyhow::anyhow; + use zombienet_sdk::{NetworkConfig, NetworkConfigBuilder, NetworkConfigExt}; + + pub fn get_config(cmd: &str, para_cmd: Option<&str>) -> Result { + let chain = if cmd == "polkadot" { "rococo-local" } else { "dev" }; + let config = NetworkConfigBuilder::new().with_relaychain(|r| { + r.with_chain(chain) + .with_default_command(cmd) + .with_node(|node| node.with_name("alice")) + .with_node(|node| node.with_name("bob")) + }); + + let config = if let Some(para_cmd) = para_cmd { + config.with_parachain(|p| { + p.with_id(1000) + .with_default_command(para_cmd) + .with_collator(|n| n.with_name("collator")) + }) + } else { + config + }; + + config.build().map_err(|e| { + let errs = e.into_iter().map(|e| e.to_string()).collect::>().join(" "); + anyhow!("config errs: {errs}") + }) + } + + #[tokio::test(flavor = "multi_thread")] + async fn parachain_template_block_production_test() -> Result<(), anyhow::Error> { + let _ = env_logger::try_init_from_env( + env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"), + ); + + let config = get_config("polkadot", Some("parachain-template-node"))?; + + let network = config.spawn_native().await?; + + // wait 6 blocks of the para + let collator = network.get_node("collator")?; + assert!(collator + .wait_metric("block_height{status=\"best\"}", |b| b > 5_f64) + .await + .is_ok()); + + Ok(()) + } + + #[tokio::test(flavor = "multi_thread")] + async fn solochain_template_block_production_test() -> Result<(), anyhow::Error> { + let _ = env_logger::try_init_from_env( + env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"), + ); + + let config = get_config("solochain-template-node", None)?; + + let network = config.spawn_native().await?; + + // wait 6 blocks + let alice = network.get_node("alice")?; + assert!(alice.wait_metric("block_height{status=\"best\"}", |b| b > 5_f64).await.is_ok()); + + Ok(()) + } + + #[tokio::test(flavor = "multi_thread")] + async fn minimal_template_block_production_test() -> Result<(), anyhow::Error> { + let _ = env_logger::try_init_from_env( + env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"), + ); + + let config = get_config("minimal-template-node", None)?; + + let network = config.spawn_native().await?; + + // wait 6 blocks + let alice = network.get_node("alice")?; + assert!(alice.wait_metric("block_height{status=\"best\"}", |b| b > 5_f64).await.is_ok()); + + Ok(()) + } +} diff --git a/umbrella/Cargo.toml b/umbrella/Cargo.toml index 6d380a4bcbb7cf8de14dd3532b9082ad39a8f4a2..e05bf129bbd5efe9e286d5f7a68e898ea728246a 100644 --- a/umbrella/Cargo.toml +++ b/umbrella/Cargo.toml @@ -335,7 +335,7 @@ runtime-benchmarks = [ "parachains-common?/runtime-benchmarks", "polkadot-cli?/runtime-benchmarks", "polkadot-node-metrics?/runtime-benchmarks", - "polkadot-parachain-lib?/runtime-benchmarks", + "polkadot-omni-node-lib?/runtime-benchmarks", "polkadot-parachain-primitives?/runtime-benchmarks", "polkadot-primitives?/runtime-benchmarks", "polkadot-runtime-common?/runtime-benchmarks", @@ -466,7 +466,7 @@ try-runtime = [ "pallet-xcm-bridge-hub?/try-runtime", "pallet-xcm?/try-runtime", "polkadot-cli?/try-runtime", - "polkadot-parachain-lib?/try-runtime", + "polkadot-omni-node-lib?/try-runtime", "polkadot-runtime-common?/try-runtime", "polkadot-runtime-parachains?/try-runtime", "polkadot-sdk-frame?/try-runtime", @@ -600,13 +600,14 @@ runtime = [ "sp-wasm-interface", "sp-weights", ] -node = ["asset-test-utils", "bridge-hub-test-utils", "cumulus-client-cli", "cumulus-client-collator", "cumulus-client-consensus-aura", "cumulus-client-consensus-common", "cumulus-client-consensus-proposer", "cumulus-client-consensus-relay-chain", "cumulus-client-network", "cumulus-client-parachain-inherent", "cumulus-client-pov-recovery", "cumulus-client-service", "cumulus-relay-chain-inprocess-interface", "cumulus-relay-chain-interface", "cumulus-relay-chain-minimal-node", "cumulus-relay-chain-rpc-interface", "cumulus-test-relay-sproof-builder", "emulated-integration-tests-common", "fork-tree", "frame-benchmarking-cli", "frame-remote-externalities", "frame-support-procedural-tools", "generate-bags", "mmr-gadget", "mmr-rpc", "pallet-contracts-mock-network", "pallet-revive-mock-network", "pallet-transaction-payment-rpc", "parachains-runtimes-test-utils", "polkadot-approval-distribution", "polkadot-availability-bitfield-distribution", "polkadot-availability-distribution", "polkadot-availability-recovery", "polkadot-cli", "polkadot-collator-protocol", "polkadot-dispute-distribution", "polkadot-erasure-coding", "polkadot-gossip-support", "polkadot-network-bridge", "polkadot-node-collation-generation", "polkadot-node-core-approval-voting", "polkadot-node-core-av-store", "polkadot-node-core-backing", "polkadot-node-core-bitfield-signing", "polkadot-node-core-candidate-validation", "polkadot-node-core-chain-api", "polkadot-node-core-chain-selection", "polkadot-node-core-dispute-coordinator", "polkadot-node-core-parachains-inherent", "polkadot-node-core-prospective-parachains", "polkadot-node-core-provisioner", "polkadot-node-core-pvf", "polkadot-node-core-pvf-checker", "polkadot-node-core-pvf-common", "polkadot-node-core-pvf-execute-worker", "polkadot-node-core-pvf-prepare-worker", "polkadot-node-core-runtime-api", "polkadot-node-jaeger", "polkadot-node-metrics", "polkadot-node-network-protocol", "polkadot-node-primitives", "polkadot-node-subsystem", "polkadot-node-subsystem-types", "polkadot-node-subsystem-util", "polkadot-overseer", "polkadot-parachain-lib", "polkadot-rpc", "polkadot-service", "polkadot-statement-distribution", "polkadot-statement-table", "sc-allocator", "sc-authority-discovery", "sc-basic-authorship", "sc-block-builder", "sc-chain-spec", "sc-cli", "sc-client-api", "sc-client-db", "sc-consensus", "sc-consensus-aura", "sc-consensus-babe", "sc-consensus-babe-rpc", "sc-consensus-beefy", "sc-consensus-beefy-rpc", "sc-consensus-epochs", "sc-consensus-grandpa", "sc-consensus-grandpa-rpc", "sc-consensus-manual-seal", "sc-consensus-pow", "sc-consensus-slots", "sc-executor", "sc-executor-common", "sc-executor-polkavm", "sc-executor-wasmtime", "sc-informant", "sc-keystore", "sc-mixnet", "sc-network", "sc-network-common", "sc-network-gossip", "sc-network-light", "sc-network-statement", "sc-network-sync", "sc-network-transactions", "sc-network-types", "sc-offchain", "sc-proposer-metrics", "sc-rpc", "sc-rpc-api", "sc-rpc-server", "sc-rpc-spec-v2", "sc-service", "sc-state-db", "sc-statement-store", "sc-storage-monitor", "sc-sync-state-rpc", "sc-sysinfo", "sc-telemetry", "sc-tracing", "sc-transaction-pool", "sc-transaction-pool-api", "sc-utils", "snowbridge-runtime-test-common", "sp-blockchain", "sp-consensus", "sp-core-hashing", "sp-core-hashing-proc-macro", "sp-database", "sp-maybe-compressed-blob", "sp-panic-handler", "sp-rpc", "staging-chain-spec-builder", "staging-node-inspect", "staging-tracking-allocator", "std", "subkey", "substrate-build-script-utils", "substrate-frame-rpc-support", "substrate-frame-rpc-system", "substrate-prometheus-endpoint", "substrate-rpc-client", "substrate-state-trie-migration-rpc", "substrate-wasm-builder", "tracing-gum", "xcm-emulator", "xcm-simulator"] +node = ["asset-test-utils", "bridge-hub-test-utils", "cumulus-client-cli", "cumulus-client-collator", "cumulus-client-consensus-aura", "cumulus-client-consensus-common", "cumulus-client-consensus-proposer", "cumulus-client-consensus-relay-chain", "cumulus-client-network", "cumulus-client-parachain-inherent", "cumulus-client-pov-recovery", "cumulus-client-service", "cumulus-relay-chain-inprocess-interface", "cumulus-relay-chain-interface", "cumulus-relay-chain-minimal-node", "cumulus-relay-chain-rpc-interface", "cumulus-test-relay-sproof-builder", "emulated-integration-tests-common", "fork-tree", "frame-benchmarking-cli", "frame-remote-externalities", "frame-support-procedural-tools", "generate-bags", "mmr-gadget", "mmr-rpc", "pallet-contracts-mock-network", "pallet-revive-mock-network", "pallet-transaction-payment-rpc", "parachains-runtimes-test-utils", "polkadot-approval-distribution", "polkadot-availability-bitfield-distribution", "polkadot-availability-distribution", "polkadot-availability-recovery", "polkadot-cli", "polkadot-collator-protocol", "polkadot-dispute-distribution", "polkadot-erasure-coding", "polkadot-gossip-support", "polkadot-network-bridge", "polkadot-node-collation-generation", "polkadot-node-core-approval-voting", "polkadot-node-core-approval-voting-parallel", "polkadot-node-core-av-store", "polkadot-node-core-backing", "polkadot-node-core-bitfield-signing", "polkadot-node-core-candidate-validation", "polkadot-node-core-chain-api", "polkadot-node-core-chain-selection", "polkadot-node-core-dispute-coordinator", "polkadot-node-core-parachains-inherent", "polkadot-node-core-prospective-parachains", "polkadot-node-core-provisioner", "polkadot-node-core-pvf", "polkadot-node-core-pvf-checker", "polkadot-node-core-pvf-common", "polkadot-node-core-pvf-execute-worker", "polkadot-node-core-pvf-prepare-worker", "polkadot-node-core-runtime-api", "polkadot-node-metrics", "polkadot-node-network-protocol", "polkadot-node-primitives", "polkadot-node-subsystem", "polkadot-node-subsystem-types", "polkadot-node-subsystem-util", "polkadot-omni-node-lib", "polkadot-overseer", "polkadot-rpc", "polkadot-service", "polkadot-statement-distribution", "polkadot-statement-table", "sc-allocator", "sc-authority-discovery", "sc-basic-authorship", "sc-block-builder", "sc-chain-spec", "sc-cli", "sc-client-api", "sc-client-db", "sc-consensus", "sc-consensus-aura", "sc-consensus-babe", "sc-consensus-babe-rpc", "sc-consensus-beefy", "sc-consensus-beefy-rpc", "sc-consensus-epochs", "sc-consensus-grandpa", "sc-consensus-grandpa-rpc", "sc-consensus-manual-seal", "sc-consensus-pow", "sc-consensus-slots", "sc-executor", "sc-executor-common", "sc-executor-polkavm", "sc-executor-wasmtime", "sc-informant", "sc-keystore", "sc-mixnet", "sc-network", "sc-network-common", "sc-network-gossip", "sc-network-light", "sc-network-statement", "sc-network-sync", "sc-network-transactions", "sc-network-types", "sc-offchain", "sc-proposer-metrics", "sc-rpc", "sc-rpc-api", "sc-rpc-server", "sc-rpc-spec-v2", "sc-service", "sc-state-db", "sc-statement-store", "sc-storage-monitor", "sc-sync-state-rpc", "sc-sysinfo", "sc-telemetry", "sc-tracing", "sc-transaction-pool", "sc-transaction-pool-api", "sc-utils", "snowbridge-runtime-test-common", "sp-blockchain", "sp-consensus", "sp-core-hashing", "sp-core-hashing-proc-macro", "sp-database", "sp-maybe-compressed-blob", "sp-panic-handler", "sp-rpc", "staging-chain-spec-builder", "staging-node-inspect", "staging-tracking-allocator", "std", "subkey", "substrate-build-script-utils", "substrate-frame-rpc-support", "substrate-frame-rpc-system", "substrate-prometheus-endpoint", "substrate-rpc-client", "substrate-state-trie-migration-rpc", "substrate-wasm-builder", "tracing-gum", "xcm-emulator", "xcm-simulator"] tuples-96 = [ "frame-support-procedural?/tuples-96", "frame-support?/tuples-96", ] riscv = [ "pallet-revive-fixtures?/riscv", + "pallet-revive-mock-network?/riscv", "pallet-revive?/riscv", ] @@ -1966,6 +1967,11 @@ path = "../polkadot/node/core/approval-voting" default-features = false optional = true +[dependencies.polkadot-node-core-approval-voting-parallel] +path = "../polkadot/node/core/approval-voting-parallel" +default-features = false +optional = true + [dependencies.polkadot-node-core-av-store] path = "../polkadot/node/core/av-store" default-features = false @@ -2046,11 +2052,6 @@ path = "../polkadot/node/core/runtime-api" default-features = false optional = true -[dependencies.polkadot-node-jaeger] -path = "../polkadot/node/jaeger" -default-features = false -optional = true - [dependencies.polkadot-node-metrics] path = "../polkadot/node/metrics" default-features = false @@ -2081,13 +2082,13 @@ path = "../polkadot/node/subsystem-util" default-features = false optional = true -[dependencies.polkadot-overseer] -path = "../polkadot/node/overseer" +[dependencies.polkadot-omni-node-lib] +path = "../cumulus/polkadot-omni-node/lib" default-features = false optional = true -[dependencies.polkadot-parachain-lib] -path = "../cumulus/polkadot-parachain/polkadot-parachain-lib" +[dependencies.polkadot-overseer] +path = "../polkadot/node/overseer" default-features = false optional = true diff --git a/umbrella/src/lib.rs b/umbrella/src/lib.rs index b7b9c15fe5880f5c631355155312a588f10d2561..7778706d515d07f60f6f349fcdb59f0425658cf9 100644 --- a/umbrella/src/lib.rs +++ b/umbrella/src/lib.rs @@ -796,6 +796,10 @@ pub use polkadot_node_collation_generation; #[cfg(feature = "polkadot-node-core-approval-voting")] pub use polkadot_node_core_approval_voting; +/// Approval Voting Subsystem running approval work in parallel. +#[cfg(feature = "polkadot-node-core-approval-voting-parallel")] +pub use polkadot_node_core_approval_voting_parallel; + /// The Availability Store subsystem. Wrapper over the DB that stores availability data and /// chunks. #[cfg(feature = "polkadot-node-core-av-store")] @@ -870,10 +874,6 @@ pub use polkadot_node_core_pvf_prepare_worker; #[cfg(feature = "polkadot-node-core-runtime-api")] pub use polkadot_node_core_runtime_api; -/// Polkadot Jaeger primitives, but equally useful for Grafana/Tempo. -#[cfg(feature = "polkadot-node-jaeger")] -pub use polkadot_node_jaeger; - /// Subsystem metric helpers. #[cfg(feature = "polkadot-node-metrics")] pub use polkadot_node_metrics; @@ -898,14 +898,14 @@ pub use polkadot_node_subsystem_types; #[cfg(feature = "polkadot-node-subsystem-util")] pub use polkadot_node_subsystem_util; +/// Helper library that can be used to build a parachain node. +#[cfg(feature = "polkadot-omni-node-lib")] +pub use polkadot_omni_node_lib; + /// System overseer of the Polkadot node. #[cfg(feature = "polkadot-overseer")] pub use polkadot_overseer; -/// Helper library that can be used to build a parachain node. -#[cfg(feature = "polkadot-parachain-lib")] -pub use polkadot_parachain_lib; - /// Types and utilities for creating and working with parachains. #[cfg(feature = "polkadot-parachain-primitives")] pub use polkadot_parachain_primitives;